From 675b4aa878f16291ce33fced48a2bc7425f635ae Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sun, 24 Nov 2013 14:19:41 +0000 Subject: Moved source to src --- source/AllToLua.bat | 27 - source/AllToLua.pkg | 81 - source/AllToLua.sh | 2 - source/Authenticator.cpp | 267 - source/Authenticator.h | 93 - source/Bindings.cpp | 31485 ------ source/Bindings.h | 8 - source/BlockArea.cpp | 2124 - source/BlockArea.h | 310 - source/BlockEntities/BlockEntity.cpp | 44 - source/BlockEntities/BlockEntity.h | 101 - source/BlockEntities/BlockEntityWithItems.h | 86 - source/BlockEntities/ChestEntity.cpp | 172 - source/BlockEntities/ChestEntity.h | 59 - source/BlockEntities/DispenserEntity.cpp | 215 - source/BlockEntities/DispenserEntity.h | 38 - source/BlockEntities/DropSpenserEntity.cpp | 266 - source/BlockEntities/DropSpenserEntity.h | 89 - source/BlockEntities/DropperEntity.cpp | 32 - source/BlockEntities/DropperEntity.h | 46 - source/BlockEntities/FurnaceEntity.cpp | 479 - source/BlockEntities/FurnaceEntity.h | 164 - source/BlockEntities/HopperEntity.cpp | 566 - source/BlockEntities/HopperEntity.h | 96 - source/BlockEntities/JukeboxEntity.cpp | 125 - source/BlockEntities/JukeboxEntity.h | 56 - source/BlockEntities/NoteEntity.cpp | 154 - source/BlockEntities/NoteEntity.h | 63 - source/BlockEntities/SignEntity.cpp | 115 - source/BlockEntities/SignEntity.h | 67 - source/BlockID.cpp | 954 - source/BlockID.h | 907 - source/BlockTracer.h | 104 - source/Blocks/BlockBed.cpp | 88 - source/Blocks/BlockBed.h | 67 - source/Blocks/BlockBrewingStand.h | 32 - source/Blocks/BlockButton.cpp | 39 - source/Blocks/BlockButton.h | 69 - source/Blocks/BlockCactus.h | 82 - source/Blocks/BlockCarpet.h | 60 - source/Blocks/BlockCauldron.h | 59 - source/Blocks/BlockChest.h | 223 - source/Blocks/BlockCloth.h | 34 - source/Blocks/BlockCobWeb.h | 30 - source/Blocks/BlockComparator.cpp | 53 - source/Blocks/BlockComparator.h | 55 - source/Blocks/BlockCrops.h | 114 - source/Blocks/BlockDeadBush.h | 35 - source/Blocks/BlockDirt.h | 88 - source/Blocks/BlockDoor.cpp | 90 - source/Blocks/BlockDoor.h | 175 - source/Blocks/BlockDropSpenser.h | 41 - source/Blocks/BlockEnderchest.h | 28 - source/Blocks/BlockEntity.h | 31 - source/Blocks/BlockFarmland.h | 107 - source/Blocks/BlockFenceGate.h | 88 - source/Blocks/BlockFire.h | 228 - source/Blocks/BlockFlower.h | 41 - source/Blocks/BlockFlowerPot.h | 105 - source/Blocks/BlockFluid.h | 56 - source/Blocks/BlockFurnace.h | 47 - source/Blocks/BlockGlass.h | 26 - source/Blocks/BlockGlowstone.h | 30 - source/Blocks/BlockGravel.h | 27 - source/Blocks/BlockHandler.cpp | 465 - source/Blocks/BlockHandler.h | 152 - source/Blocks/BlockHopper.h | 46 - source/Blocks/BlockIce.h | 37 - source/Blocks/BlockLadder.h | 115 - source/Blocks/BlockLeaves.h | 184 - source/Blocks/BlockLever.cpp | 38 - source/Blocks/BlockLever.h | 53 - source/Blocks/BlockMelon.h | 35 - source/Blocks/BlockMushroom.h | 59 - source/Blocks/BlockMycelium.h | 32 - source/Blocks/BlockNote.h | 13 - source/Blocks/BlockOre.h | 80 - source/Blocks/BlockPiston.cpp | 102 - source/Blocks/BlockPiston.h | 43 - source/Blocks/BlockPlanks.h | 41 - source/Blocks/BlockPortal.h | 108 - source/Blocks/BlockPumpkin.h | 60 - source/Blocks/BlockRail.h | 398 - source/Blocks/BlockRedstone.cpp | 27 - source/Blocks/BlockRedstone.h | 35 - source/Blocks/BlockRedstoneRepeater.cpp | 51 - source/Blocks/BlockRedstoneRepeater.h | 55 - source/Blocks/BlockRedstoneTorch.h | 36 - source/Blocks/BlockSand.h | 28 - source/Blocks/BlockSapling.h | 57 - source/Blocks/BlockSign.h | 78 - source/Blocks/BlockSlab.h | 182 - source/Blocks/BlockSnow.h | 72 - source/Blocks/BlockStairs.h | 152 - source/Blocks/BlockStems.h | 58 - source/Blocks/BlockStone.h | 29 - source/Blocks/BlockSugarcane.h | 90 - source/Blocks/BlockTallGrass.h | 51 - source/Blocks/BlockTorch.h | 277 - source/Blocks/BlockVine.h | 201 - source/Blocks/BlockWood.h | 72 - source/Blocks/BlockWorkbench.h | 43 - source/BoundingBox.cpp | 331 - source/BoundingBox.h | 90 - source/ByteBuffer.cpp | 787 - source/ByteBuffer.h | 137 - source/ChatColor.cpp | 39 - source/ChatColor.h | 43 - source/Chunk.cpp | 2732 - source/Chunk.h | 475 - source/Chunk.inl.h | 34 - source/ChunkDef.h | 617 - source/ChunkMap.cpp | 2668 - source/ChunkMap.h | 432 - source/ChunkSender.cpp | 295 - source/ChunkSender.h | 169 - source/ClientHandle.cpp | 2198 - source/ClientHandle.h | 331 - source/CommandOutput.cpp | 71 - source/CommandOutput.h | 82 - source/CraftingRecipes.cpp | 770 - source/CraftingRecipes.h | 172 - source/Cuboid.cpp | 117 - source/Cuboid.h | 75 - source/DeadlockDetect.cpp | 147 - source/DeadlockDetect.h | 65 - source/Defines.h | 562 - source/Enchantments.cpp | 299 - source/Enchantments.h | 115 - source/Endianness.h | 70 - source/Entities/Boat.cpp | 87 - source/Entities/Boat.h | 37 - source/Entities/Entity.cpp | 1450 - source/Entities/Entity.h | 445 - source/Entities/FallingBlock.cpp | 93 - source/Entities/FallingBlock.h | 43 - source/Entities/Minecart.cpp | 541 - source/Entities/Minecart.h | 169 - source/Entities/Pawn.cpp | 19 - source/Entities/Pawn.h | 28 - source/Entities/Pickup.cpp | 166 - source/Entities/Pickup.h | 64 - source/Entities/Player.cpp | 1715 - source/Entities/Player.h | 447 - source/Entities/ProjectileEntity.cpp | 743 - source/Entities/ProjectileEntity.h | 325 - source/Entities/TNTEntity.cpp | 62 - source/Entities/TNTEntity.h | 32 - source/FastRandom.cpp | 174 - source/FastRandom.h | 57 - source/FurnaceRecipe.cpp | 255 - source/FurnaceRecipe.h | 50 - source/Generating/BioGen.cpp | 707 - source/Generating/BioGen.h | 230 - source/Generating/Caves.cpp | 970 - source/Generating/Caves.h | 102 - source/Generating/ChunkDesc.cpp | 605 - source/Generating/ChunkDesc.h | 217 - source/Generating/ChunkGenerator.cpp | 329 - source/Generating/ChunkGenerator.h | 113 - source/Generating/CompoGen.cpp | 634 - source/Generating/CompoGen.h | 182 - source/Generating/ComposableGenerator.cpp | 501 - source/Generating/ComposableGenerator.h | 181 - source/Generating/DistortedHeightmap.cpp | 444 - source/Generating/DistortedHeightmap.h | 108 - source/Generating/EndGen.cpp | 217 - source/Generating/EndGen.h | 69 - source/Generating/FinishGen.cpp | 664 - source/Generating/FinishGen.h | 185 - source/Generating/HeiGen.cpp | 390 - source/Generating/HeiGen.h | 145 - source/Generating/MineShafts.cpp | 1423 - source/Generating/MineShafts.h | 61 - source/Generating/Noise3DGenerator.cpp | 581 - source/Generating/Noise3DGenerator.h | 106 - source/Generating/Ravines.cpp | 531 - source/Generating/Ravines.h | 46 - source/Generating/StructGen.cpp | 675 - source/Generating/StructGen.h | 165 - source/Generating/Trees.cpp | 684 - source/Generating/Trees.h | 93 - source/Globals.cpp | 10 - source/Globals.h | 227 - source/Group.cpp | 37 - source/Group.h | 40 - source/GroupManager.cpp | 122 - source/GroupManager.h | 30 - source/HTTPServer/EnvelopeParser.cpp | 132 - source/HTTPServer/EnvelopeParser.h | 69 - source/HTTPServer/HTTPConnection.cpp | 247 - source/HTTPServer/HTTPConnection.h | 101 - source/HTTPServer/HTTPFormParser.cpp | 290 - source/HTTPServer/HTTPFormParser.h | 112 - source/HTTPServer/HTTPMessage.cpp | 279 - source/HTTPServer/HTTPMessage.h | 164 - source/HTTPServer/HTTPServer.cpp | 258 - source/HTTPServer/HTTPServer.h | 101 - source/HTTPServer/MultipartParser.cpp | 256 - source/HTTPServer/MultipartParser.h | 76 - source/HTTPServer/NameValueParser.cpp | 412 - source/HTTPServer/NameValueParser.h | 70 - source/Inventory.cpp | 682 - source/Inventory.h | 182 - source/Item.cpp | 261 - source/Item.h | 210 - source/ItemGrid.cpp | 665 - source/ItemGrid.h | 191 - source/Items/ItemBed.h | 56 - source/Items/ItemBoat.h | 54 - source/Items/ItemBow.h | 87 - source/Items/ItemBrewingStand.h | 41 - source/Items/ItemBucket.h | 160 - source/Items/ItemCauldron.h | 41 - source/Items/ItemCloth.h | 23 - source/Items/ItemComparator.h | 40 - source/Items/ItemDoor.h | 45 - source/Items/ItemDye.h | 44 - source/Items/ItemFlowerPot.h | 41 - source/Items/ItemFood.h | 63 - source/Items/ItemHandler.cpp | 509 - source/Items/ItemHandler.h | 99 - source/Items/ItemHoe.h | 40 - source/Items/ItemLeaves.h | 41 - source/Items/ItemLighter.h | 59 - source/Items/ItemMinecart.h | 82 - source/Items/ItemPickaxe.h | 92 - source/Items/ItemRedstoneDust.h | 38 - source/Items/ItemRedstoneRepeater.h | 40 - source/Items/ItemSapling.h | 42 - source/Items/ItemSeeds.h | 65 - source/Items/ItemShears.h | 62 - source/Items/ItemShovel.h | 41 - source/Items/ItemSign.h | 51 - source/Items/ItemSpawnEgg.h | 52 - source/Items/ItemSugarcane.h | 39 - source/Items/ItemSword.h | 30 - source/Items/ItemThrowable.h | 96 - source/LeakFinder.cpp | 1047 - source/LeakFinder.h | 156 - source/LightingThread.cpp | 562 - source/LightingThread.h | 181 - source/LineBlockTracer.cpp | 262 - source/LineBlockTracer.h | 87 - source/LinearInterpolation.cpp | 251 - source/LinearInterpolation.h | 60 - source/LinearUpscale.h | 244 - source/Log.cpp | 169 - source/Log.h | 30 - source/LuaExpat/lxplib.c | 599 - source/LuaExpat/lxplib.h | 24 - source/LuaFunctions.h | 17 - source/LuaState.cpp | 958 - source/LuaState.h | 811 - source/LuaWindow.cpp | 185 - source/LuaWindow.h | 95 - source/MCLogger.cpp | 261 - source/MCLogger.h | 84 - source/ManualBindings.cpp | 2215 - source/ManualBindings.h | 8 - source/Matrix4f.cpp | 4 - source/Matrix4f.h | 225 - source/MemoryLeak.h | 19 - source/MersenneTwister.h | 456 - source/MobCensus.cpp | 92 - source/MobCensus.h | 59 - source/MobFamilyCollecter.cpp | 26 - source/MobFamilyCollecter.h | 39 - source/MobProximityCounter.cpp | 83 - source/MobProximityCounter.h | 65 - source/MobSpawner.cpp | 361 - source/MobSpawner.h | 76 - source/Mobs/AggressiveMonster.cpp | 97 - source/Mobs/AggressiveMonster.h | 30 - source/Mobs/Bat.cpp | 15 - source/Mobs/Bat.h | 25 - source/Mobs/Blaze.cpp | 52 - source/Mobs/Blaze.h | 26 - source/Mobs/Cavespider.cpp | 40 - source/Mobs/Cavespider.h | 26 - source/Mobs/Chicken.cpp | 62 - source/Mobs/Chicken.h | 29 - source/Mobs/Cow.cpp | 45 - source/Mobs/Cow.h | 26 - source/Mobs/Creeper.cpp | 47 - source/Mobs/Creeper.h | 34 - source/Mobs/EnderDragon.cpp | 27 - source/Mobs/EnderDragon.h | 25 - source/Mobs/Enderman.cpp | 29 - source/Mobs/Enderman.h | 36 - source/Mobs/Ghast.cpp | 54 - source/Mobs/Ghast.h | 28 - source/Mobs/Giant.cpp | 27 - source/Mobs/Giant.h | 25 - source/Mobs/Horse.cpp | 152 - source/Mobs/Horse.h | 44 - source/Mobs/IncludeAllMonsters.h | 29 - source/Mobs/IronGolem.cpp | 26 - source/Mobs/IronGolem.h | 25 - source/Mobs/Magmacube.cpp | 27 - source/Mobs/Magmacube.h | 32 - source/Mobs/Monster.cpp | 758 - source/Mobs/Monster.h | 195 - source/Mobs/Mooshroom.cpp | 33 - source/Mobs/Mooshroom.h | 25 - source/Mobs/Ocelot.h | 26 - source/Mobs/PassiveAggressiveMonster.cpp | 38 - source/Mobs/PassiveAggressiveMonster.h | 23 - source/Mobs/PassiveMonster.cpp | 59 - source/Mobs/PassiveMonster.h | 27 - source/Mobs/Pig.cpp | 77 - source/Mobs/Pig.h | 32 - source/Mobs/Sheep.cpp | 62 - source/Mobs/Sheep.h | 34 - source/Mobs/Silverfish.h | 26 - source/Mobs/Skeleton.cpp | 70 - source/Mobs/Skeleton.h | 33 - source/Mobs/Slime.cpp | 29 - source/Mobs/Slime.h | 32 - source/Mobs/SnowGolem.cpp | 26 - source/Mobs/SnowGolem.h | 25 - source/Mobs/Spider.cpp | 27 - source/Mobs/Spider.h | 25 - source/Mobs/Squid.cpp | 56 - source/Mobs/Squid.h | 28 - source/Mobs/Villager.cpp | 35 - source/Mobs/Villager.h | 43 - source/Mobs/Witch.cpp | 32 - source/Mobs/Witch.h | 27 - source/Mobs/Wither.cpp | 26 - source/Mobs/Wither.h | 25 - source/Mobs/Wolf.cpp | 189 - source/Mobs/Wolf.h | 54 - source/Mobs/Zombie.cpp | 47 - source/Mobs/Zombie.h | 33 - source/Mobs/Zombiepigman.cpp | 45 - source/Mobs/Zombiepigman.h | 26 - source/MonsterConfig.cpp | 104 - source/MonsterConfig.h | 32 - source/Noise.cpp | 951 - source/Noise.h | 308 - source/OSSupport/BlockingTCPLink.cpp | 149 - source/OSSupport/BlockingTCPLink.h | 28 - source/OSSupport/CriticalSection.cpp | 188 - source/OSSupport/CriticalSection.h | 80 - source/OSSupport/Event.cpp | 118 - source/OSSupport/Event.h | 47 - source/OSSupport/File.cpp | 375 - source/OSSupport/File.h | 138 - source/OSSupport/GZipFile.cpp | 107 - source/OSSupport/GZipFile.h | 52 - source/OSSupport/IsThread.cpp | 172 - source/OSSupport/IsThread.h | 100 - source/OSSupport/ListenThread.cpp | 238 - source/OSSupport/ListenThread.h | 83 - source/OSSupport/Semaphore.cpp | 91 - source/OSSupport/Semaphore.h | 17 - source/OSSupport/Sleep.cpp | 19 - source/OSSupport/Sleep.h | 7 - source/OSSupport/Socket.cpp | 396 - source/OSSupport/Socket.h | 101 - source/OSSupport/SocketThreads.cpp | 675 - source/OSSupport/SocketThreads.h | 169 - source/OSSupport/Thread.cpp | 128 - source/OSSupport/Thread.h | 26 - source/OSSupport/Timer.cpp | 37 - source/OSSupport/Timer.h | 32 - source/Piston.cpp | 306 - source/Piston.h | 94 - source/Plugin.cpp | 38 - source/Plugin.h | 149 - source/PluginLua.cpp | 1471 - source/PluginLua.h | 202 - source/PluginManager.cpp | 1664 - source/PluginManager.h | 295 - source/ProbabDistrib.cpp | 142 - source/ProbabDistrib.h | 74 - source/Protocol/ChunkDataSerializer.cpp | 176 - source/Protocol/ChunkDataSerializer.h | 48 - source/Protocol/Protocol.h | 215 - source/Protocol/Protocol125.cpp | 1869 - source/Protocol/Protocol125.h | 157 - source/Protocol/Protocol132.cpp | 950 - source/Protocol/Protocol132.h | 102 - source/Protocol/Protocol14x.cpp | 256 - source/Protocol/Protocol14x.h | 63 - source/Protocol/Protocol15x.cpp | 138 - source/Protocol/Protocol15x.h | 38 - source/Protocol/Protocol16x.cpp | 268 - source/Protocol/Protocol16x.h | 76 - source/Protocol/Protocol17x.cpp | 1917 - source/Protocol/Protocol17x.h | 258 - source/Protocol/ProtocolRecognizer.cpp | 905 - source/Protocol/ProtocolRecognizer.h | 152 - source/RCONServer.cpp | 332 - source/RCONServer.h | 109 - source/ReferenceManager.cpp | 43 - source/ReferenceManager.h | 34 - source/Root.cpp | 744 - source/Root.h | 186 - source/SQLite/lsqlite3.c | 2175 - source/SQLite/sqlite3.c | 138114 ------------------------- source/SQLite/sqlite3.h | 7174 -- source/SQLite/urls.txt | 9 - source/Server.cpp | 707 - source/Server.h | 195 - source/Simulator/DelayedFluidSimulator.cpp | 158 - source/Simulator/DelayedFluidSimulator.h | 82 - source/Simulator/FireSimulator.cpp | 374 - source/Simulator/FireSimulator.h | 75 - source/Simulator/FloodyFluidSimulator.cpp | 330 - source/Simulator/FloodyFluidSimulator.h | 53 - source/Simulator/FluidSimulator.cpp | 212 - source/Simulator/FluidSimulator.h | 75 - source/Simulator/NoopFluidSimulator.h | 36 - source/Simulator/RedstoneSimulator.cpp | 1178 - source/Simulator/RedstoneSimulator.h | 86 - source/Simulator/SandSimulator.cpp | 309 - source/Simulator/SandSimulator.h | 63 - source/Simulator/Simulator.cpp | 51 - source/Simulator/Simulator.h | 46 - source/Simulator/SimulatorManager.cpp | 80 - source/Simulator/SimulatorManager.h | 52 - source/Simulator/VaporizeFluidSimulator.cpp | 53 - source/Simulator/VaporizeFluidSimulator.h | 34 - source/StackWalker.cpp | 1345 - source/StackWalker.h | 214 - source/StringCompression.cpp | 180 - source/StringCompression.h | 25 - source/StringUtils.cpp | 815 - source/StringUtils.h | 96 - source/Tracer.cpp | 398 - source/Tracer.h | 82 - source/UI/SlotArea.cpp | 897 - source/UI/SlotArea.h | 313 - source/UI/Window.cpp | 886 - source/UI/Window.h | 300 - source/UI/WindowOwner.h | 125 - source/Vector3d.cpp | 77 - source/Vector3d.h | 81 - source/Vector3f.cpp | 34 - source/Vector3f.h | 47 - source/Vector3i.cpp | 16 - source/Vector3i.h | 45 - source/WebAdmin.cpp | 527 - source/WebAdmin.h | 215 - source/WebPlugin.cpp | 113 - source/WebPlugin.h | 48 - source/World.cpp | 2715 - source/World.h | 744 - source/WorldStorage/FastNBT.cpp | 547 - source/WorldStorage/FastNBT.h | 293 - source/WorldStorage/NBTChunkSerializer.cpp | 533 - source/WorldStorage/NBTChunkSerializer.h | 116 - source/WorldStorage/WSSAnvil.cpp | 1555 - source/WorldStorage/WSSAnvil.h | 184 - source/WorldStorage/WSSCompact.cpp | 1009 - source/WorldStorage/WSSCompact.h | 144 - source/WorldStorage/WorldStorage.cpp | 409 - source/WorldStorage/WorldStorage.h | 135 - source/XMLParser.h | 701 - source/lua5.1.dll | Bin 167424 -> 0 bytes source/main.cpp | 197 - source/md5/md5.cpp | 369 - source/md5/md5.h | 93 - source/tolua++.exe | Bin 484864 -> 0 bytes source/tolua++.h | 186 - source/tolua_base.h | 128 - source/virtual_method_hooks.lua | 506 - src/AllToLua.bat | 27 + src/AllToLua.pkg | 81 + src/AllToLua.sh | 2 + src/Authenticator.cpp | 267 + src/Authenticator.h | 93 + src/Bindings.cpp | 31485 ++++++ src/Bindings.h | 8 + src/BlockArea.cpp | 2124 + src/BlockArea.h | 310 + src/BlockEntities/BlockEntity.cpp | 44 + src/BlockEntities/BlockEntity.h | 101 + src/BlockEntities/BlockEntityWithItems.h | 86 + src/BlockEntities/ChestEntity.cpp | 172 + src/BlockEntities/ChestEntity.h | 59 + src/BlockEntities/DispenserEntity.cpp | 215 + src/BlockEntities/DispenserEntity.h | 38 + src/BlockEntities/DropSpenserEntity.cpp | 266 + src/BlockEntities/DropSpenserEntity.h | 89 + src/BlockEntities/DropperEntity.cpp | 32 + src/BlockEntities/DropperEntity.h | 46 + src/BlockEntities/FurnaceEntity.cpp | 479 + src/BlockEntities/FurnaceEntity.h | 164 + src/BlockEntities/HopperEntity.cpp | 566 + src/BlockEntities/HopperEntity.h | 96 + src/BlockEntities/JukeboxEntity.cpp | 125 + src/BlockEntities/JukeboxEntity.h | 56 + src/BlockEntities/NoteEntity.cpp | 154 + src/BlockEntities/NoteEntity.h | 63 + src/BlockEntities/SignEntity.cpp | 115 + src/BlockEntities/SignEntity.h | 67 + src/BlockID.cpp | 954 + src/BlockID.h | 907 + src/BlockTracer.h | 104 + src/Blocks/BlockBed.cpp | 88 + src/Blocks/BlockBed.h | 67 + src/Blocks/BlockBrewingStand.h | 32 + src/Blocks/BlockButton.cpp | 39 + src/Blocks/BlockButton.h | 69 + src/Blocks/BlockCactus.h | 82 + src/Blocks/BlockCarpet.h | 60 + src/Blocks/BlockCauldron.h | 59 + src/Blocks/BlockChest.h | 223 + src/Blocks/BlockCloth.h | 34 + src/Blocks/BlockCobWeb.h | 30 + src/Blocks/BlockComparator.cpp | 53 + src/Blocks/BlockComparator.h | 55 + src/Blocks/BlockCrops.h | 114 + src/Blocks/BlockDeadBush.h | 35 + src/Blocks/BlockDirt.h | 88 + src/Blocks/BlockDoor.cpp | 90 + src/Blocks/BlockDoor.h | 175 + src/Blocks/BlockDropSpenser.h | 41 + src/Blocks/BlockEnderchest.h | 28 + src/Blocks/BlockEntity.h | 31 + src/Blocks/BlockFarmland.h | 107 + src/Blocks/BlockFenceGate.h | 88 + src/Blocks/BlockFire.h | 228 + src/Blocks/BlockFlower.h | 41 + src/Blocks/BlockFlowerPot.h | 105 + src/Blocks/BlockFluid.h | 56 + src/Blocks/BlockFurnace.h | 47 + src/Blocks/BlockGlass.h | 26 + src/Blocks/BlockGlowstone.h | 30 + src/Blocks/BlockGravel.h | 27 + src/Blocks/BlockHandler.cpp | 465 + src/Blocks/BlockHandler.h | 152 + src/Blocks/BlockHopper.h | 46 + src/Blocks/BlockIce.h | 37 + src/Blocks/BlockLadder.h | 115 + src/Blocks/BlockLeaves.h | 184 + src/Blocks/BlockLever.cpp | 38 + src/Blocks/BlockLever.h | 53 + src/Blocks/BlockMelon.h | 35 + src/Blocks/BlockMushroom.h | 59 + src/Blocks/BlockMycelium.h | 32 + src/Blocks/BlockNote.h | 13 + src/Blocks/BlockOre.h | 80 + src/Blocks/BlockPiston.cpp | 102 + src/Blocks/BlockPiston.h | 43 + src/Blocks/BlockPlanks.h | 41 + src/Blocks/BlockPortal.h | 108 + src/Blocks/BlockPumpkin.h | 60 + src/Blocks/BlockRail.h | 398 + src/Blocks/BlockRedstone.cpp | 27 + src/Blocks/BlockRedstone.h | 35 + src/Blocks/BlockRedstoneRepeater.cpp | 51 + src/Blocks/BlockRedstoneRepeater.h | 55 + src/Blocks/BlockRedstoneTorch.h | 36 + src/Blocks/BlockSand.h | 28 + src/Blocks/BlockSapling.h | 57 + src/Blocks/BlockSign.h | 78 + src/Blocks/BlockSlab.h | 182 + src/Blocks/BlockSnow.h | 72 + src/Blocks/BlockStairs.h | 152 + src/Blocks/BlockStems.h | 58 + src/Blocks/BlockStone.h | 29 + src/Blocks/BlockSugarcane.h | 90 + src/Blocks/BlockTallGrass.h | 51 + src/Blocks/BlockTorch.h | 277 + src/Blocks/BlockVine.h | 201 + src/Blocks/BlockWood.h | 72 + src/Blocks/BlockWorkbench.h | 43 + src/BoundingBox.cpp | 331 + src/BoundingBox.h | 90 + src/ByteBuffer.cpp | 787 + src/ByteBuffer.h | 137 + src/ChatColor.cpp | 39 + src/ChatColor.h | 43 + src/Chunk.cpp | 2732 + src/Chunk.h | 475 + src/Chunk.inl.h | 34 + src/ChunkDef.h | 617 + src/ChunkMap.cpp | 2668 + src/ChunkMap.h | 432 + src/ChunkSender.cpp | 295 + src/ChunkSender.h | 169 + src/ClientHandle.cpp | 2198 + src/ClientHandle.h | 331 + src/CommandOutput.cpp | 71 + src/CommandOutput.h | 82 + src/CraftingRecipes.cpp | 770 + src/CraftingRecipes.h | 172 + src/Cuboid.cpp | 117 + src/Cuboid.h | 75 + src/DeadlockDetect.cpp | 147 + src/DeadlockDetect.h | 65 + src/Defines.h | 562 + src/Enchantments.cpp | 299 + src/Enchantments.h | 115 + src/Endianness.h | 70 + src/Entities/Boat.cpp | 87 + src/Entities/Boat.h | 37 + src/Entities/Entity.cpp | 1450 + src/Entities/Entity.h | 445 + src/Entities/FallingBlock.cpp | 93 + src/Entities/FallingBlock.h | 43 + src/Entities/Minecart.cpp | 541 + src/Entities/Minecart.h | 169 + src/Entities/Pawn.cpp | 19 + src/Entities/Pawn.h | 28 + src/Entities/Pickup.cpp | 166 + src/Entities/Pickup.h | 64 + src/Entities/Player.cpp | 1715 + src/Entities/Player.h | 447 + src/Entities/ProjectileEntity.cpp | 743 + src/Entities/ProjectileEntity.h | 325 + src/Entities/TNTEntity.cpp | 62 + src/Entities/TNTEntity.h | 32 + src/FastRandom.cpp | 174 + src/FastRandom.h | 57 + src/FurnaceRecipe.cpp | 255 + src/FurnaceRecipe.h | 50 + src/Generating/BioGen.cpp | 707 + src/Generating/BioGen.h | 230 + src/Generating/Caves.cpp | 970 + src/Generating/Caves.h | 102 + src/Generating/ChunkDesc.cpp | 605 + src/Generating/ChunkDesc.h | 217 + src/Generating/ChunkGenerator.cpp | 329 + src/Generating/ChunkGenerator.h | 113 + src/Generating/CompoGen.cpp | 634 + src/Generating/CompoGen.h | 182 + src/Generating/ComposableGenerator.cpp | 501 + src/Generating/ComposableGenerator.h | 181 + src/Generating/DistortedHeightmap.cpp | 444 + src/Generating/DistortedHeightmap.h | 108 + src/Generating/EndGen.cpp | 217 + src/Generating/EndGen.h | 69 + src/Generating/FinishGen.cpp | 664 + src/Generating/FinishGen.h | 185 + src/Generating/HeiGen.cpp | 390 + src/Generating/HeiGen.h | 145 + src/Generating/MineShafts.cpp | 1423 + src/Generating/MineShafts.h | 61 + src/Generating/Noise3DGenerator.cpp | 581 + src/Generating/Noise3DGenerator.h | 106 + src/Generating/Ravines.cpp | 531 + src/Generating/Ravines.h | 46 + src/Generating/StructGen.cpp | 675 + src/Generating/StructGen.h | 165 + src/Generating/Trees.cpp | 684 + src/Generating/Trees.h | 93 + src/Globals.cpp | 10 + src/Globals.h | 227 + src/Group.cpp | 37 + src/Group.h | 40 + src/GroupManager.cpp | 122 + src/GroupManager.h | 30 + src/HTTPServer/EnvelopeParser.cpp | 132 + src/HTTPServer/EnvelopeParser.h | 69 + src/HTTPServer/HTTPConnection.cpp | 247 + src/HTTPServer/HTTPConnection.h | 101 + src/HTTPServer/HTTPFormParser.cpp | 290 + src/HTTPServer/HTTPFormParser.h | 112 + src/HTTPServer/HTTPMessage.cpp | 279 + src/HTTPServer/HTTPMessage.h | 164 + src/HTTPServer/HTTPServer.cpp | 258 + src/HTTPServer/HTTPServer.h | 101 + src/HTTPServer/MultipartParser.cpp | 256 + src/HTTPServer/MultipartParser.h | 76 + src/HTTPServer/NameValueParser.cpp | 412 + src/HTTPServer/NameValueParser.h | 70 + src/Inventory.cpp | 682 + src/Inventory.h | 182 + src/Item.cpp | 261 + src/Item.h | 210 + src/ItemGrid.cpp | 665 + src/ItemGrid.h | 191 + src/Items/ItemBed.h | 56 + src/Items/ItemBoat.h | 54 + src/Items/ItemBow.h | 87 + src/Items/ItemBrewingStand.h | 41 + src/Items/ItemBucket.h | 160 + src/Items/ItemCauldron.h | 41 + src/Items/ItemCloth.h | 23 + src/Items/ItemComparator.h | 40 + src/Items/ItemDoor.h | 45 + src/Items/ItemDye.h | 44 + src/Items/ItemFlowerPot.h | 41 + src/Items/ItemFood.h | 63 + src/Items/ItemHandler.cpp | 509 + src/Items/ItemHandler.h | 99 + src/Items/ItemHoe.h | 40 + src/Items/ItemLeaves.h | 41 + src/Items/ItemLighter.h | 59 + src/Items/ItemMinecart.h | 82 + src/Items/ItemPickaxe.h | 92 + src/Items/ItemRedstoneDust.h | 38 + src/Items/ItemRedstoneRepeater.h | 40 + src/Items/ItemSapling.h | 42 + src/Items/ItemSeeds.h | 65 + src/Items/ItemShears.h | 62 + src/Items/ItemShovel.h | 41 + src/Items/ItemSign.h | 51 + src/Items/ItemSpawnEgg.h | 52 + src/Items/ItemSugarcane.h | 39 + src/Items/ItemSword.h | 30 + src/Items/ItemThrowable.h | 96 + src/LeakFinder.cpp | 1047 + src/LeakFinder.h | 156 + src/LightingThread.cpp | 562 + src/LightingThread.h | 181 + src/LineBlockTracer.cpp | 262 + src/LineBlockTracer.h | 87 + src/LinearInterpolation.cpp | 251 + src/LinearInterpolation.h | 60 + src/LinearUpscale.h | 244 + src/Log.cpp | 169 + src/Log.h | 30 + src/LuaExpat/lxplib.c | 599 + src/LuaExpat/lxplib.h | 24 + src/LuaFunctions.h | 17 + src/LuaState.cpp | 958 + src/LuaState.h | 811 + src/LuaWindow.cpp | 185 + src/LuaWindow.h | 95 + src/MCLogger.cpp | 261 + src/MCLogger.h | 84 + src/ManualBindings.cpp | 2215 + src/ManualBindings.h | 8 + src/Matrix4f.cpp | 4 + src/Matrix4f.h | 225 + src/MemoryLeak.h | 19 + src/MersenneTwister.h | 456 + src/MobCensus.cpp | 92 + src/MobCensus.h | 59 + src/MobFamilyCollecter.cpp | 26 + src/MobFamilyCollecter.h | 39 + src/MobProximityCounter.cpp | 83 + src/MobProximityCounter.h | 65 + src/MobSpawner.cpp | 361 + src/MobSpawner.h | 76 + src/Mobs/AggressiveMonster.cpp | 97 + src/Mobs/AggressiveMonster.h | 30 + src/Mobs/Bat.cpp | 15 + src/Mobs/Bat.h | 25 + src/Mobs/Blaze.cpp | 52 + src/Mobs/Blaze.h | 26 + src/Mobs/Cavespider.cpp | 40 + src/Mobs/Cavespider.h | 26 + src/Mobs/Chicken.cpp | 62 + src/Mobs/Chicken.h | 29 + src/Mobs/Cow.cpp | 45 + src/Mobs/Cow.h | 26 + src/Mobs/Creeper.cpp | 47 + src/Mobs/Creeper.h | 34 + src/Mobs/EnderDragon.cpp | 27 + src/Mobs/EnderDragon.h | 25 + src/Mobs/Enderman.cpp | 29 + src/Mobs/Enderman.h | 36 + src/Mobs/Ghast.cpp | 54 + src/Mobs/Ghast.h | 28 + src/Mobs/Giant.cpp | 27 + src/Mobs/Giant.h | 25 + src/Mobs/Horse.cpp | 152 + src/Mobs/Horse.h | 44 + src/Mobs/IncludeAllMonsters.h | 29 + src/Mobs/IronGolem.cpp | 26 + src/Mobs/IronGolem.h | 25 + src/Mobs/Magmacube.cpp | 27 + src/Mobs/Magmacube.h | 32 + src/Mobs/Monster.cpp | 758 + src/Mobs/Monster.h | 195 + src/Mobs/Mooshroom.cpp | 33 + src/Mobs/Mooshroom.h | 25 + src/Mobs/Ocelot.h | 26 + src/Mobs/PassiveAggressiveMonster.cpp | 38 + src/Mobs/PassiveAggressiveMonster.h | 23 + src/Mobs/PassiveMonster.cpp | 59 + src/Mobs/PassiveMonster.h | 27 + src/Mobs/Pig.cpp | 77 + src/Mobs/Pig.h | 32 + src/Mobs/Sheep.cpp | 62 + src/Mobs/Sheep.h | 34 + src/Mobs/Silverfish.h | 26 + src/Mobs/Skeleton.cpp | 70 + src/Mobs/Skeleton.h | 33 + src/Mobs/Slime.cpp | 29 + src/Mobs/Slime.h | 32 + src/Mobs/SnowGolem.cpp | 26 + src/Mobs/SnowGolem.h | 25 + src/Mobs/Spider.cpp | 27 + src/Mobs/Spider.h | 25 + src/Mobs/Squid.cpp | 56 + src/Mobs/Squid.h | 28 + src/Mobs/Villager.cpp | 35 + src/Mobs/Villager.h | 43 + src/Mobs/Witch.cpp | 32 + src/Mobs/Witch.h | 27 + src/Mobs/Wither.cpp | 26 + src/Mobs/Wither.h | 25 + src/Mobs/Wolf.cpp | 189 + src/Mobs/Wolf.h | 54 + src/Mobs/Zombie.cpp | 47 + src/Mobs/Zombie.h | 33 + src/Mobs/Zombiepigman.cpp | 45 + src/Mobs/Zombiepigman.h | 26 + src/MonsterConfig.cpp | 104 + src/MonsterConfig.h | 32 + src/Noise.cpp | 951 + src/Noise.h | 308 + src/OSSupport/BlockingTCPLink.cpp | 149 + src/OSSupport/BlockingTCPLink.h | 28 + src/OSSupport/CriticalSection.cpp | 188 + src/OSSupport/CriticalSection.h | 80 + src/OSSupport/Event.cpp | 118 + src/OSSupport/Event.h | 47 + src/OSSupport/File.cpp | 375 + src/OSSupport/File.h | 138 + src/OSSupport/GZipFile.cpp | 107 + src/OSSupport/GZipFile.h | 52 + src/OSSupport/IsThread.cpp | 172 + src/OSSupport/IsThread.h | 100 + src/OSSupport/ListenThread.cpp | 238 + src/OSSupport/ListenThread.h | 83 + src/OSSupport/Semaphore.cpp | 91 + src/OSSupport/Semaphore.h | 17 + src/OSSupport/Sleep.cpp | 19 + src/OSSupport/Sleep.h | 7 + src/OSSupport/Socket.cpp | 396 + src/OSSupport/Socket.h | 101 + src/OSSupport/SocketThreads.cpp | 675 + src/OSSupport/SocketThreads.h | 169 + src/OSSupport/Thread.cpp | 128 + src/OSSupport/Thread.h | 26 + src/OSSupport/Timer.cpp | 37 + src/OSSupport/Timer.h | 32 + src/Piston.cpp | 306 + src/Piston.h | 94 + src/Plugin.cpp | 38 + src/Plugin.h | 149 + src/PluginLua.cpp | 1471 + src/PluginLua.h | 202 + src/PluginManager.cpp | 1664 + src/PluginManager.h | 295 + src/ProbabDistrib.cpp | 142 + src/ProbabDistrib.h | 74 + src/Protocol/ChunkDataSerializer.cpp | 176 + src/Protocol/ChunkDataSerializer.h | 48 + src/Protocol/Protocol.h | 215 + src/Protocol/Protocol125.cpp | 1869 + src/Protocol/Protocol125.h | 157 + src/Protocol/Protocol132.cpp | 950 + src/Protocol/Protocol132.h | 102 + src/Protocol/Protocol14x.cpp | 256 + src/Protocol/Protocol14x.h | 63 + src/Protocol/Protocol15x.cpp | 138 + src/Protocol/Protocol15x.h | 38 + src/Protocol/Protocol16x.cpp | 268 + src/Protocol/Protocol16x.h | 76 + src/Protocol/Protocol17x.cpp | 1917 + src/Protocol/Protocol17x.h | 258 + src/Protocol/ProtocolRecognizer.cpp | 905 + src/Protocol/ProtocolRecognizer.h | 152 + src/RCONServer.cpp | 332 + src/RCONServer.h | 109 + src/ReferenceManager.cpp | 43 + src/ReferenceManager.h | 34 + src/Root.cpp | 744 + src/Root.h | 186 + src/SQLite/lsqlite3.c | 2175 + src/SQLite/sqlite3.c | 138114 +++++++++++++++++++++++++ src/SQLite/sqlite3.h | 7174 ++ src/SQLite/urls.txt | 9 + src/Server.cpp | 707 + src/Server.h | 195 + src/Simulator/DelayedFluidSimulator.cpp | 158 + src/Simulator/DelayedFluidSimulator.h | 82 + src/Simulator/FireSimulator.cpp | 374 + src/Simulator/FireSimulator.h | 75 + src/Simulator/FloodyFluidSimulator.cpp | 330 + src/Simulator/FloodyFluidSimulator.h | 53 + src/Simulator/FluidSimulator.cpp | 212 + src/Simulator/FluidSimulator.h | 75 + src/Simulator/NoopFluidSimulator.h | 36 + src/Simulator/RedstoneSimulator.cpp | 1178 + src/Simulator/RedstoneSimulator.h | 86 + src/Simulator/SandSimulator.cpp | 309 + src/Simulator/SandSimulator.h | 63 + src/Simulator/Simulator.cpp | 51 + src/Simulator/Simulator.h | 46 + src/Simulator/SimulatorManager.cpp | 80 + src/Simulator/SimulatorManager.h | 52 + src/Simulator/VaporizeFluidSimulator.cpp | 53 + src/Simulator/VaporizeFluidSimulator.h | 34 + src/StackWalker.cpp | 1345 + src/StackWalker.h | 214 + src/StringCompression.cpp | 180 + src/StringCompression.h | 25 + src/StringUtils.cpp | 815 + src/StringUtils.h | 96 + src/Tracer.cpp | 398 + src/Tracer.h | 82 + src/UI/SlotArea.cpp | 897 + src/UI/SlotArea.h | 313 + src/UI/Window.cpp | 886 + src/UI/Window.h | 300 + src/UI/WindowOwner.h | 125 + src/Vector3d.cpp | 77 + src/Vector3d.h | 81 + src/Vector3f.cpp | 34 + src/Vector3f.h | 47 + src/Vector3i.cpp | 16 + src/Vector3i.h | 45 + src/WebAdmin.cpp | 527 + src/WebAdmin.h | 215 + src/WebPlugin.cpp | 113 + src/WebPlugin.h | 48 + src/World.cpp | 2715 + src/World.h | 744 + src/WorldStorage/FastNBT.cpp | 547 + src/WorldStorage/FastNBT.h | 293 + src/WorldStorage/NBTChunkSerializer.cpp | 533 + src/WorldStorage/NBTChunkSerializer.h | 116 + src/WorldStorage/WSSAnvil.cpp | 1555 + src/WorldStorage/WSSAnvil.h | 184 + src/WorldStorage/WSSCompact.cpp | 1009 + src/WorldStorage/WSSCompact.h | 144 + src/WorldStorage/WorldStorage.cpp | 409 + src/WorldStorage/WorldStorage.h | 135 + src/XMLParser.h | 701 + src/lua5.1.dll | Bin 0 -> 167424 bytes src/main.cpp | 197 + src/md5/md5.cpp | 369 + src/md5/md5.h | 93 + src/tolua++.exe | Bin 0 -> 484864 bytes src/tolua++.h | 186 + src/tolua_base.h | 128 + src/virtual_method_hooks.lua | 506 + 938 files changed, 285880 insertions(+), 285880 deletions(-) delete mode 100644 source/AllToLua.bat delete mode 100644 source/AllToLua.pkg delete mode 100755 source/AllToLua.sh delete mode 100644 source/Authenticator.cpp delete mode 100644 source/Authenticator.h delete mode 100644 source/Bindings.cpp delete mode 100644 source/Bindings.h delete mode 100644 source/BlockArea.cpp delete mode 100644 source/BlockArea.h delete mode 100644 source/BlockEntities/BlockEntity.cpp delete mode 100644 source/BlockEntities/BlockEntity.h delete mode 100644 source/BlockEntities/BlockEntityWithItems.h delete mode 100644 source/BlockEntities/ChestEntity.cpp delete mode 100644 source/BlockEntities/ChestEntity.h delete mode 100644 source/BlockEntities/DispenserEntity.cpp delete mode 100644 source/BlockEntities/DispenserEntity.h delete mode 100644 source/BlockEntities/DropSpenserEntity.cpp delete mode 100644 source/BlockEntities/DropSpenserEntity.h delete mode 100644 source/BlockEntities/DropperEntity.cpp delete mode 100644 source/BlockEntities/DropperEntity.h delete mode 100644 source/BlockEntities/FurnaceEntity.cpp delete mode 100644 source/BlockEntities/FurnaceEntity.h delete mode 100644 source/BlockEntities/HopperEntity.cpp delete mode 100644 source/BlockEntities/HopperEntity.h delete mode 100644 source/BlockEntities/JukeboxEntity.cpp delete mode 100644 source/BlockEntities/JukeboxEntity.h delete mode 100644 source/BlockEntities/NoteEntity.cpp delete mode 100644 source/BlockEntities/NoteEntity.h delete mode 100644 source/BlockEntities/SignEntity.cpp delete mode 100644 source/BlockEntities/SignEntity.h delete mode 100644 source/BlockID.cpp delete mode 100644 source/BlockID.h delete mode 100644 source/BlockTracer.h delete mode 100644 source/Blocks/BlockBed.cpp delete mode 100644 source/Blocks/BlockBed.h delete mode 100644 source/Blocks/BlockBrewingStand.h delete mode 100644 source/Blocks/BlockButton.cpp delete mode 100644 source/Blocks/BlockButton.h delete mode 100644 source/Blocks/BlockCactus.h delete mode 100644 source/Blocks/BlockCarpet.h delete mode 100644 source/Blocks/BlockCauldron.h delete mode 100644 source/Blocks/BlockChest.h delete mode 100644 source/Blocks/BlockCloth.h delete mode 100644 source/Blocks/BlockCobWeb.h delete mode 100644 source/Blocks/BlockComparator.cpp delete mode 100644 source/Blocks/BlockComparator.h delete mode 100644 source/Blocks/BlockCrops.h delete mode 100644 source/Blocks/BlockDeadBush.h delete mode 100644 source/Blocks/BlockDirt.h delete mode 100644 source/Blocks/BlockDoor.cpp delete mode 100644 source/Blocks/BlockDoor.h delete mode 100644 source/Blocks/BlockDropSpenser.h delete mode 100644 source/Blocks/BlockEnderchest.h delete mode 100644 source/Blocks/BlockEntity.h delete mode 100644 source/Blocks/BlockFarmland.h delete mode 100644 source/Blocks/BlockFenceGate.h delete mode 100644 source/Blocks/BlockFire.h delete mode 100644 source/Blocks/BlockFlower.h delete mode 100644 source/Blocks/BlockFlowerPot.h delete mode 100644 source/Blocks/BlockFluid.h delete mode 100644 source/Blocks/BlockFurnace.h delete mode 100644 source/Blocks/BlockGlass.h delete mode 100644 source/Blocks/BlockGlowstone.h delete mode 100644 source/Blocks/BlockGravel.h delete mode 100644 source/Blocks/BlockHandler.cpp delete mode 100644 source/Blocks/BlockHandler.h delete mode 100644 source/Blocks/BlockHopper.h delete mode 100644 source/Blocks/BlockIce.h delete mode 100644 source/Blocks/BlockLadder.h delete mode 100644 source/Blocks/BlockLeaves.h delete mode 100644 source/Blocks/BlockLever.cpp delete mode 100644 source/Blocks/BlockLever.h delete mode 100644 source/Blocks/BlockMelon.h delete mode 100644 source/Blocks/BlockMushroom.h delete mode 100644 source/Blocks/BlockMycelium.h delete mode 100644 source/Blocks/BlockNote.h delete mode 100644 source/Blocks/BlockOre.h delete mode 100644 source/Blocks/BlockPiston.cpp delete mode 100644 source/Blocks/BlockPiston.h delete mode 100644 source/Blocks/BlockPlanks.h delete mode 100644 source/Blocks/BlockPortal.h delete mode 100644 source/Blocks/BlockPumpkin.h delete mode 100644 source/Blocks/BlockRail.h delete mode 100644 source/Blocks/BlockRedstone.cpp delete mode 100644 source/Blocks/BlockRedstone.h delete mode 100644 source/Blocks/BlockRedstoneRepeater.cpp delete mode 100644 source/Blocks/BlockRedstoneRepeater.h delete mode 100644 source/Blocks/BlockRedstoneTorch.h delete mode 100644 source/Blocks/BlockSand.h delete mode 100644 source/Blocks/BlockSapling.h delete mode 100644 source/Blocks/BlockSign.h delete mode 100644 source/Blocks/BlockSlab.h delete mode 100644 source/Blocks/BlockSnow.h delete mode 100644 source/Blocks/BlockStairs.h delete mode 100644 source/Blocks/BlockStems.h delete mode 100644 source/Blocks/BlockStone.h delete mode 100644 source/Blocks/BlockSugarcane.h delete mode 100644 source/Blocks/BlockTallGrass.h delete mode 100644 source/Blocks/BlockTorch.h delete mode 100644 source/Blocks/BlockVine.h delete mode 100644 source/Blocks/BlockWood.h delete mode 100644 source/Blocks/BlockWorkbench.h delete mode 100644 source/BoundingBox.cpp delete mode 100644 source/BoundingBox.h delete mode 100644 source/ByteBuffer.cpp delete mode 100644 source/ByteBuffer.h delete mode 100644 source/ChatColor.cpp delete mode 100644 source/ChatColor.h delete mode 100644 source/Chunk.cpp delete mode 100644 source/Chunk.h delete mode 100644 source/Chunk.inl.h delete mode 100644 source/ChunkDef.h delete mode 100644 source/ChunkMap.cpp delete mode 100644 source/ChunkMap.h delete mode 100644 source/ChunkSender.cpp delete mode 100644 source/ChunkSender.h delete mode 100644 source/ClientHandle.cpp delete mode 100644 source/ClientHandle.h delete mode 100644 source/CommandOutput.cpp delete mode 100644 source/CommandOutput.h delete mode 100644 source/CraftingRecipes.cpp delete mode 100644 source/CraftingRecipes.h delete mode 100644 source/Cuboid.cpp delete mode 100644 source/Cuboid.h delete mode 100644 source/DeadlockDetect.cpp delete mode 100644 source/DeadlockDetect.h delete mode 100644 source/Defines.h delete mode 100644 source/Enchantments.cpp delete mode 100644 source/Enchantments.h delete mode 100644 source/Endianness.h delete mode 100644 source/Entities/Boat.cpp delete mode 100644 source/Entities/Boat.h delete mode 100644 source/Entities/Entity.cpp delete mode 100644 source/Entities/Entity.h delete mode 100644 source/Entities/FallingBlock.cpp delete mode 100644 source/Entities/FallingBlock.h delete mode 100644 source/Entities/Minecart.cpp delete mode 100644 source/Entities/Minecart.h delete mode 100644 source/Entities/Pawn.cpp delete mode 100644 source/Entities/Pawn.h delete mode 100644 source/Entities/Pickup.cpp delete mode 100644 source/Entities/Pickup.h delete mode 100644 source/Entities/Player.cpp delete mode 100644 source/Entities/Player.h delete mode 100644 source/Entities/ProjectileEntity.cpp delete mode 100644 source/Entities/ProjectileEntity.h delete mode 100644 source/Entities/TNTEntity.cpp delete mode 100644 source/Entities/TNTEntity.h delete mode 100644 source/FastRandom.cpp delete mode 100644 source/FastRandom.h delete mode 100644 source/FurnaceRecipe.cpp delete mode 100644 source/FurnaceRecipe.h delete mode 100644 source/Generating/BioGen.cpp delete mode 100644 source/Generating/BioGen.h delete mode 100644 source/Generating/Caves.cpp delete mode 100644 source/Generating/Caves.h delete mode 100644 source/Generating/ChunkDesc.cpp delete mode 100644 source/Generating/ChunkDesc.h delete mode 100644 source/Generating/ChunkGenerator.cpp delete mode 100644 source/Generating/ChunkGenerator.h delete mode 100644 source/Generating/CompoGen.cpp delete mode 100644 source/Generating/CompoGen.h delete mode 100644 source/Generating/ComposableGenerator.cpp delete mode 100644 source/Generating/ComposableGenerator.h delete mode 100644 source/Generating/DistortedHeightmap.cpp delete mode 100644 source/Generating/DistortedHeightmap.h delete mode 100644 source/Generating/EndGen.cpp delete mode 100644 source/Generating/EndGen.h delete mode 100644 source/Generating/FinishGen.cpp delete mode 100644 source/Generating/FinishGen.h delete mode 100644 source/Generating/HeiGen.cpp delete mode 100644 source/Generating/HeiGen.h delete mode 100644 source/Generating/MineShafts.cpp delete mode 100644 source/Generating/MineShafts.h delete mode 100644 source/Generating/Noise3DGenerator.cpp delete mode 100644 source/Generating/Noise3DGenerator.h delete mode 100644 source/Generating/Ravines.cpp delete mode 100644 source/Generating/Ravines.h delete mode 100644 source/Generating/StructGen.cpp delete mode 100644 source/Generating/StructGen.h delete mode 100644 source/Generating/Trees.cpp delete mode 100644 source/Generating/Trees.h delete mode 100644 source/Globals.cpp delete mode 100644 source/Globals.h delete mode 100644 source/Group.cpp delete mode 100644 source/Group.h delete mode 100644 source/GroupManager.cpp delete mode 100644 source/GroupManager.h delete mode 100644 source/HTTPServer/EnvelopeParser.cpp delete mode 100644 source/HTTPServer/EnvelopeParser.h delete mode 100644 source/HTTPServer/HTTPConnection.cpp delete mode 100644 source/HTTPServer/HTTPConnection.h delete mode 100644 source/HTTPServer/HTTPFormParser.cpp delete mode 100644 source/HTTPServer/HTTPFormParser.h delete mode 100644 source/HTTPServer/HTTPMessage.cpp delete mode 100644 source/HTTPServer/HTTPMessage.h delete mode 100644 source/HTTPServer/HTTPServer.cpp delete mode 100644 source/HTTPServer/HTTPServer.h delete mode 100644 source/HTTPServer/MultipartParser.cpp delete mode 100644 source/HTTPServer/MultipartParser.h delete mode 100644 source/HTTPServer/NameValueParser.cpp delete mode 100644 source/HTTPServer/NameValueParser.h delete mode 100644 source/Inventory.cpp delete mode 100644 source/Inventory.h delete mode 100644 source/Item.cpp delete mode 100644 source/Item.h delete mode 100644 source/ItemGrid.cpp delete mode 100644 source/ItemGrid.h delete mode 100644 source/Items/ItemBed.h delete mode 100644 source/Items/ItemBoat.h delete mode 100644 source/Items/ItemBow.h delete mode 100644 source/Items/ItemBrewingStand.h delete mode 100644 source/Items/ItemBucket.h delete mode 100644 source/Items/ItemCauldron.h delete mode 100644 source/Items/ItemCloth.h delete mode 100644 source/Items/ItemComparator.h delete mode 100644 source/Items/ItemDoor.h delete mode 100644 source/Items/ItemDye.h delete mode 100644 source/Items/ItemFlowerPot.h delete mode 100644 source/Items/ItemFood.h delete mode 100644 source/Items/ItemHandler.cpp delete mode 100644 source/Items/ItemHandler.h delete mode 100644 source/Items/ItemHoe.h delete mode 100644 source/Items/ItemLeaves.h delete mode 100644 source/Items/ItemLighter.h delete mode 100644 source/Items/ItemMinecart.h delete mode 100644 source/Items/ItemPickaxe.h delete mode 100644 source/Items/ItemRedstoneDust.h delete mode 100644 source/Items/ItemRedstoneRepeater.h delete mode 100644 source/Items/ItemSapling.h delete mode 100644 source/Items/ItemSeeds.h delete mode 100644 source/Items/ItemShears.h delete mode 100644 source/Items/ItemShovel.h delete mode 100644 source/Items/ItemSign.h delete mode 100644 source/Items/ItemSpawnEgg.h delete mode 100644 source/Items/ItemSugarcane.h delete mode 100644 source/Items/ItemSword.h delete mode 100644 source/Items/ItemThrowable.h delete mode 100644 source/LeakFinder.cpp delete mode 100644 source/LeakFinder.h delete mode 100644 source/LightingThread.cpp delete mode 100644 source/LightingThread.h delete mode 100644 source/LineBlockTracer.cpp delete mode 100644 source/LineBlockTracer.h delete mode 100644 source/LinearInterpolation.cpp delete mode 100644 source/LinearInterpolation.h delete mode 100644 source/LinearUpscale.h delete mode 100644 source/Log.cpp delete mode 100644 source/Log.h delete mode 100644 source/LuaExpat/lxplib.c delete mode 100644 source/LuaExpat/lxplib.h delete mode 100644 source/LuaFunctions.h delete mode 100644 source/LuaState.cpp delete mode 100644 source/LuaState.h delete mode 100644 source/LuaWindow.cpp delete mode 100644 source/LuaWindow.h delete mode 100644 source/MCLogger.cpp delete mode 100644 source/MCLogger.h delete mode 100644 source/ManualBindings.cpp delete mode 100644 source/ManualBindings.h delete mode 100644 source/Matrix4f.cpp delete mode 100644 source/Matrix4f.h delete mode 100644 source/MemoryLeak.h delete mode 100644 source/MersenneTwister.h delete mode 100644 source/MobCensus.cpp delete mode 100644 source/MobCensus.h delete mode 100644 source/MobFamilyCollecter.cpp delete mode 100644 source/MobFamilyCollecter.h delete mode 100644 source/MobProximityCounter.cpp delete mode 100644 source/MobProximityCounter.h delete mode 100644 source/MobSpawner.cpp delete mode 100644 source/MobSpawner.h delete mode 100644 source/Mobs/AggressiveMonster.cpp delete mode 100644 source/Mobs/AggressiveMonster.h delete mode 100644 source/Mobs/Bat.cpp delete mode 100644 source/Mobs/Bat.h delete mode 100644 source/Mobs/Blaze.cpp delete mode 100644 source/Mobs/Blaze.h delete mode 100644 source/Mobs/Cavespider.cpp delete mode 100644 source/Mobs/Cavespider.h delete mode 100644 source/Mobs/Chicken.cpp delete mode 100644 source/Mobs/Chicken.h delete mode 100644 source/Mobs/Cow.cpp delete mode 100644 source/Mobs/Cow.h delete mode 100644 source/Mobs/Creeper.cpp delete mode 100644 source/Mobs/Creeper.h delete mode 100644 source/Mobs/EnderDragon.cpp delete mode 100644 source/Mobs/EnderDragon.h delete mode 100644 source/Mobs/Enderman.cpp delete mode 100644 source/Mobs/Enderman.h delete mode 100644 source/Mobs/Ghast.cpp delete mode 100644 source/Mobs/Ghast.h delete mode 100644 source/Mobs/Giant.cpp delete mode 100644 source/Mobs/Giant.h delete mode 100644 source/Mobs/Horse.cpp delete mode 100644 source/Mobs/Horse.h delete mode 100644 source/Mobs/IncludeAllMonsters.h delete mode 100644 source/Mobs/IronGolem.cpp delete mode 100644 source/Mobs/IronGolem.h delete mode 100644 source/Mobs/Magmacube.cpp delete mode 100644 source/Mobs/Magmacube.h delete mode 100644 source/Mobs/Monster.cpp delete mode 100644 source/Mobs/Monster.h delete mode 100644 source/Mobs/Mooshroom.cpp delete mode 100644 source/Mobs/Mooshroom.h delete mode 100644 source/Mobs/Ocelot.h delete mode 100644 source/Mobs/PassiveAggressiveMonster.cpp delete mode 100644 source/Mobs/PassiveAggressiveMonster.h delete mode 100644 source/Mobs/PassiveMonster.cpp delete mode 100644 source/Mobs/PassiveMonster.h delete mode 100644 source/Mobs/Pig.cpp delete mode 100644 source/Mobs/Pig.h delete mode 100644 source/Mobs/Sheep.cpp delete mode 100644 source/Mobs/Sheep.h delete mode 100644 source/Mobs/Silverfish.h delete mode 100644 source/Mobs/Skeleton.cpp delete mode 100644 source/Mobs/Skeleton.h delete mode 100644 source/Mobs/Slime.cpp delete mode 100644 source/Mobs/Slime.h delete mode 100644 source/Mobs/SnowGolem.cpp delete mode 100644 source/Mobs/SnowGolem.h delete mode 100644 source/Mobs/Spider.cpp delete mode 100644 source/Mobs/Spider.h delete mode 100644 source/Mobs/Squid.cpp delete mode 100644 source/Mobs/Squid.h delete mode 100644 source/Mobs/Villager.cpp delete mode 100644 source/Mobs/Villager.h delete mode 100644 source/Mobs/Witch.cpp delete mode 100644 source/Mobs/Witch.h delete mode 100644 source/Mobs/Wither.cpp delete mode 100644 source/Mobs/Wither.h delete mode 100644 source/Mobs/Wolf.cpp delete mode 100644 source/Mobs/Wolf.h delete mode 100644 source/Mobs/Zombie.cpp delete mode 100644 source/Mobs/Zombie.h delete mode 100644 source/Mobs/Zombiepigman.cpp delete mode 100644 source/Mobs/Zombiepigman.h delete mode 100644 source/MonsterConfig.cpp delete mode 100644 source/MonsterConfig.h delete mode 100644 source/Noise.cpp delete mode 100644 source/Noise.h delete mode 100644 source/OSSupport/BlockingTCPLink.cpp delete mode 100644 source/OSSupport/BlockingTCPLink.h delete mode 100644 source/OSSupport/CriticalSection.cpp delete mode 100644 source/OSSupport/CriticalSection.h delete mode 100644 source/OSSupport/Event.cpp delete mode 100644 source/OSSupport/Event.h delete mode 100644 source/OSSupport/File.cpp delete mode 100644 source/OSSupport/File.h delete mode 100644 source/OSSupport/GZipFile.cpp delete mode 100644 source/OSSupport/GZipFile.h delete mode 100644 source/OSSupport/IsThread.cpp delete mode 100644 source/OSSupport/IsThread.h delete mode 100644 source/OSSupport/ListenThread.cpp delete mode 100644 source/OSSupport/ListenThread.h delete mode 100644 source/OSSupport/Semaphore.cpp delete mode 100644 source/OSSupport/Semaphore.h delete mode 100644 source/OSSupport/Sleep.cpp delete mode 100644 source/OSSupport/Sleep.h delete mode 100644 source/OSSupport/Socket.cpp delete mode 100644 source/OSSupport/Socket.h delete mode 100644 source/OSSupport/SocketThreads.cpp delete mode 100644 source/OSSupport/SocketThreads.h delete mode 100644 source/OSSupport/Thread.cpp delete mode 100644 source/OSSupport/Thread.h delete mode 100644 source/OSSupport/Timer.cpp delete mode 100644 source/OSSupport/Timer.h delete mode 100644 source/Piston.cpp delete mode 100644 source/Piston.h delete mode 100644 source/Plugin.cpp delete mode 100644 source/Plugin.h delete mode 100644 source/PluginLua.cpp delete mode 100644 source/PluginLua.h delete mode 100644 source/PluginManager.cpp delete mode 100644 source/PluginManager.h delete mode 100644 source/ProbabDistrib.cpp delete mode 100644 source/ProbabDistrib.h delete mode 100644 source/Protocol/ChunkDataSerializer.cpp delete mode 100644 source/Protocol/ChunkDataSerializer.h delete mode 100644 source/Protocol/Protocol.h delete mode 100644 source/Protocol/Protocol125.cpp delete mode 100644 source/Protocol/Protocol125.h delete mode 100644 source/Protocol/Protocol132.cpp delete mode 100644 source/Protocol/Protocol132.h delete mode 100644 source/Protocol/Protocol14x.cpp delete mode 100644 source/Protocol/Protocol14x.h delete mode 100644 source/Protocol/Protocol15x.cpp delete mode 100644 source/Protocol/Protocol15x.h delete mode 100644 source/Protocol/Protocol16x.cpp delete mode 100644 source/Protocol/Protocol16x.h delete mode 100644 source/Protocol/Protocol17x.cpp delete mode 100644 source/Protocol/Protocol17x.h delete mode 100644 source/Protocol/ProtocolRecognizer.cpp delete mode 100644 source/Protocol/ProtocolRecognizer.h delete mode 100644 source/RCONServer.cpp delete mode 100644 source/RCONServer.h delete mode 100644 source/ReferenceManager.cpp delete mode 100644 source/ReferenceManager.h delete mode 100644 source/Root.cpp delete mode 100644 source/Root.h delete mode 100644 source/SQLite/lsqlite3.c delete mode 100644 source/SQLite/sqlite3.c delete mode 100644 source/SQLite/sqlite3.h delete mode 100644 source/SQLite/urls.txt delete mode 100644 source/Server.cpp delete mode 100644 source/Server.h delete mode 100644 source/Simulator/DelayedFluidSimulator.cpp delete mode 100644 source/Simulator/DelayedFluidSimulator.h delete mode 100644 source/Simulator/FireSimulator.cpp delete mode 100644 source/Simulator/FireSimulator.h delete mode 100644 source/Simulator/FloodyFluidSimulator.cpp delete mode 100644 source/Simulator/FloodyFluidSimulator.h delete mode 100644 source/Simulator/FluidSimulator.cpp delete mode 100644 source/Simulator/FluidSimulator.h delete mode 100644 source/Simulator/NoopFluidSimulator.h delete mode 100644 source/Simulator/RedstoneSimulator.cpp delete mode 100644 source/Simulator/RedstoneSimulator.h delete mode 100644 source/Simulator/SandSimulator.cpp delete mode 100644 source/Simulator/SandSimulator.h delete mode 100644 source/Simulator/Simulator.cpp delete mode 100644 source/Simulator/Simulator.h delete mode 100644 source/Simulator/SimulatorManager.cpp delete mode 100644 source/Simulator/SimulatorManager.h delete mode 100644 source/Simulator/VaporizeFluidSimulator.cpp delete mode 100644 source/Simulator/VaporizeFluidSimulator.h delete mode 100644 source/StackWalker.cpp delete mode 100644 source/StackWalker.h delete mode 100644 source/StringCompression.cpp delete mode 100644 source/StringCompression.h delete mode 100644 source/StringUtils.cpp delete mode 100644 source/StringUtils.h delete mode 100644 source/Tracer.cpp delete mode 100644 source/Tracer.h delete mode 100644 source/UI/SlotArea.cpp delete mode 100644 source/UI/SlotArea.h delete mode 100644 source/UI/Window.cpp delete mode 100644 source/UI/Window.h delete mode 100644 source/UI/WindowOwner.h delete mode 100644 source/Vector3d.cpp delete mode 100644 source/Vector3d.h delete mode 100644 source/Vector3f.cpp delete mode 100644 source/Vector3f.h delete mode 100644 source/Vector3i.cpp delete mode 100644 source/Vector3i.h delete mode 100644 source/WebAdmin.cpp delete mode 100644 source/WebAdmin.h delete mode 100644 source/WebPlugin.cpp delete mode 100644 source/WebPlugin.h delete mode 100644 source/World.cpp delete mode 100644 source/World.h delete mode 100644 source/WorldStorage/FastNBT.cpp delete mode 100644 source/WorldStorage/FastNBT.h delete mode 100644 source/WorldStorage/NBTChunkSerializer.cpp delete mode 100644 source/WorldStorage/NBTChunkSerializer.h delete mode 100644 source/WorldStorage/WSSAnvil.cpp delete mode 100644 source/WorldStorage/WSSAnvil.h delete mode 100644 source/WorldStorage/WSSCompact.cpp delete mode 100644 source/WorldStorage/WSSCompact.h delete mode 100644 source/WorldStorage/WorldStorage.cpp delete mode 100644 source/WorldStorage/WorldStorage.h delete mode 100644 source/XMLParser.h delete mode 100644 source/lua5.1.dll delete mode 100644 source/main.cpp delete mode 100644 source/md5/md5.cpp delete mode 100644 source/md5/md5.h delete mode 100644 source/tolua++.exe delete mode 100644 source/tolua++.h delete mode 100644 source/tolua_base.h delete mode 100644 source/virtual_method_hooks.lua create mode 100644 src/AllToLua.bat create mode 100644 src/AllToLua.pkg create mode 100755 src/AllToLua.sh create mode 100644 src/Authenticator.cpp create mode 100644 src/Authenticator.h create mode 100644 src/Bindings.cpp create mode 100644 src/Bindings.h create mode 100644 src/BlockArea.cpp create mode 100644 src/BlockArea.h create mode 100644 src/BlockEntities/BlockEntity.cpp create mode 100644 src/BlockEntities/BlockEntity.h create mode 100644 src/BlockEntities/BlockEntityWithItems.h create mode 100644 src/BlockEntities/ChestEntity.cpp create mode 100644 src/BlockEntities/ChestEntity.h create mode 100644 src/BlockEntities/DispenserEntity.cpp create mode 100644 src/BlockEntities/DispenserEntity.h create mode 100644 src/BlockEntities/DropSpenserEntity.cpp create mode 100644 src/BlockEntities/DropSpenserEntity.h create mode 100644 src/BlockEntities/DropperEntity.cpp create mode 100644 src/BlockEntities/DropperEntity.h create mode 100644 src/BlockEntities/FurnaceEntity.cpp create mode 100644 src/BlockEntities/FurnaceEntity.h create mode 100644 src/BlockEntities/HopperEntity.cpp create mode 100644 src/BlockEntities/HopperEntity.h create mode 100644 src/BlockEntities/JukeboxEntity.cpp create mode 100644 src/BlockEntities/JukeboxEntity.h create mode 100644 src/BlockEntities/NoteEntity.cpp create mode 100644 src/BlockEntities/NoteEntity.h create mode 100644 src/BlockEntities/SignEntity.cpp create mode 100644 src/BlockEntities/SignEntity.h create mode 100644 src/BlockID.cpp create mode 100644 src/BlockID.h create mode 100644 src/BlockTracer.h create mode 100644 src/Blocks/BlockBed.cpp create mode 100644 src/Blocks/BlockBed.h create mode 100644 src/Blocks/BlockBrewingStand.h create mode 100644 src/Blocks/BlockButton.cpp create mode 100644 src/Blocks/BlockButton.h create mode 100644 src/Blocks/BlockCactus.h create mode 100644 src/Blocks/BlockCarpet.h create mode 100644 src/Blocks/BlockCauldron.h create mode 100644 src/Blocks/BlockChest.h create mode 100644 src/Blocks/BlockCloth.h create mode 100644 src/Blocks/BlockCobWeb.h create mode 100644 src/Blocks/BlockComparator.cpp create mode 100644 src/Blocks/BlockComparator.h create mode 100644 src/Blocks/BlockCrops.h create mode 100644 src/Blocks/BlockDeadBush.h create mode 100644 src/Blocks/BlockDirt.h create mode 100644 src/Blocks/BlockDoor.cpp create mode 100644 src/Blocks/BlockDoor.h create mode 100644 src/Blocks/BlockDropSpenser.h create mode 100644 src/Blocks/BlockEnderchest.h create mode 100644 src/Blocks/BlockEntity.h create mode 100644 src/Blocks/BlockFarmland.h create mode 100644 src/Blocks/BlockFenceGate.h create mode 100644 src/Blocks/BlockFire.h create mode 100644 src/Blocks/BlockFlower.h create mode 100644 src/Blocks/BlockFlowerPot.h create mode 100644 src/Blocks/BlockFluid.h create mode 100644 src/Blocks/BlockFurnace.h create mode 100644 src/Blocks/BlockGlass.h create mode 100644 src/Blocks/BlockGlowstone.h create mode 100644 src/Blocks/BlockGravel.h create mode 100644 src/Blocks/BlockHandler.cpp create mode 100644 src/Blocks/BlockHandler.h create mode 100644 src/Blocks/BlockHopper.h create mode 100644 src/Blocks/BlockIce.h create mode 100644 src/Blocks/BlockLadder.h create mode 100644 src/Blocks/BlockLeaves.h create mode 100644 src/Blocks/BlockLever.cpp create mode 100644 src/Blocks/BlockLever.h create mode 100644 src/Blocks/BlockMelon.h create mode 100644 src/Blocks/BlockMushroom.h create mode 100644 src/Blocks/BlockMycelium.h create mode 100644 src/Blocks/BlockNote.h create mode 100644 src/Blocks/BlockOre.h create mode 100644 src/Blocks/BlockPiston.cpp create mode 100644 src/Blocks/BlockPiston.h create mode 100644 src/Blocks/BlockPlanks.h create mode 100644 src/Blocks/BlockPortal.h create mode 100644 src/Blocks/BlockPumpkin.h create mode 100644 src/Blocks/BlockRail.h create mode 100644 src/Blocks/BlockRedstone.cpp create mode 100644 src/Blocks/BlockRedstone.h create mode 100644 src/Blocks/BlockRedstoneRepeater.cpp create mode 100644 src/Blocks/BlockRedstoneRepeater.h create mode 100644 src/Blocks/BlockRedstoneTorch.h create mode 100644 src/Blocks/BlockSand.h create mode 100644 src/Blocks/BlockSapling.h create mode 100644 src/Blocks/BlockSign.h create mode 100644 src/Blocks/BlockSlab.h create mode 100644 src/Blocks/BlockSnow.h create mode 100644 src/Blocks/BlockStairs.h create mode 100644 src/Blocks/BlockStems.h create mode 100644 src/Blocks/BlockStone.h create mode 100644 src/Blocks/BlockSugarcane.h create mode 100644 src/Blocks/BlockTallGrass.h create mode 100644 src/Blocks/BlockTorch.h create mode 100644 src/Blocks/BlockVine.h create mode 100644 src/Blocks/BlockWood.h create mode 100644 src/Blocks/BlockWorkbench.h create mode 100644 src/BoundingBox.cpp create mode 100644 src/BoundingBox.h create mode 100644 src/ByteBuffer.cpp create mode 100644 src/ByteBuffer.h create mode 100644 src/ChatColor.cpp create mode 100644 src/ChatColor.h create mode 100644 src/Chunk.cpp create mode 100644 src/Chunk.h create mode 100644 src/Chunk.inl.h create mode 100644 src/ChunkDef.h create mode 100644 src/ChunkMap.cpp create mode 100644 src/ChunkMap.h create mode 100644 src/ChunkSender.cpp create mode 100644 src/ChunkSender.h create mode 100644 src/ClientHandle.cpp create mode 100644 src/ClientHandle.h create mode 100644 src/CommandOutput.cpp create mode 100644 src/CommandOutput.h create mode 100644 src/CraftingRecipes.cpp create mode 100644 src/CraftingRecipes.h create mode 100644 src/Cuboid.cpp create mode 100644 src/Cuboid.h create mode 100644 src/DeadlockDetect.cpp create mode 100644 src/DeadlockDetect.h create mode 100644 src/Defines.h create mode 100644 src/Enchantments.cpp create mode 100644 src/Enchantments.h create mode 100644 src/Endianness.h create mode 100644 src/Entities/Boat.cpp create mode 100644 src/Entities/Boat.h create mode 100644 src/Entities/Entity.cpp create mode 100644 src/Entities/Entity.h create mode 100644 src/Entities/FallingBlock.cpp create mode 100644 src/Entities/FallingBlock.h create mode 100644 src/Entities/Minecart.cpp create mode 100644 src/Entities/Minecart.h create mode 100644 src/Entities/Pawn.cpp create mode 100644 src/Entities/Pawn.h create mode 100644 src/Entities/Pickup.cpp create mode 100644 src/Entities/Pickup.h create mode 100644 src/Entities/Player.cpp create mode 100644 src/Entities/Player.h create mode 100644 src/Entities/ProjectileEntity.cpp create mode 100644 src/Entities/ProjectileEntity.h create mode 100644 src/Entities/TNTEntity.cpp create mode 100644 src/Entities/TNTEntity.h create mode 100644 src/FastRandom.cpp create mode 100644 src/FastRandom.h create mode 100644 src/FurnaceRecipe.cpp create mode 100644 src/FurnaceRecipe.h create mode 100644 src/Generating/BioGen.cpp create mode 100644 src/Generating/BioGen.h create mode 100644 src/Generating/Caves.cpp create mode 100644 src/Generating/Caves.h create mode 100644 src/Generating/ChunkDesc.cpp create mode 100644 src/Generating/ChunkDesc.h create mode 100644 src/Generating/ChunkGenerator.cpp create mode 100644 src/Generating/ChunkGenerator.h create mode 100644 src/Generating/CompoGen.cpp create mode 100644 src/Generating/CompoGen.h create mode 100644 src/Generating/ComposableGenerator.cpp create mode 100644 src/Generating/ComposableGenerator.h create mode 100644 src/Generating/DistortedHeightmap.cpp create mode 100644 src/Generating/DistortedHeightmap.h create mode 100644 src/Generating/EndGen.cpp create mode 100644 src/Generating/EndGen.h create mode 100644 src/Generating/FinishGen.cpp create mode 100644 src/Generating/FinishGen.h create mode 100644 src/Generating/HeiGen.cpp create mode 100644 src/Generating/HeiGen.h create mode 100644 src/Generating/MineShafts.cpp create mode 100644 src/Generating/MineShafts.h create mode 100644 src/Generating/Noise3DGenerator.cpp create mode 100644 src/Generating/Noise3DGenerator.h create mode 100644 src/Generating/Ravines.cpp create mode 100644 src/Generating/Ravines.h create mode 100644 src/Generating/StructGen.cpp create mode 100644 src/Generating/StructGen.h create mode 100644 src/Generating/Trees.cpp create mode 100644 src/Generating/Trees.h create mode 100644 src/Globals.cpp create mode 100644 src/Globals.h create mode 100644 src/Group.cpp create mode 100644 src/Group.h create mode 100644 src/GroupManager.cpp create mode 100644 src/GroupManager.h create mode 100644 src/HTTPServer/EnvelopeParser.cpp create mode 100644 src/HTTPServer/EnvelopeParser.h create mode 100644 src/HTTPServer/HTTPConnection.cpp create mode 100644 src/HTTPServer/HTTPConnection.h create mode 100644 src/HTTPServer/HTTPFormParser.cpp create mode 100644 src/HTTPServer/HTTPFormParser.h create mode 100644 src/HTTPServer/HTTPMessage.cpp create mode 100644 src/HTTPServer/HTTPMessage.h create mode 100644 src/HTTPServer/HTTPServer.cpp create mode 100644 src/HTTPServer/HTTPServer.h create mode 100644 src/HTTPServer/MultipartParser.cpp create mode 100644 src/HTTPServer/MultipartParser.h create mode 100644 src/HTTPServer/NameValueParser.cpp create mode 100644 src/HTTPServer/NameValueParser.h create mode 100644 src/Inventory.cpp create mode 100644 src/Inventory.h create mode 100644 src/Item.cpp create mode 100644 src/Item.h create mode 100644 src/ItemGrid.cpp create mode 100644 src/ItemGrid.h create mode 100644 src/Items/ItemBed.h create mode 100644 src/Items/ItemBoat.h create mode 100644 src/Items/ItemBow.h create mode 100644 src/Items/ItemBrewingStand.h create mode 100644 src/Items/ItemBucket.h create mode 100644 src/Items/ItemCauldron.h create mode 100644 src/Items/ItemCloth.h create mode 100644 src/Items/ItemComparator.h create mode 100644 src/Items/ItemDoor.h create mode 100644 src/Items/ItemDye.h create mode 100644 src/Items/ItemFlowerPot.h create mode 100644 src/Items/ItemFood.h create mode 100644 src/Items/ItemHandler.cpp create mode 100644 src/Items/ItemHandler.h create mode 100644 src/Items/ItemHoe.h create mode 100644 src/Items/ItemLeaves.h create mode 100644 src/Items/ItemLighter.h create mode 100644 src/Items/ItemMinecart.h create mode 100644 src/Items/ItemPickaxe.h create mode 100644 src/Items/ItemRedstoneDust.h create mode 100644 src/Items/ItemRedstoneRepeater.h create mode 100644 src/Items/ItemSapling.h create mode 100644 src/Items/ItemSeeds.h create mode 100644 src/Items/ItemShears.h create mode 100644 src/Items/ItemShovel.h create mode 100644 src/Items/ItemSign.h create mode 100644 src/Items/ItemSpawnEgg.h create mode 100644 src/Items/ItemSugarcane.h create mode 100644 src/Items/ItemSword.h create mode 100644 src/Items/ItemThrowable.h create mode 100644 src/LeakFinder.cpp create mode 100644 src/LeakFinder.h create mode 100644 src/LightingThread.cpp create mode 100644 src/LightingThread.h create mode 100644 src/LineBlockTracer.cpp create mode 100644 src/LineBlockTracer.h create mode 100644 src/LinearInterpolation.cpp create mode 100644 src/LinearInterpolation.h create mode 100644 src/LinearUpscale.h create mode 100644 src/Log.cpp create mode 100644 src/Log.h create mode 100644 src/LuaExpat/lxplib.c create mode 100644 src/LuaExpat/lxplib.h create mode 100644 src/LuaFunctions.h create mode 100644 src/LuaState.cpp create mode 100644 src/LuaState.h create mode 100644 src/LuaWindow.cpp create mode 100644 src/LuaWindow.h create mode 100644 src/MCLogger.cpp create mode 100644 src/MCLogger.h create mode 100644 src/ManualBindings.cpp create mode 100644 src/ManualBindings.h create mode 100644 src/Matrix4f.cpp create mode 100644 src/Matrix4f.h create mode 100644 src/MemoryLeak.h create mode 100644 src/MersenneTwister.h create mode 100644 src/MobCensus.cpp create mode 100644 src/MobCensus.h create mode 100644 src/MobFamilyCollecter.cpp create mode 100644 src/MobFamilyCollecter.h create mode 100644 src/MobProximityCounter.cpp create mode 100644 src/MobProximityCounter.h create mode 100644 src/MobSpawner.cpp create mode 100644 src/MobSpawner.h create mode 100644 src/Mobs/AggressiveMonster.cpp create mode 100644 src/Mobs/AggressiveMonster.h create mode 100644 src/Mobs/Bat.cpp create mode 100644 src/Mobs/Bat.h create mode 100644 src/Mobs/Blaze.cpp create mode 100644 src/Mobs/Blaze.h create mode 100644 src/Mobs/Cavespider.cpp create mode 100644 src/Mobs/Cavespider.h create mode 100644 src/Mobs/Chicken.cpp create mode 100644 src/Mobs/Chicken.h create mode 100644 src/Mobs/Cow.cpp create mode 100644 src/Mobs/Cow.h create mode 100644 src/Mobs/Creeper.cpp create mode 100644 src/Mobs/Creeper.h create mode 100644 src/Mobs/EnderDragon.cpp create mode 100644 src/Mobs/EnderDragon.h create mode 100644 src/Mobs/Enderman.cpp create mode 100644 src/Mobs/Enderman.h create mode 100644 src/Mobs/Ghast.cpp create mode 100644 src/Mobs/Ghast.h create mode 100644 src/Mobs/Giant.cpp create mode 100644 src/Mobs/Giant.h create mode 100644 src/Mobs/Horse.cpp create mode 100644 src/Mobs/Horse.h create mode 100644 src/Mobs/IncludeAllMonsters.h create mode 100644 src/Mobs/IronGolem.cpp create mode 100644 src/Mobs/IronGolem.h create mode 100644 src/Mobs/Magmacube.cpp create mode 100644 src/Mobs/Magmacube.h create mode 100644 src/Mobs/Monster.cpp create mode 100644 src/Mobs/Monster.h create mode 100644 src/Mobs/Mooshroom.cpp create mode 100644 src/Mobs/Mooshroom.h create mode 100644 src/Mobs/Ocelot.h create mode 100644 src/Mobs/PassiveAggressiveMonster.cpp create mode 100644 src/Mobs/PassiveAggressiveMonster.h create mode 100644 src/Mobs/PassiveMonster.cpp create mode 100644 src/Mobs/PassiveMonster.h create mode 100644 src/Mobs/Pig.cpp create mode 100644 src/Mobs/Pig.h create mode 100644 src/Mobs/Sheep.cpp create mode 100644 src/Mobs/Sheep.h create mode 100644 src/Mobs/Silverfish.h create mode 100644 src/Mobs/Skeleton.cpp create mode 100644 src/Mobs/Skeleton.h create mode 100644 src/Mobs/Slime.cpp create mode 100644 src/Mobs/Slime.h create mode 100644 src/Mobs/SnowGolem.cpp create mode 100644 src/Mobs/SnowGolem.h create mode 100644 src/Mobs/Spider.cpp create mode 100644 src/Mobs/Spider.h create mode 100644 src/Mobs/Squid.cpp create mode 100644 src/Mobs/Squid.h create mode 100644 src/Mobs/Villager.cpp create mode 100644 src/Mobs/Villager.h create mode 100644 src/Mobs/Witch.cpp create mode 100644 src/Mobs/Witch.h create mode 100644 src/Mobs/Wither.cpp create mode 100644 src/Mobs/Wither.h create mode 100644 src/Mobs/Wolf.cpp create mode 100644 src/Mobs/Wolf.h create mode 100644 src/Mobs/Zombie.cpp create mode 100644 src/Mobs/Zombie.h create mode 100644 src/Mobs/Zombiepigman.cpp create mode 100644 src/Mobs/Zombiepigman.h create mode 100644 src/MonsterConfig.cpp create mode 100644 src/MonsterConfig.h create mode 100644 src/Noise.cpp create mode 100644 src/Noise.h create mode 100644 src/OSSupport/BlockingTCPLink.cpp create mode 100644 src/OSSupport/BlockingTCPLink.h create mode 100644 src/OSSupport/CriticalSection.cpp create mode 100644 src/OSSupport/CriticalSection.h create mode 100644 src/OSSupport/Event.cpp create mode 100644 src/OSSupport/Event.h create mode 100644 src/OSSupport/File.cpp create mode 100644 src/OSSupport/File.h create mode 100644 src/OSSupport/GZipFile.cpp create mode 100644 src/OSSupport/GZipFile.h create mode 100644 src/OSSupport/IsThread.cpp create mode 100644 src/OSSupport/IsThread.h create mode 100644 src/OSSupport/ListenThread.cpp create mode 100644 src/OSSupport/ListenThread.h create mode 100644 src/OSSupport/Semaphore.cpp create mode 100644 src/OSSupport/Semaphore.h create mode 100644 src/OSSupport/Sleep.cpp create mode 100644 src/OSSupport/Sleep.h create mode 100644 src/OSSupport/Socket.cpp create mode 100644 src/OSSupport/Socket.h create mode 100644 src/OSSupport/SocketThreads.cpp create mode 100644 src/OSSupport/SocketThreads.h create mode 100644 src/OSSupport/Thread.cpp create mode 100644 src/OSSupport/Thread.h create mode 100644 src/OSSupport/Timer.cpp create mode 100644 src/OSSupport/Timer.h create mode 100644 src/Piston.cpp create mode 100644 src/Piston.h create mode 100644 src/Plugin.cpp create mode 100644 src/Plugin.h create mode 100644 src/PluginLua.cpp create mode 100644 src/PluginLua.h create mode 100644 src/PluginManager.cpp create mode 100644 src/PluginManager.h create mode 100644 src/ProbabDistrib.cpp create mode 100644 src/ProbabDistrib.h create mode 100644 src/Protocol/ChunkDataSerializer.cpp create mode 100644 src/Protocol/ChunkDataSerializer.h create mode 100644 src/Protocol/Protocol.h create mode 100644 src/Protocol/Protocol125.cpp create mode 100644 src/Protocol/Protocol125.h create mode 100644 src/Protocol/Protocol132.cpp create mode 100644 src/Protocol/Protocol132.h create mode 100644 src/Protocol/Protocol14x.cpp create mode 100644 src/Protocol/Protocol14x.h create mode 100644 src/Protocol/Protocol15x.cpp create mode 100644 src/Protocol/Protocol15x.h create mode 100644 src/Protocol/Protocol16x.cpp create mode 100644 src/Protocol/Protocol16x.h create mode 100644 src/Protocol/Protocol17x.cpp create mode 100644 src/Protocol/Protocol17x.h create mode 100644 src/Protocol/ProtocolRecognizer.cpp create mode 100644 src/Protocol/ProtocolRecognizer.h create mode 100644 src/RCONServer.cpp create mode 100644 src/RCONServer.h create mode 100644 src/ReferenceManager.cpp create mode 100644 src/ReferenceManager.h create mode 100644 src/Root.cpp create mode 100644 src/Root.h create mode 100644 src/SQLite/lsqlite3.c create mode 100644 src/SQLite/sqlite3.c create mode 100644 src/SQLite/sqlite3.h create mode 100644 src/SQLite/urls.txt create mode 100644 src/Server.cpp create mode 100644 src/Server.h create mode 100644 src/Simulator/DelayedFluidSimulator.cpp create mode 100644 src/Simulator/DelayedFluidSimulator.h create mode 100644 src/Simulator/FireSimulator.cpp create mode 100644 src/Simulator/FireSimulator.h create mode 100644 src/Simulator/FloodyFluidSimulator.cpp create mode 100644 src/Simulator/FloodyFluidSimulator.h create mode 100644 src/Simulator/FluidSimulator.cpp create mode 100644 src/Simulator/FluidSimulator.h create mode 100644 src/Simulator/NoopFluidSimulator.h create mode 100644 src/Simulator/RedstoneSimulator.cpp create mode 100644 src/Simulator/RedstoneSimulator.h create mode 100644 src/Simulator/SandSimulator.cpp create mode 100644 src/Simulator/SandSimulator.h create mode 100644 src/Simulator/Simulator.cpp create mode 100644 src/Simulator/Simulator.h create mode 100644 src/Simulator/SimulatorManager.cpp create mode 100644 src/Simulator/SimulatorManager.h create mode 100644 src/Simulator/VaporizeFluidSimulator.cpp create mode 100644 src/Simulator/VaporizeFluidSimulator.h create mode 100644 src/StackWalker.cpp create mode 100644 src/StackWalker.h create mode 100644 src/StringCompression.cpp create mode 100644 src/StringCompression.h create mode 100644 src/StringUtils.cpp create mode 100644 src/StringUtils.h create mode 100644 src/Tracer.cpp create mode 100644 src/Tracer.h create mode 100644 src/UI/SlotArea.cpp create mode 100644 src/UI/SlotArea.h create mode 100644 src/UI/Window.cpp create mode 100644 src/UI/Window.h create mode 100644 src/UI/WindowOwner.h create mode 100644 src/Vector3d.cpp create mode 100644 src/Vector3d.h create mode 100644 src/Vector3f.cpp create mode 100644 src/Vector3f.h create mode 100644 src/Vector3i.cpp create mode 100644 src/Vector3i.h create mode 100644 src/WebAdmin.cpp create mode 100644 src/WebAdmin.h create mode 100644 src/WebPlugin.cpp create mode 100644 src/WebPlugin.h create mode 100644 src/World.cpp create mode 100644 src/World.h create mode 100644 src/WorldStorage/FastNBT.cpp create mode 100644 src/WorldStorage/FastNBT.h create mode 100644 src/WorldStorage/NBTChunkSerializer.cpp create mode 100644 src/WorldStorage/NBTChunkSerializer.h create mode 100644 src/WorldStorage/WSSAnvil.cpp create mode 100644 src/WorldStorage/WSSAnvil.h create mode 100644 src/WorldStorage/WSSCompact.cpp create mode 100644 src/WorldStorage/WSSCompact.h create mode 100644 src/WorldStorage/WorldStorage.cpp create mode 100644 src/WorldStorage/WorldStorage.h create mode 100644 src/XMLParser.h create mode 100644 src/lua5.1.dll create mode 100644 src/main.cpp create mode 100644 src/md5/md5.cpp create mode 100644 src/md5/md5.h create mode 100644 src/tolua++.exe create mode 100644 src/tolua++.h create mode 100644 src/tolua_base.h create mode 100644 src/virtual_method_hooks.lua diff --git a/source/AllToLua.bat b/source/AllToLua.bat deleted file mode 100644 index f7867fadb..000000000 --- a/source/AllToLua.bat +++ /dev/null @@ -1,27 +0,0 @@ - -:: AllToLua.bat - -:: This scripts updates the automatically-generates Lua bindings in Bindings.cpp / Bindings.h - - - - - -:: If there was a Git conflict, resolve it by resetting to HEAD; we're regenerating the files from scratch anyway -git checkout --ours Bindings.cpp -git add -u Bindings.cpp -git checkout --ours Bindings.h -git add -u Bindings.h - - - - - -:: Regenerate the files: -"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg - - - - - -if %ALLTOLUA_WAIT%N == N pause diff --git a/source/AllToLua.pkg b/source/AllToLua.pkg deleted file mode 100644 index ee594be1a..000000000 --- a/source/AllToLua.pkg +++ /dev/null @@ -1,81 +0,0 @@ - -$#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -$#include "tolua_base.h" - -// Typedefs from Globals.h, so that we don't have to include that file: -typedef long long Int64; -typedef int Int32; -typedef short Int16; - -typedef unsigned long long UInt64; -typedef unsigned int UInt32; -typedef unsigned short UInt16; - - -$cfile "ChunkDef.h" - -$cfile "../iniFile/iniFile.h" - -$cfile "OSSupport/File.h" - -$cfile "BlockID.h" -$cfile "StringUtils.h" -$cfile "Defines.h" -$cfile "LuaFunctions.h" -$cfile "ChatColor.h" -$cfile "ClientHandle.h" -$cfile "Entities/Entity.h" -$cfile "Entities/Pawn.h" -$cfile "Entities/Player.h" -$cfile "Entities/Pickup.h" -$cfile "Entities/ProjectileEntity.h" -$cfile "PluginManager.h" -$cfile "Plugin.h" -$cfile "PluginLua.h" -$cfile "Server.h" -$cfile "World.h" -$cfile "Inventory.h" -$cfile "Enchantments.h" -$cfile "Item.h" -$cfile "ItemGrid.h" -$cfile "BlockEntities/BlockEntity.h" -$cfile "BlockEntities/BlockEntityWithItems.h" -$cfile "BlockEntities/ChestEntity.h" -$cfile "BlockEntities/DropSpenserEntity.h" -$cfile "BlockEntities/DispenserEntity.h" -$cfile "BlockEntities/DropperEntity.h" -$cfile "BlockEntities/FurnaceEntity.h" -$cfile "BlockEntities/HopperEntity.h" -$cfile "BlockEntities/JukeboxEntity.h" -$cfile "BlockEntities/NoteEntity.h" -$cfile "BlockEntities/SignEntity.h" -$cfile "WebAdmin.h" -$cfile "WebPlugin.h" -$cfile "Root.h" -$cfile "Vector3f.h" -$cfile "Vector3d.h" -$cfile "Vector3i.h" -$cfile "Matrix4f.h" -$cfile "Cuboid.h" -$cfile "BoundingBox.h" -$cfile "Tracer.h" -$cfile "Group.h" -$cfile "BlockArea.h" -$cfile "Generating/ChunkDesc.h" -$cfile "CraftingRecipes.h" -$cfile "UI/Window.h" -$cfile "LuaWindow.h" -$cfile "Mobs/Monster.h" - - - - - -// Need to declare this class so that the usertype is properly registered in Bindings.cpp - -// it seems impossible to register a usertype in ManualBindings.cpp -class cLineBlockTracer; - - - - diff --git a/source/AllToLua.sh b/source/AllToLua.sh deleted file mode 100755 index 887c2490c..000000000 --- a/source/AllToLua.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -/usr/bin/tolua++ -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg diff --git a/source/Authenticator.cpp b/source/Authenticator.cpp deleted file mode 100644 index 9a6dcf51b..000000000 --- a/source/Authenticator.cpp +++ /dev/null @@ -1,267 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Authenticator.h" -#include "OSSupport/BlockingTCPLink.h" -#include "Root.h" -#include "Server.h" - -#include "../iniFile/iniFile.h" - -#include - - - - - -#define DEFAULT_AUTH_SERVER "session.minecraft.net" -#define DEFAULT_AUTH_ADDRESS "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%" -#define MAX_REDIRECTS 10 - - - - - -cAuthenticator::cAuthenticator(void) : - super("cAuthenticator"), - m_Server(DEFAULT_AUTH_SERVER), - m_Address(DEFAULT_AUTH_ADDRESS), - m_ShouldAuthenticate(true) -{ -} - - - - - -cAuthenticator::~cAuthenticator() -{ - Stop(); -} - - - - - -/// Read custom values from INI -void cAuthenticator::ReadINI(cIniFile & IniFile) -{ - m_Server = IniFile.GetValueSet("Authentication", "Server", DEFAULT_AUTH_SERVER); - m_Address = IniFile.GetValueSet("Authentication", "Address", DEFAULT_AUTH_ADDRESS); - m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true); -} - - - - - -/// Queues a request for authenticating a user. If the auth fails, the user is kicked -void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash) -{ - if (!m_ShouldAuthenticate) - { - cRoot::Get()->AuthenticateUser(a_ClientID); - return; - } - - cCSLock Lock(m_CS); - m_Queue.push_back(cUser(a_ClientID, a_UserName, a_ServerHash)); - m_QueueNonempty.Set(); -} - - - - - -void cAuthenticator::Start(cIniFile & IniFile) -{ - ReadINI(IniFile); - m_ShouldTerminate = false; - super::Start(); -} - - - - - -void cAuthenticator::Stop(void) -{ - m_ShouldTerminate = true; - m_QueueNonempty.Set(); - Wait(); -} - - - - - -void cAuthenticator::Execute(void) -{ - for (;;) - { - cCSLock Lock(m_CS); - while (!m_ShouldTerminate && (m_Queue.size() == 0)) - { - cCSUnlock Unlock(Lock); - m_QueueNonempty.Wait(); - } - if (m_ShouldTerminate) - { - return; - } - ASSERT(!m_Queue.empty()); - - int ClientID = m_Queue.front().m_ClientID; - AString UserName = m_Queue.front().m_Name; - AString ActualAddress = m_Address; - ReplaceString(ActualAddress, "%USERNAME%", UserName); - ReplaceString(ActualAddress, "%SERVERID%", m_Queue.front().m_ServerID); - m_Queue.pop_front(); - Lock.Unlock(); - - if (!AuthFromAddress(m_Server, ActualAddress, UserName)) - { - cRoot::Get()->KickUser(ClientID, "Failed to authenticate account!"); - } - else - { - cRoot::Get()->AuthenticateUser(ClientID); - } - } // for (-ever) -} - - - - - -bool cAuthenticator::AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level /* = 1 */) -{ - // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) - - cBlockingTCPLink Link; - if (!Link.Connect(a_Server.c_str(), 80)) - { - LOGERROR("cAuthenticator: cannot connect to auth server \"%s\", kicking user \"%s\"", a_Server.c_str(), a_Server.c_str()); - return false; - } - - Link.SendMessage( AString( "GET " + a_Address + " HTTP/1.1\r\n" ).c_str()); - Link.SendMessage( AString( "User-Agent: MCServer\r\n" ).c_str()); - Link.SendMessage( AString( "Host: " + a_Server + "\r\n" ).c_str()); - //Link.SendMessage( AString( "Host: session.minecraft.net\r\n" ).c_str()); - Link.SendMessage( AString( "Accept: */*\r\n" ).c_str()); - Link.SendMessage( AString( "Connection: close\r\n" ).c_str()); //Close so we don´t have to mess with the Content-Length :) - Link.SendMessage( AString( "\r\n" ).c_str()); - AString DataRecvd; - Link.ReceiveData(DataRecvd); - Link.CloseSocket(); - - std::stringstream ss(DataRecvd); - - // Parse the data received: - std::string temp; - ss >> temp; - bool bRedirect = false; - bool bOK = false; - if ((temp.compare("HTTP/1.1") == 0) || (temp.compare("HTTP/1.0") == 0)) - { - int code; - ss >> code; - if (code == 302) - { - // redirect blabla - LOGINFO("Need to redirect!"); - if (a_Level > MAX_REDIRECTS) - { - LOGERROR("cAuthenticator: received too many levels of redirection from auth server \"%s\" for user \"%s\", bailing out and kicking the user", a_Server.c_str(), a_UserName.c_str()); - return false; - } - bRedirect = true; - } - else if (code == 200) - { - LOGD("cAuthenticator: Received status 200 OK! :D"); - bOK = true; - } - } - else - { - LOGERROR("cAuthenticator: cannot parse auth reply from server \"%s\" for user \"%s\", kicking the user.", a_Server.c_str(), a_UserName.c_str()); - return false; - } - - if( bRedirect ) - { - AString Location; - // Search for "Location:" - bool bFoundLocation = false; - while( !bFoundLocation && ss.good() ) - { - char c = 0; - while( c != '\n' ) - { - ss.get( c ); - } - AString Name; - ss >> Name; - if (Name.compare("Location:") == 0) - { - bFoundLocation = true; - ss >> Location; - } - } - if (!bFoundLocation) - { - LOGERROR("cAuthenticator: received invalid redirection from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); - return false; - } - - Location = Location.substr(strlen("http://"), std::string::npos); // Strip http:// - std::string Server = Location.substr( 0, Location.find( "/" ) ); // Only leave server address - Location = Location.substr( Server.length(), std::string::npos); - return AuthFromAddress(Server, Location, a_UserName, a_Level + 1); - } - - if (!bOK) - { - LOGERROR("cAuthenticator: received an error from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); - return false; - } - - // Header says OK, so receive the rest. - // Go past header, double \n means end of headers - char c = 0; - while (ss.good()) - { - while (c != '\n') - { - ss.get(c); - } - ss.get(c); - if( c == '\n' || c == '\r' || ss.peek() == '\r' || ss.peek() == '\n' ) - break; - } - if (!ss.good()) - { - LOGERROR("cAuthenticator: error while parsing response body from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); - return false; - } - - std::string Result; - ss >> Result; - LOGD("cAuthenticator: Authentication result was %s", Result.c_str()); - - if (Result.compare("YES") == 0) //Works well - { - LOGINFO("Authentication result \"YES\", player authentication success!"); - return true; - } - - - LOGINFO("Authentication result was \"%s\", player authentication failure!", Result.c_str()); - return false; -} - - - - diff --git a/source/Authenticator.h b/source/Authenticator.h deleted file mode 100644 index 02cd6f4c5..000000000 --- a/source/Authenticator.h +++ /dev/null @@ -1,93 +0,0 @@ - -// cAuthenticator.h - -// Interfaces to the cAuthenticator class representing the thread that authenticates users against the official MC server -// Authentication prevents "hackers" from joining with an arbitrary username (possibly impersonating the server admins) -// For more info, see http://wiki.vg/Session#Server_operation -// In MCS, authentication is implemented as a single thread that receives queued auth requests and dispatches them one by one. - - - - - -#pragma once -#ifndef CAUTHENTICATOR_H_INCLUDED -#define CAUTHENTICATOR_H_INCLUDED - -#include "OSSupport/IsThread.h" - - - - - -// fwd: "cRoot.h" -class cRoot; - - - - - -class cAuthenticator : - public cIsThread -{ - typedef cIsThread super; - -public: - cAuthenticator(void); - ~cAuthenticator(); - - /// (Re-)read server and address from INI: - void ReadINI(cIniFile & IniFile); - - /// Queues a request for authenticating a user. If the auth fails, the user is kicked - void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash); - - /// Starts the authenticator thread. The thread may be started and stopped repeatedly - void Start(cIniFile & IniFile); - - /// Stops the authenticator thread. The thread may be started and stopped repeatedly - void Stop(void); - -private: - - class cUser - { - public: - int m_ClientID; - AString m_Name; - AString m_ServerID; - - cUser(int a_ClientID, const AString & a_Name, const AString & a_ServerID) : - m_ClientID(a_ClientID), - m_Name(a_Name), - m_ServerID(a_ServerID) - { - } - } ; - - typedef std::deque cUserList; - - cCriticalSection m_CS; - cUserList m_Queue; - cEvent m_QueueNonempty; - - AString m_Server; - AString m_Address; - bool m_ShouldAuthenticate; - - // cIsThread override: - virtual void Execute(void) override; - - // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) - bool AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level = 1); -}; - - - - - -#endif // CAUTHENTICATOR_H_INCLUDED - - - - diff --git a/source/Bindings.cpp b/source/Bindings.cpp deleted file mode 100644 index 93c66d233..000000000 --- a/source/Bindings.cpp +++ /dev/null @@ -1,31485 +0,0 @@ -/* -** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 11/15/13 10:14:19. -*/ - -#ifndef __cplusplus -#include "stdlib.h" -#endif -#include "string.h" - -#include "tolua++.h" - -/* Exported function */ -TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "tolua_base.h" -#include "ChunkDef.h" -#include "../iniFile/iniFile.h" -#include "OSSupport/File.h" -#include "BlockID.h" -#include "StringUtils.h" -#include "Defines.h" -#include "LuaFunctions.h" -#include "ChatColor.h" -#include "ClientHandle.h" -#include "Entities/Entity.h" -#include "Entities/Pawn.h" -#include "Entities/Player.h" -#include "Entities/Pickup.h" -#include "Entities/ProjectileEntity.h" -#include "PluginManager.h" -#include "Plugin.h" -#include "PluginLua.h" -#include "Server.h" -#include "World.h" -#include "Inventory.h" -#include "Enchantments.h" -#include "Item.h" -#include "ItemGrid.h" -#include "BlockEntities/BlockEntity.h" -#include "BlockEntities/BlockEntityWithItems.h" -#include "BlockEntities/ChestEntity.h" -#include "BlockEntities/DropSpenserEntity.h" -#include "BlockEntities/DispenserEntity.h" -#include "BlockEntities/DropperEntity.h" -#include "BlockEntities/FurnaceEntity.h" -#include "BlockEntities/HopperEntity.h" -#include "BlockEntities/JukeboxEntity.h" -#include "BlockEntities/NoteEntity.h" -#include "BlockEntities/SignEntity.h" -#include "WebAdmin.h" -#include "WebPlugin.h" -#include "Root.h" -#include "Vector3f.h" -#include "Vector3d.h" -#include "Vector3i.h" -#include "Matrix4f.h" -#include "Cuboid.h" -#include "BoundingBox.h" -#include "Tracer.h" -#include "Group.h" -#include "BlockArea.h" -#include "Generating/ChunkDesc.h" -#include "CraftingRecipes.h" -#include "UI/Window.h" -#include "LuaWindow.h" -#include "Mobs/Monster.h" - -/* function to release collected object via destructor */ -#ifdef __cplusplus - -static int tolua_collect_sWebAdminPage (lua_State* tolua_S) -{ - sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cBoundingBox (lua_State* tolua_S) -{ - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cItem (lua_State* tolua_S) -{ - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_Vector3f (lua_State* tolua_S) -{ - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cIniFile (lua_State* tolua_S) -{ - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cPickup (lua_State* tolua_S) -{ - cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cItems (lua_State* tolua_S) -{ - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cBlockArea (lua_State* tolua_S) -{ - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cTracer (lua_State* tolua_S) -{ - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cCraftingGrid (lua_State* tolua_S) -{ - cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cCuboid (lua_State* tolua_S) -{ - cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cBlockEntity (lua_State* tolua_S) -{ - cBlockEntity* self = (cBlockEntity*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_Vector3i (lua_State* tolua_S) -{ - Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cEnchantments (lua_State* tolua_S) -{ - cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_cLuaWindow (lua_State* tolua_S) -{ - cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} - -static int tolua_collect_Vector3d (lua_State* tolua_S) -{ - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); - Mtolua_delete(self); - return 0; -} -#endif - - -/* function to register type */ -static void tolua_reg_types (lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"cThrownEnderPearlEntity"); - tolua_usertype(tolua_S,"cFurnaceEntity"); - tolua_usertype(tolua_S,"cEntity"); - tolua_usertype(tolua_S,"cCuboid"); - tolua_usertype(tolua_S,"cEnchantments"); - tolua_usertype(tolua_S,"cMonster"); - tolua_usertype(tolua_S,"cPluginLua"); - tolua_usertype(tolua_S,"cRoot"); - tolua_usertype(tolua_S,"std::vector"); - tolua_usertype(tolua_S,"cPickup"); - tolua_usertype(tolua_S,"sWebAdminPage"); - tolua_usertype(tolua_S,"cFireChargeEntity"); - tolua_usertype(tolua_S,"cWorld"); - tolua_usertype(tolua_S,"cChunkDesc"); - tolua_usertype(tolua_S,"cFurnaceRecipe"); - tolua_usertype(tolua_S,"cPluginManager"); - tolua_usertype(tolua_S,"Vector3f"); - tolua_usertype(tolua_S,"cCraftingRecipes"); - tolua_usertype(tolua_S,"cJukeboxEntity"); - tolua_usertype(tolua_S,"cChestEntity"); - tolua_usertype(tolua_S,"cDispenserEntity"); - tolua_usertype(tolua_S,"cGhastFireballEntity"); - tolua_usertype(tolua_S,"cLineBlockTracer"); - tolua_usertype(tolua_S,"cListeners"); - tolua_usertype(tolua_S,"cThrownSnowballEntity"); - tolua_usertype(tolua_S,"Vector3d"); - tolua_usertype(tolua_S,"TakeDamageInfo"); - tolua_usertype(tolua_S,"cCraftingRecipe"); - tolua_usertype(tolua_S,"cPlugin"); - tolua_usertype(tolua_S,"cItemGrid"); - tolua_usertype(tolua_S,"cHTTPServer::cCallbacks"); - tolua_usertype(tolua_S,"cLuaWindow"); - tolua_usertype(tolua_S,"cInventory"); - tolua_usertype(tolua_S,"cHopperEntity"); - tolua_usertype(tolua_S,"std::vector"); - tolua_usertype(tolua_S,"cBlockEntityWithItems"); - tolua_usertype(tolua_S,"cWindow"); - tolua_usertype(tolua_S,"cCraftingGrid"); - tolua_usertype(tolua_S,"cItem"); - tolua_usertype(tolua_S,"cBlockArea"); - tolua_usertype(tolua_S,"cArrowEntity"); - tolua_usertype(tolua_S,"cDropSpenserEntity"); - tolua_usertype(tolua_S,"cGroup"); - tolua_usertype(tolua_S,"cTracer"); - tolua_usertype(tolua_S,"cBoundingBox"); - tolua_usertype(tolua_S,"cNoteEntity"); - tolua_usertype(tolua_S,"Vector3i"); - tolua_usertype(tolua_S,"cBlockEntity"); - tolua_usertype(tolua_S,"cCriticalSection"); - tolua_usertype(tolua_S,"HTTPTemplateRequest"); - tolua_usertype(tolua_S,"cPlayer"); - tolua_usertype(tolua_S,"cServer"); - tolua_usertype(tolua_S,"cSignEntity"); - tolua_usertype(tolua_S,"cFile"); - tolua_usertype(tolua_S,"cItems"); - tolua_usertype(tolua_S,"cClientHandle"); - tolua_usertype(tolua_S,"cIniFile"); - tolua_usertype(tolua_S,"cWebPlugin"); - tolua_usertype(tolua_S,"cChatColor"); - tolua_usertype(tolua_S,"cPawn"); - tolua_usertype(tolua_S,"cThrownEggEntity"); - tolua_usertype(tolua_S,"cGroupManager"); - tolua_usertype(tolua_S,"cWebAdmin"); - tolua_usertype(tolua_S,"HTTPRequest"); - tolua_usertype(tolua_S,"cProjectileEntity"); - tolua_usertype(tolua_S,"HTTPFormData"); - tolua_usertype(tolua_S,"cItemGrid::cListener"); - tolua_usertype(tolua_S,"cDropperEntity"); -} - -/* method: new of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new00 -static int tolua_AllToLua_cIniFile_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new00_local -static int tolua_AllToLua_cIniFile_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CaseSensitive of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_CaseSensitive00 -static int tolua_AllToLua_cIniFile_CaseSensitive00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CaseSensitive'", NULL); -#endif - { - self->CaseSensitive(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CaseSensitive'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CaseInsensitive of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_CaseInsensitive00 -static int tolua_AllToLua_cIniFile_CaseInsensitive00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CaseInsensitive'", NULL); -#endif - { - self->CaseInsensitive(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CaseInsensitive'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ReadFile of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_ReadFile00 -static int tolua_AllToLua_cIniFile_ReadFile00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isboolean(tolua_S,3,1,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); - bool a_AllowExampleRedirect = ((bool) tolua_toboolean(tolua_S,3,true)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadFile'", NULL); -#endif - { - bool tolua_ret = (bool) self->ReadFile(a_FileName,a_AllowExampleRedirect); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_FileName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ReadFile'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: WriteFile of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_WriteFile00 -static int tolua_AllToLua_cIniFile_WriteFile00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteFile'", NULL); -#endif - { - bool tolua_ret = (bool) self->WriteFile(a_FileName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_FileName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'WriteFile'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Clear of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Clear00 -static int tolua_AllToLua_cIniFile_Clear00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); -#endif - { - self->Clear(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FindKey of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_FindKey00 -static int tolua_AllToLua_cIniFile_FindKey00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindKey'", NULL); -#endif - { - int tolua_ret = (int) self->FindKey(keyname); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FindKey'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FindValue of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_FindValue00 -static int tolua_AllToLua_cIniFile_FindValue00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindValue'", NULL); -#endif - { - int tolua_ret = (int) self->FindValue(keyID,valuename); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FindValue'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumKeys of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeys00 -static int tolua_AllToLua_cIniFile_GetNumKeys00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeys'", NULL); -#endif - { - int tolua_ret = (int) self->GetNumKeys(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNumKeys'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddKeyName of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyName00 -static int tolua_AllToLua_cIniFile_AddKeyName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyName'", NULL); -#endif - { - int tolua_ret = (int) self->AddKeyName(keyname); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddKeyName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetKeyName of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyName00 -static int tolua_AllToLua_cIniFile_GetKeyName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyName'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetKeyName(keyID); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetKeyName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumValues of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues00 -static int tolua_AllToLua_cIniFile_GetNumValues00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL); -#endif - { - int tolua_ret = (int) self->GetNumValues(keyname); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNumValues'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumValues of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues01 -static int tolua_AllToLua_cIniFile_GetNumValues01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL); -#endif - { - int tolua_ret = (int) self->GetNumValues(keyID); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cIniFile_GetNumValues00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueName of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName00 -static int tolua_AllToLua_cIniFile_GetValueName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetValueName(keyname,valueID); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetValueName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueName of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName01 -static int tolua_AllToLua_cIniFile_GetValueName01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); - const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetValueName(keyID,valueID); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cIniFile_GetValueName00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValue of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue00 -static int tolua_AllToLua_cIniFile_GetValue00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetValue(keyname,valuename); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetValue'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValue of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue01 -static int tolua_AllToLua_cIniFile_GetValue01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_iscppstring(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetValue(keyname,valuename,defValue); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - tolua_pushcppstring(tolua_S,(const char*)defValue); - } - } - return 4; -tolua_lerror: - return tolua_AllToLua_cIniFile_GetValue00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValue of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue02 -static int tolua_AllToLua_cIniFile_GetValue02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); - const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetValue(keyID,valueID); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cIniFile_GetValue01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValue of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue03 -static int tolua_AllToLua_cIniFile_GetValue03(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_iscppstring(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); - const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); - const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetValue(keyID,valueID,defValue); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)defValue); - } - } - return 2; -tolua_lerror: - return tolua_AllToLua_cIniFile_GetValue02(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueF of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueF00 -static int tolua_AllToLua_cIniFile_GetValueF00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const double defValue = ((const double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueF'", NULL); -#endif - { - double tolua_ret = (double) self->GetValueF(keyname,valuename,defValue); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetValueF'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueI of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueI00 -static int tolua_AllToLua_cIniFile_GetValueI00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const int defValue = ((const int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueI'", NULL); -#endif - { - int tolua_ret = (int) self->GetValueI(keyname,valuename,defValue); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetValueI'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueB of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueB00 -static int tolua_AllToLua_cIniFile_GetValueB00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isboolean(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const bool defValue = ((const bool) tolua_toboolean(tolua_S,4,false)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueB'", NULL); -#endif - { - bool tolua_ret = (bool) self->GetValueB(keyname,valuename,defValue); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetValueB'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueSet of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSet00 -static int tolua_AllToLua_cIniFile_GetValueSet00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSet'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetValueSet(keyname,valuename); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetValueSet'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueSet of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSet01 -static int tolua_AllToLua_cIniFile_GetValueSet01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_iscppstring(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSet'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetValueSet(keyname,valuename,defValue); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - tolua_pushcppstring(tolua_S,(const char*)defValue); - } - } - return 4; -tolua_lerror: - return tolua_AllToLua_cIniFile_GetValueSet00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueSetF of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetF00 -static int tolua_AllToLua_cIniFile_GetValueSetF00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const double defValue = ((const double) tolua_tonumber(tolua_S,4,0.0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetF'", NULL); -#endif - { - double tolua_ret = (double) self->GetValueSetF(keyname,valuename,defValue); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetValueSetF'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueSetI of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetI00 -static int tolua_AllToLua_cIniFile_GetValueSetI00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const int defValue = ((const int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetI'", NULL); -#endif - { - int tolua_ret = (int) self->GetValueSetI(keyname,valuename,defValue); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetValueSetI'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetValueSetB of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetB00 -static int tolua_AllToLua_cIniFile_GetValueSetB00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isboolean(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const bool defValue = ((const bool) tolua_toboolean(tolua_S,4,false)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetB'", NULL); -#endif - { - bool tolua_ret = (bool) self->GetValueSetB(keyname,valuename,defValue); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetValueSetB'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetValue of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValue00 -static int tolua_AllToLua_cIniFile_SetValue00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_iscppstring(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); - const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); - const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL); -#endif - { - bool tolua_ret = (bool) self->SetValue(keyID,valueID,value); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)value); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetValue'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetValue of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValue01 -static int tolua_AllToLua_cIniFile_SetValue01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_iscppstring(tolua_S,4,0,&tolua_err) || - !tolua_isboolean(tolua_S,5,1,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0)); - const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL); -#endif - { - bool tolua_ret = (bool) self->SetValue(keyname,valuename,value,create); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - tolua_pushcppstring(tolua_S,(const char*)value); - } - } - return 4; -tolua_lerror: - return tolua_AllToLua_cIniFile_SetValue00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetValueI of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueI00 -static int tolua_AllToLua_cIniFile_SetValueI00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isboolean(tolua_S,5,1,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const int value = ((const int) tolua_tonumber(tolua_S,4,0)); - const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueI'", NULL); -#endif - { - bool tolua_ret = (bool) self->SetValueI(keyname,valuename,value,create); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetValueI'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetValueB of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueB00 -static int tolua_AllToLua_cIniFile_SetValueB00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isboolean(tolua_S,4,0,&tolua_err) || - !tolua_isboolean(tolua_S,5,1,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const bool value = ((const bool) tolua_toboolean(tolua_S,4,0)); - const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueB'", NULL); -#endif - { - bool tolua_ret = (bool) self->SetValueB(keyname,valuename,value,create); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetValueB'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetValueF of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueF00 -static int tolua_AllToLua_cIniFile_SetValueF00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isboolean(tolua_S,5,1,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const double value = ((const double) tolua_tonumber(tolua_S,4,0)); - const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueF'", NULL); -#endif - { - bool tolua_ret = (bool) self->SetValueF(keyname,valuename,value,create); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetValueF'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DeleteValueByID of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteValueByID00 -static int tolua_AllToLua_cIniFile_DeleteValueByID00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); - const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValueByID'", NULL); -#endif - { - bool tolua_ret = (bool) self->DeleteValueByID(keyID,valueID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DeleteValueByID'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DeleteValue of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteValue00 -static int tolua_AllToLua_cIniFile_DeleteValue00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValue'", NULL); -#endif - { - bool tolua_ret = (bool) self->DeleteValue(keyname,valuename); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)valuename); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DeleteValue'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DeleteKey of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKey00 -static int tolua_AllToLua_cIniFile_DeleteKey00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKey'", NULL); -#endif - { - bool tolua_ret = (bool) self->DeleteKey(keyname); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DeleteKey'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumHeaderComments of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumHeaderComments00 -static int tolua_AllToLua_cIniFile_GetNumHeaderComments00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumHeaderComments'", NULL); -#endif - { - int tolua_ret = (int) self->GetNumHeaderComments(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNumHeaderComments'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddHeaderComment of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddHeaderComment00 -static int tolua_AllToLua_cIniFile_AddHeaderComment00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString comment = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddHeaderComment'", NULL); -#endif - { - self->AddHeaderComment(comment); - tolua_pushcppstring(tolua_S,(const char*)comment); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddHeaderComment'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHeaderComment of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetHeaderComment00 -static int tolua_AllToLua_cIniFile_GetHeaderComment00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const int commentID = ((const int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeaderComment'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetHeaderComment(commentID); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHeaderComment'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DeleteHeaderComment of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteHeaderComment00 -static int tolua_AllToLua_cIniFile_DeleteHeaderComment00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - int commentID = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteHeaderComment'", NULL); -#endif - { - bool tolua_ret = (bool) self->DeleteHeaderComment(commentID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DeleteHeaderComment'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DeleteHeaderComments of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteHeaderComments00 -static int tolua_AllToLua_cIniFile_DeleteHeaderComments00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteHeaderComments'", NULL); -#endif - { - self->DeleteHeaderComments(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DeleteHeaderComments'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumKeyComments of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments00 -static int tolua_AllToLua_cIniFile_GetNumKeyComments00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL); -#endif - { - int tolua_ret = (int) self->GetNumKeyComments(keyID); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNumKeyComments'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumKeyComments of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments01 -static int tolua_AllToLua_cIniFile_GetNumKeyComments01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL); -#endif - { - int tolua_ret = (int) self->GetNumKeyComments(keyname); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - } - } - return 2; -tolua_lerror: - return tolua_AllToLua_cIniFile_GetNumKeyComments00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddKeyComment of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment00 -static int tolua_AllToLua_cIniFile_AddKeyComment00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); - const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL); -#endif - { - bool tolua_ret = (bool) self->AddKeyComment(keyID,comment); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)comment); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddKeyComment'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddKeyComment of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment01 -static int tolua_AllToLua_cIniFile_AddKeyComment01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL); -#endif - { - bool tolua_ret = (bool) self->AddKeyComment(keyname,comment); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - tolua_pushcppstring(tolua_S,(const char*)comment); - } - } - return 3; -tolua_lerror: - return tolua_AllToLua_cIniFile_AddKeyComment00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetKeyComment of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment00 -static int tolua_AllToLua_cIniFile_GetKeyComment00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); - const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetKeyComment(keyID,commentID); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetKeyComment'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetKeyComment of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment01 -static int tolua_AllToLua_cIniFile_GetKeyComment01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetKeyComment(keyname,commentID); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - } - } - return 2; -tolua_lerror: - return tolua_AllToLua_cIniFile_GetKeyComment00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DeleteKeyComment of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComment00 -static int tolua_AllToLua_cIniFile_DeleteKeyComment00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); - const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL); -#endif - { - bool tolua_ret = (bool) self->DeleteKeyComment(keyID,commentID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DeleteKeyComment'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DeleteKeyComment of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComment01 -static int tolua_AllToLua_cIniFile_DeleteKeyComment01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL); -#endif - { - bool tolua_ret = (bool) self->DeleteKeyComment(keyname,commentID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - } - } - return 2; -tolua_lerror: - return tolua_AllToLua_cIniFile_DeleteKeyComment00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DeleteKeyComments of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComments00 -static int tolua_AllToLua_cIniFile_DeleteKeyComments00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL); -#endif - { - bool tolua_ret = (bool) self->DeleteKeyComments(keyID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DeleteKeyComments'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DeleteKeyComments of class cIniFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComments01 -static int tolua_AllToLua_cIniFile_DeleteKeyComments01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); - const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL); -#endif - { - bool tolua_ret = (bool) self->DeleteKeyComments(keyname); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)keyname); - } - } - return 2; -tolua_lerror: - return tolua_AllToLua_cIniFile_DeleteKeyComments00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Exists of class cFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Exists00 -static int tolua_AllToLua_cFile_Exists00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - bool tolua_ret = (bool) cFile::Exists(a_FileName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_FileName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Exists'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Delete of class cFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Delete00 -static int tolua_AllToLua_cFile_Delete00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - bool tolua_ret = (bool) cFile::Delete(a_FileName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_FileName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Rename of class cFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Rename00 -static int tolua_AllToLua_cFile_Rename00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_OrigPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString a_NewPath = ((const AString) tolua_tocppstring(tolua_S,3,0)); - { - bool tolua_ret = (bool) cFile::Rename(a_OrigPath,a_NewPath); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_OrigPath); - tolua_pushcppstring(tolua_S,(const char*)a_NewPath); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Rename'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Copy of class cFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Copy00 -static int tolua_AllToLua_cFile_Copy00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_SrcFileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString a_DstFileName = ((const AString) tolua_tocppstring(tolua_S,3,0)); - { - bool tolua_ret = (bool) cFile::Copy(a_SrcFileName,a_DstFileName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_SrcFileName); - tolua_pushcppstring(tolua_S,(const char*)a_DstFileName); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Copy'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsFolder of class cFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFolder00 -static int tolua_AllToLua_cFile_IsFolder00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - bool tolua_ret = (bool) cFile::IsFolder(a_Path); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Path); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsFolder'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsFile of class cFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFile00 -static int tolua_AllToLua_cFile_IsFile00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - bool tolua_ret = (bool) cFile::IsFile(a_Path); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Path); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsFile'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSize of class cFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_GetSize00 -static int tolua_AllToLua_cFile_GetSize00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - int tolua_ret = (int) cFile::GetSize(a_FileName); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_FileName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSize'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CreateFolder of class cFile */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_CreateFolder00 -static int tolua_AllToLua_cFile_CreateFolder00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_FolderPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - bool tolua_ret = (bool) cFile::CreateFolder(a_FolderPath); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_FolderPath); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CreateFolder'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: BlockStringToType */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_BlockStringToType00 -static int tolua_AllToLua_BlockStringToType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_BlockTypeString = ((const AString) tolua_tocppstring(tolua_S,1,0)); - { - unsigned char tolua_ret = ( unsigned char) BlockStringToType(a_BlockTypeString); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_BlockTypeString); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'BlockStringToType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: StringToItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToItem00 -static int tolua_AllToLua_StringToItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_ItemTypeString = ((const AString) tolua_tocppstring(tolua_S,1,0)); - cItem* a_Item = ((cItem*) tolua_tousertype(tolua_S,2,0)); - { - bool tolua_ret = (bool) StringToItem(a_ItemTypeString,*a_Item); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_ItemTypeString); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StringToItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemToString */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemToString00 -static int tolua_AllToLua_ItemToString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,1,0)); - { - AString tolua_ret = (AString) ItemToString(*a_Item); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ItemToString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemTypeToString */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemTypeToString00 -static int tolua_AllToLua_ItemTypeToString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); - { - AString tolua_ret = (AString) ItemTypeToString(a_ItemType); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ItemTypeToString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemToFullString */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemToFullString00 -static int tolua_AllToLua_ItemToFullString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,1,0)); - { - AString tolua_ret = (AString) ItemToFullString(*a_Item); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ItemToFullString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: StringToBiome */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToBiome00 -static int tolua_AllToLua_StringToBiome00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_BiomeString = ((const AString) tolua_tocppstring(tolua_S,1,0)); - { - EMCSBiome tolua_ret = (EMCSBiome) StringToBiome(a_BiomeString); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_BiomeString); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StringToBiome'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: StringToMobType */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToMobType00 -static int tolua_AllToLua_StringToMobType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_MobString = ((const AString) tolua_tocppstring(tolua_S,1,0)); - { - int tolua_ret = (int) StringToMobType(a_MobString); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_MobString); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: StringToDimension */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToDimension00 -static int tolua_AllToLua_StringToDimension00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_DimensionString = ((const AString) tolua_tocppstring(tolua_S,1,0)); - { - eDimension tolua_ret = (eDimension) StringToDimension(a_DimensionString); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_DimensionString); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StringToDimension'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: DamageTypeToString */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_DamageTypeToString00 -static int tolua_AllToLua_DamageTypeToString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,1,0)); - { - AString tolua_ret = (AString) DamageTypeToString(a_DamageType); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DamageTypeToString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: StringToDamageType */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToDamageType00 -static int tolua_AllToLua_StringToDamageType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_DamageString = ((const AString) tolua_tocppstring(tolua_S,1,0)); - { - eDamageType tolua_ret = (eDamageType) StringToDamageType(a_DamageString); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_DamageString); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StringToDamageType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: GetIniItemSet */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_GetIniItemSet00 -static int tolua_AllToLua_GetIniItemSet00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err)) || - !tolua_isstring(tolua_S,2,0,&tolua_err) || - !tolua_isstring(tolua_S,3,0,&tolua_err) || - !tolua_isstring(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cIniFile* a_IniFile = ((cIniFile*) tolua_tousertype(tolua_S,1,0)); - const char* a_Section = ((const char*) tolua_tostring(tolua_S,2,0)); - const char* a_Key = ((const char*) tolua_tostring(tolua_S,3,0)); - const char* a_Default = ((const char*) tolua_tostring(tolua_S,4,0)); - { - cItem tolua_ret = (cItem) GetIniItemSet(*a_IniFile,a_Section,a_Key,a_Default); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetIniItemSet'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: TrimString */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_TrimString00 -static int tolua_AllToLua_TrimString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString str = ((const AString) tolua_tocppstring(tolua_S,1,0)); - { - AString tolua_ret = (AString) TrimString(str); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)str); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'TrimString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: NoCaseCompare */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_NoCaseCompare00 -static int tolua_AllToLua_NoCaseCompare00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString s1 = ((const AString) tolua_tocppstring(tolua_S,1,0)); - const AString s2 = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - int tolua_ret = (int) NoCaseCompare(s1,s2); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)s1); - tolua_pushcppstring(tolua_S,(const char*)s2); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'NoCaseCompare'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ReplaceString */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ReplaceString00 -static int tolua_AllToLua_ReplaceString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - AString iHayStack = ((AString) tolua_tocppstring(tolua_S,1,0)); - const AString iNeedle = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString iReplaceWith = ((const AString) tolua_tocppstring(tolua_S,3,0)); - { - ReplaceString(iHayStack,iNeedle,iReplaceWith); - tolua_pushcppstring(tolua_S,(const char*)iHayStack); - tolua_pushcppstring(tolua_S,(const char*)iNeedle); - tolua_pushcppstring(tolua_S,(const char*)iReplaceWith); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ReplaceString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: EscapeString */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_EscapeString00 -static int tolua_AllToLua_EscapeString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,1,0)); - { - AString tolua_ret = (AString) EscapeString(a_Message); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Message); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'EscapeString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: StripColorCodes */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_StripColorCodes00 -static int tolua_AllToLua_StripColorCodes00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,1,0)); - { - AString tolua_ret = (AString) StripColorCodes(a_Message); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Message); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StripColorCodes'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: g_BlockLightValue */ -#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockLightValue -static int tolua_get_AllToLua_g_BlockLightValue(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)g_BlockLightValue[tolua_index]); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: g_BlockLightValue */ -#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockLightValue -static int tolua_set_AllToLua_g_BlockLightValue(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - g_BlockLightValue[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0)); - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: g_BlockSpreadLightFalloff */ -#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockSpreadLightFalloff -static int tolua_get_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)g_BlockSpreadLightFalloff[tolua_index]); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: g_BlockSpreadLightFalloff */ -#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockSpreadLightFalloff -static int tolua_set_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - g_BlockSpreadLightFalloff[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0)); - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: g_BlockTransparent */ -#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockTransparent -static int tolua_get_AllToLua_g_BlockTransparent(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - tolua_pushboolean(tolua_S,(bool)g_BlockTransparent[tolua_index]); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: g_BlockTransparent */ -#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockTransparent -static int tolua_set_AllToLua_g_BlockTransparent(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - g_BlockTransparent[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: g_BlockOneHitDig */ -#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockOneHitDig -static int tolua_get_AllToLua_g_BlockOneHitDig(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - tolua_pushboolean(tolua_S,(bool)g_BlockOneHitDig[tolua_index]); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: g_BlockOneHitDig */ -#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockOneHitDig -static int tolua_set_AllToLua_g_BlockOneHitDig(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - g_BlockOneHitDig[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: g_BlockPistonBreakable */ -#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockPistonBreakable -static int tolua_get_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - tolua_pushboolean(tolua_S,(bool)g_BlockPistonBreakable[tolua_index]); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: g_BlockPistonBreakable */ -#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockPistonBreakable -static int tolua_set_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - g_BlockPistonBreakable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: g_BlockIsSnowable */ -#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsSnowable -static int tolua_get_AllToLua_g_BlockIsSnowable(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - tolua_pushboolean(tolua_S,(bool)g_BlockIsSnowable[tolua_index]); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: g_BlockIsSnowable */ -#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsSnowable -static int tolua_set_AllToLua_g_BlockIsSnowable(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - g_BlockIsSnowable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: g_BlockRequiresSpecialTool */ -#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockRequiresSpecialTool -static int tolua_get_AllToLua_g_BlockRequiresSpecialTool(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - tolua_pushboolean(tolua_S,(bool)g_BlockRequiresSpecialTool[tolua_index]); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: g_BlockRequiresSpecialTool */ -#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockRequiresSpecialTool -static int tolua_set_AllToLua_g_BlockRequiresSpecialTool(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - g_BlockRequiresSpecialTool[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: g_BlockIsSolid */ -#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsSolid -static int tolua_get_AllToLua_g_BlockIsSolid(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - tolua_pushboolean(tolua_S,(bool)g_BlockIsSolid[tolua_index]); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: g_BlockIsSolid */ -#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsSolid -static int tolua_set_AllToLua_g_BlockIsSolid(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - g_BlockIsSolid[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: g_BlockIsTorchPlaceable */ -#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsTorchPlaceable -static int tolua_get_AllToLua_g_BlockIsTorchPlaceable(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - tolua_pushboolean(tolua_S,(bool)g_BlockIsTorchPlaceable[tolua_index]); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: g_BlockIsTorchPlaceable */ -#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsTorchPlaceable -static int tolua_set_AllToLua_g_BlockIsTorchPlaceable(lua_State* tolua_S) -{ - int tolua_index; -#ifndef TOLUA_RELEASE - { - tolua_Error tolua_err; - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); - } -#endif - tolua_index = (int)tolua_tonumber(tolua_S,2,0); -#ifndef TOLUA_RELEASE - if (tolua_index<0 || tolua_index>=256) - tolua_error(tolua_S,"array indexing out of range.",NULL); -#endif - g_BlockIsTorchPlaceable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ClickActionToString */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ClickActionToString00 -static int tolua_AllToLua_ClickActionToString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - eClickAction a_ClickAction = ((eClickAction) (int) tolua_tonumber(tolua_S,1,0)); - { - const char* tolua_ret = (const char*) ClickActionToString(a_ClickAction); - tolua_pushstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ClickActionToString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: IsValidBlock */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_IsValidBlock00 -static int tolua_AllToLua_IsValidBlock00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - int a_BlockType = ((int) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) IsValidBlock(a_BlockType); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsValidBlock'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: IsValidItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_IsValidItem00 -static int tolua_AllToLua_IsValidItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - int a_ItemType = ((int) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) IsValidItem(a_ItemType); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsValidItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: AddFaceDirection */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_AddFaceDirection00 -static int tolua_AllToLua_AddFaceDirection00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isboolean(tolua_S,5,1,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - int a_BlockX = ((int) tolua_tonumber(tolua_S,1,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0)); - char a_BlockFace = ((char) tolua_tonumber(tolua_S,4,0)); - bool a_bInverse = ((bool) tolua_toboolean(tolua_S,5,false)); - { - AddFaceDirection(a_BlockX,a_BlockY,a_BlockZ,a_BlockFace,a_bInverse); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockX); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockY); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockZ); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddFaceDirection'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsPickaxe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsPickaxe00 -static int tolua_AllToLua_ItemCategory_IsPickaxe00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsPickaxe(a_ItemID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsPickaxe'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsAxe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsAxe00 -static int tolua_AllToLua_ItemCategory_IsAxe00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsAxe(a_ItemID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsAxe'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsSword */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsSword00 -static int tolua_AllToLua_ItemCategory_IsSword00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsSword(a_ItemID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSword'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsHoe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsHoe00 -static int tolua_AllToLua_ItemCategory_IsHoe00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsHoe(a_ItemID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsHoe'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsShovel */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsShovel00 -static int tolua_AllToLua_ItemCategory_IsShovel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsShovel(a_ItemID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsShovel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsTool */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsTool00 -static int tolua_AllToLua_ItemCategory_IsTool00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsTool(a_ItemID); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsTool'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsHelmet */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsHelmet00 -static int tolua_AllToLua_ItemCategory_IsHelmet00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsHelmet(a_ItemType); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsHelmet'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsChestPlate */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsChestPlate00 -static int tolua_AllToLua_ItemCategory_IsChestPlate00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsChestPlate(a_ItemType); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsChestPlate'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsLeggings */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsLeggings00 -static int tolua_AllToLua_ItemCategory_IsLeggings00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsLeggings(a_ItemType); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsLeggings'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsBoots */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsBoots00 -static int tolua_AllToLua_ItemCategory_IsBoots00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsBoots(a_ItemType); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsBoots'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: ItemCategory::IsArmor */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsArmor00 -static int tolua_AllToLua_ItemCategory_IsArmor00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnumber(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); - { - bool tolua_ret = (bool) ItemCategory::IsArmor(a_ItemType); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsArmor'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: GetTime */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_GetTime00 -static int tolua_AllToLua_GetTime00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isnoobj(tolua_S,1,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - unsigned int tolua_ret = (unsigned int) GetTime(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetTime'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* function: GetChar */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_GetChar00 -static int tolua_AllToLua_GetChar00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - std::string a_Str = ((std::string) tolua_tocppstring(tolua_S,1,0)); - unsigned int a_Idx = ((unsigned int) tolua_tonumber(tolua_S,2,0)); - { - std::string tolua_ret = (std::string) GetChar(a_Str,a_Idx); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Str); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetChar'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Color of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Color -static int tolua_get_cChatColor_Color(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Color); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Delimiter of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Delimiter -static int tolua_get_cChatColor_Delimiter(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Delimiter); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Black of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Black -static int tolua_get_cChatColor_Black(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Black); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Navy of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Navy -static int tolua_get_cChatColor_Navy(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Navy); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Green of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Green -static int tolua_get_cChatColor_Green(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Green); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Blue of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Blue -static int tolua_get_cChatColor_Blue(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Blue); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Red of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Red -static int tolua_get_cChatColor_Red(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Red); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Purple of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Purple -static int tolua_get_cChatColor_Purple(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Purple); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Gold of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Gold -static int tolua_get_cChatColor_Gold(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Gold); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: LightGray of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightGray -static int tolua_get_cChatColor_LightGray(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightGray); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Gray of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Gray -static int tolua_get_cChatColor_Gray(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Gray); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: DarkPurple of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_DarkPurple -static int tolua_get_cChatColor_DarkPurple(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::DarkPurple); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: LightGreen of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightGreen -static int tolua_get_cChatColor_LightGreen(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightGreen); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: LightBlue of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightBlue -static int tolua_get_cChatColor_LightBlue(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightBlue); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Rose of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Rose -static int tolua_get_cChatColor_Rose(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Rose); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: LightPurple of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightPurple -static int tolua_get_cChatColor_LightPurple(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightPurple); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Yellow of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Yellow -static int tolua_get_cChatColor_Yellow(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Yellow); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: White of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_White -static int tolua_get_cChatColor_White(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::White); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Random of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Random -static int tolua_get_cChatColor_Random(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Random); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Bold of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Bold -static int tolua_get_cChatColor_Bold(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Bold); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Strikethrough of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Strikethrough -static int tolua_get_cChatColor_Strikethrough(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Strikethrough); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Underlined of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Underlined -static int tolua_get_cChatColor_Underlined(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Underlined); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Italic of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Italic -static int tolua_get_cChatColor_Italic(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Italic); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Plain of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Plain -static int tolua_get_cChatColor_Plain(lua_State* tolua_S) -{ - tolua_pushcppstring(tolua_S,(const char*)cChatColor::Plain); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MakeColor of class cChatColor */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChatColor_MakeColor00 -static int tolua_AllToLua_cChatColor_MakeColor00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cChatColor",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - char a_Color = ((char) tolua_tonumber(tolua_S,2,0)); - { - const std::string tolua_ret = (const std::string) cChatColor::MakeColor(a_Color); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MakeColor'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPlayer of class cClientHandle */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetPlayer00 -static int tolua_AllToLua_cClientHandle_GetPlayer00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPlayer'", NULL); -#endif - { - cPlayer* tolua_ret = (cPlayer*) self->GetPlayer(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPlayer"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPlayer'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Kick of class cClientHandle */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_Kick00 -static int tolua_AllToLua_cClientHandle_Kick00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); - const AString a_Reason = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Kick'", NULL); -#endif - { - self->Kick(a_Reason); - tolua_pushcppstring(tolua_S,(const char*)a_Reason); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Kick'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SendBlockChange of class cClientHandle */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SendBlockChange00 -static int tolua_AllToLua_cClientHandle_SendBlockChange00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendBlockChange'", NULL); -#endif - { - self->SendBlockChange(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SendBlockChange'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetUsername of class cClientHandle */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetUsername00 -static int tolua_AllToLua_cClientHandle_GetUsername00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUsername'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetUsername(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetUsername'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetUsername of class cClientHandle */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SetUsername00 -static int tolua_AllToLua_cClientHandle_SetUsername00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); - const AString a_Username = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUsername'", NULL); -#endif - { - self->SetUsername(a_Username); - tolua_pushcppstring(tolua_S,(const char*)a_Username); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetUsername'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPing of class cClientHandle */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetPing00 -static int tolua_AllToLua_cClientHandle_GetPing00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPing'", NULL); -#endif - { - short tolua_ret = (short) self->GetPing(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPing'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetViewDistance of class cClientHandle */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SetViewDistance00 -static int tolua_AllToLua_cClientHandle_SetViewDistance00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); - int a_ViewDistance = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetViewDistance'", NULL); -#endif - { - self->SetViewDistance(a_ViewDistance); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetViewDistance'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetViewDistance of class cClientHandle */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetViewDistance00 -static int tolua_AllToLua_cClientHandle_GetViewDistance00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetViewDistance'", NULL); -#endif - { - int tolua_ret = (int) self->GetViewDistance(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetViewDistance'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetUniqueID of class cClientHandle */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetUniqueID00 -static int tolua_AllToLua_cClientHandle_GetUniqueID00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUniqueID'", NULL); -#endif - { - int tolua_ret = (int) self->GetUniqueID(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetUniqueID'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: DamageType of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_DamageType -static int tolua_get_TakeDamageInfo_DamageType(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'DamageType'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->DamageType); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: DamageType of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_DamageType -static int tolua_set_TakeDamageInfo_DamageType(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'DamageType'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Attacker of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_Attacker_ptr -static int tolua_get_TakeDamageInfo_Attacker_ptr(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Attacker'",NULL); -#endif - tolua_pushusertype(tolua_S,(void*)self->Attacker,"cEntity"); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Attacker of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_Attacker_ptr -static int tolua_set_TakeDamageInfo_Attacker_ptr(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Attacker'",NULL); - if (!tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Attacker = ((cEntity*) tolua_tousertype(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: RawDamage of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_RawDamage -static int tolua_get_TakeDamageInfo_RawDamage(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RawDamage'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->RawDamage); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: RawDamage of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_RawDamage -static int tolua_set_TakeDamageInfo_RawDamage(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RawDamage'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->RawDamage = ((int) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: FinalDamage of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_FinalDamage -static int tolua_get_TakeDamageInfo_FinalDamage(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'FinalDamage'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->FinalDamage); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: FinalDamage of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_FinalDamage -static int tolua_set_TakeDamageInfo_FinalDamage(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'FinalDamage'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->FinalDamage = ((int) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Knockback of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_Knockback -static int tolua_get_TakeDamageInfo_Knockback(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Knockback'",NULL); -#endif - tolua_pushusertype(tolua_S,(void*)&self->Knockback,"Vector3d"); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Knockback of class TakeDamageInfo */ -#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_Knockback -static int tolua_set_TakeDamageInfo_Knockback(lua_State* tolua_S) -{ - TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Knockback'",NULL); - if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3d",0,&tolua_err))) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Knockback = *((Vector3d*) tolua_tousertype(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEntityType of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEntityType00 -static int tolua_AllToLua_cEntity_GetEntityType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEntityType'", NULL); -#endif - { - cEntity::eEntityType tolua_ret = (cEntity::eEntityType) self->GetEntityType(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEntityType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsPlayer of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsPlayer00 -static int tolua_AllToLua_cEntity_IsPlayer00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPlayer'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsPlayer(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsPlayer'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsPickup of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsPickup00 -static int tolua_AllToLua_cEntity_IsPickup00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPickup'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsPickup(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsPickup'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsMob of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMob00 -static int tolua_AllToLua_cEntity_IsMob00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsMob'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsMob(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsMob'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsFallingBlock of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsFallingBlock00 -static int tolua_AllToLua_cEntity_IsFallingBlock00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFallingBlock'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsFallingBlock(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsFallingBlock'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsMinecart of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMinecart00 -static int tolua_AllToLua_cEntity_IsMinecart00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsMinecart'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsMinecart(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsMinecart'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsBoat of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsBoat00 -static int tolua_AllToLua_cEntity_IsBoat00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsBoat'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsBoat(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsBoat'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsTNT of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsTNT00 -static int tolua_AllToLua_cEntity_IsTNT00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsTNT'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsTNT(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsTNT'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsProjectile of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsProjectile00 -static int tolua_AllToLua_cEntity_IsProjectile00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsProjectile'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsProjectile(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsProjectile'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsA of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsA00 -static int tolua_AllToLua_cEntity_IsA00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); - const char* a_ClassName = ((const char*) tolua_tostring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsA'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsA(a_ClassName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsA'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetClass of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetClass00 -static int tolua_AllToLua_cEntity_GetClass00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetClass'", NULL); -#endif - { - const char* tolua_ret = (const char*) self->GetClass(); - tolua_pushstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetClass'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetClassStatic of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetClassStatic00 -static int tolua_AllToLua_cEntity_GetClassStatic00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - const char* tolua_ret = (const char*) cEntity::GetClassStatic(); - tolua_pushstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetClassStatic'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetParentClass of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetParentClass00 -static int tolua_AllToLua_cEntity_GetParentClass00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetParentClass'", NULL); -#endif - { - const char* tolua_ret = (const char*) self->GetParentClass(); - tolua_pushstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetParentClass'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWorld of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetWorld00 -static int tolua_AllToLua_cEntity_GetWorld00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); -#endif - { - cWorld* tolua_ret = (cWorld*) self->GetWorld(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHeadYaw of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHeadYaw00 -static int tolua_AllToLua_cEntity_GetHeadYaw00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeadYaw'", NULL); -#endif - { - double tolua_ret = (double) self->GetHeadYaw(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHeadYaw'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHeight of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHeight00 -static int tolua_AllToLua_cEntity_GetHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); -#endif - { - double tolua_ret = (double) self->GetHeight(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMass of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetMass00 -static int tolua_AllToLua_cEntity_GetMass00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMass'", NULL); -#endif - { - double tolua_ret = (double) self->GetMass(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMass'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPosition of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosition00 -static int tolua_AllToLua_cEntity_GetPosition00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosition'", NULL); -#endif - { - const Vector3d& tolua_ret = (const Vector3d&) self->GetPosition(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPosition'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPosX of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosX00 -static int tolua_AllToLua_cEntity_GetPosX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosX'", NULL); -#endif - { - double tolua_ret = (double) self->GetPosX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPosX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPosY of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosY00 -static int tolua_AllToLua_cEntity_GetPosY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosY'", NULL); -#endif - { - double tolua_ret = (double) self->GetPosY(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPosY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPosZ of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosZ00 -static int tolua_AllToLua_cEntity_GetPosZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosZ'", NULL); -#endif - { - double tolua_ret = (double) self->GetPosZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPosZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRot of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRot00 -static int tolua_AllToLua_cEntity_GetRot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRot'", NULL); -#endif - { - const Vector3d& tolua_ret = (const Vector3d&) self->GetRot(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRotation of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRotation00 -static int tolua_AllToLua_cEntity_GetRotation00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRotation'", NULL); -#endif - { - double tolua_ret = (double) self->GetRotation(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRotation'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetYaw of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetYaw00 -static int tolua_AllToLua_cEntity_GetYaw00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetYaw'", NULL); -#endif - { - double tolua_ret = (double) self->GetYaw(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetYaw'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPitch of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPitch00 -static int tolua_AllToLua_cEntity_GetPitch00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL); -#endif - { - double tolua_ret = (double) self->GetPitch(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPitch'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRoll of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRoll00 -static int tolua_AllToLua_cEntity_GetRoll00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRoll'", NULL); -#endif - { - double tolua_ret = (double) self->GetRoll(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRoll'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetLookVector of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetLookVector00 -static int tolua_AllToLua_cEntity_GetLookVector00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLookVector'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->GetLookVector(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetLookVector'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSpeed of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeed00 -static int tolua_AllToLua_cEntity_GetSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeed'", NULL); -#endif - { - const Vector3d& tolua_ret = (const Vector3d&) self->GetSpeed(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSpeedX of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedX00 -static int tolua_AllToLua_cEntity_GetSpeedX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedX'", NULL); -#endif - { - double tolua_ret = (double) self->GetSpeedX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSpeedX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSpeedY of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedY00 -static int tolua_AllToLua_cEntity_GetSpeedY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedY'", NULL); -#endif - { - double tolua_ret = (double) self->GetSpeedY(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSpeedY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSpeedZ of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedZ00 -static int tolua_AllToLua_cEntity_GetSpeedZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedZ'", NULL); -#endif - { - double tolua_ret = (double) self->GetSpeedZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSpeedZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWidth of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetWidth00 -static int tolua_AllToLua_cEntity_GetWidth00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL); -#endif - { - double tolua_ret = (double) self->GetWidth(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetChunkX of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkX00 -static int tolua_AllToLua_cEntity_GetChunkX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL); -#endif - { - int tolua_ret = (int) self->GetChunkX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetChunkZ of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkZ00 -static int tolua_AllToLua_cEntity_GetChunkZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL); -#endif - { - int tolua_ret = (int) self->GetChunkZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetHeadYaw of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHeadYaw00 -static int tolua_AllToLua_cEntity_SetHeadYaw00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_HeadYaw = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeadYaw'", NULL); -#endif - { - self->SetHeadYaw(a_HeadYaw); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetHeadYaw'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetHeight of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHeight00 -static int tolua_AllToLua_cEntity_SetHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_Height = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeight'", NULL); -#endif - { - self->SetHeight(a_Height); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetMass of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetMass00 -static int tolua_AllToLua_cEntity_SetMass00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_Mass = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMass'", NULL); -#endif - { - self->SetMass(a_Mass); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetMass'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPosX of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosX00 -static int tolua_AllToLua_cEntity_SetPosX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosX'", NULL); -#endif - { - self->SetPosX(a_PosX); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetPosX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPosY of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosY00 -static int tolua_AllToLua_cEntity_SetPosY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_PosY = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosY'", NULL); -#endif - { - self->SetPosY(a_PosY); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetPosY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPosZ of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosZ00 -static int tolua_AllToLua_cEntity_SetPosZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_PosZ = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosZ'", NULL); -#endif - { - self->SetPosZ(a_PosZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetPosZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPosition of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosition00 -static int tolua_AllToLua_cEntity_SetPosition00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosition'", NULL); -#endif - { - self->SetPosition(a_PosX,a_PosY,a_PosZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetPosition'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPosition of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosition01 -static int tolua_AllToLua_cEntity_SetPosition01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosition'", NULL); -#endif - { - self->SetPosition(*a_Pos); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cEntity_SetPosition00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRot of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRot00 -static int tolua_AllToLua_cEntity_SetRot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - const Vector3f* a_Rot = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRot'", NULL); -#endif - { - self->SetRot(*a_Rot); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRotation of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRotation00 -static int tolua_AllToLua_cEntity_SetRotation00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_Rotation = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRotation'", NULL); -#endif - { - self->SetRotation(a_Rotation); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRotation'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetYaw of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetYaw00 -static int tolua_AllToLua_cEntity_SetYaw00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_Yaw = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetYaw'", NULL); -#endif - { - self->SetYaw(a_Yaw); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetYaw'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPitch of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPitch00 -static int tolua_AllToLua_cEntity_SetPitch00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_Pitch = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitch'", NULL); -#endif - { - self->SetPitch(a_Pitch); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetPitch'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRoll of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRoll00 -static int tolua_AllToLua_cEntity_SetRoll00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_Roll = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRoll'", NULL); -#endif - { - self->SetRoll(a_Roll); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRoll'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSpeed of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed00 -static int tolua_AllToLua_cEntity_SetSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_SpeedX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_SpeedY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_SpeedZ = ((double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL); -#endif - { - self->SetSpeed(a_SpeedX,a_SpeedY,a_SpeedZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSpeed of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed01 -static int tolua_AllToLua_cEntity_SetSpeed01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_Speed = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL); -#endif - { - self->SetSpeed(*a_Speed); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cEntity_SetSpeed00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSpeedX of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedX00 -static int tolua_AllToLua_cEntity_SetSpeedX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_SpeedX = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedX'", NULL); -#endif - { - self->SetSpeedX(a_SpeedX); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSpeedX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSpeedY of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedY00 -static int tolua_AllToLua_cEntity_SetSpeedY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_SpeedY = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedY'", NULL); -#endif - { - self->SetSpeedY(a_SpeedY); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSpeedY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSpeedZ of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedZ00 -static int tolua_AllToLua_cEntity_SetSpeedZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_SpeedZ = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedZ'", NULL); -#endif - { - self->SetSpeedZ(a_SpeedZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSpeedZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetWidth of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetWidth00 -static int tolua_AllToLua_cEntity_SetWidth00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_Width = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWidth'", NULL); -#endif - { - self->SetWidth(a_Width); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetWidth'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddPosX of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosX00 -static int tolua_AllToLua_cEntity_AddPosX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_AddPosX = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosX'", NULL); -#endif - { - self->AddPosX(a_AddPosX); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddPosX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddPosY of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosY00 -static int tolua_AllToLua_cEntity_AddPosY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_AddPosY = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosY'", NULL); -#endif - { - self->AddPosY(a_AddPosY); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddPosY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddPosZ of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosZ00 -static int tolua_AllToLua_cEntity_AddPosZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_AddPosZ = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosZ'", NULL); -#endif - { - self->AddPosZ(a_AddPosZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddPosZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddPosition of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosition00 -static int tolua_AllToLua_cEntity_AddPosition00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_AddPosX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_AddPosY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_AddPosZ = ((double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosition'", NULL); -#endif - { - self->AddPosition(a_AddPosX,a_AddPosY,a_AddPosZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddPosition'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddPosition of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosition01 -static int tolua_AllToLua_cEntity_AddPosition01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_AddPos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosition'", NULL); -#endif - { - self->AddPosition(*a_AddPos); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cEntity_AddPosition00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddSpeed of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeed00 -static int tolua_AllToLua_cEntity_AddSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_AddSpeedX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_AddSpeedY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_AddSpeedZ = ((double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeed'", NULL); -#endif - { - self->AddSpeed(a_AddSpeedX,a_AddSpeedY,a_AddSpeedZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddSpeed of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeed01 -static int tolua_AllToLua_cEntity_AddSpeed01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_AddSpeed = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeed'", NULL); -#endif - { - self->AddSpeed(*a_AddSpeed); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cEntity_AddSpeed00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddSpeedX of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedX00 -static int tolua_AllToLua_cEntity_AddSpeedX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_AddSpeedX = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedX'", NULL); -#endif - { - self->AddSpeedX(a_AddSpeedX); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddSpeedX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddSpeedY of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedY00 -static int tolua_AllToLua_cEntity_AddSpeedY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_AddSpeedY = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedY'", NULL); -#endif - { - self->AddSpeedY(a_AddSpeedY); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddSpeedY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddSpeedZ of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedZ00 -static int tolua_AllToLua_cEntity_AddSpeedZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_AddSpeedZ = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedZ'", NULL); -#endif - { - self->AddSpeedZ(a_AddSpeedZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddSpeedZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SteerVehicle of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SteerVehicle00 -static int tolua_AllToLua_cEntity_SteerVehicle00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - float a_Forward = ((float) tolua_tonumber(tolua_S,2,0)); - float a_Sideways = ((float) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SteerVehicle'", NULL); -#endif - { - self->SteerVehicle(a_Forward,a_Sideways); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SteerVehicle'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetUniqueID of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetUniqueID00 -static int tolua_AllToLua_cEntity_GetUniqueID00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUniqueID'", NULL); -#endif - { - int tolua_ret = (int) self->GetUniqueID(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetUniqueID'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsDestroyed of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsDestroyed00 -static int tolua_AllToLua_cEntity_IsDestroyed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDestroyed'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsDestroyed(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsDestroyed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Destroy of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_Destroy00 -static int tolua_AllToLua_cEntity_Destroy00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,1,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - bool a_ShouldBroadcast = ((bool) tolua_toboolean(tolua_S,2,true)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Destroy'", NULL); -#endif - { - self->Destroy(a_ShouldBroadcast); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Destroy'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: TakeDamage of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage00 -static int tolua_AllToLua_cEntity_TakeDamage00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL); -#endif - { - self->TakeDamage(*a_Attacker); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'TakeDamage'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: TakeDamage of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage01 -static int tolua_AllToLua_cEntity_TakeDamage01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isusertype(tolua_S,3,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0)); - cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,3,0)); - int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0)); - double a_KnockbackAmount = ((double) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL); -#endif - { - self->TakeDamage(a_DamageType,a_Attacker,a_RawDamage,a_KnockbackAmount); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cEntity_TakeDamage00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: TakeDamage of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage02 -static int tolua_AllToLua_cEntity_TakeDamage02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isusertype(tolua_S,3,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0)); - cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,3,0)); - int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0)); - int a_FinalDamage = ((int) tolua_tonumber(tolua_S,5,0)); - double a_KnockbackAmount = ((double) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL); -#endif - { - self->TakeDamage(a_DamageType,a_Attacker,a_RawDamage,a_FinalDamage,a_KnockbackAmount); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cEntity_TakeDamage01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetGravity of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetGravity00 -static int tolua_AllToLua_cEntity_GetGravity00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGravity'", NULL); -#endif - { - float tolua_ret = (float) self->GetGravity(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetGravity'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetGravity of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetGravity00 -static int tolua_AllToLua_cEntity_SetGravity00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - float a_Gravity = ((float) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGravity'", NULL); -#endif - { - self->SetGravity(a_Gravity); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetGravity'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRotationFromSpeed of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRotationFromSpeed00 -static int tolua_AllToLua_cEntity_SetRotationFromSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRotationFromSpeed'", NULL); -#endif - { - self->SetRotationFromSpeed(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRotationFromSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPitchFromSpeed of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPitchFromSpeed00 -static int tolua_AllToLua_cEntity_SetPitchFromSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitchFromSpeed'", NULL); -#endif - { - self->SetPitchFromSpeed(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetPitchFromSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRawDamageAgainst of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRawDamageAgainst00 -static int tolua_AllToLua_cEntity_GetRawDamageAgainst00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - const cEntity* a_Receiver = ((const cEntity*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRawDamageAgainst'", NULL); -#endif - { - int tolua_ret = (int) self->GetRawDamageAgainst(*a_Receiver); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRawDamageAgainst'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetArmorCoverAgainst of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetArmorCoverAgainst00 -static int tolua_AllToLua_cEntity_GetArmorCoverAgainst00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - const cEntity* a_Attacker = ((const cEntity*) tolua_tousertype(tolua_S,2,0)); - eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,3,0)); - int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorCoverAgainst'", NULL); -#endif - { - int tolua_ret = (int) self->GetArmorCoverAgainst(a_Attacker,a_DamageType,a_RawDamage); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetArmorCoverAgainst'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetKnockbackAmountAgainst of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00 -static int tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - const cEntity* a_Receiver = ((const cEntity*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKnockbackAmountAgainst'", NULL); -#endif - { - double tolua_ret = (double) self->GetKnockbackAmountAgainst(*a_Receiver); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetKnockbackAmountAgainst'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedWeapon of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedWeapon00 -static int tolua_AllToLua_cEntity_GetEquippedWeapon00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedWeapon'", NULL); -#endif - { - cItem tolua_ret = (cItem) self->GetEquippedWeapon(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedWeapon'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedHelmet of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedHelmet00 -static int tolua_AllToLua_cEntity_GetEquippedHelmet00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedHelmet'", NULL); -#endif - { - cItem tolua_ret = (cItem) self->GetEquippedHelmet(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedHelmet'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedChestplate of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedChestplate00 -static int tolua_AllToLua_cEntity_GetEquippedChestplate00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedChestplate'", NULL); -#endif - { - cItem tolua_ret = (cItem) self->GetEquippedChestplate(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedChestplate'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedLeggings of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedLeggings00 -static int tolua_AllToLua_cEntity_GetEquippedLeggings00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedLeggings'", NULL); -#endif - { - cItem tolua_ret = (cItem) self->GetEquippedLeggings(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedLeggings'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedBoots of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedBoots00 -static int tolua_AllToLua_cEntity_GetEquippedBoots00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedBoots'", NULL); -#endif - { - cItem tolua_ret = (cItem) self->GetEquippedBoots(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedBoots'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: KilledBy of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_KilledBy00 -static int tolua_AllToLua_cEntity_KilledBy00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - cEntity* a_Killer = ((cEntity*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'KilledBy'", NULL); -#endif - { - self->KilledBy(a_Killer); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'KilledBy'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Heal of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_Heal00 -static int tolua_AllToLua_cEntity_Heal00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - int a_HitPoints = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Heal'", NULL); -#endif - { - self->Heal(a_HitPoints); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Heal'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHealth of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHealth00 -static int tolua_AllToLua_cEntity_GetHealth00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHealth'", NULL); -#endif - { - int tolua_ret = (int) self->GetHealth(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHealth'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetHealth of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHealth00 -static int tolua_AllToLua_cEntity_SetHealth00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - int a_Health = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHealth'", NULL); -#endif - { - self->SetHealth(a_Health); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetHealth'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetMaxHealth of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetMaxHealth00 -static int tolua_AllToLua_cEntity_SetMaxHealth00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - int a_MaxHealth = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxHealth'", NULL); -#endif - { - self->SetMaxHealth(a_MaxHealth); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetMaxHealth'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMaxHealth of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetMaxHealth00 -static int tolua_AllToLua_cEntity_GetMaxHealth00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxHealth'", NULL); -#endif - { - int tolua_ret = (int) self->GetMaxHealth(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMaxHealth'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: StartBurning of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_StartBurning00 -static int tolua_AllToLua_cEntity_StartBurning00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - int a_TicksLeftBurning = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'StartBurning'", NULL); -#endif - { - self->StartBurning(a_TicksLeftBurning); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StartBurning'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: StopBurning of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_StopBurning00 -static int tolua_AllToLua_cEntity_StopBurning00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'StopBurning'", NULL); -#endif - { - self->StopBurning(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StopBurning'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: TeleportToEntity of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TeleportToEntity00 -static int tolua_AllToLua_cEntity_TeleportToEntity00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - cEntity* a_Entity = ((cEntity*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TeleportToEntity'", NULL); -#endif - { - self->TeleportToEntity(*a_Entity); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'TeleportToEntity'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: TeleportToCoords of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TeleportToCoords00 -static int tolua_AllToLua_cEntity_TeleportToCoords00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TeleportToCoords'", NULL); -#endif - { - self->TeleportToCoords(a_PosX,a_PosY,a_PosZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'TeleportToCoords'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsOnFire of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsOnFire00 -static int tolua_AllToLua_cEntity_IsOnFire00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsOnFire'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsOnFire(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsOnFire'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsCrouched of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsCrouched00 -static int tolua_AllToLua_cEntity_IsCrouched00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCrouched'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsCrouched(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsCrouched'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsRiding of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsRiding00 -static int tolua_AllToLua_cEntity_IsRiding00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsRiding'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsRiding(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsRiding'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSprinting of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsSprinting00 -static int tolua_AllToLua_cEntity_IsSprinting00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSprinting'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSprinting(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSprinting'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsRclking of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsRclking00 -static int tolua_AllToLua_cEntity_IsRclking00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsRclking'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsRclking(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsRclking'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInvisible of class cEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsInvisible00 -static int tolua_AllToLua_cEntity_IsInvisible00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInvisible'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInvisible(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsInvisible'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetExperience of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetExperience00 -static int tolua_AllToLua_cPlayer_SetExperience00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - int a_XpTotal = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetExperience'", NULL); -#endif - { - bool tolua_ret = (bool) self->SetExperience(a_XpTotal); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetExperience'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddExperience of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddExperience00 -static int tolua_AllToLua_cPlayer_AddExperience00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - int a_Xp_delta = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddExperience'", NULL); -#endif - { - int tolua_ret = (int) self->AddExperience(a_Xp_delta); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddExperience'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: XpGetTotal of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_XpGetTotal00 -static int tolua_AllToLua_cPlayer_XpGetTotal00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'XpGetTotal'", NULL); -#endif - { - int tolua_ret = (int) self->XpGetTotal(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'XpGetTotal'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: XpGetLevel of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_XpGetLevel00 -static int tolua_AllToLua_cPlayer_XpGetLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'XpGetLevel'", NULL); -#endif - { - int tolua_ret = (int) self->XpGetLevel(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'XpGetLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: XpGetPercentage of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_XpGetPercentage00 -static int tolua_AllToLua_cPlayer_XpGetPercentage00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'XpGetPercentage'", NULL); -#endif - { - float tolua_ret = (float) self->XpGetPercentage(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'XpGetPercentage'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEyeHeight of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyeHeight00 -static int tolua_AllToLua_cPlayer_GetEyeHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEyeHeight'", NULL); -#endif - { - double tolua_ret = (double) self->GetEyeHeight(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEyeHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEyePosition of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyePosition00 -static int tolua_AllToLua_cPlayer_GetEyePosition00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEyePosition'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->GetEyePosition(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEyePosition'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsOnGround of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsOnGround00 -static int tolua_AllToLua_cPlayer_IsOnGround00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsOnGround'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsOnGround(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsOnGround'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetStance of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetStance00 -static int tolua_AllToLua_cPlayer_GetStance00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStance'", NULL); -#endif - { - const double tolua_ret = (const double) self->GetStance(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetStance'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetInventory of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetInventory00 -static int tolua_AllToLua_cPlayer_GetInventory00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventory'", NULL); -#endif - { - cInventory& tolua_ret = (cInventory&) self->GetInventory(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cInventory"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetInventory'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedItem of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEquippedItem00 -static int tolua_AllToLua_cPlayer_GetEquippedItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedItem'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetEquippedItem(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetThrowStartPos of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetThrowStartPos00 -static int tolua_AllToLua_cPlayer_GetThrowStartPos00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetThrowStartPos'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->GetThrowStartPos(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetThrowStartPos'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetThrowSpeed of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetThrowSpeed00 -static int tolua_AllToLua_cPlayer_GetThrowSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); - double a_SpeedCoeff = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetThrowSpeed'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->GetThrowSpeed(a_SpeedCoeff); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetThrowSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetGameMode of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetGameMode00 -static int tolua_AllToLua_cPlayer_GetGameMode00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGameMode'", NULL); -#endif - { - eGameMode tolua_ret = (eGameMode) self->GetGameMode(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetGameMode'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEffectiveGameMode of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEffectiveGameMode00 -static int tolua_AllToLua_cPlayer_GetEffectiveGameMode00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEffectiveGameMode'", NULL); -#endif - { - eGameMode tolua_ret = (eGameMode) self->GetEffectiveGameMode(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEffectiveGameMode'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetGameMode of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetGameMode00 -static int tolua_AllToLua_cPlayer_SetGameMode00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - eGameMode a_GameMode = ((eGameMode) (int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGameMode'", NULL); -#endif - { - self->SetGameMode(a_GameMode); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetGameMode'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsGameModeCreative of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeCreative00 -static int tolua_AllToLua_cPlayer_IsGameModeCreative00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeCreative'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsGameModeCreative(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsGameModeSurvival of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeSurvival00 -static int tolua_AllToLua_cPlayer_IsGameModeSurvival00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeSurvival'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsGameModeSurvival(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsGameModeAdventure of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeAdventure00 -static int tolua_AllToLua_cPlayer_IsGameModeAdventure00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeAdventure'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsGameModeAdventure(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetIP of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetIP00 -static int tolua_AllToLua_cPlayer_GetIP00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIP'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetIP(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetIP'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MoveTo of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_MoveTo00 -static int tolua_AllToLua_cPlayer_MoveTo00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_NewPos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MoveTo'", NULL); -#endif - { - self->MoveTo(*a_NewPos); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MoveTo'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWindow of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetWindow00 -static int tolua_AllToLua_cPlayer_GetWindow00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindow'", NULL); -#endif - { - cWindow* tolua_ret = (cWindow*) self->GetWindow(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWindow"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWindow'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CloseWindow of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CloseWindow00 -static int tolua_AllToLua_cPlayer_CloseWindow00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,1,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - bool a_CanRefuse = ((bool) tolua_toboolean(tolua_S,2,true)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CloseWindow'", NULL); -#endif - { - self->CloseWindow(a_CanRefuse); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CloseWindow'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CloseWindowIfID of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CloseWindowIfID00 -static int tolua_AllToLua_cPlayer_CloseWindowIfID00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isboolean(tolua_S,3,1,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - char a_WindowID = ((char) tolua_tonumber(tolua_S,2,0)); - bool a_CanRefuse = ((bool) tolua_toboolean(tolua_S,3,true)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CloseWindowIfID'", NULL); -#endif - { - self->CloseWindowIfID(a_WindowID,a_CanRefuse); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CloseWindowIfID'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetClientHandle of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetClientHandle00 -static int tolua_AllToLua_cPlayer_GetClientHandle00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetClientHandle'", NULL); -#endif - { - cClientHandle* tolua_ret = (cClientHandle*) self->GetClientHandle(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cClientHandle"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetClientHandle'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SendMessage of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SendMessage00 -static int tolua_AllToLua_cPlayer_SendMessage00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendMessage'", NULL); -#endif - { - self->SendMessage(a_Message); - tolua_pushcppstring(tolua_S,(const char*)a_Message); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SendMessage'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetName of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetName00 -static int tolua_AllToLua_cPlayer_GetName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetName(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetName of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetName00 -static int tolua_AllToLua_cPlayer_SetName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - const AString a_Name = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL); -#endif - { - self->SetName(a_Name); - tolua_pushcppstring(tolua_S,(const char*)a_Name); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddToGroup of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddToGroup00 -static int tolua_AllToLua_cPlayer_AddToGroup00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - const AString a_GroupName = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddToGroup'", NULL); -#endif - { - self->AddToGroup(a_GroupName); - tolua_pushcppstring(tolua_S,(const char*)a_GroupName); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddToGroup'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RemoveFromGroup of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_RemoveFromGroup00 -static int tolua_AllToLua_cPlayer_RemoveFromGroup00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - const AString a_GroupName = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveFromGroup'", NULL); -#endif - { - self->RemoveFromGroup(a_GroupName); - tolua_pushcppstring(tolua_S,(const char*)a_GroupName); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RemoveFromGroup'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CanUseCommand of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CanUseCommand00 -static int tolua_AllToLua_cPlayer_CanUseCommand00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CanUseCommand'", NULL); -#endif - { - bool tolua_ret = (bool) self->CanUseCommand(a_Command); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Command); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CanUseCommand'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HasPermission of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_HasPermission00 -static int tolua_AllToLua_cPlayer_HasPermission00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - const AString a_Permission = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasPermission'", NULL); -#endif - { - bool tolua_ret = (bool) self->HasPermission(a_Permission); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Permission); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HasPermission'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInGroup of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsInGroup00 -static int tolua_AllToLua_cPlayer_IsInGroup00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - const AString a_Group = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInGroup'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInGroup(a_Group); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Group); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsInGroup'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetColor of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetColor00 -static int tolua_AllToLua_cPlayer_GetColor00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetColor(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetColor'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: TossItem of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_TossItem00 -static int tolua_AllToLua_cPlayer_TossItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,1,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnumber(tolua_S,5,1,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - bool a_bDraggingItem = ((bool) tolua_toboolean(tolua_S,2,0)); - char a_Amount = ((char) tolua_tonumber(tolua_S,3,1)); - short a_CreateType = ((short) tolua_tonumber(tolua_S,4,0)); - short a_CreateHealth = ((short) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TossItem'", NULL); -#endif - { - self->TossItem(a_bDraggingItem,a_Amount,a_CreateType,a_CreateHealth); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'TossItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Heal of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Heal00 -static int tolua_AllToLua_cPlayer_Heal00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - int a_Health = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Heal'", NULL); -#endif - { - self->Heal(a_Health); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Heal'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFoodLevel of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodLevel00 -static int tolua_AllToLua_cPlayer_GetFoodLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodLevel'", NULL); -#endif - { - int tolua_ret = (int) self->GetFoodLevel(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFoodLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFoodSaturationLevel of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodSaturationLevel00 -static int tolua_AllToLua_cPlayer_GetFoodSaturationLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodSaturationLevel'", NULL); -#endif - { - double tolua_ret = (double) self->GetFoodSaturationLevel(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFoodSaturationLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFoodTickTimer of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodTickTimer00 -static int tolua_AllToLua_cPlayer_GetFoodTickTimer00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodTickTimer'", NULL); -#endif - { - int tolua_ret = (int) self->GetFoodTickTimer(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFoodTickTimer'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFoodExhaustionLevel of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00 -static int tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodExhaustionLevel'", NULL); -#endif - { - double tolua_ret = (double) self->GetFoodExhaustionLevel(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFoodExhaustionLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFoodPoisonedTicksRemaining of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00 -static int tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodPoisonedTicksRemaining'", NULL); -#endif - { - int tolua_ret = (int) self->GetFoodPoisonedTicksRemaining(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFoodPoisonedTicksRemaining'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetAirLevel of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetAirLevel00 -static int tolua_AllToLua_cPlayer_GetAirLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetAirLevel'", NULL); -#endif - { - int tolua_ret = (int) self->GetAirLevel(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetAirLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSatiated of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSatiated00 -static int tolua_AllToLua_cPlayer_IsSatiated00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSatiated'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSatiated(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSatiated'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetFoodLevel of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodLevel00 -static int tolua_AllToLua_cPlayer_SetFoodLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - int a_FoodLevel = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodLevel'", NULL); -#endif - { - self->SetFoodLevel(a_FoodLevel); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetFoodLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetFoodSaturationLevel of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodSaturationLevel00 -static int tolua_AllToLua_cPlayer_SetFoodSaturationLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - double a_FoodSaturationLevel = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodSaturationLevel'", NULL); -#endif - { - self->SetFoodSaturationLevel(a_FoodSaturationLevel); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetFoodSaturationLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetFoodTickTimer of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodTickTimer00 -static int tolua_AllToLua_cPlayer_SetFoodTickTimer00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - int a_FoodTickTimer = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodTickTimer'", NULL); -#endif - { - self->SetFoodTickTimer(a_FoodTickTimer); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetFoodTickTimer'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetFoodExhaustionLevel of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00 -static int tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - double a_FoodExhaustionLevel = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodExhaustionLevel'", NULL); -#endif - { - self->SetFoodExhaustionLevel(a_FoodExhaustionLevel); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetFoodExhaustionLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetFoodPoisonedTicksRemaining of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00 -static int tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - int a_FoodPoisonedTicksRemaining = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodPoisonedTicksRemaining'", NULL); -#endif - { - self->SetFoodPoisonedTicksRemaining(a_FoodPoisonedTicksRemaining); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetFoodPoisonedTicksRemaining'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Feed of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Feed00 -static int tolua_AllToLua_cPlayer_Feed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - int a_Food = ((int) tolua_tonumber(tolua_S,2,0)); - double a_Saturation = ((double) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Feed'", NULL); -#endif - { - bool tolua_ret = (bool) self->Feed(a_Food,a_Saturation); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Feed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddFoodExhaustion of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddFoodExhaustion00 -static int tolua_AllToLua_cPlayer_AddFoodExhaustion00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - double a_Exhaustion = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddFoodExhaustion'", NULL); -#endif - { - self->AddFoodExhaustion(a_Exhaustion); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddFoodExhaustion'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FoodPoison of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_FoodPoison00 -static int tolua_AllToLua_cPlayer_FoodPoison00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - int a_NumTicks = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FoodPoison'", NULL); -#endif - { - self->FoodPoison(a_NumTicks); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FoodPoison'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsEating of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsEating00 -static int tolua_AllToLua_cPlayer_IsEating00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEating'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsEating(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsEating'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Respawn of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Respawn00 -static int tolua_AllToLua_cPlayer_Respawn00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Respawn'", NULL); -#endif - { - self->Respawn(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Respawn'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetVisible of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetVisible00 -static int tolua_AllToLua_cPlayer_SetVisible00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - bool a_bVisible = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetVisible'", NULL); -#endif - { - self->SetVisible(a_bVisible); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetVisible'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsVisible of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsVisible00 -static int tolua_AllToLua_cPlayer_IsVisible00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsVisible'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsVisible(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsVisible'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MoveToWorld of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_MoveToWorld00 -static int tolua_AllToLua_cPlayer_MoveToWorld00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - const char* a_WorldName = ((const char*) tolua_tostring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MoveToWorld'", NULL); -#endif - { - bool tolua_ret = (bool) self->MoveToWorld(a_WorldName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MoveToWorld'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: LoadPermissionsFromDisk of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00 -static int tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadPermissionsFromDisk'", NULL); -#endif - { - self->LoadPermissionsFromDisk(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'LoadPermissionsFromDisk'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMaxSpeed of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetMaxSpeed00 -static int tolua_AllToLua_cPlayer_GetMaxSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxSpeed'", NULL); -#endif - { - double tolua_ret = (double) self->GetMaxSpeed(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMaxSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNormalMaxSpeed of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetNormalMaxSpeed00 -static int tolua_AllToLua_cPlayer_GetNormalMaxSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNormalMaxSpeed'", NULL); -#endif - { - double tolua_ret = (double) self->GetNormalMaxSpeed(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNormalMaxSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSprintingMaxSpeed of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00 -static int tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSprintingMaxSpeed'", NULL); -#endif - { - double tolua_ret = (double) self->GetSprintingMaxSpeed(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSprintingMaxSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetNormalMaxSpeed of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetNormalMaxSpeed00 -static int tolua_AllToLua_cPlayer_SetNormalMaxSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - double a_Speed = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetNormalMaxSpeed'", NULL); -#endif - { - self->SetNormalMaxSpeed(a_Speed); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetNormalMaxSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSprintingMaxSpeed of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00 -static int tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - double a_Speed = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSprintingMaxSpeed'", NULL); -#endif - { - self->SetSprintingMaxSpeed(a_Speed); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSprintingMaxSpeed'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetCrouch of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetCrouch00 -static int tolua_AllToLua_cPlayer_SetCrouch00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - bool a_IsCrouched = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetCrouch'", NULL); -#endif - { - self->SetCrouch(a_IsCrouched); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetCrouch'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSprint of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetSprint00 -static int tolua_AllToLua_cPlayer_SetSprint00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - bool a_IsSprinting = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSprint'", NULL); -#endif - { - self->SetSprint(a_IsSprinting); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSprint'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSwimming of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSwimming00 -static int tolua_AllToLua_cPlayer_IsSwimming00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSwimming'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSwimming(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSwimming'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSubmerged of class cPlayer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSubmerged00 -static int tolua_AllToLua_cPlayer_IsSubmerged00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSubmerged'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSubmerged(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSubmerged'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cPickup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_new00 -static int tolua_AllToLua_cPickup_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cPickup",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) || - !tolua_isboolean(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,1,&tolua_err) || - !tolua_isnumber(tolua_S,8,1,&tolua_err) || - !tolua_isnumber(tolua_S,9,1,&tolua_err) || - !tolua_isnoobj(tolua_S,10,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); - bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0)); - float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f)); - float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f)); - float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f)); - { - cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cPickup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_new00_local -static int tolua_AllToLua_cPickup_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cPickup",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) || - !tolua_isboolean(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,1,&tolua_err) || - !tolua_isnumber(tolua_S,8,1,&tolua_err) || - !tolua_isnumber(tolua_S,9,1,&tolua_err) || - !tolua_isnoobj(tolua_S,10,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); - bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0)); - float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f)); - float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f)); - float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f)); - { - cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetItem of class cPickup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_GetItem00 -static int tolua_AllToLua_cPickup_GetItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPickup",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetItem'", NULL); -#endif - { - cItem& tolua_ret = (cItem&) self->GetItem(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CollectedBy of class cPickup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_CollectedBy00 -static int tolua_AllToLua_cPickup_CollectedBy00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPickup",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0); - cPlayer* a_Dest = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CollectedBy'", NULL); -#endif - { - bool tolua_ret = (bool) self->CollectedBy(a_Dest); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CollectedBy'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetAge of class cPickup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_GetAge00 -static int tolua_AllToLua_cPickup_GetAge00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetAge'", NULL); -#endif - { - int tolua_ret = (int) self->GetAge(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetAge'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsCollected of class cPickup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_IsCollected00 -static int tolua_AllToLua_cPickup_IsCollected00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCollected'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsCollected(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsCollected'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsPlayerCreated of class cPickup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_IsPlayerCreated00 -static int tolua_AllToLua_cPickup_IsPlayerCreated00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPlayerCreated'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsPlayerCreated(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsPlayerCreated'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetProjectileKind of class cProjectileEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetProjectileKind00 -static int tolua_AllToLua_cProjectileEntity_GetProjectileKind00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetProjectileKind'", NULL); -#endif - { - cProjectileEntity::eKind tolua_ret = (cProjectileEntity::eKind) self->GetProjectileKind(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetProjectileKind'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetCreator of class cProjectileEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetCreator00 -static int tolua_AllToLua_cProjectileEntity_GetCreator00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cProjectileEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cProjectileEntity* self = (cProjectileEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCreator'", NULL); -#endif - { - cEntity* tolua_ret = (cEntity*) self->GetCreator(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEntity"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetCreator'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMCAClassName of class cProjectileEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetMCAClassName00 -static int tolua_AllToLua_cProjectileEntity_GetMCAClassName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMCAClassName'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetMCAClassName(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMCAClassName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInGround of class cProjectileEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_IsInGround00 -static int tolua_AllToLua_cProjectileEntity_IsInGround00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInGround'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInGround(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsInGround'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPickupState of class cArrowEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_GetPickupState00 -static int tolua_AllToLua_cArrowEntity_GetPickupState00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPickupState'", NULL); -#endif - { - cArrowEntity::ePickupState tolua_ret = (cArrowEntity::ePickupState) self->GetPickupState(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPickupState'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPickupState of class cArrowEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetPickupState00 -static int tolua_AllToLua_cArrowEntity_SetPickupState00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0); - cArrowEntity::ePickupState a_PickupState = ((cArrowEntity::ePickupState) (int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPickupState'", NULL); -#endif - { - self->SetPickupState(a_PickupState); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetPickupState'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetDamageCoeff of class cArrowEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_GetDamageCoeff00 -static int tolua_AllToLua_cArrowEntity_GetDamageCoeff00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDamageCoeff'", NULL); -#endif - { - double tolua_ret = (double) self->GetDamageCoeff(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetDamageCoeff'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetDamageCoeff of class cArrowEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetDamageCoeff00 -static int tolua_AllToLua_cArrowEntity_SetDamageCoeff00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0); - double a_DamageCoeff = ((double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetDamageCoeff'", NULL); -#endif - { - self->SetDamageCoeff(a_DamageCoeff); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetDamageCoeff'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CanPickup of class cArrowEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_CanPickup00 -static int tolua_AllToLua_cArrowEntity_CanPickup00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cPlayer",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); - const cPlayer* a_Player = ((const cPlayer*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CanPickup'", NULL); -#endif - { - bool tolua_ret = (bool) self->CanPickup(*a_Player); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CanPickup'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsCritical of class cArrowEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_IsCritical00 -static int tolua_AllToLua_cArrowEntity_IsCritical00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCritical'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsCritical(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsCritical'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetIsCritical of class cArrowEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetIsCritical00 -static int tolua_AllToLua_cArrowEntity_SetIsCritical00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0); - bool a_IsCritical = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIsCritical'", NULL); -#endif - { - self->SetIsCritical(a_IsCritical); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetIsCritical'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Get of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_Get00 -static int tolua_AllToLua_cPluginManager_Get00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cPluginManager* tolua_ret = (cPluginManager*) cPluginManager::Get(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPluginManager"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPlugin of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetPlugin00 -static int tolua_AllToLua_cPluginManager_GetPlugin00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0); - const AString a_Plugin = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPlugin'", NULL); -#endif - { - cPlugin* tolua_ret = (cPlugin*) self->GetPlugin(a_Plugin); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPlugin"); - tolua_pushcppstring(tolua_S,(const char*)a_Plugin); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPlugin'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FindPlugins of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_FindPlugins00 -static int tolua_AllToLua_cPluginManager_FindPlugins00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindPlugins'", NULL); -#endif - { - self->FindPlugins(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FindPlugins'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ReloadPlugins of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ReloadPlugins00 -static int tolua_AllToLua_cPluginManager_ReloadPlugins00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReloadPlugins'", NULL); -#endif - { - self->ReloadPlugins(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ReloadPlugins'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumPlugins of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetNumPlugins00 -static int tolua_AllToLua_cPluginManager_GetNumPlugins00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlugins'", NULL); -#endif - { - unsigned int tolua_ret = (unsigned int) self->GetNumPlugins(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNumPlugins'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DisablePlugin of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_DisablePlugin00 -static int tolua_AllToLua_cPluginManager_DisablePlugin00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - const AString a_PluginName = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DisablePlugin'", NULL); -#endif - { - bool tolua_ret = (bool) self->DisablePlugin(a_PluginName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_PluginName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DisablePlugin'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: LoadPlugin of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_LoadPlugin00 -static int tolua_AllToLua_cPluginManager_LoadPlugin00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - const AString a_PluginName = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadPlugin'", NULL); -#endif - { - bool tolua_ret = (bool) self->LoadPlugin(a_PluginName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_PluginName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'LoadPlugin'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsCommandBound of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_IsCommandBound00 -static int tolua_AllToLua_cPluginManager_IsCommandBound00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCommandBound'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsCommandBound(a_Command); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Command); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsCommandBound'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetCommandPermission of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetCommandPermission00 -static int tolua_AllToLua_cPluginManager_GetCommandPermission00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCommandPermission'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetCommandPermission(a_Command); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Command); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetCommandPermission'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ExecuteCommand of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ExecuteCommand00 -static int tolua_AllToLua_cPluginManager_ExecuteCommand00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); - const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ExecuteCommand'", NULL); -#endif - { - bool tolua_ret = (bool) self->ExecuteCommand(a_Player,a_Command); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Command); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ExecuteCommand'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ForceExecuteCommand of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ForceExecuteCommand00 -static int tolua_AllToLua_cPluginManager_ForceExecuteCommand00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); - const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ForceExecuteCommand'", NULL); -#endif - { - bool tolua_ret = (bool) self->ForceExecuteCommand(a_Player,a_Command); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Command); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ForceExecuteCommand'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsConsoleCommandBound of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_IsConsoleCommandBound00 -static int tolua_AllToLua_cPluginManager_IsConsoleCommandBound00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsConsoleCommandBound'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsConsoleCommandBound(a_Command); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Command); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsConsoleCommandBound'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetName of class cPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetName00 -static int tolua_AllToLua_cPlugin_GetName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetName(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetName of class cPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_SetName00 -static int tolua_AllToLua_cPlugin_SetName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlugin",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0); - const AString a_Name = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL); -#endif - { - self->SetName(a_Name); - tolua_pushcppstring(tolua_S,(const char*)a_Name); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetVersion of class cPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetVersion00 -static int tolua_AllToLua_cPlugin_GetVersion00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetVersion'", NULL); -#endif - { - int tolua_ret = (int) self->GetVersion(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetVersion'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetVersion of class cPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_SetVersion00 -static int tolua_AllToLua_cPlugin_SetVersion00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPlugin",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0); - int a_Version = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetVersion'", NULL); -#endif - { - self->SetVersion(a_Version); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetVersion'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetDirectory of class cPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetDirectory00 -static int tolua_AllToLua_cPlugin_GetDirectory00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDirectory'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetDirectory(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetDirectory'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetLocalDirectory of class cPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalDirectory00 -static int tolua_AllToLua_cPlugin_GetLocalDirectory00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLocalDirectory'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetLocalDirectory(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetLocalDirectory'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetLocalFolder of class cPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalFolder00 -static int tolua_AllToLua_cPlugin_GetLocalFolder00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLocalFolder'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetLocalFolder(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetLocalFolder'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: __cWebPlugin__ of class cPluginLua */ -#ifndef TOLUA_DISABLE_tolua_get_cPluginLua___cWebPlugin__ -static int tolua_get_cPluginLua___cWebPlugin__(lua_State* tolua_S) -{ - cPluginLua* self = (cPluginLua*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable '__cWebPlugin__'",NULL); -#endif -#ifdef __cplusplus - tolua_pushusertype(tolua_S,(void*)static_cast(self), "cWebPlugin"); -#else - tolua_pushusertype(tolua_S,(void*)((cWebPlugin*)self), "cWebPlugin"); -#endif - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetDescription of class cServer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetDescription00 -static int tolua_AllToLua_cServer_GetDescription00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDescription'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetDescription(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetDescription'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMaxPlayers of class cServer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetMaxPlayers00 -static int tolua_AllToLua_cServer_GetMaxPlayers00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxPlayers'", NULL); -#endif - { - int tolua_ret = (int) self->GetMaxPlayers(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMaxPlayers'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumPlayers of class cServer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetNumPlayers00 -static int tolua_AllToLua_cServer_GetNumPlayers00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlayers'", NULL); -#endif - { - int tolua_ret = (int) self->GetNumPlayers(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNumPlayers'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetMaxPlayers of class cServer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_SetMaxPlayers00 -static int tolua_AllToLua_cServer_SetMaxPlayers00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0); - int a_MaxPlayers = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxPlayers'", NULL); -#endif - { - self->SetMaxPlayers(a_MaxPlayers); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetMaxPlayers'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsHardcore of class cServer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_IsHardcore00 -static int tolua_AllToLua_cServer_IsHardcore00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsHardcore'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsHardcore(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsHardcore'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetServerID of class cServer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetServerID00 -static int tolua_AllToLua_cServer_GetServerID00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetServerID'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetServerID(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetServerID'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetTicksUntilWeatherChange of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00 -static int tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTicksUntilWeatherChange'", NULL); -#endif - { - int tolua_ret = (int) self->GetTicksUntilWeatherChange(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetTicksUntilWeatherChange'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWorldAge of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetWorldAge00 -static int tolua_AllToLua_cWorld_GetWorldAge00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorldAge'", NULL); -#endif - { - long long tolua_ret = ( long long) self->GetWorldAge(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWorldAge'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetTimeOfDay of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTimeOfDay00 -static int tolua_AllToLua_cWorld_GetTimeOfDay00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTimeOfDay'", NULL); -#endif - { - long long tolua_ret = ( long long) self->GetTimeOfDay(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetTimeOfDay'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetTicksUntilWeatherChange of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00 -static int tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_WeatherInterval = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetTicksUntilWeatherChange'", NULL); -#endif - { - self->SetTicksUntilWeatherChange(a_WeatherInterval); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetTicksUntilWeatherChange'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetTimeOfDay of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetTimeOfDay00 -static int tolua_AllToLua_cWorld_SetTimeOfDay00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - long long a_TimeOfDay = (( long long) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetTimeOfDay'", NULL); -#endif - { - self->SetTimeOfDay(a_TimeOfDay); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetTimeOfDay'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetGameMode of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetGameMode00 -static int tolua_AllToLua_cWorld_GetGameMode00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGameMode'", NULL); -#endif - { - eGameMode tolua_ret = (eGameMode) self->GetGameMode(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetGameMode'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsGameModeCreative of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeCreative00 -static int tolua_AllToLua_cWorld_IsGameModeCreative00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeCreative'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsGameModeCreative(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsGameModeSurvival of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeSurvival00 -static int tolua_AllToLua_cWorld_IsGameModeSurvival00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeSurvival'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsGameModeSurvival(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsGameModeAdventure of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeAdventure00 -static int tolua_AllToLua_cWorld_IsGameModeAdventure00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeAdventure'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsGameModeAdventure(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsPVPEnabled of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsPVPEnabled00 -static int tolua_AllToLua_cWorld_IsPVPEnabled00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPVPEnabled'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsPVPEnabled(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsPVPEnabled'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsDeepSnowEnabled of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsDeepSnowEnabled00 -static int tolua_AllToLua_cWorld_IsDeepSnowEnabled00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDeepSnowEnabled'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsDeepSnowEnabled(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsDeepSnowEnabled'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetDimension of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetDimension00 -static int tolua_AllToLua_cWorld_GetDimension00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDimension'", NULL); -#endif - { - eDimension tolua_ret = (eDimension) self->GetDimension(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetDimension'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHeight of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetHeight00 -static int tolua_AllToLua_cWorld_GetHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); -#endif - { - int tolua_ret = (int) self->GetHeight(a_BlockX,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: BroadcastChat of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastChat00 -static int tolua_AllToLua_cWorld_BroadcastChat00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isusertype(tolua_S,3,"const cClientHandle",1,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,3,NULL)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastChat'", NULL); -#endif - { - self->BroadcastChat(a_Message,a_Exclude); - tolua_pushcppstring(tolua_S,(const char*)a_Message); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'BroadcastChat'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: BroadcastSoundEffect of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastSoundEffect00 -static int tolua_AllToLua_cWorld_BroadcastSoundEffect00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isusertype(tolua_S,8,"const cClientHandle",1,&tolua_err) || - !tolua_isnoobj(tolua_S,9,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - const AString a_SoundName = ((const AString) tolua_tocppstring(tolua_S,2,0)); - int a_SrcX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_SrcY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_SrcZ = ((int) tolua_tonumber(tolua_S,5,0)); - float a_Volume = ((float) tolua_tonumber(tolua_S,6,0)); - float a_Pitch = ((float) tolua_tonumber(tolua_S,7,0)); - const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,8,NULL)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastSoundEffect'", NULL); -#endif - { - self->BroadcastSoundEffect(a_SoundName,a_SrcX,a_SrcY,a_SrcZ,a_Volume,a_Pitch,a_Exclude); - tolua_pushcppstring(tolua_S,(const char*)a_SoundName); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'BroadcastSoundEffect'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: BroadcastSoundParticleEffect of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00 -static int tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isusertype(tolua_S,7,"const cClientHandle",1,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_EffectID = ((int) tolua_tonumber(tolua_S,2,0)); - int a_SrcX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_SrcY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_SrcZ = ((int) tolua_tonumber(tolua_S,5,0)); - int a_Data = ((int) tolua_tonumber(tolua_S,6,0)); - const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,7,NULL)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastSoundParticleEffect'", NULL); -#endif - { - self->BroadcastSoundParticleEffect(a_EffectID,a_SrcX,a_SrcY,a_SrcZ,a_Data,a_Exclude); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'BroadcastSoundParticleEffect'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: UnloadUnusedChunks of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_UnloadUnusedChunks00 -static int tolua_AllToLua_cWorld_UnloadUnusedChunks00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'UnloadUnusedChunks'", NULL); -#endif - { - self->UnloadUnusedChunks(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'UnloadUnusedChunks'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RegenerateChunk of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_RegenerateChunk00 -static int tolua_AllToLua_cWorld_RegenerateChunk00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RegenerateChunk'", NULL); -#endif - { - self->RegenerateChunk(a_ChunkX,a_ChunkZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RegenerateChunk'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GenerateChunk of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GenerateChunk00 -static int tolua_AllToLua_cWorld_GenerateChunk00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GenerateChunk'", NULL); -#endif - { - self->GenerateChunk(a_ChunkX,a_ChunkZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GenerateChunk'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlock of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlock00 -static int tolua_AllToLua_cWorld_SetBlock00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlock'", NULL); -#endif - { - self->SetBlock(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlock'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FastSetBlock of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_FastSetBlock00 -static int tolua_AllToLua_cWorld_FastSetBlock00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FastSetBlock'", NULL); -#endif - { - self->FastSetBlock(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FastSetBlock'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: QueueSetBlock of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSetBlock00 -static int tolua_AllToLua_cWorld_QueueSetBlock00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BLockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); - int a_TickDelay = ((int) tolua_tonumber(tolua_S,7,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueSetBlock'", NULL); -#endif - { - self->QueueSetBlock(a_BlockX,a_BLockY,a_BlockZ,a_BlockType,a_BlockMeta,a_TickDelay); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'QueueSetBlock'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlock of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlock00 -static int tolua_AllToLua_cWorld_GetBlock00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlock'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlock(a_BlockX,a_BlockY,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlock'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockMeta of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockMeta00 -static int tolua_AllToLua_cWorld_GetBlockMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_BlockX,a_BlockY,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockMeta of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlockMeta00 -static int tolua_AllToLua_cWorld_SetBlockMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_MetaData = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); -#endif - { - self->SetBlockMeta(a_BlockX,a_BlockY,a_BlockZ,a_MetaData); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockSkyLight of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockSkyLight00 -static int tolua_AllToLua_cWorld_GetBlockSkyLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockSkyLight'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockSkyLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockBlockLight of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockBlockLight00 -static int tolua_AllToLua_cWorld_GetBlockBlockLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockBlockLight'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockBlockLight(a_BlockX,a_BlockY,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockBlockLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FastSetBlock of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_FastSetBlock01 -static int tolua_AllToLua_cWorld_FastSetBlock01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FastSetBlock'", NULL); -#endif - { - self->FastSetBlock(*a_Pos,a_BlockType,a_BlockMeta); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cWorld_FastSetBlock00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlock of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlock01 -static int tolua_AllToLua_cWorld_GetBlock01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlock'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlock(*a_Pos); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cWorld_GetBlock00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockMeta of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockMeta01 -static int tolua_AllToLua_cWorld_GetBlockMeta01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(*a_Pos); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cWorld_GetBlockMeta00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockMeta of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlockMeta01 -static int tolua_AllToLua_cWorld_SetBlockMeta01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); - unsigned char a_MetaData = (( unsigned char) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); -#endif - { - self->SetBlockMeta(*a_Pos,a_MetaData); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cWorld_SetBlockMeta00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SpawnItemPickups of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnItemPickups00 -static int tolua_AllToLua_cWorld_SpawnItemPickups00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItems",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,1,&tolua_err) || - !tolua_isboolean(tolua_S,7,1,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - const cItems* a_Pickups = ((const cItems*) tolua_tousertype(tolua_S,2,0)); - double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0)); - double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0)); - double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0)); - double a_FlyAwaySpeed = ((double) tolua_tonumber(tolua_S,6,1.0)); - bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,7,false)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL); -#endif - { - self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_FlyAwaySpeed,IsPlayerCreated); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SpawnItemPickups'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SpawnItemPickups of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnItemPickups01 -static int tolua_AllToLua_cWorld_SpawnItemPickups01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItems",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isboolean(tolua_S,9,1,&tolua_err) || - !tolua_isnoobj(tolua_S,10,&tolua_err) - ) - goto tolua_lerror; - else - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - const cItems* a_Pickups = ((const cItems*) tolua_tousertype(tolua_S,2,0)); - double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0)); - double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0)); - double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0)); - double a_SpeedX = ((double) tolua_tonumber(tolua_S,6,0)); - double a_SpeedY = ((double) tolua_tonumber(tolua_S,7,0)); - double a_SpeedZ = ((double) tolua_tonumber(tolua_S,8,0)); - bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,9,false)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL); -#endif - { - self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_SpeedX,a_SpeedY,a_SpeedZ,IsPlayerCreated); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cWorld_SpawnItemPickups00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SpawnPrimedTNT of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnPrimedTNT00 -static int tolua_AllToLua_cWorld_SpawnPrimedTNT00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,1,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - double a_X = ((double) tolua_tonumber(tolua_S,2,0)); - double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); - double a_Z = ((double) tolua_tonumber(tolua_S,4,0)); - double a_FuseTimeInSec = ((double) tolua_tonumber(tolua_S,5,0)); - double a_InitialVelocityCoeff = ((double) tolua_tonumber(tolua_S,6,1)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnPrimedTNT'", NULL); -#endif - { - self->SpawnPrimedTNT(a_X,a_Y,a_Z,a_FuseTimeInSec,a_InitialVelocityCoeff); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SpawnPrimedTNT'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DigBlock of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_DigBlock00 -static int tolua_AllToLua_cWorld_DigBlock00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); - int a_Z = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DigBlock'", NULL); -#endif - { - bool tolua_ret = (bool) self->DigBlock(a_X,a_Y,a_Z); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DigBlock'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SendBlockTo of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SendBlockTo00 -static int tolua_AllToLua_cWorld_SendBlockTo00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isusertype(tolua_S,5,"cPlayer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); - int a_Z = ((int) tolua_tonumber(tolua_S,4,0)); - cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendBlockTo'", NULL); -#endif - { - self->SendBlockTo(a_X,a_Y,a_Z,a_Player); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SendBlockTo'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSpawnX of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnX00 -static int tolua_AllToLua_cWorld_GetSpawnX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpawnX'", NULL); -#endif - { - double tolua_ret = (double) self->GetSpawnX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSpawnX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSpawnY of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnY00 -static int tolua_AllToLua_cWorld_GetSpawnY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpawnY'", NULL); -#endif - { - double tolua_ret = (double) self->GetSpawnY(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSpawnY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSpawnZ of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnZ00 -static int tolua_AllToLua_cWorld_GetSpawnZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpawnZ'", NULL); -#endif - { - double tolua_ret = (double) self->GetSpawnZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSpawnZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: WakeUpSimulators of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_WakeUpSimulators00 -static int tolua_AllToLua_cWorld_WakeUpSimulators00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WakeUpSimulators'", NULL); -#endif - { - self->WakeUpSimulators(a_BlockX,a_BlockY,a_BlockZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'WakeUpSimulators'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: WakeUpSimulatorsInArea of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00 -static int tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_MinBlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,6,0)); - int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,7,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WakeUpSimulatorsInArea'", NULL); -#endif - { - self->WakeUpSimulatorsInArea(a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'WakeUpSimulatorsInArea'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DoExplosionAt of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_DoExplosionAt00 -static int tolua_AllToLua_cWorld_DoExplosionAt00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isboolean(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isuserdata(tolua_S,8,0,&tolua_err) || - !tolua_isnoobj(tolua_S,9,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - double a_ExplosionSize = ((double) tolua_tonumber(tolua_S,2,0)); - double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0)); - double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0)); - double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0)); - bool a_CanCauseFire = ((bool) tolua_toboolean(tolua_S,6,0)); - eExplosionSource a_Source = ((eExplosionSource) (int) tolua_tonumber(tolua_S,7,0)); - void* a_SourceData = ((void*) tolua_touserdata(tolua_S,8,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoExplosionAt'", NULL); -#endif - { - self->DoExplosionAt(a_ExplosionSize,a_BlockX,a_BlockY,a_BlockZ,a_CanCauseFire,a_Source,a_SourceData); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DoExplosionAt'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: UseBlockEntity of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_UseBlockEntity00 -static int tolua_AllToLua_cWorld_UseBlockEntity00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); - int a_BlockX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'UseBlockEntity'", NULL); -#endif - { - self->UseBlockEntity(a_Player,a_BlockX,a_BlockY,a_BlockZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'UseBlockEntity'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GrowTree of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTree00 -static int tolua_AllToLua_cWorld_GrowTree00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTree'", NULL); -#endif - { - self->GrowTree(a_BlockX,a_BlockY,a_BlockZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GrowTree'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GrowTreeFromSapling of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeFromSapling00 -static int tolua_AllToLua_cWorld_GrowTreeFromSapling00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_SaplingMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeFromSapling'", NULL); -#endif - { - self->GrowTreeFromSapling(a_BlockX,a_BlockY,a_BlockZ,a_SaplingMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GrowTreeFromSapling'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GrowTreeByBiome of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeByBiome00 -static int tolua_AllToLua_cWorld_GrowTreeByBiome00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeByBiome'", NULL); -#endif - { - self->GrowTreeByBiome(a_BlockX,a_BlockY,a_BlockZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GrowTreeByBiome'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GrowRipePlant of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowRipePlant00 -static int tolua_AllToLua_cWorld_GrowRipePlant00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isboolean(tolua_S,5,1,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - bool a_IsByBonemeal = ((bool) tolua_toboolean(tolua_S,5,false)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowRipePlant'", NULL); -#endif - { - bool tolua_ret = (bool) self->GrowRipePlant(a_BlockX,a_BlockY,a_BlockZ,a_IsByBonemeal); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GrowRipePlant'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GrowCactus of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowCactus00 -static int tolua_AllToLua_cWorld_GrowCactus00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - int a_NumBlocksToGrow = ((int) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowCactus'", NULL); -#endif - { - self->GrowCactus(a_BlockX,a_BlockY,a_BlockZ,a_NumBlocksToGrow); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GrowCactus'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GrowMelonPumpkin of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowMelonPumpkin00 -static int tolua_AllToLua_cWorld_GrowMelonPumpkin00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowMelonPumpkin'", NULL); -#endif - { - self->GrowMelonPumpkin(a_BlockX,a_BlockY,a_BlockZ,a_BlockType); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GrowMelonPumpkin'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GrowSugarcane of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowSugarcane00 -static int tolua_AllToLua_cWorld_GrowSugarcane00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - int a_NumBlocksToGrow = ((int) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowSugarcane'", NULL); -#endif - { - self->GrowSugarcane(a_BlockX,a_BlockY,a_BlockZ,a_NumBlocksToGrow); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GrowSugarcane'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBiomeAt of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBiomeAt00 -static int tolua_AllToLua_cWorld_GetBiomeAt00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBiomeAt'", NULL); -#endif - { - int tolua_ret = (int) self->GetBiomeAt(a_BlockX,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBiomeAt'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetName of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetName00 -static int tolua_AllToLua_cWorld_GetName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetName(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetIniFileName of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetIniFileName00 -static int tolua_AllToLua_cWorld_GetIniFileName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIniFileName'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetIniFileName(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetIniFileName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: QueueSaveAllChunks of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSaveAllChunks00 -static int tolua_AllToLua_cWorld_QueueSaveAllChunks00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueSaveAllChunks'", NULL); -#endif - { - self->QueueSaveAllChunks(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'QueueSaveAllChunks'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumChunks of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetNumChunks00 -static int tolua_AllToLua_cWorld_GetNumChunks00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumChunks'", NULL); -#endif - { - int tolua_ret = (int) self->GetNumChunks(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNumChunks'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetGeneratorQueueLength of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetGeneratorQueueLength00 -static int tolua_AllToLua_cWorld_GetGeneratorQueueLength00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGeneratorQueueLength'", NULL); -#endif - { - int tolua_ret = (int) self->GetGeneratorQueueLength(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetGeneratorQueueLength'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetLightingQueueLength of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetLightingQueueLength00 -static int tolua_AllToLua_cWorld_GetLightingQueueLength00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLightingQueueLength'", NULL); -#endif - { - int tolua_ret = (int) self->GetLightingQueueLength(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetLightingQueueLength'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetStorageLoadQueueLength of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetStorageLoadQueueLength00 -static int tolua_AllToLua_cWorld_GetStorageLoadQueueLength00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStorageLoadQueueLength'", NULL); -#endif - { - int tolua_ret = (int) self->GetStorageLoadQueueLength(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetStorageLoadQueueLength'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetStorageSaveQueueLength of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetStorageSaveQueueLength00 -static int tolua_AllToLua_cWorld_GetStorageSaveQueueLength00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStorageSaveQueueLength'", NULL); -#endif - { - int tolua_ret = (int) self->GetStorageSaveQueueLength(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetStorageSaveQueueLength'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: QueueBlockForTick of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueBlockForTick00 -static int tolua_AllToLua_cWorld_QueueBlockForTick00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - int a_TicksToWait = ((int) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueBlockForTick'", NULL); -#endif - { - self->QueueBlockForTick(a_BlockX,a_BlockY,a_BlockZ,a_TicksToWait); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'QueueBlockForTick'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CastThunderbolt of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_CastThunderbolt00 -static int tolua_AllToLua_cWorld_CastThunderbolt00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CastThunderbolt'", NULL); -#endif - { - self->CastThunderbolt(a_BlockX,a_BlockY,a_BlockZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CastThunderbolt'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetWeather of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetWeather00 -static int tolua_AllToLua_cWorld_SetWeather00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - eWeather a_NewWeather = ((eWeather) (int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWeather'", NULL); -#endif - { - self->SetWeather(a_NewWeather); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetWeather'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ChangeWeather of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_ChangeWeather00 -static int tolua_AllToLua_cWorld_ChangeWeather00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeWeather'", NULL); -#endif - { - self->ChangeWeather(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ChangeWeather'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWeather of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetWeather00 -static int tolua_AllToLua_cWorld_GetWeather00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWeather'", NULL); -#endif - { - eWeather tolua_ret = (eWeather) self->GetWeather(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWeather'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsWeatherSunny of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherSunny00 -static int tolua_AllToLua_cWorld_IsWeatherSunny00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherSunny'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsWeatherSunny(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsWeatherSunny'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsWeatherRain of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherRain00 -static int tolua_AllToLua_cWorld_IsWeatherRain00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherRain'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsWeatherRain(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsWeatherRain'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsWeatherStorm of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherStorm00 -static int tolua_AllToLua_cWorld_IsWeatherStorm00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherStorm'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsWeatherStorm(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsWeatherStorm'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsWeatherWet of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherWet00 -static int tolua_AllToLua_cWorld_IsWeatherWet00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherWet'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsWeatherWet(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsWeatherWet'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetNextBlockTick of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetNextBlockTick00 -static int tolua_AllToLua_cWorld_SetNextBlockTick00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetNextBlockTick'", NULL); -#endif - { - self->SetNextBlockTick(a_BlockX,a_BlockY,a_BlockZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetNextBlockTick'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMaxSugarcaneHeight of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00 -static int tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxSugarcaneHeight'", NULL); -#endif - { - int tolua_ret = (int) self->GetMaxSugarcaneHeight(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMaxSugarcaneHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMaxCactusHeight of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetMaxCactusHeight00 -static int tolua_AllToLua_cWorld_GetMaxCactusHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxCactusHeight'", NULL); -#endif - { - int tolua_ret = (int) self->GetMaxCactusHeight(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMaxCactusHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsBlockDirectlyWatered of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsBlockDirectlyWatered00 -static int tolua_AllToLua_cWorld_IsBlockDirectlyWatered00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsBlockDirectlyWatered'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsBlockDirectlyWatered(a_BlockX,a_BlockY,a_BlockZ); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsBlockDirectlyWatered'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SpawnMob of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnMob00 -static int tolua_AllToLua_cWorld_SpawnMob00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); - cMonster::eType a_MonsterType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnMob'", NULL); -#endif - { - int tolua_ret = (int) self->SpawnMob(a_PosX,a_PosY,a_PosZ,a_MonsterType); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SpawnMob'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CreateProjectile of class cWorld */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_CreateProjectile00 -static int tolua_AllToLua_cWorld_CreateProjectile00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isusertype(tolua_S,6,"cEntity",0,&tolua_err) || - !tolua_isusertype(tolua_S,7,"const Vector3d",1,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); - double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); - cProjectileEntity::eKind a_Kind = ((cProjectileEntity::eKind) (int) tolua_tonumber(tolua_S,5,0)); - cEntity* a_Creator = ((cEntity*) tolua_tousertype(tolua_S,6,0)); - const Vector3d* a_Speed = ((const Vector3d*) tolua_tousertype(tolua_S,7,NULL)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CreateProjectile'", NULL); -#endif - { - int tolua_ret = (int) self->CreateProjectile(a_PosX,a_PosY,a_PosZ,a_Kind,a_Creator,a_Speed); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CreateProjectile'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Clear of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_Clear00 -static int tolua_AllToLua_cInventory_Clear00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); -#endif - { - self->Clear(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HowManyCanFit of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyCanFit00 -static int tolua_AllToLua_cInventory_HowManyCanFit00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isboolean(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); - bool a_ConsiderEmptySlots = ((bool) tolua_toboolean(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL); -#endif - { - int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_ConsiderEmptySlots); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HowManyCanFit'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HowManyCanFit of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyCanFit01 -static int tolua_AllToLua_cInventory_HowManyCanFit01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isboolean(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); - int a_BeginSlotNum = ((int) tolua_tonumber(tolua_S,3,0)); - int a_EndSlotNum = ((int) tolua_tonumber(tolua_S,4,0)); - bool a_ConsiderEmptySlots = ((bool) tolua_toboolean(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL); -#endif - { - int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_BeginSlotNum,a_EndSlotNum,a_ConsiderEmptySlots); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cInventory_HowManyCanFit00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddItem of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_AddItem00 -static int tolua_AllToLua_cInventory_AddItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isboolean(tolua_S,3,1,&tolua_err) || - !tolua_isboolean(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); - bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); - bool a_tryToFillEquippedFirst = ((bool) tolua_toboolean(tolua_S,4,false)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItem'", NULL); -#endif - { - int tolua_ret = (int) self->AddItem(*a_ItemStack,a_AllowNewStacks,a_tryToFillEquippedFirst); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddItems of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_AddItems00 -static int tolua_AllToLua_cInventory_AddItems00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || - !tolua_isboolean(tolua_S,3,0,&tolua_err) || - !tolua_isboolean(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - cItems* a_ItemStackList = ((cItems*) tolua_tousertype(tolua_S,2,0)); - bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,0)); - bool a_tryToFillEquippedFirst = ((bool) tolua_toboolean(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItems'", NULL); -#endif - { - int tolua_ret = (int) self->AddItems(*a_ItemStackList,a_AllowNewStacks,a_tryToFillEquippedFirst); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddItems'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RemoveOneEquippedItem of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_RemoveOneEquippedItem00 -static int tolua_AllToLua_cInventory_RemoveOneEquippedItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneEquippedItem'", NULL); -#endif - { - bool tolua_ret = (bool) self->RemoveOneEquippedItem(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RemoveOneEquippedItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HowManyItems of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyItems00 -static int tolua_AllToLua_cInventory_HowManyItems00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyItems'", NULL); -#endif - { - int tolua_ret = (int) self->HowManyItems(*a_Item); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HowManyItems'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HasItems of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HasItems00 -static int tolua_AllToLua_cInventory_HasItems00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasItems'", NULL); -#endif - { - bool tolua_ret = (bool) self->HasItems(*a_ItemStack); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HasItems'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetArmorGrid of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetArmorGrid00 -static int tolua_AllToLua_cInventory_GetArmorGrid00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorGrid'", NULL); -#endif - { - cItemGrid& tolua_ret = (cItemGrid&) self->GetArmorGrid(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetArmorGrid'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetInventoryGrid of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetInventoryGrid00 -static int tolua_AllToLua_cInventory_GetInventoryGrid00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventoryGrid'", NULL); -#endif - { - cItemGrid& tolua_ret = (cItemGrid&) self->GetInventoryGrid(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetInventoryGrid'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHotbarGrid of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetHotbarGrid00 -static int tolua_AllToLua_cInventory_GetHotbarGrid00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHotbarGrid'", NULL); -#endif - { - cItemGrid& tolua_ret = (cItemGrid&) self->GetHotbarGrid(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHotbarGrid'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetOwner of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetOwner00 -static int tolua_AllToLua_cInventory_GetOwner00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOwner'", NULL); -#endif - { - cPlayer& tolua_ret = (cPlayer&) self->GetOwner(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cPlayer"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetOwner'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CopyToItems of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_CopyToItems00 -static int tolua_AllToLua_cInventory_CopyToItems00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - cItems* a_Items = ((cItems*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyToItems'", NULL); -#endif - { - self->CopyToItems(*a_Items); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CopyToItems'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSlot of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetSlot00 -static int tolua_AllToLua_cInventory_GetSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetArmorSlot of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetArmorSlot00 -static int tolua_AllToLua_cInventory_GetArmorSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); - int a_ArmorSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetArmorSlot(a_ArmorSlotNum); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetArmorSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetInventorySlot of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetInventorySlot00 -static int tolua_AllToLua_cInventory_GetInventorySlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); - int a_InventorySlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventorySlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetInventorySlot(a_InventorySlotNum); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetInventorySlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHotbarSlot of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetHotbarSlot00 -static int tolua_AllToLua_cInventory_GetHotbarSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); - int a_HotBarSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHotbarSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetHotbarSlot(a_HotBarSlotNum); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHotbarSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedItem of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedItem00 -static int tolua_AllToLua_cInventory_GetEquippedItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedItem'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetEquippedItem(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSlot of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetSlot00 -static int tolua_AllToLua_cInventory_SetSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); -#endif - { - self->SetSlot(a_SlotNum,*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetArmorSlot of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetArmorSlot00 -static int tolua_AllToLua_cInventory_SetArmorSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - int a_ArmorSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetArmorSlot'", NULL); -#endif - { - self->SetArmorSlot(a_ArmorSlotNum,*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetArmorSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetInventorySlot of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetInventorySlot00 -static int tolua_AllToLua_cInventory_SetInventorySlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - int a_InventorySlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetInventorySlot'", NULL); -#endif - { - self->SetInventorySlot(a_InventorySlotNum,*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetInventorySlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetHotbarSlot of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetHotbarSlot00 -static int tolua_AllToLua_cInventory_SetHotbarSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - int a_HotBarSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHotbarSlot'", NULL); -#endif - { - self->SetHotbarSlot(a_HotBarSlotNum,*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetHotbarSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetEquippedSlotNum of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetEquippedSlotNum00 -static int tolua_AllToLua_cInventory_SetEquippedSlotNum00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetEquippedSlotNum'", NULL); -#endif - { - self->SetEquippedSlotNum(a_SlotNum); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetEquippedSlotNum'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedSlotNum of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedSlotNum00 -static int tolua_AllToLua_cInventory_GetEquippedSlotNum00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedSlotNum'", NULL); -#endif - { - int tolua_ret = (int) self->GetEquippedSlotNum(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedSlotNum'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ChangeSlotCount of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_ChangeSlotCount00 -static int tolua_AllToLua_cInventory_ChangeSlotCount00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - int a_AddToCount = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL); -#endif - { - int tolua_ret = (int) self->ChangeSlotCount(a_SlotNum,a_AddToCount); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ChangeSlotCount'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DamageItem of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_DamageItem00 -static int tolua_AllToLua_cInventory_DamageItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - short a_Amount = ((short) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); -#endif - { - bool tolua_ret = (bool) self->DamageItem(a_SlotNum,a_Amount); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DamageEquippedItem of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_DamageEquippedItem00 -static int tolua_AllToLua_cInventory_DamageEquippedItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,1,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); - short a_Amount = ((short) tolua_tonumber(tolua_S,2,1)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageEquippedItem'", NULL); -#endif - { - bool tolua_ret = (bool) self->DamageEquippedItem(a_Amount); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DamageEquippedItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedHelmet of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedHelmet00 -static int tolua_AllToLua_cInventory_GetEquippedHelmet00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedHelmet'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetEquippedHelmet(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedHelmet'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedChestplate of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedChestplate00 -static int tolua_AllToLua_cInventory_GetEquippedChestplate00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedChestplate'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetEquippedChestplate(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedChestplate'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedLeggings of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedLeggings00 -static int tolua_AllToLua_cInventory_GetEquippedLeggings00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedLeggings'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetEquippedLeggings(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedLeggings'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetEquippedBoots of class cInventory */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedBoots00 -static int tolua_AllToLua_cInventory_GetEquippedBoots00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedBoots'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetEquippedBoots(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetEquippedBoots'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new00 -static int tolua_AllToLua_cEnchantments_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new00_local -static int tolua_AllToLua_cEnchantments_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new01 -static int tolua_AllToLua_cEnchantments_new01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)(a_StringSpec)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); - tolua_pushcppstring(tolua_S,(const char*)a_StringSpec); - } - } - return 2; -tolua_lerror: - return tolua_AllToLua_cEnchantments_new00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new01_local -static int tolua_AllToLua_cEnchantments_new01_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)(a_StringSpec)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - tolua_pushcppstring(tolua_S,(const char*)a_StringSpec); - } - } - return 2; -tolua_lerror: - return tolua_AllToLua_cEnchantments_new00_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddFromString of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_AddFromString00 -static int tolua_AllToLua_cEnchantments_AddFromString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); - const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddFromString'", NULL); -#endif - { - self->AddFromString(a_StringSpec); - tolua_pushcppstring(tolua_S,(const char*)a_StringSpec); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddFromString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ToString of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_ToString00 -static int tolua_AllToLua_cEnchantments_ToString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ToString'", NULL); -#endif - { - AString tolua_ret = (AString) self->ToString(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ToString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetLevel of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_GetLevel00 -static int tolua_AllToLua_cEnchantments_GetLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); - int a_EnchantmentID = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLevel'", NULL); -#endif - { - int tolua_ret = (int) self->GetLevel(a_EnchantmentID); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetLevel of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_SetLevel00 -static int tolua_AllToLua_cEnchantments_SetLevel00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); - int a_EnchantmentID = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Level = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLevel'", NULL); -#endif - { - self->SetLevel(a_EnchantmentID,a_Level); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetLevel'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Clear of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_Clear00 -static int tolua_AllToLua_cEnchantments_Clear00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); -#endif - { - self->Clear(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsEmpty of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_IsEmpty00 -static int tolua_AllToLua_cEnchantments_IsEmpty00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEmpty'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsEmpty(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsEmpty'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: StringToEnchantmentID of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_StringToEnchantmentID00 -static int tolua_AllToLua_cEnchantments_StringToEnchantmentID00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_EnchantmentName = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - int tolua_ret = (int) cEnchantments::StringToEnchantmentID(a_EnchantmentName); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_EnchantmentName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StringToEnchantmentID'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator== of class cEnchantments */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments__eq00 -static int tolua_AllToLua_cEnchantments__eq00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEnchantments",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); - const cEnchantments* a_Other = ((const cEnchantments*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator=='", NULL); -#endif - { - bool tolua_ret = (bool) self->operator==(*a_Other); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function '.eq'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new00 -static int tolua_AllToLua_cItem_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cItem* tolua_ret = (cItem*) Mtolua_new((cItem)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new00_local -static int tolua_AllToLua_cItem_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cItem* tolua_ret = (cItem*) Mtolua_new((cItem)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new01 -static int tolua_AllToLua_cItem_new01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,1,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); - char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,1)); - short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); - { - cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cItem_new00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new01_local -static int tolua_AllToLua_cItem_new01_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,1,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); - char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,1)); - short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); - { - cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cItem_new00_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new02 -static int tolua_AllToLua_cItem_new02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_iscppstring(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); - char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0)); - short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); - const AString a_Enchantments = ((const AString) tolua_tocppstring(tolua_S,5,0)); - { - cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage,a_Enchantments)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); - tolua_pushcppstring(tolua_S,(const char*)a_Enchantments); - } - } - return 2; -tolua_lerror: - return tolua_AllToLua_cItem_new01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new02_local -static int tolua_AllToLua_cItem_new02_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_iscppstring(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else - { - short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); - char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0)); - short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); - const AString a_Enchantments = ((const AString) tolua_tocppstring(tolua_S,5,0)); - { - cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage,a_Enchantments)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - tolua_pushcppstring(tolua_S,(const char*)a_Enchantments); - } - } - return 2; -tolua_lerror: - return tolua_AllToLua_cItem_new01_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new03 -static int tolua_AllToLua_cItem_new03(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cItem* a_CopyFrom = ((const cItem*) tolua_tousertype(tolua_S,2,0)); - { - cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(*a_CopyFrom)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cItem_new02(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new03_local -static int tolua_AllToLua_cItem_new03_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cItem* a_CopyFrom = ((const cItem*) tolua_tousertype(tolua_S,2,0)); - { - cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(*a_CopyFrom)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cItem_new02_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Empty of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_Empty00 -static int tolua_AllToLua_cItem_Empty00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Empty'", NULL); -#endif - { - self->Empty(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Empty'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Clear of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_Clear00 -static int tolua_AllToLua_cItem_Clear00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); -#endif - { - self->Clear(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsEmpty of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsEmpty00 -static int tolua_AllToLua_cItem_IsEmpty00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEmpty'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsEmpty(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsEmpty'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsEqual of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsEqual00 -static int tolua_AllToLua_cItem_IsEqual00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEqual'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsEqual(*a_Item); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsEqual'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSameType of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsSameType00 -static int tolua_AllToLua_cItem_IsSameType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSameType'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSameType(*a_Item); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSameType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CopyOne of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_CopyOne00 -static int tolua_AllToLua_cItem_CopyOne00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyOne'", NULL); -#endif - { - cItem tolua_ret = (cItem) self->CopyOne(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CopyOne'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddCount of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_AddCount00 -static int tolua_AllToLua_cItem_AddCount00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); - char a_AmountToAdd = ((char) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddCount'", NULL); -#endif - { - cItem& tolua_ret = (cItem&) self->AddCount(a_AmountToAdd); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddCount'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMaxDamage of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_GetMaxDamage00 -static int tolua_AllToLua_cItem_GetMaxDamage00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxDamage'", NULL); -#endif - { - short tolua_ret = (short) self->GetMaxDamage(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMaxDamage'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DamageItem of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_DamageItem00 -static int tolua_AllToLua_cItem_DamageItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,1,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); - short a_Amount = ((short) tolua_tonumber(tolua_S,2,1)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); -#endif - { - bool tolua_ret = (bool) self->DamageItem(a_Amount); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsDamageable of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsDamageable00 -static int tolua_AllToLua_cItem_IsDamageable00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDamageable'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsDamageable(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsDamageable'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsStackableWith of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsStackableWith00 -static int tolua_AllToLua_cItem_IsStackableWith00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); - const cItem* a_OtherStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsStackableWith'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsStackableWith(*a_OtherStack); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsStackableWith'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsFullStack of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsFullStack00 -static int tolua_AllToLua_cItem_IsFullStack00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFullStack'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsFullStack(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsFullStack'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMaxStackSize of class cItem */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_GetMaxStackSize00 -static int tolua_AllToLua_cItem_GetMaxStackSize00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxStackSize'", NULL); -#endif - { - char tolua_ret = (char) self->GetMaxStackSize(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMaxStackSize'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: m_ItemType of class cItem */ -#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemType -static int tolua_get_cItem_m_ItemType(lua_State* tolua_S) -{ - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemType'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemType); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: m_ItemType of class cItem */ -#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemType -static int tolua_set_cItem_m_ItemType(lua_State* tolua_S) -{ - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemType'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->m_ItemType = ((short) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: m_ItemCount of class cItem */ -#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemCount -static int tolua_get_cItem_m_ItemCount(lua_State* tolua_S) -{ - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemCount'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemCount); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: m_ItemCount of class cItem */ -#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemCount -static int tolua_set_cItem_m_ItemCount(lua_State* tolua_S) -{ - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemCount'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->m_ItemCount = ((char) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: m_ItemDamage of class cItem */ -#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemDamage -static int tolua_get_cItem_m_ItemDamage(lua_State* tolua_S) -{ - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemDamage'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemDamage); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: m_ItemDamage of class cItem */ -#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemDamage -static int tolua_set_cItem_m_ItemDamage(lua_State* tolua_S) -{ - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemDamage'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->m_ItemDamage = ((short) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: m_Enchantments of class cItem */ -#ifndef TOLUA_DISABLE_tolua_get_cItem_m_Enchantments -static int tolua_get_cItem_m_Enchantments(lua_State* tolua_S) -{ - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_Enchantments'",NULL); -#endif - tolua_pushusertype(tolua_S,(void*)&self->m_Enchantments,"cEnchantments"); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: m_Enchantments of class cItem */ -#ifndef TOLUA_DISABLE_tolua_set_cItem_m_Enchantments -static int tolua_set_cItem_m_Enchantments(lua_State* tolua_S) -{ - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_Enchantments'",NULL); - if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEnchantments",0,&tolua_err))) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->m_Enchantments = *((cEnchantments*) tolua_tousertype(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_new00 -static int tolua_AllToLua_cItems_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItems",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cItems* tolua_ret = (cItems*) Mtolua_new((cItems)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItems"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_new00_local -static int tolua_AllToLua_cItems_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cItems",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cItems* tolua_ret = (cItems*) Mtolua_new((cItems)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItems"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Get of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Get00 -static int tolua_AllToLua_cItems_Get00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); - int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Get'", NULL); -#endif - { - cItem* tolua_ret = (cItem*) self->Get(a_Idx); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Set of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Set00 -static int tolua_AllToLua_cItems_Set00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); - int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); -#endif - { - self->Set(a_Idx,*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Add of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Add00 -static int tolua_AllToLua_cItems_Add00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Add'", NULL); -#endif - { - self->Add(*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Add'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Delete of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Delete00 -static int tolua_AllToLua_cItems_Delete00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); - int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Delete'", NULL); -#endif - { - self->Delete(a_Idx); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Clear of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Clear00 -static int tolua_AllToLua_cItems_Clear00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); -#endif - { - self->Clear(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Size of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Size00 -static int tolua_AllToLua_cItems_Size00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Size'", NULL); -#endif - { - int tolua_ret = (int) self->Size(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Size'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Set of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Set01 -static int tolua_AllToLua_cItems_Set01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else - { - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); - int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); - ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,3,0)); - char a_ItemCount = ((char) tolua_tonumber(tolua_S,4,0)); - short a_ItemDamage = ((short) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); -#endif - { - self->Set(a_Idx,a_ItemType,a_ItemCount,a_ItemDamage); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cItems_Set00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Add of class cItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Add01 -static int tolua_AllToLua_cItems_Add01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); - ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,2,0)); - char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0)); - short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Add'", NULL); -#endif - { - self->Add(a_ItemType,a_ItemCount,a_ItemDamage); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cItems_Add00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWidth of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetWidth00 -static int tolua_AllToLua_cItemGrid_GetWidth00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL); -#endif - { - int tolua_ret = (int) self->GetWidth(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHeight of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetHeight00 -static int tolua_AllToLua_cItemGrid_GetHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); -#endif - { - int tolua_ret = (int) self->GetHeight(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNumSlots of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNumSlots00 -static int tolua_AllToLua_cItemGrid_GetNumSlots00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumSlots'", NULL); -#endif - { - int tolua_ret = (int) self->GetNumSlots(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNumSlots'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSlotNum of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlotNum00 -static int tolua_AllToLua_cItemGrid_GetSlotNum00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlotNum'", NULL); -#endif - { - int tolua_ret = (int) self->GetSlotNum(a_X,a_Y); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSlotNum'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlot00 -static int tolua_AllToLua_cItemGrid_GetSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetSlot(a_X,a_Y); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlot01 -static int tolua_AllToLua_cItemGrid_GetSlot01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cItemGrid_GetSlot00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot00 -static int tolua_AllToLua_cItemGrid_SetSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); -#endif - { - self->SetSlot(a_X,a_Y,*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot01 -static int tolua_AllToLua_cItemGrid_SetSlot01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); - short a_ItemType = ((short) tolua_tonumber(tolua_S,4,0)); - char a_ItemCount = ((char) tolua_tonumber(tolua_S,5,0)); - short a_ItemDamage = ((short) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); -#endif - { - self->SetSlot(a_X,a_Y,a_ItemType,a_ItemCount,a_ItemDamage); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cItemGrid_SetSlot00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot02 -static int tolua_AllToLua_cItemGrid_SetSlot02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); -#endif - { - self->SetSlot(a_SlotNum,*a_Item); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cItemGrid_SetSlot01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot03 -static int tolua_AllToLua_cItemGrid_SetSlot03(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - short a_ItemType = ((short) tolua_tonumber(tolua_S,3,0)); - char a_ItemCount = ((char) tolua_tonumber(tolua_S,4,0)); - short a_ItemDamage = ((short) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); -#endif - { - self->SetSlot(a_SlotNum,a_ItemType,a_ItemCount,a_ItemDamage); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cItemGrid_SetSlot02(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: EmptySlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_EmptySlot00 -static int tolua_AllToLua_cItemGrid_EmptySlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EmptySlot'", NULL); -#endif - { - self->EmptySlot(a_X,a_Y); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'EmptySlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: EmptySlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_EmptySlot01 -static int tolua_AllToLua_cItemGrid_EmptySlot01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EmptySlot'", NULL); -#endif - { - self->EmptySlot(a_SlotNum); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cItemGrid_EmptySlot00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSlotEmpty of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_IsSlotEmpty00 -static int tolua_AllToLua_cItemGrid_IsSlotEmpty00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotEmpty'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSlotEmpty(a_SlotNum); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSlotEmpty'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSlotEmpty of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_IsSlotEmpty01 -static int tolua_AllToLua_cItemGrid_IsSlotEmpty01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotEmpty'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSlotEmpty(a_X,a_Y); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cItemGrid_IsSlotEmpty00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Clear of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_Clear00 -static int tolua_AllToLua_cItemGrid_Clear00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); -#endif - { - self->Clear(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HowManyCanFit of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HowManyCanFit00 -static int tolua_AllToLua_cItemGrid_HowManyCanFit00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isboolean(tolua_S,3,1,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); - bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL); -#endif - { - int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_AllowNewStacks); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HowManyCanFit'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddItem of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_AddItem00 -static int tolua_AllToLua_cItemGrid_AddItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItem",0,&tolua_err)) || - !tolua_isboolean(tolua_S,3,1,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - cItem* a_ItemStack = ((cItem*) tolua_tousertype(tolua_S,2,0)); - bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); - int a_PrioritarySlot = ((int) tolua_tonumber(tolua_S,4,-1)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItem'", NULL); -#endif - { - int tolua_ret = (int) self->AddItem(*a_ItemStack,a_AllowNewStacks,a_PrioritarySlot); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddItems of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_AddItems00 -static int tolua_AllToLua_cItemGrid_AddItems00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || - !tolua_isboolean(tolua_S,3,1,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - cItems* a_ItemStackList = ((cItems*) tolua_tousertype(tolua_S,2,0)); - bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); - int a_PrioritarySlot = ((int) tolua_tonumber(tolua_S,4,-1)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItems'", NULL); -#endif - { - int tolua_ret = (int) self->AddItems(*a_ItemStackList,a_AllowNewStacks,a_PrioritarySlot); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddItems'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ChangeSlotCount of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_ChangeSlotCount00 -static int tolua_AllToLua_cItemGrid_ChangeSlotCount00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - int a_AddToCount = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL); -#endif - { - int tolua_ret = (int) self->ChangeSlotCount(a_SlotNum,a_AddToCount); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ChangeSlotCount'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ChangeSlotCount of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_ChangeSlotCount01 -static int tolua_AllToLua_cItemGrid_ChangeSlotCount01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); - int a_AddToCount = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL); -#endif - { - int tolua_ret = (int) self->ChangeSlotCount(a_X,a_Y,a_AddToCount); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cItemGrid_ChangeSlotCount00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RemoveOneItem of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_RemoveOneItem00 -static int tolua_AllToLua_cItemGrid_RemoveOneItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneItem'", NULL); -#endif - { - cItem tolua_ret = (cItem) self->RemoveOneItem(a_SlotNum); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RemoveOneItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RemoveOneItem of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_RemoveOneItem01 -static int tolua_AllToLua_cItemGrid_RemoveOneItem01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneItem'", NULL); -#endif - { - cItem tolua_ret = (cItem) self->RemoveOneItem(a_X,a_Y); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); - tolua_pushusertype(tolua_S,tolua_obj,"cItem"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cItemGrid_RemoveOneItem00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HowManyItems of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HowManyItems00 -static int tolua_AllToLua_cItemGrid_HowManyItems00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyItems'", NULL); -#endif - { - int tolua_ret = (int) self->HowManyItems(*a_Item); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HowManyItems'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HasItems of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HasItems00 -static int tolua_AllToLua_cItemGrid_HasItems00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasItems'", NULL); -#endif - { - bool tolua_ret = (bool) self->HasItems(*a_ItemStack); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HasItems'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFirstEmptySlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetFirstEmptySlot00 -static int tolua_AllToLua_cItemGrid_GetFirstEmptySlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFirstEmptySlot'", NULL); -#endif - { - int tolua_ret = (int) self->GetFirstEmptySlot(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFirstEmptySlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFirstUsedSlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetFirstUsedSlot00 -static int tolua_AllToLua_cItemGrid_GetFirstUsedSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFirstUsedSlot'", NULL); -#endif - { - int tolua_ret = (int) self->GetFirstUsedSlot(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFirstUsedSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetLastEmptySlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetLastEmptySlot00 -static int tolua_AllToLua_cItemGrid_GetLastEmptySlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastEmptySlot'", NULL); -#endif - { - int tolua_ret = (int) self->GetLastEmptySlot(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetLastEmptySlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetLastUsedSlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetLastUsedSlot00 -static int tolua_AllToLua_cItemGrid_GetLastUsedSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastUsedSlot'", NULL); -#endif - { - int tolua_ret = (int) self->GetLastUsedSlot(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetLastUsedSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNextEmptySlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNextEmptySlot00 -static int tolua_AllToLua_cItemGrid_GetNextEmptySlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_StartFrom = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNextEmptySlot'", NULL); -#endif - { - int tolua_ret = (int) self->GetNextEmptySlot(a_StartFrom); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNextEmptySlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetNextUsedSlot of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNextUsedSlot00 -static int tolua_AllToLua_cItemGrid_GetNextUsedSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_StartFrom = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNextUsedSlot'", NULL); -#endif - { - int tolua_ret = (int) self->GetNextUsedSlot(a_StartFrom); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetNextUsedSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CopyToItems of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_CopyToItems00 -static int tolua_AllToLua_cItemGrid_CopyToItems00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); - cItems* a_Items = ((cItems*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyToItems'", NULL); -#endif - { - self->CopyToItems(*a_Items); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CopyToItems'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DamageItem of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_DamageItem00 -static int tolua_AllToLua_cItemGrid_DamageItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - short a_Amount = ((short) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); -#endif - { - bool tolua_ret = (bool) self->DamageItem(a_SlotNum,a_Amount); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DamageItem of class cItemGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_DamageItem01 -static int tolua_AllToLua_cItemGrid_DamageItem01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); - short a_Amount = ((short) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); -#endif - { - bool tolua_ret = (bool) self->DamageItem(a_X,a_Y,a_Amount); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cItemGrid_DamageItem00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPosX of class cBlockEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosX00 -static int tolua_AllToLua_cBlockEntity_GetPosX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosX'", NULL); -#endif - { - int tolua_ret = (int) self->GetPosX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPosX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPosY of class cBlockEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosY00 -static int tolua_AllToLua_cBlockEntity_GetPosY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosY'", NULL); -#endif - { - int tolua_ret = (int) self->GetPosY(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPosY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPosZ of class cBlockEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosZ00 -static int tolua_AllToLua_cBlockEntity_GetPosZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosZ'", NULL); -#endif - { - int tolua_ret = (int) self->GetPosZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPosZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockType of class cBlockEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetBlockType00 -static int tolua_AllToLua_cBlockEntity_GetBlockType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockType(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWorld of class cBlockEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetWorld00 -static int tolua_AllToLua_cBlockEntity_GetWorld00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); -#endif - { - cWorld* tolua_ret = (cWorld*) self->GetWorld(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetChunkX of class cBlockEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetChunkX00 -static int tolua_AllToLua_cBlockEntity_GetChunkX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL); -#endif - { - int tolua_ret = (int) self->GetChunkX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetChunkZ of class cBlockEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetChunkZ00 -static int tolua_AllToLua_cBlockEntity_GetChunkZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL); -#endif - { - int tolua_ret = (int) self->GetChunkZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRelX of class cBlockEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetRelX00 -static int tolua_AllToLua_cBlockEntity_GetRelX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelX'", NULL); -#endif - { - int tolua_ret = (int) self->GetRelX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRelX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRelZ of class cBlockEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetRelZ00 -static int tolua_AllToLua_cBlockEntity_GetRelZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelZ'", NULL); -#endif - { - int tolua_ret = (int) self->GetRelZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRelZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSlot of class cBlockEntityWithItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetSlot00 -static int tolua_AllToLua_cBlockEntityWithItems_GetSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntityWithItems",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockEntityWithItems* self = (const cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSlot of class cBlockEntityWithItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetSlot01 -static int tolua_AllToLua_cBlockEntityWithItems_GetSlot01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockEntityWithItems",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - const cBlockEntityWithItems* self = (const cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetSlot(a_X,a_Y); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBlockEntityWithItems_GetSlot00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSlot of class cBlockEntityWithItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_SetSlot00 -static int tolua_AllToLua_cBlockEntityWithItems_SetSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); -#endif - { - self->SetSlot(a_SlotNum,*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSlot of class cBlockEntityWithItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_SetSlot01 -static int tolua_AllToLua_cBlockEntityWithItems_SetSlot01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); -#endif - { - self->SetSlot(a_X,a_Y,*a_Item); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cBlockEntityWithItems_SetSlot00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetContents of class cBlockEntityWithItems */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetContents00 -static int tolua_AllToLua_cBlockEntityWithItems_GetContents00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetContents'", NULL); -#endif - { - cItemGrid& tolua_ret = (cItemGrid&) self->GetContents(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetContents'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddDropSpenserDir of class cDropSpenserEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00 -static int tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_Direction = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddDropSpenserDir'", NULL); -#endif - { - self->AddDropSpenserDir(a_BlockX,a_BlockY,a_BlockZ,a_Direction); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockX); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockY); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockZ); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddDropSpenserDir'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Activate of class cDropSpenserEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_Activate00 -static int tolua_AllToLua_cDropSpenserEntity_Activate00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Activate'", NULL); -#endif - { - self->Activate(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Activate'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRedstonePower of class cDropSpenserEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00 -static int tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); - bool a_IsPowered = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRedstonePower'", NULL); -#endif - { - self->SetRedstonePower(a_IsPowered); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRedstonePower'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetInputSlot of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetInputSlot00 -static int tolua_AllToLua_cFurnaceEntity_GetInputSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInputSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetInputSlot(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetInputSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFuelSlot of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetFuelSlot00 -static int tolua_AllToLua_cFurnaceEntity_GetFuelSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFuelSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetFuelSlot(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFuelSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetOutputSlot of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetOutputSlot00 -static int tolua_AllToLua_cFurnaceEntity_GetOutputSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOutputSlot'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetOutputSlot(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetOutputSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetInputSlot of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetInputSlot00 -static int tolua_AllToLua_cFurnaceEntity_SetInputSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetInputSlot'", NULL); -#endif - { - self->SetInputSlot(*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetInputSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetFuelSlot of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetFuelSlot00 -static int tolua_AllToLua_cFurnaceEntity_SetFuelSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFuelSlot'", NULL); -#endif - { - self->SetFuelSlot(*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetFuelSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetOutputSlot of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetOutputSlot00 -static int tolua_AllToLua_cFurnaceEntity_SetOutputSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetOutputSlot'", NULL); -#endif - { - self->SetOutputSlot(*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetOutputSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetTimeCooked of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetTimeCooked00 -static int tolua_AllToLua_cFurnaceEntity_GetTimeCooked00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTimeCooked'", NULL); -#endif - { - int tolua_ret = (int) self->GetTimeCooked(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetTimeCooked'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetCookTimeLeft of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00 -static int tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCookTimeLeft'", NULL); -#endif - { - int tolua_ret = (int) self->GetCookTimeLeft(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetCookTimeLeft'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFuelBurnTimeLeft of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00 -static int tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFuelBurnTimeLeft'", NULL); -#endif - { - int tolua_ret = (int) self->GetFuelBurnTimeLeft(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFuelBurnTimeLeft'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HasFuelTimeLeft of class cFurnaceEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00 -static int tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasFuelTimeLeft'", NULL); -#endif - { - bool tolua_ret = (bool) self->HasFuelTimeLeft(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HasFuelTimeLeft'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRecord of class cJukeboxEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_GetRecord00 -static int tolua_AllToLua_cJukeboxEntity_GetRecord00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRecord'", NULL); -#endif - { - int tolua_ret = (int) self->GetRecord(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRecord'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRecord of class cJukeboxEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_SetRecord00 -static int tolua_AllToLua_cJukeboxEntity_SetRecord00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); - int a_Record = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRecord'", NULL); -#endif - { - self->SetRecord(a_Record); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRecord'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: PlayRecord of class cJukeboxEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_PlayRecord00 -static int tolua_AllToLua_cJukeboxEntity_PlayRecord00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'PlayRecord'", NULL); -#endif - { - self->PlayRecord(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'PlayRecord'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: EjectRecord of class cJukeboxEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_EjectRecord00 -static int tolua_AllToLua_cJukeboxEntity_EjectRecord00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EjectRecord'", NULL); -#endif - { - self->EjectRecord(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'EjectRecord'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPitch of class cNoteEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_GetPitch00 -static int tolua_AllToLua_cNoteEntity_GetPitch00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL); -#endif - { - char tolua_ret = (char) self->GetPitch(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPitch'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPitch of class cNoteEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_SetPitch00 -static int tolua_AllToLua_cNoteEntity_SetPitch00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); - char a_Pitch = ((char) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitch'", NULL); -#endif - { - self->SetPitch(a_Pitch); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetPitch'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IncrementPitch of class cNoteEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_IncrementPitch00 -static int tolua_AllToLua_cNoteEntity_IncrementPitch00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IncrementPitch'", NULL); -#endif - { - self->IncrementPitch(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IncrementPitch'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MakeSound of class cNoteEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_MakeSound00 -static int tolua_AllToLua_cNoteEntity_MakeSound00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MakeSound'", NULL); -#endif - { - self->MakeSound(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MakeSound'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetLines of class cSignEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_SetLines00 -static int tolua_AllToLua_cSignEntity_SetLines00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cSignEntity",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_iscppstring(tolua_S,4,0,&tolua_err) || - !tolua_iscppstring(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cSignEntity* self = (cSignEntity*) tolua_tousertype(tolua_S,1,0); - const AString a_Line1 = ((const AString) tolua_tocppstring(tolua_S,2,0)); - const AString a_Line2 = ((const AString) tolua_tocppstring(tolua_S,3,0)); - const AString a_Line3 = ((const AString) tolua_tocppstring(tolua_S,4,0)); - const AString a_Line4 = ((const AString) tolua_tocppstring(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLines'", NULL); -#endif - { - self->SetLines(a_Line1,a_Line2,a_Line3,a_Line4); - tolua_pushcppstring(tolua_S,(const char*)a_Line1); - tolua_pushcppstring(tolua_S,(const char*)a_Line2); - tolua_pushcppstring(tolua_S,(const char*)a_Line3); - tolua_pushcppstring(tolua_S,(const char*)a_Line4); - } - } - return 4; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetLines'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetLine of class cSignEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_SetLine00 -static int tolua_AllToLua_cSignEntity_SetLine00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cSignEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_iscppstring(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cSignEntity* self = (cSignEntity*) tolua_tousertype(tolua_S,1,0); - int a_Index = ((int) tolua_tonumber(tolua_S,2,0)); - const AString a_Line = ((const AString) tolua_tocppstring(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLine'", NULL); -#endif - { - self->SetLine(a_Index,a_Line); - tolua_pushcppstring(tolua_S,(const char*)a_Line); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetLine'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetLine of class cSignEntity */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_GetLine00 -static int tolua_AllToLua_cSignEntity_GetLine00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cSignEntity",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cSignEntity* self = (const cSignEntity*) tolua_tousertype(tolua_S,1,0); - int a_Index = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLine'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetLine(a_Index); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetLine'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Name of class HTTPFormData */ -#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Name -static int tolua_get_HTTPFormData_Name(lua_State* tolua_S) -{ - HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Name'",NULL); -#endif - tolua_pushcppstring(tolua_S,(const char*)self->Name); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Name of class HTTPFormData */ -#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Name -static int tolua_set_HTTPFormData_Name(lua_State* tolua_S) -{ - HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Name'",NULL); - if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Name = ((std::string) tolua_tocppstring(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Value of class HTTPFormData */ -#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Value -static int tolua_get_HTTPFormData_Value(lua_State* tolua_S) -{ - HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Value'",NULL); -#endif - tolua_pushcppstring(tolua_S,(const char*)self->Value); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Value of class HTTPFormData */ -#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Value -static int tolua_set_HTTPFormData_Value(lua_State* tolua_S) -{ - HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Value'",NULL); - if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Value = ((std::string) tolua_tocppstring(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Type of class HTTPFormData */ -#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Type -static int tolua_get_HTTPFormData_Type(lua_State* tolua_S) -{ - HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Type'",NULL); -#endif - tolua_pushcppstring(tolua_S,(const char*)self->Type); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Type of class HTTPFormData */ -#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Type -static int tolua_set_HTTPFormData_Type(lua_State* tolua_S) -{ - HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Type'",NULL); - if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Type = ((std::string) tolua_tocppstring(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Method of class HTTPRequest */ -#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Method -static int tolua_get_HTTPRequest_Method(lua_State* tolua_S) -{ - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Method'",NULL); -#endif - tolua_pushcppstring(tolua_S,(const char*)self->Method); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Method of class HTTPRequest */ -#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Method -static int tolua_set_HTTPRequest_Method(lua_State* tolua_S) -{ - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Method'",NULL); - if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Method = ((AString) tolua_tocppstring(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Path of class HTTPRequest */ -#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Path -static int tolua_get_HTTPRequest_Path(lua_State* tolua_S) -{ - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Path'",NULL); -#endif - tolua_pushcppstring(tolua_S,(const char*)self->Path); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Path of class HTTPRequest */ -#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Path -static int tolua_set_HTTPRequest_Path(lua_State* tolua_S) -{ - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Path'",NULL); - if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Path = ((AString) tolua_tocppstring(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Username of class HTTPRequest */ -#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Username -static int tolua_get_HTTPRequest_Username(lua_State* tolua_S) -{ - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Username'",NULL); -#endif - tolua_pushcppstring(tolua_S,(const char*)self->Username); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Username of class HTTPRequest */ -#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Username -static int tolua_set_HTTPRequest_Username(lua_State* tolua_S) -{ - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Username'",NULL); - if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Username = ((AString) tolua_tocppstring(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Request of class HTTPTemplateRequest */ -#ifndef TOLUA_DISABLE_tolua_get_HTTPTemplateRequest_Request -static int tolua_get_HTTPTemplateRequest_Request(lua_State* tolua_S) -{ - HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL); -#endif - tolua_pushusertype(tolua_S,(void*)&self->Request,"HTTPRequest"); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Request of class HTTPTemplateRequest */ -#ifndef TOLUA_DISABLE_tolua_set_HTTPTemplateRequest_Request -static int tolua_set_HTTPTemplateRequest_Request(lua_State* tolua_S) -{ - HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL); - if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"HTTPRequest",0,&tolua_err))) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Request = *((HTTPRequest*) tolua_tousertype(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: Content of class sWebAdminPage */ -#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_Content -static int tolua_get_sWebAdminPage_Content(lua_State* tolua_S) -{ - sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL); -#endif - tolua_pushcppstring(tolua_S,(const char*)self->Content); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: Content of class sWebAdminPage */ -#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_Content -static int tolua_set_sWebAdminPage_Content(lua_State* tolua_S) -{ - sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL); - if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->Content = ((AString) tolua_tocppstring(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: PluginName of class sWebAdminPage */ -#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_PluginName -static int tolua_get_sWebAdminPage_PluginName(lua_State* tolua_S) -{ - sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL); -#endif - tolua_pushcppstring(tolua_S,(const char*)self->PluginName); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: PluginName of class sWebAdminPage */ -#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_PluginName -static int tolua_set_sWebAdminPage_PluginName(lua_State* tolua_S) -{ - sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL); - if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->PluginName = ((AString) tolua_tocppstring(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: TabName of class sWebAdminPage */ -#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_TabName -static int tolua_get_sWebAdminPage_TabName(lua_State* tolua_S) -{ - sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL); -#endif - tolua_pushcppstring(tolua_S,(const char*)self->TabName); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: TabName of class sWebAdminPage */ -#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_TabName -static int tolua_set_sWebAdminPage_TabName(lua_State* tolua_S) -{ - sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL); - if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->TabName = ((AString) tolua_tocppstring(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPage of class cWebAdmin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00 -static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); - const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL); -#endif - { - sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage)); - tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetDefaultPage of class cWebAdmin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetDefaultPage00 -static int tolua_AllToLua_cWebAdmin_GetDefaultPage00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultPage'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetDefaultPage(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetDefaultPage'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBaseURL of class cWebAdmin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetBaseURL00 -static int tolua_AllToLua_cWebAdmin_GetBaseURL00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); - const AString a_URL = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBaseURL'", NULL); -#endif - { - AString tolua_ret = (AString) self->GetBaseURL(a_URL); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_URL); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBaseURL'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHTMLEscapedString of class cWebAdmin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00 -static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cWebAdmin",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_Input = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - AString tolua_ret = (AString) cWebAdmin::GetHTMLEscapedString(a_Input); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_Input); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHTMLEscapedString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWebTitle of class cWebPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_GetWebTitle00 -static int tolua_AllToLua_cWebPlugin_GetWebTitle00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWebPlugin",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWebPlugin* self = (const cWebPlugin*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWebTitle'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetWebTitle(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWebTitle'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HandleWebRequest of class cWebPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_HandleWebRequest00 -static int tolua_AllToLua_cWebPlugin_HandleWebRequest00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWebPlugin",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0); - const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HandleWebRequest'", NULL); -#endif - { - AString tolua_ret = (AString) self->HandleWebRequest(a_Request); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HandleWebRequest'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SafeString of class cWebPlugin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_SafeString00 -static int tolua_AllToLua_cWebPlugin_SafeString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cWebPlugin",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_String = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - AString tolua_ret = (AString) cWebPlugin::SafeString(a_String); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_String); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SafeString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Get of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_Get00 -static int tolua_AllToLua_cRoot_Get00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cRoot* tolua_ret = (cRoot*) cRoot::Get(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cRoot"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetServer of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetServer00 -static int tolua_AllToLua_cRoot_GetServer00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetServer'", NULL); -#endif - { - cServer* tolua_ret = (cServer*) self->GetServer(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cServer"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetServer'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetDefaultWorld of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetDefaultWorld00 -static int tolua_AllToLua_cRoot_GetDefaultWorld00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultWorld'", NULL); -#endif - { - cWorld* tolua_ret = (cWorld*) self->GetDefaultWorld(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetDefaultWorld'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWorld of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetWorld00 -static int tolua_AllToLua_cRoot_GetWorld00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); - const AString a_WorldName = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); -#endif - { - cWorld* tolua_ret = (cWorld*) self->GetWorld(a_WorldName); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); - tolua_pushcppstring(tolua_S,(const char*)a_WorldName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPrimaryServerVersion of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPrimaryServerVersion00 -static int tolua_AllToLua_cRoot_GetPrimaryServerVersion00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cRoot* self = (const cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPrimaryServerVersion'", NULL); -#endif - { - int tolua_ret = (int) self->GetPrimaryServerVersion(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPrimaryServerVersion'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetPrimaryServerVersion of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_SetPrimaryServerVersion00 -static int tolua_AllToLua_cRoot_SetPrimaryServerVersion00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); - int a_Version = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPrimaryServerVersion'", NULL); -#endif - { - self->SetPrimaryServerVersion(a_Version); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetPrimaryServerVersion'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetGroupManager of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetGroupManager00 -static int tolua_AllToLua_cRoot_GetGroupManager00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGroupManager'", NULL); -#endif - { - cGroupManager* tolua_ret = (cGroupManager*) self->GetGroupManager(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cGroupManager"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetGroupManager'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetCraftingRecipes of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetCraftingRecipes00 -static int tolua_AllToLua_cRoot_GetCraftingRecipes00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCraftingRecipes'", NULL); -#endif - { - cCraftingRecipes* tolua_ret = (cCraftingRecipes*) self->GetCraftingRecipes(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingRecipes"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetCraftingRecipes'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetFurnaceRecipe of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetFurnaceRecipe00 -static int tolua_AllToLua_cRoot_GetFurnaceRecipe00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFurnaceRecipe'", NULL); -#endif - { - cFurnaceRecipe* tolua_ret = (cFurnaceRecipe*) self->GetFurnaceRecipe(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cFurnaceRecipe"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetFurnaceRecipe'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWebAdmin of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetWebAdmin00 -static int tolua_AllToLua_cRoot_GetWebAdmin00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWebAdmin'", NULL); -#endif - { - cWebAdmin* tolua_ret = (cWebAdmin*) self->GetWebAdmin(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWebAdmin"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWebAdmin'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPluginManager of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPluginManager00 -static int tolua_AllToLua_cRoot_GetPluginManager00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPluginManager'", NULL); -#endif - { - cPluginManager* tolua_ret = (cPluginManager*) self->GetPluginManager(); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPluginManager"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPluginManager'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: QueueExecuteConsoleCommand of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00 -static int tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); - const AString a_Cmd = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueExecuteConsoleCommand'", NULL); -#endif - { - self->QueueExecuteConsoleCommand(a_Cmd); - tolua_pushcppstring(tolua_S,(const char*)a_Cmd); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'QueueExecuteConsoleCommand'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetTotalChunkCount of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetTotalChunkCount00 -static int tolua_AllToLua_cRoot_GetTotalChunkCount00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTotalChunkCount'", NULL); -#endif - { - int tolua_ret = (int) self->GetTotalChunkCount(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetTotalChunkCount'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SaveAllChunks of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_SaveAllChunks00 -static int tolua_AllToLua_cRoot_SaveAllChunks00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveAllChunks'", NULL); -#endif - { - self->SaveAllChunks(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SaveAllChunks'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: BroadcastChat of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_BroadcastChat00 -static int tolua_AllToLua_cRoot_BroadcastChat00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); - const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastChat'", NULL); -#endif - { - self->BroadcastChat(a_Message); - tolua_pushcppstring(tolua_S,(const char*)a_Message); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'BroadcastChat'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetProtocolVersionTextFromInt of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00 -static int tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - int a_ProtocolVersionNum = ((int) tolua_tonumber(tolua_S,2,0)); - { - AString tolua_ret = (AString) cRoot::GetProtocolVersionTextFromInt(a_ProtocolVersionNum); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetProtocolVersionTextFromInt'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetVirtualRAMUsage of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetVirtualRAMUsage00 -static int tolua_AllToLua_cRoot_GetVirtualRAMUsage00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - int tolua_ret = (int) cRoot::GetVirtualRAMUsage(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetVirtualRAMUsage'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetPhysicalRAMUsage of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPhysicalRAMUsage00 -static int tolua_AllToLua_cRoot_GetPhysicalRAMUsage00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - int tolua_ret = (int) cRoot::GetPhysicalRAMUsage(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPhysicalRAMUsage'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00 -static int tolua_AllToLua_Vector3f_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00_local -static int tolua_AllToLua_Vector3f_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new01 -static int tolua_AllToLua_Vector3f_new01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new01_local -static int tolua_AllToLua_Vector3f_new01_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new00_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new02 -static int tolua_AllToLua_Vector3f_new02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new02_local -static int tolua_AllToLua_Vector3f_new02_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new01_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new03 -static int tolua_AllToLua_Vector3f_new03(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new02(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new03_local -static int tolua_AllToLua_Vector3f_new03_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new02_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new04 -static int tolua_AllToLua_Vector3f_new04(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else - { - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new03(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new04_local -static int tolua_AllToLua_Vector3f_new04_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else - { - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new03_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new05 -static int tolua_AllToLua_Vector3f_new05(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - float a_x = ((float) tolua_tonumber(tolua_S,2,0)); - float a_y = ((float) tolua_tonumber(tolua_S,3,0)); - float a_z = ((float) tolua_tonumber(tolua_S,4,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(a_x,a_y,a_z)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new04(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new05_local -static int tolua_AllToLua_Vector3f_new05_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - float a_x = ((float) tolua_tonumber(tolua_S,2,0)); - float a_y = ((float) tolua_tonumber(tolua_S,3,0)); - float a_z = ((float) tolua_tonumber(tolua_S,4,0)); - { - Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(a_x,a_y,a_z)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f_new04_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Set of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Set00 -static int tolua_AllToLua_Vector3f_Set00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); - float a_x = ((float) tolua_tonumber(tolua_S,2,0)); - float a_y = ((float) tolua_tonumber(tolua_S,3,0)); - float a_z = ((float) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); -#endif - { - self->Set(a_x,a_y,a_z); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Normalize of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Normalize00 -static int tolua_AllToLua_Vector3f_Normalize00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Normalize'", NULL); -#endif - { - self->Normalize(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Normalize'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: NormalizeCopy of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_NormalizeCopy00 -static int tolua_AllToLua_Vector3f_NormalizeCopy00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); -#endif - { - Vector3f tolua_ret = (Vector3f) self->NormalizeCopy(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'NormalizeCopy'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: NormalizeCopy of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_NormalizeCopy01 -static int tolua_AllToLua_Vector3f_NormalizeCopy01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - Vector3f* a_V = ((Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); -#endif - { - self->NormalizeCopy(*a_V); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_Vector3f_NormalizeCopy00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Length of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Length00 -static int tolua_AllToLua_Vector3f_Length00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL); -#endif - { - float tolua_ret = (float) self->Length(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SqrLength of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_SqrLength00 -static int tolua_AllToLua_Vector3f_SqrLength00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL); -#endif - { - float tolua_ret = (float) self->SqrLength(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Dot of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Dot00 -static int tolua_AllToLua_Vector3f_Dot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - const Vector3f* a_V = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dot'", NULL); -#endif - { - float tolua_ret = (float) self->Dot(*a_V); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Dot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Cross of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Cross00 -static int tolua_AllToLua_Vector3f_Cross00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Cross'", NULL); -#endif - { - Vector3f tolua_ret = (Vector3f) self->Cross(*v); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Cross'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Equals of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Equals00 -static int tolua_AllToLua_Vector3f_Equals00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); -#endif - { - bool tolua_ret = (bool) self->Equals(*v); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator+ of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__add00 -static int tolua_AllToLua_Vector3f__add00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); -#endif - { - Vector3f tolua_ret = (Vector3f) self->operator+(*v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function '.add'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator+ of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__add01 -static int tolua_AllToLua_Vector3f__add01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); -#endif - { - Vector3f tolua_ret = (Vector3f) self->operator+(v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f__add00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator- of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__sub00 -static int tolua_AllToLua_Vector3f__sub00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); -#endif - { - Vector3f tolua_ret = (Vector3f) self->operator-(*v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function '.sub'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator- of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__sub01 -static int tolua_AllToLua_Vector3f__sub01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); -#endif - { - Vector3f tolua_ret = (Vector3f) self->operator-(v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f__sub00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator* of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__mul00 -static int tolua_AllToLua_Vector3f__mul00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - const float f = ((const float) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); -#endif - { - Vector3f tolua_ret = (Vector3f) self->operator*(f); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function '.mul'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator* of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__mul01 -static int tolua_AllToLua_Vector3f__mul01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); - const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); -#endif - { - Vector3f tolua_ret = (Vector3f) self->operator*(*v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3f__mul00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: x of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3f_x -static int tolua_get_Vector3f_x(lua_State* tolua_S) -{ - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->x); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: x of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_set_Vector3f_x -static int tolua_set_Vector3f_x(lua_State* tolua_S) -{ - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->x = ((float) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: y of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3f_y -static int tolua_get_Vector3f_y(lua_State* tolua_S) -{ - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->y); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: y of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_set_Vector3f_y -static int tolua_set_Vector3f_y(lua_State* tolua_S) -{ - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->y = ((float) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: z of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3f_z -static int tolua_get_Vector3f_z(lua_State* tolua_S) -{ - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->z); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: z of class Vector3f */ -#ifndef TOLUA_DISABLE_tolua_set_Vector3f_z -static int tolua_set_Vector3f_z(lua_State* tolua_S) -{ - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->z = ((float) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new00 -static int tolua_AllToLua_Vector3d_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); - { - Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(*v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new00_local -static int tolua_AllToLua_Vector3d_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); - { - Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(*v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new01 -static int tolua_AllToLua_Vector3d_new01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); - { - Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3d_new00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new01_local -static int tolua_AllToLua_Vector3d_new01_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); - { - Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3d_new00_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new02 -static int tolua_AllToLua_Vector3d_new02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else - { - { - Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3d_new01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new02_local -static int tolua_AllToLua_Vector3d_new02_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else - { - { - Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3d_new01_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new03 -static int tolua_AllToLua_Vector3d_new03(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - double a_x = ((double) tolua_tonumber(tolua_S,2,0)); - double a_y = ((double) tolua_tonumber(tolua_S,3,0)); - double a_z = ((double) tolua_tonumber(tolua_S,4,0)); - { - Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(a_x,a_y,a_z)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3d_new02(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new03_local -static int tolua_AllToLua_Vector3d_new03_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - double a_x = ((double) tolua_tonumber(tolua_S,2,0)); - double a_y = ((double) tolua_tonumber(tolua_S,3,0)); - double a_z = ((double) tolua_tonumber(tolua_S,4,0)); - { - Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(a_x,a_y,a_z)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3d_new02_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Set of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Set00 -static int tolua_AllToLua_Vector3d_Set00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); - double a_x = ((double) tolua_tonumber(tolua_S,2,0)); - double a_y = ((double) tolua_tonumber(tolua_S,3,0)); - double a_z = ((double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); -#endif - { - self->Set(a_x,a_y,a_z); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Normalize of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Normalize00 -static int tolua_AllToLua_Vector3d_Normalize00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Normalize'", NULL); -#endif - { - self->Normalize(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Normalize'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: NormalizeCopy of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_NormalizeCopy00 -static int tolua_AllToLua_Vector3d_NormalizeCopy00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->NormalizeCopy(); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'NormalizeCopy'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: NormalizeCopy of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_NormalizeCopy01 -static int tolua_AllToLua_Vector3d_NormalizeCopy01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); - Vector3d* a_V = ((Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); -#endif - { - self->NormalizeCopy(*a_V); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_Vector3d_NormalizeCopy00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Length of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Length00 -static int tolua_AllToLua_Vector3d_Length00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL); -#endif - { - double tolua_ret = (double) self->Length(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SqrLength of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_SqrLength00 -static int tolua_AllToLua_Vector3d_SqrLength00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL); -#endif - { - double tolua_ret = (double) self->SqrLength(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Dot of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Dot00 -static int tolua_AllToLua_Vector3d_Dot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_V = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dot'", NULL); -#endif - { - double tolua_ret = (double) self->Dot(*a_V); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Dot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Cross of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Cross00 -static int tolua_AllToLua_Vector3d_Cross00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Cross'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->Cross(*v); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Cross'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: LineCoeffToXYPlane of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToXYPlane00 -static int tolua_AllToLua_Vector3d_LineCoeffToXYPlane00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - double a_Z = ((double) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToXYPlane'", NULL); -#endif - { - double tolua_ret = (double) self->LineCoeffToXYPlane(*a_OtherEnd,a_Z); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'LineCoeffToXYPlane'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: LineCoeffToXZPlane of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToXZPlane00 -static int tolua_AllToLua_Vector3d_LineCoeffToXZPlane00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToXZPlane'", NULL); -#endif - { - double tolua_ret = (double) self->LineCoeffToXZPlane(*a_OtherEnd,a_Y); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'LineCoeffToXZPlane'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: LineCoeffToYZPlane of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToYZPlane00 -static int tolua_AllToLua_Vector3d_LineCoeffToYZPlane00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - double a_X = ((double) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToYZPlane'", NULL); -#endif - { - double tolua_ret = (double) self->LineCoeffToYZPlane(*a_OtherEnd,a_X); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'LineCoeffToYZPlane'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Equals of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Equals00 -static int tolua_AllToLua_Vector3d_Equals00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); -#endif - { - bool tolua_ret = (bool) self->Equals(*v); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator+ of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__add00 -static int tolua_AllToLua_Vector3d__add00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->operator+(*v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function '.add'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator+ of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__add01 -static int tolua_AllToLua_Vector3d__add01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->operator+(v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3d__add00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator- of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__sub00 -static int tolua_AllToLua_Vector3d__sub00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->operator-(*v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function '.sub'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator- of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__sub01 -static int tolua_AllToLua_Vector3d__sub01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->operator-(v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3d__sub00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator* of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__mul00 -static int tolua_AllToLua_Vector3d__mul00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const double f = ((const double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->operator*(f); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function '.mul'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator* of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__mul01 -static int tolua_AllToLua_Vector3d__mul01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->operator*(*v2); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3d__mul00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: operator/ of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__div00 -static int tolua_AllToLua_Vector3d__div00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); - const double f = ((const double) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator/'", NULL); -#endif - { - Vector3d tolua_ret = (Vector3d) self->operator/(f); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); - tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function '.div'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: x of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3d_x -static int tolua_get_Vector3d_x(lua_State* tolua_S) -{ - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->x); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: x of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_set_Vector3d_x -static int tolua_set_Vector3d_x(lua_State* tolua_S) -{ - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->x = ((double) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: y of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3d_y -static int tolua_get_Vector3d_y(lua_State* tolua_S) -{ - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->y); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: y of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_set_Vector3d_y -static int tolua_set_Vector3d_y(lua_State* tolua_S) -{ - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->y = ((double) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: z of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3d_z -static int tolua_get_Vector3d_z(lua_State* tolua_S) -{ - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->z); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: z of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_set_Vector3d_z -static int tolua_set_Vector3d_z(lua_State* tolua_S) -{ - Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->z = ((double) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: EPS of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3d_EPS -static int tolua_get_Vector3d_EPS(lua_State* tolua_S) -{ - tolua_pushnumber(tolua_S,(lua_Number)Vector3d::EPS); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: NO_INTERSECTION of class Vector3d */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3d_NO_INTERSECTION -static int tolua_get_Vector3d_NO_INTERSECTION(lua_State* tolua_S) -{ - tolua_pushnumber(tolua_S,(lua_Number)Vector3d::NO_INTERSECTION); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new00 -static int tolua_AllToLua_Vector3i_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - { - Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(*v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new00_local -static int tolua_AllToLua_Vector3i_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - { - Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(*v)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new01 -static int tolua_AllToLua_Vector3i_new01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else - { - { - Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3i_new00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new01_local -static int tolua_AllToLua_Vector3i_new01_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else - { - { - Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3i_new00_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new02 -static int tolua_AllToLua_Vector3i_new02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - int a_x = ((int) tolua_tonumber(tolua_S,2,0)); - int a_y = ((int) tolua_tonumber(tolua_S,3,0)); - int a_z = ((int) tolua_tonumber(tolua_S,4,0)); - { - Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(a_x,a_y,a_z)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3i_new01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new02_local -static int tolua_AllToLua_Vector3i_new02_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - int a_x = ((int) tolua_tonumber(tolua_S,2,0)); - int a_y = ((int) tolua_tonumber(tolua_S,3,0)); - int a_z = ((int) tolua_tonumber(tolua_S,4,0)); - { - Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(a_x,a_y,a_z)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3i_new01_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Set of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Set00 -static int tolua_AllToLua_Vector3i_Set00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"Vector3i",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); - int a_x = ((int) tolua_tonumber(tolua_S,2,0)); - int a_y = ((int) tolua_tonumber(tolua_S,3,0)); - int a_z = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); -#endif - { - self->Set(a_x,a_y,a_z); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Length of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Length00 -static int tolua_AllToLua_Vector3i_Length00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL); -#endif - { - float tolua_ret = (float) self->Length(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SqrLength of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_SqrLength00 -static int tolua_AllToLua_Vector3i_SqrLength00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL); -#endif - { - int tolua_ret = (int) self->SqrLength(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Equals of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Equals00 -static int tolua_AllToLua_Vector3i_Equals00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); - const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); -#endif - { - bool tolua_ret = (bool) self->Equals(*v); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Equals of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Equals01 -static int tolua_AllToLua_Vector3i_Equals01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); - const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); -#endif - { - bool tolua_ret = (bool) self->Equals(v); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_Vector3i_Equals00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: x of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3i_x -static int tolua_get_Vector3i_x(lua_State* tolua_S) -{ - Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->x); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: x of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_set_Vector3i_x -static int tolua_set_Vector3i_x(lua_State* tolua_S) -{ - Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->x = ((int) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: y of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3i_y -static int tolua_get_Vector3i_y(lua_State* tolua_S) -{ - Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->y); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: y of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_set_Vector3i_y -static int tolua_set_Vector3i_y(lua_State* tolua_S) -{ - Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->y = ((int) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: z of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_get_Vector3i_z -static int tolua_get_Vector3i_z(lua_State* tolua_S) -{ - Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->z); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: z of class Vector3i */ -#ifndef TOLUA_DISABLE_tolua_set_Vector3i_z -static int tolua_set_Vector3i_z(lua_State* tolua_S) -{ - Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->z = ((int) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: p1 of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_get_cCuboid_p1 -static int tolua_get_cCuboid_p1(lua_State* tolua_S) -{ - cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p1'",NULL); -#endif - tolua_pushusertype(tolua_S,(void*)&self->p1,"Vector3i"); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: p1 of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_set_cCuboid_p1 -static int tolua_set_cCuboid_p1(lua_State* tolua_S) -{ - cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p1'",NULL); - if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3i",0,&tolua_err))) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->p1 = *((Vector3i*) tolua_tousertype(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: p2 of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_get_cCuboid_p2 -static int tolua_get_cCuboid_p2(lua_State* tolua_S) -{ - cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p2'",NULL); -#endif - tolua_pushusertype(tolua_S,(void*)&self->p2,"Vector3i"); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: p2 of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_set_cCuboid_p2 -static int tolua_set_cCuboid_p2(lua_State* tolua_S) -{ - cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p2'",NULL); - if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3i",0,&tolua_err))) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->p2 = *((Vector3i*) tolua_tousertype(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new00 -static int tolua_AllToLua_cCuboid_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new00_local -static int tolua_AllToLua_cCuboid_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new01 -static int tolua_AllToLua_cCuboid_new01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cCuboid* a_Cuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_Cuboid)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_new00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new01_local -static int tolua_AllToLua_cCuboid_new01_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cCuboid* a_Cuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_Cuboid)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_new00_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new02 -static int tolua_AllToLua_cCuboid_new02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3i",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3i* a_p1 = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); - const Vector3i* a_p2 = ((const Vector3i*) tolua_tousertype(tolua_S,3,0)); - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_p1,*a_p2)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_new01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new02_local -static int tolua_AllToLua_cCuboid_new02_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3i",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3i* a_p1 = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); - const Vector3i* a_p2 = ((const Vector3i*) tolua_tousertype(tolua_S,3,0)); - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_p1,*a_p2)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_new01_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new03 -static int tolua_AllToLua_cCuboid_new03(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); - int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_new02(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new03_local -static int tolua_AllToLua_cCuboid_new03_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); - int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_new02_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new04 -static int tolua_AllToLua_cCuboid_new04(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else - { - int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); - int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); - int a_X2 = ((int) tolua_tonumber(tolua_S,5,0)); - int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0)); - int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0)); - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_new03(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new04_local -static int tolua_AllToLua_cCuboid_new04_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else - { - int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); - int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); - int a_X2 = ((int) tolua_tonumber(tolua_S,5,0)); - int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0)); - int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0)); - { - cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_new03_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Assign of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Assign00 -static int tolua_AllToLua_cCuboid_Assign00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); - int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); - int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); - int a_X2 = ((int) tolua_tonumber(tolua_S,5,0)); - int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0)); - int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Assign'", NULL); -#endif - { - self->Assign(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Assign'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Sort of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Sort00 -static int tolua_AllToLua_cCuboid_Sort00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Sort'", NULL); -#endif - { - self->Sort(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Sort'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DifX of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifX00 -static int tolua_AllToLua_cCuboid_DifX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifX'", NULL); -#endif - { - int tolua_ret = (int) self->DifX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DifX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DifY of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifY00 -static int tolua_AllToLua_cCuboid_DifY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifY'", NULL); -#endif - { - int tolua_ret = (int) self->DifY(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DifY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DifZ of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifZ00 -static int tolua_AllToLua_cCuboid_DifZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifZ'", NULL); -#endif - { - int tolua_ret = (int) self->DifZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DifZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DoesIntersect of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DoesIntersect00 -static int tolua_AllToLua_cCuboid_DoesIntersect00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); - const cCuboid* a_Other = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoesIntersect'", NULL); -#endif - { - bool tolua_ret = (bool) self->DoesIntersect(*a_Other); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DoesIntersect'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInside of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside00 -static int tolua_AllToLua_cCuboid_IsInside00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); - const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInside(*v); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsInside'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInside of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside01 -static int tolua_AllToLua_cCuboid_IsInside01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); - int a_X = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); - int a_Z = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInside(a_X,a_Y,a_Z); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_IsInside00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInside of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside02 -static int tolua_AllToLua_cCuboid_IsInside02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); - const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInside(*v); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cCuboid_IsInside01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsCompletelyInside of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsCompletelyInside00 -static int tolua_AllToLua_cCuboid_IsCompletelyInside00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); - const cCuboid* a_Outer = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCompletelyInside'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsCompletelyInside(*a_Outer); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsCompletelyInside'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Move of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Move00 -static int tolua_AllToLua_cCuboid_Move00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); - int a_OfsX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_OfsY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_OfsZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL); -#endif - { - self->Move(a_OfsX,a_OfsY,a_OfsZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Move'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSorted of class cCuboid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsSorted00 -static int tolua_AllToLua_cCuboid_IsSorted00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSorted'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSorted(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSorted'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new00 -static int tolua_AllToLua_cBoundingBox_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - double a_MinX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_MaxX = ((double) tolua_tonumber(tolua_S,3,0)); - double a_MinY = ((double) tolua_tonumber(tolua_S,4,0)); - double a_MaxY = ((double) tolua_tonumber(tolua_S,5,0)); - double a_MinZ = ((double) tolua_tonumber(tolua_S,6,0)); - double a_MaxZ = ((double) tolua_tonumber(tolua_S,7,0)); - { - cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new00_local -static int tolua_AllToLua_cBoundingBox_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - double a_MinX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_MaxX = ((double) tolua_tonumber(tolua_S,3,0)); - double a_MinY = ((double) tolua_tonumber(tolua_S,4,0)); - double a_MaxY = ((double) tolua_tonumber(tolua_S,5,0)); - double a_MinZ = ((double) tolua_tonumber(tolua_S,6,0)); - double a_MaxZ = ((double) tolua_tonumber(tolua_S,7,0)); - { - cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new01 -static int tolua_AllToLua_cBoundingBox_new01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); - { - cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Min,*a_Max)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_new00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new01_local -static int tolua_AllToLua_cBoundingBox_new01_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); - { - cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Min,*a_Max)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_new00_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new02 -static int tolua_AllToLua_cBoundingBox_new02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - double a_Radius = ((double) tolua_tonumber(tolua_S,3,0)); - double a_Height = ((double) tolua_tonumber(tolua_S,4,0)); - { - cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Pos,a_Radius,a_Height)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_new01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new02_local -static int tolua_AllToLua_cBoundingBox_new02_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - double a_Radius = ((double) tolua_tonumber(tolua_S,3,0)); - double a_Height = ((double) tolua_tonumber(tolua_S,4,0)); - { - cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Pos,a_Radius,a_Height)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_new01_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new03 -static int tolua_AllToLua_cBoundingBox_new03(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cBoundingBox* a_Orig = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); - { - cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Orig)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_new02(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new03_local -static int tolua_AllToLua_cBoundingBox_new03_local(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - const cBoundingBox* a_Orig = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); - { - cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Orig)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_new02_local(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Move of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Move00 -static int tolua_AllToLua_cBoundingBox_Move00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - double a_OffX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_OffY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_OffZ = ((double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL); -#endif - { - self->Move(a_OffX,a_OffY,a_OffZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Move'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Move of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Move01 -static int tolua_AllToLua_cBoundingBox_Move01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_Off = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL); -#endif - { - self->Move(*a_Off); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_Move00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Expand of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Expand00 -static int tolua_AllToLua_cBoundingBox_Expand00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - double a_ExpandX = ((double) tolua_tonumber(tolua_S,2,0)); - double a_ExpandY = ((double) tolua_tonumber(tolua_S,3,0)); - double a_ExpandZ = ((double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Expand'", NULL); -#endif - { - self->Expand(a_ExpandX,a_ExpandY,a_ExpandZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Expand'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DoesIntersect of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_DoesIntersect00 -static int tolua_AllToLua_cBoundingBox_DoesIntersect00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - const cBoundingBox* a_Other = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoesIntersect'", NULL); -#endif - { - bool tolua_ret = (bool) self->DoesIntersect(*a_Other); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DoesIntersect'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Union of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Union00 -static int tolua_AllToLua_cBoundingBox_Union00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - const cBoundingBox* a_Other = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Union'", NULL); -#endif - { - cBoundingBox tolua_ret = (cBoundingBox) self->Union(*a_Other); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((cBoundingBox)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"cBoundingBox"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cBoundingBox)); - tolua_pushusertype(tolua_S,tolua_obj,"cBoundingBox"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Union'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInside of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside00 -static int tolua_AllToLua_cBoundingBox_IsInside00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_Point = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInside(*a_Point); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsInside'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInside of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside01 -static int tolua_AllToLua_cBoundingBox_IsInside01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - double a_X = ((double) tolua_tonumber(tolua_S,2,0)); - double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); - double a_Z = ((double) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInside(a_X,a_Y,a_Z); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_IsInside00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInside of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside02 -static int tolua_AllToLua_cBoundingBox_IsInside02(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBoundingBox",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - cBoundingBox* a_Other = ((cBoundingBox*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInside(*a_Other); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_IsInside01(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInside of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside03 -static int tolua_AllToLua_cBoundingBox_IsInside03(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsInside(*a_Min,*a_Max); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_IsInside02(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInside of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside04 -static int tolua_AllToLua_cBoundingBox_IsInside04(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const Vector3d",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); - const Vector3d* a_Point = ((const Vector3d*) tolua_tousertype(tolua_S,4,0)); - { - bool tolua_ret = (bool) cBoundingBox::IsInside(*a_Min,*a_Max,*a_Point); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_IsInside03(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsInside of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside05 -static int tolua_AllToLua_cBoundingBox_IsInside05(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); - double a_X = ((double) tolua_tonumber(tolua_S,4,0)); - double a_Y = ((double) tolua_tonumber(tolua_S,5,0)); - double a_Z = ((double) tolua_tonumber(tolua_S,6,0)); - { - bool tolua_ret = (bool) cBoundingBox::IsInside(*a_Min,*a_Max,a_X,a_Y,a_Z); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_IsInside04(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CalcLineIntersection of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_CalcLineIntersection00 -static int tolua_AllToLua_cBoundingBox_CalcLineIntersection00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); - const Vector3d* a_Line1 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - const Vector3d* a_Line2 = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); - double a_LineCoeff = ((double) tolua_tonumber(tolua_S,4,0)); - char a_Face = ((char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CalcLineIntersection'", NULL); -#endif - { - bool tolua_ret = (bool) self->CalcLineIntersection(*a_Line1,*a_Line2,a_LineCoeff,a_Face); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushnumber(tolua_S,(lua_Number)a_LineCoeff); - tolua_pushnumber(tolua_S,(lua_Number)a_Face); - } - } - return 3; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CalcLineIntersection'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CalcLineIntersection of class cBoundingBox */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_CalcLineIntersection01 -static int tolua_AllToLua_cBoundingBox_CalcLineIntersection01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const Vector3d",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const Vector3d",0,&tolua_err)) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else - { - const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); - const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); - const Vector3d* a_Line1 = ((const Vector3d*) tolua_tousertype(tolua_S,4,0)); - const Vector3d* a_Line2 = ((const Vector3d*) tolua_tousertype(tolua_S,5,0)); - double a_LineCoeff = ((double) tolua_tonumber(tolua_S,6,0)); - char a_Face = ((char) tolua_tonumber(tolua_S,7,0)); - { - bool tolua_ret = (bool) cBoundingBox::CalcLineIntersection(*a_Min,*a_Max,*a_Line1,*a_Line2,a_LineCoeff,a_Face); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushnumber(tolua_S,(lua_Number)a_LineCoeff); - tolua_pushnumber(tolua_S,(lua_Number)a_Face); - } - } - return 3; -tolua_lerror: - return tolua_AllToLua_cBoundingBox_CalcLineIntersection00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: BlockHitPosition of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_get_cTracer_BlockHitPosition -static int tolua_get_cTracer_BlockHitPosition(lua_State* tolua_S) -{ - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'BlockHitPosition'",NULL); -#endif - tolua_pushusertype(tolua_S,(void*)&self->BlockHitPosition,"Vector3f"); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: BlockHitPosition of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_set_cTracer_BlockHitPosition -static int tolua_set_cTracer_BlockHitPosition(lua_State* tolua_S) -{ - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'BlockHitPosition'",NULL); - if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err))) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->BlockHitPosition = *((Vector3f*) tolua_tousertype(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: HitNormal of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_get_cTracer_HitNormal -static int tolua_get_cTracer_HitNormal(lua_State* tolua_S) -{ - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'HitNormal'",NULL); -#endif - tolua_pushusertype(tolua_S,(void*)&self->HitNormal,"Vector3f"); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: HitNormal of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_set_cTracer_HitNormal -static int tolua_set_cTracer_HitNormal(lua_State* tolua_S) -{ - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'HitNormal'",NULL); - if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err))) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->HitNormal = *((Vector3f*) tolua_tousertype(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* get function: RealHit of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_get_cTracer_RealHit -static int tolua_get_cTracer_RealHit(lua_State* tolua_S) -{ - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RealHit'",NULL); -#endif - tolua_pushusertype(tolua_S,(void*)&self->RealHit,"Vector3f"); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: RealHit of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_set_cTracer_RealHit -static int tolua_set_cTracer_RealHit(lua_State* tolua_S) -{ - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RealHit'",NULL); - if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err))) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->RealHit = *((Vector3f*) tolua_tousertype(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_new00 -static int tolua_AllToLua_cTracer_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cTracer",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); - { - cTracer* tolua_ret = (cTracer*) Mtolua_new((cTracer)(a_World)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cTracer"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_new00_local -static int tolua_AllToLua_cTracer_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cTracer",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); - { - cTracer* tolua_ret = (cTracer*) Mtolua_new((cTracer)(a_World)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cTracer"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: delete of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_delete00 -static int tolua_AllToLua_cTracer_delete00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL); -#endif - Mtolua_delete(self); - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Trace of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_Trace00 -static int tolua_AllToLua_cTracer_Trace00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3f",0,&tolua_err)) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); - const Vector3f* a_Start = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); - const Vector3f* a_Direction = ((const Vector3f*) tolua_tousertype(tolua_S,3,0)); - int a_Distance = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Trace'", NULL); -#endif - { - bool tolua_ret = (bool) self->Trace(*a_Start,*a_Direction,a_Distance); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Trace'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Trace of class cTracer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_Trace01 -static int tolua_AllToLua_cTracer_Trace01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || - (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3f",0,&tolua_err)) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isboolean(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else - { - cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); - const Vector3f* a_Start = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); - const Vector3f* a_Direction = ((const Vector3f*) tolua_tousertype(tolua_S,3,0)); - int a_Distance = ((int) tolua_tonumber(tolua_S,4,0)); - bool a_LineOfSight = ((bool) tolua_toboolean(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Trace'", NULL); -#endif - { - bool tolua_ret = (bool) self->Trace(*a_Start,*a_Direction,a_Distance,a_LineOfSight); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cTracer_Trace00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetName of class cGroup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_SetName00 -static int tolua_AllToLua_cGroup_SetName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); - std::string a_Name = ((std::string) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL); -#endif - { - self->SetName(a_Name); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetName of class cGroup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_GetName00 -static int tolua_AllToLua_cGroup_GetName00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cGroup",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cGroup* self = (const cGroup*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); -#endif - { - const std::string tolua_ret = (const std::string) self->GetName(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetColor of class cGroup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_SetColor00 -static int tolua_AllToLua_cGroup_SetColor00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); - std::string a_Color = ((std::string) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetColor'", NULL); -#endif - { - self->SetColor(a_Color); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetColor'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddCommand of class cGroup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_AddCommand00 -static int tolua_AllToLua_cGroup_AddCommand00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); - std::string a_Command = ((std::string) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddCommand'", NULL); -#endif - { - self->AddCommand(a_Command); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddCommand'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: AddPermission of class cGroup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_AddPermission00 -static int tolua_AllToLua_cGroup_AddPermission00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); - std::string a_Permission = ((std::string) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPermission'", NULL); -#endif - { - self->AddPermission(a_Permission); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'AddPermission'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: InheritFrom of class cGroup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_InheritFrom00 -static int tolua_AllToLua_cGroup_InheritFrom00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cGroup",0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); - cGroup* a_Group = ((cGroup*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'InheritFrom'", NULL); -#endif - { - self->InheritFrom(a_Group); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'InheritFrom'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HasCommand of class cGroup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_HasCommand00 -static int tolua_AllToLua_cGroup_HasCommand00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); - std::string a_Command = ((std::string) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasCommand'", NULL); -#endif - { - bool tolua_ret = (bool) self->HasCommand(a_Command); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HasCommand'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetColor of class cGroup */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_GetColor00 -static int tolua_AllToLua_cGroup_GetColor00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cGroup",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cGroup* self = (const cGroup*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetColor(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetColor'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_new00 -static int tolua_AllToLua_cBlockArea_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cBlockArea* tolua_ret = (cBlockArea*) Mtolua_new((cBlockArea)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockArea"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_new00_local -static int tolua_AllToLua_cBlockArea_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - { - cBlockArea* tolua_ret = (cBlockArea*) Mtolua_new((cBlockArea)()); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockArea"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: delete of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_delete00 -static int tolua_AllToLua_cBlockArea_delete00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL); -#endif - Mtolua_delete(self); - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Clear of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Clear00 -static int tolua_AllToLua_cBlockArea_Clear00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); -#endif - { - self->Clear(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Create of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Create00 -static int tolua_AllToLua_cBlockArea_Create00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_SizeX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_SizeY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_SizeZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Create'", NULL); -#endif - { - self->Create(a_SizeX,a_SizeY,a_SizeZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Create'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Create of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Create01 -static int tolua_AllToLua_cBlockArea_Create01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_SizeX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_SizeY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_SizeZ = ((int) tolua_tonumber(tolua_S,4,0)); - int a_DataTypes = ((int) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Create'", NULL); -#endif - { - self->Create(a_SizeX,a_SizeY,a_SizeZ,a_DataTypes); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cBlockArea_Create00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetOrigin of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetOrigin00 -static int tolua_AllToLua_cBlockArea_SetOrigin00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_OriginX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_OriginY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_OriginZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetOrigin'", NULL); -#endif - { - self->SetOrigin(a_OriginX,a_OriginY,a_OriginZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetOrigin'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Read of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Read00 -static int tolua_AllToLua_cBlockArea_Read00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnoobj(tolua_S,9,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); - int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MinBlockY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,6,0)); - int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,7,0)); - int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,8,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Read'", NULL); -#endif - { - bool tolua_ret = (bool) self->Read(a_World,a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Read'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Read of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Read01 -static int tolua_AllToLua_cBlockArea_Read01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnumber(tolua_S,9,0,&tolua_err) || - !tolua_isnoobj(tolua_S,10,&tolua_err) - ) - goto tolua_lerror; - else - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); - int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MinBlockY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,6,0)); - int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,7,0)); - int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,8,0)); - int a_DataTypes = ((int) tolua_tonumber(tolua_S,9,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Read'", NULL); -#endif - { - bool tolua_ret = (bool) self->Read(a_World,a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ,a_DataTypes); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBlockArea_Read00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Write of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Write00 -static int tolua_AllToLua_cBlockArea_Write00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); - int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Write'", NULL); -#endif - { - bool tolua_ret = (bool) self->Write(a_World,a_MinBlockX,a_MinBlockY,a_MinBlockZ); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Write'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Write of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Write01 -static int tolua_AllToLua_cBlockArea_Write01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); - int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,5,0)); - int a_DataTypes = ((int) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Write'", NULL); -#endif - { - bool tolua_ret = (bool) self->Write(a_World,a_MinBlockX,a_MinBlockY,a_MinBlockZ,a_DataTypes); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -tolua_lerror: - return tolua_AllToLua_cBlockArea_Write00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CopyTo of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_CopyTo00 -static int tolua_AllToLua_cBlockArea_CopyTo00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBlockArea",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - cBlockArea* a_Into = ((cBlockArea*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyTo'", NULL); -#endif - { - self->CopyTo(*a_Into); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CopyTo'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: CopyFrom of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_CopyFrom00 -static int tolua_AllToLua_cBlockArea_CopyFrom00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - const cBlockArea* a_From = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyFrom'", NULL); -#endif - { - self->CopyFrom(*a_From); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'CopyFrom'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: DumpToRawFile of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_DumpToRawFile00 -static int tolua_AllToLua_cBlockArea_DumpToRawFile00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DumpToRawFile'", NULL); -#endif - { - self->DumpToRawFile(a_FileName); - tolua_pushcppstring(tolua_S,(const char*)a_FileName); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'DumpToRawFile'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: LoadFromSchematicFile of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_LoadFromSchematicFile00 -static int tolua_AllToLua_cBlockArea_LoadFromSchematicFile00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadFromSchematicFile'", NULL); -#endif - { - bool tolua_ret = (bool) self->LoadFromSchematicFile(a_FileName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_FileName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'LoadFromSchematicFile'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SaveToSchematicFile of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SaveToSchematicFile00 -static int tolua_AllToLua_cBlockArea_SaveToSchematicFile00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveToSchematicFile'", NULL); -#endif - { - bool tolua_ret = (bool) self->SaveToSchematicFile(a_FileName); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_FileName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SaveToSchematicFile'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Crop of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Crop00 -static int tolua_AllToLua_cBlockArea_Crop00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_AddMinX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_SubMaxX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_AddMinY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_SubMaxY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_AddMinZ = ((int) tolua_tonumber(tolua_S,6,0)); - int a_SubMaxZ = ((int) tolua_tonumber(tolua_S,7,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Crop'", NULL); -#endif - { - self->Crop(a_AddMinX,a_SubMaxX,a_AddMinY,a_SubMaxY,a_AddMinZ,a_SubMaxZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Crop'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Expand of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Expand00 -static int tolua_AllToLua_cBlockArea_Expand00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnoobj(tolua_S,8,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_SubMinX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_AddMaxX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_SubMinY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_AddMaxY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_SubMinZ = ((int) tolua_tonumber(tolua_S,6,0)); - int a_AddMaxZ = ((int) tolua_tonumber(tolua_S,7,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Expand'", NULL); -#endif - { - self->Expand(a_SubMinX,a_AddMaxX,a_SubMinY,a_AddMaxY,a_SubMinZ,a_AddMaxZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Expand'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Merge of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Merge00 -static int tolua_AllToLua_cBlockArea_Merge00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - const cBlockArea* a_Src = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0)); - int a_RelX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,5,0)); - cBlockArea::eMergeStrategy a_Strategy = ((cBlockArea::eMergeStrategy) (int) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Merge'", NULL); -#endif - { - self->Merge(*a_Src,a_RelX,a_RelY,a_RelZ,a_Strategy); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Merge'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Fill of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Fill00 -static int tolua_AllToLua_cBlockArea_Fill00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,1,&tolua_err) || - !tolua_isnumber(tolua_S,5,1,&tolua_err) || - !tolua_isnumber(tolua_S,6,1,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_DataTypes = ((int) tolua_tonumber(tolua_S,2,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,6,0x0f)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Fill'", NULL); -#endif - { - self->Fill(a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Fill'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FillRelCuboid of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_FillRelCuboid00 -static int tolua_AllToLua_cBlockArea_FillRelCuboid00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnumber(tolua_S,9,0,&tolua_err) || - !tolua_isnumber(tolua_S,10,1,&tolua_err) || - !tolua_isnumber(tolua_S,11,1,&tolua_err) || - !tolua_isnumber(tolua_S,12,1,&tolua_err) || - !tolua_isnoobj(tolua_S,13,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_MinRelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MaxRelX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MinRelY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MaxRelY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_MinRelZ = ((int) tolua_tonumber(tolua_S,6,0)); - int a_MaxRelZ = ((int) tolua_tonumber(tolua_S,7,0)); - int a_DataTypes = ((int) tolua_tonumber(tolua_S,8,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,9,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,10,0)); - unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,11,0)); - unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,12,0x0f)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL); -#endif - { - self->FillRelCuboid(a_MinRelX,a_MaxRelX,a_MinRelY,a_MaxRelY,a_MinRelZ,a_MaxRelZ,a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FillRelCuboid'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RelLine of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RelLine00 -static int tolua_AllToLua_cBlockArea_RelLine00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnumber(tolua_S,9,0,&tolua_err) || - !tolua_isnumber(tolua_S,10,1,&tolua_err) || - !tolua_isnumber(tolua_S,11,1,&tolua_err) || - !tolua_isnumber(tolua_S,12,1,&tolua_err) || - !tolua_isnoobj(tolua_S,13,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX1 = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY1 = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ1 = ((int) tolua_tonumber(tolua_S,4,0)); - int a_RelX2 = ((int) tolua_tonumber(tolua_S,5,0)); - int a_RelY2 = ((int) tolua_tonumber(tolua_S,6,0)); - int a_RelZ2 = ((int) tolua_tonumber(tolua_S,7,0)); - int a_DataTypes = ((int) tolua_tonumber(tolua_S,8,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,9,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,10,0)); - unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,11,0)); - unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,12,0x0f)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RelLine'", NULL); -#endif - { - self->RelLine(a_RelX1,a_RelY1,a_RelZ1,a_RelX2,a_RelY2,a_RelZ2,a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RelLine'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RotateCCW of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCCW00 -static int tolua_AllToLua_cBlockArea_RotateCCW00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCCW'", NULL); -#endif - { - self->RotateCCW(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RotateCCW'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RotateCW of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCW00 -static int tolua_AllToLua_cBlockArea_RotateCW00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCW'", NULL); -#endif - { - self->RotateCW(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RotateCW'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MirrorXY of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXY00 -static int tolua_AllToLua_cBlockArea_MirrorXY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXY'", NULL); -#endif - { - self->MirrorXY(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MirrorXY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MirrorXZ of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXZ00 -static int tolua_AllToLua_cBlockArea_MirrorXZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXZ'", NULL); -#endif - { - self->MirrorXZ(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MirrorXZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MirrorYZ of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorYZ00 -static int tolua_AllToLua_cBlockArea_MirrorYZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorYZ'", NULL); -#endif - { - self->MirrorYZ(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MirrorYZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RotateCCWNoMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCCWNoMeta00 -static int tolua_AllToLua_cBlockArea_RotateCCWNoMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCCWNoMeta'", NULL); -#endif - { - self->RotateCCWNoMeta(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RotateCCWNoMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RotateCWNoMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCWNoMeta00 -static int tolua_AllToLua_cBlockArea_RotateCWNoMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCWNoMeta'", NULL); -#endif - { - self->RotateCWNoMeta(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RotateCWNoMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MirrorXYNoMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXYNoMeta00 -static int tolua_AllToLua_cBlockArea_MirrorXYNoMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXYNoMeta'", NULL); -#endif - { - self->MirrorXYNoMeta(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MirrorXYNoMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MirrorXZNoMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXZNoMeta00 -static int tolua_AllToLua_cBlockArea_MirrorXZNoMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXZNoMeta'", NULL); -#endif - { - self->MirrorXZNoMeta(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MirrorXZNoMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MirrorYZNoMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorYZNoMeta00 -static int tolua_AllToLua_cBlockArea_MirrorYZNoMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorYZNoMeta'", NULL); -#endif - { - self->MirrorYZNoMeta(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MirrorYZNoMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRelBlockType of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockType00 -static int tolua_AllToLua_cBlockArea_SetRelBlockType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockType'", NULL); -#endif - { - self->SetRelBlockType(a_RelX,a_RelY,a_RelZ,a_BlockType); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRelBlockType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockType of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockType00 -static int tolua_AllToLua_cBlockArea_SetBlockType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockType'", NULL); -#endif - { - self->SetBlockType(a_BlockX,a_BlockY,a_BlockZ,a_BlockType); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlockType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRelBlockMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockMeta00 -static int tolua_AllToLua_cBlockArea_SetRelBlockMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockMeta'", NULL); -#endif - { - self->SetRelBlockMeta(a_RelX,a_RelY,a_RelZ,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRelBlockMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockMeta00 -static int tolua_AllToLua_cBlockArea_SetBlockMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); -#endif - { - self->SetBlockMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRelBlockLight of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockLight00 -static int tolua_AllToLua_cBlockArea_SetRelBlockLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockLight'", NULL); -#endif - { - self->SetRelBlockLight(a_RelX,a_RelY,a_RelZ,a_BlockLight); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRelBlockLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockLight of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockLight00 -static int tolua_AllToLua_cBlockArea_SetBlockLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockLight'", NULL); -#endif - { - self->SetBlockLight(a_BlockX,a_BlockY,a_BlockZ,a_BlockLight); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlockLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRelBlockSkyLight of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00 -static int tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockSkyLight'", NULL); -#endif - { - self->SetRelBlockSkyLight(a_RelX,a_RelY,a_RelZ,a_BlockSkyLight); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRelBlockSkyLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockSkyLight of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockSkyLight00 -static int tolua_AllToLua_cBlockArea_SetBlockSkyLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockSkyLight'", NULL); -#endif - { - self->SetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ,a_BlockSkyLight); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlockSkyLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRelBlockType of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockType00 -static int tolua_AllToLua_cBlockArea_GetRelBlockType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockType'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetRelBlockType(a_RelX,a_RelY,a_RelZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRelBlockType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockType of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockType00 -static int tolua_AllToLua_cBlockArea_GetBlockType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockType(a_BlockX,a_BlockY,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRelBlockMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockMeta00 -static int tolua_AllToLua_cBlockArea_GetRelBlockMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockMeta'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetRelBlockMeta(a_RelX,a_RelY,a_RelZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRelBlockMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockMeta00 -static int tolua_AllToLua_cBlockArea_GetBlockMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_BlockX,a_BlockY,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRelBlockLight of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockLight00 -static int tolua_AllToLua_cBlockArea_GetRelBlockLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockLight'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetRelBlockLight(a_RelX,a_RelY,a_RelZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRelBlockLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockLight of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockLight00 -static int tolua_AllToLua_cBlockArea_GetBlockLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockLight'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockLight(a_BlockX,a_BlockY,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRelBlockSkyLight of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00 -static int tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockSkyLight'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetRelBlockSkyLight(a_RelX,a_RelY,a_RelZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRelBlockSkyLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockSkyLight of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockSkyLight00 -static int tolua_AllToLua_cBlockArea_GetBlockSkyLight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockSkyLight'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockSkyLight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockTypeMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockTypeMeta00 -static int tolua_AllToLua_cBlockArea_SetBlockTypeMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockTypeMeta'", NULL); -#endif - { - self->SetBlockTypeMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlockTypeMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetRelBlockTypeMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00 -static int tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockTypeMeta'", NULL); -#endif - { - self->SetRelBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetRelBlockTypeMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockTypeMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockTypeMeta00 -static int tolua_AllToLua_cBlockArea_GetBlockTypeMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockTypeMeta'", NULL); -#endif - { - self->GetBlockTypeMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockTypeMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetRelBlockTypeMeta of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00 -static int tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockTypeMeta'", NULL); -#endif - { - self->GetRelBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetRelBlockTypeMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSizeX of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeX00 -static int tolua_AllToLua_cBlockArea_GetSizeX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeX'", NULL); -#endif - { - int tolua_ret = (int) self->GetSizeX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSizeX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSizeY of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeY00 -static int tolua_AllToLua_cBlockArea_GetSizeY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeY'", NULL); -#endif - { - int tolua_ret = (int) self->GetSizeY(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSizeY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSizeZ of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeZ00 -static int tolua_AllToLua_cBlockArea_GetSizeZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeZ'", NULL); -#endif - { - int tolua_ret = (int) self->GetSizeZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSizeZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetOriginX of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginX00 -static int tolua_AllToLua_cBlockArea_GetOriginX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginX'", NULL); -#endif - { - int tolua_ret = (int) self->GetOriginX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetOriginX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetOriginY of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginY00 -static int tolua_AllToLua_cBlockArea_GetOriginY00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginY'", NULL); -#endif - { - int tolua_ret = (int) self->GetOriginY(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetOriginY'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetOriginZ of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginZ00 -static int tolua_AllToLua_cBlockArea_GetOriginZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginZ'", NULL); -#endif - { - int tolua_ret = (int) self->GetOriginZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetOriginZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetDataTypes of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetDataTypes00 -static int tolua_AllToLua_cBlockArea_GetDataTypes00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDataTypes'", NULL); -#endif - { - int tolua_ret = (int) self->GetDataTypes(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetDataTypes'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HasBlockTypes of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockTypes00 -static int tolua_AllToLua_cBlockArea_HasBlockTypes00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockTypes'", NULL); -#endif - { - bool tolua_ret = (bool) self->HasBlockTypes(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HasBlockTypes'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HasBlockMetas of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockMetas00 -static int tolua_AllToLua_cBlockArea_HasBlockMetas00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockMetas'", NULL); -#endif - { - bool tolua_ret = (bool) self->HasBlockMetas(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HasBlockMetas'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HasBlockLights of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockLights00 -static int tolua_AllToLua_cBlockArea_HasBlockLights00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockLights'", NULL); -#endif - { - bool tolua_ret = (bool) self->HasBlockLights(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HasBlockLights'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: HasBlockSkyLights of class cBlockArea */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockSkyLights00 -static int tolua_AllToLua_cBlockArea_HasBlockSkyLights00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockSkyLights'", NULL); -#endif - { - bool tolua_ret = (bool) self->HasBlockSkyLights(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'HasBlockSkyLights'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetChunkX of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetChunkX00 -static int tolua_AllToLua_cChunkDesc_GetChunkX00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL); -#endif - { - int tolua_ret = (int) self->GetChunkX(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetChunkZ of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetChunkZ00 -static int tolua_AllToLua_cChunkDesc_GetChunkZ00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL); -#endif - { - int tolua_ret = (int) self->GetChunkZ(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FillBlocks of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillBlocks00 -static int tolua_AllToLua_cChunkDesc_FillBlocks00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,2,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillBlocks'", NULL); -#endif - { - self->FillBlocks(a_BlockType,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FillBlocks'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockTypeMeta of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00 -static int tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockTypeMeta'", NULL); -#endif - { - self->SetBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlockTypeMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockTypeMeta of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00 -static int tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockTypeMeta'", NULL); -#endif - { - self->GetBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); - tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockTypeMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockType of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockType00 -static int tolua_AllToLua_cChunkDesc_SetBlockType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockType'", NULL); -#endif - { - self->SetBlockType(a_RelX,a_RelY,a_RelZ,a_BlockType); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlockType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockType of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockType00 -static int tolua_AllToLua_cChunkDesc_GetBlockType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockType(a_RelX,a_RelY,a_RelZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBlockMeta of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockMeta00 -static int tolua_AllToLua_cChunkDesc_SetBlockMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); -#endif - { - self->SetBlockMeta(a_RelX,a_RelY,a_RelZ,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockMeta of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockMeta00 -static int tolua_AllToLua_cChunkDesc_GetBlockMeta00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_RelX,a_RelY,a_RelZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetBiome of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBiome00 -static int tolua_AllToLua_cChunkDesc_SetBiome00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); - int a_BiomeID = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBiome'", NULL); -#endif - { - self->SetBiome(a_RelX,a_RelZ,a_BiomeID); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetBiome'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBiome of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBiome00 -static int tolua_AllToLua_cChunkDesc_GetBiome00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBiome'", NULL); -#endif - { - EMCSBiome tolua_ret = (EMCSBiome) self->GetBiome(a_RelX,a_RelZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBiome'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetHeight of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetHeight00 -static int tolua_AllToLua_cChunkDesc_SetHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); - int a_Height = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeight'", NULL); -#endif - { - self->SetHeight(a_RelX,a_RelZ,a_Height); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHeight of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetHeight00 -static int tolua_AllToLua_cChunkDesc_GetHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); -#endif - { - int tolua_ret = (int) self->GetHeight(a_RelX,a_RelZ); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetUseDefaultBiomes of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00 -static int tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - bool a_bUseDefaultBiomes = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultBiomes'", NULL); -#endif - { - self->SetUseDefaultBiomes(a_bUseDefaultBiomes); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetUseDefaultBiomes'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsUsingDefaultBiomes of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00 -static int tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultBiomes'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsUsingDefaultBiomes(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultBiomes'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetUseDefaultHeight of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00 -static int tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - bool a_bUseDefaultHeight = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultHeight'", NULL); -#endif - { - self->SetUseDefaultHeight(a_bUseDefaultHeight); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetUseDefaultHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsUsingDefaultHeight of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00 -static int tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultHeight'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsUsingDefaultHeight(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetUseDefaultComposition of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00 -static int tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - bool a_bUseDefaultComposition = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultComposition'", NULL); -#endif - { - self->SetUseDefaultComposition(a_bUseDefaultComposition); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetUseDefaultComposition'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsUsingDefaultComposition of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00 -static int tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultComposition'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsUsingDefaultComposition(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultComposition'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetUseDefaultStructures of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00 -static int tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - bool a_bUseDefaultStructures = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultStructures'", NULL); -#endif - { - self->SetUseDefaultStructures(a_bUseDefaultStructures); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetUseDefaultStructures'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsUsingDefaultStructures of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00 -static int tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultStructures'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsUsingDefaultStructures(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultStructures'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetUseDefaultFinish of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00 -static int tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isboolean(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - bool a_bUseDefaultFinish = ((bool) tolua_toboolean(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultFinish'", NULL); -#endif - { - self->SetUseDefaultFinish(a_bUseDefaultFinish); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetUseDefaultFinish'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsUsingDefaultFinish of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00 -static int tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultFinish'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsUsingDefaultFinish(); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultFinish'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: WriteBlockArea of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_WriteBlockArea00 -static int tolua_AllToLua_cChunkDesc_WriteBlockArea00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,1,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - const cBlockArea* a_BlockArea = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0)); - int a_RelX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,5,0)); - cBlockArea::eMergeStrategy a_MergeStrategy = ((cBlockArea::eMergeStrategy) (int) tolua_tonumber(tolua_S,6,cBlockArea::msOverwrite)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteBlockArea'", NULL); -#endif - { - self->WriteBlockArea(*a_BlockArea,a_RelX,a_RelY,a_RelZ,a_MergeStrategy); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'WriteBlockArea'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ReadBlockArea of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReadBlockArea00 -static int tolua_AllToLua_cChunkDesc_ReadBlockArea00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBlockArea",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnoobj(tolua_S,9,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - cBlockArea* a_Dest = ((cBlockArea*) tolua_tousertype(tolua_S,2,0)); - int a_MinRelX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MaxRelX = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MinRelY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_MaxRelY = ((int) tolua_tonumber(tolua_S,6,0)); - int a_MinRelZ = ((int) tolua_tonumber(tolua_S,7,0)); - int a_MaxRelZ = ((int) tolua_tonumber(tolua_S,8,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadBlockArea'", NULL); -#endif - { - self->ReadBlockArea(*a_Dest,a_MinRelX,a_MaxRelX,a_MinRelY,a_MaxRelY,a_MinRelZ,a_MaxRelZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ReadBlockArea'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMaxHeight of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetMaxHeight00 -static int tolua_AllToLua_cChunkDesc_GetMaxHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxHeight'", NULL); -#endif - { - unsigned char tolua_ret = ( unsigned char) self->GetMaxHeight(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMaxHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FillRelCuboid of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillRelCuboid00 -static int tolua_AllToLua_cChunkDesc_FillRelCuboid00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnumber(tolua_S,9,0,&tolua_err) || - !tolua_isnoobj(tolua_S,10,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); - int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL); -#endif - { - self->FillRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_BlockType,a_BlockMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FillRelCuboid'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FillRelCuboid of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillRelCuboid01 -static int tolua_AllToLua_cChunkDesc_FillRelCuboid01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL); -#endif - { - self->FillRelCuboid(*a_RelCuboid,a_BlockType,a_BlockMeta); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cChunkDesc_FillRelCuboid00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ReplaceRelCuboid of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00 -static int tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnumber(tolua_S,9,0,&tolua_err) || - !tolua_isnumber(tolua_S,10,0,&tolua_err) || - !tolua_isnumber(tolua_S,11,0,&tolua_err) || - !tolua_isnoobj(tolua_S,12,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); - int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); - unsigned char a_SrcType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); - unsigned char a_SrcMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); - unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,10,0)); - unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,11,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReplaceRelCuboid'", NULL); -#endif - { - self->ReplaceRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_SrcType,a_SrcMeta,a_DstType,a_DstMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ReplaceRelCuboid'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ReplaceRelCuboid of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01 -static int tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); - unsigned char a_SrcType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); - unsigned char a_SrcMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); - unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); - unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReplaceRelCuboid'", NULL); -#endif - { - self->ReplaceRelCuboid(*a_RelCuboid,a_SrcType,a_SrcMeta,a_DstType,a_DstMeta); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FloorRelCuboid of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FloorRelCuboid00 -static int tolua_AllToLua_cChunkDesc_FloorRelCuboid00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnumber(tolua_S,9,0,&tolua_err) || - !tolua_isnoobj(tolua_S,10,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); - int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); - unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); - unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FloorRelCuboid'", NULL); -#endif - { - self->FloorRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_DstType,a_DstMeta); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FloorRelCuboid'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FloorRelCuboid of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FloorRelCuboid01 -static int tolua_AllToLua_cChunkDesc_FloorRelCuboid01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); - unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); - unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FloorRelCuboid'", NULL); -#endif - { - self->FloorRelCuboid(*a_RelCuboid,a_DstType,a_DstMeta); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cChunkDesc_FloorRelCuboid00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RandomFillRelCuboid of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00 -static int tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnumber(tolua_S,7,0,&tolua_err) || - !tolua_isnumber(tolua_S,8,0,&tolua_err) || - !tolua_isnumber(tolua_S,9,0,&tolua_err) || - !tolua_isnumber(tolua_S,10,0,&tolua_err) || - !tolua_isnumber(tolua_S,11,0,&tolua_err) || - !tolua_isnoobj(tolua_S,12,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); - int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); - int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); - int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); - int a_RandomSeed = ((int) tolua_tonumber(tolua_S,10,0)); - int a_ChanceOutOf10k = ((int) tolua_tonumber(tolua_S,11,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RandomFillRelCuboid'", NULL); -#endif - { - self->RandomFillRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_BlockType,a_BlockMeta,a_RandomSeed,a_ChanceOutOf10k); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'RandomFillRelCuboid'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: RandomFillRelCuboid of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01 -static int tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); - unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); - unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); - int a_RandomSeed = ((int) tolua_tonumber(tolua_S,5,0)); - int a_ChanceOutOf10k = ((int) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RandomFillRelCuboid'", NULL); -#endif - { - self->RandomFillRelCuboid(*a_RelCuboid,a_BlockType,a_BlockMeta,a_RandomSeed,a_ChanceOutOf10k); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetBlockEntity of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockEntity00 -static int tolua_AllToLua_cChunkDesc_GetBlockEntity00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockEntity'", NULL); -#endif - { - cBlockEntity* tolua_ret = (cBlockEntity*) self->GetBlockEntity(a_RelX,a_RelY,a_RelZ); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockEntity"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetBlockEntity'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_new00 -static int tolua_AllToLua_cCraftingGrid_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCraftingGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - int a_Width = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Height = ((int) tolua_tonumber(tolua_S,3,0)); - { - cCraftingGrid* tolua_ret = (cCraftingGrid*) Mtolua_new((cCraftingGrid)(a_Width,a_Height)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingGrid"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_new00_local -static int tolua_AllToLua_cCraftingGrid_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cCraftingGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - int a_Width = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Height = ((int) tolua_tonumber(tolua_S,3,0)); - { - cCraftingGrid* tolua_ret = (cCraftingGrid*) Mtolua_new((cCraftingGrid)(a_Width,a_Height)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingGrid"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWidth of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetWidth00 -static int tolua_AllToLua_cCraftingGrid_GetWidth00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL); -#endif - { - int tolua_ret = (int) self->GetWidth(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetHeight of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetHeight00 -static int tolua_AllToLua_cCraftingGrid_GetHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); -#endif - { - int tolua_ret = (int) self->GetHeight(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetItem of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetItem00 -static int tolua_AllToLua_cCraftingGrid_GetItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0); - int x = ((int) tolua_tonumber(tolua_S,2,0)); - int y = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetItem'", NULL); -#endif - { - cItem& tolua_ret = (cItem&) self->GetItem(x,y); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetItem of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_SetItem00 -static int tolua_AllToLua_cCraftingGrid_SetItem00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); - int x = ((int) tolua_tonumber(tolua_S,2,0)); - int y = ((int) tolua_tonumber(tolua_S,3,0)); - ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,4,0)); - int a_ItemCount = ((int) tolua_tonumber(tolua_S,5,0)); - short a_ItemHealth = ((short) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetItem'", NULL); -#endif - { - self->SetItem(x,y,a_ItemType,a_ItemCount,a_ItemHealth); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetItem'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetItem of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_SetItem01 -static int tolua_AllToLua_cCraftingGrid_SetItem01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); - int x = ((int) tolua_tonumber(tolua_S,2,0)); - int y = ((int) tolua_tonumber(tolua_S,3,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetItem'", NULL); -#endif - { - self->SetItem(x,y,*a_Item); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cCraftingGrid_SetItem00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Clear of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_Clear00 -static int tolua_AllToLua_cCraftingGrid_Clear00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); -#endif - { - self->Clear(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ConsumeGrid of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_ConsumeGrid00 -static int tolua_AllToLua_cCraftingGrid_ConsumeGrid00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCraftingGrid",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); - const cCraftingGrid* a_Grid = ((const cCraftingGrid*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ConsumeGrid'", NULL); -#endif - { - self->ConsumeGrid(*a_Grid); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ConsumeGrid'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Dump of class cCraftingGrid */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_Dump00 -static int tolua_AllToLua_cCraftingGrid_Dump00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dump'", NULL); -#endif - { - self->Dump(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Dump'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Clear of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_Clear00 -static int tolua_AllToLua_cCraftingRecipe_Clear00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); -#endif - { - self->Clear(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetIngredientsWidth of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00 -static int tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredientsWidth'", NULL); -#endif - { - int tolua_ret = (int) self->GetIngredientsWidth(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetIngredientsWidth'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetIngredientsHeight of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00 -static int tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredientsHeight'", NULL); -#endif - { - int tolua_ret = (int) self->GetIngredientsHeight(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetIngredientsHeight'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetIngredient of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredient00 -static int tolua_AllToLua_cCraftingRecipe_GetIngredient00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); - int x = ((int) tolua_tonumber(tolua_S,2,0)); - int y = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredient'", NULL); -#endif - { - cItem& tolua_ret = (cItem&) self->GetIngredient(x,y); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetIngredient'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetResult of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetResult00 -static int tolua_AllToLua_cCraftingRecipe_GetResult00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetResult'", NULL); -#endif - { - const cItem& tolua_ret = (const cItem&) self->GetResult(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetResult'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetResult of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetResult00 -static int tolua_AllToLua_cCraftingRecipe_SetResult00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); - ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,2,0)); - int a_ItemCount = ((int) tolua_tonumber(tolua_S,3,0)); - short a_ItemHealth = ((short) tolua_tonumber(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetResult'", NULL); -#endif - { - self->SetResult(a_ItemType,a_ItemCount,a_ItemHealth); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetResult'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetResult of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetResult01 -static int tolua_AllToLua_cCraftingRecipe_SetResult01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else - { - cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetResult'", NULL); -#endif - { - self->SetResult(*a_Item); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cCraftingRecipe_SetResult00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetIngredient of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetIngredient00 -static int tolua_AllToLua_cCraftingRecipe_SetIngredient00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_isnumber(tolua_S,5,0,&tolua_err) || - !tolua_isnumber(tolua_S,6,0,&tolua_err) || - !tolua_isnoobj(tolua_S,7,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); - int x = ((int) tolua_tonumber(tolua_S,2,0)); - int y = ((int) tolua_tonumber(tolua_S,3,0)); - ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,4,0)); - int a_ItemCount = ((int) tolua_tonumber(tolua_S,5,0)); - short a_ItemHealth = ((short) tolua_tonumber(tolua_S,6,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIngredient'", NULL); -#endif - { - self->SetIngredient(x,y,a_ItemType,a_ItemCount,a_ItemHealth); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetIngredient'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetIngredient of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetIngredient01 -static int tolua_AllToLua_cCraftingRecipe_SetIngredient01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); - int x = ((int) tolua_tonumber(tolua_S,2,0)); - int y = ((int) tolua_tonumber(tolua_S,3,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIngredient'", NULL); -#endif - { - self->SetIngredient(x,y,*a_Item); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cCraftingRecipe_SetIngredient00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: ConsumeIngredients of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00 -static int tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cCraftingGrid",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); - cCraftingGrid* a_CraftingGrid = ((cCraftingGrid*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ConsumeIngredients'", NULL); -#endif - { - self->ConsumeIngredients(*a_CraftingGrid); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ConsumeIngredients'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: Dump of class cCraftingRecipe */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_Dump00 -static int tolua_AllToLua_cCraftingRecipe_Dump00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dump'", NULL); -#endif - { - self->Dump(); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'Dump'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWindowID of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowID00 -static int tolua_AllToLua_cWindow_GetWindowID00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowID'", NULL); -#endif - { - char tolua_ret = (char) self->GetWindowID(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWindowID'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWindowType of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowType00 -static int tolua_AllToLua_cWindow_GetWindowType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowType'", NULL); -#endif - { - int tolua_ret = (int) self->GetWindowType(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWindowType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSlot of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetSlot00 -static int tolua_AllToLua_cWindow_GetSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); - cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); -#endif - { - const cItem* tolua_ret = (const cItem*) self->GetSlot(*a_Player,a_SlotNum); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"const cItem"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetSlot of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetSlot00 -static int tolua_AllToLua_cWindow_SetSlot00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err)) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); - cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,3,0)); - const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); -#endif - { - self->SetSlot(*a_Player,a_SlotNum,*a_Item); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSlotInPlayerMainInventory of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00 -static int tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerMainInventory'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSlotInPlayerMainInventory(a_SlotNum); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerMainInventory'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSlotInPlayerHotbar of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00 -static int tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerHotbar'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSlotInPlayerHotbar(a_SlotNum); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerHotbar'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: IsSlotInPlayerInventory of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerInventory00 -static int tolua_AllToLua_cWindow_IsSlotInPlayerInventory00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); - int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerInventory'", NULL); -#endif - { - bool tolua_ret = (bool) self->IsSlotInPlayerInventory(a_SlotNum); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerInventory'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetWindowTitle of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowTitle00 -static int tolua_AllToLua_cWindow_GetWindowTitle00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowTitle'", NULL); -#endif - { - const AString tolua_ret = (const AString) self->GetWindowTitle(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetWindowTitle'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetWindowTitle of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetWindowTitle00 -static int tolua_AllToLua_cWindow_SetWindowTitle00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); - const AString a_WindowTitle = ((const AString) tolua_tocppstring(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWindowTitle'", NULL); -#endif - { - self->SetWindowTitle(a_WindowTitle); - tolua_pushcppstring(tolua_S,(const char*)a_WindowTitle); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetWindowTitle'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetProperty of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetProperty00 -static int tolua_AllToLua_cWindow_SetProperty00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); - int a_Property = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Value = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetProperty'", NULL); -#endif - { - self->SetProperty(a_Property,a_Value); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetProperty'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: SetProperty of class cWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetProperty01 -static int tolua_AllToLua_cWindow_SetProperty01(lua_State* tolua_S) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"cPlayer",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,5,&tolua_err) - ) - goto tolua_lerror; - else - { - cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); - int a_Property = ((int) tolua_tonumber(tolua_S,2,0)); - int a_Value = ((int) tolua_tonumber(tolua_S,3,0)); - cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,4,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetProperty'", NULL); -#endif - { - self->SetProperty(a_Property,a_Value,*a_Player); - } - } - return 0; -tolua_lerror: - return tolua_AllToLua_cWindow_SetProperty00(tolua_S); -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new of class cLuaWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_new00 -static int tolua_AllToLua_cLuaWindow_new00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cLuaWindow",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_iscppstring(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWindow::WindowType a_WindowType = ((cWindow::WindowType) (int) tolua_tonumber(tolua_S,2,0)); - int a_SlotsX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_SlotsY = ((int) tolua_tonumber(tolua_S,4,0)); - const AString a_Title = ((const AString) tolua_tocppstring(tolua_S,5,0)); - { - cLuaWindow* tolua_ret = (cLuaWindow*) Mtolua_new((cLuaWindow)(a_WindowType,a_SlotsX,a_SlotsY,a_Title)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cLuaWindow"); - tolua_pushcppstring(tolua_S,(const char*)a_Title); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: new_local of class cLuaWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_new00_local -static int tolua_AllToLua_cLuaWindow_new00_local(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cLuaWindow",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnumber(tolua_S,4,0,&tolua_err) || - !tolua_iscppstring(tolua_S,5,0,&tolua_err) || - !tolua_isnoobj(tolua_S,6,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cWindow::WindowType a_WindowType = ((cWindow::WindowType) (int) tolua_tonumber(tolua_S,2,0)); - int a_SlotsX = ((int) tolua_tonumber(tolua_S,3,0)); - int a_SlotsY = ((int) tolua_tonumber(tolua_S,4,0)); - const AString a_Title = ((const AString) tolua_tocppstring(tolua_S,5,0)); - { - cLuaWindow* tolua_ret = (cLuaWindow*) Mtolua_new((cLuaWindow)(a_WindowType,a_SlotsX,a_SlotsY,a_Title)); - tolua_pushusertype(tolua_S,(void*)tolua_ret,"cLuaWindow"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); - tolua_pushcppstring(tolua_S,(const char*)a_Title); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: delete of class cLuaWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_delete00 -static int tolua_AllToLua_cLuaWindow_delete00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cLuaWindow",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL); -#endif - Mtolua_delete(self); - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetContents of class cLuaWindow */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_GetContents00 -static int tolua_AllToLua_cLuaWindow_GetContents00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cLuaWindow",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetContents'", NULL); -#endif - { - cItemGrid& tolua_ret = (cItemGrid&) self->GetContents(); - tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetContents'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMobType of class cMonster */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobType00 -static int tolua_AllToLua_cMonster_GetMobType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobType'", NULL); -#endif - { - cMonster::eType tolua_ret = (cMonster::eType) self->GetMobType(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMobType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetMobFamily of class cMonster */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobFamily00 -static int tolua_AllToLua_cMonster_GetMobFamily00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobFamily'", NULL); -#endif - { - cMonster::eFamily tolua_ret = (cMonster::eFamily) self->GetMobFamily(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMobFamily'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: MobTypeToString of class cMonster */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_MobTypeToString00 -static int tolua_AllToLua_cMonster_MobTypeToString00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0)); - { - AString tolua_ret = (AString) cMonster::MobTypeToString(a_MobType); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'MobTypeToString'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: StringToMobType of class cMonster */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_StringToMobType00 -static int tolua_AllToLua_cMonster_StringToMobType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || - !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - const AString a_MobTypeName = ((const AString) tolua_tocppstring(tolua_S,2,0)); - { - cMonster::eType tolua_ret = (cMonster::eType) cMonster::StringToMobType(a_MobTypeName); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - tolua_pushcppstring(tolua_S,(const char*)a_MobTypeName); - } - } - return 2; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: FamilyFromType of class cMonster */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_FamilyFromType00 -static int tolua_AllToLua_cMonster_FamilyFromType00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0)); - { - cMonster::eFamily tolua_ret = (cMonster::eFamily) cMonster::FamilyFromType(a_MobType); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'FamilyFromType'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* method: GetSpawnDelay of class cMonster */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetSpawnDelay00 -static int tolua_AllToLua_cMonster_GetSpawnDelay00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cMonster::eFamily a_MobFamily = ((cMonster::eFamily) (int) tolua_tonumber(tolua_S,2,0)); - { - int tolua_ret = (int) cMonster::GetSpawnDelay(a_MobFamily); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetSpawnDelay'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - -/* Open function */ -TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) -{ - tolua_open(tolua_S); - tolua_reg_types(tolua_S); - tolua_module(tolua_S,NULL,1); - tolua_beginmodule(tolua_S,NULL); - tolua_constant(tolua_S,"biOcean",biOcean); - tolua_constant(tolua_S,"biPlains",biPlains); - tolua_constant(tolua_S,"biDesert",biDesert); - tolua_constant(tolua_S,"biExtremeHills",biExtremeHills); - tolua_constant(tolua_S,"biForest",biForest); - tolua_constant(tolua_S,"biTaiga",biTaiga); - tolua_constant(tolua_S,"biSwampland",biSwampland); - tolua_constant(tolua_S,"biRiver",biRiver); - tolua_constant(tolua_S,"biHell",biHell); - tolua_constant(tolua_S,"biNether",biNether); - tolua_constant(tolua_S,"biSky",biSky); - tolua_constant(tolua_S,"biEnd",biEnd); - tolua_constant(tolua_S,"biFrozenOcean",biFrozenOcean); - tolua_constant(tolua_S,"biFrozenRiver",biFrozenRiver); - tolua_constant(tolua_S,"biIcePlains",biIcePlains); - tolua_constant(tolua_S,"biTundra",biTundra); - tolua_constant(tolua_S,"biIceMountains",biIceMountains); - tolua_constant(tolua_S,"biMushroomIsland",biMushroomIsland); - tolua_constant(tolua_S,"biMushroomShore",biMushroomShore); - tolua_constant(tolua_S,"biBeach",biBeach); - tolua_constant(tolua_S,"biDesertHills",biDesertHills); - tolua_constant(tolua_S,"biForestHills",biForestHills); - tolua_constant(tolua_S,"biTaigaHills",biTaigaHills); - tolua_constant(tolua_S,"biExtremeHillsEdge",biExtremeHillsEdge); - tolua_constant(tolua_S,"biJungle",biJungle); - tolua_constant(tolua_S,"biJungleHills",biJungleHills); - tolua_constant(tolua_S,"biJungleEdge",biJungleEdge); - tolua_constant(tolua_S,"biDeepOcean",biDeepOcean); - tolua_constant(tolua_S,"biStoneBeach",biStoneBeach); - tolua_constant(tolua_S,"biColdBeach",biColdBeach); - tolua_constant(tolua_S,"biBirchForest",biBirchForest); - tolua_constant(tolua_S,"biBirchForestHills",biBirchForestHills); - tolua_constant(tolua_S,"biRoofedForest",biRoofedForest); - tolua_constant(tolua_S,"biColdTaiga",biColdTaiga); - tolua_constant(tolua_S,"biColdTaigaHills",biColdTaigaHills); - tolua_constant(tolua_S,"biMegaTaiga",biMegaTaiga); - tolua_constant(tolua_S,"biMegaTaigaHills",biMegaTaigaHills); - tolua_constant(tolua_S,"biExtremeHillsPlus",biExtremeHillsPlus); - tolua_constant(tolua_S,"biSavanna",biSavanna); - tolua_constant(tolua_S,"biSavannaPlateau",biSavannaPlateau); - tolua_constant(tolua_S,"biMesa",biMesa); - tolua_constant(tolua_S,"biMesaPlateauF",biMesaPlateauF); - tolua_constant(tolua_S,"biMesaPlateau",biMesaPlateau); - tolua_constant(tolua_S,"biNumBiomes",biNumBiomes); - tolua_constant(tolua_S,"biMaxBiome",biMaxBiome); - tolua_constant(tolua_S,"biVariant",biVariant); - tolua_constant(tolua_S,"biSunflowerPlains",biSunflowerPlains); - tolua_constant(tolua_S,"biDesertM",biDesertM); - tolua_constant(tolua_S,"biExtremeHillsM",biExtremeHillsM); - tolua_constant(tolua_S,"biFlowerForest",biFlowerForest); - tolua_constant(tolua_S,"biTaigaM",biTaigaM); - tolua_constant(tolua_S,"biSwamplandM",biSwamplandM); - tolua_constant(tolua_S,"biIcePlainsSpikes",biIcePlainsSpikes); - tolua_constant(tolua_S,"biJungleM",biJungleM); - tolua_constant(tolua_S,"biJungleEdgeM",biJungleEdgeM); - tolua_constant(tolua_S,"biBirchForestM",biBirchForestM); - tolua_constant(tolua_S,"biBirchForestHillsM",biBirchForestHillsM); - tolua_constant(tolua_S,"biRoofedForestM",biRoofedForestM); - tolua_constant(tolua_S,"biColdTaigaM",biColdTaigaM); - tolua_constant(tolua_S,"biMegaSpruceTaiga",biMegaSpruceTaiga); - tolua_constant(tolua_S,"biMegaSpruceTaigaHills",biMegaSpruceTaigaHills); - tolua_constant(tolua_S,"biExtremeHillsPlusM",biExtremeHillsPlusM); - tolua_constant(tolua_S,"biSavannaM",biSavannaM); - tolua_constant(tolua_S,"biSavannaPlateauM",biSavannaPlateauM); - tolua_constant(tolua_S,"biMesaBryce",biMesaBryce); - tolua_constant(tolua_S,"biMesaPlateauFM",biMesaPlateauFM); - tolua_constant(tolua_S,"biMesaPlateauM",biMesaPlateauM); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cIniFile","cIniFile","",tolua_collect_cIniFile); - #else - tolua_cclass(tolua_S,"cIniFile","cIniFile","",NULL); - #endif - tolua_beginmodule(tolua_S,"cIniFile"); - tolua_constant(tolua_S,"noID",cIniFile::noID); - tolua_function(tolua_S,"new",tolua_AllToLua_cIniFile_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cIniFile_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cIniFile_new00_local); - tolua_function(tolua_S,"CaseSensitive",tolua_AllToLua_cIniFile_CaseSensitive00); - tolua_function(tolua_S,"CaseInsensitive",tolua_AllToLua_cIniFile_CaseInsensitive00); - tolua_function(tolua_S,"ReadFile",tolua_AllToLua_cIniFile_ReadFile00); - tolua_function(tolua_S,"WriteFile",tolua_AllToLua_cIniFile_WriteFile00); - tolua_function(tolua_S,"Clear",tolua_AllToLua_cIniFile_Clear00); - tolua_function(tolua_S,"FindKey",tolua_AllToLua_cIniFile_FindKey00); - tolua_function(tolua_S,"FindValue",tolua_AllToLua_cIniFile_FindValue00); - tolua_function(tolua_S,"GetNumKeys",tolua_AllToLua_cIniFile_GetNumKeys00); - tolua_function(tolua_S,"AddKeyName",tolua_AllToLua_cIniFile_AddKeyName00); - tolua_function(tolua_S,"GetKeyName",tolua_AllToLua_cIniFile_GetKeyName00); - tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues00); - tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues01); - tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName00); - tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName01); - tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue00); - tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue01); - tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue02); - tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue03); - tolua_function(tolua_S,"GetValueF",tolua_AllToLua_cIniFile_GetValueF00); - tolua_function(tolua_S,"GetValueI",tolua_AllToLua_cIniFile_GetValueI00); - tolua_function(tolua_S,"GetValueB",tolua_AllToLua_cIniFile_GetValueB00); - tolua_function(tolua_S,"GetValueSet",tolua_AllToLua_cIniFile_GetValueSet00); - tolua_function(tolua_S,"GetValueSet",tolua_AllToLua_cIniFile_GetValueSet01); - tolua_function(tolua_S,"GetValueSetF",tolua_AllToLua_cIniFile_GetValueSetF00); - tolua_function(tolua_S,"GetValueSetI",tolua_AllToLua_cIniFile_GetValueSetI00); - tolua_function(tolua_S,"GetValueSetB",tolua_AllToLua_cIniFile_GetValueSetB00); - tolua_function(tolua_S,"SetValue",tolua_AllToLua_cIniFile_SetValue00); - tolua_function(tolua_S,"SetValue",tolua_AllToLua_cIniFile_SetValue01); - tolua_function(tolua_S,"SetValueI",tolua_AllToLua_cIniFile_SetValueI00); - tolua_function(tolua_S,"SetValueB",tolua_AllToLua_cIniFile_SetValueB00); - tolua_function(tolua_S,"SetValueF",tolua_AllToLua_cIniFile_SetValueF00); - tolua_function(tolua_S,"DeleteValueByID",tolua_AllToLua_cIniFile_DeleteValueByID00); - tolua_function(tolua_S,"DeleteValue",tolua_AllToLua_cIniFile_DeleteValue00); - tolua_function(tolua_S,"DeleteKey",tolua_AllToLua_cIniFile_DeleteKey00); - tolua_function(tolua_S,"GetNumHeaderComments",tolua_AllToLua_cIniFile_GetNumHeaderComments00); - tolua_function(tolua_S,"AddHeaderComment",tolua_AllToLua_cIniFile_AddHeaderComment00); - tolua_function(tolua_S,"GetHeaderComment",tolua_AllToLua_cIniFile_GetHeaderComment00); - tolua_function(tolua_S,"DeleteHeaderComment",tolua_AllToLua_cIniFile_DeleteHeaderComment00); - tolua_function(tolua_S,"DeleteHeaderComments",tolua_AllToLua_cIniFile_DeleteHeaderComments00); - tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments00); - tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments01); - tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment00); - tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment01); - tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment00); - tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment01); - tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment00); - tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment01); - tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments00); - tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments01); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cFile","cFile","",NULL); - tolua_beginmodule(tolua_S,"cFile"); - tolua_function(tolua_S,"Exists",tolua_AllToLua_cFile_Exists00); - tolua_function(tolua_S,"Delete",tolua_AllToLua_cFile_Delete00); - tolua_function(tolua_S,"Rename",tolua_AllToLua_cFile_Rename00); - tolua_function(tolua_S,"Copy",tolua_AllToLua_cFile_Copy00); - tolua_function(tolua_S,"IsFolder",tolua_AllToLua_cFile_IsFolder00); - tolua_function(tolua_S,"IsFile",tolua_AllToLua_cFile_IsFile00); - tolua_function(tolua_S,"GetSize",tolua_AllToLua_cFile_GetSize00); - tolua_function(tolua_S,"CreateFolder",tolua_AllToLua_cFile_CreateFolder00); - tolua_endmodule(tolua_S); - tolua_constant(tolua_S,"E_BLOCK_AIR",E_BLOCK_AIR); - tolua_constant(tolua_S,"E_BLOCK_STONE",E_BLOCK_STONE); - tolua_constant(tolua_S,"E_BLOCK_GRASS",E_BLOCK_GRASS); - tolua_constant(tolua_S,"E_BLOCK_DIRT",E_BLOCK_DIRT); - tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE",E_BLOCK_COBBLESTONE); - tolua_constant(tolua_S,"E_BLOCK_PLANKS",E_BLOCK_PLANKS); - tolua_constant(tolua_S,"E_BLOCK_SAPLING",E_BLOCK_SAPLING); - tolua_constant(tolua_S,"E_BLOCK_BEDROCK",E_BLOCK_BEDROCK); - tolua_constant(tolua_S,"E_BLOCK_WATER",E_BLOCK_WATER); - tolua_constant(tolua_S,"E_BLOCK_STATIONARY_WATER",E_BLOCK_STATIONARY_WATER); - tolua_constant(tolua_S,"E_BLOCK_LAVA",E_BLOCK_LAVA); - tolua_constant(tolua_S,"E_BLOCK_STATIONARY_LAVA",E_BLOCK_STATIONARY_LAVA); - tolua_constant(tolua_S,"E_BLOCK_SAND",E_BLOCK_SAND); - tolua_constant(tolua_S,"E_BLOCK_GRAVEL",E_BLOCK_GRAVEL); - tolua_constant(tolua_S,"E_BLOCK_GOLD_ORE",E_BLOCK_GOLD_ORE); - tolua_constant(tolua_S,"E_BLOCK_IRON_ORE",E_BLOCK_IRON_ORE); - tolua_constant(tolua_S,"E_BLOCK_COAL_ORE",E_BLOCK_COAL_ORE); - tolua_constant(tolua_S,"E_BLOCK_LOG",E_BLOCK_LOG); - tolua_constant(tolua_S,"E_BLOCK_LEAVES",E_BLOCK_LEAVES); - tolua_constant(tolua_S,"E_BLOCK_SPONGE",E_BLOCK_SPONGE); - tolua_constant(tolua_S,"E_BLOCK_GLASS",E_BLOCK_GLASS); - tolua_constant(tolua_S,"E_BLOCK_LAPIS_ORE",E_BLOCK_LAPIS_ORE); - tolua_constant(tolua_S,"E_BLOCK_LAPIS_BLOCK",E_BLOCK_LAPIS_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_DISPENSER",E_BLOCK_DISPENSER); - tolua_constant(tolua_S,"E_BLOCK_SANDSTONE",E_BLOCK_SANDSTONE); - tolua_constant(tolua_S,"E_BLOCK_NOTE_BLOCK",E_BLOCK_NOTE_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_BED",E_BLOCK_BED); - tolua_constant(tolua_S,"E_BLOCK_POWERED_RAIL",E_BLOCK_POWERED_RAIL); - tolua_constant(tolua_S,"E_BLOCK_DETECTOR_RAIL",E_BLOCK_DETECTOR_RAIL); - tolua_constant(tolua_S,"E_BLOCK_STICKY_PISTON",E_BLOCK_STICKY_PISTON); - tolua_constant(tolua_S,"E_BLOCK_COBWEB",E_BLOCK_COBWEB); - tolua_constant(tolua_S,"E_BLOCK_TALL_GRASS",E_BLOCK_TALL_GRASS); - tolua_constant(tolua_S,"E_BLOCK_DEAD_BUSH",E_BLOCK_DEAD_BUSH); - tolua_constant(tolua_S,"E_BLOCK_PISTON",E_BLOCK_PISTON); - tolua_constant(tolua_S,"E_BLOCK_PISTON_EXTENSION",E_BLOCK_PISTON_EXTENSION); - tolua_constant(tolua_S,"E_BLOCK_WOOL",E_BLOCK_WOOL); - tolua_constant(tolua_S,"E_BLOCK_PISTON_MOVED_BLOCK",E_BLOCK_PISTON_MOVED_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_DANDELION",E_BLOCK_DANDELION); - tolua_constant(tolua_S,"E_BLOCK_FLOWER",E_BLOCK_FLOWER); - tolua_constant(tolua_S,"E_BLOCK_BROWN_MUSHROOM",E_BLOCK_BROWN_MUSHROOM); - tolua_constant(tolua_S,"E_BLOCK_RED_MUSHROOM",E_BLOCK_RED_MUSHROOM); - tolua_constant(tolua_S,"E_BLOCK_GOLD_BLOCK",E_BLOCK_GOLD_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_IRON_BLOCK",E_BLOCK_IRON_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_DOUBLE_STONE_SLAB",E_BLOCK_DOUBLE_STONE_SLAB); - tolua_constant(tolua_S,"E_BLOCK_STONE_SLAB",E_BLOCK_STONE_SLAB); - tolua_constant(tolua_S,"E_BLOCK_BRICK",E_BLOCK_BRICK); - tolua_constant(tolua_S,"E_BLOCK_TNT",E_BLOCK_TNT); - tolua_constant(tolua_S,"E_BLOCK_BOOKCASE",E_BLOCK_BOOKCASE); - tolua_constant(tolua_S,"E_BLOCK_MOSSY_COBBLESTONE",E_BLOCK_MOSSY_COBBLESTONE); - tolua_constant(tolua_S,"E_BLOCK_OBSIDIAN",E_BLOCK_OBSIDIAN); - tolua_constant(tolua_S,"E_BLOCK_TORCH",E_BLOCK_TORCH); - tolua_constant(tolua_S,"E_BLOCK_FIRE",E_BLOCK_FIRE); - tolua_constant(tolua_S,"E_BLOCK_MOB_SPAWNER",E_BLOCK_MOB_SPAWNER); - tolua_constant(tolua_S,"E_BLOCK_WOODEN_STAIRS",E_BLOCK_WOODEN_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_CHEST",E_BLOCK_CHEST); - tolua_constant(tolua_S,"E_BLOCK_REDSTONE_WIRE",E_BLOCK_REDSTONE_WIRE); - tolua_constant(tolua_S,"E_BLOCK_DIAMOND_ORE",E_BLOCK_DIAMOND_ORE); - tolua_constant(tolua_S,"E_BLOCK_DIAMOND_BLOCK",E_BLOCK_DIAMOND_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_CRAFTING_TABLE",E_BLOCK_CRAFTING_TABLE); - tolua_constant(tolua_S,"E_BLOCK_WORKBENCH",E_BLOCK_WORKBENCH); - tolua_constant(tolua_S,"E_BLOCK_CROPS",E_BLOCK_CROPS); - tolua_constant(tolua_S,"E_BLOCK_FARMLAND",E_BLOCK_FARMLAND); - tolua_constant(tolua_S,"E_BLOCK_FURNACE",E_BLOCK_FURNACE); - tolua_constant(tolua_S,"E_BLOCK_LIT_FURNACE",E_BLOCK_LIT_FURNACE); - tolua_constant(tolua_S,"E_BLOCK_BURNING_FURNACE",E_BLOCK_BURNING_FURNACE); - tolua_constant(tolua_S,"E_BLOCK_SIGN_POST",E_BLOCK_SIGN_POST); - tolua_constant(tolua_S,"E_BLOCK_WOODEN_DOOR",E_BLOCK_WOODEN_DOOR); - tolua_constant(tolua_S,"E_BLOCK_LADDER",E_BLOCK_LADDER); - tolua_constant(tolua_S,"E_BLOCK_RAIL",E_BLOCK_RAIL); - tolua_constant(tolua_S,"E_BLOCK_MINECART_TRACKS",E_BLOCK_MINECART_TRACKS); - tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE_STAIRS",E_BLOCK_COBBLESTONE_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_WALLSIGN",E_BLOCK_WALLSIGN); - tolua_constant(tolua_S,"E_BLOCK_LEVER",E_BLOCK_LEVER); - tolua_constant(tolua_S,"E_BLOCK_STONE_PRESSURE_PLATE",E_BLOCK_STONE_PRESSURE_PLATE); - tolua_constant(tolua_S,"E_BLOCK_IRON_DOOR",E_BLOCK_IRON_DOOR); - tolua_constant(tolua_S,"E_BLOCK_WOODEN_PRESSURE_PLATE",E_BLOCK_WOODEN_PRESSURE_PLATE); - tolua_constant(tolua_S,"E_BLOCK_REDSTONE_ORE",E_BLOCK_REDSTONE_ORE); - tolua_constant(tolua_S,"E_BLOCK_REDSTONE_ORE_GLOWING",E_BLOCK_REDSTONE_ORE_GLOWING); - tolua_constant(tolua_S,"E_BLOCK_REDSTONE_TORCH_OFF",E_BLOCK_REDSTONE_TORCH_OFF); - tolua_constant(tolua_S,"E_BLOCK_REDSTONE_TORCH_ON",E_BLOCK_REDSTONE_TORCH_ON); - tolua_constant(tolua_S,"E_BLOCK_STONE_BUTTON",E_BLOCK_STONE_BUTTON); - tolua_constant(tolua_S,"E_BLOCK_SNOW",E_BLOCK_SNOW); - tolua_constant(tolua_S,"E_BLOCK_ICE",E_BLOCK_ICE); - tolua_constant(tolua_S,"E_BLOCK_SNOW_BLOCK",E_BLOCK_SNOW_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_CACTUS",E_BLOCK_CACTUS); - tolua_constant(tolua_S,"E_BLOCK_CLAY",E_BLOCK_CLAY); - tolua_constant(tolua_S,"E_BLOCK_SUGARCANE",E_BLOCK_SUGARCANE); - tolua_constant(tolua_S,"E_BLOCK_REEDS",E_BLOCK_REEDS); - tolua_constant(tolua_S,"E_BLOCK_JUKEBOX",E_BLOCK_JUKEBOX); - tolua_constant(tolua_S,"E_BLOCK_FENCE",E_BLOCK_FENCE); - tolua_constant(tolua_S,"E_BLOCK_PUMPKIN",E_BLOCK_PUMPKIN); - tolua_constant(tolua_S,"E_BLOCK_NETHERRACK",E_BLOCK_NETHERRACK); - tolua_constant(tolua_S,"E_BLOCK_SOULSAND",E_BLOCK_SOULSAND); - tolua_constant(tolua_S,"E_BLOCK_GLOWSTONE",E_BLOCK_GLOWSTONE); - tolua_constant(tolua_S,"E_BLOCK_NETHER_PORTAL",E_BLOCK_NETHER_PORTAL); - tolua_constant(tolua_S,"E_BLOCK_JACK_O_LANTERN",E_BLOCK_JACK_O_LANTERN); - tolua_constant(tolua_S,"E_BLOCK_CAKE",E_BLOCK_CAKE); - tolua_constant(tolua_S,"E_BLOCK_REDSTONE_REPEATER_OFF",E_BLOCK_REDSTONE_REPEATER_OFF); - tolua_constant(tolua_S,"E_BLOCK_REDSTONE_REPEATER_ON",E_BLOCK_REDSTONE_REPEATER_ON); - tolua_constant(tolua_S,"E_BLOCK_STAINED_GLASS",E_BLOCK_STAINED_GLASS); - tolua_constant(tolua_S,"E_BLOCK_TRAPDOOR",E_BLOCK_TRAPDOOR); - tolua_constant(tolua_S,"E_BLOCK_SILVERFISH_EGG",E_BLOCK_SILVERFISH_EGG); - tolua_constant(tolua_S,"E_BLOCK_STONE_BRICKS",E_BLOCK_STONE_BRICKS); - tolua_constant(tolua_S,"E_BLOCK_HUGE_BROWN_MUSHROOM",E_BLOCK_HUGE_BROWN_MUSHROOM); - tolua_constant(tolua_S,"E_BLOCK_HUGE_RED_MUSHROOM",E_BLOCK_HUGE_RED_MUSHROOM); - tolua_constant(tolua_S,"E_BLOCK_IRON_BARS",E_BLOCK_IRON_BARS); - tolua_constant(tolua_S,"E_BLOCK_GLASS_PANE",E_BLOCK_GLASS_PANE); - tolua_constant(tolua_S,"E_BLOCK_MELON",E_BLOCK_MELON); - tolua_constant(tolua_S,"E_BLOCK_PUMPKIN_STEM",E_BLOCK_PUMPKIN_STEM); - tolua_constant(tolua_S,"E_BLOCK_MELON_STEM",E_BLOCK_MELON_STEM); - tolua_constant(tolua_S,"E_BLOCK_VINES",E_BLOCK_VINES); - tolua_constant(tolua_S,"E_BLOCK_FENCE_GATE",E_BLOCK_FENCE_GATE); - tolua_constant(tolua_S,"E_BLOCK_BRICK_STAIRS",E_BLOCK_BRICK_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_STONE_BRICK_STAIRS",E_BLOCK_STONE_BRICK_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_MYCELIUM",E_BLOCK_MYCELIUM); - tolua_constant(tolua_S,"E_BLOCK_LILY_PAD",E_BLOCK_LILY_PAD); - tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK",E_BLOCK_NETHER_BRICK); - tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK_FENCE",E_BLOCK_NETHER_BRICK_FENCE); - tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK_STAIRS",E_BLOCK_NETHER_BRICK_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_NETHER_WART",E_BLOCK_NETHER_WART); - tolua_constant(tolua_S,"E_BLOCK_ENCHANTMENT_TABLE",E_BLOCK_ENCHANTMENT_TABLE); - tolua_constant(tolua_S,"E_BLOCK_BREWING_STAND",E_BLOCK_BREWING_STAND); - tolua_constant(tolua_S,"E_BLOCK_CAULDRON",E_BLOCK_CAULDRON); - tolua_constant(tolua_S,"E_BLOCK_END_PORTAL",E_BLOCK_END_PORTAL); - tolua_constant(tolua_S,"E_BLOCK_END_PORTAL_FRAME",E_BLOCK_END_PORTAL_FRAME); - tolua_constant(tolua_S,"E_BLOCK_END_STONE",E_BLOCK_END_STONE); - tolua_constant(tolua_S,"E_BLOCK_DRAGON_EGG",E_BLOCK_DRAGON_EGG); - tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_OFF",E_BLOCK_REDSTONE_LAMP_OFF); - tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_ON",E_BLOCK_REDSTONE_LAMP_ON); - tolua_constant(tolua_S,"E_BLOCK_DOUBLE_WOODEN_SLAB",E_BLOCK_DOUBLE_WOODEN_SLAB); - tolua_constant(tolua_S,"E_BLOCK_WOODEN_SLAB",E_BLOCK_WOODEN_SLAB); - tolua_constant(tolua_S,"E_BLOCK_COCOA_POD",E_BLOCK_COCOA_POD); - tolua_constant(tolua_S,"E_BLOCK_SANDSTONE_STAIRS",E_BLOCK_SANDSTONE_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_EMERALD_ORE",E_BLOCK_EMERALD_ORE); - tolua_constant(tolua_S,"E_BLOCK_ENDER_CHEST",E_BLOCK_ENDER_CHEST); - tolua_constant(tolua_S,"E_BLOCK_TRIPWIRE_HOOK",E_BLOCK_TRIPWIRE_HOOK); - tolua_constant(tolua_S,"E_BLOCK_TRIPWIRE",E_BLOCK_TRIPWIRE); - tolua_constant(tolua_S,"E_BLOCK_EMERALD_BLOCK",E_BLOCK_EMERALD_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_SPRUCE_WOOD_STAIRS",E_BLOCK_SPRUCE_WOOD_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_BIRCH_WOOD_STAIRS",E_BLOCK_BIRCH_WOOD_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_JUNGLE_WOOD_STAIRS",E_BLOCK_JUNGLE_WOOD_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_COMMAND_BLOCK",E_BLOCK_COMMAND_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_BEACON",E_BLOCK_BEACON); - tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE_WALL",E_BLOCK_COBBLESTONE_WALL); - tolua_constant(tolua_S,"E_BLOCK_FLOWER_POT",E_BLOCK_FLOWER_POT); - tolua_constant(tolua_S,"E_BLOCK_CARROTS",E_BLOCK_CARROTS); - tolua_constant(tolua_S,"E_BLOCK_POTATOES",E_BLOCK_POTATOES); - tolua_constant(tolua_S,"E_BLOCK_WOODEN_BUTTON",E_BLOCK_WOODEN_BUTTON); - tolua_constant(tolua_S,"E_BLOCK_HEAD",E_BLOCK_HEAD); - tolua_constant(tolua_S,"E_BLOCK_ANVIL",E_BLOCK_ANVIL); - tolua_constant(tolua_S,"E_BLOCK_TRAPPED_CHEST",E_BLOCK_TRAPPED_CHEST); - tolua_constant(tolua_S,"E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE",E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE); - tolua_constant(tolua_S,"E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE",E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE); - tolua_constant(tolua_S,"E_BLOCK_INACTIVE_COMPARATOR",E_BLOCK_INACTIVE_COMPARATOR); - tolua_constant(tolua_S,"E_BLOCK_ACTIVE_COMPARATOR",E_BLOCK_ACTIVE_COMPARATOR); - tolua_constant(tolua_S,"E_BLOCK_DAYLIGHT_SENSOR",E_BLOCK_DAYLIGHT_SENSOR); - tolua_constant(tolua_S,"E_BLOCK_BLOCK_OF_REDSTONE",E_BLOCK_BLOCK_OF_REDSTONE); - tolua_constant(tolua_S,"E_BLOCK_NETHER_QUARTZ_ORE",E_BLOCK_NETHER_QUARTZ_ORE); - tolua_constant(tolua_S,"E_BLOCK_HOPPER",E_BLOCK_HOPPER); - tolua_constant(tolua_S,"E_BLOCK_QUARTZ_BLOCK",E_BLOCK_QUARTZ_BLOCK); - tolua_constant(tolua_S,"E_BLOCK_QUARTZ_STAIRS",E_BLOCK_QUARTZ_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_ACTIVATOR_RAIL",E_BLOCK_ACTIVATOR_RAIL); - tolua_constant(tolua_S,"E_BLOCK_DROPPER",E_BLOCK_DROPPER); - tolua_constant(tolua_S,"E_BLOCK_STAINED_CLAY",E_BLOCK_STAINED_CLAY); - tolua_constant(tolua_S,"E_BLOCK_STAINED_GLASS_PANE",E_BLOCK_STAINED_GLASS_PANE); - tolua_constant(tolua_S,"E_BLOCK_NEW_LEAVES",E_BLOCK_NEW_LEAVES); - tolua_constant(tolua_S,"E_BLOCK_NEW_LOG",E_BLOCK_NEW_LOG); - tolua_constant(tolua_S,"E_BLOCK_ACACIA_WOOD_STAIRS",E_BLOCK_ACACIA_WOOD_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_DARK_OAK_WOOD_STAIRS",E_BLOCK_DARK_OAK_WOOD_STAIRS); - tolua_constant(tolua_S,"E_BLOCK_HAY_BALE",E_BLOCK_HAY_BALE); - tolua_constant(tolua_S,"E_BLOCK_CARPET",E_BLOCK_CARPET); - tolua_constant(tolua_S,"E_BLOCK_HARDENED_CLAY",E_BLOCK_HARDENED_CLAY); - tolua_constant(tolua_S,"E_BLOCK_BLOCK_OF_COAL",E_BLOCK_BLOCK_OF_COAL); - tolua_constant(tolua_S,"E_BLOCK_PACKED_ICE",E_BLOCK_PACKED_ICE); - tolua_constant(tolua_S,"E_BLOCK_BIG_FLOWER",E_BLOCK_BIG_FLOWER); - tolua_constant(tolua_S,"E_BLOCK_NUMBER_OF_TYPES",E_BLOCK_NUMBER_OF_TYPES); - tolua_constant(tolua_S,"E_BLOCK_MAX_TYPE_ID",E_BLOCK_MAX_TYPE_ID); - tolua_constant(tolua_S,"E_BLOCK_YELLOW_FLOWER",E_BLOCK_YELLOW_FLOWER); - tolua_constant(tolua_S,"E_BLOCK_RED_ROSE",E_BLOCK_RED_ROSE); - tolua_constant(tolua_S,"E_BLOCK_LOCKED_CHEST",E_BLOCK_LOCKED_CHEST); - tolua_constant(tolua_S,"E_ITEM_EMPTY",E_ITEM_EMPTY); - tolua_constant(tolua_S,"E_ITEM_FIRST",E_ITEM_FIRST); - tolua_constant(tolua_S,"E_ITEM_IRON_SHOVEL",E_ITEM_IRON_SHOVEL); - tolua_constant(tolua_S,"E_ITEM_IRON_PICKAXE",E_ITEM_IRON_PICKAXE); - tolua_constant(tolua_S,"E_ITEM_IRON_AXE",E_ITEM_IRON_AXE); - tolua_constant(tolua_S,"E_ITEM_FLINT_AND_STEEL",E_ITEM_FLINT_AND_STEEL); - tolua_constant(tolua_S,"E_ITEM_RED_APPLE",E_ITEM_RED_APPLE); - tolua_constant(tolua_S,"E_ITEM_BOW",E_ITEM_BOW); - tolua_constant(tolua_S,"E_ITEM_ARROW",E_ITEM_ARROW); - tolua_constant(tolua_S,"E_ITEM_COAL",E_ITEM_COAL); - tolua_constant(tolua_S,"E_ITEM_DIAMOND",E_ITEM_DIAMOND); - tolua_constant(tolua_S,"E_ITEM_IRON",E_ITEM_IRON); - tolua_constant(tolua_S,"E_ITEM_GOLD",E_ITEM_GOLD); - tolua_constant(tolua_S,"E_ITEM_IRON_SWORD",E_ITEM_IRON_SWORD); - tolua_constant(tolua_S,"E_ITEM_WOODEN_SWORD",E_ITEM_WOODEN_SWORD); - tolua_constant(tolua_S,"E_ITEM_WOODEN_SHOVEL",E_ITEM_WOODEN_SHOVEL); - tolua_constant(tolua_S,"E_ITEM_WOODEN_PICKAXE",E_ITEM_WOODEN_PICKAXE); - tolua_constant(tolua_S,"E_ITEM_WOODEN_AXE",E_ITEM_WOODEN_AXE); - tolua_constant(tolua_S,"E_ITEM_STONE_SWORD",E_ITEM_STONE_SWORD); - tolua_constant(tolua_S,"E_ITEM_STONE_SHOVEL",E_ITEM_STONE_SHOVEL); - tolua_constant(tolua_S,"E_ITEM_STONE_PICKAXE",E_ITEM_STONE_PICKAXE); - tolua_constant(tolua_S,"E_ITEM_STONE_AXE",E_ITEM_STONE_AXE); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_SWORD",E_ITEM_DIAMOND_SWORD); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_SHOVEL",E_ITEM_DIAMOND_SHOVEL); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_PICKAXE",E_ITEM_DIAMOND_PICKAXE); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_AXE",E_ITEM_DIAMOND_AXE); - tolua_constant(tolua_S,"E_ITEM_STICK",E_ITEM_STICK); - tolua_constant(tolua_S,"E_ITEM_BOWL",E_ITEM_BOWL); - tolua_constant(tolua_S,"E_ITEM_MUSHROOM_SOUP",E_ITEM_MUSHROOM_SOUP); - tolua_constant(tolua_S,"E_ITEM_GOLD_SWORD",E_ITEM_GOLD_SWORD); - tolua_constant(tolua_S,"E_ITEM_GOLD_SHOVEL",E_ITEM_GOLD_SHOVEL); - tolua_constant(tolua_S,"E_ITEM_GOLD_PICKAXE",E_ITEM_GOLD_PICKAXE); - tolua_constant(tolua_S,"E_ITEM_GOLD_AXE",E_ITEM_GOLD_AXE); - tolua_constant(tolua_S,"E_ITEM_STRING",E_ITEM_STRING); - tolua_constant(tolua_S,"E_ITEM_FEATHER",E_ITEM_FEATHER); - tolua_constant(tolua_S,"E_ITEM_GUNPOWDER",E_ITEM_GUNPOWDER); - tolua_constant(tolua_S,"E_ITEM_WOODEN_HOE",E_ITEM_WOODEN_HOE); - tolua_constant(tolua_S,"E_ITEM_STONE_HOE",E_ITEM_STONE_HOE); - tolua_constant(tolua_S,"E_ITEM_IRON_HOE",E_ITEM_IRON_HOE); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_HOE",E_ITEM_DIAMOND_HOE); - tolua_constant(tolua_S,"E_ITEM_GOLD_HOE",E_ITEM_GOLD_HOE); - tolua_constant(tolua_S,"E_ITEM_SEEDS",E_ITEM_SEEDS); - tolua_constant(tolua_S,"E_ITEM_WHEAT",E_ITEM_WHEAT); - tolua_constant(tolua_S,"E_ITEM_BREAD",E_ITEM_BREAD); - tolua_constant(tolua_S,"E_ITEM_LEATHER_CAP",E_ITEM_LEATHER_CAP); - tolua_constant(tolua_S,"E_ITEM_LEATHER_TUNIC",E_ITEM_LEATHER_TUNIC); - tolua_constant(tolua_S,"E_ITEM_LEATHER_PANTS",E_ITEM_LEATHER_PANTS); - tolua_constant(tolua_S,"E_ITEM_LEATHER_BOOTS",E_ITEM_LEATHER_BOOTS); - tolua_constant(tolua_S,"E_ITEM_CHAIN_HELMET",E_ITEM_CHAIN_HELMET); - tolua_constant(tolua_S,"E_ITEM_CHAIN_CHESTPLATE",E_ITEM_CHAIN_CHESTPLATE); - tolua_constant(tolua_S,"E_ITEM_CHAIN_LEGGINGS",E_ITEM_CHAIN_LEGGINGS); - tolua_constant(tolua_S,"E_ITEM_CHAIN_BOOTS",E_ITEM_CHAIN_BOOTS); - tolua_constant(tolua_S,"E_ITEM_IRON_HELMET",E_ITEM_IRON_HELMET); - tolua_constant(tolua_S,"E_ITEM_IRON_CHESTPLATE",E_ITEM_IRON_CHESTPLATE); - tolua_constant(tolua_S,"E_ITEM_IRON_LEGGINGS",E_ITEM_IRON_LEGGINGS); - tolua_constant(tolua_S,"E_ITEM_IRON_BOOTS",E_ITEM_IRON_BOOTS); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_HELMET",E_ITEM_DIAMOND_HELMET); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_CHESTPLATE",E_ITEM_DIAMOND_CHESTPLATE); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_LEGGINGS",E_ITEM_DIAMOND_LEGGINGS); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_BOOTS",E_ITEM_DIAMOND_BOOTS); - tolua_constant(tolua_S,"E_ITEM_GOLD_HELMET",E_ITEM_GOLD_HELMET); - tolua_constant(tolua_S,"E_ITEM_GOLD_CHESTPLATE",E_ITEM_GOLD_CHESTPLATE); - tolua_constant(tolua_S,"E_ITEM_GOLD_LEGGINGS",E_ITEM_GOLD_LEGGINGS); - tolua_constant(tolua_S,"E_ITEM_GOLD_BOOTS",E_ITEM_GOLD_BOOTS); - tolua_constant(tolua_S,"E_ITEM_FLINT",E_ITEM_FLINT); - tolua_constant(tolua_S,"E_ITEM_RAW_PORKCHOP",E_ITEM_RAW_PORKCHOP); - tolua_constant(tolua_S,"E_ITEM_COOKED_PORKCHOP",E_ITEM_COOKED_PORKCHOP); - tolua_constant(tolua_S,"E_ITEM_PAINTINGS",E_ITEM_PAINTINGS); - tolua_constant(tolua_S,"E_ITEM_GOLDEN_APPLE",E_ITEM_GOLDEN_APPLE); - tolua_constant(tolua_S,"E_ITEM_SIGN",E_ITEM_SIGN); - tolua_constant(tolua_S,"E_ITEM_WOODEN_DOOR",E_ITEM_WOODEN_DOOR); - tolua_constant(tolua_S,"E_ITEM_BUCKET",E_ITEM_BUCKET); - tolua_constant(tolua_S,"E_ITEM_WATER_BUCKET",E_ITEM_WATER_BUCKET); - tolua_constant(tolua_S,"E_ITEM_LAVA_BUCKET",E_ITEM_LAVA_BUCKET); - tolua_constant(tolua_S,"E_ITEM_MINECART",E_ITEM_MINECART); - tolua_constant(tolua_S,"E_ITEM_SADDLE",E_ITEM_SADDLE); - tolua_constant(tolua_S,"E_ITEM_IRON_DOOR",E_ITEM_IRON_DOOR); - tolua_constant(tolua_S,"E_ITEM_REDSTONE_DUST",E_ITEM_REDSTONE_DUST); - tolua_constant(tolua_S,"E_ITEM_SNOWBALL",E_ITEM_SNOWBALL); - tolua_constant(tolua_S,"E_ITEM_BOAT",E_ITEM_BOAT); - tolua_constant(tolua_S,"E_ITEM_LEATHER",E_ITEM_LEATHER); - tolua_constant(tolua_S,"E_ITEM_MILK",E_ITEM_MILK); - tolua_constant(tolua_S,"E_ITEM_CLAY_BRICK",E_ITEM_CLAY_BRICK); - tolua_constant(tolua_S,"E_ITEM_CLAY",E_ITEM_CLAY); - tolua_constant(tolua_S,"E_ITEM_SUGARCANE",E_ITEM_SUGARCANE); - tolua_constant(tolua_S,"E_ITEM_SUGAR_CANE",E_ITEM_SUGAR_CANE); - tolua_constant(tolua_S,"E_ITEM_PAPER",E_ITEM_PAPER); - tolua_constant(tolua_S,"E_ITEM_BOOK",E_ITEM_BOOK); - tolua_constant(tolua_S,"E_ITEM_SLIMEBALL",E_ITEM_SLIMEBALL); - tolua_constant(tolua_S,"E_ITEM_CHEST_MINECART",E_ITEM_CHEST_MINECART); - tolua_constant(tolua_S,"E_ITEM_FURNACE_MINECART",E_ITEM_FURNACE_MINECART); - tolua_constant(tolua_S,"E_ITEM_EGG",E_ITEM_EGG); - tolua_constant(tolua_S,"E_ITEM_COMPASS",E_ITEM_COMPASS); - tolua_constant(tolua_S,"E_ITEM_FISHING_ROD",E_ITEM_FISHING_ROD); - tolua_constant(tolua_S,"E_ITEM_CLOCK",E_ITEM_CLOCK); - tolua_constant(tolua_S,"E_ITEM_GLOWSTONE_DUST",E_ITEM_GLOWSTONE_DUST); - tolua_constant(tolua_S,"E_ITEM_RAW_FISH",E_ITEM_RAW_FISH); - tolua_constant(tolua_S,"E_ITEM_COOKED_FISH",E_ITEM_COOKED_FISH); - tolua_constant(tolua_S,"E_ITEM_DYE",E_ITEM_DYE); - tolua_constant(tolua_S,"E_ITEM_BONE",E_ITEM_BONE); - tolua_constant(tolua_S,"E_ITEM_SUGAR",E_ITEM_SUGAR); - tolua_constant(tolua_S,"E_ITEM_CAKE",E_ITEM_CAKE); - tolua_constant(tolua_S,"E_ITEM_BED",E_ITEM_BED); - tolua_constant(tolua_S,"E_ITEM_REDSTONE_REPEATER",E_ITEM_REDSTONE_REPEATER); - tolua_constant(tolua_S,"E_ITEM_COOKIE",E_ITEM_COOKIE); - tolua_constant(tolua_S,"E_ITEM_MAP",E_ITEM_MAP); - tolua_constant(tolua_S,"E_ITEM_SHEARS",E_ITEM_SHEARS); - tolua_constant(tolua_S,"E_ITEM_MELON_SLICE",E_ITEM_MELON_SLICE); - tolua_constant(tolua_S,"E_ITEM_PUMPKIN_SEEDS",E_ITEM_PUMPKIN_SEEDS); - tolua_constant(tolua_S,"E_ITEM_MELON_SEEDS",E_ITEM_MELON_SEEDS); - tolua_constant(tolua_S,"E_ITEM_RAW_BEEF",E_ITEM_RAW_BEEF); - tolua_constant(tolua_S,"E_ITEM_STEAK",E_ITEM_STEAK); - tolua_constant(tolua_S,"E_ITEM_RAW_CHICKEN",E_ITEM_RAW_CHICKEN); - tolua_constant(tolua_S,"E_ITEM_COOKED_CHICKEN",E_ITEM_COOKED_CHICKEN); - tolua_constant(tolua_S,"E_ITEM_ROTTEN_FLESH",E_ITEM_ROTTEN_FLESH); - tolua_constant(tolua_S,"E_ITEM_ENDER_PEARL",E_ITEM_ENDER_PEARL); - tolua_constant(tolua_S,"E_ITEM_BLAZE_ROD",E_ITEM_BLAZE_ROD); - tolua_constant(tolua_S,"E_ITEM_GHAST_TEAR",E_ITEM_GHAST_TEAR); - tolua_constant(tolua_S,"E_ITEM_GOLD_NUGGET",E_ITEM_GOLD_NUGGET); - tolua_constant(tolua_S,"E_ITEM_NETHER_WART",E_ITEM_NETHER_WART); - tolua_constant(tolua_S,"E_ITEM_POTIONS",E_ITEM_POTIONS); - tolua_constant(tolua_S,"E_ITEM_GLASS_BOTTLE",E_ITEM_GLASS_BOTTLE); - tolua_constant(tolua_S,"E_ITEM_SPIDER_EYE",E_ITEM_SPIDER_EYE); - tolua_constant(tolua_S,"E_ITEM_FERMENTED_SPIDER_EYE",E_ITEM_FERMENTED_SPIDER_EYE); - tolua_constant(tolua_S,"E_ITEM_BLAZE_POWDER",E_ITEM_BLAZE_POWDER); - tolua_constant(tolua_S,"E_ITEM_MAGMA_CREAM",E_ITEM_MAGMA_CREAM); - tolua_constant(tolua_S,"E_ITEM_BREWING_STAND",E_ITEM_BREWING_STAND); - tolua_constant(tolua_S,"E_ITEM_CAULDRON",E_ITEM_CAULDRON); - tolua_constant(tolua_S,"E_ITEM_EYE_OF_ENDER",E_ITEM_EYE_OF_ENDER); - tolua_constant(tolua_S,"E_ITEM_GLISTERING_MELON",E_ITEM_GLISTERING_MELON); - tolua_constant(tolua_S,"E_ITEM_SPAWN_EGG",E_ITEM_SPAWN_EGG); - tolua_constant(tolua_S,"E_ITEM_BOTTLE_O_ENCHANTING",E_ITEM_BOTTLE_O_ENCHANTING); - tolua_constant(tolua_S,"E_ITEM_FIRE_CHARGE",E_ITEM_FIRE_CHARGE); - tolua_constant(tolua_S,"E_ITEM_BOOK_AND_QUILL",E_ITEM_BOOK_AND_QUILL); - tolua_constant(tolua_S,"E_ITEM_WRITTEN_BOOK",E_ITEM_WRITTEN_BOOK); - tolua_constant(tolua_S,"E_ITEM_EMERALD",E_ITEM_EMERALD); - tolua_constant(tolua_S,"E_ITEM_ITEM_FRAME",E_ITEM_ITEM_FRAME); - tolua_constant(tolua_S,"E_ITEM_FLOWER_POT",E_ITEM_FLOWER_POT); - tolua_constant(tolua_S,"E_ITEM_CARROT",E_ITEM_CARROT); - tolua_constant(tolua_S,"E_ITEM_POTATO",E_ITEM_POTATO); - tolua_constant(tolua_S,"E_ITEM_BAKED_POTATO",E_ITEM_BAKED_POTATO); - tolua_constant(tolua_S,"E_ITEM_POISONOUS_POTATO",E_ITEM_POISONOUS_POTATO); - tolua_constant(tolua_S,"E_ITEM_EMPTY_MAP",E_ITEM_EMPTY_MAP); - tolua_constant(tolua_S,"E_ITEM_GOLDEN_CARROT",E_ITEM_GOLDEN_CARROT); - tolua_constant(tolua_S,"E_ITEM_HEAD",E_ITEM_HEAD); - tolua_constant(tolua_S,"E_ITEM_CARROT_ON_STICK",E_ITEM_CARROT_ON_STICK); - tolua_constant(tolua_S,"E_ITEM_NETHER_STAR",E_ITEM_NETHER_STAR); - tolua_constant(tolua_S,"E_ITEM_PUMPKIN_PIE",E_ITEM_PUMPKIN_PIE); - tolua_constant(tolua_S,"E_ITEM_FIREWORK_ROCKET",E_ITEM_FIREWORK_ROCKET); - tolua_constant(tolua_S,"E_ITEM_FIREWORK_STAR",E_ITEM_FIREWORK_STAR); - tolua_constant(tolua_S,"E_ITEM_ENCHANTED_BOOK",E_ITEM_ENCHANTED_BOOK); - tolua_constant(tolua_S,"E_ITEM_COMPARATOR",E_ITEM_COMPARATOR); - tolua_constant(tolua_S,"E_ITEM_NETHER_BRICK",E_ITEM_NETHER_BRICK); - tolua_constant(tolua_S,"E_ITEM_NETHER_QUARTZ",E_ITEM_NETHER_QUARTZ); - tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_TNT",E_ITEM_MINECART_WITH_TNT); - tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_HOPPER",E_ITEM_MINECART_WITH_HOPPER); - tolua_constant(tolua_S,"E_ITEM_IRON_HORSE_ARMOR",E_ITEM_IRON_HORSE_ARMOR); - tolua_constant(tolua_S,"E_ITEM_GOLD_HORSE_ARMOR",E_ITEM_GOLD_HORSE_ARMOR); - tolua_constant(tolua_S,"E_ITEM_DIAMOND_HORSE_ARMOR",E_ITEM_DIAMOND_HORSE_ARMOR); - tolua_constant(tolua_S,"E_ITEM_LEAD",E_ITEM_LEAD); - tolua_constant(tolua_S,"E_ITEM_NAME_TAG",E_ITEM_NAME_TAG); - tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_COMMAND_BLOCK",E_ITEM_MINECART_WITH_COMMAND_BLOCK); - tolua_constant(tolua_S,"E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES",E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES); - tolua_constant(tolua_S,"E_ITEM_MAX_CONSECUTIVE_TYPE_ID",E_ITEM_MAX_CONSECUTIVE_TYPE_ID); - tolua_constant(tolua_S,"E_ITEM_FIRST_DISC",E_ITEM_FIRST_DISC); - tolua_constant(tolua_S,"E_ITEM_13_DISC",E_ITEM_13_DISC); - tolua_constant(tolua_S,"E_ITEM_CAT_DISC",E_ITEM_CAT_DISC); - tolua_constant(tolua_S,"E_ITEM_BLOCKS_DISC",E_ITEM_BLOCKS_DISC); - tolua_constant(tolua_S,"E_ITEM_CHIRP_DISC",E_ITEM_CHIRP_DISC); - tolua_constant(tolua_S,"E_ITEM_FAR_DISC",E_ITEM_FAR_DISC); - tolua_constant(tolua_S,"E_ITEM_MALL_DISC",E_ITEM_MALL_DISC); - tolua_constant(tolua_S,"E_ITEM_MELLOHI_DISC",E_ITEM_MELLOHI_DISC); - tolua_constant(tolua_S,"E_ITEM_STAL_DISC",E_ITEM_STAL_DISC); - tolua_constant(tolua_S,"E_ITEM_STRAD_DISC",E_ITEM_STRAD_DISC); - tolua_constant(tolua_S,"E_ITEM_WARD_DISC",E_ITEM_WARD_DISC); - tolua_constant(tolua_S,"E_ITEM_11_DISC",E_ITEM_11_DISC); - tolua_constant(tolua_S,"E_ITEM_WAIT_DISC",E_ITEM_WAIT_DISC); - tolua_constant(tolua_S,"E_ITEM_LAST_DISC_PLUS_ONE",E_ITEM_LAST_DISC_PLUS_ONE); - tolua_constant(tolua_S,"E_ITEM_LAST_DISC",E_ITEM_LAST_DISC); - tolua_constant(tolua_S,"E_ITEM_LAST",E_ITEM_LAST); - tolua_constant(tolua_S,"E_META_CHEST_FACING_ZM",E_META_CHEST_FACING_ZM); - tolua_constant(tolua_S,"E_META_CHEST_FACING_ZP",E_META_CHEST_FACING_ZP); - tolua_constant(tolua_S,"E_META_CHEST_FACING_XM",E_META_CHEST_FACING_XM); - tolua_constant(tolua_S,"E_META_CHEST_FACING_XP",E_META_CHEST_FACING_XP); - tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_YM",E_META_DROPSPENSER_FACING_YM); - tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_YP",E_META_DROPSPENSER_FACING_YP); - tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_ZM",E_META_DROPSPENSER_FACING_ZM); - tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_ZP",E_META_DROPSPENSER_FACING_ZP); - tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_XM",E_META_DROPSPENSER_FACING_XM); - tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_XP",E_META_DROPSPENSER_FACING_XP); - tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE",E_META_DOUBLE_STONE_SLAB_STONE); - tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_SANDSTONE",E_META_DOUBLE_STONE_SLAB_SANDSTONE); - tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_WOODEN",E_META_DOUBLE_STONE_SLAB_WOODEN); - tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_COBBLESTONE",E_META_DOUBLE_STONE_SLAB_COBBLESTONE); - tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_BRICK",E_META_DOUBLE_STONE_SLAB_BRICK); - tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE_BRICK",E_META_DOUBLE_STONE_SLAB_STONE_BRICK); - tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_NETHER_BRICK",E_META_DOUBLE_STONE_SLAB_NETHER_BRICK); - tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE_SECRET",E_META_DOUBLE_STONE_SLAB_STONE_SECRET); - tolua_constant(tolua_S,"E_META_HOPPER_FACING_YM",E_META_HOPPER_FACING_YM); - tolua_constant(tolua_S,"E_META_HOPPER_UNATTACHED",E_META_HOPPER_UNATTACHED); - tolua_constant(tolua_S,"E_META_HOPPER_FACING_ZM",E_META_HOPPER_FACING_ZM); - tolua_constant(tolua_S,"E_META_HOPPER_FACING_ZP",E_META_HOPPER_FACING_ZP); - tolua_constant(tolua_S,"E_META_HOPPER_FACING_XM",E_META_HOPPER_FACING_XM); - tolua_constant(tolua_S,"E_META_HOPPER_FACING_XP",E_META_HOPPER_FACING_XP); - tolua_constant(tolua_S,"E_META_LEAVES_APPLE",E_META_LEAVES_APPLE); - tolua_constant(tolua_S,"E_META_LEAVES_CONIFER",E_META_LEAVES_CONIFER); - tolua_constant(tolua_S,"E_META_LEAVES_BIRCH",E_META_LEAVES_BIRCH); - tolua_constant(tolua_S,"E_META_LEAVES_JUNGLE",E_META_LEAVES_JUNGLE); - tolua_constant(tolua_S,"E_META_LOG_APPLE",E_META_LOG_APPLE); - tolua_constant(tolua_S,"E_META_LOG_CONIFER",E_META_LOG_CONIFER); - tolua_constant(tolua_S,"E_META_LOG_BIRCH",E_META_LOG_BIRCH); - tolua_constant(tolua_S,"E_META_LOG_JUNGLE",E_META_LOG_JUNGLE); - tolua_constant(tolua_S,"E_META_PLANKS_APPLE",E_META_PLANKS_APPLE); - tolua_constant(tolua_S,"E_META_PLANKS_CONIFER",E_META_PLANKS_CONIFER); - tolua_constant(tolua_S,"E_META_PLANKS_BIRCH",E_META_PLANKS_BIRCH); - tolua_constant(tolua_S,"E_META_PLANKS_JUNGLE",E_META_PLANKS_JUNGLE); - tolua_constant(tolua_S,"E_META_SANDSTONE_NORMAL",E_META_SANDSTONE_NORMAL); - tolua_constant(tolua_S,"E_META_SANDSTONE_ORNAMENT",E_META_SANDSTONE_ORNAMENT); - tolua_constant(tolua_S,"E_META_SANDSTONE_SMOOTH",E_META_SANDSTONE_SMOOTH); - tolua_constant(tolua_S,"E_META_SAPLING_APPLE",E_META_SAPLING_APPLE); - tolua_constant(tolua_S,"E_META_SAPLING_CONIFER",E_META_SAPLING_CONIFER); - tolua_constant(tolua_S,"E_META_SAPLING_BIRCH",E_META_SAPLING_BIRCH); - tolua_constant(tolua_S,"E_META_SAPLING_JUNGLE",E_META_SAPLING_JUNGLE); - tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_STONE",E_META_SILVERFISH_EGG_STONE); - tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_COBBLESTONE",E_META_SILVERFISH_EGG_COBBLESTONE); - tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_STONE_BRICK",E_META_SILVERFISH_EGG_STONE_BRICK); - tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE",E_META_STONE_SLAB_STONE); - tolua_constant(tolua_S,"E_META_STONE_SLAB_SANDSTONE",E_META_STONE_SLAB_SANDSTONE); - tolua_constant(tolua_S,"E_META_STONE_SLAB_PLANKS",E_META_STONE_SLAB_PLANKS); - tolua_constant(tolua_S,"E_META_STONE_SLAB_COBBLESTONE",E_META_STONE_SLAB_COBBLESTONE); - tolua_constant(tolua_S,"E_META_STONE_SLAB_BRICK",E_META_STONE_SLAB_BRICK); - tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE_BRICK",E_META_STONE_SLAB_STONE_BRICK); - tolua_constant(tolua_S,"E_META_STONE_SLAB_NETHER_BRICK",E_META_STONE_SLAB_NETHER_BRICK); - tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE_SECRET",E_META_STONE_SLAB_STONE_SECRET); - tolua_constant(tolua_S,"E_META_STONE_BRICK_NORMAL",E_META_STONE_BRICK_NORMAL); - tolua_constant(tolua_S,"E_META_STONE_BRICK_MOSSY",E_META_STONE_BRICK_MOSSY); - tolua_constant(tolua_S,"E_META_STONE_BRICK_CRACKED",E_META_STONE_BRICK_CRACKED); - tolua_constant(tolua_S,"E_META_STONE_BRICK_ORNAMENT",E_META_STONE_BRICK_ORNAMENT); - tolua_constant(tolua_S,"E_META_TALL_GRASS_DEAD_SHRUB",E_META_TALL_GRASS_DEAD_SHRUB); - tolua_constant(tolua_S,"E_META_TALL_GRASS_GRASS",E_META_TALL_GRASS_GRASS); - tolua_constant(tolua_S,"E_META_TALL_GRASS_FERN",E_META_TALL_GRASS_FERN); - tolua_constant(tolua_S,"E_META_TORCH_EAST",E_META_TORCH_EAST); - tolua_constant(tolua_S,"E_META_TORCH_WEST",E_META_TORCH_WEST); - tolua_constant(tolua_S,"E_META_TORCH_SOUTH",E_META_TORCH_SOUTH); - tolua_constant(tolua_S,"E_META_TORCH_NORTH",E_META_TORCH_NORTH); - tolua_constant(tolua_S,"E_META_TORCH_FLOOR",E_META_TORCH_FLOOR); - tolua_constant(tolua_S,"E_META_TORCH_XM",E_META_TORCH_XM); - tolua_constant(tolua_S,"E_META_TORCH_XP",E_META_TORCH_XP); - tolua_constant(tolua_S,"E_META_TORCH_ZM",E_META_TORCH_ZM); - tolua_constant(tolua_S,"E_META_TORCH_ZP",E_META_TORCH_ZP); - tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_APPLE",E_META_WOODEN_DOUBLE_SLAB_APPLE); - tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_CONIFER",E_META_WOODEN_DOUBLE_SLAB_CONIFER); - tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_BIRCH",E_META_WOODEN_DOUBLE_SLAB_BIRCH); - tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_JUNGLE",E_META_WOODEN_DOUBLE_SLAB_JUNGLE); - tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_ACACIA",E_META_WOODEN_DOUBLE_SLAB_ACACIA); - tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_DARK_OAK",E_META_WOODEN_DOUBLE_SLAB_DARK_OAK); - tolua_constant(tolua_S,"E_META_WOODEN_SLAB_APPLE",E_META_WOODEN_SLAB_APPLE); - tolua_constant(tolua_S,"E_META_WOODEN_SLAB_CONIFER",E_META_WOODEN_SLAB_CONIFER); - tolua_constant(tolua_S,"E_META_WOODEN_SLAB_BIRCH",E_META_WOODEN_SLAB_BIRCH); - tolua_constant(tolua_S,"E_META_WOODEN_SLAB_JUNGLE",E_META_WOODEN_SLAB_JUNGLE); - tolua_constant(tolua_S,"E_META_WOODEN_SLAB_ACACIA",E_META_WOODEN_SLAB_ACACIA); - tolua_constant(tolua_S,"E_META_WOODEN_SLAB_DARK_OAK",E_META_WOODEN_SLAB_DARK_OAK); - tolua_constant(tolua_S,"E_META_WOOL_WHITE",E_META_WOOL_WHITE); - tolua_constant(tolua_S,"E_META_WOOL_ORANGE",E_META_WOOL_ORANGE); - tolua_constant(tolua_S,"E_META_WOOL_MAGENTA",E_META_WOOL_MAGENTA); - tolua_constant(tolua_S,"E_META_WOOL_LIGHTBLUE",E_META_WOOL_LIGHTBLUE); - tolua_constant(tolua_S,"E_META_WOOL_YELLOW",E_META_WOOL_YELLOW); - tolua_constant(tolua_S,"E_META_WOOL_LIGHTGREEN",E_META_WOOL_LIGHTGREEN); - tolua_constant(tolua_S,"E_META_WOOL_PINK",E_META_WOOL_PINK); - tolua_constant(tolua_S,"E_META_WOOL_GRAY",E_META_WOOL_GRAY); - tolua_constant(tolua_S,"E_META_WOOL_LIGHTGRAY",E_META_WOOL_LIGHTGRAY); - tolua_constant(tolua_S,"E_META_WOOL_CYAN",E_META_WOOL_CYAN); - tolua_constant(tolua_S,"E_META_WOOL_PURPLE",E_META_WOOL_PURPLE); - tolua_constant(tolua_S,"E_META_WOOL_BLUE",E_META_WOOL_BLUE); - tolua_constant(tolua_S,"E_META_WOOL_BROWN",E_META_WOOL_BROWN); - tolua_constant(tolua_S,"E_META_WOOL_GREEN",E_META_WOOL_GREEN); - tolua_constant(tolua_S,"E_META_WOOL_RED",E_META_WOOL_RED); - tolua_constant(tolua_S,"E_META_WOOL_BLACK",E_META_WOOL_BLACK); - tolua_constant(tolua_S,"E_META_CARPET_WHITE",E_META_CARPET_WHITE); - tolua_constant(tolua_S,"E_META_CARPET_ORANGE",E_META_CARPET_ORANGE); - tolua_constant(tolua_S,"E_META_CARPET_MAGENTA",E_META_CARPET_MAGENTA); - tolua_constant(tolua_S,"E_META_CARPET_LIGHTBLUE",E_META_CARPET_LIGHTBLUE); - tolua_constant(tolua_S,"E_META_CARPET_YELLOW",E_META_CARPET_YELLOW); - tolua_constant(tolua_S,"E_META_CARPET_LIGHTGREEN",E_META_CARPET_LIGHTGREEN); - tolua_constant(tolua_S,"E_META_CARPET_PINK",E_META_CARPET_PINK); - tolua_constant(tolua_S,"E_META_CARPET_GRAY",E_META_CARPET_GRAY); - tolua_constant(tolua_S,"E_META_CARPET_LIGHTGRAY",E_META_CARPET_LIGHTGRAY); - tolua_constant(tolua_S,"E_META_CARPET_CYAN",E_META_CARPET_CYAN); - tolua_constant(tolua_S,"E_META_CARPET_PURPLE",E_META_CARPET_PURPLE); - tolua_constant(tolua_S,"E_META_CARPET_BLUE",E_META_CARPET_BLUE); - tolua_constant(tolua_S,"E_META_CARPET_BROWN",E_META_CARPET_BROWN); - tolua_constant(tolua_S,"E_META_CARPET_GREEN",E_META_CARPET_GREEN); - tolua_constant(tolua_S,"E_META_CARPET_RED",E_META_CARPET_RED); - tolua_constant(tolua_S,"E_META_CARPET_BLACK",E_META_CARPET_BLACK); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_WHITE",E_META_STAINED_CLAY_WHITE); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_ORANGE",E_META_STAINED_CLAY_ORANGE); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_MAGENTA",E_META_STAINED_CLAY_MAGENTA); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTBLUE",E_META_STAINED_CLAY_LIGHTBLUE); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_YELLOW",E_META_STAINED_CLAY_YELLOW); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTGREEN",E_META_STAINED_CLAY_LIGHTGREEN); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_PINK",E_META_STAINED_CLAY_PINK); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_GRAY",E_META_STAINED_CLAY_GRAY); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTGRAY",E_META_STAINED_CLAY_LIGHTGRAY); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_CYAN",E_META_STAINED_CLAY_CYAN); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_PURPLE",E_META_STAINED_CLAY_PURPLE); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_BLUE",E_META_STAINED_CLAY_BLUE); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_BROWN",E_META_STAINED_CLAY_BROWN); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_GREEN",E_META_STAINED_CLAY_GREEN); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_RED",E_META_STAINED_CLAY_RED); - tolua_constant(tolua_S,"E_META_STAINED_CLAY_BLACK",E_META_STAINED_CLAY_BLACK); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_WHITE",E_META_STAINED_GLASS_WHITE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_ORANGE",E_META_STAINED_GLASS_ORANGE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_MAGENTA",E_META_STAINED_GLASS_MAGENTA); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTBLUE",E_META_STAINED_GLASS_LIGHTBLUE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_YELLOW",E_META_STAINED_GLASS_YELLOW); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTGREEN",E_META_STAINED_GLASS_LIGHTGREEN); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PINK",E_META_STAINED_GLASS_PINK); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_GRAY",E_META_STAINED_GLASS_GRAY); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTGRAY",E_META_STAINED_GLASS_LIGHTGRAY); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_CYAN",E_META_STAINED_GLASS_CYAN); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PURPLE",E_META_STAINED_GLASS_PURPLE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_BLUE",E_META_STAINED_GLASS_BLUE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_BROWN",E_META_STAINED_GLASS_BROWN); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_GREEN",E_META_STAINED_GLASS_GREEN); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_RED",E_META_STAINED_GLASS_RED); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_BLACK",E_META_STAINED_GLASS_BLACK); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_WHITE",E_META_STAINED_GLASS_PANE_WHITE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_ORANGE",E_META_STAINED_GLASS_PANE_ORANGE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_MAGENTA",E_META_STAINED_GLASS_PANE_MAGENTA); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTBLUE",E_META_STAINED_GLASS_PANE_LIGHTBLUE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_YELLOW",E_META_STAINED_GLASS_PANE_YELLOW); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTGREEN",E_META_STAINED_GLASS_PANE_LIGHTGREEN); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_PINK",E_META_STAINED_GLASS_PANE_PINK); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_GRAY",E_META_STAINED_GLASS_PANE_GRAY); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTGRAY",E_META_STAINED_GLASS_PANE_LIGHTGRAY); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_CYAN",E_META_STAINED_GLASS_PANE_CYAN); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_PURPLE",E_META_STAINED_GLASS_PANE_PURPLE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BLUE",E_META_STAINED_GLASS_PANE_BLUE); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BROWN",E_META_STAINED_GLASS_PANE_BROWN); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_GREEN",E_META_STAINED_GLASS_PANE_GREEN); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_RED",E_META_STAINED_GLASS_PANE_RED); - tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BLACK",E_META_STAINED_GLASS_PANE_BLACK); - tolua_constant(tolua_S,"E_META_SNOW_LAYER_ONE",E_META_SNOW_LAYER_ONE); - tolua_constant(tolua_S,"E_META_SNOW_LAYER_TWO",E_META_SNOW_LAYER_TWO); - tolua_constant(tolua_S,"E_META_SNOW_LAYER_THREE",E_META_SNOW_LAYER_THREE); - tolua_constant(tolua_S,"E_META_SNOW_LAYER_FOUR",E_META_SNOW_LAYER_FOUR); - tolua_constant(tolua_S,"E_META_SNOW_LAYER_FIVE",E_META_SNOW_LAYER_FIVE); - tolua_constant(tolua_S,"E_META_SNOW_LAYER_SIX",E_META_SNOW_LAYER_SIX); - tolua_constant(tolua_S,"E_META_SNOW_LAYER_SEVEN",E_META_SNOW_LAYER_SEVEN); - tolua_constant(tolua_S,"E_META_SNOW_LAYER_EIGHT",E_META_SNOW_LAYER_EIGHT); - tolua_constant(tolua_S,"E_META_RAIL_ZM_ZP",E_META_RAIL_ZM_ZP); - tolua_constant(tolua_S,"E_META_RAIL_XM_XP",E_META_RAIL_XM_XP); - tolua_constant(tolua_S,"E_META_RAIL_ASCEND_XP",E_META_RAIL_ASCEND_XP); - tolua_constant(tolua_S,"E_META_RAIL_ASCEND_XM",E_META_RAIL_ASCEND_XM); - tolua_constant(tolua_S,"E_META_RAIL_ASCEND_ZM",E_META_RAIL_ASCEND_ZM); - tolua_constant(tolua_S,"E_META_RAIL_ASCEND_ZP",E_META_RAIL_ASCEND_ZP); - tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZP_XP",E_META_RAIL_CURVED_ZP_XP); - tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZP_XM",E_META_RAIL_CURVED_ZP_XM); - tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZM_XM",E_META_RAIL_CURVED_ZM_XM); - tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZM_XP",E_META_RAIL_CURVED_ZM_XP); - tolua_constant(tolua_S,"E_META_NEW_LEAVES_ACACIA_WOOD",E_META_NEW_LEAVES_ACACIA_WOOD); - tolua_constant(tolua_S,"E_META_NEW_LEAVES_DARK_OAK_WOOD",E_META_NEW_LEAVES_DARK_OAK_WOOD); - tolua_constant(tolua_S,"E_META_NEW_LOG_ACACIA_WOOD",E_META_NEW_LOG_ACACIA_WOOD); - tolua_constant(tolua_S,"E_META_NEW_LOG_DARK_OAK_WOOD",E_META_NEW_LOG_DARK_OAK_WOOD); - tolua_constant(tolua_S,"E_META_FLOWER_POPPY",E_META_FLOWER_POPPY); - tolua_constant(tolua_S,"E_META_FLOWER_BLUE_ORCHID",E_META_FLOWER_BLUE_ORCHID); - tolua_constant(tolua_S,"E_META_FLOWER_ALLIUM",E_META_FLOWER_ALLIUM); - tolua_constant(tolua_S,"E_META_FLOWER_RED_TULIP",E_META_FLOWER_RED_TULIP); - tolua_constant(tolua_S,"E_META_FLOWER_ORANGE_TULIP",E_META_FLOWER_ORANGE_TULIP); - tolua_constant(tolua_S,"E_META_FLOWER_WHITE_TULIP",E_META_FLOWER_WHITE_TULIP); - tolua_constant(tolua_S,"E_META_FLOWER_PINK_TULIP",E_META_FLOWER_PINK_TULIP); - tolua_constant(tolua_S,"E_META_FLOWER_OXEYE_DAISY",E_META_FLOWER_OXEYE_DAISY); - tolua_constant(tolua_S,"E_META_BIG_FLOWER_SUNFLOWER",E_META_BIG_FLOWER_SUNFLOWER); - tolua_constant(tolua_S,"E_META_BIG_FLOWER_LILAC",E_META_BIG_FLOWER_LILAC); - tolua_constant(tolua_S,"E_META_BIG_FLOWER_DOUBLE_TALL_GRASS",E_META_BIG_FLOWER_DOUBLE_TALL_GRASS); - tolua_constant(tolua_S,"E_META_BIG_FLOWER_LARGE_FERN",E_META_BIG_FLOWER_LARGE_FERN); - tolua_constant(tolua_S,"E_META_BIG_FLOWER_ROSE_BUSH",E_META_BIG_FLOWER_ROSE_BUSH); - tolua_constant(tolua_S,"E_META_BIG_FLOWER_PEONY",E_META_BIG_FLOWER_PEONY); - tolua_constant(tolua_S,"E_META_COAL_NORMAL",E_META_COAL_NORMAL); - tolua_constant(tolua_S,"E_META_COAL_CHARCOAL",E_META_COAL_CHARCOAL); - tolua_constant(tolua_S,"E_META_DYE_BLACK",E_META_DYE_BLACK); - tolua_constant(tolua_S,"E_META_DYE_RED",E_META_DYE_RED); - tolua_constant(tolua_S,"E_META_DYE_GREEN",E_META_DYE_GREEN); - tolua_constant(tolua_S,"E_META_DYE_BROWN",E_META_DYE_BROWN); - tolua_constant(tolua_S,"E_META_DYE_BLUE",E_META_DYE_BLUE); - tolua_constant(tolua_S,"E_META_DYE_PURPLE",E_META_DYE_PURPLE); - tolua_constant(tolua_S,"E_META_DYE_CYAN",E_META_DYE_CYAN); - tolua_constant(tolua_S,"E_META_DYE_LIGHTGRAY",E_META_DYE_LIGHTGRAY); - tolua_constant(tolua_S,"E_META_DYE_GRAY",E_META_DYE_GRAY); - tolua_constant(tolua_S,"E_META_DYE_PINK",E_META_DYE_PINK); - tolua_constant(tolua_S,"E_META_DYE_LIGHTGREEN",E_META_DYE_LIGHTGREEN); - tolua_constant(tolua_S,"E_META_DYE_YELLOW",E_META_DYE_YELLOW); - tolua_constant(tolua_S,"E_META_DYE_LIGHTBLUE",E_META_DYE_LIGHTBLUE); - tolua_constant(tolua_S,"E_META_DYE_MAGENTA",E_META_DYE_MAGENTA); - tolua_constant(tolua_S,"E_META_DYE_ORANGE",E_META_DYE_ORANGE); - tolua_constant(tolua_S,"E_META_DYE_WHITE",E_META_DYE_WHITE); - tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_NORMAL",E_META_GOLDEN_APPLE_NORMAL); - tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_ENCHANTED",E_META_GOLDEN_APPLE_ENCHANTED); - tolua_constant(tolua_S,"E_META_RAW_FISH_FISH",E_META_RAW_FISH_FISH); - tolua_constant(tolua_S,"E_META_RAW_FISH_SALMON",E_META_RAW_FISH_SALMON); - tolua_constant(tolua_S,"E_META_RAW_FISH_CLOWNFISH",E_META_RAW_FISH_CLOWNFISH); - tolua_constant(tolua_S,"E_META_RAW_FISH_PUFFERFISH",E_META_RAW_FISH_PUFFERFISH); - tolua_constant(tolua_S,"E_META_COOKED_FISH_FISH",E_META_COOKED_FISH_FISH); - tolua_constant(tolua_S,"E_META_COOKED_FISH_SALMON",E_META_COOKED_FISH_SALMON); - tolua_constant(tolua_S,"E_META_COOKED_FISH_CLOWNFISH",E_META_COOKED_FISH_CLOWNFISH); - tolua_constant(tolua_S,"E_META_COOKED_FISH_PUFFERFISH",E_META_COOKED_FISH_PUFFERFISH); - tolua_constant(tolua_S,"E_META_TRACKS_X",E_META_TRACKS_X); - tolua_constant(tolua_S,"E_META_TRACKS_Z",E_META_TRACKS_Z); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_PICKUP",E_META_SPAWN_EGG_PICKUP); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXPERIENCE_ORB",E_META_SPAWN_EGG_EXPERIENCE_ORB); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_LEASH_KNOT",E_META_SPAWN_EGG_LEASH_KNOT); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_PAINTING",E_META_SPAWN_EGG_PAINTING); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ARROW",E_META_SPAWN_EGG_ARROW); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOWBALL",E_META_SPAWN_EGG_SNOWBALL); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREBALL",E_META_SPAWN_EGG_FIREBALL); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SMALL_FIREBALL",E_META_SPAWN_EGG_SMALL_FIREBALL); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_PEARL",E_META_SPAWN_EGG_ENDER_PEARL); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_EYE_OF_ENDER",E_META_SPAWN_EGG_EYE_OF_ENDER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPLASH_POTION",E_META_SPAWN_EGG_SPLASH_POTION); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXP_BOTTLE",E_META_SPAWN_EGG_EXP_BOTTLE); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ITEM_FRAME",E_META_SPAWN_EGG_ITEM_FRAME); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER_SKULL",E_META_SPAWN_EGG_WITHER_SKULL); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_PRIMED_TNT",E_META_SPAWN_EGG_PRIMED_TNT); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_FALLING_BLOCK",E_META_SPAWN_EGG_FALLING_BLOCK); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREWORK",E_META_SPAWN_EGG_FIREWORK); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_BOAT",E_META_SPAWN_EGG_BOAT); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART",E_META_SPAWN_EGG_MINECART); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_CHEST",E_META_SPAWN_EGG_MINECART_CHEST); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_FURNACE",E_META_SPAWN_EGG_MINECART_FURNACE); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_TNT",E_META_SPAWN_EGG_MINECART_TNT); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_HOPPER",E_META_SPAWN_EGG_MINECART_HOPPER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_SPAWNER",E_META_SPAWN_EGG_MINECART_SPAWNER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_CREEPER",E_META_SPAWN_EGG_CREEPER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SKELETON",E_META_SPAWN_EGG_SKELETON); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPIDER",E_META_SPAWN_EGG_SPIDER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_GIANT",E_META_SPAWN_EGG_GIANT); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE",E_META_SPAWN_EGG_ZOMBIE); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SLIME",E_META_SPAWN_EGG_SLIME); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_GHAST",E_META_SPAWN_EGG_GHAST); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE_PIGMAN",E_META_SPAWN_EGG_ZOMBIE_PIGMAN); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDERMAN",E_META_SPAWN_EGG_ENDERMAN); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_CAVE_SPIDER",E_META_SPAWN_EGG_CAVE_SPIDER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SILVERFISH",E_META_SPAWN_EGG_SILVERFISH); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_BLAZE",E_META_SPAWN_EGG_BLAZE); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_MAGMA_CUBE",E_META_SPAWN_EGG_MAGMA_CUBE); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_DRAGON",E_META_SPAWN_EGG_ENDER_DRAGON); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER",E_META_SPAWN_EGG_WITHER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_BAT",E_META_SPAWN_EGG_BAT); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITCH",E_META_SPAWN_EGG_WITCH); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_PIG",E_META_SPAWN_EGG_PIG); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SHEEP",E_META_SPAWN_EGG_SHEEP); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_COW",E_META_SPAWN_EGG_COW); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_CHICKEN",E_META_SPAWN_EGG_CHICKEN); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SQUID",E_META_SPAWN_EGG_SQUID); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_WOLF",E_META_SPAWN_EGG_WOLF); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_MOOSHROOM",E_META_SPAWN_EGG_MOOSHROOM); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOW_GOLEM",E_META_SPAWN_EGG_SNOW_GOLEM); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_OCELOT",E_META_SPAWN_EGG_OCELOT); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_IRON_GOLEM",E_META_SPAWN_EGG_IRON_GOLEM); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_HORSE",E_META_SPAWN_EGG_HORSE); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_VILLAGER",E_META_SPAWN_EGG_VILLAGER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_CRYSTAL",E_META_SPAWN_EGG_ENDER_CRYSTAL); - tolua_constant(tolua_S,"dimNether",dimNether); - tolua_constant(tolua_S,"dimOverworld",dimOverworld); - tolua_constant(tolua_S,"dimEnd",dimEnd); - tolua_constant(tolua_S,"dtAttack",dtAttack); - tolua_constant(tolua_S,"dtRangedAttack",dtRangedAttack); - tolua_constant(tolua_S,"dtLightning",dtLightning); - tolua_constant(tolua_S,"dtFalling",dtFalling); - tolua_constant(tolua_S,"dtDrowning",dtDrowning); - tolua_constant(tolua_S,"dtSuffocating",dtSuffocating); - tolua_constant(tolua_S,"dtStarving",dtStarving); - tolua_constant(tolua_S,"dtCactusContact",dtCactusContact); - tolua_constant(tolua_S,"dtLavaContact",dtLavaContact); - tolua_constant(tolua_S,"dtPoisoning",dtPoisoning); - tolua_constant(tolua_S,"dtOnFire",dtOnFire); - tolua_constant(tolua_S,"dtFireContact",dtFireContact); - tolua_constant(tolua_S,"dtInVoid",dtInVoid); - tolua_constant(tolua_S,"dtPotionOfHarming",dtPotionOfHarming); - tolua_constant(tolua_S,"dtEnderPearl",dtEnderPearl); - tolua_constant(tolua_S,"dtAdmin",dtAdmin); - tolua_constant(tolua_S,"dtPawnAttack",dtPawnAttack); - tolua_constant(tolua_S,"dtEntityAttack",dtEntityAttack); - tolua_constant(tolua_S,"dtMob",dtMob); - tolua_constant(tolua_S,"dtMobAttack",dtMobAttack); - tolua_constant(tolua_S,"dtArrowAttack",dtArrowAttack); - tolua_constant(tolua_S,"dtArrow",dtArrow); - tolua_constant(tolua_S,"dtProjectile",dtProjectile); - tolua_constant(tolua_S,"dtFall",dtFall); - tolua_constant(tolua_S,"dtDrown",dtDrown); - tolua_constant(tolua_S,"dtSuffocation",dtSuffocation); - tolua_constant(tolua_S,"dtStarvation",dtStarvation); - tolua_constant(tolua_S,"dtHunger",dtHunger); - tolua_constant(tolua_S,"dtCactus",dtCactus); - tolua_constant(tolua_S,"dtCactuses",dtCactuses); - tolua_constant(tolua_S,"dtCacti",dtCacti); - tolua_constant(tolua_S,"dtLava",dtLava); - tolua_constant(tolua_S,"dtPoison",dtPoison); - tolua_constant(tolua_S,"dtBurning",dtBurning); - tolua_constant(tolua_S,"dtInFire",dtInFire); - tolua_constant(tolua_S,"dtPlugin",dtPlugin); - tolua_constant(tolua_S,"esOther",esOther); - tolua_constant(tolua_S,"esPrimedTNT",esPrimedTNT); - tolua_constant(tolua_S,"esCreeper",esCreeper); - tolua_constant(tolua_S,"esBed",esBed); - tolua_constant(tolua_S,"esEnderCrystal",esEnderCrystal); - tolua_constant(tolua_S,"esGhastFireball",esGhastFireball); - tolua_constant(tolua_S,"esWitherSkullBlack",esWitherSkullBlack); - tolua_constant(tolua_S,"esWitherSkullBlue",esWitherSkullBlue); - tolua_constant(tolua_S,"esWitherBirth",esWitherBirth); - tolua_constant(tolua_S,"esPlugin",esPlugin); - tolua_function(tolua_S,"BlockStringToType",tolua_AllToLua_BlockStringToType00); - tolua_function(tolua_S,"StringToItem",tolua_AllToLua_StringToItem00); - tolua_function(tolua_S,"ItemToString",tolua_AllToLua_ItemToString00); - tolua_function(tolua_S,"ItemTypeToString",tolua_AllToLua_ItemTypeToString00); - tolua_function(tolua_S,"ItemToFullString",tolua_AllToLua_ItemToFullString00); - tolua_function(tolua_S,"StringToBiome",tolua_AllToLua_StringToBiome00); - tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_StringToMobType00); - tolua_function(tolua_S,"StringToDimension",tolua_AllToLua_StringToDimension00); - tolua_function(tolua_S,"DamageTypeToString",tolua_AllToLua_DamageTypeToString00); - tolua_function(tolua_S,"StringToDamageType",tolua_AllToLua_StringToDamageType00); - tolua_function(tolua_S,"GetIniItemSet",tolua_AllToLua_GetIniItemSet00); - tolua_function(tolua_S,"TrimString",tolua_AllToLua_TrimString00); - tolua_function(tolua_S,"NoCaseCompare",tolua_AllToLua_NoCaseCompare00); - tolua_function(tolua_S,"ReplaceString",tolua_AllToLua_ReplaceString00); - tolua_function(tolua_S,"EscapeString",tolua_AllToLua_EscapeString00); - tolua_function(tolua_S,"StripColorCodes",tolua_AllToLua_StripColorCodes00); - tolua_array(tolua_S,"g_BlockLightValue",tolua_get_AllToLua_g_BlockLightValue,tolua_set_AllToLua_g_BlockLightValue); - tolua_array(tolua_S,"g_BlockSpreadLightFalloff",tolua_get_AllToLua_g_BlockSpreadLightFalloff,tolua_set_AllToLua_g_BlockSpreadLightFalloff); - tolua_array(tolua_S,"g_BlockTransparent",tolua_get_AllToLua_g_BlockTransparent,tolua_set_AllToLua_g_BlockTransparent); - tolua_array(tolua_S,"g_BlockOneHitDig",tolua_get_AllToLua_g_BlockOneHitDig,tolua_set_AllToLua_g_BlockOneHitDig); - tolua_array(tolua_S,"g_BlockPistonBreakable",tolua_get_AllToLua_g_BlockPistonBreakable,tolua_set_AllToLua_g_BlockPistonBreakable); - tolua_array(tolua_S,"g_BlockIsSnowable",tolua_get_AllToLua_g_BlockIsSnowable,tolua_set_AllToLua_g_BlockIsSnowable); - tolua_array(tolua_S,"g_BlockRequiresSpecialTool",tolua_get_AllToLua_g_BlockRequiresSpecialTool,tolua_set_AllToLua_g_BlockRequiresSpecialTool); - tolua_array(tolua_S,"g_BlockIsSolid",tolua_get_AllToLua_g_BlockIsSolid,tolua_set_AllToLua_g_BlockIsSolid); - tolua_array(tolua_S,"g_BlockIsTorchPlaceable",tolua_get_AllToLua_g_BlockIsTorchPlaceable,tolua_set_AllToLua_g_BlockIsTorchPlaceable); - tolua_constant(tolua_S,"MAX_EXPERIENCE_ORB_SIZE",MAX_EXPERIENCE_ORB_SIZE); - tolua_constant(tolua_S,"BLOCK_FACE_NONE",BLOCK_FACE_NONE); - tolua_constant(tolua_S,"BLOCK_FACE_XM",BLOCK_FACE_XM); - tolua_constant(tolua_S,"BLOCK_FACE_XP",BLOCK_FACE_XP); - tolua_constant(tolua_S,"BLOCK_FACE_YM",BLOCK_FACE_YM); - tolua_constant(tolua_S,"BLOCK_FACE_YP",BLOCK_FACE_YP); - tolua_constant(tolua_S,"BLOCK_FACE_ZM",BLOCK_FACE_ZM); - tolua_constant(tolua_S,"BLOCK_FACE_ZP",BLOCK_FACE_ZP); - tolua_constant(tolua_S,"BLOCK_FACE_BOTTOM",BLOCK_FACE_BOTTOM); - tolua_constant(tolua_S,"BLOCK_FACE_TOP",BLOCK_FACE_TOP); - tolua_constant(tolua_S,"BLOCK_FACE_NORTH",BLOCK_FACE_NORTH); - tolua_constant(tolua_S,"BLOCK_FACE_SOUTH",BLOCK_FACE_SOUTH); - tolua_constant(tolua_S,"BLOCK_FACE_WEST",BLOCK_FACE_WEST); - tolua_constant(tolua_S,"BLOCK_FACE_EAST",BLOCK_FACE_EAST); - tolua_constant(tolua_S,"DIG_STATUS_STARTED",DIG_STATUS_STARTED); - tolua_constant(tolua_S,"DIG_STATUS_CANCELLED",DIG_STATUS_CANCELLED); - tolua_constant(tolua_S,"DIG_STATUS_FINISHED",DIG_STATUS_FINISHED); - tolua_constant(tolua_S,"DIG_STATUS_DROP_HELD",DIG_STATUS_DROP_HELD); - tolua_constant(tolua_S,"DIG_STATUS_SHOOT_EAT",DIG_STATUS_SHOOT_EAT); - tolua_constant(tolua_S,"caLeftClick",caLeftClick); - tolua_constant(tolua_S,"caRightClick",caRightClick); - tolua_constant(tolua_S,"caShiftLeftClick",caShiftLeftClick); - tolua_constant(tolua_S,"caShiftRightClick",caShiftRightClick); - tolua_constant(tolua_S,"caNumber1",caNumber1); - tolua_constant(tolua_S,"caNumber2",caNumber2); - tolua_constant(tolua_S,"caNumber3",caNumber3); - tolua_constant(tolua_S,"caNumber4",caNumber4); - tolua_constant(tolua_S,"caNumber5",caNumber5); - tolua_constant(tolua_S,"caNumber6",caNumber6); - tolua_constant(tolua_S,"caNumber7",caNumber7); - tolua_constant(tolua_S,"caNumber8",caNumber8); - tolua_constant(tolua_S,"caNumber9",caNumber9); - tolua_constant(tolua_S,"caMiddleClick",caMiddleClick); - tolua_constant(tolua_S,"caDropKey",caDropKey); - tolua_constant(tolua_S,"caCtrlDropKey",caCtrlDropKey); - tolua_constant(tolua_S,"caLeftClickOutside",caLeftClickOutside); - tolua_constant(tolua_S,"caRightClickOutside",caRightClickOutside); - tolua_constant(tolua_S,"caLeftClickOutsideHoldNothing",caLeftClickOutsideHoldNothing); - tolua_constant(tolua_S,"caRightClickOutsideHoldNothing",caRightClickOutsideHoldNothing); - tolua_constant(tolua_S,"caLeftPaintBegin",caLeftPaintBegin); - tolua_constant(tolua_S,"caRightPaintBegin",caRightPaintBegin); - tolua_constant(tolua_S,"caLeftPaintProgress",caLeftPaintProgress); - tolua_constant(tolua_S,"caRightPaintProgress",caRightPaintProgress); - tolua_constant(tolua_S,"caLeftPaintEnd",caLeftPaintEnd); - tolua_constant(tolua_S,"caRightPaintEnd",caRightPaintEnd); - tolua_constant(tolua_S,"caDblClick",caDblClick); - tolua_constant(tolua_S,"caUnknown",caUnknown); - tolua_constant(tolua_S,"eGameMode_NotSet",eGameMode_NotSet); - tolua_constant(tolua_S,"eGameMode_Survival",eGameMode_Survival); - tolua_constant(tolua_S,"eGameMode_Creative",eGameMode_Creative); - tolua_constant(tolua_S,"eGameMode_Adventure",eGameMode_Adventure); - tolua_constant(tolua_S,"gmNotSet",gmNotSet); - tolua_constant(tolua_S,"gmSurvival",gmSurvival); - tolua_constant(tolua_S,"gmCreative",gmCreative); - tolua_constant(tolua_S,"gmAdventure",gmAdventure); - tolua_constant(tolua_S,"gmMax",gmMax); - tolua_constant(tolua_S,"gmMin",gmMin); - tolua_constant(tolua_S,"eWeather_Sunny",eWeather_Sunny); - tolua_constant(tolua_S,"eWeather_Rain",eWeather_Rain); - tolua_constant(tolua_S,"eWeather_ThunderStorm",eWeather_ThunderStorm); - tolua_constant(tolua_S,"wSunny",wSunny); - tolua_constant(tolua_S,"wRain",wRain); - tolua_constant(tolua_S,"wThunderstorm",wThunderstorm); - tolua_constant(tolua_S,"wStorm",wStorm); - tolua_function(tolua_S,"ClickActionToString",tolua_AllToLua_ClickActionToString00); - tolua_function(tolua_S,"IsValidBlock",tolua_AllToLua_IsValidBlock00); - tolua_function(tolua_S,"IsValidItem",tolua_AllToLua_IsValidItem00); - tolua_function(tolua_S,"AddFaceDirection",tolua_AllToLua_AddFaceDirection00); - tolua_module(tolua_S,"ItemCategory",0); - tolua_beginmodule(tolua_S,"ItemCategory"); - tolua_function(tolua_S,"IsPickaxe",tolua_AllToLua_ItemCategory_IsPickaxe00); - tolua_function(tolua_S,"IsAxe",tolua_AllToLua_ItemCategory_IsAxe00); - tolua_function(tolua_S,"IsSword",tolua_AllToLua_ItemCategory_IsSword00); - tolua_function(tolua_S,"IsHoe",tolua_AllToLua_ItemCategory_IsHoe00); - tolua_function(tolua_S,"IsShovel",tolua_AllToLua_ItemCategory_IsShovel00); - tolua_function(tolua_S,"IsTool",tolua_AllToLua_ItemCategory_IsTool00); - tolua_function(tolua_S,"IsHelmet",tolua_AllToLua_ItemCategory_IsHelmet00); - tolua_function(tolua_S,"IsChestPlate",tolua_AllToLua_ItemCategory_IsChestPlate00); - tolua_function(tolua_S,"IsLeggings",tolua_AllToLua_ItemCategory_IsLeggings00); - tolua_function(tolua_S,"IsBoots",tolua_AllToLua_ItemCategory_IsBoots00); - tolua_function(tolua_S,"IsArmor",tolua_AllToLua_ItemCategory_IsArmor00); - tolua_endmodule(tolua_S); - tolua_function(tolua_S,"GetTime",tolua_AllToLua_GetTime00); - tolua_function(tolua_S,"GetChar",tolua_AllToLua_GetChar00); - tolua_cclass(tolua_S,"cChatColor","cChatColor","",NULL); - tolua_beginmodule(tolua_S,"cChatColor"); - tolua_variable(tolua_S,"Color",tolua_get_cChatColor_Color,NULL); - tolua_variable(tolua_S,"Delimiter",tolua_get_cChatColor_Delimiter,NULL); - tolua_variable(tolua_S,"Black",tolua_get_cChatColor_Black,NULL); - tolua_variable(tolua_S,"Navy",tolua_get_cChatColor_Navy,NULL); - tolua_variable(tolua_S,"Green",tolua_get_cChatColor_Green,NULL); - tolua_variable(tolua_S,"Blue",tolua_get_cChatColor_Blue,NULL); - tolua_variable(tolua_S,"Red",tolua_get_cChatColor_Red,NULL); - tolua_variable(tolua_S,"Purple",tolua_get_cChatColor_Purple,NULL); - tolua_variable(tolua_S,"Gold",tolua_get_cChatColor_Gold,NULL); - tolua_variable(tolua_S,"LightGray",tolua_get_cChatColor_LightGray,NULL); - tolua_variable(tolua_S,"Gray",tolua_get_cChatColor_Gray,NULL); - tolua_variable(tolua_S,"DarkPurple",tolua_get_cChatColor_DarkPurple,NULL); - tolua_variable(tolua_S,"LightGreen",tolua_get_cChatColor_LightGreen,NULL); - tolua_variable(tolua_S,"LightBlue",tolua_get_cChatColor_LightBlue,NULL); - tolua_variable(tolua_S,"Rose",tolua_get_cChatColor_Rose,NULL); - tolua_variable(tolua_S,"LightPurple",tolua_get_cChatColor_LightPurple,NULL); - tolua_variable(tolua_S,"Yellow",tolua_get_cChatColor_Yellow,NULL); - tolua_variable(tolua_S,"White",tolua_get_cChatColor_White,NULL); - tolua_variable(tolua_S,"Random",tolua_get_cChatColor_Random,NULL); - tolua_variable(tolua_S,"Bold",tolua_get_cChatColor_Bold,NULL); - tolua_variable(tolua_S,"Strikethrough",tolua_get_cChatColor_Strikethrough,NULL); - tolua_variable(tolua_S,"Underlined",tolua_get_cChatColor_Underlined,NULL); - tolua_variable(tolua_S,"Italic",tolua_get_cChatColor_Italic,NULL); - tolua_variable(tolua_S,"Plain",tolua_get_cChatColor_Plain,NULL); - tolua_function(tolua_S,"MakeColor",tolua_AllToLua_cChatColor_MakeColor00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cClientHandle","cClientHandle","",NULL); - tolua_beginmodule(tolua_S,"cClientHandle"); - tolua_function(tolua_S,"GetPlayer",tolua_AllToLua_cClientHandle_GetPlayer00); - tolua_function(tolua_S,"Kick",tolua_AllToLua_cClientHandle_Kick00); - tolua_function(tolua_S,"SendBlockChange",tolua_AllToLua_cClientHandle_SendBlockChange00); - tolua_function(tolua_S,"GetUsername",tolua_AllToLua_cClientHandle_GetUsername00); - tolua_function(tolua_S,"SetUsername",tolua_AllToLua_cClientHandle_SetUsername00); - tolua_function(tolua_S,"GetPing",tolua_AllToLua_cClientHandle_GetPing00); - tolua_function(tolua_S,"SetViewDistance",tolua_AllToLua_cClientHandle_SetViewDistance00); - tolua_function(tolua_S,"GetViewDistance",tolua_AllToLua_cClientHandle_GetViewDistance00); - tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cClientHandle_GetUniqueID00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"TakeDamageInfo","TakeDamageInfo","",NULL); - tolua_beginmodule(tolua_S,"TakeDamageInfo"); - tolua_variable(tolua_S,"DamageType",tolua_get_TakeDamageInfo_DamageType,tolua_set_TakeDamageInfo_DamageType); - tolua_variable(tolua_S,"Attacker",tolua_get_TakeDamageInfo_Attacker_ptr,tolua_set_TakeDamageInfo_Attacker_ptr); - tolua_variable(tolua_S,"RawDamage",tolua_get_TakeDamageInfo_RawDamage,tolua_set_TakeDamageInfo_RawDamage); - tolua_variable(tolua_S,"FinalDamage",tolua_get_TakeDamageInfo_FinalDamage,tolua_set_TakeDamageInfo_FinalDamage); - tolua_variable(tolua_S,"Knockback",tolua_get_TakeDamageInfo_Knockback,tolua_set_TakeDamageInfo_Knockback); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cEntity","cEntity","",NULL); - tolua_beginmodule(tolua_S,"cEntity"); - tolua_constant(tolua_S,"etEntity",cEntity::etEntity); - tolua_constant(tolua_S,"etPlayer",cEntity::etPlayer); - tolua_constant(tolua_S,"etPickup",cEntity::etPickup); - tolua_constant(tolua_S,"etMonster",cEntity::etMonster); - tolua_constant(tolua_S,"etFallingBlock",cEntity::etFallingBlock); - tolua_constant(tolua_S,"etMinecart",cEntity::etMinecart); - tolua_constant(tolua_S,"etBoat",cEntity::etBoat); - tolua_constant(tolua_S,"etTNT",cEntity::etTNT); - tolua_constant(tolua_S,"etProjectile",cEntity::etProjectile); - tolua_constant(tolua_S,"etMob",cEntity::etMob); - tolua_function(tolua_S,"GetEntityType",tolua_AllToLua_cEntity_GetEntityType00); - tolua_function(tolua_S,"IsPlayer",tolua_AllToLua_cEntity_IsPlayer00); - tolua_function(tolua_S,"IsPickup",tolua_AllToLua_cEntity_IsPickup00); - tolua_function(tolua_S,"IsMob",tolua_AllToLua_cEntity_IsMob00); - tolua_function(tolua_S,"IsFallingBlock",tolua_AllToLua_cEntity_IsFallingBlock00); - tolua_function(tolua_S,"IsMinecart",tolua_AllToLua_cEntity_IsMinecart00); - tolua_function(tolua_S,"IsBoat",tolua_AllToLua_cEntity_IsBoat00); - tolua_function(tolua_S,"IsTNT",tolua_AllToLua_cEntity_IsTNT00); - tolua_function(tolua_S,"IsProjectile",tolua_AllToLua_cEntity_IsProjectile00); - tolua_function(tolua_S,"IsA",tolua_AllToLua_cEntity_IsA00); - tolua_function(tolua_S,"GetClass",tolua_AllToLua_cEntity_GetClass00); - tolua_function(tolua_S,"GetClassStatic",tolua_AllToLua_cEntity_GetClassStatic00); - tolua_function(tolua_S,"GetParentClass",tolua_AllToLua_cEntity_GetParentClass00); - tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cEntity_GetWorld00); - tolua_function(tolua_S,"GetHeadYaw",tolua_AllToLua_cEntity_GetHeadYaw00); - tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cEntity_GetHeight00); - tolua_function(tolua_S,"GetMass",tolua_AllToLua_cEntity_GetMass00); - tolua_function(tolua_S,"GetPosition",tolua_AllToLua_cEntity_GetPosition00); - tolua_function(tolua_S,"GetPosX",tolua_AllToLua_cEntity_GetPosX00); - tolua_function(tolua_S,"GetPosY",tolua_AllToLua_cEntity_GetPosY00); - tolua_function(tolua_S,"GetPosZ",tolua_AllToLua_cEntity_GetPosZ00); - tolua_function(tolua_S,"GetRot",tolua_AllToLua_cEntity_GetRot00); - tolua_function(tolua_S,"GetRotation",tolua_AllToLua_cEntity_GetRotation00); - tolua_function(tolua_S,"GetYaw",tolua_AllToLua_cEntity_GetYaw00); - tolua_function(tolua_S,"GetPitch",tolua_AllToLua_cEntity_GetPitch00); - tolua_function(tolua_S,"GetRoll",tolua_AllToLua_cEntity_GetRoll00); - tolua_function(tolua_S,"GetLookVector",tolua_AllToLua_cEntity_GetLookVector00); - tolua_function(tolua_S,"GetSpeed",tolua_AllToLua_cEntity_GetSpeed00); - tolua_function(tolua_S,"GetSpeedX",tolua_AllToLua_cEntity_GetSpeedX00); - tolua_function(tolua_S,"GetSpeedY",tolua_AllToLua_cEntity_GetSpeedY00); - tolua_function(tolua_S,"GetSpeedZ",tolua_AllToLua_cEntity_GetSpeedZ00); - tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cEntity_GetWidth00); - tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cEntity_GetChunkX00); - tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cEntity_GetChunkZ00); - tolua_function(tolua_S,"SetHeadYaw",tolua_AllToLua_cEntity_SetHeadYaw00); - tolua_function(tolua_S,"SetHeight",tolua_AllToLua_cEntity_SetHeight00); - tolua_function(tolua_S,"SetMass",tolua_AllToLua_cEntity_SetMass00); - tolua_function(tolua_S,"SetPosX",tolua_AllToLua_cEntity_SetPosX00); - tolua_function(tolua_S,"SetPosY",tolua_AllToLua_cEntity_SetPosY00); - tolua_function(tolua_S,"SetPosZ",tolua_AllToLua_cEntity_SetPosZ00); - tolua_function(tolua_S,"SetPosition",tolua_AllToLua_cEntity_SetPosition00); - tolua_function(tolua_S,"SetPosition",tolua_AllToLua_cEntity_SetPosition01); - tolua_function(tolua_S,"SetRot",tolua_AllToLua_cEntity_SetRot00); - tolua_function(tolua_S,"SetRotation",tolua_AllToLua_cEntity_SetRotation00); - tolua_function(tolua_S,"SetYaw",tolua_AllToLua_cEntity_SetYaw00); - tolua_function(tolua_S,"SetPitch",tolua_AllToLua_cEntity_SetPitch00); - tolua_function(tolua_S,"SetRoll",tolua_AllToLua_cEntity_SetRoll00); - tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed00); - tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed01); - tolua_function(tolua_S,"SetSpeedX",tolua_AllToLua_cEntity_SetSpeedX00); - tolua_function(tolua_S,"SetSpeedY",tolua_AllToLua_cEntity_SetSpeedY00); - tolua_function(tolua_S,"SetSpeedZ",tolua_AllToLua_cEntity_SetSpeedZ00); - tolua_function(tolua_S,"SetWidth",tolua_AllToLua_cEntity_SetWidth00); - tolua_function(tolua_S,"AddPosX",tolua_AllToLua_cEntity_AddPosX00); - tolua_function(tolua_S,"AddPosY",tolua_AllToLua_cEntity_AddPosY00); - tolua_function(tolua_S,"AddPosZ",tolua_AllToLua_cEntity_AddPosZ00); - tolua_function(tolua_S,"AddPosition",tolua_AllToLua_cEntity_AddPosition00); - tolua_function(tolua_S,"AddPosition",tolua_AllToLua_cEntity_AddPosition01); - tolua_function(tolua_S,"AddSpeed",tolua_AllToLua_cEntity_AddSpeed00); - tolua_function(tolua_S,"AddSpeed",tolua_AllToLua_cEntity_AddSpeed01); - tolua_function(tolua_S,"AddSpeedX",tolua_AllToLua_cEntity_AddSpeedX00); - tolua_function(tolua_S,"AddSpeedY",tolua_AllToLua_cEntity_AddSpeedY00); - tolua_function(tolua_S,"AddSpeedZ",tolua_AllToLua_cEntity_AddSpeedZ00); - tolua_function(tolua_S,"SteerVehicle",tolua_AllToLua_cEntity_SteerVehicle00); - tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cEntity_GetUniqueID00); - tolua_function(tolua_S,"IsDestroyed",tolua_AllToLua_cEntity_IsDestroyed00); - tolua_function(tolua_S,"Destroy",tolua_AllToLua_cEntity_Destroy00); - tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage00); - tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage01); - tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage02); - tolua_function(tolua_S,"GetGravity",tolua_AllToLua_cEntity_GetGravity00); - tolua_function(tolua_S,"SetGravity",tolua_AllToLua_cEntity_SetGravity00); - tolua_function(tolua_S,"SetRotationFromSpeed",tolua_AllToLua_cEntity_SetRotationFromSpeed00); - tolua_function(tolua_S,"SetPitchFromSpeed",tolua_AllToLua_cEntity_SetPitchFromSpeed00); - tolua_function(tolua_S,"GetRawDamageAgainst",tolua_AllToLua_cEntity_GetRawDamageAgainst00); - tolua_function(tolua_S,"GetArmorCoverAgainst",tolua_AllToLua_cEntity_GetArmorCoverAgainst00); - tolua_function(tolua_S,"GetKnockbackAmountAgainst",tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00); - tolua_function(tolua_S,"GetEquippedWeapon",tolua_AllToLua_cEntity_GetEquippedWeapon00); - tolua_function(tolua_S,"GetEquippedHelmet",tolua_AllToLua_cEntity_GetEquippedHelmet00); - tolua_function(tolua_S,"GetEquippedChestplate",tolua_AllToLua_cEntity_GetEquippedChestplate00); - tolua_function(tolua_S,"GetEquippedLeggings",tolua_AllToLua_cEntity_GetEquippedLeggings00); - tolua_function(tolua_S,"GetEquippedBoots",tolua_AllToLua_cEntity_GetEquippedBoots00); - tolua_function(tolua_S,"KilledBy",tolua_AllToLua_cEntity_KilledBy00); - tolua_function(tolua_S,"Heal",tolua_AllToLua_cEntity_Heal00); - tolua_function(tolua_S,"GetHealth",tolua_AllToLua_cEntity_GetHealth00); - tolua_function(tolua_S,"SetHealth",tolua_AllToLua_cEntity_SetHealth00); - tolua_function(tolua_S,"SetMaxHealth",tolua_AllToLua_cEntity_SetMaxHealth00); - tolua_function(tolua_S,"GetMaxHealth",tolua_AllToLua_cEntity_GetMaxHealth00); - tolua_function(tolua_S,"StartBurning",tolua_AllToLua_cEntity_StartBurning00); - tolua_function(tolua_S,"StopBurning",tolua_AllToLua_cEntity_StopBurning00); - tolua_function(tolua_S,"TeleportToEntity",tolua_AllToLua_cEntity_TeleportToEntity00); - tolua_function(tolua_S,"TeleportToCoords",tolua_AllToLua_cEntity_TeleportToCoords00); - tolua_function(tolua_S,"IsOnFire",tolua_AllToLua_cEntity_IsOnFire00); - tolua_function(tolua_S,"IsCrouched",tolua_AllToLua_cEntity_IsCrouched00); - tolua_function(tolua_S,"IsRiding",tolua_AllToLua_cEntity_IsRiding00); - tolua_function(tolua_S,"IsSprinting",tolua_AllToLua_cEntity_IsSprinting00); - tolua_function(tolua_S,"IsRclking",tolua_AllToLua_cEntity_IsRclking00); - tolua_function(tolua_S,"IsInvisible",tolua_AllToLua_cEntity_IsInvisible00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cPawn","cPawn","cEntity",NULL); - tolua_beginmodule(tolua_S,"cPawn"); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cPlayer","cPlayer","cPawn",NULL); - tolua_beginmodule(tolua_S,"cPlayer"); - tolua_constant(tolua_S,"MAX_HEALTH",cPlayer::MAX_HEALTH); - tolua_constant(tolua_S,"MAX_FOOD_LEVEL",cPlayer::MAX_FOOD_LEVEL); - tolua_constant(tolua_S,"EATING_TICKS",cPlayer::EATING_TICKS); - tolua_constant(tolua_S,"MAX_AIR_LEVEL",cPlayer::MAX_AIR_LEVEL); - tolua_constant(tolua_S,"DROWNING_TICKS",cPlayer::DROWNING_TICKS); - tolua_function(tolua_S,"SetExperience",tolua_AllToLua_cPlayer_SetExperience00); - tolua_function(tolua_S,"AddExperience",tolua_AllToLua_cPlayer_AddExperience00); - tolua_function(tolua_S,"XpGetTotal",tolua_AllToLua_cPlayer_XpGetTotal00); - tolua_function(tolua_S,"XpGetLevel",tolua_AllToLua_cPlayer_XpGetLevel00); - tolua_function(tolua_S,"XpGetPercentage",tolua_AllToLua_cPlayer_XpGetPercentage00); - tolua_function(tolua_S,"GetEyeHeight",tolua_AllToLua_cPlayer_GetEyeHeight00); - tolua_function(tolua_S,"GetEyePosition",tolua_AllToLua_cPlayer_GetEyePosition00); - tolua_function(tolua_S,"IsOnGround",tolua_AllToLua_cPlayer_IsOnGround00); - tolua_function(tolua_S,"GetStance",tolua_AllToLua_cPlayer_GetStance00); - tolua_function(tolua_S,"GetInventory",tolua_AllToLua_cPlayer_GetInventory00); - tolua_function(tolua_S,"GetEquippedItem",tolua_AllToLua_cPlayer_GetEquippedItem00); - tolua_function(tolua_S,"GetThrowStartPos",tolua_AllToLua_cPlayer_GetThrowStartPos00); - tolua_function(tolua_S,"GetThrowSpeed",tolua_AllToLua_cPlayer_GetThrowSpeed00); - tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cPlayer_GetGameMode00); - tolua_function(tolua_S,"GetEffectiveGameMode",tolua_AllToLua_cPlayer_GetEffectiveGameMode00); - tolua_function(tolua_S,"SetGameMode",tolua_AllToLua_cPlayer_SetGameMode00); - tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cPlayer_IsGameModeCreative00); - tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cPlayer_IsGameModeSurvival00); - tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cPlayer_IsGameModeAdventure00); - tolua_function(tolua_S,"GetIP",tolua_AllToLua_cPlayer_GetIP00); - tolua_function(tolua_S,"MoveTo",tolua_AllToLua_cPlayer_MoveTo00); - tolua_function(tolua_S,"GetWindow",tolua_AllToLua_cPlayer_GetWindow00); - tolua_function(tolua_S,"CloseWindow",tolua_AllToLua_cPlayer_CloseWindow00); - tolua_function(tolua_S,"CloseWindowIfID",tolua_AllToLua_cPlayer_CloseWindowIfID00); - tolua_function(tolua_S,"GetClientHandle",tolua_AllToLua_cPlayer_GetClientHandle00); - tolua_function(tolua_S,"SendMessage",tolua_AllToLua_cPlayer_SendMessage00); - tolua_function(tolua_S,"GetName",tolua_AllToLua_cPlayer_GetName00); - tolua_function(tolua_S,"SetName",tolua_AllToLua_cPlayer_SetName00); - tolua_function(tolua_S,"AddToGroup",tolua_AllToLua_cPlayer_AddToGroup00); - tolua_function(tolua_S,"RemoveFromGroup",tolua_AllToLua_cPlayer_RemoveFromGroup00); - tolua_function(tolua_S,"CanUseCommand",tolua_AllToLua_cPlayer_CanUseCommand00); - tolua_function(tolua_S,"HasPermission",tolua_AllToLua_cPlayer_HasPermission00); - tolua_function(tolua_S,"IsInGroup",tolua_AllToLua_cPlayer_IsInGroup00); - tolua_function(tolua_S,"GetColor",tolua_AllToLua_cPlayer_GetColor00); - tolua_function(tolua_S,"TossItem",tolua_AllToLua_cPlayer_TossItem00); - tolua_function(tolua_S,"Heal",tolua_AllToLua_cPlayer_Heal00); - tolua_function(tolua_S,"GetFoodLevel",tolua_AllToLua_cPlayer_GetFoodLevel00); - tolua_function(tolua_S,"GetFoodSaturationLevel",tolua_AllToLua_cPlayer_GetFoodSaturationLevel00); - tolua_function(tolua_S,"GetFoodTickTimer",tolua_AllToLua_cPlayer_GetFoodTickTimer00); - tolua_function(tolua_S,"GetFoodExhaustionLevel",tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00); - tolua_function(tolua_S,"GetFoodPoisonedTicksRemaining",tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00); - tolua_function(tolua_S,"GetAirLevel",tolua_AllToLua_cPlayer_GetAirLevel00); - tolua_function(tolua_S,"IsSatiated",tolua_AllToLua_cPlayer_IsSatiated00); - tolua_function(tolua_S,"SetFoodLevel",tolua_AllToLua_cPlayer_SetFoodLevel00); - tolua_function(tolua_S,"SetFoodSaturationLevel",tolua_AllToLua_cPlayer_SetFoodSaturationLevel00); - tolua_function(tolua_S,"SetFoodTickTimer",tolua_AllToLua_cPlayer_SetFoodTickTimer00); - tolua_function(tolua_S,"SetFoodExhaustionLevel",tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00); - tolua_function(tolua_S,"SetFoodPoisonedTicksRemaining",tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00); - tolua_function(tolua_S,"Feed",tolua_AllToLua_cPlayer_Feed00); - tolua_function(tolua_S,"AddFoodExhaustion",tolua_AllToLua_cPlayer_AddFoodExhaustion00); - tolua_function(tolua_S,"FoodPoison",tolua_AllToLua_cPlayer_FoodPoison00); - tolua_function(tolua_S,"IsEating",tolua_AllToLua_cPlayer_IsEating00); - tolua_function(tolua_S,"Respawn",tolua_AllToLua_cPlayer_Respawn00); - tolua_function(tolua_S,"SetVisible",tolua_AllToLua_cPlayer_SetVisible00); - tolua_function(tolua_S,"IsVisible",tolua_AllToLua_cPlayer_IsVisible00); - tolua_function(tolua_S,"MoveToWorld",tolua_AllToLua_cPlayer_MoveToWorld00); - tolua_function(tolua_S,"LoadPermissionsFromDisk",tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00); - tolua_function(tolua_S,"GetMaxSpeed",tolua_AllToLua_cPlayer_GetMaxSpeed00); - tolua_function(tolua_S,"GetNormalMaxSpeed",tolua_AllToLua_cPlayer_GetNormalMaxSpeed00); - tolua_function(tolua_S,"GetSprintingMaxSpeed",tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00); - tolua_function(tolua_S,"SetNormalMaxSpeed",tolua_AllToLua_cPlayer_SetNormalMaxSpeed00); - tolua_function(tolua_S,"SetSprintingMaxSpeed",tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00); - tolua_function(tolua_S,"SetCrouch",tolua_AllToLua_cPlayer_SetCrouch00); - tolua_function(tolua_S,"SetSprint",tolua_AllToLua_cPlayer_SetSprint00); - tolua_function(tolua_S,"IsSwimming",tolua_AllToLua_cPlayer_IsSwimming00); - tolua_function(tolua_S,"IsSubmerged",tolua_AllToLua_cPlayer_IsSubmerged00); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cPickup","cPickup","cEntity",tolua_collect_cPickup); - #else - tolua_cclass(tolua_S,"cPickup","cPickup","cEntity",NULL); - #endif - tolua_beginmodule(tolua_S,"cPickup"); - tolua_function(tolua_S,"new",tolua_AllToLua_cPickup_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cPickup_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cPickup_new00_local); - tolua_function(tolua_S,"GetItem",tolua_AllToLua_cPickup_GetItem00); - tolua_function(tolua_S,"CollectedBy",tolua_AllToLua_cPickup_CollectedBy00); - tolua_function(tolua_S,"GetAge",tolua_AllToLua_cPickup_GetAge00); - tolua_function(tolua_S,"IsCollected",tolua_AllToLua_cPickup_IsCollected00); - tolua_function(tolua_S,"IsPlayerCreated",tolua_AllToLua_cPickup_IsPlayerCreated00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cProjectileEntity","cProjectileEntity","cEntity",NULL); - tolua_beginmodule(tolua_S,"cProjectileEntity"); - tolua_constant(tolua_S,"pkArrow",cProjectileEntity::pkArrow); - tolua_constant(tolua_S,"pkSnowball",cProjectileEntity::pkSnowball); - tolua_constant(tolua_S,"pkEgg",cProjectileEntity::pkEgg); - tolua_constant(tolua_S,"pkGhastFireball",cProjectileEntity::pkGhastFireball); - tolua_constant(tolua_S,"pkFireCharge",cProjectileEntity::pkFireCharge); - tolua_constant(tolua_S,"pkEnderPearl",cProjectileEntity::pkEnderPearl); - tolua_constant(tolua_S,"pkExpBottle",cProjectileEntity::pkExpBottle); - tolua_constant(tolua_S,"pkSplashPotion",cProjectileEntity::pkSplashPotion); - tolua_constant(tolua_S,"pkWitherSkull",cProjectileEntity::pkWitherSkull); - tolua_constant(tolua_S,"pkFishingFloat",cProjectileEntity::pkFishingFloat); - tolua_function(tolua_S,"GetProjectileKind",tolua_AllToLua_cProjectileEntity_GetProjectileKind00); - tolua_function(tolua_S,"GetCreator",tolua_AllToLua_cProjectileEntity_GetCreator00); - tolua_function(tolua_S,"GetMCAClassName",tolua_AllToLua_cProjectileEntity_GetMCAClassName00); - tolua_function(tolua_S,"IsInGround",tolua_AllToLua_cProjectileEntity_IsInGround00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cArrowEntity","cArrowEntity","cProjectileEntity",NULL); - tolua_beginmodule(tolua_S,"cArrowEntity"); - tolua_constant(tolua_S,"psNoPickup",cArrowEntity::psNoPickup); - tolua_constant(tolua_S,"psInSurvivalOrCreative",cArrowEntity::psInSurvivalOrCreative); - tolua_constant(tolua_S,"psInCreative",cArrowEntity::psInCreative); - tolua_function(tolua_S,"GetPickupState",tolua_AllToLua_cArrowEntity_GetPickupState00); - tolua_function(tolua_S,"SetPickupState",tolua_AllToLua_cArrowEntity_SetPickupState00); - tolua_function(tolua_S,"GetDamageCoeff",tolua_AllToLua_cArrowEntity_GetDamageCoeff00); - tolua_function(tolua_S,"SetDamageCoeff",tolua_AllToLua_cArrowEntity_SetDamageCoeff00); - tolua_function(tolua_S,"CanPickup",tolua_AllToLua_cArrowEntity_CanPickup00); - tolua_function(tolua_S,"IsCritical",tolua_AllToLua_cArrowEntity_IsCritical00); - tolua_function(tolua_S,"SetIsCritical",tolua_AllToLua_cArrowEntity_SetIsCritical00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cThrownEggEntity","cThrownEggEntity","cProjectileEntity",NULL); - tolua_beginmodule(tolua_S,"cThrownEggEntity"); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cThrownEnderPearlEntity","cThrownEnderPearlEntity","cProjectileEntity",NULL); - tolua_beginmodule(tolua_S,"cThrownEnderPearlEntity"); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cThrownSnowballEntity","cThrownSnowballEntity","cProjectileEntity",NULL); - tolua_beginmodule(tolua_S,"cThrownSnowballEntity"); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cGhastFireballEntity","cGhastFireballEntity","cProjectileEntity",NULL); - tolua_beginmodule(tolua_S,"cGhastFireballEntity"); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cFireChargeEntity","cFireChargeEntity","cProjectileEntity",NULL); - tolua_beginmodule(tolua_S,"cFireChargeEntity"); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cPluginManager","cPluginManager","",NULL); - tolua_beginmodule(tolua_S,"cPluginManager"); - tolua_constant(tolua_S,"HOOK_BLOCK_TO_PICKUPS",cPluginManager::HOOK_BLOCK_TO_PICKUPS); - tolua_constant(tolua_S,"HOOK_CHAT",cPluginManager::HOOK_CHAT); - tolua_constant(tolua_S,"HOOK_CHUNK_AVAILABLE",cPluginManager::HOOK_CHUNK_AVAILABLE); - tolua_constant(tolua_S,"HOOK_CHUNK_GENERATED",cPluginManager::HOOK_CHUNK_GENERATED); - tolua_constant(tolua_S,"HOOK_CHUNK_GENERATING",cPluginManager::HOOK_CHUNK_GENERATING); - tolua_constant(tolua_S,"HOOK_CHUNK_UNLOADED",cPluginManager::HOOK_CHUNK_UNLOADED); - tolua_constant(tolua_S,"HOOK_CHUNK_UNLOADING",cPluginManager::HOOK_CHUNK_UNLOADING); - tolua_constant(tolua_S,"HOOK_COLLECTING_PICKUP",cPluginManager::HOOK_COLLECTING_PICKUP); - tolua_constant(tolua_S,"HOOK_CRAFTING_NO_RECIPE",cPluginManager::HOOK_CRAFTING_NO_RECIPE); - tolua_constant(tolua_S,"HOOK_DISCONNECT",cPluginManager::HOOK_DISCONNECT); - tolua_constant(tolua_S,"HOOK_EXECUTE_COMMAND",cPluginManager::HOOK_EXECUTE_COMMAND); - tolua_constant(tolua_S,"HOOK_EXPLODED",cPluginManager::HOOK_EXPLODED); - tolua_constant(tolua_S,"HOOK_EXPLODING",cPluginManager::HOOK_EXPLODING); - tolua_constant(tolua_S,"HOOK_HANDSHAKE",cPluginManager::HOOK_HANDSHAKE); - tolua_constant(tolua_S,"HOOK_HOPPER_PULLING_ITEM",cPluginManager::HOOK_HOPPER_PULLING_ITEM); - tolua_constant(tolua_S,"HOOK_HOPPER_PUSHING_ITEM",cPluginManager::HOOK_HOPPER_PUSHING_ITEM); - tolua_constant(tolua_S,"HOOK_KILLING",cPluginManager::HOOK_KILLING); - tolua_constant(tolua_S,"HOOK_LOGIN",cPluginManager::HOOK_LOGIN); - tolua_constant(tolua_S,"HOOK_PLAYER_ANIMATION",cPluginManager::HOOK_PLAYER_ANIMATION); - tolua_constant(tolua_S,"HOOK_PLAYER_BREAKING_BLOCK",cPluginManager::HOOK_PLAYER_BREAKING_BLOCK); - tolua_constant(tolua_S,"HOOK_PLAYER_BROKEN_BLOCK",cPluginManager::HOOK_PLAYER_BROKEN_BLOCK); - tolua_constant(tolua_S,"HOOK_PLAYER_EATING",cPluginManager::HOOK_PLAYER_EATING); - tolua_constant(tolua_S,"HOOK_PLAYER_JOINED",cPluginManager::HOOK_PLAYER_JOINED); - tolua_constant(tolua_S,"HOOK_PLAYER_LEFT_CLICK",cPluginManager::HOOK_PLAYER_LEFT_CLICK); - tolua_constant(tolua_S,"HOOK_PLAYER_MOVING",cPluginManager::HOOK_PLAYER_MOVING); - tolua_constant(tolua_S,"HOOK_PLAYER_PLACED_BLOCK",cPluginManager::HOOK_PLAYER_PLACED_BLOCK); - tolua_constant(tolua_S,"HOOK_PLAYER_PLACING_BLOCK",cPluginManager::HOOK_PLAYER_PLACING_BLOCK); - tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICK",cPluginManager::HOOK_PLAYER_RIGHT_CLICK); - tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICKING_ENTITY",cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY); - tolua_constant(tolua_S,"HOOK_PLAYER_SHOOTING",cPluginManager::HOOK_PLAYER_SHOOTING); - tolua_constant(tolua_S,"HOOK_PLAYER_SPAWNED",cPluginManager::HOOK_PLAYER_SPAWNED); - tolua_constant(tolua_S,"HOOK_PLAYER_TOSSING_ITEM",cPluginManager::HOOK_PLAYER_TOSSING_ITEM); - tolua_constant(tolua_S,"HOOK_PLAYER_USED_BLOCK",cPluginManager::HOOK_PLAYER_USED_BLOCK); - tolua_constant(tolua_S,"HOOK_PLAYER_USED_ITEM",cPluginManager::HOOK_PLAYER_USED_ITEM); - tolua_constant(tolua_S,"HOOK_PLAYER_USING_BLOCK",cPluginManager::HOOK_PLAYER_USING_BLOCK); - tolua_constant(tolua_S,"HOOK_PLAYER_USING_ITEM",cPluginManager::HOOK_PLAYER_USING_ITEM); - tolua_constant(tolua_S,"HOOK_POST_CRAFTING",cPluginManager::HOOK_POST_CRAFTING); - tolua_constant(tolua_S,"HOOK_PRE_CRAFTING",cPluginManager::HOOK_PRE_CRAFTING); - tolua_constant(tolua_S,"HOOK_SPAWNED_ENTITY",cPluginManager::HOOK_SPAWNED_ENTITY); - tolua_constant(tolua_S,"HOOK_SPAWNED_MONSTER",cPluginManager::HOOK_SPAWNED_MONSTER); - tolua_constant(tolua_S,"HOOK_SPAWNING_ENTITY",cPluginManager::HOOK_SPAWNING_ENTITY); - tolua_constant(tolua_S,"HOOK_SPAWNING_MONSTER",cPluginManager::HOOK_SPAWNING_MONSTER); - tolua_constant(tolua_S,"HOOK_TAKE_DAMAGE",cPluginManager::HOOK_TAKE_DAMAGE); - tolua_constant(tolua_S,"HOOK_TICK",cPluginManager::HOOK_TICK); - tolua_constant(tolua_S,"HOOK_UPDATED_SIGN",cPluginManager::HOOK_UPDATED_SIGN); - tolua_constant(tolua_S,"HOOK_UPDATING_SIGN",cPluginManager::HOOK_UPDATING_SIGN); - tolua_constant(tolua_S,"HOOK_WEATHER_CHANGED",cPluginManager::HOOK_WEATHER_CHANGED); - tolua_constant(tolua_S,"HOOK_WEATHER_CHANGING",cPluginManager::HOOK_WEATHER_CHANGING); - tolua_constant(tolua_S,"HOOK_WORLD_TICK",cPluginManager::HOOK_WORLD_TICK); - tolua_constant(tolua_S,"HOOK_NUM_HOOKS",cPluginManager::HOOK_NUM_HOOKS); - tolua_constant(tolua_S,"HOOK_MAX",cPluginManager::HOOK_MAX); - tolua_function(tolua_S,"Get",tolua_AllToLua_cPluginManager_Get00); - tolua_function(tolua_S,"GetPlugin",tolua_AllToLua_cPluginManager_GetPlugin00); - tolua_function(tolua_S,"FindPlugins",tolua_AllToLua_cPluginManager_FindPlugins00); - tolua_function(tolua_S,"ReloadPlugins",tolua_AllToLua_cPluginManager_ReloadPlugins00); - tolua_function(tolua_S,"GetNumPlugins",tolua_AllToLua_cPluginManager_GetNumPlugins00); - tolua_function(tolua_S,"DisablePlugin",tolua_AllToLua_cPluginManager_DisablePlugin00); - tolua_function(tolua_S,"LoadPlugin",tolua_AllToLua_cPluginManager_LoadPlugin00); - tolua_function(tolua_S,"IsCommandBound",tolua_AllToLua_cPluginManager_IsCommandBound00); - tolua_function(tolua_S,"GetCommandPermission",tolua_AllToLua_cPluginManager_GetCommandPermission00); - tolua_function(tolua_S,"ExecuteCommand",tolua_AllToLua_cPluginManager_ExecuteCommand00); - tolua_function(tolua_S,"ForceExecuteCommand",tolua_AllToLua_cPluginManager_ForceExecuteCommand00); - tolua_function(tolua_S,"IsConsoleCommandBound",tolua_AllToLua_cPluginManager_IsConsoleCommandBound00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cPlugin","cPlugin","",NULL); - tolua_beginmodule(tolua_S,"cPlugin"); - tolua_function(tolua_S,"GetName",tolua_AllToLua_cPlugin_GetName00); - tolua_function(tolua_S,"SetName",tolua_AllToLua_cPlugin_SetName00); - tolua_function(tolua_S,"GetVersion",tolua_AllToLua_cPlugin_GetVersion00); - tolua_function(tolua_S,"SetVersion",tolua_AllToLua_cPlugin_SetVersion00); - tolua_function(tolua_S,"GetDirectory",tolua_AllToLua_cPlugin_GetDirectory00); - tolua_function(tolua_S,"GetLocalDirectory",tolua_AllToLua_cPlugin_GetLocalDirectory00); - tolua_function(tolua_S,"GetLocalFolder",tolua_AllToLua_cPlugin_GetLocalFolder00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cPluginLua","cPluginLua","cPlugin",NULL); - tolua_beginmodule(tolua_S,"cPluginLua"); - tolua_variable(tolua_S,"__cWebPlugin__",tolua_get_cPluginLua___cWebPlugin__,NULL); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cServer","cServer","",NULL); - tolua_beginmodule(tolua_S,"cServer"); - tolua_function(tolua_S,"GetDescription",tolua_AllToLua_cServer_GetDescription00); - tolua_function(tolua_S,"GetMaxPlayers",tolua_AllToLua_cServer_GetMaxPlayers00); - tolua_function(tolua_S,"GetNumPlayers",tolua_AllToLua_cServer_GetNumPlayers00); - tolua_function(tolua_S,"SetMaxPlayers",tolua_AllToLua_cServer_SetMaxPlayers00); - tolua_function(tolua_S,"IsHardcore",tolua_AllToLua_cServer_IsHardcore00); - tolua_function(tolua_S,"GetServerID",tolua_AllToLua_cServer_GetServerID00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cWorld","cWorld","",NULL); - tolua_beginmodule(tolua_S,"cWorld"); - tolua_function(tolua_S,"GetTicksUntilWeatherChange",tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00); - tolua_function(tolua_S,"GetWorldAge",tolua_AllToLua_cWorld_GetWorldAge00); - tolua_function(tolua_S,"GetTimeOfDay",tolua_AllToLua_cWorld_GetTimeOfDay00); - tolua_function(tolua_S,"SetTicksUntilWeatherChange",tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00); - tolua_function(tolua_S,"SetTimeOfDay",tolua_AllToLua_cWorld_SetTimeOfDay00); - tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cWorld_GetGameMode00); - tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cWorld_IsGameModeCreative00); - tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cWorld_IsGameModeSurvival00); - tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cWorld_IsGameModeAdventure00); - tolua_function(tolua_S,"IsPVPEnabled",tolua_AllToLua_cWorld_IsPVPEnabled00); - tolua_function(tolua_S,"IsDeepSnowEnabled",tolua_AllToLua_cWorld_IsDeepSnowEnabled00); - tolua_function(tolua_S,"GetDimension",tolua_AllToLua_cWorld_GetDimension00); - tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cWorld_GetHeight00); - tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cWorld_BroadcastChat00); - tolua_function(tolua_S,"BroadcastSoundEffect",tolua_AllToLua_cWorld_BroadcastSoundEffect00); - tolua_function(tolua_S,"BroadcastSoundParticleEffect",tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00); - tolua_function(tolua_S,"UnloadUnusedChunks",tolua_AllToLua_cWorld_UnloadUnusedChunks00); - tolua_function(tolua_S,"RegenerateChunk",tolua_AllToLua_cWorld_RegenerateChunk00); - tolua_function(tolua_S,"GenerateChunk",tolua_AllToLua_cWorld_GenerateChunk00); - tolua_function(tolua_S,"SetBlock",tolua_AllToLua_cWorld_SetBlock00); - tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock00); - tolua_function(tolua_S,"QueueSetBlock",tolua_AllToLua_cWorld_QueueSetBlock00); - tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock00); - tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cWorld_GetBlockMeta00); - tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta00); - tolua_function(tolua_S,"GetBlockSkyLight",tolua_AllToLua_cWorld_GetBlockSkyLight00); - tolua_function(tolua_S,"GetBlockBlockLight",tolua_AllToLua_cWorld_GetBlockBlockLight00); - tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock01); - tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock01); - tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cWorld_GetBlockMeta01); - tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta01); - tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups00); - tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups01); - tolua_function(tolua_S,"SpawnPrimedTNT",tolua_AllToLua_cWorld_SpawnPrimedTNT00); - tolua_function(tolua_S,"DigBlock",tolua_AllToLua_cWorld_DigBlock00); - tolua_function(tolua_S,"SendBlockTo",tolua_AllToLua_cWorld_SendBlockTo00); - tolua_function(tolua_S,"GetSpawnX",tolua_AllToLua_cWorld_GetSpawnX00); - tolua_function(tolua_S,"GetSpawnY",tolua_AllToLua_cWorld_GetSpawnY00); - tolua_function(tolua_S,"GetSpawnZ",tolua_AllToLua_cWorld_GetSpawnZ00); - tolua_function(tolua_S,"WakeUpSimulators",tolua_AllToLua_cWorld_WakeUpSimulators00); - tolua_function(tolua_S,"WakeUpSimulatorsInArea",tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00); - tolua_function(tolua_S,"DoExplosionAt",tolua_AllToLua_cWorld_DoExplosionAt00); - tolua_function(tolua_S,"UseBlockEntity",tolua_AllToLua_cWorld_UseBlockEntity00); - tolua_function(tolua_S,"GrowTree",tolua_AllToLua_cWorld_GrowTree00); - tolua_function(tolua_S,"GrowTreeFromSapling",tolua_AllToLua_cWorld_GrowTreeFromSapling00); - tolua_function(tolua_S,"GrowTreeByBiome",tolua_AllToLua_cWorld_GrowTreeByBiome00); - tolua_function(tolua_S,"GrowRipePlant",tolua_AllToLua_cWorld_GrowRipePlant00); - tolua_function(tolua_S,"GrowCactus",tolua_AllToLua_cWorld_GrowCactus00); - tolua_function(tolua_S,"GrowMelonPumpkin",tolua_AllToLua_cWorld_GrowMelonPumpkin00); - tolua_function(tolua_S,"GrowSugarcane",tolua_AllToLua_cWorld_GrowSugarcane00); - tolua_function(tolua_S,"GetBiomeAt",tolua_AllToLua_cWorld_GetBiomeAt00); - tolua_function(tolua_S,"GetName",tolua_AllToLua_cWorld_GetName00); - tolua_function(tolua_S,"GetIniFileName",tolua_AllToLua_cWorld_GetIniFileName00); - tolua_function(tolua_S,"QueueSaveAllChunks",tolua_AllToLua_cWorld_QueueSaveAllChunks00); - tolua_function(tolua_S,"GetNumChunks",tolua_AllToLua_cWorld_GetNumChunks00); - tolua_function(tolua_S,"GetGeneratorQueueLength",tolua_AllToLua_cWorld_GetGeneratorQueueLength00); - tolua_function(tolua_S,"GetLightingQueueLength",tolua_AllToLua_cWorld_GetLightingQueueLength00); - tolua_function(tolua_S,"GetStorageLoadQueueLength",tolua_AllToLua_cWorld_GetStorageLoadQueueLength00); - tolua_function(tolua_S,"GetStorageSaveQueueLength",tolua_AllToLua_cWorld_GetStorageSaveQueueLength00); - tolua_function(tolua_S,"QueueBlockForTick",tolua_AllToLua_cWorld_QueueBlockForTick00); - tolua_function(tolua_S,"CastThunderbolt",tolua_AllToLua_cWorld_CastThunderbolt00); - tolua_function(tolua_S,"SetWeather",tolua_AllToLua_cWorld_SetWeather00); - tolua_function(tolua_S,"ChangeWeather",tolua_AllToLua_cWorld_ChangeWeather00); - tolua_function(tolua_S,"GetWeather",tolua_AllToLua_cWorld_GetWeather00); - tolua_function(tolua_S,"IsWeatherSunny",tolua_AllToLua_cWorld_IsWeatherSunny00); - tolua_function(tolua_S,"IsWeatherRain",tolua_AllToLua_cWorld_IsWeatherRain00); - tolua_function(tolua_S,"IsWeatherStorm",tolua_AllToLua_cWorld_IsWeatherStorm00); - tolua_function(tolua_S,"IsWeatherWet",tolua_AllToLua_cWorld_IsWeatherWet00); - tolua_function(tolua_S,"SetNextBlockTick",tolua_AllToLua_cWorld_SetNextBlockTick00); - tolua_function(tolua_S,"GetMaxSugarcaneHeight",tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00); - tolua_function(tolua_S,"GetMaxCactusHeight",tolua_AllToLua_cWorld_GetMaxCactusHeight00); - tolua_function(tolua_S,"IsBlockDirectlyWatered",tolua_AllToLua_cWorld_IsBlockDirectlyWatered00); - tolua_function(tolua_S,"SpawnMob",tolua_AllToLua_cWorld_SpawnMob00); - tolua_function(tolua_S,"CreateProjectile",tolua_AllToLua_cWorld_CreateProjectile00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cInventory","cInventory","cItemGrid::cListener",NULL); - tolua_beginmodule(tolua_S,"cInventory"); - tolua_constant(tolua_S,"invArmorCount",cInventory::invArmorCount); - tolua_constant(tolua_S,"invInventoryCount",cInventory::invInventoryCount); - tolua_constant(tolua_S,"invHotbarCount",cInventory::invHotbarCount); - tolua_constant(tolua_S,"invArmorOffset",cInventory::invArmorOffset); - tolua_constant(tolua_S,"invInventoryOffset",cInventory::invInventoryOffset); - tolua_constant(tolua_S,"invHotbarOffset",cInventory::invHotbarOffset); - tolua_constant(tolua_S,"invNumSlots",cInventory::invNumSlots); - tolua_function(tolua_S,"Clear",tolua_AllToLua_cInventory_Clear00); - tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cInventory_HowManyCanFit00); - tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cInventory_HowManyCanFit01); - tolua_function(tolua_S,"AddItem",tolua_AllToLua_cInventory_AddItem00); - tolua_function(tolua_S,"AddItems",tolua_AllToLua_cInventory_AddItems00); - tolua_function(tolua_S,"RemoveOneEquippedItem",tolua_AllToLua_cInventory_RemoveOneEquippedItem00); - tolua_function(tolua_S,"HowManyItems",tolua_AllToLua_cInventory_HowManyItems00); - tolua_function(tolua_S,"HasItems",tolua_AllToLua_cInventory_HasItems00); - tolua_function(tolua_S,"GetArmorGrid",tolua_AllToLua_cInventory_GetArmorGrid00); - tolua_function(tolua_S,"GetInventoryGrid",tolua_AllToLua_cInventory_GetInventoryGrid00); - tolua_function(tolua_S,"GetHotbarGrid",tolua_AllToLua_cInventory_GetHotbarGrid00); - tolua_function(tolua_S,"GetOwner",tolua_AllToLua_cInventory_GetOwner00); - tolua_function(tolua_S,"CopyToItems",tolua_AllToLua_cInventory_CopyToItems00); - tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cInventory_GetSlot00); - tolua_function(tolua_S,"GetArmorSlot",tolua_AllToLua_cInventory_GetArmorSlot00); - tolua_function(tolua_S,"GetInventorySlot",tolua_AllToLua_cInventory_GetInventorySlot00); - tolua_function(tolua_S,"GetHotbarSlot",tolua_AllToLua_cInventory_GetHotbarSlot00); - tolua_function(tolua_S,"GetEquippedItem",tolua_AllToLua_cInventory_GetEquippedItem00); - tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cInventory_SetSlot00); - tolua_function(tolua_S,"SetArmorSlot",tolua_AllToLua_cInventory_SetArmorSlot00); - tolua_function(tolua_S,"SetInventorySlot",tolua_AllToLua_cInventory_SetInventorySlot00); - tolua_function(tolua_S,"SetHotbarSlot",tolua_AllToLua_cInventory_SetHotbarSlot00); - tolua_function(tolua_S,"SetEquippedSlotNum",tolua_AllToLua_cInventory_SetEquippedSlotNum00); - tolua_function(tolua_S,"GetEquippedSlotNum",tolua_AllToLua_cInventory_GetEquippedSlotNum00); - tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cInventory_ChangeSlotCount00); - tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cInventory_DamageItem00); - tolua_function(tolua_S,"DamageEquippedItem",tolua_AllToLua_cInventory_DamageEquippedItem00); - tolua_function(tolua_S,"GetEquippedHelmet",tolua_AllToLua_cInventory_GetEquippedHelmet00); - tolua_function(tolua_S,"GetEquippedChestplate",tolua_AllToLua_cInventory_GetEquippedChestplate00); - tolua_function(tolua_S,"GetEquippedLeggings",tolua_AllToLua_cInventory_GetEquippedLeggings00); - tolua_function(tolua_S,"GetEquippedBoots",tolua_AllToLua_cInventory_GetEquippedBoots00); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cEnchantments","cEnchantments","",tolua_collect_cEnchantments); - #else - tolua_cclass(tolua_S,"cEnchantments","cEnchantments","",NULL); - #endif - tolua_beginmodule(tolua_S,"cEnchantments"); - tolua_constant(tolua_S,"enchProtection",cEnchantments::enchProtection); - tolua_constant(tolua_S,"enchFireProtection",cEnchantments::enchFireProtection); - tolua_constant(tolua_S,"enchFeatherFalling",cEnchantments::enchFeatherFalling); - tolua_constant(tolua_S,"enchBlastProtection",cEnchantments::enchBlastProtection); - tolua_constant(tolua_S,"enchProjectileProtection",cEnchantments::enchProjectileProtection); - tolua_constant(tolua_S,"enchRespiration",cEnchantments::enchRespiration); - tolua_constant(tolua_S,"enchAquaAffinity",cEnchantments::enchAquaAffinity); - tolua_constant(tolua_S,"enchThorns",cEnchantments::enchThorns); - tolua_constant(tolua_S,"enchSharpness",cEnchantments::enchSharpness); - tolua_constant(tolua_S,"enchSmite",cEnchantments::enchSmite); - tolua_constant(tolua_S,"enchBaneOfArthropods",cEnchantments::enchBaneOfArthropods); - tolua_constant(tolua_S,"enchKnockback",cEnchantments::enchKnockback); - tolua_constant(tolua_S,"enchFireAspect",cEnchantments::enchFireAspect); - tolua_constant(tolua_S,"enchLooting",cEnchantments::enchLooting); - tolua_constant(tolua_S,"enchEfficiency",cEnchantments::enchEfficiency); - tolua_constant(tolua_S,"enchSilkTouch",cEnchantments::enchSilkTouch); - tolua_constant(tolua_S,"enchUnbreaking",cEnchantments::enchUnbreaking); - tolua_constant(tolua_S,"enchFortune",cEnchantments::enchFortune); - tolua_constant(tolua_S,"enchPower",cEnchantments::enchPower); - tolua_constant(tolua_S,"enchPunch",cEnchantments::enchPunch); - tolua_constant(tolua_S,"enchFlame",cEnchantments::enchFlame); - tolua_constant(tolua_S,"enchInfinity",cEnchantments::enchInfinity); - tolua_constant(tolua_S,"enchLuckOfTheSea",cEnchantments::enchLuckOfTheSea); - tolua_constant(tolua_S,"enchLure",cEnchantments::enchLure); - tolua_function(tolua_S,"new",tolua_AllToLua_cEnchantments_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cEnchantments_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cEnchantments_new00_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cEnchantments_new01); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cEnchantments_new01_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cEnchantments_new01_local); - tolua_function(tolua_S,"AddFromString",tolua_AllToLua_cEnchantments_AddFromString00); - tolua_function(tolua_S,"ToString",tolua_AllToLua_cEnchantments_ToString00); - tolua_function(tolua_S,"GetLevel",tolua_AllToLua_cEnchantments_GetLevel00); - tolua_function(tolua_S,"SetLevel",tolua_AllToLua_cEnchantments_SetLevel00); - tolua_function(tolua_S,"Clear",tolua_AllToLua_cEnchantments_Clear00); - tolua_function(tolua_S,"IsEmpty",tolua_AllToLua_cEnchantments_IsEmpty00); - tolua_function(tolua_S,"StringToEnchantmentID",tolua_AllToLua_cEnchantments_StringToEnchantmentID00); - tolua_function(tolua_S,".eq",tolua_AllToLua_cEnchantments__eq00); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cItem","cItem","",tolua_collect_cItem); - #else - tolua_cclass(tolua_S,"cItem","cItem","",NULL); - #endif - tolua_beginmodule(tolua_S,"cItem"); - tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new00_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new01); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new01_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new01_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new02); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new02_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new02_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new03); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new03_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new03_local); - tolua_function(tolua_S,"Empty",tolua_AllToLua_cItem_Empty00); - tolua_function(tolua_S,"Clear",tolua_AllToLua_cItem_Clear00); - tolua_function(tolua_S,"IsEmpty",tolua_AllToLua_cItem_IsEmpty00); - tolua_function(tolua_S,"IsEqual",tolua_AllToLua_cItem_IsEqual00); - tolua_function(tolua_S,"IsSameType",tolua_AllToLua_cItem_IsSameType00); - tolua_function(tolua_S,"CopyOne",tolua_AllToLua_cItem_CopyOne00); - tolua_function(tolua_S,"AddCount",tolua_AllToLua_cItem_AddCount00); - tolua_function(tolua_S,"GetMaxDamage",tolua_AllToLua_cItem_GetMaxDamage00); - tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItem_DamageItem00); - tolua_function(tolua_S,"IsDamageable",tolua_AllToLua_cItem_IsDamageable00); - tolua_function(tolua_S,"IsStackableWith",tolua_AllToLua_cItem_IsStackableWith00); - tolua_function(tolua_S,"IsFullStack",tolua_AllToLua_cItem_IsFullStack00); - tolua_function(tolua_S,"GetMaxStackSize",tolua_AllToLua_cItem_GetMaxStackSize00); - tolua_variable(tolua_S,"m_ItemType",tolua_get_cItem_m_ItemType,tolua_set_cItem_m_ItemType); - tolua_variable(tolua_S,"m_ItemCount",tolua_get_cItem_m_ItemCount,tolua_set_cItem_m_ItemCount); - tolua_variable(tolua_S,"m_ItemDamage",tolua_get_cItem_m_ItemDamage,tolua_set_cItem_m_ItemDamage); - tolua_variable(tolua_S,"m_Enchantments",tolua_get_cItem_m_Enchantments,tolua_set_cItem_m_Enchantments); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cItems","cItems","",tolua_collect_cItems); - #else - tolua_cclass(tolua_S,"cItems","cItems","",NULL); - #endif - tolua_beginmodule(tolua_S,"cItems"); - tolua_function(tolua_S,"new",tolua_AllToLua_cItems_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cItems_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cItems_new00_local); - tolua_function(tolua_S,"Get",tolua_AllToLua_cItems_Get00); - tolua_function(tolua_S,"Set",tolua_AllToLua_cItems_Set00); - tolua_function(tolua_S,"Add",tolua_AllToLua_cItems_Add00); - tolua_function(tolua_S,"Delete",tolua_AllToLua_cItems_Delete00); - tolua_function(tolua_S,"Clear",tolua_AllToLua_cItems_Clear00); - tolua_function(tolua_S,"Size",tolua_AllToLua_cItems_Size00); - tolua_function(tolua_S,"Set",tolua_AllToLua_cItems_Set01); - tolua_function(tolua_S,"Add",tolua_AllToLua_cItems_Add01); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cItemGrid","cItemGrid","",NULL); - tolua_beginmodule(tolua_S,"cItemGrid"); - tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cItemGrid_GetWidth00); - tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cItemGrid_GetHeight00); - tolua_function(tolua_S,"GetNumSlots",tolua_AllToLua_cItemGrid_GetNumSlots00); - tolua_function(tolua_S,"GetSlotNum",tolua_AllToLua_cItemGrid_GetSlotNum00); - tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cItemGrid_GetSlot00); - tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cItemGrid_GetSlot01); - tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot00); - tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot01); - tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot02); - tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot03); - tolua_function(tolua_S,"EmptySlot",tolua_AllToLua_cItemGrid_EmptySlot00); - tolua_function(tolua_S,"EmptySlot",tolua_AllToLua_cItemGrid_EmptySlot01); - tolua_function(tolua_S,"IsSlotEmpty",tolua_AllToLua_cItemGrid_IsSlotEmpty00); - tolua_function(tolua_S,"IsSlotEmpty",tolua_AllToLua_cItemGrid_IsSlotEmpty01); - tolua_function(tolua_S,"Clear",tolua_AllToLua_cItemGrid_Clear00); - tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cItemGrid_HowManyCanFit00); - tolua_function(tolua_S,"AddItem",tolua_AllToLua_cItemGrid_AddItem00); - tolua_function(tolua_S,"AddItems",tolua_AllToLua_cItemGrid_AddItems00); - tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cItemGrid_ChangeSlotCount00); - tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cItemGrid_ChangeSlotCount01); - tolua_function(tolua_S,"RemoveOneItem",tolua_AllToLua_cItemGrid_RemoveOneItem00); - tolua_function(tolua_S,"RemoveOneItem",tolua_AllToLua_cItemGrid_RemoveOneItem01); - tolua_function(tolua_S,"HowManyItems",tolua_AllToLua_cItemGrid_HowManyItems00); - tolua_function(tolua_S,"HasItems",tolua_AllToLua_cItemGrid_HasItems00); - tolua_function(tolua_S,"GetFirstEmptySlot",tolua_AllToLua_cItemGrid_GetFirstEmptySlot00); - tolua_function(tolua_S,"GetFirstUsedSlot",tolua_AllToLua_cItemGrid_GetFirstUsedSlot00); - tolua_function(tolua_S,"GetLastEmptySlot",tolua_AllToLua_cItemGrid_GetLastEmptySlot00); - tolua_function(tolua_S,"GetLastUsedSlot",tolua_AllToLua_cItemGrid_GetLastUsedSlot00); - tolua_function(tolua_S,"GetNextEmptySlot",tolua_AllToLua_cItemGrid_GetNextEmptySlot00); - tolua_function(tolua_S,"GetNextUsedSlot",tolua_AllToLua_cItemGrid_GetNextUsedSlot00); - tolua_function(tolua_S,"CopyToItems",tolua_AllToLua_cItemGrid_CopyToItems00); - tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItemGrid_DamageItem00); - tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItemGrid_DamageItem01); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cBlockEntity","cBlockEntity","",tolua_collect_cBlockEntity); - #else - tolua_cclass(tolua_S,"cBlockEntity","cBlockEntity","",NULL); - #endif - tolua_beginmodule(tolua_S,"cBlockEntity"); - tolua_function(tolua_S,"GetPosX",tolua_AllToLua_cBlockEntity_GetPosX00); - tolua_function(tolua_S,"GetPosY",tolua_AllToLua_cBlockEntity_GetPosY00); - tolua_function(tolua_S,"GetPosZ",tolua_AllToLua_cBlockEntity_GetPosZ00); - tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cBlockEntity_GetBlockType00); - tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cBlockEntity_GetWorld00); - tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cBlockEntity_GetChunkX00); - tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cBlockEntity_GetChunkZ00); - tolua_function(tolua_S,"GetRelX",tolua_AllToLua_cBlockEntity_GetRelX00); - tolua_function(tolua_S,"GetRelZ",tolua_AllToLua_cBlockEntity_GetRelZ00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cBlockEntityWithItems","cBlockEntityWithItems","cBlockEntity",NULL); - tolua_beginmodule(tolua_S,"cBlockEntityWithItems"); - tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cBlockEntityWithItems_GetSlot00); - tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cBlockEntityWithItems_GetSlot01); - tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cBlockEntityWithItems_SetSlot00); - tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cBlockEntityWithItems_SetSlot01); - tolua_function(tolua_S,"GetContents",tolua_AllToLua_cBlockEntityWithItems_GetContents00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cChestEntity","cChestEntity","cBlockEntityWithItems",NULL); - tolua_beginmodule(tolua_S,"cChestEntity"); - tolua_constant(tolua_S,"ContentsHeight",cChestEntity::ContentsHeight); - tolua_constant(tolua_S,"ContentsWidth",cChestEntity::ContentsWidth); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cDropSpenserEntity","cDropSpenserEntity","cBlockEntityWithItems",NULL); - tolua_beginmodule(tolua_S,"cDropSpenserEntity"); - tolua_constant(tolua_S,"ContentsHeight",cDropSpenserEntity::ContentsHeight); - tolua_constant(tolua_S,"ContentsWidth",cDropSpenserEntity::ContentsWidth); - tolua_function(tolua_S,"AddDropSpenserDir",tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00); - tolua_function(tolua_S,"Activate",tolua_AllToLua_cDropSpenserEntity_Activate00); - tolua_function(tolua_S,"SetRedstonePower",tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cDispenserEntity","cDispenserEntity","cDropSpenserEntity",NULL); - tolua_beginmodule(tolua_S,"cDispenserEntity"); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cDropperEntity","cDropperEntity","cDropSpenserEntity",NULL); - tolua_beginmodule(tolua_S,"cDropperEntity"); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cFurnaceEntity","cFurnaceEntity","cBlockEntityWithItems",NULL); - tolua_beginmodule(tolua_S,"cFurnaceEntity"); - tolua_constant(tolua_S,"fsInput",cFurnaceEntity::fsInput); - tolua_constant(tolua_S,"fsFuel",cFurnaceEntity::fsFuel); - tolua_constant(tolua_S,"fsOutput",cFurnaceEntity::fsOutput); - tolua_constant(tolua_S,"ContentsWidth",cFurnaceEntity::ContentsWidth); - tolua_constant(tolua_S,"ContentsHeight",cFurnaceEntity::ContentsHeight); - tolua_function(tolua_S,"GetInputSlot",tolua_AllToLua_cFurnaceEntity_GetInputSlot00); - tolua_function(tolua_S,"GetFuelSlot",tolua_AllToLua_cFurnaceEntity_GetFuelSlot00); - tolua_function(tolua_S,"GetOutputSlot",tolua_AllToLua_cFurnaceEntity_GetOutputSlot00); - tolua_function(tolua_S,"SetInputSlot",tolua_AllToLua_cFurnaceEntity_SetInputSlot00); - tolua_function(tolua_S,"SetFuelSlot",tolua_AllToLua_cFurnaceEntity_SetFuelSlot00); - tolua_function(tolua_S,"SetOutputSlot",tolua_AllToLua_cFurnaceEntity_SetOutputSlot00); - tolua_function(tolua_S,"GetTimeCooked",tolua_AllToLua_cFurnaceEntity_GetTimeCooked00); - tolua_function(tolua_S,"GetCookTimeLeft",tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00); - tolua_function(tolua_S,"GetFuelBurnTimeLeft",tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00); - tolua_function(tolua_S,"HasFuelTimeLeft",tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cHopperEntity","cHopperEntity","cBlockEntityWithItems",NULL); - tolua_beginmodule(tolua_S,"cHopperEntity"); - tolua_constant(tolua_S,"ContentsHeight",cHopperEntity::ContentsHeight); - tolua_constant(tolua_S,"ContentsWidth",cHopperEntity::ContentsWidth); - tolua_constant(tolua_S,"TICKS_PER_TRANSFER",cHopperEntity::TICKS_PER_TRANSFER); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cJukeboxEntity","cJukeboxEntity","cBlockEntity",NULL); - tolua_beginmodule(tolua_S,"cJukeboxEntity"); - tolua_function(tolua_S,"GetRecord",tolua_AllToLua_cJukeboxEntity_GetRecord00); - tolua_function(tolua_S,"SetRecord",tolua_AllToLua_cJukeboxEntity_SetRecord00); - tolua_function(tolua_S,"PlayRecord",tolua_AllToLua_cJukeboxEntity_PlayRecord00); - tolua_function(tolua_S,"EjectRecord",tolua_AllToLua_cJukeboxEntity_EjectRecord00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cNoteEntity","cNoteEntity","cBlockEntity",NULL); - tolua_beginmodule(tolua_S,"cNoteEntity"); - tolua_function(tolua_S,"GetPitch",tolua_AllToLua_cNoteEntity_GetPitch00); - tolua_function(tolua_S,"SetPitch",tolua_AllToLua_cNoteEntity_SetPitch00); - tolua_function(tolua_S,"IncrementPitch",tolua_AllToLua_cNoteEntity_IncrementPitch00); - tolua_function(tolua_S,"MakeSound",tolua_AllToLua_cNoteEntity_MakeSound00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cSignEntity","cSignEntity","cBlockEntity",NULL); - tolua_beginmodule(tolua_S,"cSignEntity"); - tolua_function(tolua_S,"SetLines",tolua_AllToLua_cSignEntity_SetLines00); - tolua_function(tolua_S,"SetLine",tolua_AllToLua_cSignEntity_SetLine00); - tolua_function(tolua_S,"GetLine",tolua_AllToLua_cSignEntity_GetLine00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"HTTPFormData","HTTPFormData","",NULL); - tolua_beginmodule(tolua_S,"HTTPFormData"); - tolua_variable(tolua_S,"Name",tolua_get_HTTPFormData_Name,tolua_set_HTTPFormData_Name); - tolua_variable(tolua_S,"Value",tolua_get_HTTPFormData_Value,tolua_set_HTTPFormData_Value); - tolua_variable(tolua_S,"Type",tolua_get_HTTPFormData_Type,tolua_set_HTTPFormData_Type); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"HTTPRequest","HTTPRequest","",NULL); - tolua_beginmodule(tolua_S,"HTTPRequest"); - tolua_variable(tolua_S,"Method",tolua_get_HTTPRequest_Method,tolua_set_HTTPRequest_Method); - tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path); - tolua_variable(tolua_S,"Username",tolua_get_HTTPRequest_Username,tolua_set_HTTPRequest_Username); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"HTTPTemplateRequest","HTTPTemplateRequest","",NULL); - tolua_beginmodule(tolua_S,"HTTPTemplateRequest"); - tolua_variable(tolua_S,"Request",tolua_get_HTTPTemplateRequest_Request,tolua_set_HTTPTemplateRequest_Request); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",tolua_collect_sWebAdminPage); - #else - tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",NULL); - #endif - tolua_beginmodule(tolua_S,"sWebAdminPage"); - tolua_variable(tolua_S,"Content",tolua_get_sWebAdminPage_Content,tolua_set_sWebAdminPage_Content); - tolua_variable(tolua_S,"PluginName",tolua_get_sWebAdminPage_PluginName,tolua_set_sWebAdminPage_PluginName); - tolua_variable(tolua_S,"TabName",tolua_get_sWebAdminPage_TabName,tolua_set_sWebAdminPage_TabName); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","cHTTPServer::cCallbacks",NULL); - tolua_beginmodule(tolua_S,"cWebAdmin"); - tolua_function(tolua_S,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00); - tolua_function(tolua_S,"GetDefaultPage",tolua_AllToLua_cWebAdmin_GetDefaultPage00); - tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00); - tolua_function(tolua_S,"GetHTMLEscapedString",tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL); - tolua_beginmodule(tolua_S,"cWebPlugin"); - tolua_function(tolua_S,"GetWebTitle",tolua_AllToLua_cWebPlugin_GetWebTitle00); - tolua_function(tolua_S,"HandleWebRequest",tolua_AllToLua_cWebPlugin_HandleWebRequest00); - tolua_function(tolua_S,"SafeString",tolua_AllToLua_cWebPlugin_SafeString00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cRoot","cRoot","",NULL); - tolua_beginmodule(tolua_S,"cRoot"); - tolua_function(tolua_S,"Get",tolua_AllToLua_cRoot_Get00); - tolua_function(tolua_S,"GetServer",tolua_AllToLua_cRoot_GetServer00); - tolua_function(tolua_S,"GetDefaultWorld",tolua_AllToLua_cRoot_GetDefaultWorld00); - tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cRoot_GetWorld00); - tolua_function(tolua_S,"GetPrimaryServerVersion",tolua_AllToLua_cRoot_GetPrimaryServerVersion00); - tolua_function(tolua_S,"SetPrimaryServerVersion",tolua_AllToLua_cRoot_SetPrimaryServerVersion00); - tolua_function(tolua_S,"GetGroupManager",tolua_AllToLua_cRoot_GetGroupManager00); - tolua_function(tolua_S,"GetCraftingRecipes",tolua_AllToLua_cRoot_GetCraftingRecipes00); - tolua_function(tolua_S,"GetFurnaceRecipe",tolua_AllToLua_cRoot_GetFurnaceRecipe00); - tolua_function(tolua_S,"GetWebAdmin",tolua_AllToLua_cRoot_GetWebAdmin00); - tolua_function(tolua_S,"GetPluginManager",tolua_AllToLua_cRoot_GetPluginManager00); - tolua_function(tolua_S,"QueueExecuteConsoleCommand",tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00); - tolua_function(tolua_S,"GetTotalChunkCount",tolua_AllToLua_cRoot_GetTotalChunkCount00); - tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00); - tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cRoot_BroadcastChat00); - tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00); - tolua_function(tolua_S,"GetVirtualRAMUsage",tolua_AllToLua_cRoot_GetVirtualRAMUsage00); - tolua_function(tolua_S,"GetPhysicalRAMUsage",tolua_AllToLua_cRoot_GetPhysicalRAMUsage00); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"Vector3f","Vector3f","",tolua_collect_Vector3f); - #else - tolua_cclass(tolua_S,"Vector3f","Vector3f","",NULL); - #endif - tolua_beginmodule(tolua_S,"Vector3f"); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new00_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new01); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new01_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new01_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new02); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new02_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new02_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new03); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new03_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new03_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new04); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new04_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new04_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new05); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new05_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new05_local); - tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3f_Set00); - tolua_function(tolua_S,"Normalize",tolua_AllToLua_Vector3f_Normalize00); - tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3f_NormalizeCopy00); - tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3f_NormalizeCopy01); - tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3f_Length00); - tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3f_SqrLength00); - tolua_function(tolua_S,"Dot",tolua_AllToLua_Vector3f_Dot00); - tolua_function(tolua_S,"Cross",tolua_AllToLua_Vector3f_Cross00); - tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3f_Equals00); - tolua_function(tolua_S,".add",tolua_AllToLua_Vector3f__add00); - tolua_function(tolua_S,".add",tolua_AllToLua_Vector3f__add01); - tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3f__sub00); - tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3f__sub01); - tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3f__mul00); - tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3f__mul01); - tolua_variable(tolua_S,"x",tolua_get_Vector3f_x,tolua_set_Vector3f_x); - tolua_variable(tolua_S,"y",tolua_get_Vector3f_y,tolua_set_Vector3f_y); - tolua_variable(tolua_S,"z",tolua_get_Vector3f_z,tolua_set_Vector3f_z); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"Vector3d","Vector3d","",tolua_collect_Vector3d); - #else - tolua_cclass(tolua_S,"Vector3d","Vector3d","",NULL); - #endif - tolua_beginmodule(tolua_S,"Vector3d"); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new00_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new01); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new01_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new01_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new02); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new02_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new02_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new03); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new03_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new03_local); - tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3d_Set00); - tolua_function(tolua_S,"Normalize",tolua_AllToLua_Vector3d_Normalize00); - tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3d_NormalizeCopy00); - tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3d_NormalizeCopy01); - tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3d_Length00); - tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3d_SqrLength00); - tolua_function(tolua_S,"Dot",tolua_AllToLua_Vector3d_Dot00); - tolua_function(tolua_S,"Cross",tolua_AllToLua_Vector3d_Cross00); - tolua_function(tolua_S,"LineCoeffToXYPlane",tolua_AllToLua_Vector3d_LineCoeffToXYPlane00); - tolua_function(tolua_S,"LineCoeffToXZPlane",tolua_AllToLua_Vector3d_LineCoeffToXZPlane00); - tolua_function(tolua_S,"LineCoeffToYZPlane",tolua_AllToLua_Vector3d_LineCoeffToYZPlane00); - tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3d_Equals00); - tolua_function(tolua_S,".add",tolua_AllToLua_Vector3d__add00); - tolua_function(tolua_S,".add",tolua_AllToLua_Vector3d__add01); - tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3d__sub00); - tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3d__sub01); - tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3d__mul00); - tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3d__mul01); - tolua_function(tolua_S,".div",tolua_AllToLua_Vector3d__div00); - tolua_variable(tolua_S,"x",tolua_get_Vector3d_x,tolua_set_Vector3d_x); - tolua_variable(tolua_S,"y",tolua_get_Vector3d_y,tolua_set_Vector3d_y); - tolua_variable(tolua_S,"z",tolua_get_Vector3d_z,tolua_set_Vector3d_z); - tolua_variable(tolua_S,"EPS",tolua_get_Vector3d_EPS,NULL); - tolua_variable(tolua_S,"NO_INTERSECTION",tolua_get_Vector3d_NO_INTERSECTION,NULL); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"Vector3i","Vector3i","",tolua_collect_Vector3i); - #else - tolua_cclass(tolua_S,"Vector3i","Vector3i","",NULL); - #endif - tolua_beginmodule(tolua_S,"Vector3i"); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new00_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new01); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new01_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new01_local); - tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new02); - tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new02_local); - tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new02_local); - tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3i_Set00); - tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3i_Length00); - tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3i_SqrLength00); - tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3i_Equals00); - tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3i_Equals01); - tolua_variable(tolua_S,"x",tolua_get_Vector3i_x,tolua_set_Vector3i_x); - tolua_variable(tolua_S,"y",tolua_get_Vector3i_y,tolua_set_Vector3i_y); - tolua_variable(tolua_S,"z",tolua_get_Vector3i_z,tolua_set_Vector3i_z); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cCuboid","cCuboid","",tolua_collect_cCuboid); - #else - tolua_cclass(tolua_S,"cCuboid","cCuboid","",NULL); - #endif - tolua_beginmodule(tolua_S,"cCuboid"); - tolua_variable(tolua_S,"p1",tolua_get_cCuboid_p1,tolua_set_cCuboid_p1); - tolua_variable(tolua_S,"p2",tolua_get_cCuboid_p2,tolua_set_cCuboid_p2); - tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new00_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new01); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new01_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new01_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new02); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new02_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new02_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new03); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new03_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new03_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new04); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new04_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new04_local); - tolua_function(tolua_S,"Assign",tolua_AllToLua_cCuboid_Assign00); - tolua_function(tolua_S,"Sort",tolua_AllToLua_cCuboid_Sort00); - tolua_function(tolua_S,"DifX",tolua_AllToLua_cCuboid_DifX00); - tolua_function(tolua_S,"DifY",tolua_AllToLua_cCuboid_DifY00); - tolua_function(tolua_S,"DifZ",tolua_AllToLua_cCuboid_DifZ00); - tolua_function(tolua_S,"DoesIntersect",tolua_AllToLua_cCuboid_DoesIntersect00); - tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside00); - tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside01); - tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside02); - tolua_function(tolua_S,"IsCompletelyInside",tolua_AllToLua_cCuboid_IsCompletelyInside00); - tolua_function(tolua_S,"Move",tolua_AllToLua_cCuboid_Move00); - tolua_function(tolua_S,"IsSorted",tolua_AllToLua_cCuboid_IsSorted00); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cBoundingBox","cBoundingBox","",tolua_collect_cBoundingBox); - #else - tolua_cclass(tolua_S,"cBoundingBox","cBoundingBox","",NULL); - #endif - tolua_beginmodule(tolua_S,"cBoundingBox"); - tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new00_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new01); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new01_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new01_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new02); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new02_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new02_local); - tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new03); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new03_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new03_local); - tolua_function(tolua_S,"Move",tolua_AllToLua_cBoundingBox_Move00); - tolua_function(tolua_S,"Move",tolua_AllToLua_cBoundingBox_Move01); - tolua_function(tolua_S,"Expand",tolua_AllToLua_cBoundingBox_Expand00); - tolua_function(tolua_S,"DoesIntersect",tolua_AllToLua_cBoundingBox_DoesIntersect00); - tolua_function(tolua_S,"Union",tolua_AllToLua_cBoundingBox_Union00); - tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside00); - tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside01); - tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside02); - tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside03); - tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside04); - tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside05); - tolua_function(tolua_S,"CalcLineIntersection",tolua_AllToLua_cBoundingBox_CalcLineIntersection00); - tolua_function(tolua_S,"CalcLineIntersection",tolua_AllToLua_cBoundingBox_CalcLineIntersection01); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cTracer","cTracer","",tolua_collect_cTracer); - #else - tolua_cclass(tolua_S,"cTracer","cTracer","",NULL); - #endif - tolua_beginmodule(tolua_S,"cTracer"); - tolua_variable(tolua_S,"BlockHitPosition",tolua_get_cTracer_BlockHitPosition,tolua_set_cTracer_BlockHitPosition); - tolua_variable(tolua_S,"HitNormal",tolua_get_cTracer_HitNormal,tolua_set_cTracer_HitNormal); - tolua_variable(tolua_S,"RealHit",tolua_get_cTracer_RealHit,tolua_set_cTracer_RealHit); - tolua_function(tolua_S,"new",tolua_AllToLua_cTracer_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cTracer_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cTracer_new00_local); - tolua_function(tolua_S,"delete",tolua_AllToLua_cTracer_delete00); - tolua_function(tolua_S,"Trace",tolua_AllToLua_cTracer_Trace00); - tolua_function(tolua_S,"Trace",tolua_AllToLua_cTracer_Trace01); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cGroup","cGroup","",NULL); - tolua_beginmodule(tolua_S,"cGroup"); - tolua_function(tolua_S,"SetName",tolua_AllToLua_cGroup_SetName00); - tolua_function(tolua_S,"GetName",tolua_AllToLua_cGroup_GetName00); - tolua_function(tolua_S,"SetColor",tolua_AllToLua_cGroup_SetColor00); - tolua_function(tolua_S,"AddCommand",tolua_AllToLua_cGroup_AddCommand00); - tolua_function(tolua_S,"AddPermission",tolua_AllToLua_cGroup_AddPermission00); - tolua_function(tolua_S,"InheritFrom",tolua_AllToLua_cGroup_InheritFrom00); - tolua_function(tolua_S,"HasCommand",tolua_AllToLua_cGroup_HasCommand00); - tolua_function(tolua_S,"GetColor",tolua_AllToLua_cGroup_GetColor00); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cBlockArea","cBlockArea","",tolua_collect_cBlockArea); - #else - tolua_cclass(tolua_S,"cBlockArea","cBlockArea","",NULL); - #endif - tolua_beginmodule(tolua_S,"cBlockArea"); - tolua_constant(tolua_S,"baTypes",cBlockArea::baTypes); - tolua_constant(tolua_S,"baMetas",cBlockArea::baMetas); - tolua_constant(tolua_S,"baLight",cBlockArea::baLight); - tolua_constant(tolua_S,"baSkyLight",cBlockArea::baSkyLight); - tolua_constant(tolua_S,"msOverwrite",cBlockArea::msOverwrite); - tolua_constant(tolua_S,"msFillAir",cBlockArea::msFillAir); - tolua_constant(tolua_S,"msImprint",cBlockArea::msImprint); - tolua_constant(tolua_S,"msLake",cBlockArea::msLake); - tolua_function(tolua_S,"new",tolua_AllToLua_cBlockArea_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cBlockArea_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cBlockArea_new00_local); - tolua_function(tolua_S,"delete",tolua_AllToLua_cBlockArea_delete00); - tolua_function(tolua_S,"Clear",tolua_AllToLua_cBlockArea_Clear00); - tolua_function(tolua_S,"Create",tolua_AllToLua_cBlockArea_Create00); - tolua_function(tolua_S,"Create",tolua_AllToLua_cBlockArea_Create01); - tolua_function(tolua_S,"SetOrigin",tolua_AllToLua_cBlockArea_SetOrigin00); - tolua_function(tolua_S,"Read",tolua_AllToLua_cBlockArea_Read00); - tolua_function(tolua_S,"Read",tolua_AllToLua_cBlockArea_Read01); - tolua_function(tolua_S,"Write",tolua_AllToLua_cBlockArea_Write00); - tolua_function(tolua_S,"Write",tolua_AllToLua_cBlockArea_Write01); - tolua_function(tolua_S,"CopyTo",tolua_AllToLua_cBlockArea_CopyTo00); - tolua_function(tolua_S,"CopyFrom",tolua_AllToLua_cBlockArea_CopyFrom00); - tolua_function(tolua_S,"DumpToRawFile",tolua_AllToLua_cBlockArea_DumpToRawFile00); - tolua_function(tolua_S,"LoadFromSchematicFile",tolua_AllToLua_cBlockArea_LoadFromSchematicFile00); - tolua_function(tolua_S,"SaveToSchematicFile",tolua_AllToLua_cBlockArea_SaveToSchematicFile00); - tolua_function(tolua_S,"Crop",tolua_AllToLua_cBlockArea_Crop00); - tolua_function(tolua_S,"Expand",tolua_AllToLua_cBlockArea_Expand00); - tolua_function(tolua_S,"Merge",tolua_AllToLua_cBlockArea_Merge00); - tolua_function(tolua_S,"Fill",tolua_AllToLua_cBlockArea_Fill00); - tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cBlockArea_FillRelCuboid00); - tolua_function(tolua_S,"RelLine",tolua_AllToLua_cBlockArea_RelLine00); - tolua_function(tolua_S,"RotateCCW",tolua_AllToLua_cBlockArea_RotateCCW00); - tolua_function(tolua_S,"RotateCW",tolua_AllToLua_cBlockArea_RotateCW00); - tolua_function(tolua_S,"MirrorXY",tolua_AllToLua_cBlockArea_MirrorXY00); - tolua_function(tolua_S,"MirrorXZ",tolua_AllToLua_cBlockArea_MirrorXZ00); - tolua_function(tolua_S,"MirrorYZ",tolua_AllToLua_cBlockArea_MirrorYZ00); - tolua_function(tolua_S,"RotateCCWNoMeta",tolua_AllToLua_cBlockArea_RotateCCWNoMeta00); - tolua_function(tolua_S,"RotateCWNoMeta",tolua_AllToLua_cBlockArea_RotateCWNoMeta00); - tolua_function(tolua_S,"MirrorXYNoMeta",tolua_AllToLua_cBlockArea_MirrorXYNoMeta00); - tolua_function(tolua_S,"MirrorXZNoMeta",tolua_AllToLua_cBlockArea_MirrorXZNoMeta00); - tolua_function(tolua_S,"MirrorYZNoMeta",tolua_AllToLua_cBlockArea_MirrorYZNoMeta00); - tolua_function(tolua_S,"SetRelBlockType",tolua_AllToLua_cBlockArea_SetRelBlockType00); - tolua_function(tolua_S,"SetBlockType",tolua_AllToLua_cBlockArea_SetBlockType00); - tolua_function(tolua_S,"SetRelBlockMeta",tolua_AllToLua_cBlockArea_SetRelBlockMeta00); - tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cBlockArea_SetBlockMeta00); - tolua_function(tolua_S,"SetRelBlockLight",tolua_AllToLua_cBlockArea_SetRelBlockLight00); - tolua_function(tolua_S,"SetBlockLight",tolua_AllToLua_cBlockArea_SetBlockLight00); - tolua_function(tolua_S,"SetRelBlockSkyLight",tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00); - tolua_function(tolua_S,"SetBlockSkyLight",tolua_AllToLua_cBlockArea_SetBlockSkyLight00); - tolua_function(tolua_S,"GetRelBlockType",tolua_AllToLua_cBlockArea_GetRelBlockType00); - tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cBlockArea_GetBlockType00); - tolua_function(tolua_S,"GetRelBlockMeta",tolua_AllToLua_cBlockArea_GetRelBlockMeta00); - tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cBlockArea_GetBlockMeta00); - tolua_function(tolua_S,"GetRelBlockLight",tolua_AllToLua_cBlockArea_GetRelBlockLight00); - tolua_function(tolua_S,"GetBlockLight",tolua_AllToLua_cBlockArea_GetBlockLight00); - tolua_function(tolua_S,"GetRelBlockSkyLight",tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00); - tolua_function(tolua_S,"GetBlockSkyLight",tolua_AllToLua_cBlockArea_GetBlockSkyLight00); - tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cBlockArea_SetBlockTypeMeta00); - tolua_function(tolua_S,"SetRelBlockTypeMeta",tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00); - tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cBlockArea_GetBlockTypeMeta00); - tolua_function(tolua_S,"GetRelBlockTypeMeta",tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00); - tolua_function(tolua_S,"GetSizeX",tolua_AllToLua_cBlockArea_GetSizeX00); - tolua_function(tolua_S,"GetSizeY",tolua_AllToLua_cBlockArea_GetSizeY00); - tolua_function(tolua_S,"GetSizeZ",tolua_AllToLua_cBlockArea_GetSizeZ00); - tolua_function(tolua_S,"GetOriginX",tolua_AllToLua_cBlockArea_GetOriginX00); - tolua_function(tolua_S,"GetOriginY",tolua_AllToLua_cBlockArea_GetOriginY00); - tolua_function(tolua_S,"GetOriginZ",tolua_AllToLua_cBlockArea_GetOriginZ00); - tolua_function(tolua_S,"GetDataTypes",tolua_AllToLua_cBlockArea_GetDataTypes00); - tolua_function(tolua_S,"HasBlockTypes",tolua_AllToLua_cBlockArea_HasBlockTypes00); - tolua_function(tolua_S,"HasBlockMetas",tolua_AllToLua_cBlockArea_HasBlockMetas00); - tolua_function(tolua_S,"HasBlockLights",tolua_AllToLua_cBlockArea_HasBlockLights00); - tolua_function(tolua_S,"HasBlockSkyLights",tolua_AllToLua_cBlockArea_HasBlockSkyLights00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cChunkDesc","cChunkDesc","",NULL); - tolua_beginmodule(tolua_S,"cChunkDesc"); - tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cChunkDesc_GetChunkX00); - tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cChunkDesc_GetChunkZ00); - tolua_function(tolua_S,"FillBlocks",tolua_AllToLua_cChunkDesc_FillBlocks00); - tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00); - tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00); - tolua_function(tolua_S,"SetBlockType",tolua_AllToLua_cChunkDesc_SetBlockType00); - tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cChunkDesc_GetBlockType00); - tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cChunkDesc_SetBlockMeta00); - tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cChunkDesc_GetBlockMeta00); - tolua_function(tolua_S,"SetBiome",tolua_AllToLua_cChunkDesc_SetBiome00); - tolua_function(tolua_S,"GetBiome",tolua_AllToLua_cChunkDesc_GetBiome00); - tolua_function(tolua_S,"SetHeight",tolua_AllToLua_cChunkDesc_SetHeight00); - tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cChunkDesc_GetHeight00); - tolua_function(tolua_S,"SetUseDefaultBiomes",tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00); - tolua_function(tolua_S,"IsUsingDefaultBiomes",tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00); - tolua_function(tolua_S,"SetUseDefaultHeight",tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00); - tolua_function(tolua_S,"IsUsingDefaultHeight",tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00); - tolua_function(tolua_S,"SetUseDefaultComposition",tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00); - tolua_function(tolua_S,"IsUsingDefaultComposition",tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00); - tolua_function(tolua_S,"SetUseDefaultStructures",tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00); - tolua_function(tolua_S,"IsUsingDefaultStructures",tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00); - tolua_function(tolua_S,"SetUseDefaultFinish",tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00); - tolua_function(tolua_S,"IsUsingDefaultFinish",tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00); - tolua_function(tolua_S,"WriteBlockArea",tolua_AllToLua_cChunkDesc_WriteBlockArea00); - tolua_function(tolua_S,"ReadBlockArea",tolua_AllToLua_cChunkDesc_ReadBlockArea00); - tolua_function(tolua_S,"GetMaxHeight",tolua_AllToLua_cChunkDesc_GetMaxHeight00); - tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cChunkDesc_FillRelCuboid00); - tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cChunkDesc_FillRelCuboid01); - tolua_function(tolua_S,"ReplaceRelCuboid",tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00); - tolua_function(tolua_S,"ReplaceRelCuboid",tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01); - tolua_function(tolua_S,"FloorRelCuboid",tolua_AllToLua_cChunkDesc_FloorRelCuboid00); - tolua_function(tolua_S,"FloorRelCuboid",tolua_AllToLua_cChunkDesc_FloorRelCuboid01); - tolua_function(tolua_S,"RandomFillRelCuboid",tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00); - tolua_function(tolua_S,"RandomFillRelCuboid",tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01); - tolua_function(tolua_S,"GetBlockEntity",tolua_AllToLua_cChunkDesc_GetBlockEntity00); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cCraftingGrid","cCraftingGrid","",tolua_collect_cCraftingGrid); - #else - tolua_cclass(tolua_S,"cCraftingGrid","cCraftingGrid","",NULL); - #endif - tolua_beginmodule(tolua_S,"cCraftingGrid"); - tolua_function(tolua_S,"new",tolua_AllToLua_cCraftingGrid_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cCraftingGrid_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cCraftingGrid_new00_local); - tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cCraftingGrid_GetWidth00); - tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cCraftingGrid_GetHeight00); - tolua_function(tolua_S,"GetItem",tolua_AllToLua_cCraftingGrid_GetItem00); - tolua_function(tolua_S,"SetItem",tolua_AllToLua_cCraftingGrid_SetItem00); - tolua_function(tolua_S,"SetItem",tolua_AllToLua_cCraftingGrid_SetItem01); - tolua_function(tolua_S,"Clear",tolua_AllToLua_cCraftingGrid_Clear00); - tolua_function(tolua_S,"ConsumeGrid",tolua_AllToLua_cCraftingGrid_ConsumeGrid00); - tolua_function(tolua_S,"Dump",tolua_AllToLua_cCraftingGrid_Dump00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cCraftingRecipe","cCraftingRecipe","",NULL); - tolua_beginmodule(tolua_S,"cCraftingRecipe"); - tolua_function(tolua_S,"Clear",tolua_AllToLua_cCraftingRecipe_Clear00); - tolua_function(tolua_S,"GetIngredientsWidth",tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00); - tolua_function(tolua_S,"GetIngredientsHeight",tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00); - tolua_function(tolua_S,"GetIngredient",tolua_AllToLua_cCraftingRecipe_GetIngredient00); - tolua_function(tolua_S,"GetResult",tolua_AllToLua_cCraftingRecipe_GetResult00); - tolua_function(tolua_S,"SetResult",tolua_AllToLua_cCraftingRecipe_SetResult00); - tolua_function(tolua_S,"SetResult",tolua_AllToLua_cCraftingRecipe_SetResult01); - tolua_function(tolua_S,"SetIngredient",tolua_AllToLua_cCraftingRecipe_SetIngredient00); - tolua_function(tolua_S,"SetIngredient",tolua_AllToLua_cCraftingRecipe_SetIngredient01); - tolua_function(tolua_S,"ConsumeIngredients",tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00); - tolua_function(tolua_S,"Dump",tolua_AllToLua_cCraftingRecipe_Dump00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cWindow","cWindow","",NULL); - tolua_beginmodule(tolua_S,"cWindow"); - tolua_constant(tolua_S,"wtInventory",cWindow::wtInventory); - tolua_constant(tolua_S,"wtChest",cWindow::wtChest); - tolua_constant(tolua_S,"wtWorkbench",cWindow::wtWorkbench); - tolua_constant(tolua_S,"wtFurnace",cWindow::wtFurnace); - tolua_constant(tolua_S,"wtDropSpenser",cWindow::wtDropSpenser); - tolua_constant(tolua_S,"wtEnchantment",cWindow::wtEnchantment); - tolua_constant(tolua_S,"wtBrewery",cWindow::wtBrewery); - tolua_constant(tolua_S,"wtNPCTrade",cWindow::wtNPCTrade); - tolua_constant(tolua_S,"wtBeacon",cWindow::wtBeacon); - tolua_constant(tolua_S,"wtAnvil",cWindow::wtAnvil); - tolua_constant(tolua_S,"wtHopper",cWindow::wtHopper); - tolua_constant(tolua_S,"wtAnimalChest",cWindow::wtAnimalChest); - tolua_function(tolua_S,"GetWindowID",tolua_AllToLua_cWindow_GetWindowID00); - tolua_function(tolua_S,"GetWindowType",tolua_AllToLua_cWindow_GetWindowType00); - tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cWindow_GetSlot00); - tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cWindow_SetSlot00); - tolua_function(tolua_S,"IsSlotInPlayerMainInventory",tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00); - tolua_function(tolua_S,"IsSlotInPlayerHotbar",tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00); - tolua_function(tolua_S,"IsSlotInPlayerInventory",tolua_AllToLua_cWindow_IsSlotInPlayerInventory00); - tolua_function(tolua_S,"GetWindowTitle",tolua_AllToLua_cWindow_GetWindowTitle00); - tolua_function(tolua_S,"SetWindowTitle",tolua_AllToLua_cWindow_SetWindowTitle00); - tolua_function(tolua_S,"SetProperty",tolua_AllToLua_cWindow_SetProperty00); - tolua_function(tolua_S,"SetProperty",tolua_AllToLua_cWindow_SetProperty01); - tolua_endmodule(tolua_S); - #ifdef __cplusplus - tolua_cclass(tolua_S,"cLuaWindow","cLuaWindow","cWindow",tolua_collect_cLuaWindow); - #else - tolua_cclass(tolua_S,"cLuaWindow","cLuaWindow","cWindow",NULL); - #endif - tolua_beginmodule(tolua_S,"cLuaWindow"); - tolua_function(tolua_S,"new",tolua_AllToLua_cLuaWindow_new00); - tolua_function(tolua_S,"new_local",tolua_AllToLua_cLuaWindow_new00_local); - tolua_function(tolua_S,".call",tolua_AllToLua_cLuaWindow_new00_local); - tolua_function(tolua_S,"delete",tolua_AllToLua_cLuaWindow_delete00); - tolua_function(tolua_S,"GetContents",tolua_AllToLua_cLuaWindow_GetContents00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cMonster","cMonster","cPawn",NULL); - tolua_beginmodule(tolua_S,"cMonster"); - tolua_constant(tolua_S,"mtInvalidType",cMonster::mtInvalidType); - tolua_constant(tolua_S,"mtBat",cMonster::mtBat); - tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze); - tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider); - tolua_constant(tolua_S,"mtChicken",cMonster::mtChicken); - tolua_constant(tolua_S,"mtCow",cMonster::mtCow); - tolua_constant(tolua_S,"mtCreeper",cMonster::mtCreeper); - tolua_constant(tolua_S,"mtEnderDragon",cMonster::mtEnderDragon); - tolua_constant(tolua_S,"mtEnderman",cMonster::mtEnderman); - tolua_constant(tolua_S,"mtGhast",cMonster::mtGhast); - tolua_constant(tolua_S,"mtGiant",cMonster::mtGiant); - tolua_constant(tolua_S,"mtHorse",cMonster::mtHorse); - tolua_constant(tolua_S,"mtIronGolem",cMonster::mtIronGolem); - tolua_constant(tolua_S,"mtMagmaCube",cMonster::mtMagmaCube); - tolua_constant(tolua_S,"mtMooshroom",cMonster::mtMooshroom); - tolua_constant(tolua_S,"mtOcelot",cMonster::mtOcelot); - tolua_constant(tolua_S,"mtPig",cMonster::mtPig); - tolua_constant(tolua_S,"mtSheep",cMonster::mtSheep); - tolua_constant(tolua_S,"mtSilverfish",cMonster::mtSilverfish); - tolua_constant(tolua_S,"mtSkeleton",cMonster::mtSkeleton); - tolua_constant(tolua_S,"mtSlime",cMonster::mtSlime); - tolua_constant(tolua_S,"mtSnowGolem",cMonster::mtSnowGolem); - tolua_constant(tolua_S,"mtSpider",cMonster::mtSpider); - tolua_constant(tolua_S,"mtSquid",cMonster::mtSquid); - tolua_constant(tolua_S,"mtVillager",cMonster::mtVillager); - tolua_constant(tolua_S,"mtWitch",cMonster::mtWitch); - tolua_constant(tolua_S,"mtWither",cMonster::mtWither); - tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf); - tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie); - tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman); - tolua_constant(tolua_S,"mfHostile",cMonster::mfHostile); - tolua_constant(tolua_S,"mfPassive",cMonster::mfPassive); - tolua_constant(tolua_S,"mfAmbient",cMonster::mfAmbient); - tolua_constant(tolua_S,"mfWater",cMonster::mfWater); - tolua_constant(tolua_S,"mfMaxplusone",cMonster::mfMaxplusone); - tolua_function(tolua_S,"GetMobType",tolua_AllToLua_cMonster_GetMobType00); - tolua_function(tolua_S,"GetMobFamily",tolua_AllToLua_cMonster_GetMobFamily00); - tolua_function(tolua_S,"MobTypeToString",tolua_AllToLua_cMonster_MobTypeToString00); - tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_cMonster_StringToMobType00); - tolua_function(tolua_S,"FamilyFromType",tolua_AllToLua_cMonster_FamilyFromType00); - tolua_function(tolua_S,"GetSpawnDelay",tolua_AllToLua_cMonster_GetSpawnDelay00); - tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cLineBlockTracer","cLineBlockTracer","",NULL); - tolua_beginmodule(tolua_S,"cLineBlockTracer"); - tolua_endmodule(tolua_S); - tolua_endmodule(tolua_S); - return 1; -} - - -#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501 - TOLUA_API int luaopen_AllToLua (lua_State* tolua_S) { - return tolua_AllToLua_open(tolua_S); -}; -#endif - diff --git a/source/Bindings.h b/source/Bindings.h deleted file mode 100644 index 13f398a4d..000000000 --- a/source/Bindings.h +++ /dev/null @@ -1,8 +0,0 @@ -/* -** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 11/15/13 10:14:20. -*/ - -/* Exported function */ -TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); - diff --git a/source/BlockArea.cpp b/source/BlockArea.cpp deleted file mode 100644 index 5c15adfef..000000000 --- a/source/BlockArea.cpp +++ /dev/null @@ -1,2124 +0,0 @@ - -// BlockArea.cpp - -// Implements the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries -// The object also supports writing the blockdata back into cWorld, even into other coords - -#include "Globals.h" -#include "BlockArea.h" -#include "World.h" -#include "OSSupport/GZipFile.h" -#include "WorldStorage/FastNBT.h" -#include "Blocks/BlockHandler.h" - - - - - -// This wild construct allows us to pass a function argument and still have it inlined by the compiler :) -/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function -template void InternalMergeBlocks( - BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes, - NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas, - int a_SizeX, int a_SizeY, int a_SizeZ, - int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ, - int a_DstOffX, int a_DstOffY, int a_DstOffZ, - int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, - int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ, - Combinator a_Combinator -) -{ - for (int y = 0; y < a_SizeY; y++) - { - int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ; - int DstBaseY = (y + a_DstOffY) * a_DstSizeX * a_DstSizeZ; - for (int z = 0; z < a_SizeZ; z++) - { - int SrcBaseZ = SrcBaseY + (z + a_SrcOffZ) * a_SrcSizeX; - int DstBaseZ = DstBaseY + (z + a_DstOffZ) * a_DstSizeX; - int SrcIdx = SrcBaseZ + a_SrcOffX; - int DstIdx = DstBaseZ + a_DstOffX; - for (int x = 0; x < a_SizeX; x++) - { - a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]); - ++DstIdx; - ++SrcIdx; - } // for x - } // for z - } // for y -} - - - - - -/// Combinator used for cBlockArea::msOverwrite merging -static void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) -{ - a_DstType = a_SrcType; - a_DstMeta = a_SrcMeta; -} - - - - - -/// Combinator used for cBlockArea::msFillAir merging -static void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) -{ - if (a_DstType == E_BLOCK_AIR) - { - a_DstType = a_SrcType; - a_DstMeta = a_SrcMeta; - } - // "else" is the default, already in place -} - - - - - -/// Combinator used for cBlockArea::msImprint merging -static void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) -{ - if (a_SrcType != E_BLOCK_AIR) - { - a_DstType = a_SrcType; - a_DstMeta = a_SrcMeta; - } - // "else" is the default, already in place -} - - - - - -/// Combinator used for cBlockArea::msLake merging -static void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) -{ - // Sponge is the NOP block - if (a_SrcType == E_BLOCK_SPONGE) - { - return; - } - - // Air is always hollowed out - if (a_SrcType == E_BLOCK_AIR) - { - a_DstType = E_BLOCK_AIR; - a_DstMeta = 0; - return; - } - - // Water and lava are never overwritten - switch (a_DstType) - { - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - return; - } - } - - // Water and lava always overwrite - switch (a_SrcType) - { - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - a_DstType = a_SrcType; - a_DstMeta = a_DstMeta; - return; - } - } - - if (a_SrcType == E_BLOCK_STONE) - { - switch (a_DstType) - { - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_MYCELIUM: - { - a_DstType = E_BLOCK_STONE; - a_DstMeta = 0; - return; - } - } - } - // Everything else is left as it is -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBlockArea: - -cBlockArea::cBlockArea(void) : - m_SizeX(0), - m_SizeY(0), - m_SizeZ(0), - m_BlockTypes(NULL), - m_BlockMetas(NULL), - m_BlockLight(NULL), - m_BlockSkyLight(NULL) -{ -} - - - - - -cBlockArea::~cBlockArea() -{ - Clear(); -} - - - - - -void cBlockArea::Clear(void) -{ - delete[] m_BlockTypes; m_BlockTypes = NULL; - delete[] m_BlockMetas; m_BlockMetas = NULL; - delete[] m_BlockLight; m_BlockLight = NULL; - delete[] m_BlockSkyLight; m_BlockSkyLight = NULL; - m_SizeX = 0; - m_SizeY = 0; - m_SizeZ = 0; -} - - - - - -void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes) -{ - Clear(); - int BlockCount = a_SizeX * a_SizeY * a_SizeZ; - if ((a_DataTypes & baTypes) != 0) - { - m_BlockTypes = new BLOCKTYPE[BlockCount]; - for (int i = 0; i < BlockCount; i++) - { - m_BlockTypes[i] = E_BLOCK_AIR; - } - } - if ((a_DataTypes & baMetas) != 0) - { - m_BlockMetas = new NIBBLETYPE[BlockCount]; - for (int i = 0; i < BlockCount; i++) - { - m_BlockMetas[i] = 0; - } - } - if ((a_DataTypes & baLight) != 0) - { - m_BlockLight = new NIBBLETYPE[BlockCount]; - for (int i = 0; i < BlockCount; i++) - { - m_BlockLight[i] = 0; - } - } - if ((a_DataTypes & baSkyLight) != 0) - { - m_BlockSkyLight = new NIBBLETYPE[BlockCount]; - for (int i = 0; i < BlockCount; i++) - { - m_BlockSkyLight[i] = 0x0f; - } - } - m_SizeX = a_SizeX; - m_SizeY = a_SizeY; - m_SizeZ = a_SizeZ; - m_OriginX = 0; - m_OriginY = 0; - m_OriginZ = 0; -} - - - - - -void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ) -{ - m_OriginX = a_OriginX; - m_OriginY = a_OriginY; - m_OriginZ = a_OriginZ; -} - - - - - -bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes) -{ - // Normalize the coords: - if (a_MinBlockX > a_MaxBlockX) - { - std::swap(a_MinBlockX, a_MaxBlockX); - } - if (a_MinBlockY > a_MaxBlockY) - { - std::swap(a_MinBlockY, a_MaxBlockY); - } - if (a_MinBlockZ > a_MaxBlockZ) - { - std::swap(a_MinBlockZ, a_MaxBlockZ); - } - - // Include the Max coords: - a_MaxBlockX += 1; - a_MaxBlockY += 1; - a_MaxBlockZ += 1; - - // Check coords validity: - if (a_MinBlockY < 0) - { - LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__); - a_MinBlockY = 0; - } - else if (a_MinBlockY >= cChunkDef::Height) - { - LOGWARNING("%s: MinBlockY more than chunk height, adjusting to chunk height", __FUNCTION__); - a_MinBlockY = cChunkDef::Height - 1; - } - if (a_MaxBlockY < 0) - { - LOGWARNING("%s: MaxBlockY less than zero, adjusting to zero", __FUNCTION__); - a_MaxBlockY = 0; - } - else if (a_MaxBlockY >= cChunkDef::Height) - { - LOGWARNING("%s: MaxBlockY more than chunk height, adjusting to chunk height", __FUNCTION__); - a_MaxBlockY = cChunkDef::Height - 1; - } - - // Allocate the needed memory: - Clear(); - if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes)) - { - return false; - } - m_OriginX = a_MinBlockX; - m_OriginY = a_MinBlockY; - m_OriginZ = a_MinBlockZ; - cChunkReader Reader(*this); - - // Convert block coords to chunks coords: - int MinChunkX, MaxChunkX; - int MinChunkZ, MaxChunkZ; - cChunkDef::AbsoluteToRelative(a_MinBlockX, a_MinBlockY, a_MinBlockZ, MinChunkX, MinChunkZ); - cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ); - - // Query block data: - if (!a_World->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader)) - { - Clear(); - return false; - } - - return true; -} - - - - - -bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) -{ - ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have? - a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have - - // Check coords validity: - if (a_MinBlockY < 0) - { - LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__); - a_MinBlockY = 0; - } - else if (a_MinBlockY >= cChunkDef::Height - m_SizeY) - { - LOGWARNING("%s: MinBlockY + m_SizeY more than chunk height, adjusting to chunk height", __FUNCTION__); - a_MinBlockY = cChunkDef::Height - m_SizeY - 1; - } - - return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); -} - - - - - -void cBlockArea::CopyTo(cBlockArea & a_Into) const -{ - if (&a_Into == this) - { - LOGWARNING("Trying to copy a cBlockArea into self, ignoring."); - return; - } - - a_Into.Clear(); - a_Into.SetSize(m_SizeX, m_SizeY, m_SizeZ, GetDataTypes()); - a_Into.m_OriginX = m_OriginX; - a_Into.m_OriginY = m_OriginY; - a_Into.m_OriginZ = m_OriginZ; - int BlockCount = GetBlockCount(); - if (HasBlockTypes()) - { - memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE)); - } - if (HasBlockMetas()) - { - memcpy(a_Into.m_BlockMetas, m_BlockMetas, BlockCount * sizeof(NIBBLETYPE)); - } - if (HasBlockLights()) - { - memcpy(a_Into.m_BlockLight, m_BlockLight, BlockCount * sizeof(NIBBLETYPE)); - } - if (HasBlockSkyLights()) - { - memcpy(a_Into.m_BlockSkyLight, m_BlockSkyLight, BlockCount * sizeof(NIBBLETYPE)); - } -} - - - - - -void cBlockArea::CopyFrom(const cBlockArea & a_From) -{ - a_From.CopyTo(*this); -} - - - - - -void cBlockArea::DumpToRawFile(const AString & a_FileName) -{ - cFile f; - if (!f.Open(a_FileName, cFile::fmWrite)) - { - LOGWARNING("cBlockArea: Cannot open file \"%s\" for raw dump", a_FileName.c_str()); - return; - } - UInt32 SizeX = ntohl(m_SizeX); - UInt32 SizeY = ntohl(m_SizeY); - UInt32 SizeZ = ntohl(m_SizeZ); - f.Write(&SizeX, 4); - f.Write(&SizeY, 4); - f.Write(&SizeZ, 4); - unsigned char DataTypes = GetDataTypes(); - f.Write(&DataTypes, 1); - int NumBlocks = GetBlockCount(); - if (HasBlockTypes()) - { - f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE)); - } - if (HasBlockMetas()) - { - f.Write(m_BlockMetas, NumBlocks); - } - if (HasBlockLights()) - { - f.Write(m_BlockLight, NumBlocks); - } - if (HasBlockSkyLights()) - { - f.Write(m_BlockSkyLight, NumBlocks); - } -} - - - - - -bool cBlockArea::LoadFromSchematicFile(const AString & a_FileName) -{ - // Un-GZip the contents: - AString Contents; - cGZipFile File; - if (!File.Open(a_FileName, cGZipFile::fmRead)) - { - LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str()); - return false; - } - int NumBytesRead = File.ReadRestOfFile(Contents); - if (NumBytesRead < 0) - { - LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead); - return false; - } - File.Close(); - - // Parse the NBT: - cParsedNBT NBT(Contents.data(), Contents.size()); - if (!NBT.IsValid()) - { - LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str()); - return false; - } - - return LoadFromSchematicNBT(NBT); -} - - - - - -bool cBlockArea::SaveToSchematicFile(const AString & a_FileName) -{ - cFastNBTWriter Writer("Schematic"); - Writer.AddShort("Width", m_SizeX); - Writer.AddShort("Height", m_SizeY); - Writer.AddShort("Length", m_SizeZ); - Writer.AddString("Materials", "Alpha"); - if (HasBlockTypes()) - { - Writer.AddByteArray("Blocks", (const char *)m_BlockTypes, GetBlockCount()); - } - else - { - AString Dummy(GetBlockCount(), 0); - Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size()); - } - if (HasBlockMetas()) - { - Writer.AddByteArray("Data", (const char *)m_BlockMetas, GetBlockCount()); - } - else - { - AString Dummy(GetBlockCount(), 0); - Writer.AddByteArray("Data", Dummy.data(), Dummy.size()); - } - // TODO: Save entities and block entities - Writer.BeginList("Entities", TAG_Compound); - Writer.EndList(); - Writer.BeginList("TileEntities", TAG_Compound); - Writer.EndList(); - Writer.Finish(); - - // Save to file - cGZipFile File; - if (!File.Open(a_FileName, cGZipFile::fmWrite)) - { - LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str()); - return false; - } - if (!File.Write(Writer.GetResult())) - { - LOG("Cannot write data to file \"%s\".", a_FileName.c_str()); - return false; - } - return true; -} - - - - - -void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ) -{ - if ( - (a_AddMinX + a_SubMaxX >= m_SizeX) || - (a_AddMinY + a_SubMaxY >= m_SizeY) || - (a_AddMinZ + a_SubMaxZ >= m_SizeZ) - ) - { - LOGWARNING("cBlockArea:Crop called with more croping than the dimensions: %d x %d x %d with cropping %d, %d and %d", - m_SizeX, m_SizeY, m_SizeZ, - a_AddMinX + a_SubMaxX, a_AddMinY + a_SubMaxY, a_AddMinZ + a_SubMaxZ - ); - return; - } - - if (HasBlockTypes()) - { - CropBlockTypes(a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); - } - if (HasBlockMetas()) - { - CropNibbles(m_BlockMetas, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); - } - if (HasBlockLights()) - { - CropNibbles(m_BlockLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); - } - if (HasBlockSkyLights()) - { - CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); - } - m_OriginX += a_AddMinX; - m_OriginY += a_AddMinY; - m_OriginZ += a_AddMinZ; - m_SizeX -= a_AddMinX + a_SubMaxX; - m_SizeY -= a_AddMinY + a_SubMaxY; - m_SizeZ -= a_AddMinZ + a_SubMaxZ; -} - - - - - -void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) -{ - if (HasBlockTypes()) - { - ExpandBlockTypes(a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); - } - if (HasBlockMetas()) - { - ExpandNibbles(m_BlockMetas, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); - } - if (HasBlockLights()) - { - ExpandNibbles(m_BlockLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); - } - if (HasBlockSkyLights()) - { - ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); - } - m_OriginX -= a_SubMinX; - m_OriginY -= a_SubMinY; - m_OriginZ -= a_SubMinZ; - m_SizeX += a_SubMinX + a_AddMaxX; - m_SizeY += a_SubMinY + a_AddMaxY; - m_SizeZ += a_SubMinZ + a_AddMaxZ; -} - - - - - -void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy) -{ - // Block types are compulsory, block metas are voluntary - if (!HasBlockTypes() || !a_Src.HasBlockTypes()) - { - LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__); - return; - } - - // Dst is *this, Src is a_Src - int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading - int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing - int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy - - int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading - int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing - int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy - - int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading - int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing - int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy - - const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas(); - NIBBLETYPE * DstMetas = m_BlockMetas; - bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL)); - - if (IsDummyMetas) - { - SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()]; - DstMetas = new NIBBLETYPE[GetBlockCount()]; - } - - switch (a_Strategy) - { - case msOverwrite: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_SizeX, m_SizeY, m_SizeZ, - MergeCombinatorOverwrite - ); - break; - } // case msOverwrite - - case msFillAir: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_SizeX, m_SizeY, m_SizeZ, - MergeCombinatorFillAir - ); - break; - } // case msFillAir - - case msImprint: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_SizeX, m_SizeY, m_SizeZ, - MergeCombinatorImprint - ); - break; - } // case msImprint - - case msLake: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_SizeX, m_SizeY, m_SizeZ, - MergeCombinatorLake - ); - break; - } // case msLake - - default: - { - LOGWARNING("Unknown block area merge strategy: %d", a_Strategy); - ASSERT(!"Unknown block area merge strategy"); - break; - } - } // switch (a_Strategy) - - if (IsDummyMetas) - { - delete[] SrcMetas; - delete[] DstMetas; - } -} - - - - - -void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight) -{ - if ((a_DataTypes & GetDataTypes()) != a_DataTypes) - { - LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)", - __FUNCTION__, a_DataTypes, GetDataTypes() - ); - a_DataTypes = a_DataTypes & GetDataTypes(); - } - - int BlockCount = GetBlockCount(); - if ((a_DataTypes & baTypes) != 0) - { - for (int i = 0; i < BlockCount; i++) - { - m_BlockTypes[i] = a_BlockType; - } - } - if ((a_DataTypes & baMetas) != 0) - { - for (int i = 0; i < BlockCount; i++) - { - m_BlockMetas[i] = a_BlockMeta; - } - } - if ((a_DataTypes & baLight) != 0) - { - for (int i = 0; i < BlockCount; i++) - { - m_BlockLight[i] = a_BlockLight; - } - } - if ((a_DataTypes & baSkyLight) != 0) - { - for (int i = 0; i < BlockCount; i++) - { - m_BlockSkyLight[i] = a_BlockSkyLight; - } - } -} - - - - - -void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ, - int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight -) -{ - if ((a_DataTypes & GetDataTypes()) != a_DataTypes) - { - LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)", - __FUNCTION__, a_DataTypes, GetDataTypes() - ); - a_DataTypes = a_DataTypes & GetDataTypes(); - } - - if ((a_DataTypes & baTypes) != 0) - { - for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) - { - m_BlockTypes[MakeIndex(x, y, z)] = a_BlockType; - } // for x, z, y - } - if ((a_DataTypes & baMetas) != 0) - { - for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) - { - m_BlockMetas[MakeIndex(x, y, z)] = a_BlockMeta; - } // for x, z, y - } - if ((a_DataTypes & baLight) != 0) - { - for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) - { - m_BlockLight[MakeIndex(x, y, z)] = a_BlockLight; - } // for x, z, y - } - if ((a_DataTypes & baSkyLight) != 0) - { - for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) - { - m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight; - } // for x, z, y - } -} - - - - - -void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2, - int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight -) -{ - // Bresenham-3D algorithm for drawing lines: - int dx = abs(a_RelX2 - a_RelX1); - int dy = abs(a_RelY2 - a_RelY1); - int dz = abs(a_RelZ2 - a_RelZ1); - int sx = (a_RelX1 < a_RelX2) ? 1 : -1; - int sy = (a_RelY1 < a_RelY2) ? 1 : -1; - int sz = (a_RelZ1 < a_RelZ2) ? 1 : -1; - int err = dx - dz; - - if (dx >= std::max(dy, dz)) // x dominant - { - int yd = dy - dx / 2; - int zd = dz - dx / 2; - - while (true) - { - RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight); - - if (a_RelX1 == a_RelX2) - { - break; - } - - if (yd >= 0) // move along y - { - a_RelY1 += sy; - yd -= dx; - } - - if (zd >= 0) // move along z - { - a_RelZ1 += sz; - zd -= dx; - } - - // move along x - a_RelX1 += sx; - yd += dy; - zd += dz; - } - } - else if (dy >= std::max(dx, dz)) // y dominant - { - int xd = dx - dy / 2; - int zd = dz - dy / 2; - - while (true) - { - RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight); - - if (a_RelY1 == a_RelY2) - { - break; - } - - if (xd >= 0) // move along x - { - a_RelX1 += sx; - xd -= dy; - } - - if (zd >= 0) // move along z - { - a_RelZ1 += sz; - zd -= dy; - } - - // move along y - a_RelY1 += sy; - xd += dx; - zd += dz; - } - } - else - { - // z dominant - ASSERT(dz >= std::max(dx, dy)); - int xd = dx - dz / 2; - int yd = dy - dz / 2; - - while (true) - { - RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight); - - if (a_RelZ1 == a_RelZ2) - { - break; - } - - if (xd >= 0) // move along x - { - a_RelX1 += sx; - xd -= dz; - } - - if (yd >= 0) // move along y - { - a_RelY1 += sy; - yd -= dz; - } - - // move along z - a_RelZ1 += sz; - xd += dx; - yd += dy; - } - } // if (which dimension is dominant) -} - - - - - -void cBlockArea::RotateCCW(void) -{ - if (!HasBlockTypes()) - { - LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!"); - return; - } - - if (!HasBlockMetas()) - { - // There are no blockmetas to rotate, just use the NoMeta function - RotateCCWNoMeta(); - return; - } - - // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time: - BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; - NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; - for (int x = 0; x < m_SizeX; x++) - { - int NewZ = m_SizeX - x - 1; - for (int z = 0; z < m_SizeZ; z++) - { - int NewX = z; - for (int y = 0; y < m_SizeY; y++) - { - int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ; - int OldIdx = MakeIndex(x, y, z); - NewTypes[NewIdx] = m_BlockTypes[OldIdx]; - NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]); - } // for y - } // for z - } // for x - std::swap(m_BlockTypes, NewTypes); - std::swap(m_BlockMetas, NewMetas); - delete[] NewTypes; - delete[] NewMetas; - - std::swap(m_SizeX, m_SizeZ); -} - - - - - -void cBlockArea::RotateCW(void) -{ - if (!HasBlockTypes()) - { - LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!"); - return; - } - - if (!HasBlockMetas()) - { - // There are no blockmetas to rotate, just use the NoMeta function - RotateCWNoMeta(); - return; - } - - // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time: - BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; - NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; - for (int x = 0; x < m_SizeX; x++) - { - int NewZ = x; - for (int z = 0; z < m_SizeZ; z++) - { - int NewX = m_SizeZ - z - 1; - for (int y = 0; y < m_SizeY; y++) - { - int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ; - int OldIdx = MakeIndex(x, y, z); - NewTypes[NewIdx] = m_BlockTypes[OldIdx]; - NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]); - } // for y - } // for z - } // for x - std::swap(m_BlockTypes, NewTypes); - std::swap(m_BlockMetas, NewMetas); - delete[] NewTypes; - delete[] NewMetas; - - std::swap(m_SizeX, m_SizeZ); -} - - - - - -void cBlockArea::MirrorXY(void) -{ - if (!HasBlockTypes()) - { - LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); - return; - } - - if (!HasBlockMetas()) - { - // There are no blockmetas to mirror, just use the NoMeta function - MirrorXYNoMeta(); - return; - } - - // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: - int HalfZ = m_SizeZ / 2; - int MaxZ = m_SizeZ - 1; - for (int y = 0; y < m_SizeY; y++) - { - for (int z = 0; z < HalfZ; z++) - { - for (int x = 0; x < m_SizeX; x++) - { - int Idx1 = MakeIndex(x, y, z); - int Idx2 = MakeIndex(x, y, MaxZ - z); - std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); - NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXY(m_BlockMetas[Idx1]); - NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXY(m_BlockMetas[Idx2]); - m_BlockMetas[Idx1] = Meta2; - m_BlockMetas[Idx2] = Meta1; - } // for x - } // for z - } // for y -} - - - - - -void cBlockArea::MirrorXZ(void) -{ - if (!HasBlockTypes()) - { - LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); - return; - } - - if (!HasBlockMetas()) - { - // There are no blockmetas to mirror, just use the NoMeta function - MirrorXZNoMeta(); - return; - } - - // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: - int HalfY = m_SizeY / 2; - int MaxY = m_SizeY - 1; - for (int y = 0; y < HalfY; y++) - { - for (int z = 0; z < m_SizeZ; z++) - { - for (int x = 0; x < m_SizeX; x++) - { - int Idx1 = MakeIndex(x, y, z); - int Idx2 = MakeIndex(x, MaxY - y, z); - std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); - NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXZ(m_BlockMetas[Idx1]); - NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXZ(m_BlockMetas[Idx2]); - m_BlockMetas[Idx1] = Meta2; - m_BlockMetas[Idx2] = Meta1; - } // for x - } // for z - } // for y -} - - - - - -void cBlockArea::MirrorYZ(void) -{ - if (!HasBlockTypes()) - { - LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); - return; - } - - if (!HasBlockMetas()) - { - // There are no blockmetas to mirror, just use the NoMeta function - MirrorYZNoMeta(); - return; - } - - // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: - int HalfX = m_SizeX / 2; - int MaxX = m_SizeX - 1; - for (int y = 0; y < m_SizeY; y++) - { - for (int z = 0; z < m_SizeZ; z++) - { - for (int x = 0; x < HalfX; x++) - { - int Idx1 = MakeIndex(x, y, z); - int Idx2 = MakeIndex(MaxX - x, y, z); - std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); - NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]); - NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]); - m_BlockMetas[Idx1] = Meta2; - m_BlockMetas[Idx2] = Meta1; - } // for x - } // for z - } // for y -} - - - - - -void cBlockArea::RotateCCWNoMeta(void) -{ - if (HasBlockTypes()) - { - BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; - for (int x = 0; x < m_SizeX; x++) - { - int NewZ = m_SizeX - x - 1; - for (int z = 0; z < m_SizeZ; z++) - { - int NewX = z; - for (int y = 0; y < m_SizeY; y++) - { - NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; - } // for y - } // for z - } // for x - std::swap(m_BlockTypes, NewTypes); - delete[] NewTypes; - } - if (HasBlockMetas()) - { - NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; - for (int x = 0; x < m_SizeX; x++) - { - int NewZ = m_SizeX - x - 1; - for (int z = 0; z < m_SizeZ; z++) - { - int NewX = z; - for (int y = 0; y < m_SizeY; y++) - { - NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; - } // for y - } // for z - } // for x - std::swap(m_BlockMetas, NewMetas); - delete[] NewMetas; - } - std::swap(m_SizeX, m_SizeZ); -} - - - - - -void cBlockArea::RotateCWNoMeta(void) -{ - if (HasBlockTypes()) - { - BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; - for (int z = 0; z < m_SizeZ; z++) - { - int NewX = m_SizeZ - z - 1; - for (int x = 0; x < m_SizeX; x++) - { - int NewZ = x; - for (int y = 0; y < m_SizeY; y++) - { - NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; - } // for y - } // for x - } // for z - std::swap(m_BlockTypes, NewTypes); - delete[] NewTypes; - } - if (HasBlockMetas()) - { - NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; - for (int z = 0; z < m_SizeZ; z++) - { - int NewX = m_SizeZ - z - 1; - for (int x = 0; x < m_SizeX; x++) - { - int NewZ = x; - for (int y = 0; y < m_SizeY; y++) - { - NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; - } // for y - } // for x - } // for z - std::swap(m_BlockMetas, NewMetas); - delete[] NewMetas; - } - std::swap(m_SizeX, m_SizeZ); -} - - - - - -void cBlockArea::MirrorXYNoMeta(void) -{ - int HalfZ = m_SizeZ / 2; - int MaxZ = m_SizeZ - 1; - if (HasBlockTypes()) - { - for (int y = 0; y < m_SizeY; y++) - { - for (int z = 0; z < HalfZ; z++) - { - for (int x = 0; x < m_SizeX; x++) - { - std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, y, MaxZ - z)]); - } // for x - } // for z - } // for y - } // if (HasBlockTypes) - - if (HasBlockMetas()) - { - for (int y = 0; y < m_SizeY; y++) - { - for (int z = 0; z < HalfZ; z++) - { - for (int x = 0; x < m_SizeX; x++) - { - std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, y, MaxZ - z)]); - } // for x - } // for z - } // for y - } // if (HasBlockMetas) -} - - - - - -void cBlockArea::MirrorXZNoMeta(void) -{ - int HalfY = m_SizeY / 2; - int MaxY = m_SizeY - 1; - if (HasBlockTypes()) - { - for (int y = 0; y < HalfY; y++) - { - for (int z = 0; z < m_SizeZ; z++) - { - for (int x = 0; x < m_SizeX; x++) - { - std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, MaxY - y, z)]); - } // for x - } // for z - } // for y - } // if (HasBlockTypes) - - if (HasBlockMetas()) - { - for (int y = 0; y < HalfY; y++) - { - for (int z = 0; z < m_SizeZ; z++) - { - for (int x = 0; x < m_SizeX; x++) - { - std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, MaxY - y, z)]); - } // for x - } // for z - } // for y - } // if (HasBlockMetas) -} - - - - - -void cBlockArea::MirrorYZNoMeta(void) -{ - int HalfX = m_SizeX / 2; - int MaxX = m_SizeX - 1; - if (HasBlockTypes()) - { - for (int y = 0; y < m_SizeY; y++) - { - for (int z = 0; z < m_SizeZ; z++) - { - for (int x = 0; x < HalfX; x++) - { - std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(MaxX - x, y, z)]); - } // for x - } // for z - } // for y - } // if (HasBlockTypes) - - if (HasBlockMetas()) - { - for (int y = 0; y < m_SizeY; y++) - { - for (int z = 0; z < m_SizeZ; z++) - { - for (int x = 0; x < HalfX; x++) - { - std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(MaxX - x, y, z)]); - } // for x - } // for z - } // for y - } // if (HasBlockMetas) -} - - - - - -void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) -{ - if (m_BlockTypes == NULL) - { - LOGWARNING("cBlockArea: BlockTypes have not been read!"); - return; - } - m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_BlockType; -} - - - - - -void cBlockArea::SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) -{ - SetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType); -} - - - - - -void cBlockArea::SetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta) -{ - SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockMeta, m_BlockMetas); -} - - - - - -void cBlockArea::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta) -{ - SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, m_BlockMetas); -} - - - - - -void cBlockArea::SetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight) -{ - SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockLight, m_BlockLight); -} - - - - - -void cBlockArea::SetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight) -{ - SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockLight, m_BlockLight); -} - - - - - -void cBlockArea::SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight) -{ - SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockSkyLight, m_BlockSkyLight); -} - - - - - -void cBlockArea::SetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight) -{ - SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockSkyLight, m_BlockSkyLight); -} - - - - - -BLOCKTYPE cBlockArea::GetRelBlockType(int a_RelX, int a_RelY, int a_RelZ) const -{ - if (m_BlockTypes == NULL) - { - LOGWARNING("cBlockArea: BlockTypes have not been read!"); - return E_BLOCK_AIR; - } - return m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)]; -} - - - - - -BLOCKTYPE cBlockArea::GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const -{ - return GetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ); -} - - - - - -NIBBLETYPE cBlockArea::GetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const -{ - return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockMetas); -} - - - - - -NIBBLETYPE cBlockArea::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) const -{ - return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockMetas); -} - - - - - -NIBBLETYPE cBlockArea::GetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ) const -{ - return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockLight); -} - - - - - -NIBBLETYPE cBlockArea::GetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) const -{ - return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockLight); -} - - - - - -NIBBLETYPE cBlockArea::GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const -{ - return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockSkyLight); -} - - - - - -NIBBLETYPE cBlockArea::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) const -{ - return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockSkyLight); -} - - - - - -void cBlockArea::SetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - SetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta); -} - - - - - -void cBlockArea::SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - int idx = MakeIndex(a_RelX, a_RelY, a_RelZ); - if (m_BlockTypes == NULL) - { - LOGWARNING("%s: BlockTypes not available but requested to be written to.", __FUNCTION__); - } - else - { - m_BlockTypes[idx] = a_BlockType; - } - if (m_BlockMetas == NULL) - { - LOGWARNING("%s: BlockMetas not available but requested to be written to.", __FUNCTION__); - } - else - { - m_BlockMetas[idx] = a_BlockMeta; - } -} - - - - - -void cBlockArea::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const -{ - return GetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta); -} - - - - - -void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const -{ - int idx = MakeIndex(a_RelX, a_RelY, a_RelZ); - if (m_BlockTypes == NULL) - { - LOGWARNING("cBlockArea: BlockTypes have not been read!"); - a_BlockType = E_BLOCK_AIR; - } - else - { - a_BlockType = m_BlockTypes[idx]; - } - - if (m_BlockMetas == NULL) - { - LOGWARNING("cBlockArea: BlockMetas have not been read!"); - a_BlockMeta = 0; - } - else - { - a_BlockMeta = m_BlockMetas[idx]; - } -} - - - - - -int cBlockArea::GetDataTypes(void) const -{ - int res = 0; - if (m_BlockTypes != NULL) - { - res |= baTypes; - } - if (m_BlockMetas != NULL) - { - res |= baMetas; - } - if (m_BlockLight != NULL) - { - res |= baLight; - } - if (m_BlockSkyLight != NULL) - { - res |= baSkyLight; - } - return res; -} - - - - - -bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes) -{ - ASSERT(m_BlockTypes == NULL); // Has been cleared - - if (a_DataTypes & baTypes) - { - m_BlockTypes = new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ]; - if (m_BlockTypes == NULL) - { - return false; - } - } - if (a_DataTypes & baMetas) - { - m_BlockMetas = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]; - if (m_BlockMetas == NULL) - { - delete[] m_BlockTypes; - return false; - } - } - if (a_DataTypes & baLight) - { - m_BlockLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]; - if (m_BlockLight == NULL) - { - delete[] m_BlockMetas; - delete[] m_BlockTypes; - return false; - } - } - if (a_DataTypes & baSkyLight) - { - m_BlockSkyLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]; - if (m_BlockSkyLight == NULL) - { - delete[] m_BlockLight; - delete[] m_BlockMetas; - delete[] m_BlockTypes; - return false; - } - } - m_SizeX = a_SizeX; - m_SizeY = a_SizeY; - m_SizeZ = a_SizeZ; - return true; -} - - - - - -int cBlockArea::MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const -{ - ASSERT(a_RelX >= 0); - ASSERT(a_RelX < m_SizeX); - ASSERT(a_RelY >= 0); - ASSERT(a_RelY < m_SizeY); - ASSERT(a_RelZ >= 0); - ASSERT(a_RelZ < m_SizeZ); - - return a_RelX + a_RelZ * m_SizeX + a_RelY * m_SizeX * m_SizeZ; -} - - - - - -void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array) -{ - if (a_Array == NULL) - { - LOGWARNING("cBlockArea: datatype has not been read!"); - return; - } - a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value; -} - - - - - -void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array) -{ - SetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Value, a_Array); -} - - - - - -NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const -{ - if (a_Array == NULL) - { - LOGWARNING("cBlockArea: datatype has not been read!"); - return 16; - } - return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)]; -} - - - - - -NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const -{ - return GetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Array); -} - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBlockArea::cChunkReader: - -cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) : - m_Area(a_Area), - m_OriginX(a_Area.m_OriginX), - m_OriginY(a_Area.m_OriginY), - m_OriginZ(a_Area.m_OriginZ) -{ -} - - - - - -void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc) -{ - int SizeY = m_Area.m_SizeY; - int MinY = m_OriginY; - - // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union) - // OffX, OffZ are the offsets of the current chunk data from the area origin - // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders - int SizeX = cChunkDef::Width; - int SizeZ = cChunkDef::Width; - int OffX, OffZ; - int BaseX, BaseZ; - OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX; - if (OffX < 0) - { - BaseX = -OffX; - SizeX += OffX; // SizeX is decreased, OffX is negative - OffX = 0; - } - else - { - BaseX = 0; - } - OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ; - if (OffZ < 0) - { - BaseZ = -OffZ; - SizeZ += OffZ; // SizeZ is decreased, OffZ is negative - OffZ = 0; - } - else - { - BaseZ = 0; - } - // If the chunk extends beyond the area in the X or Z axis, cut off the Size: - if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX) - { - SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX); - } - if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ) - { - SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ); - } - - for (int y = 0; y < SizeY; y++) - { - int ChunkY = MinY + y; - int AreaY = y; - for (int z = 0; z < SizeZ; z++) - { - int ChunkZ = BaseZ + z; - int AreaZ = OffZ + z; - for (int x = 0; x < SizeX; x++) - { - int ChunkX = BaseX + x; - int AreaX = OffX + x; - a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ); - } // for x - } // for z - } // for y -} - - - - - -bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ) -{ - m_CurrentChunkX = a_ChunkX; - m_CurrentChunkZ = a_ChunkZ; - return true; -} - - - - - -void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes) -{ - if (m_Area.m_BlockTypes == NULL) - { - // Don't want BlockTypes - return; - } - - int SizeY = m_Area.m_SizeY; - int MinY = m_OriginY; - - // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union) - // OffX, OffZ are the offsets of the current chunk data from the area origin - // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders - int SizeX = cChunkDef::Width; - int SizeZ = cChunkDef::Width; - int OffX, OffZ; - int BaseX, BaseZ; - OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX; - if (OffX < 0) - { - BaseX = -OffX; - SizeX += OffX; // SizeX is decreased, OffX is negative - OffX = 0; - } - else - { - BaseX = 0; - } - OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ; - if (OffZ < 0) - { - BaseZ = -OffZ; - SizeZ += OffZ; // SizeZ is decreased, OffZ is negative - OffZ = 0; - } - else - { - BaseZ = 0; - } - // If the chunk extends beyond the area in the X or Z axis, cut off the Size: - if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX) - { - SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX); - } - if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ) - { - SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ); - } - - for (int y = 0; y < SizeY; y++) - { - int ChunkY = MinY + y; - int AreaY = y; - for (int z = 0; z < SizeZ; z++) - { - int ChunkZ = BaseZ + z; - int AreaZ = OffZ + z; - for (int x = 0; x < SizeX; x++) - { - int ChunkX = BaseX + x; - int AreaX = OffX + x; - m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetBlock(a_BlockTypes, ChunkX, ChunkY, ChunkZ); - } // for x - } // for z - } // for y -} - - - - - -void cBlockArea::cChunkReader::BlockMeta(const NIBBLETYPE * a_BlockMetas) -{ - if (m_Area.m_BlockMetas == NULL) - { - // Don't want metas - return; - } - CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas); -} - - - - - -void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight) -{ - if (m_Area.m_BlockLight == NULL) - { - // Don't want light - return; - } - CopyNibbles(m_Area.m_BlockLight, a_BlockLight); -} - - - - - -void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) -{ - if (m_Area.m_BlockSkyLight == NULL) - { - // Don't want skylight - return; - } - CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight); -} - - - - - -void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ) -{ - int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX; - int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY; - int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ; - BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[NewSizeX * NewSizeY * NewSizeZ]; - int idx = 0; - for (int y = 0; y < NewSizeY; y++) - { - for (int z = 0; z < NewSizeZ; z++) - { - for (int x = 0; x < NewSizeX; x++) - { - int OldIndex = MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ); - NewBlockTypes[idx++] = m_BlockTypes[OldIndex]; - } // for x - } // for z - } // for y - delete m_BlockTypes; - m_BlockTypes = NewBlockTypes; -} - - - - - -void cBlockArea::CropNibbles(NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ) -{ - int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX; - int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY; - int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ; - NIBBLETYPE * NewNibbles = new NIBBLETYPE[NewSizeX * NewSizeY * NewSizeZ]; - int idx = 0; - for (int y = 0; y < NewSizeY; y++) - { - for (int z = 0; z < NewSizeZ; z++) - { - for (int x = 0; x < NewSizeX; x++) - { - NewNibbles[idx++] = a_Array[MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ)]; - } // for x - } // for z - } // for y - delete a_Array; - a_Array = NewNibbles; -} - - - - - -void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) -{ - int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX; - int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY; - int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ; - int BlockCount = NewSizeX * NewSizeY * NewSizeZ; - BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount]; - memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE)); - int OldIndex = 0; - for (int y = 0; y < m_SizeY; y++) - { - int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ; - for (int z = 0; z < m_SizeZ; z++) - { - int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX; - int idx = IndexBaseZ + a_SubMinX; - for (int x = 0; x < m_SizeX; x++) - { - NewBlockTypes[idx++] = m_BlockTypes[OldIndex++]; - } // for x - } // for z - } // for y - delete m_BlockTypes; - m_BlockTypes = NewBlockTypes; -} - - - - - -void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) -{ - int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX; - int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY; - int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ; - int BlockCount = NewSizeX * NewSizeY * NewSizeZ; - NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount]; - memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE)); - int OldIndex = 0; - for (int y = 0; y < m_SizeY; y++) - { - int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ; - for (int z = 0; z < m_SizeZ; z++) - { - int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX; - int idx = IndexBaseZ + a_SubMinX; - for (int x = 0; x < m_SizeX; x++) - { - NewNibbles[idx++] = a_Array[OldIndex++]; - } // for x - } // for z - } // for y - delete a_Array; - a_Array = NewNibbles; -} - - - - - -bool cBlockArea::LoadFromSchematicNBT(cParsedNBT & a_NBT) -{ - int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials"); - if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String)) - { - AString Materials = a_NBT.GetString(TMaterials); - if (Materials.compare("Alpha") != 0) - { - LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str()); - return false; - } - } - int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width"); - int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height"); - int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length"); - if ( - (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) || - (a_NBT.GetType(TSizeX) != TAG_Short) || - (a_NBT.GetType(TSizeY) != TAG_Short) || - (a_NBT.GetType(TSizeZ) != TAG_Short) - ) - { - LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)", - TSizeX, TSizeY, TSizeZ, - a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ) - ); - return false; - } - - int SizeX = a_NBT.GetShort(TSizeX); - int SizeY = a_NBT.GetShort(TSizeY); - int SizeZ = a_NBT.GetShort(TSizeZ); - if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1)) - { - LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ); - return false; - } - - int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks"); - int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data"); - if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray)) - { - LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes); - return false; - } - bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray); - - Clear(); - SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (baTypes | baMetas) : baTypes); - - // Copy the block types and metas: - int NumBytes = m_SizeX * m_SizeY * m_SizeZ; - if (a_NBT.GetDataLength(TBlockTypes) < NumBytes) - { - LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.", - NumBytes, a_NBT.GetDataLength(TBlockTypes) - ); - NumBytes = a_NBT.GetDataLength(TBlockTypes); - } - memcpy(m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes); - - if (AreMetasPresent) - { - int NumBytes = m_SizeX * m_SizeY * m_SizeZ; - if (a_NBT.GetDataLength(TBlockMetas) < NumBytes) - { - LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.", - NumBytes, a_NBT.GetDataLength(TBlockMetas) - ); - NumBytes = a_NBT.GetDataLength(TBlockMetas); - } - memcpy(m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes); - } - - return true; -} - - - - -void cBlockArea::RelSetData( - int a_RelX, int a_RelY, int a_RelZ, - int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight -) -{ - int Index = MakeIndex(a_RelX, a_RelY, a_RelZ); - if ((a_DataTypes & baTypes) != 0) - { - m_BlockTypes[Index] = a_BlockType; - } - if ((a_DataTypes & baMetas) != 0) - { - m_BlockMetas[Index] = a_BlockMeta; - } - if ((a_DataTypes & baLight) != 0) - { - m_BlockLight[Index] = a_BlockLight; - } - if ((a_DataTypes & baSkyLight) != 0) - { - m_BlockSkyLight[Index] = a_BlockSkyLight; - } -} - - - - diff --git a/source/BlockArea.h b/source/BlockArea.h deleted file mode 100644 index 075cc99ec..000000000 --- a/source/BlockArea.h +++ /dev/null @@ -1,310 +0,0 @@ - -// BlockArea.h - -// Interfaces to the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries -// The object also supports writing the blockdata back into cWorld, even into other coords - -// NOTE: All Nibble values (meta, blocklight, skylight) are stored one-nibble-per-byte for faster access / editting! - - - - - -#pragma once - - - - - -// fwd: World.h -class cWorld; - -// fwd: FastNBT.h -class cParsedNBT; - - - - - -// tolua_begin -class cBlockArea -{ - // tolua_end - DISALLOW_COPY_AND_ASSIGN(cBlockArea); - // tolua_begin - -public: - - /// What data is to be queried (bit-mask) - enum - { - baTypes = 1, - baMetas = 2, - baLight = 4, - baSkyLight = 8, - } ; - - enum eMergeStrategy - { - msOverwrite, - msFillAir, - msImprint, - msLake, - } ; - - cBlockArea(void); - ~cBlockArea(); - - /// Clears the data stored to reclaim memory - void Clear(void); - - /** Creates a new area of the specified size and contents. - Origin is set to all zeroes. - BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light. - */ - void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas); - - /// Resets the origin. No other changes are made, contents are untouched. - void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ); - - /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive. - bool Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas); - - // TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write - // A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again - - /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all - bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas); - - /// Copies this object's contents into the specified BlockArea. - void CopyTo(cBlockArea & a_Into) const; - - /// Copies the contents from the specified BlockArea into this object. - void CopyFrom(const cBlockArea & a_From); - - /// For testing purposes only, dumps the area into a file. - void DumpToRawFile(const AString & a_FileName); - - /// Loads an area from a .schematic file. Returns true if successful - bool LoadFromSchematicFile(const AString & a_FileName); - - /// Saves the area into a .schematic file. Returns true if successful - bool SaveToSchematicFile(const AString & a_FileName); - - /// Crops the internal contents by the specified amount of blocks from each border. - void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); - - /// Expands the internal contents by the specified amount of blocks from each border - void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); - - /** Merges another block area into this one, using the specified block combinating strategy - This function combines another BlockArea into the current object. - The strategy parameter specifies how individual blocks are combined together, using the table below. - - | area block | result | - | this | Src | msOverwrite | msFillAir | msImprint | - +------+-----+-------------+-----------+-----------+ - | air | air | air | air | air | - | A | air | air | A | A | - | air | B | B | B | B | - | A | B | B | A | B | - - So to sum up: - - msOverwrite completely overwrites all blocks with the Src's blocks - - msFillAir overwrites only those blocks that were air - - msImprint overwrites with only those blocks that are non-air - - Special strategies: - msLake (evaluate top-down, first match wins): - | area block | | - | this | Src | result | - +----------+--------+--------+ - | A | sponge | A | Sponge is the NOP block - | * | air | air | Air always gets hollowed out, even under the oceans - | water | * | water | Water is never overwritten - | lava | * | lava | Lava is never overwritten - | * | water | water | Water always overwrites anything - | * | lava | lava | Lava always overwrites anything - | dirt | stone | stone | Stone overwrites dirt - | grass | stone | stone | ... and grass - | mycelium | stone | stone | ... and mycelium - | A | stone | A | ... but nothing else - | A | * | A | Everything else is left as it is - - */ - void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy); - - /// Fills the entire block area with the specified data - void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f); - - /// Fills a cuboid inside the block area with the specified data - void FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ, - int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, - NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f - ); - - /// Draws a line from between two points with the specified data - void RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2, - int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, - NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f - ); - - /// Rotates the entire area counter-clockwise around the Y axis - void RotateCCW(void); - - /// Rotates the entire area clockwise around the Y axis - void RotateCW(void); - - /// Mirrors the entire area around the XY plane - void MirrorXY(void); - - /// Mirrors the entire area around the XZ plane - void MirrorXZ(void); - - /// Mirrors the entire area around the YZ plane - void MirrorYZ(void); - - /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta - void RotateCCWNoMeta(void); - - /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta - void RotateCWNoMeta(void); - - /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta - void MirrorXYNoMeta(void); - - /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta - void MirrorXZNoMeta(void); - - /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta - void MirrorYZNoMeta(void); - - // Setters: - void SetRelBlockType (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType); - void SetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); - void SetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta); - void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta); - void SetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight); - void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight); - void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight); - void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight); - - // Getters: - BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const; - BLOCKTYPE GetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ) const; - NIBBLETYPE GetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ) const; - NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ) const; - NIBBLETYPE GetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ) const; - NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; - NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const; - NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; - - void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; - void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; - - int GetSizeX(void) const { return m_SizeX; } - int GetSizeY(void) const { return m_SizeY; } - int GetSizeZ(void) const { return m_SizeZ; } - - int GetOriginX(void) const { return m_OriginX; } - int GetOriginY(void) const { return m_OriginY; } - int GetOriginZ(void) const { return m_OriginZ; } - - /// Returns the datatypes that are stored in the object (bitmask of baXXX values) - int GetDataTypes(void) const; - - bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); } - bool HasBlockMetas (void) const { return (m_BlockMetas != NULL); } - bool HasBlockLights (void) const { return (m_BlockLight != NULL); } - bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != NULL); } - - // tolua_end - - // Clients can use these for faster access to all blocktypes. Be careful though! - /// Returns the internal pointer to the block types - BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; } - NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block! - NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block! - NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block! - int GetBlockCount(void) const { return m_SizeX * m_SizeY * m_SizeZ; } - int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const; - -protected: - friend class cChunkDesc; - - class cChunkReader : - public cChunkDataCallback - { - public: - cChunkReader(cBlockArea & a_Area); - - protected: - cBlockArea & m_Area; - int m_OriginX; - int m_OriginY; - int m_OriginZ; - int m_CurrentChunkX; - int m_CurrentChunkZ; - - void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc); - - // cChunkDataCallback overrides: - virtual bool Coords (int a_ChunkX, int a_ChunkZ) override; - virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override; - virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override; - virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override; - virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override; - } ; - - typedef NIBBLETYPE * NIBBLEARRAY; - - - int m_OriginX; - int m_OriginY; - int m_OriginZ; - int m_SizeX; - int m_SizeY; - int m_SizeZ; - - BLOCKTYPE * m_BlockTypes; - NIBBLETYPE * m_BlockMetas; // Each meta is stored as a separate byte for faster access - NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access - NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access - - /// Clears the data stored and prepares a fresh new block area with the specified dimensions - bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes); - - // Basic Setters: - void SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); - void SetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); - - // Basic Getters: - NIBBLETYPE GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const; - NIBBLETYPE GetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const; - - // Crop helpers: - void CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); - void CropNibbles (NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); - - // Expand helpers: - void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); - void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); - - /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful. - bool LoadFromSchematicNBT(cParsedNBT & a_NBT); - - /// Sets the specified datatypes at the specified location. - void RelSetData( - int a_RelX, int a_RelY, int a_RelZ, - int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight - ); - // tolua_begin -} ; -// tolua_end - - - - diff --git a/source/BlockEntities/BlockEntity.cpp b/source/BlockEntities/BlockEntity.cpp deleted file mode 100644 index 41a488717..000000000 --- a/source/BlockEntities/BlockEntity.cpp +++ /dev/null @@ -1,44 +0,0 @@ - -// BlockEntity.cpp - -// Implements the cBlockEntity class that is the common ancestor for all block entities - -#include "Globals.h" -#include "BlockEntity.h" -#include "ChestEntity.h" -#include "DispenserEntity.h" -#include "DropperEntity.h" -#include "FurnaceEntity.h" -#include "HopperEntity.h" -#include "JukeboxEntity.h" -#include "NoteEntity.h" -#include "SignEntity.h" - - - - - -cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) -{ - switch (a_BlockType) - { - case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_DISPENSER: return new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); - case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); - case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - } - LOGD("%s: Requesting creation of an unknown block entity - block type %d (%s)", - __FUNCTION__, a_BlockType, ItemTypeToString(a_BlockType).c_str() - ); - return NULL; -} - - - - diff --git a/source/BlockEntities/BlockEntity.h b/source/BlockEntities/BlockEntity.h deleted file mode 100644 index a2de3160a..000000000 --- a/source/BlockEntities/BlockEntity.h +++ /dev/null @@ -1,101 +0,0 @@ - -#pragma once - -#include "../ClientHandle.h" -#include "../World.h" - - - - - -namespace Json -{ - class Value; -}; - -class cPlayer; -class cPacket; - - - - - -// tolua_begin -class cBlockEntity -{ -protected: - cBlockEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - m_PosX(a_BlockX), - m_PosY(a_BlockY), - m_PosZ(a_BlockZ), - m_RelX(a_BlockX - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockX, cChunkDef::Width)), - m_RelZ(a_BlockZ - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockZ, cChunkDef::Width)), - m_BlockType(a_BlockType), - m_World(a_World) - { - } - -public: - // tolua_end - - virtual ~cBlockEntity() {}; // force a virtual destructor in all descendants - - virtual void Destroy(void) {}; - - void SetWorld(cWorld * a_World) - { - m_World = a_World; - } - - /// Creates a new block entity for the specified block type - /// If a_World is valid, then the entity is created bound to that world - /// Returns NULL for unknown block types - static cBlockEntity * CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World = NULL); - - // tolua_begin - - // Position, in absolute block coordinates: - int GetPosX(void) const { return m_PosX; } - int GetPosY(void) const { return m_PosY; } - int GetPosZ(void) const { return m_PosZ; } - - BLOCKTYPE GetBlockType(void) const { return m_BlockType; } - - cWorld * GetWorld(void) const {return m_World; } - - int GetChunkX(void) const { return FAST_FLOOR_DIV(m_PosX, cChunkDef::Width); } - int GetChunkZ(void) const { return FAST_FLOOR_DIV(m_PosZ, cChunkDef::Width); } - - int GetRelX(void) const { return m_RelX; } - int GetRelZ(void) const { return m_RelZ; } - - // tolua_end - - virtual void SaveToJson (Json::Value & a_Value) = 0; - - /// Called when a player uses this entity; should open the UI window - virtual void UsedBy( cPlayer * a_Player ) = 0; - - /** Sends the packet defining the block entity to the client specified. - To send to all eligible clients, use cWorld::BroadcastBlockEntity() - */ - virtual void SendTo(cClientHandle & a_Client) = 0; - - /// Ticks the entity; returns true if the chunk should be marked as dirty as a result of this ticking. By default does nothing. - virtual bool Tick(float a_Dt, cChunk & a_Chunk) { return false; } - -protected: - /// Position in absolute block coordinates - int m_PosX, m_PosY, m_PosZ; - - /// Position relative to the chunk, used to speed up ticking - int m_RelX, m_RelZ; - - BLOCKTYPE m_BlockType; - - cWorld * m_World; -} ; // tolua_export - - - - diff --git a/source/BlockEntities/BlockEntityWithItems.h b/source/BlockEntities/BlockEntityWithItems.h deleted file mode 100644 index 0846ae17e..000000000 --- a/source/BlockEntities/BlockEntityWithItems.h +++ /dev/null @@ -1,86 +0,0 @@ - -// BlockEntityWithItems.h - -// Declares the cBlockEntityWithItems class representing a common ancestor for all block entities that have an ItemGrid - - - - - -#pragma once - -#include "BlockEntity.h" -#include "../ItemGrid.h" - - - - - -// tolua_begin -class cBlockEntityWithItems : - public cBlockEntity - // tolua_end - // tolua doesn't seem to support multiple inheritance? - , public cItemGrid::cListener - // tolua_begin -{ - typedef cBlockEntity super; - -public: - // tolua_end - - cBlockEntityWithItems( - BLOCKTYPE a_BlockType, // Type of the block that the entity represents - int a_BlockX, int a_BlockY, int a_BlockZ, // Position of the block entity - int a_ItemGridWidth, int a_ItemGridHeight, // Dimensions of the ItemGrid - cWorld * a_World // Optional world to assign to the entity - ) : - super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World), - m_Contents(a_ItemGridWidth, a_ItemGridHeight) - { - m_Contents.AddListener(*this); - } - - virtual void Destroy(void) override - { - // Drop the contents as pickups: - ASSERT(m_World != NULL); - cItems Pickups; - m_Contents.CopyToItems(Pickups); - m_Contents.Clear(); - m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); - } - - // tolua_begin - - const cItem & GetSlot(int a_SlotNum) const { return m_Contents.GetSlot(a_SlotNum); } - const cItem & GetSlot(int a_X, int a_Y) const { return m_Contents.GetSlot(a_X, a_Y); } - - void SetSlot(int a_SlotNum, const cItem & a_Item) { m_Contents.SetSlot(a_SlotNum, a_Item); } - void SetSlot(int a_X, int a_Y, const cItem & a_Item) { m_Contents.SetSlot(a_X, a_Y, a_Item); } - - /// Returns the ItemGrid used for storing the contents - cItemGrid & GetContents(void) { return m_Contents; } - - // tolua_end - - /// Const version of the GetContents() function for C++ type-safety - const cItemGrid & GetContents(void) const { return m_Contents; } - -protected: - cItemGrid m_Contents; - - // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) - { - ASSERT(a_Grid == &m_Contents); - if (m_World != NULL) - { - m_World->MarkChunkDirty(GetChunkX(), GetChunkZ()); - } - } -} ; // tolua_export - - - - diff --git a/source/BlockEntities/ChestEntity.cpp b/source/BlockEntities/ChestEntity.cpp deleted file mode 100644 index ca2626bc9..000000000 --- a/source/BlockEntities/ChestEntity.cpp +++ /dev/null @@ -1,172 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "ChestEntity.h" -#include "../Item.h" -#include "../Entities/Player.h" -#include "../UI/Window.h" -#include - - - - - -cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_CHEST, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World) -{ - cBlockEntityWindowOwner::SetBlockEntity(this); -} - - - - - -cChestEntity::~cChestEntity() -{ - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->OwnerDestroyed(); - } -} - - - - - -bool cChestEntity::LoadFromJson(const Json::Value & a_Value) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - Json::Value AllSlots = a_Value.get("Slots", 0); - int SlotIdx = 0; - for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) - { - cItem Item; - Item.FromJson(*itr); - SetSlot(SlotIdx, Item); - SlotIdx++; - } - return true; -} - - - - - -void cChestEntity::SaveToJson(Json::Value & a_Value) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - Json::Value AllSlots; - for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) - { - Json::Value Slot; - m_Contents.GetSlot(i).GetJson(Slot); - AllSlots.append(Slot); - } - a_Value["Slots"] = AllSlots; -} - - - - - -void cChestEntity::SendTo(cClientHandle & a_Client) -{ - // The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance - // All the actual handling is in the cWindow UI code that gets called when the chest is rclked - - UNUSED(a_Client); -} - - - - - -void cChestEntity::UsedBy(cPlayer * a_Player) -{ - // If the window is not created, open it anew: - cWindow * Window = GetWindow(); - if (Window == NULL) - { - OpenNewWindow(); - Window = GetWindow(); - } - - // Open the window for the player: - if (Window != NULL) - { - if (a_Player->GetWindow() != Window) - { - a_Player->OpenWindow(Window); - } - } - - // This is rather a hack - // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now - // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. - // The few false positives aren't much to worry about - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); - m_World->MarkChunkDirty(ChunkX, ChunkZ); -} - - - - - -void cChestEntity::OpenNewWindow(void) -{ - // Callback for opening together with neighbor chest: - class cOpenDouble : - public cChestCallback - { - cChestEntity * m_ThisChest; - public: - cOpenDouble(cChestEntity * a_ThisChest) : - m_ThisChest(a_ThisChest) - { - } - - virtual bool Item(cChestEntity * a_Chest) override - { - // The primary chest should eb the one with lesser X or Z coord: - cChestEntity * Primary = a_Chest; - cChestEntity * Secondary = m_ThisChest; - if ( - (Primary->GetPosX() > Secondary->GetPosX()) || - (Primary->GetPosZ() > Secondary->GetPosZ()) - ) - { - std::swap(Primary, Secondary); - } - m_ThisChest->OpenWindow(new cChestWindow(Primary, Secondary)); - return false; - } - } ; - - // Scan neighbors for adjacent chests: - cOpenDouble OpenDbl(this); - if ( - m_World->DoWithChestAt(m_PosX - 1, m_PosY, m_PosZ, OpenDbl) || - m_World->DoWithChestAt(m_PosX + 1, m_PosY, m_PosZ, OpenDbl) || - m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ - 1, OpenDbl) || - m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ + 1, OpenDbl) - ) - { - // The double-chest window has been opened in the callback - return; - } - - // There is no chest neighbor, open a single-chest window: - OpenWindow(new cChestWindow(this)); -} - - - - diff --git a/source/BlockEntities/ChestEntity.h b/source/BlockEntities/ChestEntity.h deleted file mode 100644 index 4f2c21e91..000000000 --- a/source/BlockEntities/ChestEntity.h +++ /dev/null @@ -1,59 +0,0 @@ - -#pragma once - -#include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" - - - - - -namespace Json -{ - class Value; -}; - -class cClientHandle; -class cServer; -class cNBTData; - - - - - -class cChestEntity : // tolua_export - public cBlockEntityWindowOwner, - // tolua_begin - public cBlockEntityWithItems -{ - typedef cBlockEntityWithItems super; - -public: - enum { - ContentsHeight = 3, - ContentsWidth = 9, - } ; - - // tolua_end - - /// Constructor used for normal operation - cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - - virtual ~cChestEntity(); - - static const char * GetClassStatic(void) { return "cChestEntity"; } - - bool LoadFromJson(const Json::Value & a_Value); - - // cBlockEntity overrides: - virtual void SaveToJson(Json::Value & a_Value) override; - virtual void SendTo(cClientHandle & a_Client) override; - virtual void UsedBy(cPlayer * a_Player) override; - - /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. - void OpenNewWindow(void); -} ; // tolua_export - - - - diff --git a/source/BlockEntities/DispenserEntity.cpp b/source/BlockEntities/DispenserEntity.cpp deleted file mode 100644 index 374f3d6e3..000000000 --- a/source/BlockEntities/DispenserEntity.cpp +++ /dev/null @@ -1,215 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "DispenserEntity.h" -#include "../Entities/Player.h" -#include "../Simulator/FluidSimulator.h" -#include "../Chunk.h" - - - - - -cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, a_World) -{ - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) -{ - int DispX = m_RelX; - int DispY = m_PosY; - int DispZ = m_RelZ; - NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - AddDropSpenserDir(DispX, DispY, DispZ, Meta); - cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ); - if (DispChunk == NULL) - { - // Would dispense into / interact with a non-loaded chunk, ignore the tick - return; - } - BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ); - - // Dispense the item: - switch (m_Contents.GetSlot(a_SlotNum).m_ItemType) - { - case E_ITEM_BUCKET: - { - LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); - switch (DispBlock) - { - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_WATER: - { - if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET)) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); - } - break; - } - case E_BLOCK_STATIONARY_LAVA: - case E_BLOCK_LAVA: - { - if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET)) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); - } - break; - } - default: - { - DropFromSlot(a_Chunk, a_SlotNum); - break; - } - } - break; - } // E_ITEM_BUCKET - - case E_ITEM_WATER_BUCKET: - { - LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); - if (EmptyLiquidBucket(DispBlock, a_SlotNum)) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0); - } - else - { - DropFromSlot(a_Chunk, a_SlotNum); - } - break; - } - - case E_ITEM_LAVA_BUCKET: - { - LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); - if (EmptyLiquidBucket(DispBlock, a_SlotNum)) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0); - } - else - { - DropFromSlot(a_Chunk, a_SlotNum); - } - break; - } - - case E_ITEM_SPAWN_EGG: - { - double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); - double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); - if (m_World->SpawnMob(MobX, DispY, MobZ, (cMonster::eType)m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0) - { - m_Contents.ChangeSlotCount(a_SlotNum, -1); - } - break; - } - - case E_BLOCK_TNT: - { - // Spawn a primed TNT entity, if space allows: - if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) - { - double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); - double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); - m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 4, 0); // 4 seconds fuse, no initial velocity - m_Contents.ChangeSlotCount(a_SlotNum, -1); - } - break; - } - - case E_ITEM_FLINT_AND_STEEL: - { - // Spawn fire if the block in front is air. - if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0); - m_Contents.SetSlot(a_SlotNum, m_Contents.GetSlot(a_SlotNum).m_ItemType, m_Contents.GetSlot(a_SlotNum).m_ItemCount, m_Contents.GetSlot(a_SlotNum).m_ItemDamage + 1); - // If the durability has run out destroy the item. - if (m_Contents.GetSlot(a_SlotNum).m_ItemDamage > 64) - { - m_Contents.ChangeSlotCount(a_SlotNum, -1); - } - } - break; - } - - default: - { - DropFromSlot(a_Chunk, a_SlotNum); - break; - } - } // switch (ItemType) -} - - - - - - -bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType) -{ - cItem LiquidBucket(a_BucketItemType, 1); - if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) - { - // Special case: replacing one empty bucket with one full bucket - m_Contents.SetSlot(a_SlotNum, LiquidBucket); - return true; - } - - // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else - if (m_Contents.HowManyCanFit(LiquidBucket) < 1) - { - // Cannot fit into m_Contents - return false; - } - - m_Contents.ChangeSlotCount(a_SlotNum, -1); - m_Contents.AddItem(LiquidBucket); - return true; -} - - - - - -bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum) -{ - if ( - (a_BlockInFront != E_BLOCK_AIR) && - !IsBlockLiquid(a_BlockInFront) && - !cFluidSimulator::CanWashAway(a_BlockInFront) - ) - { - // Not a suitable block in front - return false; - } - - cItem EmptyBucket(E_ITEM_BUCKET, 1); - if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) - { - // Change the single full bucket present into a single empty bucket - m_Contents.SetSlot(a_SlotNum, EmptyBucket); - return true; - } - - // There are full buckets stacked at this slot, check if we can fit in the empty bucket - if (m_Contents.HowManyCanFit(EmptyBucket) < 1) - { - // The empty bucket wouldn't fit into m_Contents - return false; - } - - // The empty bucket fits in, remove one full bucket and add the empty one - m_Contents.ChangeSlotCount(a_SlotNum, -1); - m_Contents.AddItem(EmptyBucket); - return true; -} - - - - diff --git a/source/BlockEntities/DispenserEntity.h b/source/BlockEntities/DispenserEntity.h deleted file mode 100644 index fdfe4e5b4..000000000 --- a/source/BlockEntities/DispenserEntity.h +++ /dev/null @@ -1,38 +0,0 @@ - -#pragma once - -#include "DropSpenserEntity.h" - - - - - -// tolua_begin -class cDispenserEntity : - public cDropSpenserEntity -{ - typedef cDropSpenserEntity super; - -public: - - // tolua_end - - /// Constructor used for normal operation - cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - - static const char * GetClassStatic(void) { return "cDispenserEntity"; } - -private: - // cDropSpenser overrides: - virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; - - /// If such a bucket can fit, adds it to m_Contents and returns true - bool ScoopUpLiquid(int a_SlotNum, short a_BucketItemType); - - /// If the a_BlockInFront is liquidable and the empty bucket can fit, does the m_Contents processing and returns true - bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum); -} ; // tolua_export - - - - diff --git a/source/BlockEntities/DropSpenserEntity.cpp b/source/BlockEntities/DropSpenserEntity.cpp deleted file mode 100644 index 823ed598f..000000000 --- a/source/BlockEntities/DropSpenserEntity.cpp +++ /dev/null @@ -1,266 +0,0 @@ - -// DropSpenserEntity.cpp - -// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity -// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior - -#include "Globals.h" -#include "DropSpenserEntity.h" -#include "../Entities/Player.h" -#include "../Chunk.h" - - - - - -cDropSpenserEntity::cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), - m_ShouldDropSpense(false), - m_IsPowered(false) -{ - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -cDropSpenserEntity::~cDropSpenserEntity() -{ - // Tell window its owner is destroyed - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->OwnerDestroyed(); - } -} - - - - - -void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction) -{ - switch (a_Direction) - { - case E_META_DROPSPENSER_FACING_YM: a_BlockY--; return; - case E_META_DROPSPENSER_FACING_YP: a_BlockY++; return; - case E_META_DROPSPENSER_FACING_ZM: a_BlockZ--; return; - case E_META_DROPSPENSER_FACING_ZP: a_BlockZ++; return; - case E_META_DROPSPENSER_FACING_XM: a_BlockX--; return; - case E_META_DROPSPENSER_FACING_XP: a_BlockX++; return; - } - LOGWARNING("%s: Unhandled direction: %d", __FUNCTION__, a_Direction); - return; -} - - - - - -void cDropSpenserEntity::DropSpense(cChunk & a_Chunk) -{ - // Pick one of the occupied slots: - int OccupiedSlots[9]; - int SlotsCnt = 0; - for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) - { - if (!m_Contents.GetSlot(i).IsEmpty()) - { - OccupiedSlots[SlotsCnt] = i; - SlotsCnt++; - } - } // for i - m_Contents[] - - if (SlotsCnt == 0) - { - // Nothing in the dropspenser, play the click sound - m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f); - return; - } - - int RandomSlot = m_World->GetTickRandomNumber(SlotsCnt - 1); - - // DropSpense the item, using the specialized behavior in the subclasses: - DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]); - - // Broadcast a smoke and click effects: - NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - int SmokeDir = 0; - switch (Meta) - { - case E_META_DROPSPENSER_FACING_YP: SmokeDir = 4; break; // YP & YM don't have associated smoke dirs, just do 4 (centre of block) - case E_META_DROPSPENSER_FACING_YM: SmokeDir = 4; break; - case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break; - case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break; - case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break; - case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break; - } - m_World->BroadcastSoundParticleEffect(2000, m_PosX, m_PosY, m_PosZ, SmokeDir); - m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f); - - // Update the UI window, if open: - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->BroadcastWholeWindow(); - } -} - - - - - -void cDropSpenserEntity::Activate(void) -{ - m_ShouldDropSpense = true; -} - - - - - -void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered) -{ - if (a_IsPowered && !m_IsPowered) - { - Activate(); - } - m_IsPowered = a_IsPowered; -} - - - - - -bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - if (!m_ShouldDropSpense) - { - return false; - } - - m_ShouldDropSpense = false; - DropSpense(a_Chunk); - return true; -} - - - - - -bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - Json::Value AllSlots = a_Value.get("Slots", 0); - int SlotIdx = 0; - for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) - { - cItem Contents; - Contents.FromJson(*itr); - m_Contents.SetSlot(SlotIdx, Contents); - SlotIdx++; - if (SlotIdx >= m_Contents.GetNumSlots()) - { - return true; - } - } - - return true; -} - - - - - -void cDropSpenserEntity::SaveToJson(Json::Value & a_Value) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - Json::Value AllSlots; - int NumSlots = m_Contents.GetNumSlots(); - for (int i = 0; i < NumSlots; i++) - { - Json::Value Slot; - m_Contents.GetSlot(i).GetJson(Slot); - AllSlots.append(Slot); - } - a_Value["Slots"] = AllSlots; -} - - - - - -void cDropSpenserEntity::SendTo(cClientHandle & a_Client) -{ - // Nothing needs to be sent - UNUSED(a_Client); -} - - - - - -void cDropSpenserEntity::UsedBy(cPlayer * a_Player) -{ - cWindow * Window = GetWindow(); - if (Window == NULL) - { - OpenWindow(new cDropSpenserWindow(m_PosX, m_PosY, m_PosZ, this)); - Window = GetWindow(); - } - - if (Window != NULL) - { - if (a_Player->GetWindow() != Window) - { - a_Player->OpenWindow(Window); - } - } -} - - - - - -void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum) -{ - int DispX = m_PosX; - int DispY = m_PosY; - int DispZ = m_PosZ; - NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - AddDropSpenserDir(DispX, DispY, DispZ, Meta); - - cItems Pickups; - Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum)); - - const int PickupSpeed = m_World->GetTickRandomNumber(4) + 2; // At least 2, at most 6 - int PickupSpeedX = 0, PickupSpeedY = 0, PickupSpeedZ = 0; - switch (Meta) - { - case E_META_DROPSPENSER_FACING_YP: PickupSpeedY = PickupSpeed; break; - case E_META_DROPSPENSER_FACING_YM: PickupSpeedY = -PickupSpeed; break; - case E_META_DROPSPENSER_FACING_XM: PickupSpeedX = -PickupSpeed; break; - case E_META_DROPSPENSER_FACING_XP: PickupSpeedX = PickupSpeed; break; - case E_META_DROPSPENSER_FACING_ZM: PickupSpeedZ = -PickupSpeed; break; - case E_META_DROPSPENSER_FACING_ZP: PickupSpeedZ = PickupSpeed; break; - } - - double MicroX, MicroY, MicroZ; - MicroX = DispX + 0.5; - MicroY = DispY + 0.4; // Slightly less than half, to accomodate actual texture hole on DropSpenser - MicroZ = DispZ + 0.5; - - - m_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ, PickupSpeedX, PickupSpeedY, PickupSpeedZ); -} - - - - diff --git a/source/BlockEntities/DropSpenserEntity.h b/source/BlockEntities/DropSpenserEntity.h deleted file mode 100644 index 0e9039915..000000000 --- a/source/BlockEntities/DropSpenserEntity.h +++ /dev/null @@ -1,89 +0,0 @@ - -// DropSpenser.h - -// Declares the cDropSpenser class representing a common ancestor to the cDispenserEntity and cDropperEntity -// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior - - - - - -#pragma once - -#include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" - - - - - -namespace Json -{ - class Value; -} - -class cClientHandle; -class cServer; - - - - - -class cDropSpenserEntity : // tolua_export - public cBlockEntityWindowOwner, - // tolua_begin - public cBlockEntityWithItems -{ - typedef cBlockEntityWithItems super; - -public: - enum { - ContentsHeight = 3, - ContentsWidth = 3, - } ; - - // tolua_end - - cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - virtual ~cDropSpenserEntity(); - - static const char * GetClassStatic(void) { return "cDropSpenserEntity"; } - - bool LoadFromJson(const Json::Value & a_Value); - - // cBlockEntity overrides: - virtual void SaveToJson(Json::Value & a_Value) override; - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void SendTo(cClientHandle & a_Client) override; - virtual void UsedBy(cPlayer * a_Player) override; - - // tolua_begin - - /// Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups should materialize) - void AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction); - - /// Sets the dropspenser to dropspense an item in the next tick - void Activate(void); - - /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate - void SetRedstonePower(bool a_IsPowered); - - // tolua_end - -protected: - bool m_ShouldDropSpense; ///< If true, the dropspenser will dropspense an item in the next tick - bool m_IsPowered; ///< Set to true when the dropspenser receives redstone power. - - /// Does the actual work on dropspensing an item. Chooses the slot, calls DropSpenseFromSlot() and handles smoke / sound effects - void DropSpense(cChunk & a_Chunk); - - /// Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...) - virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) = 0; - - /// Helper function, drops one item from the specified slot (like a dropper) - void DropFromSlot(cChunk & a_Chunk, int a_SlotNum); -} ; // tolua_export - - - - diff --git a/source/BlockEntities/DropperEntity.cpp b/source/BlockEntities/DropperEntity.cpp deleted file mode 100644 index 5d4a8ad97..000000000 --- a/source/BlockEntities/DropperEntity.cpp +++ /dev/null @@ -1,32 +0,0 @@ - -// DropperEntity.cpp - -// Implements the cRtopperEntity class representing a Dropper block entity - -#include "Globals.h" -#include "DropperEntity.h" -#include "../Entities/Player.h" -#include "../Simulator/FluidSimulator.h" - - - - - -cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, a_World) -{ - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -void cDropperEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) -{ - DropFromSlot(a_Chunk, a_SlotNum); -} - - - - diff --git a/source/BlockEntities/DropperEntity.h b/source/BlockEntities/DropperEntity.h deleted file mode 100644 index 8e07bc6f8..000000000 --- a/source/BlockEntities/DropperEntity.h +++ /dev/null @@ -1,46 +0,0 @@ - -// DropperEntity.h - -// Declares the cDropperEntity class representing a dropper block entity - - - - - -#pragma once - -#include "DropSpenserEntity.h" - - - - - -// tolua_begin -class cDropperEntity : - public cDropSpenserEntity -{ - typedef cDropSpenserEntity super; - -public: - - // tolua_end - - /// Constructor used for normal operation - cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - - static const char * GetClassStatic(void) { return "cDropperEntity"; } - -protected: - // cDropSpenserEntity overrides: - virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; - - /** Takes an item from slot a_SlotNum and puts it into the container in front of the dropper. - Called when there's a container directly in front of the dropper, - so the dropper should store items there, rather than dropping. - */ - void PutIntoContainer(cChunk & a_Chunk, int a_SlotNum, BLOCKTYPE a_ContainerBlock, int a_ContainerX, int a_ContainerY, int a_ContainerZ); -} ; // tolua_export - - - - diff --git a/source/BlockEntities/FurnaceEntity.cpp b/source/BlockEntities/FurnaceEntity.cpp deleted file mode 100644 index ec5ebe8b9..000000000 --- a/source/BlockEntities/FurnaceEntity.cpp +++ /dev/null @@ -1,479 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "FurnaceEntity.h" -#include "../UI/Window.h" -#include "../Entities/Player.h" -#include "../Root.h" -#include "../Chunk.h" -#include - - - - - - -enum -{ - PROGRESSBAR_SMELTING = 0, - PROGRESSBAR_FUEL = 1, -} ; - - - - - -cFurnaceEntity::cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World) : - super(E_BLOCK_FURNACE, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), - m_BlockType(a_BlockType), - m_BlockMeta(a_BlockMeta), - m_CurrentRecipe(NULL), - m_IsCooking((a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_LIT_FURNACE)), - m_NeedCookTime(0), - m_TimeCooked(0), - m_FuelBurnTime(0), - m_TimeBurned(0), - m_LastProgressFuel(0), - m_LastProgressCook(0) -{ - cBlockEntityWindowOwner::SetBlockEntity(this); - m_Contents.AddListener(*this); -} - - - - - -cFurnaceEntity::~cFurnaceEntity() -{ - // Tell window its owner is destroyed - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->OwnerDestroyed(); - } -} - - - - - -void cFurnaceEntity::UsedBy(cPlayer * a_Player) -{ - if (GetWindow() == NULL) - { - OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this)); - } - cWindow * Window = GetWindow(); - if (Window != NULL) - { - if (a_Player->GetWindow() != Window) - { - a_Player->OpenWindow(Window); - BroadcastProgress(PROGRESSBAR_FUEL, m_LastProgressFuel); - BroadcastProgress(PROGRESSBAR_SMELTING, m_LastProgressCook); - } - } -} - - - - - -/// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking. -bool cFurnaceEntity::ContinueCooking(void) -{ - UpdateInput(); - UpdateFuel(); - return m_IsCooking; -} - - - - - -bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - if (m_FuelBurnTime <= 0) - { - // No fuel is burning, reset progressbars and bail out - if ((m_LastProgressCook > 0) || (m_LastProgressFuel > 0)) - { - UpdateProgressBars(); - } - return false; - } - - if (m_IsCooking) - { - m_TimeCooked++; - if (m_TimeCooked >= m_NeedCookTime) - { - // Finished smelting one item - FinishOne(a_Chunk); - } - } - - m_TimeBurned++; - if (m_TimeBurned >= m_FuelBurnTime) - { - // The current fuel has been exhausted, use another one, if possible - BurnNewFuel(); - } - - UpdateProgressBars(); - - return true; -} - - - - - -bool cFurnaceEntity::LoadFromJson(const Json::Value & a_Value) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - Json::Value AllSlots = a_Value.get("Slots", 0); - int SlotIdx = 0; - for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) - { - cItem Item; - Item.FromJson(*itr); - SetSlot(SlotIdx, Item); - SlotIdx++; - } - - m_NeedCookTime = (int)(a_Value.get("CookTime", 0).asDouble() / 50); - m_TimeCooked = (int)(a_Value.get("TimeCooked", 0).asDouble() / 50); - m_FuelBurnTime = (int)(a_Value.get("BurnTime", 0).asDouble() / 50); - m_TimeBurned = (int)(a_Value.get("TimeBurned", 0).asDouble() / 50); - - return true; -} - - - - - -void cFurnaceEntity::SaveToJson( Json::Value& a_Value ) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - Json::Value AllSlots; - int NumSlots = m_Contents.GetNumSlots(); - for (int i = 0; i < NumSlots; i++) - { - Json::Value Slot; - m_Contents.GetSlot(i).GetJson(Slot); - AllSlots.append(Slot); - } - a_Value["Slots"] = AllSlots; - - a_Value["CookTime"] = m_NeedCookTime * 50; - a_Value["TimeCooked"] = m_TimeCooked * 50; - a_Value["BurnTime"] = m_FuelBurnTime * 50; - a_Value["TimeBurned"] = m_TimeBurned * 50; -} - - - - - -void cFurnaceEntity::SendTo(cClientHandle & a_Client) -{ - // Nothing needs to be sent - UNUSED(a_Client); -} - - - - - -void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value) -{ - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->BroadcastProgress(a_ProgressbarID, a_Value); - } -} - - - - - -/// One item finished cooking -void cFurnaceEntity::FinishOne(cChunk & a_Chunk) -{ - m_TimeCooked = 0; - - if (m_Contents.GetSlot(fsOutput).IsEmpty()) - { - m_Contents.SetSlot(fsOutput, *m_CurrentRecipe->Out); - } - else - { - m_Contents.ChangeSlotCount(fsOutput, m_CurrentRecipe->Out->m_ItemCount); - } - m_Contents.ChangeSlotCount(fsInput, -m_CurrentRecipe->In->m_ItemCount); - - UpdateIsCooking(); -} - - - - - -void cFurnaceEntity::BurnNewFuel(void) -{ - cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); - int NewTime = FR->GetBurnTime(m_Contents.GetSlot(fsFuel)); - if (NewTime == 0) - { - // The item in the fuel slot is not suitable - m_FuelBurnTime = 0; - m_TimeBurned = 0; - SetIsCooking(false); - return; - } - - // Is the input and output ready for cooking? - if (!CanCookInputToOutput()) - { - return; - } - - // Burn one new fuel: - m_FuelBurnTime = NewTime; - m_TimeBurned = 0; - SetIsCooking(true); - if (m_Contents.GetSlot(fsFuel).m_ItemType == E_ITEM_LAVA_BUCKET) - { - m_Contents.SetSlot(fsFuel, cItem(E_ITEM_BUCKET)); - } - else - { - m_Contents.ChangeSlotCount(fsFuel, -1); - } -} - - - - - -void cFurnaceEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) -{ - super::OnSlotChanged(a_ItemGrid, a_SlotNum); - - if (m_World == NULL) - { - // The furnace isn't initialized yet, do no processing - return; - } - - ASSERT(a_ItemGrid == &m_Contents); - switch (a_SlotNum) - { - case fsInput: - { - UpdateInput(); - break; - } - - case fsFuel: - { - UpdateFuel(); - break; - } - - case fsOutput: - { - UpdateOutput(); - break; - } - } -} - - - - - - -/// Updates the current recipe, based on the current input -void cFurnaceEntity::UpdateInput(void) -{ - if (!m_Contents.GetSlot(fsInput).IsStackableWith(m_LastInput)) - { - // The input is different from what we had before, reset the cooking time - m_TimeCooked = 0; - } - m_LastInput = m_Contents.GetSlot(fsInput); - - cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); - m_CurrentRecipe = FR->GetRecipeFrom(m_Contents.GetSlot(fsInput)); - if (!CanCookInputToOutput()) - { - // This input cannot be cooked - m_NeedCookTime = 0; - SetIsCooking(false); - } - else - { - m_NeedCookTime = m_CurrentRecipe->CookTime; - SetIsCooking(true); - - // Start burning new fuel if there's no flame now: - if (GetFuelBurnTimeLeft() <= 0) - { - BurnNewFuel(); - } - } -} - - - - - -/// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate -void cFurnaceEntity::UpdateFuel(void) -{ - if (m_FuelBurnTime > m_TimeBurned) - { - // The current fuel is still burning, don't modify anything: - return; - } - - // The current fuel is spent, try to burn some more: - BurnNewFuel(); -} - - - - - -/// Called when the output slot changes; starts burning if space became available -void cFurnaceEntity::UpdateOutput(void) -{ - if (!CanCookInputToOutput()) - { - // Cannot cook anymore: - m_TimeCooked = 0; - m_NeedCookTime = 0; - SetIsCooking(false); - return; - } - - // No need to burn new fuel, the Tick() function will take care of that - - // Can cook, start cooking if not already underway: - m_NeedCookTime = m_CurrentRecipe->CookTime; - SetIsCooking(m_FuelBurnTime > 0); -} - - - - - -/// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned -void cFurnaceEntity::UpdateIsCooking(void) -{ - if ( - !CanCookInputToOutput() || // Cannot cook this - (m_FuelBurnTime <= 0) || // No fuel - (m_TimeBurned >= m_FuelBurnTime) // Fuel burnt out - ) - { - // Reset everything - SetIsCooking(false); - m_TimeCooked = 0; - m_NeedCookTime = 0; - return; - } - - SetIsCooking(true); -} - - - - - -/// Returns true if the input can be cooked into output and the item counts allow for another cooking operation -bool cFurnaceEntity::CanCookInputToOutput(void) const -{ - if (m_CurrentRecipe == NULL) - { - // This input cannot be cooked - return false; - } - - if (m_Contents.GetSlot(fsOutput).IsEmpty()) - { - // The output is empty, can cook - return true; - } - - if (!m_Contents.GetSlot(fsOutput).IsStackableWith(*m_CurrentRecipe->Out)) - { - // The output slot is blocked with something that cannot be stacked with the recipe's output - return false; - } - - if (m_Contents.GetSlot(fsOutput).IsFullStack()) - { - // Cannot add any more items to the output slot - return false; - } - - return true; -} - - - - - -/// Broadcasts progressbar updates, if needed -void cFurnaceEntity::UpdateProgressBars(void) -{ - // In order to preserve bandwidth, an update is sent only every 10th tick - // That's why the comparisons use the division by eight - - int CurFuel = (m_FuelBurnTime > 0) ? (200 - 200 * m_TimeBurned / m_FuelBurnTime) : 0; - if ((CurFuel / 8) != (m_LastProgressFuel / 8)) - { - BroadcastProgress(PROGRESSBAR_FUEL, CurFuel); - m_LastProgressFuel = CurFuel; - } - - int CurCook = (m_NeedCookTime > 0) ? (200 * m_TimeCooked / m_NeedCookTime) : 0; - if ((CurCook / 8) != (m_LastProgressCook / 8)) - { - BroadcastProgress(PROGRESSBAR_SMELTING, CurCook); - m_LastProgressCook = CurCook; - } -} - - - - - -void cFurnaceEntity::SetIsCooking(bool a_IsCooking) -{ - if (a_IsCooking == m_IsCooking) - { - return; - } - - m_IsCooking = a_IsCooking; - - // Light or extinguish the furnace: - m_World->FastSetBlock(m_PosX, m_PosY, m_PosZ, m_IsCooking ? E_BLOCK_LIT_FURNACE : E_BLOCK_FURNACE, m_BlockMeta); -} - - - - diff --git a/source/BlockEntities/FurnaceEntity.h b/source/BlockEntities/FurnaceEntity.h deleted file mode 100644 index 9464fd175..000000000 --- a/source/BlockEntities/FurnaceEntity.h +++ /dev/null @@ -1,164 +0,0 @@ - -#pragma once - -#include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" -#include "../FurnaceRecipe.h" - - - - - -namespace Json -{ - class Value; -} - -class cClientHandle; -class cServer; - - - - - -class cFurnaceEntity : // tolua_export - public cBlockEntityWindowOwner, - // tolua_begin - public cBlockEntityWithItems -{ - typedef cBlockEntityWithItems super; - -public: - enum - { - fsInput = 0, // Input slot number - fsFuel = 1, // Fuel slot number - fsOutput = 2, // Output slot number - - ContentsWidth = 3, - ContentsHeight = 1, - }; - - // tolua_end - - /// Constructor used for normal operation - cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World); - - virtual ~cFurnaceEntity(); - - static const char * GetClassStatic() { return "cFurnaceEntity"; } - - bool LoadFromJson(const Json::Value & a_Value); - - // cBlockEntity overrides: - virtual void SaveToJson(Json::Value & a_Value) override; - virtual void SendTo(cClientHandle & a_Client) override; - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void UsedBy(cPlayer * a_Player) override; - - /// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking. - bool ContinueCooking(void); - - void ResetCookTimer(); - - // tolua_begin - - /// Returns the item in the input slot - const cItem & GetInputSlot(void) const { return GetSlot(fsInput); } - - /// Returns the item in the fuel slot - const cItem & GetFuelSlot(void) const { return GetSlot(fsFuel); } - - /// Returns the item in the output slot - const cItem & GetOutputSlot(void) const { return GetSlot(fsOutput); } - - /// Sets the item in the input slot - void SetInputSlot(const cItem & a_Item) { SetSlot(fsInput, a_Item); } - - /// Sets the item in the fuel slot - void SetFuelSlot(const cItem & a_Item) { SetSlot(fsFuel, a_Item); } - - /// Sets the item in the output slot - void SetOutputSlot(const cItem & a_Item) { SetSlot(fsOutput, a_Item); } - - /// Returns the time that the current item has been cooking, in ticks - int GetTimeCooked(void) const {return m_TimeCooked; } - - /// Returns the time until the current item finishes cooking, in ticks - int GetCookTimeLeft(void) const { return m_NeedCookTime - m_TimeCooked; } - - /// Returns the time until the current fuel is depleted, in ticks - int GetFuelBurnTimeLeft(void) const {return m_FuelBurnTime - m_TimeBurned; } - - /// Returns true if there's time left before the current fuel is depleted - bool HasFuelTimeLeft(void) const { return (GetFuelBurnTimeLeft() > 0); } - - // tolua_end - - void SetBurnTimes(int a_FuelBurnTime, int a_TimeBurned) {m_FuelBurnTime = a_FuelBurnTime; m_TimeBurned = 0; } - void SetCookTimes(int a_NeedCookTime, int a_TimeCooked) {m_NeedCookTime = a_NeedCookTime; m_TimeCooked = a_TimeCooked; } - -protected: - - /// Block type of the block currently represented by this entity (changes when furnace lights up) - BLOCKTYPE m_BlockType; - - /// Block meta of the block currently represented by this entity - NIBBLETYPE m_BlockMeta; - - /// The recipe for the current input slot - const cFurnaceRecipe::Recipe * m_CurrentRecipe; - - /// The item that is being smelted - cItem m_LastInput; - - bool m_IsCooking; ///< Set to true if the furnace is cooking an item - - // All timers are in ticks - int m_NeedCookTime; ///< Amount of time needed to fully cook current item - int m_TimeCooked; ///< Amount of time that the current item has been cooking - int m_FuelBurnTime; ///< Amount of time that the current fuel can burn (in total); zero if no fuel burning - int m_TimeBurned; ///< Amount of time that the current fuel has been burning - - int m_LastProgressFuel; ///< Last value sent as the progress for the fuel - int m_LastProgressCook; ///< Last value sent as the progress for the cooking - - - /// Sends the specified progressbar value to all clients of the window - void BroadcastProgress(int a_ProgressbarID, short a_Value); - - /// One item finished cooking - void FinishOne(cChunk & a_Chunk); - - /// Starts burning a new fuel, if possible - void BurnNewFuel(void); - - /// Updates the recipe, based on the current input - void UpdateInput(void); - - /// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate - void UpdateFuel(void); - - /// Called when the output slot changes - void UpdateOutput(void); - - /// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned - void UpdateIsCooking(void); - - /// Returns true if the input can be cooked into output and the item counts allow for another cooking operation - bool CanCookInputToOutput(void) const; - - /// Broadcasts progressbar updates, if needed - void UpdateProgressBars(void); - - /// Sets the m_IsCooking variable, updates the furnace block type based on the value - void SetIsCooking(bool a_IsCooking); - - // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; - -} ; // tolua_export - - - - diff --git a/source/BlockEntities/HopperEntity.cpp b/source/BlockEntities/HopperEntity.cpp deleted file mode 100644 index 41849b1b3..000000000 --- a/source/BlockEntities/HopperEntity.cpp +++ /dev/null @@ -1,566 +0,0 @@ - -// HopperEntity.cpp - -// Implements the cHopperEntity representing a hopper block entity - -#include "Globals.h" -#include "HopperEntity.h" -#include "../Chunk.h" -#include "../Entities/Player.h" -#include "../PluginManager.h" -#include "ChestEntity.h" -#include "DropSpenserEntity.h" -#include "FurnaceEntity.h" - - - - - -cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), - m_LastMoveItemsInTick(0), - m_LastMoveItemsOutTick(0) -{ -} - - - - - -/** Returns the block coords of the block receiving the output items, based on the meta -Returns false if unattached -*/ -bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ) -{ - a_OutputX = m_PosX; - a_OutputY = m_PosY; - a_OutputZ = m_PosZ; - switch (a_BlockMeta) - { - case E_META_HOPPER_FACING_XM: a_OutputX--; return true; - case E_META_HOPPER_FACING_XP: a_OutputX++; return true; - case E_META_HOPPER_FACING_YM: a_OutputY--; return true; - case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true; - case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true; - default: - { - // Not attached - return false; - } - } -} - - - - - -bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); - - bool res = false; - res = MoveItemsIn (a_Chunk, CurrentTick) || res; - res = MovePickupsIn(a_Chunk, CurrentTick) || res; - res = MoveItemsOut (a_Chunk, CurrentTick) || res; - return res; -} - - - - - -void cHopperEntity::SaveToJson(Json::Value & a_Value) -{ - // TODO - LOGWARNING("%s: Not implemented yet", __FUNCTION__); -} - - - - - -void cHopperEntity::SendTo(cClientHandle & a_Client) -{ - // The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance - // All the actual handling is in the cWindow UI code that gets called when the hopper is rclked - - UNUSED(a_Client); -} - - - - - -void cHopperEntity::UsedBy(cPlayer * a_Player) -{ - // If the window is not created, open it anew: - cWindow * Window = GetWindow(); - if (Window == NULL) - { - OpenNewWindow(); - Window = GetWindow(); - } - - // Open the window for the player: - if (Window != NULL) - { - if (a_Player->GetWindow() != Window) - { - a_Player->OpenWindow(Window); - } - } - - // This is rather a hack - // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now - // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. - // The few false positives aren't much to worry about - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); - m_World->MarkChunkDirty(ChunkX, ChunkZ); -} - - - - - -/// Opens a new window UI for this hopper -void cHopperEntity::OpenNewWindow(void) -{ - OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this)); -} - - - - - -/// Moves items from the container above it into this hopper. Returns true if the contents have changed. -bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) -{ - if (m_PosY >= cChunkDef::Height) - { - // This hopper is at the top of the world, no more blocks above - return false; - } - - if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER) - { - // Too early after the previous transfer - return false; - } - - // Try moving an item in: - bool res = false; - switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ)) - { - case E_BLOCK_CHEST: - { - // Chests have special handling because of double-chests - res = MoveItemsFromChest(a_Chunk); - break; - } - case E_BLOCK_LIT_FURNACE: - case E_BLOCK_FURNACE: - { - // Furnaces have special handling because only the output and leftover fuel buckets shall be moved - res = MoveItemsFromFurnace(a_Chunk); - break; - } - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - case E_BLOCK_HOPPER: - { - res = MoveItemsFromGrid(*(cBlockEntityWithItems *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ)); - break; - } - } - - // If the item has been moved, reset the last tick: - if (res) - { - m_LastMoveItemsInTick = a_CurrentTick; - } - - return res; -} - - - - - -/// Moves pickups from above this hopper into it. Returns true if the contents have changed. -bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) -{ - // TODO - return false; -} - - - - - -/// Moves items out from this hopper into the destination. Returns true if the contents have changed. -bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) -{ - if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER) - { - // Too early after the previous transfer - return false; - } - - int bx, by, bz; - NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - if (!GetOutputBlockPos(Meta, bx, by, bz)) - { - // Not attached to another container - return false; - } - if (by < 0) - { - // Cannot output below the zero-th block level - return false; - } - - // Convert coords to relative: - int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width; - int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz); - if (DestChunk == NULL) - { - // The destination chunk has been unloaded, don't tick - return false; - } - - // Call proper moving function, based on the blocktype present at the coords: - bool res = false; - switch (DestChunk->GetBlock(rx, by, rz)) - { - case E_BLOCK_CHEST: - { - // Chests have special handling because of double-chests - res = MoveItemsToChest(*DestChunk, bx, by, bz); - break; - } - case E_BLOCK_LIT_FURNACE: - case E_BLOCK_FURNACE: - { - // Furnaces have special handling because of the direction-to-slot relation - res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); - break; - } - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - case E_BLOCK_HOPPER: - { - res = MoveItemsToGrid(*(cBlockEntityWithItems *)DestChunk->GetBlockEntity(bx, by, bz)); - break; - } - } - - // If the item has been moved, reset the last tick: - if (res) - { - m_LastMoveItemsOutTick = a_CurrentTick; - } - - return res; -} - - - - - -/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. -bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) -{ - if (MoveItemsFromGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))) - { - // Moved the item from the chest directly above the hopper - return true; - } - - // Check if the chest is a double-chest, if so, try to move from there: - static const struct - { - int x, z; - } - Coords [] = - { - {1, 0}, - {-1, 0}, - {0, 1}, - {0, -1}, - } ; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - int x = m_RelX + Coords[i].x; - int z = m_RelZ + Coords[i].z; - cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); - if ( - (Neighbor == NULL) || - (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) - ) - { - continue; - } - if (MoveItemsFromGrid(*(cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))) - { - return true; - } - return false; - } - - // The chest was single and nothing could be moved - return false; -} - - - - - -/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. -bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) -{ - cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); - ASSERT(Furnace != NULL); - - // Try move from the output slot: - if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true)) - { - cItem NewOutput(Furnace->GetOutputSlot()); - Furnace->SetOutputSlot(NewOutput.AddCount(-1)); - return true; - } - - // No output moved, check if we can move an empty bucket out of the fuel slot: - if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET) - { - if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel, true)) - { - Furnace->SetFuelSlot(cItem()); - return true; - } - } - - // Nothing can be moved - return false; -} - - - - - -bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity) -{ - cItemGrid & Grid = a_Entity.GetContents(); - int NumSlots = Grid.GetNumSlots(); - - // First try adding items of types already in the hopper: - for (int i = 0; i < NumSlots; i++) - { - if (Grid.IsSlotEmpty(i)) - { - continue; - } - if (MoveItemsFromSlot(a_Entity, i, false)) - { - Grid.ChangeSlotCount(i, -1); - return true; - } - } - - // No already existing stack can be topped up, try again with allowing new stacks: - for (int i = 0; i < NumSlots; i++) - { - if (Grid.IsSlotEmpty(i)) - { - continue; - } - if (MoveItemsFromSlot(a_Entity, i, true)) - { - Grid.ChangeSlotCount(i, -1); - return true; - } - } - return false; -} - - - - - -/// Moves one piece of the specified a_Entity's slot itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. -bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum, bool a_AllowNewStacks) -{ - cItem One(a_Entity.GetSlot(a_SlotNum).CopyOne()); - for (int i = 0; i < ContentsWidth * ContentsHeight; i++) - { - if (m_Contents.IsSlotEmpty(i)) - { - if (a_AllowNewStacks) - { - if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) - { - // Plugin disagrees with the move - continue; - } - } - m_Contents.SetSlot(i, One); - return true; - } - else if (m_Contents.GetSlot(i).IsStackableWith(One)) - { - if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) - { - // Plugin disagrees with the move - continue; - } - - m_Contents.ChangeSlotCount(i, 1); - return true; - } - } - return false; -} - - - - - -/// Moves items to the chest at the specified coords. Returns true if contents have changed -bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Try the chest directly connected to the hopper: - if (MoveItemsToGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) - { - return true; - } - - // Check if the chest is a double-chest, if so, try to move into the other half: - static const struct - { - int x, z; - } - Coords [] = - { - {1, 0}, - {-1, 0}, - {0, 1}, - {0, -1}, - } ; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - int x = m_RelX + Coords[i].x; - int z = m_RelZ + Coords[i].z; - cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); - if ( - (Neighbor == NULL) || - (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) - ) - { - continue; - } - if (MoveItemsToGrid(*(cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) - { - return true; - } - return false; - } - - // The chest was single and nothing could be moved - return false; -} - - - - - -/// Moves items to the furnace at the specified coords. Returns true if contents have changed -bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta) -{ - cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); - if (a_HopperMeta == E_META_HOPPER_FACING_YM) - { - // Feed the input slot of the furnace - return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsInput); - } - else - { - // Feed the fuel slot of the furnace - return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsFuel); - } - return false; -} - - - - - -bool cHopperEntity::MoveItemsToGrid(cBlockEntityWithItems & a_Entity) -{ - // Iterate through our slots, try to move from each one: - int NumSlots = a_Entity.GetContents().GetNumSlots(); - for (int i = 0; i < NumSlots; i++) - { - if (MoveItemsToSlot(a_Entity, i)) - { - return true; - } - } - return false; -} - - - - - -bool cHopperEntity::MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum) -{ - cItemGrid & Grid = a_Entity.GetContents(); - if (Grid.IsSlotEmpty(a_DstSlotNum)) - { - // The slot is empty, move the first non-empty slot from our contents: - for (int i = 0; i < ContentsWidth * ContentsHeight; i++) - { - if (!m_Contents.IsSlotEmpty(i)) - { - if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum)) - { - // A plugin disagrees with the move - continue; - } - Grid.SetSlot(a_DstSlotNum, m_Contents.GetSlot(i).CopyOne()); - m_Contents.ChangeSlotCount(i, -1); - return true; - } - } - return false; - } - else - { - // The slot is taken, try to top it up: - const cItem & DestSlot = Grid.GetSlot(a_DstSlotNum); - if (DestSlot.IsFullStack()) - { - return false; - } - for (int i = 0; i < ContentsWidth * ContentsHeight; i++) - { - if (m_Contents.GetSlot(i).IsStackableWith(DestSlot)) - { - if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum)) - { - // A plugin disagrees with the move - continue; - } - Grid.ChangeSlotCount(a_DstSlotNum, 1); - m_Contents.ChangeSlotCount(i, -1); - return true; - } - } - return false; - } -} - - - - diff --git a/source/BlockEntities/HopperEntity.h b/source/BlockEntities/HopperEntity.h deleted file mode 100644 index 3eaa05b7c..000000000 --- a/source/BlockEntities/HopperEntity.h +++ /dev/null @@ -1,96 +0,0 @@ - -// HopperEntity.h - -// Declares the cHopperEntity representing a hopper block entity - - - - - -#pragma once - -#include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" - - - - - -class cHopperEntity : // tolua_export - public cBlockEntityWindowOwner, - // tolua_begin - public cBlockEntityWithItems -{ - typedef cBlockEntityWithItems super; - -public: - enum { - ContentsHeight = 1, - ContentsWidth = 5, - TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper - } ; - - // tolua_end - - /// Constructor used for normal operation - cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - - /** Returns the block coords of the block receiving the output items, based on the meta - Returns false if unattached. - Exported in ManualBindings.cpp - */ - bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ); - - static const char * GetClassStatic(void) { return "cHopperEntity"; } - -protected: - - Int64 m_LastMoveItemsInTick; - Int64 m_LastMoveItemsOutTick; - - // cBlockEntity overrides: - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void SaveToJson(Json::Value & a_Value) override; - virtual void SendTo(cClientHandle & a_Client) override; - virtual void UsedBy(cPlayer * a_Player) override; - - /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. - void OpenNewWindow(void); - - /// Moves items from the container above it into this hopper. Returns true if the contents have changed. - bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick); - - /// Moves pickups from above this hopper into it. Returns true if the contents have changed. - bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick); - - /// Moves items out from this hopper into the destination. Returns true if the contents have changed. - bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick); - - /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. - bool MoveItemsFromChest(cChunk & a_Chunk); - - /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. - bool MoveItemsFromFurnace(cChunk & a_Chunk); - - /// Moves items from the specified a_Entity's Contents into this hopper. Returns true if contents have changed. - bool MoveItemsFromGrid(cBlockEntityWithItems & a_Entity); - - /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. - bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum, bool a_AllowNewStacks); - - /// Moves items to the chest at the specified coords. Returns true if contents have changed - bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Moves items to the furnace at the specified coords. Returns true if contents have changed - bool MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta); - - /// Moves items to the specified ItemGrid. Returns true if contents have changed - bool MoveItemsToGrid(cBlockEntityWithItems & a_Entity); - - /// Moves one piece to the specified entity's contents' slot. Returns true if contents have changed. - bool MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum); -} ; // tolua_export - - - - diff --git a/source/BlockEntities/JukeboxEntity.cpp b/source/BlockEntities/JukeboxEntity.cpp deleted file mode 100644 index aca376dd3..000000000 --- a/source/BlockEntities/JukeboxEntity.cpp +++ /dev/null @@ -1,125 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "JukeboxEntity.h" -#include "../World.h" -#include - - - - - -cJukeboxEntity::cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World), - m_Record(0) -{ -} - - - - - -cJukeboxEntity::~cJukeboxEntity() -{ - EjectRecord(); -} - - - - - -void cJukeboxEntity::UsedBy(cPlayer * a_Player) -{ - if (m_Record == 0) - { - const cItem & HeldItem = a_Player->GetEquippedItem(); - if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267) - { - m_Record = HeldItem.m_ItemType; - a_Player->GetInventory().RemoveOneEquippedItem(); - PlayRecord(); - } - } - else - { - EjectRecord(); - } -} - - - - - -void cJukeboxEntity::PlayRecord(void) -{ - m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, m_Record); -} - - - - - -void cJukeboxEntity::EjectRecord(void) -{ - if ((m_Record < E_ITEM_FIRST_DISC) || (m_Record > E_ITEM_LAST_DISC)) - { - // There's no record here - return; - } - - cItems Drops; - Drops.push_back(cItem(m_Record, 1, 0)); - m_World->SpawnItemPickups(Drops, m_PosX + 0.5, m_PosY + 1, m_PosZ + 0.5, 8); - m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, 0); - m_Record = 0; -} - - - - - -int cJukeboxEntity::GetRecord(void) -{ - return m_Record; -} - - - - - -void cJukeboxEntity::SetRecord(int a_Record) -{ - m_Record = a_Record; -} - - - - - -bool cJukeboxEntity::LoadFromJson(const Json::Value & a_Value) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - m_Record = a_Value.get("Record", 0).asInt(); - - return true; -} - - - - - -void cJukeboxEntity::SaveToJson(Json::Value & a_Value) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - a_Value["Record"] = m_Record; -} - - - - diff --git a/source/BlockEntities/JukeboxEntity.h b/source/BlockEntities/JukeboxEntity.h deleted file mode 100644 index fcafdc479..000000000 --- a/source/BlockEntities/JukeboxEntity.h +++ /dev/null @@ -1,56 +0,0 @@ - -#pragma once - -#include "BlockEntity.h" -#include "../Entities/Player.h" - - - - - -namespace Json -{ - class Value; -} - - - - - -// tolua_begin - -class cJukeboxEntity : - public cBlockEntity -{ - typedef cBlockEntity super; -public: - - // tolua_end - - cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - virtual ~cJukeboxEntity(); - - bool LoadFromJson(const Json::Value & a_Value); - virtual void SaveToJson(Json::Value & a_Value) override; - - // tolua_begin - - int GetRecord(void); - void SetRecord(int a_Record); - void PlayRecord(void); - - /// Ejects the currently held record as a pickup. Does nothing when no record inserted. - void EjectRecord(void); - - // tolua_end - - virtual void UsedBy(cPlayer * a_Player) override; - virtual void SendTo(cClientHandle & a_Client) override { }; - -private: - int m_Record; -} ; // tolua_end - - - - diff --git a/source/BlockEntities/NoteEntity.cpp b/source/BlockEntities/NoteEntity.cpp deleted file mode 100644 index 1b0620299..000000000 --- a/source/BlockEntities/NoteEntity.cpp +++ /dev/null @@ -1,154 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "NoteEntity.h" -#include "../World.h" -#include - - - - - -cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World), - m_Pitch(0) -{ -} - - - - - -void cNoteEntity::UsedBy(cPlayer * a_Player) -{ - IncrementPitch(); - MakeSound(); -} - - - - - -void cNoteEntity::MakeSound(void) -{ - char instrument; - AString sampleName; - - switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ)) - { - case E_BLOCK_PLANKS: - case E_BLOCK_LOG: - case E_BLOCK_NOTE_BLOCK: - { - // TODO: add other wood-based blocks if needed - instrument = E_INST_DOUBLE_BASS; - sampleName = "note.db"; - break; - } - - case E_BLOCK_SAND: - case E_BLOCK_GRAVEL: - case E_BLOCK_SOULSAND: - { - instrument = E_INST_SNARE_DRUM; - sampleName = "note.snare"; - break; - } - - case E_BLOCK_GLASS: - case E_BLOCK_GLASS_PANE: - case E_BLOCK_GLOWSTONE: - { - instrument = E_INST_CLICKS; - sampleName = "note.hat"; - break; - } - - case E_BLOCK_STONE: - case E_BLOCK_STONE_BRICKS: - case E_BLOCK_COBBLESTONE: - case E_BLOCK_OBSIDIAN: - case E_BLOCK_NETHERRACK: - case E_BLOCK_BRICK: - case E_BLOCK_NETHER_BRICK: - { - // TODO: add other stone-based blocks if needed - instrument = E_INST_BASS_DRUM; - sampleName = "note.bassattack"; - break; - } - - default: - { - instrument = E_INST_HARP_PIANO; - sampleName = "note.harp"; - break; - } - } - - m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK); - - // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all - float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f); - m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch); -} - - - - - -char cNoteEntity::GetPitch(void) -{ - return m_Pitch; -} - - - - - -void cNoteEntity::SetPitch(char a_Pitch) -{ - m_Pitch = a_Pitch % 25; -} - - - - - -void cNoteEntity::IncrementPitch(void) -{ - SetPitch(m_Pitch + 1); -} - - - - - -bool cNoteEntity::LoadFromJson(const Json::Value & a_Value) -{ - - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - m_Pitch = (char)a_Value.get("p", 0).asInt(); - - return true; -} - - - - - -void cNoteEntity::SaveToJson(Json::Value & a_Value) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - a_Value["p"] = m_Pitch; -} - - - - diff --git a/source/BlockEntities/NoteEntity.h b/source/BlockEntities/NoteEntity.h deleted file mode 100644 index e2d088f44..000000000 --- a/source/BlockEntities/NoteEntity.h +++ /dev/null @@ -1,63 +0,0 @@ - -#pragma once - -#include "BlockEntity.h" - - -namespace Json -{ - class Value; -} - - - - - -enum ENUM_NOTE_INSTRUMENTS -{ - E_INST_HARP_PIANO = 0, - E_INST_DOUBLE_BASS = 1, - E_INST_SNARE_DRUM = 2, - E_INST_CLICKS = 3, - E_INST_BASS_DRUM = 4 -}; - - - - - -// tolua_begin - -class cNoteEntity : - public cBlockEntity -{ - typedef cBlockEntity super; -public: - - // tolua_end - - /// Creates a new note entity. a_World may be NULL - cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); - - bool LoadFromJson(const Json::Value & a_Value); - virtual void SaveToJson(Json::Value & a_Value) override; - - // tolua_begin - - char GetPitch(void); - void SetPitch(char a_Pitch); - void IncrementPitch(void); - void MakeSound(void); - - // tolua_end - - virtual void UsedBy(cPlayer * a_Player) override; - virtual void SendTo(cClientHandle & a_Client) override { }; - -private: - char m_Pitch; -} ; // tolua_export - - - - diff --git a/source/BlockEntities/SignEntity.cpp b/source/BlockEntities/SignEntity.cpp deleted file mode 100644 index 81f6f6d77..000000000 --- a/source/BlockEntities/SignEntity.cpp +++ /dev/null @@ -1,115 +0,0 @@ - -// SignEntity.cpp - -// Implements the cSignEntity class representing a single sign in the world - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include -#include "SignEntity.h" -#include "../Entities/Player.h" - - - - - -cSignEntity::cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) : - super(a_BlockType, a_X, a_Y, a_Z, a_World) -{ -} - - - - - -// It don't do anything when 'used' -void cSignEntity::UsedBy(cPlayer * a_Player) -{ - UNUSED(a_Player); -} - - - - - -void cSignEntity::SetLines(const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) -{ - m_Line[0] = a_Line1; - m_Line[1] = a_Line2; - m_Line[2] = a_Line3; - m_Line[3] = a_Line4; -} - - - - - -void cSignEntity::SetLine(int a_Index, const AString & a_Line) -{ - if ((a_Index < 0) || (a_Index >= ARRAYCOUNT(m_Line))) - { - LOGWARNING("%s: setting a non-existent line %d (value \"%s\"", __FUNCTION__, a_Index, a_Line.c_str()); - return; - } - m_Line[a_Index] = a_Line; -} - - - - - -AString cSignEntity::GetLine(int a_Index) const -{ - if ((a_Index < 0) || (a_Index >= ARRAYCOUNT(m_Line))) - { - LOGWARNING("%s: requesting a non-existent line %d", __FUNCTION__, a_Index); - return ""; - } - return m_Line[a_Index]; -} - - - - - -void cSignEntity::SendTo(cClientHandle & a_Client) -{ - a_Client.SendUpdateSign(m_PosX, m_PosY, m_PosZ, m_Line[0], m_Line[1], m_Line[2], m_Line[3]); -} - - - - - -bool cSignEntity::LoadFromJson(const Json::Value & a_Value) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - m_Line[0] = a_Value.get("Line1", "").asString(); - m_Line[1] = a_Value.get("Line2", "").asString(); - m_Line[2] = a_Value.get("Line3", "").asString(); - m_Line[3] = a_Value.get("Line4", "").asString(); - - return true; -} - - - - - -void cSignEntity::SaveToJson(Json::Value & a_Value) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - a_Value["Line1"] = m_Line[0]; - a_Value["Line2"] = m_Line[1]; - a_Value["Line3"] = m_Line[2]; - a_Value["Line4"] = m_Line[3]; -} - - - - diff --git a/source/BlockEntities/SignEntity.h b/source/BlockEntities/SignEntity.h deleted file mode 100644 index d998ff1e8..000000000 --- a/source/BlockEntities/SignEntity.h +++ /dev/null @@ -1,67 +0,0 @@ - -// SignEntity.h - -// Declares the cSignEntity class representing a single sign in the world - - - - - -#pragma once - -#include "BlockEntity.h" - - - - - -namespace Json -{ - class Value; -} - - - - - -// tolua_begin - -class cSignEntity : - public cBlockEntity -{ - typedef cBlockEntity super; - -public: - - // tolua_end - - /// Creates a new empty sign entity at the specified block coords and block type (wall or standing). a_World may be NULL - cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World); - - bool LoadFromJson( const Json::Value& a_Value ); - virtual void SaveToJson(Json::Value& a_Value ) override; - - // tolua_begin - - /// Sets all the sign's lines - void SetLines(const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); - - /// Sets individual line (zero-based index) - void SetLine(int a_Index, const AString & a_Line); - - /// Retrieves individual line (zero-based index) - AString GetLine(int a_Index) const; - - // tolua_end - - virtual void UsedBy(cPlayer * a_Player) override; - virtual void SendTo(cClientHandle & a_Client) override; - -private: - - AString m_Line[4]; -} ; // tolua_export - - - - diff --git a/source/BlockID.cpp b/source/BlockID.cpp deleted file mode 100644 index f8949577e..000000000 --- a/source/BlockID.cpp +++ /dev/null @@ -1,954 +0,0 @@ -// BlockID.cpp - -// Implements the helper functions for converting Block ID string to int etc. - -#include "Globals.h" -#include "BlockID.h" -#include "../iniFile/iniFile.h" -#include "Item.h" -#include "Mobs/Monster.h" - - - - - -NIBBLETYPE g_BlockLightValue[256]; -NIBBLETYPE g_BlockSpreadLightFalloff[256]; -bool g_BlockTransparent[256]; -bool g_BlockOneHitDig[256]; -bool g_BlockPistonBreakable[256]; -bool g_BlockIsSnowable[256]; -bool g_BlockRequiresSpecialTool[256]; -bool g_BlockIsSolid[256]; -bool g_BlockIsTorchPlaceable[256]; - - - - - -class cBlockIDMap -{ - // Making the map case-insensitive: - struct Comparator - { - bool operator()(const AString & a_Item1, const AString & a_Item2) const - { - return (NoCaseCompare(a_Item1, a_Item2) > 0); - } - } ; - - typedef std::map, Comparator> ItemMap; - -public: - cBlockIDMap(void) - { - cIniFile Ini; - if (!Ini.ReadFile("items.ini")) - { - return; - } - int KeyID = Ini.FindKey("Items"); - if (KeyID == cIniFile::noID) - { - return; - } - int NumValues = Ini.GetNumValues(KeyID); - for (int i = 0; i < NumValues; i++) - { - AString Name = Ini.GetValueName(KeyID, i); - if (Name.empty()) - { - continue; - } - AString Value = Ini.GetValue(KeyID, i); - AddToMap(Name, Value); - } // for i - Ini.Values[] - } - - - int Resolve(const AString & a_ItemName) - { - ItemMap::iterator itr = m_Map.find(a_ItemName); - if (itr == m_Map.end()) - { - return -1; - } - return itr->second.first; - } - - - bool ResolveItem(const AString & a_ItemName, cItem & a_Item) - { - // Split into parts divided by either ':' or '^' - AStringVector Split = StringSplitAndTrim(a_ItemName, ":^"); - if (Split.empty()) - { - return false; - } - - ItemMap::iterator itr = m_Map.find(Split[0]); - if (itr != m_Map.end()) - { - // Resolved as a string, assign the type and the default damage / count - a_Item.m_ItemType = itr->second.first; - a_Item.m_ItemDamage = itr->second.second; - if (a_Item.m_ItemDamage == -1) - { - a_Item.m_ItemDamage = 0; - } - } - else - { - // Not a resolvable string, try pure numbers: "45:6", "45^6" etc. - a_Item.m_ItemType = (short)atoi(Split[0].c_str()); - if ((a_Item.m_ItemType == 0) && (Split[0] != "0")) - { - // Parsing the number failed - return false; - } - } - - // Parse the damage, if present: - if (Split.size() < 2) - { - // Not present, set the item as valid and return success: - a_Item.m_ItemCount = 1; - return true; - } - - a_Item.m_ItemDamage = atoi(Split[1].c_str()); - if ((a_Item.m_ItemDamage == 0) && (Split[1] != "0")) - { - // Parsing the number failed - return false; - } - a_Item.m_ItemCount = 1; - return true; - } - - - AString Desolve(short a_ItemType, short a_ItemDamage) - { - // First try an exact match, both ItemType and ItemDamage ("birchplanks=5:2"): - for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr) - { - if ((itr->second.first == a_ItemType) && (itr->second.second == a_ItemDamage)) - { - return itr->first; - } - } // for itr - m_Map[] - - // There is no exact match, try matching ItemType only ("planks=5"): - if (a_ItemDamage == 0) - { - for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr) - { - if ((itr->second.first == a_ItemType) && (itr->second.second == -1)) - { - return itr->first; - } - } // for itr - m_Map[] - } - - // No match at all, synthesize a string ("5:1"): - AString res; - if (a_ItemDamage == -1) - { - Printf(res, "%d", a_ItemType); - } - else - { - Printf(res, "%d:%d", a_ItemType, a_ItemDamage); - } - return res; - } - - -protected: - ItemMap m_Map; - - - void AddToMap(const AString & a_Name, const AString & a_Value) - { - AStringVector Split = StringSplit(a_Value, ":"); - if (Split.size() == 1) - { - Split = StringSplit(a_Value, "^"); - } - if (Split.empty()) - { - return; - } - short ItemType = (short)atoi(Split[0].c_str()); - short ItemDamage = (Split.size() > 1) ? (short)atoi(Split[1].c_str()) : -1; - m_Map[a_Name] = std::make_pair(ItemType, ItemDamage); - } -} ; - - - - - -static cBlockIDMap gsBlockIDMap; - - - - - -/* -// Quick self-test: -class Tester -{ -public: - Tester(void) - { - cItem Item; - gsBlockIDMap.ResolveItem("charcoal", Item); - AString Charcoal = gsBlockIDMap.Desolve(Item.m_ItemType, Item.m_ItemDamage); - ASSERT(Charcoal == "charcoal"); - } -} test; -//*/ - - - - - -BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString) -{ - int res = atoi(a_BlockTypeString.c_str()); - if ((res != 0) || (a_BlockTypeString.compare("0") == 0)) - { - // It was a valid number, return that - return res; - } - - return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString)); -} - - - - -bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item) -{ - return gsBlockIDMap.ResolveItem(TrimString(a_ItemTypeString), a_Item); -} - - - - - -AString ItemToString(const cItem & a_Item) -{ - return gsBlockIDMap.Desolve(a_Item.m_ItemType, a_Item.m_ItemDamage); -} - - - - - -AString ItemTypeToString(short a_ItemType) -{ - return gsBlockIDMap.Desolve(a_ItemType, -1); -} - - - - - -AString ItemToFullString(const cItem & a_Item) -{ - AString res; - Printf(res, "%s:%d * %d", ItemToString(a_Item).c_str(), a_Item.m_ItemDamage, a_Item.m_ItemCount); - return res; -} - - - - - -EMCSBiome StringToBiome(const AString & a_BiomeString) -{ - // If it is a number, return it: - int res = atoi(a_BiomeString.c_str()); - if ((res != 0) || (a_BiomeString.compare("0") == 0)) - { - // It was a valid number - return (EMCSBiome)res; - } - - // Convert using the built-in map: - static struct { - EMCSBiome m_Biome; - const char * m_String; - } BiomeMap[] = - { - {biOcean, "Ocean"} , - {biPlains, "Plains"}, - {biDesert, "Desert"}, - {biExtremeHills, "ExtremeHills"}, - {biForest, "Forest"}, - {biTaiga, "Taiga"}, - {biSwampland, "Swampland"}, - {biRiver, "River"}, - {biNether, "Hell"}, - {biNether, "Nether"}, - {biEnd, "Sky"}, - {biEnd, "End"}, - {biFrozenOcean, "FrozenOcean"}, - {biFrozenRiver, "FrozenRiver"}, - {biIcePlains, "IcePlains"}, - {biIcePlains, "Tundra"}, - {biIceMountains, "IceMountains"}, - {biMushroomIsland, "MushroomIsland"}, - {biMushroomShore, "MushroomShore"}, - {biBeach, "Beach"}, - {biDesertHills, "DesertHills"}, - {biForestHills, "ForestHills"}, - {biTaigaHills, "TaigaHills"}, - {biExtremeHillsEdge, "ExtremeHillsEdge"}, - {biJungle, "Jungle"}, - {biJungleHills, "JungleHills"}, - - // Release 1.7 biomes: - {biJungleEdge, "JungleEdge"}, - {biDeepOcean, "DeepOcean"}, - {biStoneBeach, "StoneBeach"}, - {biColdBeach, "ColdBeach"}, - {biBirchForest, "BirchForest"}, - {biBirchForestHills, "BirchForestHills"}, - {biRoofedForest, "RoofedForest"}, - {biColdTaiga, "ColdTaiga"}, - {biColdTaigaHills, "ColdTaigaHills"}, - {biMegaTaiga, "MegaTaiga"}, - {biMegaTaigaHills, "MegaTaigaHills"}, - {biExtremeHillsPlus, "ExtremeHillsPlus"}, - {biSavanna, "Savanna"}, - {biSavannaPlateau, "SavannaPlateau"}, - {biMesa, "Mesa"}, - {biMesaPlateauF, "MesaPlateauF"}, - {biMesaPlateau, "MesaPlateau"}, - - // Release 1.7 variants: - {biSunflowerPlains, "SunflowerPlains"}, - {biDesertM, "DesertM"}, - {biExtremeHillsM, "ExtremeHillsM"}, - {biFlowerForest, "FlowerForest"}, - {biTaigaM, "TaigaM"}, - {biSwamplandM, "SwamplandM"}, - {biIcePlainsSpikes, "IcePlainsSpikes"}, - {biJungleM, "JungleM"}, - {biJungleEdgeM, "JungleEdgeM"}, - {biBirchForestM, "BirchForestM"}, - {biBirchForestHillsM, "BirchForestHillsM"}, - {biRoofedForestM, "RoofedForestM"}, - {biColdTaigaM, "ColdTaigaM"}, - {biMegaSpruceTaiga, "MegaSpruceTaiga"}, - {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"}, - {biExtremeHillsPlusM, "ExtremeHillsPlusM"}, - {biSavannaM, "SavannaM"}, - {biSavannaPlateauM, "SavannaPlateauM"}, - {biMesaBryce, "MesaBryce"}, - {biMesaPlateauFM, "MesaPlateauFM"}, - {biMesaPlateauM, "MesaPlateauM"}, - } ; - - for (int i = 0; i < ARRAYCOUNT(BiomeMap); i++) - { - if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0) - { - return BiomeMap[i].m_Biome; - } - } // for i - BiomeMap[] - return (EMCSBiome)-1; -} - - - - - -int StringToMobType(const AString & a_MobString) -{ - static struct { - int m_MobType; - const char * m_String; - } MobMap [] = - { - {cMonster::mtCreeper, "Creeper"}, - {cMonster::mtSkeleton, "Skeleton"}, - {cMonster::mtSpider, "Spider"}, - {cMonster::mtGiant, "Giant"}, - {cMonster::mtZombie, "Zombie"}, - {cMonster::mtSlime, "Slime"}, - {cMonster::mtGhast, "Ghast"}, - {cMonster::mtZombiePigman, "ZombiePigman"}, - {cMonster::mtEnderman, "Enderman"}, - {cMonster::mtCaveSpider, "CaveSpider"}, - {cMonster::mtSilverfish, "SilverFish"}, - {cMonster::mtBlaze, "Blaze"}, - {cMonster::mtMagmaCube, "MagmaCube"}, - {cMonster::mtEnderDragon, "EnderDragon"}, - {cMonster::mtWither, "Wither"}, - {cMonster::mtBat, "Bat"}, - {cMonster::mtWitch, "Witch"}, - {cMonster::mtPig, "Pig"}, - {cMonster::mtSheep, "Sheep"}, - {cMonster::mtCow, "Cow"}, - {cMonster::mtChicken, "Chicken"}, - {cMonster::mtSquid, "Squid"}, - {cMonster::mtWolf, "Wolf"}, - {cMonster::mtMooshroom, "Mooshroom"}, - {cMonster::mtSnowGolem, "SnowGolem"}, - {cMonster::mtOcelot, "Ocelot"}, - {cMonster::mtIronGolem, "IronGolem"}, - {cMonster::mtVillager, "Villager"}, - }; - for (int i = 0; i < ARRAYCOUNT(MobMap); i++) - { - if (NoCaseCompare(MobMap[i].m_String, a_MobString) == 0) - { - return MobMap[i].m_MobType; - } - } // for i - MobMap[] - return -1; -} - - - - - -eDimension StringToDimension(const AString & a_DimensionString) -{ - // First try decoding as a number - int res = atoi(a_DimensionString.c_str()); - if ((res != 0) || (a_DimensionString == "0")) - { - // It was a valid number - return (eDimension)res; - } - - // Decode using a built-in map: - static struct - { - eDimension m_Dimension; - const char * m_String; - } DimensionMap [] = - { - { dimOverworld, "Overworld"}, - { dimOverworld, "Normal"}, - { dimOverworld, "World"}, - { dimNether, "Nether"}, - { dimNether, "Hell"}, // Alternate name for End - { dimEnd, "End"}, - { dimEnd, "Sky"}, // Old name for End - } ; - for (int i = 0; i < ARRAYCOUNT(DimensionMap); i++) - { - if (NoCaseCompare(DimensionMap[i].m_String, a_DimensionString) == 0) - { - return DimensionMap[i].m_Dimension; - } - } // for i - DimensionMap[] - - // Not found - return (eDimension)-1000; -} - - - - - -/// Translates damage type constant to a string representation (built-in). -AString DamageTypeToString(eDamageType a_DamageType) -{ - switch (a_DamageType) - { - case dtAttack: return "dtAttack"; - case dtRangedAttack: return "dtRangedAttack"; - case dtLightning: return "dtLightning"; - case dtFalling: return "dtFalling"; - case dtDrowning: return "dtDrowning"; - case dtSuffocating: return "dtSuffocation"; - case dtStarving: return "dtStarving"; - case dtCactusContact: return "dtCactusContact"; - case dtLavaContact: return "dtLavaContact"; - case dtPoisoning: return "dtPoisoning"; - case dtOnFire: return "dtOnFire"; - case dtFireContact: return "dtFireContact"; - case dtInVoid: return "dtInVoid"; - case dtPotionOfHarming: return "dtPotionOfHarming"; - case dtAdmin: return "dtAdmin"; - } - - // Unknown damage type: - ASSERT(!"Unknown DamageType"); - return Printf("dtUnknown_%d", (int)a_DamageType); -} - - - - - -/// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure -eDamageType StringToDamageType(const AString & a_DamageTypeString) -{ - // First try decoding as a number: - int res = atoi(a_DamageTypeString.c_str()); - if ((res != 0) || (a_DamageTypeString == "0")) - { - // It was a valid number - return (eDamageType)res; - } - - // Decode using a built-in map: - static struct - { - eDamageType m_DamageType; - const char * m_String; - } DamageTypeMap [] = - { - // Cannonical names: - { dtAttack, "dtAttack"}, - { dtRangedAttack, "dtRangedAttack"}, - { dtLightning, "dtLightning"}, - { dtFalling, "dtFalling"}, - { dtDrowning, "dtDrowning"}, - { dtSuffocating, "dtSuffocation"}, - { dtStarving, "dtStarving"}, - { dtCactusContact, "dtCactusContact"}, - { dtLavaContact, "dtLavaContact"}, - { dtPoisoning, "dtPoisoning"}, - { dtOnFire, "dtOnFire"}, - { dtFireContact, "dtFireContact"}, - { dtInVoid, "dtInVoid"}, - { dtPotionOfHarming, "dtPotionOfHarming"}, - { dtAdmin, "dtAdmin"}, - - // Common synonyms: - { dtAttack, "dtPawnAttack"}, - { dtAttack, "dtEntityAttack"}, - { dtAttack, "dtMob"}, - { dtAttack, "dtMobAttack"}, - { dtRangedAttack, "dtArrowAttack"}, - { dtRangedAttack, "dtArrow"}, - { dtRangedAttack, "dtProjectile"}, - { dtFalling, "dtFall"}, - { dtDrowning, "dtDrown"}, - { dtSuffocating, "dtSuffocation"}, - { dtStarving, "dtStarvation"}, - { dtStarving, "dtHunger"}, - { dtCactusContact, "dtCactus"}, - { dtCactusContact, "dtCactuses"}, - { dtCactusContact, "dtCacti"}, - { dtLavaContact, "dtLava"}, - { dtPoisoning, "dtPoison"}, - { dtOnFire, "dtBurning"}, - { dtFireContact, "dtInFire"}, - { dtAdmin, "dtPlugin"}, - } ; - for (int i = 0; i < ARRAYCOUNT(DamageTypeMap); i++) - { - if (NoCaseCompare(DamageTypeMap[i].m_String, a_DamageTypeString) == 0) - { - return DamageTypeMap[i].m_DamageType; - } - } // for i - DamageTypeMap[] - - // Not found: - return (eDamageType)-1; -} - - - - - -cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default) -{ - AString ItemStr = a_IniFile.GetValueSet(a_Section, a_Key, a_Default); - cItem res; - if (!StringToItem(ItemStr, res)) - { - res.Empty(); - } - return res; -} - - - - - -// This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor: -class cBlockPropertiesInitializer -{ -public: - cBlockPropertiesInitializer(void) - { - memset(g_BlockLightValue, 0x00, sizeof(g_BlockLightValue)); - memset(g_BlockSpreadLightFalloff, 0x0f, sizeof(g_BlockSpreadLightFalloff)); // 0x0f means total falloff - memset(g_BlockTransparent, 0x00, sizeof(g_BlockTransparent)); - memset(g_BlockOneHitDig, 0x00, sizeof(g_BlockOneHitDig)); - memset(g_BlockPistonBreakable, 0x00, sizeof(g_BlockPistonBreakable)); - memset(g_BlockIsTorchPlaceable, 0x00, sizeof(g_BlockIsTorchPlaceable)); - - // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415 - for (int i = 0; i < ARRAYCOUNT(g_BlockIsSnowable); i++) - { - g_BlockIsSnowable[i] = true; - } - memset(g_BlockRequiresSpecialTool, 0x00, sizeof(g_BlockRequiresSpecialTool)); // Set all blocks to false - - // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415 - for (int i = 0; i < ARRAYCOUNT(g_BlockIsSolid); i++) - { - g_BlockIsSolid[i] = true; - } - - // Emissive blocks - g_BlockLightValue[E_BLOCK_FIRE] = 15; - g_BlockLightValue[E_BLOCK_GLOWSTONE] = 15; - g_BlockLightValue[E_BLOCK_JACK_O_LANTERN] = 15; - g_BlockLightValue[E_BLOCK_LAVA] = 15; - g_BlockLightValue[E_BLOCK_STATIONARY_LAVA] = 15; - g_BlockLightValue[E_BLOCK_END_PORTAL] = 15; - g_BlockLightValue[E_BLOCK_REDSTONE_LAMP_ON] = 15; - g_BlockLightValue[E_BLOCK_TORCH] = 14; - g_BlockLightValue[E_BLOCK_BURNING_FURNACE] = 13; - g_BlockLightValue[E_BLOCK_NETHER_PORTAL] = 11; - g_BlockLightValue[E_BLOCK_REDSTONE_ORE_GLOWING] = 9; - g_BlockLightValue[E_BLOCK_REDSTONE_REPEATER_ON] = 9; - g_BlockLightValue[E_BLOCK_REDSTONE_TORCH_ON] = 7; - g_BlockLightValue[E_BLOCK_BREWING_STAND] = 1; - g_BlockLightValue[E_BLOCK_BROWN_MUSHROOM] = 1; - g_BlockLightValue[E_BLOCK_DRAGON_EGG] = 1; - - // Spread blocks - g_BlockSpreadLightFalloff[E_BLOCK_AIR] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_CAKE] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_CHEST] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_COBWEB] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_CROPS] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_FENCE] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_FENCE_GATE] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_FIRE] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_GLASS] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_GLASS_PANE] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_GLOWSTONE] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_IRON_BARS] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_IRON_DOOR] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_LEAVES] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_SIGN_POST] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_TORCH] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_VINES] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_WALLSIGN] = 1; - g_BlockSpreadLightFalloff[E_BLOCK_WOODEN_DOOR] = 1; - - // Light in water and lava dissapears faster: - g_BlockSpreadLightFalloff[E_BLOCK_LAVA] = 3; - g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_LAVA] = 3; - g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_WATER] = 3; - g_BlockSpreadLightFalloff[E_BLOCK_WATER] = 3; - - // Transparent blocks - g_BlockTransparent[E_BLOCK_ACTIVATOR_RAIL] = true; - g_BlockTransparent[E_BLOCK_AIR] = true; - g_BlockTransparent[E_BLOCK_BIG_FLOWER] = true; - g_BlockTransparent[E_BLOCK_BROWN_MUSHROOM] = true; - g_BlockTransparent[E_BLOCK_CARROTS] = true; - g_BlockTransparent[E_BLOCK_CHEST] = true; - g_BlockTransparent[E_BLOCK_COBWEB] = true; - g_BlockTransparent[E_BLOCK_CROPS] = true; - g_BlockTransparent[E_BLOCK_DANDELION] = true; - g_BlockTransparent[E_BLOCK_DETECTOR_RAIL] = true; - g_BlockTransparent[E_BLOCK_FENCE] = true; - g_BlockTransparent[E_BLOCK_FENCE_GATE] = true; - g_BlockTransparent[E_BLOCK_FIRE] = true; - g_BlockTransparent[E_BLOCK_FLOWER] = true; - g_BlockTransparent[E_BLOCK_FLOWER_POT] = true; - g_BlockTransparent[E_BLOCK_GLASS] = true; - g_BlockTransparent[E_BLOCK_GLASS_PANE] = true; - g_BlockTransparent[E_BLOCK_STAINED_GLASS] = true; - g_BlockTransparent[E_BLOCK_STAINED_GLASS_PANE] = true; - g_BlockTransparent[E_BLOCK_ICE] = true; - g_BlockTransparent[E_BLOCK_IRON_DOOR] = true; - g_BlockTransparent[E_BLOCK_LAVA] = true; - g_BlockTransparent[E_BLOCK_LEAVES] = true; - g_BlockTransparent[E_BLOCK_LEVER] = true; - g_BlockTransparent[E_BLOCK_MELON_STEM] = true; - g_BlockTransparent[E_BLOCK_NETHER_BRICK_FENCE] = true; - g_BlockTransparent[E_BLOCK_NEW_LEAVES] = true; - g_BlockTransparent[E_BLOCK_POTATOES] = true; - g_BlockTransparent[E_BLOCK_POWERED_RAIL] = true; - g_BlockTransparent[E_BLOCK_PISTON_EXTENSION] = true; - g_BlockTransparent[E_BLOCK_PUMPKIN_STEM] = true; - g_BlockTransparent[E_BLOCK_RAIL] = true; - g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true; - g_BlockTransparent[E_BLOCK_SIGN_POST] = true; - g_BlockTransparent[E_BLOCK_SNOW] = true; - g_BlockTransparent[E_BLOCK_STATIONARY_LAVA] = true; - g_BlockTransparent[E_BLOCK_STATIONARY_WATER] = true; - g_BlockTransparent[E_BLOCK_STONE_PRESSURE_PLATE] = true; - g_BlockTransparent[E_BLOCK_TALL_GRASS] = true; - g_BlockTransparent[E_BLOCK_TORCH] = true; - g_BlockTransparent[E_BLOCK_VINES] = true; - g_BlockTransparent[E_BLOCK_WALLSIGN] = true; - g_BlockTransparent[E_BLOCK_WATER] = true; - g_BlockTransparent[E_BLOCK_WOODEN_DOOR] = true; - g_BlockTransparent[E_BLOCK_WOODEN_PRESSURE_PLATE] = true; - - // TODO: Any other transparent blocks? - - // One hit break blocks - g_BlockOneHitDig[E_BLOCK_ACTIVE_COMPARATOR] = true; - g_BlockOneHitDig[E_BLOCK_BIG_FLOWER] = true; - g_BlockOneHitDig[E_BLOCK_BROWN_MUSHROOM] = true; - g_BlockOneHitDig[E_BLOCK_CARROTS] = true; - g_BlockOneHitDig[E_BLOCK_CROPS] = true; - g_BlockOneHitDig[E_BLOCK_DANDELION] = true; - g_BlockOneHitDig[E_BLOCK_FIRE] = true; - g_BlockOneHitDig[E_BLOCK_FLOWER] = true; - g_BlockOneHitDig[E_BLOCK_FLOWER_POT] = true; - g_BlockOneHitDig[E_BLOCK_INACTIVE_COMPARATOR] = true; - g_BlockOneHitDig[E_BLOCK_LOCKED_CHEST] = true; - g_BlockOneHitDig[E_BLOCK_MELON_STEM] = true; - g_BlockOneHitDig[E_BLOCK_POTATOES] = true; - g_BlockOneHitDig[E_BLOCK_PUMPKIN_STEM] = true; - g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_OFF] = true; - g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_ON] = true; - g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_OFF] = true; - g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_ON] = true; - g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true; - g_BlockOneHitDig[E_BLOCK_RED_MUSHROOM] = true; - g_BlockOneHitDig[E_BLOCK_REEDS] = true; - g_BlockOneHitDig[E_BLOCK_SAPLING] = true; - g_BlockOneHitDig[E_BLOCK_TNT] = true; - g_BlockOneHitDig[E_BLOCK_TALL_GRASS] = true; - g_BlockOneHitDig[E_BLOCK_TORCH] = true; - - // Blocks that breaks when pushed by piston - g_BlockPistonBreakable[E_BLOCK_ACTIVE_COMPARATOR] = true; - g_BlockPistonBreakable[E_BLOCK_AIR] = true; - g_BlockPistonBreakable[E_BLOCK_BED] = true; - g_BlockPistonBreakable[E_BLOCK_BIG_FLOWER] = true; - g_BlockPistonBreakable[E_BLOCK_BROWN_MUSHROOM] = true; - g_BlockPistonBreakable[E_BLOCK_COBWEB] = true; - g_BlockPistonBreakable[E_BLOCK_CROPS] = true; - g_BlockPistonBreakable[E_BLOCK_DANDELION] = true; - g_BlockPistonBreakable[E_BLOCK_DEAD_BUSH] = true; - g_BlockPistonBreakable[E_BLOCK_FIRE] = true; - g_BlockPistonBreakable[E_BLOCK_FLOWER] = true; - g_BlockPistonBreakable[E_BLOCK_INACTIVE_COMPARATOR] = true; - g_BlockPistonBreakable[E_BLOCK_IRON_DOOR] = true; - g_BlockPistonBreakable[E_BLOCK_JACK_O_LANTERN] = true; - g_BlockPistonBreakable[E_BLOCK_LADDER] = true; - g_BlockPistonBreakable[E_BLOCK_LAVA] = true; - g_BlockPistonBreakable[E_BLOCK_LEVER] = true; - g_BlockPistonBreakable[E_BLOCK_MELON] = true; - g_BlockPistonBreakable[E_BLOCK_MELON_STEM] = true; - g_BlockPistonBreakable[E_BLOCK_PUMPKIN] = true; - g_BlockPistonBreakable[E_BLOCK_PUMPKIN_STEM] = true; - g_BlockPistonBreakable[E_BLOCK_REDSTONE_REPEATER_OFF] = true; - g_BlockPistonBreakable[E_BLOCK_REDSTONE_REPEATER_ON] = true; - g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_OFF] = true; - g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_ON] = true; - g_BlockPistonBreakable[E_BLOCK_REDSTONE_WIRE] = true; - g_BlockPistonBreakable[E_BLOCK_RED_MUSHROOM] = true; - g_BlockPistonBreakable[E_BLOCK_REEDS] = true; - g_BlockPistonBreakable[E_BLOCK_SNOW] = true; - g_BlockPistonBreakable[E_BLOCK_STATIONARY_LAVA] = true; - g_BlockPistonBreakable[E_BLOCK_STATIONARY_WATER] = true; - g_BlockPistonBreakable[E_BLOCK_STONE_BUTTON] = true; - g_BlockPistonBreakable[E_BLOCK_STONE_PRESSURE_PLATE] = true; - g_BlockPistonBreakable[E_BLOCK_TALL_GRASS] = true; - g_BlockPistonBreakable[E_BLOCK_TORCH] = true; - g_BlockPistonBreakable[E_BLOCK_VINES] = true; - g_BlockPistonBreakable[E_BLOCK_WATER] = true; - g_BlockPistonBreakable[E_BLOCK_WOODEN_DOOR] = true; - g_BlockPistonBreakable[E_BLOCK_WOODEN_PRESSURE_PLATE] = true; - - - // Blocks that can be snowed over: - g_BlockIsSnowable[E_BLOCK_ACTIVE_COMPARATOR] = false; - g_BlockIsSnowable[E_BLOCK_AIR] = false; - g_BlockIsSnowable[E_BLOCK_BIG_FLOWER] = false; - g_BlockIsSnowable[E_BLOCK_BROWN_MUSHROOM] = false; - g_BlockIsSnowable[E_BLOCK_CACTUS] = false; - g_BlockIsSnowable[E_BLOCK_CHEST] = false; - g_BlockIsSnowable[E_BLOCK_CROPS] = false; - g_BlockIsSnowable[E_BLOCK_DANDELION] = false; - g_BlockIsSnowable[E_BLOCK_FIRE] = false; - g_BlockIsSnowable[E_BLOCK_FLOWER] = false; - g_BlockIsSnowable[E_BLOCK_GLASS] = false; - g_BlockIsSnowable[E_BLOCK_ICE] = false; - g_BlockIsSnowable[E_BLOCK_INACTIVE_COMPARATOR] = false; - g_BlockIsSnowable[E_BLOCK_LAVA] = false; - g_BlockIsSnowable[E_BLOCK_LILY_PAD] = false; - g_BlockIsSnowable[E_BLOCK_LOCKED_CHEST] = false; - g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_OFF] = false; - g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_ON] = false; - g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_OFF] = false; - g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_ON] = false; - g_BlockIsSnowable[E_BLOCK_REDSTONE_WIRE] = false; - g_BlockIsSnowable[E_BLOCK_RED_MUSHROOM] = false; - g_BlockIsSnowable[E_BLOCK_REEDS] = false; - g_BlockIsSnowable[E_BLOCK_SAPLING] = false; - g_BlockIsSnowable[E_BLOCK_SIGN_POST] = false; - g_BlockIsSnowable[E_BLOCK_SNOW] = false; - g_BlockIsSnowable[E_BLOCK_STATIONARY_LAVA] = false; - g_BlockIsSnowable[E_BLOCK_STATIONARY_WATER] = false; - g_BlockIsSnowable[E_BLOCK_TALL_GRASS] = false; - g_BlockIsSnowable[E_BLOCK_TNT] = false; - g_BlockIsSnowable[E_BLOCK_TORCH] = false; - g_BlockIsSnowable[E_BLOCK_VINES] = false; - g_BlockIsSnowable[E_BLOCK_WALLSIGN] = false; - g_BlockIsSnowable[E_BLOCK_WATER] = false; - - - // Blocks that don't drop without a special tool - g_BlockRequiresSpecialTool[E_BLOCK_BRICK] = true; - g_BlockRequiresSpecialTool[E_BLOCK_CAULDRON] = true; - g_BlockRequiresSpecialTool[E_BLOCK_COAL_ORE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_COBBLESTONE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_COBBLESTONE_STAIRS] = true; - g_BlockRequiresSpecialTool[E_BLOCK_COBWEB] = true; - g_BlockRequiresSpecialTool[E_BLOCK_DIAMOND_BLOCK] = true; - g_BlockRequiresSpecialTool[E_BLOCK_DIAMOND_ORE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_DOUBLE_STONE_SLAB] = true; - g_BlockRequiresSpecialTool[E_BLOCK_EMERALD_ORE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_END_STONE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_GOLD_BLOCK] = true; - g_BlockRequiresSpecialTool[E_BLOCK_GOLD_ORE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_IRON_BLOCK] = true; - g_BlockRequiresSpecialTool[E_BLOCK_IRON_ORE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_BLOCK] = true; - g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_ORE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_MOSSY_COBBLESTONE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_NETHERRACK] = true; - g_BlockRequiresSpecialTool[E_BLOCK_NETHER_BRICK] = true; - g_BlockRequiresSpecialTool[E_BLOCK_NETHER_BRICK_STAIRS] = true; - g_BlockRequiresSpecialTool[E_BLOCK_OBSIDIAN] = true; - g_BlockRequiresSpecialTool[E_BLOCK_REDSTONE_ORE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_REDSTONE_ORE_GLOWING] = true; - g_BlockRequiresSpecialTool[E_BLOCK_SANDSTONE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_SANDSTONE_STAIRS] = true; - g_BlockRequiresSpecialTool[E_BLOCK_SNOW] = true; - g_BlockRequiresSpecialTool[E_BLOCK_STONE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_STONE_BRICKS] = true; - g_BlockRequiresSpecialTool[E_BLOCK_STONE_BRICK_STAIRS] = true; - g_BlockRequiresSpecialTool[E_BLOCK_STONE_PRESSURE_PLATE] = true; - g_BlockRequiresSpecialTool[E_BLOCK_STONE_SLAB] = true; - g_BlockRequiresSpecialTool[E_BLOCK_VINES] = true; - - // Nonsolid Blocks: - g_BlockIsSolid[E_BLOCK_ACTIVATOR_RAIL] = false; - g_BlockIsSolid[E_BLOCK_AIR] = false; - g_BlockIsSolid[E_BLOCK_BIG_FLOWER] = false; - g_BlockIsSolid[E_BLOCK_BROWN_MUSHROOM] = false; - g_BlockIsSolid[E_BLOCK_CARROTS] = false; - g_BlockIsSolid[E_BLOCK_COBWEB] = false; - g_BlockIsSolid[E_BLOCK_CROPS] = false; - g_BlockIsSolid[E_BLOCK_DANDELION] = false; - g_BlockIsSolid[E_BLOCK_DETECTOR_RAIL] = false; - g_BlockIsSolid[E_BLOCK_END_PORTAL] = false; - g_BlockIsSolid[E_BLOCK_FIRE] = false; - g_BlockIsSolid[E_BLOCK_FLOWER] = false; - g_BlockIsSolid[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE] = false; - g_BlockIsSolid[E_BLOCK_LAVA] = false; - g_BlockIsSolid[E_BLOCK_LEVER] = false; - g_BlockIsSolid[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE] = false; - g_BlockIsSolid[E_BLOCK_MELON_STEM] = false; - g_BlockIsSolid[E_BLOCK_NETHER_PORTAL] = false; - g_BlockIsSolid[E_BLOCK_PISTON] = false; - g_BlockIsSolid[E_BLOCK_PISTON_EXTENSION] = false; - g_BlockIsSolid[E_BLOCK_RAIL] = false; - g_BlockIsSolid[E_BLOCK_REDSTONE_REPEATER_OFF] = false; - g_BlockIsSolid[E_BLOCK_REDSTONE_REPEATER_ON] = false; - g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_OFF] = false; - g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_ON] = false; - g_BlockIsSolid[E_BLOCK_REDSTONE_WIRE] = false; - g_BlockIsSolid[E_BLOCK_RED_MUSHROOM] = false; - g_BlockIsSolid[E_BLOCK_REEDS] = false; - g_BlockIsSolid[E_BLOCK_SAPLING] = false; - g_BlockIsSolid[E_BLOCK_SIGN_POST] = false; - g_BlockIsSolid[E_BLOCK_SNOW] = false; - g_BlockIsSolid[E_BLOCK_STATIONARY_LAVA] = false; - g_BlockIsSolid[E_BLOCK_STATIONARY_WATER] = false; - g_BlockIsSolid[E_BLOCK_STONE_BUTTON] = false; - g_BlockIsSolid[E_BLOCK_STONE_PRESSURE_PLATE] = false; - g_BlockIsSolid[E_BLOCK_TALL_GRASS] = false; - g_BlockIsSolid[E_BLOCK_TORCH] = false; - g_BlockIsSolid[E_BLOCK_TRIPWIRE] = false; - g_BlockIsSolid[E_BLOCK_VINES] = false; - g_BlockIsSolid[E_BLOCK_WALLSIGN] = false; - g_BlockIsSolid[E_BLOCK_WATER] = false; - g_BlockIsSolid[E_BLOCK_WOODEN_BUTTON] = false; - g_BlockIsSolid[E_BLOCK_WOODEN_PRESSURE_PLATE] = false; - g_BlockIsSolid[E_BLOCK_WOODEN_SLAB] = false; - - // Torch placeable - g_BlockIsTorchPlaceable[E_BLOCK_BEDROCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_COAL] = true; - g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_REDSTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_BOOKCASE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_BRICK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_CLAY] = true; - g_BlockIsTorchPlaceable[E_BLOCK_COAL_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_COBBLESTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_COMMAND_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_CRAFTING_TABLE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DIRT] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DISPENSER] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_STONE_SLAB] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_WOODEN_SLAB] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DROPPER] = true; - g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_END_STONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_FURNACE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GLOWSTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GOLD_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GOLD_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GRASS] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GRAVEL] = true; - g_BlockIsTorchPlaceable[E_BLOCK_HARDENED_CLAY] = true; - g_BlockIsTorchPlaceable[E_BLOCK_HAY_BALE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_HUGE_BROWN_MUSHROOM] = true; - g_BlockIsTorchPlaceable[E_BLOCK_HUGE_RED_MUSHROOM] = true; - g_BlockIsTorchPlaceable[E_BLOCK_IRON_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_IRON_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_JACK_O_LANTERN] = true; - g_BlockIsTorchPlaceable[E_BLOCK_JUKEBOX] = true; - g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_LOG] = true; - g_BlockIsTorchPlaceable[E_BLOCK_MELON] = true; - g_BlockIsTorchPlaceable[E_BLOCK_MOSSY_COBBLESTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_MYCELIUM] = true; - g_BlockIsTorchPlaceable[E_BLOCK_NETHERRACK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_NETHER_BRICK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_NETHER_QUARTZ_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_NOTE_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_OBSIDIAN] = true; - g_BlockIsTorchPlaceable[E_BLOCK_PACKED_ICE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_PLANKS] = true; - g_BlockIsTorchPlaceable[E_BLOCK_PUMPKIN] = true; - g_BlockIsTorchPlaceable[E_BLOCK_QUARTZ_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_OFF] = true; - g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_ON] = true; - g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE_GLOWING] = true; - g_BlockIsTorchPlaceable[E_BLOCK_SANDSTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_SAND] = true; - g_BlockIsTorchPlaceable[E_BLOCK_SILVERFISH_EGG] = true; - g_BlockIsTorchPlaceable[E_BLOCK_SPONGE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_STAINED_CLAY] = true; - g_BlockIsTorchPlaceable[E_BLOCK_WOOL] = true; - g_BlockIsTorchPlaceable[E_BLOCK_STONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_STONE_BRICKS] = true; - } -} BlockPropertiesInitializer; - - - - diff --git a/source/BlockID.h b/source/BlockID.h deleted file mode 100644 index f3cbc46d6..000000000 --- a/source/BlockID.h +++ /dev/null @@ -1,907 +0,0 @@ -#pragma once - -// tolua_begin -enum ENUM_BLOCK_ID -{ - E_BLOCK_AIR = 0, - E_BLOCK_STONE = 1, - E_BLOCK_GRASS = 2, - E_BLOCK_DIRT = 3, - E_BLOCK_COBBLESTONE = 4, - E_BLOCK_PLANKS = 5, - E_BLOCK_SAPLING = 6, - E_BLOCK_BEDROCK = 7, - E_BLOCK_WATER = 8, - E_BLOCK_STATIONARY_WATER = 9, - E_BLOCK_LAVA = 10, - E_BLOCK_STATIONARY_LAVA = 11, - E_BLOCK_SAND = 12, - E_BLOCK_GRAVEL = 13, - E_BLOCK_GOLD_ORE = 14, - E_BLOCK_IRON_ORE = 15, - E_BLOCK_COAL_ORE = 16, - E_BLOCK_LOG = 17, - E_BLOCK_LEAVES = 18, - E_BLOCK_SPONGE = 19, - E_BLOCK_GLASS = 20, - E_BLOCK_LAPIS_ORE = 21, - E_BLOCK_LAPIS_BLOCK = 22, - E_BLOCK_DISPENSER = 23, - E_BLOCK_SANDSTONE = 24, - E_BLOCK_NOTE_BLOCK = 25, - E_BLOCK_BED = 26, - E_BLOCK_POWERED_RAIL = 27, - E_BLOCK_DETECTOR_RAIL = 28, - E_BLOCK_STICKY_PISTON = 29, - E_BLOCK_COBWEB = 30, - E_BLOCK_TALL_GRASS = 31, - E_BLOCK_DEAD_BUSH = 32, - E_BLOCK_PISTON = 33, - E_BLOCK_PISTON_EXTENSION = 34, - E_BLOCK_WOOL = 35, - E_BLOCK_PISTON_MOVED_BLOCK = 36, - E_BLOCK_DANDELION = 37, - E_BLOCK_FLOWER = 38, - E_BLOCK_BROWN_MUSHROOM = 39, - E_BLOCK_RED_MUSHROOM = 40, - E_BLOCK_GOLD_BLOCK = 41, - E_BLOCK_IRON_BLOCK = 42, - E_BLOCK_DOUBLE_STONE_SLAB = 43, - E_BLOCK_STONE_SLAB = 44, - E_BLOCK_BRICK = 45, - E_BLOCK_TNT = 46, - E_BLOCK_BOOKCASE = 47, - E_BLOCK_MOSSY_COBBLESTONE = 48, - E_BLOCK_OBSIDIAN = 49, - E_BLOCK_TORCH = 50, - E_BLOCK_FIRE = 51, - E_BLOCK_MOB_SPAWNER = 52, - E_BLOCK_WOODEN_STAIRS = 53, - E_BLOCK_CHEST = 54, - E_BLOCK_REDSTONE_WIRE = 55, - E_BLOCK_DIAMOND_ORE = 56, - E_BLOCK_DIAMOND_BLOCK = 57, - E_BLOCK_CRAFTING_TABLE = 58, - E_BLOCK_WORKBENCH = 58, - E_BLOCK_CROPS = 59, - E_BLOCK_FARMLAND = 60, - E_BLOCK_FURNACE = 61, - E_BLOCK_LIT_FURNACE = 62, - E_BLOCK_BURNING_FURNACE = 62, - E_BLOCK_SIGN_POST = 63, - E_BLOCK_WOODEN_DOOR = 64, - E_BLOCK_LADDER = 65, - E_BLOCK_RAIL = 66, - E_BLOCK_MINECART_TRACKS = 66, - E_BLOCK_COBBLESTONE_STAIRS = 67, - E_BLOCK_WALLSIGN = 68, - E_BLOCK_LEVER = 69, - E_BLOCK_STONE_PRESSURE_PLATE = 70, - E_BLOCK_IRON_DOOR = 71, - E_BLOCK_WOODEN_PRESSURE_PLATE = 72, - E_BLOCK_REDSTONE_ORE = 73, - E_BLOCK_REDSTONE_ORE_GLOWING = 74, - E_BLOCK_REDSTONE_TORCH_OFF = 75, - E_BLOCK_REDSTONE_TORCH_ON = 76, - E_BLOCK_STONE_BUTTON = 77, - E_BLOCK_SNOW = 78, - E_BLOCK_ICE = 79, - E_BLOCK_SNOW_BLOCK = 80, - E_BLOCK_CACTUS = 81, - E_BLOCK_CLAY = 82, - E_BLOCK_SUGARCANE = 83, - E_BLOCK_REEDS = 83, - E_BLOCK_JUKEBOX = 84, - E_BLOCK_FENCE = 85, - E_BLOCK_PUMPKIN = 86, - E_BLOCK_NETHERRACK = 87, - E_BLOCK_SOULSAND = 88, - E_BLOCK_GLOWSTONE = 89, - E_BLOCK_NETHER_PORTAL = 90, - E_BLOCK_JACK_O_LANTERN = 91, - E_BLOCK_CAKE = 92, - E_BLOCK_REDSTONE_REPEATER_OFF = 93, - E_BLOCK_REDSTONE_REPEATER_ON = 94, - E_BLOCK_STAINED_GLASS = 95, - E_BLOCK_TRAPDOOR = 96, - E_BLOCK_SILVERFISH_EGG = 97, - E_BLOCK_STONE_BRICKS = 98, - E_BLOCK_HUGE_BROWN_MUSHROOM = 99, - E_BLOCK_HUGE_RED_MUSHROOM = 100, - E_BLOCK_IRON_BARS = 101, - E_BLOCK_GLASS_PANE = 102, - E_BLOCK_MELON = 103, - E_BLOCK_PUMPKIN_STEM = 104, - E_BLOCK_MELON_STEM = 105, - E_BLOCK_VINES = 106, - E_BLOCK_FENCE_GATE = 107, - E_BLOCK_BRICK_STAIRS = 108, - E_BLOCK_STONE_BRICK_STAIRS = 109, - E_BLOCK_MYCELIUM = 110, - E_BLOCK_LILY_PAD = 111, - E_BLOCK_NETHER_BRICK = 112, - E_BLOCK_NETHER_BRICK_FENCE = 113, - E_BLOCK_NETHER_BRICK_STAIRS = 114, - E_BLOCK_NETHER_WART = 115, - E_BLOCK_ENCHANTMENT_TABLE = 116, - E_BLOCK_BREWING_STAND = 117, - E_BLOCK_CAULDRON = 118, - E_BLOCK_END_PORTAL = 119, - E_BLOCK_END_PORTAL_FRAME = 120, - E_BLOCK_END_STONE = 121, - E_BLOCK_DRAGON_EGG = 122, - E_BLOCK_REDSTONE_LAMP_OFF = 123, - E_BLOCK_REDSTONE_LAMP_ON = 124, - E_BLOCK_DOUBLE_WOODEN_SLAB = 125, - E_BLOCK_WOODEN_SLAB = 126, - E_BLOCK_COCOA_POD = 127, - E_BLOCK_SANDSTONE_STAIRS = 128, - E_BLOCK_EMERALD_ORE = 129, - E_BLOCK_ENDER_CHEST = 130, - E_BLOCK_TRIPWIRE_HOOK = 131, - E_BLOCK_TRIPWIRE = 132, - E_BLOCK_EMERALD_BLOCK = 133, - E_BLOCK_SPRUCE_WOOD_STAIRS = 134, - E_BLOCK_BIRCH_WOOD_STAIRS = 135, - E_BLOCK_JUNGLE_WOOD_STAIRS = 136, - E_BLOCK_COMMAND_BLOCK = 137, - E_BLOCK_BEACON = 138, - E_BLOCK_COBBLESTONE_WALL = 139, - E_BLOCK_FLOWER_POT = 140, - E_BLOCK_CARROTS = 141, - E_BLOCK_POTATOES = 142, - E_BLOCK_WOODEN_BUTTON = 143, - E_BLOCK_HEAD = 144, - E_BLOCK_ANVIL = 145, - E_BLOCK_TRAPPED_CHEST = 146, - E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE = 147, - E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE = 148, - - E_BLOCK_INACTIVE_COMPARATOR = 149, - E_BLOCK_ACTIVE_COMPARATOR = 150, - E_BLOCK_DAYLIGHT_SENSOR = 151, - E_BLOCK_BLOCK_OF_REDSTONE = 152, - - E_BLOCK_NETHER_QUARTZ_ORE = 153, - E_BLOCK_HOPPER = 154, - E_BLOCK_QUARTZ_BLOCK = 155, - E_BLOCK_QUARTZ_STAIRS = 156, - E_BLOCK_ACTIVATOR_RAIL = 157, - - E_BLOCK_DROPPER = 158, - E_BLOCK_STAINED_CLAY = 159, - E_BLOCK_STAINED_GLASS_PANE = 160, - E_BLOCK_NEW_LEAVES = 161, // Acacia and Dark Oak IDs in Minecraft 1.7.x - E_BLOCK_NEW_LOG = 162, - E_BLOCK_ACACIA_WOOD_STAIRS = 163, - E_BLOCK_DARK_OAK_WOOD_STAIRS = 164, ///////////////////////////////// - E_BLOCK_HAY_BALE = 170, - E_BLOCK_CARPET = 171, - E_BLOCK_HARDENED_CLAY = 172, - E_BLOCK_BLOCK_OF_COAL = 173, - E_BLOCK_PACKED_ICE = 174, - E_BLOCK_BIG_FLOWER = 175, - - // Keep these two as the last values, without a number - they will get their correct number assigned automagically by C++ - // IsValidBlock() depends on this - E_BLOCK_NUMBER_OF_TYPES, ///< Number of individual (different) blocktypes - E_BLOCK_MAX_TYPE_ID = E_BLOCK_NUMBER_OF_TYPES - 1, ///< Maximum BlockType number used - - // Synonym or ID compatibility - - E_BLOCK_YELLOW_FLOWER = E_BLOCK_DANDELION, - E_BLOCK_RED_ROSE = E_BLOCK_FLOWER, - E_BLOCK_LOCKED_CHEST = E_BLOCK_STAINED_GLASS, -}; -// tolua_end - -// tolua_begin -enum ENUM_ITEM_ID -{ - E_ITEM_EMPTY = -1, - - E_ITEM_FIRST = 256, // First true item type - - E_ITEM_IRON_SHOVEL = 256, - E_ITEM_IRON_PICKAXE = 257, - E_ITEM_IRON_AXE = 258, - E_ITEM_FLINT_AND_STEEL = 259, - E_ITEM_RED_APPLE = 260, - E_ITEM_BOW = 261, - E_ITEM_ARROW = 262, - E_ITEM_COAL = 263, - E_ITEM_DIAMOND = 264, - E_ITEM_IRON = 265, - E_ITEM_GOLD = 266, - E_ITEM_IRON_SWORD = 267, - E_ITEM_WOODEN_SWORD = 268, - E_ITEM_WOODEN_SHOVEL = 269, - E_ITEM_WOODEN_PICKAXE = 270, - E_ITEM_WOODEN_AXE = 271, - E_ITEM_STONE_SWORD = 272, - E_ITEM_STONE_SHOVEL = 273, - E_ITEM_STONE_PICKAXE = 274, - E_ITEM_STONE_AXE = 275, - E_ITEM_DIAMOND_SWORD = 276, - E_ITEM_DIAMOND_SHOVEL = 277, - E_ITEM_DIAMOND_PICKAXE = 278, - E_ITEM_DIAMOND_AXE = 279, - E_ITEM_STICK = 280, - E_ITEM_BOWL = 281, - E_ITEM_MUSHROOM_SOUP = 282, - E_ITEM_GOLD_SWORD = 283, - E_ITEM_GOLD_SHOVEL = 284, - E_ITEM_GOLD_PICKAXE = 285, - E_ITEM_GOLD_AXE = 286, - E_ITEM_STRING = 287, - E_ITEM_FEATHER = 288, - E_ITEM_GUNPOWDER = 289, - E_ITEM_WOODEN_HOE = 290, - E_ITEM_STONE_HOE = 291, - E_ITEM_IRON_HOE = 292, - E_ITEM_DIAMOND_HOE = 293, - E_ITEM_GOLD_HOE = 294, - E_ITEM_SEEDS = 295, - E_ITEM_WHEAT = 296, - E_ITEM_BREAD = 297, - E_ITEM_LEATHER_CAP = 298, - E_ITEM_LEATHER_TUNIC = 299, - E_ITEM_LEATHER_PANTS = 300, - E_ITEM_LEATHER_BOOTS = 301, - E_ITEM_CHAIN_HELMET = 302, - E_ITEM_CHAIN_CHESTPLATE = 303, - E_ITEM_CHAIN_LEGGINGS = 304, - E_ITEM_CHAIN_BOOTS = 305, - E_ITEM_IRON_HELMET = 306, - E_ITEM_IRON_CHESTPLATE = 307, - E_ITEM_IRON_LEGGINGS = 308, - E_ITEM_IRON_BOOTS = 309, - E_ITEM_DIAMOND_HELMET = 310, - E_ITEM_DIAMOND_CHESTPLATE = 311, - E_ITEM_DIAMOND_LEGGINGS = 312, - E_ITEM_DIAMOND_BOOTS = 313, - E_ITEM_GOLD_HELMET = 314, - E_ITEM_GOLD_CHESTPLATE = 315, - E_ITEM_GOLD_LEGGINGS = 316, - E_ITEM_GOLD_BOOTS = 317, - E_ITEM_FLINT = 318, - E_ITEM_RAW_PORKCHOP = 319, - E_ITEM_COOKED_PORKCHOP = 320, - E_ITEM_PAINTINGS = 321, - E_ITEM_GOLDEN_APPLE = 322, - E_ITEM_SIGN = 323, - E_ITEM_WOODEN_DOOR = 324, - E_ITEM_BUCKET = 325, - E_ITEM_WATER_BUCKET = 326, - E_ITEM_LAVA_BUCKET = 327, - E_ITEM_MINECART = 328, - E_ITEM_SADDLE = 329, - E_ITEM_IRON_DOOR = 330, - E_ITEM_REDSTONE_DUST = 331, - E_ITEM_SNOWBALL = 332, - E_ITEM_BOAT = 333, - E_ITEM_LEATHER = 334, - E_ITEM_MILK = 335, - E_ITEM_CLAY_BRICK = 336, - E_ITEM_CLAY = 337, - E_ITEM_SUGARCANE = 338, - E_ITEM_SUGAR_CANE = 338, - E_ITEM_PAPER = 339, - E_ITEM_BOOK = 340, - E_ITEM_SLIMEBALL = 341, - E_ITEM_CHEST_MINECART = 342, - E_ITEM_FURNACE_MINECART = 343, - E_ITEM_EGG = 344, - E_ITEM_COMPASS = 345, - E_ITEM_FISHING_ROD = 346, - E_ITEM_CLOCK = 347, - E_ITEM_GLOWSTONE_DUST = 348, - E_ITEM_RAW_FISH = 349, - E_ITEM_COOKED_FISH = 350, - E_ITEM_DYE = 351, - E_ITEM_BONE = 352, - E_ITEM_SUGAR = 353, - E_ITEM_CAKE = 354, - E_ITEM_BED = 355, - E_ITEM_REDSTONE_REPEATER = 356, - E_ITEM_COOKIE = 357, - E_ITEM_MAP = 358, - E_ITEM_SHEARS = 359, - E_ITEM_MELON_SLICE = 360, - E_ITEM_PUMPKIN_SEEDS = 361, - E_ITEM_MELON_SEEDS = 362, - E_ITEM_RAW_BEEF = 363, - E_ITEM_STEAK = 364, - E_ITEM_RAW_CHICKEN = 365, - E_ITEM_COOKED_CHICKEN = 366, - E_ITEM_ROTTEN_FLESH = 367, - E_ITEM_ENDER_PEARL = 368, - E_ITEM_BLAZE_ROD = 369, - E_ITEM_GHAST_TEAR = 370, - E_ITEM_GOLD_NUGGET = 371, - E_ITEM_NETHER_WART = 372, - E_ITEM_POTIONS = 373, - E_ITEM_GLASS_BOTTLE = 374, - E_ITEM_SPIDER_EYE = 375, - E_ITEM_FERMENTED_SPIDER_EYE = 376, - E_ITEM_BLAZE_POWDER = 377, - E_ITEM_MAGMA_CREAM = 378, - E_ITEM_BREWING_STAND = 379, - E_ITEM_CAULDRON = 380, - E_ITEM_EYE_OF_ENDER = 381, - E_ITEM_GLISTERING_MELON = 382, - E_ITEM_SPAWN_EGG = 383, - E_ITEM_BOTTLE_O_ENCHANTING = 384, - E_ITEM_FIRE_CHARGE = 385, - E_ITEM_BOOK_AND_QUILL = 386, - E_ITEM_WRITTEN_BOOK = 387, - E_ITEM_EMERALD = 388, - E_ITEM_ITEM_FRAME = 389, - E_ITEM_FLOWER_POT = 390, - E_ITEM_CARROT = 391, - E_ITEM_POTATO = 392, - E_ITEM_BAKED_POTATO = 393, - E_ITEM_POISONOUS_POTATO = 394, - E_ITEM_EMPTY_MAP = 395, - E_ITEM_GOLDEN_CARROT = 396, - E_ITEM_HEAD = 397, - E_ITEM_CARROT_ON_STICK = 398, - E_ITEM_NETHER_STAR = 399, - E_ITEM_PUMPKIN_PIE = 400, - E_ITEM_FIREWORK_ROCKET = 401, - E_ITEM_FIREWORK_STAR = 402, - E_ITEM_ENCHANTED_BOOK = 403, - E_ITEM_COMPARATOR = 404, - E_ITEM_NETHER_BRICK = 405, - E_ITEM_NETHER_QUARTZ = 406, - E_ITEM_MINECART_WITH_TNT = 407, - E_ITEM_MINECART_WITH_HOPPER = 408, - E_ITEM_IRON_HORSE_ARMOR = 417, - E_ITEM_GOLD_HORSE_ARMOR = 418, - E_ITEM_DIAMOND_HORSE_ARMOR = 419, - E_ITEM_LEAD = 420, - E_ITEM_NAME_TAG = 421, - E_ITEM_MINECART_WITH_COMMAND_BLOCK = 422, - - // Keep these two as the last values of the consecutive list, without a number - they will get their correct number assigned automagically by C++ - // IsValidItem() depends on this! - E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES, ///< Number of individual (different) consecutive itemtypes - E_ITEM_MAX_CONSECUTIVE_TYPE_ID = E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES - 1, ///< Maximum consecutive ItemType number used - - E_ITEM_FIRST_DISC = 2256, - E_ITEM_13_DISC = 2256, - E_ITEM_CAT_DISC = 2257, - E_ITEM_BLOCKS_DISC = 2258, - E_ITEM_CHIRP_DISC = 2259, - E_ITEM_FAR_DISC = 2260, - E_ITEM_MALL_DISC = 2261, - E_ITEM_MELLOHI_DISC = 2262, - E_ITEM_STAL_DISC = 2263, - E_ITEM_STRAD_DISC = 2264, - E_ITEM_WARD_DISC = 2265, - E_ITEM_11_DISC = 2266, - E_ITEM_WAIT_DISC = 2267, - - // Keep these two as the last values of the disc list, without a number - they will get their correct number assigned automagically by C++ - // IsValidItem() depends on this! - E_ITEM_LAST_DISC_PLUS_ONE, ///< Useless, really, but needs to be present for the following value - E_ITEM_LAST_DISC = E_ITEM_LAST_DISC_PLUS_ONE - 1, ///< Maximum disc itemtype number used - - E_ITEM_LAST = E_ITEM_LAST_DISC, ///< Maximum valid ItemType -}; - - - - - -enum -{ - // Please keep this list alpha-sorted by the blocktype / itemtype part - // then number-sorted for the same block / item - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Block metas: - - // E_BLOCK_CHEST metas: - E_META_CHEST_FACING_ZM = 2, - E_META_CHEST_FACING_ZP = 3, - E_META_CHEST_FACING_XM = 4, - E_META_CHEST_FACING_XP = 5, - - // E_BLOCK_DISPENSER / E_BLOCK_DROPPER metas: - E_META_DROPSPENSER_FACING_YM = 0, - E_META_DROPSPENSER_FACING_YP = 1, - E_META_DROPSPENSER_FACING_ZM = 2, - E_META_DROPSPENSER_FACING_ZP = 3, - E_META_DROPSPENSER_FACING_XM = 4, - E_META_DROPSPENSER_FACING_XP = 5, - - // E_BLOCK_DOUBLE_STONE_SLAB metas: - E_META_DOUBLE_STONE_SLAB_STONE = 0, - E_META_DOUBLE_STONE_SLAB_SANDSTONE = 1, - E_META_DOUBLE_STONE_SLAB_WOODEN = 2, - E_META_DOUBLE_STONE_SLAB_COBBLESTONE = 3, - E_META_DOUBLE_STONE_SLAB_BRICK = 4, - E_META_DOUBLE_STONE_SLAB_STONE_BRICK = 5, - E_META_DOUBLE_STONE_SLAB_NETHER_BRICK = 6, - E_META_DOUBLE_STONE_SLAB_STONE_SECRET = 7, - - - - // E_BLOCK_HOPPER metas: - E_META_HOPPER_FACING_YM = 0, - E_META_HOPPER_UNATTACHED = 1, // Hopper doesn't move items up, there's no YP - E_META_HOPPER_FACING_ZM = 2, - E_META_HOPPER_FACING_ZP = 3, - E_META_HOPPER_FACING_XM = 4, - E_META_HOPPER_FACING_XP = 5, - - // E_BLOCK_LEAVES metas: - E_META_LEAVES_APPLE = 0, - E_META_LEAVES_CONIFER = 1, - E_META_LEAVES_BIRCH = 2, - E_META_LEAVES_JUNGLE = 3, - - // E_BLOCK_LOG metas: - E_META_LOG_APPLE = 0, - E_META_LOG_CONIFER = 1, - E_META_LOG_BIRCH = 2, - E_META_LOG_JUNGLE = 3, - - // E_BLOCK_PLANKS metas: - E_META_PLANKS_APPLE = 0, - E_META_PLANKS_CONIFER = 1, - E_META_PLANKS_BIRCH = 2, - E_META_PLANKS_JUNGLE = 3, - - // E_BLOCK_SANDSTONE metas: - E_META_SANDSTONE_NORMAL = 0, - E_META_SANDSTONE_ORNAMENT = 1, - E_META_SANDSTONE_SMOOTH = 2, - - // E_BLOCK_SAPLING metas (lowest 3 bits): - E_META_SAPLING_APPLE = 0, - E_META_SAPLING_CONIFER = 1, - E_META_SAPLING_BIRCH = 2, - E_META_SAPLING_JUNGLE = 3, - - // E_BLOCK_SILVERFISH_EGG metas: - E_META_SILVERFISH_EGG_STONE = 0, - E_META_SILVERFISH_EGG_COBBLESTONE = 1, - E_META_SILVERFISH_EGG_STONE_BRICK = 2, - - // E_BLOCK_STONE_SLAB metas: - E_META_STONE_SLAB_STONE = 0, - E_META_STONE_SLAB_SANDSTONE = 1, - E_META_STONE_SLAB_PLANKS = 2, - E_META_STONE_SLAB_COBBLESTONE = 3, - E_META_STONE_SLAB_BRICK = 4, - E_META_STONE_SLAB_STONE_BRICK = 5, - E_META_STONE_SLAB_NETHER_BRICK = 6, - E_META_STONE_SLAB_STONE_SECRET = 7, - - // E_BLOCK_STONE_BRICKS metas: - E_META_STONE_BRICK_NORMAL = 0, - E_META_STONE_BRICK_MOSSY = 1, - E_META_STONE_BRICK_CRACKED = 2, - E_META_STONE_BRICK_ORNAMENT = 3, - - // E_BLOCK_TALL_GRASS metas: - E_META_TALL_GRASS_DEAD_SHRUB = 0, - E_META_TALL_GRASS_GRASS = 1, - E_META_TALL_GRASS_FERN = 2, - - // E_BLOCK_TORCH, E_BLOCK_REDSTONE_TORCH_OFF, E_BLOCK_REDSTONE_TORCH_ON metas: - E_META_TORCH_EAST = 1, // east face of the block, pointing east - E_META_TORCH_WEST = 2, - E_META_TORCH_SOUTH = 3, - E_META_TORCH_NORTH = 4, - E_META_TORCH_FLOOR = 5, - E_META_TORCH_XM = 1, // Torch attached to the XM side of its block - E_META_TORCH_XP = 2, // Torch attached to the XP side of its block - E_META_TORCH_ZM = 3, // Torch attached to the ZM side of its block - E_META_TORCH_ZP = 4, // Torch attached to the ZP side of its block - - // E_BLOCK_WOODEN_DOUBLE_SLAB metas: - E_META_WOODEN_DOUBLE_SLAB_APPLE = 0, - E_META_WOODEN_DOUBLE_SLAB_CONIFER = 1, - E_META_WOODEN_DOUBLE_SLAB_BIRCH = 2, - E_META_WOODEN_DOUBLE_SLAB_JUNGLE = 3, - E_META_WOODEN_DOUBLE_SLAB_ACACIA = 4, - E_META_WOODEN_DOUBLE_SLAB_DARK_OAK = 5, - - // E_BLOCK_WOODEN_SLAB metas: - E_META_WOODEN_SLAB_APPLE = 0, - E_META_WOODEN_SLAB_CONIFER = 1, - E_META_WOODEN_SLAB_BIRCH = 2, - E_META_WOODEN_SLAB_JUNGLE = 3, - E_META_WOODEN_SLAB_ACACIA = 4, - E_META_WOODEN_SLAB_DARK_OAK = 5, - - // E_BLOCK_WOOL metas: - E_META_WOOL_WHITE = 0, - E_META_WOOL_ORANGE = 1, - E_META_WOOL_MAGENTA = 2, - E_META_WOOL_LIGHTBLUE = 3, - E_META_WOOL_YELLOW = 4, - E_META_WOOL_LIGHTGREEN = 5, - E_META_WOOL_PINK = 6, - E_META_WOOL_GRAY = 7, - E_META_WOOL_LIGHTGRAY = 8, - E_META_WOOL_CYAN = 9, - E_META_WOOL_PURPLE = 10, - E_META_WOOL_BLUE = 11, - E_META_WOOL_BROWN = 12, - E_META_WOOL_GREEN = 13, - E_META_WOOL_RED = 14, - E_META_WOOL_BLACK = 15, - - // E_BLOCK_CARPET metas: - E_META_CARPET_WHITE = 0, - E_META_CARPET_ORANGE = 1, - E_META_CARPET_MAGENTA = 2, - E_META_CARPET_LIGHTBLUE = 3, - E_META_CARPET_YELLOW = 4, - E_META_CARPET_LIGHTGREEN = 5, - E_META_CARPET_PINK = 6, - E_META_CARPET_GRAY = 7, - E_META_CARPET_LIGHTGRAY = 8, - E_META_CARPET_CYAN = 9, - E_META_CARPET_PURPLE = 10, - E_META_CARPET_BLUE = 11, - E_META_CARPET_BROWN = 12, - E_META_CARPET_GREEN = 13, - E_META_CARPET_RED = 14, - E_META_CARPET_BLACK = 15, - - // E_BLOCK_STAINED_CLAY metas - E_META_STAINED_CLAY_WHITE = 0, - E_META_STAINED_CLAY_ORANGE = 1, - E_META_STAINED_CLAY_MAGENTA = 2, - E_META_STAINED_CLAY_LIGHTBLUE = 3, - E_META_STAINED_CLAY_YELLOW = 4, - E_META_STAINED_CLAY_LIGHTGREEN = 5, - E_META_STAINED_CLAY_PINK = 6, - E_META_STAINED_CLAY_GRAY = 7, - E_META_STAINED_CLAY_LIGHTGRAY = 8, - E_META_STAINED_CLAY_CYAN = 9, - E_META_STAINED_CLAY_PURPLE = 10, - E_META_STAINED_CLAY_BLUE = 11, - E_META_STAINED_CLAY_BROWN = 12, - E_META_STAINED_CLAY_GREEN = 13, - E_META_STAINED_CLAY_RED = 14, - E_META_STAINED_CLAY_BLACK = 15, - - // E_BLOCK_STAINED_GLASS metas - E_META_STAINED_GLASS_WHITE = 0, - E_META_STAINED_GLASS_ORANGE = 1, - E_META_STAINED_GLASS_MAGENTA = 2, - E_META_STAINED_GLASS_LIGHTBLUE = 3, - E_META_STAINED_GLASS_YELLOW = 4, - E_META_STAINED_GLASS_LIGHTGREEN = 5, - E_META_STAINED_GLASS_PINK = 6, - E_META_STAINED_GLASS_GRAY = 7, - E_META_STAINED_GLASS_LIGHTGRAY = 8, - E_META_STAINED_GLASS_CYAN = 9, - E_META_STAINED_GLASS_PURPLE = 10, - E_META_STAINED_GLASS_BLUE = 11, - E_META_STAINED_GLASS_BROWN = 12, - E_META_STAINED_GLASS_GREEN = 13, - E_META_STAINED_GLASS_RED = 14, - E_META_STAINED_GLASS_BLACK = 15, - - // E_BLOCK_STAINED_GLASS_PANE metas - E_META_STAINED_GLASS_PANE_WHITE = 0, - E_META_STAINED_GLASS_PANE_ORANGE = 1, - E_META_STAINED_GLASS_PANE_MAGENTA = 2, - E_META_STAINED_GLASS_PANE_LIGHTBLUE = 3, - E_META_STAINED_GLASS_PANE_YELLOW = 4, - E_META_STAINED_GLASS_PANE_LIGHTGREEN = 5, - E_META_STAINED_GLASS_PANE_PINK = 6, - E_META_STAINED_GLASS_PANE_GRAY = 7, - E_META_STAINED_GLASS_PANE_LIGHTGRAY = 8, - E_META_STAINED_GLASS_PANE_CYAN = 9, - E_META_STAINED_GLASS_PANE_PURPLE = 10, - E_META_STAINED_GLASS_PANE_BLUE = 11, - E_META_STAINED_GLASS_PANE_BROWN = 12, - E_META_STAINED_GLASS_PANE_GREEN = 13, - E_META_STAINED_GLASS_PANE_RED = 14, - E_META_STAINED_GLASS_PANE_BLACK = 15, - - // E_BLOCK_SNOW metas: - E_META_SNOW_LAYER_ONE = 0, - E_META_SNOW_LAYER_TWO = 1, - E_META_SNOW_LAYER_THREE = 2, - E_META_SNOW_LAYER_FOUR = 3, - E_META_SNOW_LAYER_FIVE = 4, - E_META_SNOW_LAYER_SIX = 5, - E_META_SNOW_LAYER_SEVEN = 6, - E_META_SNOW_LAYER_EIGHT = 7, - - // E_BLOCK_RAIL metas - E_META_RAIL_ZM_ZP = 0, - E_META_RAIL_XM_XP = 1, - E_META_RAIL_ASCEND_XP = 2, - E_META_RAIL_ASCEND_XM = 3, - E_META_RAIL_ASCEND_ZM = 4, - E_META_RAIL_ASCEND_ZP = 5, - E_META_RAIL_CURVED_ZP_XP = 6, - E_META_RAIL_CURVED_ZP_XM = 7, - E_META_RAIL_CURVED_ZM_XM = 8, - E_META_RAIL_CURVED_ZM_XP = 9, - - //E_BLOCK_NEW_LEAVES metas - E_META_NEW_LEAVES_ACACIA_WOOD = 0, - E_META_NEW_LEAVES_DARK_OAK_WOOD = 1, - - //E_BLOCK_NEW_LOG metas - E_META_NEW_LOG_ACACIA_WOOD = 0, - E_META_NEW_LOG_DARK_OAK_WOOD = 1, - - //E_BLOCK_FLOWER metas - E_META_FLOWER_POPPY = 0, - E_META_FLOWER_BLUE_ORCHID = 1, - E_META_FLOWER_ALLIUM = 2, - E_META_FLOWER_RED_TULIP = 4, - E_META_FLOWER_ORANGE_TULIP = 5, - E_META_FLOWER_WHITE_TULIP = 6, - E_META_FLOWER_PINK_TULIP = 7, - E_META_FLOWER_OXEYE_DAISY = 8, - - //E_BLOCK_BIG_FLOWER metas - E_META_BIG_FLOWER_SUNFLOWER = 0, - E_META_BIG_FLOWER_LILAC = 1, - E_META_BIG_FLOWER_DOUBLE_TALL_GRASS = 2, - E_META_BIG_FLOWER_LARGE_FERN = 3, - E_META_BIG_FLOWER_ROSE_BUSH = 4, - E_META_BIG_FLOWER_PEONY = 5, - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Item metas: - - // E_ITEM_COAL metas: - E_META_COAL_NORMAL = 0, - E_META_COAL_CHARCOAL = 1, - - // E_ITEM_DYE metas: - E_META_DYE_BLACK = 0, - E_META_DYE_RED = 1, - E_META_DYE_GREEN = 2, - E_META_DYE_BROWN = 3, - E_META_DYE_BLUE = 4, - E_META_DYE_PURPLE = 5, - E_META_DYE_CYAN = 6, - E_META_DYE_LIGHTGRAY = 7, - E_META_DYE_GRAY = 8, - E_META_DYE_PINK = 9, - E_META_DYE_LIGHTGREEN = 10, - E_META_DYE_YELLOW = 11, - E_META_DYE_LIGHTBLUE = 12, - E_META_DYE_MAGENTA = 13, - E_META_DYE_ORANGE = 14, - E_META_DYE_WHITE = 15, - - // E_ITEM_GOLDEN_APPLE metas: - E_META_GOLDEN_APPLE_NORMAL = 0, - E_META_GOLDEN_APPLE_ENCHANTED = 1, - - // E_ITEM_RAW_FISH metas: - E_META_RAW_FISH_FISH = 0, - E_META_RAW_FISH_SALMON = 1, - E_META_RAW_FISH_CLOWNFISH = 2, - E_META_RAW_FISH_PUFFERFISH = 3, - - // E_ITEM_COOKED_FISH metas: - E_META_COOKED_FISH_FISH = 0, - E_META_COOKED_FISH_SALMON = 1, - E_META_COOKED_FISH_CLOWNFISH = 2, - E_META_COOKED_FISH_PUFFERFISH = 3, - - // E_ITEM_MINECART_TRACKS metas: - E_META_TRACKS_X = 1, - E_META_TRACKS_Z = 0, - - // E_ITEM_SPAWN_EGG metas: - // See also cMonster::eType, since monster type and spawn egg meta are the same - E_META_SPAWN_EGG_PICKUP = 1, - E_META_SPAWN_EGG_EXPERIENCE_ORB = 2, - E_META_SPAWN_EGG_LEASH_KNOT = 8, - E_META_SPAWN_EGG_PAINTING = 9, - E_META_SPAWN_EGG_ARROW = 10, - E_META_SPAWN_EGG_SNOWBALL = 11, - E_META_SPAWN_EGG_FIREBALL = 12, - E_META_SPAWN_EGG_SMALL_FIREBALL = 13, - E_META_SPAWN_EGG_ENDER_PEARL = 14, - E_META_SPAWN_EGG_EYE_OF_ENDER = 15, - E_META_SPAWN_EGG_SPLASH_POTION = 16, - E_META_SPAWN_EGG_EXP_BOTTLE = 17, - E_META_SPAWN_EGG_ITEM_FRAME = 18, - E_META_SPAWN_EGG_WITHER_SKULL = 19, - E_META_SPAWN_EGG_PRIMED_TNT = 20, - E_META_SPAWN_EGG_FALLING_BLOCK = 21, - E_META_SPAWN_EGG_FIREWORK = 22, - E_META_SPAWN_EGG_BOAT = 41, - E_META_SPAWN_EGG_MINECART = 42, - E_META_SPAWN_EGG_MINECART_CHEST = 43, - E_META_SPAWN_EGG_MINECART_FURNACE = 44, - E_META_SPAWN_EGG_MINECART_TNT = 45, - E_META_SPAWN_EGG_MINECART_HOPPER = 46, - E_META_SPAWN_EGG_MINECART_SPAWNER = 47, - E_META_SPAWN_EGG_CREEPER = 50, - E_META_SPAWN_EGG_SKELETON = 51, - E_META_SPAWN_EGG_SPIDER = 52, - E_META_SPAWN_EGG_GIANT = 53, - E_META_SPAWN_EGG_ZOMBIE = 54, - E_META_SPAWN_EGG_SLIME = 55, - E_META_SPAWN_EGG_GHAST = 56, - E_META_SPAWN_EGG_ZOMBIE_PIGMAN = 57, - E_META_SPAWN_EGG_ENDERMAN = 58, - E_META_SPAWN_EGG_CAVE_SPIDER = 59, - E_META_SPAWN_EGG_SILVERFISH = 60, - E_META_SPAWN_EGG_BLAZE = 61, - E_META_SPAWN_EGG_MAGMA_CUBE = 62, - E_META_SPAWN_EGG_ENDER_DRAGON = 63, - E_META_SPAWN_EGG_WITHER = 64, - E_META_SPAWN_EGG_BAT = 65, - E_META_SPAWN_EGG_WITCH = 66, - E_META_SPAWN_EGG_PIG = 90, - E_META_SPAWN_EGG_SHEEP = 91, - E_META_SPAWN_EGG_COW = 92, - E_META_SPAWN_EGG_CHICKEN = 93, - E_META_SPAWN_EGG_SQUID = 94, - E_META_SPAWN_EGG_WOLF = 95, - E_META_SPAWN_EGG_MOOSHROOM = 96, - E_META_SPAWN_EGG_SNOW_GOLEM = 97, - E_META_SPAWN_EGG_OCELOT = 98, - E_META_SPAWN_EGG_IRON_GOLEM = 99, - E_META_SPAWN_EGG_HORSE = 100, - E_META_SPAWN_EGG_VILLAGER = 120, - E_META_SPAWN_EGG_ENDER_CRYSTAL = 200, -} ; - - - - - -/// Dimension of a world -enum eDimension -{ - dimNether = -1, - dimOverworld = 0, - dimEnd = 1, -} ; - - - - - -/// Damage type, used in the TakeDamageInfo structure and related functions -enum eDamageType -{ - // Canonical names for the types (as documented in the plugin wiki): - dtAttack, // Being attacked by a mob - dtRangedAttack, // Being attacked by a projectile, possibly from a mob - dtLightning, // Hit by a lightning strike - dtFalling, // Falling down; dealt when hitting the ground - dtDrowning, // Drowning in water / lava - dtSuffocating, // Suffocating inside a block - dtStarving, // Hunger - dtCactusContact, // Contact with a cactus block - dtLavaContact, // Contact with a lava block - dtPoisoning, // Having the poison effect - dtOnFire, // Being on fire - dtFireContact, // Standing inside a fire block - dtInVoid, // Falling into the Void (Y < 0) - dtPotionOfHarming, - dtEnderPearl, // Thrown an ender pearl, teleported by it - dtAdmin, // Damage applied by an admin command - - // Some common synonyms: - dtPawnAttack = dtAttack, - dtEntityAttack = dtAttack, - dtMob = dtAttack, - dtMobAttack = dtAttack, - dtArrowAttack = dtRangedAttack, - dtArrow = dtRangedAttack, - dtProjectile = dtRangedAttack, - dtFall = dtFalling, - dtDrown = dtDrowning, - dtSuffocation = dtSuffocating, - dtStarvation = dtStarving, - dtHunger = dtStarving, - dtCactus = dtCactusContact, - dtCactuses = dtCactusContact, - dtCacti = dtCactusContact, - dtLava = dtLavaContact, - dtPoison = dtPoisoning, - dtBurning = dtOnFire, - dtInFire = dtFireContact, - dtPlugin = dtAdmin, -} ; - - - - - -enum eExplosionSource -{ - esOther, - esPrimedTNT, - esCreeper, - esBed, - esEnderCrystal, - esGhastFireball, - esWitherSkullBlack, - esWitherSkullBlue, - esWitherBirth, - esPlugin -} ; - -// tolua_end - - - - -// fwd: -class cItem; -class cIniFile; - - - - - -// tolua_begin - -/// Translates a blocktype string into blocktype. Takes either a number or an items.ini alias as input. Returns -1 on failure. -extern BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString); - -/// Translates an itemtype string into an item. Takes either a number, number^number, number:number or an items.ini alias as input. Returns true if successful. -extern bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item); - -/// Translates a full item into a string. If the ItemType is not recognized, the ItemType number is output into the string. -extern AString ItemToString(const cItem & a_Item); - -/// Translates itemtype into a string. If the type is not recognized, the itemtype number is output into the string. -extern AString ItemTypeToString(short a_ItemType); - -/// Translates a full item into a fully-specified string (including meta and count). If the ItemType is not recognized, the ItemType number is output into the string. -extern AString ItemToFullString(const cItem & a_Item); - -/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure. -extern EMCSBiome StringToBiome(const AString & a_BiomeString); - -/// Translates a mob string ("ocelot") to mobtype (E_ENTITY_TYPE_OCELOT) -extern int StringToMobType(const AString & a_MobString); - -/// Translates a dimension string to dimension enum. Takes either a number or a dimension alias (built-in). Returns -1000 on failure -extern eDimension StringToDimension(const AString & a_DimensionString); - -/// Translates damage type constant to a string representation (built-in). -extern AString DamageTypeToString(eDamageType a_DamageType); - -/// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure -extern eDamageType StringToDamageType(const AString & a_DamageString); - -/// Returns a cItem representing the item described in an IniFile's value; if the value doesn't exist, creates it with the provided default. -extern cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default); - -// tolua_end - - - - - -// Block properties: -extern NIBBLETYPE g_BlockLightValue[256]; -extern NIBBLETYPE g_BlockSpreadLightFalloff[256]; -extern bool g_BlockTransparent[256]; -extern bool g_BlockOneHitDig[256]; -extern bool g_BlockPistonBreakable[256]; -extern bool g_BlockIsSnowable[256]; -extern bool g_BlockRequiresSpecialTool[256]; -extern bool g_BlockIsSolid[256]; -extern bool g_BlockIsTorchPlaceable[256]; - - - - diff --git a/source/BlockTracer.h b/source/BlockTracer.h deleted file mode 100644 index d0a34811d..000000000 --- a/source/BlockTracer.h +++ /dev/null @@ -1,104 +0,0 @@ - -// BlockTracer.h - -// Declares the classes common for all blocktracers - - - - - -#pragma once - - - - - -// fwd: World.h -class cWorld; - - - - - -class cBlockTracer abstract -{ -public: - /** The callback class is used to notify the caller of individual events that are being traced. - */ - class cCallbacks abstract - { - public: - /** Called on each block encountered along the path, including the first block (path start) - When this callback returns true, the tracing is aborted. - */ - virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) = 0; - - /** Called on each block encountered along the path, including the first block (path start), if chunk data is not loaded - When this callback returns true, the tracing is aborted. - */ - virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) { return false; } - - /** Called when the path goes out of world, either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height) - The coords specify the exact point at which the path exited the world. - If this callback returns true, the tracing is aborted. - Note that some paths can go out of the world and come back again (parabola), - in such a case this callback is followed by OnIntoWorld() and further OnNextBlock() calls - */ - virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; } - - /** Called when the path goes into the world, from either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height) - The coords specify the exact point at which the path entered the world. - If this callback returns true, the tracing is aborted. - Note that some paths can go out of the world and come back again (parabola), - in such a case this callback is followed by further OnNextBlock() calls - */ - virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; } - - /** Called when the path is sure not to hit any more blocks. - Note that for some shapes this might never happen (line with constant Y) - */ - virtual void OnNoMoreHits(void) {} - - /** Called when the block tracing walks into a chunk that is not allocated. - This usually means that the tracing is aborted. - */ - virtual void OnNoChunk(void) {} - } ; - - - /// Creates the BlockTracer parent with the specified callbacks - cBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : - m_World(&a_World), - m_Callbacks(&a_Callbacks) - { - } - - - /// Sets new world, returns the old one. Note that both need to be valid - cWorld & SetWorld(cWorld & a_World) - { - cWorld & Old = *m_World; - m_World = &a_World; - return Old; - } - - - /// Sets new callbacks, returns the old ones. Note that both need to be valid - cCallbacks & SetCallbacks(cCallbacks & a_NewCallbacks) - { - cCallbacks & Old = *m_Callbacks; - m_Callbacks = &a_NewCallbacks; - return Old; - } - -protected: - /// The world upon which to operate - cWorld * m_World; - - /// The callback to use for reporting - cCallbacks * m_Callbacks; -} ; - - - - diff --git a/source/Blocks/BlockBed.cpp b/source/Blocks/BlockBed.cpp deleted file mode 100644 index 66eb9130c..000000000 --- a/source/Blocks/BlockBed.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "Globals.h" -#include "BlockBed.h" - - - - - -void cBlockBedHandler::OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - if (a_BlockMeta < 8) - { - Vector3i Direction = MetaDataToDirection(a_BlockMeta); - a_World->SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8); - } -} - - - - - -void cBlockBedHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ ); - Vector3i Direction = MetaDataToDirection( OldMeta & 0x7 ); - if (OldMeta & 0x8) - { - // Was pillow - if (a_World->GetBlock(ThisPos - Direction) == E_BLOCK_BED) - { - a_World->FastSetBlock(ThisPos - Direction, E_BLOCK_AIR, 0); - } - } - else - { - // Was foot end - if (a_World->GetBlock(ThisPos + Direction) == E_BLOCK_BED) - { - a_World->FastSetBlock(ThisPos + Direction, E_BLOCK_AIR, 0); - } - } -} - - - - - -void cBlockBedHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - if (a_World->GetDimension() != dimOverworld) - { - Vector3i Coords(a_BlockX, a_BlockY, a_BlockZ); - a_World->DoExplosionAt(5, a_BlockX, a_BlockY, a_BlockZ, true, esBed, &Coords); - } - else - { - if (a_World->GetTimeOfDay() > 13000) - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (Meta & 0x8) - { - // Is pillow - a_World->BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ); - } - else - { - // Is foot end - Vector3i Direction = MetaDataToDirection( Meta & 0x7 ); - if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping - { - a_World->BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z); - } - } - } else { - a_Player->SendMessage("You can only sleep at night"); - } - } -} - - - - diff --git a/source/Blocks/BlockBed.h b/source/Blocks/BlockBed.h deleted file mode 100644 index 8a289b22c..000000000 --- a/source/Blocks/BlockBed.h +++ /dev/null @@ -1,67 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -class cBlockBedHandler : - public cBlockHandler -{ -public: - cBlockBedHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to zero - a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0)); - } - - - // Bed specific helper functions - static NIBBLETYPE RotationToMetaData(double a_Rotation) - { - a_Rotation += 180 + (180 / 4); // So its not aligned with axis - if (a_Rotation > 360) a_Rotation -= 360; - - a_Rotation = (a_Rotation / 360) * 4; - - return ((char)a_Rotation + 2) % 4; - } - - - static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData) - { - switch (a_MetaData) - { - case 0: return Vector3i(0, 0, 1); - case 1: return Vector3i(-1, 0, 0); - case 2: return Vector3i(0, 0, -1); - case 3: return Vector3i(1, 0, 0); - } - return Vector3i(); - } -} ; - - - - diff --git a/source/Blocks/BlockBrewingStand.h b/source/Blocks/BlockBrewingStand.h deleted file mode 100644 index 57642bcb6..000000000 --- a/source/Blocks/BlockBrewingStand.h +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockBrewingStandHandler : - public cBlockHandler -{ -public: - cBlockBrewingStandHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_BREWING_STAND, 1, 0)); - } - - virtual bool IsUseable() override - { - return true; - } -} ; - - - - diff --git a/source/Blocks/BlockButton.cpp b/source/Blocks/BlockButton.cpp deleted file mode 100644 index 1011f9351..000000000 --- a/source/Blocks/BlockButton.cpp +++ /dev/null @@ -1,39 +0,0 @@ - -#include "Globals.h" -#include "BlockButton.h" - - - - - -cBlockButtonHandler::cBlockButtonHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockButtonHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off. - NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); - - if (Meta & 0x08) - { - a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); - } - else - { - a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.5f); - } - - // Queue a button reset (unpress), with a GetBlock to prevent duplication of buttons (press, break, wait for reset) - a_World->QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f), m_BlockType == E_BLOCK_STONE_BUTTON ? 20 : 25); -} - - - - diff --git a/source/Blocks/BlockButton.h b/source/Blocks/BlockButton.h deleted file mode 100644 index e3f655bfa..000000000 --- a/source/Blocks/BlockButton.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockButtonHandler : - public cBlockHandler -{ -public: - cBlockButtonHandler(BLOCKTYPE a_BlockType); - - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(m_BlockType == E_BLOCK_WOODEN_BUTTON ? E_BLOCK_WOODEN_BUTTON : E_BLOCK_STONE_BUTTON, 1, 0)); - } - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = BlockFaceToMetaData(a_BlockFace); - return true; - } - - - virtual const char * GetStepSound(void) override - { - return m_BlockType == E_BLOCK_WOODEN_BUTTON ? "step.wood" : "step.stone"; - } - - - inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace) - { - switch (a_BlockFace) - { - case BLOCK_FACE_ZP: { return 0x4; } - case BLOCK_FACE_ZM: { return 0x3; } - case BLOCK_FACE_XP: { return 0x2; } - case BLOCK_FACE_XM: { return 0x1; } - default: - { - ASSERT(!"Unhandled block face!"); - return 0x0; // No idea, give a special meta (button in centre of block) - } - } - } -} ; - - - - diff --git a/source/Blocks/BlockCactus.h b/source/Blocks/BlockCactus.h deleted file mode 100644 index 4147ad473..000000000 --- a/source/Blocks/BlockCactus.h +++ /dev/null @@ -1,82 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockCactusHandler : - public cBlockHandler -{ -public: - cBlockCactusHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(m_BlockType, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - if (a_RelY <= 0) - { - return false; - } - BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); - if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS)) - { - // Cactus can only be placed on sand and itself - return false; - } - - // Check surroundings. Cacti may ONLY be surrounded by air - static const struct - { - int x, z; - } Coords[] = - { - {-1, 0}, - { 1, 0}, - { 0, -1}, - { 0, 1}, - } ; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if ( - a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && - (BlockType != E_BLOCK_AIR) - ) - { - return false; - } - } // for i - Coords[] - - return true; - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - a_World->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1); - } - - - virtual const char * GetStepSound(void) override - { - return "step.cloth"; - } -} ; - - - - diff --git a/source/Blocks/BlockCarpet.h b/source/Blocks/BlockCarpet.h deleted file mode 100644 index 5eafd8c21..000000000 --- a/source/Blocks/BlockCarpet.h +++ /dev/null @@ -1,60 +0,0 @@ - -// BlockCarpet.h - -// Declares the cBlockCarpetHandler class representing the handler for the carpet block - - - - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockCarpetHandler : - public cBlockHandler -{ -public: - cBlockCarpetHandler(BLOCKTYPE a_BlockType) : - cBlockHandler(a_BlockType) - { - } - - - virtual const char * GetStepSound(void) override - { - return "step.cloth"; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = a_Player->GetEquippedItem().m_ItemDamage & 0x0f; - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_CARPET, 1, a_BlockMeta)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR); - } -} ; - - - - diff --git a/source/Blocks/BlockCauldron.h b/source/Blocks/BlockCauldron.h deleted file mode 100644 index b0e00f869..000000000 --- a/source/Blocks/BlockCauldron.h +++ /dev/null @@ -1,59 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockCauldronHandler : - public cBlockHandler -{ -public: - cBlockCauldronHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_CAULDRON, 1, 0)); - } - - void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) - { - char Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); - switch( a_Player->GetEquippedItem().m_ItemType ) - { - case E_ITEM_WATER_BUCKET: - { - a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 ); - a_Player->GetInventory().RemoveOneEquippedItem(); - cItem NewItem(E_ITEM_BUCKET, 1); - a_Player->GetInventory().AddItem(NewItem); - break; - } - case E_ITEM_GLASS_BOTTLE: - { - if( Meta > 0 ) - { - a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta); - a_Player->GetInventory().RemoveOneEquippedItem(); - cItem NewItem(E_ITEM_POTIONS, 1, 0); - a_Player->GetInventory().AddItem(NewItem); - } - break; - } - } - } - - virtual bool IsUseable() override - { - return true; - } -} ; - - - - diff --git a/source/Blocks/BlockChest.h b/source/Blocks/BlockChest.h deleted file mode 100644 index 488c58ac5..000000000 --- a/source/Blocks/BlockChest.h +++ /dev/null @@ -1,223 +0,0 @@ - -#pragma once - -#include "BlockEntity.h" -#include "../World.h" -#include "../BlockArea.h" -#include "../Entities/Player.h" - - - - - -class cBlockChestHandler : - public cBlockEntityHandler -{ -public: - cBlockChestHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - - // Is there a doublechest already next to this block? - if (!CanBeAt(a_World, a_BlockX, a_BlockY, a_BlockZ)) - { - // Yup, cannot form a triple-chest, refuse: - return false; - } - - // Check if this forms a doublechest, if so, need to adjust the meta: - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) - { - return false; - } - double rot = a_Player->GetRotation(); - if ( - (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) - ) - { - a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; - return true; - } - if ( - (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) - ) - { - a_BlockMeta = (rot < 0) ? 4 : 5; - return true; - } - - // Single chest, get meta from rotation only - a_BlockMeta = RotationToMetaData(rot); - return true; - } - - - virtual void OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - // Check if this forms a doublechest, if so, need to adjust the meta: - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) - { - return; - } - - double rot = a_Player->GetRotation(); - // Choose meta from player rotation, choose only between 2 or 3 - NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; - if ( - CheckAndAdjustNeighbor(a_World, Area, 0, 1, NewMeta) || - CheckAndAdjustNeighbor(a_World, Area, 2, 1, NewMeta) - ) - { - // Forming a double chest in the X direction - return; - } - // Choose meta from player rotation, choose only between 4 or 5 - NewMeta = (rot < 0) ? 4 : 5; - if ( - CheckAndAdjustNeighbor(a_World, Area, 1, 0, NewMeta) || - CheckAndAdjustNeighbor(a_World, Area, 2, 2, NewMeta) - ) - { - // Forming a double chest in the Z direction - return; - } - - // Single chest, no further processing needed - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } - - - virtual bool CanBeAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 2, a_BlockX + 2, a_BlockY, a_BlockY, a_BlockZ - 2, a_BlockZ + 2)) - { - // Cannot read the surroundings, probably at the edge of loaded chunks. Disallow. - return false; - } - - int NumChestNeighbors = 0; - if (Area.GetRelBlockType(1, 0, 2) == E_BLOCK_CHEST) - { - if ( - (Area.GetRelBlockType(0, 0, 2) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) - ) - { - // Already a doublechest neighbor, disallow: - return false; - } - NumChestNeighbors += 1; - } - if (Area.GetRelBlockType(3, 0, 2) == E_BLOCK_CHEST) - { - if ( - (Area.GetRelBlockType(4, 0, 2) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) - ) - { - // Already a doublechest neighbor, disallow: - return false; - } - NumChestNeighbors += 1; - } - if (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) - { - if ( - (Area.GetRelBlockType(2, 0, 0) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) - ) - { - // Already a doublechest neighbor, disallow: - return false; - } - NumChestNeighbors += 1; - } - if (Area.GetRelBlockType(2, 0, 3) == E_BLOCK_CHEST) - { - if ( - (Area.GetRelBlockType(2, 0, 4) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) - ) - { - // Already a doublechest neighbor, disallow: - return false; - } - NumChestNeighbors += 1; - } - return (NumChestNeighbors < 2); - } - - - /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only - static NIBBLETYPE RotationToMetaData(double a_Rotation) - { - a_Rotation += 90 + 45; // So its not aligned with axis - - if (a_Rotation > 360.f) - { - a_Rotation -= 360.f; - } - if ((a_Rotation >= 0.f) && (a_Rotation < 90.f)) - { - return 0x4; - } - else if ((a_Rotation >= 180) && (a_Rotation < 270)) - { - return 0x5; - } - else if ((a_Rotation >= 90) && (a_Rotation < 180)) - { - return 0x2; - } - else - { - return 0x3; - } - } - - - /// If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true. - bool CheckAndAdjustNeighbor(cWorld * a_World, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta) - { - if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != E_BLOCK_CHEST) - { - return false; - } - a_World->SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta); - return true; - } -} ; - - - - diff --git a/source/Blocks/BlockCloth.h b/source/Blocks/BlockCloth.h deleted file mode 100644 index a136d3b9d..000000000 --- a/source/Blocks/BlockCloth.h +++ /dev/null @@ -1,34 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockClothHandler : - public cBlockHandler -{ -public: - cBlockClothHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_WOOL, 1, a_BlockMeta)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.cloth"; - } -} ; - - - - diff --git a/source/Blocks/BlockCobWeb.h b/source/Blocks/BlockCobWeb.h deleted file mode 100644 index 982bfaa30..000000000 --- a/source/Blocks/BlockCobWeb.h +++ /dev/null @@ -1,30 +0,0 @@ - -// BlockCobWeb.h - -// Declares the cBlockCobWebHandler object representing the BlockHandler for cobwebs - -#pragma once - - - - - -class cBlockCobWebHandler : - public cBlockHandler -{ -public: - cBlockCobWebHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override - { - a_Pickups.push_back(cItem(E_ITEM_STRING, 1, 0)); - } -} ; - - - - diff --git a/source/Blocks/BlockComparator.cpp b/source/Blocks/BlockComparator.cpp deleted file mode 100644 index b4e5a55d0..000000000 --- a/source/Blocks/BlockComparator.cpp +++ /dev/null @@ -1,53 +0,0 @@ - -#include "Globals.h" -#include "BlockComparator.h" -#include "../Simulator/RedstoneSimulator.h" -#include "../Entities/Player.h" - - - - - -cBlockComparatorHandler::cBlockComparatorHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockComparatorHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Nothing needed yet -} - - - - - -void cBlockComparatorHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - Meta ^= 0x04; // Toggle 3rd (addition/subtraction) bit with XOR - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); -} - - - - -bool cBlockComparatorHandler::GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta -) -{ - a_BlockType = m_BlockType; - a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); - return true; -} - - - - diff --git a/source/Blocks/BlockComparator.h b/source/Blocks/BlockComparator.h deleted file mode 100644 index cb2941d3c..000000000 --- a/source/Blocks/BlockComparator.h +++ /dev/null @@ -1,55 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockComparatorHandler : - public cBlockHandler -{ -public: - cBlockComparatorHandler(BLOCKTYPE a_BlockType); - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(E_ITEM_COMPARATOR, 1, 0)); - } - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override; - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/Blocks/BlockCrops.h b/source/Blocks/BlockCrops.h deleted file mode 100644 index 9dd65aae2..000000000 --- a/source/Blocks/BlockCrops.h +++ /dev/null @@ -1,114 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -/// Common class that takes care of carrots, potatoes and wheat -class cBlockCropsHandler : - public cBlockHandler -{ -public: - cBlockCropsHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override - { - MTRand rand; - - if (a_Meta == 0x7) - { - // Is fully grown, drop the entire produce: - switch (m_BlockType) - { - case E_BLOCK_CROPS: - { - a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0)); - a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 - break; - } - case E_BLOCK_CARROTS: - { - a_Pickups.push_back(cItem(E_ITEM_CARROT, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 - break; - } - case E_BLOCK_POTATOES: - { - a_Pickups.push_back(cItem(E_ITEM_POTATO, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 - if (rand.randInt(20) == 0) - { - // With a 5% chance, drop a poisonous potato as well - a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0)); - } - break; - } - default: - { - ASSERT(!"Unhandled block type"); - break; - } - } // switch (m_BlockType) - } - else - { - // Drop 1 item of whatever is growing - switch (m_BlockType) - { - case E_BLOCK_CROPS: a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); break; - case E_BLOCK_CARROTS: a_Pickups.push_back(cItem(E_ITEM_CARROT, 1, 0)); break; - case E_BLOCK_POTATOES: a_Pickups.push_back(cItem(E_ITEM_POTATO, 1, 0)); break; - default: - { - ASSERT(!"Unhandled block type"); - break; - } - } - } - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - NIBBLETYPE Light = a_World->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); - NIBBLETYPE SkyLight = a_World->GetBlockSkyLight(a_BlockX, a_BlockY, a_BlockZ); - - if (SkyLight > Light) - { - Light = SkyLight; - } - - if ((Meta < 7) && (Light > 8)) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta); - } - else if (Light < 9) - { - a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); - } - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - diff --git a/source/Blocks/BlockDeadBush.h b/source/Blocks/BlockDeadBush.h deleted file mode 100644 index 14617d006..000000000 --- a/source/Blocks/BlockDeadBush.h +++ /dev/null @@ -1,35 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockDeadBushHandler : - public cBlockHandler -{ -public: - cBlockDeadBushHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Don't drop anything - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_SAND); - } -} ; - - - - diff --git a/source/Blocks/BlockDirt.h b/source/Blocks/BlockDirt.h deleted file mode 100644 index c694d79f6..000000000 --- a/source/Blocks/BlockDirt.h +++ /dev/null @@ -1,88 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -/// Handler used for both dirt and grass -class cBlockDirtHandler : - public cBlockHandler -{ -public: - cBlockDirtHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); - } - - - virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - if (m_BlockType != E_BLOCK_GRASS) - { - return; - } - - // Grass becomes dirt if there is something on top of it: - if (a_BlockY < cChunkDef::Height - 1) - { - BLOCKTYPE Above = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); - if ((!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above]) || IsBlockWater(Above)) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); - return; - } - } - - // Grass spreads to adjacent blocks: - MTRand rand; - for (int i = 0; i < 2; i++) // Pick two blocks to grow to - { - int OfsX = rand.randInt(2) - 1; // [-1 .. 1] - int OfsY = rand.randInt(4) - 3; // [-3 .. 1] - int OfsZ = rand.randInt(2) - 1; // [-1 .. 1] - - BLOCKTYPE DestBlock; - NIBBLETYPE DestMeta; - if ((a_BlockY + OfsY < 0) || (a_BlockY + OfsY >= cChunkDef::Height - 1)) - { - // Y Coord out of range - continue; - } - bool IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, DestBlock, DestMeta); - if (!IsValid || (DestBlock != E_BLOCK_DIRT)) - { - continue; - } - - BLOCKTYPE AboveDest; - NIBBLETYPE AboveMeta; - IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, AboveDest, AboveMeta); - ASSERT(IsValid); // WTF - how did we get the DestBlock if AboveBlock is not valid? - if ((g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest]) && !IsBlockWater(AboveDest)) - { - a_World->FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, E_BLOCK_GRASS, 0); - } - } // for i - repeat twice - } - - - virtual const char * GetStepSound(void) override - { - return "step.gravel"; - } -} ; - - - - diff --git a/source/Blocks/BlockDoor.cpp b/source/Blocks/BlockDoor.cpp deleted file mode 100644 index e71ccd368..000000000 --- a/source/Blocks/BlockDoor.cpp +++ /dev/null @@ -1,90 +0,0 @@ - -#include "Globals.h" -#include "BlockDoor.h" -#include "../Item.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - if (OldMeta & 8) - { - // Was upper part of door - if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) - { - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - } - } - else - { - // Was lower part - if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) - { - a_World->FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0); - } - } -} - - - - - -void cBlockDoorHandler::OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) - { - ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ); - } -} - - - - - -void cBlockDoorHandler::OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - NIBBLETYPE a_TopBlockMeta = 8; - if ( - (a_BlockMeta == 0) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType) || - (a_BlockMeta == 1) && (a_World->GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType) || - (a_BlockMeta == 2) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType) || - (a_BlockMeta == 3) && (a_World->GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType) - ) - { - a_TopBlockMeta = 9; - } - a_World->SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta); -} - - - - - -const char * cBlockDoorHandler::GetStepSound(void) -{ - return (m_BlockType == E_BLOCK_WOODEN_DOOR) ? "step.wood" : "step.stone"; -} - - - - diff --git a/source/Blocks/BlockDoor.h b/source/Blocks/BlockDoor.h deleted file mode 100644 index 03a79d47d..000000000 --- a/source/Blocks/BlockDoor.h +++ /dev/null @@ -1,175 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -class cBlockDoorHandler : - public cBlockHandler -{ -public: - cBlockDoorHandler(BLOCKTYPE a_BlockType); - - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - virtual const char * GetStepSound(void) override; - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - // If clicking a bottom face, place the door one block lower: - if (a_BlockFace == BLOCK_FACE_BOTTOM) - { - a_BlockY--; - } - - if ( - !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) || - !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) - ) - { - return false; - } - - a_BlockType = m_BlockType; - a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem((m_BlockType == E_BLOCK_WOODEN_DOOR) ? E_ITEM_WOODEN_DOOR : E_ITEM_IRON_DOOR, 1, 0)); - } - - - virtual void OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override; - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); - } - - - bool CanReplaceBlock(BLOCKTYPE a_BlockType) - { - switch (a_BlockType) - { - case E_BLOCK_AIR: - case E_BLOCK_TALL_GRASS: - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - case E_BLOCK_SNOW: - case E_BLOCK_FIRE: - { - return true; - } - } - return false; - } - - - /// Converts the player's yaw to placed door's blockmeta - inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) - { - ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); - - a_Yaw += 90 + 45; - if (a_Yaw > 360) - { - a_Yaw -= 360; - } - if ((a_Yaw >= 0) && (a_Yaw < 90)) - { - return 0x0; - } - else if ((a_Yaw >= 180) && (a_Yaw < 270)) - { - return 0x2; - } - else if ((a_Yaw >= 90) && (a_Yaw < 180)) - { - return 0x1; - } - else - { - return 0x3; - } - } - - - /// Returns true if the specified blocktype is any kind of door - inline static bool IsDoor(BLOCKTYPE a_Block) - { - return (a_Block == E_BLOCK_WOODEN_DOOR) || (a_Block == E_BLOCK_IRON_DOOR); - } - - - /// Returns the metadata for the opposite door state (open vs closed) - static NIBBLETYPE ChangeStateMetaData(NIBBLETYPE a_MetaData) - { - return a_MetaData ^ 4; - } - - - /// Changes the door at the specified coords from open to close or vice versa - static void ChangeDoor(cWorld * a_World, int a_X, int a_Y, int a_Z) - { - NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z); - - a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData)); - - if (OldMetaData & 8) - { - // Current block is top of the door - BLOCKTYPE BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z); - NIBBLETYPE BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z); - - if (IsDoor(BottomBlock) && !(BottomMeta & 8)) - { - a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData(BottomMeta)); - } - } - else - { - // Current block is bottom of the door - BLOCKTYPE TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z); - NIBBLETYPE TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z); - - if (IsDoor(TopBlock) && (TopMeta & 8)) - { - a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta)); - } - } - } - - -} ; - - - - diff --git a/source/Blocks/BlockDropSpenser.h b/source/Blocks/BlockDropSpenser.h deleted file mode 100644 index b7f20825d..000000000 --- a/source/Blocks/BlockDropSpenser.h +++ /dev/null @@ -1,41 +0,0 @@ - -// BlockDropSpenser.h - -// Declares the cBlockDropSpenserHandler class representing the BlockHandler for Dropper and Dispenser blocks - -#pragma once - -#include "../Piston.h" - - - - - -class cBlockDropSpenserHandler : - public cBlockEntityHandler -{ -public: - cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) : - cBlockEntityHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - - // FIXME: Do not use cPiston class for dispenser placement! - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); - return true; - } -} ; - - - - diff --git a/source/Blocks/BlockEnderchest.h b/source/Blocks/BlockEnderchest.h deleted file mode 100644 index 0ce813f1c..000000000 --- a/source/Blocks/BlockEnderchest.h +++ /dev/null @@ -1,28 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockEnderchestHandler : - public cBlockHandler -{ -public: - cBlockEnderchestHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - //todo: Drop Ender Chest if using silk touch pickaxe - a_Pickups.push_back(cItem(E_BLOCK_OBSIDIAN, 8, 0)); - } -} ; - - - - diff --git a/source/Blocks/BlockEntity.h b/source/Blocks/BlockEntity.h deleted file mode 100644 index 9c6b23665..000000000 --- a/source/Blocks/BlockEntity.h +++ /dev/null @@ -1,31 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockEntityHandler : public cBlockHandler -{ -public: - cBlockEntityHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void OnUse(cWorld * a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override - { - a_World->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); - } - - virtual bool IsUseable() override - { - return true; - } -}; - - - - diff --git a/source/Blocks/BlockFarmland.h b/source/Blocks/BlockFarmland.h deleted file mode 100644 index 7bc71f7f3..000000000 --- a/source/Blocks/BlockFarmland.h +++ /dev/null @@ -1,107 +0,0 @@ - -// BlockFarmland.h - -// Declares the cBlcokFarmlandHandler representing the block handler for farmland - - - - - -#pragma once - -#include "BlockHandler.h" -#include "../BlockArea.h" - - - - - -class cBlockFarmlandHandler : - public cBlockHandler -{ - typedef cBlockHandler super; - -public: - cBlockFarmlandHandler(void) : - super(E_BLOCK_FARMLAND) - { - } - - - virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - bool Found = false; - - int Biome = a_World->GetBiomeAt(a_BlockX, a_BlockZ); - if (a_World->IsWeatherWet() && (Biome != biDesert) && (Biome != biDesertHills)) - { - // Rain hydrates farmland, too, except in Desert biomes. - Found = true; - } - else - { - // Search for water in a close proximity: - // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4)) - { - // Too close to the world edge, cannot check surroudnings; don't tick at all - return; - } - - int NumBlocks = Area.GetBlockCount(); - BLOCKTYPE * BlockTypes = Area.GetBlockTypes(); - for (int i = 0; i < NumBlocks; i++) - { - if ( - (BlockTypes[i] == E_BLOCK_WATER) || - (BlockTypes[i] == E_BLOCK_STATIONARY_WATER) - ) - { - Found = true; - break; - } - } // for i - BlockTypes[] - } - - NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - if (Found) - { - // Water was found, hydrate the block until hydration reaches 7: - if (BlockMeta < 7) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ++BlockMeta); - } - return; - } - - // Water wasn't found, de-hydrate block: - if (BlockMeta > 0) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, --BlockMeta); - return; - } - - // Farmland too dry. If nothing is growing on top, turn back to dirt: - switch (a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) - { - case E_BLOCK_CROPS: - case E_BLOCK_MELON_STEM: - case E_BLOCK_PUMPKIN_STEM: - { - // Produce on top, don't revert - break; - } - default: - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); - break; - } - } - } -} ; - - - - diff --git a/source/Blocks/BlockFenceGate.h b/source/Blocks/BlockFenceGate.h deleted file mode 100644 index 6423a7cb0..000000000 --- a/source/Blocks/BlockFenceGate.h +++ /dev/null @@ -1,88 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockFenceGateHandler : - public cBlockHandler -{ -public: - cBlockFenceGateHandler(BLOCKTYPE a_BlockType) : - cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); - return true; - } - - - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override - { - NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - NIBBLETYPE NewMetaData = PlayerYawToMetaData(a_Player->GetRotation()); - OldMetaData ^= 4; // Toggle the gate - if ((OldMetaData & 1) == (NewMetaData & 1)) - { - // Standing in front of the gate - apply new direction - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (OldMetaData & 4) | (NewMetaData & 3)); - } - else - { - // Standing aside - use last direction - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData); - } - } - - - virtual bool IsUseable(void) override - { - return true; - } - - - /// Converts the player's yaw to placed gate's blockmeta - inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) - { - ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); - - a_Yaw += 360 + 45; - if (a_Yaw > 360) - { - a_Yaw -= 360; - } - if ((a_Yaw >= 0) && (a_Yaw < 90)) - { - return 0x0; - } - else if ((a_Yaw >= 180) && (a_Yaw < 270)) - { - return 0x2; - } - else if ((a_Yaw >= 90) && (a_Yaw < 180)) - { - return 0x1; - } - else - { - return 0x3; - } - } -} ; - - - - diff --git a/source/Blocks/BlockFire.h b/source/Blocks/BlockFire.h deleted file mode 100644 index 46b56d7e0..000000000 --- a/source/Blocks/BlockFire.h +++ /dev/null @@ -1,228 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockFireHandler : - public cBlockHandler -{ -public: - cBlockFireHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - /// Portal boundary and direction variables - int XZP, XZM, Dir; // For wont of a better name... - - virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override - { - /* - PORTAL FINDING ALGORITH - ======================= - -Get clicked base block - -Trace upwards to find first obsidian block; aborts if anything other than obsidian or air is encountered. - Uses this value as a reference (the 'ceiling') - -For both directions (if one fails, try the other), BASE (clicked) block: - -Go in one direction, only stop if a non obsidian block is encountered (abort) OR a portal border is encountered (FindObsidianCeiling returns -1) - -If a border was encountered, go the other direction and repeat above - -Write borders to XZP and XZM, write direction portal faces to Dir - -Loop through boundary variables, and fill with portal blocks based on Dir with meta from Dir - */ - - a_BlockY--; // Because we want the block below the fire - FindAndSetPortalFrame(a_BlockX, a_BlockY, a_BlockZ, a_World); // Brought to you by Aperture Science - } - - virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // No pickups from this block - } - - virtual bool IsClickedThrough(void) override - { - return true; - } - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } - - /// Traces along YP until it finds an obsidian block, returns Y difference or 0 if no portal, and -1 for border - /// Takes the X, Y, and Z of the base block; with an optional MaxY for portal border finding - int FindObsidianCeiling(int X, int Y, int Z, cWorld * a_World, int MaxY = 0) - { - if (a_World->GetBlock(X, Y, Z) != E_BLOCK_OBSIDIAN) - { - return 0; - } - - int newY = Y + 1; - - for (newY; newY < cChunkDef::Height; newY++) - { - BLOCKTYPE Block = a_World->GetBlock(X, newY, Z); - if ((Block == E_BLOCK_AIR) || (Block == E_BLOCK_FIRE)) - { - continue; - } - else if (Block == E_BLOCK_OBSIDIAN) - { - // We found an obsidian ceiling - // Make sure MaxY has a value and newY ('ceiling' location) is at one above the base block - // This is because the frame is a solid obsidian pillar - if ((MaxY != 0) && (newY == Y + 1)) - { - return EvaluatePortalBorder(X, newY, Z, MaxY, a_World); - } - else - { - // Return ceiling Y, whoever called this function will decide if it's part of a portal or not - return newY; - } - } - else { return 0; } - } - - return 0; - } - - /// Evaluates if coords have a valid border on top, based on MaxY - int EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cWorld * a_World) - { - for (int checkBorder = FoundObsidianY + 1; checkBorder <= MaxY - 1; checkBorder++) // FoundObsidianY + 1: FoundObsidianY has already been checked in FindObsidianCeiling; MaxY - 1: portal doesn't need corners - { - if (a_World->GetBlock(X, checkBorder, Z) != E_BLOCK_OBSIDIAN) - { - // Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal - return 0; - } - } - // Everything was obsidian, found a border! - return -1; // Return -1 for a frame border - } - - /// Finds entire frame in any direction with the coordinates of a base block and fills hole with nether portal (START HERE) - void FindAndSetPortalFrame(int X, int Y, int Z, cWorld * a_World) - { - int MaxY = FindObsidianCeiling(X, Y, Z, a_World); // Get topmost obsidian block as reference for all other checks - int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add/subtract one as we've checked the original already the line above - - if (MaxY == 0) // Oh noes! Not a portal coordinate :( - { - return; - } - - if (!FindPortalSliceX(X1, X2, Y, Z, MaxY, a_World)) - { - if (!FindPortalSliceZ(X, Y, Z1, Z2, MaxY, a_World)) - { - return; // No eligible portal construct, abort abort abort!! - } - } - - for (int Height = Y + 1; Height <= MaxY - 1; Height++) // Loop through boundary to set portal blocks - { - for (int Width = XZM; Width <= XZP; Width++) - { - if (Dir == 1) - { - a_World->SetBlock(Width, Height, Z, E_BLOCK_NETHER_PORTAL, Dir); - } - else - { - a_World->SetBlock(X, Height, Width, E_BLOCK_NETHER_PORTAL, Dir); - } - } - } - - return; - } - - /// Evaluates if coordinates are a portal going XP/XM; returns true if so, and writes boundaries to variable - /// Takes coordinates of base block and Y coord of target obsidian ceiling - bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cWorld * a_World) - { - Dir = 1; // Set assumed direction (will change if portal turns out to be facing the other direction) - bool FoundFrameXP = false, FoundFrameXM = false; - for (X1; ((a_World->GetBlock(X1, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X1, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X1++) // Check XP for obsidian blocks, exempting corners - { - int Value = FindObsidianCeiling(X1, Y, Z, a_World, MaxY); - int ValueTwo = FindObsidianCeiling(X1, Y + 1, Z, a_World, MaxY); // For corners without obsidian - if ((Value == -1) || (ValueTwo == -1)) // FindObsidianCeiling returns -1 upon frame-find - { - FoundFrameXP = true; // Found a frame border in this direction, proceed in other direction (don't go further) - break; - } - else if ((Value != MaxY) && (ValueTwo != MaxY)) // Make sure that there is a valid portal 'slice' - { - return false; // Not valid slice, no portal can be formed - } - } XZP = X1 - 1; // Set boundary of frame interior, note that for some reason, the loop of X and the loop of Z go to different numbers, hence -1 here and -2 there - for (X2; ((a_World->GetBlock(X2, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X2, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM) - { - int Value = FindObsidianCeiling(X2, Y, Z, a_World, MaxY); - int ValueTwo = FindObsidianCeiling(X2, Y + 1, Z, a_World, MaxY); - if ((Value == -1) || (ValueTwo == -1)) - { - FoundFrameXM = true; - break; - } - else if ((Value != MaxY) && (ValueTwo != MaxY)) - { - return false; - } - } XZM = X2 + 1; // Set boundary, see previous - return (FoundFrameXP && FoundFrameXM); - } - - /// Evaluates if coords are a portal going ZP/ZM; returns true if so, and writes boundaries to variable - bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cWorld * a_World) - { - Dir = 2; - bool FoundFrameZP = false, FoundFrameZM = false; - for (Z1; ((a_World->GetBlock(X, Y, Z1) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z1) == E_BLOCK_OBSIDIAN)); Z1++) - { - int Value = FindObsidianCeiling(X, Y, Z1, a_World, MaxY); - int ValueTwo = FindObsidianCeiling(X, Y + 1, Z1, a_World, MaxY); - if ((Value == -1) || (ValueTwo == -1)) - { - FoundFrameZP = true; - continue; - } - else if ((Value != MaxY) && (ValueTwo != MaxY)) - { - return false; - } - } XZP = Z1 - 2; - for (Z2; ((a_World->GetBlock(X, Y, Z2) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z2) == E_BLOCK_OBSIDIAN)); Z2--) - { - int Value = FindObsidianCeiling(X, Y, Z2, a_World, MaxY); - int ValueTwo = FindObsidianCeiling(X, Y + 1, Z2, a_World, MaxY); - if ((Value == -1) || (ValueTwo == -1)) - { - FoundFrameZM = true; - continue; - } - else if ((Value != MaxY) && (ValueTwo != MaxY)) - { - return false; - } - } XZM = Z2 + 2; - return (FoundFrameZP && FoundFrameZM); - } -}; - - - - diff --git a/source/Blocks/BlockFlower.h b/source/Blocks/BlockFlower.h deleted file mode 100644 index 421e2d5d8..000000000 --- a/source/Blocks/BlockFlower.h +++ /dev/null @@ -1,41 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockFlowerHandler : - public cBlockHandler -{ -public: - cBlockFlowerHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(m_BlockType, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - diff --git a/source/Blocks/BlockFlowerPot.h b/source/Blocks/BlockFlowerPot.h deleted file mode 100644 index b0faf5218..000000000 --- a/source/Blocks/BlockFlowerPot.h +++ /dev/null @@ -1,105 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockFlowerPotHandler : - public cBlockHandler -{ -public: - cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) : - cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0)); - if (a_BlockMeta == 0) - { - return; - } - cItem Plant; - switch (a_BlockMeta) - { - case 1: Plant = cItem(E_BLOCK_RED_ROSE, 1, 0); break; - case 2: Plant = cItem(E_BLOCK_YELLOW_FLOWER, 1, 0); break; - case 3: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_APPLE); break; - case 4: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_CONIFER); break; - case 5: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_BIRCH); break; - case 6: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_JUNGLE); break; - case 7: Plant = cItem(E_BLOCK_RED_MUSHROOM, 1, 0); break; - case 8: Plant = cItem(E_BLOCK_BROWN_MUSHROOM, 1, 0); break; - case 9: Plant = cItem(E_BLOCK_CACTUS, 1, 0); break; - case 10: Plant = cItem(E_BLOCK_DEAD_BUSH, 1, 0); break; - case 11: Plant = cItem(E_BLOCK_TALL_GRASS, 1, E_META_TALL_GRASS_FERN); break; - default: return; - } - a_Pickups.push_back(Plant); - } - - - void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) - { - NIBBLETYPE Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); - if (Meta != 0) - { - // Already filled - return; - } - - switch (a_Player->GetEquippedItem().m_ItemType) - { - case E_BLOCK_RED_ROSE: Meta = 1; break; - case E_BLOCK_YELLOW_FLOWER: Meta = 2; break; - case E_BLOCK_SAPLING: - { - switch (a_Player->GetEquippedItem().m_ItemDamage) - { - case E_META_SAPLING_APPLE: Meta = 3; break; - case E_META_SAPLING_CONIFER: Meta = 4; break; - case E_META_SAPLING_BIRCH: Meta = 5; break; - case E_META_SAPLING_JUNGLE: Meta = 6; break; - } - break; - } - case E_BLOCK_RED_MUSHROOM: Meta = 7; break; - case E_BLOCK_BROWN_MUSHROOM: Meta = 8; break; - case E_BLOCK_CACTUS: Meta = 9; break; - case E_BLOCK_DEAD_BUSH: Meta = 10; break; - case E_BLOCK_TALL_GRASS: - { - if (a_Player->GetEquippedItem().m_ItemDamage == E_META_TALL_GRASS_FERN) - { - Meta = 11; - } - else - { - return; - } - break; - } - } - - if (a_Player->GetGameMode() != gmCreative) - { - a_Player->GetInventory().RemoveOneEquippedItem(); - } - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); - } - - - virtual bool IsUseable(void) override - { - return true; - } -} ; - - - - diff --git a/source/Blocks/BlockFluid.h b/source/Blocks/BlockFluid.h deleted file mode 100644 index 0db2f60c4..000000000 --- a/source/Blocks/BlockFluid.h +++ /dev/null @@ -1,56 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockFluidHandler : - public cBlockHandler -{ - typedef cBlockHandler super; - -public: - cBlockFluidHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // No pickups - } - - - virtual bool DoesIgnoreBuildCollision(void) override - { - return true; - } - - - virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override - { - switch (m_BlockType) - { - case E_BLOCK_STATIONARY_LAVA: - { - a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_LAVA, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); - break; - } - case E_BLOCK_STATIONARY_WATER: - { - a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_WATER, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); - break; - } - } - super::Check(a_RelX, a_RelY, a_RelZ, a_Chunk); - } -} ; - - - - diff --git a/source/Blocks/BlockFurnace.h b/source/Blocks/BlockFurnace.h deleted file mode 100644 index fe35893d5..000000000 --- a/source/Blocks/BlockFurnace.h +++ /dev/null @@ -1,47 +0,0 @@ - -#pragma once - -#include "BlockEntity.h" -#include "../World.h" -#include "../Piston.h" -#include "../Entities/Player.h" - - - - - -class cBlockFurnaceHandler : - public cBlockEntityHandler -{ -public: - cBlockFurnaceHandler(BLOCKTYPE a_BlockType) : - cBlockEntityHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_FURNACE, 1, 0)); - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - - // FIXME: Do not use cPiston class for furnace placement! - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0); - - return true; - } -} ; - - - - diff --git a/source/Blocks/BlockGlass.h b/source/Blocks/BlockGlass.h deleted file mode 100644 index f6958bbb6..000000000 --- a/source/Blocks/BlockGlass.h +++ /dev/null @@ -1,26 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockGlassHandler : - public cBlockHandler -{ -public: - cBlockGlassHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - } -} ; - - - - diff --git a/source/Blocks/BlockGlowstone.h b/source/Blocks/BlockGlowstone.h deleted file mode 100644 index 5f0d95dee..000000000 --- a/source/Blocks/BlockGlowstone.h +++ /dev/null @@ -1,30 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockGlowstoneHandler : - public cBlockHandler -{ -public: - cBlockGlowstoneHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - // TODO: More drops? - a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0)); - } -} ; - - - - diff --git a/source/Blocks/BlockGravel.h b/source/Blocks/BlockGravel.h deleted file mode 100644 index e1c9ff390..000000000 --- a/source/Blocks/BlockGravel.h +++ /dev/null @@ -1,27 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockGravelHandler : - public cBlockHandler -{ -public: - cBlockGravelHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual const char * GetStepSound(void) override - { - return "step.gravel"; - } -} ; - - - - diff --git a/source/Blocks/BlockHandler.cpp b/source/Blocks/BlockHandler.cpp deleted file mode 100644 index cd07b3021..000000000 --- a/source/Blocks/BlockHandler.cpp +++ /dev/null @@ -1,465 +0,0 @@ - -#include "Globals.h" -#include "BlockHandler.h" -#include "../Item.h" -#include "../World.h" -#include "../Root.h" -#include "../PluginManager.h" -#include "BlockBed.h" -#include "BlockBrewingStand.h" -#include "BlockButton.h" -#include "BlockCactus.h" -#include "BlockCarpet.h" -#include "BlockCauldron.h" -#include "BlockChest.h" -#include "BlockCloth.h" -#include "BlockCobWeb.h" -#include "BlockComparator.h" -#include "BlockCrops.h" -#include "BlockDeadBush.h" -#include "BlockDirt.h" -#include "BlockDoor.h" -#include "BlockDropSpenser.h" -#include "BlockEnderchest.h" -#include "BlockEntity.h" -#include "BlockFarmland.h" -#include "BlockFenceGate.h" -#include "BlockFire.h" -#include "BlockFlower.h" -#include "BlockFlowerPot.h" -#include "BlockFluid.h" -#include "BlockFurnace.h" -#include "BlockGlass.h" -#include "BlockGlowstone.h" -#include "BlockGravel.h" -#include "BlockHopper.h" -#include "BlockIce.h" -#include "BlockLadder.h" -#include "BlockLeaves.h" -#include "BlockLever.h" -#include "BlockMelon.h" -#include "BlockMushroom.h" -#include "BlockMycelium.h" -#include "BlockNote.h" -#include "BlockOre.h" -#include "BlockPiston.h" -#include "BlockPlanks.h" -#include "BlockPortal.h" -#include "BlockPumpkin.h" -#include "BlockRail.h" -#include "BlockRedstone.h" -#include "BlockRedstoneRepeater.h" -#include "BlockRedstoneTorch.h" -#include "BlockSand.h" -#include "BlockSapling.h" -#include "BlockSign.h" -#include "BlockSlab.h" -#include "BlockSnow.h" -#include "BlockStairs.h" -#include "BlockStems.h" -#include "BlockStone.h" -#include "BlockSugarcane.h" -#include "BlockTallGrass.h" -#include "BlockTorch.h" -#include "BlockVine.h" -#include "BlockWood.h" -#include "BlockWorkbench.h" - - - - - -bool cBlockHandler::m_HandlerInitialized = false; -cBlockHandler * cBlockHandler::m_BlockHandler[256]; - - - - - -cBlockHandler * cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockType) -{ - if (!m_HandlerInitialized) - { - // We have to initialize - memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); - m_HandlerInitialized = true; - } - if (m_BlockHandler[a_BlockType] != NULL) - { - return m_BlockHandler[a_BlockType]; - } - - return m_BlockHandler[a_BlockType] = CreateBlockHandler(a_BlockType); -} - - - - - -cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) -{ - switch(a_BlockType) - { - // Block handlers, alphabetically sorted: - case E_BLOCK_ACTIVATOR_RAIL: return new cBlockRailHandler (a_BlockType); - case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType); - case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_BREWING_STAND: return new cBlockBrewingStandHandler (a_BlockType); - case E_BLOCK_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_BROWN_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); - case E_BLOCK_CACTUS: return new cBlockCactusHandler (a_BlockType); - case E_BLOCK_CARROTS: return new cBlockCropsHandler (a_BlockType); - case E_BLOCK_CARPET: return new cBlockCarpetHandler (a_BlockType); - case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType); - case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType); - case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_ACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); - case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType); - case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType); - case E_BLOCK_CROPS: return new cBlockCropsHandler (a_BlockType); - case E_BLOCK_DEAD_BUSH: return new cBlockDeadBushHandler (a_BlockType); - case E_BLOCK_DETECTOR_RAIL: return new cBlockRailHandler (a_BlockType); - case E_BLOCK_DIAMOND_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_DIRT: return new cBlockDirtHandler (a_BlockType); - case E_BLOCK_DISPENSER: return new cBlockDropSpenserHandler (a_BlockType); - case E_BLOCK_DOUBLE_STONE_SLAB: return new cBlockDoubleSlabHandler (a_BlockType); - case E_BLOCK_DOUBLE_WOODEN_SLAB: return new cBlockDoubleSlabHandler (a_BlockType); - case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType); - case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType); - case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler ( ); - case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType); - case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType); - case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType); - case E_BLOCK_FURNACE: return new cBlockFurnaceHandler (a_BlockType); - case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType); - case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_GLASS: return new cBlockGlassHandler (a_BlockType); - case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType); - case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType); - case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType); - case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType); - case E_BLOCK_INACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); - case E_BLOCK_IRON_DOOR: return new cBlockDoorHandler (a_BlockType); - case E_BLOCK_IRON_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_JUKEBOX: return new cBlockEntityHandler (a_BlockType); - case E_BLOCK_JUNGLE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_LADDER: return new cBlockLadderHandler (a_BlockType); - case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType); - case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_LAVA: return new cBlockFluidHandler (a_BlockType); - case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType); - case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType); - case E_BLOCK_LOG: return new cBlockWoodHandler (a_BlockType); - case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType); - case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType); - case E_BLOCK_MYCELIUM: return new cBlockMyceliumHandler (a_BlockType); - case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType); - case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType); - case E_BLOCK_PISTON_EXTENSION: return new cBlockPistonHeadHandler ( ); - case E_BLOCK_PLANKS: return new cBlockPlanksHandler (a_BlockType); - case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType); - case E_BLOCK_PUMPKIN: return new cBlockPumpkinHandler (a_BlockType); - case E_BLOCK_JACK_O_LANTERN: return new cBlockPumpkinHandler (a_BlockType); - case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType); - case E_BLOCK_QUARTZ_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType); - case E_BLOCK_POTATOES: return new cBlockCropsHandler (a_BlockType); - case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType); - case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType); - case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType); - case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType); - case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType); - case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType); - case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); - case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType); - case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType); - case E_BLOCK_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_SAPLING: return new cBlockSaplingHandler (a_BlockType); - case E_BLOCK_SIGN_POST: return new cBlockSignHandler (a_BlockType); - case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType); - case E_BLOCK_SPRUCE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_STATIONARY_LAVA: return new cBlockFluidHandler (a_BlockType); - case E_BLOCK_STATIONARY_WATER: return new cBlockFluidHandler (a_BlockType); - case E_BLOCK_STICKY_PISTON: return new cBlockPistonHandler (a_BlockType); - case E_BLOCK_STONE: return new cBlockStoneHandler (a_BlockType); - case E_BLOCK_STONE_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_STONE_BUTTON: return new cBlockButtonHandler (a_BlockType); - case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType); - case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType); - case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType); - case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType); - case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType); - case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); - case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType); - case E_BLOCK_WOODEN_BUTTON: return new cBlockButtonHandler (a_BlockType); - case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType); - case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType); - case E_BLOCK_WOODEN_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_WOOL: return new cBlockClothHandler (a_BlockType); - case E_BLOCK_WORKBENCH: return new cBlockWorkbenchHandler (a_BlockType); - case E_BLOCK_YELLOW_FLOWER: return new cBlockFlowerHandler (a_BlockType); - - default: return new cBlockHandler(a_BlockType); - } -} - - - - - -void cBlockHandler::Deinit() -{ - for (int i = 0; i < 256; i++) - { - delete m_BlockHandler[i]; - } - memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); // Don't leave any dangling pointers around, just in case - m_HandlerInitialized = false; -} - - - - - -cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType) -{ - m_BlockType = a_BlockType; -} - - - - - -bool cBlockHandler::GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta -) -{ - // By default, all blocks can be placed and the meta is copied over from the item's damage value: - a_BlockType = m_BlockType; - a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f); - return true; -} - - - - - -void cBlockHandler::OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ -} - - - - - -void cBlockHandler::OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ -} - - - - - -void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ -} - - - - - -void cBlockHandler::OnPlaced(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - // Notify the neighbors - NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); - NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); - NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); -} - - - - - -void cBlockHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Notify the neighbors - NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); - NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); - NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); -} - - - - - -void cBlockHandler::NeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height)) - { - GetBlockHandler(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ); - } -} - - - - - -void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ -} - - - - - -void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ -} - - - - - -void cBlockHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ -} - - - - - -void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) -{ - // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this. - a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta)); -} - - - - - -void cBlockHandler::DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cItems Pickups; - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - ConvertToPickups(Pickups, Meta); - - // Allow plugins to modify the pickups: - cRoot::Get()->GetPluginManager()->CallHookBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups); - - if (!Pickups.empty()) - { - MTRand r1; - - // Mid-block position first - double MicroX, MicroY, MicroZ; - MicroX = a_BlockX + 0.5; - MicroY = a_BlockY + 0.5; - MicroZ = a_BlockZ + 0.5; - - // Add random offset second (this causes pickups to spawn inside blocks most times, it's a little buggy) - //MicroX += (int)(r1.randInt(16) + r1.randInt(16) - 16); - //MicroY += (int)(r1.randInt(16) + r1.randInt(16) - 16); - //MicroZ += (int)(r1.randInt(16) + r1.randInt(16) - 16); - - a_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ); - } -} - - - - - -const char * cBlockHandler::GetStepSound() -{ - return "step.stone"; -} - - - - - -bool cBlockHandler::CanBeAt(int a_BlockX, int a_BlockY, int a_BlockZ, const cChunk & a_Chunk) -{ - return true; -} - - - - - -bool cBlockHandler::IsUseable() -{ - return false; -} - - - - - -bool cBlockHandler::IsClickedThrough(void) -{ - return false; -} - - - - - -bool cBlockHandler::DoesIgnoreBuildCollision(void) -{ - return (m_BlockType == E_BLOCK_AIR); -} - - - - - -bool cBlockHandler::DoesDropOnUnsuitable(void) -{ - return true; -} - - - - - -void cBlockHandler::Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) -{ - if (!CanBeAt(a_RelX, a_RelY, a_RelZ, a_Chunk)) - { - if (DoesDropOnUnsuitable()) - { - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); - } - - a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); - } - else - { - // Wake up the simulators for this block: - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); - } -} - - - - diff --git a/source/Blocks/BlockHandler.h b/source/Blocks/BlockHandler.h deleted file mode 100644 index 81d9f240c..000000000 --- a/source/Blocks/BlockHandler.h +++ /dev/null @@ -1,152 +0,0 @@ - -#pragma once - -#include "../Defines.h" -#include "../Item.h" -#include "../Chunk.h" - - - - - -// fwd: -class cWorld; -class cPlayer; - - - - - -class cBlockHandler -{ -public: - cBlockHandler(BLOCKTYPE a_BlockType); - - /// Called when the block gets ticked either by a random tick or by a queued tick - virtual void OnUpdate(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ); - - /** Called before a block is placed into a world. - The handler should return true to allow placement, false to refuse. - Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. - Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block - */ - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ); - - /// Called by cWorld::SetBlock() after the block has been set - virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced(). - virtual void OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ); - - /// Called before the player has destroyed a block - virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Called before a block gets destroyed / replaced with air - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position) - virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Notifies all neighbors of the given block about a change - static void NeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Called while the player diggs the block. - virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Called if the user right clicks the block and the block is useable - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); - - /// Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta); - - /// Handles the dropping of a block based on what ConvertToDrops() returns. This will not destroy the block. a_Digger is the entity causing the drop; it may be NULL - virtual void DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Returns step sound name of block - virtual const char * GetStepSound(void); - - /// Checks if the block can stay at the specified relative coords in the chunk - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk); - - /** Checks if the block can be placed at this point. - Default: CanBeAt(...) - NOTE: This call doesn't actually place the block - */ - // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); - - /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called - virtual bool IsUseable(void); - - /** Indicates whether the client will click through this block. - For example digging a fire will hit the block below the fire so fire is clicked through - */ - virtual bool IsClickedThrough(void); - - /** Checks if the player can build "inside" this block. - For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision - */ - virtual bool DoesIgnoreBuildCollision(void); - - /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true - virtual bool DoesDropOnUnsuitable(void); - - /** Called when one of the neighbors gets set; equivalent to MC block update. - By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()), - and wakes up all simulators on the block. - */ - virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk); - - /// Returns the meta for a block after rotating it counter-clockwise from the specified meta. Default: no change - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) { return a_Meta; } - - /// Returns the meta for a block after rotating it clockwise from the specified meta. Default: no change - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) { return a_Meta; } - - /// Returns the meta for a block after mirroring it around the XY plane. Default: no change - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) { return a_Meta; } - - /// Returns the meta for a block after mirroring it around the XZ plane. Default: no change - virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) { return a_Meta; } - - /// Returns the meta for a block after mirroring it around the YZ plane. Default: no change - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; } - - - /// Get the blockhandler for a specific block id - static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockType); - - /// Deletes all initialised block handlers - static void Deinit(); - -protected: - BLOCKTYPE m_BlockType; - - // Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead. - static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockType); - static cBlockHandler *m_BlockHandler[256]; - static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized -}; - - - - - -// Shortcut to get the blockhandler for a specific block -inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType) -{ - return cBlockHandler::GetBlockHandler(a_BlockType); -} - - - - diff --git a/source/Blocks/BlockHopper.h b/source/Blocks/BlockHopper.h deleted file mode 100644 index 3998276d7..000000000 --- a/source/Blocks/BlockHopper.h +++ /dev/null @@ -1,46 +0,0 @@ - -// BlockHopper.h - -// Declares the cBlockHopperHandler class representing the handler for the Hopper block - - - - - -class cBlockHopperHandler : - public cBlockEntityHandler -{ -public: - cBlockHopperHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - - // Convert the blockface into meta: - switch (a_BlockFace) - { - case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break; - case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break; - case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break; - case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break; - case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break; - case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break; - default: a_BlockMeta = E_META_HOPPER_UNATTACHED; break; - } - return true; - } -} ; - - - - diff --git a/source/Blocks/BlockIce.h b/source/Blocks/BlockIce.h deleted file mode 100644 index af4961114..000000000 --- a/source/Blocks/BlockIce.h +++ /dev/null @@ -1,37 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockIceHandler : - public cBlockHandler -{ -public: - cBlockIceHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // No pickups - } - - - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - // TODO: Ice destroyed with air below it should turn into air instead of water - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0); - // This is called later than the real destroying of this ice block - } -} ; - - - - diff --git a/source/Blocks/BlockLadder.h b/source/Blocks/BlockLadder.h deleted file mode 100644 index c0aa25f60..000000000 --- a/source/Blocks/BlockLadder.h +++ /dev/null @@ -1,115 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockLadderHandler : - public cBlockHandler -{ -public: - cBlockLadderHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - if (!LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) - { - a_BlockFace = FindSuitableBlockFace(a_World, a_BlockX, a_BlockY, a_BlockZ); - - if (a_BlockFace == BLOCK_FACE_BOTTOM) - { - return false; - } - } - - a_BlockType = m_BlockType; - a_BlockMeta = DirectionToMetaData(a_BlockFace); - return true; - } - - - static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export - { // tolua_export - switch (a_Direction) - { - case 0x2: return 0x2; - case 0x3: return 0x3; - case 0x4: return 0x4; - case 0x5: return 0x5; - default: return 0x2; - } - } // tolua_export - - - static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export - { // tolua_export - switch (a_MetaData) - { - case 0x2: return 0x2; - case 0x3: return 0x3; - case 0x4: return 0x4; - case 0x5: return 0x5; - default: return 0x2; - } - } // tolua_export - - - /// Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure - static char FindSuitableBlockFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - for (int Face = 2; Face <= 5; Face++) - { - if (LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, Face)) - { - return Face; - } - } - return BLOCK_FACE_BOTTOM; - } - - - static bool LadderCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) - { - if ((a_BlockFace == BLOCK_FACE_BOTTOM) || (a_BlockFace == BLOCK_FACE_TOP)) - { - return false; - } - - AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); - - return g_BlockIsSolid[a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)]; - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison - char BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - return LadderCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, BlockFace); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/Blocks/BlockLeaves.h b/source/Blocks/BlockLeaves.h deleted file mode 100644 index 6e015b8fa..000000000 --- a/source/Blocks/BlockLeaves.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" -#include "../BlockArea.h" - - - - - -// Leaves can be this many blocks that away (inclusive) from the log not to decay -#define LEAVES_CHECK_DISTANCE 6 - -#define PROCESS_NEIGHBOR(x,y,z) \ - switch (a_Area.GetBlockType(x, y, z)) \ - { \ - case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \ - case E_BLOCK_LOG: return true; \ - } - -bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); - - - - - -class cBlockLeavesHandler : - public cBlockHandler -{ -public: - cBlockLeavesHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - MTRand rand; - - // Only the first 2 bits contain the display information, the others are for growing - if (rand.randInt(5) == 0) - { - a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); - } - if ((a_BlockMeta & 3) == E_META_SAPLING_APPLE) - { - if (rand.rand(100) == 0) - { - a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); - } - } - } - - - void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - cBlockHandler::OnDestroyed(a_World, a_BlockX, a_BlockY, a_BlockZ); - - //0.5% chance of dropping an apple - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - //check if Oak (0x1 and 0x2 bit not set) - MTRand rand; - if(!(Meta & 3) && rand.randInt(200) == 100) - { - cItems Drops; - Drops.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); - a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); - } - } - - - virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x7); // Unset 0x8 bit so it gets checked for decay - } - - - virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if ((Meta & 0x04) != 0) - { - // Player-placed leaves, don't decay - return; - } - - if ((Meta & 0x8) != 0) - { - // These leaves have been checked for decay lately and nothing around them changed - return; - } - - // Get the data around the leaves: - cBlockArea Area; - if (!Area.Read( - a_World, - a_BlockX - LEAVES_CHECK_DISTANCE, a_BlockX + LEAVES_CHECK_DISTANCE, - a_BlockY - LEAVES_CHECK_DISTANCE, a_BlockY + LEAVES_CHECK_DISTANCE, - a_BlockZ - LEAVES_CHECK_DISTANCE, a_BlockZ + LEAVES_CHECK_DISTANCE, - cBlockArea::baTypes) - ) - { - // Cannot check leaves, a chunk is missing too close - return; - } - - if (HasNearLog(Area, a_BlockX, a_BlockY, a_BlockZ)) - { - // Wood found, the leaves stay; mark them as checked: - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x8); - return; - } - // Decay the leaves: - DropBlock(a_World, NULL, a_BlockX, a_BlockY, a_BlockZ); - - a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); - - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - - -bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Filter the blocks into a {leaves, log, other (air)} set: - BLOCKTYPE * Types = a_Area.GetBlockTypes(); - for (int i = a_Area.GetBlockCount() - 1; i > 0; i--) - { - switch (Types[i]) - { - case E_BLOCK_LEAVES: - case E_BLOCK_LOG: - { - break; - } - default: - { - Types[i] = E_BLOCK_AIR; - break; - } - } - } // for i - Types[] - - // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block: - // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations - a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE); - for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++) - { - for (int y = a_BlockY - i; y <= a_BlockY + i; y++) - { - for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++) - { - for (int x = a_BlockX - i; x <= a_BlockX + i; x++) - { - if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i) - { - continue; - } - PROCESS_NEIGHBOR(x - 1, y, z); - PROCESS_NEIGHBOR(x + 1, y, z); - PROCESS_NEIGHBOR(x, y, z - 1); - PROCESS_NEIGHBOR(x, y, z + 1); - PROCESS_NEIGHBOR(x, y + 1, z); - PROCESS_NEIGHBOR(x, y - 1, z); - } // for x - } // for z - } // for y - } // for i - BFS iterations - return false; -} - - - - diff --git a/source/Blocks/BlockLever.cpp b/source/Blocks/BlockLever.cpp deleted file mode 100644 index a9bd6c990..000000000 --- a/source/Blocks/BlockLever.cpp +++ /dev/null @@ -1,38 +0,0 @@ - -#include "Globals.h" -#include "BlockLever.h" -#include "../Entities/Player.h" -#include "../Simulator/RedstoneSimulator.h" - - - - - -cBlockLeverHandler::cBlockLeverHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockLeverHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off. - NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); - - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); - if (Meta & 0x08) - { - a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); - } - else - { - a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.5f); - } -} - - - - diff --git a/source/Blocks/BlockLever.h b/source/Blocks/BlockLever.h deleted file mode 100644 index 5553170e2..000000000 --- a/source/Blocks/BlockLever.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "BlockHandler.h" -#include "../Simulator/RedstoneSimulator.h" - - - - - -class cBlockLeverHandler : - public cBlockHandler -{ -public: - cBlockLeverHandler(BLOCKTYPE a_BlockType); - - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(E_BLOCK_LEVER, 1, 0)); - } - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = cRedstoneSimulator::LeverDirectionToMetaData(a_BlockFace); - return true; - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/Blocks/BlockMelon.h b/source/Blocks/BlockMelon.h deleted file mode 100644 index 2f7d9a461..000000000 --- a/source/Blocks/BlockMelon.h +++ /dev/null @@ -1,35 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockMelonHandler : - public cBlockHandler -{ -public: - cBlockMelonHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - MTRand r1; - a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, (char)(3 + r1.randInt(4)), 0)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/Blocks/BlockMushroom.h b/source/Blocks/BlockMushroom.h deleted file mode 100644 index 2846a6317..000000000 --- a/source/Blocks/BlockMushroom.h +++ /dev/null @@ -1,59 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockMushroomHandler : - public cBlockHandler -{ -public: - cBlockMushroomHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(m_BlockType, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - if (a_RelY <= 0) - { - return false; - } - - // TODO: Cannot be at too much daylight - - switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) - { - case E_BLOCK_GLASS: - case E_BLOCK_CACTUS: - case E_BLOCK_ICE: - case E_BLOCK_LEAVES: - case E_BLOCK_AIR: - { - return false; - } - } - return true; - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - diff --git a/source/Blocks/BlockMycelium.h b/source/Blocks/BlockMycelium.h deleted file mode 100644 index 7f897c72a..000000000 --- a/source/Blocks/BlockMycelium.h +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockMyceliumHandler : - public cBlockHandler -{ -public: - cBlockMyceliumHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); - } - - virtual const char * GetStepSound(void) override - { - return "step.gravel"; - } -} ; - - - - diff --git a/source/Blocks/BlockNote.h b/source/Blocks/BlockNote.h deleted file mode 100644 index fef38d845..000000000 --- a/source/Blocks/BlockNote.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "BlockHandler.h" -#include "BlockEntity.h" - -class cBlockNoteHandler : public cBlockEntityHandler -{ -public: - cBlockNoteHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) - { - } - -}; diff --git a/source/Blocks/BlockOre.h b/source/Blocks/BlockOre.h deleted file mode 100644 index 9684dbb19..000000000 --- a/source/Blocks/BlockOre.h +++ /dev/null @@ -1,80 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -class cBlockOreHandler : - public cBlockHandler -{ -public: - cBlockOreHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - short ItemType = m_BlockType; - char Count = 1; - short Meta = 0; - - MTRand r1; - switch (m_BlockType) - { - case E_BLOCK_LAPIS_ORE: - { - ItemType = E_ITEM_DYE; - Count = 4 + (char)r1.randInt(4); - Meta = 4; - break; - } - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - { - Count = 4 + (char)r1.randInt(1); - break; - } - default: - { - Count = 1; - break; - } - } - - switch (m_BlockType) - { - case E_BLOCK_DIAMOND_ORE: - { - ItemType = E_ITEM_DIAMOND; - break; - } - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - { - ItemType = E_ITEM_REDSTONE_DUST; - break; - } - case E_BLOCK_EMERALD_ORE: - { - ItemType = E_ITEM_EMERALD; - break; - } - case E_BLOCK_COAL_ORE: - { - ItemType = E_ITEM_COAL; - break; - } - } - a_Pickups.push_back(cItem(ItemType, Count, Meta)); - } -} ; - - - - diff --git a/source/Blocks/BlockPiston.cpp b/source/Blocks/BlockPiston.cpp deleted file mode 100644 index d5750ebdd..000000000 --- a/source/Blocks/BlockPiston.cpp +++ /dev/null @@ -1,102 +0,0 @@ - -#include "Globals.h" -#include "BlockPiston.h" -#include "../Item.h" -#include "../World.h" -#include "../Entities/Player.h" -#include "../Piston.h" - - - - - -#define AddPistonDir(x, y, z, dir, amount) \ - switch (dir) \ - { \ - case 0: (y) -= (amount); break; \ - case 1: (y) += (amount); break; \ - case 2: (z) -= (amount); break; \ - case 3: (z) += (amount); break; \ - case 4: (x) -= (amount); break; \ - case 5: (x) += (amount); break; \ - } - - - - -cBlockPistonHandler::cBlockPistonHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockPistonHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - int newX = a_BlockX; - int newY = a_BlockY; - int newZ = a_BlockZ; - AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1); - - if (a_World->GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION) - { - a_World->SetBlock(newX, newY, newZ, E_BLOCK_AIR, 0); - } -} - - - - - -bool cBlockPistonHandler::GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta -) -{ - a_BlockType = m_BlockType; - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); - return true; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBlockPistonHeadHandler: - -cBlockPistonHeadHandler::cBlockPistonHeadHandler(void) : - super(E_BLOCK_PISTON_EXTENSION) -{ -} - - - - - -void cBlockPistonHeadHandler::OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - int newX = a_BlockX; - int newY = a_BlockY; - int newZ = a_BlockZ; - AddPistonDir(newX, newY, newZ, OldMeta & ~(8), -1); - - BLOCKTYPE Block = a_World->GetBlock(newX, newY, newZ); - if ((Block == E_BLOCK_STICKY_PISTON) || (Block == E_BLOCK_PISTON)) - { - a_World->DigBlock(newX, newY, newZ); - } -} - - - - - diff --git a/source/Blocks/BlockPiston.h b/source/Blocks/BlockPiston.h deleted file mode 100644 index 109f5ea8b..000000000 --- a/source/Blocks/BlockPiston.h +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockPistonHandler : - public cBlockHandler -{ -public: - cBlockPistonHandler(BLOCKTYPE a_BlockType); - - virtual void OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override; -} ; - - - - - -class cBlockPistonHeadHandler : - public cBlockHandler -{ - typedef cBlockHandler super; - -public: - cBlockPistonHeadHandler(void); - - virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override; -} ; - - - - diff --git a/source/Blocks/BlockPlanks.h b/source/Blocks/BlockPlanks.h deleted file mode 100644 index f3b8dbfb6..000000000 --- a/source/Blocks/BlockPlanks.h +++ /dev/null @@ -1,41 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockPlanksHandler : public cBlockHandler -{ -public: - cBlockPlanksHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); - a_BlockMeta = Meta; - return true; - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/Blocks/BlockPortal.h b/source/Blocks/BlockPortal.h deleted file mode 100644 index c56f0cbc8..000000000 --- a/source/Blocks/BlockPortal.h +++ /dev/null @@ -1,108 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockPortalHandler : - public cBlockHandler -{ -public: - cBlockPortalHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - // We set to zero so MCS doesn't stop you from building weird portals like vanilla does - // CanBeAt doesn't do anything if meta is zero - // We set to zero because the client sends meta = 2 to the server (it calculates rotation itself) - - a_BlockType = m_BlockType; - a_BlockMeta = 0; - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - return; // No pickups - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - if ((a_RelY - 1 < 0) || (a_RelY + 1 > cChunkDef::Height)) - { - return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1 - } - - switch (a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)) - { - case 0x1: - { - static const struct - { - int x, y, z; - } PortalCheck[] = - { - { 0, 1, 0}, - { 0,-1, 0}, - { 1, 0, 0}, - {-1, 0, 0}, - } ; - - for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++) - { - BLOCKTYPE Block; - a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block); - - if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) - { - return false; - } - } - break; - } - case 0x2: - { - static const struct - { - int x, y, z; - } PortalCheck[] = - { - { 0, 1, 0}, - { 0,-1, 0}, - { 0, 0, -1}, - { 0, 0, 1}, - } ; - - for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++) - { - BLOCKTYPE Block; - a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block); - - if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) - { - return false; - } - } - break; - } - } - return true; - } -} ; - - - - diff --git a/source/Blocks/BlockPumpkin.h b/source/Blocks/BlockPumpkin.h deleted file mode 100644 index 76abc6818..000000000 --- a/source/Blocks/BlockPumpkin.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "BlockHandler.h" - - - - -class cBlockPumpkinHandler : - public cBlockHandler -{ -public: - cBlockPumpkinHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); - return true; - } - - inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) - { - ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); - - a_Yaw += 180 + 45; - if (a_Yaw > 360) - { - a_Yaw -= 360; - } - if ((a_Yaw >= 0) && (a_Yaw < 90)) - { - return 0x0; - } - else if ((a_Yaw >= 180) && (a_Yaw < 270)) - { - return 0x2; - } - else if ((a_Yaw >= 90) && (a_Yaw < 180)) - { - return 0x1; - } - else - { - return 0x3; - } - } - -} ; - - - - diff --git a/source/Blocks/BlockRail.h b/source/Blocks/BlockRail.h deleted file mode 100644 index 24a101652..000000000 --- a/source/Blocks/BlockRail.h +++ /dev/null @@ -1,398 +0,0 @@ - -#pragma once - -#include "BlockEntity.h" -#include "../World.h" - - - - - -enum ENUM_PURE -{ - E_PURE_UPDOWN = 0, - E_PURE_DOWN = 1, - E_PURE_NONE = 2 -}; - - - - - -class cBlockRailHandler : - public cBlockHandler -{ - typedef cBlockHandler super; - -public: - cBlockRailHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ); - return true; - } - - - virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ) && (Meta != FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ))) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ)); - } - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - super::ConvertToPickups(a_Pickups, 0); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - if (a_RelY <= 0) - { - return false; - } - if (!g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]) - { - return false; - } - - NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); - switch (Meta) - { - case E_META_RAIL_ASCEND_XP: - case E_META_RAIL_ASCEND_XM: - case E_META_RAIL_ASCEND_ZM: - case E_META_RAIL_ASCEND_ZP: - { - // Mapping between the meta and the neighbors that need checking - Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero - static const struct - { - int x, z; - } Coords[] = - { - { 1, 0}, // east, XP - {-1, 0}, // west, XM - { 0, -1}, // north, ZM - { 0, 1}, // south, ZP - } ; - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta)) - { - // Too close to the edge, cannot simulate - return true; - } - return g_BlockIsSolid[BlockType]; - } - } - return true; - } - - NIBBLETYPE FindMeta(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - NIBBLETYPE Meta = 0; - char RailsCnt = 0; - bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP - memset(Neighbors, false, sizeof(Neighbors)); - Neighbors[0] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN)); - Neighbors[1] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN)); - Neighbors[2] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN)); - Neighbors[3] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN)); - Neighbors[4] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST, E_PURE_NONE)); - Neighbors[5] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST, E_PURE_NONE)); - Neighbors[6] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE)); - Neighbors[7] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE)); - if (IsUnstable(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST)) - Neighbors[0] = true; - if (IsUnstable(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST)) - Neighbors[1] = true; - if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH)) - Neighbors[2] = true; - if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH)) - Neighbors[3] = true; - for (int i = 0; i < 8; i++) - { - if (Neighbors[i]) - { - RailsCnt++; - } - } - if (RailsCnt == 1) - { - if (Neighbors[7]) return E_META_RAIL_ASCEND_ZP; - else if (Neighbors[6]) return E_META_RAIL_ASCEND_ZM; - else if (Neighbors[5]) return E_META_RAIL_ASCEND_XM; - else if (Neighbors[4]) return E_META_RAIL_ASCEND_XP; - else if (Neighbors[0] || Neighbors[1]) return E_META_RAIL_XM_XP; - else if (Neighbors[2] || Neighbors[3]) return E_META_RAIL_ZM_ZP; - ASSERT(!"Weird neighbor count"); - return Meta; - } - for (int i = 0; i < 4; i++) - { - if (Neighbors[i + 4]) - { - Neighbors[i] = true; - } - } - if (RailsCnt > 1) - { - if (Neighbors[3] && Neighbors[0]) return E_META_RAIL_CURVED_ZP_XP; - else if (Neighbors[3] && Neighbors[1]) return E_META_RAIL_CURVED_ZP_XM; - else if (Neighbors[2] && Neighbors[0]) return E_META_RAIL_CURVED_ZM_XP; - else if (Neighbors[2] && Neighbors[1]) return E_META_RAIL_CURVED_ZM_XM; - else if (Neighbors[7] && Neighbors[2]) return E_META_RAIL_ASCEND_ZP; - else if (Neighbors[3] && Neighbors[6]) return E_META_RAIL_ASCEND_ZM; - else if (Neighbors[5] && Neighbors[0]) return E_META_RAIL_ASCEND_XM; - else if (Neighbors[4] && Neighbors[1]) return E_META_RAIL_ASCEND_XP; - else if (Neighbors[0] && Neighbors[1]) return E_META_RAIL_XM_XP; - else if (Neighbors[2] && Neighbors[3]) return E_META_RAIL_ZM_ZP; - ASSERT(!"Weird neighbor count"); - } - return Meta; - } - - - bool IsUnstable(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) - { - return false; - } - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - switch (Meta) - { - case E_META_RAIL_ZM_ZP: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN) - ) - { - return true; - } - break; - } - - case E_META_RAIL_XM_XP: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN) - ) - { - return true; - } - break; - } - - case E_META_RAIL_ASCEND_XP: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) - ) - { - return true; - } - break; - } - - case E_META_RAIL_ASCEND_XM: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) || - IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST) - ) - { - return true; - } - break; - } - - case E_META_RAIL_ASCEND_ZM: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) - ) - { - return true; - } - break; - } - - case E_META_RAIL_ASCEND_ZP: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH) - ) - { - return true; - } - break; - } - - case E_META_RAIL_CURVED_ZP_XP: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) - ) - { - return true; - } - break; - } - - case E_META_RAIL_CURVED_ZP_XM: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) - ) - { - return true; - } - break; - } - - case E_META_RAIL_CURVED_ZM_XM: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) - ) - { - return true; - } - break; - } - - case E_META_RAIL_CURVED_ZM_XP: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) - ) - { - return true; - } - break; - } - } - return false; - } - - - bool IsNotConnected(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Pure = 0) - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); - NIBBLETYPE Meta; - if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) - { - if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN)) - { - if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE)) - { - return true; - } - else - { - Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ); - } - } - else - { - Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ); - } - } - else - { - Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - } - - switch (a_BlockFace) - { - case BLOCK_FACE_NORTH: - { - if ( - (Meta == E_META_RAIL_ZM_ZP) || - (Meta == E_META_RAIL_ASCEND_ZM) || - (Meta == E_META_RAIL_ASCEND_ZP) || - (Meta == E_META_RAIL_CURVED_ZP_XP) || - (Meta == E_META_RAIL_CURVED_ZP_XM) - ) - { - return false; - } - break; - } - - case BLOCK_FACE_SOUTH: - { - if ( - (Meta == E_META_RAIL_ZM_ZP) || - (Meta == E_META_RAIL_ASCEND_ZM) || - (Meta == E_META_RAIL_ASCEND_ZP) || - (Meta == E_META_RAIL_CURVED_ZM_XP) || - (Meta == E_META_RAIL_CURVED_ZM_XM) - ) - { - return false; - } - break; - } - - case BLOCK_FACE_EAST: - { - if ( - (Meta == E_META_RAIL_XM_XP) || - (Meta == E_META_RAIL_ASCEND_XP) || - (Meta == E_META_RAIL_ASCEND_XM) || - (Meta == E_META_RAIL_CURVED_ZP_XM) || - (Meta == E_META_RAIL_CURVED_ZM_XM) - ) - { - return false; - } - break; - } - case BLOCK_FACE_WEST: - { - if ( - (Meta == E_META_RAIL_XM_XP) || - (Meta == E_META_RAIL_ASCEND_XP) || - (Meta == E_META_RAIL_ASCEND_XM) || - (Meta == E_META_RAIL_CURVED_ZP_XP) || - (Meta == E_META_RAIL_CURVED_ZM_XP) - ) - { - return false; - } - break; - } - } - return true; - } -} ; - - - - diff --git a/source/Blocks/BlockRedstone.cpp b/source/Blocks/BlockRedstone.cpp deleted file mode 100644 index 35cdc34cf..000000000 --- a/source/Blocks/BlockRedstone.cpp +++ /dev/null @@ -1,27 +0,0 @@ - -#include "Globals.h" -#include "BlockRedstone.h" -#include "../Item.h" -#include "../World.h" - - - - - -cBlockRedstoneHandler::cBlockRedstoneHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockRedstoneHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Nothing needed yet -} - - - - diff --git a/source/Blocks/BlockRedstone.h b/source/Blocks/BlockRedstone.h deleted file mode 100644 index f28f3f2d6..000000000 --- a/source/Blocks/BlockRedstone.h +++ /dev/null @@ -1,35 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockRedstoneHandler : - public cBlockHandler -{ -public: - cBlockRedstoneHandler(BLOCKTYPE a_BlockType); - - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]); - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, 1)); - } -} ; - - - - diff --git a/source/Blocks/BlockRedstoneRepeater.cpp b/source/Blocks/BlockRedstoneRepeater.cpp deleted file mode 100644 index 72ea21012..000000000 --- a/source/Blocks/BlockRedstoneRepeater.cpp +++ /dev/null @@ -1,51 +0,0 @@ - -#include "Globals.h" -#include "BlockRedstoneRepeater.h" -#include "../Simulator/RedstoneSimulator.h" -#include "../Entities/Player.h" - - - - - -cBlockRedstoneRepeaterHandler::cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockRedstoneRepeaterHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Nothing needed yet -} - - - - - -void cBlockRedstoneRepeaterHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f)); -} - - - - -bool cBlockRedstoneRepeaterHandler::GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta -) -{ - a_BlockType = m_BlockType; - a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); - return true; -} - - - - diff --git a/source/Blocks/BlockRedstoneRepeater.h b/source/Blocks/BlockRedstoneRepeater.h deleted file mode 100644 index 958841a34..000000000 --- a/source/Blocks/BlockRedstoneRepeater.h +++ /dev/null @@ -1,55 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockRedstoneRepeaterHandler : - public cBlockHandler -{ -public: - cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType); - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(E_ITEM_REDSTONE_REPEATER, 1, 0)); - } - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override; - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/Blocks/BlockRedstoneTorch.h b/source/Blocks/BlockRedstoneTorch.h deleted file mode 100644 index cb897ba3f..000000000 --- a/source/Blocks/BlockRedstoneTorch.h +++ /dev/null @@ -1,36 +0,0 @@ - -#pragma once - -#include "BlockRedstone.h" -#include "BlockTorch.h" - - - - - -class cBlockRedstoneTorchHandler : - public cBlockTorchHandler -{ -public: - cBlockRedstoneTorchHandler(BLOCKTYPE a_BlockType) - : cBlockTorchHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Always drop the ON torch, meta 0 - a_Pickups.push_back(cItem(E_BLOCK_REDSTONE_TORCH_ON, 1, 0)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/Blocks/BlockSand.h b/source/Blocks/BlockSand.h deleted file mode 100644 index 3fc271483..000000000 --- a/source/Blocks/BlockSand.h +++ /dev/null @@ -1,28 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockSandHandler : - public cBlockHandler -{ -public: - cBlockSandHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual const char * GetStepSound(void) override - { - return "step.sand"; - } - -}; - - - - diff --git a/source/Blocks/BlockSapling.h b/source/Blocks/BlockSapling.h deleted file mode 100644 index fff2fa88b..000000000 --- a/source/Blocks/BlockSapling.h +++ /dev/null @@ -1,57 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockSaplingHandler : - public cBlockHandler -{ -public: - cBlockSaplingHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Only the first 2 bits contain the display information, the others are for growing - a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - if ((Meta & 0x08) != 0) - { - a_World->GrowTree(a_BlockX, a_BlockY, a_BlockZ); - } - else - { - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x08); - } - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - diff --git a/source/Blocks/BlockSign.h b/source/Blocks/BlockSign.h deleted file mode 100644 index 7fbe61893..000000000 --- a/source/Blocks/BlockSign.h +++ /dev/null @@ -1,78 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -class cBlockSignHandler : - public cBlockHandler -{ -public: - cBlockSignHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_SIGN, 1, 0)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } - - - static char RotationToMetaData(double a_Rotation) - { - a_Rotation += 180 + (180 / 16); // So it's not aligned with axis - if (a_Rotation > 360) - { - a_Rotation -= 360; - } - - a_Rotation = (a_Rotation / 360) * 16; - - return ((char)a_Rotation) % 16; - } - - - static char DirectionToMetaData(char a_Direction) - { - switch (a_Direction) - { - case 0x2: return 0x2; - case 0x3: return 0x3; - case 0x4: return 0x4; - case 0x5: return 0x5; - default: - { - break; - } - } - return 0x2; - } - - - virtual void OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); - } -} ; - - - - diff --git a/source/Blocks/BlockSlab.h b/source/Blocks/BlockSlab.h deleted file mode 100644 index 7c1251b28..000000000 --- a/source/Blocks/BlockSlab.h +++ /dev/null @@ -1,182 +0,0 @@ - -// BlockSlab.h - -// Declares cBlockSlabHandler and cBlockDoubleSlabHandler classes - - - - - -#pragma once - -#include "BlockHandler.h" -#include "../Items/ItemHandler.h" - - - - - -class cBlockSlabHandler : - public cBlockHandler -{ -public: - cBlockSlabHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta)); - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - BLOCKTYPE Type = (BLOCKTYPE) (a_Player->GetEquippedItem().m_ItemType); - NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x07); - - // HandlePlaceBlock wants a cItemHandler pointer thing, so let's give it one - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(GetDoubleSlabType(Type)); - - // Check if the block at the coordinates is a slab. Eligibility for combining has already been processed in ClientHandle - if (IsAnySlabType(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))) - { - // Call the function in ClientHandle that places a block when the client sends the packet, - // so that plugins may interfere with the placement. - - if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM)) - { - // Top and bottom faces need no parameter modification - a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); - } - else - { - // The other faces need to distinguish between top and bottom cursor positions - if (a_CursorY > 7) - { - // Edit the call to use BLOCK_FACE_BOTTOM, otherwise it places incorrectly - a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_TOP, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); - } - else - { - // Edit the call to use BLOCK_FACE_TOP, otherwise it places incorrectly - a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_BOTTOM, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); - } - } - return false; // Cancel the event, because dblslabs were already placed, nothing else needed - } - - // Place the single-slab with correct metas: - switch (a_BlockFace) - { - case BLOCK_FACE_TOP: - { - // Bottom half slab block - a_BlockMeta = Meta & 0x7; - break; - } - case BLOCK_FACE_BOTTOM: - { - // Top half slab block - a_BlockMeta = Meta | 0x8; - break; - } - case BLOCK_FACE_EAST: - case BLOCK_FACE_NORTH: - case BLOCK_FACE_SOUTH: - case BLOCK_FACE_WEST: - { - if (a_CursorY > 7) - { - // Cursor at top half of block, place top slab - a_BlockMeta = Meta | 0x8; break; - } - else - { - // Cursor at bottom half of block, place bottom slab - a_BlockMeta = Meta & 0x7; break; - } - } - } // switch (a_BlockFace) - return true; - } - - - virtual const char * GetStepSound(void) override - { - switch (m_BlockType) - { - case E_BLOCK_WOODEN_SLAB: return "step.wood"; - case E_BLOCK_STONE_SLAB: return "step.stone"; - } - ASSERT(!"Unhandled slab type!"); - return ""; - } - - - /// Returns true if the specified blocktype is one of the slabs handled by this handler - static bool IsAnySlabType(BLOCKTYPE a_BlockType) - { - return ((a_BlockType == E_BLOCK_WOODEN_SLAB) || (a_BlockType == E_BLOCK_STONE_SLAB)); - } - - - /// Converts the single-slab blocktype to its equivalent double-slab blocktype - static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType) - { - switch (a_SingleSlabBlockType) - { - case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB; - case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB; - } - ASSERT(!"Unhandled slab type!"); - return E_BLOCK_AIR; - } - -} ; - - - - - -class cBlockDoubleSlabHandler : - public cBlockHandler -{ -public: - cBlockDoubleSlabHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - if (m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB) - { - m_BlockType = E_BLOCK_STONE_SLAB; - } - else - { - m_BlockType = E_BLOCK_WOODEN_SLAB; - } - a_Pickups.push_back(cItem(m_BlockType, 2, a_BlockMeta)); - } - - - virtual const char * GetStepSound(void) override - { - return ((m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone"; - } -} ; - - - - diff --git a/source/Blocks/BlockSnow.h b/source/Blocks/BlockSnow.h deleted file mode 100644 index b8d48362c..000000000 --- a/source/Blocks/BlockSnow.h +++ /dev/null @@ -1,72 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockSnowHandler : - public cBlockHandler -{ -public: - cBlockSnowHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - NIBBLETYPE Meta = a_World->GetBlockMeta(Vector3i(a_BlockX, a_BlockY, a_BlockZ)); - - if ((Meta < 7) && (Meta != 0)) // Is height at maximum (7) or at mininum (0)? Don't do anything if so - { - Meta++; - } - - a_BlockMeta = Meta; - return true; - } - - - virtual bool DoesIgnoreBuildCollision(void) override - { - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_SNOWBALL, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return (a_RelY > 0) && g_BlockIsSnowable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]; - } - - - virtual bool DoesDropOnUnsuitable(void) override - { - return false; - } - - - virtual const char * GetStepSound(void) override - { - return "step.cloth"; - } -} ; - - - - diff --git a/source/Blocks/BlockStairs.h b/source/Blocks/BlockStairs.h deleted file mode 100644 index 8d259eee3..000000000 --- a/source/Blocks/BlockStairs.h +++ /dev/null @@ -1,152 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockStairsHandler : - public cBlockHandler -{ -public: - cBlockStairsHandler(BLOCKTYPE a_BlockType) : - cBlockHandler(a_BlockType) - { - - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = RotationToMetaData(a_Player->GetRotation()); - switch (a_BlockFace) - { - case BLOCK_FACE_TOP: break; - case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block - case BLOCK_FACE_EAST: - case BLOCK_FACE_NORTH: - case BLOCK_FACE_SOUTH: - case BLOCK_FACE_WEST: - { - // When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block - if (a_CursorY > 8) - { - a_BlockMeta |= 0x4; - } - break; - } - } - return true; - } - - // TODO: step sound - - - static NIBBLETYPE RotationToMetaData(double a_Rotation) - { - a_Rotation += 90 + 45; // So its not aligned with axis - if (a_Rotation > 360) - { - a_Rotation -= 360; - } - if ((a_Rotation >= 0) && (a_Rotation < 90)) - { - return 0x0; - } - else if ((a_Rotation >= 180) && (a_Rotation < 270)) - { - return 0x1; - } - else if ((a_Rotation >= 90) && (a_Rotation < 180)) - { - return 0x2; - } - else - { - return 0x3; - } - } - - - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x03; // East -> North - case 0x01: return TopBits | 0x02; // West -> South - case 0x02: return TopBits | 0x00; // South -> East - case 0x03: return TopBits | 0x01; // North -> West - } - // Not reachable, but to avoid a compiler warning: - return 0; - } - - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x02; // East -> South - case 0x01: return TopBits | 0x03; // West -> North - case 0x02: return TopBits | 0x01; // South -> West - case 0x03: return TopBits | 0x00; // North -> East - } - // Not reachable, but to avoid a compiler warning: - return 0; - } - - - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x00; // East -> East - case 0x01: return TopBits | 0x01; // West -> West - case 0x02: return TopBits | 0x03; // South -> North - case 0x03: return TopBits | 0x02; // North -> South - } - // Not reachable, but to avoid a compiler warning: - return 0; - } - - - virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override - { - // Toggle bit 3: - return (a_Meta & 0x0b) | ((~a_Meta) & 0x04); - } - - - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x01; // East -> West - case 0x01: return TopBits | 0x00; // West -> East - case 0x02: return TopBits | 0x02; // South -> South - case 0x03: return TopBits | 0x03; // North -> North - } - // Not reachable, but to avoid a compiler warning: - return 0; - } -} ; - - - - diff --git a/source/Blocks/BlockStems.h b/source/Blocks/BlockStems.h deleted file mode 100644 index ce02d9cb8..000000000 --- a/source/Blocks/BlockStems.h +++ /dev/null @@ -1,58 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -class cBlockStemsHandler : - public cBlockHandler -{ -public: - cBlockStemsHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - int ItemType = (m_BlockType == E_BLOCK_MELON_STEM) ? E_ITEM_MELON_SEEDS : E_ITEM_PUMPKIN_SEEDS; - a_Pickups.push_back(cItem(ItemType, 1, 0)); - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (Meta >= 7) - { - // Grow the produce: - a_World->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, m_BlockType); - } - else - { - // Grow the stem: - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta + 1); - } - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/Blocks/BlockStone.h b/source/Blocks/BlockStone.h deleted file mode 100644 index af4c6509a..000000000 --- a/source/Blocks/BlockStone.h +++ /dev/null @@ -1,29 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -class cBlockStoneHandler : - public cBlockHandler -{ -public: - cBlockStoneHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_COBBLESTONE, 1, 0)); - } -} ; - - - - diff --git a/source/Blocks/BlockSugarcane.h b/source/Blocks/BlockSugarcane.h deleted file mode 100644 index 28a60df80..000000000 --- a/source/Blocks/BlockSugarcane.h +++ /dev/null @@ -1,90 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockSugarcaneHandler : - public cBlockHandler -{ -public: - cBlockSugarcaneHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_SUGARCANE, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - if (a_RelY <= 0) - { - return false; - } - switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) - { - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_FARMLAND: - case E_BLOCK_SAND: - { - static const struct - { - int x, z; - } Coords[] = - { - {-1, 0}, - { 1, 0}, - { 0, -1}, - { 0, 1}, - } ; - a_RelY -= 1; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) - { - // Too close to the edge, cannot simulate - return true; - } - if (IsBlockWater(BlockType)) - { - return true; - } - } // for i - Coords[] - // Not directly neighboring a water block - return false; - } - case E_BLOCK_SUGARCANE: - { - return true; - } - } - return false; - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - a_World->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1); - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - diff --git a/source/Blocks/BlockTallGrass.h b/source/Blocks/BlockTallGrass.h deleted file mode 100644 index cd27ab7e6..000000000 --- a/source/Blocks/BlockTallGrass.h +++ /dev/null @@ -1,51 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockTallGrassHandler : - public cBlockHandler -{ -public: - cBlockTallGrassHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool DoesIgnoreBuildCollision(void) override - { - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Drop seeds, sometimes - MTRand r1; - if (r1.randInt(10) == 5) - { - a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); - } - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - diff --git a/source/Blocks/BlockTorch.h b/source/Blocks/BlockTorch.h deleted file mode 100644 index 36383a524..000000000 --- a/source/Blocks/BlockTorch.h +++ /dev/null @@ -1,277 +0,0 @@ -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockTorchHandler : - public cBlockHandler -{ -public: - cBlockTorchHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - // Find proper placement of torch - - if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM)) - { - a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); // Top or bottom faces clicked, find a suitable face - if (a_BlockFace == BLOCK_FACE_NONE) - { - // Client wouldn't have sent anything anyway, but whatever - return false; - } - } - else - { - // Not top or bottom faces, try to preserve whatever face was clicked - if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) - { - // Torch couldn't be placed on whatever face was clicked, last ditch resort - find another face - a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); - if (a_BlockFace == BLOCK_FACE_NONE) - { - return false; - } - } - } - - a_BlockType = m_BlockType; - a_BlockMeta = DirectionToMetaData(a_BlockFace); - return true; - } - - - static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export - { // tolua_export - switch (a_Direction) - { - case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0; - case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR; - case BLOCK_FACE_EAST: return E_META_TORCH_EAST; - case BLOCK_FACE_WEST: return E_META_TORCH_WEST; - case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH; - case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH; - default: - { - ASSERT(!"Unhandled torch direction!"); - break; - } - }; - return 0x0; - } // tolua_export - - - static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export - { // tolua_export - switch (a_MetaData) - { - case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground - case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP; - case E_META_TORCH_EAST: return BLOCK_FACE_EAST; - case E_META_TORCH_WEST: return BLOCK_FACE_WEST; - case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH; - case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH; - default: - { - ASSERT(!"Unhandled torch metadata"); - break; - } - } - return 0; - } // tolua_export - - - static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos) - { - switch (a_TorchMeta) - { - case 0x0: - case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0))); - case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1))); - case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1))); - case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))); - case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0))); - default: - { - ASSERT(!"Unhandled torch meta!"); - break; - } - } - return false; - } - - - static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_BlockFace) - { - if ( !g_BlockIsTorchPlaceable[a_BlockType] ) - { - return (a_BlockFace == BLOCK_FACE_TOP); // Allow placement only when torch upright - } - else - { - return true; - } - } - - - static bool TorchCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); - return CanBePlacedOn(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), a_BlockFace); - } - - - /// Finds a suitable face to place the torch, returning BLOCK_FACE_NONE on failure - static char FindSuitableFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - for (int i = 0; i <= 5; i++) - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, true); - BLOCKTYPE BlockInQuestion = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - - if ( - ((BlockInQuestion == E_BLOCK_GLASS) || - (BlockInQuestion == E_BLOCK_FENCE) || - (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) || - (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL)) && - (i == BLOCK_FACE_TOP) - ) - { - return i; - } - else if ((g_BlockIsTorchPlaceable[BlockInQuestion]) && (i != BLOCK_FACE_BOTTOM)) - { - return i; - } - else - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, false); - } - } - return BLOCK_FACE_NONE; - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - char Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - - AddFaceDirection(a_RelX, a_RelY, a_RelZ, Face, true); - BLOCKTYPE BlockInQuestion; - a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockInQuestion); - - if ( - (BlockInQuestion == E_BLOCK_GLASS) || - (BlockInQuestion == E_BLOCK_FENCE) || - (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) || - (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL) - ) - { - // Torches can be placed on tops of glass and fences, despite them being 'untorcheable' - // No need to check for upright orientation, it was done when the torch was placed - return true; - } - else if ( !g_BlockIsTorchPlaceable[BlockInQuestion] ) - { - return false; - } - else - { - return true; - } - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Always drop meta = 0 - a_Pickups.push_back(cItem(m_BlockType, 1, 0)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } - - - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x01: return TopBits | 0x04; // East -> North - case 0x02: return TopBits | 0x03; // West -> South - case 0x03: return TopBits | 0x01; // South -> East - case 0x04: return TopBits | 0x02; // North -> West - default: return a_Meta; // Floor -> Floor - } - } - - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x01: return TopBits | 0x03; // East -> South - case 0x02: return TopBits | 0x04; // West -> North - case 0x03: return TopBits | 0x02; // South -> West - case 0x04: return TopBits | 0x01; // North -> East - default: return a_Meta; // Floor -> Floor - } - } - - - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x03: return TopBits | 0x04; // South -> North - case 0x04: return TopBits | 0x03; // North -> South - default: return a_Meta; // Keep the rest - } - } - - - // Mirroring around the XZ plane doesn't make sense for floor torches, - // the others stay the same, so let's keep all the metas the same. - // The base class does tht for us, no need to override MetaMirrorXZ() - - - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x01: return TopBits | 0x02; // East -> West - case 0x02: return TopBits | 0x01; // West -> East - default: return a_Meta; // Keep the rest - } - } -} ; - - - - diff --git a/source/Blocks/BlockVine.h b/source/Blocks/BlockVine.h deleted file mode 100644 index 2c9f67cab..000000000 --- a/source/Blocks/BlockVine.h +++ /dev/null @@ -1,201 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockVineHandler : - public cBlockHandler -{ -public: - cBlockVineHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - // TODO: Disallow placement where the vine doesn't attach to something properly - BLOCKTYPE BlockType = 0; - NIBBLETYPE BlockMeta; - a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - if (BlockType == m_BlockType) - { - a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace); - } - else - { - a_BlockMeta = DirectionToMetaData(a_BlockFace); - } - a_BlockType = m_BlockType; - return true; - } - - - static NIBBLETYPE DirectionToMetaData(char a_BlockFace) - { - switch (a_BlockFace) - { - case BLOCK_FACE_NORTH: return 0x1; - case BLOCK_FACE_SOUTH: return 0x4; - case BLOCK_FACE_WEST: return 0x8; - case BLOCK_FACE_EAST: return 0x2; - default: return 0x0; - } - } - - - static char MetaDataToDirection(NIBBLETYPE a_MetaData) - { - switch(a_MetaData) - { - case 0x1: return BLOCK_FACE_NORTH; - case 0x4: return BLOCK_FACE_SOUTH; - case 0x8: return BLOCK_FACE_WEST; - case 0x2: return BLOCK_FACE_EAST; - default: return BLOCK_FACE_TOP; - } - } - - - /// Returns true if the specified block type is good for vines to attach to - static bool IsBlockAttachable(BLOCKTYPE a_BlockType) - { - return (a_BlockType == E_BLOCK_LEAVES) || g_BlockIsSolid[a_BlockType]; - } - - - /// Returns the meta that has the maximum allowable sides of the vine, given the surroundings - NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) - { - static const struct - { - int x, z; - int Bit; - } Coords[] = - { - { 0, 1, 1}, // south, ZP - {-1, 0, 2}, // west, XM - { 0, -1, 4}, // north, ZM - { 1, 0, 8}, // east, XP - } ; - int res = 0; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if ( - a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && - IsBlockAttachable(BlockType) - ) - { - res |= Coords[i].Bit; - } - } - return res; - } - - - void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override - { - NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); - NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelX, a_RelY, a_RelZ); - - // Check if vine above us, add its meta to MaxMeta - if ((a_RelY < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == m_BlockType)) - { - MaxMeta |= a_Chunk.GetMeta(a_RelX, a_RelY + 1, a_RelZ); - } - - NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal - if (Common != CurMeta) - { - // There is a neighbor missing, need to update the meta or even destroy the block - bool HasTop = (a_RelY < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ)); - if ((Common == 0) && !HasTop) - { - // The vine just lost all its support, destroy the block: - if (DoesDropOnUnsuitable()) - { - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); - } - a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); - return; - } - a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Common); - } - else - { - // Wake up the simulators for this block: - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); - } - } - - - virtual bool DoesIgnoreBuildCollision(void) override - { - return true; - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } - - - virtual bool DoesDropOnUnsuitable(void) override - { - return false; - } - - virtual void OnUpdate(cWorld * a_World, int X, int Y, int Z) - { - if (a_World->GetBlock(X, Y - 1, Z) == E_BLOCK_AIR) - { - a_World->SetBlock(X, Y - 1, Z, E_BLOCK_VINES, a_World->GetBlockMeta(X, Y, Z)); - } - } - - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override - { - return ((a_Meta >> 1) | (a_Meta << 3)) & 0x0f; // Rotate bits to the right - } - - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override - { - return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left - } - - - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override - { - // Bits 2 and 4 stay, bits 1 and 3 swap - return ((a_Meta & 0x0a) | ((a_Meta & 0x01) << 2) | ((a_Meta & 0x04) >> 2)); - } - - - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override - { - // Bits 1 and 3 stay, bits 2 and 4 swap - return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2)); - } -} ; - - - - diff --git a/source/Blocks/BlockWood.h b/source/Blocks/BlockWood.h deleted file mode 100644 index cb5ee995a..000000000 --- a/source/Blocks/BlockWood.h +++ /dev/null @@ -1,72 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockWoodHandler : public cBlockHandler -{ -public: - cBlockWoodHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); - a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta); - return true; - } - - - inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace, NIBBLETYPE a_WoodMeta) - { - switch (a_BlockFace) - { - case BLOCK_FACE_YM: - case BLOCK_FACE_YP: - { - return a_WoodMeta; // Top or bottom, just return original - } - - case BLOCK_FACE_ZP: - case BLOCK_FACE_ZM: - { - return a_WoodMeta | 0x8; // North or south - } - - case BLOCK_FACE_XP: - case BLOCK_FACE_XM: - { - return a_WoodMeta | 0x4; // East or west - } - - default: - { - ASSERT(!"Unhandled block face!"); - return a_WoodMeta | 0xC; // No idea, give a special meta (all sides bark) - } - } - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/Blocks/BlockWorkbench.h b/source/Blocks/BlockWorkbench.h deleted file mode 100644 index a2cc6119c..000000000 --- a/source/Blocks/BlockWorkbench.h +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../UI/Window.h" -#include "../Entities/Player.h" - - - - - -class cBlockWorkbenchHandler: - public cBlockHandler -{ -public: - cBlockWorkbenchHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override - { - cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ); - a_Player->OpenWindow(Window); - } - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - diff --git a/source/BoundingBox.cpp b/source/BoundingBox.cpp deleted file mode 100644 index 02602992e..000000000 --- a/source/BoundingBox.cpp +++ /dev/null @@ -1,331 +0,0 @@ - -// BoundingBox.cpp - -// Implements the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords - -#include "Globals.h" -#include "BoundingBox.h" -#include "Defines.h" - - - - - -#if 0 - -/// A simple self-test that is executed on program start, used to verify bbox functionality -class SelfTest -{ -public: - SelfTest(void) - { - Vector3d Min(1, 1, 1); - Vector3d Max(2, 2, 2); - Vector3d LineDefs[] = - { - Vector3d(1.5, 4, 1.5), Vector3d(1.5, 3, 1.5), // Should intersect at 2, face 1 (YP) - Vector3d(1.5, 0, 1.5), Vector3d(1.5, 4, 1.5), // Should intersect at 0.25, face 0 (YM) - Vector3d(0, 0, 0), Vector3d(2, 2, 2), // Should intersect at 0.5, face 0, 3 or 5 (anyM) - Vector3d(0.999, 0, 1.5), Vector3d(0.999, 4, 1.5), // Should not intersect - Vector3d(1.999, 0, 1.5), Vector3d(1.999, 4, 1.5), // Should intersect at 0.25, face 0 (YM) - Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5), // Should not intersect - } ; - for (int i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++) - { - double LineCoeff; - char Face; - Vector3d Line1 = LineDefs[2 * i]; - Vector3d Line2 = LineDefs[2 * i + 1]; - bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face); - printf("LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n", - Line1.x, Line1.y, Line1.z, - Line2.x, Line2.y, Line2.z, - res ? 1 : 0, LineCoeff, Face - ); - } // for i - LineDefs[] - printf("BoundingBox selftest complete."); - } -} Test; - -#endif - - - - - -cBoundingBox::cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ) : - m_Min(a_MinX, a_MinY, a_MinZ), - m_Max(a_MaxX, a_MaxY, a_MaxZ) -{ -} - - - - - -cBoundingBox::cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max) : - m_Min(a_Min), - m_Max(a_Max) -{ -} - - - - - -cBoundingBox::cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height) : - m_Min(a_Pos.x - a_Radius, a_Pos.y, a_Pos.z - a_Radius), - m_Max(a_Pos.x + a_Radius, a_Pos.y + a_Height, a_Pos.z + a_Radius) -{ -} - - - - - -cBoundingBox::cBoundingBox(const cBoundingBox & a_Orig) : - m_Min(a_Orig.m_Min), - m_Max(a_Orig.m_Max) -{ -} - - - - - -void cBoundingBox::Move(double a_OffX, double a_OffY, double a_OffZ) -{ - m_Min.x += a_OffX; - m_Min.y += a_OffY; - m_Min.z += a_OffZ; - m_Max.x += a_OffX; - m_Max.y += a_OffY; - m_Max.z += a_OffZ; -} - - - - - -void cBoundingBox::Move(const Vector3d & a_Off) -{ - m_Min.x += a_Off.x; - m_Min.y += a_Off.y; - m_Min.z += a_Off.z; - m_Max.x += a_Off.x; - m_Max.y += a_Off.y; - m_Max.z += a_Off.z; -} - - - - - -void cBoundingBox::Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ) -{ - m_Min.x -= a_ExpandX; - m_Min.y -= a_ExpandY; - m_Min.z -= a_ExpandZ; - m_Max.x += a_ExpandX; - m_Max.y += a_ExpandY; - m_Max.z += a_ExpandZ; -} - - - - - -bool cBoundingBox::DoesIntersect(const cBoundingBox & a_Other) -{ - return ( - ((a_Other.m_Min.x <= m_Max.x) && (a_Other.m_Max.x >= m_Min.x)) && // X coords intersect - ((a_Other.m_Min.y <= m_Max.y) && (a_Other.m_Max.y >= m_Min.y)) && // Y coords intersect - ((a_Other.m_Min.z <= m_Max.z) && (a_Other.m_Max.z >= m_Min.z)) // Z coords intersect - ); -} - - - - - -cBoundingBox cBoundingBox::Union(const cBoundingBox & a_Other) -{ - return cBoundingBox( - std::min(m_Min.x, a_Other.m_Min.x), - std::min(m_Min.y, a_Other.m_Min.y), - std::min(m_Min.z, a_Other.m_Min.z), - std::max(m_Max.x, a_Other.m_Max.x), - std::max(m_Max.y, a_Other.m_Max.y), - std::max(m_Max.z, a_Other.m_Max.z) - ); -} - - - - - -bool cBoundingBox::IsInside(const Vector3d & a_Point) -{ - return IsInside(m_Min, m_Max, a_Point); -} - - - - - -bool cBoundingBox::IsInside(double a_X, double a_Y,double a_Z) -{ - return IsInside(m_Min, m_Max, a_X, a_Y, a_Z); -} - - - - - -bool cBoundingBox::IsInside(cBoundingBox & a_Other) -{ - // If both a_Other's coords are inside this, then the entire a_Other is inside - return (IsInside(a_Other.m_Min) && IsInside(a_Other.m_Max)); -} - - - - - -bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max) -{ - // If both coords are inside this, then the entire a_Other is inside - return (IsInside(a_Min) && IsInside(a_Max)); -} - - - - - -bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point) -{ - return ( - ((a_Point.x >= a_Min.x) && (a_Point.x <= a_Max.x)) && - ((a_Point.y >= a_Min.y) && (a_Point.y <= a_Max.y)) && - ((a_Point.z >= a_Min.z) && (a_Point.z <= a_Max.z)) - ); -} - - - - - -bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z) -{ - return ( - ((a_X >= a_Min.x) && (a_X <= a_Max.x)) && - ((a_Y >= a_Min.y) && (a_Y <= a_Max.y)) && - ((a_Z >= a_Min.z) && (a_Z <= a_Max.z)) - ); -} - - - - - -bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face) -{ - return CalcLineIntersection(m_Min, m_Max, a_Line1, a_Line2, a_LineCoeff, a_Face); -} - - - - - -bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face) -{ - if (IsInside(a_Min, a_Max, a_Line1)) - { - // The starting point is inside the bounding box. - a_LineCoeff = 0; - a_Face = BLOCK_FACE_NONE; // No faces hit - return true; - } - - char Face = BLOCK_FACE_NONE; - double Coeff = Vector3d::NO_INTERSECTION; - - // Check each individual bbox face for intersection with the line, remember the one with the lowest coeff - double c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Min.z); - if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) - { - Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM; - Coeff = c; - } - c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Max.z); - if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) - { - Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM; - Coeff = c; - } - c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Min.y); - if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) - { - Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM; - Coeff = c; - } - c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y); - if ((c >= 0) && (c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) - { - Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM; - Coeff = c; - } - c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Min.x); - if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) - { - Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM; - Coeff = c; - } - c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Max.x); - if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) - { - Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM; - Coeff = c; - } - - if (Coeff >= Vector3d::NO_INTERSECTION) - { - // There has been no intersection - return false; - } - - a_LineCoeff = Coeff; - a_Face = Face; - return true; -} - - - - - -bool cBoundingBox::Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection) -{ - a_Intersection.m_Min.x = std::max(m_Min.x, a_Other.m_Min.x); - a_Intersection.m_Max.x = std::min(m_Max.x, a_Other.m_Max.x); - if (a_Intersection.m_Min.x >= a_Intersection.m_Max.x) - { - return false; - } - a_Intersection.m_Min.y = std::max(m_Min.y, a_Other.m_Min.y); - a_Intersection.m_Max.y = std::min(m_Max.y, a_Other.m_Max.y); - if (a_Intersection.m_Min.y >= a_Intersection.m_Max.y) - { - return false; - } - a_Intersection.m_Min.z = std::max(m_Min.z, a_Other.m_Min.z); - a_Intersection.m_Max.z = std::min(m_Max.z, a_Other.m_Max.z); - if (a_Intersection.m_Min.z >= a_Intersection.m_Max.z) - { - return false; - } - return true; -} - - - - diff --git a/source/BoundingBox.h b/source/BoundingBox.h deleted file mode 100644 index ff9963989..000000000 --- a/source/BoundingBox.h +++ /dev/null @@ -1,90 +0,0 @@ - -// BoundingBox.h - -// Declares the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords - - - - -#pragma once - -#include "Vector3d.h" - - - - - -// tolua_begin - -/** Represents two sets of coords, minimum and maximum for each direction. -All the coords within those limits (inclusive the edges) are considered "inside" the box. -For intersection purposes, though, if the intersection is "sharp" in any coord (i. e. zero volume), -the boxes are considered non-intersecting. -*/ -class cBoundingBox -{ -public: - cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ); - cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max); - cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height); - cBoundingBox(const cBoundingBox & a_Orig); - - /// Moves the entire boundingbox by the specified offset - void Move(double a_OffX, double a_OffY, double a_OffZ); - - /// Moves the entire boundingbox by the specified offset - void Move(const Vector3d & a_Off); - - /// Expands the bounding box by the specified amount in each direction (so the box becomes larger by 2 * Expand in each direction) - void Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ); - - /// Returns true if the two bounding boxes intersect - bool DoesIntersect(const cBoundingBox & a_Other); - - /// Returns the union of the two bounding boxes - cBoundingBox Union(const cBoundingBox & a_Other); - - /// Returns true if the point is inside the bounding box - bool IsInside(const Vector3d & a_Point); - - /// Returns true if the point is inside the bounding box - bool IsInside(double a_X, double a_Y,double a_Z); - - /// Returns true if a_Other is inside this bounding box - bool IsInside(cBoundingBox & a_Other); - - /// Returns true if a boundingbox specified by a_Min and a_Max is inside this bounding box - bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max); - - /// Returns true if the specified point is inside the bounding box specified by its min/max corners - static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point); - - /// Returns true if the specified point is inside the bounding box specified by its min/max corners - static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z); - - /** Returns true if this bounding box is intersected by the line specified by its two points - Also calculates the distance along the line in which the intersection occurs (0 .. 1) - Only forward collisions (a_LineCoeff >= 0) are returned. - */ - bool CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face); - - /** Returns true if the specified bounding box is intersected by the line specified by its two points - Also calculates the distance along the line in which the intersection occurs (0 .. 1) and the face hit (BLOCK_FACE_ constants) - Only forward collisions (a_LineCoeff >= 0) are returned. - */ - static bool CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face); - - // tolua_end - - /// Calculates the intersection of the two bounding boxes; returns true if nonempty - bool Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection); - -protected: - Vector3d m_Min; - Vector3d m_Max; - -} ; // tolua_export - - - - diff --git a/source/ByteBuffer.cpp b/source/ByteBuffer.cpp deleted file mode 100644 index 1cdd2f430..000000000 --- a/source/ByteBuffer.cpp +++ /dev/null @@ -1,787 +0,0 @@ - -// ByteBuffer.cpp - -// Implements the cByteBuffer class representing a ringbuffer of bytes - -#include "Globals.h" - -#include "ByteBuffer.h" -#include "Endianness.h" -#include "OSSupport/IsThread.h" - - - - - -// If a string sent over the protocol is larger than this, a warning is emitted to the console -#define MAX_STRING_SIZE (512 KiB) - -#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false; // Check if at least Num bytes can be read from the buffer, return false if not -#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false; // Check if at least Num bytes can be written to the buffer, return false if not - - - - - -#if 0 - -/// Self-test of the VarInt-reading and writing code -class cByteBufferSelfTest -{ -public: - cByteBufferSelfTest(void) - { - TestRead(); - TestWrite(); - } - - void TestRead(void) - { - cByteBuffer buf(50); - buf.Write("\x05\xac\x02\x00", 4); - UInt32 v1; - ASSERT(buf.ReadVarInt(v1) && (v1 == 5)); - UInt32 v2; - ASSERT(buf.ReadVarInt(v2) && (v2 == 300)); - UInt32 v3; - ASSERT(buf.ReadVarInt(v3) && (v3 == 0)); - } - - void TestWrite(void) - { - cByteBuffer buf(50); - buf.WriteVarInt(5); - buf.WriteVarInt(300); - buf.WriteVarInt(0); - AString All; - buf.ReadAll(All); - ASSERT(All.size() == 4); - ASSERT(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0); - } -} g_ByteBufferTest; - -#endif - - - - - -#ifdef _DEBUG - -/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously -class cSingleThreadAccessChecker -{ -public: - cSingleThreadAccessChecker(unsigned long * a_ThreadID) : - m_ThreadID(a_ThreadID) - { - ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID())); - } - - ~cSingleThreadAccessChecker() - { - *m_ThreadID = 0; - } - -protected: - unsigned long * m_ThreadID; -} ; - -#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast(&m_ThreadID)) - -#else - #define CHECK_THREAD -#endif - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cByteBuffer: - -cByteBuffer::cByteBuffer(int a_BufferSize) : - m_Buffer(new char[a_BufferSize + 1]), - m_BufferSize(a_BufferSize + 1), - #ifdef _DEBUG - m_ThreadID(0), - #endif // _DEBUG - m_DataStart(0), - m_WritePos(0), - m_ReadPos(0) -{ - // Allocating one byte more than the buffer size requested, so that we can distinguish between - // completely-full and completely-empty states -} - - - - - -cByteBuffer::~cByteBuffer() -{ - CheckValid(); - delete[] m_Buffer; -} - - - - - -bool cByteBuffer::Write(const char * a_Bytes, int a_Count) -{ - CHECK_THREAD; - CheckValid(); - - // Store the current free space for a check after writing: - int CurFreeSpace = GetFreeSpace(); - int CurReadableSpace = GetReadableSpace(); - int WrittenBytes = 0; - - if (GetFreeSpace() < a_Count) - { - return false; - } - int TillEnd = m_BufferSize - m_WritePos; - if (TillEnd <= a_Count) - { - // Need to wrap around the ringbuffer end - if (TillEnd > 0) - { - memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd); - a_Bytes += TillEnd; - a_Count -= TillEnd; - WrittenBytes = TillEnd; - } - m_WritePos = 0; - } - - // We're guaranteed that we'll fit in a single write op - if (a_Count > 0) - { - memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count); - m_WritePos += a_Count; - WrittenBytes += a_Count; - } - - ASSERT(GetFreeSpace() == CurFreeSpace - WrittenBytes); - ASSERT(GetReadableSpace() == CurReadableSpace + WrittenBytes); - return true; -} - - - - - -int cByteBuffer::GetFreeSpace(void) const -{ - CHECK_THREAD; - CheckValid(); - if (m_WritePos >= m_DataStart) - { - // Wrap around the buffer end: - return m_BufferSize - m_WritePos + m_DataStart - 1; - } - // Single free space partition: - return m_DataStart - m_WritePos - 1; -} - - - - - -/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() -int cByteBuffer::GetUsedSpace(void) const -{ - CHECK_THREAD; - CheckValid(); - return m_BufferSize - GetFreeSpace() - 1; -} - - - - - -/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) -int cByteBuffer::GetReadableSpace(void) const -{ - CHECK_THREAD; - CheckValid(); - if (m_ReadPos > m_WritePos) - { - // Wrap around the buffer end: - return m_BufferSize - m_ReadPos + m_WritePos; - } - // Single readable space partition: - return m_WritePos - m_ReadPos ; -} - - - - - -bool cByteBuffer::CanReadBytes(int a_Count) const -{ - CHECK_THREAD; - CheckValid(); - return (a_Count <= GetReadableSpace()); -} - - - - - -bool cByteBuffer::CanWriteBytes(int a_Count) const -{ - CHECK_THREAD; - CheckValid(); - return (a_Count <= GetFreeSpace()); -} - - - - - -bool cByteBuffer::ReadChar(char & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(1); - ReadBuf(&a_Value, 1); - return true; -} - - - - - -bool cByteBuffer::ReadByte(unsigned char & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(1); - ReadBuf(&a_Value, 1); - return true; -} - - - - - -bool cByteBuffer::ReadBEShort(short & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(2); - ReadBuf(&a_Value, 2); - a_Value = ntohs(a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBEInt(int & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(4); - ReadBuf(&a_Value, 4); - a_Value = ntohl(a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBEInt64(Int64 & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(8); - ReadBuf(&a_Value, 8); - a_Value = NetworkToHostLong8(&a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBEFloat(float & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(4); - ReadBuf(&a_Value, 4); - a_Value = NetworkToHostFloat4(&a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBEDouble(double & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(8); - ReadBuf(&a_Value, 8); - a_Value = NetworkToHostDouble8(&a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBool(bool & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(1); - char Value = 0; - ReadBuf(&Value, 1); - a_Value = (Value != 0); - return true; -} - - - - - -bool cByteBuffer::ReadBEUTF16String16(AString & a_Value) -{ - CHECK_THREAD; - CheckValid(); - short Length; - if (!ReadBEShort(Length)) - { - return false; - } - if (Length < 0) - { - ASSERT(!"Negative string length? Are you sure?"); - return true; - } - return ReadUTF16String(a_Value, Length); -} - - - - - -bool cByteBuffer::ReadVarInt(UInt32 & a_Value) -{ - CHECK_THREAD; - CheckValid(); - UInt32 Value = 0; - int Shift = 0; - unsigned char b = 0; - do - { - NEEDBYTES(1); - ReadBuf(&b, 1); - Value = Value | (((Int64)(b & 0x7f)) << Shift); - Shift += 7; - } while ((b & 0x80) != 0); - a_Value = Value; - return true; -} - - - - - -bool cByteBuffer::ReadVarUTF8String(AString & a_Value) -{ - CHECK_THREAD; - CheckValid(); - UInt32 Size = 0; - if (!ReadVarInt(Size)) - { - return false; - } - if (Size > MAX_STRING_SIZE) - { - LOGWARNING("%s: String too large: %llu (%llu KiB)", __FUNCTION__, Size, Size / 1024); - } - return ReadString(a_Value, (int)Size); -} - - - - - -bool cByteBuffer::WriteChar(char a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(1); - return WriteBuf(&a_Value, 1); -} - - - - - -bool cByteBuffer::WriteByte(unsigned char a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(1); - return WriteBuf(&a_Value, 1); -} - - - - - -bool cByteBuffer::WriteBEShort(short a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(2); - short Converted = htons(a_Value); - return WriteBuf(&Converted, 2); -} - - - - - -bool cByteBuffer::WriteBEInt(int a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(4); - int Converted = HostToNetwork4(&a_Value); - return WriteBuf(&Converted, 4); -} - - - - - -bool cByteBuffer::WriteBEInt64(Int64 a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(8); - Int64 Converted = HostToNetwork8(&a_Value); - return WriteBuf(&Converted, 8); -} - - - - - -bool cByteBuffer::WriteBEFloat(float a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(4); - int Converted = HostToNetwork4(&a_Value); - return WriteBuf(&Converted, 4); -} - - - - - -bool cByteBuffer::WriteBEDouble(double a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(8); - Int64 Converted = HostToNetwork8(&a_Value); - return WriteBuf(&Converted, 8); -} - - - - - - -bool cByteBuffer::WriteBool(bool a_Value) -{ - CHECK_THREAD; - CheckValid(); - return WriteChar(a_Value ? 1 : 0); -} - - - - - -bool cByteBuffer::WriteBEUTF16String16(const AString & a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(2); - AString UTF16BE; - UTF8ToRawBEUTF16(a_Value.data(), a_Value.size(), UTF16BE); - WriteBEShort((short)(UTF16BE.size() / 2)); - PUTBYTES(UTF16BE.size()); - WriteBuf(UTF16BE.data(), UTF16BE.size()); - return true; -} - - - - - -bool cByteBuffer::WriteVarInt(UInt32 a_Value) -{ - CHECK_THREAD; - CheckValid(); - - // A 32-bit integer can be encoded by at most 5 bytes: - unsigned char b[5]; - int idx = 0; - do - { - b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00); - a_Value = a_Value >> 7; - idx++; - } while (a_Value > 0); - - return WriteBuf(b, idx); -} - - - - -bool cByteBuffer::WriteVarUTF8String(const AString & a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early. - bool res = WriteVarInt(a_Value.size()); - if (!res) - { - return false; - } - return WriteBuf(a_Value.data(), a_Value.size()); -} - - - - - -bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count) -{ - CHECK_THREAD; - CheckValid(); - ASSERT(a_Count >= 0); - NEEDBYTES(a_Count); - char * Dst = (char *)a_Buffer; // So that we can do byte math - int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; - ASSERT(BytesToEndOfBuffer >= 0); // Sanity check - if (BytesToEndOfBuffer <= a_Count) - { - // Reading across the ringbuffer end, read the first part and adjust parameters: - if (BytesToEndOfBuffer > 0) - { - memcpy(Dst, m_Buffer + m_ReadPos, BytesToEndOfBuffer); - Dst += BytesToEndOfBuffer; - a_Count -= BytesToEndOfBuffer; - } - m_ReadPos = 0; - } - - // Read the rest of the bytes in a single read (guaranteed to fit): - if (a_Count > 0) - { - memcpy(Dst, m_Buffer + m_ReadPos, a_Count); - m_ReadPos += a_Count; - } - return true; -} - - - - - -bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count) -{ - CHECK_THREAD; - CheckValid(); - ASSERT(a_Count >= 0); - PUTBYTES(a_Count); - char * Src = (char *)a_Buffer; // So that we can do byte math - int BytesToEndOfBuffer = m_BufferSize - m_WritePos; - if (BytesToEndOfBuffer <= a_Count) - { - // Reading across the ringbuffer end, read the first part and adjust parameters: - memcpy(m_Buffer + m_WritePos, Src, BytesToEndOfBuffer); - Src += BytesToEndOfBuffer; - a_Count -= BytesToEndOfBuffer; - m_WritePos = 0; - } - - // Read the rest of the bytes in a single read (guaranteed to fit): - if (a_Count > 0) - { - memcpy(m_Buffer + m_WritePos, Src, a_Count); - m_WritePos += a_Count; - } - return true; -} - - - - - -bool cByteBuffer::ReadString(AString & a_String, int a_Count) -{ - CHECK_THREAD; - CheckValid(); - ASSERT(a_Count >= 0); - NEEDBYTES(a_Count); - a_String.clear(); - a_String.reserve(a_Count); - int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; - ASSERT(BytesToEndOfBuffer >= 0); // Sanity check - if (BytesToEndOfBuffer <= a_Count) - { - // Reading across the ringbuffer end, read the first part and adjust parameters: - if (BytesToEndOfBuffer > 0) - { - a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer); - a_Count -= BytesToEndOfBuffer; - } - m_ReadPos = 0; - } - - // Read the rest of the bytes in a single read (guaranteed to fit): - if (a_Count > 0) - { - a_String.append(m_Buffer + m_ReadPos, a_Count); - m_ReadPos += a_Count; - } - return true; -} - - - - - -bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars) -{ - // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String - CHECK_THREAD; - CheckValid(); - ASSERT(a_NumChars >= 0); - AString RawData; - if (!ReadString(RawData, a_NumChars * 2)) - { - return false; - } - RawBEToUTF8((short *)(RawData.data()), a_NumChars, a_String); - return true; -} - - - - - -bool cByteBuffer::SkipRead(int a_Count) -{ - CHECK_THREAD; - CheckValid(); - ASSERT(a_Count >= 0); - if (!CanReadBytes(a_Count)) - { - return false; - } - AdvanceReadPos(a_Count); - return true; -} - - - - - -void cByteBuffer::ReadAll(AString & a_Data) -{ - CHECK_THREAD; - CheckValid(); - ReadString(a_Data, GetReadableSpace()); -} - - - - - -void cByteBuffer::CommitRead(void) -{ - CHECK_THREAD; - CheckValid(); - m_DataStart = m_ReadPos; -} - - - - - -void cByteBuffer::ResetRead(void) -{ - CHECK_THREAD; - CheckValid(); - m_ReadPos = m_DataStart; -} - - - - - -void cByteBuffer::ReadAgain(AString & a_Out) -{ - // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed) - // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party - CHECK_THREAD; - CheckValid(); - int DataStart = m_DataStart; - if (m_ReadPos < m_DataStart) - { - // Across the ringbuffer end, read the first part and adjust next part's start: - a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart); - DataStart = 0; - } - a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart); -} - - - - - -void cByteBuffer::AdvanceReadPos(int a_Count) -{ - CHECK_THREAD; - CheckValid(); - m_ReadPos += a_Count; - if (m_ReadPos > m_BufferSize) - { - m_ReadPos -= m_BufferSize; - } -} - - - - - -void cByteBuffer::CheckValid(void) const -{ - ASSERT(m_ReadPos >= 0); - ASSERT(m_ReadPos < m_BufferSize); - ASSERT(m_WritePos >= 0); - ASSERT(m_WritePos < m_BufferSize); -} - - - - diff --git a/source/ByteBuffer.h b/source/ByteBuffer.h deleted file mode 100644 index 21abb0377..000000000 --- a/source/ByteBuffer.h +++ /dev/null @@ -1,137 +0,0 @@ - -// ByteStream.h - -// Interfaces to the cByteBuffer class representing a ringbuffer of bytes - - - - - -#pragma once - - - - - -/** An object that can store incoming bytes and lets its clients read the bytes sequentially -The bytes are stored in a ringbuffer of constant size; if more than that size -is requested, the write operation fails. -The bytes stored can be retrieved using various ReadXXX functions; these assume that the needed -number of bytes are present in the buffer (ASSERT; for performance reasons). -The reading doesn't actually remove the bytes, it only moves the internal read ptr. -To remove the bytes, call CommitRead(). -To re-start reading from the beginning, call ResetRead(). -This class doesn't implement thread safety, the clients of this class need to provide -their own synchronization. -*/ -class cByteBuffer -{ -public: - cByteBuffer(int a_BufferSize); - ~cByteBuffer(); - - /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not - bool Write(const char * a_Bytes, int a_Count); - - /// Returns the number of bytes that can be successfully written to the ringbuffer - int GetFreeSpace(void) const; - - /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() - int GetUsedSpace(void) const; - - /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) - int GetReadableSpace(void) const; - - /// Returns true if the specified amount of bytes are available for reading - bool CanReadBytes(int a_Count) const; - - /// Returns true if the specified amount of bytes are available for writing - bool CanWriteBytes(int a_Count) const; - - // Read the specified datatype and advance the read pointer; return true if successfully read: - bool ReadChar (char & a_Value); - bool ReadByte (unsigned char & a_Value); - bool ReadBEShort (short & a_Value); - bool ReadBEInt (int & a_Value); - bool ReadBEInt64 (Int64 & a_Value); - bool ReadBEFloat (float & a_Value); - bool ReadBEDouble (double & a_Value); - bool ReadBool (bool & a_Value); - bool ReadBEUTF16String16(AString & a_Value); // string length as BE short, then string as UTF-16BE - bool ReadVarInt (UInt32 & a_Value); - bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8 - - /// Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...) - template bool ReadVarInt(T & a_Value) - { - UInt32 v; - bool res = ReadVarInt(v); - if (res) - { - a_Value = v; - } - return res; - } - - // Write the specified datatype; return true if successfully written - bool WriteChar (char a_Value); - bool WriteByte (unsigned char a_Value); - bool WriteBEShort (short a_Value); - bool WriteBEInt (int a_Value); - bool WriteBEInt64 (Int64 a_Value); - bool WriteBEFloat (float a_Value); - bool WriteBEDouble (double a_Value); - bool WriteBool (bool a_Value); - bool WriteBEUTF16String16(const AString & a_Value); // string length as BE short, then string as UTF-16BE - bool WriteVarInt (UInt32 a_Value); - bool WriteVarUTF8String (const AString & a_Value); // string length as VarInt, then string as UTF-8 - - /// Reads a_Count bytes into a_Buffer; returns true if successful - bool ReadBuf(void * a_Buffer, int a_Count); - - /// Writes a_Count bytes into a_Buffer; returns true if successful - bool WriteBuf(const void * a_Buffer, int a_Count); - - /// Reads a_Count bytes into a_String; returns true if successful - bool ReadString(AString & a_String, int a_Count); - - /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String - bool ReadUTF16String(AString & a_String, int a_NumChars); - - /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer - bool SkipRead(int a_Count); - - /// Reads all available data into a_Data - void ReadAll(AString & a_Data); - - /// Removes the bytes that have been read from the ringbuffer - void CommitRead(void); - - /// Restarts next reading operation at the start of the ringbuffer - void ResetRead(void); - - /// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication - void ReadAgain(AString & a_Out); - - /// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs - void CheckValid(void) const; - -protected: - char * m_Buffer; - int m_BufferSize; // Total size of the ringbuffer - - #ifdef _DEBUG - unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker - #endif // _DEBUG - - int m_DataStart; // Where the data starts in the ringbuffer - int m_WritePos; // Where the data ends in the ringbuffer - int m_ReadPos; // Where the next read will start in the ringbuffer - - /// Advances the m_ReadPos by a_Count bytes - void AdvanceReadPos(int a_Count); -} ; - - - - diff --git a/source/ChatColor.cpp b/source/ChatColor.cpp deleted file mode 100644 index 2b223ee76..000000000 --- a/source/ChatColor.cpp +++ /dev/null @@ -1,39 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "ChatColor.h" - -const std::string cChatColor::Color = "\xc2\xa7"; // or in other words: "§" in UTF-8 -const std::string cChatColor::Delimiter = "\xc2\xa7"; // or in other words: "§" in UTF-8 -const std::string cChatColor::Black = cChatColor::Color + "0"; -const std::string cChatColor::Navy = cChatColor::Color + "1"; -const std::string cChatColor::Green = cChatColor::Color + "2"; -const std::string cChatColor::Blue = cChatColor::Color + "3"; -const std::string cChatColor::Red = cChatColor::Color + "4"; -const std::string cChatColor::Purple = cChatColor::Color + "5"; -const std::string cChatColor::Gold = cChatColor::Color + "6"; -const std::string cChatColor::LightGray = cChatColor::Color + "7"; -const std::string cChatColor::Gray = cChatColor::Color + "8"; -const std::string cChatColor::DarkPurple = cChatColor::Color + "9"; -const std::string cChatColor::LightGreen = cChatColor::Color + "a"; -const std::string cChatColor::LightBlue = cChatColor::Color + "b"; -const std::string cChatColor::Rose = cChatColor::Color + "c"; -const std::string cChatColor::LightPurple = cChatColor::Color + "d"; -const std::string cChatColor::Yellow = cChatColor::Color + "e"; -const std::string cChatColor::White = cChatColor::Color + "f"; - -const std::string cChatColor::Random = cChatColor::Color + "k"; -const std::string cChatColor::Bold = cChatColor::Color + "l"; -const std::string cChatColor::Strikethrough = cChatColor::Color + "m"; -const std::string cChatColor::Underlined = cChatColor::Color + "n"; -const std::string cChatColor::Italic = cChatColor::Color + "o"; -const std::string cChatColor::Plain = cChatColor::Color + "r"; - -const std::string cChatColor::MakeColor( char a_Color ) -{ - return cChatColor::Color + a_Color; -} - - - - diff --git a/source/ChatColor.h b/source/ChatColor.h deleted file mode 100644 index 85b10f400..000000000 --- a/source/ChatColor.h +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once - - - - - -// tolua_begin -class cChatColor -{ -public: - static const std::string Color; - static const std::string Delimiter; - - static const std::string Black; - static const std::string Navy; - static const std::string Green; - static const std::string Blue; - static const std::string Red; - static const std::string Purple; - static const std::string Gold; - static const std::string LightGray; - static const std::string Gray; - static const std::string DarkPurple; - static const std::string LightGreen; - static const std::string LightBlue; - static const std::string Rose; - static const std::string LightPurple; - static const std::string Yellow; - static const std::string White; - - // Styles ( source: http://wiki.vg/Chat ) - static const std::string Random; - static const std::string Bold; - static const std::string Strikethrough; - static const std::string Underlined; - static const std::string Italic; - static const std::string Plain; - - static const std::string MakeColor( char a_Color ); -}; - -// tolua_end diff --git a/source/Chunk.cpp b/source/Chunk.cpp deleted file mode 100644 index 1c937c894..000000000 --- a/source/Chunk.cpp +++ /dev/null @@ -1,2732 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#ifndef _WIN32 - #include -#endif - - -#include "Chunk.h" -#include "World.h" -#include "ClientHandle.h" -#include "Server.h" -#include "zlib.h" -#include "Defines.h" -#include "BlockEntities/ChestEntity.h" -#include "BlockEntities/DispenserEntity.h" -#include "BlockEntities/DropperEntity.h" -#include "BlockEntities/FurnaceEntity.h" -#include "BlockEntities/HopperEntity.h" -#include "BlockEntities/JukeboxEntity.h" -#include "BlockEntities/NoteEntity.h" -#include "BlockEntities/SignEntity.h" -#include "Entities/Pickup.h" -#include "Item.h" -#include "Noise.h" -#include "Root.h" -#include "MersenneTwister.h" -#include "Entities/Player.h" -#include "BlockArea.h" -#include "PluginManager.h" -#include "Blocks/BlockHandler.h" -#include "Simulator/FluidSimulator.h" -#include "MobCensus.h" -#include "MobSpawner.h" - - -#include - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// sSetBlock: - -sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) // absolute block position - : x( a_BlockX ) - , y( a_BlockY ) - , z( a_BlockZ ) - , BlockType( a_BlockType ) - , BlockMeta( a_BlockMeta ) -{ - cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cChunk: - -cChunk::cChunk( - int a_ChunkX, int a_ChunkY, int a_ChunkZ, - cChunkMap * a_ChunkMap, cWorld * a_World, - cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP -) - : m_PosX( a_ChunkX ) - , m_PosY( a_ChunkY ) - , m_PosZ( a_ChunkZ ) - , m_BlockTickX( 0 ) - , m_BlockTickY( 0 ) - , m_BlockTickZ( 0 ) - , m_World( a_World ) - , m_ChunkMap(a_ChunkMap) - , m_IsValid(false) - , m_IsLightValid(false) - , m_IsDirty(false) - , m_IsSaving(false) - , m_StayCount(0) - , m_NeighborXM(a_NeighborXM) - , m_NeighborXP(a_NeighborXP) - , m_NeighborZM(a_NeighborZM) - , m_NeighborZP(a_NeighborZP) - , m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()) - , m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()) -{ - if (a_NeighborXM != NULL) - { - a_NeighborXM->m_NeighborXP = this; - } - if (a_NeighborXP != NULL) - { - a_NeighborXP->m_NeighborXM = this; - } - if (a_NeighborZM != NULL) - { - a_NeighborZM->m_NeighborZP = this; - } - if (a_NeighborZP != NULL) - { - a_NeighborZP->m_NeighborZM = this; - } -} - - - - - -cChunk::~cChunk() -{ - cPluginManager::Get()->CallHookChunkUnloaded(m_World, m_PosX, m_PosZ); - - // LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId() ); - - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - delete *itr; - } - m_BlockEntities.clear(); - - // Remove and destroy all entities that are not players: - cEntityList Entities; - std::swap(Entities, m_Entities); // Need another list because cEntity destructors check if they've been removed from chunk - for (cEntityList::const_iterator itr = Entities.begin(); itr != Entities.end(); ++itr) - { - if (!(*itr)->IsPlayer()) - { - (*itr)->Destroy(false); - delete *itr; - } - } - - if (m_NeighborXM != NULL) - { - m_NeighborXM->m_NeighborXP = NULL; - } - if (m_NeighborXP != NULL) - { - m_NeighborXP->m_NeighborXM = NULL; - } - if (m_NeighborZM != NULL) - { - m_NeighborZM->m_NeighborZP = NULL; - } - if (m_NeighborZP != NULL) - { - m_NeighborZP->m_NeighborZM = NULL; - } - delete m_WaterSimulatorData; - delete m_LavaSimulatorData; -} - - - - - -void cChunk::SetValid(void) -{ - m_IsValid = true; - - m_World->GetChunkMap()->ChunkValidated(); -} - - - - - -void cChunk::MarkRegenerating(void) -{ - // Tell all clients attached to this chunk that they want this chunk: - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) - { - (*itr)->AddWantedChunk(m_PosX, m_PosZ); - } // for itr - m_LoadedByClient[] -} - - - - - -bool cChunk::CanUnload(void) -{ - return m_LoadedByClient.empty() && !m_IsDirty && (m_StayCount == 0); -} - - - - - -void cChunk::MarkSaving(void) -{ - m_IsSaving = true; -} - - - - - -void cChunk::MarkSaved(void) -{ - if (!m_IsSaving) - { - return; - } - m_IsDirty = false; -} - - - - - -void cChunk::MarkLoaded(void) -{ - m_IsDirty = false; - SetValid(); -} - - - - - -void cChunk::MarkLoadFailed(void) -{ - if (m_IsValid) - { - return; - } - - m_HasLoadFailed = true; -} - - - - - -void cChunk::GetAllData(cChunkDataCallback & a_Callback) -{ - a_Callback.HeightMap (&m_HeightMap); - a_Callback.BiomeData (&m_BiomeMap); - a_Callback.BlockTypes (m_BlockTypes); - a_Callback.BlockMeta (m_BlockMeta); - a_Callback.LightIsValid (m_IsLightValid); - a_Callback.BlockLight (m_BlockLight); - a_Callback.BlockSkyLight(m_BlockSkyLight); - - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) - { - a_Callback.Entity(*itr); - } - - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - a_Callback.BlockEntity(*itr); - } -} - - - - - -void cChunk::SetAllData( - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const HeightMap * a_HeightMap, - const BiomeMap & a_BiomeMap, - cBlockEntityList & a_BlockEntities -) -{ - memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap)); - - if (a_HeightMap != NULL) - { - memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap)); - } - - memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes)); - memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta)); - if (a_BlockLight != NULL) - { - memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); - } - if (a_BlockSkyLight != NULL) - { - memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight)); - } - - m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL); - - if (a_HeightMap == NULL) - { - CalculateHeightmap(); - } - - // Clear the block entities present - either the loader / saver has better, or we'll create empty ones: - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - delete *itr; - } - std::swap(a_BlockEntities, m_BlockEntities); - - // Set all block entities' World variable: - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - (*itr)->SetWorld(m_World); - } - - // Create block entities that the loader didn't load; fill them with defaults - CreateBlockEntities(); - - // Set the chunk data as valid. This may be needed for some simulators that perform actions upon block adding (Vaporize) - SetValid(); - - // Wake up all simulators for their respective blocks: - WakeUpSimulators(); - - m_HasLoadFailed = false; -} - - - - - -void cChunk::SetLight( - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight -) -{ - // TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation. - // Postponing until we see how bad it is :) - memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); - memcpy(m_BlockSkyLight, a_SkyLight, sizeof(m_BlockSkyLight)); - m_IsLightValid = true; -} - - - - - -void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes) -{ - memcpy(a_BlockTypes, m_BlockTypes, NumBlocks); -} - - - - - -void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) -{ - if ((a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas)) != (cBlockArea::baTypes | cBlockArea::baMetas)) - { - LOGWARNING("cChunk::WriteBlockArea(): unsupported datatype request, can write only types + metas (0x%x), requested 0x%x. Ignoring.", - (cBlockArea::baTypes | cBlockArea::baMetas), a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas) - ); - return; - } - - // SizeX, SizeZ are the dimensions of the block data to copy to the chunk (size of the geometric union) - - int BlockStartX = std::max(a_MinBlockX, m_PosX * cChunkDef::Width); - int BlockEndX = std::min(a_MinBlockX + a_Area.GetSizeX(), (m_PosX + 1) * cChunkDef::Width); - int BlockStartZ = std::max(a_MinBlockZ, m_PosZ * cChunkDef::Width); - int BlockEndZ = std::min(a_MinBlockZ + a_Area.GetSizeZ(), (m_PosZ + 1) * cChunkDef::Width); - int SizeX = BlockEndX - BlockStartX; - int SizeZ = BlockEndZ - BlockStartZ; - int OffX = BlockStartX - m_PosX * cChunkDef::Width; - int OffZ = BlockStartZ - m_PosZ * cChunkDef::Width; - int BaseX = BlockStartX - a_MinBlockX; - int BaseZ = BlockStartZ - a_MinBlockZ; - int SizeY = a_Area.GetSizeY(); - - // TODO: Improve this by not calling FastSetBlock() and doing the processing here - // so that the heightmap is touched only once for each column. - BLOCKTYPE * AreaBlockTypes = a_Area.GetBlockTypes(); - NIBBLETYPE * AreaBlockMetas = a_Area.GetBlockMetas(); - for (int y = 0; y < SizeY; y++) - { - int ChunkY = a_MinBlockY + y; - int AreaY = y; - for (int z = 0; z < SizeZ; z++) - { - int ChunkZ = OffZ + z; - int AreaZ = BaseZ + z; - for (int x = 0; x < SizeX; x++) - { - int ChunkX = OffX + x; - int AreaX = BaseX + x; - int idx = a_Area.MakeIndex(AreaX, AreaY, AreaZ); - BLOCKTYPE BlockType = AreaBlockTypes[idx]; - NIBBLETYPE BlockMeta = AreaBlockMetas[idx]; - FastSetBlock(ChunkX, ChunkY, ChunkZ, BlockType, BlockMeta); - } // for x - } // for z - } // for y -} - - - - - -/// Returns true if there is a block entity at the coords specified -bool cChunk::HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if ( - ((*itr)->GetPosX() == a_BlockX) && - ((*itr)->GetPosY() == a_BlockY) && - ((*itr)->GetPosZ() == a_BlockZ) - ) - { - return true; - } - } // for itr - m_BlockEntities[] - return false; -} - - - - - -/// Sets or resets the internal flag that prevents chunk from being unloaded -void cChunk::Stay(bool a_Stay) -{ - m_StayCount += (a_Stay ? 1 : -1); - ASSERT(m_StayCount >= 0); -} - - - - -void cChunk::CollectMobCensus(cMobCensus& toFill) -{ - toFill.CollectSpawnableChunk(*this); - std::list playerPositions; - cPlayer* currentPlayer; - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) - { - currentPlayer = (*itr)->GetPlayer(); - playerPositions.push_back(&(currentPlayer->GetPosition())); - } - - Vector3d currentPosition; - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) - { - //LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); - if ((*itr)->IsMob()) - { - cMonster& Monster = (cMonster&)(**itr); - currentPosition = Monster.GetPosition(); - for (std::list::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); itr2 ++) - { - toFill.CollectMob(Monster,*this,(currentPosition-**itr2).SqrLength()); - } - } - } // for itr - m_Entitites[] -} - - - - -void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ) -{ - ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff); - int Random = m_World->GetTickRandomNumber(0x00ffffff); - a_X = Random % (a_MaxX * 2); - a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2); - a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2); - a_X /= 2; - a_Y /= 2; - a_Z /= 2; -} - - - - - -void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z) -{ - // MG TODO : check if this kind of optimization (only one random call) is still needed - // MG TODO : if so propagate it - - getThreeRandomNumber(a_X, a_Y, a_Z, Width, Height-2, Width); - a_Y++; -} - - - - - -void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) -{ - int Center_X,Center_Y,Center_Z; - getRandomBlockCoords(Center_X,Center_Y,Center_Z); - - BLOCKTYPE PackCenterBlock = GetBlock(Center_X, Center_Y, Center_Z); - if (a_MobSpawner.CheckPackCenter(PackCenterBlock)) - { - a_MobSpawner.NewPack(); - int NumberOfTries = 0; - int NumberOfSuccess = 0; - int MaxNbOfSuccess = 4; // this can be changed during the process for Wolves and Ghass - while (NumberOfTries < 12 && NumberOfSuccess < MaxNbOfSuccess) - { - const int HorizontalRange = 20; // MG TODO : relocate - const int VerticalRange = 0; // MG TODO : relocate - int Try_X, Try_Y, Try_Z; - getThreeRandomNumber(Try_X, Try_Y, Try_Z, 2*HorizontalRange+1 , 2*VerticalRange+1 , 2*HorizontalRange+1); - Try_X -= HorizontalRange; - Try_Y -= VerticalRange; - Try_Z -= HorizontalRange; - Try_X += Center_X; - Try_Y += Center_Y; - Try_Z += Center_Z; - - ASSERT(Try_Y > 0); - ASSERT(Try_Y < cChunkDef::Height-1); - - EMCSBiome Biome = m_ChunkMap->GetBiomeAt (Try_X, Try_Z); - // MG TODO : - // Moon cycle (for slime) - // check player and playerspawn presence < 24 blocks - // check mobs presence on the block - - // MG TODO : check that "Level" really means Y - - NIBBLETYPE SkyLight = 0; - - NIBBLETYPE BlockLight = 0; - - if (IsLightValid()) - { - cEntity* newMob = a_MobSpawner.TryToSpawnHere(this, Try_X, Try_Y, Try_Z, Biome, MaxNbOfSuccess); - if (newMob) - { - int WorldX, WorldY, WorldZ; - PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ); - double ActualX = WorldX + 0.5; - double ActualZ = WorldZ + 0.5; - newMob->SetPosition(ActualX, WorldY, ActualZ); - LOGD("Spawning %s #%i at %d,%d,%d",newMob->GetClass(),newMob->GetUniqueID(),WorldX, WorldY, WorldZ); - NumberOfSuccess++; - } - } - - NumberOfTries++; - } - } - -} - - - - - -void cChunk::Tick(float a_Dt) -{ - BroadcastPendingBlockChanges(); - - // Unload the chunk from all clients that have queued unloading: - for (cClientHandleList::iterator itr = m_UnloadQuery.begin(), end = m_UnloadQuery.end(); itr != end; ++itr) - { - (*itr)->SendUnloadChunk(m_PosX, m_PosZ); - } - m_UnloadQuery.clear(); - - // Set all blocks that have been queued for setting later: - ProcessQueuedSetBlocks(); - - CheckBlocks(); - - // Tick simulators: - m_World->GetSimulatorManager()->SimulateChunk(a_Dt, m_PosX, m_PosZ, this); - - TickBlocks(); - - // Tick all block entities in this chunk: - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - m_IsDirty = (*itr)->Tick(a_Dt, *this) | m_IsDirty; - } - - // Tick all entities in this chunk (except mobs): - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) - { - // Mobs are tickes inside MobTick (as we don't have to tick them if they are far away from players) - if (!((*itr)->IsMob())) - { - (*itr)->Tick(a_Dt, *this); - } - } // for itr - m_Entitites[] - - // Remove all entities that were scheduled for removal: - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();) - { - if ((*itr)->IsDestroyed()) - { - LOGD("Destroying entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); - cEntity * ToDelete = *itr; - itr = m_Entities.erase(itr); - delete ToDelete; - continue; - } - itr++; - } // for itr - m_Entitites[] - - // If any entity moved out of the chunk, move it to the neighbor: - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();) - { - if ( - ((*itr)->GetChunkX() != m_PosX) || - ((*itr)->GetChunkZ() != m_PosZ) - ) - { - MoveEntityToNewChunk(*itr); - itr = m_Entities.erase(itr); - } - else - { - ++itr; - } - } - - ApplyWeatherToTop(); -} - - - - - -void cChunk::MoveEntityToNewChunk(cEntity * a_Entity) -{ - cChunk * Neighbor = GetNeighborChunk(a_Entity->GetChunkX() * cChunkDef::Width, a_Entity->GetChunkZ() * cChunkDef::Width); - if (Neighbor == NULL) - { - Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); - if (Neighbor == NULL) - { - // TODO: What to do with this? - LOGWARNING("%s: Failed to move entity, destination chunk unreachable. Entity lost", __FUNCTION__); - return; - } - } - - ASSERT(Neighbor != this); // Moving into the same chunk? wtf? - - Neighbor->AddEntity(a_Entity); - - class cMover : - public cClientDiffCallback - { - virtual void Removed(cClientHandle * a_Client) override - { - a_Client->SendDestroyEntity(*m_Entity); - } - - virtual void Added(cClientHandle * a_Client) override - { - m_Entity->SpawnOn(*a_Client); - } - - cEntity * m_Entity; - - public: - cMover(cEntity * a_Entity) : - m_Entity(a_Entity) - {} - } Mover(a_Entity); - - m_ChunkMap->CompareChunkClients(this, Neighbor, Mover); -} - - - - - -void cChunk::ProcessQueuedSetBlocks(void) -{ - Int64 CurrTick = m_World->GetWorldAge(); - for (sSetBlockQueueVector::iterator itr = m_SetBlockQueue.begin(); itr != m_SetBlockQueue.end();) - { - if (itr->m_Tick < CurrTick) - { - // Not yet - ++itr; - continue; - } - else - { - // Now is the time to set the block - SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); - itr = m_SetBlockQueue.erase(itr); - } - } // for itr - m_SetBlockQueue[] -} - - - - - -void cChunk::BroadcastPendingBlockChanges(void) -{ - if (m_PendingSendBlocks.empty()) - { - return; - } - - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) - { - (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks); - } - m_PendingSendBlocks.clear(); -} - - - - - -void cChunk::CheckBlocks(void) -{ - if (m_ToTickBlocks.size() == 0) - { - return; - } - std::vector ToTickBlocks; - std::swap(m_ToTickBlocks, ToTickBlocks); - - for (std::vector::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr) - { - unsigned int index = (*itr); - Vector3i BlockPos = IndexToCoordinate(index); - - cBlockHandler * Handler = BlockHandler(GetBlock(index)); - Handler->Check(BlockPos.x, BlockPos.y, BlockPos.z, *this); - } // for itr - ToTickBlocks[] -} - - - - - -void cChunk::TickBlocks(void) -{ - // Tick dem blocks - // _X: We must limit the random number or else we get a nasty int overflow bug ( http://forum.mc-server.org/showthread.php?tid=457 ) - int RandomX = m_World->GetTickRandomNumber(0x00ffffff); - int RandomY = m_World->GetTickRandomNumber(0x00ffffff); - int RandomZ = m_World->GetTickRandomNumber(0x00ffffff); - int TickX = m_BlockTickX; - int TickY = m_BlockTickY; - int TickZ = m_BlockTickZ; - - // This for loop looks disgusting, but it actually does a simple thing - first processes m_BlockTick, then adds random to it - // This is so that SetNextBlockTick() works - for (int i = 0; i < 50; i++, - - // This weird construct (*2, then /2) is needed, - // otherwise the blocktick distribution is too biased towards even coords! - - TickX = (TickX + RandomX) % (Width * 2), - TickY = (TickY + RandomY) % (Height * 2), - TickZ = (TickZ + RandomZ) % (Width * 2), - m_BlockTickX = TickX / 2, - m_BlockTickY = TickY / 2, - m_BlockTickZ = TickZ / 2 - ) - { - - if (m_BlockTickY > cChunkDef::GetHeight(m_HeightMap, m_BlockTickX, m_BlockTickZ)) - { - continue; // It's all air up here - } - - unsigned int Index = MakeIndexNoCheck(m_BlockTickX, m_BlockTickY, m_BlockTickZ); - cBlockHandler * Handler = BlockHandler(m_BlockTypes[Index]); - ASSERT(Handler != NULL); // Happenned on server restart, FS #243 - Handler->OnUpdate(m_World, m_BlockTickX + m_PosX * Width, m_BlockTickY, m_BlockTickZ + m_PosZ * Width); - } // for i - tickblocks -} - - - - - -void cChunk::ApplyWeatherToTop() -{ - if ( - (m_World->GetTickRandomNumber(100) != 0) || - ( - (m_World->GetWeather() != eWeather_Rain) && - (m_World->GetWeather() != eWeather_ThunderStorm) - ) - ) - { - // Not the right weather, or not at this tick; bail out - return; - } - - int X = m_World->GetTickRandomNumber(15); - int Z = m_World->GetTickRandomNumber(15); - switch (GetBiomeAt(X, Z)) - { - case biTaiga: - case biFrozenOcean: - case biFrozenRiver: - case biIcePlains: - case biIceMountains: - case biTaigaHills: - { - // TODO: Check light levels, don't snow over when the BlockLight is higher than (7?) - int Height = GetHeight(X, Z); - BLOCKTYPE TopBlock = GetBlock(X, Height, Z); - NIBBLETYPE TopMeta = GetMeta (X, Height, Z); - if (m_World->IsDeepSnowEnabled() && (TopBlock == E_BLOCK_SNOW)) - { - int MaxSize = 7; - BLOCKTYPE BlockType[4]; - NIBBLETYPE BlockMeta[4]; - UnboundedRelGetBlock(X - 1, Height, Z, BlockType[0], BlockMeta[0]); - UnboundedRelGetBlock(X + 1, Height, Z, BlockType[1], BlockMeta[1]); - UnboundedRelGetBlock(X, Height, Z - 1, BlockType[2], BlockMeta[2]); - UnboundedRelGetBlock(X, Height, Z + 1, BlockType[3], BlockMeta[3]); - for (int i = 0; i < 4; i++) - { - switch (BlockType[i]) - { - case E_BLOCK_AIR: - { - MaxSize = 0; - break; - } - case E_BLOCK_SNOW: - { - MaxSize = std::min(BlockMeta[i] + 1, MaxSize); - break; - } - } - } - if (TopMeta < MaxSize) - { - FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1); - } - else if (TopMeta > MaxSize) - { - FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1); - } - } - else if (g_BlockIsSnowable[TopBlock]) - { - SetBlock(X, Height + 1, Z, E_BLOCK_SNOW, 0); - } - else if ((TopBlock == E_BLOCK_WATER) || (TopBlock == E_BLOCK_STATIONARY_WATER)) - { - SetBlock(X, Height, Z, E_BLOCK_ICE, 0); - } - else if ( - (m_World->IsDeepSnowEnabled()) && - ( - (TopBlock == E_BLOCK_RED_ROSE) || - (TopBlock == E_BLOCK_YELLOW_FLOWER) || - (TopBlock == E_BLOCK_RED_MUSHROOM) || - (TopBlock == E_BLOCK_BROWN_MUSHROOM) - ) - ) - { - SetBlock(X, Height, Z, E_BLOCK_SNOW, 0); - } - break; - } // case (snowy biomes) - - // TODO: Rainy biomes should check for farmland and cauldrons - } // switch (biome) -} - - - - - -void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) -{ - // Convert the stem BlockType into produce BlockType - BLOCKTYPE ProduceType; - switch (a_BlockType) - { - case E_BLOCK_MELON_STEM: ProduceType = E_BLOCK_MELON; break; - case E_BLOCK_PUMPKIN_STEM: ProduceType = E_BLOCK_PUMPKIN; break; - default: - { - ASSERT(!"Unhandled blocktype in TickMelonPumpkin()"); - return; - } - } - - // Check if there's another melon / pumpkin around that stem, if so, abort: - bool IsValid; - BLOCKTYPE BlockType[4]; - NIBBLETYPE BlockMeta; // unused - IsValid = UnboundedRelGetBlock(a_RelX + 1, a_RelY, a_RelZ, BlockType[0], BlockMeta); - IsValid = IsValid && UnboundedRelGetBlock(a_RelX - 1, a_RelY, a_RelZ, BlockType[1], BlockMeta); - IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + 1, BlockType[2], BlockMeta); - IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - 1, BlockType[3], BlockMeta); - if ( - !IsValid || - (BlockType[0] == ProduceType) || - (BlockType[1] == ProduceType) || - (BlockType[2] == ProduceType) || - (BlockType[3] == ProduceType) - ) - { - // Neighbors not valid or already taken by the same produce - return; - } - - // Pick a direction in which to place the produce: - int x = 0, z = 0; - int CheckType = a_TickRandom.randInt(3); // The index to the neighbors array which should be checked for emptiness - switch (CheckType) - { - case 0: x = 1; break; - case 1: x = -1; break; - case 2: z = 1; break; - case 3: z = -1; break; - } - - // Check that the block in that direction is empty: - switch (BlockType[CheckType]) - { - case E_BLOCK_AIR: - case E_BLOCK_SNOW: - case E_BLOCK_TALL_GRASS: - case E_BLOCK_DEAD_BUSH: - { - break; - } - default: return; - } - - // Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok - BLOCKTYPE Soil; - UnboundedRelGetBlock(a_RelX + x, a_RelY - 1, a_RelZ + z, Soil, BlockMeta); - switch (Soil) - { - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_FARMLAND: - { - // DEBUG: This is here to catch FS #349 - melons growing over other crops. - LOG("Growing melon/pumpkin overwriting %s, growing on %s", - ItemTypeToString(BlockType[CheckType]).c_str(), - ItemTypeToString(Soil).c_str() - ); - // Place a randomly-facing produce: - UnboundedRelFastSetBlock(a_RelX + x, a_RelY, a_RelZ + z, ProduceType, (NIBBLETYPE)(a_TickRandom.randInt(4) % 4)); - break; - } - } -} - - - - - -void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) -{ - // Check the total height of the sugarcane blocks here: - int Top = a_RelY + 1; - while ( - (Top < cChunkDef::Height) && - (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_SUGARCANE) - ) - { - ++Top; - } - int Bottom = a_RelY - 1; - while ( - (Bottom > 0) && - (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_SUGARCANE) - ) - { - --Bottom; - } - - // Grow by at most a_NumBlocks, but no more than max height: - int ToGrow = std::min(a_NumBlocks, m_World->GetMaxSugarcaneHeight() + 1 - (Top - Bottom)); - for (int i = 0; i < ToGrow; i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) - { - UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_SUGARCANE, 0); - } - else - { - break; - } - } // for i -} - - - - - -void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) -{ - // Check the total height of the sugarcane blocks here: - int Top = a_RelY + 1; - while ( - (Top < cChunkDef::Height) && - (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_CACTUS) - ) - { - ++Top; - } - int Bottom = a_RelY - 1; - while ( - (Bottom > 0) && - (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_CACTUS) - ) - { - --Bottom; - } - - // Grow by at most a_NumBlocks, but no more than max height: - int ToGrow = std::min(a_NumBlocks, m_World->GetMaxCactusHeight() + 1 - (Top - Bottom)); - for (int i = 0; i < ToGrow; i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) - { - // TODO: Check the surrounding blocks, if they aren't air, break the cactus block into pickups (and continue breaking blocks above in the next loop iterations) - UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_CACTUS, 0); - } - else - { - break; - } - } // for i -} - - - - - -bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const -{ - if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) - { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); - return false; - } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - // The chunk is not available, bail out - return false; - } - Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - return true; -} - - - - - -bool cChunk::UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const -{ - if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) - { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); - return false; - } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - // The chunk is not available, bail out - return false; - } - a_BlockType = Chunk->GetBlock(a_RelX, a_RelY, a_RelZ); - return true; -} - - - - - -bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const -{ - if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) - { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); - return false; - } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - // The chunk is not available, bail out - return false; - } - a_BlockMeta = Chunk->GetMeta(a_RelX, a_RelY, a_RelZ); - return true; -} - - - - - -bool cChunk::UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockBlockLight) const -{ - if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) - { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); - return false; - } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - // The chunk is not available, bail out - return false; - } - a_BlockBlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); - return true; -} - - - - - -bool cChunk::UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockSkyLight) const -{ - if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) - { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); - return false; - } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - // The chunk is not available, bail out - return false; - } - a_BlockSkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); - return true; -} - - - - - -bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const -{ - if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) - { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); - return false; - } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - // The chunk is not available, bail out - return false; - } - int idx = Chunk->MakeIndex(a_RelX, a_RelY, a_RelZ); - a_BlockLight = Chunk->GetBlockLight(idx); - a_SkyLight = Chunk->GetSkyLight(idx); - return true; -} - - - - - -bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) - { - LOGWARNING("UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); - return false; - } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - // The chunk is not available, bail out - return false; - } - Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - return true; -} - - - - - -bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) - { - LOGWARNING("UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); - return false; - } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - // The chunk is not available, bail out - return false; - } - Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - return true; -} - - - - - -void cChunk::UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ) -{ - if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) - { - // Outside of chunkmap - return; - } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - Chunk->QueueTickBlock(a_RelX, a_RelY, a_RelZ); - } -} - - - - - -int cChunk::GetHeight(int a_X, int a_Z) -{ - ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)); - - if ((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)) - { - return m_HeightMap[a_X + a_Z * Width]; - } - return 0; -} - - - - - -void cChunk::CreateBlockEntities(void) -{ - for (int x = 0; x < Width; x++) - { - for (int z = 0; z < Width; z++) - { - for (int y = 0; y < Height; y++) - { - BLOCKTYPE BlockType = cChunkDef::GetBlock(m_BlockTypes, x, y, z); - switch (BlockType) - { - case E_BLOCK_CHEST: - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - case E_BLOCK_LIT_FURNACE: - case E_BLOCK_FURNACE: - case E_BLOCK_HOPPER: - case E_BLOCK_SIGN_POST: - case E_BLOCK_WALLSIGN: - case E_BLOCK_NOTE_BLOCK: - case E_BLOCK_JUKEBOX: - { - if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) - { - m_BlockEntities.push_back(cBlockEntity::CreateByBlockType( - BlockType, GetMeta(x, y, z), - x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World - )); - } - break; - } - } // switch (BlockType) - } // for y - } // for z - } // for x -} - - - - - -void cChunk::WakeUpSimulators(void) -{ - cSimulator * WaterSimulator = m_World->GetWaterSimulator(); - cSimulator * LavaSimulator = m_World->GetLavaSimulator(); - int BaseX = m_PosX * cChunkDef::Width; - int BaseZ = m_PosZ * cChunkDef::Width; - for (int x = 0; x < Width; x++) - { - int BlockX = x + BaseX; - for (int z = 0; z < Width; z++) - { - int BlockZ = z + BaseZ; - for (int y = GetHeight(x, z); y >= 0; y--) - { - switch (cChunkDef::GetBlock(m_BlockTypes, x, y, z)) - { - case E_BLOCK_WATER: - { - WaterSimulator->AddBlock(BlockX, y, BlockZ, this); - break; - } - case E_BLOCK_LAVA: - { - LavaSimulator->AddBlock(BlockX, y, BlockZ, this); - break; - } - } // switch (BlockType) - } // for y - } // for z - } // for x -} - - - - - -void cChunk::CalculateHeightmap() -{ - for (int x = 0; x < Width; x++) - { - for (int z = 0; z < Width; z++) - { - for (int y = Height - 1; y > -1; y--) - { - int index = MakeIndex( x, y, z ); - if (m_BlockTypes[index] != E_BLOCK_AIR) - { - m_HeightMap[x + z * Width] = (unsigned char)y; - break; - } - } // for y - } // for z - } // for x -} - - - - - -void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - - const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - - // Tick this block and its neighbors: - m_ToTickBlocks.push_back(index); - QueueTickBlockNeighbors(a_RelX, a_RelY, a_RelZ); - - // If there was a block entity, remove it: - Vector3i WorldPos = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ); - cBlockEntity * BlockEntity = GetBlockEntity(WorldPos); - if (BlockEntity != NULL) - { - BlockEntity->Destroy(); - RemoveBlockEntity(BlockEntity); - delete BlockEntity; - } - - // If the new block is a block entity, create the entity object: - switch (a_BlockType) - { - case E_BLOCK_CHEST: - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - case E_BLOCK_LIT_FURNACE: - case E_BLOCK_FURNACE: - case E_BLOCK_HOPPER: - case E_BLOCK_SIGN_POST: - case E_BLOCK_WALLSIGN: - case E_BLOCK_NOTE_BLOCK: - case E_BLOCK_JUKEBOX: - { - AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, WorldPos.x, WorldPos.y, WorldPos.z, m_World)); - break; - } - } // switch (a_BlockType) -} - - - - - -void cChunk::QueueSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick) -{ - m_SetBlockQueue.push_back(sSetBlockQueueItem(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta, a_Tick)); -} - - - - - -void cChunk::QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ) -{ - ASSERT ( - (a_RelX >= 0) && (a_RelX < Width) && - (a_RelY >= 0) && (a_RelY < Height) && - (a_RelZ >= 0) && (a_RelZ < Width) - ); // Coords need to be valid - - if (!IsValid()) - { - return; - } - - m_ToTickBlocks.push_back(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ)); -} - - - - - -void cChunk::QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ) -{ - struct - { - int x, y, z; - } - Coords[] = - { - { 1, 0, 0}, - {-1, 0, 0}, - { 0, 1, 0}, - { 0, -1, 0}, - { 0, 0, 1}, - { 0, 0, -1}, - } ; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - UnboundedQueueTickBlock(a_RelX + Coords[i].x, a_RelY + Coords[i].y, a_RelZ + Coords[i].z); - } // for i - Coords[] -} - - - - - -void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) -{ - ASSERT(!((a_RelX < 0) || (a_RelX >= Width) || (a_RelY < 0) || (a_RelY >= Height) || (a_RelZ < 0) || (a_RelZ >= Width))); - - ASSERT(IsValid()); - - const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - const BLOCKTYPE OldBlockType = cChunkDef::GetBlock(m_BlockTypes, index); - const BLOCKTYPE OldBlockMeta = GetNibble(m_BlockMeta, index); - if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta)) - { - return; - } - - MarkDirty(); - - m_BlockTypes[index] = a_BlockType; - - // The client doesn't need to distinguish between stationary and nonstationary fluids: - if ( - (OldBlockMeta != a_BlockMeta) || // Different meta always gets sent to the client - !( - ((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water - ((OldBlockType == E_BLOCK_WATER) && (a_BlockType == E_BLOCK_STATIONARY_WATER)) || // Replacing water with stationary water - ((OldBlockType == E_BLOCK_STATIONARY_LAVA) && (a_BlockType == E_BLOCK_LAVA)) || // Replacing stationary water with water - ((OldBlockType == E_BLOCK_LAVA) && (a_BlockType == E_BLOCK_STATIONARY_LAVA)) // Replacing water with stationary water - ) - ) - { - m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta)); - } - - SetNibble(m_BlockMeta, index, a_BlockMeta); - - // ONLY recalculate lighting if it's necessary! - if( - (g_BlockLightValue[OldBlockType ] != g_BlockLightValue[a_BlockType]) || - (g_BlockSpreadLightFalloff[OldBlockType] != g_BlockSpreadLightFalloff[a_BlockType]) || - (g_BlockTransparent[OldBlockType] != g_BlockTransparent[a_BlockType]) - ) - { - m_IsLightValid = false; - } - - // Update heightmap, if needed: - if (a_RelY >= m_HeightMap[a_RelX + a_RelZ * Width]) - { - if (a_BlockType != E_BLOCK_AIR) - { - m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)a_RelY; - } - else - { - for (int y = a_RelY - 1; y > 0; --y) - { - if (m_BlockTypes[MakeIndexNoCheck(a_RelX, y, a_RelZ)] != E_BLOCK_AIR) - { - m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)y; - break; - } - } // for y - column in m_BlockData - } - } -} - - - - - -void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client) -{ - // The coords must be valid, because the upper level already does chunk lookup. No need to check them again. - // There's an debug-time assert in MakeIndexNoCheck anyway - unsigned int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - - if (a_Client == NULL) - { - // Queue the block for all clients in the chunk (will be sent in Tick()) - m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(index), GetMeta(index))); - return; - } - - Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ); - a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index)); - - // FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client: - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr) - { - if (((*itr)->GetPosX() == wp.x) && ((*itr)->GetPosY() == wp.y) && ((*itr)->GetPosZ() == wp.z)) - { - (*itr)->SendTo(*a_Client); - } - } // for itr - m_BlockEntities -} - - - - - -void cChunk::AddBlockEntity(cBlockEntity * a_BlockEntity) -{ - MarkDirty(); - m_BlockEntities.push_back(a_BlockEntity); -} - - - - - -cBlockEntity * cChunk::GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if ( - ((*itr)->GetPosX() == a_BlockX) && - ((*itr)->GetPosY() == a_BlockY) && - ((*itr)->GetPosZ() == a_BlockZ) - ) - { - return *itr; - } - } // for itr - m_BlockEntities[] - - return NULL; -} - - - - - -void cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) -{ - cBlockEntity * be = GetBlockEntity(a_X, a_Y, a_Z); - if (be != NULL) - { - be->UsedBy(a_Player); - } -} - - - - - -void cChunk::CollectPickupsByPlayer(cPlayer * a_Player) -{ - double PosX = a_Player->GetPosX(); - double PosY = a_Player->GetPosY(); - double PosZ = a_Player->GetPosZ(); - - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) - { - if ((!(*itr)->IsPickup()) && (!(*itr)->IsProjectile())) - { - continue; // Only pickups and projectiles - } - float DiffX = (float)((*itr)->GetPosX() - PosX ); - float DiffY = (float)((*itr)->GetPosY() - PosY ); - float DiffZ = (float)((*itr)->GetPosZ() - PosZ ); - float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; - if (SqrDist < 1.5f * 1.5f) // 1.5 block - { - /* - LOG("Pickup %d being collected by player \"%s\", distance %f", - (*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist - ); - */ - MarkDirty(); - if ((*itr)->IsPickup()) - { - (reinterpret_cast(*itr))->CollectedBy(a_Player); - } - else - { - (reinterpret_cast(*itr))->CollectedBy(a_Player); - } - } - else if (SqrDist < 5 * 5) - { - /* - LOG("Pickup %d close to player \"%s\", but still too far to collect: %f", - (*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist - ); - */ - } - } -} - - - - - -bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) -{ - // Also sends update packets to all clients in the chunk - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if ( - ((*itr)->GetPosX() == a_PosX) && - ((*itr)->GetPosY() == a_PosY) && - ((*itr)->GetPosZ() == a_PosZ) && - ( - ((*itr)->GetBlockType() == E_BLOCK_WALLSIGN) || - ((*itr)->GetBlockType() == E_BLOCK_SIGN_POST) - ) - ) - { - MarkDirty(); - (reinterpret_cast(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4); - m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ); - return true; - } - } // for itr - m_BlockEntities[] - return false; -} - - - - - -void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity ) -{ - MarkDirty(); - m_BlockEntities.remove(a_BlockEntity); -} - - - - - -bool cChunk::AddClient(cClientHandle* a_Client) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) - { - if (a_Client == *itr) - { - // Already there, nothing needed - return false; - } - } - m_LoadedByClient.push_back( a_Client ); - - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) - { - LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"", (*itr)->GetUniqueID(), (*itr)->GetClass(), m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str()); - (*itr)->SpawnOn(*a_Client); - } - return true; -} - - - - - -void cChunk::RemoveClient( cClientHandle* a_Client ) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) - { - if (*itr != a_Client) - { - continue; - } - - m_LoadedByClient.erase(itr); - - if (!a_Client->IsDestroyed()) - { - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) - { - LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"", m_PosX, m_PosZ, (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() ); - a_Client->SendDestroyEntity(*(*itr)); - } - } - return; - } // for itr - m_LoadedByClient[] -} - - - - - -bool cChunk::HasClient( cClientHandle* a_Client ) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) - { - if ((*itr) == a_Client) - { - return true; - } - } - return false; -} - - - - - -bool cChunk::HasAnyClients(void) -{ - return !m_LoadedByClient.empty(); -} - - - - - -void cChunk::AddEntity(cEntity * a_Entity) -{ - if (!a_Entity->IsPlayer()) - { - MarkDirty(); - } - - ASSERT(std::find(m_Entities.begin(), m_Entities.end(), a_Entity) == m_Entities.end()); // Not there already - - m_Entities.push_back(a_Entity); -} - - - - - -void cChunk::RemoveEntity(cEntity * a_Entity) -{ - size_t SizeBefore = m_Entities.size(); - m_Entities.remove(a_Entity); - size_t SizeAfter = m_Entities.size(); - - if (SizeBefore != SizeAfter) - { - // Mark as dirty if it was a server-generated entity: - if (!a_Entity->IsPlayer()) - { - MarkDirty(); - } - } -} - - - - - -bool cChunk::HasEntity(int a_EntityID) -{ - for (cEntityList::const_iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr) - { - if ((*itr)->GetUniqueID() == a_EntityID) - { - return true; - } - } // for itr - m_Entities[] - return false; -} - - - - - -bool cChunk::ForEachEntity(cEntityCallback & a_Callback) -{ - // The entity list is locked by the parent chunkmap's CS - for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2) - { - ++itr2; - if (a_Callback.Item(*itr)) - { - return false; - } - } // for itr - m_Entitites[] - return true; -} - - - - - -bool cChunk::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult) -{ - // The entity list is locked by the parent chunkmap's CS - for (cEntityList::iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr) - { - if ((*itr)->GetUniqueID() == a_EntityID) - { - a_CallbackResult = a_Callback.Item(*itr); - return true; - } - } // for itr - m_Entitites[] - return false; -} - - - - - -bool cChunk::ForEachChest(cChestCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if ((*itr)->GetBlockType() != E_BLOCK_CHEST) - { - continue; - } - if (a_Callback.Item((cChestEntity *)*itr)) - { - return false; - } - } // for itr - m_BlockEntitites[] - return true; -} - - - - - -bool cChunk::ForEachDispenser(cDispenserCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER) - { - continue; - } - if (a_Callback.Item((cDispenserEntity *)*itr)) - { - return false; - } - } // for itr - m_BlockEntitites[] - return true; -} - - - - - -bool cChunk::ForEachDropper(cDropperCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if ((*itr)->GetBlockType() != E_BLOCK_DROPPER) - { - continue; - } - if (a_Callback.Item((cDropperEntity *)*itr)) - { - return false; - } - } // for itr - m_BlockEntitites[] - return true; -} - - - - - -bool cChunk::ForEachDropSpenser(cDropSpenserCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if (((*itr)->GetBlockType() != E_BLOCK_DISPENSER) && ((*itr)->GetBlockType() != E_BLOCK_DROPPER)) - { - continue; - } - if (a_Callback.Item((cDropSpenserEntity *)*itr)) - { - return false; - } - } // for itr - m_BlockEntitites[] - return true; -} - - - - - -bool cChunk::ForEachFurnace(cFurnaceCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - switch ((*itr)->GetBlockType()) - { - case E_BLOCK_FURNACE: - case E_BLOCK_LIT_FURNACE: - { - break; - } - default: - { - continue; - } - } - if (a_Callback.Item((cFurnaceEntity *)*itr)) - { - return false; - } - } // for itr - m_BlockEntitites[] - return true; -} - - - - - -bool cChunk::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) - { - continue; - } - if ((*itr)->GetBlockType() != E_BLOCK_CHEST) - { - // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out - return false; - } - - // The correct block entity is here - if (a_Callback.Item((cChestEntity *)*itr)) - { - return false; - } - return true; - } // for itr - m_BlockEntitites[] - - // Not found: - return false; -} - - - - - -bool cChunk::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) - { - continue; - } - if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER) - { - // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out - return false; - } - - // The correct block entity is here - if (a_Callback.Item((cDispenserEntity *)*itr)) - { - return false; - } - return true; - } // for itr - m_BlockEntitites[] - - // Not found: - return false; -} - - - - - -bool cChunk::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) - { - continue; - } - if ((*itr)->GetBlockType() != E_BLOCK_DROPPER) - { - // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out - return false; - } - - // The correct block entity is here - if (a_Callback.Item((cDropperEntity *)*itr)) - { - return false; - } - return true; - } // for itr - m_BlockEntitites[] - - // Not found: - return false; -} - - - - - -bool cChunk::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) - { - continue; - } - if (((*itr)->GetBlockType() != E_BLOCK_DISPENSER) && ((*itr)->GetBlockType() != E_BLOCK_DROPPER)) - { - // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out - return false; - } - - // The correct block entity is here - if (a_Callback.Item((cDropSpenserEntity *)*itr)) - { - return false; - } - return true; - } // for itr - m_BlockEntitites[] - - // Not found: - return false; -} - - - - - -bool cChunk::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) - { - continue; - } - switch ((*itr)->GetBlockType()) - { - case E_BLOCK_FURNACE: - case E_BLOCK_LIT_FURNACE: - { - break; - } - default: - { - // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out - return false; - } - } // switch (BlockType) - - // The correct block entity is here, - if (a_Callback.Item((cFurnaceEntity *)*itr)) - { - return false; - } - return true; - } // for itr - m_BlockEntitites[] - - // Not found: - return false; -} - - - - - -bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) - { - continue; - } - switch ((*itr)->GetBlockType()) - { - case E_BLOCK_WALLSIGN: - case E_BLOCK_SIGN_POST: - { - a_Line1 = ((cSignEntity *)*itr)->GetLine(0); - a_Line2 = ((cSignEntity *)*itr)->GetLine(1); - a_Line3 = ((cSignEntity *)*itr)->GetLine(2); - a_Line4 = ((cSignEntity *)*itr)->GetLine(3); - return true; - } - } // switch (BlockType) - - // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out - return false; - } // for itr - m_BlockEntitites[] - - // Not found: - return false; -} - - - - - -BLOCKTYPE cChunk::GetBlock(int a_RelX, int a_RelY, int a_RelZ) const -{ - if ( - (a_RelX < 0) || (a_RelX >= Width) || - (a_RelY < 0) || (a_RelY >= Height) || - (a_RelZ < 0) || (a_RelZ >= Width) - ) - { - ASSERT(!"GetBlock(x, y, z) out of bounds!"); - return 0; // Clip - } - - return m_BlockTypes[MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ)]; -} - - - - - -BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const -{ - if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks)) - { - ASSERT(!"GetBlock(idx) out of bounds!"); - return 0; - } - - return m_BlockTypes[ a_BlockIdx ]; -} - - - - - -void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - a_BlockType = cChunkDef::GetBlock (m_BlockTypes, a_RelX, a_RelY, a_RelZ); - a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cChunk::GetBlockInfo(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) -{ - int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - a_BlockType = cChunkDef::GetBlock (m_BlockTypes, Idx); - a_Meta = cChunkDef::GetNibble(m_BlockMeta, Idx); - a_SkyLight = cChunkDef::GetNibble(m_BlockSkyLight, Idx); - a_BlockLight = cChunkDef::GetNibble(m_BlockLight, Idx); -} - - - - - -cChunk * cChunk::GetNeighborChunk(int a_BlockX, int a_BlockZ) -{ - // Convert coords to relative, then call the relative version: - a_BlockX -= m_PosX * cChunkDef::Width; - a_BlockZ -= m_PosZ * cChunkDef::Width; - return GetRelNeighborChunk(a_BlockX, a_BlockZ); -} - - - - - -cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ) -{ - bool ReturnThis = true; - if (a_RelX < 0) - { - if (m_NeighborXM != NULL) - { - cChunk * Candidate = m_NeighborXM->GetRelNeighborChunk(a_RelX + cChunkDef::Width, a_RelZ); - if (Candidate != NULL) - { - return Candidate; - } - } - // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on. - ReturnThis = false; - } - else if (a_RelX >= cChunkDef::Width) - { - if (m_NeighborXP != NULL) - { - cChunk * Candidate = m_NeighborXP->GetRelNeighborChunk(a_RelX - cChunkDef::Width, a_RelZ); - if (Candidate != NULL) - { - return Candidate; - } - } - // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on. - ReturnThis = false; - } - - if (a_RelZ < 0) - { - if (m_NeighborZM != NULL) - { - return m_NeighborZM->GetRelNeighborChunk(a_RelX, a_RelZ + cChunkDef::Width); - // For requests crossing both X and Z, the X-first way has been already tried - } - return NULL; - } - else if (a_RelZ >= cChunkDef::Width) - { - if (m_NeighborZP != NULL) - { - return m_NeighborZP->GetRelNeighborChunk(a_RelX, a_RelZ - cChunkDef::Width); - // For requests crossing both X and Z, the X-first way has been already tried - } - return NULL; - } - - return (ReturnThis ? this : NULL); -} - - - - - -cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const -{ - cChunk * ToReturn = const_cast(this); - - // The most common case: inside this chunk: - if ( - (a_RelX >= 0) && (a_RelX < Width) && - (a_RelZ >= 0) && (a_RelZ < Width) - ) - { - return ToReturn; - } - - // Request for a different chunk, calculate chunk offset: - int RelX = a_RelX; // Make a local copy of the coords (faster access) - int RelZ = a_RelZ; - while ((RelX >= Width) && (ToReturn != NULL)) - { - RelX -= Width; - ToReturn = ToReturn->m_NeighborXP; - } - while ((RelX < 0) && (ToReturn != NULL)) - { - RelX += Width; - ToReturn = ToReturn->m_NeighborXM; - } - while ((RelZ >= Width) && (ToReturn != NULL)) - { - RelZ -= Width; - ToReturn = ToReturn->m_NeighborZP; - } - while ((RelZ < 0) && (ToReturn != NULL)) - { - RelZ += Width; - ToReturn = ToReturn->m_NeighborZM; - } - if (ToReturn != NULL) - { - a_RelX = RelX; - a_RelZ = RelZ; - return ToReturn; - } - - // The chunk cannot be walked through neighbors, find it through the chunkmap: - int AbsX = a_RelX + m_PosX * Width; - int AbsZ = a_RelZ + m_PosZ * Width; - int DstChunkX, DstChunkZ; - BlockToChunk(AbsX, AbsZ, DstChunkX, DstChunkZ); - ToReturn = m_ChunkMap->FindChunk(DstChunkX, DstChunkZ); - a_RelX = AbsX - DstChunkX * Width; - a_RelZ = AbsZ - DstChunkZ * Width; - return ToReturn; -} - - - - - -void cChunk::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - (*itr)->SendAttachEntity(a_Entity, a_Vehicle); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendBlockBreakAnim(a_entityID, a_blockX, a_blockY, a_blockZ, a_stage); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - // We can operate on entity pointers, we're inside the ChunkMap's CS lock which guards the list - cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); - if (Entity == NULL) - { - return; - } - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - Entity->SendTo(*(*itr)); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendChunkData(m_PosX, m_PosZ, a_Serializer); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendCollectPickup(a_Pickup, a_Player); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendDestroyEntity(a_Entity); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityHeadLook(a_Entity); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityLook(a_Entity); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityMetadata(a_Entity); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityStatus(a_Entity, a_Status); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityVelocity(a_Entity); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendPlayerAnimation(a_Player, a_Animation); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - a_Entity.SpawnOn(*(*itr)); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - (*itr)->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) -{ - cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); - if (Entity == NULL) - { - return; - } - Entity->SendTo(a_Client); -} - - - - - -void cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ) -{ - a_BlockY = a_RelY; - a_BlockX = m_PosX * Width + a_RelX; - a_BlockZ = m_PosZ * Width + a_RelZ; -} - - - - - -Vector3i cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ) -{ - return Vector3i(m_PosX * Width + a_RelX, m_PosY * Height + a_RelY, m_PosZ * Width + a_RelZ); -} - - - - - -NIBBLETYPE cChunk::GetTimeAlteredLight(NIBBLETYPE a_Skylight) const -{ - a_Skylight -= m_World->GetSkyDarkness(); - // Because NIBBLETYPE is unsigned, we clamp it to 0 .. 15 by checking for values above 15 - return (a_Skylight < 16)? a_Skylight : 0; -} - - - - - -#if !C_CHUNK_USE_INLINE -# include "cChunk.inl.h" -#endif - - - - diff --git a/source/Chunk.h b/source/Chunk.h deleted file mode 100644 index 63a8f75cd..000000000 --- a/source/Chunk.h +++ /dev/null @@ -1,475 +0,0 @@ - -#pragma once - -#include "Entities/Entity.h" -#include "ChunkDef.h" - -#include "Simulator/FireSimulator.h" -#include "Simulator/SandSimulator.h" - - - - - -#define C_CHUNK_USE_INLINE 1 - -// Do not touch -#if C_CHUNK_USE_INLINE - #define __C_CHUNK_INLINE__ inline -#else - #define __C_CHUNK_INLINE__ -#endif - - - - - -namespace Json -{ - class Value; -}; - - - - - -class cWorld; -class cFurnaceEntity; -class cClientHandle; -class cServer; -class MTRand; -class cPlayer; -class cChunkMap; -class cChestEntity; -class cDispenserEntity; -class cFurnaceEntity; -class cBlockArea; -class cPawn; -class cPickup; -class cChunkDataSerializer; -class cBlockArea; -class cFluidSimulatorData; -class cMobCensus; -class cMobSpawner; - -typedef std::list cClientHandleList; -typedef cItemCallback cEntityCallback; -typedef cItemCallback cChestCallback; -typedef cItemCallback cDispenserCallback; -typedef cItemCallback cFurnaceCallback; - - - - -// This class is not to be used directly -// Instead, call actions on cChunkMap (such as cChunkMap::SetBlock() etc.) -class cChunk : - public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef -{ -public: - cChunk( - int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords - cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects - cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks - ); - ~cChunk(); - - bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated) - void SetValid(void); // Also wakes up any calls to cChunkMap::GetHeight() - void MarkRegenerating(void); // Marks all clients attached to this chunk as wanting this chunk - bool IsDirty(void) const {return m_IsDirty; } // Returns true if the chunk has changed since it was last saved - bool HasLoadFailed(void) const {return m_HasLoadFailed; } // Returns true if the chunk failed to load and hasn't been generated since then - bool CanUnload(void); - - bool IsLightValid(void) const {return m_IsLightValid; } - - /* - To save a chunk, the WSSchema must: - 1. Mark the chunk as being saved (MarkSaving() ) - 2. Get the chunk's data using GetAllData() - 3. Mark the chunk as saved (MarkSaved() ) - If anywhere inside this sequence another thread mmodifies the chunk, the chunk will not get marked as saved in MarkSaved() - */ - void MarkSaving(void); // Marks the chunk as being saved. - void MarkSaved(void); // Marks the chunk as saved, if it didn't change from the last call to MarkSaving() - void MarkLoaded(void); // Marks the chunk as freshly loaded. Fails if the chunk is already valid - void MarkLoadFailed(void); // Marks the chunk as failed to load. Ignored is the chunk is already valid - - /// Gets all chunk data, calls the a_Callback's methods for each data type - void GetAllData(cChunkDataCallback & a_Callback); - - /// Sets all chunk data - void SetAllData( - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap & a_BiomeMap, - cBlockEntityList & a_BlockEntities - ); - - void SetLight( - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight - ); - - /// Copies m_BlockData into a_BlockTypes, only the block types - void GetBlockTypes(BLOCKTYPE * a_BlockTypes); - - /// Writes the specified cBlockArea at the coords specified. Note that the coords may extend beyond the chunk! - void WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); - - /// Returns true if there is a block entity at the coords specified - bool HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Sets or resets the internal flag that prevents chunk from being unloaded - void Stay(bool a_Stay = true); - - /// Recence all mobs proximities to players in order to know what to do with them - void CollectMobCensus(cMobCensus& toFill); - - /// Try to Spawn Monsters inside chunk - void SpawnMobs(cMobSpawner& a_MobSpawner); - - void Tick(float a_Dt); - - int GetPosX(void) const { return m_PosX; } - int GetPosY(void) const { return m_PosY; } - int GetPosZ(void) const { return m_PosZ; } - - cWorld * GetWorld(void) const { return m_World; } - - void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); - // SetBlock() does a lot of work (heightmap, tickblocks, blockentities) so a BlockIdx version doesn't make sense - void SetBlock( const Vector3i & a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { SetBlock( a_RelBlockPos.x, a_RelBlockPos.y, a_RelBlockPos.z, a_BlockType, a_BlockMeta ); } - - /// Queues a block change till the specified world tick - void QueueSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick); - - /// Queues block for ticking (m_ToTickQueue) - void QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ); - - /// Queues all 6 neighbors of the specified block for ticking (m_ToTickQueue). If any are outside the chunk, relays the checking to the proper neighboring chunk - void QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ); - - void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc. - BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const; - BLOCKTYPE GetBlock(int a_BlockIdx) const; - void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); - - /** Returns the chunk into which the specified block belongs, by walking the neighbors. - Will return self if appropriate. Returns NULL if not reachable through neighbors. - */ - cChunk * GetNeighborChunk(int a_BlockX, int a_BlockZ); - - /** - Returns the chunk into which the relatively-specified block belongs, by walking the neighbors. - Will return self if appropriate. Returns NULL if not reachable through neighbors. - */ - cChunk * GetRelNeighborChunk(int a_RelX, int a_RelZ); - - /** - Returns the chunk into which the relatively-specified block belongs. - Also modifies the relative coords from this-relative to return-relative. - Will return self if appropriate. - Will try walking the neighbors first; if that fails, will query the chunkmap - */ - cChunk * GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const; - - EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); } - - void CollectPickupsByPlayer(cPlayer * a_Player); - - /// Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk - bool SetSignLines(int a_RelX, int a_RelY, int a_RelZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); - - int GetHeight( int a_X, int a_Z ); - - void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client); - - /// Adds a client to the chunk; returns true if added, false if already there - bool AddClient (cClientHandle* a_Client ); - - void RemoveClient (cClientHandle* a_Client ); - bool HasClient (cClientHandle* a_Client ); - bool HasAnyClients(void); // Returns true if theres any client in the chunk; false otherwise - - void AddEntity(cEntity * a_Entity); - void RemoveEntity(cEntity * a_Entity); - bool HasEntity(int a_EntityID); - - /// Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible - - /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. - bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult); // Lua-accessible - - /// Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true - bool ForEachChest(cChestCallback & a_Callback); // Lua-accessible - - /// Calls the callback for each dispenser; returns true if all dispensers processed, false if the callback aborted by returning true - bool ForEachDispenser(cDispenserCallback & a_Callback); - - /// Calls the callback for each dropper; returns true if all droppers processed, false if the callback aborted by returning true - bool ForEachDropper(cDropperCallback & a_Callback); - - /// Calls the callback for each dropspenser; returns true if all dropspensers processed, false if the callback aborted by returning true - bool ForEachDropSpenser(cDropSpenserCallback & a_Callback); - - /// Calls the callback for each furnace; returns true if all furnaces processed, false if the callback aborted by returning true - bool ForEachFurnace(cFurnaceCallback & a_Callback); // Lua-accessible - - /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found - bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible - - /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found - bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); - - /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found - bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); - - /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found - bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); - - /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found - bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible - - /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found - bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible - - void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords - - void CalculateLighting(); // Recalculate right now - void CalculateHeightmap(); - - // Broadcast various packets to all clients of this chunk: - // (Please keep these alpha-sorted) - void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle); - void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); - void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL); - void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); - void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); - void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); - void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityMetadata (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastPlayerAnimation (const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); - void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 - void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); - void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); - void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); - - void SendBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); - - Vector3i PositionToWorldPosition(const Vector3i & a_RelPos) - { - return PositionToWorldPosition(a_RelPos.x, a_RelPos.y, a_RelPos.z); - } - - void PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ); - Vector3i PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ ); - - inline void MarkDirty(void) - { - m_IsDirty = true; - m_IsSaving = false; - } - - /// Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call - inline void SetNextBlockTick(int a_RelX, int a_RelY, int a_RelZ) - { - m_BlockTickX = a_RelX; - m_BlockTickY = a_RelY; - m_BlockTickZ = a_RelZ; - } - - inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); } - inline NIBBLETYPE GetMeta(int a_BlockIdx) const {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); } - inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); } - inline void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_BlockIdx, a_Meta); } - - inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); } - inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); } - inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); } - inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx); } - - /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success - bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; - - /// Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success - bool UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const; - - /// Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success - bool UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const; - - /// Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success - bool UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight) const; - - /// Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success - bool UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_SkyLight) const; - - /// Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success - bool UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const; - - /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success - bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success - bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case), ignores unsuccessful attempts - void UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ); - - /// Light alterations based on time - NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const; - - - // Simulator data: - cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; } - cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; } - cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; } - cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; } - - cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ); - cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); } - -private: - - friend class cChunkMap; - - struct sSetBlockQueueItem - { - int m_RelX, m_RelY, m_RelZ; - BLOCKTYPE m_BlockType; - NIBBLETYPE m_BlockMeta; - Int64 m_Tick; - - sSetBlockQueueItem(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick) : - m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ), m_BlockType(a_BlockType), m_BlockMeta(a_BlockMeta), m_Tick(a_Tick) - { - } - } ; - - typedef std::vector sSetBlockQueueVector; - - - bool m_IsValid; // True if the chunk is loaded / generated - bool m_IsLightValid; // True if the blocklight and skylight are calculated - bool m_IsDirty; // True if the chunk has changed since it was last saved - bool m_IsSaving; // True if the chunk is being saved - bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then - - std::vector m_ToTickBlocks; - sSetBlockVector m_PendingSendBlocks; ///< Blocks that have changed and need to be sent to all clients - - sSetBlockQueueVector m_SetBlockQueue; ///< Block changes that are queued to a specific tick - - // A critical section is not needed, because all chunk access is protected by its parent ChunkMap's csLayers - cClientHandleList m_LoadedByClient; - cClientHandleList m_UnloadQuery; - cEntityList m_Entities; - cBlockEntityList m_BlockEntities; - - /// Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded - int m_StayCount; - - int m_PosX, m_PosY, m_PosZ; - cWorld * m_World; - cChunkMap * m_ChunkMap; - - // TODO: Make these pointers and don't allocate what isn't needed - BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks]; - NIBBLETYPE m_BlockMeta [cChunkDef::NumBlocks / 2]; - NIBBLETYPE m_BlockLight [cChunkDef::NumBlocks / 2]; - NIBBLETYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2]; - - cChunkDef::HeightMap m_HeightMap; - cChunkDef::BiomeMap m_BiomeMap; - - int m_BlockTickX, m_BlockTickY, m_BlockTickZ; - - cChunk * m_NeighborXM; // Neighbor at [X - 1, Z] - cChunk * m_NeighborXP; // Neighbor at [X + 1, Z] - cChunk * m_NeighborZM; // Neighbor at [X, Z - 1] - cChunk * m_NeighborZP; // Neighbor at [X, Z + 1] - - // Per-chunk simulator data: - cFireSimulatorChunkData m_FireSimulatorData; - cFluidSimulatorData * m_WaterSimulatorData; - cFluidSimulatorData * m_LavaSimulatorData; - cSandSimulatorChunkData m_SandSimulatorData; - - - // pick up a random block of this chunk - void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z); - void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ); - - void RemoveBlockEntity(cBlockEntity * a_BlockEntity); - void AddBlockEntity (cBlockEntity * a_BlockEntity); - - void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff); - - /// Creates a block entity for each block that needs a block entity and doesn't have one in the list - void CreateBlockEntities(void); - - /// Wakes up each simulator for its specific blocks; through all the blocks in the chunk - void WakeUpSimulators(void); - - // Makes a copy of the list - cClientHandleList GetAllClients(void) const {return m_LoadedByClient; } - - /// Sends m_PendingSendBlocks to all clients - void BroadcastPendingBlockChanges(void); - - /// Checks the block scheduled for checking in m_ToTickBlocks[] - void CheckBlocks(void); - - /// Ticks several random blocks in the chunk - void TickBlocks(void); - - /// Adds snow to the top of snowy biomes and hydrates farmland / fills cauldrons in rainy biomes - void ApplyWeatherToTop(void); - - /// Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) - void GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); - - /// Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) - void GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); - - /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) - void GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random); - - /// Checks if a leaves block at the specified coords has a log up to 4 blocks away connected by other leaves blocks (false if no log) - bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients - void MoveEntityToNewChunk(cEntity * a_Entity); - - /// Processes all blocks that have been scheduled for replacement by the QueueSetBlock() function - void ProcessQueuedSetBlocks(void); -}; - -typedef cChunk * cChunkPtr; - -typedef std::list cChunkPtrList; - - - - - -#if C_CHUNK_USE_INLINE - #include "Chunk.inl.h" -#endif - - - - diff --git a/source/Chunk.inl.h b/source/Chunk.inl.h deleted file mode 100644 index fb9c4dad1..000000000 --- a/source/Chunk.inl.h +++ /dev/null @@ -1,34 +0,0 @@ - -#ifndef __C_CHUNK_INL_H__ -#define __C_CHUNK_INL_H__ - -#ifndef MAX -# define MAX(a,b) (((a)>(b))?(a):(b)) -#endif - - - - - -__C_CHUNK_INLINE__ -void cChunk::SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff) -{ - unsigned char CurrentLight = cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z ); - cChunkDef::SetNibble( a_LightBuffer, a_X-1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X-1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X+1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X+1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y-1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y-1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y+1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y+1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z-1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z-1 ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z+1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z+1 ), MAX(0,CurrentLight-a_Falloff) ) ); - MarkDirty(); -} - - - - - -#endif - - - - diff --git a/source/ChunkDef.h b/source/ChunkDef.h deleted file mode 100644 index d6630df7e..000000000 --- a/source/ChunkDef.h +++ /dev/null @@ -1,617 +0,0 @@ - -// ChunkDef.h - -// Interfaces to helper types for chunk definitions. Most modules want to include this instead of cChunk.h - - - - - -#pragma once - -#include "Vector3i.h" - - - - - -/** This is really only a placeholder to be used in places where we need to "make up" a chunk's Y coord. -It will help us when the new chunk format comes out and we need to patch everything up for compatibility. -*/ -#define ZERO_CHUNK_Y 0 - -// Used to smoothly convert to new axis ordering. One will be removed when deemed stable. -#define AXIS_ORDER_YZX 1 // Original (1.1-) -#define AXIS_ORDER_XZY 2 // New (1.2+) -#define AXIS_ORDER AXIS_ORDER_XZY - - - - - -// fwd -class cBlockEntity; -class cEntity; -class cClientHandle; -class cBlockEntity; - -typedef std::list cEntityList; -typedef std::list cBlockEntityList; - - - - -// tolua_begin - -/// The datatype used by blockdata -typedef unsigned char BLOCKTYPE; - -/// The datatype used by nibbledata (meta, light, skylight) -typedef unsigned char NIBBLETYPE; - -/// The type used by the heightmap -typedef unsigned char HEIGHTTYPE; - -// tolua_end - - - - - - -// tolua_begin -/** Biome IDs -The first batch corresponds to the clientside biomes, used by MineCraft. -BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client -*/ -enum EMCSBiome -{ - biOcean = 0, - biPlains = 1, - biDesert = 2, - biExtremeHills = 3, - biForest = 4, - biTaiga = 5, - biSwampland = 6, - biRiver = 7, - biHell = 8, // same as Nether - biNether = 8, - biSky = 9, // same as biEnd - biEnd = 9, - biFrozenOcean = 10, - biFrozenRiver = 11, - biIcePlains = 12, - biTundra = 12, // same as Ice Plains - biIceMountains = 13, - biMushroomIsland = 14, - biMushroomShore = 15, - biBeach = 16, - biDesertHills = 17, - biForestHills = 18, - biTaigaHills = 19, - biExtremeHillsEdge = 20, - biJungle = 21, - biJungleHills = 22, - - // Release 1.7 biomes: - biJungleEdge = 23, - biDeepOcean = 24, - biStoneBeach = 25, - biColdBeach = 26, - biBirchForest = 27, - biBirchForestHills = 28, - biRoofedForest = 29, - biColdTaiga = 30, - biColdTaigaHills = 31, - biMegaTaiga = 32, - biMegaTaigaHills = 33, - biExtremeHillsPlus = 34, - biSavanna = 35, - biSavannaPlateau = 36, - biMesa = 37, - biMesaPlateauF = 38, - biMesaPlateau = 39, - - // Automatically capture the maximum consecutive biome value into biMaxBiome: - biNumBiomes, // True number of biomes, since they are zero-based - biMaxBiome = biNumBiomes - 1, // The maximum biome value - - // Add this number to the biomes to get the variant - biVariant = 128, - - // Release 1.7 biome variants: - biSunflowerPlains = 129, - biDesertM = 130, - biExtremeHillsM = 131, - biFlowerForest = 132, - biTaigaM = 133, - biSwamplandM = 134, - biIcePlainsSpikes = 140, - biJungleM = 149, - biJungleEdgeM = 151, - biBirchForestM = 155, - biBirchForestHillsM = 156, - biRoofedForestM = 157, - biColdTaigaM = 158, - biMegaSpruceTaiga = 160, - biMegaSpruceTaigaHills = 161, - biExtremeHillsPlusM = 162, - biSavannaM = 163, - biSavannaPlateauM = 164, - biMesaBryce = 165, - biMesaPlateauFM = 166, - biMesaPlateauM = 167, -} ; - -// tolua_end - - - - -/// Constants used throughout the code, useful typedefs and utility functions -class cChunkDef -{ -public: - static const int Width = 16; - static const int Height = 256; - static const int NumBlocks = Width * Height * Width; - static const int BlockDataSize = NumBlocks * 2 + (NumBlocks / 2); // 2.5 * numblocks - - // Offsets to individual components in the joined blockdata array - static const int MetaOffset = NumBlocks; - static const int LightOffset = MetaOffset + NumBlocks / 2; - static const int SkyLightOffset = LightOffset + NumBlocks / 2; - - static const unsigned int INDEX_OUT_OF_RANGE = 0xffffffff; - - /// The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the highest non-air block in the column - typedef HEIGHTTYPE HeightMap[Width * Width]; - - /** The type used for any biomemap operations and storage inside MCServer, - using MCServer biomes (need not correspond to client representation!) - idx = x + Width * z // Need to verify this with the protocol spec, currently unknown! - */ - typedef EMCSBiome BiomeMap[Width * Width]; - - /// The type used for block type operations and storage, AXIS_ORDER ordering - typedef BLOCKTYPE BlockTypes[NumBlocks]; - - /// The type used for block data in nibble format, AXIS_ORDER ordering - typedef NIBBLETYPE BlockNibbles[NumBlocks / 2]; - - - /// Converts absolute block coords into relative (chunk + block) coords: - inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ ) - { - BlockToChunk(a_X, a_Z, a_ChunkX, a_ChunkZ); - - a_X = a_X - a_ChunkX * Width; - a_Z = a_Z - a_ChunkZ * Width; - } - - - /// Converts absolute block coords to chunk coords: - inline static void BlockToChunk(int a_X, int a_Z, int & a_ChunkX, int & a_ChunkZ) - { - a_ChunkX = a_X / Width; - if ((a_X < 0) && (a_X % Width != 0)) - { - a_ChunkX--; - } - a_ChunkZ = a_Z / cChunkDef::Width; - if ((a_Z < 0) && (a_Z % Width != 0)) - { - a_ChunkZ--; - } - } - - - inline static unsigned int MakeIndex(int x, int y, int z ) - { - if ( - (x < Width) && (x > -1) && - (y < Height) && (y > -1) && - (z < Width) && (z > -1) - ) - { - return MakeIndexNoCheck(x, y, z); - } - ASSERT(!"cChunkDef::MakeIndex(): coords out of chunk range!"); - return INDEX_OUT_OF_RANGE; - } - - - inline static unsigned int MakeIndexNoCheck(int x, int y, int z) - { - #if AXIS_ORDER == AXIS_ORDER_XZY - // For some reason, NOT using the Horner schema is faster. Weird. - return x + (z * cChunkDef::Width) + (y * cChunkDef::Width * cChunkDef::Width); // 1.2 is XZY - #elif AXIS_ORDER == AXIS_ORDER_YZX - return y + (z * cChunkDef::Width) + (x * cChunkDef::Height * cChunkDef::Width); // 1.1 is YZX - #endif - } - - - inline static Vector3i IndexToCoordinate( unsigned int index ) - { - #if AXIS_ORDER == AXIS_ORDER_XZY - return Vector3i( // 1.2 - index % cChunkDef::Width, // X - index / (cChunkDef::Width * cChunkDef::Width), // Y - (index / cChunkDef::Width) % cChunkDef::Width // Z - ); - #elif AXIS_ORDER == AXIS_ORDER_YZX - return Vector3i( // 1.1 - index / (cChunkDef::Height * cChunkDef::Width), // X - index % cChunkDef::Height, // Y - (index / cChunkDef::Height) % cChunkDef::Width // Z - ); - #endif - } - - - inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type) - { - ASSERT((a_X >= 0) && (a_X < Width)); - ASSERT((a_Y >= 0) && (a_Y < Height)); - ASSERT((a_Z >= 0) && (a_Z < Width)); - a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)] = a_Type; - } - - - inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_Index, BLOCKTYPE a_Type) - { - ASSERT((a_Index >= 0) && (a_Index <= NumBlocks)); - a_BlockTypes[a_Index] = a_Type; - } - - - inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z) - { - ASSERT((a_X >= 0) && (a_X < Width)); - ASSERT((a_Y >= 0) && (a_Y < Height)); - ASSERT((a_Z >= 0) && (a_Z < Width)); - return a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)]; - } - - - inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_Idx) - { - ASSERT((a_Idx >= 0) && (a_Idx < NumBlocks)); - return a_BlockTypes[a_Idx]; - } - - - inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z) - { - ASSERT((a_X >= 0) && (a_X <= Width)); - ASSERT((a_Z >= 0) && (a_Z <= Width)); - return a_HeightMap[a_X + Width * a_Z]; - } - - - inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height) - { - ASSERT((a_X >= 0) && (a_X <= Width)); - ASSERT((a_Z >= 0) && (a_Z <= Width)); - a_HeightMap[a_X + Width * a_Z] = a_Height; - } - - - inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z) - { - ASSERT((a_X >= 0) && (a_X <= Width)); - ASSERT((a_Z >= 0) && (a_Z <= Width)); - return a_BiomeMap[a_X + Width * a_Z]; - } - - - inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome) - { - ASSERT((a_X >= 0) && (a_X <= Width)); - ASSERT((a_Z >= 0) && (a_Z <= Width)); - a_BiomeMap[a_X + Width * a_Z] = a_Biome; - } - - - static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int a_BlockIdx) - { - if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks)) - { - return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f; - } - ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!"); - return 0; - } - - - static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z) - { - if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1)) - { - int Index = MakeIndexNoCheck(x, y, z); - return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f; - } - ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!"); - return 0; - } - - - static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble) - { - if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks)) - { - ASSERT(!"cChunkDef::SetNibble(): index out of range!"); - return; - } - a_Buffer[a_BlockIdx / 2] = ( - (a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble - ((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set - ); - } - - - static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble) - { - if ( - (x >= Width) || (x < 0) || - (y >= Height) || (y < 0) || - (z >= Width) || (z < 0) - ) - { - ASSERT(!"cChunkDef::SetNibble(): index out of range!"); - return; - } - - int Index = MakeIndexNoCheck(x, y, z); - a_Buffer[Index / 2] = ( - (a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble - ((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set - ); - } - - - inline static char GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos ) - { - return GetNibble(a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); - } - - - inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, char a_Value ) - { - SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value ); - } - -} ; - - - - - -/** Interface class used for getting data out of a chunk using the GetAllData() function. -Implementation must use the pointers immediately and NOT store any of them for later use -The virtual methods are called in the same order as they're declared here. -*/ -class cChunkDataCallback abstract -{ -public: - /** Called before any other callbacks to inform of the current coords - (only in processes where multiple chunks can be processed, such as cWorld::ForEachChunkInRect()). - If false is returned, the chunk is skipped. - */ - virtual bool Coords(int a_ChunkX, int a_ChunkZ) { UNUSED(a_ChunkX); UNUSED(a_ChunkZ); return true; }; - - /// Called once to provide heightmap data - virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {UNUSED(a_HeightMap); }; - - /// Called once to provide biome data - virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) {UNUSED(a_BiomeMap); }; - - /// Called once to export block types - virtual void BlockTypes (const BLOCKTYPE * a_Type) {UNUSED(a_Type); }; - - /// Called once to export block meta - virtual void BlockMeta (const NIBBLETYPE * a_Meta) {UNUSED(a_Meta); }; - - /// Called once to let know if the chunk lighting is valid. Return value is used to control if BlockLight() and BlockSkyLight() are called next (if true) - virtual bool LightIsValid(bool a_IsLightValid) {UNUSED(a_IsLightValid); return true; }; - - /// Called once to export block light - virtual void BlockLight (const NIBBLETYPE * a_BlockLight) {UNUSED(a_BlockLight); }; - - /// Called once to export sky light - virtual void BlockSkyLight(const NIBBLETYPE * a_SkyLight) {UNUSED(a_SkyLight); }; - - /// Called for each entity in the chunk - virtual void Entity(cEntity * a_Entity) {UNUSED(a_Entity); }; - - /// Called for each blockentity in the chunk - virtual void BlockEntity(cBlockEntity * a_Entity) {UNUSED(a_Entity); }; -} ; - - - - - -/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer -*/ -class cChunkDataCollector : - public cChunkDataCallback -{ -public: - - // Must be unsigned char instead of BLOCKTYPE or NIBBLETYPE, because it houses both. - unsigned char m_BlockData[cChunkDef::BlockDataSize]; - -protected: - - virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override - { - memcpy(m_BlockData, a_BlockTypes, sizeof(cChunkDef::BlockTypes)); - } - - - virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override - { - memcpy(m_BlockData + cChunkDef::NumBlocks, a_BlockMeta, cChunkDef::NumBlocks / 2); - } - - - virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override - { - memcpy(m_BlockData + 3 * cChunkDef::NumBlocks / 2, a_BlockLight, cChunkDef::NumBlocks / 2); - } - - - virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override - { - memcpy(m_BlockData + 2 * cChunkDef::NumBlocks, a_BlockSkyLight, cChunkDef::NumBlocks / 2); - } -} ; - - - - - -/** A simple implementation of the cChunkDataCallback interface that collects all block data into a separate buffers -*/ -class cChunkDataSeparateCollector : - public cChunkDataCallback -{ -public: - - cChunkDef::BlockTypes m_BlockTypes; - cChunkDef::BlockNibbles m_BlockMetas; - cChunkDef::BlockNibbles m_BlockLight; - cChunkDef::BlockNibbles m_BlockSkyLight; - -protected: - - virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override - { - memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes)); - } - - - virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override - { - memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas)); - } - - - virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override - { - memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); - } - - - virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override - { - memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight)); - } -} ; - - - - - -/** Interface class used for comparing clients of two chunks. -Used primarily for entity moving while both chunks are locked. -*/ -class cClientDiffCallback -{ -public: - /// Called for clients that are in Chunk1 and not in Chunk2, - virtual void Removed(cClientHandle * a_Client) = 0; - - /// Called for clients that are in Chunk2 and not in Chunk1. - virtual void Added(cClientHandle * a_Client) = 0; -} ; - - - - - -struct sSetBlock -{ - int x, y, z; - int ChunkX, ChunkZ; - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - - sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); // absolute block position - sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : - x(a_X), y(a_Y), z(a_Z), - ChunkX(a_ChunkX), ChunkZ(a_ChunkZ), - BlockType(a_BlockType), - BlockMeta(a_BlockMeta) - {} -}; - -typedef std::list sSetBlockList; -typedef std::vector sSetBlockVector; - - - - - -class cChunkCoords -{ -public: - int m_ChunkX; - int m_ChunkY; - int m_ChunkZ; - - cChunkCoords(int a_ChunkX, int a_ChunkY, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ) {} - - bool operator == (const cChunkCoords & a_Other) const - { - return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkY == a_Other.m_ChunkY) && (m_ChunkZ == a_Other.m_ChunkZ)); - } -} ; - -typedef std::list cChunkCoordsList; - - - - - -/// Interface class used as a callback for operations that involve chunk coords -class cChunkCoordCallback -{ -public: - virtual void Call(int a_ChunkX, int a_ChunkZ) = 0; -} ; - - - - - -/// Generic template that can store any kind of data together with a triplet of 3 coords: -template class cCoordWithData -{ -public: - int x; - int y; - int z; - X Data; - - cCoordWithData(int a_X, int a_Y, int a_Z) : - x(a_X), y(a_Y), z(a_Z) - { - } - - cCoordWithData(int a_X, int a_Y, int a_Z, const X & a_Data) : - x(a_X), y(a_Y), z(a_Z), Data(a_Data) - { - } -} ; - -// Illegal in C++03: typedef std::list< cCoordWithData > cCoordWithDataList; -typedef cCoordWithData cCoordWithInt; -typedef std::list cCoordWithIntList; -typedef std::vector cCoordWithIntVector; - - - - diff --git a/source/ChunkMap.cpp b/source/ChunkMap.cpp deleted file mode 100644 index 73a16dbb4..000000000 --- a/source/ChunkMap.cpp +++ /dev/null @@ -1,2668 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "ChunkMap.h" -#include "World.h" -#include "Root.h" -#include "Entities/Player.h" -#include "Item.h" -#include "Entities/Pickup.h" -#include "Chunk.h" -#include "Generating/Trees.h" // used in cChunkMap::ReplaceTreeBlocks() for tree block discrimination -#include "BlockArea.h" -#include "PluginManager.h" -#include "Entities/TNTEntity.h" -#include "Blocks/BlockHandler.h" -#include "MobCensus.h" -#include "MobSpawner.h" - -#ifndef _WIN32 - #include // abs -#endif - -#include "zlib.h" -#include - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cChunkMap: - -cChunkMap::cChunkMap(cWorld * a_World ) - : m_World( a_World ) -{ -} - - - - - -cChunkMap::~cChunkMap() -{ - cCSLock Lock(m_CSLayers); - while (!m_Layers.empty()) - { - delete m_Layers.back(); - m_Layers.pop_back(); // Must pop, because further chunk deletions query the chunkmap for entities and that would touch deleted data - } -} - - - - - -void cChunkMap::RemoveLayer( cChunkLayer* a_Layer ) -{ - cCSLock Lock(m_CSLayers); - m_Layers.remove(a_Layer); -} - - - - - -cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) - { - return *itr; - } - } - - // Not found, create new: - cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this); - if (Layer == NULL) - { - LOGERROR("cChunkMap: Cannot create new layer, server out of memory?"); - return NULL; - } - m_Layers.push_back(Layer); - return Layer; -} - - - - - -cChunkMap::cChunkLayer * cChunkMap::FindLayerForChunk(int a_ChunkX, int a_ChunkZ) -{ - const int LayerX = FAST_FLOOR_DIV(a_ChunkX, LAYER_SIZE); - const int LayerZ = FAST_FLOOR_DIV(a_ChunkZ, LAYER_SIZE); - return FindLayer(LayerX, LayerZ); -} - - - - - -cChunkMap::cChunkLayer * cChunkMap::FindLayer(int a_LayerX, int a_LayerZ) -{ - ASSERT(m_CSLayers.IsLockedByCurrentThread()); - - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) - { - return *itr; - } - } // for itr - m_Layers[] - - // Not found - return NULL; -} - - - - - -cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ) -{ - const int LayerX = FAST_FLOOR_DIV(a_ChunkX, LAYER_SIZE); - const int LayerZ = FAST_FLOOR_DIV(a_ChunkZ, LAYER_SIZE); - return GetLayer(LayerX, LayerZ); -} - - - - - -cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) -{ - // No need to lock m_CSLayers, since it's already locked by the operation that called us - ASSERT(m_CSLayers.IsLockedByCurrentThread()); - - cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); - if (Layer == NULL) - { - // An error must have occurred, since layers are automatically created if they don't exist - return NULL; - } - - cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - return NULL; - } - if (!(Chunk->IsValid())) - { - m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, true); - } - return Chunk; -} - - - - - -cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) -{ - // No need to lock m_CSLayers, since it's already locked by the operation that called us - cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); - if (Layer == NULL) - { - // An error must have occurred, since layers are automatically created if they don't exist - return NULL; - } - - cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - return NULL; - } - if (!(Chunk->IsValid())) - { - m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, false); - } - - return Chunk; -} - - - - - -cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) -{ - // No need to lock m_CSLayers, since it's already locked by the operation that called us - cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); - if (Layer == NULL) - { - // An error must have occurred, since layers are automatically created if they don't exist - return NULL; - } - - return Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - // We already have m_CSLayers locked since this can be called only from within the tick thread - ASSERT(m_CSLayers.IsLockedByCurrentThread()); - - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return false; - } - - int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); - a_BlockType = Chunk->GetBlock(Index); - a_BlockMeta = Chunk->GetMeta(Index); - return true; -} - - - - - -bool cChunkMap::LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType) -{ - // We already have m_CSLayers locked since this can be called only from within the tick thread - ASSERT(m_CSLayers.IsLockedByCurrentThread()); - - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return false; - } - - int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); - a_BlockType = Chunk->GetBlock(Index); - return true; -} - - - - - -bool cChunkMap::LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE & a_BlockMeta) -{ - // We already have m_CSLayers locked since this can be called only from within the tick thread - ASSERT(m_CSLayers.IsLockedByCurrentThread()); - - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return false; - } - - int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); - a_BlockMeta = Chunk->GetMeta(Index); - return true; -} - - - - - -bool cChunkMap::LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - // We already have m_CSLayers locked since this can be called only from within the tick thread - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return false; - } - - Chunk->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); - return true; -} - - - - - -bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - // We already have m_CSLayers locked since this can be called only from within the tick thread - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return false; - } - - Chunk->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); - return true; -} - - - - - -cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ) -{ - ASSERT(m_CSLayers.IsLockedByCurrentThread()); - - cChunkLayer * Layer = FindLayerForChunk(a_ChunkX, a_ChunkZ); - if (Layer == NULL) - { - return NULL; - } - return Layer->FindChunk(a_ChunkX, a_ChunkZ); -} - - - - - -void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastAttachEntity(a_Entity, a_Vehicle); -} - - - - - -void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int x, y, z, ChunkX, ChunkZ; - x = a_BlockX; - y = a_BlockY; - z = a_BlockZ; - cChunkDef::BlockToChunk(x, z, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); -} - - - - - -void cChunkMap::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - - cChunkDef::BlockToChunk(a_blockX, a_blockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastBlockBreakAnimation(a_entityID, a_blockX, a_blockY, a_blockZ, a_stage, a_Exclude); -} - - - - - -void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); -} - - - - - -void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, 0, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastChunkData(a_Serializer, a_Exclude); -} - - - - - -void cChunkMap::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Pickup.GetChunkX(), ZERO_CHUNK_Y, a_Pickup.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); -} - - - - - -void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastDestroyEntity(a_Entity, a_Exclude); -} - - - - - -void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); -} - - - - - -void cChunkMap::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityHeadLook(a_Entity, a_Exclude); -} - - - - - -void cChunkMap::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityLook(a_Entity, a_Exclude); -} - - - - - -void cChunkMap::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityMetadata(a_Entity, a_Exclude); -} - - - - - -void cChunkMap::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); -} - - - - - -void cChunkMap::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); -} - - - - - -void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); -} - - - - - -void cChunkMap::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityVelocity(a_Entity, a_Exclude); -} - - - - - -void cChunkMap::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Player.GetChunkX(), ZERO_CHUNK_Y, a_Player.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); -} - - - - - -void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - - cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcZ / 8, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); -} - - - - - -void cChunkMap::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - - cChunkDef::BlockToChunk(a_SrcX, a_SrcZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data, a_Exclude); -} - - - - - -void cChunkMap::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastSpawnEntity(a_Entity, a_Exclude); -} - - - - - -void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); -} - - - - - -void cChunkMap::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - - cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cChunkMap::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); -} - - - - - -void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // a_Player rclked block entity at the coords specified, handle it - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -bool cChunkMap::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - return false; - } - return a_Callback.Item(Chunk); -} - - - - - -void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk); -} - - - - - -/// Wakes up the simulators for the specified area of blocks -void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ) -{ - cSimulatorManager * SimMgr = m_World->GetSimulatorManager(); - int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ; - cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ); - cChunkDef::BlockToChunk(a_MaxBlockX, a_MaxBlockZ, MaxChunkX, MaxChunkZ); - for (int z = MinChunkZ; z <= MaxChunkZ; z++) - { - int MinZ = std::max(a_MinBlockZ, z * cChunkDef::Width); - int MaxZ = std::min(a_MaxBlockZ, z * cChunkDef::Width + cChunkDef::Width - 1); - for (int x = MinChunkX; x <= MaxChunkX; x++) - { - cChunkPtr Chunk = GetChunkNoGen(x, 0, z); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - continue; - } - int MinX = std::max(a_MinBlockX, x * cChunkDef::Width); - int MaxX = std::min(a_MaxBlockX, x * cChunkDef::Width + cChunkDef::Width - 1); - for (int BlockY = a_MinBlockY; BlockY <= a_MaxBlockY; BlockY++) - { - for (int BlockZ = MinZ; BlockZ <= MaxZ; BlockZ++) - { - for (int BlockX = MinX; BlockX <= MaxX; BlockX++) - { - SimMgr->WakeUp(BlockX, BlockY, BlockZ, Chunk); - } // for BlockX - } // for BlockZ - } // for BlockY - } // for x - chunks - } // for z = chunks -} - - - - - -void cChunkMap::MarkChunkDirty (int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->MarkDirty(); -} - - - - - -void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->MarkSaving(); -} - - - - - -void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->MarkSaved(); -} - - - - - -void cChunkMap::SetChunkData( - int a_ChunkX, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap & a_BiomeMap, - cBlockEntityList & a_BlockEntities, - bool a_MarkDirty -) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_BlockEntities); - - if (a_MarkDirty) - { - Chunk->MarkDirty(); - } - - // Notify plugins of the chunk becoming available - cPluginManager::Get()->CallHookChunkAvailable(m_World, a_ChunkX, a_ChunkZ); -} - - - - - -void cChunkMap::ChunkLighted( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight -) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - Chunk->SetLight(a_BlockLight, a_SkyLight); - Chunk->MarkDirty(); -} - - - - - -bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return false; - } - Chunk->GetAllData(a_Callback); - return true; -} - - - - - -bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return false; - } - Chunk->GetBlockTypes(a_BlockTypes); - return true; -} - - - - - -bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - return (Chunk != NULL) && Chunk->IsValid(); -} - - - - - -bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - return (Chunk != NULL) && Chunk->HasAnyClients(); -} - - - - - -int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ) -{ - while (true) - { - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ, BlockY = 0; - cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return 0; - } - - if (Chunk->IsValid()) - { - return Chunk->GetHeight(a_BlockX, a_BlockZ); - } - - // The chunk is not valid, wait for it to become valid: - cCSUnlock Unlock(Lock); - m_evtChunkValid.Wait(); - } // while (true) -} - - - - - -bool cChunkMap::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height) -{ - // Returns false if chunk not loaded / generated - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ, BlockY = 0; - cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return false; - } - a_Height = Chunk->GetHeight(a_BlockX, a_BlockZ); - return true; -} - - - - - -void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) -{ - sSetBlockList Failed; - - // Process all items from a_BlockList, either successfully or by placing into Failed - while (!a_BlockList.empty()) - { - int ChunkX = a_BlockList.front().ChunkX; - int ChunkZ = a_BlockList.front().ChunkZ; - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) - { - if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) - { - Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - itr = a_BlockList.erase(itr); - } - else - { - ++itr; - } - } // for itr - a_BlockList[] - } - else - { - // The chunk is not valid, move all blocks within this chunk to Failed - for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) - { - if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) - { - Failed.push_back(*itr); - itr = a_BlockList.erase(itr); - } - else - { - ++itr; - } - } // for itr - a_BlockList[] - } - } - - // Return the failed: - std::swap(Failed, a_BlockList); -} - - - - - -void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player) -{ - int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway - int BlockY = (int)(a_Player->GetPosY()); - int BlockZ = (int)(a_Player->GetPosZ()); - int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1); - int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1); - - // We suppose that each player keeps their chunks in memory, therefore it makes little sense to try to re-load or even generate them. - // The only time the chunks are not valid is when the player is downloading the initial world and they should not call this at that moment - - cCSLock Lock(m_CSLayers); - GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player); - - // Check the neighboring chunks as well: - GetChunkNoLoad(OtherChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); - GetChunkNoLoad(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); - GetChunkNoLoad(ChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); - GetChunkNoLoad(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); -} - - - - - -BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - } - return 0; -} - - - - - -NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid() ) - { - return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ); - } - return 0; -} - - - - - -NIBBLETYPE cChunkMap::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid() ) - { - return Chunk->GetSkyLight(a_BlockX, a_BlockY, a_BlockZ); - } - return 0; -} - - - - - -NIBBLETYPE cChunkMap::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid() ) - { - return Chunk->GetBlockLight(a_BlockX, a_BlockY, a_BlockZ); - } - return 0; -} - - - - - -void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - // a_BlockXYZ now contains relative coords! - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - Chunk->SetMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta); - Chunk->MarkDirty(); - Chunk->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, NULL); - } -} - - - - - -void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) -{ - int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); - m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk); - } -} - - - - - -void cChunkMap::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick) -{ - int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - Chunk->QueueSetBlock(X, Y, Z, a_BlockType, a_BlockMeta, a_Tick); - } -} - - - - - -bool cChunkMap::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - Chunk->GetBlockTypeMeta(X, Y, Z, a_BlockType, a_BlockMeta); - return true; - } - return false; -} - - - - - -bool cChunkMap::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) -{ - int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - Chunk->GetBlockInfo(X, Y, Z, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); - return true; - } - return false; -} - - - - - -void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) -{ - cCSLock Lock(m_CSLayers); - for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) - { - cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - continue; - } - if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType) - { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - } - } -} - - - - - -void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks) -{ - cCSLock Lock(m_CSLayers); - for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) - { - cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - continue; - } - switch (Chunk->GetBlock(itr->x, itr->y, itr->z)) - { - CASE_TREE_OVERWRITTEN_BLOCKS: - { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - break; - } - case E_BLOCK_LEAVES: - { - if (itr->BlockType == E_BLOCK_LOG) - { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - } - break; - } - } - } // for itr - a_Blocks[] -} - - - - - -EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ) -{ - int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - return Chunk->GetBiomeAt(X, Z); - } - else - { - return m_World->GetGenerator().GetBiomeAt(a_BlockX, a_BlockZ); - } -} - - - - - -bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) -{ - bool res = true; - cCSLock Lock(m_CSLayers); - for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) - { - cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - if (!a_ContinueOnFailure) - { - return false; - } - res = false; - continue; - } - int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z); - itr->BlockType = Chunk->GetBlock(idx); - itr->BlockMeta = Chunk->GetMeta(idx); - } - return res; -} - - - - - -bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) -{ - int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ; - - cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ ); - - { - cCSLock Lock(m_CSLayers); - cChunkPtr DestChunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((DestChunk == NULL) || !DestChunk->IsValid()) - { - return false; - } - - DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0 ); - m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z, DestChunk); - } - - return true; -} - - - - - -void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk->IsValid()) - { - Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle()); - } -} - - - - - -void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, ZERO_CHUNK_Y, a_ChunkZ1); - if (Chunk1 == NULL) - { - return; - } - cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, ZERO_CHUNK_Y, a_ChunkZ2); - if (Chunk2 == NULL) - { - return; - } - - CompareChunkClients(Chunk1, Chunk2, a_Callback); -} - - - - - -void cChunkMap::CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClientDiffCallback & a_Callback) -{ - cClientHandleList Clients1(a_Chunk1->GetAllClients()); - cClientHandleList Clients2(a_Chunk2->GetAllClients()); - - // Find "removed" clients: - for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) - { - bool Found = false; - for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) - { - if (*itr1 == *itr2) - { - Found = true; - break; - } - } // for itr2 - Clients2[] - if (!Found) - { - a_Callback.Removed(*itr1); - } - } // for itr1 - Clients1[] - - // Find "added" clients: - for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) - { - bool Found = false; - for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) - { - if (*itr1 == *itr2) - { - Found = true; - break; - } - } // for itr1 - Clients1[] - if (!Found) - { - a_Callback.Added(*itr2); - } - } // for itr2 - Clients2[] -} - - - - - -bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - return false; - } - return Chunk->AddClient(a_Client); -} - - - - - -void cChunkMap::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - Chunk->RemoveClient(a_Client); -} - - - - - -void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client) -{ - cCSLock Lock(m_CSLayers); - - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->RemoveClient(a_Client); - } // for itr - m_Layers[] -} - - - - - -void cChunkMap::AddEntity(cEntity * a_Entity) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - LOGWARNING("Entity at %p (%s, ID %d) spawning in a non-existent chunk, the entity is lost.", - a_Entity, a_Entity->GetClass(), a_Entity->GetUniqueID() - ); - return; - } - Chunk->AddEntity(a_Entity); -} - - - - - -bool cChunkMap::HasEntity(int a_UniqueID) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - if ((*itr)->HasEntity(a_UniqueID)) - { - return true; - } - } - return false; -} - - - - - -void cChunkMap::RemoveEntity(cEntity * a_Entity) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return; - } - Chunk->RemoveEntity(a_Entity); -} - - - - - -bool cChunkMap::ForEachEntity(cEntityCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - if (!(*itr)->ForEachEntity(a_Callback)) - { - return false; - } - } - return true; -} - - - - - -bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->ForEachEntity(a_Callback); -} - - - - - -void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlocksAffected) -{ - // Don't explode if outside of Y range (prevents the following test running into unallocated memory): - if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height - 1)) - { - return; - } - - // Don't explode if the explosion center is inside a liquid block: - switch (m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ))) - { - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - return; - } - } - - cBlockArea area; - int bx = (int)floor(a_BlockX); - int by = (int)floor(a_BlockY); - int bz = (int)floor(a_BlockZ); - int ExplosionSizeInt = (int) ceil(a_ExplosionSize); - int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt; - a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt); - int MinY = std::max((int)floor(a_BlockY - ExplosionSizeInt), 0); - int MaxY = std::min((int)ceil(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1); - area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt)); - for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++) - { - for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++) - { - if ((by + y >= cChunkDef::Height) || (by + y < 0)) - { - // Outside of the world - continue; - } - for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++) - { - if ((x * x + y * y + z * z) > ExplosionSizeSq) - { - // Too far away - continue; - } - - BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z); - switch (Block) - { - case E_BLOCK_TNT: - { - // Activate the TNT, with a random fuse between 10 to 30 game ticks - double FuseTime = (double)(10 + m_World->GetTickRandomNumber(20)) / 20; - m_World->SpawnPrimedTNT(a_BlockX + x + 0.5, a_BlockY + y + 0.5, a_BlockZ + z + 0.5, FuseTime); - area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR); - a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); - break; - } - case E_BLOCK_OBSIDIAN: - case E_BLOCK_BEDROCK: - case E_BLOCK_WATER: - case E_BLOCK_LAVA: - { - // These blocks are not affected by explosions - break; - } - - case E_BLOCK_STATIONARY_WATER: - { - // Turn into simulated water: - area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER); - break; - } - - case E_BLOCK_STATIONARY_LAVA: - { - // Turn into simulated lava: - area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA); - break; - } - - case E_BLOCK_AIR: - { - // No pickups for air - break; - } - - default: - { - if (m_World->GetTickRandomNumber(10) == 5) - { - cItems Drops; - cBlockHandler * Handler = BlockHandler(Block); - - Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); - m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z); - } - area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR); - a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); - } - } // switch (BlockType) - } // for z - } // for y - } // for x - area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt); - - // Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391): - WakeUpSimulatorsInArea( - bx - ExplosionSizeInt, bx + ExplosionSizeInt + 1, - MinY, MaxY, - bz - ExplosionSizeInt, bz + ExplosionSizeInt + 1 - ); -} - - - - - -bool cChunkMap::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - bool res = false; - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - if ((*itr)->DoWithEntityByID(a_UniqueID, a_Callback, res)) - { - return res; - } - } - return false; -} - - - - - -bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->ForEachChest(a_Callback); -} - - - - - -bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->ForEachDispenser(a_Callback); -} - - - - - -bool cChunkMap::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->ForEachDropper(a_Callback); -} - - - - - -bool cChunkMap::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->ForEachDropSpenser(a_Callback); -} - - - - - -bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->ForEachFurnace(a_Callback); -} - - - - - -bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) -{ - int ChunkX, ChunkZ; - int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cChunkMap::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) -{ - int ChunkX, ChunkZ; - int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cChunkMap::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback) -{ - int ChunkX, ChunkZ; - int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->DoWithDropperAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cChunkMap::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback) -{ - int ChunkX, ChunkZ; - int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) -{ - int ChunkX, ChunkZ; - int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) -{ - int ChunkX, ChunkZ; - int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); -} - - - - - -void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -/// Loads the chunk synchronously, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) -bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - { - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - // Internal error - return false; - } - if (Chunk->IsValid()) - { - // Already loaded - return true; - } - if (Chunk->HasLoadFailed()) - { - // Already tried loading and it failed - return false; - } - } - return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() -void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks) -{ - for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) - { - LoadChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); - } // for itr - a_Chunks[] -} - - - - - -void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - Chunk->MarkLoadFailed(); -} - - - - - -bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return false; - } - return Chunk->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); -} - - - - - -void cChunkMap::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) -{ - cCSLock Lock(m_CSLayers); - for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) - { - cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); - if (Chunk == NULL) - { - continue; - } - Chunk->Stay(a_Stay); - } -} - - - - - -void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - // Not present - return; - } - Chunk->MarkRegenerating(); -} - - - - - -bool cChunkMap::IsChunkLighted(int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - // Not present - return false; - } - return Chunk->IsLightValid(); -} - - - - - -bool cChunkMap::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) -{ - bool Result = true; - cCSLock Lock(m_CSLayers); - for (int z = a_MinChunkZ; z <= a_MaxChunkZ; z++) - { - for (int x = a_MinChunkX; x <= a_MaxChunkX; x++) - { - cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); - if ((Chunk == NULL) || (!Chunk->IsValid())) - { - // Not present / not valid - Result = false; - continue; - } - if (!a_Callback.Coords(x, z)) - { - continue; - } - Chunk->GetAllData(a_Callback); - } - } - return Result; -} - - - - - -bool cChunkMap::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) -{ - // Convert block coords to chunks coords: - int MinChunkX, MaxChunkX; - int MinChunkZ, MaxChunkZ; - int MinBlockX = a_MinBlockX; - int MinBlockY = a_MinBlockY; - int MinBlockZ = a_MinBlockZ; - int MaxBlockX = a_MinBlockX + a_Area.GetSizeX(); - int MaxBlockY = a_MinBlockY + a_Area.GetSizeY(); - int MaxBlockZ = a_MinBlockZ + a_Area.GetSizeZ(); - cChunkDef::AbsoluteToRelative(MinBlockX, MinBlockY, MinBlockZ, MinChunkX, MinChunkZ); - cChunkDef::AbsoluteToRelative(MaxBlockX, MaxBlockY, MaxBlockZ, MaxChunkX, MaxChunkZ); - - // Iterate over chunks, write data into each: - bool Result = true; - cCSLock Lock(m_CSLayers); - for (int z = MinChunkZ; z <= MaxChunkZ; z++) - { - for (int x = MinChunkX; x <= MaxChunkX; x++) - { - cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); - if ((Chunk == NULL) || (!Chunk->IsValid())) - { - // Not present / not valid - Result = false; - continue; - } - Chunk->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); - } // for x - } // for z - return Result; -} - - - - - -void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) -{ - a_NumChunksValid = 0; - a_NumChunksDirty = 0; - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - int NumValid = 0, NumDirty = 0; - (*itr)->GetChunkStats(NumValid, NumDirty); - a_NumChunksValid += NumValid; - a_NumChunksDirty += NumDirty; - } // for itr - m_Layers[] -} - - - - - -void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk != NULL) - { - Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand); - } -} - - - - - -void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk != NULL) - { - Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); - } -} - - - - - -void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk != NULL) - { - Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); - } -} - - - - - -void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk != NULL) - { - Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); - } -} - - - - -void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->CollectMobCensus(a_ToFill); - } // for itr - m_Layers -} - - - - - - -void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->SpawnMobs(a_MobSpawner); - } // for itr - m_Layers -} - - - - - -void cChunkMap::Tick(float a_Dt) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->Tick(a_Dt); - } // for itr - m_Layers -} - - - - - -void cChunkMap::UnloadUnusedChunks() -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->UnloadUnusedChunks(); - } // for itr - m_Layers -} - - - - - -void cChunkMap::SaveAllChunks(void) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->Save(); - } // for itr - m_Layers[] -} - - - - - -int cChunkMap::GetNumChunks(void) -{ - cCSLock Lock(m_CSLayers); - int NumChunks = 0; - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - NumChunks += (*itr)->GetNumChunksLoaded(); - } - return NumChunks; -} - - - - - -void cChunkMap::ChunkValidated(void) -{ - m_evtChunkValid.Set(); -} - - - - - -void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - // a_BlockXYZ now contains relative coords! - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk != NULL) - { - Chunk->QueueTickBlock(a_BlockX, a_BlockY, a_BlockZ); - } -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cChunkMap::cChunkLayer: - -cChunkMap::cChunkLayer::cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent) - : m_LayerX( a_LayerX ) - , m_LayerZ( a_LayerZ ) - , m_Parent( a_Parent ) - , m_NumChunksLoaded( 0 ) -{ - memset(m_Chunks, 0, sizeof(m_Chunks)); -} - - - - - -cChunkMap::cChunkLayer::~cChunkLayer() -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) - { - delete m_Chunks[i]; - m_Chunks[i] = NULL; // // Must zero out, because further chunk deletions query the chunkmap for entities and that would touch deleted data - } // for i - m_Chunks[] -} - - - - - -cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) -{ - // Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check - - const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; - const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; - - if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) - { - ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); - return NULL; - } - - int Index = LocalX + LocalZ * LAYER_SIZE; - if (m_Chunks[Index] == NULL) - { - cChunk * neixm = (LocalX > 0) ? m_Chunks[Index - 1] : m_Parent->FindChunk(a_ChunkX - 1, a_ChunkZ); - cChunk * neixp = (LocalX < LAYER_SIZE - 1) ? m_Chunks[Index + 1] : m_Parent->FindChunk(a_ChunkX + 1, a_ChunkZ); - cChunk * neizm = (LocalZ > 0) ? m_Chunks[Index - LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ - 1); - cChunk * neizp = (LocalZ < LAYER_SIZE - 1) ? m_Chunks[Index + LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ + 1); - m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp); - } - return m_Chunks[Index]; -} - - - - - -cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ) -{ - const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; - const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; - - if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) - { - ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); - return NULL; - } - - int Index = LocalX + LocalZ * LAYER_SIZE; - return m_Chunks[Index]; -} - - - - -void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill) -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - // We do count every Mobs in the world. But we are assuming that every chunk not loaded by any client - // doesn't affect us. Normally they should not have mobs because every "too far" mobs despawn - // If they have (f.i. when player disconnect) we assume we don't have to make them live or despawn - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) - { - m_Chunks[i]->CollectMobCensus(a_ToFill); - } - } // for i - m_Chunks[] -} - - - - - - -void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - // We only spawn close to players - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) - { - m_Chunks[i]->SpawnMobs(a_MobSpawner); - } - } // for i - m_Chunks[] -} - - - -void cChunkMap::cChunkLayer::Tick(float a_Dt) -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - // Only tick chunks that are valid and have clients: - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) - { - m_Chunks[i]->Tick(a_Dt); - } - } // for i - m_Chunks[] -} - - - - - -void cChunkMap::cChunkLayer::RemoveClient(cClientHandle * a_Client) -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - if (m_Chunks[i] != NULL) - { - m_Chunks[i]->RemoveClient(a_Client); - } - } // for i - m_Chunks[] -} - - - - - -bool cChunkMap::cChunkLayer::ForEachEntity(cEntityCallback & a_Callback) -{ - // Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) - { - if (!m_Chunks[i]->ForEachEntity(a_Callback)) - { - return false; - } - } - } - return true; -} - - - - - -bool cChunkMap::cChunkLayer::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackReturn) -{ - // Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) - { - if (m_Chunks[i]->DoWithEntityByID(a_EntityID, a_Callback, a_CallbackReturn)) - { - return true; - } - } - } - return false; -} - - - - - -bool cChunkMap::cChunkLayer::HasEntity(int a_EntityID) -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) - { - if (m_Chunks[i]->HasEntity(a_EntityID)) - { - return true; - } - } - } - return false; -} - - - - - -int cChunkMap::cChunkLayer::GetNumChunksLoaded(void) const -{ - int NumChunks = 0; - for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) - { - if (m_Chunks[i] != NULL) - { - NumChunks++; - } - } // for i - m_Chunks[] - return NumChunks; -} - - - - - -void cChunkMap::cChunkLayer::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const -{ - int NumValid = 0; - int NumDirty = 0; - for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) - { - if (m_Chunks[i] == NULL) - { - continue; - } - NumValid++; - if (m_Chunks[i]->IsDirty()) - { - NumDirty++; - } - } // for i - m_Chunks[] - a_NumChunksValid = NumValid; - a_NumChunksDirty = NumDirty; -} - - - - - -void cChunkMap::cChunkLayer::Save(void) -{ - cWorld * World = m_Parent->GetWorld(); - for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) - { - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->IsDirty()) - { - World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosY(), m_Chunks[i]->GetPosZ()); - } - } // for i - m_Chunks[] -} - - - - - -void cChunkMap::cChunkLayer::UnloadUnusedChunks(void) -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - if ( - (m_Chunks[i] != NULL) && // Is valid - (m_Chunks[i]->CanUnload()) && // Can unload - !cPluginManager::Get()->CallHookChunkUnloading(m_Parent->GetWorld(), m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosZ()) // Plugins agree - ) - { - // The cChunk destructor calls our GetChunk() while removing its entities - // so we still need to be able to return the chunk. Therefore we first delete, then NULLify - // Doing otherwise results in bug http://forum.mc-server.org/showthread.php?tid=355 - delete m_Chunks[i]; - m_Chunks[i] = NULL; - } - } // for i - m_Chunks[] -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cChunkStay: - -cChunkStay::cChunkStay(cWorld * a_World) : - m_World(a_World), - m_IsEnabled(false) -{ -} - - - - - -cChunkStay::~cChunkStay() -{ - Clear(); -} - - - - - -void cChunkStay::Clear(void) -{ - if (m_IsEnabled) - { - Disable(); - } - m_Chunks.clear(); -} - - - - - -void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - ASSERT(!m_IsEnabled); - - for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) - { - // Already present - return; - } - } // for itr - Chunks[] - m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); -} - - - - - -void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - ASSERT(!m_IsEnabled); - - for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) - { - // Found, un-"stay" - m_Chunks.erase(itr); - return; - } - } // for itr - m_Chunks[] -} - - - - - -void cChunkStay::Enable(void) -{ - ASSERT(!m_IsEnabled); - - m_World->ChunksStay(*this, true); - m_IsEnabled = true; -} - - - - - -void cChunkStay::Load(void) -{ - for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) - { - m_World->TouchChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); - } // for itr - m_Chunks[] -} - - - - - -void cChunkStay::Disable(void) -{ - ASSERT(m_IsEnabled); - - m_World->ChunksStay(*this, false); - m_IsEnabled = false; -} - - - - diff --git a/source/ChunkMap.h b/source/ChunkMap.h deleted file mode 100644 index f68cb6472..000000000 --- a/source/ChunkMap.h +++ /dev/null @@ -1,432 +0,0 @@ - -// cChunkMap.h - -// Interfaces to the cChunkMap class representing the chunk storage for a single world - -#pragma once - -#include "ChunkDef.h" - - - - - -class cWorld; -class cItem; -class MTRand; -class cChunkStay; -class cChunk; -class cPlayer; -class cChestEntity; -class cDispenserEntity; -class cDropperEntity; -class cDropSpenserEntity; -class cFurnaceEntity; -class cPawn; -class cPickup; -class cChunkDataSerializer; -class cBlockArea; -class cMobCensus; -class cMobSpawner; - -typedef std::list cClientHandleList; -typedef cChunk * cChunkPtr; -typedef cItemCallback cEntityCallback; -typedef cItemCallback cChestCallback; -typedef cItemCallback cDispenserCallback; -typedef cItemCallback cDropperCallback; -typedef cItemCallback cDropSpenserCallback; -typedef cItemCallback cFurnaceCallback; -typedef cItemCallback cChunkCallback; - - - - - -class cChunkMap -{ -public: - - static const int LAYER_SIZE = 32; - - cChunkMap(cWorld* a_World ); - ~cChunkMap(); - - // Broadcast respective packets to all clients of the chunk where the event is taking place - // (Please keep these alpha-sorted) - void BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle); - void BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); - void BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude = NULL); - void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude); - void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); - void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); - void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); - void BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 - void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); - void BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); - void BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); - - /// Sends the block entity, if it is at the coords specified, to a_Client - void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); - - /// a_Player rclked block entity at the coords specified, handle it - void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); - - /// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback - bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback); - - /// Wakes up simulators for the specified block - void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Wakes up the simulators for the specified area of blocks - void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ); - - void MarkChunkDirty (int a_ChunkX, int a_ChunkZ); - void MarkChunkSaving (int a_ChunkX, int a_ChunkZ); - void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); - - /** Sets the chunk data as either loaded from the storage or generated. - a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. - a_BiomeMap is optional, if not present, biomes will be calculated by the generator - a_HeightMap is optional, if not present, will be calculated. - If a_MarkDirty is set, the chunk is set as dirty (used after generating) - */ - void SetChunkData( - int a_ChunkX, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap & a_BiomeMap, - cBlockEntityList & a_BlockEntities, - bool a_MarkDirty - ); - - void ChunkLighted( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight - ); - - bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback); - - /// Copies the chunk's blocktypes into a_Blocks; returns true if successful - bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks); - - bool IsChunkValid (int a_ChunkX, int a_ChunkZ); - bool HasChunkAnyClients (int a_ChunkX, int a_ChunkZ); - int GetHeight (int a_BlockX, int a_BlockZ); // Waits for the chunk to get loaded / generated - bool TryGetHeight (int a_BlockX, int a_BlockZ, int & a_Height); // Returns false if chunk not loaded / generated - void FastSetBlocks (sSetBlockList & a_BlockList); - void CollectPickupsByPlayer(cPlayer * a_Player); - - BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ); - NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ); - NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ); - NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ); - void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockMeta); - void SetBlock (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta); - void QueueSetBlock (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick); - bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); - - /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType - void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); - - /// Special function used for growing trees, replaces only blocks that tree may overwrite - void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks); - - EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ); - - /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. - bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); - - bool DigBlock (int a_X, int a_Y, int a_Z); - void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player); - - /// Compares clients of two chunks, calls the callback accordingly - void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback); - - /// Compares clients of two chunks, calls the callback accordingly - void CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClientDiffCallback & a_Callback); - - /// Adds client to a chunk, if not already present; returns true if added, false if present - bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes the client from the chunk - void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes the client from all chunks it is present in - void RemoveClientFromChunks(cClientHandle * a_Client); - - /// Adds the entity to its appropriate chunk, takes ownership of the entity pointer - void AddEntity(cEntity * a_Entity); - - /// Returns true if the entity with specified ID is present in the chunks - bool HasEntity(int a_EntityID); - - /// Removes the entity from its appropriate chunk - void RemoveEntity(cEntity * a_Entity); - - /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible - - /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible - - /// Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates - void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected); - - /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. - bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Lua-accessible - - /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true - bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Lua-accessible - - /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true - bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback); - - /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true - bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback); - - /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true - bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback); - - /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true - bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Lua-accessible - - /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found - bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible - - /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found - bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Lua-accessible - - /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found - bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Lua-accessible - - /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found - bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Lua-accessible - - /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found - bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible - - /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found - bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible - - /// Touches the chunk, causing it to be loaded or generated - void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) - bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() - void LoadChunks(const cChunkCoordsList & a_Chunks); - - /// Marks the chunk as failed-to-load - void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Sets the sign text. Returns true if sign text changed. - bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); - - /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable; to be used only by cChunkStay! - void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); - - /// Marks the chunk as being regenerated - all its clients want that chunk again (used by cWorld::RegenerateChunk() ) - void MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ); - - bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); - - /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully - bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); - - /// Writes the block area into the specified coords. Returns true if all chunks have been processed. Prefer cBlockArea::Write() instead. - bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); - - /// Returns the number of valid chunks and the number of dirty chunks - void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty); - - /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) - void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand); - - /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config - void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - - /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config - void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - - /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call - void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player - void CollectMobCensus(cMobCensus& a_ToFill); - - /// Try to Spawn Monsters inside all Chunks - void SpawnMobs(cMobSpawner& a_MobSpawner); - - void Tick(float a_Dt); - - void UnloadUnusedChunks(void); - void SaveAllChunks(void); - - cWorld * GetWorld(void) { return m_World; } - - int GetNumChunks(void); - - void ChunkValidated(void); // Called by chunks that have become valid - - /// Queues the specified block for ticking (block update) - void QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Returns the CS for locking the chunkmap; only cWorld::cLock may use this function! - cCriticalSection & GetCS(void) { return m_CSLayers; } - -private: - - friend class cChunk; // The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock() - - class cChunkLayer - { - public: - cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent); - ~cChunkLayer(); - - /// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check - cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ); - - /// Returns the specified chunk, or NULL if not created yet - cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); - - int GetX(void) const {return m_LayerX; } - int GetZ(void) const {return m_LayerZ; } - - int GetNumChunksLoaded(void) const ; - - void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const; - - void Save(void); - void UnloadUnusedChunks(void); - - /// Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player - void CollectMobCensus(cMobCensus& a_ToFill); - /// Try to Spawn Monsters inside all Chunks - void SpawnMobs(cMobSpawner& a_MobSpawner); - - void Tick(float a_Dt); - - void RemoveClient(cClientHandle * a_Client); - - /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible - - /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. - bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackReturn); // Lua-accessible - - /// Returns true if there is an entity with the specified ID within this layer's chunks - bool HasEntity(int a_EntityID); - - protected: - - cChunkPtr m_Chunks[LAYER_SIZE * LAYER_SIZE]; - int m_LayerX; - int m_LayerZ; - cChunkMap * m_Parent; - int m_NumChunksLoaded; - }; - - typedef std::list cChunkLayerList; - - /// Finds the cChunkLayer object responsible for the specified chunk; returns NULL if not found. Assumes m_CSLayers is locked. - cChunkLayer * FindLayerForChunk(int a_ChunkX, int a_ChunkZ); - - /// Returns the specified cChunkLayer object; returns NULL if not found. Assumes m_CSLayers is locked. - cChunkLayer * FindLayer(int a_LayerX, int a_LayerZ); - - /// Returns the cChunkLayer object responsible for the specified chunk; creates it if not found. - cChunkLayer * GetLayerForChunk (int a_ChunkX, int a_ChunkZ); - - /// Returns the specified cChunkLayer object; creates it if not found. - cChunkLayer * GetLayer(int a_LayerX, int a_LayerZ); - - void RemoveLayer(cChunkLayer * a_Layer); - - cCriticalSection m_CSLayers; - cChunkLayerList m_Layers; - cEvent m_evtChunkValid; // Set whenever any chunk becomes valid, via ChunkValidated() - - cWorld * m_World; - - cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid - cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate - cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate - - /// Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) - bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - - /// Gets a block type in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) - bool LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType); - - /// Gets a block meta in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) - bool LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE & a_BlockMeta); - - /// Sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) - bool LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) - bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSLayers is locked. To be called only from cChunkMap. - cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); -}; - - - - - -/** Makes chunks stay loaded until this object is cleared or destroyed -Works by setting internal flags in the cChunk that it should not be unloaded. -To optimize for speed, cChunkStay has an Enabled flag, it will "stay" the chunks only when enabled and it will refuse manipulations when enabled -The object itself is not made thread-safe, it's supposed to be used from a single thread only. -*/ -class cChunkStay -{ -public: - cChunkStay(cWorld * a_World); - ~cChunkStay(); - - void Clear(void); - - void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - void Enable(void); - void Disable(void); - - /// Queues each chunk in m_Chunks[] for loading / generating - void Load(void); - - // Allow cChunkStay be passed to functions expecting a const cChunkCoordsList & - operator const cChunkCoordsList(void) const {return m_Chunks; } - -protected: - - cWorld * m_World; - - bool m_IsEnabled; - - cChunkCoordsList m_Chunks; -} ; - - - - diff --git a/source/ChunkSender.cpp b/source/ChunkSender.cpp deleted file mode 100644 index 005cfe29d..000000000 --- a/source/ChunkSender.cpp +++ /dev/null @@ -1,295 +0,0 @@ - -// ChunkSender.cpp - -// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients - - - - - -#include "Globals.h" -#include "ChunkSender.h" -#include "World.h" -#include "BlockEntities/BlockEntity.h" -#include "Protocol/ChunkDataSerializer.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cNotifyChunkSender: - -void cNotifyChunkSender::Call(int a_ChunkX, int a_ChunkZ) -{ - m_ChunkSender->ChunkReady(a_ChunkX, a_ChunkZ); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cChunkSender: - -cChunkSender::cChunkSender(void) : - super("ChunkSender"), - m_World(NULL), - m_Notify(NULL), - m_RemoveCount(0) -{ - m_Notify.SetChunkSender(this); -} - - - - - -cChunkSender::~cChunkSender() -{ - Stop(); -} - - - - - -bool cChunkSender::Start(cWorld * a_World) -{ - m_ShouldTerminate = false; - m_World = a_World; - return super::Start(); -} - - - - - -void cChunkSender::Stop(void) -{ - m_ShouldTerminate = true; - m_evtQueue.Set(); - Wait(); -} - - - - - -void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ) -{ - // This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check - { - cCSLock Lock(m_CS); - m_ChunksReady.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); - } - m_evtQueue.Set(); -} - - - - - -void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) -{ - ASSERT(a_Client != NULL); - { - cCSLock Lock(m_CS); - if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)) != m_SendChunks.end()) - { - // Already queued, bail out - return; - } - m_SendChunks.push_back(sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)); - } - m_evtQueue.Set(); -} - - - - - -void cChunkSender::RemoveClient(cClientHandle * a_Client) -{ - { - cCSLock Lock(m_CS); - for (sSendChunkList::iterator itr = m_SendChunks.begin(); itr != m_SendChunks.end();) - { - if (itr->m_Client == a_Client) - { - itr = m_SendChunks.erase(itr); - continue; - } - ++itr; - } // for itr - m_SendChunks[] - m_RemoveCount++; - } - m_evtQueue.Set(); - m_evtRemoved.Wait(); // Wait for removal confirmation -} - - - - - -void cChunkSender::Execute(void) -{ - while (!m_ShouldTerminate) - { - cCSLock Lock(m_CS); - while (m_ChunksReady.empty() && m_SendChunks.empty()) - { - int RemoveCount = m_RemoveCount; - m_RemoveCount = 0; - cCSUnlock Unlock(Lock); - for (int i = 0; i < RemoveCount; i++) - { - m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted - } - m_evtQueue.Wait(); - if (m_ShouldTerminate) - { - return; - } - } // while (empty) - - if (!m_ChunksReady.empty()) - { - // Take one from the queue: - cChunkCoords Coords(m_ChunksReady.front()); - m_ChunksReady.pop_front(); - Lock.Unlock(); - - SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL); - } - else - { - // Take one from the queue: - sSendChunk Chunk(m_SendChunks.front()); - m_SendChunks.pop_front(); - Lock.Unlock(); - - SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client); - } - Lock.Lock(); - int RemoveCount = m_RemoveCount; - m_RemoveCount = 0; - Lock.Unlock(); - for (int i = 0; i < RemoveCount; i++) - { - m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted - } - } // while (!mShouldTerminate) -} - - - - - -void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) -{ - ASSERT(m_World != NULL); - - // Ask the client if it still wants the chunk: - if (a_Client != NULL) - { - if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ)) - { - return; - } - } - - // If the chunk has no clients, no need to packetize it: - if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ)) - { - return; - } - - // If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating - if (!m_World->IsChunkValid(a_ChunkX, a_ChunkZ)) - { - return; - } - - // If the chunk is not lighted, queue it for relighting and get notified when it's ready: - if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ)) - { - m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, &m_Notify); - return; - } - - // Query and prepare chunk data: - if (!m_World->GetChunkData(a_ChunkX, a_ChunkZ, *this)) - { - return; - } - cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap); - - // Send: - if (a_Client == NULL) - { - m_World->BroadcastChunkData(a_ChunkX, a_ChunkZ, Data); - } - else - { - a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data); - } - - // Send block-entity packets: - for (sBlockCoords::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if (a_Client == NULL) - { - m_World->BroadcastBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ); - } - else - { - m_World->SendBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ, *a_Client); - } - } // for itr - m_Packets[] - m_BlockEntities.clear(); - - // TODO: Send entity spawn packets -} - - - - - -void cChunkSender::BlockEntity(cBlockEntity * a_Entity) -{ - m_BlockEntities.push_back(sBlockCoord(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ())); -} - - - - -void cChunkSender::Entity(cEntity * a_Entity) -{ - // Nothing needed yet, perhaps in the future when we save entities into chunks we'd like to send them upon load, too ;) -} - - - - - -void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) -{ - for (int i = 0; i < ARRAYCOUNT(m_BiomeMap); i++) - { - if ((*a_BiomeMap)[i] < 255) - { - // Normal MC biome, copy as-is: - m_BiomeMap[i] = (unsigned char)((*a_BiomeMap)[i]); - } - else - { - // TODO: MCS-specific biome, need to map to some basic MC biome: - ASSERT(!"Unimplemented MCS-specific biome"); - } - } // for i - m_BiomeMap[] -} - - - - diff --git a/source/ChunkSender.h b/source/ChunkSender.h deleted file mode 100644 index a26f764a7..000000000 --- a/source/ChunkSender.h +++ /dev/null @@ -1,169 +0,0 @@ - -// ChunkSender.h - -// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients - -/* -The whole thing is a thread that runs in a loop, waiting for either: - "finished chunks" (ChunkReady()), or - "chunks to send" (QueueSendChunkTo() ) -to come to a queue. -And once they do, it requests the chunk data and sends it all away, either - broadcasting (ChunkReady), or - sends to a specific client (QueueSendChunkTo) -Chunk data is queried using the cChunkDataCallback interface. -It is cached inside the ChunkSender object during the query and then processed after the query ends. -Note that the data needs to be compressed only *after* the query finishes, -because the query callbacks run with ChunkMap's CS locked. - -A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient(); -this ensures that the client's Send() won't be called anymore by ChunkSender. -Note that it may be called by world's BroadcastToChunk() if the client is still in the chunk. -*/ - - - -#pragma once - -#include "OSSupport/IsThread.h" -#include "ChunkDef.h" - - - - - -class cWorld; -class cClientHandle; - - - - - -// fwd: -class cChunkSender; - - - - - -/// Callback that can be used to notify chunk sender upon another chunkcoord notification -class cNotifyChunkSender : - public cChunkCoordCallback -{ - virtual void Call(int a_ChunkX, int a_ChunkZ) override; - - cChunkSender * m_ChunkSender; -public: - cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {} - - void SetChunkSender(cChunkSender * a_ChunkSender) - { - m_ChunkSender = a_ChunkSender; - } -} ; - - - - - -class cChunkSender: - public cIsThread, - public cChunkDataSeparateCollector -{ - typedef cIsThread super; -public: - cChunkSender(void); - ~cChunkSender(); - - bool Start(cWorld * a_World); - - void Stop(void); - - /// Notifies that a chunk has become ready and it should be sent to all its clients - void ChunkReady(int a_ChunkX, int a_ChunkZ); - - /// Queues a chunk to be sent to a specific client - void QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes the a_Client from all waiting chunk send operations - void RemoveClient(cClientHandle * a_Client); - -protected: - - /// Used for sending chunks to specific clients - struct sSendChunk - { - int m_ChunkX; - int m_ChunkY; - int m_ChunkZ; - cClientHandle * m_Client; - - sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) : - m_ChunkX(a_ChunkX), - m_ChunkY(a_ChunkY), - m_ChunkZ(a_ChunkZ), - m_Client(a_Client) - { - } - - bool operator ==(const sSendChunk & a_Other) - { - return ( - (a_Other.m_ChunkX == m_ChunkX) && - (a_Other.m_ChunkY == m_ChunkY) && - (a_Other.m_ChunkZ == m_ChunkZ) && - (a_Other.m_Client == m_Client) - ); - } - } ; - typedef std::list sSendChunkList; - - struct sBlockCoord - { - int m_BlockX; - int m_BlockY; - int m_BlockZ; - - sBlockCoord(int a_BlockX, int a_BlockY, int a_BlockZ) : - m_BlockX(a_BlockX), - m_BlockY(a_BlockY), - m_BlockZ(a_BlockZ) - { - } - } ; - - typedef std::vector sBlockCoords; - - cWorld * m_World; - - cCriticalSection m_CS; - cChunkCoordsList m_ChunksReady; - sSendChunkList m_SendChunks; - cEvent m_evtQueue; // Set when anything is added to m_ChunksReady - cEvent m_evtRemoved; // Set when removed clients are safe to be deleted - int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times) - - cNotifyChunkSender m_Notify; // Used for chunks that don't have a valid lighting - they will be re-queued after lightcalc - - // Data about the chunk that is being sent: - // NOTE that m_BlockData[] is inherited from the cChunkDataCollector - unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width]; - sBlockCoords m_BlockEntities; // Coords of the block entities to send - // TODO: sEntityIDs m_Entities; // Entity-IDs of the entities to send - - // cIsThread override: - virtual void Execute(void) override; - - // cChunkDataCollector overrides: - // (Note that they are called while the ChunkMap's CS is locked - don't do heavy calculations here!) - virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) override; - virtual void Entity (cEntity * a_Entity) override; - virtual void BlockEntity (cBlockEntity * a_Entity) override; - - /// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL - void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); -} ; - - - - diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp deleted file mode 100644 index f8fd4a8b7..000000000 --- a/source/ClientHandle.cpp +++ /dev/null @@ -1,2198 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "ClientHandle.h" -#include "Server.h" -#include "World.h" -#include "Entities/Pickup.h" -#include "PluginManager.h" -#include "Entities/Player.h" -#include "Inventory.h" -#include "BlockEntities/ChestEntity.h" -#include "BlockEntities/SignEntity.h" -#include "UI/Window.h" -#include "Item.h" -#include "Piston.h" -#include "Mobs/Monster.h" -#include "ChatColor.h" -#include "OSSupport/Socket.h" -#include "OSSupport/Timer.h" -#include "Items/ItemHandler.h" -#include "Blocks/BlockHandler.h" -#include "Blocks/BlockSlab.h" - -#include "Vector3f.h" -#include "Vector3d.h" - -#include "Root.h" - -#include "Authenticator.h" -#include "MersenneTwister.h" - -#include "Protocol/ProtocolRecognizer.h" - - - - - -#define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\ - case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\ - case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; } - - - - - -/// If the number of queued outgoing packets reaches this, the client will be kicked -#define MAX_OUTGOING_PACKETS 2000 - -/// How many explosions per single game tick are allowed -static const int MAX_EXPLOSIONS_PER_TICK = 100; - -/// How many explosions in the recent history are allowed -static const int MAX_RUNNING_SUM_EXPLOSIONS = cClientHandle::NUM_CHECK_EXPLOSIONS_TICKS * MAX_EXPLOSIONS_PER_TICK / 8; - -/// How many ticks before the socket is closed after the client is destroyed (#31) -static const int TICKS_BEFORE_CLOSE = 20; - - - - - -#define RECI_RAND_MAX (1.f/RAND_MAX) -inline int fRadRand(MTRand & r1, int a_BlockCoord) -{ - return a_BlockCoord * 32 + (int)(16 * ((float)r1.rand() * RECI_RAND_MAX) * 16 - 8); -} - - - - - -int cClientHandle::s_ClientCount = 0; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cClientHandle: - -cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) - : m_ViewDistance(a_ViewDistance) - , m_IPString(a_Socket->GetIPString()) - , m_OutgoingData(64 KiB) - , m_Player(NULL) - , m_HasSentDC(false) - , m_TimeSinceLastPacket(0) - , m_bKeepThreadGoing(true) - , m_Ping(1000) - , m_PingID(1) - , m_TicksSinceDestruction(0) - , m_State(csConnected) - , m_LastStreamedChunkX(0x7fffffff) // bogus chunk coords to force streaming upon login - , m_LastStreamedChunkZ(0x7fffffff) - , m_ShouldCheckDownloaded(false) - , m_UniqueID(0) - , m_BlockDigAnimStage(-1) - , m_HasStartedDigging(false) - , m_CurrentExplosionTick(0) - , m_RunningSumExplosions(0) - , m_HasSentPlayerChunk(false) -{ - m_Protocol = new cProtocolRecognizer(this); - - s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread - m_UniqueID = s_ClientCount; - - cTimer t1; - m_LastPingTime = t1.GetNowTime(); - - LOGD("New ClientHandle created at %p", this); -} - - - - - -cClientHandle::~cClientHandle() -{ - ASSERT(m_State >= csDestroyedWaiting); // Has Destroy() been called? - - LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), this); - - // Remove from cSocketThreads, we're not to be called anymore: - cRoot::Get()->GetServer()->ClientDestroying(this); - - { - cCSLock Lock(m_CSChunkLists); - m_LoadedChunks.clear(); - m_ChunksToSend.clear(); - } - - if (m_Player != NULL) - { - cWorld * World = m_Player->GetWorld(); - if (!m_Username.empty() && (World != NULL)) - { - // Send the Offline PlayerList packet: - World->BroadcastPlayerListItem(*m_Player, false, this); - } - if (World != NULL) - { - World->RemovePlayer(m_Player); - m_Player->Destroy(); - } - delete m_Player; - m_Player = NULL; - } - - if (!m_HasSentDC) - { - SendDisconnect("Server shut down? Kthnxbai"); - } - - // Queue all remaining outgoing packets to cSocketThreads: - { - cCSLock Lock(m_CSOutgoingData); - AString Data; - m_OutgoingData.ReadAll(Data); - m_OutgoingData.CommitRead(); - cRoot::Get()->GetServer()->WriteToClient(this, Data); - } - - // Queue the socket to close as soon as it sends all outgoing data: - cRoot::Get()->GetServer()->QueueClientClose(this); - cRoot::Get()->GetServer()->RemoveClient(this); - - delete m_Protocol; - m_Protocol = NULL; - - LOGD("ClientHandle at %p deleted", this); -} - - - - - -void cClientHandle::Destroy(void) -{ - { - cCSLock Lock(m_CSDestroyingState); - if (m_State >= csDestroying) - { - // Already called - return; - } - m_State = csDestroying; - } - - // DEBUG: - LOGD("%s: client %p, \"%s\"", __FUNCTION__, this, m_Username.c_str()); - - if ((m_Player != NULL) && (m_Player->GetWorld() != NULL)) - { - RemoveFromAllChunks(); - m_Player->GetWorld()->RemoveClientFromChunkSender(this); - } - m_State = csDestroyedWaiting; -} - - - - - -void cClientHandle::Kick(const AString & a_Reason) -{ - if (m_State >= csAuthenticating) // Don't log pings - { - LOG("Kicking user \"%s\" for \"%s\"", m_Username.c_str(), StripColorCodes(a_Reason).c_str()); - } - SendDisconnect(a_Reason); -} - - - - - -void cClientHandle::Authenticate(void) -{ - if (m_State != csAuthenticating) - { - return; - } - - ASSERT( m_Player == NULL ); - - // Spawn player (only serversided, so data is loaded) - m_Player = new cPlayer(this, GetUsername()); - - cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName()); - if (World == NULL) - { - World = cRoot::Get()->GetDefaultWorld(); - } - - if (m_Player->GetGameMode() == eGameMode_NotSet) - { - m_Player->LoginSetGameMode(World->GetGameMode()); - } - - m_Player->SetIP (m_IPString); - - cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player); - - m_ConfirmPosition = m_Player->GetPosition(); - - // Return a server login packet - m_Protocol->SendLogin(*m_Player, *World); - - // Send Weather if raining: - if ((World->GetWeather() == 1) || (World->GetWeather() == 2)) - { - m_Protocol->SendWeather(World->GetWeather()); - } - - // Send time - m_Protocol->SendTimeUpdate(World->GetWorldAge(), World->GetTimeOfDay()); - - // Send contents of the inventory window - m_Protocol->SendWholeInventory(*m_Player->GetWindow()); - - // Send health - m_Player->SendHealth(); - - // Send gamemode (1.6.1 movementSpeed): - SendGameMode(m_Player->GetGameMode()); - - m_Player->Initialize(World); - m_State = csAuthenticated; - - // Broadcast this player's spawning to all other players in the same chunk - m_Player->GetWorld()->BroadcastSpawnEntity(*m_Player, this); - - cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); -} - - - - - -void cClientHandle::StreamChunks(void) -{ - if ((m_State < csAuthenticated) || (m_State >= csDestroying)) - { - return; - } - - ASSERT(m_Player != NULL); - - int ChunkPosX = FAST_FLOOR_DIV((int)m_Player->GetPosX(), cChunkDef::Width); - int ChunkPosZ = FAST_FLOOR_DIV((int)m_Player->GetPosZ(), cChunkDef::Width); - if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ)) - { - // Already streamed for this position - return; - } - m_LastStreamedChunkX = ChunkPosX; - m_LastStreamedChunkZ = ChunkPosZ; - - LOGD("Streaming chunks centered on [%d, %d], view distance %d", ChunkPosX, ChunkPosZ, m_ViewDistance); - - cWorld * World = m_Player->GetWorld(); - ASSERT(World != NULL); - - // Remove all loaded chunks that are no longer in range; deferred to out-of-CS: - cChunkCoordsList RemoveChunks; - { - cCSLock Lock(m_CSChunkLists); - for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();) - { - int RelX = (*itr).m_ChunkX - ChunkPosX; - int RelZ = (*itr).m_ChunkZ - ChunkPosZ; - if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) - { - RemoveChunks.push_back(*itr); - itr = m_LoadedChunks.erase(itr); - } - else - { - ++itr; - } - } // for itr - m_LoadedChunks[] - for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();) - { - int RelX = (*itr).m_ChunkX - ChunkPosX; - int RelZ = (*itr).m_ChunkZ - ChunkPosZ; - if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) - { - itr = m_ChunksToSend.erase(itr); - } - else - { - ++itr; - } - } // for itr - m_ChunksToSend[] - } - for (cChunkCoordsList::iterator itr = RemoveChunks.begin(); itr != RemoveChunks.end(); ++itr) - { - World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this); - m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); - } // for itr - RemoveChunks[] - - // Add all chunks that are in range and not yet in m_LoadedChunks: - // Queue these smartly - from the center out to the edge - for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest - { - // For each distance add chunks in a hollow square centered around current position: - for (int i = -d; i <= d; ++i) - { - StreamChunk(ChunkPosX + d, ChunkPosZ + i); - StreamChunk(ChunkPosX - d, ChunkPosZ + i); - } // for i - for (int i = -d + 1; i < d; ++i) - { - StreamChunk(ChunkPosX + i, ChunkPosZ + d); - StreamChunk(ChunkPosX + i, ChunkPosZ - d); - } // for i - } // for d - - // Touch chunks GENERATEDISTANCE ahead to let them generate: - for (int d = m_ViewDistance + 1; d <= m_ViewDistance + GENERATEDISTANCE; ++d) // cycle through (square) distance, from nearest to furthest - { - // For each distance touch chunks in a hollow square centered around current position: - for (int i = -d; i <= d; ++i) - { - World->TouchChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i); - World->TouchChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i); - } // for i - for (int i = -d + 1; i < d; ++i) - { - World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d); - World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d); - } // for i - } // for d -} - - - - -void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ) -{ - if (m_State >= csDestroying) - { - // Don't stream chunks to clients that are being destroyed - return; - } - - cWorld * World = m_Player->GetWorld(); - ASSERT(World != NULL); - - if (World->AddChunkClient(a_ChunkX, a_ChunkZ, this)) - { - { - cCSLock Lock(m_CSChunkLists); - m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); - m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); - } - World->SendChunkTo(a_ChunkX, a_ChunkZ, this); - } -} - - - - - -// Removes the client from all chunks. Used when switching worlds or destroying the player -void cClientHandle::RemoveFromAllChunks() -{ - cWorld * World = m_Player->GetWorld(); - if (World != NULL) - { - World->RemoveClientFromChunks(this); - } - - { - cCSLock Lock(m_CSChunkLists); - m_LoadedChunks.clear(); - m_ChunksToSend.clear(); - - // Also reset the LastStreamedChunk coords to bogus coords, - // so that all chunks are streamed in subsequent StreamChunks() call (FS #407) - m_LastStreamedChunkX = 0x7fffffff; - m_LastStreamedChunkZ = 0x7fffffff; - } -} - - - - - -void cClientHandle::HandlePing(void) -{ - // Somebody tries to retrieve information about the server - AString Reply; - Printf(Reply, "%s%s%i%s%i", - cRoot::Get()->GetServer()->GetDescription().c_str(), - cChatColor::Delimiter.c_str(), - cRoot::Get()->GetServer()->GetNumPlayers(), - cChatColor::Delimiter.c_str(), - cRoot::Get()->GetServer()->GetMaxPlayers() - ); - Kick(Reply.c_str()); -} - - - - - -bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Username) -{ - LOGD("LOGIN %s", a_Username.c_str()); - m_Username = a_Username; - - if (cRoot::Get()->GetPluginManager()->CallHookLogin(this, a_ProtocolVersion, a_Username)) - { - Destroy(); - return false; - } - - // Schedule for authentication; until then, let them wait (but do not block) - m_State = csAuthenticating; - cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), m_Protocol->GetAuthServerID()); - return true; -} - - - - - -void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem) -{ - // This is for creative Inventory changes - if (!m_Player->IsGameModeCreative()) - { - LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str()); - return; - } - if (m_Player->GetWindow()->GetWindowType() != cWindow::wtInventory) - { - LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str()); - return; - } - - m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, (a_SlotNum >= 0) ? caLeftClick : caLeftClickOutside, a_HeldItem); -} - - - - - -void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround) -{ - if ((m_Player == NULL) || (m_State != csPlaying)) - { - // The client hasn't been spawned yet and sends nonsense, we know better - return; - } - - /* - // TODO: Invalid stance check - if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) - { - LOGD("Invalid stance"); - SendPlayerMoveLook(); - return; - } - */ - - // If the player has moved too far, "repair" them: - Vector3d Pos(a_PosX, a_PosY, a_PosZ); - if ((m_Player->GetPosition() - Pos).SqrLength() > 100 * 100) - { - LOGD("Too far away (%0.2f), \"repairing\" the client", (m_Player->GetPosition() - Pos).Length()); - SendPlayerMoveLook(); - return; - } - - // If a jump just started, process food exhaustion: - if ((a_PosY > m_Player->GetPosY()) && !a_IsOnGround && m_Player->IsOnGround()) - { - // we only add this exhaustion if the player is not swimming - otherwise we end up with both jump + swim exhaustion - - if (!m_Player->IsSwimming()) - { - m_Player->AddFoodExhaustion(m_Player->IsSprinting() ? 0.8 : 0.2); - } - } - - m_Player->MoveTo(Pos); - m_Player->SetStance(a_Stance); - m_Player->SetTouchGround(a_IsOnGround); -} - - - - - -void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) -{ - LOGD("HandleLeftClick: {%i, %i, %i}; Face: %i; Stat: %i", - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status - ); - - cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); - if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status)) - { - // A plugin doesn't agree with the action, replace the block on the client and quit: - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } - - if (!CheckBlockInteractionsRate()) - { - // Too many interactions per second, simply ignore. Probably a hacked client, so don't even send bak the block - return; - } - - switch (a_Status) - { - case DIG_STATUS_DROP_HELD: // Drop held item - { - if (PlgMgr->CallHookPlayerTossingItem(*m_Player)) - { - // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch) - return; - } - m_Player->TossItem(false); - return; - } - - case DIG_STATUS_SHOOT_EAT: - { - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); - if (ItemHandler->IsFood()) - { - m_Player->AbortEating(); - return; - } - else - { - if (PlgMgr->CallHookPlayerShooting(*m_Player)) - { - // A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch) - return; - } - ItemHandler->OnItemShoot(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - } - return; - } - - case DIG_STATUS_STARTED: - { - BLOCKTYPE OldBlock; - NIBBLETYPE OldMeta; - m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta); - HandleBlockDigStarted(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta); - return; - } - - case DIG_STATUS_FINISHED: - { - BLOCKTYPE OldBlock; - NIBBLETYPE OldMeta; - m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta); - HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta); - return; - } - - case DIG_STATUS_CANCELLED: - { - // Block breaking cancelled by player - return; - } - - default: - { - ASSERT(!"Unhandled DIG_STATUS"); - return; - } - } // switch (a_Status) -} - - - - - -void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) -{ - if ( - m_HasStartedDigging && - (a_BlockX == m_LastDigBlockX) && - (a_BlockY == m_LastDigBlockY) && - (a_BlockZ == m_LastDigBlockZ) - ) - { - // It is a duplicate packet, drop it right away - return; - } - - if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta)) - { - // A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows: - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } - - // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client: - m_HasStartedDigging = true; - m_LastDigBlockX = a_BlockX; - m_LastDigBlockY = a_BlockY; - m_LastDigBlockZ = a_BlockZ; - - if ( - (m_Player->IsGameModeCreative()) || // In creative mode, digging is done immediately - g_BlockOneHitDig[a_OldBlock] // One-hit blocks get destroyed immediately, too - ) - { - HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta); - return; - } - - // Start dig animation - // TODO: calculate real animation speed - // TODO: Send animation packets even without receiving any other packets - m_BlockDigAnimSpeed = 10; - m_BlockDigAnimX = a_BlockX; - m_BlockDigAnimY = a_BlockY; - m_BlockDigAnimZ = a_BlockZ; - m_BlockDigAnimStage = 0; - m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 0, this); - - cWorld * World = m_Player->GetWorld(); - - cBlockHandler * Handler = cBlockHandler::GetBlockHandler(a_OldBlock); - Handler->OnDigging(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); - - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); - ItemHandler->OnDiggingBlock(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - // Check for clickthrough-blocks: - if (a_BlockFace != BLOCK_FACE_NONE) - { - int pX = a_BlockX; - int pY = a_BlockY; - int pZ = a_BlockZ; - AddFaceDirection(pX, pY, pZ, a_BlockFace); - - Handler = cBlockHandler::GetBlockHandler(World->GetBlock(pX, pY, pZ)); - - // 2013_01_05 _X: This looks weird - // Why do we ask the block "behind" the one being clicked if it is clicked through? Shouldn't we ask the primary block instead? - if (Handler->IsClickedThrough()) - { - Handler->OnDigging(World, m_Player, pX, pY, pZ); - } - } -} - - - - - -void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) -{ - if ( - !m_HasStartedDigging || // Hasn't received the DIG_STARTED packet - (m_LastDigBlockX != a_BlockX) || // DIG_STARTED has had different pos - (m_LastDigBlockY != a_BlockY) || - (m_LastDigBlockZ != a_BlockZ) - ) - { - LOGD("Prevented a dig/aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)", - a_BlockX, a_BlockY, a_BlockZ, - m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ, - m_HasStartedDigging - ); - return; - } - - m_HasStartedDigging = false; - if (m_BlockDigAnimStage != -1) - { - // End dig animation - m_BlockDigAnimStage = -1; - // It seems that 10 ends block animation - m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 10, this); - } - - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); - - if (a_OldBlock == E_BLOCK_AIR) - { - LOGD("Dug air - what the function?"); - return; - } - - cWorld * World = m_Player->GetWorld(); - ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ); - // The ItemHandler is also responsible for spawning the pickups - - BlockHandler(a_OldBlock)->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); - World->BroadcastSoundParticleEffect(2001, a_BlockX, a_BlockY, a_BlockZ, a_OldBlock, this); - World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); - - cRoot::Get()->GetPluginManager()->CallHookPlayerBrokenBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta); -} - - - - - -void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem) -{ - LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s", - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str() - ); - - cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); - if (PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) - { - // A plugin doesn't agree with the action, replace the block on the client and quit: - if (a_BlockFace > -1) - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - } - return; - } - - if (!CheckBlockInteractionsRate()) - { - LOGD("Too many block interactions, aborting placement"); - return; - } - - const cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); - - if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1)) - { - // Only compare ItemType, not meta (torches have different metas) - // The -1 check is there because sometimes the client sends -1 instead of the held item - // ( http://forum.mc-server.org/showthread.php?tid=549&pid=4502#pid4502 ) - LOGWARN("Player %s tried to place a block that was not equipped (exp %d, got %d)", - m_Username.c_str(), Equipped.m_ItemType, a_HeldItem.m_ItemType - ); - - // Let's send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block - if (a_BlockFace > -1) - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - } - return; - } - - cWorld * World = m_Player->GetWorld(); - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - cBlockHandler * BlockHandler = cBlockHandler::GetBlockHandler(BlockType); - - if (BlockHandler->IsUseable() && !m_Player->IsCrouched()) - { - if (PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) - { - // A plugin doesn't agree with using the block, abort - return; - } - BlockHandler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); - PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); - return; - } - - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType); - - if (ItemHandler->IsPlaceable()) - { - HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); - } - else if (ItemHandler->IsFood()) - { - if (m_Player->IsSatiated()) - { - // The player is satiated, they cannot eat - return; - } - m_Player->StartEating(); - if (PlgMgr->CallHookPlayerEating(*m_Player)) - { - // A plugin won't let us eat, abort (send the proper packets to the client, too): - m_Player->AbortEating(); - return; - } - return; - } - else - { - if (PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) - { - // A plugin doesn't agree with using the item, abort - return; - } - ItemHandler->OnItemUse(World, m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); - } -} - - - - - -void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler) -{ - if (a_BlockFace < 0) - { - // Clicked in air - return; - } - - cWorld * World = m_Player->GetWorld(); - - BLOCKTYPE ClickedBlock; - NIBBLETYPE ClickedBlockMeta; - BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType); - NIBBLETYPE EquippedBlockDamage = (NIBBLETYPE)(m_Player->GetEquippedItem().m_ItemDamage); - - if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) - { - // The block is being placed outside the world, ignore this packet altogether (#128) - return; - } - - World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); - - // Special slab handling - placing a slab onto another slab produces a dblslab instead: - if ( - cBlockSlabHandler::IsAnySlabType(ClickedBlock) && // Is there a slab already? - cBlockSlabHandler::IsAnySlabType(EquippedBlock) && // Is the player placing another slab? - ((ClickedBlockMeta & 0x07) == (EquippedBlockDamage & 0x07)) && // Is it the same slab type? - ( - (a_BlockFace == BLOCK_FACE_TOP) || // Clicking the top of a bottom slab - (a_BlockFace == BLOCK_FACE_BOTTOM) // Clicking the bottom of a top slab - ) - ) - { - // Coordinates at CLICKED block, don't move them anywhere - } - else - { - // Check if the block ignores build collision (water, grass etc.): - cBlockHandler * Handler = cBlockHandler::GetBlockHandler(ClickedBlock); - if (Handler->DoesIgnoreBuildCollision()) - { - Handler->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); - } - - BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision()) - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) - { - // The block is being placed outside the world, ignore this packet altogether (#128) - return; - } - - BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - - // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. - // No need to do combinability (dblslab) checks, client will do that here. - if (cBlockSlabHandler::IsAnySlabType(PlaceBlock)) - { - // It's a slab, don't do checks and proceed to double-slabbing - } - else - { - if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision()) - { - // Tried to place a block *into* another? - // Happens when you place a block aiming at side of block like torch or stem - return; - } - } - } - } - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_ItemHandler.GetPlacementBlockTypeMeta(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) - { - // Handler refused the placement, send that information back to the client: - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockY, m_Player); - return; - } - - cBlockHandler * NewBlock = BlockHandler(BlockType); - - if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) - { - // A plugin doesn't agree with placing the block, revert the block on the client: - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } - - // The actual block placement: - World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - if (m_Player->GetGameMode() != gmCreative) - { - m_Player->GetInventory().RemoveOneEquippedItem(); - } - NewBlock->OnPlacedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); - - // Step sound with 0.8f pitch is used as block placement sound - World->BroadcastSoundEffect(NewBlock->GetStepSound(), a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.8f); - cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); -} - - - - - -void cClientHandle::HandleChat(const AString & a_Message) -{ - // We no longer need to postpone message processing, because the messages already arrive in the Tick thread - - // If a command, perform it: - AString Message(a_Message); - if (cRoot::Get()->GetServer()->Command(*this, Message)) - { - return; - } - - // Not a command, broadcast as a simple message: - AString Msg; - Printf(Msg, "<%s%s%s> %s", - m_Player->GetColor().c_str(), - m_Player->GetName().c_str(), - cChatColor::White.c_str(), - Message.c_str() - ); - m_Player->GetWorld()->BroadcastChat(Msg); -} - - - - - -void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround) -{ - if ((m_Player == NULL) || (m_State != csPlaying)) - { - return; - } - - m_Player->SetRotation (a_Rotation); - m_Player->SetHeadYaw (a_Rotation); - m_Player->SetPitch (a_Pitch); - m_Player->SetTouchGround(a_IsOnGround); -} - - - - - -void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround) -{ - if ((m_Player == NULL) || (m_State != csPlaying)) - { - // The client hasn't been spawned yet and sends nonsense, we know better - return; - } - - /* - // TODO: Invalid stance check - if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) - { - LOGD("Invalid stance"); - SendPlayerMoveLook(); - return; - } - */ - - m_Player->MoveTo(Vector3d(a_PosX, a_PosY, a_PosZ)); - m_Player->SetStance (a_Stance); - m_Player->SetTouchGround(a_IsOnGround); - m_Player->SetHeadYaw (a_Rotation); - m_Player->SetRotation (a_Rotation); - m_Player->SetPitch (a_Pitch); -} - - - - - -void cClientHandle::HandleAnimation(char a_Animation) -{ - if (cPluginManager::Get()->CallHookPlayerAnimation(*m_Player, a_Animation)) - { - // Plugin disagrees, bail out - return; - } - - m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, a_Animation, this); -} - - - - - -void cClientHandle::HandleSlotSelected(short a_SlotNum) -{ - m_Player->GetInventory().SetEquippedSlotNum(a_SlotNum); - m_Player->GetWorld()->BroadcastEntityEquipment(*m_Player, 0, m_Player->GetInventory().GetEquippedItem(), this); -} - - - - - -void cClientHandle::HandleSteerVehicle(float a_Forward, float a_Sideways) -{ - m_Player->SteerVehicle(a_Forward, a_Sideways); -} - - - - - -void cClientHandle::HandleWindowClose(char a_WindowID) -{ - m_Player->CloseWindowIfID(a_WindowID); -} - - - - - -void cClientHandle::HandleWindowClick(char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem) -{ - LOGD("WindowClick: WinID %d, SlotNum %d, action: %s, Item %s x %d", - a_WindowID, a_SlotNum, ClickActionToString(a_ClickAction), - ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount - ); - - cWindow * Window = m_Player->GetWindow(); - if (Window == NULL) - { - LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str()); - return; - } - - Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_ClickAction, a_HeldItem); -} - - - - - -void cClientHandle::HandleUpdateSign( - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, - const AString & a_Line3, const AString & a_Line4 -) -{ - cWorld * World = m_Player->GetWorld(); - World->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player); -} - - - - - -void cClientHandle::HandleUseEntity(int a_TargetEntityID, bool a_IsLeftClick) -{ - // TODO: Let plugins interfere via a hook - - // If it is a right click, call the entity's OnRightClicked() handler: - if (!a_IsLeftClick) - { - class cRclkEntity : public cEntityCallback - { - cPlayer & m_Player; - virtual bool Item(cEntity * a_Entity) override - { - if (cPluginManager::Get()->CallHookPlayerRightClickingEntity(m_Player, *a_Entity)) - { - return false; - } - a_Entity->OnRightClicked(m_Player); - return false; - } - public: - cRclkEntity(cPlayer & a_Player) : m_Player(a_Player) {} - } Callback (*m_Player); - - cWorld * World = m_Player->GetWorld(); - World->DoWithEntityByID(a_TargetEntityID, Callback); - return; - } - - // If it is a left click, attack the entity: - class cDamageEntity : public cEntityCallback - { - virtual bool Item(cEntity * a_Entity) override - { - if (!a_Entity->GetWorld()->IsPVPEnabled()) - { - // PVP is disabled, disallow players hurting other players: - if (a_Entity->IsPlayer()) - { - // Player is hurting another player which is not allowed when PVP is disabled so ignore it - return true; - } - } - a_Entity->TakeDamage(*m_Attacker); - return false; - } - public: - cPawn * m_Attacker; - } Callback; - - Callback.m_Attacker = m_Player; - - cWorld * World = m_Player->GetWorld(); - if (World->DoWithEntityByID(a_TargetEntityID, Callback)) - { - // Any kind of an attack implies food exhaustion - m_Player->AddFoodExhaustion(0.3); - } -} - - - - - -void cClientHandle::HandleRespawn(void) -{ - if (m_Player == NULL) - { - Destroy(); - return; - } - m_Player->Respawn(); - cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); -} - - - - - -void cClientHandle::HandleDisconnect(const AString & a_Reason) -{ - LOGD("Received d/c packet from \"%s\" with reason \"%s\"", m_Username.c_str(), a_Reason.c_str()); - if (!cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, a_Reason)) - { - AString DisconnectMessage; - Printf(DisconnectMessage, "%s disconnected: %s", m_Username.c_str(), a_Reason.c_str()); - m_Player->GetWorld()->BroadcastChat(DisconnectMessage, this); - } - m_HasSentDC = true; - Destroy(); -} - - - - - -void cClientHandle::HandleKeepAlive(int a_KeepAliveID) -{ - if (a_KeepAliveID == m_PingID) - { - cTimer t1; - m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2); - } -} - - - - - -bool cClientHandle::HandleHandshake(const AString & a_Username) -{ - if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(this, a_Username)) - { - if (cRoot::Get()->GetServer()->GetNumPlayers() >= cRoot::Get()->GetServer()->GetMaxPlayers()) - { - Kick("The server is currently full :(-- Try again later"); - return false; - } - } - return true; -} - - - - - -void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID) -{ - if (a_EntityID != m_Player->GetUniqueID()) - { - // We should only receive entity actions from the entity that is performing the action - return; - } - - switch (a_ActionID) - { - case 1: // crouch - { - m_Player->SetCrouch(true); - break; - } - case 2: // uncrouch - { - m_Player->SetCrouch(false); - break; - } - case 3: // Leave bed - { - m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, 3); - break; - } - case 4: // Start sprinting - { - m_Player->SetSprint(true); - break; - } - case 5: // Stop sprinting - { - m_Player->SetSprint(false); - SendPlayerMaxSpeed(); - break; - } - } -} - - - - - -void cClientHandle::HandleUnmount(void) -{ - if (m_Player == NULL) - { - return; - } - m_Player->Detach(); -} - - - - - -void cClientHandle::HandleTabCompletion(const AString & a_Text) -{ - AStringVector Results; - m_Player->GetWorld()->TabCompleteUserName(a_Text, Results); - cRoot::Get()->GetPluginManager()->TabCompleteCommand(a_Text, Results, m_Player); - if (Results.empty()) - { - return; - } - std::sort(Results.begin(), Results.end()); - SendTabCompletionResults(Results); -} - - - - - -void cClientHandle::SendData(const char * a_Data, int a_Size) -{ - if (m_HasSentDC) - { - // This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31) - return; - } - - { - cCSLock Lock(m_CSOutgoingData); - - // _X 2012_09_06: We need an overflow buffer, usually when streaming the initial chunks - if (m_OutgoingDataOverflow.empty()) - { - // No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer: - int CanFit = m_OutgoingData.GetFreeSpace(); - if (CanFit > a_Size) - { - CanFit = a_Size; - } - if (CanFit > 0) - { - m_OutgoingData.Write(a_Data, CanFit); - } - if (a_Size > CanFit) - { - m_OutgoingDataOverflow.append(a_Data + CanFit, a_Size - CanFit); - } - } - else - { - // There is a queued overflow. Append to it, then send as much from its front as possible - m_OutgoingDataOverflow.append(a_Data, a_Size); - int CanFit = m_OutgoingData.GetFreeSpace(); - if (CanFit > 128) - { - // No point in moving the data over if it's not large enough - too much effort for too little an effect - m_OutgoingData.Write(m_OutgoingDataOverflow.data(), CanFit); - m_OutgoingDataOverflow.erase(0, CanFit); - } - } - } // Lock(m_CSOutgoingData) - - // Notify SocketThreads that we have something to write: - cRoot::Get()->GetServer()->NotifyClientWrite(this); -} - - - - - -void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket) -{ - ASSERT(m_Player != NULL); - - if (a_SendRespawnPacket) - { - SendRespawn(); - } - - cWorld * World = m_Player->GetWorld(); - - // Remove all associated chunks: - cChunkCoordsList Chunks; - { - cCSLock Lock(m_CSChunkLists); - std::swap(Chunks, m_LoadedChunks); - m_ChunksToSend.clear(); - } - for (cChunkCoordsList::iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr) - { - World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this); - m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); - } // for itr - Chunks[] - - // Do NOT stream new chunks, the new world runs its own tick thread and may deadlock - // Instead, the chunks will be streamed when the client is moved to the new world's Tick list, - // by setting state to csAuthenticated - m_State = csAuthenticated; - m_LastStreamedChunkX = 0x7fffffff; - m_LastStreamedChunkZ = 0x7fffffff; - m_HasSentPlayerChunk = false; -} - - - - - -bool cClientHandle::CheckBlockInteractionsRate(void) -{ - ASSERT(m_Player != NULL); - ASSERT(m_Player->GetWorld() != NULL); - /* - // TODO: _X 2012_11_01: This needs a total re-thinking and rewriting - int LastActionCnt = m_Player->GetLastBlockActionCnt(); - if ((m_Player->GetWorld()->GetTime() - m_Player->GetLastBlockActionTime()) < 0.1) - { - // Limit the number of block interactions per tick - m_Player->SetLastBlockActionTime(); //Player tried to interact with a block. Reset last block interation time. - m_Player->SetLastBlockActionCnt(LastActionCnt + 1); - if (m_Player->GetLastBlockActionCnt() > MAXBLOCKCHANGEINTERACTIONS) - { - // Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick - LOGWARN("Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked.", m_Username.c_str()); - Kick("You're a baaaaaad boy!"); - return false; - } - } - else - { - m_Player->SetLastBlockActionCnt(0); // Reset count - m_Player->SetLastBlockActionTime(); // Player tried to interact with a block. Reset last block interation time. - } - */ - return true; -} - - - - - -void cClientHandle::Tick(float a_Dt) -{ - // Handle clients that are waiting for final close while destroyed: - if (m_State == csDestroyedWaiting) - { - m_TicksSinceDestruction += 1; // This field is misused for the timeout counting - if (m_TicksSinceDestruction > TICKS_BEFORE_CLOSE) - { - m_State = csDestroyed; - } - return; - } - - // Process received network data: - AString IncomingData; - { - cCSLock Lock(m_CSIncomingData); - std::swap(IncomingData, m_IncomingData); - } - m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); - - if (m_State == csAuthenticated) - { - StreamChunks(); - m_State = csDownloadingWorld; - } - - m_TimeSinceLastPacket += a_Dt; - if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out - { - SendDisconnect("Nooooo!! You timed out! D: Come back!"); - Destroy(); - } - - if (m_Player == NULL) - { - return; - } - - // If the chunk the player's in was just sent, spawn the player: - if (m_HasSentPlayerChunk && (m_State != csPlaying) && !IsDestroying()) - { - if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player)) - { - // Broadcast that this player has joined the game! Yay~ - m_Player->GetWorld()->BroadcastChat(m_Username + " joined the game!", this); - } - m_Protocol->SendPlayerMoveLook(); - m_State = csPlaying; - } - - // Send a ping packet: - cTimer t1; - if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime())) - { - m_PingID++; - m_PingStartTime = t1.GetNowTime(); - m_Protocol->SendKeepAlive(m_PingID); - m_LastPingTime = m_PingStartTime; - } - - // Handle block break animation: - if (m_BlockDigAnimStage > -1) - { - int lastAnimVal = m_BlockDigAnimStage; - m_BlockDigAnimStage += (int)(m_BlockDigAnimSpeed * a_Dt); - if (m_BlockDigAnimStage > 9000) - { - m_BlockDigAnimStage = 9000; - } - if (m_BlockDigAnimStage / 1000 != lastAnimVal / 1000) - { - m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, (char)(m_BlockDigAnimStage / 1000), this); - } - } - - // Update the explosion statistics: - m_CurrentExplosionTick = (m_CurrentExplosionTick + 1) % ARRAYCOUNT(m_NumExplosionsPerTick); - m_RunningSumExplosions -= m_NumExplosionsPerTick[m_CurrentExplosionTick]; - m_NumExplosionsPerTick[m_CurrentExplosionTick] = 0; -} - - - - - -void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - m_Protocol->SendAttachEntity(a_Entity, a_Vehicle); -} - - - - - -void cClientHandle::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); -} - - - - - -void cClientHandle::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) -{ - m_Protocol->SendBlockBreakAnim(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage); -} - - - - - -void cClientHandle::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); -} - - - - - -void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) -{ - ASSERT(!a_Changes.empty()); // We don't want to be sending empty change packets! - - m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); -} - - - - - -void cClientHandle::SendChat(const AString & a_Message) -{ - m_Protocol->SendChat(a_Message); -} - - - - - -void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - ASSERT(m_Player != NULL); - - // Check chunks being sent, erase them from m_ChunksToSend: - bool Found = false; - { - cCSLock Lock(m_CSChunkLists); - for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) - { - m_ChunksToSend.erase(itr); - Found = true; - break; - } - } // for itr - m_ChunksToSend[] - } - if (!Found) - { - // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it - // It's not a big issue anyway, just means that some chunks may be compressed several times - // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ()); - return; - } - - m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); - - // If it is the chunk the player's in, make them spawn (in the tick thread): - if ((m_State == csAuthenticated) || (m_State == csDownloadingWorld)) - { - if ((a_ChunkX == m_Player->GetChunkX()) && (a_ChunkZ == m_Player->GetChunkZ())) - { - m_HasSentPlayerChunk = true; - } - } -} - - - - - -void cClientHandle::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) -{ - m_Protocol->SendCollectPickup(a_Pickup, a_Player); -} - - - - - -void cClientHandle::SendDestroyEntity(const cEntity & a_Entity) -{ - m_Protocol->SendDestroyEntity(a_Entity); -} - - - - - -void cClientHandle::SendDisconnect(const AString & a_Reason) -{ - if (!m_HasSentDC) - { - LOGD("Sending a DC: \"%s\"", StripColorCodes(a_Reason).c_str()); - m_Protocol->SendDisconnect(a_Reason); - m_HasSentDC = true; - } -} - - - - - -void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); -} - - - - - -void cClientHandle::SendEntityHeadLook(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - - m_Protocol->SendEntityHeadLook(a_Entity); -} - - - - - -void cClientHandle::SendEntityLook(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - - m_Protocol->SendEntityLook(a_Entity); -} - - - - - -void cClientHandle::SendEntityMetadata(const cEntity & a_Entity) -{ - m_Protocol->SendEntityMetadata(a_Entity); -} - - - - - -void cClientHandle::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - - m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cClientHandle::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - - m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status) -{ - m_Protocol->SendEntityStatus(a_Entity, a_Status); -} - - - - - -void cClientHandle::SendEntityVelocity(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - - m_Protocol->SendEntityVelocity(a_Entity); -} - - - - - -void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) -{ - if ( - (m_NumExplosionsPerTick[m_CurrentExplosionTick] > MAX_EXPLOSIONS_PER_TICK) || // Too many explosions in this tick - (m_RunningSumExplosions > MAX_RUNNING_SUM_EXPLOSIONS) // Too many explosions in the recent history - ) - { - LOGD("Dropped %u explosions", a_BlocksAffected.size()); - return; - } - - // Update the statistics: - m_NumExplosionsPerTick[m_CurrentExplosionTick] += a_BlocksAffected.size(); - m_RunningSumExplosions += a_BlocksAffected.size(); - - m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion); -} - - - - - -void cClientHandle::SendGameMode(eGameMode a_GameMode) -{ - m_Protocol->SendGameMode(a_GameMode); -} - - - - - -void cClientHandle::SendHealth(void) -{ - m_Protocol->SendHealth(); -} - - - - - -void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) -{ - m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); -} - - - - - -void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup) -{ - m_Protocol->SendPickupSpawn(a_Pickup); -} - - - - - -void cClientHandle::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) -{ - m_Protocol->SendPlayerAnimation(a_Player, a_Animation); -} - - - - - -void cClientHandle::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) -{ - m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); -} - - - - - -void cClientHandle::SendPlayerMaxSpeed(void) -{ - m_Protocol->SendPlayerMaxSpeed(); -} - - - - - -void cClientHandle::SendPlayerMoveLook(void) -{ - /* - LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", - m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 - ); - */ - m_Protocol->SendPlayerMoveLook(); -} - - - - - -void cClientHandle::SendPlayerPosition(void) -{ - m_Protocol->SendPlayerPosition(); -} - - - - - -void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player) -{ - if (a_Player.GetUniqueID() == m_Player->GetUniqueID()) - { - // Do NOT send this packet to myself - return; - } - - LOGD("Spawning player \"%s\" on client \"%s\" @ %s", - a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str() - ); - - m_Protocol->SendPlayerSpawn(a_Player); -} - - - - - -void cClientHandle::SendRespawn(void) -{ - m_Protocol->SendRespawn(); -} - - - - - -void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) -{ - m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); -} - - - - - -void cClientHandle::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); -} - - - - - -void cClientHandle::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) -{ - m_Protocol->SendSpawnFallingBlock(a_FallingBlock); -} - - - - - -void cClientHandle::SendSpawnMob(const cMonster & a_Mob) -{ - m_Protocol->SendSpawnMob(a_Mob); -} - - - - - -void cClientHandle::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) -{ - m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch); -} - - - - - -void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) // VehicleSubType is specific to Minecarts -{ - m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType); -} - - - - - -void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results) -{ - m_Protocol->SendTabCompletionResults(a_Results); -} - - - - - -void cClientHandle::SendTeleportEntity(const cEntity & a_Entity) -{ - m_Protocol->SendTeleportEntity(a_Entity); -} - - - - - -void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cClientHandle::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) -{ - m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay); -} - - - - - -void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); -} - - - - - -void cClientHandle::SendUpdateSign( - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 -) -{ - m_Protocol->SendUpdateSign( - a_BlockX, a_BlockY, a_BlockZ, - a_Line1, a_Line2, a_Line3, a_Line4 - ); -} - - - - - -void cClientHandle::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) -{ - m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); -} - - - - -void cClientHandle::SendWeather(eWeather a_Weather) -{ - m_Protocol->SendWeather(a_Weather); -} - - - - - -void cClientHandle::SendWholeInventory(const cWindow & a_Window) -{ - m_Protocol->SendWholeInventory(a_Window); -} - - - - - -void cClientHandle::SendWindowClose(const cWindow & a_Window) -{ - m_Protocol->SendWindowClose(a_Window); -} - - - - - -void cClientHandle::SendWindowOpen(const cWindow & a_Window) -{ - m_Protocol->SendWindowOpen(a_Window); -} - - - - - -void cClientHandle::SendWindowProperty(const cWindow & a_Window, int a_Property, int a_Value) -{ - m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value); -} - - - - - -const AString & cClientHandle::GetUsername(void) const -{ - return m_Username; -} - - - - - -void cClientHandle::SetUsername( const AString & a_Username ) -{ - m_Username = a_Username; -} - - - - - -void cClientHandle::SetViewDistance(int a_ViewDistance) -{ - if (a_ViewDistance < MIN_VIEW_DISTANCE) - { - a_ViewDistance = MIN_VIEW_DISTANCE; - } - if (a_ViewDistance > MAX_VIEW_DISTANCE) - { - a_ViewDistance = MAX_VIEW_DISTANCE; - } - m_ViewDistance = a_ViewDistance; - - // Need to re-stream chunks for the change to become apparent: - StreamChunks(); -} - - - - - -bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - if (m_State >= csDestroying) - { - return false; - } - - cCSLock Lock(m_CSChunkLists); - return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)) != m_ChunksToSend.end()); -} - - - - - -void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ) -{ - if (m_State >= csDestroying) - { - return; - } - - LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this); - cCSLock Lock(m_CSChunkLists); - if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)) == m_ChunksToSend.end()) - { - m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); - } -} - - - - - -void cClientHandle::PacketBufferFull(void) -{ - // Too much data in the incoming queue, the server is probably too busy, kick the client: - LOGERROR("Too much data in queue for client \"%s\" @ %s, kicking them.", m_Username.c_str(), m_IPString.c_str()); - SendDisconnect("Server busy"); - Destroy(); -} - - - - - -void cClientHandle::PacketUnknown(unsigned char a_PacketType) -{ - LOGERROR("Unknown packet type 0x%02x from client \"%s\" @ %s", a_PacketType, m_Username.c_str(), m_IPString.c_str()); - - AString Reason; - Printf(Reason, "Unknown [C->S] PacketType: 0x%02x", a_PacketType); - SendDisconnect(Reason); - Destroy(); -} - - - - - -void cClientHandle::PacketError(unsigned char a_PacketType) -{ - LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str()); - SendDisconnect("Protocol error"); - Destroy(); -} - - - - - -void cClientHandle::DataReceived(const char * a_Data, int a_Size) -{ - // Data is received from the client, store it in the buffer to be processed by the Tick thread: - m_TimeSinceLastPacket = 0; - cCSLock Lock(m_CSIncomingData); - m_IncomingData.append(a_Data, a_Size); -} - - - - - -void cClientHandle::GetOutgoingData(AString & a_Data) -{ - // Data can be sent to client - { - cCSLock Lock(m_CSOutgoingData); - m_OutgoingData.ReadAll(a_Data); - m_OutgoingData.CommitRead(); - a_Data.append(m_OutgoingDataOverflow); - m_OutgoingDataOverflow.clear(); - } - - // Disconnect player after all packets have been sent - if (m_HasSentDC && a_Data.empty()) - { - Destroy(); - } -} - - - - - -void cClientHandle::SocketClosed(void) -{ - // The socket has been closed for any reason - - LOGD("Client \"%s\" @ %s disconnected", m_Username.c_str(), m_IPString.c_str()); - Destroy(); -} - - - - - - diff --git a/source/ClientHandle.h b/source/ClientHandle.h deleted file mode 100644 index 3844937ad..000000000 --- a/source/ClientHandle.h +++ /dev/null @@ -1,331 +0,0 @@ - -// cClientHandle.h - -// Interfaces to the cClientHandle class representing a client connected to this server. The client need not be a player yet - - - - - -#pragma once -#ifndef CCLIENTHANDLE_H_INCLUDED -#define CCLIENTHANDLE_H_INCLUDED - -#include "Defines.h" -#include "Vector3d.h" -#include "OSSupport/SocketThreads.h" -#include "ChunkDef.h" -#include "ByteBuffer.h" - - - - - -class cChunkDataSerializer; -class cInventory; -class cMonster; -class cPawn; -class cPickup; -class cPlayer; -class cProtocol; -class cRedstone; -class cWindow; -class cFallingBlock; -class cItemHandler; -class cWorld; - - - - - -class cClientHandle : // tolua_export - public cSocketThreads::cCallback -{ // tolua_export -public: - enum ENUM_PRIORITY - { - E_PRIORITY_LOW, - E_PRIORITY_NORMAL - }; - - static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick - -#if defined(ANDROID_NDK) - static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini) -#else - static const int DEFAULT_VIEW_DISTANCE = 10; -#endif - static const int MAX_VIEW_DISTANCE = 15; - static const int MIN_VIEW_DISTANCE = 3; - - /// How many ticks should be checked for a running average of explosions, for limiting purposes - static const int NUM_CHECK_EXPLOSIONS_TICKS = 20; - - cClientHandle(const cSocket * a_Socket, int a_ViewDistance); - virtual ~cClientHandle(); - - const AString & GetIPString(void) const { return m_IPString; } - - cPlayer* GetPlayer() { return m_Player; } // tolua_export - - void Kick(const AString & a_Reason); // tolua_export - void Authenticate(void); // Called by cAuthenticator when the user passes authentication - - void StreamChunks(void); - - // Removes the client from all chunks. Used when switching worlds or destroying the player - void RemoveFromAllChunks(void); - - inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); } - - void Tick(float a_Dt); - - void Destroy(void); - - bool IsPlaying (void) const { return (m_State == csPlaying); } - bool IsDestroyed (void) const { return (m_State == csDestroyed); } - bool IsDestroying(void) const { return (m_State == csDestroying); } - - // The following functions send the various packets: - // (Please keep these alpha-sorted) - void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle); - void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType); - void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage); - void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export - void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes); - void SendChat (const AString & a_Message); - void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); - void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player); - void SendDestroyEntity (const cEntity & a_Entity); - void SendDisconnect (const AString & a_Reason); - void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ); - void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item); - void SendEntityHeadLook (const cEntity & a_Entity); - void SendEntityLook (const cEntity & a_Entity); - void SendEntityMetadata (const cEntity & a_Entity); - void SendEntityProperties (const cEntity & a_Entity); - void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); - void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); - void SendEntityStatus (const cEntity & a_Entity, char a_Status); - void SendEntityVelocity (const cEntity & a_Entity); - void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion); - void SendGameMode (eGameMode a_GameMode); - void SendHealth (void); - void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item); - void SendPickupSpawn (const cPickup & a_Pickup); - void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation); - void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline); - void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+) - void SendPlayerMoveLook (void); - void SendPlayerPosition (void); - void SendPlayerSpawn (const cPlayer & a_Player); - void SendRespawn (void); - void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8 - void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data); - void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock); - void SendSpawnMob (const cMonster & a_Mob); - void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch); - void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType = 0); - void SendTabCompletionResults(const AStringVector & a_Results); - void SendTeleportEntity (const cEntity & a_Entity); - void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); - void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay); - void SendUnloadChunk (int a_ChunkX, int a_ChunkZ); - void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); - void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); - void SendWeather (eWeather a_Weather); - void SendWholeInventory (const cWindow & a_Window); - void SendWindowClose (const cWindow & a_Window); - void SendWindowOpen (const cWindow & a_Window); - void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value); - - const AString & GetUsername(void) const; // tolua_export - void SetUsername( const AString & a_Username ); // tolua_export - - inline short GetPing(void) const { return m_Ping; } // tolua_export - - void SetViewDistance(int a_ViewDistance); // tolua_export - int GetViewDistance(void) const { return m_ViewDistance; } // tolua_export - - int GetUniqueID() const { return m_UniqueID; } // tolua_export - - /// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) - bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) - void AddWantedChunk(int a_ChunkX, int a_ChunkZ); - - // Calls that cProtocol descendants use to report state: - void PacketBufferFull(void); - void PacketUnknown(unsigned char a_PacketType); - void PacketError(unsigned char a_PacketType); - - // Calls that cProtocol descendants use for handling packets: - void HandleAnimation (char a_Animation); - void HandleChat (const AString & a_Message); - void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem); - void HandleDisconnect (const AString & a_Reason); - void HandleEntityAction (int a_EntityID, char a_ActionID); - bool HandleHandshake (const AString & a_Username); - void HandleKeepAlive (int a_KeepAliveID); - void HandleLeftClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); - void HandlePing (void); - void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround); - void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay) - void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround); - void HandleRespawn (void); - void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem); - void HandleSlotSelected (short a_SlotNum); - void HandleSteerVehicle (float Forward, float Sideways); - void HandleTabCompletion (const AString & a_Text); - void HandleUpdateSign ( - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, - const AString & a_Line3, const AString & a_Line4 - ); - void HandleUnmount (void); - void HandleUseEntity (int a_TargetEntityID, bool a_IsLeftClick); - void HandleWindowClick (char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem); - void HandleWindowClose (char a_WindowID); - - /** Called when the protocol has finished logging the user in. - Return true to allow the user in; false to kick them. - */ - bool HandleLogin(int a_ProtocolVersion, const AString & a_Username); - - void SendData(const char * a_Data, int a_Size); - - /// Called when the player moves into a different world; queues sreaming the new chunks - void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket); - - /// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) - void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler); - -private: - - int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 ) - - static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation - - AString m_IPString; - - int m_ProtocolVersion; - AString m_Username; - AString m_Password; - - cCriticalSection m_CSChunkLists; - cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to - cChunkCoordsList m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them) - - cProtocol * m_Protocol; - - cCriticalSection m_CSIncomingData; - AString m_IncomingData; - - cCriticalSection m_CSOutgoingData; - cByteBuffer m_OutgoingData; - AString m_OutgoingDataOverflow; ///< For data that didn't fit into the m_OutgoingData ringbuffer temporarily - - Vector3d m_ConfirmPosition; - - cPlayer * m_Player; - - bool m_HasSentDC; ///< True if a D/C packet has been sent in either direction - - // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk - int m_LastStreamedChunkX; - int m_LastStreamedChunkZ; - - /// Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) - float m_TimeSinceLastPacket; - - short m_Ping; - int m_PingID; - long long m_PingStartTime; - long long m_LastPingTime; - static const unsigned short PING_TIME_MS = 1000; //minecraft sends 1 per 20 ticks (1 second or every 1000 ms) - - // Values required for block dig animation - int m_BlockDigAnimStage; // Current stage of the animation; -1 if not digging - int m_BlockDigAnimSpeed; // Current speed of the animation (units ???) - int m_BlockDigAnimX; - int m_BlockDigAnimY; - int m_BlockDigAnimZ; - - // To avoid dig/aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet: - bool m_HasStartedDigging; - int m_LastDigBlockX; - int m_LastDigBlockY; - int m_LastDigBlockZ; - - /// Used while csDestroyedWaiting for counting the ticks until the connection is closed - int m_TicksSinceDestruction; - - enum eState - { - csConnected, ///< The client has just connected, waiting for their handshake / login - csAuthenticating, ///< The client has logged in, waiting for external authentication - csAuthenticated, ///< The client has been authenticated, will start streaming chunks in the next tick - csDownloadingWorld, ///< The client is waiting for chunks, we're waiting for the loader to provide and send them - csConfirmingPos, ///< The client has been sent the position packet, waiting for them to repeat the position back - csPlaying, ///< Normal gameplay - csDestroying, ///< The client is being destroyed, don't queue any more packets / don't add to chunks - csDestroyedWaiting, ///< The client has been destroyed, but is still kept so that the Kick packet is delivered (#31) - csDestroyed, ///< The client has been destroyed, the destructor is to be called from the owner thread - - // TODO: Add Kicking here as well - } ; - - eState m_State; - - /// m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads - cCriticalSection m_CSDestroyingState; - - bool m_bKeepThreadGoing; - - /// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() - bool m_ShouldCheckDownloaded; - - /// Stores the recent history of the number of explosions per tick - int m_NumExplosionsPerTick[NUM_CHECK_EXPLOSIONS_TICKS]; - - /// Points to the current tick in the m_NumExplosionsPerTick[] array - int m_CurrentExplosionTick; - - /// Running sum of m_NumExplosionsPerTick[] - int m_RunningSumExplosions; - - static int s_ClientCount; - int m_UniqueID; - - /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player - bool m_HasSentPlayerChunk; - - - - /// Returns true if the rate block interactions is within a reasonable limit (bot protection) - bool CheckBlockInteractionsRate(void); - - /// Adds a single chunk to be streamed to the client; used by StreamChunks() - void StreamChunk(int a_ChunkX, int a_ChunkZ); - - /// Handles the DIG_STARTED dig packet: - void HandleBlockDigStarted (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); - - /// Handles the DIG_FINISHED dig packet: - void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); - - // cSocketThreads::cCallback overrides: - virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client - virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client - virtual void SocketClosed (void) override; // The socket has been closed for any reason -}; // tolua_export - - - - -#endif // CCLIENTHANDLE_H_INCLUDED - - - - diff --git a/source/CommandOutput.cpp b/source/CommandOutput.cpp deleted file mode 100644 index c221682a1..000000000 --- a/source/CommandOutput.cpp +++ /dev/null @@ -1,71 +0,0 @@ - -// CommandOutput.cpp - -// Implements the various classes that process command output - -#include "Globals.h" -#include "CommandOutput.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCommandOutputCallback: - -void cCommandOutputCallback::Out(const char * a_Fmt, ...) -{ - AString Output; - va_list args; - va_start(args, a_Fmt); - AppendVPrintf(Output, a_Fmt, args); - va_end(args); - Output.append("\n"); - Out(Output); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cLogCommandOutputCallback: - -void cLogCommandOutputCallback::Out(const AString & a_Text) -{ - m_Buffer.append(a_Text); -} - - - - - -void cLogCommandOutputCallback::Finished(void) -{ - // Log each line separately: - size_t len = m_Buffer.length(); - size_t last = 0; - for (size_t i = 0; i < len; i++) - { - switch (m_Buffer[i]) - { - case '\n': - { - LOG(m_Buffer.substr(last, i - last).c_str()); - last = i + 1; - break; - } - } - } // for i - m_Buffer[] - if (last < len) - { - LOG(m_Buffer.substr(last).c_str()); - } - - // Clear the buffer for the next command output: - m_Buffer.clear(); -} - - - - diff --git a/source/CommandOutput.h b/source/CommandOutput.h deleted file mode 100644 index bdf675238..000000000 --- a/source/CommandOutput.h +++ /dev/null @@ -1,82 +0,0 @@ - -// CommandOutput.h - -// Declares various classes that process command output - - - - - -/** Interface for a callback that receives command output -The Out() function is called for any output the command has produced. -Descendants override that function to provide specific processing of the output. -*/ -class cCommandOutputCallback -{ -public: - virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses - - /// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n" - void Out(const char * a_Fmt, ...); - - /// Called when the command wants to output anything; may be called multiple times - virtual void Out(const AString & a_Text) = 0; - - /// Called when the command processing has been finished - virtual void Finished(void) {}; -} ; - - - - - -/// Class that discards all command output -class cNullCommandOutputCallback : - public cCommandOutputCallback -{ - // cCommandOutputCallback overrides: - virtual void Out(const AString & a_Text) override - { - // Do nothing - } -} ; - - - - - - -/// Sends all command output to a log, line by line, when the command finishes processing -class cLogCommandOutputCallback : - public cCommandOutputCallback -{ -public: - // cCommandOutputCallback overrides: - virtual void Out(const AString & a_Text) override; - virtual void Finished(void) override; - -protected: - /// Output is stored here until the command finishes processing - AString m_Buffer; -} ; - - - - - -/// Sends all command output to a log, line by line; deletes self when command finishes processing -class cLogCommandDeleteSelfOutputCallback : - public cLogCommandOutputCallback -{ - typedef cLogCommandOutputCallback super; - - virtual void Finished(void) override - { - super::Finished(); - delete this; - } -} ; - - - - diff --git a/source/CraftingRecipes.cpp b/source/CraftingRecipes.cpp deleted file mode 100644 index 9dc471781..000000000 --- a/source/CraftingRecipes.cpp +++ /dev/null @@ -1,770 +0,0 @@ - -// CraftingRecipes.cpp - -// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes - -#include "Globals.h" -#include "CraftingRecipes.h" -#include "Root.h" -#include "PluginManager.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCraftingGrid: - -cCraftingGrid::cCraftingGrid(int a_Width, int a_Height) : - m_Width(a_Width), - m_Height(a_Height), - m_Items(new cItem[a_Width * a_Height]) -{ -} - - - - - -cCraftingGrid::cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height) : - m_Width(a_Width), - m_Height(a_Height), - m_Items(new cItem[a_Width * a_Height]) -{ - for (int i = a_Width * a_Height - 1; i >= 0; i--) - { - m_Items[i] = a_Items[i]; - } -} - - - - - -cCraftingGrid::cCraftingGrid(const cCraftingGrid & a_Original) : - m_Width(a_Original.m_Width), - m_Height(a_Original.m_Height), - m_Items(new cItem[a_Original.m_Width * a_Original.m_Height]) -{ - for (int i = m_Width * m_Height - 1; i >= 0; i--) - { - m_Items[i] = a_Original.m_Items[i]; - } -} - - - - - -cCraftingGrid::~cCraftingGrid() -{ - delete[] m_Items; -} - - - - - -cItem & cCraftingGrid::GetItem(int x, int y) const -{ - // Accessible through scripting, must verify parameters: - if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) - { - LOGERROR("Attempted to get an invalid item from a crafting grid: (%d, %d), grid dimensions: (%d, %d).", - x, y, m_Width, m_Height - ); - return m_Items[0]; - } - return m_Items[x + m_Width * y]; -} - - - - - -void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) -{ - // Accessible through scripting, must verify parameters: - if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) - { - LOGERROR("Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).", - x, y, m_Width, m_Height - ); - return; - } - - m_Items[x + m_Width * y] = cItem(a_ItemType, a_ItemCount, a_ItemHealth); -} - - - - - -void cCraftingGrid::SetItem(int x, int y, const cItem & a_Item) -{ - // Accessible through scripting, must verify parameters: - if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) - { - LOGERROR("Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).", - x, y, m_Width, m_Height - ); - return; - } - - m_Items[x + m_Width * y] = a_Item; -} - - - - - -void cCraftingGrid::Clear(void) -{ - for (int y = 0; y < m_Height; y++) for (int x = 0; x < m_Width; x++) - { - m_Items[x + m_Width * y].Empty(); - } -} - - - - - -void cCraftingGrid::ConsumeGrid(const cCraftingGrid & a_Grid) -{ - if ((a_Grid.m_Width != m_Width) || (a_Grid.m_Height != m_Height)) - { - LOGWARNING("Consuming a grid of different dimensions: (%d, %d) vs (%d, %d)", - a_Grid.m_Width, a_Grid.m_Height, m_Width, m_Height - ); - } - int MinX = std::min(a_Grid.m_Width, m_Width); - int MinY = std::min(a_Grid.m_Height, m_Height); - for (int y = 0; y < MinY; y++) for (int x = 0; x < MinX; x++) - { - int ThatIdx = x + a_Grid.m_Width * y; - if (a_Grid.m_Items[ThatIdx].IsEmpty()) - { - continue; - } - int ThisIdx = x + m_Width * y; - if (a_Grid.m_Items[ThatIdx].m_ItemType != m_Items[ThisIdx].m_ItemType) - { - LOGWARNING("Consuming incompatible grids: item at (%d, %d) is %d in grid and %d in ingredients. Item not consumed.", - x, y, m_Items[ThisIdx].m_ItemType, a_Grid.m_Items[ThatIdx].m_ItemType - ); - continue; - } - char NumWantedItems = a_Grid.m_Items[ThatIdx].m_ItemCount; - if (NumWantedItems > m_Items[ThisIdx].m_ItemCount) - { - LOGWARNING("Consuming more items than there actually are in slot (%d, %d), item %d (want %d, have %d). Item zeroed out.", - x, y, m_Items[ThisIdx].m_ItemType, - NumWantedItems, m_Items[ThisIdx].m_ItemCount - ); - NumWantedItems = m_Items[ThisIdx].m_ItemCount; - } - m_Items[ThisIdx].m_ItemCount -= NumWantedItems; - if (m_Items[ThisIdx].m_ItemCount == 0) - { - m_Items[ThisIdx].Clear(); - } - } // for x, for y -} - - - - - -void cCraftingGrid::CopyToItems(cItem * a_Items) const -{ - for (int i = m_Height * m_Width - 1; i >= 0; i--) - { - a_Items[i] = m_Items[i]; - } // for x, for y -} - - - - - -void cCraftingGrid::Dump(void) -{ - for (int y = 0; y < m_Height; y++) for (int x = 0; x < m_Width; x++) - { - int idx = x + m_Width * y; - LOGD("Slot (%d, %d): Type %d, health %d, count %d", - x, y, m_Items[idx].m_ItemType, m_Items[idx].m_ItemDamage, m_Items[idx].m_ItemCount - ); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCraftingRecipe: - -cCraftingRecipe::cCraftingRecipe(const cCraftingGrid & a_CraftingGrid) : - m_Ingredients(a_CraftingGrid) -{ -} - - - - - -void cCraftingRecipe::Clear(void) -{ - m_Ingredients.Clear(); - m_Result.Clear(); -} - - - - - -void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) -{ - m_Result = cItem(a_ItemType, a_ItemCount, a_ItemHealth); -} - - - - - -void cCraftingRecipe::ConsumeIngredients(cCraftingGrid & a_CraftingGrid) -{ - a_CraftingGrid.ConsumeGrid(m_Ingredients); -} - - - - - -void cCraftingRecipe::Dump(void) -{ - LOGD("Recipe ingredients:"); - m_Ingredients.Dump(); - LOGD("Result: Type %d, health %d, count %d", - m_Result.m_ItemType, m_Result.m_ItemDamage, m_Result.m_ItemCount - ); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCraftingRecipes: - -cCraftingRecipes::cCraftingRecipes(void) -{ - LoadRecipes(); -} - - - - - -cCraftingRecipes::~cCraftingRecipes() -{ - ClearRecipes(); -} - - - - - -void cCraftingRecipes::GetRecipe(const cPlayer * a_Player, const cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe) -{ - // Allow plugins to intercept recipes using a pre-craft hook: - if (cRoot::Get()->GetPluginManager()->CallHookPreCrafting(a_Player, &a_CraftingGrid, &a_Recipe)) - { - return; - } - - // Built-in recipes: - std::auto_ptr Recipe(FindRecipe(a_CraftingGrid.GetItems(), a_CraftingGrid.GetWidth(), a_CraftingGrid.GetHeight())); - a_Recipe.Clear(); - if (Recipe.get() == NULL) - { - // Allow plugins to intercept a no-recipe-found situation: - cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, &a_CraftingGrid, &a_Recipe); - return; - } - for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr) - { - a_Recipe.SetIngredient(itr->x, itr->y, itr->m_Item); - } // for itr - a_Recipe.SetResult(Recipe->m_Result); - - // Allow plugins to intercept recipes after they are processed: - cRoot::Get()->GetPluginManager()->CallHookPostCrafting(a_Player, &a_CraftingGrid, &a_Recipe); -} - - - - - -void cCraftingRecipes::LoadRecipes(void) -{ - LOGD("Loading crafting recipes from crafting.txt..."); - ClearRecipes(); - - // Load the crafting.txt file: - cFile f; - if (!f.Open("crafting.txt", cFile::fmRead)) - { - LOGWARNING("Cannot open file \"crafting.txt\", no crafting recipes will be available!"); - return; - } - AString Everything; - f.ReadRestOfFile(Everything); - f.Close(); - - // Split it into lines, then process each line as a single recipe: - AStringVector Split = StringSplit(Everything, "\n"); - int LineNum = 1; - for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr, ++LineNum) - { - // Remove anything after a '#' sign and trim away the whitespace: - AString Recipe = TrimString(itr->substr(0, itr->find('#'))); - if (Recipe.empty()) - { - // Empty recipe - continue; - } - AddRecipeLine(LineNum, Recipe); - } // for itr - Split[] - LOG("Loaded %d crafting recipes", m_Recipes.size()); -} - - - - -void cCraftingRecipes::ClearRecipes(void) -{ - for (cRecipes::iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr) - { - delete *itr; - } - m_Recipes.clear(); -} - - - - - -void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine) -{ - AStringVector Sides = StringSplit(a_RecipeLine, "="); - if (Sides.size() != 2) - { - LOGWARNING("crafting.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1); - LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); - return; - } - - std::auto_ptr Recipe(new cCraftingRecipes::cRecipe); - - // Parse the result: - AStringVector ResultSplit = StringSplit(Sides[0], ","); - if (ResultSplit.empty()) - { - LOGWARNING("crafting.txt: line %d: Result is empty, ignoring the recipe.", a_LineNum); - LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); - return; - } - if (!ParseItem(ResultSplit[0], Recipe->m_Result)) - { - LOGWARNING("crafting.txt: line %d: Cannot parse result item, ignoring the recipe.", a_LineNum); - LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); - return; - } - if (ResultSplit.size() > 1) - { - Recipe->m_Result.m_ItemCount = atoi(ResultSplit[1].c_str()); - if (Recipe->m_Result.m_ItemCount == 0) - { - LOGWARNING("crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum); - LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); - return; - } - } - else - { - Recipe->m_Result.m_ItemCount = 1; - } - - // Parse each ingredient: - AStringVector Ingredients = StringSplit(Sides[1], "|"); - int Num = 1; - for (AStringVector::const_iterator itr = Ingredients.begin(); itr != Ingredients.end(); ++itr, ++Num) - { - if (!ParseIngredient(*itr, Recipe.get())) - { - LOGWARNING("crafting.txt: line %d: Cannot parse ingredient #%d, ignoring the recipe.", a_LineNum, Num); - LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); - return; - } - } // for itr - Ingredients[] - - NormalizeIngredients(Recipe.get()); - - m_Recipes.push_back(Recipe.release()); -} - - - - - -bool cCraftingRecipes::ParseItem(const AString & a_String, cItem & a_Item) -{ - // The caller provides error logging - - AStringVector Split = StringSplit(a_String, "^"); - if (Split.empty()) - { - return false; - } - - if (!StringToItem(Split[0], a_Item)) - { - return false; - } - - if (Split.size() > 1) - { - AString Damage = TrimString(Split[1]); - a_Item.m_ItemDamage = atoi(Damage.c_str()); - if ((a_Item.m_ItemDamage == 0) && (Damage.compare("0") != 0)) - { - // Parsing the number failed - return false; - } - } - - // Success - return true; -} - - - - - -bool cCraftingRecipes::ParseIngredient(const AString & a_String, cRecipe * a_Recipe) -{ - // a_String is in this format: "ItemType^damage, X:Y, X:Y, X:Y..." - AStringVector Split = StringSplit(a_String, ","); - if (Split.size() < 2) - { - // Not enough split items - return false; - } - cItem Item; - if (!ParseItem(Split[0], Item)) - { - return false; - } - Item.m_ItemCount = 1; - - cCraftingRecipes::cRecipeSlots TempSlots; - for (AStringVector::const_iterator itr = Split.begin() + 1; itr != Split.end(); ++itr) - { - // Parse the coords in the split item: - AStringVector Coords = StringSplit(*itr, ":"); - if ((Coords.size() == 1) && (TrimString(Coords[0]) == "*")) - { - cCraftingRecipes::cRecipeSlot Slot; - Slot.m_Item = Item; - Slot.x = -1; - Slot.y = -1; - TempSlots.push_back(Slot); - continue; - } - if (Coords.size() != 2) - { - return false; - } - Coords[0] = TrimString(Coords[0]); - Coords[1] = TrimString(Coords[1]); - if (Coords[0].empty() || Coords[1].empty()) - { - return false; - } - cCraftingRecipes::cRecipeSlot Slot; - Slot.m_Item = Item; - switch (Coords[0][0]) - { - case '1': Slot.x = 0; break; - case '2': Slot.x = 1; break; - case '3': Slot.x = 2; break; - case '*': Slot.x = -1; break; - default: - { - return false; - } - } - switch (Coords[1][0]) - { - case '1': Slot.y = 0; break; - case '2': Slot.y = 1; break; - case '3': Slot.y = 2; break; - case '*': Slot.y = -1; break; - default: - { - return false; - } - } - TempSlots.push_back(Slot); - } // for itr - Split[] - - // Append the ingredients: - a_Recipe->m_Ingredients.insert(a_Recipe->m_Ingredients.end(), TempSlots.begin(), TempSlots.end()); - return true; -} - - - - - -void cCraftingRecipes::NormalizeIngredients(cCraftingRecipes::cRecipe * a_Recipe) -{ - // Calculate the minimum coords for ingredients, excluding the "anywhere" items: - int MinX = MAX_GRID_WIDTH, MaxX = 0; - int MinY = MAX_GRID_HEIGHT, MaxY = 0; - for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr) - { - if (itr->x >= 0) - { - MinX = std::min(itr->x, MinX); - MaxX = std::max(itr->x, MaxX); - } - if (itr->y >= 0) - { - MinY = std::min(itr->y, MinY); - MaxY = std::max(itr->y, MaxY); - } - } // for itr - a_Recipe->m_Ingredients[] - - // Move ingredients so that the minimum coords are 0:0 - for (cRecipeSlots::iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr) - { - if (itr->x >= 0) - { - itr->x -= MinX; - } - if (itr->y >= 0) - { - itr->y -= MinY; - } - } // for itr - a_Recipe->m_Ingredients[] - a_Recipe->m_Width = std::max(MaxX - MinX + 1, 1); - a_Recipe->m_Height = std::max(MaxY - MinY + 1, 1); - - // TODO: Compress two same ingredients with the same coords into a single ingredient with increased item count -} - - - - - -cCraftingRecipes::cRecipe * cCraftingRecipes::FindRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight) -{ - ASSERT(a_GridWidth <= MAX_GRID_WIDTH); - ASSERT(a_GridHeight <= MAX_GRID_HEIGHT); - - // Get the real bounds of the crafting grid: - int GridLeft = MAX_GRID_WIDTH, GridTop = MAX_GRID_HEIGHT; - int GridRight = 0, GridBottom = 0; - for (int y = 0; y < a_GridHeight; y++ ) for(int x = 0; x < a_GridWidth; x++) - { - if (!a_CraftingGrid[x + y * a_GridWidth].IsEmpty()) - { - GridRight = std::max(x, GridRight); - GridBottom = std::max(y, GridBottom); - GridLeft = std::min(x, GridLeft); - GridTop = std::min(y, GridTop); - } - } - int GridWidth = GridRight - GridLeft + 1; - int GridHeight = GridBottom - GridTop + 1; - - // Search in the possibly minimized grid, but keep the stride: - const cItem * Grid = a_CraftingGrid + GridLeft + (a_GridWidth * GridTop); - cRecipe * Recipe = FindRecipeCropped(Grid, GridWidth, GridHeight, a_GridWidth); - if (Recipe == NULL) - { - return NULL; - } - - // A recipe has been found, move it to correspond to the original crafting grid: - for (cRecipeSlots::iterator itrS = Recipe->m_Ingredients.begin(); itrS != Recipe->m_Ingredients.end(); ++itrS) - { - itrS->x += GridLeft; - itrS->y += GridTop; - } // for itrS - Recipe->m_Ingredients[] - - return Recipe; -} - - - - - -cCraftingRecipes::cRecipe * cCraftingRecipes::FindRecipeCropped(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride) -{ - for (cRecipes::const_iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr) - { - // Both the crafting grid and the recipes are normalized. The only variable possible is the "anywhere" items. - // This still means that the "anywhere" item may be the one that is offsetting the grid contents to the right or downwards, so we need to check all possible positions. - // E. g. recipe "A, * | B, 1:1 | ..." still needs to check grid for B at 2:2 (in case A was in grid's 1:1) - // Calculate the maximum offsets for this recipe relative to the grid size, and iterate through all combinations of offsets. - // Also, this calculation automatically filters out recipes that are too large for the current grid - the loop won't be entered at all. - - int MaxOfsX = a_GridWidth - (*itr)->m_Width; - int MaxOfsY = a_GridHeight - (*itr)->m_Height; - for (int x = 0; x <= MaxOfsX; x++) for (int y = 0; y <= MaxOfsY; y++) - { - cRecipe * Recipe = MatchRecipe(a_CraftingGrid, a_GridWidth, a_GridHeight, a_GridStride, *itr, x, y); - if (Recipe != NULL) - { - return Recipe; - } - } // for y, for x - } // for itr - m_Recipes[] - - // No matching recipe found - return NULL; -} - - - - - -cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY) -{ - // Check the regular items first: - bool HasMatched[MAX_GRID_WIDTH][MAX_GRID_HEIGHT]; - memset(HasMatched, 0, sizeof(HasMatched)); - for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS) - { - if ((itrS->x < 0) || (itrS->y < 0)) - { - // "Anywhere" item, process later - continue; - } - ASSERT(itrS->x + a_OffsetX < a_GridWidth); - ASSERT(itrS->y + a_OffsetY < a_GridHeight); - int GridID = (itrS->x + a_OffsetX) + a_GridStride * (itrS->y + a_OffsetY); - if ( - (itrS->x >= a_GridWidth) || - (itrS->y >= a_GridHeight) || - (itrS->m_Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type? - (itrS->m_Item.m_ItemCount > a_CraftingGrid[GridID].m_ItemCount) || // not enough items - ( - (itrS->m_Item.m_ItemDamage > 0) && // should compare damage values? - (itrS->m_Item.m_ItemDamage != a_CraftingGrid[GridID].m_ItemDamage) - ) - ) - { - // Doesn't match - return NULL; - } - HasMatched[itrS->x + a_OffsetX][itrS->y + a_OffsetY] = true; - } // for itrS - Recipe->m_Ingredients[] - - // Process the "Anywhere" items now, and only in the cells that haven't matched yet - // The "anywhere" items are processed on a first-come-first-served basis. - // Do not use a recipe with one horizontal and one vertical "anywhere" ("*:1, 1:*") as it may not match properly! - cRecipeSlots MatchedSlots; // Stores the slots of "anywhere" items that have matched, with the match coords - for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS) - { - if ((itrS->x >= 0) && (itrS->y >= 0)) - { - // Regular item, already processed - continue; - } - int StartX = 0, EndX = a_GridWidth - 1; - int StartY = 0, EndY = a_GridHeight - 1; - if (itrS->x >= 0) - { - StartX = itrS->x; - EndX = itrS->x; - } - else if (itrS->y >= 0) - { - StartY = itrS->y; - EndY = itrS->y; - } - bool Found = false; - for (int x = StartX; x <= EndX; x++) - { - for (int y = StartY; y <= EndY; y++) - { - if (HasMatched[x][y]) - { - // Already matched some other item - continue; - } - int GridIdx = x + a_GridStride * y; - if ( - (a_CraftingGrid[GridIdx].m_ItemType == itrS->m_Item.m_ItemType) && - ( - (itrS->m_Item.m_ItemDamage < 0) || // doesn't want damage comparison - (itrS->m_Item.m_ItemDamage == a_CraftingGrid[GridIdx].m_ItemDamage) // the damage matches - ) - ) - { - HasMatched[x][y] = true; - Found = true; - MatchedSlots.push_back(*itrS); - MatchedSlots.back().x = x; - MatchedSlots.back().y = y; - break; - } - } // for y - if (Found) - { - break; - } - } // for x - if (!Found) - { - return NULL; - } - } // for itrS - a_Recipe->m_Ingredients[] - - // Check if the whole grid has matched: - for (int x = 0; x < a_GridWidth; x++) for (int y = 0; y < a_GridHeight; y++) - { - if (!HasMatched[x][y] && !a_CraftingGrid[x + a_GridStride * y].IsEmpty()) - { - // There's an unmatched item in the grid - return NULL; - } - } // for y, for x - - // The recipe has matched. Create a copy of the recipe and set its coords to match the crafting grid: - std::auto_ptr Recipe(new cRecipe); - Recipe->m_Result = a_Recipe->m_Result; - Recipe->m_Width = a_Recipe->m_Width; - Recipe->m_Height = a_Recipe->m_Height; - for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS) - { - if ((itrS->x < 0) || (itrS->y < 0)) - { - // "Anywhere" item, process later - continue; - } - Recipe->m_Ingredients.push_back(*itrS); - } - Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end()); - return Recipe.release(); -} - - - - diff --git a/source/CraftingRecipes.h b/source/CraftingRecipes.h deleted file mode 100644 index 9d92cbfab..000000000 --- a/source/CraftingRecipes.h +++ /dev/null @@ -1,172 +0,0 @@ - -// CraftingRecipes.h - -// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes - - - - -#pragma once - -#include "Item.h" - - - - - -// fwd: cPlayer.h -class cPlayer; - - - - - -class cCraftingGrid // tolua_export -{ // tolua_export -public: - cCraftingGrid(const cCraftingGrid & a_Original); - cCraftingGrid(int a_Width, int a_Height); // tolua_export - cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height); - ~cCraftingGrid(); - - // tolua_begin - int GetWidth (void) const {return m_Width; } - int GetHeight(void) const {return m_Height; } - cItem & GetItem (int x, int y) const; - void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth); - void SetItem (int x, int y, const cItem & a_Item); - void Clear (void); - - /// Removes items in a_Grid from m_Items[] (used by cCraftingRecipe::ConsumeIngredients()) - void ConsumeGrid(const cCraftingGrid & a_Grid); - - /// Dumps the entire crafting grid using LOGD() - void Dump(void); - - // tolua_end - - cItem * GetItems(void) const {return m_Items; } - - /// Copies internal contents into the item array specified. Assumes that the array has the same dimensions as self - void CopyToItems(cItem * a_Items) const; - -protected: - - int m_Width; - int m_Height; - cItem * m_Items; -} ; // tolua_export - - - - - -class cCraftingRecipe // tolua_export -{ // tolua_export -public: - cCraftingRecipe(const cCraftingGrid & a_CraftingGrid); - - // tolua_begin - void Clear (void); - int GetIngredientsWidth (void) const {return m_Ingredients.GetWidth(); } - int GetIngredientsHeight(void) const {return m_Ingredients.GetHeight(); } - cItem & GetIngredient (int x, int y) const {return m_Ingredients.GetItem(x, y); } - const cItem & GetResult (void) const {return m_Result; } - void SetResult (ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth); - void SetResult (const cItem & a_Item) - { - m_Result = a_Item; - } - - void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) - { - m_Ingredients.SetItem(x, y, a_ItemType, a_ItemCount, a_ItemHealth); - } - - void SetIngredient (int x, int y, const cItem & a_Item) - { - m_Ingredients.SetItem(x, y, a_Item); - } - - /// Consumes ingredients from the crafting grid specified - void ConsumeIngredients(cCraftingGrid & a_CraftingGrid); - - /// Dumps the entire recipe using LOGD() - void Dump(void); - // tolua_end - -protected: - - cCraftingGrid m_Ingredients; // Adjusted to correspond to the input crafting grid! - cItem m_Result; -} ; // tolua_export - - - - - -class cCraftingRecipes -{ -public: - static const int MAX_GRID_WIDTH = 3; - static const int MAX_GRID_HEIGHT = 3; - - cCraftingRecipes(void); - ~cCraftingRecipes(); - - /// Returns the recipe for current crafting grid. Doesn't modify the grid. Clears a_Recipe if no recipe found. - void GetRecipe(const cPlayer * a_Player, const cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe); - -protected: - - struct cRecipeSlot - { - cItem m_Item; - int x, y; // 1..3, or -1 for "any" - } ; - typedef std::vector cRecipeSlots; - - /** A single recipe, stored. Each recipe is normalized right after parsing (NormalizeIngredients()) - A normalized recipe starts at (0,0) - */ - struct cRecipe - { - cRecipeSlots m_Ingredients; - cItem m_Result; - - // Size of the regular items in the recipe; "anywhere" items are excluded: - int m_Width; - int m_Height; - } ; - typedef std::vector cRecipes; - - cRecipes m_Recipes; - - void LoadRecipes(void); - void ClearRecipes(void); - - /// Parses the recipe line and adds it into m_Recipes. a_LineNum is used for diagnostic warnings only - void AddRecipeLine(int a_LineNum, const AString & a_RecipeLine); - - /// Parses an item string in the format "[^]", returns true if successful. - bool ParseItem(const AString & a_String, cItem & a_Item); - - /// Parses one ingredient and adds it to the specified recipe. Returns true if successful. - bool ParseIngredient(const AString & a_String, cRecipe * a_Recipe); - - /// Moves the recipe to top-left corner, sets its MinWidth / MinHeight - void NormalizeIngredients(cRecipe * a_Recipe); - - /// Finds a recipe matching the crafting grid. Returns a newly allocated recipe (with all its coords set) or NULL if not found. Caller must delete return value! - cRecipe * FindRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight); - - /// Same as FindRecipe, but the grid is guaranteed to be of minimal dimensions needed - cRecipe * FindRecipeCropped(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride); - - /// Checks if the grid matches the specified recipe, offset by the specified offsets. Returns a matched cRecipe * if so, or NULL if not matching. Caller must delete the return value! - cRecipe * MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY); -} ; - - - - diff --git a/source/Cuboid.cpp b/source/Cuboid.cpp deleted file mode 100644 index ea6f7c453..000000000 --- a/source/Cuboid.cpp +++ /dev/null @@ -1,117 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Cuboid.h" - - - - - -/// Returns true if the two specified intervals have a non-empty union -static bool DoIntervalsIntersect(int a_Min1, int a_Max1, int a_Min2, int a_Max2) -{ - return ( - ((a_Min1 >= a_Min2) && (a_Min1 <= a_Max2)) || // Start of first interval is within the second interval - ((a_Max1 >= a_Min2) && (a_Max1 <= a_Max2)) || // End of first interval is within the second interval - ((a_Min2 >= a_Min1) && (a_Min2 <= a_Max1)) // Start of second interval is within the first interval - ); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCuboid: - -void cCuboid::Assign(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2) -{ - p1.x = a_X1; - p1.y = a_Y1; - p1.z = a_Z1; - p2.x = a_X2; - p2.y = a_Y2; - p2.z = a_Z2; -} - - - - - -void cCuboid::Sort(void) -{ - if (p1.x > p2.x) - { - std::swap(p1.x, p2.x); - } - if (p1.y > p2.y) - { - std::swap(p1.y, p2.y); - } - if (p1.z > p2.z) - { - std::swap(p1.z, p2.z); - } -} - - - - - -bool cCuboid::DoesIntersect(const cCuboid & a_Other) const -{ - // In order for cuboids to intersect, each of their coord intervals need to intersect - return ( - DoIntervalsIntersect(p1.x, p2.x, a_Other.p1.x, a_Other.p2.x) && - DoIntervalsIntersect(p1.y, p2.y, a_Other.p1.y, a_Other.p2.y) && - DoIntervalsIntersect(p1.z, p2.z, a_Other.p1.z, a_Other.p2.z) - ); -} - - - - - -bool cCuboid::IsCompletelyInside(const cCuboid & a_Outer) const -{ - return ( - (p1.x >= a_Outer.p1.x) && - (p2.x <= a_Outer.p2.x) && - (p1.y >= a_Outer.p1.y) && - (p2.y <= a_Outer.p2.y) && - (p1.z >= a_Outer.p1.z) && - (p2.z <= a_Outer.p2.z) - ); -} - - - - - -void cCuboid::Move(int a_OfsX, int a_OfsY, int a_OfsZ) -{ - p1.x += a_OfsX; - p1.y += a_OfsY; - p1.z += a_OfsZ; - p2.x += a_OfsX; - p2.y += a_OfsY; - p2.z += a_OfsZ; -} - - - - - - -bool cCuboid::IsSorted(void) const -{ - return ( - (p1.x <= p2.x) && - (p1.y <= p2.y) && - (p1.z <= p2.z) - ); -} - - - - diff --git a/source/Cuboid.h b/source/Cuboid.h deleted file mode 100644 index 44db7b98e..000000000 --- a/source/Cuboid.h +++ /dev/null @@ -1,75 +0,0 @@ - -#pragma once - -#include "Vector3i.h" -#include "Vector3d.h" - - - - - -// tolua_begin -class cCuboid -{ -public: - // p1 is expected to have the smaller of the coords; Sort() swaps coords to match this - Vector3i p1, p2; - - cCuboid(void) {} - cCuboid(const cCuboid & a_Cuboid ) : p1(a_Cuboid.p1), p2(a_Cuboid.p2) {} - cCuboid(const Vector3i & a_p1, const Vector3i & a_p2) : p1(a_p1), p2(a_p2) {} - cCuboid(int a_X1, int a_Y1, int a_Z1) : p1(a_X1, a_Y1, a_Z1), p2(a_X1, a_Y1, a_Z1) {} - cCuboid(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2) : p1(a_X1, a_Y1, a_Z1), p2(a_X2, a_Y2, a_Z2) {} - - void Assign(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2); - - void Sort(void); - - int DifX(void) const { return p2.x - p1.x; } - int DifY(void) const { return p2.y - p1.y; } - int DifZ(void) const { return p2.z - p1.z; } - - /// Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive. - bool DoesIntersect(const cCuboid & a_Other) const; - - bool IsInside(const Vector3i & v) const - { - return ( - (v.x >= p1.x) && (v.x <= p2.x) && - (v.y >= p1.y) && (v.y <= p2.y) && - (v.z >= p1.z) && (v.z <= p2.z) - ); - } - - bool IsInside(int a_X, int a_Y, int a_Z) const - { - return ( - (a_X >= p1.x) && (a_X <= p2.x) && - (a_Y >= p1.y) && (a_Y <= p2.y) && - (a_Z >= p1.z) && (a_Z <= p2.z) - ); - } - - bool IsInside( const Vector3d & v ) const - { - return ( - (v.x >= p1.x) && (v.x <= p2.x) && - (v.y >= p1.y) && (v.y <= p2.y) && - (v.z >= p1.z) && (v.z <= p2.z) - ); - } - - /// Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords) - bool IsCompletelyInside(const cCuboid & a_Outer) const; - - /// Moves the cuboid by the specified offsets in each direction - void Move(int a_OfsX, int a_OfsY, int a_OfsZ); - - /// Returns true if the coords are properly sorted (lesser in p1, greater in p2) - bool IsSorted(void) const; -} ; -// tolua_end - - - - diff --git a/source/DeadlockDetect.cpp b/source/DeadlockDetect.cpp deleted file mode 100644 index c774c9dce..000000000 --- a/source/DeadlockDetect.cpp +++ /dev/null @@ -1,147 +0,0 @@ - -// DeadlockDetect.cpp - -// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one - -#include "Globals.h" -#include "DeadlockDetect.h" -#include "Root.h" -#include "World.h" - - - - - -/// Number of milliseconds per cycle -const int CYCLE_MILLISECONDS = 100; - -/// When the number of cycles for the same world age hits this value, it is considered a deadlock -const int NUM_CYCLES_LIMIT = 200; // 200 = twenty seconds - - - - - -cDeadlockDetect::cDeadlockDetect(void) : - super("DeadlockDetect") -{ -} - - - - - -bool cDeadlockDetect::Start(void) -{ - // Read the initial world data: - class cFillIn : - public cWorldListCallback - { - public: - cFillIn(cDeadlockDetect * a_Detect) : - m_Detect(a_Detect) - { - } - - virtual bool Item(cWorld * a_World) override - { - m_Detect->SetWorldAge(a_World->GetName(), a_World->GetWorldAge()); - return false; - } - - protected: - cDeadlockDetect * m_Detect; - } FillIn(this); - cRoot::Get()->ForEachWorld(FillIn); - return super::Start(); -} - - - - - -void cDeadlockDetect::Execute(void) -{ - // Loop until the signal to terminate: - while (!m_ShouldTerminate) - { - // Check the world ages: - class cChecker : - public cWorldListCallback - { - public: - cChecker(cDeadlockDetect * a_Detect) : - m_Detect(a_Detect) - { - } - - protected: - cDeadlockDetect * m_Detect; - - virtual bool Item(cWorld * a_World) override - { - m_Detect->CheckWorldAge(a_World->GetName(), a_World->GetWorldAge()); - return false; - } - } Checker(this); - cRoot::Get()->ForEachWorld(Checker); - - cSleep::MilliSleep(CYCLE_MILLISECONDS); - } // while (should run) -} - - - - - -void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, Int64 a_Age) -{ - m_WorldAges[a_WorldName].m_Age = a_Age; - m_WorldAges[a_WorldName].m_NumCyclesSame = 0; -} - - - - - -void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age) -{ - WorldAges::iterator itr = m_WorldAges.find(a_WorldName); - if (itr == m_WorldAges.end()) - { - ASSERT(!"Unknown world in cDeadlockDetect"); - return; - } - if (itr->second.m_Age == a_Age) - { - itr->second.m_NumCyclesSame += 1; - if (itr->second.m_NumCyclesSame > NUM_CYCLES_LIMIT) - { - DeadlockDetected(); - return; - } - } - else - { - itr->second.m_Age = a_Age; - itr->second.m_NumCyclesSame = 0; - } -} - - - - - -void cDeadlockDetect::DeadlockDetected(void) -{ - ASSERT(!"Deadlock detected"); - - // TODO: Make a crashdump / coredump - - // Crash the server intentionally: - *((volatile int *)0) = 0; -} - - - - diff --git a/source/DeadlockDetect.h b/source/DeadlockDetect.h deleted file mode 100644 index 2559c3fff..000000000 --- a/source/DeadlockDetect.h +++ /dev/null @@ -1,65 +0,0 @@ - -// DeadlockDetect.h - -// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one - -/* -This class simply monitors each world's m_WorldAge, which is expected to grow on each tick. -If the world age doesn't grow for several seconds, it's either because the server is super-overloaded, -or because the world tick thread hangs in a deadlock. We presume the latter and therefore kill the server. -Once we learn to write crashdumps programmatically, we should do so just before killing, to enable debugging. -*/ - - - -#pragma once - -#include "OSSupport/IsThread.h" - - - - - -class cDeadlockDetect : - public cIsThread -{ - typedef cIsThread super; - -public: - cDeadlockDetect(void); - - /// Starts the detection. Hides cIsThread's Start, because we need some initialization - bool Start(void); - -protected: - struct sWorldAge - { - /// Last m_WorldAge that has been detected in this world - Int64 m_Age; - - /// Number of cycles for which the age has been the same - int m_NumCyclesSame; - } ; - - /// Maps world name -> sWorldAge - typedef std::map WorldAges; - - WorldAges m_WorldAges; - - - // cIsThread overrides: - virtual void Execute(void) override; - - /// Sets the initial world age - void SetWorldAge(const AString & a_WorldName, Int64 a_Age); - - /// Checks if the world's age has changed, updates the world's stats; calls DeadlockDetected() if deadlock detected - void CheckWorldAge(const AString & a_WorldName, Int64 a_Age); - - /// Called when a deadlock is detected. Aborts the server. - void DeadlockDetected(void); -} ; - - - - diff --git a/source/Defines.h b/source/Defines.h deleted file mode 100644 index 5621aeac1..000000000 --- a/source/Defines.h +++ /dev/null @@ -1,562 +0,0 @@ - -#pragma once - - - - - -typedef unsigned char Byte; - -/// List of slot numbers, used for inventory-painting -typedef std::vector cSlotNums; - - - - - - -// tolua_begin - -/// How much light do the blocks emit on their own? -extern unsigned char g_BlockLightValue[]; - -/// How much light do the block consume? -extern unsigned char g_BlockSpreadLightFalloff[]; - -/// Is a block completely transparent? (light doesn't get decreased(?)) -extern bool g_BlockTransparent[]; - -/// Is a block destroyed after a single hit? -extern bool g_BlockOneHitDig[]; - -/// Can a piston break this block? -extern bool g_BlockPistonBreakable[256]; - -/// Can this block hold snow atop? -extern bool g_BlockIsSnowable[256]; - -/// Does this block require a tool to drop? -extern bool g_BlockRequiresSpecialTool[256]; - -/// Is this block solid (player cannot walk through)? -extern bool g_BlockIsSolid[256]; - -/// Can torches be placed on this block? -extern bool g_BlockIsTorchPlaceable[256]; - -/// Experience Orb setup -enum -{ - //open to suggestion on naming convention here :) - MAX_EXPERIENCE_ORB_SIZE = 2000 -} ; - - - - - -/// Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc -enum eBlockFace -{ - BLOCK_FACE_NONE = -1, // Interacting with no block face - swinging the item in the air - BLOCK_FACE_XM = 5, // Interacting with the X- face of the block - BLOCK_FACE_XP = 4, // Interacting with the X+ face of the block - BLOCK_FACE_YM = 0, // Interacting with the Y- face of the block - BLOCK_FACE_YP = 1, // Interacting with the Y+ face of the block - BLOCK_FACE_ZM = 3, // Interacting with the Z- face of the block - BLOCK_FACE_ZP = 2, // Interacting with the Z+ face of the block - - // Synonyms using the (deprecated) world directions: - BLOCK_FACE_BOTTOM = BLOCK_FACE_YM, // Interacting with the bottom face of the block - BLOCK_FACE_TOP = BLOCK_FACE_YP, // Interacting with the top face of the block - BLOCK_FACE_NORTH = BLOCK_FACE_ZP, // Interacting with the northern face of the block - BLOCK_FACE_SOUTH = BLOCK_FACE_ZM, // Interacting with the southern face of the block - BLOCK_FACE_WEST = BLOCK_FACE_XP, // Interacting with the western face of the block - BLOCK_FACE_EAST = BLOCK_FACE_XM, // Interacting with the eastern face of the block -} ; - - - - - -/// PlayerDigging status constants -enum -{ - DIG_STATUS_STARTED = 0, - DIG_STATUS_CANCELLED = 1, - DIG_STATUS_FINISHED = 2, - DIG_STATUS_DROP_HELD = 4, - DIG_STATUS_SHOOT_EAT = 5, -} ; - - - - - -/// Individual actions sent in the WindowClick packet -enum eClickAction -{ - // Sorted by occurrence in the 1.5 protocol - caLeftClick, - caRightClick, - caShiftLeftClick, - caShiftRightClick, - caNumber1, - caNumber2, - caNumber3, - caNumber4, - caNumber5, - caNumber6, - caNumber7, - caNumber8, - caNumber9, - caMiddleClick, - caDropKey, - caCtrlDropKey, - caLeftClickOutside, - caRightClickOutside, - caLeftClickOutsideHoldNothing, - caRightClickOutsideHoldNothing, - caLeftPaintBegin, - caRightPaintBegin, - caLeftPaintProgress, - caRightPaintProgress, - caLeftPaintEnd, - caRightPaintEnd, - caDblClick, - // Add new actions here - caUnknown = 255, - - // Keep this list in sync with ClickActionToString() function below! -} ; - - - - - -enum eGameMode -{ - eGameMode_NotSet = -1, - eGameMode_Survival = 0, - eGameMode_Creative = 1, - eGameMode_Adventure = 2, - - // Easier-to-use synonyms: - gmNotSet = eGameMode_NotSet, - gmSurvival = eGameMode_Survival, - gmCreative = eGameMode_Creative, - gmAdventure = eGameMode_Adventure, - - // These two are used to check GameMode for validity when converting from integers. - gmMax, // Gets automatically assigned - gmMin = 0, -} ; - - - - - -enum eWeather -{ - eWeather_Sunny = 0, - eWeather_Rain = 1, - eWeather_ThunderStorm = 2, - - // Easier-to-use synonyms: - wSunny = eWeather_Sunny, - wRain = eWeather_Rain, - wThunderstorm = eWeather_ThunderStorm, - wStorm = wThunderstorm, -} ; - - - - - -inline const char * ClickActionToString(eClickAction a_ClickAction) -{ - switch (a_ClickAction) - { - case caLeftClick: return "caLeftClick"; - case caRightClick: return "caRightClick"; - case caShiftLeftClick: return "caShiftLeftClick"; - case caShiftRightClick: return "caShiftRightClick"; - case caNumber1: return "caNumber1"; - case caNumber2: return "caNumber2"; - case caNumber3: return "caNumber3"; - case caNumber4: return "caNumber4"; - case caNumber5: return "caNumber5"; - case caNumber6: return "caNumber6"; - case caNumber7: return "caNumber7"; - case caNumber8: return "caNumber8"; - case caNumber9: return "caNumber9"; - case caMiddleClick: return "caMiddleClick"; - case caDropKey: return "caDropKey"; - case caCtrlDropKey: return "caCtrlDropKey"; - case caLeftClickOutside: return "caLeftClickOutside"; - case caRightClickOutside: return "caRightClickOutside"; - case caLeftClickOutsideHoldNothing: return "caLeftClickOutsideHoldNothing"; - case caRightClickOutsideHoldNothing: return "caRightClickOutsideHoldNothing"; - case caLeftPaintBegin: return "caLeftPaintBegin"; - case caRightPaintBegin: return "caRightPaintBegin"; - case caLeftPaintProgress: return "caLeftPaintProgress"; - case caRightPaintProgress: return "caRightPaintProgress"; - case caLeftPaintEnd: return "caLeftPaintEnd"; - case caRightPaintEnd: return "caRightPaintEnd"; - case caDblClick: return "caDblClick"; - - case caUnknown: return "caUnknown"; - } - ASSERT(!"Unknown click action"); - return "caUnknown"; -} - - - - - -inline bool IsValidBlock(int a_BlockType) -{ - if ( - (a_BlockType > -1) && - (a_BlockType <= E_BLOCK_MAX_TYPE_ID) - ) - { - return true; - } - return false; -} - - - - - -inline bool IsValidItem(int a_ItemType) -{ - if ( - ((a_ItemType >= E_ITEM_FIRST) && (a_ItemType <= E_ITEM_MAX_CONSECUTIVE_TYPE_ID)) || // Basic items range - ((a_ItemType >= E_ITEM_FIRST_DISC) && (a_ItemType <= E_ITEM_LAST_DISC)) // Music discs' special range - ) - { - return true; - } - - if (a_ItemType == 0) - { - return false; - } - - return IsValidBlock(a_ItemType); -} - -// tolua_end - - - - - -inline bool IsBlockWater(BLOCKTYPE a_BlockType) -{ - return ((a_BlockType == E_BLOCK_WATER) || (a_BlockType == E_BLOCK_STATIONARY_WATER)); -} - - - - - -inline bool IsBlockLava(BLOCKTYPE a_BlockType) -{ - return ((a_BlockType == E_BLOCK_LAVA) || (a_BlockType == E_BLOCK_STATIONARY_LAVA)); -} - - - - - -inline bool IsBlockLiquid(BLOCKTYPE a_BlockType) -{ - return IsBlockWater(a_BlockType) || IsBlockLava(a_BlockType); -} - - - - - -inline bool IsBlockTypeOfDirt(BLOCKTYPE a_BlockType) -{ - switch (a_BlockType) - { - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_FARMLAND: - { - return true; - } - } - return false; -} - - - - -inline void AddFaceDirection(int & a_BlockX, int & a_BlockY, int & a_BlockZ, char a_BlockFace, bool a_bInverse = false) // tolua_export -{ // tolua_export - if (!a_bInverse) - { - switch (a_BlockFace) - { - case BLOCK_FACE_BOTTOM: a_BlockY--; break; - case BLOCK_FACE_TOP: a_BlockY++; break; - case BLOCK_FACE_EAST: a_BlockX++; break; - case BLOCK_FACE_WEST: a_BlockX--; break; - case BLOCK_FACE_NORTH: a_BlockZ--; break; - case BLOCK_FACE_SOUTH: a_BlockZ++; break; - default: - { - LOGWARNING("%s: Unknown face: %d", __FUNCTION__, a_BlockFace); - ASSERT(!"AddFaceDirection(): Unknown face"); - break; - } - } - } - else - { - switch (a_BlockFace) - { - case BLOCK_FACE_BOTTOM: a_BlockY++; break; - case BLOCK_FACE_TOP: a_BlockY--; break; - case BLOCK_FACE_EAST: a_BlockX--; break; - case BLOCK_FACE_WEST: a_BlockX++; break; - case BLOCK_FACE_NORTH: a_BlockZ++; break; - case BLOCK_FACE_SOUTH: a_BlockZ--; break; - default: - { - LOGWARNING("%s: Unknown inv face: %d", __FUNCTION__, a_BlockFace); - ASSERT(!"AddFaceDirection(): Unknown face"); - break; - } - } - } -} // tolua_export - - - - - -inline void AddFaceDirection(int & a_BlockX, unsigned char & a_BlockY, int & a_BlockZ, char a_BlockFace, bool a_bInverse = false) -{ - int Y = a_BlockY; - AddFaceDirection(a_BlockX, Y, a_BlockZ, a_BlockFace, a_bInverse); - if (Y < 0) - { - a_BlockY = 0; - } - else if (Y > 255) - { - a_BlockY = 255; - } - else - { - a_BlockY = (unsigned char)Y; - } -} - - - - - -#define PI 3.14159265358979323846264338327950288419716939937510582097494459072381640628620899862803482534211706798f - -inline void EulerToVector(double a_Pan, double a_Pitch, double & a_X, double & a_Y, double & a_Z) -{ - // a_X = sinf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI ); - // a_Y = -sinf ( a_Pitch / 180 * PI ); - // a_Z = -cosf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI ); - a_X = cos(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI); - a_Y = sin(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI); - a_Z = sin(a_Pitch / 180 * PI); -} - - - - - -inline void VectorToEuler(double a_X, double a_Y, double a_Z, double & a_Pan, double & a_Pitch) -{ - if (a_X != 0) - { - a_Pan = atan2(a_Z, a_X) * 180 / PI - 90; - } - else - { - a_Pan = 0; - } - a_Pitch = atan2(a_Y, sqrt((a_X * a_X) + (a_Z * a_Z))) * 180 / PI; -} - - - - - -inline float GetSignf(float a_Val) -{ - return (a_Val < 0.f) ? -1.f : 1.f; -} - - - - - -inline float GetSpecialSignf( float a_Val ) -{ - return (a_Val <= 0.f) ? -1.f : 1.f; -} - - - - -// tolua_begin -namespace ItemCategory -{ - inline bool IsPickaxe(short a_ItemID) - { - return (a_ItemID == E_ITEM_WOODEN_PICKAXE) - || (a_ItemID == E_ITEM_STONE_PICKAXE) - || (a_ItemID == E_ITEM_IRON_PICKAXE) - || (a_ItemID == E_ITEM_GOLD_PICKAXE) - || (a_ItemID == E_ITEM_DIAMOND_PICKAXE); - } - - - - inline bool IsAxe(short a_ItemID) - { - return (a_ItemID == E_ITEM_WOODEN_AXE) - || (a_ItemID == E_ITEM_STONE_AXE) - || (a_ItemID == E_ITEM_IRON_AXE) - || (a_ItemID == E_ITEM_GOLD_AXE) - || (a_ItemID == E_ITEM_DIAMOND_AXE); - } - - - - inline bool IsSword(short a_ItemID) - { - return (a_ItemID == E_ITEM_WOODEN_SWORD) - || (a_ItemID == E_ITEM_STONE_SWORD) - || (a_ItemID == E_ITEM_IRON_SWORD) - || (a_ItemID == E_ITEM_GOLD_SWORD) - || (a_ItemID == E_ITEM_DIAMOND_SWORD); - } - - - - inline bool IsHoe(short a_ItemID) - { - return (a_ItemID == E_ITEM_WOODEN_HOE) - || (a_ItemID == E_ITEM_STONE_HOE) - || (a_ItemID == E_ITEM_IRON_HOE) - || (a_ItemID == E_ITEM_GOLD_HOE) - || (a_ItemID == E_ITEM_DIAMOND_HOE); - } - - - - inline bool IsShovel(short a_ItemID) - { - return (a_ItemID == E_ITEM_WOODEN_SHOVEL) - || (a_ItemID == E_ITEM_STONE_SHOVEL) - || (a_ItemID == E_ITEM_IRON_SHOVEL) - || (a_ItemID == E_ITEM_GOLD_SHOVEL) - || (a_ItemID == E_ITEM_DIAMOND_SHOVEL); - } - - - - inline bool IsTool(short a_ItemID) - { - return IsPickaxe( a_ItemID ) - || IsAxe ( a_ItemID ) - || IsSword ( a_ItemID ) - || IsHoe ( a_ItemID ) - || IsShovel ( a_ItemID ); - } - - - - inline bool IsHelmet(short a_ItemType) - { - return ( - (a_ItemType == E_ITEM_LEATHER_CAP) || - (a_ItemType == E_ITEM_GOLD_HELMET) || - (a_ItemType == E_ITEM_CHAIN_HELMET) || - (a_ItemType == E_ITEM_IRON_HELMET) || - (a_ItemType == E_ITEM_DIAMOND_HELMET) - ); - } - - - - inline bool IsChestPlate(short a_ItemType) - { - return ( - (a_ItemType == E_ITEM_LEATHER_TUNIC) || - (a_ItemType == E_ITEM_GOLD_CHESTPLATE) || - (a_ItemType == E_ITEM_CHAIN_CHESTPLATE) || - (a_ItemType == E_ITEM_IRON_CHESTPLATE) || - (a_ItemType == E_ITEM_DIAMOND_CHESTPLATE) - ); - } - - - - inline bool IsLeggings(short a_ItemType) - { - return ( - (a_ItemType == E_ITEM_LEATHER_PANTS) || - (a_ItemType == E_ITEM_GOLD_LEGGINGS) || - (a_ItemType == E_ITEM_CHAIN_LEGGINGS) || - (a_ItemType == E_ITEM_IRON_LEGGINGS) || - (a_ItemType == E_ITEM_DIAMOND_LEGGINGS) - ); - } - - - - inline bool IsBoots(short a_ItemType) - { - return ( - (a_ItemType == E_ITEM_LEATHER_BOOTS) || - (a_ItemType == E_ITEM_GOLD_BOOTS) || - (a_ItemType == E_ITEM_CHAIN_BOOTS) || - (a_ItemType == E_ITEM_IRON_BOOTS) || - (a_ItemType == E_ITEM_DIAMOND_BOOTS) - ); - } - - - - inline bool IsArmor(short a_ItemType) - { - return ( - IsHelmet(a_ItemType) || - IsChestPlate(a_ItemType) || - IsLeggings(a_ItemType) || - IsBoots(a_ItemType) - ); - } -} -// tolua_end - - -inline bool BlockRequiresSpecialTool(BLOCKTYPE a_BlockType) -{ - if(!IsValidBlock(a_BlockType)) return false; - return g_BlockRequiresSpecialTool[a_BlockType]; -} - - - - - - diff --git a/source/Enchantments.cpp b/source/Enchantments.cpp deleted file mode 100644 index 6b53d0b52..000000000 --- a/source/Enchantments.cpp +++ /dev/null @@ -1,299 +0,0 @@ -// Enchantments.cpp - -// Implements the cEnchantments class representing a storage for item enchantments and stored-enchantments - -#include "Globals.h" -#include "Enchantments.h" -#include "WorldStorage/FastNBT.h" - - - - - -cEnchantments::cEnchantments(void) -{ - // Nothing needed yet, but the constructor needs to be declared and impemented in order to be usable -} - - - - - -cEnchantments::cEnchantments(const AString & a_StringSpec) -{ - AddFromString(a_StringSpec); -} - - - - - -void cEnchantments::AddFromString(const AString & a_StringSpec) -{ - // Add enchantments in the stringspec; if a specified enchantment already exists, overwrites it - - // Split the StringSpec into separate declarations, each in the form "id=lvl": - AStringVector Decls = StringSplit(a_StringSpec, ";"); - for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr) - { - // Split each declaration into the id and lvl part: - if (itr->empty()) - { - // The decl is empty (may happen if there's an extra semicolon at the end), ignore silently - continue; - } - AStringVector Split = StringSplitAndTrim(*itr, "="); - if (Split.size() != 2) - { - // Malformed decl - LOG("%s: Malformed enchantment decl: \"%s\", skipping.", __FUNCTION__, itr->c_str()); - continue; - } - int id = atoi(Split[0].c_str()); - if ((id == 0) && (Split[0] != "0")) - { - id = StringToEnchantmentID(Split[0]); - } - int lvl = atoi(Split[1].c_str()); - if ( - ((id <= 0) && (Split[0] != "0")) || - ((lvl == 0) && (Split[1] != "0")) - ) - { - // Numbers failed to parse - LOG("%s: Failed to parse enchantment declaration for numbers: \"%s\" and \"%s\", skipping.", - __FUNCTION__, Split[0].c_str(), Split[1].c_str() - ); - continue; - } - SetLevel(id, lvl); - } // for itr - Decls[] -} - - - - - -AString cEnchantments::ToString(void) const -{ - // Serialize all the enchantments into a string - AString res; - for (cEnchantments::cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) - { - AppendPrintf(res, "%d=%d;", itr->first, itr->second); - } // for itr - m_Enchantments[] - return res; -} - - - - - -int cEnchantments::GetLevel(int a_EnchantmentID) const -{ - // Return the level for the specified enchantment; 0 if not stored - cMap::const_iterator itr = m_Enchantments.find(a_EnchantmentID); - if (itr != m_Enchantments.end()) - { - return itr->second; - } - - // Not stored, return zero - return 0; -} - - - - - -void cEnchantments::SetLevel(int a_EnchantmentID, int a_Level) -{ - // Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 - if (a_Level == 0) - { - // Delete enchantment, if present: - cMap::iterator itr = m_Enchantments.find(a_EnchantmentID); - if (itr != m_Enchantments.end()) - { - m_Enchantments.erase(itr); - } - } - else - { - // Add / overwrite enchantment - m_Enchantments[a_EnchantmentID] = a_Level; - } -} - - - - - - -void cEnchantments::Clear(void) -{ - m_Enchantments.clear(); -} - - - - - -bool cEnchantments::IsEmpty(void) const -{ - return m_Enchantments.empty(); -} - - - - - -int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName) -{ - struct - { - int m_Value; - const char * m_Name; - } EnchantmentNames[] = - { - { enchProtection, "Protection"}, - { enchFireProtection, "FireProtection"}, - { enchFeatherFalling, "FeatherFalling"}, - { enchBlastProtection, "BlastProtection"}, - { enchProjectileProtection, "ProjectileProtection"}, - { enchRespiration, "Respiration"}, - { enchAquaAffinity, "AquaAffinity"}, - { enchThorns, "Thorns"}, - { enchSharpness, "Sharpness"}, - { enchSmite, "Smite"}, - { enchBaneOfArthropods, "BaneOfArthropods"}, - { enchKnockback, "Knockback"}, - { enchFireAspect, "FireAspect"}, - { enchLooting, "Looting"}, - { enchEfficiency, "Efficiency"}, - { enchSilkTouch, "SilkTouch"}, - { enchUnbreaking, "Unbreaking"}, - { enchFortune, "Fortune"}, - { enchPower, "Power"}, - { enchPunch, "Punch"}, - { enchFlame, "Flame"}, - { enchInfinity, "Infinity"}, - { enchLuckOfTheSea, "LuckOfTheSea"}, - { enchLure, "Lure"}, - } ; - for (int i = 0; i < ARRAYCOUNT(EnchantmentNames); i++) - { - if (NoCaseCompare(EnchantmentNames[i].m_Name, a_EnchantmentName) == 0) - { - return EnchantmentNames[i].m_Value; - } - } // for i - EnchantmentNames[] - return -1; -} - - - - - -bool cEnchantments::operator ==(const cEnchantments & a_Other) const -{ - return m_Enchantments == a_Other.m_Enchantments; -} - - - - - -bool cEnchantments::operator !=(const cEnchantments & a_Other) const -{ - return m_Enchantments != a_Other.m_Enchantments; -} - - - - - -void cEnchantments::WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const -{ - // Write the enchantments into the specified NBT writer - // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments") - - a_Writer.BeginList(a_ListTagName, TAG_Compound); - for (cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) - { - a_Writer.BeginCompound(""); - a_Writer.AddShort("id", itr->first); - a_Writer.AddShort("lvl", itr->second); - a_Writer.EndCompound(); - } // for itr - m_Enchantments[] - a_Writer.EndList(); -} - - - - - -void cEnchantments::ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx) -{ - // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments) - - // Verify that the tag is a list: - if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List) - { - LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed", - __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx) - ); - ASSERT(!"Bad EnchListTag type"); - return; - } - - // Verify that the list is of Compounds: - if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound) - { - LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed", - __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx) - ); - ASSERT(!"Bad EnchListTag children type"); - return; - } - - Clear(); - - // Iterate over all the compound children, parse an enchantment from each: - for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag)) - { - // tag is the compound inside the "ench" list tag - ASSERT(a_NBT.GetType(tag) == TAG_Compound); - - // Search for the id and lvl tags' values: - int id = -1, lvl = -1; - for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch)) - { - if (a_NBT.GetType(ch) != TAG_Short) - { - continue; - } - if (a_NBT.GetName(ch) == "id") - { - id = a_NBT.GetShort(ch); - } - else if (a_NBT.GetName(ch) == "lvl") - { - lvl = a_NBT.GetShort(ch); - } - } // for ch - children of the compound tag - - if ((id == -1) || (lvl <= 0)) - { - // Failed to parse either the id or the lvl, skip this compound - continue; - } - - // Store the enchantment: - m_Enchantments[id] = lvl; - } // for tag - children of the ench list tag -} - - - - diff --git a/source/Enchantments.h b/source/Enchantments.h deleted file mode 100644 index 7581b87b5..000000000 --- a/source/Enchantments.h +++ /dev/null @@ -1,115 +0,0 @@ -// Enchantments.h - -// Declares the cEnchantments class representing a storage for item enchantments and stored-enchantments - - - - - -#pragma once - - - - - -// fwd: WorldStorage/FastNBT.h -class cFastNBTWriter; -class cParsedNBT; - - - - - -// tolua_begin - -/** Class that stores item enchantments or stored-enchantments -The enchantments may be serialized to a stringspec and read back from such stringspec. -The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end, -mapping each enchantment's id onto its level. ID may be either a number or the enchantment name. -Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments. -Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs. -*/ -class cEnchantments -{ -public: - /// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs ) - enum - { - enchProtection = 0, - enchFireProtection = 1, - enchFeatherFalling = 2, - enchBlastProtection = 3, - enchProjectileProtection = 4, - enchRespiration = 5, - enchAquaAffinity = 6, - enchThorns = 7, - enchSharpness = 16, - enchSmite = 17, - enchBaneOfArthropods = 18, - enchKnockback = 19, - enchFireAspect = 20, - enchLooting = 21, - enchEfficiency = 32, - enchSilkTouch = 33, - enchUnbreaking = 34, - enchFortune = 35, - enchPower = 48, - enchPunch = 49, - enchFlame = 50, - enchInfinity = 51, - enchLuckOfTheSea = 61, - enchLure = 62, - } ; - - /// Creates an empty enchantments container - cEnchantments(void); - - /// Creates an enchantments container filled with enchantments parsed from stringspec - cEnchantments(const AString & a_StringSpec); - - /// Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it - void AddFromString(const AString & a_StringSpec); - - /// Serializes all the enchantments into a string - AString ToString(void) const; - - /// Returns the level for the specified enchantment; 0 if not stored - int GetLevel(int a_EnchantmentID) const; - - /// Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 - void SetLevel(int a_EnchantmentID, int a_Level); - - /// Removes all enchantments - void Clear(void); - - /// Returns true if there are no enchantments - bool IsEmpty(void) const; - - /// Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive - static int StringToEnchantmentID(const AString & a_EnchantmentName); - - /// Returns true if a_Other contains exactly the same enchantments and levels - bool operator ==(const cEnchantments & a_Other) const; - - // tolua_end - - /// Returns true if a_Other doesn't contain exactly the same enchantments and levels - bool operator !=(const cEnchantments & a_Other) const; - - /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") - void WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const; - - /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) - void ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx); - -protected: - /// Maps enchantment ID -> enchantment level - typedef std::map cMap; - - /// Currently stored enchantments - cMap m_Enchantments; -} ; // tolua_export - - - - diff --git a/source/Endianness.h b/source/Endianness.h deleted file mode 100644 index 86eb369f5..000000000 --- a/source/Endianness.h +++ /dev/null @@ -1,70 +0,0 @@ - -#pragma once - - - - - -// Changes endianness -inline unsigned long long HostToNetwork8(const void* a_Value ) -{ - unsigned long long __HostToNetwork8; - memcpy( &__HostToNetwork8, a_Value, sizeof( __HostToNetwork8 ) ); - __HostToNetwork8 = (( ( (unsigned long long)htonl((u_long)__HostToNetwork8) ) << 32) + htonl(__HostToNetwork8 >> 32)); - return __HostToNetwork8; -} - - - - - -inline unsigned int HostToNetwork4(const void* a_Value ) -{ - unsigned int __HostToNetwork4; - memcpy( &__HostToNetwork4, a_Value, sizeof( __HostToNetwork4 ) ); - __HostToNetwork4 = ntohl( __HostToNetwork4 ); - return __HostToNetwork4; -} - - - - - -inline double NetworkToHostDouble8(const void* a_Value ) -{ -#define ntohll(x) ((((unsigned long long)ntohl((u_long)x)) << 32) + ntohl(x >> 32)) - unsigned long long buf = 0;//(*(unsigned long long*)a_Value); - memcpy( &buf, a_Value, 8 ); - buf = ntohll(buf); - double x; - memcpy(&x, &buf, sizeof(double)); - return x; -} - - - - - -inline long long NetworkToHostLong8(const void * a_Value ) -{ - unsigned long long buf = *(unsigned long long*)a_Value; - buf = ntohll(buf); - return *reinterpret_cast(&buf); -} - - - - - -inline float NetworkToHostFloat4(const void* a_Value ) -{ - u_long buf = *(u_long*)a_Value; - buf = ntohl( buf ); - float x = 0; - memcpy( &x, &buf, sizeof(float) ); - return x; -} - - - - diff --git a/source/Entities/Boat.cpp b/source/Entities/Boat.cpp deleted file mode 100644 index 56e766dd4..000000000 --- a/source/Entities/Boat.cpp +++ /dev/null @@ -1,87 +0,0 @@ - -// Boat.cpp - -// Implements the cBoat class representing a boat in the world - -#include "Globals.h" -#include "Boat.h" -#include "../World.h" -#include "../ClientHandle.h" -#include "Player.h" - - - - - -cBoat::cBoat(double a_X, double a_Y, double a_Z) : - super(etBoat, a_X, a_Y, a_Z, 0.98, 0.7) -{ - SetMass(20.f); - SetMaxHealth(6); - SetHealth(6); -} - - - - -void cBoat::SpawnOn(cClientHandle & a_ClientHandle) -{ - a_ClientHandle.SendSpawnVehicle(*this, 1); -} - - - - - -void cBoat::DoTakeDamage(TakeDamageInfo & TDI) -{ - super::DoTakeDamage(TDI); - - if (GetHealth() == 0) - { - Destroy(true); - } -} - - - - - -void cBoat::OnRightClicked(cPlayer & a_Player) -{ - if (m_Attachee != NULL) - { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) - { - // This player is already sitting in, they want out. - a_Player.Detach(); - return; - } - - if (m_Attachee->IsPlayer()) - { - // Another player is already sitting in here, cannot attach - return; - } - - // Detach whatever is sitting in this boat now: - m_Attachee->Detach(); - } - - // Attach the player to this boat - a_Player.AttachTo(this); -} - - - - - -void cBoat::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - super::HandlePhysics(a_Dt, a_Chunk); - BroadcastMovementUpdate(); -} - - - - diff --git a/source/Entities/Boat.h b/source/Entities/Boat.h deleted file mode 100644 index 8c51ab86c..000000000 --- a/source/Entities/Boat.h +++ /dev/null @@ -1,37 +0,0 @@ - -// Boat.h - -// Declares the cBoat class representing a boat in the world - - - - - -#pragma once - -#include "Entity.h" - - - - - -class cBoat : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cBoat); - - // cEntity overrides: - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void DoTakeDamage(TakeDamageInfo & TDI) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - - cBoat(double a_X, double a_Y, double a_Z); -} ; - - - - diff --git a/source/Entities/Entity.cpp b/source/Entities/Entity.cpp deleted file mode 100644 index 3bea7bc01..000000000 --- a/source/Entities/Entity.cpp +++ /dev/null @@ -1,1450 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Entity.h" -#include "../World.h" -#include "../Server.h" -#include "../Root.h" -#include "../Vector3d.h" -#include "../Matrix4f.h" -#include "../ReferenceManager.h" -#include "../ClientHandle.h" -#include "../Chunk.h" -#include "../Simulator/FluidSimulator.h" -#include "../PluginManager.h" -#include "../Tracer.h" -#include "Minecart.h" - - - - - -int cEntity::m_EntityCount = 0; -cCriticalSection cEntity::m_CSCount; - - - - - -cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) - : m_UniqueID(0) - , m_Health(1) - , m_MaxHealth(1) - , m_AttachedTo(NULL) - , m_Attachee(NULL) - , m_Referencers(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCERS)) - , m_References(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCES)) - , m_HeadYaw( 0.0 ) - , m_Rot(0.0, 0.0, 0.0) - , m_Pos(a_X, a_Y, a_Z) - , m_Mass (0.001) //Default 1g - , m_bDirtyHead(true) - , m_bDirtyOrientation(true) - , m_bDirtyPosition(true) - , m_bDirtySpeed(true) - , m_bOnGround( false ) - , m_Gravity( -9.81f ) - , m_IsInitialized(false) - , m_LastPosX( 0.0 ) - , m_LastPosY( 0.0 ) - , m_LastPosZ( 0.0 ) - , m_TimeLastTeleportPacket(0) - , m_TimeLastMoveReltPacket(0) - , m_TimeLastSpeedPacket(0) - , m_EntityType(a_EntityType) - , m_World(NULL) - , m_TicksSinceLastBurnDamage(0) - , m_TicksSinceLastLavaDamage(0) - , m_TicksSinceLastFireDamage(0) - , m_TicksSinceLastVoidDamage(0) - , m_TicksLeftBurning(0) - , m_WaterSpeed(0, 0, 0) - , m_Width(a_Width) - , m_Height(a_Height) -{ - cCSLock Lock(m_CSCount); - m_EntityCount++; - m_UniqueID = m_EntityCount; -} - - - - - -cEntity::~cEntity() -{ - ASSERT(!m_World->HasEntity(m_UniqueID)); // Before deleting, the entity needs to have been removed from the world - - LOGD("Deleting entity %d at pos {%.2f, %.2f, %.2f} ~ [%d, %d]; ptr %p", - m_UniqueID, - m_Pos.x, m_Pos.y, m_Pos.z, - (int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width), - this - ); - - if (m_AttachedTo != NULL) - { - Detach(); - } - if (m_Attachee != NULL) - { - m_Attachee->Detach(); - } - - if (m_IsInitialized) - { - LOGWARNING("ERROR: Entity deallocated without being destroyed"); - ASSERT(!"Entity deallocated without being destroyed or unlinked"); - } - delete m_Referencers; - delete m_References; -} - - - - - -const char * cEntity::GetClass(void) const -{ - return "cEntity"; -} - - - - - -const char * cEntity::GetClassStatic(void) -{ - return "cEntity"; -} - - - - - -const char * cEntity::GetParentClass(void) const -{ - return ""; -} - - - - - -bool cEntity::Initialize(cWorld * a_World) -{ - if (cPluginManager::Get()->CallHookSpawningEntity(*a_World, *this)) - { - return false; - } - - LOGD("Initializing entity #%d (%s) at {%.02f, %.02f, %.02f}", - m_UniqueID, GetClass(), m_Pos.x, m_Pos.y, m_Pos.z - ); - m_IsInitialized = true; - m_World = a_World; - m_World->AddEntity(this); - - cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this); - - // Spawn the entity on the clients: - a_World->BroadcastSpawnEntity(*this); - - return true; -} - - - - - -void cEntity::WrapHeadYaw(void) -{ - while (m_HeadYaw > 180.f) m_HeadYaw -= 360.f; // Wrap it - while (m_HeadYaw < -180.f) m_HeadYaw += 360.f; -} - - - - - -void cEntity::WrapRotation(void) -{ - while (m_Rot.x > 180.f) m_Rot.x -= 360.f; // Wrap it - while (m_Rot.x < -180.f) m_Rot.x += 360.f; - while (m_Rot.y > 180.f) m_Rot.y -= 360.f; - while (m_Rot.y < -180.f) m_Rot.y += 360.f; -} - - - - -void cEntity::WrapSpeed(void) -{ - // There shoudn't be a need for flipping the flag on because this function is called - // after any update, so the flag is already turned on - if (m_Speed.x > 78.0f) m_Speed.x = 78.0f; - else if (m_Speed.x < -78.0f) m_Speed.x = -78.0f; - if (m_Speed.y > 78.0f) m_Speed.y = 78.0f; - else if (m_Speed.y < -78.0f) m_Speed.y = -78.0f; - if (m_Speed.z > 78.0f) m_Speed.z = 78.0f; - else if (m_Speed.z < -78.0f) m_Speed.z = -78.0f; -} - - - - - -void cEntity::Destroy(bool a_ShouldBroadcast) -{ - if (!m_IsInitialized) - { - return; - } - - if (a_ShouldBroadcast) - { - m_World->BroadcastDestroyEntity(*this); - } - - m_IsInitialized = false; - - Destroyed(); -} - - - - - -void cEntity::TakeDamage(cEntity & a_Attacker) -{ - int RawDamage = a_Attacker.GetRawDamageAgainst(*this); - - TakeDamage(dtAttack, &a_Attacker, RawDamage, a_Attacker.GetKnockbackAmountAgainst(*this)); -} - - - - - -void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount) -{ - int FinalDamage = a_RawDamage - GetArmorCoverAgainst(a_Attacker, a_DamageType, a_RawDamage); - cEntity::TakeDamage(a_DamageType, a_Attacker, a_RawDamage, FinalDamage, a_KnockbackAmount); -} - - - - - -void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount) -{ - TakeDamageInfo TDI; - TDI.DamageType = a_DamageType; - TDI.Attacker = a_Attacker; - TDI.RawDamage = a_RawDamage; - TDI.FinalDamage = a_FinalDamage; - Vector3d Heading; - Heading.x = sin(GetRotation()); - Heading.y = 0.4; // TODO: adjust the amount of "up" knockback when testing - Heading.z = cos(GetRotation()); - TDI.Knockback = Heading * a_KnockbackAmount; - DoTakeDamage(TDI); -} - - - - - -void cEntity::SetRotationFromSpeed(void) -{ - const double EPS = 0.0000001; - if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS)) - { - // atan2() may overflow or is undefined, pick any number - SetRotation(0); - return; - } - SetRotation(atan2(m_Speed.x, m_Speed.z) * 180 / PI); -} - - - - - -void cEntity::SetPitchFromSpeed(void) -{ - const double EPS = 0.0000001; - double xz = sqrt(m_Speed.x * m_Speed.x + m_Speed.z * m_Speed.z); // Speed XZ-plane component - if ((abs(xz) < EPS) && (abs(m_Speed.y) < EPS)) - { - // atan2() may overflow or is undefined, pick any number - SetPitch(0); - return; - } - SetPitch(atan2(m_Speed.y, xz) * 180 / PI); -} - - - - - -void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI)) - { - return; - } - - if (m_Health <= 0) - { - // Can't take damage if already dead - return; - } - - m_Health -= (short)a_TDI.FinalDamage; - - // TODO: Apply damage to armor - - if (m_Health < 0) - { - m_Health = 0; - } - - m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT); - - if (m_Health <= 0) - { - KilledBy(a_TDI.Attacker); - } -} - - - - - -int cEntity::GetRawDamageAgainst(const cEntity & a_Receiver) -{ - // Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items - // Ref: http://www.minecraftwiki.net/wiki/Damage#Dealing_damage as of 2012_12_20 - switch (this->GetEquippedWeapon().m_ItemType) - { - case E_ITEM_WOODEN_SWORD: return 4; - case E_ITEM_GOLD_SWORD: return 4; - case E_ITEM_STONE_SWORD: return 5; - case E_ITEM_IRON_SWORD: return 6; - case E_ITEM_DIAMOND_SWORD: return 7; - - case E_ITEM_WOODEN_AXE: return 3; - case E_ITEM_GOLD_AXE: return 3; - case E_ITEM_STONE_AXE: return 4; - case E_ITEM_IRON_AXE: return 5; - case E_ITEM_DIAMOND_AXE: return 6; - - case E_ITEM_WOODEN_PICKAXE: return 2; - case E_ITEM_GOLD_PICKAXE: return 2; - case E_ITEM_STONE_PICKAXE: return 3; - case E_ITEM_IRON_PICKAXE: return 4; - case E_ITEM_DIAMOND_PICKAXE: return 5; - - case E_ITEM_WOODEN_SHOVEL: return 1; - case E_ITEM_GOLD_SHOVEL: return 1; - case E_ITEM_STONE_SHOVEL: return 2; - case E_ITEM_IRON_SHOVEL: return 3; - case E_ITEM_DIAMOND_SHOVEL: return 4; - } - // All other equipped items give a damage of 1: - return 1; -} - - - - - -int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage) -{ - // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover - - // Filter out damage types that are not protected by armor: - // Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20 - switch (a_DamageType) - { - case dtOnFire: - case dtSuffocating: - case dtDrowning: // TODO: This one could be a special case - in various MC versions (PC vs XBox) it is and isn't armor-protected - case dtStarving: - case dtInVoid: - case dtPoisoning: - case dtPotionOfHarming: - case dtFalling: - case dtLightning: - { - return 0; - } - } - - // Add up all armor points: - // Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20 - int ArmorValue = 0; - switch (GetEquippedHelmet().m_ItemType) - { - case E_ITEM_LEATHER_CAP: ArmorValue += 1; break; - case E_ITEM_GOLD_HELMET: ArmorValue += 2; break; - case E_ITEM_CHAIN_HELMET: ArmorValue += 2; break; - case E_ITEM_IRON_HELMET: ArmorValue += 2; break; - case E_ITEM_DIAMOND_HELMET: ArmorValue += 3; break; - } - switch (GetEquippedChestplate().m_ItemType) - { - case E_ITEM_LEATHER_TUNIC: ArmorValue += 3; break; - case E_ITEM_GOLD_CHESTPLATE: ArmorValue += 5; break; - case E_ITEM_CHAIN_CHESTPLATE: ArmorValue += 5; break; - case E_ITEM_IRON_CHESTPLATE: ArmorValue += 6; break; - case E_ITEM_DIAMOND_CHESTPLATE: ArmorValue += 8; break; - } - switch (GetEquippedLeggings().m_ItemType) - { - case E_ITEM_LEATHER_PANTS: ArmorValue += 2; break; - case E_ITEM_GOLD_LEGGINGS: ArmorValue += 3; break; - case E_ITEM_CHAIN_LEGGINGS: ArmorValue += 4; break; - case E_ITEM_IRON_LEGGINGS: ArmorValue += 5; break; - case E_ITEM_DIAMOND_LEGGINGS: ArmorValue += 6; break; - } - switch (GetEquippedBoots().m_ItemType) - { - case E_ITEM_LEATHER_BOOTS: ArmorValue += 1; break; - case E_ITEM_GOLD_BOOTS: ArmorValue += 1; break; - case E_ITEM_CHAIN_BOOTS: ArmorValue += 1; break; - case E_ITEM_IRON_BOOTS: ArmorValue += 2; break; - case E_ITEM_DIAMOND_BOOTS: ArmorValue += 3; break; - } - - // TODO: Special armor cases, such as wool, saddles, dog's collar - // Ref.: http://www.minecraftwiki.net/wiki/Armor#Mob_armor as of 2012_12_20 - - // Now ArmorValue is in [0, 20] range, which corresponds to [0, 80%] protection. Calculate the hitpoints from that: - return a_Damage * (ArmorValue * 4) / 100; -} - - - - - -double cEntity::GetKnockbackAmountAgainst(const cEntity & a_Receiver) -{ - // Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit - - // TODO: Enchantments - return 1; -} - - - - - -void cEntity::KilledBy(cEntity * a_Killer) -{ - m_Health = 0; - - cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_Killer); - - if (m_Health > 0) - { - // Plugin wants to 'unkill' the pawn. Abort - return; - } - - // Drop loot: - cItems Drops; - GetDrops(Drops, a_Killer); - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); - - m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD); -} - - - - - -void cEntity::Heal(int a_HitPoints) -{ - m_Health += a_HitPoints; - if (m_Health > m_MaxHealth) - { - m_Health = m_MaxHealth; - } -} - - - - - -void cEntity::SetHealth(int a_Health) -{ - m_Health = std::max(0, std::min(m_MaxHealth, a_Health)); -} - - - - - -void cEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - if (m_AttachedTo != NULL) - { - if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5) - { - SetPosition(m_AttachedTo->GetPosition()); - } - } - else - { - if (a_Chunk.IsValid()) - { - HandlePhysics(a_Dt, a_Chunk); - } - } - if (a_Chunk.IsValid()) - { - TickBurning(a_Chunk); - } - if ((a_Chunk.IsValid()) && (GetPosY() < -46)) - { - TickInVoid(a_Chunk); - } - else { m_TicksSinceLastVoidDamage = 0; } -} - - - - - -void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - // TODO Add collision detection with entities. - a_Dt /= 1000; // Convert from msec to sec - Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ()); - Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ()); - int BlockX = (int) floor(NextPos.x); - int BlockY = (int) floor(NextPos.y); - int BlockZ = (int) floor(NextPos.z); - - if ((BlockY >= cChunkDef::Height) || (BlockY < 0)) - { - // Outside of the world - - cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); - // See if we can commit our changes. If not, we will discard them. - if (NextChunk != NULL) - { - SetSpeed(NextSpeed); - NextPos += (NextSpeed * a_Dt); - SetPosition(NextPos); - } - return; - } - - // Make sure we got the correct chunk and a valid one. No one ever knows... - cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); - if (NextChunk != NULL) - { - int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width); - int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width); - BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ ); - BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; - if (!g_BlockIsSolid[BlockIn]) // Making sure we are not inside a solid block - { - if (m_bOnGround) // check if it's still on the ground - { - if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water. - { - m_bOnGround = false; - } - } - } - else - { - // Push out entity. - BLOCKTYPE GotBlock; - - static const struct - { - int x, y, z; - } gCrossCoords[] = - { - { 1, 0, 0}, - {-1, 0, 0}, - { 0, 0, 1}, - { 0, 0, -1}, - } ; - - bool IsNoAirSurrounding = true; - for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) - { - if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock)) - { - // The pickup is too close to an unloaded chunk, bail out of any physics handling - return; - } - if (!g_BlockIsSolid[GotBlock]) - { - NextPos.x += gCrossCoords[i].x; - NextPos.z += gCrossCoords[i].z; - IsNoAirSurrounding = false; - break; - } - } // for i - gCrossCoords[] - - if (IsNoAirSurrounding) - { - NextPos.y += 0.5; - } - - m_bOnGround = true; - - LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}", - m_UniqueID, GetClass(), BlockX, BlockY, BlockZ - ); - } - - if (!m_bOnGround) - { - float fallspeed; - if (IsBlockWater(BlockIn)) - { - fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water. - } - else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts - { - fallspeed = 0; - m_bOnGround = true; - } - else if (BlockIn == E_BLOCK_COBWEB) - { - NextSpeed.y *= 0.05; // Reduce overall falling speed - fallspeed = 0; // No falling. - } - else - { - // Normal gravity - fallspeed = m_Gravity * a_Dt; - } - NextSpeed.y += fallspeed; - } - else - { - if (IsMinecart()) - { - if (!IsBlockRail(BlockBelow)) - { - // Friction if minecart is off track, otherwise, Minecart.cpp handles this - if (NextSpeed.SqrLength() > 0.0004f) - { - NextSpeed.x *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.x) < 0.05) - { - NextSpeed.x = 0; - } - NextSpeed.z *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.z) < 0.05) - { - NextSpeed.z = 0; - } - } - } - } - else - { - // Friction for non-minecarts - if (NextSpeed.SqrLength() > 0.0004f) - { - NextSpeed.x *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.x) < 0.05) - { - NextSpeed.x = 0; - } - NextSpeed.z *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.z) < 0.05) - { - NextSpeed.z = 0; - } - } - } - } - - // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we - // might have different speed modifiers according to terrain. - if (BlockIn == E_BLOCK_COBWEB) - { - NextSpeed.x *= 0.25; - NextSpeed.z *= 0.25; - } - - //Get water direction - Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ); - - m_WaterSpeed *= 0.9f; //Reduce speed each tick - - switch(WaterDir) - { - case X_PLUS: - m_WaterSpeed.x = 0.2f; - m_bOnGround = false; - break; - case X_MINUS: - m_WaterSpeed.x = -0.2f; - m_bOnGround = false; - break; - case Z_PLUS: - m_WaterSpeed.z = 0.2f; - m_bOnGround = false; - break; - case Z_MINUS: - m_WaterSpeed.z = -0.2f; - m_bOnGround = false; - break; - - default: - break; - } - - if (fabs(m_WaterSpeed.x) < 0.05) - { - m_WaterSpeed.x = 0; - } - - if (fabs(m_WaterSpeed.z) < 0.05) - { - m_WaterSpeed.z = 0; - } - - NextSpeed += m_WaterSpeed; - - if( NextSpeed.SqrLength() > 0.f ) - { - cTracer Tracer( GetWorld() ); - int Ret = Tracer.Trace( NextPos, NextSpeed, 2 ); - if( Ret ) // Oh noez! we hit something - { - // Set to hit position - if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() ) - { - if( Ret == 1 ) - { - if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f; - if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f; - if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f; - - if( Tracer.HitNormal.y > 0 ) // means on ground - { - m_bOnGround = true; - } - } - NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z); - NextPos.x += Tracer.HitNormal.x * 0.3f; - NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot - NextPos.z += Tracer.HitNormal.z * 0.3f; - } - else - { - NextPos += (NextSpeed * a_Dt); - } - } - else - { - // We didn't hit anything, so move =] - NextPos += (NextSpeed * a_Dt); - } - } - BlockX = (int) floor(NextPos.x); - BlockZ = (int) floor(NextPos.z); - NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ); - // See if we can commit our changes. If not, we will discard them. - if (NextChunk != NULL) - { - if (NextPos.x != GetPosX()) SetPosX(NextPos.x); - if (NextPos.y != GetPosY()) SetPosY(NextPos.y); - if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z); - if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x); - if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y); - if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z); - } - } -} - - - - - -void cEntity::TickBurning(cChunk & a_Chunk) -{ - // Remember the current burning state: - bool HasBeenBurning = (m_TicksLeftBurning > 0); - - // Do the burning damage: - if (m_TicksLeftBurning > 0) - { - m_TicksSinceLastBurnDamage++; - if (m_TicksSinceLastBurnDamage >= BURN_TICKS_PER_DAMAGE) - { - TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0); - m_TicksSinceLastBurnDamage = 0; - } - m_TicksLeftBurning--; - } - - // Update the burning times, based on surroundings: - int MinRelX = (int)floor(GetPosX() - m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; - int MaxRelX = (int)floor(GetPosX() + m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; - int MinRelZ = (int)floor(GetPosZ() - m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; - int MaxRelZ = (int)floor(GetPosZ() + m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; - int MinY = std::max(0, std::min(cChunkDef::Height - 1, (int)floor(GetPosY()))); - int MaxY = std::max(0, std::min(cChunkDef::Height - 1, (int)ceil (GetPosY() + m_Height))); - bool HasWater = false; - bool HasLava = false; - bool HasFire = false; - - for (int x = MinRelX; x <= MaxRelX; x++) - { - for (int z = MinRelZ; z <= MaxRelZ; z++) - { - int RelX = x; - int RelZ = z; - cChunk * CurChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelX, RelZ); - if (CurChunk == NULL) - { - continue; - } - for (int y = MinY; y <= MaxY; y++) - { - switch (CurChunk->GetBlock(RelX, y, RelZ)) - { - case E_BLOCK_FIRE: - { - HasFire = true; - break; - } - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - HasLava = true; - break; - } - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_WATER: - { - HasWater = true; - break; - } - } // switch (BlockType) - } // for y - } // for z - } // for x - - if (HasWater) - { - // Extinguish the fire - m_TicksLeftBurning = 0; - } - - if (HasLava) - { - // Burn: - m_TicksLeftBurning = BURN_TICKS; - - // Periodically damage: - m_TicksSinceLastLavaDamage++; - if (m_TicksSinceLastLavaDamage >= LAVA_TICKS_PER_DAMAGE) - { - TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0); - m_TicksSinceLastLavaDamage = 0; - } - } - else - { - m_TicksSinceLastLavaDamage = 0; - } - - if (HasFire) - { - // Burn: - m_TicksLeftBurning = BURN_TICKS; - - // Periodically damage: - m_TicksSinceLastFireDamage++; - if (m_TicksSinceLastFireDamage >= FIRE_TICKS_PER_DAMAGE) - { - TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0); - m_TicksSinceLastFireDamage = 0; - } - } - else - { - m_TicksSinceLastFireDamage = 0; - } - - // If just started / finished burning, notify descendants: - if ((m_TicksLeftBurning > 0) && !HasBeenBurning) - { - OnStartedBurning(); - } - else if ((m_TicksLeftBurning <= 0) && HasBeenBurning) - { - OnFinishedBurning(); - } -} - - - - - -void cEntity::TickInVoid(cChunk & a_Chunk) -{ - if (m_TicksSinceLastVoidDamage == 20) - { - TakeDamage(dtInVoid, NULL, 2, 0); - m_TicksSinceLastVoidDamage = 0; - } - else - { - m_TicksSinceLastVoidDamage++; - } -} - - - - - -/// Called when the entity starts burning -void cEntity::OnStartedBurning(void) -{ - // Broadcast the change: - m_World->BroadcastEntityMetadata(*this); -} - - - - - -/// Called when the entity finishes burning -void cEntity::OnFinishedBurning(void) -{ - // Broadcast the change: - m_World->BroadcastEntityMetadata(*this); -} - - - - - -/// Sets the maximum value for the health -void cEntity::SetMaxHealth(int a_MaxHealth) -{ - m_MaxHealth = a_MaxHealth; - - // Reset health, if too high: - if (m_Health > a_MaxHealth) - { - m_Health = a_MaxHealth; - } -} - - - - - -/// Puts the entity on fire for the specified amount of ticks -void cEntity::StartBurning(int a_TicksLeftBurning) -{ - if (m_TicksLeftBurning > 0) - { - // Already burning, top up the ticks left burning and bail out: - m_TicksLeftBurning = std::max(m_TicksLeftBurning, a_TicksLeftBurning); - return; - } - - m_TicksLeftBurning = a_TicksLeftBurning; - OnStartedBurning(); -} - - - - - -/// Stops the entity from burning, resets all burning timers -void cEntity::StopBurning(void) -{ - bool HasBeenBurning = (m_TicksLeftBurning > 0); - m_TicksLeftBurning = 0; - m_TicksSinceLastBurnDamage = 0; - m_TicksSinceLastFireDamage = 0; - m_TicksSinceLastLavaDamage = 0; - - // Notify if the entity has stopped burning - if (HasBeenBurning) - { - OnFinishedBurning(); - } -} - - - - - -void cEntity::TeleportToEntity(cEntity & a_Entity) -{ - TeleportToCoords(a_Entity.GetPosX(), a_Entity.GetPosY(), a_Entity.GetPosZ()); -} - - - - - -void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) -{ - SetPosition(a_PosX, a_PosY, a_PosZ); - m_World->BroadcastTeleportEntity(*this); -} - - - - - -void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) -{ - //We need to keep updating the clients when there is movement or if there was a change in speed and after 2 ticks - if( (m_Speed.SqrLength() > 0.0004f || m_bDirtySpeed) && (m_World->GetWorldAge() - m_TimeLastSpeedPacket >= 2)) - { - m_World->BroadcastEntityVelocity(*this,a_Exclude); - m_bDirtySpeed = false; - m_TimeLastSpeedPacket = m_World->GetWorldAge(); - } - - //Have to process position related packets this every two ticks - if (m_World->GetWorldAge() % 2 == 0) - { - int DiffX = (int) (floor(GetPosX() * 32.0) - floor(m_LastPosX * 32.0)); - int DiffY = (int) (floor(GetPosY() * 32.0) - floor(m_LastPosY * 32.0)); - int DiffZ = (int) (floor(GetPosZ() * 32.0) - floor(m_LastPosZ * 32.0)); - Int64 DiffTeleportPacket = m_World->GetWorldAge() - m_TimeLastTeleportPacket; - // 4 blocks is max Relative So if the Diff is greater than 127 or. Send an absolute position every 20 seconds - if (DiffTeleportPacket >= 400 || - ((DiffX > 127) || (DiffX < -128) || - (DiffY > 127) || (DiffY < -128) || - (DiffZ > 127) || (DiffZ < -128))) - { - // - m_World->BroadcastTeleportEntity(*this,a_Exclude); - m_TimeLastTeleportPacket = m_World->GetWorldAge(); - m_TimeLastMoveReltPacket = m_TimeLastTeleportPacket; //Must synchronize. - m_LastPosX = GetPosX(); - m_LastPosY = GetPosY(); - m_LastPosZ = GetPosZ(); - m_bDirtyPosition = false; - m_bDirtyOrientation = false; - } - else - { - Int64 DiffMoveRelPacket = m_World->GetWorldAge() - m_TimeLastMoveReltPacket; - //if the change is big enough. - if ((abs(DiffX) >= 4 || abs(DiffY) >= 4 || abs(DiffZ) >= 4 || DiffMoveRelPacket >= 60) && m_bDirtyPosition) - { - if (m_bDirtyOrientation) - { - m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); - m_bDirtyOrientation = false; - } - else - { - m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); - } - m_LastPosX = GetPosX(); - m_LastPosY = GetPosY(); - m_LastPosZ = GetPosZ(); - m_bDirtyPosition = false; - m_TimeLastMoveReltPacket = m_World->GetWorldAge(); - } - else - { - if (m_bDirtyOrientation) - { - m_World->BroadcastEntityLook(*this,a_Exclude); - m_bDirtyOrientation = false; - } - } - } - if (m_bDirtyHead) - { - m_World->BroadcastEntityHeadLook(*this,a_Exclude); - m_bDirtyHead = false; - } - } -} - - - - - -void cEntity::AttachTo(cEntity * a_AttachTo) -{ - if (m_AttachedTo == a_AttachTo) - { - // Already attached to that entity, nothing to do here - return; - } - - // Detach from any previous entity: - Detach(); - - // Attach to the new entity: - m_AttachedTo = a_AttachTo; - a_AttachTo->m_Attachee = this; - m_World->BroadcastAttachEntity(*this, a_AttachTo); -} - - - - - -void cEntity::Detach(void) -{ - if (m_AttachedTo == NULL) - { - // Attached to no entity, our work is done - return; - } - m_AttachedTo->m_Attachee = NULL; - m_AttachedTo = NULL; - m_World->BroadcastAttachEntity(*this, NULL); -} - - - - - -bool cEntity::IsA(const char * a_ClassName) const -{ - return (strcmp(a_ClassName, "cEntity") == 0); -} - - - - - -void cEntity::SetRot(const Vector3f & a_Rot) -{ - m_Rot = a_Rot; - m_bDirtyOrientation = true; -} - - - - - -void cEntity::SetHeadYaw(double a_HeadYaw) -{ - m_HeadYaw = a_HeadYaw; - m_bDirtyHead = true; - WrapHeadYaw(); -} - - - - - -void cEntity::SetHeight(double a_Height) -{ - m_Height = a_Height; -} - - - - - -void cEntity::SetMass(double a_Mass) -{ - if (a_Mass > 0) - { - m_Mass = a_Mass; - } - else - { - // Make sure that mass is not zero. 1g is the default because we - // have to choose a number. It's perfectly legal to have a mass - // less than 1g as long as is NOT equal or less than zero. - m_Mass = 0.001; - } -} - - - - - -void cEntity::SetYaw(double a_Yaw) -{ - m_Rot.x = a_Yaw; - m_bDirtyOrientation = true; - WrapRotation(); -} - - - - - -void cEntity::SetPitch(double a_Pitch) -{ - m_Rot.y = a_Pitch; - m_bDirtyOrientation = true; - WrapRotation(); -} - - - - - -void cEntity::SetRoll(double a_Roll) -{ - m_Rot.z = a_Roll; - m_bDirtyOrientation = true; -} - - - - - -void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) -{ - m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ); - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - -void cEntity::SetSpeedX(double a_SpeedX) -{ - m_Speed.x = a_SpeedX; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - -void cEntity::SetSpeedY(double a_SpeedY) -{ - m_Speed.y = a_SpeedY; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - -void cEntity::SetSpeedZ(double a_SpeedZ) -{ - m_Speed.z = a_SpeedZ; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::SetWidth(double a_Width) -{ - m_Width = a_Width; -} - - - - - -void cEntity::AddPosX(double a_AddPosX) -{ - m_Pos.x += a_AddPosX; - m_bDirtyPosition = true; -} - - - - -void cEntity::AddPosY(double a_AddPosY) -{ - m_Pos.y += a_AddPosY; - m_bDirtyPosition = true; -} - - - - -void cEntity::AddPosZ(double a_AddPosZ) -{ - m_Pos.z += a_AddPosZ; - m_bDirtyPosition = true; -} - - - - -void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ) -{ - m_Pos.x += a_AddPosX; - m_Pos.y += a_AddPosY; - m_Pos.z += a_AddPosZ; - m_bDirtyPosition = true; -} - - - - -void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ) -{ - m_Speed.x += a_AddSpeedX; - m_Speed.y += a_AddSpeedY; - m_Speed.z += a_AddSpeedZ; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::AddSpeedX(double a_AddSpeedX) -{ - m_Speed.x += a_AddSpeedX; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::AddSpeedY(double a_AddSpeedY) -{ - m_Speed.y += a_AddSpeedY; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::AddSpeedZ(double a_AddSpeedZ) -{ - m_Speed.z += a_AddSpeedZ; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::SteerVehicle(float a_Forward, float a_Sideways) -{ - if (m_AttachedTo == NULL) - { - return; - } - if ((a_Forward != 0) || (a_Sideways != 0)) - { - Vector3d LookVector = GetLookVector(); - double AddSpeedX = LookVector.x * a_Forward + LookVector.z * a_Sideways; - double AddSpeedZ = LookVector.z * a_Forward - LookVector.x * a_Sideways; - m_AttachedTo->AddSpeed(AddSpeedX, 0, AddSpeedZ); - } -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Get look vector (this is NOT a rotation!) -Vector3d cEntity::GetLookVector(void) const -{ - Matrix4d m; - m.Init(Vector3f(), 0, m_Rot.x, -m_Rot.y); - Vector3d Look = m.Transform(Vector3d(0, 0, 1)); - return Look; -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Set position -void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ) -{ - m_Pos.Set(a_PosX, a_PosY, a_PosZ); - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosX(double a_PosX) -{ - m_Pos.x = a_PosX; - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosY(double a_PosY) -{ - m_Pos.y = a_PosY; - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosZ(double a_PosZ) -{ - m_Pos.z = a_PosZ; - m_bDirtyPosition = true; -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Reference stuffs -void cEntity::AddReference(cEntity * & a_EntityPtr) -{ - m_References->AddReference(a_EntityPtr); - a_EntityPtr->ReferencedBy(a_EntityPtr); -} - - - - - -void cEntity::ReferencedBy(cEntity * & a_EntityPtr) -{ - m_Referencers->AddReference(a_EntityPtr); -} - - - - - -void cEntity::Dereference(cEntity * & a_EntityPtr) -{ - m_Referencers->Dereference(a_EntityPtr); -} - - - - diff --git a/source/Entities/Entity.h b/source/Entities/Entity.h deleted file mode 100644 index dafda7826..000000000 --- a/source/Entities/Entity.h +++ /dev/null @@ -1,445 +0,0 @@ - -#pragma once - -#include "../Item.h" -#include "../Vector3d.h" -#include "../Vector3f.h" - - - - - -// Place this macro in the public section of each cEntity descendant class and you're done :) -#define CLASS_PROTODEF(classname) \ - virtual bool IsA(const char * a_ClassName) const override\ - { \ - return ((strcmp(a_ClassName, #classname) == 0) || super::IsA(a_ClassName)); \ - } \ - virtual const char * GetClass(void) const override \ - { \ - return #classname; \ - } \ - static const char * GetClassStatic(void) \ - { \ - return #classname; \ - } \ - virtual const char * GetParentClass(void) const override \ - { \ - return super::GetClass(); \ - } - - - - - -class cWorld; -class cReferenceManager; -class cClientHandle; -class cPlayer; -class cChunk; - - - - - -// tolua_begin -struct TakeDamageInfo -{ - eDamageType DamageType; // Where does the damage come from? Being hit / on fire / contact with cactus / ... - cEntity * Attacker; // The attacking entity; valid only for dtAttack - int RawDamage; // What damage would the receiver get without any armor. Usually: attacker mob type + weapons - int FinalDamage; // What actual damage will be received. Usually: m_RawDamage minus armor - Vector3d Knockback; // The amount and direction of knockback received from the damage - // TODO: Effects - list of effects that the hit is causing. Unknown representation yet -} ; -// tolua_end - - - - - -// tolua_begin -class cEntity -{ -public: - - enum eEntityType - { - etEntity, // For all other types - etPlayer, - etPickup, - etMonster, - etFallingBlock, - etMinecart, - etBoat, - etTNT, - etProjectile, - - // Common variations - etMob = etMonster, // DEPRECATED, use etMonster instead! - } ; - - // tolua_end - - enum - { - ENTITY_STATUS_HURT = 2, - ENTITY_STATUS_DEAD = 3, - ENTITY_STATUS_WOLF_TAMING = 6, - ENTITY_STATUS_WOLF_TAMED = 7, - ENTITY_STATUS_WOLF_SHAKING = 8, - ENTITY_STATUS_EATING_ACCEPTED = 9, - ENTITY_STATUS_SHEEP_EATING = 10, - ENTITY_STATUS_GOLEM_ROSING = 11, - ENTITY_STATUS_VILLAGER_HEARTS = 12, - ENTITY_STATUS_VILLAGER_ANGRY = 13, - ENTITY_STATUS_VILLAGER_HAPPY = 14, - ENTITY_STATUS_WITCH_MAGICKING = 15, - // It seems 16 (zombie conversion) is now done with metadata - ENTITY_STATUS_FIREWORK_EXPLODE= 17, - } ; - - enum - { - FIRE_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in fire - FIRE_DAMAGE = 1, ///< How much damage to deal when standing in fire - LAVA_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in lava - LAVA_DAMAGE = 5, ///< How much damage to deal when standing in lava - BURN_TICKS_PER_DAMAGE = 20, ///< How many ticks to wait between damaging an entity when it is burning - BURN_DAMAGE = 1, ///< How much damage to deal when the entity is burning - BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire - } ; - - cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); - virtual ~cEntity(); - - /// Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed) - virtual bool Initialize(cWorld * a_World); - - // tolua_begin - - eEntityType GetEntityType(void) const { return m_EntityType; } - - bool IsPlayer (void) const { return (m_EntityType == etPlayer); } - bool IsPickup (void) const { return (m_EntityType == etPickup); } - bool IsMob (void) const { return (m_EntityType == etMonster); } - bool IsFallingBlock(void) const { return (m_EntityType == etFallingBlock); } - bool IsMinecart (void) const { return (m_EntityType == etMinecart); } - bool IsBoat (void) const { return (m_EntityType == etBoat); } - bool IsTNT (void) const { return (m_EntityType == etTNT); } - bool IsProjectile (void) const { return (m_EntityType == etProjectile); } - - /// Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns true) - virtual bool IsA(const char * a_ClassName) const; - - /// Returns the topmost class name for the object - virtual const char * GetClass(void) const; - - // Returns the class name of this class - static const char * GetClassStatic(void); - - /// Returns the topmost class's parent class name for the object. cEntity returns an empty string (no parent). - virtual const char * GetParentClass(void) const; - - cWorld * GetWorld(void) const { return m_World; } - - double GetHeadYaw (void) const { return m_HeadYaw; } - double GetHeight (void) const { return m_Height; } - double GetMass (void) const { return m_Mass; } - const Vector3d & GetPosition (void) const { return m_Pos; } - double GetPosX (void) const { return m_Pos.x; } - double GetPosY (void) const { return m_Pos.y; } - double GetPosZ (void) const { return m_Pos.z; } - const Vector3d & GetRot (void) const { return m_Rot; } - double GetRotation (void) const { return m_Rot.x; } // OBSOLETE, use GetYaw() instead - double GetYaw (void) const { return m_Rot.x; } - double GetPitch (void) const { return m_Rot.y; } - double GetRoll (void) const { return m_Rot.z; } - Vector3d GetLookVector(void) const; - const Vector3d & GetSpeed (void) const { return m_Speed; } - double GetSpeedX (void) const { return m_Speed.x; } - double GetSpeedY (void) const { return m_Speed.y; } - double GetSpeedZ (void) const { return m_Speed.z; } - double GetWidth (void) const { return m_Width; } - - int GetChunkX(void) const {return (int)floor(m_Pos.x / cChunkDef::Width); } - int GetChunkZ(void) const {return (int)floor(m_Pos.z / cChunkDef::Width); } - - void SetHeadYaw (double a_HeadYaw); - void SetHeight (double a_Height); - void SetMass (double a_Mass); - void SetPosX (double a_PosX); - void SetPosY (double a_PosY); - void SetPosZ (double a_PosZ); - void SetPosition(double a_PosX, double a_PosY, double a_PosZ); - void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); } - void SetRot (const Vector3f & a_Rot); - void SetRotation(double a_Rotation) { SetYaw(a_Rotation); } // OBSOLETE, use SetYaw() instead - void SetYaw (double a_Yaw); - void SetPitch (double a_Pitch); - void SetRoll (double a_Roll); - void SetSpeed (double a_SpeedX, double a_SpeedY, double a_SpeedZ); - void SetSpeed (const Vector3d & a_Speed) { SetSpeed(a_Speed.x, a_Speed.y, a_Speed.z); } - void SetSpeedX (double a_SpeedX); - void SetSpeedY (double a_SpeedY); - void SetSpeedZ (double a_SpeedZ); - void SetWidth (double a_Width); - - void AddPosX (double a_AddPosX); - void AddPosY (double a_AddPosY); - void AddPosZ (double a_AddPosZ); - void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ); - void AddPosition(const Vector3d & a_AddPos) { AddPosition(a_AddPos.x,a_AddPos.y,a_AddPos.z);} - void AddSpeed (double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ); - void AddSpeed (const Vector3d & a_AddSpeed) { AddSpeed(a_AddSpeed.x,a_AddSpeed.y,a_AddSpeed.z);} - void AddSpeedX (double a_AddSpeedX); - void AddSpeedY (double a_AddSpeedY); - void AddSpeedZ (double a_AddSpeedZ); - - void SteerVehicle(float a_Forward, float a_Sideways); - - inline int GetUniqueID(void) const { return m_UniqueID; } - inline bool IsDestroyed(void) const { return !m_IsInitialized; } - - /// Schedules the entity for destroying; if a_ShouldBroadcast is set to true, broadcasts the DestroyEntity packet - void Destroy(bool a_ShouldBroadcast = true); - - /// Makes this pawn take damage from an attack by a_Attacker. Damage values are calculated automatically and DoTakeDamage() called - void TakeDamage(cEntity & a_Attacker); - - /// Makes this entity take the specified damage. The final damage is calculated using current armor, then DoTakeDamage() called - void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount); - - /// Makes this entity take the specified damage. The values are packed into a TDI, knockback calculated, then sent through DoTakeDamage() - void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount); - - float GetGravity(void) const { return m_Gravity; } - - void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; } - - /// Sets the rotation to match the speed vector (entity goes "face-forward") - void SetRotationFromSpeed(void); - - /// Sets the pitch to match the speed vector (entity gies "face-forward") - void SetPitchFromSpeed(void); - - // tolua_end - - /// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied - virtual void DoTakeDamage(TakeDamageInfo & a_TDI); - - // tolua_begin - - /// Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items - virtual int GetRawDamageAgainst(const cEntity & a_Receiver); - - /// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover - virtual int GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_RawDamage); - - /// Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit - virtual double GetKnockbackAmountAgainst(const cEntity & a_Receiver); - - /// Returns the curently equipped weapon; empty item if none - virtual cItem GetEquippedWeapon(void) const { return cItem(); } - - /// Returns the currently equipped helmet; empty item if none - virtual cItem GetEquippedHelmet(void) const { return cItem(); } - - /// Returns the currently equipped chestplate; empty item if none - virtual cItem GetEquippedChestplate(void) const { return cItem(); } - - /// Returns the currently equipped leggings; empty item if none - virtual cItem GetEquippedLeggings(void) const { return cItem(); } - - /// Returns the currently equipped boots; empty item if none - virtual cItem GetEquippedBoots(void) const { return cItem(); } - - /// Called when the health drops below zero. a_Killer may be NULL (environmental damage) - virtual void KilledBy(cEntity * a_Killer); - - /// Heals the specified amount of HPs - void Heal(int a_HitPoints); - - /// Returns the health of this entity - int GetHealth(void) const { return m_Health; } - - /// Sets the health of this entity; doesn't broadcast any hurt animation - void SetHealth(int a_Health); - - // tolua_end - - virtual void Tick(float a_Dt, cChunk & a_Chunk); - - /// Handles the physics of the entity - updates position based on speed, updates speed based on environment - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk); - - /// Updates the state related to this entity being on fire - virtual void TickBurning(cChunk & a_Chunk); - - /// Handles when the entity is in the void - virtual void TickInVoid(cChunk & a_Chunk); - - /// Called when the entity starts burning - virtual void OnStartedBurning(void); - - /// Called when the entity finishes burning - virtual void OnFinishedBurning(void); - - // tolua_begin - - /// Sets the maximum value for the health - void SetMaxHealth(int a_MaxHealth); - - int GetMaxHealth(void) const { return m_MaxHealth; } - - /// Puts the entity on fire for the specified amount of ticks - void StartBurning(int a_TicksLeftBurning); - - /// Stops the entity from burning, resets all burning timers - void StopBurning(void); - - // tolua_end - - /** Descendants override this function to send a command to the specified client to spawn the entity on the client. - To spawn on all eligible clients, use cChunkMap::BroadcastSpawnEntity() - */ - virtual void SpawnOn(cClientHandle & a_Client) = 0; - - // tolua_begin - - /// Teleports to the entity specified - virtual void TeleportToEntity(cEntity & a_Entity); - - /// Teleports to the coordinates specified - virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ); - - // tolua_end - - /// Updates clients of changes in the entity. - virtual void BroadcastMovementUpdate(const cClientHandle * a_Exclude = NULL); - - /// Attaches to the specified entity; detaches from any previous one first - void AttachTo(cEntity * a_AttachTo); - - /// Detaches from the currently attached entity, if any - void Detach(void); - - /// Makes sure head yaw is not over the specified range. - void WrapHeadYaw(); - - /// Makes sure rotation is not over the specified range. - void WrapRotation(); - - /// Makes speed is not over 20. Max speed is 20 blocks / second - void WrapSpeed(); - - // tolua_begin - - // COMMON metadata flags; descendants may override the defaults: - virtual bool IsOnFire (void) const {return (m_TicksLeftBurning > 0); } - virtual bool IsCrouched (void) const {return false; } - virtual bool IsRiding (void) const {return false; } - virtual bool IsSprinting(void) const {return false; } - virtual bool IsRclking (void) const {return false; } - virtual bool IsInvisible(void) const {return false; } - - // tolua_end - - /// Called when the specified player right-clicks this entity - virtual void OnRightClicked(cPlayer & a_Player) {}; - - /// Returns the list of drops for this pawn when it is killed. May check a_Killer for special handling (sword of looting etc.). Called from KilledBy(). - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) {} - -protected: - static cCriticalSection m_CSCount; - static int m_EntityCount; - - int m_UniqueID; - - int m_Health; - int m_MaxHealth; - - /// The entity to which this entity is attached (vehicle), NULL if none - cEntity * m_AttachedTo; - - /// The entity which is attached to this entity (rider), NULL if none - cEntity * m_Attachee; - - cReferenceManager* m_Referencers; - cReferenceManager* m_References; - - // Flags that signal that we haven't updated the clients with the latest. - bool m_bDirtyHead; - bool m_bDirtyOrientation; - bool m_bDirtyPosition; - bool m_bDirtySpeed; - - bool m_bOnGround; - float m_Gravity; - - // Last Position. - double m_LastPosX, m_LastPosY, m_LastPosZ; - - // This variables keep track of the last time a packet was sent - Int64 m_TimeLastTeleportPacket,m_TimeLastMoveReltPacket,m_TimeLastSpeedPacket; // In ticks - - bool m_IsInitialized; // Is set to true when it's initialized, until it's destroyed (Initialize() till Destroy() ) - - eEntityType m_EntityType; - - cWorld * m_World; - - /// Time, in ticks, since the last damage dealt by being on fire. Valid only if on fire (IsOnFire()) - int m_TicksSinceLastBurnDamage; - - /// Time, in ticks, since the last damage dealt by standing in lava. Reset to zero when moving out of lava. - int m_TicksSinceLastLavaDamage; - - /// Time, in ticks, since the last damage dealt by standing in fire. Reset to zero when moving out of fire. - int m_TicksSinceLastFireDamage; - - /// Time, in ticks, until the entity extinguishes its fire - int m_TicksLeftBurning; - - /// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void. - int m_TicksSinceLastVoidDamage; - - virtual void Destroyed(void) {} // Called after the entity has been destroyed - - void SetWorld(cWorld * a_World) { m_World = a_World; } - - friend class cReferenceManager; - void AddReference( cEntity*& a_EntityPtr ); - void ReferencedBy( cEntity*& a_EntityPtr ); - void Dereference( cEntity*& a_EntityPtr ); - -private: - // Measured in degrees (MAX 360°) - double m_HeadYaw; - // Measured in meter/second (m/s) - Vector3d m_Speed; - // Measured in degrees (MAX 360°) - Vector3d m_Rot; - - /// Position of the entity's XZ center and Y bottom - Vector3d m_Pos; - - // Measured in meter / second - Vector3d m_WaterSpeed; - - // Measured in Kilograms (Kg) - double m_Mass; - - /// Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter. - double m_Width; - - /// Height of the entity (Y axis) - double m_Height; -} ; // tolua_export - -typedef std::list cEntityList; - - - - diff --git a/source/Entities/FallingBlock.cpp b/source/Entities/FallingBlock.cpp deleted file mode 100644 index 9fcd9ac80..000000000 --- a/source/Entities/FallingBlock.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "Globals.h" - -#include "FallingBlock.h" -#include "../World.h" -#include "../ClientHandle.h" -#include "../Simulator/SandSimulator.h" -#include "../Chunk.h" - - - - - -cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : - super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f, 0.98, 0.98), - m_BlockType(a_BlockType), - m_BlockMeta(a_BlockMeta), - m_OriginalPosition(a_BlockPosition) -{ -} - - - - - -void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle) -{ - a_ClientHandle.SendSpawnFallingBlock(*this); -} - - - - - -void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) -{ - float MilliDt = a_Dt * 0.001f; - AddSpeedY(MilliDt * -9.8f); - AddPosY(GetSpeedY() * MilliDt); - - // GetWorld()->BroadcastTeleportEntity(*this); // Test position - - int BlockX = m_OriginalPosition.x; - int BlockY = (int)(GetPosY() - 0.5); - int BlockZ = m_OriginalPosition.z; - - if (BlockY < 0) - { - // Fallen out of this world, just continue falling until out of sight, then destroy: - if (BlockY < 100) - { - Destroy(true); - } - return; - } - - if (BlockY >= cChunkDef::Height) - { - // Above the world, just wait for it to fall back down - return; - } - - int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width); - BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx); - NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx); - if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta)) - { - // Fallen onto a block that breaks this into pickups (e. g. half-slab) - // Must finish the fall with coords one below the block: - cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta); - Destroy(true); - return; - } - else if (!cSandSimulator::CanContinueFallThrough(BlockBelow)) - { - // Fallen onto a solid block - /* - LOGD( - "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.", - BlockX, BlockY, BlockZ, - BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width, - ItemTypeToString(BlockBelow).c_str() - ); - */ - - cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta); - Destroy(true); - return; - } -} - - - - diff --git a/source/Entities/FallingBlock.h b/source/Entities/FallingBlock.h deleted file mode 100644 index 5ba9909bb..000000000 --- a/source/Entities/FallingBlock.h +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once - -#include "Entity.h" - - - - -class cPlayer; -class cItem; - - - - - - -class cFallingBlock : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cFallingBlock); - - /// Creates a new falling block. a_BlockPosition is expected in world coords - cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - BLOCKTYPE GetBlockType(void) const { return m_BlockType; } - NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; } - - // cEntity overrides: - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - -private: - BLOCKTYPE m_BlockType; - NIBBLETYPE m_BlockMeta; - Vector3i m_OriginalPosition; // Position where the falling block has started, in world coords -} ; - - - - diff --git a/source/Entities/Minecart.cpp b/source/Entities/Minecart.cpp deleted file mode 100644 index f75e23d8b..000000000 --- a/source/Entities/Minecart.cpp +++ /dev/null @@ -1,541 +0,0 @@ - -// Minecart.cpp - -// Implements the cMinecart class representing a minecart in the world -// Indiana Jones! - -#include "Globals.h" -#include "Minecart.h" -#include "../World.h" -#include "../ClientHandle.h" -#include "../Chunk.h" -#include "Player.h" - - - - - -cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : - super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7), - m_Payload(a_Payload), - m_LastDamage(0) -{ - SetMass(20.f); - SetMaxHealth(6); - SetHealth(6); -} - - - - -void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) -{ - char SubType = 0; - switch (m_Payload) - { - case mpNone: SubType = 0; break; - case mpChest: SubType = 1; break; - case mpFurnace: SubType = 2; break; - case mpTNT: SubType = 3; break; - case mpHopper: SubType = 5; break; - default: - { - ASSERT(!"Unknown payload, cannot spawn on client"); - return; - } - } - a_ClientHandle.SendSpawnVehicle(*this, 10, SubType); // 10 = Minecarts, SubType = What type of Minecart -} - - - - - -void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - int PosY = (int)floor(GetPosY()); - if ((PosY <= 0) || (PosY >= cChunkDef::Height)) - { - // Outside the world, just process normal falling physics - super::HandlePhysics(a_Dt, a_Chunk); - BroadcastMovementUpdate(); - return; - } - - int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); - if (Chunk == NULL) - { - // Inside an unloaded chunk, bail out all processing - return; - } - BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ); - BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ); - - if (IsBlockRail(BelowType)) - { - HandleRailPhysics(a_Dt, *Chunk); - } - else - { - if (IsBlockRail(InsideType)) - { - SetPosY(PosY + 1); - HandleRailPhysics(a_Dt, *Chunk); - } - else - { - super::HandlePhysics(a_Dt, *Chunk); - BroadcastMovementUpdate(); - } - } -} - - - - - -static const double MAX_SPEED = 8; -static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED); - -void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) -{ - - super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling - - /* - NOTE: Please bear in mind that taking away from negatives make them even more negative, - adding to negatives make them positive, etc. - */ - - // Get block meta below the cart - int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; - NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ); - double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed - - switch (BelowMeta) - { - case E_META_RAIL_ZM_ZP: // NORTHSOUTH - { - SetRotation(270); - SpeedY = 0; // Don't move vertically as on ground - SpeedX = 0; // Correct diagonal movement from curved rails - - if (SpeedZ != 0) // Don't do anything if cart is stationary - { - if (SpeedZ > 0) - { - // Going SOUTH, slow down - SpeedZ = SpeedZ - 0.1; - } - else - { - // Going NORTH, slow down - SpeedZ = SpeedZ + 0.1; - } - } - break; - } - - case E_META_RAIL_XM_XP: // EASTWEST - { - SetRotation(180); - SpeedY = 0; - SpeedZ = 0; - - if (SpeedX != 0) - { - if (SpeedX > 0) - { - SpeedX = SpeedX - 0.1; - } - else - { - SpeedX = SpeedX + 0.1; - } - } - break; - } - - case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH - { - SetRotation(270); - SetPosY(floor(GetPosY()) + 0.2); // It seems it doesn't work without levitation :/ - SpeedX = 0; - - if (SpeedZ >= 0) - { - // SpeedZ POSITIVE, going SOUTH - if (SpeedZ <= MAX_SPEED) // Speed limit - { - SpeedZ = SpeedZ + 0.5; // Speed up - SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative) - } - else - { - SpeedZ = MAX_SPEED; // Enforce speed limit - SpeedY = (0 - SpeedZ); - } - } - else - { - // SpeedZ NEGATIVE, going NORTH - SpeedZ = SpeedZ + 0.4; // Slow down - SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number) - } - break; - } - - case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH - { - SetRotation(270); - SetPosY(floor(GetPosY()) + 0.2); - SpeedX = 0; - - if (SpeedZ > 0) - { - // SpeedZ POSITIVE, going SOUTH - SpeedZ = SpeedZ - 0.4; // Slow down - SpeedY = SpeedZ; // Upward movement positive - } - else - { - if (SpeedZ >= MAX_SPEED_NEGATIVE) // Speed limit - { - // SpeedZ NEGATIVE, going NORTH - SpeedZ = SpeedZ - 0.5; // Speed up - SpeedY = SpeedZ; // Downward movement negative - } - else - { - SpeedZ = MAX_SPEED_NEGATIVE; // Enforce speed limit - SpeedY = SpeedZ; - } - } - break; - } - - case E_META_RAIL_ASCEND_XM: // ASCEND EAST - { - SetRotation(180); - SetPosY(floor(GetPosY()) + 0.2); - SpeedZ = 0; - - if (SpeedX >= 0) - { - if (SpeedX <= MAX_SPEED) - { - SpeedX = SpeedX + 0.5; - SpeedY = (0 - SpeedX); - } - else - { - SpeedX = MAX_SPEED; - SpeedY = (0 - SpeedX); - } - } - else - { - SpeedX = SpeedX + 0.4; - SpeedY = (0 - SpeedX); - } - break; - } - - case E_META_RAIL_ASCEND_XP: // ASCEND WEST - { - SetRotation(180); - SetPosY(floor(GetPosY()) + 0.2); - SpeedZ = 0; - - if (SpeedX > 0) - { - SpeedX = SpeedX - 0.4; - SpeedY = SpeedX; - } - else - { - if (SpeedX >= MAX_SPEED_NEGATIVE) - { - SpeedX = SpeedX - 0.5; - SpeedY = SpeedX; - } - else - { - SpeedX = MAX_SPEED_NEGATIVE; - SpeedY = SpeedX; - } - } - break; - } - - case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST - { - SetRotation(315); // Set correct rotation server side - SetPosY(floor(GetPosY()) + 0.2); // Levitate dat cart - - if (SpeedZ > 0) // Cart moving south - { - SpeedX = (0 - SpeedZ); // Diagonally move southwest (which will make cart hit a southwest rail) - } - else if (SpeedX > 0) // Cart moving east - { - SpeedZ = (0 - SpeedX); // Diagonally move northeast - } - break; - } - - case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST - { - SetRotation(225); - SetPosY(floor(GetPosY()) + 0.2); - - if (SpeedZ > 0) - { - SpeedX = SpeedZ; - } - else if (SpeedX < 0) - { - SpeedZ = SpeedX; - } - break; - } - - case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST - { - SetRotation(135); - SetPosY(floor(GetPosY()) + 0.2); - - if (SpeedZ < 0) - { - SpeedX = SpeedZ; - } - else if (SpeedX > 0) - { - SpeedZ = SpeedX; - } - break; - } - - case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST - { - SetRotation(45); - SetPosY(floor(GetPosY()) + 0.2); - - if (SpeedZ < 0) - { - SpeedX = (0 - SpeedZ); - } - else if (SpeedX < 0) - { - SpeedZ = (0 - SpeedX); - } - break; - } - - default: - { - ASSERT(!"Unhandled rail meta!"); // Dun dun DUN! - break; - } - } - - // Set speed to speed variables - SetSpeedX(SpeedX); - SetSpeedY(SpeedY); - SetSpeedZ(SpeedZ); - - - // Broadcast position to client - BroadcastMovementUpdate(); -} - - - - - -void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) -{ - m_LastDamage = TDI.FinalDamage; - super::DoTakeDamage(TDI); - - m_World->BroadcastEntityMetadata(*this); - - if (GetHealth() <= 0) - { - Destroy(true); - - cItems Drops; - switch (m_Payload) - { - case mpNone: - { - Drops.push_back(cItem(E_ITEM_MINECART, 1, 0)); - break; - } - case mpChest: - { - Drops.push_back(cItem(E_ITEM_CHEST_MINECART, 1, 0)); - break; - } - case mpFurnace: - { - Drops.push_back(cItem(E_ITEM_FURNACE_MINECART, 1, 0)); - break; - } - case mpTNT: - { - Drops.push_back(cItem(E_ITEM_MINECART_WITH_TNT, 1, 0)); - break; - } - case mpHopper: - { - Drops.push_back(cItem(E_ITEM_MINECART_WITH_HOPPER, 1, 0)); - break; - } - default: - { - ASSERT(!"Unhandled minecart type when spawning pickup!"); - return; - } - } - - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cEmptyMinecart: - -cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) : - super(mpNone, a_X, a_Y, a_Z) -{ -} - - - - - -void cEmptyMinecart::OnRightClicked(cPlayer & a_Player) -{ - if (m_Attachee != NULL) - { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) - { - // This player is already sitting in, they want out. - a_Player.Detach(); - return; - } - - if (m_Attachee->IsPlayer()) - { - // Another player is already sitting in here, cannot attach - return; - } - - // Detach whatever is sitting in this minecart now: - m_Attachee->Detach(); - } - - // Attach the player to this minecart - a_Player.AttachTo(this); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithChest: - -cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) : - super(mpChest, a_X, a_Y, a_Z) -{ -} - - - - - -void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item) -{ - ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items))); - - m_Items[a_Idx] = a_Item; -} - - - - - -void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) -{ - // Show the chest UI window to the player - // TODO -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithFurnace: - -cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : - super(mpFurnace, a_X, a_Y, a_Z), - m_IsFueled(false) -{ -} - - - - - -void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) -{ - if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_COAL) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - - m_IsFueled = true; - m_World->BroadcastEntityMetadata(*this); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithTNT: - -cMinecartWithTNT::cMinecartWithTNT(double a_X, double a_Y, double a_Z) : - super(mpTNT, a_X, a_Y, a_Z) -{ -} - -// TODO: Make it activate when passing over activator rail - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithHopper: - -cMinecartWithHopper::cMinecartWithHopper(double a_X, double a_Y, double a_Z) : - super(mpHopper, a_X, a_Y, a_Z) -{ -} - -// TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks -// AND AVARYTHING!! \ No newline at end of file diff --git a/source/Entities/Minecart.h b/source/Entities/Minecart.h deleted file mode 100644 index b1b48be4e..000000000 --- a/source/Entities/Minecart.h +++ /dev/null @@ -1,169 +0,0 @@ - -// Minecart.h - -// Declares the cMinecart class representing a minecart in the world - - - - - -#pragma once - -#include "Entity.h" - - - - - -inline bool IsBlockRail(BLOCKTYPE a_BlockType) - { - return ( - (a_BlockType == E_BLOCK_RAIL) || - (a_BlockType == E_BLOCK_ACTIVATOR_RAIL) || - (a_BlockType == E_BLOCK_DETECTOR_RAIL) || - (a_BlockType == E_BLOCK_POWERED_RAIL) - ) ; - } - - - - - -class cMinecart : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cMinecart); - - enum ePayload - { - mpNone, // Empty minecart, ridable by player or mobs - mpChest, // Minecart-with-chest, can store a grid of 3*8 items - mpFurnace, // Minecart-with-furnace, can be powered - mpTNT, // Minecart-with-TNT, can be blown up with activator rail - mpHopper, // Minecart-with-hopper, can be hopper - // TODO: Spawner minecarts, (and possibly any block in a minecart with NBT editing) - } ; - - // cEntity overrides: - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - virtual void DoTakeDamage(TakeDamageInfo & TDI) override; - - int LastDamage(void) const { return m_LastDamage; } - void HandleRailPhysics(float a_Dt, cChunk & a_Chunk); - ePayload GetPayload(void) const { return m_Payload; } - -protected: - ePayload m_Payload; - - cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); - - int m_LastDamage; - -} ; - - - - - -class cEmptyMinecart : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cEmptyMinecart); - - cEmptyMinecart(double a_X, double a_Y, double a_Z); - - // cEntity overrides: - virtual void OnRightClicked(cPlayer & a_Player) override; -} ; - - - - - -class cMinecartWithChest : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithChest); - - /// Number of item slots in the chest - static const int NumSlots = 9 * 3; - - cMinecartWithChest(double a_X, double a_Y, double a_Z); - - const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; } - cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; } - - void SetSlot(int a_Idx, const cItem & a_Item); - -protected: - - /// The chest contents: - cItem m_Items[NumSlots]; - - // cEntity overrides: - virtual void OnRightClicked(cPlayer & a_Player) override; -} ; - - - - - -class cMinecartWithFurnace : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithFurnace); - - cMinecartWithFurnace(double a_X, double a_Y, double a_Z); - - // cEntity overrides: - virtual void OnRightClicked(cPlayer & a_Player) override; - bool IsFueled (void) const { return m_IsFueled; } - -private: - - bool m_IsFueled; - -} ; - - - - - -class cMinecartWithTNT : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithTNT); - - cMinecartWithTNT(double a_X, double a_Y, double a_Z); -} ; - - - - - -class cMinecartWithHopper : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithHopper); - - cMinecartWithHopper(double a_X, double a_Y, double a_Z); -} ; \ No newline at end of file diff --git a/source/Entities/Pawn.cpp b/source/Entities/Pawn.cpp deleted file mode 100644 index fffefd538..000000000 --- a/source/Entities/Pawn.cpp +++ /dev/null @@ -1,19 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Pawn.h" - - - - - -cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) - : cEntity(a_EntityType, 0, 0, 0, a_Width, a_Height) - , m_bBurnable(true) -{ -} - - - - - diff --git a/source/Entities/Pawn.h b/source/Entities/Pawn.h deleted file mode 100644 index e76337d86..000000000 --- a/source/Entities/Pawn.h +++ /dev/null @@ -1,28 +0,0 @@ - -#pragma once - -#include "Entity.h" - - - - - -// tolua_begin -class cPawn : - public cEntity -{ - // tolua_end - typedef cEntity super; - -public: - CLASS_PROTODEF(cPawn); - - cPawn(eEntityType a_EntityType, double a_Width, double a_Height); - -protected: - bool m_bBurnable; -} ; // tolua_export - - - - diff --git a/source/Entities/Pickup.cpp b/source/Entities/Pickup.cpp deleted file mode 100644 index f8aae9703..000000000 --- a/source/Entities/Pickup.cpp +++ /dev/null @@ -1,166 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#ifndef _WIN32 -#include -#endif - -#include "Pickup.h" -#include "../ClientHandle.h" -#include "../Inventory.h" -#include "../World.h" -#include "../Simulator/FluidSimulator.h" -#include "../Server.h" -#include "Player.h" -#include "../PluginManager.h" -#include "../Item.h" -#include "../Root.h" -#include "../Chunk.h" - -#include "../Vector3d.h" -#include "../Vector3f.h" - - - - - -cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) - : cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2) - , m_Timer( 0.f ) - , m_Item(a_Item) - , m_bCollected( false ) - , m_bIsPlayerCreated( IsPlayerCreated ) -{ - SetGravity(-10.5f); - SetMaxHealth(5); - SetHealth(5); - SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); -} - - - - - -void cPickup::SpawnOn(cClientHandle & a_Client) -{ - a_Client.SendPickupSpawn(*this); -} - - - - - -void cPickup::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - BroadcastMovementUpdate(); //Notify clients of position - - m_Timer += a_Dt; - - if (!m_bCollected) - { - int BlockY = (int) floor(GetPosY()); - if ((BlockY >= 0) && (BlockY < cChunkDef::Height)) // Don't do anything except for falling when outside the world - { - int BlockX = (int) floor(GetPosX()); - int BlockZ = (int) floor(GetPosZ()); - // Position might have changed due to physics. So we have to make sure we have the correct chunk. - cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); - if (CurrentChunk != NULL) // Make sure the chunk is loaded - { - int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width); - int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width); - - // If the pickup is on the bottommost block position, make it think the void is made of air: (#131) - BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; - BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ); - - if ( - IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) || - IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE) - ) - { - m_bCollected = true; - m_Timer = 0; // We have to reset the timer. - m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick. - if (m_Timer > 500.f) - { - Destroy(true); - return; - } - } - } - } - } - else - { - if (m_Timer > 500.f) // 0.5 second - { - Destroy(true); - return; - } - } - - if (m_Timer > 1000 * 60 * 5) // 5 minutes - { - Destroy(true); - return; - } - - if (GetPosY() < -8) // Out of this world and no more visible! - { - Destroy(true); - return; - } -} - - - - - -bool cPickup::CollectedBy(cPlayer * a_Dest) -{ - ASSERT(a_Dest != NULL); - - if (m_bCollected) - { - // LOG("Pickup %d cannot be collected by \"%s\", because it has already been collected.", m_UniqueID, a_Dest->GetName().c_str()); - return false; // It's already collected! - } - - // Two seconds if player created the pickup (vomiting), half a second if anything else - if (m_Timer < (m_bIsPlayerCreated ? 2000.f : 500.f)) - { - // LOG("Pickup %d cannot be collected by \"%s\", because it is not old enough.", m_UniqueID, a_Dest->GetName().c_str()); - return false; // Not old enough - } - - if (cRoot::Get()->GetPluginManager()->CallHookCollectingPickup(a_Dest, *this)) - { - // LOG("Pickup %d cannot be collected by \"%s\", because a plugin has said no.", m_UniqueID, a_Dest->GetName().c_str()); - return false; - } - - int NumAdded = a_Dest->GetInventory().AddItem(m_Item); - if (NumAdded > 0) - { - m_Item.m_ItemCount -= NumAdded; - m_World->BroadcastCollectPickup(*this, *a_Dest); - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - m_World->BroadcastSoundEffect("random.pop",(int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - if (m_Item.m_ItemCount == 0) - { - // All of the pickup has been collected, schedule the pickup for destroying - m_bCollected = true; - } - m_Timer = 0; - return true; - } - - // LOG("Pickup %d cannot be collected by \"%s\", because there's no space in the inventory.", a_Dest->GetName().c_str(), m_UniqueID); - return false; -} - - - - diff --git a/source/Entities/Pickup.h b/source/Entities/Pickup.h deleted file mode 100644 index d39eda298..000000000 --- a/source/Entities/Pickup.h +++ /dev/null @@ -1,64 +0,0 @@ - -#pragma once - -#include "Entity.h" -#include "../Item.h" - - - - - -class cPlayer; - - - - - -// tolua_begin -class cPickup : - public cEntity -{ - // tolua_end - typedef cEntity super; - -public: - CLASS_PROTODEF(cPickup); - - cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export - - cItem & GetItem(void) {return m_Item; } // tolua_export - const cItem & GetItem(void) const {return m_Item; } - - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - - bool CollectedBy(cPlayer * a_Dest); // tolua_export - - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - /// Returns the number of ticks that this entity has existed - int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export - - /// Returns true if the pickup has already been collected - bool IsCollected(void) const { return m_bCollected; } // tolua_export - - /// Returns true if created by player (i.e. vomiting), used for determining picking-up delay time - bool IsPlayerCreated(void) const { return m_bIsPlayerCreated; } // tolua_export - -private: - Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;) - - Vector3d m_WaterSpeed; - - /// The number of ticks that the entity has existed / timer between collect and destroy; in msec - float m_Timer; - - cItem m_Item; - - bool m_bCollected; - - bool m_bIsPlayerCreated; -}; // tolua_export - - - - diff --git a/source/Entities/Player.cpp b/source/Entities/Player.cpp deleted file mode 100644 index 098417dc5..000000000 --- a/source/Entities/Player.cpp +++ /dev/null @@ -1,1715 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Player.h" -#include "../Server.h" -#include "../ClientHandle.h" -#include "../UI/Window.h" -#include "../UI/WindowOwner.h" -#include "../World.h" -#include "Pickup.h" -#include "../PluginManager.h" -#include "../BlockEntities/BlockEntity.h" -#include "../GroupManager.h" -#include "../Group.h" -#include "../ChatColor.h" -#include "../Item.h" -#include "../Tracer.h" -#include "../Root.h" -#include "../OSSupport/Timer.h" -#include "../MersenneTwister.h" -#include "../Chunk.h" -#include "../Items/ItemHandler.h" - -#include "../Vector3d.h" -#include "../Vector3f.h" - -#include "../../iniFile/iniFile.h" -#include - -#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) - - - - - - -cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) - : super(etPlayer, 0.6, 1.8) - , m_GameMode(eGameMode_NotSet) - , m_IP("") - , m_LastBlockActionTime( 0 ) - , m_LastBlockActionCnt( 0 ) - , m_AirLevel( MAX_AIR_LEVEL ) - , m_AirTickTimer( DROWNING_TICKS ) - , m_bVisible( true ) - , m_LastGroundHeight( 0 ) - , m_bTouchGround( false ) - , m_Stance( 0.0 ) - , m_Inventory(*this) - , m_CurrentWindow(NULL) - , m_InventoryWindow(NULL) - , m_TimeLastPickupCheck( 0.f ) - , m_Color('-') - , m_ClientHandle( a_Client ) - , m_FoodLevel(MAX_FOOD_LEVEL) - , m_FoodSaturationLevel(5) - , m_FoodTickTimer(0) - , m_FoodExhaustionLevel(0) - , m_FoodPoisonedTicksRemaining(0) - , m_NormalMaxSpeed(0.1) - , m_SprintingMaxSpeed(0.13) - , m_IsCrouched(false) - , m_IsSprinting(false) - , m_IsSwimming(false) - , m_IsSubmerged(false) - , m_EatingFinishTick(-1) - , m_IsChargingBow(false) - , m_BowCharge(0) - , m_XpTotal(0) -{ - LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", - a_PlayerName.c_str(), a_Client->GetIPString().c_str(), - this, GetUniqueID() - ); - - m_InventoryWindow = new cInventoryWindow(*this); - m_CurrentWindow = m_InventoryWindow; - m_InventoryWindow->OpenedByPlayer(*this); - - SetMaxHealth(MAX_HEALTH); - m_Health = MAX_HEALTH; - - cTimer t1; - m_LastPlayerListTime = t1.GetNowTime(); - - m_TimeLastTeleportPacket = 0; - m_TimeLastPickupCheck = 0; - - m_PlayerName = a_PlayerName; - m_bDirtyPosition = true; // So chunks are streamed to player at spawn - - if (!LoadFromDisk()) - { - m_Inventory.Clear(); - SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX()); - SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY()); - SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ()); - - LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", - a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() - ); - } - m_LastJumpHeight = (float)(GetPosY()); - m_LastGroundHeight = (float)(GetPosY()); - m_Stance = GetPosY() + 1.62; - - cRoot::Get()->GetServer()->PlayerCreated(this); -} - - - - - -cPlayer::~cPlayer(void) -{ - LOGD("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID()); - - // Notify the server that the player is being destroyed - cRoot::Get()->GetServer()->PlayerDestroying(this); - - SaveToDisk(); - - m_World->RemovePlayer( this ); - - m_ClientHandle = NULL; - - delete m_InventoryWindow; - - LOGD("Player %p deleted", this); -} - - - - - -bool cPlayer::Initialize(cWorld * a_World) -{ - ASSERT(a_World != NULL); - - if (super::Initialize(a_World)) - { - // Remove the client handle from the server, it will be ticked from this object from now on - if (m_ClientHandle != NULL) - { - cRoot::Get()->GetServer()->ClientMovedToWorld(m_ClientHandle); - } - - GetWorld()->AddPlayer(this); - return true; - } - return false; -} - - - - - -void cPlayer::Destroyed() -{ - CloseWindow(false); - - m_ClientHandle = NULL; -} - - - - - -void cPlayer::SpawnOn(cClientHandle & a_Client) -{ - if (!m_bVisible || (m_ClientHandle == (&a_Client))) - { - return; - } - a_Client.SendPlayerSpawn(*this); - a_Client.SendEntityHeadLook(*this); - a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() ); - a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() ); - a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() ); - a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() ); - a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() ); -} - - - - - -void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) -{ - if (m_ClientHandle != NULL) - { - if (m_ClientHandle->IsDestroyed()) - { - // This should not happen, because destroying a client will remove it from the world, but just in case - m_ClientHandle = NULL; - return; - } - - if (!m_ClientHandle->IsPlaying()) - { - // We're not yet in the game, ignore everything - return; - } - } - - if (!a_Chunk.IsValid()) - { - // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83) - return; - } - - super::Tick(a_Dt, a_Chunk); - - // Set player swimming state - SetSwimState(a_Chunk); - - // Handle air drowning stuff - HandleAir(); - - // Handle charging the bow: - if (m_IsChargingBow) - { - m_BowCharge += 1; - } - - if (m_bDirtyPosition) - { - // Apply food exhaustion from movement: - ApplyFoodExhaustionFromMovement(); - - cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this); - BroadcastMovementUpdate(m_ClientHandle); - m_ClientHandle->StreamChunks(); - } - else - { - BroadcastMovementUpdate(m_ClientHandle); - } - - if (m_Health > 0) // make sure player is alive - { - m_World->CollectPickupsByPlayer(this); - - if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) - { - FinishEating(); - } - - HandleFood(); - } - - // Send Player List (Once per m_LastPlayerListTime/1000 ms) - cTimer t1; - if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) - { - m_World->SendPlayerList(this); - m_LastPlayerListTime = t1.GetNowTime(); - } -} - - - - - -int cPlayer::CalcLevelFromXp(int a_XpTotal) -{ - //level 0 to 15 - if(a_XpTotal <= XP_TO_LEVEL15) - { - return a_XpTotal / XP_PER_LEVEL_TO15; - } - - //level 30+ - if(a_XpTotal > XP_TO_LEVEL30) - { - return (int) (151.5 + sqrt( 22952.25 - (14 * (2220 - a_XpTotal)))) / 7; - } - - //level 16 to 30 - return (int) ( 29.5 + sqrt( 870.25 - (6 * ( 360 - a_XpTotal )))) / 3; -} - - - - - -int cPlayer::XpForLevel(int a_Level) -{ - //level 0 to 15 - if(a_Level <= 15) - { - return a_Level * XP_PER_LEVEL_TO15; - } - - //level 30+ - if(a_Level >= 31) - { - return (int) ( (3.5 * a_Level * a_Level) - (151.5 * a_Level) + 2220 ); - } - - //level 16 to 30 - return (int) ( (1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360 ); -} - - - - - -int cPlayer::XpGetLevel() -{ - return CalcLevelFromXp(m_XpTotal); -} - - - - - -float cPlayer::XpGetPercentage() -{ - int currentLevel = CalcLevelFromXp(m_XpTotal); - - return (float)m_XpTotal / (float)XpForLevel(1+currentLevel); -} - - - - - -bool cPlayer::SetExperience(int a_XpTotal) -{ - if(!(a_XpTotal >= 0) || (a_XpTotal > (INT_MAX - m_XpTotal))) - { - LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_XpTotal); - return false; //oops, they gave us a dodgey number - } - - m_XpTotal = a_XpTotal; - - return true; -} - - - - - -int cPlayer::AddExperience(int a_Xp_delta) -{ - if(a_Xp_delta < 0) - { - //value was negative, abort and report - LOGWARNING("Attempt was made to increment Xp by %d, must be positive", - a_Xp_delta); - return -1; //should we instead just return the current Xp? - } - - LOGD("Player \"%s\" earnt %d experience", m_PlayerName.c_str(), a_Xp_delta); - - m_XpTotal += a_Xp_delta; - - return m_XpTotal; -} - - - - - -void cPlayer::StartChargingBow(void) -{ - LOGD("Player \"%s\" started charging their bow", m_PlayerName.c_str()); - m_IsChargingBow = true; - m_BowCharge = 0; -} - - - - - -int cPlayer::FinishChargingBow(void) -{ - LOGD("Player \"%s\" finished charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge); - int res = m_BowCharge; - m_IsChargingBow = false; - m_BowCharge = 0; - return res; -} - - - - - -void cPlayer::CancelChargingBow(void) -{ - LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge); - m_IsChargingBow = false; - m_BowCharge = 0; -} - - - - - -void cPlayer::SetTouchGround(bool a_bTouchGround) -{ - m_bTouchGround = a_bTouchGround; - - if (!m_bTouchGround) - { - if (GetPosY() > m_LastJumpHeight) - { - m_LastJumpHeight = (float)GetPosY(); - } - cWorld * World = GetWorld(); - if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height)) - { - BLOCKTYPE BlockType = World->GetBlock(float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ())); - if (BlockType != E_BLOCK_AIR) - { - m_bTouchGround = true; - } - if ( - (BlockType == E_BLOCK_WATER) || - (BlockType == E_BLOCK_STATIONARY_WATER) || - (BlockType == E_BLOCK_LADDER) || - (BlockType == E_BLOCK_VINES) - ) - { - m_LastGroundHeight = (float)GetPosY(); - } - } - } - else - { - float Dist = (float)(m_LastGroundHeight - floor(GetPosY())); - int Damage = (int)(Dist - 3.f); - if (m_LastJumpHeight > m_LastGroundHeight) Damage++; - m_LastJumpHeight = (float)GetPosY(); - - if ((Damage > 0) && (!IsGameModeCreative())) - { - TakeDamage(dtFalling, NULL, Damage, Damage, 0); - } - - m_LastGroundHeight = (float)GetPosY(); - } -} - - - - - -void cPlayer::Heal(int a_Health) -{ - super::Heal(a_Health); - SendHealth(); -} - - - - - -void cPlayer::SetFoodLevel(int a_FoodLevel) -{ - m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL)); - SendHealth(); -} - - - - - -void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel) -{ - m_FoodSaturationLevel = std::max(0.0, std::min(a_FoodSaturationLevel, (double)m_FoodLevel)); -} - - - - - -void cPlayer::SetFoodTickTimer(int a_FoodTickTimer) -{ - m_FoodTickTimer = a_FoodTickTimer; -} - - - - - -void cPlayer::SetFoodExhaustionLevel(double a_FoodExhaustionLevel) -{ - m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodExhaustionLevel, 4.0)); -} - - - - - -void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining) -{ - m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining; -} - - - - - -bool cPlayer::Feed(int a_Food, double a_Saturation) -{ - if (m_FoodLevel >= MAX_FOOD_LEVEL) - { - return false; - } - - m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL); - m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel); - - SendHealth(); - return true; -} - - - - - -void cPlayer::FoodPoison(int a_NumTicks) -{ - bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0); - m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks); - if (!HasBeenFoodPoisoned) - { - // TODO: Send the poisoning indication to the client - how? - SendHealth(); - } -} - - - - - -void cPlayer::StartEating(void) -{ - // Set the timer: - m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS; - - // Send the packets: - m_World->BroadcastPlayerAnimation(*this, 5); - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cPlayer::FinishEating(void) -{ - // Reset the timer: - m_EatingFinishTick = -1; - - // Send the packets: - m_ClientHandle->SendEntityStatus(*this, ENTITY_STATUS_EATING_ACCEPTED); - m_World->BroadcastPlayerAnimation(*this, 0); - m_World->BroadcastEntityMetadata(*this); - - // consume the item: - cItem Item(GetEquippedItem()); - Item.m_ItemCount = 1; - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Item.m_ItemType); - if (!ItemHandler->EatItem(this, &Item)) - { - return; - } - ItemHandler->OnFoodEaten(m_World, this, &Item); - - GetInventory().RemoveOneEquippedItem(); - - //if the food is mushroom soup, return a bowl to the inventory - if( Item.m_ItemType == E_ITEM_MUSHROOM_SOUP ) { - cItem emptyBowl(E_ITEM_BOWL, 1, 0, ""); - GetInventory().AddItem(emptyBowl, true, true); - } -} - - - - - -void cPlayer::AbortEating(void) -{ - m_EatingFinishTick = -1; - m_World->BroadcastPlayerAnimation(*this, 0); - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cPlayer::SendHealth(void) -{ - if (m_ClientHandle != NULL) - { - m_ClientHandle->SendHealth(); - } -} - - - - - -void cPlayer::ClearInventoryPaintSlots(void) -{ - // Clear the list of slots that are being inventory-painted. Used by cWindow only - m_InventoryPaintSlots.clear(); -} - - - - - -void cPlayer::AddInventoryPaintSlot(int a_SlotNum) -{ - // Add a slot to the list for inventory painting. Used by cWindow only - m_InventoryPaintSlots.push_back(a_SlotNum); -} - - - - - -const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const -{ - // Return the list of slots currently stored for inventory painting. Used by cWindow only - return m_InventoryPaintSlots; -} - - - - - -double cPlayer::GetMaxSpeed(void) const -{ - return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed; -} - - - - - -void cPlayer::SetNormalMaxSpeed(double a_Speed) -{ - m_NormalMaxSpeed = a_Speed; - if (!m_IsSprinting) - { - m_ClientHandle->SendPlayerMaxSpeed(); - } -} - - - - - -void cPlayer::SetSprintingMaxSpeed(double a_Speed) -{ - m_SprintingMaxSpeed = a_Speed; - if (m_IsSprinting) - { - m_ClientHandle->SendPlayerMaxSpeed(); - } -} - - - - - -void cPlayer::SetCrouch(bool a_IsCrouched) -{ - // Set the crouch status, broadcast to all visible players - - if (a_IsCrouched == m_IsCrouched) - { - // No change - return; - } - m_IsCrouched = a_IsCrouched; - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cPlayer::SetSprint(bool a_IsSprinting) -{ - if (a_IsSprinting == m_IsSprinting) - { - // No change - return; - } - - m_IsSprinting = a_IsSprinting; - m_ClientHandle->SendPlayerMaxSpeed(); -} - - - - - -void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - if (a_TDI.DamageType != dtInVoid) - { - if (IsGameModeCreative()) - { - // No damage / health in creative mode - return; - } - } - - super::DoTakeDamage(a_TDI); - - // Any kind of damage adds food exhaustion - AddFoodExhaustion(0.3f); - - SendHealth(); -} - - - - - -void cPlayer::KilledBy(cEntity * a_Killer) -{ - super::KilledBy(a_Killer); - - if (m_Health > 0) - { - return; // not dead yet =] - } - - m_bVisible = false; // So new clients don't see the player - - // Puke out all the items - cItems Pickups; - m_Inventory.CopyToItems(Pickups); - m_Inventory.Clear(); - m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); - SaveToDisk(); // Save it, yeah the world is a tough place ! -} - - - - - -void cPlayer::Respawn(void) -{ - m_Health = GetMaxHealth(); - - // Reset food level: - m_FoodLevel = MAX_FOOD_LEVEL; - m_FoodSaturationLevel = 5; - - m_ClientHandle->SendRespawn(); - - // Extinguish the fire: - StopBurning(); - - TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ()); - - SetVisible(true); -} - - - - - -double cPlayer::GetEyeHeight(void) const -{ - return m_Stance; -} - - - - -Vector3d cPlayer::GetEyePosition(void) const -{ - return Vector3d( GetPosX(), m_Stance, GetPosZ() ); -} - - - - - -bool cPlayer::IsGameModeCreative(void) const -{ - return (m_GameMode == gmCreative) || // Either the player is explicitly in Creative - ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Creative -} - - - - - -bool cPlayer::IsGameModeSurvival(void) const -{ - return (m_GameMode == gmSurvival) || // Either the player is explicitly in Survival - ((m_GameMode == gmNotSet) && m_World->IsGameModeSurvival()); // or they inherit from the world and the world is Survival -} - - - - - -bool cPlayer::IsGameModeAdventure(void) const -{ - return (m_GameMode == gmCreative) || // Either the player is explicitly in Adventure - ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Adventure -} - - - - - -void cPlayer::OpenWindow(cWindow * a_Window) -{ - if (a_Window != m_CurrentWindow) - { - CloseWindow(false); - } - a_Window->OpenedByPlayer(*this); - m_CurrentWindow = a_Window; - a_Window->SendWholeWindow(*GetClientHandle()); -} - - - - - -void cPlayer::CloseWindow(bool a_CanRefuse) -{ - if (m_CurrentWindow == NULL) - { - m_CurrentWindow = m_InventoryWindow; - return; - } - - if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse) - { - // Close accepted, go back to inventory window (the default): - m_CurrentWindow = m_InventoryWindow; - } - else - { - // Re-open the window - m_CurrentWindow->OpenedByPlayer(*this); - m_CurrentWindow->SendWholeWindow(*GetClientHandle()); - } -} - - - - - -void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse) -{ - if ((m_CurrentWindow == NULL) || (m_CurrentWindow->GetWindowID() != a_WindowID)) - { - return; - } - CloseWindow(); -} - - - - - -void cPlayer::SetLastBlockActionTime() -{ - if (m_World != NULL) - { - m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f; - } -} - - - - - -void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) -{ - m_LastBlockActionCnt = a_LastBlockActionCnt; -} - - - - - -void cPlayer::SetGameMode(eGameMode a_GameMode) -{ - if ((a_GameMode < gmMin) || (a_GameMode >= gmMax)) - { - LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); - return; - } - - if (m_GameMode == a_GameMode) - { - // Gamemode already set - return; - } - - m_GameMode = a_GameMode; - m_ClientHandle->SendGameMode(a_GameMode); -} - - - - - -void cPlayer::LoginSetGameMode( eGameMode a_GameMode ) -{ - m_GameMode = a_GameMode; -} - - - - - -void cPlayer::SetIP(const AString & a_IP) -{ - m_IP = a_IP; -} - - - - - -void cPlayer::SendMessage(const AString & a_Message) -{ - m_ClientHandle->SendChat(a_Message); -} - - - - - -void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) -{ - SetPosition( a_PosX, a_PosY, a_PosZ ); - m_LastGroundHeight = (float)a_PosY; - - m_World->BroadcastTeleportEntity(*this, GetClientHandle()); - m_ClientHandle->SendPlayerMoveLook(); -} - - - - - -Vector3d cPlayer::GetThrowStartPos(void) const -{ - Vector3d res = GetEyePosition(); - - // Adjust the position to be just outside the player's bounding box: - res.x += 0.16 * cos(GetPitch()); - res.y += -0.1; - res.z += 0.16 * sin(GetPitch()); - - return res; -} - - - - - -Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const -{ - Vector3d res = GetLookVector(); - res.Normalize(); - - // TODO: Add a slight random change (+-0.0075 in each direction) - - return res * a_SpeedCoeff; -} - - - - - -void cPlayer::MoveTo( const Vector3d & a_NewPos ) -{ - if ((a_NewPos.y < -990) && (GetPosY() > -100)) - { - // When attached to an entity, the client sends position packets with weird coords: - // Y = -999 and X, Z = attempting to create speed, usually up to 0.03 - // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while - // the client may still send more of these nonsensical packets. - if (m_AttachedTo != NULL) - { - Vector3d AddSpeed(a_NewPos); - AddSpeed.y = 0; - m_AttachedTo->AddSpeed(AddSpeed); - } - return; - } - - // TODO: should do some checks to see if player is not moving through terrain - // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too - - SetPosition( a_NewPos ); - SetStance(a_NewPos.y + 1.62); -} - - - - - -void cPlayer::SetVisible(bool a_bVisible) -{ - if (a_bVisible && !m_bVisible) // Make visible - { - m_bVisible = true; - m_World->BroadcastSpawnEntity(*this); - } - if (!a_bVisible && m_bVisible) - { - m_bVisible = false; - m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients - } -} - - - - - -void cPlayer::AddToGroup( const AString & a_GroupName ) -{ - cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); - m_Groups.push_back( Group ); - LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); - ResolveGroups(); - ResolvePermissions(); -} - - - - - -void cPlayer::RemoveFromGroup( const AString & a_GroupName ) -{ - bool bRemoved = false; - for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) - { - if( (*itr)->GetName().compare(a_GroupName ) == 0 ) - { - m_Groups.erase( itr ); - bRemoved = true; - break; - } - } - - if( bRemoved ) - { - LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); - ResolveGroups(); - ResolvePermissions(); - } - else - { - LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() ); - } -} - - - - - -bool cPlayer::CanUseCommand( const AString & a_Command ) -{ - for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) - { - if( (*itr)->HasCommand( a_Command ) ) return true; - } - return false; -} - - - - - -bool cPlayer::HasPermission(const AString & a_Permission) -{ - if (a_Permission.empty()) - { - // Empty permission request is always granted - return true; - } - - AStringVector Split = StringSplit( a_Permission, "." ); - PermissionMap Possibilities = m_ResolvedPermissions; - // Now search the namespaces - while( Possibilities.begin() != Possibilities.end() ) - { - PermissionMap::iterator itr = Possibilities.begin(); - if( itr->second ) - { - AStringVector OtherSplit = StringSplit( itr->first, "." ); - if( OtherSplit.size() <= Split.size() ) - { - unsigned int i; - for( i = 0; i < OtherSplit.size(); ++i ) - { - if( OtherSplit[i].compare( Split[i] ) != 0 ) - { - if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard! - break; - } - } - if( i == Split.size() ) return true; - } - } - Possibilities.erase( itr ); - } - - // Nothing that matched :( - return false; -} - - - - - -bool cPlayer::IsInGroup( const AString & a_Group ) -{ - for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr ) - { - if( a_Group.compare( (*itr)->GetName().c_str() ) == 0 ) - return true; - } - return false; -} - - - - - -void cPlayer::ResolvePermissions() -{ - m_ResolvedPermissions.clear(); // Start with an empty map yo~ - - // Copy all player specific permissions into the resolved permissions map - for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr ) - { - m_ResolvedPermissions[ itr->first ] = itr->second; - } - - for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr ) - { - const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); - for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr ) - { - m_ResolvedPermissions[ itr->first ] = itr->second; - } - } -} - - - - - -void cPlayer::ResolveGroups() -{ - // Clear resolved groups first - m_ResolvedGroups.clear(); - - // Get a complete resolved list of all groups the player is in - std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates - GroupList ToIterate; - for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr ) - { - ToIterate.push_back( *GroupItr ); - } - while( ToIterate.begin() != ToIterate.end() ) - { - cGroup* CurrentGroup = *ToIterate.begin(); - if( AllGroups.find( CurrentGroup ) != AllGroups.end() ) - { - LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", - m_PlayerName.c_str(), CurrentGroup->GetName().c_str() - ); - } - else - { - AllGroups[ CurrentGroup ] = true; - m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list - const cGroup::GroupList & Inherits = CurrentGroup->GetInherits(); - for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr ) - { - if( AllGroups.find( *itr ) != AllGroups.end() ) - { - LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() ); - continue; - } - ToIterate.push_back( *itr ); - } - } - ToIterate.erase( ToIterate.begin() ); - } -} - - - - - -AString cPlayer::GetColor(void) const -{ - if ( m_Color != '-' ) - { - return cChatColor::MakeColor( m_Color ); - } - - if ( m_Groups.size() < 1 ) - { - return cChatColor::White; - } - - return (*m_Groups.begin())->GetColor(); -} - - - - - -void cPlayer::TossItem( - bool a_bDraggingItem, - char a_Amount /* = 1 */, - short a_CreateType /* = 0 */, - short a_CreateHealth /* = 0 */ -) -{ - cItems Drops; - if (a_CreateType != 0) - { - // Just create item without touching the inventory (used in creative mode) - Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth)); - } - else - { - // Drop an item from the inventory: - if (a_bDraggingItem) - { - cItem & Item = GetDraggingItem(); - if (!Item.IsEmpty()) - { - char OriginalItemAmount = Item.m_ItemCount; - Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount); - Drops.push_back(Item); - if (OriginalItemAmount > a_Amount) - { - Item.m_ItemCount = OriginalItemAmount - (char)a_Amount; - } - else - { - Item.Empty(); - } - } - } - else - { - // Else drop equipped item - cItem DroppedItem(GetInventory().GetEquippedItem()); - if (!DroppedItem.IsEmpty()) - { - if (GetInventory().RemoveOneEquippedItem()) - { - DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again - Drops.push_back(DroppedItem); - } - } - } - } - double vX = 0, vY = 0, vZ = 0; - EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); - vY = -vY * 2 + 1.f; - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player -} - - - - - -bool cPlayer::MoveToWorld(const char * a_WorldName) -{ - cWorld * World = cRoot::Get()->GetWorld(a_WorldName); - if (World == NULL) - { - LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName); - return false; - } - - eDimension OldDimension = m_World->GetDimension(); - - // Remove all links to the old world - m_World->RemovePlayer(this); - m_ClientHandle->RemoveFromAllChunks(); - m_World->RemoveEntity(this); - - // If the dimension is different, we can send the respawn packet - // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02 - m_ClientHandle->MoveToWorld(*World, (OldDimension != World->GetDimension())); - - // Add player to all the necessary parts of the new world - SetWorld(World); - World->AddEntity(this); - World->AddPlayer(this); - - return true; -} - - - - - -void cPlayer::LoadPermissionsFromDisk() -{ - m_Groups.clear(); - m_Permissions.clear(); - - cIniFile IniFile; - if (IniFile.ReadFile("users.ini")) - { - std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", ""); - if (!Groups.empty()) - { - AStringVector Split = StringSplit( Groups, "," ); - for( unsigned int i = 0; i < Split.size(); i++ ) - { - AddToGroup( Split[i].c_str() ); - } - } - else - { - AddToGroup("Default"); - } - - m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0]; - } - else - { - LOGWARN("Failed to read the users.ini file. The player will be added only to the Default group."); - AddToGroup("Default"); - } - ResolvePermissions(); -} - - - - -bool cPlayer::LoadFromDisk() -{ - LoadPermissionsFromDisk(); - - // Log player permissions, cause it's what the cool kids do - LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); - for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) - { - if( itr->second ) LOGINFO("%s", itr->first.c_str() ); - } - - AString SourceFile; - Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); - - cFile f; - if (!f.Open(SourceFile, cFile::fmRead)) - { - // This is a new player whom we haven't seen yet, bail out, let them have the defaults - return false; - } - - AString buffer; - if (f.ReadRestOfFile(buffer) != f.GetSize()) - { - LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str()); - return false; - } - f.Close(); //cool kids play nice - - Json::Value root; - Json::Reader reader; - if (!reader.parse(buffer, root, false)) - { - LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str()); - } - - Json::Value & JSON_PlayerPosition = root["position"]; - if (JSON_PlayerPosition.size() == 3) - { - SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); - SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); - SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); - m_LastPosX = GetPosX(); - m_LastPosY = GetPosY(); - m_LastPosZ = GetPosZ(); - m_LastFoodPos = GetPosition(); - } - - Json::Value & JSON_PlayerRotation = root["rotation"]; - if (JSON_PlayerRotation.size() == 3) - { - SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); - SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); - SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); - } - - m_Health = root.get("health", 0).asInt(); - m_AirLevel = root.get("air", MAX_AIR_LEVEL).asInt(); - m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt(); - m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); - m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); - m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); - - SetExperience(root.get("experience", 0).asInt()); - - m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); - - m_Inventory.LoadFromJson(root["inventory"]); - - m_LoadedWorldName = root.get("world", "world").asString(); - - LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", - m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() - ); - - return true; -} - - - - - -bool cPlayer::SaveToDisk() -{ - cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); - - // create the JSON data - Json::Value JSON_PlayerPosition; - JSON_PlayerPosition.append(Json::Value(GetPosX())); - JSON_PlayerPosition.append(Json::Value(GetPosY())); - JSON_PlayerPosition.append(Json::Value(GetPosZ())); - - Json::Value JSON_PlayerRotation; - JSON_PlayerRotation.append(Json::Value(GetRotation())); - JSON_PlayerRotation.append(Json::Value(GetPitch())); - JSON_PlayerRotation.append(Json::Value(GetRoll())); - - Json::Value JSON_Inventory; - m_Inventory.SaveToJson(JSON_Inventory); - - Json::Value root; - root["position"] = JSON_PlayerPosition; - root["rotation"] = JSON_PlayerRotation; - root["inventory"] = JSON_Inventory; - root["health"] = m_Health; - root["experience"] = m_XpTotal; - root["air"] = m_AirLevel; - root["food"] = m_FoodLevel; - root["foodSaturation"] = m_FoodSaturationLevel; - root["foodTickTimer"] = m_FoodTickTimer; - root["foodExhaustion"] = m_FoodExhaustionLevel; - root["world"] = GetWorld()->GetName(); - - if (m_GameMode == GetWorld()->GetGameMode()) - { - root["gamemode"] = (int) eGameMode_NotSet; - } - else - { - root["gamemode"] = (int) m_GameMode; - } - - Json::StyledWriter writer; - std::string JsonData = writer.write(root); - - AString SourceFile; - Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); - - cFile f; - if (!f.Open(SourceFile, cFile::fmWrite)) - { - LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); - return false; - } - if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) - { - LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); - return false; - } - return true; -} - - - - - -cPlayer::StringList cPlayer::GetResolvedPermissions() -{ - StringList Permissions; - - const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; - for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) - { - if( itr->second ) Permissions.push_back( itr->first ); - } - - return Permissions; -} - - - - - -void cPlayer::UseEquippedItem(void) -{ - if (IsGameModeCreative()) // No damage in creative - { - return; - } - - GetInventory().DamageEquippedItem(); -} - - - - - -void cPlayer::SetSwimState(cChunk & a_Chunk) -{ - int RelY = (int)floor(m_LastPosY + 0.1); - if ((RelY < 0) || (RelY >= cChunkDef::Height - 1)) - { - m_IsSwimming = false; - m_IsSubmerged = false; - return; - } - - BLOCKTYPE BlockIn; - int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width; - int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width; - - // Check if the player is swimming: - // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk - if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn)) - { - // This sometimes happens on Linux machines - // Ref.: http://forum.mc-server.org/showthread.php?tid=1244 - LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}", - RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ() - ); - m_IsSwimming = false; - m_IsSubmerged = false; - return; - } - m_IsSwimming = IsBlockWater(BlockIn); - - // Check if the player is submerged: - VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn)); - m_IsSubmerged = IsBlockWater(BlockIn); -} - - - - - -void cPlayer::HandleAir(void) -{ - // Ref.: http://www.minecraftwiki.net/wiki/Chunk_format - // see if the player is /submerged/ water (block above is water) - // Get the type of block the player's standing in: - - if (IsSubmerged()) - { - // either reduce air level or damage player - if (m_AirLevel < 1) - { - if (m_AirTickTimer < 1) - { - // damage player - TakeDamage(dtDrowning, NULL, 1, 1, 0); - // reset timer - m_AirTickTimer = DROWNING_TICKS; - } - else - { - m_AirTickTimer -= 1; - } - } - else - { - // reduce air supply - m_AirLevel -= 1; - } - } - else - { - // set the air back to maximum - m_AirLevel = MAX_AIR_LEVEL; - m_AirTickTimer = DROWNING_TICKS; - } -} - - - - - -void cPlayer::HandleFood(void) -{ - // Ref.: http://www.minecraftwiki.net/wiki/Hunger - - // Remember the food level before processing, for later comparison - int LastFoodLevel = m_FoodLevel; - - // Heal or damage, based on the food level, using the m_FoodTickTimer: - if ((m_FoodLevel > 17) || (m_FoodLevel <= 0)) - { - m_FoodTickTimer++; - if (m_FoodTickTimer >= 80) - { - m_FoodTickTimer = 0; - - if (m_FoodLevel >= 17) - { - // Regenerate health from food, incur 3 pts of food exhaustion: - Heal(1); - m_FoodExhaustionLevel += 3; - } - else if (m_FoodLevel <= 0) - { - // Damage from starving - TakeDamage(dtStarving, NULL, 1, 1, 0); - } - } - } - - // Apply food poisoning food exhaustion: - if (m_FoodPoisonedTicksRemaining > 0) - { - m_FoodPoisonedTicksRemaining--; - m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick - } - - // Apply food exhaustion that has accumulated: - if (m_FoodExhaustionLevel >= 4) - { - m_FoodExhaustionLevel -= 4; - - if (m_FoodSaturationLevel >= 1) - { - m_FoodSaturationLevel -= 1; - } - else - { - m_FoodLevel = std::max(m_FoodLevel - 1, 0); - } - } - - if (m_FoodLevel != LastFoodLevel) - { - SendHealth(); - } -} - - - - - -void cPlayer::ApplyFoodExhaustionFromMovement() -{ - if (IsGameModeCreative()) - { - return; - } - - // Calculate the distance travelled, update the last pos: - Vector3d Movement(GetPosition() - m_LastFoodPos); - Movement.y = 0; // Only take XZ movement into account - m_LastFoodPos = GetPosition(); - - // If riding anything, apply no food exhaustion - if (m_AttachedTo != NULL) - { - return; - } - - // Apply the exhaustion based on distance travelled: - double BaseExhaustion = Movement.Length(); - if (IsSprinting()) - { - // 0.1 pt per meter sprinted - BaseExhaustion = BaseExhaustion * 0.1; - } - else if (IsSwimming()) - { - // 0.015 pt per meter swum - BaseExhaustion = BaseExhaustion * 0.015; - } - else - { - // 0.01 pt per meter walked / sneaked - BaseExhaustion = BaseExhaustion * 0.01; - } - m_FoodExhaustionLevel += BaseExhaustion; -} - - - - diff --git a/source/Entities/Player.h b/source/Entities/Player.h deleted file mode 100644 index ab2f94d4c..000000000 --- a/source/Entities/Player.h +++ /dev/null @@ -1,447 +0,0 @@ - -#pragma once - -#include "Pawn.h" -#include "../Inventory.h" -#include "../Defines.h" -#include "../World.h" - - - - - -class cGroup; -class cWindow; -class cClientHandle; - - - - - -// tolua_begin -class cPlayer : - public cPawn -{ - typedef cPawn super; - -public: - enum - { - MAX_HEALTH = 20, - MAX_FOOD_LEVEL = 20, - EATING_TICKS = 30, ///< Number of ticks it takes to eat an item - MAX_AIR_LEVEL = 300, - DROWNING_TICKS = 10, //number of ticks per heart of damage - } ; - // tolua_end - - CLASS_PROTODEF(cPlayer) - - - cPlayer(cClientHandle * a_Client, const AString & a_PlayerName); - virtual ~cPlayer(); - - virtual bool Initialize(cWorld * a_World) override; - - virtual void SpawnOn(cClientHandle & a_Client) override; - - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override { }; - - /// Returns the curently equipped weapon; empty item if none - virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } - - /// Returns the currently equipped helmet; empty item if nonte - virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); } - - /// Returns the currently equipped chestplate; empty item if none - virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); } - - /// Returns the currently equipped leggings; empty item if none - virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); } - - /// Returns the currently equipped boots; empty item if none - virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); } - - - // tolua_begin - - /** Sets the experience total - Returns true on success - "should" really only be called at init or player death, plugins excepted - */ - bool SetExperience(int a_XpTotal); - - /* Adds Xp, "should" not inc more than MAX_EXPERIENCE_ORB_SIZE unless you're a plugin being funny, *cough* cheating - Returns the new total experience, -1 on error - */ - int AddExperience(int a_Xp_delta); - - /// Gets the experience total - XpTotal - inline int XpGetTotal(void) { return m_XpTotal; } - - /// Gets the current level - XpLevel - int XpGetLevel(void); - - /// Gets the experience bar percentage - XpP - float XpGetPercentage(void); - - // tolua_end - - /// Starts charging the equipped bow - void StartChargingBow(void); - - /// Finishes charging the current bow. Returns the number of ticks for which the bow has been charged - int FinishChargingBow(void); - - /// Cancels the current bow charging - void CancelChargingBow(void); - - /// Returns true if the player is currently charging the bow - bool IsChargingBow(void) const { return m_IsChargingBow; } - - void SetTouchGround( bool a_bTouchGround ); - inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; } - double GetEyeHeight(void) const; // tolua_export - Vector3d GetEyePosition(void) const; // tolua_export - inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export - inline const double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc. - inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export - inline const cInventory & GetInventory(void) const { return m_Inventory; } - - inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export - - virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override; - - // tolua_begin - - /// Returns the position where projectiles thrown by this player should start, player eye position + adjustment - Vector3d GetThrowStartPos(void) const; - - /// Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff. - Vector3d GetThrowSpeed(double a_SpeedCoeff) const; - - /// Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable - eGameMode GetGameMode(void) const { return m_GameMode; } - - /// Returns the current effective gamemode (inherited gamemode is resolved before returning) - eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; } - - /** Sets the gamemode for the player. - The gamemode may be gmNotSet, in that case the player inherits the world's gamemode. - Updates the gamemode on the client (sends the packet) - */ - void SetGameMode(eGameMode a_GameMode); - - /// Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world - bool IsGameModeCreative(void) const; - - /// Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world - bool IsGameModeSurvival(void) const; - - /// Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world - bool IsGameModeAdventure(void) const; - - AString GetIP(void) const { return m_IP; } // tolua_export - - // tolua_end - - void SetIP(const AString & a_IP); - - float GetLastBlockActionTime() { return m_LastBlockActionTime; } - int GetLastBlockActionCnt() { return m_LastBlockActionCnt; } - void SetLastBlockActionCnt( int ); - void SetLastBlockActionTime(); - - // Sets the current gamemode, doesn't check validity, doesn't send update packets to client - void LoginSetGameMode(eGameMode a_GameMode); - - /// Tries to move to a new position, with attachment-related checks (y == -999) - void MoveTo(const Vector3d & a_NewPos); // tolua_export - - cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export - const cWindow * GetWindow(void) const { return m_CurrentWindow; } - - /// Opens the specified window; closes the current one first using CloseWindow() - void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp - - // tolua_begin - - /// Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true - void CloseWindow(bool a_CanRefuse = true); - - /// Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow - void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true); - - cClientHandle * GetClientHandle(void) const { return m_ClientHandle; } - - void SendMessage(const AString & a_Message); - - const AString & GetName(void) const { return m_PlayerName; } - void SetName(const AString & a_Name) { m_PlayerName = a_Name; } - - // tolua_end - - typedef std::list< cGroup* > GroupList; - typedef std::list< std::string > StringList; - - /// Adds a player to existing group or creates a new group when it doesn't exist - void AddToGroup( const AString & a_GroupName ); // tolua_export - - /// Removes a player from the group, resolves permissions and group inheritance (case sensitive) - void RemoveFromGroup( const AString & a_GroupName ); // tolua_export - - bool CanUseCommand( const AString & a_Command ); // tolua_export - bool HasPermission( const AString & a_Permission ); // tolua_export - const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS << - StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS << - bool IsInGroup( const AString & a_Group ); // tolua_export - - // tolua_begin - - /// Returns the full color code to use for this player, based on their primary group or set in m_Color - AString GetColor(void) const; - - void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0); - - /// Heals the player by the specified amount of HPs (positive only); sends health update - void Heal(int a_Health); - - int GetFoodLevel (void) const { return m_FoodLevel; } - double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; } - int GetFoodTickTimer (void) const { return m_FoodTickTimer; } - double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; } - int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; } - - int GetAirLevel (void) const { return m_AirLevel; } - - /// Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore - bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); } - - void SetFoodLevel (int a_FoodLevel); - void SetFoodSaturationLevel (double a_FoodSaturationLevel); - void SetFoodTickTimer (int a_FoodTickTimer); - void SetFoodExhaustionLevel (double a_FoodExhaustionLevel); - void SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining); - - /// Adds to FoodLevel and FoodSaturationLevel, returns true if any food has been consumed, false if player "full" - bool Feed(int a_Food, double a_Saturation); - - /// Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. - void AddFoodExhaustion(double a_Exhaustion) - { - m_FoodExhaustionLevel += a_Exhaustion; - } - - /// Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two - void FoodPoison(int a_NumTicks); - - /// Returns true if the player is currently in the process of eating the currently equipped item - bool IsEating(void) const { return (m_EatingFinishTick >= 0); } - - // tolua_end - - /// Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet - void StartEating(void); - - /// Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets - void FinishEating(void); - - /// Aborts the current eating operation - void AbortEating(void); - - virtual void KilledBy(cEntity * a_Killer) override; - - void Respawn(void); // tolua_export - - void SetVisible( bool a_bVisible ); // tolua_export - bool IsVisible(void) const { return m_bVisible; } // tolua_export - - bool MoveToWorld(const char * a_WorldName); // tolua_export - - bool SaveToDisk(void); - bool LoadFromDisk(void); - void LoadPermissionsFromDisk(void); // tolua_export - - const AString & GetLoadedWorldName() { return m_LoadedWorldName; } - - void UseEquippedItem(void); - - void SendHealth(void); - - // In UI windows, the item that the player is dragging: - bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); } - cItem & GetDraggingItem(void) {return m_DraggingItem; } - - // In UI windows, when inventory-painting: - /// Clears the list of slots that are being inventory-painted. To be used by cWindow only - void ClearInventoryPaintSlots(void); - - /// Adds a slot to the list for inventory painting. To be used by cWindow only - void AddInventoryPaintSlot(int a_SlotNum); - - /// Returns the list of slots currently stored for inventory painting. To be used by cWindow only - const cSlotNums & GetInventoryPaintSlots(void) const; - - // tolua_begin - - /// Returns the current maximum speed, as reported in the 1.6.1+ protocol (takes current sprinting state into account) - double GetMaxSpeed(void) const; - - /// Gets the normal maximum speed, as reported in the 1.6.1+ protocol, in the protocol units - double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; } - - /// Gets the sprinting maximum speed, as reported in the 1.6.1+ protocol, in the protocol units - double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; } - - /// Sets the normal maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed. - void SetNormalMaxSpeed(double a_Speed); - - /// Sets the sprinting maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed. - void SetSprintingMaxSpeed(double a_Speed); - - /// Sets the crouch status, broadcasts to all visible players - void SetCrouch(bool a_IsCrouched); - - /// Starts or stops sprinting, sends the max speed update to the client, if needed - void SetSprint(bool a_IsSprinting); - - /// Returns whether the player is swimming or not - virtual bool IsSwimming(void) const{ return m_IsSwimming; } - - /// Return whether the player is under water or not - virtual bool IsSubmerged(void) const{ return m_IsSubmerged; } - - // tolua_end - - // cEntity overrides: - virtual bool IsCrouched (void) const { return m_IsCrouched; } - virtual bool IsSprinting(void) const { return m_IsSprinting; } - virtual bool IsRclking (void) const { return IsEating(); } - - - -protected: - typedef std::map< std::string, bool > PermissionMap; - PermissionMap m_ResolvedPermissions; - PermissionMap m_Permissions; - - GroupList m_ResolvedGroups; - GroupList m_Groups; - - std::string m_PlayerName; - std::string m_LoadedWorldName; - - /// Xp Level stuff - enum - { - XP_TO_LEVEL15 = 255, - XP_PER_LEVEL_TO15 = 17, - XP_TO_LEVEL30 = 825 - } ; - - /// Player's air level (for swimming) - int m_AirLevel; - - /// used to time ticks between damage taken via drowning/suffocation - int m_AirTickTimer; - - bool m_bVisible; - - // Food-related variables: - /// Represents the food bar, one point equals half a "drumstick" - int m_FoodLevel; - - /// "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel - double m_FoodSaturationLevel; - - /// Count-up to the healing or damaging action, based on m_FoodLevel - int m_FoodTickTimer; - - /// A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little - double m_FoodExhaustionLevel; - - /// Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned - int m_FoodPoisonedTicksRemaining; - - /// Last position that has been recorded for food-related processing: - Vector3d m_LastFoodPos; - - float m_LastJumpHeight; - float m_LastGroundHeight; - bool m_bTouchGround; - double m_Stance; - cInventory m_Inventory; - cWindow * m_CurrentWindow; - cWindow * m_InventoryWindow; - - float m_TimeLastPickupCheck; - - void ResolvePermissions(); - - void ResolveGroups(); - char m_Color; - - float m_LastBlockActionTime; - int m_LastBlockActionCnt; - eGameMode m_GameMode; - std::string m_IP; - - cItem m_DraggingItem; - - long long m_LastPlayerListTime; - static const unsigned short PLAYER_LIST_TIME_MS = 1000; // 1000 = once per second - - cClientHandle * m_ClientHandle; - - cSlotNums m_InventoryPaintSlots; - - /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is walking. 0.1 by default - double m_NormalMaxSpeed; - - /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is sprinting. 0.13 by default - double m_SprintingMaxSpeed; - - bool m_IsCrouched; - bool m_IsSprinting; - - bool m_IsSwimming; - bool m_IsSubmerged; - - /// The world tick in which eating will be finished. -1 if not eating - Int64 m_EatingFinishTick; - - /// Player Xp level - int m_XpTotal; - - /// Caculates the Xp needed for a given level, ref: http://minecraft.gamepedia.com/XP - static int XpForLevel(int a_Level); - - /// inverse of XpAtLevel, ref: http://minecraft.gamepedia.com/XP values are as per this with pre-calculations - static int CalcLevelFromXp(int a_XpTotal); - - bool m_IsChargingBow; - int m_BowCharge; - - virtual void Destroyed(void); - - /// Filters out damage for creative mode - virtual void DoTakeDamage(TakeDamageInfo & TDI) override; - - /// Called in each tick to handle food-related processing - void HandleFood(void); - - /// Called in each tick to handle air-related processing i.e. drowning - void HandleAir(); - - /// Called once per tick to set IsSwimming and IsSubmerged - void SetSwimState(cChunk & a_Chunk); - - /// Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) - void ApplyFoodExhaustionFromMovement(); -} ; // tolua_export - - - - diff --git a/source/Entities/ProjectileEntity.cpp b/source/Entities/ProjectileEntity.cpp deleted file mode 100644 index c63b9523b..000000000 --- a/source/Entities/ProjectileEntity.cpp +++ /dev/null @@ -1,743 +0,0 @@ - -// ProjectileEntity.cpp - -// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types - -#include "Globals.h" -#include "ProjectileEntity.h" -#include "../ClientHandle.h" -#include "Player.h" -#include "../LineBlockTracer.h" -#include "../BoundingBox.h" -#include "../ChunkMap.h" -#include "../Chunk.h" - - - - - -/// Converts an angle in radians into a byte representation used by the network protocol -#define ANGLE_TO_PROTO(X) (Byte)(X * 255 / 360) - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProjectileTracerCallback: - -class cProjectileTracerCallback : - public cBlockTracer::cCallbacks -{ -public: - cProjectileTracerCallback(cProjectileEntity * a_Projectile) : - m_Projectile(a_Projectile), - m_SlowdownCoeff(0.99) // Default slowdown when not in water - { - } - - double GetSlowdownCoeff(void) const { return m_SlowdownCoeff; } - -protected: - cProjectileEntity * m_Projectile; - double m_SlowdownCoeff; - - // cCallbacks overrides: - virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override - { - /* - // DEBUG: - LOGD("Hit block %d:%d at {%d, %d, %d} face %d, %s (%s)", - a_BlockType, a_BlockMeta, - a_BlockX, a_BlockY, a_BlockZ, a_EntryFace, - g_BlockIsSolid[a_BlockType] ? "solid" : "non-solid", - ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str() - ); - */ - - if (g_BlockIsSolid[a_BlockType]) - { - // The projectile hit a solid block - // Calculate the exact hit coords: - cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1); - Vector3d Line1 = m_Projectile->GetPosition(); - Vector3d Line2 = Line1 + m_Projectile->GetSpeed(); - double LineCoeff = 0; - char Face; - if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face)) - { - Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff; - m_Projectile->OnHitSolidBlock(Intersection, Face); - return true; - } - else - { - LOGD("WEIRD! block tracer reports a hit, but BBox tracer doesn't. Ignoring the hit."); - } - } - - // Convey some special effects from special blocks: - switch (a_BlockType) - { - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - m_Projectile->StartBurning(30); - m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.9); // Slow down to 0.9* the speed each tick when moving through lava - break; - } - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - { - m_Projectile->StopBurning(); - m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.8); // Slow down to 0.8* the speed each tick when moving through water - break; - } - } // switch (a_BlockType) - - // Continue tracing - return false; - } -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProjectileEntityCollisionCallback: - -class cProjectileEntityCollisionCallback : - public cEntityCallback -{ -public: - cProjectileEntityCollisionCallback(cProjectileEntity * a_Projectile, const Vector3d & a_Pos, const Vector3d & a_NextPos) : - m_Projectile(a_Projectile), - m_Pos(a_Pos), - m_NextPos(a_NextPos), - m_MinCoeff(1), - m_HitEntity(NULL) - { - } - - - virtual bool Item(cEntity * a_Entity) override - { - if ( - (a_Entity == m_Projectile) || // Do not check collisions with self - (a_Entity == m_Projectile->GetCreator()) // Do not check whoever shot the projectile - ) - { - // TODO: Don't check creator only for the first 5 ticks - // so that arrows stuck in ground and dug up can hurt the player - return false; - } - - cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); - - // Instead of colliding the bounding box with another bounding box in motion, we collide an enlarged bounding box with a hairline. - // The results should be good enough for our purposes - double LineCoeff; - char Face; - EntBox.Expand(m_Projectile->GetWidth() / 2, m_Projectile->GetHeight() / 2, m_Projectile->GetWidth() / 2); - if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face)) - { - // No intersection whatsoever - return false; - } - - // TODO: Some entities don't interact with the projectiles (pickups, falling blocks) - // TODO: Allow plugins to interfere about which entities can be hit - - if (LineCoeff < m_MinCoeff) - { - // The entity is closer than anything we've stored so far, replace it as the potential victim - m_MinCoeff = LineCoeff; - m_HitEntity = a_Entity; - } - - // Don't break the enumeration, we want all the entities - return false; - } - - /// Returns the nearest entity that was hit, after the enumeration has been completed - cEntity * GetHitEntity(void) const { return m_HitEntity; } - - /// Returns the line coeff where the hit was encountered, after the enumeration has been completed - double GetMinCoeff(void) const { return m_MinCoeff; } - - /// Returns true if the callback has encountered a true hit - bool HasHit(void) const { return (m_MinCoeff < 1); } - -protected: - cProjectileEntity * m_Projectile; - const Vector3d & m_Pos; - const Vector3d & m_NextPos; - double m_MinCoeff; // The coefficient of the nearest hit on the Pos line - - // Although it's bad(tm) to store entity ptrs from a callback, we can afford it here, because the entire callback - // is processed inside the tick thread, so the entities won't be removed in between the calls and the final processing - cEntity * m_HitEntity; // The nearest hit entity -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProjectileEntity: - -cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) : - super(etProjectile, a_X, a_Y, a_Z, a_Width, a_Height), - m_ProjectileKind(a_Kind), - m_Creator(a_Creator), - m_IsInGround(false) -{ -} - - - - - -cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) : - super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height), - m_ProjectileKind(a_Kind), - m_Creator(a_Creator), - m_IsInGround(false) -{ - SetSpeed(a_Speed); - SetRotationFromSpeed(); - SetPitchFromSpeed(); -} - - - - - -cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed) -{ - Vector3d Speed; - if (a_Speed != NULL) - { - Speed = *a_Speed; - } - - switch (a_Kind) - { - case pkArrow: return new cArrowEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkEgg: return new cThrownEggEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkEnderPearl: return new cThrownEnderPearlEntity(a_Creator, a_X, a_Y, a_Z, Speed); - case pkSnowball: return new cThrownSnowballEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed); - // TODO: the rest - } - - LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind); - return NULL; -} - - - - - -void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - // Set the position based on what face was hit: - SetPosition(a_HitPos); - SetSpeed(0, 0, 0); - - // DEBUG: - LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, hit solid block at face %d", - m_UniqueID, - a_HitPos.x, a_HitPos.y, a_HitPos.z, - a_HitFace - ); - - m_IsInGround = true; -} - - - - - -AString cProjectileEntity::GetMCAClassName(void) const -{ - switch (m_ProjectileKind) - { - case pkArrow: return "Arrow"; - case pkSnowball: return "Snowball"; - case pkEgg: return "Egg"; - case pkGhastFireball: return "Fireball"; - case pkFireCharge: return "SmallFireball"; - case pkEnderPearl: return "ThrownEnderPearl"; - case pkExpBottle: return "ThrownExpBottle"; - case pkSplashPotion: return "ThrownPotion"; - case pkWitherSkull: return "WitherSkull"; - case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this? - } - ASSERT(!"Unhandled projectile entity kind!"); - return ""; -} - - - - - -void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - BroadcastMovementUpdate(); -} - - - - - -void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - if (m_IsInGround) - { - // Already-grounded projectiles don't move at all - return; - } - - Vector3d PerTickSpeed = GetSpeed() / 20; - Vector3d Pos = GetPosition(); - - // Trace the tick's worth of movement as a line: - Vector3d NextPos = Pos + PerTickSpeed; - cProjectileTracerCallback TracerCallback(this); - if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) - { - // Something has been hit, abort all other processing - return; - } - // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff - - // Test for entity collisions: - cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos); - a_Chunk.ForEachEntity(EntityCollisionCallback); - if (EntityCollisionCallback.HasHit()) - { - // An entity was hit: - Vector3d HitPos = Pos + (NextPos - Pos) * EntityCollisionCallback.GetMinCoeff(); - - // DEBUG: - LOGD("Projectile %d has hit an entity %d (%s) at {%.02f, %.02f, %.02f} (coeff %.03f)", - m_UniqueID, - EntityCollisionCallback.GetHitEntity()->GetUniqueID(), - EntityCollisionCallback.GetHitEntity()->GetClass(), - HitPos.x, HitPos.y, HitPos.z, - EntityCollisionCallback.GetMinCoeff() - ); - - OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos); - } - // TODO: Test the entities in the neighboring chunks, too - - // Update the position: - SetPosition(NextPos); - - // Add slowdown and gravity effect to the speed: - Vector3d NewSpeed(GetSpeed()); - NewSpeed.y += m_Gravity / 20; - NewSpeed *= TracerCallback.GetSlowdownCoeff(); - SetSpeed(NewSpeed); - SetRotationFromSpeed(); - SetPitchFromSpeed(); - - // DEBUG: - LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}", - m_UniqueID, - GetPosX(), GetPosY(), GetPosZ(), - GetSpeedX(), GetSpeedY(), GetSpeedZ(), - GetRotation(), GetPitch() - ); -} - - - - - -void cProjectileEntity::SpawnOn(cClientHandle & a_Client) -{ - // Default spawning - use the projectile kind to spawn an object: - a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetRotation()), ANGLE_TO_PROTO(GetPitch())); - a_Client.SendEntityMetadata(*this); -} - - - - - -void cProjectileEntity::CollectedBy(cPlayer * a_Dest) -{ - // Overriden in arrow - UNUSED(a_Dest); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cArrowEntity: - -cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5), - m_PickupState(psNoPickup), - m_DamageCoeff(2), - m_IsCritical(false), - m_Timer(0), - m_bIsCollected(false), - m_HitBlockPos(Vector3i(0, 0, 0)) -{ - SetSpeed(a_Speed); - SetMass(0.1); - SetRotationFromSpeed(); - SetPitchFromSpeed(); - LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", - m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), - GetRotation(), GetPitch() - ); -} - - - - - -cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) : - super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5), - m_PickupState(psInSurvivalOrCreative), - m_DamageCoeff(2), - m_IsCritical((a_Force >= 1)), - m_Timer(0), - m_bIsCollected(false), - m_HitBlockPos(0, 0, 0) -{ -} - - - - - -bool cArrowEntity::CanPickup(const cPlayer & a_Player) const -{ - switch (m_PickupState) - { - case psNoPickup: return false; - case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative()); - case psInCreative: return a_Player.IsGameModeCreative(); - } - ASSERT(!"Unhandled pickup state"); - return false; -} - - - - - -void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - if (a_HitFace == BLOCK_FACE_NONE) - { - return; - } - - super::OnHitSolidBlock(a_HitPos, a_HitFace); - int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z; - - if (a_HitFace != BLOCK_FACE_YP) - { - AddFaceDirection(a_X, a_Y, a_Z, a_HitFace); - } - else if (a_HitFace == BLOCK_FACE_YP) // These conditions because xoft got a little confused on block face directions, so AddFace works with all but YP & YM - { - a_Y--; - } - else - { - a_Y++; - } - - m_HitBlockPos = Vector3i(a_X, a_Y, a_Z); - - // Broadcast arrow hit sound - m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - - // Broadcast the position and speed packets before teleporting: - BroadcastMovementUpdate(); - - // Teleport the entity to the exact hit coords: - m_World->BroadcastTeleportEntity(*this); -} - - - - - -void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat()) - { - // Not an entity that interacts with an arrow - return; - } - - int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5); - if (m_IsCritical) - { - Damage += m_World->GetTickRandomNumber(Damage / 2 + 2); - } - a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1); - - // Broadcast successful hit sound - m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - - Destroy(); -} - - - - - -void cArrowEntity::CollectedBy(cPlayer * a_Dest) -{ - if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest))) - { - int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW); - if (NumAdded > 0) // Only play effects if there was space in inventory - { - m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest); - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - m_bIsCollected = true; - } - } -} - - - - - -void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - m_Timer += a_Dt; - - if (m_bIsCollected) - { - if (m_Timer > 500.f) // 0.5 seconds - { - Destroy(); - return; - } - } - else if (m_Timer > 1000 * 60 * 5) // 5 minutes - { - Destroy(); - return; - } - - if (m_IsInGround) - { - int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); - - if (Chunk == NULL) - { - // Inside an unloaded chunk, abort - return; - } - - if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed? - { - m_IsInGround = false; // Yes, begin simulating physics again - } - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cThrownEggEntity: - -cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - if (m_World->GetTickRandomNumber(7) == 1) - { - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - } - else if (m_World->GetTickRandomNumber(32) == 1) - { - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - } - Destroy(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cThrownEnderPearlEntity : - -cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - // Teleport the creator here, make them take 5 damage: - if (m_Creator != NULL) - { - // TODO: The coords might need some tweaking based on the block face - m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5); - m_Creator->TakeDamage(dtEnderPearl, this, 5, 0); - } - - Destroy(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cThrownSnowballEntity : - -cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - // TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs - - Destroy(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cGhastFireballEntity : - -cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1) -{ - SetSpeed(a_Speed); - SetGravity(0); -} - - - - - -void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this); -} - - - - - -void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); -} - - - - - -void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFireChargeEntity : - -cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125) -{ - SetSpeed(a_Speed); - SetGravity(0); -} - - - - - -void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) - { - m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1); - } -} - - - - - -void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); -} - - - - - -void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); - - // TODO: Some entities are immune to hits - a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning -} - - - - diff --git a/source/Entities/ProjectileEntity.h b/source/Entities/ProjectileEntity.h deleted file mode 100644 index 28dd76935..000000000 --- a/source/Entities/ProjectileEntity.h +++ /dev/null @@ -1,325 +0,0 @@ - -// ProjectileEntity.h - -// Declares the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types - - - - - -#pragma once - -#include "Entity.h" - - - - - -// tolua_begin - -class cProjectileEntity : - public cEntity -{ - typedef cEntity super; - -public: - /// The kind of the projectile. The numbers correspond to the network type ID used for spawning via the 0x17 packet. - enum eKind - { - pkArrow = 60, - pkSnowball = 61, - pkEgg = 62, - pkGhastFireball = 63, - pkFireCharge = 64, - pkEnderPearl = 65, - pkExpBottle = 75, - pkSplashPotion = 73, - pkWitherSkull = 66, - pkFishingFloat = 90, - } ; - - // tolua_end - - CLASS_PROTODEF(cProjectileEntity); - - cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); - cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height); - - static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL); - - /// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace); - - /// Called by the physics blocktracer when the entity hits another entity - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) {} - - /// Called by Chunk when the projectile is eligible for player collection - virtual void CollectedBy(cPlayer * a_Dest); - - // tolua_begin - - /// Returns the kind of the projectile (fast class identification) - eKind GetProjectileKind(void) const { return m_ProjectileKind; } - - /// Returns the entity who created this projectile; may be NULL - cEntity * GetCreator(void) { return m_Creator; } - - /// Returns the string that is used as the entity type (class name) in MCA files - AString GetMCAClassName(void) const; - - /// Returns true if the projectile has hit the ground and is stuck there - bool IsInGround(void) const { return m_IsInGround; } - - // tolua_end - - /// Sets the internal InGround flag. To be used by MCA loader only! - void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; } - -protected: - eKind m_ProjectileKind; - - /// The entity who has created this projectile; may be NULL (e. g. for dispensers) - cEntity * m_Creator; - - /// True if the projectile has hit the ground and is stuck there - bool m_IsInGround; - - // cEntity overrides: - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - virtual void SpawnOn(cClientHandle & a_Client) override; - - // tolua_begin -} ; - - - - - -class cArrowEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - /// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field - enum ePickupState - { - psNoPickup = 0, - psInSurvivalOrCreative = 1, - psInCreative = 2, - } ; - - // tolua_end - - CLASS_PROTODEF(cArrowEntity); - - /// Creates a new arrow with psNoPickup state and default damage modifier coeff - cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - - /// Creates a new arrow as shot by a player, initializes it from the player object - cArrowEntity(cPlayer & a_Player, double a_Force); - - // tolua_begin - - /// Returns whether the arrow can be picked up by players - ePickupState GetPickupState(void) const { return m_PickupState; } - - /// Sets a new pickup state - void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; } - - /// Returns the damage modifier coeff. - double GetDamageCoeff(void) const { return m_DamageCoeff; } - - /// Sets the damage modifier coeff - void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; } - - /// Returns true if the specified player can pick the arrow up - bool CanPickup(const cPlayer & a_Player) const; - - /// Returns true if the arrow is set as critical - bool IsCritical(void) const { return m_IsCritical; } - - /// Sets the IsCritical flag - void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; } - - // tolua_end - -protected: - - /// Determines when the arrow can be picked up by players - ePickupState m_PickupState; - - /// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow - double m_DamageCoeff; - - /// If true, the arrow deals more damage - bool m_IsCritical; - - /// Timer for pickup collection animation or five minute timeout - float m_Timer; - - /// If true, the arrow is in the process of being collected - don't go to anyone else - bool m_bIsCollected; - - /// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air - Vector3i m_HitBlockPos; - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void CollectedBy(cPlayer * a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - // tolua_begin -} ; - - - - - -class cThrownEggEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cThrownEggEntity); - - cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // tolua_end - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - - // tolua_begin - -} ; - - - - - -class cThrownEnderPearlEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cThrownEnderPearlEntity); - - cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // tolua_end - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - - // tolua_begin - -} ; - - - - - -class cThrownSnowballEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cThrownSnowballEntity); - - cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - - // tolua_begin - -} ; - - - - - -class cGhastFireballEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cGhastFireballEntity); - - cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - - // TODO: Deflecting the fireballs by arrow- or sword- hits - - // tolua_begin - -} ; - - - - - -class cFireChargeEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cFireChargeEntity); - - cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - - // tolua_begin - -} ; - - - - -// tolua_end - - - diff --git a/source/Entities/TNTEntity.cpp b/source/Entities/TNTEntity.cpp deleted file mode 100644 index 339107b2e..000000000 --- a/source/Entities/TNTEntity.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "Globals.h" - -#include "TNTEntity.h" -#include "../World.h" -#include "../ClientHandle.h" - - - - - -cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec) : - super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98), - m_Counter(0), - m_MaxFuseTime(a_FuseTimeInSec) -{ -} - - - - - -cTNTEntity::cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec) : - super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), - m_Counter(0), - m_MaxFuseTime(a_FuseTimeInSec) -{ -} - - - - -void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle) -{ - a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT - m_bDirtyPosition = false; - m_bDirtySpeed = false; - m_bDirtyOrientation = false; - m_bDirtyHead = false; -} - - - - - -void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - BroadcastMovementUpdate(); - float delta_time = a_Dt / 1000; // Convert miliseconds to seconds - m_Counter += delta_time; - if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM - { - Destroy(true); - LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ()); - m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this); - return; - } -} - - - - diff --git a/source/Entities/TNTEntity.h b/source/Entities/TNTEntity.h deleted file mode 100644 index eb5040e8a..000000000 --- a/source/Entities/TNTEntity.h +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma once - -#include "Entity.h" - - - - - -class cTNTEntity : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cTNTEntity); - - cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec); - cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec); - - // cEntity overrides: - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - -protected: - double m_Counter; ///< How much time has elapsed since the object was created, in seconds - double m_MaxFuseTime; ///< How long the fuse is, in seconds -}; - - - - diff --git a/source/FastRandom.cpp b/source/FastRandom.cpp deleted file mode 100644 index 887e4426d..000000000 --- a/source/FastRandom.cpp +++ /dev/null @@ -1,174 +0,0 @@ - -// FastRandom.cpp - -// Implements the cFastRandom class representing a fast random number generator - -#include "Globals.h" -#include -#include "FastRandom.h" - - - - - -#if 0 && defined(_DEBUG) -// Self-test -// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs, -// and if it performs well in terms of distribution (checked by avg, expected to be in the range midpoint -class cFastRandomTest -{ -public: - cFastRandomTest(void) - { - TestInts(); - TestFloats(); - } - - - void TestInts(void) - { - printf("Testing ints...\n"); - cFastRandom rnd; - int sum = 0; - const int BUCKETS = 8; - int Counts[BUCKETS]; - memset(Counts, 0, sizeof(Counts)); - const int ITER = 10000; - for (int i = 0; i < ITER; i++) - { - int v = rnd.NextInt(1000); - ASSERT(v >= 0); - ASSERT(v < 1000); - Counts[v % BUCKETS]++; - sum += v; - } - double avg = (double)sum / ITER; - printf("avg: %f\n", avg); - for (int i = 0; i < BUCKETS; i++) - { - printf(" bucket %d: %d\n", i, Counts[i]); - } - } - - - void TestFloats(void) - { - printf("Testing floats...\n"); - cFastRandom rnd; - float sum = 0; - const int BUCKETS = 8; - int Counts[BUCKETS]; - memset(Counts, 0, sizeof(Counts)); - const int ITER = 10000; - for (int i = 0; i < ITER; i++) - { - float v = rnd.NextFloat(1000); - ASSERT(v >= 0); - ASSERT(v <= 1000); - Counts[((int)v) % BUCKETS]++; - sum += v; - } - sum = sum / ITER; - printf("avg: %f\n", sum); - for (int i = 0; i < BUCKETS; i++) - { - printf(" bucket %d: %d\n", i, Counts[i]); - } - } -} g_Test; - -#endif - - - - - - -int cFastRandom::m_SeedCounter = 0; - - - - - -cFastRandom::cFastRandom(void) : - m_Seed(m_SeedCounter++) -{ -} - - - - - -int cFastRandom::NextInt(int a_Range) -{ - ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges - ASSERT(a_Range > 0); - - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - return ((n / 11) % a_Range); -} - - - - - -int cFastRandom::NextInt(int a_Range, int a_Salt) -{ - ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges - ASSERT(a_Range > 0); - - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - return ((n / 11) % a_Range); -} - - - - - -float cFastRandom::NextFloat(float a_Range) -{ - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - - // Convert the integer into float with the specified range: - return (((float)n / (float)0x7fffffff) * a_Range); -} - - - - - -float cFastRandom::NextFloat(float a_Range, int a_Salt) -{ - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - - // Convert the integer into float with the specified range: - return (((float)n / (float)0x7fffffff) * a_Range); -} - - - - diff --git a/source/FastRandom.h b/source/FastRandom.h deleted file mode 100644 index bf70822cf..000000000 --- a/source/FastRandom.h +++ /dev/null @@ -1,57 +0,0 @@ - -// FastRandom.h - -// Declares the cFastRandom class representing a fast random number generator - -/* -The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator. -It is fast to instantiate, fast to query next, and partially multi-thread-safe. -It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may -yield duplicate numbers in that case. - -Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is -taken from a global counter and the random is calculated using a counter that is incremented on each use (hence -the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter, -and another that takes an additional "salt" parameter; this salt is used as an additional input to the random, -in order to avoid multi-thread duplication. If two threads both use the class at the same time with different -salts, the values they get will be different. -*/ - - - - - -#pragma once - - - - - -class cFastRandom -{ -public: - cFastRandom(void); - - /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M - int NextInt(int a_Range); - - /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness - int NextInt(int a_Range, int a_Salt); - - /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M - float NextFloat(float a_Range); - - /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness - float NextFloat(float a_Range, int a_Salt); - -protected: - int m_Seed; - int m_Counter; - - /// Counter that is used to initialize the seed, incremented for each object created - static int m_SeedCounter; -} ; - - - - diff --git a/source/FurnaceRecipe.cpp b/source/FurnaceRecipe.cpp deleted file mode 100644 index 2e2276981..000000000 --- a/source/FurnaceRecipe.cpp +++ /dev/null @@ -1,255 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "FurnaceRecipe.h" -#include "Item.h" - -#include -#include - - - - - -typedef std::list< cFurnaceRecipe::Recipe > RecipeList; -typedef std::list< cFurnaceRecipe::Fuel > FuelList; - - - - - -struct cFurnaceRecipe::sFurnaceRecipeState -{ - RecipeList Recipes; - FuelList Fuel; -}; - - - - - -cFurnaceRecipe::cFurnaceRecipe() - : m_pState( new sFurnaceRecipeState ) -{ - ReloadRecipes(); -} - - - - - -cFurnaceRecipe::~cFurnaceRecipe() -{ - ClearRecipes(); - delete m_pState; -} - - - - - -void cFurnaceRecipe::ReloadRecipes(void) -{ - ClearRecipes(); - LOGD("Loading furnace recipes..."); - - std::ifstream f; - char a_File[] = "furnace.txt"; - f.open(a_File, std::ios::in); - std::string input; - - if (!f.good()) - { - f.close(); - LOG("Could not open the furnace recipes file \"%s\"", a_File); - return; - } - - // TODO: Replace this messy parse with a high-level-structured one (ReadLine / ProcessLine) - bool bSyntaxError = false; - while (f.good()) - { - char c; - - ////////////////////////////////////////////////////////////////////////// - // comments - f >> c; - f.unget(); - if( c == '#' ) - { - while( f.good() && c != '\n' ) - { - f.get( c ); - } - continue; - } - - - ////////////////////////////////////////////////////////////////////////// - // Line breaks - f.get( c ); - while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); } - if (f.eof()) - { - break; - } - f.unget(); - - ////////////////////////////////////////////////////////////////////////// - // Check for fuel - f >> c; - if( c == '!' ) // It's fuel :) - { - // Read item - int IItemID = 0, IItemCount = 0, IItemHealth = 0; - f >> IItemID; - f >> c; if( c != ':' ) { bSyntaxError = true; break; } - f >> IItemCount; - - // Optional health - f >> c; - if( c != ':' ) - f.unget(); - else - { - f >> IItemHealth; - } - - // Burn time - int BurnTime; - f >> c; if( c != '=' ) { bSyntaxError = true; break; } - f >> BurnTime; - - // Add to fuel list - Fuel F; - F.In = new cItem( (ENUM_ITEM_ID) IItemID, (char)IItemCount, (short)IItemHealth ); - F.BurnTime = BurnTime; - m_pState->Fuel.push_back( F ); - continue; - } - f.unget(); - - ////////////////////////////////////////////////////////////////////////// - // Read items - int IItemID = 0, IItemCount = 0, IItemHealth = 0; - f >> IItemID; - f >> c; if( c != ':' ) { bSyntaxError = true; break; } - f >> IItemCount; - - // Optional health - f >> c; - if( c != ':' ) - f.unget(); - else - { - f >> IItemHealth; - } - - int CookTime; - f >> c; if( c != '@' ) { bSyntaxError = true; break; } - f >> CookTime; - - int OItemID = 0, OItemCount = 0, OItemHealth = 0; - f >> c; if( c != '=' ) { bSyntaxError = true; break; } - f >> OItemID; - f >> c; if( c != ':' ) { bSyntaxError = true; break; } - f >> OItemCount; - - // Optional health - f >> c; - if( c != ':' ) - f.unget(); - else - { - f >> OItemHealth; - } - - // Add to recipe list - Recipe R; - R.In = new cItem( (ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth ); - R.Out = new cItem( (ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth ); - R.CookTime = CookTime; - m_pState->Recipes.push_back( R ); - } - if (bSyntaxError) - { - LOGERROR("ERROR: FurnaceRecipe, syntax error" ); - } - LOG("Loaded %u furnace recipes and %u fuels", m_pState->Recipes.size(), m_pState->Fuel.size()); -} - - - - - -void cFurnaceRecipe::ClearRecipes(void) -{ - for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr) - { - Recipe R = *itr; - delete R.In; - delete R.Out; - } - m_pState->Recipes.clear(); - - for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) - { - Fuel F = *itr; - delete F.In; - } - m_pState->Fuel.clear(); -} - - - - - -const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const -{ - const Recipe * BestRecipe = 0; - for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr) - { - const Recipe & R = *itr; - if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount)) - { - if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount)) - { - continue; - } - else - { - BestRecipe = &R; - } - } - } - return BestRecipe; -} - - - - - -int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const -{ - int BestFuel = 0; - for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) - { - const Fuel & F = *itr; - if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount)) - { - if (BestFuel > 0 && (BestFuel > F.BurnTime)) - { - continue; - } - else - { - BestFuel = F.BurnTime; - } - } - } - return BestFuel; -} - - - - diff --git a/source/FurnaceRecipe.h b/source/FurnaceRecipe.h deleted file mode 100644 index 2f91e9bcb..000000000 --- a/source/FurnaceRecipe.h +++ /dev/null @@ -1,50 +0,0 @@ - -#pragma once - - - - - -class cItem; - - - - - -class cFurnaceRecipe -{ -public: - cFurnaceRecipe(void); - ~cFurnaceRecipe(); - - void ReloadRecipes(void); - - struct Fuel - { - cItem * In; - int BurnTime; ///< How long this fuel burns, in ticks - }; - - struct Recipe - { - cItem * In; - cItem * Out; - int CookTime; ///< How long this recipe takes to smelt, in ticks - }; - - /// Returns a recipe for the specified input, NULL if no recipe found - const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const; - - /// Returns the amount of time that the specified fuel burns, in ticks - int GetBurnTime(const cItem & a_Fuel) const; - -private: - void ClearRecipes(void); - - struct sFurnaceRecipeState; - sFurnaceRecipeState * m_pState; -}; - - - - diff --git a/source/Generating/BioGen.cpp b/source/Generating/BioGen.cpp deleted file mode 100644 index 926120afc..000000000 --- a/source/Generating/BioGen.cpp +++ /dev/null @@ -1,707 +0,0 @@ - -// BioGen.cpp - -// Implements the various biome generators - -#include "Globals.h" -#include "BioGen.h" -#include "../../iniFile/iniFile.h" -#include "../LinearUpscale.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBioGenConstant: - -void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) - { - a_BiomeMap[i] = m_Biome; - } -} - - - - - -void cBioGenConstant::InitializeBiomeGen(cIniFile & a_IniFile) -{ - AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "Plains"); - m_Biome = StringToBiome(Biome); - if (m_Biome == -1) - { - LOGWARN("[Generator]::ConstantBiome value \"%s\" not recognized, using \"Plains\".", Biome.c_str()); - m_Biome = biPlains; - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBioGenCache: - -cBioGenCache::cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize) : - m_BioGenToCache(a_BioGenToCache), - m_CacheSize(a_CacheSize), - m_CacheOrder(new int[a_CacheSize]), - m_CacheData(new sCacheData[a_CacheSize]), - m_NumHits(0), - m_NumMisses(0), - m_TotalChain(0) -{ - for (int i = 0; i < m_CacheSize; i++) - { - m_CacheOrder[i] = i; - m_CacheData[i].m_ChunkX = 0x7fffffff; - m_CacheData[i].m_ChunkZ = 0x7fffffff; - } -} - - - - - -cBioGenCache::~cBioGenCache() -{ - delete[] m_CacheData; - delete[] m_CacheOrder; -} - - - - - -void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - if (((m_NumHits + m_NumMisses) % 1024) == 10) - { - LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); - LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); - } - - for (int i = 0; i < m_CacheSize; i++) - { - if ( - (m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) || - (m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ) - ) - { - continue; - } - // Found it in the cache - int Idx = m_CacheOrder[i]; - - // Move to front: - for (int j = i; j > 0; j--) - { - m_CacheOrder[j] = m_CacheOrder[j - 1]; - } - m_CacheOrder[0] = Idx; - - // Use the cached data: - memcpy(a_BiomeMap, m_CacheData[Idx].m_BiomeMap, sizeof(a_BiomeMap)); - - m_NumHits++; - m_TotalChain += i; - return; - } // for i - cache - - // Not in the cache: - m_NumMisses++; - m_BioGenToCache->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); - - // Insert it as the first item in the MRU order: - int Idx = m_CacheOrder[m_CacheSize - 1]; - for (int i = m_CacheSize - 1; i > 0; i--) - { - m_CacheOrder[i] = m_CacheOrder[i - 1]; - } // for i - m_CacheOrder[] - m_CacheOrder[0] = Idx; - memcpy(m_CacheData[Idx].m_BiomeMap, a_BiomeMap, sizeof(a_BiomeMap)); - m_CacheData[Idx].m_ChunkX = a_ChunkX; - m_CacheData[Idx].m_ChunkZ = a_ChunkZ; -} - - - - - -void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile) -{ - super::InitializeBiomeGen(a_IniFile); - m_BioGenToCache->InitializeBiomeGen(a_IniFile); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBiomeGenList: - -void cBiomeGenList::InitializeBiomes(const AString & a_Biomes) -{ - AStringVector Split = StringSplit(a_Biomes, ","); - - // Convert each string in the list into biome: - for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr) - { - AStringVector Split2 = StringSplit(*itr, ":"); - if (Split2.size() < 1) - { - continue; - } - int Count = 1; - if (Split2.size() >= 2) - { - Count = atol(Split2[1].c_str()); - if (Count <= 0) - { - LOGWARNING("Cannot decode biome count: \"%s\"; using 1.", Split2[1].c_str()); - Count = 1; - } - } - EMCSBiome Biome = StringToBiome(Split2[0]); - if (Biome != -1) - { - for (int i = 0; i < Count; i++) - { - m_Biomes.push_back(Biome); - } - } - else - { - LOGWARNING("Cannot decode biome name: \"%s\"; skipping", Split2[0].c_str()); - } - } // for itr - Split[] - if (!m_Biomes.empty()) - { - m_BiomesCount = (int)m_Biomes.size(); - return; - } - - // There were no biomes, add default biomes: - static EMCSBiome Biomes[] = - { - biOcean, - biPlains, - biDesert, - biExtremeHills, - biForest, - biTaiga, - biSwampland, - biRiver, - biFrozenOcean, - biFrozenRiver, - biIcePlains, - biIceMountains, - biMushroomIsland, - biMushroomShore, - biBeach, - biDesertHills, - biForestHills, - biTaigaHills, - biExtremeHillsEdge, - biJungle, - biJungleHills, - } ; - m_Biomes.reserve(ARRAYCOUNT(Biomes)); - for (int i = 0; i < ARRAYCOUNT(Biomes); i++) - { - m_Biomes.push_back(Biomes[i]); - } - m_BiomesCount = (int)m_Biomes.size(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBioGenCheckerboard: - -void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - for (int z = 0; z < cChunkDef::Width; z++) - { - int Base = cChunkDef::Width * a_ChunkZ + z; - for (int x = 0; x < cChunkDef::Width; x++) - { - int Add = cChunkDef::Width * a_ChunkX + x; - a_BiomeMap[x + cChunkDef::Width * z] = m_Biomes[(Base / m_BiomeSize + Add / m_BiomeSize) % m_BiomesCount]; - } - } -} - - - - - -void cBioGenCheckerboard::InitializeBiomeGen(cIniFile & a_IniFile) -{ - super::InitializeBiomeGen(a_IniFile); - AString Biomes = a_IniFile.GetValueSet ("Generator", "CheckerBoardBiomes", ""); - m_BiomeSize = a_IniFile.GetValueSetI("Generator", "CheckerboardBiomeSize", 64); - m_BiomeSize = (m_BiomeSize < 8) ? 8 : m_BiomeSize; - InitializeBiomes(Biomes); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBioGenVoronoi : - -void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - int BaseZ = cChunkDef::Width * a_ChunkZ; - int BaseX = cChunkDef::Width * a_ChunkX; - for (int z = 0; z < cChunkDef::Width; z++) - { - int AbsoluteZ = BaseZ + z; - for (int x = 0; x < cChunkDef::Width; x++) - { - cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(BaseX + x, AbsoluteZ)); - } // for x - } // for z -} - - - - - -void cBioGenVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) -{ - super::InitializeBiomeGen(a_IniFile); - m_CellSize = a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64); - AString Biomes = a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", ""); - InitializeBiomes(Biomes); -} - - - - - -EMCSBiome cBioGenVoronoi::VoronoiBiome(int a_BlockX, int a_BlockZ) -{ - int CellX = a_BlockX / m_CellSize; - int CellZ = a_BlockZ / m_CellSize; - - // Note that Noise values need to be divided by 8 to gain a uniform modulo-2^n distribution - - // Get 5x5 neighboring cell seeds, compare distance to each. Return the biome in the minumim-distance cell - int MinDist = m_CellSize * m_CellSize * 16; // There has to be a cell closer than this - EMCSBiome res = biPlains; // Will be overriden - for (int x = CellX - 2; x <= CellX + 2; x++) - { - int BaseX = x * m_CellSize; - for (int z = CellZ - 2; z < CellZ + 2; z++) - { - int OffsetX = (m_Noise.IntNoise3DInt(x, 16 * x + 32 * z, z) / 8) % m_CellSize; - int OffsetZ = (m_Noise.IntNoise3DInt(x, 32 * x - 16 * z, z) / 8) % m_CellSize; - int SeedX = BaseX + OffsetX; - int SeedZ = z * m_CellSize + OffsetZ; - - int Dist = (SeedX - a_BlockX) * (SeedX - a_BlockX) + (SeedZ - a_BlockZ) * (SeedZ - a_BlockZ); - if (Dist < MinDist) - { - MinDist = Dist; - res = m_Biomes[(m_Noise.IntNoise3DInt(x, x - z + 1000, z) / 8) % m_BiomesCount]; - } - } // for z - } // for x - - return res; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBioGenDistortedVoronoi: - -void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - int BaseZ = cChunkDef::Width * a_ChunkZ; - int BaseX = cChunkDef::Width * a_ChunkX; - - // Distortions for linear interpolation: - int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1]; - int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1]; - for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++) - { - Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z]); - } - - LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); - LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); - - for (int z = 0; z < cChunkDef::Width; z++) - { - int AbsoluteZ = BaseZ + z; - for (int x = 0; x < cChunkDef::Width; x++) - { - // Distort(BaseX + x, AbsoluteZ, DistX, DistZ); - cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(DistortX[x][z], DistortZ[x][z])); - } // for x - } // for z -} - - - - - -void cBioGenDistortedVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) -{ - // Do NOT call super::InitializeBiomeGen(), as it would try to read Voronoi params instead of DistortedVoronoi params - m_CellSize = a_IniFile.GetValueSetI("Generator", "DistortedVoronoiCellSize", 96); - AString Biomes = a_IniFile.GetValueSet ("Generator", "DistortedVoronoiBiomes", ""); - InitializeBiomes(Biomes); -} - - - - -void cBioGenDistortedVoronoi::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ) -{ - double NoiseX = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 1000); - NoiseX += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 2000); - NoiseX += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 3000); - double NoiseZ = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 4000); - NoiseZ += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 5000); - NoiseZ += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 6000); - - a_DistortedX = a_BlockX + (int)(m_CellSize * 0.5 * NoiseX); - a_DistortedZ = a_BlockZ + (int)(m_CellSize * 0.5 * NoiseZ); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBioGenMultiStepMap : - -cBioGenMultiStepMap::cBioGenMultiStepMap(int a_Seed) : - m_Noise1(a_Seed + 1000), - m_Noise2(a_Seed + 2000), - m_Noise3(a_Seed + 3000), - m_Noise4(a_Seed + 4000), - m_Noise5(a_Seed + 5000), - m_Noise6(a_Seed + 6000), - m_Seed(a_Seed), - m_OceanCellSize(384), - m_MushroomIslandSize(64), - m_RiverCellSize(384), - m_RiverWidthThreshold(0.125), - m_LandBiomesSize(1024) -{ -} - - - - - -void cBioGenMultiStepMap::InitializeBiomeGen(cIniFile & a_IniFile) -{ - m_OceanCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapOceanCellSize", m_OceanCellSize); - m_MushroomIslandSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapMushroomIslandSize", m_MushroomIslandSize); - m_RiverCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapRiverCellSize", m_RiverCellSize); - m_RiverWidthThreshold = a_IniFile.GetValueSetF("Generator", "MultiStepMapRiverWidth", m_RiverWidthThreshold); - m_LandBiomesSize = (float)a_IniFile.GetValueSetI("Generator", "MultiStepMapLandBiomeSize", (int)m_LandBiomesSize); -} - - - - - -void cBioGenMultiStepMap::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - DecideOceanLandMushroom(a_ChunkX, a_ChunkZ, a_BiomeMap); - AddRivers(a_ChunkX, a_ChunkZ, a_BiomeMap); - ApplyTemperatureHumidity(a_ChunkX, a_ChunkZ, a_BiomeMap); -} - - - - - -void cBioGenMultiStepMap::DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - // Distorted Voronoi over 3 biomes, with mushroom having only a special occurence. - - // Prepare a distortion lookup table, by distorting a 5x5 area and using that as 1:4 zoom (linear interpolate): - int BaseZ = cChunkDef::Width * a_ChunkZ; - int BaseX = cChunkDef::Width * a_ChunkX; - int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1]; - int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1]; - int DistortSize = m_OceanCellSize / 2; - for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++) - { - Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z], DistortSize); - } - LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); - LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); - - // Prepare a 9x9 area of neighboring cell seeds - // (assuming that 7x7 cell area is larger than a chunk being generated) - const int NEIGHBORHOOD_SIZE = 4; // How many seeds in each direction to check - int CellX = BaseX / m_OceanCellSize; - int CellZ = BaseZ / m_OceanCellSize; - int SeedX[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1]; - int SeedZ[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1]; - EMCSBiome SeedV[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1]; - for (int xc = 0; xc < 2 * NEIGHBORHOOD_SIZE + 1; xc++) - { - int RealCellX = xc + CellX - NEIGHBORHOOD_SIZE; - int CellBlockX = RealCellX * m_OceanCellSize; - for (int zc = 0; zc < 2 * NEIGHBORHOOD_SIZE + 1; zc++) - { - int RealCellZ = zc + CellZ - NEIGHBORHOOD_SIZE; - int CellBlockZ = RealCellZ * m_OceanCellSize; - int OffsetX = (m_Noise2.IntNoise3DInt(RealCellX, 16 * RealCellX + 32 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize; - int OffsetZ = (m_Noise4.IntNoise3DInt(RealCellX, 32 * RealCellX - 16 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize; - SeedX[xc][zc] = CellBlockX + OffsetX; - SeedZ[xc][zc] = CellBlockZ + OffsetZ; - SeedV[xc][zc] = (((m_Noise6.IntNoise3DInt(RealCellX, RealCellX - RealCellZ + 1000, RealCellZ) / 11) % 256) > 90) ? biOcean : ((EMCSBiome)(-1)); - } // for z - } // for x - - for (int xc = 1; xc < 2 * NEIGHBORHOOD_SIZE; xc++) for (int zc = 1; zc < 2 * NEIGHBORHOOD_SIZE; zc++) - { - if ( - (SeedV[xc ][zc] == biOcean) && - (SeedV[xc - 1][zc] == biOcean) && - (SeedV[xc + 1][zc] == biOcean) && - (SeedV[xc ][zc - 1] == biOcean) && - (SeedV[xc ][zc + 1] == biOcean) && - (SeedV[xc - 1][zc - 1] == biOcean) && - (SeedV[xc + 1][zc - 1] == biOcean) && - (SeedV[xc - 1][zc + 1] == biOcean) && - (SeedV[xc + 1][zc + 1] == biOcean) - ) - { - SeedV[xc][zc] = biMushroomIsland; - } - } - - // For each column find the nearest distorted cell and use its value as the biome: - int MushroomOceanThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 1024; - int MushroomShoreThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 2048; - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int AbsoluteZ = DistortZ[x][z]; - int AbsoluteX = DistortX[x][z]; - int MinDist = m_OceanCellSize * m_OceanCellSize * 16; // There has to be a cell closer than this - EMCSBiome Biome = biPlains; - // Find the nearest cell seed: - for (int xs = 1; xs < 2 * NEIGHBORHOOD_SIZE; xs++) for (int zs = 1; zs < 2 * NEIGHBORHOOD_SIZE; zs++) - { - int Dist = (SeedX[xs][zs] - AbsoluteX) * (SeedX[xs][zs] - AbsoluteX) + (SeedZ[xs][zs] - AbsoluteZ) * (SeedZ[xs][zs] - AbsoluteZ); - if (Dist >= MinDist) - { - continue; - } - MinDist = Dist; - Biome = SeedV[xs][zs]; - // Shrink mushroom biome and add a shore: - if (Biome == biMushroomIsland) - { - if (Dist > MushroomOceanThreshold) - { - Biome = biOcean; - } - else if (Dist > MushroomShoreThreshold) - { - Biome = biMushroomShore; - } - } - } // for zs, xs - - cChunkDef::SetBiome(a_BiomeMap, x, z, Biome); - } // for x - } // for z -} - - - - - -void cBioGenMultiStepMap::AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - for (int z = 0; z < cChunkDef::Width; z++) - { - float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_RiverCellSize; - for (int x = 0; x < cChunkDef::Width; x++) - { - if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1) - { - // Biome already set, skip this column - continue; - } - - float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_RiverCellSize; - - double Noise = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ); - Noise += 0.5 * m_Noise3.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ); - Noise += 0.1 * m_Noise5.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ); - - if ((Noise > 0) && (Noise < m_RiverWidthThreshold)) - { - cChunkDef::SetBiome(a_BiomeMap, x, z, biRiver); - } - } // for x - } // for z -} - - - - - -void cBioGenMultiStepMap::ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - IntMap TemperatureMap; - IntMap HumidityMap; - BuildTemperatureHumidityMaps(a_ChunkX, a_ChunkZ, TemperatureMap, HumidityMap); - - FreezeWaterBiomes(a_BiomeMap, TemperatureMap); - DecideLandBiomes(a_BiomeMap, TemperatureMap, HumidityMap); -} - - - - - -void cBioGenMultiStepMap::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize) -{ - double NoiseX = m_Noise3.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize); - NoiseX += 0.5 * m_Noise2.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize); - NoiseX += 0.1 * m_Noise1.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize); - double NoiseZ = m_Noise6.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize); - NoiseZ += 0.5 * m_Noise5.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize); - NoiseZ += 0.1 * m_Noise4.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize); - - a_DistortedX = a_BlockX + (int)(a_CellSize * 0.5 * NoiseX); - a_DistortedZ = a_BlockZ + (int)(a_CellSize * 0.5 * NoiseZ); -} - - - - - -void cBioGenMultiStepMap::BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap) -{ - // Linear interpolation over 8x8 blocks; use double for better precision: - DblMap TemperatureMap; - DblMap HumidityMap; - for (int z = 0; z < 17; z += 8) - { - float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_LandBiomesSize; - for (int x = 0; x < 17; x += 8) - { - float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_LandBiomesSize; - - double NoiseT = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ); - NoiseT += 0.5 * m_Noise2.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ); - NoiseT += 0.1 * m_Noise3.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ); - TemperatureMap[x + 17 * z] = NoiseT; - - double NoiseH = m_Noise4.CubicNoise2D( NoiseCoordX, NoiseCoordZ); - NoiseH += 0.5 * m_Noise5.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ); - NoiseH += 0.1 * m_Noise6.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ); - HumidityMap[x + 17 * z] = NoiseH; - } // for x - } // for z - LinearUpscale2DArrayInPlace(TemperatureMap, 17, 17, 8, 8); - LinearUpscale2DArrayInPlace(HumidityMap, 17, 17, 8, 8); - - // Re-map into integral values in [0 .. 255] range: - for (int idx = 0; idx < ARRAYCOUNT(a_TemperatureMap); idx++) - { - a_TemperatureMap[idx] = std::max(0, std::min(255, (int)(128 + TemperatureMap[idx] * 128))); - a_HumidityMap[idx] = std::max(0, std::min(255, (int)(128 + HumidityMap[idx] * 128))); - } -} - - - - - -void cBioGenMultiStepMap::DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap) -{ - static const EMCSBiome BiomeMap[] = - { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - /* 0 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, - /* 1 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, - /* 2 */ biTundra, biTundra, biTundra, biTundra, biPlains, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert, - /* 3 */ biTundra, biTundra, biTundra, biTundra, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert, - /* 4 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert, - /* 5 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert, - /* 6 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, - /* 7 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, - /* 8 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, - /* 9 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, - /* 10 */ biTaiga, biTaiga, biTaiga, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, - /* 11 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, - /* 12 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, - /* 13 */ biTaiga, biTaiga, biTaiga, biIceMountains, biJungleHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, - /* 14 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, - /* 15 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, - } ; - for (int z = 0; z < cChunkDef::Width; z++) - { - int idxZ = 17 * z; - for (int x = 0; x < cChunkDef::Width; x++) - { - if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1) - { - // Already set before - continue; - } - int idx = idxZ + x; - int Temperature = a_TemperatureMap[idx] / 16; // -> [0..15] range - int Humidity = a_HumidityMap[idx] / 16; // -> [0..15] range - cChunkDef::SetBiome(a_BiomeMap, x, z, BiomeMap[Temperature + 16 * Humidity]); - } // for x - } // for z -} - - - - - -void cBioGenMultiStepMap::FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap) -{ - int idx = 0; - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++, idx++) - { - if (a_TemperatureMap[idx] > 4 * 16) - { - // Not frozen - continue; - } - switch (cChunkDef::GetBiome(a_BiomeMap, x, z)) - { - case biRiver: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenRiver); break; - case biOcean: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenOcean); break; - } - } // for x - idx += 1; - } // for z -} - - - - diff --git a/source/Generating/BioGen.h b/source/Generating/BioGen.h deleted file mode 100644 index bc70bfab2..000000000 --- a/source/Generating/BioGen.h +++ /dev/null @@ -1,230 +0,0 @@ - -// BioGen.h - -/* -Interfaces to the various biome generators: - - cBioGenConstant - - cBioGenCheckerboard - - cBioGenDistortedVoronoi -*/ - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cBioGenConstant : - public cBiomeGen -{ -public: - cBioGenConstant(void) : m_Biome(biPlains) {} - -protected: - - EMCSBiome m_Biome; - - // cBiomeGen overrides: - virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; -} ; - - - - - -/// A simple cache that stores N most recently generated chunks' biomes; N being settable upon creation -class cBioGenCache : - public cBiomeGen -{ - typedef cBiomeGen super; - -public: - cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize); // Doesn't take ownership of a_BioGenToCache - ~cBioGenCache(); - -protected: - - cBiomeGen * m_BioGenToCache; - - struct sCacheData - { - int m_ChunkX; - int m_ChunkZ; - cChunkDef::BiomeMap m_BiomeMap; - } ; - - // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data - int m_CacheSize; - int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array - sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used - - // Cache statistics - int m_NumHits; - int m_NumMisses; - int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) - - virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; -} ; - - - - - -/// Base class for generators that use a list of available biomes. This class takes care of the list. -class cBiomeGenList : - public cBiomeGen -{ - typedef cBiomeGen super; - -protected: - // List of biomes that the generator is allowed to generate: - typedef std::vector EMCSBiomes; - EMCSBiomes m_Biomes; - int m_BiomesCount; // Pulled out of m_Biomes for faster access - - /// Parses the INI file setting string into m_Biomes. - void InitializeBiomes(const AString & a_Biomes); -} ; - - - - - -class cBioGenCheckerboard : - public cBiomeGenList -{ - typedef cBiomeGenList super; - -protected: - int m_BiomeSize; - - // cBiomeGen overrides: - virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; -} ; - - - - - -class cBioGenVoronoi : - public cBiomeGenList -{ - typedef cBiomeGenList super; - -public: - cBioGenVoronoi(int a_Seed) : - m_Noise(a_Seed) - { - } - -protected: - int m_CellSize; - - cNoise m_Noise; - - // cBiomeGen overrides: - virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; - - EMCSBiome VoronoiBiome(int a_BlockX, int a_BlockZ); -} ; - - - - - -class cBioGenDistortedVoronoi : - public cBioGenVoronoi -{ - typedef cBioGenVoronoi super; -public: - cBioGenDistortedVoronoi(int a_Seed) : - cBioGenVoronoi(a_Seed) - { - } - -protected: - // cBiomeGen overrides: - virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; - - /// Distorts the coords using a Perlin-like noise - void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ); -} ; - - - - - -class cBioGenMultiStepMap : - public cBiomeGen -{ - typedef cBiomeGen super; - -public: - cBioGenMultiStepMap(int a_Seed); - -protected: - // Noises used for composing the perlin-noise: - cNoise m_Noise1; - cNoise m_Noise2; - cNoise m_Noise3; - cNoise m_Noise4; - cNoise m_Noise5; - cNoise m_Noise6; - - int m_Seed; - int m_OceanCellSize; - int m_MushroomIslandSize; - int m_RiverCellSize; - double m_RiverWidthThreshold; - float m_LandBiomesSize; - - typedef int IntMap[17 * 17]; // x + 17 * z, expected trimmed into [0..255] range - typedef double DblMap[17 * 17]; // x + 17 * z, expected trimmed into [0..1] range - - // cBiomeGen overrides: - virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; - - /** Step 1: Decides between ocean, land and mushroom, using a DistVoronoi with special conditions and post-processing for mushroom islands - Sets biomes to biOcean, -1 (i.e. land), biMushroomIsland or biMushroomShore - */ - void DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); - - /** Step 2: Add rivers to the land - Flips some "-1" biomes into biRiver - */ - void AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); - - /** Step 3: Decide land biomes using a temperature / humidity map; freeze ocean / river in low temperatures. - Flips all remaining "-1" biomes into land biomes. Also flips some biOcean and biRiver into biFrozenOcean, biFrozenRiver, based on temp map. - */ - void ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); - - /// Distorts the coords using a Perlin-like noise, with a specified cell-size - void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize); - - /// Builds two Perlin-noise maps, one for temperature, the other for humidity. Trims both into [0..255] range - void BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap); - - /// Flips all remaining "-1" biomes into land biomes using the two maps - void DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap); - - /// Flips biOcean and biRiver into biFrozenOcean and biFrozenRiver if the temperature is too low - void FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap); -} ; - - - - diff --git a/source/Generating/Caves.cpp b/source/Generating/Caves.cpp deleted file mode 100644 index 4221ea187..000000000 --- a/source/Generating/Caves.cpp +++ /dev/null @@ -1,970 +0,0 @@ - -// Caves.cpp - -// Implements the various cave structure generators: -// - cStructGenWormNestCaves -// - cStructGenDualRidgeCaves -// - cStructGenMarbleCaves -// - cStructGenNetherCaves - -/* -WormNestCave generator: -Caves are generated in "nests" - groups of tunnels generated from a single point. -For each chunk, all the nests that could intersect it are generated. -For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...) -Then each tunnel is randomized by inserting points in between its ends. -Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other. -When the tunnels are ready, they are simply carved into the chunk, one by one. -To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it. - -MarbleCaves generator: -For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept. -Problem with this is the amount of CPU work needed for each chunk. -Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground. - -DualRidgeCaves generator: -Instead of evaluating a single noise function, two different noise functions are multiplied. This produces -regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be -reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best. -*/ - -#include "Globals.h" -#include "Caves.h" - - - - - -/// How many nests in each direction are generated for a given chunk. Must be an even number -#define NEIGHBORHOOD_SIZE 8 - - - - - -const int MIN_RADIUS = 3; -const int MAX_RADIUS = 8; - - - - - -struct cCaveDefPoint -{ - int m_BlockX; - int m_BlockY; - int m_BlockZ; - int m_Radius; - - cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) : - m_BlockX(a_BlockX), - m_BlockY(a_BlockY), - m_BlockZ(a_BlockZ), - m_Radius(a_Radius) - { - } -} ; - -typedef std::vector cCaveDefPoints; - - - - - -/// A single non-branching tunnel of a WormNestCave -class cCaveTunnel -{ - // The bounding box, including the radii around defpoints: - int m_MinBlockX, m_MaxBlockX; - int m_MinBlockY, m_MaxBlockY; - int m_MinBlockZ, m_MaxBlockZ; - - /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise - void Randomize(cNoise & a_Noise); - - /// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short) - bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst); - - /// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true - void Smooth(void); - - /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block - void FinishLinear(void); - - /// Calculates the bounding box of the points present - void CalcBoundingBox(void); - -public: - cCaveDefPoints m_Points; - - cCaveTunnel( - int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, - int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, - cNoise & a_Noise - ); - - /// Carves the tunnel into the chunk specified - void ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap - ); - - #ifdef _DEBUG - AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; - #endif // _DEBUG -} ; - -typedef std::vector cCaveTunnels; - - - - - -/// A collection of connected tunnels, possibly branching. -class cStructGenWormNestCaves::cCaveSystem -{ -public: - // The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk() - int m_BlockX; - int m_BlockZ; - - cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise); - ~cCaveSystem(); - - /// Carves the cave system into the chunk specified - void ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap - ); - - #ifdef _DEBUG - AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; - #endif // _DEBUG - -protected: - int m_Size; - cCaveTunnels m_Tunnels; - - void Clear(void); - - /// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments] - void GenerateTunnelsFromPoint( - int a_OriginX, int a_OriginY, int a_OriginZ, - cNoise & a_Noise, int a_Segments - ); - - /// Returns a radius based on the location provided. - int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ); -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCaveTunnel: - -cCaveTunnel::cCaveTunnel( - int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, - int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, - cNoise & a_Noise -) -{ - m_Points.push_back(cCaveDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius)); - m_Points.push_back(cCaveDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius)); - - if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0)) - { - // Don't bother detailing this cave, it's under the world anyway - return; - } - - Randomize(a_Noise); - Smooth(); - - // We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data: - CalcBoundingBox(); - - FinishLinear(); -} - - - - - -void cCaveTunnel::Randomize(cNoise & a_Noise) -{ - // Repeat 4 times: - for (int i = 0; i < 4; i++) - { - // For each already present point, insert a point in between it and its predecessor, shifted randomly. - int PrevX = m_Points.front().m_BlockX; - int PrevY = m_Points.front().m_BlockY; - int PrevZ = m_Points.front().m_BlockZ; - int PrevR = m_Points.front().m_Radius; - cCaveDefPoints Pts; - Pts.reserve(m_Points.size() * 2 + 1); - Pts.push_back(m_Points.front()); - for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) - { - int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11; - int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX); - len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY); - len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ); - len = 3 * (int)sqrt((double)len) / 4; - int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1)); - Random /= 4; - int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2); - Random /= 256; - int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4); - Random /= 256; - int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2); - Pts.push_back(cCaveDefPoint(x, y, z, Rad)); - Pts.push_back(*itr); - PrevX = itr->m_BlockX; - PrevY = itr->m_BlockY; - PrevZ = itr->m_BlockZ; - PrevR = itr->m_Radius; - } - std::swap(Pts, m_Points); - } -} - - - - - -bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst) -{ - // Smoothing: for each line segment, add points on its 1/4 lengths - bool res = false; - int Num = a_Src.size() - 2; // this many intermediary points - a_Dst.clear(); - a_Dst.reserve(Num * 2 + 2); - cCaveDefPoints::const_iterator itr = a_Src.begin() + 1; - a_Dst.push_back(a_Src.front()); - int PrevX = a_Src.front().m_BlockX; - int PrevY = a_Src.front().m_BlockY; - int PrevZ = a_Src.front().m_BlockZ; - int PrevR = a_Src.front().m_Radius; - for (int i = 0; i <= Num; ++i, ++itr) - { - int dx = itr->m_BlockX - PrevX; - int dy = itr->m_BlockY - PrevY; - int dz = itr->m_BlockZ - PrevZ; - if (abs(dx) + abs(dz) + abs(dy) < 6) - { - // Too short a segment to smooth-subdivide into quarters - PrevX = itr->m_BlockX; - PrevY = itr->m_BlockY; - PrevZ = itr->m_BlockZ; - PrevR = itr->m_Radius; - continue; - } - int dr = itr->m_Radius - PrevR; - int Rad1 = std::max(PrevR + 1 * dr / 4, 1); - int Rad2 = std::max(PrevR + 3 * dr / 4, 1); - a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1)); - a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2)); - PrevX = itr->m_BlockX; - PrevY = itr->m_BlockY; - PrevZ = itr->m_BlockZ; - PrevR = itr->m_Radius; - res = true; - } - a_Dst.push_back(a_Src.back()); - return res && (a_Src.size() < a_Dst.size()); -} - - - - - -void cCaveTunnel::Smooth(void) -{ - cCaveDefPoints Pts; - while (true) - { - if (!RefineDefPoints(m_Points, Pts)) - { - std::swap(Pts, m_Points); - return; - } - if (!RefineDefPoints(Pts, m_Points)) - { - return; - } - } -} - - - - - -void cCaveTunnel::FinishLinear(void) -{ - // For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints - cCaveDefPoints Pts; - std::swap(Pts, m_Points); - - m_Points.reserve(Pts.size() * 3); - int PrevX = Pts.front().m_BlockX; - int PrevY = Pts.front().m_BlockY; - int PrevZ = Pts.front().m_BlockZ; - for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) - { - int x1 = itr->m_BlockX; - int y1 = itr->m_BlockY; - int z1 = itr->m_BlockZ; - int dx = abs(x1 - PrevX); - int dy = abs(y1 - PrevY); - int dz = abs(z1 - PrevZ); - int sx = (PrevX < x1) ? 1 : -1; - int sy = (PrevY < y1) ? 1 : -1; - int sz = (PrevZ < z1) ? 1 : -1; - int err = dx - dz; - int R = itr->m_Radius; - - if (dx >= std::max(dy, dz)) // x dominant - { - int yd = dy - dx / 2; - int zd = dz - dx / 2; - - while (true) - { - m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); - - if (PrevX == x1) - { - break; - } - - if (yd >= 0) // move along y - { - PrevY += sy; - yd -= dx; - } - - if (zd >= 0) // move along z - { - PrevZ += sz; - zd -= dx; - } - - // move along x - PrevX += sx; - yd += dy; - zd += dz; - } - } - else if (dy >= std::max(dx, dz)) // y dominant - { - int xd = dx - dy / 2; - int zd = dz - dy / 2; - - while (true) - { - m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); - - if (PrevY == y1) - { - break; - } - - if (xd >= 0) // move along x - { - PrevX += sx; - xd -= dy; - } - - if (zd >= 0) // move along z - { - PrevZ += sz; - zd -= dy; - } - - // move along y - PrevY += sy; - xd += dx; - zd += dz; - } - } - else - { - // z dominant - ASSERT(dz >= std::max(dx, dy)); - int xd = dx - dz / 2; - int yd = dy - dz / 2; - - while (true) - { - m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); - - if (PrevZ == z1) - { - break; - } - - if (xd >= 0) // move along x - { - PrevX += sx; - xd -= dz; - } - - if (yd >= 0) // move along y - { - PrevY += sy; - yd -= dz; - } - - // move along z - PrevZ += sz; - xd += dx; - yd += dy; - } - } // if (which dimension is dominant) - } // for itr -} - - - - - -void cCaveTunnel::CalcBoundingBox(void) -{ - m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX; - m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY; - m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ; - for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) - { - m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius); - m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius); - m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius); - m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius); - m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius); - m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius); - } // for itr - m_Points[] -} - - - - - -void cCaveTunnel::ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap -) -{ - int BaseX = a_ChunkX * cChunkDef::Width; - int BaseZ = a_ChunkZ * cChunkDef::Width; - if ( - (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) || - (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) - ) - { - // Tunnel does not intersect the chunk at all, bail out - return; - } - - int BlockStartX = a_ChunkX * cChunkDef::Width; - int BlockStartZ = a_ChunkZ * cChunkDef::Width; - int BlockEndX = BlockStartX + cChunkDef::Width; - int BlockEndZ = BlockStartZ + cChunkDef::Width; - for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) - { - if ( - (itr->m_BlockX + itr->m_Radius < BlockStartX) || - (itr->m_BlockX - itr->m_Radius > BlockEndX) || - (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || - (itr->m_BlockZ - itr->m_Radius > BlockEndZ) - ) - { - // Cannot intersect, bail out early - continue; - } - - // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom: - int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc - int DifY = itr->m_BlockY; - int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc - int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1); - int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height)); - int SqRad = itr->m_Radius * itr->m_Radius; - for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) - { - for (int y = Bottom; y <= Top; y++) - { - int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z); - if (4 * SqDist <= SqRad) - { - switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) - { - // Only carve out these specific block types - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_STONE: - case E_BLOCK_COBBLESTONE: - case E_BLOCK_GRAVEL: - case E_BLOCK_SAND: - case E_BLOCK_SANDSTONE: - case E_BLOCK_NETHERRACK: - case E_BLOCK_COAL_ORE: - case E_BLOCK_IRON_ORE: - case E_BLOCK_GOLD_ORE: - case E_BLOCK_DIAMOND_ORE: - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - { - cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); - break; - } - default: break; - } - } - } // for y - } // for x, z - } // for itr - m_Points[] - - /* - #ifdef _DEBUG - // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave: - for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) - { - int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc - int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc - if ( - (DifX >= 0) && (DifX < cChunkDef::Width) && - (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) && - (DifZ >= 0) && (DifZ < cChunkDef::Width) - ) - { - cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE); - } - } // for itr - m_Points[] - #endif // _DEBUG - //*/ -} - - - - - -#ifdef _DEBUG -AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const -{ - AString SVG; - SVG.reserve(m_Points.size() * 20 + 200); - AppendPrintf(SVG, "m_BlockX, a_OffsetZ + itr->m_BlockZ); - Prefix = 'L'; - } - SVG.append("\"/>\n"); - return SVG; -} -#endif // _DEBUG - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenWormNestCaves::cCaveSystem: - -cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) : - m_BlockX(a_BlockX), - m_BlockZ(a_BlockZ), - m_Size(a_Size) -{ - int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3; - for (int i = 0; i < Num; i++) - { - int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset; - int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset; - int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20; - - // Generate three branches from the origin point: - // The tunnels generated depend on X, Y, Z and Branches, - // for the same set of numbers it generates the same offsets! - // That's why we add a +1 to X in the third line - GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3); - GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2); - GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3); - } -} - - - - - -cStructGenWormNestCaves::cCaveSystem::~cCaveSystem() -{ - Clear(); -} - - - - - - -void cStructGenWormNestCaves::cCaveSystem::ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap -) -{ - for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) - { - (*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap); - } // for itr - m_Tunnels[] -} - - - - - -#ifdef _DEBUG -AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const -{ - AString SVG; - SVG.reserve(512 * 1024); - for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) - { - SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ)); - } // for itr - m_Tunnels[] - - // Base point highlight: - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ - ); - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 - ); - - // A gray line from the base point to the first point of the ravine, for identification: - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, - a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX, - a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ - ); - - // Offset guides: - if (a_OffsetX > 0) - { - AppendPrintf(SVG, "\n", - a_OffsetX, a_OffsetX - ); - } - if (a_OffsetZ > 0) - { - AppendPrintf(SVG, "\n", - a_OffsetZ, a_OffsetZ - ); - } - - return SVG; -} -#endif // _DEBUG - - - - - -void cStructGenWormNestCaves::cCaveSystem::Clear(void) -{ - for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) - { - delete *itr; - } - m_Tunnels.clear(); -} - - - - - -void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint( - int a_OriginX, int a_OriginY, int a_OriginZ, - cNoise & a_Noise, int a_NumSegments -) -{ - int DoubleSize = m_Size * 2; - int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX); - for (int i = a_NumSegments - 1; i >= 0; --i) - { - int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2; - int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4; - int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2; - int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX); - m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise)); - GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i); - a_OriginX = EndX; - a_OriginY = EndY; - a_OriginZ = EndZ; - Radius = EndR; - } // for i - a_NumSegments -} - - - - - -int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ) -{ - // Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large - int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11; - /* - // Not good enough: - // The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value. - // To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise - int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff); - // sum is now a gaussian-distribution noise within [0 .. 767], with center at 384. - // We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center - int res = 3 + abs((sum / 24) - 16); - */ - - // Algorithm of choice: random value in the range of zero to random value - heavily towards zero - int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1); - return res; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenWormNestCaves: - -cStructGenWormNestCaves::~cStructGenWormNestCaves() -{ - ClearCache(); -} - - - - - -void cStructGenWormNestCaves::ClearCache(void) -{ - for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } // for itr - m_Cache[] - m_Cache.clear(); -} - - - - - -void cStructGenWormNestCaves::GenStructures(cChunkDesc & a_ChunkDesc) -{ - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - cCaveSystems Caves; - GetCavesForChunk(ChunkX, ChunkZ, Caves); - for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr) - { - (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); - } // for itr - Caves[] -} - - - - - -void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves) -{ - int BaseX = a_ChunkX * cChunkDef::Width / m_Grid; - int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid; - if (BaseX < 0) - { - --BaseX; - } - if (BaseZ < 0) - { - --BaseZ; - } - BaseX -= NEIGHBORHOOD_SIZE / 2; - BaseZ -= NEIGHBORHOOD_SIZE / 2; - - // Walk the cache, move each cave system that we want into a_Caves: - int StartX = BaseX * m_Grid; - int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid; - int StartZ = BaseZ * m_Grid; - int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid; - for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) - { - if ( - ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && - ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) - ) - { - // want - a_Caves.push_back(*itr); - itr = m_Cache.erase(itr); - } - else - { - // don't want - ++itr; - } - } // for itr - m_Cache[] - - for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) - { - int RealX = (BaseX + x) * m_Grid; - for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) - { - int RealZ = (BaseZ + z) * m_Grid; - bool Found = false; - for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) - { - if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) - { - Found = true; - break; - } - } - if (!Found) - { - a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise)); - } - } - } - - // Copy a_Caves into m_Cache to the beginning: - cCaveSystems CavesCopy(a_Caves); - m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end()); - - // Trim the cache if it's too long: - if (m_Cache.size() > 100) - { - cCaveSystems::iterator itr = m_Cache.begin(); - std::advance(itr, 100); - for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } - itr = m_Cache.begin(); - std::advance(itr, 100); - m_Cache.erase(itr, m_Cache.end()); - } - - /* - // Uncomment this block for debugging the caves' shapes in 2D using an SVG export - #ifdef _DEBUG - AString SVG; - SVG.append("\n\n"); - SVG.reserve(2 * 1024 * 1024); - for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) - { - int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid); - Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid); - SVG.append((*itr)->ExportAsSVG(Color, 512, 512)); - } - SVG.append("\n"); - - AString fnam; - Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); - cFile File(fnam, cFile::fmWrite); - File.Write(SVG.c_str(), SVG.size()); - #endif // _DEBUG - //*/ -} - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenMarbleCaves: - -static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise ) -{ - static const float PI_2 = 1.57079633f; - float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4; - - oct1 = oct1 * oct1 * oct1; - if (oct1 < 0.f) oct1 = PI_2; - if (oct1 > PI_2) oct1 = PI_2; - - return oct1; -} - - - - - -void cStructGenMarbleCaves::GenStructures(cChunkDesc & a_ChunkDesc) -{ - cNoise Noise(m_Seed); - for (int z = 0; z < cChunkDef::Width; z++) - { - const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z); - for (int x = 0; x < cChunkDef::Width; x++) - { - const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x); - - int Top = a_ChunkDesc.GetHeight(x, z); - for (int y = 1; y < Top; ++y ) - { - if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE) - { - continue; - } - - const float yy = (float)y; - const float WaveNoise = 1; - if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); - } - } // for y - } // for x - } // for z -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenDualRidgeCaves: - -void cStructGenDualRidgeCaves::GenStructures(cChunkDesc & a_ChunkDesc) -{ - for (int z = 0; z < cChunkDef::Width; z++) - { - const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10; - for (int x = 0; x < cChunkDef::Width; x++) - { - const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10; - - int Top = a_ChunkDesc.GetHeight(x, z); - for (int y = 1; y <= Top; ++y) - { - const float yy = (float)y / 10; - const float WaveNoise = 1; - float n1 = m_Noise1.CubicNoise3D(xx, yy, zz); - float n2 = m_Noise2.CubicNoise3D(xx, yy, zz); - float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; - float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; - if ((abs(n1 + n3) * abs(n2 + n4)) > m_Threshold) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); - } - } // for y - } // for x - } // for z -} - - - - diff --git a/source/Generating/Caves.h b/source/Generating/Caves.h deleted file mode 100644 index 70cf6fe8c..000000000 --- a/source/Generating/Caves.h +++ /dev/null @@ -1,102 +0,0 @@ - -// Caves.h - -// Interfaces to the various cave structure generators: -// - cStructGenWormNestCaves -// - cStructGenMarbleCaves -// - cStructGenNetherCaves - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cStructGenMarbleCaves : - public cStructureGen -{ -public: - cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {} - -protected: - - int m_Seed; - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cStructGenDualRidgeCaves : - public cStructureGen -{ -public: - cStructGenDualRidgeCaves(int a_Seed, float a_Threshold) : - m_Noise1(a_Seed), - m_Noise2(2 * a_Seed + 19999), - m_Seed(a_Seed), - m_Threshold(a_Threshold) - { - } - -protected: - cNoise m_Noise1; - cNoise m_Noise2; - int m_Seed; - float m_Threshold; - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cStructGenWormNestCaves : - public cStructureGen -{ -public: - cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) : - m_Noise(a_Seed), - m_Size(a_Size), - m_Grid(a_Grid), - m_MaxOffset(a_MaxOffset) - { - } - - ~cStructGenWormNestCaves(); - -protected: - class cCaveSystem; // fwd: Caves.cpp - typedef std::list cCaveSystems; - - cNoise m_Noise; - int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel - int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to - int m_Grid; // average spacing of the nests - cCaveSystems m_Cache; - - /// Clears everything from the cache - void ClearCache(void); - - /// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function. - void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves); - - // cStructGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - diff --git a/source/Generating/ChunkDesc.cpp b/source/Generating/ChunkDesc.cpp deleted file mode 100644 index 9fb306996..000000000 --- a/source/Generating/ChunkDesc.cpp +++ /dev/null @@ -1,605 +0,0 @@ - -// ChunkDesc.cpp - -// Implements the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING. - -#include "Globals.h" -#include "ChunkDesc.h" -#include "../BlockArea.h" -#include "../Cuboid.h" -#include "../Noise.h" -#include "../BlockEntities/BlockEntity.h" - - - - - -cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) : - m_ChunkX(a_ChunkX), - m_ChunkZ(a_ChunkZ), - m_bUseDefaultBiomes(true), - m_bUseDefaultHeight(true), - m_bUseDefaultComposition(true), - m_bUseDefaultStructures(true), - m_bUseDefaultFinish(true) -{ - m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width); - /* - memset(m_BlockTypes, 0, sizeof(cChunkDef::BlockTypes)); - memset(m_BlockMeta, 0, sizeof(cChunkDef::BlockNibbles)); - */ - memset(m_BiomeMap, 0, sizeof(cChunkDef::BiomeMap)); - memset(m_HeightMap, 0, sizeof(cChunkDef::HeightMap)); -} - - - - - -cChunkDesc::~cChunkDesc() -{ - // Nothing needed yet -} - - - - - -void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ) -{ - m_ChunkX = a_ChunkX; - m_ChunkZ = a_ChunkZ; -} - - - - - -void cChunkDesc::FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - m_BlockArea.Fill(cBlockArea::baTypes | cBlockArea::baMetas, a_BlockType, a_BlockMeta); -} - - - - - -void cChunkDesc::SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - m_BlockArea.SetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); -} - - - - - -void cChunkDesc::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - m_BlockArea.GetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); -} - - - - - -void cChunkDesc::SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) -{ - cChunkDef::SetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ, a_BlockType); -} - - - - - -BLOCKTYPE cChunkDesc::GetBlockType(int a_RelX, int a_RelY, int a_RelZ) -{ - return cChunkDef::GetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ); -} - - - - - -NIBBLETYPE cChunkDesc::GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ) -{ - return m_BlockArea.GetRelBlockMeta(a_RelX, a_RelY, a_RelZ); -} - - - - - -void cChunkDesc::SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta) -{ - m_BlockArea.SetRelBlockMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta); -} - - - - - -void cChunkDesc::SetBiome(int a_RelX, int a_RelZ, int a_BiomeID) -{ - cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, (EMCSBiome)a_BiomeID); -} - - - - -EMCSBiome cChunkDesc::GetBiome(int a_RelX, int a_RelZ) -{ - return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); -} - - - - - -void cChunkDesc::SetHeight(int a_RelX, int a_RelZ, int a_Height) -{ - cChunkDef::SetHeight(m_HeightMap, a_RelX, a_RelZ, a_Height); -} - - - - - -int cChunkDesc::GetHeight(int a_RelX, int a_RelZ) -{ - return cChunkDef::GetHeight(m_HeightMap, a_RelX, a_RelZ); -} - - - - - -void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes) -{ - m_bUseDefaultBiomes = a_bUseDefaultBiomes; -} - - - - - -bool cChunkDesc::IsUsingDefaultBiomes(void) const -{ - return m_bUseDefaultBiomes; -} - - - - - -void cChunkDesc::SetUseDefaultHeight(bool a_bUseDefaultHeight) -{ - m_bUseDefaultHeight = a_bUseDefaultHeight; -} - - - - - -bool cChunkDesc::IsUsingDefaultHeight(void) const -{ - return m_bUseDefaultHeight; -} - - - - - -void cChunkDesc::SetUseDefaultComposition(bool a_bUseDefaultComposition) -{ - m_bUseDefaultComposition = a_bUseDefaultComposition; -} - - - - - -bool cChunkDesc::IsUsingDefaultComposition(void) const -{ - return m_bUseDefaultComposition; -} - - - - - -void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures) -{ - m_bUseDefaultStructures = a_bUseDefaultStructures; -} - - - - - -bool cChunkDesc::IsUsingDefaultStructures(void) const -{ - return m_bUseDefaultStructures; -} - - - - - -void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish) -{ - m_bUseDefaultFinish = a_bUseDefaultFinish; -} - - - - - -bool cChunkDesc::IsUsingDefaultFinish(void) const -{ - return m_bUseDefaultFinish; -} - - - - -void cChunkDesc::WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy) -{ - m_BlockArea.Merge(a_BlockArea, a_RelX, a_RelY, a_RelZ, a_MergeStrategy); -} - - - - - -void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ) -{ - // Normalize the coords: - if (a_MinRelX > a_MaxRelX) - { - std::swap(a_MinRelX, a_MaxRelX); - } - if (a_MinRelY > a_MaxRelY) - { - std::swap(a_MinRelY, a_MaxRelY); - } - if (a_MinRelZ > a_MaxRelZ) - { - std::swap(a_MinRelZ, a_MaxRelZ); - } - - // Include the Max coords: - a_MaxRelX += 1; - a_MaxRelY += 1; - a_MaxRelZ += 1; - - // Check coords validity: - if (a_MinRelX < 0) - { - LOGWARNING("%s: MinRelX less than zero, adjusting to zero", __FUNCTION__); - a_MinRelX = 0; - } - else if (a_MinRelX >= cChunkDef::Width) - { - LOGWARNING("%s: MinRelX more than chunk width, adjusting to chunk width", __FUNCTION__); - a_MinRelX = cChunkDef::Width - 1; - } - if (a_MaxRelX < 0) - { - LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__); - a_MaxRelX = 0; - } - else if (a_MinRelX >= cChunkDef::Width) - { - LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__); - a_MaxRelX = cChunkDef::Width - 1; - } - - if (a_MinRelY < 0) - { - LOGWARNING("%s: MinRelY less than zero, adjusting to zero", __FUNCTION__); - a_MinRelY = 0; - } - else if (a_MinRelY >= cChunkDef::Height) - { - LOGWARNING("%s: MinRelY more than chunk height, adjusting to chunk height", __FUNCTION__); - a_MinRelY = cChunkDef::Height - 1; - } - if (a_MaxRelY < 0) - { - LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__); - a_MaxRelY = 0; - } - else if (a_MinRelY >= cChunkDef::Height) - { - LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__); - a_MaxRelY = cChunkDef::Height - 1; - } - - if (a_MinRelZ < 0) - { - LOGWARNING("%s: MinRelZ less than zero, adjusting to zero", __FUNCTION__); - a_MinRelZ = 0; - } - else if (a_MinRelZ >= cChunkDef::Width) - { - LOGWARNING("%s: MinRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); - a_MinRelZ = cChunkDef::Width - 1; - } - if (a_MaxRelZ < 0) - { - LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__); - a_MaxRelZ = 0; - } - else if (a_MinRelZ >= cChunkDef::Width) - { - LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); - a_MaxRelZ = cChunkDef::Width - 1; - } - - // Prepare the block area: - int SizeX = a_MaxRelX - a_MinRelX; - int SizeY = a_MaxRelY - a_MinRelY; - int SizeZ = a_MaxRelZ - a_MinRelZ; - a_Dest.Clear(); - a_Dest.m_OriginX = m_ChunkX * cChunkDef::Width + a_MinRelX; - a_Dest.m_OriginY = a_MinRelY; - a_Dest.m_OriginZ = m_ChunkZ * cChunkDef::Width + a_MinRelZ; - a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas); - - for (int y = 0; y < SizeY; y++) - { - int CDY = a_MinRelY + y; - for (int z = 0; z < SizeZ; z++) - { - int CDZ = a_MinRelZ + z; - for (int x = 0; x < SizeX; x++) - { - int CDX = a_MinRelX + x; - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - GetBlockTypeMeta(CDX, CDY, CDZ, BlockType, BlockMeta); - a_Dest.SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta); - } // for x - } // for z - } // for y -} - - - - - -HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const -{ - HEIGHTTYPE MaxHeight = m_HeightMap[0]; - for (int i = 1; i < ARRAYCOUNT(m_HeightMap); i++) - { - if (m_HeightMap[i] > MaxHeight) - { - MaxHeight = m_HeightMap[i]; - } - } - return MaxHeight; -} - - - - - -void cChunkDesc::FillRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - int MinX = std::max(a_MinX, 0); - int MinY = std::max(a_MinY, 0); - int MinZ = std::max(a_MinZ, 0); - int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); - int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); - int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); - - for (int y = MinY; y <= MaxY; y++) - { - for (int z = MinZ; z <= MaxZ; z++) - { - for (int x = MinX; x <= MaxX; x++) - { - SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); - } - } // for z - } // for y -} - - - - - -void cChunkDesc::ReplaceRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, - BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta -) -{ - int MinX = std::max(a_MinX, 0); - int MinY = std::max(a_MinY, 0); - int MinZ = std::max(a_MinZ, 0); - int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); - int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); - int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); - - for (int y = MinY; y <= MaxY; y++) - { - for (int z = MinZ; z <= MaxZ; z++) - { - for (int x = MinX; x <= MaxX; x++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - GetBlockTypeMeta(x, y, z, BlockType, BlockMeta); - if ((BlockType == a_SrcType) && (BlockMeta == a_SrcMeta)) - { - SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); - } - } - } // for z - } // for y -} - - - - - -void cChunkDesc::FloorRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta -) -{ - int MinX = std::max(a_MinX, 0); - int MinY = std::max(a_MinY, 0); - int MinZ = std::max(a_MinZ, 0); - int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); - int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); - int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); - - for (int y = MinY; y <= MaxY; y++) - { - for (int z = MinZ; z <= MaxZ; z++) - { - for (int x = MinX; x <= MaxX; x++) - { - switch (GetBlockType(x, y, z)) - { - case E_BLOCK_AIR: - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - { - SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); - break; - } - } // switch (GetBlockType) - } // for x - } // for z - } // for y -} - - - - - -void cChunkDesc::RandomFillRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - int a_RandomSeed, int a_ChanceOutOf10k -) -{ - cNoise Noise(a_RandomSeed); - int MinX = std::max(a_MinX, 0); - int MinY = std::max(a_MinY, 0); - int MinZ = std::max(a_MinZ, 0); - int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); - int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); - int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); - - for (int y = MinY; y <= MaxY; y++) - { - for (int z = MinZ; z <= MaxZ; z++) - { - for (int x = MinX; x <= MaxX; x++) - { - int rnd = (Noise.IntNoise3DInt(x, y, z) / 7) % 10000; - if (rnd <= a_ChanceOutOf10k) - { - SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); - } - } - } // for z - } // for y -} - - - - - -cBlockEntity * cChunkDesc::GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ) -{ - int AbsX = a_RelX + m_ChunkX * cChunkDef::Width; - int AbsZ = a_RelZ + m_ChunkZ * cChunkDef::Width; - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr) - { - if (((*itr)->GetPosX() == AbsX) && ((*itr)->GetPosY() == a_RelY) && ((*itr)->GetPosZ() == AbsZ)) - { - // Already in the list: - if ((*itr)->GetBlockType() != GetBlockType(a_RelX, a_RelY, a_RelZ)) - { - // Wrong type, the block type has been overwritten. Erase and create new: - m_BlockEntities.erase(itr); - break; - } - // Correct type, already present. Return it: - return *itr; - } - } // for itr - m_BlockEntities[] - - // The block entity is not created yet, try to create it and add to list: - cBlockEntity * be = cBlockEntity::CreateByBlockType(GetBlockType(a_RelX, a_RelY, a_RelZ), GetBlockMeta(a_RelX, a_RelY, a_RelZ), AbsX, a_RelY, AbsZ); - if (be == NULL) - { - // No block entity for this block type - return NULL; - } - m_BlockEntities.push_back(be); - return be; -} - - - - - -void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas) -{ - const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas(); - for (int i = 0; i < ARRAYCOUNT(a_DestMetas); i++) - { - a_DestMetas[i] = AreaMetas[2 * i] | (AreaMetas[2 * i + 1] << 4); - } -} - - - - - -#ifdef _DEBUG - -void cChunkDesc::VerifyHeightmap(void) -{ - for (int x = 0; x < cChunkDef::Width; x++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = cChunkDef::Height - 1; y > 0; y--) - { - BLOCKTYPE BlockType = GetBlockType(x, y, z); - if (BlockType != E_BLOCK_AIR) - { - int Height = GetHeight(x, z); - ASSERT(Height == y); - break; - } - } // for y - } // for z - } // for x -} - -#endif // _DEBUG - - - - - diff --git a/source/Generating/ChunkDesc.h b/source/Generating/ChunkDesc.h deleted file mode 100644 index e130c463f..000000000 --- a/source/Generating/ChunkDesc.h +++ /dev/null @@ -1,217 +0,0 @@ - -// ChunkDesc.h - -// Declares the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING. - - - - - -#pragma once - -#include "../BlockArea.h" -#include "../ChunkDef.h" -#include "../Cuboid.h" - - - - - -// fwd: ../BlockArea.h -class cBlockArea; - - - - - -// tolua_begin -class cChunkDesc -{ -public: - // tolua_end - - /// Uncompressed block metas, 1 meta per byte - typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks]; - - cChunkDesc(int a_ChunkX, int a_ChunkZ); - ~cChunkDesc(); - - void SetChunkCoords(int a_ChunkX, int a_ChunkZ); - - // tolua_begin - - int GetChunkX(void) const { return m_ChunkX; } - int GetChunkZ(void) const { return m_ChunkZ; } - - void FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - void SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - - void SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType); - BLOCKTYPE GetBlockType(int a_RelX, int a_RelY, int a_RelZ); - - void SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta); - NIBBLETYPE GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ); - - void SetBiome(int a_RelX, int a_RelZ, int a_BiomeID); - EMCSBiome GetBiome(int a_RelX, int a_RelZ); - - void SetHeight(int a_RelX, int a_RelZ, int a_Height); - int GetHeight(int a_RelX, int a_RelZ); - - // Default generation: - void SetUseDefaultBiomes(bool a_bUseDefaultBiomes); - bool IsUsingDefaultBiomes(void) const; - void SetUseDefaultHeight(bool a_bUseDefaultHeight); - bool IsUsingDefaultHeight(void) const; - void SetUseDefaultComposition(bool a_bUseDefaultComposition); - bool IsUsingDefaultComposition(void) const; - void SetUseDefaultStructures(bool a_bUseDefaultStructures); - bool IsUsingDefaultStructures(void) const; - void SetUseDefaultFinish(bool a_bUseDefaultFinish); - bool IsUsingDefaultFinish(void) const; - - /// Writes the block area into the chunk, with its origin set at the specified relative coords. Area's data overwrite everything in the chunk. - void WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy = cBlockArea::msOverwrite); - - /// Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas - void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ); - - /// Returns the maximum height value in the heightmap - HEIGHTTYPE GetMaxHeight(void) const; - - /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk - void FillRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ); - - /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk - void FillRelCuboid(const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) - { - FillRelCuboid( - a_RelCuboid.p1.x, a_RelCuboid.p2.x, - a_RelCuboid.p1.y, a_RelCuboid.p2.y, - a_RelCuboid.p1.z, a_RelCuboid.p2.z, - a_BlockType, a_BlockMeta - ); - } - - /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk - void ReplaceRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, - BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta - ); - - /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk - void ReplaceRelCuboid( - const cCuboid & a_RelCuboid, - BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, - BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta - ) - { - ReplaceRelCuboid( - a_RelCuboid.p1.x, a_RelCuboid.p2.x, - a_RelCuboid.p1.y, a_RelCuboid.p2.y, - a_RelCuboid.p1.z, a_RelCuboid.p2.z, - a_SrcType, a_SrcMeta, - a_DstType, a_DstMeta - ); - } - - /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk - void FloorRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta - ); - - /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk - void FloorRelCuboid( - const cCuboid & a_RelCuboid, - BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta - ) - { - FloorRelCuboid( - a_RelCuboid.p1.x, a_RelCuboid.p2.x, - a_RelCuboid.p1.y, a_RelCuboid.p2.y, - a_RelCuboid.p1.z, a_RelCuboid.p2.z, - a_DstType, a_DstMeta - ); - } - - /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk - void RandomFillRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - int a_RandomSeed, int a_ChanceOutOf10k - ); - - /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk - void RandomFillRelCuboid( - const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - int a_RandomSeed, int a_ChanceOutOf10k - ) - { - RandomFillRelCuboid( - a_RelCuboid.p1.x, a_RelCuboid.p2.x, - a_RelCuboid.p1.y, a_RelCuboid.p2.y, - a_RelCuboid.p1.z, a_RelCuboid.p2.z, - a_BlockType, a_BlockMeta, - a_RandomSeed, a_ChanceOutOf10k - ); - } - - /// Returns the block entity at the specified coords. - /// If there is no block entity at those coords, tries to create one, based on the block type - /// If the blocktype doesn't support a block entity, returns NULL. - cBlockEntity * GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ); - - // tolua_end - - // Accessors used by cChunkGenerator::Generator descendants: - inline cChunkDef::BiomeMap & GetBiomeMap (void) { return m_BiomeMap; } - inline cChunkDef::BlockTypes & GetBlockTypes (void) { return *((cChunkDef::BlockTypes *)m_BlockArea.GetBlockTypes()); } - // CANNOT, different compression! - // inline cChunkDef::BlockNibbles & GetBlockMetas (void) { return *((cChunkDef::BlockNibbles *)m_BlockArea.GetBlockMetas()); } - inline BlockNibbleBytes & GetBlockMetasUncompressed(void) { return *((BlockNibbleBytes *)m_BlockArea.GetBlockMetas()); } - inline cChunkDef::HeightMap & GetHeightMap (void) { return m_HeightMap; } - inline cEntityList & GetEntities (void) { return m_Entities; } - inline cBlockEntityList & GetBlockEntities (void) { return m_BlockEntities; } - - /// Compresses the metas from the BlockArea format (1 meta per byte) into regular format (2 metas per byte) - void CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas); - - #ifdef _DEBUG - /// Verifies that the heightmap corresponds to blocktype contents; if not, asserts on that column - void VerifyHeightmap(void); - #endif // _DEBUG - -private: - int m_ChunkX; - int m_ChunkZ; - - cChunkDef::BiomeMap m_BiomeMap; - cBlockArea m_BlockArea; - cChunkDef::HeightMap m_HeightMap; - cEntityList m_Entities; // Individual entities are NOT owned by this object! - cBlockEntityList m_BlockEntities; // Individual block entities are NOT owned by this object! - - bool m_bUseDefaultBiomes; - bool m_bUseDefaultHeight; - bool m_bUseDefaultComposition; - bool m_bUseDefaultStructures; - bool m_bUseDefaultFinish; -} ; // tolua_export - - - - diff --git a/source/Generating/ChunkGenerator.cpp b/source/Generating/ChunkGenerator.cpp deleted file mode 100644 index 59a00b540..000000000 --- a/source/Generating/ChunkGenerator.cpp +++ /dev/null @@ -1,329 +0,0 @@ - -#include "Globals.h" - -#include "ChunkGenerator.h" -#include "../World.h" -#include "../../iniFile/iniFile.h" -#include "../Root.h" -#include "../PluginManager.h" -#include "ChunkDesc.h" -#include "ComposableGenerator.h" -#include "Noise3DGenerator.h" - - - - - -/// If the generation queue size exceeds this number, a warning will be output -const int QUEUE_WARNING_LIMIT = 1000; - -/// If the generation queue size exceeds this number, chunks with no clients will be skipped -const int QUEUE_SKIP_LIMIT = 500; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cChunkGenerator: - -cChunkGenerator::cChunkGenerator(void) : - super("cChunkGenerator"), - m_World(NULL), - m_Generator(NULL) -{ -} - - - - - -cChunkGenerator::~cChunkGenerator() -{ - Stop(); -} - - - - - -bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile) -{ - MTRand rnd; - m_World = a_World; - m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt()); - AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable"); - - if (NoCaseCompare(GeneratorName, "Noise3D") == 0) - { - m_Generator = new cNoise3DGenerator(*this); - } - else - { - if (NoCaseCompare(GeneratorName, "composable") != 0) - { - LOGWARN("[Generator]::Generator value \"%s\" not recognized, using \"Composable\".", GeneratorName.c_str()); - } - m_Generator = new cComposableGenerator(*this); - } - - if (m_Generator == NULL) - { - LOGERROR("Generator could not start, aborting the server"); - return false; - } - - m_Generator->Initialize(a_World, a_IniFile); - - return super::Start(); -} - - - - - -void cChunkGenerator::Stop(void) -{ - m_ShouldTerminate = true; - m_Event.Set(); - m_evtRemoved.Set(); // Wake up anybody waiting for empty queue - Wait(); - - delete m_Generator; - m_Generator = NULL; -} - - - - - -void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - { - cCSLock Lock(m_CS); - - // Check if it is already in the queue: - for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) - { - // Already in the queue, bail out - return; - } - } // for itr - m_Queue[] - - // Add to queue, issue a warning if too many: - if (m_Queue.size() >= QUEUE_WARNING_LIMIT) - { - LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%i)", a_ChunkX, a_ChunkZ, m_Queue.size()); - } - m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); - } - - m_Event.Set(); -} - - - - - -void cChunkGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - if (m_Generator != NULL) - { - m_Generator->GenerateBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); - } -} - - - - - -void cChunkGenerator::WaitForQueueEmpty(void) -{ - cCSLock Lock(m_CS); - while (!m_ShouldTerminate && !m_Queue.empty()) - { - cCSUnlock Unlock(Lock); - m_evtRemoved.Wait(); - } -} - - - - - -int cChunkGenerator::GetQueueLength(void) -{ - cCSLock Lock(m_CS); - return (int)m_Queue.size(); -} - - - - - -EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ) -{ - ASSERT(m_Generator != NULL); - return m_Generator->GetBiomeAt(a_BlockX, a_BlockZ); -} - - - - - -BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default) -{ - AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default); - BLOCKTYPE Block = BlockStringToType(BlockType); - if (Block < 0) - { - LOGWARN("[&s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(),a_Default.c_str()); - return BlockStringToType(a_Default); - } - return Block; -} - - - - - -void cChunkGenerator::Execute(void) -{ - // To be able to display performance information, the generator counts the chunks generated. - // When the queue gets empty, the count is reset, so that waiting for the queue is not counted into the total time. - int NumChunksGenerated = 0; // Number of chunks generated since the queue was last empty - clock_t GenerationStart = clock(); // Clock tick when the queue started to fill - clock_t LastReportTick = clock(); // Clock tick of the last report made (so that performance isn't reported too often) - - while (!m_ShouldTerminate) - { - cCSLock Lock(m_CS); - while (m_Queue.size() == 0) - { - if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC)) - { - LOG("Chunk generator performance: %.2f ch/s (%d ch total)", - (double)NumChunksGenerated * CLOCKS_PER_SEC/ (clock() - GenerationStart), - NumChunksGenerated - ); - } - cCSUnlock Unlock(Lock); - m_Event.Wait(); - if (m_ShouldTerminate) - { - return; - } - NumChunksGenerated = 0; - GenerationStart = clock(); - LastReportTick = clock(); - } - - cChunkCoords coords = m_Queue.front(); // Get next coord from queue - m_Queue.erase( m_Queue.begin() ); // Remove coordinate from queue - bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT); - Lock.Unlock(); // Unlock ASAP - m_evtRemoved.Set(); - - // Display perf info once in a while: - if ((NumChunksGenerated > 16) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC)) - { - LOG("Chunk generator performance: %.2f ch/s (%d ch total)", - (double)NumChunksGenerated * CLOCKS_PER_SEC / (clock() - GenerationStart), - NumChunksGenerated - ); - LastReportTick = clock(); - } - - // Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set - if ((coords.m_ChunkY == 0) && m_World->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ)) - { - LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ); - // Already generated, ignore request - continue; - } - - if (SkipEnabled && !m_World->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ)) - { - LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); - continue; - } - - LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); - DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); - - // Save the chunk right after generating, so that we don't have to generate it again on next run - m_World->GetStorage().QueueSaveChunk(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); - - NumChunksGenerated++; - } // while (!bStop) -} - - - - -void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ); - cRoot::Get()->GetPluginManager()->CallHookChunkGenerating(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc); - m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc); - cRoot::Get()->GetPluginManager()->CallHookChunkGenerated(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc); - - #ifdef _DEBUG - // Verify that the generator has produced valid data: - ChunkDesc.VerifyHeightmap(); - #endif - - cChunkDef::BlockNibbles BlockMetas; - ChunkDesc.CompressBlockMetas(BlockMetas); - - m_World->SetChunkData( - a_ChunkX, a_ChunkZ, - ChunkDesc.GetBlockTypes(), BlockMetas, - NULL, NULL, // We don't have lighting, chunk will be lighted when needed - &ChunkDesc.GetHeightMap(), &ChunkDesc.GetBiomeMap(), - ChunkDesc.GetEntities(), ChunkDesc.GetBlockEntities(), - true - ); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cChunkGenerator::cGenerator: - -cChunkGenerator::cGenerator::cGenerator(cChunkGenerator & a_ChunkGenerator) : - m_ChunkGenerator(a_ChunkGenerator) -{ -} - - - - - -void cChunkGenerator::cGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) -{ - m_World = a_World; - UNUSED(a_IniFile); -} - - - - - -EMCSBiome cChunkGenerator::cGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ) -{ - cChunkDef::BiomeMap Biomes; - int Y = 0; - int ChunkX, ChunkZ; - cWorld::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, Y, ChunkZ); - GenerateBiomes(ChunkX, ChunkZ, Biomes); - return cChunkDef::GetBiome(Biomes, a_BlockX, a_BlockZ); -} - - - - diff --git a/source/Generating/ChunkGenerator.h b/source/Generating/ChunkGenerator.h deleted file mode 100644 index 2d3bb8082..000000000 --- a/source/Generating/ChunkGenerator.h +++ /dev/null @@ -1,113 +0,0 @@ - -// ChunkGenerator.h - -// Interfaces to the cChunkGenerator class representing the thread that generates chunks - -/* -The object takes requests for generating chunks and processes them in a separate thread one by one. -The requests are not added to the queue if there is already a request with the same coords -Before generating, the thread checks if the chunk hasn't been already generated. -It is theoretically possible to have multiple generator threads by having multiple instances of this object, -but then it MAY happen that the chunk is generated twice. -If the generator queue is overloaded, the generator skips chunks with no clients in them -*/ - - - - - -#pragma once - -#include "../OSSupport/IsThread.h" -#include "../ChunkDef.h" - - - - - -// fwd: -class cWorld; -class cIniFile; -class cChunkDesc; - - - - - -class cChunkGenerator : - cIsThread -{ - typedef cIsThread super; - -public: - /// The interface that a class has to implement to become a generator - class cGenerator - { - public: - cGenerator(cChunkGenerator & a_ChunkGenerator); - virtual ~cGenerator() {} ; // Force a virtual destructor - - /// Called to initialize the generator on server startup. - virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile); - - /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. - virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; - - /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome. Default implementation uses GenerateBiomes(). - virtual EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); - - /// Called in a separate thread to do the actual chunk generation. Generator should generate into a_ChunkDesc. - virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) = 0; - - protected: - cChunkGenerator & m_ChunkGenerator; - cWorld * m_World; - } ; - - - cChunkGenerator (void); - ~cChunkGenerator(); - - bool Start(cWorld * a_World, cIniFile & a_IniFile); - void Stop(void); - - /// Queues the chunk for generation; removes duplicate requests - void QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. - void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); - - void WaitForQueueEmpty(void); - - int GetQueueLength(void); - - int GetSeed(void) const { return m_Seed; } - - /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome - EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); - - /// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. - static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default); - -private: - - cWorld * m_World; - - int m_Seed; - - cCriticalSection m_CS; - cChunkCoordsList m_Queue; - cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate - cEvent m_evtRemoved; ///< Set when an item is removed from the queue - - cGenerator * m_Generator; ///< The actual generator engine used to generate chunks - - // cIsThread override: - virtual void Execute(void) override; - - void DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ); -}; - - - - diff --git a/source/Generating/CompoGen.cpp b/source/Generating/CompoGen.cpp deleted file mode 100644 index cc2a203af..000000000 --- a/source/Generating/CompoGen.cpp +++ /dev/null @@ -1,634 +0,0 @@ - -// CompoGen.cpp - -/* Implements the various terrain composition generators: - - cCompoGenSameBlock - - cCompoGenDebugBiomes - - cCompoGenClassic -*/ - -#include "Globals.h" -#include "CompoGen.h" -#include "../BlockID.h" -#include "../Item.h" -#include "../LinearUpscale.h" -#include "../../iniFile/iniFile.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCompoGenSameBlock: - -void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int Start; - if (m_IsBedrocked) - { - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - Start = 1; - } - else - { - Start = 0; - } - for (int y = a_ChunkDesc.GetHeight(x, z); y >= Start; y--) - { - a_ChunkDesc.SetBlockType(x, y, z, m_BlockType); - } // for y - } // for z - } // for x -} - - - - - -void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile) -{ - m_BlockType = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "SameBlockType", "stone").m_ItemType); - m_IsBedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCompoGenDebugBiomes: - -void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - static BLOCKTYPE Blocks[] = - { - E_BLOCK_STONE, - E_BLOCK_COBBLESTONE, - E_BLOCK_LOG, - E_BLOCK_PLANKS, - E_BLOCK_SANDSTONE, - E_BLOCK_WOOL, - E_BLOCK_COAL_ORE, - E_BLOCK_IRON_ORE, - E_BLOCK_GOLD_ORE, - E_BLOCK_DIAMOND_ORE, - E_BLOCK_LAPIS_ORE, - E_BLOCK_REDSTONE_ORE, - E_BLOCK_IRON_BLOCK, - E_BLOCK_GOLD_BLOCK, - E_BLOCK_DIAMOND_BLOCK, - E_BLOCK_LAPIS_BLOCK, - E_BLOCK_BRICK, - E_BLOCK_MOSSY_COBBLESTONE, - E_BLOCK_OBSIDIAN, - E_BLOCK_NETHERRACK, - E_BLOCK_SOULSAND, - E_BLOCK_NETHER_BRICK, - E_BLOCK_BEDROCK, - } ; - - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - BLOCKTYPE BlockType = Blocks[a_ChunkDesc.GetBiome(x, z)]; - for (int y = a_ChunkDesc.GetHeight(x, z); y >= 0; y--) - { - a_ChunkDesc.SetBlockType(x, y, z, BlockType); - } // for y - } // for z - } // for x -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCompoGenClassic: - -cCompoGenClassic::cCompoGenClassic(void) : - m_SeaLevel(60), - m_BeachHeight(2), - m_BeachDepth(4), - m_BlockTop(E_BLOCK_GRASS), - m_BlockMiddle(E_BLOCK_DIRT), - m_BlockBottom(E_BLOCK_STONE), - m_BlockBeach(E_BLOCK_SAND), - m_BlockBeachBottom(E_BLOCK_SANDSTONE), - m_BlockSea(E_BLOCK_STATIONARY_WATER) -{ -} - - - - - -void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - /* The classic composition means: - - 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight - - 3 sand and a 1 sandstone, rest stone if between sealevel and sealevel + beachheight - - water from waterlevel to height, then 3 sand, 1 sandstone, the rest stone, if water depth < beachdepth - - water from waterlevel, then 3 dirt, the rest stone otherwise - - bedrock at the bottom - */ - - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - - // The patterns to use for different situations, must be same length! - const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ; - const BLOCKTYPE PatternBeach[] = {m_BlockBeach, m_BlockBeach, m_BlockBeach, m_BlockBeachBottom} ; - const BLOCKTYPE PatternOcean[] = {m_BlockMiddle, m_BlockMiddle, m_BlockMiddle, m_BlockBottom} ; - static int PatternLength = ARRAYCOUNT(PatternGround); - ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternBeach)); - ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternOcean)); - - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int Height = a_ChunkDesc.GetHeight(x, z); - const BLOCKTYPE * Pattern; - if (Height > m_SeaLevel + m_BeachHeight) - { - Pattern = PatternGround; - } - else if (Height > m_SeaLevel - m_BeachDepth) - { - Pattern = PatternBeach; - } - else - { - Pattern = PatternOcean; - } - - // Fill water from sealevel down to height (if any): - for (int y = m_SeaLevel; y >= Height; --y) - { - a_ChunkDesc.SetBlockType(x, y, z, m_BlockSea); - } - - // Fill from height till the bottom: - for (int y = Height; y >= 1; y--) - { - a_ChunkDesc.SetBlockType(x, y, z, (Height - y < PatternLength) ? Pattern[Height - y] : m_BlockBottom); - } - - // The last layer is always bedrock: - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - } // for x - } // for z -} - - - - - -void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile) -{ - m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel); - m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight); - m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth); - m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType); - m_BlockMiddle = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt").m_ItemType); - m_BlockBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBottom", "stone").m_ItemType); - m_BlockBeach = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeach", "sand").m_ItemType); - m_BlockBeachBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone").m_ItemType); - m_BlockSea = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater").m_ItemType); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCompoGenBiomal: - -void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - - /* - _X 2013_04_22: - There's no point in generating the whole cubic noise at once, because the noise values are used in - only about 20 % of the cases, so the speed gained by precalculating is lost by precalculating too much data - */ - - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int Height = a_ChunkDesc.GetHeight(x, z); - if (Height > m_SeaLevel) - { - switch (a_ChunkDesc.GetBiome(x, z)) - { - case biOcean: - case biPlains: - case biExtremeHills: - case biForest: - case biTaiga: - case biSwampland: - case biRiver: - case biFrozenOcean: - case biFrozenRiver: - case biIcePlains: - case biIceMountains: - case biForestHills: - case biTaigaHills: - case biExtremeHillsEdge: - case biJungle: - case biJungleHills: - { - FillColumnGrass(x, z, Height, a_ChunkDesc.GetBlockTypes()); - break; - } - case biDesertHills: - case biDesert: - case biBeach: - { - FillColumnSand(x, z, Height, a_ChunkDesc.GetBlockTypes()); - break; - } - case biMushroomIsland: - case biMushroomShore: - { - FillColumnMycelium(x, z, Height, a_ChunkDesc.GetBlockTypes()); - break; - } - default: - { - // TODO - ASSERT(!"CompoGenBiomal: Biome not implemented yet!"); - break; - } - } - } - else - { - switch (a_ChunkDesc.GetBiome(x, z)) - { - case biDesert: - case biBeach: - { - // Fill with water, sand, sandstone and stone - FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes()); - break; - } - default: - { - // Fill with water, sand/dirt/clay mix and stone - if (m_Noise.CubicNoise2D(0.3f * (cChunkDef::Width * ChunkX + x), 0.3f * (cChunkDef::Width * ChunkZ + z)) < 0) - { - FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes()); - } - else - { - FillColumnWaterDirt(x, z, Height, a_ChunkDesc.GetBlockTypes()); - } - break; - } - } // switch (biome) - a_ChunkDesc.SetHeight(x, z, m_SeaLevel + 1); - } // else (under water) - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - } // for x - } // for z -} - - - - - -void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile) -{ - m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1; -} - - - - - -void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) -{ - BLOCKTYPE Pattern[] = - { - E_BLOCK_GRASS, - E_BLOCK_DIRT, - E_BLOCK_DIRT, - E_BLOCK_DIRT, - } ; - FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); - - for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) - { - cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); - } -} - - - - - -void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) -{ - BLOCKTYPE Pattern[] = - { - E_BLOCK_SAND, - E_BLOCK_SAND, - E_BLOCK_SAND, - E_BLOCK_SANDSTONE, - } ; - FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); - - for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) - { - cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); - } -} - - - - - - -void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) -{ - BLOCKTYPE Pattern[] = - { - E_BLOCK_MYCELIUM, - E_BLOCK_DIRT, - E_BLOCK_DIRT, - E_BLOCK_DIRT, - } ; - FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); - - for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) - { - cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); - } -} - - - - - -void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) -{ - FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes); - for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++) - { - cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER); - } -} - - - - - -void cCompoGenBiomal::FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) -{ - // Dirt - BLOCKTYPE Pattern[] = - { - E_BLOCK_DIRT, - E_BLOCK_DIRT, - E_BLOCK_DIRT, - E_BLOCK_DIRT, - } ; - FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); - - for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) - { - cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); - } - for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++) - { - cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER); - } -} - - - - - - -void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize) -{ - for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++) - { - cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCompoGenNether: - -cCompoGenNether::cCompoGenNether(int a_Seed) : - m_Noise1(a_Seed + 10), - m_Noise2(a_Seed * a_Seed * 10 + a_Seed * 1000 + 6000), - m_Threshold(0) -{ -} - - - - - -void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight(); - - const int SEGMENT_HEIGHT = 8; - const int INTERPOL_X = 16; // Must be a divisor of 16 - const int INTERPOL_Z = 16; // Must be a divisor of 16 - // Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately. - // Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them - // Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it. - - int FloorBuf1[17 * 17]; - int FloorBuf2[17 * 17]; - int * FloorHi = FloorBuf1; - int * FloorLo = FloorBuf2; - int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - - // Interpolate the lowest floor: - for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) - { - FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] = - m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) * - m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) / - 256; - } // for x, z - FloorLo[] - LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); - - // Interpolate segments: - for (int Segment = 0; Segment < MaxHeight; Segment += SEGMENT_HEIGHT) - { - // First update the high floor: - for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) - { - FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] = - m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) * - m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / - 256; - } // for x, z - FloorLo[] - LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); - - // Interpolate between FloorLo and FloorHi: - for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) - { - int Lo = FloorLo[x + 17 * z] / 256; - int Hi = FloorHi[x + 17 * z] / 256; - for (int y = 0; y < SEGMENT_HEIGHT; y++) - { - int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; - a_ChunkDesc.SetBlockType(x, y + Segment, z, (Val < m_Threshold) ? E_BLOCK_NETHERRACK : E_BLOCK_AIR); - } - } - - // Swap the floors: - std::swap(FloorLo, FloorHi); - } - - // Bedrock at the bottom and at the top: - for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) - { - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - a_ChunkDesc.SetBlockType(x, a_ChunkDesc.GetHeight(x, z), z, E_BLOCK_BEDROCK); - } -} - - - - - -void cCompoGenNether::InitializeCompoGen(cIniFile & a_IniFile) -{ - m_Threshold = a_IniFile.GetValueSetI("Generator", "NetherThreshold", m_Threshold); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCompoGenCache: - -cCompoGenCache::cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize) : - m_Underlying(a_Underlying), - m_CacheSize(a_CacheSize), - m_CacheOrder(new int[a_CacheSize]), - m_CacheData(new sCacheData[a_CacheSize]), - m_NumHits(0), - m_NumMisses(0), - m_TotalChain(0) -{ - for (int i = 0; i < m_CacheSize; i++) - { - m_CacheOrder[i] = i; - m_CacheData[i].m_ChunkX = 0x7fffffff; - m_CacheData[i].m_ChunkZ = 0x7fffffff; - } -} - - - - - -cCompoGenCache::~cCompoGenCache() -{ - delete[] m_CacheData; - delete[] m_CacheOrder; -} - - - - - -void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - #ifdef _DEBUG - if (((m_NumHits + m_NumMisses) % 1024) == 10) - { - LOGD("CompoGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); - LOGD("CompoGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); - } - #endif // _DEBUG - - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - - for (int i = 0; i < m_CacheSize; i++) - { - if ( - (m_CacheData[m_CacheOrder[i]].m_ChunkX != ChunkX) || - (m_CacheData[m_CacheOrder[i]].m_ChunkZ != ChunkZ) - ) - { - continue; - } - // Found it in the cache - int Idx = m_CacheOrder[i]; - - // Move to front: - for (int j = i; j > 0; j--) - { - m_CacheOrder[j] = m_CacheOrder[j - 1]; - } - m_CacheOrder[0] = Idx; - - // Use the cached data: - memcpy(a_ChunkDesc.GetBlockTypes(), m_CacheData[Idx].m_BlockTypes, sizeof(a_ChunkDesc.GetBlockTypes())); - memcpy(a_ChunkDesc.GetBlockMetasUncompressed(), m_CacheData[Idx].m_BlockMetas, sizeof(a_ChunkDesc.GetBlockMetasUncompressed())); - - m_NumHits++; - m_TotalChain += i; - return; - } // for i - cache - - // Not in the cache: - m_NumMisses++; - m_Underlying.ComposeTerrain(a_ChunkDesc); - - // Insert it as the first item in the MRU order: - int Idx = m_CacheOrder[m_CacheSize - 1]; - for (int i = m_CacheSize - 1; i > 0; i--) - { - m_CacheOrder[i] = m_CacheOrder[i - 1]; - } // for i - m_CacheOrder[] - m_CacheOrder[0] = Idx; - memcpy(m_CacheData[Idx].m_BlockTypes, a_ChunkDesc.GetBlockTypes(), sizeof(a_ChunkDesc.GetBlockTypes())); - memcpy(m_CacheData[Idx].m_BlockMetas, a_ChunkDesc.GetBlockMetasUncompressed(), sizeof(a_ChunkDesc.GetBlockMetasUncompressed())); - m_CacheData[Idx].m_ChunkX = ChunkX; - m_CacheData[Idx].m_ChunkZ = ChunkZ; -} - - - - - -void cCompoGenCache::InitializeCompoGen(cIniFile & a_IniFile) -{ - m_Underlying.InitializeCompoGen(a_IniFile); -} - - - - diff --git a/source/Generating/CompoGen.h b/source/Generating/CompoGen.h deleted file mode 100644 index 2ee286b06..000000000 --- a/source/Generating/CompoGen.h +++ /dev/null @@ -1,182 +0,0 @@ - -// CompoGen.h - -/* Interfaces to the various terrain composition generators: - - cCompoGenSameBlock - - cCompoGenDebugBiomes - - cCompoGenClassic - - cCompoGenBiomal - - cCompoGenNether - - cCompoGenCache -*/ - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cCompoGenSameBlock : - public cTerrainCompositionGen -{ -public: - cCompoGenSameBlock(void) : - m_BlockType(E_BLOCK_STONE), - m_IsBedrocked(true) - {} - -protected: - - BLOCKTYPE m_BlockType; - bool m_IsBedrocked; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; - virtual void InitializeCompoGen(cIniFile & a_IniFile) override; -} ; - - - - - -class cCompoGenDebugBiomes : - public cTerrainCompositionGen -{ -public: - cCompoGenDebugBiomes(void) {} - -protected: - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cCompoGenClassic : - public cTerrainCompositionGen -{ -public: - cCompoGenClassic(void); - -protected: - - int m_SeaLevel; - int m_BeachHeight; - int m_BeachDepth; - BLOCKTYPE m_BlockTop; - BLOCKTYPE m_BlockMiddle; - BLOCKTYPE m_BlockBottom; - BLOCKTYPE m_BlockBeach; - BLOCKTYPE m_BlockBeachBottom; - BLOCKTYPE m_BlockSea; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; - virtual void InitializeCompoGen(cIniFile & a_IniFile) override; -} ; - - - - - -class cCompoGenBiomal : - public cTerrainCompositionGen -{ -public: - cCompoGenBiomal(int a_Seed) : - m_Noise(a_Seed + 1000), - m_SeaLevel(62) - { - } - -protected: - - cNoise m_Noise; - int m_SeaLevel; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; - virtual void InitializeCompoGen(cIniFile & a_IniFile) override; - - void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); - void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); - void FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); - void FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); - void FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); - - void FillColumnPattern (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize); -} ; - - - - - -class cCompoGenNether : - public cTerrainCompositionGen -{ -public: - cCompoGenNether(int a_Seed); - -protected: - cNoise m_Noise1; - cNoise m_Noise2; - - int m_Threshold; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; - virtual void InitializeCompoGen(cIniFile & a_IniFile) override; -} ; - - - - - -/// Caches most-recently-used chunk composition of another composition generator. Caches only the types and metas -class cCompoGenCache : - public cTerrainCompositionGen -{ -public: - cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize); // Doesn't take ownership of a_Underlying - ~cCompoGenCache(); - - // cTerrainCompositionGen override: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; - virtual void InitializeCompoGen(cIniFile & a_IniFile) override; - -protected: - - cTerrainCompositionGen & m_Underlying; - - struct sCacheData - { - int m_ChunkX; - int m_ChunkZ; - cChunkDef::BlockTypes m_BlockTypes; - cChunkDesc::BlockNibbleBytes m_BlockMetas; // The metas are uncompressed, 1 meta per byte - } ; - - // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data - int m_CacheSize; - int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array - sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used - - // Cache statistics - int m_NumHits; - int m_NumMisses; - int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) -} ; - - - - diff --git a/source/Generating/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp deleted file mode 100644 index 2637b64e7..000000000 --- a/source/Generating/ComposableGenerator.cpp +++ /dev/null @@ -1,501 +0,0 @@ - -// ComposableGenerator.cpp - -// Implements the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks - -#include "Globals.h" - -#include "ComposableGenerator.h" -#include "../World.h" -#include "../../iniFile/iniFile.h" -#include "../Root.h" - -// Individual composed algorithms: -#include "BioGen.h" -#include "HeiGen.h" -#include "CompoGen.h" -#include "StructGen.h" -#include "FinishGen.h" - -#include "Caves.h" -#include "DistortedHeightmap.h" -#include "EndGen.h" -#include "MineShafts.h" -#include "Noise3DGenerator.h" -#include "Ravines.h" - - - - - - - - - - -cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) : - super(a_ChunkGenerator), - m_BiomeGen(NULL), - m_HeightGen(NULL), - m_CompositionGen(NULL), - m_UnderlyingBiomeGen(NULL), - m_UnderlyingHeightGen(NULL), - m_UnderlyingCompositionGen(NULL) -{ -} - - - - - -cComposableGenerator::~cComposableGenerator() -{ - // Delete the generating composition: - for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) - { - delete *itr; - } - m_FinishGens.clear(); - for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) - { - delete *itr; - } - m_StructureGens.clear(); - - delete m_CompositionGen; - m_CompositionGen = NULL; - delete m_HeightGen; - m_HeightGen = NULL; - delete m_BiomeGen; - m_BiomeGen = NULL; - delete m_UnderlyingCompositionGen; - m_UnderlyingCompositionGen = NULL; - delete m_UnderlyingHeightGen; - m_UnderlyingHeightGen = NULL; - delete m_UnderlyingBiomeGen; - m_UnderlyingBiomeGen = NULL; -} - - - - - -void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) -{ - super::Initialize(a_World, a_IniFile); - - InitBiomeGen(a_IniFile); - InitHeightGen(a_IniFile); - InitCompositionGen(a_IniFile); - InitStructureGens(a_IniFile); - InitFinishGens(a_IniFile); -} - - - - - -void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - if (m_BiomeGen != NULL) // Quick fix for generator deinitializing before the world storage finishes loading - { - m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); - } -} - - - - - -void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) -{ - if (a_ChunkDesc.IsUsingDefaultBiomes()) - { - m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap()); - } - - if (a_ChunkDesc.IsUsingDefaultHeight()) - { - m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap()); - } - - if (a_ChunkDesc.IsUsingDefaultComposition()) - { - m_CompositionGen->ComposeTerrain(a_ChunkDesc); - } - - if (a_ChunkDesc.IsUsingDefaultStructures()) - { - for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) - { - (*itr)->GenStructures(a_ChunkDesc); - } // for itr - m_StructureGens[] - } - - if (a_ChunkDesc.IsUsingDefaultFinish()) - { - for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) - { - (*itr)->GenFinish(a_ChunkDesc); - } // for itr - m_FinishGens[] - } -} - - - - - -void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile) -{ - AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", ""); - if (BiomeGenName.empty()) - { - LOGWARN("[Generator] BiomeGen value not set in world.ini, using \"MultiStepMap\"."); - BiomeGenName = "MultiStepMap"; - } - - int Seed = m_ChunkGenerator.GetSeed(); - bool CacheOffByDefault = false; - if (NoCaseCompare(BiomeGenName, "constant") == 0) - { - m_BiomeGen = new cBioGenConstant; - CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :) - } - else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0) - { - m_BiomeGen = new cBioGenCheckerboard; - CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data - } - else if (NoCaseCompare(BiomeGenName, "voronoi") == 0) - { - m_BiomeGen = new cBioGenVoronoi(Seed); - } - else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0) - { - m_BiomeGen = new cBioGenDistortedVoronoi(Seed); - } - else - { - if (NoCaseCompare(BiomeGenName, "multistepmap") != 0) - { - LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str()); - } - m_BiomeGen = new cBioGenMultiStepMap(Seed); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cBioGenMultiStepMap..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 5000; x++) - { - cChunkDef::BiomeMap Biomes; - m_BiomeGen->GenBiomes(x * 5, x * 5, Biomes); - } - clock_t Duration = clock() - BeginTick; - LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); - //*/ - } - - // Add a cache, if requested: - int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64); - if (CacheSize > 0) - { - if (CacheSize < 4) - { - LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", - CacheSize, 4 - ); - CacheSize = 4; - } - LOGD("Using a cache for biomegen of size %d.", CacheSize); - m_UnderlyingBiomeGen = m_BiomeGen; - m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize); - } - m_BiomeGen->InitializeBiomeGen(a_IniFile); -} - - - - - -void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) -{ - AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", ""); - if (HeightGenName.empty()) - { - LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\"."); - HeightGenName = "Biomal"; - } - - int Seed = m_ChunkGenerator.GetSeed(); - bool CacheOffByDefault = false; - if (NoCaseCompare(HeightGenName, "flat") == 0) - { - m_HeightGen = new cHeiGenFlat; - CacheOffByDefault = true; // We're generating faster than a cache would retrieve data - } - else if (NoCaseCompare(HeightGenName, "classic") == 0) - { - m_HeightGen = new cHeiGenClassic(Seed); - } - else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) - { - m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen); - } - else if (NoCaseCompare(HeightGenName, "End") == 0) - { - m_HeightGen = new cEndGen(Seed); - } - else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) - { - m_HeightGen = new cNoise3DComposable(Seed); - } - else // "biomal" or - { - if (NoCaseCompare(HeightGenName, "biomal") != 0) - { - LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); - } - m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cHeiGenBiomal..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 500; x++) - { - cChunkDef::HeightMap Heights; - m_HeightGen->GenHeightMap(x * 5, x * 5, Heights); - } - clock_t Duration = clock() - BeginTick; - LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); - //*/ - } - - // Read the settings: - m_HeightGen->InitializeHeightGen(a_IniFile); - - // Add a cache, if requested: - int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64); - if (CacheSize > 0) - { - if (CacheSize < 4) - { - LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", - CacheSize, 4 - ); - CacheSize = 4; - } - LOGD("Using a cache for Heightgen of size %d.", CacheSize); - m_UnderlyingHeightGen = m_HeightGen; - m_HeightGen = new cHeiGenCache(*m_UnderlyingHeightGen, CacheSize); - } -} - - - - - -void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) -{ - int Seed = m_ChunkGenerator.GetSeed(); - AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", ""); - if (CompoGenName.empty()) - { - LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\"."); - CompoGenName = "Biomal"; - } - if (NoCaseCompare(CompoGenName, "sameblock") == 0) - { - m_CompositionGen = new cCompoGenSameBlock; - } - else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) - { - m_CompositionGen = new cCompoGenDebugBiomes; - } - else if (NoCaseCompare(CompoGenName, "classic") == 0) - { - m_CompositionGen = new cCompoGenClassic; - } - else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) - { - m_CompositionGen = new cDistortedHeightmap(Seed, *m_BiomeGen); - } - else if (NoCaseCompare(CompoGenName, "end") == 0) - { - m_CompositionGen = new cEndGen(Seed); - } - else if (NoCaseCompare(CompoGenName, "nether") == 0) - { - m_CompositionGen = new cCompoGenNether(Seed); - } - else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) - { - m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed()); - } - else - { - if (NoCaseCompare(CompoGenName, "biomal") != 0) - { - LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str()); - } - m_CompositionGen = new cCompoGenBiomal(Seed); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cCompoGenBiomal..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 500; x++) - { - cChunkDesc Desc(200 + x * 8, 200 + x * 8); - m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap()); - m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap()); - m_CompositionGen->ComposeTerrain(Desc); - } - clock_t Duration = clock() - BeginTick; - LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); - //*/ - } - - // Read the settings from the ini file: - m_CompositionGen->InitializeCompoGen(a_IniFile); - - int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64); - if (CompoGenCacheSize > 1) - { - m_UnderlyingCompositionGen = m_CompositionGen; - m_CompositionGen = new cCompoGenCache(*m_UnderlyingCompositionGen, 32); - } -} - - - - - -void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile) -{ - AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees"); - - int Seed = m_ChunkGenerator.GetSeed(); - AStringVector Str = StringSplitAndTrim(Structures, ","); - for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) - { - if (NoCaseCompare(*itr, "DualRidgeCaves") == 0) - { - float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3); - m_StructureGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold)); - } - else if (NoCaseCompare(*itr, "DirectOverhangs") == 0) - { - m_StructureGens.push_back(new cStructGenDirectOverhangs(Seed)); - } - else if (NoCaseCompare(*itr, "DistortedMembraneOverhangs") == 0) - { - m_StructureGens.push_back(new cStructGenDistortedMembraneOverhangs(Seed)); - } - else if (NoCaseCompare(*itr, "LavaLakes") == 0) - { - int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10); - m_StructureGens.push_back(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, *m_HeightGen, Probability)); - } - else if (NoCaseCompare(*itr, "MarbleCaves") == 0) - { - m_StructureGens.push_back(new cStructGenMarbleCaves(Seed)); - } - else if (NoCaseCompare(*itr, "MineShafts") == 0) - { - int GridSize = a_IniFile.GetValueSetI("Generator", "MineShaftsGridSize", 512); - int MaxSystemSize = a_IniFile.GetValueSetI("Generator", "MineShaftsMaxSystemSize", 160); - int ChanceCorridor = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCorridor", 600); - int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200); - int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200); - m_StructureGens.push_back(new cStructGenMineShafts( - Seed, GridSize, MaxSystemSize, - ChanceCorridor, ChanceCrossing, ChanceStaircase - )); - } - else if (NoCaseCompare(*itr, "OreNests") == 0) - { - m_StructureGens.push_back(new cStructGenOreNests(Seed)); - } - else if (NoCaseCompare(*itr, "Ravines") == 0) - { - m_StructureGens.push_back(new cStructGenRavines(Seed, 128)); - } - else if (NoCaseCompare(*itr, "Trees") == 0) - { - m_StructureGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen)); - } - else if (NoCaseCompare(*itr, "WaterLakes") == 0) - { - int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25); - m_StructureGens.push_back(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, *m_HeightGen, Probability)); - } - else if (NoCaseCompare(*itr, "WormNestCaves") == 0) - { - m_StructureGens.push_back(new cStructGenWormNestCaves(Seed)); - } - else - { - LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str()); - } - } // for itr - Str[] -} - - - - - -void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) -{ - int Seed = m_ChunkGenerator.GetSeed(); - AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator"); - - AStringVector Str = StringSplitAndTrim(Structures, ","); - for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) - { - // Finishers, alpha-sorted: - if (NoCaseCompare(*itr, "BottomLava") == 0) - { - int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10; - int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel); - m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel)); - } - else if (NoCaseCompare(*itr, "DeadBushes") == 0) - { - m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND)); - } - else if (NoCaseCompare(*itr, "Ice") == 0) - { - m_FinishGens.push_back(new cFinishGenIce); - } - else if (NoCaseCompare(*itr, "LavaSprings") == 0) - { - m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World)); - } - else if (NoCaseCompare(*itr, "Lilypads") == 0) - { - m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER)); - } - else if (NoCaseCompare(*itr, "PreSimulator") == 0) - { - m_FinishGens.push_back(new cFinishGenPreSimulator); - } - else if (NoCaseCompare(*itr, "Snow") == 0) - { - m_FinishGens.push_back(new cFinishGenSnow); - } - else if (NoCaseCompare(*itr, "SprinkleFoliage") == 0) - { - m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed)); - } - else if (NoCaseCompare(*itr, "WaterSprings") == 0) - { - m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World)); - } - } // for itr - Str[] -} - - - - diff --git a/source/Generating/ComposableGenerator.h b/source/Generating/ComposableGenerator.h deleted file mode 100644 index d5e33a439..000000000 --- a/source/Generating/ComposableGenerator.h +++ /dev/null @@ -1,181 +0,0 @@ - -// ComposableGenerator.h - -// Declares the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks - -/* -Generating works by composing several algorithms: -Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage -Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others, -based on user's preferences in the world.ini. -See http://forum.mc-server.org/showthread.php?tid=409 for details. -*/ - - - - - -#pragma once - -#include "ChunkGenerator.h" -#include "ChunkDesc.h" - - - - - -// fwd: Noise3DGenerator.h -class cNoise3DComposable; - -// fwd: DistortedHeightmap.h -class cDistortedHeightmap; - - - - - -/** The interface that a biome generator must implement -A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output. -The output array is sequenced in the same way as the MapChunk packet's biome data. -*/ -class cBiomeGen -{ -public: - virtual ~cBiomeGen() {} // Force a virtual destructor in descendants - - /// Generates biomes for the given chunk - virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; - - /// Reads parameters from the ini file, prepares generator for use. - virtual void InitializeBiomeGen(cIniFile & a_IniFile) {} -} ; - - - - - -/** The interface that a terrain height generator must implement -A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk. -The output array is sequenced in the same way as the BiomeGen's biome data. -The generator may request biome information from the underlying BiomeGen, it may even request information for -other chunks than the one it's currently generating (possibly neighbors - for averaging) -*/ -class cTerrainHeightGen -{ -public: - virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants - - /// Generates heightmap for the given chunk - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; - - /// Reads parameters from the ini file, prepares generator for use. - virtual void InitializeHeightGen(cIniFile & a_IniFile) {} -} ; - - - - - -/** The interface that a terrain composition generator must implement -Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with -the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose, -but it may request information for other chunks than the one it's currently generating from them. -*/ -class cTerrainCompositionGen -{ -public: - virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants - - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0; - - /// Reads parameters from the ini file, prepares generator for use. - virtual void InitializeCompoGen(cIniFile & a_IniFile) {} -} ; - - - - - -/** The interface that a structure generator must implement -Structures are generated after the terrain composition took place. It should modify the blocktype data to account -for whatever structures the generator is generating. -Note that ores are considered structures too, at least from the interface point of view. -Also note that a worldgenerator may contain multiple structure generators, one for each type of structure -*/ -class cStructureGen -{ -public: - virtual ~cStructureGen() {} // Force a virtual destructor in descendants - - virtual void GenStructures(cChunkDesc & a_ChunkDesc) = 0; -} ; - -typedef std::list cStructureGenList; - - - - - -/** The interface that a finisher must implement -Finisher implements small additions after all structures have been generated. -*/ -class cFinishGen -{ -public: - virtual ~cFinishGen() {} // Force a virtual destructor in descendants - - virtual void GenFinish(cChunkDesc & a_ChunkDesc) = 0; -} ; - -typedef std::list cFinishGenList; - - - - - -class cComposableGenerator : - public cChunkGenerator::cGenerator -{ - typedef cChunkGenerator::cGenerator super; - -public: - cComposableGenerator(cChunkGenerator & a_ChunkGenerator); - virtual ~cComposableGenerator(); - - virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; - virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; - -protected: - // The generation composition: - cBiomeGen * m_BiomeGen; - cTerrainHeightGen * m_HeightGen; - cTerrainCompositionGen * m_CompositionGen; - cStructureGenList m_StructureGens; - cFinishGenList m_FinishGens; - - // Generators underlying the caches: - cBiomeGen * m_UnderlyingBiomeGen; - cTerrainHeightGen * m_UnderlyingHeightGen; - cTerrainCompositionGen * m_UnderlyingCompositionGen; - - - /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly - void InitBiomeGen(cIniFile & a_IniFile); - - /// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly - void InitHeightGen(cIniFile & a_IniFile); - - /// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly - void InitCompositionGen(cIniFile & a_IniFile); - - /// Reads the structures to generate from the ini and initializes m_StructureGens accordingly - void InitStructureGens(cIniFile & a_IniFile); - - /// Reads the finishers from the ini and initializes m_FinishGens accordingly - void InitFinishGens(cIniFile & a_IniFile); -} ; - - - - diff --git a/source/Generating/DistortedHeightmap.cpp b/source/Generating/DistortedHeightmap.cpp deleted file mode 100644 index 98eab31b5..000000000 --- a/source/Generating/DistortedHeightmap.cpp +++ /dev/null @@ -1,444 +0,0 @@ - -// DistortedHeightmap.cpp - -// Implements the cDistortedHeightmap class representing the height and composition generator capable of overhangs - -#include "Globals.h" - -#include "DistortedHeightmap.h" -#include "../OSSupport/File.h" -#include "../../iniFile/iniFile.h" -#include "../LinearUpscale.h" - - - - - -/** This table assigns a relative maximum overhang size in each direction to biomes. -Both numbers indicate a number which will multiply the noise value for each coord; -this means that you can have different-sized overhangs in each direction. -Usually you'd want to keep both numbers the same. -The numbers are "relative", not absolute maximum; overhangs of a slightly larger size are possible -due to the way that noise is calculated. -*/ -const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[biNumBiomes] = -{ - /* Biome | AmpX | AmpZ */ - /* biOcean */ { 1.5f, 1.5f}, - /* biPlains */ { 0.5f, 0.5f}, - /* biDesert */ { 0.5f, 0.5f}, - /* biExtremeHills */ {16.0f, 16.0f}, - /* biForest */ { 3.0f, 3.0f}, - /* biTaiga */ { 1.5f, 1.5f}, - - /* biSwampland */ { 0.0f, 0.0f}, - /* biRiver */ { 0.0f, 0.0f}, - /* biNether */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing - /* biSky */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing - /* biFrozenOcean */ { 0.0f, 0.0f}, - /* biFrozenRiver */ { 0.0f, 0.0f}, - /* biIcePlains */ { 0.0f, 0.0f}, - /* biIceMountains */ { 8.0f, 8.0f}, - /* biMushroomIsland */ { 4.0f, 4.0f}, - /* biMushroomShore */ { 0.0f, 0.0f}, - /* biBeach */ { 0.0f, 0.0f}, - /* biDesertHills */ { 5.0f, 5.0f}, - /* biForestHills */ { 6.0f, 6.0f}, - /* biTaigaHills */ { 8.0f, 8.0f}, - /* biExtremeHillsEdge */ { 7.0f, 7.0f}, - /* biJungle */ { 0.0f, 0.0f}, - /* biJungleHills */ { 8.0f, 8.0f}, -} ; - - - - - -cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) : - m_NoiseDistortX(a_Seed + 1000), - m_NoiseDistortZ(a_Seed + 2000), - m_OceanFloorSelect(a_Seed + 3000), - m_BiomeGen(a_BiomeGen), - m_UnderlyingHeiGen(a_Seed, a_BiomeGen), - m_HeightGen(m_UnderlyingHeiGen, 64) -{ - m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); - m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); - m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); - - m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); - m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); - m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); -} - - - - - -void cDistortedHeightmap::Initialize(cIniFile & a_IniFile) -{ - if (m_IsInitialized) - { - return; - } - - // Read the params from the INI file: - m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62); - m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10); - m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10); - m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10); - - m_IsInitialized = true; -} - - - - - -void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ) -{ - if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ)) - { - return; - } - m_CurChunkX = a_ChunkX; - m_CurChunkZ = a_ChunkZ; - - - m_HeightGen.GenHeightMap(a_ChunkX, a_ChunkZ, m_CurChunkHeights); - UpdateDistortAmps(); - GenerateHeightArray(); -} - - - - - -void cDistortedHeightmap::GenerateHeightArray(void) -{ - // Generate distortion noise: - NOISE_DATATYPE DistortNoiseX[DIM_X * DIM_Y * DIM_Z]; - NOISE_DATATYPE DistortNoiseZ[DIM_X * DIM_Y * DIM_Z]; - NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; - NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width)) / m_FrequencyX; - NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_CurChunkX + 1) * cChunkDef::Width - 1)) / m_FrequencyX; - NOISE_DATATYPE StartY = 0; - NOISE_DATATYPE EndY = ((NOISE_DATATYPE)(257)) / m_FrequencyY; - NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width)) / m_FrequencyZ; - NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_CurChunkZ + 1) * cChunkDef::Width - 1)) / m_FrequencyZ; - - m_NoiseDistortX.Generate3D(DistortNoiseX, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); - m_NoiseDistortZ.Generate3D(DistortNoiseZ, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); - - // The distorted heightmap, before linear upscaling - NOISE_DATATYPE DistHei[DIM_X * DIM_Y * DIM_Z]; - - // Distort the heightmap using the distortion: - for (int z = 0; z < DIM_Z; z++) - { - int AmpIdx = z * DIM_X; - for (int y = 0; y < DIM_Y; y++) - { - int NoiseArrayIdx = z * DIM_X * DIM_Y + y * DIM_X; - for (int x = 0; x < DIM_X; x++) - { - NOISE_DATATYPE DistX = DistortNoiseX[NoiseArrayIdx + x] * m_DistortAmpX[AmpIdx + x]; - NOISE_DATATYPE DistZ = DistortNoiseZ[NoiseArrayIdx + x] * m_DistortAmpZ[AmpIdx + x]; - DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x * INTERPOL_X); - DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z * INTERPOL_Z); - // Adding 0.5 helps alleviate the interpolation artifacts - DistHei[NoiseArrayIdx + x] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5; - } - } - } - - // Upscale the distorted heightmap into full dimensions: - LinearUpscale3DArray( - DistHei, DIM_X, DIM_Y, DIM_Z, - m_DistortedHeightmap, INTERPOL_X, INTERPOL_Y, INTERPOL_Z - ); - - // DEBUG: Debug3DNoise(m_DistortedHeightmap, 17, 257, 17, Printf("DistortedHeightmap_%d_%d", m_CurChunkX, m_CurChunkZ)); -} - - - - - -void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - PrepareState(a_ChunkX, a_ChunkZ); - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int NoiseArrayIdx = x + 17 * 257 * z; - cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1); - for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--) - { - int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; - if (y < HeightMapHeight) - { - cChunkDef::SetHeight(a_HeightMap, x, z, y); - break; - } - } // for y - } // for x - } // for z -} - - - - - -void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile) -{ - Initialize(a_IniFile); -} - - - - - -void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - // Frequencies for the ocean floor selecting noise: - NOISE_DATATYPE FrequencyX = 3; - NOISE_DATATYPE FrequencyZ = 3; - - // Prepare the internal state for generating this chunk: - PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); - - // Compose: - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int NoiseArrayIdx = x + 17 * 257 * z; - int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; - bool HasHadWater = false; - for (int y = LastAir - 1; y > 0; y--) - { - int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; - - if (y >= HeightMapHeight) - { - // "air" part - LastAir = y; - if (y < m_SeaLevel) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); - HasHadWater = true; - } - continue; - } - // "ground" part: - if (y < LastAir - 4) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); - continue; - } - if (HasHadWater) - { - // Decide between clay, sand and dirt - NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / FrequencyX; - NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / FrequencyZ; - NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); - if (Val < -0.95) - { - // Clay: - switch (LastAir - y) - { - case 0: - case 1: - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_CLAY); - break; - } - case 2: - case 3: - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); - break; - } - case 4: - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SANDSTONE); - break; - } - } // switch (floor depth) - } - else if (Val < 0) - { - a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); - } - else - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_DIRT); - } - } - else - { - switch (a_ChunkDesc.GetBiome(x, z)) - { - case biOcean: - case biPlains: - case biExtremeHills: - case biForest: - case biTaiga: - case biSwampland: - case biRiver: - case biFrozenOcean: - case biFrozenRiver: - case biIcePlains: - case biIceMountains: - case biForestHills: - case biTaigaHills: - case biExtremeHillsEdge: - case biJungle: - case biJungleHills: - { - a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); - break; - } - case biDesertHills: - case biDesert: - case biBeach: - { - a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); - break; - } - case biMushroomIsland: - case biMushroomShore: - { - a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_MYCELIUM : E_BLOCK_DIRT); - break; - } - } - } - } // for y - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - } // for x - } // for z -} - - - - - -void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile) -{ - Initialize(a_IniFile); -} - - - - - -int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z) -{ - int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16); - int ChunkZ = (int)floor(a_Z / (NOISE_DATATYPE)16); - int RelX = (int)(a_X - (NOISE_DATATYPE)ChunkX * cChunkDef::Width); - int RelZ = (int)(a_Z - (NOISE_DATATYPE)ChunkZ * cChunkDef::Width); - - // If we're withing the same chunk, return the pre-cached heightmap: - if ((ChunkX == m_CurChunkX) && (ChunkZ == m_CurChunkZ)) - { - return cChunkDef::GetHeight(m_CurChunkHeights, RelX, RelZ); - } - - // Ask the cache: - HEIGHTTYPE res = 0; - if (m_HeightGen.GetHeightAt(ChunkX, ChunkZ, RelX, RelZ, res)) - { - // The height was in the cache - return res; - } - - // The height is not in the cache, generate full heightmap and get it there: - cChunkDef::HeightMap Heightmap; - m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap); - return cChunkDef::GetHeight(Heightmap, RelX, RelZ); -} - - - - - -void cDistortedHeightmap::UpdateDistortAmps(void) -{ - BiomeNeighbors Biomes; - for (int z = -1; z <= 1; z++) - { - for (int x = -1; x <= 1; x++) - { - m_BiomeGen.GenBiomes(m_CurChunkX + x, m_CurChunkZ + z, Biomes[x + 1][z + 1]); - } // for x - } // for z - - for (int z = 0; z < DIM_Z; z++) - { - for (int x = 0; x < DIM_Z; x++) - { - GetDistortAmpsAt(Biomes, x * INTERPOL_X, z * INTERPOL_Z, m_DistortAmpX[x + DIM_X * z], m_DistortAmpZ[x + DIM_X * z]); - } - } -} - - - - - -void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ) -{ - // Sum up how many biomes of each type there are in the neighborhood: - int BiomeCounts[biNumBiomes]; - memset(BiomeCounts, 0, sizeof(BiomeCounts)); - int Sum = 0; - for (int z = -8; z <= 8; z++) - { - int FinalZ = a_RelZ + z + cChunkDef::Width; - int IdxZ = FinalZ / cChunkDef::Width; - int ModZ = FinalZ % cChunkDef::Width; - int WeightZ = 9 - abs(z); - for (int x = -8; x <= 8; x++) - { - int FinalX = a_RelX + x + cChunkDef::Width; - int IdxX = FinalX / cChunkDef::Width; - int ModX = FinalX % cChunkDef::Width; - EMCSBiome Biome = cChunkDef::GetBiome(a_Neighbors[IdxX][IdxZ], ModX, ModZ); - if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts))) - { - continue; - } - int WeightX = 9 - abs(x); - BiomeCounts[Biome] += WeightX + WeightZ; - Sum += WeightX + WeightZ; - } // for x - } // for z - - if (Sum <= 0) - { - // No known biome around? Weird. Return a bogus value: - ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around"); - a_DistortAmpX = 16; - a_DistortAmpZ = 16; - } - - // For each biome type that has a nonzero count, calc its amps and add it: - NOISE_DATATYPE AmpX = 0; - NOISE_DATATYPE AmpZ = 0; - for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++) - { - AmpX += BiomeCounts[i] * m_GenParam[i].m_DistortAmpX; - AmpZ += BiomeCounts[i] * m_GenParam[i].m_DistortAmpZ; - } - a_DistortAmpX = AmpX / Sum; - a_DistortAmpZ = AmpZ / Sum; -} - - - - diff --git a/source/Generating/DistortedHeightmap.h b/source/Generating/DistortedHeightmap.h deleted file mode 100644 index 6d7007375..000000000 --- a/source/Generating/DistortedHeightmap.h +++ /dev/null @@ -1,108 +0,0 @@ - -// DistortedHeightmap.h - -// Declares the cDistortedHeightmap class representing the height and composition generator capable of overhangs - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "HeiGen.h" -#include "../Noise.h" - - - - - -#define NOISE_SIZE_Y (257 + 32) - - - - - -class cDistortedHeightmap : - public cTerrainHeightGen, - public cTerrainCompositionGen -{ -public: - cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen); - -protected: - typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; - - // Linear upscaling step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: - static const int INTERPOL_X = 8; - static const int INTERPOL_Y = 4; - static const int INTERPOL_Z = 8; - - // Linear upscaling buffer dimensions, calculated from the step sizes: - static const int DIM_X = 1 + (17 / INTERPOL_X); - static const int DIM_Y = 1 + (257 / INTERPOL_Y); - static const int DIM_Z = 1 + (17 / INTERPOL_Z); - - cPerlinNoise m_NoiseDistortX; - cPerlinNoise m_NoiseDistortZ; - cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor - - int m_SeaLevel; - NOISE_DATATYPE m_FrequencyX; - NOISE_DATATYPE m_FrequencyY; - NOISE_DATATYPE m_FrequencyZ; - - int m_CurChunkX; - int m_CurChunkZ; - NOISE_DATATYPE m_DistortedHeightmap[17 * 257 * 17]; - - cBiomeGen & m_BiomeGen; - cHeiGenBiomal m_UnderlyingHeiGen; // This generator provides us with base heightmap (before distortion) - cHeiGenCache m_HeightGen; // Cache above m_UnderlyingHeiGen - - /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization. - cChunkDef::HeightMap m_CurChunkHeights; - - // Per-biome terrain generator parameters: - struct sGenParam - { - NOISE_DATATYPE m_DistortAmpX; - NOISE_DATATYPE m_DistortAmpZ; - } ; - static const sGenParam m_GenParam[biNumBiomes]; - - // Distortion amplitudes for each direction, before linear upscaling - NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z]; - NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z]; - - /// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen) - bool m_IsInitialized; - - - /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap) - void PrepareState(int a_ChunkX, int a_ChunkZ); - - /// Generates the m_DistortedHeightmap array for the current chunk - void GenerateHeightArray(void); - - /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords - int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z); - - /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ - void UpdateDistortAmps(void); - - /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes - void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ); - - /// Reads the settings from the ini file. Skips reading if already initialized - void Initialize(cIniFile & a_IniFile); - - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - virtual void InitializeHeightGen(cIniFile & a_IniFile) override; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; - virtual void InitializeCompoGen(cIniFile & a_IniFile) override; -} ; diff --git a/source/Generating/EndGen.cpp b/source/Generating/EndGen.cpp deleted file mode 100644 index 3eba5c47b..000000000 --- a/source/Generating/EndGen.cpp +++ /dev/null @@ -1,217 +0,0 @@ - -// EndGen.cpp - -// Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen - -#include "Globals.h" -#include "EndGen.h" -#include "../../iniFile/iniFile.h" -#include "../LinearUpscale.h" - - - - - -enum -{ - // Interpolation cell size: - INTERPOL_X = 4, - INTERPOL_Y = 4, - INTERPOL_Z = 4, - - // Size of chunk data, downscaled before interpolation: - DIM_X = 16 / INTERPOL_X + 1, - DIM_Y = 256 / INTERPOL_Y + 1, - DIM_Z = 16 / INTERPOL_Z + 1, -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cEndGen: - -cEndGen::cEndGen(int a_Seed) : - m_Seed(a_Seed), - m_IslandSizeX(256), - m_IslandSizeY(96), - m_IslandSizeZ(256), - m_FrequencyX(80), - m_FrequencyY(80), - m_FrequencyZ(80) -{ - m_Perlin.AddOctave(1, 1); - m_Perlin.AddOctave(2, 0.5); - m_Perlin.AddOctave(4, 0.25); -} - - - - - -void cEndGen::Initialize(cIniFile & a_IniFile) -{ - m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX); - m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY); - m_IslandSizeZ = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeZ", m_IslandSizeZ); - - m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyX", m_FrequencyX); - m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyY", m_FrequencyY); - m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyZ", m_FrequencyZ); - - // Recalculate the min and max chunk coords of the island - m_MaxChunkX = (m_IslandSizeX + cChunkDef::Width - 1) / cChunkDef::Width; - m_MinChunkX = -m_MaxChunkX; - m_MaxChunkZ = (m_IslandSizeZ + cChunkDef::Width - 1) / cChunkDef::Width; - m_MinChunkZ = -m_MaxChunkZ; -} - - - - - -/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) -void cEndGen::PrepareState(int a_ChunkX, int a_ChunkZ) -{ - ASSERT(!IsChunkOutsideRange(a_ChunkX, a_ChunkZ)); // Should be filtered before calling this function - - if ((m_LastChunkX == a_ChunkX) && (m_LastChunkZ == a_ChunkZ)) - { - return; - } - - m_LastChunkX = a_ChunkX; - m_LastChunkZ = a_ChunkZ; - - GenerateNoiseArray(); -} - - - - - -/// Generates the m_NoiseArray array for the current chunk -void cEndGen::GenerateNoiseArray(void) -{ - NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] - NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] - - // Generate the downscaled noise: - NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width)) / m_FrequencyX; - NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_LastChunkX + 1) * cChunkDef::Width)) / m_FrequencyX; - NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width)) / m_FrequencyZ; - NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_LastChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ; - NOISE_DATATYPE StartY = 0; - NOISE_DATATYPE EndY = ((NOISE_DATATYPE)257) / m_FrequencyY; - m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace); - - // Add distance: - int idx = 0; - for (int y = 0; y < DIM_Y; y++) - { - NOISE_DATATYPE ValY = (NOISE_DATATYPE)(2 * INTERPOL_Y * y - m_IslandSizeY) / m_IslandSizeY; - ValY = ValY * ValY; - for (int z = 0; z < DIM_Z; z++) - { - NOISE_DATATYPE ValZ = (NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width + (z * cChunkDef::Width / (DIM_Z - 1))) / m_IslandSizeZ; - ValZ = ValZ * ValZ; - for (int x = 0; x < DIM_X; x++) - { - // NOISE_DATATYPE ValX = StartX + (EndX - StartX) * x / (DIM_X - 1); - NOISE_DATATYPE ValX = (NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width + (x * cChunkDef::Width / (DIM_X - 1))) / m_IslandSizeX; - ValX = ValX * ValX; - NoiseData[idx++] += ValX + ValZ + ValY; - } // for x - } // for z - } // for y - - // Upscale into real chunk size: - LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y); -} - - - - - -/// Returns true if the chunk is outside of the island's dimensions -bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ) -{ - return ( - (a_ChunkX < m_MinChunkX) || (a_ChunkX > m_MaxChunkX) || - (a_ChunkZ < m_MinChunkZ) || (a_ChunkZ > m_MaxChunkZ) - ); -} - - - - - -void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ)) - { - for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++) - { - a_HeightMap[i] = 0; - } - return; - } - - PrepareState(a_ChunkX, a_ChunkZ); - - int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - cChunkDef::SetHeight(a_HeightMap, x, z, MaxY); - for (int y = MaxY; y > 0; y--) - { - if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) - { - cChunkDef::SetHeight(a_HeightMap, x, z, y); - break; - } - } // for y - } // for x - } // for z -} - - - - - -void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ())) - { - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - return; - } - - PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); - - int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - for (int y = MaxY; y > 0; y--) - { - if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) - { - a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0); - } - else - { - a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0); - } - } // for y - } // for x - } // for z -} - - - - diff --git a/source/Generating/EndGen.h b/source/Generating/EndGen.h deleted file mode 100644 index 4904a0e3d..000000000 --- a/source/Generating/EndGen.h +++ /dev/null @@ -1,69 +0,0 @@ - -// EndGen.h - -// Declares the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cEndGen : - public cTerrainHeightGen, - public cTerrainCompositionGen -{ -public: - cEndGen(int a_Seed); - - void Initialize(cIniFile & a_IniFile); - -protected: - - /// Seed for the noise - int m_Seed; - - /// The Perlin noise used for generating - cPerlinNoise m_Perlin; - - // XYZ size of the "island", in blocks: - int m_IslandSizeX; - int m_IslandSizeY; - int m_IslandSizeZ; - - // XYZ Frequencies of the noise functions: - NOISE_DATATYPE m_FrequencyX; - NOISE_DATATYPE m_FrequencyY; - NOISE_DATATYPE m_FrequencyZ; - - // Minimum and maximum chunk coords for chunks inside the island area. Chunks outside won't get calculated at all - int m_MinChunkX, m_MaxChunkX; - int m_MinChunkZ, m_MaxChunkZ; - - // Noise array for the last chunk (in the noise range) - int m_LastChunkX; - int m_LastChunkZ; - NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y - - /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) - void PrepareState(int a_ChunkX, int a_ChunkZ); - - /// Generates the m_NoiseArray array for the current chunk - void GenerateNoiseArray(void); - - /// Returns true if the chunk is outside of the island's dimensions - bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ); - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; -} ; diff --git a/source/Generating/FinishGen.cpp b/source/Generating/FinishGen.cpp deleted file mode 100644 index 8899e4bd0..000000000 --- a/source/Generating/FinishGen.cpp +++ /dev/null @@ -1,664 +0,0 @@ - -// FinishGen.cpp - -/* Implements the various finishing generators: - - cFinishGenSnow - - cFinishGenIce - - cFinishGenSprinkleFoliage -*/ - -#include "Globals.h" - -#include "FinishGen.h" -#include "../Noise.h" -#include "../BlockID.h" -#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway() -#include "../World.h" - - - - - -#define DEF_NETHER_WATER_SPRINGS "0, 1; 255, 1" -#define DEF_NETHER_LAVA_SPRINGS "0, 0; 30, 0; 31, 50; 120, 50; 127, 0" -#define DEF_OVERWORLD_WATER_SPRINGS "0, 0; 10, 10; 11, 75; 16, 83; 20, 83; 24, 78; 32, 62; 40, 40; 44, 15; 48, 7; 56, 2; 64, 1; 255, 0" -#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0" -#define DEF_END_WATER_SPRINGS "0, 1; 255, 1" -#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1" - - - - - -static inline bool IsWater(BLOCKTYPE a_BlockType) -{ - return (a_BlockType == E_BLOCK_STATIONARY_WATER) || (a_BlockType == E_BLOCK_WATER); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFinishGenSprinkleFoliage: - -bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ) -{ - // We'll be doing comparison to neighbors, so require the coords to be 1 block away from the chunk edges: - if ( - (a_RelX < 1) || (a_RelX >= cChunkDef::Width - 1) || - (a_RelY < 1) || (a_RelY >= cChunkDef::Height - 2) || - (a_RelZ < 1) || (a_RelZ >= cChunkDef::Width - 1) - ) - { - return false; - } - - // Only allow dirt, grass or sand below sugarcane: - switch (a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ)) - { - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_SAND: - { - break; - } - default: - { - return false; - } - } - - // Water is required next to the block below the sugarcane: - if ( - !IsWater(a_ChunkDesc.GetBlockType(a_RelX - 1, a_RelY, a_RelZ)) && - !IsWater(a_ChunkDesc.GetBlockType(a_RelX + 1, a_RelY, a_RelZ)) && - !IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ - 1)) && - !IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ + 1)) - ) - { - return false; - } - - // All conditions met, place a sugarcane here: - a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE); - return true; -} - - - - - -void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc) -{ - // Generate small foliage (1-block): - - // TODO: Update heightmap with 1-block-tall foliage - for (int z = 0; z < cChunkDef::Width; z++) - { - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z; - const float zz = (float)BlockZ; - for (int x = 0; x < cChunkDef::Width; x++) - { - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width + x; - if (((m_Noise.IntNoise2DInt(BlockX, BlockZ) / 8) % 128) < 124) - { - continue; - } - int Top = a_ChunkDesc.GetHeight(x, z); - if (Top > 250) - { - // Nothing grows above Y=250 - continue; - } - if (a_ChunkDesc.GetBlockType(x, Top + 1, z) != E_BLOCK_AIR) - { - // Space already taken by something else, don't grow here - // WEIRD, since we're using heightmap, so there should NOT be anything above it - continue; - } - - const float xx = (float)BlockX; - float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f ); - float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f ); - switch (a_ChunkDesc.GetBlockType(x, Top, z)) - { - case E_BLOCK_GRASS: - { - float val3 = m_Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10 ); - float val4 = m_Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20 ); - if (val1 + val2 > 0.2f) - { - a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_YELLOW_FLOWER); - } - else if (val2 + val3 > 0.2f) - { - a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_ROSE); - } - else if (val3 + val4 > 0.2f) - { - a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_MUSHROOM); - } - else if (val1 + val4 > 0.2f) - { - a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_BROWN_MUSHROOM); - } - else if (val1 + val2 + val3 + val4 < -0.1) - { - a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_TALL_GRASS, E_META_TALL_GRASS_GRASS); - } - else if (TryAddSugarcane(a_ChunkDesc, x, Top, z)) - { - ++Top; - } - else if ((val1 > 0.5) && (val2 < -0.5)) - { - a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_PUMPKIN, (int)(val3 * 8) % 4); - } - break; - } // case E_BLOCK_GRASS - - case E_BLOCK_SAND: - { - int y = Top + 1; - if ( - (x > 0) && (x < cChunkDef::Width - 1) && - (z > 0) && (z < cChunkDef::Width - 1) && - (val1 + val2 > 0.5f) && - (a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) && - (a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) && - (a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) && - (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR) - ) - { - a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS); - } - else if (TryAddSugarcane(a_ChunkDesc, x, Top, z)) - { - ++Top; - } - break; - } - } // switch (TopBlock) - a_ChunkDesc.SetHeight(x, z, Top); - } // for y - } // for z -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFinishGenSnow: - -void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc) -{ - // Add a snow block in snowy biomes onto blocks that can be snowed over - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - switch (a_ChunkDesc.GetBiome(x, z)) - { - case biIcePlains: - case biIceMountains: - case biTaiga: - case biTaigaHills: - case biFrozenRiver: - case biFrozenOcean: - { - int Height = a_ChunkDesc.GetHeight(x, z); - if (g_BlockIsSnowable[a_ChunkDesc.GetBlockType(x, Height, z)]) - { - a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW); - a_ChunkDesc.SetHeight(x, z, Height + 1); - } - break; - } - } - } - } // for z -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFinishGenIce: - -void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc) -{ - // Turn surface water into ice in icy biomes - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - switch (a_ChunkDesc.GetBiome(x, z)) - { - case biIcePlains: - case biIceMountains: - case biTaiga: - case biTaigaHills: - case biFrozenRiver: - case biFrozenOcean: - { - int Height = a_ChunkDesc.GetHeight(x, z); - switch (a_ChunkDesc.GetBlockType(x, Height, z)) - { - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - { - a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE); - break; - } - } - break; - } - } - } - } // for z -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFinishGenLilypads: - -int cFinishGenSingleBiomeSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap) -{ - int res = 0; - for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) - { - if (a_BiomeMap[i] == m_Biome) - { - res++; - } - } // for i - a_BiomeMap[] - return m_Amount * res / 256; -} - - - - - -void cFinishGenSingleBiomeSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc) -{ - // Add Lilypads on top of water surface in Swampland - - int NumToGen = GetNumToGen(a_ChunkDesc.GetBiomeMap()); - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - for (int i = 0; i < NumToGen; i++) - { - int x = (m_Noise.IntNoise3DInt(ChunkX + ChunkZ, ChunkZ, i) / 13) % cChunkDef::Width; - int z = (m_Noise.IntNoise3DInt(ChunkX - ChunkZ, i, ChunkZ) / 11) % cChunkDef::Width; - - // Place the block at {x, z} if possible: - if (a_ChunkDesc.GetBiome(x, z) != m_Biome) - { - // Incorrect biome - continue; - } - int Height = a_ChunkDesc.GetHeight(x, z); - if (Height >= cChunkDef::Height) - { - // Too high up - continue; - } - if (a_ChunkDesc.GetBlockType(x, Height + 1, z) != E_BLOCK_AIR) - { - // Not an empty block - continue; - } - BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, Height, z); - if ((BlockBelow == m_AllowedBelow1) || (BlockBelow == m_AllowedBelow2)) - { - a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType); - a_ChunkDesc.SetHeight(x, z, Height + 1); - } - } // for i -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFinishGenBottomLava: - -void cFinishGenBottomLava::GenFinish(cChunkDesc & a_ChunkDesc) -{ - cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes(); - for (int y = m_Level; y > 0; y--) - { - for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) - { - int Index = cChunkDef::MakeIndexNoCheck(x, y, z); - if (BlockTypes[Index] == E_BLOCK_AIR) - { - BlockTypes[Index] = E_BLOCK_STATIONARY_LAVA; - } - } // for x, for z - } // for y -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFinishGenPreSimulator: - -cFinishGenPreSimulator::cFinishGenPreSimulator(void) -{ - // Nothing needed yet -} - - - - - -void cFinishGenPreSimulator::GenFinish(cChunkDesc & a_ChunkDesc) -{ - CollapseSandGravel(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); - StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); - StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); - // TODO: other operations -} - - - - - -void cFinishGenPreSimulator::CollapseSandGravel( - cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change - cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data -) -{ - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int LastY = -1; - int HeightY = 0; - for (int y = 0; y < cChunkDef::Height; y++) - { - BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z); - switch (Block) - { - default: - { - // Set the last block onto which stuff can fall to this height: - LastY = y; - HeightY = y; - break; - } - case E_BLOCK_AIR: - { - // Do nothing - break; - } - case E_BLOCK_FIRE: - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - // Do nothing, only remember this height as potentially highest - HeightY = y; - break; - } - case E_BLOCK_SAND: - case E_BLOCK_GRAVEL: - { - if (LastY < y - 1) - { - cChunkDef::SetBlock(a_BlockTypes, x, LastY + 1, z, Block); - cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); - } - LastY++; - if (LastY > HeightY) - { - HeightY = LastY; - } - break; - } - } // switch (GetBlock) - } // for y - cChunkDef::SetHeight(a_HeightMap, x, z, HeightY); - } // for x - } // for z -} - - - - - -void cFinishGenPreSimulator::StationarizeFluid( - cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change - cChunkDef::HeightMap & a_HeightMap, // Height map to read - BLOCKTYPE a_Fluid, - BLOCKTYPE a_StationaryFluid -) -{ - // Turn fluid in the middle to stationary, unless it has air or washable block next to it: - for (int z = 1; z < cChunkDef::Width - 1; z++) - { - for (int x = 1; x < cChunkDef::Width - 1; x++) - { - for (int y = cChunkDef::GetHeight(a_HeightMap, x, z); y >= 0; y--) - { - BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z); - if ((Block != a_Fluid) && (Block != a_StationaryFluid)) - { - continue; - } - static const struct - { - int x, y, z; - } Coords[] = - { - {1, 0, 0}, - {-1, 0, 0}, - {0, 0, 1}, - {0, 0, -1}, - {0, -1, 0} - } ; - BLOCKTYPE BlockToSet = a_StationaryFluid; // By default, don't simulate this block - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - if ((y == 0) && (Coords[i].y < 0)) - { - continue; - } - BLOCKTYPE Neighbor = cChunkDef::GetBlock(a_BlockTypes, x + Coords[i].x, y + Coords[i].y, z + Coords[i].z); - if ((Neighbor == E_BLOCK_AIR) || cFluidSimulator::CanWashAway(Neighbor)) - { - // There is an air / washable neighbor, simulate this block - BlockToSet = a_Fluid; - break; - } - } // for i - Coords[] - cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockToSet); - } // for y - } // for x - } // for z - - // Turn fluid at the chunk edges into non-stationary fluid: - for (int y = 0; y < cChunkDef::Height; y++) - { - for (int i = 0; i < cChunkDef::Width; i++) // i stands for both x and z here - { - if (cChunkDef::GetBlock(a_BlockTypes, 0, y, i) == a_StationaryFluid) - { - cChunkDef::SetBlock(a_BlockTypes, 0, y, i, a_Fluid); - } - if (cChunkDef::GetBlock(a_BlockTypes, i, y, 0) == a_StationaryFluid) - { - cChunkDef::SetBlock(a_BlockTypes, i, y, 0, a_Fluid); - } - if (cChunkDef::GetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i) == a_StationaryFluid) - { - cChunkDef::SetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i, a_Fluid); - } - if (cChunkDef::GetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1) == a_StationaryFluid) - { - cChunkDef::SetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1, a_Fluid); - } - } - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFinishGenFluidSprings: - -cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World) : - m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other - m_HeightDistribution(255), - m_Fluid(a_Fluid) -{ - bool IsWater = (a_Fluid == E_BLOCK_WATER); - AString SectionName = IsWater ? "WaterSprings" : "LavaSprings"; - AString DefaultHeightDistribution; - int DefaultChance; - switch (a_World.GetDimension()) - { - case dimNether: - { - DefaultHeightDistribution = IsWater ? DEF_NETHER_WATER_SPRINGS : DEF_NETHER_LAVA_SPRINGS; - DefaultChance = IsWater ? 0 : 15; - break; - } - case dimOverworld: - { - DefaultHeightDistribution = IsWater ? DEF_OVERWORLD_WATER_SPRINGS : DEF_OVERWORLD_LAVA_SPRINGS; - DefaultChance = IsWater ? 24 : 9; - break; - } - case dimEnd: - { - DefaultHeightDistribution = IsWater ? DEF_END_WATER_SPRINGS : DEF_END_LAVA_SPRINGS; - DefaultChance = 0; - break; - } - default: - { - ASSERT(!"Unhandled world dimension"); - break; - } - } // switch (dimension) - AString HeightDistribution = a_IniFile.GetValueSet(SectionName, "HeightDistribution", DefaultHeightDistribution); - if (!m_HeightDistribution.SetDefString(HeightDistribution) || (m_HeightDistribution.GetSum() <= 0)) - { - LOGWARNING("[%sSprings]: HeightDistribution is invalid, using the default of \"%s\".", - (a_Fluid == E_BLOCK_WATER) ? "Water" : "Lava", - DefaultHeightDistribution.c_str() - ); - m_HeightDistribution.SetDefString(DefaultHeightDistribution); - } - m_Chance = a_IniFile.GetValueSetI(SectionName, "Chance", DefaultChance); -} - - - - - -void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc) -{ - int ChanceRnd = (m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 512, 256 * a_ChunkDesc.GetChunkZ()) / 13) % 100; - if (ChanceRnd > m_Chance) - { - // Not in this chunk - return; - } - - // Get the height at which to try: - int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11; - Height %= m_HeightDistribution.GetSum(); - Height = m_HeightDistribution.MapValue(Height); - - // Try adding the spring at the height, if unsuccessful, move lower: - for (int y = Height; y > 1; y--) - { - // TODO: randomize the order in which the coords are being checked - for (int z = 1; z < cChunkDef::Width - 1; z++) - { - for (int x = 1; x < cChunkDef::Width - 1; x++) - { - switch (a_ChunkDesc.GetBlockType(x, y, z)) - { - case E_BLOCK_NETHERRACK: - case E_BLOCK_STONE: - { - if (TryPlaceSpring(a_ChunkDesc, x, y, z)) - { - // Succeeded, bail out - return; - } - } - } // switch (BlockType) - } // for x - } // for y - } // for y -} - - - - - -bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z) -{ - // In order to place a spring, it needs exactly one of the XZ neighbors or a below neighbor to be air - // Also, its neighbor on top of it must be non-air - if (a_ChunkDesc.GetBlockType(x, y + 1, z) == E_BLOCK_AIR) - { - return false; - } - - static const struct - { - int x, y, z; - } Coords[] = - { - {-1, 0, 0}, - { 1, 0, 0}, - { 0, -1, 0}, - { 0, 0, -1}, - { 0, 0, 1}, - } ; - int NumAirNeighbors = 0; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - switch (a_ChunkDesc.GetBlockType(x + Coords[i].x, y + Coords[i].y, z + Coords[i].z)) - { - case E_BLOCK_AIR: - { - NumAirNeighbors += 1; - if (NumAirNeighbors > 1) - { - return false; - } - } - } - } - if (NumAirNeighbors == 0) - { - return false; - } - - // Has exactly one air neighbor, place a spring: - a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0); - return true; -} - - - - diff --git a/source/Generating/FinishGen.h b/source/Generating/FinishGen.h deleted file mode 100644 index ed7df5909..000000000 --- a/source/Generating/FinishGen.h +++ /dev/null @@ -1,185 +0,0 @@ - -// FinishGen.h - -/* Interfaces to the various finishing generators: - - cFinishGenSnow - - cFinishGenIce - - cFinishGenSprinkleFoliage - - cFinishGenLilypads - - cFinishGenBottomLava - - cFinishGenPreSimulator - - cFinishGenDeadBushes -*/ - - - - - -#include "ComposableGenerator.h" -#include "../Noise.h" -#include "../ProbabDistrib.h" - - - - - -class cFinishGenSnow : - public cFinishGen -{ -protected: - // cFinishGen override: - virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cFinishGenIce : - public cFinishGen -{ -protected: - // cFinishGen override: - virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cFinishGenSprinkleFoliage : - public cFinishGen -{ -public: - cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {} - -protected: - cNoise m_Noise; - int m_Seed; - - /// Tries to place sugarcane at the coords specified, returns true if successful - bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ); - - // cFinishGen override: - virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -/** This class adds a single top block in random positions in the specified biome on top of specified allowed blocks. -Used for: -- Lilypads finisher -- DeadBushes finisher -*/ -class cFinishGenSingleBiomeSingleTopBlock : - public cFinishGen -{ -public: - cFinishGenSingleBiomeSingleTopBlock( - int a_Seed, BLOCKTYPE a_BlockType, EMCSBiome a_Biome, int a_Amount, - BLOCKTYPE a_AllowedBelow1, BLOCKTYPE a_AllowedBelow2 - ) : - m_Noise(a_Seed), - m_BlockType(a_BlockType), - m_Biome(a_Biome), - m_Amount(a_Amount), - m_AllowedBelow1(a_AllowedBelow1), - m_AllowedBelow2(a_AllowedBelow2) - { - } - -protected: - cNoise m_Noise; - BLOCKTYPE m_BlockType; - EMCSBiome m_Biome; - int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns. - BLOCKTYPE m_AllowedBelow1; ///< First of the two blocktypes that are allowed below m_BlockType - BLOCKTYPE m_AllowedBelow2; ///< Second of the two blocktypes that are allowed below m_BlockType - - int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap); - - // cFinishGen override: - virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cFinishGenBottomLava : - public cFinishGen -{ -public: - cFinishGenBottomLava(int a_Level) : - m_Level(a_Level) - { - } - -protected: - int m_Level; - - // cFinishGen override: - virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cFinishGenPreSimulator : - public cFinishGen -{ -public: - cFinishGenPreSimulator(void); - -protected: - // Drops hanging sand and gravel down to the ground, recalculates heightmap - void CollapseSandGravel( - cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change - cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data - ); - - /** For each fluid block: - - if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top) - - all fluid on the chunk's edge is made flowing - */ - void StationarizeFluid( - cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change - cChunkDef::HeightMap & a_HeightMap, // Height map to read - BLOCKTYPE a_Fluid, - BLOCKTYPE a_StationaryFluid - ); - - // cFinishGen override: - virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cFinishGenFluidSprings : - public cFinishGen -{ -public: - cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World); - -protected: - - cNoise m_Noise; - cProbabDistrib m_HeightDistribution; - BLOCKTYPE m_Fluid; - int m_Chance; ///< Chance, [0..100], that a spring will be generated in a chunk - - // cFinishGen override: - virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; - - /// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful - bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z); -} ; - - - - diff --git a/source/Generating/HeiGen.cpp b/source/Generating/HeiGen.cpp deleted file mode 100644 index 5dee181b7..000000000 --- a/source/Generating/HeiGen.cpp +++ /dev/null @@ -1,390 +0,0 @@ - -// HeiGen.cpp - -// Implements the various terrain height generators - -#include "Globals.h" -#include "HeiGen.h" -#include "../LinearUpscale.h" -#include "../../iniFile/iniFile.h" - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cHeiGenFlat: - -void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++) - { - a_HeightMap[i] = m_Height; - } -} - - - - - -void cHeiGenFlat::InitializeHeightGen(cIniFile & a_IniFile) -{ - m_Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", m_Height); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cHeiGenCache: - -cHeiGenCache::cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize) : - m_HeiGenToCache(a_HeiGenToCache), - m_CacheSize(a_CacheSize), - m_CacheOrder(new int[a_CacheSize]), - m_CacheData(new sCacheData[a_CacheSize]), - m_NumHits(0), - m_NumMisses(0), - m_TotalChain(0) -{ - for (int i = 0; i < m_CacheSize; i++) - { - m_CacheOrder[i] = i; - m_CacheData[i].m_ChunkX = 0x7fffffff; - m_CacheData[i].m_ChunkZ = 0x7fffffff; - } -} - - - - - -cHeiGenCache::~cHeiGenCache() -{ - delete[] m_CacheData; - delete[] m_CacheOrder; -} - - - - - -void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - /* - if (((m_NumHits + m_NumMisses) % 1024) == 10) - { - LOGD("HeiGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); - LOGD("HeiGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); - } - //*/ - - for (int i = 0; i < m_CacheSize; i++) - { - if ( - (m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) || - (m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ) - ) - { - continue; - } - // Found it in the cache - int Idx = m_CacheOrder[i]; - - // Move to front: - for (int j = i; j > 0; j--) - { - m_CacheOrder[j] = m_CacheOrder[j - 1]; - } - m_CacheOrder[0] = Idx; - - // Use the cached data: - memcpy(a_HeightMap, m_CacheData[Idx].m_HeightMap, sizeof(a_HeightMap)); - - m_NumHits++; - m_TotalChain += i; - return; - } // for i - cache - - // Not in the cache: - m_NumMisses++; - m_HeiGenToCache.GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap); - - // Insert it as the first item in the MRU order: - int Idx = m_CacheOrder[m_CacheSize - 1]; - for (int i = m_CacheSize - 1; i > 0; i--) - { - m_CacheOrder[i] = m_CacheOrder[i - 1]; - } // for i - m_CacheOrder[] - m_CacheOrder[0] = Idx; - memcpy(m_CacheData[Idx].m_HeightMap, a_HeightMap, sizeof(a_HeightMap)); - m_CacheData[Idx].m_ChunkX = a_ChunkX; - m_CacheData[Idx].m_ChunkZ = a_ChunkZ; -} - - - - - -void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile) -{ - m_HeiGenToCache.InitializeHeightGen(a_IniFile); -} - - - - - -bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height) -{ - for (int i = 0; i < m_CacheSize; i++) - { - if ((m_CacheData[i].m_ChunkX == a_ChunkX) && (m_CacheData[i].m_ChunkZ == a_ChunkZ)) - { - a_Height = cChunkDef::GetHeight(m_CacheData[i].m_HeightMap, a_RelX, a_RelZ); - return true; - } - } // for i - m_CacheData[] - return false; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cHeiGenClassic: - -cHeiGenClassic::cHeiGenClassic(int a_Seed) : - m_Seed(a_Seed), - m_Noise(a_Seed) -{ -} - - - - - -float cHeiGenClassic::GetNoise(float x, float y) -{ - float oct1 = m_Noise.CubicNoise2D(x * m_HeightFreq1, y * m_HeightFreq1) * m_HeightAmp1; - float oct2 = m_Noise.CubicNoise2D(x * m_HeightFreq2, y * m_HeightFreq2) * m_HeightAmp2; - float oct3 = m_Noise.CubicNoise2D(x * m_HeightFreq3, y * m_HeightFreq3) * m_HeightAmp3; - - float height = m_Noise.CubicNoise2D(x * 0.1f, y * 0.1f ) * 2; - - float flatness = ((m_Noise.CubicNoise2D(x * 0.5f, y * 0.5f) + 1.f) * 0.5f) * 1.1f; // 0 ... 1.5 - flatness *= flatness * flatness; - - return (oct1 + oct2 + oct3) * flatness + height; -} - - - - - -void cHeiGenClassic::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - for (int z = 0; z < cChunkDef::Width; z++) - { - const float zz = (float)(a_ChunkZ * cChunkDef::Width + z); - for (int x = 0; x < cChunkDef::Width; x++) - { - const float xx = (float)(a_ChunkX * cChunkDef::Width + x); - - int hei = 64 + (int)(GetNoise(xx * 0.05f, zz * 0.05f) * 16); - if (hei < 10) - { - hei = 10; - } - if (hei > 250) - { - hei = 250; - } - cChunkDef::SetHeight(a_HeightMap, x , z, hei); - } // for x - } // for z -} - - - - - -void cHeiGenClassic::InitializeHeightGen(cIniFile & a_IniFile) -{ - m_HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1); - m_HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0); - m_HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0); - m_HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0); - m_HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5); - m_HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cHeiGenBiomal: - -const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[biNumBiomes] = -{ - /* Fast-changing | Middle-changing | Slow-changing |*/ - /* Biome | Freq1 | Amp1 | Freq2 | Amp2 | Freq3 | Amp3 | BaseHeight */ - /* biOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, - /* biPlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, - /* biDesert */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, - /* biExtremeHills */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 100}, - /* biForest */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, - /* biTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, - /* biSwampland */ { 0.1f, 1.1f, 0.05f, 1.5f, 0.02f, 2.5f, 61.5}, - /* biRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56}, - /* biNether */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing - /* biSky */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing - /* biFrozenOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, - /* biFrozenRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56}, - /* biIcePlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, - /* biIceMountains */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80}, - /* biMushroomIsland */ { 0.1f, 2.0f, 0.05f, 8.0f, 0.01f, 6.0f, 80}, - /* biMushroomShore */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 64}, - /* biBeach */ { 0.1f, 0.5f, 0.05f, 1.0f, 0.01f, 1.0f, 64}, - /* biDesertHills */ { 0.2f, 2.0f, 0.05f, 5.0f, 0.01f, 4.0f, 75}, - /* biForestHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80}, - /* biTaigaHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80}, - /* biExtremeHillsEdge */ { 0.2f, 3.0f, 0.05f, 16.0f, 0.01f, 12.0f, 80}, - /* biJungle */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70}, - /* biJungleHills */ { 0.2f, 3.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80}, -} ; - - - - - -void cHeiGenBiomal::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - // Generate a 3x3 chunk area of biomes around this chunk: - BiomeNeighbors Biomes; - for (int z = -1; z <= 1; z++) - { - for (int x = -1; x <= 1; x++) - { - m_BiomeGen.GenBiomes(a_ChunkX + x, a_ChunkZ + z, Biomes[x + 1][z + 1]); - } // for x - } // for z - - /* - _X 2013_04_22: - There's no point in precalculating the entire perlin noise arrays, too many values are calculated uselessly, - resulting in speed DEcrease. - */ - - //* - // Linearly interpolate 4x4 blocks of heightmap: - // Must be done on a floating point datatype, else the results are ugly! - const int STEPZ = 4; // Must be a divisor of 16 - const int STEPX = 4; // Must be a divisor of 16 - NOISE_DATATYPE Height[17 * 17]; - for (int z = 0; z < 17; z += STEPZ) - { - for (int x = 0; x < 17; x += STEPX) - { - Height[x + 17 * z] = GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes); - } - } - LinearUpscale2DArrayInPlace(Height, 17, 17, STEPX, STEPZ); - - // Copy into the heightmap - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - cChunkDef::SetHeight(a_HeightMap, x, z, (int)Height[x + 17 * z]); - } - } - //*/ - - /* - // For each height, go through neighboring biomes and add up their idea of height: - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - cChunkDef::SetHeight(a_HeightMap, x, z, GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes)); - } // for x - } - //*/ -} - - - - - -void cHeiGenBiomal::InitializeHeightGen(cIniFile & a_IniFile) -{ - // No user-settable params -} - - - - - -NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const cHeiGenBiomal::BiomeNeighbors & a_BiomeNeighbors) -{ - // Sum up how many biomes of each type there are in the neighborhood: - int BiomeCounts[biNumBiomes]; - memset(BiomeCounts, 0, sizeof(BiomeCounts)); - int Sum = 0; - for (int z = -8; z <= 8; z++) - { - int FinalZ = a_RelZ + z + cChunkDef::Width; - int IdxZ = FinalZ / cChunkDef::Width; - int ModZ = FinalZ % cChunkDef::Width; - int WeightZ = 9 - abs(z); - for (int x = -8; x <= 8; x++) - { - int FinalX = a_RelX + x + cChunkDef::Width; - int IdxX = FinalX / cChunkDef::Width; - int ModX = FinalX % cChunkDef::Width; - EMCSBiome Biome = cChunkDef::GetBiome(a_BiomeNeighbors[IdxX][IdxZ], ModX, ModZ); - if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts))) - { - continue; - } - int WeightX = 9 - abs(x); - BiomeCounts[Biome] += WeightX + WeightZ; - Sum += WeightX + WeightZ; - } // for x - } // for z - - // For each biome type that has a nonzero count, calc its height and add it: - if (Sum > 0) - { - NOISE_DATATYPE Height = 0; - int BlockX = a_ChunkX * cChunkDef::Width + a_RelX; - int BlockZ = a_ChunkZ * cChunkDef::Width + a_RelZ; - for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++) - { - if (BiomeCounts[i] == 0) - { - continue; - } - NOISE_DATATYPE oct1 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq1, BlockZ * m_GenParam[i].m_HeightFreq1) * m_GenParam[i].m_HeightAmp1; - NOISE_DATATYPE oct2 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq2, BlockZ * m_GenParam[i].m_HeightFreq2) * m_GenParam[i].m_HeightAmp2; - NOISE_DATATYPE oct3 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq3, BlockZ * m_GenParam[i].m_HeightFreq3) * m_GenParam[i].m_HeightAmp3; - Height += BiomeCounts[i] * (m_GenParam[i].m_BaseHeight + oct1 + oct2 + oct3); - } - NOISE_DATATYPE res = Height / Sum; - return std::min((NOISE_DATATYPE)250, std::max(res, (NOISE_DATATYPE)5)); - } - - // No known biome around? Weird. Return a bogus value: - ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around"); - return 5; -} - - - - - diff --git a/source/Generating/HeiGen.h b/source/Generating/HeiGen.h deleted file mode 100644 index 1b246c70a..000000000 --- a/source/Generating/HeiGen.h +++ /dev/null @@ -1,145 +0,0 @@ - -// HeiGen.h - -/* -Interfaces to the various height generators: - - cHeiGenFlat - - cHeiGenClassic - - cHeiGenBiomal -*/ - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cHeiGenFlat : - public cTerrainHeightGen -{ -public: - cHeiGenFlat(void) : m_Height(5) {} - -protected: - - int m_Height; - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - virtual void InitializeHeightGen(cIniFile & a_IniFile) override; -} ; - - - - - -/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation -class cHeiGenCache : - public cTerrainHeightGen -{ -public: - cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize); // Doesn't take ownership of a_HeiGenToCache - ~cHeiGenCache(); - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - virtual void InitializeHeightGen(cIniFile & a_IniFile) override; - - /// Retrieves height at the specified point in the cache, returns true if found, false if not found - bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height); - -protected: - - cTerrainHeightGen & m_HeiGenToCache; - - struct sCacheData - { - int m_ChunkX; - int m_ChunkZ; - cChunkDef::HeightMap m_HeightMap; - } ; - - // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data - int m_CacheSize; - int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array - sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used - - // Cache statistics - int m_NumHits; - int m_NumMisses; - int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) -} ; - - - - - -class cHeiGenClassic : - public cTerrainHeightGen -{ -public: - cHeiGenClassic(int a_Seed); - -protected: - - int m_Seed; - cNoise m_Noise; - float m_HeightFreq1, m_HeightAmp1; - float m_HeightFreq2, m_HeightAmp2; - float m_HeightFreq3, m_HeightAmp3; - - float GetNoise(float x, float y); - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - virtual void InitializeHeightGen(cIniFile & a_IniFile) override; -} ; - - - - - -class cHeiGenBiomal : - public cTerrainHeightGen -{ -public: - cHeiGenBiomal(int a_Seed, cBiomeGen & a_BiomeGen) : - m_Noise(a_Seed), - m_BiomeGen(a_BiomeGen) - { - } - -protected: - - typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; - - cNoise m_Noise; - cBiomeGen & m_BiomeGen; - - // Per-biome terrain generator parameters: - struct sGenParam - { - float m_HeightFreq1, m_HeightAmp1; - float m_HeightFreq2, m_HeightAmp2; - float m_HeightFreq3, m_HeightAmp3; - float m_BaseHeight; - } ; - static const sGenParam m_GenParam[biNumBiomes]; - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - virtual void InitializeHeightGen(cIniFile & a_IniFile) override; - - NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors); -} ; - - - - diff --git a/source/Generating/MineShafts.cpp b/source/Generating/MineShafts.cpp deleted file mode 100644 index 159e6b4ea..000000000 --- a/source/Generating/MineShafts.cpp +++ /dev/null @@ -1,1423 +0,0 @@ - -// MineShafts.cpp - -// Implements the cStructGenMineShafts class representing the structure generator for abandoned mineshafts - -/* -Algorithm: -The cStructGenMineShafts::cMineShaftSystem class is the main controller, which knows what mineshaft -classes there are and their random weights. It gets asked to produce a new class everytime a connection is to be made. -The cMineShaft class is a base class for each mineshaft structure. -Each cMineShaft descendant knows how large it is, how to imprint itself into the chunk data and where to connect to -other descendants. Its PivotPoint is always a walkable column. Its Direction determines in which direction the structure -is facing. - -The generation starts with the central dirt room, from there corridors, crossings and staircases are added -in a depth-first processing. Each of the descendants will branch randomly, if not beyond the allowed recursion level -*/ - -#include "Globals.h" -#include "MineShafts.h" -#include "../Cuboid.h" -#include "../BlockEntities/ChestEntity.h" - - - - - -static const int NEIGHBORHOOD_SIZE = 3; - - - - - -class cMineShaft abstract -{ -public: - enum eKind - { - mskDirtRoom, - mskCorridor, - mskCrossing, - mskStaircase, - } ; - - - enum eDirection - { - dirXP, - dirZP, - dirXM, - dirZM, - } ; - - - cStructGenMineShafts::cMineShaftSystem & m_ParentSystem; - eKind m_Kind; - cCuboid m_BoundingBox; - - - cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind) : - m_ParentSystem(a_ParentSystem), - m_Kind(a_Kind) - { - } - - cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind, const cCuboid & a_BoundingBox) : - m_ParentSystem(a_ParentSystem), - m_Kind(a_Kind), - m_BoundingBox(a_BoundingBox) - { - } - - /// Returns true if this mineshaft intersects the specified cuboid - bool DoesIntersect(const cCuboid & a_Other) - { - return m_BoundingBox.DoesIntersect(a_Other); - } - - /** If recursion level is not too large, appends more branches to the parent system, - using exit points specific to this class. - */ - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) = 0; - - /// Imprints this shape into the specified chunk's data - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) = 0; -} ; - -typedef std::vector cMineShafts; - - - - - -class cMineShaftDirtRoom : - public cMineShaft -{ - typedef cMineShaft super; - -public: - cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise); - - // cMineShaft overrides: - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cMineShaftCorridor : - public cMineShaft -{ - typedef cMineShaft super; - -public: - /** Creates a new Corridor attached to the specified pivot point and direction. - Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. - May return NULL if cannot fit. - */ - static cMineShaft * CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise - ); - -protected: - static const int MAX_SEGMENTS = 5; - - int m_NumSegments; - eDirection m_Direction; - bool m_HasFullBeam[MAX_SEGMENTS]; ///< If true, segment at that index has a full beam support (planks in the top center block) - int m_ChestPosition; ///< If <0, no chest; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction - int m_SpawnerPosition; ///< If <0, no spawner; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction - bool m_HasTracks; ///< If true, random tracks will be placed on the floor - - cMineShaftCorridor( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, - cNoise & a_Noise - ); - - // cMineShaft overrides: - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; - - /// Places a chest, if the corridor has one - void PlaceChest(cChunkDesc & a_ChunkDesc); - - /// If this corridor has tracks, places them randomly - void PlaceTracks(cChunkDesc & a_ChunkDesc); - - /// If this corridor has a spawner, places the spawner - void PlaceSpawner(cChunkDesc & a_ChunkDesc); - - /// Randomly places torches around the central beam block - void PlaceTorches(cChunkDesc & a_ChunkDesc); -} ; - - - - - -class cMineShaftCrossing : - public cMineShaft -{ - typedef cMineShaft super; - -public: - /** Creates a new Crossing attached to the specified pivot point and direction. - Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. - May return NULL if cannot fit. - */ - static cMineShaft * CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise - ); - -protected: - cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox); - - // cMineShaft overrides: - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cMineShaftStaircase : - public cMineShaft -{ - typedef cMineShaft super; - -public: - enum eSlope - { - sUp, - sDown, - } ; - - /** Creates a new Staircase attached to the specified pivot point and direction. - Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. - May return NULL if cannot fit. - */ - static cMineShaft * CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise - ); - -protected: - eDirection m_Direction; - eSlope m_Slope; - - - cMineShaftStaircase( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - const cCuboid & a_BoundingBox, - eDirection a_Direction, - eSlope a_Slope - ); - - // cMineShaft overrides: - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cStructGenMineShafts::cMineShaftSystem -{ -public: - int m_BlockX, m_BlockZ; ///< The pivot point on which the system is generated - int m_GridSize; ///< Maximum offset of the dirtroom from grid center, * 2, in each direction - int m_MaxRecursion; ///< Maximum recursion level (initialized from cStructGenMineShafts::m_MaxRecursion) - int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor - int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor - int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing - int m_ChanceChest; ///< Chance [0 .. 250] that a corridor has a chest in it - int m_ChanceSpawner; ///< Chance [0 .. 250] that a corridor has a spawner in it - int m_ChanceTorch; ///< Chance [0 .. 10k] for a torch appearing attached to a corridor's beam - cMineShafts m_MineShafts; ///< List of cMineShaft descendants that comprise this system - cCuboid m_BoundingBox; ///< Bounding box into which all of the components need to fit - - /// Creates and generates the entire system - cMineShaftSystem( - int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, - int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase - ); - - ~cMineShaftSystem(); - - /// Carves the system into the chunk data - void ProcessChunk(cChunkDesc & a_Chunk); - - /** Creates new cMineShaft descendant connected at the specified point, heading the specified direction, - if it fits, appends it to the list and calls its AppendBranches() - */ - void AppendBranch( - int a_BlockX, int a_BlockY, int a_BlockZ, - cMineShaft::eDirection a_Direction, cNoise & a_Noise, - int a_RecursionLevel - ); - - /// Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid - bool CanAppend(const cCuboid & a_BoundingBox); -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenMineShafts::cMineShaftSystem: - -cStructGenMineShafts::cMineShaftSystem::cMineShaftSystem( - int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, - int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase -) : - m_BlockX(a_BlockX), - m_BlockZ(a_BlockZ), - m_GridSize(a_GridSize), - m_MaxRecursion(8), // TODO: settable - m_ProbLevelCorridor(a_ProbLevelCorridor), - m_ProbLevelCrossing(a_ProbLevelCrossing), - m_ProbLevelStaircase(a_ProbLevelStaircase + 1), - m_ChanceChest(12), // TODO: settable - m_ChanceSpawner(12), // TODO: settable - m_ChanceTorch(1000) // TODO: settable -{ - m_MineShafts.reserve(100); - - cMineShaft * Start = new cMineShaftDirtRoom(*this, a_Noise); - m_MineShafts.push_back(Start); - - m_BoundingBox.Assign( - Start->m_BoundingBox.p1.x - a_MaxSystemSize / 2, 2, Start->m_BoundingBox.p1.z - a_MaxSystemSize / 2, - Start->m_BoundingBox.p2.x + a_MaxSystemSize / 2, 50, Start->m_BoundingBox.p2.z + a_MaxSystemSize / 2 - ); - - Start->AppendBranches(0, a_Noise); - - for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) - { - ASSERT((*itr)->m_BoundingBox.IsSorted()); - } // for itr - m_MineShafts[] -} - - - - - -cStructGenMineShafts::cMineShaftSystem::~cMineShaftSystem() -{ - for (cMineShafts::iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) - { - delete *itr; - } // for itr - m_MineShafts[] - m_MineShafts.clear(); -} - - - - - -void cStructGenMineShafts::cMineShaftSystem::ProcessChunk(cChunkDesc & a_Chunk) -{ - for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) - { - (*itr)->ProcessChunk(a_Chunk); - } // for itr - m_MineShafts[] -} - - - - - -void cStructGenMineShafts::cMineShaftSystem::AppendBranch( - int a_PivotX, int a_PivotY, int a_PivotZ, - cMineShaft::eDirection a_Direction, cNoise & a_Noise, - int a_RecursionLevel -) -{ - if (a_RecursionLevel > m_MaxRecursion) - { - return; - } - - cMineShaft * Next = NULL; - int rnd = (a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_RecursionLevel * 16, a_PivotZ) / 13) % m_ProbLevelStaircase; - if (rnd < m_ProbLevelCorridor) - { - Next = cMineShaftCorridor::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); - } - else if (rnd < m_ProbLevelCrossing) - { - Next = cMineShaftCrossing::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); - } - else - { - Next = cMineShaftStaircase::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); - } - if (Next == NULL) - { - return; - } - m_MineShafts.push_back(Next); - Next->AppendBranches(a_RecursionLevel + 1, a_Noise); -} - - - - - -bool cStructGenMineShafts::cMineShaftSystem::CanAppend(const cCuboid & a_BoundingBox) -{ - if (!a_BoundingBox.IsCompletelyInside(m_BoundingBox)) - { - // Too far away, or too low / too high - return false; - } - - // Check intersections: - for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) - { - if ((*itr)->DoesIntersect(a_BoundingBox)) - { - return false; - } - } // for itr - m_MineShafts[] - return true; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMineShaftDirtRoom: - -cMineShaftDirtRoom::cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise) : - super(a_Parent, mskDirtRoom) -{ - // Make the room of random size, min 10 x 4 x 10; max 18 x 12 x 18: - int rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 0, a_Parent.m_BlockZ) / 7; - int OfsX = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; - rnd >>= 12; - int OfsZ = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; - rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 1000, a_Parent.m_BlockZ) / 11; - m_BoundingBox.p1.x = a_Parent.m_BlockX + OfsX; - m_BoundingBox.p2.x = m_BoundingBox.p1.x + 10 + (rnd % 8); - rnd >>= 4; - m_BoundingBox.p1.z = a_Parent.m_BlockZ + OfsZ; - m_BoundingBox.p2.z = m_BoundingBox.p1.z + 10 + (rnd % 8); - rnd >>= 4; - m_BoundingBox.p1.y = 20; - m_BoundingBox.p2.y = 24 + rnd % 8; -} - - - - - -void cMineShaftDirtRoom::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) -{ - int Height = m_BoundingBox.DifY() - 3; - for (int x = m_BoundingBox.p1.x + 1; x < m_BoundingBox.p2.x; x += 4) - { - int rnd = a_Noise.IntNoise3DInt(x, a_RecursionLevel, m_BoundingBox.p1.z) / 7; - m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); - rnd >>= 4; - m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); - } - - for (int z = m_BoundingBox.p1.z + 1; z < m_BoundingBox.p2.z; z += 4) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, a_RecursionLevel, z) / 13; - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXM, a_Noise, a_RecursionLevel); - rnd >>= 4; - m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXP, a_Noise, a_RecursionLevel); - } -} - - - - - -void cMineShaftDirtRoom::ProcessChunk(cChunkDesc & a_ChunkDesc) -{ - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - if ( - (m_BoundingBox.p1.x > BlockX + cChunkDef::Width) || - (m_BoundingBox.p1.z > BlockZ + cChunkDef::Width) || - (m_BoundingBox.p2.x < BlockX) || - (m_BoundingBox.p2.z < BlockZ) - ) - { - // Early bailout - cannot intersect this chunk - return; - } - - // Chunk-relative coords of the boundaries: - int MinX = std::max(BlockX, m_BoundingBox.p1.x) - BlockX; - int MaxX = std::min(BlockX + cChunkDef::Width, m_BoundingBox.p2.x + 1) - BlockX; - int MinZ = std::max(BlockZ, m_BoundingBox.p1.z) - BlockZ; - int MaxZ = std::min(BlockZ + cChunkDef::Width, m_BoundingBox.p2.z + 1) - BlockZ; - - // Carve the room out: - for (int z = MinZ; z < MaxZ; z++) - { - for (int x = MinX; x < MaxX; x++) - { - for (int y = m_BoundingBox.p1.y + 1; y < m_BoundingBox.p2.y; y++) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); - } - if (a_ChunkDesc.GetBlockType(x, m_BoundingBox.p1.y, z) != E_BLOCK_AIR) - { - a_ChunkDesc.SetBlockType(x, m_BoundingBox.p1.y, z, E_BLOCK_DIRT); - } - } // for x - } // for z -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMineShaftCorridor: - -cMineShaftCorridor::cMineShaftCorridor( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, - cNoise & a_Noise -) : - super(a_ParentSystem, mskCorridor, a_BoundingBox), - m_NumSegments(a_NumSegments), - m_Direction(a_Direction), - m_ChestPosition(-1), - m_SpawnerPosition(-1) -{ - int rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.x, a_BoundingBox.p1.y, a_BoundingBox.p1.z) / 7; - for (int i = 0; i < a_NumSegments; i++) - { - m_HasFullBeam[i] = (rnd % 4) < 3; // 75 % chance of full beam - rnd >>= 2; - } - m_HasTracks = ((rnd % 4) < 2); // 50 % chance of tracks - - rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.z, a_BoundingBox.p1.x, a_BoundingBox.p1.y) / 7; - int ChestCheck = rnd % 250; - rnd >>= 8; - int SpawnerCheck = rnd % 250; - rnd >>= 8; - if (ChestCheck < a_ParentSystem.m_ChanceChest) - { - m_ChestPosition = rnd % (a_NumSegments * 5); - } - if ((a_NumSegments < 4) && (SpawnerCheck < a_ParentSystem.m_ChanceSpawner)) - { - m_SpawnerPosition = rnd % (a_NumSegments * 5); - } -} - - - - - -cMineShaft * cMineShaftCorridor::CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise -) -{ - cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); - BoundingBox.p2.y += 3; - int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; - int NumSegments = 2 + (rnd) % (MAX_SEGMENTS - 1); // 2 .. MAX_SEGMENTS - switch (a_Direction) - { - case dirXP: BoundingBox.p2.x += NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; - case dirXM: BoundingBox.p1.x -= NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; - case dirZP: BoundingBox.p2.z += NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; - case dirZM: BoundingBox.p1.z -= NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; - } - if (!a_ParentSystem.CanAppend(BoundingBox)) - { - return NULL; - } - return new cMineShaftCorridor(a_ParentSystem, BoundingBox, NumSegments, a_Direction, a_Noise); -} - - - - - -void cMineShaftCorridor::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) -{ - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 7; - // Prefer the same height, but allow for up to one block height displacement: - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - switch (m_Direction) - { - case dirXM: - { - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); - for (int i = m_NumSegments; i >= 0; i--) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - rnd >>= 6; - int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); - } - break; - } - - case dirXP: - { - m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); - for (int i = m_NumSegments; i >= 0; i--) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - rnd >>= 6; - int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); - } - break; - } - - case dirZM: - { - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); - for (int i = m_NumSegments; i >= 0; i--) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - rnd >>= 6; - int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); - m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); - } - break; - } - - case dirZP: - { - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); - for (int i = m_NumSegments; i >= 0; i--) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - rnd >>= 6; - int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); - m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); - } - break; - } - } // switch (m_Direction) -} - - - - - -void cMineShaftCorridor::ProcessChunk(cChunkDesc & a_ChunkDesc) -{ - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - cCuboid RelBoundingBox(m_BoundingBox); - RelBoundingBox.Move(-BlockX, 0, -BlockZ); - RelBoundingBox.p1.y += 1; - RelBoundingBox.p2.y -= 1; - cCuboid Top(RelBoundingBox); - Top.p2.y += 1; - Top.p1.y = Top.p2.y; - a_ChunkDesc.FillRelCuboid(RelBoundingBox, E_BLOCK_AIR, 0); - a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_AIR, 0, BlockX ^ BlockZ + BlockX, 8000); - if (m_SpawnerPosition >= 0) - { - // Cobwebs around the spider spawner - a_ChunkDesc.RandomFillRelCuboid(RelBoundingBox, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockZ, 8000); - a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX, 5000); - } - a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX + 10, 500); - RelBoundingBox.p1.y = m_BoundingBox.p1.y; - RelBoundingBox.p2.y = m_BoundingBox.p1.y; - a_ChunkDesc.FloorRelCuboid(RelBoundingBox, E_BLOCK_PLANKS, 0); - switch (m_Direction) - { - case dirXM: - case dirXP: - { - int y1 = m_BoundingBox.p1.y + 1; - int y2 = m_BoundingBox.p1.y + 2; - int y3 = m_BoundingBox.p1.y + 3; - int z1 = m_BoundingBox.p1.z - BlockZ; - int z2 = m_BoundingBox.p2.z - BlockZ; - for (int i = 0; i < m_NumSegments; i++) - { - int x = m_BoundingBox.p1.x + i * 5 + 2 - BlockX; - if ((x < 0) || (x >= cChunkDef::Width)) - { - continue; - } - if ((z1 >= 0) && (z1 < cChunkDef::Width)) - { - a_ChunkDesc.SetBlockTypeMeta(x, y1, z1, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x, y2, z1, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x, y3, z1, E_BLOCK_PLANKS, 0); - } - if ((z2 >= 0) && (z2 < cChunkDef::Width)) - { - a_ChunkDesc.SetBlockTypeMeta(x, y1, z2, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x, y2, z2, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x, y3, z2, E_BLOCK_PLANKS, 0); - } - if ((z1 >= -1) && (z1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) - { - a_ChunkDesc.SetBlockTypeMeta(x, y3, z1 + 1, E_BLOCK_PLANKS, 0); - } - } // for i - NumSegments - break; - } - - case dirZM: - case dirZP: - { - int y1 = m_BoundingBox.p1.y + 1; - int y2 = m_BoundingBox.p1.y + 2; - int y3 = m_BoundingBox.p1.y + 3; - int x1 = m_BoundingBox.p1.x - BlockX; - int x2 = m_BoundingBox.p2.x - BlockX; - for (int i = 0; i < m_NumSegments; i++) - { - int z = m_BoundingBox.p1.z + i * 5 + 2 - BlockZ; - if ((z < 0) || (z >= cChunkDef::Width)) - { - continue; - } - if ((x1 >= 0) && (x1 < cChunkDef::Width)) - { - a_ChunkDesc.SetBlockTypeMeta(x1, y1, z, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x1, y2, z, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x1, y3, z, E_BLOCK_PLANKS, 0); - } - if ((x2 >= 0) && (x2 < cChunkDef::Width)) - { - a_ChunkDesc.SetBlockTypeMeta(x2, y1, z, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x2, y2, z, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x2, y3, z, E_BLOCK_PLANKS, 0); - } - if ((x1 >= -1) && (x1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) - { - a_ChunkDesc.SetBlockTypeMeta(x1 + 1, y3, z, E_BLOCK_PLANKS, 0); - } - } // for i - NumSegments - break; - } // case dirZ? - } // for i - - PlaceChest(a_ChunkDesc); - PlaceTracks(a_ChunkDesc); - PlaceSpawner(a_ChunkDesc); // (must be after Tracks!) - PlaceTorches(a_ChunkDesc); -} - - - - - -void cMineShaftCorridor::PlaceChest(cChunkDesc & a_ChunkDesc) -{ - static const cLootProbab LootProbab[] = - { - // Item, MinAmount, MaxAmount, Weight - { cItem(E_ITEM_IRON), 1, 5, 10 }, - { cItem(E_ITEM_GOLD), 1, 3, 5 }, - { cItem(E_ITEM_REDSTONE_DUST), 4, 9, 5 }, - { cItem(E_ITEM_DIAMOND), 1, 2, 3 }, - { cItem(E_ITEM_DYE, 1, 4), 4, 9, 5 }, // lapis lazuli dye - { cItem(E_ITEM_COAL), 3, 8, 10 }, - { cItem(E_ITEM_BREAD), 1, 3, 15 }, - { cItem(E_ITEM_IRON_PICKAXE), 1, 1, 1 }, - { cItem(E_BLOCK_MINECART_TRACKS), 4, 8, 1 }, - { cItem(E_ITEM_MELON_SEEDS), 2, 4, 10 }, - { cItem(E_ITEM_PUMPKIN_SEEDS), 2, 4, 10 }, - } ; - - if (m_ChestPosition < 0) - { - return; - } - - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - int x, z; - NIBBLETYPE Meta = 0; - switch (m_Direction) - { - case dirXM: - case dirXP: - { - x = m_BoundingBox.p1.x + m_ChestPosition - BlockX; - z = m_BoundingBox.p1.z - BlockZ; - Meta = E_META_CHEST_FACING_ZP; - break; - } - - case dirZM: - case dirZP: - { - x = m_BoundingBox.p1.x - BlockX; - z = m_BoundingBox.p1.z + m_ChestPosition - BlockZ; - Meta = E_META_CHEST_FACING_XP; - break; - } - } // switch (Dir) - - if ( - (x >= 0) && (x < cChunkDef::Width) && - (z >= 0) && (z < cChunkDef::Width) - ) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p1.y + 1, z, E_BLOCK_CHEST, Meta); - cChestEntity * ChestEntity = (cChestEntity *)a_ChunkDesc.GetBlockEntity(x, m_BoundingBox.p1.y + 1, z); - ASSERT((ChestEntity != NULL) && (ChestEntity->GetBlockType() == E_BLOCK_CHEST)); - cNoise Noise(a_ChunkDesc.GetChunkX() ^ a_ChunkDesc.GetChunkZ()); - int NumSlots = 3 + ((Noise.IntNoise3DInt(x, m_BoundingBox.p1.y, z) / 11) % 4); - int Seed = Noise.IntNoise2DInt(x, z); - ChestEntity->GetContents().GenerateRandomLootWithBooks(LootProbab, ARRAYCOUNT(LootProbab), NumSlots, Seed); - } -} - - - - - -void cMineShaftCorridor::PlaceTracks(cChunkDesc & a_ChunkDesc) -{ - if (!m_HasTracks) - { - return; - } - cCuboid Box(m_BoundingBox); - Box.Move(-a_ChunkDesc.GetChunkX() * cChunkDef::Width, 1, -a_ChunkDesc.GetChunkZ() * cChunkDef::Width); - Box.p2.y = Box.p1.y; - Box.p1.x += 1; - Box.p2.x -= 1; - Box.p1.z += 1; - Box.p2.z -= 1; - NIBBLETYPE Meta = 0; - switch (m_Direction) - { - case dirXM: - case dirXP: - { - Meta = E_META_TRACKS_X; - break; - } - - case dirZM: - case dirZP: - { - Meta = E_META_TRACKS_Z; - break; - } - } // switch (direction) - a_ChunkDesc.RandomFillRelCuboid(Box, E_BLOCK_MINECART_TRACKS, Meta, a_ChunkDesc.GetChunkX() + a_ChunkDesc.GetChunkZ(), 6000); -} - - - - - -void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc) -{ - if (m_SpawnerPosition < 0) - { - // No spawner in this corridor - return; - } - int SpawnerRelX = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int SpawnerRelZ = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - switch (m_Direction) - { - case dirXM: - case dirXP: - { - SpawnerRelX += m_SpawnerPosition - 1; - break; - } - case dirZM: - case dirZP: - { - SpawnerRelZ += m_SpawnerPosition - 1; - break; - } - } - if ( - (SpawnerRelX >= 0) && (SpawnerRelX < cChunkDef::Width) && - (SpawnerRelZ >= 0) && (SpawnerRelZ < cChunkDef::Width) - ) - { - a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0); - // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented - } -} - - - - - -void cMineShaftCorridor::PlaceTorches(cChunkDesc & a_ChunkDesc) -{ - cNoise Noise(m_BoundingBox.p1.x); - switch (m_Direction) - { - case dirXM: - case dirXP: - { - int z = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - if ((z < 0) || (z >= cChunkDef::Width)) - { - return; - } - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - for (int i = 0; i < m_NumSegments; i++) - { - if (!m_HasFullBeam[i]) - { - continue; - } - int x = m_BoundingBox.p1.x + i * 5 + 1 - BlockX; - if ((x >= 0) && (x < cChunkDef::Width)) - { - if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XP); - } - } - x += 2; - if ((x >= 0) && (x < cChunkDef::Width)) - { - if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XM); - } - } - } // for i - break; - } - - case dirZM: - case dirZP: - { - int x = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; - if ((x < 0) || (x >= cChunkDef::Width)) - { - return; - } - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - for (int i = 0; i < m_NumSegments; i++) - { - if (!m_HasFullBeam[i]) - { - continue; - } - int z = m_BoundingBox.p1.z + i * 5 + 1 - BlockZ; - if ((z >= 0) && (z < cChunkDef::Width)) - { - if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZP); - } - } - z += 2; - if ((z >= 0) && (z < cChunkDef::Width)) - { - if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZM); - } - } - } // for i - break; - } - } // switch (direction) -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMineShaftCrossing: - -cMineShaftCrossing::cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox) : - super(a_ParentSystem, mskCrossing, a_BoundingBox) -{ -} - - - - - -cMineShaft * cMineShaftCrossing::CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise -) -{ - cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); - int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; - BoundingBox.p2.y += 3; - if ((rnd % 4) < 2) - { - // 2-level crossing: - BoundingBox.p2.y += 4; - rnd >>= 2; - if ((rnd % 4) < 2) - { - // This is the higher level: - BoundingBox.p1.y -= 4; - BoundingBox.p2.y -= 4; - } - } - rnd >>= 2; - switch (a_Direction) - { - case dirXP: BoundingBox.p2.x += 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; - case dirXM: BoundingBox.p1.x -= 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; - case dirZP: BoundingBox.p2.z += 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; - case dirZM: BoundingBox.p1.z -= 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; - } - if (!a_ParentSystem.CanAppend(BoundingBox)) - { - return NULL; - } - return new cMineShaftCrossing(a_ParentSystem, BoundingBox); -} - - - - - -void cMineShaftCrossing::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) -{ - struct - { - int x, y, z; - eDirection dir; - } Exits[] = - { - // Bottom level: - {-1, 1, 2, dirXM}, - { 2, 1, -1, dirZM}, - { 5, 1, 2, dirXP}, - { 2, 1, 5, dirZP}, - // Top level: - {-1, 5, 2, dirXM}, - { 2, 5, -1, dirZM}, - { 5, 5, 2, dirXP}, - { 2, 5, 5, dirZP}, - } ; - for (int i = 0; i < ARRAYCOUNT(Exits); i++) - { - if (m_BoundingBox.p1.y + Exits[i].y >= m_BoundingBox.p2.y) - { - // This exit is not available (two-level exit on a one-level crossing) - continue; - } - - int Height = m_BoundingBox.p1.y + Exits[i].y; - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Exits[i].x, Height, m_BoundingBox.p1.z + Exits[i].z, Exits[i].dir, a_Noise, a_RecursionLevel); - } // for i -} - - - - - -void cMineShaftCrossing::ProcessChunk(cChunkDesc & a_ChunkDesc) -{ - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - cCuboid box(m_BoundingBox); - box.Move(-BlockX, 0, -BlockZ); - if ((box.p2.x < 0) || (box.p2.z < 0) || (box.p1.x >= cChunkDef::Width) || (box.p1.z > cChunkDef::Width)) - { - // Does not intersect this chunk - return; - } - int Floor = box.p1.y + 1; - int Ceil = box.p2.y; - - // The supports: - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); - - // The air in between: - a_ChunkDesc.FillRelCuboid(box.p1.x + 2, box.p1.x + 2, Floor, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 2, box.p1.z + 2, E_BLOCK_AIR, 0); - - // The air on the edges: - int Mid = Floor + 2; - a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); - Mid += 2; - if (Mid < Ceil) - { - a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); - } - - // The floor, if needed: - box.p2.y = box.p1.y; - a_ChunkDesc.FloorRelCuboid(box, E_BLOCK_PLANKS, 0); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMineShaftStaircase: - -cMineShaftStaircase::cMineShaftStaircase( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - const cCuboid & a_BoundingBox, - eDirection a_Direction, - eSlope a_Slope -) : - super(a_ParentSystem, mskStaircase, a_BoundingBox), - m_Direction(a_Direction), - m_Slope(a_Slope) -{ -} - - - - - -cMineShaft * cMineShaftStaircase::CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise -) -{ - int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; - cCuboid Box; - switch (a_Direction) - { - case dirXM: - { - Box.Assign(a_PivotX - 7, a_PivotY - 1, a_PivotZ - 1, a_PivotX, a_PivotY + 6, a_PivotZ + 1); - break; - } - case dirXP: - { - Box.Assign(a_PivotX, a_PivotY - 1, a_PivotZ - 1, a_PivotX + 7, a_PivotY + 6, a_PivotZ + 1); - break; - } - case dirZM: - { - Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ - 7, a_PivotX + 1, a_PivotY + 6, a_PivotZ); - break; - } - case dirZP: - { - Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ, a_PivotX + 1, a_PivotY + 6, a_PivotZ + 7); - break; - } - } - eSlope Slope = sUp; - if ((rnd % 4) < 2) // 50 % - { - Slope = sDown; - Box.Move(0, -4, 0); - } - if (!a_ParentSystem.CanAppend(Box)) - { - return NULL; - } - return new cMineShaftStaircase(a_ParentSystem, Box, a_Direction, Slope); -} - - - - - -void cMineShaftStaircase::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) -{ - int Height = m_BoundingBox.p1.y + ((m_Slope == sDown) ? 1 : 5); - switch (m_Direction) - { - case dirXM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); break; - case dirXP: m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); break; - case dirZM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); break; - case dirZP: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); break; - } -} - - - - - -void cMineShaftStaircase::ProcessChunk(cChunkDesc & a_ChunkDesc) -{ - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - cCuboid RelB(m_BoundingBox); - RelB.Move(-BlockX, 0, -BlockZ); - if ( - (RelB.p1.x >= cChunkDef::Width) || - (RelB.p1.z >= cChunkDef::Width) || - (RelB.p2.x < 0) || - (RelB.p2.z < 0) - ) - { - // No intersection between this staircase and this chunk - return; - } - - int SFloor = RelB.p1.y + ((m_Slope == sDown) ? 5 : 1); - int DFloor = RelB.p1.y + ((m_Slope == sDown) ? 1 : 5); - int Add = (m_Slope == sDown) ? -1 : 1; - int InitAdd = (m_Slope == sDown) ? -1 : 0; - cCuboid Box; - switch (m_Direction) - { - case dirXM: - { - a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); - Box.Assign(RelB.p2.x - 2, SFloor + InitAdd, RelB.p1.z, RelB.p2.x - 2, SFloor + 3 + InitAdd, RelB.p2.z); - for (int i = 0; i < 4; i++) - { - a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); - Box.Move(-1, Add, 0); - } - break; - } - - case dirXP: - { - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); - Box.Assign(RelB.p1.x + 2, SFloor + InitAdd, RelB.p1.z, RelB.p1.x + 2, SFloor + 3 + InitAdd, RelB.p2.z); - for (int i = 0; i < 4; i++) - { - a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); - Box.Move(1, Add, 0); - } - break; - } - - case dirZM: - { - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); - Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p2.z - 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p2.z - 2); - for (int i = 0; i < 4; i++) - { - a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); - Box.Move(0, Add, -1); - } - break; - } - - case dirZP: - { - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); - Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p1.z + 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p1.z + 2); - for (int i = 0; i < 4; i++) - { - a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); - Box.Move(0, Add, 1); - } - break; - } - - } // switch (m_Direction) -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenMineShafts: - -cStructGenMineShafts::cStructGenMineShafts( - int a_Seed, int a_GridSize, int a_MaxSystemSize, - int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase -) : - m_Noise(a_Seed), - m_GridSize(a_GridSize), - m_MaxSystemSize(a_MaxSystemSize), - m_ProbLevelCorridor(std::max(0, a_ChanceCorridor)), - m_ProbLevelCrossing(std::max(0, a_ChanceCorridor + a_ChanceCrossing)), - m_ProbLevelStaircase(std::max(0, a_ChanceCorridor + a_ChanceCrossing + a_ChanceStaircase)) -{ -} - - - - - -cStructGenMineShafts::~cStructGenMineShafts() -{ - ClearCache(); -} - - - - - -void cStructGenMineShafts::ClearCache(void) -{ - for (cMineShaftSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } // for itr - m_Cache[] - m_Cache.clear(); -} - - - - - -void cStructGenMineShafts::GetMineShaftSystemsForChunk( - int a_ChunkX, int a_ChunkZ, - cStructGenMineShafts::cMineShaftSystems & a_MineShafts -) -{ - int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize; - int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize; - if (BaseX < 0) - { - --BaseX; - } - if (BaseZ < 0) - { - --BaseZ; - } - BaseX -= NEIGHBORHOOD_SIZE / 2; - BaseZ -= NEIGHBORHOOD_SIZE / 2; - - // Walk the cache, move each cave system that we want into a_Caves: - int StartX = BaseX * m_GridSize; - int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize; - int StartZ = BaseZ * m_GridSize; - int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize; - for (cMineShaftSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) - { - if ( - ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && - ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) - ) - { - // want - a_MineShafts.push_back(*itr); - itr = m_Cache.erase(itr); - } - else - { - // don't want - ++itr; - } - } // for itr - m_Cache[] - - for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) - { - int RealX = (BaseX + x) * m_GridSize; - for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) - { - int RealZ = (BaseZ + z) * m_GridSize; - bool Found = false; - for (cMineShaftSystems::const_iterator itr = a_MineShafts.begin(), end = a_MineShafts.end(); itr != end; ++itr) - { - if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) - { - Found = true; - break; - } - } // for itr - a_Mineshafts - if (!Found) - { - a_MineShafts.push_back(new cMineShaftSystem(RealX, RealZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase)); - } - } // for z - } // for x - - // Copy a_MineShafts into m_Cache to the beginning: - cMineShaftSystems MineShaftsCopy(a_MineShafts); - m_Cache.splice(m_Cache.begin(), MineShaftsCopy, MineShaftsCopy.begin(), MineShaftsCopy.end()); - - // Trim the cache if it's too long: - if (m_Cache.size() > 100) - { - cMineShaftSystems::iterator itr = m_Cache.begin(); - std::advance(itr, 100); - for (cMineShaftSystems::iterator end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } - itr = m_Cache.begin(); - std::advance(itr, 100); - m_Cache.erase(itr, m_Cache.end()); - } -} - - - - - - -void cStructGenMineShafts::GenStructures(cChunkDesc & a_ChunkDesc) -{ - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - cMineShaftSystems MineShafts; - GetMineShaftSystemsForChunk(ChunkX, ChunkZ, MineShafts); - for (cMineShaftSystems::const_iterator itr = MineShafts.begin(); itr != MineShafts.end(); ++itr) - { - (*itr)->ProcessChunk(a_ChunkDesc); - } // for itr - MineShafts[] -} - - - - diff --git a/source/Generating/MineShafts.h b/source/Generating/MineShafts.h deleted file mode 100644 index c53d3bc53..000000000 --- a/source/Generating/MineShafts.h +++ /dev/null @@ -1,61 +0,0 @@ - -// MineShafts.h - -// Declares the cStructGenMineShafts class representing the structure generator for abandoned mineshafts - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cStructGenMineShafts : - public cStructureGen -{ -public: - cStructGenMineShafts( - int a_Seed, int a_GridSize, int a_MaxSystemSize, - int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase - ); - - virtual ~cStructGenMineShafts(); - -protected: - friend class cMineShaft; - friend class cMineShaftDirtRoom; - friend class cMineShaftCorridor; - friend class cMineShaftCrossing; - friend class cMineShaftStaircase; - class cMineShaftSystem; // fwd: MineShafts.cpp - typedef std::list cMineShaftSystems; - - cNoise m_Noise; - int m_GridSize; ///< Average spacing of the systems - int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system - int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor - int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor - int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing - cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used. - - /// Clears everything from the cache - void ClearCache(void); - - /** Returns all systems that *may* intersect the given chunk. - All the systems are valid until the next call to this function (which may delete some of the pointers). - */ - void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems); - - // cStructureGen overrides: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - diff --git a/source/Generating/Noise3DGenerator.cpp b/source/Generating/Noise3DGenerator.cpp deleted file mode 100644 index f47c64430..000000000 --- a/source/Generating/Noise3DGenerator.cpp +++ /dev/null @@ -1,581 +0,0 @@ - -// Nosie3DGenerator.cpp - -// Generates terrain using 3D noise, rather than composing. Is a test. - -#include "Globals.h" -#include "Noise3DGenerator.h" -#include "../OSSupport/File.h" -#include "../../iniFile/iniFile.h" -#include "../LinearInterpolation.h" -#include "../LinearUpscale.h" - - - - - -/* -// Perform an automatic test of upscaling upon program start (use breakpoints to debug): - -class Test -{ -public: - Test(void) - { - DoTest1(); - DoTest2(); - } - - - void DoTest1(void) - { - float In[3 * 3 * 3]; - for (int i = 0; i < ARRAYCOUNT(In); i++) - { - In[i] = (float)(i % 5); - } - Debug3DNoise(In, 3, 3, 3, "Upscale3D in"); - float Out[17 * 33 * 35]; - LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17); - Debug3DNoise(Out, 17, 33, 35, "Upscale3D test"); - } - - - void DoTest2(void) - { - float In[3 * 3]; - for (int i = 0; i < ARRAYCOUNT(In); i++) - { - In[i] = (float)(i % 5); - } - Debug2DNoise(In, 3, 3, "Upscale2D in"); - float Out[17 * 33]; - LinearUpscale2DArray(In, 3, 3, Out, 8, 16); - Debug2DNoise(Out, 17, 33, "Upscale2D test"); - } - -} gTest; -//*/ - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cNoise3DGenerator: - -cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) : - super(a_ChunkGenerator), - m_Perlin(1000), - m_Cubic(1000) -{ - m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5); - m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1); - m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2); - - #if 0 - // DEBUG: Test the noise generation: - // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size - // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M - m_SeaLevel = 62; - m_HeightAmplification = 0; - m_MidPoint = 75; - m_FrequencyX = 4; - m_FrequencyY = 4; - m_FrequencyZ = 4; - m_AirThreshold = 0.5; - - const int NumChunks = 4; - NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height]; - for (int x = 0; x < NumChunks; x++) - { - GenerateNoiseArray(x, 5, Noise[x]); - } - - // Save in XY cuts: - cFile f1; - if (f1.Open("Test_XY.grab", cFile::fmWrite)) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - for (int i = 0; i < NumChunks; i++) - { - int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; - unsigned char buf[cChunkDef::Width]; - for (int x = 0; x < cChunkDef::Width; x++) - { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); - } - f1.Write(buf, cChunkDef::Width); - } - } // for y - } // for z - } // if (XY file open) - - cFile f2; - if (f2.Open("Test_XZ.grab", cFile::fmWrite)) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int i = 0; i < NumChunks; i++) - { - int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; - unsigned char buf[cChunkDef::Width]; - for (int x = 0; x < cChunkDef::Width; x++) - { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); - } - f2.Write(buf, cChunkDef::Width); - } - } // for z - } // for y - } // if (XZ file open) - #endif // 0 -} - - - - - -cNoise3DGenerator::~cNoise3DGenerator() -{ - // Nothing needed yet -} - - - - - -void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) -{ - m_World = a_World; - - // Params: - m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); - m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); - m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); - m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8); - m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8); - m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8); - m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); -} - - - - - -void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) - { - a_BiomeMap[i] = biExtremeHills; - } -} - - - - - -void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) -{ - NOISE_DATATYPE Noise[17 * 257 * 17]; - GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise); - - // Output noise into chunk: - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - int idx = z * 17 * 257 + y * 17; - for (int x = 0; x < cChunkDef::Width; x++) - { - NOISE_DATATYPE n = Noise[idx++]; - BLOCKTYPE BlockType; - if (n > m_AirThreshold) - { - BlockType = (y > m_SeaLevel) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER; - } - else - { - BlockType = E_BLOCK_STONE; - } - a_ChunkDesc.SetBlockType(x, y, z, BlockType); - } - } - } - - UpdateHeightmap(a_ChunkDesc); - ComposeTerrain (a_ChunkDesc); -} - - - - - -void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise) -{ - NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise - NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash - - // Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed" - NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX; - NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX; - NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ; - NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ; - NOISE_DATATYPE StartY = 0; - NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY; - - m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW); - - // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ)); - - // Precalculate a "height" array: - NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source") - m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25); - for (int i = 0; i < ARRAYCOUNT(Height); i++) - { - Height[i] = abs(Height[i]) * m_HeightAmplification + 1; - } - - // Modify the noise by height data: - for (int y = 0; y < DIM_Y; y++) - { - NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20; - AddHeight *= AddHeight * AddHeight; - for (int z = 0; z < DIM_Z; z++) - { - NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]); - for (int x = 0; x < DIM_X; x++) - { - CurRow[x] += AddHeight / Height[x + DIM_X * z]; - } - } - } - - // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ)); - - // Upscale the Perlin noise into full-blown chunk dimensions: - LinearUpscale3DArray( - NoiseO, DIM_X, DIM_Y, DIM_Z, - a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z - ); - - // DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ)); -} - - - - - -void cNoise3DGenerator::UpdateHeightmap(cChunkDesc & a_ChunkDesc) -{ - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - for (int y = cChunkDef::Height - 1; y > 0; y--) - { - if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) - { - a_ChunkDesc.SetHeight(x, z, y); - break; - } - } // for y - } // for x - } // for z -} - - - - - -void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - // Make basic terrain composition: - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; - bool HasHadWater = false; - for (int y = LastAir - 1; y > 0; y--) - { - switch (a_ChunkDesc.GetBlockType(x, y, z)) - { - case E_BLOCK_AIR: - { - LastAir = y; - break; - } - case E_BLOCK_STONE: - { - if (LastAir - y > 3) - { - break; - } - if (HasHadWater) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); - } - else - { - a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); - } - break; - } - case E_BLOCK_STATIONARY_WATER: - { - LastAir = y; - HasHadWater = true; - break; - } - } // switch (GetBlockType()) - } // for y - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - } // for x - } // for z -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cNoise3DComposable: - -cNoise3DComposable::cNoise3DComposable(int a_Seed) : - m_Noise1(a_Seed + 1000), - m_Noise2(a_Seed + 2000), - m_Noise3(a_Seed + 3000) -{ -} - - - - - -void cNoise3DComposable::Initialize(cIniFile & a_IniFile) -{ - // Params: - m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); - m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); - m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); - m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10); - m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10); - m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10); - m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); -} - - - - - -void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ) -{ - if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ)) - { - // The noise for this chunk is already generated in m_Noise - return; - } - m_LastChunkX = a_ChunkX; - m_LastChunkZ = a_ChunkZ; - - // Upscaling parameters: - const int UPSCALE_X = 8; - const int UPSCALE_Y = 4; - const int UPSCALE_Z = 8; - - const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; - const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; - const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; - - // Precalculate a "height" array: - NOISE_DATATYPE Height[17 * 17]; // x + 17 * z - for (int z = 0; z < 17; z += UPSCALE_Z) - { - NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; - for (int x = 0; x < 17; x += UPSCALE_X) - { - NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; - NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1; - Height[x + 17 * z] = val * val * val; - } - } - - int idx = 0; - for (int y = 0; y < 257; y += UPSCALE_Y) - { - NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY; - NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20; - AddHeight *= AddHeight * AddHeight; - NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); - for (int z = 0; z < 17; z += UPSCALE_Z) - { - NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; - for (int x = 0; x < 17; x += UPSCALE_X) - { - NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; - CurFloor[x + 17 * z] = - m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 + - m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) + - m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 + - AddHeight / Height[x + 17 * z]; - } - } - // Linear-interpolate this XZ floor: - LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z); - } - - // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis - for (int y = 1; y < cChunkDef::Height; y++) - { - if ((y % UPSCALE_Y) == 0) - { - // This is the interpolation source floor, already calculated - continue; - } - int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y; - int HiFloorY = LoFloorY + UPSCALE_Y; - NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]); - NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]); - NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); - NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y; - int idx = 0; - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio; - idx += 1; - } - idx += 1; // Skipping one X column - } - } - - // The noise array is now fully interpolated - /* - // DEBUG: Output two images of the array, sliced by XY and XZ: - cFile f1; - if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - int idx = y * 17 * 17 + z * 17; - unsigned char buf[16]; - for (int x = 0; x < cChunkDef::Width; x++) - { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); - } - f1.Write(buf, 16); - } // for y - } // for z - } // if (XY file open) - - cFile f2; - if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - int idx = y * 17 * 17 + z * 17; - unsigned char buf[16]; - for (int x = 0; x < cChunkDef::Width; x++) - { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); - } - f2.Write(buf, 16); - } // for z - } // for y - } // if (XZ file open) - */ -} - - - - - -void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ); - - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel); - for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--) - { - if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold) - { - cChunkDef::SetHeight(a_HeightMap, x, z, y); - break; - } - } // for y - } // for x - } // for z -} - - - - - -void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); - - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - - // Make basic terrain composition: - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; - bool HasHadWater = false; - for (int y = LastAir; y < m_SeaLevel; y++) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); - } - for (int y = LastAir - 1; y > 0; y--) - { - if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold) - { - // "air" part - LastAir = y; - if (y < m_SeaLevel) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); - HasHadWater = true; - } - continue; - } - // "ground" part: - if (LastAir - y > 4) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); - continue; - } - if (HasHadWater) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); - } - else - { - a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); - } - } // for y - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - } // for x - } // for z -} - - - - diff --git a/source/Generating/Noise3DGenerator.h b/source/Generating/Noise3DGenerator.h deleted file mode 100644 index 0d211cddc..000000000 --- a/source/Generating/Noise3DGenerator.h +++ /dev/null @@ -1,106 +0,0 @@ - -// Noise3DGenerator.h - -// Generates terrain using 3D noise, rather than composing. Is a test. - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cNoise3DGenerator : - public cChunkGenerator::cGenerator -{ - typedef cChunkGenerator::cGenerator super; - -public: - cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator); - virtual ~cNoise3DGenerator(); - - virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; - virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; - -protected: - // Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: - static const int UPSCALE_X = 8; - static const int UPSCALE_Y = 4; - static const int UPSCALE_Z = 8; - - // Linear interpolation buffer dimensions, calculated from the step sizes: - static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; - static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; - static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; - - cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition - cCubicNoise m_Cubic; // The noise used for heightmap directing - - int m_SeaLevel; - NOISE_DATATYPE m_HeightAmplification; - NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be - NOISE_DATATYPE m_FrequencyX; - NOISE_DATATYPE m_FrequencyY; - NOISE_DATATYPE m_FrequencyZ; - NOISE_DATATYPE m_AirThreshold; - - /// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size - void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise); - - /// Updates heightmap based on the chunk's contents - void UpdateHeightmap(cChunkDesc & a_ChunkDesc); - - /// Composes terrain - adds dirt, grass and sand - void ComposeTerrain(cChunkDesc & a_ChunkDesc); -} ; - - - - - -class cNoise3DComposable : - public cTerrainHeightGen, - public cTerrainCompositionGen -{ -public: - cNoise3DComposable(int a_Seed); - - void Initialize(cIniFile & a_IniFile); - -protected: - cNoise m_Noise1; - cNoise m_Noise2; - cNoise m_Noise3; - - int m_SeaLevel; - NOISE_DATATYPE m_HeightAmplification; - NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be - NOISE_DATATYPE m_FrequencyX; - NOISE_DATATYPE m_FrequencyY; - NOISE_DATATYPE m_FrequencyZ; - NOISE_DATATYPE m_AirThreshold; - - int m_LastChunkX; - int m_LastChunkZ; - NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y - - - /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given - void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ); - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; -} ; - - - - diff --git a/source/Generating/Ravines.cpp b/source/Generating/Ravines.cpp deleted file mode 100644 index 6413b963b..000000000 --- a/source/Generating/Ravines.cpp +++ /dev/null @@ -1,531 +0,0 @@ - -// Ravines.cpp - -// Implements the cStructGenRavines class representing the ravine structure generator - -#include "Globals.h" -#include "Ravines.h" - - - - -/// How many ravines in each direction are generated for a given chunk. Must be an even number -static const int NEIGHBORHOOD_SIZE = 8; - -static const int NUM_RAVINE_POINTS = 4; - - - - - -struct cRavDefPoint -{ - int m_BlockX; - int m_BlockZ; - int m_Radius; - int m_Top; - int m_Bottom; - - cRavDefPoint(int a_BlockX, int a_BlockZ, int a_Radius, int a_Top, int a_Bottom) : - m_BlockX(a_BlockX), - m_BlockZ(a_BlockZ), - m_Radius(a_Radius), - m_Top (a_Top), - m_Bottom(a_Bottom) - { - } -} ; - -typedef std::vector cRavDefPoints; - - - - - -class cStructGenRavines::cRavine -{ - cRavDefPoints m_Points; - - /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise - void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); - - /// Refines (adds and smooths) defpoints from a_Src into a_Dst - void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst); - - /// Does one round of smoothing, two passes of RefineDefPoints() - void Smooth(void); - - /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block - void FinishLinear(void); - -public: - // Coords for which the ravine was generated (not necessarily the center) - int m_BlockX; - int m_BlockZ; - - cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); - - /// Carves the ravine into the chunk specified - void ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap - ); - - #ifdef _DEBUG - /// Exports itself as a SVG line definition - AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const; - #endif // _DEBUG -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenRavines: - -cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) : - m_Noise(a_Seed), - m_Size(a_Size) -{ -} - - - - - -cStructGenRavines::~cStructGenRavines() -{ - ClearCache(); -} - - - - - -void cStructGenRavines::ClearCache(void) -{ - for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } // for itr - m_Cache[] - m_Cache.clear(); -} - - - - - -void cStructGenRavines::GenStructures(cChunkDesc & a_ChunkDesc) -{ - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - cRavines Ravines; - GetRavinesForChunk(ChunkX, ChunkZ, Ravines); - for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr) - { - (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); - } // for itr - Ravines[] -} - - - - - -void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines) -{ - int BaseX = a_ChunkX * cChunkDef::Width / m_Size; - int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size; - if (BaseX < 0) - { - --BaseX; - } - if (BaseZ < 0) - { - --BaseZ; - } - BaseX -= 4; - BaseZ -= 4; - - // Walk the cache, move each ravine that we want into a_Ravines: - int StartX = BaseX * m_Size; - int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size; - int StartZ = BaseZ * m_Size; - int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size; - for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) - { - if ( - ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && - ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) - ) - { - // want - a_Ravines.push_back(*itr); - itr = m_Cache.erase(itr); - } - else - { - // don't want - ++itr; - } - } // for itr - m_Cache[] - - for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) - { - int RealX = (BaseX + x) * m_Size; - for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) - { - int RealZ = (BaseZ + z) * m_Size; - bool Found = false; - for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) - { - if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) - { - Found = true; - break; - } - } - if (!Found) - { - a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise)); - } - } - } - - // Copy a_Ravines into m_Cache to the beginning: - cRavines RavinesCopy(a_Ravines); - m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end()); - - // Trim the cache if it's too long: - if (m_Cache.size() > 100) - { - cRavines::iterator itr = m_Cache.begin(); - std::advance(itr, 100); - for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } - itr = m_Cache.begin(); - std::advance(itr, 100); - m_Cache.erase(itr, m_Cache.end()); - } - - /* - #ifdef _DEBUG - // DEBUG: Export as SVG into a file specific for the chunk, for visual verification: - AString SVG; - SVG.append("\n\n"); - for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) - { - SVG.append((*itr)->ExportAsSVG(0, 512, 512)); - } - SVG.append("\n"); - - AString fnam; - Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); - cFile File(fnam, cFile::fmWrite); - File.Write(SVG.c_str(), SVG.size()); - #endif // _DEBUG - //*/ -} - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenRavines::cRavine - -cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) : - m_BlockX(a_BlockX), - m_BlockZ(a_BlockZ) -{ - // Calculate the ravine shape-defining points: - GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise); - - // Smooth the ravine. A two passes are needed: - Smooth(); - Smooth(); - - // Linearly interpolate the neighbors so that they're close enough together: - FinishLinear(); -} - - - - - -void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) -{ - // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size): - a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024; - - // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction - int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2; - int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2; - int CenterX = a_BlockX + OffsetX; - int CenterZ = a_BlockZ + OffsetZ; - - // Get the base angle in which the ravine "axis" goes: - float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653); - float xc = sin(Angle); - float zc = cos(Angle); - - // Calculate the definition points and radii: - int MaxRadius = (int)(sqrt(12.0 + ((a_Noise.IntNoise2DInt(61 * a_BlockX, 97 * a_BlockZ) / 13) % a_Size) / 16)); - int Top = 32 + ((a_Noise.IntNoise2DInt(13 * a_BlockX, 17 * a_BlockZ) / 23) % 32); - int Bottom = 5 + ((a_Noise.IntNoise2DInt(17 * a_BlockX, 29 * a_BlockZ) / 13) % 32); - int Mid = (Top + Bottom) / 2; - int PointX = CenterX - (int)(xc * a_Size / 2); - int PointZ = CenterZ - (int)(zc * a_Size / 2); - m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, (Mid + Top) / 2, (Mid + Bottom) / 2)); - for (int i = 1; i < NUM_RAVINE_POINTS - 1; i++) - { - int LineX = CenterX + (int)(xc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); - int LineZ = CenterZ + (int)(zc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); - // Amplitude is the amount of blocks that this point is away from the ravine "axis" - int Amplitude = (a_Noise.IntNoise3DInt(70 * a_BlockX, 20 * a_BlockZ + 31 * i, 10000 * i) / 9) % a_Size; - Amplitude = Amplitude / 4 - a_Size / 8; // Amplitude is in interval [-a_Size / 4, a_Size / 4] - int PointX = LineX + (int)(zc * Amplitude); - int PointZ = LineZ - (int)(xc * Amplitude); - int Radius = MaxRadius - abs(i - NUM_RAVINE_POINTS / 2); // TODO: better radius function - int ThisTop = Top + ((a_Noise.IntNoise3DInt(7 * a_BlockX, 19 * a_BlockZ, i * 31) / 13) % 8) - 4; - int ThisBottom = Bottom + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 7 * a_BlockZ, i * 31) / 13) % 8) - 4; - m_Points.push_back(cRavDefPoint(PointX, PointZ, Radius, ThisTop, ThisBottom)); - } // for i - m_Points[] - PointX = CenterX + (int)(xc * a_Size / 2); - PointZ = CenterZ + (int)(zc * a_Size / 2); - m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, Mid, Mid)); -} - - - - - -void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst) -{ - // Smoothing: for each line segment, add points on its 1/4 lengths - int Num = a_Src.size() - 2; // this many intermediary points - a_Dst.clear(); - a_Dst.reserve(Num * 2 + 2); - cRavDefPoints::const_iterator itr = a_Src.begin() + 1; - a_Dst.push_back(a_Src.front()); - int PrevX = a_Src.front().m_BlockX; - int PrevZ = a_Src.front().m_BlockZ; - int PrevR = a_Src.front().m_Radius; - int PrevT = a_Src.front().m_Top; - int PrevB = a_Src.front().m_Bottom; - for (int i = 0; i <= Num; ++i, ++itr) - { - int dx = itr->m_BlockX - PrevX; - int dz = itr->m_BlockZ - PrevZ; - if (abs(dx) + abs(dz) < 4) - { - // Too short a segment to smooth-subdivide into quarters - continue; - } - int dr = itr->m_Radius - PrevR; - int dt = itr->m_Top - PrevT; - int db = itr->m_Bottom - PrevB; - int Rad1 = std::max(PrevR + 1 * dr / 4, 1); - int Rad2 = std::max(PrevR + 3 * dr / 4, 1); - a_Dst.push_back(cRavDefPoint(PrevX + 1 * dx / 4, PrevZ + 1 * dz / 4, Rad1, PrevT + 1 * dt / 4, PrevB + 1 * db / 4)); - a_Dst.push_back(cRavDefPoint(PrevX + 3 * dx / 4, PrevZ + 3 * dz / 4, Rad2, PrevT + 3 * dt / 4, PrevB + 3 * db / 4)); - PrevX = itr->m_BlockX; - PrevZ = itr->m_BlockZ; - PrevR = itr->m_Radius; - PrevT = itr->m_Top; - PrevB = itr->m_Bottom; - } - a_Dst.push_back(a_Src.back()); -} - - - - - -void cStructGenRavines::cRavine::Smooth(void) -{ - cRavDefPoints Pts; - RefineDefPoints(m_Points, Pts); // Refine m_Points -> Pts - RefineDefPoints(Pts, m_Points); // Refine Pts -> m_Points -} - - - - - -void cStructGenRavines::cRavine::FinishLinear(void) -{ - // For each segment, use Bresenham's line algorithm to draw a "line" of defpoints - // _X 2012_07_20: I tried modifying this algorithm to produce "thick" lines (only one coord change per point) - // But the results were about the same as the original, so I disposed of it again - no need to use twice the count of points - - cRavDefPoints Pts; - std::swap(Pts, m_Points); - - m_Points.reserve(Pts.size() * 3); - int PrevX = Pts.front().m_BlockX; - int PrevZ = Pts.front().m_BlockZ; - for (cRavDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) - { - int x1 = itr->m_BlockX; - int z1 = itr->m_BlockZ; - int dx = abs(x1 - PrevX); - int dz = abs(z1 - PrevZ); - int sx = (PrevX < x1) ? 1 : -1; - int sz = (PrevZ < z1) ? 1 : -1; - int err = dx - dz; - int R = itr->m_Radius; - int T = itr->m_Top; - int B = itr->m_Bottom; - while (true) - { - m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B)); - if ((PrevX == x1) && (PrevZ == z1)) - { - break; - } - int e2 = 2 * err; - if (e2 > -dz) - { - err -= dz; - PrevX += sx; - } - if (e2 < dx) - { - err += dx; - PrevZ += sz; - } - } // while (true) - } // for itr -} - - - - - -#ifdef _DEBUG -AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const -{ - AString SVG; - AppendPrintf(SVG, "m_BlockX, a_OffsetZ + itr->m_BlockZ); - Prefix = 'L'; - } - SVG.append("\"/>\n"); - - // Base point highlight: - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ - ); - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 - ); - - // A gray line from the base point to the first point of the ravine, for identification: - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ - ); - - // Offset guides: - if (a_OffsetX > 0) - { - AppendPrintf(SVG, "\n", - a_OffsetX, a_OffsetX - ); - } - if (a_OffsetZ > 0) - { - AppendPrintf(SVG, "\n", - a_OffsetZ, a_OffsetZ - ); - } - return SVG; -} -#endif // _DEBUG - - - - - -void cStructGenRavines::cRavine::ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap -) -{ - int BlockStartX = a_ChunkX * cChunkDef::Width; - int BlockStartZ = a_ChunkZ * cChunkDef::Width; - int BlockEndX = BlockStartX + cChunkDef::Width; - int BlockEndZ = BlockStartZ + cChunkDef::Width; - for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) - { - if ( - (itr->m_BlockX + itr->m_Radius < BlockStartX) || - (itr->m_BlockX - itr->m_Radius > BlockEndX) || - (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || - (itr->m_BlockZ - itr->m_Radius > BlockEndZ) - ) - { - // Cannot intersect, bail out early - continue; - } - - // Carve out a cylinder around the xz point, m_Radius in diameter, from Bottom to Top: - int RadiusSq = itr->m_Radius * itr->m_Radius; // instead of doing sqrt for each distance, we do sqr of the radius - int DifX = BlockStartX - itr->m_BlockX; // substitution for faster calc - int DifZ = BlockStartZ - itr->m_BlockZ; // substitution for faster calc - for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) - { - #ifdef _DEBUG - // DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on) - if ((DifX + x == 0) && (DifZ + z == 0)) - { - cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE); - } - #endif // _DEBUG - - int DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z); - if (DistSq <= RadiusSq) - { - int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast - for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++) - { - switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) - { - // Only carve out these specific block types - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_STONE: - case E_BLOCK_COBBLESTONE: - case E_BLOCK_GRAVEL: - case E_BLOCK_SAND: - case E_BLOCK_SANDSTONE: - case E_BLOCK_NETHERRACK: - case E_BLOCK_COAL_ORE: - case E_BLOCK_IRON_ORE: - case E_BLOCK_GOLD_ORE: - case E_BLOCK_DIAMOND_ORE: - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - { - cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); - break; - } - default: break; - } - } - } - } // for x, z - a_BlockTypes - } // for itr - m_Points[] -} - - - - diff --git a/source/Generating/Ravines.h b/source/Generating/Ravines.h deleted file mode 100644 index 05164a5b2..000000000 --- a/source/Generating/Ravines.h +++ /dev/null @@ -1,46 +0,0 @@ - -// Ravines.h - -// Interfaces to the cStructGenRavines class representing the ravine structure generator - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cStructGenRavines : - public cStructureGen -{ -public: - cStructGenRavines(int a_Seed, int a_Size); - ~cStructGenRavines(); - -protected: - class cRavine; // fwd: Ravines.cpp - typedef std::list cRavines; - - cNoise m_Noise; - int m_Size; // Max size, in blocks, of the ravines generated - cRavines m_Cache; - - /// Clears everything from the cache - void ClearCache(void); - - /// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function. - void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines); - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - diff --git a/source/Generating/StructGen.cpp b/source/Generating/StructGen.cpp deleted file mode 100644 index 2180261aa..000000000 --- a/source/Generating/StructGen.cpp +++ /dev/null @@ -1,675 +0,0 @@ - -// StructGen.h - -#include "Globals.h" -#include "StructGen.h" -#include "../BlockID.h" -#include "Trees.h" -#include "../BlockArea.h" -#include "../LinearUpscale.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenOreNests configuration: - -const int MAX_HEIGHT_COAL = 127; -const int NUM_NESTS_COAL = 50; -const int NEST_SIZE_COAL = 10; - -const int MAX_HEIGHT_IRON = 64; -const int NUM_NESTS_IRON = 14; -const int NEST_SIZE_IRON = 6; - -const int MAX_HEIGHT_REDSTONE = 16; -const int NUM_NESTS_REDSTONE = 4; -const int NEST_SIZE_REDSTONE = 6; - -const int MAX_HEIGHT_GOLD = 32; -const int NUM_NESTS_GOLD = 2; -const int NEST_SIZE_GOLD = 6; - -const int MAX_HEIGHT_DIAMOND = 15; -const int NUM_NESTS_DIAMOND = 1; -const int NEST_SIZE_DIAMOND = 4; - -const int MAX_HEIGHT_LAPIS = 30; -const int NUM_NESTS_LAPIS = 2; -const int NEST_SIZE_LAPIS = 5; - -const int MAX_HEIGHT_DIRT = 127; -const int NUM_NESTS_DIRT = 20; -const int NEST_SIZE_DIRT = 32; - -const int MAX_HEIGHT_GRAVEL = 70; -const int NUM_NESTS_GRAVEL = 15; -const int NEST_SIZE_GRAVEL = 32; - - - - - -template T Clamp(T a_Value, T a_Min, T a_Max) -{ - return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value); -} - - - - - -static bool SortTreeBlocks(const sSetBlock & a_First, const sSetBlock & a_Second) -{ - return (a_First.BlockType == E_BLOCK_LOG) && (a_Second.BlockType != E_BLOCK_LOG); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenTrees: - -void cStructGenTrees::GenStructures(cChunkDesc & a_ChunkDesc) -{ - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - - cChunkDesc WorkerDesc(ChunkX, ChunkZ); - - // Generate trees: - for (int x = 0; x <= 2; x++) - { - int BaseX = ChunkX + x - 1; - for (int z = 0; z <= 2; z++) - { - int BaseZ = ChunkZ + z - 1; - - cChunkDesc * Dest; - - if ((x != 1) || (z != 1)) - { - Dest = &WorkerDesc; - WorkerDesc.SetChunkCoords(BaseX, BaseZ); - - m_BiomeGen->GenBiomes (BaseX, BaseZ, WorkerDesc.GetBiomeMap()); - m_HeightGen->GenHeightMap (BaseX, BaseZ, WorkerDesc.GetHeightMap()); - m_CompositionGen->ComposeTerrain(WorkerDesc); - // TODO: Free the entity lists - } - else - { - Dest = &a_ChunkDesc; - } - - int NumTrees = GetNumTrees(BaseX, BaseZ, Dest->GetBiomeMap()); - - sSetBlockVector OutsideLogs, OutsideOther; - for (int i = 0; i < NumTrees; i++) - { - GenerateSingleTree(BaseX, BaseZ, i, *Dest, OutsideLogs, OutsideOther); - } - - sSetBlockVector IgnoredOverflow; - IgnoredOverflow.reserve(OutsideOther.size()); - ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideOther, IgnoredOverflow); - IgnoredOverflow.clear(); - IgnoredOverflow.reserve(OutsideLogs.size()); - ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideLogs, IgnoredOverflow); - } // for z - } // for x - - // Update the heightmap: - for (int x = 0; x < cChunkDef::Width; x++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = cChunkDef::Height - 1; y >= 0; y--) - { - if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) - { - a_ChunkDesc.SetHeight(x, z, y); - break; - } - } // for y - } // for z - } // for x -} - - - - - -void cStructGenTrees::GenerateSingleTree( - int a_ChunkX, int a_ChunkZ, int a_Seq, - cChunkDesc & a_ChunkDesc, - sSetBlockVector & a_OutsideLogs, - sSetBlockVector & a_OutsideOther -) -{ - int x = (m_Noise.IntNoise3DInt(a_ChunkX + a_ChunkZ, a_ChunkZ, a_Seq) / 19) % cChunkDef::Width; - int z = (m_Noise.IntNoise3DInt(a_ChunkX - a_ChunkZ, a_Seq, a_ChunkZ) / 19) % cChunkDef::Width; - - int Height = a_ChunkDesc.GetHeight(x, z); - - if ((Height <= 0) || (Height > 240)) - { - return; - } - - // Check the block underneath the tree: - BLOCKTYPE TopBlock = a_ChunkDesc.GetBlockType(x, Height, z); - if ((TopBlock != E_BLOCK_DIRT) && (TopBlock != E_BLOCK_GRASS) && (TopBlock != E_BLOCK_FARMLAND)) - { - return; - } - - sSetBlockVector TreeLogs, TreeOther; - GetTreeImageByBiome( - a_ChunkX * cChunkDef::Width + x, Height + 1, a_ChunkZ * cChunkDef::Width + z, - m_Noise, a_Seq, - a_ChunkDesc.GetBiome(x, z), - TreeLogs, TreeOther - ); - - // Check if the generated image fits the terrain. Only the logs are checked: - for (sSetBlockVector::const_iterator itr = TreeLogs.begin(); itr != TreeLogs.end(); ++itr) - { - if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ)) - { - // Outside the chunk - continue; - } - - BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z); - switch (Block) - { - CASE_TREE_ALLOWED_BLOCKS: - { - break; - } - default: - { - // There's something in the way, abort this tree altogether - return; - } - } - } - - ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeOther, a_OutsideOther); - ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeLogs, a_OutsideLogs); -} - - - - - -void cStructGenTrees::ApplyTreeImage( - int a_ChunkX, int a_ChunkZ, - cChunkDesc & a_ChunkDesc, - const sSetBlockVector & a_Image, - sSetBlockVector & a_Overflow -) -{ - // Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks - for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr) - { - if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ)) - { - // Inside this chunk, integrate into a_ChunkDesc: - switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z)) - { - case E_BLOCK_LEAVES: - { - if (itr->BlockType != E_BLOCK_LOG) - { - break; - } - // fallthrough: - } - CASE_TREE_OVERWRITTEN_BLOCKS: - { - a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - break; - } - - } // switch (GetBlock()) - continue; - } - - // Outside the chunk, push into a_Overflow. - // Don't check if already present there, by separating logs and others we don't need the checks anymore: - a_Overflow.push_back(*itr); - } -} - - - - - -int cStructGenTrees::GetNumTrees( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BiomeMap & a_Biomes -) -{ - int NumTrees = 0; - for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) - { - int Add = 0; - switch (cChunkDef::GetBiome(a_Biomes, x, z)) - { - case biPlains: Add = 1; break; - case biExtremeHills: Add = 3; break; - case biForest: Add = 30; break; - case biTaiga: Add = 30; break; - case biSwampland: Add = 8; break; - case biIcePlains: Add = 1; break; - case biIceMountains: Add = 1; break; - case biMushroomIsland: Add = 3; break; - case biMushroomShore: Add = 3; break; - case biForestHills: Add = 20; break; - case biTaigaHills: Add = 20; break; - case biExtremeHillsEdge: Add = 5; break; - case biJungle: Add = 120; break; - case biJungleHills: Add = 90; break; - } - NumTrees += Add; - } - return NumTrees / 1024; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenOreNests: - -void cStructGenOreNests::GenStructures(cChunkDesc & a_ChunkDesc) -{ - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes(); - GenerateOre(ChunkX, ChunkZ, E_BLOCK_COAL_ORE, MAX_HEIGHT_COAL, NUM_NESTS_COAL, NEST_SIZE_COAL, BlockTypes, 1); - GenerateOre(ChunkX, ChunkZ, E_BLOCK_IRON_ORE, MAX_HEIGHT_IRON, NUM_NESTS_IRON, NEST_SIZE_IRON, BlockTypes, 2); - GenerateOre(ChunkX, ChunkZ, E_BLOCK_REDSTONE_ORE, MAX_HEIGHT_REDSTONE, NUM_NESTS_REDSTONE, NEST_SIZE_REDSTONE, BlockTypes, 3); - GenerateOre(ChunkX, ChunkZ, E_BLOCK_GOLD_ORE, MAX_HEIGHT_GOLD, NUM_NESTS_GOLD, NEST_SIZE_GOLD, BlockTypes, 4); - GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIAMOND_ORE, MAX_HEIGHT_DIAMOND, NUM_NESTS_DIAMOND, NEST_SIZE_DIAMOND, BlockTypes, 5); - GenerateOre(ChunkX, ChunkZ, E_BLOCK_LAPIS_ORE, MAX_HEIGHT_LAPIS, NUM_NESTS_LAPIS, NEST_SIZE_LAPIS, BlockTypes, 6); - GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIRT, MAX_HEIGHT_DIRT, NUM_NESTS_DIRT, NEST_SIZE_DIRT, BlockTypes, 10); - GenerateOre(ChunkX, ChunkZ, E_BLOCK_GRAVEL, MAX_HEIGHT_GRAVEL, NUM_NESTS_GRAVEL, NEST_SIZE_GRAVEL, BlockTypes, 11); -} - - - - - -void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq) -{ - // This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other. - // It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes - // Only stone gets replaced with ore, all other blocks stay (so the nest can actually be smaller than specified). - - for (int i = 0; i < a_NumNests; i++) - { - int rnd = m_Noise.IntNoise3DInt(a_ChunkX + i, a_Seq, a_ChunkZ + 64 * i) / 8; - int BaseX = rnd % cChunkDef::Width; - rnd /= cChunkDef::Width; - int BaseZ = rnd % cChunkDef::Width; - rnd /= cChunkDef::Width; - int BaseY = rnd % a_MaxHeight; - rnd /= a_MaxHeight; - int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1/4 larger - int Num = 0; - while (Num < NestSize) - { - // Put a cuboid around [BaseX, BaseY, BaseZ] - int rnd = m_Noise.IntNoise3DInt(a_ChunkX + 64 * i, 2 * a_Seq + Num, a_ChunkZ + 32 * i) / 8; - int xsize = rnd % 2; - int ysize = (rnd / 4) % 2; - int zsize = (rnd / 16) % 2; - rnd >>= 8; - for (int x = xsize; x >= 0; --x) - { - int BlockX = BaseX + x; - if ((BlockX < 0) || (BlockX >= cChunkDef::Width)) - { - Num++; // So that the cycle finishes even if the base coords wander away from the chunk - continue; - } - for (int y = ysize; y >= 0; --y) - { - int BlockY = BaseY + y; - if ((BlockY < 0) || (BlockY >= cChunkDef::Height)) - { - Num++; // So that the cycle finishes even if the base coords wander away from the chunk - continue; - } - for (int z = zsize; z >= 0; --z) - { - int BlockZ = BaseZ + z; - if ((BlockZ < 0) || (BlockZ >= cChunkDef::Width)) - { - Num++; // So that the cycle finishes even if the base coords wander away from the chunk - continue; - } - - int Index = cChunkDef::MakeIndexNoCheck(BlockX, BlockY, BlockZ); - if (a_BlockTypes[Index] == E_BLOCK_STONE) - { - a_BlockTypes[Index] = a_OreType; - } - Num++; - } // for z - } // for y - } // for x - - // Move the base to a neighbor voxel - switch (rnd % 4) - { - case 0: BaseX--; break; - case 1: BaseX++; break; - } - switch ((rnd >> 3) % 4) - { - case 0: BaseY--; break; - case 1: BaseY++; break; - } - switch ((rnd >> 6) % 4) - { - case 0: BaseZ--; break; - case 1: BaseZ++; break; - } - } // while (Num < NumBlocks) - } // for i - NumNests -} - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenLakes: - -void cStructGenLakes::GenStructures(cChunkDesc & a_ChunkDesc) -{ - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - - for (int z = -1; z < 2; z++) for (int x = -1; x < 2; x++) - { - if (((m_Noise.IntNoise2DInt(ChunkX + x, ChunkZ + z) / 17) % 100) > m_Probability) - { - continue; - } - - cBlockArea Lake; - CreateLakeImage(ChunkX + x, ChunkZ + z, Lake); - - int OfsX = Lake.GetOriginX() + x * cChunkDef::Width; - int OfsZ = Lake.GetOriginZ() + z * cChunkDef::Width; - - // Merge the lake into the current data - a_ChunkDesc.WriteBlockArea(Lake, OfsX, Lake.GetOriginY(), OfsZ, cBlockArea::msLake); - } // for x, z - neighbor chunks -} - - - - - -void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake) -{ - a_Lake.Create(16, 8, 16); - a_Lake.Fill(cBlockArea::baTypes, E_BLOCK_SPONGE); // Sponge is the NOP blocktype for lake merging strategy - - // Find the minimum height in this chunk: - cChunkDef::HeightMap HeightMap; - m_HeiGen.GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap); - HEIGHTTYPE MinHeight = HeightMap[0]; - for (int i = 1; i < ARRAYCOUNT(HeightMap); i++) - { - if (HeightMap[i] < MinHeight) - { - MinHeight = HeightMap[i]; - } - } - - // Make a random position in the chunk by using a random 16 block XZ offset and random height up to chunk's max height minus 6 - MinHeight = std::max(MinHeight - 6, 2); - int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 128, a_ChunkZ) / 11; - // Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range - int OffsetX = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8; - Rnd >>= 12; - // Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range - int OffsetZ = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8; - Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 512, a_ChunkZ) / 13; - // Random height [1 .. MinHeight] with preference to center heights - int HeightY = 1 + (((Rnd & 0x1ff) % MinHeight) + (((Rnd >> 9) & 0x1ff) % MinHeight)) / 2; - - a_Lake.SetOrigin(OffsetX, HeightY, OffsetZ); - - // Hollow out a few bubbles inside the blockarea: - int NumBubbles = 4 + ((Rnd >> 18) & 0x03); // 4 .. 7 bubbles - BLOCKTYPE * BlockTypes = a_Lake.GetBlockTypes(); - for (int i = 0; i < NumBubbles; i++) - { - int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, i, a_ChunkZ) / 13; - const int BubbleR = 2 + (Rnd & 0x03); // 2 .. 5 - const int Range = 16 - 2 * BubbleR; - const int BubbleX = BubbleR + (Rnd % Range); - Rnd >>= 4; - const int BubbleY = 4 + (Rnd & 0x01); // 4 .. 5 - Rnd >>= 1; - const int BubbleZ = BubbleR + (Rnd % Range); - Rnd >>= 4; - const int HalfR = BubbleR / 2; // 1 .. 2 - const int RSquared = BubbleR * BubbleR; - for (int y = -HalfR; y <= HalfR; y++) - { - // BubbleY + y is in the [0, 7] bounds - int DistY = 4 * y * y / 3; - int IdxY = (BubbleY + y) * 16 * 16; - for (int z = -BubbleR; z <= BubbleR; z++) - { - int DistYZ = DistY + z * z; - if (DistYZ >= RSquared) - { - continue; - } - int IdxYZ = BubbleX + IdxY + (BubbleZ + z) * 16; - for (int x = -BubbleR; x <= BubbleR; x++) - { - if (x * x + DistYZ < RSquared) - { - BlockTypes[x + IdxYZ] = E_BLOCK_AIR; - } - } // for x - } // for z - } // for y - } // for i - bubbles - - // Turn air in the bottom half into liquid: - for (int y = 0; y < 4; y++) - { - for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) - { - if (BlockTypes[x + z * 16 + y * 16 * 16] == E_BLOCK_AIR) - { - BlockTypes[x + z * 16 + y * 16 * 16] = m_Fluid; - } - } // for z, x - } // for y - - // TODO: Turn sponge next to lava into stone - - // a_Lake.SaveToSchematicFile(Printf("Lake_%d_%d.schematic", a_ChunkX, a_ChunkZ)); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenDirectOverhangs: - -cStructGenDirectOverhangs::cStructGenDirectOverhangs(int a_Seed) : - m_Noise1(a_Seed), - m_Noise2(a_Seed + 1000) -{ -} - - - - - -void cStructGenDirectOverhangs::GenStructures(cChunkDesc & a_ChunkDesc) -{ - // If there is no column of the wanted biome, bail out: - if (!HasWantedBiome(a_ChunkDesc)) - { - return; - } - - HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight(); - - const int SEGMENT_HEIGHT = 8; - const int INTERPOL_X = 16; // Must be a divisor of 16 - const int INTERPOL_Z = 16; // Must be a divisor of 16 - // Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately. - // Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them - // Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it. - - int FloorBuf1[17 * 17]; - int FloorBuf2[17 * 17]; - int * FloorHi = FloorBuf1; - int * FloorLo = FloorBuf2; - int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - int BaseY = 63; - - // Interpolate the lowest floor: - for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) - { - FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] = - m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) * - m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) / - 256; - } // for x, z - FloorLo[] - LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); - - // Interpolate segments: - for (int Segment = BaseY; Segment < MaxHeight; Segment += SEGMENT_HEIGHT) - { - // First update the high floor: - for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) - { - FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] = - m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) * - m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / - 256; - } // for x, z - FloorLo[] - LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); - - // Interpolate between FloorLo and FloorHi: - for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) - { - switch (a_ChunkDesc.GetBiome(x, z)) - { - case biExtremeHills: - case biExtremeHillsEdge: - { - int Lo = FloorLo[x + 17 * z] / 256; - int Hi = FloorHi[x + 17 * z] / 256; - for (int y = 0; y < SEGMENT_HEIGHT; y++) - { - int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; - if (Val < 0) - { - a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_AIR); - } - } // for y - break; - } - } // switch (biome) - } // for z, x - - // Swap the floors: - std::swap(FloorLo, FloorHi); - } -} - - - - - -bool cStructGenDirectOverhangs::HasWantedBiome(cChunkDesc & a_ChunkDesc) const -{ - cChunkDef::BiomeMap & Biomes = a_ChunkDesc.GetBiomeMap(); - for (int i = 0; i < ARRAYCOUNT(Biomes); i++) - { - switch (Biomes[i]) - { - case biExtremeHills: - case biExtremeHillsEdge: - { - return true; - } - } - } // for i - return false; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenDistortedMembraneOverhangs: - -cStructGenDistortedMembraneOverhangs::cStructGenDistortedMembraneOverhangs(int a_Seed) : - m_NoiseX(a_Seed + 1000), - m_NoiseY(a_Seed + 2000), - m_NoiseZ(a_Seed + 3000), - m_NoiseH(a_Seed + 4000) -{ -} - - - - - -void cStructGenDistortedMembraneOverhangs::GenStructures(cChunkDesc & a_ChunkDesc) -{ - const NOISE_DATATYPE Frequency = (NOISE_DATATYPE)16; - const NOISE_DATATYPE Amount = (NOISE_DATATYPE)1; - for (int y = 50; y < 128; y++) - { - NOISE_DATATYPE NoiseY = (NOISE_DATATYPE)y / 32; - // TODO: proper water level - where to get? - BLOCKTYPE ReplacementBlock = (y > 62) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER; - for (int z = 0; z < cChunkDef::Width; z++) - { - NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z)) / Frequency; - for (int x = 0; x < cChunkDef::Width; x++) - { - NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x)) / Frequency; - NOISE_DATATYPE DistortX = m_NoiseX.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount; - NOISE_DATATYPE DistortY = m_NoiseY.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount; - NOISE_DATATYPE DistortZ = m_NoiseZ.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount; - int MembraneHeight = 96 - (int)((DistortY + m_NoiseH.CubicNoise2D(NoiseX + DistortX, NoiseZ + DistortZ)) * 30); - if (MembraneHeight < y) - { - a_ChunkDesc.SetBlockType(x, y, z, ReplacementBlock); - } - } // for y - } // for x - } // for z -} - - - - diff --git a/source/Generating/StructGen.h b/source/Generating/StructGen.h deleted file mode 100644 index 853748bb8..000000000 --- a/source/Generating/StructGen.h +++ /dev/null @@ -1,165 +0,0 @@ - -// StructGen.h - -/* Interfaces to the various structure generators: - - cStructGenTrees - - cStructGenMarbleCaves - - cStructGenOres -*/ - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cStructGenTrees : - public cStructureGen -{ -public: - cStructGenTrees(int a_Seed, cBiomeGen * a_BiomeGen, cTerrainHeightGen * a_HeightGen, cTerrainCompositionGen * a_CompositionGen) : - m_Seed(a_Seed), - m_Noise(a_Seed), - m_BiomeGen(a_BiomeGen), - m_HeightGen(a_HeightGen), - m_CompositionGen(a_CompositionGen) - {} - -protected: - - int m_Seed; - cNoise m_Noise; - cBiomeGen * m_BiomeGen; - cTerrainHeightGen * m_HeightGen; - cTerrainCompositionGen * m_CompositionGen; - - /** Generates and applies an image of a single tree. - Parts of the tree inside the chunk are applied to a_BlockX. - Parts of the tree outside the chunk are stored in a_OutsideX - */ - void GenerateSingleTree( - int a_ChunkX, int a_ChunkZ, int a_Seq, - cChunkDesc & a_ChunkDesc, - sSetBlockVector & a_OutsideLogs, - sSetBlockVector & a_OutsideOther - ) ; - - /// Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow - void ApplyTreeImage( - int a_ChunkX, int a_ChunkZ, - cChunkDesc & a_ChunkDesc, - const sSetBlockVector & a_Image, - sSetBlockVector & a_Overflow - ); - - int GetNumTrees( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BiomeMap & a_Biomes - ); - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cStructGenOreNests : - public cStructureGen -{ -public: - cStructGenOreNests(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {} - -protected: - cNoise m_Noise; - int m_Seed; - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; - - void GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq); -} ; - - - - - -class cStructGenLakes : - public cStructureGen -{ -public: - cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainHeightGen & a_HeiGen, int a_Probability) : - m_Noise(a_Seed), - m_Seed(a_Seed), - m_Fluid(a_Fluid), - m_HeiGen(a_HeiGen), - m_Probability(a_Probability) - { - } - -protected: - cNoise m_Noise; - int m_Seed; - BLOCKTYPE m_Fluid; - cTerrainHeightGen & m_HeiGen; - int m_Probability; ///< Chance, 0 .. 100, of a chunk having the lake - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; - - /// Creates a lake image for the specified chunk into a_Lake - void CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake); -} ; - - - - - - -class cStructGenDirectOverhangs : - public cStructureGen -{ -public: - cStructGenDirectOverhangs(int a_Seed); - -protected: - cNoise m_Noise1; - cNoise m_Noise2; - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; - - bool HasWantedBiome(cChunkDesc & a_ChunkDesc) const; -} ; - - - - - -class cStructGenDistortedMembraneOverhangs : - public cStructureGen -{ -public: - cStructGenDistortedMembraneOverhangs(int a_Seed); - -protected: - cNoise m_NoiseX; - cNoise m_NoiseY; - cNoise m_NoiseZ; - cNoise m_NoiseH; - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - diff --git a/source/Generating/Trees.cpp b/source/Generating/Trees.cpp deleted file mode 100644 index 7ca30c60f..000000000 --- a/source/Generating/Trees.cpp +++ /dev/null @@ -1,684 +0,0 @@ - -// Trees.cpp - -// Implements helper functions used for generating trees - -#include "Globals.h" -#include "Trees.h" -#include "../BlockID.h" - - - - -// DEBUG: -int gTotalLargeJungleTrees = 0; -int gOversizeLargeJungleTrees = 0; - - - - - -typedef struct -{ - int x, z; -} sCoords; - -typedef struct -{ - int x, z; - NIBBLETYPE Meta; -} sMetaCoords; - -static const sCoords Corners[] = -{ - {-1, -1}, - {-1, 1}, - {1, -1}, - {1, 1}, -} ; - -// BigO = a big ring of blocks, used for generating horz slices of treetops, the number indicates the radius - -static const sCoords BigO1[] = -{ - {0, -1}, - {-1, 0}, {1, 0}, - {0, 1}, -} ; - -static const sCoords BigO2[] = -{ - {-1, -2}, {0, -2}, {1, -2}, - {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, - {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, - {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, - {-1, 2}, {0, 2}, {1, 2}, -} ; - -static const sCoords BigO3[] = -{ - {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, - {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, - {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, - {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0}, - {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, - {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2}, - {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3}, -} ; - -static const sCoords BigO4[] = // Part of Big Jungle tree -{ - {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4}, - {-3, -3}, {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {3, -3}, - {-4, -2}, {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, {4, -2}, - {-4, -1}, {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, {4, -1}, - {-4, 0}, {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, - {-4, 1}, {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, - {-4, 2}, {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}, - {-3, 3}, {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, - {-2, 4}, {-1, 4}, {0, 4}, {1, 4}, {2, 4}, -} ; - - - - - -typedef struct -{ - const sCoords * Coords; - size_t Count; -} sCoordsArr; - -static const sCoordsArr BigOs[] = -{ - {BigO1, ARRAYCOUNT(BigO1)}, - {BigO2, ARRAYCOUNT(BigO2)}, - {BigO3, ARRAYCOUNT(BigO3)}, - {BigO4, ARRAYCOUNT(BigO4)}, -} ; - - - - - -/// Pushes a specified layer of blocks of the same type around (x, h, z) into a_Blocks -inline void PushCoordBlocks(int a_BlockX, int a_Height, int a_BlockZ, sSetBlockVector & a_Blocks, const sCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) -{ - for (size_t i = 0; i < a_NumCoords; i++) - { - a_Blocks.push_back(sSetBlock(a_BlockX + a_Coords[i].x, a_Height, a_BlockZ + a_Coords[i].z, a_BlockType, a_Meta)); - } -} - - - - -inline void PushCornerBlocks(int a_BlockX, int a_Height, int a_BlockZ, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, int a_CornersDist, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) -{ - for (size_t i = 0; i < ARRAYCOUNT(Corners); i++) - { - int x = a_BlockX + Corners[i].x; - int z = a_BlockZ + Corners[i].z; - if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height, z + 64 * a_Seq) <= a_Chance) - { - a_Blocks.push_back(sSetBlock(x, a_Height, z, a_BlockType, a_Meta)); - } - } // for i - Corners[] -} - - - - - -inline void PushSomeColumns(int a_BlockX, int a_Height, int a_BlockZ, int a_ColumnHeight, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, const sMetaCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType) -{ - for (size_t i = 0; i < a_NumCoords; i++) - { - int x = a_BlockX + a_Coords[i].x; - int z = a_BlockZ + a_Coords[i].z; - if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height + i, z + 64 * a_Seq) <= a_Chance) - { - for (int j = 0; j < a_ColumnHeight; j++) - { - a_Blocks.push_back(sSetBlock(x, a_Height - j, z, a_BlockType, a_Coords[i].Meta)); - } - } - } // for i - a_Coords[] -} - - - - - -void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - switch (a_Biome) - { - case biPlains: - case biExtremeHills: - case biExtremeHillsEdge: - case biForest: - case biMushroomIsland: - case biMushroomShore: - case biForestHills: - { - // Apple or birch trees: - if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x5fffffff) - { - GetAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } - else - { - GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } - break; - } - - case biTaiga: - case biIcePlains: - case biIceMountains: - case biTaigaHills: - { - // Conifers - GetConiferTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - break; - } - - case biSwampland: - { - // Swamp trees: - GetSwampTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - break; - } - - case biJungle: - case biJungleHills: - { - // Apple bushes, large jungle trees, small jungle trees - if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x6fffffff) - { - GetAppleBushImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } - else - { - GetJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } - } - } -} - - - - - -void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000) - { - GetSmallAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } - else - { - GetLargeAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } -} - - - - - -void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - /* Small apple tree has: - - a top plus (no log) - - optional BigO1 + random corners (log) - - 2 layers of BigO2 + random corners (log) - - 1 to 3 blocks of trunk - */ - - int Random = a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) >> 3; - - int Heights[] = {1, 2, 2, 3} ; - int Height = 1 + Heights[Random & 3]; - Random >>= 2; - - // Pre-alloc so that we don't realloc too often later: - a_LogBlocks.reserve(Height + 5); - a_OtherBlocks.reserve(ARRAYCOUNT(BigO2) * 2 + ARRAYCOUNT(BigO1) + ARRAYCOUNT(Corners) * 3 + 3 + 5); - - // Trunk: - for (int i = 0; i < Height; i++) - { - a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); - } - int Hei = a_BlockY + Height; - - // 2 BigO2 + corners layers: - for (int i = 0; i < 2; i++) - { - PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x5000000 - i * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); - Hei++; - } // for i - 2* - - // Optional BigO1 + corners layer: - if ((Random & 1) == 0) - { - PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x6000000, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); - Hei++; - } - - // Top plus: - PushCoordBlocks(a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - a_OtherBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); -} - - - - - -void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - // TODO -} - - - - - -void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - int Height = 5 + (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) % 3); - - // Prealloc, so that we don't realloc too often later: - a_LogBlocks.reserve(Height); - a_OtherBlocks.reserve(80); - - // The entire trunk, out of logs: - for (int i = Height - 1; i >= 0; --i) - { - a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_BIRCH)); - } - int h = a_BlockY + Height; - - // Top layer - just the Plus: - PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); - a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH)); // There's no log at this layer - h--; - - // Second layer - log, Plus and maybe Corners: - PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); - PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); - h--; - - // Third and fourth layers - BigO2 and maybe 2*Corners: - for (int Row = 0; Row < 2; Row++) - { - PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); - PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x3fffffff + Row * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); - h--; - } // for Row - 2* -} - - - - - -void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - // Half chance for a spruce, half for a pine: - if (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) < 0x40000000) - { - GetSpruceTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } - else - { - GetPineTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } -} - - - - - -void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - // Spruces have a top section with layer sizes of (0, 1, 0) or only (1, 0), - // then 1 - 3 sections of ascending sizes (1, 2) [most often], (1, 3) or (1, 2, 3) - // and an optional bottom section of size 1, followed by 1 - 3 clear trunk blocks - - // We'll use bits from this number as partial random numbers; but the noise function has mod8 irregularities - // (each of the mod8 remainders has a very different chance of occurrence) - that's why we divide by 8 - int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) / 8; - - static const int sHeights[] = {1, 2, 2, 3}; - int Height = sHeights[MyRandom & 3]; - MyRandom >>= 2; - - // Prealloc, so that we don't realloc too often later: - a_LogBlocks.reserve(Height); - a_OtherBlocks.reserve(180); - - // Clear trunk blocks: - for (int i = 0; i < Height; i++) - { - a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - } - Height += a_BlockY; - - // Optional size-1 bottom leaves layer: - if ((MyRandom & 1) == 0) - { - PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - Height++; - } - MyRandom >>= 1; - - // 1 to 3 sections of leaves layers: - static const int sNumSections[] = {1, 2, 2, 3}; - int NumSections = sNumSections[MyRandom & 3]; - MyRandom >>= 2; - for (int i = 0; i < NumSections; i++) - { - switch (MyRandom & 3) // SectionType; (1, 2) twice as often as the other two - { - case 0: - case 1: - { - PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - Height += 2; - break; - } - case 2: - { - PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - Height += 2; - break; - } - case 3: - { - PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - PushCoordBlocks(a_BlockX, Height + 2, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - Height += 3; - break; - } - } // switch (SectionType) - MyRandom >>= 2; - } // for i - Sections - - if ((MyRandom & 1) == 0) - { - // (0, 1, 0) top: - a_LogBlocks.push_back (sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); - a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); - } - else - { - // (1, 0) top: - a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); - PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); - } -} - - - - - -void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - // Tall, little leaves on top. The top leaves are arranged in a shape of two cones joined by their bases. - // There can be one or two layers representing the cone bases (SameSizeMax) - - int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8; - int TrunkHeight = 8 + (MyRandom % 3); - int SameSizeMax = ((MyRandom & 8) == 0) ? 1 : 0; - MyRandom >>= 3; - int NumLeavesLayers = 2 + (MyRandom % 3); // Number of layers that have leaves in them - if (NumLeavesLayers == 2) - { - SameSizeMax = 0; - } - - // Pre-allocate the vector: - a_LogBlocks.reserve(TrunkHeight); - a_OtherBlocks.reserve(NumLeavesLayers * 25); - - // The entire trunk, out of logs: - for (int i = TrunkHeight; i >= 0; --i) - { - a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); - } - int h = a_BlockY + TrunkHeight + 2; - - // Top layer - just a single leaves block: - a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); - h--; - - // One more layer is above the trunk, push the central leaves: - a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); - - // Layers expanding in size, then collapsing again: - // LOGD("Generating %d layers of pine leaves, SameSizeMax = %d", NumLeavesLayers, SameSizeMax); - for (int i = 0; i < NumLeavesLayers; ++i) - { - int LayerSize = std::min(i, NumLeavesLayers - i + SameSizeMax - 1); - // LOGD("LayerSize %d: %d", i, LayerSize); - if (LayerSize < 0) - { - break; - } - ASSERT(LayerSize < ARRAYCOUNT(BigOs)); - PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); - h--; - } -} - - - - - -void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - // Vines are around the BigO3, but not in the corners; need proper meta for direction - static const sMetaCoords Vines[] = - { - {-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face - {-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face - {4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face - {-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, {-4, 2, 8}, // West face - } ; - - int Height = 3 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8) % 3; - - a_LogBlocks.reserve(Height); - a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO2) + 2 * ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 20); - - for (int i = 0; i < Height; i++) - { - a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); - } - int hei = a_BlockY + Height - 2; - - // Put vines around the lowermost leaves layer: - PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES); - - // The lower two leaves layers are BigO3 with log in the middle and possibly corners: - for (int i = 0; i < 2; i++) - { - PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - hei++; - } // for i - 2* - - // The upper two leaves layers are BigO2 with leaves in the middle and possibly corners: - for (int i = 0; i < 2; i++) - { - PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); - hei++; - } // for i - 2* -} - - - - - -void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - a_OtherBlocks.reserve(3 + ARRAYCOUNT(BigO2) + ARRAYCOUNT(BigO1)); - - int hei = a_BlockY; - a_LogBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); - PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - hei++; - - a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); - PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - hei++; - - a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); -} - - - - - -void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000) - { - GetSmallJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } - else - { - GetLargeJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - } -} - - - - - -void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - // TODO: Generate proper jungle trees with branches - - // Vines are around the BigO4, but not in the corners; need proper meta for direction - static const sMetaCoords Vines[] = - { - {-2, -5, 1}, {-1, -5, 1}, {0, -5, 1}, {1, -5, 1}, {2, -5, 1}, // North face - {-2, 5, 4}, {-1, 5, 4}, {0, 5, 4}, {1, 5, 4}, {2, 5, 4}, // South face - {5, -2, 2}, {5, -1, 2}, {5, 0, 2}, {5, 1, 2}, {5, 2, 2}, // East face - {-5, -2, 8}, {-5, -1, 8}, {-5, 0, 8}, {-5, 1, 8}, {-5, 2, 8}, // West face - // TODO: vines around the trunk, proper metas and height - } ; - - int Height = 24 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 11) % 24; - - a_LogBlocks.reserve(Height * 4); - a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO4) + ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 50); - - for (int i = 0; i < Height; i++) - { - a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); - a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); - a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE)); - a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE)); - } - int hei = a_BlockY + Height - 2; - - // Put vines around the lowermost leaves layer: - PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES); - - // The lower two leaves layers are BigO4 with log in the middle and possibly corners: - for (int i = 0; i < 2; i++) - { - PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO4, ARRAYCOUNT(BigO4), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); - PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); - hei++; - } // for i - 2* - - // The top leaves layer is a BigO3 with leaves in the middle and possibly corners: - PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); - PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); - a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE)); -} - - - - - -void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) -{ - // Vines are around the BigO3, but not in the corners; need proper meta for direction - static const sMetaCoords Vines[] = - { - {-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face - {-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face - {4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face - {-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, // West face - // TODO: proper metas and height: {0, 1, 1}, {0, -1, 4}, {-1, 0, 2}, {1, 1, 8}, // Around the tunk - } ; - - int Height = 7 + (a_Noise.IntNoise3DInt(a_BlockX + 5 * a_Seq, a_BlockY, a_BlockZ + 5 * a_Seq) / 5) % 3; - - a_LogBlocks.reserve(Height); - a_OtherBlocks.reserve( - 2 * ARRAYCOUNT(BigO3) + // O3 layer, 2x - 2 * ARRAYCOUNT(BigO2) + // O2 layer, 2x - ARRAYCOUNT(BigO1) + 1 + // Plus on the top - Height * ARRAYCOUNT(Vines) + // Vines - 50 // some safety - ); - - for (int i = 0; i < Height; i++) - { - a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); - } - int hei = a_BlockY + Height - 3; - - // Put vines around the lowermost leaves layer: - PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES); - - // The lower two leaves layers are BigO3 with log in the middle and possibly corners: - for (int i = 0; i < 2; i++) - { - PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); - PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); - hei++; - } // for i - 2* - - // Two layers of BigO2 leaves, possibly with corners: - for (int i = 0; i < 1; i++) - { - PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); - PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); - hei++; - } // for i - 2* - - // Top plus, all leaves: - PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); - a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE)); -} - - - - diff --git a/source/Generating/Trees.h b/source/Generating/Trees.h deleted file mode 100644 index f5148ad6f..000000000 --- a/source/Generating/Trees.h +++ /dev/null @@ -1,93 +0,0 @@ - -// Trees.h - -// Interfaces to helper functions used for generating trees - -/* -Note that all of these functions must generate the same tree image for the same input (x, y, z, seq) - - cStructGenTrees depends on this -To generate a random image for the (x, y, z) coords, pass an arbitrary value as (seq). -Each function returns two arrays of blocks, "logs" and "other". The point is that logs are of higher priority, -logs can overwrite others(leaves), but others shouldn't overwrite logs. This is an optimization for the generator. -*/ - - - - - -#pragma once - -#include "../ChunkDef.h" -#include "../Noise.h" - - - - - -// Blocks that don't block tree growth: -#define CASE_TREE_ALLOWED_BLOCKS \ - case E_BLOCK_AIR: \ - case E_BLOCK_LEAVES: \ - case E_BLOCK_SNOW: \ - case E_BLOCK_TALL_GRASS: \ - case E_BLOCK_DEAD_BUSH: \ - case E_BLOCK_SAPLING: \ - case E_BLOCK_VINES - -// Blocks that a tree may overwrite when growing: -#define CASE_TREE_OVERWRITTEN_BLOCKS \ - case E_BLOCK_AIR: \ - /* case E_BLOCK_LEAVES: LEAVES are a special case, they can be overwritten only by log. Handled in cChunkMap::ReplaceTreeBlocks(). */ \ - case E_BLOCK_SNOW: \ - case E_BLOCK_TALL_GRASS: \ - case E_BLOCK_DEAD_BUSH: \ - case E_BLOCK_SAPLING: \ - case E_BLOCK_VINES - - - - - -/// Generates an image of a tree at the specified coords (lowest trunk block) in the specified biome -void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a random apple tree -void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a small (nonbranching) apple tree -void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a large (branching) apple tree -void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a random birch tree -void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a random conifer tree -void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a random spruce (short conifer, two layers of leaves) -void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a random pine (tall conifer, little leaves at top) -void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a random swampland tree -void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a random apple bush (for jungles) -void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a random jungle tree -void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a large jungle tree (2x2 trunk) -void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - -/// Generates an image of a small jungle tree (1x1 trunk) -void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); - - - - - diff --git a/source/Globals.cpp b/source/Globals.cpp deleted file mode 100644 index 13c6ae709..000000000 --- a/source/Globals.cpp +++ /dev/null @@ -1,10 +0,0 @@ - -// Globals.cpp - -// This file is used for precompiled header generation in MSVC environments - -#include "Globals.h" - - - - diff --git a/source/Globals.h b/source/Globals.h deleted file mode 100644 index ef79e4cf1..000000000 --- a/source/Globals.h +++ /dev/null @@ -1,227 +0,0 @@ - -// Globals.h - -// This file gets included from every module in the project, so that global symbols may be introduced easily -// Also used for precompiled header generation in MSVC environments - - - - - -// Compiler-dependent stuff: -#if defined(_MSC_VER) - // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether - #pragma warning(disable:4481) - - // Disable some warnings that we don't care about: - #pragma warning(disable:4100) - - #define OBSOLETE __declspec(deprecated) - - // No alignment needed in MSVC - #define ALIGN_8 - #define ALIGN_16 - -#elif defined(__GNUC__) - - // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? - #define abstract - - // TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) - #define override - - #define OBSOLETE __attribute__((deprecated)) - - #define ALIGN_8 __attribute__((aligned(8))) - #define ALIGN_16 __attribute__((aligned(16))) - - // Some portability macros :) - #define stricmp strcasecmp - -#else - - #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" - - /* - // Copy and uncomment this into another #elif section based on your compiler identification - - // Explicitly mark classes as abstract (no instances can be created) - #define abstract - - // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) - #define override - - // Mark functions as obsolete, so that their usage results in a compile-time warning - #define OBSOLETE - - // Mark types / variables for alignment. Do the platforms need it? - #define ALIGN_8 - #define ALIGN_16 - */ - -#endif - - - - - -// Integral types with predefined sizes: -typedef long long Int64; -typedef int Int32; -typedef short Int16; - -typedef unsigned long long UInt64; -typedef unsigned int UInt32; -typedef unsigned short UInt16; - - - - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for any class that shouldn't allow copying itself -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName &); \ - void operator=(const TypeName &) - -// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc -#define UNUSED(X) (void)(X) - - - - -// OS-dependent stuff: -#ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - - #define _WIN32_WINNT 0x501 // We want to target WinXP and higher - - #include - #include - #include // IPv6 stuff - - // Windows SDK defines min and max macros, messing up with our std::min and std::max usage - #undef min - #undef max - - // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant - #ifdef GetFreeSpace - #undef GetFreeSpace - #endif // GetFreeSpace -#else - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - #include - #include - #include -#if !defined(ANDROID_NDK) - #include -#endif -#endif - -#if defined(ANDROID_NDK) - #define FILE_IO_PREFIX "/sdcard/mcserver/" -#else - #define FILE_IO_PREFIX "" -#endif - - - - - -// CRT stuff: -#include -#include -#include -#include -#include - - - - - -// STL stuff: -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - - - -// Common headers (part 1, without macros): -#include "StringUtils.h" -#include "OSSupport/Sleep.h" -#include "OSSupport/CriticalSection.h" -#include "OSSupport/Semaphore.h" -#include "OSSupport/Event.h" -#include "OSSupport/Thread.h" -#include "OSSupport/File.h" -#include "MCLogger.h" - - - - - -// Common definitions: - -/// Evaluates to the number of elements in an array (compile-time!) -#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) - -/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" ) -#define KiB * 1024 -#define MiB * 1024 * 1024 - -/// Faster than (int)floorf((float)x / (float)div) -#define FAST_FLOOR_DIV( x, div ) (((x) - (((x) < 0) ? ((div) - 1) : 0)) / (div)) - -// Own version of assert() that writes failed assertions to the log for review -#ifdef _DEBUG - #define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) ) -#else - #define ASSERT(x) ((void)0) -#endif - -// Pretty much the same as ASSERT() but stays in Release builds -#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) ) - - - - - -/// A generic interface used mainly in ForEach() functions -template class cItemCallback -{ -public: - /// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating - virtual bool Item(Type * a_Type) = 0; -} ; - - - - - -// Common headers (part 2, with macros): -#include "ChunkDef.h" -#include "BlockID.h" - - - - diff --git a/source/Group.cpp b/source/Group.cpp deleted file mode 100644 index 448d29d87..000000000 --- a/source/Group.cpp +++ /dev/null @@ -1,37 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Group.h" - -void cGroup::AddCommand( std::string a_Command ) -{ - m_Commands[ a_Command ] = true; -} - -void cGroup::AddPermission( std::string a_Permission ) -{ - m_Permissions[ a_Permission ] = true; -} - -bool cGroup::HasCommand( std::string a_Command ) -{ - if( m_Commands.find("*") != m_Commands.end() ) return true; - - CommandMap::iterator itr = m_Commands.find( a_Command ); - if( itr != m_Commands.end() ) - { - if( itr->second ) return true; - } - - for( GroupList::iterator itr = m_Inherits.begin(); itr != m_Inherits.end(); ++itr ) - { - if( (*itr)->HasCommand( a_Command ) ) return true; - } - return false; -} - -void cGroup::InheritFrom( cGroup* a_Group ) -{ - m_Inherits.remove( a_Group ); - m_Inherits.push_back( a_Group ); -} \ No newline at end of file diff --git a/source/Group.h b/source/Group.h deleted file mode 100644 index 65ee1a60a..000000000 --- a/source/Group.h +++ /dev/null @@ -1,40 +0,0 @@ - -#pragma once - - - - - -class cGroup // tolua_export -{ // tolua_export -public: // tolua_export - cGroup() {} - ~cGroup() {} - - void SetName( std::string a_Name ) { m_Name = a_Name; } // tolua_export - const std::string & GetName() const { return m_Name; } // tolua_export - void SetColor( std::string a_Color ) { m_Color = a_Color; } // tolua_export - void AddCommand( std::string a_Command ); // tolua_export - void AddPermission( std::string a_Permission ); // tolua_export - void InheritFrom( cGroup* a_Group ); // tolua_export - - bool HasCommand( std::string a_Command ); // tolua_export - - typedef std::map< std::string, bool > PermissionMap; - const PermissionMap & GetPermissions() const { return m_Permissions; } - - typedef std::map< std::string, bool > CommandMap; - const CommandMap & GetCommands() const { return m_Commands; } - - const AString & GetColor() const { return m_Color; } // tolua_export - - typedef std::list< cGroup* > GroupList; - const GroupList & GetInherits() const { return m_Inherits; } -private: - std::string m_Name; - std::string m_Color; - - PermissionMap m_Permissions; - CommandMap m_Commands; - GroupList m_Inherits; -};// tolua_export diff --git a/source/GroupManager.cpp b/source/GroupManager.cpp deleted file mode 100644 index d7332fd0a..000000000 --- a/source/GroupManager.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "GroupManager.h" -#include "Group.h" -#include "../iniFile/iniFile.h" -#include "ChatColor.h" -#include "Root.h" - - - - - -typedef std::map< AString, cGroup* > GroupMap; - - - - - -struct cGroupManager::sGroupManagerState -{ - GroupMap Groups; -}; - - - - - -cGroupManager::~cGroupManager() -{ - for( GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr ) - { - delete itr->second; - } - m_pState->Groups.clear(); - - delete m_pState; -} - - - - - -cGroupManager::cGroupManager() - : m_pState( new sGroupManagerState ) -{ - LOGD("-- Loading Groups --"); - cIniFile IniFile; - if (!IniFile.ReadFile("groups.ini")) - { - LOGWARNING("groups.ini inaccessible, no groups are defined"); - return; - } - - unsigned int NumKeys = IniFile.GetNumKeys(); - for( unsigned int i = 0; i < NumKeys; i++ ) - { - std::string KeyName = IniFile.GetKeyName( i ); - cGroup* Group = GetGroup( KeyName.c_str() ); - - LOGD("Loading group: %s", KeyName.c_str() ); - - Group->SetName( KeyName ); - char Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; - if( Color != '-' ) - Group->SetColor( cChatColor::MakeColor(Color) ); - else - Group->SetColor( cChatColor::White ); - - std::string Commands = IniFile.GetValue( KeyName, "Commands", "" ); - if( Commands.size() > 0 ) - { - AStringVector Split = StringSplit( Commands, "," ); - for( unsigned int i = 0; i < Split.size(); i++) - { - Group->AddCommand( Split[i] ); - } - } - - std::string Permissions = IniFile.GetValue( KeyName, "Permissions", "" ); - if( Permissions.size() > 0 ) - { - AStringVector Split = StringSplit( Permissions, "," ); - for( unsigned int i = 0; i < Split.size(); i++) - { - Group->AddPermission( Split[i] ); - } - } - - std::string Groups = IniFile.GetValue( KeyName, "Inherits", "" ); - if( Groups.size() > 0 ) - { - AStringVector Split = StringSplit( Groups, "," ); - for( unsigned int i = 0; i < Split.size(); i++) - { - Group->InheritFrom( GetGroup( Split[i].c_str() ) ); - } - } - } - LOGD("-- Groups Successfully Loaded --"); -} - - - - - -cGroup* cGroupManager::GetGroup( const AString & a_Name ) -{ - GroupMap::iterator itr = m_pState->Groups.find( a_Name ); - if( itr != m_pState->Groups.end() ) - { - return itr->second; - } - - cGroup* Group = new cGroup(); - m_pState->Groups[a_Name] = Group; - - return Group; -} - - - - diff --git a/source/GroupManager.h b/source/GroupManager.h deleted file mode 100644 index d911f976c..000000000 --- a/source/GroupManager.h +++ /dev/null @@ -1,30 +0,0 @@ - -#pragma once - - - - - -class cGroup; - - - - - -class cGroupManager -{ -public: - cGroup * GetGroup(const AString & a_Name); - -private: - friend class cRoot; - cGroupManager(); - ~cGroupManager(); - - struct sGroupManagerState; - sGroupManagerState * m_pState; -} ; - - - - diff --git a/source/HTTPServer/EnvelopeParser.cpp b/source/HTTPServer/EnvelopeParser.cpp deleted file mode 100644 index 8dbe05f14..000000000 --- a/source/HTTPServer/EnvelopeParser.cpp +++ /dev/null @@ -1,132 +0,0 @@ - -// EnvelopeParser.cpp - -// Implements the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME - -#include "Globals.h" -#include "EnvelopeParser.h" - - - - - -cEnvelopeParser::cEnvelopeParser(cCallbacks & a_Callbacks) : - m_Callbacks(a_Callbacks), - m_IsInHeaders(true) -{ -} - - - - - -int cEnvelopeParser::Parse(const char * a_Data, int a_Size) -{ - if (!m_IsInHeaders) - { - return 0; - } - - // Start searching 1 char from the end of the already received data, if available: - size_t SearchStart = m_IncomingData.size(); - SearchStart = (SearchStart > 1) ? SearchStart - 1 : 0; - - m_IncomingData.append(a_Data, a_Size); - - size_t idxCRLF = m_IncomingData.find("\r\n", SearchStart); - if (idxCRLF == AString::npos) - { - // Not a complete line yet, all input consumed: - return a_Size; - } - - // Parse as many lines as found: - size_t Last = 0; - do - { - if (idxCRLF == Last) - { - // This was the last line of the data. Finish whatever value has been cached and return: - NotifyLast(); - m_IsInHeaders = false; - return a_Size - (m_IncomingData.size() - idxCRLF) + 2; - } - if (!ParseLine(m_IncomingData.c_str() + Last, idxCRLF - Last)) - { - // An error has occurred - m_IsInHeaders = false; - return -1; - } - Last = idxCRLF + 2; - idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2); - } while (idxCRLF != AString::npos); - m_IncomingData.erase(0, Last); - - // Parsed all lines and still expecting more - return a_Size; -} - - - - - -void cEnvelopeParser::Reset(void) -{ - m_IsInHeaders = true; - m_IncomingData.clear(); - m_LastKey.clear(); - m_LastValue.clear(); -} - - - - - -void cEnvelopeParser::NotifyLast(void) -{ - if (!m_LastKey.empty()) - { - m_Callbacks.OnHeaderLine(m_LastKey, m_LastValue); - m_LastKey.clear(); - } - m_LastValue.clear(); -} - - - - - -bool cEnvelopeParser::ParseLine(const char * a_Data, size_t a_Size) -{ - ASSERT(a_Size > 0); - if (a_Data[0] <= ' ') - { - // This line is a continuation for the previous line - if (m_LastKey.empty()) - { - return false; - } - // Append, including the whitespace in a_Data[0] - m_LastValue.append(a_Data, a_Size); - return true; - } - - // This is a line with a new key: - NotifyLast(); - for (size_t i = 0; i < a_Size; i++) - { - if (a_Data[i] == ':') - { - m_LastKey.assign(a_Data, i); - m_LastValue.assign(a_Data + i + 2, a_Size - i - 2); - return true; - } - } // for i - a_Data[] - - // No colon was found, key-less header?? - return false; -} - - - - diff --git a/source/HTTPServer/EnvelopeParser.h b/source/HTTPServer/EnvelopeParser.h deleted file mode 100644 index 6430fbebf..000000000 --- a/source/HTTPServer/EnvelopeParser.h +++ /dev/null @@ -1,69 +0,0 @@ - -// EnvelopeParser.h - -// Declares the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME - - - - - -#pragma once - - - - - -class cEnvelopeParser -{ -public: - class cCallbacks - { - public: - /// Called when a full header line is parsed - virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) = 0; - } ; - - - cEnvelopeParser(cCallbacks & a_Callbacks); - - /** Parses the incoming data. - Returns the number of bytes consumed from the input. The bytes not consumed are not part of the envelope header - */ - int Parse(const char * a_Data, int a_Size); - - /// Makes the parser forget everything parsed so far, so that it can be reused for parsing another datastream - void Reset(void); - - /// Returns true if more input is expected for the envelope header - bool IsInHeaders(void) const { return m_IsInHeaders; } - - /// Sets the IsInHeaders flag; used by cMultipartParser to simplify the parser initial conditions - void SetIsInHeaders(bool a_IsInHeaders) { m_IsInHeaders = a_IsInHeaders; } - -public: - /// Callbacks to call for the various events - cCallbacks & m_Callbacks; - - /// Set to true while the parser is still parsing the envelope headers. Once set to true, the parser will not consume any more data. - bool m_IsInHeaders; - - /// Buffer for the incoming data until it is parsed - AString m_IncomingData; - - /// Holds the last parsed key; used for line-wrapped values - AString m_LastKey; - - /// Holds the last parsed value; used for line-wrapped values - AString m_LastValue; - - - /// Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them - void NotifyLast(void); - - /// Parses one line of header data. Returns true if successful - bool ParseLine(const char * a_Data, size_t a_Size); -} ; - - - - diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp deleted file mode 100644 index 68afdfc11..000000000 --- a/source/HTTPServer/HTTPConnection.cpp +++ /dev/null @@ -1,247 +0,0 @@ - -// HTTPConnection.cpp - -// Implements the cHTTPConnection class representing a single persistent connection in the HTTP server. - -#include "Globals.h" -#include "HTTPConnection.h" -#include "HTTPMessage.h" -#include "HTTPServer.h" - - - - - -cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) : - m_HTTPServer(a_HTTPServer), - m_State(wcsRecvHeaders), - m_CurrentRequest(NULL) -{ - // LOGD("HTTP: New connection at %p", this); -} - - - - - -cHTTPConnection::~cHTTPConnection() -{ - // LOGD("HTTP: Del connection at %p", this); -} - - - - - -void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) -{ - AppendPrintf(m_OutgoingData, "%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str()); - m_HTTPServer.NotifyConnectionWrite(*this); - m_State = wcsRecvHeaders; -} - - - - - -void cHTTPConnection::SendNeedAuth(const AString & a_Realm) -{ - AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str()); - m_HTTPServer.NotifyConnectionWrite(*this); - m_State = wcsRecvHeaders; -} - - - - - -void cHTTPConnection::Send(const cHTTPResponse & a_Response) -{ - ASSERT(m_State = wcsRecvIdle); - a_Response.AppendToData(m_OutgoingData); - m_State = wcsSendingResp; - m_HTTPServer.NotifyConnectionWrite(*this); -} - - - - - -void cHTTPConnection::Send(const void * a_Data, int a_Size) -{ - ASSERT(m_State == wcsSendingResp); - AppendPrintf(m_OutgoingData, "%x\r\n", a_Size); - m_OutgoingData.append((const char *)a_Data, a_Size); - m_OutgoingData.append("\r\n"); - m_HTTPServer.NotifyConnectionWrite(*this); -} - - - - - -void cHTTPConnection::FinishResponse(void) -{ - ASSERT(m_State == wcsSendingResp); - m_OutgoingData.append("0\r\n\r\n"); - m_State = wcsRecvHeaders; - m_HTTPServer.NotifyConnectionWrite(*this); -} - - - - - -void cHTTPConnection::AwaitNextRequest(void) -{ - switch (m_State) - { - case wcsRecvHeaders: - { - // Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth() ) - break; - } - - case wcsRecvIdle: - { - // The client is waiting for a response, send an "Internal server error": - m_OutgoingData.append("HTTP/1.1 500 Internal Server Error\r\n\r\n"); - m_HTTPServer.NotifyConnectionWrite(*this); - m_State = wcsRecvHeaders; - break; - } - - case wcsSendingResp: - { - // The response headers have been sent, we need to terminate the response body: - m_OutgoingData.append("0\r\n\r\n"); - m_State = wcsRecvHeaders; - break; - } - - default: - { - ASSERT(!"Unhandled state recovery"); - break; - } - } -} - - - - - -void cHTTPConnection::Terminate(void) -{ - if (m_CurrentRequest != NULL) - { - m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); - } - m_HTTPServer.CloseConnection(*this); -} - - - - - -void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) -{ - switch (m_State) - { - case wcsRecvHeaders: - { - if (m_CurrentRequest == NULL) - { - m_CurrentRequest = new cHTTPRequest; - } - - int BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size); - if (BytesConsumed < 0) - { - delete m_CurrentRequest; - m_CurrentRequest = NULL; - m_State = wcsInvalid; - m_HTTPServer.CloseConnection(*this); - return; - } - if (m_CurrentRequest->IsInHeaders()) - { - // The request headers are not yet complete - return; - } - - // The request has finished parsing its headers successfully, notify of it: - m_State = wcsRecvBody; - m_HTTPServer.NewRequest(*this, *m_CurrentRequest); - m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength(); - if (m_CurrentRequestBodyRemaining < 0) - { - // The body length was not specified in the request, assume zero - m_CurrentRequestBodyRemaining = 0; - } - - // Process the rest of the incoming data into the request body: - if (a_Size > BytesConsumed) - { - DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); - } - else - { - DataReceived("", 0); // If the request has zero body length, let it be processed right-away - } - break; - } - - case wcsRecvBody: - { - ASSERT(m_CurrentRequest != NULL); - if (m_CurrentRequestBodyRemaining > 0) - { - int BytesToConsume = std::min(m_CurrentRequestBodyRemaining, a_Size); - m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume); - m_CurrentRequestBodyRemaining -= BytesToConsume; - } - if (m_CurrentRequestBodyRemaining == 0) - { - m_State = wcsRecvIdle; - m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); - delete m_CurrentRequest; - m_CurrentRequest = NULL; - } - break; - } - - default: - { - // TODO: Should we be receiving data in this state? - break; - } - } -} - - - - - -void cHTTPConnection::GetOutgoingData(AString & a_Data) -{ - std::swap(a_Data, m_OutgoingData); -} - - - - - -void cHTTPConnection::SocketClosed(void) -{ - if (m_CurrentRequest != NULL) - { - m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); - } - m_HTTPServer.CloseConnection(*this); -} - - - - - diff --git a/source/HTTPServer/HTTPConnection.h b/source/HTTPServer/HTTPConnection.h deleted file mode 100644 index 14603bb70..000000000 --- a/source/HTTPServer/HTTPConnection.h +++ /dev/null @@ -1,101 +0,0 @@ - -// HTTPConnection.h - -// Declares the cHTTPConnection class representing a single persistent connection in the HTTP server. - - - - - -#pragma once - -#include "../OSSupport/SocketThreads.h" - - - - - -// fwd: -class cHTTPServer; -class cHTTPResponse; -class cHTTPRequest; - - - - - -class cHTTPConnection : - public cSocketThreads::cCallback -{ -public: - - enum eState - { - wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if NULL) - wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid) - wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == NULL) - wcsSendingResp, ///< Sending response body (m_CurrentRequest == NULL) - wcsInvalid, ///< The request was malformed, the connection is closing - } ; - - cHTTPConnection(cHTTPServer & a_HTTPServer); - ~cHTTPConnection(); - - /// Sends HTTP status code together with a_Reason (used for HTTP errors) - void SendStatusAndReason(int a_StatusCode, const AString & a_Reason); - - /// Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm - void SendNeedAuth(const AString & a_Realm); - - /// Sends the headers contained in a_Response - void Send(const cHTTPResponse & a_Response); - - /// Sends the data as the response (may be called multiple times) - void Send(const void * a_Data, int a_Size); - - /// Sends the data as the response (may be called multiple times) - void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); } - - /// Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) - void FinishResponse(void); - - /// Resets the connection for a new request. Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" - void AwaitNextRequest(void); - - /// Terminates the connection; finishes any request being currently processed - void Terminate(void); - -protected: - typedef std::map cNameValueMap; - - /// The parent webserver that is to be notified of events on this connection - cHTTPServer & m_HTTPServer; - - /// All the incoming data until the entire request header is parsed - AString m_IncomingHeaderData; - - /// Status in which the request currently is - eState m_State; - - /// Data that is queued for sending, once the socket becomes writable - AString m_OutgoingData; - - /// The request being currently received (valid only between having parsed the headers and finishing receiving the body) - cHTTPRequest * m_CurrentRequest; - - /// Number of bytes that remain to read for the complete body of the message to be received. Valid only in wcsRecvBody - int m_CurrentRequestBodyRemaining; - - - // cSocketThreads::cCallback overrides: - virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client - virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client - virtual void SocketClosed (void) override; // The socket has been closed for any reason -} ; - -typedef std::vector cHTTPConnections; - - - - - diff --git a/source/HTTPServer/HTTPFormParser.cpp b/source/HTTPServer/HTTPFormParser.cpp deleted file mode 100644 index 596db424e..000000000 --- a/source/HTTPServer/HTTPFormParser.cpp +++ /dev/null @@ -1,290 +0,0 @@ - -// HTTPFormParser.cpp - -// Implements the cHTTPFormParser class representing a parser for forms sent over HTTP - -#include "Globals.h" -#include "HTTPFormParser.h" -#include "HTTPMessage.h" -#include "MultipartParser.h" -#include "NameValueParser.h" - - - - - -cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) : - m_Callbacks(a_Callbacks), - m_IsValid(true) -{ - if (a_Request.GetMethod() == "GET") - { - m_Kind = fpkURL; - - // Directly parse the URL in the request: - const AString & URL = a_Request.GetURL(); - size_t idxQM = URL.find('?'); - if (idxQM != AString::npos) - { - Parse(URL.c_str() + idxQM + 1, URL.size() - idxQM - 1); - } - return; - } - if ((a_Request.GetMethod() == "POST") || (a_Request.GetMethod() == "PUT")) - { - if (strncmp(a_Request.GetContentType().c_str(), "application/x-www-form-urlencoded", 33) == 0) - { - m_Kind = fpkFormUrlEncoded; - return; - } - if (strncmp(a_Request.GetContentType().c_str(), "multipart/form-data", 19) == 0) - { - m_Kind = fpkMultipart; - BeginMultipart(a_Request); - return; - } - } - // Invalid method / content type combination, this is not a HTTP form - m_IsValid = false; -} - - - - - -cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks) : - m_Callbacks(a_Callbacks), - m_Kind(a_Kind), - m_IsValid(true) -{ - Parse(a_Data, a_Size); -} - - - - - -void cHTTPFormParser::Parse(const char * a_Data, int a_Size) -{ - if (!m_IsValid) - { - return; - } - - switch (m_Kind) - { - case fpkURL: - case fpkFormUrlEncoded: - { - // This format is used for smaller forms (not file uploads), so we can delay parsing it until Finish() - m_IncomingData.append(a_Data, a_Size); - break; - } - case fpkMultipart: - { - ASSERT(m_MultipartParser.get() != NULL); - m_MultipartParser->Parse(a_Data, a_Size); - break; - } - default: - { - ASSERT(!"Unhandled form kind"); - break; - } - } -} - - - - - -bool cHTTPFormParser::Finish(void) -{ - switch (m_Kind) - { - case fpkURL: - case fpkFormUrlEncoded: - { - // m_IncomingData has all the form data, parse it now: - ParseFormUrlEncoded(); - break; - } - } - return (m_IsValid && m_IncomingData.empty()); -} - - - - - -bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request) -{ - const AString & ContentType = a_Request.GetContentType(); - return ( - (ContentType == "application/x-www-form-urlencoded") || - (strncmp(ContentType.c_str(), "multipart/form-data", 19) == 0) || - ( - (a_Request.GetMethod() == "GET") && - (a_Request.GetURL().find('?') != AString::npos) - ) - ); - return false; -} - - - - - -void cHTTPFormParser::BeginMultipart(const cHTTPRequest & a_Request) -{ - ASSERT(m_MultipartParser.get() == NULL); - m_MultipartParser.reset(new cMultipartParser(a_Request.GetContentType(), *this)); -} - - - - - -void cHTTPFormParser::ParseFormUrlEncoded(void) -{ - // Parse m_IncomingData for all the variables; no more data is incoming, since this is called from Finish() - // This may not be the most performant version, but we don't care, the form data is small enough and we're not a full-fledged web server anyway - AStringVector Lines = StringSplit(m_IncomingData, "&"); - for (AStringVector::iterator itr = Lines.begin(), end = Lines.end(); itr != end; ++itr) - { - AStringVector Components = StringSplit(*itr, "="); - switch (Components.size()) - { - default: - { - // Neither name nor value, or too many "="s, mark this as invalid form: - m_IsValid = false; - return; - } - case 1: - { - // Only name present - (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = ""; - break; - } - case 2: - { - // name=value format: - (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = URLDecode(ReplaceAllCharOccurrences(Components[1], '+', ' ')); - break; - } - } - } // for itr - Lines[] - m_IncomingData.clear(); -} - - - - - -void cHTTPFormParser::OnPartStart(void) -{ - m_CurrentPartFileName.clear(); - m_CurrentPartName.clear(); - m_IsCurrentPartFile = false; - m_FileHasBeenAnnounced = false; -} - - - - - -void cHTTPFormParser::OnPartHeader(const AString & a_Key, const AString & a_Value) -{ - if (NoCaseCompare(a_Key, "Content-Disposition") == 0) - { - size_t len = a_Value.size(); - size_t ParamsStart = AString::npos; - for (size_t i = 0; i < len; ++i) - { - if (a_Value[i] > ' ') - { - if (strncmp(a_Value.c_str() + i, "form-data", 9) != 0) - { - // Content disposition is not "form-data", mark the whole form invalid - m_IsValid = false; - return; - } - ParamsStart = a_Value.find(';', i + 9); - break; - } - } - if (ParamsStart == AString::npos) - { - // There is data missing in the Content-Disposition field, mark the whole form invalid: - m_IsValid = false; - return; - } - - // Parse the field name and optional filename from this header: - cNameValueParser Parser(a_Value.data() + ParamsStart, a_Value.size() - ParamsStart); - Parser.Finish(); - m_CurrentPartName = Parser["name"]; - if (!Parser.IsValid() || m_CurrentPartName.empty()) - { - // The required parameter "name" is missing, mark the whole form invalid: - m_IsValid = false; - return; - } - m_CurrentPartFileName = Parser["filename"]; - } -} - - - - - -void cHTTPFormParser::OnPartData(const char * a_Data, int a_Size) -{ - if (m_CurrentPartName.empty()) - { - // Prologue, epilogue or invalid part - return; - } - if (m_CurrentPartFileName.empty()) - { - // This is a variable, store it in the map - iterator itr = find(m_CurrentPartName); - if (itr == end()) - { - (*this)[m_CurrentPartName] = AString(a_Data, a_Size); - } - else - { - itr->second.append(a_Data, a_Size); - } - } - else - { - // This is a file, pass it on through the callbacks - if (!m_FileHasBeenAnnounced) - { - m_Callbacks.OnFileStart(*this, m_CurrentPartFileName); - m_FileHasBeenAnnounced = true; - } - m_Callbacks.OnFileData(*this, a_Data, a_Size); - } -} - - - - - -void cHTTPFormParser::OnPartEnd(void) -{ - if (m_FileHasBeenAnnounced) - { - m_Callbacks.OnFileEnd(*this); - } - m_CurrentPartName.clear(); - m_CurrentPartFileName.clear(); -} - - - - diff --git a/source/HTTPServer/HTTPFormParser.h b/source/HTTPServer/HTTPFormParser.h deleted file mode 100644 index a554ca5a4..000000000 --- a/source/HTTPServer/HTTPFormParser.h +++ /dev/null @@ -1,112 +0,0 @@ - -// HTTPFormParser.h - -// Declares the cHTTPFormParser class representing a parser for forms sent over HTTP - - - - -#pragma once - -#include "MultipartParser.h" - - - - - -// fwd: -class cHTTPRequest; - - - - - -class cHTTPFormParser : - public std::map, - public cMultipartParser::cCallbacks -{ -public: - enum eKind - { - fpkURL, ///< The form has been transmitted as parameters to a GET request - fpkFormUrlEncoded, ///< The form has been POSTed or PUT, with Content-Type of "application/x-www-form-urlencoded" - fpkMultipart, ///< The form has been POSTed or PUT, with Content-Type of "multipart/form-data" - } ; - - class cCallbacks - { - public: - /// Called when a new file part is encountered in the form data - virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) = 0; - - /// Called when more file data has come for the current file in the form data - virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) = 0; - - /// Called when the current file part has ended in the form data - virtual void OnFileEnd(cHTTPFormParser & a_Parser) = 0; - } ; - - - /// Creates a parser that is tied to a request and notifies of various events using a callback mechanism - cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks); - - /// Creates a parser with the specified content type that reads data from a string - cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks); - - /// Adds more data into the parser, as the request body is received - void Parse(const char * a_Data, int a_Size); - - /** Notifies that there's no more data incoming and the parser should finish its parsing. - Returns true if parsing successful - */ - bool Finish(void); - - /// Returns true if the headers suggest the request has form data parseable by this class - static bool HasFormData(const cHTTPRequest & a_Request); - -protected: - - /// The callbacks to call for incoming file data - cCallbacks & m_Callbacks; - - /// The kind of the parser (decided in the constructor, used in Parse() - eKind m_Kind; - - /// Buffer for the incoming data until it's parsed - AString m_IncomingData; - - /// True if the information received so far is a valid form; set to false on first problem. Further parsing is skipped when false. - bool m_IsValid; - - /// The parser for the multipart data, if used - std::auto_ptr m_MultipartParser; - - /// Name of the currently parsed part in multipart data - AString m_CurrentPartName; - - /// True if the currently parsed part in multipart data is a file - bool m_IsCurrentPartFile; - - /// Filename of the current parsed part in multipart data (for file uploads) - AString m_CurrentPartFileName; - - /// Set to true after m_Callbacks.OnFileStart() has been called, reset to false on PartEnd - bool m_FileHasBeenAnnounced; - - - /// Sets up the object for parsing a fpkMultipart request - void BeginMultipart(const cHTTPRequest & a_Request); - - /// Parses m_IncomingData as form-urlencoded data (fpkURL or fpkFormUrlEncoded kinds) - void ParseFormUrlEncoded(void); - - // cMultipartParser::cCallbacks overrides: - virtual void OnPartStart (void) override; - virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override; - virtual void OnPartData (const char * a_Data, int a_Size) override; - virtual void OnPartEnd (void) override; -} ; - - - - diff --git a/source/HTTPServer/HTTPMessage.cpp b/source/HTTPServer/HTTPMessage.cpp deleted file mode 100644 index ab23866e6..000000000 --- a/source/HTTPServer/HTTPMessage.cpp +++ /dev/null @@ -1,279 +0,0 @@ - -// HTTPMessage.cpp - -// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes - -#include "Globals.h" -#include "HTTPMessage.h" - - - - - -// Disable MSVC warnings: -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4355) // 'this' : used in base member initializer list -#endif - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cHTTPMessage: - -cHTTPMessage::cHTTPMessage(eKind a_Kind) : - m_Kind(a_Kind), - m_ContentLength(-1) -{ -} - - - - - -void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) -{ - AString Key = a_Key; - StrToLower(Key); - cNameValueMap::iterator itr = m_Headers.find(Key); - if (itr == m_Headers.end()) - { - m_Headers[Key] = a_Value; - } - else - { - // The header-field key is specified multiple times, combine into comma-separated list (RFC 2616 @ 4.2) - itr->second.append(", "); - itr->second.append(a_Value); - } - - // Special processing for well-known headers: - if (Key == "content-type") - { - m_ContentType = m_Headers[Key]; - } - else if (Key == "content-length") - { - m_ContentLength = atoi(m_Headers[Key].c_str()); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cHTTPRequest: - -cHTTPRequest::cHTTPRequest(void) : - super(mkRequest), - m_EnvelopeParser(*this), - m_IsValid(true), - m_UserData(NULL), - m_HasAuth(false) -{ -} - - - - - -int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size) -{ - if (!m_IsValid) - { - return -1; - } - - if (m_Method.empty()) - { - // The first line hasn't been processed yet - int res = ParseRequestLine(a_Data, a_Size); - if ((res < 0) || (res == a_Size)) - { - return res; - } - int res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res); - if (res2 < 0) - { - m_IsValid = false; - return res2; - } - return res2 + res; - } - - if (m_EnvelopeParser.IsInHeaders()) - { - int res = m_EnvelopeParser.Parse(a_Data, a_Size); - if (res < 0) - { - m_IsValid = false; - } - return res; - } - return 0; -} - - - - - -AString cHTTPRequest::GetBareURL(void) const -{ - size_t idxQM = m_URL.find('?'); - if (idxQM != AString::npos) - { - return m_URL.substr(0, idxQM); - } - else - { - return m_URL; - } -} - - - - - -int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size) -{ - m_IncomingHeaderData.append(a_Data, a_Size); - size_t IdxEnd = m_IncomingHeaderData.size(); - - // Ignore the initial CRLFs (HTTP spec's "should") - size_t LineStart = 0; - while ( - (LineStart < IdxEnd) && - ( - (m_IncomingHeaderData[LineStart] == '\r') || - (m_IncomingHeaderData[LineStart] == '\n') - ) - ) - { - LineStart++; - } - if (LineStart >= IdxEnd) - { - m_IsValid = false; - return -1; - } - - int NumSpaces = 0; - size_t MethodEnd = 0; - size_t URLEnd = 0; - for (size_t i = LineStart; i < IdxEnd; i++) - { - switch (m_IncomingHeaderData[i]) - { - case ' ': - { - switch (NumSpaces) - { - case 0: - { - MethodEnd = i; - break; - } - case 1: - { - URLEnd = i; - break; - } - default: - { - // Too many spaces in the request - m_IsValid = false; - return -1; - } - } - NumSpaces += 1; - break; - } - case '\n': - { - if ((i == 0) || (m_IncomingHeaderData[i - 1] != '\r') || (NumSpaces != 2) || (i < URLEnd + 7)) - { - // LF too early, without a CR, without two preceeding spaces or too soon after the second space - m_IsValid = false; - return -1; - } - // Check that there's HTTP/version at the end - if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0) - { - m_IsValid = false; - return -1; - } - m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart); - m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1); - return i + 1; - } - } // switch (m_IncomingHeaderData[i]) - } // for i - m_IncomingHeaderData[] - - // CRLF hasn't been encountered yet, consider all data consumed - return a_Size; -} - - - - - -void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value) -{ - if ( - (NoCaseCompare(a_Key, "Authorization") == 0) && - (strncmp(a_Value.c_str(), "Basic ", 6) == 0) - ) - { - AString UserPass = Base64Decode(a_Value.substr(6)); - size_t idxCol = UserPass.find(':'); - if (idxCol != AString::npos) - { - m_AuthUsername = UserPass.substr(0, idxCol); - m_AuthPassword = UserPass.substr(idxCol + 1); - m_HasAuth = true; - } - } - AddHeader(a_Key, a_Value); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cHTTPResponse: - -cHTTPResponse::cHTTPResponse(void) : - super(mkResponse) -{ -} - - - - - -void cHTTPResponse::AppendToData(AString & a_DataStream) const -{ - a_DataStream.append("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: "); - a_DataStream.append(m_ContentType); - a_DataStream.append("\r\n"); - for (cNameValueMap::const_iterator itr = m_Headers.begin(), end = m_Headers.end(); itr != end; ++itr) - { - if ((itr->first == "Content-Type") || (itr->first == "Content-Length")) - { - continue; - } - a_DataStream.append(itr->first); - a_DataStream.append(": "); - a_DataStream.append(itr->second); - a_DataStream.append("\r\n"); - } // for itr - m_Headers[] - a_DataStream.append("\r\n"); -} - - - - diff --git a/source/HTTPServer/HTTPMessage.h b/source/HTTPServer/HTTPMessage.h deleted file mode 100644 index f5284c535..000000000 --- a/source/HTTPServer/HTTPMessage.h +++ /dev/null @@ -1,164 +0,0 @@ - -// HTTPMessage.h - -// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes - - - - - -#pragma once - -#include "EnvelopeParser.h" - - - - - -class cHTTPMessage -{ -public: - enum - { - HTTP_OK = 200, - HTTP_BAD_REQUEST = 400, - } ; - - enum eKind - { - mkRequest, - mkResponse, - } ; - - cHTTPMessage(eKind a_Kind); - - /// Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length - void AddHeader(const AString & a_Key, const AString & a_Value); - - void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; } - void SetContentLength(int a_ContentLength) { m_ContentLength = a_ContentLength; } - - const AString & GetContentType (void) const { return m_ContentType; } - int GetContentLength(void) const { return m_ContentLength; } - -protected: - typedef std::map cNameValueMap; - - eKind m_Kind; - - cNameValueMap m_Headers; - - /// Type of the content; parsed by AddHeader(), set directly by SetContentLength() - AString m_ContentType; - - /// Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength() - int m_ContentLength; -} ; - - - - - -class cHTTPRequest : - public cHTTPMessage, - protected cEnvelopeParser::cCallbacks -{ - typedef cHTTPMessage super; - -public: - cHTTPRequest(void); - - /** Parses the request line and then headers from the received data. - Returns the number of bytes consumed or a negative number for error - */ - int ParseHeaders(const char * a_Data, int a_Size); - - /// Returns true if the request did contain a Content-Length header - bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); } - - /// Returns the method used in the request - const AString & GetMethod(void) const { return m_Method; } - - /// Returns the URL used in the request - const AString & GetURL(void) const { return m_URL; } - - /// Returns the URL used in the request, without any parameters - AString GetBareURL(void) const; - - /// Sets the UserData pointer that is stored within this request. The request doesn't touch this data (doesn't delete it)! - void SetUserData(void * a_UserData) { m_UserData = a_UserData; } - - /// Retrieves the UserData pointer that has been stored within this request. - void * GetUserData(void) const { return m_UserData; } - - /// Returns true if more data is expected for the request headers - bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); } - - /// Returns true if the request did present auth data that was understood by the parser - bool HasAuth(void) const { return m_HasAuth; } - - /// Returns the username that the request presented. Only valid if HasAuth() is true - const AString & GetAuthUsername(void) const { return m_AuthUsername; } - - /// Returns the password that the request presented. Only valid if HasAuth() is true - const AString & GetAuthPassword(void) const { return m_AuthPassword; } - -protected: - /// Parser for the envelope data - cEnvelopeParser m_EnvelopeParser; - - /// True if the data received so far is parsed successfully. When false, all further parsing is skipped - bool m_IsValid; - - /// Bufferred incoming data, while parsing for the request line - AString m_IncomingHeaderData; - - /// Method of the request (GET / PUT / POST / ...) - AString m_Method; - - /// Full URL of the request - AString m_URL; - - /// Data that the HTTPServer callbacks are allowed to store. - void * m_UserData; - - /// Set to true if the request contains auth data that was understood by the parser - bool m_HasAuth; - - /// The username used for auth - AString m_AuthUsername; - - /// The password used for auth - AString m_AuthPassword; - - - /** Parses the incoming data for the first line (RequestLine) - Returns the number of bytes consumed, or -1 for an error - */ - int ParseRequestLine(const char * a_Data, int a_Size); - - // cEnvelopeParser::cCallbacks overrides: - virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override; -} ; - - - - - -class cHTTPResponse : - public cHTTPMessage -{ - typedef cHTTPMessage super; - -public: - cHTTPResponse(void); - - /** Appends the response to the specified datastream - response line and headers. - The body will be sent later directly through cConnection::Send() - */ - void AppendToData(AString & a_DataStream) const; -} ; - - - - diff --git a/source/HTTPServer/HTTPServer.cpp b/source/HTTPServer/HTTPServer.cpp deleted file mode 100644 index f6f5b0f8b..000000000 --- a/source/HTTPServer/HTTPServer.cpp +++ /dev/null @@ -1,258 +0,0 @@ - -// HTTPServer.cpp - -// Implements the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing - -#include "Globals.h" -#include "HTTPServer.h" -#include "HTTPMessage.h" -#include "HTTPConnection.h" -#include "HTTPFormParser.h" - - - - - -// Disable MSVC warnings: -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4355) // 'this' : used in base member initializer list -#endif - - - - - -class cDebugCallbacks : - public cHTTPServer::cCallbacks, - protected cHTTPFormParser::cCallbacks -{ - virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override - { - if (cHTTPFormParser::HasFormData(a_Request)) - { - a_Request.SetUserData(new cHTTPFormParser(a_Request, *this)); - } - } - - - virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override - { - cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData()); - if (FormParser != NULL) - { - FormParser->Parse(a_Data, a_Size); - } - } - - - virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override - { - cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData()); - if (FormParser != NULL) - { - if (FormParser->Finish()) - { - cHTTPResponse Resp; - Resp.SetContentType("text/html"); - a_Connection.Send(Resp); - a_Connection.Send("\r\n"); - for (cHTTPFormParser::iterator itr = FormParser->begin(), end = FormParser->end(); itr != end; ++itr) - { - a_Connection.Send(Printf("\r\n", itr->first.c_str(), itr->second.c_str())); - } // for itr - FormParser[] - a_Connection.Send("
NameValue
%s
%s
"); - return; - } - - // Parsing failed: - cHTTPResponse Resp; - Resp.SetContentType("text/plain"); - a_Connection.Send(Resp); - a_Connection.Send("Form parsing failed"); - return; - } - - // Test the auth failure and success: - if (a_Request.GetURL() == "/auth") - { - if (!a_Request.HasAuth() || (a_Request.GetAuthUsername() != "a") || (a_Request.GetAuthPassword() != "b")) - { - a_Connection.SendNeedAuth("MCServer WebAdmin"); - return; - } - } - - cHTTPResponse Resp; - Resp.SetContentType("text/plain"); - a_Connection.Send(Resp); - a_Connection.Send("Hello, world"); - } - - - virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override - { - // TODO - } - - - virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override - { - // TODO - } - - - virtual void OnFileEnd(cHTTPFormParser & a_Parser) override - { - // TODO - } - -} g_DebugCallbacks; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cHTTPServer: - -cHTTPServer::cHTTPServer(void) : - m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer IPv4"), - m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"), - m_Callbacks(NULL) -{ -} - - - - - -cHTTPServer::~cHTTPServer() -{ - Stop(); -} - - - - - -bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6) -{ - bool HasAnyPort; - HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4); - HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort; - if (!HasAnyPort) - { - return false; - } - - return true; -} - - - - - -bool cHTTPServer::Start(cCallbacks & a_Callbacks) -{ - m_Callbacks = &a_Callbacks; - if (!m_ListenThreadIPv4.Start()) - { - return false; - } - if (!m_ListenThreadIPv6.Start()) - { - m_ListenThreadIPv4.Stop(); - return false; - } - return true; -} - - - - - -void cHTTPServer::Stop(void) -{ - m_ListenThreadIPv4.Stop(); - m_ListenThreadIPv6.Stop(); - - // Drop all current connections: - cCSLock Lock(m_CSConnections); - while (!m_Connections.empty()) - { - m_Connections.front()->Terminate(); - } // for itr - m_Connections[] -} - - - - - -void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket) -{ - cHTTPConnection * Connection = new cHTTPConnection(*this); - m_SocketThreads.AddClient(a_Socket, Connection); - cCSLock Lock(m_CSConnections); - m_Connections.push_back(Connection); -} - - - - - -void cHTTPServer::CloseConnection(cHTTPConnection & a_Connection) -{ - m_SocketThreads.RemoveClient(&a_Connection); - cCSLock Lock(m_CSConnections); - for (cHTTPConnections::iterator itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) - { - if (*itr == &a_Connection) - { - m_Connections.erase(itr); - break; - } - } - delete &a_Connection; -} - - - - - -void cHTTPServer::NotifyConnectionWrite(cHTTPConnection & a_Connection) -{ - m_SocketThreads.NotifyWrite(&a_Connection); -} - - - - - -void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) -{ - m_Callbacks->OnRequestBegun(a_Connection, a_Request); -} - - - - - -void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) -{ - m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size); -} - - - - - -void cHTTPServer::RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) -{ - m_Callbacks->OnRequestFinished(a_Connection, a_Request); - a_Connection.AwaitNextRequest(); -} - - - - diff --git a/source/HTTPServer/HTTPServer.h b/source/HTTPServer/HTTPServer.h deleted file mode 100644 index fea2a9029..000000000 --- a/source/HTTPServer/HTTPServer.h +++ /dev/null @@ -1,101 +0,0 @@ - -// HTTPServer.h - -// Declares the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing - - - - - -#pragma once - -#include "../OSSupport/ListenThread.h" -#include "../OSSupport/SocketThreads.h" -#include "../../iniFile/iniFile.h" - - - - - -// fwd: -class cHTTPMessage; -class cHTTPRequest; -class cHTTPResponse; -class cHTTPConnection; - -typedef std::vector cHTTPConnections; - - - - - - -class cHTTPServer : - public cListenThread::cCallback -{ -public: - class cCallbacks - { - public: - /** Called when a new request arrives over a connection and its headers have been parsed. - The request body needn't have arrived yet. - */ - virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; - - /// Called when another part of request body has arrived. - virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) = 0; - - /// Called when the request body has been fully received in previous calls to OnRequestBody() - virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; - } ; - - cHTTPServer(void); - ~cHTTPServer(); - - /// Initializes the server on the specified ports - bool Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6); - - /// Starts the server and assigns the callbacks to use for incoming requests - bool Start(cCallbacks & a_Callbacks); - - /// Stops the server, drops all current connections - void Stop(void); - -protected: - friend class cHTTPConnection; - - cListenThread m_ListenThreadIPv4; - cListenThread m_ListenThreadIPv6; - - cSocketThreads m_SocketThreads; - - cCriticalSection m_CSConnections; - cHTTPConnections m_Connections; ///< All the connections that are currently being serviced - - /// The callbacks to call for various events - cCallbacks * m_Callbacks; - - - // cListenThread::cCallback overrides: - virtual void OnConnectionAccepted(cSocket & a_Socket) override; - - /// Called by cHTTPConnection to close the connection (presumably due to an error) - void CloseConnection(cHTTPConnection & a_Connection); - - /// Called by cHTTPConnection to notify SocketThreads that there's data to be sent for the connection - void NotifyConnectionWrite(cHTTPConnection & a_Connection); - - /// Called by cHTTPConnection when it finishes parsing the request header - void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); - - /// Called by cHTTPConenction when it receives more data for the request body - void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size); - - /// Called by cHTTPConnection when it detects that the request has finished (all of its body has been received) - void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); -} ; - - - - - diff --git a/source/HTTPServer/MultipartParser.cpp b/source/HTTPServer/MultipartParser.cpp deleted file mode 100644 index b49f6ec07..000000000 --- a/source/HTTPServer/MultipartParser.cpp +++ /dev/null @@ -1,256 +0,0 @@ - -// MultipartParser.cpp - -// Implements the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts - -#include "Globals.h" -#include "MultipartParser.h" -#include "NameValueParser.h" - - - - - -// Disable MSVC warnings: -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4355) // 'this' : used in base member initializer list -#endif - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// self-test: - -#if 0 - -class cMultipartParserTest : - public cMultipartParser::cCallbacks -{ -public: - cMultipartParserTest(void) - { - cMultipartParser Parser("multipart/mixed; boundary=\"MyBoundaryString\"; foo=bar", *this); - const char Data[] = -"ThisIsIgnoredPrologue\r\n\ ---MyBoundaryString\r\n\ -\r\n\ -Body with confusing strings\r\n\ ---NotABoundary\r\n\ ---MyBoundaryStringWithPostfix\r\n\ ---\r\n\ ---MyBoundaryString\r\n\ -content-disposition: inline\r\n\ -\r\n\ -This is body\r\n\ ---MyBoundaryString\r\n\ -\r\n\ -Headerless body with trailing CRLF\r\n\ -\r\n\ ---MyBoundaryString--\r\n\ -ThisIsIgnoredEpilogue"; - printf("Multipart parsing test commencing.\n"); - Parser.Parse(Data, sizeof(Data) - 1); - // DEBUG: Check if the onscreen output corresponds with the data above - printf("Multipart parsing test finished\n"); - } - - virtual void OnPartStart(void) override - { - printf("Starting a new part\n"); - } - - - virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override - { - printf(" Hdr: \"%s\"=\"%s\"\n", a_Key.c_str(), a_Value.c_str()); - } - - - virtual void OnPartData(const char * a_Data, int a_Size) override - { - printf(" Data: %d bytes, \"%.*s\"\n", a_Size, a_Size, a_Data); - } - - - virtual void OnPartEnd(void) override - { - printf("Part end\n"); - } -} g_Test; - -#endif - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMultipartParser: - - -cMultipartParser::cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks) : - m_Callbacks(a_Callbacks), - m_IsValid(true), - m_EnvelopeParser(*this), - m_HasHadData(false) -{ - static AString s_Multipart = "multipart/"; - - // Check that the content type is multipart: - AString ContentType(a_ContentType); - if (strncmp(ContentType.c_str(), "multipart/", 10) != 0) - { - m_IsValid = false; - return; - } - size_t idxSC = ContentType.find(';', 10); - if (idxSC == AString::npos) - { - m_IsValid = false; - return; - } - - // Find the multipart boundary: - ContentType.erase(0, idxSC + 1); - cNameValueParser CTParser(ContentType.c_str(), ContentType.size()); - CTParser.Finish(); - if (!CTParser.IsValid()) - { - m_IsValid = false; - return; - } - m_Boundary = CTParser["boundary"]; - m_IsValid = !m_Boundary.empty(); - if (!m_IsValid) - { - return; - } - - // Set the envelope parser for parsing the body, so that our Parse() function parses the ignored prefix data as a body - m_EnvelopeParser.SetIsInHeaders(false); - - // Append an initial CRLF to the incoming data, so that a body starting with the boundary line will get caught - m_IncomingData.assign("\r\n"); - - /* - m_Boundary = AString("\r\n--") + m_Boundary - m_BoundaryEnd = m_Boundary + "--\r\n"; - m_Boundary = m_Boundary + "\r\n"; - */ -} - - - - - -void cMultipartParser::Parse(const char * a_Data, int a_Size) -{ - // Skip parsing if invalid - if (!m_IsValid) - { - return; - } - - // Append to buffer, then parse it: - m_IncomingData.append(a_Data, a_Size); - while (true) - { - if (m_EnvelopeParser.IsInHeaders()) - { - int BytesConsumed = m_EnvelopeParser.Parse(m_IncomingData.data(), m_IncomingData.size()); - if (BytesConsumed < 0) - { - m_IsValid = false; - return; - } - if ((BytesConsumed == a_Size) && m_EnvelopeParser.IsInHeaders()) - { - // All the incoming data has been consumed and still waiting for more - return; - } - m_IncomingData.erase(0, BytesConsumed); - } - - // Search for boundary / boundary end: - size_t idxBoundary = m_IncomingData.find("\r\n--"); - if (idxBoundary == AString::npos) - { - // Boundary string start not present, present as much data to the part callback as possible - if (m_IncomingData.size() > m_Boundary.size() + 8) - { - size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8; - m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport); - m_IncomingData.erase(0, BytesToReport); - } - return; - } - if (idxBoundary > 0) - { - m_Callbacks.OnPartData(m_IncomingData.data(), idxBoundary); - m_IncomingData.erase(0, idxBoundary); - } - idxBoundary = 4; - size_t LineEnd = m_IncomingData.find("\r\n", idxBoundary); - if (LineEnd == AString::npos) - { - // Not a complete line yet, present as much data to the part callback as possible - if (m_IncomingData.size() > m_Boundary.size() + 8) - { - size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8; - m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport); - m_IncomingData.erase(0, BytesToReport); - } - return; - } - if ( - (LineEnd - idxBoundary != m_Boundary.size()) && // Line length not equal to boundary - (LineEnd - idxBoundary != m_Boundary.size() + 2) // Line length not equal to boundary end - ) - { - // Got a line, but it's not a boundary, report it as data: - m_Callbacks.OnPartData(m_IncomingData.data(), LineEnd); - m_IncomingData.erase(0, LineEnd); - continue; - } - - if (strncmp(m_IncomingData.c_str() + idxBoundary, m_Boundary.c_str(), m_Boundary.size()) == 0) - { - // Boundary or BoundaryEnd found: - m_Callbacks.OnPartEnd(); - size_t idxSlash = idxBoundary + m_Boundary.size(); - if ((m_IncomingData[idxSlash] == '-') && (m_IncomingData[idxSlash + 1] == '-')) - { - // This was the last part - m_Callbacks.OnPartData(m_IncomingData.data() + idxSlash + 4, m_IncomingData.size() - idxSlash - 4); - m_IncomingData.clear(); - return; - } - m_Callbacks.OnPartStart(); - m_IncomingData.erase(0, LineEnd + 2); - - // Keep parsing for the headers that may have come with this data: - m_EnvelopeParser.Reset(); - continue; - } - - // It's a line, but not a boundary. It can be fully sent to the data receiver, since a boundary cannot cross lines - m_Callbacks.OnPartData(m_IncomingData.c_str(), LineEnd); - m_IncomingData.erase(0, LineEnd); - } // while (true) -} - - - - - -void cMultipartParser::OnHeaderLine(const AString & a_Key, const AString & a_Value) -{ - m_Callbacks.OnPartHeader(a_Key, a_Value); -} - - - - diff --git a/source/HTTPServer/MultipartParser.h b/source/HTTPServer/MultipartParser.h deleted file mode 100644 index d853929ed..000000000 --- a/source/HTTPServer/MultipartParser.h +++ /dev/null @@ -1,76 +0,0 @@ - -// MultipartParser.h - -// Declares the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts - - - - - -#pragma once - -#include "EnvelopeParser.h" - - - - - -class cMultipartParser : - protected cEnvelopeParser::cCallbacks -{ -public: - class cCallbacks - { - public: - /// Called when a new part starts - virtual void OnPartStart(void) = 0; - - /// Called when a complete header line is received for a part - virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) = 0; - - /// Called when body for a part is received - virtual void OnPartData(const char * a_Data, int a_Size) = 0; - - /// Called when the current part ends - virtual void OnPartEnd(void) = 0; - } ; - - /// Creates the parser, expects to find the boundary in a_ContentType - cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks); - - /// Parses more incoming data - void Parse(const char * a_Data, int a_Size); - -protected: - /// The callbacks to call for various parsing events - cCallbacks & m_Callbacks; - - /// True if the data parsed so far is valid; if false, further parsing is skipped - bool m_IsValid; - - /// Parser for each part's envelope - cEnvelopeParser m_EnvelopeParser; - - /// Buffer for the incoming data until it is parsed - AString m_IncomingData; - - /// The boundary, excluding both the initial "--" and the terminating CRLF - AString m_Boundary; - - /// Set to true if some data for the current part has already been signalized to m_Callbacks. Used for proper CRLF inserting. - bool m_HasHadData; - - - /// Parse one line of incoming data. The CRLF has already been stripped from a_Data / a_Size - void ParseLine(const char * a_Data, int a_Size); - - /// Parse one line of incoming data in the headers section of a part. The CRLF has already been stripped from a_Data / a_Size - void ParseHeaderLine(const char * a_Data, int a_Size); - - // cEnvelopeParser overrides: - virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override; -} ; - - - - diff --git a/source/HTTPServer/NameValueParser.cpp b/source/HTTPServer/NameValueParser.cpp deleted file mode 100644 index a27f07d19..000000000 --- a/source/HTTPServer/NameValueParser.cpp +++ /dev/null @@ -1,412 +0,0 @@ - -// NameValueParser.cpp - -// Implements the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap - -#include "Globals.h" -#include "NameValueParser.h" - - - - - - -// DEBUG: Self-test - -#if 0 - -class cNameValueParserTest -{ -public: - cNameValueParserTest(void) - { - const char Data[] = " Name1=Value1;Name2 = Value 2; Name3 =\"Value 3\"; Name4 =\'Value 4\'; Name5=\"Confusing; isn\'t it?\""; - - // Now try parsing char-by-char, to debug transitions across datachunk boundaries: - cNameValueParser Parser2; - for (int i = 0; i < sizeof(Data) - 1; i++) - { - Parser2.Parse(Data + i, 1); - } - Parser2.Finish(); - - // Parse as a single chunk of data: - cNameValueParser Parser(Data, sizeof(Data) - 1); - - // Use the debugger to inspect the Parser variable - - // Check that the two parsers have the same content: - for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr) - { - ASSERT(Parser2[itr->first] == itr->second); - } // for itr - Parser[] - - // Try parsing in 2-char chunks: - cNameValueParser Parser3; - for (int i = 0; i < sizeof(Data) - 2; i += 2) - { - Parser3.Parse(Data + i, 2); - } - if ((sizeof(Data) % 2) == 0) // There are even number of chars, including the NUL, so the data has an odd length. Parse one more char - { - Parser3.Parse(Data + sizeof(Data) - 2, 1); - } - Parser3.Finish(); - - // Check that the third parser has the same content: - for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr) - { - ASSERT(Parser3[itr->first] == itr->second); - } // for itr - Parser[] - - printf("cNameValueParserTest done"); - } -} g_Test; - -#endif - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cNameValueParser: - -cNameValueParser::cNameValueParser(bool a_AllowsKeyOnly) : - m_State(psKeySpace), - m_AllowsKeyOnly(a_AllowsKeyOnly) -{ -} - - - - - -cNameValueParser::cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly) : - m_State(psKeySpace), - m_AllowsKeyOnly(a_AllowsKeyOnly) -{ - Parse(a_Data, a_Size); -} - - - - - -void cNameValueParser::Parse(const char * a_Data, int a_Size) -{ - ASSERT(m_State != psFinished); // Calling Parse() after Finish() is wrong! - - if ((m_State == psInvalid) || (m_State == psFinished)) - { - return; - } - int Last = 0; - for (int i = 0; i < a_Size;) - { - switch (m_State) - { - case psKeySpace: - { - // Skip whitespace until a non-whitespace is found, then start the key: - while ((i < a_Size) && (a_Data[i] <= ' ')) - { - i++; - } - if ((i < a_Size) && (a_Data[i] > ' ')) - { - m_State = psKey; - Last = i; - } - break; - } - - case psKey: - { - // Read the key until whitespace or an equal sign: - while (i < a_Size) - { - if (a_Data[i] == '=') - { - m_CurrentKey.append(a_Data + Last, i - Last); - i++; - Last = i; - m_State = psEqual; - break; - } - else if (a_Data[i] <= ' ') - { - m_CurrentKey.append(a_Data + Last, i - Last); - i++; - Last = i; - m_State = psEqualSpace; - break; - } - else if (a_Data[i] == ';') - { - if (!m_AllowsKeyOnly) - { - m_State = psInvalid; - return; - } - m_CurrentKey.append(a_Data + Last, i - Last); - i++; - Last = i; - (*this)[m_CurrentKey] = ""; - m_CurrentKey.clear(); - m_State = psKeySpace; - break; - } - else if ((a_Data[i] == '\"') || (a_Data[i] == '\'')) - { - m_State = psInvalid; - return; - } - i++; - } // while (i < a_Size) - if (i == a_Size) - { - // Still the key, ran out of data to parse, store the part of the key parsed so far: - m_CurrentKey.append(a_Data + Last, a_Size - Last); - return; - } - break; - } - - case psEqualSpace: - { - // The space before the expected equal sign; the current key is already assigned - while (i < a_Size) - { - if (a_Data[i] == '=') - { - m_State = psEqual; - i++; - Last = i; - break; - } - else if (a_Data[i] == ';') - { - // Key-only - if (!m_AllowsKeyOnly) - { - m_State = psInvalid; - return; - } - i++; - Last = i; - (*this)[m_CurrentKey] = ""; - m_CurrentKey.clear(); - m_State = psKeySpace; - break; - } - else if (a_Data[i] > ' ') - { - m_State = psInvalid; - return; - } - i++; - } // while (i < a_Size) - break; - } // case psEqualSpace - - case psEqual: - { - // just parsed the equal-sign - while (i < a_Size) - { - if (a_Data[i] == ';') - { - if (!m_AllowsKeyOnly) - { - m_State = psInvalid; - return; - } - i++; - Last = i; - (*this)[m_CurrentKey] = ""; - m_CurrentKey.clear(); - m_State = psKeySpace; - break; - } - else if (a_Data[i] == '\"') - { - i++; - Last = i; - m_State = psValueInDQuotes; - break; - } - else if (a_Data[i] == '\'') - { - i++; - Last = i; - m_State = psValueInSQuotes; - break; - } - else - { - m_CurrentValue.push_back(a_Data[i]); - i++; - Last = i; - m_State = psValueRaw; - break; - } - i++; - } // while (i < a_Size) - break; - } // case psEqual - - case psValueInDQuotes: - { - while (i < a_Size) - { - if (a_Data[i] == '\"') - { - m_CurrentValue.append(a_Data + Last, i - Last); - (*this)[m_CurrentKey] = m_CurrentValue; - m_CurrentKey.clear(); - m_CurrentValue.clear(); - m_State = psAfterValue; - i++; - Last = i; - break; - } - i++; - } // while (i < a_Size) - if (i == a_Size) - { - m_CurrentValue.append(a_Data + Last, a_Size - Last); - } - break; - } // case psValueInDQuotes - - case psValueInSQuotes: - { - while (i < a_Size) - { - if (a_Data[i] == '\'') - { - m_CurrentValue.append(a_Data + Last, i - Last); - (*this)[m_CurrentKey] = m_CurrentValue; - m_CurrentKey.clear(); - m_CurrentValue.clear(); - m_State = psAfterValue; - i++; - Last = i; - break; - } - i++; - } // while (i < a_Size) - if (i == a_Size) - { - m_CurrentValue.append(a_Data + Last, a_Size - Last); - } - break; - } // case psValueInSQuotes - - case psValueRaw: - { - while (i < a_Size) - { - if (a_Data[i] == ';') - { - m_CurrentValue.append(a_Data + Last, i - Last); - (*this)[m_CurrentKey] = m_CurrentValue; - m_CurrentKey.clear(); - m_CurrentValue.clear(); - m_State = psKeySpace; - i++; - Last = i; - break; - } - i++; - } - if (i == a_Size) - { - m_CurrentValue.append(a_Data + Last, a_Size - Last); - } - break; - } // case psValueRaw - - case psAfterValue: - { - // Between the closing DQuote or SQuote and the terminating semicolon - while (i < a_Size) - { - if (a_Data[i] == ';') - { - m_State = psKeySpace; - i++; - Last = i; - break; - } - else if (a_Data[i] < ' ') - { - i++; - continue; - } - m_State = psInvalid; - return; - } // while (i < a_Size) - break; - } - } // switch (m_State) - } // for i - a_Data[] -} - - - - - -bool cNameValueParser::Finish(void) -{ - switch (m_State) - { - case psInvalid: - { - return false; - } - case psFinished: - { - return true; - } - case psKey: - case psEqualSpace: - case psEqual: - { - if ((m_AllowsKeyOnly) && !m_CurrentKey.empty()) - { - (*this)[m_CurrentKey] = ""; - m_State = psFinished; - return true; - } - m_State = psInvalid; - return false; - } - case psValueRaw: - { - (*this)[m_CurrentKey] = m_CurrentValue; - m_State = psFinished; - return true; - } - case psValueInDQuotes: - case psValueInSQuotes: - { - // Missing the terminating quotes, this is an error - m_State = psInvalid; - return false; - } - case psKeySpace: - case psAfterValue: - { - m_State = psFinished; - return true; - } - } - ASSERT(!"Unhandled parser state!"); - return false; -} - - - - diff --git a/source/HTTPServer/NameValueParser.h b/source/HTTPServer/NameValueParser.h deleted file mode 100644 index 07dc0b942..000000000 --- a/source/HTTPServer/NameValueParser.h +++ /dev/null @@ -1,70 +0,0 @@ - -// NameValueParser.h - -// Declares the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap - - - - - -#pragma once - - - - - -class cNameValueParser : - public std::map -{ -public: - /// Creates an empty parser - cNameValueParser(bool a_AllowsKeyOnly = true); - - /// Creates an empty parser, then parses the data given. Doesn't call Finish(), so more data can be parsed later - cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly = true); - - /// Parses the data given - void Parse(const char * a_Data, int a_Size); - - /// Notifies the parser that no more data will be coming. Returns true if the parser state is valid - bool Finish(void); - - /// Returns true if the data parsed so far was valid - bool IsValid(void) const { return (m_State != psInvalid); } - - /// Returns true if the parser expects no more data - bool IsFinished(void) const { return ((m_State == psInvalid) || (m_State == psFinished)); } - -protected: - enum eState - { - psKeySpace, ///< Parsing the space in front of the next key - psKey, ///< Currently adding more chars to the key in m_CurrentKey - psEqualSpace, ///< Space after m_CurrentKey - psEqual, ///< Just parsed the = sign after a name - psValueInSQuotes, ///< Just parsed a Single-quote sign after the Equal sign - psValueInDQuotes, ///< Just parsed a Double-quote sign after the Equal sign - psValueRaw, ///< Just parsed a raw value without a quote - psAfterValue, ///< Just finished parsing the value, waiting for semicolon or data end - psInvalid, ///< The parser has encountered an invalid input; further parsing is skipped - psFinished, ///< The parser has already been instructed to finish and doesn't expect any more data - } ; - - /// The current state of the parser - eState m_State; - - /// If true, the parser will accept keys without an equal sign and the value - bool m_AllowsKeyOnly; - - /// Buffer for the current Key - AString m_CurrentKey; - - /// Buffer for the current Value; - AString m_CurrentValue; - - -} ; - - - - diff --git a/source/Inventory.cpp b/source/Inventory.cpp deleted file mode 100644 index 90b998358..000000000 --- a/source/Inventory.cpp +++ /dev/null @@ -1,682 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Inventory.h" -#include "Entities/Player.h" -#include "ClientHandle.h" -#include "UI/Window.h" -#include "Item.h" -#include "Root.h" -#include "World.h" - -#include - -#include "Items/ItemHandler.h" - - - - - -cInventory::cInventory(cPlayer & a_Owner) : - m_ArmorSlots (1, 4), // 1 x 4 slots - m_InventorySlots(9, 3), // 9 x 3 slots - m_HotbarSlots (9, 1), // 9 x 1 slots - m_Owner(a_Owner) -{ - // Ask each ItemGrid to report changes to us: - m_ArmorSlots.AddListener(*this); - m_InventorySlots.AddListener(*this); - m_HotbarSlots.AddListener(*this); - - SetEquippedSlotNum(0); -} - - - - - -void cInventory::Clear(void) -{ - m_ArmorSlots.Clear(); - m_InventorySlots.Clear(); - m_HotbarSlots.Clear(); -} - - - - - -int cInventory::HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots) -{ - return HowManyCanFit(a_ItemStack, 0, invNumSlots - 1, a_ConsiderEmptySlots); -} - - - - - -int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots) -{ - if ((a_BeginSlotNum < 0) || (a_BeginSlotNum >= invNumSlots)) - { - LOGWARNING("%s: Bad BeginSlotNum, got %d, there are %d slots; correcting to 0.", __FUNCTION__, a_BeginSlotNum, invNumSlots - 1); - a_BeginSlotNum = 0; - } - if ((a_EndSlotNum < 0) || (a_EndSlotNum >= invNumSlots)) - { - LOGWARNING("%s: Bad EndSlotNum, got %d, there are %d slots; correcting to %d.", __FUNCTION__, a_BeginSlotNum, invNumSlots, invNumSlots - 1); - a_EndSlotNum = invNumSlots - 1; - } - if (a_BeginSlotNum > a_EndSlotNum) - { - std::swap(a_BeginSlotNum, a_EndSlotNum); - } - - char NumLeft = a_ItemStack.m_ItemCount; - int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); - for (int i = a_BeginSlotNum; i <= a_EndSlotNum; i++) - { - const cItem & Slot = GetSlot(i); - if (Slot.IsEmpty()) - { - NumLeft -= MaxStack; - } - else if (Slot.IsStackableWith(a_ItemStack)) - { - NumLeft -= MaxStack - Slot.m_ItemCount; - } - if (NumLeft <= 0) - { - // All items fit - return a_ItemStack.m_ItemCount; - } - } // for i - m_Slots[] - return a_ItemStack.m_ItemCount - NumLeft; -} - - - - - -int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst) -{ - cItem ToAdd(a_Item); - int res = 0; - if (ItemCategory::IsArmor(a_Item.m_ItemType)) - { - res = m_ArmorSlots.AddItem(ToAdd, a_AllowNewStacks); - ToAdd.m_ItemCount -= res; - if (ToAdd.m_ItemCount == 0) - { - return res; - } - } - - res += m_HotbarSlots.AddItem(ToAdd, a_AllowNewStacks, a_tryToFillEquippedFirst ? m_EquippedSlotNum : -1); - ToAdd.m_ItemCount = a_Item.m_ItemCount - res; - if (ToAdd.m_ItemCount == 0) - { - return res; - } - - res += m_InventorySlots.AddItem(ToAdd, a_AllowNewStacks); - return res; -} - - - - - -int cInventory::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst) -{ - int TotalAdded = 0; - for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();) - { - int NumAdded = AddItem(*itr, a_AllowNewStacks, a_tryToFillEquippedFirst); - if (itr->m_ItemCount == NumAdded) - { - itr = a_ItemStackList.erase(itr); - } - else - { - itr->m_ItemCount -= NumAdded; - ++itr; - } - TotalAdded += NumAdded; - } - return TotalAdded; -} - - - - - -bool cInventory::RemoveOneEquippedItem(void) -{ - if (m_HotbarSlots.GetSlot(m_EquippedSlotNum).IsEmpty()) - { - return false; - } - - m_HotbarSlots.ChangeSlotCount(m_EquippedSlotNum, -1); - return true; -} - - - - - -int cInventory::HowManyItems(const cItem & a_Item) -{ - return - m_ArmorSlots.HowManyItems(a_Item) + - m_InventorySlots.HowManyItems(a_Item) + - m_HotbarSlots.HowManyItems(a_Item); -} - - - - - -bool cInventory::HasItems(const cItem & a_ItemStack) -{ - int CurrentlyHave = HowManyItems(a_ItemStack); - return (CurrentlyHave >= a_ItemStack.m_ItemCount); -} - - - - - -void cInventory::SetSlot(int a_SlotNum, const cItem & a_Item) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) - { - LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Ignoring.", __FUNCTION__, a_SlotNum, invNumSlots - 1); - return; - } - - int GridSlotNum = 0; - cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); - if (Grid == NULL) - { - LOGWARNING("%s(%d): requesting an invalid itemgrid. Ignoring.", __FUNCTION__, a_SlotNum); - return; - } - Grid->SetSlot(GridSlotNum, a_Item); -} - - - - - -void cInventory::SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item) -{ - m_ArmorSlots.SetSlot(a_ArmorSlotNum, a_Item); -} - - - - - -void cInventory::SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item) -{ - m_InventorySlots.SetSlot(a_InventorySlotNum, a_Item); -} - - - - - -void cInventory::SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item) -{ - m_HotbarSlots.SetSlot(a_HotBarSlotNum, a_Item); -} - - - - - -const cItem & cInventory::GetSlot(int a_SlotNum) const -{ - if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) - { - LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first inventory slot instead.", __FUNCTION__, a_SlotNum, invNumSlots - 1); - return m_InventorySlots.GetSlot(0); - } - int GridSlotNum = 0; - const cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); - if (Grid == NULL) - { - // Something went wrong, but we don't know what. We must return a value, so return the first inventory slot - LOGWARNING("%s(%d): requesting an invalid ItemGrid, returning the first inventory slot instead.", __FUNCTION__, a_SlotNum); - return m_InventorySlots.GetSlot(0); - } - return Grid->GetSlot(GridSlotNum); -} - - - - - -const cItem & cInventory::GetArmorSlot(int a_ArmorSlotNum) const -{ - if ((a_ArmorSlotNum < 0) || (a_ArmorSlotNum >= invArmorCount)) - { - LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_ArmorSlotNum, invArmorCount - 1); - return m_ArmorSlots.GetSlot(0); - } - return m_ArmorSlots.GetSlot(a_ArmorSlotNum); -} - - - - - -const cItem & cInventory::GetInventorySlot(int a_InventorySlotNum) const -{ - if ((a_InventorySlotNum < 0) || (a_InventorySlotNum >= invInventoryCount)) - { - LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_InventorySlotNum, invInventoryCount - 1); - return m_InventorySlots.GetSlot(0); - } - return m_InventorySlots.GetSlot(a_InventorySlotNum); -} - - - - - -const cItem & cInventory::GetHotbarSlot(int a_SlotNum) const -{ - if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount)) - { - LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_SlotNum, invHotbarCount - 1); - return m_HotbarSlots.GetSlot(0); - } - return m_HotbarSlots.GetSlot(a_SlotNum); -} - - - - - -const cItem & cInventory::GetEquippedItem(void) const -{ - return GetHotbarSlot(m_EquippedSlotNum); -} - - - - - -void cInventory::SetEquippedSlotNum(int a_SlotNum) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount)) - { - LOGWARNING("%s: requesting invalid slot index: %d out of %d. Setting 0 instead.", __FUNCTION__, a_SlotNum, invHotbarCount - 1); - m_EquippedSlotNum = 0; - } - else - { - m_EquippedSlotNum = a_SlotNum; - } -} - - - - - -bool cInventory::DamageEquippedItem(short a_Amount) -{ - return DamageItem(invHotbarOffset + m_EquippedSlotNum, a_Amount); -} - - - - - -int cInventory::ChangeSlotCount(int a_SlotNum, int a_AddToCount) -{ - int GridSlotNum = 0; - cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); - if (Grid == NULL) - { - LOGWARNING("%s: invalid slot number, expected 0 .. %d, got %d; ignoring", __FUNCTION__, invNumSlots, a_SlotNum); - return -1; - } - return Grid->ChangeSlotCount(GridSlotNum, a_AddToCount); -} - - - - - -bool cInventory::DamageItem(int a_SlotNum, short a_Amount) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) - { - LOGWARNING("%s: requesting an invalid slot index: %d out of %d", __FUNCTION__, a_SlotNum, invNumSlots - 1); - return false; - } - - int GridSlotNum = 0; - cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); - if (Grid == NULL) - { - LOGWARNING("%s(%d, %d): requesting an invalid grid, ignoring.", __FUNCTION__, a_SlotNum, a_Amount); - return false; - } - if (!Grid->DamageItem(GridSlotNum, a_Amount)) - { - // The item has been damaged, but did not break yet - return false; - } - - // The item has broken, remove it: - Grid->EmptySlot(GridSlotNum); - return true; -} - - - - - -void cInventory::CopyToItems(cItems & a_Items) -{ - m_ArmorSlots.CopyToItems(a_Items); - m_InventorySlots.CopyToItems(a_Items); - m_HotbarSlots.CopyToItems(a_Items); -} - - - - - -void cInventory::SendSlot(int a_SlotNum) -{ - cItem Item(GetSlot(a_SlotNum)); - if (Item.IsEmpty()) - { - // Sanitize items that are not completely empty (ie. count == 0, but type != empty) - Item.Empty(); - } - m_Owner.GetClientHandle()->SendInventorySlot(0, a_SlotNum + 5, Item); // Slots in the client are numbered "+ 5" because of crafting grid and result -} - - - - - -/* -int cInventory::MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot) -{ - int res = 0; - for (int i = a_BeginSlot; i <= a_EndSlot; i++) - { - if ( - m_Slots[i].IsEmpty() || - ((m_Slots[i].m_ItemType == a_ItemType) && (m_Slots[i].m_ItemDamage == a_ItemDamage)) - ) - { - int MaxCount = ItemHandler(a_ItemType)->GetMaxStackSize(); - ASSERT(m_Slots[i].m_ItemCount <= MaxCount); - int NumToMove = std::min(a_Count, MaxCount - m_Slots[i].m_ItemCount); - m_Slots[i].m_ItemCount += NumToMove; - m_Slots[i].m_ItemDamage = a_ItemDamage; - m_Slots[i].m_ItemType = a_ItemType; - SendSlot(i); - res += NumToMove; - a_Count -= NumToMove; - if (a_Count <= 0) - { - // No more items to distribute - return res; - } - } - } // for i - m_Slots[] - // No more space to distribute to - return res; -} -*/ - - - - - -int cInventory::ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum) -{ - switch (a_ArmorSlotNum) - { - case 0: return 4; // Helmet - case 1: return 3; // Chestplate - case 2: return 2; // Leggings - case 3: return 1; // Boots - } - LOGWARN("%s: invalid armor slot number: %d", __FUNCTION__, a_ArmorSlotNum); - return 0; -} - - - - - -#if 0 -bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */ ) -{ - // Fill already present stacks - if( a_Mode < 2 ) - { - int MaxStackSize = cItemHandler::GetItemHandler(a_Item.m_ItemType)->GetMaxStackSize(); - for(int i = 0; i < a_Size; i++) - { - if( m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType && m_Slots[i + a_Offset].m_ItemCount < MaxStackSize && m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage ) - { - int NumFree = MaxStackSize - m_Slots[i + a_Offset].m_ItemCount; - if( NumFree >= a_Item.m_ItemCount ) - { - - //printf("1. Adding %i items ( free: %i )\n", a_Item.m_ItemCount, NumFree ); - m_Slots[i + a_Offset].m_ItemCount += a_Item.m_ItemCount; - a_Item.m_ItemCount = 0; - a_bChangedSlots[i + a_Offset] = true; - break; - } - else - { - //printf("2. Adding %i items\n", NumFree ); - m_Slots[i + a_Offset].m_ItemCount += (char)NumFree; - a_Item.m_ItemCount -= (char)NumFree; - a_bChangedSlots[i + a_Offset] = true; - } - } - } - } - - if( a_Mode > 0 ) - { - // If we got more left, find first empty slot - for(int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++) - { - if( m_Slots[i + a_Offset].m_ItemType == -1 ) - { - m_Slots[i + a_Offset] = a_Item; - a_Item.m_ItemCount = 0; - a_bChangedSlots[i + a_Offset] = true; - } - } - } - - return true; -} -#endif - - - - - -void cInventory::SaveToJson(Json::Value & a_Value) -{ - // The JSON originally included the 4 crafting slots and the result, so we have to put empty items there, too: - cItem EmptyItem; - Json::Value EmptyItemJson; - EmptyItem.GetJson(EmptyItemJson); - for (int i = 0; i < 5; i++) - { - a_Value.append(EmptyItemJson); - } - - // The 4 armor slots follow: - for (int i = 0; i < invArmorCount; i++) - { - Json::Value JSON_Item; - m_ArmorSlots.GetSlot(i).GetJson(JSON_Item); - a_Value.append(JSON_Item); - } - - // Next comes the main inventory: - for (int i = 0; i < invInventoryCount; i++) - { - Json::Value JSON_Item; - m_InventorySlots.GetSlot(i).GetJson(JSON_Item); - a_Value.append(JSON_Item); - } - - // The hotbar is the last: - for (int i = 0; i < invHotbarCount; i++) - { - Json::Value JSON_Item; - m_HotbarSlots.GetSlot(i).GetJson(JSON_Item); - a_Value.append(JSON_Item); - } -} - - - - - -bool cInventory::LoadFromJson(Json::Value & a_Value) -{ - int SlotIdx = 0; - - for (Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr, SlotIdx++) - { - cItem Item; - Item.FromJson(*itr); - - // The JSON originally included the 4 crafting slots and the result slot, so we need to skip the first 5 items: - if (SlotIdx < 5) - { - continue; - } - - // If we loaded all the slots, stop now, even if the JSON has more: - if (SlotIdx - 5 >= invNumSlots) - { - break; - } - - int GridSlotNum = 0; - cItemGrid * Grid = GetGridForSlotNum(SlotIdx - 5, GridSlotNum); - ASSERT(Grid != NULL); - Grid->SetSlot(GridSlotNum, Item); - } // for itr - a_Value[] - return true; -} - - - - - -const cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const -{ - ASSERT(a_SlotNum >= 0); - - if (a_SlotNum < invArmorCount) - { - a_GridSlotNum = a_SlotNum; - return &m_ArmorSlots; - } - a_SlotNum -= invArmorCount; - if (a_SlotNum < invInventoryCount) - { - a_GridSlotNum = a_SlotNum; - return &m_InventorySlots; - } - a_GridSlotNum = a_SlotNum - invInventoryCount; - return &m_HotbarSlots; -} - - - - - -cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) -{ - ASSERT(a_SlotNum >= 0); - - if (a_SlotNum < invArmorCount) - { - a_GridSlotNum = a_SlotNum; - return &m_ArmorSlots; - } - a_SlotNum -= invArmorCount; - if (a_SlotNum < invInventoryCount) - { - a_GridSlotNum = a_SlotNum; - return &m_InventorySlots; - } - a_GridSlotNum = a_SlotNum - invInventoryCount; - return &m_HotbarSlots; -} - - - - - -void cInventory::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) -{ - // Send the neccessary updates to whoever needs them - - if (m_Owner.IsDestroyed()) - { - // Owner is not (yet) valid, skip for now - return; - } - - // Armor update needs broadcast to other players: - cWorld * World = m_Owner.GetWorld(); - if ((a_ItemGrid == &m_ArmorSlots) && (World != NULL)) - { - World->BroadcastEntityEquipment( - m_Owner, ArmorSlotNumToEntityEquipmentID(a_SlotNum), - m_ArmorSlots.GetSlot(a_SlotNum), m_Owner.GetClientHandle() - ); - } - - // Convert the grid-local a_SlotNum to our global SlotNum: - int Base = 0; - if (a_ItemGrid == &m_ArmorSlots) - { - Base = invArmorOffset; - } - else if (a_ItemGrid == &m_InventorySlots) - { - Base = invInventoryOffset; - } - else if (a_ItemGrid == &m_HotbarSlots) - { - Base = invHotbarOffset; - } - else - { - ASSERT(!"Unknown ItemGrid calling OnSlotChanged()"); - return; - } - - SendSlot(Base + a_SlotNum); -} - - - - diff --git a/source/Inventory.h b/source/Inventory.h deleted file mode 100644 index 3c6a19de8..000000000 --- a/source/Inventory.h +++ /dev/null @@ -1,182 +0,0 @@ - -#pragma once - -#include "ItemGrid.h" - - - - - -namespace Json -{ - class Value; -}; - -class cClientHandle; -class cPlayer; - - - - -// tolua_begin - -/** This class represents the player's inventory -The slots are divided into three areas: -- armor slots (1 x 4) -- inventory slots (9 x 3) -- hotbar slots (9 x 1) -The generic GetSlot(), SetSlot() and HowManyCanFit() functions take the index of the slots, -as if armor slots, inventory slots and then hotbar slots were put one after another. -You can use the invArmorOffset, invInventoryOffset and invHotbarOffset constants. -*/ - -class cInventory : - public cItemGrid::cListener -{ -public: - - // Counts and offsets to individual parts of the inventory, as used by GetSlot() / SetSlot() / HowManyCanFit(): - enum - { - invArmorCount = 4, - invInventoryCount = 9 * 3, - invHotbarCount = 9, - - invArmorOffset = 0, - invInventoryOffset = invArmorOffset + invArmorCount, - invHotbarOffset = invInventoryOffset + invInventoryCount, - invNumSlots = invHotbarOffset + invHotbarCount - } ; - - // tolua_end - - cInventory(cPlayer & a_Owner); - - // tolua_begin - - /// Removes all items from the entire inventory - void Clear(void); - - /// Returns number of items out of a_ItemStack that can fit in the storage - int HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots); - - /// Returns how many items of the specified type would fit into the slot range specified - int HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots); - - /** Adds as many items out of a_ItemStack as can fit. - If a_AllowNewStacks is set to false, only existing stacks can be topped up; - if a_AllowNewStacks is set to true, empty slots can be used for the rest. - If a_tryToFillEquippedFirst is set to true, the currently equipped slot will be used first (if empty or - compatible with added items) - if a_tryToFillEquippedFirst is set to false, the regular order applies. - Returns the number of items that fit. - */ - int AddItem(const cItem & a_ItemStack, bool a_AllowNewStacks = true, bool a_tryToFillEquippedFirst = false); - - /** Same as AddItem, but works on an entire list of item stacks. - The a_ItemStackList is modified to reflect the leftover items. - If a_AllowNewStacks is set to false, only existing stacks can be topped up; - if a_AllowNewStacks is set to true, empty slots can be used for the rest. - If a_tryToFillEquippedFirst is set to true, the currently equipped slot will be used first (if empty or - compatible with added items) - if a_tryToFillEquippedFirst is set to false, the regular order applies. - Returns the total number of items that fit. - */ - int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst); - - /// Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed - bool RemoveOneEquippedItem(void); - - /// Returns the number of items of type a_Item that are stored - int HowManyItems(const cItem & a_Item); - - /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack - bool HasItems(const cItem & a_ItemStack); - - /// Returns the cItemGrid object representing the armor slots - cItemGrid & GetArmorGrid(void) { return m_ArmorSlots; } - - /// Returns the cItemGrid object representing the main inventory slots - cItemGrid & GetInventoryGrid(void) { return m_InventorySlots; } - - /// Returns the cItemGrid object representing the hotbar slots - cItemGrid & GetHotbarGrid(void) { return m_HotbarSlots; } - - /// Returns the player associated with this inventory - cPlayer & GetOwner(void) { return m_Owner; } - - /// Copies the non-empty slots into a_ItemStacks; preserves the original a_Items contents - void CopyToItems(cItems & a_Items); - - // tolua_end - - /// Returns the player associated with this inventory (const version) - const cPlayer & GetOwner(void) const { return m_Owner; } - - // tolua_begin - - const cItem & GetSlot(int a_SlotNum) const; - const cItem & GetArmorSlot(int a_ArmorSlotNum) const; - const cItem & GetInventorySlot(int a_InventorySlotNum) const; - const cItem & GetHotbarSlot(int a_HotBarSlotNum) const; - const cItem & GetEquippedItem(void) const; - void SetSlot(int a_SlotNum, const cItem & a_Item); - void SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item); - void SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item); - void SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item); - - void SetEquippedSlotNum(int a_SlotNum); - int GetEquippedSlotNum(void) { return m_EquippedSlotNum; } - - /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. - If the slot is empty, ignores the call. - Returns the new count, or -1 if the slot number is invalid. - */ - int ChangeSlotCount(int a_SlotNum, int a_AddToCount); - - /// Adds the specified damage to the specified item; deletes the item and returns true if the item broke. - bool DamageItem(int a_SlotNum, short a_Amount); - - /// Adds the specified damage to the currently held item; deletes the item and returns true if the item broke. - bool DamageEquippedItem(short a_Amount = 1); - - const cItem & GetEquippedHelmet (void) const { return m_ArmorSlots.GetSlot(0); } - const cItem & GetEquippedChestplate(void) const { return m_ArmorSlots.GetSlot(1); } - const cItem & GetEquippedLeggings (void) const { return m_ArmorSlots.GetSlot(2); } - const cItem & GetEquippedBoots (void) const { return m_ArmorSlots.GetSlot(3); } - - // tolua_end - - /// Sends the slot contents to the owner - void SendSlot(int a_SlotNum); - - /// Converts an armor slot number into the ID for the EntityEquipment packet - static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum); - - void SaveToJson(Json::Value & a_Value); - bool LoadFromJson(Json::Value & a_Value); - -protected: - bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0 ); - - cItemGrid m_ArmorSlots; - cItemGrid m_InventorySlots; - cItemGrid m_HotbarSlots; - - int m_EquippedSlotNum; - - cPlayer & m_Owner; - - /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum - const cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const; - - /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum - cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum); - - // cItemGrid::cListener override: - virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; -}; // tolua_export - - - - diff --git a/source/Item.cpp b/source/Item.cpp deleted file mode 100644 index 25664e4df..000000000 --- a/source/Item.cpp +++ /dev/null @@ -1,261 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Item.h" -#include -#include "Items/ItemHandler.h" - - - - - -cItem cItem::CopyOne(void) const -{ - cItem res(*this); - res.m_ItemCount = 1; - return res; -} - - - - - -cItem & cItem::AddCount(char a_AmountToAdd) -{ - m_ItemCount += a_AmountToAdd; - if (m_ItemCount <= 0) - { - Empty(); - } - return *this; -} - - - - - -short cItem::GetMaxDamage(void) const -{ - switch (m_ItemType) - { - case E_ITEM_BOW: return 384; - case E_ITEM_DIAMOND_AXE: return 1563; - case E_ITEM_DIAMOND_HOE: return 1563; - case E_ITEM_DIAMOND_PICKAXE: return 1563; - case E_ITEM_DIAMOND_SHOVEL: return 1563; - case E_ITEM_DIAMOND_SWORD: return 1563; - case E_ITEM_FLINT_AND_STEEL: return 65; - case E_ITEM_GOLD_AXE: return 32; - case E_ITEM_GOLD_HOE: return 32; - case E_ITEM_GOLD_PICKAXE: return 32; - case E_ITEM_GOLD_SHOVEL: return 32; - case E_ITEM_GOLD_SWORD: return 32; - case E_ITEM_IRON_AXE: return 251; - case E_ITEM_IRON_HOE: return 251; - case E_ITEM_IRON_PICKAXE: return 251; - case E_ITEM_IRON_SHOVEL: return 251; - case E_ITEM_IRON_SWORD: return 251; - case E_ITEM_SHEARS: return 251; - case E_ITEM_STONE_AXE: return 132; - case E_ITEM_STONE_HOE: return 132; - case E_ITEM_STONE_PICKAXE: return 132; - case E_ITEM_STONE_SHOVEL: return 132; - case E_ITEM_STONE_SWORD: return 132; - case E_ITEM_WOODEN_AXE: return 60; - case E_ITEM_WOODEN_HOE: return 60; - case E_ITEM_WOODEN_PICKAXE: return 60; - case E_ITEM_WOODEN_SHOVEL: return 60; - case E_ITEM_WOODEN_SWORD: return 60; - } - return 0; -} - - - - - -bool cItem::DamageItem(short a_Amount) -{ - short MaxDamage = GetMaxDamage(); - if (MaxDamage == 0) - { - // Item doesn't have damage - return false; - } - - m_ItemDamage += a_Amount; - return (m_ItemDamage >= MaxDamage); -} - - - - - -bool cItem::IsStackableWith(const cItem & a_OtherStack) const -{ - if (a_OtherStack.m_ItemType != m_ItemType) - { - return false; - } - if (a_OtherStack.m_ItemDamage != m_ItemDamage) - { - return false; - } - if (a_OtherStack.m_Enchantments != m_Enchantments) - { - return false; - } - - return true; -} - - - - - -bool cItem::IsFullStack(void) const -{ - return (m_ItemCount >= ItemHandler(m_ItemType)->GetMaxStackSize()); -} - - - - - -char cItem::GetMaxStackSize(void) const -{ - return ItemHandler(m_ItemType)->GetMaxStackSize(); -} - - - - - -/// Returns the cItemHandler responsible for this item type -cItemHandler * cItem::GetHandler(void) const -{ - return ItemHandler(m_ItemType); -} - - - - - -void cItem::GetJson(Json::Value & a_OutValue) const -{ - a_OutValue["ID"] = m_ItemType; - if (m_ItemType > 0) - { - a_OutValue["Count"] = m_ItemCount; - a_OutValue["Health"] = m_ItemDamage; - AString Enchantments(m_Enchantments.ToString()); - if (!Enchantments.empty()) - { - a_OutValue["ench"] = Enchantments; - } - } -} - - - - - -void cItem::FromJson(const Json::Value & a_Value) -{ - m_ItemType = (ENUM_ITEM_ID)a_Value.get("ID", -1 ).asInt(); - if (m_ItemType > 0) - { - m_ItemCount = (char)a_Value.get("Count", -1 ).asInt(); - m_ItemDamage = (short)a_Value.get("Health", -1 ).asInt(); - m_Enchantments.Clear(); - m_Enchantments.AddFromString(a_Value.get("ench", "").asString()); - } -} - - - - - -bool cItem::IsEnchantable(short item) -{ - if ((item >= 256) && (item <= 259)) - return true; - if ((item >= 267) && (item <= 279)) - return true; - if ((item >= 283) && (item <= 286)) - return true; - if ((item >= 290) && (item <= 294)) - return true; - if ((item >= 298) && (item <= 317)) - return true; - if ((item >= 290) && (item <= 294)) - return true; - - if ((item == 346) || (item == 359) || (item == 261)) - return true; - - return false; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cItems: - -cItem * cItems::Get(int a_Idx) -{ - if ((a_Idx < 0) || (a_Idx >= (int)size())) - { - LOGWARNING("cItems: Attempt to get an out-of-bounds item at index %d; there are currently %d items. Returning a nil.", a_Idx, size()); - return NULL; - } - return &at(a_Idx); -} - - - - - -void cItems::Set(int a_Idx, const cItem & a_Item) -{ - if ((a_Idx < 0) || (a_Idx >= (int)size())) - { - LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size()); - return; - } - at(a_Idx) = a_Item; -} - - - - - -void cItems::Delete(int a_Idx) -{ - if ((a_Idx < 0) || (a_Idx >= (int)size())) - { - LOGWARNING("cItems: Attempt to delete an item at an out-of-bounds index %d; there are currently %d items. Ignoring.", a_Idx, size()); - return; - } - erase(begin() + a_Idx); -} - - - - - -void cItems::Set(int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage) -{ - if ((a_Idx < 0) || (a_Idx >= (int)size())) - { - LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size()); - return; - } - at(a_Idx) = cItem(a_ItemType, a_ItemCount, a_ItemDamage); -} - - - - diff --git a/source/Item.h b/source/Item.h deleted file mode 100644 index c60d0542c..000000000 --- a/source/Item.h +++ /dev/null @@ -1,210 +0,0 @@ - -// Item.h - -// Declares the cItem class representing an item (in the inventory sense) - - - - - -#pragma once - -#include "Defines.h" -#include "Enchantments.h" - - - - - -// fwd: -class cItemHandler; - -namespace Json -{ - class Value; -} - - - - - -// tolua_begin -class cItem -{ -public: - /// Creates an empty item - cItem(void) : - m_ItemType(E_ITEM_EMPTY), - m_ItemCount(0), - m_ItemDamage(0) - { - } - - - /// Creates an item of the specified type, by default 1 piece with no damage and no enchantments - cItem( - short a_ItemType, - char a_ItemCount = 1, - short a_ItemDamage = 0, - const AString & a_Enchantments = "" - ) : - m_ItemType (a_ItemType), - m_ItemCount (a_ItemCount), - m_ItemDamage (a_ItemDamage), - m_Enchantments(a_Enchantments) - { - if (!IsValidItem(m_ItemType)) - { - if (m_ItemType != E_BLOCK_AIR) - { - LOGWARNING("%s: creating an invalid item type (%d), resetting to empty.", __FUNCTION__, a_ItemType); - } - Empty(); - } - } - - - /// Creates an exact copy of the item - cItem(const cItem & a_CopyFrom) : - m_ItemType (a_CopyFrom.m_ItemType), - m_ItemCount (a_CopyFrom.m_ItemCount), - m_ItemDamage (a_CopyFrom.m_ItemDamage), - m_Enchantments(a_CopyFrom.m_Enchantments) - { - } - - - void Empty(void) - { - m_ItemType = E_ITEM_EMPTY; - m_ItemCount = 0; - m_ItemDamage = 0; - m_Enchantments.Clear(); - } - - - void Clear(void) - { - m_ItemType = E_ITEM_EMPTY; - m_ItemCount = 0; - m_ItemDamage = 0; - } - - - bool IsEmpty(void) const - { - return ((m_ItemType <= 0) || (m_ItemCount <= 0)); - } - - - bool IsEqual(const cItem & a_Item) const - { - return ( - IsSameType(a_Item) && - (m_ItemDamage == a_Item.m_ItemDamage) && - (m_Enchantments == a_Item.m_Enchantments) - ); - } - - - bool IsSameType(const cItem & a_Item) const - { - return (m_ItemType == a_Item.m_ItemType) || (IsEmpty() && a_Item.IsEmpty()); - } - - - /// Returns a copy of this item with m_ItemCount set to 1. Useful to preserve enchantments etc. on stacked items - cItem CopyOne(void) const; - - /// Adds the specified count to this object and returns the reference to self (useful for chaining) - cItem & AddCount(char a_AmountToAdd); - - /// Returns the maximum damage value that this item can have; zero if damage is not applied - short GetMaxDamage(void) const; - - /// Damages a weapon / tool. Returns true when damage reaches max value and the item should be destroyed - bool DamageItem(short a_Amount = 1); - - inline bool IsDamageable(void) const { return (GetMaxDamage() > 0); } - - /// Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored! - bool IsStackableWith(const cItem & a_OtherStack) const; - - /// Returns true if the item is stacked up to its maximum stacking. - bool IsFullStack(void) const; - - /// Returns the maximum amount of stacked items of this type. - char GetMaxStackSize(void) const; - - // tolua_end - - /// Returns the cItemHandler responsible for this item type - cItemHandler * GetHandler(void) const; - - /// Saves the item data into JSON representation - void GetJson(Json::Value & a_OutValue) const; - - /// Loads the item data from JSON representation - void FromJson(const Json::Value & a_Value); - - /// Returns true if the specified item type is enchantable (as per 1.2.5 protocol requirements) - static bool IsEnchantable(short a_ItemType); - - // tolua_begin - - short m_ItemType; - char m_ItemCount; - short m_ItemDamage; - cEnchantments m_Enchantments; -}; -// tolua_end - - - - - -/** This class bridges a vector of cItem for safe access via Lua. It checks boundaries for all accesses -Note that this class is zero-indexed! -*/ -class cItems // tolua_export - : public std::vector -{ // tolua_export -public: - // tolua_begin - - /// Need a Lua-accessible constructor - cItems(void) {} - - cItem * Get (int a_Idx); - void Set (int a_Idx, const cItem & a_Item); - void Add (const cItem & a_Item) {push_back(a_Item); } - void Delete(int a_Idx); - void Clear (void) {clear(); } - int Size (void) {return size(); } - void Set (int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage); - - void Add (ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage) - { - push_back(cItem(a_ItemType, a_ItemCount, a_ItemDamage)); - } - - // tolua_end -} ; // tolua_export - - - - - -/// Used to store loot probability tables -class cLootProbab -{ -public: - cItem m_Item; - int m_MinAmount; - int m_MaxAmount; - int m_Weight; -} ; - - - - diff --git a/source/ItemGrid.cpp b/source/ItemGrid.cpp deleted file mode 100644 index e9b86173e..000000000 --- a/source/ItemGrid.cpp +++ /dev/null @@ -1,665 +0,0 @@ - -// ItemGrid.cpp - -// Implements the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) - -#include "Globals.h" -#include "ItemGrid.h" -#include "Items/ItemHandler.h" -#include "Noise.h" - - - - - -cItemGrid::cItemGrid(int a_Width, int a_Height) : - m_Width(a_Width), - m_Height(a_Height), - m_NumSlots(a_Width * a_Height), - m_Slots(new cItem[a_Width * a_Height]), - m_IsInTriggerListeners(false) -{ -} - - - - - -cItemGrid::~cItemGrid() -{ - delete[] m_Slots; -} - - - - - -int cItemGrid::GetSlotNum(int a_X, int a_Y) const -{ - if ( - (a_X < 0) || (a_X >= m_Width) || - (a_Y < 0) || (a_Y >= m_Height) - ) - { - LOGWARNING("%s: coords out of range: (%d, %d) in grid of size (%d, %d)", - __FUNCTION__, a_X, a_Y, m_Width, m_Height - ); - return -1; - } - return a_X + m_Width * a_Y; -} - - - - - -void cItemGrid::GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: SlotNum out of range: %d in grid of range %d", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - a_X = -1; - a_Y = -1; - return; - } - a_X = a_SlotNum % m_Width; - a_Y = a_SlotNum / m_Width; -} - - - - - -const cItem & cItemGrid::GetSlot(int a_X, int a_Y) const -{ - return GetSlot(GetSlotNum(a_X, a_Y)); -} - - - - - -const cItem & cItemGrid::GetSlot(int a_SlotNum) const -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number, %d out of %d slots", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return m_Slots[0]; - } - return m_Slots[a_SlotNum]; -} - - - - - -void cItemGrid::SetSlot(int a_X, int a_Y, const cItem & a_Item) -{ - SetSlot(GetSlotNum(a_X, a_Y), a_Item); -} - - - - - -void cItemGrid::SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage) -{ - SetSlot(GetSlotNum(a_X, a_Y), cItem(a_ItemType, a_ItemCount, a_ItemDamage)); -} - - - - - -void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return; - } - m_Slots[a_SlotNum] = a_Item; - TriggerListeners(a_SlotNum); -} - - - - - -void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage) -{ - SetSlot(a_SlotNum, cItem(a_ItemType, a_ItemCount, a_ItemDamage)); -} - - - - - -void cItemGrid::EmptySlot(int a_X, int a_Y) -{ - EmptySlot(GetSlotNum(a_X, a_Y)); -} - - - - - -void cItemGrid::EmptySlot(int a_SlotNum) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return; - } - - // Check if already empty: - if (m_Slots[a_SlotNum].IsEmpty()) - { - return; - } - - // Empty and notify - m_Slots[a_SlotNum].Empty(); - TriggerListeners(a_SlotNum); -} - - - - - -bool cItemGrid::IsSlotEmpty(int a_SlotNum) const -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return true; - } - return m_Slots[a_SlotNum].IsEmpty(); -} - - - - - -bool cItemGrid::IsSlotEmpty(int a_X, int a_Y) const -{ - return IsSlotEmpty(GetSlotNum(a_X, a_Y)); -} - - - - - -void cItemGrid::Clear(void) -{ - for (int i = 0; i < m_NumSlots; i++) - { - m_Slots[i].Empty(); - TriggerListeners(i); - } -} - - - - - -int cItemGrid::HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks) -{ - char NumLeft = a_ItemStack.m_ItemCount; - int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (m_Slots[i].IsEmpty()) - { - if (a_AllowNewStacks) - { - NumLeft -= MaxStack; - } - } - else if (m_Slots[i].IsStackableWith(a_ItemStack)) - { - NumLeft -= MaxStack - m_Slots[i].m_ItemCount; - } - if (NumLeft <= 0) - { - // All items fit - return a_ItemStack.m_ItemCount; - } - } // for i - m_Slots[] - return a_ItemStack.m_ItemCount - NumLeft; -} - - - - - -int cItemGrid::AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack) -{ - int PrevCount = 0; - if (m_Slots[a_Slot].IsEmpty()) - { - m_Slots[a_Slot] = a_ItemStack; - PrevCount = 0; - } - else - { - PrevCount = m_Slots[a_Slot].m_ItemCount; - } - m_Slots[a_Slot].m_ItemCount = std::min(a_MaxStack, PrevCount + a_Num); - int toReturn = m_Slots[a_Slot].m_ItemCount - PrevCount; - TriggerListeners(a_Slot); - return toReturn; -} - - - - - -int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_PrioritarySlot) -{ - int NumLeft = a_ItemStack.m_ItemCount; - int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); - - // Try prioritarySlot first: - if ( - (a_PrioritarySlot != -1) && - ( - m_Slots[a_PrioritarySlot].IsEmpty() || - m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack) - ) - ) - { - NumLeft -= AddItemToSlot(a_ItemStack, a_PrioritarySlot, NumLeft, MaxStack); - } - - // Scan existing stacks: - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (m_Slots[i].IsStackableWith(a_ItemStack)) - { - NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); - } - if (NumLeft <= 0) - { - // All items fit - return a_ItemStack.m_ItemCount; - } - } // for i - m_Slots[] - - if (!a_AllowNewStacks) - { - return (a_ItemStack.m_ItemCount - NumLeft); - } - - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (m_Slots[i].IsEmpty()) - { - NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); - } - if (NumLeft <= 0) - { - // All items fit - return a_ItemStack.m_ItemCount; - } - } // for i - m_Slots[] - return (a_ItemStack.m_ItemCount - NumLeft); -} - - - - - -int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, int a_PrioritarySlot) -{ - int TotalAdded = 0; - for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();) - { - int NumAdded = AddItem(*itr, a_AllowNewStacks, a_PrioritarySlot); - if (itr->m_ItemCount == NumAdded) - { - itr = a_ItemStackList.erase(itr); - } - else - { - itr->m_ItemCount -= NumAdded; - ++itr; - } - TotalAdded += NumAdded; - } - return TotalAdded; -} - - - - - -int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning -1", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return -1; - } - - if (m_Slots[a_SlotNum].IsEmpty()) - { - // The item is empty, it's not gonna change - return 0; - } - - if (m_Slots[a_SlotNum].m_ItemCount <= -a_AddToCount) - { - // Trying to remove more items than there already are, make the item empty - m_Slots[a_SlotNum].Empty(); - TriggerListeners(a_SlotNum); - return 0; - } - - m_Slots[a_SlotNum].m_ItemCount += a_AddToCount; - TriggerListeners(a_SlotNum); - return m_Slots[a_SlotNum].m_ItemCount; -} - - - - - -int cItemGrid::ChangeSlotCount(int a_X, int a_Y, int a_AddToCount) -{ - return ChangeSlotCount(GetSlotNum(a_X, a_Y), a_AddToCount); -} - - - - - -cItem cItemGrid::RemoveOneItem(int a_SlotNum) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning empty item", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return cItem(); - } - - // If the slot is empty, return an empty item - if (m_Slots[a_SlotNum].IsEmpty()) - { - return cItem(); - } - - // Make a copy of the item in slot, set count to 1 and remove one from the slot - cItem res = m_Slots[a_SlotNum]; - res.m_ItemCount = 1; - m_Slots[a_SlotNum].m_ItemCount -= 1; - - // Emptying the slot correctly if appropriate - if (m_Slots[a_SlotNum].m_ItemCount == 0) - { - m_Slots[a_SlotNum].Empty(); - } - - // Notify everyone of the change - TriggerListeners(a_SlotNum); - - // Return the stored one item - return res; -} - - - - - -cItem cItemGrid::RemoveOneItem(int a_X, int a_Y) -{ - return RemoveOneItem(GetSlotNum(a_X, a_Y)); -} - - - - - -int cItemGrid::HowManyItems(const cItem & a_Item) -{ - int res = 0; - for (int i = 0; i < m_NumSlots; i++) - { - if (m_Slots[i].IsStackableWith(a_Item)) - { - res += m_Slots[i].m_ItemCount; - } - } - return res; -} - - - - - -bool cItemGrid::HasItems(const cItem & a_ItemStack) -{ - int CurrentlyHave = HowManyItems(a_ItemStack); - return (CurrentlyHave >= a_ItemStack.m_ItemCount); -} - - - - - -int cItemGrid::GetFirstEmptySlot(void) const -{ - return GetNextEmptySlot(-1); -} - - - - - -int cItemGrid::GetFirstUsedSlot(void) const -{ - return GetNextUsedSlot(-1); -} - - - - - -int cItemGrid::GetLastEmptySlot(void) const -{ - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (m_Slots[i].IsEmpty()) - { - return i; - } - } - return -1; -} - - - - - -int cItemGrid::GetLastUsedSlot(void) const -{ - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (!m_Slots[i].IsEmpty()) - { - return i; - } - } - return -1; -} - - - - - -int cItemGrid::GetNextEmptySlot(int a_StartFrom) const -{ - for (int i = a_StartFrom + 1; i < m_NumSlots; i++) - { - if (m_Slots[i].IsEmpty()) - { - return i; - } - } - return -1; -} - - - - - -int cItemGrid::GetNextUsedSlot(int a_StartFrom) const -{ - for (int i = a_StartFrom + 1; i < m_NumSlots; i++) - { - if (!m_Slots[i].IsEmpty()) - { - return i; - } - } - return -1; -} - - - - - -void cItemGrid::CopyToItems(cItems & a_Items) const -{ - for (int i = 0; i < m_NumSlots; i++) - { - if (!m_Slots[i].IsEmpty()) - { - a_Items.push_back(m_Slots[i]); - } - } // for i - m_Slots[] -} - - - - - -bool cItemGrid::DamageItem(int a_SlotNum, short a_Amount) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: invalid slot number %d out of %d slots, ignoring.", __FUNCTION__, a_SlotNum, m_NumSlots); - return false; - } - return m_Slots[a_SlotNum].DamageItem(a_Amount); -} - - - - - -bool cItemGrid::DamageItem(int a_X, int a_Y, short a_Amount) -{ - return DamageItem(GetSlotNum(a_X, a_Y), a_Amount); -} - - - - - -void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed) -{ - // Calculate the total weight: - int TotalProbab = 1; - for (int i = 0; i < a_CountLootProbabs; i++) - { - TotalProbab += a_LootProbabs[i].m_Weight; - } - - // Pick the loot items: - cNoise Noise(a_Seed); - for (int i = 0; i < a_NumSlots; i++) - { - int Rnd = (Noise.IntNoise1DInt(i) / 7); - int LootRnd = Rnd % TotalProbab; - Rnd >>= 8; - cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment - for (int j = 0; j < a_CountLootProbabs; j++) - { - LootRnd -= a_LootProbabs[i].m_Weight; - if (LootRnd < 0) - { - CurrentLoot = a_LootProbabs[i].m_Item; - CurrentLoot.m_ItemCount = a_LootProbabs[i].m_MinAmount + (Rnd % (a_LootProbabs[i].m_MaxAmount - a_LootProbabs[i].m_MinAmount)); - Rnd >>= 8; - break; - } - } // for j - a_LootProbabs[] - SetSlot(Rnd % m_NumSlots, CurrentLoot); - } // for i - NumSlots -} - - - - - -void cItemGrid::AddListener(cListener & a_Listener) -{ - cCSLock Lock(m_CSListeners); - ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() - m_Listeners.push_back(&a_Listener); -} - - - - - -void cItemGrid::RemoveListener(cListener & a_Listener) -{ - cCSLock Lock(m_CSListeners); - ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() - for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr) - { - if (*itr == &a_Listener) - { - m_Listeners.erase(itr); - return; - } - } // for itr - m_Listeners[] -} - - - - - -void cItemGrid::TriggerListeners(int a_SlotNum) -{ - cListeners Listeners; - { - cCSLock Lock(m_CSListeners); - m_IsInTriggerListeners = true; - Listeners = m_Listeners; - } - for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr) - { - (*itr)->OnSlotChanged(this, a_SlotNum); - } // for itr - m_Listeners[] - m_IsInTriggerListeners = false; -} - - - - diff --git a/source/ItemGrid.h b/source/ItemGrid.h deleted file mode 100644 index a4af523cf..000000000 --- a/source/ItemGrid.h +++ /dev/null @@ -1,191 +0,0 @@ - -// ItemGrid.h - -// Declares the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) - - - - -#pragma once - -#include "Item.h" - - - - - -// tolua_begin -class cItemGrid -{ -public: - // tolua_end - - /// This class is used as a callback for when a slot changes - class cListener - { - public: - /// Called whenever a slot changes - virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) = 0; - } ; - typedef std::vector cListeners; - - cItemGrid(int a_Width, int a_Height); - - ~cItemGrid(); - - // tolua_begin - int GetWidth (void) const { return m_Width; } - int GetHeight (void) const { return m_Height; } - int GetNumSlots(void) const { return m_NumSlots; } - - /// Converts XY coords into slot number; returns -1 on invalid coords - int GetSlotNum(int a_X, int a_Y) const; - - // tolua_end - - /// Converts slot number into XY coords; sets coords to -1 on invalid slot number. Exported in ManualBindings.cpp - void GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const; - - // tolua_begin - - // Retrieve slots by coords or slot number; Logs warning and returns the first slot on invalid coords / slotnum - const cItem & GetSlot(int a_X, int a_Y) const; - const cItem & GetSlot(int a_SlotNum) const; - - // Set slot by coords or slot number; Logs warning and doesn't set on invalid coords / slotnum - void SetSlot(int a_X, int a_Y, const cItem & a_Item); - void SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage); - void SetSlot(int a_SlotNum, const cItem & a_Item); - void SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage); - - // Empty the specified slot; Logs warning and doesn't set on invalid coords / slotnum - void EmptySlot(int a_X, int a_Y); - void EmptySlot(int a_SlotNum); - - /// Returns true if the specified slot is empty or the slot doesn't exist - bool IsSlotEmpty(int a_SlotNum) const; - - /// Returns true if the specified slot is empty or the slot doesn't exist - bool IsSlotEmpty(int a_X, int a_Y) const; - - /// Sets all items as empty - void Clear(void); - - /// Returns number of items out of a_ItemStack that can fit in the storage - int HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks = true); - - /** Adds as many items out of a_ItemStack as can fit. - If a_AllowNewStacks is set to false, only existing stacks can be topped up; - if a_AllowNewStacks is set to true, empty slots can be used for the rest. - If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in - first (if empty or compatible with added items) - if a_PrioritarySlot is set to -1, regular order apply - Returns the number of items that fit. - */ - int AddItem(cItem & a_ItemStack, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); - - /** Same as AddItem, but works on an entire list of item stacks. - The a_ItemStackList is modified to reflect the leftover items. - If a_AllowNewStacks is set to false, only existing stacks can be topped up; - if a_AllowNewStacks is set to true, empty slots can be used for the rest. - If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in - first (if empty or compatible with added items) - if a_PrioritarySlot is set to -1, regular order apply - Returns the total number of items that fit. - */ - int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); - - /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. - If the slot is empty, ignores the call. - Returns the new count. - */ - int ChangeSlotCount(int a_SlotNum, int a_AddToCount); - - /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. - If the slot is empty, ignores the call. - Returns the new count. - */ - int ChangeSlotCount(int a_X, int a_Y, int a_AddToCount); - - /** Removes one item from the stack in the specified slot, and returns it. - If the slot was empty, returns an empty item - */ - cItem RemoveOneItem(int a_SlotNum); - - /** Removes one item from the stack in the specified slot, and returns it. - If the slot was empty, returns an empty item - */ - cItem RemoveOneItem(int a_X, int a_Y); - - /// Returns the number of items of type a_Item that are stored - int HowManyItems(const cItem & a_Item); - - /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack - bool HasItems(const cItem & a_ItemStack); - - /// Returns the index of the first empty slot; -1 if all full - int GetFirstEmptySlot(void) const; - - /// Returns the index of the first non-empty slot; -1 if all empty - int GetFirstUsedSlot(void) const; - - /// Returns the index of the last empty slot; -1 if all full - int GetLastEmptySlot(void) const; - - /// Returns the index of the last used slot; -1 if all empty - int GetLastUsedSlot(void) const; - - /// Returns the index of the first empty slot following a_StartFrom (a_StartFrom is not checked) - int GetNextEmptySlot(int a_StartFrom) const; - - /// Returns the index of the first used slot following a_StartFrom (a_StartFrom is not checked) - int GetNextUsedSlot(int a_StartFrom) const; - - /// Copies the contents into a cItems object; preserves the original a_Items contents - void CopyToItems(cItems & a_Items) const; - - /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) - bool DamageItem(int a_SlotNum, short a_Amount); - - /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) - bool DamageItem(int a_X, int a_Y, short a_Amount); - - // tolua_end - - - /** Generates random loot from the specified loot probability table, with a chance of enchanted books added. - A total of a_NumSlots are taken by the loot. - Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs - */ - void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed); - - /// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback! - void AddListener(cListener & a_Listener); - - /// Removes a slot-change-callback. Must not be called from within the listener callback! - void RemoveListener(cListener & a_Listener); - - // tolua_begin - -protected: - int m_Width; - int m_Height; - int m_NumSlots; // m_Width * m_Height, for easier validity checking in the access functions - cItem * m_Slots; // x + m_Width * y - - cListeners m_Listeners; ///< Listeners which should be notified on slot changes; the pointers are not owned by this object - cCriticalSection m_CSListeners; ///< CS that guards the m_Listeners against multi-thread access - bool m_IsInTriggerListeners; ///< Set to true while TriggerListeners is running, to detect attempts to manipulate listener list while triggerring - - /// Calls all m_Listeners for the specified slot number - void TriggerListeners(int a_SlotNum); - - /** Adds up to a_Num items out of a_ItemStack, as many as can fit, in specified slot - Returns the number of items that did fit. - */ - int AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack); -} ; -// tolua_end - - - diff --git a/source/Items/ItemBed.h b/source/Items/ItemBed.h deleted file mode 100644 index ab4182eea..000000000 --- a/source/Items/ItemBed.h +++ /dev/null @@ -1,56 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Blocks/BlockBed.h" - - - - - -class cItemBedHandler : - public cItemHandler -{ -public: - cItemBedHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - if (a_BlockFace != BLOCK_FACE_TOP) - { - // Can only be placed on the floor - return false; - } - - a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation()); - - // Check if there is empty space for the foot section: - Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta); - if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) - { - return false; - } - - a_BlockType = E_BLOCK_BED; - return true; - } -} ; - - - - diff --git a/source/Items/ItemBoat.h b/source/Items/ItemBoat.h deleted file mode 100644 index 6e3395f1d..000000000 --- a/source/Items/ItemBoat.h +++ /dev/null @@ -1,54 +0,0 @@ - -// ItemBoat.h - -// Declares the various boat ItemHandlers - - - - - -#pragma once - -#include "../Entities/Boat.h" - - - - - -class cItemBoatHandler : - public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemBoatHandler(int a_ItemType) : - super(a_ItemType) - { - } - - - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - if (a_Dir < 0) - { - return false; - } - - double x = (double)a_BlockX + 0.5; - double y = (double)a_BlockY + 0.5; - double z = (double)a_BlockZ + 0.5; - - cBoat * Boat = NULL; - - Boat = new cBoat (x, y, z); - Boat->Initialize(a_World); - - return true; - } - -} ; - - - - diff --git a/source/Items/ItemBow.h b/source/Items/ItemBow.h deleted file mode 100644 index d533c21fd..000000000 --- a/source/Items/ItemBow.h +++ /dev/null @@ -1,87 +0,0 @@ - -// ItemBow.h - -// Declares the cItemBowHandler class representing the itemhandler for bows - - - - - -#pragma once - -#include "../Entities/ProjectileEntity.h" - - - - - -class cItemBowHandler : - public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemBowHandler(void) : - super(E_ITEM_BOW) - { - } - - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - ASSERT(a_Player != NULL); - - // Check if the player has an arrow in the inventory, or is in Creative: - if (!(a_Player->IsGameModeCreative() || a_Player->GetInventory().HasItems(cItem(E_ITEM_ARROW)))) - { - return false; - } - - a_Player->StartChargingBow(); - return true; - } - - - virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override - { - // Actual shot - produce the arrow with speed based on the ticks that the bow was charged - ASSERT(a_Player != NULL); - - int BowCharge = a_Player->FinishChargingBow(); - double Force = (double)BowCharge / 20; - Force = (Force * Force + 2 * Force) / 3; // This formula is used by the 1.6.2 client - if (Force < 0.1) - { - // Too little force, ignore the shot - return; - } - if (Force > 1) - { - Force = 1; - } - - // Create the arrow entity: - cArrowEntity * Arrow = new cArrowEntity(*a_Player, Force * 2); - if (Arrow == NULL) - { - return; - } - if (!Arrow->Initialize(a_Player->GetWorld())) - { - delete Arrow; - return; - } - a_Player->GetWorld()->BroadcastSpawnEntity(*Arrow); - a_Player->GetWorld()->BroadcastSoundEffect("random.bow", (int)a_Player->GetPosX() * 8, (int)a_Player->GetPosY() * 8, (int)a_Player->GetPosZ() * 8, 0.5, (float)Force); - - if (!a_Player->IsGameModeCreative()) - { - a_Player->UseEquippedItem(); - } - } -} ; - - - - - diff --git a/source/Items/ItemBrewingStand.h b/source/Items/ItemBrewingStand.h deleted file mode 100644 index 4ff14d4b4..000000000 --- a/source/Items/ItemBrewingStand.h +++ /dev/null @@ -1,41 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemBrewingStandHandler : - public cItemHandler -{ -public: - cItemBrewingStandHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_BREWING_STAND; - a_BlockMeta = 0; - return true; - } -} ; - - - - diff --git a/source/Items/ItemBucket.h b/source/Items/ItemBucket.h deleted file mode 100644 index fa3d48da1..000000000 --- a/source/Items/ItemBucket.h +++ /dev/null @@ -1,160 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Simulator/FluidSimulator.h" -#include "../Blocks/BlockHandler.h" - - - - - -class cItemBucketHandler : - public cItemHandler -{ -public: - cItemBucketHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - - } - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - switch (m_ItemType) - { - case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir); - case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA); - case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER); - default: - { - ASSERT(!"Unhandled ItemType"); - return false; - } - } - } - - - - bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) - { - if (a_BlockFace < 0) - { - return false; - } - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - BLOCKTYPE ClickedBlock; - NIBBLETYPE ClickedMeta; - a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedMeta); - LOGD("Bucket Clicked BlockType %d, meta %d", ClickedBlock, ClickedMeta); - if (ClickedMeta != 0) - { - // Not a source block - return false; - } - - if (a_Player->GetGameMode() == gmCreative) - { - // In creative mode don't modify the inventory, just remove the fluid: - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - return true; - } - - ENUM_ITEM_ID NewItem = E_ITEM_EMPTY; - switch (ClickedBlock) - { - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - { - NewItem = E_ITEM_WATER_BUCKET; - break; - } - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - NewItem = E_ITEM_LAVA_BUCKET; - break; - } - - default: return false; - } - - // Remove the bucket from the inventory - if (!a_Player->GetInventory().RemoveOneEquippedItem()) - { - LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?"); - ASSERT(!"Inventory bucket mismatch"); - return true; - } - - // Give new bucket, filled with fluid: - cItem Item(NewItem, 1); - a_Player->GetInventory().AddItem(Item, true, true); - - // Remove water / lava block - a_Player->GetWorld()->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - return true; - } - - - bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_FluidBlock) - { - if (a_BlockFace < 0) - { - return false; - } - - BLOCKTYPE CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - bool CanWashAway = cFluidSimulator::CanWashAway(CurrentBlock); - if (!CanWashAway) - { - // The block pointed at cannot be washed away, so put fluid on top of it / on its sides - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - } - if ( - !CanWashAway && - (CurrentBlock != E_BLOCK_AIR) && - (CurrentBlock != E_BLOCK_WATER) && - (CurrentBlock != E_BLOCK_STATIONARY_WATER) && - (CurrentBlock != E_BLOCK_LAVA) && - (CurrentBlock != E_BLOCK_STATIONARY_LAVA) - ) - { - // Cannot place water here - return false; - } - - if (a_Player->GetGameMode() != gmCreative) - { - // Remove fluid bucket, add empty bucket: - if (!a_Player->GetInventory().RemoveOneEquippedItem()) - { - LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?"); - ASSERT(!"Inventory bucket mismatch"); - return false; - } - cItem Item(E_ITEM_BUCKET, 1); - if (!a_Player->GetInventory().AddItem(Item,true,true)) - { - return false; - } - } - - // Wash away anything that was there prior to placing: - if (CanWashAway) - { - cBlockHandler * Handler = BlockHandler(CurrentBlock); - if (Handler->DoesDropOnUnsuitable()) - { - Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); - } - } - - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FluidBlock, 0); - - return true; - } - -}; diff --git a/source/Items/ItemCauldron.h b/source/Items/ItemCauldron.h deleted file mode 100644 index 8b2ddc29f..000000000 --- a/source/Items/ItemCauldron.h +++ /dev/null @@ -1,41 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemCauldronHandler : - public cItemHandler -{ -public: - cItemCauldronHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_CAULDRON; - a_BlockMeta = 0; - return true; - } -} ; - - - - diff --git a/source/Items/ItemCloth.h b/source/Items/ItemCloth.h deleted file mode 100644 index aca27a299..000000000 --- a/source/Items/ItemCloth.h +++ /dev/null @@ -1,23 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemClothHandler : - public cItemHandler -{ -public: - cItemClothHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } -} ; - - - - diff --git a/source/Items/ItemComparator.h b/source/Items/ItemComparator.h deleted file mode 100644 index 53dbd020d..000000000 --- a/source/Items/ItemComparator.h +++ /dev/null @@ -1,40 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../Simulator/RedstoneSimulator.h" - - - - - -class cItemComparatorHandler : - public cItemHandler -{ -public: - cItemComparatorHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_INACTIVE_COMPARATOR; - a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); - return true; - } -} ; - - - - diff --git a/source/Items/ItemDoor.h b/source/Items/ItemDoor.h deleted file mode 100644 index 72ea0beed..000000000 --- a/source/Items/ItemDoor.h +++ /dev/null @@ -1,45 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" - - - - - -class cItemDoorHandler : - public cItemHandler -{ -public: - cItemDoorHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - - } - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = (m_ItemType == E_ITEM_WOODEN_DOOR) ? E_BLOCK_WOODEN_DOOR : E_BLOCK_IRON_DOOR; - return BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta( - a_World, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); - } -} ; - - - - diff --git a/source/Items/ItemDye.h b/source/Items/ItemDye.h deleted file mode 100644 index 99b8d2543..000000000 --- a/source/Items/ItemDye.h +++ /dev/null @@ -1,44 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -class cItemDyeHandler : - public cItemHandler -{ -public: - cItemDyeHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - // TODO: Handle coloring the sheep, too (OnItemUseOnEntity maybe) - - // Handle growing the plants: - if (a_Item.m_ItemDamage == E_META_DYE_WHITE) - { - if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true)) - { - if (a_Player->GetGameMode() != gmCreative) - { - a_Player->GetInventory().RemoveOneEquippedItem(); - return true; - } - } - } - return false; - } -} ; - - - - diff --git a/source/Items/ItemFlowerPot.h b/source/Items/ItemFlowerPot.h deleted file mode 100644 index befa2ff21..000000000 --- a/source/Items/ItemFlowerPot.h +++ /dev/null @@ -1,41 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemFlowerPotHandler : - public cItemHandler -{ -public: - cItemFlowerPotHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_FLOWER_POT; - a_BlockMeta = 0; - return true; - } -} ; - - - - diff --git a/source/Items/ItemFood.h b/source/Items/ItemFood.h deleted file mode 100644 index 2ae572331..000000000 --- a/source/Items/ItemFood.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemFoodHandler : - public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemFoodHandler(int a_ItemType) - : super(a_ItemType) - { - } - - - virtual bool IsFood(void) override - { - return true; - } - - - virtual FoodInfo GetFoodInfo(void) override - { - switch(m_ItemType) - { - // Please keep alpha-sorted. - case E_ITEM_BAKED_POTATO: return FoodInfo(6, 7.2); - case E_ITEM_BREAD: return FoodInfo(5, 6); - case E_ITEM_CARROT: return FoodInfo(4, 4.8); - case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2); - case E_ITEM_COOKED_FISH: return FoodInfo(5, 6); - case E_ITEM_COOKED_PORKCHOP: return FoodInfo(8, 12.8); - case E_ITEM_COOKIE: return FoodInfo(2, 0.4); - case E_ITEM_GOLDEN_APPLE: return FoodInfo(4, 9.6); - case E_ITEM_GOLDEN_CARROT: return FoodInfo(6, 14.4); - case E_ITEM_MELON_SLICE: return FoodInfo(2, 1.2); - case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2, 60); - case E_ITEM_POTATO: return FoodInfo(1, 0.6); - case E_ITEM_PUMPKIN_PIE: return FoodInfo(8, 4.8); - case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8); - case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2, 30); - case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2); - case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8); - case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4); - case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8, 80); - case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2, 100); - case E_ITEM_STEAK: return FoodInfo(8, 12.8); - case E_ITEM_MUSHROOM_SOUP: return FoodInfo(6, 7.2); - } - LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType); - return FoodInfo(0, 0.f); - } - -}; - - - - diff --git a/source/Items/ItemHandler.cpp b/source/Items/ItemHandler.cpp deleted file mode 100644 index 13f5293b9..000000000 --- a/source/Items/ItemHandler.cpp +++ /dev/null @@ -1,509 +0,0 @@ - -#include "Globals.h" -#include "ItemHandler.h" -#include "../Item.h" -#include "../World.h" -#include "../Entities/Player.h" -#include "../FastRandom.h" - -// Handlers: -#include "ItemBed.h" -#include "ItemBoat.h" -#include "ItemBow.h" -#include "ItemBrewingStand.h" -#include "ItemBucket.h" -#include "ItemCauldron.h" -#include "ItemCloth.h" -#include "ItemComparator.h" -#include "ItemDoor.h" -#include "ItemDye.h" -#include "ItemFlowerPot.h" -#include "ItemFood.h" -#include "ItemHoe.h" -#include "ItemLeaves.h" -#include "ItemLighter.h" -#include "ItemMinecart.h" -#include "ItemPickaxe.h" -#include "ItemThrowable.h" -#include "ItemRedstoneDust.h" -#include "ItemRedstoneRepeater.h" -#include "ItemSapling.h" -#include "ItemSeeds.h" -#include "ItemShears.h" -#include "ItemShovel.h" -#include "ItemSign.h" -#include "ItemSpawnEgg.h" -#include "ItemSugarcane.h" -#include "ItemSword.h" - -#include "../Blocks/BlockHandler.h" - - - - - -bool cItemHandler::m_HandlerInitialized = false; -cItemHandler * cItemHandler::m_ItemHandler[2268]; - - - - - -cItemHandler * cItemHandler::GetItemHandler(int a_ItemType) -{ - if (a_ItemType < 0) - { - // Either nothing (-1), or bad value, both cases should return the air handler - if (a_ItemType < -1) - { - ASSERT(!"Bad item type"); - } - a_ItemType = 0; - } - - if (!m_HandlerInitialized) - { - // We need to initialize - memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); - m_HandlerInitialized = true; - } - if (m_ItemHandler[a_ItemType] == NULL) - { - m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType); - } - return m_ItemHandler[a_ItemType]; -} - - - - - -cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) -{ - switch(a_ItemType) - { - default: return new cItemHandler(a_ItemType); - - // Single item per handler, alphabetically sorted: - case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); - case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); - case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); - case E_ITEM_BED: return new cItemBedHandler(a_ItemType); - case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType); - case E_ITEM_BOW: return new cItemBowHandler; - case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); - case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); - case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); - case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); - case E_ITEM_EGG: return new cItemEggHandler(); - case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); - case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); - case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); - case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); - case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); - case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); - case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); - case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); - case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); - case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); - - case E_ITEM_WOODEN_HOE: - case E_ITEM_STONE_HOE: - case E_ITEM_IRON_HOE: - case E_ITEM_GOLD_HOE: - case E_ITEM_DIAMOND_HOE: - { - return new cItemHoeHandler(a_ItemType); - } - - case E_ITEM_WOODEN_PICKAXE: - case E_ITEM_STONE_PICKAXE: - case E_ITEM_IRON_PICKAXE: - case E_ITEM_GOLD_PICKAXE: - case E_ITEM_DIAMOND_PICKAXE: - { - return new cItemPickaxeHandler(a_ItemType); - } - - case E_ITEM_WOODEN_SHOVEL: - case E_ITEM_STONE_SHOVEL: - case E_ITEM_IRON_SHOVEL: - case E_ITEM_GOLD_SHOVEL: - case E_ITEM_DIAMOND_SHOVEL: - { - return new cItemShovelHandler(a_ItemType); - } - - case E_ITEM_WOODEN_SWORD: - case E_ITEM_STONE_SWORD: - case E_ITEM_IRON_SWORD: - case E_ITEM_GOLD_SWORD: - case E_ITEM_DIAMOND_SWORD: - { - return new cItemSwordHandler(a_ItemType); - } - - case E_ITEM_BUCKET: - case E_ITEM_WATER_BUCKET: - case E_ITEM_LAVA_BUCKET: - { - return new cItemBucketHandler(a_ItemType); - } - - case E_ITEM_CARROT: - case E_ITEM_MELON_SEEDS: - case E_ITEM_POTATO: - case E_ITEM_PUMPKIN_SEEDS: - case E_ITEM_SEEDS: - { - return new cItemSeedsHandler(a_ItemType); - } - - case E_ITEM_IRON_DOOR: - case E_ITEM_WOODEN_DOOR: - { - return new cItemDoorHandler(a_ItemType); - } - - case E_ITEM_MINECART: - case E_ITEM_CHEST_MINECART: - case E_ITEM_FURNACE_MINECART: - case E_ITEM_MINECART_WITH_TNT: - case E_ITEM_MINECART_WITH_HOPPER: - { - return new cItemMinecartHandler(a_ItemType); - } - - // Food: - case E_ITEM_BREAD: - case E_ITEM_COOKIE: - case E_ITEM_MELON_SLICE: - case E_ITEM_RAW_CHICKEN: - case E_ITEM_COOKED_CHICKEN: - case E_ITEM_RAW_BEEF: - case E_ITEM_RAW_PORKCHOP: - case E_ITEM_STEAK: - case E_ITEM_COOKED_PORKCHOP: - case E_ITEM_RAW_FISH: - case E_ITEM_COOKED_FISH: - case E_ITEM_RED_APPLE: - case E_ITEM_GOLDEN_APPLE: - case E_ITEM_ROTTEN_FLESH: - case E_ITEM_MUSHROOM_SOUP: - case E_ITEM_SPIDER_EYE: - { - return new cItemFoodHandler(a_ItemType); - } - } -} - - - - - -void cItemHandler::Deinit() -{ - for(int i = 0; i < 2267; i++) - { - delete m_ItemHandler[i]; - } - memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); // Don't leave any dangling pointers around, just in case - m_HandlerInitialized = false; -} - - - - - -cItemHandler::cItemHandler(int a_ItemType) -{ - m_ItemType = a_ItemType; -} - - - - - -bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) -{ - return false; -} - - - - - -bool cItemHandler::OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) -{ - return false; -} - - - - - -void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block); - - if (a_Player->IsGameModeSurvival()) - { - if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) - { - Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); - } - } - - a_Player->UseEquippedItem(); -} - - - - - -void cItemHandler::OnFoodEaten(cWorld * a_World, cPlayer * a_Player, cItem * a_Item) -{ - -} - - - - - -char cItemHandler::GetMaxStackSize(void) -{ - if (m_ItemType < 256) - { - // All blocks can stack up to 64 - return 64; - } - - switch (m_ItemType) //sorted by id - { - case E_ITEM_ARROW: return 64; - case E_ITEM_BAKED_POTATO: return 64; - case E_ITEM_BLAZE_POWDER: return 64; - case E_ITEM_BLAZE_ROD: return 64; - case E_ITEM_BONE: return 64; - case E_ITEM_BOOK: return 64; - case E_ITEM_BOTTLE_O_ENCHANTING: return 64; - case E_ITEM_BOWL: return 64; - case E_ITEM_BREAD: return 64; - case E_ITEM_BREWING_STAND: return 64; - case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3 - case E_ITEM_CARROT: return 64; - case E_ITEM_CAULDRON: return 64; - case E_ITEM_CLAY: return 64; - case E_ITEM_CLAY_BRICK: return 64; - case E_ITEM_CLOCK: return 64; - case E_ITEM_COAL: return 64; - case E_ITEM_COMPARATOR: return 64; - case E_ITEM_COMPASS: return 64; - case E_ITEM_COOKED_CHICKEN: return 64; - case E_ITEM_COOKED_FISH: return 64; - case E_ITEM_COOKED_PORKCHOP: return 64; - case E_ITEM_COOKIE: return 64; - case E_ITEM_DIAMOND: return 64; - case E_ITEM_DYE: return 64; - case E_ITEM_EGG: return 16; - case E_ITEM_EMERALD: return 64; - case E_ITEM_ENDER_PEARL: return 16; - case E_ITEM_EYE_OF_ENDER: return 64; - case E_ITEM_FEATHER: return 64; - case E_ITEM_FERMENTED_SPIDER_EYE: return 64; - case E_ITEM_FIRE_CHARGE: return 64; - case E_ITEM_FIREWORK_ROCKET: return 64; - case E_ITEM_FIREWORK_STAR: return 64; - case E_ITEM_FLINT: return 64; - case E_ITEM_FLOWER_POT: return 64; - case E_ITEM_GHAST_TEAR: return 64; - case E_ITEM_GLASS_BOTTLE: return 64; - case E_ITEM_GLISTERING_MELON: return 64; - case E_ITEM_GLOWSTONE_DUST: return 64; - case E_ITEM_GOLD: return 64; - case E_ITEM_GOLDEN_APPLE: return 64; - case E_ITEM_GOLDEN_CARROT: return 64; - case E_ITEM_GOLD_NUGGET: return 64; - case E_ITEM_GUNPOWDER: return 64; - case E_ITEM_HEAD: return 64; - case E_ITEM_IRON: return 64; - case E_ITEM_LEATHER: return 64; - case E_ITEM_MAGMA_CREAM: return 64; - case E_ITEM_MAP: return 64; - case E_ITEM_MELON_SEEDS: return 64; - case E_ITEM_MELON_SLICE: return 64; - case E_ITEM_NETHER_BRICK: return 64; - case E_ITEM_NETHER_WART: return 64; - case E_ITEM_PAINTINGS: return 64; - case E_ITEM_PAPER: return 64; - case E_ITEM_POISONOUS_POTATO: return 64; - case E_ITEM_POTATO: return 64; - case E_ITEM_PUMPKIN_PIE: return 64; - case E_ITEM_PUMPKIN_SEEDS: return 64; - case E_ITEM_RAW_BEEF: return 64; - case E_ITEM_RAW_CHICKEN: return 64; - case E_ITEM_RAW_FISH: return 64; - case E_ITEM_RAW_PORKCHOP: return 64; - case E_ITEM_RED_APPLE: return 64; - case E_ITEM_REDSTONE_DUST: return 64; - case E_ITEM_REDSTONE_REPEATER: return 64; - case E_ITEM_ROTTEN_FLESH: return 64; - case E_ITEM_SEEDS: return 64; - case E_ITEM_SIGN: return 16; - case E_ITEM_SLIMEBALL: return 64; - case E_ITEM_SNOWBALL: return 16; - case E_ITEM_SPAWN_EGG: return 64; - case E_ITEM_SPIDER_EYE: return 64; - case E_ITEM_STEAK: return 64; - case E_ITEM_STICK: return 64; - case E_ITEM_STRING: return 64; - case E_ITEM_SUGAR: return 64; - case E_ITEM_SUGAR_CANE: return 64; - case E_ITEM_WHEAT: return 64; - } - // By default items don't stack: - return 1; -} - - - - - -bool cItemHandler::IsTool() -{ - // TODO: Rewrite this to list all tools specifically - return - (m_ItemType >= 256 && m_ItemType <= 259) - || (m_ItemType == 261) - || (m_ItemType >= 267 && m_ItemType <= 279) - || (m_ItemType >= 283 && m_ItemType <= 286) - || (m_ItemType >= 290 && m_ItemType <= 294) - || (m_ItemType >= 256 && m_ItemType <= 259) - || (m_ItemType == 325) - || (m_ItemType == 346); -} - - - - - -bool cItemHandler::IsFood(void) -{ - switch (m_ItemType) - { - case E_ITEM_RED_APPLE: - case E_ITEM_GOLDEN_APPLE: - case E_ITEM_MUSHROOM_SOUP: - case E_ITEM_BREAD: - case E_ITEM_RAW_PORKCHOP: - case E_ITEM_COOKED_PORKCHOP: - case E_ITEM_MILK: - case E_ITEM_RAW_FISH: - case E_ITEM_COOKED_FISH: - case E_ITEM_COOKIE: - case E_ITEM_MELON_SLICE: - case E_ITEM_RAW_BEEF: - case E_ITEM_STEAK: - case E_ITEM_RAW_CHICKEN: - case E_ITEM_COOKED_CHICKEN: - case E_ITEM_ROTTEN_FLESH: - case E_ITEM_SPIDER_EYE: - case E_ITEM_CARROT: - case E_ITEM_POTATO: - case E_ITEM_BAKED_POTATO: - case E_ITEM_POISONOUS_POTATO: - { - return true; - } - } // switch (m_ItemType) - return false; -} - - - - - -bool cItemHandler::IsPlaceable(void) -{ - // We can place any block that has a corresponding E_BLOCK_TYPE: - return (m_ItemType >= 1) && (m_ItemType <= E_BLOCK_MAX_TYPE_ID); -} - - - - - -bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType) -{ - return false; -} - - - - - -bool cItemHandler::GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta -) -{ - ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers - - if (m_ItemType > 256) - { - LOGERROR("%s: Item %d has no valid block!", __FUNCTION__, m_ItemType); - return false; - } - - cBlockHandler * BlockH = BlockHandler(m_ItemType); - return BlockH->GetPlacementBlockTypeMeta( - a_World, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); -} - - - - - -bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item) -{ - FoodInfo Info = GetFoodInfo(); - - if ((Info.FoodLevel > 0) || (Info.Saturation > 0.f)) - { - bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation); - - // If consumed and there's chance of foodpoisoning, do it: - if (Success && (Info.PoisonChance > 0)) - { - cFastRandom r1; - if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0) - { - a_Player->FoodPoison(300); - } - } - - return Success; - } - - return false; -} - - - - - -cItemHandler::FoodInfo cItemHandler::GetFoodInfo() -{ - return FoodInfo(0, 0.f); -} - - - - diff --git a/source/Items/ItemHandler.h b/source/Items/ItemHandler.h deleted file mode 100644 index e39bb054b..000000000 --- a/source/Items/ItemHandler.h +++ /dev/null @@ -1,99 +0,0 @@ - -#pragma once - -#include "../Defines.h" -#include "../Item.h" - - - - - -// fwd: -class cWorld; -class cPlayer; - - - - - -class cItemHandler -{ -public: - cItemHandler(int a_ItemType); - - /// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); - - /// Called when the client sends the SHOOT status in the lclk packet - virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) {} - - /// Called while the player diggs a block using this item - virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace); - - /// Called when the player destroys a block using this item. This also calls the drop function for the destroyed block - virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z); - - /// Called after the player has eaten this item. - virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item); - - /// Returns the maximum stack size for a given item - virtual char GetMaxStackSize(void); - - struct FoodInfo - { - int FoodLevel; - double Saturation; - int PoisonChance; // 0 - 100, in percent. 0 = no chance of poisoning, 100 = sure poisoning - - FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) : - FoodLevel(a_FoodLevel), - Saturation(a_Saturation), - PoisonChance(a_PoisonChance) - { - } - } ; - - /// Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance) - virtual FoodInfo GetFoodInfo(); - - /// Lets the player eat a selected item. Returns true if the player ate the item - virtual bool EatItem(cPlayer *a_Player, cItem *a_Item); - - /// Indicates if this item is a tool - virtual bool IsTool(void); - - /// Indicates if this item is food - virtual bool IsFood(void); - - /// Blocks simply get placed - virtual bool IsPlaceable(void); - - /** Called before a block is placed into a world. - The handler should return true to allow placement, false to refuse. - Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. - */ - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ); - - /// Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can´t) DEFAULT: False - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType); - - static cItemHandler * GetItemHandler(int a_ItemType); - static cItemHandler * GetItemHandler(const cItem & a_Item) { return GetItemHandler(a_Item.m_ItemType); } - - static void Deinit(); - -protected: - int m_ItemType; - static cItemHandler *CreateItemHandler(int m_ItemType); - - static cItemHandler * m_ItemHandler[E_ITEM_LAST + 1]; - static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized -}; - -//Short function -inline cItemHandler *ItemHandler(int a_ItemType) { return cItemHandler::GetItemHandler(a_ItemType); } diff --git a/source/Items/ItemHoe.h b/source/Items/ItemHoe.h deleted file mode 100644 index 7b6b3e6ac..000000000 --- a/source/Items/ItemHoe.h +++ /dev/null @@ -1,40 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -class cItemHoeHandler : - public cItemHandler -{ -public: - cItemHoeHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - - if ((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS)) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0); - - a_Player->UseEquippedItem(); - return true; - - } - return false; - } -} ; - - - - diff --git a/source/Items/ItemLeaves.h b/source/Items/ItemLeaves.h deleted file mode 100644 index 60222eaa9..000000000 --- a/source/Items/ItemLeaves.h +++ /dev/null @@ -1,41 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemLeavesHandler : - public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemLeavesHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - bool res = super::GetPlacementBlockTypeMeta( - a_World, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); - a_BlockMeta = a_BlockMeta | 0x4; //0x4 bit set means this is a player-placed leaves block, not to be decayed - return res; - } -} ; - - - - diff --git a/source/Items/ItemLighter.h b/source/Items/ItemLighter.h deleted file mode 100644 index 4281a2d0c..000000000 --- a/source/Items/ItemLighter.h +++ /dev/null @@ -1,59 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Entities/Player.h" -#include "../Entities/TNTEntity.h" - - - - - -class cItemLighterHandler : - public cItemHandler -{ -public: - cItemLighterHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override - { - if (a_BlockFace < 0) - { - return false; - } - - a_Player->UseEquippedItem(); - - switch (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) - { - case E_BLOCK_TNT: - { - // Activate the TNT: - a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); - a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom - a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0); - break; - } - default: - { - // Light a fire next to/on top of the block if air: - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) - { - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0); - break; - } - } - } - - return false; - } -} ; - - - - diff --git a/source/Items/ItemMinecart.h b/source/Items/ItemMinecart.h deleted file mode 100644 index f8eb31a49..000000000 --- a/source/Items/ItemMinecart.h +++ /dev/null @@ -1,82 +0,0 @@ - -// ItemMinecart.h - -// Declares the various minecart ItemHandlers - - - - - -#pragma once - -#include "../Entities/Minecart.h" - - - - - -class cItemMinecartHandler : - public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemMinecartHandler(int a_ItemType) : - super(a_ItemType) - { - } - - - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - if (a_Dir < 0) - { - return false; - } - - // Check that there's rail in there: - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - switch (Block) - { - case E_BLOCK_MINECART_TRACKS: - case E_BLOCK_POWERED_RAIL: - case E_BLOCK_DETECTOR_RAIL: - case E_BLOCK_ACTIVATOR_RAIL: - { - // These are allowed - break; - } - default: - { - LOGD("Used minecart on an unsuitable block %d (%s)", Block, ItemTypeToString(Block).c_str()); - return false; - } - } - - double x = (double)a_BlockX + 0.5; - double y = (double)a_BlockY + 0.5; - double z = (double)a_BlockZ + 0.5; - cMinecart * Minecart = NULL; - switch (m_ItemType) - { - case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break; - case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break; - case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace (x, y, z); break; - case E_ITEM_MINECART_WITH_TNT: Minecart = new cMinecartWithTNT (x, y, z); break; - case E_ITEM_MINECART_WITH_HOPPER: Minecart = new cMinecartWithHopper (x, y, z); break; - default: - { - ASSERT(!"Unhandled minecart item"); - return false; - } - } // switch (m_ItemType) - Minecart->Initialize(a_World); - return true; - } - -} ; - - - - diff --git a/source/Items/ItemPickaxe.h b/source/Items/ItemPickaxe.h deleted file mode 100644 index bde7f0905..000000000 --- a/source/Items/ItemPickaxe.h +++ /dev/null @@ -1,92 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - -class cItemPickaxeHandler : - public cItemHandler -{ -public: - cItemPickaxeHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - char PickaxeLevel() - { - switch(m_ItemType) - { - case E_ITEM_WOODEN_PICKAXE: - case E_ITEM_GOLD_PICKAXE: - return 1; - case E_ITEM_STONE_PICKAXE: - return 2; - case E_ITEM_IRON_PICKAXE: - return 3; - case E_ITEM_DIAMOND_PICKAXE: - return 4; - default: - return 0; - } - } - - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override - { - switch(a_BlockType) - { - case E_BLOCK_OBSIDIAN: - { - return PickaxeLevel() >= 4; - } - - case E_BLOCK_DIAMOND_BLOCK: - case E_BLOCK_DIAMOND_ORE: - case E_BLOCK_GOLD_BLOCK: - case E_BLOCK_GOLD_ORE: - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - case E_BLOCK_EMERALD_ORE: - { - return PickaxeLevel() >= 3; - } - - case E_BLOCK_IRON_BLOCK: - case E_BLOCK_IRON_ORE: - case E_BLOCK_LAPIS_ORE: - case E_BLOCK_LAPIS_BLOCK: - { - return PickaxeLevel() >= 2; - } - - case E_BLOCK_COAL_ORE: - case E_BLOCK_STONE: - case E_BLOCK_COBBLESTONE: - case E_BLOCK_END_STONE: - case E_BLOCK_MOSSY_COBBLESTONE: - case E_BLOCK_SANDSTONE_STAIRS: - case E_BLOCK_SANDSTONE: - case E_BLOCK_STONE_BRICKS: - case E_BLOCK_NETHER_BRICK: - case E_BLOCK_NETHERRACK: - case E_BLOCK_STONE_SLAB: - case E_BLOCK_DOUBLE_STONE_SLAB: - case E_BLOCK_STONE_PRESSURE_PLATE: - case E_BLOCK_BRICK: - case E_BLOCK_COBBLESTONE_STAIRS: - case E_BLOCK_STONE_BRICK_STAIRS: - case E_BLOCK_NETHER_BRICK_STAIRS: - case E_BLOCK_CAULDRON: - { - return PickaxeLevel() >= 1; - } - } - return false; - } -} ; - - - - diff --git a/source/Items/ItemRedstoneDust.h b/source/Items/ItemRedstoneDust.h deleted file mode 100644 index b7860b187..000000000 --- a/source/Items/ItemRedstoneDust.h +++ /dev/null @@ -1,38 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemRedstoneDustHandler : public cItemHandler -{ -public: - cItemRedstoneDustHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - } - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_REDSTONE_WIRE; - a_BlockMeta = 0; - return true; - } -} ; - - - - diff --git a/source/Items/ItemRedstoneRepeater.h b/source/Items/ItemRedstoneRepeater.h deleted file mode 100644 index 459070579..000000000 --- a/source/Items/ItemRedstoneRepeater.h +++ /dev/null @@ -1,40 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../Simulator/RedstoneSimulator.h" - - - - - -class cItemRedstoneRepeaterHandler : - public cItemHandler -{ -public: - cItemRedstoneRepeaterHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - } - - virtual bool IsPlaceable() override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF; - a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); - return true; - } -} ; - - - - diff --git a/source/Items/ItemSapling.h b/source/Items/ItemSapling.h deleted file mode 100644 index dc0810a45..000000000 --- a/source/Items/ItemSapling.h +++ /dev/null @@ -1,42 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemSaplingHandler : public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemSaplingHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - bool res = super::GetPlacementBlockTypeMeta( - a_World, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); - // Only the lowest 3 bits are important - a_BlockMeta = a_BlockMeta & 0x7; - return res; - } -} ; - - - - diff --git a/source/Items/ItemSeeds.h b/source/Items/ItemSeeds.h deleted file mode 100644 index 8ca86663f..000000000 --- a/source/Items/ItemSeeds.h +++ /dev/null @@ -1,65 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" - - - - - -class cItemSeedsHandler : - public cItemHandler -{ -public: - cItemSeedsHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - - } - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - if (a_BlockFace != BLOCK_FACE_TOP) - { - // Only allow planting seeds from the top side of the block - return false; - } - - // Only allow placement on farmland - int X = a_BlockX; - int Y = a_BlockY; - int Z = a_BlockZ; - AddFaceDirection(X, Y, Z, a_BlockFace, true); - if (a_World->GetBlock(X, Y, Z) != E_BLOCK_FARMLAND) - { - return false; - } - - a_BlockMeta = 0; - switch (m_ItemType) - { - case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true; - case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true; - case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true; - case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true; - case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true; - default: a_BlockType = E_BLOCK_AIR; return true; - } - return false; - } -} ; - - - - diff --git a/source/Items/ItemShears.h b/source/Items/ItemShears.h deleted file mode 100644 index 6a17607ee..000000000 --- a/source/Items/ItemShears.h +++ /dev/null @@ -1,62 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -class cItemShearsHandler : - public cItemHandler -{ -public: - cItemShearsHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsTool(void) override - { - return true; - } - - - virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (Block == E_BLOCK_LEAVES) - { - cItems Drops; - Drops.push_back(cItem(E_BLOCK_LEAVES, 1, a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x03)); - a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); - - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_Player->UseEquippedItem(); - return true; - } - return false; - } - - - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override - { - switch (a_BlockType) - { - case E_BLOCK_COBWEB: - case E_BLOCK_VINES: - case E_BLOCK_LEAVES: - { - return true; - } - } // switch (a_BlockType) - return false; - } -} ; - - - - diff --git a/source/Items/ItemShovel.h b/source/Items/ItemShovel.h deleted file mode 100644 index d0625ef1c..000000000 --- a/source/Items/ItemShovel.h +++ /dev/null @@ -1,41 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - -#include "../Blocks/BlockHandler.h" - - - - - -class cItemShovelHandler : public cItemHandler -{ -public: - cItemShovelHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (Block == E_BLOCK_SNOW) - { - BlockHandler(Block)->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); - - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_Player->UseEquippedItem(); - return true; - } - return false; - } - - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override - { - return (a_BlockType == E_BLOCK_SNOW); - } -}; \ No newline at end of file diff --git a/source/Items/ItemSign.h b/source/Items/ItemSign.h deleted file mode 100644 index 5ccd79e29..000000000 --- a/source/Items/ItemSign.h +++ /dev/null @@ -1,51 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Blocks/BlockSign.h" - - - - - -class cItemSignHandler : - public cItemHandler -{ -public: - cItemSignHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - if (a_BlockFace == BLOCK_FACE_TOP) - { - a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation()); - a_BlockType = E_BLOCK_SIGN_POST; - } - else - { - a_BlockMeta = cBlockSignHandler::DirectionToMetaData(a_BlockFace); - a_BlockType = E_BLOCK_WALLSIGN; - } - return true; - } -} ; - - - - diff --git a/source/Items/ItemSpawnEgg.h b/source/Items/ItemSpawnEgg.h deleted file mode 100644 index 26dd15b7d..000000000 --- a/source/Items/ItemSpawnEgg.h +++ /dev/null @@ -1,52 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -class cItemSpawnEggHandler : public cItemHandler -{ -public: - cItemSpawnEggHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - - } - - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override - { - if (a_BlockFace < 0) - { - return false; - } - - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - if (a_BlockFace == BLOCK_FACE_BOTTOM) - { - a_BlockY--; - } - - if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, (cMonster::eType)(a_Item.m_ItemDamage)) >= 0) - { - if (a_Player->GetGameMode() != 1) - { - // The mob was spawned, "use" the item: - a_Player->GetInventory().RemoveOneEquippedItem(); - } - return true; - } - - return false; - } -} ; - - - - diff --git a/source/Items/ItemSugarcane.h b/source/Items/ItemSugarcane.h deleted file mode 100644 index ce93aa3e5..000000000 --- a/source/Items/ItemSugarcane.h +++ /dev/null @@ -1,39 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemSugarcaneHandler : - public cItemHandler -{ -public: - cItemSugarcaneHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_SUGARCANE; - a_BlockMeta = 0; - return true; - } -} ; - - - - diff --git a/source/Items/ItemSword.h b/source/Items/ItemSword.h deleted file mode 100644 index a7c1d2432..000000000 --- a/source/Items/ItemSword.h +++ /dev/null @@ -1,30 +0,0 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -class cItemSwordHandler : - public cItemHandler -{ -public: - cItemSwordHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override - { - return (a_BlockType == E_BLOCK_COBWEB); - } -} ; - - - - diff --git a/source/Items/ItemThrowable.h b/source/Items/ItemThrowable.h deleted file mode 100644 index 85579daf2..000000000 --- a/source/Items/ItemThrowable.h +++ /dev/null @@ -1,96 +0,0 @@ - -// ItemThrowable.h - -// Declares the itemhandlers for throwable items: eggs, snowballs and ender pearls - - - - - -#pragma once - - - - - -class cItemThrowableHandler : - public cItemHandler -{ - typedef cItemHandler super; -public: - cItemThrowableHandler(int a_ItemType, cProjectileEntity::eKind a_ProjectileKind, double a_SpeedCoeff) : - super(a_ItemType), - m_ProjectileKind(a_ProjectileKind), - m_SpeedCoeff(a_SpeedCoeff) - { - } - - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - if (!a_Player->IsGameModeCreative()) - { - a_Player->GetInventory().RemoveOneEquippedItem(); - } - - Vector3d Pos = a_Player->GetThrowStartPos(); - Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff; - a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &Speed); - - return true; - } - -protected: - cProjectileEntity::eKind m_ProjectileKind; - double m_SpeedCoeff; -} ; - - - - - -class cItemEggHandler : - public cItemThrowableHandler -{ - typedef cItemThrowableHandler super; -public: - cItemEggHandler(void) : - super(E_ITEM_EGG, cProjectileEntity::pkEgg, 30) - { - } -} ; - - - - -class cItemSnowballHandler : - public cItemThrowableHandler -{ - typedef cItemThrowableHandler super; - -public: - cItemSnowballHandler(void) : - super(E_ITEM_SNOWBALL, cProjectileEntity::pkSnowball, 30) - { - } -} ; - - - - - -class cItemEnderPearlHandler : - public cItemThrowableHandler -{ - typedef cItemThrowableHandler super; - -public: - cItemEnderPearlHandler(void) : - super(E_ITEM_ENDER_PEARL, cProjectileEntity::pkEnderPearl, 30) - { - } -} ; - - - - diff --git a/source/LeakFinder.cpp b/source/LeakFinder.cpp deleted file mode 100644 index 0f84adb2b..000000000 --- a/source/LeakFinder.cpp +++ /dev/null @@ -1,1047 +0,0 @@ - -// LeakFinder.cpp - -// Finds memory leaks rather effectively - -// _X: downloaded from http://www.codeproject.com/Articles/3134/Memory-Leak-and-Exception-Trace-CRT-and-COM-Leaks - the real link is in the comments, RC11 version - - - - - -/********************************************************************** - * - * LEAKFINDER.CPP - * - * - * - * History: - * 2010-04-15 RC10 - Updated to VC10 RTM - * Fixed Bug: Application Verifier, thanks to handsinmypocket! - * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3439751#xx3439751xx - * 2008-08-04 RC6 - Updated to VC9 RTM - * Fixed Bug: Missing "ole32.lib" LIB - * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2253980#xx2253980xx - * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN" - * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx - * Fixed Bug: Compiling with "/Wall" - * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx - * Removed "#pragma init_seg (compiler)" from h-file - * - * 2005-12-30 RC5 - Now again VC8 RTM compatible - * - Added Xml-Output (like in the old Leakfinder) - * YOu need to define XML_LEAK_FINDER to activate it - * So you can use the LeakAnalyseTool from - * http://www.codeproject.com/tools/leakfinder.asp - * - * 2005-12-13 RC4 - Merged with the new "StackWalker"-project on - * http://www.codeproject.com/threads/StackWalker.asp - * - * 2005-08-01 RC3 - Merged with the new "StackWalker"-project on - * http://www.codeproject.com/threads/StackWalker.asp - * - * 2005-07-05 RC2 - First version with x86, IA64 and x64 support - * - * 2005-07-04 RC1 - Added "OutputOptions" - * - New define "INIT_LEAK_FINDER_VERBOSE" to - * display more info (for error reporting) - * - * 2005-07-01 Beta3 - Workaround for a bug in the new dbghelp.dll - * (version 6.5.3.7 from 2005-05-30; StakWalk64 no - * refused to produce an callstack on x86 systems - * if the context is NULL or has some registers set - * to 0 (for example Esp). This is against the - * documented behaviour of StackWalk64...) - * - First version with x64-support - * - * 2005-06-16 Beta1 First public release with the following features: - * - Completely rewritten in C++ (object oriented) - * - CRT-Leak-Report - * - COM-Leak-Report - * - Report is done via "OutputDebugString" so - * the line can directly selected in the debugger - * and is opening the corresponding file/line of - * the allocation - * - Tried to support x64 systems, bud had some - * trouble wih StackWalk64 - * See: http://blog.kalmbachnet.de/?postid=43 - * - * LICENSE (http://www.opensource.org/licenses/bsd-license.php) - * - * Copyright (c) 2005-2010, Jochen Kalmbach - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of Jochen Kalmbach nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - **********************************************************************/ - -#include -#include // Needed if compiled with "WIN32_LEAN_AND_MEAN" -#include -#include -#include - -#include -#include - - -#include "LeakFinder.h" - -// Currently only tested with MS VC++ 5 to 10 -#if (_MSC_VER < 1100) || (_MSC_VER > 1800) -#error Only MS VC++ 5/6/7/7.1/8/9/10/11/12 supported. Check if the '_CrtMemBlockHeader' has not changed with this compiler! -#endif - - -/* _X: MSVC 2012 (MSC 1700) seems to use a different allocation scheme for STL containers, -* allocating lots of small objects and running out of memory very soon -* Thus for MSVC 2012 we cut the callstack buffer length in half -* -* _X 2013_08_25: The callstack tracking gets worse even for MSVC 2008, a single lua_state eats 50 MiB of RAM -* Therefore I decided to further reduce the buffers from 0x2000 to 0x1000 -*/ -// Controlling the callstack depth -#if (_MSC_VER < 1700) - #define MAX_CALLSTACK_LEN_BUF 0x1000 -#else - #define MAX_CALLSTACK_LEN_BUF 0x0800 -#endif - - - - - -#define IGNORE_CRT_ALLOC - -// disable 64-bit compatibility-checks (because we explicite have here either x86 or x64!) -#pragma warning(disable:4312) // warning C4312: 'type cast' : conversion from 'DWORD' to 'LPCVOID' of greater size -#pragma warning(disable:4826) - - -// secure-CRT_functions are only available starting with VC8 -#if _MSC_VER < 1400 -#define _snprintf_s _snprintf -#define _tcscat_s _tcscat -#endif - - - - - -static std::string SimpleXMLEncode(LPCSTR szText) -{ - std::string szRet; - for (size_t i=0; i': - szRet.append(">"); - break; - case '"': - szRet.append("""); - break; - case '\'': - szRet.append("'"); - break; - default: - szRet += szText[i]; - } - } - return szRet; -} - - - - - -LeakFinderOutput::LeakFinderOutput(int options, LPCSTR szSymPath) - : StackWalker(options, szSymPath) -{ -} - - - - - -void LeakFinderOutput::OnLeakSearchStart(LPCSTR szLeakFinderName) -{ - CHAR buffer[1024]; - _snprintf_s(buffer, 1024, "######## %s ########\n", szLeakFinderName); - this->OnOutput(buffer); -} - - - - - -void LeakFinderOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize) -{ - CHAR buffer[1024]; - _snprintf_s(buffer, 1024, "--------------- Key: %s, %d bytes ---------\n", szKeyName, nDataSize); - this->OnOutput(buffer); -} - - - - - -void LeakFinderOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) -{ - if ( (eType != lastEntry) && (entry.offset != 0) ) - { - if ( ((this->m_options & LeakFinderShowCompleteCallstack) == 0) && ( - (strstr(entry.lineFileName, "afxmem.cpp") != NULL) || - (strstr(entry.lineFileName, "dbgheap.c") != NULL) || - (strstr(entry.lineFileName, "new.cpp") != NULL) || - (strstr(entry.lineFileName, "newop.cpp") != NULL) || - (strstr(entry.lineFileName, "leakfinder.cpp") != NULL) || - (strstr(entry.lineFileName, "stackwalker.cpp") != NULL) - ) ) - { - return; - } - } - StackWalker::OnCallstackEntry(eType, entry); -} - - - - - -// #################################################################### -// XML-Output -LeakFinderXmlOutput::LeakFinderXmlOutput() -{ - TCHAR szXMLFileName[1024]; - - GetModuleFileName(NULL, szXMLFileName, sizeof(szXMLFileName) / sizeof(TCHAR)); - _tcscat_s(szXMLFileName, _T(".mem.xml-leaks")); -#if _MSC_VER < 1400 - m_fXmlFile = _tfopen(szXMLFileName, _T("w")); -#else - m_fXmlFile = NULL; - _tfopen_s(&m_fXmlFile, szXMLFileName, _T("w")); -#endif - if (m_fXmlFile != NULL) - { - SYSTEMTIME st; - GetLocalTime(&st); - fprintf(m_fXmlFile, "\n", - st.wMonth, st.wDay, st.wYear, - st.wHour, st.wMinute, st.wSecond); - } - else - { - MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND); - } -} - - - - - -LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName) : - m_Progress(10) -{ -#if _MSC_VER < 1400 - m_fXmlFile = _tfopen(szFileName, _T("w")); -#else - m_fXmlFile = NULL; - _tfopen_s(&m_fXmlFile, szFileName, _T("w")); -#endif - if (m_fXmlFile == NULL) - { - MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND); - } - else - { - fprintf(m_fXmlFile, "\n"); - } -} - - - - - -LeakFinderXmlOutput::~LeakFinderXmlOutput() -{ - if (m_fXmlFile != NULL) - { - // Write the ending-tags and close the file - fprintf(m_fXmlFile, "\n"); - fclose(m_fXmlFile); - } - m_fXmlFile = NULL; -} - - - - - -void LeakFinderXmlOutput::OnLeakSearchStart(LPCSTR sszLeakFinderName) -{ -} - - - - - -void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize) -{ - if (m_fXmlFile != NULL) - { - fprintf(m_fXmlFile, "\t\n", SimpleXMLEncode(szKeyName).c_str(), nDataSize); - } - if (--m_Progress == 0) - { - m_Progress = 100; - putc('.', stdout); - } -} - - - - - -void LeakFinderXmlOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) -{ - if (m_fXmlFile != NULL) - { - if (eType != lastEntry) - { - fprintf(m_fXmlFile, "\t\t\n"); - } - else - { - fprintf(m_fXmlFile, "\t\n"); - } - } -} - - - - - -// ########################################################################## -// ########################################################################## -// ########################################################################## -// Base class for storing contexts in a hashtable -template class ContextHashtableBase -{ -public: - ContextHashtableBase(SIZE_T sizeOfHastable, LPCSTR finderName) - { - SIZE_T s = sizeOfHastable*sizeof(AllocHashEntryType); - m_hHeap = HeapCreate(0, 10*1024 + s, 0); - if (m_hHeap == NULL) - throw; - pAllocHashTable = (AllocHashEntryType*) own_malloc(s); - sAllocEntries = sizeOfHastable; - m_finderName = own_strdup(finderName); - } - -protected: - virtual ~ContextHashtableBase() - { - if (pAllocHashTable != NULL) - own_free(pAllocHashTable); - pAllocHashTable = NULL; - - own_free(m_finderName); - m_finderName = NULL; - - if (m_hHeap != NULL) - HeapDestroy(m_hHeap); - } - - __inline LPVOID own_malloc(SIZE_T size) - { - return HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size); - } - __inline VOID own_free(LPVOID memblock) - { - HeapFree(m_hHeap, 0, memblock); - } - __inline CHAR *own_strdup(const char *str) - { - size_t len = strlen(str)+1; - CHAR *c = (CHAR*)own_malloc(len); -#if _MSC_VER >= 1400 - strcpy_s(c, len, str); -#else - strcpy(c, str); -#endif - return c; - } - - // Disables this leak-finder - virtual LONG Disable() = 0; - // enables the leak-finder again... - virtual LONG Enable() = 0; - -protected: - // Entry for each allocation - typedef struct AllocHashEntryType { - HASHTABLE_KEY key; - SIZE_T nDataSize; // Size of the allocated memory - struct AllocHashEntryType *Next; - CONTEXT c; - PVOID pStackBaseAddr; - SIZE_T nMaxStackSize; - - PVOID pCallstackOffset; - SIZE_T nCallstackLen; - char pcCallstackAddr[MAX_CALLSTACK_LEN_BUF]; // min of both values... - } AllocHashEntryType; - -protected: - virtual SIZE_T HashFunction(HASHTABLE_KEY &key) = 0; - virtual BOOL IsKeyEmpty(HASHTABLE_KEY &key) = 0; - virtual VOID SetEmptyKey(HASHTABLE_KEY &key) = 0; - virtual VOID GetKeyAsString(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) = 0; - //virtual SIZE_T GetNativeBytes(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) { return 0; } - -public: - VOID Insert(HASHTABLE_KEY &key, CONTEXT &context, SIZE_T nDataSize) - { - SIZE_T HashIdx; - AllocHashEntryType *pHashEntry; - - // generate hash-value - HashIdx = HashFunction(key); - - pHashEntry = &pAllocHashTable[HashIdx]; - if (IsKeyEmpty(pHashEntry->key) != FALSE) { - // Entry is empty... - } - else { - // Entry is not empy! make a list of entries for this hash value... - while(pHashEntry->Next != NULL) { - pHashEntry = pHashEntry->Next; - } - - pHashEntry->Next = (AllocHashEntryType*) own_malloc(sizeof(AllocHashEntryType)); - g_CurrentMemUsage += CRTTable::AllocHashEntryTypeSize; - pHashEntry = pHashEntry->Next; - if (pHashEntry == NULL) - { - // Exhausted the available memory? - return; - } - } - pHashEntry->key = key; - pHashEntry->nDataSize = nDataSize; - pHashEntry->Next = NULL; -#ifdef _M_IX86 - pHashEntry->pCallstackOffset = (LPVOID) min(context.Ebp, context.Esp); -#elif _M_X64 - pHashEntry->pCallstackOffset = (LPVOID) min(context.Rdi, context.Rsp); -#elif _M_IA64 - pHashEntry->pCallstackOffset = (LPVOID) min(context.IntSp, context.RsBSP); -#else -#error "Platform not supported!" -#endif - pHashEntry->c = context; - - // Query the max. stack-area: - MEMORY_BASIC_INFORMATION MemBuffer; - if(VirtualQuery((LPCVOID) pHashEntry->pCallstackOffset, &MemBuffer, sizeof(MemBuffer)) > 0) - { - pHashEntry->pStackBaseAddr = MemBuffer.BaseAddress; - pHashEntry->nMaxStackSize = MemBuffer.RegionSize; - } - else - { - pHashEntry->pStackBaseAddr = 0; - pHashEntry->nMaxStackSize = 0; - } - - SIZE_T bytesToRead = MAX_CALLSTACK_LEN_BUF; - if (pHashEntry->nMaxStackSize > 0) - { - SIZE_T len = ((SIZE_T) pHashEntry->pStackBaseAddr + pHashEntry->nMaxStackSize) - (SIZE_T)pHashEntry->pCallstackOffset; - bytesToRead = min(len, MAX_CALLSTACK_LEN_BUF); - } - // Now read the callstack: - if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) pHashEntry->pCallstackOffset, &(pHashEntry->pcCallstackAddr), bytesToRead, &(pHashEntry->nCallstackLen)) == 0) - { - // Could not read memory... - pHashEntry->nCallstackLen = 0; - pHashEntry->pCallstackOffset = 0; - } // read callstack - } // Insert - - BOOL Remove(HASHTABLE_KEY &key) - { - SIZE_T HashIdx; - AllocHashEntryType *pHashEntry, *pHashEntryLast; - - // get the Hash-Value - HashIdx = HashFunction(key); - - pHashEntryLast = NULL; - pHashEntry = &pAllocHashTable[HashIdx]; - while(pHashEntry != NULL) { - if (pHashEntry->key == key) { - // release my memory - if (pHashEntryLast == NULL) { - // It is an entry in the table, so do not release this memory - if (pHashEntry->Next == NULL) { - // It was the last entry, so empty the table entry - SetEmptyKey(pAllocHashTable[HashIdx].key); - //memset(&pAllocHashTable[HashIdx], 0, sizeof(pAllocHashTable[HashIdx])); - } - else { - // There are some more entries, so shorten the list - AllocHashEntryType *pTmp = pHashEntry->Next; - *pHashEntry = *(pHashEntry->Next); - own_free(pTmp); - g_CurrentMemUsage -= CRTTable::AllocHashEntryTypeSize; - } - return TRUE; - } - else { - // now, I am in an dynamic allocated entry (it was a collision) - pHashEntryLast->Next = pHashEntry->Next; - own_free(pHashEntry); - g_CurrentMemUsage -= CRTTable::AllocHashEntryTypeSize; - return TRUE; - } - } - pHashEntryLast = pHashEntry; - pHashEntry = pHashEntry->Next; - } - - // if we are here, we could not find the RequestID - return FALSE; - } - - AllocHashEntryType *Find(HASHTABLE_KEY &key) - { - SIZE_T HashIdx; - AllocHashEntryType *pHashEntry; - - // get the Hash-Value - HashIdx = HashFunction(key); - - pHashEntry = &pAllocHashTable[HashIdx]; - while(pHashEntry != NULL) { - if (pHashEntry->key == key) { - return pHashEntry; - } - pHashEntry = pHashEntry->Next; - } - - // entry was not found! - return NULL; - } - - // For the followong static-var See comment in "ShowCallstack"... - static BOOL CALLBACK ReadProcessMemoryFromHashEntry64( - HANDLE hProcess, // hProcess must be a pointer to an hash-entry! - DWORD64 lpBaseAddress, - PVOID lpBuffer, - DWORD nSize, - LPDWORD lpNumberOfBytesRead, - LPVOID pUserData // optional data, which was passed in "ShowCallstack" - ) - { - *lpNumberOfBytesRead = 0; - AllocHashEntryType *pHashEntry = (AllocHashEntryType*) pUserData; - if (pHashEntry == NULL) - { - return FALSE; - } - - if ( ( (DWORD64)lpBaseAddress >= (DWORD64)pHashEntry->pCallstackOffset) && ((DWORD64)lpBaseAddress <= ((DWORD64)pHashEntry->pCallstackOffset+pHashEntry->nCallstackLen)) ) { - // Memory is located in saved Callstack: - // Calculate the offset - DWORD dwOffset = (DWORD) ((DWORD64)lpBaseAddress - (DWORD64)pHashEntry->pCallstackOffset); - DWORD dwSize = __min(nSize, MAX_CALLSTACK_LEN_BUF-dwOffset); - memcpy(lpBuffer, &(pHashEntry->pcCallstackAddr[dwOffset]), dwSize); - *lpNumberOfBytesRead = dwSize; - if (dwSize != nSize) - { - return FALSE; - } - *lpNumberOfBytesRead = nSize; - return TRUE; - } - - if (*lpNumberOfBytesRead == 0) // Memory could not be found - { - if ( ( (DWORD64)lpBaseAddress < (DWORD64)pHashEntry->pStackBaseAddr) || ((DWORD64)lpBaseAddress > ((DWORD64)pHashEntry->pStackBaseAddr+pHashEntry->nMaxStackSize)) ) - { - // Stackwalking is done by reading the "real memory" (normally this happens when the StackWalk64 tries to read some code) - SIZE_T st = 0; - BOOL bRet = ReadProcessMemory(hProcess, (LPCVOID) lpBaseAddress, lpBuffer, nSize, &st); - *lpNumberOfBytesRead = (DWORD) st; - return bRet; - } - } - - return TRUE; - } - - VOID ShowLeaks(LeakFinderOutput &leakFinderOutput) - { - SIZE_T ulTemp; - AllocHashEntryType *pHashEntry; - ULONG ulCount = 0; - SIZE_T ulLeaksByte = 0; - - leakFinderOutput.OnLeakSearchStart(this->m_finderName); - - // Move throu every entry - CHAR keyName[1024]; - for(ulTemp = 0; ulTemp < this->sAllocEntries; ulTemp++) { - pHashEntry = &pAllocHashTable[ulTemp]; - if (IsKeyEmpty(pHashEntry->key) == FALSE) { - while(pHashEntry != NULL) { - ulCount++; - CONTEXT c; - memcpy(&c, &(pHashEntry->c), sizeof(CONTEXT)); - - this->GetKeyAsString(pHashEntry->key, keyName, 1024); - - leakFinderOutput.OnLeakStartEntry(keyName, pHashEntry->nDataSize); - leakFinderOutput.ShowCallstack(GetCurrentThread(), &c, ReadProcessMemoryFromHashEntry64, pHashEntry); - - // Count the number of leaky bytes - ulLeaksByte += pHashEntry->nDataSize; - - pHashEntry = pHashEntry->Next; - } // while - } - } - } - - AllocHashEntryType *pAllocHashTable; - SIZE_T sAllocEntries; - HANDLE m_hHeap; - LPSTR m_finderName; - bool m_bSupressUselessLines; -}; // template class ContextHashtableBase - - - - - -// ########################################################################## -// ########################################################################## -// ########################################################################## -// Specialization for CRT-Leaks: -// VC5 has excluded all types in release-builds -#ifdef _DEBUG - -// The follwoing is copied from dbgint.h: -// -/* -* For diagnostic purpose, blocks are allocated with extra information and -* stored in a doubly-linked list. This makes all blocks registered with -* how big they are, when they were allocated, and what they are used for. -*/ - -// forward declaration: -#ifndef _M_CEE_PURE -#define MyAllocHookCallingConvention __cdecl -#endif -#if _MSC_VER >= 1400 -#ifdef _M_CEE -#define MyAllocHookCallingConvention __clrcall -#endif -#endif - -static int MyAllocHookCallingConvention MyAllocHook(int nAllocType, void *pvData, - size_t nSize, int nBlockUse, long lRequest, -#if _MSC_VER <= 1100 // Special case for VC 5 and before - const char * szFileName, -#else - const unsigned char * szFileName, -#endif - int nLine); - -static _CRT_ALLOC_HOOK s_pfnOldCrtAllocHook = NULL; -static LONG s_CrtDisableCount = 0; -static LONG s_lMallocCalled = 0; - - - - - -class CRTTable : public ContextHashtableBase -{ -public: - CRTTable() : ContextHashtableBase(1021, "CRT-Leaks") - { - // save the previous alloc hook - s_pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook); - } - - virtual ~CRTTable() - { - _CrtSetAllocHook(s_pfnOldCrtAllocHook); - } - - virtual LONG Disable() - { - return InterlockedIncrement(&s_CrtDisableCount); - } - virtual LONG Enable() - { - return InterlockedDecrement(&s_CrtDisableCount); - } - - virtual SIZE_T HashFunction(LONG &key) - { - // I couldn´t find any better and faster - return key % sAllocEntries; - } - virtual BOOL IsKeyEmpty(LONG &key) - { - if (key == 0) - return TRUE; - return FALSE; - } - virtual VOID SetEmptyKey(LONG &key) - { - key = 0; - } - virtual VOID GetKeyAsString(LONG &key, CHAR *szName, SIZE_T nBufferLen) - { -#if _MSC_VER < 1400 - _snprintf_s(szName, nBufferLen, "%d", key); -#else - _snprintf_s(szName, nBufferLen, nBufferLen, "%d", key); -#endif - } - - static const int AllocHashEntryTypeSize = sizeof(AllocHashEntryType); - -protected: - CHAR *m_pBuffer; - SIZE_T m_maxBufferLen; - SIZE_T m_bufferLen; -}; // class CRTTable - - -#define nNoMansLandSize 4 - -typedef struct _CrtMemBlockHeader -{ - struct _CrtMemBlockHeader * pBlockHeaderNext; - struct _CrtMemBlockHeader * pBlockHeaderPrev; - char * szFileName; - int nLine; -#ifdef _WIN64 - /* These items are reversed on Win64 to eliminate gaps in the struct - * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is - * maintained in the debug heap. - */ - int nBlockUse; - size_t nDataSize; -#else /* _WIN64 */ - size_t nDataSize; - int nBlockUse; -#endif /* _WIN64 */ - long lRequest; - unsigned char gap[nNoMansLandSize]; - /* followed by: - * unsigned char data[nDataSize]; - * unsigned char anotherGap[nNoMansLandSize]; - */ -} _CrtMemBlockHeader; -#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1)) -#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1) -// - -static CRTTable *g_pCRTTable = NULL; - -size_t g_CurrentMemUsage = 0; - - - - - -// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function! -static int MyAllocHook(int nAllocType, void *pvData, - size_t nSize, int nBlockUse, long lRequest, -#if _MSC_VER <= 1100 // Special case for VC 5 - const char * szFileName, -#else - const unsigned char * szFileName, -#endif - int nLine) -{ - //static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") }; - //static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") }; - -#ifdef IGNORE_CRT_ALLOC - if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations - return TRUE; -#endif - extern int _crtDbgFlag; - if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) ) - { - // Someone has disabled that the runtime should log this allocation - // so we do not log this allocation - if (s_pfnOldCrtAllocHook != NULL) - s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); - return TRUE; - } - - // Handle the Disable/Enable setting - if (InterlockedExchangeAdd(&s_CrtDisableCount, 0) != 0) - { - return TRUE; - } - - // Prevent from reentrat calls - if (InterlockedIncrement(&s_lMallocCalled) > 1) - { - // I was already called - InterlockedDecrement(&s_lMallocCalled); - // call the previous alloc hook - if (s_pfnOldCrtAllocHook != NULL) - s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); - return TRUE; - } - - _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) ); - _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) ); - - if (nAllocType == _HOOK_FREE) - { - // freeing - // Try to get the header information - if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer - // get the ID - _CrtMemBlockHeader *pHead; - // get a pointer to memory block header - pHead = pHdr(pvData); - nSize = pHead->nDataSize; - lRequest = pHead->lRequest; // This is the ID! - - if (pHead->nBlockUse == _IGNORE_BLOCK) - { - InterlockedDecrement(&s_lMallocCalled); - if (s_pfnOldCrtAllocHook != NULL) - { - s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); - } - return TRUE; - } - } - if (lRequest != 0) - { - // RequestID was found - size_t temp = g_CurrentMemUsage; - g_CurrentMemUsage -= nSize ; - g_pCRTTable->Remove(lRequest); - if (g_CurrentMemUsage > temp) - { - printf("********************************************\n"); - printf("** Server detected underflow in memory **\n"); - printf("** usage counter. Something is not right. **\n"); - printf("** Writing memory dump into memdump.xml **\n"); - printf("********************************************\n"); - printf("Please wait\n"); - - LeakFinderXmlOutput Output("memdump.xml"); - DumpUsedMemory(&Output); - - printf("\nMemory dump complete. Server will now abort.\n"); - abort(); - } - } - } // freeing - - if (nAllocType == _HOOK_REALLOC) - { - // re-allocating - // Try to get the header information - if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer - BOOL bRet; - LONG lReallocRequest; - // get the ID - _CrtMemBlockHeader *pHead; - // get a pointer to memory block header - pHead = pHdr(pvData); - // Try to find the RequestID in the Hash-Table, mark it that it was freed - lReallocRequest = pHead->lRequest; - size_t temp = g_CurrentMemUsage; - g_CurrentMemUsage -= pHead->nDataSize; - bRet = g_pCRTTable->Remove(lReallocRequest); - if (g_CurrentMemUsage > temp) - { - printf("********************************************\n"); - printf("** Server detected underflow in memory **\n"); - printf("** usage counter. Something is not right. **\n"); - printf("** Writing memory dump into memdump.xml **\n"); - printf("********************************************\n"); - printf("Please wait\n"); - - LeakFinderXmlOutput Output("memdump.xml"); - DumpUsedMemory(&Output); - - printf("\nMemory dump complete. Server will now abort.\n"); - abort(); - } - } // ValidHeapPointer - } // re-allocating - - //if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) { - if (nAllocType == _HOOK_FREE) - { - InterlockedDecrement(&s_lMallocCalled); - // call the previous alloc hook - if (s_pfnOldCrtAllocHook != NULL) - { - s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); - } - return TRUE; - } - - CONTEXT c; - GET_CURRENT_CONTEXT(c, CONTEXT_FULL); - - // Only insert in the Hash-Table if it is not a "freeing" - if (nAllocType != _HOOK_FREE) - { - if (lRequest != 0) // Always a valid RequestID should be provided (see comments in the header) - { - //No need to check for overflow since we are checking if we are getting higher than 1gb. - //If we change this, then we probably would want an overflow check. - g_CurrentMemUsage += nSize ; - g_pCRTTable->Insert(lRequest, c, nSize); - - if (g_CurrentMemUsage > 1536 * 1024* 1024) - { - printf("******************************************\n"); - printf("** Server reached 1.5 GiB memory usage, **\n"); - printf("** something is probably wrong. **\n"); - printf("** Writing memory dump into memdump.xml **\n"); - printf("******************************************\n"); - printf("Please wait\n"); - - LeakFinderXmlOutput Output("memdump.xml"); - DumpUsedMemory(&Output); - - printf("\nMemory dump complete. Server will now abort.\n"); - abort(); - } - } - } - - InterlockedDecrement(&s_lMallocCalled); - // call the previous alloc hook - if (s_pfnOldCrtAllocHook != NULL) - s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); - return TRUE; // allow the memory operation to proceed -} // MyAllocHook - -#endif // _DEBUG - - - - - -// ########################################################################## -// ########################################################################## -// ########################################################################## -// Init/Deinit functions - -HRESULT InitLeakFinder() -{ - #ifdef _DEBUG - g_pCRTTable = new CRTTable(); - #endif - return S_OK; -} - - - - - -void DumpUsedMemory(LeakFinderOutput * output) -{ - LeakFinderOutput *pLeakFinderOutput = output; - - #ifdef _DEBUG - g_pCRTTable->Disable(); - #endif - - if (pLeakFinderOutput == NULL) - { - pLeakFinderOutput = new LeakFinderOutput(); - } - - // explicitly load the modules: - pLeakFinderOutput->LoadModules(); - - #ifdef _DEBUG - g_pCRTTable->ShowLeaks(*pLeakFinderOutput); - #endif - - if (output == NULL) - { - delete pLeakFinderOutput; - } -} - - - - - -void DeinitLeakFinder(LeakFinderOutput *output) -{ - DumpUsedMemory(output); - - #ifdef _DEBUG - delete g_pCRTTable; - g_pCRTTable = NULL; - #endif -} - - - - - -void DeinitLeakFinder() -{ - DeinitLeakFinder(NULL); -} - - - - diff --git a/source/LeakFinder.h b/source/LeakFinder.h deleted file mode 100644 index e63b9ec5d..000000000 --- a/source/LeakFinder.h +++ /dev/null @@ -1,156 +0,0 @@ -/********************************************************************** - * - * LEAKFINDER.H - * - * - * - * LICENSE (http://www.opensource.org/licenses/bsd-license.php) - * - * Copyright (c) 2005-2010, Jochen Kalmbach - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of Jochen Kalmbach nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - **********************************************************************/ - -// #pragma once is supported starting with _MCS_VER 1000, -// so we need not to check the version (because we only support _MSC_VER >= 1100)! -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT InitLeakFinder(); -void DeinitLeakFinder(); - -#ifdef __cplusplus -} -#endif - - -// The following is only available if the file is CPP -#ifdef __cplusplus - -#include "StackWalker.h" - -// Interface for output... -class LeakFinderOutput : public StackWalker -{ -public: - typedef enum LeakFinderOptions - { - // No addition info will be retrived - // (only the address is available) - LeakFinderNone = 0, - LeakFinderShowCompleteCallstack = 0x1000 - } LeakFinderOptions; - - LeakFinderOutput(int options = OptionsAll, LPCSTR szSymPath = NULL); - virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName); - virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize); -protected: - virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); - virtual void OnOutput(LPCSTR szText) - { - printf(szText); - StackWalker::OnOutput(szText); - } - virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) - { - if (strcmp(szFuncName, "SymGetLineFromAddr64") == 0) return; - StackWalker::OnDbgHelpErr(szFuncName, gle, addr); - } -}; - -class LeakFinderXmlOutput : public LeakFinderOutput -{ -public: - LeakFinderXmlOutput(); - virtual ~LeakFinderXmlOutput(); - LeakFinderXmlOutput(LPCTSTR szFileName); - virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName); - virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize); -protected: - virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); - virtual void OnOutput(LPCSTR szText) { } - virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { } - - FILE * m_fXmlFile; - int m_Progress; -}; - -// C++ interface: -void DeinitLeakFinder(LeakFinderOutput *output); - -class ZZZ_LeakFinder -{ -public: - ZZZ_LeakFinder() - { - m_pXml = NULL; -#ifdef XML_LEAK_FINDER - m_pXml = new LeakFinderXmlOutput(); -#endif - InitLeakFinder(); - } - ~ZZZ_LeakFinder() - { - DeinitLeakFinder(m_pXml); - if (m_pXml != NULL) delete m_pXml; - } -protected: - LeakFinderXmlOutput *m_pXml; -}; - -#if defined(INIT_LEAK_FINDER) -#if _MSC_VER >= 1200 -#pragma warning(push) -#endif -#pragma warning (disable:4074) - -// WARNING: If you enable this option, the code might run without the CRT being initialized or after the CRT was deinitialized!!! -// Currently the code is not designed to bypass the CRT... -//#pragma init_seg (compiler) -ZZZ_LeakFinder zzz_LeakFinder; - -#if _MSC_VER >= 1200 -#pragma warning(pop) -#else -#pragma warning(default:4074) -#endif -#endif - -#endif // __cplusplus - - - - -extern void DumpUsedMemory(LeakFinderOutput * output = NULL); - - - - - diff --git a/source/LightingThread.cpp b/source/LightingThread.cpp deleted file mode 100644 index d7e60e458..000000000 --- a/source/LightingThread.cpp +++ /dev/null @@ -1,562 +0,0 @@ - -// LightingThread.cpp - -// Implements the cLightingThread class representing the thread that processes requests for lighting - -#include "Globals.h" -#include "LightingThread.h" -#include "ChunkMap.h" -#include "World.h" - - - - - -/// If more than this many chunks are in the queue, a warning is printed to the log -#define WARN_ON_QUEUE_SIZE 800 - - - - - -/// Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]: -class cReader : - public cChunkDataCallback -{ - virtual void BlockTypes(const BLOCKTYPE * a_Type) override - { - // ROW is a block of 16 Blocks, one whole row is copied at a time (hopefully the compiler will optimize that) - // C++ doesn't permit copying arrays, but arrays as a part of a struct is ok :) - typedef struct {BLOCKTYPE m_Row[16]; } ROW; - ROW * InputRows = (ROW *)a_Type; - ROW * OutputRows = (ROW *)m_BlockTypes; - int InputIdx = 0; - int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3; - for (int y = 0; y < cChunkDef::Height; y++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - OutputRows[OutputIdx] = InputRows[InputIdx++]; - OutputIdx += 3; - } // for z - // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows - // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip - OutputIdx += cChunkDef::Width * 6; - } // for y - } // BlockTypes() - - - virtual void HeightMap(const cChunkDef::HeightMap * a_Heightmap) override - { - typedef struct {HEIGHTTYPE m_Row[16]; } ROW; - ROW * InputRows = (ROW *)a_Heightmap; - ROW * OutputRows = (ROW *)m_HeightMap; - int InputIdx = 0; - int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3; - for (int z = 0; z < cChunkDef::Width; z++) - { - OutputRows[OutputIdx] = InputRows[InputIdx++]; - OutputIdx += 3; - } // for z - } - -public: - int m_ReadingChunkX; // 0, 1 or 2; x-offset of the chunk we're reading from the BlockTypes start - int m_ReadingChunkZ; // 0, 1 or 2; z-offset of the chunk we're reading from the BlockTypes start - BLOCKTYPE * m_BlockTypes; // 3x3 chunks of block types, organized as a single XZY blob of data (instead of 3x3 XZY blobs) - HEIGHTTYPE * m_HeightMap; // 3x3 chunks of height map, organized as a single XZY blob of data (instead of 3x3 XZY blobs) -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cLightingThread: - -cLightingThread::cLightingThread(void) : - super("cLightingThread"), - m_World(NULL) -{ -} - - - - - -cLightingThread::~cLightingThread() -{ - Stop(); -} - - - - - -bool cLightingThread::Start(cWorld * a_World) -{ - ASSERT(m_World == NULL); // Not started yet - m_World = a_World; - - return super::Start(); -} - - - - - -void cLightingThread::Stop(void) -{ - { - cCSLock Lock(m_CS); - for (sItems::iterator itr = m_Queue.begin(), end = m_Queue.end(); itr != end; ++itr) - { - delete itr->m_ChunkStay; - } - m_Queue.clear(); - } - m_ShouldTerminate = true; - m_evtItemAdded.Set(); - - Wait(); -} - - - - - -void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter) -{ - ASSERT(m_World != NULL); // Did you call Start() properly? - - cChunkStay * ChunkStay = new cChunkStay(m_World); - ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ + 1); - ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ); - ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ - 1); - ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ + 1); - ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ - 1); - ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ + 1); - ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ); - ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ - 1); - ChunkStay->Enable(); - ChunkStay->Load(); - cCSLock Lock(m_CS); - m_Queue.push_back(sItem(a_ChunkX, a_ChunkZ, ChunkStay, a_CallbackAfter)); - if (m_Queue.size() > WARN_ON_QUEUE_SIZE) - { - LOGINFO("Lighting thread overloaded, %d items in queue", m_Queue.size()); - } - m_evtItemAdded.Set(); -} - - - - - -void cLightingThread::WaitForQueueEmpty(void) -{ - cCSLock Lock(m_CS); - while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PostponedQueue.empty())) - { - cCSUnlock Unlock(Lock); - m_evtQueueEmpty.Wait(); - } -} - - - - - -size_t cLightingThread::GetQueueLength(void) -{ - cCSLock Lock(m_CS); - return m_Queue.size() + m_PostponedQueue.size(); -} - - - - - -void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ) -{ - // Check all the items in the m_PostponedQueue, if the chunk is their neighbor, move the item to m_Queue - - bool NewlyAdded = false; - { - cCSLock Lock(m_CS); - for (sItems::iterator itr = m_PostponedQueue.begin(); itr != m_PostponedQueue.end(); ) - { - if ( - (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) && - (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) - ) - { - // It is a neighbor - m_Queue.push_back(*itr); - itr = m_PostponedQueue.erase(itr); - NewlyAdded = true; - } - else - { - ++itr; - } - } // for itr - m_PostponedQueue[] - } // Lock(m_CS) - - if (NewlyAdded) - { - m_evtItemAdded.Set(); // Notify the thread it has some work to do - } -} - - - - - -void cLightingThread::Execute(void) -{ - while (true) - { - { - cCSLock Lock(m_CS); - if (m_Queue.size() == 0) - { - cCSUnlock Unlock(Lock); - m_evtItemAdded.Wait(); - } - } - - if (m_ShouldTerminate) - { - return; - } - - // Process one items from the queue: - sItem Item; - { - cCSLock Lock(m_CS); - if (m_Queue.empty()) - { - continue; - } - Item = m_Queue.front(); - m_Queue.pop_front(); - if (m_Queue.empty()) - { - m_evtQueueEmpty.Set(); - } - } // CSLock(m_CS) - - LightChunk(Item); - } -} - - - - - - -void cLightingThread::LightChunk(cLightingThread::sItem & a_Item) -{ - cChunkDef::BlockNibbles BlockLight, SkyLight; - - if (!ReadChunks(a_Item.x, a_Item.z)) - { - // Neighbors not available. Re-queue in the postponed queue - cCSLock Lock(m_CS); - m_PostponedQueue.push_back(a_Item); - return; - } - - /* - // DEBUG: torch somewhere: - m_BlockTypes[19 + 24 * cChunkDef::Width * 3 + (m_HeightMap[24 + 24 * cChunkDef::Width * 3] / 2) * BlocksPerYLayer] = E_BLOCK_TORCH; - // m_HeightMap[24 + 24 * cChunkDef::Width * 3]++; - */ - - PrepareBlockLight(); - CalcLight(m_BlockLight); - - PrepareSkyLight(); - - /* - // DEBUG: Save chunk data with highlighted seeds for visual inspection: - cFile f4; - if ( - f4.Open(Printf("Chunk_%d_%d_seeds.grab", a_Item.x, a_Item.z), cFile::fmWrite) - ) - { - for (int z = 0; z < cChunkDef::Width * 3; z++) - { - for (int y = cChunkDef::Height / 2; y >= 0; y--) - { - unsigned char Seeds [cChunkDef::Width * 3]; - memcpy(Seeds, m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3); - for (int x = 0; x < cChunkDef::Width * 3; x++) - { - if (m_IsSeed1[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x]) - { - Seeds[x] = E_BLOCK_DIAMOND_BLOCK; - } - } - f4.Write(Seeds, cChunkDef::Width * 3); - } - } - } - //*/ - - CalcLight(m_SkyLight); - - /* - // DEBUG: Save XY slices of the chunk data and lighting for visual inspection: - cFile f1, f2, f3; - if ( - f1.Open(Printf("Chunk_%d_%d_data.grab", a_Item.x, a_Item.z), cFile::fmWrite) && - f2.Open(Printf("Chunk_%d_%d_sky.grab", a_Item.x, a_Item.z), cFile::fmWrite) && - f3.Open(Printf("Chunk_%d_%d_glow.grab", a_Item.x, a_Item.z), cFile::fmWrite) - ) - { - for (int z = 0; z < cChunkDef::Width * 3; z++) - { - for (int y = cChunkDef::Height / 2; y >= 0; y--) - { - f1.Write(m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3); - unsigned char SkyLight [cChunkDef::Width * 3]; - unsigned char BlockLight[cChunkDef::Width * 3]; - for (int x = 0; x < cChunkDef::Width * 3; x++) - { - SkyLight[x] = m_SkyLight [y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4; - BlockLight[x] = m_BlockLight[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4; - } - f2.Write(SkyLight, cChunkDef::Width * 3); - f3.Write(BlockLight, cChunkDef::Width * 3); - } - } - } - //*/ - - CompressLight(m_BlockLight, BlockLight); - CompressLight(m_SkyLight, SkyLight); - - m_World->ChunkLighted(a_Item.x, a_Item.z, BlockLight, SkyLight); - - if (a_Item.m_Callback != NULL) - { - a_Item.m_Callback->Call(a_Item.x, a_Item.z); - } - delete a_Item.m_ChunkStay; -} - - - - - -bool cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ) -{ - cReader Reader; - Reader.m_BlockTypes = m_BlockTypes; - Reader.m_HeightMap = m_HeightMap; - - for (int z = 0; z < 3; z++) - { - Reader.m_ReadingChunkZ = z; - for (int x = 0; x < 3; x++) - { - Reader.m_ReadingChunkX = x; - if (!m_World->GetChunkData(a_ChunkX + x - 1, a_ChunkZ + z - 1, Reader)) - { - return false; - } - } // for z - } // for x - - memset(m_BlockLight, 0, sizeof(m_BlockLight)); - memset(m_SkyLight, 0, sizeof(m_SkyLight)); - return true; -} - - - - - -void cLightingThread::PrepareSkyLight(void) -{ - // Clear seeds: - memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); - m_NumSeeds = 0; - - // Walk every column that has all XZ neighbors - for (int z = 1; z < cChunkDef::Width * 3 - 1; z++) - { - int BaseZ = z * cChunkDef::Width * 3; - for (int x = 1; x < cChunkDef::Width * 3 - 1; x++) - { - int idx = BaseZ + x; - int Current = m_HeightMap[idx] + 1; - int Neighbor1 = m_HeightMap[idx + 1] + 1; // X + 1 - int Neighbor2 = m_HeightMap[idx - 1] + 1; // X - 1 - int Neighbor3 = m_HeightMap[idx + cChunkDef::Width * 3] + 1; // Z + 1 - int Neighbor4 = m_HeightMap[idx - cChunkDef::Width * 3] + 1; // Z - 1 - int MaxNeighbor = std::max(std::max(Neighbor1, Neighbor2), std::max(Neighbor3, Neighbor4)); // Maximum of the four neighbors - - // Fill the column from the top down to Current with all-light: - for (int y = cChunkDef::Height - 1, Index = idx + y * BlocksPerYLayer; y >= Current; y--, Index -= BlocksPerYLayer) - { - m_SkyLight[Index] = 15; - } - - // Add Current as a seed: - if (Current < cChunkDef::Height) - { - int CurrentIdx = idx + Current * BlocksPerYLayer; - m_IsSeed1[CurrentIdx] = true; - m_SeedIdx1[m_NumSeeds++] = CurrentIdx; - } - - // Add seed from Current up to the highest neighbor: - for (int y = Current + 1, Index = idx + y * BlocksPerYLayer; y < MaxNeighbor; y++, Index += BlocksPerYLayer) - { - m_IsSeed1[Index] = true; - m_SeedIdx1[m_NumSeeds++] = Index; - } - } - } -} - - - - - -void cLightingThread::PrepareBlockLight(void) -{ - // Clear seeds: - memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); - memset(m_IsSeed2, 0, sizeof(m_IsSeed2)); - m_NumSeeds = 0; - - // Walk every column that has all XZ neighbors, make a seed for each light-emitting block: - for (int z = 1; z < cChunkDef::Width * 3 - 1; z++) - { - int BaseZ = z * cChunkDef::Width * 3; - for (int x = 1; x < cChunkDef::Width * 3 - 1; x++) - { - int idx = BaseZ + x; - for (int y = m_HeightMap[idx], Index = idx + y * BlocksPerYLayer; y >= 0; y--, Index -= BlocksPerYLayer) - { - if (g_BlockLightValue[m_BlockTypes[Index]] == 0) - { - continue; - } - - // Add current block as a seed: - m_IsSeed1[Index] = true; - m_SeedIdx1[m_NumSeeds++] = Index; - - // Light it up: - m_BlockLight[Index] = g_BlockLightValue[m_BlockTypes[Index]]; - } - } - } -} - - - - - -void cLightingThread::CalcLight(NIBBLETYPE * a_Light) -{ - int NumSeeds2 = 0; - while (m_NumSeeds > 0) - { - // Buffer 1 -> buffer 2 - memset(m_IsSeed2, 0, sizeof(m_IsSeed2)); - NumSeeds2 = 0; - CalcLightStep(a_Light, m_NumSeeds, m_IsSeed1, m_SeedIdx1, NumSeeds2, m_IsSeed2, m_SeedIdx2); - if (NumSeeds2 == 0) - { - return; - } - - // Buffer 2 -> buffer 1 - memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); - m_NumSeeds = 0; - CalcLightStep(a_Light, NumSeeds2, m_IsSeed2, m_SeedIdx2, m_NumSeeds, m_IsSeed1, m_SeedIdx1); - } -} - - - - - -void cLightingThread::CalcLightStep( - NIBBLETYPE * a_Light, - int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn, - int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut -) -{ - int NumSeedsOut = 0; - for (int i = 0; i < a_NumSeedsIn; i++) - { - int SeedIdx = a_SeedIdxIn[i]; - int SeedX = SeedIdx % (cChunkDef::Width * 3); - int SeedZ = (SeedIdx / (cChunkDef::Width * 3)) % (cChunkDef::Width * 3); - int SeedY = SeedIdx / BlocksPerYLayer; - - // Propagate seed: - if (SeedX < cChunkDef::Width * 3 - 1) - { - PropagateLight(a_Light, SeedIdx, SeedIdx + 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); - } - if (SeedX > 0) - { - PropagateLight(a_Light, SeedIdx, SeedIdx - 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); - } - if (SeedZ < cChunkDef::Width * 3 - 1) - { - PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); - } - if (SeedZ > 0) - { - PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); - } - if (SeedY < cChunkDef::Height - 1) - { - PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); - } - if (SeedY > 0) - { - PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); - } - } // for i - a_SeedIdxIn[] - a_NumSeedsOut = NumSeedsOut; -} - - - - - -void cLightingThread::CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight) -{ - int InIdx = cChunkDef::Width * 49; // Index to the first nibble of the middle chunk in the a_LightArray - int OutIdx = 0; - for (int y = 0; y < cChunkDef::Height; y++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x += 2) - { - a_ChunkLight[OutIdx++] = (a_LightArray[InIdx + 1] << 4) | a_LightArray[InIdx]; - InIdx += 2; - } - InIdx += cChunkDef::Width * 2; - } - // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows - // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip - InIdx += cChunkDef::Width * cChunkDef::Width * 6; - } -} - - - - diff --git a/source/LightingThread.h b/source/LightingThread.h deleted file mode 100644 index 498755025..000000000 --- a/source/LightingThread.h +++ /dev/null @@ -1,181 +0,0 @@ - -// LightingThread.h - -// Interfaces to the cLightingThread class representing the thread that processes requests for lighting - -/* -Lighting is done on whole chunks. For each chunk to be lighted, the whole 3x3 chunk area around it is read, -then it is processed, so that the middle chunk area has valid lighting, and the lighting is copied into the ChunkMap. -Lighting is calculated in full char arrays instead of nibbles, so that accessing the arrays is fast. -Lighting is calculated in a flood-fill fashion: -1. Generate seeds from where the light spreads (full skylight / light-emitting blocks) -2. For each seed: - - Spread the light 1 block in each of the 6 cardinal directions, if the blocktype allows - - If the recipient block has had lower lighting value than that being spread, make it a new seed -3. Repeat step 2, until there are no more seeds -The seeds need two fast operations: - - Check if a block at [x, y, z] is already a seed - - Get the next seed in the row -For that reason it is stored in two arrays, one stores a bool saying a seed is in that position, -the other is an array of seed coords, encoded as a single int. -Step 2 needs two separate storages for old seeds and new seeds, so there are two actual storages for that purpose, -their content is swapped after each full step-2-cycle. - -The thread has two queues of chunks that are to be lighted. -The first queue, m_Queue, is the only one that is publicly visible, chunks get queued there by external requests. -The second one, m_PostponedQueue, is for chunks that have been taken out of m_Queue and didn't have neighbors ready. -Chunks from m_PostponedQueue are moved back into m_Queue when their neighbors get valid, using the ChunkReady callback. -*/ - - - -#pragma once - -#include "OSSupport/IsThread.h" -#include "ChunkDef.h" - - - - - -// fwd: "cWorld.h" -class cWorld; - -// fwd: "cChunkMap.h" -class cChunkStay; - - - - - -class cLightingThread : - public cIsThread -{ - typedef cIsThread super; - -public: - - cLightingThread(void); - ~cLightingThread(); - - bool Start(cWorld * a_World); - - void Stop(void); - - /// Queues the entire chunk for lighting - void QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter = NULL); - - /// Blocks until the queue is empty or the thread is terminated - void WaitForQueueEmpty(void); - - size_t GetQueueLength(void); - - /// Called from cWorld when a chunk gets valid. Chunks in m_PostponedQueue may need moving into m_Queue - void ChunkReady(int a_ChunkX, int a_ChunkZ); - -protected: - - struct sItem - { - int x, z; - cChunkStay * m_ChunkStay; - cChunkCoordCallback * m_Callback; - - sItem(void) {} // empty default constructor needed - sItem(int a_X, int a_Z, cChunkStay * a_ChunkStay, cChunkCoordCallback * a_Callback) : - x(a_X), - z(a_Z), - m_ChunkStay(a_ChunkStay), - m_Callback(a_Callback) - { - } - } ; - - typedef std::list sItems; - - cWorld * m_World; - cCriticalSection m_CS; - sItems m_Queue; - sItems m_PostponedQueue; // Chunks that have been postponed due to missing neighbors - cEvent m_evtItemAdded; // Set when queue is appended, or to stop the thread - cEvent m_evtQueueEmpty; // Set when the queue gets empty - - // Buffers for the 3x3 chunk data - // These buffers alone are 1.7 MiB in size, therefore they cannot be located on the stack safely - some architectures may have only 1 MiB for stack, or even less - // Placing the buffers into the object means that this object can light chunks only in one thread! - // The blobs are XZY organized as a whole, instead of 3x3 XZY-organized subarrays -> - // -> This means data has to be scatterred when reading and gathered when writing! - static const int BlocksPerYLayer = cChunkDef::Width * cChunkDef::Width * 3 * 3; - BLOCKTYPE m_BlockTypes[BlocksPerYLayer * cChunkDef::Height]; - NIBBLETYPE m_BlockLight[BlocksPerYLayer * cChunkDef::Height]; - NIBBLETYPE m_SkyLight [BlocksPerYLayer * cChunkDef::Height]; - HEIGHTTYPE m_HeightMap [BlocksPerYLayer]; - - // Seed management (5.7 MiB) - // Two buffers, in each calc step one is set as input and the other as output, then in the next step they're swapped - // Each seed is represented twice in this structure - both as a "list" and as a "position". - // "list" allows fast traversal from seed to seed - // "position" allows fast checking if a coord is already a seed - unsigned char m_IsSeed1 [BlocksPerYLayer * cChunkDef::Height]; - unsigned int m_SeedIdx1[BlocksPerYLayer * cChunkDef::Height]; - unsigned char m_IsSeed2 [BlocksPerYLayer * cChunkDef::Height]; - unsigned int m_SeedIdx2[BlocksPerYLayer * cChunkDef::Height]; - int m_NumSeeds; - - virtual void Execute(void) override; - - /// Lights the entire chunk. If neighbor chunks don't exist, touches them and re-queues the chunk - void LightChunk(sItem & a_Item); - - /// Prepares m_BlockTypes and m_HeightMap data; returns false if any of the chunks fail. Zeroes out the light arrays - bool ReadChunks(int a_ChunkX, int a_ChunkZ); - - /// Uses m_HeightMap to initialize the m_SkyLight[] data; fills in seeds for the skylight - void PrepareSkyLight(void); - - /// Uses m_BlockTypes to initialize the m_BlockLight[] data; fills in seeds for the blocklight - void PrepareBlockLight(void); - - /// Calculates light in the light array specified, using stored seeds - void CalcLight(NIBBLETYPE * a_Light); - - /// Does one step in the light calculation - one seed propagation and seed recalculation - void CalcLightStep( - NIBBLETYPE * a_Light, - int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn, - int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut - ); - - /// Compresses from 1-block-per-byte (faster calc) into 2-blocks-per-byte (MC storage): - void CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight); - - inline void PropagateLight( - NIBBLETYPE * a_Light, - int a_SrcIdx, int a_DstIdx, - int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut - ) - { - ASSERT(a_SrcIdx >= 0); - ASSERT(a_SrcIdx < ARRAYCOUNT(m_SkyLight)); - ASSERT(a_DstIdx >= 0); - ASSERT(a_DstIdx < ARRAYCOUNT(m_BlockTypes)); - - if (a_Light[a_SrcIdx] <= a_Light[a_DstIdx] + g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]]) - { - // We're not offering more light than the dest block already has - return; - } - - a_Light[a_DstIdx] = a_Light[a_SrcIdx] - g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]]; - if (!a_IsSeedOut[a_DstIdx]) - { - a_IsSeedOut[a_DstIdx] = true; - a_SeedIdxOut[a_NumSeedsOut++] = a_DstIdx; - } - } - -} ; - - - - diff --git a/source/LineBlockTracer.cpp b/source/LineBlockTracer.cpp deleted file mode 100644 index 9fcbca915..000000000 --- a/source/LineBlockTracer.cpp +++ /dev/null @@ -1,262 +0,0 @@ - -// LineBlockTracer.cpp - -// Implements the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points - -#include "Globals.h" -#include "LineBlockTracer.h" -#include "Vector3d.h" -#include "World.h" -#include "Chunk.h" - - - - - - -cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : - super(a_World, a_Callbacks) -{ -} - - - - - -bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End) -{ - cLineBlockTracer Tracer(a_World, a_Callbacks); - return Tracer.Trace(a_Start.x, a_Start.y, a_Start.z, a_End.x, a_End.y, a_End.z); -} - - - - - -bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks &a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ) -{ - cLineBlockTracer Tracer(a_World, a_Callbacks); - return Tracer.Trace(a_StartX, a_StartY, a_StartZ, a_EndX, a_EndY, a_EndZ); -} - - - - - -bool cLineBlockTracer::Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ) -{ - // Initialize the member veriables: - m_StartX = a_StartX; - m_StartY = a_StartY; - m_StartZ = a_StartZ; - m_EndX = a_EndX; - m_EndY = a_EndY; - m_EndZ = a_EndZ; - m_DirX = (m_StartX < m_EndX) ? 1 : -1; - m_DirY = (m_StartY < m_EndY) ? 1 : -1; - m_DirZ = (m_StartZ < m_EndZ) ? 1 : -1; - m_CurrentFace = BLOCK_FACE_NONE; - - // Check the start coords, adjust into the world: - if (m_StartY < 0) - { - if (m_EndY < 0) - { - // Nothing to trace - m_Callbacks->OnNoMoreHits(); - return true; - } - FixStartBelowWorld(); - m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ); - } - else if (m_StartY >= cChunkDef::Height) - { - if (m_EndY >= cChunkDef::Height) - { - m_Callbacks->OnNoMoreHits(); - return true; - } - FixStartAboveWorld(); - m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ); - } - - m_CurrentX = (int)floor(m_StartX); - m_CurrentY = (int)floor(m_StartY); - m_CurrentZ = (int)floor(m_StartZ); - - m_DiffX = m_EndX - m_StartX; - m_DiffY = m_EndY - m_StartY; - m_DiffZ = m_EndZ - m_StartZ; - - // The actual trace is handled with ChunkMapCS locked by calling our Item() for the specified chunk - int BlockX = (int)floor(m_StartX); - int BlockZ = (int)floor(m_StartZ); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); - return m_World->DoWithChunk(ChunkX, ChunkZ, *this); -} - - - - - -void cLineBlockTracer::FixStartAboveWorld(void) -{ - // We must set the start Y to less than cChunkDef::Height so that it is considered inside the world later on - // Therefore we use an EPS-offset from the height, as small as reasonably possible. - const double Height = (double)cChunkDef::Height - 0.00001; - CalcXZIntersection(Height, m_StartX, m_StartZ); - m_StartY = Height; -} - - - - - -void cLineBlockTracer::FixStartBelowWorld(void) -{ - CalcXZIntersection(0, m_StartX, m_StartZ); - m_StartY = 0; -} - - - - - -void cLineBlockTracer::CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ) -{ - double Ratio = (m_StartY - a_Y) / (m_StartY - m_EndY); - a_IntersectX = m_StartX + (m_EndX - m_StartX) * Ratio; - a_IntersectZ = m_StartZ + (m_EndZ - m_StartZ) * Ratio; -} - - - - - -bool cLineBlockTracer::MoveToNextBlock(void) -{ - // Find out which of the current block's walls gets hit by the path: - static const double EPS = 0.00001; - double Coeff = 1; - enum eDirection - { - dirNONE, - dirX, - dirY, - dirZ, - } Direction = dirNONE; - if (abs(m_DiffX) > EPS) - { - double DestX = (m_DirX > 0) ? (m_CurrentX + 1) : m_CurrentX; - Coeff = (DestX - m_StartX) / m_DiffX; - if (Coeff <= 1) - { - Direction = dirX; - } - } - if (abs(m_DiffY) > EPS) - { - double DestY = (m_DirY > 0) ? (m_CurrentY + 1) : m_CurrentY; - double CoeffY = (DestY - m_StartY) / m_DiffY; - if (CoeffY < Coeff) - { - Coeff = CoeffY; - Direction = dirY; - } - } - if (abs(m_DiffZ) > EPS) - { - double DestZ = (m_DirZ > 0) ? (m_CurrentZ + 1) : m_CurrentZ; - double CoeffZ = (DestZ - m_StartZ) / m_DiffZ; - if (CoeffZ < Coeff) - { - Coeff = CoeffZ; - Direction = dirZ; - } - } - - // Based on the wall hit, adjust the current coords - switch (Direction) - { - case dirX: m_CurrentX += m_DirX; m_CurrentFace = (m_DirX > 0) ? BLOCK_FACE_XM : BLOCK_FACE_XP; break; - case dirY: m_CurrentY += m_DirY; m_CurrentFace = (m_DirY > 0) ? BLOCK_FACE_YM : BLOCK_FACE_YP; break; - case dirZ: m_CurrentZ += m_DirZ; m_CurrentFace = (m_DirZ > 0) ? BLOCK_FACE_ZM : BLOCK_FACE_ZP; break; - case dirNONE: return false; - } - return true; -} - - - - - -bool cLineBlockTracer::Item(cChunk * a_Chunk) -{ - ASSERT((m_CurrentY >= 0) && (m_CurrentY < cChunkDef::Height)); // This should be provided by FixStartAboveWorld() / FixStartBelowWorld() - - // This is the actual line tracing loop. - bool Finished = false; - while (true) - { - // Report the current block through the callbacks: - if (a_Chunk == NULL) - { - m_Callbacks->OnNoChunk(); - return false; - } - if (a_Chunk->IsValid()) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - int RelX = m_CurrentX - a_Chunk->GetPosX() * cChunkDef::Width; - int RelZ = m_CurrentZ - a_Chunk->GetPosZ() * cChunkDef::Width; - a_Chunk->GetBlockTypeMeta(RelX, m_CurrentY, RelZ, BlockType, BlockMeta); - if (m_Callbacks->OnNextBlock(m_CurrentX, m_CurrentY, m_CurrentZ, BlockType, BlockMeta, m_CurrentFace)) - { - // The callback terminated the trace - return false; - } - } - else - { - if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ, m_CurrentFace)) - { - // The callback terminated the trace - return false; - } - } - - // Move to next block - if (!MoveToNextBlock()) - { - // We've reached the end - m_Callbacks->OnNoMoreHits(); - return true; - } - - // Update the current chunk - if (a_Chunk != NULL) - { - a_Chunk = a_Chunk->GetNeighborChunk(m_CurrentX, m_CurrentZ); - } - - if ((m_CurrentY < 0) || (m_CurrentY >= cChunkDef::Height)) - { - // We've gone out of the world, that's the end of this trace - double IntersectX, IntersectZ; - CalcXZIntersection(m_CurrentY, IntersectX, IntersectZ); - if (m_Callbacks->OnOutOfWorld(IntersectX, m_CurrentY, IntersectZ)) - { - // The callback terminated the trace - return false; - } - m_Callbacks->OnNoMoreHits(); - return true; - } - } -} - - - - diff --git a/source/LineBlockTracer.h b/source/LineBlockTracer.h deleted file mode 100644 index ccbb70ea6..000000000 --- a/source/LineBlockTracer.h +++ /dev/null @@ -1,87 +0,0 @@ - -// LineBlockTracer.h - -// Declares the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points - - - - - -#pragma once - -#include "BlockTracer.h" - - - - - -// fwd: Chunk.h -class cChunk; - -// fwd: cChunkMap.h -typedef cItemCallback cChunkCallback; - - - - - - -class cLineBlockTracer : - public cBlockTracer, - public cChunkCallback -{ - typedef cBlockTracer super; - -public: - cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks); - - /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) - bool Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ); - - // Utility functions for simple one-line usage: - /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) - static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ); - - /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) - static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End); - -protected: - // The start point of the trace - double m_StartX, m_StartY, m_StartZ; - - // The end point of the trace - double m_EndX, m_EndY, m_EndZ; - - // The difference in coords, End - Start - double m_DiffX, m_DiffY, m_DiffZ; - - // The increment at which the block coords are going from Start to End; either +1 or -1 - int m_DirX, m_DirY, m_DirZ; - - // The current block - int m_CurrentX, m_CurrentY, m_CurrentZ; - - // The face through which the current block has been entered - char m_CurrentFace; - - - /// Adjusts the start point above the world to just at the world's top - void FixStartAboveWorld(void); - - /// Adjusts the start point below the world to just at the world's bottom - void FixStartBelowWorld(void); - - /// Calculates the XZ coords of an intersection with the specified Yconst plane; assumes that such an intersection exists - void CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ); - - /// Moves m_Current to the next block on the line; returns false if no move is possible (reached the end) - bool MoveToNextBlock(void); - - // cChunkCallback overrides: - virtual bool Item(cChunk * a_Chunk) override; -} ; - - - - - diff --git a/source/LinearInterpolation.cpp b/source/LinearInterpolation.cpp deleted file mode 100644 index d4975418b..000000000 --- a/source/LinearInterpolation.cpp +++ /dev/null @@ -1,251 +0,0 @@ - -// LinearInterpolation.cpp - -// Implements methods for linear interpolation over 1D, 2D and 3D arrays - -#include "Globals.h" -#include "LinearInterpolation.h" - - - - - -/* -// Perform an automatic test upon program start (use breakpoints to debug): - -extern void Debug3DNoise(float * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase); - -class Test -{ -public: - Test(void) - { - // DoTest1(); - DoTest2(); - } - - - void DoTest1(void) - { - float In[8] = {0, 1, 2, 3, 1, 2, 2, 2}; - float Out[3 * 3 * 3]; - LinearInterpolate1DArray(In, 4, Out, 9); - LinearInterpolate2DArray(In, 2, 2, Out, 3, 3); - LinearInterpolate3DArray(In, 2, 2, 2, Out, 3, 3, 3); - LOGD("Out[0]: %f", Out[0]); - } - - - void DoTest2(void) - { - float In[3 * 3 * 3]; - for (int i = 0; i < ARRAYCOUNT(In); i++) - { - In[i] = (float)(i % 5); - } - float Out[15 * 16 * 17]; - LinearInterpolate3DArray(In, 3, 3, 3, Out, 15, 16, 17); - Debug3DNoise(Out, 15, 16, 17, "LERP test"); - } -} gTest; -//*/ - - - - - -// Puts linearly interpolated values from one array into another array. 1D version -void LinearInterpolate1DArray( - float * a_Src, - int a_SrcSizeX, - float * a_Dst, - int a_DstSizeX -) -{ - a_Dst[0] = a_Src[0]; - int DstSizeXm1 = a_DstSizeX - 1; - int SrcSizeXm1 = a_SrcSizeX - 1; - float fDstSizeXm1 = (float)DstSizeXm1; - float fSrcSizeXm1 = (float)SrcSizeXm1; - for (int x = 1; x < DstSizeXm1; x++) - { - int SrcIdx = x * SrcSizeXm1 / DstSizeXm1; - float ValLo = a_Src[SrcIdx]; - float ValHi = a_Src[SrcIdx + 1]; - float Ratio = (float)x * fSrcSizeXm1 / fDstSizeXm1 - SrcIdx; - a_Dst[x] = ValLo + (ValHi - ValLo) * Ratio; - } - a_Dst[a_DstSizeX - 1] = a_Src[a_SrcSizeX - 1]; -} - - - - - -// Puts linearly interpolated values from one array into another array. 2D version -void LinearInterpolate2DArray( - float * a_Src, - int a_SrcSizeX, int a_SrcSizeY, - float * a_Dst, - int a_DstSizeX, int a_DstSizeY -) -{ - ASSERT(a_DstSizeX > 0); - ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); - ASSERT(a_DstSizeY > 0); - ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); - - // Calculate interpolation ratios and src indices along each axis: - float RatioX[MAX_INTERPOL_SIZEX]; - float RatioY[MAX_INTERPOL_SIZEY]; - int SrcIdxX[MAX_INTERPOL_SIZEX]; - int SrcIdxY[MAX_INTERPOL_SIZEY]; - for (int x = 1; x < a_DstSizeX; x++) - { - SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); - RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; - } - for (int y = 1; y < a_DstSizeY; y++) - { - SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); - RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; - } - - // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: - SrcIdxX[0] = 0; - RatioX[0] = 0; - SrcIdxY[0] = 0; - RatioY[0] = 0; - SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; - RatioX[a_DstSizeX - 1] = 1; - SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; - RatioY[a_DstSizeY - 1] = 1; - - // Output all the dst array values using the indices and ratios: - int idx = 0; - for (int y = 0; y < a_DstSizeY; y++) - { - int idxLoY = a_SrcSizeX * SrcIdxY[y]; - int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); - float ry = RatioY[y]; - for (int x = 0; x < a_DstSizeX; x++) - { - // The four src corners of the current "cell": - float LoXLoY = a_Src[SrcIdxX[x] + idxLoY]; - float HiXLoY = a_Src[SrcIdxX[x] + 1 + idxLoY]; - float LoXHiY = a_Src[SrcIdxX[x] + idxHiY]; - float HiXHiY = a_Src[SrcIdxX[x] + 1 + idxHiY]; - - // Linear interpolation along the X axis: - float InterpXLoY = LoXLoY + (HiXLoY - LoXLoY) * RatioX[x]; - float InterpXHiY = LoXHiY + (HiXHiY - LoXHiY) * RatioX[x]; - - // Linear interpolation along the Y axis: - a_Dst[idx] = InterpXLoY + (InterpXHiY - InterpXLoY) * ry; - idx += 1; - } - } -} - - - - - -/// Puts linearly interpolated values from one array into another array. 3D version -void LinearInterpolate3DArray( - float * a_Src, - int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, - float * a_Dst, - int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ -) -{ - ASSERT(a_DstSizeX > 0); - ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); - ASSERT(a_DstSizeY > 0); - ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); - ASSERT(a_DstSizeZ > 0); - ASSERT(a_DstSizeZ < MAX_INTERPOL_SIZEZ); - - // Calculate interpolation ratios and src indices along each axis: - float RatioX[MAX_INTERPOL_SIZEX]; - float RatioY[MAX_INTERPOL_SIZEY]; - float RatioZ[MAX_INTERPOL_SIZEZ]; - int SrcIdxX[MAX_INTERPOL_SIZEX]; - int SrcIdxY[MAX_INTERPOL_SIZEY]; - int SrcIdxZ[MAX_INTERPOL_SIZEZ]; - for (int x = 1; x < a_DstSizeX; x++) - { - SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); - RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; - } - for (int y = 1; y < a_DstSizeY; y++) - { - SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); - RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; - } - for (int z = 1; z < a_DstSizeZ; z++) - { - SrcIdxZ[z] = z * (a_SrcSizeZ - 1) / (a_DstSizeZ - 1); - RatioZ[z] = ((float)(z * (a_SrcSizeZ - 1)) / (a_DstSizeZ - 1)) - SrcIdxZ[z]; - } - - // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: - SrcIdxX[0] = 0; - RatioX[0] = 0; - SrcIdxY[0] = 0; - RatioY[0] = 0; - SrcIdxZ[0] = 0; - RatioZ[0] = 0; - SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; - RatioX[a_DstSizeX - 1] = 1; - SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; - RatioY[a_DstSizeY - 1] = 1; - SrcIdxZ[a_DstSizeZ - 1] = a_SrcSizeZ - 2; - RatioZ[a_DstSizeZ - 1] = 1; - - // Output all the dst array values using the indices and ratios: - int idx = 0; - for (int z = 0; z < a_DstSizeZ; z++) - { - int idxLoZ = a_SrcSizeX * a_SrcSizeY * SrcIdxZ[z]; - int idxHiZ = a_SrcSizeX * a_SrcSizeY * (SrcIdxZ[z] + 1); - float rz = RatioZ[z]; - for (int y = 0; y < a_DstSizeY; y++) - { - int idxLoY = a_SrcSizeX * SrcIdxY[y]; - int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); - float ry = RatioY[y]; - for (int x = 0; x < a_DstSizeX; x++) - { - // The eight src corners of the current "cell": - float LoXLoYLoZ = a_Src[SrcIdxX[x] + idxLoY + idxLoZ]; - float HiXLoYLoZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxLoZ]; - float LoXHiYLoZ = a_Src[SrcIdxX[x] + idxHiY + idxLoZ]; - float HiXHiYLoZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxLoZ]; - float LoXLoYHiZ = a_Src[SrcIdxX[x] + idxLoY + idxHiZ]; - float HiXLoYHiZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxHiZ]; - float LoXHiYHiZ = a_Src[SrcIdxX[x] + idxHiY + idxHiZ]; - float HiXHiYHiZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxHiZ]; - - // Linear interpolation along the Z axis: - float LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * rz; - float HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * rz; - float LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * rz; - float HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * rz; - - // Linear interpolation along the Y axis: - float LoXInYInZ = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * ry; - float HiXInYInZ = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * ry; - - // Linear interpolation along the X axis: - a_Dst[idx] = LoXInYInZ + (HiXInYInZ - LoXInYInZ) * RatioX[x]; - idx += 1; - } // for x - } // for y - } // for z -} - - - - - diff --git a/source/LinearInterpolation.h b/source/LinearInterpolation.h deleted file mode 100644 index 4b798d9bc..000000000 --- a/source/LinearInterpolation.h +++ /dev/null @@ -1,60 +0,0 @@ - -// LinearInterpolation.h - -// Declares methods for linear interpolation over 1D, 2D and 3D arrays - - - - - -#pragma once - - - - - -// 2D and 3D Interpolation is optimized by precalculating the ratios into static-sized arrays -// These arrays enforce a max size of the dest array, but the limits are settable here: -const int MAX_INTERPOL_SIZEX = 256; ///< Maximum X-size of the interpolated array -const int MAX_INTERPOL_SIZEY = 512; ///< Maximum Y-size of the interpolated array -const int MAX_INTERPOL_SIZEZ = 256; ///< Maximum Z-size of the interpolated array - - - - - -/// Puts linearly interpolated values from one array into another array. 1D version -void LinearInterpolate1DArray( - float * a_Src, ///< Src array - int a_SrcSizeX, ///< Count of the src array - float * a_Dst, ///< Src array - int a_DstSizeX ///< Count of the dst array -); - - - - - -/// Puts linearly interpolated values from one array into another array. 2D version -void LinearInterpolate2DArray( - float * a_Src, ///< Src array, [x + a_SrcSizeX * y] - int a_SrcSizeX, int a_SrcSizeY, ///< Count of the src array, in each direction - float * a_Dst, ///< Dst array, [x + a_DstSizeX * y] - int a_DstSizeX, int a_DstSizeY ///< Count of the dst array, in each direction -); - - - - - -/// Puts linearly interpolated values from one array into another array. 3D version -void LinearInterpolate3DArray( - float * a_Src, ///< Src array, [x + a_SrcSizeX * y + a_SrcSizeX * a_SrcSizeY * z] - int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Count of the src array, in each direction - float * a_Dst, ///< Dst array, [x + a_DstSizeX * y + a_DstSizeX * a_DstSizeY * z] - int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ ///< Count of the dst array, in each direction -); - - - - diff --git a/source/LinearUpscale.h b/source/LinearUpscale.h deleted file mode 100644 index b7ac84c6a..000000000 --- a/source/LinearUpscale.h +++ /dev/null @@ -1,244 +0,0 @@ - -// LinearUpscale.h - -// Declares the functions for linearly upscaling arrays - -/* -Upscaling means that the array is divided into same-size "cells", and each cell is -linearly interpolated between its corners. The array's dimensions are therefore -1 + CellSize * NumCells, for each direction. - -Upscaling is more efficient than linear interpolation, because the cell sizes are integral -and therefore the cells' boundaries are on the array points. - -However, upscaling usually requires generating the "1 +" in each direction. - -Upscaling is implemented in templates, so that it's compatible with multiple datatypes. -Therefore, there is no cpp file. - -InPlace upscaling works on a single array and assumes that the values to work on have already -been interspersed into the array to the cell boundaries. -Specifically, a_Array[x * a_AnchorStepX + y * a_AnchorStepY] contains the anchor value. - -Regular upscaling takes two arrays and "moves" the input from src to dst; src is expected packed. -*/ - - - - -/** -Linearly interpolates values in the array between the equidistant anchor points (upscales). -Works in-place (input is already present at the correct output coords) -*/ -template void LinearUpscale2DArrayInPlace( - TYPE * a_Array, - int a_SizeX, int a_SizeY, // Dimensions of the array - int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction -) -{ - // First interpolate columns where the anchor points are: - int LastYCell = a_SizeY - a_AnchorStepY; - for (int y = 0; y < LastYCell; y += a_AnchorStepY) - { - int Idx = a_SizeX * y; - for (int x = 0; x < a_SizeX; x += a_AnchorStepX) - { - TYPE StartValue = a_Array[Idx]; - TYPE EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY]; - TYPE Diff = EndValue - StartValue; - for (int CellY = 1; CellY < a_AnchorStepY; CellY++) - { - a_Array[Idx + a_SizeX * CellY] = StartValue + Diff * CellY / a_AnchorStepY; - } // for CellY - Idx += a_AnchorStepX; - } // for x - } // for y - - // Now interpolate in rows, each row has values in the anchor columns - int LastXCell = a_SizeX - a_AnchorStepX; - for (int y = 0; y < a_SizeY; y++) - { - int Idx = a_SizeX * y; - for (int x = 0; x < LastXCell; x += a_AnchorStepX) - { - TYPE StartValue = a_Array[Idx]; - TYPE EndValue = a_Array[Idx + a_AnchorStepX]; - TYPE Diff = EndValue - StartValue; - for (int CellX = 1; CellX < a_AnchorStepX; CellX++) - { - a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX; - } // for CellY - Idx += a_AnchorStepX; - } - } -} - - - - - -/** -Linearly interpolates values in the array between the equidistant anchor points (upscales). -Works on two arrays, input is packed and output is to be completely constructed. -*/ -template void LinearUpscale2DArray( - TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY - int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array - TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) - int a_UpscaleX, int a_UpscaleY ///< Upscale factor for each direction -) -{ - // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes - // Feel free to enlarge them if needed, but keep in mind that they're on the stack - const int MAX_UPSCALE_X = 128; - const int MAX_UPSCALE_Y = 128; - - ASSERT(a_Src != NULL); - ASSERT(a_Dst != NULL); - ASSERT(a_SrcSizeX > 0); - ASSERT(a_SrcSizeY > 0); - ASSERT(a_UpscaleX > 0); - ASSERT(a_UpscaleY > 0); - ASSERT(a_UpscaleX <= MAX_UPSCALE_X); - ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); - - // Pre-calculate the upscaling ratios: - TYPE RatioX[MAX_UPSCALE_X]; - TYPE RatioY[MAX_UPSCALE_Y]; - for (int x = 0; x <= a_UpscaleX; x++) - { - RatioX[x] = (TYPE)x / a_UpscaleX; - } - for (int y = 0; y <= a_UpscaleY; y++) - { - RatioY[y] = (TYPE)y / a_UpscaleY; - } - - // Interpolate each XY cell: - int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; - int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; - for (int y = 0; y < (a_SrcSizeY - 1); y++) - { - int DstY = y * a_UpscaleY; - int idx = y * a_SrcSizeX; - for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) - { - int DstX = x * a_UpscaleX; - TYPE LoXLoY = a_Src[idx]; - TYPE LoXHiY = a_Src[idx + a_SrcSizeX]; - TYPE HiXLoY = a_Src[idx + 1]; - TYPE HiXHiY = a_Src[idx + 1 + a_SrcSizeX]; - for (int CellY = 0; CellY <= a_UpscaleY; CellY++) - { - int DestIdx = (DstY + CellY) * DstSizeX + DstX; - ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY); - TYPE LoXInY = LoXLoY + (LoXHiY - LoXLoY) * RatioY[CellY]; - TYPE HiXInY = HiXLoY + (HiXHiY - HiXLoY) * RatioY[CellY]; - for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) - { - a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; - } - } // for CellY - } // for x - } // for y -} - - - - - -/** -Linearly interpolates values in the array between the equidistant anchor points (upscales). -Works on two arrays, input is packed and output is to be completely constructed. -*/ -template void LinearUpscale3DArray( - TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ - int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array - TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1) - int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ ///< Upscale factor for each direction -) -{ - // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes - // Feel free to enlarge them if needed, but keep in mind that they're on the stack - const int MAX_UPSCALE_X = 128; - const int MAX_UPSCALE_Y = 128; - const int MAX_UPSCALE_Z = 128; - - ASSERT(a_Src != NULL); - ASSERT(a_Dst != NULL); - ASSERT(a_SrcSizeX > 0); - ASSERT(a_SrcSizeY > 0); - ASSERT(a_SrcSizeZ > 0); - ASSERT(a_UpscaleX > 0); - ASSERT(a_UpscaleY > 0); - ASSERT(a_UpscaleZ > 0); - ASSERT(a_UpscaleX <= MAX_UPSCALE_X); - ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); - ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z); - - // Pre-calculate the upscaling ratios: - TYPE RatioX[MAX_UPSCALE_X]; - TYPE RatioY[MAX_UPSCALE_Y]; - TYPE RatioZ[MAX_UPSCALE_Y]; - for (int x = 0; x <= a_UpscaleX; x++) - { - RatioX[x] = (TYPE)x / a_UpscaleX; - } - for (int y = 0; y <= a_UpscaleY; y++) - { - RatioY[y] = (TYPE)y / a_UpscaleY; - } - for (int z = 0; z <= a_UpscaleZ; z++) - { - RatioZ[z] = (TYPE)z / a_UpscaleZ; - } - - // Interpolate each XYZ cell: - int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; - int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; - int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1; - for (int z = 0; z < (a_SrcSizeZ - 1); z++) - { - int DstZ = z * a_UpscaleZ; - for (int y = 0; y < (a_SrcSizeY - 1); y++) - { - int DstY = y * a_UpscaleY; - int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY; - for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) - { - int DstX = x * a_UpscaleX; - TYPE LoXLoYLoZ = a_Src[idx]; - TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY]; - TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX]; - TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; - TYPE HiXLoYLoZ = a_Src[idx + 1]; - TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY]; - TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX]; - TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; - for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++) - { - TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ]; - TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ]; - TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ]; - TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ]; - for (int CellY = 0; CellY <= a_UpscaleY; CellY++) - { - int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX; - ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ); - TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY]; - TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY]; - for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) - { - a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; - } - } // for CellY - } // for CellZ - } // for x - } // for y - } // for z -} - - - - - diff --git a/source/Log.cpp b/source/Log.cpp deleted file mode 100644 index fc19595db..000000000 --- a/source/Log.cpp +++ /dev/null @@ -1,169 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Log.h" - -#include -#include -#include "OSSupport/IsThread.h" - -#if defined(ANDROID_NDK) - #include - #include "ToJava.h" -#endif - - - - -cLog* cLog::s_Log = NULL; - -cLog::cLog(const AString & a_FileName ) - : m_File(NULL) -{ - s_Log = this; - - // create logs directory - cFile::CreateFolder(FILE_IO_PREFIX + AString("logs")); - - OpenLog((FILE_IO_PREFIX + AString("logs/") + a_FileName).c_str() ); -} - - - - - -cLog::~cLog() -{ - CloseLog(); - s_Log = NULL; -} - - - - - -cLog* cLog::GetInstance() -{ - if (s_Log != NULL) - { - return s_Log; - } - - new cLog("log.txt"); - return s_Log; -} - - - - - -void cLog::CloseLog() -{ - if( m_File ) - fclose (m_File); - m_File = 0; -} - - - - - -void cLog::OpenLog( const char* a_FileName ) -{ - if(m_File) fclose (m_File); - #ifdef _MSC_VER - fopen_s( &m_File, a_FileName, "a+" ); - #else - m_File = fopen(a_FileName, "a+" ); - #endif -} - - - - - -void cLog::ClearLog() -{ - #ifdef _MSC_VER - if( fopen_s( &m_File, "log.txt", "w" ) == 0) - fclose (m_File); - #else - m_File = fopen("log.txt", "w" ); - if( m_File ) - fclose (m_File); - #endif - m_File = 0; -} - - - - - -void cLog::Log(const char * a_Format, va_list argList) -{ - AString Message; - AppendVPrintf(Message, a_Format, argList); - - time_t rawtime; - time ( &rawtime ); - - struct tm* timeinfo; -#ifdef _MSC_VER - struct tm timeinforeal; - timeinfo = &timeinforeal; - localtime_s(timeinfo, &rawtime ); -#else - timeinfo = localtime( &rawtime ); -#endif - - AString Line; - #ifdef _DEBUG - Printf(Line, "[%04x|%02d:%02d:%02d] %s", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); - #else - Printf(Line, "[%02d:%02d:%02d] %s", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); - #endif - if (m_File) - { - fprintf(m_File, "%s\n", Line.c_str(), m_File); - fflush(m_File); - } - - // Print to console: -#if defined(ANDROID_NDK) - //__android_log_vprint(ANDROID_LOG_ERROR,"MCServer", a_Format, argList); - __android_log_print(ANDROID_LOG_ERROR, "MCServer", "%s", Line.c_str() ); - //CallJavaFunction_Void_String(g_JavaThread, "AddToLog", Line ); -#else - printf("%s", Line.c_str()); -#endif - - #if defined (_WIN32) && defined(_DEBUG) - // In a Windows Debug build, output the log to debug console as well: - OutputDebugStringA((Line + "\n").c_str()); - #endif // _WIN32 -} - - - - - -void cLog::Log(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - Log( a_Format, argList ); - va_end(argList); -} - - - - - -void cLog::SimpleLog(const char* a_String) -{ - Log("%s", a_String ); -} - - - - diff --git a/source/Log.h b/source/Log.h deleted file mode 100644 index d00022c6f..000000000 --- a/source/Log.h +++ /dev/null @@ -1,30 +0,0 @@ - -#pragma once - - - - - -class cLog -{ // tolua_export -private: - FILE * m_File; - static cLog * s_Log; - -public: - cLog(const AString & a_FileName); - ~cLog(); - void Log(const char* a_Format, va_list argList ); - void Log(const char* a_Format, ...); - // tolua_begin - void SimpleLog(const char* a_String); - void OpenLog( const char* a_FileName ); - void CloseLog(); - void ClearLog(); - static cLog* GetInstance(); -}; -// tolua_end - - - - diff --git a/source/LuaExpat/lxplib.c b/source/LuaExpat/lxplib.c deleted file mode 100644 index e26343ce9..000000000 --- a/source/LuaExpat/lxplib.c +++ /dev/null @@ -1,599 +0,0 @@ -/* -** $Id: lxplib.c,v 1.16 2007/06/05 20:03:12 carregal Exp $ -** LuaExpat: Lua bind for Expat library -** See Copyright Notice in license.html -*/ - - -#include -#include -#include - -#include "expat.h" - -#include "lua.h" -#include "lauxlib.h" - - -#include "lxplib.h" - - -#if !defined(lua_pushliteral) -#define lua_pushliteral(L, s) \ - lua_pushstring(L, "" s, (sizeof(s)/sizeof(char))-1) -#endif - - -enum XPState { - XPSpre, /* parser just initialized */ - XPSok, /* state while parsing */ - XPSfinished, /* state after finished parsing */ - XPSerror, - XPSstring /* state while reading a string */ -}; - -struct lxp_userdata { - lua_State *L; - XML_Parser parser; /* associated expat parser */ - int tableref; /* table with callbacks for this parser */ - enum XPState state; - luaL_Buffer *b; /* to concatenate sequences of cdata pieces */ -}; - -typedef struct lxp_userdata lxp_userdata; - - -static int reporterror (lxp_userdata *xpu) { - lua_State *L = xpu->L; - XML_Parser p = xpu->parser; - lua_pushnil(L); - lua_pushstring(L, XML_ErrorString(XML_GetErrorCode(p))); - lua_pushnumber(L, XML_GetCurrentLineNumber(p)); - lua_pushnumber(L, XML_GetCurrentColumnNumber(p) + 1); - lua_pushnumber(L, XML_GetCurrentByteIndex(p) + 1); - return 5; -} - - -static lxp_userdata *createlxp (lua_State *L) { - lxp_userdata *xpu = (lxp_userdata *)lua_newuserdata(L, sizeof(lxp_userdata)); - xpu->tableref = LUA_REFNIL; /* in case of errors... */ - xpu->parser = NULL; - xpu->L = NULL; - xpu->state = XPSpre; - luaL_getmetatable(L, ParserType); - lua_setmetatable(L, -2); - return xpu; -} - - -static void lxpclose (lua_State *L, lxp_userdata *xpu) { - luaL_unref(L, LUA_REGISTRYINDEX, xpu->tableref); - xpu->tableref = LUA_REFNIL; - if (xpu->parser) - XML_ParserFree(xpu->parser); - xpu->parser = NULL; -} - - - - -/* -** Auxiliary function to call a Lua handle -*/ -static void docall (lxp_userdata *xpu, int nargs, int nres) { - lua_State *L = xpu->L; - assert(xpu->state == XPSok); - if (lua_pcall(L, nargs + 1, nres, 0) != 0) { - xpu->state = XPSerror; - luaL_unref(L, LUA_REGISTRYINDEX, xpu->tableref); - xpu->tableref = luaL_ref(L, LUA_REGISTRYINDEX); /* error message */ - } -} - - -/* -** Check whether there is pending Cdata, and call its handle if necessary -*/ -static void dischargestring (lxp_userdata *xpu) { - assert(xpu->state == XPSstring); - xpu->state = XPSok; - luaL_pushresult(xpu->b); - docall(xpu, 1, 0); -} - - -/* -** Check whether there is a Lua handle for a given event: If so, -** put it on the stack (to be called later), and also push `self' -*/ -static int getHandle (lxp_userdata *xpu, const char *handle) { - lua_State *L = xpu->L; - if (xpu->state == XPSstring) dischargestring(xpu); - if (xpu->state == XPSerror) - return 0; /* some error happened before; skip all handles */ - lua_pushstring(L, handle); - lua_gettable(L, 3); - if (lua_toboolean(L, -1) == 0) { - lua_pop(L, 1); - return 0; - } - if (!lua_isfunction(L, -1)) { - luaL_error(L, "lxp `%s' callback is not a function", handle); - } - lua_pushvalue(L, 1); /* first argument in every call (self) */ - return 1; -} - - - -/* -** {====================================================== -** Handles -** ======================================================= -*/ - - -static void f_StartCdata (void *ud) { - lxp_userdata *xpu = (lxp_userdata *)ud; - if (getHandle(xpu, StartCdataKey) == 0) return; /* no handle */ - docall(xpu, 0, 0); -} - - -static void f_EndCdataKey (void *ud) { - lxp_userdata *xpu = (lxp_userdata *)ud; - if (getHandle(xpu, EndCdataKey) == 0) return; /* no handle */ - docall(xpu, 0, 0); -} - - -static void f_CharData (void *ud, const char *s, int len) { - lxp_userdata *xpu = (lxp_userdata *)ud; - if (xpu->state == XPSok) { - if (getHandle(xpu, CharDataKey) == 0) return; /* no handle */ - xpu->state = XPSstring; - luaL_buffinit(xpu->L, xpu->b); - } - if (xpu->state == XPSstring) - luaL_addlstring(xpu->b, s, len); -} - - -static void f_Comment (void *ud, const char *data) { - lxp_userdata *xpu = (lxp_userdata *)ud; - if (getHandle(xpu, CommentKey) == 0) return; /* no handle */ - lua_pushstring(xpu->L, data); - docall(xpu, 1, 0); -} - - -static void f_Default (void *ud, const char *data, int len) { - lxp_userdata *xpu = (lxp_userdata *)ud; - if (getHandle(xpu, DefaultKey) == 0) return; /* no handle */ - lua_pushlstring(xpu->L, data, len); - docall(xpu, 1, 0); -} - - -static void f_DefaultExpand (void *ud, const char *data, int len) { - lxp_userdata *xpu = (lxp_userdata *)ud; - if (getHandle(xpu, DefaultExpandKey) == 0) return; /* no handle */ - lua_pushlstring(xpu->L, data, len); - docall(xpu, 1, 0); -} - - -static void f_StartElement (void *ud, const char *name, const char **attrs) { - lxp_userdata *xpu = (lxp_userdata *)ud; - lua_State *L = xpu->L; - int lastspec = XML_GetSpecifiedAttributeCount(xpu->parser) / 2; - int i = 1; - if (getHandle(xpu, StartElementKey) == 0) return; /* no handle */ - lua_pushstring(L, name); - lua_newtable(L); - while (*attrs) { - if (i <= lastspec) { - lua_pushnumber(L, i++); - lua_pushstring(L, *attrs); - lua_settable(L, -3); - } - lua_pushstring(L, *attrs++); - lua_pushstring(L, *attrs++); - lua_settable(L, -3); - } - docall(xpu, 2, 0); /* call function with self, name, and attributes */ -} - - -static void f_EndElement (void *ud, const char *name) { - lxp_userdata *xpu = (lxp_userdata *)ud; - if (getHandle(xpu, EndElementKey) == 0) return; /* no handle */ - lua_pushstring(xpu->L, name); - docall(xpu, 1, 0); -} - - -static int f_ExternaEntity (XML_Parser p, const char *context, - const char *base, - const char *systemId, - const char *publicId) { - lxp_userdata *xpu = (lxp_userdata *)XML_GetUserData(p); - lua_State *L = xpu->L; - lxp_userdata *child; - int status; - if (getHandle(xpu, ExternalEntityKey) == 0) return 1; /* no handle */ - child = createlxp(L); - child->parser = XML_ExternalEntityParserCreate(p, context, NULL); - if (!child->parser) - luaL_error(L, "XML_ParserCreate failed"); - lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /*lua_getref(L, xpu->tableref); */ /* child uses the same table of its father */ - child->tableref = luaL_ref(L, LUA_REGISTRYINDEX); - lua_pushstring(L, base); - lua_pushstring(L, systemId); - lua_pushstring(L, publicId); - docall(xpu, 4, 1); - status = lua_toboolean(L, -1); - lua_pop(L, 1); - lxpclose(L, child); - return status; -} - - -static void f_StartNamespaceDecl (void *ud, const char *prefix, - const char *uri) { - lxp_userdata *xpu = (lxp_userdata *)ud; - lua_State *L = xpu->L; - if (getHandle(xpu, StartNamespaceDeclKey) == 0) return; /* no handle */ - lua_pushstring(L, prefix); - lua_pushstring(L, uri); - docall(xpu, 2, 0); -} - - -static void f_EndNamespaceDecl (void *ud, const char *prefix) { - lxp_userdata *xpu = (lxp_userdata *)ud; - if (getHandle(xpu, EndNamespaceDeclKey) == 0) return; /* no handle */ - lua_pushstring(xpu->L, prefix); - docall(xpu, 1, 0); -} - - -static void f_NotationDecl (void *ud, const char *notationName, - const char *base, - const char *systemId, - const char *publicId) { - lxp_userdata *xpu = (lxp_userdata *)ud; - lua_State *L = xpu->L; - if (getHandle(xpu, NotationDeclKey) == 0) return; /* no handle */ - lua_pushstring(L, notationName); - lua_pushstring(L, base); - lua_pushstring(L, systemId); - lua_pushstring(L, publicId); - docall(xpu, 4, 0); -} - - -static int f_NotStandalone (void *ud) { - int status; - lxp_userdata *xpu = (lxp_userdata *)ud; - lua_State *L = xpu->L; - if (getHandle(xpu, NotStandaloneKey) == 0) return 1; /* no handle */ - docall(xpu, 0, 1); - status = lua_toboolean(L, -1); - lua_pop(L, 1); - return status; -} - - -static void f_ProcessingInstruction (void *ud, const char *target, - const char *data) { - lxp_userdata *xpu = (lxp_userdata *)ud; - lua_State *L = xpu->L; - if (getHandle(xpu, ProcessingInstructionKey) == 0) return; /* no handle */ - lua_pushstring(L, target); - lua_pushstring(L, data); - docall(xpu, 2, 0); -} - - -static void f_UnparsedEntityDecl (void *ud, const char *entityName, - const char *base, - const char *systemId, - const char *publicId, - const char *notationName) { - lxp_userdata *xpu = (lxp_userdata *)ud; - lua_State *L = xpu->L; - if (getHandle(xpu, UnparsedEntityDeclKey) == 0) return; /* no handle */ - lua_pushstring(L, entityName); - lua_pushstring(L, base); - lua_pushstring(L, systemId); - lua_pushstring(L, publicId); - lua_pushstring(L, notationName); - docall(xpu, 5, 0); -} - -static void f_StartDoctypeDecl (void *ud, const XML_Char *doctypeName, - const XML_Char *sysid, - const XML_Char *pubid, - int has_internal_subset) { - lxp_userdata *xpu = (lxp_userdata *)ud; - if (getHandle(xpu, StartDoctypeDeclKey) == 0) return; /* no handle */ - lua_pushstring(xpu->L, doctypeName); - lua_pushstring(xpu->L, sysid); - lua_pushstring(xpu->L, pubid); - lua_pushboolean(xpu->L, has_internal_subset); - docall(xpu, 4, 0); -} - -/* }====================================================== */ - - - -static int hasfield (lua_State *L, const char *fname) { - int res; - lua_pushstring(L, fname); - lua_gettable(L, 1); - res = !lua_isnil(L, -1); - lua_pop(L, 1); - return res; -} - - -static void checkcallbacks (lua_State *L) { - static const char *const validkeys[] = { - "StartCdataSection", "EndCdataSection", "CharacterData", "Comment", - "Default", "DefaultExpand", "StartElement", "EndElement", - "ExternalEntityRef", "StartNamespaceDecl", "EndNamespaceDecl", - "NotationDecl", "NotStandalone", "ProcessingInstruction", - "UnparsedEntityDecl", "StartDoctypeDecl", NULL}; - if (hasfield(L, "_nonstrict")) return; - lua_pushnil(L); - while (lua_next(L, 1)) { - lua_pop(L, 1); /* remove value */ -#if ! defined (LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 - if (lua_type(L, -1) != LUA_TSTRING || - luaL_findstring(lua_tostring(L, -1), validkeys) < 0) - luaL_error(L, "invalid key `%s' in callback table", lua_tostring(L, -1)); -#else - luaL_checkoption(L, -1, NULL, validkeys); -#endif - } -} - - -static int lxp_make_parser (lua_State *L) { - XML_Parser p; - char sep = *luaL_optstring(L, 2, ""); - lxp_userdata *xpu = createlxp(L); - p = xpu->parser = (sep == '\0') ? XML_ParserCreate(NULL) : - XML_ParserCreateNS(NULL, sep); - if (!p) - luaL_error(L, "XML_ParserCreate failed"); - luaL_checktype(L, 1, LUA_TTABLE); - checkcallbacks(L); - lua_pushvalue(L, 1); - xpu->tableref = luaL_ref(L, LUA_REGISTRYINDEX); - XML_SetUserData(p, xpu); - if (hasfield(L, StartCdataKey) || hasfield(L, EndCdataKey)) - XML_SetCdataSectionHandler(p, f_StartCdata, f_EndCdataKey); - if (hasfield(L, CharDataKey)) - XML_SetCharacterDataHandler(p, f_CharData); - if (hasfield(L, CommentKey)) - XML_SetCommentHandler(p, f_Comment); - if (hasfield(L, DefaultKey)) - XML_SetDefaultHandler(p, f_Default); - if (hasfield(L, DefaultExpandKey)) - XML_SetDefaultHandlerExpand(p, f_DefaultExpand); - if (hasfield(L, StartElementKey) || hasfield(L, EndElementKey)) - XML_SetElementHandler(p, f_StartElement, f_EndElement); - if (hasfield(L, ExternalEntityKey)) - XML_SetExternalEntityRefHandler(p, f_ExternaEntity); - if (hasfield(L, StartNamespaceDeclKey) || hasfield(L, EndNamespaceDeclKey)) - XML_SetNamespaceDeclHandler(p, f_StartNamespaceDecl, f_EndNamespaceDecl); - if (hasfield(L, NotationDeclKey)) - XML_SetNotationDeclHandler(p, f_NotationDecl); - if (hasfield(L, NotStandaloneKey)) - XML_SetNotStandaloneHandler(p, f_NotStandalone); - if (hasfield(L, ProcessingInstructionKey)) - XML_SetProcessingInstructionHandler(p, f_ProcessingInstruction); - if (hasfield(L, UnparsedEntityDeclKey)) - XML_SetUnparsedEntityDeclHandler(p, f_UnparsedEntityDecl); - if (hasfield(L, StartDoctypeDeclKey)) - XML_SetStartDoctypeDeclHandler(p, f_StartDoctypeDecl); - return 1; -} - - -static lxp_userdata *checkparser (lua_State *L, int idx) { - lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, idx, ParserType); - luaL_argcheck(L, xpu, idx, "expat parser expected"); - luaL_argcheck(L, xpu->parser, idx, "parser is closed"); - return xpu; -} - - -static int parser_gc (lua_State *L) { - lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, 1, ParserType); - luaL_argcheck(L, xpu, 1, "expat parser expected"); - lxpclose(L, xpu); - return 0; -} - - -static int setbase (lua_State *L) { - lxp_userdata *xpu = checkparser(L, 1); - if (XML_SetBase(xpu->parser, luaL_checkstring(L, 2)) == 0) - luaL_error(L, "no memory to store base"); - return 0; -} - - -static int getbase (lua_State *L) { - lxp_userdata *xpu = checkparser(L, 1); - lua_pushstring(L, XML_GetBase(xpu->parser)); - return 1; -} - - -static int getcallbacks (lua_State *L) { - lxp_userdata *xpu = checkparser(L, 1); - lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); - return 1; -} - - -static int parse_aux (lua_State *L, lxp_userdata *xpu, const char *s, - size_t len) { - luaL_Buffer b; - int status; - xpu->L = L; - xpu->state = XPSok; - xpu->b = &b; - lua_settop(L, 2); - lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /*lua_getref(L, xpu->tableref);*/ /* to be used by handlers */ - status = XML_Parse(xpu->parser, s, (int)len, s == NULL); - if (xpu->state == XPSstring) dischargestring(xpu); - if (xpu->state == XPSerror) { /* callback error? */ - lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /* get original msg. */ - lua_error(L); - } - if (s == NULL) xpu->state = XPSfinished; - if (status) { - lua_pushboolean(L, 1); - return 1; - } - else { /* error */ - return reporterror(xpu); - } -} - - -static int lxp_parse (lua_State *L) { - lxp_userdata *xpu = checkparser(L, 1); - size_t len; - const char *s = luaL_optlstring(L, 2, NULL, &len); - if (xpu->state == XPSfinished && s != NULL) { - lua_pushnil(L); - lua_pushliteral(L, "cannot parse - document is finished"); - return 2; - } - return parse_aux(L, xpu, s, len); -} - - -static int lxp_close (lua_State *L) { - int status = 1; - lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, 1, ParserType); - luaL_argcheck(L, xpu, 1, "expat parser expected"); - if (xpu->state != XPSfinished) - status = parse_aux(L, xpu, NULL, 0); - lxpclose(L, xpu); - if (status > 1) luaL_error(L, "error closing parser: %s", - lua_tostring(L, -status+1)); - return 0; -} - - -static int lxp_pos (lua_State *L) { - lxp_userdata *xpu = checkparser(L, 1); - XML_Parser p = xpu->parser; - lua_pushnumber(L, XML_GetCurrentLineNumber(p)); - lua_pushnumber(L, XML_GetCurrentColumnNumber(p) + 1); - lua_pushnumber(L, XML_GetCurrentByteIndex(p) + 1); - return 3; -} - - -static int lxp_setencoding (lua_State *L) { - lxp_userdata *xpu = checkparser(L, 1); - const char *encoding = luaL_checkstring(L, 2); - luaL_argcheck(L, xpu->state == XPSpre, 1, "invalid parser state"); - XML_SetEncoding(xpu->parser, encoding); - return 0; -} - -static int lxp_stop (lua_State *L) { - lxp_userdata *xpu = checkparser(L, 1); - lua_pushboolean(L, XML_StopParser(xpu->parser, XML_FALSE) == XML_STATUS_OK); - return 1; -} - -#if !defined LUA_VERSION_NUM -/* Lua 5.0 */ -#define luaL_Reg luaL_reg -#endif - -static const struct luaL_Reg lxp_meths[] = { - {"parse", lxp_parse}, - {"close", lxp_close}, - {"__gc", parser_gc}, - {"pos", lxp_pos}, - {"setencoding", lxp_setencoding}, - {"getcallbacks", getcallbacks}, - {"getbase", getbase}, - {"setbase", setbase}, - {"stop", lxp_stop}, - {NULL, NULL} -}; - -static const struct luaL_Reg lxp_funcs[] = { - {"new", lxp_make_parser}, - {NULL, NULL} -}; - - -/* -** Assumes the table is on top of the stack. -*/ -static void set_info (lua_State *L) { - lua_pushliteral (L, "_COPYRIGHT"); - lua_pushliteral (L, "Copyright (C) 2003-2012 Kepler Project"); - lua_settable (L, -3); - lua_pushliteral (L, "_DESCRIPTION"); - lua_pushliteral (L, "LuaExpat is a SAX XML parser based on the Expat library"); - lua_settable (L, -3); - lua_pushliteral (L, "_VERSION"); - lua_pushliteral (L, "LuaExpat 1.3.0"); - lua_settable (L, -3); -} - - -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 -/* -** Adapted from Lua 5.2.0 -*/ -static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -nup); - lua_pushstring(L, l->name); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); - } - lua_pop(L, nup); /* remove upvalues */ -} -#endif - - -int luaopen_lxp (lua_State *L) { - luaL_newmetatable(L, ParserType); - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, -2); - lua_rawset(L, -3); - - luaL_setfuncs (L, lxp_meths, 0); - lua_pop (L, 1); /* remove metatable */ - - // _X 2013_04_09: Modified to allow embedding - luaL_openlib (L, "lxp", lxp_funcs, 0); - /* - lua_newtable (L); - luaL_setfuncs (L, lxp_funcs, 0); - */ - set_info (L); - return 1; -} diff --git a/source/LuaExpat/lxplib.h b/source/LuaExpat/lxplib.h deleted file mode 100644 index 9c0be4f78..000000000 --- a/source/LuaExpat/lxplib.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -** See Copyright Notice in license.html -*/ - -#define ParserType "Expat" - -#define StartCdataKey "StartCdataSection" -#define EndCdataKey "EndCdataSection" -#define CharDataKey "CharacterData" -#define CommentKey "Comment" -#define DefaultKey "Default" -#define DefaultExpandKey "DefaultExpand" -#define StartElementKey "StartElement" -#define EndElementKey "EndElement" -#define ExternalEntityKey "ExternalEntityRef" -#define StartNamespaceDeclKey "StartNamespaceDecl" -#define EndNamespaceDeclKey "EndNamespaceDecl" -#define NotationDeclKey "NotationDecl" -#define NotStandaloneKey "NotStandalone" -#define ProcessingInstructionKey "ProcessingInstruction" -#define UnparsedEntityDeclKey "UnparsedEntityDecl" -#define StartDoctypeDeclKey "StartDoctypeDecl" - -int luaopen_lxp (lua_State *L); diff --git a/source/LuaFunctions.h b/source/LuaFunctions.h deleted file mode 100644 index 0ad420881..000000000 --- a/source/LuaFunctions.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "MCLogger.h" -#include -// tolua_begin - -unsigned int GetTime() -{ - return (unsigned int)time(0); -} - -std::string GetChar( std::string & a_Str, unsigned int a_Idx ) -{ - return std::string(1, a_Str[ a_Idx ]); -} - -// tolua_end diff --git a/source/LuaState.cpp b/source/LuaState.cpp deleted file mode 100644 index 8d2fa8eca..000000000 --- a/source/LuaState.cpp +++ /dev/null @@ -1,958 +0,0 @@ - -// LuaState.cpp - -// Implements the cLuaState class representing the wrapper over lua_State *, provides associated helper functions - -#include "Globals.h" -#include "LuaState.h" - -extern "C" -{ - #include "lualib.h" -} - -#include "tolua++.h" -#include "Bindings.h" -#include "ManualBindings.h" - -// fwd: SQLite/lsqlite3.c -extern "C" -{ - LUALIB_API int luaopen_lsqlite3(lua_State * L); -} - -// fwd: LuaExpat/lxplib.c: -extern "C" -{ - int luaopen_lxp(lua_State * L); -} - - - - - - -const cLuaState::cRet cLuaState::Return = {}; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cLuaState: - -cLuaState::cLuaState(const AString & a_SubsystemName) : - m_LuaState(NULL), - m_IsOwned(false), - m_SubsystemName(a_SubsystemName), - m_NumCurrentFunctionArgs(-1) -{ -} - - - - - -cLuaState::cLuaState(lua_State * a_AttachState) : - m_LuaState(a_AttachState), - m_IsOwned(false), - m_SubsystemName(""), - m_NumCurrentFunctionArgs(-1) -{ -} - - - - - -cLuaState::~cLuaState() -{ - if (IsValid()) - { - if (m_IsOwned) - { - Close(); - } - else - { - Detach(); - } - } -} - - - - - -void cLuaState::Create(void) -{ - if (m_LuaState != NULL) - { - LOGWARNING("%s: Trying to create an already-existing LuaState, ignoring.", __FUNCTION__); - return; - } - m_LuaState = lua_open(); - luaL_openlibs(m_LuaState); - tolua_AllToLua_open(m_LuaState); - ManualBindings::Bind(m_LuaState); - luaopen_lsqlite3(m_LuaState); - luaopen_lxp(m_LuaState); - m_IsOwned = true; -} - - - - - -void cLuaState::Close(void) -{ - if (m_LuaState == NULL) - { - LOGWARNING("%s: Trying to close an invalid LuaState, ignoring.", __FUNCTION__); - return; - } - if (!m_IsOwned) - { - LOGWARNING( - "%s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead.", - __FUNCTION__, m_LuaState - ); - Detach(); - return; - } - lua_close(m_LuaState); - m_LuaState = NULL; - m_IsOwned = false; -} - - - - - -void cLuaState::Attach(lua_State * a_State) -{ - if (m_LuaState != NULL) - { - LOGINFO("%s: Already contains a LuaState (0x%p), will be closed / detached.", __FUNCTION__, m_LuaState); - if (m_IsOwned) - { - Close(); - } - else - { - Detach(); - } - } - m_LuaState = a_State; - m_IsOwned = false; -} - - - - - -void cLuaState::Detach(void) -{ - if (m_LuaState == NULL) - { - return; - } - if (m_IsOwned) - { - LOGWARNING( - "%s: Detected a mis-use, calling Detach() when the state is owned. Closing the owned state (0x%p).", - __FUNCTION__, m_LuaState - ); - Close(); - return; - } - m_LuaState = NULL; -} - - - - - -bool cLuaState::LoadFile(const AString & a_FileName) -{ - ASSERT(IsValid()); - - // Load the file: - int s = luaL_loadfile(m_LuaState, a_FileName.c_str()); - if (ReportErrors(s)) - { - LOGWARNING("Can't load %s because of an error in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); - return false; - } - - // Execute the globals: - s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0); - if (ReportErrors(s)) - { - LOGWARNING("Error in %s in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); - return false; - } - - return true; -} - - - - - -bool cLuaState::HasFunction(const char * a_FunctionName) -{ - if (!IsValid()) - { - // This happens if cPlugin::Initialize() fails with an error - return false; - } - - lua_getglobal(m_LuaState, a_FunctionName); - bool res = (!lua_isnil(m_LuaState, -1) && lua_isfunction(m_LuaState, -1)); - lua_pop(m_LuaState, 1); - return res; -} - - - - - -bool cLuaState::PushFunction(const char * a_FunctionName) -{ - ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack - - if (!IsValid()) - { - // This happens if cPlugin::Initialize() fails with an error - return false; - } - - lua_getglobal(m_LuaState, a_FunctionName); - if (!lua_isfunction(m_LuaState, -1)) - { - LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName); - lua_pop(m_LuaState, 1); - return false; - } - m_CurrentFunctionName.assign(a_FunctionName); - m_NumCurrentFunctionArgs = 0; - return true; -} - - - - - -bool cLuaState::PushFunction(int a_FnRef) -{ - ASSERT(IsValid()); - ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack - - lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref() - if (!lua_isfunction(m_LuaState, -1)) - { - lua_pop(m_LuaState, 1); - return false; - } - m_CurrentFunctionName = ""; - m_NumCurrentFunctionArgs = 0; - return true; -} - - - - - -bool cLuaState::PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName) -{ - ASSERT(IsValid()); - ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack - - lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef); // Get the table ref - if (!lua_istable(m_LuaState, -1)) - { - // Not a table, bail out - lua_pop(m_LuaState, 1); - return false; - } - lua_getfield(m_LuaState, -1, a_FnName); - if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1)) - { - // Not a valid function, bail out - lua_pop(m_LuaState, 2); - return false; - } - lua_remove(m_LuaState, -2); // Remove the table ref from the stack - m_CurrentFunctionName = ""; - m_NumCurrentFunctionArgs = 0; - return true; -} - - - - - -void cLuaState::Push(const AString & a_String) -{ - ASSERT(IsValid()); - - tolua_pushcppstring(m_LuaState, a_String); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(const AStringVector & a_Vector) -{ - ASSERT(IsValid()); - - lua_createtable(m_LuaState, a_Vector.size(), 0); - int newTable = lua_gettop(m_LuaState); - int index = 1; - for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index) - { - tolua_pushstring(m_LuaState, itr->c_str()); - lua_rawseti(m_LuaState, newTable, index); - } - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::PushUserType(void * a_Object, const char * a_Type) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Object, a_Type); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(int a_Value) -{ - ASSERT(IsValid()); - - tolua_pushnumber(m_LuaState, a_Value); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(double a_Value) -{ - ASSERT(IsValid()); - - tolua_pushnumber(m_LuaState, a_Value); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(const char * a_Value) -{ - ASSERT(IsValid()); - - tolua_pushstring(m_LuaState, a_Value); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(bool a_Value) -{ - ASSERT(IsValid()); - - tolua_pushboolean(m_LuaState, a_Value ? 1 : 0); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cWorld * a_World) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_World, "cWorld"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cPlayer * a_Player) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(const cPlayer * a_Player) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cEntity * a_Entity) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Entity, "cEntity"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cMonster * a_Monster) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Monster, "cMonster"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cItem * a_Item) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Item, "cItem"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cItems * a_Items) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Items, "cItems"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cClientHandle * a_Client) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Client, "cClientHandle"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cPickup * a_Pickup) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Pickup, "cPickup"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cChunkDesc * a_ChunkDesc) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(const cCraftingGrid * a_Grid) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(const cCraftingRecipe * a_Recipe) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(TakeDamageInfo * a_TDI) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cWindow * a_Window) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Window, "cWindow"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cPluginLua * a_Plugin) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Plugin, "cPluginLua"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(const HTTPRequest * a_Request) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPRequest"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cWebAdmin * a_WebAdmin) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(const HTTPTemplateRequest * a_Request) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPTemplateRequest"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cTNTEntity * a_TNTEntity) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_TNTEntity, "cTNTEntity"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cCreeper * a_Creeper) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Creeper, "cCreeper"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(Vector3i * a_Vector) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Vector, "Vector3i"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(void * a_Ptr) -{ - ASSERT(IsValid()); - - lua_pushnil(m_LuaState); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cHopperEntity * a_Hopper) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cBlockEntity * a_BlockEntity) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_BlockEntity, "cBlockEntity"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::GetReturn(int a_StackPos, bool & a_ReturnedVal) -{ - a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0); -} - - - - - -void cLuaState::GetReturn(int a_StackPos, AString & a_ReturnedVal) -{ - if (lua_isstring(m_LuaState, a_StackPos)) - { - a_ReturnedVal = tolua_tocppstring(m_LuaState, a_StackPos, a_ReturnedVal.c_str()); - } -} - - - - - -void cLuaState::GetReturn(int a_StackPos, int & a_ReturnedVal) -{ - if (lua_isnumber(m_LuaState, a_StackPos)) - { - a_ReturnedVal = (int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); - } -} - - - - - -void cLuaState::GetReturn(int a_StackPos, double & a_ReturnedVal) -{ - if (lua_isnumber(m_LuaState, a_StackPos)) - { - a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); - } -} - - - - - -bool cLuaState::CallFunction(int a_NumResults) -{ - ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first - ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); - - int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, 0); - if (ReportErrors(s)) - { - LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), m_CurrentFunctionName.c_str()); - m_NumCurrentFunctionArgs = -1; - m_CurrentFunctionName.clear(); - return false; - } - m_NumCurrentFunctionArgs = -1; - m_CurrentFunctionName.clear(); - return true; -} - - - - - -bool cLuaState::CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam) -{ - ASSERT(IsValid()); - - if (a_EndParam < 0) - { - a_EndParam = a_StartParam; - } - - tolua_Error tolua_err; - for (int i = a_StartParam; i <= a_EndParam; i++) - { - if (tolua_isusertype(m_LuaState, i, a_UserType, 0, &tolua_err)) - { - continue; - } - // Not the correct parameter - lua_Debug entry; - VERIFY(lua_getstack(m_LuaState, 0, &entry)); - VERIFY(lua_getinfo (m_LuaState, "n", &entry)); - AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); - tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); - return false; - } // for i - Param - - // All params checked ok - return true; -} - - - - - -bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam) -{ - ASSERT(IsValid()); - - if (a_EndParam < 0) - { - a_EndParam = a_StartParam; - } - - tolua_Error tolua_err; - for (int i = a_StartParam; i <= a_EndParam; i++) - { - if (tolua_istable(m_LuaState, i, 0, &tolua_err)) - { - continue; - } - // Not the correct parameter - lua_Debug entry; - VERIFY(lua_getstack(m_LuaState, 0, &entry)); - VERIFY(lua_getinfo (m_LuaState, "n", &entry)); - AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); - tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); - return false; - } // for i - Param - - // All params checked ok - return true; -} - - - - - -bool cLuaState::CheckParamNumber(int a_StartParam, int a_EndParam) -{ - ASSERT(IsValid()); - - if (a_EndParam < 0) - { - a_EndParam = a_StartParam; - } - - tolua_Error tolua_err; - for (int i = a_StartParam; i <= a_EndParam; i++) - { - if (tolua_isnumber(m_LuaState, i, 0, &tolua_err)) - { - continue; - } - // Not the correct parameter - lua_Debug entry; - VERIFY(lua_getstack(m_LuaState, 0, &entry)); - VERIFY(lua_getinfo (m_LuaState, "n", &entry)); - AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); - tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); - return false; - } // for i - Param - - // All params checked ok - return true; -} - - - - - -bool cLuaState::CheckParamEnd(int a_Param) -{ - tolua_Error tolua_err; - if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err)) - { - return true; - } - // Not the correct parameter - lua_Debug entry; - VERIFY(lua_getstack(m_LuaState, 0, &entry)); - VERIFY(lua_getinfo (m_LuaState, "n", &entry)); - AString ErrMsg = Printf("#ferror in function '%s': Too many arguments.", (entry.name != NULL) ? entry.name : "?"); - tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); - return false; -} - - - - - -bool cLuaState::ReportErrors(int a_Status) -{ - return ReportErrors(m_LuaState, a_Status); -} - - - - - -bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status) -{ - if (a_Status == 0) - { - // No error to report - return false; - } - - LOGWARNING("LUA: %d - %s", a_Status, lua_tostring(a_LuaState, -1)); - lua_pop(a_LuaState, 1); - return true; -} - - - - - -void cLuaState::LogStackTrace(void) -{ - LOGWARNING("Stack trace:"); - lua_Debug entry; - int depth = 0; - while (lua_getstack(m_LuaState, depth, &entry)) - { - int status = lua_getinfo(m_LuaState, "Sln", &entry); - assert(status); - - LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?"); - depth++; - } - LOGWARNING("Stack trace end"); -} - - - - - -AString cLuaState::GetTypeText(int a_StackPos) -{ - int Type = lua_type(m_LuaState, a_StackPos); - switch (Type) - { - case LUA_TNONE: return "TNONE"; - case LUA_TNIL: return "TNIL"; - case LUA_TBOOLEAN: return "TBOOLEAN"; - case LUA_TLIGHTUSERDATA: return "TLIGHTUSERDATA"; - case LUA_TNUMBER: return "TNUMBER"; - case LUA_TSTRING: return "TSTRING"; - case LUA_TTABLE: return "TTABLE"; - case LUA_TFUNCTION: return "TFUNCTION"; - case LUA_TUSERDATA: return "TUSERDATA"; - case LUA_TTHREAD: return "TTHREAD"; - } - return Printf("Unknown (%d)", Type); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cLuaState::cRef: - -cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) : - m_LuaState(a_LuaState) -{ - ASSERT(m_LuaState.IsValid()); - - lua_pushvalue(m_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack - m_Ref = luaL_ref(m_LuaState, LUA_REGISTRYINDEX); -} - - - - - -cLuaState::cRef::~cRef() -{ - ASSERT(m_LuaState.IsValid()); - - if (IsValid()) - { - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref); - } -} - - - - diff --git a/source/LuaState.h b/source/LuaState.h deleted file mode 100644 index caba2484d..000000000 --- a/source/LuaState.h +++ /dev/null @@ -1,811 +0,0 @@ - -// LuaState.h - -// Declares the cLuaState class representing the wrapper over lua_State *, provides associated helper functions - -/* -The contained lua_State can be either owned or attached. -Owned lua_State is created by calling Create() and the cLuaState automatically closes the state -Or, lua_State can be attached by calling Attach(), the cLuaState doesn't close such a state -Attaching a state will automatically close an owned state. - -Calling a Lua function is done by pushing the function, either by PushFunction() or PushFunctionFromRegistry(), -then pushing the arguments (PushString(), PushNumber(), PushUserData() etc.) and finally -executing CallFunction(). cLuaState automatically keeps track of the number of arguments and the name of the -function (for logging purposes), which makes the call less error-prone. - -Reference management is provided by the cLuaState::cRef class. This is used when you need to hold a reference to -any Lua object across several function calls; usually this is used for callbacks. The class is RAII-like, with -automatic resource management. -*/ - - - - -#pragma once - -extern "C" -{ - #include "lauxlib.h" -} - - - - - -class cWorld; -class cPlayer; -class cEntity; -class cMonster; -class cItem; -class cItems; -class cClientHandle; -class cPickup; -class cChunkDesc; -class cCraftingGrid; -class cCraftingRecipe; -struct TakeDamageInfo; -class cWindow; -class cPluginLua; -struct HTTPRequest; -class cWebAdmin; -struct HTTPTemplateRequest; -class cTNTEntity; -class cCreeper; -class Vector3i; -class cHopperEntity; -class cBlockEntity; - - - - - -/// Encapsulates a Lua state and provides some syntactic sugar for common operations -class cLuaState -{ -public: - - /// Used for storing references to object in the global registry - class cRef - { - public: - /// Creates a reference in the specified LuaState for object at the specified StackPos - cRef(cLuaState & a_LuaState, int a_StackPos); - ~cRef(); - - /// Returns true if the reference is valid - bool IsValid(void) const {return (m_Ref != LUA_REFNIL); } - - /// Allows to use this class wherever an int (i. e. ref) is to be used - operator int(void) const { return m_Ref; } - - protected: - cLuaState & m_LuaState; - int m_Ref; - } ; - - - /// A dummy class that's used only to delimit function args from return values for cLuaState::Call() - class cRet - { - } ; - - static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call() - - - /** Creates a new instance. The LuaState is not initialized. - a_SubsystemName is used for reporting problems in the console, it is "plugin %s" for plugins, - or "LuaScript" for the cLuaScript template - */ - cLuaState(const AString & a_SubsystemName); - - /** Creates a new instance. The a_AttachState is attached. - Subsystem name is set to "". - */ - explicit cLuaState(lua_State * a_AttachState); - - ~cLuaState(); - - /// Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions - operator lua_State * (void) { return m_LuaState; } - - /// Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor - void Create(void); - - /// Closes the m_LuaState, if not closed already - void Close(void); - - /// Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor - void Attach(lua_State * a_State); - - /// Detaches a previously attached state. - void Detach(void); - - /// Returns true if the m_LuaState is valid - bool IsValid(void) const { return (m_LuaState != NULL); } - - /** Loads the specified file - Returns false and logs a warning to the console if not successful (but the LuaState is kept open). - m_SubsystemName is displayed in the warning log message. - */ - bool LoadFile(const AString & a_FileName); - - /// Returns true if a_FunctionName is a valid Lua function that can be called - bool HasFunction(const char * a_FunctionName); - - /** Pushes the function of the specified name onto the stack. - Returns true if successful. Logs a warning on failure (incl. m_SubsystemName) - */ - bool PushFunction(const char * a_FunctionName); - - /** Pushes a function that has been saved into the global registry, identified by a_FnRef. - Returns true if successful. Logs a warning on failure - */ - bool PushFunction(int a_FnRef); - - /** Pushes a function that is stored in a table ref. - Returns true if successful, false on failure. Doesn't log failure. - */ - bool PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName); - - /// Pushes a usertype of the specified class type onto the stack - void PushUserType(void * a_Object, const char * a_Type); - - // Push a value onto the stack - void Push(const AString & a_String); - void Push(const AStringVector & a_Vector); - void Push(int a_Value); - void Push(double a_Value); - void Push(const char * a_Value); - void Push(bool a_Value); - void Push(cWorld * a_World); - void Push(cPlayer * a_Player); - void Push(const cPlayer * a_Player); - void Push(cEntity * a_Entity); - void Push(cMonster * a_Monster); - void Push(cItem * a_Item); - void Push(cItems * a_Items); - void Push(cClientHandle * a_ClientHandle); - void Push(cPickup * a_Pickup); - void Push(cChunkDesc * a_ChunkDesc); - void Push(const cCraftingGrid * a_Grid); - void Push(const cCraftingRecipe * a_Recipe); - void Push(TakeDamageInfo * a_TDI); - void Push(cWindow * a_Window); - void Push(cPluginLua * a_Plugin); - void Push(const HTTPRequest * a_Request); - void Push(cWebAdmin * a_WebAdmin); - void Push(const HTTPTemplateRequest * a_Request); - void Push(cTNTEntity * a_TNTEntity); - void Push(cCreeper * a_Creeper); - void Push(Vector3i * a_Vector); - void Push(void * a_Ptr); - void Push(cHopperEntity * a_Hopper); - void Push(cBlockEntity * a_BlockEntity); - - /// Call any 0-param 0-return Lua function in a single line: - template - bool Call(FnT a_FnName) - { - if (!PushFunction(a_FnName)) - { - return false; - } - return CallFunction(0); - } - - /// Call any 1-param 0-return Lua function in a single line: - template< - typename FnT, - typename ArgT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - return CallFunction(0); - } - - /// Call any 2-param 0-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - return CallFunction(0); - } - - /// Call any 1-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 2-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 3-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 4-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 5-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 6-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 7-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename ArgT7, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 8-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename ArgT7, typename ArgT8, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 9-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename ArgT7, typename ArgT8, typename ArgT9, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - Push(a_Arg9); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 10-param 1-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename ArgT7, typename ArgT8, typename ArgT9, typename ArgT10, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, ArgT10 a_Arg10, const cRet & a_Mark, RetT1 & a_Ret1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - Push(a_Arg9); - Push(a_Arg10); - if (!CallFunction(1)) - { - return false; - } - GetReturn(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /// Call any 1-param 2-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - if (!CallFunction(2)) - { - return false; - } - GetReturn(-2, a_Ret1); - GetReturn(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /// Call any 2-param 2-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - if (!CallFunction(2)) - { - return false; - } - GetReturn(-2, a_Ret1); - GetReturn(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /// Call any 3-param 2-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - if (!CallFunction(2)) - { - return false; - } - GetReturn(-2, a_Ret1); - GetReturn(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /// Call any 4-param 2-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - if (!CallFunction(2)) - { - return false; - } - GetReturn(-2, a_Ret1); - GetReturn(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /// Call any 5-param 2-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - if (!CallFunction(2)) - { - return false; - } - GetReturn(-2, a_Ret1); - GetReturn(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /// Call any 6-param 2-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - if (!CallFunction(2)) - { - return false; - } - GetReturn(-2, a_Ret1); - GetReturn(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /// Call any 7-param 2-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, typename ArgT7, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - if (!CallFunction(2)) - { - return false; - } - GetReturn(-2, a_Ret1); - GetReturn(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /// Call any 7-param 3-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, typename ArgT7, - typename RetT1, typename RetT2, typename RetT3 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - if (!CallFunction(3)) - { - return false; - } - GetReturn(-3, a_Ret1); - GetReturn(-2, a_Ret2); - GetReturn(-1, a_Ret3); - lua_pop(m_LuaState, 3); - return true; - } - - /// Call any 8-param 3-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, typename ArgT7, typename ArgT8, - typename RetT1, typename RetT2, typename RetT3 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - if (!CallFunction(3)) - { - return false; - } - GetReturn(-3, a_Ret1); - GetReturn(-2, a_Ret2); - GetReturn(-1, a_Ret3); - lua_pop(m_LuaState, 3); - return true; - } - - /// Call any 9-param 5-return Lua function in a single line: - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9, - typename RetT1, typename RetT2, typename RetT3, typename RetT4, typename RetT5 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3, RetT4 & a_Ret4, RetT5 & a_Ret5) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - Push(a_Arg9); - if (!CallFunction(5)) - { - return false; - } - GetReturn(-5, a_Ret1); - GetReturn(-4, a_Ret2); - GetReturn(-3, a_Ret3); - GetReturn(-2, a_Ret4); - GetReturn(-1, a_Ret5); - lua_pop(m_LuaState, 5); - return true; - } - - - /// Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged - void GetReturn(int a_StackPos, bool & a_ReturnedVal); - - /// Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged - void GetReturn(int a_StackPos, AString & a_ReturnedVal); - - /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged - void GetReturn(int a_StackPos, int & a_ReturnedVal); - - /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged - void GetReturn(int a_StackPos, double & a_ReturnedVal); - - /** - Calls the function that has been pushed onto the stack by PushFunction(), - with arguments pushed by PushXXX(). - Returns true if successful, logs a warning on failure. - */ - bool CallFunction(int a_NumReturnValues); - - /// Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not - bool CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam = -1); - - /// Returns true if the specified parameters on the stack are a table; also logs warning if not - bool CheckParamTable(int a_StartParam, int a_EndParam = -1); - - /// Returns true if the specified parameters on the stack are a number; also logs warning if not - bool CheckParamNumber(int a_StartParam, int a_EndParam = -1); - - /// Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) - bool CheckParamEnd(int a_Param); - - /// If the status is nonzero, prints the text on the top of Lua stack and returns true - bool ReportErrors(int status); - - /// If the status is nonzero, prints the text on the top of Lua stack and returns true - static bool ReportErrors(lua_State * a_LuaState, int status); - - /// Logs all items in the current stack trace to the server console - void LogStackTrace(void); - - /// Returns the type of the item on the specified position in the stack - AString GetTypeText(int a_StackPos); - -protected: - lua_State * m_LuaState; - - /// If true, the state is owned by this object and will be auto-Closed. False => attached state - bool m_IsOwned; - - /** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript" - whatever is given to the constructor - */ - AString m_SubsystemName; - - /// Name of the currently pushed function (for the Push / Call chain) - AString m_CurrentFunctionName; - - /// Number of arguments currently pushed (for the Push / Call chain) - int m_NumCurrentFunctionArgs; -} ; - - - - diff --git a/source/LuaWindow.cpp b/source/LuaWindow.cpp deleted file mode 100644 index 9011d668c..000000000 --- a/source/LuaWindow.cpp +++ /dev/null @@ -1,185 +0,0 @@ - -// LuaWindow.cpp - -// Implements the cLuaWindow class representing a virtual window that plugins may create and open for the player - -#include "Globals.h" -#include "LuaWindow.h" -#include "UI/SlotArea.h" -#include "PluginLua.h" -#include "Entities/Player.h" -#include "lauxlib.h" // Needed for LUA_REFNIL - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cLuaWindow: - -cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : - super(a_WindowType, a_Title), - m_Contents(a_SlotsX, a_SlotsY), - m_Plugin(NULL), - m_LuaRef(LUA_REFNIL), - m_OnClosingFnRef(LUA_REFNIL), - m_OnSlotChangedFnRef(LUA_REFNIL) -{ - m_Contents.AddListener(*this); - m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this)); - - // If appropriate, add an Armor slot area: - switch (a_WindowType) - { - case cWindow::wtInventory: - case cWindow::wtWorkbench: - { - m_SlotAreas.push_back(new cSlotAreaArmor(*this)); - break; - } - } - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - - -cLuaWindow::~cLuaWindow() -{ - m_Contents.RemoveListener(*this); - - // Must delete slot areas now, because they are referencing this->m_Contents and would try to access it in cWindow's - // destructor, when the member is already gone. - for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) - { - delete *itr; - } - m_SlotAreas.clear(); - - ASSERT(m_OpenedBy.empty()); -} - - - - - -void cLuaWindow::SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef) -{ - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); - ASSERT(m_LuaRef == LUA_REFNIL); - m_Plugin = a_Plugin; - m_LuaRef = a_LuaRef; -} - - - - - -bool cLuaWindow::IsLuaReferenced(void) const -{ - return ((m_Plugin != NULL) && (m_LuaRef != LUA_REFNIL)); -} - - - - - -void cLuaWindow::SetOnClosing(cPluginLua * a_Plugin, int a_FnRef) -{ - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); - - // If there already was a function, unreference it first - if (m_OnClosingFnRef != LUA_REFNIL) - { - m_Plugin->Unreference(m_OnClosingFnRef); - } - - // Store the new reference - m_Plugin = a_Plugin; - m_OnClosingFnRef = a_FnRef; -} - - - - - -void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) -{ - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); - - // If there already was a function, unreference it first - if (m_OnSlotChangedFnRef != LUA_REFNIL) - { - m_Plugin->Unreference(m_OnSlotChangedFnRef); - } - - // Store the new reference - m_Plugin = a_Plugin; - m_OnSlotChangedFnRef = a_FnRef; -} - - - - - -bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) -{ - // First notify the plugin through the registered callback: - if (m_OnClosingFnRef != LUA_REFNIL) - { - ASSERT(m_Plugin != NULL); - if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse)) - { - // The callback disagrees (the higher levels check the CanRefuse flag compliance) - return false; - } - } - - return super::ClosedByPlayer(a_Player, a_CanRefuse); -} - - - - - -void cLuaWindow::Destroy(void) -{ - super::Destroy(); - - if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != NULL)) - { - // The object is referenced by Lua, un-reference it - m_Plugin->Unreference(m_LuaRef); - } - - // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it! - m_IsDestroyed = false; -} - - - - - -void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) -{ - if (a_ItemGrid != &m_Contents) - { - ASSERT(!"Invalid ItemGrid in callback"); - return; - } - - // If an OnSlotChanged callback has been registered, call it: - if (m_OnSlotChangedFnRef != LUA_REFNIL) - { - m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum); - } -} - - - - diff --git a/source/LuaWindow.h b/source/LuaWindow.h deleted file mode 100644 index 4c32c263e..000000000 --- a/source/LuaWindow.h +++ /dev/null @@ -1,95 +0,0 @@ - -// LuaWindow.h - -// Declares the cLuaWindow class representing a virtual window that plugins may create and open for the player - - - - - -#pragma once - -#include "UI/Window.h" -#include "ItemGrid.h" - - - - - -// fwd: PluginLua.h -class cPluginLua; - - - - - -/** A window that has been created by a Lua plugin and is handled entirely by that plugin -This object needs extra care with its lifetime management: -- It is created by Lua, so Lua expects to garbage-collect it later -- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them -To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() -delete the window, but rather leaves it dangling, with only Lua having the reference to it. -Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for -cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object. -This reference needs to be unreferenced in the Destroy() function. -*/ -class cLuaWindow : // tolua_export - public cItemGrid::cListener, - // tolua_begin - public cWindow -{ - typedef cWindow super; - -public: - /// Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size - cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); - - virtual ~cLuaWindow(); - - /// Returns the internal representation of the contents that are manipulated by Lua - cItemGrid & GetContents(void) { return m_Contents; } - - // tolua_end - - /** Sets the plugin reference and the internal Lua object reference index - used for preventing Lua's GC to collect this class while the window is open - */ - void SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef); - - /// Returns true if SetLuaRef() has been called - bool IsLuaReferenced(void) const; - - /// Sets the callback function (Lua reference) to call when the window is about to close - void SetOnClosing(cPluginLua * a_Plugin, int a_FnRef); - - /// Sets the callback function (Lua reference) to call when a slot is changed - void SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef); - -protected: - /// Contents of the non-inventory part - cItemGrid m_Contents; - - /// The plugin that has opened the window and owns the m_LuaRef - cPluginLua * m_Plugin; - - /// The Lua object reference, used for keeping the object alive as long as any player has the window open - int m_LuaRef; - - /// The Lua reference for the callback to call when the window is closing for any player - int m_OnClosingFnRef; - - /// The Lua reference for the callback to call when a slot has changed - int m_OnSlotChangedFnRef; - - // cWindow overrides: - virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; - virtual void Destroy(void) override; - - // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; -} ; // tolua_export - - - - - diff --git a/source/MCLogger.cpp b/source/MCLogger.cpp deleted file mode 100644 index 4f3e5dc0f..000000000 --- a/source/MCLogger.cpp +++ /dev/null @@ -1,261 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include -#include "Log.h" - - - - - -cMCLogger * cMCLogger::s_MCLogger = NULL; -bool g_ShouldColorOutput = false; - -#ifdef _WIN32 - #include // Needed for _isatty(), not available on Linux - - HANDLE g_Console = GetStdHandle(STD_OUTPUT_HANDLE); - WORD g_DefaultConsoleAttrib = 0x07; -#elif defined (__linux) && !defined(ANDROID_NDK) - #include // Needed for isatty() on Linux -#endif - - - - - -cMCLogger * cMCLogger::GetInstance(void) -{ - return s_MCLogger; -} - - - - - -cMCLogger::cMCLogger(void) -{ - AString FileName; - Printf(FileName, "LOG_%d.txt", (int)time(NULL)); - InitLog(FileName); -} - - - - - -cMCLogger::cMCLogger(const AString & a_FileName) -{ - InitLog(a_FileName); -} - - - - - -cMCLogger::~cMCLogger() -{ - m_Log->Log("--- Stopped Log ---\n"); - delete m_Log; - if (this == s_MCLogger) - { - s_MCLogger = NULL; - } -} - - - - - -void cMCLogger::InitLog(const AString & a_FileName) -{ - m_Log = new cLog(a_FileName); - m_Log->Log("--- Started Log ---\n"); - - s_MCLogger = this; - - #ifdef _WIN32 - // See whether we are writing to a console the default console attrib: - g_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0); - if (g_ShouldColorOutput) - { - CONSOLE_SCREEN_BUFFER_INFO sbi; - GetConsoleScreenBufferInfo(g_Console, &sbi); - g_DefaultConsoleAttrib = sbi.wAttributes; - } - #elif defined (__linux) && !defined(ANDROID_NDK) - g_ShouldColorOutput = isatty(fileno(stdout)); - // TODO: Check if the terminal supports colors, somehow? - #endif -} - - - - - -void cMCLogger::LogSimple(const char* a_Text, int a_LogType /* = 0 */ ) -{ - switch( a_LogType ) - { - case 0: - LOG("%s", a_Text); - break; - case 1: - LOGINFO("%s", a_Text); - break; - case 2: - LOGWARN("%s", a_Text); - break; - case 3: - LOGERROR("%s", a_Text); - break; - default: - LOG("(#%d#: %s", a_LogType, a_Text); - break; - } -} - - - - - -void cMCLogger::Log(const char * a_Format, va_list a_ArgList) -{ - cCSLock Lock(m_CriticalSection); - SetColor(csRegular); - m_Log->Log(a_Format, a_ArgList); - ResetColor(); - puts(""); -} - - - - - -void cMCLogger::Info(const char * a_Format, va_list a_ArgList) -{ - cCSLock Lock(m_CriticalSection); - SetColor(csInfo); - m_Log->Log(a_Format, a_ArgList); - ResetColor(); - puts(""); -} - - - - - -void cMCLogger::Warn(const char * a_Format, va_list a_ArgList) -{ - cCSLock Lock(m_CriticalSection); - SetColor(csWarning); - m_Log->Log(a_Format, a_ArgList); - ResetColor(); - puts(""); -} - - - - - -void cMCLogger::Error(const char * a_Format, va_list a_ArgList) -{ - cCSLock Lock(m_CriticalSection); - SetColor(csError); - m_Log->Log(a_Format, a_ArgList); - ResetColor(); - puts(""); -} - - - - - -void cMCLogger::SetColor(eColorScheme a_Scheme) -{ - if (!g_ShouldColorOutput) - { - return; - } - #ifdef _WIN32 - WORD Attrib = 0x07; // by default, gray on black - switch (a_Scheme) - { - case csRegular: Attrib = 0x07; break; // Gray on black - case csInfo: Attrib = 0x0e; break; // Yellow on black - case csWarning: Attrib = 0x0c; break; // Read on black - case csError: Attrib = 0xc0; break; // Black on red - default: ASSERT(!"Unhandled color scheme"); - } - SetConsoleTextAttribute(g_Console, Attrib); - #elif defined(__linux) && !defined(ANDROID_NDK) - switch (a_Scheme) - { - case csRegular: printf("\x1b[0m"); break; // Whatever the console default is - case csInfo: printf("\x1b[33;1m"); break; // Yellow on black - case csWarning: printf("\x1b[31;1m"); break; // Red on black - case csError: printf("\x1b[1;33;41;1m"); break; // Yellow on red - default: ASSERT(!"Unhandled color scheme"); - } - #endif -} - - - - - -void cMCLogger::ResetColor(void) -{ - if (!g_ShouldColorOutput) - { - return; - } - #ifdef _WIN32 - SetConsoleTextAttribute(g_Console, g_DefaultConsoleAttrib); - #elif defined(__linux) && !defined(ANDROID_NDK) - printf("\x1b[0m"); - #endif -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Global functions - -void LOG(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - cMCLogger::GetInstance()->Log( a_Format, argList ); - va_end(argList); -} - -void LOGINFO(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - cMCLogger::GetInstance()->Info( a_Format, argList ); - va_end(argList); -} - -void LOGWARN(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - cMCLogger::GetInstance()->Warn( a_Format, argList ); - va_end(argList); -} - -void LOGERROR(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - cMCLogger::GetInstance()->Error( a_Format, argList ); - va_end(argList); -} - - - - diff --git a/source/MCLogger.h b/source/MCLogger.h deleted file mode 100644 index c949a4cdf..000000000 --- a/source/MCLogger.h +++ /dev/null @@ -1,84 +0,0 @@ - -#pragma once - - - - -class cLog; - - - - - -class cMCLogger // tolua_export -{ // tolua_export -public: // tolua_export - /// Creates a logger with the default filename, "logs/LOG_.log" - cMCLogger(void); - - /// Creates a logger with the specified filename inside "logs" folder - cMCLogger(const AString & a_FileName); // tolua_export - - ~cMCLogger(); // tolua_export - - void Log(const char* a_Format, va_list a_ArgList); - void Info(const char* a_Format, va_list a_ArgList); - void Warn(const char* a_Format, va_list a_ArgList); - void Error(const char* a_Format, va_list a_ArgList); - - void LogSimple(const char* a_Text, int a_LogType = 0 ); // tolua_export - - static cMCLogger* GetInstance(); -private: - enum eColorScheme - { - csRegular, - csInfo, - csWarning, - csError, - } ; - - cCriticalSection m_CriticalSection; - cLog * m_Log; - static cMCLogger * s_MCLogger; - - - /// Sets the specified color scheme in the terminal (TODO: if coloring available) - void SetColor(eColorScheme a_Scheme); - - /// Resets the color back to whatever is the default in the terminal - void ResetColor(void); - - /// Common initialization for all constructors, creates a logfile with the specified name and assigns s_MCLogger to this - void InitLog(const AString & a_FileName); -}; // tolua_export - - - - - -extern void LOG(const char* a_Format, ...); -extern void LOGINFO(const char* a_Format, ...); -extern void LOGWARN(const char* a_Format, ...); -extern void LOGERROR(const char* a_Format, ...); - - - - - -// In debug builds, translate LOGD to LOG, otherwise leave it out altogether: -#ifdef _DEBUG - #define LOGD LOG -#else - #define LOGD(...) -#endif // _DEBUG - - - - - -#define LOGWARNING LOGWARN - - - - diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp deleted file mode 100644 index f98e25880..000000000 --- a/source/ManualBindings.cpp +++ /dev/null @@ -1,2215 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "ManualBindings.h" -#include "tolua++.h" - -#include "Root.h" -#include "World.h" -#include "Plugin.h" -#include "PluginLua.h" -#include "PluginManager.h" -#include "Entities/Player.h" -#include "WebAdmin.h" -#include "ClientHandle.h" -#include "BlockEntities/ChestEntity.h" -#include "BlockEntities/DispenserEntity.h" -#include "BlockEntities/DropperEntity.h" -#include "BlockEntities/FurnaceEntity.h" -#include "BlockEntities/HopperEntity.h" -#include "md5/md5.h" -#include "LuaWindow.h" -#include "LineBlockTracer.h" - - - - - -/**************************** - * Better error reporting for Lua - **/ -int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError) -{ - // Retrieve current function name - lua_Debug entry; - VERIFY(lua_getstack(L, 0, &entry)); - VERIFY(lua_getinfo(L, "n", &entry)); - - // Insert function name into error msg - AString msg(a_pMsg); - ReplaceString(msg, "#funcname#", entry.name?entry.name:"?"); - - // Send the error to Lua - tolua_error(L, msg.c_str(), a_pToLuaError); - return 0; -} - - - - - -int lua_do_error(lua_State* L, const char * a_pFormat, ...) -{ - // Retrieve current function name - lua_Debug entry; - VERIFY(lua_getstack(L, 0, &entry)); - VERIFY(lua_getinfo(L, "n", &entry)); - - // Insert function name into error msg - AString msg(a_pFormat); - ReplaceString(msg, "#funcname#", entry.name?entry.name:"?"); - - // Copied from luaL_error and modified - va_list argp; - va_start(argp, a_pFormat); - luaL_where(L, 1); - lua_pushvfstring(L, msg.c_str(), argp); - va_end(argp); - lua_concat(L, 2); - return lua_error(L); -} - - - - - -/**************************** - * Lua bound functions with special return types - **/ - -static int tolua_StringSplit(lua_State * tolua_S) -{ - cLuaState LuaState(tolua_S); - std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0); - std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0); - - AStringVector Split = StringSplit(str, delim); - LuaState.Push(Split); - return 1; -} - - - - - -static int tolua_StringSplitAndTrim(lua_State * tolua_S) -{ - cLuaState LuaState(tolua_S); - std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0); - std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0); - - AStringVector Split = StringSplitAndTrim(str, delim); - LuaState.Push(Split); - return 1; -} - - - - - -static int tolua_LOG(lua_State* tolua_S) -{ - const char* str = tolua_tocppstring(tolua_S,1,0); - cMCLogger::GetInstance()->LogSimple( str, 0 ); - return 0; -} - - - - - -static int tolua_LOGINFO(lua_State* tolua_S) -{ - const char* str = tolua_tocppstring(tolua_S,1,0); - cMCLogger::GetInstance()->LogSimple( str, 1 ); - return 0; -} - - - - - -static int tolua_LOGWARN(lua_State* tolua_S) -{ - const char* str = tolua_tocppstring(tolua_S,1,0); - cMCLogger::GetInstance()->LogSimple( str, 2 ); - return 0; -} - - - - - -static int tolua_LOGERROR(lua_State* tolua_S) -{ - const char* str = tolua_tocppstring(tolua_S,1,0); - cMCLogger::GetInstance()->LogSimple( str, 3 ); - return 0; -} - - - - - -cPluginLua * GetLuaPlugin(lua_State * L) -{ - // Get the plugin identification out of LuaState: - lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME); - if (!lua_islightuserdata(L, -1)) - { - LOGWARNING("%s: cannot get plugin instance, what have you done to my Lua state?", __FUNCTION__); - lua_pop(L, 1); - return NULL; - } - cPluginLua * Plugin = (cPluginLua *)lua_topointer(L, -1); - lua_pop(L, 1); - - return Plugin; -} - - - - - -template< - class Ty1, - class Ty2, - bool (Ty1::*Func1)(const AString &, cItemCallback &) - > -static int tolua_DoWith(lua_State* tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 2) && (NumArgs != 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); - - const char * ItemName = tolua_tocppstring(tolua_S, 2, ""); - if ((ItemName == NULL) || (ItemName[0] == 0)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1", NumArgs); - } - if (!lua_isfunction( tolua_S, 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 3) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs); - } - - class cLuaCallback : public cItemCallback - { - public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState ) - , FuncRef( a_FuncRef ) - , TableRef( a_TableRef ) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; // Abort enumeration - } - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - - bool bRetVal = (self->*Func1)(ItemName, Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal ); - return 1; -} - - - - - -template< - class Ty1, - class Ty2, - bool (Ty1::*Func1)(int, cItemCallback &) -> -static int tolua_DoWithID(lua_State* tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 2) && (NumArgs != 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, 0); - - int ItemID = (int)tolua_tonumber(tolua_S, 2, 0); - if (!lua_isfunction(tolua_S, 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 3) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs); - } - - class cLuaCallback : public cItemCallback - { - public: - cLuaCallback(lua_State * a_LuaState, int a_FuncRef, int a_TableRef) : - LuaState(a_LuaState), - FuncRef(a_FuncRef), - TableRef(a_TableRef) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); // Push the item - if (TableRef != LUA_REFNIL) - { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; // Abort enumeration - } - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - - bool bRetVal = (self->*Func1)(ItemID, Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal ); - return 1; -} - - - - - -template< - class Ty1, - class Ty2, - bool (Ty1::*Func1)(int, int, int, cItemCallback &) -> -static int tolua_DoWithXYZ(lua_State* tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 4) && (NumArgs != 5)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 4 or 5 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); - if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3) || !lua_isnumber(tolua_S, 4)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3"); - } - - int ItemX = ((int)tolua_tonumber(tolua_S, 2, 0)); - int ItemY = ((int)tolua_tonumber(tolua_S, 3, 0)); - int ItemZ = ((int)tolua_tonumber(tolua_S, 4, 0)); - LOG("x %i y %i z %i", ItemX, ItemY, ItemZ ); - if (!lua_isfunction( tolua_S, 5)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #4"); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 5) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #5"); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #4"); - } - - class cLuaCallback : public cItemCallback - { - public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState ) - , FuncRef( a_FuncRef ) - , TableRef( a_TableRef ) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; // Abort enumeration - } - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - bool bRetVal = (self->*Func1)(ItemX, ItemY, ItemZ, Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal ); - return 1; -} - - - - - -template< class Ty1, - class Ty2, - bool (Ty1::*Func1)(int, int, cItemCallback &) > -static int tolua_ForEachInChunk(lua_State* tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 3) && (NumArgs != 4)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 3 or 4 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); - if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1 and #2"); - } - - int ChunkX = ((int)tolua_tonumber(tolua_S, 2, 0)); - int ChunkZ = ((int)tolua_tonumber(tolua_S, 3, 0)); - - if (!lua_isfunction( tolua_S, 4)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #3"); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 4) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #4"); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #3"); - } - - class cLuaCallback : public cItemCallback - { - public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState ) - , FuncRef( a_FuncRef ) - , TableRef( a_TableRef ) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; /* Abort enumeration */ - } - - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - bool bRetVal = (self->*Func1)(ChunkX, ChunkZ, Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal ); - return 1; -} - - - - - -template< class Ty1, - class Ty2, - bool (Ty1::*Func1)(cItemCallback &) > -static int tolua_ForEach(lua_State * tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if( NumArgs != 1 && NumArgs != 2) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 1 or 2 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); - if (self == NULL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); - } - - if (!lua_isfunction( tolua_S, 2)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 2) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #2"); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); - } - - class cLuaCallback : public cItemCallback - { - public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState ) - , FuncRef( a_FuncRef ) - , TableRef( a_TableRef ) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype( LuaState, a_Item, Ty2::GetClassStatic() ); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; /* Abort enumeration */ - } - - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean( LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - bool bRetVal = (self->*Func1)(Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal ); - return 1; -} - - - - - -static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S) -{ - // Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight) - // Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight] - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || - !tolua_isnoobj (tolua_S, 5, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); - #ifndef TOLUA_RELEASE - if (self == NULL) - { - tolua_error(tolua_S, "invalid 'self' in function 'GetBlockInfo'", NULL); - } - #endif - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight; - bool res = self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight); - tolua_pushboolean(tolua_S, res ? 1 : 0); - if (res) - { - tolua_pushnumber(tolua_S, BlockType); - tolua_pushnumber(tolua_S, BlockMeta); - tolua_pushnumber(tolua_S, BlockSkyLight); - tolua_pushnumber(tolua_S, BlockBlockLight); - return 5; - } - } - } - return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'GetBlockInfo'.", &tolua_err); - return 0; - #endif -} - - - - - -static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S) -{ - // Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta) - // Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta] - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || - !tolua_isnoobj (tolua_S, 5, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); - #ifndef TOLUA_RELEASE - if (self == NULL) - { - tolua_error(tolua_S, "invalid 'self' in function 'GetBlockTypeMeta'", NULL); - } - #endif - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - bool res = self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta); - tolua_pushboolean(tolua_S, res ? 1 : 0); - if (res) - { - tolua_pushnumber(tolua_S, BlockType); - tolua_pushnumber(tolua_S, BlockMeta); - return 3; - } - } - } - return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'GetBlockTypeMeta'.", &tolua_err); - return 0; - #endif -} - - - - - -static int tolua_cWorld_GetSignLines(lua_State * tolua_S) -{ - // Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4) - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || - !tolua_isnoobj (tolua_S, 10, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); - #ifndef TOLUA_RELEASE - if (self == NULL) - { - tolua_error(tolua_S, "invalid 'self' in function 'GetSignLines'", NULL); - } - #endif - { - AString Line1, Line2, Line3, Line4; - bool res = self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); - tolua_pushboolean(tolua_S, res ? 1 : 0); - if (res) - { - tolua_pushstring(tolua_S, Line1.c_str()); - tolua_pushstring(tolua_S, Line2.c_str()); - tolua_pushstring(tolua_S, Line3.c_str()); - tolua_pushstring(tolua_S, Line4.c_str()); - return 5; - } - } - } - return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'GetSignLines'.", &tolua_err); - return 0; - #endif -} - - - - - -static int tolua_cWorld_SetSignLines(lua_State * tolua_S) -{ - // Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4) - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || - !tolua_iscppstring(tolua_S, 5, 0, &tolua_err) || - !tolua_iscppstring(tolua_S, 6, 0, &tolua_err) || - !tolua_iscppstring(tolua_S, 7, 0, &tolua_err) || - !tolua_iscppstring(tolua_S, 8, 0, &tolua_err) || - !tolua_isusertype (tolua_S, 9, "cPlayer", 1, &tolua_err) || - !tolua_isnoobj (tolua_S, 10, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); - const AString Line1 = tolua_tocppstring(tolua_S, 5, 0); - const AString Line2 = tolua_tocppstring(tolua_S, 6, 0); - const AString Line3 = tolua_tocppstring(tolua_S, 7, 0); - const AString Line4 = tolua_tocppstring(tolua_S, 8, 0); - cPlayer * Player = (cPlayer *)tolua_tousertype (tolua_S, 9, NULL); - #ifndef TOLUA_RELEASE - if (self == NULL) - { - tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines' / 'UpdateSign'", NULL); - } - #endif - { - bool res = self->UpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player); - tolua_pushboolean(tolua_S, res ? 1 : 0); - } - } - return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'SetSignLines' / 'UpdateSign'.", &tolua_err); - return 0; - #endif -} - - - - -static int tolua_cWorld_TryGetHeight(lua_State * tolua_S) -{ - // Exported manually, because tolua would require the out-only param a_Height to be used when calling - // Takes (a_World,) a_BlockX, a_BlockZ - // Returns Height, IsValid - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnoobj (tolua_S, 4, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 3, 0); - #ifndef TOLUA_RELEASE - if (self == NULL) - { - tolua_error(tolua_S, "Invalid 'self' in function 'TryGetHeight'", NULL); - } - #endif - { - int Height = 0; - bool res = self->TryGetHeight(BlockX, BlockZ, Height); - tolua_pushnumber(tolua_S, Height); - tolua_pushboolean(tolua_S, res ? 1 : 0); - } - } - return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'TryGetHeight'.", &tolua_err); - return 0; - #endif -} - - - - - -class cLuaWorldTask : - public cWorld::cTask -{ -public: - cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - m_Plugin(a_Plugin), - m_FnRef(a_FnRef) - { - } - -protected: - cPluginLua & m_Plugin; - int m_FnRef; - - // cWorld::cTask overrides: - virtual void Run(cWorld & a_World) override - { - m_Plugin.Call(m_FnRef, &a_World); - } -} ; - - - - - -static int tolua_cWorld_QueueTask(lua_State * tolua_S) -{ - // Binding for cWorld::QueueTask - // Params: function - - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = GetLuaPlugin(tolua_S); - if (Plugin == NULL) - { - // An error message has been already printed in GetLuaPlugin() - return 0; - } - - // Retrieve the args: - cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, 0); - if (self == NULL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); - } - if (!lua_isfunction(tolua_S, 2)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); - } - - // Create a reference to the function: - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FnRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); - } - - self->QueueTask(new cLuaWorldTask(*Plugin, FnRef)); - return 0; -} - - - - - -static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) -{ - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - - const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins(); - - lua_newtable(tolua_S); - //lua_createtable(tolua_S, AllPlugins.size(), 0); - int newTable = lua_gettop(tolua_S); - int index = 1; - cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin(); - while(iter != AllPlugins.end()) - { - const cPlugin* Plugin = iter->second; - tolua_pushstring( tolua_S, iter->first.c_str() ); - if( Plugin != NULL ) - { - tolua_pushusertype( tolua_S, (void*)Plugin, "const cPlugin" ); - } - else - { - tolua_pushboolean(tolua_S, 0); - } - //lua_rawseti(tolua_S, newTable, index); - lua_rawset(tolua_S, -3); - ++iter; - ++index; - } - return 1; -} - - - - - -static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx) -{ - // Helper function for cPluginmanager:AddHook() binding - // Takes care of the new case (#121): args are HOOK_TYPE and CallbackFunction - // The arg types have already been checked - - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = GetLuaPlugin(S); - if (Plugin == NULL) - { - // An error message has been already printed in GetLuaPlugin() - return 0; - } - - // Retrieve and check the hook type - int HookType = (int)tolua_tonumber(S, a_ParamIdx, -1); - if (!a_PluginManager->IsValidHookType(HookType)) - { - LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); - S.LogStackTrace(); - return 0; - } - - // Add the hook to the plugin - if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1)) - { - LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType); - S.LogStackTrace(); - return 0; - } - a_PluginManager->AddHook(Plugin, HookType); - - // Success - return 0; -} - - - - - -static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx) -{ - // Helper function for cPluginmanager:AddHook() binding - // Takes care of the old case (#121): args are cPluginLua and HOOK_TYPE - // The arg types have already been checked - - // Retrieve and check the cPlugin parameter - cPluginLua * Plugin = (cPluginLua *)tolua_tousertype(S, a_ParamIdx, NULL); - if (Plugin == NULL) - { - LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, expected a valid cPlugin object. Hook not added"); - S.LogStackTrace(); - return 0; - } - if (Plugin != GetLuaPlugin(S)) - { - // The plugin parameter passed to us is not our stored plugin. Disallow this! - LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, cannot add hook to foreign plugins. Hook not added."); - S.LogStackTrace(); - return 0; - } - - // Retrieve and check the hook type - int HookType = (int)tolua_tonumber(S, a_ParamIdx + 1, -1); - if (!a_PluginManager->IsValidHookType(HookType)) - { - LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); - S.LogStackTrace(); - return 0; - } - - // Get the standard name for the callback function: - const char * FnName = cPluginLua::GetHookFnName(HookType); - if (FnName == NULL) - { - LOGWARNING("cPluginManager.AddHook(): Unknown hook type (%d). Hook not added.", HookType); - S.LogStackTrace(); - return 0; - } - - // Retrieve the function to call and add it to the plugin: - lua_pushstring(S, FnName); - bool res = Plugin->AddHookRef(HookType, 1); - lua_pop(S, 1); // Pop the function off the stack - if (!res) - { - LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName); - S.LogStackTrace(); - return 0; - } - a_PluginManager->AddHook(Plugin, HookType); - - // Success - return 0; -} - - - - - -static int tolua_cPluginManager_AddHook(lua_State * tolua_S) -{ - /* - Function signatures: - cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) -- (1) recommended - cPluginManager:Get():AddHook(HOOK_TYPE, CallbackFunction) -- (2) accepted silently - cPluginManager:Get():AddHook(Plugin, HOOK_TYPE) -- (3) old style (#121), accepted but complained about - cPluginManager.AddHook(Plugin, HOOK_TYPE) -- (4) old style (#121) mangled, accepted but complained about - */ - - cLuaState S(tolua_S); - cPluginManager * PlgMgr = cPluginManager::Get(); - - // If the first param is a cPluginManager, use it instead of the global one: - int ParamIdx = 1; - tolua_Error err; - if (tolua_isusertype(S, 1, "cPluginManager", 0, &err)) - { - // Style 2 or 3, retrieve the PlgMgr instance - PlgMgr = (cPluginManager *)tolua_tousertype(S, 1, NULL); - if (PlgMgr == NULL) - { - LOGWARNING("Malformed plugin, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction). Fixing the call for you."); - S.LogStackTrace(); - PlgMgr = cPluginManager::Get(); - } - ParamIdx += 1; - } - - if (lua_isnumber(S, ParamIdx) && lua_isfunction(S, ParamIdx + 1)) - { - // The next params are a number and a function, assume style 1 or 2 - return tolua_cPluginManager_AddHook_FnRef(PlgMgr, S, ParamIdx); - } - else if (tolua_isusertype(S, ParamIdx, "cPlugin", 0, &err) && lua_isnumber(S, ParamIdx + 1)) - { - // The next params are a cPlugin and a number, assume style 3 or 4 - LOGINFO("cPluginManager.AddHook(): Deprecated format used, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) instead. Fixing the call for you."); - S.LogStackTrace(); - return tolua_cPluginManager_AddHook_DefFn(PlgMgr, S, ParamIdx); - } - - AString ParamDesc; - Printf(ParamDesc, "%s, %s, %s", S.GetTypeText(1).c_str(), S.GetTypeText(2).c_str(), S.GetTypeText(3).c_str()); - LOGWARNING("cPluginManager.AddHook(): bad parameters. Expected HOOK_TYPE and CallbackFunction, got %s. Hook not added.", ParamDesc.c_str()); - S.LogStackTrace(); - return 0; -} - - - - - -static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if( NumArgs != 1) - { - LOGWARN("Error in function call 'ForEachCommand': Requires 1 argument, got %i", NumArgs); - return 0; - } - - cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); - if (self == NULL) - { - LOGWARN("Error in function call 'ForEachCommand': Not called on an object instance"); - return 0; - } - - if (!lua_isfunction(tolua_S, 2)) - { - LOGWARN("Error in function call 'ForEachCommand': Expected a function for parameter #1"); - return 0; - } - - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - LOGWARN("Error in function call 'ForEachCommand': Could not get function reference of parameter #1"); - return 0; - } - - class cLuaCallback : public cPluginManager::cCommandEnumCallback - { - public: - cLuaCallback(lua_State * a_LuaState, int a_FuncRef) - : LuaState( a_LuaState ) - , FuncRef( a_FuncRef ) - {} - - private: - virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushcppstring(LuaState, a_Command); - tolua_pushcppstring(LuaState, a_Permission); - tolua_pushcppstring(LuaState, a_HelpString); - - int s = lua_pcall(LuaState, 3, 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; /* Abort enumeration */ - } - - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean( LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - } Callback(tolua_S, FuncRef); - - bool bRetVal = self->ForEachCommand(Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); - return 1; -} - - - - - -static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if( NumArgs != 1) - { - LOGWARN("Error in function call 'ForEachConsoleCommand': Requires 1 argument, got %i", NumArgs); - return 0; - } - - cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); - if (self == NULL) - { - LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance"); - return 0; - } - - if (!lua_isfunction(tolua_S, 2)) - { - LOGWARN("Error in function call 'ForEachConsoleCommand': Expected a function for parameter #1"); - return 0; - } - - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - LOGWARN("Error in function call 'ForEachConsoleCommand': Could not get function reference of parameter #1"); - return 0; - } - - class cLuaCallback : public cPluginManager::cCommandEnumCallback - { - public: - cLuaCallback(lua_State * a_LuaState, int a_FuncRef) - : LuaState( a_LuaState ) - , FuncRef( a_FuncRef ) - {} - - private: - virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushcppstring(LuaState, a_Command); - tolua_pushcppstring(LuaState, a_HelpString); - - int s = lua_pcall(LuaState, 2, 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; /* Abort enumeration */ - } - - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean( LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - } Callback(tolua_S, FuncRef); - - bool bRetVal = self->ForEachConsoleCommand(Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); - return 1; -} - - - - - -static int tolua_cPluginManager_BindCommand(lua_State * L) -{ - /* Function signatures: - cPluginManager:BindCommand(Command, Permission, Function, HelpString) - cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param - */ - cPluginLua * Plugin = GetLuaPlugin(L); - if (Plugin == NULL) - { - return 0; - } - - // Read the arguments to this API call: - tolua_Error tolua_err; - int idx = 1; - if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err)) - { - idx++; - } - if ( - !tolua_iscppstring(L, idx, 0, &tolua_err) || - !tolua_iscppstring(L, idx + 1, 0, &tolua_err) || - !tolua_iscppstring(L, idx + 3, 0, &tolua_err) || - !tolua_isnoobj (L, idx + 4, &tolua_err) - ) - { - tolua_error(L, "#ferror in function 'BindCommand'.", &tolua_err); - return 0; - } - if (!lua_isfunction(L, idx + 2)) - { - luaL_error(L, "\"BindCommand\" function expects a function as its 3rd parameter. Command-binding aborted."); - return 0; - } - cPluginManager * self = cPluginManager::Get(); - AString Command (tolua_tocppstring(L, idx, "")); - AString Permission(tolua_tocppstring(L, idx + 1, "")); - AString HelpString(tolua_tocppstring(L, idx + 3, "")); - - // Store the function reference: - lua_pop(L, 1); // Pop the help string off the stack - int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference - if (FnRef == LUA_REFNIL) - { - LOGERROR("\"BindCommand\": Cannot create a function reference. Command \"%s\" not bound.", Command.c_str()); - return 0; - } - - if (!self->BindCommand(Command, Plugin, Permission, HelpString)) - { - // Refused. Possibly already bound. Error message has been given, display the callstack: - cLuaState LS(L); - LS.LogStackTrace(); - return 0; - } - - Plugin->BindCommand(Command, FnRef); - return 0; -} - - - - - -static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) -{ - /* Function signatures: - cPluginManager:BindConsoleCommand(Command, Function, HelpString) - cPluginManager.BindConsoleCommand(Command, Function, HelpString) -- without the "self" param - */ - - // Get the plugin identification out of LuaState: - cPluginLua * Plugin = GetLuaPlugin(L); - if (Plugin == NULL) - { - return 0; - } - - // Read the arguments to this API call: - tolua_Error tolua_err; - int idx = 1; - if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err)) - { - idx++; - } - if ( - !tolua_iscppstring(L, idx, 0, &tolua_err) || // Command - !tolua_iscppstring(L, idx + 2, 0, &tolua_err) || // HelpString - !tolua_isnoobj (L, idx + 3, &tolua_err) - ) - { - tolua_error(L, "#ferror in function 'BindConsoleCommand'.", &tolua_err); - return 0; - } - if (!lua_isfunction(L, idx + 1)) - { - luaL_error(L, "\"BindConsoleCommand\" function expects a function as its 2nd parameter. Command-binding aborted."); - return 0; - } - cPluginManager * self = cPluginManager::Get(); - AString Command (tolua_tocppstring(L, idx, "")); - AString HelpString(tolua_tocppstring(L, idx + 2, "")); - - // Store the function reference: - lua_pop(L, 1); // Pop the help string off the stack - int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference - if (FnRef == LUA_REFNIL) - { - LOGERROR("\"BindConsoleCommand\": Cannot create a function reference. Console Command \"%s\" not bound.", Command.c_str()); - return 0; - } - - if (!self->BindConsoleCommand(Command, Plugin, HelpString)) - { - // Refused. Possibly already bound. Error message has been given, display the callstack: - cLuaState LS(L); - LS.LogStackTrace(); - return 0; - } - - Plugin->BindConsoleCommand(Command, FnRef); - return 0; -} - - - - - -static int tolua_cPlayer_GetGroups(lua_State* tolua_S) -{ - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - - const cPlayer::GroupList & AllGroups = self->GetGroups(); - - lua_createtable(tolua_S, AllGroups.size(), 0); - int newTable = lua_gettop(tolua_S); - int index = 1; - cPlayer::GroupList::const_iterator iter = AllGroups.begin(); - while(iter != AllGroups.end()) - { - const cGroup* Group = *iter; - tolua_pushusertype( tolua_S, (void*)Group, "const cGroup" ); - lua_rawseti(tolua_S, newTable, index); - ++iter; - ++index; - } - return 1; -} - - - - - -static int tolua_cPlayer_GetResolvedPermissions(lua_State* tolua_S) -{ - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); - - cPlayer::StringList AllPermissions = self->GetResolvedPermissions(); - - lua_createtable(tolua_S, AllPermissions.size(), 0); - int newTable = lua_gettop(tolua_S); - int index = 1; - cPlayer::StringList::iterator iter = AllPermissions.begin(); - while(iter != AllPermissions.end()) - { - std::string& Permission = *iter; - tolua_pushstring( tolua_S, Permission.c_str() ); - lua_rawseti(tolua_S, newTable, index); - ++iter; - ++index; - } - return 1; -} - - - - - -static int tolua_cPlayer_OpenWindow(lua_State * tolua_S) -{ - // Function signature: cPlayer:OpenWindow(Window) - - // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = GetLuaPlugin(tolua_S); - if (Plugin == NULL) - { - return 0; - } - - // Get the parameters: - cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL); - cWindow * wnd = (cWindow *)tolua_tousertype(tolua_S, 2, NULL); - if ((self == NULL) || (wnd == NULL)) - { - LOGWARNING("%s: invalid self (%p) or wnd (%p)", __FUNCTION__, self, wnd); - return 0; - } - - // If cLuaWindow, add a reference, so that Lua won't delete the cLuaWindow object mid-processing - tolua_Error err; - if (tolua_isusertype(tolua_S, 2, "cLuaWindow", 0, &err)) - { - cLuaWindow * LuaWnd = (cLuaWindow *)wnd; - // Only if not already referenced - if (!LuaWnd->IsLuaReferenced()) - { - int LuaRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (LuaRef == LUA_REFNIL) - { - LOGWARNING("%s: Cannot create a window reference. Cannot open window \"%s\".", - __FUNCTION__, wnd->GetWindowTitle().c_str() - ); - return 0; - } - LuaWnd->SetLuaRef(Plugin, LuaRef); - } - } - - // Open the window - self->OpenWindow(wnd); - return 0; -} - - - - - -template < - class OBJTYPE, - void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef) -> -static int tolua_SetObjectCallback(lua_State * tolua_S) -{ - // Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction) - - // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = GetLuaPlugin(tolua_S); - if (Plugin == NULL) - { - // Warning message has already been printed by GetLuaPlugin(), bail out silently - return 0; - } - - // Get the parameters - self and the function reference: - OBJTYPE * self = (OBJTYPE *)tolua_tousertype(tolua_S, 1, NULL); - if (self == NULL) - { - LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self); - return 0; - } - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); // Store function reference for later retrieval - if (FnRef == LUA_REFNIL) - { - LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__); - return 0; - } - - // Set the callback - (self->*SetCallback)(Plugin, FnRef); - return 0; -} - - - - - -static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) -{ - cPluginLua * self = (cPluginLua *)tolua_tousertype(tolua_S,1,0); - - tolua_Error tolua_err; - tolua_err.array = 0; - tolua_err.index = 3; - tolua_err.type = "function"; - - std::string Title = ""; - int Reference = LUA_REFNIL; - - if ( - tolua_isstring(tolua_S, 2, 0, &tolua_err ) && - lua_isfunction(tolua_S, 3 ) - ) - { - Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - Title = ((std::string) tolua_tocppstring(tolua_S,2,0)); - } - else - { - return tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err); - } - - if( Reference != LUA_REFNIL ) - { - if( !self->AddWebTab( Title.c_str(), tolua_S, Reference ) ) - { - luaL_unref( tolua_S, LUA_REGISTRYINDEX, Reference ); - } - } - else - { - LOGERROR("ERROR: cPluginLua:AddWebTab invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str() ); - } - - return 0; -} - - - - - -static int tolua_cPluginLua_AddTab(lua_State* tolua_S) -{ - cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); - LOGWARN("WARNING: Using deprecated function AddTab()! Use AddWebTab() instead. (plugin \"%s\" in folder \"%s\")", - self->GetName().c_str(), self->GetDirectory().c_str() - ); - return tolua_cPluginLua_AddWebTab( tolua_S ); -} - - - - -// Perhaps use this as well for copying tables https://github.com/keplerproject/rings/pull/1 -static int copy_lua_values(lua_State * a_Source, lua_State * a_Destination, int i, int top) -{ - for(; i <= top; ++i ) - { - int t = lua_type(a_Source, i); - switch (t) { - case LUA_TSTRING: /* strings */ - { - const char * s = lua_tostring(a_Source, i); - LOGD("%i push string: %s", i, s); - tolua_pushstring(a_Destination, s); - } - break; - case LUA_TBOOLEAN: /* booleans */ - { - int b = tolua_toboolean(a_Source, i, false); - LOGD("%i push bool: %i", i, b); - tolua_pushboolean(a_Destination, b ); - } - break; - case LUA_TNUMBER: /* numbers */ - { - lua_Number d = tolua_tonumber(a_Source, i, 0); - LOGD("%i push number: %0.2f", i, d); - tolua_pushnumber(a_Destination, d ); - } - break; - case LUA_TUSERDATA: - { - const char * type = 0; - if (lua_getmetatable(a_Source,i)) - { - lua_rawget(a_Source, LUA_REGISTRYINDEX); - type = lua_tostring(a_Source, -1); - lua_pop(a_Source, 1); // Pop.. something?! I don't knooow~~ T_T - } - - // don't need tolua_tousertype we already have the type - void * ud = tolua_touserdata(a_Source, i, 0); - LOGD("%i push usertype: %p of type '%s'", i, ud, type); - if( type == 0 ) - { - LOGERROR("Call(): Something went wrong when trying to get usertype name!"); - return 0; - } - tolua_pushusertype(a_Destination, ud, type); - } - break; - default: /* other values */ - LOGERROR("Call(): Unsupported value: '%s'. Can only use numbers and strings!", lua_typename(a_Source, t)); - return 0; - } - } - return 1; -} - - - - - -static int tolua_cPlugin_Call(lua_State* tolua_S) -{ - cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); - lua_State* targetState = self->GetLuaState(); - int targetTop = lua_gettop(targetState); - - int top = lua_gettop(tolua_S); - LOGD("total in stack: %i", top ); - - std::string funcName = tolua_tostring(tolua_S, 2, ""); - LOGD("Func name: %s", funcName.c_str() ); - - lua_getglobal(targetState, funcName.c_str()); - if(!lua_isfunction(targetState,-1)) - { - LOGWARN("Error could not find function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() ); - lua_pop(targetState,1); - return 0; - } - - if( copy_lua_values(tolua_S, targetState, 3, top) == 0 ) // Start at 3 because 1 and 2 are the plugin and function name respectively - { - // something went wrong, exit - return 0; - } - - int s = lua_pcall(targetState, top - 2, LUA_MULTRET, 0); - if (cLuaState::ReportErrors(targetState, s)) - { - LOGWARN("Error while calling function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() ); - return 0; - } - - int nresults = lua_gettop(targetState) - targetTop; - LOGD("num results: %i", nresults); - int ttop = lua_gettop(targetState); - if( copy_lua_values(targetState, tolua_S, targetTop+1, ttop) == 0 ) // Start at targetTop+1 and I have no idea why xD - { - // something went wrong, exit - return 0; - } - - lua_pop(targetState, nresults); // I have no idea what I'm doing, but it works - - return nresults; -} - - - - - -static int tolua_md5(lua_State* tolua_S) -{ - std::string SourceString = tolua_tostring(tolua_S, 1, 0); - std::string CryptedString = md5( SourceString ); - tolua_pushstring( tolua_S, CryptedString.c_str() ); - return 1; -} - - - - - -static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string, std::string >& a_StringStringMap ) -{ - lua_newtable(tolua_S); - int top = lua_gettop(tolua_S); - - for( std::map< std::string, std::string >::iterator it = a_StringStringMap.begin(); it != a_StringStringMap.end(); ++it ) - { - const char* key = it->first.c_str(); - const char* value = it->second.c_str(); - lua_pushstring(tolua_S, key); - lua_pushstring(tolua_S, value); - lua_settable(tolua_S, top); - } - - return 1; -} - - - - - -static int tolua_get_HTTPRequest_Params(lua_State* tolua_S) -{ - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); - return tolua_push_StringStringMap(tolua_S, self->Params); -} - - - - - -static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S) -{ - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); - return tolua_push_StringStringMap(tolua_S, self->PostParams); -} - - - - - -static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S) -{ - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); - std::map< std::string, HTTPFormData >& FormData = self->FormData; - - lua_newtable(tolua_S); - int top = lua_gettop(tolua_S); - - for( std::map< std::string, HTTPFormData >::iterator it = FormData.begin(); it != FormData.end(); ++it ) - { - lua_pushstring(tolua_S, it->first.c_str() ); - tolua_pushusertype(tolua_S, &(it->second), "HTTPFormData" ); - //lua_pushlstring(tolua_S, it->second.Value.c_str(), it->second.Value.size() ); // Might contain binary data - lua_settable(tolua_S, top); - } - - return 1; -} - - - - - -static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) -{ - cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); - - const cWebAdmin::PluginList & AllPlugins = self->GetPlugins(); - - lua_createtable(tolua_S, AllPlugins.size(), 0); - int newTable = lua_gettop(tolua_S); - int index = 1; - cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin(); - while(iter != AllPlugins.end()) - { - const cWebPlugin* Plugin = *iter; - tolua_pushusertype( tolua_S, (void*)Plugin, "const cWebPlugin" ); - lua_rawseti(tolua_S, newTable, index); - ++iter; - ++index; - } - return 1; -} - - - - - -static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) -{ - cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0); - - const cWebPlugin::TabNameList & TabNames = self->GetTabNames(); - - lua_newtable(tolua_S); - int newTable = lua_gettop(tolua_S); - int index = 1; - cWebPlugin::TabNameList::const_iterator iter = TabNames.begin(); - while(iter != TabNames.end()) - { - const AString & FancyName = iter->first; - const AString & WebName = iter->second; - tolua_pushstring( tolua_S, WebName.c_str() ); // Because the WebName is supposed to be unique, use it as key - tolua_pushstring( tolua_S, FancyName.c_str() ); - // - lua_rawset(tolua_S, -3); - ++iter; - ++index; - } - return 1; -} - - - - - -static int Lua_ItemGrid_GetSlotCoords(lua_State * L) -{ - tolua_Error tolua_err; - if ( - !tolua_isusertype(L, 1, "const cItemGrid", 0, &tolua_err) || - !tolua_isnumber (L, 2, 0, &tolua_err) || - !tolua_isnoobj (L, 3, &tolua_err) - ) - { - goto tolua_lerror; - } - - { - const cItemGrid * self = (const cItemGrid *)tolua_tousertype(L, 1, 0); - int SlotNum = (int)tolua_tonumber(L, 2, 0); - if (self == NULL) - { - tolua_error(L, "invalid 'self' in function 'cItemGrid:GetSlotCoords'", NULL); - return 0; - } - int X, Y; - self->GetSlotCoords(SlotNum, X, Y); - tolua_pushnumber(L, (lua_Number)X); - tolua_pushnumber(L, (lua_Number)Y); - return 2; - } - -tolua_lerror: - tolua_error(L, "#ferror in function 'cItemGrid:GetSlotCoords'.", &tolua_err); - return 0; -} - - - - - -/// Provides interface between a Lua table of callbacks and the cBlockTracer::cCallbacks -class cLuaBlockTracerCallbacks : - public cBlockTracer::cCallbacks -{ -public: - cLuaBlockTracerCallbacks(cLuaState & a_LuaState, int a_ParamNum) : - m_LuaState(a_LuaState), - m_TableRef(a_LuaState, a_ParamNum) - { - } - - virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override - { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlock")) - { - // No such function in the table, skip the callback - return false; - } - m_LuaState.Push(a_BlockX); - m_LuaState.Push(a_BlockY); - m_LuaState.Push(a_BlockZ); - m_LuaState.Push(a_BlockType); - m_LuaState.Push(a_BlockMeta); - m_LuaState.Push(a_EntryFace); - if (!m_LuaState.CallFunction(1)) - { - return false; - } - bool res = false; - if (lua_isboolean(m_LuaState, -1)) - { - res = (lua_toboolean(m_LuaState, -1) != 0); - } - lua_pop(m_LuaState, 1); - return res; - } - - virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) override - { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlockNoData")) - { - // No such function in the table, skip the callback - return false; - } - m_LuaState.Push(a_BlockX); - m_LuaState.Push(a_BlockY); - m_LuaState.Push(a_BlockZ); - m_LuaState.Push(a_EntryFace); - if (!m_LuaState.CallFunction(1)) - { - return false; - } - bool res = false; - if (lua_isboolean(m_LuaState, -1)) - { - res = (lua_toboolean(m_LuaState, -1) != 0); - } - lua_pop(m_LuaState, 1); - return res; - } - - virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override - { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnOutOfWorld")) - { - // No such function in the table, skip the callback - return false; - } - m_LuaState.Push(a_BlockX); - m_LuaState.Push(a_BlockY); - m_LuaState.Push(a_BlockZ); - if (!m_LuaState.CallFunction(1)) - { - return false; - } - bool res = false; - if (lua_isboolean(m_LuaState, -1)) - { - res = (lua_toboolean(m_LuaState, -1) != 0); - } - lua_pop(m_LuaState, 1); - return res; - } - - virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override - { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnIntoWorld")) - { - // No such function in the table, skip the callback - return false; - } - m_LuaState.Push(a_BlockX); - m_LuaState.Push(a_BlockY); - m_LuaState.Push(a_BlockZ); - if (!m_LuaState.CallFunction(1)) - { - return false; - } - bool res = false; - if (lua_isboolean(m_LuaState, -1)) - { - res = (lua_toboolean(m_LuaState, -1) != 0); - } - lua_pop(m_LuaState, 1); - return res; - } - - virtual void OnNoMoreHits(void) override - { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoMoreHits")) - { - // No such function in the table, skip the callback - return; - } - m_LuaState.CallFunction(0); - } - - virtual void OnNoChunk(void) override - { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoChunk")) - { - // No such function in the table, skip the callback - return; - } - m_LuaState.CallFunction(0); - } - -protected: - cLuaState & m_LuaState; - cLuaState::cRef m_TableRef; -} ; - - - - - -static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) -{ - // cLineBlockTracer.Trace(World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ) - cLuaState L(tolua_S); - if ( - !L.CheckParamUserType(1, "cWorld") || - !L.CheckParamTable (2) || - !L.CheckParamNumber (3, 8) || - !L.CheckParamEnd (9) - ) - { - return 0; - } - - cWorld * World = (cWorld *)tolua_tousertype(L, 1, NULL); - cLuaBlockTracerCallbacks Callbacks(L, 2); - double StartX = tolua_tonumber(L, 3, 0); - double StartY = tolua_tonumber(L, 4, 0); - double StartZ = tolua_tonumber(L, 5, 0); - double EndX = tolua_tonumber(L, 6, 0); - double EndY = tolua_tonumber(L, 7, 0); - double EndZ = tolua_tonumber(L, 8, 0); - bool res = cLineBlockTracer::Trace(*World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ); - tolua_pushboolean(L, res ? 1 : 0); - return 1; -} - - - - - -static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S) -{ - // function cHopperEntity::GetOutputBlockPos() - // Exported manually because tolua would require meaningless params - - cLuaState L(tolua_S); - if ( - !L.CheckParamUserType(1, "cHopperEntity") || - !L.CheckParamNumber (2) || - !L.CheckParamEnd (3) - ) - { - return 0; - } - cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, 0); - if (self == NULL) - { - tolua_error(tolua_S, "invalid 'self' in function 'cHopperEntity::GetOutputBlockPos()'", NULL); - return 0; - } - - NIBBLETYPE a_BlockMeta = ((NIBBLETYPE)tolua_tonumber(tolua_S, 2, 0)); - int a_OutputX, a_OutputY, a_OutputZ; - bool res = self->GetOutputBlockPos(a_BlockMeta, a_OutputX, a_OutputY, a_OutputZ); - tolua_pushboolean(tolua_S, res); - if (res) - { - tolua_pushnumber(tolua_S, (lua_Number)a_OutputX); - tolua_pushnumber(tolua_S, (lua_Number)a_OutputY); - tolua_pushnumber(tolua_S, (lua_Number)a_OutputZ); - return 4; - } - return 1; -} - - - - - -void ManualBindings::Bind(lua_State * tolua_S) -{ - tolua_beginmodule(tolua_S, NULL); - tolua_function(tolua_S, "StringSplit", tolua_StringSplit); - tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); - tolua_function(tolua_S, "LOG", tolua_LOG); - tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO); - tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); - tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); - tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); - - tolua_beginmodule(tolua_S, "cHopperEntity"); - tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cLineBlockTracer"); - tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cRoot"); - tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith ); - tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach); - tolua_function(tolua_S, "ForEachWorld", tolua_ForEach); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cWorld"); - tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ); - tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ); - tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ); - tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ); - tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>); - tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ); - tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>); - tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>); - tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk); - tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>); - tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk); - tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk); - tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>); - tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo); - tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); - tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); - tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); - tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); - tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); - tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cPlugin"); - tolua_function(tolua_S, "Call", tolua_cPlugin_Call); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cPluginManager"); - tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook); - tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand); - tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand); - tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand); - tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand); - tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cPlayer"); - tolua_function(tolua_S, "GetGroups", tolua_cPlayer_GetGroups); - tolua_function(tolua_S, "GetResolvedPermissions", tolua_cPlayer_GetResolvedPermissions); - tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cLuaWindow"); - tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback); - tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cPluginLua"); - tolua_function(tolua_S, "AddTab", tolua_cPluginLua_AddTab); - tolua_function(tolua_S, "AddWebTab", tolua_cPluginLua_AddWebTab); - tolua_endmodule(tolua_S); - - tolua_cclass(tolua_S,"HTTPRequest","HTTPRequest","",NULL); - tolua_beginmodule(tolua_S,"HTTPRequest"); - // tolua_variable(tolua_S,"Method",tolua_get_HTTPRequest_Method,tolua_set_HTTPRequest_Method); - // tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path); - tolua_variable(tolua_S,"FormData",tolua_get_HTTPRequest_FormData,0); - tolua_variable(tolua_S,"Params",tolua_get_HTTPRequest_Params,0); - tolua_variable(tolua_S,"PostParams",tolua_get_HTTPRequest_PostParams,0); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cWebAdmin"); - tolua_function(tolua_S, "GetPlugins", tolua_cWebAdmin_GetPlugins); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cWebPlugin"); - tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cClientHandle"); - tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE); - tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cItemGrid"); - tolua_function(tolua_S, "GetSlotCoords", Lua_ItemGrid_GetSlotCoords); - tolua_endmodule(tolua_S); - - tolua_function(tolua_S, "md5", tolua_md5); - - tolua_endmodule(tolua_S); -} - - - - diff --git a/source/ManualBindings.h b/source/ManualBindings.h deleted file mode 100644 index e6594947e..000000000 --- a/source/ManualBindings.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -struct lua_State; -class ManualBindings -{ -public: - static void Bind( lua_State* tolua_S ); -}; \ No newline at end of file diff --git a/source/Matrix4f.cpp b/source/Matrix4f.cpp deleted file mode 100644 index d0a407a99..000000000 --- a/source/Matrix4f.cpp +++ /dev/null @@ -1,4 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -// _X: empty file?? diff --git a/source/Matrix4f.h b/source/Matrix4f.h deleted file mode 100644 index 249c92f5f..000000000 --- a/source/Matrix4f.h +++ /dev/null @@ -1,225 +0,0 @@ -#pragma once - -#define _USE_MATH_DEFINES -#include -#include "Vector3f.h" - -class Matrix4f -{ -public: - enum - { - TX=3, - TY=7, - TZ=11, - D0=0, D1=5, D2=10, D3=15, - SX=D0, SY=D1, SZ=D2, - W=D3 - }; - Matrix4f() { Identity(); } - float& operator [] ( int a_N ) { return cell[a_N]; } - void Identity() - { - cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] = - cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0; - cell[D0] = cell[D1] = cell[D2] = cell[W] = 1; - } - void Init( Vector3f a_Pos, float a_RX, float a_RY, float a_RZ ) - { - Matrix4f t; - t.RotateX( a_RZ ); - RotateY( a_RY ); - Concatenate( t ); - t.RotateZ( a_RX ); - Concatenate( t ); - Translate( a_Pos ); - } - void RotateX( float a_RX ) - { - float sx = (float)sin( a_RX * M_PI / 180 ); - float cx = (float)cos( a_RX * M_PI / 180 ); - Identity(); - cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx; - } - void RotateY( float a_RY ) - { - float sy = (float)sin( a_RY * M_PI / 180 ); - float cy = (float)cos( a_RY * M_PI / 180 ); - Identity (); - cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy; - } - void RotateZ( float a_RZ ) - { - float sz = (float)sin( a_RZ * M_PI / 180 ); - float cz = (float)cos( a_RZ * M_PI / 180 ); - Identity (); - cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz; - } - void Translate( Vector3f a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; } - void SetTranslation( Vector3f a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; } - void Concatenate( const Matrix4f& m2 ) - { - Matrix4f res; - int c; - for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ ) - res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] + - cell[r * 4 + 1] * m2.cell[c + 4] + - cell[r * 4 + 2] * m2.cell[c + 8] + - cell[r * 4 + 3] * m2.cell[c + 12]; - for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c]; - } - Vector3f Transform( const Vector3f& v ) const - { - float x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3]; - float y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7]; - float z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11]; - return Vector3f( x, y, z ); - } - void Invert() - { - Matrix4f t; - int h, i; - float tx = -cell[3], ty = -cell[7], tz = -cell[11]; - for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4]; - for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i]; - cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2]; - cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6]; - cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10]; - } - Vector3f GetXColumn() { return Vector3f( cell[0], cell[1], cell[2] ); } - Vector3f GetYColumn() { return Vector3f( cell[4], cell[5], cell[6] ); } - Vector3f GetZColumn() { return Vector3f( cell[8], cell[9], cell[10] ); } - void SetXColumn( const Vector3f & a_X ) - { - cell[0] = a_X.x; - cell[1] = a_X.y; - cell[2] = a_X.z; - } - void SetYColumn( const Vector3f & a_Y ) - { - cell[4] = a_Y.x; - cell[5] = a_Y.y; - cell[6] = a_Y.z; - } - void SetZColumn( const Vector3f & a_Z ) - { - cell[8] = a_Z.x; - cell[9] = a_Z.y; - cell[10] = a_Z.z; - } - float cell[16]; -}; - - - - - -class Matrix4d -{ -public: - enum - { - TX=3, - TY=7, - TZ=11, - D0=0, D1=5, D2=10, D3=15, - SX=D0, SY=D1, SZ=D2, - W=D3 - }; - Matrix4d() { Identity(); } - double& operator [] ( int a_N ) { return cell[a_N]; } - void Identity() - { - cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] = - cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0; - cell[D0] = cell[D1] = cell[D2] = cell[W] = 1; - } - void Init( Vector3f a_Pos, double a_RX, double a_RY, double a_RZ ) - { - Matrix4d t; - t.RotateX( a_RZ ); - RotateY( a_RY ); - Concatenate( t ); - t.RotateZ( a_RX ); - Concatenate( t ); - Translate( a_Pos ); - } - void RotateX( double a_RX ) - { - double sx = (double)sin( a_RX * M_PI / 180 ); - double cx = (double)cos( a_RX * M_PI / 180 ); - Identity(); - cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx; - } - void RotateY( double a_RY ) - { - double sy = (double)sin( a_RY * M_PI / 180 ); - double cy = (double)cos( a_RY * M_PI / 180 ); - Identity (); - cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy; - } - void RotateZ( double a_RZ ) - { - double sz = (double)sin( a_RZ * M_PI / 180 ); - double cz = (double)cos( a_RZ * M_PI / 180 ); - Identity (); - cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz; - } - void Translate( Vector3d a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; } - void SetTranslation( Vector3d a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; } - void Concatenate( const Matrix4d & m2 ) - { - Matrix4d res; - int c; - for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ ) - res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] + - cell[r * 4 + 1] * m2.cell[c + 4] + - cell[r * 4 + 2] * m2.cell[c + 8] + - cell[r * 4 + 3] * m2.cell[c + 12]; - for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c]; - } - Vector3d Transform( const Vector3d & v ) const - { - double x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3]; - double y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7]; - double z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11]; - return Vector3d( x, y, z ); - } - void Invert() - { - Matrix4d t; - int h, i; - double tx = -cell[3], ty = -cell[7], tz = -cell[11]; - for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4]; - for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i]; - cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2]; - cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6]; - cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10]; - } - Vector3d GetXColumn() { return Vector3d( cell[0], cell[1], cell[2] ); } - Vector3d GetYColumn() { return Vector3d( cell[4], cell[5], cell[6] ); } - Vector3d GetZColumn() { return Vector3d( cell[8], cell[9], cell[10] ); } - void SetXColumn( const Vector3d & a_X ) - { - cell[0] = a_X.x; - cell[1] = a_X.y; - cell[2] = a_X.z; - } - void SetYColumn( const Vector3d & a_Y ) - { - cell[4] = a_Y.x; - cell[5] = a_Y.y; - cell[6] = a_Y.z; - } - void SetZColumn( const Vector3d & a_Z ) - { - cell[8] = a_Z.x; - cell[9] = a_Z.y; - cell[10] = a_Z.z; - } - double cell[16]; -} ; - - - - diff --git a/source/MemoryLeak.h b/source/MemoryLeak.h deleted file mode 100644 index e9c0c34e3..000000000 --- a/source/MemoryLeak.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#ifdef _WIN32 - #ifdef _DEBUG - // Enable the CRT debugging features: - #define _CRTDBG_MAP_ALLOC - #include - #include - - // This works only in MSVC 2010+: - #if _MSC_VER >= 1600 - // Map the new operator - #ifndef DEBUG_NEW - #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) - #define new DEBUG_NEW - #endif // _CRTDBG_MAP_ALLOC - #endif // _MSC_VER - #endif // _DEBUG -#endif // _WIN32 diff --git a/source/MersenneTwister.h b/source/MersenneTwister.h deleted file mode 100644 index dc7134a93..000000000 --- a/source/MersenneTwister.h +++ /dev/null @@ -1,456 +0,0 @@ -// MersenneTwister.h -// Mersenne Twister random number generator -- a C++ class MTRand -// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus -// Richard J. Wagner v1.1 28 September 2009 wagnerr@umich.edu - -// The Mersenne Twister is an algorithm for generating random numbers. It -// was designed with consideration of the flaws in various other generators. -// The period, 2^19937-1, and the order of equidistribution, 623 dimensions, -// are far greater. The generator is also fast; it avoids multiplication and -// division, and it benefits from caches and pipelines. For more information -// see the inventors' web page at -// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html - -// Reference -// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally -// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on -// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. - -// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, -// Copyright (C) 2000 - 2009, Richard J. Wagner -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The names of its contributors may not be used to endorse or promote -// products derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -#ifndef MERSENNETWISTER_H -#define MERSENNETWISTER_H - -// Not thread safe (unless auto-initialization is avoided and each thread has -// its own MTRand object) - -#include -#include -#include -#include -#include - -class MTRand { -// Data -public: - typedef long uint32; // unsigned integer type, at least 32 bits - - enum { N = 624 }; // length of state vector - enum { SAVE = N + 1 }; // length of array for save() - -protected: - enum { M = 397 }; // period parameter - - uint32 state[N]; // internal state - uint32 *pNext; // next value to get from state - int left; // number of values left before reload needed - -// Methods -public: - MTRand( const uint32 oneSeed ); // initialize with a simple uint32 - MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or array - MTRand(); // auto-initialize with /dev/urandom or time() and clock() - MTRand( const MTRand& o ); // copy - - // Do NOT use for CRYPTOGRAPHY without securely hashing several returned - // values together, otherwise the generator state can be learned after - // reading 624 consecutive values. - - // Access to 32-bit random numbers - uint32 randInt(); // integer in [0,2^32-1] - uint32 randInt( const uint32 n ); // integer in [0,n] for n < 2^32 - double rand(); // real number in [0,1] - double rand( const double n ); // real number in [0,n] - double randExc(); // real number in [0,1) - double randExc( const double n ); // real number in [0,n) - double randDblExc(); // real number in (0,1) - double randDblExc( const double n ); // real number in (0,n) - double operator()(); // same as rand() - - // Access to 53-bit random numbers (capacity of IEEE double precision) - double rand53(); // real number in [0,1) - - // Access to nonuniform random number distributions - double randNorm( const double mean = 0.0, const double stddev = 1.0 ); - - // Re-seeding functions with same behavior as initializers - void seed( const uint32 oneSeed ); - void seed( uint32 *const bigSeed, const uint32 seedLength = N ); - void seed(); - - // Saving and loading generator state - void save( uint32* saveArray ) const; // to array of size SAVE - void load( uint32 *const loadArray ); // from such array - friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); - friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); - MTRand& operator=( const MTRand& o ); - -protected: - void initialize( const uint32 oneSeed ); - void reload(); - uint32 hiBit( const uint32 u ) const { return u & 0x80000000UL; } - uint32 loBit( const uint32 u ) const { return u & 0x00000001UL; } - uint32 loBits( const uint32 u ) const { return u & 0x7fffffffUL; } - uint32 mixBits( const uint32 u, const uint32 v ) const - { return hiBit(u) | loBits(v); } - uint32 magic( const uint32 u ) const - { return loBit(u) ? 0x9908b0dfUL : 0x0UL; } - uint32 twist( const uint32 m, const uint32 s0, const uint32 s1 ) const - { return m ^ (mixBits(s0,s1)>>1) ^ magic(s1); } - static uint32 hash( time_t t, clock_t c ); -}; - -// Functions are defined in order of usage to assist inlining - -inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) -{ - // Get a uint32 from t and c - // Better than uint32(x) in case x is floating point in [0,1] - // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) - - static uint32 differ = 0; // guarantee time-based seeds will change - - uint32 h1 = 0; - unsigned char *p = (unsigned char *) &t; - for( size_t i = 0; i < sizeof(t); ++i ) - { - h1 *= UCHAR_MAX + 2U; - h1 += p[i]; - } - uint32 h2 = 0; - p = (unsigned char *) &c; - for( size_t j = 0; j < sizeof(c); ++j ) - { - h2 *= UCHAR_MAX + 2U; - h2 += p[j]; - } - return ( h1 + differ++ ) ^ h2; -} - -inline void MTRand::initialize( const uint32 seed ) -{ - // Initialize generator state with seed - // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. - // In previous versions, most significant bits (MSBs) of the seed affect - // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. - register uint32 *s = state; - register uint32 *r = state; - register int i = 1; - *s++ = seed & 0xffffffffUL; - for( ; i < N; ++i ) - { - *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; - r++; - } -} - -inline void MTRand::reload() -{ - // Generate N new values in state - // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) - static const int MmN = int(M) - int(N); // in case enums are unsigned - register uint32 *p = state; - register int i; - for( i = N - M; i--; ++p ) - *p = twist( p[M], p[0], p[1] ); - for( i = M; --i; ++p ) - *p = twist( p[MmN], p[0], p[1] ); - *p = twist( p[MmN], p[0], state[0] ); - - left = N, pNext = state; -} - -inline void MTRand::seed( const uint32 oneSeed ) -{ - // Seed the generator with a simple uint32 - initialize(oneSeed); - reload(); -} - -inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) -{ - // Seed the generator with an array of uint32's - // There are 2^19937-1 possible initial states. This function allows - // all of those to be accessed by providing at least 19937 bits (with a - // default seed length of N = 624 uint32's). Any bits above the lower 32 - // in each element are discarded. - // Just call seed() if you want to get array from /dev/urandom - initialize(19650218UL); - register int i = 1; - register uint32 j = 0; - register int k = ( N > seedLength ? N : seedLength ); - for( ; k; --k ) - { - state[i] = - state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); - state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; - state[i] &= 0xffffffffUL; - ++i; ++j; - if( i >= N ) { state[0] = state[N-1]; i = 1; } - if( j >= seedLength ) j = 0; - } - for( k = N - 1; k; --k ) - { - state[i] = - state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); - state[i] -= i; - state[i] &= 0xffffffffUL; - ++i; - if( i >= N ) { state[0] = state[N-1]; i = 1; } - } - state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array - reload(); -} - -inline void MTRand::seed() -{ - // Seed the generator with an array from /dev/urandom if available - // Otherwise use a hash of time() and clock() values - - // First try getting an array from /dev/urandom - - /* // Commented out by FakeTruth because doing this 200 times a tick is SUUUUPEERRR SLOW!!~~!ÕNe - FILE* urandom = fopen( "/dev/urandom", "rb" ); - if( urandom ) - { - uint32 bigSeed[N]; - register uint32 *s = bigSeed; - register int i = N; - register bool success = true; - while( success && i-- ) - success = fread( s++, sizeof(uint32), 1, urandom ); - fclose(urandom); - if( success ) { seed( bigSeed, N ); return; } - } - */ - - // Was not successful, so use time() and clock() instead - seed( hash( time(NULL), clock() ) ); -} - -inline MTRand::MTRand( const uint32 oneSeed ) - { seed(oneSeed); } - -inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) - { seed(bigSeed,seedLength); } - -inline MTRand::MTRand() - { seed(); } - -inline MTRand::MTRand( const MTRand& o ) -{ - register const uint32 *t = o.state; - register uint32 *s = state; - register int i = N; - for( ; i--; *s++ = *t++ ) {} - left = o.left; - pNext = &state[N-left]; -} - -inline MTRand::uint32 MTRand::randInt() -{ - // Pull a 32-bit integer from the generator state - // Every other access function simply transforms the numbers extracted here - - if( left == 0 ) reload(); - --left; - - register uint32 s1; - s1 = *pNext++; - s1 ^= (s1 >> 11); - s1 ^= (s1 << 7) & 0x9d2c5680UL; - s1 ^= (s1 << 15) & 0xefc60000UL; - return ( s1 ^ (s1 >> 18) ); -} - -inline MTRand::uint32 MTRand::randInt( const uint32 n ) -{ - // Find which bits are used in n - // Optimized by Magnus Jonsson (magnus@smartelectronix.com) - uint32 used = n; - used |= used >> 1; - used |= used >> 2; - used |= used >> 4; - used |= used >> 8; - used |= used >> 16; - - // Draw numbers until one is found in [0,n] - uint32 i; - do - i = randInt() & used; // toss unused bits to shorten search - while( i > n ); - return i; -} - -inline double MTRand::rand() - { return double(randInt()) * (1.0/4294967295.0); } - -inline double MTRand::rand( const double n ) - { return rand() * n; } - -inline double MTRand::randExc() - { return double(randInt()) * (1.0/4294967296.0); } - -inline double MTRand::randExc( const double n ) - { return randExc() * n; } - -inline double MTRand::randDblExc() - { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } - -inline double MTRand::randDblExc( const double n ) - { return randDblExc() * n; } - -inline double MTRand::rand53() -{ - uint32 a = randInt() >> 5, b = randInt() >> 6; - return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada -} - -inline double MTRand::randNorm( const double mean, const double stddev ) -{ - // Return a real number from a normal (Gaussian) distribution with given - // mean and standard deviation by polar form of Box-Muller transformation - double x, y, r; - do - { - x = 2.0 * rand() - 1.0; - y = 2.0 * rand() - 1.0; - r = x * x + y * y; - } - while ( r >= 1.0 || r == 0.0 ); - double s = sqrt( -2.0 * log(r) / r ); - return mean + x * s * stddev; -} - -inline double MTRand::operator()() -{ - return rand(); -} - -inline void MTRand::save( uint32* saveArray ) const -{ - register const uint32 *s = state; - register uint32 *sa = saveArray; - register int i = N; - for( ; i--; *sa++ = *s++ ) {} - *sa = left; -} - -inline void MTRand::load( uint32 *const loadArray ) -{ - register uint32 *s = state; - register uint32 *la = loadArray; - register int i = N; - for( ; i--; *s++ = *la++ ) {} - left = *la; - pNext = &state[N-left]; -} - -inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) -{ - register const MTRand::uint32 *s = mtrand.state; - register int i = mtrand.N; - for( ; i--; os << *s++ << "\t" ) {} - return os << mtrand.left; -} - -inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) -{ - register MTRand::uint32 *s = mtrand.state; - register int i = mtrand.N; - for( ; i--; is >> *s++ ) {} - is >> mtrand.left; - mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; - return is; -} - -inline MTRand& MTRand::operator=( const MTRand& o ) -{ - if( this == &o ) return (*this); - register const uint32 *t = o.state; - register uint32 *s = state; - register int i = N; - for( ; i--; *s++ = *t++ ) {} - left = o.left; - pNext = &state[N-left]; - return (*this); -} - -#endif // MERSENNETWISTER_H - -// Change log: -// -// v0.1 - First release on 15 May 2000 -// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus -// - Translated from C to C++ -// - Made completely ANSI compliant -// - Designed convenient interface for initialization, seeding, and -// obtaining numbers in default or user-defined ranges -// - Added automatic seeding from /dev/urandom or time() and clock() -// - Provided functions for saving and loading generator state -// -// v0.2 - Fixed bug which reloaded generator one step too late -// -// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew -// -// v0.4 - Removed trailing newline in saved generator format to be consistent -// with output format of built-in types -// -// v0.5 - Improved portability by replacing static const int's with enum's and -// clarifying return values in seed(); suggested by Eric Heimburg -// - Removed MAXINT constant; use 0xffffffffUL instead -// -// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits -// - Changed integer [0,n] generator to give better uniformity -// -// v0.7 - Fixed operator precedence ambiguity in reload() -// - Added access for real numbers in (0,1) and (0,n) -// -// v0.8 - Included time.h header to properly support time_t and clock_t -// -// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto -// - Allowed for seeding with arrays of any length -// - Added access for real numbers in [0,1) with 53-bit resolution -// - Added access for real numbers from normal (Gaussian) distributions -// - Increased overall speed by optimizing twist() -// - Doubled speed of integer [0,n] generation -// - Fixed out-of-range number generation on 64-bit machines -// - Improved portability by substituting literal constants for long enum's -// - Changed license from GNU LGPL to BSD -// -// v1.1 - Corrected parameter label in randNorm from "variance" to "stddev" -// - Changed randNorm algorithm from basic to polar form for efficiency -// - Updated includes from deprecated to standard forms -// - Cleaned declarations and definitions to please Intel compiler -// - Revised twist() operator to work on ones'-complement machines -// - Fixed reload() function to work when N and M are unsigned -// - Added copy constructor and copy operator from Salvador Espana diff --git a/source/MobCensus.cpp b/source/MobCensus.cpp deleted file mode 100644 index 66b5932bc..000000000 --- a/source/MobCensus.cpp +++ /dev/null @@ -1,92 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "MobCensus.h" - - - - - -void cMobCensus::CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Distance) -{ - m_ProximityCounter.CollectMob(a_Monster, a_Chunk, a_Distance); - m_MobFamilyCollecter.CollectMob(a_Monster); -} - - - - - -bool cMobCensus::IsCapped(cMonster::eFamily a_MobFamily) -{ - bool toReturn = true; - const int ratio = 319; // this should be 256 as we are only supposed to take account from chunks that are in 17x17 from a player - // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19*19) / (17*17) = 319 - // MG TODO : code the correct count - if ((GetCapMultiplier(a_MobFamily) * GetNumChunks()) / ratio >= m_MobFamilyCollecter.GetNumberOfCollectedMobs(a_MobFamily)) - { - return false; - } - return true; -} - - - - - -int cMobCensus::GetCapMultiplier(cMonster::eFamily a_MobFamily) -{ - switch (a_MobFamily) - { - case cMonster::mfHostile: return 79; - case cMonster::mfPassive: return 11; - case cMonster::mfAmbient: return 16; - case cMonster::mfWater: return 5; - } - ASSERT(!"Unhandled mob family"); - return -1; -} - - - - - -void cMobCensus::CollectSpawnableChunk(cChunk & a_Chunk) -{ - m_EligibleForSpawnChunks.insert(&a_Chunk); -} - - - - - -int cMobCensus::GetNumChunks(void) -{ - return m_EligibleForSpawnChunks.size(); -} - - - - - -cMobProximityCounter & cMobCensus::GetProximityCounter(void) -{ - return m_ProximityCounter; -} - - - - - -void cMobCensus::Logd() -{ - LOGD("Hostile mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfHostile), IsCapped(cMonster::mfHostile) ? "(capped)" : ""); - LOGD("Ambient mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfAmbient), IsCapped(cMonster::mfAmbient) ? "(capped)" : ""); - LOGD("Water mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfWater), IsCapped(cMonster::mfWater) ? "(capped)" : ""); - LOGD("Passive mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfPassive), IsCapped(cMonster::mfPassive) ? "(capped)" : ""); -} - - - - - diff --git a/source/MobCensus.h b/source/MobCensus.h deleted file mode 100644 index e3892bec6..000000000 --- a/source/MobCensus.h +++ /dev/null @@ -1,59 +0,0 @@ - -#pragma once - -#include "MobProximityCounter.h" -#include "MobFamilyCollecter.h" - - - - -// fwd: -class cChunk; -class cMonster; - - - - - -/** This class is used to collect information, for each Mob, what is the distance of the closest player -it was first being designed in order to make mobs spawn / despawn / act -as the behaviour and even life of mobs depends on the distance to closest player - -as side effect : it also collect the chunks that are elligible for spawning -as side effect 2 : it also know the caps for mobs number and can compare census to this numbers -*/ -class cMobCensus -{ -public: - /// Returns the nested proximity counter - cMobProximityCounter & GetProximityCounter(void); - - // collect an elligible Chunk for Mob Spawning - // MG TODO : code the correct rule (not loaded chunk but short distant from players) - void CollectSpawnableChunk(cChunk & a_Chunk); - - /// Collect a mob - it's distance to player, it's family ... - void CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance); - - /// Returns true if the family is capped (i.e. there are more mobs of this family than max) - bool IsCapped(cMonster::eFamily a_MobFamily); - - /// log the results of census to server console - void Logd(void); - -protected : - cMobProximityCounter m_ProximityCounter; - cMobFamilyCollecter m_MobFamilyCollecter; - - std::set m_EligibleForSpawnChunks; - - /// Returns the number of chunks that are elligible for spawning (for now, the loaded, valid chunks) - int GetNumChunks(); - - /// Returns the cap multiplier value of the given monster family - static int GetCapMultiplier(cMonster::eFamily a_MobFamily); -} ; - - - - diff --git a/source/MobFamilyCollecter.cpp b/source/MobFamilyCollecter.cpp deleted file mode 100644 index e9c69e078..000000000 --- a/source/MobFamilyCollecter.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "MobFamilyCollecter.h" -#include "Mobs/Monster.h" - - - -void cMobFamilyCollecter::CollectMob(cMonster & a_Monster) -{ - cMonster::eFamily MobFamily = a_Monster.GetMobFamily(); - m_Mobs[MobFamily].insert(&a_Monster); -} - - - - - -int cMobFamilyCollecter::GetNumberOfCollectedMobs(cMonster::eFamily a_Family) -{ - return m_Mobs[a_Family].size(); -} - - - - diff --git a/source/MobFamilyCollecter.h b/source/MobFamilyCollecter.h deleted file mode 100644 index 6cef133b5..000000000 --- a/source/MobFamilyCollecter.h +++ /dev/null @@ -1,39 +0,0 @@ - -#pragma once - -#include -#include -#include "BlockID.h" -#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it - - - - -// fwd: -class cChunk; - - - - - -/** This class is used to collect the list of mobs for each family -*/ -class cMobFamilyCollecter -{ -public : - typedef const std::set tMobFamilyList; - - // collect a mob - void CollectMob(cMonster & a_Monster); - - // return the number of mobs for this family - int GetNumberOfCollectedMobs(cMonster::eFamily a_Family); - -protected : - std::map > m_Mobs; - -} ; - - - - diff --git a/source/MobProximityCounter.cpp b/source/MobProximityCounter.cpp deleted file mode 100644 index 583a71579..000000000 --- a/source/MobProximityCounter.cpp +++ /dev/null @@ -1,83 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "MobProximityCounter.h" - -#include "Entities/Entity.h" -#include "Chunk.h" - -void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance) -{ -// LOGD("Collecting monster %s, with distance %f",a_Monster->GetClass(),a_Distance); - tMonsterToDistance::iterator it = m_MonsterToDistance.find(&a_Monster); - if (it == m_MonsterToDistance.end()) - { - sDistanceAndChunk newDistanceAndChunk(a_Distance,a_Chunk); - std::pair result = m_MonsterToDistance.insert(tMonsterToDistance::value_type(&a_Monster,newDistanceAndChunk)); - if (!result.second) - { - ASSERT(!"A collected Monster was not found inside distance map using find(), but insert() said there already is a key for it"); - } - } - else - { - if (a_Distance < it->second.m_Distance) - { - it->second.m_Distance = a_Distance; - it->second.m_Chunk = a_Chunk; - } - } - - m_EligibleForSpawnChunks.insert(&a_Chunk); - -} - -void cMobProximityCounter::convertMaps() -{ - for(tMonsterToDistance::const_iterator itr = m_MonsterToDistance.begin(); itr != m_MonsterToDistance.end(); itr++) - { - m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,itr->second.m_Chunk))); - } -} - -cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax) -{ - sIterablePair toReturn; - toReturn.m_Count = 0; - toReturn.m_Begin = m_DistanceToMonster.end(); - toReturn.m_End = m_DistanceToMonster.end(); - - a_DistanceMin *= a_DistanceMin;// this is because is use square distance - a_DistanceMax *= a_DistanceMax; - - if (m_DistanceToMonster.size() <= 0) - { - convertMaps(); - } - - for(tDistanceToMonster::const_iterator itr = m_DistanceToMonster.begin(); itr != m_DistanceToMonster.end(); itr++) - { - if (toReturn.m_Begin == m_DistanceToMonster.end()) - { - if (a_DistanceMin == -1 || itr->first > a_DistanceMin) - { - toReturn.m_Begin = itr; // this is the first one with distance > a_DistanceMin; - } - } - - if (toReturn.m_Begin != m_DistanceToMonster.end()) - { - if (a_DistanceMax != -1 && itr->first > a_DistanceMax) - { - toReturn.m_End = itr; // this is just after the last one with distance < a_DistanceMax - // Note : if we are not going through this, it's ok, toReturn.m_End will be end(); - break; - } - else - { - toReturn.m_Count ++; - } - } - } - return toReturn; -} diff --git a/source/MobProximityCounter.h b/source/MobProximityCounter.h deleted file mode 100644 index 8a67139aa..000000000 --- a/source/MobProximityCounter.h +++ /dev/null @@ -1,65 +0,0 @@ - -#pragma once - -#include - -class cChunk; -class cEntity; - - -// This class is used to collect, for each Mob, what is the distance of the closest player -// it was first being designed in order to make mobs spawn / despawn / act -// as the behaviour and even life of mobs depends on the distance to closest player -class cMobProximityCounter -{ -protected : - // structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster) - struct sDistanceAndChunk - { - sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(a_Chunk) {} - double m_Distance; - cChunk& m_Chunk; - }; - struct sMonsterAndChunk - { - sMonsterAndChunk(cEntity& a_Monster, cChunk& a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {} - cEntity& m_Monster; - cChunk& m_Chunk; - }; - -public : - typedef std::map tMonsterToDistance; - typedef std::multimap tDistanceToMonster; - -protected : - // this map is filled during collection phase, it will be later transformed into DistanceToMonster - tMonsterToDistance m_MonsterToDistance; - - // this map is generated after collection phase, in order to access monster by distance to player - tDistanceToMonster m_DistanceToMonster; - - // this are the collected chunks. Used to determinate the number of elligible chunk for spawning. - std::set m_EligibleForSpawnChunks; - -protected : - // transform monsterToDistance map (that was usefull for collecting) into distanceToMonster - // that will be usefull for picking up. - void convertMaps(); - -public : - // count a mob on a specified chunk with specified distance to an unkown player - // if the distance is shortest than the one collected, this become the new closest - // distance and the chunk become the "hosting" chunk (that is the one that will perform the action) - void CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance); - - // return the mobs that are within the range of distance of the closest player they are - // that means that if a mob is 30 m from a player and 150 m from another one. It will be - // in the range [0..50] but not in [100..200] - struct sIterablePair{ - tDistanceToMonster::const_iterator m_Begin; - tDistanceToMonster::const_iterator m_End; - int m_Count; - }; - sIterablePair getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax); - -}; diff --git a/source/MobSpawner.cpp b/source/MobSpawner.cpp deleted file mode 100644 index 4d0b2777b..000000000 --- a/source/MobSpawner.cpp +++ /dev/null @@ -1,361 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "MobSpawner.h" -#include "Mobs/IncludeAllMonsters.h" - - - - - -cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily,const std::set& a_AllowedTypes) : - m_MonsterFamily(a_MonsterFamily), - m_NewPack(true), - m_MobType(cMonster::mtInvalidType) -{ - for (std::set::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); itr++) - { - if (cMonster::FamilyFromType(*itr) == a_MonsterFamily) - { - m_AllowedTypes.insert(*itr); - } - } -} - - - - - -bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType) -{ - // Packs of non-water mobs can only be centered on an air block - // Packs of water mobs can only be centered on a water block - if (m_MonsterFamily == cMonster::mfWater) - { - return IsBlockWater(a_BlockType); - } - else - { - return a_BlockType == E_BLOCK_AIR; - } -} - - - - - -void cMobSpawner::addIfAllowed(cMonster::eType toAdd, std::set& toAddIn) -{ - std::set::iterator itr = m_AllowedTypes.find(toAdd); - if (itr != m_AllowedTypes.end()) - { - toAddIn.insert(toAdd); - } -} - - - - - -cMonster::eType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) -{ - std::set allowedMobs; - - if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore) - { - addIfAllowed(cMonster::mtMooshroom, allowedMobs); - } - else if (a_Biome == biNether) - { - addIfAllowed(cMonster::mtGhast, allowedMobs); - addIfAllowed(cMonster::mtZombiePigman, allowedMobs); - addIfAllowed(cMonster::mtMagmaCube, allowedMobs); - } - else if (a_Biome == biEnd) - { - addIfAllowed(cMonster::mtEnderman, allowedMobs); - } - else - { - addIfAllowed(cMonster::mtBat, allowedMobs); - addIfAllowed(cMonster::mtSpider, allowedMobs); - addIfAllowed(cMonster::mtZombie, allowedMobs); - addIfAllowed(cMonster::mtSkeleton, allowedMobs); - addIfAllowed(cMonster::mtCreeper, allowedMobs); - addIfAllowed(cMonster::mtSquid, allowedMobs); - - if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean) - { - addIfAllowed(cMonster::mtSheep, allowedMobs); - addIfAllowed(cMonster::mtPig, allowedMobs); - addIfAllowed(cMonster::mtCow, allowedMobs); - addIfAllowed(cMonster::mtChicken, allowedMobs); - addIfAllowed(cMonster::mtEnderman, allowedMobs); - addIfAllowed(cMonster::mtSlime, allowedMobs); // MG TODO : much more complicated rule - - if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills) - { - addIfAllowed(cMonster::mtWolf, allowedMobs); - } - else if (a_Biome == biJungle || a_Biome == biJungleHills) - { - addIfAllowed(cMonster::mtOcelot, allowedMobs); - } - } - } - - int allowedMobsSize = allowedMobs.size(); - if (allowedMobsSize > 0) - { - std::set::iterator itr = allowedMobs.begin(); - int iRandom = m_Random.NextInt(allowedMobsSize,a_Biome); - - for(int i = 0; i < iRandom; i++) - { - itr++; - } - - return *itr; - } - return cMonster::mtInvalidType; -} - - - - - -bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome) -{ - BLOCKTYPE TargetBlock; - if (m_AllowedTypes.find(a_MobType) != m_AllowedTypes.end() && a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock)) - { - NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); - NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); - BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ); - BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); - - SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight); - - switch(a_MobType) - { - case cMonster::mtSquid: - { - return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62); - } - - case cMonster::mtBat: - { - return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && (!g_BlockTransparent[BlockAbove]); - } - - case cMonster::mtChicken: - case cMonster::mtCow: - case cMonster::mtPig: - case cMonster::mtHorse: - case cMonster::mtSheep: - { - return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (!g_BlockTransparent[BlockBelow]) && - (BlockBelow == E_BLOCK_GRASS) && - (SkyLight >= 9) - ); - } - - case cMonster::mtOcelot: - { - return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - ( - (BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) - ) && - (a_RelY >= 62) && - (m_Random.NextInt(3, a_Biome) != 0) - ); - } - - case cMonster::mtEnderman: - { - if (a_RelY < 250) - { - BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ); - if (BlockTop == E_BLOCK_AIR) - { - BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ); - return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (BlockTop == E_BLOCK_AIR) && - (!g_BlockTransparent[BlockBelow]) && - (SkyLight <= 7) && - (BlockLight <= 7) - ); - } - } - break; - } - - case cMonster::mtSpider: - { - bool CanSpawn = true; - bool HaveFloor = false; - for (int x = 0; x < 2; ++x) - { - for(int z = 0; z < 2; ++z) - { - CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock); - CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR); - if (!CanSpawn) - { - return false; - } - HaveFloor = ( - HaveFloor || - ( - a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock) && - !g_BlockTransparent[TargetBlock] - ) - ); - } - } - return CanSpawn && HaveFloor && (SkyLight <= 7) && (BlockLight <= 7); - } - - case cMonster::mtCreeper: - case cMonster::mtSkeleton: - case cMonster::mtZombie: - { - return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (!g_BlockTransparent[BlockBelow]) && - (SkyLight <= 7) && - (BlockLight <= 7) && - (m_Random.NextInt(2, a_Biome) == 0) - ); - } - - case cMonster::mtSlime: - { - return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (!g_BlockTransparent[BlockBelow]) && - ( - (a_RelY <= 40) || (a_Biome == biSwampland) - ) - ); - } - - case cMonster::mtGhast: - case cMonster::mtZombiePigman: - { - return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (!g_BlockTransparent[BlockBelow]) && - (m_Random.NextInt(20, a_Biome) == 0) - ); - } - - case cMonster::mtWolf: - { - return ( - (TargetBlock == E_BLOCK_GRASS) && - (BlockAbove == E_BLOCK_AIR) && - ( - (a_Biome == biTaiga) || - (a_Biome == biTaigaHills) || - (a_Biome == biForest) || - (a_Biome == biForestHills) || - (a_Biome == biColdTaiga) || - (a_Biome == biColdTaigaHills) || - (a_Biome == biTaigaM) || - (a_Biome == biMegaTaiga) || - (a_Biome == biMegaTaigaHills) - ) - ); - } - - default: - { - LOGD("MG TODO: Write spawning rule for mob type %d", a_MobType); - return false; - } - } - } - return false; -} - - - - - -cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize) -{ - cMonster* toReturn = NULL; - if (m_NewPack) - { - m_MobType = ChooseMobType(a_Biome); - if (m_MobType == cMonster::mtInvalidType) - { - return toReturn; - } - if (m_MobType == cMonster::mtWolf) - { - a_MaxPackSize = 8; - } - else if (m_MobType == cMonster::mtGhast) - { - a_MaxPackSize = 1; - } - m_NewPack = false; - } - - // Make sure we are looking at the right chunk to spawn in - a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - - if (CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome)) - { - cMonster * newMob = cMonster::NewMonsterFromType(m_MobType); - if (newMob) - { - m_Spawned.insert(newMob); - } - toReturn = newMob; - } - return toReturn; -} - - - - - -void cMobSpawner::NewPack() -{ - m_NewPack = true; -} - - - - - -cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void) -{ - return m_Spawned; -} - - - - - -bool cMobSpawner::CanSpawnAnything(void) -{ - return !m_AllowedTypes.empty(); -} - - - - diff --git a/source/MobSpawner.h b/source/MobSpawner.h deleted file mode 100644 index ea6636310..000000000 --- a/source/MobSpawner.h +++ /dev/null @@ -1,76 +0,0 @@ - -#pragma once - -#include -#include "BlockID.h" -#include "ChunkDef.h" -#include "Chunk.h" -#include "FastRandom.h" -#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it - - - - -// fwd: -class cChunk; - - - - - -/** This class is used to determine which monster can be spawned in which place -it is essentially static (eg. Squids spawn in water, Zombies spawn in dark places) -but it also has dynamic part depending on the world.ini settings. -*/ -class cMobSpawner -{ -public : - // constructor - // a_MobFamily is the Family of mobs that this spawner will spawn - // a_AllowedTypes is the set of types allowed for mobs it will spawn. Empty set - // would result in no spawn at all - // Allowed mobs thah are not of the right Family will not be include (no warning) - cMobSpawner(cMonster::eFamily MobFamily, const std::set & a_AllowedTypes); - - /// Check if specified block can be a Pack center for this spawner - bool CheckPackCenter(BLOCKTYPE a_BlockType); - - // Try to create a monster here - // if this is the first of a Pack : determine the type of monster - // BlockType & BlockMeta are used to decide what kind of Mob can Spawn here - // MaxPackSize is set to the maximal size for a pack this type of mob - cMonster * TryToSpawnHere(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize); - - // mark the beginning of a new Pack - // all mobs of the same Pack are the same type - void NewPack(void); - - // return true if there is at least one allowed type - bool CanSpawnAnything(void); - - typedef const std::set tSpawnedContainer; - tSpawnedContainer & getSpawned(void); - -protected : - // return true if specified type of mob can spawn on specified block - bool CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome); - - // return a random type that can spawn on specified biome. - // returns E_ENTITY_TYPE_DONOTUSE if none is possible - cMonster::eType ChooseMobType(EMCSBiome a_Biome); - - // add toAdd inside toAddIn, if toAdd is in m_AllowedTypes - void addIfAllowed(cMonster::eType toAdd, std::set & toAddIn); - -protected : - cMonster::eFamily m_MonsterFamily; - std::set m_AllowedTypes; - bool m_NewPack; - cMonster::eType m_MobType; - std::set m_Spawned; - cFastRandom m_Random; -} ; - - - - diff --git a/source/Mobs/AggressiveMonster.cpp b/source/Mobs/AggressiveMonster.cpp deleted file mode 100644 index cc7e7da2b..000000000 --- a/source/Mobs/AggressiveMonster.cpp +++ /dev/null @@ -1,97 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "AggressiveMonster.h" - -#include "../World.h" -#include "../Vector3f.h" -#include "../Entities/Player.h" -#include "../MersenneTwister.h" - - - - - -cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : - super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height), - m_ChaseTime(999999) -{ - m_EMPersonality = AGGRESSIVE; -} - - - - - -// What to do if in Chasing State -void cAggressiveMonster::InStateChasing(float a_Dt) -{ - super::InStateChasing(a_Dt); - m_ChaseTime += a_Dt; - if (m_Target != NULL) - { - if (m_Target->IsPlayer()) - { - cPlayer * Player = (cPlayer *) m_Target; - if (Player->IsGameModeCreative()) - { - m_EMState = IDLE; - return; - } - } - - Vector3f Pos = Vector3f( GetPosition() ); - Vector3f Their = Vector3f( m_Target->GetPosition() ); - if ((Their - Pos).Length() <= m_AttackRange) - { - Attack(a_Dt); - } - MoveToPosition(Their + Vector3f(0, 0.65f, 0)); - } - else if (m_ChaseTime > 5.f) - { - m_ChaseTime = 0; - m_EMState = IDLE; - } -} - - - - - -void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity) -{ - super::EventSeePlayer(a_Entity); - m_EMState = CHASING; -} - - - - - -void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - - m_SeePlayerInterval += a_Dt; - - if (m_SeePlayerInterval > 1) - { - int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally - - m_SeePlayerInterval = 0.0; - if (rem >= 2) - { - if (m_EMState == CHASING) - { - CheckEventLostPlayer(); - } - else - { - CheckEventSeePlayer(); - } - } - } -} - - diff --git a/source/Mobs/AggressiveMonster.h b/source/Mobs/AggressiveMonster.h deleted file mode 100644 index 5a0d93f3d..000000000 --- a/source/Mobs/AggressiveMonster.h +++ /dev/null @@ -1,30 +0,0 @@ - -#pragma once - -#include "Monster.h" - - - - - -class cAggressiveMonster : - public cMonster -{ - typedef cMonster super; - -public: - cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - - virtual void Tick (float a_Dt, cChunk & a_Chunk) override; - virtual void InStateChasing(float a_Dt) override; - - virtual void EventSeePlayer(cEntity *) override; - - -protected: - float m_ChaseTime; -} ; - - - - diff --git a/source/Mobs/Bat.cpp b/source/Mobs/Bat.cpp deleted file mode 100644 index b9c82996b..000000000 --- a/source/Mobs/Bat.cpp +++ /dev/null @@ -1,15 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Bat.h" -#include "../Vector3d.h" -#include "../Chunk.h" - - -cBat::cBat(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7) -{ -} - - diff --git a/source/Mobs/Bat.h b/source/Mobs/Bat.h deleted file mode 100644 index e878d0ee8..000000000 --- a/source/Mobs/Bat.h +++ /dev/null @@ -1,25 +0,0 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cBat : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cBat(void); - - CLASS_PROTODEF(cBat); - - bool IsHanging(void) const {return false; } -} ; - - - - diff --git a/source/Mobs/Blaze.cpp b/source/Mobs/Blaze.cpp deleted file mode 100644 index f9c05b17a..000000000 --- a/source/Mobs/Blaze.cpp +++ /dev/null @@ -1,52 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Blaze.h" -#include "../World.h" - - - - -cBlaze::cBlaze(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8) -{ -} - - - - - -void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD); -} - - - - - -void cBlaze::Attack(float a_Dt) -{ - m_AttackInterval += a_Dt * m_AttackRate; - - if (m_Target != NULL && m_AttackInterval > 3.0) - { - // Setting this higher gives us more wiggle room for attackrate - Vector3d Speed = GetLookVector() * 20; - Speed.y = Speed.y + 1; - cFireChargeEntity * FireCharge = new cFireChargeEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); - if (FireCharge == NULL) - { - return; - } - if (!FireCharge->Initialize(m_World)) - { - delete FireCharge; - return; - } - m_World->BroadcastSpawnEntity(*FireCharge); - m_AttackInterval = 0.0; - // ToDo: Shoot 3 fireballs instead of 1. - } -} \ No newline at end of file diff --git a/source/Mobs/Blaze.h b/source/Mobs/Blaze.h deleted file mode 100644 index cdb3a1306..000000000 --- a/source/Mobs/Blaze.h +++ /dev/null @@ -1,26 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cBlaze : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cBlaze(void); - - CLASS_PROTODEF(cBlaze); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void Attack(float a_Dt) override; -} ; - - - - diff --git a/source/Mobs/Cavespider.cpp b/source/Mobs/Cavespider.cpp deleted file mode 100644 index aba1ff9f5..000000000 --- a/source/Mobs/Cavespider.cpp +++ /dev/null @@ -1,40 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Cavespider.h" -#include "../World.h" - - - - - -cCavespider::cCavespider(void) : - super("Cavespider", mtCaveSpider, "mob.spider.say", "mob.spider.death", 0.7, 0.5) -{ -} - - - - - -void cCavespider::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - - // TODO: Check vanilla if cavespiders really get passive during the day / in daylight - m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE; -} - - - - - -void cCavespider::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING); - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE); -} - - - - diff --git a/source/Mobs/Cavespider.h b/source/Mobs/Cavespider.h deleted file mode 100644 index 10ea03f7b..000000000 --- a/source/Mobs/Cavespider.h +++ /dev/null @@ -1,26 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cCavespider : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cCavespider(void); - - CLASS_PROTODEF(cCaveSpider); - - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - diff --git a/source/Mobs/Chicken.cpp b/source/Mobs/Chicken.cpp deleted file mode 100644 index 087fd088a..000000000 --- a/source/Mobs/Chicken.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Chicken.h" -#include "../World.h" - - - - - - - - -cChicken::cChicken(void) : - super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4), - m_EggDropTimer(0) -{ -} - - - - -void cChicken::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - - if ((m_EggDropTimer == 6000) && (m_World->GetTickRandomNumber(1) == 0)) - { - cItems Drops; - m_EggDropTimer = 0; - Drops.push_back(cItem(E_ITEM_EGG, 1)); - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); - } - else if (m_EggDropTimer == 12000) - { - cItems Drops; - m_EggDropTimer = 0; - Drops.push_back(cItem(E_ITEM_EGG, 1)); - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); - } - else - { - m_EggDropTimer++; - } -} - - - - - -void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_FEATHER); - a_Drops.push_back(cItem(IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN, 1)); -} - - - - - - - - diff --git a/source/Mobs/Chicken.h b/source/Mobs/Chicken.h deleted file mode 100644 index 979c4d8a0..000000000 --- a/source/Mobs/Chicken.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "PassiveMonster.h" - - - - - -class cChicken : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cChicken(void); - - CLASS_PROTODEF(cChicken); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - -private: - - - int m_EggDropTimer; -} ; - - - diff --git a/source/Mobs/Cow.cpp b/source/Mobs/Cow.cpp deleted file mode 100644 index 9eb74dac2..000000000 --- a/source/Mobs/Cow.cpp +++ /dev/null @@ -1,45 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Cow.h" -#include "../Entities/Player.h" - - - - - - - -cCow::cCow(void) : - super("Cow", mtCow, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) -{ -} - - - - - -void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); - AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); -} - - - - - -void cCow::OnRightClicked(cPlayer & a_Player) -{ - if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_BUCKET)) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - a_Player.GetInventory().AddItem(E_ITEM_MILK); - } - } -} - - - diff --git a/source/Mobs/Cow.h b/source/Mobs/Cow.h deleted file mode 100644 index 0391d4a31..000000000 --- a/source/Mobs/Cow.h +++ /dev/null @@ -1,26 +0,0 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cCow : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cCow(); - - CLASS_PROTODEF(cCow); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void OnRightClicked(cPlayer & a_Player) override; -} ; - - - - diff --git a/source/Mobs/Creeper.cpp b/source/Mobs/Creeper.cpp deleted file mode 100644 index 4e11ae13e..000000000 --- a/source/Mobs/Creeper.cpp +++ /dev/null @@ -1,47 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Creeper.h" -#include "../World.h" - - - - - -cCreeper::cCreeper(void) : - super("Creeper", mtCreeper, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8), - m_bIsBlowing(false), - m_bIsCharged(false) -{ -} - - - - - -void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER); - - // TODO Check if killed by a skeleton, then drop random music disk -} - - - - - -void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - super::DoTakeDamage(a_TDI); - - if (a_TDI.DamageType == dtLightning) - { - m_bIsCharged = true; - } - - m_World->BroadcastEntityMetadata(*this); -} - - - - diff --git a/source/Mobs/Creeper.h b/source/Mobs/Creeper.h deleted file mode 100644 index c3d4edeae..000000000 --- a/source/Mobs/Creeper.h +++ /dev/null @@ -1,34 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cCreeper : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cCreeper(void); - - CLASS_PROTODEF(cCreeper); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; - - bool IsBlowing(void) const {return m_bIsBlowing; } - bool IsCharged(void) const {return m_bIsCharged; } - -private: - - bool m_bIsBlowing, m_bIsCharged; - -} ; - - - - diff --git a/source/Mobs/EnderDragon.cpp b/source/Mobs/EnderDragon.cpp deleted file mode 100644 index acd81cde1..000000000 --- a/source/Mobs/EnderDragon.cpp +++ /dev/null @@ -1,27 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "EnderDragon.h" - - - - - -cEnderDragon::cEnderDragon(void) : - // TODO: Vanilla source says this, but is it right? Dragons fly, they don't stand - super("EnderDragon", mtEnderDragon, "mob.enderdragon.hit", "mob.enderdragon.end", 16.0, 8.0) -{ -} - - - - - -void cEnderDragon::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - return; -} - - - - diff --git a/source/Mobs/EnderDragon.h b/source/Mobs/EnderDragon.h deleted file mode 100644 index 77177edfe..000000000 --- a/source/Mobs/EnderDragon.h +++ /dev/null @@ -1,25 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cEnderDragon : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cEnderDragon(void); - - CLASS_PROTODEF(cEnderDragon); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - diff --git a/source/Mobs/Enderman.cpp b/source/Mobs/Enderman.cpp deleted file mode 100644 index a784131e4..000000000 --- a/source/Mobs/Enderman.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Enderman.h" - - - - - -cEnderman::cEnderman(void) : - super("Enderman", mtEnderman, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9), - m_bIsScreaming(false), - CarriedBlock(E_BLOCK_AIR), - CarriedMeta(0) -{ -} - - - - - -void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ENDER_PEARL); -} - - - - diff --git a/source/Mobs/Enderman.h b/source/Mobs/Enderman.h deleted file mode 100644 index 32e40e70b..000000000 --- a/source/Mobs/Enderman.h +++ /dev/null @@ -1,36 +0,0 @@ - -#pragma once - -#include "PassiveAggressiveMonster.h" - - - - - -class cEnderman : - public cPassiveAggressiveMonster -{ - typedef cPassiveAggressiveMonster super; - -public: - cEnderman(void); - - CLASS_PROTODEF(cEnderman); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - - bool IsScreaming(void) const {return m_bIsScreaming; } - BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } - NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; } - -private: - - bool m_bIsScreaming; - BLOCKTYPE CarriedBlock; - NIBBLETYPE CarriedMeta; - -} ; - - - - diff --git a/source/Mobs/Ghast.cpp b/source/Mobs/Ghast.cpp deleted file mode 100644 index 96a29b2d8..000000000 --- a/source/Mobs/Ghast.cpp +++ /dev/null @@ -1,54 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Ghast.h" -#include "../World.h" - - - - -cGhast::cGhast(void) : - super("Ghast", mtGhast, "mob.ghast.scream", "mob.ghast.death", 4, 4) -{ -} - - - - - -void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER); - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GHAST_TEAR); -} - - - - - -void cGhast::Attack(float a_Dt) -{ - m_AttackInterval += a_Dt * m_AttackRate; - - if (m_Target != NULL && m_AttackInterval > 3.0) - { - // Setting this higher gives us more wiggle room for attackrate - Vector3d Speed = GetLookVector() * 20; - Speed.y = Speed.y + 1; - cGhastFireballEntity * GhastBall = new cGhastFireballEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); - if (GhastBall == NULL) - { - return; - } - if (!GhastBall->Initialize(m_World)) - { - delete GhastBall; - return; - } - m_World->BroadcastSpawnEntity(*GhastBall); - m_AttackInterval = 0.0; - } -} - - - diff --git a/source/Mobs/Ghast.h b/source/Mobs/Ghast.h deleted file mode 100644 index 43e8bedb6..000000000 --- a/source/Mobs/Ghast.h +++ /dev/null @@ -1,28 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cGhast : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cGhast(void); - - CLASS_PROTODEF(cGhast); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void Attack(float a_Dt) override; - - bool IsCharging(void) const {return false; } -} ; - - - - diff --git a/source/Mobs/Giant.cpp b/source/Mobs/Giant.cpp deleted file mode 100644 index f41977535..000000000 --- a/source/Mobs/Giant.cpp +++ /dev/null @@ -1,27 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Giant.h" - - - - - -cGiant::cGiant(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Giant", mtGiant, "mob.zombie.hurt", "mob.zombie.death", 2.0, 13.5) -{ -} - - - - - -void cGiant::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 10, 50, E_ITEM_ROTTEN_FLESH); -} - - - - diff --git a/source/Mobs/Giant.h b/source/Mobs/Giant.h deleted file mode 100644 index 356dd4352..000000000 --- a/source/Mobs/Giant.h +++ /dev/null @@ -1,25 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cGiant : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cGiant(void); - - CLASS_PROTODEF(cGiant); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - diff --git a/source/Mobs/Horse.cpp b/source/Mobs/Horse.cpp deleted file mode 100644 index bb9a4e3f6..000000000 --- a/source/Mobs/Horse.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Horse.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : - super("Horse", mtHorse, "mob.horse.hit", "mob.horse.death", 1.4, 1.6), - m_bHasChest(false), - m_bIsEating(false), - m_bIsRearing(false), - m_bIsMouthOpen(false), - m_bIsTame(false), - m_bIsSaddled(false), - m_Type(Type), - m_Color(Color), - m_Style(Style), - m_Armour(0), - m_TimesToTame(TameTimes), - m_TameAttemptTimes(0), - m_RearTickCount(0) -{ -} - - - - - -void cHorse::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - - if (!m_bIsMouthOpen) - { - if (m_World->GetTickRandomNumber(50) == 25) - { - m_bIsMouthOpen = true; - } - } - else - { - if (m_World->GetTickRandomNumber(10) == 5) - { - m_bIsMouthOpen = false; - } - } - - if ((m_Attachee != NULL) && (!m_bIsTame)) - { - if (m_TameAttemptTimes < m_TimesToTame) - { - if (m_World->GetTickRandomNumber(50) == 25) - { - m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 0); - m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 2); - m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 6); - m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 8); - - m_Attachee->Detach(); - m_bIsRearing = true; - } - } - else - { - m_bIsTame = true; - } - } - - if (m_bIsRearing) - { - if (m_RearTickCount == 20) - { - m_bIsRearing = false; - m_RearTickCount = 0; - } - else - { - m_RearTickCount++; - } - } - - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cHorse::OnRightClicked(cPlayer & a_Player) -{ - if (!m_bIsSaddled && m_bIsTame) - { - if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) - { - // Saddle the horse: - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - m_bIsSaddled = true; - m_World->BroadcastEntityMetadata(*this); - } - else if (!a_Player.GetEquippedItem().IsEmpty()) - { - // The horse doesn't like being hit, make it rear: - m_bIsRearing = true; - m_RearTickCount = 0; - } - } - else - { - if (m_Attachee != NULL) - { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) - { - a_Player.Detach(); - return; - } - - if (m_Attachee->IsPlayer()) - { - return; - } - - m_Attachee->Detach(); - } - - m_TameAttemptTimes++; - a_Player.AttachTo(this); - } -} - - - - - -void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); - if (m_bIsSaddled) - { - a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); - } -} - - - - diff --git a/source/Mobs/Horse.h b/source/Mobs/Horse.h deleted file mode 100644 index be0c23f9b..000000000 --- a/source/Mobs/Horse.h +++ /dev/null @@ -1,44 +0,0 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cHorse : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cHorse(int Type, int Color, int Style, int TameTimes); - - CLASS_PROTODEF(cHorse); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - - bool IsSaddled (void) const {return m_bIsSaddled; } - bool IsChested (void) const {return m_bHasChest; } - bool IsEating (void) const {return m_bIsEating; } - bool IsRearing (void) const {return m_bIsRearing; } - bool IsMthOpen (void) const {return m_bIsMouthOpen; } - bool IsTame (void) const {return m_bIsTame; } - int GetHorseType (void) const {return m_Type; } - int GetHorseColor (void) const {return m_Color; } - int GetHorseStyle (void) const {return m_Style; } - int GetHorseArmour (void) const {return m_Armour;} - -private: - - bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled; - int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; - -} ; - - - - diff --git a/source/Mobs/IncludeAllMonsters.h b/source/Mobs/IncludeAllMonsters.h deleted file mode 100644 index 1b436a11f..000000000 --- a/source/Mobs/IncludeAllMonsters.h +++ /dev/null @@ -1,29 +0,0 @@ -#include "Bat.h" -#include "Blaze.h" -#include "Cavespider.h" -#include "Chicken.h" -#include "Cow.h" -#include "Creeper.h" -#include "Enderman.h" -#include "EnderDragon.h" -#include "Ghast.h" -#include "Giant.h" -#include "Horse.h" -#include "IronGolem.h" -#include "Magmacube.h" -#include "Mooshroom.h" -#include "Ocelot.h" -#include "Pig.h" -#include "Sheep.h" -#include "Silverfish.h" -#include "Skeleton.h" -#include "Slime.h" -#include "SnowGolem.h" -#include "Spider.h" -#include "Squid.h" -#include "Villager.h" -#include "Witch.h" -#include "Wither.h" -#include "Wolf.h" -#include "Zombie.h" -#include "Zombiepigman.h" diff --git a/source/Mobs/IronGolem.cpp b/source/Mobs/IronGolem.cpp deleted file mode 100644 index 47c961098..000000000 --- a/source/Mobs/IronGolem.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "IronGolem.h" - - - - - -cIronGolem::cIronGolem(void) : - super("IronGolem", mtIronGolem, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9) -{ -} - - - - - -void cIronGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 5, E_ITEM_IRON); -} - - - - diff --git a/source/Mobs/IronGolem.h b/source/Mobs/IronGolem.h deleted file mode 100644 index d49ff4cab..000000000 --- a/source/Mobs/IronGolem.h +++ /dev/null @@ -1,25 +0,0 @@ - -#pragma once - -#include "PassiveAggressiveMonster.h" - - - - - -class cIronGolem : - public cPassiveAggressiveMonster -{ - typedef cPassiveAggressiveMonster super; - -public: - cIronGolem(void); - - CLASS_PROTODEF(cIronGolem); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - diff --git a/source/Mobs/Magmacube.cpp b/source/Mobs/Magmacube.cpp deleted file mode 100644 index 86447ff6b..000000000 --- a/source/Mobs/Magmacube.cpp +++ /dev/null @@ -1,27 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Magmacube.h" - - - - - -cMagmaCube::cMagmaCube(int a_Size) : - super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size), - m_Size(a_Size) -{ -} - - - - - -void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM); -} - - - - diff --git a/source/Mobs/Magmacube.h b/source/Mobs/Magmacube.h deleted file mode 100644 index 130952970..000000000 --- a/source/Mobs/Magmacube.h +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cMagmaCube : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - /// Creates a MagmaCube of the specified size; size is 1 .. 3, with 1 being the smallest - cMagmaCube(int a_Size); - - CLASS_PROTODEF(cMagmaCube); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - int GetSize(void) const { return m_Size; } - -protected: - - /// Size of the MagmaCube, 1 .. 3, with 1 being the smallest - int m_Size; -} ; - - - - diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp deleted file mode 100644 index 8a5717e27..000000000 --- a/source/Mobs/Monster.cpp +++ /dev/null @@ -1,758 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "IncludeAllMonsters.h" -#include "../Root.h" -#include "../Server.h" -#include "../ClientHandle.h" -#include "../World.h" -#include "../Entities/Player.h" -#include "../Defines.h" -#include "../MonsterConfig.h" -#include "../MersenneTwister.h" - -#include "../Vector3f.h" -#include "../Vector3i.h" -#include "../Vector3d.h" -#include "../Tracer.h" -#include "../Chunk.h" -#include "../FastRandom.h" - - - - - -/** Map for eType <-> string -Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType() -The strings need to be lowercase (for more efficient comparisons in StringToMobType()) -*/ -static const struct -{ - cMonster::eType m_Type; - const char * m_lcName; -} g_MobTypeNames[] = -{ - {cMonster::mtBat, "bat"}, - {cMonster::mtBlaze, "blaze"}, - {cMonster::mtCaveSpider, "cavespider"}, - {cMonster::mtChicken, "chicken"}, - {cMonster::mtCow, "cow"}, - {cMonster::mtCreeper, "creeper"}, - {cMonster::mtEnderman, "enderman"}, - {cMonster::mtGhast, "ghast"}, - {cMonster::mtHorse, "horse"}, - {cMonster::mtMagmaCube, "magmacube"}, - {cMonster::mtMooshroom, "mooshroom"}, - {cMonster::mtOcelot, "ocelot"}, - {cMonster::mtPig, "pig"}, - {cMonster::mtSheep, "sheep"}, - {cMonster::mtSilverfish, "silverfish"}, - {cMonster::mtSkeleton, "skeleton"}, - {cMonster::mtSlime, "slime"}, - {cMonster::mtSpider, "spider"}, - {cMonster::mtSquid, "squid"}, - {cMonster::mtVillager, "villager"}, - {cMonster::mtWitch, "witch"}, - {cMonster::mtWolf, "wolf"}, - {cMonster::mtZombie, "zombie"}, - {cMonster::mtZombiePigman, "zombiepigman"}, -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMonster: - -cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) - : super(etMonster, a_Width, a_Height) - , m_Target(NULL) - , m_AttackRate(3) - , idle_interval(0) - , m_bMovingToDestination(false) - , m_DestinationTime( 0 ) - , m_DestroyTimer( 0 ) - , m_Jump(0) - , m_MobType(a_MobType) - , m_SoundHurt(a_SoundHurt) - , m_SoundDeath(a_SoundDeath) - , m_EMState(IDLE) - , m_SightDistance(25) - , m_SeePlayerInterval (0) - , m_EMPersonality(AGGRESSIVE) - , m_AttackDamage(1.0f) - , m_AttackRange(2.0f) - , m_AttackInterval(0) - , m_BurnsInDaylight(false) -{ - if (!a_ConfigName.empty()) - { - GetMonsterConfig(a_ConfigName); - } -} - - - - - -void cMonster::SpawnOn(cClientHandle & a_Client) -{ - a_Client.SendSpawnMob(*this); -} - - - - - -void cMonster::MoveToPosition( const Vector3f & a_Position ) -{ - m_bMovingToDestination = true; - - m_Destination = a_Position; -} - - - - - -bool cMonster::ReachedDestination() -{ - Vector3f Distance = (m_Destination) - GetPosition(); - if( Distance.SqrLength() < 2.f ) - return true; - - return false; -} - - - - - -void cMonster::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - - if (m_Health <= 0) - { - // The mob is dead, but we're still animating the "puff" they leave when they die - m_DestroyTimer += a_Dt / 1000; - if (m_DestroyTimer > 1) - { - Destroy(true); - } - return; - } - - // Burning in daylight - HandleDaylightBurning(a_Chunk); - - HandlePhysics(a_Dt,a_Chunk); - BroadcastMovementUpdate(); - - a_Dt /= 1000; - - if (m_bMovingToDestination) - { - Vector3f Pos( GetPosition() ); - Vector3f Distance = m_Destination - Pos; - if( !ReachedDestination() ) - { - Distance.y = 0; - Distance.Normalize(); - Distance *= 3; - SetSpeedX( Distance.x ); - SetSpeedZ( Distance.z ); - - if (m_EMState == ESCAPING) - { //Runs Faster when escaping :D otherwise they just walk away - SetSpeedX (GetSpeedX() * 2.f); - SetSpeedZ (GetSpeedZ() * 2.f); - } - } - else - { - m_bMovingToDestination = false; - } - - if( GetSpeed().SqrLength() > 0.f ) - { - if( m_bOnGround ) - { - Vector3f NormSpeed = Vector3f(GetSpeed()).NormalizeCopy(); - Vector3f NextBlock = Vector3f( GetPosition() ) + NormSpeed; - int NextHeight; - if (!m_World->TryGetHeight((int)NextBlock.x, (int)NextBlock.z, NextHeight)) - { - // The chunk at NextBlock is not loaded - return; - } - if( NextHeight > (GetPosY() - 1.0) && (NextHeight - GetPosY()) < 2.5 ) - { - m_bOnGround = false; - SetSpeedY(5.f); // Jump!! - } - } - } - } - - Vector3d Distance = m_Destination - GetPosition(); - if (Distance.SqrLength() > 0.1f) - { - double Rotation, Pitch; - Distance.Normalize(); - VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch ); - SetHeadYaw (Rotation); - SetRotation( Rotation ); - SetPitch( -Pitch ); - } - - switch (m_EMState) - { - case IDLE: - { - // If enemy passive we ignore checks for player visibility - InStateIdle(a_Dt); - break; - } - - case CHASING: - { - // If we do not see a player anymore skip chasing action - InStateChasing(a_Dt); - break; - } - - case ESCAPING: - { - InStateEscaping(a_Dt); - break; - } - } // switch (m_EMState) -} - - - - - - -void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - super::DoTakeDamage(a_TDI); - if((m_SoundHurt != "") && (m_Health > 0)) m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); - if (a_TDI.Attacker != NULL) - { - m_Target = a_TDI.Attacker; - AddReference(m_Target); - } -} - - - - - -void cMonster::KilledBy(cEntity * a_Killer) -{ - super::KilledBy(a_Killer); - if (m_SoundHurt != "") - { - m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); - } - m_DestroyTimer = 0; -} - - - - - -//----State Logic - -const char *cMonster::GetState() -{ - switch(m_EMState) - { - case IDLE: return "Idle"; - case ATTACKING: return "Attacking"; - case CHASING: return "Chasing"; - default: return "Unknown"; - } -} - - - - - -// for debugging -void cMonster::SetState(const AString & a_State) -{ - if (a_State.compare("Idle") == 0) - { - m_EMState = IDLE; - } - else if (a_State.compare("Attacking") == 0) - { - m_EMState = ATTACKING; - } - else if (a_State.compare("Chasing") == 0) - { - m_EMState = CHASING; - } - else - { - LOGD("cMonster::SetState(): Invalid state"); - ASSERT(!"Invalid state"); - } -} - - - - - -//Checks to see if EventSeePlayer should be fired -//monster sez: Do I see the player -void cMonster::CheckEventSeePlayer(void) -{ - // TODO: Rewrite this to use cWorld's DoWithPlayers() - cPlayer * Closest = FindClosestPlayer(); - - if (Closest != NULL) - { - EventSeePlayer(Closest); - } -} - - - - - -void cMonster::CheckEventLostPlayer(void) -{ - Vector3f pos; - cTracer LineOfSight(GetWorld()); - - if (m_Target != NULL) - { - pos = m_Target->GetPosition(); - if ((pos - GetPosition()).Length() > m_SightDistance || LineOfSight.Trace(GetPosition(),(pos - GetPosition()), (int)(pos - GetPosition()).Length())) - { - EventLosePlayer(); - } - } - else - { - EventLosePlayer(); - } -} - - - - - -// What to do if player is seen -// default to change state to chasing -void cMonster::EventSeePlayer(cEntity * a_SeenPlayer) -{ - m_Target = a_SeenPlayer; - AddReference(m_Target); -} - - - - - -void cMonster::EventLosePlayer(void) -{ - Dereference(m_Target); - m_Target = NULL; - m_EMState = IDLE; -} - - - - - -// What to do if in Idle State -void cMonster::InStateIdle(float a_Dt) -{ - idle_interval += a_Dt; - if (idle_interval > 1) - { - // at this interval the results are predictable - int rem = m_World->GetTickRandomNumber(6) + 1; - // LOGD("Moving: int: %3.3f rem: %i",idle_interval,rem); - idle_interval -= 1; // So nothing gets dropped when the server hangs for a few seconds - Vector3f Dist; - Dist.x = (float)(m_World->GetTickRandomNumber(10) - 5); - Dist.z = (float)(m_World->GetTickRandomNumber(10) - 5); - if ((Dist.SqrLength() > 2) && (rem >= 3)) - { - m_Destination.x = (float)(GetPosX() + Dist.x); - m_Destination.z = (float)(GetPosZ() + Dist.z); - int PosY; - if (m_World->TryGetHeight((int)m_Destination.x, (int)m_Destination.z, PosY)) - { - m_Destination.y = (float)PosY + 1.2f; - MoveToPosition(m_Destination); - } - } - } -} - - - - - -// What to do if in Chasing State -// This state should always be defined in each child class -void cMonster::InStateChasing(float a_Dt) -{ - UNUSED(a_Dt); -} - - - - - -// What to do if in Escaping State -void cMonster::InStateEscaping(float a_Dt) -{ - UNUSED(a_Dt); - - if (m_Target != NULL) - { - Vector3d newloc = GetPosition(); - newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); - newloc.z = (m_Target->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); - MoveToPosition(newloc); - } - else - { - m_EMState = IDLE; // This shouldnt be required but just to be safe - } -} - - - - - -// Do attack here -// a_Dt is passed so we can set attack rate -void cMonster::Attack(float a_Dt) -{ - m_AttackInterval += a_Dt * m_AttackRate; - if ((m_Target != NULL) && (m_AttackInterval > 3.0)) - { - // Setting this higher gives us more wiggle room for attackrate - m_AttackInterval = 0.0; - ((cPawn *)m_Target)->TakeDamage(*this); - } -} - - - - - -// Checks for Players close by and if they are visible return the closest -cPlayer * cMonster::FindClosestPlayer(void) -{ - return m_World->FindClosestPlayer(GetPosition(), m_SightDistance); -} - - - - - -void cMonster::GetMonsterConfig(const AString & a_Name) -{ - cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); -} - - - - - -void cMonster::SetAttackRate(int ar) -{ - m_AttackRate = (float)ar; -} - - - - - -void cMonster::SetAttackRange(float ar) -{ - m_AttackRange = ar; -} - - - - - -void cMonster::SetAttackDamage(float ad) -{ - m_AttackDamage = ad; -} - - - - - -void cMonster::SetSightDistance(float sd) -{ - m_SightDistance = sd; -} - - - - - -AString cMonster::MobTypeToString(cMonster::eType a_MobType) -{ - // Mob types aren't sorted, so we need to search linearly: - for (int i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (g_MobTypeNames[i].m_Type == a_MobType) - { - return g_MobTypeNames[i].m_lcName; - } - } - - // Not found: - return ""; -} - - - - - -cMonster::eType cMonster::StringToMobType(const AString & a_Name) -{ - AString lcName(a_Name); - StrToLower(lcName); - - // Binary-search for the lowercase name: - int lo = 0, hi = ARRAYCOUNT(g_MobTypeNames) - 1; - while (hi - lo > 1) - { - int mid = (lo + hi) / 2; - int res = strcmp(g_MobTypeNames[mid].m_lcName, lcName.c_str()); - if (res == 0) - { - return g_MobTypeNames[mid].m_Type; - } - if (res < 0) - { - lo = mid; - } - else - { - hi = mid; - } - } - // Range has collapsed to at most two elements, compare each: - if (strcmp(g_MobTypeNames[lo].m_lcName, lcName.c_str()) == 0) - { - return g_MobTypeNames[lo].m_Type; - } - if ((lo != hi) && (strcmp(g_MobTypeNames[hi].m_lcName, lcName.c_str()) == 0)) - { - return g_MobTypeNames[hi].m_Type; - } - - // Not found: - return mtInvalidType; -} - - - - - -cMonster::eFamily cMonster::FamilyFromType(eType a_Type) -{ - switch (a_Type) - { - case mtBat: return mfAmbient; - case mtBlaze: return mfHostile; - case mtCaveSpider: return mfHostile; - case mtChicken: return mfPassive; - case mtCow: return mfPassive; - case mtCreeper: return mfHostile; - case mtEnderman: return mfHostile; - case mtGhast: return mfHostile; - case mtHorse: return mfPassive; - case mtMagmaCube: return mfHostile; - case mtMooshroom: return mfHostile; - case mtOcelot: return mfHostile; - case mtPig: return mfPassive; - case mtSheep: return mfPassive; - case mtSilverfish: return mfHostile; - case mtSkeleton: return mfHostile; - case mtSlime: return mfHostile; - case mtSpider: return mfHostile; - case mtSquid: return mfWater; - case mtVillager: return mfPassive; - case mtWitch: return mfHostile; - case mtWolf: return mfHostile; - case mtZombie: return mfHostile; - case mtZombiePigman: return mfHostile; - } ; - ASSERT(!"Unhandled mob type"); - return mfMaxplusone; -} - - - - - -int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) -{ - switch (a_MobFamily) - { - case mfHostile: return 40; - case mfPassive: return 40; - case mfAmbient: return 40; - case mfWater: return 400; - } - ASSERT(!"Unhandled mob family"); - return -1; -} - - - - - -cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) -{ - cFastRandom Random; - cMonster * toReturn = NULL; - - // Create the mob entity - switch (a_MobType) - { - case mtMagmaCube: - case mtSlime: - { - toReturn = new cSlime (Random.NextInt(2) + 1); - break; - } - case mtSkeleton: - { - // TODO: Actual detection of spawning in Nether - toReturn = new cSkeleton(Random.NextInt(1) == 0 ? false : true); - break; - } - case mtVillager: - { - int VillagerType = Random.NextInt(6); - if (VillagerType == 6) - { - // Give farmers a better chance of spawning - VillagerType = 0; - } - - toReturn = new cVillager((cVillager::eVillagerType)VillagerType); - break; - } - case mtHorse: - { - // Horses take a type (species), a colour, and a style (dots, stripes, etc.) - int HorseType = Random.NextInt(7); - int HorseColor = Random.NextInt(6); - int HorseStyle = Random.NextInt(6); - int HorseTameTimes = Random.NextInt(6) + 1; - - if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7)) - { - // Increase chances of normal horse (zero) - HorseType = 0; - } - - toReturn = new cHorse(HorseType, HorseColor, HorseStyle, HorseTameTimes); - break; - } - - case mtBat: toReturn = new cBat(); break; - case mtBlaze: toReturn = new cBlaze(); break; - case mtCaveSpider: toReturn = new cCavespider(); break; - case mtChicken: toReturn = new cChicken(); break; - case mtCow: toReturn = new cCow(); break; - case mtCreeper: toReturn = new cCreeper(); break; - case mtEnderman: toReturn = new cEnderman(); break; - case mtGhast: toReturn = new cGhast(); break; - case mtMooshroom: toReturn = new cMooshroom(); break; - case mtOcelot: toReturn = new cOcelot(); break; - case mtPig: toReturn = new cPig(); break; - case mtSheep: toReturn = new cSheep (Random.NextInt(15)); break; // Colour parameter - case mtSilverfish: toReturn = new cSilverfish(); break; - case mtSpider: toReturn = new cSpider(); break; - case mtSquid: toReturn = new cSquid(); break; - case mtWitch: toReturn = new cWitch(); break; - case mtWolf: toReturn = new cWolf(); break; - case mtZombie: toReturn = new cZombie(false); break; // TODO: Infected zombie parameter - case mtZombiePigman: toReturn = new cZombiePigman(); break; - default: - { - ASSERT(!"Unhandled mob type whilst trying to spawn mob!"); - } - } - return toReturn; -} - - - - - -void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth) -{ - MTRand r1; - int Count = r1.randInt() % (a_Max + 1 - a_Min) + a_Min; - if (Count > 0) - { - a_Drops.push_back(cItem(a_Item, Count, a_ItemHealth)); - } -} - - - - - -void cMonster::HandleDaylightBurning(cChunk & a_Chunk) -{ - if (!m_BurnsInDaylight) - { - return; - } - - int RelY = (int)floor(GetPosY()); - if ((RelY < 0) || (RelY >= cChunkDef::Height)) - { - // Outside the world - return; - } - - int RelX = (int)floor(GetPosX()) - GetChunkX() * cChunkDef::Width; - int RelZ = (int)floor(GetPosZ()) - GetChunkZ() * cChunkDef::Width; - if ( - (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight - (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand - (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime - !IsOnFire() // Not already burning - ) - { - // Burn for 100 ticks, then decide again - StartBurning(100); - } -} - - - - -cMonster::eFamily cMonster::GetMobFamily(void) const -{ - return FamilyFromType(m_MobType); -} - - - - diff --git a/source/Mobs/Monster.h b/source/Mobs/Monster.h deleted file mode 100644 index 29a705d11..000000000 --- a/source/Mobs/Monster.h +++ /dev/null @@ -1,195 +0,0 @@ - -#pragma once - -#include "../Entities/Pawn.h" -#include "../Defines.h" -#include "../BlockID.h" -#include "../Item.h" - - - - - -class Vector3f; -class cClientHandle; -class cWorld; - - - - -// tolua_begin -class cMonster : - public cPawn -{ - typedef cPawn super; -public: - /// This identifies individual monster type, as well as their network type-ID - enum eType - { - mtInvalidType = -1, - - mtBat = E_META_SPAWN_EGG_BAT, - mtBlaze = E_META_SPAWN_EGG_BLAZE, - mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER, - mtChicken = E_META_SPAWN_EGG_CHICKEN, - mtCow = E_META_SPAWN_EGG_COW, - mtCreeper = E_META_SPAWN_EGG_CREEPER, - mtEnderDragon = E_META_SPAWN_EGG_ENDER_DRAGON, - mtEnderman = E_META_SPAWN_EGG_ENDERMAN, - mtGhast = E_META_SPAWN_EGG_GHAST, - mtGiant = E_META_SPAWN_EGG_GIANT, - mtHorse = E_META_SPAWN_EGG_HORSE, - mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM, - mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE, - mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM, - mtOcelot = E_META_SPAWN_EGG_OCELOT, - mtPig = E_META_SPAWN_EGG_PIG, - mtSheep = E_META_SPAWN_EGG_SHEEP, - mtSilverfish = E_META_SPAWN_EGG_SILVERFISH, - mtSkeleton = E_META_SPAWN_EGG_SKELETON, - mtSlime = E_META_SPAWN_EGG_SLIME, - mtSnowGolem = E_META_SPAWN_EGG_SNOW_GOLEM, - mtSpider = E_META_SPAWN_EGG_SPIDER, - mtSquid = E_META_SPAWN_EGG_SQUID, - mtVillager = E_META_SPAWN_EGG_VILLAGER, - mtWitch = E_META_SPAWN_EGG_WITCH, - mtWither = E_META_SPAWN_EGG_WITHER, - mtWolf = E_META_SPAWN_EGG_WOLF, - mtZombie = E_META_SPAWN_EGG_ZOMBIE, - mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, - } ; - - enum eFamily - { - mfHostile = 0, // Spider, Zombies ... - mfPassive = 1, // Cows, Pigs - mfAmbient = 2, // Bats - mfWater = 3, // Squid - - mfMaxplusone, // Nothing. Be sure this is the last and the others are in order - } ; - - // tolua_end - - enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; - enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality; - - float m_SightDistance; - - /** Creates the mob object. - * If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig() - * a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22)) - * a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively - */ - cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - - CLASS_PROTODEF(cMonster); - - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; - - virtual void KilledBy(cEntity * a_Killer) override; - - virtual void MoveToPosition(const Vector3f & a_Position); - virtual bool ReachedDestination(void); - - // tolua_begin - eType GetMobType(void) const {return m_MobType; } - eFamily GetMobFamily(void) const; - // tolua_end - - - const char * GetState(); - void SetState(const AString & str); - - virtual void CheckEventSeePlayer(void); - virtual void EventSeePlayer(cEntity * a_Player); - virtual cPlayer * FindClosestPlayer(); // non static is easier. also virtual so other mobs can implement their own searching algo - - /// Reads the monster configuration for the specified monster name and assigns it to this object. - void GetMonsterConfig(const AString & a_Name); - - virtual void EventLosePlayer(void); - virtual void CheckEventLostPlayer(void); - - virtual void InStateIdle (float a_Dt); - virtual void InStateChasing (float a_Dt); - virtual void InStateEscaping(float a_Dt); - - virtual void Attack(float a_Dt); - - int GetAttackRate(){return (int)m_AttackRate;} - void SetAttackRate(int ar); - void SetAttackRange(float ar); - void SetAttackDamage(float ad); - void SetSightDistance(float sd); - - /// Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick - void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; } - - // Overridables to handle ageable mobs - virtual bool IsBaby (void) const { return false; } - virtual bool IsTame (void) const { return false; } - virtual bool IsSitting (void) const { return false; } - - // tolua_begin - - /// Translates MobType enum to a string, empty string if unknown - static AString MobTypeToString(eType a_MobType); - - /// Translates MobType string to the enum, mtInvalidType if not recognized - static eType StringToMobType(const AString & a_MobTypeName); - - /// Returns the mob family based on the type - static eFamily FamilyFromType(eType a_MobType); - - /// Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family - static int GetSpawnDelay(cMonster::eFamily a_MobFamily); - - // tolua_end - - /** Creates a new object of the specified mob. - a_MobType is the type of the mob to be created - Asserts and returns null if mob type is not specified - */ - static cMonster * NewMonsterFromType(eType a_MobType); - -protected: - - cEntity * m_Target; - float m_AttackRate; - float idle_interval; - - Vector3f m_Destination; - bool m_bMovingToDestination; - bool m_bPassiveAggressive; - - float m_DestinationTime; - - float m_DestroyTimer; - float m_Jump; - - eType m_MobType; - - AString m_SoundHurt; - AString m_SoundDeath; - - float m_SeePlayerInterval; - float m_AttackDamage; - float m_AttackRange; - float m_AttackInterval; - - bool m_BurnsInDaylight; - - void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0); - - void HandleDaylightBurning(cChunk & a_Chunk); - -} ; // tolua_export - - - - diff --git a/source/Mobs/Mooshroom.cpp b/source/Mobs/Mooshroom.cpp deleted file mode 100644 index 940e2db44..000000000 --- a/source/Mobs/Mooshroom.cpp +++ /dev/null @@ -1,33 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Mooshroom.h" - - - - - -// TODO: Milk Cow - - - - - -cMooshroom::cMooshroom(void) : - super("Mooshroom", mtMooshroom, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) -{ -} - - - - - -void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); - AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); -} - - - - diff --git a/source/Mobs/Mooshroom.h b/source/Mobs/Mooshroom.h deleted file mode 100644 index 73f6348b6..000000000 --- a/source/Mobs/Mooshroom.h +++ /dev/null @@ -1,25 +0,0 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cMooshroom : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cMooshroom(void); - - CLASS_PROTODEF(cMooshroom); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - diff --git a/source/Mobs/Ocelot.h b/source/Mobs/Ocelot.h deleted file mode 100644 index adb7a1f75..000000000 --- a/source/Mobs/Ocelot.h +++ /dev/null @@ -1,26 +0,0 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cOcelot : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cOcelot(void) : - super("Ocelot", mtOcelot, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8) - { - } - - CLASS_PROTODEF(cOcelot); -} ; - - - - diff --git a/source/Mobs/PassiveAggressiveMonster.cpp b/source/Mobs/PassiveAggressiveMonster.cpp deleted file mode 100644 index 28de65905..000000000 --- a/source/Mobs/PassiveAggressiveMonster.cpp +++ /dev/null @@ -1,38 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "PassiveAggressiveMonster.h" - -#include "../Entities/Player.h" - - - - - -cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : - super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) -{ - m_EMPersonality = PASSIVE; -} - - - - - -void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - super::DoTakeDamage(a_TDI); - - if ((m_Target != NULL) && (m_Target->IsPlayer())) - { - cPlayer * Player = (cPlayer *) m_Target; - if (Player->GetGameMode() != 1) - { - m_EMState = CHASING; - } - } -} - - - - diff --git a/source/Mobs/PassiveAggressiveMonster.h b/source/Mobs/PassiveAggressiveMonster.h deleted file mode 100644 index 2c5ef30b1..000000000 --- a/source/Mobs/PassiveAggressiveMonster.h +++ /dev/null @@ -1,23 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cPassiveAggressiveMonster : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; -} ; - - - - diff --git a/source/Mobs/PassiveMonster.cpp b/source/Mobs/PassiveMonster.cpp deleted file mode 100644 index 91ceb5a53..000000000 --- a/source/Mobs/PassiveMonster.cpp +++ /dev/null @@ -1,59 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "PassiveMonster.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : - super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) -{ - m_EMPersonality = PASSIVE; -} - - - - - -void cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - super::DoTakeDamage(a_TDI); - if ((a_TDI.Attacker != this) && (a_TDI.Attacker != NULL)) - { - m_EMState = ESCAPING; - } -} - - - - - -void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - - m_SeePlayerInterval += a_Dt; - - if (m_SeePlayerInterval > 1) // Check every second - { - int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally - - m_SeePlayerInterval = 0.0; - if (rem >= 2) - { - if (m_EMState == ESCAPING) - { - CheckEventLostPlayer(); - } - } - } -} - - - - - diff --git a/source/Mobs/PassiveMonster.h b/source/Mobs/PassiveMonster.h deleted file mode 100644 index 14a6be6b1..000000000 --- a/source/Mobs/PassiveMonster.h +++ /dev/null @@ -1,27 +0,0 @@ - -#pragma once - -#include "Monster.h" - - - - - -class cPassiveMonster : - public cMonster -{ - typedef cMonster super; - -public: - cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - /// When hit by someone, run away - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; - -} ; - - - - diff --git a/source/Mobs/Pig.cpp b/source/Mobs/Pig.cpp deleted file mode 100644 index 0871a38a9..000000000 --- a/source/Mobs/Pig.cpp +++ /dev/null @@ -1,77 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Pig.h" -#include "../Entities/Player.h" -#include "../World.h" - - - - - -cPig::cPig(void) : - super("Pig", mtPig, "mob.pig.say", "mob.pig.death", 0.9, 0.9), - m_bIsSaddled(false) -{ -} - - - - - -void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP); - if (m_bIsSaddled) - { - a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); - } -} - - - - - -void cPig::OnRightClicked(cPlayer & a_Player) -{ - if (m_bIsSaddled) - { - if (m_Attachee != NULL) - { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) - { - // This player is already sitting in, they want out. - a_Player.Detach(); - return; - } - - if (m_Attachee->IsPlayer()) - { - // Another player is already sitting in here, cannot attach - return; - } - - // Detach whatever is sitting in this pig now: - m_Attachee->Detach(); - } - - // Attach the player to this pig - a_Player.AttachTo(this); - } - else if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - - // Set saddle state & broadcast metadata - m_bIsSaddled = true; - m_World->BroadcastEntityMetadata(*this); - } -} - - - - - diff --git a/source/Mobs/Pig.h b/source/Mobs/Pig.h deleted file mode 100644 index 4fd0d8db8..000000000 --- a/source/Mobs/Pig.h +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cPig : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cPig(void); - - CLASS_PROTODEF(cPig); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - bool IsSaddled(void) const { return m_bIsSaddled; } - -private: - - bool m_bIsSaddled; - -} ; - - - - diff --git a/source/Mobs/Sheep.cpp b/source/Mobs/Sheep.cpp deleted file mode 100644 index bda4ccff8..000000000 --- a/source/Mobs/Sheep.cpp +++ /dev/null @@ -1,62 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Sheep.h" -#include "../BlockID.h" -#include "../Entities/Player.h" -#include "../World.h" - - - - - -cSheep::cSheep(int a_Color) : - super("Sheep", mtSheep, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3), - m_IsSheared(false), - m_WoolColor(a_Color) -{ -} - - - - - -void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - if (!m_IsSheared) - { - a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, m_WoolColor)); - } -} - - - - - -void cSheep::OnRightClicked(cPlayer & a_Player) -{ - if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_SHEARS) && (!m_IsSheared)) - { - m_IsSheared = true; - m_World->BroadcastEntityMetadata(*this); - - if (!a_Player.IsGameModeCreative()) - { - a_Player.UseEquippedItem(); - } - - cItems Drops; - int NumDrops = m_World->GetTickRandomNumber(2) + 1; - Drops.push_back(cItem(E_BLOCK_WOOL, NumDrops, m_WoolColor)); - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); - } - if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - a_Player.GetEquippedItem().m_ItemDamage)) - { - m_WoolColor = 15 - a_Player.GetEquippedItem().m_ItemDamage; - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - m_World->BroadcastEntityMetadata(*this); - } -} diff --git a/source/Mobs/Sheep.h b/source/Mobs/Sheep.h deleted file mode 100644 index 8293a2c05..000000000 --- a/source/Mobs/Sheep.h +++ /dev/null @@ -1,34 +0,0 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cSheep : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cSheep(int a_Color); - - CLASS_PROTODEF(cSheep); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - bool IsSheared(void) const { return m_IsSheared; } - int GetFurColor(void) const { return m_WoolColor; } - -private: - - bool m_IsSheared; - int m_WoolColor; - -} ; - - - - diff --git a/source/Mobs/Silverfish.h b/source/Mobs/Silverfish.h deleted file mode 100644 index a6e11c49d..000000000 --- a/source/Mobs/Silverfish.h +++ /dev/null @@ -1,26 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cSilverfish : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cSilverfish(void) : - super("Silverfish", mtSilverfish, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7) - { - } - - CLASS_PROTODEF(cSilverfish); -} ; - - - - diff --git a/source/Mobs/Skeleton.cpp b/source/Mobs/Skeleton.cpp deleted file mode 100644 index 509c2191e..000000000 --- a/source/Mobs/Skeleton.cpp +++ /dev/null @@ -1,70 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Skeleton.h" -#include "../World.h" - - - - -cSkeleton::cSkeleton(bool IsWither) : - super("Skeleton", mtSkeleton, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8), - m_bIsWither(IsWither) -{ - SetBurnsInDaylight(true); -} - - - - - -void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ARROW); - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_BONE); -} - - - - - -void cSkeleton::MoveToPosition(const Vector3f & a_Position) -{ - m_Destination = a_Position; - - // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement. - if (!IsOnFire() && m_World->GetTimeOfDay() < 13187 && m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) - { - m_bMovingToDestination = false; - return; - } - m_bMovingToDestination = true; -} - - - - - -void cSkeleton::Attack(float a_Dt) -{ - m_AttackInterval += a_Dt * m_AttackRate; - - if (m_Target != NULL && m_AttackInterval > 3.0) - { - // Setting this higher gives us more wiggle room for attackrate - Vector3d Speed = GetLookVector() * 20; - Speed.y = Speed.y + 1; - cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); - if (Arrow == NULL) - { - return; - } - if (!Arrow->Initialize(m_World)) - { - delete Arrow; - return; - } - m_World->BroadcastSpawnEntity(*Arrow); - m_AttackInterval = 0.0; - } -} \ No newline at end of file diff --git a/source/Mobs/Skeleton.h b/source/Mobs/Skeleton.h deleted file mode 100644 index 8f31b42e1..000000000 --- a/source/Mobs/Skeleton.h +++ /dev/null @@ -1,33 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cSkeleton : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cSkeleton(bool IsWither); - - CLASS_PROTODEF(cSkeleton); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void MoveToPosition(const Vector3f & a_Position) override; - virtual void Attack(float a_Dt) override; - bool IsWither(void) const { return m_bIsWither; }; - -private: - - bool m_bIsWither; - -} ; - - - - diff --git a/source/Mobs/Slime.cpp b/source/Mobs/Slime.cpp deleted file mode 100644 index 19f376c21..000000000 --- a/source/Mobs/Slime.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Slime.h" - - - - - -/// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest -cSlime::cSlime(int a_Size) : - super("Slime", mtSlime, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size), - m_Size(a_Size) -{ -} - - - - - -void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - // TODO: only when tiny - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_SLIMEBALL); -} - - - - diff --git a/source/Mobs/Slime.h b/source/Mobs/Slime.h deleted file mode 100644 index 782c3113f..000000000 --- a/source/Mobs/Slime.h +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cSlime : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - /// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest - cSlime(int a_Size); - - CLASS_PROTODEF(cSlime); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - int GetSize(void) const { return m_Size; } - -protected: - - /// Size of the slime, 1 .. 3, with 1 being the smallest - int m_Size; -} ; - - - - diff --git a/source/Mobs/SnowGolem.cpp b/source/Mobs/SnowGolem.cpp deleted file mode 100644 index 9e199f87e..000000000 --- a/source/Mobs/SnowGolem.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "SnowGolem.h" - - - - - -cSnowGolem::cSnowGolem(void) : - super("SnowGolem", mtSnowGolem, "", "", 0.4, 1.8) -{ -} - - - - - -void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 5, E_ITEM_SNOWBALL); -} - - - - diff --git a/source/Mobs/SnowGolem.h b/source/Mobs/SnowGolem.h deleted file mode 100644 index d1344adfd..000000000 --- a/source/Mobs/SnowGolem.h +++ /dev/null @@ -1,25 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cSnowGolem : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cSnowGolem(void); - - CLASS_PROTODEF(cSnowGolem); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - diff --git a/source/Mobs/Spider.cpp b/source/Mobs/Spider.cpp deleted file mode 100644 index b19a5dcef..000000000 --- a/source/Mobs/Spider.cpp +++ /dev/null @@ -1,27 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Spider.h" - - - - - -cSpider::cSpider(void) : - super("Spider", mtSpider, "mob.spider.say", "mob.spider.death", 1.4, 0.9) -{ -} - - - - - -void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING); - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE); -} - - - - diff --git a/source/Mobs/Spider.h b/source/Mobs/Spider.h deleted file mode 100644 index 51e65d028..000000000 --- a/source/Mobs/Spider.h +++ /dev/null @@ -1,25 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cSpider : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cSpider(void); - - CLASS_PROTODEF(cSpider); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - diff --git a/source/Mobs/Squid.cpp b/source/Mobs/Squid.cpp deleted file mode 100644 index a311108ae..000000000 --- a/source/Mobs/Squid.cpp +++ /dev/null @@ -1,56 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Squid.h" -#include "../Vector3d.h" -#include "../Chunk.h" - - - - - -cSquid::cSquid(void) : - super("Squid", mtSquid, "", "", 0.95, 0.95) -{ -} - - - - - -void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - // Drops 0-3 Ink Sacs - AddRandomDropItem(a_Drops, 0, 3, E_ITEM_DYE, E_META_DYE_BLACK); -} - - - - - -void cSquid::Tick(float a_Dt, cChunk & a_Chunk) -{ - // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk - // that is not where the entity currently resides (FS #411) - - Vector3d Pos = GetPosition(); - - // TODO: Not a real behavior, but cool :D - int RelY = (int)floor(Pos.y); - if ((RelY < 0) || (RelY >= cChunkDef::Height)) - { - return; - } - int RelX = (int)floor(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width; - int RelZ = (int)floor(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width; - if (!IsBlockWater(a_Chunk.GetBlock(RelX, RelY, RelZ)) && !IsOnFire()) - { - // Burn for 10 ticks, then decide again - StartBurning(10); - } - - super::Tick(a_Dt, a_Chunk); -} - - - diff --git a/source/Mobs/Squid.h b/source/Mobs/Squid.h deleted file mode 100644 index ad299b95c..000000000 --- a/source/Mobs/Squid.h +++ /dev/null @@ -1,28 +0,0 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cSquid : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cSquid(); - - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - CLASS_PROTODEF(cSquid); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - -} ; - - - - diff --git a/source/Mobs/Villager.cpp b/source/Mobs/Villager.cpp deleted file mode 100644 index 7f89fb6cc..000000000 --- a/source/Mobs/Villager.cpp +++ /dev/null @@ -1,35 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Villager.h" -#include "../World.h" - - - - - -cVillager::cVillager(eVillagerType VillagerType) : - super("Villager", mtVillager, "", "", 0.6, 1.8), - m_Type(VillagerType) -{ -} - - - - - -void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - super::DoTakeDamage(a_TDI); - if (a_TDI.Attacker->IsPlayer()) - { - if (m_World->GetTickRandomNumber(5) == 3) - { - m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_VILLAGER_ANGRY); - } - } -} - - - - diff --git a/source/Mobs/Villager.h b/source/Mobs/Villager.h deleted file mode 100644 index 4cd9aaa8e..000000000 --- a/source/Mobs/Villager.h +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cVillager : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - - enum eVillagerType - { - vtFarmer = 0, - vtLibrarian = 1, - vtPriest = 2, - vtBlacksmith = 3, - vtButcher = 4, - vtGeneric = 5, - vtMax - } ; - - cVillager(eVillagerType VillagerType); - - CLASS_PROTODEF(cVillager); - - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; - int GetVilType(void) const { return m_Type; } - -private: - - int m_Type; - -} ; - - - - diff --git a/source/Mobs/Witch.cpp b/source/Mobs/Witch.cpp deleted file mode 100644 index 25d27041f..000000000 --- a/source/Mobs/Witch.cpp +++ /dev/null @@ -1,32 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Witch.h" - - - - - -cWitch::cWitch(void) : - super("Witch", mtWitch, "", "", 0.6, 1.8) -{ -} - - - - - -void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR); -} - - - - diff --git a/source/Mobs/Witch.h b/source/Mobs/Witch.h deleted file mode 100644 index 4e637beea..000000000 --- a/source/Mobs/Witch.h +++ /dev/null @@ -1,27 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cWitch : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cWitch(); - - CLASS_PROTODEF(cWitch); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - - bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); } -} ; - - - - diff --git a/source/Mobs/Wither.cpp b/source/Mobs/Wither.cpp deleted file mode 100644 index c46e0beab..000000000 --- a/source/Mobs/Wither.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Wither.h" - - - - - -cWither::cWither(void) : - super("Wither", mtWither, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0) -{ -} - - - - - -void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 1, 1, E_ITEM_NETHER_STAR); -} - - - - diff --git a/source/Mobs/Wither.h b/source/Mobs/Wither.h deleted file mode 100644 index 56effc6bb..000000000 --- a/source/Mobs/Wither.h +++ /dev/null @@ -1,25 +0,0 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cWither : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cWither(void); - - CLASS_PROTODEF(cWither); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - diff --git a/source/Mobs/Wolf.cpp b/source/Mobs/Wolf.cpp deleted file mode 100644 index c86250142..000000000 --- a/source/Mobs/Wolf.cpp +++ /dev/null @@ -1,189 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Wolf.h" -#include "../World.h" -#include "../Entities/Player.h" - - - - - -cWolf::cWolf(void) : - super("Wolf", mtWolf, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8), - m_IsAngry(false), - m_IsTame(false), - m_IsSitting(false), - m_IsBegging(false), - m_OwnerName(""), - m_CollarColor(14) -{ -} - - - - - -void cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - super::DoTakeDamage(a_TDI); - if (!m_IsTame) - { - m_IsAngry = true; - } - m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face -} - - - - - -void cWolf::OnRightClicked(cPlayer & a_Player) -{ - if (!IsTame() && !IsAngry()) - { - if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_BONE) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - - if (m_World->GetTickRandomNumber(7) == 0) - { - SetMaxHealth(20); - SetIsTame(true); - SetOwner(a_Player.GetName()); - m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMED); - } - else - { - m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMING); - } - } - } - else if (IsTame()) - { - if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? - { - if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE) - { - SetCollarColor(15 - a_Player.GetEquippedItem().m_ItemDamage); - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - } - else if (IsSitting()) - { - SetIsSitting(false); - } - else - { - SetIsSitting(true); - } - } - } - - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cWolf::Tick(float a_Dt, cChunk & a_Chunk) -{ - if (!IsAngry()) - { - cMonster::Tick(a_Dt, a_Chunk); - } - else - { - super::Tick(a_Dt, a_Chunk); - } - - if (IsSitting()) - { - m_bMovingToDestination = false; - } - - cPlayer * a_Closest_Player = FindClosestPlayer(); - if (a_Closest_Player != NULL) - { - switch (a_Closest_Player->GetEquippedItem().m_ItemType) - { - case E_ITEM_BONE: - case E_ITEM_RAW_BEEF: - case E_ITEM_STEAK: - case E_ITEM_RAW_CHICKEN: - case E_ITEM_COOKED_CHICKEN: - case E_ITEM_ROTTEN_FLESH: - { - if (!IsBegging()) - { - SetIsBegging(true); - m_World->BroadcastEntityMetadata(*this); - } - Vector3f a_NewDestination = a_Closest_Player->GetPosition(); - a_NewDestination.y = a_NewDestination.y + 1; // Look at the head of the player, not his feet. - m_Destination = Vector3f(a_NewDestination); - m_bMovingToDestination = false; - break; - } - default: - { - if (IsBegging()) - { - SetIsBegging(false); - m_World->BroadcastEntityMetadata(*this); - } - } - } - } - - if (IsTame()) - { - TickFollowPlayer(); - } -} - - - - - -void cWolf::TickFollowPlayer() -{ - class cCallback : - public cPlayerListCallback - { - virtual bool Item(cPlayer * a_Player) override - { - OwnerPos = a_Player->GetPosition(); - return false; - } - public: - Vector3f OwnerPos; - } Callback; - if (m_World->DoWithPlayer(m_OwnerName, Callback)) - { - // The player is present in the world, follow them: - double Distance = (Callback.OwnerPos - GetPosition()).Length(); - if (Distance < 3) - { - m_bMovingToDestination = false; - } - else if ((Distance > 30) && (!IsSitting())) - { - TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z); - } - else - { - m_Destination = Callback.OwnerPos; - } - } -} - - - - diff --git a/source/Mobs/Wolf.h b/source/Mobs/Wolf.h deleted file mode 100644 index 040e2cf7a..000000000 --- a/source/Mobs/Wolf.h +++ /dev/null @@ -1,54 +0,0 @@ - -#pragma once - -#include "PassiveAggressiveMonster.h" -#include "../Entities/Entity.h" - - - - - -class cWolf : - public cPassiveAggressiveMonster -{ - typedef cPassiveAggressiveMonster super; - -public: - cWolf(void); - - CLASS_PROTODEF(cWolf); - - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void TickFollowPlayer(); - - // Get functions - bool IsSitting (void) const { return m_IsSitting; } - bool IsTame (void) const { return m_IsTame; } - bool IsBegging (void) const { return m_IsBegging; } - bool IsAngry (void) const { return m_IsAngry; } - AString GetOwner (void) const { return m_OwnerName; } - int GetCollarColor(void) const { return m_CollarColor; } - - // Set functions - void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; } - void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; } - void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; } - void SetIsAngry (bool a_IsAngry) { m_IsAngry = a_IsAngry; } - void SetOwner (AString a_NewOwner) { m_OwnerName = a_NewOwner; } - void SetCollarColor(int a_CollarColor) { m_CollarColor = a_CollarColor; } - -protected: - - bool m_IsSitting; - bool m_IsTame; - bool m_IsBegging; - bool m_IsAngry; - AString m_OwnerName; - int m_CollarColor; -} ; - - - - diff --git a/source/Mobs/Zombie.cpp b/source/Mobs/Zombie.cpp deleted file mode 100644 index a485d2b55..000000000 --- a/source/Mobs/Zombie.cpp +++ /dev/null @@ -1,47 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Zombie.h" -#include "../World.h" -#include "../LineBlockTracer.h" - - - - -cZombie::cZombie(bool IsVillagerZombie) : - super("Zombie", mtZombie, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8), - m_bIsConverting(false), - m_bIsVillagerZombie(IsVillagerZombie) -{ - SetBurnsInDaylight(true); -} - - - - - -void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ROTTEN_FLESH); - - // TODO: Rare drops -} - - - - - -void cZombie::MoveToPosition(const Vector3f & a_Position) -{ - m_Destination = a_Position; - - // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement. - if ((m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) && (m_World->GetTimeOfDay() < 13187) && !IsOnFire()) - { - m_bMovingToDestination = false; - return; - } - m_bMovingToDestination = true; -} - - diff --git a/source/Mobs/Zombie.h b/source/Mobs/Zombie.h deleted file mode 100644 index 7e14fe42f..000000000 --- a/source/Mobs/Zombie.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cZombie : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cZombie(bool IsVillagerZombie); - - CLASS_PROTODEF(cZombie); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void MoveToPosition(const Vector3f & a_Position) override; - - bool IsVillagerZombie(void) const {return m_bIsVillagerZombie; } - bool IsConverting(void) const {return m_bIsConverting; } - -private: - - bool m_bIsVillagerZombie, m_bIsConverting; - -} ; - - - - diff --git a/source/Mobs/Zombiepigman.cpp b/source/Mobs/Zombiepigman.cpp deleted file mode 100644 index 6ac89ed4c..000000000 --- a/source/Mobs/Zombiepigman.cpp +++ /dev/null @@ -1,45 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Zombiepigman.h" -#include "../World.h" - - - - - -cZombiePigman::cZombiePigman(void) : - super("ZombiePigman", mtZombiePigman, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8) -{ -} - - - - - -void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ROTTEN_FLESH); - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GOLD_NUGGET); - - // TODO: Rare drops -} - - - - - -void cZombiePigman::KilledBy(cEntity * a_Killer) -{ - super::KilledBy(a_Killer); - - if ((a_Killer != NULL) && (a_Killer->IsPlayer())) - { - // TODO: Anger all nearby zombie pigmen - // TODO: In vanilla, if one player angers ZPs, do they attack any nearby player, or only that one attacker? - } -} - - - - diff --git a/source/Mobs/Zombiepigman.h b/source/Mobs/Zombiepigman.h deleted file mode 100644 index 67991d56a..000000000 --- a/source/Mobs/Zombiepigman.h +++ /dev/null @@ -1,26 +0,0 @@ - -#pragma once - -#include "PassiveAggressiveMonster.h" - - - - - -class cZombiePigman : - public cPassiveAggressiveMonster -{ - typedef cPassiveAggressiveMonster super; - -public: - cZombiePigman(void); - - CLASS_PROTODEF(cZombiePigman); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void KilledBy(cEntity * a_Killer) override; -} ; - - - - diff --git a/source/MonsterConfig.cpp b/source/MonsterConfig.cpp deleted file mode 100644 index a5a1ebd49..000000000 --- a/source/MonsterConfig.cpp +++ /dev/null @@ -1,104 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "MonsterConfig.h" -#include "Mobs/Monster.h" -#include "../iniFile/iniFile.h" -//#include - - - - - -struct cMonsterConfig::sAttributesStruct -{ - AString m_Name; - double m_SightDistance; - double m_AttackDamage; - double m_AttackRange; - double m_AttackRate; - int m_MaxHealth; -}; - - - - - -struct cMonsterConfig::sMonsterConfigState -{ - AString MonsterTypes; - std::list< sAttributesStruct > AttributesList; -}; - - - - - -cMonsterConfig::cMonsterConfig(void) - : m_pState( new sMonsterConfigState ) -{ - Initialize(); -} - - - - - -cMonsterConfig::~cMonsterConfig() -{ - delete m_pState; -} - - - - - -void cMonsterConfig::Initialize() -{ - cIniFile MonstersIniFile; - - if (!MonstersIniFile.ReadFile("monsters.ini")) - { - LOGWARNING("%s: Cannot read monsters.ini file, monster attributes not available", __FUNCTION__); - return; - } - - for (int i = (int)MonstersIniFile.GetNumKeys(); i >= 0; i--) - { - sAttributesStruct Attributes; - AString Name = MonstersIniFile.GetKeyName(i); - Attributes.m_Name = Name; - Attributes.m_AttackDamage = MonstersIniFile.GetValueF(Name, "AttackDamage", 0); - Attributes.m_AttackRange = MonstersIniFile.GetValueF(Name, "AttackRange", 0); - Attributes.m_SightDistance = MonstersIniFile.GetValueF(Name, "SightDistance", 0); - Attributes.m_AttackRate = MonstersIniFile.GetValueF(Name, "AttackRate", 0); - Attributes.m_MaxHealth = MonstersIniFile.GetValueI(Name, "MaxHealth", 1); - m_pState->AttributesList.push_front(Attributes); - } // for i - SplitList[] -} - - - - - -void cMonsterConfig::AssignAttributes(cMonster * a_Monster, const AString & a_Name) -{ - std::list::const_iterator itr; - for (itr = m_pState->AttributesList.begin(); itr != m_pState->AttributesList.end(); ++itr) - { - if (itr->m_Name.compare(a_Name) == 0) - { - a_Monster->SetAttackDamage ((float)itr->m_AttackDamage); - a_Monster->SetAttackRange ((float)itr->m_AttackRange); - a_Monster->SetSightDistance((float)itr->m_SightDistance); - a_Monster->SetAttackRate ((int)itr->m_AttackRate); - a_Monster->SetMaxHealth (itr->m_MaxHealth); - return; - } - } // for itr - m_pState->AttributesList[] -} - - - - - diff --git a/source/MonsterConfig.h b/source/MonsterConfig.h deleted file mode 100644 index 371d324c2..000000000 --- a/source/MonsterConfig.h +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma once - - - - - -// fwd: -class cMonster; - - - - - -class cMonsterConfig -{ -public: - cMonsterConfig(void); - ~cMonsterConfig(); - - void AssignAttributes(cMonster * a_Monster, const AString & a_Name); - -private: - struct sAttributesStruct; - struct sMonsterConfigState; - sMonsterConfigState* m_pState; - void Initialize(); -} ; - - - - diff --git a/source/Noise.cpp b/source/Noise.cpp deleted file mode 100644 index 729641961..000000000 --- a/source/Noise.cpp +++ /dev/null @@ -1,951 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Noise.h" - - - - - -#if NOISE_USE_SSE - #include //_mm_mul_epi32 -#endif - -#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x)) - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Globals: - -void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase) -{ - const int BUF_SIZE = 512; - ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed - - // Save in XY cuts: - cFile f1; - if (f1.Open(Printf("%s_XY (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) - { - for (int z = 0; z < a_SizeZ; z++) - { - for (int y = 0; y < a_SizeY; y++) - { - int idx = y * a_SizeX + z * a_SizeX * a_SizeY; - unsigned char buf[BUF_SIZE]; - for (int x = 0; x < a_SizeX; x++) - { - buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); - } - f1.Write(buf, a_SizeX); - } // for y - unsigned char buf[BUF_SIZE]; - memset(buf, 0, a_SizeX); - f1.Write(buf, a_SizeX); - } // for z - } // if (XY file open) - - cFile f2; - if (f2.Open(Printf("%s_XZ (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) - { - for (int y = 0; y < a_SizeY; y++) - { - for (int z = 0; z < a_SizeZ; z++) - { - int idx = y * a_SizeX + z * a_SizeX * a_SizeY; - unsigned char buf[BUF_SIZE]; - for (int x = 0; x < a_SizeX; x++) - { - buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); - } - f2.Write(buf, a_SizeX); - } // for z - unsigned char buf[BUF_SIZE]; - memset(buf, 0, a_SizeX); - f2.Write(buf, a_SizeX); - } // for y - } // if (XZ file open) -} - - - - - -void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase) -{ - const int BUF_SIZE = 512; - ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed - - cFile f1; - if (f1.Open(Printf("%s (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) - { - for (int y = 0; y < a_SizeY; y++) - { - int idx = y * a_SizeX; - unsigned char buf[BUF_SIZE]; - for (int x = 0; x < a_SizeX; x++) - { - buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); - } - f1.Write(buf, a_SizeX); - } // for y - } // if (file open) -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCubicCell2D: - -class cCubicCell2D -{ -public: - cCubicCell2D( - const cNoise & a_Noise, ///< Noise to use for generating the random values - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] - int a_SizeX, int a_SizeY, ///< Count of the array, in each direction - const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values - const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values - ); - - /// Uses current m_WorkRnds[] to generate part of the array - void Generate( - int a_FromX, int a_ToX, - int a_FromY, int a_ToY - ); - - /// Initializes m_WorkRnds[] with the specified Floor values - void InitWorkRnds(int a_FloorX, int a_FloorY); - - /// Updates m_WorkRnds[] for the new Floor values. - void Move(int a_NewFloorX, int a_NewFloorY); - -protected: - typedef NOISE_DATATYPE Workspace[4][4]; - - const cNoise & m_Noise; - - Workspace * m_WorkRnds; ///< The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) - Workspace m_Workspace1; ///< Buffer 1 for workspace doublebuffering, used in Move() - Workspace m_Workspace2; ///< Buffer 2 for workspace doublebuffering, used in Move() - int m_CurFloorX; - int m_CurFloorY; - - NOISE_DATATYPE * m_Array; - int m_SizeX, m_SizeY; - const NOISE_DATATYPE * m_FracX; - const NOISE_DATATYPE * m_FracY; -} ; - - - - - -cCubicCell2D::cCubicCell2D( - const cNoise & a_Noise, ///< Noise to use for generating the random values - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] - int a_SizeX, int a_SizeY, ///< Count of the array, in each direction - const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values - const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values -) : - m_Noise(a_Noise), - m_WorkRnds(&m_Workspace1), - m_Array(a_Array), - m_SizeX(a_SizeX), - m_SizeY(a_SizeY), - m_FracX(a_FracX), - m_FracY(a_FracY) -{ -} - - - - - -void cCubicCell2D::Generate( - int a_FromX, int a_ToX, - int a_FromY, int a_ToY -) -{ - for (int y = a_FromY; y < a_ToY; y++) - { - NOISE_DATATYPE Interp[4]; - NOISE_DATATYPE FracY = m_FracY[y]; - Interp[0] = cNoise::CubicInterpolate((*m_WorkRnds)[0][0], (*m_WorkRnds)[0][1], (*m_WorkRnds)[0][2], (*m_WorkRnds)[0][3], FracY); - Interp[1] = cNoise::CubicInterpolate((*m_WorkRnds)[1][0], (*m_WorkRnds)[1][1], (*m_WorkRnds)[1][2], (*m_WorkRnds)[1][3], FracY); - Interp[2] = cNoise::CubicInterpolate((*m_WorkRnds)[2][0], (*m_WorkRnds)[2][1], (*m_WorkRnds)[2][2], (*m_WorkRnds)[2][3], FracY); - Interp[3] = cNoise::CubicInterpolate((*m_WorkRnds)[3][0], (*m_WorkRnds)[3][1], (*m_WorkRnds)[3][2], (*m_WorkRnds)[3][3], FracY); - int idx = y * m_SizeX + a_FromX; - for (int x = a_FromX; x < a_ToX; x++) - { - m_Array[idx++] = cNoise::CubicInterpolate(Interp[0], Interp[1], Interp[2], Interp[3], m_FracX[x]); - } // for x - } // for y -} - - - - - -void cCubicCell2D::InitWorkRnds(int a_FloorX, int a_FloorY) -{ - m_CurFloorX = a_FloorX; - m_CurFloorY = a_FloorY; - for (int x = 0; x < 4; x++) - { - int cx = a_FloorX + x - 1; - for (int y = 0; y < 4; y++) - { - int cy = a_FloorY + y - 1; - (*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy); - } - } -} - - - - - -void cCubicCell2D::Move(int a_NewFloorX, int a_NewFloorY) -{ - // Swap the doublebuffer: - int OldFloorX = m_CurFloorX; - int OldFloorY = m_CurFloorY; - Workspace * OldWorkRnds = m_WorkRnds; - m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1; - - // Reuse as much of the old workspace as possible: - int DiffX = OldFloorX - a_NewFloorX; - int DiffY = OldFloorY - a_NewFloorY; - for (int x = 0; x < 4; x++) - { - int cx = a_NewFloorX + x - 1; - int OldX = x - DiffX; // Where would this X be in the old grid? - for (int y = 0; y < 4; y++) - { - int cy = a_NewFloorY + y - 1; - int OldY = y - DiffY; // Where would this Y be in the old grid? - if ((OldX >= 0) && (OldX < 4) && (OldY >= 0) && (OldY < 4)) - { - (*m_WorkRnds)[x][y] = (*OldWorkRnds)[OldX][OldY]; - } - else - { - (*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy); - } - } - } - m_CurFloorX = a_NewFloorX; - m_CurFloorY = a_NewFloorY; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCubicCell3D: - -class cCubicCell3D -{ -public: - cCubicCell3D( - const cNoise & a_Noise, ///< Noise to use for generating the random values - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] - int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction - const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values - const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values - const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values - ); - - /// Uses current m_WorkRnds[] to generate part of the array - void Generate( - int a_FromX, int a_ToX, - int a_FromY, int a_ToY, - int a_FromZ, int a_ToZ - ); - - /// Initializes m_WorkRnds[] with the specified Floor values - void InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ); - - /// Updates m_WorkRnds[] for the new Floor values. - void Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ); - -protected: - typedef NOISE_DATATYPE Workspace[4][4][4]; - - const cNoise & m_Noise; - - Workspace * m_WorkRnds; ///< The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) - Workspace m_Workspace1; ///< Buffer 1 for workspace doublebuffering, used in Move() - Workspace m_Workspace2; ///< Buffer 2 for workspace doublebuffering, used in Move() - int m_CurFloorX; - int m_CurFloorY; - int m_CurFloorZ; - - NOISE_DATATYPE * m_Array; - int m_SizeX, m_SizeY, m_SizeZ; - const NOISE_DATATYPE * m_FracX; - const NOISE_DATATYPE * m_FracY; - const NOISE_DATATYPE * m_FracZ; -} ; - - - - - -cCubicCell3D::cCubicCell3D( - const cNoise & a_Noise, ///< Noise to use for generating the random values - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] - int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction - const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values - const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values - const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values -) : - m_Noise(a_Noise), - m_WorkRnds(&m_Workspace1), - m_Array(a_Array), - m_SizeX(a_SizeX), - m_SizeY(a_SizeY), - m_SizeZ(a_SizeZ), - m_FracX(a_FracX), - m_FracY(a_FracY), - m_FracZ(a_FracZ) -{ -} - - - - - -void cCubicCell3D::Generate( - int a_FromX, int a_ToX, - int a_FromY, int a_ToY, - int a_FromZ, int a_ToZ -) -{ - for (int z = a_FromZ; z < a_ToZ; z++) - { - int idxZ = z * m_SizeX * m_SizeY; - NOISE_DATATYPE Interp2[4][4]; - NOISE_DATATYPE FracZ = m_FracZ[z]; - for (int x = 0; x < 4; x++) - { - for (int y = 0; y < 4; y++) - { - Interp2[x][y] = cNoise::CubicInterpolate((*m_WorkRnds)[x][y][0], (*m_WorkRnds)[x][y][1], (*m_WorkRnds)[x][y][2], (*m_WorkRnds)[x][y][3], FracZ); - } - } - for (int y = a_FromY; y < a_ToY; y++) - { - NOISE_DATATYPE Interp[4]; - NOISE_DATATYPE FracY = m_FracY[y]; - Interp[0] = cNoise::CubicInterpolate(Interp2[0][0], Interp2[0][1], Interp2[0][2], Interp2[0][3], FracY); - Interp[1] = cNoise::CubicInterpolate(Interp2[1][0], Interp2[1][1], Interp2[1][2], Interp2[1][3], FracY); - Interp[2] = cNoise::CubicInterpolate(Interp2[2][0], Interp2[2][1], Interp2[2][2], Interp2[2][3], FracY); - Interp[3] = cNoise::CubicInterpolate(Interp2[3][0], Interp2[3][1], Interp2[3][2], Interp2[3][3], FracY); - int idx = idxZ + y * m_SizeX + a_FromX; - for (int x = a_FromX; x < a_ToX; x++) - { - m_Array[idx++] = cNoise::CubicInterpolate(Interp[0], Interp[1], Interp[2], Interp[3], m_FracX[x]); - } // for x - } // for y - } // for z -} - - - - - -void cCubicCell3D::InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ) -{ - m_CurFloorX = a_FloorX; - m_CurFloorY = a_FloorY; - m_CurFloorZ = a_FloorZ; - for (int x = 0; x < 4; x++) - { - int cx = a_FloorX + x - 1; - for (int y = 0; y < 4; y++) - { - int cy = a_FloorY + y - 1; - for (int z = 0; z < 4; z++) - { - int cz = a_FloorZ + z - 1; - (*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz); - } - } - } -} - - - - - -void cCubicCell3D::Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ) -{ - // Swap the doublebuffer: - int OldFloorX = m_CurFloorX; - int OldFloorY = m_CurFloorY; - int OldFloorZ = m_CurFloorZ; - Workspace * OldWorkRnds = m_WorkRnds; - m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1; - - // Reuse as much of the old workspace as possible: - int DiffX = OldFloorX - a_NewFloorX; - int DiffY = OldFloorY - a_NewFloorY; - int DiffZ = OldFloorZ - a_NewFloorZ; - for (int x = 0; x < 4; x++) - { - int cx = a_NewFloorX + x - 1; - int OldX = x - DiffX; // Where would this X be in the old grid? - for (int y = 0; y < 4; y++) - { - int cy = a_NewFloorY + y - 1; - int OldY = y - DiffY; // Where would this Y be in the old grid? - for (int z = 0; z < 4; z++) - { - int cz = a_NewFloorZ + z - 1; - int OldZ = z - DiffZ; - if ((OldX >= 0) && (OldX < 4) && (OldY >= 0) && (OldY < 4) && (OldZ >= 0) && (OldZ < 4)) - { - (*m_WorkRnds)[x][y][z] = (*OldWorkRnds)[OldX][OldY][OldZ]; - } - else - { - (*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz); - } - } // for z - } // for y - } // for x - m_CurFloorX = a_NewFloorX; - m_CurFloorY = a_NewFloorY; - m_CurFloorZ = a_NewFloorZ; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cNoise: - -cNoise::cNoise(unsigned int a_Seed) : - m_Seed(a_Seed) -{ -} - - - - - -cNoise::cNoise(const cNoise & a_Noise) : - m_Seed(a_Noise.m_Seed) -{ -} - - - - - -NOISE_DATATYPE cNoise::LinearNoise1D(NOISE_DATATYPE a_X) const -{ - int BaseX = FAST_FLOOR(a_X); - NOISE_DATATYPE FracX = a_X - BaseX; - return LinearInterpolate(IntNoise1D(BaseX), IntNoise1D(BaseX + 1), FracX); -} - - - - - -NOISE_DATATYPE cNoise::CosineNoise1D(NOISE_DATATYPE a_X) const -{ - int BaseX = FAST_FLOOR(a_X); - NOISE_DATATYPE FracX = a_X - BaseX; - return CosineInterpolate(IntNoise1D(BaseX), IntNoise1D(BaseX + 1), FracX); -} - - - - - -NOISE_DATATYPE cNoise::CubicNoise1D(NOISE_DATATYPE a_X) const -{ - int BaseX = FAST_FLOOR(a_X); - NOISE_DATATYPE FracX = a_X - BaseX; - return CubicInterpolate(IntNoise1D(BaseX - 1), IntNoise1D(BaseX), IntNoise1D(BaseX + 1), IntNoise1D(BaseX + 2), FracX); -} - - - - - -NOISE_DATATYPE cNoise::SmoothNoise1D(int a_X) const -{ - return IntNoise1D(a_X) / 2 + IntNoise1D(a_X - 1) / 4 + IntNoise1D(a_X + 1) / 4; -} - - - - - -NOISE_DATATYPE cNoise::CubicNoise2D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const -{ - const int BaseX = FAST_FLOOR(a_X); - const int BaseY = FAST_FLOOR(a_Y); - - const NOISE_DATATYPE points[4][4] = - { - IntNoise2D(BaseX - 1, BaseY - 1), IntNoise2D(BaseX, BaseY - 1), IntNoise2D(BaseX + 1, BaseY - 1), IntNoise2D(BaseX + 2, BaseY - 1), - IntNoise2D(BaseX - 1, BaseY), IntNoise2D(BaseX, BaseY), IntNoise2D(BaseX + 1, BaseY), IntNoise2D(BaseX + 2, BaseY), - IntNoise2D(BaseX - 1, BaseY + 1), IntNoise2D(BaseX, BaseY + 1), IntNoise2D(BaseX + 1, BaseY + 1), IntNoise2D(BaseX + 2, BaseY + 1), - IntNoise2D(BaseX - 1, BaseY + 2), IntNoise2D(BaseX, BaseY + 2), IntNoise2D(BaseX + 1, BaseY + 2), IntNoise2D(BaseX + 2, BaseY + 2), - }; - - const NOISE_DATATYPE FracX = a_X - BaseX; - const NOISE_DATATYPE interp1 = CubicInterpolate(points[0][0], points[0][1], points[0][2], points[0][3], FracX); - const NOISE_DATATYPE interp2 = CubicInterpolate(points[1][0], points[1][1], points[1][2], points[1][3], FracX); - const NOISE_DATATYPE interp3 = CubicInterpolate(points[2][0], points[2][1], points[2][2], points[2][3], FracX); - const NOISE_DATATYPE interp4 = CubicInterpolate(points[3][0], points[3][1], points[3][2], points[3][3], FracX); - - - const NOISE_DATATYPE FracY = a_Y - BaseY; - return CubicInterpolate(interp1, interp2, interp3, interp4, FracY); -} - - - - - -NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const -{ - const int BaseX = FAST_FLOOR(a_X); - const int BaseY = FAST_FLOOR(a_Y); - const int BaseZ = FAST_FLOOR(a_Z); - - const NOISE_DATATYPE points1[4][4] = { - IntNoise3D(BaseX - 1, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY - 1, BaseZ - 1), - IntNoise3D(BaseX - 1, BaseY, BaseZ - 1), IntNoise3D(BaseX, BaseY, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY, BaseZ - 1), - IntNoise3D(BaseX - 1, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY + 1, BaseZ - 1), - IntNoise3D(BaseX - 1, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ - 1), - }; - - const NOISE_DATATYPE FracX = (a_X) - BaseX; - const NOISE_DATATYPE x1interp1 = CubicInterpolate( points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX ); - const NOISE_DATATYPE x1interp2 = CubicInterpolate( points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX ); - const NOISE_DATATYPE x1interp3 = CubicInterpolate( points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX ); - const NOISE_DATATYPE x1interp4 = CubicInterpolate( points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX ); - - const NOISE_DATATYPE points2[4][4] = { - IntNoise3D( BaseX-1, BaseY-1, BaseZ ), IntNoise3D( BaseX, BaseY-1, BaseZ ), IntNoise3D( BaseX+1, BaseY-1, BaseZ ), IntNoise3D( BaseX+2, BaseY-1, BaseZ ), - IntNoise3D( BaseX-1, BaseY, BaseZ ), IntNoise3D( BaseX, BaseY, BaseZ ), IntNoise3D( BaseX+1, BaseY, BaseZ ), IntNoise3D( BaseX+2, BaseY, BaseZ ), - IntNoise3D( BaseX-1, BaseY+1, BaseZ ), IntNoise3D( BaseX, BaseY+1, BaseZ ), IntNoise3D( BaseX+1, BaseY+1, BaseZ ), IntNoise3D( BaseX+2, BaseY+1, BaseZ ), - IntNoise3D( BaseX-1, BaseY+2, BaseZ ), IntNoise3D( BaseX, BaseY+2, BaseZ ), IntNoise3D( BaseX+1, BaseY+2, BaseZ ), IntNoise3D( BaseX+2, BaseY+2, BaseZ ), - }; - - const NOISE_DATATYPE x2interp1 = CubicInterpolate( points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX ); - const NOISE_DATATYPE x2interp2 = CubicInterpolate( points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX ); - const NOISE_DATATYPE x2interp3 = CubicInterpolate( points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX ); - const NOISE_DATATYPE x2interp4 = CubicInterpolate( points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX ); - - const NOISE_DATATYPE points3[4][4] = { - IntNoise3D( BaseX-1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+1 ), - IntNoise3D( BaseX-1, BaseY, BaseZ+1 ), IntNoise3D( BaseX, BaseY, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY, BaseZ+1 ), - IntNoise3D( BaseX-1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+1 ), - IntNoise3D( BaseX-1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+1 ), - }; - - const NOISE_DATATYPE x3interp1 = CubicInterpolate( points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX ); - const NOISE_DATATYPE x3interp2 = CubicInterpolate( points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX ); - const NOISE_DATATYPE x3interp3 = CubicInterpolate( points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX ); - const NOISE_DATATYPE x3interp4 = CubicInterpolate( points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX ); - - const NOISE_DATATYPE points4[4][4] = { - IntNoise3D( BaseX-1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+2 ), - IntNoise3D( BaseX-1, BaseY, BaseZ+2 ), IntNoise3D( BaseX, BaseY, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY, BaseZ+2 ), - IntNoise3D( BaseX-1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+2 ), - IntNoise3D( BaseX-1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+2 ), - }; - - const NOISE_DATATYPE x4interp1 = CubicInterpolate( points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX ); - const NOISE_DATATYPE x4interp2 = CubicInterpolate( points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX ); - const NOISE_DATATYPE x4interp3 = CubicInterpolate( points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX ); - const NOISE_DATATYPE x4interp4 = CubicInterpolate( points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX ); - - const NOISE_DATATYPE FracY = (a_Y) - BaseY; - const NOISE_DATATYPE yinterp1 = CubicInterpolate( x1interp1, x1interp2, x1interp3, x1interp4, FracY ); - const NOISE_DATATYPE yinterp2 = CubicInterpolate( x2interp1, x2interp2, x2interp3, x2interp4, FracY ); - const NOISE_DATATYPE yinterp3 = CubicInterpolate( x3interp1, x3interp2, x3interp3, x3interp4, FracY ); - const NOISE_DATATYPE yinterp4 = CubicInterpolate( x4interp1, x4interp2, x4interp3, x4interp4, FracY ); - - const NOISE_DATATYPE FracZ = (a_Z) - BaseZ; - return CubicInterpolate( yinterp1, yinterp2, yinterp3, yinterp4, FracZ ); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCubicNoise: - -#ifdef _DEBUG - int cCubicNoise::m_NumSingleX = 0; - int cCubicNoise::m_NumSingleXY = 0; - int cCubicNoise::m_NumSingleY = 0; - int cCubicNoise::m_NumCalls = 0; -#endif // _DEBUG - -cCubicNoise::cCubicNoise(int a_Seed) : - m_Noise(a_Seed) -{ -} - - - - - -void cCubicNoise::Generate2D( - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] - int a_SizeX, int a_SizeY, ///< Size of the array (num doubles), in each direction - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction - NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction -) const -{ - ASSERT(a_SizeX < MAX_SIZE); - ASSERT(a_SizeY < MAX_SIZE); - ASSERT(a_StartX < a_EndX); - ASSERT(a_StartY < a_EndY); - - // Calculate the integral and fractional parts of each coord: - int FloorX[MAX_SIZE]; - int FloorY[MAX_SIZE]; - NOISE_DATATYPE FracX[MAX_SIZE]; - NOISE_DATATYPE FracY[MAX_SIZE]; - int SameX[MAX_SIZE]; - int SameY[MAX_SIZE]; - int NumSameX, NumSameY; - CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX); - CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY); - - cCubicCell2D Cell(m_Noise, a_Array, a_SizeX, a_SizeY, FracX, FracY); - - Cell.InitWorkRnds(FloorX[0], FloorY[0]); - - #ifdef _DEBUG - // Statistics on the noise-space coords: - if (NumSameX == 1) - { - m_NumSingleX++; - if (NumSameY == 1) - { - m_NumSingleXY++; - } - } - if (NumSameY == 1) - { - m_NumSingleY++; - } - m_NumCalls++; - #endif // _DEBUG - - // Calculate query values using Cell: - int FromY = 0; - for (int y = 0; y < NumSameY; y++) - { - int ToY = FromY + SameY[y]; - int FromX = 0; - int CurFloorY = FloorY[FromY]; - for (int x = 0; x < NumSameX; x++) - { - int ToX = FromX + SameX[x]; - Cell.Generate(FromX, ToX, FromY, ToY); - Cell.Move(FloorX[ToX], CurFloorY); - FromX = ToX; - } - Cell.Move(FloorX[0], FloorY[ToY]); - FromY = ToY; - } -} - - - - - -void cCubicNoise::Generate3D( - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] - int a_SizeX, int a_SizeY, int a_SizeZ, ///< Size of the array (num doubles), in each direction - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction - NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction - NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Y direction -) const -{ - ASSERT(a_SizeX < MAX_SIZE); - ASSERT(a_SizeY < MAX_SIZE); - ASSERT(a_SizeZ < MAX_SIZE); - ASSERT(a_StartX < a_EndX); - ASSERT(a_StartY < a_EndY); - ASSERT(a_StartZ < a_EndZ); - - // Calculate the integral and fractional parts of each coord: - int FloorX[MAX_SIZE]; - int FloorY[MAX_SIZE]; - int FloorZ[MAX_SIZE]; - NOISE_DATATYPE FracX[MAX_SIZE]; - NOISE_DATATYPE FracY[MAX_SIZE]; - NOISE_DATATYPE FracZ[MAX_SIZE]; - int SameX[MAX_SIZE]; - int SameY[MAX_SIZE]; - int SameZ[MAX_SIZE]; - int NumSameX, NumSameY, NumSameZ; - CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX); - CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY); - CalcFloorFrac(a_SizeZ, a_StartZ, a_EndZ, FloorZ, FracZ, SameZ, NumSameZ); - - cCubicCell3D Cell( - m_Noise, a_Array, - a_SizeX, a_SizeY, a_SizeZ, - FracX, FracY, FracZ - ); - - Cell.InitWorkRnds(FloorX[0], FloorY[0], FloorZ[0]); - - // Calculate query values using Cell: - int FromZ = 0; - for (int z = 0; z < NumSameZ; z++) - { - int ToZ = FromZ + SameZ[z]; - int CurFloorZ = FloorZ[FromZ]; - int FromY = 0; - for (int y = 0; y < NumSameY; y++) - { - int ToY = FromY + SameY[y]; - int CurFloorY = FloorY[FromY]; - int FromX = 0; - for (int x = 0; x < NumSameX; x++) - { - int ToX = FromX + SameX[x]; - Cell.Generate(FromX, ToX, FromY, ToY, FromZ, ToZ); - Cell.Move(FloorX[ToX], CurFloorY, CurFloorZ); - FromX = ToX; - } - Cell.Move(FloorX[0], FloorY[ToY], CurFloorZ); - FromY = ToY; - } // for y - Cell.Move(FloorX[0], FloorY[0], FloorZ[ToZ]); - FromZ = ToZ; - } // for z -} - - - - - -void cCubicNoise::CalcFloorFrac( - int a_Size, - NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End, - int * a_Floor, NOISE_DATATYPE * a_Frac, - int * a_Same, int & a_NumSame -) const -{ - NOISE_DATATYPE val = a_Start; - NOISE_DATATYPE dif = (a_End - a_Start) / (a_Size - 1); - for (int i = 0; i < a_Size; i++) - { - a_Floor[i] = FAST_FLOOR(val); - a_Frac[i] = val - a_Floor[i]; - val += dif; - } - - // Mark up the same floor values into a_Same / a_NumSame: - int CurFloor = a_Floor[0]; - int LastSame = 0; - a_NumSame = 0; - for (int i = 1; i < a_Size; i++) - { - if (a_Floor[i] != CurFloor) - { - a_Same[a_NumSame] = i - LastSame; - LastSame = i; - a_NumSame += 1; - CurFloor = a_Floor[i]; - } - } // for i - a_Floor[] - if (LastSame < a_Size) - { - a_Same[a_NumSame] = a_Size - LastSame; - a_NumSame += 1; - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cPerlinNoise: - -cPerlinNoise::cPerlinNoise(void) : - m_Seed(0) -{ -} - - - - - -cPerlinNoise::cPerlinNoise(int a_Seed) : - m_Seed(a_Seed) -{ -} - - - - - -void cPerlinNoise::SetSeed(int a_Seed) -{ - m_Seed = a_Seed; -} - - - - - -void cPerlinNoise::AddOctave(float a_Frequency, float a_Amplitude) -{ - m_Octaves.push_back(cOctave(m_Seed * (m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude)); -} - - - - - -void cPerlinNoise::Generate2D( - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] - int a_SizeX, int a_SizeY, ///< Count of the array, in each direction - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction - NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction - NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash -) const -{ - if (m_Octaves.empty()) - { - // No work to be done - ASSERT(!"Perlin: No octaves to generate!"); - return; - } - - bool ShouldFreeWorkspace = (a_Workspace == NULL); - int ArrayCount = a_SizeX * a_SizeY; - if (ShouldFreeWorkspace) - { - a_Workspace = new NOISE_DATATYPE[ArrayCount]; - } - - // Generate the first octave directly into array: - m_Octaves.front().m_Noise.Generate2D( - a_Workspace, a_SizeX, a_SizeY, - a_StartX * m_Octaves.front().m_Frequency, a_EndX * m_Octaves.front().m_Frequency, - a_StartY * m_Octaves.front().m_Frequency, a_EndY * m_Octaves.front().m_Frequency - ); - NOISE_DATATYPE Amplitude = m_Octaves.front().m_Amplitude; - for (int i = 0; i < ArrayCount; i++) - { - a_Array[i] *= Amplitude; - } - - // Add each octave: - for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr) - { - // Generate cubic noise for the octave: - itr->m_Noise.Generate2D( - a_Workspace, a_SizeX, a_SizeY, - a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency, - a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency - ); - // Add the cubic noise into the output: - NOISE_DATATYPE Amplitude = itr->m_Amplitude; - for (int i = 0; i < ArrayCount; i++) - { - a_Array[i] += a_Workspace[i] * Amplitude; - } - } - - if (ShouldFreeWorkspace) - { - delete[] a_Workspace; - } -} - - - - - -void cPerlinNoise::Generate3D( - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] - int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction - NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction - NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction - NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash -) const -{ - if (m_Octaves.empty()) - { - // No work to be done - ASSERT(!"Perlin: No octaves to generate!"); - return; - } - - bool ShouldFreeWorkspace = (a_Workspace == NULL); - int ArrayCount = a_SizeX * a_SizeY * a_SizeZ; - if (ShouldFreeWorkspace) - { - a_Workspace = new NOISE_DATATYPE[ArrayCount]; - } - - // Generate the first octave directly into array: - m_Octaves.front().m_Noise.Generate3D( - a_Workspace, a_SizeX, a_SizeY, a_SizeZ, - a_StartX * m_Octaves.front().m_Frequency, a_EndX * m_Octaves.front().m_Frequency, - a_StartY * m_Octaves.front().m_Frequency, a_EndY * m_Octaves.front().m_Frequency, - a_StartZ * m_Octaves.front().m_Frequency, a_EndZ * m_Octaves.front().m_Frequency - ); - NOISE_DATATYPE Amplitude = m_Octaves.front().m_Amplitude; - for (int i = 0; i < ArrayCount; i++) - { - a_Array[i] = a_Workspace[i] * Amplitude; - } - - // Add each octave: - for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr) - { - // Generate cubic noise for the octave: - itr->m_Noise.Generate3D( - a_Workspace, a_SizeX, a_SizeY, a_SizeZ, - a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency, - a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency, - a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency - ); - // Add the cubic noise into the output: - NOISE_DATATYPE Amplitude = itr->m_Amplitude; - for (int i = 0; i < ArrayCount; i++) - { - a_Array[i] += a_Workspace[i] * Amplitude; - } - } - - if (ShouldFreeWorkspace) - { - delete[] a_Workspace; - } -} - - - - diff --git a/source/Noise.h b/source/Noise.h deleted file mode 100644 index ea72c64e9..000000000 --- a/source/Noise.h +++ /dev/null @@ -1,308 +0,0 @@ - -// Noise.h - -// Declares the cNoise, cCubicNoise and cPerlinNoise classes for generating noise - -#pragma once - -// Some settings -#define NOISE_DATATYPE float - - - - - -#ifdef _MSC_VER - #define INLINE __forceinline -#else - #define INLINE inline -#endif - - - - - -class cNoise -{ -public: - cNoise(unsigned int a_Seed); - cNoise(const cNoise & a_Noise); - - // The following functions, if not marked INLINE, are about 20 % slower - INLINE NOISE_DATATYPE IntNoise1D(int a_X) const; - INLINE NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const; - INLINE NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const; - - // Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify. - INLINE int IntNoise1DInt(int a_X) const; - INLINE int IntNoise2DInt(int a_X, int a_Y) const; - INLINE int IntNoise3DInt(int a_X, int a_Y, int a_Z) const; - - NOISE_DATATYPE LinearNoise1D(NOISE_DATATYPE a_X) const; - NOISE_DATATYPE CosineNoise1D(NOISE_DATATYPE a_X) const; - NOISE_DATATYPE CubicNoise1D (NOISE_DATATYPE a_X) const; - NOISE_DATATYPE SmoothNoise1D(int a_X) const; - - NOISE_DATATYPE CubicNoise2D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const; - - NOISE_DATATYPE CubicNoise3D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const; - - void SetSeed(unsigned int a_Seed) { m_Seed = a_Seed; } - - INLINE static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct); - INLINE static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct); - INLINE static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct); - -private: - unsigned int m_Seed; -} ; - - - - - -class cCubicNoise -{ -public: - static const int MAX_SIZE = 512; ///< Maximum size of each dimension of the query arrays. - - - cCubicNoise(int a_Seed); - - - void Generate1D( - NOISE_DATATYPE * a_Array, ///< Array to generate into - int a_SizeX, ///< Count of the array - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX ///< Noise-space coords of the array - ) const; - - - void Generate2D( - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] - int a_SizeX, int a_SizeY, ///< Count of the array, in each direction - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction - NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction - ) const; - - - void Generate3D( - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] - int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction - NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction - NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction - ) const; - -protected: - typedef NOISE_DATATYPE Workspace1D[4]; - typedef NOISE_DATATYPE Workspace2D[4][4]; - - cNoise m_Noise; // Used for integral rnd values - - #ifdef _DEBUG - // Statistics on the noise-space coords: - static int m_NumSingleX; - static int m_NumSingleXY; - static int m_NumSingleY; - static int m_NumCalls; - #endif // _DEBUG - - /// Calculates the integral and fractional parts along one axis. - void CalcFloorFrac( - int a_Size, - NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End, - int * a_Floor, NOISE_DATATYPE * a_Frac, - int * a_Same, int & a_NumSame - ) const; - - void UpdateWorkRnds2DX( - Workspace2D & a_WorkRnds, - Workspace1D & a_Interps, - int a_LastFloorX, int a_NewFloorX, - int a_FloorY, - NOISE_DATATYPE a_FractionY - ) const; -} ; - - - - - -class cPerlinNoise -{ -public: - cPerlinNoise(void); - cPerlinNoise(int a_Seed); - - - void SetSeed(int a_Seed); - - void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude); - - void Generate1D( - NOISE_DATATYPE * a_Array, ///< Array to generate into - int a_SizeX, ///< Count of the array - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array - NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash - ) const; - - - void Generate2D( - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] - int a_SizeX, int a_SizeY, ///< Count of the array, in each direction - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction - NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction - NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash - ) const; - - - void Generate3D( - NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] - int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction - NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction - NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction - NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction - NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash - ) const; - -protected: - class cOctave - { - public: - cCubicNoise m_Noise; - - NOISE_DATATYPE m_Frequency; // Coord multiplier - NOISE_DATATYPE m_Amplitude; // Value multiplier - - cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) : - m_Noise(a_Seed), - m_Frequency(a_Frequency), - m_Amplitude(a_Amplitude) - { - } - } ; - - typedef std::vector cOctaves; - - int m_Seed; - cOctaves m_Octaves; -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Inline function definitions: -// These need to be in the header, otherwise linker error occur in MSVC - -NOISE_DATATYPE cNoise::IntNoise1D(int a_X) const -{ - int x = ((a_X * m_Seed) << 13) ^ a_X; - return (1 - (NOISE_DATATYPE)((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824); - // returns a float number in the range of [-1, 1] -} - - - - - -NOISE_DATATYPE cNoise::IntNoise2D(int a_X, int a_Y) const -{ - int n = a_X + a_Y * 57 + m_Seed * 57 * 57; - n = (n << 13) ^ n; - return (1 - (NOISE_DATATYPE)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824); - // returns a float number in the range of [-1, 1] -} - - - - - -NOISE_DATATYPE cNoise::IntNoise3D(int a_X, int a_Y, int a_Z) const -{ - int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57; - n = (n << 13) ^ n; - return ((NOISE_DATATYPE)1 - (NOISE_DATATYPE)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f); - // returns a float number in the range of [-1, 1] -} - - - - - -int cNoise::IntNoise1DInt(int a_X) const -{ - int x = ((a_X * m_Seed) << 13) ^ a_X; - return ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff); -} - - - - - -int cNoise::IntNoise2DInt(int a_X, int a_Y) const -{ - int n = a_X + a_Y * 57 + m_Seed * 57 * 57; - n = (n << 13) ^ n; - return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); -} - - - - - -int cNoise::IntNoise3DInt(int a_X, int a_Y, int a_Z) const -{ - int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57; - n = (n << 13) ^ n; - return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); -} - - - - - -NOISE_DATATYPE cNoise::CubicInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct) -{ - NOISE_DATATYPE P = (a_D - a_C) - (a_A - a_B); - NOISE_DATATYPE Q = (a_A - a_B) - P; - NOISE_DATATYPE R = a_C - a_A; - NOISE_DATATYPE S = a_B; - - return ((P * a_Pct + Q) * a_Pct + R) * a_Pct + S; -} - - - - - -NOISE_DATATYPE cNoise::CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct) -{ - const NOISE_DATATYPE ft = a_Pct * (NOISE_DATATYPE)3.1415927; - const NOISE_DATATYPE f = (1 - cos(ft)) * (NOISE_DATATYPE)0.5; - return a_A * (1 - f) + a_B * f; -} - - - - - -NOISE_DATATYPE cNoise::LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct) -{ - return a_A * (1 - a_Pct) + a_B * a_Pct; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Global functions: - -extern void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase); -extern void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase); - - - - diff --git a/source/OSSupport/BlockingTCPLink.cpp b/source/OSSupport/BlockingTCPLink.cpp deleted file mode 100644 index 55454a4b5..000000000 --- a/source/OSSupport/BlockingTCPLink.cpp +++ /dev/null @@ -1,149 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "BlockingTCPLink.h" - - - - - -#ifdef _WIN32 - #define MSG_NOSIGNAL (0) -#endif -#ifdef __MACH__ - #define MSG_NOSIGNAL (0) -#endif - - - - - -cBlockingTCPLink::cBlockingTCPLink(void) -{ -} - - - - - -cBlockingTCPLink::~cBlockingTCPLink() -{ - CloseSocket(); -} - - - - - -void cBlockingTCPLink::CloseSocket() -{ - if (!m_Socket.IsValid()) - { - m_Socket.CloseSocket(); - } -} - - - - - -bool cBlockingTCPLink::Connect(const char * iAddress, unsigned int iPort) -{ - ASSERT(!m_Socket.IsValid()); - if (m_Socket.IsValid()) - { - LOGWARN("WARNING: cTCPLink Connect() called while still connected."); - m_Socket.CloseSocket(); - } - - struct hostent *hp; - unsigned int addr; - struct sockaddr_in server; - - m_Socket = socket(AF_INET, SOCK_STREAM, 0); - if (!m_Socket.IsValid()) - { - LOGERROR("cTCPLink: Cannot create a socket"); - return false; - } - - addr = inet_addr(iAddress); - hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); - if (hp == NULL) - { - //LOGWARN("cTCPLink: gethostbyaddr returned NULL"); - hp = gethostbyname(iAddress); - if (hp == NULL) - { - LOGWARN("cTCPLink: Could not resolve %s", iAddress); - CloseSocket(); - return false; - } - } - - server.sin_addr.s_addr = *((unsigned long *)hp->h_addr); - server.sin_family = AF_INET; - server.sin_port = htons( (unsigned short)iPort); - if (connect(m_Socket, (struct sockaddr *)&server, sizeof(server))) - { - LOGWARN("cTCPLink: Connection to \"%s:%d\" failed (%s)", iAddress, iPort, cSocket::GetErrorString( cSocket::GetLastError() ).c_str() ); - CloseSocket(); - return false; - } - - return true; -} - - - - - -int cBlockingTCPLink::Send(char * a_Data, unsigned int a_Size, int a_Flags /* = 0 */ ) -{ - ASSERT(m_Socket.IsValid()); - if (!m_Socket.IsValid()) - { - LOGERROR("cBlockingTCPLink: Trying to send data without a valid connection!"); - return -1; - } - return m_Socket.Send(a_Data, a_Size); -} - - - - - -int cBlockingTCPLink::SendMessage( const char* a_Message, int a_Flags /* = 0 */ ) -{ - ASSERT(m_Socket.IsValid()); - if (!m_Socket.IsValid()) - { - LOGWARN("cBlockingTCPLink: Trying to send message without a valid connection!"); - return -1; - } - return m_Socket.Send(a_Message, strlen(a_Message)); -} - - - - - -void cBlockingTCPLink::ReceiveData(AString & oData) -{ - ASSERT(m_Socket.IsValid()); - if (!m_Socket.IsValid()) - { - return; - } - - int Received = 0; - char Buffer[256]; - while ((Received = recv(m_Socket, Buffer, sizeof(Buffer), 0)) > 0) - { - oData.append(Buffer, Received); - } -} - - - - diff --git a/source/OSSupport/BlockingTCPLink.h b/source/OSSupport/BlockingTCPLink.h deleted file mode 100644 index cb5f9e3f4..000000000 --- a/source/OSSupport/BlockingTCPLink.h +++ /dev/null @@ -1,28 +0,0 @@ - -#pragma once - -#include "Socket.h" - - - - - -class cBlockingTCPLink // tolua_export -{ // tolua_export -public: // tolua_export - cBlockingTCPLink(void); // tolua_export - ~cBlockingTCPLink(); // tolua_export - - bool Connect( const char* a_Address, unsigned int a_Port ); // tolua_export - int Send( char* a_Data, unsigned int a_Size, int a_Flags = 0 ); // tolua_export - int SendMessage( const char* a_Message, int a_Flags = 0 ); // tolua_export - void CloseSocket(); // tolua_export - void ReceiveData(AString & oData); // tolua_export -protected: - - cSocket m_Socket; -}; // tolua_export - - - - diff --git a/source/OSSupport/CriticalSection.cpp b/source/OSSupport/CriticalSection.cpp deleted file mode 100644 index bda97e3a1..000000000 --- a/source/OSSupport/CriticalSection.cpp +++ /dev/null @@ -1,188 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "IsThread.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCriticalSection: - -cCriticalSection::cCriticalSection() -{ - #ifdef _WIN32 - InitializeCriticalSection(&m_CriticalSection); - #else - pthread_mutexattr_init(&m_Attributes); - pthread_mutexattr_settype(&m_Attributes, PTHREAD_MUTEX_RECURSIVE); - - if (pthread_mutex_init(&m_CriticalSection, &m_Attributes) != 0) - { - LOGERROR("Could not initialize Critical Section!"); - } - #endif - - #ifdef _DEBUG - m_IsLocked = 0; - #endif // _DEBUG -} - - - - - -cCriticalSection::~cCriticalSection() -{ - #ifdef _WIN32 - DeleteCriticalSection(&m_CriticalSection); - #else - if (pthread_mutex_destroy(&m_CriticalSection) != 0) - { - LOGWARNING("Could not destroy Critical Section!"); - } - pthread_mutexattr_destroy(&m_Attributes); - #endif -} - - - - - -void cCriticalSection::Lock() -{ - #ifdef _WIN32 - EnterCriticalSection(&m_CriticalSection); - #else - pthread_mutex_lock(&m_CriticalSection); - #endif - - #ifdef _DEBUG - m_IsLocked += 1; - m_OwningThreadID = cIsThread::GetCurrentID(); - #endif // _DEBUG -} - - - - - -void cCriticalSection::Unlock() -{ - #ifdef _DEBUG - ASSERT(m_IsLocked > 0); - m_IsLocked -= 1; - #endif // _DEBUG - - #ifdef _WIN32 - LeaveCriticalSection(&m_CriticalSection); - #else - pthread_mutex_unlock(&m_CriticalSection); - #endif -} - - - - - -#ifdef _DEBUG -bool cCriticalSection::IsLocked(void) -{ - return (m_IsLocked > 0); -} - - - - - -bool cCriticalSection::IsLockedByCurrentThread(void) -{ - return ((m_IsLocked > 0) && (m_OwningThreadID == cIsThread::GetCurrentID())); -} -#endif // _DEBUG - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCSLock - -cCSLock::cCSLock(cCriticalSection * a_CS) - : m_CS(a_CS) - , m_IsLocked(false) -{ - Lock(); -} - - - - - -cCSLock::cCSLock(cCriticalSection & a_CS) - : m_CS(&a_CS) - , m_IsLocked(false) -{ - Lock(); -} - - - - - -cCSLock::~cCSLock() -{ - if (!m_IsLocked) - { - return; - } - Unlock(); -} - - - - - -void cCSLock::Lock(void) -{ - ASSERT(!m_IsLocked); - m_IsLocked = true; - m_CS->Lock(); -} - - - - - -void cCSLock::Unlock(void) -{ - ASSERT(m_IsLocked); - m_IsLocked = false; - m_CS->Unlock(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCSUnlock: - -cCSUnlock::cCSUnlock(cCSLock & a_Lock) : - m_Lock(a_Lock) -{ - m_Lock.Unlock(); -} - - - - - -cCSUnlock::~cCSUnlock() -{ - m_Lock.Lock(); -} - - - - diff --git a/source/OSSupport/CriticalSection.h b/source/OSSupport/CriticalSection.h deleted file mode 100644 index 1bfe81439..000000000 --- a/source/OSSupport/CriticalSection.h +++ /dev/null @@ -1,80 +0,0 @@ - -#pragma once - - - - - -class cCriticalSection -{ -public: - cCriticalSection(void); - ~cCriticalSection(); - - void Lock(void); - void Unlock(void); - - #ifdef _DEBUG - bool IsLocked(void); - bool IsLockedByCurrentThread(void); - #endif // _DEBUG - -private: - #ifdef _DEBUG - int m_IsLocked; // Number of times this CS is locked - unsigned long m_OwningThreadID; - #endif // _DEBUG - - #ifdef _WIN32 - CRITICAL_SECTION m_CriticalSection; - #else // _WIN32 - pthread_mutex_t m_CriticalSection; - pthread_mutexattr_t m_Attributes; - #endif // else _WIN32 -} ALIGN_8; - - - - -/// RAII for cCriticalSection - locks the CS on creation, unlocks on destruction -class cCSLock -{ - cCriticalSection * m_CS; - - // Unlike a cCriticalSection, this object should be used from a single thread, therefore access to m_IsLocked is not threadsafe - // In Windows, it is an error to call cCriticalSection::Unlock() multiple times if the lock is not held, - // therefore we need to check this value whether we are locked or not. - bool m_IsLocked; - -public: - cCSLock(cCriticalSection * a_CS); - cCSLock(cCriticalSection & a_CS); - ~cCSLock(); - - // Temporarily unlock or re-lock: - void Lock(void); - void Unlock(void); - -private: - DISALLOW_COPY_AND_ASSIGN(cCSLock); -} ; - - - - - -/// Temporary RAII unlock for a cCSLock. Useful for unlock-wait-relock scenarios -class cCSUnlock -{ - cCSLock & m_Lock; -public: - cCSUnlock(cCSLock & a_Lock); - ~cCSUnlock(); - -private: - DISALLOW_COPY_AND_ASSIGN(cCSUnlock); -} ; - - - - diff --git a/source/OSSupport/Event.cpp b/source/OSSupport/Event.cpp deleted file mode 100644 index cbacbba17..000000000 --- a/source/OSSupport/Event.cpp +++ /dev/null @@ -1,118 +0,0 @@ - -// Event.cpp - -// Implements the cEvent object representing an OS-specific synchronization primitive that can be waited-for -// Implemented as an Event on Win and as a 1-semaphore on *nix - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Event.h" - - - - - -cEvent::cEvent(void) -{ -#ifdef _WIN32 - m_Event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (m_Event == NULL) - { - LOGERROR("cEvent: cannot create event, GLE = %d. Aborting server.", GetLastError()); - abort(); - } -#else // *nix - m_bIsNamed = false; - m_Event = new sem_t; - if (sem_init(m_Event, 0, 0)) - { - // This path is used by MacOS, because it doesn't support unnamed semaphores. - delete m_Event; - m_bIsNamed = true; - - AString EventName; - Printf(EventName, "cEvent%p", this); - m_Event = sem_open(EventName.c_str(), O_CREAT, 777, 0 ); - if (m_Event == SEM_FAILED) - { - LOGERROR("cEvent: Cannot create event, errno = %i. Aborting server.", errno); - abort(); - } - // Unlink the semaphore immediately - it will continue to function but will not pollute the namespace - // We don't store the name, so can't call this in the destructor - if (sem_unlink(EventName.c_str()) != 0) - { - LOGWARN("ERROR: Could not unlink cEvent. (%i)", errno); - } - } -#endif // *nix -} - - - - - -cEvent::~cEvent() -{ -#ifdef _WIN32 - CloseHandle(m_Event); -#else - if (m_bIsNamed) - { - if (sem_close(m_Event) != 0) - { - LOGERROR("ERROR: Could not close cEvent. (%i)", errno); - } - } - else - { - sem_destroy(m_Event); - delete m_Event; - } -#endif -} - - - - - -void cEvent::Wait(void) -{ - #ifdef _WIN32 - DWORD res = WaitForSingleObject(m_Event, INFINITE); - if (res != WAIT_OBJECT_0) - { - LOGWARN("cEvent: waiting for the event failed: %d, GLE = %d. Continuing, but server may be unstable.", res, GetLastError()); - } - #else - int res = sem_wait(m_Event); - if (res != 0 ) - { - LOGWARN("cEvent: waiting for the event failed: %i, errno = %i. Continuing, but server may be unstable.", res, errno); - } - #endif -} - - - - - -void cEvent::Set(void) -{ - #ifdef _WIN32 - if (!SetEvent(m_Event)) - { - LOGWARN("cEvent: Could not set cEvent: GLE = %d", GetLastError()); - } - #else - int res = sem_post(m_Event); - if (res != 0) - { - LOGWARN("cEvent: Could not set cEvent: %i, errno = %d", res, errno); - } - #endif -} - - - - diff --git a/source/OSSupport/Event.h b/source/OSSupport/Event.h deleted file mode 100644 index 71f418c0c..000000000 --- a/source/OSSupport/Event.h +++ /dev/null @@ -1,47 +0,0 @@ - -// Event.h - -// Interfaces to the cEvent object representing an OS-specific synchronization primitive that can be waited-for -// Implemented as an Event on Win and as a 1-semaphore on *nix - - - - - -#pragma once -#ifndef CEVENT_H_INCLUDED -#define CEVENT_H_INCLUDED - - - - - -class cEvent -{ -public: - cEvent(void); - ~cEvent(); - - void Wait(void); - void Set (void); - -private: - - #ifdef _WIN32 - HANDLE m_Event; - #else - sem_t * m_Event; - bool m_bIsNamed; - #endif -} ; - - - - - - -#endif // CEVENT_H_INCLUDED - - - - diff --git a/source/OSSupport/File.cpp b/source/OSSupport/File.cpp deleted file mode 100644 index d2eea498a..000000000 --- a/source/OSSupport/File.cpp +++ /dev/null @@ -1,375 +0,0 @@ - -// cFile.cpp - -// Implements the cFile class providing an OS-independent abstraction of a file. - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "File.h" -#include - - - - - -cFile::cFile(void) : - #ifdef USE_STDIO_FILE - m_File(NULL) - #else - m_File(INVALID_HANDLE_VALUE) - #endif // USE_STDIO_FILE -{ - // Nothing needed yet -} - - - - - -cFile::cFile(const AString & iFileName, eMode iMode) : - #ifdef USE_STDIO_FILE - m_File(NULL) - #else - m_File(INVALID_HANDLE_VALUE) - #endif // USE_STDIO_FILE -{ - Open(iFileName, iMode); -} - - - - - -cFile::~cFile() -{ - if (IsOpen()) - { - Close(); - } -} - - - - - -bool cFile::Open(const AString & iFileName, eMode iMode) -{ - ASSERT(!IsOpen()); // You should close the file before opening another one - - if (IsOpen()) - { - Close(); - } - - const char * Mode = NULL; - switch (iMode) - { - case fmRead: Mode = "rb"; break; - case fmWrite: Mode = "wb"; break; - case fmReadWrite: Mode = "rb+"; break; - default: - { - ASSERT(!"Unhandled file mode"); - return false; - } - } - m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), Mode); - if ((m_File == NULL) && (iMode == fmReadWrite)) - { - // Fix for MS not following C spec, opening "a" mode files for writing at the end only - // The file open operation has been tried with "read update", fails if file not found - // So now we know either the file doesn't exist or we don't have rights, no need to worry about file contents. - // Simply re-open for read-writing, erasing existing contents: - m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), "wb+"); - } - return (m_File != NULL); -} - - - - - -void cFile::Close(void) -{ - if (!IsOpen()) - { - // Closing an unopened file is a legal nop - return; - } - - fclose(m_File); - m_File = NULL; -} - - - - - -bool cFile::IsOpen(void) const -{ - return (m_File != NULL); -} - - - - - -bool cFile::IsEOF(void) const -{ - ASSERT(IsOpen()); - - if (!IsOpen()) - { - // Unopened files behave as at EOF - return true; - } - - return (feof(m_File) != 0); -} - - - - - -int cFile::Read (void * iBuffer, int iNumBytes) -{ - ASSERT(IsOpen()); - - if (!IsOpen()) - { - return -1; - } - - return fread(iBuffer, 1, iNumBytes, m_File); // fread() returns the portion of Count parameter actually read, so we need to send iNumBytes as Count -} - - - - - -int cFile::Write(const void * iBuffer, int iNumBytes) -{ - ASSERT(IsOpen()); - - if (!IsOpen()) - { - return -1; - } - - int res = fwrite(iBuffer, 1, iNumBytes, m_File); // fwrite() returns the portion of Count parameter actually written, so we need to send iNumBytes as Count - return res; -} - - - - - -int cFile::Seek (int iPosition) -{ - ASSERT(IsOpen()); - - if (!IsOpen()) - { - return -1; - } - - if (fseek(m_File, iPosition, SEEK_SET) != 0) - { - return -1; - } - return ftell(m_File); -} - - - - - - -int cFile::Tell (void) const -{ - ASSERT(IsOpen()); - - if (!IsOpen()) - { - return -1; - } - - return ftell(m_File); -} - - - - - -int cFile::GetSize(void) const -{ - ASSERT(IsOpen()); - - if (!IsOpen()) - { - return -1; - } - - int CurPos = ftell(m_File); - if (CurPos < 0) - { - return -1; - } - if (fseek(m_File, 0, SEEK_END) != 0) - { - return -1; - } - int res = ftell(m_File); - if (fseek(m_File, CurPos, SEEK_SET) != 0) - { - return -1; - } - return res; -} - - - - - -int cFile::ReadRestOfFile(AString & a_Contents) -{ - ASSERT(IsOpen()); - - if (!IsOpen()) - { - return -1; - } - - int DataSize = GetSize() - Tell(); - - // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly - a_Contents.assign(DataSize, '\0'); - return Read((void *)a_Contents.data(), DataSize); -} - - - - - -bool cFile::Exists(const AString & a_FileName) -{ - cFile test(a_FileName, fmRead); - return test.IsOpen(); -} - - - - - -bool cFile::Delete(const AString & a_FileName) -{ - return (remove(a_FileName.c_str()) == 0); -} - - - - - -bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName) -{ - return (rename(a_OrigFileName.c_str(), a_NewFileName.c_str()) == 0); -} - - - - - -bool cFile::Copy(const AString & a_SrcFileName, const AString & a_DstFileName) -{ - #ifdef _WIN32 - return (CopyFile(a_SrcFileName.c_str(), a_DstFileName.c_str(), true) != 0); - #else - // Other OSs don't have a direct CopyFile equivalent, do it the harder way: - std::ifstream src(a_SrcFileName.c_str(), std::ios::binary); - std::ofstream dst(a_DstFileName.c_str(), std::ios::binary); - if (dst.good()) - { - dst << src.rdbuf(); - return true; - } - else - { - return false; - } - #endif -} - - - - - -bool cFile::IsFolder(const AString & a_Path) -{ - #ifdef _WIN32 - DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); - return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0)); - #else - struct stat st; - return ((stat(a_Path.c_str(), &st) == 0) && S_ISDIR(st.st_mode)); - #endif -} - - - - - -bool cFile::IsFile(const AString & a_Path) -{ - #ifdef _WIN32 - DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); - return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0)); - #else - struct stat st; - return ((stat(a_Path.c_str(), &st) == 0) && S_ISREG(st.st_mode)); - #endif -} - - - - - -int cFile::GetSize(const AString & a_FileName) -{ - struct stat st; - if (stat(a_FileName.c_str(), &st) == 0) - { - return st.st_size; - } - return -1; -} - - - - - -bool cFile::CreateFolder(const AString & a_FolderPath) -{ - #ifdef _WIN32 - return (CreateDirectory(a_FolderPath.c_str(), NULL) != 0); - #else - return (mkdir(a_FolderPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0); - #endif -} - - - - - -int cFile::Printf(const char * a_Fmt, ...) -{ - AString buf; - va_list args; - va_start(args, a_Fmt); - AppendVPrintf(buf, a_Fmt, args); - va_end(args); - return Write(buf.c_str(), buf.length()); -} - - - - diff --git a/source/OSSupport/File.h b/source/OSSupport/File.h deleted file mode 100644 index cfb3a2019..000000000 --- a/source/OSSupport/File.h +++ /dev/null @@ -1,138 +0,0 @@ - -// cFile.h - -// Interfaces to the cFile class providing an OS-independent abstraction of a file. - -/* -The object is optimized towards binary reads. -The object has no multithreading locks, don't use from multiple threads! -Usage: -1, Construct a cFile instance (no-param constructor) -2, Open a file using Open(), check return value for success -3, Read / write -4, Destroy the instance - --- OR -- - -1, Construct a cFile instance opening the file (filename-param constructor) -2, Check if the file was opened using IsOpen() -3, Read / write -4, Destroy the instance -*/ - - - - - -#pragma once - - - - - -#ifndef _WIN32 - #define USE_STDIO_FILE -#endif // _WIN32 - -// DEBUG: -#define USE_STDIO_FILE - - - - - -// tolua_begin - -class cFile -{ -public: - - // tolua_end - - #ifdef _WIN32 - static const char PathSeparator = '\\'; - #else - static const char PathSeparator = '/'; - #endif - - /// The mode in which to open the file - enum eMode - { - fmRead, // Read-only. If the file doesn't exist, object will not be valid - fmWrite, // Write-only. If the file already exists, it will be overwritten - fmReadWrite // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning - } ; - - /// Simple constructor - creates an unopened file object, use Open() to open / create a real file - cFile(void); - - /// Constructs and opens / creates the file specified, use IsOpen() to check for success - cFile(const AString & iFileName, eMode iMode); - - /// Auto-closes the file, if open - ~cFile(); - - bool Open(const AString & iFileName, eMode iMode); - void Close(void); - bool IsOpen(void) const; - bool IsEOF(void) const; - - /// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open - int Read (void * iBuffer, int iNumBytes); - - /// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open - int Write(const void * iBuffer, int iNumBytes); - - /// Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open - int Seek (int iPosition); - - /// Returns the current position (bytes from file start) or -1 for failure; asserts if not open - int Tell (void) const; - - /// Returns the size of file, in bytes, or -1 for failure; asserts if not open - int GetSize(void) const; - - /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error - int ReadRestOfFile(AString & a_Contents); - - // tolua_begin - - /// Returns true if the file specified exists - static bool Exists(const AString & a_FileName); - - /// Deletes a file, returns true if successful - static bool Delete(const AString & a_FileName); - - /// Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)! - static bool Rename(const AString & a_OrigPath, const AString & a_NewPath); - - /// Copies a file, returns true if successful. - static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName); - - /// Returns true if the specified path is a folder - static bool IsFolder(const AString & a_Path); - - /// Returns true if the specified path is a regular file - static bool IsFile(const AString & a_Path); - - /// Returns the size of the file, or a negative number on error - static int GetSize(const AString & a_FileName); - - /// Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute - static bool CreateFolder(const AString & a_FolderPath); - - // tolua_end - - int Printf(const char * a_Fmt, ...); - -private: - #ifdef USE_STDIO_FILE - FILE * m_File; - #else - HANDLE m_File; - #endif -} ; // tolua_export - - - - diff --git a/source/OSSupport/GZipFile.cpp b/source/OSSupport/GZipFile.cpp deleted file mode 100644 index cbf6be6c4..000000000 --- a/source/OSSupport/GZipFile.cpp +++ /dev/null @@ -1,107 +0,0 @@ - -// GZipFile.cpp - -// Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines - -#include "Globals.h" -#include "GZipFile.h" - - - - - -cGZipFile::cGZipFile(void) : - m_File(NULL) -{ -} - - - - - -cGZipFile::~cGZipFile() -{ - Close(); -} - - - - - -bool cGZipFile::Open(const AString & a_FileName, eMode a_Mode) -{ - if (m_File != NULL) - { - ASSERT(!"A file is already open in this object"); - return false; - } - m_File = gzopen(a_FileName.c_str(), (a_Mode == fmRead) ? "r" : "w"); - m_Mode = a_Mode; - return (m_File != NULL); -} - - - - - -void cGZipFile::Close(void) -{ - if (m_File != NULL) - { - gzclose(m_File); - m_File = NULL; - } -} - - - - - -int cGZipFile::ReadRestOfFile(AString & a_Contents) -{ - if (m_File == NULL) - { - ASSERT(!"No file has been opened"); - return -1; - } - - if (m_Mode != fmRead) - { - ASSERT(!"Bad file mode, cannot read"); - return -1; - } - - // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck! - int NumBytesRead = 0; - char Buffer[64 KiB]; - while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0) - { - a_Contents.append(Buffer, NumBytesRead); - } - return NumBytesRead; -} - - - - - -bool cGZipFile::Write(const char * a_Contents, int a_Size) -{ - if (m_File == NULL) - { - ASSERT(!"No file has been opened"); - return false; - } - - if (m_Mode != fmWrite) - { - ASSERT(!"Bad file mode, cannot write"); - return false; - } - - return (gzwrite(m_File, a_Contents, a_Size) != 0); -} - - - - diff --git a/source/OSSupport/GZipFile.h b/source/OSSupport/GZipFile.h deleted file mode 100644 index e5aa68afa..000000000 --- a/source/OSSupport/GZipFile.h +++ /dev/null @@ -1,52 +0,0 @@ - -// GZipFile.h - -// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines - - - - - -#pragma once - -#include "zlib.h" - - - - - -class cGZipFile -{ -public: - enum eMode - { - fmRead, // Read-only. If the file doesn't exist, object will not be valid - fmWrite, // Write-only. If the file already exists, it will be overwritten - } ; - - cGZipFile(void); - ~cGZipFile(); - - /// Opens the file. Returns true if successful. Fails if a file has already been opened through this object. - bool Open(const AString & a_FileName, eMode a_Mode); - - /// Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode - void Close(void); - - /// Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error - int ReadRestOfFile(AString & a_Contents); - - /// Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported. - bool Write(const AString & a_Contents) { return Write(a_Contents.data(), (int)(a_Contents.size())); } - - bool Write(const char * a_Data, int a_Size); - -protected: - gzFile m_File; - eMode m_Mode; -} ; - - - - - diff --git a/source/OSSupport/IsThread.cpp b/source/OSSupport/IsThread.cpp deleted file mode 100644 index 4da9f9949..000000000 --- a/source/OSSupport/IsThread.cpp +++ /dev/null @@ -1,172 +0,0 @@ - -// IsThread.cpp - -// Implements the cIsThread class representing an OS-independent wrapper for a class that implements a thread. -// This class will eventually suupersede the old cThread class - -#include "Globals.h" - -#include "IsThread.h" - - - - - -// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here: -#if defined(_MSC_VER) && defined(_DEBUG) -// -// Usage: SetThreadName (-1, "MainThread"); -// - -static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) -{ - struct - { - DWORD dwType; // must be 0x1000 - LPCSTR szName; // pointer to name (in user addr space) - DWORD dwThreadID; // thread ID (-1=caller thread) - DWORD dwFlags; // reserved for future use, must be zero - } info; - - info.dwType = 0x1000; - info.szName = szThreadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - __try - { - RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info); - } - __except(EXCEPTION_CONTINUE_EXECUTION) - { - } -} -#endif // _MSC_VER && _DEBUG - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cIsThread: - -cIsThread::cIsThread(const AString & iThreadName) : - m_ThreadName(iThreadName), - m_ShouldTerminate(false), - m_Handle(NULL_HANDLE) -{ -} - - - - - -cIsThread::~cIsThread() -{ - m_ShouldTerminate = true; - Wait(); -} - - - - - -bool cIsThread::Start(void) -{ - ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread? - #ifdef _WIN32 - // Create the thread suspended, so that the mHandle variable is valid in the thread procedure - DWORD ThreadID = 0; - m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID); - if (m_Handle == NULL) - { - LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError()); - return false; - } - ResumeThread(m_Handle); - - #if defined(_DEBUG) && defined(_MSC_VER) - // Thread naming is available only in MSVC - if (!m_ThreadName.empty()) - { - SetThreadName(ThreadID, m_ThreadName.c_str()); - } - #endif // _DEBUG and _MSC_VER - - #else // _WIN32 - if (pthread_create(&m_Handle, NULL, thrExecute, this)) - { - LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str()); - return false; - } - #endif // else _WIN32 - - return true; -} - - - - - -void cIsThread::Stop(void) -{ - if (m_Handle == NULL_HANDLE) - { - return; - } - m_ShouldTerminate = true; - Wait(); -} - - - - - -bool cIsThread::Wait(void) -{ - if (m_Handle == NULL) - { - return true; - } - - #ifdef LOGD // ProtoProxy doesn't have LOGD - LOGD("Waiting for thread %s to finish", m_ThreadName.c_str()); - #endif // LOGD - - #ifdef _WIN32 - int res = WaitForSingleObject(m_Handle, INFINITE); - m_Handle = NULL; - - #ifdef LOGD // ProtoProxy doesn't have LOGD - LOGD("Thread %s finished", m_ThreadName.c_str()); - #endif // LOGD - - return (res == WAIT_OBJECT_0); - #else // _WIN32 - int res = pthread_join(m_Handle, NULL); - m_Handle = NULL; - - #ifdef LOGD // ProtoProxy doesn't have LOGD - LOGD("Thread %s finished", m_ThreadName.c_str()); - #endif // LOGD - - return (res == 0); - #endif // else _WIN32 -} - - - - - -unsigned long cIsThread::GetCurrentID(void) -{ - #ifdef _WIN32 - return (unsigned long) GetCurrentThreadId(); - #else - return (unsigned long) pthread_self(); - #endif -} - - - - diff --git a/source/OSSupport/IsThread.h b/source/OSSupport/IsThread.h deleted file mode 100644 index b8784ea33..000000000 --- a/source/OSSupport/IsThread.h +++ /dev/null @@ -1,100 +0,0 @@ - -// IsThread.h - -// Interfaces to the cIsThread class representing an OS-independent wrapper for a class that implements a thread. -// This class will eventually suupersede the old cThread class - -/* -Usage: -To have a new thread, declare a class descending from cIsClass. -Then override its Execute() method to provide your thread processing. -In the descending class' constructor call the Start() method to start the thread once you're finished with initialization. -*/ - - - - - -#pragma once -#ifndef CISTHREAD_H_INCLUDED -#define CISTHREAD_H_INCLUDED - - - - - -class cIsThread -{ -protected: - /// This is the main thread entrypoint - virtual void Execute(void) = 0; - - /// The overriden Execute() method should check this value periodically and terminate if this is true - volatile bool m_ShouldTerminate; - -public: - cIsThread(const AString & iThreadName); - ~cIsThread(); - - /// Starts the thread; returns without waiting for the actual start - bool Start(void); - - /// Signals the thread to terminate and waits until it's finished - void Stop(void); - - /// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag - bool Wait(void); - - /// Returns the OS-dependent thread ID for the caller's thread - static unsigned long GetCurrentID(void); - -protected: - AString m_ThreadName; - - // Value used for "no handle": - #ifdef _WIN32 - #define NULL_HANDLE NULL - #else - #define NULL_HANDLE 0 - #endif - - #ifdef _WIN32 - - HANDLE m_Handle; - - static DWORD_PTR __stdcall thrExecute(LPVOID a_Param) - { - // Create a window so that the thread can be identified by 3rd party tools: - HWND IdentificationWnd = CreateWindow("STATIC", ((cIsThread *)a_Param)->m_ThreadName.c_str(), 0, 0, 0, 0, WS_OVERLAPPED, NULL, NULL, NULL, NULL); - - // Run the thread: - ((cIsThread *)a_Param)->Execute(); - - // Destroy the identification window: - DestroyWindow(IdentificationWnd); - - return 0; - } - - #else // _WIN32 - - pthread_t m_Handle; - - static void * thrExecute(void * a_Param) - { - ((cIsThread *)a_Param)->Execute(); - return NULL; - } - - #endif // else _WIN32 -} ; - - - - - -#endif // CISTHREAD_H_INCLUDED - - - - diff --git a/source/OSSupport/ListenThread.cpp b/source/OSSupport/ListenThread.cpp deleted file mode 100644 index ba3198764..000000000 --- a/source/OSSupport/ListenThread.cpp +++ /dev/null @@ -1,238 +0,0 @@ - -// ListenThread.cpp - -// Implements the cListenThread class representing the thread that listens for client connections - -#include "Globals.h" -#include "ListenThread.h" - - - - - -cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) : - super(Printf("ListenThread %s", a_ServiceName.c_str())), - m_Callback(a_Callback), - m_Family(a_Family), - m_ShouldReuseAddr(false), - m_ServiceName(a_ServiceName) -{ -} - - - - - -cListenThread::~cListenThread() -{ - Stop(); -} - - - - - -bool cListenThread::Initialize(const AString & a_PortsString) -{ - ASSERT(m_Sockets.empty()); // Not yet started - - if (!CreateSockets(a_PortsString)) - { - return false; - } - - return true; -} - - - - - -bool cListenThread::Start(void) -{ - if (m_Sockets.empty()) - { - // There are no sockets listening, either forgotten to initialize or the user specified no listening ports - // Report as successful, though - return true; - } - return super::Start(); -} - - - - - -void cListenThread::Stop(void) -{ - if (m_Sockets.empty()) - { - // No sockets means no thread was running in the first place - return; - } - - m_ShouldTerminate = true; - - // Close one socket to wake the thread up from the select() call - m_Sockets[0].CloseSocket(); - - // Wait for the thread to finish - super::Wait(); - - // Close all the listening sockets: - for (cSockets::iterator itr = m_Sockets.begin() + 1, end = m_Sockets.end(); itr != end; ++itr) - { - itr->CloseSocket(); - } // for itr - m_Sockets[] - m_Sockets.clear(); -} - - - - - -void cListenThread::SetReuseAddr(bool a_Reuse) -{ - ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet - - m_ShouldReuseAddr = a_Reuse; -} - - - - - -bool cListenThread::CreateSockets(const AString & a_PortsString) -{ - AStringVector Ports = StringSplitAndTrim(a_PortsString, ","); - - if (Ports.empty()) - { - return false; - } - - AString FamilyStr = m_ServiceName; - switch (m_Family) - { - case cSocket::IPv4: FamilyStr.append(" IPv4"); break; - case cSocket::IPv6: FamilyStr.append(" IPv6"); break; - default: - { - ASSERT(!"Unknown address family"); - break; - } - } - - for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr) - { - int Port = atoi(itr->c_str()); - if ((Port <= 0) || (Port > 65535)) - { - LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str()); - continue; - } - m_Sockets.push_back(cSocket::CreateSocket(m_Family)); - if (!m_Sockets.back().IsValid()) - { - LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - m_Sockets.pop_back(); - continue; - } - - if (m_ShouldReuseAddr) - { - if (!m_Sockets.back().SetReuseAddress()) - { - LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - } - } - - // Bind to port: - bool res = false; - switch (m_Family) - { - case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break; - case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break; - default: - { - ASSERT(!"Unknown address family"); - res = false; - } - } - if (!res) - { - LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - m_Sockets.pop_back(); - continue; - } - - if (!m_Sockets.back().Listen()) - { - LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - m_Sockets.pop_back(); - continue; - } - - LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port); - } // for itr - Ports[] - - return !(m_Sockets.empty()); -} - - - - - -void cListenThread::Execute(void) -{ - if (m_Sockets.empty()) - { - LOGD("Empty cListenThread, ending thread now."); - return; - } - - // Find the highest socket number: - cSocket::xSocket Highest = m_Sockets[0].GetSocket(); - for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) - { - if (itr->GetSocket() > Highest) - { - Highest = itr->GetSocket(); - } - } // for itr - m_Sockets[] - - while (!m_ShouldTerminate) - { - // Put all sockets into a FD set: - fd_set fdRead; - FD_ZERO(&fdRead); - for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) - { - FD_SET(itr->GetSocket(), &fdRead); - } // for itr - m_Sockets[] - - timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait: - tv.tv_sec = 1; - tv.tv_usec = 0; - if (select(Highest + 1, &fdRead, NULL, NULL, &tv) == -1) - { - LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str()); - continue; - } - for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) - { - if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead)) - { - cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6(); - if (Client.IsValid()) - { - m_Callback.OnConnectionAccepted(Client); - } - } - } // for itr - m_Sockets[] - } // while (!m_ShouldTerminate) -} - - - - diff --git a/source/OSSupport/ListenThread.h b/source/OSSupport/ListenThread.h deleted file mode 100644 index 4e337d814..000000000 --- a/source/OSSupport/ListenThread.h +++ /dev/null @@ -1,83 +0,0 @@ - -// ListenThread.h - -// Declares the cListenThread class representing the thread that listens for client connections - - - - - -#pragma once - -#include "IsThread.h" -#include "Socket.h" - - - - - -// fwd: -class cServer; - - - - - -class cListenThread : - public cIsThread -{ - typedef cIsThread super; - -public: - /// Used as the callback for connection events - class cCallback - { - public: - /// This callback is called whenever a socket connection is accepted - virtual void OnConnectionAccepted(cSocket & a_Socket) = 0; - } ; - - cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName = ""); - ~cListenThread(); - - /// Creates all the sockets, returns trus if successful, false if not. - bool Initialize(const AString & a_PortsString); - - bool Start(void); - - void Stop(void); - - /// Call before Initialize() to set the "reuse" flag on the sockets - void SetReuseAddr(bool a_Reuse = true); - -protected: - typedef std::vector cSockets; - - /// The callback which to notify of incoming connections - cCallback & m_Callback; - - /// Socket address family to use - cSocket::eFamily m_Family; - - /// Sockets that are being monitored - cSockets m_Sockets; - - /// If set to true, the SO_REUSEADDR socket option is set to true - bool m_ShouldReuseAddr; - - /// Name of the service that's listening on the ports; for logging purposes only - AString m_ServiceName; - - - /** Fills in m_Sockets with individual sockets, each for one port specified in a_PortsString. - Returns true if successful and at least one socket has been created - */ - bool CreateSockets(const AString & a_PortsString); - - // cIsThread override: - virtual void Execute(void) override; -} ; - - - - diff --git a/source/OSSupport/Semaphore.cpp b/source/OSSupport/Semaphore.cpp deleted file mode 100644 index 468de6858..000000000 --- a/source/OSSupport/Semaphore.cpp +++ /dev/null @@ -1,91 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - - - - - -cSemaphore::cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount /* = 0 */ ) -#ifndef _WIN32 - : m_bNamed( false ) -#endif -{ -#ifndef _WIN32 - (void)a_MaxCount; - m_Handle = new sem_t; - if (sem_init( (sem_t*)m_Handle, 0, 0)) - { - LOG("WARNING cSemaphore: Could not create unnamed semaphore, fallback to named."); - delete (sem_t*)m_Handle; // named semaphores return their own address - m_bNamed = true; - - AString Name; - Printf(Name, "cSemaphore%p", this ); - m_Handle = sem_open(Name.c_str(), O_CREAT, 777, a_InitialCount); - if( m_Handle == SEM_FAILED ) - { - LOG("ERROR: Could not create Semaphore. (%i)", errno ); - } - else - { - if( sem_unlink(Name.c_str()) != 0 ) - { - LOG("ERROR: Could not unlink cSemaphore. (%i)", errno); - } - } - } -#else - m_Handle = CreateSemaphore( - NULL, // security attribute - a_InitialCount, // initial count - a_MaxCount, // maximum count - 0 // name (optional) - ); -#endif -} - -cSemaphore::~cSemaphore() -{ -#ifdef _WIN32 - CloseHandle( m_Handle ); -#else - if( m_bNamed ) - { - if( sem_close( (sem_t*)m_Handle ) != 0 ) - { - LOG("ERROR: Could not close cSemaphore. (%i)", errno); - } - } - else - { - sem_destroy( (sem_t*)m_Handle ); - delete (sem_t*)m_Handle; - } - m_Handle = 0; - -#endif -} - -void cSemaphore::Wait() -{ -#ifndef _WIN32 - if( sem_wait( (sem_t*)m_Handle ) != 0) - { - LOG("ERROR: Could not wait for cSemaphore. (%i)", errno); - } -#else - WaitForSingleObject( m_Handle, INFINITE); -#endif -} - -void cSemaphore::Signal() -{ -#ifndef _WIN32 - if( sem_post( (sem_t*)m_Handle ) != 0 ) - { - LOG("ERROR: Could not signal cSemaphore. (%i)", errno); - } -#else - ReleaseSemaphore( m_Handle, 1, NULL ); -#endif -} diff --git a/source/OSSupport/Semaphore.h b/source/OSSupport/Semaphore.h deleted file mode 100644 index fbe8907f1..000000000 --- a/source/OSSupport/Semaphore.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -class cSemaphore -{ -public: - cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount = 0 ); - ~cSemaphore(); - - void Wait(); - void Signal(); -private: - void* m_Handle; // HANDLE pointer - -#ifndef _WIN32 - bool m_bNamed; -#endif -}; diff --git a/source/OSSupport/Sleep.cpp b/source/OSSupport/Sleep.cpp deleted file mode 100644 index 70fb06b40..000000000 --- a/source/OSSupport/Sleep.cpp +++ /dev/null @@ -1,19 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#ifndef _WIN32 - #include -#endif - - - - - -void cSleep::MilliSleep( unsigned int a_MilliSeconds ) -{ -#ifdef _WIN32 - Sleep(a_MilliSeconds); // Don't tick too much -#else - usleep(a_MilliSeconds*1000); -#endif -} diff --git a/source/OSSupport/Sleep.h b/source/OSSupport/Sleep.h deleted file mode 100644 index 5298c15da..000000000 --- a/source/OSSupport/Sleep.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -class cSleep -{ -public: - static void MilliSleep( unsigned int a_MilliSeconds ); -}; \ No newline at end of file diff --git a/source/OSSupport/Socket.cpp b/source/OSSupport/Socket.cpp deleted file mode 100644 index 48b5d704d..000000000 --- a/source/OSSupport/Socket.cpp +++ /dev/null @@ -1,396 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Socket.h" - -#ifndef _WIN32 - #include - #include - #include //inet_ntoa() -#else - #define socklen_t int -#endif - - - - - -cSocket::cSocket(xSocket a_Socket) - : m_Socket(a_Socket) -{ -} - - - - - -cSocket::~cSocket() -{ - // Do NOT close the socket; this class is an API wrapper, not a RAII! -} - - - - - -cSocket::operator cSocket::xSocket() const -{ - return m_Socket; -} - - - - - -cSocket::xSocket cSocket::GetSocket() const -{ - return m_Socket; -} - - - - - -bool cSocket::IsValidSocket(cSocket::xSocket a_Socket) -{ - #ifdef _WIN32 - return (a_Socket != INVALID_SOCKET); - #else // _WIN32 - return (a_Socket >= 0); - #endif // else _WIN32 -} - - - - - -void cSocket::CloseSocket() -{ - #ifdef _WIN32 - - closesocket(m_Socket); - - #else // _WIN32 - - if (shutdown(m_Socket, SHUT_RDWR) != 0)//SD_BOTH); - { - LOGWARN("Error on shutting down socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); - } - if (close(m_Socket) != 0) - { - LOGWARN("Error closing socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); - } - - #endif // else _WIN32 - - // Invalidate the socket so that this object can be re-used for another connection - m_Socket = INVALID_SOCKET; -} - - - - - -AString cSocket::GetErrorString( int a_ErrNo ) -{ - char buffer[ 1024 ]; - AString Out; - - #ifdef _WIN32 - - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, a_ErrNo, 0, buffer, ARRAYCOUNT(buffer), NULL); - Printf(Out, "%d: %s", a_ErrNo, buffer); - if (!Out.empty() && (Out[Out.length() - 1] == '\n')) - { - Out.erase(Out.length() - 2); - } - return Out; - - #else // _WIN32 - - // According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r(): - - #if ( _GNU_SOURCE ) && !defined(ANDROID_NDK) // GNU version of strerror_r() - - char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) ); - if( res != NULL ) - { - Printf(Out, "%d: %s", a_ErrNo, res); - return Out; - } - - #else // XSI version of strerror_r(): - - int res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) ); - if( res == 0 ) - { - Printf(Out, "%d: %s", a_ErrNo, buffer); - return Out; - } - - #endif // strerror_r() version - - else - { - Printf(Out, "Error %d while getting error string for error #%d!", errno, a_ErrNo); - return Out; - } - - #endif // else _WIN32 -} - - - - -int cSocket::GetLastError() -{ -#ifdef _WIN32 - return WSAGetLastError(); -#else - return errno; -#endif -} - - - - - -bool cSocket::SetReuseAddress(void) -{ - #if defined(_WIN32) || defined(ANDROID_NDK) - char yes = 1; - #else - int yes = 1; - #endif - return (setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == 0); -} - - - - - -int cSocket::WSAStartup() -{ -#ifdef _WIN32 - WSADATA wsaData; - memset(&wsaData, 0, sizeof(wsaData)); - return ::WSAStartup(MAKEWORD(2, 2),&wsaData); -#else - return 0; -#endif -} - - - - - -cSocket cSocket::CreateSocket(eFamily a_Family) -{ - return socket((int)a_Family, SOCK_STREAM, 0); -} - - - - - -bool cSocket::BindToAnyIPv4(unsigned short a_Port) -{ - sockaddr_in local; - memset(&local, 0, sizeof(local)); - - local.sin_family = AF_INET; - local.sin_port = htons((u_short)a_Port); - - return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0); -} - - - - - -bool cSocket::BindToAnyIPv6(unsigned short a_Port) -{ - // Cannot use socckaddr_in6, because it is not defined in the default VS2008 SDK - // Must jump through hoops here - - sockaddr_in6 local; - memset(&local, 0, sizeof(local)); - - local.sin6_family = AF_INET6; - local.sin6_port = htons((u_short)a_Port); - - return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0); -} - - - - - -bool cSocket::BindToLocalhostIPv4(unsigned short a_Port) -{ - sockaddr_in local; - memset(&local, 0, sizeof(local)); - - local.sin_family = AF_INET;; - local.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - local.sin_port = htons((u_short)a_Port); - - return (bind(m_Socket, (sockaddr*)&local, sizeof(local)) == 0); -} - - - - - -bool cSocket::Listen(int a_Backlog) -{ - return (listen(m_Socket, a_Backlog) == 0); -} - - - - - -cSocket cSocket::AcceptIPv4(void) -{ - sockaddr_in from; - socklen_t fromlen = sizeof(from); - - cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen); - - if (SClient.IsValid() && (from.sin_addr.s_addr != 0)) // Get IP in string form - { - SClient.m_IPString = inet_ntoa(from.sin_addr); - } - return SClient; -} - - - - - -cSocket cSocket::AcceptIPv6(void) -{ - sockaddr_in6 from; - socklen_t fromlen = sizeof(from); - - cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen); - - // Get IP in string form: - if (SClient.IsValid()) - { - #if defined(_WIN32) - // Windows XP doesn't have inet_ntop, so we need to improvise. And MSVC has different headers than GCC - #ifdef _MSC_VER - // MSVC version - Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x", - from.sin6_addr.u.Word[0], - from.sin6_addr.u.Word[1], - from.sin6_addr.u.Word[2], - from.sin6_addr.u.Word[3], - from.sin6_addr.u.Word[4], - from.sin6_addr.u.Word[5], - from.sin6_addr.u.Word[6], - from.sin6_addr.u.Word[7] - ); - #else // _MSC_VER - // MinGW - Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x", - from.sin6_addr.s6_addr16[0], - from.sin6_addr.s6_addr16[1], - from.sin6_addr.s6_addr16[2], - from.sin6_addr.s6_addr16[3], - from.sin6_addr.s6_addr16[4], - from.sin6_addr.s6_addr16[5], - from.sin6_addr.s6_addr16[6], - from.sin6_addr.s6_addr16[7] - ); - #endif // else _MSC_VER - #else - char buffer[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &(from.sin6_addr), buffer, sizeof(buffer)); - SClient.m_IPString.assign(buffer); - #endif // _WIN32 - } - return SClient; -} - - - - - -bool cSocket::ConnectToLocalhostIPv4(unsigned short a_Port) -{ - sockaddr_in server; - server.sin_family = AF_INET; - server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - server.sin_port = htons(a_Port); - return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0); -} - - - - - -bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port) -{ - // First try IP Address string to hostent conversion, because it's faster - unsigned long addr = inet_addr(a_HostNameOrAddr.c_str()); - hostent * hp = gethostbyaddr((char*)&addr, sizeof(addr), AF_INET); - if (hp == NULL) - { - // It is not an IP Address string, but rather a regular hostname, resolve: - hp = gethostbyname(a_HostNameOrAddr.c_str()); - if (hp == NULL) - { - LOGWARN("cTCPLink: Could not resolve hostname \"%s\"", a_HostNameOrAddr.c_str()); - CloseSocket(); - return false; - } - } - - sockaddr_in server; - server.sin_addr.s_addr = *((unsigned long*)hp->h_addr); - server.sin_family = AF_INET; - server.sin_port = htons( (unsigned short)a_Port ); - return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0); -} - - - - - -int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags) -{ - return recv(m_Socket, a_Buffer, a_Length, a_Flags); -} - - - - - -int cSocket::Send(const char * a_Buffer, unsigned int a_Length) -{ - return send(m_Socket, a_Buffer, a_Length, 0); -} - - - - - -unsigned short cSocket::GetPort(void) const -{ - ASSERT(IsValid()); - - sockaddr_in Addr; - socklen_t AddrSize = sizeof(Addr); - if (getsockname(m_Socket, (sockaddr *)&Addr, &AddrSize) != 0) - { - return 0; - } - return ntohs(Addr.sin_port); -} - - - - diff --git a/source/OSSupport/Socket.h b/source/OSSupport/Socket.h deleted file mode 100644 index 34f09cc74..000000000 --- a/source/OSSupport/Socket.h +++ /dev/null @@ -1,101 +0,0 @@ - -#pragma once - - - - - -class cSocket -{ -public: - enum eFamily - { - IPv4 = AF_INET, - IPv6 = AF_INET6, - } ; - -#ifdef _WIN32 - typedef SOCKET xSocket; -#else - typedef int xSocket; - static const int INVALID_SOCKET = -1; -#endif - - cSocket(void) : m_Socket(INVALID_SOCKET) {} - cSocket(xSocket a_Socket); - ~cSocket(); - - bool IsValid(void) const { return IsValidSocket(m_Socket); } - void CloseSocket(void); - - operator xSocket(void) const; - xSocket GetSocket(void) const; - - bool operator == (const cSocket & a_Other) {return m_Socket == a_Other.m_Socket; } - - void SetSocket(xSocket a_Socket); - - /// Sets the address-reuse socket flag; returns true on success - bool SetReuseAddress(void); - - static int WSAStartup(void); - - static AString GetErrorString(int a_ErrNo); - static int GetLastError(); - static AString GetLastErrorString(void) - { - return GetErrorString(GetLastError()); - } - - /// Creates a new socket of the specified address family - static cSocket CreateSocket(eFamily a_Family); - - inline static bool IsSocketError(int a_ReturnedValue) - { - #ifdef _WIN32 - return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0); - #else - return (a_ReturnedValue <= 0); - #endif - } - - static bool IsValidSocket(xSocket a_Socket); - - static const unsigned short ANY_PORT = 0; // When given to Bind() functions, they will find a free port - static const int DEFAULT_BACKLOG = 10; - - /// Binds to the specified port on "any" interface (0.0.0.0). Returns true if successful. - bool BindToAnyIPv4(unsigned short a_Port); - - /// Binds to the specified port on "any" interface (::/128). Returns true if successful. - bool BindToAnyIPv6(unsigned short a_Port); - - /// Binds to the specified port on localhost interface (127.0.0.1) through IPv4. Returns true if successful. - bool BindToLocalhostIPv4(unsigned short a_Port); - - /// Sets the socket to listen for incoming connections. Returns true if successful. - bool Listen(int a_Backlog = DEFAULT_BACKLOG); - - /// Accepts an IPv4 incoming connection. Blocks if none available. - cSocket AcceptIPv4(void); - - /// Accepts an IPv6 incoming connection. Blocks if none available. - cSocket AcceptIPv6(void); - - /// Connects to a localhost socket on the specified port using IPv4; returns true if successful. - bool ConnectToLocalhostIPv4(unsigned short a_Port); - - /// Connects to the specified host or string IP address and port, using IPv4. Returns true if successful. - bool ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port); - - int Receive(char * a_Buffer, unsigned int a_Length, unsigned int a_Flags); - int Send (const char * a_Buffer, unsigned int a_Length); - - unsigned short GetPort(void) const; // Returns 0 on failure - - const AString & GetIPString(void) const { return m_IPString; } - -private: - xSocket m_Socket; - AString m_IPString; -}; \ No newline at end of file diff --git a/source/OSSupport/SocketThreads.cpp b/source/OSSupport/SocketThreads.cpp deleted file mode 100644 index 3e505616c..000000000 --- a/source/OSSupport/SocketThreads.cpp +++ /dev/null @@ -1,675 +0,0 @@ - -// cSocketThreads.cpp - -// Implements the cSocketThreads class representing the heart of MCS's client networking. -// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support -// For more detail, see http://forum.mc-server.org/showthread.php?tid=327 - -#include "Globals.h" -#include "SocketThreads.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSocketThreads: - -cSocketThreads::cSocketThreads(void) -{ -} - - - - - -cSocketThreads::~cSocketThreads() -{ - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - delete *itr; - } // for itr - m_Threads[] - m_Threads.clear(); -} - - - - - - -bool cSocketThreads::AddClient(const cSocket & a_Socket, cCallback * a_Client) -{ - // Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client - - // Try to add to existing threads: - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->IsValid() && (*itr)->HasEmptySlot()) - { - (*itr)->AddClient(a_Socket, a_Client); - return true; - } - } - - // No thread has free space, create a new one: - LOGD("Creating a new cSocketThread (currently have %d)", m_Threads.size()); - cSocketThread * Thread = new cSocketThread(this); - if (!Thread->Start()) - { - // There was an error launching the thread (but it was already logged along with the reason) - LOGERROR("A new cSocketThread failed to start"); - delete Thread; - return false; - } - Thread->AddClient(a_Socket, a_Client); - m_Threads.push_back(Thread); - return true; -} - - - - - -/* -void cSocketThreads::RemoveClient(const cSocket * a_Socket) -{ - // Remove the socket (and associated client) from processing - - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->RemoveSocket(a_Socket)) - { - return; - } - } // for itr - m_Threads[] - - // Cannot assert here, this may actually happen legally, since cClientHandle has to clean up the socket and it may have already closed in the meantime - // ASSERT(!"Removing an unknown socket"); -} -*/ - - - - - -void cSocketThreads::RemoveClient(const cCallback * a_Client) -{ - // Remove the associated socket and the client from processing - - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->RemoveClient(a_Client)) - { - return; - } - } // for itr - m_Threads[] - - ASSERT(!"Removing an unknown client"); -} - - - - - -void cSocketThreads::NotifyWrite(const cCallback * a_Client) -{ - // Notifies the thread responsible for a_Client that the client has something to write - - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->NotifyWrite(a_Client)) - { - return; - } - } // for itr - m_Threads[] - - // Cannot assert - this normally happens if a client disconnects and has pending packets, the cServer::cNotifyWriteThread will call this on invalid clients too - // ASSERT(!"Notifying write to an unknown client"); -} - - - - - -void cSocketThreads::Write(const cCallback * a_Client, const AString & a_Data) -{ - // Puts a_Data into outgoing data queue for a_Client - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->Write(a_Client, a_Data)) - { - return; - } - } // for itr - m_Threads[] - - // This may be perfectly legal, if the socket has been destroyed and the client is finishing up - // ASSERT(!"Writing to an unknown socket"); -} - - - - - -/// Stops reading from the socket - when this call returns, no more calls to the callbacks are made -void cSocketThreads::StopReading(const cCallback * a_Client) -{ - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->StopReading(a_Client)) - { - return; - } - } // for itr - m_Threads[] - - // Cannot assert, this normally happens if the socket is closed before the client deinitializes - // ASSERT(!"Stopping reading on an unknown client"); -} - - - - - -/// Queues the socket for closing, as soon as its outgoing data is sent -void cSocketThreads::QueueClose(const cCallback * a_Client) -{ - LOGD("QueueClose(client %p)", a_Client); - - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->QueueClose(a_Client)) - { - return; - } - } // for itr - m_Threads[] - - ASSERT(!"Queueing close of an unknown client"); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cSocketThreads::cSocketThread: - -cSocketThreads::cSocketThread::cSocketThread(cSocketThreads * a_Parent) : - cIsThread("cSocketThread"), - m_Parent(a_Parent), - m_NumSlots(0) -{ - // Nothing needed yet -} - - - - - -cSocketThreads::cSocketThread::~cSocketThread() -{ - m_ShouldTerminate = true; - - // Notify the thread: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("a", 1); - - // Wait for the thread to finish: - Wait(); - - // Close the control sockets: - m_ControlSocket1.CloseSocket(); - m_ControlSocket2.CloseSocket(); -} - - - - - -void cSocketThreads::cSocketThread::AddClient(const cSocket & a_Socket, cCallback * a_Client) -{ - ASSERT(m_NumSlots < MAX_SLOTS); // Use HasEmptySlot() to check before adding - - m_Slots[m_NumSlots].m_Client = a_Client; - m_Slots[m_NumSlots].m_Socket = a_Socket; - m_Slots[m_NumSlots].m_Outgoing.clear(); - m_Slots[m_NumSlots].m_ShouldClose = false; - m_Slots[m_NumSlots].m_ShouldCallClient = true; - m_NumSlots++; - - // Notify the thread of the change: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("a", 1); -} - - - - - -bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client) -{ - // Returns true if removed, false if not found - - if (m_NumSlots == 0) - { - return false; - } - - for (int i = m_NumSlots - 1; i >= 0 ; --i) - { - if (m_Slots[i].m_Client != a_Client) - { - continue; - } - - // Found, remove it: - m_Slots[i] = m_Slots[--m_NumSlots]; - - // Notify the thread of the change: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("r", 1); - return true; - } // for i - m_Slots[] - - // Not found - return false; -} - - - - - -bool cSocketThreads::cSocketThread::RemoveSocket(const cSocket * a_Socket) -{ - // Returns true if removed, false if not found - - for (int i = m_NumSlots - 1; i >= 0 ; --i) - { - if (m_Slots[i].m_Socket != *a_Socket) - { - continue; - } - - // Found, remove it: - m_Slots[i] = m_Slots[--m_NumSlots]; - - // Notify the thread of the change: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("r", 1); - return true; - } // for i - m_Slots[] - - // Not found - return false; -} - - - - - -bool cSocketThreads::cSocketThread::HasClient(const cCallback * a_Client) const -{ - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (m_Slots[i].m_Client == a_Client) - { - return true; - } - } // for i - m_Slots[] - return false; -} - - - - - -bool cSocketThreads::cSocketThread::HasSocket(const cSocket * a_Socket) const -{ - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (m_Slots[i].m_Socket == *a_Socket) - { - return true; - } - } // for i - m_Slots[] - return false; -} - - - - - -bool cSocketThreads::cSocketThread::NotifyWrite(const cCallback * a_Client) -{ - if (HasClient(a_Client)) - { - // Notify the thread that there's another packet in the queue: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("q", 1); - return true; - } - return false; -} - - - - - -bool cSocketThreads::cSocketThread::Write(const cCallback * a_Client, const AString & a_Data) -{ - // Returns true if socket handled by this thread - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (m_Slots[i].m_Client == a_Client) - { - m_Slots[i].m_Outgoing.append(a_Data); - - // Notify the thread that there's data in the queue: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("q", 1); - - return true; - } - } // for i - m_Slots[] - return false; -} - - - - - -bool cSocketThreads::cSocketThread::StopReading (const cCallback * a_Client) -{ - // Returns true if client handled by this thread - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (m_Slots[i].m_Client == a_Client) - { - m_Slots[i].m_ShouldCallClient = false; - return true; - } - } // for i - m_Slots[] - return false; -} - - - - - -bool cSocketThreads::cSocketThread::QueueClose(const cCallback * a_Client) -{ - // Returns true if socket handled by this thread - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (m_Slots[i].m_Client == a_Client) - { - m_Slots[i].m_ShouldClose = true; - - // Notify the thread that there's a close queued (in case its conditions are already met): - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("c", 1); - - return true; - } - } // for i - m_Slots[] - return false; -} - - - - - -bool cSocketThreads::cSocketThread::Start(void) -{ - // Create the control socket listener - m_ControlSocket2 = cSocket::CreateSocket(cSocket::IPv4); - if (!m_ControlSocket2.IsValid()) - { - LOGERROR("Cannot create a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - return false; - } - if (!m_ControlSocket2.BindToLocalhostIPv4(cSocket::ANY_PORT)) - { - LOGERROR("Cannot bind a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return false; - } - if (!m_ControlSocket2.Listen(1)) - { - LOGERROR("Cannot listen on a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return false; - } - if (m_ControlSocket2.GetPort() == 0) - { - LOGERROR("Cannot determine Control socket port (\"%s\"); conitnuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return false; - } - - // Start the thread - if (!super::Start()) - { - LOGERROR("Cannot start new cSocketThread"); - m_ControlSocket2.CloseSocket(); - return false; - } - - // Finish connecting the control socket by accepting connection from the thread's socket - cSocket tmp = m_ControlSocket2.AcceptIPv4(); - if (!tmp.IsValid()) - { - LOGERROR("Cannot link Control sockets for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return false; - } - m_ControlSocket2.CloseSocket(); - m_ControlSocket2 = tmp; - - return true; -} - - - - - -void cSocketThreads::cSocketThread::Execute(void) -{ - // Connect the "client" part of the Control socket: - m_ControlSocket1 = cSocket::CreateSocket(cSocket::IPv4); - ASSERT(m_ControlSocket2.GetPort() != 0); // We checked in the Start() method, but let's be sure - if (!m_ControlSocket1.ConnectToLocalhostIPv4(m_ControlSocket2.GetPort())) - { - LOGERROR("Cannot connect Control sockets for a cSocketThread (\"%s\"); continuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return; - } - - // The main thread loop: - while (!m_ShouldTerminate) - { - // Put all sockets into the Read set: - fd_set fdRead; - cSocket::xSocket Highest = m_ControlSocket1.GetSocket(); - - PrepareSet(&fdRead, Highest); - - // Wait for the sockets: - if (select(Highest + 1, &fdRead, NULL, NULL, NULL) == -1) - { - LOG("select(R) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str()); - continue; - } - - ReadFromSockets(&fdRead); - - // Test sockets for writing: - fd_set fdWrite; - Highest = m_ControlSocket1.GetSocket(); - PrepareSet(&fdWrite, Highest); - timeval Timeout; - Timeout.tv_sec = 0; - Timeout.tv_usec = 0; - if (select(Highest + 1, NULL, &fdWrite, NULL, &Timeout) == -1) - { - LOG("select(W) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str()); - continue; - } - - WriteToSockets(&fdWrite); - } // while (!mShouldTerminate) -} - - - - - -void cSocketThreads::cSocketThread::PrepareSet(fd_set * a_Set, cSocket::xSocket & a_Highest) -{ - FD_ZERO(a_Set); - FD_SET(m_ControlSocket1.GetSocket(), a_Set); - - cCSLock Lock(m_Parent->m_CS); - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (!m_Slots[i].m_Socket.IsValid()) - { - continue; - } - cSocket::xSocket s = m_Slots[i].m_Socket.GetSocket(); - FD_SET(s, a_Set); - if (s > a_Highest) - { - a_Highest = s; - } - } // for i - m_Slots[] -} - - - - - -void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read) -{ - // Read on available sockets: - - // Reset Control socket state: - if (FD_ISSET(m_ControlSocket1.GetSocket(), a_Read)) - { - char Dummy[128]; - m_ControlSocket1.Receive(Dummy, sizeof(Dummy), 0); - } - - // Read from clients: - cCSLock Lock(m_Parent->m_CS); - for (int i = m_NumSlots - 1; i >= 0; --i) - { - cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket(); - if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Read)) - { - continue; - } - char Buffer[1024]; - int Received = m_Slots[i].m_Socket.Receive(Buffer, ARRAYCOUNT(Buffer), 0); - if (Received == 0) - { - // The socket has been closed by the remote party, close our socket and let it be removed after we process all reading - m_Slots[i].m_Socket.CloseSocket(); - if (m_Slots[i].m_ShouldCallClient) - { - m_Slots[i].m_Client->SocketClosed(); - } - } - else if (Received > 0) - { - if (m_Slots[i].m_ShouldCallClient) - { - m_Slots[i].m_Client->DataReceived(Buffer, Received); - } - } - else - { - // The socket has encountered an error, close it and let it be removed after we process all reading - m_Slots[i].m_Socket.CloseSocket(); - if (m_Slots[i].m_ShouldCallClient) - { - m_Slots[i].m_Client->SocketClosed(); - } - } - } // for i - m_Slots[] -} - - - - - -void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write) -{ - // Write to available client sockets: - cCSLock Lock(m_Parent->m_CS); - for (int i = m_NumSlots - 1; i >= 0; --i) - { - cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket(); - if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Write)) - { - continue; - } - if (m_Slots[i].m_Outgoing.empty()) - { - // Request another chunk of outgoing data: - if (m_Slots[i].m_ShouldCallClient) - { - m_Slots[i].m_Client->GetOutgoingData(m_Slots[i].m_Outgoing); - } - if (m_Slots[i].m_Outgoing.empty()) - { - // Nothing ready - if (m_Slots[i].m_ShouldClose) - { - // Socket was queued for closing and there's no more data to send, close it now: - - // DEBUG - LOGD("Socket was queued for closing, closing now. Slot %d, client %p, socket %d", i, m_Slots[i].m_Client, m_Slots[i].m_Socket.GetSocket()); - - m_Slots[i].m_Socket.CloseSocket(); - // The slot must be freed actively by the client, using RemoveClient() - } - continue; - } - } // if (outgoing data is empty) - - int Sent = m_Slots[i].m_Socket.Send(m_Slots[i].m_Outgoing.data(), m_Slots[i].m_Outgoing.size()); - if (Sent < 0) - { - int Err = cSocket::GetLastError(); - LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket.GetIPString().c_str(), cSocket::GetErrorString(Err).c_str()); - m_Slots[i].m_Socket.CloseSocket(); - if (m_Slots[i].m_ShouldCallClient) - { - m_Slots[i].m_Client->SocketClosed(); - } - return; - } - m_Slots[i].m_Outgoing.erase(0, Sent); - - // _X: If there's data left, it means the client is not reading fast enough, the server would unnecessarily spin in the main loop with zero actions taken; so signalling is disabled - // This means that if there's data left, it will be sent only when there's incoming data or someone queues another packet (for any socket handled by this thread) - /* - // If there's any data left, signalize the Control socket: - if (!m_Slots[i].m_Outgoing.empty()) - { - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("q", 1); - } - */ - } // for i - m_Slots[i] -} - - - - diff --git a/source/OSSupport/SocketThreads.h b/source/OSSupport/SocketThreads.h deleted file mode 100644 index ecbac3aeb..000000000 --- a/source/OSSupport/SocketThreads.h +++ /dev/null @@ -1,169 +0,0 @@ - -// SocketThreads.h - -// Interfaces to the cSocketThreads class representing the heart of MCS's client networking. -// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support -// For more detail, see http://forum.mc-server.org/showthread.php?tid=327 - -/* -Additional details: -When a client is terminating a connection: -- they call the StopReading() method to disable callbacks for the incoming data -- they call the Write() method to queue any outstanding outgoing data -- they call the QueueClose() method to queue the socket to close after outgoing data has been sent. -When a socket slot is marked as having no callback, it is kept alive until its outgoing data queue is empty and its m_ShouldClose flag is set. -This means that the socket can be written to several times before finally closing it via QueueClose() -*/ - - - - - -/// How many clients should one thread handle? (must be less than FD_SETSIZE for your platform) -#define MAX_SLOTS 63 - - - - - -#pragma once -#ifndef CSOCKETTHREADS_H_INCLUDED -#define CSOCKETTHREADS_H_INCLUDED - -#include "Socket.h" -#include "IsThread.h" - - - - -// Check MAX_SLOTS: -#if MAX_SLOTS >= FD_SETSIZE - #error "MAX_SLOTS must be less than FD_SETSIZE for your platform! (otherwise select() won't work)" -#endif - - - - - -// fwd: -class cSocket; -class cClientHandle; - - - - - -class cSocketThreads -{ -public: - - // Clients of cSocketThreads must implement this interface to be able to communicate - class cCallback - { - public: - /// Called when data is received from the remote party - virtual void DataReceived(const char * a_Data, int a_Size) = 0; - - /// Called when data can be sent to remote party; the function is supposed to append outgoing data to a_Data - virtual void GetOutgoingData(AString & a_Data) = 0; - - /// Called when the socket has been closed for any reason - virtual void SocketClosed(void) = 0; - } ; - - - cSocketThreads(void); - ~cSocketThreads(); - - /// Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client; returns true if successful - bool AddClient(const cSocket & a_Socket, cCallback * a_Client); - - /// Remove the associated socket and the client from processing. The socket is left to send its data and is removed only after all its m_OutgoingData is sent - void RemoveClient(const cCallback * a_Client); - - /// Notify the thread responsible for a_Client that the client has something to write - void NotifyWrite(const cCallback * a_Client); - - /// Puts a_Data into outgoing data queue for a_Client - void Write(const cCallback * a_Client, const AString & a_Data); - - /// Stops reading from the client - when this call returns, no more calls to the callbacks are made - void StopReading(const cCallback * a_Client); - - /// Queues the client for closing, as soon as its outgoing data is sent - void QueueClose(const cCallback * a_Client); - -private: - - class cSocketThread : - public cIsThread - { - typedef cIsThread super; - - public: - - cSocketThread(cSocketThreads * a_Parent); - ~cSocketThread(); - - // All these methods assume parent's m_CS is locked - bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; } - bool IsEmpty (void) const {return m_NumSlots == 0; } - - void AddClient (const cSocket & a_Socket, cCallback * a_Client); // Takes ownership of the socket - bool RemoveClient(const cCallback * a_Client); // Returns true if removed, false if not found - bool RemoveSocket(const cSocket * a_Socket); // Returns true if removed, false if not found - bool HasClient (const cCallback * a_Client) const; - bool HasSocket (const cSocket * a_Socket) const; - bool NotifyWrite (const cCallback * a_Client); // Returns true if client handled by this thread - bool Write (const cCallback * a_Client, const AString & a_Data); // Returns true if client handled by this thread - bool StopReading (const cCallback * a_Client); // Returns true if client handled by this thread - bool QueueClose (const cCallback * a_Client); // Returns true if client handled by this thread - - bool Start(void); // Hide the cIsThread's Start method, we need to provide our own startup to create the control socket - - bool IsValid(void) const {return m_ControlSocket2.IsValid(); } // If the Control socket dies, the thread is not valid anymore - - private: - - cSocketThreads * m_Parent; - - // Two ends of the control socket, the first is select()-ed, the second is written to for notifications - cSocket m_ControlSocket1; - cSocket m_ControlSocket2; - - // Socket-client-packetqueues triplets. - // Manipulation with these assumes that the parent's m_CS is locked - struct sSlot - { - cSocket m_Socket; // The socket is primarily owned by this - cCallback * m_Client; - AString m_Outgoing; // If sending writes only partial data, the rest is stored here for another send - bool m_ShouldClose; // If true, the socket is to be closed after sending all outgoing data - bool m_ShouldCallClient; // If true, the client callbacks are called. Set to false in StopReading() - } ; - sSlot m_Slots[MAX_SLOTS]; - int m_NumSlots; // Number of slots actually used - - virtual void Execute(void) override; - - void PrepareSet (fd_set * a_Set, cSocket::xSocket & a_Highest); // Puts all sockets into the set, along with m_ControlSocket1 - void ReadFromSockets(fd_set * a_Read); // Reads from sockets indicated in a_Read - void WriteToSockets (fd_set * a_Write); // Writes to sockets indicated in a_Write - } ; - - typedef std::list cSocketThreadList; - - - cCriticalSection m_CS; - cSocketThreadList m_Threads; -} ; - - - - - -#endif // CSOCKETTHREADS_H_INCLUDED - - - - diff --git a/source/OSSupport/Thread.cpp b/source/OSSupport/Thread.cpp deleted file mode 100644 index 3df75f0e7..000000000 --- a/source/OSSupport/Thread.cpp +++ /dev/null @@ -1,128 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - - - - - -// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here: -#ifdef _MSC_VER -// -// Usage: SetThreadName (-1, "MainThread"); -// -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; // must be 0x1000 - LPCSTR szName; // pointer to name (in user addr space) - DWORD dwThreadID; // thread ID (-1=caller thread) - DWORD dwFlags; // reserved for future use, must be zero -} THREADNAME_INFO; - -void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) -{ - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = szThreadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - __try - { - RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info ); - } - __except(EXCEPTION_CONTINUE_EXECUTION) - { - } -} -#endif // _MSC_VER - - - - - -cThread::cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName /* = 0 */ ) - : m_ThreadFunction( a_ThreadFunction ) - , m_Param( a_Param ) - , m_Event( new cEvent() ) - , m_StopEvent( 0 ) -{ - if( a_ThreadName ) - { - m_ThreadName.assign(a_ThreadName); - } -} - - - - - -cThread::~cThread() -{ - delete m_Event; - - if( m_StopEvent ) - { - m_StopEvent->Wait(); - delete m_StopEvent; - } -} - - - - - -void cThread::Start( bool a_bWaitOnDelete /* = true */ ) -{ - if( a_bWaitOnDelete ) - m_StopEvent = new cEvent(); - -#ifndef _WIN32 - pthread_t SndThread; - if( pthread_create( &SndThread, NULL, MyThread, this) ) - LOGERROR("ERROR: Could not create thread!"); -#else - DWORD ThreadID = 0; - HANDLE hThread = CreateThread( 0 // security - ,0 // stack size - , (LPTHREAD_START_ROUTINE) MyThread // function name - ,this // parameters - ,0 // flags - ,&ThreadID ); // thread id - CloseHandle( hThread ); - - #ifdef _MSC_VER - if (!m_ThreadName.empty()) - { - SetThreadName(ThreadID, m_ThreadName.c_str()); - } - #endif // _MSC_VER -#endif - - // Wait until thread has actually been created - m_Event->Wait(); -} - - - - - -#ifdef _WIN32 -unsigned long cThread::MyThread(void* a_Param ) -#else -void *cThread::MyThread( void *a_Param ) -#endif -{ - cThread* self = (cThread*)a_Param; - cEvent* StopEvent = self->m_StopEvent; - - ThreadFunc* ThreadFunction = self->m_ThreadFunction; - void* ThreadParam = self->m_Param; - - // Set event to let other thread know this thread has been created and it's safe to delete the cThread object - self->m_Event->Set(); - - ThreadFunction( ThreadParam ); - - if( StopEvent ) StopEvent->Set(); - return 0; -} diff --git a/source/OSSupport/Thread.h b/source/OSSupport/Thread.h deleted file mode 100644 index 3c9316424..000000000 --- a/source/OSSupport/Thread.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -class cThread -{ -public: - typedef void (ThreadFunc)(void*); - cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName = 0 ); - ~cThread(); - - void Start( bool a_bWaitOnDelete = true ); - void WaitForThread(); -private: - ThreadFunc* m_ThreadFunction; - -#ifdef _WIN32 - static unsigned long MyThread(void* a_Param ); -#else - static void *MyThread( void *lpParam ); -#endif - - void* m_Param; - cEvent* m_Event; - cEvent* m_StopEvent; - - AString m_ThreadName; -}; \ No newline at end of file diff --git a/source/OSSupport/Timer.cpp b/source/OSSupport/Timer.cpp deleted file mode 100644 index ed16f9e3a..000000000 --- a/source/OSSupport/Timer.cpp +++ /dev/null @@ -1,37 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Timer.h" - - - - - - -cTimer::cTimer(void) -{ - #ifdef _WIN32 - QueryPerformanceFrequency(&m_TicksPerSecond); - #endif -} - - - - - -long long cTimer::GetNowTime(void) -{ - #ifdef _WIN32 - LARGE_INTEGER now; - QueryPerformanceCounter(&now); - return ((now.QuadPart * 1000) / m_TicksPerSecond.QuadPart); - #else - struct timeval now; - gettimeofday(&now, NULL); - return (long long)(now.tv_sec * 1000 + now.tv_usec / 1000); - #endif -} - - - - diff --git a/source/OSSupport/Timer.h b/source/OSSupport/Timer.h deleted file mode 100644 index a059daa41..000000000 --- a/source/OSSupport/Timer.h +++ /dev/null @@ -1,32 +0,0 @@ - -// Timer.h - -// Declares the cTimer class representing an OS-independent of retrieving current time with msec accuracy - - - - - -#pragma once - - - - - -class cTimer -{ -public: - cTimer(void); - - // Returns the current time expressed in milliseconds - long long GetNowTime(void); -private: - - #ifdef _WIN32 - LARGE_INTEGER m_TicksPerSecond; - #endif -} ; - - - - diff --git a/source/Piston.cpp b/source/Piston.cpp deleted file mode 100644 index 136100922..000000000 --- a/source/Piston.cpp +++ /dev/null @@ -1,306 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Piston.h" -#include "ChunkDef.h" -#include "Entities/Pickup.h" -#include "Item.h" -#include "Root.h" -#include "ClientHandle.h" -#include "World.h" -#include "Server.h" -#include "Blocks/BlockHandler.h" - - - - - -extern bool g_BlockPistonBreakable[]; - - - - - -/// Number of ticks that the piston extending / retracting waits before setting the block -const int PISTON_TICK_DELAY = 20; - - - - - -cPiston::cPiston(cWorld * a_World) - : m_World(a_World) -{ - -} - - - - - -int cPiston::FirstPassthroughBlock(int pistonX, int pistonY, int pistonZ, NIBBLETYPE pistonmeta) -{ - // Examine each of the 12 blocks ahead of the piston: - for (int ret = 0; ret < 12; ret++) - { - BLOCKTYPE currBlock; - NIBBLETYPE currMeta; - AddDir(pistonX, pistonY, pistonZ, pistonmeta, 1); - m_World->GetBlockTypeMeta(pistonX, pistonY, pistonZ, currBlock, currMeta); - if (CanBreakPush(currBlock, currMeta)) - { - // This block breaks when pushed, extend up to here - return ret; - } - if (!CanPush(currBlock, currMeta)) - { - // This block cannot be pushed at all, the piston can't extend - return -1; - } - } - // There is no space for the blocks to move, piston can't extend - return -1; -} - - - - - -void cPiston::ExtendPiston(int pistx, int pisty, int pistz) -{ - BLOCKTYPE pistonBlock; - NIBBLETYPE pistonMeta; - m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta); - - if (IsExtended(pistonMeta)) - { - // Already extended, bail out - return; - } - - m_World->BroadcastBlockAction(pistx, pisty, pistz, 0, pistonMeta, pistonBlock); - m_World->BroadcastSoundEffect("tile.piston.out", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f); - - int dist = FirstPassthroughBlock(pistx, pisty, pistz, pistonMeta); - if (dist < 0) - { - // FirstPassthroughBlock says piston can't push anything, bail out - return; - } - - // Drop the breakable block in the line, if appropriate: - AddDir(pistx, pisty, pistz, pistonMeta, dist + 1); // "pist" now at the breakable / empty block - BLOCKTYPE currBlock; - NIBBLETYPE currMeta; - m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currMeta); - if (currBlock != E_BLOCK_AIR) - { - cBlockHandler * Handler = BlockHandler(currBlock); - if (Handler->DoesDropOnUnsuitable()) - { - Handler->DropBlock(m_World, NULL, pistx, pisty, pistz); - } - } - - // Push blocks, from the furthest to the nearest: - int oldx = pistx, oldy = pisty, oldz = pistz; - NIBBLETYPE currBlockMeta; - for (int i = dist + 1; i > 1; i--) - { - AddDir(pistx, pisty, pistz, pistonMeta, -1); - m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currBlockMeta); - m_World->QueueSetBlock( oldx, oldy, oldz, currBlock, currBlockMeta, PISTON_TICK_DELAY); - oldx = pistx; - oldy = pisty; - oldz = pistz; - } - - int extx = pistx; - int exty = pisty; - int extz = pistz; - AddDir(pistx, pisty, pistz, pistonMeta, -1); - // "pist" now at piston body, "ext" at future extension - - m_World->QueueSetBlock( pistx, pisty, pistz, pistonBlock, pistonMeta | 0x8, PISTON_TICK_DELAY); - m_World->QueueSetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), PISTON_TICK_DELAY); -} - - - - - -void cPiston::RetractPiston(int pistx, int pisty, int pistz) -{ - BLOCKTYPE pistonBlock; - NIBBLETYPE pistonMeta; - m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta); - if (!IsExtended(pistonMeta)) - { - // Already retracted, bail out - return; - } - - m_World->BroadcastBlockAction(pistx, pisty, pistz, 1, pistonMeta & ~(8), pistonBlock); - m_World->BroadcastSoundEffect("tile.piston.in", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f); - m_World->QueueSetBlock(pistx, pisty, pistz, pistonBlock, pistonMeta & ~(8), PISTON_TICK_DELAY); - - // Check the extension: - AddDir(pistx, pisty, pistz, pistonMeta, 1); - if (m_World->GetBlock(pistx, pisty, pistz) != E_BLOCK_PISTON_EXTENSION) - { - LOGD("%s: Piston without an extension?", __FUNCTION__); - return; - } - - // Retract the extension, pull block if appropriate - if (IsSticky(pistonBlock)) - { - int tempx = pistx, tempy = pisty, tempz = pistz; - AddDir( tempx, tempy, tempz, pistonMeta, 1); - BLOCKTYPE tempBlock; - NIBBLETYPE tempMeta; - m_World->GetBlockTypeMeta(tempx, tempy, tempz, tempBlock, tempMeta); - if (CanPull(tempBlock, tempMeta)) - { - // Pull the block - m_World->QueueSetBlock(pistx, pisty, pistz, tempBlock, tempMeta, PISTON_TICK_DELAY); - m_World->QueueSetBlock(tempx, tempy, tempz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY); - } - else - { - // Retract without pulling - m_World->QueueSetBlock(pistx, pisty, pistz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY); - } - } - else - { - m_World->QueueSetBlock(pistx, pisty, pistz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY); - } -} - - - - - -bool cPiston::IsExtended(NIBBLETYPE a_PistonMeta) -{ - return ((a_PistonMeta & 0x8) != 0x0); -} - - - - - -bool cPiston::IsSticky(BLOCKTYPE a_BlockType) -{ - return (a_BlockType == E_BLOCK_STICKY_PISTON); -} - - - - - -bool cPiston::IsStickyExtension(NIBBLETYPE a_ExtMeta) -{ - return ((a_ExtMeta & 0x08) != 0); -} - - - - - -bool cPiston::CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - switch (a_BlockType) - { - case E_BLOCK_ANVIL: - case E_BLOCK_BED: - case E_BLOCK_BEDROCK: - case E_BLOCK_BREWING_STAND: - case E_BLOCK_CHEST: - case E_BLOCK_COMMAND_BLOCK: - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - case E_BLOCK_ENCHANTMENT_TABLE: - case E_BLOCK_END_PORTAL: - case E_BLOCK_END_PORTAL_FRAME: - case E_BLOCK_FURNACE: - case E_BLOCK_LIT_FURNACE: - case E_BLOCK_HOPPER: - case E_BLOCK_JUKEBOX: - case E_BLOCK_MOB_SPAWNER: - case E_BLOCK_NETHER_PORTAL: - case E_BLOCK_NOTE_BLOCK: - case E_BLOCK_OBSIDIAN: - case E_BLOCK_PISTON_EXTENSION: - { - return false; - } - case E_BLOCK_STICKY_PISTON: - case E_BLOCK_PISTON: - { - // A piston can only be pushed if retracted: - return !IsExtended(a_BlockMeta); - } - } - return true; -} - - - - - -bool cPiston::CanBreakPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - return g_BlockPistonBreakable[a_BlockType]; -} - - - - - -bool cPiston::CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - switch (a_BlockType) - { - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_WATER: - { - return false; - } - } - - if (CanBreakPush(a_BlockType, a_BlockMeta)) - { - return false; // CanBreakPush returns true, but we need false to prevent pulling - } - - return CanPush(a_BlockType, a_BlockMeta); -} - - - - - -void cPiston::AddDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_PistonMeta, int a_Amount) -{ - switch (a_PistonMeta & 0x07) - { - case 0: a_BlockY -= a_Amount; break; - case 1: a_BlockY += a_Amount; break; - case 2: a_BlockZ -= a_Amount; break; - case 3: a_BlockZ += a_Amount; break; - case 4: a_BlockX -= a_Amount; break; - case 5: a_BlockX += a_Amount; break; - default: - { - LOGWARNING("%s: invalid direction %d, ignoring", __FUNCTION__, a_PistonMeta & 0x07); - break; - } - } -} - - - - diff --git a/source/Piston.h b/source/Piston.h deleted file mode 100644 index cc051e454..000000000 --- a/source/Piston.h +++ /dev/null @@ -1,94 +0,0 @@ - -#pragma once - - - - - -// fwd: World.h -class cWorld; - - - - - -class cPiston -{ -public: - - cPiston(cWorld * a_World); - - static NIBBLETYPE RotationPitchToMetaData(double a_Rotation, double a_Pitch) - { - if (a_Pitch >= 50) - { - return 0x1; - } - else if (a_Pitch <= -50) - { - return 0x0; - } - else - { - a_Rotation += 90 + 45; // So its not aligned with axis - - if (a_Rotation > 360) - { - a_Rotation -= 360; - } - if ((a_Rotation >= 0) && (a_Rotation < 90)) - { - return 0x4; - } - else if ((a_Rotation >= 180) && (a_Rotation < 270)) - { - return 0x5; - } - else if ((a_Rotation >= 90) && (a_Rotation < 180)) - { - return 0x2; - } - else - { - return 0x3; - } - } - } - - void ExtendPiston( int, int, int ); - void RetractPiston( int, int, int ); - - /// Returns true if the piston (specified by blocktype) is a sticky piston - static bool IsSticky(BLOCKTYPE a_BlockType); - - /// Returns true if the piston (with the specified meta) is extended - static bool IsExtended(NIBBLETYPE a_PistonMeta); - - /// Returns true if the extension (with the specified meta) is sticky - static bool IsStickyExtension(NIBBLETYPE a_ExtMeta); - - /// Returns true if the specified block can be pushed by a piston (and left intact) - static bool CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Returns true if the specified block can be pushed by a piston and broken / replaced - static bool CanBreakPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Returns true if the specified block can be pulled by a sticky piston - static bool CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Updates the coords by the specified amount in the direction a piston of the specified meta is facing - static void AddDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_PistonMeta, int a_Amount); - - - cWorld * m_World; - -private: - void ChainMove( int, int, int, char, unsigned short * ); - - /// Returns how many blocks the piston has to push (where the first free space is); <0 when unpushable - int FirstPassthroughBlock(int a_PistonX, int a_PistonY, int a_PistonZ, NIBBLETYPE a_PistonMeta); -} ; - - - - diff --git a/source/Plugin.cpp b/source/Plugin.cpp deleted file mode 100644 index 98ccfb88c..000000000 --- a/source/Plugin.cpp +++ /dev/null @@ -1,38 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Plugin.h" - - - - - -cPlugin::cPlugin(const AString & a_PluginDirectory) : - m_Language(E_CPP), - m_Name(a_PluginDirectory), - m_Version(0), - m_Directory(a_PluginDirectory) -{ -} - - - - - -cPlugin::~cPlugin() -{ - LOGD("Destroying plugin \"%s\".", m_Name.c_str()); -} - - - - - -AString cPlugin::GetLocalFolder(void) const -{ - return std::string("Plugins/") + m_Directory; -} - - - - diff --git a/source/Plugin.h b/source/Plugin.h deleted file mode 100644 index 06e5819df..000000000 --- a/source/Plugin.h +++ /dev/null @@ -1,149 +0,0 @@ - -#pragma once - -#include "Item.h" -#include "PluginManager.h" - - - - - -class cClientHandle; -class cPlayer; -class cPickup; -class cItem; -class cEntity; -class cWorld; -class cChunkDesc; -struct TakeDamageInfo; - -// fwd: cPlayer.h -class cPlayer; - -// fwd: CraftingRecipes.h -class cCraftingGrid; -class cCraftingRecipe; - - - - - -// tolua_begin -class cPlugin -{ -public: - // tolua_end - - cPlugin( const AString & a_PluginDirectory ); - virtual ~cPlugin(); - - virtual void OnDisable(void) {} - virtual bool Initialize(void) = 0; - - // Called each tick - virtual void Tick(float a_Dt) = 0; - - /** - * On all these functions, return true if you want to override default behavior and not call other plugins on that callback. - * You can also return false, so default behavior is used. - **/ - virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) = 0; - virtual bool OnChat (cPlayer * a_Player, AString & a_Message) = 0; - virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; - virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) = 0; - virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) = 0; - virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; - virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; - virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) = 0; - virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; - virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) = 0; - virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; - virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; - virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; - virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) = 0; - virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) = 0; - virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) = 0; - virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) = 0; - virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) = 0; - virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) = 0; - virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual bool OnPlayerEating (cPlayer & a_Player) = 0; - virtual bool OnPlayerJoined (cPlayer & a_Player) = 0; - virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0; - virtual bool OnPlayerMoved (cPlayer & a_Player) = 0; - virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; - virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) = 0; - virtual bool OnPlayerShooting (cPlayer & a_Player) = 0; - virtual bool OnPlayerSpawned (cPlayer & a_Player) = 0; - virtual bool OnPlayerTossingItem (cPlayer & a_Player) = 0; - virtual bool OnPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; - virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; - virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; - virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; - virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0; - virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) = 0; - virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) = 0; - virtual bool OnSpawningMonster (cWorld & a_World, cMonster & a_Monster) = 0; - virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) = 0; - virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) = 0; - virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) = 0; - virtual bool OnWeatherChanged (cWorld & a_World) = 0; - virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) = 0; - virtual bool OnWorldTick (cWorld & a_World, float a_Dt) = 0; - - /** Handles the command split into a_Split, issued by player a_Player. - Command permissions have already been checked. - Returns true if command handled successfully - */ - virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) = 0; - - /** Handles the console command split into a_Split. - Returns true if command handled successfully. Output is to be sent to the a_Output callback. - */ - virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) = 0; - - /// All bound commands are to be removed, do any language-dependent cleanup here - virtual void ClearCommands(void) {} ; - - /// All bound console commands are to be removed, do any language-dependent cleanup here - virtual void ClearConsoleCommands(void) {} ; - - // tolua_begin - const AString & GetName(void) const { return m_Name; } - void SetName(const AString & a_Name) { m_Name = a_Name; } - - int GetVersion(void) const { return m_Version; } - void SetVersion(int a_Version) { m_Version = a_Version; } - - const AString & GetDirectory(void) const {return m_Directory; } - AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead - AString GetLocalFolder(void) const; - // tolua_end - - - /* This should not be exposed to scripting languages */ - enum PluginLanguage - { - E_CPP, - E_LUA, - E_SQUIRREL, // OBSOLETE, but kept in place to remind us of the horrors lurking in the history - }; - PluginLanguage GetLanguage() { return m_Language; } - void SetLanguage( PluginLanguage a_Language ) { m_Language = a_Language; } - -private: - PluginLanguage m_Language; - AString m_Name; - int m_Version; - - AString m_Directory; -}; // tolua_export - - - - diff --git a/source/PluginLua.cpp b/source/PluginLua.cpp deleted file mode 100644 index 03aefb098..000000000 --- a/source/PluginLua.cpp +++ /dev/null @@ -1,1471 +0,0 @@ - -// PluginLua.cpp - -// Implements the cPluginLua class representing a plugin written in Lua - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#define LUA_USE_POSIX -#include "PluginLua.h" -#include "CommandOutput.h" - -extern "C" -{ - #include "lualib.h" -} - -#include "tolua++.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cPluginLua: - -cPluginLua::cPluginLua(const AString & a_PluginDirectory) : - cPlugin(a_PluginDirectory), - m_LuaState(Printf("plugin %s", a_PluginDirectory.c_str())) -{ -} - - - - - -cPluginLua::~cPluginLua() -{ - cCSLock Lock(m_CriticalSection); - Close(); -} - - - - - -void cPluginLua::Close(void) -{ - if (m_LuaState.IsValid()) - { - // Release all the references in the hook map: - for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH) - { - for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR) - { - delete *itrR; - } // for itrR - itrH->second[] - } // for itrH - m_HookMap[] - m_HookMap.clear(); - - m_LuaState.Close(); - } - else - { - ASSERT(m_HookMap.empty()); - } -} - - - - - -bool cPluginLua::Initialize(void) -{ - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - m_LuaState.Create(); - - // Inject the identification global variables into the state: - lua_pushlightuserdata(m_LuaState, this); - lua_setglobal(m_LuaState, LUA_PLUGIN_INSTANCE_VAR_NAME); - lua_pushstring(m_LuaState, GetName().c_str()); - lua_setglobal(m_LuaState, LUA_PLUGIN_NAME_VAR_NAME); - - tolua_pushusertype(m_LuaState, this, "cPluginLua"); - lua_setglobal(m_LuaState, "g_Plugin"); - } - - std::string PluginPath = FILE_IO_PREFIX + GetLocalDirectory() + "/"; - - // Load all files for this plugin, and execute them - AStringList Files = GetDirectoryContents(PluginPath.c_str()); - for (AStringList::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) - { - if (itr->rfind(".lua") == AString::npos) - { - continue; - } - AString Path = PluginPath + *itr; - if (!m_LuaState.LoadFile(Path)) - { - Close(); - return false; - } - } // for itr - Files[] - - // Call intialize function - bool res = false; - if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res)) - { - LOGWARNING("Error in plugin %s: Cannot call the Initialize() function. Plugin is temporarily disabled.", GetName().c_str()); - Close(); - return false; - } - - if (!res) - { - LOGINFO("Plugin %s: Initialize() call failed, plugin is temporarily disabled.", GetName().c_str()); - Close(); - return false; - } - - return true; -} - - - - - -void cPluginLua::OnDisable(void) -{ - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.HasFunction("OnDisable")) - { - return; - } - m_LuaState.Call("OnDisable"); -} - - - - - -void cPluginLua::Tick(float a_Dt) -{ - cCSLock Lock(m_CriticalSection); - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_Dt); - } -} - - - - - -bool cPluginLua::OnBlockToPickups(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_TO_PICKUPS]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnChat(cPlayer * a_Player, AString & a_Message) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_Player, a_Message, cLuaState::Return, res, a_Message); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnChunkAvailable(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_AVAILABLE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnChunkUnloaded(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnChunkUnloading(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnCollectingPickup(cPlayer * a_Player, cPickup * a_Pickup) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_COLLECTING_PICKUP]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_Player, a_Pickup, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CRAFTING_NO_RECIPE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), (cPlayer *)a_Player, a_Grid, a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnDisconnect(cPlayer * a_Player, const AString & a_Reason) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_Player, a_Reason, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_Player, a_Split, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - switch (a_Source) - { - case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; - case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res); break; - case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res); break; - case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break; - case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break; - case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; - case esWitherSkullBlack: - case esWitherSkullBlue: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; - case esWitherBirth: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; - case esPlugin: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; - default: - { - ASSERT(!"Unhandled ExplosionSource"); - return false; - } - } - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - switch (a_Source) - { - case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esWitherSkullBlack: - case esWitherSkullBlue: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esWitherBirth: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esPlugin: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - default: - { - ASSERT(!"Unhandled ExplosionSource"); - return false; - } - } - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnHandshake(cClientHandle * a_Client, const AString & a_Username) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HANDSHAKE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_Client, a_Username, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PULLING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PUSHING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Victim, a_Killer, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_LOGIN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_Client, a_ProtocolVersion, a_Username, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_ANIMATION]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_Animation, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BREAKING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BROKEN_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerEating(cPlayer & a_Player) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_EATING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_JOINED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_LEFT_CLICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerMoved(cPlayer & a_Player) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SHOOTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SPAWNED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_TOSSING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_POST_CRAFTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_Player, a_Grid, a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PRE_CRAFTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_Player, a_Grid, a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_World, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_MONSTER]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_World, &a_Monster, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_World, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_MONSTER]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_World, &a_Monster, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TAKE_DAMAGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_Receiver, &a_TDI, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnUpdatedSign( - cWorld * a_World, - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, - cPlayer * a_Player -) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATED_SIGN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnUpdatingSign( - cWorld * a_World, - int a_BlockX, int a_BlockY, int a_BlockZ, - AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, - cPlayer * a_Player -) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnWeatherChanged(cWorld & a_World) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_World, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; -} - - - - - -bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) -{ - cCSLock Lock(m_CriticalSection); - bool res = false; - int NewWeather = a_NewWeather; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_World, NewWeather, cLuaState::Return, res, NewWeather); - if (res) - { - a_NewWeather = (eWeather)NewWeather; - return true; - } - } - a_NewWeather = (eWeather)NewWeather; - return false; -} - - - - - -bool cPluginLua::OnWorldTick(cWorld & a_World, float a_Dt) -{ - cCSLock Lock(m_CriticalSection); - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call((int)(**itr), &a_World, a_Dt); - } - return false; -} - - - - - -bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) -{ - ASSERT(!a_Split.empty()); - CommandMap::iterator cmd = m_Commands.find(a_Split[0]); - if (cmd == m_Commands.end()) - { - LOGWARNING("Command handler is registered in cPluginManager but not in cPlugin, wtf? Command \"%s\".", a_Split[0].c_str()); - return false; - } - - cCSLock Lock(m_CriticalSection); - bool res = false; - m_LuaState.Call(cmd->second, a_Split, a_Player, cLuaState::Return, res); - return res; -} - - - - - -bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) -{ - ASSERT(!a_Split.empty()); - CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); - if (cmd == m_ConsoleCommands.end()) - { - LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\", plugin \"%s\".", - a_Split[0].c_str(), GetName().c_str() - ); - return false; - } - - cCSLock Lock(m_CriticalSection); - bool res = false; - AString str; - m_LuaState.Call(cmd->second, a_Split, cLuaState::Return, res, str); - if (res && !str.empty()) - { - a_Output.Out(str); - } - return res; -} - - - - - -void cPluginLua::ClearCommands(void) -{ - cCSLock Lock(m_CriticalSection); - - // Unreference the bound functions so that Lua can GC them - if (m_LuaState != NULL) - { - for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) - { - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); - } - } - m_Commands.clear(); -} - - - - - -void cPluginLua::ClearConsoleCommands(void) -{ - cCSLock Lock(m_CriticalSection); - - // Unreference the bound functions so that Lua can GC them - if (m_LuaState != NULL) - { - for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr) - { - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); - } - } - m_ConsoleCommands.clear(); -} - - - - - -bool cPluginLua::CanAddOldStyleHook(int a_HookType) -{ - const char * FnName = GetHookFnName(a_HookType); - if (FnName == NULL) - { - // Unknown hook ID - LOGWARNING("Plugin %s wants to add an unknown hook ID (%d). The plugin need not work properly.", - GetName().c_str(), a_HookType - ); - m_LuaState.LogStackTrace(); - return false; - } - - // Check if the function is available - if (m_LuaState.HasFunction(FnName)) - { - return true; - } - - LOGWARNING("Plugin %s wants to add a hook (%d), but it doesn't provide the callback function \"%s\" for it. The plugin need not work properly.", - GetName().c_str(), a_HookType, FnName - ); - m_LuaState.LogStackTrace(); - return false; -} - - - - - -const char * cPluginLua::GetHookFnName(int a_HookType) -{ - switch (a_HookType) - { - case cPluginManager::HOOK_BLOCK_TO_PICKUPS: return "OnBlockToPickups"; - case cPluginManager::HOOK_CHAT: return "OnChat"; - case cPluginManager::HOOK_CHUNK_AVAILABLE: return "OnChunkAvailable"; - case cPluginManager::HOOK_CHUNK_GENERATED: return "OnChunkGenerated"; - case cPluginManager::HOOK_CHUNK_GENERATING: return "OnChunkGenerating"; - case cPluginManager::HOOK_CHUNK_UNLOADED: return "OnChunkUnloaded"; - case cPluginManager::HOOK_CHUNK_UNLOADING: return "OnChunkUnloading"; - case cPluginManager::HOOK_COLLECTING_PICKUP: return "OnCollectingPickup"; - case cPluginManager::HOOK_CRAFTING_NO_RECIPE: return "OnCraftingNoRecipe"; - case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; - case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; - case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; - case cPluginManager::HOOK_KILLING: return "OnKilling"; - case cPluginManager::HOOK_LOGIN: return "OnLogin"; - case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; - case cPluginManager::HOOK_PLAYER_BREAKING_BLOCK: return "OnPlayerBreakingBlock"; - case cPluginManager::HOOK_PLAYER_BROKEN_BLOCK: return "OnPlayerBrokenBlock"; - case cPluginManager::HOOK_PLAYER_EATING: return "OnPlayerEating"; - case cPluginManager::HOOK_PLAYER_JOINED: return "OnPlayerJoined"; - case cPluginManager::HOOK_PLAYER_LEFT_CLICK: return "OnPlayerLeftClick"; - case cPluginManager::HOOK_PLAYER_MOVING: return "OnPlayerMoving"; - case cPluginManager::HOOK_PLAYER_PLACED_BLOCK: return "OnPlayerPlacedBlock"; - case cPluginManager::HOOK_PLAYER_PLACING_BLOCK: return "OnPlayerPlacingBlock"; - case cPluginManager::HOOK_PLAYER_RIGHT_CLICK: return "OnPlayerRightClick"; - case cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY: return "OnPlayerRightClickingEntity"; - case cPluginManager::HOOK_PLAYER_SHOOTING: return "OnPlayerShooting"; - case cPluginManager::HOOK_PLAYER_SPAWNED: return "OnPlayerSpawned"; - case cPluginManager::HOOK_PLAYER_TOSSING_ITEM: return "OnPlayerTossingItem"; - case cPluginManager::HOOK_PLAYER_USED_BLOCK: return "OnPlayerUsedBlock"; - case cPluginManager::HOOK_PLAYER_USED_ITEM: return "OnPlayerUsedItem"; - case cPluginManager::HOOK_PLAYER_USING_BLOCK: return "OnPlayerUsingBlock"; - case cPluginManager::HOOK_PLAYER_USING_ITEM: return "OnPlayerUsingItem"; - case cPluginManager::HOOK_POST_CRAFTING: return "OnPostCrafting"; - case cPluginManager::HOOK_PRE_CRAFTING: return "OnPreCrafting"; - case cPluginManager::HOOK_SPAWNED_ENTITY: return "OnSpawnedEntity"; - case cPluginManager::HOOK_SPAWNED_MONSTER: return "OnSpawnedMonster"; - case cPluginManager::HOOK_SPAWNING_ENTITY: return "OnSpawningEntity"; - case cPluginManager::HOOK_SPAWNING_MONSTER: return "OnSpawningMonster"; - case cPluginManager::HOOK_TAKE_DAMAGE: return "OnTakeDamage"; - case cPluginManager::HOOK_TICK: return "OnTick"; - case cPluginManager::HOOK_UPDATED_SIGN: return "OnUpdatedSign"; - case cPluginManager::HOOK_UPDATING_SIGN: return "OnUpdatingSign"; - case cPluginManager::HOOK_WEATHER_CHANGED: return "OnWeatherChanged"; - case cPluginManager::HOOK_WEATHER_CHANGING: return "OnWeatherChanging"; - case cPluginManager::HOOK_WORLD_TICK: return "OnWorldTick"; - default: return NULL; - } // switch (a_Hook) -} - - - - - -bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx) -{ - ASSERT(m_CriticalSection.IsLockedByCurrentThread()); // It probably has to be, how else would we have a LuaState? - - // Check if the function reference is valid: - cLuaState::cRef * Ref = new cLuaState::cRef(m_LuaState, a_FnRefIdx); - if ((Ref == NULL) || !Ref->IsValid()) - { - LOGWARNING("Plugin %s tried to add a hook %d with bad handler function.", GetName().c_str(), a_HookType); - m_LuaState.LogStackTrace(); - delete Ref; - return false; - } - - m_HookMap[a_HookType].push_back(Ref); - return true; -} - - - - - -AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request ) -{ - cCSLock Lock(m_CriticalSection); - std::string RetVal = ""; - - std::pair< std::string, std::string > TabName = GetTabNameForRequest(a_Request); - std::string SafeTabName = TabName.second; - if (SafeTabName.empty()) - { - return ""; - } - - sWebPluginTab * Tab = 0; - for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr) - { - if ((*itr)->SafeTitle.compare(SafeTabName) == 0) // This is the one! Rawr - { - Tab = *itr; - break; - } - } - - if (Tab != NULL) - { - AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->Title.c_str()); - if (!m_LuaState.Call(Tab->UserData, a_Request, cLuaState::Return, Contents)) - { - return "Lua encountered error while processing the page request"; - } - - RetVal += Contents; - } - - return RetVal; -} - - - - - -bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference) -{ - cCSLock Lock(m_CriticalSection); - if (a_LuaState != m_LuaState) - { - LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!"); - return false; - } - sWebPluginTab * Tab = new sWebPluginTab(); - Tab->Title = a_Title; - Tab->SafeTitle = SafeString(a_Title); - - Tab->UserData = a_FunctionReference; - - GetTabs().push_back(Tab); - return true; -} - - - - - -void cPluginLua::BindCommand(const AString & a_Command, int a_FnRef) -{ - ASSERT(m_Commands.find(a_Command) == m_Commands.end()); - m_Commands[a_Command] = a_FnRef; -} - - - - - -void cPluginLua::BindConsoleCommand(const AString & a_Command, int a_FnRef) -{ - ASSERT(m_ConsoleCommands.find(a_Command) == m_ConsoleCommands.end()); - m_ConsoleCommands[a_Command] = a_FnRef; -} - - - - - -void cPluginLua::Unreference(int a_LuaRef) -{ - cCSLock Lock(m_CriticalSection); - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, a_LuaRef); -} - - - - - -bool cPluginLua::CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse) -{ - ASSERT(a_FnRef != LUA_REFNIL); - - cCSLock Lock(m_CriticalSection); - bool res; - m_LuaState.Call(a_FnRef, &a_Window, &a_Player, a_CanRefuse, cLuaState::Return, res); - return res; -} - - - - - -void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum) -{ - ASSERT(a_FnRef != LUA_REFNIL); - - cCSLock Lock(m_CriticalSection); - m_LuaState.Call(a_FnRef, &a_Window, a_SlotNum); -} - - - - diff --git a/source/PluginLua.h b/source/PluginLua.h deleted file mode 100644 index 908466966..000000000 --- a/source/PluginLua.h +++ /dev/null @@ -1,202 +0,0 @@ - -// PluginLua.h - -// Declares the cPluginLua class representing a plugin written in Lua - - - - - -#pragma once - -#include "Plugin.h" -#include "WebPlugin.h" -#include "LuaState.h" - -// Names for the global variables through which the plugin is identified in its LuaState -#define LUA_PLUGIN_NAME_VAR_NAME "_MCServerInternal_PluginName" -#define LUA_PLUGIN_INSTANCE_VAR_NAME "_MCServerInternal_PluginInstance" - - - - -// fwd: UI/Window.h -class cWindow; - - - - - -// tolua_begin -class cPluginLua : - public cPlugin, - public cWebPlugin -{ -public: - // tolua_end - - cPluginLua( const AString & a_PluginDirectory ); - ~cPluginLua(); - - virtual void OnDisable(void) override; - virtual bool Initialize(void) override; - - virtual void Tick(float a_Dt) override; - - virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) override; - virtual bool OnChat (cPlayer * a_Player, AString & a_Message) override; - virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; - virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override; - virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override; - virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; - virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; - virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override; - virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; - virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override; - virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; - virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; - virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) override; - virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) override; - virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) override; - virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) override; - virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; - virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) override; - virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual bool OnPlayerEating (cPlayer & a_Player) override; - virtual bool OnPlayerJoined (cPlayer & a_Player) override; - virtual bool OnPlayerMoved (cPlayer & a_Player) override; - virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override; - virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override; - virtual bool OnPlayerShooting (cPlayer & a_Player) override; - virtual bool OnPlayerSpawned (cPlayer & a_Player) override; - virtual bool OnPlayerTossingItem (cPlayer & a_Player) override; - virtual bool OnPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) override; - virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) override; - virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) override; - virtual bool OnSpawningMonster (cWorld & a_World, cMonster & a_Monster) override; - virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) override; - virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override; - virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override; - virtual bool OnWeatherChanged (cWorld & a_World) override; - virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) override; - virtual bool OnWorldTick (cWorld & a_World, float a_Dt) override; - - virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) override; - - virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override; - - virtual void ClearCommands(void) override; - - virtual void ClearConsoleCommands(void) override; - - /// Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) - bool CanAddOldStyleHook(int a_HookType); - - // cWebPlugin override - virtual const AString GetWebTitle(void) const {return GetName(); } - - // cWebPlugin and WebAdmin stuff - virtual AString HandleWebRequest(const HTTPRequest * a_Request ) override; - bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS << - - /// Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. - void BindCommand(const AString & a_Command, int a_FnRef); - - /// Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap. - void BindConsoleCommand(const AString & a_Command, int a_FnRef); - - cLuaState & GetLuaState(void) { return m_LuaState; } - - cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; } - - /// Removes a previously referenced object (luaL_unref()) - void Unreference(int a_LuaRef); - - /// Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true - bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse); - - /// Calls the plugin-specified "cLuaWindow slot changed" callback. - void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum); - - /// Returns the name of Lua function that should handle the specified hook type in the older (#121) API - static const char * GetHookFnName(int a_HookType); - - /** Adds a Lua function to be called for the specified hook. - The function has to be on the Lua stack at the specified index a_FnRefIdx - Returns true if the hook was added successfully. - */ - bool AddHookRef(int a_HookType, int a_FnRefIdx); - - // The following templates allow calls to arbitrary Lua functions residing in the plugin: - - /// Call a Lua function with 0 args - template bool Call(FnT a_Fn) - { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn); - } - - /// Call a Lua function with 1 arg - template bool Call(FnT a_Fn, ArgT0 a_Arg0) - { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Arg0); - } - - /// Call a Lua function with 2 args - template bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1) - { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1); - } - - /// Call a Lua function with 3 args - template bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2) - { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2); - } - - /// Call a Lua function with 4 args - template bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3) - { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2, a_Arg3); - } - -protected: - /// Maps command name into Lua function reference - typedef std::map CommandMap; - - /// Provides an array of Lua function references - typedef std::vector cLuaRefs; - - /// Maps hook types into arrays of Lua function references to call for each hook type - typedef std::map cHookMap; - - cCriticalSection m_CriticalSection; - cLuaState m_LuaState; - - CommandMap m_Commands; - CommandMap m_ConsoleCommands; - - cHookMap m_HookMap; - - /// Releases all Lua references and closes the LuaState - void Close(void); -} ; // tolua_export - - - - diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp deleted file mode 100644 index c1f695163..000000000 --- a/source/PluginManager.cpp +++ /dev/null @@ -1,1664 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "PluginManager.h" -#include "Plugin.h" -#include "PluginLua.h" -#include "WebAdmin.h" -#include "Item.h" -#include "Root.h" -#include "Server.h" -#include "CommandOutput.h" - -#include "../iniFile/iniFile.h" -#include "tolua++.h" -#include "Entities/Player.h" - - - - - -cPluginManager * cPluginManager::Get(void) -{ - return cRoot::Get()->GetPluginManager(); -} - - - - - -cPluginManager::cPluginManager(void) : - m_bReloadPlugins(false) -{ -} - - - - - -cPluginManager::~cPluginManager() -{ - UnloadPluginsNow(); -} - - - - - -void cPluginManager::ReloadPlugins(void) -{ - m_bReloadPlugins = true; -} - - - - - -void cPluginManager::FindPlugins(void) -{ - AString PluginsPath = FILE_IO_PREFIX + AString( "Plugins/" ); - - // First get a clean list of only the currently running plugins, we don't want to mess those up - for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();) - { - if (itr->second == NULL) - { - PluginMap::iterator thiz = itr; - ++thiz; - m_Plugins.erase( itr ); - itr = thiz; - continue; - } - ++itr; - } - - AStringList Files = GetDirectoryContents(PluginsPath.c_str()); - for (AStringList::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) - { - if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr))) - { - // We only want folders, and don't want "." or ".." - continue; - } - - // Add plugin name/directory to the list - if (m_Plugins.find(*itr) == m_Plugins.end()) - { - m_Plugins[*itr] = NULL; - } - } -} - - - - - -void cPluginManager::ReloadPluginsNow(void) -{ - cIniFile a_SettingsIni; - a_SettingsIni.ReadFile("settings.ini"); - ReloadPluginsNow(a_SettingsIni); -} - - - - - -void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) -{ - LOG("-- Loading Plugins --"); - m_bReloadPlugins = false; - UnloadPluginsNow(); - - FindPlugins(); - - cServer::BindBuiltInConsoleCommands(); - - unsigned int KeyNum = a_SettingsIni.FindKey("Plugins"); - unsigned int NumPlugins = ((KeyNum != -1) ? (a_SettingsIni.GetNumValues(KeyNum)) : 0); - if (KeyNum == -1) - { - InsertDefaultPlugins(a_SettingsIni); - } - else if (NumPlugins > 0) - { - for(unsigned int i = 0; i < NumPlugins; i++) - { - AString ValueName = a_SettingsIni.GetValueName(KeyNum, i); - if (ValueName.compare("Plugin") == 0) - { - AString PluginFile = a_SettingsIni.GetValue(KeyNum, i); - if (!PluginFile.empty()) - { - if (m_Plugins.find(PluginFile) != m_Plugins.end()) - { - LoadPlugin( PluginFile ); - } - } - } - } - } - - if (GetNumPlugins() == 0) - { - LOG("-- No Plugins Loaded --"); - } - else if (GetNumPlugins() > 1) - { - LOG("-- Loaded %i Plugins --", GetNumPlugins()); - } - else - { - LOG("-- Loaded 1 Plugin --"); - } -} - - - - - -void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni) -{ - a_SettingsIni.AddKeyName("Plugins"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump"); - a_SettingsIni.SetValue("Plugins", "Plugin", "Core"); - a_SettingsIni.SetValue("Plugins", "Plugin", "TransAPI"); - a_SettingsIni.SetValue("Plugins", "Plugin", "ChatLog"); -} - - - - - -void cPluginManager::Tick(float a_Dt) -{ - while (!m_DisablePluginList.empty()) - { - RemovePlugin(m_DisablePluginList.front()); - m_DisablePluginList.pop_front(); - } - - if (m_bReloadPlugins) - { - ReloadPluginsNow(); - } - - HookMap::iterator Plugins = m_Hooks.find(HOOK_TICK); - if (Plugins != m_Hooks.end()) - { - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - (*itr)->Tick(a_Dt); - } - } -} - - - - - -bool cPluginManager::CallHookBlockToPickups( - cWorld * a_World, cEntity * a_Digger, - int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - cItems & a_Pickups -) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_TO_PICKUPS); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_Pickups)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookChat(cPlayer * a_Player, AString & a_Message) -{ - if (ExecuteCommand(a_Player, a_Message)) - { - return true; - } - - // Check if it was a standard command (starts with a slash) - if (!a_Message.empty() && (a_Message[0] == '/')) - { - AStringVector Split(StringSplit(a_Message, " ")); - ASSERT(!Split.empty()); // This should not happen - we know there's at least one char in the message so the split needs to be at least one item long - a_Player->SendMessage(Printf("Unknown Command: \"%s\"", Split[0].c_str())); - LOGINFO("Player \"%s\" issued an unknown command: \"%s\"", a_Player->GetName().c_str(), a_Message.c_str()); - return true; // Cancel sending - } - - HookMap::iterator Plugins = m_Hooks.find(HOOK_CHAT); - if (Plugins == m_Hooks.end()) - { - return false; - } - - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnChat(a_Player, a_Message)) - { - return true; - } - } - - return false; -} - - - - - -bool cPluginManager::CallHookChunkAvailable(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_AVAILABLE); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnChunkAvailable(a_World, a_ChunkX, a_ChunkZ)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATED); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnChunkGenerating(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookChunkUnloaded(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_UNLOADED); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnChunkUnloaded(a_World, a_ChunkX, a_ChunkZ)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookChunkUnloading(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_UNLOADING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnChunkUnloading(a_World, a_ChunkX, a_ChunkZ)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookCollectingPickup(cPlayer * a_Player, cPickup & a_Pickup) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_COLLECTING_PICKUP); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnCollectingPickup(a_Player, &a_Pickup)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_CRAFTING_NO_RECIPE); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnCraftingNoRecipe(a_Player, a_Grid, a_Recipe)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Reason) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnDisconnect(a_Player, a_Reason)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_EXECUTE_COMMAND); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnExecuteCommand(a_Player, a_Split)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_EXPLODED); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnExploded(a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_EXPLODING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnExploding(a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookHandshake(cClientHandle * a_ClientHandle, const AString & a_Username) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_HANDSHAKE); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnHandshake(a_ClientHandle, a_Username)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_HOPPER_PULLING_ITEM); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnHopperPullingItem(a_World, a_Hopper, a_DstSlotNum, a_SrcEntity, a_SrcSlotNum)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_HOPPER_PUSHING_ITEM); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnHopperPushingItem(a_World, a_Hopper, a_SrcSlotNum, a_DstEntity, a_DstSlotNum)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookKilling(cEntity & a_Victim, cEntity * a_Killer) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_KILLING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnKilling(a_Victim, a_Killer)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_LOGIN); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnLogin(a_Client, a_ProtocolVersion, a_Username)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerAnimation(cPlayer & a_Player, int a_Animation) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_ANIMATION); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerAnimation(a_Player, a_Animation)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_BREAKING_BLOCK); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerBreakingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_BROKEN_BLOCK); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerBrokenBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerEating(cPlayer & a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_EATING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerEating(a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerJoined(cPlayer & a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_JOINED); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerJoined(a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_LEFT_CLICK); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerLeftClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_MOVING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerMoved(a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_PLACED_BLOCK); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_PLACING_BLOCK); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_RIGHT_CLICK); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerRightClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_RIGHT_CLICKING_ENTITY); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerRightClickingEntity(a_Player, a_Entity)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerShooting(cPlayer & a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SHOOTING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerShooting(a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerSpawned(cPlayer & a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SPAWNED); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerSpawned(a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerTossingItem(cPlayer & a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_TOSSING_ITEM); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerTossingItem(a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USED_BLOCK); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerUsedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USED_ITEM); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerUsedItem(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USING_BLOCK); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerUsingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USING_ITEM); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPlayerUsingItem(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPostCrafting(a_Player, a_Grid, a_Recipe)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PRE_CRAFTING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPreCrafting(a_Player, a_Grid, a_Recipe)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookSpawnedEntity(cWorld & a_World, cEntity & a_Entity) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNED_ENTITY); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnSpawnedEntity(a_World, a_Entity)) - { - return true; - } - } - return false; -} - - - - -bool cPluginManager::CallHookSpawnedMonster(cWorld & a_World, cMonster & a_Monster) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNED_MONSTER); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnSpawnedMonster(a_World, a_Monster)) - { - return true; - } - } - return false; -} - - - - -bool cPluginManager::CallHookSpawningEntity(cWorld & a_World, cEntity & a_Entity) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNING_ENTITY); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnSpawningEntity(a_World, a_Entity)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookSpawningMonster(cWorld & a_World, cMonster & a_Monster) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNING_MONSTER); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnSpawningMonster(a_World, a_Monster)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_TAKE_DAMAGE); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnTakeDamage(a_Receiver, a_TDI)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookUpdatingSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATING_SIGN); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnUpdatingSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookUpdatedSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATED_SIGN); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnUpdatedSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookWeatherChanged(cWorld & a_World) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGED); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnWeatherChanged(a_World)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnWeatherChanging(a_World, a_NewWeather)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookWorldTick(cWorld & a_World, float a_Dt) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_WORLD_TICK); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnWorldTick(a_World, a_Dt)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions) -{ - ASSERT(a_Player != NULL); - - AStringVector Split(StringSplit(a_Command, " ")); - if (Split.empty()) - { - return false; - } - - CommandMap::iterator cmd = m_Commands.find(Split[0]); - if (cmd == m_Commands.end()) - { - // Command not found - return false; - } - - // Ask plugins first if a command is okay to execute the command: - if (CallHookExecuteCommand(a_Player, Split)) - { - LOGINFO("Player \"%s\" tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player->GetName().c_str(), Split[0].c_str()); - return false; - } - - if ( - a_ShouldCheckPermissions && - !cmd->second.m_Permission.empty() && - !a_Player->HasPermission(cmd->second.m_Permission) - ) - { - LOGINFO("Player \"%s\" tried to execute forbidden command \"%s\".", a_Player->GetName().c_str(), Split[0].c_str()); - return false; - } - - ASSERT(cmd->second.m_Plugin != NULL); - - return cmd->second.m_Plugin->HandleCommand(Split, a_Player); -} - - - - - -cPlugin * cPluginManager::GetPlugin( const AString & a_Plugin ) const -{ - for( PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr ) - { - if (itr->second == NULL ) continue; - if (itr->second->GetName().compare(a_Plugin) == 0) - { - return itr->second; - } - } - return 0; -} - - - - - -const cPluginManager::PluginMap & cPluginManager::GetAllPlugins() const -{ - return m_Plugins; -} - - - - - -void cPluginManager::UnloadPluginsNow() -{ - m_Hooks.clear(); - - while (!m_Plugins.empty()) - { - RemovePlugin(m_Plugins.begin()->second); - } - - m_Commands.clear(); - m_ConsoleCommands.clear(); -} - - - - - -bool cPluginManager::DisablePlugin(const AString & a_PluginName) -{ - PluginMap::iterator itr = m_Plugins.find(a_PluginName); - if (itr == m_Plugins.end()) - { - return false; - } - - if (itr->first.compare(a_PluginName) == 0) // _X 2013_02_01: wtf? Isn't this supposed to be what find() does? - { - m_DisablePluginList.push_back(itr->second); - itr->second = NULL; // Get rid of this thing right away - return true; - } - return false; -} - - - - - -bool cPluginManager::LoadPlugin(const AString & a_PluginName) -{ - return AddPlugin(new cPluginLua(a_PluginName.c_str())); -} - - - - - -void cPluginManager::RemoveHooks(cPlugin * a_Plugin) -{ - for (HookMap::iterator itr = m_Hooks.begin(), end = m_Hooks.end(); itr != end; ++itr) - { - itr->second.remove(a_Plugin); - } -} - - - - - -void cPluginManager::RemovePlugin(cPlugin * a_Plugin) -{ - for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) - { - if (itr->second == a_Plugin) - { - m_Plugins.erase(itr); - break; - } - } - - RemovePluginCommands(a_Plugin); - RemovePluginConsoleCommands(a_Plugin); - RemoveHooks(a_Plugin); - if (a_Plugin != NULL) - { - a_Plugin->OnDisable(); - } - delete a_Plugin; -} - - - - - -void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin) -{ - if (a_Plugin != NULL) - { - a_Plugin->ClearCommands(); - } - - for (CommandMap::iterator itr = m_Commands.begin(); itr != m_Commands.end();) - { - if (itr->second.m_Plugin == a_Plugin) - { - CommandMap::iterator EraseMe = itr; // Stupid GCC doesn't have a std::map::erase() that would return the next iterator - ++itr; - m_Commands.erase(EraseMe); - } - else - { - ++itr; - } - } // for itr - m_Commands[] -} - - - - - -bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) -{ - CommandMap::iterator cmd = m_Commands.find(a_Command); - if (cmd != m_Commands.end()) - { - LOGWARNING("Command \"%s\" is already bound to plugin \"%s\".", a_Command.c_str(), cmd->second.m_Plugin->GetName().c_str()); - return false; - } - - m_Commands[a_Command].m_Plugin = a_Plugin; - m_Commands[a_Command].m_Permission = a_Permission; - m_Commands[a_Command].m_HelpString = a_HelpString; - return true; -} - - - - - -bool cPluginManager::ForEachCommand(cCommandEnumCallback & a_Callback) -{ - for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) - { - if (a_Callback.Command(itr->first, itr->second.m_Plugin, itr->second.m_Permission, itr->second.m_HelpString)) - { - return false; - } - } // for itr - m_Commands[] - return true; -} - - - - - -bool cPluginManager::IsCommandBound(const AString & a_Command) -{ - return (m_Commands.find(a_Command) != m_Commands.end()); -} - - - - - -AString cPluginManager::GetCommandPermission(const AString & a_Command) -{ - CommandMap::iterator cmd = m_Commands.find(a_Command); - return (cmd == m_Commands.end()) ? "" : cmd->second.m_Permission; -} - - - - - -bool cPluginManager::ExecuteCommand(cPlayer * a_Player, const AString & a_Command) -{ - return HandleCommand(a_Player, a_Command, true); -} - - - - - -bool cPluginManager::ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command) -{ - return HandleCommand(a_Player, a_Command, false); -} - - - - - -void cPluginManager::RemovePluginConsoleCommands(cPlugin * a_Plugin) -{ - if (a_Plugin != NULL) - { - a_Plugin->ClearConsoleCommands(); - } - - for (CommandMap::iterator itr = m_ConsoleCommands.begin(); itr != m_ConsoleCommands.end();) - { - if (itr->second.m_Plugin == a_Plugin) - { - CommandMap::iterator EraseMe = itr; // Stupid GCC doesn't have a std::map::erase() that would return the next iterator - ++itr; - m_ConsoleCommands.erase(EraseMe); - } - else - { - ++itr; - } - } // for itr - m_Commands[] -} - - - - - -bool cPluginManager::BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString) -{ - CommandMap::iterator cmd = m_ConsoleCommands.find(a_Command); - if (cmd != m_ConsoleCommands.end()) - { - if (cmd->second.m_Plugin == NULL) - { - LOGWARNING("Console command \"%s\" is already bound internally by MCServer, cannot bind in plugin \"%s\".", a_Command.c_str(), a_Plugin->GetName().c_str()); - } - else - { - LOGWARNING("Console command \"%s\" is already bound to plugin \"%s\", cannot bind in plugin \"%s\".", a_Command.c_str(), cmd->second.m_Plugin->GetName().c_str(), a_Plugin->GetName().c_str()); - } - return false; - } - - m_ConsoleCommands[a_Command].m_Plugin = a_Plugin; - m_ConsoleCommands[a_Command].m_Permission = ""; - m_ConsoleCommands[a_Command].m_HelpString = a_HelpString; - return true; -} - - - - - -bool cPluginManager::ForEachConsoleCommand(cCommandEnumCallback & a_Callback) -{ - for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr) - { - if (a_Callback.Command(itr->first, itr->second.m_Plugin, "", itr->second.m_HelpString)) - { - return false; - } - } // for itr - m_Commands[] - return true; -} - - - - - -bool cPluginManager::IsConsoleCommandBound(const AString & a_Command) -{ - return (m_ConsoleCommands.find(a_Command) != m_ConsoleCommands.end()); -} - - - - - -bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) -{ - if (a_Split.empty()) - { - return false; - } - - CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); - if (cmd == m_ConsoleCommands.end()) - { - // Command not found - return false; - } - - if (cmd->second.m_Plugin == NULL) - { - // This is a built-in command - return false; - } - - // Ask plugins first if a command is okay to execute the console command: - if (CallHookExecuteCommand(NULL, a_Split)) - { - a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str()); - return false; - } - - return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output); -} - - - - - -void cPluginManager::TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player) -{ - for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) - { - if (NoCaseCompare(itr->first.substr(0, a_Text.length()), a_Text) != 0) - { - // Command name doesn't match - continue; - } - if ((a_Player != NULL) && !a_Player->HasPermission(itr->second.m_Permission)) - { - // Player doesn't have permission for the command - continue; - } - a_Results.push_back(itr->first); - } -} - - - - - -bool cPluginManager::IsValidHookType(int a_HookType) -{ - return ((a_HookType >= 0) && (a_HookType <= HOOK_MAX)); -} - - - - - -bool cPluginManager::AddPlugin(cPlugin * a_Plugin) -{ - m_Plugins[a_Plugin->GetDirectory()] = a_Plugin; - if (a_Plugin->Initialize()) - { - // Initialization OK - return true; - } - - // Initialization failed - RemovePlugin(a_Plugin); // Also undoes any registrations that Initialize() might have made - return false; -} - - - - - -void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook) -{ - if (!a_Plugin) - { - LOGWARN("Called cPluginManager::AddHook() with a_Plugin == NULL"); - return; - } - PluginList & Plugins = m_Hooks[a_Hook]; - Plugins.remove(a_Plugin); - Plugins.push_back(a_Plugin); -} - - - - - -unsigned int cPluginManager::GetNumPlugins() const -{ - return m_Plugins.size(); -} - - - - diff --git a/source/PluginManager.h b/source/PluginManager.h deleted file mode 100644 index 4140bffb5..000000000 --- a/source/PluginManager.h +++ /dev/null @@ -1,295 +0,0 @@ - -#pragma once - -#include "Item.h" - - - - - -class cPlugin; - -// fwd: World.h -class cWorld; - -// fwd: ChunkDesc.h -class cChunkDesc; - -// fwd: Entities/Entity.h -class cEntity; - -// fwd: Mobs/Monster.h -class cMonster; - -// fwd: Player.h -class cPlayer; - -// fwd: CraftingRecipes.h -class cCraftingGrid; -class cCraftingRecipe; - -// fwd: Pickup.h -class cPickup; - -// fwd: Pawn.h -struct TakeDamageInfo; -class cPawn; - -// fwd: CommandOutput.h -class cCommandOutputCallback; - -// fwd: BlockEntities/HopperEntity.h -class cHopperEntity; - -// fwd: BlockEntities/BlockEntityWithItems.h -class cBlockEntityWithItems; - - - - - -class cPluginManager // tolua_export -{ // tolua_export -public: // tolua_export - - // Called each tick - virtual void Tick(float a_Dt); - - // tolua_begin - enum PluginHook - { - HOOK_BLOCK_TO_PICKUPS, - HOOK_CHAT, - HOOK_CHUNK_AVAILABLE, - HOOK_CHUNK_GENERATED, - HOOK_CHUNK_GENERATING, - HOOK_CHUNK_UNLOADED, - HOOK_CHUNK_UNLOADING, - HOOK_COLLECTING_PICKUP, - HOOK_CRAFTING_NO_RECIPE, - HOOK_DISCONNECT, - HOOK_EXECUTE_COMMAND, - HOOK_EXPLODED, - HOOK_EXPLODING, - HOOK_HANDSHAKE, - HOOK_HOPPER_PULLING_ITEM, - HOOK_HOPPER_PUSHING_ITEM, - HOOK_KILLING, - HOOK_LOGIN, - HOOK_PLAYER_ANIMATION, - HOOK_PLAYER_BREAKING_BLOCK, - HOOK_PLAYER_BROKEN_BLOCK, - HOOK_PLAYER_EATING, - HOOK_PLAYER_JOINED, - HOOK_PLAYER_LEFT_CLICK, - HOOK_PLAYER_MOVING, - HOOK_PLAYER_PLACED_BLOCK, - HOOK_PLAYER_PLACING_BLOCK, - HOOK_PLAYER_RIGHT_CLICK, - HOOK_PLAYER_RIGHT_CLICKING_ENTITY, - HOOK_PLAYER_SHOOTING, - HOOK_PLAYER_SPAWNED, - HOOK_PLAYER_TOSSING_ITEM, - HOOK_PLAYER_USED_BLOCK, - HOOK_PLAYER_USED_ITEM, - HOOK_PLAYER_USING_BLOCK, - HOOK_PLAYER_USING_ITEM, - HOOK_POST_CRAFTING, - HOOK_PRE_CRAFTING, - HOOK_SPAWNED_ENTITY, - HOOK_SPAWNED_MONSTER, - HOOK_SPAWNING_ENTITY, - HOOK_SPAWNING_MONSTER, - HOOK_TAKE_DAMAGE, - HOOK_TICK, - HOOK_UPDATED_SIGN, - HOOK_UPDATING_SIGN, - HOOK_WEATHER_CHANGED, - HOOK_WEATHER_CHANGING, - HOOK_WORLD_TICK, - - // Note that if a hook type is added, it may need processing in cPlugin::CanAddHook() descendants, - // and it definitely needs adding in cPluginLua::GetHookFnName() ! - - // Keep these two as the last items, they are used for validity checking and get their values automagically - HOOK_NUM_HOOKS, - HOOK_MAX = HOOK_NUM_HOOKS - 1, - } ; - // tolua_end - - /// Used as a callback for enumerating bound commands - class cCommandEnumCallback - { - public: - /** Called for each command; return true to abort enumeration - For console commands, a_Permission is not used (set to empty string) - */ - virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0; - } ; - - /// Returns the instance of the Plugin Manager (there is only ever one) - static cPluginManager * Get(void); // tolua_export - - typedef std::map< AString, cPlugin * > PluginMap; - typedef std::list< cPlugin * > PluginList; - cPlugin * GetPlugin( const AString & a_Plugin ) const; // tolua_export - const PluginMap & GetAllPlugins() const; // >> EXPORTED IN MANUALBINDINGS << - - void FindPlugins(); // tolua_export - void ReloadPlugins(); // tolua_export - - /// Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add - void AddHook(cPlugin * a_Plugin, int a_HookType); - - unsigned int GetNumPlugins() const; // tolua_export - - // Calls for individual hooks. Each returns false if the action is to continue or true if the plugin wants to abort - bool CallHookBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups); - bool CallHookChat (cPlayer * a_Player, AString & a_Message); - bool CallHookChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ); - bool CallHookChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc); - bool CallHookChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc); - bool CallHookChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ); - bool CallHookChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ); - bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup); - bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason); - bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd - bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); - bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); - bool CallHookHandshake (cClientHandle * a_ClientHandle, const AString & a_Username); - bool CallHookHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum); - bool CallHookHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum); - bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer); - bool CallHookLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username); - bool CallHookPlayerAnimation (cPlayer & a_Player, int a_Animation); - bool CallHookPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - bool CallHookPlayerEating (cPlayer & a_Player); - bool CallHookPlayerJoined (cPlayer & a_Player); - bool CallHookPlayerMoving (cPlayer & a_Player); - bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); - bool CallHookPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - bool CallHookPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); - bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity); - bool CallHookPlayerShooting (cPlayer & a_Player); - bool CallHookPlayerSpawned (cPlayer & a_Player); - bool CallHookPlayerTossingItem (cPlayer & a_Player); - bool CallHookPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); - bool CallHookPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); - bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity); - bool CallHookSpawnedMonster (cWorld & a_World, cMonster & a_Monster); - bool CallHookSpawningEntity (cWorld & a_World, cEntity & a_Entity); - bool CallHookSpawningMonster (cWorld & a_World, cMonster & a_Monster); - bool CallHookTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TDI); - bool CallHookUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player); - bool CallHookUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player); - bool CallHookWeatherChanged (cWorld & a_World); - bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather); - bool CallHookWorldTick (cWorld & a_World, float a_Dt); - - bool DisablePlugin(const AString & a_PluginName); // tolua_export - bool LoadPlugin (const AString & a_PluginName); // tolua_export - - /// Removes all hooks the specified plugin has registered - void RemoveHooks(cPlugin * a_Plugin); - - /// Removes the plugin from the internal structures and deletes its object. - void RemovePlugin(cPlugin * a_Plugin); - - /// Removes all command bindings that the specified plugin has made - void RemovePluginCommands(cPlugin * a_Plugin); - - /// Binds a command to the specified plugin. Returns true if successful, false if command already bound. - bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param - - /// Calls a_Callback for each bound command, returns true if all commands were enumerated - bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Returns true if the command is in the command map - bool IsCommandBound(const AString & a_Command); // tolua_export - - /// Returns the permission needed for the specified command; empty string if command not found - AString GetCommandPermission(const AString & a_Command); // tolua_export - - /// Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed. - bool ExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export - - /// Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found) - bool ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export - - /// Removes all console command bindings that the specified plugin has made - void RemovePluginConsoleCommands(cPlugin * a_Plugin); - - /// Binds a console command to the specified plugin. Returns true if successful, false if command already bound. - bool BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param - - /// Calls a_Callback for each bound console command, returns true if all commands were enumerated - bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Returns true if the console command is in the command map - bool IsConsoleCommandBound(const AString & a_Command); // tolua_export - - /// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback - bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output); - - /** Appends all commands beginning with a_Text (case-insensitive) into a_Results. - If a_Player is not NULL, only commands for which the player has permissions are added. - */ - void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player); - - /// Returns true if the specified hook type is within the allowed range - static bool IsValidHookType(int a_HookType); - -private: - friend class cRoot; - - class cCommandReg - { - public: - cPlugin * m_Plugin; - AString m_Permission; // Not used for console commands - AString m_HelpString; - } ; - - typedef std::map HookMap; - typedef std::map CommandMap; - - PluginList m_DisablePluginList; - PluginMap m_Plugins; - HookMap m_Hooks; - CommandMap m_Commands; - CommandMap m_ConsoleCommands; - - bool m_bReloadPlugins; - - cPluginManager(); - ~cPluginManager(); - - /// Reloads all plugins, defaulting to settings.ini for settings location - void ReloadPluginsNow(void); - - /// Reloads all plugins with a cIniFile object expected to be initialised to settings.ini - void ReloadPluginsNow(cIniFile & a_SettingsIni); - - /// Unloads all plugins - void UnloadPluginsNow(void); - - /// Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini - void InsertDefaultPlugins(cIniFile & a_SettingsIni); - - /// Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. - bool AddPlugin(cPlugin * a_Plugin); - - /// Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled. - bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions); -} ; // tolua_export - - - - diff --git a/source/ProbabDistrib.cpp b/source/ProbabDistrib.cpp deleted file mode 100644 index 5fa17c276..000000000 --- a/source/ProbabDistrib.cpp +++ /dev/null @@ -1,142 +0,0 @@ - -// ProbabDistrib.cpp - -// Implements the cProbabDistrib class representing a discrete probability distribution curve and random generator - -#include "Globals.h" -#include "ProbabDistrib.h" -#include "MersenneTwister.h" - - - - - - -cProbabDistrib::cProbabDistrib(int a_MaxValue) : - m_MaxValue(a_MaxValue), - m_Sum(-1) -{ -} - - - - - - -void cProbabDistrib::SetPoints(const cProbabDistrib::cPoints & a_Points) -{ - ASSERT(!a_Points.empty()); - m_Sum = 0; - m_Cumulative.clear(); - m_Cumulative.reserve(a_Points.size() + 1); - int ProbSum = 0; - int LastProb = 0; - int LastValue = -1; - if (a_Points[0].m_Value != 0) - { - m_Cumulative.push_back(cPoint(0, 0)); // Always push in the [0, 0] point for easier search algorithm bounds - LastValue = 0; - } - for (cPoints::const_iterator itr = a_Points.begin(), end = a_Points.end(); itr != end; ++itr) - { - if (itr->m_Value == LastValue) - { - continue; - } - - // Add the current trapezoid to the sum: - ProbSum += (LastProb + itr->m_Probability) * (itr->m_Value - LastValue) / 2; - LastProb = itr->m_Probability; - LastValue = itr->m_Value; - m_Cumulative.push_back(cPoint(itr->m_Value, ProbSum)); - } // for itr - a_Points[] - if (LastValue != m_MaxValue) - { - m_Cumulative.push_back(cPoint(m_MaxValue, 0)); // Always push in the last point for easier search algorithm bounds - } - m_Sum = ProbSum; -} - - - - - -bool cProbabDistrib::SetDefString(const AString & a_DefString) -{ - AStringVector Points = StringSplitAndTrim(a_DefString, ";"); - if (Points.empty()) - { - return false; - } - cPoints Pts; - for (AStringVector::const_iterator itr = Points.begin(), end = Points.end(); itr != end; ++itr) - { - AStringVector Split = StringSplitAndTrim(*itr, ","); - if (Split.size() != 2) - { - // Bad format - return false; - } - int Value = atoi(Split[0].c_str()); - int Prob = atoi(Split[1].c_str()); - if ( - ((Value == 0) && (Split[0] != "0")) || - ((Prob == 0) && (Split[1] != "0")) - ) - { - // Number parse error - return false; - } - Pts.push_back(cPoint(Value, Prob)); - } // for itr - Points[] - - SetPoints(Pts); - return true; -} - - - - - -int cProbabDistrib::Random(MTRand & a_Rand) const -{ - int v = a_Rand.randInt(m_Sum); - return MapValue(v); -} - - - - - -int cProbabDistrib::MapValue(int a_OrigValue) const -{ - ASSERT(a_OrigValue >= 0); - ASSERT(a_OrigValue < m_Sum); - - // Binary search through m_Cumulative for placement: - size_t Lo = 0; - size_t Hi = m_Cumulative.size() - 1; - while (Hi - Lo > 1) - { - int Mid = (Lo + Hi) / 2; - int MidProbab = m_Cumulative[Mid].m_Probability; - if (MidProbab < a_OrigValue) - { - Lo = Mid; - } - else - { - Hi = Mid; - } - } - ASSERT(Hi - Lo == 1); - - // Linearly interpolate between Lo and Hi: - int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability; - int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value; - return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif; -} - - - - diff --git a/source/ProbabDistrib.h b/source/ProbabDistrib.h deleted file mode 100644 index ddaadd9b7..000000000 --- a/source/ProbabDistrib.h +++ /dev/null @@ -1,74 +0,0 @@ - -// ProbabDistrib.h - -// Declares the cProbabDistrib class representing a discrete probability distribution curve and random generator - -/* -Usage: -1, Create a cProbabDistrib instance -2, Initialize the distribution either programmatically, using the SetPoints() function, or using a definition string -3, Ask for random numbers in that probability distribution using the Random() function -*/ - - - - - -#pragma once - - - - - -// fwd: -class MTRand; - - - - - -class cProbabDistrib -{ -public: - class cPoint - { - public: - int m_Value; - int m_Probability; - - cPoint(int a_Value, int a_Probability) : - m_Value(a_Value), - m_Probability(a_Probability) - { - } - } ; - - typedef std::vector cPoints; - - - cProbabDistrib(int a_MaxValue); - - /// Sets the distribution curve using an array of [value, probability] points, linearly interpolated. a_Points must not be empty. - void SetPoints(const cPoints & a_Points); - - /// Sets the distribution curve using a definition string; returns true on successful parse - bool SetDefString(const AString & a_DefString); - - /// Gets a random value from a_Rand, shapes it into the distribution curve and returns the value. - int Random(MTRand & a_Rand) const; - - /// Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability - int MapValue(int a_OrigValue) const; - - int GetSum(void) const { return m_Sum; } - -protected: - - int m_MaxValue; - cPoints m_Cumulative; ///< Cumulative probability of the values, sorted, for fast bsearch lookup - int m_Sum; ///< Sum of all the probabilities across all values in the domain; -1 if not set -} ; - - - - diff --git a/source/Protocol/ChunkDataSerializer.cpp b/source/Protocol/ChunkDataSerializer.cpp deleted file mode 100644 index 2a9230fee..000000000 --- a/source/Protocol/ChunkDataSerializer.cpp +++ /dev/null @@ -1,176 +0,0 @@ - -// ChunkDataSerializer.cpp - -// Implements the cChunkDataSerializer class representing the object that can: -// - serialize chunk data to different protocol versions -// - cache such serialized data for multiple clients - -#include "Globals.h" -#include "ChunkDataSerializer.h" -#include "zlib.h" - - - - -cChunkDataSerializer::cChunkDataSerializer( - const cChunkDef::BlockTypes & a_BlockTypes, - const cChunkDef::BlockNibbles & a_BlockMetas, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_BlockSkyLight, - const unsigned char * a_BiomeData -) : - m_BlockTypes(a_BlockTypes), - m_BlockMetas(a_BlockMetas), - m_BlockLight(a_BlockLight), - m_BlockSkyLight(a_BlockSkyLight), - m_BiomeData(a_BiomeData) -{ -} - - - - -const AString & cChunkDataSerializer::Serialize(int a_Version) -{ - Serializations::const_iterator itr = m_Serializations.find(a_Version); - if (itr != m_Serializations.end()) - { - return itr->second; - } - - AString data; - switch (a_Version) - { - case RELEASE_1_2_5: Serialize29(data); break; - case RELEASE_1_3_2: Serialize39(data); break; - // TODO: Other protocol versions may serialize the data differently; implement here - - default: - { - LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version); - ASSERT(!"Unknown chunk data serialization version"); - break; - } - } - m_Serializations[a_Version] = data; - return m_Serializations[a_Version]; -} - - - - - -void cChunkDataSerializer::Serialize29(AString & a_Data) -{ - // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) - - const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - const int MetadataOffset = sizeof(m_BlockTypes); - const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); - const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); - const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); - const int DataSize = BiomeOffset + BiomeDataSize; - - // Temporary buffer for the composed data: - char AllData [DataSize]; - - memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); - memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); - memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); - memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); - memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); - - // Compress the data: - // In order not to use allocation, use a fixed-size buffer, with the size - // that uses the same calculation as compressBound(): - const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; - char CompressedBlockData[CompressedMaxSize]; - - uLongf CompressedSize = compressBound(DataSize); - - // Run-time check that our compile-time guess about CompressedMaxSize was enough: - ASSERT(CompressedSize <= CompressedMaxSize); - - compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); - - // Now put all those data into a_Data: - - // "Ground-up continuous", or rather, "biome data present" flag: - a_Data.push_back('\x01'); - - // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively - // Also, no endian flipping is needed because of the const values - unsigned short BitMap1 = 0xffff; - unsigned short BitMap2 = 0; - a_Data.append((const char *)&BitMap1, sizeof(short)); - a_Data.append((const char *)&BitMap2, sizeof(short)); - - Int32 CompressedSizeBE = htonl(CompressedSize); - a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); - - Int32 UnusedInt32 = 0; - a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32)); - - a_Data.append(CompressedBlockData, CompressedSize); -} - - - - - -void cChunkDataSerializer::Serialize39(AString & a_Data) -{ - // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) - - const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - const int MetadataOffset = sizeof(m_BlockTypes); - const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); - const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); - const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); - const int DataSize = BiomeOffset + BiomeDataSize; - - // Temporary buffer for the composed data: - char AllData [DataSize]; - - memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); - memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); - memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); - memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); - memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); - - // Compress the data: - // In order not to use allocation, use a fixed-size buffer, with the size - // that uses the same calculation as compressBound(): - const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; - char CompressedBlockData[CompressedMaxSize]; - - uLongf CompressedSize = compressBound(DataSize); - - // Run-time check that our compile-time guess about CompressedMaxSize was enough: - ASSERT(CompressedSize <= CompressedMaxSize); - - compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); - - // Now put all those data into a_Data: - - // "Ground-up continuous", or rather, "biome data present" flag: - a_Data.push_back('\x01'); - - // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively - // Also, no endian flipping is needed because of the const values - unsigned short BitMap1 = 0xffff; - unsigned short BitMap2 = 0; - a_Data.append((const char *)&BitMap1, sizeof(short)); - a_Data.append((const char *)&BitMap2, sizeof(short)); - - Int32 CompressedSizeBE = htonl(CompressedSize); - a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); - - // Unlike 29, 39 doesn't have the "unused" int - - a_Data.append(CompressedBlockData, CompressedSize); -} - - - - diff --git a/source/Protocol/ChunkDataSerializer.h b/source/Protocol/ChunkDataSerializer.h deleted file mode 100644 index a42856356..000000000 --- a/source/Protocol/ChunkDataSerializer.h +++ /dev/null @@ -1,48 +0,0 @@ - -// ChunkDataSerializer.h - -// Interfaces to the cChunkDataSerializer class representing the object that can: -// - serialize chunk data to different protocol versions -// - cache such serialized data for multiple clients - - - - - -class cChunkDataSerializer -{ -protected: - const cChunkDef::BlockTypes & m_BlockTypes; - const cChunkDef::BlockNibbles & m_BlockMetas; - const cChunkDef::BlockNibbles & m_BlockLight; - const cChunkDef::BlockNibbles & m_BlockSkyLight; - const unsigned char * m_BiomeData; - - typedef std::map Serializations; - - Serializations m_Serializations; - - void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5 - void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2 - -public: - enum - { - RELEASE_1_2_5 = 29, - RELEASE_1_3_2 = 39, - } ; - - cChunkDataSerializer( - const cChunkDef::BlockTypes & a_BlockTypes, - const cChunkDef::BlockNibbles & a_BlockMetas, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_BlockSkyLight, - const unsigned char * a_BiomeData - ); - - const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[] -} ; - - - - diff --git a/source/Protocol/Protocol.h b/source/Protocol/Protocol.h deleted file mode 100644 index 5023ea227..000000000 --- a/source/Protocol/Protocol.h +++ /dev/null @@ -1,215 +0,0 @@ - -// Protocol.h - -// Interfaces to the cProtocol class representing the generic interface that a protocol -// parser and serializer must implement - - - - - -#pragma once - -#include "../Defines.h" -#include "../Endianness.h" - - - - -class cPlayer; -class cEntity; -class cWindow; -class cInventory; -class cPawn; -class cPickup; -class cMonster; -class cChunkDataSerializer; -class cWorld; -class cFallingBlock; - - - - - -typedef unsigned char Byte; - - - - - -class cProtocol -{ -public: - cProtocol(cClientHandle * a_Client) : - m_Client(a_Client) - { - } - virtual ~cProtocol() {} - - /// Called when client sends some data - virtual void DataReceived(const char * a_Data, int a_Size) = 0; - - // Sending stuff to clients (alphabetically sorted): - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0; - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0; - virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0; - virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0; - virtual void SendChat (const AString & a_Message) = 0; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0; - virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; - virtual void SendDisconnect (const AString & a_Reason) = 0; - virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+) - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0; - virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0; - virtual void SendEntityLook (const cEntity & a_Entity) = 0; - virtual void SendEntityMetadata (const cEntity & a_Entity) = 0; - virtual void SendEntityProperties (const cEntity & a_Entity) = 0; - virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; - virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; - virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0; - virtual void SendEntityVelocity (const cEntity & a_Entity) = 0; - virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0; - virtual void SendGameMode (eGameMode a_GameMode) = 0; - virtual void SendHealth (void) = 0; - virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0; - virtual void SendKeepAlive (int a_PingID) = 0; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0; - virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; - virtual void SendPlayerAbilities (void) = 0; - virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0; - virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0; - virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+) - virtual void SendPlayerMoveLook (void) = 0; - virtual void SendPlayerPosition (void) = 0; - virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; - virtual void SendRespawn (void) = 0; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 - virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; - virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; - virtual void SendSpawnMob (const cMonster & a_Mob) = 0; - virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0; - virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) = 0; - virtual void SendTabCompletionResults(const AStringVector & a_Results) = 0; - virtual void SendTeleportEntity (const cEntity & a_Entity) = 0; - virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0; - virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0; - virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0; - virtual void SendWeather (eWeather a_Weather) = 0; - virtual void SendWholeInventory (const cWindow & a_Window) = 0; - virtual void SendWindowClose (const cWindow & a_Window) = 0; - virtual void SendWindowOpen (const cWindow & a_Window) = 0; - virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) = 0; - - /// Returns the ServerID used for authentication through session.minecraft.net - virtual AString GetAuthServerID(void) = 0; - -protected: - cClientHandle * m_Client; - cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once - - /// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it - virtual void SendData(const char * a_Data, int a_Size) = 0; - - /// Called after writing each packet, enables descendants to flush their buffers - virtual void Flush(void) {}; - - // Helpers for writing partial packet data, write using SendData() - void WriteByte(Byte a_Value) - { - SendData((const char *)&a_Value, 1); - } - - void WriteShort(short a_Value) - { - a_Value = htons(a_Value); - SendData((const char *)&a_Value, 2); - } - - /* - void WriteShort(unsigned short a_Value) - { - a_Value = htons(a_Value); - SendData((const char *)&a_Value, 2); - } - */ - - void WriteInt(int a_Value) - { - a_Value = htonl(a_Value); - SendData((const char *)&a_Value, 4); - } - - void WriteUInt(unsigned int a_Value) - { - a_Value = htonl(a_Value); - SendData((const char *)&a_Value, 4); - } - - void WriteInt64 (Int64 a_Value) - { - a_Value = HostToNetwork8(&a_Value); - SendData((const char *)&a_Value, 8); - } - - void WriteFloat (float a_Value) - { - unsigned int val = HostToNetwork4(&a_Value); - SendData((const char *)&val, 4); - } - - void WriteDouble(double a_Value) - { - unsigned long long val = HostToNetwork8(&a_Value); - SendData((const char *)&val, 8); - } - - void WriteString(const AString & a_Value) - { - AString UTF16; - UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16); - WriteShort((unsigned short)(UTF16.size() / 2)); - SendData(UTF16.data(), UTF16.size()); - } - - void WriteBool(bool a_Value) - { - WriteByte(a_Value ? 1 : 0); - } - - void WriteVectorI(const Vector3i & a_Vector) - { - WriteInt(a_Vector.x); - WriteInt(a_Vector.y); - WriteInt(a_Vector.z); - } - - void WriteVarInt(UInt32 a_Value) - { - // A 32-bit integer can be encoded by at most 5 bytes: - unsigned char b[5]; - int idx = 0; - do - { - b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00); - a_Value = a_Value >> 7; - idx++; - } while (a_Value > 0); - - SendData((const char *)b, idx); - } - - void WriteVarUTF8String(const AString & a_String) - { - WriteVarInt(a_String.size()); - SendData(a_String.data(), a_String.size()); - } -} ; - - - - - diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp deleted file mode 100644 index 9f2770815..000000000 --- a/source/Protocol/Protocol125.cpp +++ /dev/null @@ -1,1869 +0,0 @@ - -// Protocol125.cpp - -// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29) -/* -Documentation: - - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513 - - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262 - - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152 -*/ - -#include "Globals.h" - -#include "Protocol125.h" - -#include "../ClientHandle.h" -#include "../World.h" -#include "ChunkDataSerializer.h" -#include "../Entities/Entity.h" -#include "../Mobs/Monster.h" -#include "../Entities/Pickup.h" -#include "../Entities/Player.h" -#include "../ChatColor.h" -#include "../UI/Window.h" -#include "../Root.h" -#include "../Server.h" - -#include "../Entities/ProjectileEntity.h" -#include "../Entities/Minecart.h" -#include "../Entities/FallingBlock.h" - -#include "../Mobs/IncludeAllMonsters.h" - - - - - -enum -{ - PACKET_KEEP_ALIVE = 0x00, - PACKET_LOGIN = 0x01, - PACKET_HANDSHAKE = 0x02, - PACKET_CHAT = 0x03, - PACKET_UPDATE_TIME = 0x04, - PACKET_ENTITY_EQUIPMENT = 0x05, - PACKET_USE_ENTITY = 0x07, - PACKET_UPDATE_HEALTH = 0x08, - PACKET_RESPAWN = 0x09, - PACKET_PLAYER_ON_GROUND = 0x0a, - PACKET_PLAYER_POS = 0x0b, - PACKET_PLAYER_LOOK = 0x0c, - PACKET_PLAYER_MOVE_LOOK = 0x0d, - PACKET_BLOCK_DIG = 0x0e, - PACKET_BLOCK_PLACE = 0x0f, - PACKET_SLOT_SELECTED = 0x10, - PACKET_USE_BED = 0x11, - PACKET_ANIMATION = 0x12, - PACKET_PACKET_ENTITY_ACTION = 0x13, - PACKET_PLAYER_SPAWN = 0x14, - PACKET_PICKUP_SPAWN = 0x15, - PACKET_COLLECT_PICKUP = 0x16, - PACKET_SPAWN_OBJECT = 0x17, - PACKET_SPAWN_MOB = 0x18, - PACKET_ENTITY_VELOCITY = 0x1c, - PACKET_DESTROY_ENTITY = 0x1d, - PACKET_ENTITY = 0x1e, - PACKET_ENT_REL_MOVE = 0x1f, - PACKET_ENT_LOOK = 0x20, - PACKET_ENT_REL_MOVE_LOOK = 0x21, - PACKET_ENT_TELEPORT = 0x22, - PACKET_ENT_HEAD_LOOK = 0x23, - PACKET_ENT_STATUS = 0x26, - PACKET_ATTACH_ENTITY = 0x27, - PACKET_METADATA = 0x28, - PACKET_PRE_CHUNK = 0x32, - PACKET_MAP_CHUNK = 0x33, - PACKET_MULTI_BLOCK = 0x34, - PACKET_BLOCK_CHANGE = 0x35, - PACKET_BLOCK_ACTION = 0x36, - PACKET_EXPLOSION = 0x3C, - PACKET_SOUND_EFFECT = 0x3e, - PACKET_SOUND_PARTICLE_EFFECT = 0x3d, - PACKET_CHANGE_GAME_STATE = 0x46, - PACKET_THUNDERBOLT = 0x47, - PACKET_WINDOW_OPEN = 0x64, - PACKET_WINDOW_CLOSE = 0x65, - PACKET_WINDOW_CLICK = 0x66, - PACKET_INVENTORY_SLOT = 0x67, - PACKET_INVENTORY_WHOLE = 0x68, - PACKET_WINDOW_PROPERTY = 0x69, - PACKET_CREATIVE_INVENTORY_ACTION = 0x6B, - PACKET_UPDATE_SIGN = 0x82, - PACKET_PLAYER_LIST_ITEM = 0xC9, - PACKET_PLAYER_ABILITIES = 0xca, - PACKET_PLUGIN_MESSAGE = 0xfa, - PACKET_PING = 0xfe, - PACKET_DISCONNECT = 0xff -} ; - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - -typedef unsigned char Byte; - - - - - -cProtocol125::cProtocol125(cClientHandle * a_Client) : - super(a_Client), - m_ReceivedData(32 KiB) -{ -} - - - - - -void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ATTACH_ENTITY); - WriteInt(a_Entity.GetUniqueID()); - WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); - Flush(); -} - - - - - -void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - UNUSED(a_BlockType); - - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_BLOCK_ACTION); - WriteInt (a_BlockX); - WriteShort((short)a_BlockY); - WriteInt (a_BlockZ); - WriteByte (a_Byte1); - WriteByte (a_Byte2); - Flush(); -} - - - - - -void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) -{ - // Not supported in this protocol version -} - - - - - -void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_BLOCK_CHANGE); - WriteInt (a_BlockX); - WriteByte((unsigned char)a_BlockY); - WriteInt (a_BlockZ); - WriteByte(a_BlockType); - WriteByte(a_BlockMeta); - Flush(); -} - - - - - -void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) -{ - cCSLock Lock(m_CSPacket); - if (a_Changes.size() == 1) - { - // Special packet for single-block changes - const sSetBlock & blk = a_Changes.front(); - SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta); - return; - } - - WriteByte (PACKET_MULTI_BLOCK); - WriteInt (a_ChunkX); - WriteInt (a_ChunkZ); - WriteShort((unsigned short)a_Changes.size()); - WriteUInt (sizeof(int) * a_Changes.size()); - for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) - { - unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); - unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); - WriteUInt(Coords << 16 | Blocks); - } - Flush(); -} - - - - - -void cProtocol125::SendChat(const AString & a_Message) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_CHAT); - WriteString(a_Message); - Flush(); -} - - - - - -void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - cCSLock Lock(m_CSPacket); - - // Send the pre-chunk: - SendPreChunk(a_ChunkX, a_ChunkZ, true); - - // Send the chunk data: - AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5); - WriteByte(PACKET_MAP_CHUNK); - WriteInt (a_ChunkX); - WriteInt (a_ChunkZ); - SendData(Serialized.data(), Serialized.size()); - Flush(); -} - - - - - -void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_COLLECT_PICKUP); - WriteInt (a_Pickup.GetUniqueID()); - WriteInt (a_Player.GetUniqueID()); - Flush(); -} - - - - - -void cProtocol125::SendDestroyEntity(const cEntity & a_Entity) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_DESTROY_ENTITY); - WriteInt (a_Entity.GetUniqueID()); - Flush(); -} - - - - - -void cProtocol125::SendDisconnect(const AString & a_Reason) -{ - cCSLock Lock(m_CSPacket); - WriteByte ((unsigned char)PACKET_DISCONNECT); - WriteString(a_Reason); - Flush(); -} - - - - - -void cProtocol125::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // This protocol version doesn't support this packet, sign editor is invoked by the client automatically - UNUSED(a_BlockX); - UNUSED(a_BlockY); - UNUSED(a_BlockZ); -} - - - - - -void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_ENTITY_EQUIPMENT); - WriteInt (a_Entity.GetUniqueID()); - WriteShort(a_SlotNum); - WriteShort(a_Item.m_ItemType); - WriteShort(a_Item.m_ItemDamage); - Flush(); -} - - - - - -void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_HEAD_LOOK); - WriteInt (a_Entity.GetUniqueID()); - WriteByte((char)((a_Entity.GetHeadYaw() / 360.f) * 256)); - Flush(); -} - - - - - -void cProtocol125::SendEntityLook(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_LOOK); - WriteInt (a_Entity.GetUniqueID()); - WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); - WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); - Flush(); -} - - - - - -void cProtocol125::SendEntityMetadata(const cEntity & a_Entity) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_METADATA); - WriteInt (a_Entity.GetUniqueID()); - - WriteCommonMetadata(a_Entity); - if (a_Entity.IsMob()) - { - WriteMobMetadata(((const cMonster &)a_Entity)); - } - else - { - WriteEntityMetadata(a_Entity); - } - WriteByte(0x7f); - - Flush(); -} - - - - - -void cProtocol125::SendEntityProperties(const cEntity & a_Entity) -{ - // Not supported in this protocol version -} - - - - - -void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_REL_MOVE); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_RelX); - WriteByte(a_RelY); - WriteByte(a_RelZ); - Flush(); -} - - - - - -void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_REL_MOVE_LOOK); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_RelX); - WriteByte(a_RelY); - WriteByte(a_RelZ); - WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); - WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); - Flush(); -} - - - - - -void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_STATUS); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_Status); - Flush(); -} - - - - - -void cProtocol125::SendEntityVelocity(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENTITY_VELOCITY); - WriteInt (a_Entity.GetUniqueID()); - WriteShort((short) (a_Entity.GetSpeedX() * 400)); //400 = 8000 / 20 - WriteShort((short) (a_Entity.GetSpeedY() * 400)); - WriteShort((short) (a_Entity.GetSpeedZ() * 400)); - Flush(); -} - - - - - -void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_EXPLOSION); - WriteDouble (a_BlockX); - WriteDouble (a_BlockY); - WriteDouble (a_BlockZ); - WriteFloat (a_Radius); - WriteInt (a_BlocksAffected.size()); - int BlockX = (int)a_BlockX; - int BlockY = (int)a_BlockY; - int BlockZ = (int)a_BlockZ; - for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr) - { - WriteByte((Byte)(itr->x - BlockX)); - WriteByte((Byte)(itr->y - BlockY)); - WriteByte((Byte)(itr->z - BlockZ)); - } - WriteFloat((float)a_PlayerMotion.x); - WriteFloat((float)a_PlayerMotion.y); - WriteFloat((float)a_PlayerMotion.z); - Flush(); -} - - - - - -void cProtocol125::SendGameMode(eGameMode a_GameMode) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_CHANGE_GAME_STATE); - WriteByte(3); - WriteByte((char)a_GameMode); - Flush(); -} - - - - - -void cProtocol125::SendHandshake(const AString & a_ConnectionHash) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_HANDSHAKE); - WriteString(a_ConnectionHash); - Flush(); -} - - - - - -void cProtocol125::SendHealth(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_UPDATE_HEALTH); - WriteShort((short)m_Client->GetPlayer()->GetHealth()); - WriteShort(m_Client->GetPlayer()->GetFoodLevel()); - WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); - Flush(); -} - - - - - -void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_INVENTORY_SLOT); - WriteByte (a_WindowID); - WriteShort(a_SlotNum); - WriteItem (a_Item); - Flush(); -} - - - - - -void cProtocol125::SendKeepAlive(int a_PingID) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_KEEP_ALIVE); - WriteInt (a_PingID); - Flush(); -} - - - - - -void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World) -{ - UNUSED(a_World); - cCSLock Lock(m_CSPacket); - - WriteByte (PACKET_LOGIN); - WriteInt (a_Player.GetUniqueID()); // EntityID of the player - WriteString(""); // Username, not used - WriteString("default"); // Level type - WriteInt ((int)a_Player.GetGameMode()); - WriteInt ((int)(a_World.GetDimension())); - WriteByte (2); // TODO: Difficulty - WriteByte (0); // Unused - WriteByte (60); // Client list width or something - Flush(); -} - - - - - -void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_PICKUP_SPAWN); - WriteInt (a_Pickup.GetUniqueID()); - WriteShort (a_Pickup.GetItem().m_ItemType); - WriteByte (a_Pickup.GetItem().m_ItemCount); - WriteShort (a_Pickup.GetItem().m_ItemDamage); - WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); - WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); - WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); - WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); - Flush(); -} - - - - - -void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ANIMATION); - WriteInt (a_Player.GetUniqueID()); - WriteByte(a_Animation); - Flush(); -} - - - - - -void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) -{ - cCSLock Lock(m_CSPacket); - AString PlayerName(a_Player.GetColor()); - PlayerName.append(a_Player.GetName()); - if (PlayerName.length() > 14) - { - PlayerName.erase(14); - } - PlayerName += cChatColor::White; - - WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM); - WriteString(PlayerName); - WriteBool (a_IsOnline); - WriteShort (a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0); - Flush(); -} - - - - - -void cProtocol125::SendPlayerMaxSpeed(void) -{ - // Not supported by this protocol version -} - - - - - -void cProtocol125::SendPlayerMoveLook(void) -{ - cCSLock Lock(m_CSPacket); - - /* - LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", - m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 - ); - */ - - WriteByte (PACKET_PLAYER_MOVE_LOOK); - cPlayer * Player = m_Client->GetPlayer(); - WriteDouble(Player->GetPosX()); - WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block - WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block - WriteDouble(Player->GetPosZ()); - WriteFloat ((float)(Player->GetRotation())); - WriteFloat ((float)(Player->GetPitch())); - WriteBool (Player->IsOnGround()); - Flush(); -} - - - - - -void cProtocol125::SendPlayerPosition(void) -{ - cCSLock Lock(m_CSPacket); - LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now -} - - - - - -void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player) -{ - const cItem & HeldItem = a_Player.GetEquippedItem(); - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_PLAYER_SPAWN); - WriteInt (a_Player.GetUniqueID()); - WriteString(a_Player.GetName()); - WriteInt ((int)(a_Player.GetPosX() * 32)); - WriteInt ((int)(a_Player.GetPosY() * 32)); - WriteInt ((int)(a_Player.GetPosZ() * 32)); - WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); - WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); - WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); - Flush(); -} - - - - - -void cProtocol125::SendRespawn(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_RESPAWN); - WriteInt ((int)(m_Client->GetPlayer()->GetWorld()->GetDimension())); - WriteByte (2); // TODO: Difficulty; 2 = Normal - WriteByte ((char)m_Client->GetPlayer()->GetGameMode()); - WriteShort (256); // Current world height - WriteString("default"); -} - - - - - -void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) -{ - // Not needed in this protocol version -} - - - - - -void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - // Not implemented in this protocol version -} - - - - - -void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) -{ - // This protocol version implements falling blocks using the spawn object / vehicle packet: - SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0); -} - - - - - -void cProtocol125::SendSpawnMob(const cMonster & a_Mob) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_SPAWN_MOB); - WriteInt (a_Mob.GetUniqueID()); - WriteByte (a_Mob.GetMobType()); - WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); - WriteByte (0); - WriteByte (0); - WriteByte (0); - - WriteCommonMetadata(a_Mob); - WriteMobMetadata(a_Mob); - WriteByte(0x7f); - - Flush(); -} - - - - - -void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) -{ - UNUSED(a_Yaw); - UNUSED(a_Pitch); - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_SPAWN_OBJECT); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_ObjectType); - WriteInt ((int)(a_Entity.GetPosX() * 32)); - WriteInt ((int)(a_Entity.GetPosY() * 32)); - WriteInt ((int)(a_Entity.GetPosZ() * 32)); - WriteByte(a_Pitch); - WriteByte(a_Yaw); - WriteInt (a_ObjectData); - if (a_ObjectData != 0) - { - WriteShort((short)(a_Entity.GetSpeedX() * 400)); - WriteShort((short)(a_Entity.GetSpeedY() * 400)); - WriteShort((short)(a_Entity.GetSpeedZ() * 400)); - } - Flush(); -} - - - - - -void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_SPAWN_OBJECT); - WriteInt (a_Vehicle.GetUniqueID()); - WriteByte (a_VehicleType); - WriteInt ((int)(a_Vehicle.GetPosX() * 32)); - WriteInt ((int)(a_Vehicle.GetPosY() * 32)); - WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); - WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); - WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); - WriteInt (a_VehicleSubType); - if (a_VehicleSubType != 0) - { - WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); - WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); - WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); - } - Flush(); -} - - - - - -void cProtocol125::SendTabCompletionResults(const AStringVector & a_Results) -{ - // This protocol version doesn't support tab completion - UNUSED(a_Results); -} - - - - - -void cProtocol125::SendTeleportEntity(const cEntity & a_Entity) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_ENT_TELEPORT); - WriteInt (a_Entity.GetUniqueID()); - WriteInt ((int)(floor(a_Entity.GetPosX() * 32))); - WriteInt ((int)(floor(a_Entity.GetPosY() * 32))); - WriteInt ((int)(floor(a_Entity.GetPosZ() * 32))); - WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256)); - WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256)); - Flush(); -} - - - - - -void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_THUNDERBOLT); - WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one - WriteBool(true); // Unknown bool - WriteInt (a_BlockX * 32); - WriteInt (a_BlockY * 32); - WriteInt (a_BlockZ * 32); - Flush(); -} - - - - - -void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_UPDATE_TIME); - // Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day: - WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000)); - Flush(); -} - - - - - -void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSPacket); - SendPreChunk(a_ChunkX, a_ChunkZ, false); -} - - - - - -void cProtocol125::SendUpdateSign( - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 -) -{ - cCSLock Lock(m_CSPacket); - WriteByte ((unsigned char)PACKET_UPDATE_SIGN); - WriteInt (a_BlockX); - WriteShort ((short)a_BlockY); - WriteInt (a_BlockZ); - WriteString(a_Line1); - WriteString(a_Line2); - WriteString(a_Line3); - WriteString(a_Line4); - Flush(); -} - - - - - -void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_USE_BED); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(0); // Unknown byte only 0 has been observed - WriteInt (a_BlockX); - WriteByte(a_BlockY); - WriteInt (a_BlockZ); - Flush(); -} - - - - - -void cProtocol125::SendWeather(eWeather a_Weather) -{ - cCSLock Lock(m_CSPacket); - switch( a_Weather ) - { - case eWeather_Sunny: - { - WriteByte(PACKET_CHANGE_GAME_STATE); - WriteByte(2); // Stop rain - WriteByte(0); // Unused - Flush(); - break; - } - - case eWeather_Rain: - case eWeather_ThunderStorm: - { - WriteByte(PACKET_CHANGE_GAME_STATE); - WriteByte(1); // Begin rain - WriteByte(0); // Unused - Flush(); - break; - } - } -} - - - - - -void cProtocol125::SendWholeInventory(const cWindow & a_Window) -{ - cCSLock Lock(m_CSPacket); - cItems Slots; - a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); - SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0])); -} - - - - - -void cProtocol125::SendWindowClose(const cWindow & a_Window) -{ - if (a_Window.GetWindowType() == cWindow::wtInventory) - { - // Do not send inventory-window-close - return; - } - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_WINDOW_CLOSE); - WriteByte(a_Window.GetWindowID()); - Flush(); -} - - - - - -void cProtocol125::SendWindowOpen(const cWindow & a_Window) -{ - if (a_Window.GetWindowType() < 0) - { - // Do not send for inventory windows - return; - } - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_WINDOW_OPEN); - WriteByte (a_Window.GetWindowID()); - WriteByte (a_Window.GetWindowType()); - WriteString(a_Window.GetWindowTitle()); - WriteByte (a_Window.GetNumNonInventorySlots()); - Flush(); -} - - - - - -void cProtocol125::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_WINDOW_PROPERTY); - WriteByte (a_Window.GetWindowID()); - WriteShort(a_Property); - WriteShort(a_Value); - Flush(); -} - - - - - -AString cProtocol125::GetAuthServerID(void) -{ - // http://wiki.vg/wiki/index.php?title=Session&oldid=2262 - // The server generates a random hash and that is used for all clients, unmodified - return cRoot::Get()->GetServer()->GetServerID(); -} - - - - - -void cProtocol125::SendData(const char * a_Data, int a_Size) -{ - m_Client->SendData(a_Data, a_Size); -} - - - - - -void cProtocol125::DataReceived(const char * a_Data, int a_Size) -{ - if (!m_ReceivedData.Write(a_Data, a_Size)) - { - // Too much data in the incoming queue, report to caller: - m_Client->PacketBufferFull(); - return; - } - - // Parse and handle all complete packets in m_ReceivedData: - while (m_ReceivedData.CanReadBytes(1)) - { - unsigned char PacketType; - m_ReceivedData.ReadByte(PacketType); - switch (ParsePacket(PacketType)) - { - case PARSE_UNKNOWN: - { - // An unknown packet has been received, notify the client and abort: - m_Client->PacketUnknown(PacketType); - return; - } - case PARSE_ERROR: - { - // An error occurred while parsing a known packet, notify the client and abort: - m_Client->PacketError(PacketType); - return; - } - case PARSE_INCOMPLETE: - { - // Incomplete packet, bail out and process with the next batch of data - m_ReceivedData.ResetRead(); - return; - } - default: - { - // Packet successfully parsed, commit the read data and try again one more packet - m_ReceivedData.CommitRead(); - break; - } - } - } -} - - - - - -int cProtocol125::ParsePacket(unsigned char a_PacketType) -{ - switch (a_PacketType) - { - default: return PARSE_UNKNOWN; - case PACKET_ANIMATION: return ParseArmAnim(); - case PACKET_BLOCK_DIG: return ParseBlockDig(); - case PACKET_BLOCK_PLACE: return ParseBlockPlace(); - case PACKET_CHAT: return ParseChat(); - case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction(); - case PACKET_DISCONNECT: return ParseDisconnect(); - case PACKET_HANDSHAKE: return ParseHandshake(); - case PACKET_KEEP_ALIVE: return ParseKeepAlive(); - case PACKET_LOGIN: return ParseLogin(); - case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction(); - case PACKET_PING: return ParsePing(); - case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities(); - case PACKET_PLAYER_LOOK: return ParsePlayerLook(); - case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook(); - case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround(); - case PACKET_PLAYER_POS: return ParsePlayerPosition(); - case PACKET_PLUGIN_MESSAGE: return ParsePluginMessage(); - case PACKET_RESPAWN: return ParseRespawn(); - case PACKET_SLOT_SELECTED: return ParseSlotSelected(); - case PACKET_UPDATE_SIGN: return ParseUpdateSign(); - case PACKET_USE_ENTITY: return ParseUseEntity(); - case PACKET_WINDOW_CLICK: return ParseWindowClick(); - case PACKET_WINDOW_CLOSE: return ParseWindowClose(); - } -} - - - - - -#define HANDLE_PACKET_PARSE(Packet) \ - { \ - int res = Packet.Parse(m_ReceivedData); \ - if (res < 0) \ - { \ - return res; \ - } \ - } - - - - - -int cProtocol125::ParseArmAnim(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_PACKET_READ(ReadChar, char, Animation); - m_Client->HandleAnimation(Animation); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseBlockDig(void) -{ - HANDLE_PACKET_READ(ReadChar, char, Status); - HANDLE_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_PACKET_READ(ReadByte, Byte, PosY); - HANDLE_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_PACKET_READ(ReadChar, char, BlockFace); - m_Client->HandleLeftClick(PosX, PosY, PosZ, BlockFace, Status); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseBlockPlace(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_PACKET_READ(ReadByte, Byte, PosY); - HANDLE_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_PACKET_READ(ReadChar, char, BlockFace); - - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - - // 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable. - m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, 8, 8, 8, HeldItem); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseChat(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message); - m_Client->HandleChat(Message); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseCreativeInventoryAction(void) -{ - HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - m_Client->HandleCreativeInventory(SlotNum, HeldItem); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseDisconnect(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason); - m_Client->HandleDisconnect(Reason); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseEntityAction(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_PACKET_READ(ReadChar, char, ActionID); - m_Client->HandleEntityAction(EntityID, ActionID); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseHandshake(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); - - AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565" - if (UserData.empty()) - { - m_Client->Kick("Did not receive username"); - return PARSE_OK; - } - m_Username = UserData[0]; - - LOGD("HANDSHAKE %s", Username.c_str()); - - if (!m_Client->HandleHandshake( m_Username )) - { - return PARSE_OK; // Player is not allowed into the server - } - - SendHandshake(cRoot::Get()->GetServer()->GetServerID()); - LOGD("User \"%s\" was sent a handshake response", m_Username.c_str()); - - return PARSE_OK; -} - - - - - -int cProtocol125::ParseKeepAlive(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID); - m_Client->HandleKeepAlive(KeepAliveID); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseLogin(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); - HANDLE_PACKET_READ(ReadBEInt, int, ServerMode); - HANDLE_PACKET_READ(ReadBEInt, int, Dimension); - HANDLE_PACKET_READ(ReadChar, char, Difficulty); - HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight); - HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers); - - if (ProtocolVersion < 29) - { - m_Client->Kick("Your client is outdated!"); - return PARSE_OK; - } - else if (ProtocolVersion > 29) - { - m_Client->Kick("Your client version is higher than the server!"); - return PARSE_OK; - } - - if (m_Username.compare(Username) != 0) - { - LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking", - Username.c_str(), - m_Username.c_str(), - m_Client->GetIPString().c_str() - ); - m_Client->Kick("Hacked client"); // Don't tell them why we don't want them - return PARSE_OK; - } - - m_Client->HandleLogin(ProtocolVersion, Username); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePing(void) -{ - // Packet has no more data - m_Client->HandlePing(); - return PARSE_OK; -} - - - - - - -int cProtocol125::ParsePlayerAbilities(void) -{ - HANDLE_PACKET_READ(ReadBool, bool, Invulnerable); - HANDLE_PACKET_READ(ReadBool, bool, IsFlying); - HANDLE_PACKET_READ(ReadBool, bool, CanFly); - HANDLE_PACKET_READ(ReadBool, bool, InstaMine); - // TODO: m_Client->HandlePlayerAbilities(...); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePlayerLook(void) -{ - HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); - HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); - HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); - m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePlayerMoveLook(void) -{ - HANDLE_PACKET_READ(ReadBEDouble, double, PosX); - HANDLE_PACKET_READ(ReadBEDouble, double, PosY); - HANDLE_PACKET_READ(ReadBEDouble, double, Stance); - HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); - HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); - HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); - HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); - // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0); - m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePlayerOnGround(void) -{ - HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); - // TODO: m_Client->HandleFlying(IsOnGround); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePlayerPosition(void) -{ - HANDLE_PACKET_READ(ReadBEDouble, double, PosX); - HANDLE_PACKET_READ(ReadBEDouble, double, PosY); - HANDLE_PACKET_READ(ReadBEDouble, double, Stance); - HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); - HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); - m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePluginMessage(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); - HANDLE_PACKET_READ(ReadBEShort, short, Length); - AString Data; - if (!m_ReceivedData.ReadString(Data, Length)) - { - m_ReceivedData.CheckValid(); - return PARSE_INCOMPLETE; - } - m_ReceivedData.CheckValid(); - - // TODO: Process the data - LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str()); - - return PARSE_OK; -} - - - - - -int cProtocol125::ParseRespawn(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, Dimension); - HANDLE_PACKET_READ(ReadChar, char, Difficulty); - HANDLE_PACKET_READ(ReadChar, char, CreativeMode); - HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); - m_Client->HandleRespawn(); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseSlotSelected(void) -{ - HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); - m_Client->HandleSlotSelected(SlotNum); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseUpdateSign(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_PACKET_READ(ReadBEShort, short, BlockY); - HANDLE_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4); - m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseUseEntity(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID); - HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID); - HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick); - m_Client->HandleUseEntity(TargetEntityID, IsLeftClick); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseWindowClick(void) -{ - HANDLE_PACKET_READ(ReadChar, char, WindowID); - HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); - HANDLE_PACKET_READ(ReadBool, bool, IsRightClick); - HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); - HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed); - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - - // Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols: - eClickAction Action; - if (IsRightClick) - { - if (IsShiftPressed) - { - Action = caShiftRightClick; - } - else - { - if (SlotNum == -999) - { - Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside; - } - else - { - Action = caRightClick; - } - } - } - else - { - // IsLeftClick - if (IsShiftPressed) - { - Action = caShiftLeftClick; - } - else - { - if (SlotNum == -999) - { - Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside; - } - else - { - Action = caLeftClick; - } - } - } - m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseWindowClose(void) -{ - HANDLE_PACKET_READ(ReadChar, char, WindowID); - m_Client->HandleWindowClose(WindowID); - return PARSE_OK; -} - - - - - -void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad) -{ - WriteByte(PACKET_PRE_CHUNK); - WriteInt (a_ChunkX); - WriteInt (a_ChunkZ); - WriteBool(a_ShouldLoad); - Flush(); -} - - - - - -void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items) -{ - WriteByte (PACKET_INVENTORY_WHOLE); - WriteByte (a_WindowID); - WriteShort((short)a_NumItems); - - for (int j = 0; j < a_NumItems; j++) - { - WriteItem(a_Items[j]); - } - Flush(); -} - - - - - -void cProtocol125::WriteItem(const cItem & a_Item) -{ - short ItemType = a_Item.m_ItemType; - ASSERT(ItemType >= -1); // Check validity of packets in debug runtime - if (ItemType <= 0) - { - // Fix, to make sure no invalid values are sent. - ItemType = -1; - } - - WriteShort(ItemType); - if (a_Item.IsEmpty()) - { - return; - } - - WriteByte (a_Item.m_ItemCount); - WriteShort(a_Item.m_ItemDamage); - - if (cItem::IsEnchantable(a_Item.m_ItemType)) - { - // TODO: Implement enchantments - WriteShort(-1); - } -} - - - - - -int cProtocol125::ParseItem(cItem & a_Item) -{ - HANDLE_PACKET_READ(ReadBEShort, short, ItemType); - - if (ItemType <= -1) - { - a_Item.Empty(); - return PARSE_OK; - } - a_Item.m_ItemType = ItemType; - - HANDLE_PACKET_READ(ReadChar, char, ItemCount); - HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); - a_Item.m_ItemCount = ItemCount; - a_Item.m_ItemDamage = ItemDamage; - if (ItemCount <= 0) - { - a_Item.Empty(); - } - - if (!cItem::IsEnchantable(ItemType)) - { - return PARSE_OK; - } - - HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes); - - if (EnchantNumBytes <= 0) - { - return PARSE_OK; - } - - // TODO: Enchantment not implemented yet! - if (!m_ReceivedData.SkipRead(EnchantNumBytes)) - { - return PARSE_INCOMPLETE; - } - - return PARSE_OK; -} - - - - - -void cProtocol125::WriteCommonMetadata(const cEntity & a_Entity) -{ - Byte CommonMetadata = 0; - - if (a_Entity.IsOnFire()) - { - CommonMetadata |= 0x1; - } - if (a_Entity.IsCrouched()) - { - CommonMetadata |= 0x2; - } - if (a_Entity.IsRiding()) - { - CommonMetadata |= 0x4; - } - if (a_Entity.IsSprinting()) - { - CommonMetadata |= 0x8; - } - if (a_Entity.IsRclking()) - { - CommonMetadata |= 0x10; - } - if (a_Entity.IsInvisible()) - { - CommonMetadata |= 0x20; - } - - WriteByte(0x0); - WriteByte(CommonMetadata); -} - - - - - -void cProtocol125::WriteEntityMetadata(const cEntity & a_Entity) -{ - if (a_Entity.IsMinecart()) - { - WriteByte(0x51); - // No idea how Mojang makes their carts shakey shakey, so here is a complicated one-liner expression that does something similar - WriteInt( (((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4 ); - WriteByte(0x52); - WriteInt(1); // Shaking direction, doesn't seem to affect anything - WriteByte(0x73); - WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer - - if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) - { - WriteByte(0x10); - WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); // Fueled? - } - } - else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow)) - { - WriteByte(0x10); - WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); // Critical hitting arrow? - } -} - - - - - -void cProtocol125::WriteMobMetadata(const cMonster & a_Mob) -{ - switch (a_Mob.GetMobType()) - { - case cMonster::mtCreeper: - { - WriteByte(0x10); - WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); // Blowing up? - WriteByte(0x11); - WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); // Lightning-charged? - break; - } - case cMonster::mtBat: - { - WriteByte(0x10); - WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); // Upside down? - break; - } - case cMonster::mtPig: - { - WriteByte(0x10); - WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); // Saddled? - break; - } - case cMonster::mtVillager: - { - WriteByte(0x50); - WriteInt(((const cVillager &)a_Mob).GetVilType()); // What sort of TESTIFICATE? - break; - } - case cMonster::mtZombie: - { - WriteByte(0xC); - WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); // Babby zombie? - WriteByte(0xD); - WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); // Converted zombie? - WriteByte(0xE); - WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); // Converted-but-converting-back zombllager? - break; - } - case cMonster::mtGhast: - { - WriteByte(0x10); - WriteByte(((const cGhast &)a_Mob).IsCharging()); // About to eject un flamé-bol? :P - break; - } - case cMonster::mtWolf: - { - Byte WolfStatus = 0; - if (((const cWolf &)a_Mob).IsSitting()) - { - WolfStatus |= 0x1; - } - if (((const cWolf &)a_Mob).IsAngry()) - { - WolfStatus |= 0x2; - } - if (((const cWolf &)a_Mob).IsTame()) - { - WolfStatus |= 0x4; - } - WriteByte(0x10); - WriteByte(WolfStatus); - - WriteByte(0x72); - WriteFloat((float)(a_Mob.GetHealth())); // Tail health-o-meter (only shown when tamed, by the way) - WriteByte(0x13); - WriteByte(((const cWolf &)a_Mob).IsBegging() ? 1 : 0); // Ultra cute mode? - break; - } - case cMonster::mtSheep: - { - // [1](1111) - // [] = Is sheared? () = Color, from 0 to 15 - - WriteByte(0x10); - Byte SheepMetadata = 0; - SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); // Fur colour - - if (((const cSheep &)a_Mob).IsSheared()) // Is sheared? - { - SheepMetadata |= 0x16; - } - WriteByte(SheepMetadata); - break; - } - case cMonster::mtEnderman: - { - WriteByte(0x10); - WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); // Block that he stole from your house - WriteByte(0x11); - WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); // Meta of block that he stole from your house - WriteByte(0x12); - WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); // Screaming at your face? - break; - } - case cMonster::mtSkeleton: - { - WriteByte(0xD); - WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); // It's a skeleton, but it's not - break; - } - case cMonster::mtWitch: - { - WriteByte(0x15); - WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); // Aggravated? Doesn't seem to do anything - break; - } - case cMonster::mtSlime: - case cMonster::mtMagmaCube: - { - WriteByte(0x10); - if (a_Mob.GetMobType() == cMonster::mtSlime) - { - WriteByte(((const cSlime &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME - } - else - { - WriteByte(((const cMagmaCube &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME - } - break; - } - case cMonster::mtHorse: - { - int Flags = 0; - if (((const cHorse &)a_Mob).IsTame()) - { - Flags |= 0x2; - } - if (((const cHorse &)a_Mob).IsSaddled()) - { - Flags |= 0x4; - } - if (((const cHorse &)a_Mob).IsChested()) - { - Flags |= 0x8; - } - if (((const cHorse &)a_Mob).IsBaby()) - { - Flags |= 0x10; // IsBred flag, according to wiki.vg - don't think it does anything in multiplayer - } - if (((const cHorse &)a_Mob).IsEating()) - { - Flags |= 0x20; - } - if (((const cHorse &)a_Mob).IsRearing()) - { - Flags |= 0x40; - } - if (((const cHorse &)a_Mob).IsMthOpen()) - { - Flags |= 0x80; - } - WriteByte(0x50); - WriteInt(Flags); - - WriteByte(0x13); - WriteByte(((const cHorse &)a_Mob).GetHorseType()); // Type of horse (donkey, chestnut, etc.) - - WriteByte(0x54); - int Appearance = 0; - Appearance = ((const cHorse &)a_Mob).GetHorseColor(); // Mask FF - Appearance |= ((const cHorse &)a_Mob).GetHorseStyle() * 256; // Mask FF00, so multiply by 256 - WriteInt(Appearance); - - WriteByte(0x56); - WriteInt(((const cHorse &)a_Mob).GetHorseArmour()); // Horshey armour - break; - } - } -} - - - - diff --git a/source/Protocol/Protocol125.h b/source/Protocol/Protocol125.h deleted file mode 100644 index db913bb57..000000000 --- a/source/Protocol/Protocol125.h +++ /dev/null @@ -1,157 +0,0 @@ - -// Protocol125.h - -// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29) - - - - - -#pragma once - -#include "Protocol.h" -#include "../ByteBuffer.h" - - - - - -class cProtocol125 : - public cProtocol -{ - typedef cProtocol super; -public: - cProtocol125(cClientHandle * a_Client); - - /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; - - /// Sending stuff to clients (alphabetically sorted): - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; - virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; - virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; - virtual void SendChat (const AString & a_Message) override; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; - virtual void SendDestroyEntity (const cEntity & a_Entity) override; - virtual void SendDisconnect (const AString & a_Reason) override; - virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; - virtual void SendEntityHeadLook (const cEntity & a_Entity) override; - virtual void SendEntityLook (const cEntity & a_Entity) override; - virtual void SendEntityMetadata (const cEntity & a_Entity) override; - virtual void SendEntityProperties (const cEntity & a_Entity) override; - virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; - virtual void SendEntityVelocity (const cEntity & a_Entity) override; - virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; - virtual void SendGameMode (eGameMode a_GameMode) override; - virtual void SendHealth (void) override; - virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; - virtual void SendKeepAlive (int a_PingID) override; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; - virtual void SendPickupSpawn (const cPickup & a_Pickup) override; - virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message - virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; - virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; - virtual void SendPlayerMaxSpeed (void) override; - virtual void SendPlayerMoveLook (void) override; - virtual void SendPlayerPosition (void) override; - virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendRespawn (void) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 - virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; - virtual void SendSpawnMob (const cMonster & a_Mob) override; - virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; - virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; - virtual void SendTabCompletionResults(const AStringVector & a_Results) override; - virtual void SendTeleportEntity (const cEntity & a_Entity) override; - virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; - virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; - virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; - virtual void SendWeather (eWeather a_Weather) override; - virtual void SendWholeInventory (const cWindow & a_Window) override; - virtual void SendWindowClose (const cWindow & a_Window) override; - virtual void SendWindowOpen (const cWindow & a_Window) override; - virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; - - virtual AString GetAuthServerID(void) override; - -protected: - /// Results of packet-parsing: - enum { - PARSE_OK = 1, - PARSE_ERROR = -1, - PARSE_UNKNOWN = -2, - PARSE_INCOMPLETE = -3, - } ; - - cByteBuffer m_ReceivedData; ///< Buffer for the received data - - AString m_Username; ///< Stored in ParseHandshake(), compared to Login username - - virtual void SendData(const char * a_Data, int a_Size) override; - - /// Sends the Handshake packet - void SendHandshake(const AString & a_ConnectionHash); - - /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() ) - virtual int ParsePacket(unsigned char a_PacketType); - - // Specific packet parsers: - virtual int ParseArmAnim (void); - virtual int ParseBlockDig (void); - virtual int ParseBlockPlace (void); - virtual int ParseChat (void); - virtual int ParseCreativeInventoryAction(void); - virtual int ParseDisconnect (void); - virtual int ParseEntityAction (void); - virtual int ParseHandshake (void); - virtual int ParseKeepAlive (void); - virtual int ParseLogin (void); - virtual int ParsePing (void); - virtual int ParsePlayerAbilities (void); - virtual int ParsePlayerLook (void); - virtual int ParsePlayerMoveLook (void); - virtual int ParsePlayerOnGround (void); - virtual int ParsePlayerPosition (void); - virtual int ParsePluginMessage (void); - virtual int ParseRespawn (void); - virtual int ParseSlotSelected (void); - virtual int ParseUpdateSign (void); - virtual int ParseUseEntity (void); - virtual int ParseWindowClick (void); - virtual int ParseWindowClose (void); - - // Utility functions: - /// Writes a "pre-chunk" packet - void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad); - - /// Writes a "set window items" packet with the specified params - void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items); - - /// Writes one item, "slot" as the protocol wiki calls it - virtual void WriteItem(const cItem & a_Item); - - /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes - virtual int ParseItem(cItem & a_Item); - - /// Writes the COMMON entity metadata - void WriteCommonMetadata(const cEntity & a_Entity); - - /// Writes normal entity metadata - void WriteEntityMetadata(const cEntity & a_Entity); - - /// Writes mobile entity metadata - void WriteMobMetadata(const cMonster & a_Mob); -} ; - - - - diff --git a/source/Protocol/Protocol132.cpp b/source/Protocol/Protocol132.cpp deleted file mode 100644 index 22eac4312..000000000 --- a/source/Protocol/Protocol132.cpp +++ /dev/null @@ -1,950 +0,0 @@ - -// Protocol132.cpp - -// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39) - -#include "Globals.h" -#include "Protocol132.h" -#include "../Root.h" -#include "../Server.h" -#include "../World.h" -#include "../ClientHandle.h" -#include "../../CryptoPP/randpool.h" -#include "../Item.h" -#include "ChunkDataSerializer.h" -#include "../Entities/Player.h" -#include "../Mobs/Monster.h" -#include "../UI/Window.h" -#include "../Entities/Pickup.h" -#include "../WorldStorage/FastNBT.h" -#include "../StringCompression.h" - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - -typedef unsigned char Byte; - - - - - -using namespace CryptoPP; - - - - - -const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows... - - - - - -enum -{ - PACKET_KEEP_ALIVE = 0x00, - PACKET_LOGIN = 0x01, - PACKET_ENTITY_EQUIPMENT = 0x05, - PACKET_COMPASS = 0x06, - PACKET_PLAYER_SPAWN = 0x14, - PACKET_COLLECT_PICKUP = 0x16, - PACKET_SPAWN_MOB = 0x18, - PACKET_DESTROY_ENTITIES = 0x1d, - PACKET_CHUNK_DATA = 0x33, - PACKET_BLOCK_CHANGE = 0x35, - PACKET_BLOCK_ACTION = 0x36, - PACKET_BLOCK_BREAK_ANIM = 0x37, - PACKET_SOUND_EFFECT = 0x3e, - PACKET_SOUND_PARTICLE_EFFECT = 0x3d, - PACKET_TAB_COMPLETION = 0xcb, - PACKET_LOCALE_VIEW_DISTANCE = 0xcc, - PACKET_CLIENT_STATUSES = 0xcd, - PACKET_ENCRYPTION_KEY_RESP = 0xfc, -} ; - - - - - -// Converts a raw 160-bit SHA1 digest into a Java Hex representation -// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802 -static void DigestToJava(byte a_Digest[20], AString & a_Out) -{ - bool IsNegative = (a_Digest[0] >= 0x80); - if (IsNegative) - { - // Two's complement: - bool carry = true; // Add one to the whole number - for (int i = 19; i >= 0; i--) - { - a_Digest[i] = ~a_Digest[i]; - if (carry) - { - carry = (a_Digest[i] == 0xff); - a_Digest[i]++; - } - } - } - a_Out.clear(); - a_Out.reserve(40); - for (int i = 0; i < 20; i++) - { - AppendPrintf(a_Out, "%02x", a_Digest[i]); - } - while ((a_Out.length() > 0) && (a_Out[0] == '0')) - { - a_Out.erase(0, 1); - } - if (IsNegative) - { - a_Out.insert(0, "-"); - } -} - - - - - -/* -// Self-test the hash formatting for known values: -// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48 -// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1 -// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6 - -class Test -{ -public: - Test(void) - { - AString DigestNotch, DigestJeb, DigestSimon; - byte Digest[20]; - CryptoPP::SHA1 Checksum; - Checksum.Update((const byte *)"Notch", 5); - Checksum.Final(Digest); - DigestToJava(Digest, DigestNotch); - Checksum.Restart(); - Checksum.Update((const byte *)"jeb_", 4); - Checksum.Final(Digest); - DigestToJava(Digest, DigestJeb); - Checksum.Restart(); - Checksum.Update((const byte *)"simon", 5); - Checksum.Final(Digest); - DigestToJava(Digest, DigestSimon); - printf("Notch: \"%s\"", DigestNotch.c_str()); - printf("jeb_: \"%s\"", DigestJeb.c_str()); - printf("simon: \"%s\"", DigestSimon.c_str()); - } -} test; -*/ - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol132: - -cProtocol132::cProtocol132(cClientHandle * a_Client) : - super(a_Client), - m_IsEncrypted(false) -{ -} - - - - - -cProtocol132::~cProtocol132() -{ - if (!m_DataToSend.empty()) - { - LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size()); - } -} - - - - - -void cProtocol132::DataReceived(const char * a_Data, int a_Size) -{ - if (m_IsEncrypted) - { - byte Decrypted[512]; - while (a_Size > 0) - { - int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; - m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); - super::DataReceived((const char *)Decrypted, NumBytes); - a_Size -= NumBytes; - a_Data += NumBytes; - } - } - else - { - super::DataReceived(a_Data, a_Size); - } -} - - - - - -void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_BLOCK_ACTION); - WriteInt (a_BlockX); - WriteShort((short)a_BlockY); - WriteInt (a_BlockZ); - WriteByte (a_Byte1); - WriteByte (a_Byte2); - WriteShort(a_BlockType); - Flush(); -} - - - - - -void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_BLOCK_BREAK_ANIM); - WriteInt (a_entityID); - WriteInt (a_BlockX); - WriteInt (a_BlockY); - WriteInt (a_BlockZ); - WriteByte (stage); - Flush(); -} - - - - - -void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_BLOCK_CHANGE); - WriteInt (a_BlockX); - WriteByte ((unsigned char)a_BlockY); - WriteInt (a_BlockZ); - WriteShort(a_BlockType); - WriteByte (a_BlockMeta); - Flush(); -} - - - - - -void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - cCSLock Lock(m_CSPacket); - - // Pre-chunk not used in 1.3.2. Finally. - - // Send the chunk data: - AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); - WriteByte(PACKET_CHUNK_DATA); - WriteInt (a_ChunkX); - WriteInt (a_ChunkZ); - SendData(Serialized.data(), Serialized.size()); - Flush(); -} - - - - - -void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_COLLECT_PICKUP); - WriteInt (a_Pickup.GetUniqueID()); - WriteInt (a_Player.GetUniqueID()); - Flush(); - - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - SendSoundEffect( - "random.pop", - (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8), - 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64) - ); -} - - - - - -void cProtocol132::SendDestroyEntity(const cEntity & a_Entity) -{ - if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID()) - { - // Do not send "destroy self" to the client, the client would crash (FS #254) - return; - } - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_DESTROY_ENTITIES); - WriteByte(1); // entity count - WriteInt (a_Entity.GetUniqueID()); - Flush(); -} - - - - - -void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_ENTITY_EQUIPMENT); - WriteInt (a_Entity.GetUniqueID()); - WriteShort(a_SlotNum); - WriteItem (a_Item); - Flush(); -} - - - - - -void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_LOGIN); - WriteInt (a_Player.GetUniqueID()); // EntityID of the player - WriteString("default"); // Level type - WriteByte ((int)a_Player.GetGameMode()); - WriteByte ((Byte)(a_World.GetDimension())); - WriteByte (2); // TODO: Difficulty - WriteByte (0); // Unused, used to be world height - WriteByte (8); // Client list width or something - Flush(); - - SendCompass(a_World); -} - - - - - -void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) -{ - const cItem & HeldItem = a_Player.GetEquippedItem(); - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_PLAYER_SPAWN); - WriteInt (a_Player.GetUniqueID()); - WriteString(a_Player.GetName()); - WriteInt ((int)(a_Player.GetPosX() * 32)); - WriteInt ((int)(a_Player.GetPosY() * 32)); - WriteInt ((int)(a_Player.GetPosZ() * 32)); - WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); - WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); - WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); - // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata: - WriteByte (0); // Index 0, byte (flags) - WriteByte (0); // Flags, empty - WriteByte (0x7f); // End of metadata - Flush(); -} - - - - - -void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_SOUND_EFFECT); - WriteString (a_SoundName); - WriteInt (a_SrcX); - WriteInt (a_SrcY); - WriteInt (a_SrcZ); - WriteFloat (a_Volume); - WriteByte ((char)(a_Pitch * 63.0f)); - Flush(); -} - - - - - -void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_SOUND_PARTICLE_EFFECT); - WriteInt (a_EffectID); - WriteInt (a_SrcX); - WriteByte(a_SrcY); - WriteInt (a_SrcZ); - WriteInt (a_Data); - Flush(); -} - - - - - -void cProtocol132::SendSpawnMob(const cMonster & a_Mob) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_SPAWN_MOB); - WriteInt (a_Mob.GetUniqueID()); - WriteByte (a_Mob.GetMobType()); - WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); - WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256)); - WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256)); - WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256)); - WriteShort ((short)(a_Mob.GetSpeedX() * 400)); - WriteShort ((short)(a_Mob.GetSpeedY() * 400)); - WriteShort ((short)(a_Mob.GetSpeedZ() * 400)); - - WriteCommonMetadata(a_Mob); - WriteMobMetadata(a_Mob); - WriteByte(0x7f); - - Flush(); -} - - - - - -void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results) -{ - if (a_Results.empty()) - { - // No results to send - return; - } - - AString Serialized(a_Results[0]); - for (AStringVector::const_iterator itr = a_Results.begin() + 1, end = a_Results.end(); itr != end; ++itr) - { - Serialized.push_back(0); - Serialized.append(*itr); - } // for itr - a_Results[] - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_TAB_COMPLETION); - WriteString(Serialized); - Flush(); -} - - - - - -void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - // Unloading the chunk is done by sending a "map chunk" packet - // with IncludeInitialize set to true and primary bitmap set to 0: - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_CHUNK_DATA); - WriteInt (a_ChunkX); - WriteInt (a_ChunkZ); - WriteBool(true); // IncludeInitialize - WriteShort(0); // Primary bitmap - WriteShort(0); // Add bitmap - WriteInt(0); - Flush(); -} - - - - - -void cProtocol132::SendWholeInventory(const cWindow & a_Window) -{ - // 1.3.2 requires player inventory slots to be sent as SetSlot packets, - // otherwise it sometimes fails to update the window - - // Send the entire window: - super::SendWholeInventory(a_Window); - - // Send the player inventory and hotbar: - const cInventory & Inventory = m_Client->GetPlayer()->GetInventory(); - int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots - char WindowID = a_Window.GetWindowID(); - for (int i = 0; i < cInventory::invInventoryCount; i++) - { - SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i)); - } // for i - Inventory[] - BaseOffset += cInventory::invInventoryCount; - for (int i = 0; i < cInventory::invHotbarCount; i++) - { - SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i)); - } // for i - Hotbar[] - - // Send even the item being dragged: - SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem()); -} - - - - - -AString cProtocol132::GetAuthServerID(void) -{ - // http://wiki.vg/wiki/index.php?title=Session&oldid=2615 - // Server uses SHA1 to mix ServerID, Client secret and server public key together - // The mixing is done in StartEncryption, the result is in m_AuthServerID - - return m_AuthServerID; -} - - - - - -int cProtocol132::ParsePacket(unsigned char a_PacketType) -{ - switch (a_PacketType) - { - default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125 - case PACKET_CLIENT_STATUSES: return ParseClientStatuses(); - case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse(); - case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance(); - case PACKET_TAB_COMPLETION: return ParseTabCompletion(); - } -} - - - - - -int cProtocol132::ParseBlockPlace(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_PACKET_READ(ReadByte, Byte, PosY); - HANDLE_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_PACKET_READ(ReadChar, char, BlockFace); - - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - - HANDLE_PACKET_READ(ReadChar, char, CursorX); - HANDLE_PACKET_READ(ReadChar, char, CursorY); - HANDLE_PACKET_READ(ReadChar, char, CursorZ); - - m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem); - return PARSE_OK; -} - - - - - -int cProtocol132::ParseHandshake(void) -{ - HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost); - HANDLE_PACKET_READ(ReadBEInt, int, ServerPort); - m_Username = Username; - - if (!m_Client->HandleHandshake( m_Username )) - { - return PARSE_OK; // Player is not allowed into the server - } - - // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD - CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs - cRoot::Get()->GetServer()->GetPublicKey().Save(sink); - SendEncryptionKeyRequest(); - - return PARSE_OK; -} - - - - - -int cProtocol132::ParseClientStatuses(void) -{ - HANDLE_PACKET_READ(ReadByte, byte, Status); - if ((Status & 1) == 0) - { - m_Client->HandleLogin(39, m_Username); - } - else - { - m_Client->HandleRespawn(); - } - return PARSE_OK; -} - - - - - -int cProtocol132::ParseEncryptionKeyResponse(void) -{ - HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength); - AString EncKey; - if (!m_ReceivedData.ReadString(EncKey, EncKeyLength)) - { - return PARSE_INCOMPLETE; - } - HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength); - AString EncNonce; - if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength)) - { - return PARSE_INCOMPLETE; - } - if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) - { - LOGD("Too long encryption"); - m_Client->Kick("Hacked client"); - return PARSE_OK; - } - - HandleEncryptionKeyResponse(EncKey, EncNonce); - return PARSE_OK; -} - - - - - -int cProtocol132::ParseLocaleViewDistance(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); - HANDLE_PACKET_READ(ReadChar, char, ViewDistance); - HANDLE_PACKET_READ(ReadChar, char, ChatFlags); - HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); - // TODO: m_Client->HandleLocale(Locale); - // TODO: m_Client->HandleViewDistance(ViewDistance); - // TODO: m_Client->HandleChatFlags(ChatFlags); - // Ignoring client difficulty - return PARSE_OK; -} - - - - - -int cProtocol132::ParseLogin(void) -{ - // Login packet not used in 1.3.2 - return PARSE_ERROR; -} - - - - - -int cProtocol132::ParsePlayerAbilities(void) -{ - HANDLE_PACKET_READ(ReadBool, bool, Flags); - HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed); - HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed); - // TODO: m_Client->HandlePlayerAbilities(...); - return PARSE_OK; -} - - - - - -int cProtocol132::ParseTabCompletion(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Text); - m_Client->HandleTabCompletion(Text); - return PARSE_OK; -} - - - - - -void cProtocol132::SendData(const char * a_Data, int a_Size) -{ - m_DataToSend.append(a_Data, a_Size); -} - - - - - -void cProtocol132::Flush(void) -{ - ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly? - - if (m_DataToSend.empty()) - { - LOGD("Flushing empty"); - return; - } - const char * a_Data = m_DataToSend.data(); - int a_Size = m_DataToSend.size(); - if (m_IsEncrypted) - { - byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) - while (a_Size > 0) - { - int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; - m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); - super::SendData((const char *)Encrypted, NumBytes); - a_Size -= NumBytes; - a_Data += NumBytes; - } - } - else - { - super::SendData(a_Data, a_Size); - } - m_DataToSend.clear(); -} - - - - - -void cProtocol132::WriteItem(const cItem & a_Item) -{ - short ItemType = a_Item.m_ItemType; - ASSERT(ItemType >= -1); // Check validity of packets in debug runtime - if (ItemType <= 0) - { - // Fix, to make sure no invalid values are sent. - ItemType = -1; - } - - if (a_Item.IsEmpty()) - { - WriteShort(-1); - return; - } - - WriteShort(ItemType); - WriteByte (a_Item.m_ItemCount); - WriteShort(a_Item.m_ItemDamage); - - if (a_Item.m_Enchantments.IsEmpty()) - { - WriteShort(-1); - return; - } - - // Send the enchantments: - cFastNBTWriter Writer; - const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; - a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); - Writer.Finish(); - AString Compressed; - CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); - WriteShort(Compressed.size()); - SendData(Compressed.data(), Compressed.size()); -} - - - - - -int cProtocol132::ParseItem(cItem & a_Item) -{ - HANDLE_PACKET_READ(ReadBEShort, short, ItemType); - - if (ItemType <= -1) - { - a_Item.Empty(); - return PARSE_OK; - } - a_Item.m_ItemType = ItemType; - - HANDLE_PACKET_READ(ReadChar, char, ItemCount); - HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); - a_Item.m_ItemCount = ItemCount; - a_Item.m_ItemDamage = ItemDamage; - if (ItemCount <= 0) - { - a_Item.Empty(); - } - - HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength); - if (MetadataLength <= 0) - { - return PARSE_OK; - } - - // Read the metadata - AString Metadata; - Metadata.resize(MetadataLength); - if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength)) - { - return PARSE_INCOMPLETE; - } - - return ParseItemMetadata(a_Item, Metadata); -} - - - - - -int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) -{ - // Uncompress the GZIPped data: - AString Uncompressed; - if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) - { - AString HexDump; - CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); - LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str()); - return PARSE_ERROR; - } - - // Parse into NBT: - cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); - if (!NBT.IsValid()) - { - AString HexDump; - CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); - LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str()); - return PARSE_ERROR; - } - - // Load enchantments from the NBT: - for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) - { - if ( - (NBT.GetType(tag) == TAG_List) && - ( - (NBT.GetName(tag) == "ench") || - (NBT.GetName(tag) == "StoredEnchantments") - ) - ) - { - a_Item.m_Enchantments.ParseFromNBT(NBT, tag); - } - } - - return PARSE_OK; -} - - - - - -void cProtocol132::SendCompass(const cWorld & a_World) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_COMPASS); - WriteInt((int)(a_World.GetSpawnX())); - WriteInt((int)(a_World.GetSpawnY())); - WriteInt((int)(a_World.GetSpawnZ())); - Flush(); -} - - - - - -void cProtocol132::SendEncryptionKeyRequest(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte((char)0xfd); - WriteString(cRoot::Get()->GetServer()->GetServerID()); - WriteShort((short)m_ServerPublicKey.size()); - SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size()); - WriteShort(4); - WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) - Flush(); -} - - - - - -void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce) -{ - // Decrypt EncNonce using privkey - RSAES::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey()); - time_t CurTime = time(NULL); - CryptoPP::RandomPool rng; - rng.Put((const byte *)&CurTime, sizeof(CurTime)); - byte DecryptedNonce[MAX_ENC_LEN]; - DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce); - if (!res.isValidCoding || (res.messageLength != 4)) - { - LOGD("Bad nonce length"); - m_Client->Kick("Hacked client"); - return; - } - if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this) - { - LOGD("Bad nonce value"); - m_Client->Kick("Hacked client"); - return; - } - - // Decrypt the symmetric encryption key using privkey: - byte DecryptedKey[MAX_ENC_LEN]; - res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey); - if (!res.isValidCoding || (res.messageLength != 16)) - { - LOGD("Bad key length"); - m_Client->Kick("Hacked client"); - return; - } - - { - // Send encryption key response: - cCSLock Lock(m_CSPacket); - WriteByte((char)0xfc); - WriteShort(0); - WriteShort(0); - Flush(); - } - - StartEncryption(DecryptedKey); - return; -} - - - - - -void cProtocol132::StartEncryption(const byte * a_Key) -{ - m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); - m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); - m_IsEncrypted = true; - - // Prepare the m_AuthServerID: - CryptoPP::SHA1 Checksum; - AString ServerID = cRoot::Get()->GetServer()->GetServerID(); - Checksum.Update((const byte *)ServerID.c_str(), ServerID.length()); - Checksum.Update(a_Key, 16); - Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length()); - byte Digest[20]; - Checksum.Final(Digest); - DigestToJava(Digest, m_AuthServerID); -} - - - - diff --git a/source/Protocol/Protocol132.h b/source/Protocol/Protocol132.h deleted file mode 100644 index dc4d8aeef..000000000 --- a/source/Protocol/Protocol132.h +++ /dev/null @@ -1,102 +0,0 @@ - -// Protocol132.h - -// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39) - - - - - -#pragma once - -#include "Protocol125.h" -#include "../../CryptoPP/modes.h" -#include "../../CryptoPP/aes.h" - - - - - -class cProtocol132 : - public cProtocol125 -{ - typedef cProtocol125 super; -public: - - cProtocol132(cClientHandle * a_Client); - virtual ~cProtocol132(); - - /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; - - // Sending commands (alphabetically sorted): - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; - virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; - virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; - virtual void SendDestroyEntity (const cEntity & a_Entity) override; - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; - virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 - virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendSpawnMob (const cMonster & a_Mob) override; - virtual void SendTabCompletionResults(const AStringVector & a_Results) override; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; - virtual void SendWholeInventory (const cWindow & a_Window) override; - - virtual AString GetAuthServerID(void) override; - - /// Handling of the additional packets: - virtual int ParsePacket(unsigned char a_PacketType) override; - - // Modified packets: - virtual int ParseBlockPlace (void) override; - virtual int ParseHandshake (void) override; - virtual int ParseLogin (void) override; - virtual int ParsePlayerAbilities(void) override; - - // New packets: - virtual int ParseClientStatuses (void); - virtual int ParseEncryptionKeyResponse(void); - virtual int ParseLocaleViewDistance (void); - virtual int ParseTabCompletion (void); - -protected: - bool m_IsEncrypted; - CryptoPP::CFB_Mode::Decryption m_Decryptor; - CryptoPP::CFB_Mode::Encryption m_Encryptor; - AString m_DataToSend; - - /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID() - AString m_AuthServerID; - - /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption() - AString m_ServerPublicKey; - - virtual void SendData(const char * a_Data, int a_Size) override; - - // DEBUG: - virtual void Flush(void) override; - - // Items in slots are sent differently - virtual void WriteItem(const cItem & a_Item) override; - virtual int ParseItem(cItem & a_Item) override; - - /// Parses the metadata that may come with the item. - int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); - - virtual void SendCompass(const cWorld & a_World); - virtual void SendEncryptionKeyRequest(void); - - /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption - void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce); - - /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID - void StartEncryption(const byte * a_Key); -} ; - - - - diff --git a/source/Protocol/Protocol14x.cpp b/source/Protocol/Protocol14x.cpp deleted file mode 100644 index d2582458b..000000000 --- a/source/Protocol/Protocol14x.cpp +++ /dev/null @@ -1,256 +0,0 @@ - -// Protocol14x.cpp - -/* -Implements the 1.4.x protocol classes representing these protocols: -- cProtocol142: - - release 1.4.2 protocol (#47) - - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) - - release 1.4.5 protocol (same as 1.4.4) -- cProtocol146: - - release 1.4.6 protocol (#51) -*/ - -#include "Globals.h" -#include "Protocol14x.h" -#include "../Root.h" -#include "../Server.h" -#include "../ClientHandle.h" -#include "../../CryptoPP/randpool.h" -#include "../Item.h" -#include "ChunkDataSerializer.h" -#include "../Entities/Player.h" -#include "../Mobs/Monster.h" -#include "../UI/Window.h" -#include "../Entities/Pickup.h" -#include "../Entities/FallingBlock.h" - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - - -enum -{ - PACKET_UPDATE_TIME = 0x04, - PACKET_PICKUP_SPAWN = 0x15, - PACKET_SPAWN_OBJECT = 0x17, - PACKET_ENTITY_METADATA = 0x28, - PACKET_SOUND_PARTICLE_EFFECT = 0x3d -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol142: - -cProtocol142::cProtocol142(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -int cProtocol142::ParseLocaleViewDistance(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); - HANDLE_PACKET_READ(ReadChar, char, ViewDistance); - HANDLE_PACKET_READ(ReadChar, char, ChatFlags); - HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); - HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2 - // TODO: m_Client->HandleLocale(Locale); - // TODO: m_Client->HandleViewDistance(ViewDistance); - // TODO: m_Client->HandleChatFlags(ChatFlags); - // Ignoring client difficulty - return PARSE_OK; -} - - - - - -void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_PICKUP_SPAWN); - WriteInt (a_Pickup.GetUniqueID()); - WriteItem (a_Pickup.GetItem()); - WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); - WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); - WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); - WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); - Flush(); -} - - - - - -void cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_SOUND_PARTICLE_EFFECT); - WriteInt (a_EffectID); - WriteInt (a_SrcX); - WriteByte(a_SrcY); - WriteInt (a_SrcZ); - WriteInt (a_Data); - WriteBool(0); - Flush(); -} - - - - - -void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_UPDATE_TIME); - WriteInt64(a_WorldAge); - WriteInt64(a_TimeOfDay); - Flush(); -} - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol146: - -cProtocol146::cProtocol146(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup) -{ - ASSERT(!a_Pickup.GetItem().IsEmpty()); - - cCSLock Lock(m_CSPacket); - - // Send a SPAWN_OBJECT packet for the base entity: - WriteByte(PACKET_SPAWN_OBJECT); - WriteInt (a_Pickup.GetUniqueID()); - WriteByte(0x02); - WriteInt ((int)(a_Pickup.GetPosX() * 32)); - WriteInt ((int)(a_Pickup.GetPosY() * 32)); - WriteInt ((int)(a_Pickup.GetPosZ() * 32)); - WriteInt (1); - WriteShort((short)(a_Pickup.GetSpeed().x * 32)); - WriteShort((short)(a_Pickup.GetSpeed().y * 32)); - WriteShort((short)(a_Pickup.GetSpeed().z * 32)); - WriteByte(0); - WriteByte(0); - - // Send a ENTITY_METADATA packet with the slot info: - WriteByte(PACKET_ENTITY_METADATA); - WriteInt(a_Pickup.GetUniqueID()); - WriteByte(0xaa); // a slot value at index 10 - WriteItem(a_Pickup.GetItem()); - WriteByte(0x7f); // End of metadata - Flush(); -} - - - - - -void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) -{ - // Send a spawn object / vehicle packet - cCSLock Lock(m_CSPacket); - - WriteByte(PACKET_SPAWN_OBJECT); - WriteInt (a_FallingBlock.GetUniqueID()); - WriteByte(70); - WriteInt ((int)(a_FallingBlock.GetPosX() * 32)); - WriteInt ((int)(a_FallingBlock.GetPosY() * 32)); - WriteInt ((int)(a_FallingBlock.GetPosZ() * 32)); - WriteByte (0); // Pitch - WriteByte (0); // Yaw - WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype - WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); - WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); - WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); - Flush(); -} - - - - - -void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_SPAWN_OBJECT); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_ObjectType); - WriteInt ((int)(a_Entity.GetPosX() * 32)); - WriteInt ((int)(a_Entity.GetPosY() * 32)); - WriteInt ((int)(a_Entity.GetPosZ() * 32)); - WriteByte(a_Pitch); - WriteByte(a_Yaw); - WriteInt (a_ObjectData); - if (a_ObjectData != 0) - { - WriteShort((short)(a_Entity.GetSpeedX() * 400)); - WriteShort((short)(a_Entity.GetSpeedY() * 400)); - WriteShort((short)(a_Entity.GetSpeedZ() * 400)); - } - Flush(); -} - - - - - -void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_SPAWN_OBJECT); - WriteInt (a_Vehicle.GetUniqueID()); - WriteByte (a_VehicleType); - WriteInt ((int)(a_Vehicle.GetPosX() * 32)); - WriteInt ((int)(a_Vehicle.GetPosY() * 32)); - WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); - WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); - WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); - WriteInt (a_VehicleSubType); - if (a_VehicleSubType != 0) - { - WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); - WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); - WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); - } - Flush(); -} - - - - - diff --git a/source/Protocol/Protocol14x.h b/source/Protocol/Protocol14x.h deleted file mode 100644 index ca497bbc1..000000000 --- a/source/Protocol/Protocol14x.h +++ /dev/null @@ -1,63 +0,0 @@ - -// Protocol14x.h - -/* -Interfaces to the 1.4.x protocol classes representing these protocols: -- cProtocol142: - - release 1.4.2 protocol (#47) - - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) - - release 1.4.5 protocol (same as 1.4.4) -- cProtocol146: - - release 1.4.6 protocol (#51) -*/ - - - - - -#pragma once - -#include "Protocol132.h" - - - - - -class cProtocol142 : - public cProtocol132 -{ - typedef cProtocol132 super; - -public: - cProtocol142(cClientHandle * a_Client); - - // Sending commands (alphabetically sorted): - virtual void SendPickupSpawn (const cPickup & a_Pickup) override; - virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; - - // Specific packet parsers: - virtual int ParseLocaleViewDistance(void) override; -} ; - - - - - -class cProtocol146 : - public cProtocol142 -{ - typedef cProtocol142 super; - -public: - cProtocol146(cClientHandle * a_Client); - - virtual void SendPickupSpawn (const cPickup & a_Pickup) override; - virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override; - virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; - virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; -} ; - - - - diff --git a/source/Protocol/Protocol15x.cpp b/source/Protocol/Protocol15x.cpp deleted file mode 100644 index c337d26e7..000000000 --- a/source/Protocol/Protocol15x.cpp +++ /dev/null @@ -1,138 +0,0 @@ - -// Protocol15x.cpp - -/* -Implements the 1.5.x protocol classes: - - cProtocol150 - - release 1.5 protocol (#60) - - release 1.5.2 protocol (#61, no relevant changes found) -*/ - -#include "Globals.h" -#include "Protocol15x.h" -#include "../ClientHandle.h" -#include "../Item.h" -#include "../UI/Window.h" - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - - -enum -{ - PACKET_WINDOW_OPEN = 0x64, -} ; - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol150: - -cProtocol150::cProtocol150(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -void cProtocol150::SendWindowOpen(const cWindow & a_Window) -{ - if (a_Window.GetWindowType() < 0) - { - // Do not send for inventory windows - return; - } - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_WINDOW_OPEN); - WriteByte (a_Window.GetWindowID()); - WriteByte (a_Window.GetWindowType()); - WriteString(a_Window.GetWindowTitle()); - WriteByte (a_Window.GetNumNonInventorySlots()); - WriteByte (1); // Use title - Flush(); -} - - - - - -int cProtocol150::ParseWindowClick(void) -{ - HANDLE_PACKET_READ(ReadChar, char, WindowID); - HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); - HANDLE_PACKET_READ(ReadByte, Byte, Button); - HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); - HANDLE_PACKET_READ(ReadByte, Byte, Mode); - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - - // Convert Button, Mode, SlotNum and HeldItem into eClickAction: - eClickAction Action; - switch ((Mode << 8) | Button) - { - case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break; - case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break; - case 0x0100: Action = caShiftLeftClick; break; - case 0x0101: Action = caShiftRightClick; break; - case 0x0200: Action = caNumber1; break; - case 0x0201: Action = caNumber2; break; - case 0x0202: Action = caNumber3; break; - case 0x0203: Action = caNumber4; break; - case 0x0204: Action = caNumber5; break; - case 0x0205: Action = caNumber6; break; - case 0x0206: Action = caNumber7; break; - case 0x0207: Action = caNumber8; break; - case 0x0208: Action = caNumber9; break; - case 0x0300: Action = caMiddleClick; break; - case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break; - case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break; - case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break; - case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break; - case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break; - case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break; - case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break; - case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break; - case 0x0600: Action = caDblClick; break; - } - - if (Action == caUnknown) - { - LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.", - Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str() - ); - ASSERT(!"Unknown click action"); - return PARSE_OK; - } - - m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); - return PARSE_OK; -} - - - - - diff --git a/source/Protocol/Protocol15x.h b/source/Protocol/Protocol15x.h deleted file mode 100644 index e554fe130..000000000 --- a/source/Protocol/Protocol15x.h +++ /dev/null @@ -1,38 +0,0 @@ - -// Protocol15x.h - -/* -Declares the 1.5.x protocol classes: - - cProtocol150 - - release 1.5 and 1.5.1 protocol (#60) - - release 1.5.2 protocol (#61; no relevant changes found) -*/ - - - - - -#pragma once - -#include "Protocol14x.h" - - - - - -class cProtocol150 : - public cProtocol146 -{ - typedef cProtocol146 super; - -public: - cProtocol150(cClientHandle * a_Client); - - virtual void SendWindowOpen(const cWindow & a_Window) override; - - virtual int ParseWindowClick(void); -} ; - - - - diff --git a/source/Protocol/Protocol16x.cpp b/source/Protocol/Protocol16x.cpp deleted file mode 100644 index cfa27b3c4..000000000 --- a/source/Protocol/Protocol16x.cpp +++ /dev/null @@ -1,268 +0,0 @@ - -// Protocol16x.cpp - -/* -Implements the 1.6.x protocol classes: - - cProtocol161 - - release 1.6.1 protocol (#73) - - cProtocol162 - - release 1.6.2 protocol (#74) - - release 1.6.3 protocol (#77) - no relevant changes - - release 1.6.4 protocol (#78) - no relevant changes -(others may be added later in the future for the 1.6 release series) -*/ - -#include "Globals.h" -#include "Protocol16x.h" -#include "../ClientHandle.h" -#include "../Entities/Entity.h" -#include "../Entities/Player.h" -#include "../UI/Window.h" - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - - -enum -{ - PACKET_CHAT = 0x03, - PACKET_UPDATE_HEALTH = 0x08, - PACKET_STEER_VEHICLE = 0x1b, - PACKET_ATTACH_ENTITY = 0x27, - PACKET_ENTITY_PROPERTIES = 0x2c, - PACKET_WINDOW_OPEN = 0x64, - PACKET_TILE_EDITOR_OPEN = 0x85, - PACKET_PLAYER_ABILITIES = 0xca, -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol161: - -cProtocol161::cProtocol161(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ATTACH_ENTITY); - WriteInt(a_Entity.GetUniqueID()); - WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); - WriteBool(false); // TODO: "Should use leash?" -> no - Flush(); -} - - - - - -void cProtocol161::SendChat(const AString & a_Message) -{ - super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str())); -} - - - - - -void cProtocol161::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_TILE_EDITOR_OPEN); - WriteByte(0); - WriteInt(a_BlockX); - WriteInt(a_BlockY); - WriteInt(a_BlockZ); - Flush(); -} - - - - - -void cProtocol161::SendGameMode(eGameMode a_GameMode) -{ - super::SendGameMode(a_GameMode); - SendPlayerMaxSpeed(); -} - - - - - -void cProtocol161::SendHealth(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_UPDATE_HEALTH); - WriteFloat((float)m_Client->GetPlayer()->GetHealth()); - WriteShort(m_Client->GetPlayer()->GetFoodLevel()); - WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); - Flush(); -} - - - - - -void cProtocol161::SendPlayerMaxSpeed(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENTITY_PROPERTIES); - WriteInt(m_Client->GetPlayer()->GetUniqueID()); - WriteInt(1); - WriteString("generic.movementSpeed"); - WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); - Flush(); -} - - - - - -void cProtocol161::SendRespawn(void) -{ - // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast - super::SendRespawn(); - SendPlayerMaxSpeed(); -} - - - - - -void cProtocol161::SendWindowOpen(const cWindow & a_Window) -{ - if (a_Window.GetWindowType() < 0) - { - // Do not send for inventory windows - return; - } - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_WINDOW_OPEN); - WriteByte (a_Window.GetWindowID()); - WriteByte (a_Window.GetWindowType()); - WriteString(a_Window.GetWindowTitle()); - WriteByte (a_Window.GetNumNonInventorySlots()); - WriteByte (1); // Use title - if (a_Window.GetWindowType() == cWindow::wtAnimalChest) - { - WriteInt(0); // TODO: The animal's EntityID - } - Flush(); -} - - - - - -int cProtocol161::ParseEntityAction(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_PACKET_READ(ReadChar, char, ActionID); - HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal); - m_Client->HandleEntityAction(EntityID, ActionID); - return PARSE_OK; -} - - - - - -int cProtocol161::ParsePlayerAbilities(void) -{ - HANDLE_PACKET_READ(ReadByte, Byte, Flags); - HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed); - HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed); - // TODO: m_Client->HandlePlayerAbilities(...); - return PARSE_OK; -} - - - - - -int cProtocol161::ParseSteerVehicle(void) -{ - HANDLE_PACKET_READ(ReadBEFloat, float, Sideways); - HANDLE_PACKET_READ(ReadBEFloat, float, Forward); - HANDLE_PACKET_READ(ReadBool, bool, Jump); - HANDLE_PACKET_READ(ReadBool, bool, Unmount); - if (Unmount) - { - m_Client->HandleUnmount(); - } - else - { - m_Client->HandleSteerVehicle(Forward, Sideways); - } - return PARSE_OK; -} - - - - - -int cProtocol161::ParsePacket(unsigned char a_PacketType) -{ - switch (a_PacketType) - { - case PACKET_STEER_VEHICLE: return ParseSteerVehicle(); - default: return super::ParsePacket(a_PacketType); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol162: - -cProtocol162::cProtocol162(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -void cProtocol162::SendPlayerMaxSpeed(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENTITY_PROPERTIES); - WriteInt(m_Client->GetPlayer()->GetUniqueID()); - WriteInt(1); - WriteString("generic.movementSpeed"); - WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); - WriteShort(0); - Flush(); -} - - - - diff --git a/source/Protocol/Protocol16x.h b/source/Protocol/Protocol16x.h deleted file mode 100644 index 325e41c5a..000000000 --- a/source/Protocol/Protocol16x.h +++ /dev/null @@ -1,76 +0,0 @@ - -// Protocol16x.h - -/* -Declares the 1.6.x protocol classes: - - cProtocol161 - - release 1.6.1 protocol (#73) - - cProtocol162 - - release 1.6.2 protocol (#74) - - release 1.6.3 protocol (#77) - no relevant changes - - release 1.6.4 protocol (#78) - no relevant changes -(others may be added later in the future for the 1.6 release series) -*/ - - - - - -#pragma once - -#include "Protocol15x.h" - - - - - -class cProtocol161 : - public cProtocol150 -{ - typedef cProtocol150 super; - -public: - cProtocol161(cClientHandle * a_Client); - -protected: - - // cProtocol150 overrides: - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; - virtual void SendChat (const AString & a_Message) override; - virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) - virtual void SendGameMode (eGameMode a_GameMode) override; - virtual void SendHealth (void) override; - virtual void SendPlayerMaxSpeed(void) override; - virtual void SendRespawn (void) override; - virtual void SendWindowOpen (const cWindow & a_Window) override; - - virtual int ParseEntityAction (void) override; - virtual int ParsePlayerAbilities(void) override; - - // New packets: - virtual int ParseSteerVehicle(void); - - // Enable new packets' handling - virtual int ParsePacket(unsigned char a_PacketType) override; -} ; - - - - - -class cProtocol162 : - public cProtocol161 -{ - typedef cProtocol161 super; - -public: - cProtocol162(cClientHandle * a_Client); - -protected: - // cProtocol161 overrides: - virtual void SendPlayerMaxSpeed(void) override; -} ; - - - - diff --git a/source/Protocol/Protocol17x.cpp b/source/Protocol/Protocol17x.cpp deleted file mode 100644 index 628b8e071..000000000 --- a/source/Protocol/Protocol17x.cpp +++ /dev/null @@ -1,1917 +0,0 @@ - -// Protocol17x.cpp - -/* -Implements the 1.7.x protocol classes: - - cProtocol172 - - release 1.7.2 protocol (#4) -(others may be added later in the future for the 1.7 release series) -*/ - -#include "Globals.h" -#include "Protocol17x.h" -#include "ChunkDataSerializer.h" -#include "../ClientHandle.h" -#include "../Root.h" -#include "../Server.h" -#include "../World.h" -#include "../WorldStorage/FastNBT.h" -#include "../StringCompression.h" -#include "../Entities/Minecart.h" -#include "../Entities/FallingBlock.h" -#include "../Entities/Pickup.h" -#include "../Entities/Player.h" -#include "../Mobs/IncludeAllMonsters.h" -#include "../UI/Window.h" - - - - - -#define HANDLE_READ(Proc, Type, Var) \ - Type Var; \ - m_ReceivedData.Proc(Var); - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return false; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - - -cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) : - super(a_Client), - m_ServerAddress(a_ServerAddress), - m_ServerPort(a_ServerPort), - m_State(a_State), - m_ReceivedData(32 KiB), - m_OutPacketBuffer(64 KiB), - m_OutPacketLenBuffer(20), // 20 bytes is more than enough for one VarInt - m_IsEncrypted(false) -{ -} - - - - - -void cProtocol172::DataReceived(const char * a_Data, int a_Size) -{ - if (m_IsEncrypted) - { - byte Decrypted[512]; - while (a_Size > 0) - { - int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; - m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); - AddReceivedData((const char *)Decrypted, NumBytes); - a_Size -= NumBytes; - a_Data += NumBytes; - } - } - else - { - AddReceivedData(a_Data, a_Size); - } -} - - - - - -void cProtocol172::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - cPacketizer Pkt(*this, 0x1b); // Attach Entity packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteInt((a_Vehicle != NULL) ? a_Vehicle->GetUniqueID() : 0); - Pkt.WriteBool(false); -} - - - - - -void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - cPacketizer Pkt(*this, 0x24); // Block Action packet - Pkt.WriteInt(a_BlockX); - Pkt.WriteShort(a_BlockY); - Pkt.WriteInt(a_BlockZ); - Pkt.WriteByte(a_Byte1); - Pkt.WriteByte(a_Byte2); - Pkt.WriteVarInt(a_BlockType); -} - - - - - -void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) -{ - cPacketizer Pkt(*this, 0x24); // Block Break Animation packet - Pkt.WriteInt(a_EntityID); - Pkt.WriteInt(a_BlockX); - Pkt.WriteInt(a_BlockY); - Pkt.WriteInt(a_BlockZ); - Pkt.WriteChar(a_Stage); -} - - - - - -void cProtocol172::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cPacketizer Pkt(*this, 0x23); // Block Change packet - Pkt.WriteInt(a_BlockX); - Pkt.WriteByte(a_BlockY); - Pkt.WriteInt(a_BlockZ); - Pkt.WriteVarInt(a_BlockType); - Pkt.WriteByte(a_BlockMeta); -} - - - - - -void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) -{ - cPacketizer Pkt(*this, 0x22); // Multi Block Change packet - Pkt.WriteInt(a_ChunkX); - Pkt.WriteInt(a_ChunkZ); - Pkt.WriteShort((short)a_Changes.size()); - Pkt.WriteInt(a_Changes.size() * 4); - for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) - { - unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); - unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); - Pkt.WriteInt((Coords << 16) | Blocks); - } // for itr - a_Changes[] -} - - - - - -void cProtocol172::SendChat(const AString & a_Message) -{ - cPacketizer Pkt(*this, 0x02); // Chat Message packet - Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str())); -} - - - - - -void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - // Serialize first, before creating the Packetizer (the packetizer locks a CS) - // This contains the flags and bitmasks, too - const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); - - cPacketizer Pkt(*this, 0x21); // Chunk Data packet - Pkt.WriteInt(a_ChunkX); - Pkt.WriteInt(a_ChunkZ); - Pkt.WriteBuf(ChunkData.data(), ChunkData.size()); -} - - - - - -void cProtocol172::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) -{ - cPacketizer Pkt(*this, 0x0d); // Collect Item packet - Pkt.WriteInt(a_Pickup.GetUniqueID()); - Pkt.WriteInt(a_Player.GetUniqueID()); -} - - - - - -void cProtocol172::SendDestroyEntity(const cEntity & a_Entity) -{ - cPacketizer Pkt(*this, 0x13); // Destroy Entities packet - Pkt.WriteByte(1); - Pkt.WriteInt(a_Entity.GetUniqueID()); -} - - - - - -void cProtocol172::SendDisconnect(const AString & a_Reason) -{ - cPacketizer Pkt(*this, 0x40); - Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str())); -} - - - - - -void cProtocol172::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cPacketizer Pkt(*this, 0x36); // Sign Editor Open packet - Pkt.WriteInt(a_BlockX); - Pkt.WriteInt(a_BlockY); - Pkt.WriteInt(a_BlockZ); -} - - - - - -void cProtocol172::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - cPacketizer Pkt(*this, 0x04); // Entity Equipment packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteShort(a_SlotNum); - Pkt.WriteItem(a_Item); -} - - - - - -void cProtocol172::SendEntityHeadLook(const cEntity & a_Entity) -{ - cPacketizer Pkt(*this, 0x19); // Entity Head Look packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByteAngle(a_Entity.GetHeadYaw()); -} - - - - - -void cProtocol172::SendEntityLook(const cEntity & a_Entity) -{ - cPacketizer Pkt(*this, 0x16); // Entity Look packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByteAngle(a_Entity.GetYaw()); - Pkt.WriteByteAngle(a_Entity.GetPitch()); -} - - - - - -void cProtocol172::SendEntityMetadata(const cEntity & a_Entity) -{ - cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteEntityMetadata(a_Entity); - Pkt.WriteByte(0x7f); // The termination byte -} - - - - - -void cProtocol172::SendEntityProperties(const cEntity & a_Entity) -{ - cPacketizer Pkt(*this, 0x20); // Entity Properties packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteEntityProperties(a_Entity); -} - - - - - -void cProtocol172::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - cPacketizer Pkt(*this, 0x15); // Entity Relative Move packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_RelX); - Pkt.WriteByte(a_RelY); - Pkt.WriteByte(a_RelZ); -} - - - - - -void cProtocol172::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - cPacketizer Pkt(*this, 0x17); // Entity Look And Relative Move packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_RelX); - Pkt.WriteByte(a_RelY); - Pkt.WriteByte(a_RelZ); - Pkt.WriteByteAngle(a_Entity.GetYaw()); - Pkt.WriteByteAngle(a_Entity.GetPitch()); -} - - - - - -void cProtocol172::SendEntityStatus(const cEntity & a_Entity, char a_Status) -{ - cPacketizer Pkt(*this, 0x1a); // Entity Status packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteChar(a_Status); -} - - - - - -void cProtocol172::SendEntityVelocity(const cEntity & a_Entity) -{ - cPacketizer Pkt(*this, 0x12); // Entity Velocity packet - Pkt.WriteInt(a_Entity.GetUniqueID()); - // 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick - Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400)); -} - - - - - -void cProtocol172::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) -{ - cPacketizer Pkt(*this, 0x27); // Explosion packet - Pkt.WriteFloat((float)a_BlockX); - Pkt.WriteFloat((float)a_BlockY); - Pkt.WriteFloat((float)a_BlockZ); - Pkt.WriteFloat((float)a_Radius); - Pkt.WriteInt(a_BlocksAffected.size()); - for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(), end = a_BlocksAffected.end(); itr != end; ++itr) - { - Pkt.WriteChar((char)itr->x); - Pkt.WriteChar((char)itr->y); - Pkt.WriteChar((char)itr->z); - } // for itr - a_BlockAffected[] - Pkt.WriteFloat((float)a_PlayerMotion.x); - Pkt.WriteFloat((float)a_PlayerMotion.y); - Pkt.WriteFloat((float)a_PlayerMotion.z); -} - - - - - -void cProtocol172::SendGameMode(eGameMode a_GameMode) -{ - cPacketizer Pkt(*this, 0x2b); // Change Game State packet - Pkt.WriteByte(3); // Reason: Change game mode - Pkt.WriteFloat((float)a_GameMode); -} - - - - - -void cProtocol172::SendHealth(void) -{ - cPacketizer Pkt(*this, 0x06); // Update Health packet - Pkt.WriteFloat((float)m_Client->GetPlayer()->GetHealth()); - Pkt.WriteShort(m_Client->GetPlayer()->GetFoodLevel()); - Pkt.WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); -} - - - - - -void cProtocol172::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) -{ - cPacketizer Pkt(*this, 0x2f); // Set Slot packet - Pkt.WriteChar(a_WindowID); - Pkt.WriteShort(a_SlotNum); - Pkt.WriteItem(a_Item); -} - - - - - -void cProtocol172::SendKeepAlive(int a_PingID) -{ - cPacketizer Pkt(*this, 0x00); // Keep Alive packet - Pkt.WriteInt(a_PingID); -} - - - - - -void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) -{ - // Send the Join Game packet: - { - cPacketizer Pkt(*this, 0x01); // Join Game packet - Pkt.WriteInt(a_Player.GetUniqueID()); - Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (cRoot::Get()->GetServer()->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 - Pkt.WriteChar((char)a_World.GetDimension()); - Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) - Pkt.WriteByte(cRoot::Get()->GetServer()->GetMaxPlayers()); - Pkt.WriteString("default"); // Level type - wtf? - } - - // Send the spawn position: - { - cPacketizer Pkt(*this, 0x05); // Spawn Position packet - Pkt.WriteInt((int)a_World.GetSpawnX()); - Pkt.WriteInt((int)a_World.GetSpawnY()); - Pkt.WriteInt((int)a_World.GetSpawnZ()); - } - - // Send player abilities: - SendPlayerAbilities(); -} - - - - - -void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup) -{ - { - cPacketizer Pkt(*this, 0x0e); // Spawn Object packet - Pkt.WriteVarInt(a_Pickup.GetUniqueID()); - Pkt.WriteByte(2); // Type = Pickup - Pkt.WriteFPInt(a_Pickup.GetPosX()); - Pkt.WriteFPInt(a_Pickup.GetPosY()); - Pkt.WriteFPInt(a_Pickup.GetPosZ()); - Pkt.WriteByteAngle(a_Pickup.GetYaw()); - Pkt.WriteByteAngle(a_Pickup.GetPitch()); - Pkt.WriteInt(0); // No object data - } - { - cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet - Pkt.WriteInt(a_Pickup.GetUniqueID()); - Pkt.WriteByte((0x05 << 5) | 10); // Slot type + index 10 - Pkt.WriteItem(a_Pickup.GetItem()); - Pkt.WriteByte(0x7f); // End of metadata - } -} - - - - - -void cProtocol172::SendPlayerAbilities(void) -{ - cPacketizer Pkt(*this, 0x39); // Player Abilities packet - Byte Flags = 0; - if (m_Client->GetPlayer()->IsGameModeCreative()) - { - Flags |= 0x01; - } - // TODO: Other flags (god mode, flying, can fly - Pkt.WriteByte(Flags); - // TODO: Pkt.WriteFloat(m_Client->GetPlayer()->GetMaxFlyingSpeed()); - Pkt.WriteFloat(0.05f); - Pkt.WriteFloat((float)m_Client->GetPlayer()->GetMaxSpeed()); -} - - - - - -void cProtocol172::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) -{ - cPacketizer Pkt(*this, 0x0b); // Animation packet - Pkt.WriteVarInt(a_Player.GetUniqueID()); - Pkt.WriteChar(a_Animation); -} - - - - - -void cProtocol172::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) -{ - cPacketizer Pkt(*this, 0x38); // Playerlist Item packet - Pkt.WriteString(a_Player.GetName()); - Pkt.WriteBool(a_IsOnline); - Pkt.WriteShort(a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0); -} - - - - - -void cProtocol172::SendPlayerMaxSpeed(void) -{ - cPacketizer Pkt(*this, 0x20); // Entity Properties - Pkt.WriteInt(m_Client->GetPlayer()->GetUniqueID()); - Pkt.WriteInt(1); // Count - Pkt.WriteString("generic.movementSpeed"); - Pkt.WriteDouble(0.1); - if (m_Client->GetPlayer()->IsSprinting()) - { - Pkt.WriteShort(1); // Modifier count - Pkt.WriteInt64(0x662a6b8dda3e4c1c); - Pkt.WriteInt64(0x881396ea6097278d); // UUID of the modifier - Pkt.WriteDouble(0.3); - Pkt.WriteByte(2); - } - else - { - Pkt.WriteShort(0); // Modifier count - } -} - - - - - -void cProtocol172::SendPlayerMoveLook(void) -{ - cPacketizer Pkt(*this, 0x08); // Player Position And Look packet - Pkt.WriteDouble(m_Client->GetPlayer()->GetPosX()); - Pkt.WriteDouble(m_Client->GetPlayer()->GetPosY()); - Pkt.WriteDouble(m_Client->GetPlayer()->GetPosZ()); - Pkt.WriteFloat((float)m_Client->GetPlayer()->GetYaw()); - Pkt.WriteFloat((float)m_Client->GetPlayer()->GetPitch()); - Pkt.WriteBool(m_Client->GetPlayer()->IsOnGround()); -} - - - - - -void cProtocol172::SendPlayerPosition(void) -{ - // There is no dedicated packet for this, send the whole thing: - SendPlayerMoveLook(); -} - - - - - -void cProtocol172::SendPlayerSpawn(const cPlayer & a_Player) -{ - // Called to spawn another player for the client - cPacketizer Pkt(*this, 0x0c); // Spawn Player packet - Pkt.WriteVarInt(a_Player.GetUniqueID()); - Pkt.WriteString(Printf("%d", a_Player.GetUniqueID())); // TODO: Proper UUID - Pkt.WriteString(a_Player.GetName()); - Pkt.WriteFPInt(a_Player.GetPosX()); - Pkt.WriteFPInt(a_Player.GetPosY()); - Pkt.WriteFPInt(a_Player.GetPosZ()); - Pkt.WriteByteAngle(a_Player.GetYaw()); - Pkt.WriteByteAngle(a_Player.GetPitch()); - short ItemType = a_Player.GetEquippedItem().IsEmpty() ? 0 : a_Player.GetEquippedItem().m_ItemType; - Pkt.WriteShort(ItemType); - Pkt.WriteByte((3 << 5) | 6); // Metadata: float + index 6 - Pkt.WriteFloat((float)a_Player.GetHealth()); - Pkt.WriteByte(0x7f); // Metadata: end -} - - - - - -void cProtocol172::SendRespawn(void) -{ - cPacketizer Pkt(*this, 0x07); // Respawn packet - Pkt.WriteInt(m_Client->GetPlayer()->GetWorld()->GetDimension()); - Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) - Pkt.WriteByte((Byte)m_Client->GetPlayer()->GetEffectiveGameMode()); - Pkt.WriteString("default"); -} - - - - - -void cProtocol172::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) // a_Src coords are Block * 8 -{ - cPacketizer Pkt(*this, 0x29); // Sound Effect packet - Pkt.WriteString(a_SoundName); - Pkt.WriteInt(a_SrcX); - Pkt.WriteInt(a_SrcY); - Pkt.WriteInt(a_SrcZ); - Pkt.WriteFloat(a_Volume); - Pkt.WriteByte((Byte)(a_Pitch * 63)); -} - - - - - -void cProtocol172::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - cPacketizer Pkt(*this, 0x28); // Effect packet - Pkt.WriteInt(a_EffectID); - Pkt.WriteInt(a_SrcX); - Pkt.WriteByte(a_SrcY); - Pkt.WriteInt(a_SrcZ); - Pkt.WriteInt(a_Data); - Pkt.WriteBool(false); -} - - - - - -void cProtocol172::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) -{ - cPacketizer Pkt(*this, 0x0e); // Spawn Object packet - Pkt.WriteVarInt(a_FallingBlock.GetUniqueID()); - Pkt.WriteByte(70); // Falling block - Pkt.WriteFPInt(a_FallingBlock.GetPosX()); - Pkt.WriteFPInt(a_FallingBlock.GetPosY()); - Pkt.WriteFPInt(a_FallingBlock.GetPosZ()); - Pkt.WriteByteAngle(a_FallingBlock.GetYaw()); - Pkt.WriteByteAngle(a_FallingBlock.GetPitch()); - Pkt.WriteInt(((int)a_FallingBlock.GetBlockType()) | (((int)a_FallingBlock.GetBlockMeta()) << 12)); - Pkt.WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); -} - - - - - -void cProtocol172::SendSpawnMob(const cMonster & a_Mob) -{ - cPacketizer Pkt(*this, 0x0f); // Spawn Mob packet - Pkt.WriteVarInt(a_Mob.GetUniqueID()); - Pkt.WriteByte((Byte)a_Mob.GetMobType()); - Pkt.WriteFPInt(a_Mob.GetPosX()); - Pkt.WriteFPInt(a_Mob.GetPosY()); - Pkt.WriteFPInt(a_Mob.GetPosZ()); - Pkt.WriteByteAngle(a_Mob.GetPitch()); - Pkt.WriteByteAngle(a_Mob.GetHeadYaw()); - Pkt.WriteByteAngle(a_Mob.GetYaw()); - Pkt.WriteShort((short)(a_Mob.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Mob.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Mob.GetSpeedZ() * 400)); - Pkt.WriteEntityMetadata(a_Mob); - Pkt.WriteByte(0x7f); // Metadata terminator -} - - - - - -void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) -{ - cPacketizer Pkt(*this, 0xe); // Spawn Object packet - Pkt.WriteVarInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_ObjectType); - Pkt.WriteFPInt(a_Entity.GetPosX()); - Pkt.WriteFPInt(a_Entity.GetPosY()); - Pkt.WriteFPInt(a_Entity.GetPosZ()); - Pkt.WriteByteAngle(a_Entity.GetYaw()); - Pkt.WriteByteAngle(a_Entity.GetPitch()); - Pkt.WriteInt(a_ObjectData); - if (a_ObjectData != 0) - { - Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400)); - } -} - - - - - -void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) -{ - cPacketizer Pkt(*this, 0xe); // Spawn Object packet - Pkt.WriteVarInt(a_Vehicle.GetUniqueID()); - Pkt.WriteByte(a_VehicleType); - Pkt.WriteFPInt(a_Vehicle.GetPosX()); - Pkt.WriteFPInt(a_Vehicle.GetPosY()); - Pkt.WriteFPInt(a_Vehicle.GetPosZ()); - Pkt.WriteByteAngle(a_Vehicle.GetYaw()); - Pkt.WriteByteAngle(a_Vehicle.GetPitch()); - Pkt.WriteInt(a_VehicleSubType); - if (a_VehicleSubType != 0) - { - Pkt.WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); - } -} - - - - - -void cProtocol172::SendTabCompletionResults(const AStringVector & a_Results) -{ - AString Results; - Results.reserve(500); // Make a moderate reservation to avoid excessive reallocations - for (AStringVector::const_iterator itr = a_Results.begin(), end = a_Results.end(); itr != end; ++itr) - { - Results.append(*itr); - Results.push_back(0); - } - - cPacketizer Pkt(*this, 0x3a); // Tab-Complete packet - Pkt.WriteVarInt(a_Results.size()); - Pkt.WriteString(Results); -} - - - - - -void cProtocol172::SendTeleportEntity(const cEntity & a_Entity) -{ - cPacketizer Pkt(*this, 0x18); - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteFPInt(a_Entity.GetPosX()); - Pkt.WriteFPInt(a_Entity.GetPosY()); - Pkt.WriteFPInt(a_Entity.GetPosZ()); - Pkt.WriteByteAngle(a_Entity.GetYaw()); - Pkt.WriteByteAngle(a_Entity.GetPitch()); -} - - - - - -void cProtocol172::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cPacketizer Pkt(*this, 0x2c); // Spawn Global Entity packet - Pkt.WriteVarInt(0); // EntityID = 0, always - Pkt.WriteByte(1); // Type = Thunderbolt - Pkt.WriteFPInt(a_BlockX); - Pkt.WriteFPInt(a_BlockY); - Pkt.WriteFPInt(a_BlockZ); -} - - - - - -void cProtocol172::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) -{ - cPacketizer Pkt(*this, 0x03); - Pkt.WriteInt64(a_WorldAge); - Pkt.WriteInt64(a_TimeOfDay); -} - - - - - -void cProtocol172::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - cPacketizer Pkt(*this, 0x21); // Chunk Data packet - Pkt.WriteInt(a_ChunkX); - Pkt.WriteInt(a_ChunkZ); - Pkt.WriteBool(true); - Pkt.WriteShort(0); // Primary bitmap - Pkt.WriteShort(0); // Add bitmap - Pkt.WriteInt(0); // Compressed data size -} - - - - - -void cProtocol172::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) -{ - cPacketizer Pkt(*this, 0x33); - Pkt.WriteInt(a_BlockX); - Pkt.WriteShort((short)a_BlockY); - Pkt.WriteInt(a_BlockZ); - Pkt.WriteString(a_Line1); - Pkt.WriteString(a_Line2); - Pkt.WriteString(a_Line3); - Pkt.WriteString(a_Line4); -} - - - - - -void cProtocol172::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cPacketizer Pkt(*this, 0x0a); - Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteInt(a_BlockX); - Pkt.WriteByte((Byte)a_BlockY); - Pkt.WriteInt(a_BlockZ); -} - - - - - -void cProtocol172::SendWeather(eWeather a_Weather) -{ - { - cPacketizer Pkt(*this, 0x2b); // Change Game State packet - Pkt.WriteByte((a_Weather == wSunny) ? 1 : 2); // End rain / begin rain - Pkt.WriteFloat(0); // Unused for weather - } - - // TODO: Fade effect, somehow -} - - - - - -void cProtocol172::SendWholeInventory(const cWindow & a_Window) -{ - cPacketizer Pkt(*this, 0x30); // Window Items packet - Pkt.WriteChar(a_Window.GetWindowID()); - Pkt.WriteShort(a_Window.GetNumSlots()); - cItems Slots; - a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); - for (cItems::const_iterator itr = Slots.begin(), end = Slots.end(); itr != end; ++itr) - { - Pkt.WriteItem(*itr); - } // for itr - Slots[] -} - - - - - -void cProtocol172::SendWindowClose(const cWindow & a_Window) -{ - cPacketizer Pkt(*this, 0x2e); - Pkt.WriteChar(a_Window.GetWindowID()); -} - - - - - -void cProtocol172::SendWindowOpen(const cWindow & a_Window) -{ - if (a_Window.GetWindowType() < 0) - { - // Do not send this packet for player inventory windows - return; - } - - cPacketizer Pkt(*this, 0x2d); - Pkt.WriteChar(a_Window.GetWindowID()); - Pkt.WriteChar(a_Window.GetWindowType()); - Pkt.WriteString(a_Window.GetWindowTitle()); - Pkt.WriteChar(a_Window.GetNumNonInventorySlots()); - Pkt.WriteBool(true); - if (a_Window.GetWindowType() == cWindow::wtAnimalChest) - { - Pkt.WriteInt(0); // TODO: The animal's EntityID - } -} - - - - - -void cProtocol172::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) -{ - cPacketizer Pkt(*this, 0x31); // Window Property packet - Pkt.WriteChar(a_Window.GetWindowID()); - Pkt.WriteShort(a_Property); - Pkt.WriteShort(a_Value); -} - - - - - -void cProtocol172::AddReceivedData(const char * a_Data, int a_Size) -{ - if (!m_ReceivedData.Write(a_Data, a_Size)) - { - // Too much data in the incoming queue, report to caller: - m_Client->PacketBufferFull(); - return; - } - - // Handle all complete packets: - while (true) - { - UInt32 PacketLen; - if (!m_ReceivedData.ReadVarInt(PacketLen)) - { - // Not enough data - return; - } - if (!m_ReceivedData.CanReadBytes(PacketLen)) - { - // The full packet hasn't been received yet - return; - } - UInt32 PacketType; - UInt32 Mark1 = m_ReceivedData.GetReadableSpace(); - if (!m_ReceivedData.ReadVarInt(PacketType)) - { - // Not enough data - return; - } - - UInt32 NumBytesRead = Mark1 - m_ReceivedData.GetReadableSpace(); - HandlePacket(PacketType, PacketLen - NumBytesRead); - - if (Mark1 - m_ReceivedData.GetReadableSpace() > PacketLen) - { - // Read more than packet length, report as error - m_Client->PacketError(PacketType); - } - - // Go to packet end in any case: - m_ReceivedData.ResetRead(); - m_ReceivedData.ReadVarInt(PacketType); - m_ReceivedData.SkipRead(PacketLen); - m_ReceivedData.CommitRead(); - } // while (true) -} - - - - -void cProtocol172::HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes) -{ - switch (m_State) - { - case 1: - { - // Status - switch (a_PacketType) - { - case 0x00: HandlePacketStatusRequest(a_RemainingBytes); return; - case 0x01: HandlePacketStatusPing (a_RemainingBytes); return; - } - break; - } - - case 2: - { - // Login - switch (a_PacketType) - { - case 0x00: HandlePacketLoginStart(a_RemainingBytes); return; - case 0x01: HandlePacketLoginEncryptionResponse(a_RemainingBytes); return; - } - break; - } - - case 3: - { - // Game - switch (a_PacketType) - { - case 0x00: HandlePacketKeepAlive (a_RemainingBytes); return; - case 0x01: HandlePacketChatMessage (a_RemainingBytes); return; - case 0x02: HandlePacketUseEntity (a_RemainingBytes); return; - case 0x03: HandlePacketPlayer (a_RemainingBytes); return; - case 0x04: HandlePacketPlayerPos (a_RemainingBytes); return; - case 0x05: HandlePacketPlayerLook (a_RemainingBytes); return; - case 0x06: HandlePacketPlayerPosLook (a_RemainingBytes); return; - case 0x07: HandlePacketBlockDig (a_RemainingBytes); return; - case 0x08: HandlePacketBlockPlace (a_RemainingBytes); return; - case 0x09: HandlePacketSlotSelect (a_RemainingBytes); return; - case 0x0a: HandlePacketAnimation (a_RemainingBytes); return; - case 0x0b: HandlePacketEntityAction (a_RemainingBytes); return; - case 0x0c: HandlePacketSteerVehicle (a_RemainingBytes); return; - case 0x0d: HandlePacketWindowClose (a_RemainingBytes); return; - case 0x0e: HandlePacketWindowClick (a_RemainingBytes); return; - case 0x0f: // Confirm transaction - not used in MCS - case 0x10: HandlePacketCreativeInventoryAction(a_RemainingBytes); return; - case 0x12: HandlePacketUpdateSign (a_RemainingBytes); return; - case 0x13: HandlePacketPlayerAbilities (a_RemainingBytes); return; - case 0x14: HandlePacketTabComplete (a_RemainingBytes); return; - case 0x15: HandlePacketClientSettings (a_RemainingBytes); return; - case 0x16: HandlePacketClientStatus (a_RemainingBytes); return; - case 0x17: HandlePacketPluginMessage (a_RemainingBytes); return; - } - break; - } - } // switch (m_State) - - // Unknown packet type, report to the client: - m_Client->PacketUnknown(a_PacketType); - m_ReceivedData.SkipRead(a_RemainingBytes); - m_ReceivedData.CommitRead(); -} - - - - - -void cProtocol172::HandlePacketStatusPing(UInt32 a_RemainingBytes) -{ - ASSERT(a_RemainingBytes == 8); - if (a_RemainingBytes != 8) - { - m_Client->PacketError(0x01); - m_ReceivedData.SkipRead(a_RemainingBytes); - m_ReceivedData.CommitRead(); - return; - } - Int64 Timestamp; - m_ReceivedData.ReadBEInt64(Timestamp); - m_ReceivedData.CommitRead(); - - cPacketizer Pkt(*this, 0x01); // Ping packet - Pkt.WriteInt64(Timestamp); -} - - - - - -void cProtocol172::HandlePacketStatusRequest(UInt32 a_RemainingBytes) -{ - // No more bytes in this packet - ASSERT(a_RemainingBytes == 0); - m_ReceivedData.CommitRead(); - - // Send the response: - AString Response = "{\"version\":{\"name\":\"1.7.2\",\"protocol\":4},\"players\":{"; - AppendPrintf(Response, "\"max\":%u,\"online\":%u,\"sample\":[]},", - cRoot::Get()->GetServer()->GetMaxPlayers(), - cRoot::Get()->GetServer()->GetNumPlayers() - ); - AppendPrintf(Response, "\"description\":{\"text\":\"%s\"}", - cRoot::Get()->GetServer()->GetDescription().c_str() - ); - Response.append("}"); - - cPacketizer Pkt(*this, 0x00); // Response packet - Pkt.WriteString(Response); -} - - - - - -void cProtocol172::HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes) -{ - // TODO: Add protocol encryption -} - - - - - -void cProtocol172::HandlePacketLoginStart(UInt32 a_RemainingBytes) -{ - AString Username; - m_ReceivedData.ReadVarUTF8String(Username); - - // TODO: Protocol encryption should be set up here if not localhost / auth - - // Send login success: - { - cPacketizer Pkt(*this, 0x02); // Login success packet - Pkt.WriteString(Printf("%d", m_Client->GetUniqueID())); // TODO: proper UUID - Pkt.WriteString(Username); - } - - m_State = 3; // State = Game - m_Client->HandleLogin(4, Username); -} - - - - - -void cProtocol172::HandlePacketAnimation(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEInt, int, EntityID); - HANDLE_READ(ReadByte, Byte, Animation); - m_Client->HandleAnimation(Animation); -} - - - - - -void cProtocol172::HandlePacketBlockDig(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadByte, Byte, Status); - HANDLE_READ(ReadBEInt, int, BlockX); - HANDLE_READ(ReadByte, Byte, BlockY); - HANDLE_READ(ReadBEInt, int, BlockZ); - HANDLE_READ(ReadByte, Byte, Face); - m_Client->HandleLeftClick(BlockX, BlockY, BlockZ, Face, Status); -} - - - - - -void cProtocol172::HandlePacketBlockPlace(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEInt, int, BlockX); - HANDLE_READ(ReadByte, Byte, BlockY); - HANDLE_READ(ReadBEInt, int, BlockZ); - HANDLE_READ(ReadByte, Byte, Face); - HANDLE_READ(ReadByte, Byte, CursorX); - HANDLE_READ(ReadByte, Byte, CursorY); - HANDLE_READ(ReadByte, Byte, CursorZ); - m_Client->HandleRightClick(BlockX, BlockY, BlockZ, Face, CursorX, CursorY, CursorZ, m_Client->GetPlayer()->GetEquippedItem()); -} - - - - - -void cProtocol172::HandlePacketChatMessage(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadVarUTF8String, AString, Message); - m_Client->HandleChat(Message); -} - - - - - -void cProtocol172::HandlePacketClientSettings(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadVarUTF8String, AString, Locale); - HANDLE_READ(ReadByte, Byte, ViewDistance); - HANDLE_READ(ReadByte, Byte, ChatFlags); - HANDLE_READ(ReadByte, Byte, Unused); - HANDLE_READ(ReadByte, Byte, Difficulty); - HANDLE_READ(ReadByte, Byte, ShowCape); - // TODO: handle in m_Client -} - - - - - -void cProtocol172::HandlePacketClientStatus(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadByte, Byte, ActionID); - switch (ActionID) - { - case 0: - { - // Respawn - m_Client->HandleRespawn(); - break; - } - case 1: - { - // Request stats - // TODO - break; - } - case 2: - { - // Open Inventory achievement - // TODO - break; - } - } -} - - - - - -void cProtocol172::HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEShort, short, SlotNum); - cItem Item; - if (!ReadItem(Item)) - { - return; - } - m_Client->HandleCreativeInventory(SlotNum, Item); -} - - - - - -void cProtocol172::HandlePacketEntityAction(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEInt, int, PlayerID); - HANDLE_READ(ReadByte, Byte, Action); - HANDLE_READ(ReadBEInt, int, JumpBoost); - m_Client->HandleEntityAction(PlayerID, Action); -} - - - - - -void cProtocol172::HandlePacketKeepAlive(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEInt, int, KeepAliveID); - m_Client->HandleKeepAlive(KeepAliveID); -} - - - - - -void cProtocol172::HandlePacketPlayer(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBool, bool, IsOnGround); - // TODO: m_Client->HandlePlayerOnGround(IsOnGround); -} - - - - - -void cProtocol172::HandlePacketPlayerAbilities(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadByte, Byte, Flags); - HANDLE_READ(ReadBEFloat, float, FlyingSpeed); - HANDLE_READ(ReadBEFloat, float, WalkingSpeed); - // TODO: m_Client->HandlePlayerAbilities(); -} - - - - - -void cProtocol172::HandlePacketPlayerLook(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEFloat, float, Yaw); - HANDLE_READ(ReadBEFloat, float, Pitch); - HANDLE_READ(ReadBool, bool, IsOnGround); - m_Client->HandlePlayerLook(Yaw, Pitch, IsOnGround); -} - - - - - -void cProtocol172::HandlePacketPlayerPos(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEDouble, double, PosX); - HANDLE_READ(ReadBEDouble, double, PosY); - HANDLE_READ(ReadBEDouble, double, Stance); - HANDLE_READ(ReadBEDouble, double, PosZ); - HANDLE_READ(ReadBool, bool, IsOnGround); - m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround); -} - - - - - -void cProtocol172::HandlePacketPlayerPosLook(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEDouble, double, PosX); - HANDLE_READ(ReadBEDouble, double, PosY); - HANDLE_READ(ReadBEDouble, double, Stance); - HANDLE_READ(ReadBEDouble, double, PosZ); - HANDLE_READ(ReadBEFloat, float, Yaw); - HANDLE_READ(ReadBEFloat, float, Pitch); - HANDLE_READ(ReadBool, bool, IsOnGround); - m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Yaw, Pitch, IsOnGround); -} - - - - - -void cProtocol172::HandlePacketPluginMessage(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadVarUTF8String, AString, Channel); - HANDLE_READ(ReadBEShort, short, Length); - AString Data; - m_ReceivedData.ReadString(Data, Length); - // TODO: m_Client->HandlePluginMessage(Channel, Data); -} - - - - - -void cProtocol172::HandlePacketSlotSelect(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEShort, short, SlotNum); - m_Client->HandleSlotSelected(SlotNum); -} - - - - - -void cProtocol172::HandlePacketSteerVehicle(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEFloat, float, Forward); - HANDLE_READ(ReadBEFloat, float, Sideways); - HANDLE_READ(ReadBool, bool, ShouldJump); - HANDLE_READ(ReadBool, bool, ShouldUnmount); - if (ShouldUnmount) - { - m_Client->HandleUnmount(); - } - else - { - m_Client->HandleSteerVehicle(Forward, Sideways); - } -} - - - - - -void cProtocol172::HandlePacketTabComplete(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadVarUTF8String, AString, Text); - m_Client->HandleTabCompletion(Text); -} - - - - - -void cProtocol172::HandlePacketUpdateSign(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEInt, int, BlockX); - HANDLE_READ(ReadBEShort, short, BlockY); - HANDLE_READ(ReadBEInt, int, BlockZ); - HANDLE_READ(ReadVarUTF8String, AString, Line1); - HANDLE_READ(ReadVarUTF8String, AString, Line2); - HANDLE_READ(ReadVarUTF8String, AString, Line3); - HANDLE_READ(ReadVarUTF8String, AString, Line4); - m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); -} - - - - - -void cProtocol172::HandlePacketUseEntity(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadBEInt, int, EntityID); - HANDLE_READ(ReadByte, Byte, MouseButton); - m_Client->HandleUseEntity(EntityID, (MouseButton == 1)); -} - - - - - -void cProtocol172::HandlePacketWindowClick(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadChar, char, WindowID); - HANDLE_READ(ReadBEShort, short, SlotNum); - HANDLE_READ(ReadByte, Byte, Button); - HANDLE_READ(ReadBEShort, short, TransactionID); - HANDLE_READ(ReadByte, Byte, Mode); - cItem Item; - ReadItem(Item); - - // Convert Button, Mode, SlotNum and HeldItem into eClickAction: - eClickAction Action; - switch ((Mode << 8) | Button) - { - case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break; - case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break; - case 0x0100: Action = caShiftLeftClick; break; - case 0x0101: Action = caShiftRightClick; break; - case 0x0200: Action = caNumber1; break; - case 0x0201: Action = caNumber2; break; - case 0x0202: Action = caNumber3; break; - case 0x0203: Action = caNumber4; break; - case 0x0204: Action = caNumber5; break; - case 0x0205: Action = caNumber6; break; - case 0x0206: Action = caNumber7; break; - case 0x0207: Action = caNumber8; break; - case 0x0208: Action = caNumber9; break; - case 0x0300: Action = caMiddleClick; break; - case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break; - case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break; - case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break; - case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break; - case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break; - case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break; - case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break; - case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break; - case 0x0600: Action = caDblClick; break; - } - - m_Client->HandleWindowClick(WindowID, SlotNum, Action, Item); -} - - - - - -void cProtocol172::HandlePacketWindowClose(UInt32 a_RemainingBytes) -{ - HANDLE_READ(ReadChar, char, WindowID); - m_Client->HandleWindowClose(WindowID); -} - - - - - -void cProtocol172::WritePacket(cByteBuffer & a_Packet) -{ - cCSLock Lock(m_CSPacket); - AString Pkt; - a_Packet.ReadAll(Pkt); - WriteVarInt(Pkt.size()); - SendData(Pkt.data(), Pkt.size()); - Flush(); -} - - - - - -void cProtocol172::SendData(const char * a_Data, int a_Size) -{ - if (m_IsEncrypted) - { - byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) - while (a_Size > 0) - { - int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; - m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); - m_Client->SendData((const char *)Encrypted, NumBytes); - a_Size -= NumBytes; - a_Data += NumBytes; - } - } - else - { - m_Client->SendData(a_Data, a_Size); - } -} - - - - - - -bool cProtocol172::ReadItem(cItem & a_Item) -{ - HANDLE_PACKET_READ(ReadBEShort, short, ItemType); - if (ItemType == -1) - { - // The item is empty, no more data follows - a_Item.Empty(); - return true; - } - a_Item.m_ItemType = ItemType; - - HANDLE_PACKET_READ(ReadChar, char, ItemCount); - HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); - a_Item.m_ItemCount = ItemCount; - a_Item.m_ItemDamage = ItemDamage; - if (ItemCount <= 0) - { - a_Item.Empty(); - } - - HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength); - if (MetadataLength <= 0) - { - return true; - } - - // Read the metadata - AString Metadata; - if (!m_ReceivedData.ReadString(Metadata, MetadataLength)) - { - return false; - } - - ParseItemMetadata(a_Item, Metadata); - return true; -} - - - - - -void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) -{ - // Uncompress the GZIPped data: - AString Uncompressed; - if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) - { - AString HexDump; - CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); - LOGWARNING("Cannot unGZIP item metadata (%u bytes):\n%s", a_Metadata.size(), HexDump.c_str()); - return; - } - - // Parse into NBT: - cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); - if (!NBT.IsValid()) - { - AString HexDump; - CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); - LOGWARNING("Cannot parse NBT item metadata: (%u bytes)\n%s", Uncompressed.size(), HexDump.c_str()); - return; - } - - // Load enchantments from the NBT: - for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) - { - if ( - (NBT.GetType(tag) == TAG_List) && - ( - (NBT.GetName(tag) == "ench") || - (NBT.GetName(tag) == "StoredEnchantments") - ) - ) - { - a_Item.m_Enchantments.ParseFromNBT(NBT, tag); - } - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol172::cPacketizer: - -cProtocol172::cPacketizer::~cPacketizer() -{ - AString DataToSend; - - // Send the packet length - UInt32 PacketLen = m_Out.GetUsedSpace(); - m_Protocol.m_OutPacketLenBuffer.WriteVarInt(PacketLen); - m_Protocol.m_OutPacketLenBuffer.ReadAll(DataToSend); - m_Protocol.SendData(DataToSend.data(), DataToSend.size()); - m_Protocol.m_OutPacketLenBuffer.CommitRead(); - - // Send the packet data: - m_Out.ReadAll(DataToSend); - m_Protocol.SendData(DataToSend.data(), DataToSend.size()); - m_Out.CommitRead(); -} - - - - - -void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item) -{ - short ItemType = a_Item.m_ItemType; - ASSERT(ItemType >= -1); // Check validity of packets in debug runtime - if (ItemType <= 0) - { - // Fix, to make sure no invalid values are sent. - ItemType = -1; - } - - if (a_Item.IsEmpty()) - { - WriteShort(-1); - return; - } - - WriteShort(ItemType); - WriteByte (a_Item.m_ItemCount); - WriteShort(a_Item.m_ItemDamage); - - if (a_Item.m_Enchantments.IsEmpty()) - { - WriteShort(-1); - return; - } - - // Send the enchantments: - cFastNBTWriter Writer; - const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; - a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); - Writer.Finish(); - AString Compressed; - CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); - WriteShort(Compressed.size()); - WriteBuf(Compressed.data(), Compressed.size()); -} - - - - - -void cProtocol172::cPacketizer::WriteByteAngle(double a_Angle) -{ - WriteByte((char)(255 * a_Angle / 360)); -} - - - - - -void cProtocol172::cPacketizer::WriteFPInt(double a_Value) -{ - int Value = (int)(a_Value * 32); - WriteInt(Value); -} - - - - - -void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity) -{ - // Common metadata: - Byte Flags = 0; - if (a_Entity.IsOnFire()) - { - Flags |= 0x01; - } - if (a_Entity.IsCrouched()) - { - Flags |= 0x02; - } - if (a_Entity.IsSprinting()) - { - Flags |= 0x08; - } - if (a_Entity.IsRclking()) - { - Flags |= 0x10; - } - if (a_Entity.IsInvisible()) - { - Flags |= 0x20; - } - WriteByte(0); // Byte(0) + index 0 - WriteByte(Flags); - - switch (a_Entity.GetEntityType()) - { - case cEntity::etPlayer: break; // TODO? - case cEntity::etPickup: - { - WriteByte((5 << 5) | 10); // Slot(5) + index 10 - WriteItem(((const cPickup &)a_Entity).GetItem()); - break; - } - case cEntity::etMinecart: - { - WriteByte(0x51); - - // The following expression makes Minecarts shake more with less health or higher damage taken - // It gets half the maximum health, and takes it away from the current health minus the half health: - /* Health: 5 | 3 - (5 - 3) = 1 (shake power) - Health: 3 | 3 - (3 - 3) = 3 - Health: 1 | 3 - (1 - 3) = 5 - */ - WriteInt((((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4); - WriteByte(0x52); - WriteInt(1); // Shaking direction, doesn't seem to affect anything - WriteByte(0x73); - WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer - - if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) - { - WriteByte(0x10); - WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); - } - break; - } - case cEntity::etProjectile: - { - if (((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow) - { - WriteByte(0x10); - WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); - } - break; - } - case cEntity::etMonster: - { - WriteMobMetadata((const cMonster &)a_Entity); - break; - } - } -} - - - - - -void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) -{ - switch (a_Mob.GetMobType()) - { - case cMonster::mtCreeper: - { - WriteByte(0x10); - WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); - WriteByte(0x11); - WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); - break; - } - - case cMonster::mtBat: - { - WriteByte(0x10); - WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); - break; - } - - case cMonster::mtPig: - { - WriteByte(0x10); - WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); - break; - } - - case cMonster::mtVillager: - { - WriteByte(0x50); - WriteInt(((const cVillager &)a_Mob).GetVilType()); - break; - } - - case cMonster::mtZombie: - { - WriteByte(0x0c); - WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); - WriteByte(0x0d); - WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); - WriteByte(0x0e); - WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); - break; - } - - case cMonster::mtGhast: - { - WriteByte(0x10); - WriteByte(((const cGhast &)a_Mob).IsCharging()); - break; - } - - case cMonster::mtWolf: - { - const cWolf & Wolf = (const cWolf &)a_Mob; - Byte WolfStatus = 0; - if (Wolf.IsSitting()) - { - WolfStatus |= 0x1; - } - if (Wolf.IsAngry()) - { - WolfStatus |= 0x2; - } - if (Wolf.IsTame()) - { - WolfStatus |= 0x4; - } - WriteByte(0x10); - WriteByte(WolfStatus); - - WriteByte(0x72); - WriteFloat((float)(a_Mob.GetHealth())); - WriteByte(0x13); - WriteByte(Wolf.IsBegging() ? 1 : 0); - WriteByte(0x14); - WriteByte(Wolf.GetCollarColor()); - break; - } - - case cMonster::mtSheep: - { - WriteByte(0x10); - Byte SheepMetadata = 0; - SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); - if (((const cSheep &)a_Mob).IsSheared()) - { - SheepMetadata |= 0x10; - } - WriteByte(SheepMetadata); - break; - } - - case cMonster::mtEnderman: - { - WriteByte(0x10); - WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); - WriteByte(0x11); - WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); - WriteByte(0x12); - WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); - break; - } - - case cMonster::mtSkeleton: - { - WriteByte(0x0d); - WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); - break; - } - - case cMonster::mtWitch: - { - WriteByte(0x15); - WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); - break; - } - - case cMonster::mtSlime: - { - WriteByte(0x10); - WriteByte(((const cSlime &)a_Mob).GetSize()); - break; - } - - case cMonster::mtMagmaCube: - { - WriteByte(0x10); - WriteByte(((const cMagmaCube &)a_Mob).GetSize()); - break; - } - - case cMonster::mtHorse: - { - const cHorse & Horse = (const cHorse &)a_Mob; - int Flags = 0; - if (Horse.IsTame()) - { - Flags |= 0x02; - } - if (Horse.IsSaddled()) - { - Flags |= 0x04; - } - if (Horse.IsChested()) - { - Flags |= 0x08; - } - if (Horse.IsBaby()) - { - Flags |= 0x10; - } - if (Horse.IsEating()) - { - Flags |= 0x20; - } - if (Horse.IsRearing()) - { - Flags |= 0x40; - } - if (Horse.IsMthOpen()) - { - Flags |= 0x80; - } - WriteByte(0x50); // Int at index 16 - WriteInt(Flags); - WriteByte(0x13); // Byte at index 19 - WriteByte(Horse.GetHorseType()); - WriteByte(0x54); // Int at index 20 - int Appearance = 0; - Appearance = Horse.GetHorseColor(); - Appearance |= Horse.GetHorseStyle() << 8; - WriteInt(Appearance); - WriteByte(0x56); // Int at index 22 - WriteInt(Horse.GetHorseArmour()); - break; - } - } // switch (a_Mob.GetType()) -} - - - - - -void cProtocol172::cPacketizer::WriteEntityProperties(const cEntity & a_Entity) -{ - if (!a_Entity.IsMob()) - { - // No properties for anything else than mobs - WriteInt(0); - return; - } - const cMonster & Mob = (const cMonster &)a_Entity; - - // TODO: Send properties and modifiers based on the mob type - - WriteInt(0); // NumProperties -} - - - - diff --git a/source/Protocol/Protocol17x.h b/source/Protocol/Protocol17x.h deleted file mode 100644 index 844069403..000000000 --- a/source/Protocol/Protocol17x.h +++ /dev/null @@ -1,258 +0,0 @@ - -// Protocol17x.h - -/* -Declares the 1.7.x protocol classes: - - cProtocol172 - - release 1.7.2 protocol (#4) -(others may be added later in the future for the 1.7 release series) -*/ - - - - - -#pragma once - -#include "Protocol.h" -#include "../ByteBuffer.h" -#include "../../CryptoPP/modes.h" -#include "../../CryptoPP/aes.h" - - - - - -class cProtocol172 : - public cProtocol // TODO -{ - typedef cProtocol super; // TODO - -public: - - cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); - - /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; - - /// Sending stuff to clients (alphabetically sorted): - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; - virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; - virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; - virtual void SendChat (const AString & a_Message) override; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; - virtual void SendDestroyEntity (const cEntity & a_Entity) override; - virtual void SendDisconnect (const AString & a_Reason) override; - virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; - virtual void SendEntityHeadLook (const cEntity & a_Entity) override; - virtual void SendEntityLook (const cEntity & a_Entity) override; - virtual void SendEntityMetadata (const cEntity & a_Entity) override; - virtual void SendEntityProperties (const cEntity & a_Entity) override; - virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; - virtual void SendEntityVelocity (const cEntity & a_Entity) override; - virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; - virtual void SendGameMode (eGameMode a_GameMode) override; - virtual void SendHealth (void) override; - virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; - virtual void SendKeepAlive (int a_PingID) override; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; - virtual void SendPickupSpawn (const cPickup & a_Pickup) override; - virtual void SendPlayerAbilities (void) override; - virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; - virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; - virtual void SendPlayerMaxSpeed (void) override; - virtual void SendPlayerMoveLook (void) override; - virtual void SendPlayerPosition (void) override; - virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendRespawn (void) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 - virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; - virtual void SendSpawnMob (const cMonster & a_Mob) override; - virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; - virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; - virtual void SendTabCompletionResults(const AStringVector & a_Results) override; - virtual void SendTeleportEntity (const cEntity & a_Entity) override; - virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; - virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; - virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; - virtual void SendWeather (eWeather a_Weather) override; - virtual void SendWholeInventory (const cWindow & a_Window) override; - virtual void SendWindowClose (const cWindow & a_Window) override; - virtual void SendWindowOpen (const cWindow & a_Window) override; - virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; - - virtual AString GetAuthServerID(void) override { return m_AuthServerID; } - -protected: - - /// Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed - class cPacketizer - { - public: - cPacketizer(cProtocol172 & a_Protocol, UInt32 a_PacketType) : - m_Protocol(a_Protocol), - m_Out(a_Protocol.m_OutPacketBuffer), - m_Lock(a_Protocol.m_CSPacket) - { - m_Out.WriteVarInt(a_PacketType); - } - - ~cPacketizer(); - - void WriteBool(bool a_Value) - { - m_Out.WriteBool(a_Value); - } - - void WriteByte(Byte a_Value) - { - m_Out.WriteByte(a_Value); - } - - void WriteChar(char a_Value) - { - m_Out.WriteChar(a_Value); - } - - void WriteShort(short a_Value) - { - m_Out.WriteBEShort(a_Value); - } - - void WriteInt(int a_Value) - { - m_Out.WriteBEInt(a_Value); - } - - void WriteInt64(Int64 a_Value) - { - m_Out.WriteBEInt64(a_Value); - } - - void WriteFloat(float a_Value) - { - m_Out.WriteBEFloat(a_Value); - } - - void WriteDouble(double a_Value) - { - m_Out.WriteBEDouble(a_Value); - } - - void WriteVarInt(UInt32 a_Value) - { - m_Out.WriteVarInt(a_Value); - } - - void WriteString(const AString & a_Value) - { - m_Out.WriteVarUTF8String(a_Value); - } - - void WriteBuf(const char * a_Data, int a_Size) - { - m_Out.Write(a_Data, a_Size); - } - - void WriteItem(const cItem & a_Item); - void WriteByteAngle(double a_Angle); // Writes the specified angle using a single byte - void WriteFPInt(double a_Value); // Writes the double value as a 27:5 fixed-point integer - void WriteEntityMetadata(const cEntity & a_Entity); // Writes the metadata for the specified entity, not including the terminating 0x7f - void WriteMobMetadata(const cMonster & a_Mob); // Writes the mob-specific metadata for the specified mob - void WriteEntityProperties(const cEntity & a_Entity); // Writes the entity properties for the specified entity, including the Count field - - protected: - cProtocol172 & m_Protocol; - cByteBuffer & m_Out; - cCSLock m_Lock; - } ; - - AString m_ServerAddress; - - UInt16 m_ServerPort; - - AString m_AuthServerID; - - /// State of the protocol. 1 = status, 2 = login, 3 = game - UInt32 m_State; - - /// Buffer for the received data - cByteBuffer m_ReceivedData; - - /// Buffer for composing the outgoing packets, through cPacketizer - cByteBuffer m_OutPacketBuffer; - - /// Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) - cByteBuffer m_OutPacketLenBuffer; - - bool m_IsEncrypted; - CryptoPP::CFB_Mode::Decryption m_Decryptor; - CryptoPP::CFB_Mode::Encryption m_Encryptor; - - - /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets - void AddReceivedData(const char * a_Data, int a_Size); - - /// Reads and handles the packet. The packet length and type have already been read. - void HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes); - - // Packet handlers while in the Status state (m_State == 1): - void HandlePacketStatusPing (UInt32 a_RemainingBytes); - void HandlePacketStatusRequest(UInt32 a_RemainingBytes); - - // Packet handlers while in the Login state (m_State == 2): - void HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes); - void HandlePacketLoginStart (UInt32 a_RemainingBytes); - - // Packet handlers while in the Game state (m_State == 3): - void HandlePacketAnimation (UInt32 a_RemainingBytes); - void HandlePacketBlockDig (UInt32 a_RemainingBytes); - void HandlePacketBlockPlace (UInt32 a_RemainingBytes); - void HandlePacketChatMessage (UInt32 a_RemainingBytes); - void HandlePacketClientSettings (UInt32 a_RemainingBytes); - void HandlePacketClientStatus (UInt32 a_RemainingBytes); - void HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes); - void HandlePacketEntityAction (UInt32 a_RemainingBytes); - void HandlePacketKeepAlive (UInt32 a_RemainingBytes); - void HandlePacketPlayer (UInt32 a_RemainingBytes); - void HandlePacketPlayerAbilities (UInt32 a_RemainingBytes); - void HandlePacketPlayerLook (UInt32 a_RemainingBytes); - void HandlePacketPlayerPos (UInt32 a_RemainingBytes); - void HandlePacketPlayerPosLook (UInt32 a_RemainingBytes); - void HandlePacketPluginMessage (UInt32 a_RemainingBytes); - void HandlePacketSlotSelect (UInt32 a_RemainingBytes); - void HandlePacketSteerVehicle (UInt32 a_RemainingBytes); - void HandlePacketTabComplete (UInt32 a_RemainingBytes); - void HandlePacketUpdateSign (UInt32 a_RemainingBytes); - void HandlePacketUseEntity (UInt32 a_RemainingBytes); - void HandlePacketWindowClick (UInt32 a_RemainingBytes); - void HandlePacketWindowClose (UInt32 a_RemainingBytes); - - - /// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. - void WritePacket(cByteBuffer & a_Packet); - - /// Sends the data to the client, encrypting them if needed. - virtual void SendData(const char * a_Data, int a_Size) override; - - void SendCompass(const cWorld & a_World); - - /// Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data - bool ReadItem(cItem & a_Item); - - /// Parses item metadata as read by ReadItem(), into the item enchantments. - void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); -} ; - - - - diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp deleted file mode 100644 index 9234785b5..000000000 --- a/source/Protocol/ProtocolRecognizer.cpp +++ /dev/null @@ -1,905 +0,0 @@ - -// ProtocolRecognizer.cpp - -// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple -// protocol versions and redirects everything to them - -#include "Globals.h" - -#include "ProtocolRecognizer.h" -#include "Protocol125.h" -#include "Protocol132.h" -#include "Protocol14x.h" -#include "Protocol15x.h" -#include "Protocol16x.h" -#include "Protocol17x.h" -#include "../ClientHandle.h" -#include "../Root.h" -#include "../Server.h" -#include "../World.h" -#include "../ChatColor.h" - - - - - -cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) : - super(a_Client), - m_Protocol(NULL), - m_Buffer(512) -{ -} - - - - - -cProtocolRecognizer::~cProtocolRecognizer() -{ - delete m_Protocol; -} - - - - - -AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) -{ - switch (a_ProtocolVersion) - { - case PROTO_VERSION_1_2_5: return "1.2.5"; - case PROTO_VERSION_1_3_2: return "1.3.2"; - case PROTO_VERSION_1_4_2: return "1.4.2"; - case PROTO_VERSION_1_4_4: return "1.4.4"; - case PROTO_VERSION_1_4_6: return "1.4.6"; - case PROTO_VERSION_1_5_0: return "1.5"; - case PROTO_VERSION_1_5_2: return "1.5.2"; - case PROTO_VERSION_1_6_1: return "1.6.1"; - case PROTO_VERSION_1_6_2: return "1.6.2"; - case PROTO_VERSION_1_6_3: return "1.6.3"; - case PROTO_VERSION_1_6_4: return "1.6.4"; - case PROTO_VERSION_1_7_2: return "1.7.2"; - } - ASSERT(!"Unknown protocol version"); - return Printf("Unknown protocol (%d)", a_ProtocolVersion); -} - - - - - -void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size) -{ - if (m_Protocol == NULL) - { - if (!m_Buffer.Write(a_Data, a_Size)) - { - m_Client->Kick("Unsupported protocol version"); - return; - } - - if (!TryRecognizeProtocol()) - { - return; - } - - // The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing: - AString Dump; - m_Buffer.ResetRead(); - m_Buffer.ReadAll(Dump); - m_Protocol->DataReceived(Dump.data(), Dump.size()); - } - else - { - m_Protocol->DataReceived(a_Data, a_Size); - } -} - - - - - -void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendAttachEntity(a_Entity, a_Vehicle); -} - - - - - -void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); -} - - - - - -void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage); -} - - - - - -void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); -} - - - - - -void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); -} - - - - - -void cProtocolRecognizer::SendChat(const AString & a_Message) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendChat(a_Message); -} - - - - - -void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); -} - - - - - -void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendCollectPickup(a_Pickup, a_Player); -} - - - - - -void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendDestroyEntity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendDisconnect(const AString & a_Reason) -{ - if (m_Protocol != NULL) - { - m_Protocol->SendDisconnect(a_Reason); - } - else - { - // This is used when the client sends a server-ping, respond with the default packet: - WriteByte ((char)0xff); // PACKET_DISCONNECT - WriteString(a_Reason); - } -} - - - - -void cProtocolRecognizer::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); -} - - - - - -void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityHeadLook(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityLook(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityMetadata(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityProperties(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityStatus(a_Entity, a_Status); -} - - - - - -void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityVelocity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion); -} - - - - - -void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendGameMode(a_GameMode); -} - - - - - -void cProtocolRecognizer::SendHealth(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendHealth(); -} - - - - - -void cProtocolRecognizer::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value); -} - - - - - -void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); -} - - - - - -void cProtocolRecognizer::SendKeepAlive(int a_PingID) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendKeepAlive(a_PingID); -} - - - - - -void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendLogin(a_Player, a_World); -} - - - - - -void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPickupSpawn(a_Pickup); -} - - - - - -void cProtocolRecognizer::SendPlayerAbilities(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerAbilities(); -} - - - - - -void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerAnimation(a_Player, a_Animation); -} - - - - - -void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); -} - - - - - -void cProtocolRecognizer::SendPlayerMaxSpeed(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerMaxSpeed(); -} - - - - - -void cProtocolRecognizer::SendPlayerMoveLook(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerMoveLook(); -} - - - - - -void cProtocolRecognizer::SendPlayerPosition(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerPosition(); -} - - - - - -void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerSpawn(a_Player); -} - - - - - -void cProtocolRecognizer::SendRespawn(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendRespawn(); -} - - - - - -void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); -} - - - - - -void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); -} - - - - - -void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSpawnFallingBlock(a_FallingBlock); -} - - - - - -void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSpawnMob(a_Mob); -} - - - - - -void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch); -} - - - - - -void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType); -} - - - - - -void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendTabCompletionResults(a_Results); -} - - - - - -void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendTeleportEntity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay); -} - - - - - -void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); -} - - - - - -void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); -} - - - - - -void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cProtocolRecognizer::SendWeather(eWeather a_Weather) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWeather(a_Weather); -} - - - - - -void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWholeInventory(a_Window); -} - - - - - -void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWindowClose(a_Window); -} - - - - - -void cProtocolRecognizer::SendWindowOpen(const cWindow & a_Window) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWindowOpen(a_Window); -} - - - - - -AString cProtocolRecognizer::GetAuthServerID(void) -{ - ASSERT(m_Protocol != NULL); - return m_Protocol->GetAuthServerID(); -} - - - - - -void cProtocolRecognizer::SendData(const char * a_Data, int a_Size) -{ - // This is used only when handling the server ping - m_Client->SendData(a_Data, a_Size); -} - - - - - -bool cProtocolRecognizer::TryRecognizeProtocol(void) -{ - // NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and - // MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro - - // The first packet should be a Handshake, 0x02: - unsigned char PacketType; - if (!m_Buffer.ReadByte(PacketType)) - { - return false; - } - switch (PacketType) - { - case 0x02: return TryRecognizeLengthlessProtocol(); // Handshake, continue recognizing - case 0xfe: - { - // This may be either a packet length or the length-less Ping packet - Byte NextByte; - if (!m_Buffer.ReadByte(NextByte)) - { - // Not enough data for either protocol - // This could actually happen with the 1.2 / 1.3 client, but their support is fading out anyway - return false; - } - if (NextByte != 0x01) - { - // This is definitely NOT a length-less Ping packet, handle as lengthed protocol: - break; - } - if (!m_Buffer.ReadByte(NextByte)) - { - // There is no more data. Although this *could* mean TCP fragmentation, it is highly unlikely - // and rather this is a 1.4 client sending a regular Ping packet (without the following Plugin message) - SendLengthlessServerPing(); - return false; - } - if (NextByte == 0xfa) - { - // Definitely a length-less Ping followed by a Plugin message - SendLengthlessServerPing(); - return false; - } - // Definitely a lengthed Initial handshake, handle below: - break; - } - } // switch (PacketType) - - // This must be a lengthed protocol, try if it has the entire initial handshake packet: - m_Buffer.ResetRead(); - UInt32 PacketLen; - UInt32 ReadSoFar = m_Buffer.GetReadableSpace(); - if (!m_Buffer.ReadVarInt(PacketLen)) - { - // Not enough bytes for the packet length, keep waiting - return false; - } - ReadSoFar -= m_Buffer.GetReadableSpace(); - if (!m_Buffer.CanReadBytes(PacketLen)) - { - // Not enough bytes for the packet, keep waiting - return false; - } - return TryRecognizeLengthedProtocol(PacketLen - ReadSoFar); -} - - - - - -bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void) -{ - // The comm started with 0x02, which is a Handshake packet in the length-less protocol family - // 1.3.2 starts with 0x02 0x39 - // 1.2.5 starts with 0x02 and name is expected to less than 0x3900 long :) - char ch; - if (!m_Buffer.ReadChar(ch)) - { - return false; - } - switch (ch) - { - case PROTO_VERSION_1_3_2: - { - m_Protocol = new cProtocol132(m_Client); - return true; - } - case PROTO_VERSION_1_4_2: - case PROTO_VERSION_1_4_4: - { - m_Protocol = new cProtocol142(m_Client); - return true; - } - case PROTO_VERSION_1_4_6: - { - m_Protocol = new cProtocol146(m_Client); - return true; - } - case PROTO_VERSION_1_5_0: - case PROTO_VERSION_1_5_2: - { - m_Protocol = new cProtocol150(m_Client); - return true; - } - case PROTO_VERSION_1_6_1: - { - m_Protocol = new cProtocol161(m_Client); - return true; - } - case PROTO_VERSION_1_6_2: - case PROTO_VERSION_1_6_3: - case PROTO_VERSION_1_6_4: - { - m_Protocol = new cProtocol162(m_Client); - return true; - } - } - m_Protocol = new cProtocol125(m_Client); - return true; -} - - - - - -bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining) -{ - UInt32 PacketType; - UInt32 NumBytesRead = m_Buffer.GetReadableSpace(); - if (!m_Buffer.ReadVarInt(PacketType)) - { - return false; - } - if (PacketType != 0x00) - { - // Not an initial handshake packet, we don't know how to talk to them - LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, initial packet %u)", - m_Client->GetIPString().c_str(), PacketType - ); - m_Client->Kick("Unsupported protocol version"); - return false; - } - UInt32 ProtocolVersion; - if (!m_Buffer.ReadVarInt(ProtocolVersion)) - { - return false; - } - NumBytesRead -= m_Buffer.GetReadableSpace(); - switch (ProtocolVersion) - { - case PROTO_VERSION_1_7_2: - { - AString ServerAddress; - short ServerPort; - UInt32 NextState; - m_Buffer.ReadVarUTF8String(ServerAddress); - m_Buffer.ReadBEShort(ServerPort); - m_Buffer.ReadVarInt(NextState); - m_Buffer.CommitRead(); - m_Protocol = new cProtocol172(m_Client, ServerAddress, ServerPort, NextState); - return true; - } - } - LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u)", - m_Client->GetIPString().c_str(), ProtocolVersion - ); - m_Client->Kick("Unsupported protocol version"); - return false; -} - - - - - -void cProtocolRecognizer::SendLengthlessServerPing(void) -{ - AString Reply; - switch (cRoot::Get()->GetPrimaryServerVersion()) - { - case PROTO_VERSION_1_2_5: - case PROTO_VERSION_1_3_2: - { - // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29 - Printf(Reply, "%s%s%i%s%i", - cRoot::Get()->GetServer()->GetDescription().c_str(), - cChatColor::Delimiter.c_str(), - cRoot::Get()->GetServer()->GetNumPlayers(), - cChatColor::Delimiter.c_str(), - cRoot::Get()->GetServer()->GetMaxPlayers() - ); - break; - } - - case PROTO_VERSION_1_4_2: - case PROTO_VERSION_1_4_4: - case PROTO_VERSION_1_4_6: - case PROTO_VERSION_1_5_0: - case PROTO_VERSION_1_5_2: - case PROTO_VERSION_1_6_1: - case PROTO_VERSION_1_6_2: - case PROTO_VERSION_1_6_3: - case PROTO_VERSION_1_6_4: - { - // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff. - // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29 - // _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit. - // Who cares? We're disconnecting anyway. - m_Buffer.ResetRead(); - if (m_Buffer.CanReadBytes(2)) - { - byte val; - m_Buffer.ReadByte(val); // Packet type - Serverlist ping - m_Buffer.ReadByte(val); // 0x01 magic value - ASSERT(val == 0x01); - } - - // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100 - AString NumPlayers; - Printf(NumPlayers, "%d", cRoot::Get()->GetServer()->GetNumPlayers()); - AString MaxPlayers; - Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers()); - - AString ProtocolVersionNum; - Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion()); - AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion())); - - // Cannot use Printf() because of in-string NUL bytes. - Reply = cChatColor::Delimiter; - Reply.append("1"); - Reply.push_back(0); - Reply.append(ProtocolVersionNum); - Reply.push_back(0); - Reply.append(ProtocolVersionTxt); - Reply.push_back(0); - Reply.append(cRoot::Get()->GetServer()->GetDescription()); - Reply.push_back(0); - Reply.append(NumPlayers); - Reply.push_back(0); - Reply.append(MaxPlayers); - break; - } - } // switch (m_PrimaryServerVersion) - m_Client->Kick(Reply); -} - - - - diff --git a/source/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h deleted file mode 100644 index c085e2cd8..000000000 --- a/source/Protocol/ProtocolRecognizer.h +++ /dev/null @@ -1,152 +0,0 @@ - -// ProtocolRecognizer.h - -// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple -// protocol versions and redirects everything to them - - - - - -#pragma once - -#include "Protocol.h" -#include "../ByteBuffer.h" - - - - - -// Adjust these if a new protocol is added or an old one is removed: -#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2" -#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4" - - - - - -class cProtocolRecognizer : - public cProtocol -{ - typedef cProtocol super; - -public: - enum - { - PROTO_VERSION_1_2_5 = 29, - PROTO_VERSION_1_3_2 = 39, - PROTO_VERSION_1_4_2 = 47, - PROTO_VERSION_1_4_4 = 49, - PROTO_VERSION_1_4_6 = 51, - PROTO_VERSION_1_5_0 = 60, - PROTO_VERSION_1_5_2 = 61, - PROTO_VERSION_1_6_1 = 73, - PROTO_VERSION_1_6_2 = 74, - PROTO_VERSION_1_6_3 = 77, - PROTO_VERSION_1_6_4 = 78, - - PROTO_VERSION_NEXT, - PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion - - // These will be kept "under" the next / latest, because the next and latest are only needed for previous protocols - PROTO_VERSION_1_7_2 = 4, - } ; - - cProtocolRecognizer(cClientHandle * a_Client); - virtual ~cProtocolRecognizer(); - - /// Translates protocol version number into protocol version text: 49 -> "1.4.4" - static AString GetVersionTextFromInt(int a_ProtocolVersion); - - /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; - - /// Sending stuff to clients (alphabetically sorted): - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; - virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; - virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; - virtual void SendChat (const AString & a_Message) override; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; - virtual void SendDestroyEntity (const cEntity & a_Entity) override; - virtual void SendDisconnect (const AString & a_Reason) override; - virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; - virtual void SendEntityHeadLook (const cEntity & a_Entity) override; - virtual void SendEntityLook (const cEntity & a_Entity) override; - virtual void SendEntityMetadata (const cEntity & a_Entity) override; - virtual void SendEntityProperties (const cEntity & a_Entity) override; - virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; - virtual void SendEntityVelocity (const cEntity & a_Entity) override; - virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; - virtual void SendGameMode (eGameMode a_GameMode) override; - virtual void SendHealth (void) override; - virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; - virtual void SendKeepAlive (int a_PingID) override; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; - virtual void SendPickupSpawn (const cPickup & a_Pickup) override; - virtual void SendPlayerAbilities (void) override; - virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; - virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; - virtual void SendPlayerMaxSpeed (void) override; - virtual void SendPlayerMoveLook (void) override; - virtual void SendPlayerPosition (void) override; - virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendRespawn (void) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; - virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; - virtual void SendSpawnMob (const cMonster & a_Mob) override; - virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; - virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; - virtual void SendTabCompletionResults(const AStringVector & a_Results) override; - virtual void SendTeleportEntity (const cEntity & a_Entity) override; - virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; - virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; - virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; - virtual void SendWeather (eWeather a_Weather) override; - virtual void SendWholeInventory (const cWindow & a_Window) override; - virtual void SendWindowClose (const cWindow & a_Window) override; - virtual void SendWindowOpen (const cWindow & a_Window) override; - virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; - - virtual AString GetAuthServerID(void) override; - - virtual void SendData(const char * a_Data, int a_Size) override; - -protected: - cProtocol * m_Protocol; //< The recognized protocol - cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol - - /// Tries to recognize protocol based on m_Buffer contents; returns true if recognized - bool TryRecognizeProtocol(void); - - /** Tries to recognize a protocol in the length-less family, based on m_Buffer; returns true if recognized. - Handles protocols before release 1.7, that didn't include packet lengths, and started with a 0x02 handshake packet - Note that length-less server ping is handled directly in TryRecognizeProtocol(), this function is called only - when the 0x02 Handshake packet has been received - */ - bool TryRecognizeLengthlessProtocol(void); - - /** Tries to recognize a protocol in the leghted family (1.7+), based on m_Buffer; returns true if recognized. - The packet length and type have already been read, type is 0 - The number of bytes remaining in the packet is passed as a_PacketLengthRemaining - **/ - bool TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining); - - /** Called when the recognizer gets a length-less protocol's server ping packet - Responds with server stats and destroys the client. - */ - void SendLengthlessServerPing(void); -} ; - - - - - diff --git a/source/RCONServer.cpp b/source/RCONServer.cpp deleted file mode 100644 index 93f2ccdd3..000000000 --- a/source/RCONServer.cpp +++ /dev/null @@ -1,332 +0,0 @@ - -// RCONServer.cpp - -// Implements the cRCONServer class representing the RCON server - -#include "Globals.h" -#include "../iniFile/iniFile.h" -#include "RCONServer.h" -#include "Server.h" -#include "Root.h" -#include "CommandOutput.h" - - - - - -// Disable MSVC warnings: -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4355) // 'this' : used in base member initializer list -#endif - - - - - -enum -{ - // Client -> Server: - RCON_PACKET_COMMAND = 2, - RCON_PACKET_LOGIN = 3, - - // Server -> Client: - RCON_PACKET_RESPONSE = 2, -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cRCONCommandOutput: - -class cRCONCommandOutput : - public cCommandOutputCallback -{ -public: - cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) : - m_Connection(a_Connection), - m_RequestID(a_RequestID) - { - } - - // cCommandOutputCallback overrides: - virtual void Out(const AString & a_Text) override - { - m_Buffer.append(a_Text); - } - - virtual void Finished(void) override - { - m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str()); - delete this; - } - -protected: - cRCONServer::cConnection & m_Connection; - int m_RequestID; - AString m_Buffer; -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cRCONServer: - -cRCONServer::cRCONServer(cServer & a_Server) : - m_Server(a_Server), - m_ListenThread4(*this, cSocket::IPv4, "RCON IPv4"), - m_ListenThread6(*this, cSocket::IPv6, "RCON IPv6") -{ -} - - - - - -cRCONServer::~cRCONServer() -{ - m_ListenThread4.Stop(); - m_ListenThread6.Stop(); -} - - - - - -void cRCONServer::Initialize(cIniFile & a_IniFile) -{ - if (!a_IniFile.GetValueSetB("RCON", "Enabled", false)) - { - return; - } - - // Read the password, don't allow an empty one: - m_Password = a_IniFile.GetValueSet("RCON", "Password", ""); - if (m_Password.empty()) - { - LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled."); - return; - } - - // Read and initialize both IPv4 and IPv6 ports for RCON - bool HasAnyPorts = false; - AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575"); - if (m_ListenThread4.Initialize(Ports4)) - { - HasAnyPorts = true; - m_ListenThread4.Start(); - } - AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575"); - if (m_ListenThread6.Initialize(Ports6)) - { - HasAnyPorts = true; - m_ListenThread6.Start(); - } - if (!HasAnyPorts) - { - LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled."); - return; - } -} - - - - - -void cRCONServer::OnConnectionAccepted(cSocket & a_Socket) -{ - if (!a_Socket.IsValid()) - { - return; - } - - LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str()); - - // Create a new cConnection object, it will be deleted when the connection is closed - m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket)); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cRCONServer::cConnection: - -cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) : - m_IsAuthenticated(false), - m_RCONServer(a_RCONServer), - m_Socket(a_Socket), - m_IPAddress(a_Socket.GetIPString()) -{ -} - - - - - -void cRCONServer::cConnection::DataReceived(const char * a_Data, int a_Size) -{ - // Append data to the buffer: - m_Buffer.append(a_Data, a_Size); - - // Process the packets in the buffer: - while (m_Buffer.size() >= 14) - { - int Length = IntFromBuffer(m_Buffer.data()); - if (Length > 1500) - { - // Too long, drop the connection - LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.", - Length, m_IPAddress.c_str() - ); - m_RCONServer.m_SocketThreads.RemoveClient(this); - m_Socket.CloseSocket(); - delete this; - return; - } - if (Length > (int)(m_Buffer.size() + 4)) - { - // Incomplete packet yet, wait for more data to come - return; - } - - int RequestID = IntFromBuffer(m_Buffer.data() + 4); - int PacketType = IntFromBuffer(m_Buffer.data() + 8); - if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12)) - { - m_RCONServer.m_SocketThreads.RemoveClient(this); - m_Socket.CloseSocket(); - delete this; - return; - } - m_Buffer.erase(0, Length + 4); - } // while (m_Buffer.size() >= 14) -} - - - - - -void cRCONServer::cConnection::GetOutgoingData(AString & a_Data) -{ - a_Data.assign(m_Outgoing); - m_Outgoing.clear(); -} - - - - - -void cRCONServer::cConnection::SocketClosed(void) -{ - m_RCONServer.m_SocketThreads.RemoveClient(this); - delete this; -} - - - - - -bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) -{ - switch (a_PacketType) - { - case RCON_PACKET_LOGIN: - { - if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0) - { - LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str()); - return false; - } - m_IsAuthenticated = true; - - LOGD("RCON: Client at %s has successfully authenticated", m_IPAddress.c_str()); - - // Send OK response: - SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); - return true; - } - - case RCON_PACKET_COMMAND: - { - if (!m_IsAuthenticated) - { - char AuthNeeded[] = "You need to authenticate first!"; - SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded); - return false; - } - - AString cmd(a_Payload, a_PayloadLength); - LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str()); - cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID))); - - // Send an empty response: - SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); - return true; - } - } - - // Unknown packet type, drop the connection: - LOGWARNING("RCON: Client at %s has sent an unknown packet type %d, dropping connection.", - m_IPAddress.c_str(), a_PacketType - ); - return false; -} - - - - - -/// Reads 4 bytes from a_Buffer and returns the int they represent -int cRCONServer::cConnection::IntFromBuffer(const char * a_Buffer) -{ - return ((unsigned char)a_Buffer[3] << 24) | ((unsigned char)a_Buffer[2] << 16) | ((unsigned char)a_Buffer[1] << 8) | (unsigned char)a_Buffer[0]; -} - - - - - -/// Puts 4 bytes representing the int into the buffer -void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer) -{ - a_Buffer[0] = a_Value & 0xff; - a_Buffer[1] = (a_Value >> 8) & 0xff; - a_Buffer[2] = (a_Value >> 16) & 0xff; - a_Buffer[3] = (a_Value >> 24) & 0xff; -} - - - - - -/// Sends a RCON packet back to the client -void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) -{ - ASSERT((a_PayloadLength == 0) || (a_Payload != NULL)); // Either zero data to send, or a valid payload ptr - - char Buffer[4]; - int Length = a_PayloadLength + 10; - IntToBuffer(Length, Buffer); - m_Outgoing.append(Buffer, 4); - IntToBuffer(a_RequestID, Buffer); - m_Outgoing.append(Buffer, 4); - IntToBuffer(a_PacketType, Buffer); - m_Outgoing.append(Buffer, 4); - if (a_PayloadLength > 0) - { - m_Outgoing.append(a_Payload, a_PayloadLength); - } - m_Outgoing.push_back(0); - m_Outgoing.push_back(0); - m_RCONServer.m_SocketThreads.NotifyWrite(this); -} - - - - diff --git a/source/RCONServer.h b/source/RCONServer.h deleted file mode 100644 index 0e89800a2..000000000 --- a/source/RCONServer.h +++ /dev/null @@ -1,109 +0,0 @@ - -// RCONServer.h - -// Declares the cRCONServer class representing the RCON server - - - - - -#pragma once - -#include "OSSupport/SocketThreads.h" -#include "OSSupport/ListenThread.h" - - - - - -// fwd: -class cServer; -class cIniFile; - - - - - -class cRCONServer : - public cListenThread::cCallback -{ -public: - cRCONServer(cServer & a_Server); - ~cRCONServer(); - - void Initialize(cIniFile & a_IniFile); - -protected: - friend class cRCONCommandOutput; - - class cConnection : - public cSocketThreads::cCallback - { - public: - cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket); - - protected: - friend class cRCONCommandOutput; - - /// Set to true if the client has successfully authenticated - bool m_IsAuthenticated; - - /// Buffer for the incoming data - AString m_Buffer; - - /// Buffer for the outgoing data - AString m_Outgoing; - - /// Server that owns this connection and processes requests - cRCONServer & m_RCONServer; - - /// The socket belonging to the client - cSocket & m_Socket; - - /// Address of the client - AString m_IPAddress; - - - // cSocketThreads::cCallback overrides: - virtual void DataReceived(const char * a_Data, int a_Size) override; - virtual void GetOutgoingData(AString & a_Data) override; - virtual void SocketClosed(void) override; - - /// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped - bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); - - /// Reads 4 bytes from a_Buffer and returns the int they represent - int IntFromBuffer(const char * a_Buffer); - - /// Puts 4 bytes representing the int into the buffer - void IntToBuffer(int a_Value, char * a_Buffer); - - /// Sends a RCON packet back to the client - void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); - } ; - - - /// The server object that will process the commands received - cServer & m_Server; - - /// The thread(s) that take care of all the traffic on the RCON ports - cSocketThreads m_SocketThreads; - - /// The thread for accepting IPv4 RCON connections - cListenThread m_ListenThread4; - - /// The thread for accepting IPv6 RCON connections - cListenThread m_ListenThread6; - - /// Password for authentication - AString m_Password; - - - // cListenThread::cCallback overrides: - virtual void OnConnectionAccepted(cSocket & a_Socket) override; -} ; - - - - - diff --git a/source/ReferenceManager.cpp b/source/ReferenceManager.cpp deleted file mode 100644 index 6a9ed0e43..000000000 --- a/source/ReferenceManager.cpp +++ /dev/null @@ -1,43 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "ReferenceManager.h" -#include "Entities/Entity.h" - - - - - -cReferenceManager::cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ) - : m_Type( a_Type ) -{ -} - -cReferenceManager::~cReferenceManager() -{ - if( m_Type == RFMNGR_REFERENCERS ) - { - for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) - { - *(*itr) = 0; // Set referenced pointer to 0 - } - } - else - { - for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) - { - cEntity* Ptr = (*(*itr)); - if( Ptr ) Ptr->Dereference( *(*itr) ); - } - } -} - -void cReferenceManager::AddReference( cEntity*& a_EntityPtr ) -{ - m_References.push_back( &a_EntityPtr ); -} - -void cReferenceManager::Dereference( cEntity*& a_EntityPtr ) -{ - m_References.remove( &a_EntityPtr ); -} \ No newline at end of file diff --git a/source/ReferenceManager.h b/source/ReferenceManager.h deleted file mode 100644 index bcd451f72..000000000 --- a/source/ReferenceManager.h +++ /dev/null @@ -1,34 +0,0 @@ - -#pragma once - - - - - -class cEntity; - - - - - -class cReferenceManager -{ -public: - enum ENUM_REFERENCE_MANAGER_TYPE - { - RFMNGR_REFERENCERS, - RFMNGR_REFERENCES, - }; - cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ); - ~cReferenceManager(); - - void AddReference( cEntity*& a_EntityPtr ); - void Dereference( cEntity*& a_EntityPtr ); -private: - ENUM_REFERENCE_MANAGER_TYPE m_Type; - std::list< cEntity** > m_References; -}; - - - - diff --git a/source/Root.cpp b/source/Root.cpp deleted file mode 100644 index be5a0553c..000000000 --- a/source/Root.cpp +++ /dev/null @@ -1,744 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Root.h" -#include "Server.h" -#include "World.h" -#include "WebAdmin.h" -#include "FurnaceRecipe.h" -#include "GroupManager.h" -#include "CraftingRecipes.h" -#include "PluginManager.h" -#include "MonsterConfig.h" -#include "Entities/Player.h" -#include "Blocks/BlockHandler.h" -#include "Items/ItemHandler.h" -#include "Chunk.h" -#include "Protocol/ProtocolRecognizer.h" // for protocol version constants -#include "CommandOutput.h" -#include "DeadlockDetect.h" -#include "OSSupport/Timer.h" - -#include "../iniFile/iniFile.h" - -#ifdef _WIN32 - #include -#elif defined(__linux__) - #include -#elif defined(__APPLE__) - #include -#endif - - - - - -cRoot* cRoot::s_Root = NULL; - - - - - -cRoot::cRoot() - : m_Server( NULL ) - , m_MonsterConfig( NULL ) - , m_GroupManager( NULL ) - , m_CraftingRecipes(NULL) - , m_FurnaceRecipe( NULL ) - , m_WebAdmin( NULL ) - , m_PluginManager( NULL ) - , m_Log( NULL ) - , m_bStop( false ) - , m_bRestart( false ) - , m_InputThread( NULL ) - , m_pDefaultWorld( NULL ) -{ - s_Root = this; -} - - - - - -cRoot::~cRoot() -{ - s_Root = 0; -} - - - - - -void cRoot::InputThread(void * a_Params) -{ - cRoot & self = *(cRoot*)a_Params; - - cLogCommandOutputCallback Output; - - while (!(self.m_bStop || self.m_bRestart) && std::cin.good()) - { - std::string Command; - std::getline(std::cin, Command); - if (!Command.empty()) - { - self.ExecuteConsoleCommand(Command, Output); - } - } - - if (!(self.m_bStop || self.m_bRestart)) - { - // We have come here because the std::cin has received an EOF and the server is still running; stop the server: - self.m_bStop = true; - } -} - - - - - -void cRoot::Start(void) -{ - cDeadlockDetect dd; - delete m_Log; - m_Log = new cMCLogger(); - - m_bStop = false; - while (!m_bStop) - { - cTimer Time; - long long mseconds = Time.GetNowTime(); - - m_bRestart = false; - - LoadGlobalSettings(); - - LOG("Creating new server instance..."); - m_Server = new cServer(); - - LOG("Reading server config..."); - cIniFile IniFile; - if (!IniFile.ReadFile("settings.ini")) - { - LOGWARN("Regenerating settings.ini, all settings will be reset"); - IniFile.AddHeaderComment(" This is the main server configuration"); - IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); - IniFile.AddHeaderComment(" See: http://www.mc-server.org/wiki/doku.php?id=configure:settings.ini for further configuration help"); - } - - m_PrimaryServerVersion = IniFile.GetValueI("Server", "PrimaryServerVersion", 0); - if (m_PrimaryServerVersion == 0) - { - m_PrimaryServerVersion = cProtocolRecognizer::PROTO_VERSION_LATEST; - } - else - { - // Make a note in the log that the primary server version is explicitly set in the ini file - LOGINFO("Primary server version set explicitly to %d.", m_PrimaryServerVersion); - } - - LOG("Starting server..."); - if (!m_Server->InitServer(IniFile)) - { - LOGERROR("Failure starting server, aborting..."); - return; - } - - m_WebAdmin = new cWebAdmin(); - m_WebAdmin->Init(); - - LOGD("Loading settings..."); - m_GroupManager = new cGroupManager(); - m_CraftingRecipes = new cCraftingRecipes; - m_FurnaceRecipe = new cFurnaceRecipe(); - - LOGD("Loading worlds..."); - LoadWorlds(IniFile); - - LOGD("Loading plugin manager..."); - m_PluginManager = new cPluginManager(); - m_PluginManager->ReloadPluginsNow(IniFile); - - LOGD("Loading MonsterConfig..."); - m_MonsterConfig = new cMonsterConfig; - - // This sets stuff in motion - LOGD("Starting Authenticator..."); - m_Authenticator.Start(IniFile); - - IniFile.WriteFile("settings.ini"); - - LOGD("Starting worlds..."); - StartWorlds(); - - LOGD("Starting deadlock detector..."); - dd.Start(); - - LOGD("Finalising startup..."); - m_Server->Start(); - - m_WebAdmin->Start(); - - #if !defined(ANDROID_NDK) - LOGD("Starting InputThread..."); - m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" ); - m_InputThread->Start( false ); // We should NOT wait? Otherwise we can´t stop the server from other threads than the input thread - #endif - - long long finishmseconds = Time.GetNowTime(); - finishmseconds -= mseconds; - - LOG("Startup complete, took %i ms!", finishmseconds); - - while (!m_bStop && !m_bRestart) // These are modified by external threads - { - cSleep::MilliSleep(1000); - } - - #if !defined(ANDROID_NDK) - delete m_InputThread; m_InputThread = NULL; - #endif - - // Deallocate stuffs - LOG("Shutting down server..."); - m_Server->Shutdown(); - - LOGD("Shutting down deadlock detector..."); - dd.Stop(); - - LOGD("Stopping world threads..."); - StopWorlds(); - - LOGD("Stopping authenticator..."); - m_Authenticator.Stop(); - - LOGD("Freeing MonsterConfig..."); - delete m_MonsterConfig; m_MonsterConfig = NULL; - delete m_WebAdmin; m_WebAdmin = NULL; - LOGD("Unloading recipes..."); - delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; - delete m_CraftingRecipes; m_CraftingRecipes = NULL; - LOGD("Forgetting groups..."); - delete m_GroupManager; m_GroupManager = 0; - LOGD("Unloading worlds..."); - UnloadWorlds(); - - LOGD("Stopping plugin manager..."); - delete m_PluginManager; m_PluginManager = NULL; - - cItemHandler::Deinit(); - cBlockHandler::Deinit(); - - LOG("Cleaning up..."); - //delete HeartBeat; HeartBeat = 0; - delete m_Server; m_Server = 0; - LOG("Shutdown successful!"); - } - - delete m_Log; m_Log = 0; -} - - - - - -void cRoot::LoadGlobalSettings() -{ - // Nothing needed yet -} - - - - - -void cRoot::LoadWorlds(cIniFile & IniFile) -{ - // First get the default world - AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world"); - m_pDefaultWorld = new cWorld( DefaultWorldName.c_str() ); - m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld; - - // Then load the other worlds - unsigned int KeyNum = IniFile.FindKey("Worlds"); - unsigned int NumWorlds = IniFile.GetNumValues( KeyNum ); - if (NumWorlds <= 0) - { - return; - } - - bool FoundAdditionalWorlds = false; - for (unsigned int i = 0; i < NumWorlds; i++) - { - AString ValueName = IniFile.GetValueName(KeyNum, i ); - if (ValueName.compare("World") != 0) - { - continue; - } - AString WorldName = IniFile.GetValue(KeyNum, i ); - if (WorldName.empty()) - { - continue; - } - FoundAdditionalWorlds = true; - cWorld* NewWorld = new cWorld( WorldName.c_str() ); - m_WorldsByName[ WorldName ] = NewWorld; - } // for i - Worlds - - if (!FoundAdditionalWorlds) - { - if (IniFile.GetKeyComment("Worlds", 0) != " World=secondworld") - { - IniFile.AddKeyComment("Worlds", " World=secondworld"); - } - } -} - - - - - -void cRoot::StartWorlds(void) -{ - for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) - { - itr->second->Start(); - itr->second->InitializeSpawn(); - } -} - - - - - -void cRoot::StopWorlds(void) -{ - for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) - { - itr->second->Stop(); - } -} - - - - - -void cRoot::UnloadWorlds(void) -{ - m_pDefaultWorld = NULL; - for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) - { - delete itr->second; - } - m_WorldsByName.clear(); -} - - - - - -cWorld* cRoot::GetDefaultWorld() -{ - return m_pDefaultWorld; -} - - - - - -cWorld* cRoot::GetWorld( const AString & a_WorldName ) -{ - WorldMap::iterator itr = m_WorldsByName.find( a_WorldName ); - if( itr != m_WorldsByName.end() ) - return itr->second; - return 0; -} - - - - - -bool cRoot::ForEachWorld(cWorldListCallback & a_Callback) -{ - for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) - { - ++itr2; - if (a_Callback.Item(itr->second)) - { - return false; - } - } - return true; -} - - - - - -void cRoot::TickCommands(void) -{ - // Execute any pending commands: - cCommandQueue PendingCommands; - { - cCSLock Lock(m_CSPendingCommands); - std::swap(PendingCommands, m_PendingCommands); - } - for (cCommandQueue::iterator itr = PendingCommands.begin(), end = PendingCommands.end(); itr != end; ++itr) - { - ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output)); - } -} - - - - - -void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) -{ - // Some commands are built-in: - if (a_Cmd == "stop") - { - m_bStop = true; - } - else if (a_Cmd == "restart") - { - m_bRestart = true; - } - - // Put the command into a queue (Alleviates FS #363): - cCSLock Lock(m_CSPendingCommands); - m_PendingCommands.push_back(cCommand(a_Cmd, &a_Output)); -} - - - - - -void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd) -{ - // Some commands are built-in: - if (a_Cmd == "stop") - { - m_bStop = true; - } - else if (a_Cmd == "restart") - { - m_bRestart = true; - } - - // Put the command into a queue (Alleviates FS #363): - cCSLock Lock(m_CSPendingCommands); - m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback)); -} - - - - - -void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) -{ - // Some commands are built-in: - if (a_Cmd == "stop") - { - m_bStop = true; - } - else if (a_Cmd == "restart") - { - m_bRestart = true; - } - - LOG("Executing console command: \"%s\"", a_Cmd.c_str()); - m_Server->ExecuteConsoleCommand(a_Cmd, a_Output); -} - - - - - -void cRoot::KickUser(int a_ClientID, const AString & a_Reason) -{ - m_Server->KickUser(a_ClientID, a_Reason); -} - - - - - -void cRoot::AuthenticateUser(int a_ClientID) -{ - m_Server->AuthenticateUser(a_ClientID); -} - - - - - -int cRoot::GetTotalChunkCount(void) -{ - int res = 0; - for ( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) - { - res += itr->second->GetNumChunks(); - } - return res; -} - - - - - -void cRoot::SaveAllChunks(void) -{ - for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) - { - itr->second->QueueSaveAllChunks(); - } -} - - - - - -void cRoot::BroadcastChat(const AString & a_Message) -{ - for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) - { - itr->second->BroadcastChat(a_Message); - } // for itr - m_WorldsByName[] -} - - - - - -bool cRoot::ForEachPlayer(cPlayerListCallback & a_Callback) -{ - for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) - { - ++itr2; - if (!itr->second->ForEachPlayer(a_Callback)) - { - return false; - } - } - return true; -} - - - - - -bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) -{ - class cCallback : public cPlayerListCallback - { - unsigned int BestRating; - unsigned int NameLength; - const AString PlayerName; - - cPlayerListCallback & m_Callback; - virtual bool Item (cPlayer * a_pPlayer) - { - unsigned int Rating = RateCompareString (PlayerName, a_pPlayer->GetName()); - if (Rating > 0 && Rating >= BestRating) - { - BestMatch = a_pPlayer; - if( Rating > BestRating ) NumMatches = 0; - BestRating = Rating; - ++NumMatches; - } - if (Rating == NameLength) // Perfect match - { - return true; - } - return false; - } - - public: - cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback) - : m_Callback( a_Callback ) - , BestMatch( NULL ) - , BestRating( 0 ) - , NumMatches( 0 ) - , NameLength( a_PlayerName.length() ) - , PlayerName( a_PlayerName ) - {} - - cPlayer * BestMatch; - unsigned int NumMatches; - } Callback (a_PlayerName, a_Callback); - ForEachPlayer( Callback ); - - if (Callback.NumMatches == 1) - { - return a_Callback.Item (Callback.BestMatch); - } - return false; -} - - - - - -AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion) -{ - return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion); -} - - - - - -int cRoot::GetVirtualRAMUsage(void) -{ - #ifdef _WIN32 - PROCESS_MEMORY_COUNTERS_EX pmc; - if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS *)&pmc, sizeof(pmc))) - { - return (int)(pmc.PrivateUsage / 1024); - } - return -1; - #elif defined(__linux__) - // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process - std::ifstream StatFile("/proc/self/status"); - if (!StatFile.good()) - { - return -1; - } - while (StatFile.good()) - { - AString Line; - std::getline(StatFile, Line); - if (strncmp(Line.c_str(), "VmSize:", 7) == 0) - { - int res = atoi(Line.c_str() + 8); - return (res == 0) ? -1 : res; // If parsing failed, return -1 - } - } - return -1; - #elif defined (__APPLE__) - // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process - struct task_basic_info t_info; - mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; - - if (KERN_SUCCESS == task_info( - mach_task_self(), - TASK_BASIC_INFO, - (task_info_t)&t_info, - &t_info_count - )) - { - return (int)(t_info.virtual_size / 1024); - } - return -1; - #else - LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__); - return -1; - #endif -} - - - - - -int cRoot::GetPhysicalRAMUsage(void) -{ - #ifdef _WIN32 - PROCESS_MEMORY_COUNTERS pmc; - if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) - { - return (int)(pmc.WorkingSetSize / 1024); - } - return -1; - #elif defined(__linux__) - // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process - std::ifstream StatFile("/proc/self/status"); - if (!StatFile.good()) - { - return -1; - } - while (StatFile.good()) - { - AString Line; - std::getline(StatFile, Line); - if (strncmp(Line.c_str(), "VmRSS:", 7) == 0) - { - int res = atoi(Line.c_str() + 8); - return (res == 0) ? -1 : res; // If parsing failed, return -1 - } - } - return -1; - #elif defined (__APPLE__) - // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process - struct task_basic_info t_info; - mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; - - if (KERN_SUCCESS == task_info( - mach_task_self(), - TASK_BASIC_INFO, - (task_info_t)&t_info, - &t_info_count - )) - { - return (int)(t_info.resident_size / 1024); - } - return -1; - #else - LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__); - return -1; - #endif -} - - - - - -void cRoot::LogChunkStats(cCommandOutputCallback & a_Output) -{ - int SumNumValid = 0; - int SumNumDirty = 0; - int SumNumInLighting = 0; - int SumNumInGenerator = 0; - int SumMem = 0; - for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) - { - cWorld * World = itr->second; - int NumInGenerator = World->GetGeneratorQueueLength(); - int NumInSaveQueue = World->GetStorageSaveQueueLength(); - int NumInLoadQueue = World->GetStorageLoadQueueLength(); - int NumValid = 0; - int NumDirty = 0; - int NumInLighting = 0; - World->GetChunkStats(NumValid, NumDirty, NumInLighting); - a_Output.Out("World %s:", World->GetName().c_str()); - a_Output.Out(" Num loaded chunks: %d", NumValid); - a_Output.Out(" Num dirty chunks: %d", NumDirty); - a_Output.Out(" Num chunks in lighting queue: %d", NumInLighting); - a_Output.Out(" Num chunks in generator queue: %d", NumInGenerator); - a_Output.Out(" Num chunks in storage load queue: %d", NumInLoadQueue); - a_Output.Out(" Num chunks in storage save queue: %d", NumInSaveQueue); - int Mem = NumValid * sizeof(cChunk); - a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024)); - a_Output.Out(" Per-chunk memory size breakdown:"); - a_Output.Out(" block types: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024); - a_Output.Out(" block metadata: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockNibbles), (sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); - a_Output.Out(" block lighting: %6d bytes (%3d KiB)", 2 * sizeof(cChunkDef::BlockNibbles), (2 * sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); - a_Output.Out(" heightmap: %6d bytes (%3d KiB)", sizeof(cChunkDef::HeightMap), (sizeof(cChunkDef::HeightMap) + 1023) / 1024); - a_Output.Out(" biomemap: %6d bytes (%3d KiB)", sizeof(cChunkDef::BiomeMap), (sizeof(cChunkDef::BiomeMap) + 1023) / 1024); - int Rest = sizeof(cChunk) - sizeof(cChunkDef::BlockTypes) - 3 * sizeof(cChunkDef::BlockNibbles) - sizeof(cChunkDef::HeightMap) - sizeof(cChunkDef::BiomeMap); - a_Output.Out(" other: %6d bytes (%3d KiB)", Rest, (Rest + 1023) / 1024); - SumNumValid += NumValid; - SumNumDirty += NumDirty; - SumNumInLighting += NumInLighting; - SumNumInGenerator += NumInGenerator; - SumMem += Mem; - } - a_Output.Out("Totals:"); - a_Output.Out(" Num loaded chunks: %d", SumNumValid); - a_Output.Out(" Num dirty chunks: %d", SumNumDirty); - a_Output.Out(" Num chunks in lighting queue: %d", SumNumInLighting); - a_Output.Out(" Num chunks in generator queue: %d", SumNumInGenerator); - a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024)); -} - - - - diff --git a/source/Root.h b/source/Root.h deleted file mode 100644 index 175084c53..000000000 --- a/source/Root.h +++ /dev/null @@ -1,186 +0,0 @@ - -#pragma once - -#include "Authenticator.h" -#include "HTTPServer/HTTPServer.h" - - - - - -// fwd: -class cThread; -class cMonsterConfig; -class cGroupManager; -class cCraftingRecipes; -class cFurnaceRecipe; -class cWebAdmin; -class cPluginManager; -class cServer; -class cWorld; -class cPlayer; -class cCommandOutputCallback ; - -typedef cItemCallback cPlayerListCallback; -typedef cItemCallback cWorldListCallback; - - - - - -/// The root of the object hierarchy -class cRoot // tolua_export -{ // tolua_export -public: - static cRoot * Get() { return s_Root; } // tolua_export - - cRoot(void); - ~cRoot(); - - void Start(void); - - cServer * GetServer(void) { return m_Server; } // tolua_export - cWorld * GetDefaultWorld(void); // tolua_export - cWorld * GetWorld(const AString & a_WorldName); // tolua_export - - /// Calls the callback for each world; returns true if the callback didn't abort (return true) - bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings << - - /// Writes chunkstats, for each world and totals, to the output callback - void LogChunkStats(cCommandOutputCallback & a_Output); - - int GetPrimaryServerVersion(void) const { return m_PrimaryServerVersion; } // tolua_export - void SetPrimaryServerVersion(int a_Version) { m_PrimaryServerVersion = a_Version; } // tolua_export - - cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; } - - cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export - cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export - cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // tolua_export - cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export - cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export - cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } - - /** Queues a console command for execution through the cServer class. - The command will be executed in the tick thread - The command's output will be written to the a_Output callback - "stop" and "restart" commands have special handling. - */ - void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); - - /** Queues a console command for execution through the cServer class. - The command will be executed in the tick thread - The command's output will be sent to console - "stop" and "restart" commands have special handling. - */ - void QueueExecuteConsoleCommand(const AString & a_Cmd); // tolua_export - - /// Executes a console command through the cServer class; does special handling for "stop" and "restart". - void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); - - /// Kicks the user, no matter in what world they are. Used from cAuthenticator - void KickUser(int a_ClientID, const AString & a_Reason); - - /// Called by cAuthenticator to auth the specified user - void AuthenticateUser(int a_ClientID); - - /// Executes commands queued in the command queue - void TickCommands(void); - - /// Returns the number of chunks loaded - int GetTotalChunkCount(void); // tolua_export - - /// Saves all chunks in all worlds - void SaveAllChunks(void); // tolua_export - - /// Sends a chat message to all connected clients (in all worlds) - void BroadcastChat(const AString & a_Message); // tolua_export - - /// Calls the callback for each player in all worlds - bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - - /// Finds a player from a partial or complete player name and calls the callback - case-insensitive - bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - - // tolua_begin - - /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API - static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); - - /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error - static int GetVirtualRAMUsage(void); - - /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error - static int GetPhysicalRAMUsage(void); - - // tolua_end - -private: - class cCommand - { - public: - cCommand(const AString & a_Command, cCommandOutputCallback * a_Output) : - m_Command(a_Command), - m_Output(a_Output) - { - } - - AString m_Command; - cCommandOutputCallback * m_Output; - } ; - - typedef std::map WorldMap; - typedef std::vector cCommandQueue; - - /// The version of the protocol that is primary for the server (reported in the server list). All versions are still supported. - int m_PrimaryServerVersion; - - cWorld * m_pDefaultWorld; - WorldMap m_WorldsByName; - - cCriticalSection m_CSPendingCommands; - cCommandQueue m_PendingCommands; - - cThread * m_InputThread; - - cServer * m_Server; - cMonsterConfig * m_MonsterConfig; - - cGroupManager * m_GroupManager; - cCraftingRecipes * m_CraftingRecipes; - cFurnaceRecipe * m_FurnaceRecipe; - cWebAdmin * m_WebAdmin; - cPluginManager * m_PluginManager; - cAuthenticator m_Authenticator; - cHTTPServer m_HTTPServer; - - cMCLogger * m_Log; - - bool m_bStop; - bool m_bRestart; - - void LoadGlobalSettings(); - - /// Loads the worlds from settings.ini, creates the worldmap - void LoadWorlds(cIniFile & IniFile); - - /// Starts each world's life - void StartWorlds(void); - - /// Stops each world's threads, so that it's safe to unload them - void StopWorlds(void); - - /// Unloads all worlds from memory - void UnloadWorlds(void); - - /// Does the actual work of executing a command - void DoExecuteConsoleCommand(const AString & a_Cmd); - - static void InputThread(void* a_Params); - - static cRoot* s_Root; -}; // tolua_export - - - - diff --git a/source/SQLite/lsqlite3.c b/source/SQLite/lsqlite3.c deleted file mode 100644 index 4c81b5878..000000000 --- a/source/SQLite/lsqlite3.c +++ /dev/null @@ -1,2175 +0,0 @@ -/************************************************************************ -* lsqlite3 * -* Copyright (C) 2002-2013 Tiago Dionizio, Doug Currie * -* All rights reserved. * -* Author : Tiago Dionizio * -* Author : Doug Currie * -* Library : lsqlite3 - a SQLite 3 database binding for Lua 5 * -* * -* 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. * -************************************************************************/ -// Slightly modified by _Xoft to compile in MSVC - - - - -// 2013_04_07 _X: Added the following #define-s so that MSVC doesn't complain about non-secure stuff: -#define _CRT_SECURE_NO_WARNINGS -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - - - - -#include -#include -#include - -#define LUA_LIB -#include "lua.h" -#include "lauxlib.h" - -#if LUA_VERSION_NUM > 501 -// -// Lua 5.2 -// -#define lua_strlen lua_rawlen -// luaL_typerror always used with arg at ndx == NULL -#define luaL_typerror(L,ndx,str) luaL_error(L,"bad argument %d (%s expected, got nil)",ndx,str) -// luaL_register used once, so below expansion is OK for this case -#define luaL_register(L,name,reg) lua_newtable(L);luaL_setfuncs(L,reg,0) -// luaL_openlib always used with name == NULL -#define luaL_openlib(L,name,reg,nup) luaL_setfuncs(L,reg,nup) -#endif - -#include "sqlite3.h" - -/* compile time features */ -#if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) - #define SQLITE_OMIT_PROGRESS_CALLBACK 0 -#endif -#if !defined(LSQLITE_OMIT_UPDATE_HOOK) - #define LSQLITE_OMIT_UPDATE_HOOK 0 -#endif - -typedef struct sdb sdb; -typedef struct sdb_vm sdb_vm; -typedef struct sdb_func sdb_func; - -/* to use as C user data so i know what function sqlite is calling */ -struct sdb_func { - /* references to associated lua values */ - int fn_step; - int fn_finalize; - int udata; - - sdb *db; - char aggregate; - - sdb_func *next; -}; - -/* information about database */ -struct sdb { - /* associated lua state */ - lua_State *L; - /* sqlite database handle */ - sqlite3 *db; - - /* sql functions stack usage */ - sdb_func *func; /* top SQL function being called */ - - /* references */ - int busy_cb; /* busy callback */ - int busy_udata; - - int progress_cb; /* progress handler */ - int progress_udata; - - int trace_cb; /* trace callback */ - int trace_udata; - -#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK - - int update_hook_cb; /* update_hook callback */ - int update_hook_udata; - - int commit_hook_cb; /* commit_hook callback */ - int commit_hook_udata; - - int rollback_hook_cb; /* rollback_hook callback */ - int rollback_hook_udata; - -#endif -}; - -static const char *sqlite_meta = ":sqlite3"; -static const char *sqlite_vm_meta = ":sqlite3:vm"; -static const char *sqlite_ctx_meta = ":sqlite3:ctx"; -static int sqlite_ctx_meta_ref; - -/* -** ======================================================= -** Database Virtual Machine Operations -** ======================================================= -*/ - -static void vm_push_column(lua_State *L, sqlite3_stmt *vm, int idx) { - switch (sqlite3_column_type(vm, idx)) { - case SQLITE_INTEGER: - { - sqlite_int64 i64 = sqlite3_column_int64(vm, idx); - lua_Number n = (lua_Number)i64; - if (n == i64) - lua_pushnumber(L, n); - else - lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx), sqlite3_column_bytes(vm, idx)); - } - break; - case SQLITE_FLOAT: - lua_pushnumber(L, sqlite3_column_double(vm, idx)); - break; - case SQLITE_TEXT: - lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx), sqlite3_column_bytes(vm, idx)); - break; - case SQLITE_BLOB: - lua_pushlstring(L, sqlite3_column_blob(vm, idx), sqlite3_column_bytes(vm, idx)); - break; - case SQLITE_NULL: - lua_pushnil(L); - break; - default: - lua_pushnil(L); - break; - } -} - -/* virtual machine information */ -struct sdb_vm { - sdb *db; /* associated database handle */ - sqlite3_stmt *vm; /* virtual machine */ - - /* sqlite3_step info */ - int columns; /* number of columns in result */ - char has_values; /* true when step succeeds */ - - char temp; /* temporary vm used in db:rows */ -}; - -/* called with sql text on the lua stack */ -static sdb_vm *newvm(lua_State *L, sdb *db) { - sdb_vm *svm = (sdb_vm*)lua_newuserdata(L, sizeof(sdb_vm)); - - luaL_getmetatable(L, sqlite_vm_meta); - lua_setmetatable(L, -2); /* set metatable */ - - svm->db = db; - svm->columns = 0; - svm->has_values = 0; - svm->vm = NULL; - svm->temp = 0; - - /* add an entry on the database table: svm -> sql text */ - lua_pushlightuserdata(L, db); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(L, svm); - lua_pushvalue(L, -4); /* the sql text */ - lua_rawset(L, -3); - lua_pop(L, 1); - - return svm; -} - -static int cleanupvm(lua_State *L, sdb_vm *svm) { - /* remove entry in database table - no harm if not present in the table */ - lua_pushlightuserdata(L, svm->db); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(L, svm); - lua_pushnil(L); - lua_rawset(L, -3); - lua_pop(L, 1); - - svm->columns = 0; - svm->has_values = 0; - - if (!svm->vm) return 0; - - lua_pushnumber(L, sqlite3_finalize(svm->vm)); - svm->vm = NULL; - return 1; -} - -static int stepvm(lua_State *L, sdb_vm *svm) { - int result; - int loop_limit = 3; - while ( loop_limit-- ) { - result = sqlite3_step(svm->vm); - if ( result==SQLITE_ERROR ) { - result = sqlite3_reset (svm->vm); - } - if ( result==SQLITE_SCHEMA ) { - sqlite3_stmt *vn; - const char *sql; - /* recover sql text */ - lua_pushlightuserdata(L, svm->db); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(L, svm); - lua_rawget(L, -2); /* sql text */ - sql = luaL_checkstring(L, -1); - /* re-prepare */ - result = sqlite3_prepare(svm->db->db, sql, -1, &vn, NULL); - if (result != SQLITE_OK) break; - sqlite3_transfer_bindings(svm->vm, vn); - sqlite3_finalize(svm->vm); - svm->vm = vn; - lua_pop(L,2); - } else { - break; - } - } - return result; -} - -static sdb_vm *lsqlite_getvm(lua_State *L, int index) { - sdb_vm *svm = (sdb_vm*)luaL_checkudata(L, index, sqlite_vm_meta); - if (svm == NULL) luaL_argerror(L, index, "bad sqlite virtual machine"); - return svm; -} - -static sdb_vm *lsqlite_checkvm(lua_State *L, int index) { - sdb_vm *svm = lsqlite_getvm(L, index); - if (svm->vm == NULL) luaL_argerror(L, index, "attempt to use closed sqlite virtual machine"); - return svm; -} - -static int dbvm_isopen(lua_State *L) { - sdb_vm *svm = lsqlite_getvm(L, 1); - lua_pushboolean(L, svm->vm != NULL ? 1 : 0); - return 1; -} - -static int dbvm_tostring(lua_State *L) { - char buff[39]; - sdb_vm *svm = lsqlite_getvm(L, 1); - if (svm->vm == NULL) - strcpy(buff, "closed"); - else - sprintf(buff, "%p", svm); - lua_pushfstring(L, "sqlite virtual machine (%s)", buff); - return 1; -} - -static int dbvm_gc(lua_State *L) { - sdb_vm *svm = lsqlite_getvm(L, 1); - if (svm->vm != NULL) /* ignore closed vms */ - cleanupvm(L, svm); - return 0; -} - -static int dbvm_step(lua_State *L) { - int result; - sdb_vm *svm = lsqlite_checkvm(L, 1); - - result = stepvm(L, svm); - svm->has_values = result == SQLITE_ROW ? 1 : 0; - svm->columns = sqlite3_data_count(svm->vm); - - lua_pushnumber(L, result); - return 1; -} - -static int dbvm_finalize(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - return cleanupvm(L, svm); -} - -static int dbvm_reset(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_reset(svm->vm); - lua_pushnumber(L, sqlite3_errcode(svm->db->db)); - return 1; -} - -static void dbvm_check_contents(lua_State *L, sdb_vm *svm) { - if (!svm->has_values) { - luaL_error(L, "misuse of function"); - } -} - -static void dbvm_check_index(lua_State *L, sdb_vm *svm, int index) { - if (index < 0 || index >= svm->columns) { - luaL_error(L, "index out of range [0..%d]", svm->columns - 1); - } -} - -static void dbvm_check_bind_index(lua_State *L, sdb_vm *svm, int index) { - if (index < 1 || index > sqlite3_bind_parameter_count(svm->vm)) { - luaL_error(L, "bind index out of range [1..%d]", sqlite3_bind_parameter_count(svm->vm)); - } -} - -/* -** ======================================================= -** Virtual Machine - generic info -** ======================================================= -*/ -static int dbvm_columns(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - lua_pushnumber(L, sqlite3_column_count(svm->vm)); - return 1; -} - -/* -** ======================================================= -** Virtual Machine - getters -** ======================================================= -*/ - -static int dbvm_get_value(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = luaL_checkint(L, 2); - dbvm_check_contents(L, svm); - dbvm_check_index(L, svm, index); - vm_push_column(L, svm->vm, index); - return 1; -} - -static int dbvm_get_name(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = (int)luaL_checknumber(L, 2); - dbvm_check_index(L, svm, index); - lua_pushstring(L, sqlite3_column_name(svm->vm, index)); - return 1; -} - -static int dbvm_get_type(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = (int)luaL_checknumber(L, 2); - dbvm_check_index(L, svm, index); - lua_pushstring(L, sqlite3_column_decltype(svm->vm, index)); - return 1; -} - -static int dbvm_get_values(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int columns = svm->columns; - int n; - dbvm_check_contents(L, svm); - - lua_newtable(L); - for (n = 0; n < columns;) { - vm_push_column(L, vm, n++); - lua_rawseti(L, -2, n); - } - return 1; -} - -static int dbvm_get_names(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ - int n; - - lua_newtable(L); - for (n = 0; n < columns;) { - lua_pushstring(L, sqlite3_column_name(vm, n++)); - lua_rawseti(L, -2, n); - } - return 1; -} - -static int dbvm_get_types(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ - int n; - - lua_newtable(L); - for (n = 0; n < columns;) { - lua_pushstring(L, sqlite3_column_decltype(vm, n++)); - lua_rawseti(L, -2, n); - } - return 1; -} - -static int dbvm_get_uvalues(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int columns = svm->columns; - int n; - dbvm_check_contents(L, svm); - - lua_checkstack(L, columns); - for (n = 0; n < columns; ++n) - vm_push_column(L, vm, n); - return columns; -} - -static int dbvm_get_unames(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ - int n; - - lua_checkstack(L, columns); - for (n = 0; n < columns; ++n) - lua_pushstring(L, sqlite3_column_name(vm, n)); - return columns; -} - -static int dbvm_get_utypes(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ - int n; - - lua_checkstack(L, columns); - for (n = 0; n < columns; ++n) - lua_pushstring(L, sqlite3_column_decltype(vm, n)); - return columns; -} - -static int dbvm_get_named_values(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int columns = svm->columns; - int n; - dbvm_check_contents(L, svm); - - lua_newtable(L); - for (n = 0; n < columns; ++n) { - lua_pushstring(L, sqlite3_column_name(vm, n)); - vm_push_column(L, vm, n); - lua_rawset(L, -3); - } - return 1; -} - -static int dbvm_get_named_types(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); - int n; - - lua_newtable(L); - for (n = 0; n < columns; ++n) { - lua_pushstring(L, sqlite3_column_name(vm, n)); - lua_pushstring(L, sqlite3_column_decltype(vm, n)); - lua_rawset(L, -3); - } - return 1; -} - -/* -** ======================================================= -** Virtual Machine - Bind -** ======================================================= -*/ - -static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex) { - switch (lua_type(L, lindex)) { - case LUA_TSTRING: - return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), lua_strlen(L, lindex), SQLITE_TRANSIENT); - case LUA_TNUMBER: - return sqlite3_bind_double(vm, index, lua_tonumber(L, lindex)); - case LUA_TBOOLEAN: - return sqlite3_bind_int(vm, index, lua_toboolean(L, lindex) ? 1 : 0); - case LUA_TNONE: - case LUA_TNIL: - return sqlite3_bind_null(vm, index); - default: - luaL_error(L, "index (%d) - invalid data type for bind (%s)", index, lua_typename(L, lua_type(L, lindex))); - return SQLITE_MISUSE; /*!*/ - } -} - - -static int dbvm_bind_parameter_count(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - lua_pushnumber(L, sqlite3_bind_parameter_count(svm->vm)); - return 1; -} - -static int dbvm_bind_parameter_name(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = (int)luaL_checknumber(L, 2); - dbvm_check_bind_index(L, svm, index); - lua_pushstring(L, sqlite3_bind_parameter_name(svm->vm, index)); - return 1; -} - -static int dbvm_bind(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int index = luaL_checkint(L, 2); - int result; - - dbvm_check_bind_index(L, svm, index); - result = dbvm_bind_index(L, vm, index, 3); - - lua_pushnumber(L, result); - return 1; -} - -static int dbvm_bind_blob(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = luaL_checkint(L, 2); - const char *value = luaL_checkstring(L, 3); - int len = lua_strlen(L, 3); - - lua_pushnumber(L, sqlite3_bind_blob(svm->vm, index, value, len, SQLITE_TRANSIENT)); - return 1; -} - -static int dbvm_bind_values(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int top = lua_gettop(L); - int result, n; - - if (top - 1 != sqlite3_bind_parameter_count(vm)) - luaL_error(L, - "incorrect number of parameters to bind (%d given, %d to bind)", - top - 1, - sqlite3_bind_parameter_count(vm) - ); - - for (n = 2; n <= top; ++n) { - if ((result = dbvm_bind_index(L, vm, n - 1, n)) != SQLITE_OK) { - lua_pushnumber(L, result); - return 1; - } - } - - lua_pushnumber(L, SQLITE_OK); - return 1; -} - -static int dbvm_bind_names(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm = svm->vm; - int count = sqlite3_bind_parameter_count(vm); - const char *name; - int result, n; - luaL_checktype(L, 2, LUA_TTABLE); - - for (n = 1; n <= count; ++n) { - name = sqlite3_bind_parameter_name(vm, n); - if (name && (name[0] == ':' || name[0] == '$')) { - lua_pushstring(L, ++name); - lua_gettable(L, 2); - result = dbvm_bind_index(L, vm, n, -1); - lua_pop(L, 1); - } - else { - lua_pushnumber(L, n); - lua_gettable(L, 2); - result = dbvm_bind_index(L, vm, n, -1); - lua_pop(L, 1); - } - - if (result != SQLITE_OK) { - lua_pushnumber(L, result); - return 1; - } - } - - lua_pushnumber(L, SQLITE_OK); - return 1; -} - -/* -** ======================================================= -** Database (internal management) -** ======================================================= -*/ - -/* -** When creating database handles, always creates a `closed' database handle -** before opening the actual database; so, if there is a memory error, the -** database is not left opened. -** -** Creates a new 'table' and leaves it in the stack -*/ -static sdb *newdb (lua_State *L) { - sdb *db = (sdb*)lua_newuserdata(L, sizeof(sdb)); - db->L = L; - db->db = NULL; /* database handle is currently `closed' */ - db->func = NULL; - - db->busy_cb = - db->busy_udata = - db->progress_cb = - db->progress_udata = - db->trace_cb = - db->trace_udata = -#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK - db->update_hook_cb = - db->update_hook_udata = - db->commit_hook_cb = - db->commit_hook_udata = - db->rollback_hook_cb = - db->rollback_hook_udata = -#endif - LUA_NOREF; - - luaL_getmetatable(L, sqlite_meta); - lua_setmetatable(L, -2); /* set metatable */ - - /* to keep track of 'open' virtual machines */ - lua_pushlightuserdata(L, db); - lua_newtable(L); - lua_rawset(L, LUA_REGISTRYINDEX); - - return db; -} - -static int cleanupdb(lua_State *L, sdb *db) { - sdb_func *func; - sdb_func *func_next; - int top; - int result; - - /* free associated virtual machines */ - lua_pushlightuserdata(L, db); - lua_rawget(L, LUA_REGISTRYINDEX); - - /* close all used handles */ - top = lua_gettop(L); - lua_pushnil(L); - while (lua_next(L, -2)) { - sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */ - cleanupvm(L, svm); - - lua_settop(L, top); - lua_pushnil(L); - } - - lua_pop(L, 1); /* pop vm table */ - - /* remove entry in lua registry table */ - lua_pushlightuserdata(L, db); - lua_pushnil(L); - lua_rawset(L, LUA_REGISTRYINDEX); - - /* 'free' all references */ - luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); - luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata); - luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata); -#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK - luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata); - luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata); - luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); -#endif - - /* close database */ - result = sqlite3_close(db->db); - db->db = NULL; - - /* free associated memory with created functions */ - func = db->func; - while (func) { - func_next = func->next; - luaL_unref(L, LUA_REGISTRYINDEX, func->fn_step); - luaL_unref(L, LUA_REGISTRYINDEX, func->fn_finalize); - luaL_unref(L, LUA_REGISTRYINDEX, func->udata); - free(func); - func = func_next; - } - db->func = NULL; - return result; -} - -static sdb *lsqlite_getdb(lua_State *L, int index) { - sdb *db = (sdb*)luaL_checkudata(L, index, sqlite_meta); - if (db == NULL) luaL_typerror(L, index, "sqlite database"); - return db; -} - -static sdb *lsqlite_checkdb(lua_State *L, int index) { - sdb *db = lsqlite_getdb(L, index); - if (db->db == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database"); - return db; -} - - -/* -** ======================================================= -** User Defined Functions - Context Methods -** ======================================================= -*/ -typedef struct { - sqlite3_context *ctx; - int ud; -} lcontext; - -static lcontext *lsqlite_make_context(lua_State *L) { - lcontext *ctx = (lcontext*)lua_newuserdata(L, sizeof(lcontext)); - lua_rawgeti(L, LUA_REGISTRYINDEX, sqlite_ctx_meta_ref); - lua_setmetatable(L, -2); - ctx->ctx = NULL; - ctx->ud = LUA_NOREF; - return ctx; -} - -static lcontext *lsqlite_getcontext(lua_State *L, int index) { - lcontext *ctx = (lcontext*)luaL_checkudata(L, index, sqlite_ctx_meta); - if (ctx == NULL) luaL_typerror(L, index, "sqlite context"); - return ctx; -} - -static lcontext *lsqlite_checkcontext(lua_State *L, int index) { - lcontext *ctx = lsqlite_getcontext(L, index); - if (ctx->ctx == NULL) luaL_argerror(L, index, "invalid sqlite context"); - return ctx; -} - -static int lcontext_tostring(lua_State *L) { - char buff[39]; - lcontext *ctx = lsqlite_getcontext(L, 1); - if (ctx->ctx == NULL) - strcpy(buff, "closed"); - else - sprintf(buff, "%p", ctx->ctx); - lua_pushfstring(L, "sqlite function context (%s)", buff); - return 1; -} - -static void lcontext_check_aggregate(lua_State *L, lcontext *ctx) { - sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx); - if (!func->aggregate) { - luaL_error(L, "attempt to call aggregate method from scalar function"); - } -} - -static int lcontext_user_data(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx); - lua_rawgeti(L, LUA_REGISTRYINDEX, func->udata); - return 1; -} - -static int lcontext_get_aggregate_context(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - lcontext_check_aggregate(L, ctx); - lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ud); - return 1; -} - -static int lcontext_set_aggregate_context(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - lcontext_check_aggregate(L, ctx); - lua_settop(L, 2); - luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); - ctx->ud = luaL_ref(L, LUA_REGISTRYINDEX); - return 0; -} - -static int lcontext_aggregate_count(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - lcontext_check_aggregate(L, ctx); - lua_pushnumber(L, sqlite3_aggregate_count(ctx->ctx)); - return 1; -} - -#if 0 -void *sqlite3_get_auxdata(sqlite3_context*, int); -void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*)); -#endif - -static int lcontext_result(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - switch (lua_type(L, 2)) { - case LUA_TNUMBER: - sqlite3_result_double(ctx->ctx, luaL_checknumber(L, 2)); - break; - case LUA_TSTRING: - sqlite3_result_text(ctx->ctx, luaL_checkstring(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT); - break; - case LUA_TNIL: - case LUA_TNONE: - sqlite3_result_null(ctx->ctx); - break; - default: - luaL_error(L, "invalid result type %s", lua_typename(L, 2)); - break; - } - - return 0; -} - -static int lcontext_result_blob(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - const char *blob = luaL_checkstring(L, 2); - int size = lua_strlen(L, 2); - sqlite3_result_blob(ctx->ctx, (const void*)blob, size, SQLITE_TRANSIENT); - return 0; -} - -static int lcontext_result_double(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - double d = luaL_checknumber(L, 2); - sqlite3_result_double(ctx->ctx, d); - return 0; -} - -static int lcontext_result_error(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - const char *err = luaL_checkstring(L, 2); - int size = lua_strlen(L, 2); - sqlite3_result_error(ctx->ctx, err, size); - return 0; -} - -static int lcontext_result_int(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - int i = luaL_checkint(L, 2); - sqlite3_result_int(ctx->ctx, i); - return 0; -} - -static int lcontext_result_null(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - sqlite3_result_null(ctx->ctx); - return 0; -} - -static int lcontext_result_text(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - const char *text = luaL_checkstring(L, 2); - int size = lua_strlen(L, 2); - sqlite3_result_text(ctx->ctx, text, size, SQLITE_TRANSIENT); - return 0; -} - -/* -** ======================================================= -** Database Methods -** ======================================================= -*/ - -static int db_isopen(lua_State *L) { - sdb *db = lsqlite_getdb(L, 1); - lua_pushboolean(L, db->db != NULL ? 1 : 0); - return 1; -} - -static int db_last_insert_rowid(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - /* conversion warning: int64 -> luaNumber */ - sqlite_int64 rowid = sqlite3_last_insert_rowid(db->db); - lua_Number n = (lua_Number)rowid; - if (n == rowid) - lua_pushnumber(L, n); - else - lua_pushfstring(L, "%ll", rowid); - return 1; -} - -static int db_changes(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - lua_pushnumber(L, sqlite3_changes(db->db)); - return 1; -} - -static int db_total_changes(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - lua_pushnumber(L, sqlite3_total_changes(db->db)); - return 1; -} - -static int db_errcode(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - lua_pushnumber(L, sqlite3_errcode(db->db)); - return 1; -} - -static int db_errmsg(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - lua_pushstring(L, sqlite3_errmsg(db->db)); - return 1; -} - -static int db_interrupt(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - sqlite3_interrupt(db->db); - return 0; -} - -/* -** Registering SQL functions: -*/ - -static void db_push_value(lua_State *L, sqlite3_value *value) { - switch (sqlite3_value_type(value)) { - case SQLITE_TEXT: - lua_pushlstring(L, (const char*)sqlite3_value_text(value), sqlite3_value_bytes(value)); - break; - - case SQLITE_INTEGER: - { - sqlite_int64 i64 = sqlite3_value_int64(value); - lua_Number n = (lua_Number)i64; - if (n == i64) - lua_pushnumber(L, n); - else - lua_pushlstring(L, (const char*)sqlite3_value_text(value), sqlite3_value_bytes(value)); - } - break; - - case SQLITE_FLOAT: - lua_pushnumber(L, sqlite3_value_double(value)); - break; - - case SQLITE_BLOB: - lua_pushlstring(L, sqlite3_value_blob(value), sqlite3_value_bytes(value)); - break; - - case SQLITE_NULL: - lua_pushnil(L); - break; - - default: - /* things done properly (SQLite + Lua SQLite) - ** this should never happen */ - lua_pushnil(L); - break; - } -} - -/* -** callback functions used when calling registered sql functions -*/ - -/* scalar function to be called -** callback params: context, values... */ -static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_value **argv) { - sdb_func *func = (sdb_func*)sqlite3_user_data(context); - lua_State *L = func->db->L; - int n; - lcontext *ctx; - - int top = lua_gettop(L); - - /* ensure there is enough space in the stack */ - lua_checkstack(L, argc + 3); - - lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_step); /* function to call */ - - if (!func->aggregate) { - ctx = lsqlite_make_context(L); /* push context - used to set results */ - } - else { - /* reuse context userdata value */ - void *p = sqlite3_aggregate_context(context, 1); - /* i think it is OK to use assume that using a light user data - ** as an entry on LUA REGISTRY table will be unique */ - lua_pushlightuserdata(L, p); - lua_rawget(L, LUA_REGISTRYINDEX); /* context table */ - - if (lua_isnil(L, -1)) { /* not yet created? */ - lua_pop(L, 1); - ctx = lsqlite_make_context(L); - lua_pushlightuserdata(L, p); - lua_pushvalue(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); - } - else - ctx = lsqlite_getcontext(L, -1); - } - - /* push params */ - for (n = 0; n < argc; ++n) { - db_push_value(L, argv[n]); - } - - /* set context */ - ctx->ctx = context; - - if (lua_pcall(L, argc + 1, 0, 0)) { - const char *errmsg = lua_tostring(L, -1); - int size = lua_strlen(L, -1); - sqlite3_result_error(context, errmsg, size); - } - - /* invalidate context */ - ctx->ctx = NULL; - - if (!func->aggregate) { - luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); - } - - lua_settop(L, top); -} - -static void db_sql_finalize_function(sqlite3_context *context) { - sdb_func *func = (sdb_func*)sqlite3_user_data(context); - lua_State *L = func->db->L; - void *p = sqlite3_aggregate_context(context, 1); /* minimal mem usage */ - lcontext *ctx; - int top = lua_gettop(L); - - lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_finalize); /* function to call */ - - /* i think it is OK to use assume that using a light user data - ** as an entry on LUA REGISTRY table will be unique */ - lua_pushlightuserdata(L, p); - lua_rawget(L, LUA_REGISTRYINDEX); /* context table */ - - if (lua_isnil(L, -1)) { /* not yet created? - shouldn't happen in finalize function */ - lua_pop(L, 1); - ctx = lsqlite_make_context(L); - lua_pushlightuserdata(L, p); - lua_pushvalue(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); - } - else - ctx = lsqlite_getcontext(L, -1); - - /* set context */ - ctx->ctx = context; - - if (lua_pcall(L, 1, 0, 0)) { - sqlite3_result_error(context, lua_tostring(L, -1), -1); - } - - /* invalidate context */ - ctx->ctx = NULL; - - /* cleanup context */ - luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); - /* remove it from registry */ - lua_pushlightuserdata(L, p); - lua_pushnil(L); - lua_rawset(L, LUA_REGISTRYINDEX); - - lua_settop(L, top); -} - -/* -** Register a normal function -** Params: db, function name, number arguments, [ callback | step, finalize], user data -** Returns: true on sucess -** -** Normal function: -** Params: context, params -** -** Aggregate function: -** Params of step: context, params -** Params of finalize: context -*/ -static int db_register_function(lua_State *L, int aggregate) { - sdb *db = lsqlite_checkdb(L, 1); - const char *name; - int args; - int result; - sdb_func *func; - - /* safety measure */ - if (aggregate) aggregate = 1; - - name = luaL_checkstring(L, 2); - args = luaL_checkint(L, 3); - luaL_checktype(L, 4, LUA_TFUNCTION); - if (aggregate) luaL_checktype(L, 5, LUA_TFUNCTION); - - /* maybe an alternative way to allocate memory should be used/avoided */ - func = (sdb_func*)malloc(sizeof(sdb_func)); - if (func == NULL) { - luaL_error(L, "out of memory"); - } - - result = sqlite3_create_function( - db->db, name, args, SQLITE_UTF8, func, - aggregate ? NULL : db_sql_normal_function, - aggregate ? db_sql_normal_function : NULL, - aggregate ? db_sql_finalize_function : NULL - ); - - if (result == SQLITE_OK) { - /* safety measures for userdata field to be present in the stack */ - lua_settop(L, 5 + aggregate); - - /* save registered function in db function list */ - func->db = db; - func->aggregate = aggregate; - func->next = db->func; - db->func = func; - - /* save the setp/normal function callback */ - lua_pushvalue(L, 4); - func->fn_step = luaL_ref(L, LUA_REGISTRYINDEX); - /* save user data */ - lua_pushvalue(L, 5+aggregate); - func->udata = luaL_ref(L, LUA_REGISTRYINDEX); - - if (aggregate) { - lua_pushvalue(L, 5); - func->fn_finalize = luaL_ref(L, LUA_REGISTRYINDEX); - } - else - func->fn_finalize = LUA_NOREF; - } - else { - /* free allocated memory */ - free(func); - } - - lua_pushboolean(L, result == SQLITE_OK ? 1 : 0); - return 1; -} - -static int db_create_function(lua_State *L) { - return db_register_function(L, 0); -} - -static int db_create_aggregate(lua_State *L) { - return db_register_function(L, 1); -} - -/* create_collation; contributed by Thomas Lauer -*/ - -typedef struct { - lua_State *L; - int ref; -} scc; - -static int collwrapper(scc *co,int l1,const void *p1, - int l2,const void *p2) { - int res=0; - lua_State *L=co->L; - lua_rawgeti(L,LUA_REGISTRYINDEX,co->ref); - lua_pushlstring(L,p1,l1); - lua_pushlstring(L,p2,l2); - if (lua_pcall(L,2,1,0)==0) res=(int)lua_tonumber(L,-1); - lua_pop(L,1); - return res; -} - -static void collfree(scc *co) { - if (co) { - luaL_unref(co->L,LUA_REGISTRYINDEX,co->ref); - free(co); - } -} - -static int db_create_collation(lua_State *L) { - sdb *db=lsqlite_checkdb(L,1); - const char *collname=luaL_checkstring(L,2); - scc *co=NULL; - int (*collfunc)(scc *,int,const void *,int,const void *)=NULL; - lua_settop(L,3); /* default args to nil, and exclude extras */ - if (lua_isfunction(L,3)) collfunc=collwrapper; - else if (!lua_isnil(L,3)) - luaL_error(L,"create_collation: function or nil expected"); - if (collfunc != NULL) { - co=(scc *)malloc(sizeof(scc)); /* userdata is a no-no as it - will be garbage-collected */ - if (co) { - co->L=L; - /* lua_settop(L,3) above means we don't need: lua_pushvalue(L,3); */ - co->ref=luaL_ref(L,LUA_REGISTRYINDEX); - } - else luaL_error(L,"create_collation: could not allocate callback"); - } - sqlite3_create_collation_v2(db->db, collname, SQLITE_UTF8, - (void *)co, - (int(*)(void*,int,const void*,int,const void*))collfunc, - (void(*)(void*))collfree); - return 0; -} - -/* -** trace callback: -** Params: database, callback function, userdata -** -** callback function: -** Params: userdata, sql -*/ -static void db_trace_callback(void *user, const char *sql) { - sdb *db = (sdb*)user; - lua_State *L = db->L; - int top = lua_gettop(L); - - /* setup lua callback call */ - lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_cb); /* get callback */ - lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_udata); /* get callback user data */ - lua_pushstring(L, sql); /* traced sql statement */ - - /* call lua function */ - lua_pcall(L, 2, 0, 0); - /* ignore any error generated by this function */ - - lua_settop(L, top); -} - -static int db_trace(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - - if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { - luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata); - - db->trace_cb = - db->trace_udata = LUA_NOREF; - - /* clear trace handler */ - sqlite3_trace(db->db, NULL, NULL); - } - else { - luaL_checktype(L, 2, LUA_TFUNCTION); - - /* make sure we have an userdata field (even if nil) */ - lua_settop(L, 3); - - luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata); - - db->trace_udata = luaL_ref(L, LUA_REGISTRYINDEX); - db->trace_cb = luaL_ref(L, LUA_REGISTRYINDEX); - - /* set trace handler */ - sqlite3_trace(db->db, db_trace_callback, db); - } - - return 0; -} - -#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK - -/* -** update_hook callback: -** Params: database, callback function, userdata -** -** callback function: -** Params: userdata, {one of SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE}, -** database name, table name (containing the affected row), rowid of the row -*/ -static void db_update_hook_callback(void *user, int op, char const *dbname, char const *tblname, sqlite3_int64 rowid) { - sdb *db = (sdb*)user; - lua_State *L = db->L; - int top = lua_gettop(L); - lua_Number n = (lua_Number)rowid; - - /* setup lua callback call */ - lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_cb); /* get callback */ - lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_udata); /* get callback user data */ - lua_pushnumber(L, (lua_Number )op); - lua_pushstring(L, dbname); /* update_hook database name */ - lua_pushstring(L, tblname); /* update_hook database name */ - if (n == rowid) - lua_pushnumber(L, n); - else - lua_pushfstring(L, "%ll", rowid); - - /* call lua function */ - lua_pcall(L, 5, 0, 0); - /* ignore any error generated by this function */ - - lua_settop(L, top); -} - -static int db_update_hook(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - - if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { - luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata); - - db->update_hook_cb = - db->update_hook_udata = LUA_NOREF; - - /* clear update_hook handler */ - sqlite3_update_hook(db->db, NULL, NULL); - } - else { - luaL_checktype(L, 2, LUA_TFUNCTION); - - /* make sure we have an userdata field (even if nil) */ - lua_settop(L, 3); - - luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata); - - db->update_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX); - db->update_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX); - - /* set update_hook handler */ - sqlite3_update_hook(db->db, db_update_hook_callback, db); - } - - return 0; -} - -/* -** commit_hook callback: -** Params: database, callback function, userdata -** -** callback function: -** Params: userdata -** Returned value: Return false or nil to continue the COMMIT operation normally. -** return true (non false, non nil), then the COMMIT is converted into a ROLLBACK. -*/ -static int db_commit_hook_callback(void *user) { - sdb *db = (sdb*)user; - lua_State *L = db->L; - int top = lua_gettop(L); - int rollback = 0; - - /* setup lua callback call */ - lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_cb); /* get callback */ - lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_udata); /* get callback user data */ - - /* call lua function */ - if (!lua_pcall(L, 1, 1, 0)) - rollback = lua_toboolean(L, -1); /* use result if there was no error */ - - lua_settop(L, top); - return rollback; -} - -static int db_commit_hook(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - - if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { - luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata); - - db->commit_hook_cb = - db->commit_hook_udata = LUA_NOREF; - - /* clear commit_hook handler */ - sqlite3_commit_hook(db->db, NULL, NULL); - } - else { - luaL_checktype(L, 2, LUA_TFUNCTION); - - /* make sure we have an userdata field (even if nil) */ - lua_settop(L, 3); - - luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata); - - db->commit_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX); - db->commit_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX); - - /* set commit_hook handler */ - sqlite3_commit_hook(db->db, db_commit_hook_callback, db); - } - - return 0; -} - -/* -** rollback hook callback: -** Params: database, callback function, userdata -** -** callback function: -** Params: userdata -*/ -static void db_rollback_hook_callback(void *user) { - sdb *db = (sdb*)user; - lua_State *L = db->L; - int top = lua_gettop(L); - - /* setup lua callback call */ - lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); /* get callback */ - lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); /* get callback user data */ - - /* call lua function */ - lua_pcall(L, 1, 0, 0); - /* ignore any error generated by this function */ - - lua_settop(L, top); -} - -static int db_rollback_hook(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - - if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { - luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); - - db->rollback_hook_cb = - db->rollback_hook_udata = LUA_NOREF; - - /* clear rollback_hook handler */ - sqlite3_rollback_hook(db->db, NULL, NULL); - } - else { - luaL_checktype(L, 2, LUA_TFUNCTION); - - /* make sure we have an userdata field (even if nil) */ - lua_settop(L, 3); - - luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); - - db->rollback_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX); - db->rollback_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX); - - /* set rollback_hook handler */ - sqlite3_rollback_hook(db->db, db_rollback_hook_callback, db); - } - - return 0; -} - -#endif /* #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK */ - -#if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK - -/* -** progress handler: -** Params: database, number of opcodes, callback function, userdata -** -** callback function: -** Params: userdata -** returns: 0 to return immediatly and return SQLITE_ABORT, non-zero to continue -*/ -static int db_progress_callback(void *user) { - int result = 1; /* abort by default */ - sdb *db = (sdb*)user; - lua_State *L = db->L; - int top = lua_gettop(L); - - lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_cb); - lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_udata); - - /* call lua function */ - if (!lua_pcall(L, 1, 1, 0)) - result = lua_toboolean(L, -1); - - lua_settop(L, top); - return result; -} - -static int db_progress_handler(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - - if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { - luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata); - - db->progress_cb = - db->progress_udata = LUA_NOREF; - - /* clear busy handler */ - sqlite3_progress_handler(db->db, 0, NULL, NULL); - } - else { - int nop = luaL_checkint(L, 2); /* number of opcodes */ - luaL_checktype(L, 3, LUA_TFUNCTION); - - /* make sure we have an userdata field (even if nil) */ - lua_settop(L, 4); - - luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata); - - db->progress_udata = luaL_ref(L, LUA_REGISTRYINDEX); - db->progress_cb = luaL_ref(L, LUA_REGISTRYINDEX); - - /* set progress callback */ - sqlite3_progress_handler(db->db, nop, db_progress_callback, db); - } - - return 0; -} - -#else /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */ - -static int db_progress_handler(lua_State *L) { - lua_pushliteral(L, "progress callback support disabled at compile time"); - lua_error(L); - return 0; -} - -#endif /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */ - -/* -** busy handler: -** Params: database, callback function, userdata -** -** callback function: -** Params: userdata, number of tries -** returns: 0 to return immediatly and return SQLITE_BUSY, non-zero to try again -*/ -static int db_busy_callback(void *user, int tries) { - int retry = 0; /* abort by default */ - sdb *db = (sdb*)user; - lua_State *L = db->L; - int top = lua_gettop(L); - - lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_cb); - lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_udata); - lua_pushnumber(L, tries); - - /* call lua function */ - if (!lua_pcall(L, 2, 1, 0)) - retry = lua_toboolean(L, -1); - - lua_settop(L, top); - return retry; -} - -static int db_busy_handler(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - - if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { - luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); - - db->busy_cb = - db->busy_udata = LUA_NOREF; - - /* clear busy handler */ - sqlite3_busy_handler(db->db, NULL, NULL); - } - else { - luaL_checktype(L, 2, LUA_TFUNCTION); - /* make sure we have an userdata field (even if nil) */ - lua_settop(L, 3); - - luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); - - db->busy_udata = luaL_ref(L, LUA_REGISTRYINDEX); - db->busy_cb = luaL_ref(L, LUA_REGISTRYINDEX); - - /* set busy handler */ - sqlite3_busy_handler(db->db, db_busy_callback, db); - } - - return 0; -} - -static int db_busy_timeout(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - int timeout = luaL_checkint(L, 2); - sqlite3_busy_timeout(db->db, timeout); - - /* if there was a timeout callback registered, it is now - ** invalid/useless. free any references we may have */ - luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); - luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); - db->busy_cb = - db->busy_udata = LUA_NOREF; - - return 0; -} - -/* -** Params: db, sql, callback, user -** returns: code [, errmsg] -** -** Callback: -** Params: user, number of columns, values, names -** Returns: 0 to continue, other value will cause abort -*/ -static int db_exec_callback(void* user, int columns, char **data, char **names) { - int result = SQLITE_ABORT; /* abort by default */ - lua_State *L = (lua_State*)user; - int n; - - int top = lua_gettop(L); - - lua_pushvalue(L, 3); /* function to call */ - lua_pushvalue(L, 4); /* user data */ - lua_pushnumber(L, columns); /* total number of rows in result */ - - /* column values */ - lua_pushvalue(L, 6); - for (n = 0; n < columns;) { - lua_pushstring(L, data[n++]); - lua_rawseti(L, -2, n); - } - - /* columns names */ - lua_pushvalue(L, 5); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - lua_newtable(L); - lua_pushvalue(L, -1); - lua_replace(L, 5); - for (n = 0; n < columns;) { - lua_pushstring(L, names[n++]); - lua_rawseti(L, -2, n); - } - } - - /* call lua function */ - if (!lua_pcall(L, 4, 1, 0)) { - if (lua_isnumber(L, -1)) - result = (int)lua_tonumber(L, -1); - } - - lua_settop(L, top); - return result; -} - -static int db_exec(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - const char *sql = luaL_checkstring(L, 2); - int result; - - if (!lua_isnoneornil(L, 3)) { - /* stack: - ** 3: callback function - ** 4: userdata - ** 5: column names - ** 6: reusable column values - */ - luaL_checktype(L, 3, LUA_TFUNCTION); - lua_settop(L, 4); /* 'trap' userdata - nil extra parameters */ - lua_pushnil(L); /* column names not known at this point */ - lua_newtable(L); /* column values table */ - - result = sqlite3_exec(db->db, sql, db_exec_callback, L, NULL); - } - else { - /* no callbacks */ - result = sqlite3_exec(db->db, sql, NULL, NULL, NULL); - } - - lua_pushnumber(L, result); - return 1; -} - -/* -** Params: db, sql -** returns: code, compiled length or error message -*/ -static int db_prepare(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - const char *sql = luaL_checkstring(L, 2); - int sql_len = lua_strlen(L, 2); - const char *sqltail; - sdb_vm *svm; - lua_settop(L,2); /* sql is on top of stack for call to newvm */ - svm = newvm(L, db); - - if (sqlite3_prepare(db->db, sql, sql_len, &svm->vm, &sqltail) != SQLITE_OK) { - cleanupvm(L, svm); - - lua_pushnil(L); - lua_pushnumber(L, sqlite3_errcode(db->db)); - return 2; - } - - /* vm already in the stack */ - lua_pushstring(L, sqltail); - return 2; -} - -static int db_do_next_row(lua_State *L, int packed) { - int result; - sdb_vm *svm = lsqlite_checkvm(L, 1); - sqlite3_stmt *vm; - int columns; - int i; - - result = stepvm(L, svm); - vm = svm->vm; /* stepvm may change svm->vm if re-prepare is needed */ - svm->has_values = result == SQLITE_ROW ? 1 : 0; - svm->columns = columns = sqlite3_data_count(vm); - - if (result == SQLITE_ROW) { - if (packed) { - lua_newtable(L); - if (packed == 1) { - for (i = 0; i < columns;) { - vm_push_column(L, vm, i); - lua_rawseti(L, -2, ++i); - } - } - else { - for (i = 0; i < columns; ++i) { - lua_pushstring(L, sqlite3_column_name(vm, i)); - vm_push_column(L, vm, i); - lua_rawset(L, -3); - } - } - return 1; - } - else { - lua_checkstack(L, columns); - for (i = 0; i < columns; ++i) - vm_push_column(L, vm, i); - return svm->columns; - } - } - - if (svm->temp) { - /* finalize and check for errors */ - result = sqlite3_finalize(vm); - svm->vm = NULL; - cleanupvm(L, svm); - } - else if (result == SQLITE_DONE) { - result = sqlite3_reset(vm); - } - - if (result != SQLITE_OK) { - lua_pushstring(L, sqlite3_errmsg(svm->db->db)); - lua_error(L); - } - return 0; -} - -static int db_next_row(lua_State *L) { - return db_do_next_row(L, 0); -} - -static int db_next_packed_row(lua_State *L) { - return db_do_next_row(L, 1); -} - -static int db_next_named_row(lua_State *L) { - return db_do_next_row(L, 2); -} - -static int dbvm_do_rows(lua_State *L, int(*f)(lua_State *)) { - /* sdb_vm *svm = */ - lsqlite_checkvm(L, 1); - lua_pushvalue(L,1); - lua_pushcfunction(L, f); - lua_insert(L, -2); - return 2; -} - -static int dbvm_rows(lua_State *L) { - return dbvm_do_rows(L, db_next_packed_row); -} - -static int dbvm_nrows(lua_State *L) { - return dbvm_do_rows(L, db_next_named_row); -} - -static int dbvm_urows(lua_State *L) { - return dbvm_do_rows(L, db_next_row); -} - -static int db_do_rows(lua_State *L, int(*f)(lua_State *)) { - sdb *db = lsqlite_checkdb(L, 1); - const char *sql = luaL_checkstring(L, 2); - sdb_vm *svm; - lua_settop(L,2); /* sql is on top of stack for call to newvm */ - svm = newvm(L, db); - svm->temp = 1; - - if (sqlite3_prepare(db->db, sql, -1, &svm->vm, NULL) != SQLITE_OK) { - cleanupvm(L, svm); - - lua_pushstring(L, sqlite3_errmsg(svm->db->db)); - lua_error(L); - } - - lua_pushcfunction(L, f); - lua_insert(L, -2); - return 2; -} - -static int db_rows(lua_State *L) { - return db_do_rows(L, db_next_packed_row); -} - -static int db_nrows(lua_State *L) { - return db_do_rows(L, db_next_named_row); -} - -/* unpacked version of db:rows */ -static int db_urows(lua_State *L) { - return db_do_rows(L, db_next_row); -} - -static int db_tostring(lua_State *L) { - char buff[32]; - sdb *db = lsqlite_getdb(L, 1); - if (db->db == NULL) - strcpy(buff, "closed"); - else - sprintf(buff, "%p", lua_touserdata(L, 1)); - lua_pushfstring(L, "sqlite database (%s)", buff); - return 1; -} - -static int db_close(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - lua_pushnumber(L, cleanupdb(L, db)); - return 1; -} - -static int db_close_vm(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); - /* cleanup temporary only tables? */ - int temp = lua_toboolean(L, 2); - - /* free associated virtual machines */ - lua_pushlightuserdata(L, db); - lua_rawget(L, LUA_REGISTRYINDEX); - - /* close all used handles */ - lua_pushnil(L); - while (lua_next(L, -2)) { - sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */ - - if ((!temp || svm->temp) && svm->vm) - { - sqlite3_finalize(svm->vm); - svm->vm = NULL; - } - - /* leave key in the stack */ - lua_pop(L, 1); - } - return 0; -} - -static int db_gc(lua_State *L) { - sdb *db = lsqlite_getdb(L, 1); - if (db->db != NULL) /* ignore closed databases */ - cleanupdb(L, db); - return 0; -} - -/* -** ======================================================= -** General library functions -** ======================================================= -*/ - -static int lsqlite_version(lua_State *L) { - lua_pushstring(L, sqlite3_libversion()); - return 1; -} - -static int lsqlite_complete(lua_State *L) { - const char *sql = luaL_checkstring(L, 1); - lua_pushboolean(L, sqlite3_complete(sql)); - return 1; -} - -#ifndef WIN32 -static int lsqlite_temp_directory(lua_State *L) { - const char *oldtemp = sqlite3_temp_directory; - - if (!lua_isnone(L, 1)) { - const char *temp = luaL_optstring(L, 1, NULL); - if (sqlite3_temp_directory) { - sqlite3_free((char*)sqlite3_temp_directory); - } - if (temp) { - sqlite3_temp_directory = sqlite3_mprintf("%s", temp); - } - else { - sqlite3_temp_directory = NULL; - } - } - lua_pushstring(L, oldtemp); - return 1; -} -#endif - -static int lsqlite_do_open(lua_State *L, const char *filename) { - sdb *db = newdb(L); /* create and leave in stack */ - - if (sqlite3_open(filename, &db->db) == SQLITE_OK) { - /* database handle already in the stack - return it */ - return 1; - } - - /* failed to open database */ - lua_pushnil(L); /* push nil */ - lua_pushnumber(L, sqlite3_errcode(db->db)); - lua_pushstring(L, sqlite3_errmsg(db->db)); /* push error message */ - - /* clean things up */ - cleanupdb(L, db); - - /* return */ - return 3; -} - -static int lsqlite_open(lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - return lsqlite_do_open(L, filename); -} - -static int lsqlite_open_memory(lua_State *L) { - return lsqlite_do_open(L, ":memory:"); -} - -static int lsqlite_newindex(lua_State *L) { - lua_pushliteral(L, "attempt to change readonly table"); - lua_error(L); - return 0; -} - -/* -** ======================================================= -** Register functions -** ======================================================= -*/ - -#define SC(s) { #s, SQLITE_ ## s }, -#define LSC(s) { #s, LSQLITE_ ## s }, - -static const struct { - const char* name; - int value; -} sqlite_constants[] = { - /* error codes */ - SC(OK) SC(ERROR) SC(INTERNAL) SC(PERM) - SC(ABORT) SC(BUSY) SC(LOCKED) SC(NOMEM) - SC(READONLY) SC(INTERRUPT) SC(IOERR) SC(CORRUPT) - SC(NOTFOUND) SC(FULL) SC(CANTOPEN) SC(PROTOCOL) - SC(EMPTY) SC(SCHEMA) SC(TOOBIG) SC(CONSTRAINT) - SC(MISMATCH) SC(MISUSE) SC(NOLFS) - SC(FORMAT) SC(NOTADB) - - /* sqlite_step specific return values */ - SC(RANGE) SC(ROW) SC(DONE) - - /* column types */ - SC(INTEGER) SC(FLOAT) SC(TEXT) SC(BLOB) - SC(NULL) - - /* Authorizer Action Codes */ - SC(CREATE_INDEX ) - SC(CREATE_TABLE ) - SC(CREATE_TEMP_INDEX ) - SC(CREATE_TEMP_TABLE ) - SC(CREATE_TEMP_TRIGGER) - SC(CREATE_TEMP_VIEW ) - SC(CREATE_TRIGGER ) - SC(CREATE_VIEW ) - SC(DELETE ) - SC(DROP_INDEX ) - SC(DROP_TABLE ) - SC(DROP_TEMP_INDEX ) - SC(DROP_TEMP_TABLE ) - SC(DROP_TEMP_TRIGGER ) - SC(DROP_TEMP_VIEW ) - SC(DROP_TRIGGER ) - SC(DROP_VIEW ) - SC(INSERT ) - SC(PRAGMA ) - SC(READ ) - SC(SELECT ) - SC(TRANSACTION ) - SC(UPDATE ) - SC(ATTACH ) - SC(DETACH ) - SC(ALTER_TABLE ) - SC(REINDEX ) - SC(ANALYZE ) - SC(CREATE_VTABLE ) - SC(DROP_VTABLE ) - SC(FUNCTION ) - SC(SAVEPOINT ) - - /* terminator */ - { NULL, 0 } -}; - -/* ======================================================= */ - -static const luaL_Reg dblib[] = { - {"isopen", db_isopen }, - {"last_insert_rowid", db_last_insert_rowid }, - {"changes", db_changes }, - {"total_changes", db_total_changes }, - {"errcode", db_errcode }, - {"error_code", db_errcode }, - {"errmsg", db_errmsg }, - {"error_message", db_errmsg }, - {"interrupt", db_interrupt }, - - {"create_function", db_create_function }, - {"create_aggregate", db_create_aggregate }, - {"create_collation", db_create_collation }, - - {"trace", db_trace }, - {"progress_handler", db_progress_handler }, - {"busy_timeout", db_busy_timeout }, - {"busy_handler", db_busy_handler }, -#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK - {"update_hook", db_update_hook }, - {"commit_hook", db_commit_hook }, - {"rollback_hook", db_rollback_hook }, -#endif - - {"prepare", db_prepare }, - {"rows", db_rows }, - {"urows", db_urows }, - {"nrows", db_nrows }, - - {"exec", db_exec }, - {"execute", db_exec }, - {"close", db_close }, - {"close_vm", db_close_vm }, - - {"__tostring", db_tostring }, - {"__gc", db_gc }, - - {NULL, NULL} -}; - -static const luaL_Reg vmlib[] = { - {"isopen", dbvm_isopen }, - - {"step", dbvm_step }, - {"reset", dbvm_reset }, - {"finalize", dbvm_finalize }, - - {"columns", dbvm_columns }, - - {"bind", dbvm_bind }, - {"bind_values", dbvm_bind_values }, - {"bind_names", dbvm_bind_names }, - {"bind_blob", dbvm_bind_blob }, - {"bind_parameter_count",dbvm_bind_parameter_count}, - {"bind_parameter_name", dbvm_bind_parameter_name}, - - {"get_value", dbvm_get_value }, - {"get_values", dbvm_get_values }, - {"get_name", dbvm_get_name }, - {"get_names", dbvm_get_names }, - {"get_type", dbvm_get_type }, - {"get_types", dbvm_get_types }, - {"get_uvalues", dbvm_get_uvalues }, - {"get_unames", dbvm_get_unames }, - {"get_utypes", dbvm_get_utypes }, - - {"get_named_values", dbvm_get_named_values }, - {"get_named_types", dbvm_get_named_types }, - - {"rows", dbvm_rows }, - {"urows", dbvm_urows }, - {"nrows", dbvm_nrows }, - - /* compatibility names (added by request) */ - {"idata", dbvm_get_values }, - {"inames", dbvm_get_names }, - {"itypes", dbvm_get_types }, - {"data", dbvm_get_named_values }, - {"type", dbvm_get_named_types }, - - {"__tostring", dbvm_tostring }, - {"__gc", dbvm_gc }, - - { NULL, NULL } -}; - -static const luaL_Reg ctxlib[] = { - {"user_data", lcontext_user_data }, - - {"get_aggregate_data", lcontext_get_aggregate_context }, - {"set_aggregate_data", lcontext_set_aggregate_context }, - {"aggregate_count", lcontext_aggregate_count }, - - {"result", lcontext_result }, - {"result_null", lcontext_result_null }, - {"result_number", lcontext_result_double }, - {"result_double", lcontext_result_double }, - {"result_int", lcontext_result_int }, - {"result_text", lcontext_result_text }, - {"result_blob", lcontext_result_blob }, - {"result_error", lcontext_result_error }, - - {"__tostring", lcontext_tostring }, - {NULL, NULL} -}; - -static const luaL_Reg sqlitelib[] = { - {"version", lsqlite_version }, - {"complete", lsqlite_complete }, -#ifndef WIN32 - {"temp_directory", lsqlite_temp_directory }, -#endif - {"open", lsqlite_open }, - {"open_memory", lsqlite_open_memory }, - - {"__newindex", lsqlite_newindex }, - {NULL, NULL} -}; - -static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) { - luaL_newmetatable(L, name); - lua_pushstring(L, "__index"); - lua_pushvalue(L, -2); /* push metatable */ - lua_rawset(L, -3); /* metatable.__index = metatable */ - - /* register metatable functions */ - luaL_openlib(L, NULL, lib, 0); - - /* remove metatable from stack */ - lua_pop(L, 1); -} - -LUALIB_API int luaopen_lsqlite3(lua_State *L) { - create_meta(L, sqlite_meta, dblib); - create_meta(L, sqlite_vm_meta, vmlib); - create_meta(L, sqlite_ctx_meta, ctxlib); - - luaL_getmetatable(L, sqlite_ctx_meta); - sqlite_ctx_meta_ref = luaL_ref(L, LUA_REGISTRYINDEX); - - /* register (local) sqlite metatable */ - luaL_register(L, "sqlite3", sqlitelib); - - { - int i = 0; - /* add constants to global table */ - while (sqlite_constants[i].name) { - lua_pushstring(L, sqlite_constants[i].name); - lua_pushnumber(L, sqlite_constants[i].value); - lua_rawset(L, -3); - ++i; - } - } - - /* set sqlite's metatable to itself - set as readonly (__newindex) */ - lua_pushvalue(L, -1); - lua_setmetatable(L, -2); - - return 1; -} - - - - -#ifdef __cplusplus -} // extern "C" -#endif // __cplusplus - - - - diff --git a/source/SQLite/sqlite3.c b/source/SQLite/sqlite3.c deleted file mode 100644 index 37ee4ad38..000000000 --- a/source/SQLite/sqlite3.c +++ /dev/null @@ -1,138114 +0,0 @@ -/****************************************************************************** -** This file is an amalgamation of many separate C source files from SQLite -** version 3.7.16.1. By combining all the individual C code files into this -** single large file, the entire code can be compiled as a single translation -** unit. This allows many compilers to do optimizations that would not be -** possible if the files were compiled separately. Performance improvements -** of 5% or more are commonly seen when SQLite is compiled as a single -** translation unit. -** -** This file is all you need to compile SQLite. To use SQLite in other -** programs, you need this file and the "sqlite3.h" header file that defines -** the programming interface to the SQLite library. (If you do not have -** the "sqlite3.h" header file at hand, you will find a copy embedded within -** the text of this file. Search for "Begin file sqlite3.h" to find the start -** of the embedded sqlite3.h header file.) Additional code files may be needed -** if you want a wrapper to interface SQLite with your choice of programming -** language. The code for the "sqlite3" command-line shell is also in a -** separate file. This file contains only code for the core SQLite library. -*/ -#define SQLITE_CORE 1 -#define SQLITE_AMALGAMATION 1 -#ifndef SQLITE_PRIVATE -# define SQLITE_PRIVATE static -#endif -#ifndef SQLITE_API -# define SQLITE_API -#endif -/************** Begin file sqliteInt.h ***************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Internal interface definitions for SQLite. -** -*/ -#ifndef _SQLITEINT_H_ -#define _SQLITEINT_H_ - -/* -** These #defines should enable >2GB file support on POSIX if the -** underlying operating system supports it. If the OS lacks -** large file support, or if the OS is windows, these should be no-ops. -** -** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any -** system #includes. Hence, this block of code must be the very first -** code in all source files. -** -** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch -** on the compiler command line. This is necessary if you are compiling -** on a recent machine (ex: Red Hat 7.2) but you want your code to work -** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 -** without this option, LFS is enable. But LFS does not exist in the kernel -** in Red Hat 6.0, so the code won't work. Hence, for maximum binary -** portability you should omit LFS. -** -** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. -*/ -#ifndef SQLITE_DISABLE_LFS -# define _LARGE_FILE 1 -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# define _LARGEFILE_SOURCE 1 -#endif - -/* -** Include the configuration header output by 'configure' if we're using the -** autoconf-based build -*/ -#ifdef _HAVE_SQLITE_CONFIG_H -#include "config.h" -#endif - -/************** Include sqliteLimit.h in the middle of sqliteInt.h ***********/ -/************** Begin file sqliteLimit.h *************************************/ -/* -** 2007 May 7 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file defines various limits of what SQLite can process. -*/ - -/* -** The maximum length of a TEXT or BLOB in bytes. This also -** limits the size of a row in a table or index. -** -** The hard limit is the ability of a 32-bit signed integer -** to count the size: 2^31-1 or 2147483647. -*/ -#ifndef SQLITE_MAX_LENGTH -# define SQLITE_MAX_LENGTH 1000000000 -#endif - -/* -** This is the maximum number of -** -** * Columns in a table -** * Columns in an index -** * Columns in a view -** * Terms in the SET clause of an UPDATE statement -** * Terms in the result set of a SELECT statement -** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement. -** * Terms in the VALUES clause of an INSERT statement -** -** The hard upper limit here is 32676. Most database people will -** tell you that in a well-normalized database, you usually should -** not have more than a dozen or so columns in any table. And if -** that is the case, there is no point in having more than a few -** dozen values in any of the other situations described above. -*/ -#ifndef SQLITE_MAX_COLUMN -# define SQLITE_MAX_COLUMN 2000 -#endif - -/* -** The maximum length of a single SQL statement in bytes. -** -** It used to be the case that setting this value to zero would -** turn the limit off. That is no longer true. It is not possible -** to turn this limit off. -*/ -#ifndef SQLITE_MAX_SQL_LENGTH -# define SQLITE_MAX_SQL_LENGTH 1000000000 -#endif - -/* -** The maximum depth of an expression tree. This is limited to -** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might -** want to place more severe limits on the complexity of an -** expression. -** -** A value of 0 used to mean that the limit was not enforced. -** But that is no longer true. The limit is now strictly enforced -** at all times. -*/ -#ifndef SQLITE_MAX_EXPR_DEPTH -# define SQLITE_MAX_EXPR_DEPTH 1000 -#endif - -/* -** The maximum number of terms in a compound SELECT statement. -** The code generator for compound SELECT statements does one -** level of recursion for each term. A stack overflow can result -** if the number of terms is too large. In practice, most SQL -** never has more than 3 or 4 terms. Use a value of 0 to disable -** any limit on the number of terms in a compount SELECT. -*/ -#ifndef SQLITE_MAX_COMPOUND_SELECT -# define SQLITE_MAX_COMPOUND_SELECT 500 -#endif - -/* -** The maximum number of opcodes in a VDBE program. -** Not currently enforced. -*/ -#ifndef SQLITE_MAX_VDBE_OP -# define SQLITE_MAX_VDBE_OP 25000 -#endif - -/* -** The maximum number of arguments to an SQL function. -*/ -#ifndef SQLITE_MAX_FUNCTION_ARG -# define SQLITE_MAX_FUNCTION_ARG 127 -#endif - -/* -** The maximum number of in-memory pages to use for the main database -** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE -*/ -#ifndef SQLITE_DEFAULT_CACHE_SIZE -# define SQLITE_DEFAULT_CACHE_SIZE 2000 -#endif -#ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE -# define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500 -#endif - -/* -** The default number of frames to accumulate in the log file before -** checkpointing the database in WAL mode. -*/ -#ifndef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT -# define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000 -#endif - -/* -** The maximum number of attached databases. This must be between 0 -** and 62. The upper bound on 62 is because a 64-bit integer bitmap -** is used internally to track attached databases. -*/ -#ifndef SQLITE_MAX_ATTACHED -# define SQLITE_MAX_ATTACHED 10 -#endif - - -/* -** The maximum value of a ?nnn wildcard that the parser will accept. -*/ -#ifndef SQLITE_MAX_VARIABLE_NUMBER -# define SQLITE_MAX_VARIABLE_NUMBER 999 -#endif - -/* Maximum page size. The upper bound on this value is 65536. This a limit -** imposed by the use of 16-bit offsets within each page. -** -** Earlier versions of SQLite allowed the user to change this value at -** compile time. This is no longer permitted, on the grounds that it creates -** a library that is technically incompatible with an SQLite library -** compiled with a different limit. If a process operating on a database -** with a page-size of 65536 bytes crashes, then an instance of SQLite -** compiled with the default page-size limit will not be able to rollback -** the aborted transaction. This could lead to database corruption. -*/ -#ifdef SQLITE_MAX_PAGE_SIZE -# undef SQLITE_MAX_PAGE_SIZE -#endif -#define SQLITE_MAX_PAGE_SIZE 65536 - - -/* -** The default size of a database page. -*/ -#ifndef SQLITE_DEFAULT_PAGE_SIZE -# define SQLITE_DEFAULT_PAGE_SIZE 1024 -#endif -#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE -# undef SQLITE_DEFAULT_PAGE_SIZE -# define SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE -#endif - -/* -** Ordinarily, if no value is explicitly provided, SQLite creates databases -** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain -** device characteristics (sector-size and atomic write() support), -** SQLite may choose a larger value. This constant is the maximum value -** SQLite will choose on its own. -*/ -#ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE -# define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192 -#endif -#if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE -# undef SQLITE_MAX_DEFAULT_PAGE_SIZE -# define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE -#endif - - -/* -** Maximum number of pages in one database file. -** -** This is really just the default value for the max_page_count pragma. -** This value can be lowered (or raised) at run-time using that the -** max_page_count macro. -*/ -#ifndef SQLITE_MAX_PAGE_COUNT -# define SQLITE_MAX_PAGE_COUNT 1073741823 -#endif - -/* -** Maximum length (in bytes) of the pattern in a LIKE or GLOB -** operator. -*/ -#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH -# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 -#endif - -/* -** Maximum depth of recursion for triggers. -** -** A value of 1 means that a trigger program will not be able to itself -** fire any triggers. A value of 0 means that no trigger programs at all -** may be executed. -*/ -#ifndef SQLITE_MAX_TRIGGER_DEPTH -# define SQLITE_MAX_TRIGGER_DEPTH 1000 -#endif - -/************** End of sqliteLimit.h *****************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ - -/* Disable nuisance warnings on Borland compilers */ -#if defined(__BORLANDC__) -#pragma warn -rch /* unreachable code */ -#pragma warn -ccc /* Condition is always true or false */ -#pragma warn -aus /* Assigned value is never used */ -#pragma warn -csu /* Comparing signed and unsigned */ -#pragma warn -spa /* Suspicious pointer arithmetic */ -#endif - -/* Needed for various definitions... */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif - -#if defined(__OpenBSD__) && !defined(_BSD_SOURCE) -# define _BSD_SOURCE -#endif - -/* -** Include standard header files as necessary -*/ -#ifdef HAVE_STDINT_H -#include -#endif -#ifdef HAVE_INTTYPES_H -#include -#endif - -/* -** The following macros are used to cast pointers to integers and -** integers to pointers. The way you do this varies from one compiler -** to the next, so we have developed the following set of #if statements -** to generate appropriate macros for a wide range of compilers. -** -** The correct "ANSI" way to do this is to use the intptr_t type. -** Unfortunately, that typedef is not available on all compilers, or -** if it is available, it requires an #include of specific headers -** that vary from one machine to the next. -** -** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on -** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)). -** So we have to define the macros in different ways depending on the -** compiler. -*/ -#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ -# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) -# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) -#elif !defined(__GNUC__) /* Works for compilers other than LLVM */ -# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) -# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) -#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */ -# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X)) -# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) -#else /* Generates a warning - but it always works */ -# define SQLITE_INT_TO_PTR(X) ((void*)(X)) -# define SQLITE_PTR_TO_INT(X) ((int)(X)) -#endif - -/* -** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. -** 0 means mutexes are permanently disable and the library is never -** threadsafe. 1 means the library is serialized which is the highest -** level of threadsafety. 2 means the libary is multithreaded - multiple -** threads can use SQLite as long as no two threads try to use the same -** database connection at the same time. -** -** Older versions of SQLite used an optional THREADSAFE macro. -** We support that for legacy. -*/ -#if !defined(SQLITE_THREADSAFE) -#if defined(THREADSAFE) -# define SQLITE_THREADSAFE THREADSAFE -#else -# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ -#endif -#endif - -/* -** Powersafe overwrite is on by default. But can be turned off using -** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option. -*/ -#ifndef SQLITE_POWERSAFE_OVERWRITE -# define SQLITE_POWERSAFE_OVERWRITE 1 -#endif - -/* -** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1. -** It determines whether or not the features related to -** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can -** be overridden at runtime using the sqlite3_config() API. -*/ -#if !defined(SQLITE_DEFAULT_MEMSTATUS) -# define SQLITE_DEFAULT_MEMSTATUS 1 -#endif - -/* -** Exactly one of the following macros must be defined in order to -** specify which memory allocation subsystem to use. -** -** SQLITE_SYSTEM_MALLOC // Use normal system malloc() -** SQLITE_WIN32_MALLOC // Use Win32 native heap API -** SQLITE_ZERO_MALLOC // Use a stub allocator that always fails -** SQLITE_MEMDEBUG // Debugging version of system malloc() -** -** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the -** assert() macro is enabled, each call into the Win32 native heap subsystem -** will cause HeapValidate to be called. If heap validation should fail, an -** assertion will be triggered. -** -** (Historical note: There used to be several other options, but we've -** pared it down to just these three.) -** -** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as -** the default. -*/ -#if defined(SQLITE_SYSTEM_MALLOC) \ - + defined(SQLITE_WIN32_MALLOC) \ - + defined(SQLITE_ZERO_MALLOC) \ - + defined(SQLITE_MEMDEBUG)>1 -# error "Two or more of the following compile-time configuration options\ - are defined but at most one is allowed:\ - SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG,\ - SQLITE_ZERO_MALLOC" -#endif -#if defined(SQLITE_SYSTEM_MALLOC) \ - + defined(SQLITE_WIN32_MALLOC) \ - + defined(SQLITE_ZERO_MALLOC) \ - + defined(SQLITE_MEMDEBUG)==0 -# define SQLITE_SYSTEM_MALLOC 1 -#endif - -/* -** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the -** sizes of memory allocations below this value where possible. -*/ -#if !defined(SQLITE_MALLOC_SOFT_LIMIT) -# define SQLITE_MALLOC_SOFT_LIMIT 1024 -#endif - -/* -** We need to define _XOPEN_SOURCE as follows in order to enable -** recursive mutexes on most Unix systems. But Mac OS X is different. -** The _XOPEN_SOURCE define causes problems for Mac OS X we are told, -** so it is omitted there. See ticket #2673. -** -** Later we learn that _XOPEN_SOURCE is poorly or incorrectly -** implemented on some systems. So we avoid defining it at all -** if it is already defined or if it is unneeded because we are -** not doing a threadsafe build. Ticket #2681. -** -** See also ticket #2741. -*/ -#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) \ - && !defined(__APPLE__) && SQLITE_THREADSAFE -# define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */ -#endif - -/* -** The TCL headers are only needed when compiling the TCL bindings. -*/ -#if defined(SQLITE_TCL) || defined(TCLSH) -# include -#endif - -/* -** NDEBUG and SQLITE_DEBUG are opposites. It should always be true that -** defined(NDEBUG)==!defined(SQLITE_DEBUG). If this is not currently true, -** make it true by defining or undefining NDEBUG. -** -** Setting NDEBUG makes the code smaller and run faster by disabling the -** number assert() statements in the code. So we want the default action -** to be for NDEBUG to be set and NDEBUG to be undefined only if SQLITE_DEBUG -** is set. Thus NDEBUG becomes an opt-in rather than an opt-out -** feature. -*/ -#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) -# define NDEBUG 1 -#endif -#if defined(NDEBUG) && defined(SQLITE_DEBUG) -# undef NDEBUG -#endif - -/* -** The testcase() macro is used to aid in coverage testing. When -** doing coverage testing, the condition inside the argument to -** testcase() must be evaluated both true and false in order to -** get full branch coverage. The testcase() macro is inserted -** to help ensure adequate test coverage in places where simple -** condition/decision coverage is inadequate. For example, testcase() -** can be used to make sure boundary values are tested. For -** bitmask tests, testcase() can be used to make sure each bit -** is significant and used at least once. On switch statements -** where multiple cases go to the same block of code, testcase() -** can insure that all cases are evaluated. -** -*/ -#ifdef SQLITE_COVERAGE_TEST -SQLITE_PRIVATE void sqlite3Coverage(int); -# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); } -#else -# define testcase(X) -#endif - -/* -** The TESTONLY macro is used to enclose variable declarations or -** other bits of code that are needed to support the arguments -** within testcase() and assert() macros. -*/ -#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST) -# define TESTONLY(X) X -#else -# define TESTONLY(X) -#endif - -/* -** Sometimes we need a small amount of code such as a variable initialization -** to setup for a later assert() statement. We do not want this code to -** appear when assert() is disabled. The following macro is therefore -** used to contain that setup code. The "VVA" acronym stands for -** "Verification, Validation, and Accreditation". In other words, the -** code within VVA_ONLY() will only run during verification processes. -*/ -#ifndef NDEBUG -# define VVA_ONLY(X) X -#else -# define VVA_ONLY(X) -#endif - -/* -** The ALWAYS and NEVER macros surround boolean expressions which -** are intended to always be true or false, respectively. Such -** expressions could be omitted from the code completely. But they -** are included in a few cases in order to enhance the resilience -** of SQLite to unexpected behavior - to make the code "self-healing" -** or "ductile" rather than being "brittle" and crashing at the first -** hint of unplanned behavior. -** -** In other words, ALWAYS and NEVER are added for defensive code. -** -** When doing coverage testing ALWAYS and NEVER are hard-coded to -** be true and false so that the unreachable code then specify will -** not be counted as untested code. -*/ -#if defined(SQLITE_COVERAGE_TEST) -# define ALWAYS(X) (1) -# define NEVER(X) (0) -#elif !defined(NDEBUG) -# define ALWAYS(X) ((X)?1:(assert(0),0)) -# define NEVER(X) ((X)?(assert(0),1):0) -#else -# define ALWAYS(X) (X) -# define NEVER(X) (X) -#endif - -/* -** Return true (non-zero) if the input is a integer that is too large -** to fit in 32-bits. This macro is used inside of various testcase() -** macros to verify that we have tested SQLite for large-file support. -*/ -#define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0) - -/* -** The macro unlikely() is a hint that surrounds a boolean -** expression that is usually false. Macro likely() surrounds -** a boolean expression that is usually true. GCC is able to -** use these hints to generate better code, sometimes. -*/ -#if defined(__GNUC__) && 0 -# define likely(X) __builtin_expect((X),1) -# define unlikely(X) __builtin_expect((X),0) -#else -# define likely(X) !!(X) -# define unlikely(X) !!(X) -#endif - -/************** Include sqlite3.h in the middle of sqliteInt.h ***************/ -/************** Begin file sqlite3.h *****************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface that the SQLite library -** presents to client programs. If a C-function, structure, datatype, -** or constant definition does not appear in this file, then it is -** not a published API of SQLite, is subject to change without -** notice, and should not be referenced by programs that use SQLite. -** -** Some of the definitions that are in this file are marked as -** "experimental". Experimental interfaces are normally new -** features recently added to SQLite. We do not anticipate changes -** to experimental interfaces but reserve the right to make minor changes -** if experience from use "in the wild" suggest such changes are prudent. -** -** The official C-language API documentation for SQLite is derived -** from comments in this file. This file is the authoritative source -** on how SQLite interfaces are suppose to operate. -** -** The name of this file under configuration management is "sqlite.h.in". -** The makefile makes some minor changes to this file (such as inserting -** the version number) and changes its name to "sqlite3.h" as -** part of the build process. -*/ -#ifndef _SQLITE3_H_ -#define _SQLITE3_H_ -#include /* Needed for the definition of va_list */ - -/* -** Make sure we can call this stuff from C++. -*/ -#if 0 -extern "C" { -#endif - - -/* -** Add the ability to override 'extern' -*/ -#ifndef SQLITE_EXTERN -# define SQLITE_EXTERN extern -#endif - -#ifndef SQLITE_API -# define SQLITE_API -#endif - - -/* -** These no-op macros are used in front of interfaces to mark those -** interfaces as either deprecated or experimental. New applications -** should not use deprecated interfaces - they are support for backwards -** compatibility only. Application writers should be aware that -** experimental interfaces are subject to change in point releases. -** -** These macros used to resolve to various kinds of compiler magic that -** would generate warning messages when they were used. But that -** compiler magic ended up generating such a flurry of bug reports -** that we have taken it all out and gone back to using simple -** noop macros. -*/ -#define SQLITE_DEPRECATED -#define SQLITE_EXPERIMENTAL - -/* -** Ensure these symbols were not defined by some previous header file. -*/ -#ifdef SQLITE_VERSION -# undef SQLITE_VERSION -#endif -#ifdef SQLITE_VERSION_NUMBER -# undef SQLITE_VERSION_NUMBER -#endif - -/* -** CAPI3REF: Compile-Time Library Version Numbers -** -** ^(The [SQLITE_VERSION] C preprocessor macro in the sqlite3.h header -** evaluates to a string literal that is the SQLite version in the -** format "X.Y.Z" where X is the major version number (always 3 for -** SQLite3) and Y is the minor version number and Z is the release number.)^ -** ^(The [SQLITE_VERSION_NUMBER] C preprocessor macro resolves to an integer -** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same -** numbers used in [SQLITE_VERSION].)^ -** The SQLITE_VERSION_NUMBER for any given release of SQLite will also -** be larger than the release from which it is derived. Either Y will -** be held constant and Z will be incremented or else Y will be incremented -** and Z will be reset to zero. -** -** Since version 3.6.18, SQLite source code has been stored in the -** Fossil configuration management -** system. ^The SQLITE_SOURCE_ID macro evaluates to -** a string which identifies a particular check-in of SQLite -** within its configuration management system. ^The SQLITE_SOURCE_ID -** string contains the date and time of the check-in (UTC) and an SHA1 -** hash of the entire source tree. -** -** See also: [sqlite3_libversion()], -** [sqlite3_libversion_number()], [sqlite3_sourceid()], -** [sqlite_version()] and [sqlite_source_id()]. -*/ -#define SQLITE_VERSION "3.7.16.1" -#define SQLITE_VERSION_NUMBER 3007016 -#define SQLITE_SOURCE_ID "2013-03-29 13:44:34 527231bc67285f01fb18d4451b28f61da3c4e39d" - -/* -** CAPI3REF: Run-Time Library Version Numbers -** KEYWORDS: sqlite3_version, sqlite3_sourceid -** -** These interfaces provide the same information as the [SQLITE_VERSION], -** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros -** but are associated with the library instead of the header file. ^(Cautious -** programmers might include assert() statements in their application to -** verify that values returned by these interfaces match the macros in -** the header, and thus insure that the application is -** compiled with matching library and header files. -** -**
-** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
-** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );
-** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
-** 
)^ -** -** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION] -** macro. ^The sqlite3_libversion() function returns a pointer to the -** to the sqlite3_version[] string constant. The sqlite3_libversion() -** function is provided for use in DLLs since DLL users usually do not have -** direct access to string constants within the DLL. ^The -** sqlite3_libversion_number() function returns an integer equal to -** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns -** a pointer to a string constant whose value is the same as the -** [SQLITE_SOURCE_ID] C preprocessor macro. -** -** See also: [sqlite_version()] and [sqlite_source_id()]. -*/ -SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; -SQLITE_API const char *sqlite3_libversion(void); -SQLITE_API const char *sqlite3_sourceid(void); -SQLITE_API int sqlite3_libversion_number(void); - -/* -** CAPI3REF: Run-Time Library Compilation Options Diagnostics -** -** ^The sqlite3_compileoption_used() function returns 0 or 1 -** indicating whether the specified option was defined at -** compile time. ^The SQLITE_ prefix may be omitted from the -** option name passed to sqlite3_compileoption_used(). -** -** ^The sqlite3_compileoption_get() function allows iterating -** over the list of options that were defined at compile time by -** returning the N-th compile time option string. ^If N is out of range, -** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ -** prefix is omitted from any strings returned by -** sqlite3_compileoption_get(). -** -** ^Support for the diagnostic functions sqlite3_compileoption_used() -** and sqlite3_compileoption_get() may be omitted by specifying the -** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. -** -** See also: SQL functions [sqlite_compileoption_used()] and -** [sqlite_compileoption_get()] and the [compile_options pragma]. -*/ -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS -SQLITE_API int sqlite3_compileoption_used(const char *zOptName); -SQLITE_API const char *sqlite3_compileoption_get(int N); -#endif - -/* -** CAPI3REF: Test To See If The Library Is Threadsafe -** -** ^The sqlite3_threadsafe() function returns zero if and only if -** SQLite was compiled with mutexing code omitted due to the -** [SQLITE_THREADSAFE] compile-time option being set to 0. -** -** SQLite can be compiled with or without mutexes. When -** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes -** are enabled and SQLite is threadsafe. When the -** [SQLITE_THREADSAFE] macro is 0, -** the mutexes are omitted. Without the mutexes, it is not safe -** to use SQLite concurrently from more than one thread. -** -** Enabling mutexes incurs a measurable performance penalty. -** So if speed is of utmost importance, it makes sense to disable -** the mutexes. But for maximum safety, mutexes should be enabled. -** ^The default behavior is for mutexes to be enabled. -** -** This interface can be used by an application to make sure that the -** version of SQLite that it is linking against was compiled with -** the desired setting of the [SQLITE_THREADSAFE] macro. -** -** This interface only reports on the compile-time mutex setting -** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with -** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but -** can be fully or partially disabled using a call to [sqlite3_config()] -** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], -** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the -** sqlite3_threadsafe() function shows only the compile-time setting of -** thread safety, not any run-time changes to that setting made by -** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() -** is unchanged by calls to sqlite3_config().)^ -** -** See the [threading mode] documentation for additional information. -*/ -SQLITE_API int sqlite3_threadsafe(void); - -/* -** CAPI3REF: Database Connection Handle -** KEYWORDS: {database connection} {database connections} -** -** Each open SQLite database is represented by a pointer to an instance of -** the opaque structure named "sqlite3". It is useful to think of an sqlite3 -** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and -** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()] -** and [sqlite3_close_v2()] are its destructors. There are many other -** interfaces (such as -** [sqlite3_prepare_v2()], [sqlite3_create_function()], and -** [sqlite3_busy_timeout()] to name but three) that are methods on an -** sqlite3 object. -*/ -typedef struct sqlite3 sqlite3; - -/* -** CAPI3REF: 64-Bit Integer Types -** KEYWORDS: sqlite_int64 sqlite_uint64 -** -** Because there is no cross-platform way to specify 64-bit integer types -** SQLite includes typedefs for 64-bit signed and unsigned integers. -** -** The sqlite3_int64 and sqlite3_uint64 are the preferred type definitions. -** The sqlite_int64 and sqlite_uint64 types are supported for backwards -** compatibility only. -** -** ^The sqlite3_int64 and sqlite_int64 types can store integer values -** between -9223372036854775808 and +9223372036854775807 inclusive. ^The -** sqlite3_uint64 and sqlite_uint64 types can store integer values -** between 0 and +18446744073709551615 inclusive. -*/ -#ifdef SQLITE_INT64_TYPE - typedef SQLITE_INT64_TYPE sqlite_int64; - typedef unsigned SQLITE_INT64_TYPE sqlite_uint64; -#elif defined(_MSC_VER) || defined(__BORLANDC__) - typedef __int64 sqlite_int64; - typedef unsigned __int64 sqlite_uint64; -#else - typedef long long int sqlite_int64; - typedef unsigned long long int sqlite_uint64; -#endif -typedef sqlite_int64 sqlite3_int64; -typedef sqlite_uint64 sqlite3_uint64; - -/* -** If compiling for a processor that lacks floating point support, -** substitute integer for floating-point. -*/ -#ifdef SQLITE_OMIT_FLOATING_POINT -# define double sqlite3_int64 -#endif - -/* -** CAPI3REF: Closing A Database Connection -** -** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors -** for the [sqlite3] object. -** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if -** the [sqlite3] object is successfully destroyed and all associated -** resources are deallocated. -** -** ^If the database connection is associated with unfinalized prepared -** statements or unfinished sqlite3_backup objects then sqlite3_close() -** will leave the database connection open and return [SQLITE_BUSY]. -** ^If sqlite3_close_v2() is called with unfinalized prepared statements -** and unfinished sqlite3_backups, then the database connection becomes -** an unusable "zombie" which will automatically be deallocated when the -** last prepared statement is finalized or the last sqlite3_backup is -** finished. The sqlite3_close_v2() interface is intended for use with -** host languages that are garbage collected, and where the order in which -** destructors are called is arbitrary. -** -** Applications should [sqlite3_finalize | finalize] all [prepared statements], -** [sqlite3_blob_close | close] all [BLOB handles], and -** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated -** with the [sqlite3] object prior to attempting to close the object. ^If -** sqlite3_close_v2() is called on a [database connection] that still has -** outstanding [prepared statements], [BLOB handles], and/or -** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation -** of resources is deferred until all [prepared statements], [BLOB handles], -** and [sqlite3_backup] objects are also destroyed. -** -** ^If an [sqlite3] object is destroyed while a transaction is open, -** the transaction is automatically rolled back. -** -** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] -** must be either a NULL -** pointer or an [sqlite3] object pointer obtained -** from [sqlite3_open()], [sqlite3_open16()], or -** [sqlite3_open_v2()], and not previously closed. -** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer -** argument is a harmless no-op. -*/ -SQLITE_API int sqlite3_close(sqlite3*); -SQLITE_API int sqlite3_close_v2(sqlite3*); - -/* -** The type for a callback function. -** This is legacy and deprecated. It is included for historical -** compatibility and is not documented. -*/ -typedef int (*sqlite3_callback)(void*,int,char**, char**); - -/* -** CAPI3REF: One-Step Query Execution Interface -** -** The sqlite3_exec() interface is a convenience wrapper around -** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], -** that allows an application to run multiple statements of SQL -** without having to use a lot of C code. -** -** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, -** semicolon-separate SQL statements passed into its 2nd argument, -** in the context of the [database connection] passed in as its 1st -** argument. ^If the callback function of the 3rd argument to -** sqlite3_exec() is not NULL, then it is invoked for each result row -** coming out of the evaluated SQL statements. ^The 4th argument to -** sqlite3_exec() is relayed through to the 1st argument of each -** callback invocation. ^If the callback pointer to sqlite3_exec() -** is NULL, then no callback is ever invoked and result rows are -** ignored. -** -** ^If an error occurs while evaluating the SQL statements passed into -** sqlite3_exec(), then execution of the current statement stops and -** subsequent statements are skipped. ^If the 5th parameter to sqlite3_exec() -** is not NULL then any error message is written into memory obtained -** from [sqlite3_malloc()] and passed back through the 5th parameter. -** To avoid memory leaks, the application should invoke [sqlite3_free()] -** on error message strings returned through the 5th parameter of -** of sqlite3_exec() after the error message string is no longer needed. -** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors -** occur, then sqlite3_exec() sets the pointer in its 5th parameter to -** NULL before returning. -** -** ^If an sqlite3_exec() callback returns non-zero, the sqlite3_exec() -** routine returns SQLITE_ABORT without invoking the callback again and -** without running any subsequent SQL statements. -** -** ^The 2nd argument to the sqlite3_exec() callback function is the -** number of columns in the result. ^The 3rd argument to the sqlite3_exec() -** callback is an array of pointers to strings obtained as if from -** [sqlite3_column_text()], one for each column. ^If an element of a -** result row is NULL then the corresponding string pointer for the -** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the -** sqlite3_exec() callback is an array of pointers to strings where each -** entry represents the name of corresponding result column as obtained -** from [sqlite3_column_name()]. -** -** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer -** to an empty string, or a pointer that contains only whitespace and/or -** SQL comments, then no SQL statements are evaluated and the database -** is not changed. -** -** Restrictions: -** -**
    -**
  • The application must insure that the 1st parameter to sqlite3_exec() -** is a valid and open [database connection]. -**
  • The application must not close [database connection] specified by -** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. -**
  • The application must not modify the SQL statement text passed into -** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. -**
-*/ -SQLITE_API int sqlite3_exec( - sqlite3*, /* An open database */ - const char *sql, /* SQL to be evaluated */ - int (*callback)(void*,int,char**,char**), /* Callback function */ - void *, /* 1st argument to callback */ - char **errmsg /* Error msg written here */ -); - -/* -** CAPI3REF: Result Codes -** KEYWORDS: SQLITE_OK {error code} {error codes} -** KEYWORDS: {result code} {result codes} -** -** Many SQLite functions return an integer result code from the set shown -** here in order to indicate success or failure. -** -** New error codes may be added in future versions of SQLite. -** -** See also: [SQLITE_IOERR_READ | extended result codes], -** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes]. -*/ -#define SQLITE_OK 0 /* Successful result */ -/* beginning-of-error-codes */ -#define SQLITE_ERROR 1 /* SQL error or missing database */ -#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ -#define SQLITE_PERM 3 /* Access permission denied */ -#define SQLITE_ABORT 4 /* Callback routine requested an abort */ -#define SQLITE_BUSY 5 /* The database file is locked */ -#define SQLITE_LOCKED 6 /* A table in the database is locked */ -#define SQLITE_NOMEM 7 /* A malloc() failed */ -#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ -#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ -#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ -#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ -#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ -#define SQLITE_FULL 13 /* Insertion failed because database is full */ -#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ -#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ -#define SQLITE_EMPTY 16 /* Database is empty */ -#define SQLITE_SCHEMA 17 /* The database schema changed */ -#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ -#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ -#define SQLITE_MISMATCH 20 /* Data type mismatch */ -#define SQLITE_MISUSE 21 /* Library used incorrectly */ -#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ -#define SQLITE_AUTH 23 /* Authorization denied */ -#define SQLITE_FORMAT 24 /* Auxiliary database format error */ -#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ -#define SQLITE_NOTADB 26 /* File opened that is not a database file */ -#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ -#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ -/* end-of-error-codes */ - -/* -** CAPI3REF: Extended Result Codes -** KEYWORDS: {extended error code} {extended error codes} -** KEYWORDS: {extended result code} {extended result codes} -** -** In its default configuration, SQLite API routines return one of 26 integer -** [SQLITE_OK | result codes]. However, experience has shown that many of -** these result codes are too coarse-grained. They do not provide as -** much information about problems as programmers might like. In an effort to -** address this, newer versions of SQLite (version 3.3.8 and later) include -** support for additional result codes that provide more detailed information -** about errors. The extended result codes are enabled or disabled -** on a per database connection basis using the -** [sqlite3_extended_result_codes()] API. -** -** Some of the available extended result codes are listed here. -** One may expect the number of extended result codes will be expand -** over time. Software that uses extended result codes should expect -** to see new result codes in future releases of SQLite. -** -** The SQLITE_OK result code will never be extended. It will always -** be exactly zero. -*/ -#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) -#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) -#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) -#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) -#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) -#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) -#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) -#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8)) -#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8)) -#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8)) -#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8)) -#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8)) -#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) -#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) -#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) -#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) -#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) -#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) -#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) -#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) -#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) -#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) -#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) -#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) -#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) -#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) -#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) -#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) -#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) -#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) -#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) -#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) -#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) -#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8)) -#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8)) -#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8)) -#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8)) -#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) -#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) -#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) -#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) -#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) - -/* -** CAPI3REF: Flags For File Open Operations -** -** These bit values are intended for use in the -** 3rd parameter to the [sqlite3_open_v2()] interface and -** in the 4th parameter to the [sqlite3_vfs.xOpen] method. -*/ -#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ -#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ -#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */ -#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_MEMORY 0x00000080 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */ -#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */ -#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */ -#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800 /* VFS only */ -#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */ -#define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */ -#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ -#define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ - -/* Reserved: 0x00F00000 */ - -/* -** CAPI3REF: Device Characteristics -** -** The xDeviceCharacteristics method of the [sqlite3_io_methods] -** object returns an integer which is a vector of these -** bit values expressing I/O characteristics of the mass storage -** device that holds the file that the [sqlite3_io_methods] -** refers to. -** -** The SQLITE_IOCAP_ATOMIC property means that all writes of -** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values -** mean that writes of blocks that are nnn bytes in size and -** are aligned to an address which is an integer multiple of -** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means -** that when data is appended to a file, the data is appended -** first then the size of the file is extended, never the other -** way around. The SQLITE_IOCAP_SEQUENTIAL property means that -** information is written to disk in the same order as calls -** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that -** after reboot following a crash or power loss, the only bytes in a -** file that were written at the application level might have changed -** and that adjacent bytes, even bytes within the same sector are -** guaranteed to be unchanged. -*/ -#define SQLITE_IOCAP_ATOMIC 0x00000001 -#define SQLITE_IOCAP_ATOMIC512 0x00000002 -#define SQLITE_IOCAP_ATOMIC1K 0x00000004 -#define SQLITE_IOCAP_ATOMIC2K 0x00000008 -#define SQLITE_IOCAP_ATOMIC4K 0x00000010 -#define SQLITE_IOCAP_ATOMIC8K 0x00000020 -#define SQLITE_IOCAP_ATOMIC16K 0x00000040 -#define SQLITE_IOCAP_ATOMIC32K 0x00000080 -#define SQLITE_IOCAP_ATOMIC64K 0x00000100 -#define SQLITE_IOCAP_SAFE_APPEND 0x00000200 -#define SQLITE_IOCAP_SEQUENTIAL 0x00000400 -#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 -#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 - -/* -** CAPI3REF: File Locking Levels -** -** SQLite uses one of these integer values as the second -** argument to calls it makes to the xLock() and xUnlock() methods -** of an [sqlite3_io_methods] object. -*/ -#define SQLITE_LOCK_NONE 0 -#define SQLITE_LOCK_SHARED 1 -#define SQLITE_LOCK_RESERVED 2 -#define SQLITE_LOCK_PENDING 3 -#define SQLITE_LOCK_EXCLUSIVE 4 - -/* -** CAPI3REF: Synchronization Type Flags -** -** When SQLite invokes the xSync() method of an -** [sqlite3_io_methods] object it uses a combination of -** these integer values as the second argument. -** -** When the SQLITE_SYNC_DATAONLY flag is used, it means that the -** sync operation only needs to flush data to mass storage. Inode -** information need not be flushed. If the lower four bits of the flag -** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics. -** If the lower four bits equal SQLITE_SYNC_FULL, that means -** to use Mac OS X style fullsync instead of fsync(). -** -** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags -** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL -** settings. The [synchronous pragma] determines when calls to the -** xSync VFS method occur and applies uniformly across all platforms. -** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how -** energetic or rigorous or forceful the sync operations are and -** only make a difference on Mac OSX for the default SQLite code. -** (Third-party VFS implementations might also make the distinction -** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the -** operating systems natively supported by SQLite, only Mac OSX -** cares about the difference.) -*/ -#define SQLITE_SYNC_NORMAL 0x00002 -#define SQLITE_SYNC_FULL 0x00003 -#define SQLITE_SYNC_DATAONLY 0x00010 - -/* -** CAPI3REF: OS Interface Open File Handle -** -** An [sqlite3_file] object represents an open file in the -** [sqlite3_vfs | OS interface layer]. Individual OS interface -** implementations will -** want to subclass this object by appending additional fields -** for their own use. The pMethods entry is a pointer to an -** [sqlite3_io_methods] object that defines methods for performing -** I/O operations on the open file. -*/ -typedef struct sqlite3_file sqlite3_file; -struct sqlite3_file { - const struct sqlite3_io_methods *pMethods; /* Methods for an open file */ -}; - -/* -** CAPI3REF: OS Interface File Virtual Methods Object -** -** Every file opened by the [sqlite3_vfs.xOpen] method populates an -** [sqlite3_file] object (or, more commonly, a subclass of the -** [sqlite3_file] object) with a pointer to an instance of this object. -** This object defines the methods used to perform various operations -** against the open file represented by the [sqlite3_file] object. -** -** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element -** to a non-NULL pointer, then the sqlite3_io_methods.xClose method -** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The -** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen] -** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element -** to NULL. -** -** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or -** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). -** The second choice is a Mac OS X style fullsync. The [SQLITE_SYNC_DATAONLY] -** flag may be ORed in to indicate that only the data of the file -** and not its inode needs to be synced. -** -** The integer values to xLock() and xUnlock() are one of -**
    -**
  • [SQLITE_LOCK_NONE], -**
  • [SQLITE_LOCK_SHARED], -**
  • [SQLITE_LOCK_RESERVED], -**
  • [SQLITE_LOCK_PENDING], or -**
  • [SQLITE_LOCK_EXCLUSIVE]. -**
-** xLock() increases the lock. xUnlock() decreases the lock. -** The xCheckReservedLock() method checks whether any database connection, -** either in this process or in some other process, is holding a RESERVED, -** PENDING, or EXCLUSIVE lock on the file. It returns true -** if such a lock exists and false otherwise. -** -** The xFileControl() method is a generic interface that allows custom -** VFS implementations to directly control an open file using the -** [sqlite3_file_control()] interface. The second "op" argument is an -** integer opcode. The third argument is a generic pointer intended to -** point to a structure that may contain arguments or space in which to -** write return values. Potential uses for xFileControl() might be -** functions to enable blocking locks with timeouts, to change the -** locking strategy (for example to use dot-file locks), to inquire -** about the status of a lock, or to break stale locks. The SQLite -** core reserves all opcodes less than 100 for its own use. -** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available. -** Applications that define a custom xFileControl method should use opcodes -** greater than 100 to avoid conflicts. VFS implementations should -** return [SQLITE_NOTFOUND] for file control opcodes that they do not -** recognize. -** -** The xSectorSize() method returns the sector size of the -** device that underlies the file. The sector size is the -** minimum write that can be performed without disturbing -** other bytes in the file. The xDeviceCharacteristics() -** method returns a bit vector describing behaviors of the -** underlying device: -** -**
    -**
  • [SQLITE_IOCAP_ATOMIC] -**
  • [SQLITE_IOCAP_ATOMIC512] -**
  • [SQLITE_IOCAP_ATOMIC1K] -**
  • [SQLITE_IOCAP_ATOMIC2K] -**
  • [SQLITE_IOCAP_ATOMIC4K] -**
  • [SQLITE_IOCAP_ATOMIC8K] -**
  • [SQLITE_IOCAP_ATOMIC16K] -**
  • [SQLITE_IOCAP_ATOMIC32K] -**
  • [SQLITE_IOCAP_ATOMIC64K] -**
  • [SQLITE_IOCAP_SAFE_APPEND] -**
  • [SQLITE_IOCAP_SEQUENTIAL] -**
-** -** The SQLITE_IOCAP_ATOMIC property means that all writes of -** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values -** mean that writes of blocks that are nnn bytes in size and -** are aligned to an address which is an integer multiple of -** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means -** that when data is appended to a file, the data is appended -** first then the size of the file is extended, never the other -** way around. The SQLITE_IOCAP_SEQUENTIAL property means that -** information is written to disk in the same order as calls -** to xWrite(). -** -** If xRead() returns SQLITE_IOERR_SHORT_READ it must also fill -** in the unread portions of the buffer with zeros. A VFS that -** fails to zero-fill short reads might seem to work. However, -** failure to zero-fill short reads will eventually lead to -** database corruption. -*/ -typedef struct sqlite3_io_methods sqlite3_io_methods; -struct sqlite3_io_methods { - int iVersion; - int (*xClose)(sqlite3_file*); - int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); - int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst); - int (*xTruncate)(sqlite3_file*, sqlite3_int64 size); - int (*xSync)(sqlite3_file*, int flags); - int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize); - int (*xLock)(sqlite3_file*, int); - int (*xUnlock)(sqlite3_file*, int); - int (*xCheckReservedLock)(sqlite3_file*, int *pResOut); - int (*xFileControl)(sqlite3_file*, int op, void *pArg); - int (*xSectorSize)(sqlite3_file*); - int (*xDeviceCharacteristics)(sqlite3_file*); - /* Methods above are valid for version 1 */ - int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**); - int (*xShmLock)(sqlite3_file*, int offset, int n, int flags); - void (*xShmBarrier)(sqlite3_file*); - int (*xShmUnmap)(sqlite3_file*, int deleteFlag); - /* Methods above are valid for version 2 */ - /* Additional methods may be added in future releases */ -}; - -/* -** CAPI3REF: Standard File Control Opcodes -** -** These integer constants are opcodes for the xFileControl method -** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] -** interface. -** -** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This -** opcode causes the xFileControl method to write the current state of -** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], -** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) -** into an integer that the pArg argument points to. This capability -** is used during testing and only needs to be supported when SQLITE_TEST -** is defined. -**
    -**
  • [[SQLITE_FCNTL_SIZE_HINT]] -** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS -** layer a hint of how large the database file will grow to be during the -** current transaction. This hint is not guaranteed to be accurate but it -** is often close. The underlying VFS might choose to preallocate database -** file space based on this hint in order to help writes to the database -** file run faster. -** -**
  • [[SQLITE_FCNTL_CHUNK_SIZE]] -** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS -** extends and truncates the database file in chunks of a size specified -** by the user. The fourth argument to [sqlite3_file_control()] should -** point to an integer (type int) containing the new chunk-size to use -** for the nominated database. Allocating database file space in large -** chunks (say 1MB at a time), may reduce file-system fragmentation and -** improve performance on some systems. -** -**
  • [[SQLITE_FCNTL_FILE_POINTER]] -** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer -** to the [sqlite3_file] object associated with a particular database -** connection. See the [sqlite3_file_control()] documentation for -** additional information. -** -**
  • [[SQLITE_FCNTL_SYNC_OMITTED]] -** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by -** SQLite and sent to all VFSes in place of a call to the xSync method -** when the database connection has [PRAGMA synchronous] set to OFF.)^ -** Some specialized VFSes need this signal in order to operate correctly -** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most -** VFSes do not need this signal and should silently ignore this opcode. -** Applications should not call [sqlite3_file_control()] with this -** opcode as doing so may disrupt the operation of the specialized VFSes -** that do require it. -** -**
  • [[SQLITE_FCNTL_WIN32_AV_RETRY]] -** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic -** retry counts and intervals for certain disk I/O operations for the -** windows [VFS] in order to provide robustness in the presence of -** anti-virus programs. By default, the windows VFS will retry file read, -** file write, and file delete operations up to 10 times, with a delay -** of 25 milliseconds before the first retry and with the delay increasing -** by an additional 25 milliseconds with each subsequent retry. This -** opcode allows these two values (10 retries and 25 milliseconds of delay) -** to be adjusted. The values are changed for all database connections -** within the same process. The argument is a pointer to an array of two -** integers where the first integer i the new retry count and the second -** integer is the delay. If either integer is negative, then the setting -** is not changed but instead the prior value of that setting is written -** into the array entry, allowing the current retry settings to be -** interrogated. The zDbName parameter is ignored. -** -**
  • [[SQLITE_FCNTL_PERSIST_WAL]] -** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the -** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary -** write ahead log and shared memory files used for transaction control -** are automatically deleted when the latest connection to the database -** closes. Setting persistent WAL mode causes those files to persist after -** close. Persisting the files is useful when other processes that do not -** have write permission on the directory containing the database file want -** to read the database file, as the WAL and shared memory files must exist -** in order for the database to be readable. The fourth parameter to -** [sqlite3_file_control()] for this opcode should be a pointer to an integer. -** That integer is 0 to disable persistent WAL mode or 1 to enable persistent -** WAL mode. If the integer is -1, then it is overwritten with the current -** WAL persistence setting. -** -**
  • [[SQLITE_FCNTL_POWERSAFE_OVERWRITE]] -** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the -** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting -** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the -** xDeviceCharacteristics methods. The fourth parameter to -** [sqlite3_file_control()] for this opcode should be a pointer to an integer. -** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage -** mode. If the integer is -1, then it is overwritten with the current -** zero-damage mode setting. -** -**
  • [[SQLITE_FCNTL_OVERWRITE]] -** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening -** a write transaction to indicate that, unless it is rolled back for some -** reason, the entire database file will be overwritten by the current -** transaction. This is used by VACUUM operations. -** -**
  • [[SQLITE_FCNTL_VFSNAME]] -** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of -** all [VFSes] in the VFS stack. The names are of all VFS shims and the -** final bottom-level VFS are written into memory obtained from -** [sqlite3_malloc()] and the result is stored in the char* variable -** that the fourth parameter of [sqlite3_file_control()] points to. -** The caller is responsible for freeing the memory when done. As with -** all file-control actions, there is no guarantee that this will actually -** do anything. Callers should initialize the char* variable to a NULL -** pointer in case this file-control is not implemented. This file-control -** is intended for diagnostic use only. -** -**
  • [[SQLITE_FCNTL_PRAGMA]] -** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA] -** file control is sent to the open [sqlite3_file] object corresponding -** to the database file to which the pragma statement refers. ^The argument -** to the [SQLITE_FCNTL_PRAGMA] file control is an array of -** pointers to strings (char**) in which the second element of the array -** is the name of the pragma and the third element is the argument to the -** pragma or NULL if the pragma has no argument. ^The handler for an -** [SQLITE_FCNTL_PRAGMA] file control can optionally make the first element -** of the char** argument point to a string obtained from [sqlite3_mprintf()] -** or the equivalent and that string will become the result of the pragma or -** the error message if the pragma fails. ^If the -** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal -** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA] -** file control returns [SQLITE_OK], then the parser assumes that the -** VFS has handled the PRAGMA itself and the parser generates a no-op -** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns -** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means -** that the VFS encountered an error while handling the [PRAGMA] and the -** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] -** file control occurs at the beginning of pragma statement analysis and so -** it is able to override built-in [PRAGMA] statements. -** -**
  • [[SQLITE_FCNTL_BUSYHANDLER]] -** ^This file-control may be invoked by SQLite on the database file handle -** shortly after it is opened in order to provide a custom VFS with access -** to the connections busy-handler callback. The argument is of type (void **) -** - an array of two (void *) values. The first (void *) actually points -** to a function of type (int (*)(void *)). In order to invoke the connections -** busy-handler, this function should be invoked with the second (void *) in -** the array as the only argument. If it returns non-zero, then the operation -** should be retried. If it returns zero, the custom VFS should abandon the -** current operation. -** -**
  • [[SQLITE_FCNTL_TEMPFILENAME]] -** ^Application can invoke this file-control to have SQLite generate a -** temporary filename using the same algorithm that is followed to generate -** temporary filenames for TEMP tables and other internal uses. The -** argument should be a char** which will be filled with the filename -** written into memory obtained from [sqlite3_malloc()]. The caller should -** invoke [sqlite3_free()] on the result to avoid a memory leak. -** -**
-*/ -#define SQLITE_FCNTL_LOCKSTATE 1 -#define SQLITE_GET_LOCKPROXYFILE 2 -#define SQLITE_SET_LOCKPROXYFILE 3 -#define SQLITE_LAST_ERRNO 4 -#define SQLITE_FCNTL_SIZE_HINT 5 -#define SQLITE_FCNTL_CHUNK_SIZE 6 -#define SQLITE_FCNTL_FILE_POINTER 7 -#define SQLITE_FCNTL_SYNC_OMITTED 8 -#define SQLITE_FCNTL_WIN32_AV_RETRY 9 -#define SQLITE_FCNTL_PERSIST_WAL 10 -#define SQLITE_FCNTL_OVERWRITE 11 -#define SQLITE_FCNTL_VFSNAME 12 -#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 -#define SQLITE_FCNTL_PRAGMA 14 -#define SQLITE_FCNTL_BUSYHANDLER 15 -#define SQLITE_FCNTL_TEMPFILENAME 16 - -/* -** CAPI3REF: Mutex Handle -** -** The mutex module within SQLite defines [sqlite3_mutex] to be an -** abstract type for a mutex object. The SQLite core never looks -** at the internal representation of an [sqlite3_mutex]. It only -** deals with pointers to the [sqlite3_mutex] object. -** -** Mutexes are created using [sqlite3_mutex_alloc()]. -*/ -typedef struct sqlite3_mutex sqlite3_mutex; - -/* -** CAPI3REF: OS Interface Object -** -** An instance of the sqlite3_vfs object defines the interface between -** the SQLite core and the underlying operating system. The "vfs" -** in the name of the object stands for "virtual file system". See -** the [VFS | VFS documentation] for further information. -** -** The value of the iVersion field is initially 1 but may be larger in -** future versions of SQLite. Additional fields may be appended to this -** object when the iVersion value is increased. Note that the structure -** of the sqlite3_vfs object changes in the transaction between -** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not -** modified. -** -** The szOsFile field is the size of the subclassed [sqlite3_file] -** structure used by this VFS. mxPathname is the maximum length of -** a pathname in this VFS. -** -** Registered sqlite3_vfs objects are kept on a linked list formed by -** the pNext pointer. The [sqlite3_vfs_register()] -** and [sqlite3_vfs_unregister()] interfaces manage this list -** in a thread-safe way. The [sqlite3_vfs_find()] interface -** searches the list. Neither the application code nor the VFS -** implementation should use the pNext pointer. -** -** The pNext field is the only field in the sqlite3_vfs -** structure that SQLite will ever modify. SQLite will only access -** or modify this field while holding a particular static mutex. -** The application should never modify anything within the sqlite3_vfs -** object once the object has been registered. -** -** The zName field holds the name of the VFS module. The name must -** be unique across all VFS modules. -** -** [[sqlite3_vfs.xOpen]] -** ^SQLite guarantees that the zFilename parameter to xOpen -** is either a NULL pointer or string obtained -** from xFullPathname() with an optional suffix added. -** ^If a suffix is added to the zFilename parameter, it will -** consist of a single "-" character followed by no more than -** 11 alphanumeric and/or "-" characters. -** ^SQLite further guarantees that -** the string will be valid and unchanged until xClose() is -** called. Because of the previous sentence, -** the [sqlite3_file] can safely store a pointer to the -** filename if it needs to remember the filename for some reason. -** If the zFilename parameter to xOpen is a NULL pointer then xOpen -** must invent its own temporary name for the file. ^Whenever the -** xFilename parameter is NULL it will also be the case that the -** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. -** -** The flags argument to xOpen() includes all bits set in -** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] -** or [sqlite3_open16()] is used, then flags includes at least -** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. -** If xOpen() opens a file read-only then it sets *pOutFlags to -** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. -** -** ^(SQLite will also add one of the following flags to the xOpen() -** call, depending on the object being opened: -** -**
    -**
  • [SQLITE_OPEN_MAIN_DB] -**
  • [SQLITE_OPEN_MAIN_JOURNAL] -**
  • [SQLITE_OPEN_TEMP_DB] -**
  • [SQLITE_OPEN_TEMP_JOURNAL] -**
  • [SQLITE_OPEN_TRANSIENT_DB] -**
  • [SQLITE_OPEN_SUBJOURNAL] -**
  • [SQLITE_OPEN_MASTER_JOURNAL] -**
  • [SQLITE_OPEN_WAL] -**
)^ -** -** The file I/O implementation can use the object type flags to -** change the way it deals with files. For example, an application -** that does not care about crash recovery or rollback might make -** the open of a journal file a no-op. Writes to this journal would -** also be no-ops, and any attempt to read the journal would return -** SQLITE_IOERR. Or the implementation might recognize that a database -** file will be doing page-aligned sector reads and writes in a random -** order and set up its I/O subsystem accordingly. -** -** SQLite might also add one of the following flags to the xOpen method: -** -**
    -**
  • [SQLITE_OPEN_DELETEONCLOSE] -**
  • [SQLITE_OPEN_EXCLUSIVE] -**
-** -** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be -** deleted when it is closed. ^The [SQLITE_OPEN_DELETEONCLOSE] -** will be set for TEMP databases and their journals, transient -** databases, and subjournals. -** -** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction -** with the [SQLITE_OPEN_CREATE] flag, which are both directly -** analogous to the O_EXCL and O_CREAT flags of the POSIX open() -** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the -** SQLITE_OPEN_CREATE, is used to indicate that file should always -** be created, and that it is an error if it already exists. -** It is not used to indicate the file should be opened -** for exclusive access. -** -** ^At least szOsFile bytes of memory are allocated by SQLite -** to hold the [sqlite3_file] structure passed as the third -** argument to xOpen. The xOpen method does not have to -** allocate the structure; it should just fill it in. Note that -** the xOpen method must set the sqlite3_file.pMethods to either -** a valid [sqlite3_io_methods] object or to NULL. xOpen must do -** this even if the open fails. SQLite expects that the sqlite3_file.pMethods -** element will be valid after xOpen returns regardless of the success -** or failure of the xOpen call. -** -** [[sqlite3_vfs.xAccess]] -** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] -** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to -** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] -** to test whether a file is at least readable. The file can be a -** directory. -** -** ^SQLite will always allocate at least mxPathname+1 bytes for the -** output buffer xFullPathname. The exact size of the output buffer -** is also passed as a parameter to both methods. If the output buffer -** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is -** handled as a fatal error by SQLite, vfs implementations should endeavor -** to prevent this by setting mxPathname to a sufficiently large value. -** -** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() -** interfaces are not strictly a part of the filesystem, but they are -** included in the VFS structure for completeness. -** The xRandomness() function attempts to return nBytes bytes -** of good-quality randomness into zOut. The return value is -** the actual number of bytes of randomness obtained. -** The xSleep() method causes the calling thread to sleep for at -** least the number of microseconds given. ^The xCurrentTime() -** method returns a Julian Day Number for the current date and time as -** a floating point value. -** ^The xCurrentTimeInt64() method returns, as an integer, the Julian -** Day Number multiplied by 86400000 (the number of milliseconds in -** a 24-hour day). -** ^SQLite will use the xCurrentTimeInt64() method to get the current -** date and time if that method is available (if iVersion is 2 or -** greater and the function pointer is not NULL) and will fall back -** to xCurrentTime() if xCurrentTimeInt64() is unavailable. -** -** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces -** are not used by the SQLite core. These optional interfaces are provided -** by some VFSes to facilitate testing of the VFS code. By overriding -** system calls with functions under its control, a test program can -** simulate faults and error conditions that would otherwise be difficult -** or impossible to induce. The set of system calls that can be overridden -** varies from one VFS to another, and from one version of the same VFS to the -** next. Applications that use these interfaces must be prepared for any -** or all of these interfaces to be NULL or for their behavior to change -** from one release to the next. Applications must not attempt to access -** any of these methods if the iVersion of the VFS is less than 3. -*/ -typedef struct sqlite3_vfs sqlite3_vfs; -typedef void (*sqlite3_syscall_ptr)(void); -struct sqlite3_vfs { - int iVersion; /* Structure version number (currently 3) */ - int szOsFile; /* Size of subclassed sqlite3_file */ - int mxPathname; /* Maximum file pathname length */ - sqlite3_vfs *pNext; /* Next registered VFS */ - const char *zName; /* Name of this virtual file system */ - void *pAppData; /* Pointer to application-specific data */ - int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, - int flags, int *pOutFlags); - int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); - int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); - int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut); - void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename); - void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg); - void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void); - void (*xDlClose)(sqlite3_vfs*, void*); - int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut); - int (*xSleep)(sqlite3_vfs*, int microseconds); - int (*xCurrentTime)(sqlite3_vfs*, double*); - int (*xGetLastError)(sqlite3_vfs*, int, char *); - /* - ** The methods above are in version 1 of the sqlite_vfs object - ** definition. Those that follow are added in version 2 or later - */ - int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); - /* - ** The methods above are in versions 1 and 2 of the sqlite_vfs object. - ** Those below are for version 3 and greater. - */ - int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr); - sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName); - const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName); - /* - ** The methods above are in versions 1 through 3 of the sqlite_vfs object. - ** New fields may be appended in figure versions. The iVersion - ** value will increment whenever this happens. - */ -}; - -/* -** CAPI3REF: Flags for the xAccess VFS method -** -** These integer constants can be used as the third parameter to -** the xAccess method of an [sqlite3_vfs] object. They determine -** what kind of permissions the xAccess method is looking for. -** With SQLITE_ACCESS_EXISTS, the xAccess method -** simply checks whether the file exists. -** With SQLITE_ACCESS_READWRITE, the xAccess method -** checks whether the named directory is both readable and writable -** (in other words, if files can be added, removed, and renamed within -** the directory). -** The SQLITE_ACCESS_READWRITE constant is currently used only by the -** [temp_store_directory pragma], though this could change in a future -** release of SQLite. -** With SQLITE_ACCESS_READ, the xAccess method -** checks whether the file is readable. The SQLITE_ACCESS_READ constant is -** currently unused, though it might be used in a future release of -** SQLite. -*/ -#define SQLITE_ACCESS_EXISTS 0 -#define SQLITE_ACCESS_READWRITE 1 /* Used by PRAGMA temp_store_directory */ -#define SQLITE_ACCESS_READ 2 /* Unused */ - -/* -** CAPI3REF: Flags for the xShmLock VFS method -** -** These integer constants define the various locking operations -** allowed by the xShmLock method of [sqlite3_io_methods]. The -** following are the only legal combinations of flags to the -** xShmLock method: -** -**
    -**
  • SQLITE_SHM_LOCK | SQLITE_SHM_SHARED -**
  • SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE -**
  • SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED -**
  • SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE -**
-** -** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as -** was given no the corresponding lock. -** -** The xShmLock method can transition between unlocked and SHARED or -** between unlocked and EXCLUSIVE. It cannot transition between SHARED -** and EXCLUSIVE. -*/ -#define SQLITE_SHM_UNLOCK 1 -#define SQLITE_SHM_LOCK 2 -#define SQLITE_SHM_SHARED 4 -#define SQLITE_SHM_EXCLUSIVE 8 - -/* -** CAPI3REF: Maximum xShmLock index -** -** The xShmLock method on [sqlite3_io_methods] may use values -** between 0 and this upper bound as its "offset" argument. -** The SQLite core will never attempt to acquire or release a -** lock outside of this range -*/ -#define SQLITE_SHM_NLOCK 8 - - -/* -** CAPI3REF: Initialize The SQLite Library -** -** ^The sqlite3_initialize() routine initializes the -** SQLite library. ^The sqlite3_shutdown() routine -** deallocates any resources that were allocated by sqlite3_initialize(). -** These routines are designed to aid in process initialization and -** shutdown on embedded systems. Workstation applications using -** SQLite normally do not need to invoke either of these routines. -** -** A call to sqlite3_initialize() is an "effective" call if it is -** the first time sqlite3_initialize() is invoked during the lifetime of -** the process, or if it is the first time sqlite3_initialize() is invoked -** following a call to sqlite3_shutdown(). ^(Only an effective call -** of sqlite3_initialize() does any initialization. All other calls -** are harmless no-ops.)^ -** -** A call to sqlite3_shutdown() is an "effective" call if it is the first -** call to sqlite3_shutdown() since the last sqlite3_initialize(). ^(Only -** an effective call to sqlite3_shutdown() does any deinitialization. -** All other valid calls to sqlite3_shutdown() are harmless no-ops.)^ -** -** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown() -** is not. The sqlite3_shutdown() interface must only be called from a -** single thread. All open [database connections] must be closed and all -** other SQLite resources must be deallocated prior to invoking -** sqlite3_shutdown(). -** -** Among other things, ^sqlite3_initialize() will invoke -** sqlite3_os_init(). Similarly, ^sqlite3_shutdown() -** will invoke sqlite3_os_end(). -** -** ^The sqlite3_initialize() routine returns [SQLITE_OK] on success. -** ^If for some reason, sqlite3_initialize() is unable to initialize -** the library (perhaps it is unable to allocate a needed resource such -** as a mutex) it returns an [error code] other than [SQLITE_OK]. -** -** ^The sqlite3_initialize() routine is called internally by many other -** SQLite interfaces so that an application usually does not need to -** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] -** calls sqlite3_initialize() so the SQLite library will be automatically -** initialized when [sqlite3_open()] is called if it has not be initialized -** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] -** compile-time option, then the automatic calls to sqlite3_initialize() -** are omitted and the application must call sqlite3_initialize() directly -** prior to using any other SQLite interface. For maximum portability, -** it is recommended that applications always invoke sqlite3_initialize() -** directly prior to using any other SQLite interface. Future releases -** of SQLite may require this. In other words, the behavior exhibited -** when SQLite is compiled with [SQLITE_OMIT_AUTOINIT] might become the -** default behavior in some future release of SQLite. -** -** The sqlite3_os_init() routine does operating-system specific -** initialization of the SQLite library. The sqlite3_os_end() -** routine undoes the effect of sqlite3_os_init(). Typical tasks -** performed by these routines include allocation or deallocation -** of static resources, initialization of global variables, -** setting up a default [sqlite3_vfs] module, or setting up -** a default configuration using [sqlite3_config()]. -** -** The application should never invoke either sqlite3_os_init() -** or sqlite3_os_end() directly. The application should only invoke -** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init() -** interface is called automatically by sqlite3_initialize() and -** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate -** implementations for sqlite3_os_init() and sqlite3_os_end() -** are built into SQLite when it is compiled for Unix, Windows, or OS/2. -** When [custom builds | built for other platforms] -** (using the [SQLITE_OS_OTHER=1] compile-time -** option) the application must supply a suitable implementation for -** sqlite3_os_init() and sqlite3_os_end(). An application-supplied -** implementation of sqlite3_os_init() or sqlite3_os_end() -** must return [SQLITE_OK] on success and some other [error code] upon -** failure. -*/ -SQLITE_API int sqlite3_initialize(void); -SQLITE_API int sqlite3_shutdown(void); -SQLITE_API int sqlite3_os_init(void); -SQLITE_API int sqlite3_os_end(void); - -/* -** CAPI3REF: Configuring The SQLite Library -** -** The sqlite3_config() interface is used to make global configuration -** changes to SQLite in order to tune SQLite to the specific needs of -** the application. The default configuration is recommended for most -** applications and so this routine is usually not necessary. It is -** provided to support rare applications with unusual needs. -** -** The sqlite3_config() interface is not threadsafe. The application -** must insure that no other SQLite interfaces are invoked by other -** threads while sqlite3_config() is running. Furthermore, sqlite3_config() -** may only be invoked prior to library initialization using -** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. -** ^If sqlite3_config() is called after [sqlite3_initialize()] and before -** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. -** Note, however, that ^sqlite3_config() can be called as part of the -** implementation of an application-defined [sqlite3_os_init()]. -** -** The first argument to sqlite3_config() is an integer -** [configuration option] that determines -** what property of SQLite is to be configured. Subsequent arguments -** vary depending on the [configuration option] -** in the first argument. -** -** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. -** ^If the option is unknown or SQLite is unable to set the option -** then this routine returns a non-zero [error code]. -*/ -SQLITE_API int sqlite3_config(int, ...); - -/* -** CAPI3REF: Configure database connections -** -** The sqlite3_db_config() interface is used to make configuration -** changes to a [database connection]. The interface is similar to -** [sqlite3_config()] except that the changes apply to a single -** [database connection] (specified in the first argument). -** -** The second argument to sqlite3_db_config(D,V,...) is the -** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code -** that indicates what aspect of the [database connection] is being configured. -** Subsequent arguments vary depending on the configuration verb. -** -** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if -** the call is considered successful. -*/ -SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); - -/* -** CAPI3REF: Memory Allocation Routines -** -** An instance of this object defines the interface between SQLite -** and low-level memory allocation routines. -** -** This object is used in only one place in the SQLite interface. -** A pointer to an instance of this object is the argument to -** [sqlite3_config()] when the configuration option is -** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC]. -** By creating an instance of this object -** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC]) -** during configuration, an application can specify an alternative -** memory allocation subsystem for SQLite to use for all of its -** dynamic memory needs. -** -** Note that SQLite comes with several [built-in memory allocators] -** that are perfectly adequate for the overwhelming majority of applications -** and that this object is only useful to a tiny minority of applications -** with specialized memory allocation requirements. This object is -** also used during testing of SQLite in order to specify an alternative -** memory allocator that simulates memory out-of-memory conditions in -** order to verify that SQLite recovers gracefully from such -** conditions. -** -** The xMalloc, xRealloc, and xFree methods must work like the -** malloc(), realloc() and free() functions from the standard C library. -** ^SQLite guarantees that the second argument to -** xRealloc is always a value returned by a prior call to xRoundup. -** -** xSize should return the allocated size of a memory allocation -** previously obtained from xMalloc or xRealloc. The allocated size -** is always at least as big as the requested size but may be larger. -** -** The xRoundup method returns what would be the allocated size of -** a memory allocation given a particular requested size. Most memory -** allocators round up memory allocations at least to the next multiple -** of 8. Some allocators round up to a larger multiple or to a power of 2. -** Every memory allocation request coming in through [sqlite3_malloc()] -** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, -** that causes the corresponding memory allocation to fail. -** -** The xInit method initializes the memory allocator. (For example, -** it might allocate any require mutexes or initialize internal data -** structures. The xShutdown method is invoked (indirectly) by -** [sqlite3_shutdown()] and should deallocate any resources acquired -** by xInit. The pAppData pointer is used as the only parameter to -** xInit and xShutdown. -** -** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes -** the xInit method, so the xInit method need not be threadsafe. The -** xShutdown method is only called from [sqlite3_shutdown()] so it does -** not need to be threadsafe either. For all other methods, SQLite -** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the -** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which -** it is by default) and so the methods are automatically serialized. -** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other -** methods must be threadsafe or else make their own arrangements for -** serialization. -** -** SQLite will never invoke xInit() more than once without an intervening -** call to xShutdown(). -*/ -typedef struct sqlite3_mem_methods sqlite3_mem_methods; -struct sqlite3_mem_methods { - void *(*xMalloc)(int); /* Memory allocation function */ - void (*xFree)(void*); /* Free a prior allocation */ - void *(*xRealloc)(void*,int); /* Resize an allocation */ - int (*xSize)(void*); /* Return the size of an allocation */ - int (*xRoundup)(int); /* Round up request size to allocation size */ - int (*xInit)(void*); /* Initialize the memory allocator */ - void (*xShutdown)(void*); /* Deinitialize the memory allocator */ - void *pAppData; /* Argument to xInit() and xShutdown() */ -}; - -/* -** CAPI3REF: Configuration Options -** KEYWORDS: {configuration option} -** -** These constants are the available integer configuration options that -** can be passed as the first argument to the [sqlite3_config()] interface. -** -** New configuration options may be added in future releases of SQLite. -** Existing configuration options might be discontinued. Applications -** should check the return code from [sqlite3_config()] to make sure that -** the call worked. The [sqlite3_config()] interface will return a -** non-zero [error code] if a discontinued or unsupported configuration option -** is invoked. -** -**
-** [[SQLITE_CONFIG_SINGLETHREAD]]
SQLITE_CONFIG_SINGLETHREAD
-**
There are no arguments to this option. ^This option sets the -** [threading mode] to Single-thread. In other words, it disables -** all mutexing and puts SQLite into a mode where it can only be used -** by a single thread. ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** it is not possible to change the [threading mode] from its default -** value of Single-thread and so [sqlite3_config()] will return -** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD -** configuration option.
-** -** [[SQLITE_CONFIG_MULTITHREAD]]
SQLITE_CONFIG_MULTITHREAD
-**
There are no arguments to this option. ^This option sets the -** [threading mode] to Multi-thread. In other words, it disables -** mutexing on [database connection] and [prepared statement] objects. -** The application is responsible for serializing access to -** [database connections] and [prepared statements]. But other mutexes -** are enabled so that SQLite will be safe to use in a multi-threaded -** environment as long as no two threads attempt to use the same -** [database connection] at the same time. ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** it is not possible to set the Multi-thread [threading mode] and -** [sqlite3_config()] will return [SQLITE_ERROR] if called with the -** SQLITE_CONFIG_MULTITHREAD configuration option.
-** -** [[SQLITE_CONFIG_SERIALIZED]]
SQLITE_CONFIG_SERIALIZED
-**
There are no arguments to this option. ^This option sets the -** [threading mode] to Serialized. In other words, this option enables -** all mutexes including the recursive -** mutexes on [database connection] and [prepared statement] objects. -** In this mode (which is the default when SQLite is compiled with -** [SQLITE_THREADSAFE=1]) the SQLite library will itself serialize access -** to [database connections] and [prepared statements] so that the -** application is free to use the same [database connection] or the -** same [prepared statement] in different threads at the same time. -** ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** it is not possible to set the Serialized [threading mode] and -** [sqlite3_config()] will return [SQLITE_ERROR] if called with the -** SQLITE_CONFIG_SERIALIZED configuration option.
-** -** [[SQLITE_CONFIG_MALLOC]]
SQLITE_CONFIG_MALLOC
-**
^(This option takes a single argument which is a pointer to an -** instance of the [sqlite3_mem_methods] structure. The argument specifies -** alternative low-level memory allocation routines to be used in place of -** the memory allocation routines built into SQLite.)^ ^SQLite makes -** its own private copy of the content of the [sqlite3_mem_methods] structure -** before the [sqlite3_config()] call returns.
-** -** [[SQLITE_CONFIG_GETMALLOC]]
SQLITE_CONFIG_GETMALLOC
-**
^(This option takes a single argument which is a pointer to an -** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] -** structure is filled with the currently defined memory allocation routines.)^ -** This option can be used to overload the default memory allocation -** routines with a wrapper that simulations memory allocation failure or -** tracks memory usage, for example.
-** -** [[SQLITE_CONFIG_MEMSTATUS]]
SQLITE_CONFIG_MEMSTATUS
-**
^This option takes single argument of type int, interpreted as a -** boolean, which enables or disables the collection of memory allocation -** statistics. ^(When memory allocation statistics are disabled, the -** following SQLite interfaces become non-operational: -**
    -**
  • [sqlite3_memory_used()] -**
  • [sqlite3_memory_highwater()] -**
  • [sqlite3_soft_heap_limit64()] -**
  • [sqlite3_status()] -**
)^ -** ^Memory allocation statistics are enabled by default unless SQLite is -** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory -** allocation statistics are disabled by default. -**
-** -** [[SQLITE_CONFIG_SCRATCH]]
SQLITE_CONFIG_SCRATCH
-**
^This option specifies a static memory buffer that SQLite can use for -** scratch memory. There are three arguments: A pointer an 8-byte -** aligned memory buffer from which the scratch allocations will be -** drawn, the size of each scratch allocation (sz), -** and the maximum number of scratch allocations (N). The sz -** argument must be a multiple of 16. -** The first argument must be a pointer to an 8-byte aligned buffer -** of at least sz*N bytes of memory. -** ^SQLite will use no more than two scratch buffers per thread. So -** N should be set to twice the expected maximum number of threads. -** ^SQLite will never require a scratch buffer that is more than 6 -** times the database page size. ^If SQLite needs needs additional -** scratch memory beyond what is provided by this configuration option, then -** [sqlite3_malloc()] will be used to obtain the memory needed.
-** -** [[SQLITE_CONFIG_PAGECACHE]]
SQLITE_CONFIG_PAGECACHE
-**
^This option specifies a static memory buffer that SQLite can use for -** the database page cache with the default page cache implementation. -** This configuration should not be used if an application-define page -** cache implementation is loaded using the SQLITE_CONFIG_PCACHE2 option. -** There are three arguments to this option: A pointer to 8-byte aligned -** memory, the size of each page buffer (sz), and the number of pages (N). -** The sz argument should be the size of the largest database page -** (a power of two between 512 and 32768) plus a little extra for each -** page header. ^The page header size is 20 to 40 bytes depending on -** the host architecture. ^It is harmless, apart from the wasted memory, -** to make sz a little too large. The first -** argument should point to an allocation of at least sz*N bytes of memory. -** ^SQLite will use the memory provided by the first argument to satisfy its -** memory needs for the first N pages that it adds to cache. ^If additional -** page cache memory is needed beyond what is provided by this option, then -** SQLite goes to [sqlite3_malloc()] for the additional storage space. -** The pointer in the first argument must -** be aligned to an 8-byte boundary or subsequent behavior of SQLite -** will be undefined.
-** -** [[SQLITE_CONFIG_HEAP]]
SQLITE_CONFIG_HEAP
-**
^This option specifies a static memory buffer that SQLite will use -** for all of its dynamic memory allocation needs beyond those provided -** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. -** There are three arguments: An 8-byte aligned pointer to the memory, -** the number of bytes in the memory buffer, and the minimum allocation size. -** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts -** to using its default memory allocator (the system malloc() implementation), -** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the -** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or -** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory -** allocator is engaged to handle all of SQLites memory allocation needs. -** The first pointer (the memory pointer) must be aligned to an 8-byte -** boundary or subsequent behavior of SQLite will be undefined. -** The minimum allocation size is capped at 2**12. Reasonable values -** for the minimum allocation size are 2**5 through 2**8.
-** -** [[SQLITE_CONFIG_MUTEX]]
SQLITE_CONFIG_MUTEX
-**
^(This option takes a single argument which is a pointer to an -** instance of the [sqlite3_mutex_methods] structure. The argument specifies -** alternative low-level mutex routines to be used in place -** the mutex routines built into SQLite.)^ ^SQLite makes a copy of the -** content of the [sqlite3_mutex_methods] structure before the call to -** [sqlite3_config()] returns. ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** the entire mutexing subsystem is omitted from the build and hence calls to -** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will -** return [SQLITE_ERROR].
-** -** [[SQLITE_CONFIG_GETMUTEX]]
SQLITE_CONFIG_GETMUTEX
-**
^(This option takes a single argument which is a pointer to an -** instance of the [sqlite3_mutex_methods] structure. The -** [sqlite3_mutex_methods] -** structure is filled with the currently defined mutex routines.)^ -** This option can be used to overload the default mutex allocation -** routines with a wrapper used to track mutex usage for performance -** profiling or testing, for example. ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** the entire mutexing subsystem is omitted from the build and hence calls to -** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will -** return [SQLITE_ERROR].
-** -** [[SQLITE_CONFIG_LOOKASIDE]]
SQLITE_CONFIG_LOOKASIDE
-**
^(This option takes two arguments that determine the default -** memory allocation for the lookaside memory allocator on each -** [database connection]. The first argument is the -** size of each lookaside buffer slot and the second is the number of -** slots allocated to each database connection.)^ ^(This option sets the -** default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] -** verb to [sqlite3_db_config()] can be used to change the lookaside -** configuration on individual connections.)^
-** -** [[SQLITE_CONFIG_PCACHE2]]
SQLITE_CONFIG_PCACHE2
-**
^(This option takes a single argument which is a pointer to -** an [sqlite3_pcache_methods2] object. This object specifies the interface -** to a custom page cache implementation.)^ ^SQLite makes a copy of the -** object and uses it for page cache memory allocations.
-** -** [[SQLITE_CONFIG_GETPCACHE2]]
SQLITE_CONFIG_GETPCACHE2
-**
^(This option takes a single argument which is a pointer to an -** [sqlite3_pcache_methods2] object. SQLite copies of the current -** page cache implementation into that object.)^
-** -** [[SQLITE_CONFIG_LOG]]
SQLITE_CONFIG_LOG
-**
^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a -** function with a call signature of void(*)(void*,int,const char*), -** and a pointer to void. ^If the function pointer is not NULL, it is -** invoked by [sqlite3_log()] to process each logging event. ^If the -** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op. -** ^The void pointer that is the second argument to SQLITE_CONFIG_LOG is -** passed through as the first parameter to the application-defined logger -** function whenever that function is invoked. ^The second parameter to -** the logger function is a copy of the first parameter to the corresponding -** [sqlite3_log()] call and is intended to be a [result code] or an -** [extended result code]. ^The third parameter passed to the logger is -** log message after formatting via [sqlite3_snprintf()]. -** The SQLite logging interface is not reentrant; the logger function -** supplied by the application must not invoke any SQLite interface. -** In a multi-threaded application, the application-defined logger -** function must be threadsafe.
-** -** [[SQLITE_CONFIG_URI]]
SQLITE_CONFIG_URI -**
This option takes a single argument of type int. If non-zero, then -** URI handling is globally enabled. If the parameter is zero, then URI handling -** is globally disabled. If URI handling is globally enabled, all filenames -** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or -** specified as part of [ATTACH] commands are interpreted as URIs, regardless -** of whether or not the [SQLITE_OPEN_URI] flag is set when the database -** connection is opened. If it is globally disabled, filenames are -** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the -** database connection is opened. By default, URI handling is globally -** disabled. The default value may be changed by compiling with the -** [SQLITE_USE_URI] symbol defined. -** -** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]]
SQLITE_CONFIG_COVERING_INDEX_SCAN -**
This option takes a single integer argument which is interpreted as -** a boolean in order to enable or disable the use of covering indices for -** full table scans in the query optimizer. The default setting is determined -** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on" -** if that compile-time option is omitted. -** The ability to disable the use of covering indices for full table scans -** is because some incorrectly coded legacy applications might malfunction -** malfunction when the optimization is enabled. Providing the ability to -** disable the optimization allows the older, buggy application code to work -** without change even with newer versions of SQLite. -** -** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]] -**
SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE -**
These options are obsolete and should not be used by new code. -** They are retained for backwards compatibility but are now no-ops. -**
-** -** [[SQLITE_CONFIG_SQLLOG]] -**
SQLITE_CONFIG_SQLLOG -**
This option is only available if sqlite is compiled with the -** SQLITE_ENABLE_SQLLOG pre-processor macro defined. The first argument should -** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int). -** The second should be of type (void*). The callback is invoked by the library -** in three separate circumstances, identified by the value passed as the -** fourth parameter. If the fourth parameter is 0, then the database connection -** passed as the second argument has just been opened. The third argument -** points to a buffer containing the name of the main database file. If the -** fourth parameter is 1, then the SQL statement that the third parameter -** points to has just been executed. Or, if the fourth parameter is 2, then -** the connection being passed as the second parameter is being closed. The -** third parameter is passed NULL In this case. -** -*/ -#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ -#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ -#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ -#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */ -#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ -#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ -#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ -#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ -#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ -/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ -#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -#define SQLITE_CONFIG_PCACHE 14 /* no-op */ -#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ -#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ -#define SQLITE_CONFIG_URI 17 /* int */ -#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ -#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ -#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ -#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ - -/* -** CAPI3REF: Database Connection Configuration Options -** -** These constants are the available integer configuration options that -** can be passed as the second argument to the [sqlite3_db_config()] interface. -** -** New configuration options may be added in future releases of SQLite. -** Existing configuration options might be discontinued. Applications -** should check the return code from [sqlite3_db_config()] to make sure that -** the call worked. ^The [sqlite3_db_config()] interface will return a -** non-zero [error code] if a discontinued or unsupported configuration option -** is invoked. -** -**
-**
SQLITE_DBCONFIG_LOOKASIDE
-**
^This option takes three additional arguments that determine the -** [lookaside memory allocator] configuration for the [database connection]. -** ^The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to a memory buffer to use for lookaside memory. -** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb -** may be NULL in which case SQLite will allocate the -** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the -** size of each lookaside buffer slot. ^The third argument is the number of -** slots. The size of the buffer in the first argument must be greater than -** or equal to the product of the second and third arguments. The buffer -** must be aligned to an 8-byte boundary. ^If the second argument to -** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally -** rounded down to the next smaller multiple of 8. ^(The lookaside memory -** configuration for a database connection can only be changed when that -** connection is not currently using lookaside memory, or in other words -** when the "current value" returned by -** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. -** Any attempt to change the lookaside memory configuration when lookaside -** memory is in use leaves the configuration unchanged and returns -** [SQLITE_BUSY].)^
-** -**
SQLITE_DBCONFIG_ENABLE_FKEY
-**
^This option is used to enable or disable the enforcement of -** [foreign key constraints]. There should be two additional arguments. -** The first argument is an integer which is 0 to disable FK enforcement, -** positive to enable FK enforcement or negative to leave FK enforcement -** unchanged. The second parameter is a pointer to an integer into which -** is written 0 or 1 to indicate whether FK enforcement is off or on -** following this call. The second parameter may be a NULL pointer, in -** which case the FK enforcement setting is not reported back.
-** -**
SQLITE_DBCONFIG_ENABLE_TRIGGER
-**
^This option is used to enable or disable [CREATE TRIGGER | triggers]. -** There should be two additional arguments. -** The first argument is an integer which is 0 to disable triggers, -** positive to enable triggers or negative to leave the setting unchanged. -** The second parameter is a pointer to an integer into which -** is written 0 or 1 to indicate whether triggers are disabled or enabled -** following this call. The second parameter may be a NULL pointer, in -** which case the trigger setting is not reported back.
-** -**
-*/ -#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ -#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ -#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ - - -/* -** CAPI3REF: Enable Or Disable Extended Result Codes -** -** ^The sqlite3_extended_result_codes() routine enables or disables the -** [extended result codes] feature of SQLite. ^The extended result -** codes are disabled by default for historical compatibility. -*/ -SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); - -/* -** CAPI3REF: Last Insert Rowid -** -** ^Each entry in an SQLite table has a unique 64-bit signed -** integer key called the [ROWID | "rowid"]. ^The rowid is always available -** as an undeclared column named ROWID, OID, or _ROWID_ as long as those -** names are not also used by explicitly declared columns. ^If -** the table has a column of type [INTEGER PRIMARY KEY] then that column -** is another alias for the rowid. -** -** ^This routine returns the [rowid] of the most recent -** successful [INSERT] into the database from the [database connection] -** in the first argument. ^As of SQLite version 3.7.7, this routines -** records the last insert rowid of both ordinary tables and [virtual tables]. -** ^If no successful [INSERT]s -** have ever occurred on that database connection, zero is returned. -** -** ^(If an [INSERT] occurs within a trigger or within a [virtual table] -** method, then this routine will return the [rowid] of the inserted -** row as long as the trigger or virtual table method is running. -** But once the trigger or virtual table method ends, the value returned -** by this routine reverts to what it was before the trigger or virtual -** table method began.)^ -** -** ^An [INSERT] that fails due to a constraint violation is not a -** successful [INSERT] and does not change the value returned by this -** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, -** and INSERT OR ABORT make no changes to the return value of this -** routine when their insertion fails. ^(When INSERT OR REPLACE -** encounters a constraint violation, it does not fail. The -** INSERT continues to completion after deleting rows that caused -** the constraint problem so INSERT OR REPLACE will always change -** the return value of this interface.)^ -** -** ^For the purposes of this routine, an [INSERT] is considered to -** be successful even if it is subsequently rolled back. -** -** This function is accessible to SQL statements via the -** [last_insert_rowid() SQL function]. -** -** If a separate thread performs a new [INSERT] on the same -** database connection while the [sqlite3_last_insert_rowid()] -** function is running and thus changes the last insert [rowid], -** then the value returned by [sqlite3_last_insert_rowid()] is -** unpredictable and might not equal either the old or the new -** last insert [rowid]. -*/ -SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); - -/* -** CAPI3REF: Count The Number Of Rows Modified -** -** ^This function returns the number of database rows that were changed -** or inserted or deleted by the most recently completed SQL statement -** on the [database connection] specified by the first parameter. -** ^(Only changes that are directly specified by the [INSERT], [UPDATE], -** or [DELETE] statement are counted. Auxiliary changes caused by -** triggers or [foreign key actions] are not counted.)^ Use the -** [sqlite3_total_changes()] function to find the total number of changes -** including changes caused by triggers and foreign key actions. -** -** ^Changes to a view that are simulated by an [INSTEAD OF trigger] -** are not counted. Only real table changes are counted. -** -** ^(A "row change" is a change to a single row of a single table -** caused by an INSERT, DELETE, or UPDATE statement. Rows that -** are changed as side effects of [REPLACE] constraint resolution, -** rollback, ABORT processing, [DROP TABLE], or by any other -** mechanisms do not count as direct row changes.)^ -** -** A "trigger context" is a scope of execution that begins and -** ends with the script of a [CREATE TRIGGER | trigger]. -** Most SQL statements are -** evaluated outside of any trigger. This is the "top level" -** trigger context. If a trigger fires from the top level, a -** new trigger context is entered for the duration of that one -** trigger. Subtriggers create subcontexts for their duration. -** -** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does -** not create a new trigger context. -** -** ^This function returns the number of direct row changes in the -** most recent INSERT, UPDATE, or DELETE statement within the same -** trigger context. -** -** ^Thus, when called from the top level, this function returns the -** number of changes in the most recent INSERT, UPDATE, or DELETE -** that also occurred at the top level. ^(Within the body of a trigger, -** the sqlite3_changes() interface can be called to find the number of -** changes in the most recently completed INSERT, UPDATE, or DELETE -** statement within the body of the same trigger. -** However, the number returned does not include changes -** caused by subtriggers since those have their own context.)^ -** -** See also the [sqlite3_total_changes()] interface, the -** [count_changes pragma], and the [changes() SQL function]. -** -** If a separate thread makes changes on the same database connection -** while [sqlite3_changes()] is running then the value returned -** is unpredictable and not meaningful. -*/ -SQLITE_API int sqlite3_changes(sqlite3*); - -/* -** CAPI3REF: Total Number Of Rows Modified -** -** ^This function returns the number of row changes caused by [INSERT], -** [UPDATE] or [DELETE] statements since the [database connection] was opened. -** ^(The count returned by sqlite3_total_changes() includes all changes -** from all [CREATE TRIGGER | trigger] contexts and changes made by -** [foreign key actions]. However, -** the count does not include changes used to implement [REPLACE] constraints, -** do rollbacks or ABORT processing, or [DROP TABLE] processing. The -** count does not include rows of views that fire an [INSTEAD OF trigger], -** though if the INSTEAD OF trigger makes changes of its own, those changes -** are counted.)^ -** ^The sqlite3_total_changes() function counts the changes as soon as -** the statement that makes them is completed (when the statement handle -** is passed to [sqlite3_reset()] or [sqlite3_finalize()]). -** -** See also the [sqlite3_changes()] interface, the -** [count_changes pragma], and the [total_changes() SQL function]. -** -** If a separate thread makes changes on the same database connection -** while [sqlite3_total_changes()] is running then the value -** returned is unpredictable and not meaningful. -*/ -SQLITE_API int sqlite3_total_changes(sqlite3*); - -/* -** CAPI3REF: Interrupt A Long-Running Query -** -** ^This function causes any pending database operation to abort and -** return at its earliest opportunity. This routine is typically -** called in response to a user action such as pressing "Cancel" -** or Ctrl-C where the user wants a long query operation to halt -** immediately. -** -** ^It is safe to call this routine from a thread different from the -** thread that is currently running the database operation. But it -** is not safe to call this routine with a [database connection] that -** is closed or might close before sqlite3_interrupt() returns. -** -** ^If an SQL operation is very nearly finished at the time when -** sqlite3_interrupt() is called, then it might not have an opportunity -** to be interrupted and might continue to completion. -** -** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. -** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE -** that is inside an explicit transaction, then the entire transaction -** will be rolled back automatically. -** -** ^The sqlite3_interrupt(D) call is in effect until all currently running -** SQL statements on [database connection] D complete. ^Any new SQL statements -** that are started after the sqlite3_interrupt() call and before the -** running statements reaches zero are interrupted as if they had been -** running prior to the sqlite3_interrupt() call. ^New SQL statements -** that are started after the running statement count reaches zero are -** not effected by the sqlite3_interrupt(). -** ^A call to sqlite3_interrupt(D) that occurs when there are no running -** SQL statements is a no-op and has no effect on SQL statements -** that are started after the sqlite3_interrupt() call returns. -** -** If the database connection closes while [sqlite3_interrupt()] -** is running then bad things will likely happen. -*/ -SQLITE_API void sqlite3_interrupt(sqlite3*); - -/* -** CAPI3REF: Determine If An SQL Statement Is Complete -** -** These routines are useful during command-line input to determine if the -** currently entered text seems to form a complete SQL statement or -** if additional input is needed before sending the text into -** SQLite for parsing. ^These routines return 1 if the input string -** appears to be a complete SQL statement. ^A statement is judged to be -** complete if it ends with a semicolon token and is not a prefix of a -** well-formed CREATE TRIGGER statement. ^Semicolons that are embedded within -** string literals or quoted identifier names or comments are not -** independent tokens (they are part of the token in which they are -** embedded) and thus do not count as a statement terminator. ^Whitespace -** and comments that follow the final semicolon are ignored. -** -** ^These routines return 0 if the statement is incomplete. ^If a -** memory allocation fails, then SQLITE_NOMEM is returned. -** -** ^These routines do not parse the SQL statements thus -** will not detect syntactically incorrect SQL. -** -** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior -** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked -** automatically by sqlite3_complete16(). If that initialization fails, -** then the return value from sqlite3_complete16() will be non-zero -** regardless of whether or not the input SQL is complete.)^ -** -** The input to [sqlite3_complete()] must be a zero-terminated -** UTF-8 string. -** -** The input to [sqlite3_complete16()] must be a zero-terminated -** UTF-16 string in native byte order. -*/ -SQLITE_API int sqlite3_complete(const char *sql); -SQLITE_API int sqlite3_complete16(const void *sql); - -/* -** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors -** -** ^This routine sets a callback function that might be invoked whenever -** an attempt is made to open a database table that another thread -** or process has locked. -** -** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] -** is returned immediately upon encountering the lock. ^If the busy callback -** is not NULL, then the callback might be invoked with two arguments. -** -** ^The first argument to the busy handler is a copy of the void* pointer which -** is the third argument to sqlite3_busy_handler(). ^The second argument to -** the busy handler callback is the number of times that the busy handler has -** been invoked for this locking event. ^If the -** busy callback returns 0, then no additional attempts are made to -** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned. -** ^If the callback returns non-zero, then another attempt -** is made to open the database for reading and the cycle repeats. -** -** The presence of a busy handler does not guarantee that it will be invoked -** when there is lock contention. ^If SQLite determines that invoking the busy -** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] -** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler. -** Consider a scenario where one process is holding a read lock that -** it is trying to promote to a reserved lock and -** a second process is holding a reserved lock that it is trying -** to promote to an exclusive lock. The first process cannot proceed -** because it is blocked by the second and the second process cannot -** proceed because it is blocked by the first. If both processes -** invoke the busy handlers, neither will make any progress. Therefore, -** SQLite returns [SQLITE_BUSY] for the first process, hoping that this -** will induce the first process to release its read lock and allow -** the second process to proceed. -** -** ^The default busy callback is NULL. -** -** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] -** when SQLite is in the middle of a large transaction where all the -** changes will not fit into the in-memory cache. SQLite will -** already hold a RESERVED lock on the database file, but it needs -** to promote this lock to EXCLUSIVE so that it can spill cache -** pages into the database file without harm to concurrent -** readers. ^If it is unable to promote the lock, then the in-memory -** cache will be left in an inconsistent state and so the error -** code is promoted from the relatively benign [SQLITE_BUSY] to -** the more severe [SQLITE_IOERR_BLOCKED]. ^This error code promotion -** forces an automatic rollback of the changes. See the -** -** CorruptionFollowingBusyError wiki page for a discussion of why -** this is important. -** -** ^(There can only be a single busy handler defined for each -** [database connection]. Setting a new busy handler clears any -** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] -** will also set or clear the busy handler. -** -** The busy callback should not take any actions which modify the -** database connection that invoked the busy handler. Any such actions -** result in undefined behavior. -** -** A busy handler must not close the database connection -** or [prepared statement] that invoked the busy handler. -*/ -SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); - -/* -** CAPI3REF: Set A Busy Timeout -** -** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps -** for a specified amount of time when a table is locked. ^The handler -** will sleep multiple times until at least "ms" milliseconds of sleeping -** have accumulated. ^After at least "ms" milliseconds of sleeping, -** the handler returns 0 which causes [sqlite3_step()] to return -** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]. -** -** ^Calling this routine with an argument less than or equal to zero -** turns off all busy handlers. -** -** ^(There can only be a single busy handler for a particular -** [database connection] any any given moment. If another busy handler -** was defined (using [sqlite3_busy_handler()]) prior to calling -** this routine, that other busy handler is cleared.)^ -*/ -SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); - -/* -** CAPI3REF: Convenience Routines For Running Queries -** -** This is a legacy interface that is preserved for backwards compatibility. -** Use of this interface is not recommended. -** -** Definition: A result table is memory data structure created by the -** [sqlite3_get_table()] interface. A result table records the -** complete query results from one or more queries. -** -** The table conceptually has a number of rows and columns. But -** these numbers are not part of the result table itself. These -** numbers are obtained separately. Let N be the number of rows -** and M be the number of columns. -** -** A result table is an array of pointers to zero-terminated UTF-8 strings. -** There are (N+1)*M elements in the array. The first M pointers point -** to zero-terminated strings that contain the names of the columns. -** The remaining entries all point to query results. NULL values result -** in NULL pointers. All other values are in their UTF-8 zero-terminated -** string representation as returned by [sqlite3_column_text()]. -** -** A result table might consist of one or more memory allocations. -** It is not safe to pass a result table directly to [sqlite3_free()]. -** A result table should be deallocated using [sqlite3_free_table()]. -** -** ^(As an example of the result table format, suppose a query result -** is as follows: -** -**
-**        Name        | Age
-**        -----------------------
-**        Alice       | 43
-**        Bob         | 28
-**        Cindy       | 21
-** 
-** -** There are two column (M==2) and three rows (N==3). Thus the -** result table has 8 entries. Suppose the result table is stored -** in an array names azResult. Then azResult holds this content: -** -**
-**        azResult[0] = "Name";
-**        azResult[1] = "Age";
-**        azResult[2] = "Alice";
-**        azResult[3] = "43";
-**        azResult[4] = "Bob";
-**        azResult[5] = "28";
-**        azResult[6] = "Cindy";
-**        azResult[7] = "21";
-** 
)^ -** -** ^The sqlite3_get_table() function evaluates one or more -** semicolon-separated SQL statements in the zero-terminated UTF-8 -** string of its 2nd parameter and returns a result table to the -** pointer given in its 3rd parameter. -** -** After the application has finished with the result from sqlite3_get_table(), -** it must pass the result table pointer to sqlite3_free_table() in order to -** release the memory that was malloced. Because of the way the -** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling -** function must not try to call [sqlite3_free()] directly. Only -** [sqlite3_free_table()] is able to release the memory properly and safely. -** -** The sqlite3_get_table() interface is implemented as a wrapper around -** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access -** to any internal data structures of SQLite. It uses only the public -** interface defined here. As a consequence, errors that occur in the -** wrapper layer outside of the internal [sqlite3_exec()] call are not -** reflected in subsequent calls to [sqlite3_errcode()] or -** [sqlite3_errmsg()]. -*/ -SQLITE_API int sqlite3_get_table( - sqlite3 *db, /* An open database */ - const char *zSql, /* SQL to be evaluated */ - char ***pazResult, /* Results of the query */ - int *pnRow, /* Number of result rows written here */ - int *pnColumn, /* Number of result columns written here */ - char **pzErrmsg /* Error msg written here */ -); -SQLITE_API void sqlite3_free_table(char **result); - -/* -** CAPI3REF: Formatted String Printing Functions -** -** These routines are work-alikes of the "printf()" family of functions -** from the standard C library. -** -** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their -** results into memory obtained from [sqlite3_malloc()]. -** The strings returned by these two routines should be -** released by [sqlite3_free()]. ^Both routines return a -** NULL pointer if [sqlite3_malloc()] is unable to allocate enough -** memory to hold the resulting string. -** -** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from -** the standard C library. The result is written into the -** buffer supplied as the second parameter whose size is given by -** the first parameter. Note that the order of the -** first two parameters is reversed from snprintf().)^ This is an -** historical accident that cannot be fixed without breaking -** backwards compatibility. ^(Note also that sqlite3_snprintf() -** returns a pointer to its buffer instead of the number of -** characters actually written into the buffer.)^ We admit that -** the number of characters written would be a more useful return -** value but we cannot change the implementation of sqlite3_snprintf() -** now without breaking compatibility. -** -** ^As long as the buffer size is greater than zero, sqlite3_snprintf() -** guarantees that the buffer is always zero-terminated. ^The first -** parameter "n" is the total size of the buffer, including space for -** the zero terminator. So the longest string that can be completely -** written will be n-1 characters. -** -** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). -** -** These routines all implement some additional formatting -** options that are useful for constructing SQL statements. -** All of the usual printf() formatting options apply. In addition, there -** is are "%q", "%Q", and "%z" options. -** -** ^(The %q option works like %s in that it substitutes a nul-terminated -** string from the argument list. But %q also doubles every '\'' character. -** %q is designed for use inside a string literal.)^ By doubling each '\'' -** character it escapes that character and allows it to be inserted into -** the string. -** -** For example, assume the string variable zText contains text as follows: -** -**
-**  char *zText = "It's a happy day!";
-** 
-** -** One can use this text in an SQL statement as follows: -** -**
-**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
-**  sqlite3_exec(db, zSQL, 0, 0, 0);
-**  sqlite3_free(zSQL);
-** 
-** -** Because the %q format string is used, the '\'' character in zText -** is escaped and the SQL generated is as follows: -** -**
-**  INSERT INTO table1 VALUES('It''s a happy day!')
-** 
-** -** This is correct. Had we used %s instead of %q, the generated SQL -** would have looked like this: -** -**
-**  INSERT INTO table1 VALUES('It's a happy day!');
-** 
-** -** This second example is an SQL syntax error. As a general rule you should -** always use %q instead of %s when inserting text into a string literal. -** -** ^(The %Q option works like %q except it also adds single quotes around -** the outside of the total string. Additionally, if the parameter in the -** argument list is a NULL pointer, %Q substitutes the text "NULL" (without -** single quotes).)^ So, for example, one could say: -** -**
-**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
-**  sqlite3_exec(db, zSQL, 0, 0, 0);
-**  sqlite3_free(zSQL);
-** 
-** -** The code above will render a correct SQL statement in the zSQL -** variable even if the zText variable is a NULL pointer. -** -** ^(The "%z" formatting option works like "%s" but with the -** addition that after the string has been read and copied into -** the result, [sqlite3_free()] is called on the input string.)^ -*/ -SQLITE_API char *sqlite3_mprintf(const char*,...); -SQLITE_API char *sqlite3_vmprintf(const char*, va_list); -SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); -SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); - -/* -** CAPI3REF: Memory Allocation Subsystem -** -** The SQLite core uses these three routines for all of its own -** internal memory allocation needs. "Core" in the previous sentence -** does not include operating-system specific VFS implementation. The -** Windows VFS uses native malloc() and free() for some operations. -** -** ^The sqlite3_malloc() routine returns a pointer to a block -** of memory at least N bytes in length, where N is the parameter. -** ^If sqlite3_malloc() is unable to obtain sufficient free -** memory, it returns a NULL pointer. ^If the parameter N to -** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns -** a NULL pointer. -** -** ^Calling sqlite3_free() with a pointer previously returned -** by sqlite3_malloc() or sqlite3_realloc() releases that memory so -** that it might be reused. ^The sqlite3_free() routine is -** a no-op if is called with a NULL pointer. Passing a NULL pointer -** to sqlite3_free() is harmless. After being freed, memory -** should neither be read nor written. Even reading previously freed -** memory might result in a segmentation fault or other severe error. -** Memory corruption, a segmentation fault, or other severe error -** might result if sqlite3_free() is called with a non-NULL pointer that -** was not obtained from sqlite3_malloc() or sqlite3_realloc(). -** -** ^(The sqlite3_realloc() interface attempts to resize a -** prior memory allocation to be at least N bytes, where N is the -** second parameter. The memory allocation to be resized is the first -** parameter.)^ ^ If the first parameter to sqlite3_realloc() -** is a NULL pointer then its behavior is identical to calling -** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc(). -** ^If the second parameter to sqlite3_realloc() is zero or -** negative then the behavior is exactly the same as calling -** sqlite3_free(P) where P is the first parameter to sqlite3_realloc(). -** ^sqlite3_realloc() returns a pointer to a memory allocation -** of at least N bytes in size or NULL if sufficient memory is unavailable. -** ^If M is the size of the prior allocation, then min(N,M) bytes -** of the prior allocation are copied into the beginning of buffer returned -** by sqlite3_realloc() and the prior allocation is freed. -** ^If sqlite3_realloc() returns NULL, then the prior allocation -** is not freed. -** -** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() -** is always aligned to at least an 8 byte boundary, or to a -** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time -** option is used. -** -** In SQLite version 3.5.0 and 3.5.1, it was possible to define -** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in -** implementation of these routines to be omitted. That capability -** is no longer provided. Only built-in memory allocators can be used. -** -** Prior to SQLite version 3.7.10, the Windows OS interface layer called -** the system malloc() and free() directly when converting -** filenames between the UTF-8 encoding used by SQLite -** and whatever filename encoding is used by the particular Windows -** installation. Memory allocation errors were detected, but -** they were reported back as [SQLITE_CANTOPEN] or -** [SQLITE_IOERR] rather than [SQLITE_NOMEM]. -** -** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] -** must be either NULL or else pointers obtained from a prior -** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have -** not yet been released. -** -** The application must not read or write any part of -** a block of memory after it has been released using -** [sqlite3_free()] or [sqlite3_realloc()]. -*/ -SQLITE_API void *sqlite3_malloc(int); -SQLITE_API void *sqlite3_realloc(void*, int); -SQLITE_API void sqlite3_free(void*); - -/* -** CAPI3REF: Memory Allocator Statistics -** -** SQLite provides these two interfaces for reporting on the status -** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] -** routines, which form the built-in memory allocation subsystem. -** -** ^The [sqlite3_memory_used()] routine returns the number of bytes -** of memory currently outstanding (malloced but not freed). -** ^The [sqlite3_memory_highwater()] routine returns the maximum -** value of [sqlite3_memory_used()] since the high-water mark -** was last reset. ^The values returned by [sqlite3_memory_used()] and -** [sqlite3_memory_highwater()] include any overhead -** added by SQLite in its implementation of [sqlite3_malloc()], -** but not overhead added by the any underlying system library -** routines that [sqlite3_malloc()] may call. -** -** ^The memory high-water mark is reset to the current value of -** [sqlite3_memory_used()] if and only if the parameter to -** [sqlite3_memory_highwater()] is true. ^The value returned -** by [sqlite3_memory_highwater(1)] is the high-water mark -** prior to the reset. -*/ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void); -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); - -/* -** CAPI3REF: Pseudo-Random Number Generator -** -** SQLite contains a high-quality pseudo-random number generator (PRNG) used to -** select random [ROWID | ROWIDs] when inserting new records into a table that -** already uses the largest possible [ROWID]. The PRNG is also used for -** the build-in random() and randomblob() SQL functions. This interface allows -** applications to access the same PRNG for other purposes. -** -** ^A call to this routine stores N bytes of randomness into buffer P. -** -** ^The first time this routine is invoked (either internally or by -** the application) the PRNG is seeded using randomness obtained -** from the xRandomness method of the default [sqlite3_vfs] object. -** ^On all subsequent invocations, the pseudo-randomness is generated -** internally and without recourse to the [sqlite3_vfs] xRandomness -** method. -*/ -SQLITE_API void sqlite3_randomness(int N, void *P); - -/* -** CAPI3REF: Compile-Time Authorization Callbacks -** -** ^This routine registers an authorizer callback with a particular -** [database connection], supplied in the first argument. -** ^The authorizer callback is invoked as SQL statements are being compiled -** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], -** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. ^At various -** points during the compilation process, as logic is being created -** to perform various actions, the authorizer callback is invoked to -** see if those actions are allowed. ^The authorizer callback should -** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the -** specific action but allow the SQL statement to continue to be -** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be -** rejected with an error. ^If the authorizer callback returns -** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY] -** then the [sqlite3_prepare_v2()] or equivalent call that triggered -** the authorizer will fail with an error message. -** -** When the callback returns [SQLITE_OK], that means the operation -** requested is ok. ^When the callback returns [SQLITE_DENY], the -** [sqlite3_prepare_v2()] or equivalent call that triggered the -** authorizer will fail with an error message explaining that -** access is denied. -** -** ^The first parameter to the authorizer callback is a copy of the third -** parameter to the sqlite3_set_authorizer() interface. ^The second parameter -** to the callback is an integer [SQLITE_COPY | action code] that specifies -** the particular action to be authorized. ^The third through sixth parameters -** to the callback are zero-terminated strings that contain additional -** details about the action to be authorized. -** -** ^If the action code is [SQLITE_READ] -** and the callback returns [SQLITE_IGNORE] then the -** [prepared statement] statement is constructed to substitute -** a NULL value in place of the table column that would have -** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE] -** return can be used to deny an untrusted user access to individual -** columns of a table. -** ^If the action code is [SQLITE_DELETE] and the callback returns -** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the -** [truncate optimization] is disabled and all rows are deleted individually. -** -** An authorizer is used when [sqlite3_prepare | preparing] -** SQL statements from an untrusted source, to ensure that the SQL statements -** do not try to access data they are not allowed to see, or that they do not -** try to execute malicious statements that damage the database. For -** example, an application may allow a user to enter arbitrary -** SQL queries for evaluation by a database. But the application does -** not want the user to be able to make arbitrary changes to the -** database. An authorizer could then be put in place while the -** user-entered SQL is being [sqlite3_prepare | prepared] that -** disallows everything except [SELECT] statements. -** -** Applications that need to process SQL from untrusted sources -** might also consider lowering resource limits using [sqlite3_limit()] -** and limiting database size using the [max_page_count] [PRAGMA] -** in addition to using an authorizer. -** -** ^(Only a single authorizer can be in place on a database connection -** at a time. Each call to sqlite3_set_authorizer overrides the -** previous call.)^ ^Disable the authorizer by installing a NULL callback. -** The authorizer is disabled by default. -** -** The authorizer callback must not do anything that will modify -** the database connection that invoked the authorizer callback. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. -** -** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the -** statement might be re-prepared during [sqlite3_step()] due to a -** schema change. Hence, the application should ensure that the -** correct authorizer callback remains in place during the [sqlite3_step()]. -** -** ^Note that the authorizer callback is invoked only during -** [sqlite3_prepare()] or its variants. Authorization is not -** performed during statement evaluation in [sqlite3_step()], unless -** as stated in the previous paragraph, sqlite3_step() invokes -** sqlite3_prepare_v2() to reprepare a statement after a schema change. -*/ -SQLITE_API int sqlite3_set_authorizer( - sqlite3*, - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), - void *pUserData -); - -/* -** CAPI3REF: Authorizer Return Codes -** -** The [sqlite3_set_authorizer | authorizer callback function] must -** return either [SQLITE_OK] or one of these two constants in order -** to signal SQLite whether or not the action is permitted. See the -** [sqlite3_set_authorizer | authorizer documentation] for additional -** information. -** -** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code] -** from the [sqlite3_vtab_on_conflict()] interface. -*/ -#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ -#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ - -/* -** CAPI3REF: Authorizer Action Codes -** -** The [sqlite3_set_authorizer()] interface registers a callback function -** that is invoked to authorize certain SQL statement actions. The -** second parameter to the callback is an integer code that specifies -** what action is being authorized. These are the integer action codes that -** the authorizer callback may be passed. -** -** These action code values signify what kind of operation is to be -** authorized. The 3rd and 4th parameters to the authorization -** callback function will be parameters or NULL depending on which of these -** codes is used as the second parameter. ^(The 5th parameter to the -** authorizer callback is the name of the database ("main", "temp", -** etc.) if applicable.)^ ^The 6th parameter to the authorizer callback -** is the name of the inner-most trigger or view that is responsible for -** the access attempt or NULL if this access attempt is directly from -** top-level SQL code. -*/ -/******************************************* 3rd ************ 4th ***********/ -#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ -#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ -#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ -#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ -#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ -#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ -#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ -#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ -#define SQLITE_DELETE 9 /* Table Name NULL */ -#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ -#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ -#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ -#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ -#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ -#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ -#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ -#define SQLITE_DROP_VIEW 17 /* View Name NULL */ -#define SQLITE_INSERT 18 /* Table Name NULL */ -#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ -#define SQLITE_READ 20 /* Table Name Column Name */ -#define SQLITE_SELECT 21 /* NULL NULL */ -#define SQLITE_TRANSACTION 22 /* Operation NULL */ -#define SQLITE_UPDATE 23 /* Table Name Column Name */ -#define SQLITE_ATTACH 24 /* Filename NULL */ -#define SQLITE_DETACH 25 /* Database Name NULL */ -#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */ -#define SQLITE_REINDEX 27 /* Index Name NULL */ -#define SQLITE_ANALYZE 28 /* Table Name NULL */ -#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ -#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ -#define SQLITE_FUNCTION 31 /* NULL Function Name */ -#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ -#define SQLITE_COPY 0 /* No longer used */ - -/* -** CAPI3REF: Tracing And Profiling Functions -** -** These routines register callback functions that can be used for -** tracing and profiling the execution of SQL statements. -** -** ^The callback function registered by sqlite3_trace() is invoked at -** various times when an SQL statement is being run by [sqlite3_step()]. -** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the -** SQL statement text as the statement first begins executing. -** ^(Additional sqlite3_trace() callbacks might occur -** as each triggered subprogram is entered. The callbacks for triggers -** contain a UTF-8 SQL comment that identifies the trigger.)^ -** -** ^The callback function registered by sqlite3_profile() is invoked -** as each SQL statement finishes. ^The profile callback contains -** the original statement text and an estimate of wall-clock time -** of how long that statement took to run. ^The profile callback -** time is in units of nanoseconds, however the current implementation -** is only capable of millisecond resolution so the six least significant -** digits in the time are meaningless. Future versions of SQLite -** might provide greater resolution on the profiler callback. The -** sqlite3_profile() function is considered experimental and is -** subject to change in future versions of SQLite. -*/ -SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); -SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, - void(*xProfile)(void*,const char*,sqlite3_uint64), void*); - -/* -** CAPI3REF: Query Progress Callbacks -** -** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback -** function X to be invoked periodically during long running calls to -** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for -** database connection D. An example use for this -** interface is to keep a GUI updated during a large query. -** -** ^The parameter P is passed through as the only parameter to the -** callback function X. ^The parameter N is the number of -** [virtual machine instructions] that are evaluated between successive -** invocations of the callback X. -** -** ^Only a single progress handler may be defined at one time per -** [database connection]; setting a new progress handler cancels the -** old one. ^Setting parameter X to NULL disables the progress handler. -** ^The progress handler is also disabled by setting N to a value less -** than 1. -** -** ^If the progress callback returns non-zero, the operation is -** interrupted. This feature can be used to implement a -** "Cancel" button on a GUI progress dialog box. -** -** The progress handler callback must not do anything that will modify -** the database connection that invoked the progress handler. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. -** -*/ -SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - -/* -** CAPI3REF: Opening A New Database Connection -** -** ^These routines open an SQLite database file as specified by the -** filename argument. ^The filename argument is interpreted as UTF-8 for -** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte -** order for sqlite3_open16(). ^(A [database connection] handle is usually -** returned in *ppDb, even if an error occurs. The only exception is that -** if SQLite is unable to allocate memory to hold the [sqlite3] object, -** a NULL will be written into *ppDb instead of a pointer to the [sqlite3] -** object.)^ ^(If the database is opened (and/or created) successfully, then -** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The -** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain -** an English language description of the error following a failure of any -** of the sqlite3_open() routines. -** -** ^The default encoding for the database will be UTF-8 if -** sqlite3_open() or sqlite3_open_v2() is called and -** UTF-16 in the native byte order if sqlite3_open16() is used. -** -** Whether or not an error occurs when it is opened, resources -** associated with the [database connection] handle should be released by -** passing it to [sqlite3_close()] when it is no longer required. -** -** The sqlite3_open_v2() interface works like sqlite3_open() -** except that it accepts two additional parameters for additional control -** over the new database connection. ^(The flags parameter to -** sqlite3_open_v2() can take one of -** the following three values, optionally combined with the -** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE], -** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^ -** -**
-** ^(
[SQLITE_OPEN_READONLY]
-**
The database is opened in read-only mode. If the database does not -** already exist, an error is returned.
)^ -** -** ^(
[SQLITE_OPEN_READWRITE]
-**
The database is opened for reading and writing if possible, or reading -** only if the file is write protected by the operating system. In either -** case the database must already exist, otherwise an error is returned.
)^ -** -** ^(
[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
-**
The database is opened for reading and writing, and is created if -** it does not already exist. This is the behavior that is always used for -** sqlite3_open() and sqlite3_open16().
)^ -**
-** -** If the 3rd parameter to sqlite3_open_v2() is not one of the -** combinations shown above optionally combined with other -** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] -** then the behavior is undefined. -** -** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection -** opens in the multi-thread [threading mode] as long as the single-thread -** mode has not been set at compile-time or start-time. ^If the -** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens -** in the serialized [threading mode] unless single-thread was -** previously selected at compile-time or start-time. -** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be -** eligible to use [shared cache mode], regardless of whether or not shared -** cache is enabled using [sqlite3_enable_shared_cache()]. ^The -** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not -** participate in [shared cache mode] even if it is enabled. -** -** ^The fourth parameter to sqlite3_open_v2() is the name of the -** [sqlite3_vfs] object that defines the operating system interface that -** the new database connection should use. ^If the fourth parameter is -** a NULL pointer then the default [sqlite3_vfs] object is used. -** -** ^If the filename is ":memory:", then a private, temporary in-memory database -** is created for the connection. ^This in-memory database will vanish when -** the database connection is closed. Future versions of SQLite might -** make use of additional special filenames that begin with the ":" character. -** It is recommended that when a database filename actually does begin with -** a ":" character you should prefix the filename with a pathname such as -** "./" to avoid ambiguity. -** -** ^If the filename is an empty string, then a private, temporary -** on-disk database will be created. ^This private database will be -** automatically deleted as soon as the database connection is closed. -** -** [[URI filenames in sqlite3_open()]]

URI Filenames

-** -** ^If [URI filename] interpretation is enabled, and the filename argument -** begins with "file:", then the filename is interpreted as a URI. ^URI -** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is -** set in the fourth argument to sqlite3_open_v2(), or if it has -** been enabled globally using the [SQLITE_CONFIG_URI] option with the -** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. -** As of SQLite version 3.7.7, URI filename interpretation is turned off -** by default, but future releases of SQLite might enable URI filename -** interpretation by default. See "[URI filenames]" for additional -** information. -** -** URI filenames are parsed according to RFC 3986. ^If the URI contains an -** authority, then it must be either an empty string or the string -** "localhost". ^If the authority is not an empty string or "localhost", an -** error is returned to the caller. ^The fragment component of a URI, if -** present, is ignored. -** -** ^SQLite uses the path component of the URI as the name of the disk file -** which contains the database. ^If the path begins with a '/' character, -** then it is interpreted as an absolute path. ^If the path does not begin -** with a '/' (meaning that the authority section is omitted from the URI) -** then the path is interpreted as a relative path. -** ^On windows, the first component of an absolute path -** is a drive specification (e.g. "C:"). -** -** [[core URI query parameters]] -** The query component of a URI may contain parameters that are interpreted -** either by SQLite itself, or by a [VFS | custom VFS implementation]. -** SQLite interprets the following three query parameters: -** -**
    -**
  • vfs: ^The "vfs" parameter may be used to specify the name of -** a VFS object that provides the operating system interface that should -** be used to access the database file on disk. ^If this option is set to -** an empty string the default VFS object is used. ^Specifying an unknown -** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is -** present, then the VFS specified by the option takes precedence over -** the value passed as the fourth parameter to sqlite3_open_v2(). -** -**
  • mode: ^(The mode parameter may be set to either "ro", "rw", -** "rwc", or "memory". Attempting to set it to any other value is -** an error)^. -** ^If "ro" is specified, then the database is opened for read-only -** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the -** third argument to sqlite3_open_v2(). ^If the mode option is set to -** "rw", then the database is opened for read-write (but not create) -** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had -** been set. ^Value "rwc" is equivalent to setting both -** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is -** set to "memory" then a pure [in-memory database] that never reads -** or writes from disk is used. ^It is an error to specify a value for -** the mode parameter that is less restrictive than that specified by -** the flags passed in the third parameter to sqlite3_open_v2(). -** -**
  • cache: ^The cache parameter may be set to either "shared" or -** "private". ^Setting it to "shared" is equivalent to setting the -** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to -** sqlite3_open_v2(). ^Setting the cache parameter to "private" is -** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. -** ^If sqlite3_open_v2() is used and the "cache" parameter is present in -** a URI filename, its value overrides any behavior requested by setting -** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. -**
-** -** ^Specifying an unknown parameter in the query component of a URI is not an -** error. Future versions of SQLite might understand additional query -** parameters. See "[query parameters with special meaning to SQLite]" for -** additional information. -** -** [[URI filename examples]]

URI filename examples

-** -** -**
URI filenames Results -**
file:data.db -** Open the file "data.db" in the current directory. -**
file:/home/fred/data.db
-** file:///home/fred/data.db
-** file://localhost/home/fred/data.db
-** Open the database file "/home/fred/data.db". -**
file://darkstar/home/fred/data.db -** An error. "darkstar" is not a recognized authority. -**
-** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db -** Windows only: Open the file "data.db" on fred's desktop on drive -** C:. Note that the %20 escaping in this example is not strictly -** necessary - space characters can be used literally -** in URI filenames. -**
file:data.db?mode=ro&cache=private -** Open file "data.db" in the current directory for read-only access. -** Regardless of whether or not shared-cache mode is enabled by -** default, use a private cache. -**
file:/home/fred/data.db?vfs=unix-nolock -** Open file "/home/fred/data.db". Use the special VFS "unix-nolock". -**
file:data.db?mode=readonly -** An error. "readonly" is not a valid option for the "mode" parameter. -**
-** -** ^URI hexadecimal escape sequences (%HH) are supported within the path and -** query components of a URI. A hexadecimal escape sequence consists of a -** percent sign - "%" - followed by exactly two hexadecimal digits -** specifying an octet value. ^Before the path or query components of a -** URI filename are interpreted, they are encoded using UTF-8 and all -** hexadecimal escape sequences replaced by a single byte containing the -** corresponding octet. If this process generates an invalid UTF-8 encoding, -** the results are undefined. -** -** Note to Windows users: The encoding used for the filename argument -** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever -** codepage is currently defined. Filenames containing international -** characters must be converted to UTF-8 prior to passing them into -** sqlite3_open() or sqlite3_open_v2(). -** -** Note to Windows Runtime users: The temporary directory must be set -** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various -** features that require the use of temporary files may fail. -** -** See also: [sqlite3_temp_directory] -*/ -SQLITE_API int sqlite3_open( - const char *filename, /* Database filename (UTF-8) */ - sqlite3 **ppDb /* OUT: SQLite db handle */ -); -SQLITE_API int sqlite3_open16( - const void *filename, /* Database filename (UTF-16) */ - sqlite3 **ppDb /* OUT: SQLite db handle */ -); -SQLITE_API int sqlite3_open_v2( - const char *filename, /* Database filename (UTF-8) */ - sqlite3 **ppDb, /* OUT: SQLite db handle */ - int flags, /* Flags */ - const char *zVfs /* Name of VFS module to use */ -); - -/* -** CAPI3REF: Obtain Values For URI Parameters -** -** These are utility routines, useful to VFS implementations, that check -** to see if a database file was a URI that contained a specific query -** parameter, and if so obtains the value of that query parameter. -** -** If F is the database filename pointer passed into the xOpen() method of -** a VFS implementation when the flags parameter to xOpen() has one or -** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and -** P is the name of the query parameter, then -** sqlite3_uri_parameter(F,P) returns the value of the P -** parameter if it exists or a NULL pointer if P does not appear as a -** query parameter on F. If P is a query parameter of F -** has no explicit value, then sqlite3_uri_parameter(F,P) returns -** a pointer to an empty string. -** -** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean -** parameter and returns true (1) or false (0) according to the value -** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the -** value of query parameter P is one of "yes", "true", or "on" in any -** case or if the value begins with a non-zero number. The -** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of -** query parameter P is one of "no", "false", or "off" in any case or -** if the value begins with a numeric zero. If P is not a query -** parameter on F or if the value of P is does not match any of the -** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0). -** -** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a -** 64-bit signed integer and returns that integer, or D if P does not -** exist. If the value of P is something other than an integer, then -** zero is returned. -** -** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and -** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and -** is not a database file pathname pointer that SQLite passed into the xOpen -** VFS method, then the behavior of this routine is undefined and probably -** undesirable. -*/ -SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); -SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); -SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); - - -/* -** CAPI3REF: Error Codes And Messages -** -** ^The sqlite3_errcode() interface returns the numeric [result code] or -** [extended result code] for the most recent failed sqlite3_* API call -** associated with a [database connection]. If a prior API call failed -** but the most recent API call succeeded, the return value from -** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() -** interface is the same except that it always returns the -** [extended result code] even when extended result codes are -** disabled. -** -** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language -** text that describes the error, as either UTF-8 or UTF-16 respectively. -** ^(Memory to hold the error message string is managed internally. -** The application does not need to worry about freeing the result. -** However, the error string might be overwritten or deallocated by -** subsequent calls to other SQLite interface functions.)^ -** -** ^The sqlite3_errstr() interface returns the English-language text -** that describes the [result code], as UTF-8. -** ^(Memory to hold the error message string is managed internally -** and must not be freed by the application)^. -** -** When the serialized [threading mode] is in use, it might be the -** case that a second error occurs on a separate thread in between -** the time of the first error and the call to these interfaces. -** When that happens, the second error will be reported since these -** interfaces always report the most recent result. To avoid -** this, each thread can obtain exclusive use of the [database connection] D -** by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning -** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after -** all calls to the interfaces listed here are completed. -** -** If an interface fails with SQLITE_MISUSE, that means the interface -** was invoked incorrectly by the application. In that case, the -** error code and message may or may not be set. -*/ -SQLITE_API int sqlite3_errcode(sqlite3 *db); -SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); -SQLITE_API const char *sqlite3_errmsg(sqlite3*); -SQLITE_API const void *sqlite3_errmsg16(sqlite3*); -SQLITE_API const char *sqlite3_errstr(int); - -/* -** CAPI3REF: SQL Statement Object -** KEYWORDS: {prepared statement} {prepared statements} -** -** An instance of this object represents a single SQL statement. -** This object is variously known as a "prepared statement" or a -** "compiled SQL statement" or simply as a "statement". -** -** The life of a statement object goes something like this: -** -**
    -**
  1. Create the object using [sqlite3_prepare_v2()] or a related -** function. -**
  2. Bind values to [host parameters] using the sqlite3_bind_*() -** interfaces. -**
  3. Run the SQL by calling [sqlite3_step()] one or more times. -**
  4. Reset the statement using [sqlite3_reset()] then go back -** to step 2. Do this zero or more times. -**
  5. Destroy the object using [sqlite3_finalize()]. -**
-** -** Refer to documentation on individual methods above for additional -** information. -*/ -typedef struct sqlite3_stmt sqlite3_stmt; - -/* -** CAPI3REF: Run-time Limits -** -** ^(This interface allows the size of various constructs to be limited -** on a connection by connection basis. The first parameter is the -** [database connection] whose limit is to be set or queried. The -** second parameter is one of the [limit categories] that define a -** class of constructs to be size limited. The third parameter is the -** new limit for that construct.)^ -** -** ^If the new limit is a negative number, the limit is unchanged. -** ^(For each limit category SQLITE_LIMIT_NAME there is a -** [limits | hard upper bound] -** set at compile-time by a C preprocessor macro called -** [limits | SQLITE_MAX_NAME]. -** (The "_LIMIT_" in the name is changed to "_MAX_".))^ -** ^Attempts to increase a limit above its hard upper bound are -** silently truncated to the hard upper bound. -** -** ^Regardless of whether or not the limit was changed, the -** [sqlite3_limit()] interface returns the prior value of the limit. -** ^Hence, to find the current value of a limit without changing it, -** simply invoke this interface with the third parameter set to -1. -** -** Run-time limits are intended for use in applications that manage -** both their own internal database and also databases that are controlled -** by untrusted external sources. An example application might be a -** web browser that has its own databases for storing history and -** separate databases controlled by JavaScript applications downloaded -** off the Internet. The internal databases can be given the -** large, default limits. Databases managed by external sources can -** be given much smaller limits designed to prevent a denial of service -** attack. Developers might also want to use the [sqlite3_set_authorizer()] -** interface to further control untrusted SQL. The size of the database -** created by an untrusted script can be contained using the -** [max_page_count] [PRAGMA]. -** -** New run-time limit categories may be added in future releases. -*/ -SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); - -/* -** CAPI3REF: Run-Time Limit Categories -** KEYWORDS: {limit category} {*limit categories} -** -** These constants define various performance limits -** that can be lowered at run-time using [sqlite3_limit()]. -** The synopsis of the meanings of the various limits is shown below. -** Additional information is available at [limits | Limits in SQLite]. -** -**
-** [[SQLITE_LIMIT_LENGTH]] ^(
SQLITE_LIMIT_LENGTH
-**
The maximum size of any string or BLOB or table row, in bytes.
)^ -** -** [[SQLITE_LIMIT_SQL_LENGTH]] ^(
SQLITE_LIMIT_SQL_LENGTH
-**
The maximum length of an SQL statement, in bytes.
)^ -** -** [[SQLITE_LIMIT_COLUMN]] ^(
SQLITE_LIMIT_COLUMN
-**
The maximum number of columns in a table definition or in the -** result set of a [SELECT] or the maximum number of columns in an index -** or in an ORDER BY or GROUP BY clause.
)^ -** -** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(
SQLITE_LIMIT_EXPR_DEPTH
-**
The maximum depth of the parse tree on any expression.
)^ -** -** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(
SQLITE_LIMIT_COMPOUND_SELECT
-**
The maximum number of terms in a compound SELECT statement.
)^ -** -** [[SQLITE_LIMIT_VDBE_OP]] ^(
SQLITE_LIMIT_VDBE_OP
-**
The maximum number of instructions in a virtual machine program -** used to implement an SQL statement. This limit is not currently -** enforced, though that might be added in some future release of -** SQLite.
)^ -** -** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(
SQLITE_LIMIT_FUNCTION_ARG
-**
The maximum number of arguments on a function.
)^ -** -** [[SQLITE_LIMIT_ATTACHED]] ^(
SQLITE_LIMIT_ATTACHED
-**
The maximum number of [ATTACH | attached databases].)^
-** -** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]] -** ^(
SQLITE_LIMIT_LIKE_PATTERN_LENGTH
-**
The maximum length of the pattern argument to the [LIKE] or -** [GLOB] operators.
)^ -** -** [[SQLITE_LIMIT_VARIABLE_NUMBER]] -** ^(
SQLITE_LIMIT_VARIABLE_NUMBER
-**
The maximum index number of any [parameter] in an SQL statement.)^ -** -** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(
SQLITE_LIMIT_TRIGGER_DEPTH
-**
The maximum depth of recursion for triggers.
)^ -**
-*/ -#define SQLITE_LIMIT_LENGTH 0 -#define SQLITE_LIMIT_SQL_LENGTH 1 -#define SQLITE_LIMIT_COLUMN 2 -#define SQLITE_LIMIT_EXPR_DEPTH 3 -#define SQLITE_LIMIT_COMPOUND_SELECT 4 -#define SQLITE_LIMIT_VDBE_OP 5 -#define SQLITE_LIMIT_FUNCTION_ARG 6 -#define SQLITE_LIMIT_ATTACHED 7 -#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 -#define SQLITE_LIMIT_VARIABLE_NUMBER 9 -#define SQLITE_LIMIT_TRIGGER_DEPTH 10 - -/* -** CAPI3REF: Compiling An SQL Statement -** KEYWORDS: {SQL statement compiler} -** -** To execute an SQL query, it must first be compiled into a byte-code -** program using one of these routines. -** -** The first argument, "db", is a [database connection] obtained from a -** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or -** [sqlite3_open16()]. The database connection must not have been closed. -** -** The second argument, "zSql", is the statement to be compiled, encoded -** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2() -** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() -** use UTF-16. -** -** ^If the nByte argument is less than zero, then zSql is read up to the -** first zero terminator. ^If nByte is non-negative, then it is the maximum -** number of bytes read from zSql. ^When nByte is non-negative, the -** zSql string ends at either the first '\000' or '\u0000' character or -** the nByte-th byte, whichever comes first. If the caller knows -** that the supplied string is nul-terminated, then there is a small -** performance advantage to be gained by passing an nByte parameter that -** is equal to the number of bytes in the input string including -** the nul-terminator bytes as this saves SQLite from having to -** make a copy of the input string. -** -** ^If pzTail is not NULL then *pzTail is made to point to the first byte -** past the end of the first SQL statement in zSql. These routines only -** compile the first statement in zSql, so *pzTail is left pointing to -** what remains uncompiled. -** -** ^*ppStmt is left pointing to a compiled [prepared statement] that can be -** executed using [sqlite3_step()]. ^If there is an error, *ppStmt is set -** to NULL. ^If the input text contains no SQL (if the input is an empty -** string or a comment) then *ppStmt is set to NULL. -** The calling procedure is responsible for deleting the compiled -** SQL statement using [sqlite3_finalize()] after it has finished with it. -** ppStmt may not be NULL. -** -** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; -** otherwise an [error code] is returned. -** -** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are -** recommended for all new programs. The two older interfaces are retained -** for backwards compatibility, but their use is discouraged. -** ^In the "v2" interfaces, the prepared statement -** that is returned (the [sqlite3_stmt] object) contains a copy of the -** original SQL text. This causes the [sqlite3_step()] interface to -** behave differently in three ways: -** -**
    -**
  1. -** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it -** always used to do, [sqlite3_step()] will automatically recompile the SQL -** statement and try to run it again. -**
  2. -** -**
  3. -** ^When an error occurs, [sqlite3_step()] will return one of the detailed -** [error codes] or [extended error codes]. ^The legacy behavior was that -** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code -** and the application would have to make a second call to [sqlite3_reset()] -** in order to find the underlying cause of the problem. With the "v2" prepare -** interfaces, the underlying reason for the error is returned immediately. -**
  4. -** -**
  5. -** ^If the specific value bound to [parameter | host parameter] in the -** WHERE clause might influence the choice of query plan for a statement, -** then the statement will be automatically recompiled, as if there had been -** a schema change, on the first [sqlite3_step()] call following any change -** to the [sqlite3_bind_text | bindings] of that [parameter]. -** ^The specific value of WHERE-clause [parameter] might influence the -** choice of query plan if the parameter is the left-hand side of a [LIKE] -** or [GLOB] operator or if the parameter is compared to an indexed column -** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. -** the -**
  6. -**
-*/ -SQLITE_API int sqlite3_prepare( - sqlite3 *db, /* Database handle */ - const char *zSql, /* SQL statement, UTF-8 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const char **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare_v2( - sqlite3 *db, /* Database handle */ - const char *zSql, /* SQL statement, UTF-8 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const char **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare16( - sqlite3 *db, /* Database handle */ - const void *zSql, /* SQL statement, UTF-16 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const void **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare16_v2( - sqlite3 *db, /* Database handle */ - const void *zSql, /* SQL statement, UTF-16 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const void **pzTail /* OUT: Pointer to unused portion of zSql */ -); - -/* -** CAPI3REF: Retrieving Statement SQL -** -** ^This interface can be used to retrieve a saved copy of the original -** SQL text used to create a [prepared statement] if that statement was -** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. -*/ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Determine If An SQL Statement Writes The Database -** -** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if -** and only if the [prepared statement] X makes no direct changes to -** the content of the database file. -** -** Note that [application-defined SQL functions] or -** [virtual tables] might change the database indirectly as a side effect. -** ^(For example, if an application defines a function "eval()" that -** calls [sqlite3_exec()], then the following SQL statement would -** change the database file through side-effects: -** -**
-**    SELECT eval('DELETE FROM t1') FROM t2;
-** 
-** -** But because the [SELECT] statement does not change the database file -** directly, sqlite3_stmt_readonly() would still return true.)^ -** -** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK], -** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, -** since the statements themselves do not actually modify the database but -** rather they control the timing of when other statements modify the -** database. ^The [ATTACH] and [DETACH] statements also cause -** sqlite3_stmt_readonly() to return true since, while those statements -** change the configuration of a database connection, they do not make -** changes to the content of the database files on disk. -*/ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Determine If A Prepared Statement Has Been Reset -** -** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the -** [prepared statement] S has been stepped at least once using -** [sqlite3_step(S)] but has not run to completion and/or has not -** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) -** interface returns false if S is a NULL pointer. If S is not a -** NULL pointer and is not a pointer to a valid [prepared statement] -** object, then the behavior is undefined and probably undesirable. -** -** This interface can be used in combination [sqlite3_next_stmt()] -** to locate all prepared statements associated with a database -** connection that are in need of being reset. This can be used, -** for example, in diagnostic routines to search for prepared -** statements that are holding a transaction open. -*/ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); - -/* -** CAPI3REF: Dynamically Typed Value Object -** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} -** -** SQLite uses the sqlite3_value object to represent all values -** that can be stored in a database table. SQLite uses dynamic typing -** for the values it stores. ^Values stored in sqlite3_value objects -** can be integers, floating point values, strings, BLOBs, or NULL. -** -** An sqlite3_value object may be either "protected" or "unprotected". -** Some interfaces require a protected sqlite3_value. Other interfaces -** will accept either a protected or an unprotected sqlite3_value. -** Every interface that accepts sqlite3_value arguments specifies -** whether or not it requires a protected sqlite3_value. -** -** The terms "protected" and "unprotected" refer to whether or not -** a mutex is held. An internal mutex is held for a protected -** sqlite3_value object but no mutex is held for an unprotected -** sqlite3_value object. If SQLite is compiled to be single-threaded -** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) -** or if SQLite is run in one of reduced mutex modes -** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] -** then there is no distinction between protected and unprotected -** sqlite3_value objects and they can be used interchangeably. However, -** for maximum code portability it is recommended that applications -** still make the distinction between protected and unprotected -** sqlite3_value objects even when not strictly required. -** -** ^The sqlite3_value objects that are passed as parameters into the -** implementation of [application-defined SQL functions] are protected. -** ^The sqlite3_value object returned by -** [sqlite3_column_value()] is unprotected. -** Unprotected sqlite3_value objects may only be used with -** [sqlite3_result_value()] and [sqlite3_bind_value()]. -** The [sqlite3_value_blob | sqlite3_value_type()] family of -** interfaces require protected sqlite3_value objects. -*/ -typedef struct Mem sqlite3_value; - -/* -** CAPI3REF: SQL Function Context Object -** -** The context in which an SQL function executes is stored in an -** sqlite3_context object. ^A pointer to an sqlite3_context object -** is always first parameter to [application-defined SQL functions]. -** The application-defined SQL function implementation will pass this -** pointer through into calls to [sqlite3_result_int | sqlite3_result()], -** [sqlite3_aggregate_context()], [sqlite3_user_data()], -** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()], -** and/or [sqlite3_set_auxdata()]. -*/ -typedef struct sqlite3_context sqlite3_context; - -/* -** CAPI3REF: Binding Values To Prepared Statements -** KEYWORDS: {host parameter} {host parameters} {host parameter name} -** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} -** -** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, -** literals may be replaced by a [parameter] that matches one of following -** templates: -** -**
    -**
  • ? -**
  • ?NNN -**
  • :VVV -**
  • @VVV -**
  • $VVV -**
-** -** In the templates above, NNN represents an integer literal, -** and VVV represents an alphanumeric identifier.)^ ^The values of these -** parameters (also called "host parameter names" or "SQL parameters") -** can be set using the sqlite3_bind_*() routines defined here. -** -** ^The first argument to the sqlite3_bind_*() routines is always -** a pointer to the [sqlite3_stmt] object returned from -** [sqlite3_prepare_v2()] or its variants. -** -** ^The second argument is the index of the SQL parameter to be set. -** ^The leftmost SQL parameter has an index of 1. ^When the same named -** SQL parameter is used more than once, second and subsequent -** occurrences have the same index as the first occurrence. -** ^The index for named parameters can be looked up using the -** [sqlite3_bind_parameter_index()] API if desired. ^The index -** for "?NNN" parameters is the value of NNN. -** ^The NNN value must be between 1 and the [sqlite3_limit()] -** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). -** -** ^The third argument is the value to bind to the parameter. -** -** ^(In those routines that have a fourth argument, its value is the -** number of bytes in the parameter. To be clear: the value is the -** number of bytes in the value, not the number of characters.)^ -** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() -** is negative, then the length of the string is -** the number of bytes up to the first zero terminator. -** If the fourth parameter to sqlite3_bind_blob() is negative, then -** the behavior is undefined. -** If a non-negative fourth parameter is provided to sqlite3_bind_text() -** or sqlite3_bind_text16() then that parameter must be the byte offset -** where the NUL terminator would occur assuming the string were NUL -** terminated. If any NUL characters occur at byte offsets less than -** the value of the fourth parameter then the resulting string value will -** contain embedded NULs. The result of expressions involving strings -** with embedded NULs is undefined. -** -** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and -** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), -** sqlite3_bind_text(), or sqlite3_bind_text16() fails. -** ^If the fifth argument is -** the special value [SQLITE_STATIC], then SQLite assumes that the -** information is in static, unmanaged space and does not need to be freed. -** ^If the fifth argument has the value [SQLITE_TRANSIENT], then -** SQLite makes its own private copy of the data immediately, before -** the sqlite3_bind_*() routine returns. -** -** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that -** is filled with zeroes. ^A zeroblob uses a fixed amount of memory -** (just an integer to hold its size) while it is being processed. -** Zeroblobs are intended to serve as placeholders for BLOBs whose -** content is later written using -** [sqlite3_blob_open | incremental BLOB I/O] routines. -** ^A negative value for the zeroblob results in a zero-length BLOB. -** -** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer -** for the [prepared statement] or with a prepared statement for which -** [sqlite3_step()] has been called more recently than [sqlite3_reset()], -** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() -** routine is passed a [prepared statement] that has been finalized, the -** result is undefined and probably harmful. -** -** ^Bindings are not cleared by the [sqlite3_reset()] routine. -** ^Unbound parameters are interpreted as NULL. -** -** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an -** [error code] if anything goes wrong. -** ^[SQLITE_RANGE] is returned if the parameter -** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. -** -** See also: [sqlite3_bind_parameter_count()], -** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); -SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); -SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); -SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); -SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); -SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); -SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); - -/* -** CAPI3REF: Number Of SQL Parameters -** -** ^This routine can be used to find the number of [SQL parameters] -** in a [prepared statement]. SQL parameters are tokens of the -** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as -** placeholders for values that are [sqlite3_bind_blob | bound] -** to the parameters at a later time. -** -** ^(This routine actually returns the index of the largest (rightmost) -** parameter. For all forms except ?NNN, this will correspond to the -** number of unique parameters. If parameters of the ?NNN form are used, -** there may be gaps in the list.)^ -** -** See also: [sqlite3_bind_blob|sqlite3_bind()], -** [sqlite3_bind_parameter_name()], and -** [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); - -/* -** CAPI3REF: Name Of A Host Parameter -** -** ^The sqlite3_bind_parameter_name(P,N) interface returns -** the name of the N-th [SQL parameter] in the [prepared statement] P. -** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" -** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" -** respectively. -** In other words, the initial ":" or "$" or "@" or "?" -** is included as part of the name.)^ -** ^Parameters of the form "?" without a following integer have no name -** and are referred to as "nameless" or "anonymous parameters". -** -** ^The first host parameter has an index of 1, not 0. -** -** ^If the value N is out of range or if the N-th parameter is -** nameless, then NULL is returned. ^The returned string is -** always in UTF-8 encoding even if the named parameter was -** originally specified as UTF-16 in [sqlite3_prepare16()] or -** [sqlite3_prepare16_v2()]. -** -** See also: [sqlite3_bind_blob|sqlite3_bind()], -** [sqlite3_bind_parameter_count()], and -** [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); - -/* -** CAPI3REF: Index Of A Parameter With A Given Name -** -** ^Return the index of an SQL parameter given its name. ^The -** index value returned is suitable for use as the second -** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero -** is returned if no matching parameter is found. ^The parameter -** name must be given in UTF-8 even if the original statement -** was prepared from UTF-16 text using [sqlite3_prepare16_v2()]. -** -** See also: [sqlite3_bind_blob|sqlite3_bind()], -** [sqlite3_bind_parameter_count()], and -** [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); - -/* -** CAPI3REF: Reset All Bindings On A Prepared Statement -** -** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset -** the [sqlite3_bind_blob | bindings] on a [prepared statement]. -** ^Use this routine to reset all host parameters to NULL. -*/ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); - -/* -** CAPI3REF: Number Of Columns In A Result Set -** -** ^Return the number of columns in the result set returned by the -** [prepared statement]. ^This routine returns 0 if pStmt is an SQL -** statement that does not return data (for example an [UPDATE]). -** -** See also: [sqlite3_data_count()] -*/ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Column Names In A Result Set -** -** ^These routines return the name assigned to a particular column -** in the result set of a [SELECT] statement. ^The sqlite3_column_name() -** interface returns a pointer to a zero-terminated UTF-8 string -** and sqlite3_column_name16() returns a pointer to a zero-terminated -** UTF-16 string. ^The first parameter is the [prepared statement] -** that implements the [SELECT] statement. ^The second parameter is the -** column number. ^The leftmost column is number 0. -** -** ^The returned string pointer is valid until either the [prepared statement] -** is destroyed by [sqlite3_finalize()] or until the statement is automatically -** reprepared by the first call to [sqlite3_step()] for a particular run -** or until the next call to -** sqlite3_column_name() or sqlite3_column_name16() on the same column. -** -** ^If sqlite3_malloc() fails during the processing of either routine -** (for example during a conversion from UTF-8 to UTF-16) then a -** NULL pointer is returned. -** -** ^The name of a result column is the value of the "AS" clause for -** that column, if there is an AS clause. If there is no AS clause -** then the name of the column is unspecified and may change from -** one release of SQLite to the next. -*/ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); - -/* -** CAPI3REF: Source Of Data In A Query Result -** -** ^These routines provide a means to determine the database, table, and -** table column that is the origin of a particular result column in -** [SELECT] statement. -** ^The name of the database or table or column can be returned as -** either a UTF-8 or UTF-16 string. ^The _database_ routines return -** the database name, the _table_ routines return the table name, and -** the origin_ routines return the column name. -** ^The returned string is valid until the [prepared statement] is destroyed -** using [sqlite3_finalize()] or until the statement is automatically -** reprepared by the first call to [sqlite3_step()] for a particular run -** or until the same information is requested -** again in a different encoding. -** -** ^The names returned are the original un-aliased names of the -** database, table, and column. -** -** ^The first argument to these interfaces is a [prepared statement]. -** ^These functions return information about the Nth result column returned by -** the statement, where N is the second function argument. -** ^The left-most column is column 0 for these routines. -** -** ^If the Nth column returned by the statement is an expression or -** subquery and is not a column value, then all of these functions return -** NULL. ^These routine might also return NULL if a memory allocation error -** occurs. ^Otherwise, they return the name of the attached database, table, -** or column that query result column was extracted from. -** -** ^As with all other SQLite APIs, those whose names end with "16" return -** UTF-16 encoded strings and the other functions return UTF-8. -** -** ^These APIs are only available if the library was compiled with the -** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol. -** -** If two or more threads call one or more of these routines against the same -** prepared statement and column at the same time then the results are -** undefined. -** -** If two or more threads call one or more -** [sqlite3_column_database_name | column metadata interfaces] -** for the same [prepared statement] and result column -** at the same time then the results are undefined. -*/ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); - -/* -** CAPI3REF: Declared Datatype Of A Query Result -** -** ^(The first parameter is a [prepared statement]. -** If this statement is a [SELECT] statement and the Nth column of the -** returned result set of that [SELECT] is a table column (not an -** expression or subquery) then the declared type of the table -** column is returned.)^ ^If the Nth column of the result set is an -** expression or subquery, then a NULL pointer is returned. -** ^The returned string is always UTF-8 encoded. -** -** ^(For example, given the database schema: -** -** CREATE TABLE t1(c1 VARIANT); -** -** and the following statement to be compiled: -** -** SELECT c1 + 1, c1 FROM t1; -** -** this routine would return the string "VARIANT" for the second result -** column (i==1), and a NULL pointer for the first result column (i==0).)^ -** -** ^SQLite uses dynamic run-time typing. ^So just because a column -** is declared to contain a particular type does not mean that the -** data stored in that column is of the declared type. SQLite is -** strongly typed, but the typing is dynamic not static. ^Type -** is associated with individual values, not with the containers -** used to hold those values. -*/ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); - -/* -** CAPI3REF: Evaluate An SQL Statement -** -** After a [prepared statement] has been prepared using either -** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy -** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function -** must be called one or more times to evaluate the statement. -** -** The details of the behavior of the sqlite3_step() interface depend -** on whether the statement was prepared using the newer "v2" interface -** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy -** interface [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the -** new "v2" interface is recommended for new applications but the legacy -** interface will continue to be supported. -** -** ^In the legacy interface, the return value will be either [SQLITE_BUSY], -** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. -** ^With the "v2" interface, any of the other [result codes] or -** [extended result codes] might be returned as well. -** -** ^[SQLITE_BUSY] means that the database engine was unable to acquire the -** database locks it needs to do its job. ^If the statement is a [COMMIT] -** or occurs outside of an explicit transaction, then you can retry the -** statement. If the statement is not a [COMMIT] and occurs within an -** explicit transaction then you should rollback the transaction before -** continuing. -** -** ^[SQLITE_DONE] means that the statement has finished executing -** successfully. sqlite3_step() should not be called again on this virtual -** machine without first calling [sqlite3_reset()] to reset the virtual -** machine back to its initial state. -** -** ^If the SQL statement being executed returns any data, then [SQLITE_ROW] -** is returned each time a new row of data is ready for processing by the -** caller. The values may be accessed using the [column access functions]. -** sqlite3_step() is called again to retrieve the next row of data. -** -** ^[SQLITE_ERROR] means that a run-time error (such as a constraint -** violation) has occurred. sqlite3_step() should not be called again on -** the VM. More information may be found by calling [sqlite3_errmsg()]. -** ^With the legacy interface, a more specific error code (for example, -** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) -** can be obtained by calling [sqlite3_reset()] on the -** [prepared statement]. ^In the "v2" interface, -** the more specific error code is returned directly by sqlite3_step(). -** -** [SQLITE_MISUSE] means that the this routine was called inappropriately. -** Perhaps it was called on a [prepared statement] that has -** already been [sqlite3_finalize | finalized] or on one that had -** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could -** be the case that the same database connection is being used by two or -** more threads at the same moment in time. -** -** For all versions of SQLite up to and including 3.6.23.1, a call to -** [sqlite3_reset()] was required after sqlite3_step() returned anything -** other than [SQLITE_ROW] before any subsequent invocation of -** sqlite3_step(). Failure to reset the prepared statement using -** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from -** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began -** calling [sqlite3_reset()] automatically in this circumstance rather -** than returning [SQLITE_MISUSE]. This is not considered a compatibility -** break because any application that ever receives an SQLITE_MISUSE error -** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option -** can be used to restore the legacy behavior. -** -** Goofy Interface Alert: In the legacy interface, the sqlite3_step() -** API always returns a generic error code, [SQLITE_ERROR], following any -** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call -** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the -** specific [error codes] that better describes the error. -** We admit that this is a goofy design. The problem has been fixed -** with the "v2" interface. If you prepare all of your SQL statements -** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead -** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, -** then the more specific [error codes] are returned directly -** by sqlite3_step(). The use of the "v2" interface is recommended. -*/ -SQLITE_API int sqlite3_step(sqlite3_stmt*); - -/* -** CAPI3REF: Number of columns in a result set -** -** ^The sqlite3_data_count(P) interface returns the number of columns in the -** current row of the result set of [prepared statement] P. -** ^If prepared statement P does not have results ready to return -** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of -** interfaces) then sqlite3_data_count(P) returns 0. -** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. -** ^The sqlite3_data_count(P) routine returns 0 if the previous call to -** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) -** will return non-zero if previous call to [sqlite3_step](P) returned -** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] -** where it always returns zero since each step of that multi-step -** pragma returns 0 columns of data. -** -** See also: [sqlite3_column_count()] -*/ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Fundamental Datatypes -** KEYWORDS: SQLITE_TEXT -** -** ^(Every value in SQLite has one of five fundamental datatypes: -** -**
    -**
  • 64-bit signed integer -**
  • 64-bit IEEE floating point number -**
  • string -**
  • BLOB -**
  • NULL -**
)^ -** -** These constants are codes for each of those types. -** -** Note that the SQLITE_TEXT constant was also used in SQLite version 2 -** for a completely different meaning. Software that links against both -** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not -** SQLITE_TEXT. -*/ -#define SQLITE_INTEGER 1 -#define SQLITE_FLOAT 2 -#define SQLITE_BLOB 4 -#define SQLITE_NULL 5 -#ifdef SQLITE_TEXT -# undef SQLITE_TEXT -#else -# define SQLITE_TEXT 3 -#endif -#define SQLITE3_TEXT 3 - -/* -** CAPI3REF: Result Values From A Query -** KEYWORDS: {column access functions} -** -** These routines form the "result set" interface. -** -** ^These routines return information about a single column of the current -** result row of a query. ^In every case the first argument is a pointer -** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] -** that was returned from [sqlite3_prepare_v2()] or one of its variants) -** and the second argument is the index of the column for which information -** should be returned. ^The leftmost column of the result set has the index 0. -** ^The number of columns in the result can be determined using -** [sqlite3_column_count()]. -** -** If the SQL statement does not currently point to a valid row, or if the -** column index is out of range, the result is undefined. -** These routines may only be called when the most recent call to -** [sqlite3_step()] has returned [SQLITE_ROW] and neither -** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently. -** If any of these routines are called after [sqlite3_reset()] or -** [sqlite3_finalize()] or after [sqlite3_step()] has returned -** something other than [SQLITE_ROW], the results are undefined. -** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] -** are called from a different thread while any of these routines -** are pending, then the results are undefined. -** -** ^The sqlite3_column_type() routine returns the -** [SQLITE_INTEGER | datatype code] for the initial data type -** of the result column. ^The returned value is one of [SQLITE_INTEGER], -** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value -** returned by sqlite3_column_type() is only meaningful if no type -** conversions have occurred as described below. After a type conversion, -** the value returned by sqlite3_column_type() is undefined. Future -** versions of SQLite may change the behavior of sqlite3_column_type() -** following a type conversion. -** -** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() -** routine returns the number of bytes in that BLOB or string. -** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts -** the string to UTF-8 and then returns the number of bytes. -** ^If the result is a numeric value then sqlite3_column_bytes() uses -** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns -** the number of bytes in that string. -** ^If the result is NULL, then sqlite3_column_bytes() returns zero. -** -** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() -** routine returns the number of bytes in that BLOB or string. -** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts -** the string to UTF-16 and then returns the number of bytes. -** ^If the result is a numeric value then sqlite3_column_bytes16() uses -** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns -** the number of bytes in that string. -** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. -** -** ^The values returned by [sqlite3_column_bytes()] and -** [sqlite3_column_bytes16()] do not include the zero terminators at the end -** of the string. ^For clarity: the values returned by -** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of -** bytes in the string, not the number of characters. -** -** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), -** even empty strings, are always zero-terminated. ^The return -** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. -** -** ^The object returned by [sqlite3_column_value()] is an -** [unprotected sqlite3_value] object. An unprotected sqlite3_value object -** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. -** If the [unprotected sqlite3_value] object returned by -** [sqlite3_column_value()] is used in any other way, including calls -** to routines like [sqlite3_value_int()], [sqlite3_value_text()], -** or [sqlite3_value_bytes()], then the behavior is undefined. -** -** These routines attempt to convert the value where appropriate. ^For -** example, if the internal representation is FLOAT and a text result -** is requested, [sqlite3_snprintf()] is used internally to perform the -** conversion automatically. ^(The following table details the conversions -** that are applied: -** -**
-** -**
Internal
Type
Requested
Type
Conversion -** -**
NULL INTEGER Result is 0 -**
NULL FLOAT Result is 0.0 -**
NULL TEXT Result is NULL pointer -**
NULL BLOB Result is NULL pointer -**
INTEGER FLOAT Convert from integer to float -**
INTEGER TEXT ASCII rendering of the integer -**
INTEGER BLOB Same as INTEGER->TEXT -**
FLOAT INTEGER Convert from float to integer -**
FLOAT TEXT ASCII rendering of the float -**
FLOAT BLOB Same as FLOAT->TEXT -**
TEXT INTEGER Use atoi() -**
TEXT FLOAT Use atof() -**
TEXT BLOB No change -**
BLOB INTEGER Convert to TEXT then use atoi() -**
BLOB FLOAT Convert to TEXT then use atof() -**
BLOB TEXT Add a zero terminator if needed -**
-**
)^ -** -** The table above makes reference to standard C library functions atoi() -** and atof(). SQLite does not really use these functions. It has its -** own equivalent internal routines. The atoi() and atof() names are -** used in the table for brevity and because they are familiar to most -** C programmers. -** -** Note that when type conversions occur, pointers returned by prior -** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or -** sqlite3_column_text16() may be invalidated. -** Type conversions and pointer invalidations might occur -** in the following cases: -** -**
    -**
  • The initial content is a BLOB and sqlite3_column_text() or -** sqlite3_column_text16() is called. A zero-terminator might -** need to be added to the string.
  • -**
  • The initial content is UTF-8 text and sqlite3_column_bytes16() or -** sqlite3_column_text16() is called. The content must be converted -** to UTF-16.
  • -**
  • The initial content is UTF-16 text and sqlite3_column_bytes() or -** sqlite3_column_text() is called. The content must be converted -** to UTF-8.
  • -**
-** -** ^Conversions between UTF-16be and UTF-16le are always done in place and do -** not invalidate a prior pointer, though of course the content of the buffer -** that the prior pointer references will have been modified. Other kinds -** of conversion are done in place when it is possible, but sometimes they -** are not possible and in those cases prior pointers are invalidated. -** -** The safest and easiest to remember policy is to invoke these routines -** in one of the following ways: -** -**
    -**
  • sqlite3_column_text() followed by sqlite3_column_bytes()
  • -**
  • sqlite3_column_blob() followed by sqlite3_column_bytes()
  • -**
  • sqlite3_column_text16() followed by sqlite3_column_bytes16()
  • -**
-** -** In other words, you should call sqlite3_column_text(), -** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result -** into the desired format, then invoke sqlite3_column_bytes() or -** sqlite3_column_bytes16() to find the size of the result. Do not mix calls -** to sqlite3_column_text() or sqlite3_column_blob() with calls to -** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() -** with calls to sqlite3_column_bytes(). -** -** ^The pointers returned are valid until a type conversion occurs as -** described above, or until [sqlite3_step()] or [sqlite3_reset()] or -** [sqlite3_finalize()] is called. ^The memory space used to hold strings -** and BLOBs is freed automatically. Do not pass the pointers returned -** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into -** [sqlite3_free()]. -** -** ^(If a memory allocation error occurs during the evaluation of any -** of these routines, a default value is returned. The default value -** is either the integer 0, the floating point number 0.0, or a NULL -** pointer. Subsequent calls to [sqlite3_errcode()] will return -** [SQLITE_NOMEM].)^ -*/ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); - -/* -** CAPI3REF: Destroy A Prepared Statement Object -** -** ^The sqlite3_finalize() function is called to delete a [prepared statement]. -** ^If the most recent evaluation of the statement encountered no errors -** or if the statement is never been evaluated, then sqlite3_finalize() returns -** SQLITE_OK. ^If the most recent evaluation of statement S failed, then -** sqlite3_finalize(S) returns the appropriate [error code] or -** [extended error code]. -** -** ^The sqlite3_finalize(S) routine can be called at any point during -** the life cycle of [prepared statement] S: -** before statement S is ever evaluated, after -** one or more calls to [sqlite3_reset()], or after any call -** to [sqlite3_step()] regardless of whether or not the statement has -** completed execution. -** -** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. -** -** The application must finalize every [prepared statement] in order to avoid -** resource leaks. It is a grievous error for the application to try to use -** a prepared statement after it has been finalized. Any use of a prepared -** statement after it has been finalized can result in undefined and -** undesirable behavior such as segfaults and heap corruption. -*/ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Reset A Prepared Statement Object -** -** The sqlite3_reset() function is called to reset a [prepared statement] -** object back to its initial state, ready to be re-executed. -** ^Any SQL statement variables that had values bound to them using -** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. -** Use [sqlite3_clear_bindings()] to reset the bindings. -** -** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S -** back to the beginning of its program. -** -** ^If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], -** or if [sqlite3_step(S)] has never before been called on S, -** then [sqlite3_reset(S)] returns [SQLITE_OK]. -** -** ^If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S indicated an error, then -** [sqlite3_reset(S)] returns an appropriate [error code]. -** -** ^The [sqlite3_reset(S)] interface does not change the values -** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. -*/ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Create Or Redefine SQL Functions -** KEYWORDS: {function creation routines} -** KEYWORDS: {application-defined SQL function} -** KEYWORDS: {application-defined SQL functions} -** -** ^These functions (collectively known as "function creation routines") -** are used to add SQL functions or aggregates or to redefine the behavior -** of existing SQL functions or aggregates. The only differences between -** these routines are the text encoding expected for -** the second parameter (the name of the function being created) -** and the presence or absence of a destructor callback for -** the application data pointer. -** -** ^The first parameter is the [database connection] to which the SQL -** function is to be added. ^If an application uses more than one database -** connection then application-defined SQL functions must be added -** to each database connection separately. -** -** ^The second parameter is the name of the SQL function to be created or -** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 -** representation, exclusive of the zero-terminator. ^Note that the name -** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. -** ^Any attempt to create a function with a longer name -** will result in [SQLITE_MISUSE] being returned. -** -** ^The third parameter (nArg) -** is the number of arguments that the SQL function or -** aggregate takes. ^If this parameter is -1, then the SQL function or -** aggregate may take any number of arguments between 0 and the limit -** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third -** parameter is less than -1 or greater than 127 then the behavior is -** undefined. -** -** ^The fourth parameter, eTextRep, specifies what -** [SQLITE_UTF8 | text encoding] this SQL function prefers for -** its parameters. Every SQL function implementation must be able to work -** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be -** more efficient with one encoding than another. ^An application may -** invoke sqlite3_create_function() or sqlite3_create_function16() multiple -** times with the same function but with different values of eTextRep. -** ^When multiple implementations of the same function are available, SQLite -** will pick the one that involves the least amount of data conversion. -** If there is only a single implementation which does not care what text -** encoding is used, then the fourth argument should be [SQLITE_ANY]. -** -** ^(The fifth parameter is an arbitrary pointer. The implementation of the -** function can gain access to this pointer using [sqlite3_user_data()].)^ -** -** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are -** pointers to C-language functions that implement the SQL function or -** aggregate. ^A scalar SQL function requires an implementation of the xFunc -** callback only; NULL pointers must be passed as the xStep and xFinal -** parameters. ^An aggregate SQL function requires an implementation of xStep -** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing -** SQL function or aggregate, pass NULL pointers for all three function -** callbacks. -** -** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL, -** then it is destructor for the application data pointer. -** The destructor is invoked when the function is deleted, either by being -** overloaded or when the database connection closes.)^ -** ^The destructor is also invoked if the call to -** sqlite3_create_function_v2() fails. -** ^When the destructor callback of the tenth parameter is invoked, it -** is passed a single argument which is a copy of the application data -** pointer which was the fifth parameter to sqlite3_create_function_v2(). -** -** ^It is permitted to register multiple implementations of the same -** functions with the same name but with either differing numbers of -** arguments or differing preferred text encodings. ^SQLite will use -** the implementation that most closely matches the way in which the -** SQL function is used. ^A function implementation with a non-negative -** nArg parameter is a better match than a function implementation with -** a negative nArg. ^A function where the preferred text encoding -** matches the database encoding is a better -** match than a function where the encoding is different. -** ^A function where the encoding difference is between UTF16le and UTF16be -** is a closer match than a function where the encoding difference is -** between UTF8 and UTF16. -** -** ^Built-in functions may be overloaded by new application-defined functions. -** -** ^An application-defined function is permitted to call other -** SQLite interfaces. However, such calls must not -** close the database connection nor finalize or reset the prepared -** statement in which the function is running. -*/ -SQLITE_API int sqlite3_create_function( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -); -SQLITE_API int sqlite3_create_function16( - sqlite3 *db, - const void *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -); -SQLITE_API int sqlite3_create_function_v2( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*), - void(*xDestroy)(void*) -); - -/* -** CAPI3REF: Text Encodings -** -** These constant define integer codes that represent the various -** text encodings supported by SQLite. -*/ -#define SQLITE_UTF8 1 -#define SQLITE_UTF16LE 2 -#define SQLITE_UTF16BE 3 -#define SQLITE_UTF16 4 /* Use native byte order */ -#define SQLITE_ANY 5 /* sqlite3_create_function only */ -#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ - -/* -** CAPI3REF: Deprecated Functions -** DEPRECATED -** -** These functions are [deprecated]. In order to maintain -** backwards compatibility with older code, these functions continue -** to be supported. However, new applications should avoid -** the use of these functions. To help encourage people to avoid -** using these functions, we are not going to tell you what they do. -*/ -#ifndef SQLITE_OMIT_DEPRECATED -SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); -SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); -SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), - void*,sqlite3_int64); -#endif - -/* -** CAPI3REF: Obtaining SQL Function Parameter Values -** -** The C-language implementation of SQL functions and aggregates uses -** this set of interface routines to access the parameter values on -** the function or aggregate. -** -** The xFunc (for scalar functions) or xStep (for aggregates) parameters -** to [sqlite3_create_function()] and [sqlite3_create_function16()] -** define callbacks that implement the SQL functions and aggregates. -** The 3rd parameter to these callbacks is an array of pointers to -** [protected sqlite3_value] objects. There is one [sqlite3_value] object for -** each parameter to the SQL function. These routines are used to -** extract values from the [sqlite3_value] objects. -** -** These routines work only with [protected sqlite3_value] objects. -** Any attempt to use these routines on an [unprotected sqlite3_value] -** object results in undefined behavior. -** -** ^These routines work just like the corresponding [column access functions] -** except that these routines take a single [protected sqlite3_value] object -** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. -** -** ^The sqlite3_value_text16() interface extracts a UTF-16 string -** in the native byte-order of the host machine. ^The -** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces -** extract UTF-16 strings as big-endian and little-endian respectively. -** -** ^(The sqlite3_value_numeric_type() interface attempts to apply -** numeric affinity to the value. This means that an attempt is -** made to convert the value to an integer or floating point. If -** such a conversion is possible without loss of information (in other -** words, if the value is a string that looks like a number) -** then the conversion is performed. Otherwise no conversion occurs. -** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ -** -** Please pay particular attention to the fact that the pointer returned -** from [sqlite3_value_blob()], [sqlite3_value_text()], or -** [sqlite3_value_text16()] can be invalidated by a subsequent call to -** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], -** or [sqlite3_value_text16()]. -** -** These routines must be called from the same thread as -** the SQL function that supplied the [sqlite3_value*] parameters. -*/ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); -SQLITE_API double sqlite3_value_double(sqlite3_value*); -SQLITE_API int sqlite3_value_int(sqlite3_value*); -SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); -SQLITE_API int sqlite3_value_type(sqlite3_value*); -SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); - -/* -** CAPI3REF: Obtain Aggregate Function Context -** -** Implementations of aggregate SQL functions use this -** routine to allocate memory for storing their state. -** -** ^The first time the sqlite3_aggregate_context(C,N) routine is called -** for a particular aggregate function, SQLite -** allocates N of memory, zeroes out that memory, and returns a pointer -** to the new memory. ^On second and subsequent calls to -** sqlite3_aggregate_context() for the same aggregate function instance, -** the same buffer is returned. Sqlite3_aggregate_context() is normally -** called once for each invocation of the xStep callback and then one -** last time when the xFinal callback is invoked. ^(When no rows match -** an aggregate query, the xStep() callback of the aggregate function -** implementation is never called and xFinal() is called exactly once. -** In those cases, sqlite3_aggregate_context() might be called for the -** first time from within xFinal().)^ -** -** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer -** when first called if N is less than or equal to zero or if a memory -** allocate error occurs. -** -** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is -** determined by the N parameter on first successful call. Changing the -** value of N in subsequent call to sqlite3_aggregate_context() within -** the same aggregate function instance will not resize the memory -** allocation.)^ Within the xFinal callback, it is customary to set -** N=0 in calls to sqlite3_aggregate_context(C,N) so that no -** pointless memory allocations occur. -** -** ^SQLite automatically frees the memory allocated by -** sqlite3_aggregate_context() when the aggregate query concludes. -** -** The first parameter must be a copy of the -** [sqlite3_context | SQL function context] that is the first parameter -** to the xStep or xFinal callback routine that implements the aggregate -** function. -** -** This routine must be called from the same thread in which -** the aggregate SQL function is running. -*/ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); - -/* -** CAPI3REF: User Data For Functions -** -** ^The sqlite3_user_data() interface returns a copy of -** the pointer that was the pUserData parameter (the 5th parameter) -** of the [sqlite3_create_function()] -** and [sqlite3_create_function16()] routines that originally -** registered the application defined function. -** -** This routine must be called from the same thread in which -** the application-defined function is running. -*/ -SQLITE_API void *sqlite3_user_data(sqlite3_context*); - -/* -** CAPI3REF: Database Connection For Functions -** -** ^The sqlite3_context_db_handle() interface returns a copy of -** the pointer to the [database connection] (the 1st parameter) -** of the [sqlite3_create_function()] -** and [sqlite3_create_function16()] routines that originally -** registered the application defined function. -*/ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); - -/* -** CAPI3REF: Function Auxiliary Data -** -** The following two functions may be used by scalar SQL functions to -** associate metadata with argument values. If the same value is passed to -** multiple invocations of the same SQL function during query execution, under -** some circumstances the associated metadata may be preserved. This may -** be used, for example, to add a regular-expression matching scalar -** function. The compiled version of the regular expression is stored as -** metadata associated with the SQL value passed as the regular expression -** pattern. The compiled regular expression can be reused on multiple -** invocations of the same function so that the original pattern string -** does not need to be recompiled on each invocation. -** -** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata -** associated by the sqlite3_set_auxdata() function with the Nth argument -** value to the application-defined function. ^If no metadata has been ever -** been set for the Nth argument of the function, or if the corresponding -** function parameter has changed since the meta-data was set, -** then sqlite3_get_auxdata() returns a NULL pointer. -** -** ^The sqlite3_set_auxdata() interface saves the metadata -** pointed to by its 3rd parameter as the metadata for the N-th -** argument of the application-defined function. Subsequent -** calls to sqlite3_get_auxdata() might return this data, if it has -** not been destroyed. -** ^If it is not NULL, SQLite will invoke the destructor -** function given by the 4th parameter to sqlite3_set_auxdata() on -** the metadata when the corresponding function parameter changes -** or when the SQL statement completes, whichever comes first. -** -** SQLite is free to call the destructor and drop metadata on any -** parameter of any function at any time. ^The only guarantee is that -** the destructor will be called before the metadata is dropped. -** -** ^(In practice, metadata is preserved between function calls for -** expressions that are constant at compile time. This includes literal -** values and [parameters].)^ -** -** These routines must be called from the same thread in which -** the SQL function is running. -*/ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); -SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); - - -/* -** CAPI3REF: Constants Defining Special Destructor Behavior -** -** These are special values for the destructor that is passed in as the -** final argument to routines like [sqlite3_result_blob()]. ^If the destructor -** argument is SQLITE_STATIC, it means that the content pointer is constant -** and will never change. It does not need to be destroyed. ^The -** SQLITE_TRANSIENT value means that the content will likely change in -** the near future and that SQLite should make its own private copy of -** the content before returning. -** -** The typedef is necessary to work around problems in certain -** C++ compilers. See ticket #2191. -*/ -typedef void (*sqlite3_destructor_type)(void*); -#define SQLITE_STATIC ((sqlite3_destructor_type)0) -#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) - -/* -** CAPI3REF: Setting The Result Of An SQL Function -** -** These routines are used by the xFunc or xFinal callbacks that -** implement SQL functions and aggregates. See -** [sqlite3_create_function()] and [sqlite3_create_function16()] -** for additional information. -** -** These functions work very much like the [parameter binding] family of -** functions used to bind values to host parameters in prepared statements. -** Refer to the [SQL parameter] documentation for additional information. -** -** ^The sqlite3_result_blob() interface sets the result from -** an application-defined function to be the BLOB whose content is pointed -** to by the second parameter and which is N bytes long where N is the -** third parameter. -** -** ^The sqlite3_result_zeroblob() interfaces set the result of -** the application-defined function to be a BLOB containing all zero -** bytes and N bytes in size, where N is the value of the 2nd parameter. -** -** ^The sqlite3_result_double() interface sets the result from -** an application-defined function to be a floating point value specified -** by its 2nd argument. -** -** ^The sqlite3_result_error() and sqlite3_result_error16() functions -** cause the implemented SQL function to throw an exception. -** ^SQLite uses the string pointed to by the -** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() -** as the text of an error message. ^SQLite interprets the error -** message string from sqlite3_result_error() as UTF-8. ^SQLite -** interprets the string from sqlite3_result_error16() as UTF-16 in native -** byte order. ^If the third parameter to sqlite3_result_error() -** or sqlite3_result_error16() is negative then SQLite takes as the error -** message all text up through the first zero character. -** ^If the third parameter to sqlite3_result_error() or -** sqlite3_result_error16() is non-negative then SQLite takes that many -** bytes (not characters) from the 2nd parameter as the error message. -** ^The sqlite3_result_error() and sqlite3_result_error16() -** routines make a private copy of the error message text before -** they return. Hence, the calling function can deallocate or -** modify the text after they return without harm. -** ^The sqlite3_result_error_code() function changes the error code -** returned by SQLite as a result of an error in a function. ^By default, -** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() -** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. -** -** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an -** error indicating that a string or BLOB is too long to represent. -** -** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an -** error indicating that a memory allocation failed. -** -** ^The sqlite3_result_int() interface sets the return value -** of the application-defined function to be the 32-bit signed integer -** value given in the 2nd argument. -** ^The sqlite3_result_int64() interface sets the return value -** of the application-defined function to be the 64-bit signed integer -** value given in the 2nd argument. -** -** ^The sqlite3_result_null() interface sets the return value -** of the application-defined function to be NULL. -** -** ^The sqlite3_result_text(), sqlite3_result_text16(), -** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces -** set the return value of the application-defined function to be -** a text string which is represented as UTF-8, UTF-16 native byte order, -** UTF-16 little endian, or UTF-16 big endian, respectively. -** ^SQLite takes the text result from the application from -** the 2nd parameter of the sqlite3_result_text* interfaces. -** ^If the 3rd parameter to the sqlite3_result_text* interfaces -** is negative, then SQLite takes result text from the 2nd parameter -** through the first zero character. -** ^If the 3rd parameter to the sqlite3_result_text* interfaces -** is non-negative, then as many bytes (not characters) of the text -** pointed to by the 2nd parameter are taken as the application-defined -** function result. If the 3rd parameter is non-negative, then it -** must be the byte offset into the string where the NUL terminator would -** appear if the string where NUL terminated. If any NUL characters occur -** in the string at a byte offset that is less than the value of the 3rd -** parameter, then the resulting string will contain embedded NULs and the -** result of expressions operating on strings with embedded NULs is undefined. -** ^If the 4th parameter to the sqlite3_result_text* interfaces -** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that -** function as the destructor on the text or BLOB result when it has -** finished using that result. -** ^If the 4th parameter to the sqlite3_result_text* interfaces or to -** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite -** assumes that the text or BLOB result is in constant space and does not -** copy the content of the parameter nor call a destructor on the content -** when it has finished using that result. -** ^If the 4th parameter to the sqlite3_result_text* interfaces -** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT -** then SQLite makes a copy of the result into space obtained from -** from [sqlite3_malloc()] before it returns. -** -** ^The sqlite3_result_value() interface sets the result of -** the application-defined function to be a copy the -** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The -** sqlite3_result_value() interface makes a copy of the [sqlite3_value] -** so that the [sqlite3_value] specified in the parameter may change or -** be deallocated after sqlite3_result_value() returns without harm. -** ^A [protected sqlite3_value] object may always be used where an -** [unprotected sqlite3_value] object is required, so either -** kind of [sqlite3_value] object can be used with this interface. -** -** If these routines are called from within the different thread -** than the one containing the application-defined function that received -** the [sqlite3_context] pointer, the results are undefined. -*/ -SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_double(sqlite3_context*, double); -SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); -SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); -SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); -SQLITE_API void sqlite3_result_null(sqlite3_context*); -SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); - -/* -** CAPI3REF: Define New Collating Sequences -** -** ^These functions add, remove, or modify a [collation] associated -** with the [database connection] specified as the first argument. -** -** ^The name of the collation is a UTF-8 string -** for sqlite3_create_collation() and sqlite3_create_collation_v2() -** and a UTF-16 string in native byte order for sqlite3_create_collation16(). -** ^Collation names that compare equal according to [sqlite3_strnicmp()] are -** considered to be the same name. -** -** ^(The third argument (eTextRep) must be one of the constants: -**
    -**
  • [SQLITE_UTF8], -**
  • [SQLITE_UTF16LE], -**
  • [SQLITE_UTF16BE], -**
  • [SQLITE_UTF16], or -**
  • [SQLITE_UTF16_ALIGNED]. -**
)^ -** ^The eTextRep argument determines the encoding of strings passed -** to the collating function callback, xCallback. -** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep -** force strings to be UTF16 with native byte order. -** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin -** on an even byte address. -** -** ^The fourth argument, pArg, is an application data pointer that is passed -** through as the first argument to the collating function callback. -** -** ^The fifth argument, xCallback, is a pointer to the collating function. -** ^Multiple collating functions can be registered using the same name but -** with different eTextRep parameters and SQLite will use whichever -** function requires the least amount of data transformation. -** ^If the xCallback argument is NULL then the collating function is -** deleted. ^When all collating functions having the same name are deleted, -** that collation is no longer usable. -** -** ^The collating function callback is invoked with a copy of the pArg -** application data pointer and with two strings in the encoding specified -** by the eTextRep argument. The collating function must return an -** integer that is negative, zero, or positive -** if the first string is less than, equal to, or greater than the second, -** respectively. A collating function must always return the same answer -** given the same inputs. If two or more collating functions are registered -** to the same collation name (using different eTextRep values) then all -** must give an equivalent answer when invoked with equivalent strings. -** The collating function must obey the following properties for all -** strings A, B, and C: -** -**
    -**
  1. If A==B then B==A. -**
  2. If A==B and B==C then A==C. -**
  3. If A<B THEN B>A. -**
  4. If A<B and B<C then A<C. -**
-** -** If a collating function fails any of the above constraints and that -** collating function is registered and used, then the behavior of SQLite -** is undefined. -** -** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() -** with the addition that the xDestroy callback is invoked on pArg when -** the collating function is deleted. -** ^Collating functions are deleted when they are overridden by later -** calls to the collation creation functions or when the -** [database connection] is closed using [sqlite3_close()]. -** -** ^The xDestroy callback is not called if the -** sqlite3_create_collation_v2() function fails. Applications that invoke -** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should -** check the return code and dispose of the application data pointer -** themselves rather than expecting SQLite to deal with it for them. -** This is different from every other SQLite interface. The inconsistency -** is unfortunate but cannot be changed without breaking backwards -** compatibility. -** -** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. -*/ -SQLITE_API int sqlite3_create_collation( - sqlite3*, - const char *zName, - int eTextRep, - void *pArg, - int(*xCompare)(void*,int,const void*,int,const void*) -); -SQLITE_API int sqlite3_create_collation_v2( - sqlite3*, - const char *zName, - int eTextRep, - void *pArg, - int(*xCompare)(void*,int,const void*,int,const void*), - void(*xDestroy)(void*) -); -SQLITE_API int sqlite3_create_collation16( - sqlite3*, - const void *zName, - int eTextRep, - void *pArg, - int(*xCompare)(void*,int,const void*,int,const void*) -); - -/* -** CAPI3REF: Collation Needed Callbacks -** -** ^To avoid having to register all collation sequences before a database -** can be used, a single callback function may be registered with the -** [database connection] to be invoked whenever an undefined collation -** sequence is required. -** -** ^If the function is registered using the sqlite3_collation_needed() API, -** then it is passed the names of undefined collation sequences as strings -** encoded in UTF-8. ^If sqlite3_collation_needed16() is used, -** the names are passed as UTF-16 in machine native byte order. -** ^A call to either function replaces the existing collation-needed callback. -** -** ^(When the callback is invoked, the first argument passed is a copy -** of the second argument to sqlite3_collation_needed() or -** sqlite3_collation_needed16(). The second argument is the database -** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], -** or [SQLITE_UTF16LE], indicating the most desirable form of the collation -** sequence function required. The fourth parameter is the name of the -** required collation sequence.)^ -** -** The callback function should register the desired collation using -** [sqlite3_create_collation()], [sqlite3_create_collation16()], or -** [sqlite3_create_collation_v2()]. -*/ -SQLITE_API int sqlite3_collation_needed( - sqlite3*, - void*, - void(*)(void*,sqlite3*,int eTextRep,const char*) -); -SQLITE_API int sqlite3_collation_needed16( - sqlite3*, - void*, - void(*)(void*,sqlite3*,int eTextRep,const void*) -); - -#ifdef SQLITE_HAS_CODEC -/* -** Specify the key for an encrypted database. This routine should be -** called right after sqlite3_open(). -** -** The code to implement this API is not available in the public release -** of SQLite. -*/ -SQLITE_API int sqlite3_key( - sqlite3 *db, /* Database to be rekeyed */ - const void *pKey, int nKey /* The key */ -); - -/* -** Change the key on an open database. If the current database is not -** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the -** database is decrypted. -** -** The code to implement this API is not available in the public release -** of SQLite. -*/ -SQLITE_API int sqlite3_rekey( - sqlite3 *db, /* Database to be rekeyed */ - const void *pKey, int nKey /* The new key */ -); - -/* -** Specify the activation key for a SEE database. Unless -** activated, none of the SEE routines will work. -*/ -SQLITE_API void sqlite3_activate_see( - const char *zPassPhrase /* Activation phrase */ -); -#endif - -#ifdef SQLITE_ENABLE_CEROD -/* -** Specify the activation key for a CEROD database. Unless -** activated, none of the CEROD routines will work. -*/ -SQLITE_API void sqlite3_activate_cerod( - const char *zPassPhrase /* Activation phrase */ -); -#endif - -/* -** CAPI3REF: Suspend Execution For A Short Time -** -** The sqlite3_sleep() function causes the current thread to suspend execution -** for at least a number of milliseconds specified in its parameter. -** -** If the operating system does not support sleep requests with -** millisecond time resolution, then the time will be rounded up to -** the nearest second. The number of milliseconds of sleep actually -** requested from the operating system is returned. -** -** ^SQLite implements this interface by calling the xSleep() -** method of the default [sqlite3_vfs] object. If the xSleep() method -** of the default VFS is not implemented correctly, or not implemented at -** all, then the behavior of sqlite3_sleep() may deviate from the description -** in the previous paragraphs. -*/ -SQLITE_API int sqlite3_sleep(int); - -/* -** CAPI3REF: Name Of The Folder Holding Temporary Files -** -** ^(If this global variable is made to point to a string which is -** the name of a folder (a.k.a. directory), then all temporary files -** created by SQLite when using a built-in [sqlite3_vfs | VFS] -** will be placed in that directory.)^ ^If this variable -** is a NULL pointer, then SQLite performs a search for an appropriate -** temporary file directory. -** -** It is not safe to read or modify this variable in more than one -** thread at a time. It is not safe to read or modify this variable -** if a [database connection] is being used at the same time in a separate -** thread. -** It is intended that this variable be set once -** as part of process initialization and before any SQLite interface -** routines have been called and that this variable remain unchanged -** thereafter. -** -** ^The [temp_store_directory pragma] may modify this variable and cause -** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, -** the [temp_store_directory pragma] always assumes that any string -** that this variable points to is held in memory obtained from -** [sqlite3_malloc] and the pragma may attempt to free that memory -** using [sqlite3_free]. -** Hence, if this variable is modified directly, either it should be -** made NULL or made to point to memory obtained from [sqlite3_malloc] -** or else the use of the [temp_store_directory pragma] should be avoided. -** -** Note to Windows Runtime users: The temporary directory must be set -** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various -** features that require the use of temporary files may fail. Here is an -** example of how to do this using C++ with the Windows Runtime: -** -**
-** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
-**       TemporaryFolder->Path->Data();
-** char zPathBuf[MAX_PATH + 1];
-** memset(zPathBuf, 0, sizeof(zPathBuf));
-** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
-**       NULL, NULL);
-** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
-** 
-*/ -SQLITE_API char *sqlite3_temp_directory; - -/* -** CAPI3REF: Name Of The Folder Holding Database Files -** -** ^(If this global variable is made to point to a string which is -** the name of a folder (a.k.a. directory), then all database files -** specified with a relative pathname and created or accessed by -** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed -** to be relative to that directory.)^ ^If this variable is a NULL -** pointer, then SQLite assumes that all database files specified -** with a relative pathname are relative to the current directory -** for the process. Only the windows VFS makes use of this global -** variable; it is ignored by the unix VFS. -** -** Changing the value of this variable while a database connection is -** open can result in a corrupt database. -** -** It is not safe to read or modify this variable in more than one -** thread at a time. It is not safe to read or modify this variable -** if a [database connection] is being used at the same time in a separate -** thread. -** It is intended that this variable be set once -** as part of process initialization and before any SQLite interface -** routines have been called and that this variable remain unchanged -** thereafter. -** -** ^The [data_store_directory pragma] may modify this variable and cause -** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, -** the [data_store_directory pragma] always assumes that any string -** that this variable points to is held in memory obtained from -** [sqlite3_malloc] and the pragma may attempt to free that memory -** using [sqlite3_free]. -** Hence, if this variable is modified directly, either it should be -** made NULL or made to point to memory obtained from [sqlite3_malloc] -** or else the use of the [data_store_directory pragma] should be avoided. -*/ -SQLITE_API char *sqlite3_data_directory; - -/* -** CAPI3REF: Test For Auto-Commit Mode -** KEYWORDS: {autocommit mode} -** -** ^The sqlite3_get_autocommit() interface returns non-zero or -** zero if the given database connection is or is not in autocommit mode, -** respectively. ^Autocommit mode is on by default. -** ^Autocommit mode is disabled by a [BEGIN] statement. -** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. -** -** If certain kinds of errors occur on a statement within a multi-statement -** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], -** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the -** transaction might be rolled back automatically. The only way to -** find out whether SQLite automatically rolled back the transaction after -** an error is to use this function. -** -** If another thread changes the autocommit status of the database -** connection while this routine is running, then the return value -** is undefined. -*/ -SQLITE_API int sqlite3_get_autocommit(sqlite3*); - -/* -** CAPI3REF: Find The Database Handle Of A Prepared Statement -** -** ^The sqlite3_db_handle interface returns the [database connection] handle -** to which a [prepared statement] belongs. ^The [database connection] -** returned by sqlite3_db_handle is the same [database connection] -** that was the first argument -** to the [sqlite3_prepare_v2()] call (or its variants) that was used to -** create the statement in the first place. -*/ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); - -/* -** CAPI3REF: Return The Filename For A Database Connection -** -** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename -** associated with database N of connection D. ^The main database file -** has the name "main". If there is no attached database N on the database -** connection D, or if database N is a temporary or in-memory database, then -** a NULL pointer is returned. -** -** ^The filename returned by this function is the output of the -** xFullPathname method of the [VFS]. ^In other words, the filename -** will be an absolute pathname, even if the filename used -** to open the database originally was a URI or relative pathname. -*/ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); - -/* -** CAPI3REF: Determine if a database is read-only -** -** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N -** of connection D is read-only, 0 if it is read/write, or -1 if N is not -** the name of a database on connection D. -*/ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); - -/* -** CAPI3REF: Find the next prepared statement -** -** ^This interface returns a pointer to the next [prepared statement] after -** pStmt associated with the [database connection] pDb. ^If pStmt is NULL -** then this interface returns a pointer to the first prepared statement -** associated with the database connection pDb. ^If no prepared statement -** satisfies the conditions of this routine, it returns NULL. -** -** The [database connection] pointer D in a call to -** [sqlite3_next_stmt(D,S)] must refer to an open database -** connection and in particular must not be a NULL pointer. -*/ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Commit And Rollback Notification Callbacks -** -** ^The sqlite3_commit_hook() interface registers a callback -** function to be invoked whenever a transaction is [COMMIT | committed]. -** ^Any callback set by a previous call to sqlite3_commit_hook() -** for the same database connection is overridden. -** ^The sqlite3_rollback_hook() interface registers a callback -** function to be invoked whenever a transaction is [ROLLBACK | rolled back]. -** ^Any callback set by a previous call to sqlite3_rollback_hook() -** for the same database connection is overridden. -** ^The pArg argument is passed through to the callback. -** ^If the callback on a commit hook function returns non-zero, -** then the commit is converted into a rollback. -** -** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions -** return the P argument from the previous call of the same function -** on the same [database connection] D, or NULL for -** the first call for each function on D. -** -** The commit and rollback hook callbacks are not reentrant. -** The callback implementation must not do anything that will modify -** the database connection that invoked the callback. Any actions -** to modify the database connection must be deferred until after the -** completion of the [sqlite3_step()] call that triggered the commit -** or rollback hook in the first place. -** Note that running any other SQL statements, including SELECT statements, -** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify -** the database connections for the meaning of "modify" in this paragraph. -** -** ^Registering a NULL function disables the callback. -** -** ^When the commit hook callback routine returns zero, the [COMMIT] -** operation is allowed to continue normally. ^If the commit hook -** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK]. -** ^The rollback hook is invoked on a rollback that results from a commit -** hook returning non-zero, just as it would be with any other rollback. -** -** ^For the purposes of this API, a transaction is said to have been -** rolled back if an explicit "ROLLBACK" statement is executed, or -** an error or constraint causes an implicit rollback to occur. -** ^The rollback callback is not invoked if a transaction is -** automatically rolled back because the database connection is closed. -** -** See also the [sqlite3_update_hook()] interface. -*/ -SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); -SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); - -/* -** CAPI3REF: Data Change Notification Callbacks -** -** ^The sqlite3_update_hook() interface registers a callback function -** with the [database connection] identified by the first argument -** to be invoked whenever a row is updated, inserted or deleted. -** ^Any callback set by a previous call to this function -** for the same database connection is overridden. -** -** ^The second argument is a pointer to the function to invoke when a -** row is updated, inserted or deleted. -** ^The first argument to the callback is a copy of the third argument -** to sqlite3_update_hook(). -** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], -** or [SQLITE_UPDATE], depending on the operation that caused the callback -** to be invoked. -** ^The third and fourth arguments to the callback contain pointers to the -** database and table name containing the affected row. -** ^The final callback parameter is the [rowid] of the row. -** ^In the case of an update, this is the [rowid] after the update takes place. -** -** ^(The update hook is not invoked when internal system tables are -** modified (i.e. sqlite_master and sqlite_sequence).)^ -** -** ^In the current implementation, the update hook -** is not invoked when duplication rows are deleted because of an -** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook -** invoked when rows are deleted using the [truncate optimization]. -** The exceptions defined in this paragraph might change in a future -** release of SQLite. -** -** The update hook implementation must not do anything that will modify -** the database connection that invoked the update hook. Any actions -** to modify the database connection must be deferred until after the -** completion of the [sqlite3_step()] call that triggered the update hook. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. -** -** ^The sqlite3_update_hook(D,C,P) function -** returns the P argument from the previous call -** on the same [database connection] D, or NULL for -** the first call on D. -** -** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] -** interfaces. -*/ -SQLITE_API void *sqlite3_update_hook( - sqlite3*, - void(*)(void *,int ,char const *,char const *,sqlite3_int64), - void* -); - -/* -** CAPI3REF: Enable Or Disable Shared Pager Cache -** -** ^(This routine enables or disables the sharing of the database cache -** and schema data structures between [database connection | connections] -** to the same database. Sharing is enabled if the argument is true -** and disabled if the argument is false.)^ -** -** ^Cache sharing is enabled and disabled for an entire process. -** This is a change as of SQLite version 3.5.0. In prior versions of SQLite, -** sharing was enabled or disabled for each thread separately. -** -** ^(The cache sharing mode set by this interface effects all subsequent -** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. -** Existing database connections continue use the sharing mode -** that was in effect at the time they were opened.)^ -** -** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled -** successfully. An [error code] is returned otherwise.)^ -** -** ^Shared cache is disabled by default. But this might change in -** future releases of SQLite. Applications that care about shared -** cache setting should set it explicitly. -** -** This interface is threadsafe on processors where writing a -** 32-bit integer is atomic. -** -** See Also: [SQLite Shared-Cache Mode] -*/ -SQLITE_API int sqlite3_enable_shared_cache(int); - -/* -** CAPI3REF: Attempt To Free Heap Memory -** -** ^The sqlite3_release_memory() interface attempts to free N bytes -** of heap memory by deallocating non-essential memory allocations -** held by the database library. Memory used to cache database -** pages to improve performance is an example of non-essential memory. -** ^sqlite3_release_memory() returns the number of bytes actually freed, -** which might be more or less than the amount requested. -** ^The sqlite3_release_memory() routine is a no-op returning zero -** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. -** -** See also: [sqlite3_db_release_memory()] -*/ -SQLITE_API int sqlite3_release_memory(int); - -/* -** CAPI3REF: Free Memory Used By A Database Connection -** -** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap -** memory as possible from database connection D. Unlike the -** [sqlite3_release_memory()] interface, this interface is effect even -** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is -** omitted. -** -** See also: [sqlite3_release_memory()] -*/ -SQLITE_API int sqlite3_db_release_memory(sqlite3*); - -/* -** CAPI3REF: Impose A Limit On Heap Size -** -** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the -** soft limit on the amount of heap memory that may be allocated by SQLite. -** ^SQLite strives to keep heap memory utilization below the soft heap -** limit by reducing the number of pages held in the page cache -** as heap memory usages approaches the limit. -** ^The soft heap limit is "soft" because even though SQLite strives to stay -** below the limit, it will exceed the limit rather than generate -** an [SQLITE_NOMEM] error. In other words, the soft heap limit -** is advisory only. -** -** ^The return value from sqlite3_soft_heap_limit64() is the size of -** the soft heap limit prior to the call, or negative in the case of an -** error. ^If the argument N is negative -** then no change is made to the soft heap limit. Hence, the current -** size of the soft heap limit can be determined by invoking -** sqlite3_soft_heap_limit64() with a negative argument. -** -** ^If the argument N is zero then the soft heap limit is disabled. -** -** ^(The soft heap limit is not enforced in the current implementation -** if one or more of following conditions are true: -** -**
    -**
  • The soft heap limit is set to zero. -**
  • Memory accounting is disabled using a combination of the -** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and -** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. -**
  • An alternative page cache implementation is specified using -** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). -**
  • The page cache allocates from its own memory pool supplied -** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than -** from the heap. -**
)^ -** -** Beginning with SQLite version 3.7.3, the soft heap limit is enforced -** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT] -** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT], -** the soft heap limit is enforced on every memory allocation. Without -** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced -** when memory is allocated by the page cache. Testing suggests that because -** the page cache is the predominate memory user in SQLite, most -** applications will achieve adequate soft heap limit enforcement without -** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. -** -** The circumstances under which SQLite will enforce the soft heap limit may -** changes in future releases of SQLite. -*/ -SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); - -/* -** CAPI3REF: Deprecated Soft Heap Limit Interface -** DEPRECATED -** -** This is a deprecated version of the [sqlite3_soft_heap_limit64()] -** interface. This routine is provided for historical compatibility -** only. All new applications should use the -** [sqlite3_soft_heap_limit64()] interface rather than this one. -*/ -SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); - - -/* -** CAPI3REF: Extract Metadata About A Column Of A Table -** -** ^This routine returns metadata about a specific column of a specific -** database table accessible using the [database connection] handle -** passed as the first function argument. -** -** ^The column is identified by the second, third and fourth parameters to -** this function. ^The second parameter is either the name of the database -** (i.e. "main", "temp", or an attached database) containing the specified -** table or NULL. ^If it is NULL, then all attached databases are searched -** for the table using the same algorithm used by the database engine to -** resolve unqualified table references. -** -** ^The third and fourth parameters to this function are the table and column -** name of the desired column, respectively. Neither of these parameters -** may be NULL. -** -** ^Metadata is returned by writing to the memory locations passed as the 5th -** and subsequent parameters to this function. ^Any of these arguments may be -** NULL, in which case the corresponding element of metadata is omitted. -** -** ^(
-** -**
Parameter Output
Type
Description -** -**
5th const char* Data type -**
6th const char* Name of default collation sequence -**
7th int True if column has a NOT NULL constraint -**
8th int True if column is part of the PRIMARY KEY -**
9th int True if column is [AUTOINCREMENT] -**
-**
)^ -** -** ^The memory pointed to by the character pointers returned for the -** declaration type and collation sequence is valid only until the next -** call to any SQLite API function. -** -** ^If the specified table is actually a view, an [error code] is returned. -** -** ^If the specified column is "rowid", "oid" or "_rowid_" and an -** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output -** parameters are set for the explicitly declared column. ^(If there is no -** explicitly declared [INTEGER PRIMARY KEY] column, then the output -** parameters are set as follows: -** -**
-**     data type: "INTEGER"
-**     collation sequence: "BINARY"
-**     not null: 0
-**     primary key: 1
-**     auto increment: 0
-** 
)^ -** -** ^(This function may load one or more schemas from database files. If an -** error occurs during this process, or if the requested table or column -** cannot be found, an [error code] is returned and an error message left -** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^ -** -** ^This API is only available if the library was compiled with the -** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. -*/ -SQLITE_API int sqlite3_table_column_metadata( - sqlite3 *db, /* Connection handle */ - const char *zDbName, /* Database name or NULL */ - const char *zTableName, /* Table name */ - const char *zColumnName, /* Column name */ - char const **pzDataType, /* OUTPUT: Declared data type */ - char const **pzCollSeq, /* OUTPUT: Collation sequence name */ - int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ - int *pPrimaryKey, /* OUTPUT: True if column part of PK */ - int *pAutoinc /* OUTPUT: True if column is auto-increment */ -); - -/* -** CAPI3REF: Load An Extension -** -** ^This interface loads an SQLite extension library from the named file. -** -** ^The sqlite3_load_extension() interface attempts to load an -** SQLite extension library contained in the file zFile. -** -** ^The entry point is zProc. -** ^zProc may be 0, in which case the name of the entry point -** defaults to "sqlite3_extension_init". -** ^The sqlite3_load_extension() interface returns -** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. -** ^If an error occurs and pzErrMsg is not 0, then the -** [sqlite3_load_extension()] interface shall attempt to -** fill *pzErrMsg with error message text stored in memory -** obtained from [sqlite3_malloc()]. The calling function -** should free this memory by calling [sqlite3_free()]. -** -** ^Extension loading must be enabled using -** [sqlite3_enable_load_extension()] prior to calling this API, -** otherwise an error will be returned. -** -** See also the [load_extension() SQL function]. -*/ -SQLITE_API int sqlite3_load_extension( - sqlite3 *db, /* Load the extension into this database connection */ - const char *zFile, /* Name of the shared library containing extension */ - const char *zProc, /* Entry point. Derived from zFile if 0 */ - char **pzErrMsg /* Put error message here if not 0 */ -); - -/* -** CAPI3REF: Enable Or Disable Extension Loading -** -** ^So as not to open security holes in older applications that are -** unprepared to deal with extension loading, and as a means of disabling -** extension loading while evaluating user-entered SQL, the following API -** is provided to turn the [sqlite3_load_extension()] mechanism on and off. -** -** ^Extension loading is off by default. See ticket #1863. -** ^Call the sqlite3_enable_load_extension() routine with onoff==1 -** to turn extension loading on and call it with onoff==0 to turn -** it back off again. -*/ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); - -/* -** CAPI3REF: Automatically Load Statically Linked Extensions -** -** ^This interface causes the xEntryPoint() function to be invoked for -** each new [database connection] that is created. The idea here is that -** xEntryPoint() is the entry point for a statically linked SQLite extension -** that is to be automatically loaded into all new database connections. -** -** ^(Even though the function prototype shows that xEntryPoint() takes -** no arguments and returns void, SQLite invokes xEntryPoint() with three -** arguments and expects and integer result as if the signature of the -** entry point where as follows: -** -**
-**    int xEntryPoint(
-**      sqlite3 *db,
-**      const char **pzErrMsg,
-**      const struct sqlite3_api_routines *pThunk
-**    );
-** 
)^ -** -** If the xEntryPoint routine encounters an error, it should make *pzErrMsg -** point to an appropriate error message (obtained from [sqlite3_mprintf()]) -** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg -** is NULL before calling the xEntryPoint(). ^SQLite will invoke -** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any -** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], -** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. -** -** ^Calling sqlite3_auto_extension(X) with an entry point X that is already -** on the list of automatic extensions is a harmless no-op. ^No entry point -** will be called more than once for each database connection that is opened. -** -** See also: [sqlite3_reset_auto_extension()]. -*/ -SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); - -/* -** CAPI3REF: Reset Automatic Extension Loading -** -** ^This interface disables all automatic extensions previously -** registered using [sqlite3_auto_extension()]. -*/ -SQLITE_API void sqlite3_reset_auto_extension(void); - -/* -** The interface to the virtual-table mechanism is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - -/* -** Structures used by the virtual table interface -*/ -typedef struct sqlite3_vtab sqlite3_vtab; -typedef struct sqlite3_index_info sqlite3_index_info; -typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; -typedef struct sqlite3_module sqlite3_module; - -/* -** CAPI3REF: Virtual Table Object -** KEYWORDS: sqlite3_module {virtual table module} -** -** This structure, sometimes called a "virtual table module", -** defines the implementation of a [virtual tables]. -** This structure consists mostly of methods for the module. -** -** ^A virtual table module is created by filling in a persistent -** instance of this structure and passing a pointer to that instance -** to [sqlite3_create_module()] or [sqlite3_create_module_v2()]. -** ^The registration remains valid until it is replaced by a different -** module or until the [database connection] closes. The content -** of this structure must not change while it is registered with -** any database connection. -*/ -struct sqlite3_module { - int iVersion; - int (*xCreate)(sqlite3*, void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, char**); - int (*xConnect)(sqlite3*, void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, char**); - int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*); - int (*xDisconnect)(sqlite3_vtab *pVTab); - int (*xDestroy)(sqlite3_vtab *pVTab); - int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor); - int (*xClose)(sqlite3_vtab_cursor*); - int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, - int argc, sqlite3_value **argv); - int (*xNext)(sqlite3_vtab_cursor*); - int (*xEof)(sqlite3_vtab_cursor*); - int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int); - int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid); - int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *); - int (*xBegin)(sqlite3_vtab *pVTab); - int (*xSync)(sqlite3_vtab *pVTab); - int (*xCommit)(sqlite3_vtab *pVTab); - int (*xRollback)(sqlite3_vtab *pVTab); - int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName, - void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), - void **ppArg); - int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); - /* The methods above are in version 1 of the sqlite_module object. Those - ** below are for version 2 and greater. */ - int (*xSavepoint)(sqlite3_vtab *pVTab, int); - int (*xRelease)(sqlite3_vtab *pVTab, int); - int (*xRollbackTo)(sqlite3_vtab *pVTab, int); -}; - -/* -** CAPI3REF: Virtual Table Indexing Information -** KEYWORDS: sqlite3_index_info -** -** The sqlite3_index_info structure and its substructures is used as part -** of the [virtual table] interface to -** pass information into and receive the reply from the [xBestIndex] -** method of a [virtual table module]. The fields under **Inputs** are the -** inputs to xBestIndex and are read-only. xBestIndex inserts its -** results into the **Outputs** fields. -** -** ^(The aConstraint[] array records WHERE clause constraints of the form: -** -**
column OP expr
-** -** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is -** stored in aConstraint[].op using one of the -** [SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_ values].)^ -** ^(The index of the column is stored in -** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the -** expr on the right-hand side can be evaluated (and thus the constraint -** is usable) and false if it cannot.)^ -** -** ^The optimizer automatically inverts terms of the form "expr OP column" -** and makes other simplifications to the WHERE clause in an attempt to -** get as many WHERE clause terms into the form shown above as possible. -** ^The aConstraint[] array only reports WHERE clause terms that are -** relevant to the particular virtual table being queried. -** -** ^Information about the ORDER BY clause is stored in aOrderBy[]. -** ^Each term of aOrderBy records a column of the ORDER BY clause. -** -** The [xBestIndex] method must fill aConstraintUsage[] with information -** about what parameters to pass to xFilter. ^If argvIndex>0 then -** the right-hand side of the corresponding aConstraint[] is evaluated -** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit -** is true, then the constraint is assumed to be fully handled by the -** virtual table and is not checked again by SQLite.)^ -** -** ^The idxNum and idxPtr values are recorded and passed into the -** [xFilter] method. -** ^[sqlite3_free()] is used to free idxPtr if and only if -** needToFreeIdxPtr is true. -** -** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in -** the correct order to satisfy the ORDER BY clause so that no separate -** sorting step is required. -** -** ^The estimatedCost value is an estimate of the cost of doing the -** particular lookup. A full scan of a table with N entries should have -** a cost of N. A binary search of a table of N entries should have a -** cost of approximately log(N). -*/ -struct sqlite3_index_info { - /* Inputs */ - int nConstraint; /* Number of entries in aConstraint */ - struct sqlite3_index_constraint { - int iColumn; /* Column on left-hand side of constraint */ - unsigned char op; /* Constraint operator */ - unsigned char usable; /* True if this constraint is usable */ - int iTermOffset; /* Used internally - xBestIndex should ignore */ - } *aConstraint; /* Table of WHERE clause constraints */ - int nOrderBy; /* Number of terms in the ORDER BY clause */ - struct sqlite3_index_orderby { - int iColumn; /* Column number */ - unsigned char desc; /* True for DESC. False for ASC. */ - } *aOrderBy; /* The ORDER BY clause */ - /* Outputs */ - struct sqlite3_index_constraint_usage { - int argvIndex; /* if >0, constraint is part of argv to xFilter */ - unsigned char omit; /* Do not code a test for this constraint */ - } *aConstraintUsage; - int idxNum; /* Number used to identify the index */ - char *idxStr; /* String, possibly obtained from sqlite3_malloc */ - int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ - int orderByConsumed; /* True if output is already ordered */ - double estimatedCost; /* Estimated cost of using this index */ -}; - -/* -** CAPI3REF: Virtual Table Constraint Operator Codes -** -** These macros defined the allowed values for the -** [sqlite3_index_info].aConstraint[].op field. Each value represents -** an operator that is part of a constraint term in the wHERE clause of -** a query that uses a [virtual table]. -*/ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 - -/* -** CAPI3REF: Register A Virtual Table Implementation -** -** ^These routines are used to register a new [virtual table module] name. -** ^Module names must be registered before -** creating a new [virtual table] using the module and before using a -** preexisting [virtual table] for the module. -** -** ^The module name is registered on the [database connection] specified -** by the first parameter. ^The name of the module is given by the -** second parameter. ^The third parameter is a pointer to -** the implementation of the [virtual table module]. ^The fourth -** parameter is an arbitrary client data pointer that is passed through -** into the [xCreate] and [xConnect] methods of the virtual table module -** when a new virtual table is be being created or reinitialized. -** -** ^The sqlite3_create_module_v2() interface has a fifth parameter which -** is a pointer to a destructor for the pClientData. ^SQLite will -** invoke the destructor function (if it is not NULL) when SQLite -** no longer needs the pClientData pointer. ^The destructor will also -** be invoked if the call to sqlite3_create_module_v2() fails. -** ^The sqlite3_create_module() -** interface is equivalent to sqlite3_create_module_v2() with a NULL -** destructor. -*/ -SQLITE_API int sqlite3_create_module( - sqlite3 *db, /* SQLite connection to register module with */ - const char *zName, /* Name of the module */ - const sqlite3_module *p, /* Methods for the module */ - void *pClientData /* Client data for xCreate/xConnect */ -); -SQLITE_API int sqlite3_create_module_v2( - sqlite3 *db, /* SQLite connection to register module with */ - const char *zName, /* Name of the module */ - const sqlite3_module *p, /* Methods for the module */ - void *pClientData, /* Client data for xCreate/xConnect */ - void(*xDestroy)(void*) /* Module destructor function */ -); - -/* -** CAPI3REF: Virtual Table Instance Object -** KEYWORDS: sqlite3_vtab -** -** Every [virtual table module] implementation uses a subclass -** of this object to describe a particular instance -** of the [virtual table]. Each subclass will -** be tailored to the specific needs of the module implementation. -** The purpose of this superclass is to define certain fields that are -** common to all module implementations. -** -** ^Virtual tables methods can set an error message by assigning a -** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should -** take care that any prior string is freed by a call to [sqlite3_free()] -** prior to assigning a new string to zErrMsg. ^After the error message -** is delivered up to the client application, the string will be automatically -** freed by sqlite3_free() and the zErrMsg field will be zeroed. -*/ -struct sqlite3_vtab { - const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* NO LONGER USED */ - char *zErrMsg; /* Error message from sqlite3_mprintf() */ - /* Virtual table implementations will typically add additional fields */ -}; - -/* -** CAPI3REF: Virtual Table Cursor Object -** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} -** -** Every [virtual table module] implementation uses a subclass of the -** following structure to describe cursors that point into the -** [virtual table] and are used -** to loop through the virtual table. Cursors are created using the -** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed -** by the [sqlite3_module.xClose | xClose] method. Cursors are used -** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods -** of the module. Each module implementation will define -** the content of a cursor structure to suit its own needs. -** -** This superclass exists in order to define fields of the cursor that -** are common to all implementations. -*/ -struct sqlite3_vtab_cursor { - sqlite3_vtab *pVtab; /* Virtual table of this cursor */ - /* Virtual table implementations will typically add additional fields */ -}; - -/* -** CAPI3REF: Declare The Schema Of A Virtual Table -** -** ^The [xCreate] and [xConnect] methods of a -** [virtual table module] call this interface -** to declare the format (the names and datatypes of the columns) of -** the virtual tables they implement. -*/ -SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); - -/* -** CAPI3REF: Overload A Function For A Virtual Table -** -** ^(Virtual tables can provide alternative implementations of functions -** using the [xFindFunction] method of the [virtual table module]. -** But global versions of those functions -** must exist in order to be overloaded.)^ -** -** ^(This API makes sure a global version of a function with a particular -** name and number of parameters exists. If no such function exists -** before this API is called, a new function is created.)^ ^The implementation -** of the new function always causes an exception to be thrown. So -** the new function is not good for anything by itself. Its only -** purpose is to be a placeholder function that can be overloaded -** by a [virtual table]. -*/ -SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); - -/* -** The interface to the virtual-table mechanism defined above (back up -** to a comment remarkably similar to this one) is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - -/* -** CAPI3REF: A Handle To An Open BLOB -** KEYWORDS: {BLOB handle} {BLOB handles} -** -** An instance of this object represents an open BLOB on which -** [sqlite3_blob_open | incremental BLOB I/O] can be performed. -** ^Objects of this type are created by [sqlite3_blob_open()] -** and destroyed by [sqlite3_blob_close()]. -** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces -** can be used to read or write small subsections of the BLOB. -** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. -*/ -typedef struct sqlite3_blob sqlite3_blob; - -/* -** CAPI3REF: Open A BLOB For Incremental I/O -** -** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located -** in row iRow, column zColumn, table zTable in database zDb; -** in other words, the same BLOB that would be selected by: -** -**
-**     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
-** 
)^ -** -** ^If the flags parameter is non-zero, then the BLOB is opened for read -** and write access. ^If it is zero, the BLOB is opened for read access. -** ^It is not possible to open a column that is part of an index or primary -** key for writing. ^If [foreign key constraints] are enabled, it is -** not possible to open a column that is part of a [child key] for writing. -** -** ^Note that the database name is not the filename that contains -** the database but rather the symbolic name of the database that -** appears after the AS keyword when the database is connected using [ATTACH]. -** ^For the main database file, the database name is "main". -** ^For TEMP tables, the database name is "temp". -** -** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written -** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set -** to be a null pointer.)^ -** ^This function sets the [database connection] error code and message -** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related -** functions. ^Note that the *ppBlob variable is always initialized in a -** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob -** regardless of the success or failure of this routine. -** -** ^(If the row that a BLOB handle points to is modified by an -** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects -** then the BLOB handle is marked as "expired". -** This is true if any column of the row is changed, even a column -** other than the one the BLOB handle is open on.)^ -** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for -** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. -** ^(Changes written into a BLOB prior to the BLOB expiring are not -** rolled back by the expiration of the BLOB. Such changes will eventually -** commit if the transaction continues to completion.)^ -** -** ^Use the [sqlite3_blob_bytes()] interface to determine the size of -** the opened blob. ^The size of a blob may not be changed by this -** interface. Use the [UPDATE] SQL command to change the size of a -** blob. -** -** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces -** and the built-in [zeroblob] SQL function can be used, if desired, -** to create an empty, zero-filled blob in which to read or write using -** this interface. -** -** To avoid a resource leak, every open [BLOB handle] should eventually -** be released by a call to [sqlite3_blob_close()]. -*/ -SQLITE_API int sqlite3_blob_open( - sqlite3*, - const char *zDb, - const char *zTable, - const char *zColumn, - sqlite3_int64 iRow, - int flags, - sqlite3_blob **ppBlob -); - -/* -** CAPI3REF: Move a BLOB Handle to a New Row -** -** ^This function is used to move an existing blob handle so that it points -** to a different row of the same database table. ^The new row is identified -** by the rowid value passed as the second argument. Only the row can be -** changed. ^The database, table and column on which the blob handle is open -** remain the same. Moving an existing blob handle to a new row can be -** faster than closing the existing handle and opening a new one. -** -** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] - -** it must exist and there must be either a blob or text value stored in -** the nominated column.)^ ^If the new row is not present in the table, or if -** it does not contain a blob or text value, or if another error occurs, an -** SQLite error code is returned and the blob handle is considered aborted. -** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or -** [sqlite3_blob_reopen()] on an aborted blob handle immediately return -** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle -** always returns zero. -** -** ^This function sets the database handle error code and message. -*/ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); - -/* -** CAPI3REF: Close A BLOB Handle -** -** ^Closes an open [BLOB handle]. -** -** ^Closing a BLOB shall cause the current transaction to commit -** if there are no other BLOBs, no pending prepared statements, and the -** database connection is in [autocommit mode]. -** ^If any writes were made to the BLOB, they might be held in cache -** until the close operation if they will fit. -** -** ^(Closing the BLOB often forces the changes -** out to disk and so if any I/O errors occur, they will likely occur -** at the time when the BLOB is closed. Any errors that occur during -** closing are reported as a non-zero return value.)^ -** -** ^(The BLOB is closed unconditionally. Even if this routine returns -** an error code, the BLOB is still closed.)^ -** -** ^Calling this routine with a null pointer (such as would be returned -** by a failed call to [sqlite3_blob_open()]) is a harmless no-op. -*/ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *); - -/* -** CAPI3REF: Return The Size Of An Open BLOB -** -** ^Returns the size in bytes of the BLOB accessible via the -** successfully opened [BLOB handle] in its only argument. ^The -** incremental blob I/O routines can only read or overwriting existing -** blob content; they cannot change the size of a blob. -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite3_blob_open()] and which has not -** been closed by [sqlite3_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -*/ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); - -/* -** CAPI3REF: Read Data From A BLOB Incrementally -** -** ^(This function is used to read data from an open [BLOB handle] into a -** caller-supplied buffer. N bytes of data are copied into buffer Z -** from the open BLOB, starting at offset iOffset.)^ -** -** ^If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is -** less than zero, [SQLITE_ERROR] is returned and no data is read. -** ^The size of the blob (and hence the maximum value of N+iOffset) -** can be determined using the [sqlite3_blob_bytes()] interface. -** -** ^An attempt to read from an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. -** -** ^(On success, sqlite3_blob_read() returns SQLITE_OK. -** Otherwise, an [error code] or an [extended error code] is returned.)^ -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite3_blob_open()] and which has not -** been closed by [sqlite3_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -** -** See also: [sqlite3_blob_write()]. -*/ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); - -/* -** CAPI3REF: Write Data Into A BLOB Incrementally -** -** ^This function is used to write data into an open [BLOB handle] from a -** caller-supplied buffer. ^N bytes of data are copied from the buffer Z -** into the open BLOB, starting at offset iOffset. -** -** ^If the [BLOB handle] passed as the first argument was not opened for -** writing (the flags parameter to [sqlite3_blob_open()] was zero), -** this function returns [SQLITE_READONLY]. -** -** ^This function may only modify the contents of the BLOB; it is -** not possible to increase the size of a BLOB using this API. -** ^If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is written. ^If N is -** less than zero [SQLITE_ERROR] is returned and no data is written. -** The size of the BLOB (and hence the maximum value of N+iOffset) -** can be determined using the [sqlite3_blob_bytes()] interface. -** -** ^An attempt to write to an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred -** before the [BLOB handle] expired are not rolled back by the -** expiration of the handle, though of course those changes might -** have been overwritten by the statement that expired the BLOB handle -** or by other independent statements. -** -** ^(On success, sqlite3_blob_write() returns SQLITE_OK. -** Otherwise, an [error code] or an [extended error code] is returned.)^ -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite3_blob_open()] and which has not -** been closed by [sqlite3_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -** -** See also: [sqlite3_blob_read()]. -*/ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); - -/* -** CAPI3REF: Virtual File System Objects -** -** A virtual filesystem (VFS) is an [sqlite3_vfs] object -** that SQLite uses to interact -** with the underlying operating system. Most SQLite builds come with a -** single default VFS that is appropriate for the host computer. -** New VFSes can be registered and existing VFSes can be unregistered. -** The following interfaces are provided. -** -** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. -** ^Names are case sensitive. -** ^Names are zero-terminated UTF-8 strings. -** ^If there is no match, a NULL pointer is returned. -** ^If zVfsName is NULL then the default VFS is returned. -** -** ^New VFSes are registered with sqlite3_vfs_register(). -** ^Each new VFS becomes the default VFS if the makeDflt flag is set. -** ^The same VFS can be registered multiple times without injury. -** ^To make an existing VFS into the default VFS, register it again -** with the makeDflt flag set. If two different VFSes with the -** same name are registered, the behavior is undefined. If a -** VFS is registered with a name that is NULL or an empty string, -** then the behavior is undefined. -** -** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. -** ^(If the default VFS is unregistered, another VFS is chosen as -** the default. The choice for the new VFS is arbitrary.)^ -*/ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); - -/* -** CAPI3REF: Mutexes -** -** The SQLite core uses these routines for thread -** synchronization. Though they are intended for internal -** use by SQLite, code that links against SQLite is -** permitted to use any of these routines. -** -** The SQLite source code contains multiple implementations -** of these mutex routines. An appropriate implementation -** is selected automatically at compile-time. ^(The following -** implementations are available in the SQLite core: -** -**
    -**
  • SQLITE_MUTEX_PTHREADS -**
  • SQLITE_MUTEX_W32 -**
  • SQLITE_MUTEX_NOOP -**
)^ -** -** ^The SQLITE_MUTEX_NOOP implementation is a set of routines -** that does no real locking and is appropriate for use in -** a single-threaded application. ^The SQLITE_MUTEX_PTHREADS and -** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix -** and Windows. -** -** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor -** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex -** implementation is included with the library. In this case the -** application must supply a custom mutex implementation using the -** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function -** before calling sqlite3_initialize() or any other public sqlite3_ -** function that calls sqlite3_initialize().)^ -** -** ^The sqlite3_mutex_alloc() routine allocates a new -** mutex and returns a pointer to it. ^If it returns NULL -** that means that a mutex could not be allocated. ^SQLite -** will unwind its stack and return an error. ^(The argument -** to sqlite3_mutex_alloc() is one of these integer constants: -** -**
    -**
  • SQLITE_MUTEX_FAST -**
  • SQLITE_MUTEX_RECURSIVE -**
  • SQLITE_MUTEX_STATIC_MASTER -**
  • SQLITE_MUTEX_STATIC_MEM -**
  • SQLITE_MUTEX_STATIC_MEM2 -**
  • SQLITE_MUTEX_STATIC_PRNG -**
  • SQLITE_MUTEX_STATIC_LRU -**
  • SQLITE_MUTEX_STATIC_LRU2 -**
)^ -** -** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) -** cause sqlite3_mutex_alloc() to create -** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE -** is used but not necessarily so when SQLITE_MUTEX_FAST is used. -** The mutex implementation does not need to make a distinction -** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does -** not want to. ^SQLite will only request a recursive mutex in -** cases where it really needs one. ^If a faster non-recursive mutex -** implementation is available on the host platform, the mutex subsystem -** might return such a mutex in response to SQLITE_MUTEX_FAST. -** -** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other -** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return -** a pointer to a static preexisting mutex. ^Six static mutexes are -** used by the current version of SQLite. Future versions of SQLite -** may add additional static mutexes. Static mutexes are for internal -** use by SQLite only. Applications that use SQLite mutexes should -** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or -** SQLITE_MUTEX_RECURSIVE. -** -** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST -** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() -** returns a different mutex on every call. ^But for the static -** mutex types, the same mutex is returned on every call that has -** the same type number. -** -** ^The sqlite3_mutex_free() routine deallocates a previously -** allocated dynamic mutex. ^SQLite is careful to deallocate every -** dynamic mutex that it allocates. The dynamic mutexes must not be in -** use when they are deallocated. Attempting to deallocate a static -** mutex results in undefined behavior. ^SQLite never deallocates -** a static mutex. -** -** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt -** to enter a mutex. ^If another thread is already within the mutex, -** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return -** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] -** upon successful entry. ^(Mutexes created using -** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. -** In such cases the, -** mutex must be exited an equal number of times before another thread -** can enter.)^ ^(If the same thread tries to enter any other -** kind of mutex more than once, the behavior is undefined. -** SQLite will never exhibit -** such behavior in its own use of mutexes.)^ -** -** ^(Some systems (for example, Windows 95) do not support the operation -** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() -** will always return SQLITE_BUSY. The SQLite core only ever uses -** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^ -** -** ^The sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. ^(The behavior -** is undefined if the mutex is not currently entered by the -** calling thread or is not currently allocated. SQLite will -** never do either.)^ -** -** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or -** sqlite3_mutex_leave() is a NULL pointer, then all three routines -** behave as no-ops. -** -** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. -*/ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); - -/* -** CAPI3REF: Mutex Methods Object -** -** An instance of this structure defines the low-level routines -** used to allocate and use mutexes. -** -** Usually, the default mutex implementations provided by SQLite are -** sufficient, however the user has the option of substituting a custom -** implementation for specialized deployments or systems for which SQLite -** does not provide a suitable implementation. In this case, the user -** creates and populates an instance of this structure to pass -** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. -** Additionally, an instance of this structure can be used as an -** output variable when querying the system for the current mutex -** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. -** -** ^The xMutexInit method defined by this structure is invoked as -** part of system initialization by the sqlite3_initialize() function. -** ^The xMutexInit routine is called by SQLite exactly once for each -** effective call to [sqlite3_initialize()]. -** -** ^The xMutexEnd method defined by this structure is invoked as -** part of system shutdown by the sqlite3_shutdown() function. The -** implementation of this method is expected to release all outstanding -** resources obtained by the mutex methods implementation, especially -** those obtained by the xMutexInit method. ^The xMutexEnd() -** interface is invoked exactly once for each call to [sqlite3_shutdown()]. -** -** ^(The remaining seven methods defined by this structure (xMutexAlloc, -** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and -** xMutexNotheld) implement the following interfaces (respectively): -** -**
    -**
  • [sqlite3_mutex_alloc()]
  • -**
  • [sqlite3_mutex_free()]
  • -**
  • [sqlite3_mutex_enter()]
  • -**
  • [sqlite3_mutex_try()]
  • -**
  • [sqlite3_mutex_leave()]
  • -**
  • [sqlite3_mutex_held()]
  • -**
  • [sqlite3_mutex_notheld()]
  • -**
)^ -** -** The only difference is that the public sqlite3_XXX functions enumerated -** above silently ignore any invocations that pass a NULL pointer instead -** of a valid mutex handle. The implementations of the methods defined -** by this structure are not required to handle this case, the results -** of passing a NULL pointer instead of a valid mutex handle are undefined -** (i.e. it is acceptable to provide an implementation that segfaults if -** it is passed a NULL pointer). -** -** The xMutexInit() method must be threadsafe. ^It must be harmless to -** invoke xMutexInit() multiple times within the same process and without -** intervening calls to xMutexEnd(). Second and subsequent calls to -** xMutexInit() must be no-ops. -** -** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] -** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory -** allocation for a static mutex. ^However xMutexAlloc() may use SQLite -** memory allocation for a fast or recursive mutex. -** -** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is -** called, but only if the prior call to xMutexInit returned SQLITE_OK. -** If xMutexInit fails in any way, it is expected to clean up after itself -** prior to returning. -*/ -typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; -struct sqlite3_mutex_methods { - int (*xMutexInit)(void); - int (*xMutexEnd)(void); - sqlite3_mutex *(*xMutexAlloc)(int); - void (*xMutexFree)(sqlite3_mutex *); - void (*xMutexEnter)(sqlite3_mutex *); - int (*xMutexTry)(sqlite3_mutex *); - void (*xMutexLeave)(sqlite3_mutex *); - int (*xMutexHeld)(sqlite3_mutex *); - int (*xMutexNotheld)(sqlite3_mutex *); -}; - -/* -** CAPI3REF: Mutex Verification Routines -** -** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines -** are intended for use inside assert() statements. ^The SQLite core -** never uses these routines except inside an assert() and applications -** are advised to follow the lead of the core. ^The SQLite core only -** provides implementations for these routines when it is compiled -** with the SQLITE_DEBUG flag. ^External mutex implementations -** are only required to provide these routines if SQLITE_DEBUG is -** defined and if NDEBUG is not defined. -** -** ^These routines should return true if the mutex in their argument -** is held or not held, respectively, by the calling thread. -** -** ^The implementation is not required to provide versions of these -** routines that actually work. If the implementation does not provide working -** versions of these routines, it should at least provide stubs that always -** return true so that one does not get spurious assertion failures. -** -** ^If the argument to sqlite3_mutex_held() is a NULL pointer then -** the routine should return 1. This seems counter-intuitive since -** clearly the mutex cannot be held if it does not exist. But -** the reason the mutex does not exist is because the build is not -** using mutexes. And we do not want the assert() containing the -** call to sqlite3_mutex_held() to fail, so a non-zero return is -** the appropriate thing to do. ^The sqlite3_mutex_notheld() -** interface should also return 1 when given a NULL pointer. -*/ -#ifndef NDEBUG -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); -#endif - -/* -** CAPI3REF: Mutex Types -** -** The [sqlite3_mutex_alloc()] interface takes a single argument -** which is one of these integer constants. -** -** The set of static mutexes may change from one SQLite release to the -** next. Applications that override the built-in mutex logic must be -** prepared to accommodate additional static mutexes. -*/ -#define SQLITE_MUTEX_FAST 0 -#define SQLITE_MUTEX_RECURSIVE 1 -#define SQLITE_MUTEX_STATIC_MASTER 2 -#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ -#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ -#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ -#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ -#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ -#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ -#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ - -/* -** CAPI3REF: Retrieve the mutex for a database connection -** -** ^This interface returns a pointer the [sqlite3_mutex] object that -** serializes access to the [database connection] given in the argument -** when the [threading mode] is Serialized. -** ^If the [threading mode] is Single-thread or Multi-thread then this -** routine returns a NULL pointer. -*/ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); - -/* -** CAPI3REF: Low-Level Control Of Database Files -** -** ^The [sqlite3_file_control()] interface makes a direct call to the -** xFileControl method for the [sqlite3_io_methods] object associated -** with a particular database identified by the second argument. ^The -** name of the database is "main" for the main database or "temp" for the -** TEMP database, or the name that appears after the AS keyword for -** databases that are added using the [ATTACH] SQL command. -** ^A NULL pointer can be used in place of "main" to refer to the -** main database file. -** ^The third and fourth parameters to this routine -** are passed directly through to the second and third parameters of -** the xFileControl method. ^The return value of the xFileControl -** method becomes the return value of this routine. -** -** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes -** a pointer to the underlying [sqlite3_file] object to be written into -** the space pointed to by the 4th parameter. ^The SQLITE_FCNTL_FILE_POINTER -** case is a short-circuit path which does not actually invoke the -** underlying sqlite3_io_methods.xFileControl method. -** -** ^If the second parameter (zDbName) does not match the name of any -** open database file, then SQLITE_ERROR is returned. ^This error -** code is not remembered and will not be recalled by [sqlite3_errcode()] -** or [sqlite3_errmsg()]. The underlying xFileControl method might -** also return SQLITE_ERROR. There is no way to distinguish between -** an incorrect zDbName and an SQLITE_ERROR return from the underlying -** xFileControl method. -** -** See also: [SQLITE_FCNTL_LOCKSTATE] -*/ -SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); - -/* -** CAPI3REF: Testing Interface -** -** ^The sqlite3_test_control() interface is used to read out internal -** state of SQLite and to inject faults into SQLite for testing -** purposes. ^The first parameter is an operation code that determines -** the number, meaning, and operation of all subsequent parameters. -** -** This interface is not for use by applications. It exists solely -** for verifying the correct operation of the SQLite library. Depending -** on how the SQLite library is compiled, this interface might not exist. -** -** The details of the operation codes, their meanings, the parameters -** they take, and what they do are all subject to change without notice. -** Unlike most of the SQLite API, this function is not guaranteed to -** operate consistently from one release to the next. -*/ -SQLITE_API int sqlite3_test_control(int op, ...); - -/* -** CAPI3REF: Testing Interface Operation Codes -** -** These constants are the valid operation code parameters used -** as the first argument to [sqlite3_test_control()]. -** -** These parameters and their meanings are subject to change -** without notice. These values are for testing purposes only. -** Applications should not use any of these parameters or the -** [sqlite3_test_control()] interface. -*/ -#define SQLITE_TESTCTRL_FIRST 5 -#define SQLITE_TESTCTRL_PRNG_SAVE 5 -#define SQLITE_TESTCTRL_PRNG_RESTORE 6 -#define SQLITE_TESTCTRL_PRNG_RESET 7 -#define SQLITE_TESTCTRL_BITVEC_TEST 8 -#define SQLITE_TESTCTRL_FAULT_INSTALL 9 -#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 -#define SQLITE_TESTCTRL_PENDING_BYTE 11 -#define SQLITE_TESTCTRL_ASSERT 12 -#define SQLITE_TESTCTRL_ALWAYS 13 -#define SQLITE_TESTCTRL_RESERVE 14 -#define SQLITE_TESTCTRL_OPTIMIZATIONS 15 -#define SQLITE_TESTCTRL_ISKEYWORD 16 -#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 -#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 -#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 -#define SQLITE_TESTCTRL_LAST 19 - -/* -** CAPI3REF: SQLite Runtime Status -** -** ^This interface is used to retrieve runtime status information -** about the performance of SQLite, and optionally to reset various -** highwater marks. ^The first argument is an integer code for -** the specific parameter to measure. ^(Recognized integer codes -** are of the form [status parameters | SQLITE_STATUS_...].)^ -** ^The current value of the parameter is returned into *pCurrent. -** ^The highest recorded value is returned in *pHighwater. ^If the -** resetFlag is true, then the highest record value is reset after -** *pHighwater is written. ^(Some parameters do not record the highest -** value. For those parameters -** nothing is written into *pHighwater and the resetFlag is ignored.)^ -** ^(Other parameters record only the highwater mark and not the current -** value. For these latter parameters nothing is written into *pCurrent.)^ -** -** ^The sqlite3_status() routine returns SQLITE_OK on success and a -** non-zero [error code] on failure. -** -** This routine is threadsafe but is not atomic. This routine can be -** called while other threads are running the same or different SQLite -** interfaces. However the values returned in *pCurrent and -** *pHighwater reflect the status of SQLite at different points in time -** and it is possible that another thread might change the parameter -** in between the times when *pCurrent and *pHighwater are written. -** -** See also: [sqlite3_db_status()] -*/ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); - - -/* -** CAPI3REF: Status Parameters -** KEYWORDS: {status parameters} -** -** These integer constants designate various run-time status parameters -** that can be returned by [sqlite3_status()]. -** -**
-** [[SQLITE_STATUS_MEMORY_USED]] ^(
SQLITE_STATUS_MEMORY_USED
-**
This parameter is the current amount of memory checked out -** using [sqlite3_malloc()], either directly or indirectly. The -** figure includes calls made to [sqlite3_malloc()] by the application -** and internal memory usage by the SQLite library. Scratch memory -** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache -** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in -** this parameter. The amount returned is the sum of the allocation -** sizes as reported by the xSize method in [sqlite3_mem_methods].
)^ -** -** [[SQLITE_STATUS_MALLOC_SIZE]] ^(
SQLITE_STATUS_MALLOC_SIZE
-**
This parameter records the largest memory allocation request -** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their -** internal equivalents). Only the value returned in the -** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
)^ -** -** [[SQLITE_STATUS_MALLOC_COUNT]] ^(
SQLITE_STATUS_MALLOC_COUNT
-**
This parameter records the number of separate memory allocations -** currently checked out.
)^ -** -** [[SQLITE_STATUS_PAGECACHE_USED]] ^(
SQLITE_STATUS_PAGECACHE_USED
-**
This parameter returns the number of pages used out of the -** [pagecache memory allocator] that was configured using -** [SQLITE_CONFIG_PAGECACHE]. The -** value returned is in pages, not in bytes.
)^ -** -** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] -** ^(
SQLITE_STATUS_PAGECACHE_OVERFLOW
-**
This parameter returns the number of bytes of page cache -** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] -** buffer and where forced to overflow to [sqlite3_malloc()]. The -** returned value includes allocations that overflowed because they -** where too large (they were larger than the "sz" parameter to -** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because -** no space was left in the page cache.
)^ -** -** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(
SQLITE_STATUS_PAGECACHE_SIZE
-**
This parameter records the largest memory allocation request -** handed to [pagecache memory allocator]. Only the value returned in the -** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
)^ -** -** [[SQLITE_STATUS_SCRATCH_USED]] ^(
SQLITE_STATUS_SCRATCH_USED
-**
This parameter returns the number of allocations used out of the -** [scratch memory allocator] configured using -** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not -** in bytes. Since a single thread may only have one scratch allocation -** outstanding at time, this parameter also reports the number of threads -** using scratch memory at the same time.
)^ -** -** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(
SQLITE_STATUS_SCRATCH_OVERFLOW
-**
This parameter returns the number of bytes of scratch memory -** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] -** buffer and where forced to overflow to [sqlite3_malloc()]. The values -** returned include overflows because the requested allocation was too -** larger (that is, because the requested allocation was larger than the -** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer -** slots were available. -**
)^ -** -** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(
SQLITE_STATUS_SCRATCH_SIZE
-**
This parameter records the largest memory allocation request -** handed to [scratch memory allocator]. Only the value returned in the -** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
)^ -** -** [[SQLITE_STATUS_PARSER_STACK]] ^(
SQLITE_STATUS_PARSER_STACK
-**
This parameter records the deepest parser stack. It is only -** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].
)^ -**
-** -** New status parameters may be added from time to time. -*/ -#define SQLITE_STATUS_MEMORY_USED 0 -#define SQLITE_STATUS_PAGECACHE_USED 1 -#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2 -#define SQLITE_STATUS_SCRATCH_USED 3 -#define SQLITE_STATUS_SCRATCH_OVERFLOW 4 -#define SQLITE_STATUS_MALLOC_SIZE 5 -#define SQLITE_STATUS_PARSER_STACK 6 -#define SQLITE_STATUS_PAGECACHE_SIZE 7 -#define SQLITE_STATUS_SCRATCH_SIZE 8 -#define SQLITE_STATUS_MALLOC_COUNT 9 - -/* -** CAPI3REF: Database Connection Status -** -** ^This interface is used to retrieve runtime status information -** about a single [database connection]. ^The first argument is the -** database connection object to be interrogated. ^The second argument -** is an integer constant, taken from the set of -** [SQLITE_DBSTATUS options], that -** determines the parameter to interrogate. The set of -** [SQLITE_DBSTATUS options] is likely -** to grow in future releases of SQLite. -** -** ^The current value of the requested parameter is written into *pCur -** and the highest instantaneous value is written into *pHiwtr. ^If -** the resetFlg is true, then the highest instantaneous value is -** reset back down to the current value. -** -** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a -** non-zero [error code] on failure. -** -** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. -*/ -SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); - -/* -** CAPI3REF: Status Parameters for database connections -** KEYWORDS: {SQLITE_DBSTATUS options} -** -** These constants are the available integer "verbs" that can be passed as -** the second argument to the [sqlite3_db_status()] interface. -** -** New verbs may be added in future releases of SQLite. Existing verbs -** might be discontinued. Applications should check the return code from -** [sqlite3_db_status()] to make sure that the call worked. -** The [sqlite3_db_status()] interface will return a non-zero error code -** if a discontinued or unsupported verb is invoked. -** -**
-** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(
SQLITE_DBSTATUS_LOOKASIDE_USED
-**
This parameter returns the number of lookaside memory slots currently -** checked out.
)^ -** -** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(
SQLITE_DBSTATUS_LOOKASIDE_HIT
-**
This parameter returns the number malloc attempts that were -** satisfied using lookaside memory. Only the high-water value is meaningful; -** the current value is always zero.)^ -** -** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] -** ^(
SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
-**
This parameter returns the number malloc attempts that might have -** been satisfied using lookaside memory but failed due to the amount of -** memory requested being larger than the lookaside slot size. -** Only the high-water value is meaningful; -** the current value is always zero.)^ -** -** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] -** ^(
SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
-**
This parameter returns the number malloc attempts that might have -** been satisfied using lookaside memory but failed due to all lookaside -** memory already being in use. -** Only the high-water value is meaningful; -** the current value is always zero.)^ -** -** [[SQLITE_DBSTATUS_CACHE_USED]] ^(
SQLITE_DBSTATUS_CACHE_USED
-**
This parameter returns the approximate number of of bytes of heap -** memory used by all pager caches associated with the database connection.)^ -** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. -** -** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
SQLITE_DBSTATUS_SCHEMA_USED
-**
This parameter returns the approximate number of of bytes of heap -** memory used to store the schema for all databases associated -** with the connection - main, temp, and any [ATTACH]-ed databases.)^ -** ^The full amount of memory used by the schemas is reported, even if the -** schema memory is shared with other database connections due to -** [shared cache mode] being enabled. -** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. -** -** [[SQLITE_DBSTATUS_STMT_USED]] ^(
SQLITE_DBSTATUS_STMT_USED
-**
This parameter returns the approximate number of of bytes of heap -** and lookaside memory used by all prepared statements associated with -** the database connection.)^ -** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0. -**
-** -** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(
SQLITE_DBSTATUS_CACHE_HIT
-**
This parameter returns the number of pager cache hits that have -** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT -** is always 0. -**
-** -** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(
SQLITE_DBSTATUS_CACHE_MISS
-**
This parameter returns the number of pager cache misses that have -** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS -** is always 0. -**
-** -** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(
SQLITE_DBSTATUS_CACHE_WRITE
-**
This parameter returns the number of dirty cache entries that have -** been written to disk. Specifically, the number of pages written to the -** wal file in wal mode databases, or the number of pages written to the -** database file in rollback mode databases. Any pages written as part of -** transaction rollback or database recovery operations are not included. -** If an IO or other error occurs while writing a page to disk, the effect -** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The -** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. -**
-**
-*/ -#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 -#define SQLITE_DBSTATUS_CACHE_USED 1 -#define SQLITE_DBSTATUS_SCHEMA_USED 2 -#define SQLITE_DBSTATUS_STMT_USED 3 -#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 -#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 -#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 -#define SQLITE_DBSTATUS_CACHE_HIT 7 -#define SQLITE_DBSTATUS_CACHE_MISS 8 -#define SQLITE_DBSTATUS_CACHE_WRITE 9 -#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */ - - -/* -** CAPI3REF: Prepared Statement Status -** -** ^(Each prepared statement maintains various -** [SQLITE_STMTSTATUS counters] that measure the number -** of times it has performed specific operations.)^ These counters can -** be used to monitor the performance characteristics of the prepared -** statements. For example, if the number of table steps greatly exceeds -** the number of table searches or result rows, that would tend to indicate -** that the prepared statement is using a full table scan rather than -** an index. -** -** ^(This interface is used to retrieve and reset counter values from -** a [prepared statement]. The first argument is the prepared statement -** object to be interrogated. The second argument -** is an integer code for a specific [SQLITE_STMTSTATUS counter] -** to be interrogated.)^ -** ^The current value of the requested counter is returned. -** ^If the resetFlg is true, then the counter is reset to zero after this -** interface call returns. -** -** See also: [sqlite3_status()] and [sqlite3_db_status()]. -*/ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); - -/* -** CAPI3REF: Status Parameters for prepared statements -** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters} -** -** These preprocessor macros define integer codes that name counter -** values associated with the [sqlite3_stmt_status()] interface. -** The meanings of the various counters are as follows: -** -**
-** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]]
SQLITE_STMTSTATUS_FULLSCAN_STEP
-**
^This is the number of times that SQLite has stepped forward in -** a table as part of a full table scan. Large numbers for this counter -** may indicate opportunities for performance improvement through -** careful use of indices.
-** -** [[SQLITE_STMTSTATUS_SORT]]
SQLITE_STMTSTATUS_SORT
-**
^This is the number of sort operations that have occurred. -** A non-zero value in this counter may indicate an opportunity to -** improvement performance through careful use of indices.
-** -** [[SQLITE_STMTSTATUS_AUTOINDEX]]
SQLITE_STMTSTATUS_AUTOINDEX
-**
^This is the number of rows inserted into transient indices that -** were created automatically in order to help joins run faster. -** A non-zero value in this counter may indicate an opportunity to -** improvement performance by adding permanent indices that do not -** need to be reinitialized each time the statement is run.
-**
-*/ -#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 -#define SQLITE_STMTSTATUS_SORT 2 -#define SQLITE_STMTSTATUS_AUTOINDEX 3 - -/* -** CAPI3REF: Custom Page Cache Object -** -** The sqlite3_pcache type is opaque. It is implemented by -** the pluggable module. The SQLite core has no knowledge of -** its size or internal structure and never deals with the -** sqlite3_pcache object except by holding and passing pointers -** to the object. -** -** See [sqlite3_pcache_methods2] for additional information. -*/ -typedef struct sqlite3_pcache sqlite3_pcache; - -/* -** CAPI3REF: Custom Page Cache Object -** -** The sqlite3_pcache_page object represents a single page in the -** page cache. The page cache will allocate instances of this -** object. Various methods of the page cache use pointers to instances -** of this object as parameters or as their return value. -** -** See [sqlite3_pcache_methods2] for additional information. -*/ -typedef struct sqlite3_pcache_page sqlite3_pcache_page; -struct sqlite3_pcache_page { - void *pBuf; /* The content of the page */ - void *pExtra; /* Extra information associated with the page */ -}; - -/* -** CAPI3REF: Application Defined Page Cache. -** KEYWORDS: {page cache} -** -** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can -** register an alternative page cache implementation by passing in an -** instance of the sqlite3_pcache_methods2 structure.)^ -** In many applications, most of the heap memory allocated by -** SQLite is used for the page cache. -** By implementing a -** custom page cache using this API, an application can better control -** the amount of memory consumed by SQLite, the way in which -** that memory is allocated and released, and the policies used to -** determine exactly which parts of a database file are cached and for -** how long. -** -** The alternative page cache mechanism is an -** extreme measure that is only needed by the most demanding applications. -** The built-in page cache is recommended for most uses. -** -** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an -** internal buffer by SQLite within the call to [sqlite3_config]. Hence -** the application may discard the parameter after the call to -** [sqlite3_config()] returns.)^ -** -** [[the xInit() page cache method]] -** ^(The xInit() method is called once for each effective -** call to [sqlite3_initialize()])^ -** (usually only once during the lifetime of the process). ^(The xInit() -** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^ -** The intent of the xInit() method is to set up global data structures -** required by the custom page cache implementation. -** ^(If the xInit() method is NULL, then the -** built-in default page cache is used instead of the application defined -** page cache.)^ -** -** [[the xShutdown() page cache method]] -** ^The xShutdown() method is called by [sqlite3_shutdown()]. -** It can be used to clean up -** any outstanding resources before process shutdown, if required. -** ^The xShutdown() method may be NULL. -** -** ^SQLite automatically serializes calls to the xInit method, -** so the xInit method need not be threadsafe. ^The -** xShutdown method is only called from [sqlite3_shutdown()] so it does -** not need to be threadsafe either. All other methods must be threadsafe -** in multithreaded applications. -** -** ^SQLite will never invoke xInit() more than once without an intervening -** call to xShutdown(). -** -** [[the xCreate() page cache methods]] -** ^SQLite invokes the xCreate() method to construct a new cache instance. -** SQLite will typically create one cache instance for each open database file, -** though this is not guaranteed. ^The -** first parameter, szPage, is the size in bytes of the pages that must -** be allocated by the cache. ^szPage will always a power of two. ^The -** second parameter szExtra is a number of bytes of extra storage -** associated with each page cache entry. ^The szExtra parameter will -** a number less than 250. SQLite will use the -** extra szExtra bytes on each page to store metadata about the underlying -** database page on disk. The value passed into szExtra depends -** on the SQLite version, the target platform, and how SQLite was compiled. -** ^The third argument to xCreate(), bPurgeable, is true if the cache being -** created will be used to cache database pages of a file stored on disk, or -** false if it is used for an in-memory database. The cache implementation -** does not have to do anything special based with the value of bPurgeable; -** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will -** never invoke xUnpin() except to deliberately delete a page. -** ^In other words, calls to xUnpin() on a cache with bPurgeable set to -** false will always have the "discard" flag set to true. -** ^Hence, a cache created with bPurgeable false will -** never contain any unpinned pages. -** -** [[the xCachesize() page cache method]] -** ^(The xCachesize() method may be called at any time by SQLite to set the -** suggested maximum cache-size (number of pages stored by) the cache -** instance passed as the first argument. This is the value configured using -** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable -** parameter, the implementation is not required to do anything with this -** value; it is advisory only. -** -** [[the xPagecount() page cache methods]] -** The xPagecount() method must return the number of pages currently -** stored in the cache, both pinned and unpinned. -** -** [[the xFetch() page cache methods]] -** The xFetch() method locates a page in the cache and returns a pointer to -** an sqlite3_pcache_page object associated with that page, or a NULL pointer. -** The pBuf element of the returned sqlite3_pcache_page object will be a -** pointer to a buffer of szPage bytes used to store the content of a -** single database page. The pExtra element of sqlite3_pcache_page will be -** a pointer to the szExtra bytes of extra storage that SQLite has requested -** for each entry in the page cache. -** -** The page to be fetched is determined by the key. ^The minimum key value -** is 1. After it has been retrieved using xFetch, the page is considered -** to be "pinned". -** -** If the requested page is already in the page cache, then the page cache -** implementation must return a pointer to the page buffer with its content -** intact. If the requested page is not already in the cache, then the -** cache implementation should use the value of the createFlag -** parameter to help it determined what action to take: -** -** -**
createFlag Behavior when page is not already in cache -**
0 Do not allocate a new page. Return NULL. -**
1 Allocate a new page if it easy and convenient to do so. -** Otherwise return NULL. -**
2 Make every effort to allocate a new page. Only return -** NULL if allocating a new page is effectively impossible. -**
-** -** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite -** will only use a createFlag of 2 after a prior call with a createFlag of 1 -** failed.)^ In between the to xFetch() calls, SQLite may -** attempt to unpin one or more cache pages by spilling the content of -** pinned pages to disk and synching the operating system disk cache. -** -** [[the xUnpin() page cache method]] -** ^xUnpin() is called by SQLite with a pointer to a currently pinned page -** as its second argument. If the third parameter, discard, is non-zero, -** then the page must be evicted from the cache. -** ^If the discard parameter is -** zero, then the page may be discarded or retained at the discretion of -** page cache implementation. ^The page cache implementation -** may choose to evict unpinned pages at any time. -** -** The cache must not perform any reference counting. A single -** call to xUnpin() unpins the page regardless of the number of prior calls -** to xFetch(). -** -** [[the xRekey() page cache methods]] -** The xRekey() method is used to change the key value associated with the -** page passed as the second argument. If the cache -** previously contains an entry associated with newKey, it must be -** discarded. ^Any prior cache entry associated with newKey is guaranteed not -** to be pinned. -** -** When SQLite calls the xTruncate() method, the cache must discard all -** existing cache entries with page numbers (keys) greater than or equal -** to the value of the iLimit parameter passed to xTruncate(). If any -** of these pages are pinned, they are implicitly unpinned, meaning that -** they can be safely discarded. -** -** [[the xDestroy() page cache method]] -** ^The xDestroy() method is used to delete a cache allocated by xCreate(). -** All resources associated with the specified cache should be freed. ^After -** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] -** handle invalid, and will not use it with any other sqlite3_pcache_methods2 -** functions. -** -** [[the xShrink() page cache method]] -** ^SQLite invokes the xShrink() method when it wants the page cache to -** free up as much of heap memory as possible. The page cache implementation -** is not obligated to free any memory, but well-behaved implementations should -** do their best. -*/ -typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; -struct sqlite3_pcache_methods2 { - int iVersion; - void *pArg; - int (*xInit)(void*); - void (*xShutdown)(void*); - sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable); - void (*xCachesize)(sqlite3_pcache*, int nCachesize); - int (*xPagecount)(sqlite3_pcache*); - sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); - void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); - void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, - unsigned oldKey, unsigned newKey); - void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); - void (*xDestroy)(sqlite3_pcache*); - void (*xShrink)(sqlite3_pcache*); -}; - -/* -** This is the obsolete pcache_methods object that has now been replaced -** by sqlite3_pcache_methods2. This object is not used by SQLite. It is -** retained in the header file for backwards compatibility only. -*/ -typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; -struct sqlite3_pcache_methods { - void *pArg; - int (*xInit)(void*); - void (*xShutdown)(void*); - sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable); - void (*xCachesize)(sqlite3_pcache*, int nCachesize); - int (*xPagecount)(sqlite3_pcache*); - void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); - void (*xUnpin)(sqlite3_pcache*, void*, int discard); - void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); - void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); - void (*xDestroy)(sqlite3_pcache*); -}; - - -/* -** CAPI3REF: Online Backup Object -** -** The sqlite3_backup object records state information about an ongoing -** online backup operation. ^The sqlite3_backup object is created by -** a call to [sqlite3_backup_init()] and is destroyed by a call to -** [sqlite3_backup_finish()]. -** -** See Also: [Using the SQLite Online Backup API] -*/ -typedef struct sqlite3_backup sqlite3_backup; - -/* -** CAPI3REF: Online Backup API. -** -** The backup API copies the content of one database into another. -** It is useful either for creating backups of databases or -** for copying in-memory databases to or from persistent files. -** -** See Also: [Using the SQLite Online Backup API] -** -** ^SQLite holds a write transaction open on the destination database file -** for the duration of the backup operation. -** ^The source database is read-locked only while it is being read; -** it is not locked continuously for the entire backup operation. -** ^Thus, the backup may be performed on a live source database without -** preventing other database connections from -** reading or writing to the source database while the backup is underway. -** -** ^(To perform a backup operation: -**
    -**
  1. sqlite3_backup_init() is called once to initialize the -** backup, -**
  2. sqlite3_backup_step() is called one or more times to transfer -** the data between the two databases, and finally -**
  3. sqlite3_backup_finish() is called to release all resources -** associated with the backup operation. -**
)^ -** There should be exactly one call to sqlite3_backup_finish() for each -** successful call to sqlite3_backup_init(). -** -** [[sqlite3_backup_init()]] sqlite3_backup_init() -** -** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the -** [database connection] associated with the destination database -** and the database name, respectively. -** ^The database name is "main" for the main database, "temp" for the -** temporary database, or the name specified after the AS keyword in -** an [ATTACH] statement for an attached database. -** ^The S and M arguments passed to -** sqlite3_backup_init(D,N,S,M) identify the [database connection] -** and database name of the source database, respectively. -** ^The source and destination [database connections] (parameters S and D) -** must be different or else sqlite3_backup_init(D,N,S,M) will fail with -** an error. -** -** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is -** returned and an error code and error message are stored in the -** destination [database connection] D. -** ^The error code and message for the failed call to sqlite3_backup_init() -** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or -** [sqlite3_errmsg16()] functions. -** ^A successful call to sqlite3_backup_init() returns a pointer to an -** [sqlite3_backup] object. -** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and -** sqlite3_backup_finish() functions to perform the specified backup -** operation. -** -** [[sqlite3_backup_step()]] sqlite3_backup_step() -** -** ^Function sqlite3_backup_step(B,N) will copy up to N pages between -** the source and destination databases specified by [sqlite3_backup] object B. -** ^If N is negative, all remaining source pages are copied. -** ^If sqlite3_backup_step(B,N) successfully copies N pages and there -** are still more pages to be copied, then the function returns [SQLITE_OK]. -** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages -** from source to destination, then it returns [SQLITE_DONE]. -** ^If an error occurs while running sqlite3_backup_step(B,N), -** then an [error code] is returned. ^As well as [SQLITE_OK] and -** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], -** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. -** -** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if -**
    -**
  1. the destination database was opened read-only, or -**
  2. the destination database is using write-ahead-log journaling -** and the destination and source page sizes differ, or -**
  3. the destination database is an in-memory database and the -** destination and source page sizes differ. -**
)^ -** -** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then -** the [sqlite3_busy_handler | busy-handler function] -** is invoked (if one is specified). ^If the -** busy-handler returns non-zero before the lock is available, then -** [SQLITE_BUSY] is returned to the caller. ^In this case the call to -** sqlite3_backup_step() can be retried later. ^If the source -** [database connection] -** is being used to write to the source database when sqlite3_backup_step() -** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this -** case the call to sqlite3_backup_step() can be retried later on. ^(If -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or -** [SQLITE_READONLY] is returned, then -** there is no point in retrying the call to sqlite3_backup_step(). These -** errors are considered fatal.)^ The application must accept -** that the backup operation has failed and pass the backup operation handle -** to the sqlite3_backup_finish() to release associated resources. -** -** ^The first call to sqlite3_backup_step() obtains an exclusive lock -** on the destination file. ^The exclusive lock is not released until either -** sqlite3_backup_finish() is called or the backup operation is complete -** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to -** sqlite3_backup_step() obtains a [shared lock] on the source database that -** lasts for the duration of the sqlite3_backup_step() call. -** ^Because the source database is not locked between calls to -** sqlite3_backup_step(), the source database may be modified mid-way -** through the backup process. ^If the source database is modified by an -** external process or via a database connection other than the one being -** used by the backup operation, then the backup will be automatically -** restarted by the next call to sqlite3_backup_step(). ^If the source -** database is modified by the using the same database connection as is used -** by the backup operation, then the backup database is automatically -** updated at the same time. -** -** [[sqlite3_backup_finish()]] sqlite3_backup_finish() -** -** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the -** application wishes to abandon the backup operation, the application -** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). -** ^The sqlite3_backup_finish() interfaces releases all -** resources associated with the [sqlite3_backup] object. -** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any -** active write-transaction on the destination database is rolled back. -** The [sqlite3_backup] object is invalid -** and may not be used following a call to sqlite3_backup_finish(). -** -** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no -** sqlite3_backup_step() errors occurred, regardless or whether or not -** sqlite3_backup_step() completed. -** ^If an out-of-memory condition or IO error occurred during any prior -** sqlite3_backup_step() call on the same [sqlite3_backup] object, then -** sqlite3_backup_finish() returns the corresponding [error code]. -** -** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() -** is not a permanent error and does not affect the return value of -** sqlite3_backup_finish(). -** -** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] -** sqlite3_backup_remaining() and sqlite3_backup_pagecount() -** -** ^Each call to sqlite3_backup_step() sets two values inside -** the [sqlite3_backup] object: the number of pages still to be backed -** up and the total number of pages in the source database file. -** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces -** retrieve these two values, respectively. -** -** ^The values returned by these functions are only updated by -** sqlite3_backup_step(). ^If the source database is modified during a backup -** operation, then the values are not updated to account for any extra -** pages that need to be updated or the size of the source database file -** changing. -** -** Concurrent Usage of Database Handles -** -** ^The source [database connection] may be used by the application for other -** purposes while a backup operation is underway or being initialized. -** ^If SQLite is compiled and configured to support threadsafe database -** connections, then the source database connection may be used concurrently -** from within other threads. -** -** However, the application must guarantee that the destination -** [database connection] is not passed to any other API (by any thread) after -** sqlite3_backup_init() is called and before the corresponding call to -** sqlite3_backup_finish(). SQLite does not currently check to see -** if the application incorrectly accesses the destination [database connection] -** and so no error code is reported, but the operations may malfunction -** nevertheless. Use of the destination database connection while a -** backup is in progress might also also cause a mutex deadlock. -** -** If running in [shared cache mode], the application must -** guarantee that the shared cache used by the destination database -** is not accessed while the backup is running. In practice this means -** that the application must guarantee that the disk file being -** backed up to is not accessed by any connection within the process, -** not just the specific connection that was passed to sqlite3_backup_init(). -** -** The [sqlite3_backup] object itself is partially threadsafe. Multiple -** threads may safely make multiple concurrent calls to sqlite3_backup_step(). -** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() -** APIs are not strictly speaking threadsafe. If they are invoked at the -** same time as another thread is invoking sqlite3_backup_step() it is -** possible that they return invalid values. -*/ -SQLITE_API sqlite3_backup *sqlite3_backup_init( - sqlite3 *pDest, /* Destination database handle */ - const char *zDestName, /* Destination database name */ - sqlite3 *pSource, /* Source database handle */ - const char *zSourceName /* Source database name */ -); -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); - -/* -** CAPI3REF: Unlock Notification -** -** ^When running in shared-cache mode, a database operation may fail with -** an [SQLITE_LOCKED] error if the required locks on the shared-cache or -** individual tables within the shared-cache cannot be obtained. See -** [SQLite Shared-Cache Mode] for a description of shared-cache locking. -** ^This API may be used to register a callback that SQLite will invoke -** when the connection currently holding the required lock relinquishes it. -** ^This API is only available if the library was compiled with the -** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. -** -** See Also: [Using the SQLite Unlock Notification Feature]. -** -** ^Shared-cache locks are released when a database connection concludes -** its current transaction, either by committing it or rolling it back. -** -** ^When a connection (known as the blocked connection) fails to obtain a -** shared-cache lock and SQLITE_LOCKED is returned to the caller, the -** identity of the database connection (the blocking connection) that -** has locked the required resource is stored internally. ^After an -** application receives an SQLITE_LOCKED error, it may call the -** sqlite3_unlock_notify() method with the blocked connection handle as -** the first argument to register for a callback that will be invoked -** when the blocking connections current transaction is concluded. ^The -** callback is invoked from within the [sqlite3_step] or [sqlite3_close] -** call that concludes the blocking connections transaction. -** -** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, -** there is a chance that the blocking connection will have already -** concluded its transaction by the time sqlite3_unlock_notify() is invoked. -** If this happens, then the specified callback is invoked immediately, -** from within the call to sqlite3_unlock_notify().)^ -** -** ^If the blocked connection is attempting to obtain a write-lock on a -** shared-cache table, and more than one other connection currently holds -** a read-lock on the same table, then SQLite arbitrarily selects one of -** the other connections to use as the blocking connection. -** -** ^(There may be at most one unlock-notify callback registered by a -** blocked connection. If sqlite3_unlock_notify() is called when the -** blocked connection already has a registered unlock-notify callback, -** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is -** called with a NULL pointer as its second argument, then any existing -** unlock-notify callback is canceled. ^The blocked connections -** unlock-notify callback may also be canceled by closing the blocked -** connection using [sqlite3_close()]. -** -** The unlock-notify callback is not reentrant. If an application invokes -** any sqlite3_xxx API functions from within an unlock-notify callback, a -** crash or deadlock may be the result. -** -** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always -** returns SQLITE_OK. -** -** Callback Invocation Details -** -** When an unlock-notify callback is registered, the application provides a -** single void* pointer that is passed to the callback when it is invoked. -** However, the signature of the callback function allows SQLite to pass -** it an array of void* context pointers. The first argument passed to -** an unlock-notify callback is a pointer to an array of void* pointers, -** and the second is the number of entries in the array. -** -** When a blocking connections transaction is concluded, there may be -** more than one blocked connection that has registered for an unlock-notify -** callback. ^If two or more such blocked connections have specified the -** same callback function, then instead of invoking the callback function -** multiple times, it is invoked once with the set of void* context pointers -** specified by the blocked connections bundled together into an array. -** This gives the application an opportunity to prioritize any actions -** related to the set of unblocked database connections. -** -** Deadlock Detection -** -** Assuming that after registering for an unlock-notify callback a -** database waits for the callback to be issued before taking any further -** action (a reasonable assumption), then using this API may cause the -** application to deadlock. For example, if connection X is waiting for -** connection Y's transaction to be concluded, and similarly connection -** Y is waiting on connection X's transaction, then neither connection -** will proceed and the system may remain deadlocked indefinitely. -** -** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock -** detection. ^If a given call to sqlite3_unlock_notify() would put the -** system in a deadlocked state, then SQLITE_LOCKED is returned and no -** unlock-notify callback is registered. The system is said to be in -** a deadlocked state if connection A has registered for an unlock-notify -** callback on the conclusion of connection B's transaction, and connection -** B has itself registered for an unlock-notify callback when connection -** A's transaction is concluded. ^Indirect deadlock is also detected, so -** the system is also considered to be deadlocked if connection B has -** registered for an unlock-notify callback on the conclusion of connection -** C's transaction, where connection C is waiting on connection A. ^Any -** number of levels of indirection are allowed. -** -** The "DROP TABLE" Exception -** -** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost -** always appropriate to call sqlite3_unlock_notify(). There is however, -** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement, -** SQLite checks if there are any currently executing SELECT statements -** that belong to the same connection. If there are, SQLITE_LOCKED is -** returned. In this case there is no "blocking connection", so invoking -** sqlite3_unlock_notify() results in the unlock-notify callback being -** invoked immediately. If the application then re-attempts the "DROP TABLE" -** or "DROP INDEX" query, an infinite loop might be the result. -** -** One way around this problem is to check the extended error code returned -** by an sqlite3_step() call. ^(If there is a blocking connection, then the -** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in -** the special "DROP TABLE/INDEX" case, the extended error code is just -** SQLITE_LOCKED.)^ -*/ -SQLITE_API int sqlite3_unlock_notify( - sqlite3 *pBlocked, /* Waiting connection */ - void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ - void *pNotifyArg /* Argument to pass to xNotify */ -); - - -/* -** CAPI3REF: String Comparison -** -** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications -** and extensions to compare the contents of two buffers containing UTF-8 -** strings in a case-independent fashion, using the same definition of "case -** independence" that SQLite uses internally when comparing identifiers. -*/ -SQLITE_API int sqlite3_stricmp(const char *, const char *); -SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); - -/* -** CAPI3REF: Error Logging Interface -** -** ^The [sqlite3_log()] interface writes a message into the error log -** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. -** ^If logging is enabled, the zFormat string and subsequent arguments are -** used with [sqlite3_snprintf()] to generate the final output string. -** -** The sqlite3_log() interface is intended for use by extensions such as -** virtual tables, collating functions, and SQL functions. While there is -** nothing to prevent an application from calling sqlite3_log(), doing so -** is considered bad form. -** -** The zFormat string must not be NULL. -** -** To avoid deadlocks and other threading problems, the sqlite3_log() routine -** will not use dynamically allocated memory. The log message is stored in -** a fixed-length buffer on the stack. If the log message is longer than -** a few hundred characters, it will be truncated to the length of the -** buffer. -*/ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); - -/* -** CAPI3REF: Write-Ahead Log Commit Hook -** -** ^The [sqlite3_wal_hook()] function is used to register a callback that -** will be invoked each time a database connection commits data to a -** [write-ahead log] (i.e. whenever a transaction is committed in -** [journal_mode | journal_mode=WAL mode]). -** -** ^The callback is invoked by SQLite after the commit has taken place and -** the associated write-lock on the database released, so the implementation -** may read, write or [checkpoint] the database as required. -** -** ^The first parameter passed to the callback function when it is invoked -** is a copy of the third parameter passed to sqlite3_wal_hook() when -** registering the callback. ^The second is a copy of the database handle. -** ^The third parameter is the name of the database that was written to - -** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter -** is the number of pages currently in the write-ahead log file, -** including those that were just committed. -** -** The callback function should normally return [SQLITE_OK]. ^If an error -** code is returned, that error will propagate back up through the -** SQLite code base to cause the statement that provoked the callback -** to report an error, though the commit will have still occurred. If the -** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value -** that does not correspond to any valid SQLite error code, the results -** are undefined. -** -** A single database handle may have at most a single write-ahead log callback -** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any -** previously registered write-ahead log callback. ^Note that the -** [sqlite3_wal_autocheckpoint()] interface and the -** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will -** those overwrite any prior [sqlite3_wal_hook()] settings. -*/ -SQLITE_API void *sqlite3_wal_hook( - sqlite3*, - int(*)(void *,sqlite3*,const char*,int), - void* -); - -/* -** CAPI3REF: Configure an auto-checkpoint -** -** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around -** [sqlite3_wal_hook()] that causes any database on [database connection] D -** to automatically [checkpoint] -** after committing a transaction if there are N or -** more frames in the [write-ahead log] file. ^Passing zero or -** a negative value as the nFrame parameter disables automatic -** checkpoints entirely. -** -** ^The callback registered by this function replaces any existing callback -** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback -** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism -** configured by this function. -** -** ^The [wal_autocheckpoint pragma] can be used to invoke this interface -** from SQL. -** -** ^Every new [database connection] defaults to having the auto-checkpoint -** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] -** pages. The use of this interface -** is only necessary if the default setting is found to be suboptimal -** for a particular application. -*/ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); - -/* -** CAPI3REF: Checkpoint a database -** -** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X -** on [database connection] D to be [checkpointed]. ^If X is NULL or an -** empty string, then a checkpoint is run on all databases of -** connection D. ^If the database connection D is not in -** [WAL | write-ahead log mode] then this interface is a harmless no-op. -** -** ^The [wal_checkpoint pragma] can be used to invoke this interface -** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the -** [wal_autocheckpoint pragma] can be used to cause this interface to be -** run whenever the WAL reaches a certain size threshold. -** -** See also: [sqlite3_wal_checkpoint_v2()] -*/ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); - -/* -** CAPI3REF: Checkpoint a database -** -** Run a checkpoint operation on WAL database zDb attached to database -** handle db. The specific operation is determined by the value of the -** eMode parameter: -** -**
-**
SQLITE_CHECKPOINT_PASSIVE
-** Checkpoint as many frames as possible without waiting for any database -** readers or writers to finish. Sync the db file if all frames in the log -** are checkpointed. This mode is the same as calling -** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked. -** -**
SQLITE_CHECKPOINT_FULL
-** This mode blocks (calls the busy-handler callback) until there is no -** database writer and all readers are reading from the most recent database -** snapshot. It then checkpoints all frames in the log file and syncs the -** database file. This call blocks database writers while it is running, -** but not database readers. -** -**
SQLITE_CHECKPOINT_RESTART
-** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after -** checkpointing the log file it blocks (calls the busy-handler callback) -** until all readers are reading from the database file only. This ensures -** that the next client to write to the database file restarts the log file -** from the beginning. This call blocks database writers while it is running, -** but not database readers. -**
-** -** If pnLog is not NULL, then *pnLog is set to the total number of frames in -** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to -** the total number of checkpointed frames (including any that were already -** checkpointed when this function is called). *pnLog and *pnCkpt may be -** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK. -** If no values are available because of an error, they are both set to -1 -** before returning to communicate this to the caller. -** -** All calls obtain an exclusive "checkpoint" lock on the database file. If -** any other process is running a checkpoint operation at the same time, the -** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a -** busy-handler configured, it will not be invoked in this case. -** -** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive -** "writer" lock on the database file. If the writer lock cannot be obtained -** immediately, and a busy-handler is configured, it is invoked and the writer -** lock retried until either the busy-handler returns 0 or the lock is -** successfully obtained. The busy-handler is also invoked while waiting for -** database readers as described above. If the busy-handler returns 0 before -** the writer lock is obtained or while waiting for database readers, the -** checkpoint operation proceeds from that point in the same way as -** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible -** without blocking any further. SQLITE_BUSY is returned in this case. -** -** If parameter zDb is NULL or points to a zero length string, then the -** specified operation is attempted on all WAL databases. In this case the -** values written to output parameters *pnLog and *pnCkpt are undefined. If -** an SQLITE_BUSY error is encountered when processing one or more of the -** attached WAL databases, the operation is still attempted on any remaining -** attached databases and SQLITE_BUSY is returned to the caller. If any other -** error occurs while processing an attached database, processing is abandoned -** and the error code returned to the caller immediately. If no error -** (SQLITE_BUSY or otherwise) is encountered while processing the attached -** databases, SQLITE_OK is returned. -** -** If database zDb is the name of an attached database that is not in WAL -** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If -** zDb is not NULL (or a zero length string) and is not the name of any -** attached database, SQLITE_ERROR is returned to the caller. -*/ -SQLITE_API int sqlite3_wal_checkpoint_v2( - sqlite3 *db, /* Database handle */ - const char *zDb, /* Name of attached database (or NULL) */ - int eMode, /* SQLITE_CHECKPOINT_* value */ - int *pnLog, /* OUT: Size of WAL log in frames */ - int *pnCkpt /* OUT: Total number of frames checkpointed */ -); - -/* -** CAPI3REF: Checkpoint operation parameters -** -** These constants can be used as the 3rd parameter to -** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] -** documentation for additional information about the meaning and use of -** each of these values. -*/ -#define SQLITE_CHECKPOINT_PASSIVE 0 -#define SQLITE_CHECKPOINT_FULL 1 -#define SQLITE_CHECKPOINT_RESTART 2 - -/* -** CAPI3REF: Virtual Table Interface Configuration -** -** This function may be called by either the [xConnect] or [xCreate] method -** of a [virtual table] implementation to configure -** various facets of the virtual table interface. -** -** If this interface is invoked outside the context of an xConnect or -** xCreate virtual table method then the behavior is undefined. -** -** At present, there is only one option that may be configured using -** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options -** may be added in the future. -*/ -SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); - -/* -** CAPI3REF: Virtual Table Configuration Options -** -** These macros define the various options to the -** [sqlite3_vtab_config()] interface that [virtual table] implementations -** can use to customize and optimize their behavior. -** -**
-**
SQLITE_VTAB_CONSTRAINT_SUPPORT -**
Calls of the form -** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, -** where X is an integer. If X is zero, then the [virtual table] whose -** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not -** support constraints. In this configuration (which is the default) if -** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire -** statement is rolled back as if [ON CONFLICT | OR ABORT] had been -** specified as part of the users SQL statement, regardless of the actual -** ON CONFLICT mode specified. -** -** If X is non-zero, then the virtual table implementation guarantees -** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before -** any modifications to internal or persistent data structures have been made. -** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite -** is able to roll back a statement or database transaction, and abandon -** or continue processing the current SQL statement as appropriate. -** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns -** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode -** had been ABORT. -** -** Virtual table implementations that are required to handle OR REPLACE -** must do so within the [xUpdate] method. If a call to the -** [sqlite3_vtab_on_conflict()] function indicates that the current ON -** CONFLICT policy is REPLACE, the virtual table implementation should -** silently replace the appropriate rows within the xUpdate callback and -** return SQLITE_OK. Or, if this is not possible, it may return -** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT -** constraint handling. -**
-*/ -#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 - -/* -** CAPI3REF: Determine The Virtual Table Conflict Policy -** -** This function may only be called from within a call to the [xUpdate] method -** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The -** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], -** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode -** of the SQL statement that triggered the call to the [xUpdate] method of the -** [virtual table]. -*/ -SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); - -/* -** CAPI3REF: Conflict resolution modes -** -** These constants are returned by [sqlite3_vtab_on_conflict()] to -** inform a [virtual table] implementation what the [ON CONFLICT] mode -** is for the SQL statement being evaluated. -** -** Note that the [SQLITE_IGNORE] constant is also used as a potential -** return value from the [sqlite3_set_authorizer()] callback and that -** [SQLITE_ABORT] is also a [result code]. -*/ -#define SQLITE_ROLLBACK 1 -/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ -#define SQLITE_FAIL 3 -/* #define SQLITE_ABORT 4 // Also an error code */ -#define SQLITE_REPLACE 5 - - - -/* -** Undo the hack that converts floating point types to integer for -** builds on processors without floating point support. -*/ -#ifdef SQLITE_OMIT_FLOATING_POINT -# undef double -#endif - -#if 0 -} /* End of the 'extern "C"' block */ -#endif -#endif - -/* -** 2010 August 30 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -*/ - -#ifndef _SQLITE3RTREE_H_ -#define _SQLITE3RTREE_H_ - - -#if 0 -extern "C" { -#endif - -typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; - -/* -** Register a geometry callback named zGeom that can be used as part of an -** R-Tree geometry query as follows: -** -** SELECT ... FROM WHERE MATCH $zGeom(... params ...) -*/ -SQLITE_API int sqlite3_rtree_geometry_callback( - sqlite3 *db, - const char *zGeom, -#ifdef SQLITE_RTREE_INT_ONLY - int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes), -#else - int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes), -#endif - void *pContext -); - - -/* -** A pointer to a structure of the following type is passed as the first -** argument to callbacks registered using rtree_geometry_callback(). -*/ -struct sqlite3_rtree_geometry { - void *pContext; /* Copy of pContext passed to s_r_g_c() */ - int nParam; /* Size of array aParam[] */ - double *aParam; /* Parameters passed to SQL geom function */ - void *pUser; /* Callback implementation user data */ - void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ -}; - - -#if 0 -} /* end of the 'extern "C"' block */ -#endif - -#endif /* ifndef _SQLITE3RTREE_H_ */ - - -/************** End of sqlite3.h *********************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ -/************** Include hash.h in the middle of sqliteInt.h ******************/ -/************** Begin file hash.h ********************************************/ -/* -** 2001 September 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This is the header file for the generic hash-table implementation -** used in SQLite. -*/ -#ifndef _SQLITE_HASH_H_ -#define _SQLITE_HASH_H_ - -/* Forward declarations of structures. */ -typedef struct Hash Hash; -typedef struct HashElem HashElem; - -/* A complete hash table is an instance of the following structure. -** The internals of this structure are intended to be opaque -- client -** code should not attempt to access or modify the fields of this structure -** directly. Change this structure only by using the routines below. -** However, some of the "procedures" and "functions" for modifying and -** accessing this structure are really macros, so we can't really make -** this structure opaque. -** -** All elements of the hash table are on a single doubly-linked list. -** Hash.first points to the head of this list. -** -** There are Hash.htsize buckets. Each bucket points to a spot in -** the global doubly-linked list. The contents of the bucket are the -** element pointed to plus the next _ht.count-1 elements in the list. -** -** Hash.htsize and Hash.ht may be zero. In that case lookup is done -** by a linear search of the global list. For small tables, the -** Hash.ht table is never allocated because if there are few elements -** in the table, it is faster to do a linear search than to manage -** the hash table. -*/ -struct Hash { - unsigned int htsize; /* Number of buckets in the hash table */ - unsigned int count; /* Number of entries in this table */ - HashElem *first; /* The first element of the array */ - struct _ht { /* the hash table */ - int count; /* Number of entries with this hash */ - HashElem *chain; /* Pointer to first entry with this hash */ - } *ht; -}; - -/* Each element in the hash table is an instance of the following -** structure. All elements are stored on a single doubly-linked list. -** -** Again, this structure is intended to be opaque, but it can't really -** be opaque because it is used by macros. -*/ -struct HashElem { - HashElem *next, *prev; /* Next and previous elements in the table */ - void *data; /* Data associated with this element */ - const char *pKey; int nKey; /* Key associated with this element */ -}; - -/* -** Access routines. To delete, insert a NULL pointer. -*/ -SQLITE_PRIVATE void sqlite3HashInit(Hash*); -SQLITE_PRIVATE void *sqlite3HashInsert(Hash*, const char *pKey, int nKey, void *pData); -SQLITE_PRIVATE void *sqlite3HashFind(const Hash*, const char *pKey, int nKey); -SQLITE_PRIVATE void sqlite3HashClear(Hash*); - -/* -** Macros for looping over all elements of a hash table. The idiom is -** like this: -** -** Hash h; -** HashElem *p; -** ... -** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){ -** SomeStructure *pData = sqliteHashData(p); -** // do something with pData -** } -*/ -#define sqliteHashFirst(H) ((H)->first) -#define sqliteHashNext(E) ((E)->next) -#define sqliteHashData(E) ((E)->data) -/* #define sqliteHashKey(E) ((E)->pKey) // NOT USED */ -/* #define sqliteHashKeysize(E) ((E)->nKey) // NOT USED */ - -/* -** Number of entries in a hash table -*/ -/* #define sqliteHashCount(H) ((H)->count) // NOT USED */ - -#endif /* _SQLITE_HASH_H_ */ - -/************** End of hash.h ************************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ -/************** Include parse.h in the middle of sqliteInt.h *****************/ -/************** Begin file parse.h *******************************************/ -#define TK_SEMI 1 -#define TK_EXPLAIN 2 -#define TK_QUERY 3 -#define TK_PLAN 4 -#define TK_BEGIN 5 -#define TK_TRANSACTION 6 -#define TK_DEFERRED 7 -#define TK_IMMEDIATE 8 -#define TK_EXCLUSIVE 9 -#define TK_COMMIT 10 -#define TK_END 11 -#define TK_ROLLBACK 12 -#define TK_SAVEPOINT 13 -#define TK_RELEASE 14 -#define TK_TO 15 -#define TK_TABLE 16 -#define TK_CREATE 17 -#define TK_IF 18 -#define TK_NOT 19 -#define TK_EXISTS 20 -#define TK_TEMP 21 -#define TK_LP 22 -#define TK_RP 23 -#define TK_AS 24 -#define TK_COMMA 25 -#define TK_ID 26 -#define TK_INDEXED 27 -#define TK_ABORT 28 -#define TK_ACTION 29 -#define TK_AFTER 30 -#define TK_ANALYZE 31 -#define TK_ASC 32 -#define TK_ATTACH 33 -#define TK_BEFORE 34 -#define TK_BY 35 -#define TK_CASCADE 36 -#define TK_CAST 37 -#define TK_COLUMNKW 38 -#define TK_CONFLICT 39 -#define TK_DATABASE 40 -#define TK_DESC 41 -#define TK_DETACH 42 -#define TK_EACH 43 -#define TK_FAIL 44 -#define TK_FOR 45 -#define TK_IGNORE 46 -#define TK_INITIALLY 47 -#define TK_INSTEAD 48 -#define TK_LIKE_KW 49 -#define TK_MATCH 50 -#define TK_NO 51 -#define TK_KEY 52 -#define TK_OF 53 -#define TK_OFFSET 54 -#define TK_PRAGMA 55 -#define TK_RAISE 56 -#define TK_REPLACE 57 -#define TK_RESTRICT 58 -#define TK_ROW 59 -#define TK_TRIGGER 60 -#define TK_VACUUM 61 -#define TK_VIEW 62 -#define TK_VIRTUAL 63 -#define TK_REINDEX 64 -#define TK_RENAME 65 -#define TK_CTIME_KW 66 -#define TK_ANY 67 -#define TK_OR 68 -#define TK_AND 69 -#define TK_IS 70 -#define TK_BETWEEN 71 -#define TK_IN 72 -#define TK_ISNULL 73 -#define TK_NOTNULL 74 -#define TK_NE 75 -#define TK_EQ 76 -#define TK_GT 77 -#define TK_LE 78 -#define TK_LT 79 -#define TK_GE 80 -#define TK_ESCAPE 81 -#define TK_BITAND 82 -#define TK_BITOR 83 -#define TK_LSHIFT 84 -#define TK_RSHIFT 85 -#define TK_PLUS 86 -#define TK_MINUS 87 -#define TK_STAR 88 -#define TK_SLASH 89 -#define TK_REM 90 -#define TK_CONCAT 91 -#define TK_COLLATE 92 -#define TK_BITNOT 93 -#define TK_STRING 94 -#define TK_JOIN_KW 95 -#define TK_CONSTRAINT 96 -#define TK_DEFAULT 97 -#define TK_NULL 98 -#define TK_PRIMARY 99 -#define TK_UNIQUE 100 -#define TK_CHECK 101 -#define TK_REFERENCES 102 -#define TK_AUTOINCR 103 -#define TK_ON 104 -#define TK_INSERT 105 -#define TK_DELETE 106 -#define TK_UPDATE 107 -#define TK_SET 108 -#define TK_DEFERRABLE 109 -#define TK_FOREIGN 110 -#define TK_DROP 111 -#define TK_UNION 112 -#define TK_ALL 113 -#define TK_EXCEPT 114 -#define TK_INTERSECT 115 -#define TK_SELECT 116 -#define TK_DISTINCT 117 -#define TK_DOT 118 -#define TK_FROM 119 -#define TK_JOIN 120 -#define TK_USING 121 -#define TK_ORDER 122 -#define TK_GROUP 123 -#define TK_HAVING 124 -#define TK_LIMIT 125 -#define TK_WHERE 126 -#define TK_INTO 127 -#define TK_VALUES 128 -#define TK_INTEGER 129 -#define TK_FLOAT 130 -#define TK_BLOB 131 -#define TK_REGISTER 132 -#define TK_VARIABLE 133 -#define TK_CASE 134 -#define TK_WHEN 135 -#define TK_THEN 136 -#define TK_ELSE 137 -#define TK_INDEX 138 -#define TK_ALTER 139 -#define TK_ADD 140 -#define TK_TO_TEXT 141 -#define TK_TO_BLOB 142 -#define TK_TO_NUMERIC 143 -#define TK_TO_INT 144 -#define TK_TO_REAL 145 -#define TK_ISNOT 146 -#define TK_END_OF_FILE 147 -#define TK_ILLEGAL 148 -#define TK_SPACE 149 -#define TK_UNCLOSED_STRING 150 -#define TK_FUNCTION 151 -#define TK_COLUMN 152 -#define TK_AGG_FUNCTION 153 -#define TK_AGG_COLUMN 154 -#define TK_CONST_FUNC 155 -#define TK_UMINUS 156 -#define TK_UPLUS 157 - -/************** End of parse.h ***********************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ -#include -#include -#include -#include -#include - -/* -** If compiling for a processor that lacks floating point support, -** substitute integer for floating-point -*/ -#ifdef SQLITE_OMIT_FLOATING_POINT -# define double sqlite_int64 -# define float sqlite_int64 -# define LONGDOUBLE_TYPE sqlite_int64 -# ifndef SQLITE_BIG_DBL -# define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) -# endif -# define SQLITE_OMIT_DATETIME_FUNCS 1 -# define SQLITE_OMIT_TRACE 1 -# undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT -# undef SQLITE_HAVE_ISNAN -#endif -#ifndef SQLITE_BIG_DBL -# define SQLITE_BIG_DBL (1e99) -#endif - -/* -** OMIT_TEMPDB is set to 1 if SQLITE_OMIT_TEMPDB is defined, or 0 -** afterward. Having this macro allows us to cause the C compiler -** to omit code used by TEMP tables without messy #ifndef statements. -*/ -#ifdef SQLITE_OMIT_TEMPDB -#define OMIT_TEMPDB 1 -#else -#define OMIT_TEMPDB 0 -#endif - -/* -** The "file format" number is an integer that is incremented whenever -** the VDBE-level file format changes. The following macros define the -** the default file format for new databases and the maximum file format -** that the library can read. -*/ -#define SQLITE_MAX_FILE_FORMAT 4 -#ifndef SQLITE_DEFAULT_FILE_FORMAT -# define SQLITE_DEFAULT_FILE_FORMAT 4 -#endif - -/* -** Determine whether triggers are recursive by default. This can be -** changed at run-time using a pragma. -*/ -#ifndef SQLITE_DEFAULT_RECURSIVE_TRIGGERS -# define SQLITE_DEFAULT_RECURSIVE_TRIGGERS 0 -#endif - -/* -** Provide a default value for SQLITE_TEMP_STORE in case it is not specified -** on the command-line -*/ -#ifndef SQLITE_TEMP_STORE -# define SQLITE_TEMP_STORE 1 -#endif - -/* -** GCC does not define the offsetof() macro so we'll have to do it -** ourselves. -*/ -#ifndef offsetof -#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) -#endif - -/* -** Check to see if this machine uses EBCDIC. (Yes, believe it or -** not, there are still machines out there that use EBCDIC.) -*/ -#if 'A' == '\301' -# define SQLITE_EBCDIC 1 -#else -# define SQLITE_ASCII 1 -#endif - -/* -** Integers of known sizes. These typedefs might change for architectures -** where the sizes very. Preprocessor macros are available so that the -** types can be conveniently redefined at compile-type. Like this: -** -** cc '-DUINTPTR_TYPE=long long int' ... -*/ -#ifndef UINT32_TYPE -# ifdef HAVE_UINT32_T -# define UINT32_TYPE uint32_t -# else -# define UINT32_TYPE unsigned int -# endif -#endif -#ifndef UINT16_TYPE -# ifdef HAVE_UINT16_T -# define UINT16_TYPE uint16_t -# else -# define UINT16_TYPE unsigned short int -# endif -#endif -#ifndef INT16_TYPE -# ifdef HAVE_INT16_T -# define INT16_TYPE int16_t -# else -# define INT16_TYPE short int -# endif -#endif -#ifndef UINT8_TYPE -# ifdef HAVE_UINT8_T -# define UINT8_TYPE uint8_t -# else -# define UINT8_TYPE unsigned char -# endif -#endif -#ifndef INT8_TYPE -# ifdef HAVE_INT8_T -# define INT8_TYPE int8_t -# else -# define INT8_TYPE signed char -# endif -#endif -#ifndef LONGDOUBLE_TYPE -# define LONGDOUBLE_TYPE long double -#endif -typedef sqlite_int64 i64; /* 8-byte signed integer */ -typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ -typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ -typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ -typedef INT16_TYPE i16; /* 2-byte signed integer */ -typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ -typedef INT8_TYPE i8; /* 1-byte signed integer */ - -/* -** SQLITE_MAX_U32 is a u64 constant that is the maximum u64 value -** that can be stored in a u32 without loss of data. The value -** is 0x00000000ffffffff. But because of quirks of some compilers, we -** have to specify the value in the less intuitive manner shown: -*/ -#define SQLITE_MAX_U32 ((((u64)1)<<32)-1) - -/* -** The datatype used to store estimates of the number of rows in a -** table or index. This is an unsigned integer type. For 99.9% of -** the world, a 32-bit integer is sufficient. But a 64-bit integer -** can be used at compile-time if desired. -*/ -#ifdef SQLITE_64BIT_STATS - typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */ -#else - typedef u32 tRowcnt; /* 32-bit is the default */ -#endif - -/* -** Macros to determine whether the machine is big or little endian, -** evaluated at runtime. -*/ -#ifdef SQLITE_AMALGAMATION -SQLITE_PRIVATE const int sqlite3one = 1; -#else -SQLITE_PRIVATE const int sqlite3one; -#endif -#if defined(i386) || defined(__i386__) || defined(_M_IX86)\ - || defined(__x86_64) || defined(__x86_64__) -# define SQLITE_BIGENDIAN 0 -# define SQLITE_LITTLEENDIAN 1 -# define SQLITE_UTF16NATIVE SQLITE_UTF16LE -#else -# define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0) -# define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1) -# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) -#endif - -/* -** Constants for the largest and smallest possible 64-bit signed integers. -** These macros are designed to work correctly on both 32-bit and 64-bit -** compilers. -*/ -#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) -#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) - -/* -** Round up a number to the next larger multiple of 8. This is used -** to force 8-byte alignment on 64-bit architectures. -*/ -#define ROUND8(x) (((x)+7)&~7) - -/* -** Round down to the nearest multiple of 8 -*/ -#define ROUNDDOWN8(x) ((x)&~7) - -/* -** Assert that the pointer X is aligned to an 8-byte boundary. This -** macro is used only within assert() to verify that the code gets -** all alignment restrictions correct. -** -** Except, if SQLITE_4_BYTE_ALIGNED_MALLOC is defined, then the -** underlying malloc() implemention might return us 4-byte aligned -** pointers. In that case, only verify 4-byte alignment. -*/ -#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC -# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0) -#else -# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) -#endif - - -/* -** An instance of the following structure is used to store the busy-handler -** callback for a given sqlite handle. -** -** The sqlite.busyHandler member of the sqlite struct contains the busy -** callback for the database handle. Each pager opened via the sqlite -** handle is passed a pointer to sqlite.busyHandler. The busy-handler -** callback is currently invoked only from within pager.c. -*/ -typedef struct BusyHandler BusyHandler; -struct BusyHandler { - int (*xFunc)(void *,int); /* The busy callback */ - void *pArg; /* First arg to busy callback */ - int nBusy; /* Incremented with each busy call */ -}; - -/* -** Name of the master database table. The master database table -** is a special table that holds the names and attributes of all -** user tables and indices. -*/ -#define MASTER_NAME "sqlite_master" -#define TEMP_MASTER_NAME "sqlite_temp_master" - -/* -** The root-page of the master database table. -*/ -#define MASTER_ROOT 1 - -/* -** The name of the schema table. -*/ -#define SCHEMA_TABLE(x) ((!OMIT_TEMPDB)&&(x==1)?TEMP_MASTER_NAME:MASTER_NAME) - -/* -** A convenience macro that returns the number of elements in -** an array. -*/ -#define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) - -/* -** Determine if the argument is a power of two -*/ -#define IsPowerOfTwo(X) (((X)&((X)-1))==0) - -/* -** The following value as a destructor means to use sqlite3DbFree(). -** The sqlite3DbFree() routine requires two parameters instead of the -** one parameter that destructors normally want. So we have to introduce -** this magic value that the code knows to handle differently. Any -** pointer will work here as long as it is distinct from SQLITE_STATIC -** and SQLITE_TRANSIENT. -*/ -#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize) - -/* -** When SQLITE_OMIT_WSD is defined, it means that the target platform does -** not support Writable Static Data (WSD) such as global and static variables. -** All variables must either be on the stack or dynamically allocated from -** the heap. When WSD is unsupported, the variable declarations scattered -** throughout the SQLite code must become constants instead. The SQLITE_WSD -** macro is used for this purpose. And instead of referencing the variable -** directly, we use its constant as a key to lookup the run-time allocated -** buffer that holds real variable. The constant is also the initializer -** for the run-time allocated buffer. -** -** In the usual case where WSD is supported, the SQLITE_WSD and GLOBAL -** macros become no-ops and have zero performance impact. -*/ -#ifdef SQLITE_OMIT_WSD - #define SQLITE_WSD const - #define GLOBAL(t,v) (*(t*)sqlite3_wsd_find((void*)&(v), sizeof(v))) - #define sqlite3GlobalConfig GLOBAL(struct Sqlite3Config, sqlite3Config) -SQLITE_API int sqlite3_wsd_init(int N, int J); -SQLITE_API void *sqlite3_wsd_find(void *K, int L); -#else - #define SQLITE_WSD - #define GLOBAL(t,v) v - #define sqlite3GlobalConfig sqlite3Config -#endif - -/* -** The following macros are used to suppress compiler warnings and to -** make it clear to human readers when a function parameter is deliberately -** left unused within the body of a function. This usually happens when -** a function is called via a function pointer. For example the -** implementation of an SQL aggregate step callback may not use the -** parameter indicating the number of arguments passed to the aggregate, -** if it knows that this is enforced elsewhere. -** -** When a function parameter is not used at all within the body of a function, -** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. -** However, these macros may also be used to suppress warnings related to -** parameters that may or may not be used depending on compilation options. -** For example those parameters only used in assert() statements. In these -** cases the parameters are named as per the usual conventions. -*/ -#define UNUSED_PARAMETER(x) (void)(x) -#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) - -/* -** Forward references to structures -*/ -typedef struct AggInfo AggInfo; -typedef struct AuthContext AuthContext; -typedef struct AutoincInfo AutoincInfo; -typedef struct Bitvec Bitvec; -typedef struct CollSeq CollSeq; -typedef struct Column Column; -typedef struct Db Db; -typedef struct Schema Schema; -typedef struct Expr Expr; -typedef struct ExprList ExprList; -typedef struct ExprSpan ExprSpan; -typedef struct FKey FKey; -typedef struct FuncDestructor FuncDestructor; -typedef struct FuncDef FuncDef; -typedef struct FuncDefHash FuncDefHash; -typedef struct IdList IdList; -typedef struct Index Index; -typedef struct IndexSample IndexSample; -typedef struct KeyClass KeyClass; -typedef struct KeyInfo KeyInfo; -typedef struct Lookaside Lookaside; -typedef struct LookasideSlot LookasideSlot; -typedef struct Module Module; -typedef struct NameContext NameContext; -typedef struct Parse Parse; -typedef struct RowSet RowSet; -typedef struct Savepoint Savepoint; -typedef struct Select Select; -typedef struct SelectDest SelectDest; -typedef struct SrcList SrcList; -typedef struct StrAccum StrAccum; -typedef struct Table Table; -typedef struct TableLock TableLock; -typedef struct Token Token; -typedef struct Trigger Trigger; -typedef struct TriggerPrg TriggerPrg; -typedef struct TriggerStep TriggerStep; -typedef struct UnpackedRecord UnpackedRecord; -typedef struct VTable VTable; -typedef struct VtabCtx VtabCtx; -typedef struct Walker Walker; -typedef struct WherePlan WherePlan; -typedef struct WhereInfo WhereInfo; -typedef struct WhereLevel WhereLevel; - -/* -** Defer sourcing vdbe.h and btree.h until after the "u8" and -** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque -** pointer types (i.e. FuncDef) defined above. -*/ -/************** Include btree.h in the middle of sqliteInt.h *****************/ -/************** Begin file btree.h *******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface that the sqlite B-Tree file -** subsystem. See comments in the source code for a detailed description -** of what each interface routine does. -*/ -#ifndef _BTREE_H_ -#define _BTREE_H_ - -/* TODO: This definition is just included so other modules compile. It -** needs to be revisited. -*/ -#define SQLITE_N_BTREE_META 10 - -/* -** If defined as non-zero, auto-vacuum is enabled by default. Otherwise -** it must be turned on for each database using "PRAGMA auto_vacuum = 1". -*/ -#ifndef SQLITE_DEFAULT_AUTOVACUUM - #define SQLITE_DEFAULT_AUTOVACUUM 0 -#endif - -#define BTREE_AUTOVACUUM_NONE 0 /* Do not do auto-vacuum */ -#define BTREE_AUTOVACUUM_FULL 1 /* Do full auto-vacuum */ -#define BTREE_AUTOVACUUM_INCR 2 /* Incremental vacuum */ - -/* -** Forward declarations of structure -*/ -typedef struct Btree Btree; -typedef struct BtCursor BtCursor; -typedef struct BtShared BtShared; - - -SQLITE_PRIVATE int sqlite3BtreeOpen( - sqlite3_vfs *pVfs, /* VFS to use with this b-tree */ - const char *zFilename, /* Name of database file to open */ - sqlite3 *db, /* Associated database connection */ - Btree **ppBtree, /* Return open Btree* here */ - int flags, /* Flags */ - int vfsFlags /* Flags passed through to VFS open */ -); - -/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the -** following values. -** -** NOTE: These values must match the corresponding PAGER_ values in -** pager.h. -*/ -#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */ -#define BTREE_MEMORY 2 /* This is an in-memory DB */ -#define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */ -#define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */ - -SQLITE_PRIVATE int sqlite3BtreeClose(Btree*); -SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int); -SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int); -SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); -SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); -SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); -SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); -SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); -SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); -SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) -SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); -#endif -SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); -SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); -SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); -SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); -SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); -SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); -SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int); -SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); -SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags); -SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); -SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); -SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); -SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); -SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); -SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); -SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int); - -SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); -SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); -SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); - -SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); - -/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR -** of the flags shown below. -** -** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set. -** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data -** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With -** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored -** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL -** indices.) -*/ -#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ -#define BTREE_BLOBKEY 2 /* Table has keys only - no data */ - -SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); -SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); -SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int); - -SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); -SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); - -SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p); - -/* -** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta -** should be one of the following values. The integer values are assigned -** to constants so that the offset of the corresponding field in an -** SQLite database header may be found using the following formula: -** -** offset = 36 + (idx * 4) -** -** For example, the free-page-count field is located at byte offset 36 of -** the database file header. The incr-vacuum-flag field is located at -** byte offset 64 (== 36+4*7). -*/ -#define BTREE_FREE_PAGE_COUNT 0 -#define BTREE_SCHEMA_VERSION 1 -#define BTREE_FILE_FORMAT 2 -#define BTREE_DEFAULT_CACHE_SIZE 3 -#define BTREE_LARGEST_ROOT_PAGE 4 -#define BTREE_TEXT_ENCODING 5 -#define BTREE_USER_VERSION 6 -#define BTREE_INCR_VACUUM 7 - -/* -** Values that may be OR'd together to form the second argument of an -** sqlite3BtreeCursorHints() call. -*/ -#define BTREE_BULKLOAD 0x00000001 - -SQLITE_PRIVATE int sqlite3BtreeCursor( - Btree*, /* BTree containing table to open */ - int iTable, /* Index of root page */ - int wrFlag, /* 1 for writing. 0 for read-only */ - struct KeyInfo*, /* First argument to compare function */ - BtCursor *pCursor /* Space to write cursor structure */ -); -SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); -SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); - -SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); -SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( - BtCursor*, - UnpackedRecord *pUnKey, - i64 intKey, - int bias, - int *pRes -); -SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*, int*); -SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*); -SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, - const void *pData, int nData, - int nZero, int bias, int seekResult); -SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes); -SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes); -SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int *pRes); -SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); -SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int *pRes); -SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); -SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); -SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); -SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); -SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); -SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); -SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64); -SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*); - -SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); -SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); - -SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); -SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *); -SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *); -SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); -SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask); - -#ifndef NDEBUG -SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); -#endif - -#ifndef SQLITE_OMIT_BTREECOUNT -SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *, i64 *); -#endif - -#ifdef SQLITE_TEST -SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); -SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); -#endif - -#ifndef SQLITE_OMIT_WAL -SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); -#endif - -/* -** If we are not using shared cache, then there is no need to -** use mutexes to access the BtShared structures. So make the -** Enter and Leave procedures no-ops. -*/ -#ifndef SQLITE_OMIT_SHARED_CACHE -SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*); -SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); -#else -# define sqlite3BtreeEnter(X) -# define sqlite3BtreeEnterAll(X) -#endif - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE -SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*); -SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); -SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); -SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); -SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); -#ifndef NDEBUG - /* These routines are used inside assert() statements only. */ -SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); -SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); -SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); -#endif -#else - -# define sqlite3BtreeSharable(X) 0 -# define sqlite3BtreeLeave(X) -# define sqlite3BtreeEnterCursor(X) -# define sqlite3BtreeLeaveCursor(X) -# define sqlite3BtreeLeaveAll(X) - -# define sqlite3BtreeHoldsMutex(X) 1 -# define sqlite3BtreeHoldsAllMutexes(X) 1 -# define sqlite3SchemaMutexHeld(X,Y,Z) 1 -#endif - - -#endif /* _BTREE_H_ */ - -/************** End of btree.h ***********************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ -/************** Include vdbe.h in the middle of sqliteInt.h ******************/ -/************** Begin file vdbe.h ********************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Header file for the Virtual DataBase Engine (VDBE) -** -** This header defines the interface to the virtual database engine -** or VDBE. The VDBE implements an abstract machine that runs a -** simple program to access and modify the underlying database. -*/ -#ifndef _SQLITE_VDBE_H_ -#define _SQLITE_VDBE_H_ -/* #include */ - -/* -** A single VDBE is an opaque structure named "Vdbe". Only routines -** in the source file sqliteVdbe.c are allowed to see the insides -** of this structure. -*/ -typedef struct Vdbe Vdbe; - -/* -** The names of the following types declared in vdbeInt.h are required -** for the VdbeOp definition. -*/ -typedef struct VdbeFunc VdbeFunc; -typedef struct Mem Mem; -typedef struct SubProgram SubProgram; - -/* -** A single instruction of the virtual machine has an opcode -** and as many as three operands. The instruction is recorded -** as an instance of the following structure: -*/ -struct VdbeOp { - u8 opcode; /* What operation to perform */ - signed char p4type; /* One of the P4_xxx constants for p4 */ - u8 opflags; /* Mask of the OPFLG_* flags in opcodes.h */ - u8 p5; /* Fifth parameter is an unsigned character */ - int p1; /* First operand */ - int p2; /* Second parameter (often the jump destination) */ - int p3; /* The third parameter */ - union { /* fourth parameter */ - int i; /* Integer value if p4type==P4_INT32 */ - void *p; /* Generic pointer */ - char *z; /* Pointer to data for string (char array) types */ - i64 *pI64; /* Used when p4type is P4_INT64 */ - double *pReal; /* Used when p4type is P4_REAL */ - FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ - VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */ - CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ - Mem *pMem; /* Used when p4type is P4_MEM */ - VTable *pVtab; /* Used when p4type is P4_VTAB */ - KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ - int *ai; /* Used when p4type is P4_INTARRAY */ - SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ - int (*xAdvance)(BtCursor *, int *); - } p4; -#ifdef SQLITE_DEBUG - char *zComment; /* Comment to improve readability */ -#endif -#ifdef VDBE_PROFILE - int cnt; /* Number of times this instruction was executed */ - u64 cycles; /* Total time spent executing this instruction */ -#endif -}; -typedef struct VdbeOp VdbeOp; - - -/* -** A sub-routine used to implement a trigger program. -*/ -struct SubProgram { - VdbeOp *aOp; /* Array of opcodes for sub-program */ - int nOp; /* Elements in aOp[] */ - int nMem; /* Number of memory cells required */ - int nCsr; /* Number of cursors required */ - int nOnce; /* Number of OP_Once instructions */ - void *token; /* id that may be used to recursive triggers */ - SubProgram *pNext; /* Next sub-program already visited */ -}; - -/* -** A smaller version of VdbeOp used for the VdbeAddOpList() function because -** it takes up less space. -*/ -struct VdbeOpList { - u8 opcode; /* What operation to perform */ - signed char p1; /* First operand */ - signed char p2; /* Second parameter (often the jump destination) */ - signed char p3; /* Third parameter */ -}; -typedef struct VdbeOpList VdbeOpList; - -/* -** Allowed values of VdbeOp.p4type -*/ -#define P4_NOTUSED 0 /* The P4 parameter is not used */ -#define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ -#define P4_STATIC (-2) /* Pointer to a static string */ -#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */ -#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */ -#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */ -#define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */ -#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */ -#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */ -#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */ -#define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ -#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ -#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ -#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ -#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ -#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ -#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */ - -/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure -** is made. That copy is freed when the Vdbe is finalized. But if the -** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still -** gets freed when the Vdbe is finalized so it still should be obtained -** from a single sqliteMalloc(). But no copy is made and the calling -** function should *not* try to free the KeyInfo. -*/ -#define P4_KEYINFO_HANDOFF (-16) -#define P4_KEYINFO_STATIC (-17) - -/* -** The Vdbe.aColName array contains 5n Mem structures, where n is the -** number of columns of data returned by the statement. -*/ -#define COLNAME_NAME 0 -#define COLNAME_DECLTYPE 1 -#define COLNAME_DATABASE 2 -#define COLNAME_TABLE 3 -#define COLNAME_COLUMN 4 -#ifdef SQLITE_ENABLE_COLUMN_METADATA -# define COLNAME_N 5 /* Number of COLNAME_xxx symbols */ -#else -# ifdef SQLITE_OMIT_DECLTYPE -# define COLNAME_N 1 /* Store only the name */ -# else -# define COLNAME_N 2 /* Store the name and decltype */ -# endif -#endif - -/* -** The following macro converts a relative address in the p2 field -** of a VdbeOp structure into a negative number so that -** sqlite3VdbeAddOpList() knows that the address is relative. Calling -** the macro again restores the address. -*/ -#define ADDR(X) (-1-(X)) - -/* -** The makefile scans the vdbe.c source file and creates the "opcodes.h" -** header file that defines a number for each opcode used by the VDBE. -*/ -/************** Include opcodes.h in the middle of vdbe.h ********************/ -/************** Begin file opcodes.h *****************************************/ -/* Automatically generated. Do not edit */ -/* See the mkopcodeh.awk script for details */ -#define OP_Goto 1 -#define OP_Gosub 2 -#define OP_Return 3 -#define OP_Yield 4 -#define OP_HaltIfNull 5 -#define OP_Halt 6 -#define OP_Integer 7 -#define OP_Int64 8 -#define OP_Real 130 /* same as TK_FLOAT */ -#define OP_String8 94 /* same as TK_STRING */ -#define OP_String 9 -#define OP_Null 10 -#define OP_Blob 11 -#define OP_Variable 12 -#define OP_Move 13 -#define OP_Copy 14 -#define OP_SCopy 15 -#define OP_ResultRow 16 -#define OP_Concat 91 /* same as TK_CONCAT */ -#define OP_Add 86 /* same as TK_PLUS */ -#define OP_Subtract 87 /* same as TK_MINUS */ -#define OP_Multiply 88 /* same as TK_STAR */ -#define OP_Divide 89 /* same as TK_SLASH */ -#define OP_Remainder 90 /* same as TK_REM */ -#define OP_CollSeq 17 -#define OP_Function 18 -#define OP_BitAnd 82 /* same as TK_BITAND */ -#define OP_BitOr 83 /* same as TK_BITOR */ -#define OP_ShiftLeft 84 /* same as TK_LSHIFT */ -#define OP_ShiftRight 85 /* same as TK_RSHIFT */ -#define OP_AddImm 20 -#define OP_MustBeInt 21 -#define OP_RealAffinity 22 -#define OP_ToText 141 /* same as TK_TO_TEXT */ -#define OP_ToBlob 142 /* same as TK_TO_BLOB */ -#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/ -#define OP_ToInt 144 /* same as TK_TO_INT */ -#define OP_ToReal 145 /* same as TK_TO_REAL */ -#define OP_Eq 76 /* same as TK_EQ */ -#define OP_Ne 75 /* same as TK_NE */ -#define OP_Lt 79 /* same as TK_LT */ -#define OP_Le 78 /* same as TK_LE */ -#define OP_Gt 77 /* same as TK_GT */ -#define OP_Ge 80 /* same as TK_GE */ -#define OP_Permutation 23 -#define OP_Compare 24 -#define OP_Jump 25 -#define OP_And 69 /* same as TK_AND */ -#define OP_Or 68 /* same as TK_OR */ -#define OP_Not 19 /* same as TK_NOT */ -#define OP_BitNot 93 /* same as TK_BITNOT */ -#define OP_Once 26 -#define OP_If 27 -#define OP_IfNot 28 -#define OP_IsNull 73 /* same as TK_ISNULL */ -#define OP_NotNull 74 /* same as TK_NOTNULL */ -#define OP_Column 29 -#define OP_Affinity 30 -#define OP_MakeRecord 31 -#define OP_Count 32 -#define OP_Savepoint 33 -#define OP_AutoCommit 34 -#define OP_Transaction 35 -#define OP_ReadCookie 36 -#define OP_SetCookie 37 -#define OP_VerifyCookie 38 -#define OP_OpenRead 39 -#define OP_OpenWrite 40 -#define OP_OpenAutoindex 41 -#define OP_OpenEphemeral 42 -#define OP_SorterOpen 43 -#define OP_OpenPseudo 44 -#define OP_Close 45 -#define OP_SeekLt 46 -#define OP_SeekLe 47 -#define OP_SeekGe 48 -#define OP_SeekGt 49 -#define OP_Seek 50 -#define OP_NotFound 51 -#define OP_Found 52 -#define OP_IsUnique 53 -#define OP_NotExists 54 -#define OP_Sequence 55 -#define OP_NewRowid 56 -#define OP_Insert 57 -#define OP_InsertInt 58 -#define OP_Delete 59 -#define OP_ResetCount 60 -#define OP_SorterCompare 61 -#define OP_SorterData 62 -#define OP_RowKey 63 -#define OP_RowData 64 -#define OP_Rowid 65 -#define OP_NullRow 66 -#define OP_Last 67 -#define OP_SorterSort 70 -#define OP_Sort 71 -#define OP_Rewind 72 -#define OP_SorterNext 81 -#define OP_Prev 92 -#define OP_Next 95 -#define OP_SorterInsert 96 -#define OP_IdxInsert 97 -#define OP_IdxDelete 98 -#define OP_IdxRowid 99 -#define OP_IdxLT 100 -#define OP_IdxGE 101 -#define OP_Destroy 102 -#define OP_Clear 103 -#define OP_CreateIndex 104 -#define OP_CreateTable 105 -#define OP_ParseSchema 106 -#define OP_LoadAnalysis 107 -#define OP_DropTable 108 -#define OP_DropIndex 109 -#define OP_DropTrigger 110 -#define OP_IntegrityCk 111 -#define OP_RowSetAdd 112 -#define OP_RowSetRead 113 -#define OP_RowSetTest 114 -#define OP_Program 115 -#define OP_Param 116 -#define OP_FkCounter 117 -#define OP_FkIfZero 118 -#define OP_MemMax 119 -#define OP_IfPos 120 -#define OP_IfNeg 121 -#define OP_IfZero 122 -#define OP_AggStep 123 -#define OP_AggFinal 124 -#define OP_Checkpoint 125 -#define OP_JournalMode 126 -#define OP_Vacuum 127 -#define OP_IncrVacuum 128 -#define OP_Expire 129 -#define OP_TableLock 131 -#define OP_VBegin 132 -#define OP_VCreate 133 -#define OP_VDestroy 134 -#define OP_VOpen 135 -#define OP_VFilter 136 -#define OP_VColumn 137 -#define OP_VNext 138 -#define OP_VRename 139 -#define OP_VUpdate 140 -#define OP_Pagecount 146 -#define OP_MaxPgcnt 147 -#define OP_Trace 148 -#define OP_Noop 149 -#define OP_Explain 150 - - -/* Properties such as "out2" or "jump" that are specified in -** comments following the "case" for each opcode in the vdbe.c -** are encoded into bitvectors as follows: -*/ -#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */ -#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */ -#define OPFLG_IN1 0x0004 /* in1: P1 is an input */ -#define OPFLG_IN2 0x0008 /* in2: P2 is an input */ -#define OPFLG_IN3 0x0010 /* in3: P3 is an input */ -#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */ -#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ -#define OPFLG_INITIALIZER {\ -/* 0 */ 0x00, 0x01, 0x01, 0x04, 0x04, 0x10, 0x00, 0x02,\ -/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x24,\ -/* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\ -/* 24 */ 0x00, 0x01, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00,\ -/* 32 */ 0x02, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00,\ -/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\ -/* 48 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x02,\ -/* 56 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 64 */ 0x00, 0x02, 0x00, 0x01, 0x4c, 0x4c, 0x01, 0x01,\ -/* 72 */ 0x01, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\ -/* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\ -/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x01, 0x24, 0x02, 0x01,\ -/* 96 */ 0x08, 0x08, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\ -/* 104 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 112 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\ -/* 120 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00,\ -/* 128 */ 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 136 */ 0x01, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x04,\ -/* 144 */ 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,} - -/************** End of opcodes.h *********************************************/ -/************** Continuing where we left off in vdbe.h ***********************/ - -/* -** Prototypes for the VDBE interface. See comments on the implementation -** for a description of what each of these routines does. -*/ -SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); -SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); -SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); -SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); -SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); -SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); -SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); -SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); -SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); -SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1); -SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2); -SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); -SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); -SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); -SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr); -SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); -SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); -SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); -SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3*,Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*); -SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); -SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); -#ifdef SQLITE_DEBUG -SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *, int); -SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); -#endif -SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe*); -SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe*,int); -SQLITE_PRIVATE int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); -SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe*); -SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); -SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); -SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); -SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8); -SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int); -#ifndef SQLITE_OMIT_TRACE -SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*); -#endif - -SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); -SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); -SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **); - -#ifndef SQLITE_OMIT_TRIGGER -SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); -#endif - - -#ifndef NDEBUG -SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...); -# define VdbeComment(X) sqlite3VdbeComment X -SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); -# define VdbeNoopComment(X) sqlite3VdbeNoopComment X -#else -# define VdbeComment(X) -# define VdbeNoopComment(X) -#endif - -#endif - -/************** End of vdbe.h ************************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ -/************** Include pager.h in the middle of sqliteInt.h *****************/ -/************** Begin file pager.h *******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface that the sqlite page cache -** subsystem. The page cache subsystem reads and writes a file a page -** at a time and provides a journal for rollback. -*/ - -#ifndef _PAGER_H_ -#define _PAGER_H_ - -/* -** Default maximum size for persistent journal files. A negative -** value means no limit. This value may be overridden using the -** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". -*/ -#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT - #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 -#endif - -/* -** The type used to represent a page number. The first page in a file -** is called page 1. 0 is used to represent "not a page". -*/ -typedef u32 Pgno; - -/* -** Each open file is managed by a separate instance of the "Pager" structure. -*/ -typedef struct Pager Pager; - -/* -** Handle type for pages. -*/ -typedef struct PgHdr DbPage; - -/* -** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is -** reserved for working around a windows/posix incompatibility). It is -** used in the journal to signify that the remainder of the journal file -** is devoted to storing a master journal name - there are no more pages to -** roll back. See comments for function writeMasterJournal() in pager.c -** for details. -*/ -#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) - -/* -** Allowed values for the flags parameter to sqlite3PagerOpen(). -** -** NOTE: These values must match the corresponding BTREE_ values in btree.h. -*/ -#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ -#define PAGER_MEMORY 0x0002 /* In-memory database */ - -/* -** Valid values for the second argument to sqlite3PagerLockingMode(). -*/ -#define PAGER_LOCKINGMODE_QUERY -1 -#define PAGER_LOCKINGMODE_NORMAL 0 -#define PAGER_LOCKINGMODE_EXCLUSIVE 1 - -/* -** Numeric constants that encode the journalmode. -*/ -#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ -#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ -#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ -#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ -#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ -#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ -#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ - -/* -** The remainder of this file contains the declarations of the functions -** that make up the Pager sub-system API. See source code comments for -** a detailed description of each routine. -*/ - -/* Open and close a Pager connection. */ -SQLITE_PRIVATE int sqlite3PagerOpen( - sqlite3_vfs*, - Pager **ppPager, - const char*, - int, - int, - int, - void(*)(DbPage*) -); -SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); -SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); - -/* Functions used to configure a Pager object. */ -SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); -SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); -SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); -SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); -SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); -SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int,int); -SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); -SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); -SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); -SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); -SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); -SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); - -/* Functions used to obtain and release page references. */ -SQLITE_PRIVATE int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); -#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0) -SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); -SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); -SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); - -/* Operations on page references. */ -SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); -SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); -SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); -SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); -SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); -SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); - -/* Functions used to manage pager transactions and savepoints. */ -SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); -SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); -SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); -SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); -SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); -SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); -SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); -SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); -SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); -SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); - -#ifndef SQLITE_OMIT_WAL -SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*); -SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); -SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); -SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); -SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager); -#endif - -#ifdef SQLITE_ENABLE_ZIPVFS -SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); -#endif - -/* Functions used to query pager state and configuration. */ -SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); -SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); -SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); -SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); -SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); -SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); -SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); -SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); -SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); -SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); -SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); -SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); -SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); - -/* Functions used to truncate the database file. */ -SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); - -#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) -SQLITE_PRIVATE void *sqlite3PagerCodec(DbPage *); -#endif - -/* Functions to support testing and debugging. */ -#if !defined(NDEBUG) || defined(SQLITE_TEST) -SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); -SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); -#endif -#ifdef SQLITE_TEST -SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); -SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); - void disable_simulated_io_errors(void); - void enable_simulated_io_errors(void); -#else -# define disable_simulated_io_errors() -# define enable_simulated_io_errors() -#endif - -#endif /* _PAGER_H_ */ - -/************** End of pager.h ***********************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ -/************** Include pcache.h in the middle of sqliteInt.h ****************/ -/************** Begin file pcache.h ******************************************/ -/* -** 2008 August 05 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface that the sqlite page cache -** subsystem. -*/ - -#ifndef _PCACHE_H_ - -typedef struct PgHdr PgHdr; -typedef struct PCache PCache; - -/* -** Every page in the cache is controlled by an instance of the following -** structure. -*/ -struct PgHdr { - sqlite3_pcache_page *pPage; /* Pcache object page handle */ - void *pData; /* Page data */ - void *pExtra; /* Extra content */ - PgHdr *pDirty; /* Transient list of dirty pages */ - Pager *pPager; /* The pager this page is part of */ - Pgno pgno; /* Page number for this page */ -#ifdef SQLITE_CHECK_PAGES - u32 pageHash; /* Hash of page content */ -#endif - u16 flags; /* PGHDR flags defined below */ - - /********************************************************************** - ** Elements above are public. All that follows is private to pcache.c - ** and should not be accessed by other modules. - */ - i16 nRef; /* Number of users of this page */ - PCache *pCache; /* Cache that owns this page */ - - PgHdr *pDirtyNext; /* Next element in list of dirty pages */ - PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ -}; - -/* Bit values for PgHdr.flags */ -#define PGHDR_DIRTY 0x002 /* Page has changed */ -#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before - ** writing this page to the database */ -#define PGHDR_NEED_READ 0x008 /* Content is unread */ -#define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */ -#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ - -/* Initialize and shutdown the page cache subsystem */ -SQLITE_PRIVATE int sqlite3PcacheInitialize(void); -SQLITE_PRIVATE void sqlite3PcacheShutdown(void); - -/* Page cache buffer management: -** These routines implement SQLITE_CONFIG_PAGECACHE. -*/ -SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *, int sz, int n); - -/* Create a new pager cache. -** Under memory stress, invoke xStress to try to make pages clean. -** Only clean and unpinned pages can be reclaimed. -*/ -SQLITE_PRIVATE void sqlite3PcacheOpen( - int szPage, /* Size of every page */ - int szExtra, /* Extra space associated with each page */ - int bPurgeable, /* True if pages are on backing store */ - int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */ - void *pStress, /* Argument to xStress */ - PCache *pToInit /* Preallocated space for the PCache */ -); - -/* Modify the page-size after the cache has been created. */ -SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *, int); - -/* Return the size in bytes of a PCache object. Used to preallocate -** storage space. -*/ -SQLITE_PRIVATE int sqlite3PcacheSize(void); - -/* One release per successful fetch. Page is pinned until released. -** Reference counted. -*/ -SQLITE_PRIVATE int sqlite3PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**); -SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr*); - -SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */ -SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */ -SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */ -SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */ - -/* Change a page number. Used by incr-vacuum. */ -SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr*, Pgno); - -/* Remove all pages with pgno>x. Reset the cache if x==0 */ -SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache*, Pgno x); - -/* Get a list of all dirty pages in the cache, sorted by page number */ -SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache*); - -/* Reset and close the cache object */ -SQLITE_PRIVATE void sqlite3PcacheClose(PCache*); - -/* Clear flags from pages of the page cache */ -SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); - -/* Discard the contents of the cache */ -SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); - -/* Return the total number of outstanding page references */ -SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*); - -/* Increment the reference count of an existing page */ -SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); - -SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*); - -/* Return the total number of pages stored in the cache */ -SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); - -#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) -/* Iterate through all dirty pages currently stored in the cache. This -** interface is only available if SQLITE_CHECK_PAGES is defined when the -** library is built. -*/ -SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)); -#endif - -/* Set and get the suggested cache-size for the specified pager-cache. -** -** If no global maximum is configured, then the system attempts to limit -** the total number of pages cached by purgeable pager-caches to the sum -** of the suggested cache-sizes. -*/ -SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int); -#ifdef SQLITE_TEST -SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *); -#endif - -/* Free up as much memory as possible from the page cache */ -SQLITE_PRIVATE void sqlite3PcacheShrink(PCache*); - -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -/* Try to return memory used by the pcache module to the main memory heap */ -SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int); -#endif - -#ifdef SQLITE_TEST -SQLITE_PRIVATE void sqlite3PcacheStats(int*,int*,int*,int*); -#endif - -SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); - -#endif /* _PCACHE_H_ */ - -/************** End of pcache.h **********************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ - -/************** Include os.h in the middle of sqliteInt.h ********************/ -/************** Begin file os.h **********************************************/ -/* -** 2001 September 16 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This header file (together with is companion C source-code file -** "os.c") attempt to abstract the underlying operating system so that -** the SQLite library will work on both POSIX and windows systems. -** -** This header file is #include-ed by sqliteInt.h and thus ends up -** being included by every source file. -*/ -#ifndef _SQLITE_OS_H_ -#define _SQLITE_OS_H_ - -/* -** Figure out if we are dealing with Unix, Windows, or some other -** operating system. After the following block of preprocess macros, -** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, and SQLITE_OS_OTHER -** will defined to either 1 or 0. One of the four will be 1. The other -** three will be 0. -*/ -#if defined(SQLITE_OS_OTHER) -# if SQLITE_OS_OTHER==1 -# undef SQLITE_OS_UNIX -# define SQLITE_OS_UNIX 0 -# undef SQLITE_OS_WIN -# define SQLITE_OS_WIN 0 -# else -# undef SQLITE_OS_OTHER -# endif -#endif -#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER) -# define SQLITE_OS_OTHER 0 -# ifndef SQLITE_OS_WIN -# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) -# define SQLITE_OS_WIN 1 -# define SQLITE_OS_UNIX 0 -# else -# define SQLITE_OS_WIN 0 -# define SQLITE_OS_UNIX 1 -# endif -# else -# define SQLITE_OS_UNIX 0 -# endif -#else -# ifndef SQLITE_OS_WIN -# define SQLITE_OS_WIN 0 -# endif -#endif - -#if SQLITE_OS_WIN -# include -#endif - -/* -** Determine if we are dealing with Windows NT. -** -** We ought to be able to determine if we are compiling for win98 or winNT -** using the _WIN32_WINNT macro as follows: -** -** #if defined(_WIN32_WINNT) -** # define SQLITE_OS_WINNT 1 -** #else -** # define SQLITE_OS_WINNT 0 -** #endif -** -** However, vs2005 does not set _WIN32_WINNT by default, as it ought to, -** so the above test does not work. We'll just assume that everything is -** winNT unless the programmer explicitly says otherwise by setting -** SQLITE_OS_WINNT to 0. -*/ -#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT) -# define SQLITE_OS_WINNT 1 -#endif - -/* -** Determine if we are dealing with WindowsCE - which has a much -** reduced API. -*/ -#if defined(_WIN32_WCE) -# define SQLITE_OS_WINCE 1 -#else -# define SQLITE_OS_WINCE 0 -#endif - -/* -** Determine if we are dealing with WinRT, which provides only a subset of -** the full Win32 API. -*/ -#if !defined(SQLITE_OS_WINRT) -# define SQLITE_OS_WINRT 0 -#endif - -/* -** When compiled for WinCE or WinRT, there is no concept of the current -** directory. - */ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT -# define SQLITE_CURDIR 1 -#endif - -/* If the SET_FULLSYNC macro is not defined above, then make it -** a no-op -*/ -#ifndef SET_FULLSYNC -# define SET_FULLSYNC(x,y) -#endif - -/* -** The default size of a disk sector -*/ -#ifndef SQLITE_DEFAULT_SECTOR_SIZE -# define SQLITE_DEFAULT_SECTOR_SIZE 4096 -#endif - -/* -** Temporary files are named starting with this prefix followed by 16 random -** alphanumeric characters, and no file extension. They are stored in the -** OS's standard temporary file directory, and are deleted prior to exit. -** If sqlite is being embedded in another program, you may wish to change the -** prefix to reflect your program's name, so that if your program exits -** prematurely, old temporary files can be easily identified. This can be done -** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. -** -** 2006-10-31: The default prefix used to be "sqlite_". But then -** Mcafee started using SQLite in their anti-virus product and it -** started putting files with the "sqlite" name in the c:/temp folder. -** This annoyed many windows users. Those users would then do a -** Google search for "sqlite", find the telephone numbers of the -** developers and call to wake them up at night and complain. -** For this reason, the default name prefix is changed to be "sqlite" -** spelled backwards. So the temp files are still identified, but -** anybody smart enough to figure out the code is also likely smart -** enough to know that calling the developer will not help get rid -** of the file. -*/ -#ifndef SQLITE_TEMP_FILE_PREFIX -# define SQLITE_TEMP_FILE_PREFIX "etilqs_" -#endif - -/* -** The following values may be passed as the second argument to -** sqlite3OsLock(). The various locks exhibit the following semantics: -** -** SHARED: Any number of processes may hold a SHARED lock simultaneously. -** RESERVED: A single process may hold a RESERVED lock on a file at -** any time. Other processes may hold and obtain new SHARED locks. -** PENDING: A single process may hold a PENDING lock on a file at -** any one time. Existing SHARED locks may persist, but no new -** SHARED locks may be obtained by other processes. -** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. -** -** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a -** process that requests an EXCLUSIVE lock may actually obtain a PENDING -** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to -** sqlite3OsLock(). -*/ -#define NO_LOCK 0 -#define SHARED_LOCK 1 -#define RESERVED_LOCK 2 -#define PENDING_LOCK 3 -#define EXCLUSIVE_LOCK 4 - -/* -** File Locking Notes: (Mostly about windows but also some info for Unix) -** -** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because -** those functions are not available. So we use only LockFile() and -** UnlockFile(). -** -** LockFile() prevents not just writing but also reading by other processes. -** A SHARED_LOCK is obtained by locking a single randomly-chosen -** byte out of a specific range of bytes. The lock byte is obtained at -** random so two separate readers can probably access the file at the -** same time, unless they are unlucky and choose the same lock byte. -** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. -** There can only be one writer. A RESERVED_LOCK is obtained by locking -** a single byte of the file that is designated as the reserved lock byte. -** A PENDING_LOCK is obtained by locking a designated byte different from -** the RESERVED_LOCK byte. -** -** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, -** which means we can use reader/writer locks. When reader/writer locks -** are used, the lock is placed on the same range of bytes that is used -** for probabilistic locking in Win95/98/ME. Hence, the locking scheme -** will support two or more Win95 readers or two or more WinNT readers. -** But a single Win95 reader will lock out all WinNT readers and a single -** WinNT reader will lock out all other Win95 readers. -** -** The following #defines specify the range of bytes used for locking. -** SHARED_SIZE is the number of bytes available in the pool from which -** a random byte is selected for a shared lock. The pool of bytes for -** shared locks begins at SHARED_FIRST. -** -** The same locking strategy and -** byte ranges are used for Unix. This leaves open the possiblity of having -** clients on win95, winNT, and unix all talking to the same shared file -** and all locking correctly. To do so would require that samba (or whatever -** tool is being used for file sharing) implements locks correctly between -** windows and unix. I'm guessing that isn't likely to happen, but by -** using the same locking range we are at least open to the possibility. -** -** Locking in windows is manditory. For this reason, we cannot store -** actual data in the bytes used for locking. The pager never allocates -** the pages involved in locking therefore. SHARED_SIZE is selected so -** that all locks will fit on a single page even at the minimum page size. -** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE -** is set high so that we don't have to allocate an unused page except -** for very large databases. But one should test the page skipping logic -** by setting PENDING_BYTE low and running the entire regression suite. -** -** Changing the value of PENDING_BYTE results in a subtly incompatible -** file format. Depending on how it is changed, you might not notice -** the incompatibility right away, even running a full regression test. -** The default location of PENDING_BYTE is the first byte past the -** 1GB boundary. -** -*/ -#ifdef SQLITE_OMIT_WSD -# define PENDING_BYTE (0x40000000) -#else -# define PENDING_BYTE sqlite3PendingByte -#endif -#define RESERVED_BYTE (PENDING_BYTE+1) -#define SHARED_FIRST (PENDING_BYTE+2) -#define SHARED_SIZE 510 - -/* -** Wrapper around OS specific sqlite3_os_init() function. -*/ -SQLITE_PRIVATE int sqlite3OsInit(void); - -/* -** Functions for accessing sqlite3_file methods -*/ -SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file*); -SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); -SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); -SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); -SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); -SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); -SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); -SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); -SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); -SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); -SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*); -#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 -SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); -SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); -SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); -SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int); -SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id); -SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int); - - -/* -** Functions for accessing sqlite3_vfs methods -*/ -SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); -SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); -SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); -SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); -#ifndef SQLITE_OMIT_LOAD_EXTENSION -SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); -SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); -SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); -SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); -#endif /* SQLITE_OMIT_LOAD_EXTENSION */ -SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); -SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); -SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*); - -/* -** Convenience functions for opening and closing files using -** sqlite3_malloc() to obtain space for the file-handle structure. -*/ -SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); -SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *); - -#endif /* _SQLITE_OS_H_ */ - -/************** End of os.h **************************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ -/************** Include mutex.h in the middle of sqliteInt.h *****************/ -/************** Begin file mutex.h *******************************************/ -/* -** 2007 August 28 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains the common header for all mutex implementations. -** The sqliteInt.h header #includes this file so that it is available -** to all source files. We break it out in an effort to keep the code -** better organized. -** -** NOTE: source files should *not* #include this header file directly. -** Source files should #include the sqliteInt.h file and let that file -** include this one indirectly. -*/ - - -/* -** Figure out what version of the code to use. The choices are -** -** SQLITE_MUTEX_OMIT No mutex logic. Not even stubs. The -** mutexes implemention cannot be overridden -** at start-time. -** -** SQLITE_MUTEX_NOOP For single-threaded applications. No -** mutual exclusion is provided. But this -** implementation can be overridden at -** start-time. -** -** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix. -** -** SQLITE_MUTEX_W32 For multi-threaded applications on Win32. -*/ -#if !SQLITE_THREADSAFE -# define SQLITE_MUTEX_OMIT -#endif -#if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP) -# if SQLITE_OS_UNIX -# define SQLITE_MUTEX_PTHREADS -# elif SQLITE_OS_WIN -# define SQLITE_MUTEX_W32 -# else -# define SQLITE_MUTEX_NOOP -# endif -#endif - -#ifdef SQLITE_MUTEX_OMIT -/* -** If this is a no-op implementation, implement everything as macros. -*/ -#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) -#define sqlite3_mutex_free(X) -#define sqlite3_mutex_enter(X) -#define sqlite3_mutex_try(X) SQLITE_OK -#define sqlite3_mutex_leave(X) -#define sqlite3_mutex_held(X) ((void)(X),1) -#define sqlite3_mutex_notheld(X) ((void)(X),1) -#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) -#define sqlite3MutexInit() SQLITE_OK -#define sqlite3MutexEnd() -#define MUTEX_LOGIC(X) -#else -#define MUTEX_LOGIC(X) X -#endif /* defined(SQLITE_MUTEX_OMIT) */ - -/************** End of mutex.h ***********************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ - - -/* -** Each database file to be accessed by the system is an instance -** of the following structure. There are normally two of these structures -** in the sqlite.aDb[] array. aDb[0] is the main database file and -** aDb[1] is the database file used to hold temporary tables. Additional -** databases may be attached. -*/ -struct Db { - char *zName; /* Name of this database */ - Btree *pBt; /* The B*Tree structure for this database file */ - u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ - u8 safety_level; /* How aggressive at syncing data to disk */ - Schema *pSchema; /* Pointer to database schema (possibly shared) */ -}; - -/* -** An instance of the following structure stores a database schema. -** -** Most Schema objects are associated with a Btree. The exception is -** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. -** In shared cache mode, a single Schema object can be shared by multiple -** Btrees that refer to the same underlying BtShared object. -** -** Schema objects are automatically deallocated when the last Btree that -** references them is destroyed. The TEMP Schema is manually freed by -** sqlite3_close(). -* -** A thread must be holding a mutex on the corresponding Btree in order -** to access Schema content. This implies that the thread must also be -** holding a mutex on the sqlite3 connection pointer that owns the Btree. -** For a TEMP Schema, only the connection mutex is required. -*/ -struct Schema { - int schema_cookie; /* Database schema version number for this file */ - int iGeneration; /* Generation counter. Incremented with each change */ - Hash tblHash; /* All tables indexed by name */ - Hash idxHash; /* All (named) indices indexed by name */ - Hash trigHash; /* All triggers indexed by name */ - Hash fkeyHash; /* All foreign keys by referenced table name */ - Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ - u8 file_format; /* Schema format version for this file */ - u8 enc; /* Text encoding used by this database */ - u16 flags; /* Flags associated with this schema */ - int cache_size; /* Number of pages to use in the cache */ -}; - -/* -** These macros can be used to test, set, or clear bits in the -** Db.pSchema->flags field. -*/ -#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P)) -#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0) -#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P) -#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P) - -/* -** Allowed values for the DB.pSchema->flags field. -** -** The DB_SchemaLoaded flag is set after the database schema has been -** read into internal hash tables. -** -** DB_UnresetViews means that one or more views have column names that -** have been filled out. If the schema changes, these column names might -** changes and so the view will need to be reset. -*/ -#define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ -#define DB_UnresetViews 0x0002 /* Some views have defined column names */ -#define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */ - -/* -** The number of different kinds of things that can be limited -** using the sqlite3_limit() interface. -*/ -#define SQLITE_N_LIMIT (SQLITE_LIMIT_TRIGGER_DEPTH+1) - -/* -** Lookaside malloc is a set of fixed-size buffers that can be used -** to satisfy small transient memory allocation requests for objects -** associated with a particular database connection. The use of -** lookaside malloc provides a significant performance enhancement -** (approx 10%) by avoiding numerous malloc/free requests while parsing -** SQL statements. -** -** The Lookaside structure holds configuration information about the -** lookaside malloc subsystem. Each available memory allocation in -** the lookaside subsystem is stored on a linked list of LookasideSlot -** objects. -** -** Lookaside allocations are only allowed for objects that are associated -** with a particular database connection. Hence, schema information cannot -** be stored in lookaside because in shared cache mode the schema information -** is shared by multiple database connections. Therefore, while parsing -** schema information, the Lookaside.bEnabled flag is cleared so that -** lookaside allocations are not used to construct the schema objects. -*/ -struct Lookaside { - u16 sz; /* Size of each buffer in bytes */ - u8 bEnabled; /* False to disable new lookaside allocations */ - u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ - int nOut; /* Number of buffers currently checked out */ - int mxOut; /* Highwater mark for nOut */ - int anStat[3]; /* 0: hits. 1: size misses. 2: full misses */ - LookasideSlot *pFree; /* List of available buffers */ - void *pStart; /* First byte of available memory space */ - void *pEnd; /* First byte past end of available space */ -}; -struct LookasideSlot { - LookasideSlot *pNext; /* Next buffer in the list of free buffers */ -}; - -/* -** A hash table for function definitions. -** -** Hash each FuncDef structure into one of the FuncDefHash.a[] slots. -** Collisions are on the FuncDef.pHash chain. -*/ -struct FuncDefHash { - FuncDef *a[23]; /* Hash table for functions */ -}; - -/* -** Each database connection is an instance of the following structure. -*/ -struct sqlite3 { - sqlite3_vfs *pVfs; /* OS Interface */ - struct Vdbe *pVdbe; /* List of active virtual machines */ - CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ - sqlite3_mutex *mutex; /* Connection mutex */ - Db *aDb; /* All backends */ - int nDb; /* Number of backends currently in use */ - int flags; /* Miscellaneous flags. See below */ - i64 lastRowid; /* ROWID of most recent insert (see above) */ - unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ - int errCode; /* Most recent error code (SQLITE_*) */ - int errMask; /* & result codes with this before returning */ - u16 dbOptFlags; /* Flags to enable/disable optimizations */ - u8 autoCommit; /* The auto-commit flag. */ - u8 temp_store; /* 1: file 2: memory 0: default */ - u8 mallocFailed; /* True if we have seen a malloc failure */ - u8 dfltLockMode; /* Default locking-mode for attached dbs */ - signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ - u8 suppressErr; /* Do not issue error messages if true */ - u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ - u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ - int nextPagesize; /* Pagesize after VACUUM if >0 */ - u32 magic; /* Magic number for detect library misuse */ - int nChange; /* Value returned by sqlite3_changes() */ - int nTotalChange; /* Value returned by sqlite3_total_changes() */ - int aLimit[SQLITE_N_LIMIT]; /* Limits */ - struct sqlite3InitInfo { /* Information used during initialization */ - int newTnum; /* Rootpage of table being initialized */ - u8 iDb; /* Which db file is being initialized */ - u8 busy; /* TRUE if currently initializing */ - u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ - } init; - int activeVdbeCnt; /* Number of VDBEs currently executing */ - int writeVdbeCnt; /* Number of active VDBEs that are writing */ - int vdbeExecCnt; /* Number of nested calls to VdbeExec() */ - int nExtension; /* Number of loaded extensions */ - void **aExtension; /* Array of shared library handles */ - void (*xTrace)(void*,const char*); /* Trace function */ - void *pTraceArg; /* Argument to the trace function */ - void (*xProfile)(void*,const char*,u64); /* Profiling function */ - void *pProfileArg; /* Argument to profile function */ - void *pCommitArg; /* Argument to xCommitCallback() */ - int (*xCommitCallback)(void*); /* Invoked at every commit. */ - void *pRollbackArg; /* Argument to xRollbackCallback() */ - void (*xRollbackCallback)(void*); /* Invoked at every commit. */ - void *pUpdateArg; - void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); -#ifndef SQLITE_OMIT_WAL - int (*xWalCallback)(void *, sqlite3 *, const char *, int); - void *pWalArg; -#endif - void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); - void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); - void *pCollNeededArg; - sqlite3_value *pErr; /* Most recent error message */ - char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ - char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ - union { - volatile int isInterrupted; /* True if sqlite3_interrupt has been called */ - double notUsed1; /* Spacer */ - } u1; - Lookaside lookaside; /* Lookaside malloc configuration */ -#ifndef SQLITE_OMIT_AUTHORIZATION - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); - /* Access authorization function */ - void *pAuthArg; /* 1st argument to the access auth function */ -#endif -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - int (*xProgress)(void *); /* The progress callback */ - void *pProgressArg; /* Argument to the progress callback */ - int nProgressOps; /* Number of opcodes for progress callback */ -#endif -#ifndef SQLITE_OMIT_VIRTUALTABLE - int nVTrans; /* Allocated size of aVTrans */ - Hash aModule; /* populated by sqlite3_create_module() */ - VtabCtx *pVtabCtx; /* Context for active vtab connect/create */ - VTable **aVTrans; /* Virtual tables with open transactions */ - VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ -#endif - FuncDefHash aFunc; /* Hash table of connection functions */ - Hash aCollSeq; /* All collating sequences */ - BusyHandler busyHandler; /* Busy callback */ - Db aDbStatic[2]; /* Static space for the 2 default backends */ - Savepoint *pSavepoint; /* List of active savepoints */ - int busyTimeout; /* Busy handler timeout, in msec */ - int nSavepoint; /* Number of non-transaction savepoints */ - int nStatement; /* Number of nested statement-transactions */ - i64 nDeferredCons; /* Net deferred constraints this transaction. */ - int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ - -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY - /* The following variables are all protected by the STATIC_MASTER - ** mutex, not by sqlite3.mutex. They are used by code in notify.c. - ** - ** When X.pUnlockConnection==Y, that means that X is waiting for Y to - ** unlock so that it can proceed. - ** - ** When X.pBlockingConnection==Y, that means that something that X tried - ** tried to do recently failed with an SQLITE_LOCKED error due to locks - ** held by Y. - */ - sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ - sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ - void *pUnlockArg; /* Argument to xUnlockNotify */ - void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ - sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ -#endif -}; - -/* -** A macro to discover the encoding of a database. -*/ -#define ENC(db) ((db)->aDb[0].pSchema->enc) - -/* -** Possible values for the sqlite3.flags. -*/ -#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ -#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */ -#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */ -#define SQLITE_ShortColNames 0x00000008 /* Show short columns names */ -#define SQLITE_CountRows 0x00000010 /* Count rows changed by INSERT, */ - /* DELETE, or UPDATE and return */ - /* the count using a callback. */ -#define SQLITE_NullCallback 0x00000020 /* Invoke the callback once if the */ - /* result set is empty */ -#define SQLITE_SqlTrace 0x00000040 /* Debug print SQL as it executes */ -#define SQLITE_VdbeListing 0x00000080 /* Debug listings of VDBE programs */ -#define SQLITE_WriteSchema 0x00000100 /* OK to update SQLITE_MASTER */ -#define SQLITE_VdbeAddopTrace 0x00000200 /* Trace sqlite3VdbeAddOp() calls */ -#define SQLITE_IgnoreChecks 0x00000400 /* Do not enforce check constraints */ -#define SQLITE_ReadUncommitted 0x0000800 /* For shared-cache mode */ -#define SQLITE_LegacyFileFmt 0x00001000 /* Create new databases in format 1 */ -#define SQLITE_FullFSync 0x00002000 /* Use full fsync on the backend */ -#define SQLITE_CkptFullFSync 0x00004000 /* Use full fsync for checkpoint */ -#define SQLITE_RecoveryMode 0x00008000 /* Ignore schema errors */ -#define SQLITE_ReverseOrder 0x00010000 /* Reverse unordered SELECTs */ -#define SQLITE_RecTriggers 0x00020000 /* Enable recursive triggers */ -#define SQLITE_ForeignKeys 0x00040000 /* Enforce foreign key constraints */ -#define SQLITE_AutoIndex 0x00080000 /* Enable automatic indexes */ -#define SQLITE_PreferBuiltin 0x00100000 /* Preference to built-in funcs */ -#define SQLITE_LoadExtension 0x00200000 /* Enable load_extension */ -#define SQLITE_EnableTrigger 0x00400000 /* True to enable triggers */ - -/* -** Bits of the sqlite3.dbOptFlags field that are used by the -** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to -** selectively disable various optimizations. -*/ -#define SQLITE_QueryFlattener 0x0001 /* Query flattening */ -#define SQLITE_ColumnCache 0x0002 /* Column cache */ -#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ -#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ -#define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ -#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ -#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ -#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ -#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ -#define SQLITE_Transitive 0x0200 /* Transitive constraints */ -#define SQLITE_AllOpts 0xffff /* All optimizations */ - -/* -** Macros for testing whether or not optimizations are enabled or disabled. -*/ -#ifndef SQLITE_OMIT_BUILTIN_TEST -#define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) -#define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0) -#else -#define OptimizationDisabled(db, mask) 0 -#define OptimizationEnabled(db, mask) 1 -#endif - -/* -** Possible values for the sqlite.magic field. -** The numbers are obtained at random and have no special meaning, other -** than being distinct from one another. -*/ -#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ -#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ -#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ -#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ -#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ -#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */ - -/* -** Each SQL function is defined by an instance of the following -** structure. A pointer to this structure is stored in the sqlite.aFunc -** hash table. When multiple functions have the same name, the hash table -** points to a linked list of these structures. -*/ -struct FuncDef { - i16 nArg; /* Number of arguments. -1 means unlimited */ - u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */ - u8 flags; /* Some combination of SQLITE_FUNC_* */ - void *pUserData; /* User data parameter */ - FuncDef *pNext; /* Next function with same name */ - void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ - void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ - void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ - char *zName; /* SQL name of the function. */ - FuncDef *pHash; /* Next with a different name but the same hash */ - FuncDestructor *pDestructor; /* Reference counted destructor function */ -}; - -/* -** This structure encapsulates a user-function destructor callback (as -** configured using create_function_v2()) and a reference counter. When -** create_function_v2() is called to create a function with a destructor, -** a single object of this type is allocated. FuncDestructor.nRef is set to -** the number of FuncDef objects created (either 1 or 3, depending on whether -** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor -** member of each of the new FuncDef objects is set to point to the allocated -** FuncDestructor. -** -** Thereafter, when one of the FuncDef objects is deleted, the reference -** count on this object is decremented. When it reaches 0, the destructor -** is invoked and the FuncDestructor structure freed. -*/ -struct FuncDestructor { - int nRef; - void (*xDestroy)(void *); - void *pUserData; -}; - -/* -** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF -** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There -** are assert() statements in the code to verify this. -*/ -#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ -#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ -#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ -#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ -#define SQLITE_FUNC_COUNT 0x10 /* Built-in count(*) aggregate */ -#define SQLITE_FUNC_COALESCE 0x20 /* Built-in coalesce() or ifnull() function */ -#define SQLITE_FUNC_LENGTH 0x40 /* Built-in length() function */ -#define SQLITE_FUNC_TYPEOF 0x80 /* Built-in typeof() function */ - -/* -** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are -** used to create the initializers for the FuncDef structures. -** -** FUNCTION(zName, nArg, iArg, bNC, xFunc) -** Used to create a scalar function definition of a function zName -** implemented by C function xFunc that accepts nArg arguments. The -** value passed as iArg is cast to a (void*) and made available -** as the user-data (sqlite3_user_data()) for the function. If -** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set. -** -** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal) -** Used to create an aggregate function definition implemented by -** the C functions xStep and xFinal. The first four parameters -** are interpreted in the same way as the first 4 parameters to -** FUNCTION(). -** -** LIKEFUNC(zName, nArg, pArg, flags) -** Used to create a scalar function definition of a function zName -** that accepts nArg arguments and is implemented by a call to C -** function likeFunc. Argument pArg is cast to a (void *) and made -** available as the function user-data (sqlite3_user_data()). The -** FuncDef.flags variable is set to the value passed as the flags -** parameter. -*/ -#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} -#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ - {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} -#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ - {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ - pArg, 0, xFunc, 0, 0, #zName, 0, 0} -#define LIKEFUNC(zName, nArg, arg, flags) \ - {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} -#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ - {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \ - SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0} - -/* -** All current savepoints are stored in a linked list starting at -** sqlite3.pSavepoint. The first element in the list is the most recently -** opened savepoint. Savepoints are added to the list by the vdbe -** OP_Savepoint instruction. -*/ -struct Savepoint { - char *zName; /* Savepoint name (nul-terminated) */ - i64 nDeferredCons; /* Number of deferred fk violations */ - Savepoint *pNext; /* Parent savepoint (if any) */ -}; - -/* -** The following are used as the second parameter to sqlite3Savepoint(), -** and as the P1 argument to the OP_Savepoint instruction. -*/ -#define SAVEPOINT_BEGIN 0 -#define SAVEPOINT_RELEASE 1 -#define SAVEPOINT_ROLLBACK 2 - - -/* -** Each SQLite module (virtual table definition) is defined by an -** instance of the following structure, stored in the sqlite3.aModule -** hash table. -*/ -struct Module { - const sqlite3_module *pModule; /* Callback pointers */ - const char *zName; /* Name passed to create_module() */ - void *pAux; /* pAux passed to create_module() */ - void (*xDestroy)(void *); /* Module destructor function */ -}; - -/* -** information about each column of an SQL table is held in an instance -** of this structure. -*/ -struct Column { - char *zName; /* Name of this column */ - Expr *pDflt; /* Default value of this column */ - char *zDflt; /* Original text of the default value */ - char *zType; /* Data type for this column */ - char *zColl; /* Collating sequence. If NULL, use the default */ - u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ - char affinity; /* One of the SQLITE_AFF_... values */ - u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ -}; - -/* Allowed values for Column.colFlags: -*/ -#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */ -#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */ - -/* -** A "Collating Sequence" is defined by an instance of the following -** structure. Conceptually, a collating sequence consists of a name and -** a comparison routine that defines the order of that sequence. -** -** If CollSeq.xCmp is NULL, it means that the -** collating sequence is undefined. Indices built on an undefined -** collating sequence may not be read or written. -*/ -struct CollSeq { - char *zName; /* Name of the collating sequence, UTF-8 encoded */ - u8 enc; /* Text encoding handled by xCmp() */ - void *pUser; /* First argument to xCmp() */ - int (*xCmp)(void*,int, const void*, int, const void*); - void (*xDel)(void*); /* Destructor for pUser */ -}; - -/* -** A sort order can be either ASC or DESC. -*/ -#define SQLITE_SO_ASC 0 /* Sort in ascending order */ -#define SQLITE_SO_DESC 1 /* Sort in ascending order */ - -/* -** Column affinity types. -** -** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and -** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve -** the speed a little by numbering the values consecutively. -** -** But rather than start with 0 or 1, we begin with 'a'. That way, -** when multiple affinity types are concatenated into a string and -** used as the P4 operand, they will be more readable. -** -** Note also that the numeric types are grouped together so that testing -** for a numeric type is a single comparison. -*/ -#define SQLITE_AFF_TEXT 'a' -#define SQLITE_AFF_NONE 'b' -#define SQLITE_AFF_NUMERIC 'c' -#define SQLITE_AFF_INTEGER 'd' -#define SQLITE_AFF_REAL 'e' - -#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) - -/* -** The SQLITE_AFF_MASK values masks off the significant bits of an -** affinity value. -*/ -#define SQLITE_AFF_MASK 0x67 - -/* -** Additional bit values that can be ORed with an affinity without -** changing the affinity. -*/ -#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ -#define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ -#define SQLITE_NULLEQ 0x80 /* NULL=NULL */ - -/* -** An object of this type is created for each virtual table present in -** the database schema. -** -** If the database schema is shared, then there is one instance of this -** structure for each database connection (sqlite3*) that uses the shared -** schema. This is because each database connection requires its own unique -** instance of the sqlite3_vtab* handle used to access the virtual table -** implementation. sqlite3_vtab* handles can not be shared between -** database connections, even when the rest of the in-memory database -** schema is shared, as the implementation often stores the database -** connection handle passed to it via the xConnect() or xCreate() method -** during initialization internally. This database connection handle may -** then be used by the virtual table implementation to access real tables -** within the database. So that they appear as part of the callers -** transaction, these accesses need to be made via the same database -** connection as that used to execute SQL operations on the virtual table. -** -** All VTable objects that correspond to a single table in a shared -** database schema are initially stored in a linked-list pointed to by -** the Table.pVTable member variable of the corresponding Table object. -** When an sqlite3_prepare() operation is required to access the virtual -** table, it searches the list for the VTable that corresponds to the -** database connection doing the preparing so as to use the correct -** sqlite3_vtab* handle in the compiled query. -** -** When an in-memory Table object is deleted (for example when the -** schema is being reloaded for some reason), the VTable objects are not -** deleted and the sqlite3_vtab* handles are not xDisconnect()ed -** immediately. Instead, they are moved from the Table.pVTable list to -** another linked list headed by the sqlite3.pDisconnect member of the -** corresponding sqlite3 structure. They are then deleted/xDisconnected -** next time a statement is prepared using said sqlite3*. This is done -** to avoid deadlock issues involving multiple sqlite3.mutex mutexes. -** Refer to comments above function sqlite3VtabUnlockList() for an -** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect -** list without holding the corresponding sqlite3.mutex mutex. -** -** The memory for objects of this type is always allocated by -** sqlite3DbMalloc(), using the connection handle stored in VTable.db as -** the first argument. -*/ -struct VTable { - sqlite3 *db; /* Database connection associated with this table */ - Module *pMod; /* Pointer to module implementation */ - sqlite3_vtab *pVtab; /* Pointer to vtab instance */ - int nRef; /* Number of pointers to this structure */ - u8 bConstraint; /* True if constraints are supported */ - int iSavepoint; /* Depth of the SAVEPOINT stack */ - VTable *pNext; /* Next in linked list (see above) */ -}; - -/* -** Each SQL table is represented in memory by an instance of the -** following structure. -** -** Table.zName is the name of the table. The case of the original -** CREATE TABLE statement is stored, but case is not significant for -** comparisons. -** -** Table.nCol is the number of columns in this table. Table.aCol is a -** pointer to an array of Column structures, one for each column. -** -** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of -** the column that is that key. Otherwise Table.iPKey is negative. Note -** that the datatype of the PRIMARY KEY must be INTEGER for this field to -** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of -** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid -** is generated for each row of the table. TF_HasPrimaryKey is set if -** the table has any PRIMARY KEY, INTEGER or otherwise. -** -** Table.tnum is the page number for the root BTree page of the table in the -** database file. If Table.iDb is the index of the database table backend -** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that -** holds temporary tables and indices. If TF_Ephemeral is set -** then the table is stored in a file that is automatically deleted -** when the VDBE cursor to the table is closed. In this case Table.tnum -** refers VDBE cursor number that holds the table open, not to the root -** page number. Transient tables are used to hold the results of a -** sub-query that appears instead of a real table name in the FROM clause -** of a SELECT statement. -*/ -struct Table { - char *zName; /* Name of the table or view */ - Column *aCol; /* Information about each column */ - Index *pIndex; /* List of SQL indexes on this table. */ - Select *pSelect; /* NULL for tables. Points to definition if a view. */ - FKey *pFKey; /* Linked list of all foreign keys in this table */ - char *zColAff; /* String defining the affinity of each column */ -#ifndef SQLITE_OMIT_CHECK - ExprList *pCheck; /* All CHECK constraints */ -#endif - tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ - int tnum; /* Root BTree node for this table (see note above) */ - i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */ - i16 nCol; /* Number of columns in this table */ - u16 nRef; /* Number of pointers to this Table */ - u8 tabFlags; /* Mask of TF_* values */ - u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ -#ifndef SQLITE_OMIT_ALTERTABLE - int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ -#endif -#ifndef SQLITE_OMIT_VIRTUALTABLE - int nModuleArg; /* Number of arguments to the module */ - char **azModuleArg; /* Text of all module args. [0] is module name */ - VTable *pVTable; /* List of VTable objects. */ -#endif - Trigger *pTrigger; /* List of triggers stored in pSchema */ - Schema *pSchema; /* Schema that contains this table */ - Table *pNextZombie; /* Next on the Parse.pZombieTab list */ -}; - -/* -** Allowed values for Tabe.tabFlags. -*/ -#define TF_Readonly 0x01 /* Read-only system table */ -#define TF_Ephemeral 0x02 /* An ephemeral table */ -#define TF_HasPrimaryKey 0x04 /* Table has a primary key */ -#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ -#define TF_Virtual 0x10 /* Is a virtual table */ - - -/* -** Test to see whether or not a table is a virtual table. This is -** done as a macro so that it will be optimized out when virtual -** table support is omitted from the build. -*/ -#ifndef SQLITE_OMIT_VIRTUALTABLE -# define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0) -# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0) -#else -# define IsVirtual(X) 0 -# define IsHiddenColumn(X) 0 -#endif - -/* -** Each foreign key constraint is an instance of the following structure. -** -** A foreign key is associated with two tables. The "from" table is -** the table that contains the REFERENCES clause that creates the foreign -** key. The "to" table is the table that is named in the REFERENCES clause. -** Consider this example: -** -** CREATE TABLE ex1( -** a INTEGER PRIMARY KEY, -** b INTEGER CONSTRAINT fk1 REFERENCES ex2(x) -** ); -** -** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2". -** -** Each REFERENCES clause generates an instance of the following structure -** which is attached to the from-table. The to-table need not exist when -** the from-table is created. The existence of the to-table is not checked. -*/ -struct FKey { - Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */ - FKey *pNextFrom; /* Next foreign key in pFrom */ - char *zTo; /* Name of table that the key points to (aka: Parent) */ - FKey *pNextTo; /* Next foreign key on table named zTo */ - FKey *pPrevTo; /* Previous foreign key on table named zTo */ - int nCol; /* Number of columns in this key */ - /* EV: R-30323-21917 */ - u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ - u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ - Trigger *apTrigger[2]; /* Triggers for aAction[] actions */ - struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ - int iFrom; /* Index of column in pFrom */ - char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ - } aCol[1]; /* One entry for each of nCol column s */ -}; - -/* -** SQLite supports many different ways to resolve a constraint -** error. ROLLBACK processing means that a constraint violation -** causes the operation in process to fail and for the current transaction -** to be rolled back. ABORT processing means the operation in process -** fails and any prior changes from that one operation are backed out, -** but the transaction is not rolled back. FAIL processing means that -** the operation in progress stops and returns an error code. But prior -** changes due to the same operation are not backed out and no rollback -** occurs. IGNORE means that the particular row that caused the constraint -** error is not inserted or updated. Processing continues and no error -** is returned. REPLACE means that preexisting database rows that caused -** a UNIQUE constraint violation are removed so that the new insert or -** update can proceed. Processing continues and no error is reported. -** -** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. -** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the -** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign -** key is set to NULL. CASCADE means that a DELETE or UPDATE of the -** referenced table row is propagated into the row that holds the -** foreign key. -** -** The following symbolic values are used to record which type -** of action to take. -*/ -#define OE_None 0 /* There is no constraint to check */ -#define OE_Rollback 1 /* Fail the operation and rollback the transaction */ -#define OE_Abort 2 /* Back out changes but do no rollback transaction */ -#define OE_Fail 3 /* Stop the operation but leave all prior changes */ -#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ -#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ - -#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */ -#define OE_SetNull 7 /* Set the foreign key value to NULL */ -#define OE_SetDflt 8 /* Set the foreign key value to its default */ -#define OE_Cascade 9 /* Cascade the changes */ - -#define OE_Default 99 /* Do whatever the default action is */ - - -/* -** An instance of the following structure is passed as the first -** argument to sqlite3VdbeKeyCompare and is used to control the -** comparison of the two index keys. -*/ -struct KeyInfo { - sqlite3 *db; /* The database connection */ - u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ - u16 nField; /* Number of entries in aColl[] */ - u8 *aSortOrder; /* Sort order for each column. May be NULL */ - CollSeq *aColl[1]; /* Collating sequence for each term of the key */ -}; - -/* -** An instance of the following structure holds information about a -** single index record that has already been parsed out into individual -** values. -** -** A record is an object that contains one or more fields of data. -** Records are used to store the content of a table row and to store -** the key of an index. A blob encoding of a record is created by -** the OP_MakeRecord opcode of the VDBE and is disassembled by the -** OP_Column opcode. -** -** This structure holds a record that has already been disassembled -** into its constituent fields. -*/ -struct UnpackedRecord { - KeyInfo *pKeyInfo; /* Collation and sort-order information */ - u16 nField; /* Number of entries in apMem[] */ - u8 flags; /* Boolean settings. UNPACKED_... below */ - i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */ - Mem *aMem; /* Values */ -}; - -/* -** Allowed values of UnpackedRecord.flags -*/ -#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */ -#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */ -#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */ - -/* -** Each SQL index is represented in memory by an -** instance of the following structure. -** -** The columns of the table that are to be indexed are described -** by the aiColumn[] field of this structure. For example, suppose -** we have the following table and index: -** -** CREATE TABLE Ex1(c1 int, c2 int, c3 text); -** CREATE INDEX Ex2 ON Ex1(c3,c1); -** -** In the Table structure describing Ex1, nCol==3 because there are -** three columns in the table. In the Index structure describing -** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed. -** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the -** first column to be indexed (c3) has an index of 2 in Ex1.aCol[]. -** The second column to be indexed (c1) has an index of 0 in -** Ex1.aCol[], hence Ex2.aiColumn[1]==0. -** -** The Index.onError field determines whether or not the indexed columns -** must be unique and what to do if they are not. When Index.onError=OE_None, -** it means this is not a unique index. Otherwise it is a unique index -** and the value of Index.onError indicate the which conflict resolution -** algorithm to employ whenever an attempt is made to insert a non-unique -** element. -*/ -struct Index { - char *zName; /* Name of this index */ - int *aiColumn; /* Which columns are used by this index. 1st is 0 */ - tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ - Table *pTable; /* The SQL table being indexed */ - char *zColAff; /* String defining the affinity of each column */ - Index *pNext; /* The next index associated with the same table */ - Schema *pSchema; /* Schema containing this index */ - u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ - char **azColl; /* Array of collation sequence names for index */ - int tnum; /* DB Page containing root of this index */ - u16 nColumn; /* Number of columns in table used by this index */ - u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ - unsigned bUnordered:1; /* Use this index for == or IN queries only */ -#ifdef SQLITE_ENABLE_STAT3 - int nSample; /* Number of elements in aSample[] */ - tRowcnt avgEq; /* Average nEq value for key values not in aSample */ - IndexSample *aSample; /* Samples of the left-most key */ -#endif -}; - -/* -** Each sample stored in the sqlite_stat3 table is represented in memory -** using a structure of this type. See documentation at the top of the -** analyze.c source file for additional information. -*/ -struct IndexSample { - union { - char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ - double r; /* Value if eType is SQLITE_FLOAT */ - i64 i; /* Value if eType is SQLITE_INTEGER */ - } u; - u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */ - int nByte; /* Size in byte of text or blob. */ - tRowcnt nEq; /* Est. number of rows where the key equals this sample */ - tRowcnt nLt; /* Est. number of rows where key is less than this sample */ - tRowcnt nDLt; /* Est. number of distinct keys less than this sample */ -}; - -/* -** Each token coming out of the lexer is an instance of -** this structure. Tokens are also used as part of an expression. -** -** Note if Token.z==0 then Token.dyn and Token.n are undefined and -** may contain random values. Do not make any assumptions about Token.dyn -** and Token.n when Token.z==0. -*/ -struct Token { - const char *z; /* Text of the token. Not NULL-terminated! */ - unsigned int n; /* Number of characters in this token */ -}; - -/* -** An instance of this structure contains information needed to generate -** code for a SELECT that contains aggregate functions. -** -** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a -** pointer to this structure. The Expr.iColumn field is the index in -** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate -** code for that node. -** -** AggInfo.pGroupBy and AggInfo.aFunc.pExpr point to fields within the -** original Select structure that describes the SELECT statement. These -** fields do not need to be freed when deallocating the AggInfo structure. -*/ -struct AggInfo { - u8 directMode; /* Direct rendering mode means take data directly - ** from source tables rather than from accumulators */ - u8 useSortingIdx; /* In direct mode, reference the sorting index rather - ** than the source table */ - int sortingIdx; /* Cursor number of the sorting index */ - int sortingIdxPTab; /* Cursor number of pseudo-table */ - int nSortingColumn; /* Number of columns in the sorting index */ - ExprList *pGroupBy; /* The group by clause */ - struct AggInfo_col { /* For each column used in source tables */ - Table *pTab; /* Source table */ - int iTable; /* Cursor number of the source table */ - int iColumn; /* Column number within the source table */ - int iSorterColumn; /* Column number in the sorting index */ - int iMem; /* Memory location that acts as accumulator */ - Expr *pExpr; /* The original expression */ - } *aCol; - int nColumn; /* Number of used entries in aCol[] */ - int nAccumulator; /* Number of columns that show through to the output. - ** Additional columns are used only as parameters to - ** aggregate functions */ - struct AggInfo_func { /* For each aggregate function */ - Expr *pExpr; /* Expression encoding the function */ - FuncDef *pFunc; /* The aggregate function implementation */ - int iMem; /* Memory location that acts as accumulator */ - int iDistinct; /* Ephemeral table used to enforce DISTINCT */ - } *aFunc; - int nFunc; /* Number of entries in aFunc[] */ -}; - -/* -** The datatype ynVar is a signed integer, either 16-bit or 32-bit. -** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater -** than 32767 we have to make it 32-bit. 16-bit is preferred because -** it uses less memory in the Expr object, which is a big memory user -** in systems with lots of prepared statements. And few applications -** need more than about 10 or 20 variables. But some extreme users want -** to have prepared statements with over 32767 variables, and for them -** the option is available (at compile-time). -*/ -#if SQLITE_MAX_VARIABLE_NUMBER<=32767 -typedef i16 ynVar; -#else -typedef int ynVar; -#endif - -/* -** Each node of an expression in the parse tree is an instance -** of this structure. -** -** Expr.op is the opcode. The integer parser token codes are reused -** as opcodes here. For example, the parser defines TK_GE to be an integer -** code representing the ">=" operator. This same integer code is reused -** to represent the greater-than-or-equal-to operator in the expression -** tree. -** -** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, -** or TK_STRING), then Expr.token contains the text of the SQL literal. If -** the expression is a variable (TK_VARIABLE), then Expr.token contains the -** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), -** then Expr.token contains the name of the function. -** -** Expr.pRight and Expr.pLeft are the left and right subexpressions of a -** binary operator. Either or both may be NULL. -** -** Expr.x.pList is a list of arguments if the expression is an SQL function, -** a CASE expression or an IN expression of the form " IN (, ...)". -** Expr.x.pSelect is used if the expression is a sub-select or an expression of -** the form " IN (SELECT ...)". If the EP_xIsSelect bit is set in the -** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is -** valid. -** -** An expression of the form ID or ID.ID refers to a column in a table. -** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is -** the integer cursor number of a VDBE cursor pointing to that table and -** Expr.iColumn is the column number for the specific column. If the -** expression is used as a result in an aggregate SELECT, then the -** value is also stored in the Expr.iAgg column in the aggregate so that -** it can be accessed after all aggregates are computed. -** -** If the expression is an unbound variable marker (a question mark -** character '?' in the original SQL) then the Expr.iTable holds the index -** number for that variable. -** -** If the expression is a subquery then Expr.iColumn holds an integer -** register number containing the result of the subquery. If the -** subquery gives a constant result, then iTable is -1. If the subquery -** gives a different answer at different times during statement processing -** then iTable is the address of a subroutine that computes the subquery. -** -** If the Expr is of type OP_Column, and the table it is selecting from -** is a disk table or the "old.*" pseudo-table, then pTab points to the -** corresponding table definition. -** -** ALLOCATION NOTES: -** -** Expr objects can use a lot of memory space in database schema. To -** help reduce memory requirements, sometimes an Expr object will be -** truncated. And to reduce the number of memory allocations, sometimes -** two or more Expr objects will be stored in a single memory allocation, -** together with Expr.zToken strings. -** -** If the EP_Reduced and EP_TokenOnly flags are set when -** an Expr object is truncated. When EP_Reduced is set, then all -** the child Expr objects in the Expr.pLeft and Expr.pRight subtrees -** are contained within the same memory allocation. Note, however, that -** the subtrees in Expr.x.pList or Expr.x.pSelect are always separately -** allocated, regardless of whether or not EP_Reduced is set. -*/ -struct Expr { - u8 op; /* Operation performed by this node */ - char affinity; /* The affinity of the column or 0 if not a column */ - u16 flags; /* Various flags. EP_* See below */ - union { - char *zToken; /* Token value. Zero terminated and dequoted */ - int iValue; /* Non-negative integer value if EP_IntValue */ - } u; - - /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no - ** space is allocated for the fields below this point. An attempt to - ** access them will result in a segfault or malfunction. - *********************************************************************/ - - Expr *pLeft; /* Left subnode */ - Expr *pRight; /* Right subnode */ - union { - ExprList *pList; /* Function arguments or in " IN ( IN (" \ - "" \ - ""; - cHTTPResponse Resp; - Resp.SetContentType("text/html"); - a_Connection.Send(Resp); - a_Connection.Send(LoginForm, sizeof(LoginForm) - 1); - a_Connection.FinishResponse(); -} - - - - - -sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) -{ - sWebAdminPage Page; - AStringVector Split = StringSplit(a_Request.Path, "/"); - - // Find the plugin that corresponds to the requested path - AString FoundPlugin; - if (Split.size() > 1) - { - for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) - { - if ((*itr)->GetWebTitle() == Split[1]) - { - Page.Content = (*itr)->HandleWebRequest(&a_Request); - cWebPlugin * WebPlugin = *itr; - FoundPlugin = WebPlugin->GetWebTitle(); - AString TabName = WebPlugin->GetTabNameForRequest(&a_Request).first; - Page.PluginName = FoundPlugin; - Page.TabName = TabName; - break; - } - } - } - - // Return the page contents - return Page; -} - - - - - -AString cWebAdmin::GetDefaultPage(void) -{ - AString Content; - Content += "

Server Name:

"; - Content += "

" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "

"; - - Content += "

Plugins:

    "; - cPluginManager * PM = cPluginManager::Get(); - const cPluginManager::PluginMap & List = PM->GetAllPlugins(); - for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr) - { - if (itr->second == NULL) - { - continue; - } - AString VersionNum; - AppendPrintf(Content, "
  • %s V.%i
  • ", itr->second->GetName().c_str(), itr->second->GetVersion()); - } - Content += "
"; - Content += "

Players:

    "; - - cPlayerAccum PlayerAccum; - cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players - if( World != NULL ) - { - World->ForEachPlayer(PlayerAccum); - Content.append(PlayerAccum.m_Contents); - } - Content += "

"; - return Content; -} - - - - -AString cWebAdmin::GetBaseURL( const AString& a_URL ) -{ - return GetBaseURL(StringSplit(a_URL, "/")); -} - - - - - -AString cWebAdmin::GetHTMLEscapedString(const AString & a_Input) -{ - AString dst; - dst.reserve(a_Input.length()); - - // Loop over input and substitute HTML characters for their alternatives: - size_t len = a_Input.length(); - for (size_t i = 0; i < len; i++) - { - switch (a_Input[i]) - { - case '&': dst.append("&"); break; - case '\'': dst.append("'"); break; - case '"': dst.append("""); break; - case '<': dst.append("<"); break; - case '>': dst.append(">"); break; - default: - { - dst.push_back(a_Input[i]); - break; - } - } // switch (a_Input[i]) - } // for i - a_Input[] - - return dst; -} - - - - - -AString cWebAdmin::GetBaseURL(const AStringVector & a_URLSplit) -{ - AString BaseURL = "./"; - if (a_URLSplit.size() > 1) - { - for (unsigned int i = 0; i < a_URLSplit.size(); i++) - { - BaseURL += "../"; - } - BaseURL += "webadmin/"; - } - return BaseURL; -} - - - - - -void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) -{ - const AString & URL = a_Request.GetURL(); - if ( - (strncmp(URL.c_str(), "/webadmin", 9) == 0) || - (strncmp(URL.c_str(), "/~webadmin", 10) == 0) - ) - { - a_Request.SetUserData(new cWebadminRequestData(a_Request)); - return; - } - if (URL == "/") - { - // The root needs no body handler and is fully handled in the OnRequestFinished() call - return; - } - // TODO: Handle other requests -} - - - - - -void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) -{ - cRequestData * Data = (cRequestData *)(a_Request.GetUserData()); - if (Data == NULL) - { - return; - } - Data->OnBody(a_Data, a_Size); -} - - - - - -void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) -{ - const AString & URL = a_Request.GetURL(); - if ( - (strncmp(URL.c_str(), "/webadmin", 9) == 0) || - (strncmp(URL.c_str(), "/~webadmin", 10) == 0) - ) - { - HandleWebadminRequest(a_Connection, a_Request); - } - else if (URL == "/") - { - // The root needs no body handler and is fully handled in the OnRequestFinished() call - HandleRootRequest(a_Connection, a_Request); - } - else - { - // TODO: Handle other requests - } - - // Delete any request data assigned to the request: - cRequestData * Data = (cRequestData *)(a_Request.GetUserData()); - delete Data; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWebAdmin::cWebadminRequestData - -void cWebAdmin::cWebadminRequestData::OnBody(const char * a_Data, int a_Size) -{ - m_Form.Parse(a_Data, a_Size); -} - - - - diff --git a/source/WebAdmin.h b/source/WebAdmin.h deleted file mode 100644 index dc6ea850e..000000000 --- a/source/WebAdmin.h +++ /dev/null @@ -1,215 +0,0 @@ - -// WebAdmin.h - -// Declares the cWebAdmin class representing the admin interface over http protocol, and related services (API) - -#pragma once - -#include "OSSupport/Socket.h" -#include "LuaState.h" -#include "../iniFile/iniFile.h" -#include "HTTPServer/HTTPServer.h" -#include "HTTPServer/HTTPFormParser.h" - - - - - -// Disable MSVC warnings: -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4355) // 'this' : used in base member initializer list -#endif - - - - - -// fwd: -class cEvent; -class cWebPlugin; - - - - - -// tolua_begin -struct HTTPFormData -{ - std::string Name; - std::string Value; - std::string Type; -} ; -// tolua_end - - - - -// tolua_begin -struct HTTPRequest -{ - typedef std::map< std::string, std::string > StringStringMap; - typedef std::map< std::string, HTTPFormData > FormDataMap; - - AString Method; - AString Path; - AString Username; - // tolua_end - - /// Parameters given in the URL, after the questionmark - StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS << - - /// Parameters posted as a part of a form - either in the URL (GET method) or in the body (POST method) - StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS << - - /// Same as PostParams - FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS << -} ; // tolua_export - - - - - -// tolua_begin -struct HTTPTemplateRequest -{ - HTTPRequest Request; -} ; -// tolua_end - - - - - -// tolua_begin -struct sWebAdminPage -{ - AString Content; - AString PluginName; - AString TabName; -}; -// tolua_end - - - - - -// tolua_begin -class cWebAdmin : - public cHTTPServer::cCallbacks -{ -public: - // tolua_end - - typedef std::list< cWebPlugin* > PluginList; - - - cWebAdmin(void); - ~cWebAdmin(); - - /// Initializes the object. Returns true if successfully initialized and ready to start - bool Init(void); - - /// Starts the HTTP server taking care of the admin. Returns true if successful - bool Start(void); - - void AddPlugin( cWebPlugin* a_Plugin ); - void RemovePlugin( cWebPlugin* a_Plugin ); - - // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such - PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS << - - // tolua_begin - - sWebAdminPage GetPage(const HTTPRequest & a_Request); - - /// Returns the contents of the default page - the list of plugins and players - AString GetDefaultPage(void); - - /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) - AString GetBaseURL(const AString & a_URL); - - /// Escapes text passed into it, so it can be embedded into html. - static AString GetHTMLEscapedString(const AString & a_Input); - - // tolua_end - - /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) - AString GetBaseURL(const AStringVector& a_URLSplit); - -protected: - /// Common base class for request body data handlers - class cRequestData - { - public: - virtual ~cRequestData() {} // Force a virtual destructor in all descendants - - /// Called when a new chunk of body data is received - virtual void OnBody(const char * a_Data, int a_Size) = 0; - } ; - - /// The body handler for requests in the "/webadmin" and "/~webadmin" paths - class cWebadminRequestData : - public cRequestData, - public cHTTPFormParser::cCallbacks - { - public: - cHTTPFormParser m_Form; - - - cWebadminRequestData(cHTTPRequest & a_Request) : - m_Form(a_Request, *this) - { - } - - // cRequestData overrides: - virtual void OnBody(const char * a_Data, int a_Size) override; - - // cHTTPFormParser::cCallbacks overrides. Files are ignored: - virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override {} - virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override {} - virtual void OnFileEnd(cHTTPFormParser & a_Parser) override {} - } ; - - - /// Set to true if Init() succeeds and the webadmin isn't to be disabled - bool m_IsInitialized; - - /// The webadmin.ini file, used for the settings and allowed logins - cIniFile m_IniFile; - - PluginList m_Plugins; - - /// The Lua template script to provide templates: - cLuaState m_TemplateScript; - - /// The HTTP server which provides the underlying HTTP parsing, serialization and events - cHTTPServer m_HTTPServer; - - - AString GetTemplate(void); - - /// Handles requests coming to the "/webadmin" or "/~webadmin" URLs - void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); - - /// Handles requests for the root page - void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); - - // cHTTPServer::cCallbacks overrides: - virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; - virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override; - virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; -} ; // tolua_export - - - - - -// Revert MSVC warnings back to orignal state: -#if defined(_MSC_VER) - #pragma warning(pop) -#endif - - - - diff --git a/source/WebPlugin.cpp b/source/WebPlugin.cpp deleted file mode 100644 index 48ddb2076..000000000 --- a/source/WebPlugin.cpp +++ /dev/null @@ -1,113 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "WebPlugin.h" -#include "WebAdmin.h" -#include "Server.h" -#include "Root.h" - - - - - -cWebPlugin::cWebPlugin() -{ - cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); - if (WebAdmin != NULL) - { - WebAdmin->AddPlugin(this); - } -} - - - - - -cWebPlugin::~cWebPlugin() -{ - cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); - if (WebAdmin != NULL) - { - WebAdmin->RemovePlugin(this); - } - - for (TabList::iterator itr = m_Tabs.begin(); itr != m_Tabs.end(); ++itr) - { - delete *itr; - } - m_Tabs.clear(); -} - - - - - -std::list > cWebPlugin::GetTabNames(void) -{ - std::list< std::pair< AString, AString > > NameList; - for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) - { - std::pair< AString, AString > StringPair; - StringPair.first = (*itr)->Title; - StringPair.second = (*itr)->SafeTitle; - NameList.push_back( StringPair ); - } - return NameList; -} - - - - - -std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest * a_Request) -{ - std::pair< AString, AString > Names; - AStringVector Split = StringSplit(a_Request->Path, "/"); - - if( Split.size() > 1 ) - { - sWebPluginTab* Tab = 0; - if( Split.size() > 2 ) // If we got the tab name, show that page - { - for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) - { - if( (*itr)->SafeTitle.compare( Split[2] ) == 0 ) // This is the one! Rawr - { - Tab = *itr; - break; - } - } - } - else // Otherwise show the first tab - { - if( GetTabs().size() > 0 ) - Tab = *GetTabs().begin(); - } - - if( Tab ) - { - Names.first = Tab->Title; - Names.second = Tab->SafeTitle; - } - } - - return Names; -} - - - - -AString cWebPlugin::SafeString( const AString & a_String ) -{ - AString RetVal; - for( unsigned int i = 0; i < a_String.size(); ++i ) - { - char c = a_String[i]; - if( c == ' ' ) - { - c = '_'; - } - RetVal.push_back( c ); - } - return RetVal; -} \ No newline at end of file diff --git a/source/WebPlugin.h b/source/WebPlugin.h deleted file mode 100644 index 22587b892..000000000 --- a/source/WebPlugin.h +++ /dev/null @@ -1,48 +0,0 @@ - -#pragma once - -struct lua_State; -struct HTTPRequest; - - - - - -// tolua_begin -class cWebPlugin -{ -public: - // tolua_end - cWebPlugin(); - virtual ~cWebPlugin(); - - // tolua_begin - virtual const AString GetWebTitle(void) const = 0; - - virtual AString HandleWebRequest(const HTTPRequest * a_Request ) = 0; - - static AString SafeString( const AString & a_String ); - // tolua_end - - struct sWebPluginTab - { - std::string Title; - std::string SafeTitle; - - int UserData; - }; - - typedef std::list< sWebPluginTab* > TabList; - TabList & GetTabs() { return m_Tabs; } - - typedef std::list< std::pair > TabNameList; - TabNameList GetTabNames(); // >> EXPORTED IN MANUALBINDINGS << - std::pair< AString, AString > GetTabNameForRequest(const HTTPRequest* a_Request ); - -private: - TabList m_Tabs; -}; // tolua_export - - - - diff --git a/source/World.cpp b/source/World.cpp deleted file mode 100644 index 0f9df8a62..000000000 --- a/source/World.cpp +++ /dev/null @@ -1,2715 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "BlockID.h" -#include "World.h" -#include "ChunkDef.h" -#include "ClientHandle.h" -#include "Server.h" -#include "Item.h" -#include "Root.h" -#include "../iniFile/iniFile.h" -#include "ChunkMap.h" -#include "OSSupport/Timer.h" - -// Entities (except mobs): -#include "Entities/Pickup.h" -#include "Entities/Player.h" -#include "Entities/TNTEntity.h" - -// Simulators: -#include "Simulator/SimulatorManager.h" -#include "Simulator/FloodyFluidSimulator.h" -#include "Simulator/FluidSimulator.h" -#include "Simulator/FireSimulator.h" -#include "Simulator/NoopFluidSimulator.h" -#include "Simulator/SandSimulator.h" -#include "Simulator/RedstoneSimulator.h" -#include "Simulator/VaporizeFluidSimulator.h" - -// Mobs: -#include "Mobs/IncludeAllMonsters.h" -#include "MobCensus.h" -#include "MobSpawner.h" - -#include "MersenneTwister.h" -#include "Generating/Trees.h" -#include "PluginManager.h" -#include "Blocks/BlockHandler.h" -#include "Vector3d.h" - -#include "Tracer.h" -#include "tolua++.h" - -// DEBUG: Test out the cLineBlockTracer class by tracing a few lines: -#include "LineBlockTracer.h" - -#ifndef _WIN32 - #include -#endif - - - - - -/// Up to this many m_SpreadQueue elements are handled each world tick -const int MAX_LIGHTING_SPREAD_PER_TICK = 10; - -const int TIME_SUNSET = 12000; -const int TIME_NIGHT_START = 13187; -const int TIME_NIGHT_END = 22812; -const int TIME_SUNRISE = 23999; -const int TIME_SPAWN_DIVISOR = 148; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorldLoadProgress: - -/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn() -class cWorldLoadProgress : - public cIsThread -{ -public: - cWorldLoadProgress(cWorld * a_World) : - cIsThread("cWorldLoadProgress"), - m_World(a_World) - { - Start(); - } - - void Stop(void) - { - m_ShouldTerminate = true; - Wait(); - } - -protected: - - cWorld * m_World; - - virtual void Execute(void) override - { - for (;;) - { - LOG("%d chunks to load, %d chunks to generate", - m_World->GetStorage().GetLoadQueueLength(), - m_World->GetGenerator().GetQueueLength() - ); - - // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish - for (int i = 0; i < 20; i++) - { - cSleep::MilliSleep(100); - if (m_ShouldTerminate) - { - return; - } - } - } // for (-ever) - } - -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorldLightingProgress: - -/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn() -class cWorldLightingProgress : - public cIsThread -{ -public: - cWorldLightingProgress(cLightingThread * a_Lighting) : - cIsThread("cWorldLightingProgress"), - m_Lighting(a_Lighting) - { - Start(); - } - - void Stop(void) - { - m_ShouldTerminate = true; - Wait(); - } - -protected: - - cLightingThread * m_Lighting; - - virtual void Execute(void) override - { - for (;;) - { - LOG("%d chunks remaining to light", m_Lighting->GetQueueLength() - ); - - // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish - for (int i = 0; i < 20; i++) - { - cSleep::MilliSleep(100); - if (m_ShouldTerminate) - { - return; - } - } - } // for (-ever) - } - -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorld::cLock: - -cWorld::cLock::cLock(cWorld & a_World) : - super(&(a_World.m_ChunkMap->GetCS())) -{ -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorld::cTickThread: - -cWorld::cTickThread::cTickThread(cWorld & a_World) : - super(Printf("WorldTickThread: %s", a_World.GetName().c_str())), - m_World(a_World) -{ -} - - - - - -void cWorld::cTickThread::Execute(void) -{ - cTimer Timer; - - long long msPerTick = 50; - long long LastTime = Timer.GetNowTime(); - - while (!m_ShouldTerminate) - { - long long NowTime = Timer.GetNowTime(); - float DeltaTime = (float)(NowTime - LastTime); - m_World.Tick(DeltaTime); - long long TickTime = Timer.GetNowTime() - NowTime; - - if (TickTime < msPerTick) - { - // Stretch tick time until it's at least msPerTick - cSleep::MilliSleep((unsigned int)(msPerTick - TickTime)); - } - - LastTime = NowTime; - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorld: - -cWorld::cWorld(const AString & a_WorldName) : - m_WorldName(a_WorldName), - m_IniFileName(m_WorldName + "/world.ini"), - m_StorageSchema("Default"), - m_WorldAgeSecs(0), - m_TimeOfDaySecs(0), - m_WorldAge(0), - m_TimeOfDay(0), - m_LastTimeUpdate(0), - m_RSList(0), - m_Weather(eWeather_Sunny), - m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :) - m_TickThread(*this), - m_SkyDarkness(0) -{ - LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); - - cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName); -} - - - - - -cWorld::~cWorld() -{ - delete m_SimulatorManager; - delete m_SandSimulator; - delete m_WaterSimulator; - delete m_LavaSimulator; - delete m_FireSimulator; - delete m_RedstoneSimulator; - - UnloadUnusedChunks(); - - m_Storage.WaitForFinish(); - - delete m_ChunkMap; -} - - - - - -void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) -{ - BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cWorld::SetWeather(eWeather a_NewWeather) -{ - // Do the plugins agree? Do they want a different weather? - cRoot::Get()->GetPluginManager()->CallHookWeatherChanging(*this, a_NewWeather); - - // Set new period for the selected weather: - switch (a_NewWeather) - { - case eWeather_Sunny: m_WeatherInterval = 14400 + (m_TickRand.randInt() % 4800); break; // 12 - 16 minutes - case eWeather_Rain: m_WeatherInterval = 9600 + (m_TickRand.randInt() % 7200); break; // 8 - 14 minutes - case eWeather_ThunderStorm: m_WeatherInterval = 2400 + (m_TickRand.randInt() % 4800); break; // 2 - 6 minutes - default: - { - LOGWARNING("Requested unknown weather %d, setting sunny for a minute instead.", a_NewWeather); - a_NewWeather = eWeather_Sunny; - m_WeatherInterval = 1200; - break; - } - } // switch (NewWeather) - m_Weather = a_NewWeather; - BroadcastWeather(m_Weather); - - // Let the plugins know about the change: - cPluginManager::Get()->CallHookWeatherChanged(*this); -} - - - - - -void cWorld::ChangeWeather(void) -{ - // In the next tick the weather will be changed - m_WeatherInterval = 0; -} - - - - - -void cWorld::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - return m_ChunkMap->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cWorld::InitializeSpawn(void) -{ - int ChunkX = 0, ChunkY = 0, ChunkZ = 0; - BlockToChunk((int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ); - - // For the debugging builds, don't make the server build too much world upon start: - #if defined(_DEBUG) || defined(ANDROID_NDK) - int ViewDist = 9; - #else - int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is - #endif // _DEBUG - - LOG("Preparing spawn area in world \"%s\"...", m_WorldName.c_str()); - for (int x = 0; x < ViewDist; x++) - { - for (int z = 0; z < ViewDist; z++) - { - m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, ZERO_CHUNK_Y, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader - } - } - - { - // Display progress during this process: - cWorldLoadProgress Progress(this); - - // Wait for the loader to finish loading - m_Storage.WaitForQueuesEmpty(); - - // Wait for the generator to finish generating - m_Generator.WaitForQueueEmpty(); - - Progress.Stop(); - } - - // Light all chunks that have been newly generated: - LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str()); - - for (int x = 0; x < ViewDist; x++) - { - int ChX = x + ChunkX-(ViewDist - 1) / 2; - for (int z = 0; z < ViewDist; z++) - { - int ChZ = z + ChunkZ-(ViewDist - 1) / 2; - if (!m_ChunkMap->IsChunkLighted(ChX, ChZ)) - { - m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread - } - } // for z - } // for x - - { - cWorldLightingProgress Progress(&m_Lighting); - m_Lighting.WaitForQueueEmpty(); - Progress.Stop(); - } - - // TODO: Better spawn detection - move spawn out of the water if it isn't set in the INI already - m_SpawnY = (double)GetHeight((int)m_SpawnX, (int)m_SpawnZ) + 1.6f; // +1.6f eye height - - - #ifdef TEST_LINEBLOCKTRACER - // DEBUG: Test out the cLineBlockTracer class by tracing a few lines: - class cTracerCallbacks : - public cBlockTracer::cCallbacks - { - virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override - { - LOGD("Block {%d, %d, %d}: %d:%d (%s)", - a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, - ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str() - ); - return false; - } - - virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ) override - { - LOGD("Block {%d, %d, %d}: no data available", - a_BlockX, a_BlockY, a_BlockZ - ); - return false; - } - - virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override - { - LOGD("Out of world at {%f, %f, %f}", a_BlockX, a_BlockY, a_BlockZ); - return false; - } - - virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override - { - LOGD("Into world at {%f, %f, %f}", a_BlockX, a_BlockY, a_BlockZ); - return false; - } - - virtual void OnNoMoreHits(void) override - { - LOGD("No more hits"); - } - } Callbacks; - LOGD("Spawn is at {%f, %f, %f}", m_SpawnX, m_SpawnY, m_SpawnZ); - LOGD("Tracing a line along +X:"); - cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 10, m_SpawnY, m_SpawnZ, m_SpawnX + 10, m_SpawnY, m_SpawnZ); - LOGD("Tracing a line along -Z:"); - cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX, m_SpawnY, m_SpawnZ + 10, m_SpawnX, m_SpawnY, m_SpawnZ - 10); - LOGD("Tracing a line along -Y, out of world:"); - cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX, 260, m_SpawnZ, m_SpawnX, -5, m_SpawnZ); - LOGD("Tracing a line along XY:"); - cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 10, m_SpawnY - 10, m_SpawnZ, m_SpawnX + 10, m_SpawnY + 10, m_SpawnZ); - LOGD("Tracing a line in generic direction:"); - cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 15, m_SpawnY - 5, m_SpawnZ + 7.5, m_SpawnX + 13, m_SpawnY - 10, m_SpawnZ + 8.5); - LOGD("Tracing tests done"); - #endif // TEST_LINEBLOCKTRACER -} - - - - - -void cWorld::Start(void) -{ - // TODO: Find a proper spawn location, based on the biomes (not in ocean) - m_SpawnX = (double)((m_TickRand.randInt() % 1000) - 500); - m_SpawnY = cChunkDef::Height; - m_SpawnZ = (double)((m_TickRand.randInt() % 1000) - 500); - m_GameMode = eGameMode_Creative; - - cIniFile IniFile; - if (!IniFile.ReadFile(m_IniFileName)) - { - LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str()); - } - AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld"); - m_Dimension = StringToDimension(Dimension); - switch (m_Dimension) - { - case dimNether: - case dimOverworld: - case dimEnd: - { - break; - } - default: - { - LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", Dimension.c_str()); - m_Dimension = dimOverworld; - break; - } - } // switch (m_Dimension) - m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX); - m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY); - m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ); - m_StorageSchema = IniFile.GetValueSet ("Storage", "Schema", m_StorageSchema); - m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3); - m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3); - m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false); - m_IsCarrotsBonemealable = IniFile.GetValueSetB("Plants", "IsCarrotsBonemealable", true); - m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true); - m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true); - m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true); - m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false); - m_IsPotatoesBonemealable = IniFile.GetValueSetB("Plants", "IsPotatoesBonemealable", true); - m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true); - m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false); - m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true); - m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false); - m_bEnabledPVP = IniFile.GetValueSetB("PVP", "Enabled", true); - m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false); - - m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode); - - // Load allowed mobs: - const char * DefaultMonsters = ""; - switch (m_Dimension) - { - case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; - case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break; - case dimEnd: DefaultMonsters = "enderman"; break; - default: - { - ASSERT(!"Unhandled world dimension"); - DefaultMonsters = "wither"; - break; - } - } - m_bAnimals = IniFile.GetValueSetB("Monsters", "AnimalsOn", true); - AString AllMonsters = IniFile.GetValueSet("Monsters", "Types", DefaultMonsters); - AStringVector SplitList = StringSplitAndTrim(AllMonsters, ","); - for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr) - { - cMonster::eType ToAdd = cMonster::StringToMobType(*itr); - if (ToAdd != cMonster::mtInvalidType) - { - m_AllowedMobs.insert(ToAdd); - LOGD("Allowed mob: %s", itr->c_str()); - } - else - { - LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str()); - } - } - - m_ChunkMap = new cChunkMap(this); - - m_LastSave = 0; - m_LastUnload = 0; - - // preallocate some memory for ticking blocks so we don't need to allocate that often - m_BlockTickQueue.reserve(1000); - m_BlockTickQueueCopy.reserve(1000); - - // Simulators: - m_SimulatorManager = new cSimulatorManager(*this); - m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); - m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); - m_SandSimulator = new cSandSimulator(*this, IniFile); - m_FireSimulator = new cFireSimulator(*this, IniFile); - m_RedstoneSimulator = new cRedstoneSimulator(*this); - - // Water and Lava simulators get registered in InitializeFluidSimulator() - m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1); - m_SimulatorManager->RegisterSimulator(m_FireSimulator, 1); - m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1); - - m_Lighting.Start(this); - m_Storage.Start(this, m_StorageSchema); - m_Generator.Start(this, IniFile); - m_ChunkSender.Start(this); - m_TickThread.Start(); - - // Init of the spawn monster time (as they are supposed to have different spawn rate) - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfHostile, 0)); - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfPassive, 0)); - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfAmbient, 0)); - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfWater, 0)); - - - // Save any changes that the defaults may have done to the ini file: - if (!IniFile.WriteFile(m_IniFileName)) - { - LOGWARNING("Could not write world config to %s", m_IniFileName.c_str()); - } - -} - - - - - -void cWorld::Stop(void) -{ - // Delete the clients that have been in this world: - { - cCSLock Lock(m_CSClients); - for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) - { - (*itr)->Destroy(); - delete *itr; - } // for itr - m_Clients[] - m_Clients.clear(); - } - - m_TickThread.Stop(); - m_Lighting.Stop(); - m_Generator.Stop(); - m_ChunkSender.Stop(); - m_Storage.Stop(); -} - - - - - -void cWorld::Tick(float a_Dt) -{ - // Call the plugins - cPluginManager::Get()->CallHookWorldTick(*this, a_Dt); - - // We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it - m_WorldAgeSecs += (double)a_Dt / 1000.0; - m_TimeOfDaySecs += (double)a_Dt / 1000.0; - - // Wrap time of day each 20 minutes (1200 seconds) - if (m_TimeOfDaySecs > 1200.0) - { - m_TimeOfDaySecs -= 1200.0; - } - - m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0); - m_TimeOfDay = (Int64)(m_TimeOfDaySecs * 20.0); - - // Updates the sky darkness based on current time of day - UpdateSkyDarkness(); - - // Broadcast time update every 40 ticks (2 seconds) - if (m_LastTimeUpdate < m_WorldAge - 40) - { - BroadcastTimeUpdate(); - m_LastTimeUpdate = m_WorldAge; - } - - m_ChunkMap->Tick(a_Dt); - - TickClients(a_Dt); - TickQueuedBlocks(); - TickQueuedTasks(); - - GetSimulatorManager()->Simulate(a_Dt); - - TickWeather(a_Dt); - - // Asynchronously set blocks: - sSetBlockList FastSetBlockQueueCopy; - { - cCSLock Lock(m_CSFastSetBlock); - std::swap(FastSetBlockQueueCopy, m_FastSetBlockQueue); - } - m_ChunkMap->FastSetBlocks(FastSetBlockQueueCopy); - if (!FastSetBlockQueueCopy.empty()) - { - // Some blocks failed, store them for next tick: - cCSLock Lock(m_CSFastSetBlock); - m_FastSetBlockQueue.splice(m_FastSetBlockQueue.end(), FastSetBlockQueueCopy); - } - - if (m_WorldAge - m_LastSave > 60 * 5 * 20) // Save each 5 minutes - { - SaveAllChunks(); - } - - if (m_WorldAge - m_LastUnload > 10 * 20) // Unload every 10 seconds - { - UnloadUnusedChunks(); - } - - TickMobs(a_Dt); - - std::vector m_RSList_copy(m_RSList); - - m_RSList.clear(); - - std::vector::const_iterator cii; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter) - for (cii = m_RSList_copy.begin(); cii != m_RSList_copy.end();) - { - int tempX = *cii; cii++; - int tempY = *cii; cii++; - int tempZ = *cii; cii++; - int state = *cii; cii++; - - if ((state == 11111) && ((int)GetBlock(tempX, tempY, tempZ) == E_BLOCK_REDSTONE_TORCH_OFF)) - { - FastSetBlock(tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_ON, (int)GetBlockMeta(tempX, tempY, tempZ)); - } - else if ((state == 00000) && ((int)GetBlock(tempX, tempY, tempZ) == E_BLOCK_REDSTONE_TORCH_ON)) - { - FastSetBlock(tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_OFF, (int)GetBlockMeta(tempX, tempY, tempZ)); - } - } - m_RSList_copy.erase(m_RSList_copy.begin(),m_RSList_copy.end()); -} - - - - - -void cWorld::TickWeather(float a_Dt) -{ - // There are no weather changes anywhere but in the Overworld: - if (GetDimension() != dimOverworld) - { - return; - } - - if (m_WeatherInterval > 0) - { - // Not yet, wait for the weather period to end - m_WeatherInterval--; - } - else - { - // Change weather: - - // Pick a new weather. Only reasonable transitions allowed: - eWeather NewWeather = m_Weather; - switch (m_Weather) - { - case eWeather_Sunny: NewWeather = eWeather_Rain; break; - case eWeather_ThunderStorm: NewWeather = eWeather_Rain; break; - case eWeather_Rain: - { - // 1/8 chance of turning into a thunderstorm - NewWeather = ((m_TickRand.randInt() % 256) < 32) ? eWeather_ThunderStorm : eWeather_Sunny; - break; - } - - default: - { - LOGWARNING("Unknown current weather: %d. Setting sunny.", m_Weather); - ASSERT(!"Unknown weather"); - NewWeather = eWeather_Sunny; - } - } - - SetWeather(NewWeather); - } // else (m_WeatherInterval > 0) - - if (m_Weather == eWeather_ThunderStorm) - { - // 0.5% chance per tick of thunderbolt - if (m_TickRand.randInt() % 199 == 0) - { - CastThunderbolt(0, 0, 0); // TODO: find random possitions near players to cast thunderbolts. - } - } -} - - - - - -void cWorld::TickMobs(float a_Dt) -{ - // _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs - cWorld::cLock Lock(*this); - - // before every Mob action, we have to count them depending on the distance to players, on their family ... - cMobCensus MobCensus; - m_ChunkMap->CollectMobCensus(MobCensus); - if (m_bAnimals) - { - // Spawning is enabled, spawn now: - static const cMonster::eFamily AllFamilies[] = - { - cMonster::mfHostile, - cMonster::mfPassive, - cMonster::mfAmbient, - cMonster::mfWater, - } ; - for (int i = 0; i < ARRAYCOUNT(AllFamilies); i++) - { - cMonster::eFamily Family = AllFamilies[i]; - int SpawnDelay = cMonster::GetSpawnDelay(Family); - if ( - (m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks before the next round - MobCensus.IsCapped(Family) - ) - { - continue; - } - m_LastSpawnMonster[Family] = m_WorldAge; - cMobSpawner Spawner(Family, m_AllowedMobs); - if (Spawner.CanSpawnAnything()) - { - m_ChunkMap->SpawnMobs(Spawner); - // do the spawn - for (cMobSpawner::tSpawnedContainer::const_iterator itr2 = Spawner.getSpawned().begin(); itr2 != Spawner.getSpawned().end(); itr2++) - { - SpawnMobFinalize(*itr2); - } - } - } // for i - AllFamilies[] - } // if (Spawning enabled) - - // move close mobs - cMobProximityCounter::sIterablePair allCloseEnoughToMoveMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(-1, 64 * 16);// MG TODO : deal with this magic number (the 16 is the size of a block) - for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allCloseEnoughToMoveMobs.m_Begin; itr != allCloseEnoughToMoveMobs.m_End; itr++) - { - itr->second.m_Monster.Tick(a_Dt, itr->second.m_Chunk); - } - - // remove too far mobs - cMobProximityCounter::sIterablePair allTooFarMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(128 * 16, -1);// MG TODO : deal with this magic number (the 16 is the size of a block) - for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allTooFarMobs.m_Begin; itr != allTooFarMobs.m_End; itr++) - { - itr->second.m_Monster.Destroy(true); - } -} - - - - - -void cWorld::TickQueuedTasks(void) -{ - // Make a copy of the tasks to avoid deadlocks on accessing m_Tasks - cTasks Tasks; - { - cCSLock Lock(m_CSTasks); - std::swap(Tasks, m_Tasks); - } - - // Execute and delete each task: - for (cTasks::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr) - { - (*itr)->Run(*this); - delete *itr; - } // for itr - m_Tasks[] -} - - - - - -void cWorld::TickClients(float a_Dt) -{ - cClientHandleList RemoveClients; - { - cCSLock Lock(m_CSClients); - - // Remove clients scheduled for removal: - for (cClientHandleList::iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) - { - m_Clients.remove(*itr); - } // for itr - m_ClientsToRemove[] - m_ClientsToRemove.clear(); - - // Add clients scheduled for adding: - for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr) - { - if (std::find(m_Clients.begin(), m_Clients.end(), *itr) != m_Clients.end()) - { - ASSERT(!"Adding a client that is already in the clientlist"); - continue; - } - m_Clients.push_back(*itr); - } // for itr - m_ClientsToRemove[] - m_ClientsToAdd.clear(); - - // Tick the clients, take out those that have been destroyed into RemoveClients - for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();) - { - if ((*itr)->IsDestroyed()) - { - // Remove the client later, when CS is not held, to avoid deadlock - RemoveClients.push_back(*itr); - itr = m_Clients.erase(itr); - continue; - } - (*itr)->Tick(a_Dt); - ++itr; - } // for itr - m_Clients[] - } - - // Delete the clients that have been destroyed - for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr) - { - delete *itr; - } // for itr - RemoveClients[] -} - - - - - -void cWorld::UpdateSkyDarkness(void) -{ - int TempTime = (int)m_TimeOfDay; - if (TempTime <= TIME_SUNSET) - { - m_SkyDarkness = 0; - } - else if (TempTime <= TIME_NIGHT_START) - { - m_SkyDarkness = (TIME_NIGHT_START - TempTime) / TIME_SPAWN_DIVISOR; - } - else if (TempTime <= TIME_NIGHT_END) - { - m_SkyDarkness = 8; - } - else - { - m_SkyDarkness = (TIME_SUNRISE - TempTime) / TIME_SPAWN_DIVISOR; - } -} - - - - - -void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -/// Wakes up the simulators for the specified area of blocks -void cWorld::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ) -{ - return m_ChunkMap->WakeUpSimulatorsInArea(a_MinBlockX, a_MaxBlockX, a_MinBlockY, a_MaxBlockY, a_MinBlockZ, a_MaxBlockZ); -} - - - - - -bool cWorld::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) -{ - return m_ChunkMap->ForEachChestInChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback) -{ - return m_ChunkMap->ForEachDispenserInChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback) -{ - return m_ChunkMap->ForEachDropperInChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback) -{ - return m_ChunkMap->ForEachDropSpenserInChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) -{ - return m_ChunkMap->ForEachFurnaceInChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData) -{ - if (cPluginManager::Get()->CallHookExploding(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData) || (a_ExplosionSize <= 0)) - { - return; - } - - // TODO: Add damage to entities, add support for pickups, and implement block hardiness - Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ); - cVector3iArray BlocksAffected; - m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected); - BroadcastSoundEffect("random.explode", (int)floor(a_BlockX * 8), (int)floor(a_BlockY * 8), (int)floor(a_BlockZ * 8), 1.0f, 0.6f); - { - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos; - if (distance_explosion.SqrLength() < 4096.0) - { - double real_distance = std::max(0.004, sqrt(distance_explosion.SqrLength())); - double power = a_ExplosionSize / real_distance; - if (power <= 1) - { - power = 0; - } - distance_explosion.Normalize(); - distance_explosion *= power; - ch->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, (float)a_ExplosionSize, BlocksAffected, distance_explosion); - } - } - } - cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData); -} - - - - - -bool cWorld::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) -{ - return m_ChunkMap->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cWorld::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) -{ - return m_ChunkMap->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cWorld::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback) -{ - return m_ChunkMap->DoWithDropperAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cWorld::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback) -{ - return m_ChunkMap->DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cWorld::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) -{ - return m_ChunkMap->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) -{ - return m_ChunkMap->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); -} - - - - - -bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback) -{ - return m_ChunkMap->DoWithChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -void cWorld::GrowTree(int a_X, int a_Y, int a_Z) -{ - if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING) - { - // There is a sapling here, grow a tree according to its type: - GrowTreeFromSapling(a_X, a_Y, a_Z, GetBlockMeta(a_X, a_Y, a_Z)); - } - else - { - // There is nothing here, grow a tree based on the current biome here: - GrowTreeByBiome(a_X, a_Y, a_Z); - } -} - - - - - -void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_SaplingMeta) -{ - cNoise Noise(m_Generator.GetSeed()); - sSetBlockVector Logs, Other; - switch (a_SaplingMeta & 0x07) - { - case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - } - Other.insert(Other.begin(), Logs.begin(), Logs.end()); - Logs.clear(); - GrowTreeImage(Other); -} - - - - - -void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z) -{ - cNoise Noise(m_Generator.GetSeed()); - sSetBlockVector Logs, Other; - GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), (EMCSBiome)GetBiomeAt(a_X, a_Z), Logs, Other); - Other.insert(Other.begin(), Logs.begin(), Logs.end()); - Logs.clear(); - GrowTreeImage(Other); -} - - - - - -void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) -{ - // Check that the tree has place to grow - - // Make a copy of the log blocks: - sSetBlockVector b2; - for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) - { - if (itr->BlockType == E_BLOCK_LOG) - { - b2.push_back(*itr); - } - } // for itr - a_Blocks[] - - // Query blocktypes and metas at those log blocks: - if (!GetBlocks(b2, false)) - { - return; - } - - // Check that at each log's coord there's an block allowed to be overwritten: - for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr) - { - switch (itr->BlockType) - { - CASE_TREE_ALLOWED_BLOCKS: - { - break; - } - default: - { - return; - } - } - } // for itr - b2[] - - // All ok, replace blocks with the tree image: - m_ChunkMap->ReplaceTreeBlocks(a_Blocks); -} - - - - - -bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal) -{ - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - switch (BlockType) - { - case E_BLOCK_CARROTS: - { - if (a_IsByBonemeal && !m_IsCarrotsBonemealable) - { - return false; - } - if (BlockMeta < 7) - { - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); - } - return true; - } - - case E_BLOCK_CROPS: - { - if (a_IsByBonemeal && !m_IsCropsBonemealable) - { - return false; - } - if (BlockMeta < 7) - { - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); - } - return true; - } - - case E_BLOCK_MELON_STEM: - { - if (BlockMeta < 7) - { - if (a_IsByBonemeal && !m_IsMelonStemBonemealable) - { - return false; - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); - } - else - { - if (a_IsByBonemeal && !m_IsMelonBonemealable) - { - return false; - } - GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); - } - return true; - } - - case E_BLOCK_POTATOES: - { - if (a_IsByBonemeal && !m_IsPotatoesBonemealable) - { - return false; - } - if (BlockMeta < 7) - { - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); - } - return true; - } - - case E_BLOCK_PUMPKIN_STEM: - { - if (BlockMeta < 7) - { - if (a_IsByBonemeal && !m_IsPumpkinStemBonemealable) - { - return false; - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); - } - else - { - if (a_IsByBonemeal && !m_IsPumpkinBonemealable) - { - return false; - } - GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); - } - return true; - } - - case E_BLOCK_SAPLING: - { - if (a_IsByBonemeal && !m_IsSaplingBonemealable) - { - return false; - } - GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); - return true; - } - - case E_BLOCK_GRASS: - { - if (a_IsByBonemeal && !m_IsGrassBonemealable) - { - return false; - } - MTRand r1; - for (int i = 0; i < 60; i++) - { - int OfsX = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; - int OfsY = r1.randInt(3) + r1.randInt(3) - 3; - int OfsZ = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; - BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ); - if (Ground != E_BLOCK_GRASS) - { - continue; - } - BLOCKTYPE Above = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ); - if (Above != E_BLOCK_AIR) - { - continue; - } - BLOCKTYPE SpawnType; - NIBBLETYPE SpawnMeta = 0; - switch (r1.randInt(10)) - { - case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break; - case 1: SpawnType = E_BLOCK_RED_ROSE; break; - default: - { - SpawnType = E_BLOCK_TALL_GRASS; - SpawnMeta = E_META_TALL_GRASS_GRASS; - break; - } - } // switch (random spawn block type) - FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, SpawnType, SpawnMeta); - } // for i - 50 times - return true; - } - - case E_BLOCK_SUGARCANE: - { - if (a_IsByBonemeal && !m_IsSugarcaneBonemealable) - { - return false; - } - m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, m_MaxSugarcaneHeight); - return true; - } - - case E_BLOCK_CACTUS: - { - if (a_IsByBonemeal && !m_IsCactusBonemealable) - { - return false; - } - m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, m_MaxCactusHeight); - return true; - } - } // switch (BlockType) - return false; -} - - - - - -void cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) -{ - m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); -} - - - - - -void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) -{ - MTRand Rand; - m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand); -} - - - - - -void cWorld::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) -{ - m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); -} - - - - - -int cWorld::GetBiomeAt (int a_BlockX, int a_BlockZ) -{ - return m_ChunkMap->GetBiomeAt(a_BlockX, a_BlockZ); -} - - - - - -void cWorld::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - if (a_BlockType == E_BLOCK_AIR) - { - BlockHandler(GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnDestroyed(this, a_BlockX, a_BlockY, a_BlockZ); - } - m_ChunkMap->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); - - BlockHandler(a_BlockType)->OnPlaced(this, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); -} - - - - - -void cWorld::FastSetBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CSFastSetBlock); - m_FastSetBlockQueue.push_back(sSetBlock(a_X, a_Y, a_Z, a_BlockType, a_BlockMeta)); -} - - - - - -void cWorld::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_TickDelay) -{ - m_ChunkMap->QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, GetWorldAge() + a_TickDelay); -} - - - - - -BLOCKTYPE cWorld::GetBlock(int a_X, int a_Y, int a_Z) -{ - // First check if it isn't queued in the m_FastSetBlockQueue: - { - int X = a_X, Y = a_Y, Z = a_Z; - int ChunkX, ChunkY, ChunkZ; - AbsoluteToRelative(X, Y, Z, ChunkX, ChunkY, ChunkZ); - - cCSLock Lock(m_CSFastSetBlock); - for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) - { - if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) - { - return itr->BlockType; - } - } // for itr - m_FastSetBlockQueue[] - } - - return m_ChunkMap->GetBlock(a_X, a_Y, a_Z); -} - - - - - -NIBBLETYPE cWorld::GetBlockMeta(int a_X, int a_Y, int a_Z) -{ - // First check if it isn't queued in the m_FastSetBlockQueue: - { - cCSLock Lock(m_CSFastSetBlock); - for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) - { - if ((itr->x == a_X) && (itr->y == a_Y) && (itr->y == a_Y)) - { - return itr->BlockMeta; - } - } // for itr - m_FastSetBlockQueue[] - } - - return m_ChunkMap->GetBlockMeta(a_X, a_Y, a_Z); -} - - - - - -void cWorld::SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData) -{ - m_ChunkMap->SetBlockMeta(a_X, a_Y, a_Z, a_MetaData); -} - - - - - -NIBBLETYPE cWorld::GetBlockSkyLight(int a_X, int a_Y, int a_Z) -{ - return m_ChunkMap->GetBlockSkyLight(a_X, a_Y, a_Z); -} - - - - - -NIBBLETYPE cWorld::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - return m_ChunkMap->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -bool cWorld::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - return m_ChunkMap->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, (BLOCKTYPE &)a_BlockType, (NIBBLETYPE &)a_BlockMeta); -} - - - - - -bool cWorld::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) -{ - return m_ChunkMap->GetBlockInfo(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); -} - - - - - -bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) -{ - return m_ChunkMap->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); -} - - - - - -void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed, bool IsPlayerCreated) -{ - MTRand r1; - a_FlyAwaySpeed /= 1000; // Pre-divide, so that we don't have to divide each time inside the loop - for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) - { - float SpeedX = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); - float SpeedY = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); - float SpeedZ = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); - - cPickup * Pickup = new cPickup( - a_BlockX, a_BlockY, a_BlockZ, - *itr, IsPlayerCreated, SpeedX, SpeedY, SpeedZ - ); - Pickup->Initialize(this); - } -} - - - - - -void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated) -{ - for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) - { - cPickup * Pickup = new cPickup( - a_BlockX, a_BlockY, a_BlockZ, - *itr, IsPlayerCreated, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ - ); - Pickup->Initialize(this); - } -} - - - - - -void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff) -{ - cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTimeInSec); - TNT->Initialize(this); - // TODO: Add a bit of speed in horiz and vert axes, based on the a_InitialVelocityCoeff -} - - - - - -void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) -{ - m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType); -} - - - - - -bool cWorld::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) -{ - return m_ChunkMap->GetBlocks(a_Blocks, a_ContinueOnFailure); -} - - - - - -bool cWorld::DigBlock(int a_X, int a_Y, int a_Z) -{ - cBlockHandler *Handler = cBlockHandler::GetBlockHandler(GetBlock(a_X, a_Y, a_Z)); - Handler->OnDestroyed(this, a_X, a_Y, a_Z); - return m_ChunkMap->DigBlock(a_X, a_Y, a_Z); -} - - - - - -void cWorld::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) -{ - m_ChunkMap->SendBlockTo(a_X, a_Y, a_Z, a_Player); -} - - - - - -int cWorld::GetHeight(int a_X, int a_Z) -{ - return m_ChunkMap->GetHeight(a_X, a_Z); -} - - - - - -bool cWorld::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height) -{ - return m_ChunkMap->TryGetHeight(a_BlockX, a_BlockZ, a_Height); -} - - - - - -void cWorld::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - return m_ChunkMap->BroadcastAttachEntity(a_Entity, a_Vehicle); -} - - - - - -void cWorld::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); -} - - - - - -void cWorld::BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastBlockBreakAnimation(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage, a_Exclude); -} - - - - - -void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); -} - - - - - -void cWorld::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendChat(a_Message); - } -} - - - - - -void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude); -} - - - - - -void cWorld::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); -} - - - - - -void cWorld::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastDestroyEntity(a_Entity, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityHeadLook(a_Entity, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityLook(a_Entity, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityMetadata(a_Entity, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityVelocity(a_Entity, a_Exclude); -} - - - - -void cWorld::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); -} - - - - - -void cWorld::BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendPlayerListItem(a_Player, a_IsOnline); - } -} - - - - - -void cWorld::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); -} - - - - - -void cWorld::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data, a_Exclude); -} - - - - - -void cWorld::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastSpawnEntity(a_Entity, a_Exclude); -} - - - - - -void cWorld::BroadcastTeleportEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendTeleportEntity(a_Entity); - } -} - - - - - -void cWorld::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); -} - - - - - -void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendTimeUpdate(m_WorldAge, m_TimeOfDay); - } -} - - - - - -void cWorld::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - m_ChunkMap->BroadcastUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cWorld::BroadcastWeather(eWeather a_Weather, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendWeather(a_Weather); - } -} - - - - - -void cWorld::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) -{ - m_ChunkMap->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); -} - - - - - -void cWorld::MarkChunkDirty (int a_ChunkX, int a_ChunkZ) -{ - m_ChunkMap->MarkChunkDirty (a_ChunkX, a_ChunkZ); -} - - - - - -void cWorld::MarkChunkSaving(int a_ChunkX, int a_ChunkZ) -{ - m_ChunkMap->MarkChunkSaving(a_ChunkX, a_ChunkZ); -} - - - - - -void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) -{ - m_ChunkMap->MarkChunkSaved (a_ChunkX, a_ChunkZ); -} - - - - - -void cWorld::SetChunkData( - int a_ChunkX, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap * a_BiomeMap, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities, - bool a_MarkDirty -) -{ - // Validate biomes, if needed: - cChunkDef::BiomeMap BiomeMap; - const cChunkDef::BiomeMap * Biomes = a_BiomeMap; - if (a_BiomeMap == NULL) - { - // The biomes are not assigned, get them from the generator: - Biomes = &BiomeMap; - m_Generator.GenerateBiomes(a_ChunkX, a_ChunkZ, BiomeMap); - } - - m_ChunkMap->SetChunkData( - a_ChunkX, a_ChunkZ, - a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, - a_HeightMap, *Biomes, - a_BlockEntities, - a_MarkDirty - ); - - // Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347): - for (cEntityList::iterator itr = a_Entities.begin(), end = a_Entities.end(); itr != end; ++itr) - { - (*itr)->Initialize(this); - } - - // If a client is requesting this chunk, send it to them: - if (m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkZ)) - { - m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkZ); - } - - // Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk): - m_Lighting.ChunkReady(a_ChunkX, a_ChunkZ); -} - - - - - -void cWorld::ChunkLighted( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight -) -{ - m_ChunkMap->ChunkLighted(a_ChunkX, a_ChunkZ, a_BlockLight, a_SkyLight); -} - - - - - -bool cWorld::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback) -{ - return m_ChunkMap->GetChunkData(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) -{ - return m_ChunkMap->GetChunkBlockTypes(a_ChunkX, a_ChunkZ, a_BlockTypes); -} - - - - - -bool cWorld::IsChunkValid(int a_ChunkX, int a_ChunkZ) const -{ - return m_ChunkMap->IsChunkValid(a_ChunkX, a_ChunkZ); -} - - - - - -bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const -{ - return m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkZ); -} - - - - - -void cWorld::UnloadUnusedChunks(void) -{ - m_LastUnload = m_WorldAge; - m_ChunkMap->UnloadUnusedChunks(); -} - - - - - -void cWorld::CollectPickupsByPlayer(cPlayer * a_Player) -{ - m_ChunkMap->CollectPickupsByPlayer(a_Player); -} - - - - - -void cWorld::AddPlayer(cPlayer * a_Player) -{ - { - cCSLock Lock(m_CSPlayers); - - ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW? - - m_Players.remove(a_Player); // Make sure the player is registered only once - m_Players.push_back(a_Player); - } - - // Add the player's client to the list of clients to be ticked: - if (a_Player->GetClientHandle() != NULL) - { - cCSLock Lock(m_CSClients); - m_ClientsToAdd.push_back(a_Player->GetClientHandle()); - } - - // The player has already been added to the chunkmap as the entity, do NOT add again! -} - - - - - -void cWorld::RemovePlayer(cPlayer * a_Player) -{ - m_ChunkMap->RemoveEntity(a_Player); - { - cCSLock Lock(m_CSPlayers); - m_Players.remove(a_Player); - } - - // Remove the player's client from the list of clients to be ticked: - if (a_Player->GetClientHandle() != NULL) - { - cCSLock Lock(m_CSClients); - m_ClientsToRemove.push_back(a_Player->GetClientHandle()); - } -} - - - - - -bool cWorld::ForEachPlayer(cPlayerListCallback & a_Callback) -{ - // Calls the callback for each player in the list - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(), itr2 = itr; itr != m_Players.end(); itr = itr2) - { - ++itr2; - if (a_Callback.Item(*itr)) - { - return false; - } - } // for itr - m_Players[] - return true; -} - - - - - -bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) -{ - // Calls the callback for each player in the list - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if (NoCaseCompare((*itr)->GetName(), a_PlayerName) == 0) - { - a_Callback.Item(*itr); - return true; - } - } // for itr - m_Players[] - return false; -} - - - - - -bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback) -{ - cPlayer * BestMatch = NULL; - unsigned int BestRating = 0; - unsigned int NameLength = a_PlayerNameHint.length(); - - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - unsigned int Rating = RateCompareString (a_PlayerNameHint, (*itr)->GetName()); - if (Rating >= BestRating) - { - BestMatch = *itr; - BestRating = Rating; - } - if (Rating == NameLength) // Perfect match - { - break; - } - } // for itr - m_Players[] - - if (BestMatch != NULL) - { - LOG("Compared %s and %s with rating %i", a_PlayerNameHint.c_str(), BestMatch->GetName().c_str(), BestRating); - return a_Callback.Item (BestMatch); - } - return false; -} - - - - - -// TODO: This interface is dangerous! -cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit) -{ - cTracer LineOfSight(this); - - float ClosestDistance = a_SightLimit; - cPlayer* ClosestPlayer = NULL; - - cCSLock Lock(m_CSPlayers); - for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - Vector3f Pos = (*itr)->GetPosition(); - float Distance = (Pos - a_Pos).Length(); - - if (Distance < ClosestDistance) - { - if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length())) - { - ClosestDistance = Distance; - ClosestPlayer = *itr; - } - } - } - return ClosestPlayer; -} - - - - - -void cWorld::SendPlayerList(cPlayer * a_DestPlayer) -{ - // Sends the playerlist to a_DestPlayer - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch != NULL) && !ch->IsDestroyed()) - { - a_DestPlayer->GetClientHandle()->SendPlayerListItem(*(*itr), true); - } - } -} - - - - - -bool cWorld::ForEachEntity(cEntityCallback & a_Callback) -{ - return m_ChunkMap->ForEachEntity(a_Callback); -} - - - - - -bool cWorld::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) -{ - return m_ChunkMap->ForEachEntityInChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback) -{ - return m_ChunkMap->DoWithEntityByID(a_UniqueID, a_Callback); -} - - - - - -void cWorld::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback) -{ - m_ChunkMap->CompareChunkClients(a_ChunkX1, a_ChunkZ1, a_ChunkX2, a_ChunkZ2, a_Callback); -} - - - - - -bool cWorld::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) -{ - return m_ChunkMap->AddChunkClient(a_ChunkX, a_ChunkZ, a_Client); -} - - - - - -void cWorld::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) -{ - m_ChunkMap->RemoveChunkClient(a_ChunkX, a_ChunkZ, a_Client); -} - - - - - -void cWorld::RemoveClientFromChunks(cClientHandle * a_Client) -{ - m_ChunkMap->RemoveClientFromChunks(a_Client); -} - - - - - -void cWorld::SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) -{ - m_ChunkSender.QueueSendChunkTo(a_ChunkX, a_ChunkZ, a_Client); -} - - - - - -void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client) -{ - m_ChunkSender.RemoveClient(a_Client); -} - - - - - -void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks) -{ - m_ChunkMap->LoadChunks(a_Chunks); -} - - - - - -void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -bool cWorld::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) -{ - AString Line1(a_Line1); - AString Line2(a_Line2); - AString Line3(a_Line3); - AString Line4(a_Line4); - if (cRoot::Get()->GetPluginManager()->CallHookUpdatingSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player)) - { - return false; - } - if (m_ChunkMap->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4)) - { - cRoot::Get()->GetPluginManager()->CallHookUpdatedSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player); - return true; - } - return false; -} - - - - - -bool cWorld::UpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) -{ - return SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player); -} - - - - - -void cWorld::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) -{ - m_ChunkMap->ChunksStay(a_Chunks, a_Stay); -} - - - - - -void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) -{ - m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ); - - // Trick: use Y=1 to force the chunk generation even though the chunk data is already present - m_Generator.QueueGenerateChunk(a_ChunkX, 1, a_ChunkZ); -} - - - - - -void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ) -{ - m_Generator.QueueGenerateChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); -} - - - - - -void cWorld::QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) -{ - m_Lighting.QueueChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::IsChunkLighted(int a_ChunkX, int a_ChunkZ) -{ - return m_ChunkMap->IsChunkLighted(a_ChunkX, a_ChunkZ); -} - - - - - -bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) -{ - return m_ChunkMap->ForEachChunkInRect(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ, a_Callback); -} - - - - - -void cWorld::SaveAllChunks(void) -{ - LOGINFO("Saving all chunks..."); - m_LastSave = m_WorldAge; - m_ChunkMap->SaveAllChunks(); - m_Storage.QueueSavedMessage(); -} - - - - - -void cWorld::QueueSaveAllChunks(void) -{ - QueueTask(new cWorld::cTaskSaveAllChunks); -} - - - - - -void cWorld::QueueTask(cTask * a_Task) -{ - cCSLock Lock(m_CSTasks); - m_Tasks.push_back(a_Task); -} - - - - - -void cWorld::AddEntity(cEntity * a_Entity) -{ - m_ChunkMap->AddEntity(a_Entity); -} - - - - - -bool cWorld::HasEntity(int a_UniqueID) -{ - return m_ChunkMap->HasEntity(a_UniqueID); -} - - - - - -void cWorld::RemoveEntity(cEntity * a_Entity) -{ - m_ChunkMap->RemoveEntity(a_Entity); -} - - - - - -/* -unsigned int cWorld::GetNumPlayers(void) -{ - cCSLock Lock(m_CSPlayers); - return m_Players.size(); -} -*/ - - - - - -int cWorld::GetNumChunks(void) const -{ - return m_ChunkMap->GetNumChunks(); -} - - - - - -void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue) -{ - m_ChunkMap->GetChunkStats(a_NumValid, a_NumDirty); - a_NumInLightingQueue = (int) m_Lighting.GetQueueLength(); -} - - - - - -void cWorld::TickQueuedBlocks(void) -{ - if (m_BlockTickQueue.empty()) - { - return; - } - m_BlockTickQueueCopy.clear(); - m_BlockTickQueue.swap(m_BlockTickQueueCopy); - - for (std::vector::iterator itr = m_BlockTickQueueCopy.begin(); itr != m_BlockTickQueueCopy.end(); itr++) - { - BlockTickQueueItem *Block = (*itr); - Block->TicksToWait -= 1; - if (Block->TicksToWait <= 0) - { - // TODO: Handle the case when the chunk is already unloaded - BlockHandler(GetBlock(Block->X, Block->Y, Block->Z))->OnUpdate(this, Block->X, Block->Y, Block->Z); - delete Block; // We don't have to remove it from the vector, this will happen automatically on the next tick - } - else - { - m_BlockTickQueue.push_back(Block); // Keep the block in the queue - } - } // for itr - m_BlockTickQueueCopy[] -} - - - - - -void cWorld::QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait) -{ - BlockTickQueueItem * Block = new BlockTickQueueItem; - Block->X = a_BlockX; - Block->Y = a_BlockY; - Block->Z = a_BlockZ; - Block->TicksToWait = a_TicksToWait; - - m_BlockTickQueue.push_back(Block); -} - - - - - -bool cWorld::IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - return ( - IsBlockWater(GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ)) || - IsBlockWater(GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ)) || - IsBlockWater(GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1)) || - IsBlockWater(GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1)) - ); -} - - - - - -int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType) -{ - cMonster * Monster = NULL; - - Monster = cMonster::NewMonsterFromType(a_MonsterType); - if (Monster != NULL) - { - Monster->SetPosition(a_PosX, a_PosY, a_PosZ); - } - - // Because it's logical that ALL mob spawns need spawn effects, not just spawners - BroadcastSoundParticleEffect(2004, (int)a_PosX, (int)a_PosY, (int)a_PosZ, 0); - - return SpawnMobFinalize(Monster); -} - - - - -int cWorld::SpawnMobFinalize(cMonster * a_Monster) -{ - if (!a_Monster) - return -1; - a_Monster->SetHealth(a_Monster->GetMaxHealth()); - if (cPluginManager::Get()->CallHookSpawningMonster(*this, *a_Monster)) - { - delete a_Monster; - return -1; - } - if (!a_Monster->Initialize(this)) - { - delete a_Monster; - return -1; - } - BroadcastSpawnEntity(*a_Monster); - cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster); - - return a_Monster->GetUniqueID(); -} - - - - - -int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed) -{ - cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Speed); - if (Projectile == NULL) - { - return -1; - } - if (!Projectile->Initialize(this)) - { - delete Projectile; - return -1; - } - BroadcastSpawnEntity(*Projectile); - return Projectile->GetUniqueID(); -} - - - - - -void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Results) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(), end = m_Players.end(); itr != end; ++itr) - { - size_t LastSpace = a_Text.find_last_of(" "); //Find the position of the last space - - std::string LastWord = a_Text.substr(LastSpace + 1, a_Text.length()); //Find the last word - std::string PlayerName ((*itr)->GetName()); - std::size_t Found = PlayerName.find(LastWord); //Try to find last word in playername - - if (Found!=0) - { - continue; //No match - } - - a_Results.push_back((*itr)->GetName()); //Match! - } -} - - - - - -cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock) -{ - AString SimulatorNameKey; - Printf(SimulatorNameKey, "%sSimulator", a_FluidName); - AString SimulatorSectionName; - Printf(SimulatorSectionName, "%sSimulator", a_FluidName); - AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, ""); - if (SimulatorName.empty()) - { - LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Floody\".", SimulatorNameKey.c_str(), GetIniFileName().c_str()); - SimulatorName = "Floody"; - } - - cFluidSimulator * res = NULL; - bool IsWater = (strcmp(a_FluidName, "Water") == 0); // Used for defaults - int Rate = 1; - if ( - (NoCaseCompare(SimulatorName, "vaporize") == 0) || - (NoCaseCompare(SimulatorName, "vaporise") == 0) - ) - { - res = new cVaporizeFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock); - } - else if ( - (NoCaseCompare(SimulatorName, "noop") == 0) || - (NoCaseCompare(SimulatorName, "nop") == 0) || - (NoCaseCompare(SimulatorName, "null") == 0) || - (NoCaseCompare(SimulatorName, "nil") == 0) - ) - { - res = new cNoopFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock); - } - else - { - if (NoCaseCompare(SimulatorName, "floody") != 0) - { - // The simulator name doesn't match anything we have, issue a warning: - LOGWARNING("%s [Physics]:%s specifies an unknown simulator, using the default \"Floody\".", GetIniFileName().c_str(), SimulatorNameKey.c_str()); - } - int Falloff = a_IniFile.GetValueSetI(SimulatorSectionName, "Falloff", IsWater ? 1 : 2); - int TickDelay = a_IniFile.GetValueSetI(SimulatorSectionName, "TickDelay", IsWater ? 5 : 30); - int NumNeighborsForSource = a_IniFile.GetValueSetI(SimulatorSectionName, "NumNeighborsForSource", IsWater ? 2 : -1); - res = new cFloodyFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource); - } - - m_SimulatorManager->RegisterSimulator(res, Rate); - - return res; -} - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorld::cTaskSaveAllChunks: - -void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World) -{ - a_World.SaveAllChunks(); -} - - - - - diff --git a/source/World.h b/source/World.h deleted file mode 100644 index ee4a23b14..000000000 --- a/source/World.h +++ /dev/null @@ -1,744 +0,0 @@ - -#pragma once - -#ifndef _WIN32 - #include "BlockID.h" -#else - enum ENUM_ITEM_ID; -#endif - -#define MAX_PLAYERS 65535 - -#include "Simulator/SimulatorManager.h" -#include "MersenneTwister.h" -#include "ChunkMap.h" -#include "WorldStorage/WorldStorage.h" -#include "Generating/ChunkGenerator.h" -#include "Vector3i.h" -#include "Vector3f.h" -#include "ChunkSender.h" -#include "Defines.h" -#include "LightingThread.h" -#include "Item.h" -#include "Mobs/Monster.h" -#include "Entities/ProjectileEntity.h" - - - - - -class cRedstone; -class cFireSimulator; -class cFluidSimulator; -class cSandSimulator; -class cRedstoneSimulator; -class cItem; -class cPlayer; -class cClientHandle; -class cEntity; -class cBlockEntity; -class cWorldGenerator; // The generator that actually generates the chunks for a single world -class cChunkGenerator; // The thread responsible for generating chunks -class cChestEntity; -class cDispenserEntity; -class cFurnaceEntity; -class cMobCensus; - -typedef std::list< cPlayer * > cPlayerList; - -typedef cItemCallback cPlayerListCallback; -typedef cItemCallback cEntityCallback; -typedef cItemCallback cChestCallback; -typedef cItemCallback cDispenserCallback; -typedef cItemCallback cFurnaceCallback; - - - - - - -// tolua_begin -class cWorld -{ -public: - - // tolua_end - - /// A simple RAII locker for the chunkmap - locks the chunkmap in its constructor, unlocks it in the destructor - class cLock : - public cCSLock - { - typedef cCSLock super; - public: - cLock(cWorld & a_World); - } ; - - /// A common ancestor for all tasks queued onto the tick thread - class cTask - { - public: - virtual void Run(cWorld & a_World) = 0; - } ; - - typedef std::vector cTasks; - - class cTaskSaveAllChunks : - public cTask - { - protected: - // cTask overrides: - virtual void Run(cWorld & a_World) override; - } ; - - - static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates - { - return "cWorld"; - } - - // tolua_begin - - int GetTicksUntilWeatherChange(void) const { return m_WeatherInterval; } - Int64 GetWorldAge(void) const { return m_WorldAge; } - Int64 GetTimeOfDay(void) const { return m_TimeOfDay; } - - void SetTicksUntilWeatherChange(int a_WeatherInterval) - { - m_WeatherInterval = a_WeatherInterval; - } - - void SetTimeOfDay(Int64 a_TimeOfDay) - { - m_TimeOfDay = a_TimeOfDay; - m_TimeOfDaySecs = (double)a_TimeOfDay / 20.0; - BroadcastTimeUpdate(); - } - - /// Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable - eGameMode GetGameMode(void) const { return m_GameMode; } - - /// Returns true if the world is in Creative mode - bool IsGameModeCreative(void) const { return (m_GameMode == gmCreative); } - - /// Returns true if the world is in Survival mode - bool IsGameModeSurvival(void) const { return (m_GameMode == gmSurvival); } - - /// Returns true if the world is in Adventure mode - bool IsGameModeAdventure(void) const { return (m_GameMode == gmAdventure); } - - bool IsPVPEnabled(void) const { return m_bEnabledPVP; } - bool IsDeepSnowEnabled(void) const { return m_IsDeepSnowEnabled; } - - eDimension GetDimension(void) const { return m_Dimension; } - - /// Returns the world height at the specified coords; waits for the chunk to get loaded / generated - int GetHeight(int a_BlockX, int a_BlockZ); - - // tolua_end - - /// Retrieves the world height at the specified coords; returns false if chunk not loaded / generated - bool TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height); // Exported in ManualBindings.cpp - - // Broadcast respective packets to all clients of the chunk where the event is taking place - // (Please keep these alpha-sorted) - void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle); - void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); - void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL); - void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude - void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_export - void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); - void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); - void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityMetadata (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastPlayerAnimation (const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); - void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL); - void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // tolua_export a_Src coords are Block * 8 - void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); // tolua_export - void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); - void BroadcastTimeUpdate (const cClientHandle * a_Exclude = NULL); - void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); - void BroadcastWeather (eWeather a_Weather, const cClientHandle * a_Exclude = NULL); - - /// If there is a block entity at the specified coords, sends it to the client specified - void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); - - void MarkChunkDirty (int a_ChunkX, int a_ChunkZ); - void MarkChunkSaving(int a_ChunkX, int a_ChunkZ); - void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); - - /** Sets the chunk data as either loaded from the storage or generated. - a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. - a_BiomeMap is optional, if not present, biomes will be calculated by the generator - a_HeightMap is optional, if not present, will be calculated. - If a_MarkDirty is set, the chunk is set as dirty (used after generating) - */ - void SetChunkData( - int a_ChunkX, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap * a_BiomeMap, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities, - bool a_MarkDirty - ); - - void ChunkLighted( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight - ); - - bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback); - - /// Gets the chunk's blocks, only the block types - bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes); - - bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const; - bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const; - - void UnloadUnusedChunks(void); // tolua_export - - void CollectPickupsByPlayer(cPlayer * a_Player); - - void AddPlayer( cPlayer* a_Player ); - void RemovePlayer( cPlayer* a_Player ); - - /// Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true - bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - - /// Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored - bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - - /// Finds a player from a partial or complete player name and calls the callback - case-insensitive - bool FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - - // TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action) - cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit); - - void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player - - /// Adds the entity into its appropriate chunk; takes ownership of the entity ptr - void AddEntity(cEntity * a_Entity); - - bool HasEntity(int a_UniqueID); - - /// Removes the entity, the entity ptr ownership is assumed taken by the caller - void RemoveEntity(cEntity * a_Entity); - - /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntity(cEntityCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. - bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Compares clients of two chunks, calls the callback accordingly - void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback); - - /// Adds client to a chunk, if not already present; returns true if added, false if present - bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes client from the chunk specified - void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes the client from all chunks it is present in - void RemoveClientFromChunks(cClientHandle * a_Client); - - /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted) - void SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes client from ChunkSender's queue of chunks to be sent - void RemoveClientFromChunkSender(cClientHandle * a_Client); - - /// Touches the chunk, causing it to be loaded or generated - void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) - bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() - void LoadChunks(const cChunkCoordsList & a_Chunks); - - /// Marks the chunk as failed-to-load: - void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as UpdateSign() - bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp - - /// Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as SetSignLines() - bool UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp - - /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay! - void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); - - /// Regenerate the given chunk: - void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export - - /// Generates the given chunk, if not already generated - void GenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export - - /// Queues a chunk for lighting; a_Callback is called after the chunk is lighted - void QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = NULL); - - bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); - - /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully - bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); - - // tolua_begin - - /** Sets the block at the specified coords to the specified value. - Full processing, incl. updating neighbors, is performed. - */ - void SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /** Sets the block at the specified coords to the specified value. - The replacement doesn't trigger block updates. - The replaced blocks aren't checked for block entities (block entity is leaked if it exists at this block) - */ - void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /** Queues a SetBlock() with the specified parameters after the specified number of ticks. - Calls SetBlock(), so performs full processing of the replaced block. - */ - void QueueSetBlock(int a_BlockX, int a_BLockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_TickDelay); - - BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ); - NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ); - void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_MetaData); - NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ); - NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ); - - // tolua_end - - bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); // TODO: Exported in ManualBindings.cpp - bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); // TODO: Exported in ManualBindings.cpp - // TODO: NIBBLETYPE GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ); - - // tolua_begin - - // Vector3i variants: - void FastSetBlock(const Vector3i & a_Pos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { FastSetBlock( a_Pos.x, a_Pos.y, a_Pos.z, a_BlockType, a_BlockMeta ); } - BLOCKTYPE GetBlock (const Vector3i & a_Pos ) { return GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); } - NIBBLETYPE GetBlockMeta(const Vector3i & a_Pos ) { return GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z ); } - void SetBlockMeta(const Vector3i & a_Pos, NIBBLETYPE a_MetaData ) { SetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z, a_MetaData ); } - // tolua_end - - /** Writes the block area into the specified coords. - Returns true if all chunks have been processed. - Prefer cBlockArea::Write() instead, this is the internal implementation; cBlockArea does error checking, too. - a_DataTypes is a bitmask of cBlockArea::baXXX constants ORed together. - */ - bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); - - // tolua_begin - - /// Spawns item pickups for each item in the list. May compress pickups if too many entities: - void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false); - - /// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified: - void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false); - - /// Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided - void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff = 1); - - // tolua_end - - /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType - void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); - - /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. - bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); - - // tolua_begin - bool DigBlock (int a_X, int a_Y, int a_Z); - void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player ); - - double GetSpawnX(void) const { return m_SpawnX; } - double GetSpawnY(void) const { return m_SpawnY; } - double GetSpawnZ(void) const { return m_SpawnZ; } - - /// Wakes up the simulators for the specified block - void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Wakes up the simulators for the specified area of blocks - void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ); - - // tolua_end - - inline cSimulatorManager * GetSimulatorManager(void) { return m_SimulatorManager; } - - inline cFluidSimulator * GetWaterSimulator(void) { return m_WaterSimulator; } - inline cFluidSimulator * GetLavaSimulator (void) { return m_LavaSimulator; } - - /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true - bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true - bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback); - - /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true - bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback); - - /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true - bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback); - - /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true - bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp - - /** Does an explosion with the specified strength at the specified coordinate - a_SourceData exact type depends on the a_Source: - | esOther | void * | - | esPrimedTNT | cTNTEntity * | - | esCreeper | cCreeper * | - | esBed | cVector3i * | - | esEnderCrystal | Vector3i * | - | esGhastFireball | cGhastFireball * | - | esWitherSkullBlack | TBD | - | esWitherSkullBlue | TBD | - | esWitherBirth | TBD | - | esPlugin | void * | - */ - void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData); // tolua_export - - /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found - bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found - bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found - bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found - bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found - bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found - bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp - - /// a_Player is using block entity at [x, y, z], handle that: - void UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {m_ChunkMap->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); } // tolua_export - - /// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback - bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback); - - void GrowTreeImage(const sSetBlockVector & a_Blocks); - - // tolua_begin - - /// Grows a tree at the specified coords, either from a sapling there, or based on the biome - void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Grows a tree at the specified coords, based on the sapling meta provided - void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_SaplingMeta); - - /// Grows a tree at the specified coords, based on the biome in the place - void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini - bool GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false); - - /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config - void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - - /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) - void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); - - /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config - void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - - /// Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value - int GetBiomeAt(int a_BlockX, int a_BlockZ); - - /// Returns the name of the world - const AString & GetName(void) const { return m_WorldName; } - - /// Returns the name of the world.ini file used by this world - const AString & GetIniFileName(void) const {return m_IniFileName; } - - // tolua_end - - inline static void AbsoluteToRelative( int & a_X, int & a_Y, int & a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) - { - // TODO: Use floor() instead of weird if statements - // Also fix Y - a_ChunkX = a_X/cChunkDef::Width; - if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; - a_ChunkY = 0; - a_ChunkZ = a_Z/cChunkDef::Width; - if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; - - a_X = a_X - a_ChunkX*cChunkDef::Width; - a_Y = a_Y - a_ChunkY*cChunkDef::Height; - a_Z = a_Z - a_ChunkZ*cChunkDef::Width; - } - - inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) - { - // TODO: Use floor() instead of weird if statements - // Also fix Y - (void)a_Y; // not unused anymore - a_ChunkX = a_X/cChunkDef::Width; - if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; - a_ChunkY = 0; - a_ChunkZ = a_Z/cChunkDef::Width; - if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; - } - - /// Saves all chunks immediately. Dangerous interface, may deadlock, use QueueSaveAllChunks() instead - void SaveAllChunks(void); - - /// Queues a task to save all chunks onto the tick thread. The prefferred way of saving chunks from external sources - void QueueSaveAllChunks(void); // tolua_export - - /// Queues a task onto the tick thread. The task object will be deleted once the task is finished - void QueueTask(cTask * a_Task); // Exported in ManualBindings.cpp - - /// Returns the number of chunks loaded - int GetNumChunks() const; // tolua_export - - /// Returns the number of chunks loaded and dirty, and in the lighting queue - void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue); - - // Various queues length queries (cannot be const, they lock their CS): - inline int GetGeneratorQueueLength (void) { return m_Generator.GetQueueLength(); } // tolua_export - inline int GetLightingQueueLength (void) { return m_Lighting.GetQueueLength(); } // tolua_export - inline int GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export - inline int GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export - - void InitializeSpawn(void); - - /// Starts threads that belong to this world - void Start(void); - - /// Stops threads that belong to this world (part of deinit) - void Stop(void); - - /// Processes the blocks queued for ticking with a delay (m_BlockTickQueue[]) - void TickQueuedBlocks(void); - - struct BlockTickQueueItem - { - int X; - int Y; - int Z; - int TicksToWait; - }; - - /// Queues the block to be ticked after the specified number of game ticks - void QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait); // tolua_export - - // tolua_begin - /// Casts a thunderbolt at the specified coords - void CastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Sets the specified weather; resets weather interval; asks and notifies plugins of the change - void SetWeather (eWeather a_NewWeather); - - /// Forces a weather change in the next game tick - void ChangeWeather (void); - - /// Returns the current weather. Instead of comparing values directly to the weather constants, use IsWeatherXXX() functions, if possible - eWeather GetWeather (void) const { return m_Weather; }; - - bool IsWeatherSunny(void) const { return (m_Weather == wSunny); } - bool IsWeatherRain (void) const { return (m_Weather == wRain); } - bool IsWeatherStorm(void) const { return (m_Weather == wStorm); } - - /// Returns true if the current weather has any precipitation - rain or storm - bool IsWeatherWet (void) const { return (m_Weather != wSunny); } - - // tolua_end - - cChunkGenerator & GetGenerator(void) { return m_Generator; } - cWorldStorage & GetStorage (void) { return m_Storage; } - cChunkMap * GetChunkMap (void) { return m_ChunkMap; } - - /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call - void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export - - int GetMaxSugarcaneHeight(void) const { return m_MaxSugarcaneHeight; } // tolua_export - int GetMaxCactusHeight (void) const { return m_MaxCactusHeight; } // tolua_export - - bool IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export - - /// Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise - int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType); // tolua_export - int SpawnMobFinalize(cMonster* a_Monster); - - /// Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise - int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed = NULL); // tolua_export - - /// Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! - int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); } - - /// Appends all usernames starting with a_Text (case-insensitive) into Results - void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results); - - /// Get the current darkness level based on the time - NIBBLETYPE GetSkyDarkness() { return m_SkyDarkness; } - -private: - - friend class cRoot; - - class cTickThread : - public cIsThread - { - typedef cIsThread super; - public: - cTickThread(cWorld & a_World); - - protected: - cWorld & m_World; - - // cIsThread overrides: - virtual void Execute(void) override; - } ; - - - AString m_WorldName; - AString m_IniFileName; - - /// Name of the storage schema used to load and save chunks - AString m_StorageSchema; - - /// The dimension of the world, used by the client to provide correct lighting scheme - eDimension m_Dimension; - - /// This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe) - MTRand m_TickRand; - - double m_SpawnX; - double m_SpawnY; - double m_SpawnZ; - - double m_WorldAgeSecs; // World age, in seconds. Is only incremented, cannot be set by plugins. - double m_TimeOfDaySecs; // Time of day in seconds. Can be adjusted. Is wrapped to zero each day. - Int64 m_WorldAge; // World age in ticks, calculated off of m_WorldAgeSecs - Int64 m_TimeOfDay; // Time in ticks, calculated off of m_TimeOfDaySecs - Int64 m_LastTimeUpdate; // The tick in which the last time update has been sent. - Int64 m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred - Int64 m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred - std::map m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed) - - NIBBLETYPE m_SkyDarkness; - - eGameMode m_GameMode; - bool m_bEnabledPVP; - bool m_IsDeepSnowEnabled; - - // The cRedstone class simulates redstone and needs access to m_RSList - // friend class cRedstone; - std::vector m_RSList; - - std::vector m_BlockTickQueue; - std::vector m_BlockTickQueueCopy; // Second is for safely removing the objects from the queue - - cSimulatorManager * m_SimulatorManager; - cSandSimulator * m_SandSimulator; - cFluidSimulator * m_WaterSimulator; - cFluidSimulator * m_LavaSimulator; - cFireSimulator * m_FireSimulator; - cRedstoneSimulator * m_RedstoneSimulator; - - cCriticalSection m_CSPlayers; - cPlayerList m_Players; - - cWorldStorage m_Storage; - - unsigned int m_MaxPlayers; - - cChunkMap * m_ChunkMap; - - bool m_bAnimals; - std::set m_AllowedMobs; - - eWeather m_Weather; - int m_WeatherInterval; - - int m_MaxCactusHeight; - int m_MaxSugarcaneHeight; - bool m_IsCactusBonemealable; - bool m_IsCarrotsBonemealable; - bool m_IsCropsBonemealable; - bool m_IsGrassBonemealable; - bool m_IsMelonStemBonemealable; - bool m_IsMelonBonemealable; - bool m_IsPotatoesBonemealable; - bool m_IsPumpkinStemBonemealable; - bool m_IsPumpkinBonemealable; - bool m_IsSaplingBonemealable; - bool m_IsSugarcaneBonemealable; - - cCriticalSection m_CSFastSetBlock; - sSetBlockList m_FastSetBlockQueue; - - cChunkGenerator m_Generator; - - cChunkSender m_ChunkSender; - cLightingThread m_Lighting; - cTickThread m_TickThread; - - /// Guards the m_Tasks - cCriticalSection m_CSTasks; - - /// Tasks that have been queued onto the tick thread; guarded by m_CSTasks - cTasks m_Tasks; - - /// Guards m_Clients - cCriticalSection m_CSClients; - - /// List of clients in this world, these will be ticked by this world - cClientHandleList m_Clients; - - /// Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them - cClientHandleList m_ClientsToRemove; - - /// Clients that are scheduled for adding, waiting for TickClients to add them - cClientHandleList m_ClientsToAdd; - - - cWorld(const AString & a_WorldName); - ~cWorld(); - - void Tick(float a_Dt); - - /// Handles the weather in each tick - void TickWeather(float a_Dt); - - /// Handles the mob spawning/moving/destroying each tick - void TickMobs(float a_Dt); - - /// Executes all tasks queued onto the tick thread - void TickQueuedTasks(void); - - /// Ticks all clients that are in this world - void TickClients(float a_Dt); - - void UpdateSkyDarkness(); - - /// Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section) - cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock); -}; // tolua_export - - - - diff --git a/source/WorldStorage/FastNBT.cpp b/source/WorldStorage/FastNBT.cpp deleted file mode 100644 index e55011069..000000000 --- a/source/WorldStorage/FastNBT.cpp +++ /dev/null @@ -1,547 +0,0 @@ - -// FastNBT.cpp - -// Implements the fast NBT parser and writer - -#include "Globals.h" -#include "FastNBT.h" - - - - - -// The number of NBT tags that are reserved when an NBT parsing is started. -// You can override this by using a cmdline define -#ifndef NBT_RESERVE_SIZE - #define NBT_RESERVE_SIZE 200 -#endif // NBT_RESERVE_SIZE - -#define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while (0) - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cParsedNBT: - -#define NEEDBYTES(N) \ - if (m_Length - m_Pos < N) \ - { \ - return false; \ - } - - - - - -cParsedNBT::cParsedNBT(const char * a_Data, int a_Length) : - m_Data(a_Data), - m_Length(a_Length), - m_Pos(0) -{ - m_IsValid = Parse(); -} - - - - - -bool cParsedNBT::Parse(void) -{ - if (m_Length < 3) - { - // Data too short - return false; - } - if (m_Data[0] != TAG_Compound) - { - // The top-level tag must be a Compound - return false; - } - - m_Tags.reserve(NBT_RESERVE_SIZE); - - m_Tags.push_back(cFastNBTTag(TAG_Compound, -1)); - - m_Pos = 1; - - RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength)); - RETURN_FALSE_IF_FALSE(ReadCompound()); - - return true; -} - - - - - -bool cParsedNBT::ReadString(int & a_StringStart, int & a_StringLen) -{ - NEEDBYTES(2); - a_StringStart = m_Pos + 2; - a_StringLen = ntohs(*((short *)(m_Data + m_Pos))); - if (a_StringLen < 0) - { - // Invalid string length - return false; - } - m_Pos += 2 + a_StringLen; - return true; -} - - - - - -bool cParsedNBT::ReadCompound(void) -{ - // Reads the latest tag as a compound - int ParentIdx = m_Tags.size() - 1; - int PrevSibling = -1; - while (true) - { - NEEDBYTES(1); - eTagType TagType = (eTagType)(m_Data[m_Pos]); - m_Pos++; - if (TagType == TAG_End) - { - break; - } - m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling)); - if (PrevSibling >= 0) - { - m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1; - } - else - { - m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1; - } - PrevSibling = m_Tags.size() - 1; - RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength)); - RETURN_FALSE_IF_FALSE(ReadTag()); - } // while (true) - m_Tags[ParentIdx].m_LastChild = PrevSibling; - return true; -} - - - - - -bool cParsedNBT::ReadList(eTagType a_ChildrenType) -{ - // Reads the latest tag as a list of items of type a_ChildrenType - - // Read the count: - NEEDBYTES(4); - int Count = ntohl(*((int *)(m_Data + m_Pos))); - m_Pos += 4; - if (Count < 0) - { - return false; - } - - // Read items: - int ParentIdx = m_Tags.size() - 1; - int PrevSibling = -1; - for (int i = 0; i < Count; i++) - { - m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling)); - if (PrevSibling >= 0) - { - m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1; - } - else - { - m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1; - } - PrevSibling = m_Tags.size() - 1; - RETURN_FALSE_IF_FALSE(ReadTag()); - } // for (i) - m_Tags[ParentIdx].m_LastChild = PrevSibling; - return true; -} - - - - - -#define CASE_SIMPLE_TAG(TAGTYPE, LEN) \ - case TAG_##TAGTYPE: \ - { \ - NEEDBYTES(LEN); \ - Tag.m_DataStart = m_Pos; \ - Tag.m_DataLength = LEN; \ - m_Pos += LEN; \ - return true; \ - } - -bool cParsedNBT::ReadTag(void) -{ - cFastNBTTag & Tag = m_Tags.back(); - switch (Tag.m_Type) - { - CASE_SIMPLE_TAG(Byte, 1) - CASE_SIMPLE_TAG(Short, 2) - CASE_SIMPLE_TAG(Int, 4) - CASE_SIMPLE_TAG(Long, 8) - CASE_SIMPLE_TAG(Float, 4) - CASE_SIMPLE_TAG(Double, 8) - - case TAG_String: - { - return ReadString(Tag.m_DataStart, Tag.m_DataLength); - } - - case TAG_ByteArray: - { - NEEDBYTES(4); - int len = ntohl(*((int *)(m_Data + m_Pos))); - m_Pos += 4; - if (len < 0) - { - // Invalid length - return false; - } - NEEDBYTES(len); - Tag.m_DataLength = len; - Tag.m_DataStart = m_Pos; - m_Pos += len; - return true; - } - - case TAG_List: - { - NEEDBYTES(1); - eTagType ItemType = (eTagType)m_Data[m_Pos]; - m_Pos++; - RETURN_FALSE_IF_FALSE(ReadList(ItemType)); - return true; - } - - case TAG_Compound: - { - RETURN_FALSE_IF_FALSE(ReadCompound()); - return true; - } - - case TAG_IntArray: - { - NEEDBYTES(4); - int len = ntohl(*((int *)(m_Data + m_Pos))); - m_Pos += 4; - if (len < 0) - { - // Invalid length - return false; - } - len *= 4; - NEEDBYTES(len); - Tag.m_DataLength = len; - Tag.m_DataStart = m_Pos; - m_Pos += len; - return true; - } - - default: - { - ASSERT(!"Unhandled NBT tag type"); - return false; - } - } // switch (iType) -} - -#undef CASE_SIMPLE_TAG - - - - - -int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength) const -{ - if (a_Tag < 0) - { - return -1; - } - if (m_Tags[a_Tag].m_Type != TAG_Compound) - { - return -1; - } - - if (a_NameLength == 0) - { - a_NameLength = strlen(a_Name); - } - for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling) - { - if ( - (m_Tags[Child].m_NameLength == a_NameLength) && - (memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0) - ) - { - return Child; - } - } // for Child - children of a_Tag - return -1; -} - - - - - -int cParsedNBT::FindTagByPath(int a_Tag, const AString & a_Path) const -{ - if (a_Tag < 0) - { - return -1; - } - size_t Begin = 0; - size_t Length = a_Path.length(); - int Tag = a_Tag; - for (size_t i = 0; i < Length; i++) - { - if (a_Path[i] != '\\') - { - continue; - } - Tag = FindChildByName(Tag, a_Path.c_str() + Begin, i - Begin - 1); - if (Tag < 0) - { - return -1; - } - Begin = i + 1; - } // for i - a_Path[] - - if (Begin < Length) - { - Tag = FindChildByName(Tag, a_Path.c_str() + Begin, Length - Begin); - } - return Tag; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFastNBTWriter: - -cFastNBTWriter::cFastNBTWriter(const AString & a_RootTagName) : - m_CurrentStack(0) -{ - m_Stack[0].m_Type = TAG_Compound; - m_Result.reserve(100 * 1024); - m_Result.push_back(TAG_Compound); - WriteString(a_RootTagName.data(), a_RootTagName.size()); -} - - - - - -void cFastNBTWriter::BeginCompound(const AString & a_Name) -{ - if (m_CurrentStack >= MAX_STACK) - { - ASSERT(!"Stack overflow"); - return; - } - - TagCommon(a_Name, TAG_Compound); - - ++m_CurrentStack; - m_Stack[m_CurrentStack].m_Type = TAG_Compound; -} - - - - - -void cFastNBTWriter::EndCompound(void) -{ - ASSERT(m_CurrentStack > 0); - ASSERT(IsStackTopCompound()); - - m_Result.push_back(TAG_End); - --m_CurrentStack; -} - - - - - -void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType) -{ - if (m_CurrentStack >= MAX_STACK) - { - ASSERT(!"Stack overflow"); - return; - } - - TagCommon(a_Name, TAG_List); - - m_Result.push_back((char)a_ChildrenType); - m_Result.append(4, (char)0); - - ++m_CurrentStack; - m_Stack[m_CurrentStack].m_Type = TAG_List; - m_Stack[m_CurrentStack].m_Pos = m_Result.size() - 4; - m_Stack[m_CurrentStack].m_Count = 0; - m_Stack[m_CurrentStack].m_ItemType = a_ChildrenType; -} - - - - - -void cFastNBTWriter::EndList(void) -{ - ASSERT(m_CurrentStack > 0); - ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List); - - // Update the list count: - *((int *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos)) = htonl(m_Stack[m_CurrentStack].m_Count); - - --m_CurrentStack; -} - - - - - -void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value) -{ - TagCommon(a_Name, TAG_Byte); - m_Result.push_back(a_Value); -} - - - - - -void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value) -{ - TagCommon(a_Name, TAG_Short); - Int16 Value = htons(a_Value); - m_Result.append((const char *)&Value, 2); -} - - - - - -void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value) -{ - TagCommon(a_Name, TAG_Int); - Int32 Value = htonl(a_Value); - m_Result.append((const char *)&Value, 4); -} - - - - - -void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value) -{ - TagCommon(a_Name, TAG_Long); - Int64 Value = HostToNetwork8(&a_Value); - m_Result.append((const char *)&Value, 8); -} - - - - - -void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value) -{ - TagCommon(a_Name, TAG_Float); - Int32 Value = HostToNetwork4(&a_Value); - m_Result.append((const char *)&Value, 4); -} - - - - - -void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value) -{ - TagCommon(a_Name, TAG_Double); - Int64 Value = HostToNetwork8(&a_Value); - m_Result.append((const char *)&Value, 8); -} - - - - - -void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value) -{ - TagCommon(a_Name, TAG_String); - Int16 len = htons((short)(a_Value.size())); - m_Result.append((const char *)&len, 2); - m_Result.append(a_Value.c_str(), a_Value.size()); -} - - - - - -void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements) -{ - TagCommon(a_Name, TAG_ByteArray); - Int32 len = htonl(a_NumElements); - m_Result.append((const char *)&len, 4); - m_Result.append(a_Value, a_NumElements); -} - - - - - -void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements) -{ - TagCommon(a_Name, TAG_IntArray); - Int32 len = htonl(a_NumElements); - m_Result.append((const char *)&len, 4); -#if defined(ANDROID_NDK) - // Android has alignment issues - cannot byteswap (htonl) an int that is not 32-bit-aligned, which happens in the regular version - for (size_t i = 0; i < a_NumElements; i++) - { - int Element = htonl(a_Value[i]); - m_Result.append((const char *)&Element, 4); - } -#else - int * Elements = (int *)(m_Result.data() + m_Result.size()); - m_Result.append(a_NumElements * 4, (char)0); - for (size_t i = 0; i < a_NumElements; i++) - { - Elements[i] = htonl(a_Value[i]); - } -#endif -} - - - - - -void cFastNBTWriter::Finish(void) -{ - ASSERT(m_CurrentStack == 0); - m_Result.push_back(TAG_End); -} - - - - - -void cFastNBTWriter::WriteString(const char * a_Data, short a_Length) -{ - Int16 Len = htons(a_Length); - m_Result.append((const char *)&Len, 2); - m_Result.append(a_Data, a_Length); -} - - - - diff --git a/source/WorldStorage/FastNBT.h b/source/WorldStorage/FastNBT.h deleted file mode 100644 index 7323c29cb..000000000 --- a/source/WorldStorage/FastNBT.h +++ /dev/null @@ -1,293 +0,0 @@ - -// FastNBT.h - -// Interfaces to the fast NBT parser and writer - -/* -The fast parser parses the data into a vector of cFastNBTTag structures. These structures describe the NBT tree, -but themselves are allocated in a vector, thus minimizing reallocation. -The structures have a minimal constructor, setting all member "pointers" to "invalid". - -The fast writer doesn't need a NBT tree structure built beforehand, it is commanded to open, append and close tags -(just like XML); it keeps the internal tag stack and reports errors in usage. -It directly outputs a string containing the serialized NBT data. -*/ - - - - - -#pragma once - -#include "../Endianness.h" - - - - - -enum eTagType -{ - TAG_Min = 0, // The minimum value for a tag type - TAG_End = 0, - TAG_Byte = 1, - TAG_Short = 2, - TAG_Int = 3, - TAG_Long = 4, - TAG_Float = 5, - TAG_Double = 6, - TAG_ByteArray = 7, - TAG_String = 8, - TAG_List = 9, - TAG_Compound = 10, - TAG_IntArray = 11, - TAG_Max = 11, // The maximum value for a tag type -} ; - - - - - -/** This structure is used for all NBT tags. -It contains indices to the parent array of tags, building the NBT tree this way. -Also contains indices into the data stream being parsed, used for values; -NO dynamically allocated memory is used! -Structure (all with the tree structure it describes) supports moving in memory (std::vector reallocation) -*/ -struct cFastNBTTag -{ -public: - - eTagType m_Type; - - // The following members are indices into the data stream. m_DataLength == 0 if no data available - // They must not be pointers, because the datastream may be copied into another AString object in the meantime. - int m_NameStart; - int m_NameLength; - int m_DataStart; - int m_DataLength; - - // The following members are indices into the array returned; -1 if not valid - // They must not be pointers, because pointers would not survive std::vector reallocation - int m_Parent; - int m_PrevSibling; - int m_NextSibling; - int m_FirstChild; - int m_LastChild; - - cFastNBTTag(eTagType a_Type, int a_Parent) : - m_Type(a_Type), - m_NameLength(0), - m_DataLength(0), - m_Parent(a_Parent), - m_PrevSibling(-1), - m_NextSibling(-1), - m_FirstChild(-1), - m_LastChild(-1) - { - } - - cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling) : - m_Type(a_Type), - m_NameLength(0), - m_DataLength(0), - m_Parent(a_Parent), - m_PrevSibling(a_PrevSibling), - m_NextSibling(-1), - m_FirstChild(-1), - m_LastChild(-1) - { - } -} ; - - - - - -/** Parses and contains the parsed data -Also implements data accessor functions for tree traversal and value getters -The data pointer passed in the constructor is assumed to be valid throughout the object's life. Care must be taken not to initialize from a temporary. -*/ -class cParsedNBT -{ -public: - cParsedNBT(const char * a_Data, int a_Length); - - bool IsValid(void) const {return m_IsValid; } - - int GetRoot(void) const {return 0; } - int GetFirstChild (int a_Tag) const { return m_Tags[a_Tag].m_FirstChild; } - int GetLastChild (int a_Tag) const { return m_Tags[a_Tag].m_LastChild; } - int GetNextSibling(int a_Tag) const { return m_Tags[a_Tag].m_NextSibling; } - int GetPrevSibling(int a_Tag) const { return m_Tags[a_Tag].m_PrevSibling; } - int GetDataLength (int a_Tag) const { return m_Tags[a_Tag].m_DataLength; } - - const char * GetData(int a_Tag) const - { - ASSERT(m_Tags[a_Tag].m_Type != TAG_List); - ASSERT(m_Tags[a_Tag].m_Type != TAG_Compound); - return m_Data + m_Tags[a_Tag].m_DataStart; - } - - int FindChildByName(int a_Tag, const AString & a_Name) const - { - return FindChildByName(a_Tag, a_Name.c_str(), a_Name.length()); - } - - int FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength = 0) const; - int FindTagByPath (int a_Tag, const AString & a_Path) const; - - eTagType GetType(int a_Tag) const { return m_Tags[a_Tag].m_Type; } - - /// Returns the children type for a list tag; undefined on other tags. If list empty, returns TAG_End - eTagType GetChildrenType(int a_Tag) const - { - ASSERT(m_Tags[a_Tag].m_Type == TAG_List); - return (m_Tags[a_Tag].m_FirstChild < 0) ? TAG_End : m_Tags[m_Tags[a_Tag].m_FirstChild].m_Type; - } - - inline unsigned char GetByte(int a_Tag) const - { - ASSERT(m_Tags[a_Tag].m_Type == TAG_Byte); - return (unsigned char)(m_Data[m_Tags[a_Tag].m_DataStart]); - } - - inline Int16 GetShort(int a_Tag) const - { - ASSERT(m_Tags[a_Tag].m_Type == TAG_Short); - return ntohs(*((Int16 *)(m_Data + m_Tags[a_Tag].m_DataStart))); - } - - inline Int32 GetInt(int a_Tag) const - { - ASSERT(m_Tags[a_Tag].m_Type == TAG_Int); - return ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart))); - } - - inline Int64 GetLong(int a_Tag) const - { - ASSERT(m_Tags[a_Tag].m_Type == TAG_Long); - return NetworkToHostLong8(m_Data + m_Tags[a_Tag].m_DataStart); - } - - inline float GetFloat(int a_Tag) const - { - ASSERT(m_Tags[a_Tag].m_Type == TAG_Float); - Int32 tmp = ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart))); - return *((float *)&tmp); - } - - inline double GetDouble(int a_Tag) const - { - ASSERT(m_Tags[a_Tag].m_Type == TAG_Double); - return NetworkToHostDouble8(m_Data + m_Tags[a_Tag].m_DataStart); - } - - inline AString GetString(int a_Tag) const - { - ASSERT(m_Tags[a_Tag].m_Type == TAG_String); - AString res; - res.assign(m_Data + m_Tags[a_Tag].m_DataStart, m_Tags[a_Tag].m_DataLength); - return res; - } - - inline AString GetName(int a_Tag) const - { - AString res; - res.assign(m_Data + m_Tags[a_Tag].m_NameStart, m_Tags[a_Tag].m_NameLength); - return res; - } - -protected: - const char * m_Data; - int m_Length; - std::vector m_Tags; - bool m_IsValid; // True if parsing succeeded - - // Used while parsing: - int m_Pos; - - bool Parse(void); - bool ReadString(int & a_StringStart, int & a_StringLen); // Reads a simple string (2 bytes length + data), sets the string descriptors - bool ReadCompound(void); // Reads the latest tag as a compound - bool ReadList(eTagType a_ChildrenType); // Reads the latest tag as a list of items of type a_ChildrenType - bool ReadTag(void); // Reads the latest tag, depending on its m_Type setting -} ; - - - - - -class cFastNBTWriter -{ -public: - cFastNBTWriter(const AString & a_RootTagName = ""); - - void BeginCompound(const AString & a_Name); - void EndCompound(void); - - void BeginList(const AString & a_Name, eTagType a_ChildrenType); - void EndList(void); - - void AddByte (const AString & a_Name, unsigned char a_Value); - void AddShort (const AString & a_Name, Int16 a_Value); - void AddInt (const AString & a_Name, Int32 a_Value); - void AddLong (const AString & a_Name, Int64 a_Value); - void AddFloat (const AString & a_Name, float a_Value); - void AddDouble (const AString & a_Name, double a_Value); - void AddString (const AString & a_Name, const AString & a_Value); - void AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements); - void AddIntArray (const AString & a_Name, const int * a_Value, size_t a_NumElements); - - void AddByteArray(const AString & a_Name, const AString & a_Value) - { - AddByteArray(a_Name, a_Value.data(), a_Value.size()); - } - - const AString & GetResult(void) const {return m_Result; } - - void Finish(void); - -protected: - - struct sParent - { - int m_Type; // TAG_Compound or TAG_List - int m_Pos; // for TAG_List, the position of the list count - int m_Count; // for TAG_List, the element count - eTagType m_ItemType; // for TAG_List, the element type - } ; - - static const int MAX_STACK = 50; // Highliy doubtful that an NBT would be constructed this many levels deep - - // These two fields emulate a stack. A raw array is used due to speed issues - no reallocations are allowed. - sParent m_Stack[MAX_STACK]; - int m_CurrentStack; - - AString m_Result; - - bool IsStackTopCompound(void) const { return (m_Stack[m_CurrentStack].m_Type == TAG_Compound); } - - void WriteString(const char * a_Data, short a_Length); - - inline void TagCommon(const AString & a_Name, eTagType a_Type) - { - // If we're directly inside a list, check that the list is of the correct type: - ASSERT((m_Stack[m_CurrentStack].m_Type != TAG_List) || (m_Stack[m_CurrentStack].m_ItemType == a_Type)); - - if (IsStackTopCompound()) - { - // Compound: add the type and name: - m_Result.push_back((char)a_Type); - WriteString(a_Name.c_str(), (short)a_Name.length()); - } - else - { - // List: add to the counter - m_Stack[m_CurrentStack].m_Count++; - } - } -} ; - - - - diff --git a/source/WorldStorage/NBTChunkSerializer.cpp b/source/WorldStorage/NBTChunkSerializer.cpp deleted file mode 100644 index c9013b1b3..000000000 --- a/source/WorldStorage/NBTChunkSerializer.cpp +++ /dev/null @@ -1,533 +0,0 @@ - -// NBTChunkSerializer.cpp - - -#include "Globals.h" -#include "NBTChunkSerializer.h" -#include "../BlockID.h" -#include "../BlockEntities/ChestEntity.h" -#include "../BlockEntities/DispenserEntity.h" -#include "../BlockEntities/DropperEntity.h" -#include "../BlockEntities/FurnaceEntity.h" -#include "../BlockEntities/HopperEntity.h" -#include "../BlockEntities/JukeboxEntity.h" -#include "../BlockEntities/NoteEntity.h" -#include "../BlockEntities/SignEntity.h" -#include "../ItemGrid.h" -#include "../StringCompression.h" -#include "../Entities/Entity.h" -#include "FastNBT.h" -#include "../Entities/FallingBlock.h" -#include "../Entities/Boat.h" -#include "../Entities/Minecart.h" -#include "../Mobs/Monster.h" -#include "../Entities/Pickup.h" -#include "../Entities/ProjectileEntity.h" - - - - - -cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) : - m_BiomesAreValid(false), - m_Writer(a_Writer), - m_IsTagOpen(false), - m_HasHadEntity(false), - m_HasHadBlockEntity(false), - m_IsLightValid(false) -{ -} - - - - - -void cNBTChunkSerializer::Finish(void) -{ - if (m_IsTagOpen) - { - m_Writer.EndList(); - } - - // If light not valid, reset it to all zeroes: - if (!m_IsLightValid) - { - memset(m_BlockLight, 0, sizeof(m_BlockLight)); - memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight)); - } -} - - - - - -void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName) -{ - m_Writer.BeginCompound(a_CompoundName); - m_Writer.AddShort("id", (short)(a_Item.m_ItemType)); - m_Writer.AddShort("Damage", a_Item.m_ItemDamage); - m_Writer.AddByte ("Count", a_Item.m_ItemCount); - if (a_Slot >= 0) - { - m_Writer.AddByte ("Slot", (unsigned char)a_Slot); - } - - // Write the enchantments: - if (!a_Item.m_Enchantments.IsEmpty()) - { - const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; - m_Writer.BeginCompound("tag"); - a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName); - m_Writer.EndCompound(); - } - - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum) -{ - int NumSlots = a_Grid.GetNumSlots(); - for (int i = 0; i < NumSlots; i++) - { - const cItem & Item = a_Grid.GetSlot(i); - if (Item.IsEmpty()) - { - continue; - } - AddItem(Item, i + a_BeginSlotNum); - } // for i - chest slots[] -} - - - - - -void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID) -{ - m_Writer.AddInt ("x", a_Entity->GetPosX()); - m_Writer.AddInt ("y", a_Entity->GetPosY()); - m_Writer.AddInt ("z", a_Entity->GetPosZ()); - m_Writer.AddString("id", a_EntityTypeID); -} - - - - - -void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Entity, "Chest"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Entity->GetContents()); - m_Writer.EndList(); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Entity, "Trap"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Entity->GetContents()); - m_Writer.EndList(); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddDropperEntity(cDropperEntity * a_Entity) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Entity, "Dropper"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Entity->GetContents()); - m_Writer.EndList(); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Furnace, "Furnace"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Furnace->GetContents()); - m_Writer.EndList(); - m_Writer.AddShort("BurnTime", a_Furnace->GetFuelBurnTimeLeft()); - m_Writer.AddShort("CookTime", a_Furnace->GetTimeCooked()); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Entity, "Hopper"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Entity->GetContents()); - m_Writer.EndList(); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Jukebox, "RecordPlayer"); - m_Writer.AddInt("Record", a_Jukebox->GetRecord()); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Note, "Music"); - m_Writer.AddByte("note", a_Note->GetPitch()); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Sign, "Sign"); - m_Writer.AddString("Text1", a_Sign->GetLine(0)); - m_Writer.AddString("Text2", a_Sign->GetLine(1)); - m_Writer.AddString("Text3", a_Sign->GetLine(2)); - m_Writer.AddString("Text4", a_Sign->GetLine(3)); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName) -{ - m_Writer.AddString("id", a_ClassName); - m_Writer.BeginList("Pos", TAG_Double); - m_Writer.AddDouble("", a_Entity->GetPosX()); - m_Writer.AddDouble("", a_Entity->GetPosY()); - m_Writer.AddDouble("", a_Entity->GetPosZ()); - m_Writer.EndList(); - m_Writer.BeginList("Motion", TAG_Double); - m_Writer.AddDouble("", a_Entity->GetSpeedX()); - m_Writer.AddDouble("", a_Entity->GetSpeedY()); - m_Writer.AddDouble("", a_Entity->GetSpeedZ()); - m_Writer.EndList(); - m_Writer.BeginList("Rotation", TAG_Double); - m_Writer.AddDouble("", a_Entity->GetRotation()); - m_Writer.AddDouble("", a_Entity->GetPitch()); - m_Writer.EndList(); -} - - - - - -void cNBTChunkSerializer::AddBoatEntity(cBoat * a_Boat) -{ - m_Writer.BeginCompound(""); - AddBasicEntity(a_Boat, "Boat"); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock) -{ - m_Writer.BeginCompound(""); - AddBasicEntity(a_FallingBlock, "FallingSand"); - m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType()); - m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta()); - m_Writer.AddByte("Time", 1); // Unused in MCServer, Vanilla said to need nonzero - m_Writer.AddByte("DropItem", 1); - m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart) -{ - const char * EntityClass = NULL; - switch (a_Minecart->GetPayload()) - { - case cMinecart::mpNone: EntityClass = "MinecartRideable"; break; - case cMinecart::mpChest: EntityClass = "MinecartChest"; break; - case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break; - case cMinecart::mpTNT: EntityClass = "MinecartTNT"; break; - case cMinecart::mpHopper: EntityClass = "MinecartHopper"; break; - default: - { - ASSERT(!"Unhandled minecart payload type"); - return; - } - } // switch (payload) - - m_Writer.BeginCompound(""); - AddBasicEntity(a_Minecart, EntityClass); - switch (a_Minecart->GetPayload()) - { - case cMinecart::mpChest: - { - // Add chest contents into the Items tag: - AddMinecartChestContents((cMinecartWithChest *)a_Minecart); - break; - } - - case cMinecart::mpFurnace: - { - // TODO: Add "Push" and "Fuel" tags - break; - } - } // switch (Payload) - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) -{ - // TODO -} - - - - - -void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup) -{ - m_Writer.BeginCompound(""); - AddBasicEntity(a_Pickup, "Item"); - AddItem(a_Pickup->GetItem(), -1, "Item"); - m_Writer.AddShort("Health", a_Pickup->GetHealth()); - m_Writer.AddShort("Age", a_Pickup->GetAge()); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile) -{ - m_Writer.BeginCompound(""); - AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName()); - Vector3d Pos = a_Projectile->GetPosition(); - m_Writer.AddShort("xTile", (Int16)floor(Pos.x)); - m_Writer.AddShort("yTile", (Int16)floor(Pos.y)); - m_Writer.AddShort("zTile", (Int16)floor(Pos.z)); - m_Writer.AddShort("inTile", 0); // TODO: Query the block type - m_Writer.AddShort("shake", 0); // TODO: Any shake? - m_Writer.AddByte ("inGround", a_Projectile->IsInGround() ? 1 : 0); - - switch (a_Projectile->GetProjectileKind()) - { - case cProjectileEntity::pkArrow: - { - m_Writer.AddByte("inData", 0); // TODO: Query the block meta (is it needed?) - m_Writer.AddByte("pickup", ((cArrowEntity *)a_Projectile)->GetPickupState()); - m_Writer.AddDouble("damage", ((cArrowEntity *)a_Projectile)->GetDamageCoeff()); - break; - } - case cProjectileEntity::pkGhastFireball: - { - m_Writer.AddInt("ExplosionPower", 1); - // fall-through: - } - case cProjectileEntity::pkFireCharge: - case cProjectileEntity::pkWitherSkull: - case cProjectileEntity::pkEnderPearl: - { - m_Writer.BeginList("Motion", TAG_Double); - m_Writer.AddDouble("", a_Projectile->GetSpeedX()); - m_Writer.AddDouble("", a_Projectile->GetSpeedY()); - m_Writer.AddDouble("", a_Projectile->GetSpeedZ()); - m_Writer.EndList(); - break; - } - default: - { - ASSERT(!"Unsaved projectile entity!"); - } - } // switch (ProjectileKind) - cEntity * Creator = a_Projectile->GetCreator(); - if (Creator != NULL) - { - if (Creator->GetEntityType() == cEntity::etPlayer) - { - m_Writer.AddString("ownerName", ((cPlayer *)Creator)->GetName()); - } - } - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart) -{ - m_Writer.BeginList("Items", TAG_Compound); - for (int i = 0; i < cMinecartWithChest::NumSlots; i++) - { - const cItem & Item = a_Minecart->GetSlot(i); - if (Item.IsEmpty()) - { - continue; - } - AddItem(Item, i); - } - m_Writer.EndList(); -} - - - - - -bool cNBTChunkSerializer::LightIsValid(bool a_IsLightValid) -{ - m_IsLightValid = a_IsLightValid; - return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother -} - - - - - -void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) -{ - memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes)); - for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++) - { - if ((*a_BiomeMap)[i] < 255) - { - // Normal MC biome, copy as-is: - m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]); - } - else - { - // TODO: MCS-specific biome, need to map to some basic MC biome: - ASSERT(!"Unimplemented MCS-specific biome"); - return; - } - } // for i - m_BiomeMap[] - m_BiomesAreValid = true; -} - - - - - -void cNBTChunkSerializer::Entity(cEntity * a_Entity) -{ - // Add entity into NBT: - if (m_IsTagOpen) - { - if (!m_HasHadEntity) - { - m_Writer.EndList(); - m_Writer.BeginList("Entities", TAG_Compound); - } - } - else - { - m_Writer.BeginList("Entities", TAG_Compound); - } - m_IsTagOpen = true; - m_HasHadEntity = true; - - switch (a_Entity->GetEntityType()) - { - case cEntity::etBoat: AddBoatEntity ((cBoat *) a_Entity); break; - case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *) a_Entity); break; - case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break; - case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break; - case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break; - case cEntity::etProjectile: AddProjectileEntity ((cProjectileEntity *)a_Entity); break; - case cEntity::etPlayer: return; // Players aren't saved into the world - default: - { - ASSERT(!"Unhandled entity type is being saved"); - break; - } - } -} - - - - - -void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) -{ - if (m_IsTagOpen) - { - if (!m_HasHadBlockEntity) - { - m_Writer.EndList(); - m_Writer.BeginList("TileEntities", TAG_Compound); - } - } - else - { - m_Writer.BeginList("TileEntities", TAG_Compound); - } - m_IsTagOpen = true; - - // Add tile-entity into NBT: - switch (a_Entity->GetBlockType()) - { - case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break; - case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break; - case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break; - case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; - case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; - case E_BLOCK_SIGN_POST: - case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break; - case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break; - case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break; - default: - { - ASSERT(!"Unhandled block entity saved into Anvil"); - } - } - m_HasHadBlockEntity = true; -} - - - - diff --git a/source/WorldStorage/NBTChunkSerializer.h b/source/WorldStorage/NBTChunkSerializer.h deleted file mode 100644 index 9d4ac208c..000000000 --- a/source/WorldStorage/NBTChunkSerializer.h +++ /dev/null @@ -1,116 +0,0 @@ - -// NBTChunkSerializer.h - -// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil - - - - - -#pragma once - -#include "../ChunkDef.h" - - - - - -// fwd: -class cFastNBTWriter; -class cEntity; -class cBlockEntity; -class cBoat; -class cChestEntity; -class cDispenserEntity; -class cDropperEntity; -class cFurnaceEntity; -class cHopperEntity; -class cJukeboxEntity; -class cNoteEntity; -class cSignEntity; -class cFallingBlock; -class cMinecart; -class cMinecartWithChest; -class cMinecartWithFurnace; -class cMinecartWithTNT; -class cMinecartWithHopper; -class cMonster; -class cPickup; -class cItemGrid; -class cProjectileEntity; - - - - - -class cNBTChunkSerializer : - public cChunkDataSeparateCollector -{ -public: - cChunkDef::BiomeMap m_Biomes; - unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width]; - bool m_BiomesAreValid; - - - cNBTChunkSerializer(cFastNBTWriter & a_Writer); - - /// Close NBT tags that we've opened - void Finish(void); - - bool IsLightValid(void) const {return m_IsLightValid; } - -protected: - - /* From cChunkDataSeparateCollector we inherit: - - m_BlockTypes[] - - m_BlockMetas[] - - m_BlockLight[] - - m_BlockSkyLight[] - */ - - cFastNBTWriter & m_Writer; - - bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed. - bool m_HasHadEntity; // True if any Entity has already been received and processed - bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed - bool m_IsLightValid; // True if the chunk lighting is valid - - - /// Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested. - void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = ""); - - /// Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag - void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0); - - // Block entities: - void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID); - void AddChestEntity (cChestEntity * a_Entity); - void AddDispenserEntity(cDispenserEntity * a_Entity); - void AddDropperEntity (cDropperEntity * a_Entity); - void AddFurnaceEntity (cFurnaceEntity * a_Furnace); - void AddHopperEntity (cHopperEntity * a_Entity); - void AddJukeboxEntity (cJukeboxEntity * a_Jukebox); - void AddNoteEntity (cNoteEntity * a_Note); - void AddSignEntity (cSignEntity * a_Sign); - - // Entities: - void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName); - void AddBoatEntity (cBoat * a_Boat); - void AddFallingBlockEntity(cFallingBlock * a_FallingBlock); - void AddMinecartEntity (cMinecart * a_Minecart); - void AddMonsterEntity (cMonster * a_Monster); - void AddPickupEntity (cPickup * a_Pickup); - void AddProjectileEntity (cProjectileEntity * a_Projectile); - - void AddMinecartChestContents(cMinecartWithChest * a_Minecart); - - // cChunkDataSeparateCollector overrides: - virtual bool LightIsValid(bool a_IsLightValid) override; - virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override; - virtual void Entity(cEntity * a_Entity) override; - virtual void BlockEntity(cBlockEntity * a_Entity) override; -} ; // class cNBTChunkSerializer - - - - diff --git a/source/WorldStorage/WSSAnvil.cpp b/source/WorldStorage/WSSAnvil.cpp deleted file mode 100644 index b2e104a78..000000000 --- a/source/WorldStorage/WSSAnvil.cpp +++ /dev/null @@ -1,1555 +0,0 @@ - -// WSSAnvil.cpp - -// Implements the cWSSAnvil class representing the Anvil world storage scheme - -#include "Globals.h" -#include "WSSAnvil.h" -#include "NBTChunkSerializer.h" -#include "../World.h" -#include "zlib.h" -#include "../BlockID.h" -#include "../BlockEntities/ChestEntity.h" -#include "../BlockEntities/DispenserEntity.h" -#include "../BlockEntities/DropperEntity.h" -#include "../BlockEntities/FurnaceEntity.h" -#include "../BlockEntities/HopperEntity.h" -#include "../BlockEntities/JukeboxEntity.h" -#include "../BlockEntities/NoteEntity.h" -#include "../BlockEntities/SignEntity.h" -#include "../Item.h" -#include "../ItemGrid.h" -#include "../StringCompression.h" -#include "FastNBT.h" -#include "../Mobs/Monster.h" -#include "../Entities/Boat.h" -#include "../Entities/FallingBlock.h" -#include "../Entities/Minecart.h" -#include "../Entities/Pickup.h" -#include "../Entities/ProjectileEntity.h" - - - - - -/** If defined, the BlockSkyLight values will be copied over to BlockLight upon chunk saving, -thus making skylight visible in Minutor's Lighting mode -*/ -// #define DEBUG_SKYLIGHT - -/** Maximum number of MCA files that are cached in memory. -Since only the header is actually in the memory, this number can be high, but still, each file means an OS FS handle. -*/ -#define MAX_MCA_FILES 32 - -/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities -#define CHUNK_INFLATE_MAX 256 KiB - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWSSAnvil: - -cWSSAnvil::cWSSAnvil(cWorld * a_World) : - super(a_World) -{ - // Create a level.dat file for mapping tools, if it doesn't already exist: - AString fnam; - Printf(fnam, "%s/level.dat", a_World->GetName().c_str()); - if (!cFile::Exists(fnam)) - { - cFastNBTWriter Writer; - Writer.BeginCompound(""); - Writer.AddInt("SpawnX", (int)(a_World->GetSpawnX())); - Writer.AddInt("SpawnY", (int)(a_World->GetSpawnY())); - Writer.AddInt("SpawnZ", (int)(a_World->GetSpawnZ())); - Writer.EndCompound(); - Writer.Finish(); - - #ifdef _DEBUG - cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); - ASSERT(TestParse.IsValid()); - #endif // _DEBUG - - gzFile gz = gzopen((FILE_IO_PREFIX + fnam).c_str(), "wb"); - if (gz != NULL) - { - gzwrite(gz, Writer.GetResult().data(), Writer.GetResult().size()); - } - gzclose(gz); - } -} - - - - - -cWSSAnvil::~cWSSAnvil() -{ - cCSLock Lock(m_CS); - for (cMCAFiles::iterator itr = m_Files.begin(); itr != m_Files.end(); ++itr) - { - delete *itr; - } // for itr - m_Files[] -} - - - - - -bool cWSSAnvil::LoadChunk(const cChunkCoords & a_Chunk) -{ - AString ChunkData; - if (!GetChunkData(a_Chunk, ChunkData)) - { - // The reason for failure is already printed in GetChunkData() - return false; - } - - return LoadChunkFromData(a_Chunk, ChunkData); -} - - - - - -bool cWSSAnvil::SaveChunk(const cChunkCoords & a_Chunk) -{ - AString ChunkData; - if (!SaveChunkToData(a_Chunk, ChunkData)) - { - LOGWARNING("Cannot serialize chunk [%d, %d] into data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); - return false; - } - if (!SetChunkData(a_Chunk, ChunkData)) - { - LOGWARNING("Cannot store chunk [%d, %d] data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); - return false; - } - - // Everything successful - return true; -} - - - - - -bool cWSSAnvil::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) -{ - cCSLock Lock(m_CS); - cMCAFile * File = LoadMCAFile(a_Chunk); - if (File == NULL) - { - return false; - } - return File->GetChunkData(a_Chunk, a_Data); -} - - - - - -bool cWSSAnvil::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) -{ - cCSLock Lock(m_CS); - cMCAFile * File = LoadMCAFile(a_Chunk); - if (File == NULL) - { - return false; - } - return File->SetChunkData(a_Chunk, a_Data); -} - - - - - -cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) -{ - // ASSUME m_CS is locked - ASSERT(m_CS.IsLocked()); - - const int RegionX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32); - const int RegionZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32); - ASSERT(a_Chunk.m_ChunkX - RegionX * 32 >= 0); - ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 >= 0); - ASSERT(a_Chunk.m_ChunkX - RegionX * 32 < 32); - ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 < 32); - - // Is it already cached? - for (cMCAFiles::iterator itr = m_Files.begin(); itr != m_Files.end(); ++itr) - { - if (((*itr) != NULL) && ((*itr)->GetRegionX() == RegionX) && ((*itr)->GetRegionZ() == RegionZ)) - { - // Move the file to front and return it: - cMCAFile * f = *itr; - if (itr != m_Files.begin()) - { - m_Files.erase(itr); - m_Files.push_front(f); - } - return f; - } - } - - // Load it anew: - AString FileName; - Printf(FileName, "%s/region", m_World->GetName().c_str()); - cFile::CreateFolder(FILE_IO_PREFIX + FileName); - AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ); - cMCAFile * f = new cMCAFile(FileName, RegionX, RegionZ); - if (f == NULL) - { - return NULL; - } - m_Files.push_front(f); - - // If there are too many MCA files cached, delete the last one used: - if (m_Files.size() > MAX_MCA_FILES) - { - delete m_Files.back(); - m_Files.pop_back(); - } - return f; -} - - - - - -bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data) -{ - // Decompress the data: - char Uncompressed[CHUNK_INFLATE_MAX]; - z_stream strm; - strm.zalloc = (alloc_func)NULL; - strm.zfree = (free_func)NULL; - strm.opaque = NULL; - inflateInit(&strm); - strm.next_out = (Bytef *)Uncompressed; - strm.avail_out = sizeof(Uncompressed); - strm.next_in = (Bytef *)a_Data.data(); - strm.avail_in = a_Data.size(); - int res = inflate(&strm, Z_FINISH); - inflateEnd(&strm); - if (res != Z_STREAM_END) - { - return false; - } - - // Parse the NBT data: - cParsedNBT NBT(Uncompressed, strm.total_out); - if (!NBT.IsValid()) - { - // NBT Parsing failed - return false; - } - - // Load the data from NBT: - return LoadChunkFromNBT(a_Chunk, NBT); -} - - - - - -bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data) -{ - cFastNBTWriter Writer; - if (!SaveChunkToNBT(a_Chunk, Writer)) - { - LOGWARNING("Cannot save chunk [%d, %d] to NBT", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); - return false; - } - Writer.Finish(); - - CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data); - return true; -} - - - - - -bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT) -{ - // The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data) - cChunkDef::BlockTypes BlockTypes; - cChunkDef::BlockNibbles MetaData; - cChunkDef::BlockNibbles BlockLight; - cChunkDef::BlockNibbles SkyLight; - - memset(BlockTypes, E_BLOCK_AIR, sizeof(BlockTypes)); - memset(MetaData, 0, sizeof(MetaData)); - memset(SkyLight, 0xff, sizeof(SkyLight)); // By default, data not present in the NBT means air, which means full skylight - memset(BlockLight, 0x00, sizeof(BlockLight)); - - // Load the blockdata, blocklight and skylight: - int Level = a_NBT.FindChildByName(0, "Level"); - if (Level < 0) - { - return false; - } - int Sections = a_NBT.FindChildByName(Level, "Sections"); - if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound)) - { - return false; - } - for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child)) - { - int y = 0; - int SectionY = a_NBT.FindChildByName(Child, "Y"); - if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte)) - { - continue; - } - y = a_NBT.GetByte(SectionY); - if ((y < 0) || (y > 15)) - { - continue; - } - CopyNBTData(a_NBT, Child, "Blocks", (char *)&(BlockTypes[y * 4096]), 4096); - CopyNBTData(a_NBT, Child, "Data", (char *)&(MetaData[y * 2048]), 2048); - CopyNBTData(a_NBT, Child, "SkyLight", (char *)&(SkyLight[y * 2048]), 2048); - CopyNBTData(a_NBT, Child, "BlockLight", (char *)&(BlockLight[y * 2048]), 2048); - } // for itr - LevelSections[] - - // Load the biomes from NBT, if present and valid. First try MCS-style, then Vanilla-style: - cChunkDef::BiomeMap BiomeMap; - cChunkDef::BiomeMap * Biomes = LoadBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "MCSBiomes")); - if (Biomes == NULL) - { - // MCS-style biomes not available, load vanilla-style: - Biomes = LoadVanillaBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "Biomes")); - } - - // Load the entities from NBT: - cEntityList Entities; - cBlockEntityList BlockEntities; - LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities")); - LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"), BlockTypes, MetaData); - - bool IsLightValid = (a_NBT.FindChildByName(Level, "MCSIsLightValid") > 0); - - /* - // Uncomment this block for really cool stuff :) - // DEBUG magic: Invert the underground, so that we can see the MC generator in action :) - bool ShouldInvert[cChunkDef::Width * cChunkDef::Width]; - memset(ShouldInvert, 0, sizeof(ShouldInvert)); - for (int y = cChunkDef::Height - 1; y >= 0; y--) - { - for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) - { - int Index = cChunkDef::MakeIndexNoCheck(x, y, z); - if (ShouldInvert[x + cChunkDef::Width * z]) - { - BlockTypes[Index] = (BlockTypes[Index] == E_BLOCK_AIR) ? E_BLOCK_STONE : E_BLOCK_AIR; - } - else - { - switch (BlockTypes[Index]) - { - case E_BLOCK_AIR: - case E_BLOCK_LEAVES: - { - // nothing needed - break; - } - default: - { - ShouldInvert[x + cChunkDef::Width * z] = true; - } - } - BlockTypes[Index] = E_BLOCK_AIR; - } - } - } // for y - //*/ - - m_World->SetChunkData( - a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, - BlockTypes, MetaData, - IsLightValid ? BlockLight : NULL, - IsLightValid ? SkyLight : NULL, - NULL, Biomes, - Entities, BlockEntities, - false - ); - return true; -} - - - - -void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length) -{ - int Child = a_NBT.FindChildByName(a_Tag, a_ChildName); - if ((Child >= 0) && (a_NBT.GetType(Child) == TAG_ByteArray) && (a_NBT.GetDataLength(Child) == a_Length)) - { - memcpy(a_Destination, a_NBT.GetData(Child), a_Length); - } -} - - - - - -bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer) -{ - a_Writer.BeginCompound("Level"); - a_Writer.AddInt("xPos", a_Chunk.m_ChunkX); - a_Writer.AddInt("zPos", a_Chunk.m_ChunkZ); - cNBTChunkSerializer Serializer(a_Writer); - if (!m_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer)) - { - LOGWARNING("Cannot get chunk [%d, %d] data for NBT saving", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); - return false; - } - Serializer.Finish(); // Close NBT tags - - // Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray): - if (Serializer.m_BiomesAreValid) - { - a_Writer.AddByteArray("Biomes", (const char *)(Serializer.m_VanillaBiomes), ARRAYCOUNT(Serializer.m_VanillaBiomes)); - a_Writer.AddIntArray ("MCSBiomes", (const int *)(Serializer.m_Biomes), ARRAYCOUNT(Serializer.m_Biomes)); - } - - // Save blockdata: - a_Writer.BeginList("Sections", TAG_Compound); - int SliceSizeBlock = cChunkDef::Width * cChunkDef::Width * 16; - int SliceSizeNibble = SliceSizeBlock / 2; - const char * BlockTypes = (const char *)(Serializer.m_BlockTypes); - const char * BlockMetas = (const char *)(Serializer.m_BlockMetas); - #ifdef DEBUG_SKYLIGHT - const char * BlockLight = (const char *)(Serializer.m_BlockSkyLight); - #else - const char * BlockLight = (const char *)(Serializer.m_BlockLight); - #endif - const char * BlockSkyLight = (const char *)(Serializer.m_BlockSkyLight); - for (int Y = 0; Y < 16; Y++) - { - a_Writer.BeginCompound(""); - a_Writer.AddByteArray("Blocks", BlockTypes + Y * SliceSizeBlock, SliceSizeBlock); - a_Writer.AddByteArray("Data", BlockMetas + Y * SliceSizeNibble, SliceSizeNibble); - a_Writer.AddByteArray("SkyLight", BlockSkyLight + Y * SliceSizeNibble, SliceSizeNibble); - a_Writer.AddByteArray("BlockLight", BlockLight + Y * SliceSizeNibble, SliceSizeNibble); - a_Writer.AddByte("Y", (unsigned char)Y); - a_Writer.EndCompound(); - } - a_Writer.EndList(); // "Sections" - - // Store the information that the lighting is valid. - // For compatibility reason, the default is "invalid" (missing) - this means older data is re-lighted upon loading. - if (Serializer.IsLightValid()) - { - a_Writer.AddByte("MCSIsLightValid", 1); - } - - a_Writer.EndCompound(); // "Level" - return true; -} - - - - - -cChunkDef::BiomeMap * cWSSAnvil::LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx) -{ - if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_ByteArray)) - { - return NULL; - } - if (a_NBT.GetDataLength(a_TagIdx) != 16 * 16) - { - // The biomes stored don't match in size - return NULL; - } - const unsigned char * VanillaBiomeData = (const unsigned char *)(a_NBT.GetData(a_TagIdx)); - for (int i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++) - { - if ((VanillaBiomeData)[i] == 0xff) - { - // Unassigned biomes - return NULL; - } - (*a_BiomeMap)[i] = (EMCSBiome)(VanillaBiomeData[i]); - } - return a_BiomeMap; -} - - - - - -cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx) -{ - if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_IntArray)) - { - return NULL; - } - if (a_NBT.GetDataLength(a_TagIdx) != sizeof(*a_BiomeMap)) - { - // The biomes stored don't match in size - return NULL; - } - const int * BiomeData = (const int *)(a_NBT.GetData(a_TagIdx)); - for (int i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++) - { - (*a_BiomeMap)[i] = (EMCSBiome)(ntohl(BiomeData[i])); - if ((*a_BiomeMap)[i] == 0xff) - { - // Unassigned biomes - return NULL; - } - } - return a_BiomeMap; -} - - - - - -void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List)) - { - return; - } - - for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) - { - if (a_NBT.GetType(Child) != TAG_Compound) - { - continue; - } - int sID = a_NBT.FindChildByName(Child, "id"); - if (sID < 0) - { - continue; - } - LoadEntityFromNBT(a_Entities, a_NBT, Child, a_NBT.GetData(sID), a_NBT.GetDataLength(sID)); - } // for Child - a_NBT[] -} - - - - - -void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) -{ - if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List)) - { - return; - } - - for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) - { - if (a_NBT.GetType(Child) != TAG_Compound) - { - continue; - } - int sID = a_NBT.FindChildByName(Child, "id"); - if (sID < 0) - { - continue; - } - if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0) - { - LoadChestFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0) - { - LoadDropperFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0) - { - LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas); - } - else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0) - { - LoadHopperFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0) - { - LoadNoteFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "RecordPlayer", a_NBT.GetDataLength(sID)) == 0) - { - LoadJukeboxFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Sign", a_NBT.GetDataLength(sID)) == 0) - { - LoadSignFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0) - { - LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child); - } - // TODO: Other block entities - } // for Child - tag children -} - - - - - -bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx) -{ - int ID = a_NBT.FindChildByName(a_TagIdx, "id"); - if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short)) - { - return false; - } - a_Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID)); - - int Damage = a_NBT.FindChildByName(a_TagIdx, "Damage"); - if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short)) - { - return false; - } - a_Item.m_ItemDamage = a_NBT.GetShort(Damage); - - int Count = a_NBT.FindChildByName(a_TagIdx, "Count"); - if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte)) - { - return false; - } - a_Item.m_ItemCount = a_NBT.GetByte(Count); - - // Find the "tag" tag, used for enchantments and other extra data - int TagTag = a_NBT.FindChildByName(a_TagIdx, "tag"); - if (TagTag <= 0) - { - // No extra data - return true; - } - - // Load enchantments: - const char * EnchName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; - int EnchTag = a_NBT.FindChildByName(TagTag, EnchName); - if (EnchTag > 0) - { - a_Item.m_Enchantments.ParseFromNBT(a_NBT, EnchTag); - } - - return true; -} - - - - - -void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int a_SlotOffset) -{ - int NumSlots = a_ItemGrid.GetNumSlots(); - for (int Child = a_NBT.GetFirstChild(a_ItemsTagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) - { - int SlotTag = a_NBT.FindChildByName(Child, "Slot"); - if ((SlotTag < 0) || (a_NBT.GetType(SlotTag) != TAG_Byte)) - { - continue; - } - int SlotNum = (int)(a_NBT.GetByte(SlotTag)) - a_SlotOffset; - if ((SlotNum < 0) || (SlotNum >= NumSlots)) - { - // SlotNum outside of the range - continue; - } - cItem Item; - if (LoadItemFromNBT(Item, a_NBT, Child)) - { - a_ItemGrid.SetSlot(SlotNum, Item); - } - } // for itr - ItemDefs[] -} - - - - - -void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); - if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) - { - return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this - } - std::auto_ptr Chest(new cChestEntity(x, y, z, m_World)); - LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items); - a_BlockEntities.push_back(Chest.release()); -} - - - - - -void cWSSAnvil::LoadDispenserFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); - if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) - { - return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this - } - std::auto_ptr Dispenser(new cDispenserEntity(x, y, z, m_World)); - LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items); - a_BlockEntities.push_back(Dispenser.release()); -} - - - - - -void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); - if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) - { - return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this - } - std::auto_ptr Dropper(new cDropperEntity(x, y, z, m_World)); - LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items); - a_BlockEntities.push_back(Dropper.release()); -} - - - - - -void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) -{ - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); - if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) - { - return; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this - } - - // Convert coords to relative: - int RelX = x; - int RelZ = z; - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(RelX, y, RelZ, ChunkX, ChunkZ); - - // Create the furnace entity, with proper BlockType and BlockMeta info: - BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, y, RelZ); - NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, y, RelZ); - std::auto_ptr Furnace(new cFurnaceEntity(x, y, z, BlockType, BlockMeta, m_World)); - - // Load slots: - for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) - { - int Slot = a_NBT.FindChildByName(Child, "Slot"); - if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte)) - { - continue; - } - cItem Item; - if (LoadItemFromNBT(Item, a_NBT, Child)) - { - Furnace->SetSlot(a_NBT.GetByte(Slot), Item); - } - } // for itr - ItemDefs[] - - // Load burn time: - int BurnTime = a_NBT.FindChildByName(a_TagIdx, "BurnTime"); - if (BurnTime >= 0) - { - Int16 bt = a_NBT.GetShort(BurnTime); - // Anvil doesn't store the time that the fuel can burn. We simply "reset" the current value to be the 100% - Furnace->SetBurnTimes(bt, 0); - } - - // Load cook time: - int CookTime = a_NBT.FindChildByName(a_TagIdx, "CookTime"); - if (CookTime >= 0) - { - Int16 ct = a_NBT.GetShort(CookTime); - // Anvil doesn't store the time that an item takes to cook. We simply use the default - 10 seconds (200 ticks) - Furnace->SetCookTimes(200, ct); - } - - // Restart cooking: - Furnace->ContinueCooking(); - a_BlockEntities.push_back(Furnace.release()); -} - - - - - -void cWSSAnvil::LoadHopperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); - if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) - { - return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this - } - std::auto_ptr Hopper(new cHopperEntity(x, y, z, m_World)); - LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items); - a_BlockEntities.push_back(Hopper.release()); -} - - - - - -void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - std::auto_ptr Jukebox(new cJukeboxEntity(x, y, z, m_World)); - int Record = a_NBT.FindChildByName(a_TagIdx, "Record"); - if (Record >= 0) - { - Jukebox->SetRecord(a_NBT.GetInt(Record)); - } - a_BlockEntities.push_back(Jukebox.release()); -} - - - - - -void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - std::auto_ptr Note(new cNoteEntity(x, y, z, m_World)); - int note = a_NBT.FindChildByName(a_TagIdx, "note"); - if (note >= 0) - { - Note->SetPitch(a_NBT.GetByte(note)); - } - a_BlockEntities.push_back(Note.release()); -} - - - - - -void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - std::auto_ptr Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World)); - - int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1"); - if (currentLine >= 0) - { - Sign->SetLine(0, a_NBT.GetString(currentLine)); - } - - currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2"); - if (currentLine >= 0) - { - Sign->SetLine(1, a_NBT.GetString(currentLine)); - } - - currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3"); - if (currentLine >= 0) - { - Sign->SetLine(2, a_NBT.GetString(currentLine)); - } - - currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4"); - if (currentLine >= 0) - { - Sign->SetLine(3, a_NBT.GetString(currentLine)); - } - - a_BlockEntities.push_back(Sign.release()); -} - - - - - -void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength) -{ - if (strncmp(a_IDTag, "Boat", a_IDTagLength) == 0) - { - LoadBoatFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "FallingBlock", a_IDTagLength) == 0) - { - LoadFallingBlockFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "Minecart", a_IDTagLength) == 0) - { - // It is a minecart, old style, find out the type: - int TypeTag = a_NBT.FindChildByName(a_EntityTagIdx, "Type"); - if ((TypeTag < 0) || (a_NBT.GetType(TypeTag) != TAG_Int)) - { - return; - } - switch (a_NBT.GetInt(TypeTag)) - { - case 0: LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Rideable minecart - case 1: LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with chest - case 2: LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with furnace - case 3: LoadMinecartTFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with TNT - case 4: LoadMinecartHFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with Hopper - } - } - else if (strncmp(a_IDTag, "MinecartRideable", a_IDTagLength) == 0) - { - LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "MinecartChest", a_IDTagLength) == 0) - { - LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "MinecartFurnace", a_IDTagLength) == 0) - { - LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "MinecartTNT", a_IDTagLength) == 0) - { - LoadMinecartTFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "MinecartHopper", a_IDTagLength) == 0) - { - LoadMinecartHFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "Item", a_IDTagLength) == 0) - { - LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "Arrow", a_IDTagLength) == 0) - { - LoadArrowFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "Snowball", a_IDTagLength) == 0) - { - LoadSnowballFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "Egg", a_IDTagLength) == 0) - { - LoadEggFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "Fireball", a_IDTagLength) == 0) - { - LoadFireballFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "SmallFireball", a_IDTagLength) == 0) - { - LoadFireChargeFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - else if (strncmp(a_IDTag, "ThrownEnderpearl", a_IDTagLength) == 0) - { - LoadThrownEnderpearlFromNBT(a_Entities, a_NBT, a_EntityTagIdx); - } - // TODO: other entities -} - - - - - -void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Boat(new cBoat(0, 0, 0)); - if (!LoadEntityBaseFromNBT(*Boat.get(), a_NBT, a_TagIdx)) - { - return; - } - a_Entities.push_back(Boat.release()); -} - - - - - -void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - // TODO -} - - - - - -void cWSSAnvil::LoadMinecartRFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Minecart(new cEmptyMinecart(0, 0, 0)); - if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) - { - return; - } - a_Entities.push_back(Minecart.release()); -} - - - - - -void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); - if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) - { - return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this - } - std::auto_ptr Minecart(new cMinecartWithChest(0, 0, 0)); - if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) - { - return; - } - for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) - { - int Slot = a_NBT.FindChildByName(Child, "Slot"); - if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte)) - { - continue; - } - cItem Item; - if (LoadItemFromNBT(Item, a_NBT, Child)) - { - Minecart->SetSlot(a_NBT.GetByte(Slot), Item); - } - } // for itr - ItemDefs[] - a_Entities.push_back(Minecart.release()); -} - - - - - -void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Minecart(new cMinecartWithFurnace(0, 0, 0)); - if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) - { - return; - } - - // TODO: Load the Push and Fuel tags - - a_Entities.push_back(Minecart.release()); -} - - - - - -void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Minecart(new cMinecartWithTNT(0, 0, 0)); - if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) - { - return; - } - - // TODO: Everything to do with TNT carts - - a_Entities.push_back(Minecart.release()); -} - - - - - -void cWSSAnvil::LoadMinecartHFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Minecart(new cMinecartWithHopper(0, 0, 0)); - if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) - { - return; - } - - // TODO: Everything to do with hopper carts - - a_Entities.push_back(Minecart.release()); -} - - - - - -void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - int ItemTag = a_NBT.FindChildByName(a_TagIdx, "Item"); - if ((ItemTag < 0) || (a_NBT.GetType(ItemTag) != TAG_Compound)) - { - return; - } - cItem Item; - if (!LoadItemFromNBT(Item, a_NBT, ItemTag)) - { - return; - } - std::auto_ptr Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false - if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx)) - { - return; - } - a_Entities.push_back(Pickup.release()); -} - - - - - -void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Arrow(new cArrowEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); - if (!LoadProjectileBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx)) - { - return; - } - - // Load pickup state: - int PickupIdx = a_NBT.FindChildByName(a_TagIdx, "pickup"); - if (PickupIdx > 0) - { - Arrow->SetPickupState((cArrowEntity::ePickupState)a_NBT.GetByte(PickupIdx)); - } - else - { - // Try the older "player" tag: - int PlayerIdx = a_NBT.FindChildByName(a_TagIdx, "player"); - if (PlayerIdx > 0) - { - Arrow->SetPickupState((a_NBT.GetByte(PlayerIdx) == 0) ? cArrowEntity::psNoPickup : cArrowEntity::psInSurvivalOrCreative); - } - } - - // Load damage: - int DamageIdx = a_NBT.FindChildByName(a_TagIdx, "damage"); - if (DamageIdx > 0) - { - Arrow->SetDamageCoeff(a_NBT.GetDouble(DamageIdx)); - } - - // Store the new arrow in the entities list: - a_Entities.push_back(Arrow.release()); -} - - - - - -void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Snowball(new cThrownSnowballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); - if (!LoadProjectileBaseFromNBT(*Snowball.get(), a_NBT, a_TagIdx)) - { - return; - } - - // Store the new snowball in the entities list: - a_Entities.push_back(Snowball.release()); -} - - - - - -void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Egg(new cThrownEggEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); - if (!LoadProjectileBaseFromNBT(*Egg.get(), a_NBT, a_TagIdx)) - { - return; - } - - // Store the new egg in the entities list: - a_Entities.push_back(Egg.release()); -} - - - - - -void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Fireball(new cGhastFireballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); - if (!LoadProjectileBaseFromNBT(*Fireball.get(), a_NBT, a_TagIdx)) - { - return; - } - - // Store the new fireball in the entities list: - a_Entities.push_back(Fireball.release()); -} - - - - - -void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr FireCharge(new cFireChargeEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); - if (!LoadProjectileBaseFromNBT(*FireCharge.get(), a_NBT, a_TagIdx)) - { - return; - } - - // Store the new FireCharge in the entities list: - a_Entities.push_back(FireCharge.release()); -} - - - - - -void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - std::auto_ptr Enderpearl(new cThrownEnderPearlEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); - if (!LoadProjectileBaseFromNBT(*Enderpearl.get(), a_NBT, a_TagIdx)) - { - return; - } - - // Store the new enderpearl in the entities list: - a_Entities.push_back(Enderpearl.release()); -} - - - - - -bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx) -{ - double Pos[3]; - if (!LoadDoublesListFromNBT(Pos, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Pos"))) - { - return false; - } - a_Entity.SetPosition(Pos[0], Pos[1], Pos[2]); - - double Speed[3]; - if (!LoadDoublesListFromNBT(Speed, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Motion"))) - { - return false; - } - a_Entity.SetSpeed(Speed[0], Speed[1], Speed[2]); - - double Rotation[3]; - if (!LoadDoublesListFromNBT(Rotation, 2, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Rotation"))) - { - return false; - } - a_Entity.SetRotation(Rotation[0]); - a_Entity.SetRoll (Rotation[1]); - - return true; -} - - - - - -bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx) -{ - if (!LoadEntityBaseFromNBT(a_Entity, a_NBT, a_TagIdx)) - { - return false; - } - - bool IsInGround = false; - int InGroundIdx = a_NBT.FindChildByName(a_TagIdx, "inGround"); - if (InGroundIdx > 0) - { - IsInGround = (a_NBT.GetByte(InGroundIdx) != 0); - } - a_Entity.SetIsInGround(IsInGround); - - // TODO: Load inTile, TileCoords - - return true; -} - - - - - -bool cWSSAnvil::LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx) -{ - if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List) || (a_NBT.GetChildrenType(a_TagIdx) != TAG_Double)) - { - return false; - } - int idx = 0; - for (int Tag = a_NBT.GetFirstChild(a_TagIdx); (Tag > 0) && (idx < a_NumDoubles); Tag = a_NBT.GetNextSibling(Tag), ++idx) - { - a_Doubles[idx] = a_NBT.GetDouble(Tag); - } // for Tag - PosTag[] - return (idx == a_NumDoubles); // Did we read enough doubles? -} - - - - - -bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z) -{ - int x = a_NBT.FindChildByName(a_TagIdx, "x"); - if ((x < 0) || (a_NBT.GetType(x) != TAG_Int)) - { - return false; - } - int y = a_NBT.FindChildByName(a_TagIdx, "y"); - if ((y < 0) || (a_NBT.GetType(y) != TAG_Int)) - { - return false; - } - int z = a_NBT.FindChildByName(a_TagIdx, "z"); - if ((z < 0) || (a_NBT.GetType(z) != TAG_Int)) - { - return false; - } - a_X = a_NBT.GetInt(x); - a_Y = a_NBT.GetInt(y); - a_Z = a_NBT.GetInt(z); - return true; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWSSAnvil::cMCAFile: - -cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ) : - m_RegionX(a_RegionX), - m_RegionZ(a_RegionZ), - m_FileName(a_FileName) -{ -} - - - - - -bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading) -{ - if (m_File.IsOpen()) - { - // Already open - return true; - } - - if (a_IsForReading) - { - if (!cFile::Exists(m_FileName)) - { - // We want to read and the file doesn't exist. Fail. - return false; - } - } - - if (!m_File.Open(m_FileName, cFile::fmReadWrite)) - { - // The file failed to open - return false; - } - - // Load the header: - if (m_File.Read(m_Header, sizeof(m_Header)) != sizeof(m_Header)) - { - // Cannot read the header - perhaps the file has just been created? - // Try writing a NULL header (both chunk offsets and timestamps): - memset(m_Header, 0, sizeof(m_Header)); - if ( - (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Real header - chunk offsets - (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) // Bogus data for the chunk timestamps - ) - { - LOGWARNING("Cannot process MCA header in file \"%s\", chunks in that file will be lost", m_FileName.c_str()); - m_File.Close(); - return false; - } - } - return true; -} - - - - - -bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) -{ - if (!OpenFile(true)) - { - return false; - } - - int LocalX = a_Chunk.m_ChunkX % 32; - if (LocalX < 0) - { - LocalX = 32 + LocalX; - } - int LocalZ = a_Chunk.m_ChunkZ % 32; - if (LocalZ < 0) - { - LocalZ = 32 + LocalZ; - } - unsigned ChunkLocation = ntohl(m_Header[LocalX + 32 * LocalZ]); - unsigned ChunkOffset = ChunkLocation >> 8; - - m_File.Seek(ChunkOffset * 4096); - - int ChunkSize = 0; - if (m_File.Read(&ChunkSize, 4) != 4) - { - return false; - } - ChunkSize = ntohl(ChunkSize); - char CompressionType = 0; - if (m_File.Read(&CompressionType, 1) != 1) - { - return false; - } - if (CompressionType != 2) - { - // Chunk is in an unknown compression - return false; - } - ChunkSize--; - - // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly - a_Data.assign(ChunkSize, '\0'); - return (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize); -} - - - - - -bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) -{ - if (!OpenFile(false)) - { - LOGWARNING("Cannot save chunk [%d, %d], opening file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); - return false; - } - - int LocalX = a_Chunk.m_ChunkX % 32; - if (LocalX < 0) - { - LocalX = 32 + LocalX; - } - int LocalZ = a_Chunk.m_ChunkZ % 32; - if (LocalZ < 0) - { - LocalZ = 32 + LocalZ; - } - - unsigned ChunkSector = FindFreeLocation(LocalX, LocalZ, a_Data); - - // Store the chunk data: - m_File.Seek(ChunkSector * 4096); - unsigned ChunkSize = htonl(a_Data.size() + 1); - if (m_File.Write(&ChunkSize, 4) != 4) - { - LOGWARNING("Cannot save chunk [%d, %d], writing(1) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); - return false; - } - char CompressionType = 2; - if (m_File.Write(&CompressionType, 1) != 1) - { - LOGWARNING("Cannot save chunk [%d, %d], writing(2) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); - return false; - } - if (m_File.Write(a_Data.data(), a_Data.size()) != (int)(a_Data.size())) - { - LOGWARNING("Cannot save chunk [%d, %d], writing(3) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); - return false; - } - - // Store the header: - ChunkSize = (a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number - ASSERT(ChunkSize < 256); - m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize); - if (m_File.Seek(0) < 0) - { - LOGWARNING("Cannot save chunk [%d, %d], seeking in file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); - return false; - } - if (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) - { - LOGWARNING("Cannot save chunk [%d, %d], writing header to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); - return false; - } - - return true; -} - - - - - -unsigned cWSSAnvil::cMCAFile::FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data) -{ - // See if it fits the current location: - unsigned ChunkLocation = ntohl(m_Header[a_LocalX + 32 * a_LocalZ]); - unsigned ChunkLen = ChunkLocation & 0xff; - if (a_Data.size() + MCA_CHUNK_HEADER_LENGTH <= (ChunkLen * 4096)) - { - return ChunkLocation >> 8; - } - - // Doesn't fit, append to the end of file (we're wasting a lot of space, TODO: fix this later) - unsigned MaxLocation = 2 << 8; // Minimum sector is #2 - after the headers - for (int i = 0; i < ARRAYCOUNT(m_Header); i++) - { - ChunkLocation = ntohl(m_Header[i]); - ChunkLocation = ChunkLocation + ((ChunkLocation & 0xff) << 8); // Add the number of sectors used; don't care about the 4th byte - if (MaxLocation < ChunkLocation) - { - MaxLocation = ChunkLocation; - } - } // for i - m_Header[] - return MaxLocation >> 8; -} - - - - diff --git a/source/WorldStorage/WSSAnvil.h b/source/WorldStorage/WSSAnvil.h deleted file mode 100644 index 7685d2236..000000000 --- a/source/WorldStorage/WSSAnvil.h +++ /dev/null @@ -1,184 +0,0 @@ - -// WSSAnvil.h - -// Interfaces to the cWSSAnvil class representing the Anvil world storage scheme - - - - -#pragma once - -#include "WorldStorage.h" -#include "FastNBT.h" - - - - - -// fwd: ItemGrid.h -class cItemGrid; - -class cProjectileEntity; - - - - - -enum -{ - /// Maximum number of chunks in an MCA file - also the count of the header items - MCA_MAX_CHUNKS = 32 * 32, - - /// The MCA header is 8 KiB - MCA_HEADER_SIZE = MCA_MAX_CHUNKS * 8, - - /// There are 5 bytes of header in front of each chunk - MCA_CHUNK_HEADER_LENGTH = 5, -} ; - - - - - -class cWSSAnvil : - public cWSSchema -{ - typedef cWSSchema super; - -public: - - cWSSAnvil(cWorld * a_World); - virtual ~cWSSAnvil(); - -protected: - - class cMCAFile - { - public: - - cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ); - - bool GetChunkData (const cChunkCoords & a_Chunk, AString & a_Data); - bool SetChunkData (const cChunkCoords & a_Chunk, const AString & a_Data); - bool EraseChunkData(const cChunkCoords & a_Chunk); - - int GetRegionX (void) const {return m_RegionX; } - int GetRegionZ (void) const {return m_RegionZ; } - const AString & GetFileName(void) const {return m_FileName; } - - protected: - - int m_RegionX; - int m_RegionZ; - cFile m_File; - AString m_FileName; - - // The header, copied from the file so we don't have to seek to it all the time - // First 1024 entries are chunk locations - the 3 + 1 byte sector-offset and sector-count - unsigned m_Header[MCA_MAX_CHUNKS]; - - // Chunk timestamps, following the chunk headers, are unused by MCS - - /// Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number. - unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data); - - /// Opens a MCA file either for a Read operation (fails if doesn't exist) or for a Write operation (creates new if not found) - bool OpenFile(bool a_IsForReading); - } ; - typedef std::list cMCAFiles; - - cCriticalSection m_CS; - cMCAFiles m_Files; // a MRU cache of MCA files - - /// Gets chunk data from the correct file; locks file CS as needed - bool GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data); - - /// Sets chunk data into the correct file; locks file CS as needed - bool SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data); - - /// Loads the chunk from the data (no locking needed) - bool LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data); - - /// Saves the chunk into datastream (no locking needed) - bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data); - - /// Loads the chunk from NBT data (no locking needed) - bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT); - - /// Saves the chunk into NBT data using a_Writer; returns true on success - bool SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer); - - /// Loads the chunk's biome map from vanilla-format; returns a_BiomeMap if biomes present and valid, NULL otherwise - cChunkDef::BiomeMap * LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); - - /// Loads the chunk's biome map from MCS format; returns a_BiomeMap if biomes present and valid, NULL otherwise - cChunkDef::BiomeMap * LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); - - /// Loads the chunk's entities from NBT data (a_Tag is the Level\\Entities list tag; may be -1) - void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_Tag); - - /// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1) - void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); - - /// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag - bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx); - - /** Loads contentents of an Items[] list tag into a cItemGrid - ItemGrid begins at the specified slot offset - Slots outside the ItemGrid range are ignored - */ - void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0); - - void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); - void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - - void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength); - - void LoadBoatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadFallingBlockFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadMinecartFFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadMinecartTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadSnowballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadEggFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadFireballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadFireChargeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - - /// Loads entity common data from the NBT compound; returns true if successful - bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx); - - /// Loads projectile common data from the NBT compound; returns true if successful - bool LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIx); - - /// Loads an array of doubles of the specified length from the specified NBT list tag a_TagIdx; returns true if successful - bool LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx); - - /// Helper function for extracting the X, Y, and Z int subtags of a NBT compound; returns true if successful - bool GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z); - - /// Gets the correct MCA file either from cache or from disk, manages the m_MCAFiles cache; assumes m_CS is locked - cMCAFile * LoadMCAFile(const cChunkCoords & a_Chunk); - - /// Copies a_Length bytes of data from the specified NBT Tag's Child into the a_Destination buffer - void CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length); - - // cWSSchema overrides: - virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; - virtual bool SaveChunk(const cChunkCoords & a_Chunk) override; - virtual const AString GetName(void) const override {return "anvil"; } -} ; - - - - diff --git a/source/WorldStorage/WSSCompact.cpp b/source/WorldStorage/WSSCompact.cpp deleted file mode 100644 index 694f3ed1d..000000000 --- a/source/WorldStorage/WSSCompact.cpp +++ /dev/null @@ -1,1009 +0,0 @@ - -// WSSCompact.cpp - -// Interfaces to the cWSSCompact class representing the "compact" storage schema (PAK-files) - -#include "Globals.h" -#include "WSSCompact.h" -#include "../World.h" -#include "zlib.h" -#include -#include "../StringCompression.h" -#include "../BlockEntities/ChestEntity.h" -#include "../BlockEntities/DispenserEntity.h" -#include "../BlockEntities/FurnaceEntity.h" -#include "../BlockEntities/JukeboxEntity.h" -#include "../BlockEntities/NoteEntity.h" -#include "../BlockEntities/SignEntity.h" - - - - - -#pragma pack(push, 1) -/// The chunk header, as stored in the file: -struct cWSSCompact::sChunkHeader -{ - int m_ChunkX; - int m_ChunkZ; - int m_CompressedSize; - int m_UncompressedSize; -} ; -#pragma pack(pop) - - - - - -/// The maximum number of PAK files that are cached -const int MAX_PAK_FILES = 16; - -/// The maximum number of unsaved chunks before the cPAKFile saves them to disk -const int MAX_DIRTY_CHUNKS = 16; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cJsonChunkSerializer: - -cJsonChunkSerializer::cJsonChunkSerializer(void) : - m_HasJsonData(false) -{ -} - - - - - -void cJsonChunkSerializer::Entity(cEntity * a_Entity) -{ - // TODO: a_Entity->SaveToJson(m_Root); -} - - - - - -void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity) -{ - const char * SaveInto = NULL; - switch (a_BlockEntity->GetBlockType()) - { - case E_BLOCK_CHEST: SaveInto = "Chests"; break; - case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break; - case E_BLOCK_DROPPER: SaveInto = "Droppers"; break; - case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break; - case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break; - case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break; - case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break; - case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break; - - default: - { - ASSERT(!"Unhandled blocktype in BlockEntities list while saving to JSON"); - break; - } - } // switch (BlockEntity->GetBlockType()) - if (SaveInto == NULL) - { - return; - } - - Json::Value val; - a_BlockEntity->SaveToJson(val); - m_Root[SaveInto].append(val); - m_HasJsonData = true; -} - - - - - -bool cJsonChunkSerializer::LightIsValid(bool a_IsLightValid) -{ - if (!a_IsLightValid) - { - return false; - } - m_Root["IsLightValid"] = true; - m_HasJsonData = true; - return true; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWSSCompact: - -cWSSCompact::~cWSSCompact() -{ - for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr) - { - delete *itr; - } -} - - - - - -bool cWSSCompact::LoadChunk(const cChunkCoords & a_Chunk) -{ - AString ChunkData; - int UncompressedSize = 0; - if (!GetChunkData(a_Chunk, UncompressedSize, ChunkData)) - { - // The reason for failure is already printed in GetChunkData() - return false; - } - - return LoadChunkFromData(a_Chunk, UncompressedSize, ChunkData, m_World); -} - - - - - -bool cWSSCompact::SaveChunk(const cChunkCoords & a_Chunk) -{ - cCSLock Lock(m_CS); - - cPAKFile * f = LoadPAKFile(a_Chunk); - if (f == NULL) - { - // For some reason we couldn't locate the file - LOG("Cannot locate a proper PAK file for chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); - return false; - } - return f->SaveChunk(a_Chunk, m_World); -} - - - - - -cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkCoords & a_Chunk) -{ - // ASSUMES that m_CS has been locked - - // We need to retain this weird conversion code, because some edge chunks are in the wrong PAK file - const int LayerX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32); - const int LayerZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32); - - // Is it already cached? - for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr) - { - if (((*itr) != NULL) && ((*itr)->GetLayerX() == LayerX) && ((*itr)->GetLayerZ() == LayerZ)) - { - // Move the file to front and return it: - cPAKFile * f = *itr; - if (itr != m_PAKFiles.begin()) - { - m_PAKFiles.erase(itr); - m_PAKFiles.push_front(f); - } - return f; - } - } - - // Load it anew: - AString FileName; - Printf(FileName, "%s/X%i_Z%i.pak", m_World->GetName().c_str(), LayerX, LayerZ ); - cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ); - if (f == NULL) - { - return NULL; - } - m_PAKFiles.push_front(f); - - // If there are too many PAK files cached, delete the last one used: - if (m_PAKFiles.size() > MAX_PAK_FILES) - { - delete m_PAKFiles.back(); - m_PAKFiles.pop_back(); - } - return f; -} - - - - - -bool cWSSCompact::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data) -{ - cCSLock Lock(m_CS); - cPAKFile * f = LoadPAKFile(a_Chunk); - if (f == NULL) - { - return false; - } - return f->GetChunkData(a_Chunk, a_UncompressedSize, a_Data); -} - - - - - -/* -// TODO: Rewrite saving to use the same principles as loading -bool cWSSCompact::SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data) -{ - cCSLock Lock(m_CS); - cPAKFile * f = LoadPAKFile(a_Chunk); - if (f == NULL) - { - return false; - } - return f->SetChunkData(a_Chunk, a_UncompressedSize, a_Data); -} -*/ - - - - - -bool cWSSCompact::EraseChunkData(const cChunkCoords & a_Chunk) -{ - cCSLock Lock(m_CS); - cPAKFile * f = LoadPAKFile(a_Chunk); - if (f == NULL) - { - return false; - } - return f->EraseChunkData(a_Chunk); -} - - - - - -void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World) -{ - // Load chests - Json::Value AllChests = a_Value.get("Chests", Json::nullValue); - if (!AllChests.empty()) - { - for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr ) - { - Json::Value & Chest = *itr; - cChestEntity * ChestEntity = new cChestEntity(0,0,0, a_World); - if (!ChestEntity->LoadFromJson( Chest ) ) - { - LOGERROR("ERROR READING CHEST FROM JSON!" ); - delete ChestEntity; - } - else - { - a_BlockEntities.push_back( ChestEntity ); - } - } // for itr - AllChests[] - } - - // Load dispensers - Json::Value AllDispensers = a_Value.get("Dispensers", Json::nullValue); - if( !AllDispensers.empty() ) - { - for( Json::Value::iterator itr = AllDispensers.begin(); itr != AllDispensers.end(); ++itr ) - { - Json::Value & Dispenser = *itr; - cDispenserEntity * DispenserEntity = new cDispenserEntity(0,0,0, a_World); - if( !DispenserEntity->LoadFromJson( Dispenser ) ) - { - LOGERROR("ERROR READING DISPENSER FROM JSON!" ); - delete DispenserEntity; - } - else - { - a_BlockEntities.push_back( DispenserEntity ); - } - } // for itr - AllDispensers[] - } - - // Load furnaces - Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue); - if( !AllFurnaces.empty() ) - { - for( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr ) - { - Json::Value & Furnace = *itr; - // TODO: The block type and meta aren't correct, there's no way to get them here - cFurnaceEntity * FurnaceEntity = new cFurnaceEntity(0, 0, 0, E_BLOCK_FURNACE, 0, a_World); - if (!FurnaceEntity->LoadFromJson(Furnace)) - { - LOGERROR("ERROR READING FURNACE FROM JSON!" ); - delete FurnaceEntity; - } - else - { - a_BlockEntities.push_back(FurnaceEntity); - } - } // for itr - AllFurnaces[] - } - - // Load signs - Json::Value AllSigns = a_Value.get("Signs", Json::nullValue); - if( !AllSigns.empty() ) - { - for( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr ) - { - Json::Value & Sign = *itr; - cSignEntity * SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0,0,0, a_World); - if ( !SignEntity->LoadFromJson( Sign ) ) - { - LOGERROR("ERROR READING SIGN FROM JSON!" ); - delete SignEntity; - } - else - { - a_BlockEntities.push_back( SignEntity ); - } - } // for itr - AllSigns[] - } - - // Load note blocks - Json::Value AllNotes = a_Value.get("Notes", Json::nullValue); - if( !AllNotes.empty() ) - { - for( Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr ) - { - Json::Value & Note = *itr; - cNoteEntity * NoteEntity = new cNoteEntity(0, 0, 0, a_World); - if ( !NoteEntity->LoadFromJson( Note ) ) - { - LOGERROR("ERROR READING NOTE BLOCK FROM JSON!" ); - delete NoteEntity; - } - else - { - a_BlockEntities.push_back( NoteEntity ); - } - } // for itr - AllNotes[] - } - - // Load jukeboxes - Json::Value AllJukeboxes = a_Value.get("Jukeboxes", Json::nullValue); - if( !AllJukeboxes.empty() ) - { - for( Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr ) - { - Json::Value & Jukebox = *itr; - cJukeboxEntity * JukeboxEntity = new cJukeboxEntity(0, 0, 0, a_World); - if ( !JukeboxEntity->LoadFromJson( Jukebox ) ) - { - LOGERROR("ERROR READING JUKEBOX FROM JSON!" ); - delete JukeboxEntity; - } - else - { - a_BlockEntities.push_back( JukeboxEntity ); - } - } // for itr - AllJukeboxes[] - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWSSCompact::cPAKFile - -#define READ(Var) \ - if (f.Read(&Var, sizeof(Var)) != sizeof(Var)) \ - { \ - LOGERROR("ERROR READING %s FROM FILE %s (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \ - return; \ - } - -cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ) : - m_FileName(a_FileName), - m_LayerX(a_LayerX), - m_LayerZ(a_LayerZ), - m_NumDirty(0), - m_ChunkVersion( CHUNK_VERSION ), // Init with latest version - m_PakVersion( PAK_VERSION ) -{ - cFile f; - if (!f.Open(m_FileName, cFile::fmRead)) - { - return; - } - - // Read headers: - READ(m_PakVersion); - if (m_PakVersion != 1) - { - LOGERROR("File \"%s\" is in an unknown pak format (%d)", m_FileName.c_str(), m_PakVersion); - return; - } - - READ(m_ChunkVersion); - switch( m_ChunkVersion ) - { - case 1: - m_ChunkSize.Set(16, 128, 16); - break; - case 2: - case 3: - m_ChunkSize.Set(16, 256, 16); - break; - default: - LOGERROR("File \"%s\" is in an unknown chunk format (%d)", m_FileName.c_str(), m_ChunkVersion); - return; - }; - - short NumChunks = 0; - READ(NumChunks); - - // Read chunk headers: - for (int i = 0; i < NumChunks; i++) - { - sChunkHeader * Header = new sChunkHeader; - READ(*Header); - m_ChunkHeaders.push_back(Header); - } // for i - chunk headers - - // Read chunk data: - if (f.ReadRestOfFile(m_DataContents) == -1) - { - LOGERROR("Cannot read file \"%s\" contents", m_FileName.c_str()); - return; - } - - if( m_ChunkVersion == 1 ) // Convert chunks to version 2 - { - UpdateChunk1To2(); - } -#if AXIS_ORDER == AXIS_ORDER_XZY - if( m_ChunkVersion == 2 ) // Convert chunks to version 3 - { - UpdateChunk2To3(); - } -#endif -} - - - - - -cWSSCompact::cPAKFile::~cPAKFile() -{ - if (m_NumDirty > 0) - { - SynchronizeFile(); - } - for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) - { - delete *itr; - } -} - - - - - -bool cWSSCompact::cPAKFile::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data) -{ - int ChunkX = a_Chunk.m_ChunkX; - int ChunkZ = a_Chunk.m_ChunkZ; - sChunkHeader * Header = NULL; - int Offset = 0; - for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) - { - if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ)) - { - Header = *itr; - break; - } - Offset += (*itr)->m_CompressedSize; - } - if ((Header == NULL) || (Offset + Header->m_CompressedSize > (int)m_DataContents.size())) - { - // Chunk not found / data invalid - return false; - } - - a_UncompressedSize = Header->m_UncompressedSize; - a_Data.assign(m_DataContents, Offset, Header->m_CompressedSize); - return true; -} - - - - - -bool cWSSCompact::cPAKFile::SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World) -{ - if (!SaveChunkToData(a_Chunk, a_World)) - { - return false; - } - if (m_NumDirty > MAX_DIRTY_CHUNKS) - { - SynchronizeFile(); - } - return true; -} - - - - - -void cWSSCompact::cPAKFile::UpdateChunk1To2() -{ - int Offset = 0; - AString NewDataContents; - int ChunksConverted = 0; - for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) - { - sChunkHeader * Header = *itr; - - if( ChunksConverted % 32 == 0 ) - { - LOGINFO("Updating \"%s\" version 1 to version 2: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() ); - } - ChunksConverted++; - - AString Data; - int UncompressedSize = Header->m_UncompressedSize; - Data.assign(m_DataContents, Offset, Header->m_CompressedSize); - Offset += Header->m_CompressedSize; - - // Crude data integrity check: - int ExpectedSize = (16*128*16)*2 + (16*128*16)/2; // For version 1 - if (UncompressedSize < ExpectedSize) - { - LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing", - Header->m_ChunkX, Header->m_ChunkZ, - UncompressedSize, ExpectedSize - ); - Offset += Header->m_CompressedSize; - continue; - } - - // Decompress the data: - AString UncompressedData; - { - int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize); - if (errorcode != Z_OK) - { - LOGERROR("Error %d decompressing data for chunk [%d, %d]", - errorcode, - Header->m_ChunkX, Header->m_ChunkZ - ); - Offset += Header->m_CompressedSize; - continue; - } - } - - if (UncompressedSize != (int)UncompressedData.size()) - { - LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]", - UncompressedSize, UncompressedData.size(), - Header->m_ChunkX, Header->m_ChunkZ - ); - Offset += Header->m_CompressedSize; - continue; - } - - - // Old version is 128 blocks high with YZX axis order - char ConvertedData[cChunkDef::BlockDataSize]; - int Index = 0; - unsigned int InChunkOffset = 0; - for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) - { - for( int y = 0; y < 128; ++y ) - { - ConvertedData[Index++] = UncompressedData[y + z * 128 + x * 128 * 16 + InChunkOffset]; - } - // Add 128 empty blocks after an old y column - memset(ConvertedData + Index, E_BLOCK_AIR, 128); - Index += 128; - } - InChunkOffset += (16 * 128 * 16); - for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Metadata - { - for( int y = 0; y < 64; ++y ) - { - ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset]; - } - memset(ConvertedData + Index, 0, 64); - Index += 64; - } - InChunkOffset += (16 * 128 * 16) / 2; - for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Block light - { - for( int y = 0; y < 64; ++y ) - { - ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset]; - } - memset(ConvertedData + Index, 0, 64); - Index += 64; - } - InChunkOffset += (16*128*16)/2; - for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Sky light - { - for( int y = 0; y < 64; ++y ) - { - ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset]; - } - memset(ConvertedData + Index, 0, 64); - Index += 64; - } - InChunkOffset += (16 * 128 * 16) / 2; - - AString Converted(ConvertedData, ARRAYCOUNT(ConvertedData)); - - // Add JSON data afterwards - if (UncompressedData.size() > InChunkOffset) - { - Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() ); - } - - // Re-compress data - AString CompressedData; - { - int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData); - if (errorcode != Z_OK) - { - LOGERROR("Error %d compressing data for chunk [%d, %d]", - errorcode, - Header->m_ChunkX, Header->m_ChunkZ - ); - continue; - } - } - - // Save into file's cache - Header->m_UncompressedSize = Converted.size(); - Header->m_CompressedSize = CompressedData.size(); - NewDataContents.append( CompressedData ); - } - - // Done converting - m_DataContents = NewDataContents; - m_ChunkVersion = 2; - SynchronizeFile(); - - LOGINFO("Updated \"%s\" version 1 to version 2", m_FileName.c_str() ); -} - - - - - -void cWSSCompact::cPAKFile::UpdateChunk2To3() -{ - int Offset = 0; - AString NewDataContents; - int ChunksConverted = 0; - for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) - { - sChunkHeader * Header = *itr; - - if( ChunksConverted % 32 == 0 ) - { - LOGINFO("Updating \"%s\" version 2 to version 3: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() ); - } - ChunksConverted++; - - AString Data; - int UncompressedSize = Header->m_UncompressedSize; - Data.assign(m_DataContents, Offset, Header->m_CompressedSize); - Offset += Header->m_CompressedSize; - - // Crude data integrity check: - const int ExpectedSize = (16*256*16)*2 + (16*256*16)/2; // For version 2 - if (UncompressedSize < ExpectedSize) - { - LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing", - Header->m_ChunkX, Header->m_ChunkZ, - UncompressedSize, ExpectedSize - ); - Offset += Header->m_CompressedSize; - continue; - } - - // Decompress the data: - AString UncompressedData; - { - int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize); - if (errorcode != Z_OK) - { - LOGERROR("Error %d decompressing data for chunk [%d, %d]", - errorcode, - Header->m_ChunkX, Header->m_ChunkZ - ); - Offset += Header->m_CompressedSize; - continue; - } - } - - if (UncompressedSize != (int)UncompressedData.size()) - { - LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]", - UncompressedSize, UncompressedData.size(), - Header->m_ChunkX, Header->m_ChunkZ - ); - Offset += Header->m_CompressedSize; - continue; - } - - char ConvertedData[ExpectedSize]; - memset(ConvertedData, 0, ExpectedSize); - - // Cannot use cChunk::MakeIndex because it might change again????????? - // For compatibility, use what we know is current - #define MAKE_2_INDEX( x, y, z ) ( y + (z * 256) + (x * 256 * 16) ) - #define MAKE_3_INDEX( x, y, z ) ( x + (z * 16) + (y * 16 * 16) ) - - unsigned int InChunkOffset = 0; - for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) // YZX Loop order is important, in 1.1 Y was first then Z then X - { - ConvertedData[ MAKE_3_INDEX(x, y, z) ] = UncompressedData[InChunkOffset]; - ++InChunkOffset; - } // for y, z, x - - - unsigned int index2 = 0; - for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) - { - ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4); - ++index2; - } - InChunkOffset += index2 / 2; - index2 = 0; - - for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) - { - ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4); - ++index2; - } - InChunkOffset += index2 / 2; - index2 = 0; - - for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) - { - ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4); - ++index2; - } - InChunkOffset += index2 / 2; - index2 = 0; - - AString Converted(ConvertedData, ExpectedSize); - - // Add JSON data afterwards - if (UncompressedData.size() > InChunkOffset) - { - Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() ); - } - - // Re-compress data - AString CompressedData; - { - int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData); - if (errorcode != Z_OK) - { - LOGERROR("Error %d compressing data for chunk [%d, %d]", - errorcode, - Header->m_ChunkX, Header->m_ChunkZ - ); - continue; - } - } - - // Save into file's cache - Header->m_UncompressedSize = Converted.size(); - Header->m_CompressedSize = CompressedData.size(); - NewDataContents.append( CompressedData ); - } - - // Done converting - m_DataContents = NewDataContents; - m_ChunkVersion = 3; - SynchronizeFile(); - - LOGINFO("Updated \"%s\" version 2 to version 3", m_FileName.c_str() ); -} - - - - - -bool cWSSCompact::LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World) -{ - // Crude data integrity check: - if (a_UncompressedSize < cChunkDef::BlockDataSize) - { - LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing", - a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, - a_UncompressedSize, cChunkDef::BlockDataSize - ); - EraseChunkData(a_Chunk); - return false; - } - - // Decompress the data: - AString UncompressedData; - int errorcode = UncompressString(a_Data.data(), a_Data.size(), UncompressedData, a_UncompressedSize); - if (errorcode != Z_OK) - { - LOGERROR("Error %d decompressing data for chunk [%d, %d]", - errorcode, - a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ - ); - return false; - } - - if (a_UncompressedSize != (int)UncompressedData.size()) - { - LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]", - a_UncompressedSize, UncompressedData.size(), - a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ - ); - return false; - } - - cEntityList Entities; - cBlockEntityList BlockEntities; - bool IsLightValid = false; - - if (a_UncompressedSize > cChunkDef::BlockDataSize) - { - Json::Value root; // will contain the root value after parsing. - Json::Reader reader; - if ( !reader.parse( UncompressedData.data() + cChunkDef::BlockDataSize, root, false ) ) - { - LOGERROR("Failed to parse trailing JSON in chunk [%d, %d]!", - a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ - ); - } - else - { - LoadEntitiesFromJson(root, Entities, BlockEntities, a_World); - IsLightValid = root.get("IsLightValid", false).asBool(); - } - } - - BLOCKTYPE * BlockData = (BLOCKTYPE *)UncompressedData.data(); - NIBBLETYPE * MetaData = (NIBBLETYPE *)(BlockData + cChunkDef::MetaOffset); - NIBBLETYPE * BlockLight = (NIBBLETYPE *)(BlockData + cChunkDef::LightOffset); - NIBBLETYPE * SkyLight = (NIBBLETYPE *)(BlockData + cChunkDef::SkyLightOffset); - - a_World->SetChunkData( - a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, - BlockData, MetaData, - IsLightValid ? BlockLight : NULL, - IsLightValid ? SkyLight : NULL, - NULL, NULL, - Entities, BlockEntities, - false - ); - - return true; -} - - - - - -bool cWSSCompact::cPAKFile::EraseChunkData(const cChunkCoords & a_Chunk) -{ - int ChunkX = a_Chunk.m_ChunkX; - int ChunkZ = a_Chunk.m_ChunkZ; - int Offset = 0; - for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) - { - if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ)) - { - m_DataContents.erase(Offset, (*itr)->m_CompressedSize); - delete *itr; - itr = m_ChunkHeaders.erase(itr); - return true; - } - Offset += (*itr)->m_CompressedSize; - } - - return false; -} - - - - - -bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World) -{ - // Serialize the chunk: - cJsonChunkSerializer Serializer; - if (!a_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer)) - { - // Chunk not valid - LOG("cWSSCompact: Trying to save chunk [%d, %d, %d] that has no data, ignoring request.", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); - return false; - } - - AString Data; - Data.assign((const char *)Serializer.GetBlockData(), cChunkDef::BlockDataSize); - if (Serializer.HasJsonData()) - { - AString JsonData; - Json::StyledWriter writer; - JsonData = writer.write(Serializer.GetRoot()); - Data.append(JsonData); - } - - // Compress the data: - AString CompressedData; - int errorcode = CompressString(Data.data(), Data.size(), CompressedData); - if ( errorcode != Z_OK ) - { - LOGERROR("Error %i compressing data for chunk [%d, %d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); - return false; - } - - // Erase any existing data for the chunk: - EraseChunkData(a_Chunk); - - // Save the header: - sChunkHeader * Header = new sChunkHeader; - if (Header == NULL) - { - LOGWARNING("Cannot create a new chunk header to save chunk [%d, %d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); - return false; - } - Header->m_CompressedSize = (int)CompressedData.size(); - Header->m_ChunkX = a_Chunk.m_ChunkX; - Header->m_ChunkZ = a_Chunk.m_ChunkZ; - Header->m_UncompressedSize = (int)Data.size(); - m_ChunkHeaders.push_back(Header); - - m_DataContents.append(CompressedData.data(), CompressedData.size()); - - m_NumDirty++; - return true; -} - - - - - -#define WRITE(Var) \ - if (f.Write(&Var, sizeof(Var)) != sizeof(Var)) \ - { \ - LOGERROR("cWSSCompact: ERROR writing %s to file \"%s\" (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \ - return; \ - } - -void cWSSCompact::cPAKFile::SynchronizeFile(void) -{ - cFile f; - if (!f.Open(m_FileName, cFile::fmWrite)) - { - LOGERROR("Cannot open PAK file \"%s\" for writing", m_FileName.c_str()); - return; - } - - WRITE(m_PakVersion); - WRITE(m_ChunkVersion); - short NumChunks = (short)m_ChunkHeaders.size(); - WRITE(NumChunks); - for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) - { - WRITE(**itr); - } - if (f.Write(m_DataContents.data(), m_DataContents.size()) != (int)m_DataContents.size()) - { - LOGERROR("cWSSCompact: ERROR writing chunk contents to file \"%s\" (line %d); file offset %d", m_FileName.c_str(), __LINE__, f.Tell()); - return; - } - m_NumDirty = 0; -} - - - - diff --git a/source/WorldStorage/WSSCompact.h b/source/WorldStorage/WSSCompact.h deleted file mode 100644 index e6a013eaf..000000000 --- a/source/WorldStorage/WSSCompact.h +++ /dev/null @@ -1,144 +0,0 @@ - -// WSSCompact.h - -// Interfaces to the cWSSCompact class representing the "Compact" storage schema (PAK-files) - - - - - -#pragma once -#ifndef WSSCOMPACT_H_INCLUDED -#define WSSCOMPACT_H_INCLUDED - -#include "WorldStorage.h" -#include "../Vector3i.h" - - - - - -/// Helper class for serializing a chunk into Json -class cJsonChunkSerializer : - public cChunkDataCollector -{ -public: - - cJsonChunkSerializer(void); - - Json::Value & GetRoot (void) {return m_Root; } - BLOCKTYPE * GetBlockData(void) {return (BLOCKTYPE *)m_BlockData; } - bool HasJsonData (void) const {return m_HasJsonData; } - -protected: - - // NOTE: block data is serialized into inherited cChunkDataCollector's m_BlockData[] array - - // Entities and BlockEntities are serialized to Json - Json::Value m_Root; - bool m_HasJsonData; - - // cChunkDataCollector overrides: - virtual void Entity (cEntity * a_Entity) override; - virtual void BlockEntity (cBlockEntity * a_Entity) override; - virtual bool LightIsValid (bool a_IsLightValid) override; -} ; - - - - - -class cWSSCompact : - public cWSSchema -{ -public: - cWSSCompact(cWorld * a_World) : cWSSchema(a_World) {} - virtual ~cWSSCompact(); - -protected: - - struct sChunkHeader; - typedef std::vector sChunkHeaders; - - /// Implements a cache for a single PAK file; implements lazy-write in order to be able to write multiple chunks fast - class cPAKFile - { - public: - - cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ); - ~cPAKFile(); - - bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data); - bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data); - bool EraseChunkData(const cChunkCoords & a_Chunk); - - bool SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World); - - int GetLayerX(void) const {return m_LayerX; } - int GetLayerZ(void) const {return m_LayerZ; } - - static const int PAK_VERSION = 1; -#if AXIS_ORDER == AXIS_ORDER_XZY - static const int CHUNK_VERSION = 3; -#elif AXIS_ORDER == AXIS_ORDER_YZX - static const int CHUNK_VERSION = 2; -#endif - protected: - - AString m_FileName; - int m_LayerX; - int m_LayerZ; - - sChunkHeaders m_ChunkHeaders; - AString m_DataContents; // Data contents of the file, cached - - int m_NumDirty; // Number of chunks that were written into m_DataContents but not into the file - - Vector3i m_ChunkSize; // Is related to m_ChunkVersion - char m_ChunkVersion; - char m_PakVersion; - - bool SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World); // Saves the chunk to m_DataContents, updates headers and m_NumDirty - void SynchronizeFile(void); // Writes m_DataContents along with the headers to file, resets m_NumDirty - - void UpdateChunk1To2(void); // Height from 128 to 256 - void UpdateChunk2To3(void); // Axis order from YZX to XZY - } ; - - typedef std::list cPAKFiles; - - cCriticalSection m_CS; - cPAKFiles m_PAKFiles; // A MRU cache of PAK files - - /// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache - cPAKFile * LoadPAKFile(const cChunkCoords & a_Chunk); - - /// Gets chunk data from the correct file; locks CS as needed - bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data); - - /// Sets chunk data to the correct file; locks CS as needed - bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data); - - /// Erases chunk data from the correct file; locks CS as needed - bool EraseChunkData(const cChunkCoords & a_Chunk); - - /// Loads the chunk from the data (no locking needed) - bool LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World); - - void LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World); - - // cWSSchema overrides: - virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; - virtual bool SaveChunk(const cChunkCoords & a_Chunk) override; - virtual const AString GetName(void) const override {return "compact"; } -} ; - - - - - -#endif // WSSCOMPACT_H_INCLUDED - - - - diff --git a/source/WorldStorage/WorldStorage.cpp b/source/WorldStorage/WorldStorage.cpp deleted file mode 100644 index f290ec128..000000000 --- a/source/WorldStorage/WorldStorage.cpp +++ /dev/null @@ -1,409 +0,0 @@ - -// WorldStorage.cpp - -// Implements the cWorldStorage class representing the chunk loading / saving thread - -// To add a new storage schema, implement a cWSSchema descendant and add it to cWorldStorage::InitSchemas() - -#include "Globals.h" -#include "WorldStorage.h" -#include "WSSCompact.h" -#include "WSSAnvil.h" -#include "../World.h" -#include "../Generating/ChunkGenerator.h" -#include "../Entities/Entity.h" -#include "../BlockEntities/BlockEntity.h" - - - - - -/// If a chunk with this Y coord is de-queued, it is a signal to emit the saved-all message (cWorldStorage::QueueSavedMessage()) -#define CHUNK_Y_MESSAGE 2 - - - - - -/// Example storage schema - forgets all chunks ;) -class cWSSForgetful : - public cWSSchema -{ -public: - cWSSForgetful(cWorld * a_World) : cWSSchema(a_World) {} - -protected: - // cWSSchema overrides: - virtual bool LoadChunk(const cChunkCoords & a_Chunk) override {return false; } - virtual bool SaveChunk(const cChunkCoords & a_Chunk) override {return true; } - virtual const AString GetName(void) const override {return "forgetful"; } -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorldStorage: - -cWorldStorage::cWorldStorage(void) : - super("cWorldStorage"), - m_World(NULL), - m_SaveSchema(NULL) -{ -} - - - - - -cWorldStorage::~cWorldStorage() -{ - for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) - { - delete *itr; - } // for itr - m_Schemas[] - m_LoadQueue.clear(); - m_SaveQueue.clear(); -} - - - - - -bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName) -{ - m_World = a_World; - m_StorageSchemaName = a_StorageSchemaName; - InitSchemas(); - - return super::Start(); -} - - - - - -void cWorldStorage::Stop(void) -{ - WaitForFinish(); -} - - - - - -void cWorldStorage::WaitForFinish(void) -{ - LOG("Waiting for the world storage to finish saving"); - - { - // Cancel all loading requests: - cCSLock Lock(m_CSQueues); - m_LoadQueue.clear(); - } - - // Wait for the saving to finish: - WaitForQueuesEmpty(); - - // Wait for the thread to finish: - m_ShouldTerminate = true; - m_Event.Set(); - m_evtRemoved.Set(); // Wake up anybody waiting in the WaitForQueuesEmpty() method - super::Wait(); - LOG("World storage thread finished"); -} - - - - - -void cWorldStorage::WaitForQueuesEmpty(void) -{ - cCSLock Lock(m_CSQueues); - while (!m_ShouldTerminate && (!m_LoadQueue.empty() || !m_SaveQueue.empty())) - { - cCSUnlock Unlock(Lock); - m_evtRemoved.Wait(); - } -} - - - - - -int cWorldStorage::GetLoadQueueLength(void) -{ - cCSLock Lock(m_CSQueues); - return (int)m_LoadQueue.size(); -} - - - - - -int cWorldStorage::GetSaveQueueLength(void) -{ - cCSLock Lock(m_CSQueues); - return (int)m_SaveQueue.size(); -} - - - - - -void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) -{ - // Queues the chunk for loading; if not loaded, the chunk will be generated - { - cCSLock Lock(m_CSQueues); - - // Check if already in the queue: - for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_Generate == a_Generate)) - { - return; - } - } - m_LoadQueue.push_back(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate)); - } - - m_Event.Set(); -} - - - - - -void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - { - cCSLock Lock(m_CSQueues); - m_SaveQueue.remove (cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); // Don't add twice - m_SaveQueue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); - } - m_Event.Set(); -} - - - - - -void cWorldStorage::QueueSavedMessage(void) -{ - // Pushes a special coord pair into the queue, signalizing a message instead: - { - cCSLock Lock(m_CSQueues); - m_SaveQueue.push_back(cChunkCoords(0, CHUNK_Y_MESSAGE, 0)); - } - m_Event.Set(); -} - - - - - -void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSQueues); - for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr) - { - if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkY != a_ChunkY) || (itr->m_ChunkZ != a_ChunkZ)) - { - continue; - } - m_LoadQueue.erase(itr); - Lock.Unlock(); - m_evtRemoved.Set(); - return; - } // for itr - m_LoadQueue[] -} - - - - - -void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk) -{ - { - cCSLock Lock(m_CSQueues); - m_SaveQueue.remove(a_Chunk); - } - m_evtRemoved.Set(); -} - - - - - -void cWorldStorage::InitSchemas(void) -{ - // The first schema added is considered the default - m_Schemas.push_back(new cWSSAnvil (m_World)); - m_Schemas.push_back(new cWSSCompact (m_World)); - m_Schemas.push_back(new cWSSForgetful(m_World)); - // Add new schemas here - - if (NoCaseCompare(m_StorageSchemaName, "default") == 0) - { - m_SaveSchema = m_Schemas.front(); - return; - } - for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) - { - if (NoCaseCompare((*itr)->GetName(), m_StorageSchemaName) == 0) - { - m_SaveSchema = *itr; - return; - } - } // for itr - m_Schemas[] - - // Unknown schema selected, let the admin know: - LOGWARNING("Unknown storage schema name \"%s\". Using default (\"%s\"). Available schemas:", - m_StorageSchemaName.c_str(), m_SaveSchema->GetName().c_str() - ); - for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) - { - LOGWARNING("\t\"%s\"", (*itr)->GetName().c_str()); - } - m_SaveSchema = m_Schemas.front(); -} - - - - - -void cWorldStorage::Execute(void) -{ - while (!m_ShouldTerminate) - { - m_Event.Wait(); - - // Process both queues until they are empty again: - bool HasMore; - do - { - HasMore = false; - if (m_ShouldTerminate) - { - return; - } - - HasMore = LoadOneChunk(); - HasMore = HasMore | SaveOneChunk(); - m_evtRemoved.Set(); - } while (HasMore); - } -} - - - - - -bool cWorldStorage::LoadOneChunk(void) -{ - sChunkLoad ToLoad(0, 0, 0, false); - bool HasMore; - bool ShouldLoad = false; - { - cCSLock Lock(m_CSQueues); - if (!m_LoadQueue.empty()) - { - ToLoad = m_LoadQueue.front(); - m_LoadQueue.pop_front(); - ShouldLoad = true; - } - HasMore = !m_LoadQueue.empty(); - } - - if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ)) - { - if (ToLoad.m_Generate) - { - // The chunk couldn't be loaded, generate it: - m_World->GetGenerator().QueueGenerateChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ); - } - else - { - // TODO: Notify the world that the load has failed: - // m_World->ChunkLoadFailed(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ); - } - } - return HasMore; -} - - - - - -bool cWorldStorage::SaveOneChunk(void) -{ - cChunkCoords Save(0, 0, 0); - bool HasMore; - bool ShouldSave = false; - { - cCSLock Lock(m_CSQueues); - if (!m_SaveQueue.empty()) - { - Save = m_SaveQueue.front(); - m_SaveQueue.pop_front(); - ShouldSave = true; - } - HasMore = !m_SaveQueue.empty(); - } - if (Save.m_ChunkY == CHUNK_Y_MESSAGE) - { - LOGINFO("Saved all chunks in world %s", m_World->GetName().c_str()); - return HasMore; - } - if (ShouldSave && m_World->IsChunkValid(Save.m_ChunkX, Save.m_ChunkZ)) - { - m_World->MarkChunkSaving(Save.m_ChunkX, Save.m_ChunkZ); - if (m_SaveSchema->SaveChunk(Save)) - { - m_World->MarkChunkSaved(Save.m_ChunkX, Save.m_ChunkZ); - } - } - return HasMore; -} - - - - - -bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - if (m_World->IsChunkValid(a_ChunkX, a_ChunkZ)) - { - // Already loaded (can happen, since the queue is async) - return true; - } - - cChunkCoords Coords(a_ChunkX, a_ChunkY, a_ChunkZ); - - // First try the schema that is used for saving - if (m_SaveSchema->LoadChunk(Coords)) - { - return true; - } - - // If it didn't have the chunk, try all the other schemas: - for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) - { - if (((*itr) != m_SaveSchema) && (*itr)->LoadChunk(Coords)) - { - return true; - } - } - - // Notify the chunk owner that the chunk failed to load (sets cChunk::m_HasLoadFailed to true): - m_World->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); - - return false; -} - - - - - diff --git a/source/WorldStorage/WorldStorage.h b/source/WorldStorage/WorldStorage.h deleted file mode 100644 index bf8dbd3d5..000000000 --- a/source/WorldStorage/WorldStorage.h +++ /dev/null @@ -1,135 +0,0 @@ - -// WorldStorage.h - -// Interfaces to the cWorldStorage class representing the chunk loading / saving thread -// This class decides which storage schema to use for saving; it queries all available schemas for loading -// Also declares the base class for all storage schemas, cWSSchema -// Helper serialization class cJsonChunkSerializer is declared as well - - - - - -#pragma once -#ifndef WORLDSTORAGE_H_INCLUDED -#define WORLDSTORAGE_H_INCLUDED - -#include "../ChunkDef.h" -#include "../OSSupport/IsThread.h" -#include - - - - - -// fwd: -class cWorld; - - - - - -/// Interface that all the world storage schemas need to implement -class cWSSchema abstract -{ -public: - cWSSchema(cWorld * a_World) : m_World(a_World) {} - virtual ~cWSSchema() {} // Force the descendants' destructors to be virtual - - virtual bool LoadChunk(const cChunkCoords & a_Chunk) = 0; - virtual bool SaveChunk(const cChunkCoords & a_Chunk) = 0; - virtual const AString GetName(void) const = 0; - -protected: - - cWorld * m_World; -} ; - -typedef std::list cWSSchemaList; - - - - - -/// The actual world storage class -class cWorldStorage : - public cIsThread -{ - typedef cIsThread super; - -public: - - cWorldStorage(void); - ~cWorldStorage(); - - void QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate); // Queues the chunk for loading; if not loaded, the chunk will be generated if a_Generate is true - void QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Signals that a message should be output to the console when all the chunks have been saved - void QueueSavedMessage(void); - - /// Loads the chunk specified; returns true on success, false on failure - bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - void UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void UnqueueSave(const cChunkCoords & a_Chunk); - - bool Start(cWorld * a_World, const AString & a_StorageSchemaName); // Hide the cIsThread's Start() method, we need to provide args - void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event - void WaitForFinish(void); - void WaitForQueuesEmpty(void); - - int GetLoadQueueLength(void); - int GetSaveQueueLength(void); - -protected: - - struct sChunkLoad - { - int m_ChunkX; - int m_ChunkY; - int m_ChunkZ; - bool m_Generate; // If true, the chunk will be generated if it cannot be loaded - - sChunkLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ), m_Generate(a_Generate) {} - } ; - - typedef std::list sChunkLoadQueue; - - cWorld * m_World; - AString m_StorageSchemaName; - - // Both queues are locked by the same CS - cCriticalSection m_CSQueues; - sChunkLoadQueue m_LoadQueue; - cChunkCoordsList m_SaveQueue; - - cEvent m_Event; // Set when there's any addition to the queues - cEvent m_evtRemoved; // Set when an item has been removed from the queue, either by the worker thread or the Unqueue methods - - /// All the storage schemas (all used for loading) - cWSSchemaList m_Schemas; - - /// The one storage schema used for saving - cWSSchema * m_SaveSchema; - - void InitSchemas(void); - - virtual void Execute(void) override; - - /// Loads one chunk from the queue (if any queued); returns true if there are more chunks in the load queue - bool LoadOneChunk(void); - - /// Saves one chunk from the queue (if any queued); returns true if there are more chunks in the save queue - bool SaveOneChunk(void); -} ; - - - - - -#endif // WORLDSTORAGE_H_INCLUDED - - - - diff --git a/source/XMLParser.h b/source/XMLParser.h deleted file mode 100644 index f492d1a5d..000000000 --- a/source/XMLParser.h +++ /dev/null @@ -1,701 +0,0 @@ - -// XMLParser.h - -// Interfaces to the CXMLParser class representing the base class for XML parsing - -// To use, derive a class from this base and override its OnStartElement(), OnEndElement() and OnCharacters() functions - - - - - -#pragma once - -#include "expat/expat.h" - - - - - -class CXMLParser -{ -public: - CXMLParser(void); - virtual ~CXMLParser(); - - // The actual parsing, may be called several times; the last time needs iIsFinal == true (-> flush) - int Parse(const char * iData, size_t iLength, bool iIsFinal = false); - -private: - // LibExpat stuff: - XML_Parser mParser; - - static void StartElementHandler(void * iContext, const XML_Char * iElement, const XML_Char ** iAttributes) - { - ((CXMLParser *)iContext)->OnStartElement(iElement, iAttributes); - } - - static void EndElementHandler (void * iContext, const XML_Char * iElement) - { - ((CXMLParser *)iContext)->OnEndElement(iElement); - } - - static void CharacterDataHandler (void * iContext, const XML_Char * iData, int iLength) - { - ((CXMLParser *)iContext)->OnCharacters(iData, iLength); - } - -protected: - virtual void OnStartElement(const XML_Char * iElement, const XML_Char ** iAttributes) = 0; - virtual void OnEndElement (const XML_Char * iElement) = 0; - virtual void OnCharacters (const XML_Char * iCharacters, int iLength) = 0; -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// The following template has been modified from code available at -// http://www.codeproject.com/Articles/1847/C-Wrappers-for-the-Expat-XML-Parser -// It uses templates to remove the virtual function call penalty (both size and speed) for each callback - -/* Usage: -1, Declare a subclass: - class CMyParser : public CExpatImpl -2, Declare handlers that you want in that subclass: - void CMyParser::OnEndElement(const XML_Char * iTagName); -3, Create an instance of your class: - CMyParser Parser; -4, Call Create(): - Parser.Create(NULL, NULL); -4, Call Parse(), repeatedly: - Parser.Parse(Buffer, Length); -*/ - -template -class CExpatImpl -{ - -// @access Constructors and destructors -public: - - // @cmember General constructor - - CExpatImpl () - { - m_p = NULL; - } - - // @cmember Destructor - - ~CExpatImpl () - { - Destroy (); - } - -// @access Parser creation and deletion methods -public: - - // @cmember Create a parser - - bool Create (const XML_Char * pszEncoding = NULL, const XML_Char * pszSep = NULL) - { - // Destroy the old parser - Destroy (); - - // If the encoding or seperator are empty, then NULL - if (pszEncoding != NULL && pszEncoding [0] == 0) - { - pszEncoding = NULL; - } - if (pszSep != NULL && pszSep [0] == 0) - { - pszSep = NULL; - } - - // Create the new parser - m_p = XML_ParserCreate_MM (pszEncoding, NULL, pszSep); - if (m_p == NULL) - { - return false; - } - - // Invoke the post create routine - _T * pThis = static_cast <_T *> (this); - pThis ->OnPostCreate (); - - // Set the user data used in callbacks - XML_SetUserData (m_p, (void *) this); - return true; - } - - // @cmember Destroy the parser - - void Destroy (void) - { - if (m_p != NULL) - { - XML_ParserFree (m_p); - } - m_p = NULL; - } - - - // @cmember Parse a block of data - - bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true) - { - assert (m_p != NULL); - return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0; - } - - // @cmember Parse internal buffer - - bool ParseBuffer (int nLength, bool fIsFinal = true) - { - assert (m_p != NULL); - return XML_ParseBuffer (m_p, nLength, fIsFinal) != 0; - } - - // @cmember Get the internal buffer - - void *GetBuffer (int nLength) - { - assert (m_p != NULL); - return XML_GetBuffer (m_p, nLength); - } - - -protected: - // Parser callback enable/disable methods: - - // @cmember Enable/Disable the start element handler - - void EnableStartElementHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : NULL); - } - - // @cmember Enable/Disable the end element handler - - void EnableEndElementHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : NULL); - } - - // @cmember Enable/Disable the element handlers - - void EnableElementHandler (bool fEnable = true) - { - assert (m_p != NULL); - EnableStartElementHandler (fEnable); - EnableEndElementHandler (fEnable); - } - - // @cmember Enable/Disable the character data handler - - void EnableCharacterDataHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : NULL); - } - - // @cmember Enable/Disable the processing instruction handler - - void EnableProcessingInstructionHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : NULL); - } - - // @cmember Enable/Disable the comment handler - - void EnableCommentHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetCommentHandler (m_p, fEnable ? CommentHandler : NULL); - } - - // @cmember Enable/Disable the start CDATA section handler - - void EnableStartCdataSectionHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : NULL); - } - - // @cmember Enable/Disable the end CDATA section handler - - void EnableEndCdataSectionHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : NULL); - } - - // @cmember Enable/Disable the CDATA section handlers - - void EnableCdataSectionHandler (bool fEnable = true) - { - assert (m_p != NULL); - EnableStartCdataSectionHandler (fEnable); - EnableEndCdataSectionHandler (fEnable); - } - - // @cmember Enable/Disable default handler - - void EnableDefaultHandler (bool fEnable = true, bool fExpand = true) - { - assert (m_p != NULL); - if (fExpand) - { - XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : NULL); - } - else - XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : NULL); - } - - // @cmember Enable/Disable external entity ref handler - - void EnableExternalEntityRefHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : NULL); - } - - // @cmember Enable/Disable unknown encoding handler - - void EnableUnknownEncodingHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : NULL); - } - - // @cmember Enable/Disable start namespace handler - - void EnableStartNamespaceDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : NULL); - } - - // @cmember Enable/Disable end namespace handler - - void EnableEndNamespaceDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : NULL); - } - - // @cmember Enable/Disable namespace handlers - - void EnableNamespaceDeclHandler (bool fEnable = true) - { - EnableStartNamespaceDeclHandler (fEnable); - EnableEndNamespaceDeclHandler (fEnable); - } - - // @cmember Enable/Disable the XML declaration handler - - void EnableXmlDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : NULL); - } - - // @cmember Enable/Disable the start DOCTYPE declaration handler - - void EnableStartDoctypeDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : NULL); - } - - // @cmember Enable/Disable the end DOCTYPE declaration handler - - void EnableEndDoctypeDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetEndDoctypeDeclHandler (m_p, - fEnable ? EndDoctypeDeclHandler : NULL); - } - - // @cmember Enable/Disable the DOCTYPE declaration handler - - void EnableDoctypeDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - EnableStartDoctypeDeclHandler (fEnable); - EnableEndDoctypeDeclHandler (fEnable); - } - -public: - // Parser error reporting methods - - // @cmember Get last error - - enum XML_Error GetErrorCode () - { - assert (m_p != NULL); - return XML_GetErrorCode (m_p); - } - - // @cmember Get the current byte index - - long GetCurrentByteIndex () - { - assert (m_p != NULL); - return XML_GetCurrentByteIndex (m_p); - } - - // @cmember Get the current line number - - int GetCurrentLineNumber () - { - assert (m_p != NULL); - return XML_GetCurrentLineNumber (m_p); - } - - // @cmember Get the current column number - - int GetCurrentColumnNumber () - { - assert (m_p != NULL); - return XML_GetCurrentColumnNumber (m_p); - } - - // @cmember Get the current byte count - - int GetCurrentByteCount () - { - assert (m_p != NULL); - return XML_GetCurrentByteCount (m_p); - } - - // @cmember Get the input context - - const char *GetInputContext (int *pnOffset, int *pnSize) - { - assert (m_p != NULL); - return XML_GetInputContext (m_p, pnOffset, pnSize); - } - - // @cmember Get last error string - - const XML_LChar *GetErrorString () - { - return XML_ErrorString (GetErrorCode ()); - } - - // @cmember Return the version string - - static const XML_LChar *GetExpatVersion () - { - return XML_ExpatVersion (); - } - - // @cmember Get the version information - - static void GetExpatVersion (int *pnMajor, int *pnMinor, int *pnMicro) - { - XML_expat_version v = XML_ExpatVersionInfo (); - if (pnMajor) - *pnMajor = v .major; - if (pnMinor) - *pnMinor = v .minor; - if (pnMicro) - *pnMicro = v .micro; - } - - // @cmember Get last error string - - static const XML_LChar *GetErrorString (enum XML_Error nError) - { - return XML_ErrorString (nError); - } - - - // Public handler methods: - // The template parameter should provide their own implementation for those handlers that they want - - // @cmember Start element handler - - void OnStartElement (const XML_Char *pszName, const XML_Char **papszAttrs) - { - return; - } - - // @cmember End element handler - - void OnEndElement (const XML_Char *pszName) - { - return; - } - - // @cmember Character data handler - - void OnCharacterData (const XML_Char *pszData, int nLength) - { - return; - } - - // @cmember Processing instruction handler - - void OnProcessingInstruction (const XML_Char *pszTarget, - const XML_Char *pszData) - { - return; - } - - // @cmember Comment handler - - void OnComment (const XML_Char *pszData) - { - return; - } - - // @cmember Start CDATA section handler - - void OnStartCdataSection () - { - return; - } - - // @cmember End CDATA section handler - - void OnEndCdataSection () - { - return; - } - - // @cmember Default handler - - void OnDefault (const XML_Char *pszData, int nLength) - { - return; - } - - // @cmember External entity ref handler - - bool OnExternalEntityRef (const XML_Char *pszContext, - const XML_Char *pszBase, const XML_Char *pszSystemID, - const XML_Char *pszPublicID) - { - return false; - } - - // @cmember Unknown encoding handler - - bool OnUnknownEncoding (const XML_Char *pszName, XML_Encoding *pInfo) - { - return false; - } - - // @cmember Start namespace declaration handler - - void OnStartNamespaceDecl (const XML_Char *pszPrefix, - const XML_Char *pszURI) - { - return; - } - - // @cmember End namespace declaration handler - - void OnEndNamespaceDecl (const XML_Char *pszPrefix) - { - return; - } - - // @cmember XML declaration handler - - void OnXmlDecl (const XML_Char *pszVersion, const XML_Char *pszEncoding, - bool fStandalone) - { - return; - } - - // @cmember Start DOCTYPE declaration handler - - void OnStartDoctypeDecl (const XML_Char *pszDoctypeName, - const XML_Char *pszSysID, const XML_Char *pszPubID, - bool fHasInternalSubset) - { - return; - } - - // @cmember End DOCTYPE declaration handler - - void OnEndDoctypeDecl () - { - return; - } - -// @access Protected methods -protected: - - // @cmember Handle any post creation - - void OnPostCreate () - { - } - -// @access Protected static methods -protected: - - // @cmember Start element handler wrapper - - static void __cdecl StartElementHandler (void *pUserData, - const XML_Char *pszName, const XML_Char **papszAttrs) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnStartElement (pszName, papszAttrs); - } - - // @cmember End element handler wrapper - - static void __cdecl EndElementHandler (void *pUserData, - const XML_Char *pszName) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnEndElement (pszName); - } - - // @cmember Character data handler wrapper - - static void __cdecl CharacterDataHandler (void *pUserData, - const XML_Char *pszData, int nLength) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnCharacterData (pszData, nLength); - } - - // @cmember Processing instruction handler wrapper - - static void __cdecl ProcessingInstructionHandler (void *pUserData, - const XML_Char *pszTarget, const XML_Char *pszData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnProcessingInstruction (pszTarget, pszData); - } - - // @cmember Comment handler wrapper - - static void __cdecl CommentHandler (void *pUserData, - const XML_Char *pszData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnComment (pszData); - } - - // @cmember Start CDATA section wrapper - - static void __cdecl StartCdataSectionHandler (void *pUserData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnStartCdataSection (); - } - - // @cmember End CDATA section wrapper - - static void __cdecl EndCdataSectionHandler (void *pUserData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnEndCdataSection (); - } - - // @cmember Default wrapper - - static void __cdecl DefaultHandler (void *pUserData, - const XML_Char *pszData, int nLength) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnDefault (pszData, nLength); - } - - // @cmember External entity ref wrapper - - static int __cdecl ExternalEntityRefHandler (void *pUserData, - const XML_Char *pszContext, const XML_Char *pszBase, - const XML_Char *pszSystemID, const XML_Char *pszPublicID) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - return pThis ->OnExternalEntityRef (pszContext, - pszBase, pszSystemID, pszPublicID) ? 1 : 0; - } - - // @cmember Unknown encoding wrapper - - static int __cdecl UnknownEncodingHandler (void * pUserData, const XML_Char * pszName, XML_Encoding * pInfo) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - return pThis ->OnUnknownEncoding (pszName, pInfo) ? 1 : 0; - } - - // @cmember Start namespace decl wrapper - - static void __cdecl StartNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix, const XML_Char * pszURI) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnStartNamespaceDecl (pszPrefix, pszURI); - } - - // @cmember End namespace decl wrapper - - static void __cdecl EndNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnEndNamespaceDecl (pszPrefix); - } - - // @cmember XML declaration wrapper - - static void __cdecl XmlDeclHandler (void *pUserData, const XML_Char *pszVersion, const XML_Char *pszEncoding, int nStandalone) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnXmlDecl (pszVersion, pszEncoding, nStandalone != 0); - } - - // @cmember Start Doctype declaration wrapper - - static void __cdecl StartDoctypeDeclHandler ( - void *pUserData, const XML_Char *pszDoctypeName, const XML_Char *pszSysID, - const XML_Char *pszPubID, int nHasInternalSubset - ) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnStartDoctypeDecl (pszDoctypeName, pszSysID, - pszPubID, nHasInternalSubset != 0); - } - - // @cmember End Doctype declaration wrapper - - static void __cdecl EndDoctypeDeclHandler (void *pUserData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnEndDoctypeDecl (); - } - - -protected: - - XML_Parser m_p; - - /// Returns the value of the specified attribute, if found; NULL otherwise - static const XML_Char * FindAttr(const XML_Char ** iAttrs, const XML_Char * iAttrToFind) - { - for (const XML_Char ** Attr = iAttrs; *Attr != NULL; Attr += 2) - { - if (strcmp(*Attr, iAttrToFind) == 0) - { - return *(Attr + 1); - } - } // for Attr - iAttrs[] - return NULL; - } -} ; - - - - diff --git a/source/lua5.1.dll b/source/lua5.1.dll deleted file mode 100644 index 515cf8b30..000000000 Binary files a/source/lua5.1.dll and /dev/null differ diff --git a/source/main.cpp b/source/main.cpp deleted file mode 100644 index 1f6aad24f..000000000 --- a/source/main.cpp +++ /dev/null @@ -1,197 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Root.h" - -#include //std::exception -#include //std::signal -#include //exit() - -#ifdef _MSC_VER - #include -#endif // _MSC_VER - - - - - -/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window -#define ENABLE_LEAK_FINDER - - - - - -#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) - #pragma warning(push) - #pragma warning(disable:4100) - #include "LeakFinder.h" - #pragma warning(pop) -#endif - - - - - - -void ShowCrashReport(int) -{ - std::signal(SIGSEGV, SIG_DFL); - - printf("\n\nMCServer has crashed!\n"); - - exit(-1); -} - - - - - -#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Windows 32-bit stuff: when the server crashes, create a "dump file" containing the callstack of each thread and some variables; let the user send us that crash file for analysis - -typedef BOOL (WINAPI *pMiniDumpWriteDump)( - HANDLE hProcess, - DWORD ProcessId, - HANDLE hFile, - MINIDUMP_TYPE DumpType, - PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - PMINIDUMP_CALLBACK_INFORMATION CallbackParam -); - -pMiniDumpWriteDump g_WriteMiniDump; // The function in dbghlp DLL that creates dump files - -char g_DumpFileName[MAX_PATH]; // Filename of the dump file; hes to be created before the dump handler kicks in -char g_ExceptionStack[128 * 1024]; // Substitute stack, just in case the handler kicks in because of "insufficient stack space" -MINIDUMP_TYPE g_DumpFlags = MiniDumpNormal; // By default dump only the stack and some helpers - - - - - -/** This function gets called just before the "program executed an illegal instruction and will be terminated" or similar. -Its purpose is to create the crashdump using the dbghlp DLLs -*/ -LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_ExceptionInfo) -{ - char * newStack = &g_ExceptionStack[sizeof(g_ExceptionStack)]; - char * oldStack; - - // Use the substitute stack: - // This code is the reason why we don't support 64-bit (yet) - _asm - { - mov oldStack, esp - mov esp, newStack - } - - MINIDUMP_EXCEPTION_INFORMATION ExcInformation; - ExcInformation.ThreadId = GetCurrentThreadId(); - ExcInformation.ExceptionPointers = a_ExceptionInfo; - ExcInformation.ClientPointers = 0; - - // Write the dump file: - HANDLE dumpFile = CreateFile(g_DumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - g_WriteMiniDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, g_DumpFlags, (a_ExceptionInfo) ? &ExcInformation : NULL, NULL, NULL); - CloseHandle(dumpFile); - - // Revert to old stack: - _asm - { - mov esp, oldStack - } - - return 0; -} - -#endif // _WIN32 && !_WIN64 - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// main: - -int main( int argc, char **argv ) -{ - (void)argc; - (void)argv; - - #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) - InitLeakFinder(); - #endif - - // Magic code to produce dump-files on Windows if the server crashes: - #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) - HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL"); - g_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(hDbgHelp, "MiniDumpWriteDump"); - if (g_WriteMiniDump != NULL) - { - _snprintf_s(g_DumpFileName, ARRAYCOUNT(g_DumpFileName), _TRUNCATE, "crash_mcs_%x.dmp", GetCurrentProcessId()); - SetUnhandledExceptionFilter(LastChanceExceptionFilter); - - // Parse arguments for minidump flags: - for (int i = 0; i < argc; i++) - { - if (_stricmp(argv[i], "/cdg") == 0) - { - // Add globals to the dump - g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithDataSegs); - } - else if (_stricmp(argv[i], "/cdf") == 0) - { - // Add full memory to the dump (HUUUGE file) - g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithFullMemory); - } - } // for i - argv[] - } - #endif // _WIN32 && !_WIN64 - // End of dump-file magic - - #if defined(_DEBUG) && defined(_MSC_VER) - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); - - // _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output) - // Only useful when the leak is in the same sequence all the time - // _CrtSetBreakAlloc(85950); - - #endif // _DEBUG && _MSC_VER - - #ifndef _DEBUG - std::signal(SIGSEGV, ShowCrashReport); - #endif - - // DEBUG: test the dumpfile creation: - // *((int *)0) = 0; - - #if !defined(ANDROID_NDK) - try - #endif - { - cRoot Root; - Root.Start(); - } - #if !defined(ANDROID_NDK) - catch( std::exception& e ) - { - LOGERROR("Standard exception: %s", e.what() ); - } - catch( ... ) - { - LOGERROR("Unknown exception!"); - } - #endif - - - #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) - DeinitLeakFinder(); - #endif - - return 0; -} - - - - diff --git a/source/md5/md5.cpp b/source/md5/md5.cpp deleted file mode 100644 index eae0fc3f2..000000000 --- a/source/md5/md5.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* MD5 - converted to C++ class by Frank Thilo (thilo@unix-ag.org) - for bzflag (http://www.bzflag.org) - - based on: - - md5.h and md5.c - reference implemantion of RFC 1321 - - Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - -*/ - -/* interface header */ -#include "md5.h" - -/* system implementation headers */ -#include - -#ifndef _WIN32 - #include -#endif - - - - - -// Constants for MD5Transform routine. -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - -/////////////////////////////////////////////// - -// F, G, H and I are basic MD5 functions. -inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) { - return x&y | ~x&z; -} - -inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) { - return x&z | y&~z; -} - -inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) { - return x^y^z; -} - -inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) { - return y ^ (x | ~z); -} - -// rotate_left rotates x left n bits. -inline MD5::uint4 MD5::rotate_left(uint4 x, int n) { - return (x << n) | (x >> (32-n)); -} - -// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. -// Rotation is separate from addition to prevent recomputation. -inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; -} - -inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + G(b,c,d) + x + ac, s) + b; -} - -inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + H(b,c,d) + x + ac, s) + b; -} - -inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + I(b,c,d) + x + ac, s) + b; -} - -////////////////////////////////////////////// - -// default ctor, just initailize -MD5::MD5() -{ - init(); -} - -////////////////////////////////////////////// - -// nifty shortcut ctor, compute MD5 for string and finalize it right away -MD5::MD5(const std::string &text) -{ - init(); - update(text.c_str(), text.length()); - finalize(); -} - -////////////////////////////// - -void MD5::init() -{ - finalized=false; - - count[0] = 0; - count[1] = 0; - - // load magic initialization constants. - state[0] = 0x67452301; - state[1] = 0xefcdab89; - state[2] = 0x98badcfe; - state[3] = 0x10325476; -} - -////////////////////////////// - -// decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. -void MD5::decode(uint4 output[], const uint1 input[], size_type len) -{ - for (unsigned int i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | - (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); -} - -////////////////////////////// - -// encodes input (uint4) into output (unsigned char). Assumes len is -// a multiple of 4. -void MD5::encode(uint1 output[], const uint4 input[], size_type len) -{ - for (size_type i = 0, j = 0; j < len; i++, j += 4) { - output[j] = input[i] & 0xff; - output[j+1] = (input[i] >> 8) & 0xff; - output[j+2] = (input[i] >> 16) & 0xff; - output[j+3] = (input[i] >> 24) & 0xff; - } -} - -////////////////////////////// - -// apply MD5 algo on a block -void MD5::transform(const uint1 block[blocksize]) -{ - uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - decode (x, block, blocksize); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - // Zeroize sensitive information. - memset(x, 0, sizeof x); -} - -////////////////////////////// - -// MD5 block update operation. Continues an MD5 message-digest -// operation, processing another message block -void MD5::update(const unsigned char input[], size_type length) -{ - // compute number of bytes mod 64 - size_type index = count[0] / 8 % blocksize; - - // Update number of bits - if ((count[0] += (length << 3)) < (length << 3)) - count[1]++; - count[1] += (length >> 29); - - // number of bytes we need to fill in buffer - size_type firstpart = 64 - index; - - size_type i; - - // transform as many times as possible. - if (length >= firstpart) - { - // fill buffer first, transform - memcpy(&buffer[index], input, firstpart); - transform(buffer); - - // transform chunks of blocksize (64 bytes) - for (i = firstpart; i + blocksize <= length; i += blocksize) - transform(&input[i]); - - index = 0; - } - else - i = 0; - - // buffer remaining input - memcpy(&buffer[index], &input[i], length-i); -} - -////////////////////////////// - -// for convenience provide a verson with signed char -void MD5::update(const char input[], size_type length) -{ - update((const unsigned char*)input, length); -} - -////////////////////////////// - -// MD5 finalization. Ends an MD5 message-digest operation, writing the -// the message digest and zeroizing the context. -MD5& MD5::finalize() -{ - static unsigned char padding[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - if (!finalized) { - // Save number of bits - unsigned char bits[8]; - encode(bits, count, 8); - - // pad out to 56 mod 64. - size_type index = count[0] / 8 % 64; - size_type padLen = (index < 56) ? (56 - index) : (120 - index); - update(padding, padLen); - - // Append length (before padding) - update(bits, 8); - - // Store state in digest - encode(digest, state, 16); - - // Zeroize sensitive information. - memset(buffer, 0, sizeof buffer); - memset(count, 0, sizeof count); - - finalized=true; - } - - return *this; -} - -////////////////////////////// - -// return hex representation of digest as string -std::string MD5::hexdigest() const -{ - if (!finalized) - return ""; - - char buf[33]; - for (int i=0; i<16; i++) - sprintf(buf+i*2, "%02x", digest[i]); - buf[32]=0; - - return std::string(buf); -} - -////////////////////////////// - -std::ostream& operator<<(std::ostream& out, MD5 md5) -{ - return out << md5.hexdigest(); -} - -////////////////////////////// - -std::string md5(const std::string & str) -{ - MD5 md5 = MD5(str); - - return md5.hexdigest(); -} diff --git a/source/md5/md5.h b/source/md5/md5.h deleted file mode 100644 index ad5ad5384..000000000 --- a/source/md5/md5.h +++ /dev/null @@ -1,93 +0,0 @@ -/* MD5 - converted to C++ class by Frank Thilo (thilo@unix-ag.org) - for bzflag (http://www.bzflag.org) - - based on: - - md5.h and md5.c - reference implementation of RFC 1321 - - Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - -*/ - -#ifndef BZF_MD5_H -#define BZF_MD5_H - -#include -#include - - -// a small class for calculating MD5 hashes of strings or byte arrays -// it is not meant to be fast or secure -// -// usage: 1) feed it blocks of uchars with update() -// 2) finalize() -// 3) get hexdigest() string -// or -// MD5(std::string).hexdigest() -// -// assumes that char is 8 bit and int is 32 bit -class MD5 -{ -public: - typedef unsigned int size_type; // must be 32bit - - MD5(); - MD5(const std::string& text); - void update(const unsigned char *buf, size_type length); - void update(const char *buf, size_type length); - MD5& finalize(); - std::string hexdigest() const; - friend std::ostream& operator<<(std::ostream&, MD5 md5); - -private: - void init(); - typedef unsigned char uint1; // 8bit - typedef unsigned int uint4; // 32bit - enum {blocksize = 64}; // VC6 won't eat a const static int here - - void transform(const uint1 block[blocksize]); - static void decode(uint4 output[], const uint1 input[], size_type len); - static void encode(uint1 output[], const uint4 input[], size_type len); - - bool finalized; - uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk - uint4 count[2]; // 64bit counter for number of bits (lo, hi) - uint4 state[4]; // digest so far - uint1 digest[16]; // the result - - // low level logic operations - static inline uint4 F(uint4 x, uint4 y, uint4 z); - static inline uint4 G(uint4 x, uint4 y, uint4 z); - static inline uint4 H(uint4 x, uint4 y, uint4 z); - static inline uint4 I(uint4 x, uint4 y, uint4 z); - static inline uint4 rotate_left(uint4 x, int n); - static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); -}; - -std::string md5(const std::string & str); - -#endif \ No newline at end of file diff --git a/source/tolua++.exe b/source/tolua++.exe deleted file mode 100644 index e5cec6d78..000000000 Binary files a/source/tolua++.exe and /dev/null differ diff --git a/source/tolua++.h b/source/tolua++.h deleted file mode 100644 index ed5344926..000000000 --- a/source/tolua++.h +++ /dev/null @@ -1,186 +0,0 @@ -/* tolua -** Support code for Lua bindings. -** Written by Waldemar Celes -** TeCGraf/PUC-Rio -** Apr 2003 -** $Id: $ -*/ - -/* This code is free software; you can redistribute it and/or modify it. -** The software provided hereunder is on an "as is" basis, and -** the author has no obligation to provide maintenance, support, updates, -** enhancements, or modifications. -*/ - - -#ifndef TOLUA_H -#define TOLUA_H - -#ifndef TOLUA_API -#define TOLUA_API extern -#endif - -#define TOLUA_VERSION "tolua++-1.0.92" - -#ifdef __cplusplus -extern "C" { -#endif - -#define tolua_pushcppstring(x,y) tolua_pushstring(x,y.c_str()) -#define tolua_iscppstring tolua_isstring - -#define tolua_iscppstringarray tolua_isstringarray -#define tolua_pushfieldcppstring(L,lo,idx,s) tolua_pushfieldstring(L, lo, idx, s.c_str()) - -#ifndef TEMPLATE_BIND - #define TEMPLATE_BIND(p) -#endif - -#define TOLUA_TEMPLATE_BIND(p) - -#define TOLUA_PROTECTED_DESTRUCTOR -#define TOLUA_PROPERTY_TYPE(p) - -typedef int lua_Object; - -#include "lua.h" -#include "lauxlib.h" - -struct tolua_Error -{ - int index; - int array; - const char* type; -}; -typedef struct tolua_Error tolua_Error; - -#define TOLUA_NOPEER LUA_REGISTRYINDEX /* for lua 5.1 */ - -TOLUA_API const char* tolua_typename (lua_State* L, int lo); -TOLUA_API void tolua_error (lua_State* L, const char* msg, tolua_Error* err); -TOLUA_API int tolua_isnoobj (lua_State* L, int lo, tolua_Error* err); -TOLUA_API int tolua_isvalue (lua_State* L, int lo, int def, tolua_Error* err); -TOLUA_API int tolua_isvaluenil (lua_State* L, int lo, tolua_Error* err); -TOLUA_API int tolua_isboolean (lua_State* L, int lo, int def, tolua_Error* err); -TOLUA_API int tolua_isnumber (lua_State* L, int lo, int def, tolua_Error* err); -TOLUA_API int tolua_isstring (lua_State* L, int lo, int def, tolua_Error* err); -TOLUA_API int tolua_istable (lua_State* L, int lo, int def, tolua_Error* err); -TOLUA_API int tolua_isusertable (lua_State* L, int lo, const char* type, int def, tolua_Error* err); -TOLUA_API int tolua_isuserdata (lua_State* L, int lo, int def, tolua_Error* err); -TOLUA_API int tolua_isusertype (lua_State* L, int lo, const char* type, int def, tolua_Error* err); -TOLUA_API int tolua_isvaluearray - (lua_State* L, int lo, int dim, int def, tolua_Error* err); -TOLUA_API int tolua_isbooleanarray - (lua_State* L, int lo, int dim, int def, tolua_Error* err); -TOLUA_API int tolua_isnumberarray - (lua_State* L, int lo, int dim, int def, tolua_Error* err); -TOLUA_API int tolua_isstringarray - (lua_State* L, int lo, int dim, int def, tolua_Error* err); -TOLUA_API int tolua_istablearray - (lua_State* L, int lo, int dim, int def, tolua_Error* err); -TOLUA_API int tolua_isuserdataarray - (lua_State* L, int lo, int dim, int def, tolua_Error* err); -TOLUA_API int tolua_isusertypearray - (lua_State* L, int lo, const char* type, int dim, int def, tolua_Error* err); - -TOLUA_API void tolua_open (lua_State* L); - -TOLUA_API void* tolua_copy (lua_State* L, void* value, unsigned int size); -TOLUA_API int tolua_register_gc (lua_State* L, int lo); -TOLUA_API int tolua_default_collect (lua_State* tolua_S); - -TOLUA_API void tolua_usertype (lua_State* L, const char* type); -TOLUA_API void tolua_beginmodule (lua_State* L, const char* name); -TOLUA_API void tolua_endmodule (lua_State* L); -TOLUA_API void tolua_module (lua_State* L, const char* name, int hasvar); -TOLUA_API void tolua_class (lua_State* L, const char* name, const char* base); -TOLUA_API void tolua_cclass (lua_State* L, const char* lname, const char* name, const char* base, lua_CFunction col); -TOLUA_API void tolua_function (lua_State* L, const char* name, lua_CFunction func); -TOLUA_API void tolua_constant (lua_State* L, const char* name, lua_Number value); -TOLUA_API void tolua_variable (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set); -TOLUA_API void tolua_array (lua_State* L,const char* name, lua_CFunction get, lua_CFunction set); - -/* TOLUA_API void tolua_set_call_event(lua_State* L, lua_CFunction func, char* type); */ -/* TOLUA_API void tolua_addbase(lua_State* L, char* name, char* base); */ - -TOLUA_API void tolua_pushvalue (lua_State* L, int lo); -TOLUA_API void tolua_pushboolean (lua_State* L, int value); -TOLUA_API void tolua_pushnumber (lua_State* L, lua_Number value); -TOLUA_API void tolua_pushstring (lua_State* L, const char* value); -TOLUA_API void tolua_pushuserdata (lua_State* L, void* value); -TOLUA_API void tolua_pushusertype (lua_State* L, void* value, const char* type); -TOLUA_API void tolua_pushusertype_and_takeownership(lua_State* L, void* value, const char* type); -TOLUA_API void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v); -TOLUA_API void tolua_pushfieldboolean (lua_State* L, int lo, int index, int v); -TOLUA_API void tolua_pushfieldnumber (lua_State* L, int lo, int index, lua_Number v); -TOLUA_API void tolua_pushfieldstring (lua_State* L, int lo, int index, const char* v); -TOLUA_API void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v); -TOLUA_API void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, const char* type); -TOLUA_API void tolua_pushfieldusertype_and_takeownership (lua_State* L, int lo, int index, void* v, const char* type); - -TOLUA_API lua_Number tolua_tonumber (lua_State* L, int narg, lua_Number def); -TOLUA_API const char* tolua_tostring (lua_State* L, int narg, const char* def); -TOLUA_API void* tolua_touserdata (lua_State* L, int narg, void* def); -TOLUA_API void* tolua_tousertype (lua_State* L, int narg, void* def); -TOLUA_API int tolua_tovalue (lua_State* L, int narg, int def); -TOLUA_API int tolua_toboolean (lua_State* L, int narg, int def); -TOLUA_API lua_Number tolua_tofieldnumber (lua_State* L, int lo, int index, lua_Number def); -TOLUA_API const char* tolua_tofieldstring (lua_State* L, int lo, int index, const char* def); -TOLUA_API void* tolua_tofielduserdata (lua_State* L, int lo, int index, void* def); -TOLUA_API void* tolua_tofieldusertype (lua_State* L, int lo, int index, void* def); -TOLUA_API int tolua_tofieldvalue (lua_State* L, int lo, int index, int def); -TOLUA_API int tolua_getfieldboolean (lua_State* L, int lo, int index, int def); - -TOLUA_API void tolua_dobuffer(lua_State* L, char* B, unsigned int size, const char* name); - -TOLUA_API int class_gc_event (lua_State* L); - -#ifdef __cplusplus -static inline const char* tolua_tocppstring (lua_State* L, int narg, const char* def) { - - const char* s = tolua_tostring(L, narg, def); - return s?s:""; -}; - -static inline const char* tolua_tofieldcppstring (lua_State* L, int lo, int index, const char* def) { - - const char* s = tolua_tofieldstring(L, lo, index, def); - return s?s:""; -}; - -#else -#define tolua_tocppstring tolua_tostring -#define tolua_tofieldcppstring tolua_tofieldstring -#endif - -TOLUA_API int tolua_fast_isa(lua_State *L, int mt_indexa, int mt_indexb, int super_index); - -#ifndef Mtolua_new -#define Mtolua_new(EXP) new EXP -#endif - -#ifndef Mtolua_delete -#define Mtolua_delete(EXP) delete EXP -#endif - -#ifndef Mtolua_new_dim -#define Mtolua_new_dim(EXP, len) new EXP[len] -#endif - -#ifndef Mtolua_delete_dim -#define Mtolua_delete_dim(EXP) delete [] EXP -#endif - -#ifndef tolua_outside -#define tolua_outside -#endif - -#ifndef tolua_owned -#define tolua_owned -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/tolua_base.h b/source/tolua_base.h deleted file mode 100644 index 4f1038c09..000000000 --- a/source/tolua_base.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef TOLUA_BASE_H -#define TOLUA_BASE_H - -#pragma warning(disable:4800) // This file is ONLY included by Bindings.cpp and it throws lots of C4800 warnings - -#include "tolua++.h" - - - - - -class ToluaBase { - - int lua_instance; - -protected: - - lua_State* lua_state; - - void lua_stacktrace(lua_State* L) const - { - lua_Debug entry; - int depth = 0; - - while (lua_getstack(L, depth, &entry)) - { - lua_getinfo(L, "Sln", &entry); - - LOGERROR("%s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?"); - depth++; - } - } - - - bool report_errors(int status) const - { - if ( status!=0 ) - { - const char* s = lua_tostring(lua_state, -1); - LOGERROR("-- %s", s ); - //lua_pop(lua_state, 1); - LOGERROR("Stack:"); - lua_stacktrace( lua_state ); - return true; - } - return false; - } - - bool push_method(const char* name, lua_CFunction f) const { - - if (!lua_state) return false; - - lua_getref(lua_state, lua_instance); - lua_pushstring(lua_state, name); - //LOGINFO("1. push_method() Stack size: %i", lua_gettop( lua_state ) ); - lua_gettable(lua_state, -2); - //LOGINFO("2. push_method() Stack size: %i", lua_gettop( lua_state ) ); - - if (lua_isnil(lua_state, -1)) { - - // pop the table - lua_pop(lua_state, 2); - return false; - - } else { - - if (f) { - if (lua_iscfunction(lua_state, -1)) { - lua_pop(lua_state, 2); - return false; - }; - /* // not for now - lua_pushcfunction(lua_state, f); - if (lua_rawequal(lua_state, -1, -2)) { - - // avoid recursion, pop both functions and the table - lua_pop(lua_state, 3); - return false; - }; - - // pop f - lua_pop(lua_state, 1); - */ - }; - - // swap table with function - lua_insert(lua_state, -2); - }; - - return true; - }; - - void dbcall(lua_State* L, int nargs, int nresults) const { - - // using lua_call for now - int s = lua_pcall(L, nargs, nresults, 0); - report_errors( s ); - }; -public: - - int GetInstance() { return lua_instance; } - lua_State* GetLuaState() { return lua_state; } - - void tolua__set_instance(lua_State* L, lua_Object lo) { - - lua_state = L; - - lua_pushvalue(L, lo); - lua_instance = lua_ref(lua_state, 1); - }; - - ToluaBase() { - - lua_state = NULL; - }; - - ~ToluaBase() { - - if (lua_state) { - - lua_unref(lua_state, lua_instance); - }; - }; -}; - -#endif - - diff --git a/source/virtual_method_hooks.lua b/source/virtual_method_hooks.lua deleted file mode 100644 index 15ff1d7f8..000000000 --- a/source/virtual_method_hooks.lua +++ /dev/null @@ -1,506 +0,0 @@ --- flags -local disable_virtual_hooks = true -local enable_pure_virtual = true -local default_private_access = false - -local access = {public = 0, protected = 1, private = 2} - -function preparse_hook(p) - - if default_private_access then - -- we need to make all structs 'public' by default - p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n") - end -end - - -function parser_hook(s) - - local container = classContainer.curr -- get the current container - - if default_private_access then - if not container.curr_member_access and container.classtype == 'class' then - -- default access for classes is private - container.curr_member_access = access.private - end - end - - -- try labels (public, private, etc) - do - local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type' - if b then - - -- found a label, get the new access value from the global 'access' table - if access[label] then - container.curr_member_access = access[label] - end -- else ? - - return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:] - end - end - - - local ret = nil - - if disable_virtual_hooks then - - return ret - end - - local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())") - local const - if b then - local ret = string.sub(s, e+1) - if string.find(ret, "^%s*const") then - const = "const" - ret = string.gsub(ret, "^%s*const", "") - end - local purev = false - if string.find(ret, "^%s*=%s*0") then - purev = true - ret = string.gsub(ret, "^%s*=%s*0", "") - end - ret = string.gsub(ret, "^%s*%b{}", "") - - local func = Function(decl, arg, const) - func.pure_virtual = purev - --func.access = access - func.original_sig = decl - - local curflags = classContainer.curr.flags - if not curflags.virtual_class then - - curflags.virtual_class = VirtualClass() - end - curflags.virtual_class:add(func) - curflags.pure_virtual = curflags.pure_virtual or purev - - return ret - end - - return ret -end - - --- class VirtualClass -classVirtualClass = { - classtype = 'class', - name = '', - base = '', - type = '', - btype = '', - ctype = '', -} -classVirtualClass.__index = classVirtualClass -setmetatable(classVirtualClass,classClass) - -function classVirtualClass:add(f) - - local parent = classContainer.curr - pop() - - table.insert(self.methods, {f=f}) - - local name,sig - - -- doble negative means positive - if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then - - name = self.original_name - elseif f.name == 'delete' then - name = '~'..self.original_name - else - if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then - name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name - end - end - - if name then - sig = name..self:get_arg_list(f, true)..";\n" - push(self) - sig = preprocess(sig) - self:parse(sig) - pop() - end - - push(parent) -end - -function preprocess(sig) - - sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' - sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' - sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*' - sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*' - - return sig -end - -function classVirtualClass:get_arg_list(f, decl) - - local ret = "" - local sep = "" - local i=1 - while f.args[i] do - - local arg = f.args[i] - if decl then - local ptr - if arg.ret ~= '' then - ptr = arg.ret - else - ptr = arg.ptr - end - local def = "" - if arg.def and arg.def ~= "" then - - def = " = "..arg.def - end - ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def - else - ret = ret..sep..arg.name - end - - sep = "," - i = i+1 - end - - return "("..ret..")" -end - -function classVirtualClass:add_parent_virtual_methods(parent) - - parent = parent or _global_classes[self.flags.parent_object.btype] - - if not parent then return end - - if parent.flags.virtual_class then - - local vclass = parent.flags.virtual_class - for k,v in ipairs(vclass.methods) do - if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then - table.insert(self.methods, {f=v.f}) - end - end - end - - parent = _global_classes[parent.btype] - if parent then - self:add_parent_virtual_methods(parent) - end -end - -function classVirtualClass:has_method(f) - - for k,v in pairs(self.methods) do - -- just match name for now - if v.f.name == f.name then - return true - end - end - - return false -end - -function classVirtualClass:add_constructors() - - local i=1 - while self.flags.parent_object[i] do - - local v = self.flags.parent_object[i] - if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then - - self:add(v) - end - - i = i+1 - end - -end - ---[[ -function classVirtualClass:requirecollection(t) - - self:add_constructors() - local req = classClass.requirecollection(self, t) - if req then - output('class ',self.name,";") - end - return req -end ---]] - -function classVirtualClass:supcode() - - -- pure virtual classes can have no default constructors on gcc 4 - - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n') - end - - local ns - if self.prox.classtype == 'namespace' then - output('namespace ',self.prox.name, " {") - ns = true - end - - output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {") - - output("public:\n") - - self:add_parent_virtual_methods() - - self:output_methods(self.btype) - self:output_parent_methods() - - self:add_constructors() - - -- no constructor for pure virtual classes - if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then - - self:output_constructors() - end - - output("};\n\n") - - if ns then - output("};") - end - - classClass.supcode(self) - - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#endif // __GNUC__ >= 4\n') - end - - -- output collector for custom class if required - if self:requirecollection(_collect) and _collect[self.type] then - - output('\n') - output('/* function to release collected object via destructor */') - output('#ifdef __cplusplus\n') - --for i,v in pairs(collect) do - i,v = self.type, _collect[self.type] - output('\nstatic int '..v..' (lua_State* tolua_S)') - output('{') - output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);') - output(' delete self;') - output(' return 0;') - output('}') - --end - output('#endif\n\n') - end - -end - -function classVirtualClass:register(pre) - - -- pure virtual classes can have no default constructors on gcc 4 - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n') - end - - classClass.register(self, pre) - - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#endif // __GNUC__ >= 4\n') - end -end - - ---function classVirtualClass:requirecollection(_c) --- if self.flags.parent_object.flags.pure_virtual then --- return false --- end --- return classClass.requirecollection(self, _c) ---end - -function classVirtualClass:output_parent_methods() - - for k,v in ipairs(self.methods) do - - if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then - - local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." " - local parent_name = rettype..self.btype.."__"..v.f.name - - local par_list = self:get_arg_list(v.f, true) - local var_list = self:get_arg_list(v.f, false) - - -- the parent's virtual function - output("\t"..parent_name..par_list.." {") - - output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";") - output("\t};") - end - end -end - -function classVirtualClass:output_methods(btype) - - for k,v in ipairs(self.methods) do - - if v.f.name ~= 'new' and v.f.name ~= 'delete' then - - self:output_method(v.f, btype) - end - end - output("\n") -end - -function classVirtualClass:output_constructors() - - for k,v in ipairs(self.methods) do - - if v.f.name == 'new' then - - local par_list = self:get_arg_list(v.f, true) - local var_list = self:get_arg_list(v.f, false) - - output("\t",self.original_name,par_list,":",self.btype,var_list,"{};") - end - end -end - -function classVirtualClass:output_method(f, btype) - - if f.access == 2 then -- private - return - end - - local ptr - if f.ret ~= '' then - ptr = f.ret - else - ptr = f.ptr - end - - local rettype = f.mod.." "..f.type..f.ptr.." " - local par_list = self:get_arg_list(f, true) - local var_list = self:get_arg_list(f, false) - - if string.find(rettype, "%s*LuaQtGenericFlags%s*") then - - _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+") - end - - -- the caller of the lua method - output("\t"..rettype.." "..f.name..par_list..f.const.." {") - local fn = f.cname - if f.access == 1 then - fn = "NULL" - end - output('\t\tif (push_method("',f.lname,'", ',fn,')) {') - - --if f.type ~= 'void' then - -- output("\t\t\tint top = lua_gettop(lua_state)-1;") - --end - - -- push the parameters - local argn = 0 - for i,arg in ipairs(f.args) do - if arg.type ~= 'void' then - local t,ct = isbasic(arg.type) - if t and t ~= '' then - if arg.ret == "*" then - t = 'userdata' - ct = 'void*' - end - output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");"); - else - local m = arg.ptr - if m and m~= "" then - if m == "*" then m = "" end - output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");") - else - output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n") - output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n') - end - end - argn = argn+1 - end - end - - -- call the function - output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ") - - -- return value - if f.type ~= 'void' then - output("1);") - - local t,ct = isbasic(f.type) - if t and t ~= '' then - --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);") - output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);") - else - - local mod = "" - if f.ptr ~= "*" then - mod = "*("..f.type.."*)" - end - - --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);") - output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);") - end - output("\t\t\tlua_pop(lua_state, 1);") - output("\t\t\treturn tolua_ret;") - else - output("0);") - end - - -- handle non-implemeted function - output("\t\t} else {") - - if f.pure_virtual then - - output('\t\t\tif (lua_state)') - --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);') - output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");') - output('\t\t\telse {') - output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");') - output('\t\t\t\t::abort();') - output('\t\t\t};') - if( rettype == " std::string " ) then - output('\t\t\treturn "";') - else - output('\t\t\treturn (',rettype,')0;') - end - else - - output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';') - end - - output("\t\t};") - - output("\t};") -end - -function VirtualClass() - - local parent = classContainer.curr - pop() - - local name = "Lua__"..parent.original_name - - local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil}) - setmetatable(c, classVirtualClass) - - local ft = getnamespace(c.parent)..c.original_name - append_global_type(ft, c) - - push(parent) - - c.flags.parent_object = parent - c.methods = {} - - push(c) - c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n") - pop() - - return c -end - - - - - diff --git a/src/AllToLua.bat b/src/AllToLua.bat new file mode 100644 index 000000000..f7867fadb --- /dev/null +++ b/src/AllToLua.bat @@ -0,0 +1,27 @@ + +:: AllToLua.bat + +:: This scripts updates the automatically-generates Lua bindings in Bindings.cpp / Bindings.h + + + + + +:: If there was a Git conflict, resolve it by resetting to HEAD; we're regenerating the files from scratch anyway +git checkout --ours Bindings.cpp +git add -u Bindings.cpp +git checkout --ours Bindings.h +git add -u Bindings.h + + + + + +:: Regenerate the files: +"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg + + + + + +if %ALLTOLUA_WAIT%N == N pause diff --git a/src/AllToLua.pkg b/src/AllToLua.pkg new file mode 100644 index 000000000..ee594be1a --- /dev/null +++ b/src/AllToLua.pkg @@ -0,0 +1,81 @@ + +$#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +$#include "tolua_base.h" + +// Typedefs from Globals.h, so that we don't have to include that file: +typedef long long Int64; +typedef int Int32; +typedef short Int16; + +typedef unsigned long long UInt64; +typedef unsigned int UInt32; +typedef unsigned short UInt16; + + +$cfile "ChunkDef.h" + +$cfile "../iniFile/iniFile.h" + +$cfile "OSSupport/File.h" + +$cfile "BlockID.h" +$cfile "StringUtils.h" +$cfile "Defines.h" +$cfile "LuaFunctions.h" +$cfile "ChatColor.h" +$cfile "ClientHandle.h" +$cfile "Entities/Entity.h" +$cfile "Entities/Pawn.h" +$cfile "Entities/Player.h" +$cfile "Entities/Pickup.h" +$cfile "Entities/ProjectileEntity.h" +$cfile "PluginManager.h" +$cfile "Plugin.h" +$cfile "PluginLua.h" +$cfile "Server.h" +$cfile "World.h" +$cfile "Inventory.h" +$cfile "Enchantments.h" +$cfile "Item.h" +$cfile "ItemGrid.h" +$cfile "BlockEntities/BlockEntity.h" +$cfile "BlockEntities/BlockEntityWithItems.h" +$cfile "BlockEntities/ChestEntity.h" +$cfile "BlockEntities/DropSpenserEntity.h" +$cfile "BlockEntities/DispenserEntity.h" +$cfile "BlockEntities/DropperEntity.h" +$cfile "BlockEntities/FurnaceEntity.h" +$cfile "BlockEntities/HopperEntity.h" +$cfile "BlockEntities/JukeboxEntity.h" +$cfile "BlockEntities/NoteEntity.h" +$cfile "BlockEntities/SignEntity.h" +$cfile "WebAdmin.h" +$cfile "WebPlugin.h" +$cfile "Root.h" +$cfile "Vector3f.h" +$cfile "Vector3d.h" +$cfile "Vector3i.h" +$cfile "Matrix4f.h" +$cfile "Cuboid.h" +$cfile "BoundingBox.h" +$cfile "Tracer.h" +$cfile "Group.h" +$cfile "BlockArea.h" +$cfile "Generating/ChunkDesc.h" +$cfile "CraftingRecipes.h" +$cfile "UI/Window.h" +$cfile "LuaWindow.h" +$cfile "Mobs/Monster.h" + + + + + +// Need to declare this class so that the usertype is properly registered in Bindings.cpp - +// it seems impossible to register a usertype in ManualBindings.cpp +class cLineBlockTracer; + + + + diff --git a/src/AllToLua.sh b/src/AllToLua.sh new file mode 100755 index 000000000..887c2490c --- /dev/null +++ b/src/AllToLua.sh @@ -0,0 +1,2 @@ +#!/bin/bash +/usr/bin/tolua++ -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg diff --git a/src/Authenticator.cpp b/src/Authenticator.cpp new file mode 100644 index 000000000..9a6dcf51b --- /dev/null +++ b/src/Authenticator.cpp @@ -0,0 +1,267 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Authenticator.h" +#include "OSSupport/BlockingTCPLink.h" +#include "Root.h" +#include "Server.h" + +#include "../iniFile/iniFile.h" + +#include + + + + + +#define DEFAULT_AUTH_SERVER "session.minecraft.net" +#define DEFAULT_AUTH_ADDRESS "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%" +#define MAX_REDIRECTS 10 + + + + + +cAuthenticator::cAuthenticator(void) : + super("cAuthenticator"), + m_Server(DEFAULT_AUTH_SERVER), + m_Address(DEFAULT_AUTH_ADDRESS), + m_ShouldAuthenticate(true) +{ +} + + + + + +cAuthenticator::~cAuthenticator() +{ + Stop(); +} + + + + + +/// Read custom values from INI +void cAuthenticator::ReadINI(cIniFile & IniFile) +{ + m_Server = IniFile.GetValueSet("Authentication", "Server", DEFAULT_AUTH_SERVER); + m_Address = IniFile.GetValueSet("Authentication", "Address", DEFAULT_AUTH_ADDRESS); + m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true); +} + + + + + +/// Queues a request for authenticating a user. If the auth fails, the user is kicked +void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash) +{ + if (!m_ShouldAuthenticate) + { + cRoot::Get()->AuthenticateUser(a_ClientID); + return; + } + + cCSLock Lock(m_CS); + m_Queue.push_back(cUser(a_ClientID, a_UserName, a_ServerHash)); + m_QueueNonempty.Set(); +} + + + + + +void cAuthenticator::Start(cIniFile & IniFile) +{ + ReadINI(IniFile); + m_ShouldTerminate = false; + super::Start(); +} + + + + + +void cAuthenticator::Stop(void) +{ + m_ShouldTerminate = true; + m_QueueNonempty.Set(); + Wait(); +} + + + + + +void cAuthenticator::Execute(void) +{ + for (;;) + { + cCSLock Lock(m_CS); + while (!m_ShouldTerminate && (m_Queue.size() == 0)) + { + cCSUnlock Unlock(Lock); + m_QueueNonempty.Wait(); + } + if (m_ShouldTerminate) + { + return; + } + ASSERT(!m_Queue.empty()); + + int ClientID = m_Queue.front().m_ClientID; + AString UserName = m_Queue.front().m_Name; + AString ActualAddress = m_Address; + ReplaceString(ActualAddress, "%USERNAME%", UserName); + ReplaceString(ActualAddress, "%SERVERID%", m_Queue.front().m_ServerID); + m_Queue.pop_front(); + Lock.Unlock(); + + if (!AuthFromAddress(m_Server, ActualAddress, UserName)) + { + cRoot::Get()->KickUser(ClientID, "Failed to authenticate account!"); + } + else + { + cRoot::Get()->AuthenticateUser(ClientID); + } + } // for (-ever) +} + + + + + +bool cAuthenticator::AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level /* = 1 */) +{ + // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) + + cBlockingTCPLink Link; + if (!Link.Connect(a_Server.c_str(), 80)) + { + LOGERROR("cAuthenticator: cannot connect to auth server \"%s\", kicking user \"%s\"", a_Server.c_str(), a_Server.c_str()); + return false; + } + + Link.SendMessage( AString( "GET " + a_Address + " HTTP/1.1\r\n" ).c_str()); + Link.SendMessage( AString( "User-Agent: MCServer\r\n" ).c_str()); + Link.SendMessage( AString( "Host: " + a_Server + "\r\n" ).c_str()); + //Link.SendMessage( AString( "Host: session.minecraft.net\r\n" ).c_str()); + Link.SendMessage( AString( "Accept: */*\r\n" ).c_str()); + Link.SendMessage( AString( "Connection: close\r\n" ).c_str()); //Close so we don´t have to mess with the Content-Length :) + Link.SendMessage( AString( "\r\n" ).c_str()); + AString DataRecvd; + Link.ReceiveData(DataRecvd); + Link.CloseSocket(); + + std::stringstream ss(DataRecvd); + + // Parse the data received: + std::string temp; + ss >> temp; + bool bRedirect = false; + bool bOK = false; + if ((temp.compare("HTTP/1.1") == 0) || (temp.compare("HTTP/1.0") == 0)) + { + int code; + ss >> code; + if (code == 302) + { + // redirect blabla + LOGINFO("Need to redirect!"); + if (a_Level > MAX_REDIRECTS) + { + LOGERROR("cAuthenticator: received too many levels of redirection from auth server \"%s\" for user \"%s\", bailing out and kicking the user", a_Server.c_str(), a_UserName.c_str()); + return false; + } + bRedirect = true; + } + else if (code == 200) + { + LOGD("cAuthenticator: Received status 200 OK! :D"); + bOK = true; + } + } + else + { + LOGERROR("cAuthenticator: cannot parse auth reply from server \"%s\" for user \"%s\", kicking the user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + if( bRedirect ) + { + AString Location; + // Search for "Location:" + bool bFoundLocation = false; + while( !bFoundLocation && ss.good() ) + { + char c = 0; + while( c != '\n' ) + { + ss.get( c ); + } + AString Name; + ss >> Name; + if (Name.compare("Location:") == 0) + { + bFoundLocation = true; + ss >> Location; + } + } + if (!bFoundLocation) + { + LOGERROR("cAuthenticator: received invalid redirection from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + Location = Location.substr(strlen("http://"), std::string::npos); // Strip http:// + std::string Server = Location.substr( 0, Location.find( "/" ) ); // Only leave server address + Location = Location.substr( Server.length(), std::string::npos); + return AuthFromAddress(Server, Location, a_UserName, a_Level + 1); + } + + if (!bOK) + { + LOGERROR("cAuthenticator: received an error from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + // Header says OK, so receive the rest. + // Go past header, double \n means end of headers + char c = 0; + while (ss.good()) + { + while (c != '\n') + { + ss.get(c); + } + ss.get(c); + if( c == '\n' || c == '\r' || ss.peek() == '\r' || ss.peek() == '\n' ) + break; + } + if (!ss.good()) + { + LOGERROR("cAuthenticator: error while parsing response body from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + std::string Result; + ss >> Result; + LOGD("cAuthenticator: Authentication result was %s", Result.c_str()); + + if (Result.compare("YES") == 0) //Works well + { + LOGINFO("Authentication result \"YES\", player authentication success!"); + return true; + } + + + LOGINFO("Authentication result was \"%s\", player authentication failure!", Result.c_str()); + return false; +} + + + + diff --git a/src/Authenticator.h b/src/Authenticator.h new file mode 100644 index 000000000..02cd6f4c5 --- /dev/null +++ b/src/Authenticator.h @@ -0,0 +1,93 @@ + +// cAuthenticator.h + +// Interfaces to the cAuthenticator class representing the thread that authenticates users against the official MC server +// Authentication prevents "hackers" from joining with an arbitrary username (possibly impersonating the server admins) +// For more info, see http://wiki.vg/Session#Server_operation +// In MCS, authentication is implemented as a single thread that receives queued auth requests and dispatches them one by one. + + + + + +#pragma once +#ifndef CAUTHENTICATOR_H_INCLUDED +#define CAUTHENTICATOR_H_INCLUDED + +#include "OSSupport/IsThread.h" + + + + + +// fwd: "cRoot.h" +class cRoot; + + + + + +class cAuthenticator : + public cIsThread +{ + typedef cIsThread super; + +public: + cAuthenticator(void); + ~cAuthenticator(); + + /// (Re-)read server and address from INI: + void ReadINI(cIniFile & IniFile); + + /// Queues a request for authenticating a user. If the auth fails, the user is kicked + void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash); + + /// Starts the authenticator thread. The thread may be started and stopped repeatedly + void Start(cIniFile & IniFile); + + /// Stops the authenticator thread. The thread may be started and stopped repeatedly + void Stop(void); + +private: + + class cUser + { + public: + int m_ClientID; + AString m_Name; + AString m_ServerID; + + cUser(int a_ClientID, const AString & a_Name, const AString & a_ServerID) : + m_ClientID(a_ClientID), + m_Name(a_Name), + m_ServerID(a_ServerID) + { + } + } ; + + typedef std::deque cUserList; + + cCriticalSection m_CS; + cUserList m_Queue; + cEvent m_QueueNonempty; + + AString m_Server; + AString m_Address; + bool m_ShouldAuthenticate; + + // cIsThread override: + virtual void Execute(void) override; + + // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) + bool AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level = 1); +}; + + + + + +#endif // CAUTHENTICATOR_H_INCLUDED + + + + diff --git a/src/Bindings.cpp b/src/Bindings.cpp new file mode 100644 index 000000000..93c66d233 --- /dev/null +++ b/src/Bindings.cpp @@ -0,0 +1,31485 @@ +/* +** Lua binding: AllToLua +** Generated automatically by tolua++-1.0.92 on 11/15/13 10:14:19. +*/ + +#ifndef __cplusplus +#include "stdlib.h" +#endif +#include "string.h" + +#include "tolua++.h" + +/* Exported function */ +TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "tolua_base.h" +#include "ChunkDef.h" +#include "../iniFile/iniFile.h" +#include "OSSupport/File.h" +#include "BlockID.h" +#include "StringUtils.h" +#include "Defines.h" +#include "LuaFunctions.h" +#include "ChatColor.h" +#include "ClientHandle.h" +#include "Entities/Entity.h" +#include "Entities/Pawn.h" +#include "Entities/Player.h" +#include "Entities/Pickup.h" +#include "Entities/ProjectileEntity.h" +#include "PluginManager.h" +#include "Plugin.h" +#include "PluginLua.h" +#include "Server.h" +#include "World.h" +#include "Inventory.h" +#include "Enchantments.h" +#include "Item.h" +#include "ItemGrid.h" +#include "BlockEntities/BlockEntity.h" +#include "BlockEntities/BlockEntityWithItems.h" +#include "BlockEntities/ChestEntity.h" +#include "BlockEntities/DropSpenserEntity.h" +#include "BlockEntities/DispenserEntity.h" +#include "BlockEntities/DropperEntity.h" +#include "BlockEntities/FurnaceEntity.h" +#include "BlockEntities/HopperEntity.h" +#include "BlockEntities/JukeboxEntity.h" +#include "BlockEntities/NoteEntity.h" +#include "BlockEntities/SignEntity.h" +#include "WebAdmin.h" +#include "WebPlugin.h" +#include "Root.h" +#include "Vector3f.h" +#include "Vector3d.h" +#include "Vector3i.h" +#include "Matrix4f.h" +#include "Cuboid.h" +#include "BoundingBox.h" +#include "Tracer.h" +#include "Group.h" +#include "BlockArea.h" +#include "Generating/ChunkDesc.h" +#include "CraftingRecipes.h" +#include "UI/Window.h" +#include "LuaWindow.h" +#include "Mobs/Monster.h" + +/* function to release collected object via destructor */ +#ifdef __cplusplus + +static int tolua_collect_sWebAdminPage (lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cBoundingBox (lua_State* tolua_S) +{ + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cItem (lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_Vector3f (lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cIniFile (lua_State* tolua_S) +{ + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cPickup (lua_State* tolua_S) +{ + cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cItems (lua_State* tolua_S) +{ + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cBlockArea (lua_State* tolua_S) +{ + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cTracer (lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cCraftingGrid (lua_State* tolua_S) +{ + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cCuboid (lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cBlockEntity (lua_State* tolua_S) +{ + cBlockEntity* self = (cBlockEntity*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_Vector3i (lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cEnchantments (lua_State* tolua_S) +{ + cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cLuaWindow (lua_State* tolua_S) +{ + cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_Vector3d (lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} +#endif + + +/* function to register type */ +static void tolua_reg_types (lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"cThrownEnderPearlEntity"); + tolua_usertype(tolua_S,"cFurnaceEntity"); + tolua_usertype(tolua_S,"cEntity"); + tolua_usertype(tolua_S,"cCuboid"); + tolua_usertype(tolua_S,"cEnchantments"); + tolua_usertype(tolua_S,"cMonster"); + tolua_usertype(tolua_S,"cPluginLua"); + tolua_usertype(tolua_S,"cRoot"); + tolua_usertype(tolua_S,"std::vector"); + tolua_usertype(tolua_S,"cPickup"); + tolua_usertype(tolua_S,"sWebAdminPage"); + tolua_usertype(tolua_S,"cFireChargeEntity"); + tolua_usertype(tolua_S,"cWorld"); + tolua_usertype(tolua_S,"cChunkDesc"); + tolua_usertype(tolua_S,"cFurnaceRecipe"); + tolua_usertype(tolua_S,"cPluginManager"); + tolua_usertype(tolua_S,"Vector3f"); + tolua_usertype(tolua_S,"cCraftingRecipes"); + tolua_usertype(tolua_S,"cJukeboxEntity"); + tolua_usertype(tolua_S,"cChestEntity"); + tolua_usertype(tolua_S,"cDispenserEntity"); + tolua_usertype(tolua_S,"cGhastFireballEntity"); + tolua_usertype(tolua_S,"cLineBlockTracer"); + tolua_usertype(tolua_S,"cListeners"); + tolua_usertype(tolua_S,"cThrownSnowballEntity"); + tolua_usertype(tolua_S,"Vector3d"); + tolua_usertype(tolua_S,"TakeDamageInfo"); + tolua_usertype(tolua_S,"cCraftingRecipe"); + tolua_usertype(tolua_S,"cPlugin"); + tolua_usertype(tolua_S,"cItemGrid"); + tolua_usertype(tolua_S,"cHTTPServer::cCallbacks"); + tolua_usertype(tolua_S,"cLuaWindow"); + tolua_usertype(tolua_S,"cInventory"); + tolua_usertype(tolua_S,"cHopperEntity"); + tolua_usertype(tolua_S,"std::vector"); + tolua_usertype(tolua_S,"cBlockEntityWithItems"); + tolua_usertype(tolua_S,"cWindow"); + tolua_usertype(tolua_S,"cCraftingGrid"); + tolua_usertype(tolua_S,"cItem"); + tolua_usertype(tolua_S,"cBlockArea"); + tolua_usertype(tolua_S,"cArrowEntity"); + tolua_usertype(tolua_S,"cDropSpenserEntity"); + tolua_usertype(tolua_S,"cGroup"); + tolua_usertype(tolua_S,"cTracer"); + tolua_usertype(tolua_S,"cBoundingBox"); + tolua_usertype(tolua_S,"cNoteEntity"); + tolua_usertype(tolua_S,"Vector3i"); + tolua_usertype(tolua_S,"cBlockEntity"); + tolua_usertype(tolua_S,"cCriticalSection"); + tolua_usertype(tolua_S,"HTTPTemplateRequest"); + tolua_usertype(tolua_S,"cPlayer"); + tolua_usertype(tolua_S,"cServer"); + tolua_usertype(tolua_S,"cSignEntity"); + tolua_usertype(tolua_S,"cFile"); + tolua_usertype(tolua_S,"cItems"); + tolua_usertype(tolua_S,"cClientHandle"); + tolua_usertype(tolua_S,"cIniFile"); + tolua_usertype(tolua_S,"cWebPlugin"); + tolua_usertype(tolua_S,"cChatColor"); + tolua_usertype(tolua_S,"cPawn"); + tolua_usertype(tolua_S,"cThrownEggEntity"); + tolua_usertype(tolua_S,"cGroupManager"); + tolua_usertype(tolua_S,"cWebAdmin"); + tolua_usertype(tolua_S,"HTTPRequest"); + tolua_usertype(tolua_S,"cProjectileEntity"); + tolua_usertype(tolua_S,"HTTPFormData"); + tolua_usertype(tolua_S,"cItemGrid::cListener"); + tolua_usertype(tolua_S,"cDropperEntity"); +} + +/* method: new of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new00 +static int tolua_AllToLua_cIniFile_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new00_local +static int tolua_AllToLua_cIniFile_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CaseSensitive of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_CaseSensitive00 +static int tolua_AllToLua_cIniFile_CaseSensitive00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CaseSensitive'", NULL); +#endif + { + self->CaseSensitive(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CaseSensitive'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CaseInsensitive of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_CaseInsensitive00 +static int tolua_AllToLua_cIniFile_CaseInsensitive00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CaseInsensitive'", NULL); +#endif + { + self->CaseInsensitive(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CaseInsensitive'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReadFile of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_ReadFile00 +static int tolua_AllToLua_cIniFile_ReadFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + bool a_AllowExampleRedirect = ((bool) tolua_toboolean(tolua_S,3,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadFile'", NULL); +#endif + { + bool tolua_ret = (bool) self->ReadFile(a_FileName,a_AllowExampleRedirect); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReadFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: WriteFile of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_WriteFile00 +static int tolua_AllToLua_cIniFile_WriteFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteFile'", NULL); +#endif + { + bool tolua_ret = (bool) self->WriteFile(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'WriteFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Clear00 +static int tolua_AllToLua_cIniFile_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FindKey of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_FindKey00 +static int tolua_AllToLua_cIniFile_FindKey00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindKey'", NULL); +#endif + { + int tolua_ret = (int) self->FindKey(keyname); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FindKey'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FindValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_FindValue00 +static int tolua_AllToLua_cIniFile_FindValue00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindValue'", NULL); +#endif + { + int tolua_ret = (int) self->FindValue(keyID,valuename); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FindValue'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumKeys of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeys00 +static int tolua_AllToLua_cIniFile_GetNumKeys00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeys'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumKeys(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumKeys'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddKeyName of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyName00 +static int tolua_AllToLua_cIniFile_AddKeyName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyName'", NULL); +#endif + { + int tolua_ret = (int) self->AddKeyName(keyname); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddKeyName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetKeyName of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyName00 +static int tolua_AllToLua_cIniFile_GetKeyName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyName'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetKeyName(keyID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetKeyName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumValues of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues00 +static int tolua_AllToLua_cIniFile_GetNumValues00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumValues(keyname); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumValues'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumValues of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues01 +static int tolua_AllToLua_cIniFile_GetNumValues01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumValues(keyID); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetNumValues00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueName of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName00 +static int tolua_AllToLua_cIniFile_GetValueName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValueName(keyname,valueID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueName of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName01 +static int tolua_AllToLua_cIniFile_GetValueName01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValueName(keyID,valueID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValueName00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue00 +static int tolua_AllToLua_cIniFile_GetValue00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValue(keyname,valuename); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValue'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue01 +static int tolua_AllToLua_cIniFile_GetValue01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValue(keyname,valuename,defValue); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + tolua_pushcppstring(tolua_S,(const char*)defValue); + } + } + return 4; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValue00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue02 +static int tolua_AllToLua_cIniFile_GetValue02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValue(keyID,valueID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValue01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue03 +static int tolua_AllToLua_cIniFile_GetValue03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); + const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValue(keyID,valueID,defValue); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)defValue); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValue02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueF of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueF00 +static int tolua_AllToLua_cIniFile_GetValueF00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const double defValue = ((const double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueF'", NULL); +#endif + { + double tolua_ret = (double) self->GetValueF(keyname,valuename,defValue); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueF'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueI of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueI00 +static int tolua_AllToLua_cIniFile_GetValueI00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const int defValue = ((const int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueI'", NULL); +#endif + { + int tolua_ret = (int) self->GetValueI(keyname,valuename,defValue); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueI'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueB of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueB00 +static int tolua_AllToLua_cIniFile_GetValueB00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isboolean(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const bool defValue = ((const bool) tolua_toboolean(tolua_S,4,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueB'", NULL); +#endif + { + bool tolua_ret = (bool) self->GetValueB(keyname,valuename,defValue); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueB'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSet of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSet00 +static int tolua_AllToLua_cIniFile_GetValueSet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSet'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValueSet(keyname,valuename); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueSet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSet of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSet01 +static int tolua_AllToLua_cIniFile_GetValueSet01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSet'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValueSet(keyname,valuename,defValue); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + tolua_pushcppstring(tolua_S,(const char*)defValue); + } + } + return 4; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValueSet00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSetF of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetF00 +static int tolua_AllToLua_cIniFile_GetValueSetF00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const double defValue = ((const double) tolua_tonumber(tolua_S,4,0.0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetF'", NULL); +#endif + { + double tolua_ret = (double) self->GetValueSetF(keyname,valuename,defValue); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueSetF'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSetI of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetI00 +static int tolua_AllToLua_cIniFile_GetValueSetI00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const int defValue = ((const int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetI'", NULL); +#endif + { + int tolua_ret = (int) self->GetValueSetI(keyname,valuename,defValue); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueSetI'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSetB of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetB00 +static int tolua_AllToLua_cIniFile_GetValueSetB00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isboolean(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const bool defValue = ((const bool) tolua_toboolean(tolua_S,4,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetB'", NULL); +#endif + { + bool tolua_ret = (bool) self->GetValueSetB(keyname,valuename,defValue); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueSetB'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValue00 +static int tolua_AllToLua_cIniFile_SetValue00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); + const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValue(keyID,valueID,value); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)value); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetValue'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValue01 +static int tolua_AllToLua_cIniFile_SetValue01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0)); + const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValue(keyname,valuename,value,create); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + tolua_pushcppstring(tolua_S,(const char*)value); + } + } + return 4; +tolua_lerror: + return tolua_AllToLua_cIniFile_SetValue00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValueI of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueI00 +static int tolua_AllToLua_cIniFile_SetValueI00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const int value = ((const int) tolua_tonumber(tolua_S,4,0)); + const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueI'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValueI(keyname,valuename,value,create); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetValueI'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValueB of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueB00 +static int tolua_AllToLua_cIniFile_SetValueB00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isboolean(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const bool value = ((const bool) tolua_toboolean(tolua_S,4,0)); + const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueB'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValueB(keyname,valuename,value,create); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetValueB'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValueF of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueF00 +static int tolua_AllToLua_cIniFile_SetValueF00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const double value = ((const double) tolua_tonumber(tolua_S,4,0)); + const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueF'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValueF(keyname,valuename,value,create); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetValueF'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteValueByID of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteValueByID00 +static int tolua_AllToLua_cIniFile_DeleteValueByID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValueByID'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteValueByID(keyID,valueID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteValueByID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteValue00 +static int tolua_AllToLua_cIniFile_DeleteValue00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValue'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteValue(keyname,valuename); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteValue'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKey of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKey00 +static int tolua_AllToLua_cIniFile_DeleteKey00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKey'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKey(keyname); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteKey'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumHeaderComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumHeaderComments00 +static int tolua_AllToLua_cIniFile_GetNumHeaderComments00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumHeaderComments'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumHeaderComments(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumHeaderComments'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddHeaderComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddHeaderComment00 +static int tolua_AllToLua_cIniFile_AddHeaderComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString comment = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddHeaderComment'", NULL); +#endif + { + self->AddHeaderComment(comment); + tolua_pushcppstring(tolua_S,(const char*)comment); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddHeaderComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeaderComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetHeaderComment00 +static int tolua_AllToLua_cIniFile_GetHeaderComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int commentID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeaderComment'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetHeaderComment(commentID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeaderComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteHeaderComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteHeaderComment00 +static int tolua_AllToLua_cIniFile_DeleteHeaderComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + int commentID = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteHeaderComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteHeaderComment(commentID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteHeaderComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteHeaderComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteHeaderComments00 +static int tolua_AllToLua_cIniFile_DeleteHeaderComments00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteHeaderComments'", NULL); +#endif + { + self->DeleteHeaderComments(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteHeaderComments'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumKeyComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments00 +static int tolua_AllToLua_cIniFile_GetNumKeyComments00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumKeyComments(keyID); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumKeyComments'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumKeyComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments01 +static int tolua_AllToLua_cIniFile_GetNumKeyComments01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumKeyComments(keyname); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetNumKeyComments00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment00 +static int tolua_AllToLua_cIniFile_AddKeyComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->AddKeyComment(keyID,comment); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)comment); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddKeyComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment01 +static int tolua_AllToLua_cIniFile_AddKeyComment01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->AddKeyComment(keyname,comment); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)comment); + } + } + return 3; +tolua_lerror: + return tolua_AllToLua_cIniFile_AddKeyComment00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment00 +static int tolua_AllToLua_cIniFile_GetKeyComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetKeyComment(keyID,commentID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetKeyComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment01 +static int tolua_AllToLua_cIniFile_GetKeyComment01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetKeyComment(keyname,commentID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetKeyComment00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComment00 +static int tolua_AllToLua_cIniFile_DeleteKeyComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKeyComment(keyID,commentID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteKeyComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComment01 +static int tolua_AllToLua_cIniFile_DeleteKeyComment01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKeyComment(keyname,commentID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_DeleteKeyComment00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKeyComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComments00 +static int tolua_AllToLua_cIniFile_DeleteKeyComments00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKeyComments(keyID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteKeyComments'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKeyComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComments01 +static int tolua_AllToLua_cIniFile_DeleteKeyComments01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKeyComments(keyname); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_DeleteKeyComments00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Exists of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Exists00 +static int tolua_AllToLua_cFile_Exists00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::Exists(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Exists'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Delete of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Delete00 +static int tolua_AllToLua_cFile_Delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::Delete(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Rename of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Rename00 +static int tolua_AllToLua_cFile_Rename00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_OrigPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_NewPath = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + bool tolua_ret = (bool) cFile::Rename(a_OrigPath,a_NewPath); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_OrigPath); + tolua_pushcppstring(tolua_S,(const char*)a_NewPath); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Rename'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Copy of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Copy00 +static int tolua_AllToLua_cFile_Copy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_SrcFileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_DstFileName = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + bool tolua_ret = (bool) cFile::Copy(a_SrcFileName,a_DstFileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_SrcFileName); + tolua_pushcppstring(tolua_S,(const char*)a_DstFileName); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Copy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFolder of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFolder00 +static int tolua_AllToLua_cFile_IsFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::IsFolder(a_Path); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Path); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFile of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFile00 +static int tolua_AllToLua_cFile_IsFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::IsFile(a_Path); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Path); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSize of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_GetSize00 +static int tolua_AllToLua_cFile_GetSize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + int tolua_ret = (int) cFile::GetSize(a_FileName); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CreateFolder of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_CreateFolder00 +static int tolua_AllToLua_cFile_CreateFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FolderPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::CreateFolder(a_FolderPath); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FolderPath); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CreateFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: BlockStringToType */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_BlockStringToType00 +static int tolua_AllToLua_BlockStringToType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_BlockTypeString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + unsigned char tolua_ret = ( unsigned char) BlockStringToType(a_BlockTypeString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_BlockTypeString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BlockStringToType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToItem00 +static int tolua_AllToLua_StringToItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_ItemTypeString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + cItem* a_Item = ((cItem*) tolua_tousertype(tolua_S,2,0)); + { + bool tolua_ret = (bool) StringToItem(a_ItemTypeString,*a_Item); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_ItemTypeString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemToString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemToString00 +static int tolua_AllToLua_ItemToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,1,0)); + { + AString tolua_ret = (AString) ItemToString(*a_Item); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ItemToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemTypeToString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemTypeToString00 +static int tolua_AllToLua_ItemTypeToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + AString tolua_ret = (AString) ItemTypeToString(a_ItemType); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ItemTypeToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemToFullString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemToFullString00 +static int tolua_AllToLua_ItemToFullString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,1,0)); + { + AString tolua_ret = (AString) ItemToFullString(*a_Item); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ItemToFullString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToBiome */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToBiome00 +static int tolua_AllToLua_StringToBiome00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_BiomeString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + EMCSBiome tolua_ret = (EMCSBiome) StringToBiome(a_BiomeString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_BiomeString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToBiome'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToMobType */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToMobType00 +static int tolua_AllToLua_StringToMobType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_MobString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + int tolua_ret = (int) StringToMobType(a_MobString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_MobString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToDimension */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToDimension00 +static int tolua_AllToLua_StringToDimension00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_DimensionString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + eDimension tolua_ret = (eDimension) StringToDimension(a_DimensionString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_DimensionString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToDimension'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DamageTypeToString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_DamageTypeToString00 +static int tolua_AllToLua_DamageTypeToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,1,0)); + { + AString tolua_ret = (AString) DamageTypeToString(a_DamageType); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageTypeToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToDamageType */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToDamageType00 +static int tolua_AllToLua_StringToDamageType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_DamageString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + eDamageType tolua_ret = (eDamageType) StringToDamageType(a_DamageString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_DamageString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToDamageType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetIniItemSet */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_GetIniItemSet00 +static int tolua_AllToLua_GetIniItemSet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err)) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isstring(tolua_S,3,0,&tolua_err) || + !tolua_isstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* a_IniFile = ((cIniFile*) tolua_tousertype(tolua_S,1,0)); + const char* a_Section = ((const char*) tolua_tostring(tolua_S,2,0)); + const char* a_Key = ((const char*) tolua_tostring(tolua_S,3,0)); + const char* a_Default = ((const char*) tolua_tostring(tolua_S,4,0)); + { + cItem tolua_ret = (cItem) GetIniItemSet(*a_IniFile,a_Section,a_Key,a_Default); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIniItemSet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: TrimString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_TrimString00 +static int tolua_AllToLua_TrimString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString str = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + AString tolua_ret = (AString) TrimString(str); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)str); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TrimString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: NoCaseCompare */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_NoCaseCompare00 +static int tolua_AllToLua_NoCaseCompare00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString s1 = ((const AString) tolua_tocppstring(tolua_S,1,0)); + const AString s2 = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + int tolua_ret = (int) NoCaseCompare(s1,s2); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)s1); + tolua_pushcppstring(tolua_S,(const char*)s2); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'NoCaseCompare'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ReplaceString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ReplaceString00 +static int tolua_AllToLua_ReplaceString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + AString iHayStack = ((AString) tolua_tocppstring(tolua_S,1,0)); + const AString iNeedle = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString iReplaceWith = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + ReplaceString(iHayStack,iNeedle,iReplaceWith); + tolua_pushcppstring(tolua_S,(const char*)iHayStack); + tolua_pushcppstring(tolua_S,(const char*)iNeedle); + tolua_pushcppstring(tolua_S,(const char*)iReplaceWith); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReplaceString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: EscapeString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_EscapeString00 +static int tolua_AllToLua_EscapeString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + AString tolua_ret = (AString) EscapeString(a_Message); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'EscapeString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StripColorCodes */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StripColorCodes00 +static int tolua_AllToLua_StripColorCodes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + AString tolua_ret = (AString) StripColorCodes(a_Message); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StripColorCodes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockLightValue */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockLightValue +static int tolua_get_AllToLua_g_BlockLightValue(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)g_BlockLightValue[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockLightValue */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockLightValue +static int tolua_set_AllToLua_g_BlockLightValue(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockLightValue[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockSpreadLightFalloff */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockSpreadLightFalloff +static int tolua_get_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)g_BlockSpreadLightFalloff[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockSpreadLightFalloff */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockSpreadLightFalloff +static int tolua_set_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockSpreadLightFalloff[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockTransparent */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockTransparent +static int tolua_get_AllToLua_g_BlockTransparent(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockTransparent[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockTransparent */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockTransparent +static int tolua_set_AllToLua_g_BlockTransparent(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockTransparent[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockOneHitDig */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockOneHitDig +static int tolua_get_AllToLua_g_BlockOneHitDig(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockOneHitDig[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockOneHitDig */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockOneHitDig +static int tolua_set_AllToLua_g_BlockOneHitDig(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockOneHitDig[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockPistonBreakable */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockPistonBreakable +static int tolua_get_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockPistonBreakable[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockPistonBreakable */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockPistonBreakable +static int tolua_set_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockPistonBreakable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockIsSnowable */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsSnowable +static int tolua_get_AllToLua_g_BlockIsSnowable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockIsSnowable[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockIsSnowable */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsSnowable +static int tolua_set_AllToLua_g_BlockIsSnowable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockIsSnowable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockRequiresSpecialTool */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockRequiresSpecialTool +static int tolua_get_AllToLua_g_BlockRequiresSpecialTool(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockRequiresSpecialTool[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockRequiresSpecialTool */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockRequiresSpecialTool +static int tolua_set_AllToLua_g_BlockRequiresSpecialTool(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockRequiresSpecialTool[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockIsSolid */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsSolid +static int tolua_get_AllToLua_g_BlockIsSolid(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockIsSolid[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockIsSolid */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsSolid +static int tolua_set_AllToLua_g_BlockIsSolid(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockIsSolid[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockIsTorchPlaceable */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsTorchPlaceable +static int tolua_get_AllToLua_g_BlockIsTorchPlaceable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockIsTorchPlaceable[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockIsTorchPlaceable */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsTorchPlaceable +static int tolua_set_AllToLua_g_BlockIsTorchPlaceable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockIsTorchPlaceable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ClickActionToString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ClickActionToString00 +static int tolua_AllToLua_ClickActionToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + eClickAction a_ClickAction = ((eClickAction) (int) tolua_tonumber(tolua_S,1,0)); + { + const char* tolua_ret = (const char*) ClickActionToString(a_ClickAction); + tolua_pushstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ClickActionToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: IsValidBlock */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_IsValidBlock00 +static int tolua_AllToLua_IsValidBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_BlockType = ((int) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) IsValidBlock(a_BlockType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsValidBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: IsValidItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_IsValidItem00 +static int tolua_AllToLua_IsValidItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_ItemType = ((int) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) IsValidItem(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsValidItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: AddFaceDirection */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_AddFaceDirection00 +static int tolua_AllToLua_AddFaceDirection00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_BlockX = ((int) tolua_tonumber(tolua_S,1,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0)); + char a_BlockFace = ((char) tolua_tonumber(tolua_S,4,0)); + bool a_bInverse = ((bool) tolua_toboolean(tolua_S,5,false)); + { + AddFaceDirection(a_BlockX,a_BlockY,a_BlockZ,a_BlockFace,a_bInverse); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockX); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockY); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockZ); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddFaceDirection'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsPickaxe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsPickaxe00 +static int tolua_AllToLua_ItemCategory_IsPickaxe00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsPickaxe(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPickaxe'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsAxe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsAxe00 +static int tolua_AllToLua_ItemCategory_IsAxe00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsAxe(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsAxe'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsSword */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsSword00 +static int tolua_AllToLua_ItemCategory_IsSword00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsSword(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSword'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsHoe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsHoe00 +static int tolua_AllToLua_ItemCategory_IsHoe00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsHoe(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsHoe'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsShovel */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsShovel00 +static int tolua_AllToLua_ItemCategory_IsShovel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsShovel(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsShovel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsTool */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsTool00 +static int tolua_AllToLua_ItemCategory_IsTool00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsTool(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsTool'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsHelmet */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsHelmet00 +static int tolua_AllToLua_ItemCategory_IsHelmet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsHelmet(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsHelmet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsChestPlate */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsChestPlate00 +static int tolua_AllToLua_ItemCategory_IsChestPlate00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsChestPlate(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsChestPlate'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsLeggings */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsLeggings00 +static int tolua_AllToLua_ItemCategory_IsLeggings00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsLeggings(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsLeggings'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsBoots */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsBoots00 +static int tolua_AllToLua_ItemCategory_IsBoots00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsBoots(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsBoots'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsArmor */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsArmor00 +static int tolua_AllToLua_ItemCategory_IsArmor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsArmor(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsArmor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetTime */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_GetTime00 +static int tolua_AllToLua_GetTime00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnoobj(tolua_S,1,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + unsigned int tolua_ret = (unsigned int) GetTime(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTime'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetChar */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_GetChar00 +static int tolua_AllToLua_GetChar00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + std::string a_Str = ((std::string) tolua_tocppstring(tolua_S,1,0)); + unsigned int a_Idx = ((unsigned int) tolua_tonumber(tolua_S,2,0)); + { + std::string tolua_ret = (std::string) GetChar(a_Str,a_Idx); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Str); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChar'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Color of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Color +static int tolua_get_cChatColor_Color(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Color); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Delimiter of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Delimiter +static int tolua_get_cChatColor_Delimiter(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Delimiter); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Black of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Black +static int tolua_get_cChatColor_Black(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Black); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Navy of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Navy +static int tolua_get_cChatColor_Navy(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Navy); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Green of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Green +static int tolua_get_cChatColor_Green(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Green); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Blue of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Blue +static int tolua_get_cChatColor_Blue(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Blue); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Red of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Red +static int tolua_get_cChatColor_Red(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Red); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Purple of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Purple +static int tolua_get_cChatColor_Purple(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Purple); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Gold of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Gold +static int tolua_get_cChatColor_Gold(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Gold); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: LightGray of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightGray +static int tolua_get_cChatColor_LightGray(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightGray); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Gray of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Gray +static int tolua_get_cChatColor_Gray(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Gray); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: DarkPurple of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_DarkPurple +static int tolua_get_cChatColor_DarkPurple(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::DarkPurple); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: LightGreen of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightGreen +static int tolua_get_cChatColor_LightGreen(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightGreen); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: LightBlue of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightBlue +static int tolua_get_cChatColor_LightBlue(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightBlue); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Rose of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Rose +static int tolua_get_cChatColor_Rose(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Rose); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: LightPurple of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightPurple +static int tolua_get_cChatColor_LightPurple(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightPurple); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Yellow of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Yellow +static int tolua_get_cChatColor_Yellow(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Yellow); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: White of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_White +static int tolua_get_cChatColor_White(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::White); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Random of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Random +static int tolua_get_cChatColor_Random(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Random); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Bold of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Bold +static int tolua_get_cChatColor_Bold(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Bold); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Strikethrough of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Strikethrough +static int tolua_get_cChatColor_Strikethrough(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Strikethrough); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Underlined of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Underlined +static int tolua_get_cChatColor_Underlined(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Underlined); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Italic of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Italic +static int tolua_get_cChatColor_Italic(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Italic); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Plain of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Plain +static int tolua_get_cChatColor_Plain(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Plain); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MakeColor of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChatColor_MakeColor00 +static int tolua_AllToLua_cChatColor_MakeColor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cChatColor",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + char a_Color = ((char) tolua_tonumber(tolua_S,2,0)); + { + const std::string tolua_ret = (const std::string) cChatColor::MakeColor(a_Color); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MakeColor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPlayer of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetPlayer00 +static int tolua_AllToLua_cClientHandle_GetPlayer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPlayer'", NULL); +#endif + { + cPlayer* tolua_ret = (cPlayer*) self->GetPlayer(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPlayer"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPlayer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Kick of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_Kick00 +static int tolua_AllToLua_cClientHandle_Kick00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); + const AString a_Reason = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Kick'", NULL); +#endif + { + self->Kick(a_Reason); + tolua_pushcppstring(tolua_S,(const char*)a_Reason); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Kick'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SendBlockChange of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SendBlockChange00 +static int tolua_AllToLua_cClientHandle_SendBlockChange00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendBlockChange'", NULL); +#endif + { + self->SendBlockChange(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SendBlockChange'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetUsername of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetUsername00 +static int tolua_AllToLua_cClientHandle_GetUsername00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUsername'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetUsername(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetUsername'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUsername of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SetUsername00 +static int tolua_AllToLua_cClientHandle_SetUsername00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); + const AString a_Username = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUsername'", NULL); +#endif + { + self->SetUsername(a_Username); + tolua_pushcppstring(tolua_S,(const char*)a_Username); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUsername'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPing of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetPing00 +static int tolua_AllToLua_cClientHandle_GetPing00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPing'", NULL); +#endif + { + short tolua_ret = (short) self->GetPing(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPing'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetViewDistance of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SetViewDistance00 +static int tolua_AllToLua_cClientHandle_SetViewDistance00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); + int a_ViewDistance = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetViewDistance'", NULL); +#endif + { + self->SetViewDistance(a_ViewDistance); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetViewDistance'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetViewDistance of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetViewDistance00 +static int tolua_AllToLua_cClientHandle_GetViewDistance00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetViewDistance'", NULL); +#endif + { + int tolua_ret = (int) self->GetViewDistance(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetViewDistance'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetUniqueID of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetUniqueID00 +static int tolua_AllToLua_cClientHandle_GetUniqueID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUniqueID'", NULL); +#endif + { + int tolua_ret = (int) self->GetUniqueID(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetUniqueID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: DamageType of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_DamageType +static int tolua_get_TakeDamageInfo_DamageType(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'DamageType'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->DamageType); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: DamageType of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_DamageType +static int tolua_set_TakeDamageInfo_DamageType(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'DamageType'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Attacker of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_Attacker_ptr +static int tolua_get_TakeDamageInfo_Attacker_ptr(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Attacker'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)self->Attacker,"cEntity"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Attacker of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_Attacker_ptr +static int tolua_set_TakeDamageInfo_Attacker_ptr(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Attacker'",NULL); + if (!tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Attacker = ((cEntity*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: RawDamage of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_RawDamage +static int tolua_get_TakeDamageInfo_RawDamage(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RawDamage'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->RawDamage); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: RawDamage of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_RawDamage +static int tolua_set_TakeDamageInfo_RawDamage(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RawDamage'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->RawDamage = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: FinalDamage of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_FinalDamage +static int tolua_get_TakeDamageInfo_FinalDamage(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'FinalDamage'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->FinalDamage); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: FinalDamage of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_FinalDamage +static int tolua_set_TakeDamageInfo_FinalDamage(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'FinalDamage'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->FinalDamage = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Knockback of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_Knockback +static int tolua_get_TakeDamageInfo_Knockback(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Knockback'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->Knockback,"Vector3d"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Knockback of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_Knockback +static int tolua_set_TakeDamageInfo_Knockback(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Knockback'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3d",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Knockback = *((Vector3d*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEntityType of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEntityType00 +static int tolua_AllToLua_cEntity_GetEntityType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEntityType'", NULL); +#endif + { + cEntity::eEntityType tolua_ret = (cEntity::eEntityType) self->GetEntityType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEntityType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsPlayer of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsPlayer00 +static int tolua_AllToLua_cEntity_IsPlayer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPlayer'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsPlayer(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPlayer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsPickup of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsPickup00 +static int tolua_AllToLua_cEntity_IsPickup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPickup'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsPickup(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPickup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsMob of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMob00 +static int tolua_AllToLua_cEntity_IsMob00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsMob'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsMob(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsMob'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFallingBlock of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsFallingBlock00 +static int tolua_AllToLua_cEntity_IsFallingBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFallingBlock'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsFallingBlock(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFallingBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsMinecart of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMinecart00 +static int tolua_AllToLua_cEntity_IsMinecart00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsMinecart'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsMinecart(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsMinecart'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsBoat of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsBoat00 +static int tolua_AllToLua_cEntity_IsBoat00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsBoat'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsBoat(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsBoat'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsTNT of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsTNT00 +static int tolua_AllToLua_cEntity_IsTNT00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsTNT'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsTNT(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsTNT'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsProjectile of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsProjectile00 +static int tolua_AllToLua_cEntity_IsProjectile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsProjectile'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsProjectile(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsProjectile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsA of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsA00 +static int tolua_AllToLua_cEntity_IsA00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); + const char* a_ClassName = ((const char*) tolua_tostring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsA'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsA(a_ClassName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsA'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetClass of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetClass00 +static int tolua_AllToLua_cEntity_GetClass00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetClass'", NULL); +#endif + { + const char* tolua_ret = (const char*) self->GetClass(); + tolua_pushstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetClass'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetClassStatic of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetClassStatic00 +static int tolua_AllToLua_cEntity_GetClassStatic00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + const char* tolua_ret = (const char*) cEntity::GetClassStatic(); + tolua_pushstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetClassStatic'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetParentClass of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetParentClass00 +static int tolua_AllToLua_cEntity_GetParentClass00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetParentClass'", NULL); +#endif + { + const char* tolua_ret = (const char*) self->GetParentClass(); + tolua_pushstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetParentClass'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWorld of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetWorld00 +static int tolua_AllToLua_cEntity_GetWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); +#endif + { + cWorld* tolua_ret = (cWorld*) self->GetWorld(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeadYaw of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHeadYaw00 +static int tolua_AllToLua_cEntity_GetHeadYaw00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeadYaw'", NULL); +#endif + { + double tolua_ret = (double) self->GetHeadYaw(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeadYaw'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHeight00 +static int tolua_AllToLua_cEntity_GetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + double tolua_ret = (double) self->GetHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMass of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetMass00 +static int tolua_AllToLua_cEntity_GetMass00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMass'", NULL); +#endif + { + double tolua_ret = (double) self->GetMass(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMass'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosition00 +static int tolua_AllToLua_cEntity_GetPosition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosition'", NULL); +#endif + { + const Vector3d& tolua_ret = (const Vector3d&) self->GetPosition(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosX00 +static int tolua_AllToLua_cEntity_GetPosX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosX'", NULL); +#endif + { + double tolua_ret = (double) self->GetPosX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosY00 +static int tolua_AllToLua_cEntity_GetPosY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosY'", NULL); +#endif + { + double tolua_ret = (double) self->GetPosY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosZ00 +static int tolua_AllToLua_cEntity_GetPosZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosZ'", NULL); +#endif + { + double tolua_ret = (double) self->GetPosZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRot of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRot00 +static int tolua_AllToLua_cEntity_GetRot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRot'", NULL); +#endif + { + const Vector3d& tolua_ret = (const Vector3d&) self->GetRot(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRotation of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRotation00 +static int tolua_AllToLua_cEntity_GetRotation00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRotation'", NULL); +#endif + { + double tolua_ret = (double) self->GetRotation(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRotation'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetYaw of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetYaw00 +static int tolua_AllToLua_cEntity_GetYaw00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetYaw'", NULL); +#endif + { + double tolua_ret = (double) self->GetYaw(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetYaw'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPitch of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPitch00 +static int tolua_AllToLua_cEntity_GetPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL); +#endif + { + double tolua_ret = (double) self->GetPitch(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRoll of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRoll00 +static int tolua_AllToLua_cEntity_GetRoll00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRoll'", NULL); +#endif + { + double tolua_ret = (double) self->GetRoll(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRoll'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLookVector of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetLookVector00 +static int tolua_AllToLua_cEntity_GetLookVector00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLookVector'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->GetLookVector(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLookVector'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeed00 +static int tolua_AllToLua_cEntity_GetSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeed'", NULL); +#endif + { + const Vector3d& tolua_ret = (const Vector3d&) self->GetSpeed(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpeedX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedX00 +static int tolua_AllToLua_cEntity_GetSpeedX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedX'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpeedX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpeedX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpeedY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedY00 +static int tolua_AllToLua_cEntity_GetSpeedY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedY'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpeedY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpeedY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpeedZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedZ00 +static int tolua_AllToLua_cEntity_GetSpeedZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedZ'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpeedZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpeedZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWidth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetWidth00 +static int tolua_AllToLua_cEntity_GetWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL); +#endif + { + double tolua_ret = (double) self->GetWidth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkX00 +static int tolua_AllToLua_cEntity_GetChunkX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkZ00 +static int tolua_AllToLua_cEntity_GetChunkZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHeadYaw of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHeadYaw00 +static int tolua_AllToLua_cEntity_SetHeadYaw00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_HeadYaw = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeadYaw'", NULL); +#endif + { + self->SetHeadYaw(a_HeadYaw); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHeadYaw'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHeight of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHeight00 +static int tolua_AllToLua_cEntity_SetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Height = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeight'", NULL); +#endif + { + self->SetHeight(a_Height); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetMass of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetMass00 +static int tolua_AllToLua_cEntity_SetMass00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Mass = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMass'", NULL); +#endif + { + self->SetMass(a_Mass); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetMass'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosX00 +static int tolua_AllToLua_cEntity_SetPosX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosX'", NULL); +#endif + { + self->SetPosX(a_PosX); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPosX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosY00 +static int tolua_AllToLua_cEntity_SetPosY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosY = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosY'", NULL); +#endif + { + self->SetPosY(a_PosY); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPosY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosZ00 +static int tolua_AllToLua_cEntity_SetPosZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosZ = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosZ'", NULL); +#endif + { + self->SetPosZ(a_PosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPosZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosition00 +static int tolua_AllToLua_cEntity_SetPosition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosition'", NULL); +#endif + { + self->SetPosition(a_PosX,a_PosY,a_PosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPosition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosition01 +static int tolua_AllToLua_cEntity_SetPosition01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosition'", NULL); +#endif + { + self->SetPosition(*a_Pos); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_SetPosition00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRot of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRot00 +static int tolua_AllToLua_cEntity_SetRot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3f* a_Rot = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRot'", NULL); +#endif + { + self->SetRot(*a_Rot); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRotation of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRotation00 +static int tolua_AllToLua_cEntity_SetRotation00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Rotation = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRotation'", NULL); +#endif + { + self->SetRotation(a_Rotation); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRotation'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetYaw of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetYaw00 +static int tolua_AllToLua_cEntity_SetYaw00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Yaw = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetYaw'", NULL); +#endif + { + self->SetYaw(a_Yaw); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetYaw'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPitch of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPitch00 +static int tolua_AllToLua_cEntity_SetPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Pitch = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitch'", NULL); +#endif + { + self->SetPitch(a_Pitch); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRoll of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRoll00 +static int tolua_AllToLua_cEntity_SetRoll00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Roll = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRoll'", NULL); +#endif + { + self->SetRoll(a_Roll); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRoll'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed00 +static int tolua_AllToLua_cEntity_SetSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_SpeedX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_SpeedY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_SpeedZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL); +#endif + { + self->SetSpeed(a_SpeedX,a_SpeedY,a_SpeedZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed01 +static int tolua_AllToLua_cEntity_SetSpeed01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Speed = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL); +#endif + { + self->SetSpeed(*a_Speed); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_SetSpeed00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeedX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedX00 +static int tolua_AllToLua_cEntity_SetSpeedX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_SpeedX = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedX'", NULL); +#endif + { + self->SetSpeedX(a_SpeedX); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSpeedX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeedY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedY00 +static int tolua_AllToLua_cEntity_SetSpeedY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_SpeedY = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedY'", NULL); +#endif + { + self->SetSpeedY(a_SpeedY); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSpeedY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeedZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedZ00 +static int tolua_AllToLua_cEntity_SetSpeedZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_SpeedZ = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedZ'", NULL); +#endif + { + self->SetSpeedZ(a_SpeedZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSpeedZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetWidth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetWidth00 +static int tolua_AllToLua_cEntity_SetWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Width = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWidth'", NULL); +#endif + { + self->SetWidth(a_Width); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosX00 +static int tolua_AllToLua_cEntity_AddPosX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddPosX = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosX'", NULL); +#endif + { + self->AddPosX(a_AddPosX); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPosX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosY00 +static int tolua_AllToLua_cEntity_AddPosY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddPosY = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosY'", NULL); +#endif + { + self->AddPosY(a_AddPosY); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPosY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosZ00 +static int tolua_AllToLua_cEntity_AddPosZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddPosZ = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosZ'", NULL); +#endif + { + self->AddPosZ(a_AddPosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPosZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosition00 +static int tolua_AllToLua_cEntity_AddPosition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddPosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_AddPosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_AddPosZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosition'", NULL); +#endif + { + self->AddPosition(a_AddPosX,a_AddPosY,a_AddPosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPosition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosition01 +static int tolua_AllToLua_cEntity_AddPosition01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_AddPos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosition'", NULL); +#endif + { + self->AddPosition(*a_AddPos); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_AddPosition00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeed00 +static int tolua_AllToLua_cEntity_AddSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddSpeedX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_AddSpeedY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_AddSpeedZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeed'", NULL); +#endif + { + self->AddSpeed(a_AddSpeedX,a_AddSpeedY,a_AddSpeedZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeed01 +static int tolua_AllToLua_cEntity_AddSpeed01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_AddSpeed = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeed'", NULL); +#endif + { + self->AddSpeed(*a_AddSpeed); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_AddSpeed00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeedX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedX00 +static int tolua_AllToLua_cEntity_AddSpeedX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddSpeedX = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedX'", NULL); +#endif + { + self->AddSpeedX(a_AddSpeedX); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddSpeedX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeedY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedY00 +static int tolua_AllToLua_cEntity_AddSpeedY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddSpeedY = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedY'", NULL); +#endif + { + self->AddSpeedY(a_AddSpeedY); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddSpeedY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeedZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedZ00 +static int tolua_AllToLua_cEntity_AddSpeedZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddSpeedZ = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedZ'", NULL); +#endif + { + self->AddSpeedZ(a_AddSpeedZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddSpeedZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SteerVehicle of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SteerVehicle00 +static int tolua_AllToLua_cEntity_SteerVehicle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + float a_Forward = ((float) tolua_tonumber(tolua_S,2,0)); + float a_Sideways = ((float) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SteerVehicle'", NULL); +#endif + { + self->SteerVehicle(a_Forward,a_Sideways); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SteerVehicle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetUniqueID of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetUniqueID00 +static int tolua_AllToLua_cEntity_GetUniqueID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUniqueID'", NULL); +#endif + { + int tolua_ret = (int) self->GetUniqueID(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetUniqueID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsDestroyed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsDestroyed00 +static int tolua_AllToLua_cEntity_IsDestroyed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDestroyed'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsDestroyed(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsDestroyed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Destroy of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_Destroy00 +static int tolua_AllToLua_cEntity_Destroy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,1,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + bool a_ShouldBroadcast = ((bool) tolua_toboolean(tolua_S,2,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Destroy'", NULL); +#endif + { + self->Destroy(a_ShouldBroadcast); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Destroy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TakeDamage of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage00 +static int tolua_AllToLua_cEntity_TakeDamage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL); +#endif + { + self->TakeDamage(*a_Attacker); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TakeDamage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TakeDamage of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage01 +static int tolua_AllToLua_cEntity_TakeDamage01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isusertype(tolua_S,3,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0)); + cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,3,0)); + int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0)); + double a_KnockbackAmount = ((double) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL); +#endif + { + self->TakeDamage(a_DamageType,a_Attacker,a_RawDamage,a_KnockbackAmount); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_TakeDamage00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TakeDamage of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage02 +static int tolua_AllToLua_cEntity_TakeDamage02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isusertype(tolua_S,3,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0)); + cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,3,0)); + int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0)); + int a_FinalDamage = ((int) tolua_tonumber(tolua_S,5,0)); + double a_KnockbackAmount = ((double) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL); +#endif + { + self->TakeDamage(a_DamageType,a_Attacker,a_RawDamage,a_FinalDamage,a_KnockbackAmount); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_TakeDamage01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGravity of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetGravity00 +static int tolua_AllToLua_cEntity_GetGravity00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGravity'", NULL); +#endif + { + float tolua_ret = (float) self->GetGravity(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGravity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetGravity of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetGravity00 +static int tolua_AllToLua_cEntity_SetGravity00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + float a_Gravity = ((float) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGravity'", NULL); +#endif + { + self->SetGravity(a_Gravity); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetGravity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRotationFromSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRotationFromSpeed00 +static int tolua_AllToLua_cEntity_SetRotationFromSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRotationFromSpeed'", NULL); +#endif + { + self->SetRotationFromSpeed(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRotationFromSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPitchFromSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPitchFromSpeed00 +static int tolua_AllToLua_cEntity_SetPitchFromSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitchFromSpeed'", NULL); +#endif + { + self->SetPitchFromSpeed(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPitchFromSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRawDamageAgainst of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRawDamageAgainst00 +static int tolua_AllToLua_cEntity_GetRawDamageAgainst00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* a_Receiver = ((const cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRawDamageAgainst'", NULL); +#endif + { + int tolua_ret = (int) self->GetRawDamageAgainst(*a_Receiver); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRawDamageAgainst'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetArmorCoverAgainst of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetArmorCoverAgainst00 +static int tolua_AllToLua_cEntity_GetArmorCoverAgainst00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* a_Attacker = ((const cEntity*) tolua_tousertype(tolua_S,2,0)); + eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,3,0)); + int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorCoverAgainst'", NULL); +#endif + { + int tolua_ret = (int) self->GetArmorCoverAgainst(a_Attacker,a_DamageType,a_RawDamage); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetArmorCoverAgainst'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetKnockbackAmountAgainst of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00 +static int tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* a_Receiver = ((const cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKnockbackAmountAgainst'", NULL); +#endif + { + double tolua_ret = (double) self->GetKnockbackAmountAgainst(*a_Receiver); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetKnockbackAmountAgainst'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedWeapon of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedWeapon00 +static int tolua_AllToLua_cEntity_GetEquippedWeapon00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedWeapon'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedWeapon(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedWeapon'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedHelmet of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedHelmet00 +static int tolua_AllToLua_cEntity_GetEquippedHelmet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedHelmet'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedHelmet(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedHelmet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedChestplate of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedChestplate00 +static int tolua_AllToLua_cEntity_GetEquippedChestplate00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedChestplate'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedChestplate(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedChestplate'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedLeggings of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedLeggings00 +static int tolua_AllToLua_cEntity_GetEquippedLeggings00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedLeggings'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedLeggings(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedLeggings'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedBoots of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedBoots00 +static int tolua_AllToLua_cEntity_GetEquippedBoots00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedBoots'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedBoots(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedBoots'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: KilledBy of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_KilledBy00 +static int tolua_AllToLua_cEntity_KilledBy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + cEntity* a_Killer = ((cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'KilledBy'", NULL); +#endif + { + self->KilledBy(a_Killer); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'KilledBy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Heal of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_Heal00 +static int tolua_AllToLua_cEntity_Heal00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + int a_HitPoints = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Heal'", NULL); +#endif + { + self->Heal(a_HitPoints); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Heal'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHealth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHealth00 +static int tolua_AllToLua_cEntity_GetHealth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHealth'", NULL); +#endif + { + int tolua_ret = (int) self->GetHealth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHealth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHealth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHealth00 +static int tolua_AllToLua_cEntity_SetHealth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + int a_Health = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHealth'", NULL); +#endif + { + self->SetHealth(a_Health); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHealth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetMaxHealth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetMaxHealth00 +static int tolua_AllToLua_cEntity_SetMaxHealth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + int a_MaxHealth = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxHealth'", NULL); +#endif + { + self->SetMaxHealth(a_MaxHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetMaxHealth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxHealth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetMaxHealth00 +static int tolua_AllToLua_cEntity_GetMaxHealth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxHealth'", NULL); +#endif + { + int tolua_ret = (int) self->GetMaxHealth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxHealth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: StartBurning of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_StartBurning00 +static int tolua_AllToLua_cEntity_StartBurning00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + int a_TicksLeftBurning = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'StartBurning'", NULL); +#endif + { + self->StartBurning(a_TicksLeftBurning); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StartBurning'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: StopBurning of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_StopBurning00 +static int tolua_AllToLua_cEntity_StopBurning00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'StopBurning'", NULL); +#endif + { + self->StopBurning(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StopBurning'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TeleportToEntity of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TeleportToEntity00 +static int tolua_AllToLua_cEntity_TeleportToEntity00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + cEntity* a_Entity = ((cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TeleportToEntity'", NULL); +#endif + { + self->TeleportToEntity(*a_Entity); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TeleportToEntity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TeleportToCoords of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TeleportToCoords00 +static int tolua_AllToLua_cEntity_TeleportToCoords00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TeleportToCoords'", NULL); +#endif + { + self->TeleportToCoords(a_PosX,a_PosY,a_PosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TeleportToCoords'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsOnFire of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsOnFire00 +static int tolua_AllToLua_cEntity_IsOnFire00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsOnFire'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsOnFire(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsOnFire'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCrouched of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsCrouched00 +static int tolua_AllToLua_cEntity_IsCrouched00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCrouched'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCrouched(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCrouched'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsRiding of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsRiding00 +static int tolua_AllToLua_cEntity_IsRiding00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsRiding'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsRiding(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsRiding'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSprinting of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsSprinting00 +static int tolua_AllToLua_cEntity_IsSprinting00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSprinting'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSprinting(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSprinting'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsRclking of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsRclking00 +static int tolua_AllToLua_cEntity_IsRclking00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsRclking'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsRclking(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsRclking'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInvisible of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsInvisible00 +static int tolua_AllToLua_cEntity_IsInvisible00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInvisible'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInvisible(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInvisible'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetExperience of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetExperience00 +static int tolua_AllToLua_cPlayer_SetExperience00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_XpTotal = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetExperience'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetExperience(a_XpTotal); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetExperience'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddExperience of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddExperience00 +static int tolua_AllToLua_cPlayer_AddExperience00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_Xp_delta = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddExperience'", NULL); +#endif + { + int tolua_ret = (int) self->AddExperience(a_Xp_delta); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddExperience'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: XpGetTotal of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_XpGetTotal00 +static int tolua_AllToLua_cPlayer_XpGetTotal00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'XpGetTotal'", NULL); +#endif + { + int tolua_ret = (int) self->XpGetTotal(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'XpGetTotal'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: XpGetLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_XpGetLevel00 +static int tolua_AllToLua_cPlayer_XpGetLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'XpGetLevel'", NULL); +#endif + { + int tolua_ret = (int) self->XpGetLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'XpGetLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: XpGetPercentage of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_XpGetPercentage00 +static int tolua_AllToLua_cPlayer_XpGetPercentage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'XpGetPercentage'", NULL); +#endif + { + float tolua_ret = (float) self->XpGetPercentage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'XpGetPercentage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEyeHeight of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyeHeight00 +static int tolua_AllToLua_cPlayer_GetEyeHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEyeHeight'", NULL); +#endif + { + double tolua_ret = (double) self->GetEyeHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEyeHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEyePosition of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyePosition00 +static int tolua_AllToLua_cPlayer_GetEyePosition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEyePosition'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->GetEyePosition(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEyePosition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsOnGround of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsOnGround00 +static int tolua_AllToLua_cPlayer_IsOnGround00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsOnGround'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsOnGround(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsOnGround'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetStance of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetStance00 +static int tolua_AllToLua_cPlayer_GetStance00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStance'", NULL); +#endif + { + const double tolua_ret = (const double) self->GetStance(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetStance'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetInventory of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetInventory00 +static int tolua_AllToLua_cPlayer_GetInventory00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventory'", NULL); +#endif + { + cInventory& tolua_ret = (cInventory&) self->GetInventory(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cInventory"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetInventory'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedItem of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEquippedItem00 +static int tolua_AllToLua_cPlayer_GetEquippedItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedItem'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedItem(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetThrowStartPos of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetThrowStartPos00 +static int tolua_AllToLua_cPlayer_GetThrowStartPos00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetThrowStartPos'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->GetThrowStartPos(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetThrowStartPos'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetThrowSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetThrowSpeed00 +static int tolua_AllToLua_cPlayer_GetThrowSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_SpeedCoeff = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetThrowSpeed'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->GetThrowSpeed(a_SpeedCoeff); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetThrowSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGameMode of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetGameMode00 +static int tolua_AllToLua_cPlayer_GetGameMode00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGameMode'", NULL); +#endif + { + eGameMode tolua_ret = (eGameMode) self->GetGameMode(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGameMode'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEffectiveGameMode of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEffectiveGameMode00 +static int tolua_AllToLua_cPlayer_GetEffectiveGameMode00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEffectiveGameMode'", NULL); +#endif + { + eGameMode tolua_ret = (eGameMode) self->GetEffectiveGameMode(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEffectiveGameMode'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetGameMode of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetGameMode00 +static int tolua_AllToLua_cPlayer_SetGameMode00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + eGameMode a_GameMode = ((eGameMode) (int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGameMode'", NULL); +#endif + { + self->SetGameMode(a_GameMode); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetGameMode'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeCreative of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeCreative00 +static int tolua_AllToLua_cPlayer_IsGameModeCreative00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeCreative'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeCreative(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeSurvival of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeSurvival00 +static int tolua_AllToLua_cPlayer_IsGameModeSurvival00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeSurvival'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeSurvival(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeAdventure of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeAdventure00 +static int tolua_AllToLua_cPlayer_IsGameModeAdventure00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeAdventure'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeAdventure(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIP of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetIP00 +static int tolua_AllToLua_cPlayer_GetIP00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIP'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetIP(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIP'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MoveTo of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_MoveTo00 +static int tolua_AllToLua_cPlayer_MoveTo00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_NewPos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MoveTo'", NULL); +#endif + { + self->MoveTo(*a_NewPos); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MoveTo'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWindow of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetWindow00 +static int tolua_AllToLua_cPlayer_GetWindow00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindow'", NULL); +#endif + { + cWindow* tolua_ret = (cWindow*) self->GetWindow(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWindow"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWindow'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CloseWindow of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CloseWindow00 +static int tolua_AllToLua_cPlayer_CloseWindow00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,1,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_CanRefuse = ((bool) tolua_toboolean(tolua_S,2,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CloseWindow'", NULL); +#endif + { + self->CloseWindow(a_CanRefuse); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CloseWindow'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CloseWindowIfID of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CloseWindowIfID00 +static int tolua_AllToLua_cPlayer_CloseWindowIfID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + char a_WindowID = ((char) tolua_tonumber(tolua_S,2,0)); + bool a_CanRefuse = ((bool) tolua_toboolean(tolua_S,3,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CloseWindowIfID'", NULL); +#endif + { + self->CloseWindowIfID(a_WindowID,a_CanRefuse); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CloseWindowIfID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetClientHandle of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetClientHandle00 +static int tolua_AllToLua_cPlayer_GetClientHandle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetClientHandle'", NULL); +#endif + { + cClientHandle* tolua_ret = (cClientHandle*) self->GetClientHandle(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cClientHandle"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetClientHandle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SendMessage of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SendMessage00 +static int tolua_AllToLua_cPlayer_SendMessage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendMessage'", NULL); +#endif + { + self->SendMessage(a_Message); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SendMessage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetName of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetName00 +static int tolua_AllToLua_cPlayer_GetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetName of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetName00 +static int tolua_AllToLua_cPlayer_SetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Name = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL); +#endif + { + self->SetName(a_Name); + tolua_pushcppstring(tolua_S,(const char*)a_Name); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddToGroup of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddToGroup00 +static int tolua_AllToLua_cPlayer_AddToGroup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_GroupName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddToGroup'", NULL); +#endif + { + self->AddToGroup(a_GroupName); + tolua_pushcppstring(tolua_S,(const char*)a_GroupName); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddToGroup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RemoveFromGroup of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_RemoveFromGroup00 +static int tolua_AllToLua_cPlayer_RemoveFromGroup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_GroupName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveFromGroup'", NULL); +#endif + { + self->RemoveFromGroup(a_GroupName); + tolua_pushcppstring(tolua_S,(const char*)a_GroupName); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RemoveFromGroup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CanUseCommand of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CanUseCommand00 +static int tolua_AllToLua_cPlayer_CanUseCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CanUseCommand'", NULL); +#endif + { + bool tolua_ret = (bool) self->CanUseCommand(a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CanUseCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasPermission of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_HasPermission00 +static int tolua_AllToLua_cPlayer_HasPermission00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Permission = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasPermission'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasPermission(a_Permission); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Permission); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasPermission'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInGroup of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsInGroup00 +static int tolua_AllToLua_cPlayer_IsInGroup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Group = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInGroup'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInGroup(a_Group); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Group); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInGroup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetColor of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetColor00 +static int tolua_AllToLua_cPlayer_GetColor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetColor(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetColor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TossItem of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_TossItem00 +static int tolua_AllToLua_cPlayer_TossItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnumber(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_bDraggingItem = ((bool) tolua_toboolean(tolua_S,2,0)); + char a_Amount = ((char) tolua_tonumber(tolua_S,3,1)); + short a_CreateType = ((short) tolua_tonumber(tolua_S,4,0)); + short a_CreateHealth = ((short) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TossItem'", NULL); +#endif + { + self->TossItem(a_bDraggingItem,a_Amount,a_CreateType,a_CreateHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TossItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Heal of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Heal00 +static int tolua_AllToLua_cPlayer_Heal00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_Health = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Heal'", NULL); +#endif + { + self->Heal(a_Health); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Heal'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodLevel00 +static int tolua_AllToLua_cPlayer_GetFoodLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodLevel'", NULL); +#endif + { + int tolua_ret = (int) self->GetFoodLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodSaturationLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodSaturationLevel00 +static int tolua_AllToLua_cPlayer_GetFoodSaturationLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodSaturationLevel'", NULL); +#endif + { + double tolua_ret = (double) self->GetFoodSaturationLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodSaturationLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodTickTimer of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodTickTimer00 +static int tolua_AllToLua_cPlayer_GetFoodTickTimer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodTickTimer'", NULL); +#endif + { + int tolua_ret = (int) self->GetFoodTickTimer(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodTickTimer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodExhaustionLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00 +static int tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodExhaustionLevel'", NULL); +#endif + { + double tolua_ret = (double) self->GetFoodExhaustionLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodExhaustionLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodPoisonedTicksRemaining of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00 +static int tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodPoisonedTicksRemaining'", NULL); +#endif + { + int tolua_ret = (int) self->GetFoodPoisonedTicksRemaining(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodPoisonedTicksRemaining'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetAirLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetAirLevel00 +static int tolua_AllToLua_cPlayer_GetAirLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetAirLevel'", NULL); +#endif + { + int tolua_ret = (int) self->GetAirLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetAirLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSatiated of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSatiated00 +static int tolua_AllToLua_cPlayer_IsSatiated00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSatiated'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSatiated(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSatiated'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodLevel00 +static int tolua_AllToLua_cPlayer_SetFoodLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_FoodLevel = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodLevel'", NULL); +#endif + { + self->SetFoodLevel(a_FoodLevel); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodSaturationLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodSaturationLevel00 +static int tolua_AllToLua_cPlayer_SetFoodSaturationLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_FoodSaturationLevel = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodSaturationLevel'", NULL); +#endif + { + self->SetFoodSaturationLevel(a_FoodSaturationLevel); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodSaturationLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodTickTimer of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodTickTimer00 +static int tolua_AllToLua_cPlayer_SetFoodTickTimer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_FoodTickTimer = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodTickTimer'", NULL); +#endif + { + self->SetFoodTickTimer(a_FoodTickTimer); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodTickTimer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodExhaustionLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00 +static int tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_FoodExhaustionLevel = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodExhaustionLevel'", NULL); +#endif + { + self->SetFoodExhaustionLevel(a_FoodExhaustionLevel); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodExhaustionLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodPoisonedTicksRemaining of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00 +static int tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_FoodPoisonedTicksRemaining = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodPoisonedTicksRemaining'", NULL); +#endif + { + self->SetFoodPoisonedTicksRemaining(a_FoodPoisonedTicksRemaining); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodPoisonedTicksRemaining'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Feed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Feed00 +static int tolua_AllToLua_cPlayer_Feed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_Food = ((int) tolua_tonumber(tolua_S,2,0)); + double a_Saturation = ((double) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Feed'", NULL); +#endif + { + bool tolua_ret = (bool) self->Feed(a_Food,a_Saturation); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Feed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddFoodExhaustion of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddFoodExhaustion00 +static int tolua_AllToLua_cPlayer_AddFoodExhaustion00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_Exhaustion = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddFoodExhaustion'", NULL); +#endif + { + self->AddFoodExhaustion(a_Exhaustion); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddFoodExhaustion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FoodPoison of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_FoodPoison00 +static int tolua_AllToLua_cPlayer_FoodPoison00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_NumTicks = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FoodPoison'", NULL); +#endif + { + self->FoodPoison(a_NumTicks); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FoodPoison'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsEating of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsEating00 +static int tolua_AllToLua_cPlayer_IsEating00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEating'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsEating(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsEating'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Respawn of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Respawn00 +static int tolua_AllToLua_cPlayer_Respawn00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Respawn'", NULL); +#endif + { + self->Respawn(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Respawn'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetVisible of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetVisible00 +static int tolua_AllToLua_cPlayer_SetVisible00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_bVisible = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetVisible'", NULL); +#endif + { + self->SetVisible(a_bVisible); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetVisible'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsVisible of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsVisible00 +static int tolua_AllToLua_cPlayer_IsVisible00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsVisible'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsVisible(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsVisible'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MoveToWorld of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_MoveToWorld00 +static int tolua_AllToLua_cPlayer_MoveToWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const char* a_WorldName = ((const char*) tolua_tostring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MoveToWorld'", NULL); +#endif + { + bool tolua_ret = (bool) self->MoveToWorld(a_WorldName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MoveToWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LoadPermissionsFromDisk of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00 +static int tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadPermissionsFromDisk'", NULL); +#endif + { + self->LoadPermissionsFromDisk(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LoadPermissionsFromDisk'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetMaxSpeed00 +static int tolua_AllToLua_cPlayer_GetMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxSpeed'", NULL); +#endif + { + double tolua_ret = (double) self->GetMaxSpeed(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNormalMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetNormalMaxSpeed00 +static int tolua_AllToLua_cPlayer_GetNormalMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNormalMaxSpeed'", NULL); +#endif + { + double tolua_ret = (double) self->GetNormalMaxSpeed(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNormalMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSprintingMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00 +static int tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSprintingMaxSpeed'", NULL); +#endif + { + double tolua_ret = (double) self->GetSprintingMaxSpeed(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSprintingMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetNormalMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetNormalMaxSpeed00 +static int tolua_AllToLua_cPlayer_SetNormalMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_Speed = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetNormalMaxSpeed'", NULL); +#endif + { + self->SetNormalMaxSpeed(a_Speed); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetNormalMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSprintingMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00 +static int tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_Speed = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSprintingMaxSpeed'", NULL); +#endif + { + self->SetSprintingMaxSpeed(a_Speed); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSprintingMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetCrouch of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetCrouch00 +static int tolua_AllToLua_cPlayer_SetCrouch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_IsCrouched = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetCrouch'", NULL); +#endif + { + self->SetCrouch(a_IsCrouched); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetCrouch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSprint of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetSprint00 +static int tolua_AllToLua_cPlayer_SetSprint00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_IsSprinting = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSprint'", NULL); +#endif + { + self->SetSprint(a_IsSprinting); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSprint'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSwimming of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSwimming00 +static int tolua_AllToLua_cPlayer_IsSwimming00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSwimming'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSwimming(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSwimming'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSubmerged of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSubmerged00 +static int tolua_AllToLua_cPlayer_IsSubmerged00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSubmerged'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSubmerged(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSubmerged'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_new00 +static int tolua_AllToLua_cPickup_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cPickup",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,1,&tolua_err) || + !tolua_isnumber(tolua_S,8,1,&tolua_err) || + !tolua_isnumber(tolua_S,9,1,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); + bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0)); + float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f)); + float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f)); + float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f)); + { + cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_new00_local +static int tolua_AllToLua_cPickup_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cPickup",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,1,&tolua_err) || + !tolua_isnumber(tolua_S,8,1,&tolua_err) || + !tolua_isnumber(tolua_S,9,1,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); + bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0)); + float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f)); + float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f)); + float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f)); + { + cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetItem of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_GetItem00 +static int tolua_AllToLua_cPickup_GetItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPickup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetItem'", NULL); +#endif + { + cItem& tolua_ret = (cItem&) self->GetItem(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CollectedBy of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_CollectedBy00 +static int tolua_AllToLua_cPickup_CollectedBy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPickup",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Dest = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CollectedBy'", NULL); +#endif + { + bool tolua_ret = (bool) self->CollectedBy(a_Dest); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CollectedBy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetAge of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_GetAge00 +static int tolua_AllToLua_cPickup_GetAge00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetAge'", NULL); +#endif + { + int tolua_ret = (int) self->GetAge(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetAge'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCollected of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_IsCollected00 +static int tolua_AllToLua_cPickup_IsCollected00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCollected'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCollected(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCollected'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsPlayerCreated of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_IsPlayerCreated00 +static int tolua_AllToLua_cPickup_IsPlayerCreated00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPlayerCreated'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsPlayerCreated(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPlayerCreated'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetProjectileKind of class cProjectileEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetProjectileKind00 +static int tolua_AllToLua_cProjectileEntity_GetProjectileKind00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetProjectileKind'", NULL); +#endif + { + cProjectileEntity::eKind tolua_ret = (cProjectileEntity::eKind) self->GetProjectileKind(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetProjectileKind'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetCreator of class cProjectileEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetCreator00 +static int tolua_AllToLua_cProjectileEntity_GetCreator00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cProjectileEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cProjectileEntity* self = (cProjectileEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCreator'", NULL); +#endif + { + cEntity* tolua_ret = (cEntity*) self->GetCreator(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEntity"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetCreator'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMCAClassName of class cProjectileEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetMCAClassName00 +static int tolua_AllToLua_cProjectileEntity_GetMCAClassName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMCAClassName'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetMCAClassName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMCAClassName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInGround of class cProjectileEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_IsInGround00 +static int tolua_AllToLua_cProjectileEntity_IsInGround00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInGround'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInGround(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInGround'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPickupState of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_GetPickupState00 +static int tolua_AllToLua_cArrowEntity_GetPickupState00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPickupState'", NULL); +#endif + { + cArrowEntity::ePickupState tolua_ret = (cArrowEntity::ePickupState) self->GetPickupState(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPickupState'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPickupState of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetPickupState00 +static int tolua_AllToLua_cArrowEntity_SetPickupState00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0); + cArrowEntity::ePickupState a_PickupState = ((cArrowEntity::ePickupState) (int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPickupState'", NULL); +#endif + { + self->SetPickupState(a_PickupState); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPickupState'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDamageCoeff of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_GetDamageCoeff00 +static int tolua_AllToLua_cArrowEntity_GetDamageCoeff00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDamageCoeff'", NULL); +#endif + { + double tolua_ret = (double) self->GetDamageCoeff(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDamageCoeff'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetDamageCoeff of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetDamageCoeff00 +static int tolua_AllToLua_cArrowEntity_SetDamageCoeff00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0); + double a_DamageCoeff = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetDamageCoeff'", NULL); +#endif + { + self->SetDamageCoeff(a_DamageCoeff); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetDamageCoeff'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CanPickup of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_CanPickup00 +static int tolua_AllToLua_cArrowEntity_CanPickup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cPlayer",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); + const cPlayer* a_Player = ((const cPlayer*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CanPickup'", NULL); +#endif + { + bool tolua_ret = (bool) self->CanPickup(*a_Player); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CanPickup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCritical of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_IsCritical00 +static int tolua_AllToLua_cArrowEntity_IsCritical00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCritical'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCritical(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCritical'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetIsCritical of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetIsCritical00 +static int tolua_AllToLua_cArrowEntity_SetIsCritical00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0); + bool a_IsCritical = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIsCritical'", NULL); +#endif + { + self->SetIsCritical(a_IsCritical); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetIsCritical'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Get of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_Get00 +static int tolua_AllToLua_cPluginManager_Get00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cPluginManager* tolua_ret = (cPluginManager*) cPluginManager::Get(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPluginManager"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPlugin of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetPlugin00 +static int tolua_AllToLua_cPluginManager_GetPlugin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_Plugin = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPlugin'", NULL); +#endif + { + cPlugin* tolua_ret = (cPlugin*) self->GetPlugin(a_Plugin); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPlugin"); + tolua_pushcppstring(tolua_S,(const char*)a_Plugin); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPlugin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FindPlugins of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_FindPlugins00 +static int tolua_AllToLua_cPluginManager_FindPlugins00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindPlugins'", NULL); +#endif + { + self->FindPlugins(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FindPlugins'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReloadPlugins of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ReloadPlugins00 +static int tolua_AllToLua_cPluginManager_ReloadPlugins00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReloadPlugins'", NULL); +#endif + { + self->ReloadPlugins(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReloadPlugins'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumPlugins of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetNumPlugins00 +static int tolua_AllToLua_cPluginManager_GetNumPlugins00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlugins'", NULL); +#endif + { + unsigned int tolua_ret = (unsigned int) self->GetNumPlugins(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumPlugins'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DisablePlugin of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_DisablePlugin00 +static int tolua_AllToLua_cPluginManager_DisablePlugin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_PluginName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DisablePlugin'", NULL); +#endif + { + bool tolua_ret = (bool) self->DisablePlugin(a_PluginName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_PluginName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DisablePlugin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LoadPlugin of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_LoadPlugin00 +static int tolua_AllToLua_cPluginManager_LoadPlugin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_PluginName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadPlugin'", NULL); +#endif + { + bool tolua_ret = (bool) self->LoadPlugin(a_PluginName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_PluginName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LoadPlugin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCommandBound of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_IsCommandBound00 +static int tolua_AllToLua_cPluginManager_IsCommandBound00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCommandBound'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCommandBound(a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCommandBound'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetCommandPermission of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetCommandPermission00 +static int tolua_AllToLua_cPluginManager_GetCommandPermission00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCommandPermission'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetCommandPermission(a_Command); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetCommandPermission'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ExecuteCommand of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ExecuteCommand00 +static int tolua_AllToLua_cPluginManager_ExecuteCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ExecuteCommand'", NULL); +#endif + { + bool tolua_ret = (bool) self->ExecuteCommand(a_Player,a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ExecuteCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ForceExecuteCommand of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ForceExecuteCommand00 +static int tolua_AllToLua_cPluginManager_ForceExecuteCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ForceExecuteCommand'", NULL); +#endif + { + bool tolua_ret = (bool) self->ForceExecuteCommand(a_Player,a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ForceExecuteCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsConsoleCommandBound of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_IsConsoleCommandBound00 +static int tolua_AllToLua_cPluginManager_IsConsoleCommandBound00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsConsoleCommandBound'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsConsoleCommandBound(a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsConsoleCommandBound'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetName of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetName00 +static int tolua_AllToLua_cPlugin_GetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetName of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_SetName00 +static int tolua_AllToLua_cPlugin_SetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlugin",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0); + const AString a_Name = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL); +#endif + { + self->SetName(a_Name); + tolua_pushcppstring(tolua_S,(const char*)a_Name); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetVersion of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetVersion00 +static int tolua_AllToLua_cPlugin_GetVersion00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetVersion'", NULL); +#endif + { + int tolua_ret = (int) self->GetVersion(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetVersion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetVersion of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_SetVersion00 +static int tolua_AllToLua_cPlugin_SetVersion00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlugin",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0); + int a_Version = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetVersion'", NULL); +#endif + { + self->SetVersion(a_Version); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetVersion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDirectory of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetDirectory00 +static int tolua_AllToLua_cPlugin_GetDirectory00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDirectory'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetDirectory(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDirectory'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLocalDirectory of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalDirectory00 +static int tolua_AllToLua_cPlugin_GetLocalDirectory00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLocalDirectory'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetLocalDirectory(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLocalDirectory'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLocalFolder of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalFolder00 +static int tolua_AllToLua_cPlugin_GetLocalFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLocalFolder'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetLocalFolder(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLocalFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: __cWebPlugin__ of class cPluginLua */ +#ifndef TOLUA_DISABLE_tolua_get_cPluginLua___cWebPlugin__ +static int tolua_get_cPluginLua___cWebPlugin__(lua_State* tolua_S) +{ + cPluginLua* self = (cPluginLua*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable '__cWebPlugin__'",NULL); +#endif +#ifdef __cplusplus + tolua_pushusertype(tolua_S,(void*)static_cast(self), "cWebPlugin"); +#else + tolua_pushusertype(tolua_S,(void*)((cWebPlugin*)self), "cWebPlugin"); +#endif + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDescription of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetDescription00 +static int tolua_AllToLua_cServer_GetDescription00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDescription'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetDescription(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDescription'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxPlayers of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetMaxPlayers00 +static int tolua_AllToLua_cServer_GetMaxPlayers00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxPlayers'", NULL); +#endif + { + int tolua_ret = (int) self->GetMaxPlayers(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxPlayers'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumPlayers of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetNumPlayers00 +static int tolua_AllToLua_cServer_GetNumPlayers00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlayers'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumPlayers(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumPlayers'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetMaxPlayers of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_SetMaxPlayers00 +static int tolua_AllToLua_cServer_SetMaxPlayers00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0); + int a_MaxPlayers = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxPlayers'", NULL); +#endif + { + self->SetMaxPlayers(a_MaxPlayers); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetMaxPlayers'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsHardcore of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_IsHardcore00 +static int tolua_AllToLua_cServer_IsHardcore00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsHardcore'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsHardcore(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsHardcore'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetServerID of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetServerID00 +static int tolua_AllToLua_cServer_GetServerID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetServerID'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetServerID(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetServerID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetTicksUntilWeatherChange of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00 +static int tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTicksUntilWeatherChange'", NULL); +#endif + { + int tolua_ret = (int) self->GetTicksUntilWeatherChange(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTicksUntilWeatherChange'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWorldAge of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetWorldAge00 +static int tolua_AllToLua_cWorld_GetWorldAge00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorldAge'", NULL); +#endif + { + long long tolua_ret = ( long long) self->GetWorldAge(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWorldAge'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetTimeOfDay of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTimeOfDay00 +static int tolua_AllToLua_cWorld_GetTimeOfDay00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTimeOfDay'", NULL); +#endif + { + long long tolua_ret = ( long long) self->GetTimeOfDay(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTimeOfDay'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetTicksUntilWeatherChange of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00 +static int tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_WeatherInterval = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetTicksUntilWeatherChange'", NULL); +#endif + { + self->SetTicksUntilWeatherChange(a_WeatherInterval); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetTicksUntilWeatherChange'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetTimeOfDay of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetTimeOfDay00 +static int tolua_AllToLua_cWorld_SetTimeOfDay00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + long long a_TimeOfDay = (( long long) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetTimeOfDay'", NULL); +#endif + { + self->SetTimeOfDay(a_TimeOfDay); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetTimeOfDay'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGameMode of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetGameMode00 +static int tolua_AllToLua_cWorld_GetGameMode00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGameMode'", NULL); +#endif + { + eGameMode tolua_ret = (eGameMode) self->GetGameMode(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGameMode'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeCreative of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeCreative00 +static int tolua_AllToLua_cWorld_IsGameModeCreative00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeCreative'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeCreative(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeSurvival of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeSurvival00 +static int tolua_AllToLua_cWorld_IsGameModeSurvival00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeSurvival'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeSurvival(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeAdventure of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeAdventure00 +static int tolua_AllToLua_cWorld_IsGameModeAdventure00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeAdventure'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeAdventure(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsPVPEnabled of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsPVPEnabled00 +static int tolua_AllToLua_cWorld_IsPVPEnabled00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPVPEnabled'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsPVPEnabled(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPVPEnabled'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsDeepSnowEnabled of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsDeepSnowEnabled00 +static int tolua_AllToLua_cWorld_IsDeepSnowEnabled00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDeepSnowEnabled'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsDeepSnowEnabled(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsDeepSnowEnabled'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDimension of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetDimension00 +static int tolua_AllToLua_cWorld_GetDimension00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDimension'", NULL); +#endif + { + eDimension tolua_ret = (eDimension) self->GetDimension(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDimension'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetHeight00 +static int tolua_AllToLua_cWorld_GetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetHeight(a_BlockX,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: BroadcastChat of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastChat00 +static int tolua_AllToLua_cWorld_BroadcastChat00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isusertype(tolua_S,3,"const cClientHandle",1,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,3,NULL)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastChat'", NULL); +#endif + { + self->BroadcastChat(a_Message,a_Exclude); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BroadcastChat'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: BroadcastSoundEffect of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastSoundEffect00 +static int tolua_AllToLua_cWorld_BroadcastSoundEffect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isusertype(tolua_S,8,"const cClientHandle",1,&tolua_err) || + !tolua_isnoobj(tolua_S,9,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const AString a_SoundName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + int a_SrcX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SrcY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_SrcZ = ((int) tolua_tonumber(tolua_S,5,0)); + float a_Volume = ((float) tolua_tonumber(tolua_S,6,0)); + float a_Pitch = ((float) tolua_tonumber(tolua_S,7,0)); + const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,8,NULL)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastSoundEffect'", NULL); +#endif + { + self->BroadcastSoundEffect(a_SoundName,a_SrcX,a_SrcY,a_SrcZ,a_Volume,a_Pitch,a_Exclude); + tolua_pushcppstring(tolua_S,(const char*)a_SoundName); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BroadcastSoundEffect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: BroadcastSoundParticleEffect of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00 +static int tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isusertype(tolua_S,7,"const cClientHandle",1,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_EffectID = ((int) tolua_tonumber(tolua_S,2,0)); + int a_SrcX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SrcY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_SrcZ = ((int) tolua_tonumber(tolua_S,5,0)); + int a_Data = ((int) tolua_tonumber(tolua_S,6,0)); + const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,7,NULL)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastSoundParticleEffect'", NULL); +#endif + { + self->BroadcastSoundParticleEffect(a_EffectID,a_SrcX,a_SrcY,a_SrcZ,a_Data,a_Exclude); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BroadcastSoundParticleEffect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: UnloadUnusedChunks of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_UnloadUnusedChunks00 +static int tolua_AllToLua_cWorld_UnloadUnusedChunks00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'UnloadUnusedChunks'", NULL); +#endif + { + self->UnloadUnusedChunks(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'UnloadUnusedChunks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RegenerateChunk of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_RegenerateChunk00 +static int tolua_AllToLua_cWorld_RegenerateChunk00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RegenerateChunk'", NULL); +#endif + { + self->RegenerateChunk(a_ChunkX,a_ChunkZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RegenerateChunk'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GenerateChunk of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GenerateChunk00 +static int tolua_AllToLua_cWorld_GenerateChunk00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GenerateChunk'", NULL); +#endif + { + self->GenerateChunk(a_ChunkX,a_ChunkZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GenerateChunk'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlock00 +static int tolua_AllToLua_cWorld_SetBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlock'", NULL); +#endif + { + self->SetBlock(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FastSetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_FastSetBlock00 +static int tolua_AllToLua_cWorld_FastSetBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FastSetBlock'", NULL); +#endif + { + self->FastSetBlock(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FastSetBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: QueueSetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSetBlock00 +static int tolua_AllToLua_cWorld_QueueSetBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BLockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); + int a_TickDelay = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueSetBlock'", NULL); +#endif + { + self->QueueSetBlock(a_BlockX,a_BLockY,a_BlockZ,a_BlockType,a_BlockMeta,a_TickDelay); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'QueueSetBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlock00 +static int tolua_AllToLua_cWorld_GetBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlock'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlock(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockMeta of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockMeta00 +static int tolua_AllToLua_cWorld_GetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockMeta of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlockMeta00 +static int tolua_AllToLua_cWorld_SetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_MetaData = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); +#endif + { + self->SetBlockMeta(a_BlockX,a_BlockY,a_BlockZ,a_MetaData); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockSkyLight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockSkyLight00 +static int tolua_AllToLua_cWorld_GetBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockSkyLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockBlockLight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockBlockLight00 +static int tolua_AllToLua_cWorld_GetBlockBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockBlockLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockBlockLight(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FastSetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_FastSetBlock01 +static int tolua_AllToLua_cWorld_FastSetBlock01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FastSetBlock'", NULL); +#endif + { + self->FastSetBlock(*a_Pos,a_BlockType,a_BlockMeta); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cWorld_FastSetBlock00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlock01 +static int tolua_AllToLua_cWorld_GetBlock01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlock'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlock(*a_Pos); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cWorld_GetBlock00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockMeta of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockMeta01 +static int tolua_AllToLua_cWorld_GetBlockMeta01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(*a_Pos); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cWorld_GetBlockMeta00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockMeta of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlockMeta01 +static int tolua_AllToLua_cWorld_SetBlockMeta01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_MetaData = (( unsigned char) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); +#endif + { + self->SetBlockMeta(*a_Pos,a_MetaData); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cWorld_SetBlockMeta00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SpawnItemPickups of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnItemPickups00 +static int tolua_AllToLua_cWorld_SpawnItemPickups00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItems",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,1,&tolua_err) || + !tolua_isboolean(tolua_S,7,1,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const cItems* a_Pickups = ((const cItems*) tolua_tousertype(tolua_S,2,0)); + double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0)); + double a_FlyAwaySpeed = ((double) tolua_tonumber(tolua_S,6,1.0)); + bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,7,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL); +#endif + { + self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_FlyAwaySpeed,IsPlayerCreated); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SpawnItemPickups'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SpawnItemPickups of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnItemPickups01 +static int tolua_AllToLua_cWorld_SpawnItemPickups01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItems",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isboolean(tolua_S,9,1,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const cItems* a_Pickups = ((const cItems*) tolua_tousertype(tolua_S,2,0)); + double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0)); + double a_SpeedX = ((double) tolua_tonumber(tolua_S,6,0)); + double a_SpeedY = ((double) tolua_tonumber(tolua_S,7,0)); + double a_SpeedZ = ((double) tolua_tonumber(tolua_S,8,0)); + bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,9,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL); +#endif + { + self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_SpeedX,a_SpeedY,a_SpeedZ,IsPlayerCreated); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cWorld_SpawnItemPickups00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SpawnPrimedTNT of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnPrimedTNT00 +static int tolua_AllToLua_cWorld_SpawnPrimedTNT00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,1,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + double a_X = ((double) tolua_tonumber(tolua_S,2,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,4,0)); + double a_FuseTimeInSec = ((double) tolua_tonumber(tolua_S,5,0)); + double a_InitialVelocityCoeff = ((double) tolua_tonumber(tolua_S,6,1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnPrimedTNT'", NULL); +#endif + { + self->SpawnPrimedTNT(a_X,a_Y,a_Z,a_FuseTimeInSec,a_InitialVelocityCoeff); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SpawnPrimedTNT'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DigBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_DigBlock00 +static int tolua_AllToLua_cWorld_DigBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DigBlock'", NULL); +#endif + { + bool tolua_ret = (bool) self->DigBlock(a_X,a_Y,a_Z); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DigBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SendBlockTo of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SendBlockTo00 +static int tolua_AllToLua_cWorld_SendBlockTo00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isusertype(tolua_S,5,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z = ((int) tolua_tonumber(tolua_S,4,0)); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendBlockTo'", NULL); +#endif + { + self->SendBlockTo(a_X,a_Y,a_Z,a_Player); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SendBlockTo'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpawnX of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnX00 +static int tolua_AllToLua_cWorld_GetSpawnX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpawnX'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpawnX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpawnX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpawnY of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnY00 +static int tolua_AllToLua_cWorld_GetSpawnY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpawnY'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpawnY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpawnY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpawnZ of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnZ00 +static int tolua_AllToLua_cWorld_GetSpawnZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpawnZ'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpawnZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpawnZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: WakeUpSimulators of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_WakeUpSimulators00 +static int tolua_AllToLua_cWorld_WakeUpSimulators00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WakeUpSimulators'", NULL); +#endif + { + self->WakeUpSimulators(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'WakeUpSimulators'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: WakeUpSimulatorsInArea of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00 +static int tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WakeUpSimulatorsInArea'", NULL); +#endif + { + self->WakeUpSimulatorsInArea(a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'WakeUpSimulatorsInArea'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DoExplosionAt of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_DoExplosionAt00 +static int tolua_AllToLua_cWorld_DoExplosionAt00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isboolean(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isuserdata(tolua_S,8,0,&tolua_err) || + !tolua_isnoobj(tolua_S,9,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + double a_ExplosionSize = ((double) tolua_tonumber(tolua_S,2,0)); + double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0)); + bool a_CanCauseFire = ((bool) tolua_toboolean(tolua_S,6,0)); + eExplosionSource a_Source = ((eExplosionSource) (int) tolua_tonumber(tolua_S,7,0)); + void* a_SourceData = ((void*) tolua_touserdata(tolua_S,8,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoExplosionAt'", NULL); +#endif + { + self->DoExplosionAt(a_ExplosionSize,a_BlockX,a_BlockY,a_BlockZ,a_CanCauseFire,a_Source,a_SourceData); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DoExplosionAt'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: UseBlockEntity of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_UseBlockEntity00 +static int tolua_AllToLua_cWorld_UseBlockEntity00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + int a_BlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'UseBlockEntity'", NULL); +#endif + { + self->UseBlockEntity(a_Player,a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'UseBlockEntity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowTree of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTree00 +static int tolua_AllToLua_cWorld_GrowTree00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTree'", NULL); +#endif + { + self->GrowTree(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowTree'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowTreeFromSapling of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeFromSapling00 +static int tolua_AllToLua_cWorld_GrowTreeFromSapling00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_SaplingMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeFromSapling'", NULL); +#endif + { + self->GrowTreeFromSapling(a_BlockX,a_BlockY,a_BlockZ,a_SaplingMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowTreeFromSapling'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowTreeByBiome of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeByBiome00 +static int tolua_AllToLua_cWorld_GrowTreeByBiome00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeByBiome'", NULL); +#endif + { + self->GrowTreeByBiome(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowTreeByBiome'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowRipePlant of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowRipePlant00 +static int tolua_AllToLua_cWorld_GrowRipePlant00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + bool a_IsByBonemeal = ((bool) tolua_toboolean(tolua_S,5,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowRipePlant'", NULL); +#endif + { + bool tolua_ret = (bool) self->GrowRipePlant(a_BlockX,a_BlockY,a_BlockZ,a_IsByBonemeal); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowRipePlant'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowCactus of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowCactus00 +static int tolua_AllToLua_cWorld_GrowCactus00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + int a_NumBlocksToGrow = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowCactus'", NULL); +#endif + { + self->GrowCactus(a_BlockX,a_BlockY,a_BlockZ,a_NumBlocksToGrow); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowCactus'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowMelonPumpkin of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowMelonPumpkin00 +static int tolua_AllToLua_cWorld_GrowMelonPumpkin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowMelonPumpkin'", NULL); +#endif + { + self->GrowMelonPumpkin(a_BlockX,a_BlockY,a_BlockZ,a_BlockType); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowMelonPumpkin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowSugarcane of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowSugarcane00 +static int tolua_AllToLua_cWorld_GrowSugarcane00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + int a_NumBlocksToGrow = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowSugarcane'", NULL); +#endif + { + self->GrowSugarcane(a_BlockX,a_BlockY,a_BlockZ,a_NumBlocksToGrow); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowSugarcane'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBiomeAt of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBiomeAt00 +static int tolua_AllToLua_cWorld_GetBiomeAt00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBiomeAt'", NULL); +#endif + { + int tolua_ret = (int) self->GetBiomeAt(a_BlockX,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBiomeAt'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetName of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetName00 +static int tolua_AllToLua_cWorld_GetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIniFileName of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetIniFileName00 +static int tolua_AllToLua_cWorld_GetIniFileName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIniFileName'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetIniFileName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIniFileName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: QueueSaveAllChunks of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSaveAllChunks00 +static int tolua_AllToLua_cWorld_QueueSaveAllChunks00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueSaveAllChunks'", NULL); +#endif + { + self->QueueSaveAllChunks(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'QueueSaveAllChunks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumChunks of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetNumChunks00 +static int tolua_AllToLua_cWorld_GetNumChunks00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumChunks'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumChunks(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumChunks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGeneratorQueueLength of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetGeneratorQueueLength00 +static int tolua_AllToLua_cWorld_GetGeneratorQueueLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGeneratorQueueLength'", NULL); +#endif + { + int tolua_ret = (int) self->GetGeneratorQueueLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGeneratorQueueLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLightingQueueLength of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetLightingQueueLength00 +static int tolua_AllToLua_cWorld_GetLightingQueueLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLightingQueueLength'", NULL); +#endif + { + int tolua_ret = (int) self->GetLightingQueueLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLightingQueueLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetStorageLoadQueueLength of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetStorageLoadQueueLength00 +static int tolua_AllToLua_cWorld_GetStorageLoadQueueLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStorageLoadQueueLength'", NULL); +#endif + { + int tolua_ret = (int) self->GetStorageLoadQueueLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetStorageLoadQueueLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetStorageSaveQueueLength of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetStorageSaveQueueLength00 +static int tolua_AllToLua_cWorld_GetStorageSaveQueueLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStorageSaveQueueLength'", NULL); +#endif + { + int tolua_ret = (int) self->GetStorageSaveQueueLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetStorageSaveQueueLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: QueueBlockForTick of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueBlockForTick00 +static int tolua_AllToLua_cWorld_QueueBlockForTick00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + int a_TicksToWait = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueBlockForTick'", NULL); +#endif + { + self->QueueBlockForTick(a_BlockX,a_BlockY,a_BlockZ,a_TicksToWait); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'QueueBlockForTick'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CastThunderbolt of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_CastThunderbolt00 +static int tolua_AllToLua_cWorld_CastThunderbolt00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CastThunderbolt'", NULL); +#endif + { + self->CastThunderbolt(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CastThunderbolt'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetWeather of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetWeather00 +static int tolua_AllToLua_cWorld_SetWeather00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + eWeather a_NewWeather = ((eWeather) (int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWeather'", NULL); +#endif + { + self->SetWeather(a_NewWeather); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetWeather'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ChangeWeather of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_ChangeWeather00 +static int tolua_AllToLua_cWorld_ChangeWeather00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeWeather'", NULL); +#endif + { + self->ChangeWeather(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ChangeWeather'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWeather of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetWeather00 +static int tolua_AllToLua_cWorld_GetWeather00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWeather'", NULL); +#endif + { + eWeather tolua_ret = (eWeather) self->GetWeather(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWeather'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherSunny of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherSunny00 +static int tolua_AllToLua_cWorld_IsWeatherSunny00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherSunny'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherSunny(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherSunny'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherRain of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherRain00 +static int tolua_AllToLua_cWorld_IsWeatherRain00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherRain'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherRain(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherRain'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherStorm of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherStorm00 +static int tolua_AllToLua_cWorld_IsWeatherStorm00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherStorm'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherStorm(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherStorm'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherWet of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherWet00 +static int tolua_AllToLua_cWorld_IsWeatherWet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherWet'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherWet(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherWet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetNextBlockTick of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetNextBlockTick00 +static int tolua_AllToLua_cWorld_SetNextBlockTick00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetNextBlockTick'", NULL); +#endif + { + self->SetNextBlockTick(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetNextBlockTick'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxSugarcaneHeight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00 +static int tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxSugarcaneHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetMaxSugarcaneHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxSugarcaneHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxCactusHeight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetMaxCactusHeight00 +static int tolua_AllToLua_cWorld_GetMaxCactusHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxCactusHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetMaxCactusHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxCactusHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsBlockDirectlyWatered of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsBlockDirectlyWatered00 +static int tolua_AllToLua_cWorld_IsBlockDirectlyWatered00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsBlockDirectlyWatered'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsBlockDirectlyWatered(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsBlockDirectlyWatered'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SpawnMob of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnMob00 +static int tolua_AllToLua_cWorld_SpawnMob00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); + cMonster::eType a_MonsterType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnMob'", NULL); +#endif + { + int tolua_ret = (int) self->SpawnMob(a_PosX,a_PosY,a_PosZ,a_MonsterType); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SpawnMob'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CreateProjectile of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_CreateProjectile00 +static int tolua_AllToLua_cWorld_CreateProjectile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isusertype(tolua_S,6,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,7,"const Vector3d",1,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); + cProjectileEntity::eKind a_Kind = ((cProjectileEntity::eKind) (int) tolua_tonumber(tolua_S,5,0)); + cEntity* a_Creator = ((cEntity*) tolua_tousertype(tolua_S,6,0)); + const Vector3d* a_Speed = ((const Vector3d*) tolua_tousertype(tolua_S,7,NULL)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CreateProjectile'", NULL); +#endif + { + int tolua_ret = (int) self->CreateProjectile(a_PosX,a_PosY,a_PosZ,a_Kind,a_Creator,a_Speed); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CreateProjectile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_Clear00 +static int tolua_AllToLua_cInventory_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyCanFit of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyCanFit00 +static int tolua_AllToLua_cInventory_HowManyCanFit00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + bool a_ConsiderEmptySlots = ((bool) tolua_toboolean(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_ConsiderEmptySlots); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HowManyCanFit'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyCanFit of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyCanFit01 +static int tolua_AllToLua_cInventory_HowManyCanFit01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + int a_BeginSlotNum = ((int) tolua_tonumber(tolua_S,3,0)); + int a_EndSlotNum = ((int) tolua_tonumber(tolua_S,4,0)); + bool a_ConsiderEmptySlots = ((bool) tolua_toboolean(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_BeginSlotNum,a_EndSlotNum,a_ConsiderEmptySlots); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cInventory_HowManyCanFit00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_AddItem00 +static int tolua_AllToLua_cInventory_AddItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isboolean(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); + bool a_tryToFillEquippedFirst = ((bool) tolua_toboolean(tolua_S,4,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItem'", NULL); +#endif + { + int tolua_ret = (int) self->AddItem(*a_ItemStack,a_AllowNewStacks,a_tryToFillEquippedFirst); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddItems of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_AddItems00 +static int tolua_AllToLua_cInventory_AddItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,0,&tolua_err) || + !tolua_isboolean(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + cItems* a_ItemStackList = ((cItems*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,0)); + bool a_tryToFillEquippedFirst = ((bool) tolua_toboolean(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItems'", NULL); +#endif + { + int tolua_ret = (int) self->AddItems(*a_ItemStackList,a_AllowNewStacks,a_tryToFillEquippedFirst); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RemoveOneEquippedItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_RemoveOneEquippedItem00 +static int tolua_AllToLua_cInventory_RemoveOneEquippedItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneEquippedItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->RemoveOneEquippedItem(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RemoveOneEquippedItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyItems of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyItems00 +static int tolua_AllToLua_cInventory_HowManyItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyItems'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyItems(*a_Item); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HowManyItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasItems of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HasItems00 +static int tolua_AllToLua_cInventory_HasItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasItems'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasItems(*a_ItemStack); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetArmorGrid of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetArmorGrid00 +static int tolua_AllToLua_cInventory_GetArmorGrid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorGrid'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetArmorGrid(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetArmorGrid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetInventoryGrid of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetInventoryGrid00 +static int tolua_AllToLua_cInventory_GetInventoryGrid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventoryGrid'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetInventoryGrid(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetInventoryGrid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHotbarGrid of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetHotbarGrid00 +static int tolua_AllToLua_cInventory_GetHotbarGrid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHotbarGrid'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetHotbarGrid(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHotbarGrid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOwner of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetOwner00 +static int tolua_AllToLua_cInventory_GetOwner00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOwner'", NULL); +#endif + { + cPlayer& tolua_ret = (cPlayer&) self->GetOwner(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cPlayer"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOwner'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyToItems of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_CopyToItems00 +static int tolua_AllToLua_cInventory_CopyToItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + cItems* a_Items = ((cItems*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyToItems'", NULL); +#endif + { + self->CopyToItems(*a_Items); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyToItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetSlot00 +static int tolua_AllToLua_cInventory_GetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetArmorSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetArmorSlot00 +static int tolua_AllToLua_cInventory_GetArmorSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); + int a_ArmorSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetArmorSlot(a_ArmorSlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetArmorSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetInventorySlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetInventorySlot00 +static int tolua_AllToLua_cInventory_GetInventorySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); + int a_InventorySlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventorySlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetInventorySlot(a_InventorySlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetInventorySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHotbarSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetHotbarSlot00 +static int tolua_AllToLua_cInventory_GetHotbarSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); + int a_HotBarSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHotbarSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetHotbarSlot(a_HotBarSlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHotbarSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedItem00 +static int tolua_AllToLua_cInventory_GetEquippedItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedItem'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedItem(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetSlot00 +static int tolua_AllToLua_cInventory_SetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_SlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetArmorSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetArmorSlot00 +static int tolua_AllToLua_cInventory_SetArmorSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_ArmorSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetArmorSlot'", NULL); +#endif + { + self->SetArmorSlot(a_ArmorSlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetArmorSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetInventorySlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetInventorySlot00 +static int tolua_AllToLua_cInventory_SetInventorySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_InventorySlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetInventorySlot'", NULL); +#endif + { + self->SetInventorySlot(a_InventorySlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetInventorySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHotbarSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetHotbarSlot00 +static int tolua_AllToLua_cInventory_SetHotbarSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_HotBarSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHotbarSlot'", NULL); +#endif + { + self->SetHotbarSlot(a_HotBarSlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHotbarSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetEquippedSlotNum of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetEquippedSlotNum00 +static int tolua_AllToLua_cInventory_SetEquippedSlotNum00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetEquippedSlotNum'", NULL); +#endif + { + self->SetEquippedSlotNum(a_SlotNum); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetEquippedSlotNum'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedSlotNum of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedSlotNum00 +static int tolua_AllToLua_cInventory_GetEquippedSlotNum00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedSlotNum'", NULL); +#endif + { + int tolua_ret = (int) self->GetEquippedSlotNum(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedSlotNum'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ChangeSlotCount of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_ChangeSlotCount00 +static int tolua_AllToLua_cInventory_ChangeSlotCount00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + int a_AddToCount = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL); +#endif + { + int tolua_ret = (int) self->ChangeSlotCount(a_SlotNum,a_AddToCount); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ChangeSlotCount'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_DamageItem00 +static int tolua_AllToLua_cInventory_DamageItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + short a_Amount = ((short) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageItem(a_SlotNum,a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageEquippedItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_DamageEquippedItem00 +static int tolua_AllToLua_cInventory_DamageEquippedItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,1,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + short a_Amount = ((short) tolua_tonumber(tolua_S,2,1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageEquippedItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageEquippedItem(a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageEquippedItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedHelmet of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedHelmet00 +static int tolua_AllToLua_cInventory_GetEquippedHelmet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedHelmet'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedHelmet(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedHelmet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedChestplate of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedChestplate00 +static int tolua_AllToLua_cInventory_GetEquippedChestplate00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedChestplate'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedChestplate(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedChestplate'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedLeggings of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedLeggings00 +static int tolua_AllToLua_cInventory_GetEquippedLeggings00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedLeggings'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedLeggings(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedLeggings'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedBoots of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedBoots00 +static int tolua_AllToLua_cInventory_GetEquippedBoots00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedBoots'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedBoots(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedBoots'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new00 +static int tolua_AllToLua_cEnchantments_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new00_local +static int tolua_AllToLua_cEnchantments_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new01 +static int tolua_AllToLua_cEnchantments_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)(a_StringSpec)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); + tolua_pushcppstring(tolua_S,(const char*)a_StringSpec); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cEnchantments_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new01_local +static int tolua_AllToLua_cEnchantments_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)(a_StringSpec)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + tolua_pushcppstring(tolua_S,(const char*)a_StringSpec); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cEnchantments_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddFromString of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_AddFromString00 +static int tolua_AllToLua_cEnchantments_AddFromString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); + const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddFromString'", NULL); +#endif + { + self->AddFromString(a_StringSpec); + tolua_pushcppstring(tolua_S,(const char*)a_StringSpec); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddFromString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ToString of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_ToString00 +static int tolua_AllToLua_cEnchantments_ToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ToString'", NULL); +#endif + { + AString tolua_ret = (AString) self->ToString(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLevel of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_GetLevel00 +static int tolua_AllToLua_cEnchantments_GetLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); + int a_EnchantmentID = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLevel'", NULL); +#endif + { + int tolua_ret = (int) self->GetLevel(a_EnchantmentID); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetLevel of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_SetLevel00 +static int tolua_AllToLua_cEnchantments_SetLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); + int a_EnchantmentID = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Level = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLevel'", NULL); +#endif + { + self->SetLevel(a_EnchantmentID,a_Level); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_Clear00 +static int tolua_AllToLua_cEnchantments_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsEmpty of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_IsEmpty00 +static int tolua_AllToLua_cEnchantments_IsEmpty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEmpty'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsEmpty(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsEmpty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: StringToEnchantmentID of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_StringToEnchantmentID00 +static int tolua_AllToLua_cEnchantments_StringToEnchantmentID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_EnchantmentName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + int tolua_ret = (int) cEnchantments::StringToEnchantmentID(a_EnchantmentName); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_EnchantmentName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToEnchantmentID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator== of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments__eq00 +static int tolua_AllToLua_cEnchantments__eq00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEnchantments",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); + const cEnchantments* a_Other = ((const cEnchantments*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator=='", NULL); +#endif + { + bool tolua_ret = (bool) self->operator==(*a_Other); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.eq'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new00 +static int tolua_AllToLua_cItem_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new00_local +static int tolua_AllToLua_cItem_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new01 +static int tolua_AllToLua_cItem_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,1)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItem_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new01_local +static int tolua_AllToLua_cItem_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,1)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItem_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new02 +static int tolua_AllToLua_cItem_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); + const AString a_Enchantments = ((const AString) tolua_tocppstring(tolua_S,5,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage,a_Enchantments)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_pushcppstring(tolua_S,(const char*)a_Enchantments); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cItem_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new02_local +static int tolua_AllToLua_cItem_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); + const AString a_Enchantments = ((const AString) tolua_tocppstring(tolua_S,5,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage,a_Enchantments)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + tolua_pushcppstring(tolua_S,(const char*)a_Enchantments); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cItem_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new03 +static int tolua_AllToLua_cItem_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cItem* a_CopyFrom = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(*a_CopyFrom)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItem_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new03_local +static int tolua_AllToLua_cItem_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cItem* a_CopyFrom = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(*a_CopyFrom)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItem_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Empty of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_Empty00 +static int tolua_AllToLua_cItem_Empty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Empty'", NULL); +#endif + { + self->Empty(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Empty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_Clear00 +static int tolua_AllToLua_cItem_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsEmpty of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsEmpty00 +static int tolua_AllToLua_cItem_IsEmpty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEmpty'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsEmpty(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsEmpty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsEqual of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsEqual00 +static int tolua_AllToLua_cItem_IsEqual00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEqual'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsEqual(*a_Item); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsEqual'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSameType of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsSameType00 +static int tolua_AllToLua_cItem_IsSameType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSameType'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSameType(*a_Item); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSameType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyOne of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_CopyOne00 +static int tolua_AllToLua_cItem_CopyOne00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyOne'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->CopyOne(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyOne'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddCount of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_AddCount00 +static int tolua_AllToLua_cItem_AddCount00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); + char a_AmountToAdd = ((char) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddCount'", NULL); +#endif + { + cItem& tolua_ret = (cItem&) self->AddCount(a_AmountToAdd); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddCount'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxDamage of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_GetMaxDamage00 +static int tolua_AllToLua_cItem_GetMaxDamage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxDamage'", NULL); +#endif + { + short tolua_ret = (short) self->GetMaxDamage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxDamage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageItem of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_DamageItem00 +static int tolua_AllToLua_cItem_DamageItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,1,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); + short a_Amount = ((short) tolua_tonumber(tolua_S,2,1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageItem(a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsDamageable of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsDamageable00 +static int tolua_AllToLua_cItem_IsDamageable00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDamageable'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsDamageable(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsDamageable'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsStackableWith of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsStackableWith00 +static int tolua_AllToLua_cItem_IsStackableWith00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); + const cItem* a_OtherStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsStackableWith'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsStackableWith(*a_OtherStack); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsStackableWith'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFullStack of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsFullStack00 +static int tolua_AllToLua_cItem_IsFullStack00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFullStack'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsFullStack(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFullStack'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxStackSize of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_GetMaxStackSize00 +static int tolua_AllToLua_cItem_GetMaxStackSize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxStackSize'", NULL); +#endif + { + char tolua_ret = (char) self->GetMaxStackSize(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxStackSize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: m_ItemType of class cItem */ +#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemType +static int tolua_get_cItem_m_ItemType(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemType'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemType); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: m_ItemType of class cItem */ +#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemType +static int tolua_set_cItem_m_ItemType(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemType'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->m_ItemType = ((short) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: m_ItemCount of class cItem */ +#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemCount +static int tolua_get_cItem_m_ItemCount(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemCount'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemCount); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: m_ItemCount of class cItem */ +#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemCount +static int tolua_set_cItem_m_ItemCount(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemCount'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->m_ItemCount = ((char) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: m_ItemDamage of class cItem */ +#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemDamage +static int tolua_get_cItem_m_ItemDamage(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemDamage'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemDamage); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: m_ItemDamage of class cItem */ +#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemDamage +static int tolua_set_cItem_m_ItemDamage(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemDamage'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->m_ItemDamage = ((short) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: m_Enchantments of class cItem */ +#ifndef TOLUA_DISABLE_tolua_get_cItem_m_Enchantments +static int tolua_get_cItem_m_Enchantments(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_Enchantments'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->m_Enchantments,"cEnchantments"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: m_Enchantments of class cItem */ +#ifndef TOLUA_DISABLE_tolua_set_cItem_m_Enchantments +static int tolua_set_cItem_m_Enchantments(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_Enchantments'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEnchantments",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->m_Enchantments = *((cEnchantments*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_new00 +static int tolua_AllToLua_cItems_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cItems* tolua_ret = (cItems*) Mtolua_new((cItems)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItems"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_new00_local +static int tolua_AllToLua_cItems_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cItems* tolua_ret = (cItems*) Mtolua_new((cItems)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItems"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Get of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Get00 +static int tolua_AllToLua_cItems_Get00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Get'", NULL); +#endif + { + cItem* tolua_ret = (cItem*) self->Get(a_Idx); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Set00 +static int tolua_AllToLua_cItems_Set00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_Idx,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Add of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Add00 +static int tolua_AllToLua_cItems_Add00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Add'", NULL); +#endif + { + self->Add(*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Add'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Delete of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Delete00 +static int tolua_AllToLua_cItems_Delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Delete'", NULL); +#endif + { + self->Delete(a_Idx); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Clear00 +static int tolua_AllToLua_cItems_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Size of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Size00 +static int tolua_AllToLua_cItems_Size00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Size'", NULL); +#endif + { + int tolua_ret = (int) self->Size(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Size'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Set01 +static int tolua_AllToLua_cItems_Set01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,3,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,4,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_Idx,a_ItemType,a_ItemCount,a_ItemDamage); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItems_Set00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Add of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Add01 +static int tolua_AllToLua_cItems_Add01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Add'", NULL); +#endif + { + self->Add(a_ItemType,a_ItemCount,a_ItemDamage); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItems_Add00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWidth of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetWidth00 +static int tolua_AllToLua_cItemGrid_GetWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL); +#endif + { + int tolua_ret = (int) self->GetWidth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetHeight00 +static int tolua_AllToLua_cItemGrid_GetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumSlots of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNumSlots00 +static int tolua_AllToLua_cItemGrid_GetNumSlots00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumSlots'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumSlots(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumSlots'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlotNum of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlotNum00 +static int tolua_AllToLua_cItemGrid_GetSlotNum00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlotNum'", NULL); +#endif + { + int tolua_ret = (int) self->GetSlotNum(a_X,a_Y); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlotNum'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlot00 +static int tolua_AllToLua_cItemGrid_GetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_X,a_Y); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlot01 +static int tolua_AllToLua_cItemGrid_GetSlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_GetSlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot00 +static int tolua_AllToLua_cItemGrid_SetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_X,a_Y,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot01 +static int tolua_AllToLua_cItemGrid_SetSlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + short a_ItemType = ((short) tolua_tonumber(tolua_S,4,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,5,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_X,a_Y,a_ItemType,a_ItemCount,a_ItemDamage); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItemGrid_SetSlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot02 +static int tolua_AllToLua_cItemGrid_SetSlot02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_SlotNum,*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItemGrid_SetSlot01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot03 +static int tolua_AllToLua_cItemGrid_SetSlot03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + short a_ItemType = ((short) tolua_tonumber(tolua_S,3,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,4,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_SlotNum,a_ItemType,a_ItemCount,a_ItemDamage); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItemGrid_SetSlot02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: EmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_EmptySlot00 +static int tolua_AllToLua_cItemGrid_EmptySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EmptySlot'", NULL); +#endif + { + self->EmptySlot(a_X,a_Y); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'EmptySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: EmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_EmptySlot01 +static int tolua_AllToLua_cItemGrid_EmptySlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EmptySlot'", NULL); +#endif + { + self->EmptySlot(a_SlotNum); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItemGrid_EmptySlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotEmpty of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_IsSlotEmpty00 +static int tolua_AllToLua_cItemGrid_IsSlotEmpty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotEmpty'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotEmpty(a_SlotNum); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSlotEmpty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotEmpty of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_IsSlotEmpty01 +static int tolua_AllToLua_cItemGrid_IsSlotEmpty01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotEmpty'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotEmpty(a_X,a_Y); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_IsSlotEmpty00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_Clear00 +static int tolua_AllToLua_cItemGrid_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyCanFit of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HowManyCanFit00 +static int tolua_AllToLua_cItemGrid_HowManyCanFit00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_AllowNewStacks); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HowManyCanFit'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_AddItem00 +static int tolua_AllToLua_cItemGrid_AddItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + cItem* a_ItemStack = ((cItem*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); + int a_PrioritarySlot = ((int) tolua_tonumber(tolua_S,4,-1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItem'", NULL); +#endif + { + int tolua_ret = (int) self->AddItem(*a_ItemStack,a_AllowNewStacks,a_PrioritarySlot); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddItems of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_AddItems00 +static int tolua_AllToLua_cItemGrid_AddItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + cItems* a_ItemStackList = ((cItems*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); + int a_PrioritarySlot = ((int) tolua_tonumber(tolua_S,4,-1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItems'", NULL); +#endif + { + int tolua_ret = (int) self->AddItems(*a_ItemStackList,a_AllowNewStacks,a_PrioritarySlot); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ChangeSlotCount of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_ChangeSlotCount00 +static int tolua_AllToLua_cItemGrid_ChangeSlotCount00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + int a_AddToCount = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL); +#endif + { + int tolua_ret = (int) self->ChangeSlotCount(a_SlotNum,a_AddToCount); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ChangeSlotCount'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ChangeSlotCount of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_ChangeSlotCount01 +static int tolua_AllToLua_cItemGrid_ChangeSlotCount01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_AddToCount = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL); +#endif + { + int tolua_ret = (int) self->ChangeSlotCount(a_X,a_Y,a_AddToCount); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_ChangeSlotCount00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RemoveOneItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_RemoveOneItem00 +static int tolua_AllToLua_cItemGrid_RemoveOneItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneItem'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->RemoveOneItem(a_SlotNum); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RemoveOneItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RemoveOneItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_RemoveOneItem01 +static int tolua_AllToLua_cItemGrid_RemoveOneItem01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneItem'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->RemoveOneItem(a_X,a_Y); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_RemoveOneItem00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyItems of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HowManyItems00 +static int tolua_AllToLua_cItemGrid_HowManyItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyItems'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyItems(*a_Item); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HowManyItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasItems of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HasItems00 +static int tolua_AllToLua_cItemGrid_HasItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasItems'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasItems(*a_ItemStack); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFirstEmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetFirstEmptySlot00 +static int tolua_AllToLua_cItemGrid_GetFirstEmptySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFirstEmptySlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetFirstEmptySlot(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFirstEmptySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFirstUsedSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetFirstUsedSlot00 +static int tolua_AllToLua_cItemGrid_GetFirstUsedSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFirstUsedSlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetFirstUsedSlot(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFirstUsedSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLastEmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetLastEmptySlot00 +static int tolua_AllToLua_cItemGrid_GetLastEmptySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastEmptySlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetLastEmptySlot(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLastEmptySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLastUsedSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetLastUsedSlot00 +static int tolua_AllToLua_cItemGrid_GetLastUsedSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastUsedSlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetLastUsedSlot(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLastUsedSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNextEmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNextEmptySlot00 +static int tolua_AllToLua_cItemGrid_GetNextEmptySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_StartFrom = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNextEmptySlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetNextEmptySlot(a_StartFrom); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNextEmptySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNextUsedSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNextUsedSlot00 +static int tolua_AllToLua_cItemGrid_GetNextUsedSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_StartFrom = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNextUsedSlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetNextUsedSlot(a_StartFrom); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNextUsedSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyToItems of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_CopyToItems00 +static int tolua_AllToLua_cItemGrid_CopyToItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + cItems* a_Items = ((cItems*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyToItems'", NULL); +#endif + { + self->CopyToItems(*a_Items); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyToItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_DamageItem00 +static int tolua_AllToLua_cItemGrid_DamageItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + short a_Amount = ((short) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageItem(a_SlotNum,a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_DamageItem01 +static int tolua_AllToLua_cItemGrid_DamageItem01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + short a_Amount = ((short) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageItem(a_X,a_Y,a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_DamageItem00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosX of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosX00 +static int tolua_AllToLua_cBlockEntity_GetPosX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosX'", NULL); +#endif + { + int tolua_ret = (int) self->GetPosX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosY of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosY00 +static int tolua_AllToLua_cBlockEntity_GetPosY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosY'", NULL); +#endif + { + int tolua_ret = (int) self->GetPosY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosZ of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosZ00 +static int tolua_AllToLua_cBlockEntity_GetPosZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetPosZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockType of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetBlockType00 +static int tolua_AllToLua_cBlockEntity_GetBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWorld of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetWorld00 +static int tolua_AllToLua_cBlockEntity_GetWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); +#endif + { + cWorld* tolua_ret = (cWorld*) self->GetWorld(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkX of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetChunkX00 +static int tolua_AllToLua_cBlockEntity_GetChunkX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkZ of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetChunkZ00 +static int tolua_AllToLua_cBlockEntity_GetChunkZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelX of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetRelX00 +static int tolua_AllToLua_cBlockEntity_GetRelX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelX'", NULL); +#endif + { + int tolua_ret = (int) self->GetRelX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelZ of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetRelZ00 +static int tolua_AllToLua_cBlockEntity_GetRelZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetRelZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetSlot00 +static int tolua_AllToLua_cBlockEntityWithItems_GetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntityWithItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntityWithItems* self = (const cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetSlot01 +static int tolua_AllToLua_cBlockEntityWithItems_GetSlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntityWithItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const cBlockEntityWithItems* self = (const cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_X,a_Y); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBlockEntityWithItems_GetSlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_SetSlot00 +static int tolua_AllToLua_cBlockEntityWithItems_SetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_SlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_SetSlot01 +static int tolua_AllToLua_cBlockEntityWithItems_SetSlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_X,a_Y,*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cBlockEntityWithItems_SetSlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetContents of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetContents00 +static int tolua_AllToLua_cBlockEntityWithItems_GetContents00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetContents'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetContents(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetContents'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddDropSpenserDir of class cDropSpenserEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00 +static int tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_Direction = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddDropSpenserDir'", NULL); +#endif + { + self->AddDropSpenserDir(a_BlockX,a_BlockY,a_BlockZ,a_Direction); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockX); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockY); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockZ); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddDropSpenserDir'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Activate of class cDropSpenserEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_Activate00 +static int tolua_AllToLua_cDropSpenserEntity_Activate00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Activate'", NULL); +#endif + { + self->Activate(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Activate'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRedstonePower of class cDropSpenserEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00 +static int tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); + bool a_IsPowered = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRedstonePower'", NULL); +#endif + { + self->SetRedstonePower(a_IsPowered); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRedstonePower'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetInputSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetInputSlot00 +static int tolua_AllToLua_cFurnaceEntity_GetInputSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInputSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetInputSlot(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetInputSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFuelSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetFuelSlot00 +static int tolua_AllToLua_cFurnaceEntity_GetFuelSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFuelSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetFuelSlot(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFuelSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOutputSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetOutputSlot00 +static int tolua_AllToLua_cFurnaceEntity_GetOutputSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOutputSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetOutputSlot(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOutputSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetInputSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetInputSlot00 +static int tolua_AllToLua_cFurnaceEntity_SetInputSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetInputSlot'", NULL); +#endif + { + self->SetInputSlot(*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetInputSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFuelSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetFuelSlot00 +static int tolua_AllToLua_cFurnaceEntity_SetFuelSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFuelSlot'", NULL); +#endif + { + self->SetFuelSlot(*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFuelSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetOutputSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetOutputSlot00 +static int tolua_AllToLua_cFurnaceEntity_SetOutputSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetOutputSlot'", NULL); +#endif + { + self->SetOutputSlot(*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetOutputSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetTimeCooked of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetTimeCooked00 +static int tolua_AllToLua_cFurnaceEntity_GetTimeCooked00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTimeCooked'", NULL); +#endif + { + int tolua_ret = (int) self->GetTimeCooked(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTimeCooked'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetCookTimeLeft of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00 +static int tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCookTimeLeft'", NULL); +#endif + { + int tolua_ret = (int) self->GetCookTimeLeft(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetCookTimeLeft'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFuelBurnTimeLeft of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00 +static int tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFuelBurnTimeLeft'", NULL); +#endif + { + int tolua_ret = (int) self->GetFuelBurnTimeLeft(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFuelBurnTimeLeft'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasFuelTimeLeft of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00 +static int tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasFuelTimeLeft'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasFuelTimeLeft(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasFuelTimeLeft'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRecord of class cJukeboxEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_GetRecord00 +static int tolua_AllToLua_cJukeboxEntity_GetRecord00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRecord'", NULL); +#endif + { + int tolua_ret = (int) self->GetRecord(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRecord'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRecord of class cJukeboxEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_SetRecord00 +static int tolua_AllToLua_cJukeboxEntity_SetRecord00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); + int a_Record = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRecord'", NULL); +#endif + { + self->SetRecord(a_Record); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRecord'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: PlayRecord of class cJukeboxEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_PlayRecord00 +static int tolua_AllToLua_cJukeboxEntity_PlayRecord00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'PlayRecord'", NULL); +#endif + { + self->PlayRecord(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'PlayRecord'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: EjectRecord of class cJukeboxEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_EjectRecord00 +static int tolua_AllToLua_cJukeboxEntity_EjectRecord00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EjectRecord'", NULL); +#endif + { + self->EjectRecord(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'EjectRecord'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPitch of class cNoteEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_GetPitch00 +static int tolua_AllToLua_cNoteEntity_GetPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL); +#endif + { + char tolua_ret = (char) self->GetPitch(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPitch of class cNoteEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_SetPitch00 +static int tolua_AllToLua_cNoteEntity_SetPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); + char a_Pitch = ((char) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitch'", NULL); +#endif + { + self->SetPitch(a_Pitch); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IncrementPitch of class cNoteEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_IncrementPitch00 +static int tolua_AllToLua_cNoteEntity_IncrementPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IncrementPitch'", NULL); +#endif + { + self->IncrementPitch(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IncrementPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MakeSound of class cNoteEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_MakeSound00 +static int tolua_AllToLua_cNoteEntity_MakeSound00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MakeSound'", NULL); +#endif + { + self->MakeSound(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MakeSound'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetLines of class cSignEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_SetLines00 +static int tolua_AllToLua_cSignEntity_SetLines00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cSignEntity",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cSignEntity* self = (cSignEntity*) tolua_tousertype(tolua_S,1,0); + const AString a_Line1 = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_Line2 = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const AString a_Line3 = ((const AString) tolua_tocppstring(tolua_S,4,0)); + const AString a_Line4 = ((const AString) tolua_tocppstring(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLines'", NULL); +#endif + { + self->SetLines(a_Line1,a_Line2,a_Line3,a_Line4); + tolua_pushcppstring(tolua_S,(const char*)a_Line1); + tolua_pushcppstring(tolua_S,(const char*)a_Line2); + tolua_pushcppstring(tolua_S,(const char*)a_Line3); + tolua_pushcppstring(tolua_S,(const char*)a_Line4); + } + } + return 4; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetLines'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetLine of class cSignEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_SetLine00 +static int tolua_AllToLua_cSignEntity_SetLine00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cSignEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cSignEntity* self = (cSignEntity*) tolua_tousertype(tolua_S,1,0); + int a_Index = ((int) tolua_tonumber(tolua_S,2,0)); + const AString a_Line = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLine'", NULL); +#endif + { + self->SetLine(a_Index,a_Line); + tolua_pushcppstring(tolua_S,(const char*)a_Line); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetLine'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLine of class cSignEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_GetLine00 +static int tolua_AllToLua_cSignEntity_GetLine00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cSignEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cSignEntity* self = (const cSignEntity*) tolua_tousertype(tolua_S,1,0); + int a_Index = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLine'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetLine(a_Index); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLine'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Name of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Name +static int tolua_get_HTTPFormData_Name(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Name'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Name); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Name of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Name +static int tolua_set_HTTPFormData_Name(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Name'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Name = ((std::string) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Value of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Value +static int tolua_get_HTTPFormData_Value(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Value'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Value); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Value of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Value +static int tolua_set_HTTPFormData_Value(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Value'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Value = ((std::string) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Type of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Type +static int tolua_get_HTTPFormData_Type(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Type'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Type); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Type of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Type +static int tolua_set_HTTPFormData_Type(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Type'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Type = ((std::string) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Method of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Method +static int tolua_get_HTTPRequest_Method(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Method'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Method); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Method of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Method +static int tolua_set_HTTPRequest_Method(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Method'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Method = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Path of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Path +static int tolua_get_HTTPRequest_Path(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Path'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Path); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Path of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Path +static int tolua_set_HTTPRequest_Path(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Path'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Path = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Username of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Username +static int tolua_get_HTTPRequest_Username(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Username'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Username); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Username of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Username +static int tolua_set_HTTPRequest_Username(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Username'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Username = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Request of class HTTPTemplateRequest */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPTemplateRequest_Request +static int tolua_get_HTTPTemplateRequest_Request(lua_State* tolua_S) +{ + HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->Request,"HTTPRequest"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Request of class HTTPTemplateRequest */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPTemplateRequest_Request +static int tolua_set_HTTPTemplateRequest_Request(lua_State* tolua_S) +{ + HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"HTTPRequest",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Request = *((HTTPRequest*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Content of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_Content +static int tolua_get_sWebAdminPage_Content(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Content); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Content of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_Content +static int tolua_set_sWebAdminPage_Content(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Content = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: PluginName of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_PluginName +static int tolua_get_sWebAdminPage_PluginName(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->PluginName); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: PluginName of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_PluginName +static int tolua_set_sWebAdminPage_PluginName(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->PluginName = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: TabName of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_TabName +static int tolua_get_sWebAdminPage_TabName(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->TabName); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: TabName of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_TabName +static int tolua_set_sWebAdminPage_TabName(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->TabName = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPage of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00 +static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); + const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL); +#endif + { + sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage)); + tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDefaultPage of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetDefaultPage00 +static int tolua_AllToLua_cWebAdmin_GetDefaultPage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultPage'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetDefaultPage(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDefaultPage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBaseURL of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetBaseURL00 +static int tolua_AllToLua_cWebAdmin_GetBaseURL00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); + const AString a_URL = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBaseURL'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetBaseURL(a_URL); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_URL); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBaseURL'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHTMLEscapedString of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00 +static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cWebAdmin",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Input = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + AString tolua_ret = (AString) cWebAdmin::GetHTMLEscapedString(a_Input); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Input); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHTMLEscapedString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWebTitle of class cWebPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_GetWebTitle00 +static int tolua_AllToLua_cWebPlugin_GetWebTitle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWebPlugin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWebPlugin* self = (const cWebPlugin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWebTitle'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetWebTitle(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWebTitle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HandleWebRequest of class cWebPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_HandleWebRequest00 +static int tolua_AllToLua_cWebPlugin_HandleWebRequest00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWebPlugin",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0); + const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HandleWebRequest'", NULL); +#endif + { + AString tolua_ret = (AString) self->HandleWebRequest(a_Request); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HandleWebRequest'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SafeString of class cWebPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_SafeString00 +static int tolua_AllToLua_cWebPlugin_SafeString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cWebPlugin",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_String = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + AString tolua_ret = (AString) cWebPlugin::SafeString(a_String); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_String); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SafeString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Get of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_Get00 +static int tolua_AllToLua_cRoot_Get00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cRoot* tolua_ret = (cRoot*) cRoot::Get(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cRoot"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetServer of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetServer00 +static int tolua_AllToLua_cRoot_GetServer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetServer'", NULL); +#endif + { + cServer* tolua_ret = (cServer*) self->GetServer(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cServer"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetServer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDefaultWorld of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetDefaultWorld00 +static int tolua_AllToLua_cRoot_GetDefaultWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultWorld'", NULL); +#endif + { + cWorld* tolua_ret = (cWorld*) self->GetDefaultWorld(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDefaultWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWorld of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetWorld00 +static int tolua_AllToLua_cRoot_GetWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); + const AString a_WorldName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); +#endif + { + cWorld* tolua_ret = (cWorld*) self->GetWorld(a_WorldName); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); + tolua_pushcppstring(tolua_S,(const char*)a_WorldName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPrimaryServerVersion of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPrimaryServerVersion00 +static int tolua_AllToLua_cRoot_GetPrimaryServerVersion00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cRoot* self = (const cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPrimaryServerVersion'", NULL); +#endif + { + int tolua_ret = (int) self->GetPrimaryServerVersion(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPrimaryServerVersion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPrimaryServerVersion of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_SetPrimaryServerVersion00 +static int tolua_AllToLua_cRoot_SetPrimaryServerVersion00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); + int a_Version = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPrimaryServerVersion'", NULL); +#endif + { + self->SetPrimaryServerVersion(a_Version); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPrimaryServerVersion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGroupManager of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetGroupManager00 +static int tolua_AllToLua_cRoot_GetGroupManager00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGroupManager'", NULL); +#endif + { + cGroupManager* tolua_ret = (cGroupManager*) self->GetGroupManager(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cGroupManager"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGroupManager'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetCraftingRecipes of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetCraftingRecipes00 +static int tolua_AllToLua_cRoot_GetCraftingRecipes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCraftingRecipes'", NULL); +#endif + { + cCraftingRecipes* tolua_ret = (cCraftingRecipes*) self->GetCraftingRecipes(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingRecipes"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetCraftingRecipes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFurnaceRecipe of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetFurnaceRecipe00 +static int tolua_AllToLua_cRoot_GetFurnaceRecipe00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFurnaceRecipe'", NULL); +#endif + { + cFurnaceRecipe* tolua_ret = (cFurnaceRecipe*) self->GetFurnaceRecipe(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cFurnaceRecipe"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFurnaceRecipe'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWebAdmin of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetWebAdmin00 +static int tolua_AllToLua_cRoot_GetWebAdmin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWebAdmin'", NULL); +#endif + { + cWebAdmin* tolua_ret = (cWebAdmin*) self->GetWebAdmin(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWebAdmin"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWebAdmin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPluginManager of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPluginManager00 +static int tolua_AllToLua_cRoot_GetPluginManager00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPluginManager'", NULL); +#endif + { + cPluginManager* tolua_ret = (cPluginManager*) self->GetPluginManager(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPluginManager"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPluginManager'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: QueueExecuteConsoleCommand of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00 +static int tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); + const AString a_Cmd = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueExecuteConsoleCommand'", NULL); +#endif + { + self->QueueExecuteConsoleCommand(a_Cmd); + tolua_pushcppstring(tolua_S,(const char*)a_Cmd); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'QueueExecuteConsoleCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetTotalChunkCount of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetTotalChunkCount00 +static int tolua_AllToLua_cRoot_GetTotalChunkCount00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTotalChunkCount'", NULL); +#endif + { + int tolua_ret = (int) self->GetTotalChunkCount(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTotalChunkCount'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SaveAllChunks of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_SaveAllChunks00 +static int tolua_AllToLua_cRoot_SaveAllChunks00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveAllChunks'", NULL); +#endif + { + self->SaveAllChunks(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SaveAllChunks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: BroadcastChat of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_BroadcastChat00 +static int tolua_AllToLua_cRoot_BroadcastChat00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastChat'", NULL); +#endif + { + self->BroadcastChat(a_Message); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BroadcastChat'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetProtocolVersionTextFromInt of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00 +static int tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_ProtocolVersionNum = ((int) tolua_tonumber(tolua_S,2,0)); + { + AString tolua_ret = (AString) cRoot::GetProtocolVersionTextFromInt(a_ProtocolVersionNum); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetProtocolVersionTextFromInt'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetVirtualRAMUsage of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetVirtualRAMUsage00 +static int tolua_AllToLua_cRoot_GetVirtualRAMUsage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + int tolua_ret = (int) cRoot::GetVirtualRAMUsage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetVirtualRAMUsage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPhysicalRAMUsage of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPhysicalRAMUsage00 +static int tolua_AllToLua_cRoot_GetPhysicalRAMUsage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + int tolua_ret = (int) cRoot::GetPhysicalRAMUsage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPhysicalRAMUsage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00 +static int tolua_AllToLua_Vector3f_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00_local +static int tolua_AllToLua_Vector3f_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new01 +static int tolua_AllToLua_Vector3f_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new01_local +static int tolua_AllToLua_Vector3f_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new02 +static int tolua_AllToLua_Vector3f_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new02_local +static int tolua_AllToLua_Vector3f_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new03 +static int tolua_AllToLua_Vector3f_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new03_local +static int tolua_AllToLua_Vector3f_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new04 +static int tolua_AllToLua_Vector3f_new04(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new03(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new04_local +static int tolua_AllToLua_Vector3f_new04_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new03_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new05 +static int tolua_AllToLua_Vector3f_new05(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + float a_x = ((float) tolua_tonumber(tolua_S,2,0)); + float a_y = ((float) tolua_tonumber(tolua_S,3,0)); + float a_z = ((float) tolua_tonumber(tolua_S,4,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new04(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new05_local +static int tolua_AllToLua_Vector3f_new05_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + float a_x = ((float) tolua_tonumber(tolua_S,2,0)); + float a_y = ((float) tolua_tonumber(tolua_S,3,0)); + float a_z = ((float) tolua_tonumber(tolua_S,4,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new04_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Set00 +static int tolua_AllToLua_Vector3f_Set00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); + float a_x = ((float) tolua_tonumber(tolua_S,2,0)); + float a_y = ((float) tolua_tonumber(tolua_S,3,0)); + float a_z = ((float) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_x,a_y,a_z); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Normalize of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Normalize00 +static int tolua_AllToLua_Vector3f_Normalize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Normalize'", NULL); +#endif + { + self->Normalize(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Normalize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: NormalizeCopy of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_NormalizeCopy00 +static int tolua_AllToLua_Vector3f_NormalizeCopy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->NormalizeCopy(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'NormalizeCopy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: NormalizeCopy of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_NormalizeCopy01 +static int tolua_AllToLua_Vector3f_NormalizeCopy01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + Vector3f* a_V = ((Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); +#endif + { + self->NormalizeCopy(*a_V); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_Vector3f_NormalizeCopy00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Length of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Length00 +static int tolua_AllToLua_Vector3f_Length00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL); +#endif + { + float tolua_ret = (float) self->Length(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SqrLength of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_SqrLength00 +static int tolua_AllToLua_Vector3f_SqrLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL); +#endif + { + float tolua_ret = (float) self->SqrLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Dot of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Dot00 +static int tolua_AllToLua_Vector3f_Dot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* a_V = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dot'", NULL); +#endif + { + float tolua_ret = (float) self->Dot(*a_V); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Dot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Cross of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Cross00 +static int tolua_AllToLua_Vector3f_Cross00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Cross'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->Cross(*v); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Cross'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Equals of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Equals00 +static int tolua_AllToLua_Vector3f_Equals00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); +#endif + { + bool tolua_ret = (bool) self->Equals(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator+ of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__add00 +static int tolua_AllToLua_Vector3f__add00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator+(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.add'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator+ of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__add01 +static int tolua_AllToLua_Vector3f__add01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator+(v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f__add00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator- of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__sub00 +static int tolua_AllToLua_Vector3f__sub00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator-(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.sub'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator- of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__sub01 +static int tolua_AllToLua_Vector3f__sub01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator-(v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f__sub00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator* of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__mul00 +static int tolua_AllToLua_Vector3f__mul00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const float f = ((const float) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator*(f); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.mul'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator* of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__mul01 +static int tolua_AllToLua_Vector3f__mul01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator*(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f__mul00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: x of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3f_x +static int tolua_get_Vector3f_x(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->x); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: x of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3f_x +static int tolua_set_Vector3f_x(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->x = ((float) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: y of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3f_y +static int tolua_get_Vector3f_y(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->y); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: y of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3f_y +static int tolua_set_Vector3f_y(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->y = ((float) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: z of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3f_z +static int tolua_get_Vector3f_z(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->z); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: z of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3f_z +static int tolua_set_Vector3f_z(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->z = ((float) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new00 +static int tolua_AllToLua_Vector3d_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new00_local +static int tolua_AllToLua_Vector3d_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new01 +static int tolua_AllToLua_Vector3d_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new01_local +static int tolua_AllToLua_Vector3d_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new02 +static int tolua_AllToLua_Vector3d_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new02_local +static int tolua_AllToLua_Vector3d_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new03 +static int tolua_AllToLua_Vector3d_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + double a_x = ((double) tolua_tonumber(tolua_S,2,0)); + double a_y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_z = ((double) tolua_tonumber(tolua_S,4,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new03_local +static int tolua_AllToLua_Vector3d_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + double a_x = ((double) tolua_tonumber(tolua_S,2,0)); + double a_y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_z = ((double) tolua_tonumber(tolua_S,4,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Set00 +static int tolua_AllToLua_Vector3d_Set00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); + double a_x = ((double) tolua_tonumber(tolua_S,2,0)); + double a_y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_z = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_x,a_y,a_z); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Normalize of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Normalize00 +static int tolua_AllToLua_Vector3d_Normalize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Normalize'", NULL); +#endif + { + self->Normalize(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Normalize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: NormalizeCopy of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_NormalizeCopy00 +static int tolua_AllToLua_Vector3d_NormalizeCopy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->NormalizeCopy(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'NormalizeCopy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: NormalizeCopy of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_NormalizeCopy01 +static int tolua_AllToLua_Vector3d_NormalizeCopy01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); + Vector3d* a_V = ((Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); +#endif + { + self->NormalizeCopy(*a_V); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_Vector3d_NormalizeCopy00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Length of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Length00 +static int tolua_AllToLua_Vector3d_Length00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL); +#endif + { + double tolua_ret = (double) self->Length(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SqrLength of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_SqrLength00 +static int tolua_AllToLua_Vector3d_SqrLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL); +#endif + { + double tolua_ret = (double) self->SqrLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Dot of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Dot00 +static int tolua_AllToLua_Vector3d_Dot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_V = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dot'", NULL); +#endif + { + double tolua_ret = (double) self->Dot(*a_V); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Dot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Cross of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Cross00 +static int tolua_AllToLua_Vector3d_Cross00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Cross'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->Cross(*v); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Cross'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LineCoeffToXYPlane of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToXYPlane00 +static int tolua_AllToLua_Vector3d_LineCoeffToXYPlane00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToXYPlane'", NULL); +#endif + { + double tolua_ret = (double) self->LineCoeffToXYPlane(*a_OtherEnd,a_Z); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LineCoeffToXYPlane'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LineCoeffToXZPlane of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToXZPlane00 +static int tolua_AllToLua_Vector3d_LineCoeffToXZPlane00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToXZPlane'", NULL); +#endif + { + double tolua_ret = (double) self->LineCoeffToXZPlane(*a_OtherEnd,a_Y); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LineCoeffToXZPlane'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LineCoeffToYZPlane of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToYZPlane00 +static int tolua_AllToLua_Vector3d_LineCoeffToYZPlane00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_X = ((double) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToYZPlane'", NULL); +#endif + { + double tolua_ret = (double) self->LineCoeffToYZPlane(*a_OtherEnd,a_X); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LineCoeffToYZPlane'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Equals of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Equals00 +static int tolua_AllToLua_Vector3d_Equals00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); +#endif + { + bool tolua_ret = (bool) self->Equals(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator+ of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__add00 +static int tolua_AllToLua_Vector3d__add00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator+(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.add'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator+ of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__add01 +static int tolua_AllToLua_Vector3d__add01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator+(v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d__add00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator- of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__sub00 +static int tolua_AllToLua_Vector3d__sub00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator-(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.sub'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator- of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__sub01 +static int tolua_AllToLua_Vector3d__sub01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator-(v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d__sub00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator* of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__mul00 +static int tolua_AllToLua_Vector3d__mul00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const double f = ((const double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator*(f); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.mul'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator* of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__mul01 +static int tolua_AllToLua_Vector3d__mul01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator*(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d__mul00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator/ of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__div00 +static int tolua_AllToLua_Vector3d__div00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const double f = ((const double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator/'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator/(f); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.div'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: x of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_x +static int tolua_get_Vector3d_x(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->x); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: x of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3d_x +static int tolua_set_Vector3d_x(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->x = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: y of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_y +static int tolua_get_Vector3d_y(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->y); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: y of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3d_y +static int tolua_set_Vector3d_y(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->y = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: z of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_z +static int tolua_get_Vector3d_z(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->z); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: z of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3d_z +static int tolua_set_Vector3d_z(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->z = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: EPS of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_EPS +static int tolua_get_Vector3d_EPS(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)Vector3d::EPS); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: NO_INTERSECTION of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_NO_INTERSECTION +static int tolua_get_Vector3d_NO_INTERSECTION(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)Vector3d::NO_INTERSECTION); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new00 +static int tolua_AllToLua_Vector3i_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new00_local +static int tolua_AllToLua_Vector3i_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new01 +static int tolua_AllToLua_Vector3i_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new01_local +static int tolua_AllToLua_Vector3i_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new02 +static int tolua_AllToLua_Vector3i_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_x = ((int) tolua_tonumber(tolua_S,2,0)); + int a_y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_z = ((int) tolua_tonumber(tolua_S,4,0)); + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new02_local +static int tolua_AllToLua_Vector3i_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_x = ((int) tolua_tonumber(tolua_S,2,0)); + int a_y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_z = ((int) tolua_tonumber(tolua_S,4,0)); + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Set00 +static int tolua_AllToLua_Vector3i_Set00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); + int a_x = ((int) tolua_tonumber(tolua_S,2,0)); + int a_y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_z = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_x,a_y,a_z); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Length of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Length00 +static int tolua_AllToLua_Vector3i_Length00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL); +#endif + { + float tolua_ret = (float) self->Length(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SqrLength of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_SqrLength00 +static int tolua_AllToLua_Vector3i_SqrLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL); +#endif + { + int tolua_ret = (int) self->SqrLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Equals of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Equals00 +static int tolua_AllToLua_Vector3i_Equals00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); +#endif + { + bool tolua_ret = (bool) self->Equals(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Equals of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Equals01 +static int tolua_AllToLua_Vector3i_Equals01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); +#endif + { + bool tolua_ret = (bool) self->Equals(v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_Equals00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: x of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3i_x +static int tolua_get_Vector3i_x(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->x); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: x of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3i_x +static int tolua_set_Vector3i_x(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->x = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: y of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3i_y +static int tolua_get_Vector3i_y(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->y); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: y of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3i_y +static int tolua_set_Vector3i_y(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->y = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: z of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3i_z +static int tolua_get_Vector3i_z(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->z); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: z of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3i_z +static int tolua_set_Vector3i_z(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->z = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: p1 of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_get_cCuboid_p1 +static int tolua_get_cCuboid_p1(lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p1'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->p1,"Vector3i"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: p1 of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_set_cCuboid_p1 +static int tolua_set_cCuboid_p1(lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p1'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3i",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->p1 = *((Vector3i*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: p2 of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_get_cCuboid_p2 +static int tolua_get_cCuboid_p2(lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p2'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->p2,"Vector3i"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: p2 of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_set_cCuboid_p2 +static int tolua_set_cCuboid_p2(lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p2'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3i",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->p2 = *((Vector3i*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new00 +static int tolua_AllToLua_cCuboid_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new00_local +static int tolua_AllToLua_cCuboid_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new01 +static int tolua_AllToLua_cCuboid_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cCuboid* a_Cuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_Cuboid)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new01_local +static int tolua_AllToLua_cCuboid_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cCuboid* a_Cuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_Cuboid)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new02 +static int tolua_AllToLua_cCuboid_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* a_p1 = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + const Vector3i* a_p2 = ((const Vector3i*) tolua_tousertype(tolua_S,3,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_p1,*a_p2)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new02_local +static int tolua_AllToLua_cCuboid_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* a_p1 = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + const Vector3i* a_p2 = ((const Vector3i*) tolua_tousertype(tolua_S,3,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_p1,*a_p2)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new03 +static int tolua_AllToLua_cCuboid_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new03_local +static int tolua_AllToLua_cCuboid_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new04 +static int tolua_AllToLua_cCuboid_new04(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + int a_X2 = ((int) tolua_tonumber(tolua_S,5,0)); + int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0)); + int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new03(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new04_local +static int tolua_AllToLua_cCuboid_new04_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + int a_X2 = ((int) tolua_tonumber(tolua_S,5,0)); + int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0)); + int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new03_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Assign of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Assign00 +static int tolua_AllToLua_cCuboid_Assign00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + int a_X2 = ((int) tolua_tonumber(tolua_S,5,0)); + int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0)); + int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Assign'", NULL); +#endif + { + self->Assign(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Assign'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Sort of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Sort00 +static int tolua_AllToLua_cCuboid_Sort00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Sort'", NULL); +#endif + { + self->Sort(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Sort'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DifX of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifX00 +static int tolua_AllToLua_cCuboid_DifX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifX'", NULL); +#endif + { + int tolua_ret = (int) self->DifX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DifX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DifY of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifY00 +static int tolua_AllToLua_cCuboid_DifY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifY'", NULL); +#endif + { + int tolua_ret = (int) self->DifY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DifY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DifZ of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifZ00 +static int tolua_AllToLua_cCuboid_DifZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifZ'", NULL); +#endif + { + int tolua_ret = (int) self->DifZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DifZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DoesIntersect of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DoesIntersect00 +static int tolua_AllToLua_cCuboid_DoesIntersect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_Other = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoesIntersect'", NULL); +#endif + { + bool tolua_ret = (bool) self->DoesIntersect(*a_Other); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DoesIntersect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside00 +static int tolua_AllToLua_cCuboid_IsInside00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInside'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside01 +static int tolua_AllToLua_cCuboid_IsInside01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(a_X,a_Y,a_Z); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_IsInside00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside02 +static int tolua_AllToLua_cCuboid_IsInside02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_IsInside01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCompletelyInside of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsCompletelyInside00 +static int tolua_AllToLua_cCuboid_IsCompletelyInside00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_Outer = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCompletelyInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCompletelyInside(*a_Outer); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCompletelyInside'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Move of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Move00 +static int tolua_AllToLua_cCuboid_Move00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); + int a_OfsX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_OfsY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_OfsZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL); +#endif + { + self->Move(a_OfsX,a_OfsY,a_OfsZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Move'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSorted of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsSorted00 +static int tolua_AllToLua_cCuboid_IsSorted00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSorted'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSorted(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSorted'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new00 +static int tolua_AllToLua_cBoundingBox_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + double a_MinX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_MaxX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_MinY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_MaxY = ((double) tolua_tonumber(tolua_S,5,0)); + double a_MinZ = ((double) tolua_tonumber(tolua_S,6,0)); + double a_MaxZ = ((double) tolua_tonumber(tolua_S,7,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new00_local +static int tolua_AllToLua_cBoundingBox_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + double a_MinX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_MaxX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_MinY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_MaxY = ((double) tolua_tonumber(tolua_S,5,0)); + double a_MinZ = ((double) tolua_tonumber(tolua_S,6,0)); + double a_MaxZ = ((double) tolua_tonumber(tolua_S,7,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new01 +static int tolua_AllToLua_cBoundingBox_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Min,*a_Max)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new01_local +static int tolua_AllToLua_cBoundingBox_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Min,*a_Max)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new02 +static int tolua_AllToLua_cBoundingBox_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_Radius = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Height = ((double) tolua_tonumber(tolua_S,4,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Pos,a_Radius,a_Height)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new02_local +static int tolua_AllToLua_cBoundingBox_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_Radius = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Height = ((double) tolua_tonumber(tolua_S,4,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Pos,a_Radius,a_Height)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new03 +static int tolua_AllToLua_cBoundingBox_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cBoundingBox* a_Orig = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Orig)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new03_local +static int tolua_AllToLua_cBoundingBox_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cBoundingBox* a_Orig = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Orig)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Move of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Move00 +static int tolua_AllToLua_cBoundingBox_Move00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + double a_OffX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_OffY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_OffZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL); +#endif + { + self->Move(a_OffX,a_OffY,a_OffZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Move'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Move of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Move01 +static int tolua_AllToLua_cBoundingBox_Move01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Off = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL); +#endif + { + self->Move(*a_Off); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_Move00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Expand of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Expand00 +static int tolua_AllToLua_cBoundingBox_Expand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + double a_ExpandX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_ExpandY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_ExpandZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Expand'", NULL); +#endif + { + self->Expand(a_ExpandX,a_ExpandY,a_ExpandZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Expand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DoesIntersect of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_DoesIntersect00 +static int tolua_AllToLua_cBoundingBox_DoesIntersect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const cBoundingBox* a_Other = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoesIntersect'", NULL); +#endif + { + bool tolua_ret = (bool) self->DoesIntersect(*a_Other); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DoesIntersect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Union of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Union00 +static int tolua_AllToLua_cBoundingBox_Union00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const cBoundingBox* a_Other = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Union'", NULL); +#endif + { + cBoundingBox tolua_ret = (cBoundingBox) self->Union(*a_Other); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cBoundingBox)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cBoundingBox)); + tolua_pushusertype(tolua_S,tolua_obj,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Union'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside00 +static int tolua_AllToLua_cBoundingBox_IsInside00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Point = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*a_Point); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInside'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside01 +static int tolua_AllToLua_cBoundingBox_IsInside01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + double a_X = ((double) tolua_tonumber(tolua_S,2,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(a_X,a_Y,a_Z); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside02 +static int tolua_AllToLua_cBoundingBox_IsInside02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + cBoundingBox* a_Other = ((cBoundingBox*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*a_Other); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside03 +static int tolua_AllToLua_cBoundingBox_IsInside03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*a_Min,*a_Max); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside04 +static int tolua_AllToLua_cBoundingBox_IsInside04(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + const Vector3d* a_Point = ((const Vector3d*) tolua_tousertype(tolua_S,4,0)); + { + bool tolua_ret = (bool) cBoundingBox::IsInside(*a_Min,*a_Max,*a_Point); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside03(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside05 +static int tolua_AllToLua_cBoundingBox_IsInside05(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + double a_X = ((double) tolua_tonumber(tolua_S,4,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,5,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,6,0)); + { + bool tolua_ret = (bool) cBoundingBox::IsInside(*a_Min,*a_Max,a_X,a_Y,a_Z); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside04(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CalcLineIntersection of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_CalcLineIntersection00 +static int tolua_AllToLua_cBoundingBox_CalcLineIntersection00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Line1 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Line2 = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + double a_LineCoeff = ((double) tolua_tonumber(tolua_S,4,0)); + char a_Face = ((char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CalcLineIntersection'", NULL); +#endif + { + bool tolua_ret = (bool) self->CalcLineIntersection(*a_Line1,*a_Line2,a_LineCoeff,a_Face); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushnumber(tolua_S,(lua_Number)a_LineCoeff); + tolua_pushnumber(tolua_S,(lua_Number)a_Face); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CalcLineIntersection'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CalcLineIntersection of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_CalcLineIntersection01 +static int tolua_AllToLua_cBoundingBox_CalcLineIntersection01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + const Vector3d* a_Line1 = ((const Vector3d*) tolua_tousertype(tolua_S,4,0)); + const Vector3d* a_Line2 = ((const Vector3d*) tolua_tousertype(tolua_S,5,0)); + double a_LineCoeff = ((double) tolua_tonumber(tolua_S,6,0)); + char a_Face = ((char) tolua_tonumber(tolua_S,7,0)); + { + bool tolua_ret = (bool) cBoundingBox::CalcLineIntersection(*a_Min,*a_Max,*a_Line1,*a_Line2,a_LineCoeff,a_Face); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushnumber(tolua_S,(lua_Number)a_LineCoeff); + tolua_pushnumber(tolua_S,(lua_Number)a_Face); + } + } + return 3; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_CalcLineIntersection00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: BlockHitPosition of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_get_cTracer_BlockHitPosition +static int tolua_get_cTracer_BlockHitPosition(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'BlockHitPosition'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->BlockHitPosition,"Vector3f"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: BlockHitPosition of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_set_cTracer_BlockHitPosition +static int tolua_set_cTracer_BlockHitPosition(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'BlockHitPosition'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->BlockHitPosition = *((Vector3f*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: HitNormal of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_get_cTracer_HitNormal +static int tolua_get_cTracer_HitNormal(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'HitNormal'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->HitNormal,"Vector3f"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: HitNormal of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_set_cTracer_HitNormal +static int tolua_set_cTracer_HitNormal(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'HitNormal'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->HitNormal = *((Vector3f*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: RealHit of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_get_cTracer_RealHit +static int tolua_get_cTracer_RealHit(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RealHit'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->RealHit,"Vector3f"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: RealHit of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_set_cTracer_RealHit +static int tolua_set_cTracer_RealHit(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RealHit'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->RealHit = *((Vector3f*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_new00 +static int tolua_AllToLua_cTracer_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cTracer",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + { + cTracer* tolua_ret = (cTracer*) Mtolua_new((cTracer)(a_World)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cTracer"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_new00_local +static int tolua_AllToLua_cTracer_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cTracer",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + { + cTracer* tolua_ret = (cTracer*) Mtolua_new((cTracer)(a_World)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cTracer"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_delete00 +static int tolua_AllToLua_cTracer_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL); +#endif + Mtolua_delete(self); + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Trace of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_Trace00 +static int tolua_AllToLua_cTracer_Trace00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3f",0,&tolua_err)) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); + const Vector3f* a_Start = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + const Vector3f* a_Direction = ((const Vector3f*) tolua_tousertype(tolua_S,3,0)); + int a_Distance = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Trace'", NULL); +#endif + { + bool tolua_ret = (bool) self->Trace(*a_Start,*a_Direction,a_Distance); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Trace'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Trace of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_Trace01 +static int tolua_AllToLua_cTracer_Trace01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3f",0,&tolua_err)) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); + const Vector3f* a_Start = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + const Vector3f* a_Direction = ((const Vector3f*) tolua_tousertype(tolua_S,3,0)); + int a_Distance = ((int) tolua_tonumber(tolua_S,4,0)); + bool a_LineOfSight = ((bool) tolua_toboolean(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Trace'", NULL); +#endif + { + bool tolua_ret = (bool) self->Trace(*a_Start,*a_Direction,a_Distance,a_LineOfSight); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cTracer_Trace00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetName of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_SetName00 +static int tolua_AllToLua_cGroup_SetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Name = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL); +#endif + { + self->SetName(a_Name); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetName of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_GetName00 +static int tolua_AllToLua_cGroup_GetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cGroup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cGroup* self = (const cGroup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); +#endif + { + const std::string tolua_ret = (const std::string) self->GetName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetColor of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_SetColor00 +static int tolua_AllToLua_cGroup_SetColor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Color = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetColor'", NULL); +#endif + { + self->SetColor(a_Color); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetColor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddCommand of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_AddCommand00 +static int tolua_AllToLua_cGroup_AddCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Command = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddCommand'", NULL); +#endif + { + self->AddCommand(a_Command); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPermission of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_AddPermission00 +static int tolua_AllToLua_cGroup_AddPermission00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Permission = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPermission'", NULL); +#endif + { + self->AddPermission(a_Permission); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPermission'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: InheritFrom of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_InheritFrom00 +static int tolua_AllToLua_cGroup_InheritFrom00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cGroup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + cGroup* a_Group = ((cGroup*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'InheritFrom'", NULL); +#endif + { + self->InheritFrom(a_Group); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'InheritFrom'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasCommand of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_HasCommand00 +static int tolua_AllToLua_cGroup_HasCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Command = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasCommand'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasCommand(a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetColor of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_GetColor00 +static int tolua_AllToLua_cGroup_GetColor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cGroup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cGroup* self = (const cGroup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetColor(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetColor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_new00 +static int tolua_AllToLua_cBlockArea_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cBlockArea* tolua_ret = (cBlockArea*) Mtolua_new((cBlockArea)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockArea"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_new00_local +static int tolua_AllToLua_cBlockArea_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cBlockArea* tolua_ret = (cBlockArea*) Mtolua_new((cBlockArea)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockArea"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_delete00 +static int tolua_AllToLua_cBlockArea_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL); +#endif + Mtolua_delete(self); + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Clear00 +static int tolua_AllToLua_cBlockArea_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Create of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Create00 +static int tolua_AllToLua_cBlockArea_Create00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_SizeX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_SizeY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SizeZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Create'", NULL); +#endif + { + self->Create(a_SizeX,a_SizeY,a_SizeZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Create'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Create of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Create01 +static int tolua_AllToLua_cBlockArea_Create01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_SizeX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_SizeY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SizeZ = ((int) tolua_tonumber(tolua_S,4,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Create'", NULL); +#endif + { + self->Create(a_SizeX,a_SizeY,a_SizeZ,a_DataTypes); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cBlockArea_Create00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetOrigin of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetOrigin00 +static int tolua_AllToLua_cBlockArea_SetOrigin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_OriginX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_OriginY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_OriginZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetOrigin'", NULL); +#endif + { + self->SetOrigin(a_OriginX,a_OriginY,a_OriginZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetOrigin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Read of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Read00 +static int tolua_AllToLua_cBlockArea_Read00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnoobj(tolua_S,9,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,7,0)); + int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,8,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Read'", NULL); +#endif + { + bool tolua_ret = (bool) self->Read(a_World,a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Read'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Read of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Read01 +static int tolua_AllToLua_cBlockArea_Read01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,7,0)); + int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,8,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,9,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Read'", NULL); +#endif + { + bool tolua_ret = (bool) self->Read(a_World,a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ,a_DataTypes); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBlockArea_Read00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Write of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Write00 +static int tolua_AllToLua_cBlockArea_Write00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Write'", NULL); +#endif + { + bool tolua_ret = (bool) self->Write(a_World,a_MinBlockX,a_MinBlockY,a_MinBlockZ); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Write'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Write of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Write01 +static int tolua_AllToLua_cBlockArea_Write01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,5,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Write'", NULL); +#endif + { + bool tolua_ret = (bool) self->Write(a_World,a_MinBlockX,a_MinBlockY,a_MinBlockZ,a_DataTypes); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBlockArea_Write00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyTo of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_CopyTo00 +static int tolua_AllToLua_cBlockArea_CopyTo00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBlockArea",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + cBlockArea* a_Into = ((cBlockArea*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyTo'", NULL); +#endif + { + self->CopyTo(*a_Into); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyTo'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyFrom of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_CopyFrom00 +static int tolua_AllToLua_cBlockArea_CopyFrom00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const cBlockArea* a_From = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyFrom'", NULL); +#endif + { + self->CopyFrom(*a_From); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyFrom'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DumpToRawFile of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_DumpToRawFile00 +static int tolua_AllToLua_cBlockArea_DumpToRawFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DumpToRawFile'", NULL); +#endif + { + self->DumpToRawFile(a_FileName); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DumpToRawFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LoadFromSchematicFile of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_LoadFromSchematicFile00 +static int tolua_AllToLua_cBlockArea_LoadFromSchematicFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadFromSchematicFile'", NULL); +#endif + { + bool tolua_ret = (bool) self->LoadFromSchematicFile(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LoadFromSchematicFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SaveToSchematicFile of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SaveToSchematicFile00 +static int tolua_AllToLua_cBlockArea_SaveToSchematicFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveToSchematicFile'", NULL); +#endif + { + bool tolua_ret = (bool) self->SaveToSchematicFile(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SaveToSchematicFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Crop of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Crop00 +static int tolua_AllToLua_cBlockArea_Crop00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_AddMinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_SubMaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_AddMinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_SubMaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_AddMinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_SubMaxZ = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Crop'", NULL); +#endif + { + self->Crop(a_AddMinX,a_SubMaxX,a_AddMinY,a_SubMaxY,a_AddMinZ,a_SubMaxZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Crop'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Expand of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Expand00 +static int tolua_AllToLua_cBlockArea_Expand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_SubMinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_AddMaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SubMinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_AddMaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_SubMinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_AddMaxZ = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Expand'", NULL); +#endif + { + self->Expand(a_SubMinX,a_AddMaxX,a_SubMinY,a_AddMaxY,a_SubMinZ,a_AddMaxZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Expand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Merge of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Merge00 +static int tolua_AllToLua_cBlockArea_Merge00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const cBlockArea* a_Src = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0)); + int a_RelX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,5,0)); + cBlockArea::eMergeStrategy a_Strategy = ((cBlockArea::eMergeStrategy) (int) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Merge'", NULL); +#endif + { + self->Merge(*a_Src,a_RelX,a_RelY,a_RelZ,a_Strategy); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Merge'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Fill of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Fill00 +static int tolua_AllToLua_cBlockArea_Fill00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnumber(tolua_S,5,1,&tolua_err) || + !tolua_isnumber(tolua_S,6,1,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,2,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,6,0x0f)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Fill'", NULL); +#endif + { + self->Fill(a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Fill'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FillRelCuboid of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_FillRelCuboid00 +static int tolua_AllToLua_cBlockArea_FillRelCuboid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnumber(tolua_S,10,1,&tolua_err) || + !tolua_isnumber(tolua_S,11,1,&tolua_err) || + !tolua_isnumber(tolua_S,12,1,&tolua_err) || + !tolua_isnoobj(tolua_S,13,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_MinRelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxRelX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinRelY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxRelY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinRelZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxRelZ = ((int) tolua_tonumber(tolua_S,7,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,8,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,9,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,10,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,11,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,12,0x0f)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL); +#endif + { + self->FillRelCuboid(a_MinRelX,a_MaxRelX,a_MinRelY,a_MaxRelY,a_MinRelZ,a_MaxRelZ,a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FillRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RelLine of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RelLine00 +static int tolua_AllToLua_cBlockArea_RelLine00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnumber(tolua_S,10,1,&tolua_err) || + !tolua_isnumber(tolua_S,11,1,&tolua_err) || + !tolua_isnumber(tolua_S,12,1,&tolua_err) || + !tolua_isnoobj(tolua_S,13,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ1 = ((int) tolua_tonumber(tolua_S,4,0)); + int a_RelX2 = ((int) tolua_tonumber(tolua_S,5,0)); + int a_RelY2 = ((int) tolua_tonumber(tolua_S,6,0)); + int a_RelZ2 = ((int) tolua_tonumber(tolua_S,7,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,8,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,9,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,10,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,11,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,12,0x0f)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RelLine'", NULL); +#endif + { + self->RelLine(a_RelX1,a_RelY1,a_RelZ1,a_RelX2,a_RelY2,a_RelZ2,a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RelLine'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RotateCCW of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCCW00 +static int tolua_AllToLua_cBlockArea_RotateCCW00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCCW'", NULL); +#endif + { + self->RotateCCW(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RotateCCW'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RotateCW of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCW00 +static int tolua_AllToLua_cBlockArea_RotateCW00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCW'", NULL); +#endif + { + self->RotateCW(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RotateCW'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorXY of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXY00 +static int tolua_AllToLua_cBlockArea_MirrorXY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXY'", NULL); +#endif + { + self->MirrorXY(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorXY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorXZ of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXZ00 +static int tolua_AllToLua_cBlockArea_MirrorXZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXZ'", NULL); +#endif + { + self->MirrorXZ(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorXZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorYZ of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorYZ00 +static int tolua_AllToLua_cBlockArea_MirrorYZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorYZ'", NULL); +#endif + { + self->MirrorYZ(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorYZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RotateCCWNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCCWNoMeta00 +static int tolua_AllToLua_cBlockArea_RotateCCWNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCCWNoMeta'", NULL); +#endif + { + self->RotateCCWNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RotateCCWNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RotateCWNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCWNoMeta00 +static int tolua_AllToLua_cBlockArea_RotateCWNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCWNoMeta'", NULL); +#endif + { + self->RotateCWNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RotateCWNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorXYNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXYNoMeta00 +static int tolua_AllToLua_cBlockArea_MirrorXYNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXYNoMeta'", NULL); +#endif + { + self->MirrorXYNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorXYNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorXZNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXZNoMeta00 +static int tolua_AllToLua_cBlockArea_MirrorXZNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXZNoMeta'", NULL); +#endif + { + self->MirrorXZNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorXZNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorYZNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorYZNoMeta00 +static int tolua_AllToLua_cBlockArea_MirrorYZNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorYZNoMeta'", NULL); +#endif + { + self->MirrorYZNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorYZNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockType of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockType00 +static int tolua_AllToLua_cBlockArea_SetRelBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockType'", NULL); +#endif + { + self->SetRelBlockType(a_RelX,a_RelY,a_RelZ,a_BlockType); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockType of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockType00 +static int tolua_AllToLua_cBlockArea_SetBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockType'", NULL); +#endif + { + self->SetBlockType(a_BlockX,a_BlockY,a_BlockZ,a_BlockType); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockMeta00 +static int tolua_AllToLua_cBlockArea_SetRelBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockMeta'", NULL); +#endif + { + self->SetRelBlockMeta(a_RelX,a_RelY,a_RelZ,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockMeta00 +static int tolua_AllToLua_cBlockArea_SetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); +#endif + { + self->SetBlockMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockLight00 +static int tolua_AllToLua_cBlockArea_SetRelBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockLight'", NULL); +#endif + { + self->SetRelBlockLight(a_RelX,a_RelY,a_RelZ,a_BlockLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockLight00 +static int tolua_AllToLua_cBlockArea_SetBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockLight'", NULL); +#endif + { + self->SetBlockLight(a_BlockX,a_BlockY,a_BlockZ,a_BlockLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockSkyLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00 +static int tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockSkyLight'", NULL); +#endif + { + self->SetRelBlockSkyLight(a_RelX,a_RelY,a_RelZ,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockSkyLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockSkyLight00 +static int tolua_AllToLua_cBlockArea_SetBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockSkyLight'", NULL); +#endif + { + self->SetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockType of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockType00 +static int tolua_AllToLua_cBlockArea_GetRelBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockType'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetRelBlockType(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockType of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockType00 +static int tolua_AllToLua_cBlockArea_GetBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockType(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockMeta00 +static int tolua_AllToLua_cBlockArea_GetRelBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetRelBlockMeta(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockMeta00 +static int tolua_AllToLua_cBlockArea_GetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockLight00 +static int tolua_AllToLua_cBlockArea_GetRelBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetRelBlockLight(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockLight00 +static int tolua_AllToLua_cBlockArea_GetBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockLight(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockSkyLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00 +static int tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockSkyLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetRelBlockSkyLight(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockSkyLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockSkyLight00 +static int tolua_AllToLua_cBlockArea_GetBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockSkyLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockTypeMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockTypeMeta00 +static int tolua_AllToLua_cBlockArea_SetBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockTypeMeta'", NULL); +#endif + { + self->SetBlockTypeMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockTypeMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00 +static int tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockTypeMeta'", NULL); +#endif + { + self->SetRelBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockTypeMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockTypeMeta00 +static int tolua_AllToLua_cBlockArea_GetBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockTypeMeta'", NULL); +#endif + { + self->GetBlockTypeMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockTypeMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00 +static int tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockTypeMeta'", NULL); +#endif + { + self->GetRelBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSizeX of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeX00 +static int tolua_AllToLua_cBlockArea_GetSizeX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeX'", NULL); +#endif + { + int tolua_ret = (int) self->GetSizeX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSizeX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSizeY of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeY00 +static int tolua_AllToLua_cBlockArea_GetSizeY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeY'", NULL); +#endif + { + int tolua_ret = (int) self->GetSizeY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSizeY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSizeZ of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeZ00 +static int tolua_AllToLua_cBlockArea_GetSizeZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetSizeZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSizeZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOriginX of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginX00 +static int tolua_AllToLua_cBlockArea_GetOriginX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginX'", NULL); +#endif + { + int tolua_ret = (int) self->GetOriginX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOriginX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOriginY of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginY00 +static int tolua_AllToLua_cBlockArea_GetOriginY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginY'", NULL); +#endif + { + int tolua_ret = (int) self->GetOriginY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOriginY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOriginZ of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginZ00 +static int tolua_AllToLua_cBlockArea_GetOriginZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetOriginZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOriginZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDataTypes of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetDataTypes00 +static int tolua_AllToLua_cBlockArea_GetDataTypes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDataTypes'", NULL); +#endif + { + int tolua_ret = (int) self->GetDataTypes(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDataTypes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasBlockTypes of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockTypes00 +static int tolua_AllToLua_cBlockArea_HasBlockTypes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockTypes'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasBlockTypes(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasBlockTypes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasBlockMetas of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockMetas00 +static int tolua_AllToLua_cBlockArea_HasBlockMetas00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockMetas'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasBlockMetas(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasBlockMetas'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasBlockLights of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockLights00 +static int tolua_AllToLua_cBlockArea_HasBlockLights00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockLights'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasBlockLights(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasBlockLights'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasBlockSkyLights of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockSkyLights00 +static int tolua_AllToLua_cBlockArea_HasBlockSkyLights00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockSkyLights'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasBlockSkyLights(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasBlockSkyLights'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkX of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetChunkX00 +static int tolua_AllToLua_cChunkDesc_GetChunkX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkZ of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetChunkZ00 +static int tolua_AllToLua_cChunkDesc_GetChunkZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FillBlocks of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillBlocks00 +static int tolua_AllToLua_cChunkDesc_FillBlocks00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,2,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillBlocks'", NULL); +#endif + { + self->FillBlocks(a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FillBlocks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockTypeMeta of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00 +static int tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockTypeMeta'", NULL); +#endif + { + self->SetBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockTypeMeta of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00 +static int tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockTypeMeta'", NULL); +#endif + { + self->GetBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockType of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockType00 +static int tolua_AllToLua_cChunkDesc_SetBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockType'", NULL); +#endif + { + self->SetBlockType(a_RelX,a_RelY,a_RelZ,a_BlockType); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockType of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockType00 +static int tolua_AllToLua_cChunkDesc_GetBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockType(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockMeta of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockMeta00 +static int tolua_AllToLua_cChunkDesc_SetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); +#endif + { + self->SetBlockMeta(a_RelX,a_RelY,a_RelZ,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockMeta of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockMeta00 +static int tolua_AllToLua_cChunkDesc_GetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBiome of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBiome00 +static int tolua_AllToLua_cChunkDesc_SetBiome00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BiomeID = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBiome'", NULL); +#endif + { + self->SetBiome(a_RelX,a_RelZ,a_BiomeID); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBiome'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBiome of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBiome00 +static int tolua_AllToLua_cChunkDesc_GetBiome00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBiome'", NULL); +#endif + { + EMCSBiome tolua_ret = (EMCSBiome) self->GetBiome(a_RelX,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBiome'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetHeight00 +static int tolua_AllToLua_cChunkDesc_SetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Height = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeight'", NULL); +#endif + { + self->SetHeight(a_RelX,a_RelZ,a_Height); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetHeight00 +static int tolua_AllToLua_cChunkDesc_GetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetHeight(a_RelX,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultBiomes of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultBiomes = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultBiomes'", NULL); +#endif + { + self->SetUseDefaultBiomes(a_bUseDefaultBiomes); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultBiomes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultBiomes of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultBiomes'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultBiomes(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultBiomes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultHeight = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultHeight'", NULL); +#endif + { + self->SetUseDefaultHeight(a_bUseDefaultHeight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultHeight'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultHeight(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultComposition of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultComposition = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultComposition'", NULL); +#endif + { + self->SetUseDefaultComposition(a_bUseDefaultComposition); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultComposition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultComposition of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultComposition'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultComposition(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultComposition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultStructures of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultStructures = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultStructures'", NULL); +#endif + { + self->SetUseDefaultStructures(a_bUseDefaultStructures); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultStructures'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultStructures of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultStructures'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultStructures(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultStructures'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultFinish of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultFinish = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultFinish'", NULL); +#endif + { + self->SetUseDefaultFinish(a_bUseDefaultFinish); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultFinish'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultFinish of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultFinish'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultFinish(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultFinish'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: WriteBlockArea of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_WriteBlockArea00 +static int tolua_AllToLua_cChunkDesc_WriteBlockArea00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,1,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cBlockArea* a_BlockArea = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0)); + int a_RelX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,5,0)); + cBlockArea::eMergeStrategy a_MergeStrategy = ((cBlockArea::eMergeStrategy) (int) tolua_tonumber(tolua_S,6,cBlockArea::msOverwrite)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteBlockArea'", NULL); +#endif + { + self->WriteBlockArea(*a_BlockArea,a_RelX,a_RelY,a_RelZ,a_MergeStrategy); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'WriteBlockArea'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReadBlockArea of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReadBlockArea00 +static int tolua_AllToLua_cChunkDesc_ReadBlockArea00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBlockArea",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnoobj(tolua_S,9,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + cBlockArea* a_Dest = ((cBlockArea*) tolua_tousertype(tolua_S,2,0)); + int a_MinRelX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MaxRelX = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinRelY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MaxRelY = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MinRelZ = ((int) tolua_tonumber(tolua_S,7,0)); + int a_MaxRelZ = ((int) tolua_tonumber(tolua_S,8,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadBlockArea'", NULL); +#endif + { + self->ReadBlockArea(*a_Dest,a_MinRelX,a_MaxRelX,a_MinRelY,a_MaxRelY,a_MinRelZ,a_MaxRelZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReadBlockArea'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetMaxHeight00 +static int tolua_AllToLua_cChunkDesc_GetMaxHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxHeight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetMaxHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FillRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillRelCuboid00 +static int tolua_AllToLua_cChunkDesc_FillRelCuboid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL); +#endif + { + self->FillRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FillRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FillRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillRelCuboid01 +static int tolua_AllToLua_cChunkDesc_FillRelCuboid01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL); +#endif + { + self->FillRelCuboid(*a_RelCuboid,a_BlockType,a_BlockMeta); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cChunkDesc_FillRelCuboid00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReplaceRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00 +static int tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnumber(tolua_S,10,0,&tolua_err) || + !tolua_isnumber(tolua_S,11,0,&tolua_err) || + !tolua_isnoobj(tolua_S,12,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); + unsigned char a_SrcType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); + unsigned char a_SrcMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); + unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,10,0)); + unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,11,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReplaceRelCuboid'", NULL); +#endif + { + self->ReplaceRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_SrcType,a_SrcMeta,a_DstType,a_DstMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReplaceRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReplaceRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01 +static int tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_SrcType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_SrcMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); + unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReplaceRelCuboid'", NULL); +#endif + { + self->ReplaceRelCuboid(*a_RelCuboid,a_SrcType,a_SrcMeta,a_DstType,a_DstMeta); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FloorRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FloorRelCuboid00 +static int tolua_AllToLua_cChunkDesc_FloorRelCuboid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); + unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); + unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FloorRelCuboid'", NULL); +#endif + { + self->FloorRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_DstType,a_DstMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FloorRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FloorRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FloorRelCuboid01 +static int tolua_AllToLua_cChunkDesc_FloorRelCuboid01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FloorRelCuboid'", NULL); +#endif + { + self->FloorRelCuboid(*a_RelCuboid,a_DstType,a_DstMeta); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cChunkDesc_FloorRelCuboid00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RandomFillRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00 +static int tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnumber(tolua_S,10,0,&tolua_err) || + !tolua_isnumber(tolua_S,11,0,&tolua_err) || + !tolua_isnoobj(tolua_S,12,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); + int a_RandomSeed = ((int) tolua_tonumber(tolua_S,10,0)); + int a_ChanceOutOf10k = ((int) tolua_tonumber(tolua_S,11,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RandomFillRelCuboid'", NULL); +#endif + { + self->RandomFillRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_BlockType,a_BlockMeta,a_RandomSeed,a_ChanceOutOf10k); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RandomFillRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RandomFillRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01 +static int tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); + int a_RandomSeed = ((int) tolua_tonumber(tolua_S,5,0)); + int a_ChanceOutOf10k = ((int) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RandomFillRelCuboid'", NULL); +#endif + { + self->RandomFillRelCuboid(*a_RelCuboid,a_BlockType,a_BlockMeta,a_RandomSeed,a_ChanceOutOf10k); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockEntity of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockEntity00 +static int tolua_AllToLua_cChunkDesc_GetBlockEntity00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockEntity'", NULL); +#endif + { + cBlockEntity* tolua_ret = (cBlockEntity*) self->GetBlockEntity(a_RelX,a_RelY,a_RelZ); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockEntity"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockEntity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_new00 +static int tolua_AllToLua_cCraftingGrid_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_Width = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Height = ((int) tolua_tonumber(tolua_S,3,0)); + { + cCraftingGrid* tolua_ret = (cCraftingGrid*) Mtolua_new((cCraftingGrid)(a_Width,a_Height)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_new00_local +static int tolua_AllToLua_cCraftingGrid_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_Width = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Height = ((int) tolua_tonumber(tolua_S,3,0)); + { + cCraftingGrid* tolua_ret = (cCraftingGrid*) Mtolua_new((cCraftingGrid)(a_Width,a_Height)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingGrid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWidth of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetWidth00 +static int tolua_AllToLua_cCraftingGrid_GetWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL); +#endif + { + int tolua_ret = (int) self->GetWidth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetHeight00 +static int tolua_AllToLua_cCraftingGrid_GetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetItem of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetItem00 +static int tolua_AllToLua_cCraftingGrid_GetItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetItem'", NULL); +#endif + { + cItem& tolua_ret = (cItem&) self->GetItem(x,y); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetItem of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_SetItem00 +static int tolua_AllToLua_cCraftingGrid_SetItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,4,0)); + int a_ItemCount = ((int) tolua_tonumber(tolua_S,5,0)); + short a_ItemHealth = ((short) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetItem'", NULL); +#endif + { + self->SetItem(x,y,a_ItemType,a_ItemCount,a_ItemHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetItem of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_SetItem01 +static int tolua_AllToLua_cCraftingGrid_SetItem01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetItem'", NULL); +#endif + { + self->SetItem(x,y,*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cCraftingGrid_SetItem00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_Clear00 +static int tolua_AllToLua_cCraftingGrid_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ConsumeGrid of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_ConsumeGrid00 +static int tolua_AllToLua_cCraftingGrid_ConsumeGrid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCraftingGrid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + const cCraftingGrid* a_Grid = ((const cCraftingGrid*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ConsumeGrid'", NULL); +#endif + { + self->ConsumeGrid(*a_Grid); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ConsumeGrid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Dump of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_Dump00 +static int tolua_AllToLua_cCraftingGrid_Dump00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dump'", NULL); +#endif + { + self->Dump(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Dump'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_Clear00 +static int tolua_AllToLua_cCraftingRecipe_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIngredientsWidth of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00 +static int tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredientsWidth'", NULL); +#endif + { + int tolua_ret = (int) self->GetIngredientsWidth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIngredientsWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIngredientsHeight of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00 +static int tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredientsHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetIngredientsHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIngredientsHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIngredient of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredient00 +static int tolua_AllToLua_cCraftingRecipe_GetIngredient00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredient'", NULL); +#endif + { + cItem& tolua_ret = (cItem&) self->GetIngredient(x,y); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIngredient'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetResult of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetResult00 +static int tolua_AllToLua_cCraftingRecipe_GetResult00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetResult'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetResult(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetResult'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetResult of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetResult00 +static int tolua_AllToLua_cCraftingRecipe_SetResult00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,2,0)); + int a_ItemCount = ((int) tolua_tonumber(tolua_S,3,0)); + short a_ItemHealth = ((short) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetResult'", NULL); +#endif + { + self->SetResult(a_ItemType,a_ItemCount,a_ItemHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetResult'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetResult of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetResult01 +static int tolua_AllToLua_cCraftingRecipe_SetResult01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetResult'", NULL); +#endif + { + self->SetResult(*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cCraftingRecipe_SetResult00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetIngredient of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetIngredient00 +static int tolua_AllToLua_cCraftingRecipe_SetIngredient00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,4,0)); + int a_ItemCount = ((int) tolua_tonumber(tolua_S,5,0)); + short a_ItemHealth = ((short) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIngredient'", NULL); +#endif + { + self->SetIngredient(x,y,a_ItemType,a_ItemCount,a_ItemHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetIngredient'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetIngredient of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetIngredient01 +static int tolua_AllToLua_cCraftingRecipe_SetIngredient01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIngredient'", NULL); +#endif + { + self->SetIngredient(x,y,*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cCraftingRecipe_SetIngredient00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ConsumeIngredients of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00 +static int tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cCraftingGrid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + cCraftingGrid* a_CraftingGrid = ((cCraftingGrid*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ConsumeIngredients'", NULL); +#endif + { + self->ConsumeIngredients(*a_CraftingGrid); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ConsumeIngredients'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Dump of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_Dump00 +static int tolua_AllToLua_cCraftingRecipe_Dump00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dump'", NULL); +#endif + { + self->Dump(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Dump'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWindowID of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowID00 +static int tolua_AllToLua_cWindow_GetWindowID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowID'", NULL); +#endif + { + char tolua_ret = (char) self->GetWindowID(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWindowID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWindowType of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowType00 +static int tolua_AllToLua_cWindow_GetWindowType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowType'", NULL); +#endif + { + int tolua_ret = (int) self->GetWindowType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWindowType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetSlot00 +static int tolua_AllToLua_cWindow_GetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem* tolua_ret = (const cItem*) self->GetSlot(*a_Player,a_SlotNum); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetSlot00 +static int tolua_AllToLua_cWindow_SetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(*a_Player,a_SlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotInPlayerMainInventory of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00 +static int tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerMainInventory'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotInPlayerMainInventory(a_SlotNum); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerMainInventory'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotInPlayerHotbar of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00 +static int tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerHotbar'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotInPlayerHotbar(a_SlotNum); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerHotbar'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotInPlayerInventory of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerInventory00 +static int tolua_AllToLua_cWindow_IsSlotInPlayerInventory00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerInventory'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotInPlayerInventory(a_SlotNum); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerInventory'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWindowTitle of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowTitle00 +static int tolua_AllToLua_cWindow_GetWindowTitle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowTitle'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetWindowTitle(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWindowTitle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetWindowTitle of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetWindowTitle00 +static int tolua_AllToLua_cWindow_SetWindowTitle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); + const AString a_WindowTitle = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWindowTitle'", NULL); +#endif + { + self->SetWindowTitle(a_WindowTitle); + tolua_pushcppstring(tolua_S,(const char*)a_WindowTitle); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetWindowTitle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetProperty of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetProperty00 +static int tolua_AllToLua_cWindow_SetProperty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); + int a_Property = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Value = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetProperty'", NULL); +#endif + { + self->SetProperty(a_Property,a_Value); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetProperty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetProperty of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetProperty01 +static int tolua_AllToLua_cWindow_SetProperty01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"cPlayer",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); + int a_Property = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Value = ((int) tolua_tonumber(tolua_S,3,0)); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetProperty'", NULL); +#endif + { + self->SetProperty(a_Property,a_Value,*a_Player); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cWindow_SetProperty00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cLuaWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_new00 +static int tolua_AllToLua_cLuaWindow_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cLuaWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWindow::WindowType a_WindowType = ((cWindow::WindowType) (int) tolua_tonumber(tolua_S,2,0)); + int a_SlotsX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SlotsY = ((int) tolua_tonumber(tolua_S,4,0)); + const AString a_Title = ((const AString) tolua_tocppstring(tolua_S,5,0)); + { + cLuaWindow* tolua_ret = (cLuaWindow*) Mtolua_new((cLuaWindow)(a_WindowType,a_SlotsX,a_SlotsY,a_Title)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cLuaWindow"); + tolua_pushcppstring(tolua_S,(const char*)a_Title); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cLuaWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_new00_local +static int tolua_AllToLua_cLuaWindow_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cLuaWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWindow::WindowType a_WindowType = ((cWindow::WindowType) (int) tolua_tonumber(tolua_S,2,0)); + int a_SlotsX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SlotsY = ((int) tolua_tonumber(tolua_S,4,0)); + const AString a_Title = ((const AString) tolua_tocppstring(tolua_S,5,0)); + { + cLuaWindow* tolua_ret = (cLuaWindow*) Mtolua_new((cLuaWindow)(a_WindowType,a_SlotsX,a_SlotsY,a_Title)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cLuaWindow"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + tolua_pushcppstring(tolua_S,(const char*)a_Title); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class cLuaWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_delete00 +static int tolua_AllToLua_cLuaWindow_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cLuaWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL); +#endif + Mtolua_delete(self); + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetContents of class cLuaWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_GetContents00 +static int tolua_AllToLua_cLuaWindow_GetContents00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cLuaWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetContents'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetContents(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetContents'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMobType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobType00 +static int tolua_AllToLua_cMonster_GetMobType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobType'", NULL); +#endif + { + cMonster::eType tolua_ret = (cMonster::eType) self->GetMobType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMobType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMobFamily of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobFamily00 +static int tolua_AllToLua_cMonster_GetMobFamily00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobFamily'", NULL); +#endif + { + cMonster::eFamily tolua_ret = (cMonster::eFamily) self->GetMobFamily(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMobFamily'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MobTypeToString of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_MobTypeToString00 +static int tolua_AllToLua_cMonster_MobTypeToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0)); + { + AString tolua_ret = (AString) cMonster::MobTypeToString(a_MobType); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MobTypeToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: StringToMobType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_StringToMobType00 +static int tolua_AllToLua_cMonster_StringToMobType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_MobTypeName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + cMonster::eType tolua_ret = (cMonster::eType) cMonster::StringToMobType(a_MobTypeName); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_MobTypeName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FamilyFromType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_FamilyFromType00 +static int tolua_AllToLua_cMonster_FamilyFromType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0)); + { + cMonster::eFamily tolua_ret = (cMonster::eFamily) cMonster::FamilyFromType(a_MobType); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FamilyFromType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpawnDelay of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetSpawnDelay00 +static int tolua_AllToLua_cMonster_GetSpawnDelay00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster::eFamily a_MobFamily = ((cMonster::eFamily) (int) tolua_tonumber(tolua_S,2,0)); + { + int tolua_ret = (int) cMonster::GetSpawnDelay(a_MobFamily); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpawnDelay'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* Open function */ +TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) +{ + tolua_open(tolua_S); + tolua_reg_types(tolua_S); + tolua_module(tolua_S,NULL,1); + tolua_beginmodule(tolua_S,NULL); + tolua_constant(tolua_S,"biOcean",biOcean); + tolua_constant(tolua_S,"biPlains",biPlains); + tolua_constant(tolua_S,"biDesert",biDesert); + tolua_constant(tolua_S,"biExtremeHills",biExtremeHills); + tolua_constant(tolua_S,"biForest",biForest); + tolua_constant(tolua_S,"biTaiga",biTaiga); + tolua_constant(tolua_S,"biSwampland",biSwampland); + tolua_constant(tolua_S,"biRiver",biRiver); + tolua_constant(tolua_S,"biHell",biHell); + tolua_constant(tolua_S,"biNether",biNether); + tolua_constant(tolua_S,"biSky",biSky); + tolua_constant(tolua_S,"biEnd",biEnd); + tolua_constant(tolua_S,"biFrozenOcean",biFrozenOcean); + tolua_constant(tolua_S,"biFrozenRiver",biFrozenRiver); + tolua_constant(tolua_S,"biIcePlains",biIcePlains); + tolua_constant(tolua_S,"biTundra",biTundra); + tolua_constant(tolua_S,"biIceMountains",biIceMountains); + tolua_constant(tolua_S,"biMushroomIsland",biMushroomIsland); + tolua_constant(tolua_S,"biMushroomShore",biMushroomShore); + tolua_constant(tolua_S,"biBeach",biBeach); + tolua_constant(tolua_S,"biDesertHills",biDesertHills); + tolua_constant(tolua_S,"biForestHills",biForestHills); + tolua_constant(tolua_S,"biTaigaHills",biTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsEdge",biExtremeHillsEdge); + tolua_constant(tolua_S,"biJungle",biJungle); + tolua_constant(tolua_S,"biJungleHills",biJungleHills); + tolua_constant(tolua_S,"biJungleEdge",biJungleEdge); + tolua_constant(tolua_S,"biDeepOcean",biDeepOcean); + tolua_constant(tolua_S,"biStoneBeach",biStoneBeach); + tolua_constant(tolua_S,"biColdBeach",biColdBeach); + tolua_constant(tolua_S,"biBirchForest",biBirchForest); + tolua_constant(tolua_S,"biBirchForestHills",biBirchForestHills); + tolua_constant(tolua_S,"biRoofedForest",biRoofedForest); + tolua_constant(tolua_S,"biColdTaiga",biColdTaiga); + tolua_constant(tolua_S,"biColdTaigaHills",biColdTaigaHills); + tolua_constant(tolua_S,"biMegaTaiga",biMegaTaiga); + tolua_constant(tolua_S,"biMegaTaigaHills",biMegaTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsPlus",biExtremeHillsPlus); + tolua_constant(tolua_S,"biSavanna",biSavanna); + tolua_constant(tolua_S,"biSavannaPlateau",biSavannaPlateau); + tolua_constant(tolua_S,"biMesa",biMesa); + tolua_constant(tolua_S,"biMesaPlateauF",biMesaPlateauF); + tolua_constant(tolua_S,"biMesaPlateau",biMesaPlateau); + tolua_constant(tolua_S,"biNumBiomes",biNumBiomes); + tolua_constant(tolua_S,"biMaxBiome",biMaxBiome); + tolua_constant(tolua_S,"biVariant",biVariant); + tolua_constant(tolua_S,"biSunflowerPlains",biSunflowerPlains); + tolua_constant(tolua_S,"biDesertM",biDesertM); + tolua_constant(tolua_S,"biExtremeHillsM",biExtremeHillsM); + tolua_constant(tolua_S,"biFlowerForest",biFlowerForest); + tolua_constant(tolua_S,"biTaigaM",biTaigaM); + tolua_constant(tolua_S,"biSwamplandM",biSwamplandM); + tolua_constant(tolua_S,"biIcePlainsSpikes",biIcePlainsSpikes); + tolua_constant(tolua_S,"biJungleM",biJungleM); + tolua_constant(tolua_S,"biJungleEdgeM",biJungleEdgeM); + tolua_constant(tolua_S,"biBirchForestM",biBirchForestM); + tolua_constant(tolua_S,"biBirchForestHillsM",biBirchForestHillsM); + tolua_constant(tolua_S,"biRoofedForestM",biRoofedForestM); + tolua_constant(tolua_S,"biColdTaigaM",biColdTaigaM); + tolua_constant(tolua_S,"biMegaSpruceTaiga",biMegaSpruceTaiga); + tolua_constant(tolua_S,"biMegaSpruceTaigaHills",biMegaSpruceTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsPlusM",biExtremeHillsPlusM); + tolua_constant(tolua_S,"biSavannaM",biSavannaM); + tolua_constant(tolua_S,"biSavannaPlateauM",biSavannaPlateauM); + tolua_constant(tolua_S,"biMesaBryce",biMesaBryce); + tolua_constant(tolua_S,"biMesaPlateauFM",biMesaPlateauFM); + tolua_constant(tolua_S,"biMesaPlateauM",biMesaPlateauM); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cIniFile","cIniFile","",tolua_collect_cIniFile); + #else + tolua_cclass(tolua_S,"cIniFile","cIniFile","",NULL); + #endif + tolua_beginmodule(tolua_S,"cIniFile"); + tolua_constant(tolua_S,"noID",cIniFile::noID); + tolua_function(tolua_S,"new",tolua_AllToLua_cIniFile_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cIniFile_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cIniFile_new00_local); + tolua_function(tolua_S,"CaseSensitive",tolua_AllToLua_cIniFile_CaseSensitive00); + tolua_function(tolua_S,"CaseInsensitive",tolua_AllToLua_cIniFile_CaseInsensitive00); + tolua_function(tolua_S,"ReadFile",tolua_AllToLua_cIniFile_ReadFile00); + tolua_function(tolua_S,"WriteFile",tolua_AllToLua_cIniFile_WriteFile00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cIniFile_Clear00); + tolua_function(tolua_S,"FindKey",tolua_AllToLua_cIniFile_FindKey00); + tolua_function(tolua_S,"FindValue",tolua_AllToLua_cIniFile_FindValue00); + tolua_function(tolua_S,"GetNumKeys",tolua_AllToLua_cIniFile_GetNumKeys00); + tolua_function(tolua_S,"AddKeyName",tolua_AllToLua_cIniFile_AddKeyName00); + tolua_function(tolua_S,"GetKeyName",tolua_AllToLua_cIniFile_GetKeyName00); + tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues00); + tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues01); + tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName00); + tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName01); + tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue00); + tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue01); + tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue02); + tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue03); + tolua_function(tolua_S,"GetValueF",tolua_AllToLua_cIniFile_GetValueF00); + tolua_function(tolua_S,"GetValueI",tolua_AllToLua_cIniFile_GetValueI00); + tolua_function(tolua_S,"GetValueB",tolua_AllToLua_cIniFile_GetValueB00); + tolua_function(tolua_S,"GetValueSet",tolua_AllToLua_cIniFile_GetValueSet00); + tolua_function(tolua_S,"GetValueSet",tolua_AllToLua_cIniFile_GetValueSet01); + tolua_function(tolua_S,"GetValueSetF",tolua_AllToLua_cIniFile_GetValueSetF00); + tolua_function(tolua_S,"GetValueSetI",tolua_AllToLua_cIniFile_GetValueSetI00); + tolua_function(tolua_S,"GetValueSetB",tolua_AllToLua_cIniFile_GetValueSetB00); + tolua_function(tolua_S,"SetValue",tolua_AllToLua_cIniFile_SetValue00); + tolua_function(tolua_S,"SetValue",tolua_AllToLua_cIniFile_SetValue01); + tolua_function(tolua_S,"SetValueI",tolua_AllToLua_cIniFile_SetValueI00); + tolua_function(tolua_S,"SetValueB",tolua_AllToLua_cIniFile_SetValueB00); + tolua_function(tolua_S,"SetValueF",tolua_AllToLua_cIniFile_SetValueF00); + tolua_function(tolua_S,"DeleteValueByID",tolua_AllToLua_cIniFile_DeleteValueByID00); + tolua_function(tolua_S,"DeleteValue",tolua_AllToLua_cIniFile_DeleteValue00); + tolua_function(tolua_S,"DeleteKey",tolua_AllToLua_cIniFile_DeleteKey00); + tolua_function(tolua_S,"GetNumHeaderComments",tolua_AllToLua_cIniFile_GetNumHeaderComments00); + tolua_function(tolua_S,"AddHeaderComment",tolua_AllToLua_cIniFile_AddHeaderComment00); + tolua_function(tolua_S,"GetHeaderComment",tolua_AllToLua_cIniFile_GetHeaderComment00); + tolua_function(tolua_S,"DeleteHeaderComment",tolua_AllToLua_cIniFile_DeleteHeaderComment00); + tolua_function(tolua_S,"DeleteHeaderComments",tolua_AllToLua_cIniFile_DeleteHeaderComments00); + tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments00); + tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments01); + tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment00); + tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment01); + tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment00); + tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment01); + tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment00); + tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment01); + tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments00); + tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments01); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cFile","cFile","",NULL); + tolua_beginmodule(tolua_S,"cFile"); + tolua_function(tolua_S,"Exists",tolua_AllToLua_cFile_Exists00); + tolua_function(tolua_S,"Delete",tolua_AllToLua_cFile_Delete00); + tolua_function(tolua_S,"Rename",tolua_AllToLua_cFile_Rename00); + tolua_function(tolua_S,"Copy",tolua_AllToLua_cFile_Copy00); + tolua_function(tolua_S,"IsFolder",tolua_AllToLua_cFile_IsFolder00); + tolua_function(tolua_S,"IsFile",tolua_AllToLua_cFile_IsFile00); + tolua_function(tolua_S,"GetSize",tolua_AllToLua_cFile_GetSize00); + tolua_function(tolua_S,"CreateFolder",tolua_AllToLua_cFile_CreateFolder00); + tolua_endmodule(tolua_S); + tolua_constant(tolua_S,"E_BLOCK_AIR",E_BLOCK_AIR); + tolua_constant(tolua_S,"E_BLOCK_STONE",E_BLOCK_STONE); + tolua_constant(tolua_S,"E_BLOCK_GRASS",E_BLOCK_GRASS); + tolua_constant(tolua_S,"E_BLOCK_DIRT",E_BLOCK_DIRT); + tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE",E_BLOCK_COBBLESTONE); + tolua_constant(tolua_S,"E_BLOCK_PLANKS",E_BLOCK_PLANKS); + tolua_constant(tolua_S,"E_BLOCK_SAPLING",E_BLOCK_SAPLING); + tolua_constant(tolua_S,"E_BLOCK_BEDROCK",E_BLOCK_BEDROCK); + tolua_constant(tolua_S,"E_BLOCK_WATER",E_BLOCK_WATER); + tolua_constant(tolua_S,"E_BLOCK_STATIONARY_WATER",E_BLOCK_STATIONARY_WATER); + tolua_constant(tolua_S,"E_BLOCK_LAVA",E_BLOCK_LAVA); + tolua_constant(tolua_S,"E_BLOCK_STATIONARY_LAVA",E_BLOCK_STATIONARY_LAVA); + tolua_constant(tolua_S,"E_BLOCK_SAND",E_BLOCK_SAND); + tolua_constant(tolua_S,"E_BLOCK_GRAVEL",E_BLOCK_GRAVEL); + tolua_constant(tolua_S,"E_BLOCK_GOLD_ORE",E_BLOCK_GOLD_ORE); + tolua_constant(tolua_S,"E_BLOCK_IRON_ORE",E_BLOCK_IRON_ORE); + tolua_constant(tolua_S,"E_BLOCK_COAL_ORE",E_BLOCK_COAL_ORE); + tolua_constant(tolua_S,"E_BLOCK_LOG",E_BLOCK_LOG); + tolua_constant(tolua_S,"E_BLOCK_LEAVES",E_BLOCK_LEAVES); + tolua_constant(tolua_S,"E_BLOCK_SPONGE",E_BLOCK_SPONGE); + tolua_constant(tolua_S,"E_BLOCK_GLASS",E_BLOCK_GLASS); + tolua_constant(tolua_S,"E_BLOCK_LAPIS_ORE",E_BLOCK_LAPIS_ORE); + tolua_constant(tolua_S,"E_BLOCK_LAPIS_BLOCK",E_BLOCK_LAPIS_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_DISPENSER",E_BLOCK_DISPENSER); + tolua_constant(tolua_S,"E_BLOCK_SANDSTONE",E_BLOCK_SANDSTONE); + tolua_constant(tolua_S,"E_BLOCK_NOTE_BLOCK",E_BLOCK_NOTE_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_BED",E_BLOCK_BED); + tolua_constant(tolua_S,"E_BLOCK_POWERED_RAIL",E_BLOCK_POWERED_RAIL); + tolua_constant(tolua_S,"E_BLOCK_DETECTOR_RAIL",E_BLOCK_DETECTOR_RAIL); + tolua_constant(tolua_S,"E_BLOCK_STICKY_PISTON",E_BLOCK_STICKY_PISTON); + tolua_constant(tolua_S,"E_BLOCK_COBWEB",E_BLOCK_COBWEB); + tolua_constant(tolua_S,"E_BLOCK_TALL_GRASS",E_BLOCK_TALL_GRASS); + tolua_constant(tolua_S,"E_BLOCK_DEAD_BUSH",E_BLOCK_DEAD_BUSH); + tolua_constant(tolua_S,"E_BLOCK_PISTON",E_BLOCK_PISTON); + tolua_constant(tolua_S,"E_BLOCK_PISTON_EXTENSION",E_BLOCK_PISTON_EXTENSION); + tolua_constant(tolua_S,"E_BLOCK_WOOL",E_BLOCK_WOOL); + tolua_constant(tolua_S,"E_BLOCK_PISTON_MOVED_BLOCK",E_BLOCK_PISTON_MOVED_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_DANDELION",E_BLOCK_DANDELION); + tolua_constant(tolua_S,"E_BLOCK_FLOWER",E_BLOCK_FLOWER); + tolua_constant(tolua_S,"E_BLOCK_BROWN_MUSHROOM",E_BLOCK_BROWN_MUSHROOM); + tolua_constant(tolua_S,"E_BLOCK_RED_MUSHROOM",E_BLOCK_RED_MUSHROOM); + tolua_constant(tolua_S,"E_BLOCK_GOLD_BLOCK",E_BLOCK_GOLD_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_IRON_BLOCK",E_BLOCK_IRON_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_DOUBLE_STONE_SLAB",E_BLOCK_DOUBLE_STONE_SLAB); + tolua_constant(tolua_S,"E_BLOCK_STONE_SLAB",E_BLOCK_STONE_SLAB); + tolua_constant(tolua_S,"E_BLOCK_BRICK",E_BLOCK_BRICK); + tolua_constant(tolua_S,"E_BLOCK_TNT",E_BLOCK_TNT); + tolua_constant(tolua_S,"E_BLOCK_BOOKCASE",E_BLOCK_BOOKCASE); + tolua_constant(tolua_S,"E_BLOCK_MOSSY_COBBLESTONE",E_BLOCK_MOSSY_COBBLESTONE); + tolua_constant(tolua_S,"E_BLOCK_OBSIDIAN",E_BLOCK_OBSIDIAN); + tolua_constant(tolua_S,"E_BLOCK_TORCH",E_BLOCK_TORCH); + tolua_constant(tolua_S,"E_BLOCK_FIRE",E_BLOCK_FIRE); + tolua_constant(tolua_S,"E_BLOCK_MOB_SPAWNER",E_BLOCK_MOB_SPAWNER); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_STAIRS",E_BLOCK_WOODEN_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_CHEST",E_BLOCK_CHEST); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_WIRE",E_BLOCK_REDSTONE_WIRE); + tolua_constant(tolua_S,"E_BLOCK_DIAMOND_ORE",E_BLOCK_DIAMOND_ORE); + tolua_constant(tolua_S,"E_BLOCK_DIAMOND_BLOCK",E_BLOCK_DIAMOND_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_CRAFTING_TABLE",E_BLOCK_CRAFTING_TABLE); + tolua_constant(tolua_S,"E_BLOCK_WORKBENCH",E_BLOCK_WORKBENCH); + tolua_constant(tolua_S,"E_BLOCK_CROPS",E_BLOCK_CROPS); + tolua_constant(tolua_S,"E_BLOCK_FARMLAND",E_BLOCK_FARMLAND); + tolua_constant(tolua_S,"E_BLOCK_FURNACE",E_BLOCK_FURNACE); + tolua_constant(tolua_S,"E_BLOCK_LIT_FURNACE",E_BLOCK_LIT_FURNACE); + tolua_constant(tolua_S,"E_BLOCK_BURNING_FURNACE",E_BLOCK_BURNING_FURNACE); + tolua_constant(tolua_S,"E_BLOCK_SIGN_POST",E_BLOCK_SIGN_POST); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_DOOR",E_BLOCK_WOODEN_DOOR); + tolua_constant(tolua_S,"E_BLOCK_LADDER",E_BLOCK_LADDER); + tolua_constant(tolua_S,"E_BLOCK_RAIL",E_BLOCK_RAIL); + tolua_constant(tolua_S,"E_BLOCK_MINECART_TRACKS",E_BLOCK_MINECART_TRACKS); + tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE_STAIRS",E_BLOCK_COBBLESTONE_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_WALLSIGN",E_BLOCK_WALLSIGN); + tolua_constant(tolua_S,"E_BLOCK_LEVER",E_BLOCK_LEVER); + tolua_constant(tolua_S,"E_BLOCK_STONE_PRESSURE_PLATE",E_BLOCK_STONE_PRESSURE_PLATE); + tolua_constant(tolua_S,"E_BLOCK_IRON_DOOR",E_BLOCK_IRON_DOOR); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_PRESSURE_PLATE",E_BLOCK_WOODEN_PRESSURE_PLATE); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_ORE",E_BLOCK_REDSTONE_ORE); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_ORE_GLOWING",E_BLOCK_REDSTONE_ORE_GLOWING); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_TORCH_OFF",E_BLOCK_REDSTONE_TORCH_OFF); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_TORCH_ON",E_BLOCK_REDSTONE_TORCH_ON); + tolua_constant(tolua_S,"E_BLOCK_STONE_BUTTON",E_BLOCK_STONE_BUTTON); + tolua_constant(tolua_S,"E_BLOCK_SNOW",E_BLOCK_SNOW); + tolua_constant(tolua_S,"E_BLOCK_ICE",E_BLOCK_ICE); + tolua_constant(tolua_S,"E_BLOCK_SNOW_BLOCK",E_BLOCK_SNOW_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_CACTUS",E_BLOCK_CACTUS); + tolua_constant(tolua_S,"E_BLOCK_CLAY",E_BLOCK_CLAY); + tolua_constant(tolua_S,"E_BLOCK_SUGARCANE",E_BLOCK_SUGARCANE); + tolua_constant(tolua_S,"E_BLOCK_REEDS",E_BLOCK_REEDS); + tolua_constant(tolua_S,"E_BLOCK_JUKEBOX",E_BLOCK_JUKEBOX); + tolua_constant(tolua_S,"E_BLOCK_FENCE",E_BLOCK_FENCE); + tolua_constant(tolua_S,"E_BLOCK_PUMPKIN",E_BLOCK_PUMPKIN); + tolua_constant(tolua_S,"E_BLOCK_NETHERRACK",E_BLOCK_NETHERRACK); + tolua_constant(tolua_S,"E_BLOCK_SOULSAND",E_BLOCK_SOULSAND); + tolua_constant(tolua_S,"E_BLOCK_GLOWSTONE",E_BLOCK_GLOWSTONE); + tolua_constant(tolua_S,"E_BLOCK_NETHER_PORTAL",E_BLOCK_NETHER_PORTAL); + tolua_constant(tolua_S,"E_BLOCK_JACK_O_LANTERN",E_BLOCK_JACK_O_LANTERN); + tolua_constant(tolua_S,"E_BLOCK_CAKE",E_BLOCK_CAKE); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_REPEATER_OFF",E_BLOCK_REDSTONE_REPEATER_OFF); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_REPEATER_ON",E_BLOCK_REDSTONE_REPEATER_ON); + tolua_constant(tolua_S,"E_BLOCK_STAINED_GLASS",E_BLOCK_STAINED_GLASS); + tolua_constant(tolua_S,"E_BLOCK_TRAPDOOR",E_BLOCK_TRAPDOOR); + tolua_constant(tolua_S,"E_BLOCK_SILVERFISH_EGG",E_BLOCK_SILVERFISH_EGG); + tolua_constant(tolua_S,"E_BLOCK_STONE_BRICKS",E_BLOCK_STONE_BRICKS); + tolua_constant(tolua_S,"E_BLOCK_HUGE_BROWN_MUSHROOM",E_BLOCK_HUGE_BROWN_MUSHROOM); + tolua_constant(tolua_S,"E_BLOCK_HUGE_RED_MUSHROOM",E_BLOCK_HUGE_RED_MUSHROOM); + tolua_constant(tolua_S,"E_BLOCK_IRON_BARS",E_BLOCK_IRON_BARS); + tolua_constant(tolua_S,"E_BLOCK_GLASS_PANE",E_BLOCK_GLASS_PANE); + tolua_constant(tolua_S,"E_BLOCK_MELON",E_BLOCK_MELON); + tolua_constant(tolua_S,"E_BLOCK_PUMPKIN_STEM",E_BLOCK_PUMPKIN_STEM); + tolua_constant(tolua_S,"E_BLOCK_MELON_STEM",E_BLOCK_MELON_STEM); + tolua_constant(tolua_S,"E_BLOCK_VINES",E_BLOCK_VINES); + tolua_constant(tolua_S,"E_BLOCK_FENCE_GATE",E_BLOCK_FENCE_GATE); + tolua_constant(tolua_S,"E_BLOCK_BRICK_STAIRS",E_BLOCK_BRICK_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_STONE_BRICK_STAIRS",E_BLOCK_STONE_BRICK_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_MYCELIUM",E_BLOCK_MYCELIUM); + tolua_constant(tolua_S,"E_BLOCK_LILY_PAD",E_BLOCK_LILY_PAD); + tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK",E_BLOCK_NETHER_BRICK); + tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK_FENCE",E_BLOCK_NETHER_BRICK_FENCE); + tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK_STAIRS",E_BLOCK_NETHER_BRICK_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_NETHER_WART",E_BLOCK_NETHER_WART); + tolua_constant(tolua_S,"E_BLOCK_ENCHANTMENT_TABLE",E_BLOCK_ENCHANTMENT_TABLE); + tolua_constant(tolua_S,"E_BLOCK_BREWING_STAND",E_BLOCK_BREWING_STAND); + tolua_constant(tolua_S,"E_BLOCK_CAULDRON",E_BLOCK_CAULDRON); + tolua_constant(tolua_S,"E_BLOCK_END_PORTAL",E_BLOCK_END_PORTAL); + tolua_constant(tolua_S,"E_BLOCK_END_PORTAL_FRAME",E_BLOCK_END_PORTAL_FRAME); + tolua_constant(tolua_S,"E_BLOCK_END_STONE",E_BLOCK_END_STONE); + tolua_constant(tolua_S,"E_BLOCK_DRAGON_EGG",E_BLOCK_DRAGON_EGG); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_OFF",E_BLOCK_REDSTONE_LAMP_OFF); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_ON",E_BLOCK_REDSTONE_LAMP_ON); + tolua_constant(tolua_S,"E_BLOCK_DOUBLE_WOODEN_SLAB",E_BLOCK_DOUBLE_WOODEN_SLAB); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_SLAB",E_BLOCK_WOODEN_SLAB); + tolua_constant(tolua_S,"E_BLOCK_COCOA_POD",E_BLOCK_COCOA_POD); + tolua_constant(tolua_S,"E_BLOCK_SANDSTONE_STAIRS",E_BLOCK_SANDSTONE_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_EMERALD_ORE",E_BLOCK_EMERALD_ORE); + tolua_constant(tolua_S,"E_BLOCK_ENDER_CHEST",E_BLOCK_ENDER_CHEST); + tolua_constant(tolua_S,"E_BLOCK_TRIPWIRE_HOOK",E_BLOCK_TRIPWIRE_HOOK); + tolua_constant(tolua_S,"E_BLOCK_TRIPWIRE",E_BLOCK_TRIPWIRE); + tolua_constant(tolua_S,"E_BLOCK_EMERALD_BLOCK",E_BLOCK_EMERALD_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_SPRUCE_WOOD_STAIRS",E_BLOCK_SPRUCE_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_BIRCH_WOOD_STAIRS",E_BLOCK_BIRCH_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_JUNGLE_WOOD_STAIRS",E_BLOCK_JUNGLE_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_COMMAND_BLOCK",E_BLOCK_COMMAND_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_BEACON",E_BLOCK_BEACON); + tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE_WALL",E_BLOCK_COBBLESTONE_WALL); + tolua_constant(tolua_S,"E_BLOCK_FLOWER_POT",E_BLOCK_FLOWER_POT); + tolua_constant(tolua_S,"E_BLOCK_CARROTS",E_BLOCK_CARROTS); + tolua_constant(tolua_S,"E_BLOCK_POTATOES",E_BLOCK_POTATOES); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_BUTTON",E_BLOCK_WOODEN_BUTTON); + tolua_constant(tolua_S,"E_BLOCK_HEAD",E_BLOCK_HEAD); + tolua_constant(tolua_S,"E_BLOCK_ANVIL",E_BLOCK_ANVIL); + tolua_constant(tolua_S,"E_BLOCK_TRAPPED_CHEST",E_BLOCK_TRAPPED_CHEST); + tolua_constant(tolua_S,"E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE",E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE); + tolua_constant(tolua_S,"E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE",E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE); + tolua_constant(tolua_S,"E_BLOCK_INACTIVE_COMPARATOR",E_BLOCK_INACTIVE_COMPARATOR); + tolua_constant(tolua_S,"E_BLOCK_ACTIVE_COMPARATOR",E_BLOCK_ACTIVE_COMPARATOR); + tolua_constant(tolua_S,"E_BLOCK_DAYLIGHT_SENSOR",E_BLOCK_DAYLIGHT_SENSOR); + tolua_constant(tolua_S,"E_BLOCK_BLOCK_OF_REDSTONE",E_BLOCK_BLOCK_OF_REDSTONE); + tolua_constant(tolua_S,"E_BLOCK_NETHER_QUARTZ_ORE",E_BLOCK_NETHER_QUARTZ_ORE); + tolua_constant(tolua_S,"E_BLOCK_HOPPER",E_BLOCK_HOPPER); + tolua_constant(tolua_S,"E_BLOCK_QUARTZ_BLOCK",E_BLOCK_QUARTZ_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_QUARTZ_STAIRS",E_BLOCK_QUARTZ_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_ACTIVATOR_RAIL",E_BLOCK_ACTIVATOR_RAIL); + tolua_constant(tolua_S,"E_BLOCK_DROPPER",E_BLOCK_DROPPER); + tolua_constant(tolua_S,"E_BLOCK_STAINED_CLAY",E_BLOCK_STAINED_CLAY); + tolua_constant(tolua_S,"E_BLOCK_STAINED_GLASS_PANE",E_BLOCK_STAINED_GLASS_PANE); + tolua_constant(tolua_S,"E_BLOCK_NEW_LEAVES",E_BLOCK_NEW_LEAVES); + tolua_constant(tolua_S,"E_BLOCK_NEW_LOG",E_BLOCK_NEW_LOG); + tolua_constant(tolua_S,"E_BLOCK_ACACIA_WOOD_STAIRS",E_BLOCK_ACACIA_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_DARK_OAK_WOOD_STAIRS",E_BLOCK_DARK_OAK_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_HAY_BALE",E_BLOCK_HAY_BALE); + tolua_constant(tolua_S,"E_BLOCK_CARPET",E_BLOCK_CARPET); + tolua_constant(tolua_S,"E_BLOCK_HARDENED_CLAY",E_BLOCK_HARDENED_CLAY); + tolua_constant(tolua_S,"E_BLOCK_BLOCK_OF_COAL",E_BLOCK_BLOCK_OF_COAL); + tolua_constant(tolua_S,"E_BLOCK_PACKED_ICE",E_BLOCK_PACKED_ICE); + tolua_constant(tolua_S,"E_BLOCK_BIG_FLOWER",E_BLOCK_BIG_FLOWER); + tolua_constant(tolua_S,"E_BLOCK_NUMBER_OF_TYPES",E_BLOCK_NUMBER_OF_TYPES); + tolua_constant(tolua_S,"E_BLOCK_MAX_TYPE_ID",E_BLOCK_MAX_TYPE_ID); + tolua_constant(tolua_S,"E_BLOCK_YELLOW_FLOWER",E_BLOCK_YELLOW_FLOWER); + tolua_constant(tolua_S,"E_BLOCK_RED_ROSE",E_BLOCK_RED_ROSE); + tolua_constant(tolua_S,"E_BLOCK_LOCKED_CHEST",E_BLOCK_LOCKED_CHEST); + tolua_constant(tolua_S,"E_ITEM_EMPTY",E_ITEM_EMPTY); + tolua_constant(tolua_S,"E_ITEM_FIRST",E_ITEM_FIRST); + tolua_constant(tolua_S,"E_ITEM_IRON_SHOVEL",E_ITEM_IRON_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_IRON_PICKAXE",E_ITEM_IRON_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_IRON_AXE",E_ITEM_IRON_AXE); + tolua_constant(tolua_S,"E_ITEM_FLINT_AND_STEEL",E_ITEM_FLINT_AND_STEEL); + tolua_constant(tolua_S,"E_ITEM_RED_APPLE",E_ITEM_RED_APPLE); + tolua_constant(tolua_S,"E_ITEM_BOW",E_ITEM_BOW); + tolua_constant(tolua_S,"E_ITEM_ARROW",E_ITEM_ARROW); + tolua_constant(tolua_S,"E_ITEM_COAL",E_ITEM_COAL); + tolua_constant(tolua_S,"E_ITEM_DIAMOND",E_ITEM_DIAMOND); + tolua_constant(tolua_S,"E_ITEM_IRON",E_ITEM_IRON); + tolua_constant(tolua_S,"E_ITEM_GOLD",E_ITEM_GOLD); + tolua_constant(tolua_S,"E_ITEM_IRON_SWORD",E_ITEM_IRON_SWORD); + tolua_constant(tolua_S,"E_ITEM_WOODEN_SWORD",E_ITEM_WOODEN_SWORD); + tolua_constant(tolua_S,"E_ITEM_WOODEN_SHOVEL",E_ITEM_WOODEN_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_WOODEN_PICKAXE",E_ITEM_WOODEN_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_WOODEN_AXE",E_ITEM_WOODEN_AXE); + tolua_constant(tolua_S,"E_ITEM_STONE_SWORD",E_ITEM_STONE_SWORD); + tolua_constant(tolua_S,"E_ITEM_STONE_SHOVEL",E_ITEM_STONE_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_STONE_PICKAXE",E_ITEM_STONE_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_STONE_AXE",E_ITEM_STONE_AXE); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_SWORD",E_ITEM_DIAMOND_SWORD); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_SHOVEL",E_ITEM_DIAMOND_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_PICKAXE",E_ITEM_DIAMOND_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_AXE",E_ITEM_DIAMOND_AXE); + tolua_constant(tolua_S,"E_ITEM_STICK",E_ITEM_STICK); + tolua_constant(tolua_S,"E_ITEM_BOWL",E_ITEM_BOWL); + tolua_constant(tolua_S,"E_ITEM_MUSHROOM_SOUP",E_ITEM_MUSHROOM_SOUP); + tolua_constant(tolua_S,"E_ITEM_GOLD_SWORD",E_ITEM_GOLD_SWORD); + tolua_constant(tolua_S,"E_ITEM_GOLD_SHOVEL",E_ITEM_GOLD_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_GOLD_PICKAXE",E_ITEM_GOLD_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_GOLD_AXE",E_ITEM_GOLD_AXE); + tolua_constant(tolua_S,"E_ITEM_STRING",E_ITEM_STRING); + tolua_constant(tolua_S,"E_ITEM_FEATHER",E_ITEM_FEATHER); + tolua_constant(tolua_S,"E_ITEM_GUNPOWDER",E_ITEM_GUNPOWDER); + tolua_constant(tolua_S,"E_ITEM_WOODEN_HOE",E_ITEM_WOODEN_HOE); + tolua_constant(tolua_S,"E_ITEM_STONE_HOE",E_ITEM_STONE_HOE); + tolua_constant(tolua_S,"E_ITEM_IRON_HOE",E_ITEM_IRON_HOE); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_HOE",E_ITEM_DIAMOND_HOE); + tolua_constant(tolua_S,"E_ITEM_GOLD_HOE",E_ITEM_GOLD_HOE); + tolua_constant(tolua_S,"E_ITEM_SEEDS",E_ITEM_SEEDS); + tolua_constant(tolua_S,"E_ITEM_WHEAT",E_ITEM_WHEAT); + tolua_constant(tolua_S,"E_ITEM_BREAD",E_ITEM_BREAD); + tolua_constant(tolua_S,"E_ITEM_LEATHER_CAP",E_ITEM_LEATHER_CAP); + tolua_constant(tolua_S,"E_ITEM_LEATHER_TUNIC",E_ITEM_LEATHER_TUNIC); + tolua_constant(tolua_S,"E_ITEM_LEATHER_PANTS",E_ITEM_LEATHER_PANTS); + tolua_constant(tolua_S,"E_ITEM_LEATHER_BOOTS",E_ITEM_LEATHER_BOOTS); + tolua_constant(tolua_S,"E_ITEM_CHAIN_HELMET",E_ITEM_CHAIN_HELMET); + tolua_constant(tolua_S,"E_ITEM_CHAIN_CHESTPLATE",E_ITEM_CHAIN_CHESTPLATE); + tolua_constant(tolua_S,"E_ITEM_CHAIN_LEGGINGS",E_ITEM_CHAIN_LEGGINGS); + tolua_constant(tolua_S,"E_ITEM_CHAIN_BOOTS",E_ITEM_CHAIN_BOOTS); + tolua_constant(tolua_S,"E_ITEM_IRON_HELMET",E_ITEM_IRON_HELMET); + tolua_constant(tolua_S,"E_ITEM_IRON_CHESTPLATE",E_ITEM_IRON_CHESTPLATE); + tolua_constant(tolua_S,"E_ITEM_IRON_LEGGINGS",E_ITEM_IRON_LEGGINGS); + tolua_constant(tolua_S,"E_ITEM_IRON_BOOTS",E_ITEM_IRON_BOOTS); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_HELMET",E_ITEM_DIAMOND_HELMET); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_CHESTPLATE",E_ITEM_DIAMOND_CHESTPLATE); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_LEGGINGS",E_ITEM_DIAMOND_LEGGINGS); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_BOOTS",E_ITEM_DIAMOND_BOOTS); + tolua_constant(tolua_S,"E_ITEM_GOLD_HELMET",E_ITEM_GOLD_HELMET); + tolua_constant(tolua_S,"E_ITEM_GOLD_CHESTPLATE",E_ITEM_GOLD_CHESTPLATE); + tolua_constant(tolua_S,"E_ITEM_GOLD_LEGGINGS",E_ITEM_GOLD_LEGGINGS); + tolua_constant(tolua_S,"E_ITEM_GOLD_BOOTS",E_ITEM_GOLD_BOOTS); + tolua_constant(tolua_S,"E_ITEM_FLINT",E_ITEM_FLINT); + tolua_constant(tolua_S,"E_ITEM_RAW_PORKCHOP",E_ITEM_RAW_PORKCHOP); + tolua_constant(tolua_S,"E_ITEM_COOKED_PORKCHOP",E_ITEM_COOKED_PORKCHOP); + tolua_constant(tolua_S,"E_ITEM_PAINTINGS",E_ITEM_PAINTINGS); + tolua_constant(tolua_S,"E_ITEM_GOLDEN_APPLE",E_ITEM_GOLDEN_APPLE); + tolua_constant(tolua_S,"E_ITEM_SIGN",E_ITEM_SIGN); + tolua_constant(tolua_S,"E_ITEM_WOODEN_DOOR",E_ITEM_WOODEN_DOOR); + tolua_constant(tolua_S,"E_ITEM_BUCKET",E_ITEM_BUCKET); + tolua_constant(tolua_S,"E_ITEM_WATER_BUCKET",E_ITEM_WATER_BUCKET); + tolua_constant(tolua_S,"E_ITEM_LAVA_BUCKET",E_ITEM_LAVA_BUCKET); + tolua_constant(tolua_S,"E_ITEM_MINECART",E_ITEM_MINECART); + tolua_constant(tolua_S,"E_ITEM_SADDLE",E_ITEM_SADDLE); + tolua_constant(tolua_S,"E_ITEM_IRON_DOOR",E_ITEM_IRON_DOOR); + tolua_constant(tolua_S,"E_ITEM_REDSTONE_DUST",E_ITEM_REDSTONE_DUST); + tolua_constant(tolua_S,"E_ITEM_SNOWBALL",E_ITEM_SNOWBALL); + tolua_constant(tolua_S,"E_ITEM_BOAT",E_ITEM_BOAT); + tolua_constant(tolua_S,"E_ITEM_LEATHER",E_ITEM_LEATHER); + tolua_constant(tolua_S,"E_ITEM_MILK",E_ITEM_MILK); + tolua_constant(tolua_S,"E_ITEM_CLAY_BRICK",E_ITEM_CLAY_BRICK); + tolua_constant(tolua_S,"E_ITEM_CLAY",E_ITEM_CLAY); + tolua_constant(tolua_S,"E_ITEM_SUGARCANE",E_ITEM_SUGARCANE); + tolua_constant(tolua_S,"E_ITEM_SUGAR_CANE",E_ITEM_SUGAR_CANE); + tolua_constant(tolua_S,"E_ITEM_PAPER",E_ITEM_PAPER); + tolua_constant(tolua_S,"E_ITEM_BOOK",E_ITEM_BOOK); + tolua_constant(tolua_S,"E_ITEM_SLIMEBALL",E_ITEM_SLIMEBALL); + tolua_constant(tolua_S,"E_ITEM_CHEST_MINECART",E_ITEM_CHEST_MINECART); + tolua_constant(tolua_S,"E_ITEM_FURNACE_MINECART",E_ITEM_FURNACE_MINECART); + tolua_constant(tolua_S,"E_ITEM_EGG",E_ITEM_EGG); + tolua_constant(tolua_S,"E_ITEM_COMPASS",E_ITEM_COMPASS); + tolua_constant(tolua_S,"E_ITEM_FISHING_ROD",E_ITEM_FISHING_ROD); + tolua_constant(tolua_S,"E_ITEM_CLOCK",E_ITEM_CLOCK); + tolua_constant(tolua_S,"E_ITEM_GLOWSTONE_DUST",E_ITEM_GLOWSTONE_DUST); + tolua_constant(tolua_S,"E_ITEM_RAW_FISH",E_ITEM_RAW_FISH); + tolua_constant(tolua_S,"E_ITEM_COOKED_FISH",E_ITEM_COOKED_FISH); + tolua_constant(tolua_S,"E_ITEM_DYE",E_ITEM_DYE); + tolua_constant(tolua_S,"E_ITEM_BONE",E_ITEM_BONE); + tolua_constant(tolua_S,"E_ITEM_SUGAR",E_ITEM_SUGAR); + tolua_constant(tolua_S,"E_ITEM_CAKE",E_ITEM_CAKE); + tolua_constant(tolua_S,"E_ITEM_BED",E_ITEM_BED); + tolua_constant(tolua_S,"E_ITEM_REDSTONE_REPEATER",E_ITEM_REDSTONE_REPEATER); + tolua_constant(tolua_S,"E_ITEM_COOKIE",E_ITEM_COOKIE); + tolua_constant(tolua_S,"E_ITEM_MAP",E_ITEM_MAP); + tolua_constant(tolua_S,"E_ITEM_SHEARS",E_ITEM_SHEARS); + tolua_constant(tolua_S,"E_ITEM_MELON_SLICE",E_ITEM_MELON_SLICE); + tolua_constant(tolua_S,"E_ITEM_PUMPKIN_SEEDS",E_ITEM_PUMPKIN_SEEDS); + tolua_constant(tolua_S,"E_ITEM_MELON_SEEDS",E_ITEM_MELON_SEEDS); + tolua_constant(tolua_S,"E_ITEM_RAW_BEEF",E_ITEM_RAW_BEEF); + tolua_constant(tolua_S,"E_ITEM_STEAK",E_ITEM_STEAK); + tolua_constant(tolua_S,"E_ITEM_RAW_CHICKEN",E_ITEM_RAW_CHICKEN); + tolua_constant(tolua_S,"E_ITEM_COOKED_CHICKEN",E_ITEM_COOKED_CHICKEN); + tolua_constant(tolua_S,"E_ITEM_ROTTEN_FLESH",E_ITEM_ROTTEN_FLESH); + tolua_constant(tolua_S,"E_ITEM_ENDER_PEARL",E_ITEM_ENDER_PEARL); + tolua_constant(tolua_S,"E_ITEM_BLAZE_ROD",E_ITEM_BLAZE_ROD); + tolua_constant(tolua_S,"E_ITEM_GHAST_TEAR",E_ITEM_GHAST_TEAR); + tolua_constant(tolua_S,"E_ITEM_GOLD_NUGGET",E_ITEM_GOLD_NUGGET); + tolua_constant(tolua_S,"E_ITEM_NETHER_WART",E_ITEM_NETHER_WART); + tolua_constant(tolua_S,"E_ITEM_POTIONS",E_ITEM_POTIONS); + tolua_constant(tolua_S,"E_ITEM_GLASS_BOTTLE",E_ITEM_GLASS_BOTTLE); + tolua_constant(tolua_S,"E_ITEM_SPIDER_EYE",E_ITEM_SPIDER_EYE); + tolua_constant(tolua_S,"E_ITEM_FERMENTED_SPIDER_EYE",E_ITEM_FERMENTED_SPIDER_EYE); + tolua_constant(tolua_S,"E_ITEM_BLAZE_POWDER",E_ITEM_BLAZE_POWDER); + tolua_constant(tolua_S,"E_ITEM_MAGMA_CREAM",E_ITEM_MAGMA_CREAM); + tolua_constant(tolua_S,"E_ITEM_BREWING_STAND",E_ITEM_BREWING_STAND); + tolua_constant(tolua_S,"E_ITEM_CAULDRON",E_ITEM_CAULDRON); + tolua_constant(tolua_S,"E_ITEM_EYE_OF_ENDER",E_ITEM_EYE_OF_ENDER); + tolua_constant(tolua_S,"E_ITEM_GLISTERING_MELON",E_ITEM_GLISTERING_MELON); + tolua_constant(tolua_S,"E_ITEM_SPAWN_EGG",E_ITEM_SPAWN_EGG); + tolua_constant(tolua_S,"E_ITEM_BOTTLE_O_ENCHANTING",E_ITEM_BOTTLE_O_ENCHANTING); + tolua_constant(tolua_S,"E_ITEM_FIRE_CHARGE",E_ITEM_FIRE_CHARGE); + tolua_constant(tolua_S,"E_ITEM_BOOK_AND_QUILL",E_ITEM_BOOK_AND_QUILL); + tolua_constant(tolua_S,"E_ITEM_WRITTEN_BOOK",E_ITEM_WRITTEN_BOOK); + tolua_constant(tolua_S,"E_ITEM_EMERALD",E_ITEM_EMERALD); + tolua_constant(tolua_S,"E_ITEM_ITEM_FRAME",E_ITEM_ITEM_FRAME); + tolua_constant(tolua_S,"E_ITEM_FLOWER_POT",E_ITEM_FLOWER_POT); + tolua_constant(tolua_S,"E_ITEM_CARROT",E_ITEM_CARROT); + tolua_constant(tolua_S,"E_ITEM_POTATO",E_ITEM_POTATO); + tolua_constant(tolua_S,"E_ITEM_BAKED_POTATO",E_ITEM_BAKED_POTATO); + tolua_constant(tolua_S,"E_ITEM_POISONOUS_POTATO",E_ITEM_POISONOUS_POTATO); + tolua_constant(tolua_S,"E_ITEM_EMPTY_MAP",E_ITEM_EMPTY_MAP); + tolua_constant(tolua_S,"E_ITEM_GOLDEN_CARROT",E_ITEM_GOLDEN_CARROT); + tolua_constant(tolua_S,"E_ITEM_HEAD",E_ITEM_HEAD); + tolua_constant(tolua_S,"E_ITEM_CARROT_ON_STICK",E_ITEM_CARROT_ON_STICK); + tolua_constant(tolua_S,"E_ITEM_NETHER_STAR",E_ITEM_NETHER_STAR); + tolua_constant(tolua_S,"E_ITEM_PUMPKIN_PIE",E_ITEM_PUMPKIN_PIE); + tolua_constant(tolua_S,"E_ITEM_FIREWORK_ROCKET",E_ITEM_FIREWORK_ROCKET); + tolua_constant(tolua_S,"E_ITEM_FIREWORK_STAR",E_ITEM_FIREWORK_STAR); + tolua_constant(tolua_S,"E_ITEM_ENCHANTED_BOOK",E_ITEM_ENCHANTED_BOOK); + tolua_constant(tolua_S,"E_ITEM_COMPARATOR",E_ITEM_COMPARATOR); + tolua_constant(tolua_S,"E_ITEM_NETHER_BRICK",E_ITEM_NETHER_BRICK); + tolua_constant(tolua_S,"E_ITEM_NETHER_QUARTZ",E_ITEM_NETHER_QUARTZ); + tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_TNT",E_ITEM_MINECART_WITH_TNT); + tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_HOPPER",E_ITEM_MINECART_WITH_HOPPER); + tolua_constant(tolua_S,"E_ITEM_IRON_HORSE_ARMOR",E_ITEM_IRON_HORSE_ARMOR); + tolua_constant(tolua_S,"E_ITEM_GOLD_HORSE_ARMOR",E_ITEM_GOLD_HORSE_ARMOR); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_HORSE_ARMOR",E_ITEM_DIAMOND_HORSE_ARMOR); + tolua_constant(tolua_S,"E_ITEM_LEAD",E_ITEM_LEAD); + tolua_constant(tolua_S,"E_ITEM_NAME_TAG",E_ITEM_NAME_TAG); + tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_COMMAND_BLOCK",E_ITEM_MINECART_WITH_COMMAND_BLOCK); + tolua_constant(tolua_S,"E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES",E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES); + tolua_constant(tolua_S,"E_ITEM_MAX_CONSECUTIVE_TYPE_ID",E_ITEM_MAX_CONSECUTIVE_TYPE_ID); + tolua_constant(tolua_S,"E_ITEM_FIRST_DISC",E_ITEM_FIRST_DISC); + tolua_constant(tolua_S,"E_ITEM_13_DISC",E_ITEM_13_DISC); + tolua_constant(tolua_S,"E_ITEM_CAT_DISC",E_ITEM_CAT_DISC); + tolua_constant(tolua_S,"E_ITEM_BLOCKS_DISC",E_ITEM_BLOCKS_DISC); + tolua_constant(tolua_S,"E_ITEM_CHIRP_DISC",E_ITEM_CHIRP_DISC); + tolua_constant(tolua_S,"E_ITEM_FAR_DISC",E_ITEM_FAR_DISC); + tolua_constant(tolua_S,"E_ITEM_MALL_DISC",E_ITEM_MALL_DISC); + tolua_constant(tolua_S,"E_ITEM_MELLOHI_DISC",E_ITEM_MELLOHI_DISC); + tolua_constant(tolua_S,"E_ITEM_STAL_DISC",E_ITEM_STAL_DISC); + tolua_constant(tolua_S,"E_ITEM_STRAD_DISC",E_ITEM_STRAD_DISC); + tolua_constant(tolua_S,"E_ITEM_WARD_DISC",E_ITEM_WARD_DISC); + tolua_constant(tolua_S,"E_ITEM_11_DISC",E_ITEM_11_DISC); + tolua_constant(tolua_S,"E_ITEM_WAIT_DISC",E_ITEM_WAIT_DISC); + tolua_constant(tolua_S,"E_ITEM_LAST_DISC_PLUS_ONE",E_ITEM_LAST_DISC_PLUS_ONE); + tolua_constant(tolua_S,"E_ITEM_LAST_DISC",E_ITEM_LAST_DISC); + tolua_constant(tolua_S,"E_ITEM_LAST",E_ITEM_LAST); + tolua_constant(tolua_S,"E_META_CHEST_FACING_ZM",E_META_CHEST_FACING_ZM); + tolua_constant(tolua_S,"E_META_CHEST_FACING_ZP",E_META_CHEST_FACING_ZP); + tolua_constant(tolua_S,"E_META_CHEST_FACING_XM",E_META_CHEST_FACING_XM); + tolua_constant(tolua_S,"E_META_CHEST_FACING_XP",E_META_CHEST_FACING_XP); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_YM",E_META_DROPSPENSER_FACING_YM); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_YP",E_META_DROPSPENSER_FACING_YP); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_ZM",E_META_DROPSPENSER_FACING_ZM); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_ZP",E_META_DROPSPENSER_FACING_ZP); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_XM",E_META_DROPSPENSER_FACING_XM); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_XP",E_META_DROPSPENSER_FACING_XP); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE",E_META_DOUBLE_STONE_SLAB_STONE); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_SANDSTONE",E_META_DOUBLE_STONE_SLAB_SANDSTONE); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_WOODEN",E_META_DOUBLE_STONE_SLAB_WOODEN); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_COBBLESTONE",E_META_DOUBLE_STONE_SLAB_COBBLESTONE); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_BRICK",E_META_DOUBLE_STONE_SLAB_BRICK); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE_BRICK",E_META_DOUBLE_STONE_SLAB_STONE_BRICK); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_NETHER_BRICK",E_META_DOUBLE_STONE_SLAB_NETHER_BRICK); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE_SECRET",E_META_DOUBLE_STONE_SLAB_STONE_SECRET); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_YM",E_META_HOPPER_FACING_YM); + tolua_constant(tolua_S,"E_META_HOPPER_UNATTACHED",E_META_HOPPER_UNATTACHED); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_ZM",E_META_HOPPER_FACING_ZM); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_ZP",E_META_HOPPER_FACING_ZP); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_XM",E_META_HOPPER_FACING_XM); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_XP",E_META_HOPPER_FACING_XP); + tolua_constant(tolua_S,"E_META_LEAVES_APPLE",E_META_LEAVES_APPLE); + tolua_constant(tolua_S,"E_META_LEAVES_CONIFER",E_META_LEAVES_CONIFER); + tolua_constant(tolua_S,"E_META_LEAVES_BIRCH",E_META_LEAVES_BIRCH); + tolua_constant(tolua_S,"E_META_LEAVES_JUNGLE",E_META_LEAVES_JUNGLE); + tolua_constant(tolua_S,"E_META_LOG_APPLE",E_META_LOG_APPLE); + tolua_constant(tolua_S,"E_META_LOG_CONIFER",E_META_LOG_CONIFER); + tolua_constant(tolua_S,"E_META_LOG_BIRCH",E_META_LOG_BIRCH); + tolua_constant(tolua_S,"E_META_LOG_JUNGLE",E_META_LOG_JUNGLE); + tolua_constant(tolua_S,"E_META_PLANKS_APPLE",E_META_PLANKS_APPLE); + tolua_constant(tolua_S,"E_META_PLANKS_CONIFER",E_META_PLANKS_CONIFER); + tolua_constant(tolua_S,"E_META_PLANKS_BIRCH",E_META_PLANKS_BIRCH); + tolua_constant(tolua_S,"E_META_PLANKS_JUNGLE",E_META_PLANKS_JUNGLE); + tolua_constant(tolua_S,"E_META_SANDSTONE_NORMAL",E_META_SANDSTONE_NORMAL); + tolua_constant(tolua_S,"E_META_SANDSTONE_ORNAMENT",E_META_SANDSTONE_ORNAMENT); + tolua_constant(tolua_S,"E_META_SANDSTONE_SMOOTH",E_META_SANDSTONE_SMOOTH); + tolua_constant(tolua_S,"E_META_SAPLING_APPLE",E_META_SAPLING_APPLE); + tolua_constant(tolua_S,"E_META_SAPLING_CONIFER",E_META_SAPLING_CONIFER); + tolua_constant(tolua_S,"E_META_SAPLING_BIRCH",E_META_SAPLING_BIRCH); + tolua_constant(tolua_S,"E_META_SAPLING_JUNGLE",E_META_SAPLING_JUNGLE); + tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_STONE",E_META_SILVERFISH_EGG_STONE); + tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_COBBLESTONE",E_META_SILVERFISH_EGG_COBBLESTONE); + tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_STONE_BRICK",E_META_SILVERFISH_EGG_STONE_BRICK); + tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE",E_META_STONE_SLAB_STONE); + tolua_constant(tolua_S,"E_META_STONE_SLAB_SANDSTONE",E_META_STONE_SLAB_SANDSTONE); + tolua_constant(tolua_S,"E_META_STONE_SLAB_PLANKS",E_META_STONE_SLAB_PLANKS); + tolua_constant(tolua_S,"E_META_STONE_SLAB_COBBLESTONE",E_META_STONE_SLAB_COBBLESTONE); + tolua_constant(tolua_S,"E_META_STONE_SLAB_BRICK",E_META_STONE_SLAB_BRICK); + tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE_BRICK",E_META_STONE_SLAB_STONE_BRICK); + tolua_constant(tolua_S,"E_META_STONE_SLAB_NETHER_BRICK",E_META_STONE_SLAB_NETHER_BRICK); + tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE_SECRET",E_META_STONE_SLAB_STONE_SECRET); + tolua_constant(tolua_S,"E_META_STONE_BRICK_NORMAL",E_META_STONE_BRICK_NORMAL); + tolua_constant(tolua_S,"E_META_STONE_BRICK_MOSSY",E_META_STONE_BRICK_MOSSY); + tolua_constant(tolua_S,"E_META_STONE_BRICK_CRACKED",E_META_STONE_BRICK_CRACKED); + tolua_constant(tolua_S,"E_META_STONE_BRICK_ORNAMENT",E_META_STONE_BRICK_ORNAMENT); + tolua_constant(tolua_S,"E_META_TALL_GRASS_DEAD_SHRUB",E_META_TALL_GRASS_DEAD_SHRUB); + tolua_constant(tolua_S,"E_META_TALL_GRASS_GRASS",E_META_TALL_GRASS_GRASS); + tolua_constant(tolua_S,"E_META_TALL_GRASS_FERN",E_META_TALL_GRASS_FERN); + tolua_constant(tolua_S,"E_META_TORCH_EAST",E_META_TORCH_EAST); + tolua_constant(tolua_S,"E_META_TORCH_WEST",E_META_TORCH_WEST); + tolua_constant(tolua_S,"E_META_TORCH_SOUTH",E_META_TORCH_SOUTH); + tolua_constant(tolua_S,"E_META_TORCH_NORTH",E_META_TORCH_NORTH); + tolua_constant(tolua_S,"E_META_TORCH_FLOOR",E_META_TORCH_FLOOR); + tolua_constant(tolua_S,"E_META_TORCH_XM",E_META_TORCH_XM); + tolua_constant(tolua_S,"E_META_TORCH_XP",E_META_TORCH_XP); + tolua_constant(tolua_S,"E_META_TORCH_ZM",E_META_TORCH_ZM); + tolua_constant(tolua_S,"E_META_TORCH_ZP",E_META_TORCH_ZP); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_APPLE",E_META_WOODEN_DOUBLE_SLAB_APPLE); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_CONIFER",E_META_WOODEN_DOUBLE_SLAB_CONIFER); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_BIRCH",E_META_WOODEN_DOUBLE_SLAB_BIRCH); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_JUNGLE",E_META_WOODEN_DOUBLE_SLAB_JUNGLE); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_ACACIA",E_META_WOODEN_DOUBLE_SLAB_ACACIA); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_DARK_OAK",E_META_WOODEN_DOUBLE_SLAB_DARK_OAK); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_APPLE",E_META_WOODEN_SLAB_APPLE); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_CONIFER",E_META_WOODEN_SLAB_CONIFER); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_BIRCH",E_META_WOODEN_SLAB_BIRCH); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_JUNGLE",E_META_WOODEN_SLAB_JUNGLE); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_ACACIA",E_META_WOODEN_SLAB_ACACIA); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_DARK_OAK",E_META_WOODEN_SLAB_DARK_OAK); + tolua_constant(tolua_S,"E_META_WOOL_WHITE",E_META_WOOL_WHITE); + tolua_constant(tolua_S,"E_META_WOOL_ORANGE",E_META_WOOL_ORANGE); + tolua_constant(tolua_S,"E_META_WOOL_MAGENTA",E_META_WOOL_MAGENTA); + tolua_constant(tolua_S,"E_META_WOOL_LIGHTBLUE",E_META_WOOL_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_WOOL_YELLOW",E_META_WOOL_YELLOW); + tolua_constant(tolua_S,"E_META_WOOL_LIGHTGREEN",E_META_WOOL_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_WOOL_PINK",E_META_WOOL_PINK); + tolua_constant(tolua_S,"E_META_WOOL_GRAY",E_META_WOOL_GRAY); + tolua_constant(tolua_S,"E_META_WOOL_LIGHTGRAY",E_META_WOOL_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_WOOL_CYAN",E_META_WOOL_CYAN); + tolua_constant(tolua_S,"E_META_WOOL_PURPLE",E_META_WOOL_PURPLE); + tolua_constant(tolua_S,"E_META_WOOL_BLUE",E_META_WOOL_BLUE); + tolua_constant(tolua_S,"E_META_WOOL_BROWN",E_META_WOOL_BROWN); + tolua_constant(tolua_S,"E_META_WOOL_GREEN",E_META_WOOL_GREEN); + tolua_constant(tolua_S,"E_META_WOOL_RED",E_META_WOOL_RED); + tolua_constant(tolua_S,"E_META_WOOL_BLACK",E_META_WOOL_BLACK); + tolua_constant(tolua_S,"E_META_CARPET_WHITE",E_META_CARPET_WHITE); + tolua_constant(tolua_S,"E_META_CARPET_ORANGE",E_META_CARPET_ORANGE); + tolua_constant(tolua_S,"E_META_CARPET_MAGENTA",E_META_CARPET_MAGENTA); + tolua_constant(tolua_S,"E_META_CARPET_LIGHTBLUE",E_META_CARPET_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_CARPET_YELLOW",E_META_CARPET_YELLOW); + tolua_constant(tolua_S,"E_META_CARPET_LIGHTGREEN",E_META_CARPET_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_CARPET_PINK",E_META_CARPET_PINK); + tolua_constant(tolua_S,"E_META_CARPET_GRAY",E_META_CARPET_GRAY); + tolua_constant(tolua_S,"E_META_CARPET_LIGHTGRAY",E_META_CARPET_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_CARPET_CYAN",E_META_CARPET_CYAN); + tolua_constant(tolua_S,"E_META_CARPET_PURPLE",E_META_CARPET_PURPLE); + tolua_constant(tolua_S,"E_META_CARPET_BLUE",E_META_CARPET_BLUE); + tolua_constant(tolua_S,"E_META_CARPET_BROWN",E_META_CARPET_BROWN); + tolua_constant(tolua_S,"E_META_CARPET_GREEN",E_META_CARPET_GREEN); + tolua_constant(tolua_S,"E_META_CARPET_RED",E_META_CARPET_RED); + tolua_constant(tolua_S,"E_META_CARPET_BLACK",E_META_CARPET_BLACK); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_WHITE",E_META_STAINED_CLAY_WHITE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_ORANGE",E_META_STAINED_CLAY_ORANGE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_MAGENTA",E_META_STAINED_CLAY_MAGENTA); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTBLUE",E_META_STAINED_CLAY_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_YELLOW",E_META_STAINED_CLAY_YELLOW); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTGREEN",E_META_STAINED_CLAY_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_PINK",E_META_STAINED_CLAY_PINK); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_GRAY",E_META_STAINED_CLAY_GRAY); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTGRAY",E_META_STAINED_CLAY_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_CYAN",E_META_STAINED_CLAY_CYAN); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_PURPLE",E_META_STAINED_CLAY_PURPLE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_BLUE",E_META_STAINED_CLAY_BLUE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_BROWN",E_META_STAINED_CLAY_BROWN); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_GREEN",E_META_STAINED_CLAY_GREEN); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_RED",E_META_STAINED_CLAY_RED); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_BLACK",E_META_STAINED_CLAY_BLACK); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_WHITE",E_META_STAINED_GLASS_WHITE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_ORANGE",E_META_STAINED_GLASS_ORANGE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_MAGENTA",E_META_STAINED_GLASS_MAGENTA); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTBLUE",E_META_STAINED_GLASS_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_YELLOW",E_META_STAINED_GLASS_YELLOW); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTGREEN",E_META_STAINED_GLASS_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PINK",E_META_STAINED_GLASS_PINK); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_GRAY",E_META_STAINED_GLASS_GRAY); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTGRAY",E_META_STAINED_GLASS_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_CYAN",E_META_STAINED_GLASS_CYAN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PURPLE",E_META_STAINED_GLASS_PURPLE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_BLUE",E_META_STAINED_GLASS_BLUE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_BROWN",E_META_STAINED_GLASS_BROWN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_GREEN",E_META_STAINED_GLASS_GREEN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_RED",E_META_STAINED_GLASS_RED); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_BLACK",E_META_STAINED_GLASS_BLACK); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_WHITE",E_META_STAINED_GLASS_PANE_WHITE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_ORANGE",E_META_STAINED_GLASS_PANE_ORANGE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_MAGENTA",E_META_STAINED_GLASS_PANE_MAGENTA); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTBLUE",E_META_STAINED_GLASS_PANE_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_YELLOW",E_META_STAINED_GLASS_PANE_YELLOW); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTGREEN",E_META_STAINED_GLASS_PANE_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_PINK",E_META_STAINED_GLASS_PANE_PINK); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_GRAY",E_META_STAINED_GLASS_PANE_GRAY); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTGRAY",E_META_STAINED_GLASS_PANE_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_CYAN",E_META_STAINED_GLASS_PANE_CYAN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_PURPLE",E_META_STAINED_GLASS_PANE_PURPLE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BLUE",E_META_STAINED_GLASS_PANE_BLUE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BROWN",E_META_STAINED_GLASS_PANE_BROWN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_GREEN",E_META_STAINED_GLASS_PANE_GREEN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_RED",E_META_STAINED_GLASS_PANE_RED); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BLACK",E_META_STAINED_GLASS_PANE_BLACK); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_ONE",E_META_SNOW_LAYER_ONE); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_TWO",E_META_SNOW_LAYER_TWO); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_THREE",E_META_SNOW_LAYER_THREE); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_FOUR",E_META_SNOW_LAYER_FOUR); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_FIVE",E_META_SNOW_LAYER_FIVE); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_SIX",E_META_SNOW_LAYER_SIX); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_SEVEN",E_META_SNOW_LAYER_SEVEN); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_EIGHT",E_META_SNOW_LAYER_EIGHT); + tolua_constant(tolua_S,"E_META_RAIL_ZM_ZP",E_META_RAIL_ZM_ZP); + tolua_constant(tolua_S,"E_META_RAIL_XM_XP",E_META_RAIL_XM_XP); + tolua_constant(tolua_S,"E_META_RAIL_ASCEND_XP",E_META_RAIL_ASCEND_XP); + tolua_constant(tolua_S,"E_META_RAIL_ASCEND_XM",E_META_RAIL_ASCEND_XM); + tolua_constant(tolua_S,"E_META_RAIL_ASCEND_ZM",E_META_RAIL_ASCEND_ZM); + tolua_constant(tolua_S,"E_META_RAIL_ASCEND_ZP",E_META_RAIL_ASCEND_ZP); + tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZP_XP",E_META_RAIL_CURVED_ZP_XP); + tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZP_XM",E_META_RAIL_CURVED_ZP_XM); + tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZM_XM",E_META_RAIL_CURVED_ZM_XM); + tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZM_XP",E_META_RAIL_CURVED_ZM_XP); + tolua_constant(tolua_S,"E_META_NEW_LEAVES_ACACIA_WOOD",E_META_NEW_LEAVES_ACACIA_WOOD); + tolua_constant(tolua_S,"E_META_NEW_LEAVES_DARK_OAK_WOOD",E_META_NEW_LEAVES_DARK_OAK_WOOD); + tolua_constant(tolua_S,"E_META_NEW_LOG_ACACIA_WOOD",E_META_NEW_LOG_ACACIA_WOOD); + tolua_constant(tolua_S,"E_META_NEW_LOG_DARK_OAK_WOOD",E_META_NEW_LOG_DARK_OAK_WOOD); + tolua_constant(tolua_S,"E_META_FLOWER_POPPY",E_META_FLOWER_POPPY); + tolua_constant(tolua_S,"E_META_FLOWER_BLUE_ORCHID",E_META_FLOWER_BLUE_ORCHID); + tolua_constant(tolua_S,"E_META_FLOWER_ALLIUM",E_META_FLOWER_ALLIUM); + tolua_constant(tolua_S,"E_META_FLOWER_RED_TULIP",E_META_FLOWER_RED_TULIP); + tolua_constant(tolua_S,"E_META_FLOWER_ORANGE_TULIP",E_META_FLOWER_ORANGE_TULIP); + tolua_constant(tolua_S,"E_META_FLOWER_WHITE_TULIP",E_META_FLOWER_WHITE_TULIP); + tolua_constant(tolua_S,"E_META_FLOWER_PINK_TULIP",E_META_FLOWER_PINK_TULIP); + tolua_constant(tolua_S,"E_META_FLOWER_OXEYE_DAISY",E_META_FLOWER_OXEYE_DAISY); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_SUNFLOWER",E_META_BIG_FLOWER_SUNFLOWER); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_LILAC",E_META_BIG_FLOWER_LILAC); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_DOUBLE_TALL_GRASS",E_META_BIG_FLOWER_DOUBLE_TALL_GRASS); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_LARGE_FERN",E_META_BIG_FLOWER_LARGE_FERN); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_ROSE_BUSH",E_META_BIG_FLOWER_ROSE_BUSH); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_PEONY",E_META_BIG_FLOWER_PEONY); + tolua_constant(tolua_S,"E_META_COAL_NORMAL",E_META_COAL_NORMAL); + tolua_constant(tolua_S,"E_META_COAL_CHARCOAL",E_META_COAL_CHARCOAL); + tolua_constant(tolua_S,"E_META_DYE_BLACK",E_META_DYE_BLACK); + tolua_constant(tolua_S,"E_META_DYE_RED",E_META_DYE_RED); + tolua_constant(tolua_S,"E_META_DYE_GREEN",E_META_DYE_GREEN); + tolua_constant(tolua_S,"E_META_DYE_BROWN",E_META_DYE_BROWN); + tolua_constant(tolua_S,"E_META_DYE_BLUE",E_META_DYE_BLUE); + tolua_constant(tolua_S,"E_META_DYE_PURPLE",E_META_DYE_PURPLE); + tolua_constant(tolua_S,"E_META_DYE_CYAN",E_META_DYE_CYAN); + tolua_constant(tolua_S,"E_META_DYE_LIGHTGRAY",E_META_DYE_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_DYE_GRAY",E_META_DYE_GRAY); + tolua_constant(tolua_S,"E_META_DYE_PINK",E_META_DYE_PINK); + tolua_constant(tolua_S,"E_META_DYE_LIGHTGREEN",E_META_DYE_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_DYE_YELLOW",E_META_DYE_YELLOW); + tolua_constant(tolua_S,"E_META_DYE_LIGHTBLUE",E_META_DYE_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_DYE_MAGENTA",E_META_DYE_MAGENTA); + tolua_constant(tolua_S,"E_META_DYE_ORANGE",E_META_DYE_ORANGE); + tolua_constant(tolua_S,"E_META_DYE_WHITE",E_META_DYE_WHITE); + tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_NORMAL",E_META_GOLDEN_APPLE_NORMAL); + tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_ENCHANTED",E_META_GOLDEN_APPLE_ENCHANTED); + tolua_constant(tolua_S,"E_META_RAW_FISH_FISH",E_META_RAW_FISH_FISH); + tolua_constant(tolua_S,"E_META_RAW_FISH_SALMON",E_META_RAW_FISH_SALMON); + tolua_constant(tolua_S,"E_META_RAW_FISH_CLOWNFISH",E_META_RAW_FISH_CLOWNFISH); + tolua_constant(tolua_S,"E_META_RAW_FISH_PUFFERFISH",E_META_RAW_FISH_PUFFERFISH); + tolua_constant(tolua_S,"E_META_COOKED_FISH_FISH",E_META_COOKED_FISH_FISH); + tolua_constant(tolua_S,"E_META_COOKED_FISH_SALMON",E_META_COOKED_FISH_SALMON); + tolua_constant(tolua_S,"E_META_COOKED_FISH_CLOWNFISH",E_META_COOKED_FISH_CLOWNFISH); + tolua_constant(tolua_S,"E_META_COOKED_FISH_PUFFERFISH",E_META_COOKED_FISH_PUFFERFISH); + tolua_constant(tolua_S,"E_META_TRACKS_X",E_META_TRACKS_X); + tolua_constant(tolua_S,"E_META_TRACKS_Z",E_META_TRACKS_Z); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PICKUP",E_META_SPAWN_EGG_PICKUP); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXPERIENCE_ORB",E_META_SPAWN_EGG_EXPERIENCE_ORB); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_LEASH_KNOT",E_META_SPAWN_EGG_LEASH_KNOT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PAINTING",E_META_SPAWN_EGG_PAINTING); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ARROW",E_META_SPAWN_EGG_ARROW); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOWBALL",E_META_SPAWN_EGG_SNOWBALL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREBALL",E_META_SPAWN_EGG_FIREBALL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SMALL_FIREBALL",E_META_SPAWN_EGG_SMALL_FIREBALL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_PEARL",E_META_SPAWN_EGG_ENDER_PEARL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_EYE_OF_ENDER",E_META_SPAWN_EGG_EYE_OF_ENDER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPLASH_POTION",E_META_SPAWN_EGG_SPLASH_POTION); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXP_BOTTLE",E_META_SPAWN_EGG_EXP_BOTTLE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ITEM_FRAME",E_META_SPAWN_EGG_ITEM_FRAME); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER_SKULL",E_META_SPAWN_EGG_WITHER_SKULL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PRIMED_TNT",E_META_SPAWN_EGG_PRIMED_TNT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_FALLING_BLOCK",E_META_SPAWN_EGG_FALLING_BLOCK); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREWORK",E_META_SPAWN_EGG_FIREWORK); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_BOAT",E_META_SPAWN_EGG_BOAT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART",E_META_SPAWN_EGG_MINECART); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_CHEST",E_META_SPAWN_EGG_MINECART_CHEST); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_FURNACE",E_META_SPAWN_EGG_MINECART_FURNACE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_TNT",E_META_SPAWN_EGG_MINECART_TNT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_HOPPER",E_META_SPAWN_EGG_MINECART_HOPPER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_SPAWNER",E_META_SPAWN_EGG_MINECART_SPAWNER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_CREEPER",E_META_SPAWN_EGG_CREEPER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SKELETON",E_META_SPAWN_EGG_SKELETON); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPIDER",E_META_SPAWN_EGG_SPIDER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_GIANT",E_META_SPAWN_EGG_GIANT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE",E_META_SPAWN_EGG_ZOMBIE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SLIME",E_META_SPAWN_EGG_SLIME); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_GHAST",E_META_SPAWN_EGG_GHAST); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE_PIGMAN",E_META_SPAWN_EGG_ZOMBIE_PIGMAN); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDERMAN",E_META_SPAWN_EGG_ENDERMAN); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_CAVE_SPIDER",E_META_SPAWN_EGG_CAVE_SPIDER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SILVERFISH",E_META_SPAWN_EGG_SILVERFISH); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_BLAZE",E_META_SPAWN_EGG_BLAZE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MAGMA_CUBE",E_META_SPAWN_EGG_MAGMA_CUBE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_DRAGON",E_META_SPAWN_EGG_ENDER_DRAGON); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER",E_META_SPAWN_EGG_WITHER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_BAT",E_META_SPAWN_EGG_BAT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITCH",E_META_SPAWN_EGG_WITCH); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PIG",E_META_SPAWN_EGG_PIG); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SHEEP",E_META_SPAWN_EGG_SHEEP); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_COW",E_META_SPAWN_EGG_COW); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_CHICKEN",E_META_SPAWN_EGG_CHICKEN); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SQUID",E_META_SPAWN_EGG_SQUID); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_WOLF",E_META_SPAWN_EGG_WOLF); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MOOSHROOM",E_META_SPAWN_EGG_MOOSHROOM); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOW_GOLEM",E_META_SPAWN_EGG_SNOW_GOLEM); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_OCELOT",E_META_SPAWN_EGG_OCELOT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_IRON_GOLEM",E_META_SPAWN_EGG_IRON_GOLEM); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_HORSE",E_META_SPAWN_EGG_HORSE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_VILLAGER",E_META_SPAWN_EGG_VILLAGER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_CRYSTAL",E_META_SPAWN_EGG_ENDER_CRYSTAL); + tolua_constant(tolua_S,"dimNether",dimNether); + tolua_constant(tolua_S,"dimOverworld",dimOverworld); + tolua_constant(tolua_S,"dimEnd",dimEnd); + tolua_constant(tolua_S,"dtAttack",dtAttack); + tolua_constant(tolua_S,"dtRangedAttack",dtRangedAttack); + tolua_constant(tolua_S,"dtLightning",dtLightning); + tolua_constant(tolua_S,"dtFalling",dtFalling); + tolua_constant(tolua_S,"dtDrowning",dtDrowning); + tolua_constant(tolua_S,"dtSuffocating",dtSuffocating); + tolua_constant(tolua_S,"dtStarving",dtStarving); + tolua_constant(tolua_S,"dtCactusContact",dtCactusContact); + tolua_constant(tolua_S,"dtLavaContact",dtLavaContact); + tolua_constant(tolua_S,"dtPoisoning",dtPoisoning); + tolua_constant(tolua_S,"dtOnFire",dtOnFire); + tolua_constant(tolua_S,"dtFireContact",dtFireContact); + tolua_constant(tolua_S,"dtInVoid",dtInVoid); + tolua_constant(tolua_S,"dtPotionOfHarming",dtPotionOfHarming); + tolua_constant(tolua_S,"dtEnderPearl",dtEnderPearl); + tolua_constant(tolua_S,"dtAdmin",dtAdmin); + tolua_constant(tolua_S,"dtPawnAttack",dtPawnAttack); + tolua_constant(tolua_S,"dtEntityAttack",dtEntityAttack); + tolua_constant(tolua_S,"dtMob",dtMob); + tolua_constant(tolua_S,"dtMobAttack",dtMobAttack); + tolua_constant(tolua_S,"dtArrowAttack",dtArrowAttack); + tolua_constant(tolua_S,"dtArrow",dtArrow); + tolua_constant(tolua_S,"dtProjectile",dtProjectile); + tolua_constant(tolua_S,"dtFall",dtFall); + tolua_constant(tolua_S,"dtDrown",dtDrown); + tolua_constant(tolua_S,"dtSuffocation",dtSuffocation); + tolua_constant(tolua_S,"dtStarvation",dtStarvation); + tolua_constant(tolua_S,"dtHunger",dtHunger); + tolua_constant(tolua_S,"dtCactus",dtCactus); + tolua_constant(tolua_S,"dtCactuses",dtCactuses); + tolua_constant(tolua_S,"dtCacti",dtCacti); + tolua_constant(tolua_S,"dtLava",dtLava); + tolua_constant(tolua_S,"dtPoison",dtPoison); + tolua_constant(tolua_S,"dtBurning",dtBurning); + tolua_constant(tolua_S,"dtInFire",dtInFire); + tolua_constant(tolua_S,"dtPlugin",dtPlugin); + tolua_constant(tolua_S,"esOther",esOther); + tolua_constant(tolua_S,"esPrimedTNT",esPrimedTNT); + tolua_constant(tolua_S,"esCreeper",esCreeper); + tolua_constant(tolua_S,"esBed",esBed); + tolua_constant(tolua_S,"esEnderCrystal",esEnderCrystal); + tolua_constant(tolua_S,"esGhastFireball",esGhastFireball); + tolua_constant(tolua_S,"esWitherSkullBlack",esWitherSkullBlack); + tolua_constant(tolua_S,"esWitherSkullBlue",esWitherSkullBlue); + tolua_constant(tolua_S,"esWitherBirth",esWitherBirth); + tolua_constant(tolua_S,"esPlugin",esPlugin); + tolua_function(tolua_S,"BlockStringToType",tolua_AllToLua_BlockStringToType00); + tolua_function(tolua_S,"StringToItem",tolua_AllToLua_StringToItem00); + tolua_function(tolua_S,"ItemToString",tolua_AllToLua_ItemToString00); + tolua_function(tolua_S,"ItemTypeToString",tolua_AllToLua_ItemTypeToString00); + tolua_function(tolua_S,"ItemToFullString",tolua_AllToLua_ItemToFullString00); + tolua_function(tolua_S,"StringToBiome",tolua_AllToLua_StringToBiome00); + tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_StringToMobType00); + tolua_function(tolua_S,"StringToDimension",tolua_AllToLua_StringToDimension00); + tolua_function(tolua_S,"DamageTypeToString",tolua_AllToLua_DamageTypeToString00); + tolua_function(tolua_S,"StringToDamageType",tolua_AllToLua_StringToDamageType00); + tolua_function(tolua_S,"GetIniItemSet",tolua_AllToLua_GetIniItemSet00); + tolua_function(tolua_S,"TrimString",tolua_AllToLua_TrimString00); + tolua_function(tolua_S,"NoCaseCompare",tolua_AllToLua_NoCaseCompare00); + tolua_function(tolua_S,"ReplaceString",tolua_AllToLua_ReplaceString00); + tolua_function(tolua_S,"EscapeString",tolua_AllToLua_EscapeString00); + tolua_function(tolua_S,"StripColorCodes",tolua_AllToLua_StripColorCodes00); + tolua_array(tolua_S,"g_BlockLightValue",tolua_get_AllToLua_g_BlockLightValue,tolua_set_AllToLua_g_BlockLightValue); + tolua_array(tolua_S,"g_BlockSpreadLightFalloff",tolua_get_AllToLua_g_BlockSpreadLightFalloff,tolua_set_AllToLua_g_BlockSpreadLightFalloff); + tolua_array(tolua_S,"g_BlockTransparent",tolua_get_AllToLua_g_BlockTransparent,tolua_set_AllToLua_g_BlockTransparent); + tolua_array(tolua_S,"g_BlockOneHitDig",tolua_get_AllToLua_g_BlockOneHitDig,tolua_set_AllToLua_g_BlockOneHitDig); + tolua_array(tolua_S,"g_BlockPistonBreakable",tolua_get_AllToLua_g_BlockPistonBreakable,tolua_set_AllToLua_g_BlockPistonBreakable); + tolua_array(tolua_S,"g_BlockIsSnowable",tolua_get_AllToLua_g_BlockIsSnowable,tolua_set_AllToLua_g_BlockIsSnowable); + tolua_array(tolua_S,"g_BlockRequiresSpecialTool",tolua_get_AllToLua_g_BlockRequiresSpecialTool,tolua_set_AllToLua_g_BlockRequiresSpecialTool); + tolua_array(tolua_S,"g_BlockIsSolid",tolua_get_AllToLua_g_BlockIsSolid,tolua_set_AllToLua_g_BlockIsSolid); + tolua_array(tolua_S,"g_BlockIsTorchPlaceable",tolua_get_AllToLua_g_BlockIsTorchPlaceable,tolua_set_AllToLua_g_BlockIsTorchPlaceable); + tolua_constant(tolua_S,"MAX_EXPERIENCE_ORB_SIZE",MAX_EXPERIENCE_ORB_SIZE); + tolua_constant(tolua_S,"BLOCK_FACE_NONE",BLOCK_FACE_NONE); + tolua_constant(tolua_S,"BLOCK_FACE_XM",BLOCK_FACE_XM); + tolua_constant(tolua_S,"BLOCK_FACE_XP",BLOCK_FACE_XP); + tolua_constant(tolua_S,"BLOCK_FACE_YM",BLOCK_FACE_YM); + tolua_constant(tolua_S,"BLOCK_FACE_YP",BLOCK_FACE_YP); + tolua_constant(tolua_S,"BLOCK_FACE_ZM",BLOCK_FACE_ZM); + tolua_constant(tolua_S,"BLOCK_FACE_ZP",BLOCK_FACE_ZP); + tolua_constant(tolua_S,"BLOCK_FACE_BOTTOM",BLOCK_FACE_BOTTOM); + tolua_constant(tolua_S,"BLOCK_FACE_TOP",BLOCK_FACE_TOP); + tolua_constant(tolua_S,"BLOCK_FACE_NORTH",BLOCK_FACE_NORTH); + tolua_constant(tolua_S,"BLOCK_FACE_SOUTH",BLOCK_FACE_SOUTH); + tolua_constant(tolua_S,"BLOCK_FACE_WEST",BLOCK_FACE_WEST); + tolua_constant(tolua_S,"BLOCK_FACE_EAST",BLOCK_FACE_EAST); + tolua_constant(tolua_S,"DIG_STATUS_STARTED",DIG_STATUS_STARTED); + tolua_constant(tolua_S,"DIG_STATUS_CANCELLED",DIG_STATUS_CANCELLED); + tolua_constant(tolua_S,"DIG_STATUS_FINISHED",DIG_STATUS_FINISHED); + tolua_constant(tolua_S,"DIG_STATUS_DROP_HELD",DIG_STATUS_DROP_HELD); + tolua_constant(tolua_S,"DIG_STATUS_SHOOT_EAT",DIG_STATUS_SHOOT_EAT); + tolua_constant(tolua_S,"caLeftClick",caLeftClick); + tolua_constant(tolua_S,"caRightClick",caRightClick); + tolua_constant(tolua_S,"caShiftLeftClick",caShiftLeftClick); + tolua_constant(tolua_S,"caShiftRightClick",caShiftRightClick); + tolua_constant(tolua_S,"caNumber1",caNumber1); + tolua_constant(tolua_S,"caNumber2",caNumber2); + tolua_constant(tolua_S,"caNumber3",caNumber3); + tolua_constant(tolua_S,"caNumber4",caNumber4); + tolua_constant(tolua_S,"caNumber5",caNumber5); + tolua_constant(tolua_S,"caNumber6",caNumber6); + tolua_constant(tolua_S,"caNumber7",caNumber7); + tolua_constant(tolua_S,"caNumber8",caNumber8); + tolua_constant(tolua_S,"caNumber9",caNumber9); + tolua_constant(tolua_S,"caMiddleClick",caMiddleClick); + tolua_constant(tolua_S,"caDropKey",caDropKey); + tolua_constant(tolua_S,"caCtrlDropKey",caCtrlDropKey); + tolua_constant(tolua_S,"caLeftClickOutside",caLeftClickOutside); + tolua_constant(tolua_S,"caRightClickOutside",caRightClickOutside); + tolua_constant(tolua_S,"caLeftClickOutsideHoldNothing",caLeftClickOutsideHoldNothing); + tolua_constant(tolua_S,"caRightClickOutsideHoldNothing",caRightClickOutsideHoldNothing); + tolua_constant(tolua_S,"caLeftPaintBegin",caLeftPaintBegin); + tolua_constant(tolua_S,"caRightPaintBegin",caRightPaintBegin); + tolua_constant(tolua_S,"caLeftPaintProgress",caLeftPaintProgress); + tolua_constant(tolua_S,"caRightPaintProgress",caRightPaintProgress); + tolua_constant(tolua_S,"caLeftPaintEnd",caLeftPaintEnd); + tolua_constant(tolua_S,"caRightPaintEnd",caRightPaintEnd); + tolua_constant(tolua_S,"caDblClick",caDblClick); + tolua_constant(tolua_S,"caUnknown",caUnknown); + tolua_constant(tolua_S,"eGameMode_NotSet",eGameMode_NotSet); + tolua_constant(tolua_S,"eGameMode_Survival",eGameMode_Survival); + tolua_constant(tolua_S,"eGameMode_Creative",eGameMode_Creative); + tolua_constant(tolua_S,"eGameMode_Adventure",eGameMode_Adventure); + tolua_constant(tolua_S,"gmNotSet",gmNotSet); + tolua_constant(tolua_S,"gmSurvival",gmSurvival); + tolua_constant(tolua_S,"gmCreative",gmCreative); + tolua_constant(tolua_S,"gmAdventure",gmAdventure); + tolua_constant(tolua_S,"gmMax",gmMax); + tolua_constant(tolua_S,"gmMin",gmMin); + tolua_constant(tolua_S,"eWeather_Sunny",eWeather_Sunny); + tolua_constant(tolua_S,"eWeather_Rain",eWeather_Rain); + tolua_constant(tolua_S,"eWeather_ThunderStorm",eWeather_ThunderStorm); + tolua_constant(tolua_S,"wSunny",wSunny); + tolua_constant(tolua_S,"wRain",wRain); + tolua_constant(tolua_S,"wThunderstorm",wThunderstorm); + tolua_constant(tolua_S,"wStorm",wStorm); + tolua_function(tolua_S,"ClickActionToString",tolua_AllToLua_ClickActionToString00); + tolua_function(tolua_S,"IsValidBlock",tolua_AllToLua_IsValidBlock00); + tolua_function(tolua_S,"IsValidItem",tolua_AllToLua_IsValidItem00); + tolua_function(tolua_S,"AddFaceDirection",tolua_AllToLua_AddFaceDirection00); + tolua_module(tolua_S,"ItemCategory",0); + tolua_beginmodule(tolua_S,"ItemCategory"); + tolua_function(tolua_S,"IsPickaxe",tolua_AllToLua_ItemCategory_IsPickaxe00); + tolua_function(tolua_S,"IsAxe",tolua_AllToLua_ItemCategory_IsAxe00); + tolua_function(tolua_S,"IsSword",tolua_AllToLua_ItemCategory_IsSword00); + tolua_function(tolua_S,"IsHoe",tolua_AllToLua_ItemCategory_IsHoe00); + tolua_function(tolua_S,"IsShovel",tolua_AllToLua_ItemCategory_IsShovel00); + tolua_function(tolua_S,"IsTool",tolua_AllToLua_ItemCategory_IsTool00); + tolua_function(tolua_S,"IsHelmet",tolua_AllToLua_ItemCategory_IsHelmet00); + tolua_function(tolua_S,"IsChestPlate",tolua_AllToLua_ItemCategory_IsChestPlate00); + tolua_function(tolua_S,"IsLeggings",tolua_AllToLua_ItemCategory_IsLeggings00); + tolua_function(tolua_S,"IsBoots",tolua_AllToLua_ItemCategory_IsBoots00); + tolua_function(tolua_S,"IsArmor",tolua_AllToLua_ItemCategory_IsArmor00); + tolua_endmodule(tolua_S); + tolua_function(tolua_S,"GetTime",tolua_AllToLua_GetTime00); + tolua_function(tolua_S,"GetChar",tolua_AllToLua_GetChar00); + tolua_cclass(tolua_S,"cChatColor","cChatColor","",NULL); + tolua_beginmodule(tolua_S,"cChatColor"); + tolua_variable(tolua_S,"Color",tolua_get_cChatColor_Color,NULL); + tolua_variable(tolua_S,"Delimiter",tolua_get_cChatColor_Delimiter,NULL); + tolua_variable(tolua_S,"Black",tolua_get_cChatColor_Black,NULL); + tolua_variable(tolua_S,"Navy",tolua_get_cChatColor_Navy,NULL); + tolua_variable(tolua_S,"Green",tolua_get_cChatColor_Green,NULL); + tolua_variable(tolua_S,"Blue",tolua_get_cChatColor_Blue,NULL); + tolua_variable(tolua_S,"Red",tolua_get_cChatColor_Red,NULL); + tolua_variable(tolua_S,"Purple",tolua_get_cChatColor_Purple,NULL); + tolua_variable(tolua_S,"Gold",tolua_get_cChatColor_Gold,NULL); + tolua_variable(tolua_S,"LightGray",tolua_get_cChatColor_LightGray,NULL); + tolua_variable(tolua_S,"Gray",tolua_get_cChatColor_Gray,NULL); + tolua_variable(tolua_S,"DarkPurple",tolua_get_cChatColor_DarkPurple,NULL); + tolua_variable(tolua_S,"LightGreen",tolua_get_cChatColor_LightGreen,NULL); + tolua_variable(tolua_S,"LightBlue",tolua_get_cChatColor_LightBlue,NULL); + tolua_variable(tolua_S,"Rose",tolua_get_cChatColor_Rose,NULL); + tolua_variable(tolua_S,"LightPurple",tolua_get_cChatColor_LightPurple,NULL); + tolua_variable(tolua_S,"Yellow",tolua_get_cChatColor_Yellow,NULL); + tolua_variable(tolua_S,"White",tolua_get_cChatColor_White,NULL); + tolua_variable(tolua_S,"Random",tolua_get_cChatColor_Random,NULL); + tolua_variable(tolua_S,"Bold",tolua_get_cChatColor_Bold,NULL); + tolua_variable(tolua_S,"Strikethrough",tolua_get_cChatColor_Strikethrough,NULL); + tolua_variable(tolua_S,"Underlined",tolua_get_cChatColor_Underlined,NULL); + tolua_variable(tolua_S,"Italic",tolua_get_cChatColor_Italic,NULL); + tolua_variable(tolua_S,"Plain",tolua_get_cChatColor_Plain,NULL); + tolua_function(tolua_S,"MakeColor",tolua_AllToLua_cChatColor_MakeColor00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cClientHandle","cClientHandle","",NULL); + tolua_beginmodule(tolua_S,"cClientHandle"); + tolua_function(tolua_S,"GetPlayer",tolua_AllToLua_cClientHandle_GetPlayer00); + tolua_function(tolua_S,"Kick",tolua_AllToLua_cClientHandle_Kick00); + tolua_function(tolua_S,"SendBlockChange",tolua_AllToLua_cClientHandle_SendBlockChange00); + tolua_function(tolua_S,"GetUsername",tolua_AllToLua_cClientHandle_GetUsername00); + tolua_function(tolua_S,"SetUsername",tolua_AllToLua_cClientHandle_SetUsername00); + tolua_function(tolua_S,"GetPing",tolua_AllToLua_cClientHandle_GetPing00); + tolua_function(tolua_S,"SetViewDistance",tolua_AllToLua_cClientHandle_SetViewDistance00); + tolua_function(tolua_S,"GetViewDistance",tolua_AllToLua_cClientHandle_GetViewDistance00); + tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cClientHandle_GetUniqueID00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"TakeDamageInfo","TakeDamageInfo","",NULL); + tolua_beginmodule(tolua_S,"TakeDamageInfo"); + tolua_variable(tolua_S,"DamageType",tolua_get_TakeDamageInfo_DamageType,tolua_set_TakeDamageInfo_DamageType); + tolua_variable(tolua_S,"Attacker",tolua_get_TakeDamageInfo_Attacker_ptr,tolua_set_TakeDamageInfo_Attacker_ptr); + tolua_variable(tolua_S,"RawDamage",tolua_get_TakeDamageInfo_RawDamage,tolua_set_TakeDamageInfo_RawDamage); + tolua_variable(tolua_S,"FinalDamage",tolua_get_TakeDamageInfo_FinalDamage,tolua_set_TakeDamageInfo_FinalDamage); + tolua_variable(tolua_S,"Knockback",tolua_get_TakeDamageInfo_Knockback,tolua_set_TakeDamageInfo_Knockback); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cEntity","cEntity","",NULL); + tolua_beginmodule(tolua_S,"cEntity"); + tolua_constant(tolua_S,"etEntity",cEntity::etEntity); + tolua_constant(tolua_S,"etPlayer",cEntity::etPlayer); + tolua_constant(tolua_S,"etPickup",cEntity::etPickup); + tolua_constant(tolua_S,"etMonster",cEntity::etMonster); + tolua_constant(tolua_S,"etFallingBlock",cEntity::etFallingBlock); + tolua_constant(tolua_S,"etMinecart",cEntity::etMinecart); + tolua_constant(tolua_S,"etBoat",cEntity::etBoat); + tolua_constant(tolua_S,"etTNT",cEntity::etTNT); + tolua_constant(tolua_S,"etProjectile",cEntity::etProjectile); + tolua_constant(tolua_S,"etMob",cEntity::etMob); + tolua_function(tolua_S,"GetEntityType",tolua_AllToLua_cEntity_GetEntityType00); + tolua_function(tolua_S,"IsPlayer",tolua_AllToLua_cEntity_IsPlayer00); + tolua_function(tolua_S,"IsPickup",tolua_AllToLua_cEntity_IsPickup00); + tolua_function(tolua_S,"IsMob",tolua_AllToLua_cEntity_IsMob00); + tolua_function(tolua_S,"IsFallingBlock",tolua_AllToLua_cEntity_IsFallingBlock00); + tolua_function(tolua_S,"IsMinecart",tolua_AllToLua_cEntity_IsMinecart00); + tolua_function(tolua_S,"IsBoat",tolua_AllToLua_cEntity_IsBoat00); + tolua_function(tolua_S,"IsTNT",tolua_AllToLua_cEntity_IsTNT00); + tolua_function(tolua_S,"IsProjectile",tolua_AllToLua_cEntity_IsProjectile00); + tolua_function(tolua_S,"IsA",tolua_AllToLua_cEntity_IsA00); + tolua_function(tolua_S,"GetClass",tolua_AllToLua_cEntity_GetClass00); + tolua_function(tolua_S,"GetClassStatic",tolua_AllToLua_cEntity_GetClassStatic00); + tolua_function(tolua_S,"GetParentClass",tolua_AllToLua_cEntity_GetParentClass00); + tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cEntity_GetWorld00); + tolua_function(tolua_S,"GetHeadYaw",tolua_AllToLua_cEntity_GetHeadYaw00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cEntity_GetHeight00); + tolua_function(tolua_S,"GetMass",tolua_AllToLua_cEntity_GetMass00); + tolua_function(tolua_S,"GetPosition",tolua_AllToLua_cEntity_GetPosition00); + tolua_function(tolua_S,"GetPosX",tolua_AllToLua_cEntity_GetPosX00); + tolua_function(tolua_S,"GetPosY",tolua_AllToLua_cEntity_GetPosY00); + tolua_function(tolua_S,"GetPosZ",tolua_AllToLua_cEntity_GetPosZ00); + tolua_function(tolua_S,"GetRot",tolua_AllToLua_cEntity_GetRot00); + tolua_function(tolua_S,"GetRotation",tolua_AllToLua_cEntity_GetRotation00); + tolua_function(tolua_S,"GetYaw",tolua_AllToLua_cEntity_GetYaw00); + tolua_function(tolua_S,"GetPitch",tolua_AllToLua_cEntity_GetPitch00); + tolua_function(tolua_S,"GetRoll",tolua_AllToLua_cEntity_GetRoll00); + tolua_function(tolua_S,"GetLookVector",tolua_AllToLua_cEntity_GetLookVector00); + tolua_function(tolua_S,"GetSpeed",tolua_AllToLua_cEntity_GetSpeed00); + tolua_function(tolua_S,"GetSpeedX",tolua_AllToLua_cEntity_GetSpeedX00); + tolua_function(tolua_S,"GetSpeedY",tolua_AllToLua_cEntity_GetSpeedY00); + tolua_function(tolua_S,"GetSpeedZ",tolua_AllToLua_cEntity_GetSpeedZ00); + tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cEntity_GetWidth00); + tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cEntity_GetChunkX00); + tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cEntity_GetChunkZ00); + tolua_function(tolua_S,"SetHeadYaw",tolua_AllToLua_cEntity_SetHeadYaw00); + tolua_function(tolua_S,"SetHeight",tolua_AllToLua_cEntity_SetHeight00); + tolua_function(tolua_S,"SetMass",tolua_AllToLua_cEntity_SetMass00); + tolua_function(tolua_S,"SetPosX",tolua_AllToLua_cEntity_SetPosX00); + tolua_function(tolua_S,"SetPosY",tolua_AllToLua_cEntity_SetPosY00); + tolua_function(tolua_S,"SetPosZ",tolua_AllToLua_cEntity_SetPosZ00); + tolua_function(tolua_S,"SetPosition",tolua_AllToLua_cEntity_SetPosition00); + tolua_function(tolua_S,"SetPosition",tolua_AllToLua_cEntity_SetPosition01); + tolua_function(tolua_S,"SetRot",tolua_AllToLua_cEntity_SetRot00); + tolua_function(tolua_S,"SetRotation",tolua_AllToLua_cEntity_SetRotation00); + tolua_function(tolua_S,"SetYaw",tolua_AllToLua_cEntity_SetYaw00); + tolua_function(tolua_S,"SetPitch",tolua_AllToLua_cEntity_SetPitch00); + tolua_function(tolua_S,"SetRoll",tolua_AllToLua_cEntity_SetRoll00); + tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed00); + tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed01); + tolua_function(tolua_S,"SetSpeedX",tolua_AllToLua_cEntity_SetSpeedX00); + tolua_function(tolua_S,"SetSpeedY",tolua_AllToLua_cEntity_SetSpeedY00); + tolua_function(tolua_S,"SetSpeedZ",tolua_AllToLua_cEntity_SetSpeedZ00); + tolua_function(tolua_S,"SetWidth",tolua_AllToLua_cEntity_SetWidth00); + tolua_function(tolua_S,"AddPosX",tolua_AllToLua_cEntity_AddPosX00); + tolua_function(tolua_S,"AddPosY",tolua_AllToLua_cEntity_AddPosY00); + tolua_function(tolua_S,"AddPosZ",tolua_AllToLua_cEntity_AddPosZ00); + tolua_function(tolua_S,"AddPosition",tolua_AllToLua_cEntity_AddPosition00); + tolua_function(tolua_S,"AddPosition",tolua_AllToLua_cEntity_AddPosition01); + tolua_function(tolua_S,"AddSpeed",tolua_AllToLua_cEntity_AddSpeed00); + tolua_function(tolua_S,"AddSpeed",tolua_AllToLua_cEntity_AddSpeed01); + tolua_function(tolua_S,"AddSpeedX",tolua_AllToLua_cEntity_AddSpeedX00); + tolua_function(tolua_S,"AddSpeedY",tolua_AllToLua_cEntity_AddSpeedY00); + tolua_function(tolua_S,"AddSpeedZ",tolua_AllToLua_cEntity_AddSpeedZ00); + tolua_function(tolua_S,"SteerVehicle",tolua_AllToLua_cEntity_SteerVehicle00); + tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cEntity_GetUniqueID00); + tolua_function(tolua_S,"IsDestroyed",tolua_AllToLua_cEntity_IsDestroyed00); + tolua_function(tolua_S,"Destroy",tolua_AllToLua_cEntity_Destroy00); + tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage00); + tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage01); + tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage02); + tolua_function(tolua_S,"GetGravity",tolua_AllToLua_cEntity_GetGravity00); + tolua_function(tolua_S,"SetGravity",tolua_AllToLua_cEntity_SetGravity00); + tolua_function(tolua_S,"SetRotationFromSpeed",tolua_AllToLua_cEntity_SetRotationFromSpeed00); + tolua_function(tolua_S,"SetPitchFromSpeed",tolua_AllToLua_cEntity_SetPitchFromSpeed00); + tolua_function(tolua_S,"GetRawDamageAgainst",tolua_AllToLua_cEntity_GetRawDamageAgainst00); + tolua_function(tolua_S,"GetArmorCoverAgainst",tolua_AllToLua_cEntity_GetArmorCoverAgainst00); + tolua_function(tolua_S,"GetKnockbackAmountAgainst",tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00); + tolua_function(tolua_S,"GetEquippedWeapon",tolua_AllToLua_cEntity_GetEquippedWeapon00); + tolua_function(tolua_S,"GetEquippedHelmet",tolua_AllToLua_cEntity_GetEquippedHelmet00); + tolua_function(tolua_S,"GetEquippedChestplate",tolua_AllToLua_cEntity_GetEquippedChestplate00); + tolua_function(tolua_S,"GetEquippedLeggings",tolua_AllToLua_cEntity_GetEquippedLeggings00); + tolua_function(tolua_S,"GetEquippedBoots",tolua_AllToLua_cEntity_GetEquippedBoots00); + tolua_function(tolua_S,"KilledBy",tolua_AllToLua_cEntity_KilledBy00); + tolua_function(tolua_S,"Heal",tolua_AllToLua_cEntity_Heal00); + tolua_function(tolua_S,"GetHealth",tolua_AllToLua_cEntity_GetHealth00); + tolua_function(tolua_S,"SetHealth",tolua_AllToLua_cEntity_SetHealth00); + tolua_function(tolua_S,"SetMaxHealth",tolua_AllToLua_cEntity_SetMaxHealth00); + tolua_function(tolua_S,"GetMaxHealth",tolua_AllToLua_cEntity_GetMaxHealth00); + tolua_function(tolua_S,"StartBurning",tolua_AllToLua_cEntity_StartBurning00); + tolua_function(tolua_S,"StopBurning",tolua_AllToLua_cEntity_StopBurning00); + tolua_function(tolua_S,"TeleportToEntity",tolua_AllToLua_cEntity_TeleportToEntity00); + tolua_function(tolua_S,"TeleportToCoords",tolua_AllToLua_cEntity_TeleportToCoords00); + tolua_function(tolua_S,"IsOnFire",tolua_AllToLua_cEntity_IsOnFire00); + tolua_function(tolua_S,"IsCrouched",tolua_AllToLua_cEntity_IsCrouched00); + tolua_function(tolua_S,"IsRiding",tolua_AllToLua_cEntity_IsRiding00); + tolua_function(tolua_S,"IsSprinting",tolua_AllToLua_cEntity_IsSprinting00); + tolua_function(tolua_S,"IsRclking",tolua_AllToLua_cEntity_IsRclking00); + tolua_function(tolua_S,"IsInvisible",tolua_AllToLua_cEntity_IsInvisible00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cPawn","cPawn","cEntity",NULL); + tolua_beginmodule(tolua_S,"cPawn"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cPlayer","cPlayer","cPawn",NULL); + tolua_beginmodule(tolua_S,"cPlayer"); + tolua_constant(tolua_S,"MAX_HEALTH",cPlayer::MAX_HEALTH); + tolua_constant(tolua_S,"MAX_FOOD_LEVEL",cPlayer::MAX_FOOD_LEVEL); + tolua_constant(tolua_S,"EATING_TICKS",cPlayer::EATING_TICKS); + tolua_constant(tolua_S,"MAX_AIR_LEVEL",cPlayer::MAX_AIR_LEVEL); + tolua_constant(tolua_S,"DROWNING_TICKS",cPlayer::DROWNING_TICKS); + tolua_function(tolua_S,"SetExperience",tolua_AllToLua_cPlayer_SetExperience00); + tolua_function(tolua_S,"AddExperience",tolua_AllToLua_cPlayer_AddExperience00); + tolua_function(tolua_S,"XpGetTotal",tolua_AllToLua_cPlayer_XpGetTotal00); + tolua_function(tolua_S,"XpGetLevel",tolua_AllToLua_cPlayer_XpGetLevel00); + tolua_function(tolua_S,"XpGetPercentage",tolua_AllToLua_cPlayer_XpGetPercentage00); + tolua_function(tolua_S,"GetEyeHeight",tolua_AllToLua_cPlayer_GetEyeHeight00); + tolua_function(tolua_S,"GetEyePosition",tolua_AllToLua_cPlayer_GetEyePosition00); + tolua_function(tolua_S,"IsOnGround",tolua_AllToLua_cPlayer_IsOnGround00); + tolua_function(tolua_S,"GetStance",tolua_AllToLua_cPlayer_GetStance00); + tolua_function(tolua_S,"GetInventory",tolua_AllToLua_cPlayer_GetInventory00); + tolua_function(tolua_S,"GetEquippedItem",tolua_AllToLua_cPlayer_GetEquippedItem00); + tolua_function(tolua_S,"GetThrowStartPos",tolua_AllToLua_cPlayer_GetThrowStartPos00); + tolua_function(tolua_S,"GetThrowSpeed",tolua_AllToLua_cPlayer_GetThrowSpeed00); + tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cPlayer_GetGameMode00); + tolua_function(tolua_S,"GetEffectiveGameMode",tolua_AllToLua_cPlayer_GetEffectiveGameMode00); + tolua_function(tolua_S,"SetGameMode",tolua_AllToLua_cPlayer_SetGameMode00); + tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cPlayer_IsGameModeCreative00); + tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cPlayer_IsGameModeSurvival00); + tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cPlayer_IsGameModeAdventure00); + tolua_function(tolua_S,"GetIP",tolua_AllToLua_cPlayer_GetIP00); + tolua_function(tolua_S,"MoveTo",tolua_AllToLua_cPlayer_MoveTo00); + tolua_function(tolua_S,"GetWindow",tolua_AllToLua_cPlayer_GetWindow00); + tolua_function(tolua_S,"CloseWindow",tolua_AllToLua_cPlayer_CloseWindow00); + tolua_function(tolua_S,"CloseWindowIfID",tolua_AllToLua_cPlayer_CloseWindowIfID00); + tolua_function(tolua_S,"GetClientHandle",tolua_AllToLua_cPlayer_GetClientHandle00); + tolua_function(tolua_S,"SendMessage",tolua_AllToLua_cPlayer_SendMessage00); + tolua_function(tolua_S,"GetName",tolua_AllToLua_cPlayer_GetName00); + tolua_function(tolua_S,"SetName",tolua_AllToLua_cPlayer_SetName00); + tolua_function(tolua_S,"AddToGroup",tolua_AllToLua_cPlayer_AddToGroup00); + tolua_function(tolua_S,"RemoveFromGroup",tolua_AllToLua_cPlayer_RemoveFromGroup00); + tolua_function(tolua_S,"CanUseCommand",tolua_AllToLua_cPlayer_CanUseCommand00); + tolua_function(tolua_S,"HasPermission",tolua_AllToLua_cPlayer_HasPermission00); + tolua_function(tolua_S,"IsInGroup",tolua_AllToLua_cPlayer_IsInGroup00); + tolua_function(tolua_S,"GetColor",tolua_AllToLua_cPlayer_GetColor00); + tolua_function(tolua_S,"TossItem",tolua_AllToLua_cPlayer_TossItem00); + tolua_function(tolua_S,"Heal",tolua_AllToLua_cPlayer_Heal00); + tolua_function(tolua_S,"GetFoodLevel",tolua_AllToLua_cPlayer_GetFoodLevel00); + tolua_function(tolua_S,"GetFoodSaturationLevel",tolua_AllToLua_cPlayer_GetFoodSaturationLevel00); + tolua_function(tolua_S,"GetFoodTickTimer",tolua_AllToLua_cPlayer_GetFoodTickTimer00); + tolua_function(tolua_S,"GetFoodExhaustionLevel",tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00); + tolua_function(tolua_S,"GetFoodPoisonedTicksRemaining",tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00); + tolua_function(tolua_S,"GetAirLevel",tolua_AllToLua_cPlayer_GetAirLevel00); + tolua_function(tolua_S,"IsSatiated",tolua_AllToLua_cPlayer_IsSatiated00); + tolua_function(tolua_S,"SetFoodLevel",tolua_AllToLua_cPlayer_SetFoodLevel00); + tolua_function(tolua_S,"SetFoodSaturationLevel",tolua_AllToLua_cPlayer_SetFoodSaturationLevel00); + tolua_function(tolua_S,"SetFoodTickTimer",tolua_AllToLua_cPlayer_SetFoodTickTimer00); + tolua_function(tolua_S,"SetFoodExhaustionLevel",tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00); + tolua_function(tolua_S,"SetFoodPoisonedTicksRemaining",tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00); + tolua_function(tolua_S,"Feed",tolua_AllToLua_cPlayer_Feed00); + tolua_function(tolua_S,"AddFoodExhaustion",tolua_AllToLua_cPlayer_AddFoodExhaustion00); + tolua_function(tolua_S,"FoodPoison",tolua_AllToLua_cPlayer_FoodPoison00); + tolua_function(tolua_S,"IsEating",tolua_AllToLua_cPlayer_IsEating00); + tolua_function(tolua_S,"Respawn",tolua_AllToLua_cPlayer_Respawn00); + tolua_function(tolua_S,"SetVisible",tolua_AllToLua_cPlayer_SetVisible00); + tolua_function(tolua_S,"IsVisible",tolua_AllToLua_cPlayer_IsVisible00); + tolua_function(tolua_S,"MoveToWorld",tolua_AllToLua_cPlayer_MoveToWorld00); + tolua_function(tolua_S,"LoadPermissionsFromDisk",tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00); + tolua_function(tolua_S,"GetMaxSpeed",tolua_AllToLua_cPlayer_GetMaxSpeed00); + tolua_function(tolua_S,"GetNormalMaxSpeed",tolua_AllToLua_cPlayer_GetNormalMaxSpeed00); + tolua_function(tolua_S,"GetSprintingMaxSpeed",tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00); + tolua_function(tolua_S,"SetNormalMaxSpeed",tolua_AllToLua_cPlayer_SetNormalMaxSpeed00); + tolua_function(tolua_S,"SetSprintingMaxSpeed",tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00); + tolua_function(tolua_S,"SetCrouch",tolua_AllToLua_cPlayer_SetCrouch00); + tolua_function(tolua_S,"SetSprint",tolua_AllToLua_cPlayer_SetSprint00); + tolua_function(tolua_S,"IsSwimming",tolua_AllToLua_cPlayer_IsSwimming00); + tolua_function(tolua_S,"IsSubmerged",tolua_AllToLua_cPlayer_IsSubmerged00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cPickup","cPickup","cEntity",tolua_collect_cPickup); + #else + tolua_cclass(tolua_S,"cPickup","cPickup","cEntity",NULL); + #endif + tolua_beginmodule(tolua_S,"cPickup"); + tolua_function(tolua_S,"new",tolua_AllToLua_cPickup_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cPickup_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cPickup_new00_local); + tolua_function(tolua_S,"GetItem",tolua_AllToLua_cPickup_GetItem00); + tolua_function(tolua_S,"CollectedBy",tolua_AllToLua_cPickup_CollectedBy00); + tolua_function(tolua_S,"GetAge",tolua_AllToLua_cPickup_GetAge00); + tolua_function(tolua_S,"IsCollected",tolua_AllToLua_cPickup_IsCollected00); + tolua_function(tolua_S,"IsPlayerCreated",tolua_AllToLua_cPickup_IsPlayerCreated00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cProjectileEntity","cProjectileEntity","cEntity",NULL); + tolua_beginmodule(tolua_S,"cProjectileEntity"); + tolua_constant(tolua_S,"pkArrow",cProjectileEntity::pkArrow); + tolua_constant(tolua_S,"pkSnowball",cProjectileEntity::pkSnowball); + tolua_constant(tolua_S,"pkEgg",cProjectileEntity::pkEgg); + tolua_constant(tolua_S,"pkGhastFireball",cProjectileEntity::pkGhastFireball); + tolua_constant(tolua_S,"pkFireCharge",cProjectileEntity::pkFireCharge); + tolua_constant(tolua_S,"pkEnderPearl",cProjectileEntity::pkEnderPearl); + tolua_constant(tolua_S,"pkExpBottle",cProjectileEntity::pkExpBottle); + tolua_constant(tolua_S,"pkSplashPotion",cProjectileEntity::pkSplashPotion); + tolua_constant(tolua_S,"pkWitherSkull",cProjectileEntity::pkWitherSkull); + tolua_constant(tolua_S,"pkFishingFloat",cProjectileEntity::pkFishingFloat); + tolua_function(tolua_S,"GetProjectileKind",tolua_AllToLua_cProjectileEntity_GetProjectileKind00); + tolua_function(tolua_S,"GetCreator",tolua_AllToLua_cProjectileEntity_GetCreator00); + tolua_function(tolua_S,"GetMCAClassName",tolua_AllToLua_cProjectileEntity_GetMCAClassName00); + tolua_function(tolua_S,"IsInGround",tolua_AllToLua_cProjectileEntity_IsInGround00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cArrowEntity","cArrowEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cArrowEntity"); + tolua_constant(tolua_S,"psNoPickup",cArrowEntity::psNoPickup); + tolua_constant(tolua_S,"psInSurvivalOrCreative",cArrowEntity::psInSurvivalOrCreative); + tolua_constant(tolua_S,"psInCreative",cArrowEntity::psInCreative); + tolua_function(tolua_S,"GetPickupState",tolua_AllToLua_cArrowEntity_GetPickupState00); + tolua_function(tolua_S,"SetPickupState",tolua_AllToLua_cArrowEntity_SetPickupState00); + tolua_function(tolua_S,"GetDamageCoeff",tolua_AllToLua_cArrowEntity_GetDamageCoeff00); + tolua_function(tolua_S,"SetDamageCoeff",tolua_AllToLua_cArrowEntity_SetDamageCoeff00); + tolua_function(tolua_S,"CanPickup",tolua_AllToLua_cArrowEntity_CanPickup00); + tolua_function(tolua_S,"IsCritical",tolua_AllToLua_cArrowEntity_IsCritical00); + tolua_function(tolua_S,"SetIsCritical",tolua_AllToLua_cArrowEntity_SetIsCritical00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cThrownEggEntity","cThrownEggEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cThrownEggEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cThrownEnderPearlEntity","cThrownEnderPearlEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cThrownEnderPearlEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cThrownSnowballEntity","cThrownSnowballEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cThrownSnowballEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cGhastFireballEntity","cGhastFireballEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cGhastFireballEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cFireChargeEntity","cFireChargeEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cFireChargeEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cPluginManager","cPluginManager","",NULL); + tolua_beginmodule(tolua_S,"cPluginManager"); + tolua_constant(tolua_S,"HOOK_BLOCK_TO_PICKUPS",cPluginManager::HOOK_BLOCK_TO_PICKUPS); + tolua_constant(tolua_S,"HOOK_CHAT",cPluginManager::HOOK_CHAT); + tolua_constant(tolua_S,"HOOK_CHUNK_AVAILABLE",cPluginManager::HOOK_CHUNK_AVAILABLE); + tolua_constant(tolua_S,"HOOK_CHUNK_GENERATED",cPluginManager::HOOK_CHUNK_GENERATED); + tolua_constant(tolua_S,"HOOK_CHUNK_GENERATING",cPluginManager::HOOK_CHUNK_GENERATING); + tolua_constant(tolua_S,"HOOK_CHUNK_UNLOADED",cPluginManager::HOOK_CHUNK_UNLOADED); + tolua_constant(tolua_S,"HOOK_CHUNK_UNLOADING",cPluginManager::HOOK_CHUNK_UNLOADING); + tolua_constant(tolua_S,"HOOK_COLLECTING_PICKUP",cPluginManager::HOOK_COLLECTING_PICKUP); + tolua_constant(tolua_S,"HOOK_CRAFTING_NO_RECIPE",cPluginManager::HOOK_CRAFTING_NO_RECIPE); + tolua_constant(tolua_S,"HOOK_DISCONNECT",cPluginManager::HOOK_DISCONNECT); + tolua_constant(tolua_S,"HOOK_EXECUTE_COMMAND",cPluginManager::HOOK_EXECUTE_COMMAND); + tolua_constant(tolua_S,"HOOK_EXPLODED",cPluginManager::HOOK_EXPLODED); + tolua_constant(tolua_S,"HOOK_EXPLODING",cPluginManager::HOOK_EXPLODING); + tolua_constant(tolua_S,"HOOK_HANDSHAKE",cPluginManager::HOOK_HANDSHAKE); + tolua_constant(tolua_S,"HOOK_HOPPER_PULLING_ITEM",cPluginManager::HOOK_HOPPER_PULLING_ITEM); + tolua_constant(tolua_S,"HOOK_HOPPER_PUSHING_ITEM",cPluginManager::HOOK_HOPPER_PUSHING_ITEM); + tolua_constant(tolua_S,"HOOK_KILLING",cPluginManager::HOOK_KILLING); + tolua_constant(tolua_S,"HOOK_LOGIN",cPluginManager::HOOK_LOGIN); + tolua_constant(tolua_S,"HOOK_PLAYER_ANIMATION",cPluginManager::HOOK_PLAYER_ANIMATION); + tolua_constant(tolua_S,"HOOK_PLAYER_BREAKING_BLOCK",cPluginManager::HOOK_PLAYER_BREAKING_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_BROKEN_BLOCK",cPluginManager::HOOK_PLAYER_BROKEN_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_EATING",cPluginManager::HOOK_PLAYER_EATING); + tolua_constant(tolua_S,"HOOK_PLAYER_JOINED",cPluginManager::HOOK_PLAYER_JOINED); + tolua_constant(tolua_S,"HOOK_PLAYER_LEFT_CLICK",cPluginManager::HOOK_PLAYER_LEFT_CLICK); + tolua_constant(tolua_S,"HOOK_PLAYER_MOVING",cPluginManager::HOOK_PLAYER_MOVING); + tolua_constant(tolua_S,"HOOK_PLAYER_PLACED_BLOCK",cPluginManager::HOOK_PLAYER_PLACED_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_PLACING_BLOCK",cPluginManager::HOOK_PLAYER_PLACING_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICK",cPluginManager::HOOK_PLAYER_RIGHT_CLICK); + tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICKING_ENTITY",cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY); + tolua_constant(tolua_S,"HOOK_PLAYER_SHOOTING",cPluginManager::HOOK_PLAYER_SHOOTING); + tolua_constant(tolua_S,"HOOK_PLAYER_SPAWNED",cPluginManager::HOOK_PLAYER_SPAWNED); + tolua_constant(tolua_S,"HOOK_PLAYER_TOSSING_ITEM",cPluginManager::HOOK_PLAYER_TOSSING_ITEM); + tolua_constant(tolua_S,"HOOK_PLAYER_USED_BLOCK",cPluginManager::HOOK_PLAYER_USED_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_USED_ITEM",cPluginManager::HOOK_PLAYER_USED_ITEM); + tolua_constant(tolua_S,"HOOK_PLAYER_USING_BLOCK",cPluginManager::HOOK_PLAYER_USING_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_USING_ITEM",cPluginManager::HOOK_PLAYER_USING_ITEM); + tolua_constant(tolua_S,"HOOK_POST_CRAFTING",cPluginManager::HOOK_POST_CRAFTING); + tolua_constant(tolua_S,"HOOK_PRE_CRAFTING",cPluginManager::HOOK_PRE_CRAFTING); + tolua_constant(tolua_S,"HOOK_SPAWNED_ENTITY",cPluginManager::HOOK_SPAWNED_ENTITY); + tolua_constant(tolua_S,"HOOK_SPAWNED_MONSTER",cPluginManager::HOOK_SPAWNED_MONSTER); + tolua_constant(tolua_S,"HOOK_SPAWNING_ENTITY",cPluginManager::HOOK_SPAWNING_ENTITY); + tolua_constant(tolua_S,"HOOK_SPAWNING_MONSTER",cPluginManager::HOOK_SPAWNING_MONSTER); + tolua_constant(tolua_S,"HOOK_TAKE_DAMAGE",cPluginManager::HOOK_TAKE_DAMAGE); + tolua_constant(tolua_S,"HOOK_TICK",cPluginManager::HOOK_TICK); + tolua_constant(tolua_S,"HOOK_UPDATED_SIGN",cPluginManager::HOOK_UPDATED_SIGN); + tolua_constant(tolua_S,"HOOK_UPDATING_SIGN",cPluginManager::HOOK_UPDATING_SIGN); + tolua_constant(tolua_S,"HOOK_WEATHER_CHANGED",cPluginManager::HOOK_WEATHER_CHANGED); + tolua_constant(tolua_S,"HOOK_WEATHER_CHANGING",cPluginManager::HOOK_WEATHER_CHANGING); + tolua_constant(tolua_S,"HOOK_WORLD_TICK",cPluginManager::HOOK_WORLD_TICK); + tolua_constant(tolua_S,"HOOK_NUM_HOOKS",cPluginManager::HOOK_NUM_HOOKS); + tolua_constant(tolua_S,"HOOK_MAX",cPluginManager::HOOK_MAX); + tolua_function(tolua_S,"Get",tolua_AllToLua_cPluginManager_Get00); + tolua_function(tolua_S,"GetPlugin",tolua_AllToLua_cPluginManager_GetPlugin00); + tolua_function(tolua_S,"FindPlugins",tolua_AllToLua_cPluginManager_FindPlugins00); + tolua_function(tolua_S,"ReloadPlugins",tolua_AllToLua_cPluginManager_ReloadPlugins00); + tolua_function(tolua_S,"GetNumPlugins",tolua_AllToLua_cPluginManager_GetNumPlugins00); + tolua_function(tolua_S,"DisablePlugin",tolua_AllToLua_cPluginManager_DisablePlugin00); + tolua_function(tolua_S,"LoadPlugin",tolua_AllToLua_cPluginManager_LoadPlugin00); + tolua_function(tolua_S,"IsCommandBound",tolua_AllToLua_cPluginManager_IsCommandBound00); + tolua_function(tolua_S,"GetCommandPermission",tolua_AllToLua_cPluginManager_GetCommandPermission00); + tolua_function(tolua_S,"ExecuteCommand",tolua_AllToLua_cPluginManager_ExecuteCommand00); + tolua_function(tolua_S,"ForceExecuteCommand",tolua_AllToLua_cPluginManager_ForceExecuteCommand00); + tolua_function(tolua_S,"IsConsoleCommandBound",tolua_AllToLua_cPluginManager_IsConsoleCommandBound00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cPlugin","cPlugin","",NULL); + tolua_beginmodule(tolua_S,"cPlugin"); + tolua_function(tolua_S,"GetName",tolua_AllToLua_cPlugin_GetName00); + tolua_function(tolua_S,"SetName",tolua_AllToLua_cPlugin_SetName00); + tolua_function(tolua_S,"GetVersion",tolua_AllToLua_cPlugin_GetVersion00); + tolua_function(tolua_S,"SetVersion",tolua_AllToLua_cPlugin_SetVersion00); + tolua_function(tolua_S,"GetDirectory",tolua_AllToLua_cPlugin_GetDirectory00); + tolua_function(tolua_S,"GetLocalDirectory",tolua_AllToLua_cPlugin_GetLocalDirectory00); + tolua_function(tolua_S,"GetLocalFolder",tolua_AllToLua_cPlugin_GetLocalFolder00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cPluginLua","cPluginLua","cPlugin",NULL); + tolua_beginmodule(tolua_S,"cPluginLua"); + tolua_variable(tolua_S,"__cWebPlugin__",tolua_get_cPluginLua___cWebPlugin__,NULL); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cServer","cServer","",NULL); + tolua_beginmodule(tolua_S,"cServer"); + tolua_function(tolua_S,"GetDescription",tolua_AllToLua_cServer_GetDescription00); + tolua_function(tolua_S,"GetMaxPlayers",tolua_AllToLua_cServer_GetMaxPlayers00); + tolua_function(tolua_S,"GetNumPlayers",tolua_AllToLua_cServer_GetNumPlayers00); + tolua_function(tolua_S,"SetMaxPlayers",tolua_AllToLua_cServer_SetMaxPlayers00); + tolua_function(tolua_S,"IsHardcore",tolua_AllToLua_cServer_IsHardcore00); + tolua_function(tolua_S,"GetServerID",tolua_AllToLua_cServer_GetServerID00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cWorld","cWorld","",NULL); + tolua_beginmodule(tolua_S,"cWorld"); + tolua_function(tolua_S,"GetTicksUntilWeatherChange",tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00); + tolua_function(tolua_S,"GetWorldAge",tolua_AllToLua_cWorld_GetWorldAge00); + tolua_function(tolua_S,"GetTimeOfDay",tolua_AllToLua_cWorld_GetTimeOfDay00); + tolua_function(tolua_S,"SetTicksUntilWeatherChange",tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00); + tolua_function(tolua_S,"SetTimeOfDay",tolua_AllToLua_cWorld_SetTimeOfDay00); + tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cWorld_GetGameMode00); + tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cWorld_IsGameModeCreative00); + tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cWorld_IsGameModeSurvival00); + tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cWorld_IsGameModeAdventure00); + tolua_function(tolua_S,"IsPVPEnabled",tolua_AllToLua_cWorld_IsPVPEnabled00); + tolua_function(tolua_S,"IsDeepSnowEnabled",tolua_AllToLua_cWorld_IsDeepSnowEnabled00); + tolua_function(tolua_S,"GetDimension",tolua_AllToLua_cWorld_GetDimension00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cWorld_GetHeight00); + tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cWorld_BroadcastChat00); + tolua_function(tolua_S,"BroadcastSoundEffect",tolua_AllToLua_cWorld_BroadcastSoundEffect00); + tolua_function(tolua_S,"BroadcastSoundParticleEffect",tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00); + tolua_function(tolua_S,"UnloadUnusedChunks",tolua_AllToLua_cWorld_UnloadUnusedChunks00); + tolua_function(tolua_S,"RegenerateChunk",tolua_AllToLua_cWorld_RegenerateChunk00); + tolua_function(tolua_S,"GenerateChunk",tolua_AllToLua_cWorld_GenerateChunk00); + tolua_function(tolua_S,"SetBlock",tolua_AllToLua_cWorld_SetBlock00); + tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock00); + tolua_function(tolua_S,"QueueSetBlock",tolua_AllToLua_cWorld_QueueSetBlock00); + tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock00); + tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cWorld_GetBlockMeta00); + tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta00); + tolua_function(tolua_S,"GetBlockSkyLight",tolua_AllToLua_cWorld_GetBlockSkyLight00); + tolua_function(tolua_S,"GetBlockBlockLight",tolua_AllToLua_cWorld_GetBlockBlockLight00); + tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock01); + tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock01); + tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cWorld_GetBlockMeta01); + tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta01); + tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups00); + tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups01); + tolua_function(tolua_S,"SpawnPrimedTNT",tolua_AllToLua_cWorld_SpawnPrimedTNT00); + tolua_function(tolua_S,"DigBlock",tolua_AllToLua_cWorld_DigBlock00); + tolua_function(tolua_S,"SendBlockTo",tolua_AllToLua_cWorld_SendBlockTo00); + tolua_function(tolua_S,"GetSpawnX",tolua_AllToLua_cWorld_GetSpawnX00); + tolua_function(tolua_S,"GetSpawnY",tolua_AllToLua_cWorld_GetSpawnY00); + tolua_function(tolua_S,"GetSpawnZ",tolua_AllToLua_cWorld_GetSpawnZ00); + tolua_function(tolua_S,"WakeUpSimulators",tolua_AllToLua_cWorld_WakeUpSimulators00); + tolua_function(tolua_S,"WakeUpSimulatorsInArea",tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00); + tolua_function(tolua_S,"DoExplosionAt",tolua_AllToLua_cWorld_DoExplosionAt00); + tolua_function(tolua_S,"UseBlockEntity",tolua_AllToLua_cWorld_UseBlockEntity00); + tolua_function(tolua_S,"GrowTree",tolua_AllToLua_cWorld_GrowTree00); + tolua_function(tolua_S,"GrowTreeFromSapling",tolua_AllToLua_cWorld_GrowTreeFromSapling00); + tolua_function(tolua_S,"GrowTreeByBiome",tolua_AllToLua_cWorld_GrowTreeByBiome00); + tolua_function(tolua_S,"GrowRipePlant",tolua_AllToLua_cWorld_GrowRipePlant00); + tolua_function(tolua_S,"GrowCactus",tolua_AllToLua_cWorld_GrowCactus00); + tolua_function(tolua_S,"GrowMelonPumpkin",tolua_AllToLua_cWorld_GrowMelonPumpkin00); + tolua_function(tolua_S,"GrowSugarcane",tolua_AllToLua_cWorld_GrowSugarcane00); + tolua_function(tolua_S,"GetBiomeAt",tolua_AllToLua_cWorld_GetBiomeAt00); + tolua_function(tolua_S,"GetName",tolua_AllToLua_cWorld_GetName00); + tolua_function(tolua_S,"GetIniFileName",tolua_AllToLua_cWorld_GetIniFileName00); + tolua_function(tolua_S,"QueueSaveAllChunks",tolua_AllToLua_cWorld_QueueSaveAllChunks00); + tolua_function(tolua_S,"GetNumChunks",tolua_AllToLua_cWorld_GetNumChunks00); + tolua_function(tolua_S,"GetGeneratorQueueLength",tolua_AllToLua_cWorld_GetGeneratorQueueLength00); + tolua_function(tolua_S,"GetLightingQueueLength",tolua_AllToLua_cWorld_GetLightingQueueLength00); + tolua_function(tolua_S,"GetStorageLoadQueueLength",tolua_AllToLua_cWorld_GetStorageLoadQueueLength00); + tolua_function(tolua_S,"GetStorageSaveQueueLength",tolua_AllToLua_cWorld_GetStorageSaveQueueLength00); + tolua_function(tolua_S,"QueueBlockForTick",tolua_AllToLua_cWorld_QueueBlockForTick00); + tolua_function(tolua_S,"CastThunderbolt",tolua_AllToLua_cWorld_CastThunderbolt00); + tolua_function(tolua_S,"SetWeather",tolua_AllToLua_cWorld_SetWeather00); + tolua_function(tolua_S,"ChangeWeather",tolua_AllToLua_cWorld_ChangeWeather00); + tolua_function(tolua_S,"GetWeather",tolua_AllToLua_cWorld_GetWeather00); + tolua_function(tolua_S,"IsWeatherSunny",tolua_AllToLua_cWorld_IsWeatherSunny00); + tolua_function(tolua_S,"IsWeatherRain",tolua_AllToLua_cWorld_IsWeatherRain00); + tolua_function(tolua_S,"IsWeatherStorm",tolua_AllToLua_cWorld_IsWeatherStorm00); + tolua_function(tolua_S,"IsWeatherWet",tolua_AllToLua_cWorld_IsWeatherWet00); + tolua_function(tolua_S,"SetNextBlockTick",tolua_AllToLua_cWorld_SetNextBlockTick00); + tolua_function(tolua_S,"GetMaxSugarcaneHeight",tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00); + tolua_function(tolua_S,"GetMaxCactusHeight",tolua_AllToLua_cWorld_GetMaxCactusHeight00); + tolua_function(tolua_S,"IsBlockDirectlyWatered",tolua_AllToLua_cWorld_IsBlockDirectlyWatered00); + tolua_function(tolua_S,"SpawnMob",tolua_AllToLua_cWorld_SpawnMob00); + tolua_function(tolua_S,"CreateProjectile",tolua_AllToLua_cWorld_CreateProjectile00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cInventory","cInventory","cItemGrid::cListener",NULL); + tolua_beginmodule(tolua_S,"cInventory"); + tolua_constant(tolua_S,"invArmorCount",cInventory::invArmorCount); + tolua_constant(tolua_S,"invInventoryCount",cInventory::invInventoryCount); + tolua_constant(tolua_S,"invHotbarCount",cInventory::invHotbarCount); + tolua_constant(tolua_S,"invArmorOffset",cInventory::invArmorOffset); + tolua_constant(tolua_S,"invInventoryOffset",cInventory::invInventoryOffset); + tolua_constant(tolua_S,"invHotbarOffset",cInventory::invHotbarOffset); + tolua_constant(tolua_S,"invNumSlots",cInventory::invNumSlots); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cInventory_Clear00); + tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cInventory_HowManyCanFit00); + tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cInventory_HowManyCanFit01); + tolua_function(tolua_S,"AddItem",tolua_AllToLua_cInventory_AddItem00); + tolua_function(tolua_S,"AddItems",tolua_AllToLua_cInventory_AddItems00); + tolua_function(tolua_S,"RemoveOneEquippedItem",tolua_AllToLua_cInventory_RemoveOneEquippedItem00); + tolua_function(tolua_S,"HowManyItems",tolua_AllToLua_cInventory_HowManyItems00); + tolua_function(tolua_S,"HasItems",tolua_AllToLua_cInventory_HasItems00); + tolua_function(tolua_S,"GetArmorGrid",tolua_AllToLua_cInventory_GetArmorGrid00); + tolua_function(tolua_S,"GetInventoryGrid",tolua_AllToLua_cInventory_GetInventoryGrid00); + tolua_function(tolua_S,"GetHotbarGrid",tolua_AllToLua_cInventory_GetHotbarGrid00); + tolua_function(tolua_S,"GetOwner",tolua_AllToLua_cInventory_GetOwner00); + tolua_function(tolua_S,"CopyToItems",tolua_AllToLua_cInventory_CopyToItems00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cInventory_GetSlot00); + tolua_function(tolua_S,"GetArmorSlot",tolua_AllToLua_cInventory_GetArmorSlot00); + tolua_function(tolua_S,"GetInventorySlot",tolua_AllToLua_cInventory_GetInventorySlot00); + tolua_function(tolua_S,"GetHotbarSlot",tolua_AllToLua_cInventory_GetHotbarSlot00); + tolua_function(tolua_S,"GetEquippedItem",tolua_AllToLua_cInventory_GetEquippedItem00); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cInventory_SetSlot00); + tolua_function(tolua_S,"SetArmorSlot",tolua_AllToLua_cInventory_SetArmorSlot00); + tolua_function(tolua_S,"SetInventorySlot",tolua_AllToLua_cInventory_SetInventorySlot00); + tolua_function(tolua_S,"SetHotbarSlot",tolua_AllToLua_cInventory_SetHotbarSlot00); + tolua_function(tolua_S,"SetEquippedSlotNum",tolua_AllToLua_cInventory_SetEquippedSlotNum00); + tolua_function(tolua_S,"GetEquippedSlotNum",tolua_AllToLua_cInventory_GetEquippedSlotNum00); + tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cInventory_ChangeSlotCount00); + tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cInventory_DamageItem00); + tolua_function(tolua_S,"DamageEquippedItem",tolua_AllToLua_cInventory_DamageEquippedItem00); + tolua_function(tolua_S,"GetEquippedHelmet",tolua_AllToLua_cInventory_GetEquippedHelmet00); + tolua_function(tolua_S,"GetEquippedChestplate",tolua_AllToLua_cInventory_GetEquippedChestplate00); + tolua_function(tolua_S,"GetEquippedLeggings",tolua_AllToLua_cInventory_GetEquippedLeggings00); + tolua_function(tolua_S,"GetEquippedBoots",tolua_AllToLua_cInventory_GetEquippedBoots00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cEnchantments","cEnchantments","",tolua_collect_cEnchantments); + #else + tolua_cclass(tolua_S,"cEnchantments","cEnchantments","",NULL); + #endif + tolua_beginmodule(tolua_S,"cEnchantments"); + tolua_constant(tolua_S,"enchProtection",cEnchantments::enchProtection); + tolua_constant(tolua_S,"enchFireProtection",cEnchantments::enchFireProtection); + tolua_constant(tolua_S,"enchFeatherFalling",cEnchantments::enchFeatherFalling); + tolua_constant(tolua_S,"enchBlastProtection",cEnchantments::enchBlastProtection); + tolua_constant(tolua_S,"enchProjectileProtection",cEnchantments::enchProjectileProtection); + tolua_constant(tolua_S,"enchRespiration",cEnchantments::enchRespiration); + tolua_constant(tolua_S,"enchAquaAffinity",cEnchantments::enchAquaAffinity); + tolua_constant(tolua_S,"enchThorns",cEnchantments::enchThorns); + tolua_constant(tolua_S,"enchSharpness",cEnchantments::enchSharpness); + tolua_constant(tolua_S,"enchSmite",cEnchantments::enchSmite); + tolua_constant(tolua_S,"enchBaneOfArthropods",cEnchantments::enchBaneOfArthropods); + tolua_constant(tolua_S,"enchKnockback",cEnchantments::enchKnockback); + tolua_constant(tolua_S,"enchFireAspect",cEnchantments::enchFireAspect); + tolua_constant(tolua_S,"enchLooting",cEnchantments::enchLooting); + tolua_constant(tolua_S,"enchEfficiency",cEnchantments::enchEfficiency); + tolua_constant(tolua_S,"enchSilkTouch",cEnchantments::enchSilkTouch); + tolua_constant(tolua_S,"enchUnbreaking",cEnchantments::enchUnbreaking); + tolua_constant(tolua_S,"enchFortune",cEnchantments::enchFortune); + tolua_constant(tolua_S,"enchPower",cEnchantments::enchPower); + tolua_constant(tolua_S,"enchPunch",cEnchantments::enchPunch); + tolua_constant(tolua_S,"enchFlame",cEnchantments::enchFlame); + tolua_constant(tolua_S,"enchInfinity",cEnchantments::enchInfinity); + tolua_constant(tolua_S,"enchLuckOfTheSea",cEnchantments::enchLuckOfTheSea); + tolua_constant(tolua_S,"enchLure",cEnchantments::enchLure); + tolua_function(tolua_S,"new",tolua_AllToLua_cEnchantments_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cEnchantments_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cEnchantments_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cEnchantments_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cEnchantments_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cEnchantments_new01_local); + tolua_function(tolua_S,"AddFromString",tolua_AllToLua_cEnchantments_AddFromString00); + tolua_function(tolua_S,"ToString",tolua_AllToLua_cEnchantments_ToString00); + tolua_function(tolua_S,"GetLevel",tolua_AllToLua_cEnchantments_GetLevel00); + tolua_function(tolua_S,"SetLevel",tolua_AllToLua_cEnchantments_SetLevel00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cEnchantments_Clear00); + tolua_function(tolua_S,"IsEmpty",tolua_AllToLua_cEnchantments_IsEmpty00); + tolua_function(tolua_S,"StringToEnchantmentID",tolua_AllToLua_cEnchantments_StringToEnchantmentID00); + tolua_function(tolua_S,".eq",tolua_AllToLua_cEnchantments__eq00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cItem","cItem","",tolua_collect_cItem); + #else + tolua_cclass(tolua_S,"cItem","cItem","",NULL); + #endif + tolua_beginmodule(tolua_S,"cItem"); + tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new03_local); + tolua_function(tolua_S,"Empty",tolua_AllToLua_cItem_Empty00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cItem_Clear00); + tolua_function(tolua_S,"IsEmpty",tolua_AllToLua_cItem_IsEmpty00); + tolua_function(tolua_S,"IsEqual",tolua_AllToLua_cItem_IsEqual00); + tolua_function(tolua_S,"IsSameType",tolua_AllToLua_cItem_IsSameType00); + tolua_function(tolua_S,"CopyOne",tolua_AllToLua_cItem_CopyOne00); + tolua_function(tolua_S,"AddCount",tolua_AllToLua_cItem_AddCount00); + tolua_function(tolua_S,"GetMaxDamage",tolua_AllToLua_cItem_GetMaxDamage00); + tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItem_DamageItem00); + tolua_function(tolua_S,"IsDamageable",tolua_AllToLua_cItem_IsDamageable00); + tolua_function(tolua_S,"IsStackableWith",tolua_AllToLua_cItem_IsStackableWith00); + tolua_function(tolua_S,"IsFullStack",tolua_AllToLua_cItem_IsFullStack00); + tolua_function(tolua_S,"GetMaxStackSize",tolua_AllToLua_cItem_GetMaxStackSize00); + tolua_variable(tolua_S,"m_ItemType",tolua_get_cItem_m_ItemType,tolua_set_cItem_m_ItemType); + tolua_variable(tolua_S,"m_ItemCount",tolua_get_cItem_m_ItemCount,tolua_set_cItem_m_ItemCount); + tolua_variable(tolua_S,"m_ItemDamage",tolua_get_cItem_m_ItemDamage,tolua_set_cItem_m_ItemDamage); + tolua_variable(tolua_S,"m_Enchantments",tolua_get_cItem_m_Enchantments,tolua_set_cItem_m_Enchantments); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cItems","cItems","",tolua_collect_cItems); + #else + tolua_cclass(tolua_S,"cItems","cItems","",NULL); + #endif + tolua_beginmodule(tolua_S,"cItems"); + tolua_function(tolua_S,"new",tolua_AllToLua_cItems_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItems_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItems_new00_local); + tolua_function(tolua_S,"Get",tolua_AllToLua_cItems_Get00); + tolua_function(tolua_S,"Set",tolua_AllToLua_cItems_Set00); + tolua_function(tolua_S,"Add",tolua_AllToLua_cItems_Add00); + tolua_function(tolua_S,"Delete",tolua_AllToLua_cItems_Delete00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cItems_Clear00); + tolua_function(tolua_S,"Size",tolua_AllToLua_cItems_Size00); + tolua_function(tolua_S,"Set",tolua_AllToLua_cItems_Set01); + tolua_function(tolua_S,"Add",tolua_AllToLua_cItems_Add01); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cItemGrid","cItemGrid","",NULL); + tolua_beginmodule(tolua_S,"cItemGrid"); + tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cItemGrid_GetWidth00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cItemGrid_GetHeight00); + tolua_function(tolua_S,"GetNumSlots",tolua_AllToLua_cItemGrid_GetNumSlots00); + tolua_function(tolua_S,"GetSlotNum",tolua_AllToLua_cItemGrid_GetSlotNum00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cItemGrid_GetSlot00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cItemGrid_GetSlot01); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot00); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot01); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot02); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot03); + tolua_function(tolua_S,"EmptySlot",tolua_AllToLua_cItemGrid_EmptySlot00); + tolua_function(tolua_S,"EmptySlot",tolua_AllToLua_cItemGrid_EmptySlot01); + tolua_function(tolua_S,"IsSlotEmpty",tolua_AllToLua_cItemGrid_IsSlotEmpty00); + tolua_function(tolua_S,"IsSlotEmpty",tolua_AllToLua_cItemGrid_IsSlotEmpty01); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cItemGrid_Clear00); + tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cItemGrid_HowManyCanFit00); + tolua_function(tolua_S,"AddItem",tolua_AllToLua_cItemGrid_AddItem00); + tolua_function(tolua_S,"AddItems",tolua_AllToLua_cItemGrid_AddItems00); + tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cItemGrid_ChangeSlotCount00); + tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cItemGrid_ChangeSlotCount01); + tolua_function(tolua_S,"RemoveOneItem",tolua_AllToLua_cItemGrid_RemoveOneItem00); + tolua_function(tolua_S,"RemoveOneItem",tolua_AllToLua_cItemGrid_RemoveOneItem01); + tolua_function(tolua_S,"HowManyItems",tolua_AllToLua_cItemGrid_HowManyItems00); + tolua_function(tolua_S,"HasItems",tolua_AllToLua_cItemGrid_HasItems00); + tolua_function(tolua_S,"GetFirstEmptySlot",tolua_AllToLua_cItemGrid_GetFirstEmptySlot00); + tolua_function(tolua_S,"GetFirstUsedSlot",tolua_AllToLua_cItemGrid_GetFirstUsedSlot00); + tolua_function(tolua_S,"GetLastEmptySlot",tolua_AllToLua_cItemGrid_GetLastEmptySlot00); + tolua_function(tolua_S,"GetLastUsedSlot",tolua_AllToLua_cItemGrid_GetLastUsedSlot00); + tolua_function(tolua_S,"GetNextEmptySlot",tolua_AllToLua_cItemGrid_GetNextEmptySlot00); + tolua_function(tolua_S,"GetNextUsedSlot",tolua_AllToLua_cItemGrid_GetNextUsedSlot00); + tolua_function(tolua_S,"CopyToItems",tolua_AllToLua_cItemGrid_CopyToItems00); + tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItemGrid_DamageItem00); + tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItemGrid_DamageItem01); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cBlockEntity","cBlockEntity","",tolua_collect_cBlockEntity); + #else + tolua_cclass(tolua_S,"cBlockEntity","cBlockEntity","",NULL); + #endif + tolua_beginmodule(tolua_S,"cBlockEntity"); + tolua_function(tolua_S,"GetPosX",tolua_AllToLua_cBlockEntity_GetPosX00); + tolua_function(tolua_S,"GetPosY",tolua_AllToLua_cBlockEntity_GetPosY00); + tolua_function(tolua_S,"GetPosZ",tolua_AllToLua_cBlockEntity_GetPosZ00); + tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cBlockEntity_GetBlockType00); + tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cBlockEntity_GetWorld00); + tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cBlockEntity_GetChunkX00); + tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cBlockEntity_GetChunkZ00); + tolua_function(tolua_S,"GetRelX",tolua_AllToLua_cBlockEntity_GetRelX00); + tolua_function(tolua_S,"GetRelZ",tolua_AllToLua_cBlockEntity_GetRelZ00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cBlockEntityWithItems","cBlockEntityWithItems","cBlockEntity",NULL); + tolua_beginmodule(tolua_S,"cBlockEntityWithItems"); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cBlockEntityWithItems_GetSlot00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cBlockEntityWithItems_GetSlot01); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cBlockEntityWithItems_SetSlot00); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cBlockEntityWithItems_SetSlot01); + tolua_function(tolua_S,"GetContents",tolua_AllToLua_cBlockEntityWithItems_GetContents00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cChestEntity","cChestEntity","cBlockEntityWithItems",NULL); + tolua_beginmodule(tolua_S,"cChestEntity"); + tolua_constant(tolua_S,"ContentsHeight",cChestEntity::ContentsHeight); + tolua_constant(tolua_S,"ContentsWidth",cChestEntity::ContentsWidth); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cDropSpenserEntity","cDropSpenserEntity","cBlockEntityWithItems",NULL); + tolua_beginmodule(tolua_S,"cDropSpenserEntity"); + tolua_constant(tolua_S,"ContentsHeight",cDropSpenserEntity::ContentsHeight); + tolua_constant(tolua_S,"ContentsWidth",cDropSpenserEntity::ContentsWidth); + tolua_function(tolua_S,"AddDropSpenserDir",tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00); + tolua_function(tolua_S,"Activate",tolua_AllToLua_cDropSpenserEntity_Activate00); + tolua_function(tolua_S,"SetRedstonePower",tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cDispenserEntity","cDispenserEntity","cDropSpenserEntity",NULL); + tolua_beginmodule(tolua_S,"cDispenserEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cDropperEntity","cDropperEntity","cDropSpenserEntity",NULL); + tolua_beginmodule(tolua_S,"cDropperEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cFurnaceEntity","cFurnaceEntity","cBlockEntityWithItems",NULL); + tolua_beginmodule(tolua_S,"cFurnaceEntity"); + tolua_constant(tolua_S,"fsInput",cFurnaceEntity::fsInput); + tolua_constant(tolua_S,"fsFuel",cFurnaceEntity::fsFuel); + tolua_constant(tolua_S,"fsOutput",cFurnaceEntity::fsOutput); + tolua_constant(tolua_S,"ContentsWidth",cFurnaceEntity::ContentsWidth); + tolua_constant(tolua_S,"ContentsHeight",cFurnaceEntity::ContentsHeight); + tolua_function(tolua_S,"GetInputSlot",tolua_AllToLua_cFurnaceEntity_GetInputSlot00); + tolua_function(tolua_S,"GetFuelSlot",tolua_AllToLua_cFurnaceEntity_GetFuelSlot00); + tolua_function(tolua_S,"GetOutputSlot",tolua_AllToLua_cFurnaceEntity_GetOutputSlot00); + tolua_function(tolua_S,"SetInputSlot",tolua_AllToLua_cFurnaceEntity_SetInputSlot00); + tolua_function(tolua_S,"SetFuelSlot",tolua_AllToLua_cFurnaceEntity_SetFuelSlot00); + tolua_function(tolua_S,"SetOutputSlot",tolua_AllToLua_cFurnaceEntity_SetOutputSlot00); + tolua_function(tolua_S,"GetTimeCooked",tolua_AllToLua_cFurnaceEntity_GetTimeCooked00); + tolua_function(tolua_S,"GetCookTimeLeft",tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00); + tolua_function(tolua_S,"GetFuelBurnTimeLeft",tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00); + tolua_function(tolua_S,"HasFuelTimeLeft",tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cHopperEntity","cHopperEntity","cBlockEntityWithItems",NULL); + tolua_beginmodule(tolua_S,"cHopperEntity"); + tolua_constant(tolua_S,"ContentsHeight",cHopperEntity::ContentsHeight); + tolua_constant(tolua_S,"ContentsWidth",cHopperEntity::ContentsWidth); + tolua_constant(tolua_S,"TICKS_PER_TRANSFER",cHopperEntity::TICKS_PER_TRANSFER); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cJukeboxEntity","cJukeboxEntity","cBlockEntity",NULL); + tolua_beginmodule(tolua_S,"cJukeboxEntity"); + tolua_function(tolua_S,"GetRecord",tolua_AllToLua_cJukeboxEntity_GetRecord00); + tolua_function(tolua_S,"SetRecord",tolua_AllToLua_cJukeboxEntity_SetRecord00); + tolua_function(tolua_S,"PlayRecord",tolua_AllToLua_cJukeboxEntity_PlayRecord00); + tolua_function(tolua_S,"EjectRecord",tolua_AllToLua_cJukeboxEntity_EjectRecord00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cNoteEntity","cNoteEntity","cBlockEntity",NULL); + tolua_beginmodule(tolua_S,"cNoteEntity"); + tolua_function(tolua_S,"GetPitch",tolua_AllToLua_cNoteEntity_GetPitch00); + tolua_function(tolua_S,"SetPitch",tolua_AllToLua_cNoteEntity_SetPitch00); + tolua_function(tolua_S,"IncrementPitch",tolua_AllToLua_cNoteEntity_IncrementPitch00); + tolua_function(tolua_S,"MakeSound",tolua_AllToLua_cNoteEntity_MakeSound00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cSignEntity","cSignEntity","cBlockEntity",NULL); + tolua_beginmodule(tolua_S,"cSignEntity"); + tolua_function(tolua_S,"SetLines",tolua_AllToLua_cSignEntity_SetLines00); + tolua_function(tolua_S,"SetLine",tolua_AllToLua_cSignEntity_SetLine00); + tolua_function(tolua_S,"GetLine",tolua_AllToLua_cSignEntity_GetLine00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"HTTPFormData","HTTPFormData","",NULL); + tolua_beginmodule(tolua_S,"HTTPFormData"); + tolua_variable(tolua_S,"Name",tolua_get_HTTPFormData_Name,tolua_set_HTTPFormData_Name); + tolua_variable(tolua_S,"Value",tolua_get_HTTPFormData_Value,tolua_set_HTTPFormData_Value); + tolua_variable(tolua_S,"Type",tolua_get_HTTPFormData_Type,tolua_set_HTTPFormData_Type); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"HTTPRequest","HTTPRequest","",NULL); + tolua_beginmodule(tolua_S,"HTTPRequest"); + tolua_variable(tolua_S,"Method",tolua_get_HTTPRequest_Method,tolua_set_HTTPRequest_Method); + tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path); + tolua_variable(tolua_S,"Username",tolua_get_HTTPRequest_Username,tolua_set_HTTPRequest_Username); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"HTTPTemplateRequest","HTTPTemplateRequest","",NULL); + tolua_beginmodule(tolua_S,"HTTPTemplateRequest"); + tolua_variable(tolua_S,"Request",tolua_get_HTTPTemplateRequest_Request,tolua_set_HTTPTemplateRequest_Request); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",tolua_collect_sWebAdminPage); + #else + tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",NULL); + #endif + tolua_beginmodule(tolua_S,"sWebAdminPage"); + tolua_variable(tolua_S,"Content",tolua_get_sWebAdminPage_Content,tolua_set_sWebAdminPage_Content); + tolua_variable(tolua_S,"PluginName",tolua_get_sWebAdminPage_PluginName,tolua_set_sWebAdminPage_PluginName); + tolua_variable(tolua_S,"TabName",tolua_get_sWebAdminPage_TabName,tolua_set_sWebAdminPage_TabName); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","cHTTPServer::cCallbacks",NULL); + tolua_beginmodule(tolua_S,"cWebAdmin"); + tolua_function(tolua_S,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00); + tolua_function(tolua_S,"GetDefaultPage",tolua_AllToLua_cWebAdmin_GetDefaultPage00); + tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00); + tolua_function(tolua_S,"GetHTMLEscapedString",tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL); + tolua_beginmodule(tolua_S,"cWebPlugin"); + tolua_function(tolua_S,"GetWebTitle",tolua_AllToLua_cWebPlugin_GetWebTitle00); + tolua_function(tolua_S,"HandleWebRequest",tolua_AllToLua_cWebPlugin_HandleWebRequest00); + tolua_function(tolua_S,"SafeString",tolua_AllToLua_cWebPlugin_SafeString00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cRoot","cRoot","",NULL); + tolua_beginmodule(tolua_S,"cRoot"); + tolua_function(tolua_S,"Get",tolua_AllToLua_cRoot_Get00); + tolua_function(tolua_S,"GetServer",tolua_AllToLua_cRoot_GetServer00); + tolua_function(tolua_S,"GetDefaultWorld",tolua_AllToLua_cRoot_GetDefaultWorld00); + tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cRoot_GetWorld00); + tolua_function(tolua_S,"GetPrimaryServerVersion",tolua_AllToLua_cRoot_GetPrimaryServerVersion00); + tolua_function(tolua_S,"SetPrimaryServerVersion",tolua_AllToLua_cRoot_SetPrimaryServerVersion00); + tolua_function(tolua_S,"GetGroupManager",tolua_AllToLua_cRoot_GetGroupManager00); + tolua_function(tolua_S,"GetCraftingRecipes",tolua_AllToLua_cRoot_GetCraftingRecipes00); + tolua_function(tolua_S,"GetFurnaceRecipe",tolua_AllToLua_cRoot_GetFurnaceRecipe00); + tolua_function(tolua_S,"GetWebAdmin",tolua_AllToLua_cRoot_GetWebAdmin00); + tolua_function(tolua_S,"GetPluginManager",tolua_AllToLua_cRoot_GetPluginManager00); + tolua_function(tolua_S,"QueueExecuteConsoleCommand",tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00); + tolua_function(tolua_S,"GetTotalChunkCount",tolua_AllToLua_cRoot_GetTotalChunkCount00); + tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00); + tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cRoot_BroadcastChat00); + tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00); + tolua_function(tolua_S,"GetVirtualRAMUsage",tolua_AllToLua_cRoot_GetVirtualRAMUsage00); + tolua_function(tolua_S,"GetPhysicalRAMUsage",tolua_AllToLua_cRoot_GetPhysicalRAMUsage00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"Vector3f","Vector3f","",tolua_collect_Vector3f); + #else + tolua_cclass(tolua_S,"Vector3f","Vector3f","",NULL); + #endif + tolua_beginmodule(tolua_S,"Vector3f"); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new03_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new04); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new04_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new04_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new05); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new05_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new05_local); + tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3f_Set00); + tolua_function(tolua_S,"Normalize",tolua_AllToLua_Vector3f_Normalize00); + tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3f_NormalizeCopy00); + tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3f_NormalizeCopy01); + tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3f_Length00); + tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3f_SqrLength00); + tolua_function(tolua_S,"Dot",tolua_AllToLua_Vector3f_Dot00); + tolua_function(tolua_S,"Cross",tolua_AllToLua_Vector3f_Cross00); + tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3f_Equals00); + tolua_function(tolua_S,".add",tolua_AllToLua_Vector3f__add00); + tolua_function(tolua_S,".add",tolua_AllToLua_Vector3f__add01); + tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3f__sub00); + tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3f__sub01); + tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3f__mul00); + tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3f__mul01); + tolua_variable(tolua_S,"x",tolua_get_Vector3f_x,tolua_set_Vector3f_x); + tolua_variable(tolua_S,"y",tolua_get_Vector3f_y,tolua_set_Vector3f_y); + tolua_variable(tolua_S,"z",tolua_get_Vector3f_z,tolua_set_Vector3f_z); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"Vector3d","Vector3d","",tolua_collect_Vector3d); + #else + tolua_cclass(tolua_S,"Vector3d","Vector3d","",NULL); + #endif + tolua_beginmodule(tolua_S,"Vector3d"); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new03_local); + tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3d_Set00); + tolua_function(tolua_S,"Normalize",tolua_AllToLua_Vector3d_Normalize00); + tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3d_NormalizeCopy00); + tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3d_NormalizeCopy01); + tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3d_Length00); + tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3d_SqrLength00); + tolua_function(tolua_S,"Dot",tolua_AllToLua_Vector3d_Dot00); + tolua_function(tolua_S,"Cross",tolua_AllToLua_Vector3d_Cross00); + tolua_function(tolua_S,"LineCoeffToXYPlane",tolua_AllToLua_Vector3d_LineCoeffToXYPlane00); + tolua_function(tolua_S,"LineCoeffToXZPlane",tolua_AllToLua_Vector3d_LineCoeffToXZPlane00); + tolua_function(tolua_S,"LineCoeffToYZPlane",tolua_AllToLua_Vector3d_LineCoeffToYZPlane00); + tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3d_Equals00); + tolua_function(tolua_S,".add",tolua_AllToLua_Vector3d__add00); + tolua_function(tolua_S,".add",tolua_AllToLua_Vector3d__add01); + tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3d__sub00); + tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3d__sub01); + tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3d__mul00); + tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3d__mul01); + tolua_function(tolua_S,".div",tolua_AllToLua_Vector3d__div00); + tolua_variable(tolua_S,"x",tolua_get_Vector3d_x,tolua_set_Vector3d_x); + tolua_variable(tolua_S,"y",tolua_get_Vector3d_y,tolua_set_Vector3d_y); + tolua_variable(tolua_S,"z",tolua_get_Vector3d_z,tolua_set_Vector3d_z); + tolua_variable(tolua_S,"EPS",tolua_get_Vector3d_EPS,NULL); + tolua_variable(tolua_S,"NO_INTERSECTION",tolua_get_Vector3d_NO_INTERSECTION,NULL); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"Vector3i","Vector3i","",tolua_collect_Vector3i); + #else + tolua_cclass(tolua_S,"Vector3i","Vector3i","",NULL); + #endif + tolua_beginmodule(tolua_S,"Vector3i"); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new02_local); + tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3i_Set00); + tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3i_Length00); + tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3i_SqrLength00); + tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3i_Equals00); + tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3i_Equals01); + tolua_variable(tolua_S,"x",tolua_get_Vector3i_x,tolua_set_Vector3i_x); + tolua_variable(tolua_S,"y",tolua_get_Vector3i_y,tolua_set_Vector3i_y); + tolua_variable(tolua_S,"z",tolua_get_Vector3i_z,tolua_set_Vector3i_z); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cCuboid","cCuboid","",tolua_collect_cCuboid); + #else + tolua_cclass(tolua_S,"cCuboid","cCuboid","",NULL); + #endif + tolua_beginmodule(tolua_S,"cCuboid"); + tolua_variable(tolua_S,"p1",tolua_get_cCuboid_p1,tolua_set_cCuboid_p1); + tolua_variable(tolua_S,"p2",tolua_get_cCuboid_p2,tolua_set_cCuboid_p2); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new03_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new04); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new04_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new04_local); + tolua_function(tolua_S,"Assign",tolua_AllToLua_cCuboid_Assign00); + tolua_function(tolua_S,"Sort",tolua_AllToLua_cCuboid_Sort00); + tolua_function(tolua_S,"DifX",tolua_AllToLua_cCuboid_DifX00); + tolua_function(tolua_S,"DifY",tolua_AllToLua_cCuboid_DifY00); + tolua_function(tolua_S,"DifZ",tolua_AllToLua_cCuboid_DifZ00); + tolua_function(tolua_S,"DoesIntersect",tolua_AllToLua_cCuboid_DoesIntersect00); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside00); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside01); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside02); + tolua_function(tolua_S,"IsCompletelyInside",tolua_AllToLua_cCuboid_IsCompletelyInside00); + tolua_function(tolua_S,"Move",tolua_AllToLua_cCuboid_Move00); + tolua_function(tolua_S,"IsSorted",tolua_AllToLua_cCuboid_IsSorted00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cBoundingBox","cBoundingBox","",tolua_collect_cBoundingBox); + #else + tolua_cclass(tolua_S,"cBoundingBox","cBoundingBox","",NULL); + #endif + tolua_beginmodule(tolua_S,"cBoundingBox"); + tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new03_local); + tolua_function(tolua_S,"Move",tolua_AllToLua_cBoundingBox_Move00); + tolua_function(tolua_S,"Move",tolua_AllToLua_cBoundingBox_Move01); + tolua_function(tolua_S,"Expand",tolua_AllToLua_cBoundingBox_Expand00); + tolua_function(tolua_S,"DoesIntersect",tolua_AllToLua_cBoundingBox_DoesIntersect00); + tolua_function(tolua_S,"Union",tolua_AllToLua_cBoundingBox_Union00); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside00); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside01); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside02); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside03); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside04); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside05); + tolua_function(tolua_S,"CalcLineIntersection",tolua_AllToLua_cBoundingBox_CalcLineIntersection00); + tolua_function(tolua_S,"CalcLineIntersection",tolua_AllToLua_cBoundingBox_CalcLineIntersection01); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cTracer","cTracer","",tolua_collect_cTracer); + #else + tolua_cclass(tolua_S,"cTracer","cTracer","",NULL); + #endif + tolua_beginmodule(tolua_S,"cTracer"); + tolua_variable(tolua_S,"BlockHitPosition",tolua_get_cTracer_BlockHitPosition,tolua_set_cTracer_BlockHitPosition); + tolua_variable(tolua_S,"HitNormal",tolua_get_cTracer_HitNormal,tolua_set_cTracer_HitNormal); + tolua_variable(tolua_S,"RealHit",tolua_get_cTracer_RealHit,tolua_set_cTracer_RealHit); + tolua_function(tolua_S,"new",tolua_AllToLua_cTracer_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cTracer_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cTracer_new00_local); + tolua_function(tolua_S,"delete",tolua_AllToLua_cTracer_delete00); + tolua_function(tolua_S,"Trace",tolua_AllToLua_cTracer_Trace00); + tolua_function(tolua_S,"Trace",tolua_AllToLua_cTracer_Trace01); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cGroup","cGroup","",NULL); + tolua_beginmodule(tolua_S,"cGroup"); + tolua_function(tolua_S,"SetName",tolua_AllToLua_cGroup_SetName00); + tolua_function(tolua_S,"GetName",tolua_AllToLua_cGroup_GetName00); + tolua_function(tolua_S,"SetColor",tolua_AllToLua_cGroup_SetColor00); + tolua_function(tolua_S,"AddCommand",tolua_AllToLua_cGroup_AddCommand00); + tolua_function(tolua_S,"AddPermission",tolua_AllToLua_cGroup_AddPermission00); + tolua_function(tolua_S,"InheritFrom",tolua_AllToLua_cGroup_InheritFrom00); + tolua_function(tolua_S,"HasCommand",tolua_AllToLua_cGroup_HasCommand00); + tolua_function(tolua_S,"GetColor",tolua_AllToLua_cGroup_GetColor00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cBlockArea","cBlockArea","",tolua_collect_cBlockArea); + #else + tolua_cclass(tolua_S,"cBlockArea","cBlockArea","",NULL); + #endif + tolua_beginmodule(tolua_S,"cBlockArea"); + tolua_constant(tolua_S,"baTypes",cBlockArea::baTypes); + tolua_constant(tolua_S,"baMetas",cBlockArea::baMetas); + tolua_constant(tolua_S,"baLight",cBlockArea::baLight); + tolua_constant(tolua_S,"baSkyLight",cBlockArea::baSkyLight); + tolua_constant(tolua_S,"msOverwrite",cBlockArea::msOverwrite); + tolua_constant(tolua_S,"msFillAir",cBlockArea::msFillAir); + tolua_constant(tolua_S,"msImprint",cBlockArea::msImprint); + tolua_constant(tolua_S,"msLake",cBlockArea::msLake); + tolua_function(tolua_S,"new",tolua_AllToLua_cBlockArea_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBlockArea_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBlockArea_new00_local); + tolua_function(tolua_S,"delete",tolua_AllToLua_cBlockArea_delete00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cBlockArea_Clear00); + tolua_function(tolua_S,"Create",tolua_AllToLua_cBlockArea_Create00); + tolua_function(tolua_S,"Create",tolua_AllToLua_cBlockArea_Create01); + tolua_function(tolua_S,"SetOrigin",tolua_AllToLua_cBlockArea_SetOrigin00); + tolua_function(tolua_S,"Read",tolua_AllToLua_cBlockArea_Read00); + tolua_function(tolua_S,"Read",tolua_AllToLua_cBlockArea_Read01); + tolua_function(tolua_S,"Write",tolua_AllToLua_cBlockArea_Write00); + tolua_function(tolua_S,"Write",tolua_AllToLua_cBlockArea_Write01); + tolua_function(tolua_S,"CopyTo",tolua_AllToLua_cBlockArea_CopyTo00); + tolua_function(tolua_S,"CopyFrom",tolua_AllToLua_cBlockArea_CopyFrom00); + tolua_function(tolua_S,"DumpToRawFile",tolua_AllToLua_cBlockArea_DumpToRawFile00); + tolua_function(tolua_S,"LoadFromSchematicFile",tolua_AllToLua_cBlockArea_LoadFromSchematicFile00); + tolua_function(tolua_S,"SaveToSchematicFile",tolua_AllToLua_cBlockArea_SaveToSchematicFile00); + tolua_function(tolua_S,"Crop",tolua_AllToLua_cBlockArea_Crop00); + tolua_function(tolua_S,"Expand",tolua_AllToLua_cBlockArea_Expand00); + tolua_function(tolua_S,"Merge",tolua_AllToLua_cBlockArea_Merge00); + tolua_function(tolua_S,"Fill",tolua_AllToLua_cBlockArea_Fill00); + tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cBlockArea_FillRelCuboid00); + tolua_function(tolua_S,"RelLine",tolua_AllToLua_cBlockArea_RelLine00); + tolua_function(tolua_S,"RotateCCW",tolua_AllToLua_cBlockArea_RotateCCW00); + tolua_function(tolua_S,"RotateCW",tolua_AllToLua_cBlockArea_RotateCW00); + tolua_function(tolua_S,"MirrorXY",tolua_AllToLua_cBlockArea_MirrorXY00); + tolua_function(tolua_S,"MirrorXZ",tolua_AllToLua_cBlockArea_MirrorXZ00); + tolua_function(tolua_S,"MirrorYZ",tolua_AllToLua_cBlockArea_MirrorYZ00); + tolua_function(tolua_S,"RotateCCWNoMeta",tolua_AllToLua_cBlockArea_RotateCCWNoMeta00); + tolua_function(tolua_S,"RotateCWNoMeta",tolua_AllToLua_cBlockArea_RotateCWNoMeta00); + tolua_function(tolua_S,"MirrorXYNoMeta",tolua_AllToLua_cBlockArea_MirrorXYNoMeta00); + tolua_function(tolua_S,"MirrorXZNoMeta",tolua_AllToLua_cBlockArea_MirrorXZNoMeta00); + tolua_function(tolua_S,"MirrorYZNoMeta",tolua_AllToLua_cBlockArea_MirrorYZNoMeta00); + tolua_function(tolua_S,"SetRelBlockType",tolua_AllToLua_cBlockArea_SetRelBlockType00); + tolua_function(tolua_S,"SetBlockType",tolua_AllToLua_cBlockArea_SetBlockType00); + tolua_function(tolua_S,"SetRelBlockMeta",tolua_AllToLua_cBlockArea_SetRelBlockMeta00); + tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cBlockArea_SetBlockMeta00); + tolua_function(tolua_S,"SetRelBlockLight",tolua_AllToLua_cBlockArea_SetRelBlockLight00); + tolua_function(tolua_S,"SetBlockLight",tolua_AllToLua_cBlockArea_SetBlockLight00); + tolua_function(tolua_S,"SetRelBlockSkyLight",tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00); + tolua_function(tolua_S,"SetBlockSkyLight",tolua_AllToLua_cBlockArea_SetBlockSkyLight00); + tolua_function(tolua_S,"GetRelBlockType",tolua_AllToLua_cBlockArea_GetRelBlockType00); + tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cBlockArea_GetBlockType00); + tolua_function(tolua_S,"GetRelBlockMeta",tolua_AllToLua_cBlockArea_GetRelBlockMeta00); + tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cBlockArea_GetBlockMeta00); + tolua_function(tolua_S,"GetRelBlockLight",tolua_AllToLua_cBlockArea_GetRelBlockLight00); + tolua_function(tolua_S,"GetBlockLight",tolua_AllToLua_cBlockArea_GetBlockLight00); + tolua_function(tolua_S,"GetRelBlockSkyLight",tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00); + tolua_function(tolua_S,"GetBlockSkyLight",tolua_AllToLua_cBlockArea_GetBlockSkyLight00); + tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cBlockArea_SetBlockTypeMeta00); + tolua_function(tolua_S,"SetRelBlockTypeMeta",tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00); + tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cBlockArea_GetBlockTypeMeta00); + tolua_function(tolua_S,"GetRelBlockTypeMeta",tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00); + tolua_function(tolua_S,"GetSizeX",tolua_AllToLua_cBlockArea_GetSizeX00); + tolua_function(tolua_S,"GetSizeY",tolua_AllToLua_cBlockArea_GetSizeY00); + tolua_function(tolua_S,"GetSizeZ",tolua_AllToLua_cBlockArea_GetSizeZ00); + tolua_function(tolua_S,"GetOriginX",tolua_AllToLua_cBlockArea_GetOriginX00); + tolua_function(tolua_S,"GetOriginY",tolua_AllToLua_cBlockArea_GetOriginY00); + tolua_function(tolua_S,"GetOriginZ",tolua_AllToLua_cBlockArea_GetOriginZ00); + tolua_function(tolua_S,"GetDataTypes",tolua_AllToLua_cBlockArea_GetDataTypes00); + tolua_function(tolua_S,"HasBlockTypes",tolua_AllToLua_cBlockArea_HasBlockTypes00); + tolua_function(tolua_S,"HasBlockMetas",tolua_AllToLua_cBlockArea_HasBlockMetas00); + tolua_function(tolua_S,"HasBlockLights",tolua_AllToLua_cBlockArea_HasBlockLights00); + tolua_function(tolua_S,"HasBlockSkyLights",tolua_AllToLua_cBlockArea_HasBlockSkyLights00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cChunkDesc","cChunkDesc","",NULL); + tolua_beginmodule(tolua_S,"cChunkDesc"); + tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cChunkDesc_GetChunkX00); + tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cChunkDesc_GetChunkZ00); + tolua_function(tolua_S,"FillBlocks",tolua_AllToLua_cChunkDesc_FillBlocks00); + tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00); + tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00); + tolua_function(tolua_S,"SetBlockType",tolua_AllToLua_cChunkDesc_SetBlockType00); + tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cChunkDesc_GetBlockType00); + tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cChunkDesc_SetBlockMeta00); + tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cChunkDesc_GetBlockMeta00); + tolua_function(tolua_S,"SetBiome",tolua_AllToLua_cChunkDesc_SetBiome00); + tolua_function(tolua_S,"GetBiome",tolua_AllToLua_cChunkDesc_GetBiome00); + tolua_function(tolua_S,"SetHeight",tolua_AllToLua_cChunkDesc_SetHeight00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cChunkDesc_GetHeight00); + tolua_function(tolua_S,"SetUseDefaultBiomes",tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00); + tolua_function(tolua_S,"IsUsingDefaultBiomes",tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00); + tolua_function(tolua_S,"SetUseDefaultHeight",tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00); + tolua_function(tolua_S,"IsUsingDefaultHeight",tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00); + tolua_function(tolua_S,"SetUseDefaultComposition",tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00); + tolua_function(tolua_S,"IsUsingDefaultComposition",tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00); + tolua_function(tolua_S,"SetUseDefaultStructures",tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00); + tolua_function(tolua_S,"IsUsingDefaultStructures",tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00); + tolua_function(tolua_S,"SetUseDefaultFinish",tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00); + tolua_function(tolua_S,"IsUsingDefaultFinish",tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00); + tolua_function(tolua_S,"WriteBlockArea",tolua_AllToLua_cChunkDesc_WriteBlockArea00); + tolua_function(tolua_S,"ReadBlockArea",tolua_AllToLua_cChunkDesc_ReadBlockArea00); + tolua_function(tolua_S,"GetMaxHeight",tolua_AllToLua_cChunkDesc_GetMaxHeight00); + tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cChunkDesc_FillRelCuboid00); + tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cChunkDesc_FillRelCuboid01); + tolua_function(tolua_S,"ReplaceRelCuboid",tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00); + tolua_function(tolua_S,"ReplaceRelCuboid",tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01); + tolua_function(tolua_S,"FloorRelCuboid",tolua_AllToLua_cChunkDesc_FloorRelCuboid00); + tolua_function(tolua_S,"FloorRelCuboid",tolua_AllToLua_cChunkDesc_FloorRelCuboid01); + tolua_function(tolua_S,"RandomFillRelCuboid",tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00); + tolua_function(tolua_S,"RandomFillRelCuboid",tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01); + tolua_function(tolua_S,"GetBlockEntity",tolua_AllToLua_cChunkDesc_GetBlockEntity00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cCraftingGrid","cCraftingGrid","",tolua_collect_cCraftingGrid); + #else + tolua_cclass(tolua_S,"cCraftingGrid","cCraftingGrid","",NULL); + #endif + tolua_beginmodule(tolua_S,"cCraftingGrid"); + tolua_function(tolua_S,"new",tolua_AllToLua_cCraftingGrid_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCraftingGrid_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCraftingGrid_new00_local); + tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cCraftingGrid_GetWidth00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cCraftingGrid_GetHeight00); + tolua_function(tolua_S,"GetItem",tolua_AllToLua_cCraftingGrid_GetItem00); + tolua_function(tolua_S,"SetItem",tolua_AllToLua_cCraftingGrid_SetItem00); + tolua_function(tolua_S,"SetItem",tolua_AllToLua_cCraftingGrid_SetItem01); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cCraftingGrid_Clear00); + tolua_function(tolua_S,"ConsumeGrid",tolua_AllToLua_cCraftingGrid_ConsumeGrid00); + tolua_function(tolua_S,"Dump",tolua_AllToLua_cCraftingGrid_Dump00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cCraftingRecipe","cCraftingRecipe","",NULL); + tolua_beginmodule(tolua_S,"cCraftingRecipe"); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cCraftingRecipe_Clear00); + tolua_function(tolua_S,"GetIngredientsWidth",tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00); + tolua_function(tolua_S,"GetIngredientsHeight",tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00); + tolua_function(tolua_S,"GetIngredient",tolua_AllToLua_cCraftingRecipe_GetIngredient00); + tolua_function(tolua_S,"GetResult",tolua_AllToLua_cCraftingRecipe_GetResult00); + tolua_function(tolua_S,"SetResult",tolua_AllToLua_cCraftingRecipe_SetResult00); + tolua_function(tolua_S,"SetResult",tolua_AllToLua_cCraftingRecipe_SetResult01); + tolua_function(tolua_S,"SetIngredient",tolua_AllToLua_cCraftingRecipe_SetIngredient00); + tolua_function(tolua_S,"SetIngredient",tolua_AllToLua_cCraftingRecipe_SetIngredient01); + tolua_function(tolua_S,"ConsumeIngredients",tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00); + tolua_function(tolua_S,"Dump",tolua_AllToLua_cCraftingRecipe_Dump00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cWindow","cWindow","",NULL); + tolua_beginmodule(tolua_S,"cWindow"); + tolua_constant(tolua_S,"wtInventory",cWindow::wtInventory); + tolua_constant(tolua_S,"wtChest",cWindow::wtChest); + tolua_constant(tolua_S,"wtWorkbench",cWindow::wtWorkbench); + tolua_constant(tolua_S,"wtFurnace",cWindow::wtFurnace); + tolua_constant(tolua_S,"wtDropSpenser",cWindow::wtDropSpenser); + tolua_constant(tolua_S,"wtEnchantment",cWindow::wtEnchantment); + tolua_constant(tolua_S,"wtBrewery",cWindow::wtBrewery); + tolua_constant(tolua_S,"wtNPCTrade",cWindow::wtNPCTrade); + tolua_constant(tolua_S,"wtBeacon",cWindow::wtBeacon); + tolua_constant(tolua_S,"wtAnvil",cWindow::wtAnvil); + tolua_constant(tolua_S,"wtHopper",cWindow::wtHopper); + tolua_constant(tolua_S,"wtAnimalChest",cWindow::wtAnimalChest); + tolua_function(tolua_S,"GetWindowID",tolua_AllToLua_cWindow_GetWindowID00); + tolua_function(tolua_S,"GetWindowType",tolua_AllToLua_cWindow_GetWindowType00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cWindow_GetSlot00); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cWindow_SetSlot00); + tolua_function(tolua_S,"IsSlotInPlayerMainInventory",tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00); + tolua_function(tolua_S,"IsSlotInPlayerHotbar",tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00); + tolua_function(tolua_S,"IsSlotInPlayerInventory",tolua_AllToLua_cWindow_IsSlotInPlayerInventory00); + tolua_function(tolua_S,"GetWindowTitle",tolua_AllToLua_cWindow_GetWindowTitle00); + tolua_function(tolua_S,"SetWindowTitle",tolua_AllToLua_cWindow_SetWindowTitle00); + tolua_function(tolua_S,"SetProperty",tolua_AllToLua_cWindow_SetProperty00); + tolua_function(tolua_S,"SetProperty",tolua_AllToLua_cWindow_SetProperty01); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cLuaWindow","cLuaWindow","cWindow",tolua_collect_cLuaWindow); + #else + tolua_cclass(tolua_S,"cLuaWindow","cLuaWindow","cWindow",NULL); + #endif + tolua_beginmodule(tolua_S,"cLuaWindow"); + tolua_function(tolua_S,"new",tolua_AllToLua_cLuaWindow_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cLuaWindow_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cLuaWindow_new00_local); + tolua_function(tolua_S,"delete",tolua_AllToLua_cLuaWindow_delete00); + tolua_function(tolua_S,"GetContents",tolua_AllToLua_cLuaWindow_GetContents00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cMonster","cMonster","cPawn",NULL); + tolua_beginmodule(tolua_S,"cMonster"); + tolua_constant(tolua_S,"mtInvalidType",cMonster::mtInvalidType); + tolua_constant(tolua_S,"mtBat",cMonster::mtBat); + tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze); + tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider); + tolua_constant(tolua_S,"mtChicken",cMonster::mtChicken); + tolua_constant(tolua_S,"mtCow",cMonster::mtCow); + tolua_constant(tolua_S,"mtCreeper",cMonster::mtCreeper); + tolua_constant(tolua_S,"mtEnderDragon",cMonster::mtEnderDragon); + tolua_constant(tolua_S,"mtEnderman",cMonster::mtEnderman); + tolua_constant(tolua_S,"mtGhast",cMonster::mtGhast); + tolua_constant(tolua_S,"mtGiant",cMonster::mtGiant); + tolua_constant(tolua_S,"mtHorse",cMonster::mtHorse); + tolua_constant(tolua_S,"mtIronGolem",cMonster::mtIronGolem); + tolua_constant(tolua_S,"mtMagmaCube",cMonster::mtMagmaCube); + tolua_constant(tolua_S,"mtMooshroom",cMonster::mtMooshroom); + tolua_constant(tolua_S,"mtOcelot",cMonster::mtOcelot); + tolua_constant(tolua_S,"mtPig",cMonster::mtPig); + tolua_constant(tolua_S,"mtSheep",cMonster::mtSheep); + tolua_constant(tolua_S,"mtSilverfish",cMonster::mtSilverfish); + tolua_constant(tolua_S,"mtSkeleton",cMonster::mtSkeleton); + tolua_constant(tolua_S,"mtSlime",cMonster::mtSlime); + tolua_constant(tolua_S,"mtSnowGolem",cMonster::mtSnowGolem); + tolua_constant(tolua_S,"mtSpider",cMonster::mtSpider); + tolua_constant(tolua_S,"mtSquid",cMonster::mtSquid); + tolua_constant(tolua_S,"mtVillager",cMonster::mtVillager); + tolua_constant(tolua_S,"mtWitch",cMonster::mtWitch); + tolua_constant(tolua_S,"mtWither",cMonster::mtWither); + tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf); + tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie); + tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman); + tolua_constant(tolua_S,"mfHostile",cMonster::mfHostile); + tolua_constant(tolua_S,"mfPassive",cMonster::mfPassive); + tolua_constant(tolua_S,"mfAmbient",cMonster::mfAmbient); + tolua_constant(tolua_S,"mfWater",cMonster::mfWater); + tolua_constant(tolua_S,"mfMaxplusone",cMonster::mfMaxplusone); + tolua_function(tolua_S,"GetMobType",tolua_AllToLua_cMonster_GetMobType00); + tolua_function(tolua_S,"GetMobFamily",tolua_AllToLua_cMonster_GetMobFamily00); + tolua_function(tolua_S,"MobTypeToString",tolua_AllToLua_cMonster_MobTypeToString00); + tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_cMonster_StringToMobType00); + tolua_function(tolua_S,"FamilyFromType",tolua_AllToLua_cMonster_FamilyFromType00); + tolua_function(tolua_S,"GetSpawnDelay",tolua_AllToLua_cMonster_GetSpawnDelay00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cLineBlockTracer","cLineBlockTracer","",NULL); + tolua_beginmodule(tolua_S,"cLineBlockTracer"); + tolua_endmodule(tolua_S); + tolua_endmodule(tolua_S); + return 1; +} + + +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501 + TOLUA_API int luaopen_AllToLua (lua_State* tolua_S) { + return tolua_AllToLua_open(tolua_S); +}; +#endif + diff --git a/src/Bindings.h b/src/Bindings.h new file mode 100644 index 000000000..13f398a4d --- /dev/null +++ b/src/Bindings.h @@ -0,0 +1,8 @@ +/* +** Lua binding: AllToLua +** Generated automatically by tolua++-1.0.92 on 11/15/13 10:14:20. +*/ + +/* Exported function */ +TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); + diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp new file mode 100644 index 000000000..5c15adfef --- /dev/null +++ b/src/BlockArea.cpp @@ -0,0 +1,2124 @@ + +// BlockArea.cpp + +// Implements the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries +// The object also supports writing the blockdata back into cWorld, even into other coords + +#include "Globals.h" +#include "BlockArea.h" +#include "World.h" +#include "OSSupport/GZipFile.h" +#include "WorldStorage/FastNBT.h" +#include "Blocks/BlockHandler.h" + + + + + +// This wild construct allows us to pass a function argument and still have it inlined by the compiler :) +/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function +template void InternalMergeBlocks( + BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes, + NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas, + int a_SizeX, int a_SizeY, int a_SizeZ, + int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ, + int a_DstOffX, int a_DstOffY, int a_DstOffZ, + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, + int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ, + Combinator a_Combinator +) +{ + for (int y = 0; y < a_SizeY; y++) + { + int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ; + int DstBaseY = (y + a_DstOffY) * a_DstSizeX * a_DstSizeZ; + for (int z = 0; z < a_SizeZ; z++) + { + int SrcBaseZ = SrcBaseY + (z + a_SrcOffZ) * a_SrcSizeX; + int DstBaseZ = DstBaseY + (z + a_DstOffZ) * a_DstSizeX; + int SrcIdx = SrcBaseZ + a_SrcOffX; + int DstIdx = DstBaseZ + a_DstOffX; + for (int x = 0; x < a_SizeX; x++) + { + a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]); + ++DstIdx; + ++SrcIdx; + } // for x + } // for z + } // for y +} + + + + + +/// Combinator used for cBlockArea::msOverwrite merging +static void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + a_DstType = a_SrcType; + a_DstMeta = a_SrcMeta; +} + + + + + +/// Combinator used for cBlockArea::msFillAir merging +static void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + if (a_DstType == E_BLOCK_AIR) + { + a_DstType = a_SrcType; + a_DstMeta = a_SrcMeta; + } + // "else" is the default, already in place +} + + + + + +/// Combinator used for cBlockArea::msImprint merging +static void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + if (a_SrcType != E_BLOCK_AIR) + { + a_DstType = a_SrcType; + a_DstMeta = a_SrcMeta; + } + // "else" is the default, already in place +} + + + + + +/// Combinator used for cBlockArea::msLake merging +static void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + // Sponge is the NOP block + if (a_SrcType == E_BLOCK_SPONGE) + { + return; + } + + // Air is always hollowed out + if (a_SrcType == E_BLOCK_AIR) + { + a_DstType = E_BLOCK_AIR; + a_DstMeta = 0; + return; + } + + // Water and lava are never overwritten + switch (a_DstType) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + return; + } + } + + // Water and lava always overwrite + switch (a_SrcType) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + a_DstType = a_SrcType; + a_DstMeta = a_DstMeta; + return; + } + } + + if (a_SrcType == E_BLOCK_STONE) + { + switch (a_DstType) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_MYCELIUM: + { + a_DstType = E_BLOCK_STONE; + a_DstMeta = 0; + return; + } + } + } + // Everything else is left as it is +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBlockArea: + +cBlockArea::cBlockArea(void) : + m_SizeX(0), + m_SizeY(0), + m_SizeZ(0), + m_BlockTypes(NULL), + m_BlockMetas(NULL), + m_BlockLight(NULL), + m_BlockSkyLight(NULL) +{ +} + + + + + +cBlockArea::~cBlockArea() +{ + Clear(); +} + + + + + +void cBlockArea::Clear(void) +{ + delete[] m_BlockTypes; m_BlockTypes = NULL; + delete[] m_BlockMetas; m_BlockMetas = NULL; + delete[] m_BlockLight; m_BlockLight = NULL; + delete[] m_BlockSkyLight; m_BlockSkyLight = NULL; + m_SizeX = 0; + m_SizeY = 0; + m_SizeZ = 0; +} + + + + + +void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes) +{ + Clear(); + int BlockCount = a_SizeX * a_SizeY * a_SizeZ; + if ((a_DataTypes & baTypes) != 0) + { + m_BlockTypes = new BLOCKTYPE[BlockCount]; + for (int i = 0; i < BlockCount; i++) + { + m_BlockTypes[i] = E_BLOCK_AIR; + } + } + if ((a_DataTypes & baMetas) != 0) + { + m_BlockMetas = new NIBBLETYPE[BlockCount]; + for (int i = 0; i < BlockCount; i++) + { + m_BlockMetas[i] = 0; + } + } + if ((a_DataTypes & baLight) != 0) + { + m_BlockLight = new NIBBLETYPE[BlockCount]; + for (int i = 0; i < BlockCount; i++) + { + m_BlockLight[i] = 0; + } + } + if ((a_DataTypes & baSkyLight) != 0) + { + m_BlockSkyLight = new NIBBLETYPE[BlockCount]; + for (int i = 0; i < BlockCount; i++) + { + m_BlockSkyLight[i] = 0x0f; + } + } + m_SizeX = a_SizeX; + m_SizeY = a_SizeY; + m_SizeZ = a_SizeZ; + m_OriginX = 0; + m_OriginY = 0; + m_OriginZ = 0; +} + + + + + +void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ) +{ + m_OriginX = a_OriginX; + m_OriginY = a_OriginY; + m_OriginZ = a_OriginZ; +} + + + + + +bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes) +{ + // Normalize the coords: + if (a_MinBlockX > a_MaxBlockX) + { + std::swap(a_MinBlockX, a_MaxBlockX); + } + if (a_MinBlockY > a_MaxBlockY) + { + std::swap(a_MinBlockY, a_MaxBlockY); + } + if (a_MinBlockZ > a_MaxBlockZ) + { + std::swap(a_MinBlockZ, a_MaxBlockZ); + } + + // Include the Max coords: + a_MaxBlockX += 1; + a_MaxBlockY += 1; + a_MaxBlockZ += 1; + + // Check coords validity: + if (a_MinBlockY < 0) + { + LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__); + a_MinBlockY = 0; + } + else if (a_MinBlockY >= cChunkDef::Height) + { + LOGWARNING("%s: MinBlockY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MinBlockY = cChunkDef::Height - 1; + } + if (a_MaxBlockY < 0) + { + LOGWARNING("%s: MaxBlockY less than zero, adjusting to zero", __FUNCTION__); + a_MaxBlockY = 0; + } + else if (a_MaxBlockY >= cChunkDef::Height) + { + LOGWARNING("%s: MaxBlockY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MaxBlockY = cChunkDef::Height - 1; + } + + // Allocate the needed memory: + Clear(); + if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes)) + { + return false; + } + m_OriginX = a_MinBlockX; + m_OriginY = a_MinBlockY; + m_OriginZ = a_MinBlockZ; + cChunkReader Reader(*this); + + // Convert block coords to chunks coords: + int MinChunkX, MaxChunkX; + int MinChunkZ, MaxChunkZ; + cChunkDef::AbsoluteToRelative(a_MinBlockX, a_MinBlockY, a_MinBlockZ, MinChunkX, MinChunkZ); + cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ); + + // Query block data: + if (!a_World->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader)) + { + Clear(); + return false; + } + + return true; +} + + + + + +bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +{ + ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have? + a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have + + // Check coords validity: + if (a_MinBlockY < 0) + { + LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__); + a_MinBlockY = 0; + } + else if (a_MinBlockY >= cChunkDef::Height - m_SizeY) + { + LOGWARNING("%s: MinBlockY + m_SizeY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MinBlockY = cChunkDef::Height - m_SizeY - 1; + } + + return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); +} + + + + + +void cBlockArea::CopyTo(cBlockArea & a_Into) const +{ + if (&a_Into == this) + { + LOGWARNING("Trying to copy a cBlockArea into self, ignoring."); + return; + } + + a_Into.Clear(); + a_Into.SetSize(m_SizeX, m_SizeY, m_SizeZ, GetDataTypes()); + a_Into.m_OriginX = m_OriginX; + a_Into.m_OriginY = m_OriginY; + a_Into.m_OriginZ = m_OriginZ; + int BlockCount = GetBlockCount(); + if (HasBlockTypes()) + { + memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE)); + } + if (HasBlockMetas()) + { + memcpy(a_Into.m_BlockMetas, m_BlockMetas, BlockCount * sizeof(NIBBLETYPE)); + } + if (HasBlockLights()) + { + memcpy(a_Into.m_BlockLight, m_BlockLight, BlockCount * sizeof(NIBBLETYPE)); + } + if (HasBlockSkyLights()) + { + memcpy(a_Into.m_BlockSkyLight, m_BlockSkyLight, BlockCount * sizeof(NIBBLETYPE)); + } +} + + + + + +void cBlockArea::CopyFrom(const cBlockArea & a_From) +{ + a_From.CopyTo(*this); +} + + + + + +void cBlockArea::DumpToRawFile(const AString & a_FileName) +{ + cFile f; + if (!f.Open(a_FileName, cFile::fmWrite)) + { + LOGWARNING("cBlockArea: Cannot open file \"%s\" for raw dump", a_FileName.c_str()); + return; + } + UInt32 SizeX = ntohl(m_SizeX); + UInt32 SizeY = ntohl(m_SizeY); + UInt32 SizeZ = ntohl(m_SizeZ); + f.Write(&SizeX, 4); + f.Write(&SizeY, 4); + f.Write(&SizeZ, 4); + unsigned char DataTypes = GetDataTypes(); + f.Write(&DataTypes, 1); + int NumBlocks = GetBlockCount(); + if (HasBlockTypes()) + { + f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE)); + } + if (HasBlockMetas()) + { + f.Write(m_BlockMetas, NumBlocks); + } + if (HasBlockLights()) + { + f.Write(m_BlockLight, NumBlocks); + } + if (HasBlockSkyLights()) + { + f.Write(m_BlockSkyLight, NumBlocks); + } +} + + + + + +bool cBlockArea::LoadFromSchematicFile(const AString & a_FileName) +{ + // Un-GZip the contents: + AString Contents; + cGZipFile File; + if (!File.Open(a_FileName, cGZipFile::fmRead)) + { + LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str()); + return false; + } + int NumBytesRead = File.ReadRestOfFile(Contents); + if (NumBytesRead < 0) + { + LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead); + return false; + } + File.Close(); + + // Parse the NBT: + cParsedNBT NBT(Contents.data(), Contents.size()); + if (!NBT.IsValid()) + { + LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str()); + return false; + } + + return LoadFromSchematicNBT(NBT); +} + + + + + +bool cBlockArea::SaveToSchematicFile(const AString & a_FileName) +{ + cFastNBTWriter Writer("Schematic"); + Writer.AddShort("Width", m_SizeX); + Writer.AddShort("Height", m_SizeY); + Writer.AddShort("Length", m_SizeZ); + Writer.AddString("Materials", "Alpha"); + if (HasBlockTypes()) + { + Writer.AddByteArray("Blocks", (const char *)m_BlockTypes, GetBlockCount()); + } + else + { + AString Dummy(GetBlockCount(), 0); + Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size()); + } + if (HasBlockMetas()) + { + Writer.AddByteArray("Data", (const char *)m_BlockMetas, GetBlockCount()); + } + else + { + AString Dummy(GetBlockCount(), 0); + Writer.AddByteArray("Data", Dummy.data(), Dummy.size()); + } + // TODO: Save entities and block entities + Writer.BeginList("Entities", TAG_Compound); + Writer.EndList(); + Writer.BeginList("TileEntities", TAG_Compound); + Writer.EndList(); + Writer.Finish(); + + // Save to file + cGZipFile File; + if (!File.Open(a_FileName, cGZipFile::fmWrite)) + { + LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str()); + return false; + } + if (!File.Write(Writer.GetResult())) + { + LOG("Cannot write data to file \"%s\".", a_FileName.c_str()); + return false; + } + return true; +} + + + + + +void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ) +{ + if ( + (a_AddMinX + a_SubMaxX >= m_SizeX) || + (a_AddMinY + a_SubMaxY >= m_SizeY) || + (a_AddMinZ + a_SubMaxZ >= m_SizeZ) + ) + { + LOGWARNING("cBlockArea:Crop called with more croping than the dimensions: %d x %d x %d with cropping %d, %d and %d", + m_SizeX, m_SizeY, m_SizeZ, + a_AddMinX + a_SubMaxX, a_AddMinY + a_SubMaxY, a_AddMinZ + a_SubMaxZ + ); + return; + } + + if (HasBlockTypes()) + { + CropBlockTypes(a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); + } + if (HasBlockMetas()) + { + CropNibbles(m_BlockMetas, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); + } + if (HasBlockLights()) + { + CropNibbles(m_BlockLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); + } + if (HasBlockSkyLights()) + { + CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); + } + m_OriginX += a_AddMinX; + m_OriginY += a_AddMinY; + m_OriginZ += a_AddMinZ; + m_SizeX -= a_AddMinX + a_SubMaxX; + m_SizeY -= a_AddMinY + a_SubMaxY; + m_SizeZ -= a_AddMinZ + a_SubMaxZ; +} + + + + + +void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) +{ + if (HasBlockTypes()) + { + ExpandBlockTypes(a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); + } + if (HasBlockMetas()) + { + ExpandNibbles(m_BlockMetas, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); + } + if (HasBlockLights()) + { + ExpandNibbles(m_BlockLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); + } + if (HasBlockSkyLights()) + { + ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); + } + m_OriginX -= a_SubMinX; + m_OriginY -= a_SubMinY; + m_OriginZ -= a_SubMinZ; + m_SizeX += a_SubMinX + a_AddMaxX; + m_SizeY += a_SubMinY + a_AddMaxY; + m_SizeZ += a_SubMinZ + a_AddMaxZ; +} + + + + + +void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy) +{ + // Block types are compulsory, block metas are voluntary + if (!HasBlockTypes() || !a_Src.HasBlockTypes()) + { + LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__); + return; + } + + // Dst is *this, Src is a_Src + int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading + int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing + int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy + + int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading + int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing + int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy + + int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading + int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing + int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy + + const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas(); + NIBBLETYPE * DstMetas = m_BlockMetas; + bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL)); + + if (IsDummyMetas) + { + SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()]; + DstMetas = new NIBBLETYPE[GetBlockCount()]; + } + + switch (a_Strategy) + { + case msOverwrite: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_SizeX, m_SizeY, m_SizeZ, + MergeCombinatorOverwrite + ); + break; + } // case msOverwrite + + case msFillAir: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_SizeX, m_SizeY, m_SizeZ, + MergeCombinatorFillAir + ); + break; + } // case msFillAir + + case msImprint: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_SizeX, m_SizeY, m_SizeZ, + MergeCombinatorImprint + ); + break; + } // case msImprint + + case msLake: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_SizeX, m_SizeY, m_SizeZ, + MergeCombinatorLake + ); + break; + } // case msLake + + default: + { + LOGWARNING("Unknown block area merge strategy: %d", a_Strategy); + ASSERT(!"Unknown block area merge strategy"); + break; + } + } // switch (a_Strategy) + + if (IsDummyMetas) + { + delete[] SrcMetas; + delete[] DstMetas; + } +} + + + + + +void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight) +{ + if ((a_DataTypes & GetDataTypes()) != a_DataTypes) + { + LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)", + __FUNCTION__, a_DataTypes, GetDataTypes() + ); + a_DataTypes = a_DataTypes & GetDataTypes(); + } + + int BlockCount = GetBlockCount(); + if ((a_DataTypes & baTypes) != 0) + { + for (int i = 0; i < BlockCount; i++) + { + m_BlockTypes[i] = a_BlockType; + } + } + if ((a_DataTypes & baMetas) != 0) + { + for (int i = 0; i < BlockCount; i++) + { + m_BlockMetas[i] = a_BlockMeta; + } + } + if ((a_DataTypes & baLight) != 0) + { + for (int i = 0; i < BlockCount; i++) + { + m_BlockLight[i] = a_BlockLight; + } + } + if ((a_DataTypes & baSkyLight) != 0) + { + for (int i = 0; i < BlockCount; i++) + { + m_BlockSkyLight[i] = a_BlockSkyLight; + } + } +} + + + + + +void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight +) +{ + if ((a_DataTypes & GetDataTypes()) != a_DataTypes) + { + LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)", + __FUNCTION__, a_DataTypes, GetDataTypes() + ); + a_DataTypes = a_DataTypes & GetDataTypes(); + } + + if ((a_DataTypes & baTypes) != 0) + { + for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) + { + m_BlockTypes[MakeIndex(x, y, z)] = a_BlockType; + } // for x, z, y + } + if ((a_DataTypes & baMetas) != 0) + { + for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) + { + m_BlockMetas[MakeIndex(x, y, z)] = a_BlockMeta; + } // for x, z, y + } + if ((a_DataTypes & baLight) != 0) + { + for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) + { + m_BlockLight[MakeIndex(x, y, z)] = a_BlockLight; + } // for x, z, y + } + if ((a_DataTypes & baSkyLight) != 0) + { + for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) + { + m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight; + } // for x, z, y + } +} + + + + + +void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight +) +{ + // Bresenham-3D algorithm for drawing lines: + int dx = abs(a_RelX2 - a_RelX1); + int dy = abs(a_RelY2 - a_RelY1); + int dz = abs(a_RelZ2 - a_RelZ1); + int sx = (a_RelX1 < a_RelX2) ? 1 : -1; + int sy = (a_RelY1 < a_RelY2) ? 1 : -1; + int sz = (a_RelZ1 < a_RelZ2) ? 1 : -1; + int err = dx - dz; + + if (dx >= std::max(dy, dz)) // x dominant + { + int yd = dy - dx / 2; + int zd = dz - dx / 2; + + while (true) + { + RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight); + + if (a_RelX1 == a_RelX2) + { + break; + } + + if (yd >= 0) // move along y + { + a_RelY1 += sy; + yd -= dx; + } + + if (zd >= 0) // move along z + { + a_RelZ1 += sz; + zd -= dx; + } + + // move along x + a_RelX1 += sx; + yd += dy; + zd += dz; + } + } + else if (dy >= std::max(dx, dz)) // y dominant + { + int xd = dx - dy / 2; + int zd = dz - dy / 2; + + while (true) + { + RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight); + + if (a_RelY1 == a_RelY2) + { + break; + } + + if (xd >= 0) // move along x + { + a_RelX1 += sx; + xd -= dy; + } + + if (zd >= 0) // move along z + { + a_RelZ1 += sz; + zd -= dy; + } + + // move along y + a_RelY1 += sy; + xd += dx; + zd += dz; + } + } + else + { + // z dominant + ASSERT(dz >= std::max(dx, dy)); + int xd = dx - dz / 2; + int yd = dy - dz / 2; + + while (true) + { + RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight); + + if (a_RelZ1 == a_RelZ2) + { + break; + } + + if (xd >= 0) // move along x + { + a_RelX1 += sx; + xd -= dz; + } + + if (yd >= 0) // move along y + { + a_RelY1 += sy; + yd -= dz; + } + + // move along z + a_RelZ1 += sz; + xd += dx; + yd += dy; + } + } // if (which dimension is dominant) +} + + + + + +void cBlockArea::RotateCCW(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to rotate, just use the NoMeta function + RotateCCWNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time: + BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; + NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = m_SizeX - x - 1; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = z; + for (int y = 0; y < m_SizeY; y++) + { + int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ; + int OldIdx = MakeIndex(x, y, z); + NewTypes[NewIdx] = m_BlockTypes[OldIdx]; + NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]); + } // for y + } // for z + } // for x + std::swap(m_BlockTypes, NewTypes); + std::swap(m_BlockMetas, NewMetas); + delete[] NewTypes; + delete[] NewMetas; + + std::swap(m_SizeX, m_SizeZ); +} + + + + + +void cBlockArea::RotateCW(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to rotate, just use the NoMeta function + RotateCWNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time: + BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; + NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = x; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = m_SizeZ - z - 1; + for (int y = 0; y < m_SizeY; y++) + { + int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ; + int OldIdx = MakeIndex(x, y, z); + NewTypes[NewIdx] = m_BlockTypes[OldIdx]; + NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]); + } // for y + } // for z + } // for x + std::swap(m_BlockTypes, NewTypes); + std::swap(m_BlockMetas, NewMetas); + delete[] NewTypes; + delete[] NewMetas; + + std::swap(m_SizeX, m_SizeZ); +} + + + + + +void cBlockArea::MirrorXY(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to mirror, just use the NoMeta function + MirrorXYNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: + int HalfZ = m_SizeZ / 2; + int MaxZ = m_SizeZ - 1; + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < HalfZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + int Idx1 = MakeIndex(x, y, z); + int Idx2 = MakeIndex(x, y, MaxZ - z); + std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); + NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXY(m_BlockMetas[Idx1]); + NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXY(m_BlockMetas[Idx2]); + m_BlockMetas[Idx1] = Meta2; + m_BlockMetas[Idx2] = Meta1; + } // for x + } // for z + } // for y +} + + + + + +void cBlockArea::MirrorXZ(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to mirror, just use the NoMeta function + MirrorXZNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: + int HalfY = m_SizeY / 2; + int MaxY = m_SizeY - 1; + for (int y = 0; y < HalfY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + int Idx1 = MakeIndex(x, y, z); + int Idx2 = MakeIndex(x, MaxY - y, z); + std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); + NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXZ(m_BlockMetas[Idx1]); + NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXZ(m_BlockMetas[Idx2]); + m_BlockMetas[Idx1] = Meta2; + m_BlockMetas[Idx2] = Meta1; + } // for x + } // for z + } // for y +} + + + + + +void cBlockArea::MirrorYZ(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to mirror, just use the NoMeta function + MirrorYZNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: + int HalfX = m_SizeX / 2; + int MaxX = m_SizeX - 1; + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < HalfX; x++) + { + int Idx1 = MakeIndex(x, y, z); + int Idx2 = MakeIndex(MaxX - x, y, z); + std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); + NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]); + NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]); + m_BlockMetas[Idx1] = Meta2; + m_BlockMetas[Idx2] = Meta1; + } // for x + } // for z + } // for y +} + + + + + +void cBlockArea::RotateCCWNoMeta(void) +{ + if (HasBlockTypes()) + { + BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = m_SizeX - x - 1; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = z; + for (int y = 0; y < m_SizeY; y++) + { + NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; + } // for y + } // for z + } // for x + std::swap(m_BlockTypes, NewTypes); + delete[] NewTypes; + } + if (HasBlockMetas()) + { + NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = m_SizeX - x - 1; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = z; + for (int y = 0; y < m_SizeY; y++) + { + NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; + } // for y + } // for z + } // for x + std::swap(m_BlockMetas, NewMetas); + delete[] NewMetas; + } + std::swap(m_SizeX, m_SizeZ); +} + + + + + +void cBlockArea::RotateCWNoMeta(void) +{ + if (HasBlockTypes()) + { + BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = m_SizeZ - z - 1; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = x; + for (int y = 0; y < m_SizeY; y++) + { + NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; + } // for y + } // for x + } // for z + std::swap(m_BlockTypes, NewTypes); + delete[] NewTypes; + } + if (HasBlockMetas()) + { + NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = m_SizeZ - z - 1; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = x; + for (int y = 0; y < m_SizeY; y++) + { + NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; + } // for y + } // for x + } // for z + std::swap(m_BlockMetas, NewMetas); + delete[] NewMetas; + } + std::swap(m_SizeX, m_SizeZ); +} + + + + + +void cBlockArea::MirrorXYNoMeta(void) +{ + int HalfZ = m_SizeZ / 2; + int MaxZ = m_SizeZ - 1; + if (HasBlockTypes()) + { + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < HalfZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, y, MaxZ - z)]); + } // for x + } // for z + } // for y + } // if (HasBlockTypes) + + if (HasBlockMetas()) + { + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < HalfZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, y, MaxZ - z)]); + } // for x + } // for z + } // for y + } // if (HasBlockMetas) +} + + + + + +void cBlockArea::MirrorXZNoMeta(void) +{ + int HalfY = m_SizeY / 2; + int MaxY = m_SizeY - 1; + if (HasBlockTypes()) + { + for (int y = 0; y < HalfY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, MaxY - y, z)]); + } // for x + } // for z + } // for y + } // if (HasBlockTypes) + + if (HasBlockMetas()) + { + for (int y = 0; y < HalfY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, MaxY - y, z)]); + } // for x + } // for z + } // for y + } // if (HasBlockMetas) +} + + + + + +void cBlockArea::MirrorYZNoMeta(void) +{ + int HalfX = m_SizeX / 2; + int MaxX = m_SizeX - 1; + if (HasBlockTypes()) + { + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < HalfX; x++) + { + std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(MaxX - x, y, z)]); + } // for x + } // for z + } // for y + } // if (HasBlockTypes) + + if (HasBlockMetas()) + { + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < HalfX; x++) + { + std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(MaxX - x, y, z)]); + } // for x + } // for z + } // for y + } // if (HasBlockMetas) +} + + + + + +void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) +{ + if (m_BlockTypes == NULL) + { + LOGWARNING("cBlockArea: BlockTypes have not been read!"); + return; + } + m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_BlockType; +} + + + + + +void cBlockArea::SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) +{ + SetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType); +} + + + + + +void cBlockArea::SetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta) +{ + SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockMeta, m_BlockMetas); +} + + + + + +void cBlockArea::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta) +{ + SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, m_BlockMetas); +} + + + + + +void cBlockArea::SetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight) +{ + SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockLight, m_BlockLight); +} + + + + + +void cBlockArea::SetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight) +{ + SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockLight, m_BlockLight); +} + + + + + +void cBlockArea::SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight) +{ + SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockSkyLight, m_BlockSkyLight); +} + + + + + +void cBlockArea::SetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight) +{ + SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockSkyLight, m_BlockSkyLight); +} + + + + + +BLOCKTYPE cBlockArea::GetRelBlockType(int a_RelX, int a_RelY, int a_RelZ) const +{ + if (m_BlockTypes == NULL) + { + LOGWARNING("cBlockArea: BlockTypes have not been read!"); + return E_BLOCK_AIR; + } + return m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)]; +} + + + + + +BLOCKTYPE cBlockArea::GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const +{ + return GetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ); +} + + + + + +NIBBLETYPE cBlockArea::GetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const +{ + return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockMetas); +} + + + + + +NIBBLETYPE cBlockArea::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) const +{ + return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockMetas); +} + + + + + +NIBBLETYPE cBlockArea::GetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ) const +{ + return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockLight); +} + + + + + +NIBBLETYPE cBlockArea::GetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) const +{ + return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockLight); +} + + + + + +NIBBLETYPE cBlockArea::GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const +{ + return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockSkyLight); +} + + + + + +NIBBLETYPE cBlockArea::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) const +{ + return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockSkyLight); +} + + + + + +void cBlockArea::SetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + SetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta); +} + + + + + +void cBlockArea::SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + int idx = MakeIndex(a_RelX, a_RelY, a_RelZ); + if (m_BlockTypes == NULL) + { + LOGWARNING("%s: BlockTypes not available but requested to be written to.", __FUNCTION__); + } + else + { + m_BlockTypes[idx] = a_BlockType; + } + if (m_BlockMetas == NULL) + { + LOGWARNING("%s: BlockMetas not available but requested to be written to.", __FUNCTION__); + } + else + { + m_BlockMetas[idx] = a_BlockMeta; + } +} + + + + + +void cBlockArea::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const +{ + return GetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta); +} + + + + + +void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const +{ + int idx = MakeIndex(a_RelX, a_RelY, a_RelZ); + if (m_BlockTypes == NULL) + { + LOGWARNING("cBlockArea: BlockTypes have not been read!"); + a_BlockType = E_BLOCK_AIR; + } + else + { + a_BlockType = m_BlockTypes[idx]; + } + + if (m_BlockMetas == NULL) + { + LOGWARNING("cBlockArea: BlockMetas have not been read!"); + a_BlockMeta = 0; + } + else + { + a_BlockMeta = m_BlockMetas[idx]; + } +} + + + + + +int cBlockArea::GetDataTypes(void) const +{ + int res = 0; + if (m_BlockTypes != NULL) + { + res |= baTypes; + } + if (m_BlockMetas != NULL) + { + res |= baMetas; + } + if (m_BlockLight != NULL) + { + res |= baLight; + } + if (m_BlockSkyLight != NULL) + { + res |= baSkyLight; + } + return res; +} + + + + + +bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes) +{ + ASSERT(m_BlockTypes == NULL); // Has been cleared + + if (a_DataTypes & baTypes) + { + m_BlockTypes = new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ]; + if (m_BlockTypes == NULL) + { + return false; + } + } + if (a_DataTypes & baMetas) + { + m_BlockMetas = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]; + if (m_BlockMetas == NULL) + { + delete[] m_BlockTypes; + return false; + } + } + if (a_DataTypes & baLight) + { + m_BlockLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]; + if (m_BlockLight == NULL) + { + delete[] m_BlockMetas; + delete[] m_BlockTypes; + return false; + } + } + if (a_DataTypes & baSkyLight) + { + m_BlockSkyLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]; + if (m_BlockSkyLight == NULL) + { + delete[] m_BlockLight; + delete[] m_BlockMetas; + delete[] m_BlockTypes; + return false; + } + } + m_SizeX = a_SizeX; + m_SizeY = a_SizeY; + m_SizeZ = a_SizeZ; + return true; +} + + + + + +int cBlockArea::MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const +{ + ASSERT(a_RelX >= 0); + ASSERT(a_RelX < m_SizeX); + ASSERT(a_RelY >= 0); + ASSERT(a_RelY < m_SizeY); + ASSERT(a_RelZ >= 0); + ASSERT(a_RelZ < m_SizeZ); + + return a_RelX + a_RelZ * m_SizeX + a_RelY * m_SizeX * m_SizeZ; +} + + + + + +void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array) +{ + if (a_Array == NULL) + { + LOGWARNING("cBlockArea: datatype has not been read!"); + return; + } + a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value; +} + + + + + +void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array) +{ + SetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Value, a_Array); +} + + + + + +NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const +{ + if (a_Array == NULL) + { + LOGWARNING("cBlockArea: datatype has not been read!"); + return 16; + } + return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)]; +} + + + + + +NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const +{ + return GetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Array); +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBlockArea::cChunkReader: + +cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) : + m_Area(a_Area), + m_OriginX(a_Area.m_OriginX), + m_OriginY(a_Area.m_OriginY), + m_OriginZ(a_Area.m_OriginZ) +{ +} + + + + + +void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc) +{ + int SizeY = m_Area.m_SizeY; + int MinY = m_OriginY; + + // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union) + // OffX, OffZ are the offsets of the current chunk data from the area origin + // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders + int SizeX = cChunkDef::Width; + int SizeZ = cChunkDef::Width; + int OffX, OffZ; + int BaseX, BaseZ; + OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX; + if (OffX < 0) + { + BaseX = -OffX; + SizeX += OffX; // SizeX is decreased, OffX is negative + OffX = 0; + } + else + { + BaseX = 0; + } + OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ; + if (OffZ < 0) + { + BaseZ = -OffZ; + SizeZ += OffZ; // SizeZ is decreased, OffZ is negative + OffZ = 0; + } + else + { + BaseZ = 0; + } + // If the chunk extends beyond the area in the X or Z axis, cut off the Size: + if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX) + { + SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX); + } + if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ) + { + SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ); + } + + for (int y = 0; y < SizeY; y++) + { + int ChunkY = MinY + y; + int AreaY = y; + for (int z = 0; z < SizeZ; z++) + { + int ChunkZ = BaseZ + z; + int AreaZ = OffZ + z; + for (int x = 0; x < SizeX; x++) + { + int ChunkX = BaseX + x; + int AreaX = OffX + x; + a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ); + } // for x + } // for z + } // for y +} + + + + + +bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ) +{ + m_CurrentChunkX = a_ChunkX; + m_CurrentChunkZ = a_ChunkZ; + return true; +} + + + + + +void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes) +{ + if (m_Area.m_BlockTypes == NULL) + { + // Don't want BlockTypes + return; + } + + int SizeY = m_Area.m_SizeY; + int MinY = m_OriginY; + + // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union) + // OffX, OffZ are the offsets of the current chunk data from the area origin + // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders + int SizeX = cChunkDef::Width; + int SizeZ = cChunkDef::Width; + int OffX, OffZ; + int BaseX, BaseZ; + OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX; + if (OffX < 0) + { + BaseX = -OffX; + SizeX += OffX; // SizeX is decreased, OffX is negative + OffX = 0; + } + else + { + BaseX = 0; + } + OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ; + if (OffZ < 0) + { + BaseZ = -OffZ; + SizeZ += OffZ; // SizeZ is decreased, OffZ is negative + OffZ = 0; + } + else + { + BaseZ = 0; + } + // If the chunk extends beyond the area in the X or Z axis, cut off the Size: + if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX) + { + SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX); + } + if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ) + { + SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ); + } + + for (int y = 0; y < SizeY; y++) + { + int ChunkY = MinY + y; + int AreaY = y; + for (int z = 0; z < SizeZ; z++) + { + int ChunkZ = BaseZ + z; + int AreaZ = OffZ + z; + for (int x = 0; x < SizeX; x++) + { + int ChunkX = BaseX + x; + int AreaX = OffX + x; + m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetBlock(a_BlockTypes, ChunkX, ChunkY, ChunkZ); + } // for x + } // for z + } // for y +} + + + + + +void cBlockArea::cChunkReader::BlockMeta(const NIBBLETYPE * a_BlockMetas) +{ + if (m_Area.m_BlockMetas == NULL) + { + // Don't want metas + return; + } + CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas); +} + + + + + +void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight) +{ + if (m_Area.m_BlockLight == NULL) + { + // Don't want light + return; + } + CopyNibbles(m_Area.m_BlockLight, a_BlockLight); +} + + + + + +void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) +{ + if (m_Area.m_BlockSkyLight == NULL) + { + // Don't want skylight + return; + } + CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight); +} + + + + + +void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ) +{ + int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX; + int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY; + int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ; + BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[NewSizeX * NewSizeY * NewSizeZ]; + int idx = 0; + for (int y = 0; y < NewSizeY; y++) + { + for (int z = 0; z < NewSizeZ; z++) + { + for (int x = 0; x < NewSizeX; x++) + { + int OldIndex = MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ); + NewBlockTypes[idx++] = m_BlockTypes[OldIndex]; + } // for x + } // for z + } // for y + delete m_BlockTypes; + m_BlockTypes = NewBlockTypes; +} + + + + + +void cBlockArea::CropNibbles(NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ) +{ + int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX; + int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY; + int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ; + NIBBLETYPE * NewNibbles = new NIBBLETYPE[NewSizeX * NewSizeY * NewSizeZ]; + int idx = 0; + for (int y = 0; y < NewSizeY; y++) + { + for (int z = 0; z < NewSizeZ; z++) + { + for (int x = 0; x < NewSizeX; x++) + { + NewNibbles[idx++] = a_Array[MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ)]; + } // for x + } // for z + } // for y + delete a_Array; + a_Array = NewNibbles; +} + + + + + +void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) +{ + int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX; + int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY; + int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ; + int BlockCount = NewSizeX * NewSizeY * NewSizeZ; + BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount]; + memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE)); + int OldIndex = 0; + for (int y = 0; y < m_SizeY; y++) + { + int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ; + for (int z = 0; z < m_SizeZ; z++) + { + int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX; + int idx = IndexBaseZ + a_SubMinX; + for (int x = 0; x < m_SizeX; x++) + { + NewBlockTypes[idx++] = m_BlockTypes[OldIndex++]; + } // for x + } // for z + } // for y + delete m_BlockTypes; + m_BlockTypes = NewBlockTypes; +} + + + + + +void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) +{ + int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX; + int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY; + int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ; + int BlockCount = NewSizeX * NewSizeY * NewSizeZ; + NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount]; + memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE)); + int OldIndex = 0; + for (int y = 0; y < m_SizeY; y++) + { + int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ; + for (int z = 0; z < m_SizeZ; z++) + { + int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX; + int idx = IndexBaseZ + a_SubMinX; + for (int x = 0; x < m_SizeX; x++) + { + NewNibbles[idx++] = a_Array[OldIndex++]; + } // for x + } // for z + } // for y + delete a_Array; + a_Array = NewNibbles; +} + + + + + +bool cBlockArea::LoadFromSchematicNBT(cParsedNBT & a_NBT) +{ + int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials"); + if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String)) + { + AString Materials = a_NBT.GetString(TMaterials); + if (Materials.compare("Alpha") != 0) + { + LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str()); + return false; + } + } + int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width"); + int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height"); + int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length"); + if ( + (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) || + (a_NBT.GetType(TSizeX) != TAG_Short) || + (a_NBT.GetType(TSizeY) != TAG_Short) || + (a_NBT.GetType(TSizeZ) != TAG_Short) + ) + { + LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)", + TSizeX, TSizeY, TSizeZ, + a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ) + ); + return false; + } + + int SizeX = a_NBT.GetShort(TSizeX); + int SizeY = a_NBT.GetShort(TSizeY); + int SizeZ = a_NBT.GetShort(TSizeZ); + if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1)) + { + LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ); + return false; + } + + int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks"); + int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data"); + if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray)) + { + LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes); + return false; + } + bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray); + + Clear(); + SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (baTypes | baMetas) : baTypes); + + // Copy the block types and metas: + int NumBytes = m_SizeX * m_SizeY * m_SizeZ; + if (a_NBT.GetDataLength(TBlockTypes) < NumBytes) + { + LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.", + NumBytes, a_NBT.GetDataLength(TBlockTypes) + ); + NumBytes = a_NBT.GetDataLength(TBlockTypes); + } + memcpy(m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes); + + if (AreMetasPresent) + { + int NumBytes = m_SizeX * m_SizeY * m_SizeZ; + if (a_NBT.GetDataLength(TBlockMetas) < NumBytes) + { + LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.", + NumBytes, a_NBT.GetDataLength(TBlockMetas) + ); + NumBytes = a_NBT.GetDataLength(TBlockMetas); + } + memcpy(m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes); + } + + return true; +} + + + + +void cBlockArea::RelSetData( + int a_RelX, int a_RelY, int a_RelZ, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight +) +{ + int Index = MakeIndex(a_RelX, a_RelY, a_RelZ); + if ((a_DataTypes & baTypes) != 0) + { + m_BlockTypes[Index] = a_BlockType; + } + if ((a_DataTypes & baMetas) != 0) + { + m_BlockMetas[Index] = a_BlockMeta; + } + if ((a_DataTypes & baLight) != 0) + { + m_BlockLight[Index] = a_BlockLight; + } + if ((a_DataTypes & baSkyLight) != 0) + { + m_BlockSkyLight[Index] = a_BlockSkyLight; + } +} + + + + diff --git a/src/BlockArea.h b/src/BlockArea.h new file mode 100644 index 000000000..075cc99ec --- /dev/null +++ b/src/BlockArea.h @@ -0,0 +1,310 @@ + +// BlockArea.h + +// Interfaces to the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries +// The object also supports writing the blockdata back into cWorld, even into other coords + +// NOTE: All Nibble values (meta, blocklight, skylight) are stored one-nibble-per-byte for faster access / editting! + + + + + +#pragma once + + + + + +// fwd: World.h +class cWorld; + +// fwd: FastNBT.h +class cParsedNBT; + + + + + +// tolua_begin +class cBlockArea +{ + // tolua_end + DISALLOW_COPY_AND_ASSIGN(cBlockArea); + // tolua_begin + +public: + + /// What data is to be queried (bit-mask) + enum + { + baTypes = 1, + baMetas = 2, + baLight = 4, + baSkyLight = 8, + } ; + + enum eMergeStrategy + { + msOverwrite, + msFillAir, + msImprint, + msLake, + } ; + + cBlockArea(void); + ~cBlockArea(); + + /// Clears the data stored to reclaim memory + void Clear(void); + + /** Creates a new area of the specified size and contents. + Origin is set to all zeroes. + BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light. + */ + void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas); + + /// Resets the origin. No other changes are made, contents are untouched. + void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ); + + /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive. + bool Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas); + + // TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write + // A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again + + /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all + bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas); + + /// Copies this object's contents into the specified BlockArea. + void CopyTo(cBlockArea & a_Into) const; + + /// Copies the contents from the specified BlockArea into this object. + void CopyFrom(const cBlockArea & a_From); + + /// For testing purposes only, dumps the area into a file. + void DumpToRawFile(const AString & a_FileName); + + /// Loads an area from a .schematic file. Returns true if successful + bool LoadFromSchematicFile(const AString & a_FileName); + + /// Saves the area into a .schematic file. Returns true if successful + bool SaveToSchematicFile(const AString & a_FileName); + + /// Crops the internal contents by the specified amount of blocks from each border. + void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); + + /// Expands the internal contents by the specified amount of blocks from each border + void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + + /** Merges another block area into this one, using the specified block combinating strategy + This function combines another BlockArea into the current object. + The strategy parameter specifies how individual blocks are combined together, using the table below. + + | area block | result | + | this | Src | msOverwrite | msFillAir | msImprint | + +------+-----+-------------+-----------+-----------+ + | air | air | air | air | air | + | A | air | air | A | A | + | air | B | B | B | B | + | A | B | B | A | B | + + So to sum up: + - msOverwrite completely overwrites all blocks with the Src's blocks + - msFillAir overwrites only those blocks that were air + - msImprint overwrites with only those blocks that are non-air + + Special strategies: + msLake (evaluate top-down, first match wins): + | area block | | + | this | Src | result | + +----------+--------+--------+ + | A | sponge | A | Sponge is the NOP block + | * | air | air | Air always gets hollowed out, even under the oceans + | water | * | water | Water is never overwritten + | lava | * | lava | Lava is never overwritten + | * | water | water | Water always overwrites anything + | * | lava | lava | Lava always overwrites anything + | dirt | stone | stone | Stone overwrites dirt + | grass | stone | stone | ... and grass + | mycelium | stone | stone | ... and mycelium + | A | stone | A | ... but nothing else + | A | * | A | Everything else is left as it is + + */ + void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy); + + /// Fills the entire block area with the specified data + void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f); + + /// Fills a cuboid inside the block area with the specified data + void FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, + NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f + ); + + /// Draws a line from between two points with the specified data + void RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, + NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f + ); + + /// Rotates the entire area counter-clockwise around the Y axis + void RotateCCW(void); + + /// Rotates the entire area clockwise around the Y axis + void RotateCW(void); + + /// Mirrors the entire area around the XY plane + void MirrorXY(void); + + /// Mirrors the entire area around the XZ plane + void MirrorXZ(void); + + /// Mirrors the entire area around the YZ plane + void MirrorYZ(void); + + /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta + void RotateCCWNoMeta(void); + + /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta + void RotateCWNoMeta(void); + + /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta + void MirrorXYNoMeta(void); + + /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta + void MirrorXZNoMeta(void); + + /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta + void MirrorYZNoMeta(void); + + // Setters: + void SetRelBlockType (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType); + void SetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + void SetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta); + void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta); + void SetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight); + void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight); + void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight); + void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight); + + // Getters: + BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const; + BLOCKTYPE GetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ) const; + NIBBLETYPE GetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ) const; + NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ) const; + NIBBLETYPE GetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ) const; + NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; + NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const; + NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; + + void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + + int GetSizeX(void) const { return m_SizeX; } + int GetSizeY(void) const { return m_SizeY; } + int GetSizeZ(void) const { return m_SizeZ; } + + int GetOriginX(void) const { return m_OriginX; } + int GetOriginY(void) const { return m_OriginY; } + int GetOriginZ(void) const { return m_OriginZ; } + + /// Returns the datatypes that are stored in the object (bitmask of baXXX values) + int GetDataTypes(void) const; + + bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); } + bool HasBlockMetas (void) const { return (m_BlockMetas != NULL); } + bool HasBlockLights (void) const { return (m_BlockLight != NULL); } + bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != NULL); } + + // tolua_end + + // Clients can use these for faster access to all blocktypes. Be careful though! + /// Returns the internal pointer to the block types + BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; } + NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block! + NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block! + NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block! + int GetBlockCount(void) const { return m_SizeX * m_SizeY * m_SizeZ; } + int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const; + +protected: + friend class cChunkDesc; + + class cChunkReader : + public cChunkDataCallback + { + public: + cChunkReader(cBlockArea & a_Area); + + protected: + cBlockArea & m_Area; + int m_OriginX; + int m_OriginY; + int m_OriginZ; + int m_CurrentChunkX; + int m_CurrentChunkZ; + + void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc); + + // cChunkDataCallback overrides: + virtual bool Coords (int a_ChunkX, int a_ChunkZ) override; + virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override; + virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override; + virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override; + virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override; + } ; + + typedef NIBBLETYPE * NIBBLEARRAY; + + + int m_OriginX; + int m_OriginY; + int m_OriginZ; + int m_SizeX; + int m_SizeY; + int m_SizeZ; + + BLOCKTYPE * m_BlockTypes; + NIBBLETYPE * m_BlockMetas; // Each meta is stored as a separate byte for faster access + NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access + NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access + + /// Clears the data stored and prepares a fresh new block area with the specified dimensions + bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes); + + // Basic Setters: + void SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); + void SetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); + + // Basic Getters: + NIBBLETYPE GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const; + NIBBLETYPE GetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const; + + // Crop helpers: + void CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); + void CropNibbles (NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); + + // Expand helpers: + void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + + /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful. + bool LoadFromSchematicNBT(cParsedNBT & a_NBT); + + /// Sets the specified datatypes at the specified location. + void RelSetData( + int a_RelX, int a_RelY, int a_RelZ, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight + ); + // tolua_begin +} ; +// tolua_end + + + + diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp new file mode 100644 index 000000000..41a488717 --- /dev/null +++ b/src/BlockEntities/BlockEntity.cpp @@ -0,0 +1,44 @@ + +// BlockEntity.cpp + +// Implements the cBlockEntity class that is the common ancestor for all block entities + +#include "Globals.h" +#include "BlockEntity.h" +#include "ChestEntity.h" +#include "DispenserEntity.h" +#include "DropperEntity.h" +#include "FurnaceEntity.h" +#include "HopperEntity.h" +#include "JukeboxEntity.h" +#include "NoteEntity.h" +#include "SignEntity.h" + + + + + +cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) +{ + switch (a_BlockType) + { + case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_DISPENSER: return new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); + case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); + case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + } + LOGD("%s: Requesting creation of an unknown block entity - block type %d (%s)", + __FUNCTION__, a_BlockType, ItemTypeToString(a_BlockType).c_str() + ); + return NULL; +} + + + + diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h new file mode 100644 index 000000000..a2de3160a --- /dev/null +++ b/src/BlockEntities/BlockEntity.h @@ -0,0 +1,101 @@ + +#pragma once + +#include "../ClientHandle.h" +#include "../World.h" + + + + + +namespace Json +{ + class Value; +}; + +class cPlayer; +class cPacket; + + + + + +// tolua_begin +class cBlockEntity +{ +protected: + cBlockEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + m_PosX(a_BlockX), + m_PosY(a_BlockY), + m_PosZ(a_BlockZ), + m_RelX(a_BlockX - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockX, cChunkDef::Width)), + m_RelZ(a_BlockZ - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockZ, cChunkDef::Width)), + m_BlockType(a_BlockType), + m_World(a_World) + { + } + +public: + // tolua_end + + virtual ~cBlockEntity() {}; // force a virtual destructor in all descendants + + virtual void Destroy(void) {}; + + void SetWorld(cWorld * a_World) + { + m_World = a_World; + } + + /// Creates a new block entity for the specified block type + /// If a_World is valid, then the entity is created bound to that world + /// Returns NULL for unknown block types + static cBlockEntity * CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World = NULL); + + // tolua_begin + + // Position, in absolute block coordinates: + int GetPosX(void) const { return m_PosX; } + int GetPosY(void) const { return m_PosY; } + int GetPosZ(void) const { return m_PosZ; } + + BLOCKTYPE GetBlockType(void) const { return m_BlockType; } + + cWorld * GetWorld(void) const {return m_World; } + + int GetChunkX(void) const { return FAST_FLOOR_DIV(m_PosX, cChunkDef::Width); } + int GetChunkZ(void) const { return FAST_FLOOR_DIV(m_PosZ, cChunkDef::Width); } + + int GetRelX(void) const { return m_RelX; } + int GetRelZ(void) const { return m_RelZ; } + + // tolua_end + + virtual void SaveToJson (Json::Value & a_Value) = 0; + + /// Called when a player uses this entity; should open the UI window + virtual void UsedBy( cPlayer * a_Player ) = 0; + + /** Sends the packet defining the block entity to the client specified. + To send to all eligible clients, use cWorld::BroadcastBlockEntity() + */ + virtual void SendTo(cClientHandle & a_Client) = 0; + + /// Ticks the entity; returns true if the chunk should be marked as dirty as a result of this ticking. By default does nothing. + virtual bool Tick(float a_Dt, cChunk & a_Chunk) { return false; } + +protected: + /// Position in absolute block coordinates + int m_PosX, m_PosY, m_PosZ; + + /// Position relative to the chunk, used to speed up ticking + int m_RelX, m_RelZ; + + BLOCKTYPE m_BlockType; + + cWorld * m_World; +} ; // tolua_export + + + + diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h new file mode 100644 index 000000000..0846ae17e --- /dev/null +++ b/src/BlockEntities/BlockEntityWithItems.h @@ -0,0 +1,86 @@ + +// BlockEntityWithItems.h + +// Declares the cBlockEntityWithItems class representing a common ancestor for all block entities that have an ItemGrid + + + + + +#pragma once + +#include "BlockEntity.h" +#include "../ItemGrid.h" + + + + + +// tolua_begin +class cBlockEntityWithItems : + public cBlockEntity + // tolua_end + // tolua doesn't seem to support multiple inheritance? + , public cItemGrid::cListener + // tolua_begin +{ + typedef cBlockEntity super; + +public: + // tolua_end + + cBlockEntityWithItems( + BLOCKTYPE a_BlockType, // Type of the block that the entity represents + int a_BlockX, int a_BlockY, int a_BlockZ, // Position of the block entity + int a_ItemGridWidth, int a_ItemGridHeight, // Dimensions of the ItemGrid + cWorld * a_World // Optional world to assign to the entity + ) : + super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Contents(a_ItemGridWidth, a_ItemGridHeight) + { + m_Contents.AddListener(*this); + } + + virtual void Destroy(void) override + { + // Drop the contents as pickups: + ASSERT(m_World != NULL); + cItems Pickups; + m_Contents.CopyToItems(Pickups); + m_Contents.Clear(); + m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); + } + + // tolua_begin + + const cItem & GetSlot(int a_SlotNum) const { return m_Contents.GetSlot(a_SlotNum); } + const cItem & GetSlot(int a_X, int a_Y) const { return m_Contents.GetSlot(a_X, a_Y); } + + void SetSlot(int a_SlotNum, const cItem & a_Item) { m_Contents.SetSlot(a_SlotNum, a_Item); } + void SetSlot(int a_X, int a_Y, const cItem & a_Item) { m_Contents.SetSlot(a_X, a_Y, a_Item); } + + /// Returns the ItemGrid used for storing the contents + cItemGrid & GetContents(void) { return m_Contents; } + + // tolua_end + + /// Const version of the GetContents() function for C++ type-safety + const cItemGrid & GetContents(void) const { return m_Contents; } + +protected: + cItemGrid m_Contents; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) + { + ASSERT(a_Grid == &m_Contents); + if (m_World != NULL) + { + m_World->MarkChunkDirty(GetChunkX(), GetChunkZ()); + } + } +} ; // tolua_export + + + + diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp new file mode 100644 index 000000000..ca2626bc9 --- /dev/null +++ b/src/BlockEntities/ChestEntity.cpp @@ -0,0 +1,172 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChestEntity.h" +#include "../Item.h" +#include "../Entities/Player.h" +#include "../UI/Window.h" +#include + + + + + +cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_CHEST, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World) +{ + cBlockEntityWindowOwner::SetBlockEntity(this); +} + + + + + +cChestEntity::~cChestEntity() +{ + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->OwnerDestroyed(); + } +} + + + + + +bool cChestEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + Json::Value AllSlots = a_Value.get("Slots", 0); + int SlotIdx = 0; + for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) + { + cItem Item; + Item.FromJson(*itr); + SetSlot(SlotIdx, Item); + SlotIdx++; + } + return true; +} + + + + + +void cChestEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + Json::Value AllSlots; + for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) + { + Json::Value Slot; + m_Contents.GetSlot(i).GetJson(Slot); + AllSlots.append(Slot); + } + a_Value["Slots"] = AllSlots; +} + + + + + +void cChestEntity::SendTo(cClientHandle & a_Client) +{ + // The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance + // All the actual handling is in the cWindow UI code that gets called when the chest is rclked + + UNUSED(a_Client); +} + + + + + +void cChestEntity::UsedBy(cPlayer * a_Player) +{ + // If the window is not created, open it anew: + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenNewWindow(); + Window = GetWindow(); + } + + // Open the window for the player: + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } + + // This is rather a hack + // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now + // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. + // The few false positives aren't much to worry about + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); + m_World->MarkChunkDirty(ChunkX, ChunkZ); +} + + + + + +void cChestEntity::OpenNewWindow(void) +{ + // Callback for opening together with neighbor chest: + class cOpenDouble : + public cChestCallback + { + cChestEntity * m_ThisChest; + public: + cOpenDouble(cChestEntity * a_ThisChest) : + m_ThisChest(a_ThisChest) + { + } + + virtual bool Item(cChestEntity * a_Chest) override + { + // The primary chest should eb the one with lesser X or Z coord: + cChestEntity * Primary = a_Chest; + cChestEntity * Secondary = m_ThisChest; + if ( + (Primary->GetPosX() > Secondary->GetPosX()) || + (Primary->GetPosZ() > Secondary->GetPosZ()) + ) + { + std::swap(Primary, Secondary); + } + m_ThisChest->OpenWindow(new cChestWindow(Primary, Secondary)); + return false; + } + } ; + + // Scan neighbors for adjacent chests: + cOpenDouble OpenDbl(this); + if ( + m_World->DoWithChestAt(m_PosX - 1, m_PosY, m_PosZ, OpenDbl) || + m_World->DoWithChestAt(m_PosX + 1, m_PosY, m_PosZ, OpenDbl) || + m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ - 1, OpenDbl) || + m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ + 1, OpenDbl) + ) + { + // The double-chest window has been opened in the callback + return; + } + + // There is no chest neighbor, open a single-chest window: + OpenWindow(new cChestWindow(this)); +} + + + + diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h new file mode 100644 index 000000000..4f2c21e91 --- /dev/null +++ b/src/BlockEntities/ChestEntity.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +namespace Json +{ + class Value; +}; + +class cClientHandle; +class cServer; +class cNBTData; + + + + + +class cChestEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 3, + ContentsWidth = 9, + } ; + + // tolua_end + + /// Constructor used for normal operation + cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + virtual ~cChestEntity(); + + static const char * GetClassStatic(void) { return "cChestEntity"; } + + bool LoadFromJson(const Json::Value & a_Value); + + // cBlockEntity overrides: + virtual void SaveToJson(Json::Value & a_Value) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual void UsedBy(cPlayer * a_Player) override; + + /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. + void OpenNewWindow(void); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/DispenserEntity.cpp b/src/BlockEntities/DispenserEntity.cpp new file mode 100644 index 000000000..374f3d6e3 --- /dev/null +++ b/src/BlockEntities/DispenserEntity.cpp @@ -0,0 +1,215 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "DispenserEntity.h" +#include "../Entities/Player.h" +#include "../Simulator/FluidSimulator.h" +#include "../Chunk.h" + + + + + +cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, a_World) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) +{ + int DispX = m_RelX; + int DispY = m_PosY; + int DispZ = m_RelZ; + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + AddDropSpenserDir(DispX, DispY, DispZ, Meta); + cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ); + if (DispChunk == NULL) + { + // Would dispense into / interact with a non-loaded chunk, ignore the tick + return; + } + BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ); + + // Dispense the item: + switch (m_Contents.GetSlot(a_SlotNum).m_ItemType) + { + case E_ITEM_BUCKET: + { + LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); + switch (DispBlock) + { + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); + } + break; + } + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_LAVA: + { + if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); + } + break; + } + default: + { + DropFromSlot(a_Chunk, a_SlotNum); + break; + } + } + break; + } // E_ITEM_BUCKET + + case E_ITEM_WATER_BUCKET: + { + LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); + if (EmptyLiquidBucket(DispBlock, a_SlotNum)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0); + } + else + { + DropFromSlot(a_Chunk, a_SlotNum); + } + break; + } + + case E_ITEM_LAVA_BUCKET: + { + LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); + if (EmptyLiquidBucket(DispBlock, a_SlotNum)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0); + } + else + { + DropFromSlot(a_Chunk, a_SlotNum); + } + break; + } + + case E_ITEM_SPAWN_EGG: + { + double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); + double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); + if (m_World->SpawnMob(MobX, DispY, MobZ, (cMonster::eType)m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + + case E_BLOCK_TNT: + { + // Spawn a primed TNT entity, if space allows: + if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) + { + double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); + double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); + m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 4, 0); // 4 seconds fuse, no initial velocity + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + + case E_ITEM_FLINT_AND_STEEL: + { + // Spawn fire if the block in front is air. + if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0); + m_Contents.SetSlot(a_SlotNum, m_Contents.GetSlot(a_SlotNum).m_ItemType, m_Contents.GetSlot(a_SlotNum).m_ItemCount, m_Contents.GetSlot(a_SlotNum).m_ItemDamage + 1); + // If the durability has run out destroy the item. + if (m_Contents.GetSlot(a_SlotNum).m_ItemDamage > 64) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + } + break; + } + + default: + { + DropFromSlot(a_Chunk, a_SlotNum); + break; + } + } // switch (ItemType) +} + + + + + + +bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType) +{ + cItem LiquidBucket(a_BucketItemType, 1); + if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) + { + // Special case: replacing one empty bucket with one full bucket + m_Contents.SetSlot(a_SlotNum, LiquidBucket); + return true; + } + + // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else + if (m_Contents.HowManyCanFit(LiquidBucket) < 1) + { + // Cannot fit into m_Contents + return false; + } + + m_Contents.ChangeSlotCount(a_SlotNum, -1); + m_Contents.AddItem(LiquidBucket); + return true; +} + + + + + +bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum) +{ + if ( + (a_BlockInFront != E_BLOCK_AIR) && + !IsBlockLiquid(a_BlockInFront) && + !cFluidSimulator::CanWashAway(a_BlockInFront) + ) + { + // Not a suitable block in front + return false; + } + + cItem EmptyBucket(E_ITEM_BUCKET, 1); + if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) + { + // Change the single full bucket present into a single empty bucket + m_Contents.SetSlot(a_SlotNum, EmptyBucket); + return true; + } + + // There are full buckets stacked at this slot, check if we can fit in the empty bucket + if (m_Contents.HowManyCanFit(EmptyBucket) < 1) + { + // The empty bucket wouldn't fit into m_Contents + return false; + } + + // The empty bucket fits in, remove one full bucket and add the empty one + m_Contents.ChangeSlotCount(a_SlotNum, -1); + m_Contents.AddItem(EmptyBucket); + return true; +} + + + + diff --git a/src/BlockEntities/DispenserEntity.h b/src/BlockEntities/DispenserEntity.h new file mode 100644 index 000000000..fdfe4e5b4 --- /dev/null +++ b/src/BlockEntities/DispenserEntity.h @@ -0,0 +1,38 @@ + +#pragma once + +#include "DropSpenserEntity.h" + + + + + +// tolua_begin +class cDispenserEntity : + public cDropSpenserEntity +{ + typedef cDropSpenserEntity super; + +public: + + // tolua_end + + /// Constructor used for normal operation + cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + static const char * GetClassStatic(void) { return "cDispenserEntity"; } + +private: + // cDropSpenser overrides: + virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; + + /// If such a bucket can fit, adds it to m_Contents and returns true + bool ScoopUpLiquid(int a_SlotNum, short a_BucketItemType); + + /// If the a_BlockInFront is liquidable and the empty bucket can fit, does the m_Contents processing and returns true + bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp new file mode 100644 index 000000000..823ed598f --- /dev/null +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -0,0 +1,266 @@ + +// DropSpenserEntity.cpp + +// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity +// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior + +#include "Globals.h" +#include "DropSpenserEntity.h" +#include "../Entities/Player.h" +#include "../Chunk.h" + + + + + +cDropSpenserEntity::cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_ShouldDropSpense(false), + m_IsPowered(false) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +cDropSpenserEntity::~cDropSpenserEntity() +{ + // Tell window its owner is destroyed + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->OwnerDestroyed(); + } +} + + + + + +void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction) +{ + switch (a_Direction) + { + case E_META_DROPSPENSER_FACING_YM: a_BlockY--; return; + case E_META_DROPSPENSER_FACING_YP: a_BlockY++; return; + case E_META_DROPSPENSER_FACING_ZM: a_BlockZ--; return; + case E_META_DROPSPENSER_FACING_ZP: a_BlockZ++; return; + case E_META_DROPSPENSER_FACING_XM: a_BlockX--; return; + case E_META_DROPSPENSER_FACING_XP: a_BlockX++; return; + } + LOGWARNING("%s: Unhandled direction: %d", __FUNCTION__, a_Direction); + return; +} + + + + + +void cDropSpenserEntity::DropSpense(cChunk & a_Chunk) +{ + // Pick one of the occupied slots: + int OccupiedSlots[9]; + int SlotsCnt = 0; + for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) + { + if (!m_Contents.GetSlot(i).IsEmpty()) + { + OccupiedSlots[SlotsCnt] = i; + SlotsCnt++; + } + } // for i - m_Contents[] + + if (SlotsCnt == 0) + { + // Nothing in the dropspenser, play the click sound + m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f); + return; + } + + int RandomSlot = m_World->GetTickRandomNumber(SlotsCnt - 1); + + // DropSpense the item, using the specialized behavior in the subclasses: + DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]); + + // Broadcast a smoke and click effects: + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + int SmokeDir = 0; + switch (Meta) + { + case E_META_DROPSPENSER_FACING_YP: SmokeDir = 4; break; // YP & YM don't have associated smoke dirs, just do 4 (centre of block) + case E_META_DROPSPENSER_FACING_YM: SmokeDir = 4; break; + case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break; + case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break; + case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break; + case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break; + } + m_World->BroadcastSoundParticleEffect(2000, m_PosX, m_PosY, m_PosZ, SmokeDir); + m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f); + + // Update the UI window, if open: + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->BroadcastWholeWindow(); + } +} + + + + + +void cDropSpenserEntity::Activate(void) +{ + m_ShouldDropSpense = true; +} + + + + + +void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered) +{ + if (a_IsPowered && !m_IsPowered) + { + Activate(); + } + m_IsPowered = a_IsPowered; +} + + + + + +bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (!m_ShouldDropSpense) + { + return false; + } + + m_ShouldDropSpense = false; + DropSpense(a_Chunk); + return true; +} + + + + + +bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + Json::Value AllSlots = a_Value.get("Slots", 0); + int SlotIdx = 0; + for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) + { + cItem Contents; + Contents.FromJson(*itr); + m_Contents.SetSlot(SlotIdx, Contents); + SlotIdx++; + if (SlotIdx >= m_Contents.GetNumSlots()) + { + return true; + } + } + + return true; +} + + + + + +void cDropSpenserEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + Json::Value AllSlots; + int NumSlots = m_Contents.GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + Json::Value Slot; + m_Contents.GetSlot(i).GetJson(Slot); + AllSlots.append(Slot); + } + a_Value["Slots"] = AllSlots; +} + + + + + +void cDropSpenserEntity::SendTo(cClientHandle & a_Client) +{ + // Nothing needs to be sent + UNUSED(a_Client); +} + + + + + +void cDropSpenserEntity::UsedBy(cPlayer * a_Player) +{ + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenWindow(new cDropSpenserWindow(m_PosX, m_PosY, m_PosZ, this)); + Window = GetWindow(); + } + + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } +} + + + + + +void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum) +{ + int DispX = m_PosX; + int DispY = m_PosY; + int DispZ = m_PosZ; + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + AddDropSpenserDir(DispX, DispY, DispZ, Meta); + + cItems Pickups; + Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum)); + + const int PickupSpeed = m_World->GetTickRandomNumber(4) + 2; // At least 2, at most 6 + int PickupSpeedX = 0, PickupSpeedY = 0, PickupSpeedZ = 0; + switch (Meta) + { + case E_META_DROPSPENSER_FACING_YP: PickupSpeedY = PickupSpeed; break; + case E_META_DROPSPENSER_FACING_YM: PickupSpeedY = -PickupSpeed; break; + case E_META_DROPSPENSER_FACING_XM: PickupSpeedX = -PickupSpeed; break; + case E_META_DROPSPENSER_FACING_XP: PickupSpeedX = PickupSpeed; break; + case E_META_DROPSPENSER_FACING_ZM: PickupSpeedZ = -PickupSpeed; break; + case E_META_DROPSPENSER_FACING_ZP: PickupSpeedZ = PickupSpeed; break; + } + + double MicroX, MicroY, MicroZ; + MicroX = DispX + 0.5; + MicroY = DispY + 0.4; // Slightly less than half, to accomodate actual texture hole on DropSpenser + MicroZ = DispZ + 0.5; + + + m_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ, PickupSpeedX, PickupSpeedY, PickupSpeedZ); +} + + + + diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h new file mode 100644 index 000000000..0e9039915 --- /dev/null +++ b/src/BlockEntities/DropSpenserEntity.h @@ -0,0 +1,89 @@ + +// DropSpenser.h + +// Declares the cDropSpenser class representing a common ancestor to the cDispenserEntity and cDropperEntity +// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior + + + + + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +namespace Json +{ + class Value; +} + +class cClientHandle; +class cServer; + + + + + +class cDropSpenserEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 3, + ContentsWidth = 3, + } ; + + // tolua_end + + cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + virtual ~cDropSpenserEntity(); + + static const char * GetClassStatic(void) { return "cDropSpenserEntity"; } + + bool LoadFromJson(const Json::Value & a_Value); + + // cBlockEntity overrides: + virtual void SaveToJson(Json::Value & a_Value) override; + virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual void UsedBy(cPlayer * a_Player) override; + + // tolua_begin + + /// Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups should materialize) + void AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction); + + /// Sets the dropspenser to dropspense an item in the next tick + void Activate(void); + + /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate + void SetRedstonePower(bool a_IsPowered); + + // tolua_end + +protected: + bool m_ShouldDropSpense; ///< If true, the dropspenser will dropspense an item in the next tick + bool m_IsPowered; ///< Set to true when the dropspenser receives redstone power. + + /// Does the actual work on dropspensing an item. Chooses the slot, calls DropSpenseFromSlot() and handles smoke / sound effects + void DropSpense(cChunk & a_Chunk); + + /// Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...) + virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) = 0; + + /// Helper function, drops one item from the specified slot (like a dropper) + void DropFromSlot(cChunk & a_Chunk, int a_SlotNum); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/DropperEntity.cpp b/src/BlockEntities/DropperEntity.cpp new file mode 100644 index 000000000..5d4a8ad97 --- /dev/null +++ b/src/BlockEntities/DropperEntity.cpp @@ -0,0 +1,32 @@ + +// DropperEntity.cpp + +// Implements the cRtopperEntity class representing a Dropper block entity + +#include "Globals.h" +#include "DropperEntity.h" +#include "../Entities/Player.h" +#include "../Simulator/FluidSimulator.h" + + + + + +cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, a_World) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +void cDropperEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) +{ + DropFromSlot(a_Chunk, a_SlotNum); +} + + + + diff --git a/src/BlockEntities/DropperEntity.h b/src/BlockEntities/DropperEntity.h new file mode 100644 index 000000000..8e07bc6f8 --- /dev/null +++ b/src/BlockEntities/DropperEntity.h @@ -0,0 +1,46 @@ + +// DropperEntity.h + +// Declares the cDropperEntity class representing a dropper block entity + + + + + +#pragma once + +#include "DropSpenserEntity.h" + + + + + +// tolua_begin +class cDropperEntity : + public cDropSpenserEntity +{ + typedef cDropSpenserEntity super; + +public: + + // tolua_end + + /// Constructor used for normal operation + cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + static const char * GetClassStatic(void) { return "cDropperEntity"; } + +protected: + // cDropSpenserEntity overrides: + virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; + + /** Takes an item from slot a_SlotNum and puts it into the container in front of the dropper. + Called when there's a container directly in front of the dropper, + so the dropper should store items there, rather than dropping. + */ + void PutIntoContainer(cChunk & a_Chunk, int a_SlotNum, BLOCKTYPE a_ContainerBlock, int a_ContainerX, int a_ContainerY, int a_ContainerZ); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp new file mode 100644 index 000000000..ec5ebe8b9 --- /dev/null +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -0,0 +1,479 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FurnaceEntity.h" +#include "../UI/Window.h" +#include "../Entities/Player.h" +#include "../Root.h" +#include "../Chunk.h" +#include + + + + + + +enum +{ + PROGRESSBAR_SMELTING = 0, + PROGRESSBAR_FUEL = 1, +} ; + + + + + +cFurnaceEntity::cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World) : + super(E_BLOCK_FURNACE, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta), + m_CurrentRecipe(NULL), + m_IsCooking((a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_LIT_FURNACE)), + m_NeedCookTime(0), + m_TimeCooked(0), + m_FuelBurnTime(0), + m_TimeBurned(0), + m_LastProgressFuel(0), + m_LastProgressCook(0) +{ + cBlockEntityWindowOwner::SetBlockEntity(this); + m_Contents.AddListener(*this); +} + + + + + +cFurnaceEntity::~cFurnaceEntity() +{ + // Tell window its owner is destroyed + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->OwnerDestroyed(); + } +} + + + + + +void cFurnaceEntity::UsedBy(cPlayer * a_Player) +{ + if (GetWindow() == NULL) + { + OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this)); + } + cWindow * Window = GetWindow(); + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + BroadcastProgress(PROGRESSBAR_FUEL, m_LastProgressFuel); + BroadcastProgress(PROGRESSBAR_SMELTING, m_LastProgressCook); + } + } +} + + + + + +/// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking. +bool cFurnaceEntity::ContinueCooking(void) +{ + UpdateInput(); + UpdateFuel(); + return m_IsCooking; +} + + + + + +bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (m_FuelBurnTime <= 0) + { + // No fuel is burning, reset progressbars and bail out + if ((m_LastProgressCook > 0) || (m_LastProgressFuel > 0)) + { + UpdateProgressBars(); + } + return false; + } + + if (m_IsCooking) + { + m_TimeCooked++; + if (m_TimeCooked >= m_NeedCookTime) + { + // Finished smelting one item + FinishOne(a_Chunk); + } + } + + m_TimeBurned++; + if (m_TimeBurned >= m_FuelBurnTime) + { + // The current fuel has been exhausted, use another one, if possible + BurnNewFuel(); + } + + UpdateProgressBars(); + + return true; +} + + + + + +bool cFurnaceEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + Json::Value AllSlots = a_Value.get("Slots", 0); + int SlotIdx = 0; + for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) + { + cItem Item; + Item.FromJson(*itr); + SetSlot(SlotIdx, Item); + SlotIdx++; + } + + m_NeedCookTime = (int)(a_Value.get("CookTime", 0).asDouble() / 50); + m_TimeCooked = (int)(a_Value.get("TimeCooked", 0).asDouble() / 50); + m_FuelBurnTime = (int)(a_Value.get("BurnTime", 0).asDouble() / 50); + m_TimeBurned = (int)(a_Value.get("TimeBurned", 0).asDouble() / 50); + + return true; +} + + + + + +void cFurnaceEntity::SaveToJson( Json::Value& a_Value ) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + Json::Value AllSlots; + int NumSlots = m_Contents.GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + Json::Value Slot; + m_Contents.GetSlot(i).GetJson(Slot); + AllSlots.append(Slot); + } + a_Value["Slots"] = AllSlots; + + a_Value["CookTime"] = m_NeedCookTime * 50; + a_Value["TimeCooked"] = m_TimeCooked * 50; + a_Value["BurnTime"] = m_FuelBurnTime * 50; + a_Value["TimeBurned"] = m_TimeBurned * 50; +} + + + + + +void cFurnaceEntity::SendTo(cClientHandle & a_Client) +{ + // Nothing needs to be sent + UNUSED(a_Client); +} + + + + + +void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value) +{ + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->BroadcastProgress(a_ProgressbarID, a_Value); + } +} + + + + + +/// One item finished cooking +void cFurnaceEntity::FinishOne(cChunk & a_Chunk) +{ + m_TimeCooked = 0; + + if (m_Contents.GetSlot(fsOutput).IsEmpty()) + { + m_Contents.SetSlot(fsOutput, *m_CurrentRecipe->Out); + } + else + { + m_Contents.ChangeSlotCount(fsOutput, m_CurrentRecipe->Out->m_ItemCount); + } + m_Contents.ChangeSlotCount(fsInput, -m_CurrentRecipe->In->m_ItemCount); + + UpdateIsCooking(); +} + + + + + +void cFurnaceEntity::BurnNewFuel(void) +{ + cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); + int NewTime = FR->GetBurnTime(m_Contents.GetSlot(fsFuel)); + if (NewTime == 0) + { + // The item in the fuel slot is not suitable + m_FuelBurnTime = 0; + m_TimeBurned = 0; + SetIsCooking(false); + return; + } + + // Is the input and output ready for cooking? + if (!CanCookInputToOutput()) + { + return; + } + + // Burn one new fuel: + m_FuelBurnTime = NewTime; + m_TimeBurned = 0; + SetIsCooking(true); + if (m_Contents.GetSlot(fsFuel).m_ItemType == E_ITEM_LAVA_BUCKET) + { + m_Contents.SetSlot(fsFuel, cItem(E_ITEM_BUCKET)); + } + else + { + m_Contents.ChangeSlotCount(fsFuel, -1); + } +} + + + + + +void cFurnaceEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + super::OnSlotChanged(a_ItemGrid, a_SlotNum); + + if (m_World == NULL) + { + // The furnace isn't initialized yet, do no processing + return; + } + + ASSERT(a_ItemGrid == &m_Contents); + switch (a_SlotNum) + { + case fsInput: + { + UpdateInput(); + break; + } + + case fsFuel: + { + UpdateFuel(); + break; + } + + case fsOutput: + { + UpdateOutput(); + break; + } + } +} + + + + + + +/// Updates the current recipe, based on the current input +void cFurnaceEntity::UpdateInput(void) +{ + if (!m_Contents.GetSlot(fsInput).IsStackableWith(m_LastInput)) + { + // The input is different from what we had before, reset the cooking time + m_TimeCooked = 0; + } + m_LastInput = m_Contents.GetSlot(fsInput); + + cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); + m_CurrentRecipe = FR->GetRecipeFrom(m_Contents.GetSlot(fsInput)); + if (!CanCookInputToOutput()) + { + // This input cannot be cooked + m_NeedCookTime = 0; + SetIsCooking(false); + } + else + { + m_NeedCookTime = m_CurrentRecipe->CookTime; + SetIsCooking(true); + + // Start burning new fuel if there's no flame now: + if (GetFuelBurnTimeLeft() <= 0) + { + BurnNewFuel(); + } + } +} + + + + + +/// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate +void cFurnaceEntity::UpdateFuel(void) +{ + if (m_FuelBurnTime > m_TimeBurned) + { + // The current fuel is still burning, don't modify anything: + return; + } + + // The current fuel is spent, try to burn some more: + BurnNewFuel(); +} + + + + + +/// Called when the output slot changes; starts burning if space became available +void cFurnaceEntity::UpdateOutput(void) +{ + if (!CanCookInputToOutput()) + { + // Cannot cook anymore: + m_TimeCooked = 0; + m_NeedCookTime = 0; + SetIsCooking(false); + return; + } + + // No need to burn new fuel, the Tick() function will take care of that + + // Can cook, start cooking if not already underway: + m_NeedCookTime = m_CurrentRecipe->CookTime; + SetIsCooking(m_FuelBurnTime > 0); +} + + + + + +/// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned +void cFurnaceEntity::UpdateIsCooking(void) +{ + if ( + !CanCookInputToOutput() || // Cannot cook this + (m_FuelBurnTime <= 0) || // No fuel + (m_TimeBurned >= m_FuelBurnTime) // Fuel burnt out + ) + { + // Reset everything + SetIsCooking(false); + m_TimeCooked = 0; + m_NeedCookTime = 0; + return; + } + + SetIsCooking(true); +} + + + + + +/// Returns true if the input can be cooked into output and the item counts allow for another cooking operation +bool cFurnaceEntity::CanCookInputToOutput(void) const +{ + if (m_CurrentRecipe == NULL) + { + // This input cannot be cooked + return false; + } + + if (m_Contents.GetSlot(fsOutput).IsEmpty()) + { + // The output is empty, can cook + return true; + } + + if (!m_Contents.GetSlot(fsOutput).IsStackableWith(*m_CurrentRecipe->Out)) + { + // The output slot is blocked with something that cannot be stacked with the recipe's output + return false; + } + + if (m_Contents.GetSlot(fsOutput).IsFullStack()) + { + // Cannot add any more items to the output slot + return false; + } + + return true; +} + + + + + +/// Broadcasts progressbar updates, if needed +void cFurnaceEntity::UpdateProgressBars(void) +{ + // In order to preserve bandwidth, an update is sent only every 10th tick + // That's why the comparisons use the division by eight + + int CurFuel = (m_FuelBurnTime > 0) ? (200 - 200 * m_TimeBurned / m_FuelBurnTime) : 0; + if ((CurFuel / 8) != (m_LastProgressFuel / 8)) + { + BroadcastProgress(PROGRESSBAR_FUEL, CurFuel); + m_LastProgressFuel = CurFuel; + } + + int CurCook = (m_NeedCookTime > 0) ? (200 * m_TimeCooked / m_NeedCookTime) : 0; + if ((CurCook / 8) != (m_LastProgressCook / 8)) + { + BroadcastProgress(PROGRESSBAR_SMELTING, CurCook); + m_LastProgressCook = CurCook; + } +} + + + + + +void cFurnaceEntity::SetIsCooking(bool a_IsCooking) +{ + if (a_IsCooking == m_IsCooking) + { + return; + } + + m_IsCooking = a_IsCooking; + + // Light or extinguish the furnace: + m_World->FastSetBlock(m_PosX, m_PosY, m_PosZ, m_IsCooking ? E_BLOCK_LIT_FURNACE : E_BLOCK_FURNACE, m_BlockMeta); +} + + + + diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h new file mode 100644 index 000000000..9464fd175 --- /dev/null +++ b/src/BlockEntities/FurnaceEntity.h @@ -0,0 +1,164 @@ + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" +#include "../FurnaceRecipe.h" + + + + + +namespace Json +{ + class Value; +} + +class cClientHandle; +class cServer; + + + + + +class cFurnaceEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum + { + fsInput = 0, // Input slot number + fsFuel = 1, // Fuel slot number + fsOutput = 2, // Output slot number + + ContentsWidth = 3, + ContentsHeight = 1, + }; + + // tolua_end + + /// Constructor used for normal operation + cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World); + + virtual ~cFurnaceEntity(); + + static const char * GetClassStatic() { return "cFurnaceEntity"; } + + bool LoadFromJson(const Json::Value & a_Value); + + // cBlockEntity overrides: + virtual void SaveToJson(Json::Value & a_Value) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void UsedBy(cPlayer * a_Player) override; + + /// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking. + bool ContinueCooking(void); + + void ResetCookTimer(); + + // tolua_begin + + /// Returns the item in the input slot + const cItem & GetInputSlot(void) const { return GetSlot(fsInput); } + + /// Returns the item in the fuel slot + const cItem & GetFuelSlot(void) const { return GetSlot(fsFuel); } + + /// Returns the item in the output slot + const cItem & GetOutputSlot(void) const { return GetSlot(fsOutput); } + + /// Sets the item in the input slot + void SetInputSlot(const cItem & a_Item) { SetSlot(fsInput, a_Item); } + + /// Sets the item in the fuel slot + void SetFuelSlot(const cItem & a_Item) { SetSlot(fsFuel, a_Item); } + + /// Sets the item in the output slot + void SetOutputSlot(const cItem & a_Item) { SetSlot(fsOutput, a_Item); } + + /// Returns the time that the current item has been cooking, in ticks + int GetTimeCooked(void) const {return m_TimeCooked; } + + /// Returns the time until the current item finishes cooking, in ticks + int GetCookTimeLeft(void) const { return m_NeedCookTime - m_TimeCooked; } + + /// Returns the time until the current fuel is depleted, in ticks + int GetFuelBurnTimeLeft(void) const {return m_FuelBurnTime - m_TimeBurned; } + + /// Returns true if there's time left before the current fuel is depleted + bool HasFuelTimeLeft(void) const { return (GetFuelBurnTimeLeft() > 0); } + + // tolua_end + + void SetBurnTimes(int a_FuelBurnTime, int a_TimeBurned) {m_FuelBurnTime = a_FuelBurnTime; m_TimeBurned = 0; } + void SetCookTimes(int a_NeedCookTime, int a_TimeCooked) {m_NeedCookTime = a_NeedCookTime; m_TimeCooked = a_TimeCooked; } + +protected: + + /// Block type of the block currently represented by this entity (changes when furnace lights up) + BLOCKTYPE m_BlockType; + + /// Block meta of the block currently represented by this entity + NIBBLETYPE m_BlockMeta; + + /// The recipe for the current input slot + const cFurnaceRecipe::Recipe * m_CurrentRecipe; + + /// The item that is being smelted + cItem m_LastInput; + + bool m_IsCooking; ///< Set to true if the furnace is cooking an item + + // All timers are in ticks + int m_NeedCookTime; ///< Amount of time needed to fully cook current item + int m_TimeCooked; ///< Amount of time that the current item has been cooking + int m_FuelBurnTime; ///< Amount of time that the current fuel can burn (in total); zero if no fuel burning + int m_TimeBurned; ///< Amount of time that the current fuel has been burning + + int m_LastProgressFuel; ///< Last value sent as the progress for the fuel + int m_LastProgressCook; ///< Last value sent as the progress for the cooking + + + /// Sends the specified progressbar value to all clients of the window + void BroadcastProgress(int a_ProgressbarID, short a_Value); + + /// One item finished cooking + void FinishOne(cChunk & a_Chunk); + + /// Starts burning a new fuel, if possible + void BurnNewFuel(void); + + /// Updates the recipe, based on the current input + void UpdateInput(void); + + /// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate + void UpdateFuel(void); + + /// Called when the output slot changes + void UpdateOutput(void); + + /// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned + void UpdateIsCooking(void); + + /// Returns true if the input can be cooked into output and the item counts allow for another cooking operation + bool CanCookInputToOutput(void) const; + + /// Broadcasts progressbar updates, if needed + void UpdateProgressBars(void); + + /// Sets the m_IsCooking variable, updates the furnace block type based on the value + void SetIsCooking(bool a_IsCooking); + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; + +} ; // tolua_export + + + + diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp new file mode 100644 index 000000000..41849b1b3 --- /dev/null +++ b/src/BlockEntities/HopperEntity.cpp @@ -0,0 +1,566 @@ + +// HopperEntity.cpp + +// Implements the cHopperEntity representing a hopper block entity + +#include "Globals.h" +#include "HopperEntity.h" +#include "../Chunk.h" +#include "../Entities/Player.h" +#include "../PluginManager.h" +#include "ChestEntity.h" +#include "DropSpenserEntity.h" +#include "FurnaceEntity.h" + + + + + +cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_LastMoveItemsInTick(0), + m_LastMoveItemsOutTick(0) +{ +} + + + + + +/** Returns the block coords of the block receiving the output items, based on the meta +Returns false if unattached +*/ +bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ) +{ + a_OutputX = m_PosX; + a_OutputY = m_PosY; + a_OutputZ = m_PosZ; + switch (a_BlockMeta) + { + case E_META_HOPPER_FACING_XM: a_OutputX--; return true; + case E_META_HOPPER_FACING_XP: a_OutputX++; return true; + case E_META_HOPPER_FACING_YM: a_OutputY--; return true; + case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true; + case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true; + default: + { + // Not attached + return false; + } + } +} + + + + + +bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); + + bool res = false; + res = MoveItemsIn (a_Chunk, CurrentTick) || res; + res = MovePickupsIn(a_Chunk, CurrentTick) || res; + res = MoveItemsOut (a_Chunk, CurrentTick) || res; + return res; +} + + + + + +void cHopperEntity::SaveToJson(Json::Value & a_Value) +{ + // TODO + LOGWARNING("%s: Not implemented yet", __FUNCTION__); +} + + + + + +void cHopperEntity::SendTo(cClientHandle & a_Client) +{ + // The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance + // All the actual handling is in the cWindow UI code that gets called when the hopper is rclked + + UNUSED(a_Client); +} + + + + + +void cHopperEntity::UsedBy(cPlayer * a_Player) +{ + // If the window is not created, open it anew: + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenNewWindow(); + Window = GetWindow(); + } + + // Open the window for the player: + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } + + // This is rather a hack + // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now + // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. + // The few false positives aren't much to worry about + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); + m_World->MarkChunkDirty(ChunkX, ChunkZ); +} + + + + + +/// Opens a new window UI for this hopper +void cHopperEntity::OpenNewWindow(void) +{ + OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this)); +} + + + + + +/// Moves items from the container above it into this hopper. Returns true if the contents have changed. +bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + if (m_PosY >= cChunkDef::Height) + { + // This hopper is at the top of the world, no more blocks above + return false; + } + + if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER) + { + // Too early after the previous transfer + return false; + } + + // Try moving an item in: + bool res = false; + switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ)) + { + case E_BLOCK_CHEST: + { + // Chests have special handling because of double-chests + res = MoveItemsFromChest(a_Chunk); + break; + } + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_FURNACE: + { + // Furnaces have special handling because only the output and leftover fuel buckets shall be moved + res = MoveItemsFromFurnace(a_Chunk); + break; + } + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_HOPPER: + { + res = MoveItemsFromGrid(*(cBlockEntityWithItems *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ)); + break; + } + } + + // If the item has been moved, reset the last tick: + if (res) + { + m_LastMoveItemsInTick = a_CurrentTick; + } + + return res; +} + + + + + +/// Moves pickups from above this hopper into it. Returns true if the contents have changed. +bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + // TODO + return false; +} + + + + + +/// Moves items out from this hopper into the destination. Returns true if the contents have changed. +bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER) + { + // Too early after the previous transfer + return false; + } + + int bx, by, bz; + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + if (!GetOutputBlockPos(Meta, bx, by, bz)) + { + // Not attached to another container + return false; + } + if (by < 0) + { + // Cannot output below the zero-th block level + return false; + } + + // Convert coords to relative: + int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width; + int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz); + if (DestChunk == NULL) + { + // The destination chunk has been unloaded, don't tick + return false; + } + + // Call proper moving function, based on the blocktype present at the coords: + bool res = false; + switch (DestChunk->GetBlock(rx, by, rz)) + { + case E_BLOCK_CHEST: + { + // Chests have special handling because of double-chests + res = MoveItemsToChest(*DestChunk, bx, by, bz); + break; + } + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_FURNACE: + { + // Furnaces have special handling because of the direction-to-slot relation + res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); + break; + } + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_HOPPER: + { + res = MoveItemsToGrid(*(cBlockEntityWithItems *)DestChunk->GetBlockEntity(bx, by, bz)); + break; + } + } + + // If the item has been moved, reset the last tick: + if (res) + { + m_LastMoveItemsOutTick = a_CurrentTick; + } + + return res; +} + + + + + +/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) +{ + if (MoveItemsFromGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))) + { + // Moved the item from the chest directly above the hopper + return true; + } + + // Check if the chest is a double-chest, if so, try to move from there: + static const struct + { + int x, z; + } + Coords [] = + { + {1, 0}, + {-1, 0}, + {0, 1}, + {0, -1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + int x = m_RelX + Coords[i].x; + int z = m_RelZ + Coords[i].z; + cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); + if ( + (Neighbor == NULL) || + (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) + ) + { + continue; + } + if (MoveItemsFromGrid(*(cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))) + { + return true; + } + return false; + } + + // The chest was single and nothing could be moved + return false; +} + + + + + +/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) +{ + cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); + ASSERT(Furnace != NULL); + + // Try move from the output slot: + if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true)) + { + cItem NewOutput(Furnace->GetOutputSlot()); + Furnace->SetOutputSlot(NewOutput.AddCount(-1)); + return true; + } + + // No output moved, check if we can move an empty bucket out of the fuel slot: + if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET) + { + if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel, true)) + { + Furnace->SetFuelSlot(cItem()); + return true; + } + } + + // Nothing can be moved + return false; +} + + + + + +bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity) +{ + cItemGrid & Grid = a_Entity.GetContents(); + int NumSlots = Grid.GetNumSlots(); + + // First try adding items of types already in the hopper: + for (int i = 0; i < NumSlots; i++) + { + if (Grid.IsSlotEmpty(i)) + { + continue; + } + if (MoveItemsFromSlot(a_Entity, i, false)) + { + Grid.ChangeSlotCount(i, -1); + return true; + } + } + + // No already existing stack can be topped up, try again with allowing new stacks: + for (int i = 0; i < NumSlots; i++) + { + if (Grid.IsSlotEmpty(i)) + { + continue; + } + if (MoveItemsFromSlot(a_Entity, i, true)) + { + Grid.ChangeSlotCount(i, -1); + return true; + } + } + return false; +} + + + + + +/// Moves one piece of the specified a_Entity's slot itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. +bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum, bool a_AllowNewStacks) +{ + cItem One(a_Entity.GetSlot(a_SlotNum).CopyOne()); + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (m_Contents.IsSlotEmpty(i)) + { + if (a_AllowNewStacks) + { + if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) + { + // Plugin disagrees with the move + continue; + } + } + m_Contents.SetSlot(i, One); + return true; + } + else if (m_Contents.GetSlot(i).IsStackableWith(One)) + { + if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) + { + // Plugin disagrees with the move + continue; + } + + m_Contents.ChangeSlotCount(i, 1); + return true; + } + } + return false; +} + + + + + +/// Moves items to the chest at the specified coords. Returns true if contents have changed +bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Try the chest directly connected to the hopper: + if (MoveItemsToGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + + // Check if the chest is a double-chest, if so, try to move into the other half: + static const struct + { + int x, z; + } + Coords [] = + { + {1, 0}, + {-1, 0}, + {0, 1}, + {0, -1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + int x = m_RelX + Coords[i].x; + int z = m_RelZ + Coords[i].z; + cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); + if ( + (Neighbor == NULL) || + (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) + ) + { + continue; + } + if (MoveItemsToGrid(*(cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + return false; + } + + // The chest was single and nothing could be moved + return false; +} + + + + + +/// Moves items to the furnace at the specified coords. Returns true if contents have changed +bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta) +{ + cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (a_HopperMeta == E_META_HOPPER_FACING_YM) + { + // Feed the input slot of the furnace + return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsInput); + } + else + { + // Feed the fuel slot of the furnace + return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsFuel); + } + return false; +} + + + + + +bool cHopperEntity::MoveItemsToGrid(cBlockEntityWithItems & a_Entity) +{ + // Iterate through our slots, try to move from each one: + int NumSlots = a_Entity.GetContents().GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + if (MoveItemsToSlot(a_Entity, i)) + { + return true; + } + } + return false; +} + + + + + +bool cHopperEntity::MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum) +{ + cItemGrid & Grid = a_Entity.GetContents(); + if (Grid.IsSlotEmpty(a_DstSlotNum)) + { + // The slot is empty, move the first non-empty slot from our contents: + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (!m_Contents.IsSlotEmpty(i)) + { + if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum)) + { + // A plugin disagrees with the move + continue; + } + Grid.SetSlot(a_DstSlotNum, m_Contents.GetSlot(i).CopyOne()); + m_Contents.ChangeSlotCount(i, -1); + return true; + } + } + return false; + } + else + { + // The slot is taken, try to top it up: + const cItem & DestSlot = Grid.GetSlot(a_DstSlotNum); + if (DestSlot.IsFullStack()) + { + return false; + } + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (m_Contents.GetSlot(i).IsStackableWith(DestSlot)) + { + if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum)) + { + // A plugin disagrees with the move + continue; + } + Grid.ChangeSlotCount(a_DstSlotNum, 1); + m_Contents.ChangeSlotCount(i, -1); + return true; + } + } + return false; + } +} + + + + diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h new file mode 100644 index 000000000..3eaa05b7c --- /dev/null +++ b/src/BlockEntities/HopperEntity.h @@ -0,0 +1,96 @@ + +// HopperEntity.h + +// Declares the cHopperEntity representing a hopper block entity + + + + + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +class cHopperEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 1, + ContentsWidth = 5, + TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper + } ; + + // tolua_end + + /// Constructor used for normal operation + cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + /** Returns the block coords of the block receiving the output items, based on the meta + Returns false if unattached. + Exported in ManualBindings.cpp + */ + bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ); + + static const char * GetClassStatic(void) { return "cHopperEntity"; } + +protected: + + Int64 m_LastMoveItemsInTick; + Int64 m_LastMoveItemsOutTick; + + // cBlockEntity overrides: + virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void SaveToJson(Json::Value & a_Value) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual void UsedBy(cPlayer * a_Player) override; + + /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. + void OpenNewWindow(void); + + /// Moves items from the container above it into this hopper. Returns true if the contents have changed. + bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves pickups from above this hopper into it. Returns true if the contents have changed. + bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves items out from this hopper into the destination. Returns true if the contents have changed. + bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. + bool MoveItemsFromChest(cChunk & a_Chunk); + + /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. + bool MoveItemsFromFurnace(cChunk & a_Chunk); + + /// Moves items from the specified a_Entity's Contents into this hopper. Returns true if contents have changed. + bool MoveItemsFromGrid(cBlockEntityWithItems & a_Entity); + + /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. + bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum, bool a_AllowNewStacks); + + /// Moves items to the chest at the specified coords. Returns true if contents have changed + bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Moves items to the furnace at the specified coords. Returns true if contents have changed + bool MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta); + + /// Moves items to the specified ItemGrid. Returns true if contents have changed + bool MoveItemsToGrid(cBlockEntityWithItems & a_Entity); + + /// Moves one piece to the specified entity's contents' slot. Returns true if contents have changed. + bool MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/JukeboxEntity.cpp b/src/BlockEntities/JukeboxEntity.cpp new file mode 100644 index 000000000..aca376dd3 --- /dev/null +++ b/src/BlockEntities/JukeboxEntity.cpp @@ -0,0 +1,125 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "JukeboxEntity.h" +#include "../World.h" +#include + + + + + +cJukeboxEntity::cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Record(0) +{ +} + + + + + +cJukeboxEntity::~cJukeboxEntity() +{ + EjectRecord(); +} + + + + + +void cJukeboxEntity::UsedBy(cPlayer * a_Player) +{ + if (m_Record == 0) + { + const cItem & HeldItem = a_Player->GetEquippedItem(); + if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267) + { + m_Record = HeldItem.m_ItemType; + a_Player->GetInventory().RemoveOneEquippedItem(); + PlayRecord(); + } + } + else + { + EjectRecord(); + } +} + + + + + +void cJukeboxEntity::PlayRecord(void) +{ + m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, m_Record); +} + + + + + +void cJukeboxEntity::EjectRecord(void) +{ + if ((m_Record < E_ITEM_FIRST_DISC) || (m_Record > E_ITEM_LAST_DISC)) + { + // There's no record here + return; + } + + cItems Drops; + Drops.push_back(cItem(m_Record, 1, 0)); + m_World->SpawnItemPickups(Drops, m_PosX + 0.5, m_PosY + 1, m_PosZ + 0.5, 8); + m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, 0); + m_Record = 0; +} + + + + + +int cJukeboxEntity::GetRecord(void) +{ + return m_Record; +} + + + + + +void cJukeboxEntity::SetRecord(int a_Record) +{ + m_Record = a_Record; +} + + + + + +bool cJukeboxEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Record = a_Value.get("Record", 0).asInt(); + + return true; +} + + + + + +void cJukeboxEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["Record"] = m_Record; +} + + + + diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h new file mode 100644 index 000000000..fcafdc479 --- /dev/null +++ b/src/BlockEntities/JukeboxEntity.h @@ -0,0 +1,56 @@ + +#pragma once + +#include "BlockEntity.h" +#include "../Entities/Player.h" + + + + + +namespace Json +{ + class Value; +} + + + + + +// tolua_begin + +class cJukeboxEntity : + public cBlockEntity +{ + typedef cBlockEntity super; +public: + + // tolua_end + + cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + virtual ~cJukeboxEntity(); + + bool LoadFromJson(const Json::Value & a_Value); + virtual void SaveToJson(Json::Value & a_Value) override; + + // tolua_begin + + int GetRecord(void); + void SetRecord(int a_Record); + void PlayRecord(void); + + /// Ejects the currently held record as a pickup. Does nothing when no record inserted. + void EjectRecord(void); + + // tolua_end + + virtual void UsedBy(cPlayer * a_Player) override; + virtual void SendTo(cClientHandle & a_Client) override { }; + +private: + int m_Record; +} ; // tolua_end + + + + diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp new file mode 100644 index 000000000..1b0620299 --- /dev/null +++ b/src/BlockEntities/NoteEntity.cpp @@ -0,0 +1,154 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "NoteEntity.h" +#include "../World.h" +#include + + + + + +cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Pitch(0) +{ +} + + + + + +void cNoteEntity::UsedBy(cPlayer * a_Player) +{ + IncrementPitch(); + MakeSound(); +} + + + + + +void cNoteEntity::MakeSound(void) +{ + char instrument; + AString sampleName; + + switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ)) + { + case E_BLOCK_PLANKS: + case E_BLOCK_LOG: + case E_BLOCK_NOTE_BLOCK: + { + // TODO: add other wood-based blocks if needed + instrument = E_INST_DOUBLE_BASS; + sampleName = "note.db"; + break; + } + + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + case E_BLOCK_SOULSAND: + { + instrument = E_INST_SNARE_DRUM; + sampleName = "note.snare"; + break; + } + + case E_BLOCK_GLASS: + case E_BLOCK_GLASS_PANE: + case E_BLOCK_GLOWSTONE: + { + instrument = E_INST_CLICKS; + sampleName = "note.hat"; + break; + } + + case E_BLOCK_STONE: + case E_BLOCK_STONE_BRICKS: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_OBSIDIAN: + case E_BLOCK_NETHERRACK: + case E_BLOCK_BRICK: + case E_BLOCK_NETHER_BRICK: + { + // TODO: add other stone-based blocks if needed + instrument = E_INST_BASS_DRUM; + sampleName = "note.bassattack"; + break; + } + + default: + { + instrument = E_INST_HARP_PIANO; + sampleName = "note.harp"; + break; + } + } + + m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK); + + // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all + float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f); + m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch); +} + + + + + +char cNoteEntity::GetPitch(void) +{ + return m_Pitch; +} + + + + + +void cNoteEntity::SetPitch(char a_Pitch) +{ + m_Pitch = a_Pitch % 25; +} + + + + + +void cNoteEntity::IncrementPitch(void) +{ + SetPitch(m_Pitch + 1); +} + + + + + +bool cNoteEntity::LoadFromJson(const Json::Value & a_Value) +{ + + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Pitch = (char)a_Value.get("p", 0).asInt(); + + return true; +} + + + + + +void cNoteEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["p"] = m_Pitch; +} + + + + diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h new file mode 100644 index 000000000..e2d088f44 --- /dev/null +++ b/src/BlockEntities/NoteEntity.h @@ -0,0 +1,63 @@ + +#pragma once + +#include "BlockEntity.h" + + +namespace Json +{ + class Value; +} + + + + + +enum ENUM_NOTE_INSTRUMENTS +{ + E_INST_HARP_PIANO = 0, + E_INST_DOUBLE_BASS = 1, + E_INST_SNARE_DRUM = 2, + E_INST_CLICKS = 3, + E_INST_BASS_DRUM = 4 +}; + + + + + +// tolua_begin + +class cNoteEntity : + public cBlockEntity +{ + typedef cBlockEntity super; +public: + + // tolua_end + + /// Creates a new note entity. a_World may be NULL + cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); + + bool LoadFromJson(const Json::Value & a_Value); + virtual void SaveToJson(Json::Value & a_Value) override; + + // tolua_begin + + char GetPitch(void); + void SetPitch(char a_Pitch); + void IncrementPitch(void); + void MakeSound(void); + + // tolua_end + + virtual void UsedBy(cPlayer * a_Player) override; + virtual void SendTo(cClientHandle & a_Client) override { }; + +private: + char m_Pitch; +} ; // tolua_export + + + + diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp new file mode 100644 index 000000000..81f6f6d77 --- /dev/null +++ b/src/BlockEntities/SignEntity.cpp @@ -0,0 +1,115 @@ + +// SignEntity.cpp + +// Implements the cSignEntity class representing a single sign in the world + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include +#include "SignEntity.h" +#include "../Entities/Player.h" + + + + + +cSignEntity::cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) : + super(a_BlockType, a_X, a_Y, a_Z, a_World) +{ +} + + + + + +// It don't do anything when 'used' +void cSignEntity::UsedBy(cPlayer * a_Player) +{ + UNUSED(a_Player); +} + + + + + +void cSignEntity::SetLines(const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + m_Line[0] = a_Line1; + m_Line[1] = a_Line2; + m_Line[2] = a_Line3; + m_Line[3] = a_Line4; +} + + + + + +void cSignEntity::SetLine(int a_Index, const AString & a_Line) +{ + if ((a_Index < 0) || (a_Index >= ARRAYCOUNT(m_Line))) + { + LOGWARNING("%s: setting a non-existent line %d (value \"%s\"", __FUNCTION__, a_Index, a_Line.c_str()); + return; + } + m_Line[a_Index] = a_Line; +} + + + + + +AString cSignEntity::GetLine(int a_Index) const +{ + if ((a_Index < 0) || (a_Index >= ARRAYCOUNT(m_Line))) + { + LOGWARNING("%s: requesting a non-existent line %d", __FUNCTION__, a_Index); + return ""; + } + return m_Line[a_Index]; +} + + + + + +void cSignEntity::SendTo(cClientHandle & a_Client) +{ + a_Client.SendUpdateSign(m_PosX, m_PosY, m_PosZ, m_Line[0], m_Line[1], m_Line[2], m_Line[3]); +} + + + + + +bool cSignEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Line[0] = a_Value.get("Line1", "").asString(); + m_Line[1] = a_Value.get("Line2", "").asString(); + m_Line[2] = a_Value.get("Line3", "").asString(); + m_Line[3] = a_Value.get("Line4", "").asString(); + + return true; +} + + + + + +void cSignEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["Line1"] = m_Line[0]; + a_Value["Line2"] = m_Line[1]; + a_Value["Line3"] = m_Line[2]; + a_Value["Line4"] = m_Line[3]; +} + + + + diff --git a/src/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h new file mode 100644 index 000000000..d998ff1e8 --- /dev/null +++ b/src/BlockEntities/SignEntity.h @@ -0,0 +1,67 @@ + +// SignEntity.h + +// Declares the cSignEntity class representing a single sign in the world + + + + + +#pragma once + +#include "BlockEntity.h" + + + + + +namespace Json +{ + class Value; +} + + + + + +// tolua_begin + +class cSignEntity : + public cBlockEntity +{ + typedef cBlockEntity super; + +public: + + // tolua_end + + /// Creates a new empty sign entity at the specified block coords and block type (wall or standing). a_World may be NULL + cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World); + + bool LoadFromJson( const Json::Value& a_Value ); + virtual void SaveToJson(Json::Value& a_Value ) override; + + // tolua_begin + + /// Sets all the sign's lines + void SetLines(const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + + /// Sets individual line (zero-based index) + void SetLine(int a_Index, const AString & a_Line); + + /// Retrieves individual line (zero-based index) + AString GetLine(int a_Index) const; + + // tolua_end + + virtual void UsedBy(cPlayer * a_Player) override; + virtual void SendTo(cClientHandle & a_Client) override; + +private: + + AString m_Line[4]; +} ; // tolua_export + + + + diff --git a/src/BlockID.cpp b/src/BlockID.cpp new file mode 100644 index 000000000..f8949577e --- /dev/null +++ b/src/BlockID.cpp @@ -0,0 +1,954 @@ +// BlockID.cpp + +// Implements the helper functions for converting Block ID string to int etc. + +#include "Globals.h" +#include "BlockID.h" +#include "../iniFile/iniFile.h" +#include "Item.h" +#include "Mobs/Monster.h" + + + + + +NIBBLETYPE g_BlockLightValue[256]; +NIBBLETYPE g_BlockSpreadLightFalloff[256]; +bool g_BlockTransparent[256]; +bool g_BlockOneHitDig[256]; +bool g_BlockPistonBreakable[256]; +bool g_BlockIsSnowable[256]; +bool g_BlockRequiresSpecialTool[256]; +bool g_BlockIsSolid[256]; +bool g_BlockIsTorchPlaceable[256]; + + + + + +class cBlockIDMap +{ + // Making the map case-insensitive: + struct Comparator + { + bool operator()(const AString & a_Item1, const AString & a_Item2) const + { + return (NoCaseCompare(a_Item1, a_Item2) > 0); + } + } ; + + typedef std::map, Comparator> ItemMap; + +public: + cBlockIDMap(void) + { + cIniFile Ini; + if (!Ini.ReadFile("items.ini")) + { + return; + } + int KeyID = Ini.FindKey("Items"); + if (KeyID == cIniFile::noID) + { + return; + } + int NumValues = Ini.GetNumValues(KeyID); + for (int i = 0; i < NumValues; i++) + { + AString Name = Ini.GetValueName(KeyID, i); + if (Name.empty()) + { + continue; + } + AString Value = Ini.GetValue(KeyID, i); + AddToMap(Name, Value); + } // for i - Ini.Values[] + } + + + int Resolve(const AString & a_ItemName) + { + ItemMap::iterator itr = m_Map.find(a_ItemName); + if (itr == m_Map.end()) + { + return -1; + } + return itr->second.first; + } + + + bool ResolveItem(const AString & a_ItemName, cItem & a_Item) + { + // Split into parts divided by either ':' or '^' + AStringVector Split = StringSplitAndTrim(a_ItemName, ":^"); + if (Split.empty()) + { + return false; + } + + ItemMap::iterator itr = m_Map.find(Split[0]); + if (itr != m_Map.end()) + { + // Resolved as a string, assign the type and the default damage / count + a_Item.m_ItemType = itr->second.first; + a_Item.m_ItemDamage = itr->second.second; + if (a_Item.m_ItemDamage == -1) + { + a_Item.m_ItemDamage = 0; + } + } + else + { + // Not a resolvable string, try pure numbers: "45:6", "45^6" etc. + a_Item.m_ItemType = (short)atoi(Split[0].c_str()); + if ((a_Item.m_ItemType == 0) && (Split[0] != "0")) + { + // Parsing the number failed + return false; + } + } + + // Parse the damage, if present: + if (Split.size() < 2) + { + // Not present, set the item as valid and return success: + a_Item.m_ItemCount = 1; + return true; + } + + a_Item.m_ItemDamage = atoi(Split[1].c_str()); + if ((a_Item.m_ItemDamage == 0) && (Split[1] != "0")) + { + // Parsing the number failed + return false; + } + a_Item.m_ItemCount = 1; + return true; + } + + + AString Desolve(short a_ItemType, short a_ItemDamage) + { + // First try an exact match, both ItemType and ItemDamage ("birchplanks=5:2"): + for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr) + { + if ((itr->second.first == a_ItemType) && (itr->second.second == a_ItemDamage)) + { + return itr->first; + } + } // for itr - m_Map[] + + // There is no exact match, try matching ItemType only ("planks=5"): + if (a_ItemDamage == 0) + { + for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr) + { + if ((itr->second.first == a_ItemType) && (itr->second.second == -1)) + { + return itr->first; + } + } // for itr - m_Map[] + } + + // No match at all, synthesize a string ("5:1"): + AString res; + if (a_ItemDamage == -1) + { + Printf(res, "%d", a_ItemType); + } + else + { + Printf(res, "%d:%d", a_ItemType, a_ItemDamage); + } + return res; + } + + +protected: + ItemMap m_Map; + + + void AddToMap(const AString & a_Name, const AString & a_Value) + { + AStringVector Split = StringSplit(a_Value, ":"); + if (Split.size() == 1) + { + Split = StringSplit(a_Value, "^"); + } + if (Split.empty()) + { + return; + } + short ItemType = (short)atoi(Split[0].c_str()); + short ItemDamage = (Split.size() > 1) ? (short)atoi(Split[1].c_str()) : -1; + m_Map[a_Name] = std::make_pair(ItemType, ItemDamage); + } +} ; + + + + + +static cBlockIDMap gsBlockIDMap; + + + + + +/* +// Quick self-test: +class Tester +{ +public: + Tester(void) + { + cItem Item; + gsBlockIDMap.ResolveItem("charcoal", Item); + AString Charcoal = gsBlockIDMap.Desolve(Item.m_ItemType, Item.m_ItemDamage); + ASSERT(Charcoal == "charcoal"); + } +} test; +//*/ + + + + + +BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString) +{ + int res = atoi(a_BlockTypeString.c_str()); + if ((res != 0) || (a_BlockTypeString.compare("0") == 0)) + { + // It was a valid number, return that + return res; + } + + return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString)); +} + + + + +bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item) +{ + return gsBlockIDMap.ResolveItem(TrimString(a_ItemTypeString), a_Item); +} + + + + + +AString ItemToString(const cItem & a_Item) +{ + return gsBlockIDMap.Desolve(a_Item.m_ItemType, a_Item.m_ItemDamage); +} + + + + + +AString ItemTypeToString(short a_ItemType) +{ + return gsBlockIDMap.Desolve(a_ItemType, -1); +} + + + + + +AString ItemToFullString(const cItem & a_Item) +{ + AString res; + Printf(res, "%s:%d * %d", ItemToString(a_Item).c_str(), a_Item.m_ItemDamage, a_Item.m_ItemCount); + return res; +} + + + + + +EMCSBiome StringToBiome(const AString & a_BiomeString) +{ + // If it is a number, return it: + int res = atoi(a_BiomeString.c_str()); + if ((res != 0) || (a_BiomeString.compare("0") == 0)) + { + // It was a valid number + return (EMCSBiome)res; + } + + // Convert using the built-in map: + static struct { + EMCSBiome m_Biome; + const char * m_String; + } BiomeMap[] = + { + {biOcean, "Ocean"} , + {biPlains, "Plains"}, + {biDesert, "Desert"}, + {biExtremeHills, "ExtremeHills"}, + {biForest, "Forest"}, + {biTaiga, "Taiga"}, + {biSwampland, "Swampland"}, + {biRiver, "River"}, + {biNether, "Hell"}, + {biNether, "Nether"}, + {biEnd, "Sky"}, + {biEnd, "End"}, + {biFrozenOcean, "FrozenOcean"}, + {biFrozenRiver, "FrozenRiver"}, + {biIcePlains, "IcePlains"}, + {biIcePlains, "Tundra"}, + {biIceMountains, "IceMountains"}, + {biMushroomIsland, "MushroomIsland"}, + {biMushroomShore, "MushroomShore"}, + {biBeach, "Beach"}, + {biDesertHills, "DesertHills"}, + {biForestHills, "ForestHills"}, + {biTaigaHills, "TaigaHills"}, + {biExtremeHillsEdge, "ExtremeHillsEdge"}, + {biJungle, "Jungle"}, + {biJungleHills, "JungleHills"}, + + // Release 1.7 biomes: + {biJungleEdge, "JungleEdge"}, + {biDeepOcean, "DeepOcean"}, + {biStoneBeach, "StoneBeach"}, + {biColdBeach, "ColdBeach"}, + {biBirchForest, "BirchForest"}, + {biBirchForestHills, "BirchForestHills"}, + {biRoofedForest, "RoofedForest"}, + {biColdTaiga, "ColdTaiga"}, + {biColdTaigaHills, "ColdTaigaHills"}, + {biMegaTaiga, "MegaTaiga"}, + {biMegaTaigaHills, "MegaTaigaHills"}, + {biExtremeHillsPlus, "ExtremeHillsPlus"}, + {biSavanna, "Savanna"}, + {biSavannaPlateau, "SavannaPlateau"}, + {biMesa, "Mesa"}, + {biMesaPlateauF, "MesaPlateauF"}, + {biMesaPlateau, "MesaPlateau"}, + + // Release 1.7 variants: + {biSunflowerPlains, "SunflowerPlains"}, + {biDesertM, "DesertM"}, + {biExtremeHillsM, "ExtremeHillsM"}, + {biFlowerForest, "FlowerForest"}, + {biTaigaM, "TaigaM"}, + {biSwamplandM, "SwamplandM"}, + {biIcePlainsSpikes, "IcePlainsSpikes"}, + {biJungleM, "JungleM"}, + {biJungleEdgeM, "JungleEdgeM"}, + {biBirchForestM, "BirchForestM"}, + {biBirchForestHillsM, "BirchForestHillsM"}, + {biRoofedForestM, "RoofedForestM"}, + {biColdTaigaM, "ColdTaigaM"}, + {biMegaSpruceTaiga, "MegaSpruceTaiga"}, + {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"}, + {biExtremeHillsPlusM, "ExtremeHillsPlusM"}, + {biSavannaM, "SavannaM"}, + {biSavannaPlateauM, "SavannaPlateauM"}, + {biMesaBryce, "MesaBryce"}, + {biMesaPlateauFM, "MesaPlateauFM"}, + {biMesaPlateauM, "MesaPlateauM"}, + } ; + + for (int i = 0; i < ARRAYCOUNT(BiomeMap); i++) + { + if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0) + { + return BiomeMap[i].m_Biome; + } + } // for i - BiomeMap[] + return (EMCSBiome)-1; +} + + + + + +int StringToMobType(const AString & a_MobString) +{ + static struct { + int m_MobType; + const char * m_String; + } MobMap [] = + { + {cMonster::mtCreeper, "Creeper"}, + {cMonster::mtSkeleton, "Skeleton"}, + {cMonster::mtSpider, "Spider"}, + {cMonster::mtGiant, "Giant"}, + {cMonster::mtZombie, "Zombie"}, + {cMonster::mtSlime, "Slime"}, + {cMonster::mtGhast, "Ghast"}, + {cMonster::mtZombiePigman, "ZombiePigman"}, + {cMonster::mtEnderman, "Enderman"}, + {cMonster::mtCaveSpider, "CaveSpider"}, + {cMonster::mtSilverfish, "SilverFish"}, + {cMonster::mtBlaze, "Blaze"}, + {cMonster::mtMagmaCube, "MagmaCube"}, + {cMonster::mtEnderDragon, "EnderDragon"}, + {cMonster::mtWither, "Wither"}, + {cMonster::mtBat, "Bat"}, + {cMonster::mtWitch, "Witch"}, + {cMonster::mtPig, "Pig"}, + {cMonster::mtSheep, "Sheep"}, + {cMonster::mtCow, "Cow"}, + {cMonster::mtChicken, "Chicken"}, + {cMonster::mtSquid, "Squid"}, + {cMonster::mtWolf, "Wolf"}, + {cMonster::mtMooshroom, "Mooshroom"}, + {cMonster::mtSnowGolem, "SnowGolem"}, + {cMonster::mtOcelot, "Ocelot"}, + {cMonster::mtIronGolem, "IronGolem"}, + {cMonster::mtVillager, "Villager"}, + }; + for (int i = 0; i < ARRAYCOUNT(MobMap); i++) + { + if (NoCaseCompare(MobMap[i].m_String, a_MobString) == 0) + { + return MobMap[i].m_MobType; + } + } // for i - MobMap[] + return -1; +} + + + + + +eDimension StringToDimension(const AString & a_DimensionString) +{ + // First try decoding as a number + int res = atoi(a_DimensionString.c_str()); + if ((res != 0) || (a_DimensionString == "0")) + { + // It was a valid number + return (eDimension)res; + } + + // Decode using a built-in map: + static struct + { + eDimension m_Dimension; + const char * m_String; + } DimensionMap [] = + { + { dimOverworld, "Overworld"}, + { dimOverworld, "Normal"}, + { dimOverworld, "World"}, + { dimNether, "Nether"}, + { dimNether, "Hell"}, // Alternate name for End + { dimEnd, "End"}, + { dimEnd, "Sky"}, // Old name for End + } ; + for (int i = 0; i < ARRAYCOUNT(DimensionMap); i++) + { + if (NoCaseCompare(DimensionMap[i].m_String, a_DimensionString) == 0) + { + return DimensionMap[i].m_Dimension; + } + } // for i - DimensionMap[] + + // Not found + return (eDimension)-1000; +} + + + + + +/// Translates damage type constant to a string representation (built-in). +AString DamageTypeToString(eDamageType a_DamageType) +{ + switch (a_DamageType) + { + case dtAttack: return "dtAttack"; + case dtRangedAttack: return "dtRangedAttack"; + case dtLightning: return "dtLightning"; + case dtFalling: return "dtFalling"; + case dtDrowning: return "dtDrowning"; + case dtSuffocating: return "dtSuffocation"; + case dtStarving: return "dtStarving"; + case dtCactusContact: return "dtCactusContact"; + case dtLavaContact: return "dtLavaContact"; + case dtPoisoning: return "dtPoisoning"; + case dtOnFire: return "dtOnFire"; + case dtFireContact: return "dtFireContact"; + case dtInVoid: return "dtInVoid"; + case dtPotionOfHarming: return "dtPotionOfHarming"; + case dtAdmin: return "dtAdmin"; + } + + // Unknown damage type: + ASSERT(!"Unknown DamageType"); + return Printf("dtUnknown_%d", (int)a_DamageType); +} + + + + + +/// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure +eDamageType StringToDamageType(const AString & a_DamageTypeString) +{ + // First try decoding as a number: + int res = atoi(a_DamageTypeString.c_str()); + if ((res != 0) || (a_DamageTypeString == "0")) + { + // It was a valid number + return (eDamageType)res; + } + + // Decode using a built-in map: + static struct + { + eDamageType m_DamageType; + const char * m_String; + } DamageTypeMap [] = + { + // Cannonical names: + { dtAttack, "dtAttack"}, + { dtRangedAttack, "dtRangedAttack"}, + { dtLightning, "dtLightning"}, + { dtFalling, "dtFalling"}, + { dtDrowning, "dtDrowning"}, + { dtSuffocating, "dtSuffocation"}, + { dtStarving, "dtStarving"}, + { dtCactusContact, "dtCactusContact"}, + { dtLavaContact, "dtLavaContact"}, + { dtPoisoning, "dtPoisoning"}, + { dtOnFire, "dtOnFire"}, + { dtFireContact, "dtFireContact"}, + { dtInVoid, "dtInVoid"}, + { dtPotionOfHarming, "dtPotionOfHarming"}, + { dtAdmin, "dtAdmin"}, + + // Common synonyms: + { dtAttack, "dtPawnAttack"}, + { dtAttack, "dtEntityAttack"}, + { dtAttack, "dtMob"}, + { dtAttack, "dtMobAttack"}, + { dtRangedAttack, "dtArrowAttack"}, + { dtRangedAttack, "dtArrow"}, + { dtRangedAttack, "dtProjectile"}, + { dtFalling, "dtFall"}, + { dtDrowning, "dtDrown"}, + { dtSuffocating, "dtSuffocation"}, + { dtStarving, "dtStarvation"}, + { dtStarving, "dtHunger"}, + { dtCactusContact, "dtCactus"}, + { dtCactusContact, "dtCactuses"}, + { dtCactusContact, "dtCacti"}, + { dtLavaContact, "dtLava"}, + { dtPoisoning, "dtPoison"}, + { dtOnFire, "dtBurning"}, + { dtFireContact, "dtInFire"}, + { dtAdmin, "dtPlugin"}, + } ; + for (int i = 0; i < ARRAYCOUNT(DamageTypeMap); i++) + { + if (NoCaseCompare(DamageTypeMap[i].m_String, a_DamageTypeString) == 0) + { + return DamageTypeMap[i].m_DamageType; + } + } // for i - DamageTypeMap[] + + // Not found: + return (eDamageType)-1; +} + + + + + +cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default) +{ + AString ItemStr = a_IniFile.GetValueSet(a_Section, a_Key, a_Default); + cItem res; + if (!StringToItem(ItemStr, res)) + { + res.Empty(); + } + return res; +} + + + + + +// This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor: +class cBlockPropertiesInitializer +{ +public: + cBlockPropertiesInitializer(void) + { + memset(g_BlockLightValue, 0x00, sizeof(g_BlockLightValue)); + memset(g_BlockSpreadLightFalloff, 0x0f, sizeof(g_BlockSpreadLightFalloff)); // 0x0f means total falloff + memset(g_BlockTransparent, 0x00, sizeof(g_BlockTransparent)); + memset(g_BlockOneHitDig, 0x00, sizeof(g_BlockOneHitDig)); + memset(g_BlockPistonBreakable, 0x00, sizeof(g_BlockPistonBreakable)); + memset(g_BlockIsTorchPlaceable, 0x00, sizeof(g_BlockIsTorchPlaceable)); + + // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415 + for (int i = 0; i < ARRAYCOUNT(g_BlockIsSnowable); i++) + { + g_BlockIsSnowable[i] = true; + } + memset(g_BlockRequiresSpecialTool, 0x00, sizeof(g_BlockRequiresSpecialTool)); // Set all blocks to false + + // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415 + for (int i = 0; i < ARRAYCOUNT(g_BlockIsSolid); i++) + { + g_BlockIsSolid[i] = true; + } + + // Emissive blocks + g_BlockLightValue[E_BLOCK_FIRE] = 15; + g_BlockLightValue[E_BLOCK_GLOWSTONE] = 15; + g_BlockLightValue[E_BLOCK_JACK_O_LANTERN] = 15; + g_BlockLightValue[E_BLOCK_LAVA] = 15; + g_BlockLightValue[E_BLOCK_STATIONARY_LAVA] = 15; + g_BlockLightValue[E_BLOCK_END_PORTAL] = 15; + g_BlockLightValue[E_BLOCK_REDSTONE_LAMP_ON] = 15; + g_BlockLightValue[E_BLOCK_TORCH] = 14; + g_BlockLightValue[E_BLOCK_BURNING_FURNACE] = 13; + g_BlockLightValue[E_BLOCK_NETHER_PORTAL] = 11; + g_BlockLightValue[E_BLOCK_REDSTONE_ORE_GLOWING] = 9; + g_BlockLightValue[E_BLOCK_REDSTONE_REPEATER_ON] = 9; + g_BlockLightValue[E_BLOCK_REDSTONE_TORCH_ON] = 7; + g_BlockLightValue[E_BLOCK_BREWING_STAND] = 1; + g_BlockLightValue[E_BLOCK_BROWN_MUSHROOM] = 1; + g_BlockLightValue[E_BLOCK_DRAGON_EGG] = 1; + + // Spread blocks + g_BlockSpreadLightFalloff[E_BLOCK_AIR] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_CAKE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_CHEST] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_COBWEB] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_CROPS] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_FENCE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_FENCE_GATE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_FIRE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_GLASS] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_GLASS_PANE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_GLOWSTONE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_IRON_BARS] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_IRON_DOOR] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_LEAVES] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_SIGN_POST] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_TORCH] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_VINES] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_WALLSIGN] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_WOODEN_DOOR] = 1; + + // Light in water and lava dissapears faster: + g_BlockSpreadLightFalloff[E_BLOCK_LAVA] = 3; + g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_LAVA] = 3; + g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_WATER] = 3; + g_BlockSpreadLightFalloff[E_BLOCK_WATER] = 3; + + // Transparent blocks + g_BlockTransparent[E_BLOCK_ACTIVATOR_RAIL] = true; + g_BlockTransparent[E_BLOCK_AIR] = true; + g_BlockTransparent[E_BLOCK_BIG_FLOWER] = true; + g_BlockTransparent[E_BLOCK_BROWN_MUSHROOM] = true; + g_BlockTransparent[E_BLOCK_CARROTS] = true; + g_BlockTransparent[E_BLOCK_CHEST] = true; + g_BlockTransparent[E_BLOCK_COBWEB] = true; + g_BlockTransparent[E_BLOCK_CROPS] = true; + g_BlockTransparent[E_BLOCK_DANDELION] = true; + g_BlockTransparent[E_BLOCK_DETECTOR_RAIL] = true; + g_BlockTransparent[E_BLOCK_FENCE] = true; + g_BlockTransparent[E_BLOCK_FENCE_GATE] = true; + g_BlockTransparent[E_BLOCK_FIRE] = true; + g_BlockTransparent[E_BLOCK_FLOWER] = true; + g_BlockTransparent[E_BLOCK_FLOWER_POT] = true; + g_BlockTransparent[E_BLOCK_GLASS] = true; + g_BlockTransparent[E_BLOCK_GLASS_PANE] = true; + g_BlockTransparent[E_BLOCK_STAINED_GLASS] = true; + g_BlockTransparent[E_BLOCK_STAINED_GLASS_PANE] = true; + g_BlockTransparent[E_BLOCK_ICE] = true; + g_BlockTransparent[E_BLOCK_IRON_DOOR] = true; + g_BlockTransparent[E_BLOCK_LAVA] = true; + g_BlockTransparent[E_BLOCK_LEAVES] = true; + g_BlockTransparent[E_BLOCK_LEVER] = true; + g_BlockTransparent[E_BLOCK_MELON_STEM] = true; + g_BlockTransparent[E_BLOCK_NETHER_BRICK_FENCE] = true; + g_BlockTransparent[E_BLOCK_NEW_LEAVES] = true; + g_BlockTransparent[E_BLOCK_POTATOES] = true; + g_BlockTransparent[E_BLOCK_POWERED_RAIL] = true; + g_BlockTransparent[E_BLOCK_PISTON_EXTENSION] = true; + g_BlockTransparent[E_BLOCK_PUMPKIN_STEM] = true; + g_BlockTransparent[E_BLOCK_RAIL] = true; + g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true; + g_BlockTransparent[E_BLOCK_SIGN_POST] = true; + g_BlockTransparent[E_BLOCK_SNOW] = true; + g_BlockTransparent[E_BLOCK_STATIONARY_LAVA] = true; + g_BlockTransparent[E_BLOCK_STATIONARY_WATER] = true; + g_BlockTransparent[E_BLOCK_STONE_PRESSURE_PLATE] = true; + g_BlockTransparent[E_BLOCK_TALL_GRASS] = true; + g_BlockTransparent[E_BLOCK_TORCH] = true; + g_BlockTransparent[E_BLOCK_VINES] = true; + g_BlockTransparent[E_BLOCK_WALLSIGN] = true; + g_BlockTransparent[E_BLOCK_WATER] = true; + g_BlockTransparent[E_BLOCK_WOODEN_DOOR] = true; + g_BlockTransparent[E_BLOCK_WOODEN_PRESSURE_PLATE] = true; + + // TODO: Any other transparent blocks? + + // One hit break blocks + g_BlockOneHitDig[E_BLOCK_ACTIVE_COMPARATOR] = true; + g_BlockOneHitDig[E_BLOCK_BIG_FLOWER] = true; + g_BlockOneHitDig[E_BLOCK_BROWN_MUSHROOM] = true; + g_BlockOneHitDig[E_BLOCK_CARROTS] = true; + g_BlockOneHitDig[E_BLOCK_CROPS] = true; + g_BlockOneHitDig[E_BLOCK_DANDELION] = true; + g_BlockOneHitDig[E_BLOCK_FIRE] = true; + g_BlockOneHitDig[E_BLOCK_FLOWER] = true; + g_BlockOneHitDig[E_BLOCK_FLOWER_POT] = true; + g_BlockOneHitDig[E_BLOCK_INACTIVE_COMPARATOR] = true; + g_BlockOneHitDig[E_BLOCK_LOCKED_CHEST] = true; + g_BlockOneHitDig[E_BLOCK_MELON_STEM] = true; + g_BlockOneHitDig[E_BLOCK_POTATOES] = true; + g_BlockOneHitDig[E_BLOCK_PUMPKIN_STEM] = true; + g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_OFF] = true; + g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_ON] = true; + g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_OFF] = true; + g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_ON] = true; + g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true; + g_BlockOneHitDig[E_BLOCK_RED_MUSHROOM] = true; + g_BlockOneHitDig[E_BLOCK_REEDS] = true; + g_BlockOneHitDig[E_BLOCK_SAPLING] = true; + g_BlockOneHitDig[E_BLOCK_TNT] = true; + g_BlockOneHitDig[E_BLOCK_TALL_GRASS] = true; + g_BlockOneHitDig[E_BLOCK_TORCH] = true; + + // Blocks that breaks when pushed by piston + g_BlockPistonBreakable[E_BLOCK_ACTIVE_COMPARATOR] = true; + g_BlockPistonBreakable[E_BLOCK_AIR] = true; + g_BlockPistonBreakable[E_BLOCK_BED] = true; + g_BlockPistonBreakable[E_BLOCK_BIG_FLOWER] = true; + g_BlockPistonBreakable[E_BLOCK_BROWN_MUSHROOM] = true; + g_BlockPistonBreakable[E_BLOCK_COBWEB] = true; + g_BlockPistonBreakable[E_BLOCK_CROPS] = true; + g_BlockPistonBreakable[E_BLOCK_DANDELION] = true; + g_BlockPistonBreakable[E_BLOCK_DEAD_BUSH] = true; + g_BlockPistonBreakable[E_BLOCK_FIRE] = true; + g_BlockPistonBreakable[E_BLOCK_FLOWER] = true; + g_BlockPistonBreakable[E_BLOCK_INACTIVE_COMPARATOR] = true; + g_BlockPistonBreakable[E_BLOCK_IRON_DOOR] = true; + g_BlockPistonBreakable[E_BLOCK_JACK_O_LANTERN] = true; + g_BlockPistonBreakable[E_BLOCK_LADDER] = true; + g_BlockPistonBreakable[E_BLOCK_LAVA] = true; + g_BlockPistonBreakable[E_BLOCK_LEVER] = true; + g_BlockPistonBreakable[E_BLOCK_MELON] = true; + g_BlockPistonBreakable[E_BLOCK_MELON_STEM] = true; + g_BlockPistonBreakable[E_BLOCK_PUMPKIN] = true; + g_BlockPistonBreakable[E_BLOCK_PUMPKIN_STEM] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_REPEATER_OFF] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_REPEATER_ON] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_OFF] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_ON] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_WIRE] = true; + g_BlockPistonBreakable[E_BLOCK_RED_MUSHROOM] = true; + g_BlockPistonBreakable[E_BLOCK_REEDS] = true; + g_BlockPistonBreakable[E_BLOCK_SNOW] = true; + g_BlockPistonBreakable[E_BLOCK_STATIONARY_LAVA] = true; + g_BlockPistonBreakable[E_BLOCK_STATIONARY_WATER] = true; + g_BlockPistonBreakable[E_BLOCK_STONE_BUTTON] = true; + g_BlockPistonBreakable[E_BLOCK_STONE_PRESSURE_PLATE] = true; + g_BlockPistonBreakable[E_BLOCK_TALL_GRASS] = true; + g_BlockPistonBreakable[E_BLOCK_TORCH] = true; + g_BlockPistonBreakable[E_BLOCK_VINES] = true; + g_BlockPistonBreakable[E_BLOCK_WATER] = true; + g_BlockPistonBreakable[E_BLOCK_WOODEN_DOOR] = true; + g_BlockPistonBreakable[E_BLOCK_WOODEN_PRESSURE_PLATE] = true; + + + // Blocks that can be snowed over: + g_BlockIsSnowable[E_BLOCK_ACTIVE_COMPARATOR] = false; + g_BlockIsSnowable[E_BLOCK_AIR] = false; + g_BlockIsSnowable[E_BLOCK_BIG_FLOWER] = false; + g_BlockIsSnowable[E_BLOCK_BROWN_MUSHROOM] = false; + g_BlockIsSnowable[E_BLOCK_CACTUS] = false; + g_BlockIsSnowable[E_BLOCK_CHEST] = false; + g_BlockIsSnowable[E_BLOCK_CROPS] = false; + g_BlockIsSnowable[E_BLOCK_DANDELION] = false; + g_BlockIsSnowable[E_BLOCK_FIRE] = false; + g_BlockIsSnowable[E_BLOCK_FLOWER] = false; + g_BlockIsSnowable[E_BLOCK_GLASS] = false; + g_BlockIsSnowable[E_BLOCK_ICE] = false; + g_BlockIsSnowable[E_BLOCK_INACTIVE_COMPARATOR] = false; + g_BlockIsSnowable[E_BLOCK_LAVA] = false; + g_BlockIsSnowable[E_BLOCK_LILY_PAD] = false; + g_BlockIsSnowable[E_BLOCK_LOCKED_CHEST] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_OFF] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_ON] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_OFF] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_ON] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_WIRE] = false; + g_BlockIsSnowable[E_BLOCK_RED_MUSHROOM] = false; + g_BlockIsSnowable[E_BLOCK_REEDS] = false; + g_BlockIsSnowable[E_BLOCK_SAPLING] = false; + g_BlockIsSnowable[E_BLOCK_SIGN_POST] = false; + g_BlockIsSnowable[E_BLOCK_SNOW] = false; + g_BlockIsSnowable[E_BLOCK_STATIONARY_LAVA] = false; + g_BlockIsSnowable[E_BLOCK_STATIONARY_WATER] = false; + g_BlockIsSnowable[E_BLOCK_TALL_GRASS] = false; + g_BlockIsSnowable[E_BLOCK_TNT] = false; + g_BlockIsSnowable[E_BLOCK_TORCH] = false; + g_BlockIsSnowable[E_BLOCK_VINES] = false; + g_BlockIsSnowable[E_BLOCK_WALLSIGN] = false; + g_BlockIsSnowable[E_BLOCK_WATER] = false; + + + // Blocks that don't drop without a special tool + g_BlockRequiresSpecialTool[E_BLOCK_BRICK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_CAULDRON] = true; + g_BlockRequiresSpecialTool[E_BLOCK_COAL_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_COBBLESTONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_COBBLESTONE_STAIRS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_COBWEB] = true; + g_BlockRequiresSpecialTool[E_BLOCK_DIAMOND_BLOCK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_DIAMOND_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_DOUBLE_STONE_SLAB] = true; + g_BlockRequiresSpecialTool[E_BLOCK_EMERALD_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_END_STONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_GOLD_BLOCK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_GOLD_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_IRON_BLOCK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_IRON_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_BLOCK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_MOSSY_COBBLESTONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_NETHERRACK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_NETHER_BRICK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_NETHER_BRICK_STAIRS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_OBSIDIAN] = true; + g_BlockRequiresSpecialTool[E_BLOCK_REDSTONE_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_REDSTONE_ORE_GLOWING] = true; + g_BlockRequiresSpecialTool[E_BLOCK_SANDSTONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_SANDSTONE_STAIRS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_SNOW] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE_BRICKS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE_BRICK_STAIRS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE_PRESSURE_PLATE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE_SLAB] = true; + g_BlockRequiresSpecialTool[E_BLOCK_VINES] = true; + + // Nonsolid Blocks: + g_BlockIsSolid[E_BLOCK_ACTIVATOR_RAIL] = false; + g_BlockIsSolid[E_BLOCK_AIR] = false; + g_BlockIsSolid[E_BLOCK_BIG_FLOWER] = false; + g_BlockIsSolid[E_BLOCK_BROWN_MUSHROOM] = false; + g_BlockIsSolid[E_BLOCK_CARROTS] = false; + g_BlockIsSolid[E_BLOCK_COBWEB] = false; + g_BlockIsSolid[E_BLOCK_CROPS] = false; + g_BlockIsSolid[E_BLOCK_DANDELION] = false; + g_BlockIsSolid[E_BLOCK_DETECTOR_RAIL] = false; + g_BlockIsSolid[E_BLOCK_END_PORTAL] = false; + g_BlockIsSolid[E_BLOCK_FIRE] = false; + g_BlockIsSolid[E_BLOCK_FLOWER] = false; + g_BlockIsSolid[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE] = false; + g_BlockIsSolid[E_BLOCK_LAVA] = false; + g_BlockIsSolid[E_BLOCK_LEVER] = false; + g_BlockIsSolid[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE] = false; + g_BlockIsSolid[E_BLOCK_MELON_STEM] = false; + g_BlockIsSolid[E_BLOCK_NETHER_PORTAL] = false; + g_BlockIsSolid[E_BLOCK_PISTON] = false; + g_BlockIsSolid[E_BLOCK_PISTON_EXTENSION] = false; + g_BlockIsSolid[E_BLOCK_RAIL] = false; + g_BlockIsSolid[E_BLOCK_REDSTONE_REPEATER_OFF] = false; + g_BlockIsSolid[E_BLOCK_REDSTONE_REPEATER_ON] = false; + g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_OFF] = false; + g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_ON] = false; + g_BlockIsSolid[E_BLOCK_REDSTONE_WIRE] = false; + g_BlockIsSolid[E_BLOCK_RED_MUSHROOM] = false; + g_BlockIsSolid[E_BLOCK_REEDS] = false; + g_BlockIsSolid[E_BLOCK_SAPLING] = false; + g_BlockIsSolid[E_BLOCK_SIGN_POST] = false; + g_BlockIsSolid[E_BLOCK_SNOW] = false; + g_BlockIsSolid[E_BLOCK_STATIONARY_LAVA] = false; + g_BlockIsSolid[E_BLOCK_STATIONARY_WATER] = false; + g_BlockIsSolid[E_BLOCK_STONE_BUTTON] = false; + g_BlockIsSolid[E_BLOCK_STONE_PRESSURE_PLATE] = false; + g_BlockIsSolid[E_BLOCK_TALL_GRASS] = false; + g_BlockIsSolid[E_BLOCK_TORCH] = false; + g_BlockIsSolid[E_BLOCK_TRIPWIRE] = false; + g_BlockIsSolid[E_BLOCK_VINES] = false; + g_BlockIsSolid[E_BLOCK_WALLSIGN] = false; + g_BlockIsSolid[E_BLOCK_WATER] = false; + g_BlockIsSolid[E_BLOCK_WOODEN_BUTTON] = false; + g_BlockIsSolid[E_BLOCK_WOODEN_PRESSURE_PLATE] = false; + g_BlockIsSolid[E_BLOCK_WOODEN_SLAB] = false; + + // Torch placeable + g_BlockIsTorchPlaceable[E_BLOCK_BEDROCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_COAL] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_REDSTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BOOKCASE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BRICK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_CLAY] = true; + g_BlockIsTorchPlaceable[E_BLOCK_COAL_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_COBBLESTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_COMMAND_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_CRAFTING_TABLE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DIRT] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DISPENSER] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_STONE_SLAB] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_WOODEN_SLAB] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DROPPER] = true; + g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_END_STONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_FURNACE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GLOWSTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GOLD_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GOLD_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GRASS] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GRAVEL] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HARDENED_CLAY] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HAY_BALE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HUGE_BROWN_MUSHROOM] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HUGE_RED_MUSHROOM] = true; + g_BlockIsTorchPlaceable[E_BLOCK_IRON_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_IRON_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_JACK_O_LANTERN] = true; + g_BlockIsTorchPlaceable[E_BLOCK_JUKEBOX] = true; + g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_LOG] = true; + g_BlockIsTorchPlaceable[E_BLOCK_MELON] = true; + g_BlockIsTorchPlaceable[E_BLOCK_MOSSY_COBBLESTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_MYCELIUM] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NETHERRACK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NETHER_BRICK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NETHER_QUARTZ_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NOTE_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_OBSIDIAN] = true; + g_BlockIsTorchPlaceable[E_BLOCK_PACKED_ICE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_PLANKS] = true; + g_BlockIsTorchPlaceable[E_BLOCK_PUMPKIN] = true; + g_BlockIsTorchPlaceable[E_BLOCK_QUARTZ_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_OFF] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_ON] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE_GLOWING] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SANDSTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SAND] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SILVERFISH_EGG] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SPONGE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_STAINED_CLAY] = true; + g_BlockIsTorchPlaceable[E_BLOCK_WOOL] = true; + g_BlockIsTorchPlaceable[E_BLOCK_STONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_STONE_BRICKS] = true; + } +} BlockPropertiesInitializer; + + + + diff --git a/src/BlockID.h b/src/BlockID.h new file mode 100644 index 000000000..f3cbc46d6 --- /dev/null +++ b/src/BlockID.h @@ -0,0 +1,907 @@ +#pragma once + +// tolua_begin +enum ENUM_BLOCK_ID +{ + E_BLOCK_AIR = 0, + E_BLOCK_STONE = 1, + E_BLOCK_GRASS = 2, + E_BLOCK_DIRT = 3, + E_BLOCK_COBBLESTONE = 4, + E_BLOCK_PLANKS = 5, + E_BLOCK_SAPLING = 6, + E_BLOCK_BEDROCK = 7, + E_BLOCK_WATER = 8, + E_BLOCK_STATIONARY_WATER = 9, + E_BLOCK_LAVA = 10, + E_BLOCK_STATIONARY_LAVA = 11, + E_BLOCK_SAND = 12, + E_BLOCK_GRAVEL = 13, + E_BLOCK_GOLD_ORE = 14, + E_BLOCK_IRON_ORE = 15, + E_BLOCK_COAL_ORE = 16, + E_BLOCK_LOG = 17, + E_BLOCK_LEAVES = 18, + E_BLOCK_SPONGE = 19, + E_BLOCK_GLASS = 20, + E_BLOCK_LAPIS_ORE = 21, + E_BLOCK_LAPIS_BLOCK = 22, + E_BLOCK_DISPENSER = 23, + E_BLOCK_SANDSTONE = 24, + E_BLOCK_NOTE_BLOCK = 25, + E_BLOCK_BED = 26, + E_BLOCK_POWERED_RAIL = 27, + E_BLOCK_DETECTOR_RAIL = 28, + E_BLOCK_STICKY_PISTON = 29, + E_BLOCK_COBWEB = 30, + E_BLOCK_TALL_GRASS = 31, + E_BLOCK_DEAD_BUSH = 32, + E_BLOCK_PISTON = 33, + E_BLOCK_PISTON_EXTENSION = 34, + E_BLOCK_WOOL = 35, + E_BLOCK_PISTON_MOVED_BLOCK = 36, + E_BLOCK_DANDELION = 37, + E_BLOCK_FLOWER = 38, + E_BLOCK_BROWN_MUSHROOM = 39, + E_BLOCK_RED_MUSHROOM = 40, + E_BLOCK_GOLD_BLOCK = 41, + E_BLOCK_IRON_BLOCK = 42, + E_BLOCK_DOUBLE_STONE_SLAB = 43, + E_BLOCK_STONE_SLAB = 44, + E_BLOCK_BRICK = 45, + E_BLOCK_TNT = 46, + E_BLOCK_BOOKCASE = 47, + E_BLOCK_MOSSY_COBBLESTONE = 48, + E_BLOCK_OBSIDIAN = 49, + E_BLOCK_TORCH = 50, + E_BLOCK_FIRE = 51, + E_BLOCK_MOB_SPAWNER = 52, + E_BLOCK_WOODEN_STAIRS = 53, + E_BLOCK_CHEST = 54, + E_BLOCK_REDSTONE_WIRE = 55, + E_BLOCK_DIAMOND_ORE = 56, + E_BLOCK_DIAMOND_BLOCK = 57, + E_BLOCK_CRAFTING_TABLE = 58, + E_BLOCK_WORKBENCH = 58, + E_BLOCK_CROPS = 59, + E_BLOCK_FARMLAND = 60, + E_BLOCK_FURNACE = 61, + E_BLOCK_LIT_FURNACE = 62, + E_BLOCK_BURNING_FURNACE = 62, + E_BLOCK_SIGN_POST = 63, + E_BLOCK_WOODEN_DOOR = 64, + E_BLOCK_LADDER = 65, + E_BLOCK_RAIL = 66, + E_BLOCK_MINECART_TRACKS = 66, + E_BLOCK_COBBLESTONE_STAIRS = 67, + E_BLOCK_WALLSIGN = 68, + E_BLOCK_LEVER = 69, + E_BLOCK_STONE_PRESSURE_PLATE = 70, + E_BLOCK_IRON_DOOR = 71, + E_BLOCK_WOODEN_PRESSURE_PLATE = 72, + E_BLOCK_REDSTONE_ORE = 73, + E_BLOCK_REDSTONE_ORE_GLOWING = 74, + E_BLOCK_REDSTONE_TORCH_OFF = 75, + E_BLOCK_REDSTONE_TORCH_ON = 76, + E_BLOCK_STONE_BUTTON = 77, + E_BLOCK_SNOW = 78, + E_BLOCK_ICE = 79, + E_BLOCK_SNOW_BLOCK = 80, + E_BLOCK_CACTUS = 81, + E_BLOCK_CLAY = 82, + E_BLOCK_SUGARCANE = 83, + E_BLOCK_REEDS = 83, + E_BLOCK_JUKEBOX = 84, + E_BLOCK_FENCE = 85, + E_BLOCK_PUMPKIN = 86, + E_BLOCK_NETHERRACK = 87, + E_BLOCK_SOULSAND = 88, + E_BLOCK_GLOWSTONE = 89, + E_BLOCK_NETHER_PORTAL = 90, + E_BLOCK_JACK_O_LANTERN = 91, + E_BLOCK_CAKE = 92, + E_BLOCK_REDSTONE_REPEATER_OFF = 93, + E_BLOCK_REDSTONE_REPEATER_ON = 94, + E_BLOCK_STAINED_GLASS = 95, + E_BLOCK_TRAPDOOR = 96, + E_BLOCK_SILVERFISH_EGG = 97, + E_BLOCK_STONE_BRICKS = 98, + E_BLOCK_HUGE_BROWN_MUSHROOM = 99, + E_BLOCK_HUGE_RED_MUSHROOM = 100, + E_BLOCK_IRON_BARS = 101, + E_BLOCK_GLASS_PANE = 102, + E_BLOCK_MELON = 103, + E_BLOCK_PUMPKIN_STEM = 104, + E_BLOCK_MELON_STEM = 105, + E_BLOCK_VINES = 106, + E_BLOCK_FENCE_GATE = 107, + E_BLOCK_BRICK_STAIRS = 108, + E_BLOCK_STONE_BRICK_STAIRS = 109, + E_BLOCK_MYCELIUM = 110, + E_BLOCK_LILY_PAD = 111, + E_BLOCK_NETHER_BRICK = 112, + E_BLOCK_NETHER_BRICK_FENCE = 113, + E_BLOCK_NETHER_BRICK_STAIRS = 114, + E_BLOCK_NETHER_WART = 115, + E_BLOCK_ENCHANTMENT_TABLE = 116, + E_BLOCK_BREWING_STAND = 117, + E_BLOCK_CAULDRON = 118, + E_BLOCK_END_PORTAL = 119, + E_BLOCK_END_PORTAL_FRAME = 120, + E_BLOCK_END_STONE = 121, + E_BLOCK_DRAGON_EGG = 122, + E_BLOCK_REDSTONE_LAMP_OFF = 123, + E_BLOCK_REDSTONE_LAMP_ON = 124, + E_BLOCK_DOUBLE_WOODEN_SLAB = 125, + E_BLOCK_WOODEN_SLAB = 126, + E_BLOCK_COCOA_POD = 127, + E_BLOCK_SANDSTONE_STAIRS = 128, + E_BLOCK_EMERALD_ORE = 129, + E_BLOCK_ENDER_CHEST = 130, + E_BLOCK_TRIPWIRE_HOOK = 131, + E_BLOCK_TRIPWIRE = 132, + E_BLOCK_EMERALD_BLOCK = 133, + E_BLOCK_SPRUCE_WOOD_STAIRS = 134, + E_BLOCK_BIRCH_WOOD_STAIRS = 135, + E_BLOCK_JUNGLE_WOOD_STAIRS = 136, + E_BLOCK_COMMAND_BLOCK = 137, + E_BLOCK_BEACON = 138, + E_BLOCK_COBBLESTONE_WALL = 139, + E_BLOCK_FLOWER_POT = 140, + E_BLOCK_CARROTS = 141, + E_BLOCK_POTATOES = 142, + E_BLOCK_WOODEN_BUTTON = 143, + E_BLOCK_HEAD = 144, + E_BLOCK_ANVIL = 145, + E_BLOCK_TRAPPED_CHEST = 146, + E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE = 147, + E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE = 148, + + E_BLOCK_INACTIVE_COMPARATOR = 149, + E_BLOCK_ACTIVE_COMPARATOR = 150, + E_BLOCK_DAYLIGHT_SENSOR = 151, + E_BLOCK_BLOCK_OF_REDSTONE = 152, + + E_BLOCK_NETHER_QUARTZ_ORE = 153, + E_BLOCK_HOPPER = 154, + E_BLOCK_QUARTZ_BLOCK = 155, + E_BLOCK_QUARTZ_STAIRS = 156, + E_BLOCK_ACTIVATOR_RAIL = 157, + + E_BLOCK_DROPPER = 158, + E_BLOCK_STAINED_CLAY = 159, + E_BLOCK_STAINED_GLASS_PANE = 160, + E_BLOCK_NEW_LEAVES = 161, // Acacia and Dark Oak IDs in Minecraft 1.7.x + E_BLOCK_NEW_LOG = 162, + E_BLOCK_ACACIA_WOOD_STAIRS = 163, + E_BLOCK_DARK_OAK_WOOD_STAIRS = 164, ///////////////////////////////// + E_BLOCK_HAY_BALE = 170, + E_BLOCK_CARPET = 171, + E_BLOCK_HARDENED_CLAY = 172, + E_BLOCK_BLOCK_OF_COAL = 173, + E_BLOCK_PACKED_ICE = 174, + E_BLOCK_BIG_FLOWER = 175, + + // Keep these two as the last values, without a number - they will get their correct number assigned automagically by C++ + // IsValidBlock() depends on this + E_BLOCK_NUMBER_OF_TYPES, ///< Number of individual (different) blocktypes + E_BLOCK_MAX_TYPE_ID = E_BLOCK_NUMBER_OF_TYPES - 1, ///< Maximum BlockType number used + + // Synonym or ID compatibility + + E_BLOCK_YELLOW_FLOWER = E_BLOCK_DANDELION, + E_BLOCK_RED_ROSE = E_BLOCK_FLOWER, + E_BLOCK_LOCKED_CHEST = E_BLOCK_STAINED_GLASS, +}; +// tolua_end + +// tolua_begin +enum ENUM_ITEM_ID +{ + E_ITEM_EMPTY = -1, + + E_ITEM_FIRST = 256, // First true item type + + E_ITEM_IRON_SHOVEL = 256, + E_ITEM_IRON_PICKAXE = 257, + E_ITEM_IRON_AXE = 258, + E_ITEM_FLINT_AND_STEEL = 259, + E_ITEM_RED_APPLE = 260, + E_ITEM_BOW = 261, + E_ITEM_ARROW = 262, + E_ITEM_COAL = 263, + E_ITEM_DIAMOND = 264, + E_ITEM_IRON = 265, + E_ITEM_GOLD = 266, + E_ITEM_IRON_SWORD = 267, + E_ITEM_WOODEN_SWORD = 268, + E_ITEM_WOODEN_SHOVEL = 269, + E_ITEM_WOODEN_PICKAXE = 270, + E_ITEM_WOODEN_AXE = 271, + E_ITEM_STONE_SWORD = 272, + E_ITEM_STONE_SHOVEL = 273, + E_ITEM_STONE_PICKAXE = 274, + E_ITEM_STONE_AXE = 275, + E_ITEM_DIAMOND_SWORD = 276, + E_ITEM_DIAMOND_SHOVEL = 277, + E_ITEM_DIAMOND_PICKAXE = 278, + E_ITEM_DIAMOND_AXE = 279, + E_ITEM_STICK = 280, + E_ITEM_BOWL = 281, + E_ITEM_MUSHROOM_SOUP = 282, + E_ITEM_GOLD_SWORD = 283, + E_ITEM_GOLD_SHOVEL = 284, + E_ITEM_GOLD_PICKAXE = 285, + E_ITEM_GOLD_AXE = 286, + E_ITEM_STRING = 287, + E_ITEM_FEATHER = 288, + E_ITEM_GUNPOWDER = 289, + E_ITEM_WOODEN_HOE = 290, + E_ITEM_STONE_HOE = 291, + E_ITEM_IRON_HOE = 292, + E_ITEM_DIAMOND_HOE = 293, + E_ITEM_GOLD_HOE = 294, + E_ITEM_SEEDS = 295, + E_ITEM_WHEAT = 296, + E_ITEM_BREAD = 297, + E_ITEM_LEATHER_CAP = 298, + E_ITEM_LEATHER_TUNIC = 299, + E_ITEM_LEATHER_PANTS = 300, + E_ITEM_LEATHER_BOOTS = 301, + E_ITEM_CHAIN_HELMET = 302, + E_ITEM_CHAIN_CHESTPLATE = 303, + E_ITEM_CHAIN_LEGGINGS = 304, + E_ITEM_CHAIN_BOOTS = 305, + E_ITEM_IRON_HELMET = 306, + E_ITEM_IRON_CHESTPLATE = 307, + E_ITEM_IRON_LEGGINGS = 308, + E_ITEM_IRON_BOOTS = 309, + E_ITEM_DIAMOND_HELMET = 310, + E_ITEM_DIAMOND_CHESTPLATE = 311, + E_ITEM_DIAMOND_LEGGINGS = 312, + E_ITEM_DIAMOND_BOOTS = 313, + E_ITEM_GOLD_HELMET = 314, + E_ITEM_GOLD_CHESTPLATE = 315, + E_ITEM_GOLD_LEGGINGS = 316, + E_ITEM_GOLD_BOOTS = 317, + E_ITEM_FLINT = 318, + E_ITEM_RAW_PORKCHOP = 319, + E_ITEM_COOKED_PORKCHOP = 320, + E_ITEM_PAINTINGS = 321, + E_ITEM_GOLDEN_APPLE = 322, + E_ITEM_SIGN = 323, + E_ITEM_WOODEN_DOOR = 324, + E_ITEM_BUCKET = 325, + E_ITEM_WATER_BUCKET = 326, + E_ITEM_LAVA_BUCKET = 327, + E_ITEM_MINECART = 328, + E_ITEM_SADDLE = 329, + E_ITEM_IRON_DOOR = 330, + E_ITEM_REDSTONE_DUST = 331, + E_ITEM_SNOWBALL = 332, + E_ITEM_BOAT = 333, + E_ITEM_LEATHER = 334, + E_ITEM_MILK = 335, + E_ITEM_CLAY_BRICK = 336, + E_ITEM_CLAY = 337, + E_ITEM_SUGARCANE = 338, + E_ITEM_SUGAR_CANE = 338, + E_ITEM_PAPER = 339, + E_ITEM_BOOK = 340, + E_ITEM_SLIMEBALL = 341, + E_ITEM_CHEST_MINECART = 342, + E_ITEM_FURNACE_MINECART = 343, + E_ITEM_EGG = 344, + E_ITEM_COMPASS = 345, + E_ITEM_FISHING_ROD = 346, + E_ITEM_CLOCK = 347, + E_ITEM_GLOWSTONE_DUST = 348, + E_ITEM_RAW_FISH = 349, + E_ITEM_COOKED_FISH = 350, + E_ITEM_DYE = 351, + E_ITEM_BONE = 352, + E_ITEM_SUGAR = 353, + E_ITEM_CAKE = 354, + E_ITEM_BED = 355, + E_ITEM_REDSTONE_REPEATER = 356, + E_ITEM_COOKIE = 357, + E_ITEM_MAP = 358, + E_ITEM_SHEARS = 359, + E_ITEM_MELON_SLICE = 360, + E_ITEM_PUMPKIN_SEEDS = 361, + E_ITEM_MELON_SEEDS = 362, + E_ITEM_RAW_BEEF = 363, + E_ITEM_STEAK = 364, + E_ITEM_RAW_CHICKEN = 365, + E_ITEM_COOKED_CHICKEN = 366, + E_ITEM_ROTTEN_FLESH = 367, + E_ITEM_ENDER_PEARL = 368, + E_ITEM_BLAZE_ROD = 369, + E_ITEM_GHAST_TEAR = 370, + E_ITEM_GOLD_NUGGET = 371, + E_ITEM_NETHER_WART = 372, + E_ITEM_POTIONS = 373, + E_ITEM_GLASS_BOTTLE = 374, + E_ITEM_SPIDER_EYE = 375, + E_ITEM_FERMENTED_SPIDER_EYE = 376, + E_ITEM_BLAZE_POWDER = 377, + E_ITEM_MAGMA_CREAM = 378, + E_ITEM_BREWING_STAND = 379, + E_ITEM_CAULDRON = 380, + E_ITEM_EYE_OF_ENDER = 381, + E_ITEM_GLISTERING_MELON = 382, + E_ITEM_SPAWN_EGG = 383, + E_ITEM_BOTTLE_O_ENCHANTING = 384, + E_ITEM_FIRE_CHARGE = 385, + E_ITEM_BOOK_AND_QUILL = 386, + E_ITEM_WRITTEN_BOOK = 387, + E_ITEM_EMERALD = 388, + E_ITEM_ITEM_FRAME = 389, + E_ITEM_FLOWER_POT = 390, + E_ITEM_CARROT = 391, + E_ITEM_POTATO = 392, + E_ITEM_BAKED_POTATO = 393, + E_ITEM_POISONOUS_POTATO = 394, + E_ITEM_EMPTY_MAP = 395, + E_ITEM_GOLDEN_CARROT = 396, + E_ITEM_HEAD = 397, + E_ITEM_CARROT_ON_STICK = 398, + E_ITEM_NETHER_STAR = 399, + E_ITEM_PUMPKIN_PIE = 400, + E_ITEM_FIREWORK_ROCKET = 401, + E_ITEM_FIREWORK_STAR = 402, + E_ITEM_ENCHANTED_BOOK = 403, + E_ITEM_COMPARATOR = 404, + E_ITEM_NETHER_BRICK = 405, + E_ITEM_NETHER_QUARTZ = 406, + E_ITEM_MINECART_WITH_TNT = 407, + E_ITEM_MINECART_WITH_HOPPER = 408, + E_ITEM_IRON_HORSE_ARMOR = 417, + E_ITEM_GOLD_HORSE_ARMOR = 418, + E_ITEM_DIAMOND_HORSE_ARMOR = 419, + E_ITEM_LEAD = 420, + E_ITEM_NAME_TAG = 421, + E_ITEM_MINECART_WITH_COMMAND_BLOCK = 422, + + // Keep these two as the last values of the consecutive list, without a number - they will get their correct number assigned automagically by C++ + // IsValidItem() depends on this! + E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES, ///< Number of individual (different) consecutive itemtypes + E_ITEM_MAX_CONSECUTIVE_TYPE_ID = E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES - 1, ///< Maximum consecutive ItemType number used + + E_ITEM_FIRST_DISC = 2256, + E_ITEM_13_DISC = 2256, + E_ITEM_CAT_DISC = 2257, + E_ITEM_BLOCKS_DISC = 2258, + E_ITEM_CHIRP_DISC = 2259, + E_ITEM_FAR_DISC = 2260, + E_ITEM_MALL_DISC = 2261, + E_ITEM_MELLOHI_DISC = 2262, + E_ITEM_STAL_DISC = 2263, + E_ITEM_STRAD_DISC = 2264, + E_ITEM_WARD_DISC = 2265, + E_ITEM_11_DISC = 2266, + E_ITEM_WAIT_DISC = 2267, + + // Keep these two as the last values of the disc list, without a number - they will get their correct number assigned automagically by C++ + // IsValidItem() depends on this! + E_ITEM_LAST_DISC_PLUS_ONE, ///< Useless, really, but needs to be present for the following value + E_ITEM_LAST_DISC = E_ITEM_LAST_DISC_PLUS_ONE - 1, ///< Maximum disc itemtype number used + + E_ITEM_LAST = E_ITEM_LAST_DISC, ///< Maximum valid ItemType +}; + + + + + +enum +{ + // Please keep this list alpha-sorted by the blocktype / itemtype part + // then number-sorted for the same block / item + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Block metas: + + // E_BLOCK_CHEST metas: + E_META_CHEST_FACING_ZM = 2, + E_META_CHEST_FACING_ZP = 3, + E_META_CHEST_FACING_XM = 4, + E_META_CHEST_FACING_XP = 5, + + // E_BLOCK_DISPENSER / E_BLOCK_DROPPER metas: + E_META_DROPSPENSER_FACING_YM = 0, + E_META_DROPSPENSER_FACING_YP = 1, + E_META_DROPSPENSER_FACING_ZM = 2, + E_META_DROPSPENSER_FACING_ZP = 3, + E_META_DROPSPENSER_FACING_XM = 4, + E_META_DROPSPENSER_FACING_XP = 5, + + // E_BLOCK_DOUBLE_STONE_SLAB metas: + E_META_DOUBLE_STONE_SLAB_STONE = 0, + E_META_DOUBLE_STONE_SLAB_SANDSTONE = 1, + E_META_DOUBLE_STONE_SLAB_WOODEN = 2, + E_META_DOUBLE_STONE_SLAB_COBBLESTONE = 3, + E_META_DOUBLE_STONE_SLAB_BRICK = 4, + E_META_DOUBLE_STONE_SLAB_STONE_BRICK = 5, + E_META_DOUBLE_STONE_SLAB_NETHER_BRICK = 6, + E_META_DOUBLE_STONE_SLAB_STONE_SECRET = 7, + + + + // E_BLOCK_HOPPER metas: + E_META_HOPPER_FACING_YM = 0, + E_META_HOPPER_UNATTACHED = 1, // Hopper doesn't move items up, there's no YP + E_META_HOPPER_FACING_ZM = 2, + E_META_HOPPER_FACING_ZP = 3, + E_META_HOPPER_FACING_XM = 4, + E_META_HOPPER_FACING_XP = 5, + + // E_BLOCK_LEAVES metas: + E_META_LEAVES_APPLE = 0, + E_META_LEAVES_CONIFER = 1, + E_META_LEAVES_BIRCH = 2, + E_META_LEAVES_JUNGLE = 3, + + // E_BLOCK_LOG metas: + E_META_LOG_APPLE = 0, + E_META_LOG_CONIFER = 1, + E_META_LOG_BIRCH = 2, + E_META_LOG_JUNGLE = 3, + + // E_BLOCK_PLANKS metas: + E_META_PLANKS_APPLE = 0, + E_META_PLANKS_CONIFER = 1, + E_META_PLANKS_BIRCH = 2, + E_META_PLANKS_JUNGLE = 3, + + // E_BLOCK_SANDSTONE metas: + E_META_SANDSTONE_NORMAL = 0, + E_META_SANDSTONE_ORNAMENT = 1, + E_META_SANDSTONE_SMOOTH = 2, + + // E_BLOCK_SAPLING metas (lowest 3 bits): + E_META_SAPLING_APPLE = 0, + E_META_SAPLING_CONIFER = 1, + E_META_SAPLING_BIRCH = 2, + E_META_SAPLING_JUNGLE = 3, + + // E_BLOCK_SILVERFISH_EGG metas: + E_META_SILVERFISH_EGG_STONE = 0, + E_META_SILVERFISH_EGG_COBBLESTONE = 1, + E_META_SILVERFISH_EGG_STONE_BRICK = 2, + + // E_BLOCK_STONE_SLAB metas: + E_META_STONE_SLAB_STONE = 0, + E_META_STONE_SLAB_SANDSTONE = 1, + E_META_STONE_SLAB_PLANKS = 2, + E_META_STONE_SLAB_COBBLESTONE = 3, + E_META_STONE_SLAB_BRICK = 4, + E_META_STONE_SLAB_STONE_BRICK = 5, + E_META_STONE_SLAB_NETHER_BRICK = 6, + E_META_STONE_SLAB_STONE_SECRET = 7, + + // E_BLOCK_STONE_BRICKS metas: + E_META_STONE_BRICK_NORMAL = 0, + E_META_STONE_BRICK_MOSSY = 1, + E_META_STONE_BRICK_CRACKED = 2, + E_META_STONE_BRICK_ORNAMENT = 3, + + // E_BLOCK_TALL_GRASS metas: + E_META_TALL_GRASS_DEAD_SHRUB = 0, + E_META_TALL_GRASS_GRASS = 1, + E_META_TALL_GRASS_FERN = 2, + + // E_BLOCK_TORCH, E_BLOCK_REDSTONE_TORCH_OFF, E_BLOCK_REDSTONE_TORCH_ON metas: + E_META_TORCH_EAST = 1, // east face of the block, pointing east + E_META_TORCH_WEST = 2, + E_META_TORCH_SOUTH = 3, + E_META_TORCH_NORTH = 4, + E_META_TORCH_FLOOR = 5, + E_META_TORCH_XM = 1, // Torch attached to the XM side of its block + E_META_TORCH_XP = 2, // Torch attached to the XP side of its block + E_META_TORCH_ZM = 3, // Torch attached to the ZM side of its block + E_META_TORCH_ZP = 4, // Torch attached to the ZP side of its block + + // E_BLOCK_WOODEN_DOUBLE_SLAB metas: + E_META_WOODEN_DOUBLE_SLAB_APPLE = 0, + E_META_WOODEN_DOUBLE_SLAB_CONIFER = 1, + E_META_WOODEN_DOUBLE_SLAB_BIRCH = 2, + E_META_WOODEN_DOUBLE_SLAB_JUNGLE = 3, + E_META_WOODEN_DOUBLE_SLAB_ACACIA = 4, + E_META_WOODEN_DOUBLE_SLAB_DARK_OAK = 5, + + // E_BLOCK_WOODEN_SLAB metas: + E_META_WOODEN_SLAB_APPLE = 0, + E_META_WOODEN_SLAB_CONIFER = 1, + E_META_WOODEN_SLAB_BIRCH = 2, + E_META_WOODEN_SLAB_JUNGLE = 3, + E_META_WOODEN_SLAB_ACACIA = 4, + E_META_WOODEN_SLAB_DARK_OAK = 5, + + // E_BLOCK_WOOL metas: + E_META_WOOL_WHITE = 0, + E_META_WOOL_ORANGE = 1, + E_META_WOOL_MAGENTA = 2, + E_META_WOOL_LIGHTBLUE = 3, + E_META_WOOL_YELLOW = 4, + E_META_WOOL_LIGHTGREEN = 5, + E_META_WOOL_PINK = 6, + E_META_WOOL_GRAY = 7, + E_META_WOOL_LIGHTGRAY = 8, + E_META_WOOL_CYAN = 9, + E_META_WOOL_PURPLE = 10, + E_META_WOOL_BLUE = 11, + E_META_WOOL_BROWN = 12, + E_META_WOOL_GREEN = 13, + E_META_WOOL_RED = 14, + E_META_WOOL_BLACK = 15, + + // E_BLOCK_CARPET metas: + E_META_CARPET_WHITE = 0, + E_META_CARPET_ORANGE = 1, + E_META_CARPET_MAGENTA = 2, + E_META_CARPET_LIGHTBLUE = 3, + E_META_CARPET_YELLOW = 4, + E_META_CARPET_LIGHTGREEN = 5, + E_META_CARPET_PINK = 6, + E_META_CARPET_GRAY = 7, + E_META_CARPET_LIGHTGRAY = 8, + E_META_CARPET_CYAN = 9, + E_META_CARPET_PURPLE = 10, + E_META_CARPET_BLUE = 11, + E_META_CARPET_BROWN = 12, + E_META_CARPET_GREEN = 13, + E_META_CARPET_RED = 14, + E_META_CARPET_BLACK = 15, + + // E_BLOCK_STAINED_CLAY metas + E_META_STAINED_CLAY_WHITE = 0, + E_META_STAINED_CLAY_ORANGE = 1, + E_META_STAINED_CLAY_MAGENTA = 2, + E_META_STAINED_CLAY_LIGHTBLUE = 3, + E_META_STAINED_CLAY_YELLOW = 4, + E_META_STAINED_CLAY_LIGHTGREEN = 5, + E_META_STAINED_CLAY_PINK = 6, + E_META_STAINED_CLAY_GRAY = 7, + E_META_STAINED_CLAY_LIGHTGRAY = 8, + E_META_STAINED_CLAY_CYAN = 9, + E_META_STAINED_CLAY_PURPLE = 10, + E_META_STAINED_CLAY_BLUE = 11, + E_META_STAINED_CLAY_BROWN = 12, + E_META_STAINED_CLAY_GREEN = 13, + E_META_STAINED_CLAY_RED = 14, + E_META_STAINED_CLAY_BLACK = 15, + + // E_BLOCK_STAINED_GLASS metas + E_META_STAINED_GLASS_WHITE = 0, + E_META_STAINED_GLASS_ORANGE = 1, + E_META_STAINED_GLASS_MAGENTA = 2, + E_META_STAINED_GLASS_LIGHTBLUE = 3, + E_META_STAINED_GLASS_YELLOW = 4, + E_META_STAINED_GLASS_LIGHTGREEN = 5, + E_META_STAINED_GLASS_PINK = 6, + E_META_STAINED_GLASS_GRAY = 7, + E_META_STAINED_GLASS_LIGHTGRAY = 8, + E_META_STAINED_GLASS_CYAN = 9, + E_META_STAINED_GLASS_PURPLE = 10, + E_META_STAINED_GLASS_BLUE = 11, + E_META_STAINED_GLASS_BROWN = 12, + E_META_STAINED_GLASS_GREEN = 13, + E_META_STAINED_GLASS_RED = 14, + E_META_STAINED_GLASS_BLACK = 15, + + // E_BLOCK_STAINED_GLASS_PANE metas + E_META_STAINED_GLASS_PANE_WHITE = 0, + E_META_STAINED_GLASS_PANE_ORANGE = 1, + E_META_STAINED_GLASS_PANE_MAGENTA = 2, + E_META_STAINED_GLASS_PANE_LIGHTBLUE = 3, + E_META_STAINED_GLASS_PANE_YELLOW = 4, + E_META_STAINED_GLASS_PANE_LIGHTGREEN = 5, + E_META_STAINED_GLASS_PANE_PINK = 6, + E_META_STAINED_GLASS_PANE_GRAY = 7, + E_META_STAINED_GLASS_PANE_LIGHTGRAY = 8, + E_META_STAINED_GLASS_PANE_CYAN = 9, + E_META_STAINED_GLASS_PANE_PURPLE = 10, + E_META_STAINED_GLASS_PANE_BLUE = 11, + E_META_STAINED_GLASS_PANE_BROWN = 12, + E_META_STAINED_GLASS_PANE_GREEN = 13, + E_META_STAINED_GLASS_PANE_RED = 14, + E_META_STAINED_GLASS_PANE_BLACK = 15, + + // E_BLOCK_SNOW metas: + E_META_SNOW_LAYER_ONE = 0, + E_META_SNOW_LAYER_TWO = 1, + E_META_SNOW_LAYER_THREE = 2, + E_META_SNOW_LAYER_FOUR = 3, + E_META_SNOW_LAYER_FIVE = 4, + E_META_SNOW_LAYER_SIX = 5, + E_META_SNOW_LAYER_SEVEN = 6, + E_META_SNOW_LAYER_EIGHT = 7, + + // E_BLOCK_RAIL metas + E_META_RAIL_ZM_ZP = 0, + E_META_RAIL_XM_XP = 1, + E_META_RAIL_ASCEND_XP = 2, + E_META_RAIL_ASCEND_XM = 3, + E_META_RAIL_ASCEND_ZM = 4, + E_META_RAIL_ASCEND_ZP = 5, + E_META_RAIL_CURVED_ZP_XP = 6, + E_META_RAIL_CURVED_ZP_XM = 7, + E_META_RAIL_CURVED_ZM_XM = 8, + E_META_RAIL_CURVED_ZM_XP = 9, + + //E_BLOCK_NEW_LEAVES metas + E_META_NEW_LEAVES_ACACIA_WOOD = 0, + E_META_NEW_LEAVES_DARK_OAK_WOOD = 1, + + //E_BLOCK_NEW_LOG metas + E_META_NEW_LOG_ACACIA_WOOD = 0, + E_META_NEW_LOG_DARK_OAK_WOOD = 1, + + //E_BLOCK_FLOWER metas + E_META_FLOWER_POPPY = 0, + E_META_FLOWER_BLUE_ORCHID = 1, + E_META_FLOWER_ALLIUM = 2, + E_META_FLOWER_RED_TULIP = 4, + E_META_FLOWER_ORANGE_TULIP = 5, + E_META_FLOWER_WHITE_TULIP = 6, + E_META_FLOWER_PINK_TULIP = 7, + E_META_FLOWER_OXEYE_DAISY = 8, + + //E_BLOCK_BIG_FLOWER metas + E_META_BIG_FLOWER_SUNFLOWER = 0, + E_META_BIG_FLOWER_LILAC = 1, + E_META_BIG_FLOWER_DOUBLE_TALL_GRASS = 2, + E_META_BIG_FLOWER_LARGE_FERN = 3, + E_META_BIG_FLOWER_ROSE_BUSH = 4, + E_META_BIG_FLOWER_PEONY = 5, + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Item metas: + + // E_ITEM_COAL metas: + E_META_COAL_NORMAL = 0, + E_META_COAL_CHARCOAL = 1, + + // E_ITEM_DYE metas: + E_META_DYE_BLACK = 0, + E_META_DYE_RED = 1, + E_META_DYE_GREEN = 2, + E_META_DYE_BROWN = 3, + E_META_DYE_BLUE = 4, + E_META_DYE_PURPLE = 5, + E_META_DYE_CYAN = 6, + E_META_DYE_LIGHTGRAY = 7, + E_META_DYE_GRAY = 8, + E_META_DYE_PINK = 9, + E_META_DYE_LIGHTGREEN = 10, + E_META_DYE_YELLOW = 11, + E_META_DYE_LIGHTBLUE = 12, + E_META_DYE_MAGENTA = 13, + E_META_DYE_ORANGE = 14, + E_META_DYE_WHITE = 15, + + // E_ITEM_GOLDEN_APPLE metas: + E_META_GOLDEN_APPLE_NORMAL = 0, + E_META_GOLDEN_APPLE_ENCHANTED = 1, + + // E_ITEM_RAW_FISH metas: + E_META_RAW_FISH_FISH = 0, + E_META_RAW_FISH_SALMON = 1, + E_META_RAW_FISH_CLOWNFISH = 2, + E_META_RAW_FISH_PUFFERFISH = 3, + + // E_ITEM_COOKED_FISH metas: + E_META_COOKED_FISH_FISH = 0, + E_META_COOKED_FISH_SALMON = 1, + E_META_COOKED_FISH_CLOWNFISH = 2, + E_META_COOKED_FISH_PUFFERFISH = 3, + + // E_ITEM_MINECART_TRACKS metas: + E_META_TRACKS_X = 1, + E_META_TRACKS_Z = 0, + + // E_ITEM_SPAWN_EGG metas: + // See also cMonster::eType, since monster type and spawn egg meta are the same + E_META_SPAWN_EGG_PICKUP = 1, + E_META_SPAWN_EGG_EXPERIENCE_ORB = 2, + E_META_SPAWN_EGG_LEASH_KNOT = 8, + E_META_SPAWN_EGG_PAINTING = 9, + E_META_SPAWN_EGG_ARROW = 10, + E_META_SPAWN_EGG_SNOWBALL = 11, + E_META_SPAWN_EGG_FIREBALL = 12, + E_META_SPAWN_EGG_SMALL_FIREBALL = 13, + E_META_SPAWN_EGG_ENDER_PEARL = 14, + E_META_SPAWN_EGG_EYE_OF_ENDER = 15, + E_META_SPAWN_EGG_SPLASH_POTION = 16, + E_META_SPAWN_EGG_EXP_BOTTLE = 17, + E_META_SPAWN_EGG_ITEM_FRAME = 18, + E_META_SPAWN_EGG_WITHER_SKULL = 19, + E_META_SPAWN_EGG_PRIMED_TNT = 20, + E_META_SPAWN_EGG_FALLING_BLOCK = 21, + E_META_SPAWN_EGG_FIREWORK = 22, + E_META_SPAWN_EGG_BOAT = 41, + E_META_SPAWN_EGG_MINECART = 42, + E_META_SPAWN_EGG_MINECART_CHEST = 43, + E_META_SPAWN_EGG_MINECART_FURNACE = 44, + E_META_SPAWN_EGG_MINECART_TNT = 45, + E_META_SPAWN_EGG_MINECART_HOPPER = 46, + E_META_SPAWN_EGG_MINECART_SPAWNER = 47, + E_META_SPAWN_EGG_CREEPER = 50, + E_META_SPAWN_EGG_SKELETON = 51, + E_META_SPAWN_EGG_SPIDER = 52, + E_META_SPAWN_EGG_GIANT = 53, + E_META_SPAWN_EGG_ZOMBIE = 54, + E_META_SPAWN_EGG_SLIME = 55, + E_META_SPAWN_EGG_GHAST = 56, + E_META_SPAWN_EGG_ZOMBIE_PIGMAN = 57, + E_META_SPAWN_EGG_ENDERMAN = 58, + E_META_SPAWN_EGG_CAVE_SPIDER = 59, + E_META_SPAWN_EGG_SILVERFISH = 60, + E_META_SPAWN_EGG_BLAZE = 61, + E_META_SPAWN_EGG_MAGMA_CUBE = 62, + E_META_SPAWN_EGG_ENDER_DRAGON = 63, + E_META_SPAWN_EGG_WITHER = 64, + E_META_SPAWN_EGG_BAT = 65, + E_META_SPAWN_EGG_WITCH = 66, + E_META_SPAWN_EGG_PIG = 90, + E_META_SPAWN_EGG_SHEEP = 91, + E_META_SPAWN_EGG_COW = 92, + E_META_SPAWN_EGG_CHICKEN = 93, + E_META_SPAWN_EGG_SQUID = 94, + E_META_SPAWN_EGG_WOLF = 95, + E_META_SPAWN_EGG_MOOSHROOM = 96, + E_META_SPAWN_EGG_SNOW_GOLEM = 97, + E_META_SPAWN_EGG_OCELOT = 98, + E_META_SPAWN_EGG_IRON_GOLEM = 99, + E_META_SPAWN_EGG_HORSE = 100, + E_META_SPAWN_EGG_VILLAGER = 120, + E_META_SPAWN_EGG_ENDER_CRYSTAL = 200, +} ; + + + + + +/// Dimension of a world +enum eDimension +{ + dimNether = -1, + dimOverworld = 0, + dimEnd = 1, +} ; + + + + + +/// Damage type, used in the TakeDamageInfo structure and related functions +enum eDamageType +{ + // Canonical names for the types (as documented in the plugin wiki): + dtAttack, // Being attacked by a mob + dtRangedAttack, // Being attacked by a projectile, possibly from a mob + dtLightning, // Hit by a lightning strike + dtFalling, // Falling down; dealt when hitting the ground + dtDrowning, // Drowning in water / lava + dtSuffocating, // Suffocating inside a block + dtStarving, // Hunger + dtCactusContact, // Contact with a cactus block + dtLavaContact, // Contact with a lava block + dtPoisoning, // Having the poison effect + dtOnFire, // Being on fire + dtFireContact, // Standing inside a fire block + dtInVoid, // Falling into the Void (Y < 0) + dtPotionOfHarming, + dtEnderPearl, // Thrown an ender pearl, teleported by it + dtAdmin, // Damage applied by an admin command + + // Some common synonyms: + dtPawnAttack = dtAttack, + dtEntityAttack = dtAttack, + dtMob = dtAttack, + dtMobAttack = dtAttack, + dtArrowAttack = dtRangedAttack, + dtArrow = dtRangedAttack, + dtProjectile = dtRangedAttack, + dtFall = dtFalling, + dtDrown = dtDrowning, + dtSuffocation = dtSuffocating, + dtStarvation = dtStarving, + dtHunger = dtStarving, + dtCactus = dtCactusContact, + dtCactuses = dtCactusContact, + dtCacti = dtCactusContact, + dtLava = dtLavaContact, + dtPoison = dtPoisoning, + dtBurning = dtOnFire, + dtInFire = dtFireContact, + dtPlugin = dtAdmin, +} ; + + + + + +enum eExplosionSource +{ + esOther, + esPrimedTNT, + esCreeper, + esBed, + esEnderCrystal, + esGhastFireball, + esWitherSkullBlack, + esWitherSkullBlue, + esWitherBirth, + esPlugin +} ; + +// tolua_end + + + + +// fwd: +class cItem; +class cIniFile; + + + + + +// tolua_begin + +/// Translates a blocktype string into blocktype. Takes either a number or an items.ini alias as input. Returns -1 on failure. +extern BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString); + +/// Translates an itemtype string into an item. Takes either a number, number^number, number:number or an items.ini alias as input. Returns true if successful. +extern bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item); + +/// Translates a full item into a string. If the ItemType is not recognized, the ItemType number is output into the string. +extern AString ItemToString(const cItem & a_Item); + +/// Translates itemtype into a string. If the type is not recognized, the itemtype number is output into the string. +extern AString ItemTypeToString(short a_ItemType); + +/// Translates a full item into a fully-specified string (including meta and count). If the ItemType is not recognized, the ItemType number is output into the string. +extern AString ItemToFullString(const cItem & a_Item); + +/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure. +extern EMCSBiome StringToBiome(const AString & a_BiomeString); + +/// Translates a mob string ("ocelot") to mobtype (E_ENTITY_TYPE_OCELOT) +extern int StringToMobType(const AString & a_MobString); + +/// Translates a dimension string to dimension enum. Takes either a number or a dimension alias (built-in). Returns -1000 on failure +extern eDimension StringToDimension(const AString & a_DimensionString); + +/// Translates damage type constant to a string representation (built-in). +extern AString DamageTypeToString(eDamageType a_DamageType); + +/// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure +extern eDamageType StringToDamageType(const AString & a_DamageString); + +/// Returns a cItem representing the item described in an IniFile's value; if the value doesn't exist, creates it with the provided default. +extern cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default); + +// tolua_end + + + + + +// Block properties: +extern NIBBLETYPE g_BlockLightValue[256]; +extern NIBBLETYPE g_BlockSpreadLightFalloff[256]; +extern bool g_BlockTransparent[256]; +extern bool g_BlockOneHitDig[256]; +extern bool g_BlockPistonBreakable[256]; +extern bool g_BlockIsSnowable[256]; +extern bool g_BlockRequiresSpecialTool[256]; +extern bool g_BlockIsSolid[256]; +extern bool g_BlockIsTorchPlaceable[256]; + + + + diff --git a/src/BlockTracer.h b/src/BlockTracer.h new file mode 100644 index 000000000..d0a34811d --- /dev/null +++ b/src/BlockTracer.h @@ -0,0 +1,104 @@ + +// BlockTracer.h + +// Declares the classes common for all blocktracers + + + + + +#pragma once + + + + + +// fwd: World.h +class cWorld; + + + + + +class cBlockTracer abstract +{ +public: + /** The callback class is used to notify the caller of individual events that are being traced. + */ + class cCallbacks abstract + { + public: + /** Called on each block encountered along the path, including the first block (path start) + When this callback returns true, the tracing is aborted. + */ + virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) = 0; + + /** Called on each block encountered along the path, including the first block (path start), if chunk data is not loaded + When this callback returns true, the tracing is aborted. + */ + virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) { return false; } + + /** Called when the path goes out of world, either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height) + The coords specify the exact point at which the path exited the world. + If this callback returns true, the tracing is aborted. + Note that some paths can go out of the world and come back again (parabola), + in such a case this callback is followed by OnIntoWorld() and further OnNextBlock() calls + */ + virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; } + + /** Called when the path goes into the world, from either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height) + The coords specify the exact point at which the path entered the world. + If this callback returns true, the tracing is aborted. + Note that some paths can go out of the world and come back again (parabola), + in such a case this callback is followed by further OnNextBlock() calls + */ + virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; } + + /** Called when the path is sure not to hit any more blocks. + Note that for some shapes this might never happen (line with constant Y) + */ + virtual void OnNoMoreHits(void) {} + + /** Called when the block tracing walks into a chunk that is not allocated. + This usually means that the tracing is aborted. + */ + virtual void OnNoChunk(void) {} + } ; + + + /// Creates the BlockTracer parent with the specified callbacks + cBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : + m_World(&a_World), + m_Callbacks(&a_Callbacks) + { + } + + + /// Sets new world, returns the old one. Note that both need to be valid + cWorld & SetWorld(cWorld & a_World) + { + cWorld & Old = *m_World; + m_World = &a_World; + return Old; + } + + + /// Sets new callbacks, returns the old ones. Note that both need to be valid + cCallbacks & SetCallbacks(cCallbacks & a_NewCallbacks) + { + cCallbacks & Old = *m_Callbacks; + m_Callbacks = &a_NewCallbacks; + return Old; + } + +protected: + /// The world upon which to operate + cWorld * m_World; + + /// The callback to use for reporting + cCallbacks * m_Callbacks; +} ; + + + + diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp new file mode 100644 index 000000000..66eb9130c --- /dev/null +++ b/src/Blocks/BlockBed.cpp @@ -0,0 +1,88 @@ +#include "Globals.h" +#include "BlockBed.h" + + + + + +void cBlockBedHandler::OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta +) +{ + if (a_BlockMeta < 8) + { + Vector3i Direction = MetaDataToDirection(a_BlockMeta); + a_World->SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8); + } +} + + + + + +void cBlockBedHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ ); + Vector3i Direction = MetaDataToDirection( OldMeta & 0x7 ); + if (OldMeta & 0x8) + { + // Was pillow + if (a_World->GetBlock(ThisPos - Direction) == E_BLOCK_BED) + { + a_World->FastSetBlock(ThisPos - Direction, E_BLOCK_AIR, 0); + } + } + else + { + // Was foot end + if (a_World->GetBlock(ThisPos + Direction) == E_BLOCK_BED) + { + a_World->FastSetBlock(ThisPos + Direction, E_BLOCK_AIR, 0); + } + } +} + + + + + +void cBlockBedHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + if (a_World->GetDimension() != dimOverworld) + { + Vector3i Coords(a_BlockX, a_BlockY, a_BlockZ); + a_World->DoExplosionAt(5, a_BlockX, a_BlockY, a_BlockZ, true, esBed, &Coords); + } + else + { + if (a_World->GetTimeOfDay() > 13000) + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (Meta & 0x8) + { + // Is pillow + a_World->BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + else + { + // Is foot end + Vector3i Direction = MetaDataToDirection( Meta & 0x7 ); + if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping + { + a_World->BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z); + } + } + } else { + a_Player->SendMessage("You can only sleep at night"); + } + } +} + + + + diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h new file mode 100644 index 000000000..8a289b22c --- /dev/null +++ b/src/Blocks/BlockBed.h @@ -0,0 +1,67 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cBlockBedHandler : + public cBlockHandler +{ +public: + cBlockBedHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to zero + a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0)); + } + + + // Bed specific helper functions + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 180 + (180 / 4); // So its not aligned with axis + if (a_Rotation > 360) a_Rotation -= 360; + + a_Rotation = (a_Rotation / 360) * 4; + + return ((char)a_Rotation + 2) % 4; + } + + + static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData) + { + switch (a_MetaData) + { + case 0: return Vector3i(0, 0, 1); + case 1: return Vector3i(-1, 0, 0); + case 2: return Vector3i(0, 0, -1); + case 3: return Vector3i(1, 0, 0); + } + return Vector3i(); + } +} ; + + + + diff --git a/src/Blocks/BlockBrewingStand.h b/src/Blocks/BlockBrewingStand.h new file mode 100644 index 000000000..57642bcb6 --- /dev/null +++ b/src/Blocks/BlockBrewingStand.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockBrewingStandHandler : + public cBlockHandler +{ +public: + cBlockBrewingStandHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_BREWING_STAND, 1, 0)); + } + + virtual bool IsUseable() override + { + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockButton.cpp b/src/Blocks/BlockButton.cpp new file mode 100644 index 000000000..1011f9351 --- /dev/null +++ b/src/Blocks/BlockButton.cpp @@ -0,0 +1,39 @@ + +#include "Globals.h" +#include "BlockButton.h" + + + + + +cBlockButtonHandler::cBlockButtonHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockButtonHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off. + NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); + + if (Meta & 0x08) + { + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + } + else + { + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.5f); + } + + // Queue a button reset (unpress), with a GetBlock to prevent duplication of buttons (press, break, wait for reset) + a_World->QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f), m_BlockType == E_BLOCK_STONE_BUTTON ? 20 : 25); +} + + + + diff --git a/src/Blocks/BlockButton.h b/src/Blocks/BlockButton.h new file mode 100644 index 000000000..e3f655bfa --- /dev/null +++ b/src/Blocks/BlockButton.h @@ -0,0 +1,69 @@ +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockButtonHandler : + public cBlockHandler +{ +public: + cBlockButtonHandler(BLOCKTYPE a_BlockType); + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType == E_BLOCK_WOODEN_BUTTON ? E_BLOCK_WOODEN_BUTTON : E_BLOCK_STONE_BUTTON, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = BlockFaceToMetaData(a_BlockFace); + return true; + } + + + virtual const char * GetStepSound(void) override + { + return m_BlockType == E_BLOCK_WOODEN_BUTTON ? "step.wood" : "step.stone"; + } + + + inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace) + { + switch (a_BlockFace) + { + case BLOCK_FACE_ZP: { return 0x4; } + case BLOCK_FACE_ZM: { return 0x3; } + case BLOCK_FACE_XP: { return 0x2; } + case BLOCK_FACE_XM: { return 0x1; } + default: + { + ASSERT(!"Unhandled block face!"); + return 0x0; // No idea, give a special meta (button in centre of block) + } + } + } +} ; + + + + diff --git a/src/Blocks/BlockCactus.h b/src/Blocks/BlockCactus.h new file mode 100644 index 000000000..4147ad473 --- /dev/null +++ b/src/Blocks/BlockCactus.h @@ -0,0 +1,82 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockCactusHandler : + public cBlockHandler +{ +public: + cBlockCactusHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); + if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS)) + { + // Cactus can only be placed on sand and itself + return false; + } + + // Check surroundings. Cacti may ONLY be surrounded by air + static const struct + { + int x, z; + } Coords[] = + { + {-1, 0}, + { 1, 0}, + { 0, -1}, + { 0, 1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if ( + a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && + (BlockType != E_BLOCK_AIR) + ) + { + return false; + } + } // for i - Coords[] + + return true; + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + a_World->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1); + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } +} ; + + + + diff --git a/src/Blocks/BlockCarpet.h b/src/Blocks/BlockCarpet.h new file mode 100644 index 000000000..5eafd8c21 --- /dev/null +++ b/src/Blocks/BlockCarpet.h @@ -0,0 +1,60 @@ + +// BlockCarpet.h + +// Declares the cBlockCarpetHandler class representing the handler for the carpet block + + + + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockCarpetHandler : + public cBlockHandler +{ +public: + cBlockCarpetHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = a_Player->GetEquippedItem().m_ItemDamage & 0x0f; + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_CARPET, 1, a_BlockMeta)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR); + } +} ; + + + + diff --git a/src/Blocks/BlockCauldron.h b/src/Blocks/BlockCauldron.h new file mode 100644 index 000000000..b0e00f869 --- /dev/null +++ b/src/Blocks/BlockCauldron.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockCauldronHandler : + public cBlockHandler +{ +public: + cBlockCauldronHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_CAULDRON, 1, 0)); + } + + void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + char Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); + switch( a_Player->GetEquippedItem().m_ItemType ) + { + case E_ITEM_WATER_BUCKET: + { + a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 ); + a_Player->GetInventory().RemoveOneEquippedItem(); + cItem NewItem(E_ITEM_BUCKET, 1); + a_Player->GetInventory().AddItem(NewItem); + break; + } + case E_ITEM_GLASS_BOTTLE: + { + if( Meta > 0 ) + { + a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta); + a_Player->GetInventory().RemoveOneEquippedItem(); + cItem NewItem(E_ITEM_POTIONS, 1, 0); + a_Player->GetInventory().AddItem(NewItem); + } + break; + } + } + } + + virtual bool IsUseable() override + { + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h new file mode 100644 index 000000000..488c58ac5 --- /dev/null +++ b/src/Blocks/BlockChest.h @@ -0,0 +1,223 @@ + +#pragma once + +#include "BlockEntity.h" +#include "../World.h" +#include "../BlockArea.h" +#include "../Entities/Player.h" + + + + + +class cBlockChestHandler : + public cBlockEntityHandler +{ +public: + cBlockChestHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // Is there a doublechest already next to this block? + if (!CanBeAt(a_World, a_BlockX, a_BlockY, a_BlockZ)) + { + // Yup, cannot form a triple-chest, refuse: + return false; + } + + // Check if this forms a doublechest, if so, need to adjust the meta: + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) + { + return false; + } + double rot = a_Player->GetRotation(); + if ( + (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + ) + { + a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; + return true; + } + if ( + (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + ) + { + a_BlockMeta = (rot < 0) ? 4 : 5; + return true; + } + + // Single chest, get meta from rotation only + a_BlockMeta = RotationToMetaData(rot); + return true; + } + + + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ) override + { + // Check if this forms a doublechest, if so, need to adjust the meta: + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) + { + return; + } + + double rot = a_Player->GetRotation(); + // Choose meta from player rotation, choose only between 2 or 3 + NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; + if ( + CheckAndAdjustNeighbor(a_World, Area, 0, 1, NewMeta) || + CheckAndAdjustNeighbor(a_World, Area, 2, 1, NewMeta) + ) + { + // Forming a double chest in the X direction + return; + } + // Choose meta from player rotation, choose only between 4 or 5 + NewMeta = (rot < 0) ? 4 : 5; + if ( + CheckAndAdjustNeighbor(a_World, Area, 1, 0, NewMeta) || + CheckAndAdjustNeighbor(a_World, Area, 2, 2, NewMeta) + ) + { + // Forming a double chest in the Z direction + return; + } + + // Single chest, no further processing needed + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + virtual bool CanBeAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 2, a_BlockX + 2, a_BlockY, a_BlockY, a_BlockZ - 2, a_BlockZ + 2)) + { + // Cannot read the surroundings, probably at the edge of loaded chunks. Disallow. + return false; + } + + int NumChestNeighbors = 0; + if (Area.GetRelBlockType(1, 0, 2) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(0, 0, 2) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + if (Area.GetRelBlockType(3, 0, 2) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(4, 0, 2) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + if (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(2, 0, 0) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + if (Area.GetRelBlockType(2, 0, 3) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(2, 0, 4) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + return (NumChestNeighbors < 2); + } + + + /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 90 + 45; // So its not aligned with axis + + if (a_Rotation > 360.f) + { + a_Rotation -= 360.f; + } + if ((a_Rotation >= 0.f) && (a_Rotation < 90.f)) + { + return 0x4; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x5; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x3; + } + } + + + /// If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true. + bool CheckAndAdjustNeighbor(cWorld * a_World, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta) + { + if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != E_BLOCK_CHEST) + { + return false; + } + a_World->SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta); + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockCloth.h b/src/Blocks/BlockCloth.h new file mode 100644 index 000000000..a136d3b9d --- /dev/null +++ b/src/Blocks/BlockCloth.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockClothHandler : + public cBlockHandler +{ +public: + cBlockClothHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_WOOL, 1, a_BlockMeta)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } +} ; + + + + diff --git a/src/Blocks/BlockCobWeb.h b/src/Blocks/BlockCobWeb.h new file mode 100644 index 000000000..982bfaa30 --- /dev/null +++ b/src/Blocks/BlockCobWeb.h @@ -0,0 +1,30 @@ + +// BlockCobWeb.h + +// Declares the cBlockCobWebHandler object representing the BlockHandler for cobwebs + +#pragma once + + + + + +class cBlockCobWebHandler : + public cBlockHandler +{ +public: + cBlockCobWebHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override + { + a_Pickups.push_back(cItem(E_ITEM_STRING, 1, 0)); + } +} ; + + + + diff --git a/src/Blocks/BlockComparator.cpp b/src/Blocks/BlockComparator.cpp new file mode 100644 index 000000000..b4e5a55d0 --- /dev/null +++ b/src/Blocks/BlockComparator.cpp @@ -0,0 +1,53 @@ + +#include "Globals.h" +#include "BlockComparator.h" +#include "../Simulator/RedstoneSimulator.h" +#include "../Entities/Player.h" + + + + + +cBlockComparatorHandler::cBlockComparatorHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockComparatorHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Nothing needed yet +} + + + + + +void cBlockComparatorHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + Meta ^= 0x04; // Toggle 3rd (addition/subtraction) bit with XOR + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); +} + + + + +bool cBlockComparatorHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + a_BlockType = m_BlockType; + a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; +} + + + + diff --git a/src/Blocks/BlockComparator.h b/src/Blocks/BlockComparator.h new file mode 100644 index 000000000..cb2941d3c --- /dev/null +++ b/src/Blocks/BlockComparator.h @@ -0,0 +1,55 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockComparatorHandler : + public cBlockHandler +{ +public: + cBlockComparatorHandler(BLOCKTYPE a_BlockType); + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_ITEM_COMPARATOR, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override; + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockCrops.h b/src/Blocks/BlockCrops.h new file mode 100644 index 000000000..9dd65aae2 --- /dev/null +++ b/src/Blocks/BlockCrops.h @@ -0,0 +1,114 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +/// Common class that takes care of carrots, potatoes and wheat +class cBlockCropsHandler : + public cBlockHandler +{ +public: + cBlockCropsHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override + { + MTRand rand; + + if (a_Meta == 0x7) + { + // Is fully grown, drop the entire produce: + switch (m_BlockType) + { + case E_BLOCK_CROPS: + { + a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0)); + a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 + break; + } + case E_BLOCK_CARROTS: + { + a_Pickups.push_back(cItem(E_ITEM_CARROT, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 + break; + } + case E_BLOCK_POTATOES: + { + a_Pickups.push_back(cItem(E_ITEM_POTATO, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 + if (rand.randInt(20) == 0) + { + // With a 5% chance, drop a poisonous potato as well + a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0)); + } + break; + } + default: + { + ASSERT(!"Unhandled block type"); + break; + } + } // switch (m_BlockType) + } + else + { + // Drop 1 item of whatever is growing + switch (m_BlockType) + { + case E_BLOCK_CROPS: a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); break; + case E_BLOCK_CARROTS: a_Pickups.push_back(cItem(E_ITEM_CARROT, 1, 0)); break; + case E_BLOCK_POTATOES: a_Pickups.push_back(cItem(E_ITEM_POTATO, 1, 0)); break; + default: + { + ASSERT(!"Unhandled block type"); + break; + } + } + } + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + NIBBLETYPE Light = a_World->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); + NIBBLETYPE SkyLight = a_World->GetBlockSkyLight(a_BlockX, a_BlockY, a_BlockZ); + + if (SkyLight > Light) + { + Light = SkyLight; + } + + if ((Meta < 7) && (Light > 8)) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta); + } + else if (Light < 9) + { + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockDeadBush.h b/src/Blocks/BlockDeadBush.h new file mode 100644 index 000000000..14617d006 --- /dev/null +++ b/src/Blocks/BlockDeadBush.h @@ -0,0 +1,35 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockDeadBushHandler : + public cBlockHandler +{ +public: + cBlockDeadBushHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Don't drop anything + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_SAND); + } +} ; + + + + diff --git a/src/Blocks/BlockDirt.h b/src/Blocks/BlockDirt.h new file mode 100644 index 000000000..c694d79f6 --- /dev/null +++ b/src/Blocks/BlockDirt.h @@ -0,0 +1,88 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +/// Handler used for both dirt and grass +class cBlockDirtHandler : + public cBlockHandler +{ +public: + cBlockDirtHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); + } + + + virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + if (m_BlockType != E_BLOCK_GRASS) + { + return; + } + + // Grass becomes dirt if there is something on top of it: + if (a_BlockY < cChunkDef::Height - 1) + { + BLOCKTYPE Above = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); + if ((!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above]) || IsBlockWater(Above)) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); + return; + } + } + + // Grass spreads to adjacent blocks: + MTRand rand; + for (int i = 0; i < 2; i++) // Pick two blocks to grow to + { + int OfsX = rand.randInt(2) - 1; // [-1 .. 1] + int OfsY = rand.randInt(4) - 3; // [-3 .. 1] + int OfsZ = rand.randInt(2) - 1; // [-1 .. 1] + + BLOCKTYPE DestBlock; + NIBBLETYPE DestMeta; + if ((a_BlockY + OfsY < 0) || (a_BlockY + OfsY >= cChunkDef::Height - 1)) + { + // Y Coord out of range + continue; + } + bool IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, DestBlock, DestMeta); + if (!IsValid || (DestBlock != E_BLOCK_DIRT)) + { + continue; + } + + BLOCKTYPE AboveDest; + NIBBLETYPE AboveMeta; + IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, AboveDest, AboveMeta); + ASSERT(IsValid); // WTF - how did we get the DestBlock if AboveBlock is not valid? + if ((g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest]) && !IsBlockWater(AboveDest)) + { + a_World->FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, E_BLOCK_GRASS, 0); + } + } // for i - repeat twice + } + + + virtual const char * GetStepSound(void) override + { + return "step.gravel"; + } +} ; + + + + diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp new file mode 100644 index 000000000..e71ccd368 --- /dev/null +++ b/src/Blocks/BlockDoor.cpp @@ -0,0 +1,90 @@ + +#include "Globals.h" +#include "BlockDoor.h" +#include "../Item.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if (OldMeta & 8) + { + // Was upper part of door + if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + { + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + } + } + else + { + // Was lower part + if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) + { + a_World->FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0); + } + } +} + + + + + +void cBlockDoorHandler::OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) + { + ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +void cBlockDoorHandler::OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta +) +{ + NIBBLETYPE a_TopBlockMeta = 8; + if ( + (a_BlockMeta == 0) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType) || + (a_BlockMeta == 1) && (a_World->GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType) || + (a_BlockMeta == 2) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType) || + (a_BlockMeta == 3) && (a_World->GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType) + ) + { + a_TopBlockMeta = 9; + } + a_World->SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta); +} + + + + + +const char * cBlockDoorHandler::GetStepSound(void) +{ + return (m_BlockType == E_BLOCK_WOODEN_DOOR) ? "step.wood" : "step.stone"; +} + + + + diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h new file mode 100644 index 000000000..03a79d47d --- /dev/null +++ b/src/Blocks/BlockDoor.h @@ -0,0 +1,175 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cBlockDoorHandler : + public cBlockHandler +{ +public: + cBlockDoorHandler(BLOCKTYPE a_BlockType); + + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual const char * GetStepSound(void) override; + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // If clicking a bottom face, place the door one block lower: + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + a_BlockY--; + } + + if ( + !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) || + !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) + ) + { + return false; + } + + a_BlockType = m_BlockType; + a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem((m_BlockType == E_BLOCK_WOODEN_DOOR) ? E_ITEM_WOODEN_DOOR : E_ITEM_IRON_DOOR, 1, 0)); + } + + + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ) override; + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + bool CanReplaceBlock(BLOCKTYPE a_BlockType) + { + switch (a_BlockType) + { + case E_BLOCK_AIR: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_SNOW: + case E_BLOCK_FIRE: + { + return true; + } + } + return false; + } + + + /// Converts the player's yaw to placed door's blockmeta + inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) + { + ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); + + a_Yaw += 90 + 45; + if (a_Yaw > 360) + { + a_Yaw -= 360; + } + if ((a_Yaw >= 0) && (a_Yaw < 90)) + { + return 0x0; + } + else if ((a_Yaw >= 180) && (a_Yaw < 270)) + { + return 0x2; + } + else if ((a_Yaw >= 90) && (a_Yaw < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } + + + /// Returns true if the specified blocktype is any kind of door + inline static bool IsDoor(BLOCKTYPE a_Block) + { + return (a_Block == E_BLOCK_WOODEN_DOOR) || (a_Block == E_BLOCK_IRON_DOOR); + } + + + /// Returns the metadata for the opposite door state (open vs closed) + static NIBBLETYPE ChangeStateMetaData(NIBBLETYPE a_MetaData) + { + return a_MetaData ^ 4; + } + + + /// Changes the door at the specified coords from open to close or vice versa + static void ChangeDoor(cWorld * a_World, int a_X, int a_Y, int a_Z) + { + NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z); + + a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData)); + + if (OldMetaData & 8) + { + // Current block is top of the door + BLOCKTYPE BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z); + NIBBLETYPE BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z); + + if (IsDoor(BottomBlock) && !(BottomMeta & 8)) + { + a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData(BottomMeta)); + } + } + else + { + // Current block is bottom of the door + BLOCKTYPE TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z); + NIBBLETYPE TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z); + + if (IsDoor(TopBlock) && (TopMeta & 8)) + { + a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta)); + } + } + } + + +} ; + + + + diff --git a/src/Blocks/BlockDropSpenser.h b/src/Blocks/BlockDropSpenser.h new file mode 100644 index 000000000..b7f20825d --- /dev/null +++ b/src/Blocks/BlockDropSpenser.h @@ -0,0 +1,41 @@ + +// BlockDropSpenser.h + +// Declares the cBlockDropSpenserHandler class representing the BlockHandler for Dropper and Dispenser blocks + +#pragma once + +#include "../Piston.h" + + + + + +class cBlockDropSpenserHandler : + public cBlockEntityHandler +{ +public: + cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) : + cBlockEntityHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // FIXME: Do not use cPiston class for dispenser placement! + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockEnderchest.h b/src/Blocks/BlockEnderchest.h new file mode 100644 index 000000000..0ce813f1c --- /dev/null +++ b/src/Blocks/BlockEnderchest.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockEnderchestHandler : + public cBlockHandler +{ +public: + cBlockEnderchestHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + //todo: Drop Ender Chest if using silk touch pickaxe + a_Pickups.push_back(cItem(E_BLOCK_OBSIDIAN, 8, 0)); + } +} ; + + + + diff --git a/src/Blocks/BlockEntity.h b/src/Blocks/BlockEntity.h new file mode 100644 index 000000000..9c6b23665 --- /dev/null +++ b/src/Blocks/BlockEntity.h @@ -0,0 +1,31 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockEntityHandler : public cBlockHandler +{ +public: + cBlockEntityHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void OnUse(cWorld * a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override + { + a_World->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + + virtual bool IsUseable() override + { + return true; + } +}; + + + + diff --git a/src/Blocks/BlockFarmland.h b/src/Blocks/BlockFarmland.h new file mode 100644 index 000000000..7bc71f7f3 --- /dev/null +++ b/src/Blocks/BlockFarmland.h @@ -0,0 +1,107 @@ + +// BlockFarmland.h + +// Declares the cBlcokFarmlandHandler representing the block handler for farmland + + + + + +#pragma once + +#include "BlockHandler.h" +#include "../BlockArea.h" + + + + + +class cBlockFarmlandHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockFarmlandHandler(void) : + super(E_BLOCK_FARMLAND) + { + } + + + virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + bool Found = false; + + int Biome = a_World->GetBiomeAt(a_BlockX, a_BlockZ); + if (a_World->IsWeatherWet() && (Biome != biDesert) && (Biome != biDesertHills)) + { + // Rain hydrates farmland, too, except in Desert biomes. + Found = true; + } + else + { + // Search for water in a close proximity: + // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4)) + { + // Too close to the world edge, cannot check surroudnings; don't tick at all + return; + } + + int NumBlocks = Area.GetBlockCount(); + BLOCKTYPE * BlockTypes = Area.GetBlockTypes(); + for (int i = 0; i < NumBlocks; i++) + { + if ( + (BlockTypes[i] == E_BLOCK_WATER) || + (BlockTypes[i] == E_BLOCK_STATIONARY_WATER) + ) + { + Found = true; + break; + } + } // for i - BlockTypes[] + } + + NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if (Found) + { + // Water was found, hydrate the block until hydration reaches 7: + if (BlockMeta < 7) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ++BlockMeta); + } + return; + } + + // Water wasn't found, de-hydrate block: + if (BlockMeta > 0) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, --BlockMeta); + return; + } + + // Farmland too dry. If nothing is growing on top, turn back to dirt: + switch (a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) + { + case E_BLOCK_CROPS: + case E_BLOCK_MELON_STEM: + case E_BLOCK_PUMPKIN_STEM: + { + // Produce on top, don't revert + break; + } + default: + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); + break; + } + } + } +} ; + + + + diff --git a/src/Blocks/BlockFenceGate.h b/src/Blocks/BlockFenceGate.h new file mode 100644 index 000000000..6423a7cb0 --- /dev/null +++ b/src/Blocks/BlockFenceGate.h @@ -0,0 +1,88 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFenceGateHandler : + public cBlockHandler +{ +public: + cBlockFenceGateHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + return true; + } + + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override + { + NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + NIBBLETYPE NewMetaData = PlayerYawToMetaData(a_Player->GetRotation()); + OldMetaData ^= 4; // Toggle the gate + if ((OldMetaData & 1) == (NewMetaData & 1)) + { + // Standing in front of the gate - apply new direction + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (OldMetaData & 4) | (NewMetaData & 3)); + } + else + { + // Standing aside - use last direction + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData); + } + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + /// Converts the player's yaw to placed gate's blockmeta + inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) + { + ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); + + a_Yaw += 360 + 45; + if (a_Yaw > 360) + { + a_Yaw -= 360; + } + if ((a_Yaw >= 0) && (a_Yaw < 90)) + { + return 0x0; + } + else if ((a_Yaw >= 180) && (a_Yaw < 270)) + { + return 0x2; + } + else if ((a_Yaw >= 90) && (a_Yaw < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } +} ; + + + + diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h new file mode 100644 index 000000000..46b56d7e0 --- /dev/null +++ b/src/Blocks/BlockFire.h @@ -0,0 +1,228 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFireHandler : + public cBlockHandler +{ +public: + cBlockFireHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + /// Portal boundary and direction variables + int XZP, XZM, Dir; // For wont of a better name... + + virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + /* + PORTAL FINDING ALGORITH + ======================= + -Get clicked base block + -Trace upwards to find first obsidian block; aborts if anything other than obsidian or air is encountered. + Uses this value as a reference (the 'ceiling') + -For both directions (if one fails, try the other), BASE (clicked) block: + -Go in one direction, only stop if a non obsidian block is encountered (abort) OR a portal border is encountered (FindObsidianCeiling returns -1) + -If a border was encountered, go the other direction and repeat above + -Write borders to XZP and XZM, write direction portal faces to Dir + -Loop through boundary variables, and fill with portal blocks based on Dir with meta from Dir + */ + + a_BlockY--; // Because we want the block below the fire + FindAndSetPortalFrame(a_BlockX, a_BlockY, a_BlockZ, a_World); // Brought to you by Aperture Science + } + + virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // No pickups from this block + } + + virtual bool IsClickedThrough(void) override + { + return true; + } + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + /// Traces along YP until it finds an obsidian block, returns Y difference or 0 if no portal, and -1 for border + /// Takes the X, Y, and Z of the base block; with an optional MaxY for portal border finding + int FindObsidianCeiling(int X, int Y, int Z, cWorld * a_World, int MaxY = 0) + { + if (a_World->GetBlock(X, Y, Z) != E_BLOCK_OBSIDIAN) + { + return 0; + } + + int newY = Y + 1; + + for (newY; newY < cChunkDef::Height; newY++) + { + BLOCKTYPE Block = a_World->GetBlock(X, newY, Z); + if ((Block == E_BLOCK_AIR) || (Block == E_BLOCK_FIRE)) + { + continue; + } + else if (Block == E_BLOCK_OBSIDIAN) + { + // We found an obsidian ceiling + // Make sure MaxY has a value and newY ('ceiling' location) is at one above the base block + // This is because the frame is a solid obsidian pillar + if ((MaxY != 0) && (newY == Y + 1)) + { + return EvaluatePortalBorder(X, newY, Z, MaxY, a_World); + } + else + { + // Return ceiling Y, whoever called this function will decide if it's part of a portal or not + return newY; + } + } + else { return 0; } + } + + return 0; + } + + /// Evaluates if coords have a valid border on top, based on MaxY + int EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cWorld * a_World) + { + for (int checkBorder = FoundObsidianY + 1; checkBorder <= MaxY - 1; checkBorder++) // FoundObsidianY + 1: FoundObsidianY has already been checked in FindObsidianCeiling; MaxY - 1: portal doesn't need corners + { + if (a_World->GetBlock(X, checkBorder, Z) != E_BLOCK_OBSIDIAN) + { + // Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal + return 0; + } + } + // Everything was obsidian, found a border! + return -1; // Return -1 for a frame border + } + + /// Finds entire frame in any direction with the coordinates of a base block and fills hole with nether portal (START HERE) + void FindAndSetPortalFrame(int X, int Y, int Z, cWorld * a_World) + { + int MaxY = FindObsidianCeiling(X, Y, Z, a_World); // Get topmost obsidian block as reference for all other checks + int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add/subtract one as we've checked the original already the line above + + if (MaxY == 0) // Oh noes! Not a portal coordinate :( + { + return; + } + + if (!FindPortalSliceX(X1, X2, Y, Z, MaxY, a_World)) + { + if (!FindPortalSliceZ(X, Y, Z1, Z2, MaxY, a_World)) + { + return; // No eligible portal construct, abort abort abort!! + } + } + + for (int Height = Y + 1; Height <= MaxY - 1; Height++) // Loop through boundary to set portal blocks + { + for (int Width = XZM; Width <= XZP; Width++) + { + if (Dir == 1) + { + a_World->SetBlock(Width, Height, Z, E_BLOCK_NETHER_PORTAL, Dir); + } + else + { + a_World->SetBlock(X, Height, Width, E_BLOCK_NETHER_PORTAL, Dir); + } + } + } + + return; + } + + /// Evaluates if coordinates are a portal going XP/XM; returns true if so, and writes boundaries to variable + /// Takes coordinates of base block and Y coord of target obsidian ceiling + bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cWorld * a_World) + { + Dir = 1; // Set assumed direction (will change if portal turns out to be facing the other direction) + bool FoundFrameXP = false, FoundFrameXM = false; + for (X1; ((a_World->GetBlock(X1, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X1, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X1++) // Check XP for obsidian blocks, exempting corners + { + int Value = FindObsidianCeiling(X1, Y, Z, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X1, Y + 1, Z, a_World, MaxY); // For corners without obsidian + if ((Value == -1) || (ValueTwo == -1)) // FindObsidianCeiling returns -1 upon frame-find + { + FoundFrameXP = true; // Found a frame border in this direction, proceed in other direction (don't go further) + break; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) // Make sure that there is a valid portal 'slice' + { + return false; // Not valid slice, no portal can be formed + } + } XZP = X1 - 1; // Set boundary of frame interior, note that for some reason, the loop of X and the loop of Z go to different numbers, hence -1 here and -2 there + for (X2; ((a_World->GetBlock(X2, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X2, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM) + { + int Value = FindObsidianCeiling(X2, Y, Z, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X2, Y + 1, Z, a_World, MaxY); + if ((Value == -1) || (ValueTwo == -1)) + { + FoundFrameXM = true; + break; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) + { + return false; + } + } XZM = X2 + 1; // Set boundary, see previous + return (FoundFrameXP && FoundFrameXM); + } + + /// Evaluates if coords are a portal going ZP/ZM; returns true if so, and writes boundaries to variable + bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cWorld * a_World) + { + Dir = 2; + bool FoundFrameZP = false, FoundFrameZM = false; + for (Z1; ((a_World->GetBlock(X, Y, Z1) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z1) == E_BLOCK_OBSIDIAN)); Z1++) + { + int Value = FindObsidianCeiling(X, Y, Z1, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X, Y + 1, Z1, a_World, MaxY); + if ((Value == -1) || (ValueTwo == -1)) + { + FoundFrameZP = true; + continue; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) + { + return false; + } + } XZP = Z1 - 2; + for (Z2; ((a_World->GetBlock(X, Y, Z2) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z2) == E_BLOCK_OBSIDIAN)); Z2--) + { + int Value = FindObsidianCeiling(X, Y, Z2, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X, Y + 1, Z2, a_World, MaxY); + if ((Value == -1) || (ValueTwo == -1)) + { + FoundFrameZM = true; + continue; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) + { + return false; + } + } XZM = Z2 + 2; + return (FoundFrameZP && FoundFrameZM); + } +}; + + + + diff --git a/src/Blocks/BlockFlower.h b/src/Blocks/BlockFlower.h new file mode 100644 index 000000000..421e2d5d8 --- /dev/null +++ b/src/Blocks/BlockFlower.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFlowerHandler : + public cBlockHandler +{ +public: + cBlockFlowerHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockFlowerPot.h b/src/Blocks/BlockFlowerPot.h new file mode 100644 index 000000000..b0faf5218 --- /dev/null +++ b/src/Blocks/BlockFlowerPot.h @@ -0,0 +1,105 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFlowerPotHandler : + public cBlockHandler +{ +public: + cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0)); + if (a_BlockMeta == 0) + { + return; + } + cItem Plant; + switch (a_BlockMeta) + { + case 1: Plant = cItem(E_BLOCK_RED_ROSE, 1, 0); break; + case 2: Plant = cItem(E_BLOCK_YELLOW_FLOWER, 1, 0); break; + case 3: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_APPLE); break; + case 4: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_CONIFER); break; + case 5: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_BIRCH); break; + case 6: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_JUNGLE); break; + case 7: Plant = cItem(E_BLOCK_RED_MUSHROOM, 1, 0); break; + case 8: Plant = cItem(E_BLOCK_BROWN_MUSHROOM, 1, 0); break; + case 9: Plant = cItem(E_BLOCK_CACTUS, 1, 0); break; + case 10: Plant = cItem(E_BLOCK_DEAD_BUSH, 1, 0); break; + case 11: Plant = cItem(E_BLOCK_TALL_GRASS, 1, E_META_TALL_GRASS_FERN); break; + default: return; + } + a_Pickups.push_back(Plant); + } + + + void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + NIBBLETYPE Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); + if (Meta != 0) + { + // Already filled + return; + } + + switch (a_Player->GetEquippedItem().m_ItemType) + { + case E_BLOCK_RED_ROSE: Meta = 1; break; + case E_BLOCK_YELLOW_FLOWER: Meta = 2; break; + case E_BLOCK_SAPLING: + { + switch (a_Player->GetEquippedItem().m_ItemDamage) + { + case E_META_SAPLING_APPLE: Meta = 3; break; + case E_META_SAPLING_CONIFER: Meta = 4; break; + case E_META_SAPLING_BIRCH: Meta = 5; break; + case E_META_SAPLING_JUNGLE: Meta = 6; break; + } + break; + } + case E_BLOCK_RED_MUSHROOM: Meta = 7; break; + case E_BLOCK_BROWN_MUSHROOM: Meta = 8; break; + case E_BLOCK_CACTUS: Meta = 9; break; + case E_BLOCK_DEAD_BUSH: Meta = 10; break; + case E_BLOCK_TALL_GRASS: + { + if (a_Player->GetEquippedItem().m_ItemDamage == E_META_TALL_GRASS_FERN) + { + Meta = 11; + } + else + { + return; + } + break; + } + } + + if (a_Player->GetGameMode() != gmCreative) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); + } + + + virtual bool IsUseable(void) override + { + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockFluid.h b/src/Blocks/BlockFluid.h new file mode 100644 index 000000000..0db2f60c4 --- /dev/null +++ b/src/Blocks/BlockFluid.h @@ -0,0 +1,56 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFluidHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockFluidHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // No pickups + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override + { + switch (m_BlockType) + { + case E_BLOCK_STATIONARY_LAVA: + { + a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_LAVA, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + break; + } + case E_BLOCK_STATIONARY_WATER: + { + a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_WATER, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + break; + } + } + super::Check(a_RelX, a_RelY, a_RelZ, a_Chunk); + } +} ; + + + + diff --git a/src/Blocks/BlockFurnace.h b/src/Blocks/BlockFurnace.h new file mode 100644 index 000000000..fe35893d5 --- /dev/null +++ b/src/Blocks/BlockFurnace.h @@ -0,0 +1,47 @@ + +#pragma once + +#include "BlockEntity.h" +#include "../World.h" +#include "../Piston.h" +#include "../Entities/Player.h" + + + + + +class cBlockFurnaceHandler : + public cBlockEntityHandler +{ +public: + cBlockFurnaceHandler(BLOCKTYPE a_BlockType) : + cBlockEntityHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_FURNACE, 1, 0)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // FIXME: Do not use cPiston class for furnace placement! + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0); + + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockGlass.h b/src/Blocks/BlockGlass.h new file mode 100644 index 000000000..f6958bbb6 --- /dev/null +++ b/src/Blocks/BlockGlass.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockGlassHandler : + public cBlockHandler +{ +public: + cBlockGlassHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + } +} ; + + + + diff --git a/src/Blocks/BlockGlowstone.h b/src/Blocks/BlockGlowstone.h new file mode 100644 index 000000000..5f0d95dee --- /dev/null +++ b/src/Blocks/BlockGlowstone.h @@ -0,0 +1,30 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockGlowstoneHandler : + public cBlockHandler +{ +public: + cBlockGlowstoneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + // TODO: More drops? + a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0)); + } +} ; + + + + diff --git a/src/Blocks/BlockGravel.h b/src/Blocks/BlockGravel.h new file mode 100644 index 000000000..e1c9ff390 --- /dev/null +++ b/src/Blocks/BlockGravel.h @@ -0,0 +1,27 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockGravelHandler : + public cBlockHandler +{ +public: + cBlockGravelHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual const char * GetStepSound(void) override + { + return "step.gravel"; + } +} ; + + + + diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp new file mode 100644 index 000000000..cd07b3021 --- /dev/null +++ b/src/Blocks/BlockHandler.cpp @@ -0,0 +1,465 @@ + +#include "Globals.h" +#include "BlockHandler.h" +#include "../Item.h" +#include "../World.h" +#include "../Root.h" +#include "../PluginManager.h" +#include "BlockBed.h" +#include "BlockBrewingStand.h" +#include "BlockButton.h" +#include "BlockCactus.h" +#include "BlockCarpet.h" +#include "BlockCauldron.h" +#include "BlockChest.h" +#include "BlockCloth.h" +#include "BlockCobWeb.h" +#include "BlockComparator.h" +#include "BlockCrops.h" +#include "BlockDeadBush.h" +#include "BlockDirt.h" +#include "BlockDoor.h" +#include "BlockDropSpenser.h" +#include "BlockEnderchest.h" +#include "BlockEntity.h" +#include "BlockFarmland.h" +#include "BlockFenceGate.h" +#include "BlockFire.h" +#include "BlockFlower.h" +#include "BlockFlowerPot.h" +#include "BlockFluid.h" +#include "BlockFurnace.h" +#include "BlockGlass.h" +#include "BlockGlowstone.h" +#include "BlockGravel.h" +#include "BlockHopper.h" +#include "BlockIce.h" +#include "BlockLadder.h" +#include "BlockLeaves.h" +#include "BlockLever.h" +#include "BlockMelon.h" +#include "BlockMushroom.h" +#include "BlockMycelium.h" +#include "BlockNote.h" +#include "BlockOre.h" +#include "BlockPiston.h" +#include "BlockPlanks.h" +#include "BlockPortal.h" +#include "BlockPumpkin.h" +#include "BlockRail.h" +#include "BlockRedstone.h" +#include "BlockRedstoneRepeater.h" +#include "BlockRedstoneTorch.h" +#include "BlockSand.h" +#include "BlockSapling.h" +#include "BlockSign.h" +#include "BlockSlab.h" +#include "BlockSnow.h" +#include "BlockStairs.h" +#include "BlockStems.h" +#include "BlockStone.h" +#include "BlockSugarcane.h" +#include "BlockTallGrass.h" +#include "BlockTorch.h" +#include "BlockVine.h" +#include "BlockWood.h" +#include "BlockWorkbench.h" + + + + + +bool cBlockHandler::m_HandlerInitialized = false; +cBlockHandler * cBlockHandler::m_BlockHandler[256]; + + + + + +cBlockHandler * cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockType) +{ + if (!m_HandlerInitialized) + { + // We have to initialize + memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); + m_HandlerInitialized = true; + } + if (m_BlockHandler[a_BlockType] != NULL) + { + return m_BlockHandler[a_BlockType]; + } + + return m_BlockHandler[a_BlockType] = CreateBlockHandler(a_BlockType); +} + + + + + +cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) +{ + switch(a_BlockType) + { + // Block handlers, alphabetically sorted: + case E_BLOCK_ACTIVATOR_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType); + case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_BREWING_STAND: return new cBlockBrewingStandHandler (a_BlockType); + case E_BLOCK_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_BROWN_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); + case E_BLOCK_CACTUS: return new cBlockCactusHandler (a_BlockType); + case E_BLOCK_CARROTS: return new cBlockCropsHandler (a_BlockType); + case E_BLOCK_CARPET: return new cBlockCarpetHandler (a_BlockType); + case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType); + case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType); + case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_ACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); + case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType); + case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType); + case E_BLOCK_CROPS: return new cBlockCropsHandler (a_BlockType); + case E_BLOCK_DEAD_BUSH: return new cBlockDeadBushHandler (a_BlockType); + case E_BLOCK_DETECTOR_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_DIAMOND_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_DIRT: return new cBlockDirtHandler (a_BlockType); + case E_BLOCK_DISPENSER: return new cBlockDropSpenserHandler (a_BlockType); + case E_BLOCK_DOUBLE_STONE_SLAB: return new cBlockDoubleSlabHandler (a_BlockType); + case E_BLOCK_DOUBLE_WOODEN_SLAB: return new cBlockDoubleSlabHandler (a_BlockType); + case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType); + case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType); + case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler ( ); + case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType); + case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType); + case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType); + case E_BLOCK_FURNACE: return new cBlockFurnaceHandler (a_BlockType); + case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType); + case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_GLASS: return new cBlockGlassHandler (a_BlockType); + case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType); + case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType); + case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType); + case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType); + case E_BLOCK_INACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); + case E_BLOCK_IRON_DOOR: return new cBlockDoorHandler (a_BlockType); + case E_BLOCK_IRON_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_JUKEBOX: return new cBlockEntityHandler (a_BlockType); + case E_BLOCK_JUNGLE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_LADDER: return new cBlockLadderHandler (a_BlockType); + case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType); + case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_LAVA: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType); + case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType); + case E_BLOCK_LOG: return new cBlockWoodHandler (a_BlockType); + case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType); + case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType); + case E_BLOCK_MYCELIUM: return new cBlockMyceliumHandler (a_BlockType); + case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType); + case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType); + case E_BLOCK_PISTON_EXTENSION: return new cBlockPistonHeadHandler ( ); + case E_BLOCK_PLANKS: return new cBlockPlanksHandler (a_BlockType); + case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType); + case E_BLOCK_PUMPKIN: return new cBlockPumpkinHandler (a_BlockType); + case E_BLOCK_JACK_O_LANTERN: return new cBlockPumpkinHandler (a_BlockType); + case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType); + case E_BLOCK_QUARTZ_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_POTATOES: return new cBlockCropsHandler (a_BlockType); + case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType); + case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType); + case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType); + case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType); + case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType); + case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); + case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType); + case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType); + case E_BLOCK_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_SAPLING: return new cBlockSaplingHandler (a_BlockType); + case E_BLOCK_SIGN_POST: return new cBlockSignHandler (a_BlockType); + case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType); + case E_BLOCK_SPRUCE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_STATIONARY_LAVA: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_STATIONARY_WATER: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_STICKY_PISTON: return new cBlockPistonHandler (a_BlockType); + case E_BLOCK_STONE: return new cBlockStoneHandler (a_BlockType); + case E_BLOCK_STONE_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_STONE_BUTTON: return new cBlockButtonHandler (a_BlockType); + case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType); + case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType); + case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType); + case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType); + case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType); + case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); + case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_WOODEN_BUTTON: return new cBlockButtonHandler (a_BlockType); + case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType); + case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType); + case E_BLOCK_WOODEN_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_WOOL: return new cBlockClothHandler (a_BlockType); + case E_BLOCK_WORKBENCH: return new cBlockWorkbenchHandler (a_BlockType); + case E_BLOCK_YELLOW_FLOWER: return new cBlockFlowerHandler (a_BlockType); + + default: return new cBlockHandler(a_BlockType); + } +} + + + + + +void cBlockHandler::Deinit() +{ + for (int i = 0; i < 256; i++) + { + delete m_BlockHandler[i]; + } + memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); // Don't leave any dangling pointers around, just in case + m_HandlerInitialized = false; +} + + + + + +cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType) +{ + m_BlockType = a_BlockType; +} + + + + + +bool cBlockHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + // By default, all blocks can be placed and the meta is copied over from the item's damage value: + a_BlockType = m_BlockType; + a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f); + return true; +} + + + + + +void cBlockHandler::OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ +} + + + + + +void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnPlaced(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // Notify the neighbors + NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); +} + + + + + +void cBlockHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Notify the neighbors + NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); +} + + + + + +void cBlockHandler::NeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height)) + { + GetBlockHandler(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ +} + + + + + +void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) +{ + // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this. + a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta)); +} + + + + + +void cBlockHandler::DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cItems Pickups; + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + ConvertToPickups(Pickups, Meta); + + // Allow plugins to modify the pickups: + cRoot::Get()->GetPluginManager()->CallHookBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups); + + if (!Pickups.empty()) + { + MTRand r1; + + // Mid-block position first + double MicroX, MicroY, MicroZ; + MicroX = a_BlockX + 0.5; + MicroY = a_BlockY + 0.5; + MicroZ = a_BlockZ + 0.5; + + // Add random offset second (this causes pickups to spawn inside blocks most times, it's a little buggy) + //MicroX += (int)(r1.randInt(16) + r1.randInt(16) - 16); + //MicroY += (int)(r1.randInt(16) + r1.randInt(16) - 16); + //MicroZ += (int)(r1.randInt(16) + r1.randInt(16) - 16); + + a_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ); + } +} + + + + + +const char * cBlockHandler::GetStepSound() +{ + return "step.stone"; +} + + + + + +bool cBlockHandler::CanBeAt(int a_BlockX, int a_BlockY, int a_BlockZ, const cChunk & a_Chunk) +{ + return true; +} + + + + + +bool cBlockHandler::IsUseable() +{ + return false; +} + + + + + +bool cBlockHandler::IsClickedThrough(void) +{ + return false; +} + + + + + +bool cBlockHandler::DoesIgnoreBuildCollision(void) +{ + return (m_BlockType == E_BLOCK_AIR); +} + + + + + +bool cBlockHandler::DoesDropOnUnsuitable(void) +{ + return true; +} + + + + + +void cBlockHandler::Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) +{ + if (!CanBeAt(a_RelX, a_RelY, a_RelZ, a_Chunk)) + { + if (DoesDropOnUnsuitable()) + { + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); + } + + a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + } + else + { + // Wake up the simulators for this block: + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); + } +} + + + + diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h new file mode 100644 index 000000000..81d9f240c --- /dev/null +++ b/src/Blocks/BlockHandler.h @@ -0,0 +1,152 @@ + +#pragma once + +#include "../Defines.h" +#include "../Item.h" +#include "../Chunk.h" + + + + + +// fwd: +class cWorld; +class cPlayer; + + + + + +class cBlockHandler +{ +public: + cBlockHandler(BLOCKTYPE a_BlockType); + + /// Called when the block gets ticked either by a random tick or by a queued tick + virtual void OnUpdate(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /** Called before a block is placed into a world. + The handler should return true to allow placement, false to refuse. + Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. + Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block + */ + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ); + + /// Called by cWorld::SetBlock() after the block has been set + virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced(). + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ); + + /// Called before the player has destroyed a block + virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called before a block gets destroyed / replaced with air + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position) + virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Notifies all neighbors of the given block about a change + static void NeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called while the player diggs the block. + virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called if the user right clicks the block and the block is useable + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + + /// Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta); + + /// Handles the dropping of a block based on what ConvertToDrops() returns. This will not destroy the block. a_Digger is the entity causing the drop; it may be NULL + virtual void DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Returns step sound name of block + virtual const char * GetStepSound(void); + + /// Checks if the block can stay at the specified relative coords in the chunk + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk); + + /** Checks if the block can be placed at this point. + Default: CanBeAt(...) + NOTE: This call doesn't actually place the block + */ + // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); + + /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called + virtual bool IsUseable(void); + + /** Indicates whether the client will click through this block. + For example digging a fire will hit the block below the fire so fire is clicked through + */ + virtual bool IsClickedThrough(void); + + /** Checks if the player can build "inside" this block. + For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision + */ + virtual bool DoesIgnoreBuildCollision(void); + + /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true + virtual bool DoesDropOnUnsuitable(void); + + /** Called when one of the neighbors gets set; equivalent to MC block update. + By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()), + and wakes up all simulators on the block. + */ + virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk); + + /// Returns the meta for a block after rotating it counter-clockwise from the specified meta. Default: no change + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after rotating it clockwise from the specified meta. Default: no change + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after mirroring it around the XY plane. Default: no change + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after mirroring it around the XZ plane. Default: no change + virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after mirroring it around the YZ plane. Default: no change + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; } + + + /// Get the blockhandler for a specific block id + static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockType); + + /// Deletes all initialised block handlers + static void Deinit(); + +protected: + BLOCKTYPE m_BlockType; + + // Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead. + static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockType); + static cBlockHandler *m_BlockHandler[256]; + static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized +}; + + + + + +// Shortcut to get the blockhandler for a specific block +inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType) +{ + return cBlockHandler::GetBlockHandler(a_BlockType); +} + + + + diff --git a/src/Blocks/BlockHopper.h b/src/Blocks/BlockHopper.h new file mode 100644 index 000000000..3998276d7 --- /dev/null +++ b/src/Blocks/BlockHopper.h @@ -0,0 +1,46 @@ + +// BlockHopper.h + +// Declares the cBlockHopperHandler class representing the handler for the Hopper block + + + + + +class cBlockHopperHandler : + public cBlockEntityHandler +{ +public: + cBlockHopperHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // Convert the blockface into meta: + switch (a_BlockFace) + { + case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break; + case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break; + case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break; + case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break; + case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break; + case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break; + default: a_BlockMeta = E_META_HOPPER_UNATTACHED; break; + } + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockIce.h b/src/Blocks/BlockIce.h new file mode 100644 index 000000000..af4961114 --- /dev/null +++ b/src/Blocks/BlockIce.h @@ -0,0 +1,37 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockIceHandler : + public cBlockHandler +{ +public: + cBlockIceHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // No pickups + } + + + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + // TODO: Ice destroyed with air below it should turn into air instead of water + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0); + // This is called later than the real destroying of this ice block + } +} ; + + + + diff --git a/src/Blocks/BlockLadder.h b/src/Blocks/BlockLadder.h new file mode 100644 index 000000000..c0aa25f60 --- /dev/null +++ b/src/Blocks/BlockLadder.h @@ -0,0 +1,115 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockLadderHandler : + public cBlockHandler +{ +public: + cBlockLadderHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (!LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + a_BlockFace = FindSuitableBlockFace(a_World, a_BlockX, a_BlockY, a_BlockZ); + + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + return false; + } + } + + a_BlockType = m_BlockType; + a_BlockMeta = DirectionToMetaData(a_BlockFace); + return true; + } + + + static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export + { // tolua_export + switch (a_Direction) + { + case 0x2: return 0x2; + case 0x3: return 0x3; + case 0x4: return 0x4; + case 0x5: return 0x5; + default: return 0x2; + } + } // tolua_export + + + static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export + { // tolua_export + switch (a_MetaData) + { + case 0x2: return 0x2; + case 0x3: return 0x3; + case 0x4: return 0x4; + case 0x5: return 0x5; + default: return 0x2; + } + } // tolua_export + + + /// Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure + static char FindSuitableBlockFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + for (int Face = 2; Face <= 5; Face++) + { + if (LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, Face)) + { + return Face; + } + } + return BLOCK_FACE_BOTTOM; + } + + + static bool LadderCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + { + if ((a_BlockFace == BLOCK_FACE_BOTTOM) || (a_BlockFace == BLOCK_FACE_TOP)) + { + return false; + } + + AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); + + return g_BlockIsSolid[a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)]; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison + char BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + return LadderCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, BlockFace); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h new file mode 100644 index 000000000..6e015b8fa --- /dev/null +++ b/src/Blocks/BlockLeaves.h @@ -0,0 +1,184 @@ +#pragma once +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" +#include "../BlockArea.h" + + + + + +// Leaves can be this many blocks that away (inclusive) from the log not to decay +#define LEAVES_CHECK_DISTANCE 6 + +#define PROCESS_NEIGHBOR(x,y,z) \ + switch (a_Area.GetBlockType(x, y, z)) \ + { \ + case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \ + case E_BLOCK_LOG: return true; \ + } + +bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); + + + + + +class cBlockLeavesHandler : + public cBlockHandler +{ +public: + cBlockLeavesHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + MTRand rand; + + // Only the first 2 bits contain the display information, the others are for growing + if (rand.randInt(5) == 0) + { + a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); + } + if ((a_BlockMeta & 3) == E_META_SAPLING_APPLE) + { + if (rand.rand(100) == 0) + { + a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); + } + } + } + + + void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + cBlockHandler::OnDestroyed(a_World, a_BlockX, a_BlockY, a_BlockZ); + + //0.5% chance of dropping an apple + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + //check if Oak (0x1 and 0x2 bit not set) + MTRand rand; + if(!(Meta & 3) && rand.randInt(200) == 100) + { + cItems Drops; + Drops.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); + a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); + } + } + + + virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x7); // Unset 0x8 bit so it gets checked for decay + } + + + virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if ((Meta & 0x04) != 0) + { + // Player-placed leaves, don't decay + return; + } + + if ((Meta & 0x8) != 0) + { + // These leaves have been checked for decay lately and nothing around them changed + return; + } + + // Get the data around the leaves: + cBlockArea Area; + if (!Area.Read( + a_World, + a_BlockX - LEAVES_CHECK_DISTANCE, a_BlockX + LEAVES_CHECK_DISTANCE, + a_BlockY - LEAVES_CHECK_DISTANCE, a_BlockY + LEAVES_CHECK_DISTANCE, + a_BlockZ - LEAVES_CHECK_DISTANCE, a_BlockZ + LEAVES_CHECK_DISTANCE, + cBlockArea::baTypes) + ) + { + // Cannot check leaves, a chunk is missing too close + return; + } + + if (HasNearLog(Area, a_BlockX, a_BlockY, a_BlockZ)) + { + // Wood found, the leaves stay; mark them as checked: + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x8); + return; + } + // Decay the leaves: + DropBlock(a_World, NULL, a_BlockX, a_BlockY, a_BlockZ); + + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + + +bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Filter the blocks into a {leaves, log, other (air)} set: + BLOCKTYPE * Types = a_Area.GetBlockTypes(); + for (int i = a_Area.GetBlockCount() - 1; i > 0; i--) + { + switch (Types[i]) + { + case E_BLOCK_LEAVES: + case E_BLOCK_LOG: + { + break; + } + default: + { + Types[i] = E_BLOCK_AIR; + break; + } + } + } // for i - Types[] + + // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block: + // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations + a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE); + for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++) + { + for (int y = a_BlockY - i; y <= a_BlockY + i; y++) + { + for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++) + { + for (int x = a_BlockX - i; x <= a_BlockX + i; x++) + { + if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i) + { + continue; + } + PROCESS_NEIGHBOR(x - 1, y, z); + PROCESS_NEIGHBOR(x + 1, y, z); + PROCESS_NEIGHBOR(x, y, z - 1); + PROCESS_NEIGHBOR(x, y, z + 1); + PROCESS_NEIGHBOR(x, y + 1, z); + PROCESS_NEIGHBOR(x, y - 1, z); + } // for x + } // for z + } // for y + } // for i - BFS iterations + return false; +} + + + + diff --git a/src/Blocks/BlockLever.cpp b/src/Blocks/BlockLever.cpp new file mode 100644 index 000000000..a9bd6c990 --- /dev/null +++ b/src/Blocks/BlockLever.cpp @@ -0,0 +1,38 @@ + +#include "Globals.h" +#include "BlockLever.h" +#include "../Entities/Player.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +cBlockLeverHandler::cBlockLeverHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockLeverHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off. + NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); + + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); + if (Meta & 0x08) + { + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + } + else + { + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.5f); + } +} + + + + diff --git a/src/Blocks/BlockLever.h b/src/Blocks/BlockLever.h new file mode 100644 index 000000000..5553170e2 --- /dev/null +++ b/src/Blocks/BlockLever.h @@ -0,0 +1,53 @@ +#pragma once + +#include "BlockHandler.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +class cBlockLeverHandler : + public cBlockHandler +{ +public: + cBlockLeverHandler(BLOCKTYPE a_BlockType); + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_BLOCK_LEVER, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = cRedstoneSimulator::LeverDirectionToMetaData(a_BlockFace); + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockMelon.h b/src/Blocks/BlockMelon.h new file mode 100644 index 000000000..2f7d9a461 --- /dev/null +++ b/src/Blocks/BlockMelon.h @@ -0,0 +1,35 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockMelonHandler : + public cBlockHandler +{ +public: + cBlockMelonHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + MTRand r1; + a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, (char)(3 + r1.randInt(4)), 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockMushroom.h b/src/Blocks/BlockMushroom.h new file mode 100644 index 000000000..2846a6317 --- /dev/null +++ b/src/Blocks/BlockMushroom.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockMushroomHandler : + public cBlockHandler +{ +public: + cBlockMushroomHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + + // TODO: Cannot be at too much daylight + + switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) + { + case E_BLOCK_GLASS: + case E_BLOCK_CACTUS: + case E_BLOCK_ICE: + case E_BLOCK_LEAVES: + case E_BLOCK_AIR: + { + return false; + } + } + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockMycelium.h b/src/Blocks/BlockMycelium.h new file mode 100644 index 000000000..7f897c72a --- /dev/null +++ b/src/Blocks/BlockMycelium.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockMyceliumHandler : + public cBlockHandler +{ +public: + cBlockMyceliumHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); + } + + virtual const char * GetStepSound(void) override + { + return "step.gravel"; + } +} ; + + + + diff --git a/src/Blocks/BlockNote.h b/src/Blocks/BlockNote.h new file mode 100644 index 000000000..fef38d845 --- /dev/null +++ b/src/Blocks/BlockNote.h @@ -0,0 +1,13 @@ +#pragma once +#include "BlockHandler.h" +#include "BlockEntity.h" + +class cBlockNoteHandler : public cBlockEntityHandler +{ +public: + cBlockNoteHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + +}; diff --git a/src/Blocks/BlockOre.h b/src/Blocks/BlockOre.h new file mode 100644 index 000000000..9684dbb19 --- /dev/null +++ b/src/Blocks/BlockOre.h @@ -0,0 +1,80 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +class cBlockOreHandler : + public cBlockHandler +{ +public: + cBlockOreHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + short ItemType = m_BlockType; + char Count = 1; + short Meta = 0; + + MTRand r1; + switch (m_BlockType) + { + case E_BLOCK_LAPIS_ORE: + { + ItemType = E_ITEM_DYE; + Count = 4 + (char)r1.randInt(4); + Meta = 4; + break; + } + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + Count = 4 + (char)r1.randInt(1); + break; + } + default: + { + Count = 1; + break; + } + } + + switch (m_BlockType) + { + case E_BLOCK_DIAMOND_ORE: + { + ItemType = E_ITEM_DIAMOND; + break; + } + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + ItemType = E_ITEM_REDSTONE_DUST; + break; + } + case E_BLOCK_EMERALD_ORE: + { + ItemType = E_ITEM_EMERALD; + break; + } + case E_BLOCK_COAL_ORE: + { + ItemType = E_ITEM_COAL; + break; + } + } + a_Pickups.push_back(cItem(ItemType, Count, Meta)); + } +} ; + + + + diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp new file mode 100644 index 000000000..d5750ebdd --- /dev/null +++ b/src/Blocks/BlockPiston.cpp @@ -0,0 +1,102 @@ + +#include "Globals.h" +#include "BlockPiston.h" +#include "../Item.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../Piston.h" + + + + + +#define AddPistonDir(x, y, z, dir, amount) \ + switch (dir) \ + { \ + case 0: (y) -= (amount); break; \ + case 1: (y) += (amount); break; \ + case 2: (z) -= (amount); break; \ + case 3: (z) += (amount); break; \ + case 4: (x) -= (amount); break; \ + case 5: (x) += (amount); break; \ + } + + + + +cBlockPistonHandler::cBlockPistonHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockPistonHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + int newX = a_BlockX; + int newY = a_BlockY; + int newZ = a_BlockZ; + AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1); + + if (a_World->GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION) + { + a_World->SetBlock(newX, newY, newZ, E_BLOCK_AIR, 0); + } +} + + + + + +bool cBlockPistonHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + a_BlockType = m_BlockType; + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBlockPistonHeadHandler: + +cBlockPistonHeadHandler::cBlockPistonHeadHandler(void) : + super(E_BLOCK_PISTON_EXTENSION) +{ +} + + + + + +void cBlockPistonHeadHandler::OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + int newX = a_BlockX; + int newY = a_BlockY; + int newZ = a_BlockZ; + AddPistonDir(newX, newY, newZ, OldMeta & ~(8), -1); + + BLOCKTYPE Block = a_World->GetBlock(newX, newY, newZ); + if ((Block == E_BLOCK_STICKY_PISTON) || (Block == E_BLOCK_PISTON)) + { + a_World->DigBlock(newX, newY, newZ); + } +} + + + + + diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h new file mode 100644 index 000000000..109f5ea8b --- /dev/null +++ b/src/Blocks/BlockPiston.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockPistonHandler : + public cBlockHandler +{ +public: + cBlockPistonHandler(BLOCKTYPE a_BlockType); + + virtual void OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override; +} ; + + + + + +class cBlockPistonHeadHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockPistonHeadHandler(void); + + virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override; +} ; + + + + diff --git a/src/Blocks/BlockPlanks.h b/src/Blocks/BlockPlanks.h new file mode 100644 index 000000000..f3b8dbfb6 --- /dev/null +++ b/src/Blocks/BlockPlanks.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockPlanksHandler : public cBlockHandler +{ +public: + cBlockPlanksHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); + a_BlockMeta = Meta; + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockPortal.h b/src/Blocks/BlockPortal.h new file mode 100644 index 000000000..c56f0cbc8 --- /dev/null +++ b/src/Blocks/BlockPortal.h @@ -0,0 +1,108 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockPortalHandler : + public cBlockHandler +{ +public: + cBlockPortalHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // We set to zero so MCS doesn't stop you from building weird portals like vanilla does + // CanBeAt doesn't do anything if meta is zero + // We set to zero because the client sends meta = 2 to the server (it calculates rotation itself) + + a_BlockType = m_BlockType; + a_BlockMeta = 0; + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + return; // No pickups + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if ((a_RelY - 1 < 0) || (a_RelY + 1 > cChunkDef::Height)) + { + return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1 + } + + switch (a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)) + { + case 0x1: + { + static const struct + { + int x, y, z; + } PortalCheck[] = + { + { 0, 1, 0}, + { 0,-1, 0}, + { 1, 0, 0}, + {-1, 0, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++) + { + BLOCKTYPE Block; + a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block); + + if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) + { + return false; + } + } + break; + } + case 0x2: + { + static const struct + { + int x, y, z; + } PortalCheck[] = + { + { 0, 1, 0}, + { 0,-1, 0}, + { 0, 0, -1}, + { 0, 0, 1}, + } ; + + for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++) + { + BLOCKTYPE Block; + a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block); + + if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) + { + return false; + } + } + break; + } + } + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h new file mode 100644 index 000000000..76abc6818 --- /dev/null +++ b/src/Blocks/BlockPumpkin.h @@ -0,0 +1,60 @@ +#pragma once + +#include "BlockHandler.h" + + + + +class cBlockPumpkinHandler : + public cBlockHandler +{ +public: + cBlockPumpkinHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + return true; + } + + inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) + { + ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); + + a_Yaw += 180 + 45; + if (a_Yaw > 360) + { + a_Yaw -= 360; + } + if ((a_Yaw >= 0) && (a_Yaw < 90)) + { + return 0x0; + } + else if ((a_Yaw >= 180) && (a_Yaw < 270)) + { + return 0x2; + } + else if ((a_Yaw >= 90) && (a_Yaw < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } + +} ; + + + + diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h new file mode 100644 index 000000000..24a101652 --- /dev/null +++ b/src/Blocks/BlockRail.h @@ -0,0 +1,398 @@ + +#pragma once + +#include "BlockEntity.h" +#include "../World.h" + + + + + +enum ENUM_PURE +{ + E_PURE_UPDOWN = 0, + E_PURE_DOWN = 1, + E_PURE_NONE = 2 +}; + + + + + +class cBlockRailHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockRailHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ); + return true; + } + + + virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ) && (Meta != FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ))) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ)); + } + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + super::ConvertToPickups(a_Pickups, 0); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + if (!g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]) + { + return false; + } + + NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + switch (Meta) + { + case E_META_RAIL_ASCEND_XP: + case E_META_RAIL_ASCEND_XM: + case E_META_RAIL_ASCEND_ZM: + case E_META_RAIL_ASCEND_ZP: + { + // Mapping between the meta and the neighbors that need checking + Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero + static const struct + { + int x, z; + } Coords[] = + { + { 1, 0}, // east, XP + {-1, 0}, // west, XM + { 0, -1}, // north, ZM + { 0, 1}, // south, ZP + } ; + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta)) + { + // Too close to the edge, cannot simulate + return true; + } + return g_BlockIsSolid[BlockType]; + } + } + return true; + } + + NIBBLETYPE FindMeta(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + NIBBLETYPE Meta = 0; + char RailsCnt = 0; + bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP + memset(Neighbors, false, sizeof(Neighbors)); + Neighbors[0] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN)); + Neighbors[1] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN)); + Neighbors[2] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN)); + Neighbors[3] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN)); + Neighbors[4] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST, E_PURE_NONE)); + Neighbors[5] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST, E_PURE_NONE)); + Neighbors[6] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE)); + Neighbors[7] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE)); + if (IsUnstable(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST)) + Neighbors[0] = true; + if (IsUnstable(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST)) + Neighbors[1] = true; + if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH)) + Neighbors[2] = true; + if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH)) + Neighbors[3] = true; + for (int i = 0; i < 8; i++) + { + if (Neighbors[i]) + { + RailsCnt++; + } + } + if (RailsCnt == 1) + { + if (Neighbors[7]) return E_META_RAIL_ASCEND_ZP; + else if (Neighbors[6]) return E_META_RAIL_ASCEND_ZM; + else if (Neighbors[5]) return E_META_RAIL_ASCEND_XM; + else if (Neighbors[4]) return E_META_RAIL_ASCEND_XP; + else if (Neighbors[0] || Neighbors[1]) return E_META_RAIL_XM_XP; + else if (Neighbors[2] || Neighbors[3]) return E_META_RAIL_ZM_ZP; + ASSERT(!"Weird neighbor count"); + return Meta; + } + for (int i = 0; i < 4; i++) + { + if (Neighbors[i + 4]) + { + Neighbors[i] = true; + } + } + if (RailsCnt > 1) + { + if (Neighbors[3] && Neighbors[0]) return E_META_RAIL_CURVED_ZP_XP; + else if (Neighbors[3] && Neighbors[1]) return E_META_RAIL_CURVED_ZP_XM; + else if (Neighbors[2] && Neighbors[0]) return E_META_RAIL_CURVED_ZM_XP; + else if (Neighbors[2] && Neighbors[1]) return E_META_RAIL_CURVED_ZM_XM; + else if (Neighbors[7] && Neighbors[2]) return E_META_RAIL_ASCEND_ZP; + else if (Neighbors[3] && Neighbors[6]) return E_META_RAIL_ASCEND_ZM; + else if (Neighbors[5] && Neighbors[0]) return E_META_RAIL_ASCEND_XM; + else if (Neighbors[4] && Neighbors[1]) return E_META_RAIL_ASCEND_XP; + else if (Neighbors[0] && Neighbors[1]) return E_META_RAIL_XM_XP; + else if (Neighbors[2] && Neighbors[3]) return E_META_RAIL_ZM_ZP; + ASSERT(!"Weird neighbor count"); + } + return Meta; + } + + + bool IsUnstable(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) + { + return false; + } + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + switch (Meta) + { + case E_META_RAIL_ZM_ZP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN) + ) + { + return true; + } + break; + } + + case E_META_RAIL_XM_XP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN) + ) + { + return true; + } + break; + } + + case E_META_RAIL_ASCEND_XP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_ASCEND_XM: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) || + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_ASCEND_ZM: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) + ) + { + return true; + } + break; + } + + case E_META_RAIL_ASCEND_ZP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH) + ) + { + return true; + } + break; + } + + case E_META_RAIL_CURVED_ZP_XP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_CURVED_ZP_XM: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_CURVED_ZM_XM: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_CURVED_ZM_XP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) + ) + { + return true; + } + break; + } + } + return false; + } + + + bool IsNotConnected(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Pure = 0) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); + NIBBLETYPE Meta; + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) + { + if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN)) + { + if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE)) + { + return true; + } + else + { + Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ); + } + } + else + { + Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ); + } + } + else + { + Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + } + + switch (a_BlockFace) + { + case BLOCK_FACE_NORTH: + { + if ( + (Meta == E_META_RAIL_ZM_ZP) || + (Meta == E_META_RAIL_ASCEND_ZM) || + (Meta == E_META_RAIL_ASCEND_ZP) || + (Meta == E_META_RAIL_CURVED_ZP_XP) || + (Meta == E_META_RAIL_CURVED_ZP_XM) + ) + { + return false; + } + break; + } + + case BLOCK_FACE_SOUTH: + { + if ( + (Meta == E_META_RAIL_ZM_ZP) || + (Meta == E_META_RAIL_ASCEND_ZM) || + (Meta == E_META_RAIL_ASCEND_ZP) || + (Meta == E_META_RAIL_CURVED_ZM_XP) || + (Meta == E_META_RAIL_CURVED_ZM_XM) + ) + { + return false; + } + break; + } + + case BLOCK_FACE_EAST: + { + if ( + (Meta == E_META_RAIL_XM_XP) || + (Meta == E_META_RAIL_ASCEND_XP) || + (Meta == E_META_RAIL_ASCEND_XM) || + (Meta == E_META_RAIL_CURVED_ZP_XM) || + (Meta == E_META_RAIL_CURVED_ZM_XM) + ) + { + return false; + } + break; + } + case BLOCK_FACE_WEST: + { + if ( + (Meta == E_META_RAIL_XM_XP) || + (Meta == E_META_RAIL_ASCEND_XP) || + (Meta == E_META_RAIL_ASCEND_XM) || + (Meta == E_META_RAIL_CURVED_ZP_XP) || + (Meta == E_META_RAIL_CURVED_ZM_XP) + ) + { + return false; + } + break; + } + } + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockRedstone.cpp b/src/Blocks/BlockRedstone.cpp new file mode 100644 index 000000000..35cdc34cf --- /dev/null +++ b/src/Blocks/BlockRedstone.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" +#include "BlockRedstone.h" +#include "../Item.h" +#include "../World.h" + + + + + +cBlockRedstoneHandler::cBlockRedstoneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockRedstoneHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Nothing needed yet +} + + + + diff --git a/src/Blocks/BlockRedstone.h b/src/Blocks/BlockRedstone.h new file mode 100644 index 000000000..f28f3f2d6 --- /dev/null +++ b/src/Blocks/BlockRedstone.h @@ -0,0 +1,35 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockRedstoneHandler : + public cBlockHandler +{ +public: + cBlockRedstoneHandler(BLOCKTYPE a_BlockType); + + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]); + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, 1)); + } +} ; + + + + diff --git a/src/Blocks/BlockRedstoneRepeater.cpp b/src/Blocks/BlockRedstoneRepeater.cpp new file mode 100644 index 000000000..72ea21012 --- /dev/null +++ b/src/Blocks/BlockRedstoneRepeater.cpp @@ -0,0 +1,51 @@ + +#include "Globals.h" +#include "BlockRedstoneRepeater.h" +#include "../Simulator/RedstoneSimulator.h" +#include "../Entities/Player.h" + + + + + +cBlockRedstoneRepeaterHandler::cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockRedstoneRepeaterHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Nothing needed yet +} + + + + + +void cBlockRedstoneRepeaterHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f)); +} + + + + +bool cBlockRedstoneRepeaterHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + a_BlockType = m_BlockType; + a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; +} + + + + diff --git a/src/Blocks/BlockRedstoneRepeater.h b/src/Blocks/BlockRedstoneRepeater.h new file mode 100644 index 000000000..958841a34 --- /dev/null +++ b/src/Blocks/BlockRedstoneRepeater.h @@ -0,0 +1,55 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockRedstoneRepeaterHandler : + public cBlockHandler +{ +public: + cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType); + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_ITEM_REDSTONE_REPEATER, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override; + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockRedstoneTorch.h b/src/Blocks/BlockRedstoneTorch.h new file mode 100644 index 000000000..cb897ba3f --- /dev/null +++ b/src/Blocks/BlockRedstoneTorch.h @@ -0,0 +1,36 @@ + +#pragma once + +#include "BlockRedstone.h" +#include "BlockTorch.h" + + + + + +class cBlockRedstoneTorchHandler : + public cBlockTorchHandler +{ +public: + cBlockRedstoneTorchHandler(BLOCKTYPE a_BlockType) + : cBlockTorchHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Always drop the ON torch, meta 0 + a_Pickups.push_back(cItem(E_BLOCK_REDSTONE_TORCH_ON, 1, 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockSand.h b/src/Blocks/BlockSand.h new file mode 100644 index 000000000..3fc271483 --- /dev/null +++ b/src/Blocks/BlockSand.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSandHandler : + public cBlockHandler +{ +public: + cBlockSandHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual const char * GetStepSound(void) override + { + return "step.sand"; + } + +}; + + + + diff --git a/src/Blocks/BlockSapling.h b/src/Blocks/BlockSapling.h new file mode 100644 index 000000000..fff2fa88b --- /dev/null +++ b/src/Blocks/BlockSapling.h @@ -0,0 +1,57 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockSaplingHandler : + public cBlockHandler +{ +public: + cBlockSaplingHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Only the first 2 bits contain the display information, the others are for growing + a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if ((Meta & 0x08) != 0) + { + a_World->GrowTree(a_BlockX, a_BlockY, a_BlockZ); + } + else + { + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x08); + } + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockSign.h b/src/Blocks/BlockSign.h new file mode 100644 index 000000000..7fbe61893 --- /dev/null +++ b/src/Blocks/BlockSign.h @@ -0,0 +1,78 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cBlockSignHandler : + public cBlockHandler +{ +public: + cBlockSignHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_SIGN, 1, 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + static char RotationToMetaData(double a_Rotation) + { + a_Rotation += 180 + (180 / 16); // So it's not aligned with axis + if (a_Rotation > 360) + { + a_Rotation -= 360; + } + + a_Rotation = (a_Rotation / 360) * 16; + + return ((char)a_Rotation) % 16; + } + + + static char DirectionToMetaData(char a_Direction) + { + switch (a_Direction) + { + case 0x2: return 0x2; + case 0x3: return 0x3; + case 0x4: return 0x4; + case 0x5: return 0x5; + default: + { + break; + } + } + return 0x2; + } + + + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ) override + { + a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); + } +} ; + + + + diff --git a/src/Blocks/BlockSlab.h b/src/Blocks/BlockSlab.h new file mode 100644 index 000000000..7c1251b28 --- /dev/null +++ b/src/Blocks/BlockSlab.h @@ -0,0 +1,182 @@ + +// BlockSlab.h + +// Declares cBlockSlabHandler and cBlockDoubleSlabHandler classes + + + + + +#pragma once + +#include "BlockHandler.h" +#include "../Items/ItemHandler.h" + + + + + +class cBlockSlabHandler : + public cBlockHandler +{ +public: + cBlockSlabHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + BLOCKTYPE Type = (BLOCKTYPE) (a_Player->GetEquippedItem().m_ItemType); + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x07); + + // HandlePlaceBlock wants a cItemHandler pointer thing, so let's give it one + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(GetDoubleSlabType(Type)); + + // Check if the block at the coordinates is a slab. Eligibility for combining has already been processed in ClientHandle + if (IsAnySlabType(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))) + { + // Call the function in ClientHandle that places a block when the client sends the packet, + // so that plugins may interfere with the placement. + + if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM)) + { + // Top and bottom faces need no parameter modification + a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + } + else + { + // The other faces need to distinguish between top and bottom cursor positions + if (a_CursorY > 7) + { + // Edit the call to use BLOCK_FACE_BOTTOM, otherwise it places incorrectly + a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_TOP, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + } + else + { + // Edit the call to use BLOCK_FACE_TOP, otherwise it places incorrectly + a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_BOTTOM, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + } + } + return false; // Cancel the event, because dblslabs were already placed, nothing else needed + } + + // Place the single-slab with correct metas: + switch (a_BlockFace) + { + case BLOCK_FACE_TOP: + { + // Bottom half slab block + a_BlockMeta = Meta & 0x7; + break; + } + case BLOCK_FACE_BOTTOM: + { + // Top half slab block + a_BlockMeta = Meta | 0x8; + break; + } + case BLOCK_FACE_EAST: + case BLOCK_FACE_NORTH: + case BLOCK_FACE_SOUTH: + case BLOCK_FACE_WEST: + { + if (a_CursorY > 7) + { + // Cursor at top half of block, place top slab + a_BlockMeta = Meta | 0x8; break; + } + else + { + // Cursor at bottom half of block, place bottom slab + a_BlockMeta = Meta & 0x7; break; + } + } + } // switch (a_BlockFace) + return true; + } + + + virtual const char * GetStepSound(void) override + { + switch (m_BlockType) + { + case E_BLOCK_WOODEN_SLAB: return "step.wood"; + case E_BLOCK_STONE_SLAB: return "step.stone"; + } + ASSERT(!"Unhandled slab type!"); + return ""; + } + + + /// Returns true if the specified blocktype is one of the slabs handled by this handler + static bool IsAnySlabType(BLOCKTYPE a_BlockType) + { + return ((a_BlockType == E_BLOCK_WOODEN_SLAB) || (a_BlockType == E_BLOCK_STONE_SLAB)); + } + + + /// Converts the single-slab blocktype to its equivalent double-slab blocktype + static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType) + { + switch (a_SingleSlabBlockType) + { + case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB; + case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB; + } + ASSERT(!"Unhandled slab type!"); + return E_BLOCK_AIR; + } + +} ; + + + + + +class cBlockDoubleSlabHandler : + public cBlockHandler +{ +public: + cBlockDoubleSlabHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + if (m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB) + { + m_BlockType = E_BLOCK_STONE_SLAB; + } + else + { + m_BlockType = E_BLOCK_WOODEN_SLAB; + } + a_Pickups.push_back(cItem(m_BlockType, 2, a_BlockMeta)); + } + + + virtual const char * GetStepSound(void) override + { + return ((m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone"; + } +} ; + + + + diff --git a/src/Blocks/BlockSnow.h b/src/Blocks/BlockSnow.h new file mode 100644 index 000000000..b8d48362c --- /dev/null +++ b/src/Blocks/BlockSnow.h @@ -0,0 +1,72 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSnowHandler : + public cBlockHandler +{ +public: + cBlockSnowHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = a_World->GetBlockMeta(Vector3i(a_BlockX, a_BlockY, a_BlockZ)); + + if ((Meta < 7) && (Meta != 0)) // Is height at maximum (7) or at mininum (0)? Don't do anything if so + { + Meta++; + } + + a_BlockMeta = Meta; + return true; + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_SNOWBALL, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && g_BlockIsSnowable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]; + } + + + virtual bool DoesDropOnUnsuitable(void) override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } +} ; + + + + diff --git a/src/Blocks/BlockStairs.h b/src/Blocks/BlockStairs.h new file mode 100644 index 000000000..8d259eee3 --- /dev/null +++ b/src/Blocks/BlockStairs.h @@ -0,0 +1,152 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockStairsHandler : + public cBlockHandler +{ +public: + cBlockStairsHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = RotationToMetaData(a_Player->GetRotation()); + switch (a_BlockFace) + { + case BLOCK_FACE_TOP: break; + case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block + case BLOCK_FACE_EAST: + case BLOCK_FACE_NORTH: + case BLOCK_FACE_SOUTH: + case BLOCK_FACE_WEST: + { + // When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block + if (a_CursorY > 8) + { + a_BlockMeta |= 0x4; + } + break; + } + } + return true; + } + + // TODO: step sound + + + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 90 + 45; // So its not aligned with axis + if (a_Rotation > 360) + { + a_Rotation -= 360; + } + if ((a_Rotation >= 0) && (a_Rotation < 90)) + { + return 0x0; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x1; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x3; + } + } + + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x03; // East -> North + case 0x01: return TopBits | 0x02; // West -> South + case 0x02: return TopBits | 0x00; // South -> East + case 0x03: return TopBits | 0x01; // North -> West + } + // Not reachable, but to avoid a compiler warning: + return 0; + } + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x02; // East -> South + case 0x01: return TopBits | 0x03; // West -> North + case 0x02: return TopBits | 0x01; // South -> West + case 0x03: return TopBits | 0x00; // North -> East + } + // Not reachable, but to avoid a compiler warning: + return 0; + } + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x00; // East -> East + case 0x01: return TopBits | 0x01; // West -> West + case 0x02: return TopBits | 0x03; // South -> North + case 0x03: return TopBits | 0x02; // North -> South + } + // Not reachable, but to avoid a compiler warning: + return 0; + } + + + virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override + { + // Toggle bit 3: + return (a_Meta & 0x0b) | ((~a_Meta) & 0x04); + } + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x01; // East -> West + case 0x01: return TopBits | 0x00; // West -> East + case 0x02: return TopBits | 0x02; // South -> South + case 0x03: return TopBits | 0x03; // North -> North + } + // Not reachable, but to avoid a compiler warning: + return 0; + } +} ; + + + + diff --git a/src/Blocks/BlockStems.h b/src/Blocks/BlockStems.h new file mode 100644 index 000000000..ce02d9cb8 --- /dev/null +++ b/src/Blocks/BlockStems.h @@ -0,0 +1,58 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +class cBlockStemsHandler : + public cBlockHandler +{ +public: + cBlockStemsHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + int ItemType = (m_BlockType == E_BLOCK_MELON_STEM) ? E_ITEM_MELON_SEEDS : E_ITEM_PUMPKIN_SEEDS; + a_Pickups.push_back(cItem(ItemType, 1, 0)); + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (Meta >= 7) + { + // Grow the produce: + a_World->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, m_BlockType); + } + else + { + // Grow the stem: + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta + 1); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockStone.h b/src/Blocks/BlockStone.h new file mode 100644 index 000000000..af4c6509a --- /dev/null +++ b/src/Blocks/BlockStone.h @@ -0,0 +1,29 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +class cBlockStoneHandler : + public cBlockHandler +{ +public: + cBlockStoneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_COBBLESTONE, 1, 0)); + } +} ; + + + + diff --git a/src/Blocks/BlockSugarcane.h b/src/Blocks/BlockSugarcane.h new file mode 100644 index 000000000..28a60df80 --- /dev/null +++ b/src/Blocks/BlockSugarcane.h @@ -0,0 +1,90 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSugarcaneHandler : + public cBlockHandler +{ +public: + cBlockSugarcaneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_SUGARCANE, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + case E_BLOCK_SAND: + { + static const struct + { + int x, z; + } Coords[] = + { + {-1, 0}, + { 1, 0}, + { 0, -1}, + { 0, 1}, + } ; + a_RelY -= 1; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) + { + // Too close to the edge, cannot simulate + return true; + } + if (IsBlockWater(BlockType)) + { + return true; + } + } // for i - Coords[] + // Not directly neighboring a water block + return false; + } + case E_BLOCK_SUGARCANE: + { + return true; + } + } + return false; + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + a_World->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockTallGrass.h b/src/Blocks/BlockTallGrass.h new file mode 100644 index 000000000..cd27ab7e6 --- /dev/null +++ b/src/Blocks/BlockTallGrass.h @@ -0,0 +1,51 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockTallGrassHandler : + public cBlockHandler +{ +public: + cBlockTallGrassHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Drop seeds, sometimes + MTRand r1; + if (r1.randInt(10) == 5) + { + a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h new file mode 100644 index 000000000..36383a524 --- /dev/null +++ b/src/Blocks/BlockTorch.h @@ -0,0 +1,277 @@ +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockTorchHandler : + public cBlockHandler +{ +public: + cBlockTorchHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // Find proper placement of torch + + if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM)) + { + a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); // Top or bottom faces clicked, find a suitable face + if (a_BlockFace == BLOCK_FACE_NONE) + { + // Client wouldn't have sent anything anyway, but whatever + return false; + } + } + else + { + // Not top or bottom faces, try to preserve whatever face was clicked + if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + // Torch couldn't be placed on whatever face was clicked, last ditch resort - find another face + a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); + if (a_BlockFace == BLOCK_FACE_NONE) + { + return false; + } + } + } + + a_BlockType = m_BlockType; + a_BlockMeta = DirectionToMetaData(a_BlockFace); + return true; + } + + + static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export + { // tolua_export + switch (a_Direction) + { + case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0; + case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR; + case BLOCK_FACE_EAST: return E_META_TORCH_EAST; + case BLOCK_FACE_WEST: return E_META_TORCH_WEST; + case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH; + case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH; + default: + { + ASSERT(!"Unhandled torch direction!"); + break; + } + }; + return 0x0; + } // tolua_export + + + static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export + { // tolua_export + switch (a_MetaData) + { + case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground + case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP; + case E_META_TORCH_EAST: return BLOCK_FACE_EAST; + case E_META_TORCH_WEST: return BLOCK_FACE_WEST; + case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH; + case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH; + default: + { + ASSERT(!"Unhandled torch metadata"); + break; + } + } + return 0; + } // tolua_export + + + static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos) + { + switch (a_TorchMeta) + { + case 0x0: + case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0))); + case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1))); + case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1))); + case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))); + case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0))); + default: + { + ASSERT(!"Unhandled torch meta!"); + break; + } + } + return false; + } + + + static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_BlockFace) + { + if ( !g_BlockIsTorchPlaceable[a_BlockType] ) + { + return (a_BlockFace == BLOCK_FACE_TOP); // Allow placement only when torch upright + } + else + { + return true; + } + } + + + static bool TorchCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); + return CanBePlacedOn(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), a_BlockFace); + } + + + /// Finds a suitable face to place the torch, returning BLOCK_FACE_NONE on failure + static char FindSuitableFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + for (int i = 0; i <= 5; i++) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, true); + BLOCKTYPE BlockInQuestion = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + + if ( + ((BlockInQuestion == E_BLOCK_GLASS) || + (BlockInQuestion == E_BLOCK_FENCE) || + (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) || + (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL)) && + (i == BLOCK_FACE_TOP) + ) + { + return i; + } + else if ((g_BlockIsTorchPlaceable[BlockInQuestion]) && (i != BLOCK_FACE_BOTTOM)) + { + return i; + } + else + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, false); + } + } + return BLOCK_FACE_NONE; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + char Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + + AddFaceDirection(a_RelX, a_RelY, a_RelZ, Face, true); + BLOCKTYPE BlockInQuestion; + a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockInQuestion); + + if ( + (BlockInQuestion == E_BLOCK_GLASS) || + (BlockInQuestion == E_BLOCK_FENCE) || + (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) || + (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL) + ) + { + // Torches can be placed on tops of glass and fences, despite them being 'untorcheable' + // No need to check for upright orientation, it was done when the torch was placed + return true; + } + else if ( !g_BlockIsTorchPlaceable[BlockInQuestion] ) + { + return false; + } + else + { + return true; + } + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Always drop meta = 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x01: return TopBits | 0x04; // East -> North + case 0x02: return TopBits | 0x03; // West -> South + case 0x03: return TopBits | 0x01; // South -> East + case 0x04: return TopBits | 0x02; // North -> West + default: return a_Meta; // Floor -> Floor + } + } + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x01: return TopBits | 0x03; // East -> South + case 0x02: return TopBits | 0x04; // West -> North + case 0x03: return TopBits | 0x02; // South -> West + case 0x04: return TopBits | 0x01; // North -> East + default: return a_Meta; // Floor -> Floor + } + } + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x03: return TopBits | 0x04; // South -> North + case 0x04: return TopBits | 0x03; // North -> South + default: return a_Meta; // Keep the rest + } + } + + + // Mirroring around the XZ plane doesn't make sense for floor torches, + // the others stay the same, so let's keep all the metas the same. + // The base class does tht for us, no need to override MetaMirrorXZ() + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x01: return TopBits | 0x02; // East -> West + case 0x02: return TopBits | 0x01; // West -> East + default: return a_Meta; // Keep the rest + } + } +} ; + + + + diff --git a/src/Blocks/BlockVine.h b/src/Blocks/BlockVine.h new file mode 100644 index 000000000..2c9f67cab --- /dev/null +++ b/src/Blocks/BlockVine.h @@ -0,0 +1,201 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockVineHandler : + public cBlockHandler +{ +public: + cBlockVineHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // TODO: Disallow placement where the vine doesn't attach to something properly + BLOCKTYPE BlockType = 0; + NIBBLETYPE BlockMeta; + a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + if (BlockType == m_BlockType) + { + a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace); + } + else + { + a_BlockMeta = DirectionToMetaData(a_BlockFace); + } + a_BlockType = m_BlockType; + return true; + } + + + static NIBBLETYPE DirectionToMetaData(char a_BlockFace) + { + switch (a_BlockFace) + { + case BLOCK_FACE_NORTH: return 0x1; + case BLOCK_FACE_SOUTH: return 0x4; + case BLOCK_FACE_WEST: return 0x8; + case BLOCK_FACE_EAST: return 0x2; + default: return 0x0; + } + } + + + static char MetaDataToDirection(NIBBLETYPE a_MetaData) + { + switch(a_MetaData) + { + case 0x1: return BLOCK_FACE_NORTH; + case 0x4: return BLOCK_FACE_SOUTH; + case 0x8: return BLOCK_FACE_WEST; + case 0x2: return BLOCK_FACE_EAST; + default: return BLOCK_FACE_TOP; + } + } + + + /// Returns true if the specified block type is good for vines to attach to + static bool IsBlockAttachable(BLOCKTYPE a_BlockType) + { + return (a_BlockType == E_BLOCK_LEAVES) || g_BlockIsSolid[a_BlockType]; + } + + + /// Returns the meta that has the maximum allowable sides of the vine, given the surroundings + NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) + { + static const struct + { + int x, z; + int Bit; + } Coords[] = + { + { 0, 1, 1}, // south, ZP + {-1, 0, 2}, // west, XM + { 0, -1, 4}, // north, ZM + { 1, 0, 8}, // east, XP + } ; + int res = 0; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if ( + a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && + IsBlockAttachable(BlockType) + ) + { + res |= Coords[i].Bit; + } + } + return res; + } + + + void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override + { + NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelX, a_RelY, a_RelZ); + + // Check if vine above us, add its meta to MaxMeta + if ((a_RelY < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == m_BlockType)) + { + MaxMeta |= a_Chunk.GetMeta(a_RelX, a_RelY + 1, a_RelZ); + } + + NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal + if (Common != CurMeta) + { + // There is a neighbor missing, need to update the meta or even destroy the block + bool HasTop = (a_RelY < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ)); + if ((Common == 0) && !HasTop) + { + // The vine just lost all its support, destroy the block: + if (DoesDropOnUnsuitable()) + { + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); + } + a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + return; + } + a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Common); + } + else + { + // Wake up the simulators for this block: + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); + } + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } + + + virtual bool DoesDropOnUnsuitable(void) override + { + return false; + } + + virtual void OnUpdate(cWorld * a_World, int X, int Y, int Z) + { + if (a_World->GetBlock(X, Y - 1, Z) == E_BLOCK_AIR) + { + a_World->SetBlock(X, Y - 1, Z, E_BLOCK_VINES, a_World->GetBlockMeta(X, Y, Z)); + } + } + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + return ((a_Meta >> 1) | (a_Meta << 3)) & 0x0f; // Rotate bits to the right + } + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left + } + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + // Bits 2 and 4 stay, bits 1 and 3 swap + return ((a_Meta & 0x0a) | ((a_Meta & 0x01) << 2) | ((a_Meta & 0x04) >> 2)); + } + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + // Bits 1 and 3 stay, bits 2 and 4 swap + return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2)); + } +} ; + + + + diff --git a/src/Blocks/BlockWood.h b/src/Blocks/BlockWood.h new file mode 100644 index 000000000..cb5ee995a --- /dev/null +++ b/src/Blocks/BlockWood.h @@ -0,0 +1,72 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockWoodHandler : public cBlockHandler +{ +public: + cBlockWoodHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); + a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta); + return true; + } + + + inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace, NIBBLETYPE a_WoodMeta) + { + switch (a_BlockFace) + { + case BLOCK_FACE_YM: + case BLOCK_FACE_YP: + { + return a_WoodMeta; // Top or bottom, just return original + } + + case BLOCK_FACE_ZP: + case BLOCK_FACE_ZM: + { + return a_WoodMeta | 0x8; // North or south + } + + case BLOCK_FACE_XP: + case BLOCK_FACE_XM: + { + return a_WoodMeta | 0x4; // East or west + } + + default: + { + ASSERT(!"Unhandled block face!"); + return a_WoodMeta | 0xC; // No idea, give a special meta (all sides bark) + } + } + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockWorkbench.h b/src/Blocks/BlockWorkbench.h new file mode 100644 index 000000000..a2cc6119c --- /dev/null +++ b/src/Blocks/BlockWorkbench.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../UI/Window.h" +#include "../Entities/Player.h" + + + + + +class cBlockWorkbenchHandler: + public cBlockHandler +{ +public: + cBlockWorkbenchHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override + { + cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ); + a_Player->OpenWindow(Window); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/BoundingBox.cpp b/src/BoundingBox.cpp new file mode 100644 index 000000000..02602992e --- /dev/null +++ b/src/BoundingBox.cpp @@ -0,0 +1,331 @@ + +// BoundingBox.cpp + +// Implements the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords + +#include "Globals.h" +#include "BoundingBox.h" +#include "Defines.h" + + + + + +#if 0 + +/// A simple self-test that is executed on program start, used to verify bbox functionality +class SelfTest +{ +public: + SelfTest(void) + { + Vector3d Min(1, 1, 1); + Vector3d Max(2, 2, 2); + Vector3d LineDefs[] = + { + Vector3d(1.5, 4, 1.5), Vector3d(1.5, 3, 1.5), // Should intersect at 2, face 1 (YP) + Vector3d(1.5, 0, 1.5), Vector3d(1.5, 4, 1.5), // Should intersect at 0.25, face 0 (YM) + Vector3d(0, 0, 0), Vector3d(2, 2, 2), // Should intersect at 0.5, face 0, 3 or 5 (anyM) + Vector3d(0.999, 0, 1.5), Vector3d(0.999, 4, 1.5), // Should not intersect + Vector3d(1.999, 0, 1.5), Vector3d(1.999, 4, 1.5), // Should intersect at 0.25, face 0 (YM) + Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5), // Should not intersect + } ; + for (int i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++) + { + double LineCoeff; + char Face; + Vector3d Line1 = LineDefs[2 * i]; + Vector3d Line2 = LineDefs[2 * i + 1]; + bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face); + printf("LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n", + Line1.x, Line1.y, Line1.z, + Line2.x, Line2.y, Line2.z, + res ? 1 : 0, LineCoeff, Face + ); + } // for i - LineDefs[] + printf("BoundingBox selftest complete."); + } +} Test; + +#endif + + + + + +cBoundingBox::cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ) : + m_Min(a_MinX, a_MinY, a_MinZ), + m_Max(a_MaxX, a_MaxY, a_MaxZ) +{ +} + + + + + +cBoundingBox::cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max) : + m_Min(a_Min), + m_Max(a_Max) +{ +} + + + + + +cBoundingBox::cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height) : + m_Min(a_Pos.x - a_Radius, a_Pos.y, a_Pos.z - a_Radius), + m_Max(a_Pos.x + a_Radius, a_Pos.y + a_Height, a_Pos.z + a_Radius) +{ +} + + + + + +cBoundingBox::cBoundingBox(const cBoundingBox & a_Orig) : + m_Min(a_Orig.m_Min), + m_Max(a_Orig.m_Max) +{ +} + + + + + +void cBoundingBox::Move(double a_OffX, double a_OffY, double a_OffZ) +{ + m_Min.x += a_OffX; + m_Min.y += a_OffY; + m_Min.z += a_OffZ; + m_Max.x += a_OffX; + m_Max.y += a_OffY; + m_Max.z += a_OffZ; +} + + + + + +void cBoundingBox::Move(const Vector3d & a_Off) +{ + m_Min.x += a_Off.x; + m_Min.y += a_Off.y; + m_Min.z += a_Off.z; + m_Max.x += a_Off.x; + m_Max.y += a_Off.y; + m_Max.z += a_Off.z; +} + + + + + +void cBoundingBox::Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ) +{ + m_Min.x -= a_ExpandX; + m_Min.y -= a_ExpandY; + m_Min.z -= a_ExpandZ; + m_Max.x += a_ExpandX; + m_Max.y += a_ExpandY; + m_Max.z += a_ExpandZ; +} + + + + + +bool cBoundingBox::DoesIntersect(const cBoundingBox & a_Other) +{ + return ( + ((a_Other.m_Min.x <= m_Max.x) && (a_Other.m_Max.x >= m_Min.x)) && // X coords intersect + ((a_Other.m_Min.y <= m_Max.y) && (a_Other.m_Max.y >= m_Min.y)) && // Y coords intersect + ((a_Other.m_Min.z <= m_Max.z) && (a_Other.m_Max.z >= m_Min.z)) // Z coords intersect + ); +} + + + + + +cBoundingBox cBoundingBox::Union(const cBoundingBox & a_Other) +{ + return cBoundingBox( + std::min(m_Min.x, a_Other.m_Min.x), + std::min(m_Min.y, a_Other.m_Min.y), + std::min(m_Min.z, a_Other.m_Min.z), + std::max(m_Max.x, a_Other.m_Max.x), + std::max(m_Max.y, a_Other.m_Max.y), + std::max(m_Max.z, a_Other.m_Max.z) + ); +} + + + + + +bool cBoundingBox::IsInside(const Vector3d & a_Point) +{ + return IsInside(m_Min, m_Max, a_Point); +} + + + + + +bool cBoundingBox::IsInside(double a_X, double a_Y,double a_Z) +{ + return IsInside(m_Min, m_Max, a_X, a_Y, a_Z); +} + + + + + +bool cBoundingBox::IsInside(cBoundingBox & a_Other) +{ + // If both a_Other's coords are inside this, then the entire a_Other is inside + return (IsInside(a_Other.m_Min) && IsInside(a_Other.m_Max)); +} + + + + + +bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max) +{ + // If both coords are inside this, then the entire a_Other is inside + return (IsInside(a_Min) && IsInside(a_Max)); +} + + + + + +bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point) +{ + return ( + ((a_Point.x >= a_Min.x) && (a_Point.x <= a_Max.x)) && + ((a_Point.y >= a_Min.y) && (a_Point.y <= a_Max.y)) && + ((a_Point.z >= a_Min.z) && (a_Point.z <= a_Max.z)) + ); +} + + + + + +bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z) +{ + return ( + ((a_X >= a_Min.x) && (a_X <= a_Max.x)) && + ((a_Y >= a_Min.y) && (a_Y <= a_Max.y)) && + ((a_Z >= a_Min.z) && (a_Z <= a_Max.z)) + ); +} + + + + + +bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face) +{ + return CalcLineIntersection(m_Min, m_Max, a_Line1, a_Line2, a_LineCoeff, a_Face); +} + + + + + +bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face) +{ + if (IsInside(a_Min, a_Max, a_Line1)) + { + // The starting point is inside the bounding box. + a_LineCoeff = 0; + a_Face = BLOCK_FACE_NONE; // No faces hit + return true; + } + + char Face = BLOCK_FACE_NONE; + double Coeff = Vector3d::NO_INTERSECTION; + + // Check each individual bbox face for intersection with the line, remember the one with the lowest coeff + double c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Min.z); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM; + Coeff = c; + } + c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Max.z); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM; + Coeff = c; + } + c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Min.y); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM; + Coeff = c; + } + c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y); + if ((c >= 0) && (c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM; + Coeff = c; + } + c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Min.x); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM; + Coeff = c; + } + c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Max.x); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM; + Coeff = c; + } + + if (Coeff >= Vector3d::NO_INTERSECTION) + { + // There has been no intersection + return false; + } + + a_LineCoeff = Coeff; + a_Face = Face; + return true; +} + + + + + +bool cBoundingBox::Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection) +{ + a_Intersection.m_Min.x = std::max(m_Min.x, a_Other.m_Min.x); + a_Intersection.m_Max.x = std::min(m_Max.x, a_Other.m_Max.x); + if (a_Intersection.m_Min.x >= a_Intersection.m_Max.x) + { + return false; + } + a_Intersection.m_Min.y = std::max(m_Min.y, a_Other.m_Min.y); + a_Intersection.m_Max.y = std::min(m_Max.y, a_Other.m_Max.y); + if (a_Intersection.m_Min.y >= a_Intersection.m_Max.y) + { + return false; + } + a_Intersection.m_Min.z = std::max(m_Min.z, a_Other.m_Min.z); + a_Intersection.m_Max.z = std::min(m_Max.z, a_Other.m_Max.z); + if (a_Intersection.m_Min.z >= a_Intersection.m_Max.z) + { + return false; + } + return true; +} + + + + diff --git a/src/BoundingBox.h b/src/BoundingBox.h new file mode 100644 index 000000000..ff9963989 --- /dev/null +++ b/src/BoundingBox.h @@ -0,0 +1,90 @@ + +// BoundingBox.h + +// Declares the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords + + + + +#pragma once + +#include "Vector3d.h" + + + + + +// tolua_begin + +/** Represents two sets of coords, minimum and maximum for each direction. +All the coords within those limits (inclusive the edges) are considered "inside" the box. +For intersection purposes, though, if the intersection is "sharp" in any coord (i. e. zero volume), +the boxes are considered non-intersecting. +*/ +class cBoundingBox +{ +public: + cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ); + cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max); + cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height); + cBoundingBox(const cBoundingBox & a_Orig); + + /// Moves the entire boundingbox by the specified offset + void Move(double a_OffX, double a_OffY, double a_OffZ); + + /// Moves the entire boundingbox by the specified offset + void Move(const Vector3d & a_Off); + + /// Expands the bounding box by the specified amount in each direction (so the box becomes larger by 2 * Expand in each direction) + void Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ); + + /// Returns true if the two bounding boxes intersect + bool DoesIntersect(const cBoundingBox & a_Other); + + /// Returns the union of the two bounding boxes + cBoundingBox Union(const cBoundingBox & a_Other); + + /// Returns true if the point is inside the bounding box + bool IsInside(const Vector3d & a_Point); + + /// Returns true if the point is inside the bounding box + bool IsInside(double a_X, double a_Y,double a_Z); + + /// Returns true if a_Other is inside this bounding box + bool IsInside(cBoundingBox & a_Other); + + /// Returns true if a boundingbox specified by a_Min and a_Max is inside this bounding box + bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max); + + /// Returns true if the specified point is inside the bounding box specified by its min/max corners + static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point); + + /// Returns true if the specified point is inside the bounding box specified by its min/max corners + static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z); + + /** Returns true if this bounding box is intersected by the line specified by its two points + Also calculates the distance along the line in which the intersection occurs (0 .. 1) + Only forward collisions (a_LineCoeff >= 0) are returned. + */ + bool CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face); + + /** Returns true if the specified bounding box is intersected by the line specified by its two points + Also calculates the distance along the line in which the intersection occurs (0 .. 1) and the face hit (BLOCK_FACE_ constants) + Only forward collisions (a_LineCoeff >= 0) are returned. + */ + static bool CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face); + + // tolua_end + + /// Calculates the intersection of the two bounding boxes; returns true if nonempty + bool Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection); + +protected: + Vector3d m_Min; + Vector3d m_Max; + +} ; // tolua_export + + + + diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp new file mode 100644 index 000000000..1cdd2f430 --- /dev/null +++ b/src/ByteBuffer.cpp @@ -0,0 +1,787 @@ + +// ByteBuffer.cpp + +// Implements the cByteBuffer class representing a ringbuffer of bytes + +#include "Globals.h" + +#include "ByteBuffer.h" +#include "Endianness.h" +#include "OSSupport/IsThread.h" + + + + + +// If a string sent over the protocol is larger than this, a warning is emitted to the console +#define MAX_STRING_SIZE (512 KiB) + +#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false; // Check if at least Num bytes can be read from the buffer, return false if not +#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false; // Check if at least Num bytes can be written to the buffer, return false if not + + + + + +#if 0 + +/// Self-test of the VarInt-reading and writing code +class cByteBufferSelfTest +{ +public: + cByteBufferSelfTest(void) + { + TestRead(); + TestWrite(); + } + + void TestRead(void) + { + cByteBuffer buf(50); + buf.Write("\x05\xac\x02\x00", 4); + UInt32 v1; + ASSERT(buf.ReadVarInt(v1) && (v1 == 5)); + UInt32 v2; + ASSERT(buf.ReadVarInt(v2) && (v2 == 300)); + UInt32 v3; + ASSERT(buf.ReadVarInt(v3) && (v3 == 0)); + } + + void TestWrite(void) + { + cByteBuffer buf(50); + buf.WriteVarInt(5); + buf.WriteVarInt(300); + buf.WriteVarInt(0); + AString All; + buf.ReadAll(All); + ASSERT(All.size() == 4); + ASSERT(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0); + } +} g_ByteBufferTest; + +#endif + + + + + +#ifdef _DEBUG + +/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously +class cSingleThreadAccessChecker +{ +public: + cSingleThreadAccessChecker(unsigned long * a_ThreadID) : + m_ThreadID(a_ThreadID) + { + ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID())); + } + + ~cSingleThreadAccessChecker() + { + *m_ThreadID = 0; + } + +protected: + unsigned long * m_ThreadID; +} ; + +#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast(&m_ThreadID)) + +#else + #define CHECK_THREAD +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cByteBuffer: + +cByteBuffer::cByteBuffer(int a_BufferSize) : + m_Buffer(new char[a_BufferSize + 1]), + m_BufferSize(a_BufferSize + 1), + #ifdef _DEBUG + m_ThreadID(0), + #endif // _DEBUG + m_DataStart(0), + m_WritePos(0), + m_ReadPos(0) +{ + // Allocating one byte more than the buffer size requested, so that we can distinguish between + // completely-full and completely-empty states +} + + + + + +cByteBuffer::~cByteBuffer() +{ + CheckValid(); + delete[] m_Buffer; +} + + + + + +bool cByteBuffer::Write(const char * a_Bytes, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + + // Store the current free space for a check after writing: + int CurFreeSpace = GetFreeSpace(); + int CurReadableSpace = GetReadableSpace(); + int WrittenBytes = 0; + + if (GetFreeSpace() < a_Count) + { + return false; + } + int TillEnd = m_BufferSize - m_WritePos; + if (TillEnd <= a_Count) + { + // Need to wrap around the ringbuffer end + if (TillEnd > 0) + { + memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd); + a_Bytes += TillEnd; + a_Count -= TillEnd; + WrittenBytes = TillEnd; + } + m_WritePos = 0; + } + + // We're guaranteed that we'll fit in a single write op + if (a_Count > 0) + { + memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count); + m_WritePos += a_Count; + WrittenBytes += a_Count; + } + + ASSERT(GetFreeSpace() == CurFreeSpace - WrittenBytes); + ASSERT(GetReadableSpace() == CurReadableSpace + WrittenBytes); + return true; +} + + + + + +int cByteBuffer::GetFreeSpace(void) const +{ + CHECK_THREAD; + CheckValid(); + if (m_WritePos >= m_DataStart) + { + // Wrap around the buffer end: + return m_BufferSize - m_WritePos + m_DataStart - 1; + } + // Single free space partition: + return m_DataStart - m_WritePos - 1; +} + + + + + +/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() +int cByteBuffer::GetUsedSpace(void) const +{ + CHECK_THREAD; + CheckValid(); + return m_BufferSize - GetFreeSpace() - 1; +} + + + + + +/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) +int cByteBuffer::GetReadableSpace(void) const +{ + CHECK_THREAD; + CheckValid(); + if (m_ReadPos > m_WritePos) + { + // Wrap around the buffer end: + return m_BufferSize - m_ReadPos + m_WritePos; + } + // Single readable space partition: + return m_WritePos - m_ReadPos ; +} + + + + + +bool cByteBuffer::CanReadBytes(int a_Count) const +{ + CHECK_THREAD; + CheckValid(); + return (a_Count <= GetReadableSpace()); +} + + + + + +bool cByteBuffer::CanWriteBytes(int a_Count) const +{ + CHECK_THREAD; + CheckValid(); + return (a_Count <= GetFreeSpace()); +} + + + + + +bool cByteBuffer::ReadChar(char & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(1); + ReadBuf(&a_Value, 1); + return true; +} + + + + + +bool cByteBuffer::ReadByte(unsigned char & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(1); + ReadBuf(&a_Value, 1); + return true; +} + + + + + +bool cByteBuffer::ReadBEShort(short & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(2); + ReadBuf(&a_Value, 2); + a_Value = ntohs(a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEInt(int & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(4); + ReadBuf(&a_Value, 4); + a_Value = ntohl(a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEInt64(Int64 & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(8); + ReadBuf(&a_Value, 8); + a_Value = NetworkToHostLong8(&a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEFloat(float & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(4); + ReadBuf(&a_Value, 4); + a_Value = NetworkToHostFloat4(&a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEDouble(double & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(8); + ReadBuf(&a_Value, 8); + a_Value = NetworkToHostDouble8(&a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBool(bool & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(1); + char Value = 0; + ReadBuf(&Value, 1); + a_Value = (Value != 0); + return true; +} + + + + + +bool cByteBuffer::ReadBEUTF16String16(AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + short Length; + if (!ReadBEShort(Length)) + { + return false; + } + if (Length < 0) + { + ASSERT(!"Negative string length? Are you sure?"); + return true; + } + return ReadUTF16String(a_Value, Length); +} + + + + + +bool cByteBuffer::ReadVarInt(UInt32 & a_Value) +{ + CHECK_THREAD; + CheckValid(); + UInt32 Value = 0; + int Shift = 0; + unsigned char b = 0; + do + { + NEEDBYTES(1); + ReadBuf(&b, 1); + Value = Value | (((Int64)(b & 0x7f)) << Shift); + Shift += 7; + } while ((b & 0x80) != 0); + a_Value = Value; + return true; +} + + + + + +bool cByteBuffer::ReadVarUTF8String(AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + UInt32 Size = 0; + if (!ReadVarInt(Size)) + { + return false; + } + if (Size > MAX_STRING_SIZE) + { + LOGWARNING("%s: String too large: %llu (%llu KiB)", __FUNCTION__, Size, Size / 1024); + } + return ReadString(a_Value, (int)Size); +} + + + + + +bool cByteBuffer::WriteChar(char a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(1); + return WriteBuf(&a_Value, 1); +} + + + + + +bool cByteBuffer::WriteByte(unsigned char a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(1); + return WriteBuf(&a_Value, 1); +} + + + + + +bool cByteBuffer::WriteBEShort(short a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(2); + short Converted = htons(a_Value); + return WriteBuf(&Converted, 2); +} + + + + + +bool cByteBuffer::WriteBEInt(int a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(4); + int Converted = HostToNetwork4(&a_Value); + return WriteBuf(&Converted, 4); +} + + + + + +bool cByteBuffer::WriteBEInt64(Int64 a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(8); + Int64 Converted = HostToNetwork8(&a_Value); + return WriteBuf(&Converted, 8); +} + + + + + +bool cByteBuffer::WriteBEFloat(float a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(4); + int Converted = HostToNetwork4(&a_Value); + return WriteBuf(&Converted, 4); +} + + + + + +bool cByteBuffer::WriteBEDouble(double a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(8); + Int64 Converted = HostToNetwork8(&a_Value); + return WriteBuf(&Converted, 8); +} + + + + + + +bool cByteBuffer::WriteBool(bool a_Value) +{ + CHECK_THREAD; + CheckValid(); + return WriteChar(a_Value ? 1 : 0); +} + + + + + +bool cByteBuffer::WriteBEUTF16String16(const AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(2); + AString UTF16BE; + UTF8ToRawBEUTF16(a_Value.data(), a_Value.size(), UTF16BE); + WriteBEShort((short)(UTF16BE.size() / 2)); + PUTBYTES(UTF16BE.size()); + WriteBuf(UTF16BE.data(), UTF16BE.size()); + return true; +} + + + + + +bool cByteBuffer::WriteVarInt(UInt32 a_Value) +{ + CHECK_THREAD; + CheckValid(); + + // A 32-bit integer can be encoded by at most 5 bytes: + unsigned char b[5]; + int idx = 0; + do + { + b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00); + a_Value = a_Value >> 7; + idx++; + } while (a_Value > 0); + + return WriteBuf(b, idx); +} + + + + +bool cByteBuffer::WriteVarUTF8String(const AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early. + bool res = WriteVarInt(a_Value.size()); + if (!res) + { + return false; + } + return WriteBuf(a_Value.data(), a_Value.size()); +} + + + + + +bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + NEEDBYTES(a_Count); + char * Dst = (char *)a_Buffer; // So that we can do byte math + int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; + ASSERT(BytesToEndOfBuffer >= 0); // Sanity check + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + if (BytesToEndOfBuffer > 0) + { + memcpy(Dst, m_Buffer + m_ReadPos, BytesToEndOfBuffer); + Dst += BytesToEndOfBuffer; + a_Count -= BytesToEndOfBuffer; + } + m_ReadPos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + memcpy(Dst, m_Buffer + m_ReadPos, a_Count); + m_ReadPos += a_Count; + } + return true; +} + + + + + +bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + PUTBYTES(a_Count); + char * Src = (char *)a_Buffer; // So that we can do byte math + int BytesToEndOfBuffer = m_BufferSize - m_WritePos; + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + memcpy(m_Buffer + m_WritePos, Src, BytesToEndOfBuffer); + Src += BytesToEndOfBuffer; + a_Count -= BytesToEndOfBuffer; + m_WritePos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + memcpy(m_Buffer + m_WritePos, Src, a_Count); + m_WritePos += a_Count; + } + return true; +} + + + + + +bool cByteBuffer::ReadString(AString & a_String, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + NEEDBYTES(a_Count); + a_String.clear(); + a_String.reserve(a_Count); + int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; + ASSERT(BytesToEndOfBuffer >= 0); // Sanity check + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + if (BytesToEndOfBuffer > 0) + { + a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer); + a_Count -= BytesToEndOfBuffer; + } + m_ReadPos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + a_String.append(m_Buffer + m_ReadPos, a_Count); + m_ReadPos += a_Count; + } + return true; +} + + + + + +bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars) +{ + // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String + CHECK_THREAD; + CheckValid(); + ASSERT(a_NumChars >= 0); + AString RawData; + if (!ReadString(RawData, a_NumChars * 2)) + { + return false; + } + RawBEToUTF8((short *)(RawData.data()), a_NumChars, a_String); + return true; +} + + + + + +bool cByteBuffer::SkipRead(int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + if (!CanReadBytes(a_Count)) + { + return false; + } + AdvanceReadPos(a_Count); + return true; +} + + + + + +void cByteBuffer::ReadAll(AString & a_Data) +{ + CHECK_THREAD; + CheckValid(); + ReadString(a_Data, GetReadableSpace()); +} + + + + + +void cByteBuffer::CommitRead(void) +{ + CHECK_THREAD; + CheckValid(); + m_DataStart = m_ReadPos; +} + + + + + +void cByteBuffer::ResetRead(void) +{ + CHECK_THREAD; + CheckValid(); + m_ReadPos = m_DataStart; +} + + + + + +void cByteBuffer::ReadAgain(AString & a_Out) +{ + // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed) + // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party + CHECK_THREAD; + CheckValid(); + int DataStart = m_DataStart; + if (m_ReadPos < m_DataStart) + { + // Across the ringbuffer end, read the first part and adjust next part's start: + a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart); + DataStart = 0; + } + a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart); +} + + + + + +void cByteBuffer::AdvanceReadPos(int a_Count) +{ + CHECK_THREAD; + CheckValid(); + m_ReadPos += a_Count; + if (m_ReadPos > m_BufferSize) + { + m_ReadPos -= m_BufferSize; + } +} + + + + + +void cByteBuffer::CheckValid(void) const +{ + ASSERT(m_ReadPos >= 0); + ASSERT(m_ReadPos < m_BufferSize); + ASSERT(m_WritePos >= 0); + ASSERT(m_WritePos < m_BufferSize); +} + + + + diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h new file mode 100644 index 000000000..21abb0377 --- /dev/null +++ b/src/ByteBuffer.h @@ -0,0 +1,137 @@ + +// ByteStream.h + +// Interfaces to the cByteBuffer class representing a ringbuffer of bytes + + + + + +#pragma once + + + + + +/** An object that can store incoming bytes and lets its clients read the bytes sequentially +The bytes are stored in a ringbuffer of constant size; if more than that size +is requested, the write operation fails. +The bytes stored can be retrieved using various ReadXXX functions; these assume that the needed +number of bytes are present in the buffer (ASSERT; for performance reasons). +The reading doesn't actually remove the bytes, it only moves the internal read ptr. +To remove the bytes, call CommitRead(). +To re-start reading from the beginning, call ResetRead(). +This class doesn't implement thread safety, the clients of this class need to provide +their own synchronization. +*/ +class cByteBuffer +{ +public: + cByteBuffer(int a_BufferSize); + ~cByteBuffer(); + + /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not + bool Write(const char * a_Bytes, int a_Count); + + /// Returns the number of bytes that can be successfully written to the ringbuffer + int GetFreeSpace(void) const; + + /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() + int GetUsedSpace(void) const; + + /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) + int GetReadableSpace(void) const; + + /// Returns true if the specified amount of bytes are available for reading + bool CanReadBytes(int a_Count) const; + + /// Returns true if the specified amount of bytes are available for writing + bool CanWriteBytes(int a_Count) const; + + // Read the specified datatype and advance the read pointer; return true if successfully read: + bool ReadChar (char & a_Value); + bool ReadByte (unsigned char & a_Value); + bool ReadBEShort (short & a_Value); + bool ReadBEInt (int & a_Value); + bool ReadBEInt64 (Int64 & a_Value); + bool ReadBEFloat (float & a_Value); + bool ReadBEDouble (double & a_Value); + bool ReadBool (bool & a_Value); + bool ReadBEUTF16String16(AString & a_Value); // string length as BE short, then string as UTF-16BE + bool ReadVarInt (UInt32 & a_Value); + bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8 + + /// Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...) + template bool ReadVarInt(T & a_Value) + { + UInt32 v; + bool res = ReadVarInt(v); + if (res) + { + a_Value = v; + } + return res; + } + + // Write the specified datatype; return true if successfully written + bool WriteChar (char a_Value); + bool WriteByte (unsigned char a_Value); + bool WriteBEShort (short a_Value); + bool WriteBEInt (int a_Value); + bool WriteBEInt64 (Int64 a_Value); + bool WriteBEFloat (float a_Value); + bool WriteBEDouble (double a_Value); + bool WriteBool (bool a_Value); + bool WriteBEUTF16String16(const AString & a_Value); // string length as BE short, then string as UTF-16BE + bool WriteVarInt (UInt32 a_Value); + bool WriteVarUTF8String (const AString & a_Value); // string length as VarInt, then string as UTF-8 + + /// Reads a_Count bytes into a_Buffer; returns true if successful + bool ReadBuf(void * a_Buffer, int a_Count); + + /// Writes a_Count bytes into a_Buffer; returns true if successful + bool WriteBuf(const void * a_Buffer, int a_Count); + + /// Reads a_Count bytes into a_String; returns true if successful + bool ReadString(AString & a_String, int a_Count); + + /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String + bool ReadUTF16String(AString & a_String, int a_NumChars); + + /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer + bool SkipRead(int a_Count); + + /// Reads all available data into a_Data + void ReadAll(AString & a_Data); + + /// Removes the bytes that have been read from the ringbuffer + void CommitRead(void); + + /// Restarts next reading operation at the start of the ringbuffer + void ResetRead(void); + + /// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication + void ReadAgain(AString & a_Out); + + /// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs + void CheckValid(void) const; + +protected: + char * m_Buffer; + int m_BufferSize; // Total size of the ringbuffer + + #ifdef _DEBUG + unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker + #endif // _DEBUG + + int m_DataStart; // Where the data starts in the ringbuffer + int m_WritePos; // Where the data ends in the ringbuffer + int m_ReadPos; // Where the next read will start in the ringbuffer + + /// Advances the m_ReadPos by a_Count bytes + void AdvanceReadPos(int a_Count); +} ; + + + + diff --git a/src/ChatColor.cpp b/src/ChatColor.cpp new file mode 100644 index 000000000..2b223ee76 --- /dev/null +++ b/src/ChatColor.cpp @@ -0,0 +1,39 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChatColor.h" + +const std::string cChatColor::Color = "\xc2\xa7"; // or in other words: "§" in UTF-8 +const std::string cChatColor::Delimiter = "\xc2\xa7"; // or in other words: "§" in UTF-8 +const std::string cChatColor::Black = cChatColor::Color + "0"; +const std::string cChatColor::Navy = cChatColor::Color + "1"; +const std::string cChatColor::Green = cChatColor::Color + "2"; +const std::string cChatColor::Blue = cChatColor::Color + "3"; +const std::string cChatColor::Red = cChatColor::Color + "4"; +const std::string cChatColor::Purple = cChatColor::Color + "5"; +const std::string cChatColor::Gold = cChatColor::Color + "6"; +const std::string cChatColor::LightGray = cChatColor::Color + "7"; +const std::string cChatColor::Gray = cChatColor::Color + "8"; +const std::string cChatColor::DarkPurple = cChatColor::Color + "9"; +const std::string cChatColor::LightGreen = cChatColor::Color + "a"; +const std::string cChatColor::LightBlue = cChatColor::Color + "b"; +const std::string cChatColor::Rose = cChatColor::Color + "c"; +const std::string cChatColor::LightPurple = cChatColor::Color + "d"; +const std::string cChatColor::Yellow = cChatColor::Color + "e"; +const std::string cChatColor::White = cChatColor::Color + "f"; + +const std::string cChatColor::Random = cChatColor::Color + "k"; +const std::string cChatColor::Bold = cChatColor::Color + "l"; +const std::string cChatColor::Strikethrough = cChatColor::Color + "m"; +const std::string cChatColor::Underlined = cChatColor::Color + "n"; +const std::string cChatColor::Italic = cChatColor::Color + "o"; +const std::string cChatColor::Plain = cChatColor::Color + "r"; + +const std::string cChatColor::MakeColor( char a_Color ) +{ + return cChatColor::Color + a_Color; +} + + + + diff --git a/src/ChatColor.h b/src/ChatColor.h new file mode 100644 index 000000000..85b10f400 --- /dev/null +++ b/src/ChatColor.h @@ -0,0 +1,43 @@ + +#pragma once + + + + + +// tolua_begin +class cChatColor +{ +public: + static const std::string Color; + static const std::string Delimiter; + + static const std::string Black; + static const std::string Navy; + static const std::string Green; + static const std::string Blue; + static const std::string Red; + static const std::string Purple; + static const std::string Gold; + static const std::string LightGray; + static const std::string Gray; + static const std::string DarkPurple; + static const std::string LightGreen; + static const std::string LightBlue; + static const std::string Rose; + static const std::string LightPurple; + static const std::string Yellow; + static const std::string White; + + // Styles ( source: http://wiki.vg/Chat ) + static const std::string Random; + static const std::string Bold; + static const std::string Strikethrough; + static const std::string Underlined; + static const std::string Italic; + static const std::string Plain; + + static const std::string MakeColor( char a_Color ); +}; + +// tolua_end diff --git a/src/Chunk.cpp b/src/Chunk.cpp new file mode 100644 index 000000000..1c937c894 --- /dev/null +++ b/src/Chunk.cpp @@ -0,0 +1,2732 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#ifndef _WIN32 + #include +#endif + + +#include "Chunk.h" +#include "World.h" +#include "ClientHandle.h" +#include "Server.h" +#include "zlib.h" +#include "Defines.h" +#include "BlockEntities/ChestEntity.h" +#include "BlockEntities/DispenserEntity.h" +#include "BlockEntities/DropperEntity.h" +#include "BlockEntities/FurnaceEntity.h" +#include "BlockEntities/HopperEntity.h" +#include "BlockEntities/JukeboxEntity.h" +#include "BlockEntities/NoteEntity.h" +#include "BlockEntities/SignEntity.h" +#include "Entities/Pickup.h" +#include "Item.h" +#include "Noise.h" +#include "Root.h" +#include "MersenneTwister.h" +#include "Entities/Player.h" +#include "BlockArea.h" +#include "PluginManager.h" +#include "Blocks/BlockHandler.h" +#include "Simulator/FluidSimulator.h" +#include "MobCensus.h" +#include "MobSpawner.h" + + +#include + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// sSetBlock: + +sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) // absolute block position + : x( a_BlockX ) + , y( a_BlockY ) + , z( a_BlockZ ) + , BlockType( a_BlockType ) + , BlockMeta( a_BlockMeta ) +{ + cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunk: + +cChunk::cChunk( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, + cChunkMap * a_ChunkMap, cWorld * a_World, + cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP +) + : m_PosX( a_ChunkX ) + , m_PosY( a_ChunkY ) + , m_PosZ( a_ChunkZ ) + , m_BlockTickX( 0 ) + , m_BlockTickY( 0 ) + , m_BlockTickZ( 0 ) + , m_World( a_World ) + , m_ChunkMap(a_ChunkMap) + , m_IsValid(false) + , m_IsLightValid(false) + , m_IsDirty(false) + , m_IsSaving(false) + , m_StayCount(0) + , m_NeighborXM(a_NeighborXM) + , m_NeighborXP(a_NeighborXP) + , m_NeighborZM(a_NeighborZM) + , m_NeighborZP(a_NeighborZP) + , m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()) + , m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()) +{ + if (a_NeighborXM != NULL) + { + a_NeighborXM->m_NeighborXP = this; + } + if (a_NeighborXP != NULL) + { + a_NeighborXP->m_NeighborXM = this; + } + if (a_NeighborZM != NULL) + { + a_NeighborZM->m_NeighborZP = this; + } + if (a_NeighborZP != NULL) + { + a_NeighborZP->m_NeighborZM = this; + } +} + + + + + +cChunk::~cChunk() +{ + cPluginManager::Get()->CallHookChunkUnloaded(m_World, m_PosX, m_PosZ); + + // LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId() ); + + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + delete *itr; + } + m_BlockEntities.clear(); + + // Remove and destroy all entities that are not players: + cEntityList Entities; + std::swap(Entities, m_Entities); // Need another list because cEntity destructors check if they've been removed from chunk + for (cEntityList::const_iterator itr = Entities.begin(); itr != Entities.end(); ++itr) + { + if (!(*itr)->IsPlayer()) + { + (*itr)->Destroy(false); + delete *itr; + } + } + + if (m_NeighborXM != NULL) + { + m_NeighborXM->m_NeighborXP = NULL; + } + if (m_NeighborXP != NULL) + { + m_NeighborXP->m_NeighborXM = NULL; + } + if (m_NeighborZM != NULL) + { + m_NeighborZM->m_NeighborZP = NULL; + } + if (m_NeighborZP != NULL) + { + m_NeighborZP->m_NeighborZM = NULL; + } + delete m_WaterSimulatorData; + delete m_LavaSimulatorData; +} + + + + + +void cChunk::SetValid(void) +{ + m_IsValid = true; + + m_World->GetChunkMap()->ChunkValidated(); +} + + + + + +void cChunk::MarkRegenerating(void) +{ + // Tell all clients attached to this chunk that they want this chunk: + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + (*itr)->AddWantedChunk(m_PosX, m_PosZ); + } // for itr - m_LoadedByClient[] +} + + + + + +bool cChunk::CanUnload(void) +{ + return m_LoadedByClient.empty() && !m_IsDirty && (m_StayCount == 0); +} + + + + + +void cChunk::MarkSaving(void) +{ + m_IsSaving = true; +} + + + + + +void cChunk::MarkSaved(void) +{ + if (!m_IsSaving) + { + return; + } + m_IsDirty = false; +} + + + + + +void cChunk::MarkLoaded(void) +{ + m_IsDirty = false; + SetValid(); +} + + + + + +void cChunk::MarkLoadFailed(void) +{ + if (m_IsValid) + { + return; + } + + m_HasLoadFailed = true; +} + + + + + +void cChunk::GetAllData(cChunkDataCallback & a_Callback) +{ + a_Callback.HeightMap (&m_HeightMap); + a_Callback.BiomeData (&m_BiomeMap); + a_Callback.BlockTypes (m_BlockTypes); + a_Callback.BlockMeta (m_BlockMeta); + a_Callback.LightIsValid (m_IsLightValid); + a_Callback.BlockLight (m_BlockLight); + a_Callback.BlockSkyLight(m_BlockSkyLight); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + a_Callback.Entity(*itr); + } + + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + a_Callback.BlockEntity(*itr); + } +} + + + + + +void cChunk::SetAllData( + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const HeightMap * a_HeightMap, + const BiomeMap & a_BiomeMap, + cBlockEntityList & a_BlockEntities +) +{ + memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap)); + + if (a_HeightMap != NULL) + { + memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap)); + } + + memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes)); + memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta)); + if (a_BlockLight != NULL) + { + memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); + } + if (a_BlockSkyLight != NULL) + { + memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight)); + } + + m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL); + + if (a_HeightMap == NULL) + { + CalculateHeightmap(); + } + + // Clear the block entities present - either the loader / saver has better, or we'll create empty ones: + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + delete *itr; + } + std::swap(a_BlockEntities, m_BlockEntities); + + // Set all block entities' World variable: + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + (*itr)->SetWorld(m_World); + } + + // Create block entities that the loader didn't load; fill them with defaults + CreateBlockEntities(); + + // Set the chunk data as valid. This may be needed for some simulators that perform actions upon block adding (Vaporize) + SetValid(); + + // Wake up all simulators for their respective blocks: + WakeUpSimulators(); + + m_HasLoadFailed = false; +} + + + + + +void cChunk::SetLight( + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight +) +{ + // TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation. + // Postponing until we see how bad it is :) + memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); + memcpy(m_BlockSkyLight, a_SkyLight, sizeof(m_BlockSkyLight)); + m_IsLightValid = true; +} + + + + + +void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes) +{ + memcpy(a_BlockTypes, m_BlockTypes, NumBlocks); +} + + + + + +void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +{ + if ((a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas)) != (cBlockArea::baTypes | cBlockArea::baMetas)) + { + LOGWARNING("cChunk::WriteBlockArea(): unsupported datatype request, can write only types + metas (0x%x), requested 0x%x. Ignoring.", + (cBlockArea::baTypes | cBlockArea::baMetas), a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas) + ); + return; + } + + // SizeX, SizeZ are the dimensions of the block data to copy to the chunk (size of the geometric union) + + int BlockStartX = std::max(a_MinBlockX, m_PosX * cChunkDef::Width); + int BlockEndX = std::min(a_MinBlockX + a_Area.GetSizeX(), (m_PosX + 1) * cChunkDef::Width); + int BlockStartZ = std::max(a_MinBlockZ, m_PosZ * cChunkDef::Width); + int BlockEndZ = std::min(a_MinBlockZ + a_Area.GetSizeZ(), (m_PosZ + 1) * cChunkDef::Width); + int SizeX = BlockEndX - BlockStartX; + int SizeZ = BlockEndZ - BlockStartZ; + int OffX = BlockStartX - m_PosX * cChunkDef::Width; + int OffZ = BlockStartZ - m_PosZ * cChunkDef::Width; + int BaseX = BlockStartX - a_MinBlockX; + int BaseZ = BlockStartZ - a_MinBlockZ; + int SizeY = a_Area.GetSizeY(); + + // TODO: Improve this by not calling FastSetBlock() and doing the processing here + // so that the heightmap is touched only once for each column. + BLOCKTYPE * AreaBlockTypes = a_Area.GetBlockTypes(); + NIBBLETYPE * AreaBlockMetas = a_Area.GetBlockMetas(); + for (int y = 0; y < SizeY; y++) + { + int ChunkY = a_MinBlockY + y; + int AreaY = y; + for (int z = 0; z < SizeZ; z++) + { + int ChunkZ = OffZ + z; + int AreaZ = BaseZ + z; + for (int x = 0; x < SizeX; x++) + { + int ChunkX = OffX + x; + int AreaX = BaseX + x; + int idx = a_Area.MakeIndex(AreaX, AreaY, AreaZ); + BLOCKTYPE BlockType = AreaBlockTypes[idx]; + NIBBLETYPE BlockMeta = AreaBlockMetas[idx]; + FastSetBlock(ChunkX, ChunkY, ChunkZ, BlockType, BlockMeta); + } // for x + } // for z + } // for y +} + + + + + +/// Returns true if there is a block entity at the coords specified +bool cChunk::HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_BlockX) && + ((*itr)->GetPosY() == a_BlockY) && + ((*itr)->GetPosZ() == a_BlockZ) + ) + { + return true; + } + } // for itr - m_BlockEntities[] + return false; +} + + + + + +/// Sets or resets the internal flag that prevents chunk from being unloaded +void cChunk::Stay(bool a_Stay) +{ + m_StayCount += (a_Stay ? 1 : -1); + ASSERT(m_StayCount >= 0); +} + + + + +void cChunk::CollectMobCensus(cMobCensus& toFill) +{ + toFill.CollectSpawnableChunk(*this); + std::list playerPositions; + cPlayer* currentPlayer; + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + currentPlayer = (*itr)->GetPlayer(); + playerPositions.push_back(&(currentPlayer->GetPosition())); + } + + Vector3d currentPosition; + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + //LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); + if ((*itr)->IsMob()) + { + cMonster& Monster = (cMonster&)(**itr); + currentPosition = Monster.GetPosition(); + for (std::list::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); itr2 ++) + { + toFill.CollectMob(Monster,*this,(currentPosition-**itr2).SqrLength()); + } + } + } // for itr - m_Entitites[] +} + + + + +void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ) +{ + ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff); + int Random = m_World->GetTickRandomNumber(0x00ffffff); + a_X = Random % (a_MaxX * 2); + a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2); + a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2); + a_X /= 2; + a_Y /= 2; + a_Z /= 2; +} + + + + + +void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z) +{ + // MG TODO : check if this kind of optimization (only one random call) is still needed + // MG TODO : if so propagate it + + getThreeRandomNumber(a_X, a_Y, a_Z, Width, Height-2, Width); + a_Y++; +} + + + + + +void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + int Center_X,Center_Y,Center_Z; + getRandomBlockCoords(Center_X,Center_Y,Center_Z); + + BLOCKTYPE PackCenterBlock = GetBlock(Center_X, Center_Y, Center_Z); + if (a_MobSpawner.CheckPackCenter(PackCenterBlock)) + { + a_MobSpawner.NewPack(); + int NumberOfTries = 0; + int NumberOfSuccess = 0; + int MaxNbOfSuccess = 4; // this can be changed during the process for Wolves and Ghass + while (NumberOfTries < 12 && NumberOfSuccess < MaxNbOfSuccess) + { + const int HorizontalRange = 20; // MG TODO : relocate + const int VerticalRange = 0; // MG TODO : relocate + int Try_X, Try_Y, Try_Z; + getThreeRandomNumber(Try_X, Try_Y, Try_Z, 2*HorizontalRange+1 , 2*VerticalRange+1 , 2*HorizontalRange+1); + Try_X -= HorizontalRange; + Try_Y -= VerticalRange; + Try_Z -= HorizontalRange; + Try_X += Center_X; + Try_Y += Center_Y; + Try_Z += Center_Z; + + ASSERT(Try_Y > 0); + ASSERT(Try_Y < cChunkDef::Height-1); + + EMCSBiome Biome = m_ChunkMap->GetBiomeAt (Try_X, Try_Z); + // MG TODO : + // Moon cycle (for slime) + // check player and playerspawn presence < 24 blocks + // check mobs presence on the block + + // MG TODO : check that "Level" really means Y + + NIBBLETYPE SkyLight = 0; + + NIBBLETYPE BlockLight = 0; + + if (IsLightValid()) + { + cEntity* newMob = a_MobSpawner.TryToSpawnHere(this, Try_X, Try_Y, Try_Z, Biome, MaxNbOfSuccess); + if (newMob) + { + int WorldX, WorldY, WorldZ; + PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ); + double ActualX = WorldX + 0.5; + double ActualZ = WorldZ + 0.5; + newMob->SetPosition(ActualX, WorldY, ActualZ); + LOGD("Spawning %s #%i at %d,%d,%d",newMob->GetClass(),newMob->GetUniqueID(),WorldX, WorldY, WorldZ); + NumberOfSuccess++; + } + } + + NumberOfTries++; + } + } + +} + + + + + +void cChunk::Tick(float a_Dt) +{ + BroadcastPendingBlockChanges(); + + // Unload the chunk from all clients that have queued unloading: + for (cClientHandleList::iterator itr = m_UnloadQuery.begin(), end = m_UnloadQuery.end(); itr != end; ++itr) + { + (*itr)->SendUnloadChunk(m_PosX, m_PosZ); + } + m_UnloadQuery.clear(); + + // Set all blocks that have been queued for setting later: + ProcessQueuedSetBlocks(); + + CheckBlocks(); + + // Tick simulators: + m_World->GetSimulatorManager()->SimulateChunk(a_Dt, m_PosX, m_PosZ, this); + + TickBlocks(); + + // Tick all block entities in this chunk: + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + m_IsDirty = (*itr)->Tick(a_Dt, *this) | m_IsDirty; + } + + // Tick all entities in this chunk (except mobs): + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + // Mobs are tickes inside MobTick (as we don't have to tick them if they are far away from players) + if (!((*itr)->IsMob())) + { + (*itr)->Tick(a_Dt, *this); + } + } // for itr - m_Entitites[] + + // Remove all entities that were scheduled for removal: + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();) + { + if ((*itr)->IsDestroyed()) + { + LOGD("Destroying entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); + cEntity * ToDelete = *itr; + itr = m_Entities.erase(itr); + delete ToDelete; + continue; + } + itr++; + } // for itr - m_Entitites[] + + // If any entity moved out of the chunk, move it to the neighbor: + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();) + { + if ( + ((*itr)->GetChunkX() != m_PosX) || + ((*itr)->GetChunkZ() != m_PosZ) + ) + { + MoveEntityToNewChunk(*itr); + itr = m_Entities.erase(itr); + } + else + { + ++itr; + } + } + + ApplyWeatherToTop(); +} + + + + + +void cChunk::MoveEntityToNewChunk(cEntity * a_Entity) +{ + cChunk * Neighbor = GetNeighborChunk(a_Entity->GetChunkX() * cChunkDef::Width, a_Entity->GetChunkZ() * cChunkDef::Width); + if (Neighbor == NULL) + { + Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + if (Neighbor == NULL) + { + // TODO: What to do with this? + LOGWARNING("%s: Failed to move entity, destination chunk unreachable. Entity lost", __FUNCTION__); + return; + } + } + + ASSERT(Neighbor != this); // Moving into the same chunk? wtf? + + Neighbor->AddEntity(a_Entity); + + class cMover : + public cClientDiffCallback + { + virtual void Removed(cClientHandle * a_Client) override + { + a_Client->SendDestroyEntity(*m_Entity); + } + + virtual void Added(cClientHandle * a_Client) override + { + m_Entity->SpawnOn(*a_Client); + } + + cEntity * m_Entity; + + public: + cMover(cEntity * a_Entity) : + m_Entity(a_Entity) + {} + } Mover(a_Entity); + + m_ChunkMap->CompareChunkClients(this, Neighbor, Mover); +} + + + + + +void cChunk::ProcessQueuedSetBlocks(void) +{ + Int64 CurrTick = m_World->GetWorldAge(); + for (sSetBlockQueueVector::iterator itr = m_SetBlockQueue.begin(); itr != m_SetBlockQueue.end();) + { + if (itr->m_Tick < CurrTick) + { + // Not yet + ++itr; + continue; + } + else + { + // Now is the time to set the block + SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); + itr = m_SetBlockQueue.erase(itr); + } + } // for itr - m_SetBlockQueue[] +} + + + + + +void cChunk::BroadcastPendingBlockChanges(void) +{ + if (m_PendingSendBlocks.empty()) + { + return; + } + + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks); + } + m_PendingSendBlocks.clear(); +} + + + + + +void cChunk::CheckBlocks(void) +{ + if (m_ToTickBlocks.size() == 0) + { + return; + } + std::vector ToTickBlocks; + std::swap(m_ToTickBlocks, ToTickBlocks); + + for (std::vector::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr) + { + unsigned int index = (*itr); + Vector3i BlockPos = IndexToCoordinate(index); + + cBlockHandler * Handler = BlockHandler(GetBlock(index)); + Handler->Check(BlockPos.x, BlockPos.y, BlockPos.z, *this); + } // for itr - ToTickBlocks[] +} + + + + + +void cChunk::TickBlocks(void) +{ + // Tick dem blocks + // _X: We must limit the random number or else we get a nasty int overflow bug ( http://forum.mc-server.org/showthread.php?tid=457 ) + int RandomX = m_World->GetTickRandomNumber(0x00ffffff); + int RandomY = m_World->GetTickRandomNumber(0x00ffffff); + int RandomZ = m_World->GetTickRandomNumber(0x00ffffff); + int TickX = m_BlockTickX; + int TickY = m_BlockTickY; + int TickZ = m_BlockTickZ; + + // This for loop looks disgusting, but it actually does a simple thing - first processes m_BlockTick, then adds random to it + // This is so that SetNextBlockTick() works + for (int i = 0; i < 50; i++, + + // This weird construct (*2, then /2) is needed, + // otherwise the blocktick distribution is too biased towards even coords! + + TickX = (TickX + RandomX) % (Width * 2), + TickY = (TickY + RandomY) % (Height * 2), + TickZ = (TickZ + RandomZ) % (Width * 2), + m_BlockTickX = TickX / 2, + m_BlockTickY = TickY / 2, + m_BlockTickZ = TickZ / 2 + ) + { + + if (m_BlockTickY > cChunkDef::GetHeight(m_HeightMap, m_BlockTickX, m_BlockTickZ)) + { + continue; // It's all air up here + } + + unsigned int Index = MakeIndexNoCheck(m_BlockTickX, m_BlockTickY, m_BlockTickZ); + cBlockHandler * Handler = BlockHandler(m_BlockTypes[Index]); + ASSERT(Handler != NULL); // Happenned on server restart, FS #243 + Handler->OnUpdate(m_World, m_BlockTickX + m_PosX * Width, m_BlockTickY, m_BlockTickZ + m_PosZ * Width); + } // for i - tickblocks +} + + + + + +void cChunk::ApplyWeatherToTop() +{ + if ( + (m_World->GetTickRandomNumber(100) != 0) || + ( + (m_World->GetWeather() != eWeather_Rain) && + (m_World->GetWeather() != eWeather_ThunderStorm) + ) + ) + { + // Not the right weather, or not at this tick; bail out + return; + } + + int X = m_World->GetTickRandomNumber(15); + int Z = m_World->GetTickRandomNumber(15); + switch (GetBiomeAt(X, Z)) + { + case biTaiga: + case biFrozenOcean: + case biFrozenRiver: + case biIcePlains: + case biIceMountains: + case biTaigaHills: + { + // TODO: Check light levels, don't snow over when the BlockLight is higher than (7?) + int Height = GetHeight(X, Z); + BLOCKTYPE TopBlock = GetBlock(X, Height, Z); + NIBBLETYPE TopMeta = GetMeta (X, Height, Z); + if (m_World->IsDeepSnowEnabled() && (TopBlock == E_BLOCK_SNOW)) + { + int MaxSize = 7; + BLOCKTYPE BlockType[4]; + NIBBLETYPE BlockMeta[4]; + UnboundedRelGetBlock(X - 1, Height, Z, BlockType[0], BlockMeta[0]); + UnboundedRelGetBlock(X + 1, Height, Z, BlockType[1], BlockMeta[1]); + UnboundedRelGetBlock(X, Height, Z - 1, BlockType[2], BlockMeta[2]); + UnboundedRelGetBlock(X, Height, Z + 1, BlockType[3], BlockMeta[3]); + for (int i = 0; i < 4; i++) + { + switch (BlockType[i]) + { + case E_BLOCK_AIR: + { + MaxSize = 0; + break; + } + case E_BLOCK_SNOW: + { + MaxSize = std::min(BlockMeta[i] + 1, MaxSize); + break; + } + } + } + if (TopMeta < MaxSize) + { + FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1); + } + else if (TopMeta > MaxSize) + { + FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1); + } + } + else if (g_BlockIsSnowable[TopBlock]) + { + SetBlock(X, Height + 1, Z, E_BLOCK_SNOW, 0); + } + else if ((TopBlock == E_BLOCK_WATER) || (TopBlock == E_BLOCK_STATIONARY_WATER)) + { + SetBlock(X, Height, Z, E_BLOCK_ICE, 0); + } + else if ( + (m_World->IsDeepSnowEnabled()) && + ( + (TopBlock == E_BLOCK_RED_ROSE) || + (TopBlock == E_BLOCK_YELLOW_FLOWER) || + (TopBlock == E_BLOCK_RED_MUSHROOM) || + (TopBlock == E_BLOCK_BROWN_MUSHROOM) + ) + ) + { + SetBlock(X, Height, Z, E_BLOCK_SNOW, 0); + } + break; + } // case (snowy biomes) + + // TODO: Rainy biomes should check for farmland and cauldrons + } // switch (biome) +} + + + + + +void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) +{ + // Convert the stem BlockType into produce BlockType + BLOCKTYPE ProduceType; + switch (a_BlockType) + { + case E_BLOCK_MELON_STEM: ProduceType = E_BLOCK_MELON; break; + case E_BLOCK_PUMPKIN_STEM: ProduceType = E_BLOCK_PUMPKIN; break; + default: + { + ASSERT(!"Unhandled blocktype in TickMelonPumpkin()"); + return; + } + } + + // Check if there's another melon / pumpkin around that stem, if so, abort: + bool IsValid; + BLOCKTYPE BlockType[4]; + NIBBLETYPE BlockMeta; // unused + IsValid = UnboundedRelGetBlock(a_RelX + 1, a_RelY, a_RelZ, BlockType[0], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX - 1, a_RelY, a_RelZ, BlockType[1], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + 1, BlockType[2], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - 1, BlockType[3], BlockMeta); + if ( + !IsValid || + (BlockType[0] == ProduceType) || + (BlockType[1] == ProduceType) || + (BlockType[2] == ProduceType) || + (BlockType[3] == ProduceType) + ) + { + // Neighbors not valid or already taken by the same produce + return; + } + + // Pick a direction in which to place the produce: + int x = 0, z = 0; + int CheckType = a_TickRandom.randInt(3); // The index to the neighbors array which should be checked for emptiness + switch (CheckType) + { + case 0: x = 1; break; + case 1: x = -1; break; + case 2: z = 1; break; + case 3: z = -1; break; + } + + // Check that the block in that direction is empty: + switch (BlockType[CheckType]) + { + case E_BLOCK_AIR: + case E_BLOCK_SNOW: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_DEAD_BUSH: + { + break; + } + default: return; + } + + // Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok + BLOCKTYPE Soil; + UnboundedRelGetBlock(a_RelX + x, a_RelY - 1, a_RelZ + z, Soil, BlockMeta); + switch (Soil) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + { + // DEBUG: This is here to catch FS #349 - melons growing over other crops. + LOG("Growing melon/pumpkin overwriting %s, growing on %s", + ItemTypeToString(BlockType[CheckType]).c_str(), + ItemTypeToString(Soil).c_str() + ); + // Place a randomly-facing produce: + UnboundedRelFastSetBlock(a_RelX + x, a_RelY, a_RelZ + z, ProduceType, (NIBBLETYPE)(a_TickRandom.randInt(4) % 4)); + break; + } + } +} + + + + + +void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) +{ + // Check the total height of the sugarcane blocks here: + int Top = a_RelY + 1; + while ( + (Top < cChunkDef::Height) && + (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_SUGARCANE) + ) + { + ++Top; + } + int Bottom = a_RelY - 1; + while ( + (Bottom > 0) && + (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_SUGARCANE) + ) + { + --Bottom; + } + + // Grow by at most a_NumBlocks, but no more than max height: + int ToGrow = std::min(a_NumBlocks, m_World->GetMaxSugarcaneHeight() + 1 - (Top - Bottom)); + for (int i = 0; i < ToGrow; i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) + { + UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_SUGARCANE, 0); + } + else + { + break; + } + } // for i +} + + + + + +void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) +{ + // Check the total height of the sugarcane blocks here: + int Top = a_RelY + 1; + while ( + (Top < cChunkDef::Height) && + (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_CACTUS) + ) + { + ++Top; + } + int Bottom = a_RelY - 1; + while ( + (Bottom > 0) && + (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_CACTUS) + ) + { + --Bottom; + } + + // Grow by at most a_NumBlocks, but no more than max height: + int ToGrow = std::min(a_NumBlocks, m_World->GetMaxCactusHeight() + 1 - (Top - Bottom)); + for (int i = 0; i < ToGrow; i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) + { + // TODO: Check the surrounding blocks, if they aren't air, break the cactus block into pickups (and continue breaking blocks above in the next loop iterations) + UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_CACTUS, 0); + } + else + { + break; + } + } // for i +} + + + + + +bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + a_BlockType = Chunk->GetBlock(a_RelX, a_RelY, a_RelZ); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + a_BlockMeta = Chunk->GetMeta(a_RelX, a_RelY, a_RelZ); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockBlockLight) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + a_BlockBlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockSkyLight) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + a_BlockSkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + int idx = Chunk->MakeIndex(a_RelX, a_RelY, a_RelZ); + a_BlockLight = Chunk->GetBlockLight(idx); + a_SkyLight = Chunk->GetSkyLight(idx); + return true; +} + + + + + +bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) + { + LOGWARNING("UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) + { + LOGWARNING("UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +void cChunk::UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ) +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + // Outside of chunkmap + return; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->QueueTickBlock(a_RelX, a_RelY, a_RelZ); + } +} + + + + + +int cChunk::GetHeight(int a_X, int a_Z) +{ + ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)); + + if ((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)) + { + return m_HeightMap[a_X + a_Z * Width]; + } + return 0; +} + + + + + +void cChunk::CreateBlockEntities(void) +{ + for (int x = 0; x < Width; x++) + { + for (int z = 0; z < Width; z++) + { + for (int y = 0; y < Height; y++) + { + BLOCKTYPE BlockType = cChunkDef::GetBlock(m_BlockTypes, x, y, z); + switch (BlockType) + { + case E_BLOCK_CHEST: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_FURNACE: + case E_BLOCK_HOPPER: + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_JUKEBOX: + { + if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) + { + m_BlockEntities.push_back(cBlockEntity::CreateByBlockType( + BlockType, GetMeta(x, y, z), + x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World + )); + } + break; + } + } // switch (BlockType) + } // for y + } // for z + } // for x +} + + + + + +void cChunk::WakeUpSimulators(void) +{ + cSimulator * WaterSimulator = m_World->GetWaterSimulator(); + cSimulator * LavaSimulator = m_World->GetLavaSimulator(); + int BaseX = m_PosX * cChunkDef::Width; + int BaseZ = m_PosZ * cChunkDef::Width; + for (int x = 0; x < Width; x++) + { + int BlockX = x + BaseX; + for (int z = 0; z < Width; z++) + { + int BlockZ = z + BaseZ; + for (int y = GetHeight(x, z); y >= 0; y--) + { + switch (cChunkDef::GetBlock(m_BlockTypes, x, y, z)) + { + case E_BLOCK_WATER: + { + WaterSimulator->AddBlock(BlockX, y, BlockZ, this); + break; + } + case E_BLOCK_LAVA: + { + LavaSimulator->AddBlock(BlockX, y, BlockZ, this); + break; + } + } // switch (BlockType) + } // for y + } // for z + } // for x +} + + + + + +void cChunk::CalculateHeightmap() +{ + for (int x = 0; x < Width; x++) + { + for (int z = 0; z < Width; z++) + { + for (int y = Height - 1; y > -1; y--) + { + int index = MakeIndex( x, y, z ); + if (m_BlockTypes[index] != E_BLOCK_AIR) + { + m_HeightMap[x + z * Width] = (unsigned char)y; + break; + } + } // for y + } // for z + } // for x +} + + + + + +void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + + const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + + // Tick this block and its neighbors: + m_ToTickBlocks.push_back(index); + QueueTickBlockNeighbors(a_RelX, a_RelY, a_RelZ); + + // If there was a block entity, remove it: + Vector3i WorldPos = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ); + cBlockEntity * BlockEntity = GetBlockEntity(WorldPos); + if (BlockEntity != NULL) + { + BlockEntity->Destroy(); + RemoveBlockEntity(BlockEntity); + delete BlockEntity; + } + + // If the new block is a block entity, create the entity object: + switch (a_BlockType) + { + case E_BLOCK_CHEST: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_FURNACE: + case E_BLOCK_HOPPER: + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_JUKEBOX: + { + AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, WorldPos.x, WorldPos.y, WorldPos.z, m_World)); + break; + } + } // switch (a_BlockType) +} + + + + + +void cChunk::QueueSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick) +{ + m_SetBlockQueue.push_back(sSetBlockQueueItem(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta, a_Tick)); +} + + + + + +void cChunk::QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ) +{ + ASSERT ( + (a_RelX >= 0) && (a_RelX < Width) && + (a_RelY >= 0) && (a_RelY < Height) && + (a_RelZ >= 0) && (a_RelZ < Width) + ); // Coords need to be valid + + if (!IsValid()) + { + return; + } + + m_ToTickBlocks.push_back(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ)); +} + + + + + +void cChunk::QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ) +{ + struct + { + int x, y, z; + } + Coords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 1, 0}, + { 0, -1, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + UnboundedQueueTickBlock(a_RelX + Coords[i].x, a_RelY + Coords[i].y, a_RelZ + Coords[i].z); + } // for i - Coords[] +} + + + + + +void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) +{ + ASSERT(!((a_RelX < 0) || (a_RelX >= Width) || (a_RelY < 0) || (a_RelY >= Height) || (a_RelZ < 0) || (a_RelZ >= Width))); + + ASSERT(IsValid()); + + const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + const BLOCKTYPE OldBlockType = cChunkDef::GetBlock(m_BlockTypes, index); + const BLOCKTYPE OldBlockMeta = GetNibble(m_BlockMeta, index); + if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta)) + { + return; + } + + MarkDirty(); + + m_BlockTypes[index] = a_BlockType; + + // The client doesn't need to distinguish between stationary and nonstationary fluids: + if ( + (OldBlockMeta != a_BlockMeta) || // Different meta always gets sent to the client + !( + ((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water + ((OldBlockType == E_BLOCK_WATER) && (a_BlockType == E_BLOCK_STATIONARY_WATER)) || // Replacing water with stationary water + ((OldBlockType == E_BLOCK_STATIONARY_LAVA) && (a_BlockType == E_BLOCK_LAVA)) || // Replacing stationary water with water + ((OldBlockType == E_BLOCK_LAVA) && (a_BlockType == E_BLOCK_STATIONARY_LAVA)) // Replacing water with stationary water + ) + ) + { + m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta)); + } + + SetNibble(m_BlockMeta, index, a_BlockMeta); + + // ONLY recalculate lighting if it's necessary! + if( + (g_BlockLightValue[OldBlockType ] != g_BlockLightValue[a_BlockType]) || + (g_BlockSpreadLightFalloff[OldBlockType] != g_BlockSpreadLightFalloff[a_BlockType]) || + (g_BlockTransparent[OldBlockType] != g_BlockTransparent[a_BlockType]) + ) + { + m_IsLightValid = false; + } + + // Update heightmap, if needed: + if (a_RelY >= m_HeightMap[a_RelX + a_RelZ * Width]) + { + if (a_BlockType != E_BLOCK_AIR) + { + m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)a_RelY; + } + else + { + for (int y = a_RelY - 1; y > 0; --y) + { + if (m_BlockTypes[MakeIndexNoCheck(a_RelX, y, a_RelZ)] != E_BLOCK_AIR) + { + m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)y; + break; + } + } // for y - column in m_BlockData + } + } +} + + + + + +void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client) +{ + // The coords must be valid, because the upper level already does chunk lookup. No need to check them again. + // There's an debug-time assert in MakeIndexNoCheck anyway + unsigned int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + + if (a_Client == NULL) + { + // Queue the block for all clients in the chunk (will be sent in Tick()) + m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(index), GetMeta(index))); + return; + } + + Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ); + a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index)); + + // FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client: + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr) + { + if (((*itr)->GetPosX() == wp.x) && ((*itr)->GetPosY() == wp.y) && ((*itr)->GetPosZ() == wp.z)) + { + (*itr)->SendTo(*a_Client); + } + } // for itr - m_BlockEntities +} + + + + + +void cChunk::AddBlockEntity(cBlockEntity * a_BlockEntity) +{ + MarkDirty(); + m_BlockEntities.push_back(a_BlockEntity); +} + + + + + +cBlockEntity * cChunk::GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_BlockX) && + ((*itr)->GetPosY() == a_BlockY) && + ((*itr)->GetPosZ() == a_BlockZ) + ) + { + return *itr; + } + } // for itr - m_BlockEntities[] + + return NULL; +} + + + + + +void cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) +{ + cBlockEntity * be = GetBlockEntity(a_X, a_Y, a_Z); + if (be != NULL) + { + be->UsedBy(a_Player); + } +} + + + + + +void cChunk::CollectPickupsByPlayer(cPlayer * a_Player) +{ + double PosX = a_Player->GetPosX(); + double PosY = a_Player->GetPosY(); + double PosZ = a_Player->GetPosZ(); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + if ((!(*itr)->IsPickup()) && (!(*itr)->IsProjectile())) + { + continue; // Only pickups and projectiles + } + float DiffX = (float)((*itr)->GetPosX() - PosX ); + float DiffY = (float)((*itr)->GetPosY() - PosY ); + float DiffZ = (float)((*itr)->GetPosZ() - PosZ ); + float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; + if (SqrDist < 1.5f * 1.5f) // 1.5 block + { + /* + LOG("Pickup %d being collected by player \"%s\", distance %f", + (*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist + ); + */ + MarkDirty(); + if ((*itr)->IsPickup()) + { + (reinterpret_cast(*itr))->CollectedBy(a_Player); + } + else + { + (reinterpret_cast(*itr))->CollectedBy(a_Player); + } + } + else if (SqrDist < 5 * 5) + { + /* + LOG("Pickup %d close to player \"%s\", but still too far to collect: %f", + (*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist + ); + */ + } + } +} + + + + + +bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + // Also sends update packets to all clients in the chunk + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_PosX) && + ((*itr)->GetPosY() == a_PosY) && + ((*itr)->GetPosZ() == a_PosZ) && + ( + ((*itr)->GetBlockType() == E_BLOCK_WALLSIGN) || + ((*itr)->GetBlockType() == E_BLOCK_SIGN_POST) + ) + ) + { + MarkDirty(); + (reinterpret_cast(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4); + m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ); + return true; + } + } // for itr - m_BlockEntities[] + return false; +} + + + + + +void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity ) +{ + MarkDirty(); + m_BlockEntities.remove(a_BlockEntity); +} + + + + + +bool cChunk::AddClient(cClientHandle* a_Client) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if (a_Client == *itr) + { + // Already there, nothing needed + return false; + } + } + m_LoadedByClient.push_back( a_Client ); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) + { + LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"", (*itr)->GetUniqueID(), (*itr)->GetClass(), m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str()); + (*itr)->SpawnOn(*a_Client); + } + return true; +} + + + + + +void cChunk::RemoveClient( cClientHandle* a_Client ) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if (*itr != a_Client) + { + continue; + } + + m_LoadedByClient.erase(itr); + + if (!a_Client->IsDestroyed()) + { + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) + { + LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"", m_PosX, m_PosZ, (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() ); + a_Client->SendDestroyEntity(*(*itr)); + } + } + return; + } // for itr - m_LoadedByClient[] +} + + + + + +bool cChunk::HasClient( cClientHandle* a_Client ) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if ((*itr) == a_Client) + { + return true; + } + } + return false; +} + + + + + +bool cChunk::HasAnyClients(void) +{ + return !m_LoadedByClient.empty(); +} + + + + + +void cChunk::AddEntity(cEntity * a_Entity) +{ + if (!a_Entity->IsPlayer()) + { + MarkDirty(); + } + + ASSERT(std::find(m_Entities.begin(), m_Entities.end(), a_Entity) == m_Entities.end()); // Not there already + + m_Entities.push_back(a_Entity); +} + + + + + +void cChunk::RemoveEntity(cEntity * a_Entity) +{ + size_t SizeBefore = m_Entities.size(); + m_Entities.remove(a_Entity); + size_t SizeAfter = m_Entities.size(); + + if (SizeBefore != SizeAfter) + { + // Mark as dirty if it was a server-generated entity: + if (!a_Entity->IsPlayer()) + { + MarkDirty(); + } + } +} + + + + + +bool cChunk::HasEntity(int a_EntityID) +{ + for (cEntityList::const_iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr) + { + if ((*itr)->GetUniqueID() == a_EntityID) + { + return true; + } + } // for itr - m_Entities[] + return false; +} + + + + + +bool cChunk::ForEachEntity(cEntityCallback & a_Callback) +{ + // The entity list is locked by the parent chunkmap's CS + for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_Entitites[] + return true; +} + + + + + +bool cChunk::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult) +{ + // The entity list is locked by the parent chunkmap's CS + for (cEntityList::iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr) + { + if ((*itr)->GetUniqueID() == a_EntityID) + { + a_CallbackResult = a_Callback.Item(*itr); + return true; + } + } // for itr - m_Entitites[] + return false; +} + + + + + +bool cChunk::ForEachChest(cChestCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if ((*itr)->GetBlockType() != E_BLOCK_CHEST) + { + continue; + } + if (a_Callback.Item((cChestEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachDispenser(cDispenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER) + { + continue; + } + if (a_Callback.Item((cDispenserEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachDropper(cDropperCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if ((*itr)->GetBlockType() != E_BLOCK_DROPPER) + { + continue; + } + if (a_Callback.Item((cDropperEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachDropSpenser(cDropSpenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetBlockType() != E_BLOCK_DISPENSER) && ((*itr)->GetBlockType() != E_BLOCK_DROPPER)) + { + continue; + } + if (a_Callback.Item((cDropSpenserEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachFurnace(cFurnaceCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + switch ((*itr)->GetBlockType()) + { + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: + { + break; + } + default: + { + continue; + } + } + if (a_Callback.Item((cFurnaceEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_CHEST) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cChestEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cDispenserEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_DROPPER) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cDropperEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if (((*itr)->GetBlockType() != E_BLOCK_DISPENSER) && ((*itr)->GetBlockType() != E_BLOCK_DROPPER)) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cDropSpenserEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + switch ((*itr)->GetBlockType()) + { + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: + { + break; + } + default: + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + } // switch (BlockType) + + // The correct block entity is here, + if (a_Callback.Item((cFurnaceEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + switch ((*itr)->GetBlockType()) + { + case E_BLOCK_WALLSIGN: + case E_BLOCK_SIGN_POST: + { + a_Line1 = ((cSignEntity *)*itr)->GetLine(0); + a_Line2 = ((cSignEntity *)*itr)->GetLine(1); + a_Line3 = ((cSignEntity *)*itr)->GetLine(2); + a_Line4 = ((cSignEntity *)*itr)->GetLine(3); + return true; + } + } // switch (BlockType) + + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +BLOCKTYPE cChunk::GetBlock(int a_RelX, int a_RelY, int a_RelZ) const +{ + if ( + (a_RelX < 0) || (a_RelX >= Width) || + (a_RelY < 0) || (a_RelY >= Height) || + (a_RelZ < 0) || (a_RelZ >= Width) + ) + { + ASSERT(!"GetBlock(x, y, z) out of bounds!"); + return 0; // Clip + } + + return m_BlockTypes[MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ)]; +} + + + + + +BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const +{ + if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks)) + { + ASSERT(!"GetBlock(idx) out of bounds!"); + return 0; + } + + return m_BlockTypes[ a_BlockIdx ]; +} + + + + + +void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + a_BlockType = cChunkDef::GetBlock (m_BlockTypes, a_RelX, a_RelY, a_RelZ); + a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cChunk::GetBlockInfo(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) +{ + int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + a_BlockType = cChunkDef::GetBlock (m_BlockTypes, Idx); + a_Meta = cChunkDef::GetNibble(m_BlockMeta, Idx); + a_SkyLight = cChunkDef::GetNibble(m_BlockSkyLight, Idx); + a_BlockLight = cChunkDef::GetNibble(m_BlockLight, Idx); +} + + + + + +cChunk * cChunk::GetNeighborChunk(int a_BlockX, int a_BlockZ) +{ + // Convert coords to relative, then call the relative version: + a_BlockX -= m_PosX * cChunkDef::Width; + a_BlockZ -= m_PosZ * cChunkDef::Width; + return GetRelNeighborChunk(a_BlockX, a_BlockZ); +} + + + + + +cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ) +{ + bool ReturnThis = true; + if (a_RelX < 0) + { + if (m_NeighborXM != NULL) + { + cChunk * Candidate = m_NeighborXM->GetRelNeighborChunk(a_RelX + cChunkDef::Width, a_RelZ); + if (Candidate != NULL) + { + return Candidate; + } + } + // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on. + ReturnThis = false; + } + else if (a_RelX >= cChunkDef::Width) + { + if (m_NeighborXP != NULL) + { + cChunk * Candidate = m_NeighborXP->GetRelNeighborChunk(a_RelX - cChunkDef::Width, a_RelZ); + if (Candidate != NULL) + { + return Candidate; + } + } + // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on. + ReturnThis = false; + } + + if (a_RelZ < 0) + { + if (m_NeighborZM != NULL) + { + return m_NeighborZM->GetRelNeighborChunk(a_RelX, a_RelZ + cChunkDef::Width); + // For requests crossing both X and Z, the X-first way has been already tried + } + return NULL; + } + else if (a_RelZ >= cChunkDef::Width) + { + if (m_NeighborZP != NULL) + { + return m_NeighborZP->GetRelNeighborChunk(a_RelX, a_RelZ - cChunkDef::Width); + // For requests crossing both X and Z, the X-first way has been already tried + } + return NULL; + } + + return (ReturnThis ? this : NULL); +} + + + + + +cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const +{ + cChunk * ToReturn = const_cast(this); + + // The most common case: inside this chunk: + if ( + (a_RelX >= 0) && (a_RelX < Width) && + (a_RelZ >= 0) && (a_RelZ < Width) + ) + { + return ToReturn; + } + + // Request for a different chunk, calculate chunk offset: + int RelX = a_RelX; // Make a local copy of the coords (faster access) + int RelZ = a_RelZ; + while ((RelX >= Width) && (ToReturn != NULL)) + { + RelX -= Width; + ToReturn = ToReturn->m_NeighborXP; + } + while ((RelX < 0) && (ToReturn != NULL)) + { + RelX += Width; + ToReturn = ToReturn->m_NeighborXM; + } + while ((RelZ >= Width) && (ToReturn != NULL)) + { + RelZ -= Width; + ToReturn = ToReturn->m_NeighborZP; + } + while ((RelZ < 0) && (ToReturn != NULL)) + { + RelZ += Width; + ToReturn = ToReturn->m_NeighborZM; + } + if (ToReturn != NULL) + { + a_RelX = RelX; + a_RelZ = RelZ; + return ToReturn; + } + + // The chunk cannot be walked through neighbors, find it through the chunkmap: + int AbsX = a_RelX + m_PosX * Width; + int AbsZ = a_RelZ + m_PosZ * Width; + int DstChunkX, DstChunkZ; + BlockToChunk(AbsX, AbsZ, DstChunkX, DstChunkZ); + ToReturn = m_ChunkMap->FindChunk(DstChunkX, DstChunkZ); + a_RelX = AbsX - DstChunkX * Width; + a_RelZ = AbsZ - DstChunkZ * Width; + return ToReturn; +} + + + + + +void cChunk::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + (*itr)->SendAttachEntity(a_Entity, a_Vehicle); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendBlockBreakAnim(a_entityID, a_blockX, a_blockY, a_blockZ, a_stage); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + // We can operate on entity pointers, we're inside the ChunkMap's CS lock which guards the list + cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (Entity == NULL) + { + return; + } + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + Entity->SendTo(*(*itr)); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendChunkData(m_PosX, m_PosZ, a_Serializer); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendCollectPickup(a_Pickup, a_Player); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendDestroyEntity(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityHeadLook(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityLook(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityMetadata(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityStatus(a_Entity, a_Status); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityVelocity(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendPlayerAnimation(a_Player, a_Animation); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + a_Entity.SpawnOn(*(*itr)); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + (*itr)->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) +{ + cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (Entity == NULL) + { + return; + } + Entity->SendTo(a_Client); +} + + + + + +void cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ) +{ + a_BlockY = a_RelY; + a_BlockX = m_PosX * Width + a_RelX; + a_BlockZ = m_PosZ * Width + a_RelZ; +} + + + + + +Vector3i cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ) +{ + return Vector3i(m_PosX * Width + a_RelX, m_PosY * Height + a_RelY, m_PosZ * Width + a_RelZ); +} + + + + + +NIBBLETYPE cChunk::GetTimeAlteredLight(NIBBLETYPE a_Skylight) const +{ + a_Skylight -= m_World->GetSkyDarkness(); + // Because NIBBLETYPE is unsigned, we clamp it to 0 .. 15 by checking for values above 15 + return (a_Skylight < 16)? a_Skylight : 0; +} + + + + + +#if !C_CHUNK_USE_INLINE +# include "cChunk.inl.h" +#endif + + + + diff --git a/src/Chunk.h b/src/Chunk.h new file mode 100644 index 000000000..63a8f75cd --- /dev/null +++ b/src/Chunk.h @@ -0,0 +1,475 @@ + +#pragma once + +#include "Entities/Entity.h" +#include "ChunkDef.h" + +#include "Simulator/FireSimulator.h" +#include "Simulator/SandSimulator.h" + + + + + +#define C_CHUNK_USE_INLINE 1 + +// Do not touch +#if C_CHUNK_USE_INLINE + #define __C_CHUNK_INLINE__ inline +#else + #define __C_CHUNK_INLINE__ +#endif + + + + + +namespace Json +{ + class Value; +}; + + + + + +class cWorld; +class cFurnaceEntity; +class cClientHandle; +class cServer; +class MTRand; +class cPlayer; +class cChunkMap; +class cChestEntity; +class cDispenserEntity; +class cFurnaceEntity; +class cBlockArea; +class cPawn; +class cPickup; +class cChunkDataSerializer; +class cBlockArea; +class cFluidSimulatorData; +class cMobCensus; +class cMobSpawner; + +typedef std::list cClientHandleList; +typedef cItemCallback cEntityCallback; +typedef cItemCallback cChestCallback; +typedef cItemCallback cDispenserCallback; +typedef cItemCallback cFurnaceCallback; + + + + +// This class is not to be used directly +// Instead, call actions on cChunkMap (such as cChunkMap::SetBlock() etc.) +class cChunk : + public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef +{ +public: + cChunk( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords + cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects + cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks + ); + ~cChunk(); + + bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated) + void SetValid(void); // Also wakes up any calls to cChunkMap::GetHeight() + void MarkRegenerating(void); // Marks all clients attached to this chunk as wanting this chunk + bool IsDirty(void) const {return m_IsDirty; } // Returns true if the chunk has changed since it was last saved + bool HasLoadFailed(void) const {return m_HasLoadFailed; } // Returns true if the chunk failed to load and hasn't been generated since then + bool CanUnload(void); + + bool IsLightValid(void) const {return m_IsLightValid; } + + /* + To save a chunk, the WSSchema must: + 1. Mark the chunk as being saved (MarkSaving() ) + 2. Get the chunk's data using GetAllData() + 3. Mark the chunk as saved (MarkSaved() ) + If anywhere inside this sequence another thread mmodifies the chunk, the chunk will not get marked as saved in MarkSaved() + */ + void MarkSaving(void); // Marks the chunk as being saved. + void MarkSaved(void); // Marks the chunk as saved, if it didn't change from the last call to MarkSaving() + void MarkLoaded(void); // Marks the chunk as freshly loaded. Fails if the chunk is already valid + void MarkLoadFailed(void); // Marks the chunk as failed to load. Ignored is the chunk is already valid + + /// Gets all chunk data, calls the a_Callback's methods for each data type + void GetAllData(cChunkDataCallback & a_Callback); + + /// Sets all chunk data + void SetAllData( + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap & a_BiomeMap, + cBlockEntityList & a_BlockEntities + ); + + void SetLight( + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight + ); + + /// Copies m_BlockData into a_BlockTypes, only the block types + void GetBlockTypes(BLOCKTYPE * a_BlockTypes); + + /// Writes the specified cBlockArea at the coords specified. Note that the coords may extend beyond the chunk! + void WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); + + /// Returns true if there is a block entity at the coords specified + bool HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Sets or resets the internal flag that prevents chunk from being unloaded + void Stay(bool a_Stay = true); + + /// Recence all mobs proximities to players in order to know what to do with them + void CollectMobCensus(cMobCensus& toFill); + + /// Try to Spawn Monsters inside chunk + void SpawnMobs(cMobSpawner& a_MobSpawner); + + void Tick(float a_Dt); + + int GetPosX(void) const { return m_PosX; } + int GetPosY(void) const { return m_PosY; } + int GetPosZ(void) const { return m_PosZ; } + + cWorld * GetWorld(void) const { return m_World; } + + void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); + // SetBlock() does a lot of work (heightmap, tickblocks, blockentities) so a BlockIdx version doesn't make sense + void SetBlock( const Vector3i & a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { SetBlock( a_RelBlockPos.x, a_RelBlockPos.y, a_RelBlockPos.z, a_BlockType, a_BlockMeta ); } + + /// Queues a block change till the specified world tick + void QueueSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick); + + /// Queues block for ticking (m_ToTickQueue) + void QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ); + + /// Queues all 6 neighbors of the specified block for ticking (m_ToTickQueue). If any are outside the chunk, relays the checking to the proper neighboring chunk + void QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ); + + void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc. + BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const; + BLOCKTYPE GetBlock(int a_BlockIdx) const; + void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); + + /** Returns the chunk into which the specified block belongs, by walking the neighbors. + Will return self if appropriate. Returns NULL if not reachable through neighbors. + */ + cChunk * GetNeighborChunk(int a_BlockX, int a_BlockZ); + + /** + Returns the chunk into which the relatively-specified block belongs, by walking the neighbors. + Will return self if appropriate. Returns NULL if not reachable through neighbors. + */ + cChunk * GetRelNeighborChunk(int a_RelX, int a_RelZ); + + /** + Returns the chunk into which the relatively-specified block belongs. + Also modifies the relative coords from this-relative to return-relative. + Will return self if appropriate. + Will try walking the neighbors first; if that fails, will query the chunkmap + */ + cChunk * GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const; + + EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); } + + void CollectPickupsByPlayer(cPlayer * a_Player); + + /// Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk + bool SetSignLines(int a_RelX, int a_RelY, int a_RelZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + + int GetHeight( int a_X, int a_Z ); + + void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client); + + /// Adds a client to the chunk; returns true if added, false if already there + bool AddClient (cClientHandle* a_Client ); + + void RemoveClient (cClientHandle* a_Client ); + bool HasClient (cClientHandle* a_Client ); + bool HasAnyClients(void); // Returns true if theres any client in the chunk; false otherwise + + void AddEntity(cEntity * a_Entity); + void RemoveEntity(cEntity * a_Entity); + bool HasEntity(int a_EntityID); + + /// Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. + bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult); // Lua-accessible + + /// Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true + bool ForEachChest(cChestCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each dispenser; returns true if all dispensers processed, false if the callback aborted by returning true + bool ForEachDispenser(cDispenserCallback & a_Callback); + + /// Calls the callback for each dropper; returns true if all droppers processed, false if the callback aborted by returning true + bool ForEachDropper(cDropperCallback & a_Callback); + + /// Calls the callback for each dropspenser; returns true if all dropspensers processed, false if the callback aborted by returning true + bool ForEachDropSpenser(cDropSpenserCallback & a_Callback); + + /// Calls the callback for each furnace; returns true if all furnaces processed, false if the callback aborted by returning true + bool ForEachFurnace(cFurnaceCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found + bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found + bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found + bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found + bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); + + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found + bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible + + /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found + bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible + + void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords + + void CalculateLighting(); // Recalculate right now + void CalculateHeightmap(); + + // Broadcast various packets to all clients of this chunk: + // (Please keep these alpha-sorted) + void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle); + void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityMetadata (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerAnimation (const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 + void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); + void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); + + void SendBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); + + Vector3i PositionToWorldPosition(const Vector3i & a_RelPos) + { + return PositionToWorldPosition(a_RelPos.x, a_RelPos.y, a_RelPos.z); + } + + void PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ); + Vector3i PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ ); + + inline void MarkDirty(void) + { + m_IsDirty = true; + m_IsSaving = false; + } + + /// Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call + inline void SetNextBlockTick(int a_RelX, int a_RelY, int a_RelZ) + { + m_BlockTickX = a_RelX; + m_BlockTickY = a_RelY; + m_BlockTickZ = a_RelZ; + } + + inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); } + inline NIBBLETYPE GetMeta(int a_BlockIdx) const {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); } + inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); } + inline void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_BlockIdx, a_Meta); } + + inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); } + inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); } + inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); } + inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx); } + + /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + + /// Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const; + + /// Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const; + + /// Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight) const; + + /// Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_SkyLight) const; + + /// Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const; + + /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case), ignores unsuccessful attempts + void UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ); + + /// Light alterations based on time + NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const; + + + // Simulator data: + cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; } + cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; } + cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; } + cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; } + + cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); } + +private: + + friend class cChunkMap; + + struct sSetBlockQueueItem + { + int m_RelX, m_RelY, m_RelZ; + BLOCKTYPE m_BlockType; + NIBBLETYPE m_BlockMeta; + Int64 m_Tick; + + sSetBlockQueueItem(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick) : + m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ), m_BlockType(a_BlockType), m_BlockMeta(a_BlockMeta), m_Tick(a_Tick) + { + } + } ; + + typedef std::vector sSetBlockQueueVector; + + + bool m_IsValid; // True if the chunk is loaded / generated + bool m_IsLightValid; // True if the blocklight and skylight are calculated + bool m_IsDirty; // True if the chunk has changed since it was last saved + bool m_IsSaving; // True if the chunk is being saved + bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then + + std::vector m_ToTickBlocks; + sSetBlockVector m_PendingSendBlocks; ///< Blocks that have changed and need to be sent to all clients + + sSetBlockQueueVector m_SetBlockQueue; ///< Block changes that are queued to a specific tick + + // A critical section is not needed, because all chunk access is protected by its parent ChunkMap's csLayers + cClientHandleList m_LoadedByClient; + cClientHandleList m_UnloadQuery; + cEntityList m_Entities; + cBlockEntityList m_BlockEntities; + + /// Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded + int m_StayCount; + + int m_PosX, m_PosY, m_PosZ; + cWorld * m_World; + cChunkMap * m_ChunkMap; + + // TODO: Make these pointers and don't allocate what isn't needed + BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks]; + NIBBLETYPE m_BlockMeta [cChunkDef::NumBlocks / 2]; + NIBBLETYPE m_BlockLight [cChunkDef::NumBlocks / 2]; + NIBBLETYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2]; + + cChunkDef::HeightMap m_HeightMap; + cChunkDef::BiomeMap m_BiomeMap; + + int m_BlockTickX, m_BlockTickY, m_BlockTickZ; + + cChunk * m_NeighborXM; // Neighbor at [X - 1, Z] + cChunk * m_NeighborXP; // Neighbor at [X + 1, Z] + cChunk * m_NeighborZM; // Neighbor at [X, Z - 1] + cChunk * m_NeighborZP; // Neighbor at [X, Z + 1] + + // Per-chunk simulator data: + cFireSimulatorChunkData m_FireSimulatorData; + cFluidSimulatorData * m_WaterSimulatorData; + cFluidSimulatorData * m_LavaSimulatorData; + cSandSimulatorChunkData m_SandSimulatorData; + + + // pick up a random block of this chunk + void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z); + void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ); + + void RemoveBlockEntity(cBlockEntity * a_BlockEntity); + void AddBlockEntity (cBlockEntity * a_BlockEntity); + + void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff); + + /// Creates a block entity for each block that needs a block entity and doesn't have one in the list + void CreateBlockEntities(void); + + /// Wakes up each simulator for its specific blocks; through all the blocks in the chunk + void WakeUpSimulators(void); + + // Makes a copy of the list + cClientHandleList GetAllClients(void) const {return m_LoadedByClient; } + + /// Sends m_PendingSendBlocks to all clients + void BroadcastPendingBlockChanges(void); + + /// Checks the block scheduled for checking in m_ToTickBlocks[] + void CheckBlocks(void); + + /// Ticks several random blocks in the chunk + void TickBlocks(void); + + /// Adds snow to the top of snowy biomes and hydrates farmland / fills cauldrons in rainy biomes + void ApplyWeatherToTop(void); + + /// Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) + void GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); + + /// Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) + void GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); + + /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) + void GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random); + + /// Checks if a leaves block at the specified coords has a log up to 4 blocks away connected by other leaves blocks (false if no log) + bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients + void MoveEntityToNewChunk(cEntity * a_Entity); + + /// Processes all blocks that have been scheduled for replacement by the QueueSetBlock() function + void ProcessQueuedSetBlocks(void); +}; + +typedef cChunk * cChunkPtr; + +typedef std::list cChunkPtrList; + + + + + +#if C_CHUNK_USE_INLINE + #include "Chunk.inl.h" +#endif + + + + diff --git a/src/Chunk.inl.h b/src/Chunk.inl.h new file mode 100644 index 000000000..fb9c4dad1 --- /dev/null +++ b/src/Chunk.inl.h @@ -0,0 +1,34 @@ + +#ifndef __C_CHUNK_INL_H__ +#define __C_CHUNK_INL_H__ + +#ifndef MAX +# define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + + + + + +__C_CHUNK_INLINE__ +void cChunk::SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff) +{ + unsigned char CurrentLight = cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z ); + cChunkDef::SetNibble( a_LightBuffer, a_X-1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X-1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X+1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X+1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y-1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y-1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y+1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y+1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z-1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z-1 ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z+1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z+1 ), MAX(0,CurrentLight-a_Falloff) ) ); + MarkDirty(); +} + + + + + +#endif + + + + diff --git a/src/ChunkDef.h b/src/ChunkDef.h new file mode 100644 index 000000000..d6630df7e --- /dev/null +++ b/src/ChunkDef.h @@ -0,0 +1,617 @@ + +// ChunkDef.h + +// Interfaces to helper types for chunk definitions. Most modules want to include this instead of cChunk.h + + + + + +#pragma once + +#include "Vector3i.h" + + + + + +/** This is really only a placeholder to be used in places where we need to "make up" a chunk's Y coord. +It will help us when the new chunk format comes out and we need to patch everything up for compatibility. +*/ +#define ZERO_CHUNK_Y 0 + +// Used to smoothly convert to new axis ordering. One will be removed when deemed stable. +#define AXIS_ORDER_YZX 1 // Original (1.1-) +#define AXIS_ORDER_XZY 2 // New (1.2+) +#define AXIS_ORDER AXIS_ORDER_XZY + + + + + +// fwd +class cBlockEntity; +class cEntity; +class cClientHandle; +class cBlockEntity; + +typedef std::list cEntityList; +typedef std::list cBlockEntityList; + + + + +// tolua_begin + +/// The datatype used by blockdata +typedef unsigned char BLOCKTYPE; + +/// The datatype used by nibbledata (meta, light, skylight) +typedef unsigned char NIBBLETYPE; + +/// The type used by the heightmap +typedef unsigned char HEIGHTTYPE; + +// tolua_end + + + + + + +// tolua_begin +/** Biome IDs +The first batch corresponds to the clientside biomes, used by MineCraft. +BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client +*/ +enum EMCSBiome +{ + biOcean = 0, + biPlains = 1, + biDesert = 2, + biExtremeHills = 3, + biForest = 4, + biTaiga = 5, + biSwampland = 6, + biRiver = 7, + biHell = 8, // same as Nether + biNether = 8, + biSky = 9, // same as biEnd + biEnd = 9, + biFrozenOcean = 10, + biFrozenRiver = 11, + biIcePlains = 12, + biTundra = 12, // same as Ice Plains + biIceMountains = 13, + biMushroomIsland = 14, + biMushroomShore = 15, + biBeach = 16, + biDesertHills = 17, + biForestHills = 18, + biTaigaHills = 19, + biExtremeHillsEdge = 20, + biJungle = 21, + biJungleHills = 22, + + // Release 1.7 biomes: + biJungleEdge = 23, + biDeepOcean = 24, + biStoneBeach = 25, + biColdBeach = 26, + biBirchForest = 27, + biBirchForestHills = 28, + biRoofedForest = 29, + biColdTaiga = 30, + biColdTaigaHills = 31, + biMegaTaiga = 32, + biMegaTaigaHills = 33, + biExtremeHillsPlus = 34, + biSavanna = 35, + biSavannaPlateau = 36, + biMesa = 37, + biMesaPlateauF = 38, + biMesaPlateau = 39, + + // Automatically capture the maximum consecutive biome value into biMaxBiome: + biNumBiomes, // True number of biomes, since they are zero-based + biMaxBiome = biNumBiomes - 1, // The maximum biome value + + // Add this number to the biomes to get the variant + biVariant = 128, + + // Release 1.7 biome variants: + biSunflowerPlains = 129, + biDesertM = 130, + biExtremeHillsM = 131, + biFlowerForest = 132, + biTaigaM = 133, + biSwamplandM = 134, + biIcePlainsSpikes = 140, + biJungleM = 149, + biJungleEdgeM = 151, + biBirchForestM = 155, + biBirchForestHillsM = 156, + biRoofedForestM = 157, + biColdTaigaM = 158, + biMegaSpruceTaiga = 160, + biMegaSpruceTaigaHills = 161, + biExtremeHillsPlusM = 162, + biSavannaM = 163, + biSavannaPlateauM = 164, + biMesaBryce = 165, + biMesaPlateauFM = 166, + biMesaPlateauM = 167, +} ; + +// tolua_end + + + + +/// Constants used throughout the code, useful typedefs and utility functions +class cChunkDef +{ +public: + static const int Width = 16; + static const int Height = 256; + static const int NumBlocks = Width * Height * Width; + static const int BlockDataSize = NumBlocks * 2 + (NumBlocks / 2); // 2.5 * numblocks + + // Offsets to individual components in the joined blockdata array + static const int MetaOffset = NumBlocks; + static const int LightOffset = MetaOffset + NumBlocks / 2; + static const int SkyLightOffset = LightOffset + NumBlocks / 2; + + static const unsigned int INDEX_OUT_OF_RANGE = 0xffffffff; + + /// The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the highest non-air block in the column + typedef HEIGHTTYPE HeightMap[Width * Width]; + + /** The type used for any biomemap operations and storage inside MCServer, + using MCServer biomes (need not correspond to client representation!) + idx = x + Width * z // Need to verify this with the protocol spec, currently unknown! + */ + typedef EMCSBiome BiomeMap[Width * Width]; + + /// The type used for block type operations and storage, AXIS_ORDER ordering + typedef BLOCKTYPE BlockTypes[NumBlocks]; + + /// The type used for block data in nibble format, AXIS_ORDER ordering + typedef NIBBLETYPE BlockNibbles[NumBlocks / 2]; + + + /// Converts absolute block coords into relative (chunk + block) coords: + inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ ) + { + BlockToChunk(a_X, a_Z, a_ChunkX, a_ChunkZ); + + a_X = a_X - a_ChunkX * Width; + a_Z = a_Z - a_ChunkZ * Width; + } + + + /// Converts absolute block coords to chunk coords: + inline static void BlockToChunk(int a_X, int a_Z, int & a_ChunkX, int & a_ChunkZ) + { + a_ChunkX = a_X / Width; + if ((a_X < 0) && (a_X % Width != 0)) + { + a_ChunkX--; + } + a_ChunkZ = a_Z / cChunkDef::Width; + if ((a_Z < 0) && (a_Z % Width != 0)) + { + a_ChunkZ--; + } + } + + + inline static unsigned int MakeIndex(int x, int y, int z ) + { + if ( + (x < Width) && (x > -1) && + (y < Height) && (y > -1) && + (z < Width) && (z > -1) + ) + { + return MakeIndexNoCheck(x, y, z); + } + ASSERT(!"cChunkDef::MakeIndex(): coords out of chunk range!"); + return INDEX_OUT_OF_RANGE; + } + + + inline static unsigned int MakeIndexNoCheck(int x, int y, int z) + { + #if AXIS_ORDER == AXIS_ORDER_XZY + // For some reason, NOT using the Horner schema is faster. Weird. + return x + (z * cChunkDef::Width) + (y * cChunkDef::Width * cChunkDef::Width); // 1.2 is XZY + #elif AXIS_ORDER == AXIS_ORDER_YZX + return y + (z * cChunkDef::Width) + (x * cChunkDef::Height * cChunkDef::Width); // 1.1 is YZX + #endif + } + + + inline static Vector3i IndexToCoordinate( unsigned int index ) + { + #if AXIS_ORDER == AXIS_ORDER_XZY + return Vector3i( // 1.2 + index % cChunkDef::Width, // X + index / (cChunkDef::Width * cChunkDef::Width), // Y + (index / cChunkDef::Width) % cChunkDef::Width // Z + ); + #elif AXIS_ORDER == AXIS_ORDER_YZX + return Vector3i( // 1.1 + index / (cChunkDef::Height * cChunkDef::Width), // X + index % cChunkDef::Height, // Y + (index / cChunkDef::Height) % cChunkDef::Width // Z + ); + #endif + } + + + inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type) + { + ASSERT((a_X >= 0) && (a_X < Width)); + ASSERT((a_Y >= 0) && (a_Y < Height)); + ASSERT((a_Z >= 0) && (a_Z < Width)); + a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)] = a_Type; + } + + + inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_Index, BLOCKTYPE a_Type) + { + ASSERT((a_Index >= 0) && (a_Index <= NumBlocks)); + a_BlockTypes[a_Index] = a_Type; + } + + + inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z) + { + ASSERT((a_X >= 0) && (a_X < Width)); + ASSERT((a_Y >= 0) && (a_Y < Height)); + ASSERT((a_Z >= 0) && (a_Z < Width)); + return a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)]; + } + + + inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_Idx) + { + ASSERT((a_Idx >= 0) && (a_Idx < NumBlocks)); + return a_BlockTypes[a_Idx]; + } + + + inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z) + { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); + return a_HeightMap[a_X + Width * a_Z]; + } + + + inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height) + { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); + a_HeightMap[a_X + Width * a_Z] = a_Height; + } + + + inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z) + { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); + return a_BiomeMap[a_X + Width * a_Z]; + } + + + inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome) + { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); + a_BiomeMap[a_X + Width * a_Z] = a_Biome; + } + + + static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int a_BlockIdx) + { + if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks)) + { + return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f; + } + ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!"); + return 0; + } + + + static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z) + { + if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1)) + { + int Index = MakeIndexNoCheck(x, y, z); + return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f; + } + ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!"); + return 0; + } + + + static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble) + { + if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks)) + { + ASSERT(!"cChunkDef::SetNibble(): index out of range!"); + return; + } + a_Buffer[a_BlockIdx / 2] = ( + (a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble + ((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set + ); + } + + + static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble) + { + if ( + (x >= Width) || (x < 0) || + (y >= Height) || (y < 0) || + (z >= Width) || (z < 0) + ) + { + ASSERT(!"cChunkDef::SetNibble(): index out of range!"); + return; + } + + int Index = MakeIndexNoCheck(x, y, z); + a_Buffer[Index / 2] = ( + (a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble + ((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set + ); + } + + + inline static char GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos ) + { + return GetNibble(a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); + } + + + inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, char a_Value ) + { + SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value ); + } + +} ; + + + + + +/** Interface class used for getting data out of a chunk using the GetAllData() function. +Implementation must use the pointers immediately and NOT store any of them for later use +The virtual methods are called in the same order as they're declared here. +*/ +class cChunkDataCallback abstract +{ +public: + /** Called before any other callbacks to inform of the current coords + (only in processes where multiple chunks can be processed, such as cWorld::ForEachChunkInRect()). + If false is returned, the chunk is skipped. + */ + virtual bool Coords(int a_ChunkX, int a_ChunkZ) { UNUSED(a_ChunkX); UNUSED(a_ChunkZ); return true; }; + + /// Called once to provide heightmap data + virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {UNUSED(a_HeightMap); }; + + /// Called once to provide biome data + virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) {UNUSED(a_BiomeMap); }; + + /// Called once to export block types + virtual void BlockTypes (const BLOCKTYPE * a_Type) {UNUSED(a_Type); }; + + /// Called once to export block meta + virtual void BlockMeta (const NIBBLETYPE * a_Meta) {UNUSED(a_Meta); }; + + /// Called once to let know if the chunk lighting is valid. Return value is used to control if BlockLight() and BlockSkyLight() are called next (if true) + virtual bool LightIsValid(bool a_IsLightValid) {UNUSED(a_IsLightValid); return true; }; + + /// Called once to export block light + virtual void BlockLight (const NIBBLETYPE * a_BlockLight) {UNUSED(a_BlockLight); }; + + /// Called once to export sky light + virtual void BlockSkyLight(const NIBBLETYPE * a_SkyLight) {UNUSED(a_SkyLight); }; + + /// Called for each entity in the chunk + virtual void Entity(cEntity * a_Entity) {UNUSED(a_Entity); }; + + /// Called for each blockentity in the chunk + virtual void BlockEntity(cBlockEntity * a_Entity) {UNUSED(a_Entity); }; +} ; + + + + + +/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer +*/ +class cChunkDataCollector : + public cChunkDataCallback +{ +public: + + // Must be unsigned char instead of BLOCKTYPE or NIBBLETYPE, because it houses both. + unsigned char m_BlockData[cChunkDef::BlockDataSize]; + +protected: + + virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override + { + memcpy(m_BlockData, a_BlockTypes, sizeof(cChunkDef::BlockTypes)); + } + + + virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override + { + memcpy(m_BlockData + cChunkDef::NumBlocks, a_BlockMeta, cChunkDef::NumBlocks / 2); + } + + + virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override + { + memcpy(m_BlockData + 3 * cChunkDef::NumBlocks / 2, a_BlockLight, cChunkDef::NumBlocks / 2); + } + + + virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override + { + memcpy(m_BlockData + 2 * cChunkDef::NumBlocks, a_BlockSkyLight, cChunkDef::NumBlocks / 2); + } +} ; + + + + + +/** A simple implementation of the cChunkDataCallback interface that collects all block data into a separate buffers +*/ +class cChunkDataSeparateCollector : + public cChunkDataCallback +{ +public: + + cChunkDef::BlockTypes m_BlockTypes; + cChunkDef::BlockNibbles m_BlockMetas; + cChunkDef::BlockNibbles m_BlockLight; + cChunkDef::BlockNibbles m_BlockSkyLight; + +protected: + + virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override + { + memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes)); + } + + + virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override + { + memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas)); + } + + + virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override + { + memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); + } + + + virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override + { + memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight)); + } +} ; + + + + + +/** Interface class used for comparing clients of two chunks. +Used primarily for entity moving while both chunks are locked. +*/ +class cClientDiffCallback +{ +public: + /// Called for clients that are in Chunk1 and not in Chunk2, + virtual void Removed(cClientHandle * a_Client) = 0; + + /// Called for clients that are in Chunk2 and not in Chunk1. + virtual void Added(cClientHandle * a_Client) = 0; +} ; + + + + + +struct sSetBlock +{ + int x, y, z; + int ChunkX, ChunkZ; + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + + sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); // absolute block position + sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : + x(a_X), y(a_Y), z(a_Z), + ChunkX(a_ChunkX), ChunkZ(a_ChunkZ), + BlockType(a_BlockType), + BlockMeta(a_BlockMeta) + {} +}; + +typedef std::list sSetBlockList; +typedef std::vector sSetBlockVector; + + + + + +class cChunkCoords +{ +public: + int m_ChunkX; + int m_ChunkY; + int m_ChunkZ; + + cChunkCoords(int a_ChunkX, int a_ChunkY, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ) {} + + bool operator == (const cChunkCoords & a_Other) const + { + return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkY == a_Other.m_ChunkY) && (m_ChunkZ == a_Other.m_ChunkZ)); + } +} ; + +typedef std::list cChunkCoordsList; + + + + + +/// Interface class used as a callback for operations that involve chunk coords +class cChunkCoordCallback +{ +public: + virtual void Call(int a_ChunkX, int a_ChunkZ) = 0; +} ; + + + + + +/// Generic template that can store any kind of data together with a triplet of 3 coords: +template class cCoordWithData +{ +public: + int x; + int y; + int z; + X Data; + + cCoordWithData(int a_X, int a_Y, int a_Z) : + x(a_X), y(a_Y), z(a_Z) + { + } + + cCoordWithData(int a_X, int a_Y, int a_Z, const X & a_Data) : + x(a_X), y(a_Y), z(a_Z), Data(a_Data) + { + } +} ; + +// Illegal in C++03: typedef std::list< cCoordWithData > cCoordWithDataList; +typedef cCoordWithData cCoordWithInt; +typedef std::list cCoordWithIntList; +typedef std::vector cCoordWithIntVector; + + + + diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp new file mode 100644 index 000000000..73a16dbb4 --- /dev/null +++ b/src/ChunkMap.cpp @@ -0,0 +1,2668 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChunkMap.h" +#include "World.h" +#include "Root.h" +#include "Entities/Player.h" +#include "Item.h" +#include "Entities/Pickup.h" +#include "Chunk.h" +#include "Generating/Trees.h" // used in cChunkMap::ReplaceTreeBlocks() for tree block discrimination +#include "BlockArea.h" +#include "PluginManager.h" +#include "Entities/TNTEntity.h" +#include "Blocks/BlockHandler.h" +#include "MobCensus.h" +#include "MobSpawner.h" + +#ifndef _WIN32 + #include // abs +#endif + +#include "zlib.h" +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cChunkMap: + +cChunkMap::cChunkMap(cWorld * a_World ) + : m_World( a_World ) +{ +} + + + + + +cChunkMap::~cChunkMap() +{ + cCSLock Lock(m_CSLayers); + while (!m_Layers.empty()) + { + delete m_Layers.back(); + m_Layers.pop_back(); // Must pop, because further chunk deletions query the chunkmap for entities and that would touch deleted data + } +} + + + + + +void cChunkMap::RemoveLayer( cChunkLayer* a_Layer ) +{ + cCSLock Lock(m_CSLayers); + m_Layers.remove(a_Layer); +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) + { + return *itr; + } + } + + // Not found, create new: + cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this); + if (Layer == NULL) + { + LOGERROR("cChunkMap: Cannot create new layer, server out of memory?"); + return NULL; + } + m_Layers.push_back(Layer); + return Layer; +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::FindLayerForChunk(int a_ChunkX, int a_ChunkZ) +{ + const int LayerX = FAST_FLOOR_DIV(a_ChunkX, LAYER_SIZE); + const int LayerZ = FAST_FLOOR_DIV(a_ChunkZ, LAYER_SIZE); + return FindLayer(LayerX, LayerZ); +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::FindLayer(int a_LayerX, int a_LayerZ) +{ + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) + { + return *itr; + } + } // for itr - m_Layers[] + + // Not found + return NULL; +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ) +{ + const int LayerX = FAST_FLOOR_DIV(a_ChunkX, LAYER_SIZE); + const int LayerZ = FAST_FLOOR_DIV(a_ChunkZ, LAYER_SIZE); + return GetLayer(LayerX, LayerZ); +} + + + + + +cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // No need to lock m_CSLayers, since it's already locked by the operation that called us + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) + { + // An error must have occurred, since layers are automatically created if they don't exist + return NULL; + } + + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return NULL; + } + if (!(Chunk->IsValid())) + { + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, true); + } + return Chunk; +} + + + + + +cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // No need to lock m_CSLayers, since it's already locked by the operation that called us + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) + { + // An error must have occurred, since layers are automatically created if they don't exist + return NULL; + } + + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return NULL; + } + if (!(Chunk->IsValid())) + { + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, false); + } + + return Chunk; +} + + + + + +cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // No need to lock m_CSLayers, since it's already locked by the operation that called us + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) + { + // An error must have occurred, since layers are automatically created if they don't exist + return NULL; + } + + return Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); + a_BlockType = Chunk->GetBlock(Index); + a_BlockMeta = Chunk->GetMeta(Index); + return true; +} + + + + + +bool cChunkMap::LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); + a_BlockType = Chunk->GetBlock(Index); + return true; +} + + + + + +bool cChunkMap::LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE & a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); + a_BlockMeta = Chunk->GetMeta(Index); + return true; +} + + + + + +bool cChunkMap::LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + Chunk->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + Chunk->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + cChunkLayer * Layer = FindLayerForChunk(a_ChunkX, a_ChunkZ); + if (Layer == NULL) + { + return NULL; + } + return Layer->FindChunk(a_ChunkX, a_ChunkZ); +} + + + + + +void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastAttachEntity(a_Entity, a_Vehicle); +} + + + + + +void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int x, y, z, ChunkX, ChunkZ; + x = a_BlockX; + y = a_BlockY; + z = a_BlockZ; + cChunkDef::BlockToChunk(x, z, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); +} + + + + + +void cChunkMap::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_blockX, a_blockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastBlockBreakAnimation(a_entityID, a_blockX, a_blockY, a_blockZ, a_stage, a_Exclude); +} + + + + + +void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, 0, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastChunkData(a_Serializer, a_Exclude); +} + + + + + +void cChunkMap::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Pickup.GetChunkX(), ZERO_CHUNK_Y, a_Pickup.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); +} + + + + + +void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastDestroyEntity(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityHeadLook(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityLook(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityMetadata(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityVelocity(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Player.GetChunkX(), ZERO_CHUNK_Y, a_Player.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); +} + + + + + +void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcZ / 8, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); +} + + + + + +void cChunkMap::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_SrcX, a_SrcZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data, a_Exclude); +} + + + + + +void cChunkMap::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastSpawnEntity(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cChunkMap::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cChunkMap::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); +} + + + + + +void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // a_Player rclked block entity at the coords specified, handle it + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +bool cChunkMap::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return false; + } + return a_Callback.Item(Chunk); +} + + + + + +void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk); +} + + + + + +/// Wakes up the simulators for the specified area of blocks +void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ) +{ + cSimulatorManager * SimMgr = m_World->GetSimulatorManager(); + int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ; + cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ); + cChunkDef::BlockToChunk(a_MaxBlockX, a_MaxBlockZ, MaxChunkX, MaxChunkZ); + for (int z = MinChunkZ; z <= MaxChunkZ; z++) + { + int MinZ = std::max(a_MinBlockZ, z * cChunkDef::Width); + int MaxZ = std::min(a_MaxBlockZ, z * cChunkDef::Width + cChunkDef::Width - 1); + for (int x = MinChunkX; x <= MaxChunkX; x++) + { + cChunkPtr Chunk = GetChunkNoGen(x, 0, z); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + continue; + } + int MinX = std::max(a_MinBlockX, x * cChunkDef::Width); + int MaxX = std::min(a_MaxBlockX, x * cChunkDef::Width + cChunkDef::Width - 1); + for (int BlockY = a_MinBlockY; BlockY <= a_MaxBlockY; BlockY++) + { + for (int BlockZ = MinZ; BlockZ <= MaxZ; BlockZ++) + { + for (int BlockX = MinX; BlockX <= MaxX; BlockX++) + { + SimMgr->WakeUp(BlockX, BlockY, BlockZ, Chunk); + } // for BlockX + } // for BlockZ + } // for BlockY + } // for x - chunks + } // for z = chunks +} + + + + + +void cChunkMap::MarkChunkDirty (int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->MarkDirty(); +} + + + + + +void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->MarkSaving(); +} + + + + + +void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->MarkSaved(); +} + + + + + +void cChunkMap::SetChunkData( + int a_ChunkX, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap & a_BiomeMap, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty +) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_BlockEntities); + + if (a_MarkDirty) + { + Chunk->MarkDirty(); + } + + // Notify plugins of the chunk becoming available + cPluginManager::Get()->CallHookChunkAvailable(m_World, a_ChunkX, a_ChunkZ); +} + + + + + +void cChunkMap::ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight +) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->SetLight(a_BlockLight, a_SkyLight); + Chunk->MarkDirty(); +} + + + + + +bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + Chunk->GetAllData(a_Callback); + return true; +} + + + + + +bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + Chunk->GetBlockTypes(a_BlockTypes); + return true; +} + + + + + +bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + return (Chunk != NULL) && Chunk->IsValid(); +} + + + + + +bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + return (Chunk != NULL) && Chunk->HasAnyClients(); +} + + + + + +int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ) +{ + while (true) + { + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ, BlockY = 0; + cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return 0; + } + + if (Chunk->IsValid()) + { + return Chunk->GetHeight(a_BlockX, a_BlockZ); + } + + // The chunk is not valid, wait for it to become valid: + cCSUnlock Unlock(Lock); + m_evtChunkValid.Wait(); + } // while (true) +} + + + + + +bool cChunkMap::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height) +{ + // Returns false if chunk not loaded / generated + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ, BlockY = 0; + cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + a_Height = Chunk->GetHeight(a_BlockX, a_BlockZ); + return true; +} + + + + + +void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) +{ + sSetBlockList Failed; + + // Process all items from a_BlockList, either successfully or by placing into Failed + while (!a_BlockList.empty()) + { + int ChunkX = a_BlockList.front().ChunkX; + int ChunkZ = a_BlockList.front().ChunkZ; + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) + { + if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + { + Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + itr = a_BlockList.erase(itr); + } + else + { + ++itr; + } + } // for itr - a_BlockList[] + } + else + { + // The chunk is not valid, move all blocks within this chunk to Failed + for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) + { + if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + { + Failed.push_back(*itr); + itr = a_BlockList.erase(itr); + } + else + { + ++itr; + } + } // for itr - a_BlockList[] + } + } + + // Return the failed: + std::swap(Failed, a_BlockList); +} + + + + + +void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player) +{ + int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway + int BlockY = (int)(a_Player->GetPosY()); + int BlockZ = (int)(a_Player->GetPosZ()); + int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1); + int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1); + + // We suppose that each player keeps their chunks in memory, therefore it makes little sense to try to re-load or even generate them. + // The only time the chunks are not valid is when the player is downloading the initial world and they should not call this at that moment + + cCSLock Lock(m_CSLayers); + GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player); + + // Check the neighboring chunks as well: + GetChunkNoLoad(OtherChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(ChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); +} + + + + + +BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + } + return 0; +} + + + + + +NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid() ) + { + return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ); + } + return 0; +} + + + + + +NIBBLETYPE cChunkMap::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid() ) + { + return Chunk->GetSkyLight(a_BlockX, a_BlockY, a_BlockZ); + } + return 0; +} + + + + + +NIBBLETYPE cChunkMap::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid() ) + { + return Chunk->GetBlockLight(a_BlockX, a_BlockY, a_BlockZ); + } + return 0; +} + + + + + +void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + // a_BlockXYZ now contains relative coords! + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->SetMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta); + Chunk->MarkDirty(); + Chunk->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, NULL); + } +} + + + + + +void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); + m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk); + } +} + + + + + +void cChunkMap::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->QueueSetBlock(X, Y, Z, a_BlockType, a_BlockMeta, a_Tick); + } +} + + + + + +bool cChunkMap::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->GetBlockTypeMeta(X, Y, Z, a_BlockType, a_BlockMeta); + return true; + } + return false; +} + + + + + +bool cChunkMap::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->GetBlockInfo(X, Y, Z, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); + return true; + } + return false; +} + + + + + +void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) +{ + cCSLock Lock(m_CSLayers); + for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + continue; + } + if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType) + { + Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + } + } +} + + + + + +void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks) +{ + cCSLock Lock(m_CSLayers); + for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + continue; + } + switch (Chunk->GetBlock(itr->x, itr->y, itr->z)) + { + CASE_TREE_OVERWRITTEN_BLOCKS: + { + Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + break; + } + case E_BLOCK_LEAVES: + { + if (itr->BlockType == E_BLOCK_LOG) + { + Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + } + break; + } + } + } // for itr - a_Blocks[] +} + + + + + +EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + return Chunk->GetBiomeAt(X, Z); + } + else + { + return m_World->GetGenerator().GetBiomeAt(a_BlockX, a_BlockZ); + } +} + + + + + +bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) +{ + bool res = true; + cCSLock Lock(m_CSLayers); + for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + if (!a_ContinueOnFailure) + { + return false; + } + res = false; + continue; + } + int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z); + itr->BlockType = Chunk->GetBlock(idx); + itr->BlockMeta = Chunk->GetMeta(idx); + } + return res; +} + + + + + +bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) +{ + int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ; + + cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ ); + + { + cCSLock Lock(m_CSLayers); + cChunkPtr DestChunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((DestChunk == NULL) || !DestChunk->IsValid()) + { + return false; + } + + DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0 ); + m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z, DestChunk); + } + + return true; +} + + + + + +void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk->IsValid()) + { + Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle()); + } +} + + + + + +void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, ZERO_CHUNK_Y, a_ChunkZ1); + if (Chunk1 == NULL) + { + return; + } + cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, ZERO_CHUNK_Y, a_ChunkZ2); + if (Chunk2 == NULL) + { + return; + } + + CompareChunkClients(Chunk1, Chunk2, a_Callback); +} + + + + + +void cChunkMap::CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClientDiffCallback & a_Callback) +{ + cClientHandleList Clients1(a_Chunk1->GetAllClients()); + cClientHandleList Clients2(a_Chunk2->GetAllClients()); + + // Find "removed" clients: + for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) + { + bool Found = false; + for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) + { + if (*itr1 == *itr2) + { + Found = true; + break; + } + } // for itr2 - Clients2[] + if (!Found) + { + a_Callback.Removed(*itr1); + } + } // for itr1 - Clients1[] + + // Find "added" clients: + for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) + { + bool Found = false; + for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) + { + if (*itr1 == *itr2) + { + Found = true; + break; + } + } // for itr1 - Clients1[] + if (!Found) + { + a_Callback.Added(*itr2); + } + } // for itr2 - Clients2[] +} + + + + + +bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return false; + } + return Chunk->AddClient(a_Client); +} + + + + + +void cChunkMap::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->RemoveClient(a_Client); +} + + + + + +void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client) +{ + cCSLock Lock(m_CSLayers); + + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->RemoveClient(a_Client); + } // for itr - m_Layers[] +} + + + + + +void cChunkMap::AddEntity(cEntity * a_Entity) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + LOGWARNING("Entity at %p (%s, ID %d) spawning in a non-existent chunk, the entity is lost.", + a_Entity, a_Entity->GetClass(), a_Entity->GetUniqueID() + ); + return; + } + Chunk->AddEntity(a_Entity); +} + + + + + +bool cChunkMap::HasEntity(int a_UniqueID) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if ((*itr)->HasEntity(a_UniqueID)) + { + return true; + } + } + return false; +} + + + + + +void cChunkMap::RemoveEntity(cEntity * a_Entity) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return; + } + Chunk->RemoveEntity(a_Entity); +} + + + + + +bool cChunkMap::ForEachEntity(cEntityCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if (!(*itr)->ForEachEntity(a_Callback)) + { + return false; + } + } + return true; +} + + + + + +bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachEntity(a_Callback); +} + + + + + +void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlocksAffected) +{ + // Don't explode if outside of Y range (prevents the following test running into unallocated memory): + if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height - 1)) + { + return; + } + + // Don't explode if the explosion center is inside a liquid block: + switch (m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ))) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + return; + } + } + + cBlockArea area; + int bx = (int)floor(a_BlockX); + int by = (int)floor(a_BlockY); + int bz = (int)floor(a_BlockZ); + int ExplosionSizeInt = (int) ceil(a_ExplosionSize); + int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt; + a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt); + int MinY = std::max((int)floor(a_BlockY - ExplosionSizeInt), 0); + int MaxY = std::min((int)ceil(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1); + area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt)); + for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++) + { + for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++) + { + if ((by + y >= cChunkDef::Height) || (by + y < 0)) + { + // Outside of the world + continue; + } + for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++) + { + if ((x * x + y * y + z * z) > ExplosionSizeSq) + { + // Too far away + continue; + } + + BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z); + switch (Block) + { + case E_BLOCK_TNT: + { + // Activate the TNT, with a random fuse between 10 to 30 game ticks + double FuseTime = (double)(10 + m_World->GetTickRandomNumber(20)) / 20; + m_World->SpawnPrimedTNT(a_BlockX + x + 0.5, a_BlockY + y + 0.5, a_BlockZ + z + 0.5, FuseTime); + area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR); + a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); + break; + } + case E_BLOCK_OBSIDIAN: + case E_BLOCK_BEDROCK: + case E_BLOCK_WATER: + case E_BLOCK_LAVA: + { + // These blocks are not affected by explosions + break; + } + + case E_BLOCK_STATIONARY_WATER: + { + // Turn into simulated water: + area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER); + break; + } + + case E_BLOCK_STATIONARY_LAVA: + { + // Turn into simulated lava: + area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA); + break; + } + + case E_BLOCK_AIR: + { + // No pickups for air + break; + } + + default: + { + if (m_World->GetTickRandomNumber(10) == 5) + { + cItems Drops; + cBlockHandler * Handler = BlockHandler(Block); + + Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); + m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z); + } + area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR); + a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); + } + } // switch (BlockType) + } // for z + } // for y + } // for x + area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt); + + // Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391): + WakeUpSimulatorsInArea( + bx - ExplosionSizeInt, bx + ExplosionSizeInt + 1, + MinY, MaxY, + bz - ExplosionSizeInt, bz + ExplosionSizeInt + 1 + ); +} + + + + + +bool cChunkMap::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + bool res = false; + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if ((*itr)->DoWithEntityByID(a_UniqueID, a_Callback, res)) + { + return res; + } + } + return false; +} + + + + + +bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachChest(a_Callback); +} + + + + + +bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachDispenser(a_Callback); +} + + + + + +bool cChunkMap::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachDropper(a_Callback); +} + + + + + +bool cChunkMap::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachDropSpenser(a_Callback); +} + + + + + +bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachFurnace(a_Callback); +} + + + + + +bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithDropperAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +/// Loads the chunk synchronously, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) +bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + { + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + // Internal error + return false; + } + if (Chunk->IsValid()) + { + // Already loaded + return true; + } + if (Chunk->HasLoadFailed()) + { + // Already tried loading and it failed + return false; + } + } + return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() +void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks) +{ + for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) + { + LoadChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + } // for itr - a_Chunks[] +} + + + + + +void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->MarkLoadFailed(); +} + + + + + +bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + return Chunk->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cChunkMap::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) +{ + cCSLock Lock(m_CSLayers); + for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) + { + cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + if (Chunk == NULL) + { + continue; + } + Chunk->Stay(a_Stay); + } +} + + + + + +void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + // Not present + return; + } + Chunk->MarkRegenerating(); +} + + + + + +bool cChunkMap::IsChunkLighted(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + // Not present + return false; + } + return Chunk->IsLightValid(); +} + + + + + +bool cChunkMap::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) +{ + bool Result = true; + cCSLock Lock(m_CSLayers); + for (int z = a_MinChunkZ; z <= a_MaxChunkZ; z++) + { + for (int x = a_MinChunkX; x <= a_MaxChunkX; x++) + { + cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); + if ((Chunk == NULL) || (!Chunk->IsValid())) + { + // Not present / not valid + Result = false; + continue; + } + if (!a_Callback.Coords(x, z)) + { + continue; + } + Chunk->GetAllData(a_Callback); + } + } + return Result; +} + + + + + +bool cChunkMap::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +{ + // Convert block coords to chunks coords: + int MinChunkX, MaxChunkX; + int MinChunkZ, MaxChunkZ; + int MinBlockX = a_MinBlockX; + int MinBlockY = a_MinBlockY; + int MinBlockZ = a_MinBlockZ; + int MaxBlockX = a_MinBlockX + a_Area.GetSizeX(); + int MaxBlockY = a_MinBlockY + a_Area.GetSizeY(); + int MaxBlockZ = a_MinBlockZ + a_Area.GetSizeZ(); + cChunkDef::AbsoluteToRelative(MinBlockX, MinBlockY, MinBlockZ, MinChunkX, MinChunkZ); + cChunkDef::AbsoluteToRelative(MaxBlockX, MaxBlockY, MaxBlockZ, MaxChunkX, MaxChunkZ); + + // Iterate over chunks, write data into each: + bool Result = true; + cCSLock Lock(m_CSLayers); + for (int z = MinChunkZ; z <= MaxChunkZ; z++) + { + for (int x = MinChunkX; x <= MaxChunkX; x++) + { + cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); + if ((Chunk == NULL) || (!Chunk->IsValid())) + { + // Not present / not valid + Result = false; + continue; + } + Chunk->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); + } // for x + } // for z + return Result; +} + + + + + +void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) +{ + a_NumChunksValid = 0; + a_NumChunksDirty = 0; + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + int NumValid = 0, NumDirty = 0; + (*itr)->GetChunkStats(NumValid, NumDirty); + a_NumChunksValid += NumValid; + a_NumChunksDirty += NumDirty; + } // for itr - m_Layers[] +} + + + + + +void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand); + } +} + + + + + +void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + } +} + + + + + +void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + } +} + + + + + +void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + +void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->CollectMobCensus(a_ToFill); + } // for itr - m_Layers +} + + + + + + +void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->SpawnMobs(a_MobSpawner); + } // for itr - m_Layers +} + + + + + +void cChunkMap::Tick(float a_Dt) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->Tick(a_Dt); + } // for itr - m_Layers +} + + + + + +void cChunkMap::UnloadUnusedChunks() +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->UnloadUnusedChunks(); + } // for itr - m_Layers +} + + + + + +void cChunkMap::SaveAllChunks(void) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->Save(); + } // for itr - m_Layers[] +} + + + + + +int cChunkMap::GetNumChunks(void) +{ + cCSLock Lock(m_CSLayers); + int NumChunks = 0; + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + NumChunks += (*itr)->GetNumChunksLoaded(); + } + return NumChunks; +} + + + + + +void cChunkMap::ChunkValidated(void) +{ + m_evtChunkValid.Set(); +} + + + + + +void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + // a_BlockXYZ now contains relative coords! + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->QueueTickBlock(a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cChunkMap::cChunkLayer: + +cChunkMap::cChunkLayer::cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent) + : m_LayerX( a_LayerX ) + , m_LayerZ( a_LayerZ ) + , m_Parent( a_Parent ) + , m_NumChunksLoaded( 0 ) +{ + memset(m_Chunks, 0, sizeof(m_Chunks)); +} + + + + + +cChunkMap::cChunkLayer::~cChunkLayer() +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) + { + delete m_Chunks[i]; + m_Chunks[i] = NULL; // // Must zero out, because further chunk deletions query the chunkmap for entities and that would touch deleted data + } // for i - m_Chunks[] +} + + + + + +cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check + + const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; + const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; + + if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) + { + ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); + return NULL; + } + + int Index = LocalX + LocalZ * LAYER_SIZE; + if (m_Chunks[Index] == NULL) + { + cChunk * neixm = (LocalX > 0) ? m_Chunks[Index - 1] : m_Parent->FindChunk(a_ChunkX - 1, a_ChunkZ); + cChunk * neixp = (LocalX < LAYER_SIZE - 1) ? m_Chunks[Index + 1] : m_Parent->FindChunk(a_ChunkX + 1, a_ChunkZ); + cChunk * neizm = (LocalZ > 0) ? m_Chunks[Index - LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ - 1); + cChunk * neizp = (LocalZ < LAYER_SIZE - 1) ? m_Chunks[Index + LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ + 1); + m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp); + } + return m_Chunks[Index]; +} + + + + + +cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ) +{ + const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; + const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; + + if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) + { + ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); + return NULL; + } + + int Index = LocalX + LocalZ * LAYER_SIZE; + return m_Chunks[Index]; +} + + + + +void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // We do count every Mobs in the world. But we are assuming that every chunk not loaded by any client + // doesn't affect us. Normally they should not have mobs because every "too far" mobs despawn + // If they have (f.i. when player disconnect) we assume we don't have to make them live or despawn + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->CollectMobCensus(a_ToFill); + } + } // for i - m_Chunks[] +} + + + + + + +void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // We only spawn close to players + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->SpawnMobs(a_MobSpawner); + } + } // for i - m_Chunks[] +} + + + +void cChunkMap::cChunkLayer::Tick(float a_Dt) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // Only tick chunks that are valid and have clients: + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->Tick(a_Dt); + } + } // for i - m_Chunks[] +} + + + + + +void cChunkMap::cChunkLayer::RemoveClient(cClientHandle * a_Client) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if (m_Chunks[i] != NULL) + { + m_Chunks[i]->RemoveClient(a_Client); + } + } // for i - m_Chunks[] +} + + + + + +bool cChunkMap::cChunkLayer::ForEachEntity(cEntityCallback & a_Callback) +{ + // Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) + { + if (!m_Chunks[i]->ForEachEntity(a_Callback)) + { + return false; + } + } + } + return true; +} + + + + + +bool cChunkMap::cChunkLayer::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackReturn) +{ + // Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) + { + if (m_Chunks[i]->DoWithEntityByID(a_EntityID, a_Callback, a_CallbackReturn)) + { + return true; + } + } + } + return false; +} + + + + + +bool cChunkMap::cChunkLayer::HasEntity(int a_EntityID) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) + { + if (m_Chunks[i]->HasEntity(a_EntityID)) + { + return true; + } + } + } + return false; +} + + + + + +int cChunkMap::cChunkLayer::GetNumChunksLoaded(void) const +{ + int NumChunks = 0; + for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) + { + if (m_Chunks[i] != NULL) + { + NumChunks++; + } + } // for i - m_Chunks[] + return NumChunks; +} + + + + + +void cChunkMap::cChunkLayer::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const +{ + int NumValid = 0; + int NumDirty = 0; + for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) + { + if (m_Chunks[i] == NULL) + { + continue; + } + NumValid++; + if (m_Chunks[i]->IsDirty()) + { + NumDirty++; + } + } // for i - m_Chunks[] + a_NumChunksValid = NumValid; + a_NumChunksDirty = NumDirty; +} + + + + + +void cChunkMap::cChunkLayer::Save(void) +{ + cWorld * World = m_Parent->GetWorld(); + for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) + { + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->IsDirty()) + { + World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosY(), m_Chunks[i]->GetPosZ()); + } + } // for i - m_Chunks[] +} + + + + + +void cChunkMap::cChunkLayer::UnloadUnusedChunks(void) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if ( + (m_Chunks[i] != NULL) && // Is valid + (m_Chunks[i]->CanUnload()) && // Can unload + !cPluginManager::Get()->CallHookChunkUnloading(m_Parent->GetWorld(), m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosZ()) // Plugins agree + ) + { + // The cChunk destructor calls our GetChunk() while removing its entities + // so we still need to be able to return the chunk. Therefore we first delete, then NULLify + // Doing otherwise results in bug http://forum.mc-server.org/showthread.php?tid=355 + delete m_Chunks[i]; + m_Chunks[i] = NULL; + } + } // for i - m_Chunks[] +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkStay: + +cChunkStay::cChunkStay(cWorld * a_World) : + m_World(a_World), + m_IsEnabled(false) +{ +} + + + + + +cChunkStay::~cChunkStay() +{ + Clear(); +} + + + + + +void cChunkStay::Clear(void) +{ + if (m_IsEnabled) + { + Disable(); + } + m_Chunks.clear(); +} + + + + + +void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + ASSERT(!m_IsEnabled); + + for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) + { + // Already present + return; + } + } // for itr - Chunks[] + m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); +} + + + + + +void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + ASSERT(!m_IsEnabled); + + for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) + { + // Found, un-"stay" + m_Chunks.erase(itr); + return; + } + } // for itr - m_Chunks[] +} + + + + + +void cChunkStay::Enable(void) +{ + ASSERT(!m_IsEnabled); + + m_World->ChunksStay(*this, true); + m_IsEnabled = true; +} + + + + + +void cChunkStay::Load(void) +{ + for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) + { + m_World->TouchChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + } // for itr - m_Chunks[] +} + + + + + +void cChunkStay::Disable(void) +{ + ASSERT(m_IsEnabled); + + m_World->ChunksStay(*this, false); + m_IsEnabled = false; +} + + + + diff --git a/src/ChunkMap.h b/src/ChunkMap.h new file mode 100644 index 000000000..f68cb6472 --- /dev/null +++ b/src/ChunkMap.h @@ -0,0 +1,432 @@ + +// cChunkMap.h + +// Interfaces to the cChunkMap class representing the chunk storage for a single world + +#pragma once + +#include "ChunkDef.h" + + + + + +class cWorld; +class cItem; +class MTRand; +class cChunkStay; +class cChunk; +class cPlayer; +class cChestEntity; +class cDispenserEntity; +class cDropperEntity; +class cDropSpenserEntity; +class cFurnaceEntity; +class cPawn; +class cPickup; +class cChunkDataSerializer; +class cBlockArea; +class cMobCensus; +class cMobSpawner; + +typedef std::list cClientHandleList; +typedef cChunk * cChunkPtr; +typedef cItemCallback cEntityCallback; +typedef cItemCallback cChestCallback; +typedef cItemCallback cDispenserCallback; +typedef cItemCallback cDropperCallback; +typedef cItemCallback cDropSpenserCallback; +typedef cItemCallback cFurnaceCallback; +typedef cItemCallback cChunkCallback; + + + + + +class cChunkMap +{ +public: + + static const int LAYER_SIZE = 32; + + cChunkMap(cWorld* a_World ); + ~cChunkMap(); + + // Broadcast respective packets to all clients of the chunk where the event is taking place + // (Please keep these alpha-sorted) + void BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle); + void BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude); + void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 + void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); + void BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); + + /// Sends the block entity, if it is at the coords specified, to a_Client + void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); + + /// a_Player rclked block entity at the coords specified, handle it + void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); + + /// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback + bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback); + + /// Wakes up simulators for the specified block + void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Wakes up the simulators for the specified area of blocks + void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ); + + void MarkChunkDirty (int a_ChunkX, int a_ChunkZ); + void MarkChunkSaving (int a_ChunkX, int a_ChunkZ); + void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); + + /** Sets the chunk data as either loaded from the storage or generated. + a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. + a_BiomeMap is optional, if not present, biomes will be calculated by the generator + a_HeightMap is optional, if not present, will be calculated. + If a_MarkDirty is set, the chunk is set as dirty (used after generating) + */ + void SetChunkData( + int a_ChunkX, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap & a_BiomeMap, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty + ); + + void ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight + ); + + bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback); + + /// Copies the chunk's blocktypes into a_Blocks; returns true if successful + bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks); + + bool IsChunkValid (int a_ChunkX, int a_ChunkZ); + bool HasChunkAnyClients (int a_ChunkX, int a_ChunkZ); + int GetHeight (int a_BlockX, int a_BlockZ); // Waits for the chunk to get loaded / generated + bool TryGetHeight (int a_BlockX, int a_BlockZ, int & a_Height); // Returns false if chunk not loaded / generated + void FastSetBlocks (sSetBlockList & a_BlockList); + void CollectPickupsByPlayer(cPlayer * a_Player); + + BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ); + void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockMeta); + void SetBlock (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta); + void QueueSetBlock (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick); + bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); + + /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType + void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); + + /// Special function used for growing trees, replaces only blocks that tree may overwrite + void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks); + + EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ); + + /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. + bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); + + bool DigBlock (int a_X, int a_Y, int a_Z); + void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player); + + /// Compares clients of two chunks, calls the callback accordingly + void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback); + + /// Compares clients of two chunks, calls the callback accordingly + void CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClientDiffCallback & a_Callback); + + /// Adds client to a chunk, if not already present; returns true if added, false if present + bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the client from the chunk + void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the client from all chunks it is present in + void RemoveClientFromChunks(cClientHandle * a_Client); + + /// Adds the entity to its appropriate chunk, takes ownership of the entity pointer + void AddEntity(cEntity * a_Entity); + + /// Returns true if the entity with specified ID is present in the chunks + bool HasEntity(int a_EntityID); + + /// Removes the entity from its appropriate chunk + void RemoveEntity(cEntity * a_Entity); + + /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible + + /// Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates + void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected); + + /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. + bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true + bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true + bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback); + + /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true + bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback); + + /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true + bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback); + + /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true + bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found + bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found + bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found + bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found + bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found + bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible + + /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found + bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible + + /// Touches the chunk, causing it to be loaded or generated + void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) + bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() + void LoadChunks(const cChunkCoordsList & a_Chunks); + + /// Marks the chunk as failed-to-load + void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Sets the sign text. Returns true if sign text changed. + bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + + /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable; to be used only by cChunkStay! + void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); + + /// Marks the chunk as being regenerated - all its clients want that chunk again (used by cWorld::RegenerateChunk() ) + void MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ); + + bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); + + /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully + bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); + + /// Writes the block area into the specified coords. Returns true if all chunks have been processed. Prefer cBlockArea::Write() instead. + bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); + + /// Returns the number of valid chunks and the number of dirty chunks + void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty); + + /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) + void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand); + + /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config + void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config + void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call + void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player + void CollectMobCensus(cMobCensus& a_ToFill); + + /// Try to Spawn Monsters inside all Chunks + void SpawnMobs(cMobSpawner& a_MobSpawner); + + void Tick(float a_Dt); + + void UnloadUnusedChunks(void); + void SaveAllChunks(void); + + cWorld * GetWorld(void) { return m_World; } + + int GetNumChunks(void); + + void ChunkValidated(void); // Called by chunks that have become valid + + /// Queues the specified block for ticking (block update) + void QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Returns the CS for locking the chunkmap; only cWorld::cLock may use this function! + cCriticalSection & GetCS(void) { return m_CSLayers; } + +private: + + friend class cChunk; // The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock() + + class cChunkLayer + { + public: + cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent); + ~cChunkLayer(); + + /// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check + cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ); + + /// Returns the specified chunk, or NULL if not created yet + cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); + + int GetX(void) const {return m_LayerX; } + int GetZ(void) const {return m_LayerZ; } + + int GetNumChunksLoaded(void) const ; + + void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const; + + void Save(void); + void UnloadUnusedChunks(void); + + /// Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player + void CollectMobCensus(cMobCensus& a_ToFill); + /// Try to Spawn Monsters inside all Chunks + void SpawnMobs(cMobSpawner& a_MobSpawner); + + void Tick(float a_Dt); + + void RemoveClient(cClientHandle * a_Client); + + /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. + bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackReturn); // Lua-accessible + + /// Returns true if there is an entity with the specified ID within this layer's chunks + bool HasEntity(int a_EntityID); + + protected: + + cChunkPtr m_Chunks[LAYER_SIZE * LAYER_SIZE]; + int m_LayerX; + int m_LayerZ; + cChunkMap * m_Parent; + int m_NumChunksLoaded; + }; + + typedef std::list cChunkLayerList; + + /// Finds the cChunkLayer object responsible for the specified chunk; returns NULL if not found. Assumes m_CSLayers is locked. + cChunkLayer * FindLayerForChunk(int a_ChunkX, int a_ChunkZ); + + /// Returns the specified cChunkLayer object; returns NULL if not found. Assumes m_CSLayers is locked. + cChunkLayer * FindLayer(int a_LayerX, int a_LayerZ); + + /// Returns the cChunkLayer object responsible for the specified chunk; creates it if not found. + cChunkLayer * GetLayerForChunk (int a_ChunkX, int a_ChunkZ); + + /// Returns the specified cChunkLayer object; creates it if not found. + cChunkLayer * GetLayer(int a_LayerX, int a_LayerZ); + + void RemoveLayer(cChunkLayer * a_Layer); + + cCriticalSection m_CSLayers; + cChunkLayerList m_Layers; + cEvent m_evtChunkValid; // Set whenever any chunk becomes valid, via ChunkValidated() + + cWorld * m_World; + + cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid + cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate + cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate + + /// Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + + /// Gets a block type in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType); + + /// Gets a block meta in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE & a_BlockMeta); + + /// Sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSLayers is locked. To be called only from cChunkMap. + cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); +}; + + + + + +/** Makes chunks stay loaded until this object is cleared or destroyed +Works by setting internal flags in the cChunk that it should not be unloaded. +To optimize for speed, cChunkStay has an Enabled flag, it will "stay" the chunks only when enabled and it will refuse manipulations when enabled +The object itself is not made thread-safe, it's supposed to be used from a single thread only. +*/ +class cChunkStay +{ +public: + cChunkStay(cWorld * a_World); + ~cChunkStay(); + + void Clear(void); + + void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + void Enable(void); + void Disable(void); + + /// Queues each chunk in m_Chunks[] for loading / generating + void Load(void); + + // Allow cChunkStay be passed to functions expecting a const cChunkCoordsList & + operator const cChunkCoordsList(void) const {return m_Chunks; } + +protected: + + cWorld * m_World; + + bool m_IsEnabled; + + cChunkCoordsList m_Chunks; +} ; + + + + diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp new file mode 100644 index 000000000..005cfe29d --- /dev/null +++ b/src/ChunkSender.cpp @@ -0,0 +1,295 @@ + +// ChunkSender.cpp + +// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients + + + + + +#include "Globals.h" +#include "ChunkSender.h" +#include "World.h" +#include "BlockEntities/BlockEntity.h" +#include "Protocol/ChunkDataSerializer.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNotifyChunkSender: + +void cNotifyChunkSender::Call(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkSender->ChunkReady(a_ChunkX, a_ChunkZ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkSender: + +cChunkSender::cChunkSender(void) : + super("ChunkSender"), + m_World(NULL), + m_Notify(NULL), + m_RemoveCount(0) +{ + m_Notify.SetChunkSender(this); +} + + + + + +cChunkSender::~cChunkSender() +{ + Stop(); +} + + + + + +bool cChunkSender::Start(cWorld * a_World) +{ + m_ShouldTerminate = false; + m_World = a_World; + return super::Start(); +} + + + + + +void cChunkSender::Stop(void) +{ + m_ShouldTerminate = true; + m_evtQueue.Set(); + Wait(); +} + + + + + +void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ) +{ + // This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check + { + cCSLock Lock(m_CS); + m_ChunksReady.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + } + m_evtQueue.Set(); +} + + + + + +void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + ASSERT(a_Client != NULL); + { + cCSLock Lock(m_CS); + if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)) != m_SendChunks.end()) + { + // Already queued, bail out + return; + } + m_SendChunks.push_back(sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)); + } + m_evtQueue.Set(); +} + + + + + +void cChunkSender::RemoveClient(cClientHandle * a_Client) +{ + { + cCSLock Lock(m_CS); + for (sSendChunkList::iterator itr = m_SendChunks.begin(); itr != m_SendChunks.end();) + { + if (itr->m_Client == a_Client) + { + itr = m_SendChunks.erase(itr); + continue; + } + ++itr; + } // for itr - m_SendChunks[] + m_RemoveCount++; + } + m_evtQueue.Set(); + m_evtRemoved.Wait(); // Wait for removal confirmation +} + + + + + +void cChunkSender::Execute(void) +{ + while (!m_ShouldTerminate) + { + cCSLock Lock(m_CS); + while (m_ChunksReady.empty() && m_SendChunks.empty()) + { + int RemoveCount = m_RemoveCount; + m_RemoveCount = 0; + cCSUnlock Unlock(Lock); + for (int i = 0; i < RemoveCount; i++) + { + m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted + } + m_evtQueue.Wait(); + if (m_ShouldTerminate) + { + return; + } + } // while (empty) + + if (!m_ChunksReady.empty()) + { + // Take one from the queue: + cChunkCoords Coords(m_ChunksReady.front()); + m_ChunksReady.pop_front(); + Lock.Unlock(); + + SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL); + } + else + { + // Take one from the queue: + sSendChunk Chunk(m_SendChunks.front()); + m_SendChunks.pop_front(); + Lock.Unlock(); + + SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client); + } + Lock.Lock(); + int RemoveCount = m_RemoveCount; + m_RemoveCount = 0; + Lock.Unlock(); + for (int i = 0; i < RemoveCount; i++) + { + m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted + } + } // while (!mShouldTerminate) +} + + + + + +void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + ASSERT(m_World != NULL); + + // Ask the client if it still wants the chunk: + if (a_Client != NULL) + { + if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ)) + { + return; + } + } + + // If the chunk has no clients, no need to packetize it: + if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ)) + { + return; + } + + // If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating + if (!m_World->IsChunkValid(a_ChunkX, a_ChunkZ)) + { + return; + } + + // If the chunk is not lighted, queue it for relighting and get notified when it's ready: + if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ)) + { + m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, &m_Notify); + return; + } + + // Query and prepare chunk data: + if (!m_World->GetChunkData(a_ChunkX, a_ChunkZ, *this)) + { + return; + } + cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap); + + // Send: + if (a_Client == NULL) + { + m_World->BroadcastChunkData(a_ChunkX, a_ChunkZ, Data); + } + else + { + a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data); + } + + // Send block-entity packets: + for (sBlockCoords::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if (a_Client == NULL) + { + m_World->BroadcastBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ); + } + else + { + m_World->SendBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ, *a_Client); + } + } // for itr - m_Packets[] + m_BlockEntities.clear(); + + // TODO: Send entity spawn packets +} + + + + + +void cChunkSender::BlockEntity(cBlockEntity * a_Entity) +{ + m_BlockEntities.push_back(sBlockCoord(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ())); +} + + + + +void cChunkSender::Entity(cEntity * a_Entity) +{ + // Nothing needed yet, perhaps in the future when we save entities into chunks we'd like to send them upon load, too ;) +} + + + + + +void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) +{ + for (int i = 0; i < ARRAYCOUNT(m_BiomeMap); i++) + { + if ((*a_BiomeMap)[i] < 255) + { + // Normal MC biome, copy as-is: + m_BiomeMap[i] = (unsigned char)((*a_BiomeMap)[i]); + } + else + { + // TODO: MCS-specific biome, need to map to some basic MC biome: + ASSERT(!"Unimplemented MCS-specific biome"); + } + } // for i - m_BiomeMap[] +} + + + + diff --git a/src/ChunkSender.h b/src/ChunkSender.h new file mode 100644 index 000000000..a26f764a7 --- /dev/null +++ b/src/ChunkSender.h @@ -0,0 +1,169 @@ + +// ChunkSender.h + +// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients + +/* +The whole thing is a thread that runs in a loop, waiting for either: + "finished chunks" (ChunkReady()), or + "chunks to send" (QueueSendChunkTo() ) +to come to a queue. +And once they do, it requests the chunk data and sends it all away, either + broadcasting (ChunkReady), or + sends to a specific client (QueueSendChunkTo) +Chunk data is queried using the cChunkDataCallback interface. +It is cached inside the ChunkSender object during the query and then processed after the query ends. +Note that the data needs to be compressed only *after* the query finishes, +because the query callbacks run with ChunkMap's CS locked. + +A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient(); +this ensures that the client's Send() won't be called anymore by ChunkSender. +Note that it may be called by world's BroadcastToChunk() if the client is still in the chunk. +*/ + + + +#pragma once + +#include "OSSupport/IsThread.h" +#include "ChunkDef.h" + + + + + +class cWorld; +class cClientHandle; + + + + + +// fwd: +class cChunkSender; + + + + + +/// Callback that can be used to notify chunk sender upon another chunkcoord notification +class cNotifyChunkSender : + public cChunkCoordCallback +{ + virtual void Call(int a_ChunkX, int a_ChunkZ) override; + + cChunkSender * m_ChunkSender; +public: + cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {} + + void SetChunkSender(cChunkSender * a_ChunkSender) + { + m_ChunkSender = a_ChunkSender; + } +} ; + + + + + +class cChunkSender: + public cIsThread, + public cChunkDataSeparateCollector +{ + typedef cIsThread super; +public: + cChunkSender(void); + ~cChunkSender(); + + bool Start(cWorld * a_World); + + void Stop(void); + + /// Notifies that a chunk has become ready and it should be sent to all its clients + void ChunkReady(int a_ChunkX, int a_ChunkZ); + + /// Queues a chunk to be sent to a specific client + void QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the a_Client from all waiting chunk send operations + void RemoveClient(cClientHandle * a_Client); + +protected: + + /// Used for sending chunks to specific clients + struct sSendChunk + { + int m_ChunkX; + int m_ChunkY; + int m_ChunkZ; + cClientHandle * m_Client; + + sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) : + m_ChunkX(a_ChunkX), + m_ChunkY(a_ChunkY), + m_ChunkZ(a_ChunkZ), + m_Client(a_Client) + { + } + + bool operator ==(const sSendChunk & a_Other) + { + return ( + (a_Other.m_ChunkX == m_ChunkX) && + (a_Other.m_ChunkY == m_ChunkY) && + (a_Other.m_ChunkZ == m_ChunkZ) && + (a_Other.m_Client == m_Client) + ); + } + } ; + typedef std::list sSendChunkList; + + struct sBlockCoord + { + int m_BlockX; + int m_BlockY; + int m_BlockZ; + + sBlockCoord(int a_BlockX, int a_BlockY, int a_BlockZ) : + m_BlockX(a_BlockX), + m_BlockY(a_BlockY), + m_BlockZ(a_BlockZ) + { + } + } ; + + typedef std::vector sBlockCoords; + + cWorld * m_World; + + cCriticalSection m_CS; + cChunkCoordsList m_ChunksReady; + sSendChunkList m_SendChunks; + cEvent m_evtQueue; // Set when anything is added to m_ChunksReady + cEvent m_evtRemoved; // Set when removed clients are safe to be deleted + int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times) + + cNotifyChunkSender m_Notify; // Used for chunks that don't have a valid lighting - they will be re-queued after lightcalc + + // Data about the chunk that is being sent: + // NOTE that m_BlockData[] is inherited from the cChunkDataCollector + unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width]; + sBlockCoords m_BlockEntities; // Coords of the block entities to send + // TODO: sEntityIDs m_Entities; // Entity-IDs of the entities to send + + // cIsThread override: + virtual void Execute(void) override; + + // cChunkDataCollector overrides: + // (Note that they are called while the ChunkMap's CS is locked - don't do heavy calculations here!) + virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) override; + virtual void Entity (cEntity * a_Entity) override; + virtual void BlockEntity (cBlockEntity * a_Entity) override; + + /// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL + void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); +} ; + + + + diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp new file mode 100644 index 000000000..f8fd4a8b7 --- /dev/null +++ b/src/ClientHandle.cpp @@ -0,0 +1,2198 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ClientHandle.h" +#include "Server.h" +#include "World.h" +#include "Entities/Pickup.h" +#include "PluginManager.h" +#include "Entities/Player.h" +#include "Inventory.h" +#include "BlockEntities/ChestEntity.h" +#include "BlockEntities/SignEntity.h" +#include "UI/Window.h" +#include "Item.h" +#include "Piston.h" +#include "Mobs/Monster.h" +#include "ChatColor.h" +#include "OSSupport/Socket.h" +#include "OSSupport/Timer.h" +#include "Items/ItemHandler.h" +#include "Blocks/BlockHandler.h" +#include "Blocks/BlockSlab.h" + +#include "Vector3f.h" +#include "Vector3d.h" + +#include "Root.h" + +#include "Authenticator.h" +#include "MersenneTwister.h" + +#include "Protocol/ProtocolRecognizer.h" + + + + + +#define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\ + case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\ + case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; } + + + + + +/// If the number of queued outgoing packets reaches this, the client will be kicked +#define MAX_OUTGOING_PACKETS 2000 + +/// How many explosions per single game tick are allowed +static const int MAX_EXPLOSIONS_PER_TICK = 100; + +/// How many explosions in the recent history are allowed +static const int MAX_RUNNING_SUM_EXPLOSIONS = cClientHandle::NUM_CHECK_EXPLOSIONS_TICKS * MAX_EXPLOSIONS_PER_TICK / 8; + +/// How many ticks before the socket is closed after the client is destroyed (#31) +static const int TICKS_BEFORE_CLOSE = 20; + + + + + +#define RECI_RAND_MAX (1.f/RAND_MAX) +inline int fRadRand(MTRand & r1, int a_BlockCoord) +{ + return a_BlockCoord * 32 + (int)(16 * ((float)r1.rand() * RECI_RAND_MAX) * 16 - 8); +} + + + + + +int cClientHandle::s_ClientCount = 0; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cClientHandle: + +cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) + : m_ViewDistance(a_ViewDistance) + , m_IPString(a_Socket->GetIPString()) + , m_OutgoingData(64 KiB) + , m_Player(NULL) + , m_HasSentDC(false) + , m_TimeSinceLastPacket(0) + , m_bKeepThreadGoing(true) + , m_Ping(1000) + , m_PingID(1) + , m_TicksSinceDestruction(0) + , m_State(csConnected) + , m_LastStreamedChunkX(0x7fffffff) // bogus chunk coords to force streaming upon login + , m_LastStreamedChunkZ(0x7fffffff) + , m_ShouldCheckDownloaded(false) + , m_UniqueID(0) + , m_BlockDigAnimStage(-1) + , m_HasStartedDigging(false) + , m_CurrentExplosionTick(0) + , m_RunningSumExplosions(0) + , m_HasSentPlayerChunk(false) +{ + m_Protocol = new cProtocolRecognizer(this); + + s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread + m_UniqueID = s_ClientCount; + + cTimer t1; + m_LastPingTime = t1.GetNowTime(); + + LOGD("New ClientHandle created at %p", this); +} + + + + + +cClientHandle::~cClientHandle() +{ + ASSERT(m_State >= csDestroyedWaiting); // Has Destroy() been called? + + LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), this); + + // Remove from cSocketThreads, we're not to be called anymore: + cRoot::Get()->GetServer()->ClientDestroying(this); + + { + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.clear(); + m_ChunksToSend.clear(); + } + + if (m_Player != NULL) + { + cWorld * World = m_Player->GetWorld(); + if (!m_Username.empty() && (World != NULL)) + { + // Send the Offline PlayerList packet: + World->BroadcastPlayerListItem(*m_Player, false, this); + } + if (World != NULL) + { + World->RemovePlayer(m_Player); + m_Player->Destroy(); + } + delete m_Player; + m_Player = NULL; + } + + if (!m_HasSentDC) + { + SendDisconnect("Server shut down? Kthnxbai"); + } + + // Queue all remaining outgoing packets to cSocketThreads: + { + cCSLock Lock(m_CSOutgoingData); + AString Data; + m_OutgoingData.ReadAll(Data); + m_OutgoingData.CommitRead(); + cRoot::Get()->GetServer()->WriteToClient(this, Data); + } + + // Queue the socket to close as soon as it sends all outgoing data: + cRoot::Get()->GetServer()->QueueClientClose(this); + cRoot::Get()->GetServer()->RemoveClient(this); + + delete m_Protocol; + m_Protocol = NULL; + + LOGD("ClientHandle at %p deleted", this); +} + + + + + +void cClientHandle::Destroy(void) +{ + { + cCSLock Lock(m_CSDestroyingState); + if (m_State >= csDestroying) + { + // Already called + return; + } + m_State = csDestroying; + } + + // DEBUG: + LOGD("%s: client %p, \"%s\"", __FUNCTION__, this, m_Username.c_str()); + + if ((m_Player != NULL) && (m_Player->GetWorld() != NULL)) + { + RemoveFromAllChunks(); + m_Player->GetWorld()->RemoveClientFromChunkSender(this); + } + m_State = csDestroyedWaiting; +} + + + + + +void cClientHandle::Kick(const AString & a_Reason) +{ + if (m_State >= csAuthenticating) // Don't log pings + { + LOG("Kicking user \"%s\" for \"%s\"", m_Username.c_str(), StripColorCodes(a_Reason).c_str()); + } + SendDisconnect(a_Reason); +} + + + + + +void cClientHandle::Authenticate(void) +{ + if (m_State != csAuthenticating) + { + return; + } + + ASSERT( m_Player == NULL ); + + // Spawn player (only serversided, so data is loaded) + m_Player = new cPlayer(this, GetUsername()); + + cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName()); + if (World == NULL) + { + World = cRoot::Get()->GetDefaultWorld(); + } + + if (m_Player->GetGameMode() == eGameMode_NotSet) + { + m_Player->LoginSetGameMode(World->GetGameMode()); + } + + m_Player->SetIP (m_IPString); + + cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player); + + m_ConfirmPosition = m_Player->GetPosition(); + + // Return a server login packet + m_Protocol->SendLogin(*m_Player, *World); + + // Send Weather if raining: + if ((World->GetWeather() == 1) || (World->GetWeather() == 2)) + { + m_Protocol->SendWeather(World->GetWeather()); + } + + // Send time + m_Protocol->SendTimeUpdate(World->GetWorldAge(), World->GetTimeOfDay()); + + // Send contents of the inventory window + m_Protocol->SendWholeInventory(*m_Player->GetWindow()); + + // Send health + m_Player->SendHealth(); + + // Send gamemode (1.6.1 movementSpeed): + SendGameMode(m_Player->GetGameMode()); + + m_Player->Initialize(World); + m_State = csAuthenticated; + + // Broadcast this player's spawning to all other players in the same chunk + m_Player->GetWorld()->BroadcastSpawnEntity(*m_Player, this); + + cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); +} + + + + + +void cClientHandle::StreamChunks(void) +{ + if ((m_State < csAuthenticated) || (m_State >= csDestroying)) + { + return; + } + + ASSERT(m_Player != NULL); + + int ChunkPosX = FAST_FLOOR_DIV((int)m_Player->GetPosX(), cChunkDef::Width); + int ChunkPosZ = FAST_FLOOR_DIV((int)m_Player->GetPosZ(), cChunkDef::Width); + if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ)) + { + // Already streamed for this position + return; + } + m_LastStreamedChunkX = ChunkPosX; + m_LastStreamedChunkZ = ChunkPosZ; + + LOGD("Streaming chunks centered on [%d, %d], view distance %d", ChunkPosX, ChunkPosZ, m_ViewDistance); + + cWorld * World = m_Player->GetWorld(); + ASSERT(World != NULL); + + // Remove all loaded chunks that are no longer in range; deferred to out-of-CS: + cChunkCoordsList RemoveChunks; + { + cCSLock Lock(m_CSChunkLists); + for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();) + { + int RelX = (*itr).m_ChunkX - ChunkPosX; + int RelZ = (*itr).m_ChunkZ - ChunkPosZ; + if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) + { + RemoveChunks.push_back(*itr); + itr = m_LoadedChunks.erase(itr); + } + else + { + ++itr; + } + } // for itr - m_LoadedChunks[] + for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();) + { + int RelX = (*itr).m_ChunkX - ChunkPosX; + int RelZ = (*itr).m_ChunkZ - ChunkPosZ; + if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) + { + itr = m_ChunksToSend.erase(itr); + } + else + { + ++itr; + } + } // for itr - m_ChunksToSend[] + } + for (cChunkCoordsList::iterator itr = RemoveChunks.begin(); itr != RemoveChunks.end(); ++itr) + { + World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this); + m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); + } // for itr - RemoveChunks[] + + // Add all chunks that are in range and not yet in m_LoadedChunks: + // Queue these smartly - from the center out to the edge + for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest + { + // For each distance add chunks in a hollow square centered around current position: + for (int i = -d; i <= d; ++i) + { + StreamChunk(ChunkPosX + d, ChunkPosZ + i); + StreamChunk(ChunkPosX - d, ChunkPosZ + i); + } // for i + for (int i = -d + 1; i < d; ++i) + { + StreamChunk(ChunkPosX + i, ChunkPosZ + d); + StreamChunk(ChunkPosX + i, ChunkPosZ - d); + } // for i + } // for d + + // Touch chunks GENERATEDISTANCE ahead to let them generate: + for (int d = m_ViewDistance + 1; d <= m_ViewDistance + GENERATEDISTANCE; ++d) // cycle through (square) distance, from nearest to furthest + { + // For each distance touch chunks in a hollow square centered around current position: + for (int i = -d; i <= d; ++i) + { + World->TouchChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i); + World->TouchChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i); + } // for i + for (int i = -d + 1; i < d; ++i) + { + World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d); + World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d); + } // for i + } // for d +} + + + + +void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ) +{ + if (m_State >= csDestroying) + { + // Don't stream chunks to clients that are being destroyed + return; + } + + cWorld * World = m_Player->GetWorld(); + ASSERT(World != NULL); + + if (World->AddChunkClient(a_ChunkX, a_ChunkZ, this)) + { + { + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + } + World->SendChunkTo(a_ChunkX, a_ChunkZ, this); + } +} + + + + + +// Removes the client from all chunks. Used when switching worlds or destroying the player +void cClientHandle::RemoveFromAllChunks() +{ + cWorld * World = m_Player->GetWorld(); + if (World != NULL) + { + World->RemoveClientFromChunks(this); + } + + { + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.clear(); + m_ChunksToSend.clear(); + + // Also reset the LastStreamedChunk coords to bogus coords, + // so that all chunks are streamed in subsequent StreamChunks() call (FS #407) + m_LastStreamedChunkX = 0x7fffffff; + m_LastStreamedChunkZ = 0x7fffffff; + } +} + + + + + +void cClientHandle::HandlePing(void) +{ + // Somebody tries to retrieve information about the server + AString Reply; + Printf(Reply, "%s%s%i%s%i", + cRoot::Get()->GetServer()->GetDescription().c_str(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetServer()->GetNumPlayers(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetServer()->GetMaxPlayers() + ); + Kick(Reply.c_str()); +} + + + + + +bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Username) +{ + LOGD("LOGIN %s", a_Username.c_str()); + m_Username = a_Username; + + if (cRoot::Get()->GetPluginManager()->CallHookLogin(this, a_ProtocolVersion, a_Username)) + { + Destroy(); + return false; + } + + // Schedule for authentication; until then, let them wait (but do not block) + m_State = csAuthenticating; + cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), m_Protocol->GetAuthServerID()); + return true; +} + + + + + +void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem) +{ + // This is for creative Inventory changes + if (!m_Player->IsGameModeCreative()) + { + LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str()); + return; + } + if (m_Player->GetWindow()->GetWindowType() != cWindow::wtInventory) + { + LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str()); + return; + } + + m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, (a_SlotNum >= 0) ? caLeftClick : caLeftClickOutside, a_HeldItem); +} + + + + + +void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround) +{ + if ((m_Player == NULL) || (m_State != csPlaying)) + { + // The client hasn't been spawned yet and sends nonsense, we know better + return; + } + + /* + // TODO: Invalid stance check + if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) + { + LOGD("Invalid stance"); + SendPlayerMoveLook(); + return; + } + */ + + // If the player has moved too far, "repair" them: + Vector3d Pos(a_PosX, a_PosY, a_PosZ); + if ((m_Player->GetPosition() - Pos).SqrLength() > 100 * 100) + { + LOGD("Too far away (%0.2f), \"repairing\" the client", (m_Player->GetPosition() - Pos).Length()); + SendPlayerMoveLook(); + return; + } + + // If a jump just started, process food exhaustion: + if ((a_PosY > m_Player->GetPosY()) && !a_IsOnGround && m_Player->IsOnGround()) + { + // we only add this exhaustion if the player is not swimming - otherwise we end up with both jump + swim exhaustion + + if (!m_Player->IsSwimming()) + { + m_Player->AddFoodExhaustion(m_Player->IsSprinting() ? 0.8 : 0.2); + } + } + + m_Player->MoveTo(Pos); + m_Player->SetStance(a_Stance); + m_Player->SetTouchGround(a_IsOnGround); +} + + + + + +void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) +{ + LOGD("HandleLeftClick: {%i, %i, %i}; Face: %i; Stat: %i", + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status + ); + + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); + if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status)) + { + // A plugin doesn't agree with the action, replace the block on the client and quit: + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + + if (!CheckBlockInteractionsRate()) + { + // Too many interactions per second, simply ignore. Probably a hacked client, so don't even send bak the block + return; + } + + switch (a_Status) + { + case DIG_STATUS_DROP_HELD: // Drop held item + { + if (PlgMgr->CallHookPlayerTossingItem(*m_Player)) + { + // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch) + return; + } + m_Player->TossItem(false); + return; + } + + case DIG_STATUS_SHOOT_EAT: + { + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); + if (ItemHandler->IsFood()) + { + m_Player->AbortEating(); + return; + } + else + { + if (PlgMgr->CallHookPlayerShooting(*m_Player)) + { + // A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch) + return; + } + ItemHandler->OnItemShoot(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + } + return; + } + + case DIG_STATUS_STARTED: + { + BLOCKTYPE OldBlock; + NIBBLETYPE OldMeta; + m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta); + HandleBlockDigStarted(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta); + return; + } + + case DIG_STATUS_FINISHED: + { + BLOCKTYPE OldBlock; + NIBBLETYPE OldMeta; + m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta); + HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta); + return; + } + + case DIG_STATUS_CANCELLED: + { + // Block breaking cancelled by player + return; + } + + default: + { + ASSERT(!"Unhandled DIG_STATUS"); + return; + } + } // switch (a_Status) +} + + + + + +void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) +{ + if ( + m_HasStartedDigging && + (a_BlockX == m_LastDigBlockX) && + (a_BlockY == m_LastDigBlockY) && + (a_BlockZ == m_LastDigBlockZ) + ) + { + // It is a duplicate packet, drop it right away + return; + } + + if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta)) + { + // A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows: + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + + // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client: + m_HasStartedDigging = true; + m_LastDigBlockX = a_BlockX; + m_LastDigBlockY = a_BlockY; + m_LastDigBlockZ = a_BlockZ; + + if ( + (m_Player->IsGameModeCreative()) || // In creative mode, digging is done immediately + g_BlockOneHitDig[a_OldBlock] // One-hit blocks get destroyed immediately, too + ) + { + HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta); + return; + } + + // Start dig animation + // TODO: calculate real animation speed + // TODO: Send animation packets even without receiving any other packets + m_BlockDigAnimSpeed = 10; + m_BlockDigAnimX = a_BlockX; + m_BlockDigAnimY = a_BlockY; + m_BlockDigAnimZ = a_BlockZ; + m_BlockDigAnimStage = 0; + m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 0, this); + + cWorld * World = m_Player->GetWorld(); + + cBlockHandler * Handler = cBlockHandler::GetBlockHandler(a_OldBlock); + Handler->OnDigging(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); + + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); + ItemHandler->OnDiggingBlock(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + // Check for clickthrough-blocks: + if (a_BlockFace != BLOCK_FACE_NONE) + { + int pX = a_BlockX; + int pY = a_BlockY; + int pZ = a_BlockZ; + AddFaceDirection(pX, pY, pZ, a_BlockFace); + + Handler = cBlockHandler::GetBlockHandler(World->GetBlock(pX, pY, pZ)); + + // 2013_01_05 _X: This looks weird + // Why do we ask the block "behind" the one being clicked if it is clicked through? Shouldn't we ask the primary block instead? + if (Handler->IsClickedThrough()) + { + Handler->OnDigging(World, m_Player, pX, pY, pZ); + } + } +} + + + + + +void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) +{ + if ( + !m_HasStartedDigging || // Hasn't received the DIG_STARTED packet + (m_LastDigBlockX != a_BlockX) || // DIG_STARTED has had different pos + (m_LastDigBlockY != a_BlockY) || + (m_LastDigBlockZ != a_BlockZ) + ) + { + LOGD("Prevented a dig/aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)", + a_BlockX, a_BlockY, a_BlockZ, + m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ, + m_HasStartedDigging + ); + return; + } + + m_HasStartedDigging = false; + if (m_BlockDigAnimStage != -1) + { + // End dig animation + m_BlockDigAnimStage = -1; + // It seems that 10 ends block animation + m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 10, this); + } + + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); + + if (a_OldBlock == E_BLOCK_AIR) + { + LOGD("Dug air - what the function?"); + return; + } + + cWorld * World = m_Player->GetWorld(); + ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ); + // The ItemHandler is also responsible for spawning the pickups + + BlockHandler(a_OldBlock)->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); + World->BroadcastSoundParticleEffect(2001, a_BlockX, a_BlockY, a_BlockZ, a_OldBlock, this); + World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + + cRoot::Get()->GetPluginManager()->CallHookPlayerBrokenBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta); +} + + + + + +void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem) +{ + LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s", + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str() + ); + + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); + if (PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + // A plugin doesn't agree with the action, replace the block on the client and quit: + if (a_BlockFace > -1) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + } + return; + } + + if (!CheckBlockInteractionsRate()) + { + LOGD("Too many block interactions, aborting placement"); + return; + } + + const cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); + + if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1)) + { + // Only compare ItemType, not meta (torches have different metas) + // The -1 check is there because sometimes the client sends -1 instead of the held item + // ( http://forum.mc-server.org/showthread.php?tid=549&pid=4502#pid4502 ) + LOGWARN("Player %s tried to place a block that was not equipped (exp %d, got %d)", + m_Username.c_str(), Equipped.m_ItemType, a_HeldItem.m_ItemType + ); + + // Let's send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block + if (a_BlockFace > -1) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + } + return; + } + + cWorld * World = m_Player->GetWorld(); + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + cBlockHandler * BlockHandler = cBlockHandler::GetBlockHandler(BlockType); + + if (BlockHandler->IsUseable() && !m_Player->IsCrouched()) + { + if (PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + { + // A plugin doesn't agree with using the block, abort + return; + } + BlockHandler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); + return; + } + + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType); + + if (ItemHandler->IsPlaceable()) + { + HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + } + else if (ItemHandler->IsFood()) + { + if (m_Player->IsSatiated()) + { + // The player is satiated, they cannot eat + return; + } + m_Player->StartEating(); + if (PlgMgr->CallHookPlayerEating(*m_Player)) + { + // A plugin won't let us eat, abort (send the proper packets to the client, too): + m_Player->AbortEating(); + return; + } + return; + } + else + { + if (PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + // A plugin doesn't agree with using the item, abort + return; + } + ItemHandler->OnItemUse(World, m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + } +} + + + + + +void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler) +{ + if (a_BlockFace < 0) + { + // Clicked in air + return; + } + + cWorld * World = m_Player->GetWorld(); + + BLOCKTYPE ClickedBlock; + NIBBLETYPE ClickedBlockMeta; + BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType); + NIBBLETYPE EquippedBlockDamage = (NIBBLETYPE)(m_Player->GetEquippedItem().m_ItemDamage); + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The block is being placed outside the world, ignore this packet altogether (#128) + return; + } + + World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); + + // Special slab handling - placing a slab onto another slab produces a dblslab instead: + if ( + cBlockSlabHandler::IsAnySlabType(ClickedBlock) && // Is there a slab already? + cBlockSlabHandler::IsAnySlabType(EquippedBlock) && // Is the player placing another slab? + ((ClickedBlockMeta & 0x07) == (EquippedBlockDamage & 0x07)) && // Is it the same slab type? + ( + (a_BlockFace == BLOCK_FACE_TOP) || // Clicking the top of a bottom slab + (a_BlockFace == BLOCK_FACE_BOTTOM) // Clicking the bottom of a top slab + ) + ) + { + // Coordinates at CLICKED block, don't move them anywhere + } + else + { + // Check if the block ignores build collision (water, grass etc.): + cBlockHandler * Handler = cBlockHandler::GetBlockHandler(ClickedBlock); + if (Handler->DoesIgnoreBuildCollision()) + { + Handler->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); + } + + BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision()) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The block is being placed outside the world, ignore this packet altogether (#128) + return; + } + + BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + + // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. + // No need to do combinability (dblslab) checks, client will do that here. + if (cBlockSlabHandler::IsAnySlabType(PlaceBlock)) + { + // It's a slab, don't do checks and proceed to double-slabbing + } + else + { + if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision()) + { + // Tried to place a block *into* another? + // Happens when you place a block aiming at side of block like torch or stem + return; + } + } + } + } + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_ItemHandler.GetPlacementBlockTypeMeta(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + { + // Handler refused the placement, send that information back to the client: + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockY, m_Player); + return; + } + + cBlockHandler * NewBlock = BlockHandler(BlockType); + + if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + { + // A plugin doesn't agree with placing the block, revert the block on the client: + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + + // The actual block placement: + World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + if (m_Player->GetGameMode() != gmCreative) + { + m_Player->GetInventory().RemoveOneEquippedItem(); + } + NewBlock->OnPlacedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); + + // Step sound with 0.8f pitch is used as block placement sound + World->BroadcastSoundEffect(NewBlock->GetStepSound(), a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.8f); + cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); +} + + + + + +void cClientHandle::HandleChat(const AString & a_Message) +{ + // We no longer need to postpone message processing, because the messages already arrive in the Tick thread + + // If a command, perform it: + AString Message(a_Message); + if (cRoot::Get()->GetServer()->Command(*this, Message)) + { + return; + } + + // Not a command, broadcast as a simple message: + AString Msg; + Printf(Msg, "<%s%s%s> %s", + m_Player->GetColor().c_str(), + m_Player->GetName().c_str(), + cChatColor::White.c_str(), + Message.c_str() + ); + m_Player->GetWorld()->BroadcastChat(Msg); +} + + + + + +void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround) +{ + if ((m_Player == NULL) || (m_State != csPlaying)) + { + return; + } + + m_Player->SetRotation (a_Rotation); + m_Player->SetHeadYaw (a_Rotation); + m_Player->SetPitch (a_Pitch); + m_Player->SetTouchGround(a_IsOnGround); +} + + + + + +void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround) +{ + if ((m_Player == NULL) || (m_State != csPlaying)) + { + // The client hasn't been spawned yet and sends nonsense, we know better + return; + } + + /* + // TODO: Invalid stance check + if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) + { + LOGD("Invalid stance"); + SendPlayerMoveLook(); + return; + } + */ + + m_Player->MoveTo(Vector3d(a_PosX, a_PosY, a_PosZ)); + m_Player->SetStance (a_Stance); + m_Player->SetTouchGround(a_IsOnGround); + m_Player->SetHeadYaw (a_Rotation); + m_Player->SetRotation (a_Rotation); + m_Player->SetPitch (a_Pitch); +} + + + + + +void cClientHandle::HandleAnimation(char a_Animation) +{ + if (cPluginManager::Get()->CallHookPlayerAnimation(*m_Player, a_Animation)) + { + // Plugin disagrees, bail out + return; + } + + m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, a_Animation, this); +} + + + + + +void cClientHandle::HandleSlotSelected(short a_SlotNum) +{ + m_Player->GetInventory().SetEquippedSlotNum(a_SlotNum); + m_Player->GetWorld()->BroadcastEntityEquipment(*m_Player, 0, m_Player->GetInventory().GetEquippedItem(), this); +} + + + + + +void cClientHandle::HandleSteerVehicle(float a_Forward, float a_Sideways) +{ + m_Player->SteerVehicle(a_Forward, a_Sideways); +} + + + + + +void cClientHandle::HandleWindowClose(char a_WindowID) +{ + m_Player->CloseWindowIfID(a_WindowID); +} + + + + + +void cClientHandle::HandleWindowClick(char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem) +{ + LOGD("WindowClick: WinID %d, SlotNum %d, action: %s, Item %s x %d", + a_WindowID, a_SlotNum, ClickActionToString(a_ClickAction), + ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount + ); + + cWindow * Window = m_Player->GetWindow(); + if (Window == NULL) + { + LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str()); + return; + } + + Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_ClickAction, a_HeldItem); +} + + + + + +void cClientHandle::HandleUpdateSign( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, + const AString & a_Line3, const AString & a_Line4 +) +{ + cWorld * World = m_Player->GetWorld(); + World->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player); +} + + + + + +void cClientHandle::HandleUseEntity(int a_TargetEntityID, bool a_IsLeftClick) +{ + // TODO: Let plugins interfere via a hook + + // If it is a right click, call the entity's OnRightClicked() handler: + if (!a_IsLeftClick) + { + class cRclkEntity : public cEntityCallback + { + cPlayer & m_Player; + virtual bool Item(cEntity * a_Entity) override + { + if (cPluginManager::Get()->CallHookPlayerRightClickingEntity(m_Player, *a_Entity)) + { + return false; + } + a_Entity->OnRightClicked(m_Player); + return false; + } + public: + cRclkEntity(cPlayer & a_Player) : m_Player(a_Player) {} + } Callback (*m_Player); + + cWorld * World = m_Player->GetWorld(); + World->DoWithEntityByID(a_TargetEntityID, Callback); + return; + } + + // If it is a left click, attack the entity: + class cDamageEntity : public cEntityCallback + { + virtual bool Item(cEntity * a_Entity) override + { + if (!a_Entity->GetWorld()->IsPVPEnabled()) + { + // PVP is disabled, disallow players hurting other players: + if (a_Entity->IsPlayer()) + { + // Player is hurting another player which is not allowed when PVP is disabled so ignore it + return true; + } + } + a_Entity->TakeDamage(*m_Attacker); + return false; + } + public: + cPawn * m_Attacker; + } Callback; + + Callback.m_Attacker = m_Player; + + cWorld * World = m_Player->GetWorld(); + if (World->DoWithEntityByID(a_TargetEntityID, Callback)) + { + // Any kind of an attack implies food exhaustion + m_Player->AddFoodExhaustion(0.3); + } +} + + + + + +void cClientHandle::HandleRespawn(void) +{ + if (m_Player == NULL) + { + Destroy(); + return; + } + m_Player->Respawn(); + cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); +} + + + + + +void cClientHandle::HandleDisconnect(const AString & a_Reason) +{ + LOGD("Received d/c packet from \"%s\" with reason \"%s\"", m_Username.c_str(), a_Reason.c_str()); + if (!cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, a_Reason)) + { + AString DisconnectMessage; + Printf(DisconnectMessage, "%s disconnected: %s", m_Username.c_str(), a_Reason.c_str()); + m_Player->GetWorld()->BroadcastChat(DisconnectMessage, this); + } + m_HasSentDC = true; + Destroy(); +} + + + + + +void cClientHandle::HandleKeepAlive(int a_KeepAliveID) +{ + if (a_KeepAliveID == m_PingID) + { + cTimer t1; + m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2); + } +} + + + + + +bool cClientHandle::HandleHandshake(const AString & a_Username) +{ + if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(this, a_Username)) + { + if (cRoot::Get()->GetServer()->GetNumPlayers() >= cRoot::Get()->GetServer()->GetMaxPlayers()) + { + Kick("The server is currently full :(-- Try again later"); + return false; + } + } + return true; +} + + + + + +void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID) +{ + if (a_EntityID != m_Player->GetUniqueID()) + { + // We should only receive entity actions from the entity that is performing the action + return; + } + + switch (a_ActionID) + { + case 1: // crouch + { + m_Player->SetCrouch(true); + break; + } + case 2: // uncrouch + { + m_Player->SetCrouch(false); + break; + } + case 3: // Leave bed + { + m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, 3); + break; + } + case 4: // Start sprinting + { + m_Player->SetSprint(true); + break; + } + case 5: // Stop sprinting + { + m_Player->SetSprint(false); + SendPlayerMaxSpeed(); + break; + } + } +} + + + + + +void cClientHandle::HandleUnmount(void) +{ + if (m_Player == NULL) + { + return; + } + m_Player->Detach(); +} + + + + + +void cClientHandle::HandleTabCompletion(const AString & a_Text) +{ + AStringVector Results; + m_Player->GetWorld()->TabCompleteUserName(a_Text, Results); + cRoot::Get()->GetPluginManager()->TabCompleteCommand(a_Text, Results, m_Player); + if (Results.empty()) + { + return; + } + std::sort(Results.begin(), Results.end()); + SendTabCompletionResults(Results); +} + + + + + +void cClientHandle::SendData(const char * a_Data, int a_Size) +{ + if (m_HasSentDC) + { + // This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31) + return; + } + + { + cCSLock Lock(m_CSOutgoingData); + + // _X 2012_09_06: We need an overflow buffer, usually when streaming the initial chunks + if (m_OutgoingDataOverflow.empty()) + { + // No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer: + int CanFit = m_OutgoingData.GetFreeSpace(); + if (CanFit > a_Size) + { + CanFit = a_Size; + } + if (CanFit > 0) + { + m_OutgoingData.Write(a_Data, CanFit); + } + if (a_Size > CanFit) + { + m_OutgoingDataOverflow.append(a_Data + CanFit, a_Size - CanFit); + } + } + else + { + // There is a queued overflow. Append to it, then send as much from its front as possible + m_OutgoingDataOverflow.append(a_Data, a_Size); + int CanFit = m_OutgoingData.GetFreeSpace(); + if (CanFit > 128) + { + // No point in moving the data over if it's not large enough - too much effort for too little an effect + m_OutgoingData.Write(m_OutgoingDataOverflow.data(), CanFit); + m_OutgoingDataOverflow.erase(0, CanFit); + } + } + } // Lock(m_CSOutgoingData) + + // Notify SocketThreads that we have something to write: + cRoot::Get()->GetServer()->NotifyClientWrite(this); +} + + + + + +void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket) +{ + ASSERT(m_Player != NULL); + + if (a_SendRespawnPacket) + { + SendRespawn(); + } + + cWorld * World = m_Player->GetWorld(); + + // Remove all associated chunks: + cChunkCoordsList Chunks; + { + cCSLock Lock(m_CSChunkLists); + std::swap(Chunks, m_LoadedChunks); + m_ChunksToSend.clear(); + } + for (cChunkCoordsList::iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr) + { + World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this); + m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); + } // for itr - Chunks[] + + // Do NOT stream new chunks, the new world runs its own tick thread and may deadlock + // Instead, the chunks will be streamed when the client is moved to the new world's Tick list, + // by setting state to csAuthenticated + m_State = csAuthenticated; + m_LastStreamedChunkX = 0x7fffffff; + m_LastStreamedChunkZ = 0x7fffffff; + m_HasSentPlayerChunk = false; +} + + + + + +bool cClientHandle::CheckBlockInteractionsRate(void) +{ + ASSERT(m_Player != NULL); + ASSERT(m_Player->GetWorld() != NULL); + /* + // TODO: _X 2012_11_01: This needs a total re-thinking and rewriting + int LastActionCnt = m_Player->GetLastBlockActionCnt(); + if ((m_Player->GetWorld()->GetTime() - m_Player->GetLastBlockActionTime()) < 0.1) + { + // Limit the number of block interactions per tick + m_Player->SetLastBlockActionTime(); //Player tried to interact with a block. Reset last block interation time. + m_Player->SetLastBlockActionCnt(LastActionCnt + 1); + if (m_Player->GetLastBlockActionCnt() > MAXBLOCKCHANGEINTERACTIONS) + { + // Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick + LOGWARN("Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked.", m_Username.c_str()); + Kick("You're a baaaaaad boy!"); + return false; + } + } + else + { + m_Player->SetLastBlockActionCnt(0); // Reset count + m_Player->SetLastBlockActionTime(); // Player tried to interact with a block. Reset last block interation time. + } + */ + return true; +} + + + + + +void cClientHandle::Tick(float a_Dt) +{ + // Handle clients that are waiting for final close while destroyed: + if (m_State == csDestroyedWaiting) + { + m_TicksSinceDestruction += 1; // This field is misused for the timeout counting + if (m_TicksSinceDestruction > TICKS_BEFORE_CLOSE) + { + m_State = csDestroyed; + } + return; + } + + // Process received network data: + AString IncomingData; + { + cCSLock Lock(m_CSIncomingData); + std::swap(IncomingData, m_IncomingData); + } + m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); + + if (m_State == csAuthenticated) + { + StreamChunks(); + m_State = csDownloadingWorld; + } + + m_TimeSinceLastPacket += a_Dt; + if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out + { + SendDisconnect("Nooooo!! You timed out! D: Come back!"); + Destroy(); + } + + if (m_Player == NULL) + { + return; + } + + // If the chunk the player's in was just sent, spawn the player: + if (m_HasSentPlayerChunk && (m_State != csPlaying) && !IsDestroying()) + { + if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player)) + { + // Broadcast that this player has joined the game! Yay~ + m_Player->GetWorld()->BroadcastChat(m_Username + " joined the game!", this); + } + m_Protocol->SendPlayerMoveLook(); + m_State = csPlaying; + } + + // Send a ping packet: + cTimer t1; + if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime())) + { + m_PingID++; + m_PingStartTime = t1.GetNowTime(); + m_Protocol->SendKeepAlive(m_PingID); + m_LastPingTime = m_PingStartTime; + } + + // Handle block break animation: + if (m_BlockDigAnimStage > -1) + { + int lastAnimVal = m_BlockDigAnimStage; + m_BlockDigAnimStage += (int)(m_BlockDigAnimSpeed * a_Dt); + if (m_BlockDigAnimStage > 9000) + { + m_BlockDigAnimStage = 9000; + } + if (m_BlockDigAnimStage / 1000 != lastAnimVal / 1000) + { + m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, (char)(m_BlockDigAnimStage / 1000), this); + } + } + + // Update the explosion statistics: + m_CurrentExplosionTick = (m_CurrentExplosionTick + 1) % ARRAYCOUNT(m_NumExplosionsPerTick); + m_RunningSumExplosions -= m_NumExplosionsPerTick[m_CurrentExplosionTick]; + m_NumExplosionsPerTick[m_CurrentExplosionTick] = 0; +} + + + + + +void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + m_Protocol->SendAttachEntity(a_Entity, a_Vehicle); +} + + + + + +void cClientHandle::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); +} + + + + + +void cClientHandle::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) +{ + m_Protocol->SendBlockBreakAnim(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage); +} + + + + + +void cClientHandle::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); +} + + + + + +void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + ASSERT(!a_Changes.empty()); // We don't want to be sending empty change packets! + + m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); +} + + + + + +void cClientHandle::SendChat(const AString & a_Message) +{ + m_Protocol->SendChat(a_Message); +} + + + + + +void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + ASSERT(m_Player != NULL); + + // Check chunks being sent, erase them from m_ChunksToSend: + bool Found = false; + { + cCSLock Lock(m_CSChunkLists); + for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) + { + m_ChunksToSend.erase(itr); + Found = true; + break; + } + } // for itr - m_ChunksToSend[] + } + if (!Found) + { + // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it + // It's not a big issue anyway, just means that some chunks may be compressed several times + // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ()); + return; + } + + m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); + + // If it is the chunk the player's in, make them spawn (in the tick thread): + if ((m_State == csAuthenticated) || (m_State == csDownloadingWorld)) + { + if ((a_ChunkX == m_Player->GetChunkX()) && (a_ChunkZ == m_Player->GetChunkZ())) + { + m_HasSentPlayerChunk = true; + } + } +} + + + + + +void cClientHandle::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + m_Protocol->SendCollectPickup(a_Pickup, a_Player); +} + + + + + +void cClientHandle::SendDestroyEntity(const cEntity & a_Entity) +{ + m_Protocol->SendDestroyEntity(a_Entity); +} + + + + + +void cClientHandle::SendDisconnect(const AString & a_Reason) +{ + if (!m_HasSentDC) + { + LOGD("Sending a DC: \"%s\"", StripColorCodes(a_Reason).c_str()); + m_Protocol->SendDisconnect(a_Reason); + m_HasSentDC = true; + } +} + + + + + +void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); +} + + + + + +void cClientHandle::SendEntityHeadLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityHeadLook(a_Entity); +} + + + + + +void cClientHandle::SendEntityLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityLook(a_Entity); +} + + + + + +void cClientHandle::SendEntityMetadata(const cEntity & a_Entity) +{ + m_Protocol->SendEntityMetadata(a_Entity); +} + + + + + +void cClientHandle::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cClientHandle::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + m_Protocol->SendEntityStatus(a_Entity, a_Status); +} + + + + + +void cClientHandle::SendEntityVelocity(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityVelocity(a_Entity); +} + + + + + +void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + if ( + (m_NumExplosionsPerTick[m_CurrentExplosionTick] > MAX_EXPLOSIONS_PER_TICK) || // Too many explosions in this tick + (m_RunningSumExplosions > MAX_RUNNING_SUM_EXPLOSIONS) // Too many explosions in the recent history + ) + { + LOGD("Dropped %u explosions", a_BlocksAffected.size()); + return; + } + + // Update the statistics: + m_NumExplosionsPerTick[m_CurrentExplosionTick] += a_BlocksAffected.size(); + m_RunningSumExplosions += a_BlocksAffected.size(); + + m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion); +} + + + + + +void cClientHandle::SendGameMode(eGameMode a_GameMode) +{ + m_Protocol->SendGameMode(a_GameMode); +} + + + + + +void cClientHandle::SendHealth(void) +{ + m_Protocol->SendHealth(); +} + + + + + +void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); +} + + + + + +void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup) +{ + m_Protocol->SendPickupSpawn(a_Pickup); +} + + + + + +void cClientHandle::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + m_Protocol->SendPlayerAnimation(a_Player, a_Animation); +} + + + + + +void cClientHandle::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); +} + + + + + +void cClientHandle::SendPlayerMaxSpeed(void) +{ + m_Protocol->SendPlayerMaxSpeed(); +} + + + + + +void cClientHandle::SendPlayerMoveLook(void) +{ + /* + LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", + m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 + ); + */ + m_Protocol->SendPlayerMoveLook(); +} + + + + + +void cClientHandle::SendPlayerPosition(void) +{ + m_Protocol->SendPlayerPosition(); +} + + + + + +void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player) +{ + if (a_Player.GetUniqueID() == m_Player->GetUniqueID()) + { + // Do NOT send this packet to myself + return; + } + + LOGD("Spawning player \"%s\" on client \"%s\" @ %s", + a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str() + ); + + m_Protocol->SendPlayerSpawn(a_Player); +} + + + + + +void cClientHandle::SendRespawn(void) +{ + m_Protocol->SendRespawn(); +} + + + + + +void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); +} + + + + + +void cClientHandle::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); +} + + + + + +void cClientHandle::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + m_Protocol->SendSpawnFallingBlock(a_FallingBlock); +} + + + + + +void cClientHandle::SendSpawnMob(const cMonster & a_Mob) +{ + m_Protocol->SendSpawnMob(a_Mob); +} + + + + + +void cClientHandle::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch); +} + + + + + +void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) // VehicleSubType is specific to Minecarts +{ + m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType); +} + + + + + +void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results) +{ + m_Protocol->SendTabCompletionResults(a_Results); +} + + + + + +void cClientHandle::SendTeleportEntity(const cEntity & a_Entity) +{ + m_Protocol->SendTeleportEntity(a_Entity); +} + + + + + +void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cClientHandle::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay); +} + + + + + +void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); +} + + + + + +void cClientHandle::SendUpdateSign( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 +) +{ + m_Protocol->SendUpdateSign( + a_BlockX, a_BlockY, a_BlockZ, + a_Line1, a_Line2, a_Line3, a_Line4 + ); +} + + + + + +void cClientHandle::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); +} + + + + +void cClientHandle::SendWeather(eWeather a_Weather) +{ + m_Protocol->SendWeather(a_Weather); +} + + + + + +void cClientHandle::SendWholeInventory(const cWindow & a_Window) +{ + m_Protocol->SendWholeInventory(a_Window); +} + + + + + +void cClientHandle::SendWindowClose(const cWindow & a_Window) +{ + m_Protocol->SendWindowClose(a_Window); +} + + + + + +void cClientHandle::SendWindowOpen(const cWindow & a_Window) +{ + m_Protocol->SendWindowOpen(a_Window); +} + + + + + +void cClientHandle::SendWindowProperty(const cWindow & a_Window, int a_Property, int a_Value) +{ + m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value); +} + + + + + +const AString & cClientHandle::GetUsername(void) const +{ + return m_Username; +} + + + + + +void cClientHandle::SetUsername( const AString & a_Username ) +{ + m_Username = a_Username; +} + + + + + +void cClientHandle::SetViewDistance(int a_ViewDistance) +{ + if (a_ViewDistance < MIN_VIEW_DISTANCE) + { + a_ViewDistance = MIN_VIEW_DISTANCE; + } + if (a_ViewDistance > MAX_VIEW_DISTANCE) + { + a_ViewDistance = MAX_VIEW_DISTANCE; + } + m_ViewDistance = a_ViewDistance; + + // Need to re-stream chunks for the change to become apparent: + StreamChunks(); +} + + + + + +bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + if (m_State >= csDestroying) + { + return false; + } + + cCSLock Lock(m_CSChunkLists); + return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)) != m_ChunksToSend.end()); +} + + + + + +void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ) +{ + if (m_State >= csDestroying) + { + return; + } + + LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this); + cCSLock Lock(m_CSChunkLists); + if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)) == m_ChunksToSend.end()) + { + m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + } +} + + + + + +void cClientHandle::PacketBufferFull(void) +{ + // Too much data in the incoming queue, the server is probably too busy, kick the client: + LOGERROR("Too much data in queue for client \"%s\" @ %s, kicking them.", m_Username.c_str(), m_IPString.c_str()); + SendDisconnect("Server busy"); + Destroy(); +} + + + + + +void cClientHandle::PacketUnknown(unsigned char a_PacketType) +{ + LOGERROR("Unknown packet type 0x%02x from client \"%s\" @ %s", a_PacketType, m_Username.c_str(), m_IPString.c_str()); + + AString Reason; + Printf(Reason, "Unknown [C->S] PacketType: 0x%02x", a_PacketType); + SendDisconnect(Reason); + Destroy(); +} + + + + + +void cClientHandle::PacketError(unsigned char a_PacketType) +{ + LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str()); + SendDisconnect("Protocol error"); + Destroy(); +} + + + + + +void cClientHandle::DataReceived(const char * a_Data, int a_Size) +{ + // Data is received from the client, store it in the buffer to be processed by the Tick thread: + m_TimeSinceLastPacket = 0; + cCSLock Lock(m_CSIncomingData); + m_IncomingData.append(a_Data, a_Size); +} + + + + + +void cClientHandle::GetOutgoingData(AString & a_Data) +{ + // Data can be sent to client + { + cCSLock Lock(m_CSOutgoingData); + m_OutgoingData.ReadAll(a_Data); + m_OutgoingData.CommitRead(); + a_Data.append(m_OutgoingDataOverflow); + m_OutgoingDataOverflow.clear(); + } + + // Disconnect player after all packets have been sent + if (m_HasSentDC && a_Data.empty()) + { + Destroy(); + } +} + + + + + +void cClientHandle::SocketClosed(void) +{ + // The socket has been closed for any reason + + LOGD("Client \"%s\" @ %s disconnected", m_Username.c_str(), m_IPString.c_str()); + Destroy(); +} + + + + + + diff --git a/src/ClientHandle.h b/src/ClientHandle.h new file mode 100644 index 000000000..3844937ad --- /dev/null +++ b/src/ClientHandle.h @@ -0,0 +1,331 @@ + +// cClientHandle.h + +// Interfaces to the cClientHandle class representing a client connected to this server. The client need not be a player yet + + + + + +#pragma once +#ifndef CCLIENTHANDLE_H_INCLUDED +#define CCLIENTHANDLE_H_INCLUDED + +#include "Defines.h" +#include "Vector3d.h" +#include "OSSupport/SocketThreads.h" +#include "ChunkDef.h" +#include "ByteBuffer.h" + + + + + +class cChunkDataSerializer; +class cInventory; +class cMonster; +class cPawn; +class cPickup; +class cPlayer; +class cProtocol; +class cRedstone; +class cWindow; +class cFallingBlock; +class cItemHandler; +class cWorld; + + + + + +class cClientHandle : // tolua_export + public cSocketThreads::cCallback +{ // tolua_export +public: + enum ENUM_PRIORITY + { + E_PRIORITY_LOW, + E_PRIORITY_NORMAL + }; + + static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick + +#if defined(ANDROID_NDK) + static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini) +#else + static const int DEFAULT_VIEW_DISTANCE = 10; +#endif + static const int MAX_VIEW_DISTANCE = 15; + static const int MIN_VIEW_DISTANCE = 3; + + /// How many ticks should be checked for a running average of explosions, for limiting purposes + static const int NUM_CHECK_EXPLOSIONS_TICKS = 20; + + cClientHandle(const cSocket * a_Socket, int a_ViewDistance); + virtual ~cClientHandle(); + + const AString & GetIPString(void) const { return m_IPString; } + + cPlayer* GetPlayer() { return m_Player; } // tolua_export + + void Kick(const AString & a_Reason); // tolua_export + void Authenticate(void); // Called by cAuthenticator when the user passes authentication + + void StreamChunks(void); + + // Removes the client from all chunks. Used when switching worlds or destroying the player + void RemoveFromAllChunks(void); + + inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); } + + void Tick(float a_Dt); + + void Destroy(void); + + bool IsPlaying (void) const { return (m_State == csPlaying); } + bool IsDestroyed (void) const { return (m_State == csDestroyed); } + bool IsDestroying(void) const { return (m_State == csDestroying); } + + // The following functions send the various packets: + // (Please keep these alpha-sorted) + void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle); + void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType); + void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage); + void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export + void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes); + void SendChat (const AString & a_Message); + void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); + void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player); + void SendDestroyEntity (const cEntity & a_Entity); + void SendDisconnect (const AString & a_Reason); + void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ); + void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item); + void SendEntityHeadLook (const cEntity & a_Entity); + void SendEntityLook (const cEntity & a_Entity); + void SendEntityMetadata (const cEntity & a_Entity); + void SendEntityProperties (const cEntity & a_Entity); + void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); + void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); + void SendEntityStatus (const cEntity & a_Entity, char a_Status); + void SendEntityVelocity (const cEntity & a_Entity); + void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion); + void SendGameMode (eGameMode a_GameMode); + void SendHealth (void); + void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item); + void SendPickupSpawn (const cPickup & a_Pickup); + void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation); + void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline); + void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+) + void SendPlayerMoveLook (void); + void SendPlayerPosition (void); + void SendPlayerSpawn (const cPlayer & a_Player); + void SendRespawn (void); + void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8 + void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data); + void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock); + void SendSpawnMob (const cMonster & a_Mob); + void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch); + void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType = 0); + void SendTabCompletionResults(const AStringVector & a_Results); + void SendTeleportEntity (const cEntity & a_Entity); + void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); + void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay); + void SendUnloadChunk (int a_ChunkX, int a_ChunkZ); + void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); + void SendWeather (eWeather a_Weather); + void SendWholeInventory (const cWindow & a_Window); + void SendWindowClose (const cWindow & a_Window); + void SendWindowOpen (const cWindow & a_Window); + void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value); + + const AString & GetUsername(void) const; // tolua_export + void SetUsername( const AString & a_Username ); // tolua_export + + inline short GetPing(void) const { return m_Ping; } // tolua_export + + void SetViewDistance(int a_ViewDistance); // tolua_export + int GetViewDistance(void) const { return m_ViewDistance; } // tolua_export + + int GetUniqueID() const { return m_UniqueID; } // tolua_export + + /// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) + bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) + void AddWantedChunk(int a_ChunkX, int a_ChunkZ); + + // Calls that cProtocol descendants use to report state: + void PacketBufferFull(void); + void PacketUnknown(unsigned char a_PacketType); + void PacketError(unsigned char a_PacketType); + + // Calls that cProtocol descendants use for handling packets: + void HandleAnimation (char a_Animation); + void HandleChat (const AString & a_Message); + void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem); + void HandleDisconnect (const AString & a_Reason); + void HandleEntityAction (int a_EntityID, char a_ActionID); + bool HandleHandshake (const AString & a_Username); + void HandleKeepAlive (int a_KeepAliveID); + void HandleLeftClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); + void HandlePing (void); + void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround); + void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay) + void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround); + void HandleRespawn (void); + void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem); + void HandleSlotSelected (short a_SlotNum); + void HandleSteerVehicle (float Forward, float Sideways); + void HandleTabCompletion (const AString & a_Text); + void HandleUpdateSign ( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, + const AString & a_Line3, const AString & a_Line4 + ); + void HandleUnmount (void); + void HandleUseEntity (int a_TargetEntityID, bool a_IsLeftClick); + void HandleWindowClick (char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem); + void HandleWindowClose (char a_WindowID); + + /** Called when the protocol has finished logging the user in. + Return true to allow the user in; false to kick them. + */ + bool HandleLogin(int a_ProtocolVersion, const AString & a_Username); + + void SendData(const char * a_Data, int a_Size); + + /// Called when the player moves into a different world; queues sreaming the new chunks + void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket); + + /// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) + void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler); + +private: + + int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 ) + + static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation + + AString m_IPString; + + int m_ProtocolVersion; + AString m_Username; + AString m_Password; + + cCriticalSection m_CSChunkLists; + cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to + cChunkCoordsList m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them) + + cProtocol * m_Protocol; + + cCriticalSection m_CSIncomingData; + AString m_IncomingData; + + cCriticalSection m_CSOutgoingData; + cByteBuffer m_OutgoingData; + AString m_OutgoingDataOverflow; ///< For data that didn't fit into the m_OutgoingData ringbuffer temporarily + + Vector3d m_ConfirmPosition; + + cPlayer * m_Player; + + bool m_HasSentDC; ///< True if a D/C packet has been sent in either direction + + // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk + int m_LastStreamedChunkX; + int m_LastStreamedChunkZ; + + /// Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) + float m_TimeSinceLastPacket; + + short m_Ping; + int m_PingID; + long long m_PingStartTime; + long long m_LastPingTime; + static const unsigned short PING_TIME_MS = 1000; //minecraft sends 1 per 20 ticks (1 second or every 1000 ms) + + // Values required for block dig animation + int m_BlockDigAnimStage; // Current stage of the animation; -1 if not digging + int m_BlockDigAnimSpeed; // Current speed of the animation (units ???) + int m_BlockDigAnimX; + int m_BlockDigAnimY; + int m_BlockDigAnimZ; + + // To avoid dig/aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet: + bool m_HasStartedDigging; + int m_LastDigBlockX; + int m_LastDigBlockY; + int m_LastDigBlockZ; + + /// Used while csDestroyedWaiting for counting the ticks until the connection is closed + int m_TicksSinceDestruction; + + enum eState + { + csConnected, ///< The client has just connected, waiting for their handshake / login + csAuthenticating, ///< The client has logged in, waiting for external authentication + csAuthenticated, ///< The client has been authenticated, will start streaming chunks in the next tick + csDownloadingWorld, ///< The client is waiting for chunks, we're waiting for the loader to provide and send them + csConfirmingPos, ///< The client has been sent the position packet, waiting for them to repeat the position back + csPlaying, ///< Normal gameplay + csDestroying, ///< The client is being destroyed, don't queue any more packets / don't add to chunks + csDestroyedWaiting, ///< The client has been destroyed, but is still kept so that the Kick packet is delivered (#31) + csDestroyed, ///< The client has been destroyed, the destructor is to be called from the owner thread + + // TODO: Add Kicking here as well + } ; + + eState m_State; + + /// m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads + cCriticalSection m_CSDestroyingState; + + bool m_bKeepThreadGoing; + + /// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() + bool m_ShouldCheckDownloaded; + + /// Stores the recent history of the number of explosions per tick + int m_NumExplosionsPerTick[NUM_CHECK_EXPLOSIONS_TICKS]; + + /// Points to the current tick in the m_NumExplosionsPerTick[] array + int m_CurrentExplosionTick; + + /// Running sum of m_NumExplosionsPerTick[] + int m_RunningSumExplosions; + + static int s_ClientCount; + int m_UniqueID; + + /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player + bool m_HasSentPlayerChunk; + + + + /// Returns true if the rate block interactions is within a reasonable limit (bot protection) + bool CheckBlockInteractionsRate(void); + + /// Adds a single chunk to be streamed to the client; used by StreamChunks() + void StreamChunk(int a_ChunkX, int a_ChunkZ); + + /// Handles the DIG_STARTED dig packet: + void HandleBlockDigStarted (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); + + /// Handles the DIG_FINISHED dig packet: + void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); + + // cSocketThreads::cCallback overrides: + virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client + virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client + virtual void SocketClosed (void) override; // The socket has been closed for any reason +}; // tolua_export + + + + +#endif // CCLIENTHANDLE_H_INCLUDED + + + + diff --git a/src/CommandOutput.cpp b/src/CommandOutput.cpp new file mode 100644 index 000000000..c221682a1 --- /dev/null +++ b/src/CommandOutput.cpp @@ -0,0 +1,71 @@ + +// CommandOutput.cpp + +// Implements the various classes that process command output + +#include "Globals.h" +#include "CommandOutput.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCommandOutputCallback: + +void cCommandOutputCallback::Out(const char * a_Fmt, ...) +{ + AString Output; + va_list args; + va_start(args, a_Fmt); + AppendVPrintf(Output, a_Fmt, args); + va_end(args); + Output.append("\n"); + Out(Output); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLogCommandOutputCallback: + +void cLogCommandOutputCallback::Out(const AString & a_Text) +{ + m_Buffer.append(a_Text); +} + + + + + +void cLogCommandOutputCallback::Finished(void) +{ + // Log each line separately: + size_t len = m_Buffer.length(); + size_t last = 0; + for (size_t i = 0; i < len; i++) + { + switch (m_Buffer[i]) + { + case '\n': + { + LOG(m_Buffer.substr(last, i - last).c_str()); + last = i + 1; + break; + } + } + } // for i - m_Buffer[] + if (last < len) + { + LOG(m_Buffer.substr(last).c_str()); + } + + // Clear the buffer for the next command output: + m_Buffer.clear(); +} + + + + diff --git a/src/CommandOutput.h b/src/CommandOutput.h new file mode 100644 index 000000000..bdf675238 --- /dev/null +++ b/src/CommandOutput.h @@ -0,0 +1,82 @@ + +// CommandOutput.h + +// Declares various classes that process command output + + + + + +/** Interface for a callback that receives command output +The Out() function is called for any output the command has produced. +Descendants override that function to provide specific processing of the output. +*/ +class cCommandOutputCallback +{ +public: + virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses + + /// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n" + void Out(const char * a_Fmt, ...); + + /// Called when the command wants to output anything; may be called multiple times + virtual void Out(const AString & a_Text) = 0; + + /// Called when the command processing has been finished + virtual void Finished(void) {}; +} ; + + + + + +/// Class that discards all command output +class cNullCommandOutputCallback : + public cCommandOutputCallback +{ + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override + { + // Do nothing + } +} ; + + + + + + +/// Sends all command output to a log, line by line, when the command finishes processing +class cLogCommandOutputCallback : + public cCommandOutputCallback +{ +public: + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override; + virtual void Finished(void) override; + +protected: + /// Output is stored here until the command finishes processing + AString m_Buffer; +} ; + + + + + +/// Sends all command output to a log, line by line; deletes self when command finishes processing +class cLogCommandDeleteSelfOutputCallback : + public cLogCommandOutputCallback +{ + typedef cLogCommandOutputCallback super; + + virtual void Finished(void) override + { + super::Finished(); + delete this; + } +} ; + + + + diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp new file mode 100644 index 000000000..9dc471781 --- /dev/null +++ b/src/CraftingRecipes.cpp @@ -0,0 +1,770 @@ + +// CraftingRecipes.cpp + +// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes + +#include "Globals.h" +#include "CraftingRecipes.h" +#include "Root.h" +#include "PluginManager.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCraftingGrid: + +cCraftingGrid::cCraftingGrid(int a_Width, int a_Height) : + m_Width(a_Width), + m_Height(a_Height), + m_Items(new cItem[a_Width * a_Height]) +{ +} + + + + + +cCraftingGrid::cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height) : + m_Width(a_Width), + m_Height(a_Height), + m_Items(new cItem[a_Width * a_Height]) +{ + for (int i = a_Width * a_Height - 1; i >= 0; i--) + { + m_Items[i] = a_Items[i]; + } +} + + + + + +cCraftingGrid::cCraftingGrid(const cCraftingGrid & a_Original) : + m_Width(a_Original.m_Width), + m_Height(a_Original.m_Height), + m_Items(new cItem[a_Original.m_Width * a_Original.m_Height]) +{ + for (int i = m_Width * m_Height - 1; i >= 0; i--) + { + m_Items[i] = a_Original.m_Items[i]; + } +} + + + + + +cCraftingGrid::~cCraftingGrid() +{ + delete[] m_Items; +} + + + + + +cItem & cCraftingGrid::GetItem(int x, int y) const +{ + // Accessible through scripting, must verify parameters: + if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) + { + LOGERROR("Attempted to get an invalid item from a crafting grid: (%d, %d), grid dimensions: (%d, %d).", + x, y, m_Width, m_Height + ); + return m_Items[0]; + } + return m_Items[x + m_Width * y]; +} + + + + + +void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) +{ + // Accessible through scripting, must verify parameters: + if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) + { + LOGERROR("Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).", + x, y, m_Width, m_Height + ); + return; + } + + m_Items[x + m_Width * y] = cItem(a_ItemType, a_ItemCount, a_ItemHealth); +} + + + + + +void cCraftingGrid::SetItem(int x, int y, const cItem & a_Item) +{ + // Accessible through scripting, must verify parameters: + if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) + { + LOGERROR("Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).", + x, y, m_Width, m_Height + ); + return; + } + + m_Items[x + m_Width * y] = a_Item; +} + + + + + +void cCraftingGrid::Clear(void) +{ + for (int y = 0; y < m_Height; y++) for (int x = 0; x < m_Width; x++) + { + m_Items[x + m_Width * y].Empty(); + } +} + + + + + +void cCraftingGrid::ConsumeGrid(const cCraftingGrid & a_Grid) +{ + if ((a_Grid.m_Width != m_Width) || (a_Grid.m_Height != m_Height)) + { + LOGWARNING("Consuming a grid of different dimensions: (%d, %d) vs (%d, %d)", + a_Grid.m_Width, a_Grid.m_Height, m_Width, m_Height + ); + } + int MinX = std::min(a_Grid.m_Width, m_Width); + int MinY = std::min(a_Grid.m_Height, m_Height); + for (int y = 0; y < MinY; y++) for (int x = 0; x < MinX; x++) + { + int ThatIdx = x + a_Grid.m_Width * y; + if (a_Grid.m_Items[ThatIdx].IsEmpty()) + { + continue; + } + int ThisIdx = x + m_Width * y; + if (a_Grid.m_Items[ThatIdx].m_ItemType != m_Items[ThisIdx].m_ItemType) + { + LOGWARNING("Consuming incompatible grids: item at (%d, %d) is %d in grid and %d in ingredients. Item not consumed.", + x, y, m_Items[ThisIdx].m_ItemType, a_Grid.m_Items[ThatIdx].m_ItemType + ); + continue; + } + char NumWantedItems = a_Grid.m_Items[ThatIdx].m_ItemCount; + if (NumWantedItems > m_Items[ThisIdx].m_ItemCount) + { + LOGWARNING("Consuming more items than there actually are in slot (%d, %d), item %d (want %d, have %d). Item zeroed out.", + x, y, m_Items[ThisIdx].m_ItemType, + NumWantedItems, m_Items[ThisIdx].m_ItemCount + ); + NumWantedItems = m_Items[ThisIdx].m_ItemCount; + } + m_Items[ThisIdx].m_ItemCount -= NumWantedItems; + if (m_Items[ThisIdx].m_ItemCount == 0) + { + m_Items[ThisIdx].Clear(); + } + } // for x, for y +} + + + + + +void cCraftingGrid::CopyToItems(cItem * a_Items) const +{ + for (int i = m_Height * m_Width - 1; i >= 0; i--) + { + a_Items[i] = m_Items[i]; + } // for x, for y +} + + + + + +void cCraftingGrid::Dump(void) +{ + for (int y = 0; y < m_Height; y++) for (int x = 0; x < m_Width; x++) + { + int idx = x + m_Width * y; + LOGD("Slot (%d, %d): Type %d, health %d, count %d", + x, y, m_Items[idx].m_ItemType, m_Items[idx].m_ItemDamage, m_Items[idx].m_ItemCount + ); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCraftingRecipe: + +cCraftingRecipe::cCraftingRecipe(const cCraftingGrid & a_CraftingGrid) : + m_Ingredients(a_CraftingGrid) +{ +} + + + + + +void cCraftingRecipe::Clear(void) +{ + m_Ingredients.Clear(); + m_Result.Clear(); +} + + + + + +void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) +{ + m_Result = cItem(a_ItemType, a_ItemCount, a_ItemHealth); +} + + + + + +void cCraftingRecipe::ConsumeIngredients(cCraftingGrid & a_CraftingGrid) +{ + a_CraftingGrid.ConsumeGrid(m_Ingredients); +} + + + + + +void cCraftingRecipe::Dump(void) +{ + LOGD("Recipe ingredients:"); + m_Ingredients.Dump(); + LOGD("Result: Type %d, health %d, count %d", + m_Result.m_ItemType, m_Result.m_ItemDamage, m_Result.m_ItemCount + ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCraftingRecipes: + +cCraftingRecipes::cCraftingRecipes(void) +{ + LoadRecipes(); +} + + + + + +cCraftingRecipes::~cCraftingRecipes() +{ + ClearRecipes(); +} + + + + + +void cCraftingRecipes::GetRecipe(const cPlayer * a_Player, const cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe) +{ + // Allow plugins to intercept recipes using a pre-craft hook: + if (cRoot::Get()->GetPluginManager()->CallHookPreCrafting(a_Player, &a_CraftingGrid, &a_Recipe)) + { + return; + } + + // Built-in recipes: + std::auto_ptr Recipe(FindRecipe(a_CraftingGrid.GetItems(), a_CraftingGrid.GetWidth(), a_CraftingGrid.GetHeight())); + a_Recipe.Clear(); + if (Recipe.get() == NULL) + { + // Allow plugins to intercept a no-recipe-found situation: + cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, &a_CraftingGrid, &a_Recipe); + return; + } + for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr) + { + a_Recipe.SetIngredient(itr->x, itr->y, itr->m_Item); + } // for itr + a_Recipe.SetResult(Recipe->m_Result); + + // Allow plugins to intercept recipes after they are processed: + cRoot::Get()->GetPluginManager()->CallHookPostCrafting(a_Player, &a_CraftingGrid, &a_Recipe); +} + + + + + +void cCraftingRecipes::LoadRecipes(void) +{ + LOGD("Loading crafting recipes from crafting.txt..."); + ClearRecipes(); + + // Load the crafting.txt file: + cFile f; + if (!f.Open("crafting.txt", cFile::fmRead)) + { + LOGWARNING("Cannot open file \"crafting.txt\", no crafting recipes will be available!"); + return; + } + AString Everything; + f.ReadRestOfFile(Everything); + f.Close(); + + // Split it into lines, then process each line as a single recipe: + AStringVector Split = StringSplit(Everything, "\n"); + int LineNum = 1; + for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr, ++LineNum) + { + // Remove anything after a '#' sign and trim away the whitespace: + AString Recipe = TrimString(itr->substr(0, itr->find('#'))); + if (Recipe.empty()) + { + // Empty recipe + continue; + } + AddRecipeLine(LineNum, Recipe); + } // for itr - Split[] + LOG("Loaded %d crafting recipes", m_Recipes.size()); +} + + + + +void cCraftingRecipes::ClearRecipes(void) +{ + for (cRecipes::iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr) + { + delete *itr; + } + m_Recipes.clear(); +} + + + + + +void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine) +{ + AStringVector Sides = StringSplit(a_RecipeLine, "="); + if (Sides.size() != 2) + { + LOGWARNING("crafting.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + + std::auto_ptr Recipe(new cCraftingRecipes::cRecipe); + + // Parse the result: + AStringVector ResultSplit = StringSplit(Sides[0], ","); + if (ResultSplit.empty()) + { + LOGWARNING("crafting.txt: line %d: Result is empty, ignoring the recipe.", a_LineNum); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + if (!ParseItem(ResultSplit[0], Recipe->m_Result)) + { + LOGWARNING("crafting.txt: line %d: Cannot parse result item, ignoring the recipe.", a_LineNum); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + if (ResultSplit.size() > 1) + { + Recipe->m_Result.m_ItemCount = atoi(ResultSplit[1].c_str()); + if (Recipe->m_Result.m_ItemCount == 0) + { + LOGWARNING("crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + } + else + { + Recipe->m_Result.m_ItemCount = 1; + } + + // Parse each ingredient: + AStringVector Ingredients = StringSplit(Sides[1], "|"); + int Num = 1; + for (AStringVector::const_iterator itr = Ingredients.begin(); itr != Ingredients.end(); ++itr, ++Num) + { + if (!ParseIngredient(*itr, Recipe.get())) + { + LOGWARNING("crafting.txt: line %d: Cannot parse ingredient #%d, ignoring the recipe.", a_LineNum, Num); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + } // for itr - Ingredients[] + + NormalizeIngredients(Recipe.get()); + + m_Recipes.push_back(Recipe.release()); +} + + + + + +bool cCraftingRecipes::ParseItem(const AString & a_String, cItem & a_Item) +{ + // The caller provides error logging + + AStringVector Split = StringSplit(a_String, "^"); + if (Split.empty()) + { + return false; + } + + if (!StringToItem(Split[0], a_Item)) + { + return false; + } + + if (Split.size() > 1) + { + AString Damage = TrimString(Split[1]); + a_Item.m_ItemDamage = atoi(Damage.c_str()); + if ((a_Item.m_ItemDamage == 0) && (Damage.compare("0") != 0)) + { + // Parsing the number failed + return false; + } + } + + // Success + return true; +} + + + + + +bool cCraftingRecipes::ParseIngredient(const AString & a_String, cRecipe * a_Recipe) +{ + // a_String is in this format: "ItemType^damage, X:Y, X:Y, X:Y..." + AStringVector Split = StringSplit(a_String, ","); + if (Split.size() < 2) + { + // Not enough split items + return false; + } + cItem Item; + if (!ParseItem(Split[0], Item)) + { + return false; + } + Item.m_ItemCount = 1; + + cCraftingRecipes::cRecipeSlots TempSlots; + for (AStringVector::const_iterator itr = Split.begin() + 1; itr != Split.end(); ++itr) + { + // Parse the coords in the split item: + AStringVector Coords = StringSplit(*itr, ":"); + if ((Coords.size() == 1) && (TrimString(Coords[0]) == "*")) + { + cCraftingRecipes::cRecipeSlot Slot; + Slot.m_Item = Item; + Slot.x = -1; + Slot.y = -1; + TempSlots.push_back(Slot); + continue; + } + if (Coords.size() != 2) + { + return false; + } + Coords[0] = TrimString(Coords[0]); + Coords[1] = TrimString(Coords[1]); + if (Coords[0].empty() || Coords[1].empty()) + { + return false; + } + cCraftingRecipes::cRecipeSlot Slot; + Slot.m_Item = Item; + switch (Coords[0][0]) + { + case '1': Slot.x = 0; break; + case '2': Slot.x = 1; break; + case '3': Slot.x = 2; break; + case '*': Slot.x = -1; break; + default: + { + return false; + } + } + switch (Coords[1][0]) + { + case '1': Slot.y = 0; break; + case '2': Slot.y = 1; break; + case '3': Slot.y = 2; break; + case '*': Slot.y = -1; break; + default: + { + return false; + } + } + TempSlots.push_back(Slot); + } // for itr - Split[] + + // Append the ingredients: + a_Recipe->m_Ingredients.insert(a_Recipe->m_Ingredients.end(), TempSlots.begin(), TempSlots.end()); + return true; +} + + + + + +void cCraftingRecipes::NormalizeIngredients(cCraftingRecipes::cRecipe * a_Recipe) +{ + // Calculate the minimum coords for ingredients, excluding the "anywhere" items: + int MinX = MAX_GRID_WIDTH, MaxX = 0; + int MinY = MAX_GRID_HEIGHT, MaxY = 0; + for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr) + { + if (itr->x >= 0) + { + MinX = std::min(itr->x, MinX); + MaxX = std::max(itr->x, MaxX); + } + if (itr->y >= 0) + { + MinY = std::min(itr->y, MinY); + MaxY = std::max(itr->y, MaxY); + } + } // for itr - a_Recipe->m_Ingredients[] + + // Move ingredients so that the minimum coords are 0:0 + for (cRecipeSlots::iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr) + { + if (itr->x >= 0) + { + itr->x -= MinX; + } + if (itr->y >= 0) + { + itr->y -= MinY; + } + } // for itr - a_Recipe->m_Ingredients[] + a_Recipe->m_Width = std::max(MaxX - MinX + 1, 1); + a_Recipe->m_Height = std::max(MaxY - MinY + 1, 1); + + // TODO: Compress two same ingredients with the same coords into a single ingredient with increased item count +} + + + + + +cCraftingRecipes::cRecipe * cCraftingRecipes::FindRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight) +{ + ASSERT(a_GridWidth <= MAX_GRID_WIDTH); + ASSERT(a_GridHeight <= MAX_GRID_HEIGHT); + + // Get the real bounds of the crafting grid: + int GridLeft = MAX_GRID_WIDTH, GridTop = MAX_GRID_HEIGHT; + int GridRight = 0, GridBottom = 0; + for (int y = 0; y < a_GridHeight; y++ ) for(int x = 0; x < a_GridWidth; x++) + { + if (!a_CraftingGrid[x + y * a_GridWidth].IsEmpty()) + { + GridRight = std::max(x, GridRight); + GridBottom = std::max(y, GridBottom); + GridLeft = std::min(x, GridLeft); + GridTop = std::min(y, GridTop); + } + } + int GridWidth = GridRight - GridLeft + 1; + int GridHeight = GridBottom - GridTop + 1; + + // Search in the possibly minimized grid, but keep the stride: + const cItem * Grid = a_CraftingGrid + GridLeft + (a_GridWidth * GridTop); + cRecipe * Recipe = FindRecipeCropped(Grid, GridWidth, GridHeight, a_GridWidth); + if (Recipe == NULL) + { + return NULL; + } + + // A recipe has been found, move it to correspond to the original crafting grid: + for (cRecipeSlots::iterator itrS = Recipe->m_Ingredients.begin(); itrS != Recipe->m_Ingredients.end(); ++itrS) + { + itrS->x += GridLeft; + itrS->y += GridTop; + } // for itrS - Recipe->m_Ingredients[] + + return Recipe; +} + + + + + +cCraftingRecipes::cRecipe * cCraftingRecipes::FindRecipeCropped(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride) +{ + for (cRecipes::const_iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr) + { + // Both the crafting grid and the recipes are normalized. The only variable possible is the "anywhere" items. + // This still means that the "anywhere" item may be the one that is offsetting the grid contents to the right or downwards, so we need to check all possible positions. + // E. g. recipe "A, * | B, 1:1 | ..." still needs to check grid for B at 2:2 (in case A was in grid's 1:1) + // Calculate the maximum offsets for this recipe relative to the grid size, and iterate through all combinations of offsets. + // Also, this calculation automatically filters out recipes that are too large for the current grid - the loop won't be entered at all. + + int MaxOfsX = a_GridWidth - (*itr)->m_Width; + int MaxOfsY = a_GridHeight - (*itr)->m_Height; + for (int x = 0; x <= MaxOfsX; x++) for (int y = 0; y <= MaxOfsY; y++) + { + cRecipe * Recipe = MatchRecipe(a_CraftingGrid, a_GridWidth, a_GridHeight, a_GridStride, *itr, x, y); + if (Recipe != NULL) + { + return Recipe; + } + } // for y, for x + } // for itr - m_Recipes[] + + // No matching recipe found + return NULL; +} + + + + + +cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY) +{ + // Check the regular items first: + bool HasMatched[MAX_GRID_WIDTH][MAX_GRID_HEIGHT]; + memset(HasMatched, 0, sizeof(HasMatched)); + for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS) + { + if ((itrS->x < 0) || (itrS->y < 0)) + { + // "Anywhere" item, process later + continue; + } + ASSERT(itrS->x + a_OffsetX < a_GridWidth); + ASSERT(itrS->y + a_OffsetY < a_GridHeight); + int GridID = (itrS->x + a_OffsetX) + a_GridStride * (itrS->y + a_OffsetY); + if ( + (itrS->x >= a_GridWidth) || + (itrS->y >= a_GridHeight) || + (itrS->m_Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type? + (itrS->m_Item.m_ItemCount > a_CraftingGrid[GridID].m_ItemCount) || // not enough items + ( + (itrS->m_Item.m_ItemDamage > 0) && // should compare damage values? + (itrS->m_Item.m_ItemDamage != a_CraftingGrid[GridID].m_ItemDamage) + ) + ) + { + // Doesn't match + return NULL; + } + HasMatched[itrS->x + a_OffsetX][itrS->y + a_OffsetY] = true; + } // for itrS - Recipe->m_Ingredients[] + + // Process the "Anywhere" items now, and only in the cells that haven't matched yet + // The "anywhere" items are processed on a first-come-first-served basis. + // Do not use a recipe with one horizontal and one vertical "anywhere" ("*:1, 1:*") as it may not match properly! + cRecipeSlots MatchedSlots; // Stores the slots of "anywhere" items that have matched, with the match coords + for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS) + { + if ((itrS->x >= 0) && (itrS->y >= 0)) + { + // Regular item, already processed + continue; + } + int StartX = 0, EndX = a_GridWidth - 1; + int StartY = 0, EndY = a_GridHeight - 1; + if (itrS->x >= 0) + { + StartX = itrS->x; + EndX = itrS->x; + } + else if (itrS->y >= 0) + { + StartY = itrS->y; + EndY = itrS->y; + } + bool Found = false; + for (int x = StartX; x <= EndX; x++) + { + for (int y = StartY; y <= EndY; y++) + { + if (HasMatched[x][y]) + { + // Already matched some other item + continue; + } + int GridIdx = x + a_GridStride * y; + if ( + (a_CraftingGrid[GridIdx].m_ItemType == itrS->m_Item.m_ItemType) && + ( + (itrS->m_Item.m_ItemDamage < 0) || // doesn't want damage comparison + (itrS->m_Item.m_ItemDamage == a_CraftingGrid[GridIdx].m_ItemDamage) // the damage matches + ) + ) + { + HasMatched[x][y] = true; + Found = true; + MatchedSlots.push_back(*itrS); + MatchedSlots.back().x = x; + MatchedSlots.back().y = y; + break; + } + } // for y + if (Found) + { + break; + } + } // for x + if (!Found) + { + return NULL; + } + } // for itrS - a_Recipe->m_Ingredients[] + + // Check if the whole grid has matched: + for (int x = 0; x < a_GridWidth; x++) for (int y = 0; y < a_GridHeight; y++) + { + if (!HasMatched[x][y] && !a_CraftingGrid[x + a_GridStride * y].IsEmpty()) + { + // There's an unmatched item in the grid + return NULL; + } + } // for y, for x + + // The recipe has matched. Create a copy of the recipe and set its coords to match the crafting grid: + std::auto_ptr Recipe(new cRecipe); + Recipe->m_Result = a_Recipe->m_Result; + Recipe->m_Width = a_Recipe->m_Width; + Recipe->m_Height = a_Recipe->m_Height; + for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS) + { + if ((itrS->x < 0) || (itrS->y < 0)) + { + // "Anywhere" item, process later + continue; + } + Recipe->m_Ingredients.push_back(*itrS); + } + Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end()); + return Recipe.release(); +} + + + + diff --git a/src/CraftingRecipes.h b/src/CraftingRecipes.h new file mode 100644 index 000000000..9d92cbfab --- /dev/null +++ b/src/CraftingRecipes.h @@ -0,0 +1,172 @@ + +// CraftingRecipes.h + +// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes + + + + +#pragma once + +#include "Item.h" + + + + + +// fwd: cPlayer.h +class cPlayer; + + + + + +class cCraftingGrid // tolua_export +{ // tolua_export +public: + cCraftingGrid(const cCraftingGrid & a_Original); + cCraftingGrid(int a_Width, int a_Height); // tolua_export + cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height); + ~cCraftingGrid(); + + // tolua_begin + int GetWidth (void) const {return m_Width; } + int GetHeight(void) const {return m_Height; } + cItem & GetItem (int x, int y) const; + void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth); + void SetItem (int x, int y, const cItem & a_Item); + void Clear (void); + + /// Removes items in a_Grid from m_Items[] (used by cCraftingRecipe::ConsumeIngredients()) + void ConsumeGrid(const cCraftingGrid & a_Grid); + + /// Dumps the entire crafting grid using LOGD() + void Dump(void); + + // tolua_end + + cItem * GetItems(void) const {return m_Items; } + + /// Copies internal contents into the item array specified. Assumes that the array has the same dimensions as self + void CopyToItems(cItem * a_Items) const; + +protected: + + int m_Width; + int m_Height; + cItem * m_Items; +} ; // tolua_export + + + + + +class cCraftingRecipe // tolua_export +{ // tolua_export +public: + cCraftingRecipe(const cCraftingGrid & a_CraftingGrid); + + // tolua_begin + void Clear (void); + int GetIngredientsWidth (void) const {return m_Ingredients.GetWidth(); } + int GetIngredientsHeight(void) const {return m_Ingredients.GetHeight(); } + cItem & GetIngredient (int x, int y) const {return m_Ingredients.GetItem(x, y); } + const cItem & GetResult (void) const {return m_Result; } + void SetResult (ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth); + void SetResult (const cItem & a_Item) + { + m_Result = a_Item; + } + + void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) + { + m_Ingredients.SetItem(x, y, a_ItemType, a_ItemCount, a_ItemHealth); + } + + void SetIngredient (int x, int y, const cItem & a_Item) + { + m_Ingredients.SetItem(x, y, a_Item); + } + + /// Consumes ingredients from the crafting grid specified + void ConsumeIngredients(cCraftingGrid & a_CraftingGrid); + + /// Dumps the entire recipe using LOGD() + void Dump(void); + // tolua_end + +protected: + + cCraftingGrid m_Ingredients; // Adjusted to correspond to the input crafting grid! + cItem m_Result; +} ; // tolua_export + + + + + +class cCraftingRecipes +{ +public: + static const int MAX_GRID_WIDTH = 3; + static const int MAX_GRID_HEIGHT = 3; + + cCraftingRecipes(void); + ~cCraftingRecipes(); + + /// Returns the recipe for current crafting grid. Doesn't modify the grid. Clears a_Recipe if no recipe found. + void GetRecipe(const cPlayer * a_Player, const cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe); + +protected: + + struct cRecipeSlot + { + cItem m_Item; + int x, y; // 1..3, or -1 for "any" + } ; + typedef std::vector cRecipeSlots; + + /** A single recipe, stored. Each recipe is normalized right after parsing (NormalizeIngredients()) + A normalized recipe starts at (0,0) + */ + struct cRecipe + { + cRecipeSlots m_Ingredients; + cItem m_Result; + + // Size of the regular items in the recipe; "anywhere" items are excluded: + int m_Width; + int m_Height; + } ; + typedef std::vector cRecipes; + + cRecipes m_Recipes; + + void LoadRecipes(void); + void ClearRecipes(void); + + /// Parses the recipe line and adds it into m_Recipes. a_LineNum is used for diagnostic warnings only + void AddRecipeLine(int a_LineNum, const AString & a_RecipeLine); + + /// Parses an item string in the format "[^]", returns true if successful. + bool ParseItem(const AString & a_String, cItem & a_Item); + + /// Parses one ingredient and adds it to the specified recipe. Returns true if successful. + bool ParseIngredient(const AString & a_String, cRecipe * a_Recipe); + + /// Moves the recipe to top-left corner, sets its MinWidth / MinHeight + void NormalizeIngredients(cRecipe * a_Recipe); + + /// Finds a recipe matching the crafting grid. Returns a newly allocated recipe (with all its coords set) or NULL if not found. Caller must delete return value! + cRecipe * FindRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight); + + /// Same as FindRecipe, but the grid is guaranteed to be of minimal dimensions needed + cRecipe * FindRecipeCropped(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride); + + /// Checks if the grid matches the specified recipe, offset by the specified offsets. Returns a matched cRecipe * if so, or NULL if not matching. Caller must delete the return value! + cRecipe * MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY); +} ; + + + + diff --git a/src/Cuboid.cpp b/src/Cuboid.cpp new file mode 100644 index 000000000..ea6f7c453 --- /dev/null +++ b/src/Cuboid.cpp @@ -0,0 +1,117 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Cuboid.h" + + + + + +/// Returns true if the two specified intervals have a non-empty union +static bool DoIntervalsIntersect(int a_Min1, int a_Max1, int a_Min2, int a_Max2) +{ + return ( + ((a_Min1 >= a_Min2) && (a_Min1 <= a_Max2)) || // Start of first interval is within the second interval + ((a_Max1 >= a_Min2) && (a_Max1 <= a_Max2)) || // End of first interval is within the second interval + ((a_Min2 >= a_Min1) && (a_Min2 <= a_Max1)) // Start of second interval is within the first interval + ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCuboid: + +void cCuboid::Assign(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2) +{ + p1.x = a_X1; + p1.y = a_Y1; + p1.z = a_Z1; + p2.x = a_X2; + p2.y = a_Y2; + p2.z = a_Z2; +} + + + + + +void cCuboid::Sort(void) +{ + if (p1.x > p2.x) + { + std::swap(p1.x, p2.x); + } + if (p1.y > p2.y) + { + std::swap(p1.y, p2.y); + } + if (p1.z > p2.z) + { + std::swap(p1.z, p2.z); + } +} + + + + + +bool cCuboid::DoesIntersect(const cCuboid & a_Other) const +{ + // In order for cuboids to intersect, each of their coord intervals need to intersect + return ( + DoIntervalsIntersect(p1.x, p2.x, a_Other.p1.x, a_Other.p2.x) && + DoIntervalsIntersect(p1.y, p2.y, a_Other.p1.y, a_Other.p2.y) && + DoIntervalsIntersect(p1.z, p2.z, a_Other.p1.z, a_Other.p2.z) + ); +} + + + + + +bool cCuboid::IsCompletelyInside(const cCuboid & a_Outer) const +{ + return ( + (p1.x >= a_Outer.p1.x) && + (p2.x <= a_Outer.p2.x) && + (p1.y >= a_Outer.p1.y) && + (p2.y <= a_Outer.p2.y) && + (p1.z >= a_Outer.p1.z) && + (p2.z <= a_Outer.p2.z) + ); +} + + + + + +void cCuboid::Move(int a_OfsX, int a_OfsY, int a_OfsZ) +{ + p1.x += a_OfsX; + p1.y += a_OfsY; + p1.z += a_OfsZ; + p2.x += a_OfsX; + p2.y += a_OfsY; + p2.z += a_OfsZ; +} + + + + + + +bool cCuboid::IsSorted(void) const +{ + return ( + (p1.x <= p2.x) && + (p1.y <= p2.y) && + (p1.z <= p2.z) + ); +} + + + + diff --git a/src/Cuboid.h b/src/Cuboid.h new file mode 100644 index 000000000..44db7b98e --- /dev/null +++ b/src/Cuboid.h @@ -0,0 +1,75 @@ + +#pragma once + +#include "Vector3i.h" +#include "Vector3d.h" + + + + + +// tolua_begin +class cCuboid +{ +public: + // p1 is expected to have the smaller of the coords; Sort() swaps coords to match this + Vector3i p1, p2; + + cCuboid(void) {} + cCuboid(const cCuboid & a_Cuboid ) : p1(a_Cuboid.p1), p2(a_Cuboid.p2) {} + cCuboid(const Vector3i & a_p1, const Vector3i & a_p2) : p1(a_p1), p2(a_p2) {} + cCuboid(int a_X1, int a_Y1, int a_Z1) : p1(a_X1, a_Y1, a_Z1), p2(a_X1, a_Y1, a_Z1) {} + cCuboid(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2) : p1(a_X1, a_Y1, a_Z1), p2(a_X2, a_Y2, a_Z2) {} + + void Assign(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2); + + void Sort(void); + + int DifX(void) const { return p2.x - p1.x; } + int DifY(void) const { return p2.y - p1.y; } + int DifZ(void) const { return p2.z - p1.z; } + + /// Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive. + bool DoesIntersect(const cCuboid & a_Other) const; + + bool IsInside(const Vector3i & v) const + { + return ( + (v.x >= p1.x) && (v.x <= p2.x) && + (v.y >= p1.y) && (v.y <= p2.y) && + (v.z >= p1.z) && (v.z <= p2.z) + ); + } + + bool IsInside(int a_X, int a_Y, int a_Z) const + { + return ( + (a_X >= p1.x) && (a_X <= p2.x) && + (a_Y >= p1.y) && (a_Y <= p2.y) && + (a_Z >= p1.z) && (a_Z <= p2.z) + ); + } + + bool IsInside( const Vector3d & v ) const + { + return ( + (v.x >= p1.x) && (v.x <= p2.x) && + (v.y >= p1.y) && (v.y <= p2.y) && + (v.z >= p1.z) && (v.z <= p2.z) + ); + } + + /// Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords) + bool IsCompletelyInside(const cCuboid & a_Outer) const; + + /// Moves the cuboid by the specified offsets in each direction + void Move(int a_OfsX, int a_OfsY, int a_OfsZ); + + /// Returns true if the coords are properly sorted (lesser in p1, greater in p2) + bool IsSorted(void) const; +} ; +// tolua_end + + + + diff --git a/src/DeadlockDetect.cpp b/src/DeadlockDetect.cpp new file mode 100644 index 000000000..c774c9dce --- /dev/null +++ b/src/DeadlockDetect.cpp @@ -0,0 +1,147 @@ + +// DeadlockDetect.cpp + +// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one + +#include "Globals.h" +#include "DeadlockDetect.h" +#include "Root.h" +#include "World.h" + + + + + +/// Number of milliseconds per cycle +const int CYCLE_MILLISECONDS = 100; + +/// When the number of cycles for the same world age hits this value, it is considered a deadlock +const int NUM_CYCLES_LIMIT = 200; // 200 = twenty seconds + + + + + +cDeadlockDetect::cDeadlockDetect(void) : + super("DeadlockDetect") +{ +} + + + + + +bool cDeadlockDetect::Start(void) +{ + // Read the initial world data: + class cFillIn : + public cWorldListCallback + { + public: + cFillIn(cDeadlockDetect * a_Detect) : + m_Detect(a_Detect) + { + } + + virtual bool Item(cWorld * a_World) override + { + m_Detect->SetWorldAge(a_World->GetName(), a_World->GetWorldAge()); + return false; + } + + protected: + cDeadlockDetect * m_Detect; + } FillIn(this); + cRoot::Get()->ForEachWorld(FillIn); + return super::Start(); +} + + + + + +void cDeadlockDetect::Execute(void) +{ + // Loop until the signal to terminate: + while (!m_ShouldTerminate) + { + // Check the world ages: + class cChecker : + public cWorldListCallback + { + public: + cChecker(cDeadlockDetect * a_Detect) : + m_Detect(a_Detect) + { + } + + protected: + cDeadlockDetect * m_Detect; + + virtual bool Item(cWorld * a_World) override + { + m_Detect->CheckWorldAge(a_World->GetName(), a_World->GetWorldAge()); + return false; + } + } Checker(this); + cRoot::Get()->ForEachWorld(Checker); + + cSleep::MilliSleep(CYCLE_MILLISECONDS); + } // while (should run) +} + + + + + +void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, Int64 a_Age) +{ + m_WorldAges[a_WorldName].m_Age = a_Age; + m_WorldAges[a_WorldName].m_NumCyclesSame = 0; +} + + + + + +void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age) +{ + WorldAges::iterator itr = m_WorldAges.find(a_WorldName); + if (itr == m_WorldAges.end()) + { + ASSERT(!"Unknown world in cDeadlockDetect"); + return; + } + if (itr->second.m_Age == a_Age) + { + itr->second.m_NumCyclesSame += 1; + if (itr->second.m_NumCyclesSame > NUM_CYCLES_LIMIT) + { + DeadlockDetected(); + return; + } + } + else + { + itr->second.m_Age = a_Age; + itr->second.m_NumCyclesSame = 0; + } +} + + + + + +void cDeadlockDetect::DeadlockDetected(void) +{ + ASSERT(!"Deadlock detected"); + + // TODO: Make a crashdump / coredump + + // Crash the server intentionally: + *((volatile int *)0) = 0; +} + + + + diff --git a/src/DeadlockDetect.h b/src/DeadlockDetect.h new file mode 100644 index 000000000..2559c3fff --- /dev/null +++ b/src/DeadlockDetect.h @@ -0,0 +1,65 @@ + +// DeadlockDetect.h + +// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one + +/* +This class simply monitors each world's m_WorldAge, which is expected to grow on each tick. +If the world age doesn't grow for several seconds, it's either because the server is super-overloaded, +or because the world tick thread hangs in a deadlock. We presume the latter and therefore kill the server. +Once we learn to write crashdumps programmatically, we should do so just before killing, to enable debugging. +*/ + + + +#pragma once + +#include "OSSupport/IsThread.h" + + + + + +class cDeadlockDetect : + public cIsThread +{ + typedef cIsThread super; + +public: + cDeadlockDetect(void); + + /// Starts the detection. Hides cIsThread's Start, because we need some initialization + bool Start(void); + +protected: + struct sWorldAge + { + /// Last m_WorldAge that has been detected in this world + Int64 m_Age; + + /// Number of cycles for which the age has been the same + int m_NumCyclesSame; + } ; + + /// Maps world name -> sWorldAge + typedef std::map WorldAges; + + WorldAges m_WorldAges; + + + // cIsThread overrides: + virtual void Execute(void) override; + + /// Sets the initial world age + void SetWorldAge(const AString & a_WorldName, Int64 a_Age); + + /// Checks if the world's age has changed, updates the world's stats; calls DeadlockDetected() if deadlock detected + void CheckWorldAge(const AString & a_WorldName, Int64 a_Age); + + /// Called when a deadlock is detected. Aborts the server. + void DeadlockDetected(void); +} ; + + + + diff --git a/src/Defines.h b/src/Defines.h new file mode 100644 index 000000000..5621aeac1 --- /dev/null +++ b/src/Defines.h @@ -0,0 +1,562 @@ + +#pragma once + + + + + +typedef unsigned char Byte; + +/// List of slot numbers, used for inventory-painting +typedef std::vector cSlotNums; + + + + + + +// tolua_begin + +/// How much light do the blocks emit on their own? +extern unsigned char g_BlockLightValue[]; + +/// How much light do the block consume? +extern unsigned char g_BlockSpreadLightFalloff[]; + +/// Is a block completely transparent? (light doesn't get decreased(?)) +extern bool g_BlockTransparent[]; + +/// Is a block destroyed after a single hit? +extern bool g_BlockOneHitDig[]; + +/// Can a piston break this block? +extern bool g_BlockPistonBreakable[256]; + +/// Can this block hold snow atop? +extern bool g_BlockIsSnowable[256]; + +/// Does this block require a tool to drop? +extern bool g_BlockRequiresSpecialTool[256]; + +/// Is this block solid (player cannot walk through)? +extern bool g_BlockIsSolid[256]; + +/// Can torches be placed on this block? +extern bool g_BlockIsTorchPlaceable[256]; + +/// Experience Orb setup +enum +{ + //open to suggestion on naming convention here :) + MAX_EXPERIENCE_ORB_SIZE = 2000 +} ; + + + + + +/// Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc +enum eBlockFace +{ + BLOCK_FACE_NONE = -1, // Interacting with no block face - swinging the item in the air + BLOCK_FACE_XM = 5, // Interacting with the X- face of the block + BLOCK_FACE_XP = 4, // Interacting with the X+ face of the block + BLOCK_FACE_YM = 0, // Interacting with the Y- face of the block + BLOCK_FACE_YP = 1, // Interacting with the Y+ face of the block + BLOCK_FACE_ZM = 3, // Interacting with the Z- face of the block + BLOCK_FACE_ZP = 2, // Interacting with the Z+ face of the block + + // Synonyms using the (deprecated) world directions: + BLOCK_FACE_BOTTOM = BLOCK_FACE_YM, // Interacting with the bottom face of the block + BLOCK_FACE_TOP = BLOCK_FACE_YP, // Interacting with the top face of the block + BLOCK_FACE_NORTH = BLOCK_FACE_ZP, // Interacting with the northern face of the block + BLOCK_FACE_SOUTH = BLOCK_FACE_ZM, // Interacting with the southern face of the block + BLOCK_FACE_WEST = BLOCK_FACE_XP, // Interacting with the western face of the block + BLOCK_FACE_EAST = BLOCK_FACE_XM, // Interacting with the eastern face of the block +} ; + + + + + +/// PlayerDigging status constants +enum +{ + DIG_STATUS_STARTED = 0, + DIG_STATUS_CANCELLED = 1, + DIG_STATUS_FINISHED = 2, + DIG_STATUS_DROP_HELD = 4, + DIG_STATUS_SHOOT_EAT = 5, +} ; + + + + + +/// Individual actions sent in the WindowClick packet +enum eClickAction +{ + // Sorted by occurrence in the 1.5 protocol + caLeftClick, + caRightClick, + caShiftLeftClick, + caShiftRightClick, + caNumber1, + caNumber2, + caNumber3, + caNumber4, + caNumber5, + caNumber6, + caNumber7, + caNumber8, + caNumber9, + caMiddleClick, + caDropKey, + caCtrlDropKey, + caLeftClickOutside, + caRightClickOutside, + caLeftClickOutsideHoldNothing, + caRightClickOutsideHoldNothing, + caLeftPaintBegin, + caRightPaintBegin, + caLeftPaintProgress, + caRightPaintProgress, + caLeftPaintEnd, + caRightPaintEnd, + caDblClick, + // Add new actions here + caUnknown = 255, + + // Keep this list in sync with ClickActionToString() function below! +} ; + + + + + +enum eGameMode +{ + eGameMode_NotSet = -1, + eGameMode_Survival = 0, + eGameMode_Creative = 1, + eGameMode_Adventure = 2, + + // Easier-to-use synonyms: + gmNotSet = eGameMode_NotSet, + gmSurvival = eGameMode_Survival, + gmCreative = eGameMode_Creative, + gmAdventure = eGameMode_Adventure, + + // These two are used to check GameMode for validity when converting from integers. + gmMax, // Gets automatically assigned + gmMin = 0, +} ; + + + + + +enum eWeather +{ + eWeather_Sunny = 0, + eWeather_Rain = 1, + eWeather_ThunderStorm = 2, + + // Easier-to-use synonyms: + wSunny = eWeather_Sunny, + wRain = eWeather_Rain, + wThunderstorm = eWeather_ThunderStorm, + wStorm = wThunderstorm, +} ; + + + + + +inline const char * ClickActionToString(eClickAction a_ClickAction) +{ + switch (a_ClickAction) + { + case caLeftClick: return "caLeftClick"; + case caRightClick: return "caRightClick"; + case caShiftLeftClick: return "caShiftLeftClick"; + case caShiftRightClick: return "caShiftRightClick"; + case caNumber1: return "caNumber1"; + case caNumber2: return "caNumber2"; + case caNumber3: return "caNumber3"; + case caNumber4: return "caNumber4"; + case caNumber5: return "caNumber5"; + case caNumber6: return "caNumber6"; + case caNumber7: return "caNumber7"; + case caNumber8: return "caNumber8"; + case caNumber9: return "caNumber9"; + case caMiddleClick: return "caMiddleClick"; + case caDropKey: return "caDropKey"; + case caCtrlDropKey: return "caCtrlDropKey"; + case caLeftClickOutside: return "caLeftClickOutside"; + case caRightClickOutside: return "caRightClickOutside"; + case caLeftClickOutsideHoldNothing: return "caLeftClickOutsideHoldNothing"; + case caRightClickOutsideHoldNothing: return "caRightClickOutsideHoldNothing"; + case caLeftPaintBegin: return "caLeftPaintBegin"; + case caRightPaintBegin: return "caRightPaintBegin"; + case caLeftPaintProgress: return "caLeftPaintProgress"; + case caRightPaintProgress: return "caRightPaintProgress"; + case caLeftPaintEnd: return "caLeftPaintEnd"; + case caRightPaintEnd: return "caRightPaintEnd"; + case caDblClick: return "caDblClick"; + + case caUnknown: return "caUnknown"; + } + ASSERT(!"Unknown click action"); + return "caUnknown"; +} + + + + + +inline bool IsValidBlock(int a_BlockType) +{ + if ( + (a_BlockType > -1) && + (a_BlockType <= E_BLOCK_MAX_TYPE_ID) + ) + { + return true; + } + return false; +} + + + + + +inline bool IsValidItem(int a_ItemType) +{ + if ( + ((a_ItemType >= E_ITEM_FIRST) && (a_ItemType <= E_ITEM_MAX_CONSECUTIVE_TYPE_ID)) || // Basic items range + ((a_ItemType >= E_ITEM_FIRST_DISC) && (a_ItemType <= E_ITEM_LAST_DISC)) // Music discs' special range + ) + { + return true; + } + + if (a_ItemType == 0) + { + return false; + } + + return IsValidBlock(a_ItemType); +} + +// tolua_end + + + + + +inline bool IsBlockWater(BLOCKTYPE a_BlockType) +{ + return ((a_BlockType == E_BLOCK_WATER) || (a_BlockType == E_BLOCK_STATIONARY_WATER)); +} + + + + + +inline bool IsBlockLava(BLOCKTYPE a_BlockType) +{ + return ((a_BlockType == E_BLOCK_LAVA) || (a_BlockType == E_BLOCK_STATIONARY_LAVA)); +} + + + + + +inline bool IsBlockLiquid(BLOCKTYPE a_BlockType) +{ + return IsBlockWater(a_BlockType) || IsBlockLava(a_BlockType); +} + + + + + +inline bool IsBlockTypeOfDirt(BLOCKTYPE a_BlockType) +{ + switch (a_BlockType) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + { + return true; + } + } + return false; +} + + + + +inline void AddFaceDirection(int & a_BlockX, int & a_BlockY, int & a_BlockZ, char a_BlockFace, bool a_bInverse = false) // tolua_export +{ // tolua_export + if (!a_bInverse) + { + switch (a_BlockFace) + { + case BLOCK_FACE_BOTTOM: a_BlockY--; break; + case BLOCK_FACE_TOP: a_BlockY++; break; + case BLOCK_FACE_EAST: a_BlockX++; break; + case BLOCK_FACE_WEST: a_BlockX--; break; + case BLOCK_FACE_NORTH: a_BlockZ--; break; + case BLOCK_FACE_SOUTH: a_BlockZ++; break; + default: + { + LOGWARNING("%s: Unknown face: %d", __FUNCTION__, a_BlockFace); + ASSERT(!"AddFaceDirection(): Unknown face"); + break; + } + } + } + else + { + switch (a_BlockFace) + { + case BLOCK_FACE_BOTTOM: a_BlockY++; break; + case BLOCK_FACE_TOP: a_BlockY--; break; + case BLOCK_FACE_EAST: a_BlockX--; break; + case BLOCK_FACE_WEST: a_BlockX++; break; + case BLOCK_FACE_NORTH: a_BlockZ++; break; + case BLOCK_FACE_SOUTH: a_BlockZ--; break; + default: + { + LOGWARNING("%s: Unknown inv face: %d", __FUNCTION__, a_BlockFace); + ASSERT(!"AddFaceDirection(): Unknown face"); + break; + } + } + } +} // tolua_export + + + + + +inline void AddFaceDirection(int & a_BlockX, unsigned char & a_BlockY, int & a_BlockZ, char a_BlockFace, bool a_bInverse = false) +{ + int Y = a_BlockY; + AddFaceDirection(a_BlockX, Y, a_BlockZ, a_BlockFace, a_bInverse); + if (Y < 0) + { + a_BlockY = 0; + } + else if (Y > 255) + { + a_BlockY = 255; + } + else + { + a_BlockY = (unsigned char)Y; + } +} + + + + + +#define PI 3.14159265358979323846264338327950288419716939937510582097494459072381640628620899862803482534211706798f + +inline void EulerToVector(double a_Pan, double a_Pitch, double & a_X, double & a_Y, double & a_Z) +{ + // a_X = sinf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI ); + // a_Y = -sinf ( a_Pitch / 180 * PI ); + // a_Z = -cosf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI ); + a_X = cos(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI); + a_Y = sin(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI); + a_Z = sin(a_Pitch / 180 * PI); +} + + + + + +inline void VectorToEuler(double a_X, double a_Y, double a_Z, double & a_Pan, double & a_Pitch) +{ + if (a_X != 0) + { + a_Pan = atan2(a_Z, a_X) * 180 / PI - 90; + } + else + { + a_Pan = 0; + } + a_Pitch = atan2(a_Y, sqrt((a_X * a_X) + (a_Z * a_Z))) * 180 / PI; +} + + + + + +inline float GetSignf(float a_Val) +{ + return (a_Val < 0.f) ? -1.f : 1.f; +} + + + + + +inline float GetSpecialSignf( float a_Val ) +{ + return (a_Val <= 0.f) ? -1.f : 1.f; +} + + + + +// tolua_begin +namespace ItemCategory +{ + inline bool IsPickaxe(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_PICKAXE) + || (a_ItemID == E_ITEM_STONE_PICKAXE) + || (a_ItemID == E_ITEM_IRON_PICKAXE) + || (a_ItemID == E_ITEM_GOLD_PICKAXE) + || (a_ItemID == E_ITEM_DIAMOND_PICKAXE); + } + + + + inline bool IsAxe(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_AXE) + || (a_ItemID == E_ITEM_STONE_AXE) + || (a_ItemID == E_ITEM_IRON_AXE) + || (a_ItemID == E_ITEM_GOLD_AXE) + || (a_ItemID == E_ITEM_DIAMOND_AXE); + } + + + + inline bool IsSword(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_SWORD) + || (a_ItemID == E_ITEM_STONE_SWORD) + || (a_ItemID == E_ITEM_IRON_SWORD) + || (a_ItemID == E_ITEM_GOLD_SWORD) + || (a_ItemID == E_ITEM_DIAMOND_SWORD); + } + + + + inline bool IsHoe(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_HOE) + || (a_ItemID == E_ITEM_STONE_HOE) + || (a_ItemID == E_ITEM_IRON_HOE) + || (a_ItemID == E_ITEM_GOLD_HOE) + || (a_ItemID == E_ITEM_DIAMOND_HOE); + } + + + + inline bool IsShovel(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_SHOVEL) + || (a_ItemID == E_ITEM_STONE_SHOVEL) + || (a_ItemID == E_ITEM_IRON_SHOVEL) + || (a_ItemID == E_ITEM_GOLD_SHOVEL) + || (a_ItemID == E_ITEM_DIAMOND_SHOVEL); + } + + + + inline bool IsTool(short a_ItemID) + { + return IsPickaxe( a_ItemID ) + || IsAxe ( a_ItemID ) + || IsSword ( a_ItemID ) + || IsHoe ( a_ItemID ) + || IsShovel ( a_ItemID ); + } + + + + inline bool IsHelmet(short a_ItemType) + { + return ( + (a_ItemType == E_ITEM_LEATHER_CAP) || + (a_ItemType == E_ITEM_GOLD_HELMET) || + (a_ItemType == E_ITEM_CHAIN_HELMET) || + (a_ItemType == E_ITEM_IRON_HELMET) || + (a_ItemType == E_ITEM_DIAMOND_HELMET) + ); + } + + + + inline bool IsChestPlate(short a_ItemType) + { + return ( + (a_ItemType == E_ITEM_LEATHER_TUNIC) || + (a_ItemType == E_ITEM_GOLD_CHESTPLATE) || + (a_ItemType == E_ITEM_CHAIN_CHESTPLATE) || + (a_ItemType == E_ITEM_IRON_CHESTPLATE) || + (a_ItemType == E_ITEM_DIAMOND_CHESTPLATE) + ); + } + + + + inline bool IsLeggings(short a_ItemType) + { + return ( + (a_ItemType == E_ITEM_LEATHER_PANTS) || + (a_ItemType == E_ITEM_GOLD_LEGGINGS) || + (a_ItemType == E_ITEM_CHAIN_LEGGINGS) || + (a_ItemType == E_ITEM_IRON_LEGGINGS) || + (a_ItemType == E_ITEM_DIAMOND_LEGGINGS) + ); + } + + + + inline bool IsBoots(short a_ItemType) + { + return ( + (a_ItemType == E_ITEM_LEATHER_BOOTS) || + (a_ItemType == E_ITEM_GOLD_BOOTS) || + (a_ItemType == E_ITEM_CHAIN_BOOTS) || + (a_ItemType == E_ITEM_IRON_BOOTS) || + (a_ItemType == E_ITEM_DIAMOND_BOOTS) + ); + } + + + + inline bool IsArmor(short a_ItemType) + { + return ( + IsHelmet(a_ItemType) || + IsChestPlate(a_ItemType) || + IsLeggings(a_ItemType) || + IsBoots(a_ItemType) + ); + } +} +// tolua_end + + +inline bool BlockRequiresSpecialTool(BLOCKTYPE a_BlockType) +{ + if(!IsValidBlock(a_BlockType)) return false; + return g_BlockRequiresSpecialTool[a_BlockType]; +} + + + + + + diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp new file mode 100644 index 000000000..6b53d0b52 --- /dev/null +++ b/src/Enchantments.cpp @@ -0,0 +1,299 @@ +// Enchantments.cpp + +// Implements the cEnchantments class representing a storage for item enchantments and stored-enchantments + +#include "Globals.h" +#include "Enchantments.h" +#include "WorldStorage/FastNBT.h" + + + + + +cEnchantments::cEnchantments(void) +{ + // Nothing needed yet, but the constructor needs to be declared and impemented in order to be usable +} + + + + + +cEnchantments::cEnchantments(const AString & a_StringSpec) +{ + AddFromString(a_StringSpec); +} + + + + + +void cEnchantments::AddFromString(const AString & a_StringSpec) +{ + // Add enchantments in the stringspec; if a specified enchantment already exists, overwrites it + + // Split the StringSpec into separate declarations, each in the form "id=lvl": + AStringVector Decls = StringSplit(a_StringSpec, ";"); + for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr) + { + // Split each declaration into the id and lvl part: + if (itr->empty()) + { + // The decl is empty (may happen if there's an extra semicolon at the end), ignore silently + continue; + } + AStringVector Split = StringSplitAndTrim(*itr, "="); + if (Split.size() != 2) + { + // Malformed decl + LOG("%s: Malformed enchantment decl: \"%s\", skipping.", __FUNCTION__, itr->c_str()); + continue; + } + int id = atoi(Split[0].c_str()); + if ((id == 0) && (Split[0] != "0")) + { + id = StringToEnchantmentID(Split[0]); + } + int lvl = atoi(Split[1].c_str()); + if ( + ((id <= 0) && (Split[0] != "0")) || + ((lvl == 0) && (Split[1] != "0")) + ) + { + // Numbers failed to parse + LOG("%s: Failed to parse enchantment declaration for numbers: \"%s\" and \"%s\", skipping.", + __FUNCTION__, Split[0].c_str(), Split[1].c_str() + ); + continue; + } + SetLevel(id, lvl); + } // for itr - Decls[] +} + + + + + +AString cEnchantments::ToString(void) const +{ + // Serialize all the enchantments into a string + AString res; + for (cEnchantments::cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) + { + AppendPrintf(res, "%d=%d;", itr->first, itr->second); + } // for itr - m_Enchantments[] + return res; +} + + + + + +int cEnchantments::GetLevel(int a_EnchantmentID) const +{ + // Return the level for the specified enchantment; 0 if not stored + cMap::const_iterator itr = m_Enchantments.find(a_EnchantmentID); + if (itr != m_Enchantments.end()) + { + return itr->second; + } + + // Not stored, return zero + return 0; +} + + + + + +void cEnchantments::SetLevel(int a_EnchantmentID, int a_Level) +{ + // Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 + if (a_Level == 0) + { + // Delete enchantment, if present: + cMap::iterator itr = m_Enchantments.find(a_EnchantmentID); + if (itr != m_Enchantments.end()) + { + m_Enchantments.erase(itr); + } + } + else + { + // Add / overwrite enchantment + m_Enchantments[a_EnchantmentID] = a_Level; + } +} + + + + + + +void cEnchantments::Clear(void) +{ + m_Enchantments.clear(); +} + + + + + +bool cEnchantments::IsEmpty(void) const +{ + return m_Enchantments.empty(); +} + + + + + +int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName) +{ + struct + { + int m_Value; + const char * m_Name; + } EnchantmentNames[] = + { + { enchProtection, "Protection"}, + { enchFireProtection, "FireProtection"}, + { enchFeatherFalling, "FeatherFalling"}, + { enchBlastProtection, "BlastProtection"}, + { enchProjectileProtection, "ProjectileProtection"}, + { enchRespiration, "Respiration"}, + { enchAquaAffinity, "AquaAffinity"}, + { enchThorns, "Thorns"}, + { enchSharpness, "Sharpness"}, + { enchSmite, "Smite"}, + { enchBaneOfArthropods, "BaneOfArthropods"}, + { enchKnockback, "Knockback"}, + { enchFireAspect, "FireAspect"}, + { enchLooting, "Looting"}, + { enchEfficiency, "Efficiency"}, + { enchSilkTouch, "SilkTouch"}, + { enchUnbreaking, "Unbreaking"}, + { enchFortune, "Fortune"}, + { enchPower, "Power"}, + { enchPunch, "Punch"}, + { enchFlame, "Flame"}, + { enchInfinity, "Infinity"}, + { enchLuckOfTheSea, "LuckOfTheSea"}, + { enchLure, "Lure"}, + } ; + for (int i = 0; i < ARRAYCOUNT(EnchantmentNames); i++) + { + if (NoCaseCompare(EnchantmentNames[i].m_Name, a_EnchantmentName) == 0) + { + return EnchantmentNames[i].m_Value; + } + } // for i - EnchantmentNames[] + return -1; +} + + + + + +bool cEnchantments::operator ==(const cEnchantments & a_Other) const +{ + return m_Enchantments == a_Other.m_Enchantments; +} + + + + + +bool cEnchantments::operator !=(const cEnchantments & a_Other) const +{ + return m_Enchantments != a_Other.m_Enchantments; +} + + + + + +void cEnchantments::WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const +{ + // Write the enchantments into the specified NBT writer + // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments") + + a_Writer.BeginList(a_ListTagName, TAG_Compound); + for (cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) + { + a_Writer.BeginCompound(""); + a_Writer.AddShort("id", itr->first); + a_Writer.AddShort("lvl", itr->second); + a_Writer.EndCompound(); + } // for itr - m_Enchantments[] + a_Writer.EndList(); +} + + + + + +void cEnchantments::ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx) +{ + // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments) + + // Verify that the tag is a list: + if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List) + { + LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed", + __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx) + ); + ASSERT(!"Bad EnchListTag type"); + return; + } + + // Verify that the list is of Compounds: + if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound) + { + LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed", + __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx) + ); + ASSERT(!"Bad EnchListTag children type"); + return; + } + + Clear(); + + // Iterate over all the compound children, parse an enchantment from each: + for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag)) + { + // tag is the compound inside the "ench" list tag + ASSERT(a_NBT.GetType(tag) == TAG_Compound); + + // Search for the id and lvl tags' values: + int id = -1, lvl = -1; + for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch)) + { + if (a_NBT.GetType(ch) != TAG_Short) + { + continue; + } + if (a_NBT.GetName(ch) == "id") + { + id = a_NBT.GetShort(ch); + } + else if (a_NBT.GetName(ch) == "lvl") + { + lvl = a_NBT.GetShort(ch); + } + } // for ch - children of the compound tag + + if ((id == -1) || (lvl <= 0)) + { + // Failed to parse either the id or the lvl, skip this compound + continue; + } + + // Store the enchantment: + m_Enchantments[id] = lvl; + } // for tag - children of the ench list tag +} + + + + diff --git a/src/Enchantments.h b/src/Enchantments.h new file mode 100644 index 000000000..7581b87b5 --- /dev/null +++ b/src/Enchantments.h @@ -0,0 +1,115 @@ +// Enchantments.h + +// Declares the cEnchantments class representing a storage for item enchantments and stored-enchantments + + + + + +#pragma once + + + + + +// fwd: WorldStorage/FastNBT.h +class cFastNBTWriter; +class cParsedNBT; + + + + + +// tolua_begin + +/** Class that stores item enchantments or stored-enchantments +The enchantments may be serialized to a stringspec and read back from such stringspec. +The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end, +mapping each enchantment's id onto its level. ID may be either a number or the enchantment name. +Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments. +Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs. +*/ +class cEnchantments +{ +public: + /// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs ) + enum + { + enchProtection = 0, + enchFireProtection = 1, + enchFeatherFalling = 2, + enchBlastProtection = 3, + enchProjectileProtection = 4, + enchRespiration = 5, + enchAquaAffinity = 6, + enchThorns = 7, + enchSharpness = 16, + enchSmite = 17, + enchBaneOfArthropods = 18, + enchKnockback = 19, + enchFireAspect = 20, + enchLooting = 21, + enchEfficiency = 32, + enchSilkTouch = 33, + enchUnbreaking = 34, + enchFortune = 35, + enchPower = 48, + enchPunch = 49, + enchFlame = 50, + enchInfinity = 51, + enchLuckOfTheSea = 61, + enchLure = 62, + } ; + + /// Creates an empty enchantments container + cEnchantments(void); + + /// Creates an enchantments container filled with enchantments parsed from stringspec + cEnchantments(const AString & a_StringSpec); + + /// Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it + void AddFromString(const AString & a_StringSpec); + + /// Serializes all the enchantments into a string + AString ToString(void) const; + + /// Returns the level for the specified enchantment; 0 if not stored + int GetLevel(int a_EnchantmentID) const; + + /// Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 + void SetLevel(int a_EnchantmentID, int a_Level); + + /// Removes all enchantments + void Clear(void); + + /// Returns true if there are no enchantments + bool IsEmpty(void) const; + + /// Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive + static int StringToEnchantmentID(const AString & a_EnchantmentName); + + /// Returns true if a_Other contains exactly the same enchantments and levels + bool operator ==(const cEnchantments & a_Other) const; + + // tolua_end + + /// Returns true if a_Other doesn't contain exactly the same enchantments and levels + bool operator !=(const cEnchantments & a_Other) const; + + /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") + void WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const; + + /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) + void ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx); + +protected: + /// Maps enchantment ID -> enchantment level + typedef std::map cMap; + + /// Currently stored enchantments + cMap m_Enchantments; +} ; // tolua_export + + + + diff --git a/src/Endianness.h b/src/Endianness.h new file mode 100644 index 000000000..86eb369f5 --- /dev/null +++ b/src/Endianness.h @@ -0,0 +1,70 @@ + +#pragma once + + + + + +// Changes endianness +inline unsigned long long HostToNetwork8(const void* a_Value ) +{ + unsigned long long __HostToNetwork8; + memcpy( &__HostToNetwork8, a_Value, sizeof( __HostToNetwork8 ) ); + __HostToNetwork8 = (( ( (unsigned long long)htonl((u_long)__HostToNetwork8) ) << 32) + htonl(__HostToNetwork8 >> 32)); + return __HostToNetwork8; +} + + + + + +inline unsigned int HostToNetwork4(const void* a_Value ) +{ + unsigned int __HostToNetwork4; + memcpy( &__HostToNetwork4, a_Value, sizeof( __HostToNetwork4 ) ); + __HostToNetwork4 = ntohl( __HostToNetwork4 ); + return __HostToNetwork4; +} + + + + + +inline double NetworkToHostDouble8(const void* a_Value ) +{ +#define ntohll(x) ((((unsigned long long)ntohl((u_long)x)) << 32) + ntohl(x >> 32)) + unsigned long long buf = 0;//(*(unsigned long long*)a_Value); + memcpy( &buf, a_Value, 8 ); + buf = ntohll(buf); + double x; + memcpy(&x, &buf, sizeof(double)); + return x; +} + + + + + +inline long long NetworkToHostLong8(const void * a_Value ) +{ + unsigned long long buf = *(unsigned long long*)a_Value; + buf = ntohll(buf); + return *reinterpret_cast(&buf); +} + + + + + +inline float NetworkToHostFloat4(const void* a_Value ) +{ + u_long buf = *(u_long*)a_Value; + buf = ntohl( buf ); + float x = 0; + memcpy( &x, &buf, sizeof(float) ); + return x; +} + + + + diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp new file mode 100644 index 000000000..56e766dd4 --- /dev/null +++ b/src/Entities/Boat.cpp @@ -0,0 +1,87 @@ + +// Boat.cpp + +// Implements the cBoat class representing a boat in the world + +#include "Globals.h" +#include "Boat.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "Player.h" + + + + + +cBoat::cBoat(double a_X, double a_Y, double a_Z) : + super(etBoat, a_X, a_Y, a_Z, 0.98, 0.7) +{ + SetMass(20.f); + SetMaxHealth(6); + SetHealth(6); +} + + + + +void cBoat::SpawnOn(cClientHandle & a_ClientHandle) +{ + a_ClientHandle.SendSpawnVehicle(*this, 1); +} + + + + + +void cBoat::DoTakeDamage(TakeDamageInfo & TDI) +{ + super::DoTakeDamage(TDI); + + if (GetHealth() == 0) + { + Destroy(true); + } +} + + + + + +void cBoat::OnRightClicked(cPlayer & a_Player) +{ + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + // This player is already sitting in, they want out. + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + // Another player is already sitting in here, cannot attach + return; + } + + // Detach whatever is sitting in this boat now: + m_Attachee->Detach(); + } + + // Attach the player to this boat + a_Player.AttachTo(this); +} + + + + + +void cBoat::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + super::HandlePhysics(a_Dt, a_Chunk); + BroadcastMovementUpdate(); +} + + + + diff --git a/src/Entities/Boat.h b/src/Entities/Boat.h new file mode 100644 index 000000000..8c51ab86c --- /dev/null +++ b/src/Entities/Boat.h @@ -0,0 +1,37 @@ + +// Boat.h + +// Declares the cBoat class representing a boat in the world + + + + + +#pragma once + +#include "Entity.h" + + + + + +class cBoat : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cBoat); + + // cEntity overrides: + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + + cBoat(double a_X, double a_Y, double a_Z); +} ; + + + + diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp new file mode 100644 index 000000000..3bea7bc01 --- /dev/null +++ b/src/Entities/Entity.cpp @@ -0,0 +1,1450 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Entity.h" +#include "../World.h" +#include "../Server.h" +#include "../Root.h" +#include "../Vector3d.h" +#include "../Matrix4f.h" +#include "../ReferenceManager.h" +#include "../ClientHandle.h" +#include "../Chunk.h" +#include "../Simulator/FluidSimulator.h" +#include "../PluginManager.h" +#include "../Tracer.h" +#include "Minecart.h" + + + + + +int cEntity::m_EntityCount = 0; +cCriticalSection cEntity::m_CSCount; + + + + + +cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) + : m_UniqueID(0) + , m_Health(1) + , m_MaxHealth(1) + , m_AttachedTo(NULL) + , m_Attachee(NULL) + , m_Referencers(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCERS)) + , m_References(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCES)) + , m_HeadYaw( 0.0 ) + , m_Rot(0.0, 0.0, 0.0) + , m_Pos(a_X, a_Y, a_Z) + , m_Mass (0.001) //Default 1g + , m_bDirtyHead(true) + , m_bDirtyOrientation(true) + , m_bDirtyPosition(true) + , m_bDirtySpeed(true) + , m_bOnGround( false ) + , m_Gravity( -9.81f ) + , m_IsInitialized(false) + , m_LastPosX( 0.0 ) + , m_LastPosY( 0.0 ) + , m_LastPosZ( 0.0 ) + , m_TimeLastTeleportPacket(0) + , m_TimeLastMoveReltPacket(0) + , m_TimeLastSpeedPacket(0) + , m_EntityType(a_EntityType) + , m_World(NULL) + , m_TicksSinceLastBurnDamage(0) + , m_TicksSinceLastLavaDamage(0) + , m_TicksSinceLastFireDamage(0) + , m_TicksSinceLastVoidDamage(0) + , m_TicksLeftBurning(0) + , m_WaterSpeed(0, 0, 0) + , m_Width(a_Width) + , m_Height(a_Height) +{ + cCSLock Lock(m_CSCount); + m_EntityCount++; + m_UniqueID = m_EntityCount; +} + + + + + +cEntity::~cEntity() +{ + ASSERT(!m_World->HasEntity(m_UniqueID)); // Before deleting, the entity needs to have been removed from the world + + LOGD("Deleting entity %d at pos {%.2f, %.2f, %.2f} ~ [%d, %d]; ptr %p", + m_UniqueID, + m_Pos.x, m_Pos.y, m_Pos.z, + (int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width), + this + ); + + if (m_AttachedTo != NULL) + { + Detach(); + } + if (m_Attachee != NULL) + { + m_Attachee->Detach(); + } + + if (m_IsInitialized) + { + LOGWARNING("ERROR: Entity deallocated without being destroyed"); + ASSERT(!"Entity deallocated without being destroyed or unlinked"); + } + delete m_Referencers; + delete m_References; +} + + + + + +const char * cEntity::GetClass(void) const +{ + return "cEntity"; +} + + + + + +const char * cEntity::GetClassStatic(void) +{ + return "cEntity"; +} + + + + + +const char * cEntity::GetParentClass(void) const +{ + return ""; +} + + + + + +bool cEntity::Initialize(cWorld * a_World) +{ + if (cPluginManager::Get()->CallHookSpawningEntity(*a_World, *this)) + { + return false; + } + + LOGD("Initializing entity #%d (%s) at {%.02f, %.02f, %.02f}", + m_UniqueID, GetClass(), m_Pos.x, m_Pos.y, m_Pos.z + ); + m_IsInitialized = true; + m_World = a_World; + m_World->AddEntity(this); + + cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this); + + // Spawn the entity on the clients: + a_World->BroadcastSpawnEntity(*this); + + return true; +} + + + + + +void cEntity::WrapHeadYaw(void) +{ + while (m_HeadYaw > 180.f) m_HeadYaw -= 360.f; // Wrap it + while (m_HeadYaw < -180.f) m_HeadYaw += 360.f; +} + + + + + +void cEntity::WrapRotation(void) +{ + while (m_Rot.x > 180.f) m_Rot.x -= 360.f; // Wrap it + while (m_Rot.x < -180.f) m_Rot.x += 360.f; + while (m_Rot.y > 180.f) m_Rot.y -= 360.f; + while (m_Rot.y < -180.f) m_Rot.y += 360.f; +} + + + + +void cEntity::WrapSpeed(void) +{ + // There shoudn't be a need for flipping the flag on because this function is called + // after any update, so the flag is already turned on + if (m_Speed.x > 78.0f) m_Speed.x = 78.0f; + else if (m_Speed.x < -78.0f) m_Speed.x = -78.0f; + if (m_Speed.y > 78.0f) m_Speed.y = 78.0f; + else if (m_Speed.y < -78.0f) m_Speed.y = -78.0f; + if (m_Speed.z > 78.0f) m_Speed.z = 78.0f; + else if (m_Speed.z < -78.0f) m_Speed.z = -78.0f; +} + + + + + +void cEntity::Destroy(bool a_ShouldBroadcast) +{ + if (!m_IsInitialized) + { + return; + } + + if (a_ShouldBroadcast) + { + m_World->BroadcastDestroyEntity(*this); + } + + m_IsInitialized = false; + + Destroyed(); +} + + + + + +void cEntity::TakeDamage(cEntity & a_Attacker) +{ + int RawDamage = a_Attacker.GetRawDamageAgainst(*this); + + TakeDamage(dtAttack, &a_Attacker, RawDamage, a_Attacker.GetKnockbackAmountAgainst(*this)); +} + + + + + +void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount) +{ + int FinalDamage = a_RawDamage - GetArmorCoverAgainst(a_Attacker, a_DamageType, a_RawDamage); + cEntity::TakeDamage(a_DamageType, a_Attacker, a_RawDamage, FinalDamage, a_KnockbackAmount); +} + + + + + +void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount) +{ + TakeDamageInfo TDI; + TDI.DamageType = a_DamageType; + TDI.Attacker = a_Attacker; + TDI.RawDamage = a_RawDamage; + TDI.FinalDamage = a_FinalDamage; + Vector3d Heading; + Heading.x = sin(GetRotation()); + Heading.y = 0.4; // TODO: adjust the amount of "up" knockback when testing + Heading.z = cos(GetRotation()); + TDI.Knockback = Heading * a_KnockbackAmount; + DoTakeDamage(TDI); +} + + + + + +void cEntity::SetRotationFromSpeed(void) +{ + const double EPS = 0.0000001; + if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS)) + { + // atan2() may overflow or is undefined, pick any number + SetRotation(0); + return; + } + SetRotation(atan2(m_Speed.x, m_Speed.z) * 180 / PI); +} + + + + + +void cEntity::SetPitchFromSpeed(void) +{ + const double EPS = 0.0000001; + double xz = sqrt(m_Speed.x * m_Speed.x + m_Speed.z * m_Speed.z); // Speed XZ-plane component + if ((abs(xz) < EPS) && (abs(m_Speed.y) < EPS)) + { + // atan2() may overflow or is undefined, pick any number + SetPitch(0); + return; + } + SetPitch(atan2(m_Speed.y, xz) * 180 / PI); +} + + + + + +void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI)) + { + return; + } + + if (m_Health <= 0) + { + // Can't take damage if already dead + return; + } + + m_Health -= (short)a_TDI.FinalDamage; + + // TODO: Apply damage to armor + + if (m_Health < 0) + { + m_Health = 0; + } + + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT); + + if (m_Health <= 0) + { + KilledBy(a_TDI.Attacker); + } +} + + + + + +int cEntity::GetRawDamageAgainst(const cEntity & a_Receiver) +{ + // Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items + // Ref: http://www.minecraftwiki.net/wiki/Damage#Dealing_damage as of 2012_12_20 + switch (this->GetEquippedWeapon().m_ItemType) + { + case E_ITEM_WOODEN_SWORD: return 4; + case E_ITEM_GOLD_SWORD: return 4; + case E_ITEM_STONE_SWORD: return 5; + case E_ITEM_IRON_SWORD: return 6; + case E_ITEM_DIAMOND_SWORD: return 7; + + case E_ITEM_WOODEN_AXE: return 3; + case E_ITEM_GOLD_AXE: return 3; + case E_ITEM_STONE_AXE: return 4; + case E_ITEM_IRON_AXE: return 5; + case E_ITEM_DIAMOND_AXE: return 6; + + case E_ITEM_WOODEN_PICKAXE: return 2; + case E_ITEM_GOLD_PICKAXE: return 2; + case E_ITEM_STONE_PICKAXE: return 3; + case E_ITEM_IRON_PICKAXE: return 4; + case E_ITEM_DIAMOND_PICKAXE: return 5; + + case E_ITEM_WOODEN_SHOVEL: return 1; + case E_ITEM_GOLD_SHOVEL: return 1; + case E_ITEM_STONE_SHOVEL: return 2; + case E_ITEM_IRON_SHOVEL: return 3; + case E_ITEM_DIAMOND_SHOVEL: return 4; + } + // All other equipped items give a damage of 1: + return 1; +} + + + + + +int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage) +{ + // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover + + // Filter out damage types that are not protected by armor: + // Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20 + switch (a_DamageType) + { + case dtOnFire: + case dtSuffocating: + case dtDrowning: // TODO: This one could be a special case - in various MC versions (PC vs XBox) it is and isn't armor-protected + case dtStarving: + case dtInVoid: + case dtPoisoning: + case dtPotionOfHarming: + case dtFalling: + case dtLightning: + { + return 0; + } + } + + // Add up all armor points: + // Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20 + int ArmorValue = 0; + switch (GetEquippedHelmet().m_ItemType) + { + case E_ITEM_LEATHER_CAP: ArmorValue += 1; break; + case E_ITEM_GOLD_HELMET: ArmorValue += 2; break; + case E_ITEM_CHAIN_HELMET: ArmorValue += 2; break; + case E_ITEM_IRON_HELMET: ArmorValue += 2; break; + case E_ITEM_DIAMOND_HELMET: ArmorValue += 3; break; + } + switch (GetEquippedChestplate().m_ItemType) + { + case E_ITEM_LEATHER_TUNIC: ArmorValue += 3; break; + case E_ITEM_GOLD_CHESTPLATE: ArmorValue += 5; break; + case E_ITEM_CHAIN_CHESTPLATE: ArmorValue += 5; break; + case E_ITEM_IRON_CHESTPLATE: ArmorValue += 6; break; + case E_ITEM_DIAMOND_CHESTPLATE: ArmorValue += 8; break; + } + switch (GetEquippedLeggings().m_ItemType) + { + case E_ITEM_LEATHER_PANTS: ArmorValue += 2; break; + case E_ITEM_GOLD_LEGGINGS: ArmorValue += 3; break; + case E_ITEM_CHAIN_LEGGINGS: ArmorValue += 4; break; + case E_ITEM_IRON_LEGGINGS: ArmorValue += 5; break; + case E_ITEM_DIAMOND_LEGGINGS: ArmorValue += 6; break; + } + switch (GetEquippedBoots().m_ItemType) + { + case E_ITEM_LEATHER_BOOTS: ArmorValue += 1; break; + case E_ITEM_GOLD_BOOTS: ArmorValue += 1; break; + case E_ITEM_CHAIN_BOOTS: ArmorValue += 1; break; + case E_ITEM_IRON_BOOTS: ArmorValue += 2; break; + case E_ITEM_DIAMOND_BOOTS: ArmorValue += 3; break; + } + + // TODO: Special armor cases, such as wool, saddles, dog's collar + // Ref.: http://www.minecraftwiki.net/wiki/Armor#Mob_armor as of 2012_12_20 + + // Now ArmorValue is in [0, 20] range, which corresponds to [0, 80%] protection. Calculate the hitpoints from that: + return a_Damage * (ArmorValue * 4) / 100; +} + + + + + +double cEntity::GetKnockbackAmountAgainst(const cEntity & a_Receiver) +{ + // Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit + + // TODO: Enchantments + return 1; +} + + + + + +void cEntity::KilledBy(cEntity * a_Killer) +{ + m_Health = 0; + + cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_Killer); + + if (m_Health > 0) + { + // Plugin wants to 'unkill' the pawn. Abort + return; + } + + // Drop loot: + cItems Drops; + GetDrops(Drops, a_Killer); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); + + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD); +} + + + + + +void cEntity::Heal(int a_HitPoints) +{ + m_Health += a_HitPoints; + if (m_Health > m_MaxHealth) + { + m_Health = m_MaxHealth; + } +} + + + + + +void cEntity::SetHealth(int a_Health) +{ + m_Health = std::max(0, std::min(m_MaxHealth, a_Health)); +} + + + + + +void cEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (m_AttachedTo != NULL) + { + if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5) + { + SetPosition(m_AttachedTo->GetPosition()); + } + } + else + { + if (a_Chunk.IsValid()) + { + HandlePhysics(a_Dt, a_Chunk); + } + } + if (a_Chunk.IsValid()) + { + TickBurning(a_Chunk); + } + if ((a_Chunk.IsValid()) && (GetPosY() < -46)) + { + TickInVoid(a_Chunk); + } + else { m_TicksSinceLastVoidDamage = 0; } +} + + + + + +void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + // TODO Add collision detection with entities. + a_Dt /= 1000; // Convert from msec to sec + Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ()); + Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ()); + int BlockX = (int) floor(NextPos.x); + int BlockY = (int) floor(NextPos.y); + int BlockZ = (int) floor(NextPos.z); + + if ((BlockY >= cChunkDef::Height) || (BlockY < 0)) + { + // Outside of the world + + cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); + // See if we can commit our changes. If not, we will discard them. + if (NextChunk != NULL) + { + SetSpeed(NextSpeed); + NextPos += (NextSpeed * a_Dt); + SetPosition(NextPos); + } + return; + } + + // Make sure we got the correct chunk and a valid one. No one ever knows... + cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); + if (NextChunk != NULL) + { + int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width); + int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width); + BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ ); + BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; + if (!g_BlockIsSolid[BlockIn]) // Making sure we are not inside a solid block + { + if (m_bOnGround) // check if it's still on the ground + { + if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water. + { + m_bOnGround = false; + } + } + } + else + { + // Push out entity. + BLOCKTYPE GotBlock; + + static const struct + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + } ; + + bool IsNoAirSurrounding = true; + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock)) + { + // The pickup is too close to an unloaded chunk, bail out of any physics handling + return; + } + if (!g_BlockIsSolid[GotBlock]) + { + NextPos.x += gCrossCoords[i].x; + NextPos.z += gCrossCoords[i].z; + IsNoAirSurrounding = false; + break; + } + } // for i - gCrossCoords[] + + if (IsNoAirSurrounding) + { + NextPos.y += 0.5; + } + + m_bOnGround = true; + + LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}", + m_UniqueID, GetClass(), BlockX, BlockY, BlockZ + ); + } + + if (!m_bOnGround) + { + float fallspeed; + if (IsBlockWater(BlockIn)) + { + fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water. + } + else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts + { + fallspeed = 0; + m_bOnGround = true; + } + else if (BlockIn == E_BLOCK_COBWEB) + { + NextSpeed.y *= 0.05; // Reduce overall falling speed + fallspeed = 0; // No falling. + } + else + { + // Normal gravity + fallspeed = m_Gravity * a_Dt; + } + NextSpeed.y += fallspeed; + } + else + { + if (IsMinecart()) + { + if (!IsBlockRail(BlockBelow)) + { + // Friction if minecart is off track, otherwise, Minecart.cpp handles this + if (NextSpeed.SqrLength() > 0.0004f) + { + NextSpeed.x *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.x) < 0.05) + { + NextSpeed.x = 0; + } + NextSpeed.z *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.z) < 0.05) + { + NextSpeed.z = 0; + } + } + } + } + else + { + // Friction for non-minecarts + if (NextSpeed.SqrLength() > 0.0004f) + { + NextSpeed.x *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.x) < 0.05) + { + NextSpeed.x = 0; + } + NextSpeed.z *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.z) < 0.05) + { + NextSpeed.z = 0; + } + } + } + } + + // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we + // might have different speed modifiers according to terrain. + if (BlockIn == E_BLOCK_COBWEB) + { + NextSpeed.x *= 0.25; + NextSpeed.z *= 0.25; + } + + //Get water direction + Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ); + + m_WaterSpeed *= 0.9f; //Reduce speed each tick + + switch(WaterDir) + { + case X_PLUS: + m_WaterSpeed.x = 0.2f; + m_bOnGround = false; + break; + case X_MINUS: + m_WaterSpeed.x = -0.2f; + m_bOnGround = false; + break; + case Z_PLUS: + m_WaterSpeed.z = 0.2f; + m_bOnGround = false; + break; + case Z_MINUS: + m_WaterSpeed.z = -0.2f; + m_bOnGround = false; + break; + + default: + break; + } + + if (fabs(m_WaterSpeed.x) < 0.05) + { + m_WaterSpeed.x = 0; + } + + if (fabs(m_WaterSpeed.z) < 0.05) + { + m_WaterSpeed.z = 0; + } + + NextSpeed += m_WaterSpeed; + + if( NextSpeed.SqrLength() > 0.f ) + { + cTracer Tracer( GetWorld() ); + int Ret = Tracer.Trace( NextPos, NextSpeed, 2 ); + if( Ret ) // Oh noez! we hit something + { + // Set to hit position + if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() ) + { + if( Ret == 1 ) + { + if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f; + if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f; + if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f; + + if( Tracer.HitNormal.y > 0 ) // means on ground + { + m_bOnGround = true; + } + } + NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z); + NextPos.x += Tracer.HitNormal.x * 0.3f; + NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot + NextPos.z += Tracer.HitNormal.z * 0.3f; + } + else + { + NextPos += (NextSpeed * a_Dt); + } + } + else + { + // We didn't hit anything, so move =] + NextPos += (NextSpeed * a_Dt); + } + } + BlockX = (int) floor(NextPos.x); + BlockZ = (int) floor(NextPos.z); + NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ); + // See if we can commit our changes. If not, we will discard them. + if (NextChunk != NULL) + { + if (NextPos.x != GetPosX()) SetPosX(NextPos.x); + if (NextPos.y != GetPosY()) SetPosY(NextPos.y); + if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z); + if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x); + if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y); + if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z); + } + } +} + + + + + +void cEntity::TickBurning(cChunk & a_Chunk) +{ + // Remember the current burning state: + bool HasBeenBurning = (m_TicksLeftBurning > 0); + + // Do the burning damage: + if (m_TicksLeftBurning > 0) + { + m_TicksSinceLastBurnDamage++; + if (m_TicksSinceLastBurnDamage >= BURN_TICKS_PER_DAMAGE) + { + TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0); + m_TicksSinceLastBurnDamage = 0; + } + m_TicksLeftBurning--; + } + + // Update the burning times, based on surroundings: + int MinRelX = (int)floor(GetPosX() - m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; + int MaxRelX = (int)floor(GetPosX() + m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; + int MinRelZ = (int)floor(GetPosZ() - m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; + int MaxRelZ = (int)floor(GetPosZ() + m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; + int MinY = std::max(0, std::min(cChunkDef::Height - 1, (int)floor(GetPosY()))); + int MaxY = std::max(0, std::min(cChunkDef::Height - 1, (int)ceil (GetPosY() + m_Height))); + bool HasWater = false; + bool HasLava = false; + bool HasFire = false; + + for (int x = MinRelX; x <= MaxRelX; x++) + { + for (int z = MinRelZ; z <= MaxRelZ; z++) + { + int RelX = x; + int RelZ = z; + cChunk * CurChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelX, RelZ); + if (CurChunk == NULL) + { + continue; + } + for (int y = MinY; y <= MaxY; y++) + { + switch (CurChunk->GetBlock(RelX, y, RelZ)) + { + case E_BLOCK_FIRE: + { + HasFire = true; + break; + } + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + HasLava = true; + break; + } + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + HasWater = true; + break; + } + } // switch (BlockType) + } // for y + } // for z + } // for x + + if (HasWater) + { + // Extinguish the fire + m_TicksLeftBurning = 0; + } + + if (HasLava) + { + // Burn: + m_TicksLeftBurning = BURN_TICKS; + + // Periodically damage: + m_TicksSinceLastLavaDamage++; + if (m_TicksSinceLastLavaDamage >= LAVA_TICKS_PER_DAMAGE) + { + TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0); + m_TicksSinceLastLavaDamage = 0; + } + } + else + { + m_TicksSinceLastLavaDamage = 0; + } + + if (HasFire) + { + // Burn: + m_TicksLeftBurning = BURN_TICKS; + + // Periodically damage: + m_TicksSinceLastFireDamage++; + if (m_TicksSinceLastFireDamage >= FIRE_TICKS_PER_DAMAGE) + { + TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0); + m_TicksSinceLastFireDamage = 0; + } + } + else + { + m_TicksSinceLastFireDamage = 0; + } + + // If just started / finished burning, notify descendants: + if ((m_TicksLeftBurning > 0) && !HasBeenBurning) + { + OnStartedBurning(); + } + else if ((m_TicksLeftBurning <= 0) && HasBeenBurning) + { + OnFinishedBurning(); + } +} + + + + + +void cEntity::TickInVoid(cChunk & a_Chunk) +{ + if (m_TicksSinceLastVoidDamage == 20) + { + TakeDamage(dtInVoid, NULL, 2, 0); + m_TicksSinceLastVoidDamage = 0; + } + else + { + m_TicksSinceLastVoidDamage++; + } +} + + + + + +/// Called when the entity starts burning +void cEntity::OnStartedBurning(void) +{ + // Broadcast the change: + m_World->BroadcastEntityMetadata(*this); +} + + + + + +/// Called when the entity finishes burning +void cEntity::OnFinishedBurning(void) +{ + // Broadcast the change: + m_World->BroadcastEntityMetadata(*this); +} + + + + + +/// Sets the maximum value for the health +void cEntity::SetMaxHealth(int a_MaxHealth) +{ + m_MaxHealth = a_MaxHealth; + + // Reset health, if too high: + if (m_Health > a_MaxHealth) + { + m_Health = a_MaxHealth; + } +} + + + + + +/// Puts the entity on fire for the specified amount of ticks +void cEntity::StartBurning(int a_TicksLeftBurning) +{ + if (m_TicksLeftBurning > 0) + { + // Already burning, top up the ticks left burning and bail out: + m_TicksLeftBurning = std::max(m_TicksLeftBurning, a_TicksLeftBurning); + return; + } + + m_TicksLeftBurning = a_TicksLeftBurning; + OnStartedBurning(); +} + + + + + +/// Stops the entity from burning, resets all burning timers +void cEntity::StopBurning(void) +{ + bool HasBeenBurning = (m_TicksLeftBurning > 0); + m_TicksLeftBurning = 0; + m_TicksSinceLastBurnDamage = 0; + m_TicksSinceLastFireDamage = 0; + m_TicksSinceLastLavaDamage = 0; + + // Notify if the entity has stopped burning + if (HasBeenBurning) + { + OnFinishedBurning(); + } +} + + + + + +void cEntity::TeleportToEntity(cEntity & a_Entity) +{ + TeleportToCoords(a_Entity.GetPosX(), a_Entity.GetPosY(), a_Entity.GetPosZ()); +} + + + + + +void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) +{ + SetPosition(a_PosX, a_PosY, a_PosZ); + m_World->BroadcastTeleportEntity(*this); +} + + + + + +void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) +{ + //We need to keep updating the clients when there is movement or if there was a change in speed and after 2 ticks + if( (m_Speed.SqrLength() > 0.0004f || m_bDirtySpeed) && (m_World->GetWorldAge() - m_TimeLastSpeedPacket >= 2)) + { + m_World->BroadcastEntityVelocity(*this,a_Exclude); + m_bDirtySpeed = false; + m_TimeLastSpeedPacket = m_World->GetWorldAge(); + } + + //Have to process position related packets this every two ticks + if (m_World->GetWorldAge() % 2 == 0) + { + int DiffX = (int) (floor(GetPosX() * 32.0) - floor(m_LastPosX * 32.0)); + int DiffY = (int) (floor(GetPosY() * 32.0) - floor(m_LastPosY * 32.0)); + int DiffZ = (int) (floor(GetPosZ() * 32.0) - floor(m_LastPosZ * 32.0)); + Int64 DiffTeleportPacket = m_World->GetWorldAge() - m_TimeLastTeleportPacket; + // 4 blocks is max Relative So if the Diff is greater than 127 or. Send an absolute position every 20 seconds + if (DiffTeleportPacket >= 400 || + ((DiffX > 127) || (DiffX < -128) || + (DiffY > 127) || (DiffY < -128) || + (DiffZ > 127) || (DiffZ < -128))) + { + // + m_World->BroadcastTeleportEntity(*this,a_Exclude); + m_TimeLastTeleportPacket = m_World->GetWorldAge(); + m_TimeLastMoveReltPacket = m_TimeLastTeleportPacket; //Must synchronize. + m_LastPosX = GetPosX(); + m_LastPosY = GetPosY(); + m_LastPosZ = GetPosZ(); + m_bDirtyPosition = false; + m_bDirtyOrientation = false; + } + else + { + Int64 DiffMoveRelPacket = m_World->GetWorldAge() - m_TimeLastMoveReltPacket; + //if the change is big enough. + if ((abs(DiffX) >= 4 || abs(DiffY) >= 4 || abs(DiffZ) >= 4 || DiffMoveRelPacket >= 60) && m_bDirtyPosition) + { + if (m_bDirtyOrientation) + { + m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); + m_bDirtyOrientation = false; + } + else + { + m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); + } + m_LastPosX = GetPosX(); + m_LastPosY = GetPosY(); + m_LastPosZ = GetPosZ(); + m_bDirtyPosition = false; + m_TimeLastMoveReltPacket = m_World->GetWorldAge(); + } + else + { + if (m_bDirtyOrientation) + { + m_World->BroadcastEntityLook(*this,a_Exclude); + m_bDirtyOrientation = false; + } + } + } + if (m_bDirtyHead) + { + m_World->BroadcastEntityHeadLook(*this,a_Exclude); + m_bDirtyHead = false; + } + } +} + + + + + +void cEntity::AttachTo(cEntity * a_AttachTo) +{ + if (m_AttachedTo == a_AttachTo) + { + // Already attached to that entity, nothing to do here + return; + } + + // Detach from any previous entity: + Detach(); + + // Attach to the new entity: + m_AttachedTo = a_AttachTo; + a_AttachTo->m_Attachee = this; + m_World->BroadcastAttachEntity(*this, a_AttachTo); +} + + + + + +void cEntity::Detach(void) +{ + if (m_AttachedTo == NULL) + { + // Attached to no entity, our work is done + return; + } + m_AttachedTo->m_Attachee = NULL; + m_AttachedTo = NULL; + m_World->BroadcastAttachEntity(*this, NULL); +} + + + + + +bool cEntity::IsA(const char * a_ClassName) const +{ + return (strcmp(a_ClassName, "cEntity") == 0); +} + + + + + +void cEntity::SetRot(const Vector3f & a_Rot) +{ + m_Rot = a_Rot; + m_bDirtyOrientation = true; +} + + + + + +void cEntity::SetHeadYaw(double a_HeadYaw) +{ + m_HeadYaw = a_HeadYaw; + m_bDirtyHead = true; + WrapHeadYaw(); +} + + + + + +void cEntity::SetHeight(double a_Height) +{ + m_Height = a_Height; +} + + + + + +void cEntity::SetMass(double a_Mass) +{ + if (a_Mass > 0) + { + m_Mass = a_Mass; + } + else + { + // Make sure that mass is not zero. 1g is the default because we + // have to choose a number. It's perfectly legal to have a mass + // less than 1g as long as is NOT equal or less than zero. + m_Mass = 0.001; + } +} + + + + + +void cEntity::SetYaw(double a_Yaw) +{ + m_Rot.x = a_Yaw; + m_bDirtyOrientation = true; + WrapRotation(); +} + + + + + +void cEntity::SetPitch(double a_Pitch) +{ + m_Rot.y = a_Pitch; + m_bDirtyOrientation = true; + WrapRotation(); +} + + + + + +void cEntity::SetRoll(double a_Roll) +{ + m_Rot.z = a_Roll; + m_bDirtyOrientation = true; +} + + + + + +void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) +{ + m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ); + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + +void cEntity::SetSpeedX(double a_SpeedX) +{ + m_Speed.x = a_SpeedX; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + +void cEntity::SetSpeedY(double a_SpeedY) +{ + m_Speed.y = a_SpeedY; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + +void cEntity::SetSpeedZ(double a_SpeedZ) +{ + m_Speed.z = a_SpeedZ; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::SetWidth(double a_Width) +{ + m_Width = a_Width; +} + + + + + +void cEntity::AddPosX(double a_AddPosX) +{ + m_Pos.x += a_AddPosX; + m_bDirtyPosition = true; +} + + + + +void cEntity::AddPosY(double a_AddPosY) +{ + m_Pos.y += a_AddPosY; + m_bDirtyPosition = true; +} + + + + +void cEntity::AddPosZ(double a_AddPosZ) +{ + m_Pos.z += a_AddPosZ; + m_bDirtyPosition = true; +} + + + + +void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ) +{ + m_Pos.x += a_AddPosX; + m_Pos.y += a_AddPosY; + m_Pos.z += a_AddPosZ; + m_bDirtyPosition = true; +} + + + + +void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ) +{ + m_Speed.x += a_AddSpeedX; + m_Speed.y += a_AddSpeedY; + m_Speed.z += a_AddSpeedZ; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::AddSpeedX(double a_AddSpeedX) +{ + m_Speed.x += a_AddSpeedX; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::AddSpeedY(double a_AddSpeedY) +{ + m_Speed.y += a_AddSpeedY; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::AddSpeedZ(double a_AddSpeedZ) +{ + m_Speed.z += a_AddSpeedZ; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::SteerVehicle(float a_Forward, float a_Sideways) +{ + if (m_AttachedTo == NULL) + { + return; + } + if ((a_Forward != 0) || (a_Sideways != 0)) + { + Vector3d LookVector = GetLookVector(); + double AddSpeedX = LookVector.x * a_Forward + LookVector.z * a_Sideways; + double AddSpeedZ = LookVector.z * a_Forward - LookVector.x * a_Sideways; + m_AttachedTo->AddSpeed(AddSpeedX, 0, AddSpeedZ); + } +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Get look vector (this is NOT a rotation!) +Vector3d cEntity::GetLookVector(void) const +{ + Matrix4d m; + m.Init(Vector3f(), 0, m_Rot.x, -m_Rot.y); + Vector3d Look = m.Transform(Vector3d(0, 0, 1)); + return Look; +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Set position +void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ) +{ + m_Pos.Set(a_PosX, a_PosY, a_PosZ); + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosX(double a_PosX) +{ + m_Pos.x = a_PosX; + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosY(double a_PosY) +{ + m_Pos.y = a_PosY; + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosZ(double a_PosZ) +{ + m_Pos.z = a_PosZ; + m_bDirtyPosition = true; +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Reference stuffs +void cEntity::AddReference(cEntity * & a_EntityPtr) +{ + m_References->AddReference(a_EntityPtr); + a_EntityPtr->ReferencedBy(a_EntityPtr); +} + + + + + +void cEntity::ReferencedBy(cEntity * & a_EntityPtr) +{ + m_Referencers->AddReference(a_EntityPtr); +} + + + + + +void cEntity::Dereference(cEntity * & a_EntityPtr) +{ + m_Referencers->Dereference(a_EntityPtr); +} + + + + diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h new file mode 100644 index 000000000..dafda7826 --- /dev/null +++ b/src/Entities/Entity.h @@ -0,0 +1,445 @@ + +#pragma once + +#include "../Item.h" +#include "../Vector3d.h" +#include "../Vector3f.h" + + + + + +// Place this macro in the public section of each cEntity descendant class and you're done :) +#define CLASS_PROTODEF(classname) \ + virtual bool IsA(const char * a_ClassName) const override\ + { \ + return ((strcmp(a_ClassName, #classname) == 0) || super::IsA(a_ClassName)); \ + } \ + virtual const char * GetClass(void) const override \ + { \ + return #classname; \ + } \ + static const char * GetClassStatic(void) \ + { \ + return #classname; \ + } \ + virtual const char * GetParentClass(void) const override \ + { \ + return super::GetClass(); \ + } + + + + + +class cWorld; +class cReferenceManager; +class cClientHandle; +class cPlayer; +class cChunk; + + + + + +// tolua_begin +struct TakeDamageInfo +{ + eDamageType DamageType; // Where does the damage come from? Being hit / on fire / contact with cactus / ... + cEntity * Attacker; // The attacking entity; valid only for dtAttack + int RawDamage; // What damage would the receiver get without any armor. Usually: attacker mob type + weapons + int FinalDamage; // What actual damage will be received. Usually: m_RawDamage minus armor + Vector3d Knockback; // The amount and direction of knockback received from the damage + // TODO: Effects - list of effects that the hit is causing. Unknown representation yet +} ; +// tolua_end + + + + + +// tolua_begin +class cEntity +{ +public: + + enum eEntityType + { + etEntity, // For all other types + etPlayer, + etPickup, + etMonster, + etFallingBlock, + etMinecart, + etBoat, + etTNT, + etProjectile, + + // Common variations + etMob = etMonster, // DEPRECATED, use etMonster instead! + } ; + + // tolua_end + + enum + { + ENTITY_STATUS_HURT = 2, + ENTITY_STATUS_DEAD = 3, + ENTITY_STATUS_WOLF_TAMING = 6, + ENTITY_STATUS_WOLF_TAMED = 7, + ENTITY_STATUS_WOLF_SHAKING = 8, + ENTITY_STATUS_EATING_ACCEPTED = 9, + ENTITY_STATUS_SHEEP_EATING = 10, + ENTITY_STATUS_GOLEM_ROSING = 11, + ENTITY_STATUS_VILLAGER_HEARTS = 12, + ENTITY_STATUS_VILLAGER_ANGRY = 13, + ENTITY_STATUS_VILLAGER_HAPPY = 14, + ENTITY_STATUS_WITCH_MAGICKING = 15, + // It seems 16 (zombie conversion) is now done with metadata + ENTITY_STATUS_FIREWORK_EXPLODE= 17, + } ; + + enum + { + FIRE_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in fire + FIRE_DAMAGE = 1, ///< How much damage to deal when standing in fire + LAVA_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in lava + LAVA_DAMAGE = 5, ///< How much damage to deal when standing in lava + BURN_TICKS_PER_DAMAGE = 20, ///< How many ticks to wait between damaging an entity when it is burning + BURN_DAMAGE = 1, ///< How much damage to deal when the entity is burning + BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire + } ; + + cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); + virtual ~cEntity(); + + /// Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed) + virtual bool Initialize(cWorld * a_World); + + // tolua_begin + + eEntityType GetEntityType(void) const { return m_EntityType; } + + bool IsPlayer (void) const { return (m_EntityType == etPlayer); } + bool IsPickup (void) const { return (m_EntityType == etPickup); } + bool IsMob (void) const { return (m_EntityType == etMonster); } + bool IsFallingBlock(void) const { return (m_EntityType == etFallingBlock); } + bool IsMinecart (void) const { return (m_EntityType == etMinecart); } + bool IsBoat (void) const { return (m_EntityType == etBoat); } + bool IsTNT (void) const { return (m_EntityType == etTNT); } + bool IsProjectile (void) const { return (m_EntityType == etProjectile); } + + /// Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns true) + virtual bool IsA(const char * a_ClassName) const; + + /// Returns the topmost class name for the object + virtual const char * GetClass(void) const; + + // Returns the class name of this class + static const char * GetClassStatic(void); + + /// Returns the topmost class's parent class name for the object. cEntity returns an empty string (no parent). + virtual const char * GetParentClass(void) const; + + cWorld * GetWorld(void) const { return m_World; } + + double GetHeadYaw (void) const { return m_HeadYaw; } + double GetHeight (void) const { return m_Height; } + double GetMass (void) const { return m_Mass; } + const Vector3d & GetPosition (void) const { return m_Pos; } + double GetPosX (void) const { return m_Pos.x; } + double GetPosY (void) const { return m_Pos.y; } + double GetPosZ (void) const { return m_Pos.z; } + const Vector3d & GetRot (void) const { return m_Rot; } + double GetRotation (void) const { return m_Rot.x; } // OBSOLETE, use GetYaw() instead + double GetYaw (void) const { return m_Rot.x; } + double GetPitch (void) const { return m_Rot.y; } + double GetRoll (void) const { return m_Rot.z; } + Vector3d GetLookVector(void) const; + const Vector3d & GetSpeed (void) const { return m_Speed; } + double GetSpeedX (void) const { return m_Speed.x; } + double GetSpeedY (void) const { return m_Speed.y; } + double GetSpeedZ (void) const { return m_Speed.z; } + double GetWidth (void) const { return m_Width; } + + int GetChunkX(void) const {return (int)floor(m_Pos.x / cChunkDef::Width); } + int GetChunkZ(void) const {return (int)floor(m_Pos.z / cChunkDef::Width); } + + void SetHeadYaw (double a_HeadYaw); + void SetHeight (double a_Height); + void SetMass (double a_Mass); + void SetPosX (double a_PosX); + void SetPosY (double a_PosY); + void SetPosZ (double a_PosZ); + void SetPosition(double a_PosX, double a_PosY, double a_PosZ); + void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); } + void SetRot (const Vector3f & a_Rot); + void SetRotation(double a_Rotation) { SetYaw(a_Rotation); } // OBSOLETE, use SetYaw() instead + void SetYaw (double a_Yaw); + void SetPitch (double a_Pitch); + void SetRoll (double a_Roll); + void SetSpeed (double a_SpeedX, double a_SpeedY, double a_SpeedZ); + void SetSpeed (const Vector3d & a_Speed) { SetSpeed(a_Speed.x, a_Speed.y, a_Speed.z); } + void SetSpeedX (double a_SpeedX); + void SetSpeedY (double a_SpeedY); + void SetSpeedZ (double a_SpeedZ); + void SetWidth (double a_Width); + + void AddPosX (double a_AddPosX); + void AddPosY (double a_AddPosY); + void AddPosZ (double a_AddPosZ); + void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ); + void AddPosition(const Vector3d & a_AddPos) { AddPosition(a_AddPos.x,a_AddPos.y,a_AddPos.z);} + void AddSpeed (double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ); + void AddSpeed (const Vector3d & a_AddSpeed) { AddSpeed(a_AddSpeed.x,a_AddSpeed.y,a_AddSpeed.z);} + void AddSpeedX (double a_AddSpeedX); + void AddSpeedY (double a_AddSpeedY); + void AddSpeedZ (double a_AddSpeedZ); + + void SteerVehicle(float a_Forward, float a_Sideways); + + inline int GetUniqueID(void) const { return m_UniqueID; } + inline bool IsDestroyed(void) const { return !m_IsInitialized; } + + /// Schedules the entity for destroying; if a_ShouldBroadcast is set to true, broadcasts the DestroyEntity packet + void Destroy(bool a_ShouldBroadcast = true); + + /// Makes this pawn take damage from an attack by a_Attacker. Damage values are calculated automatically and DoTakeDamage() called + void TakeDamage(cEntity & a_Attacker); + + /// Makes this entity take the specified damage. The final damage is calculated using current armor, then DoTakeDamage() called + void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount); + + /// Makes this entity take the specified damage. The values are packed into a TDI, knockback calculated, then sent through DoTakeDamage() + void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount); + + float GetGravity(void) const { return m_Gravity; } + + void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; } + + /// Sets the rotation to match the speed vector (entity goes "face-forward") + void SetRotationFromSpeed(void); + + /// Sets the pitch to match the speed vector (entity gies "face-forward") + void SetPitchFromSpeed(void); + + // tolua_end + + /// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied + virtual void DoTakeDamage(TakeDamageInfo & a_TDI); + + // tolua_begin + + /// Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items + virtual int GetRawDamageAgainst(const cEntity & a_Receiver); + + /// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover + virtual int GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_RawDamage); + + /// Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit + virtual double GetKnockbackAmountAgainst(const cEntity & a_Receiver); + + /// Returns the curently equipped weapon; empty item if none + virtual cItem GetEquippedWeapon(void) const { return cItem(); } + + /// Returns the currently equipped helmet; empty item if none + virtual cItem GetEquippedHelmet(void) const { return cItem(); } + + /// Returns the currently equipped chestplate; empty item if none + virtual cItem GetEquippedChestplate(void) const { return cItem(); } + + /// Returns the currently equipped leggings; empty item if none + virtual cItem GetEquippedLeggings(void) const { return cItem(); } + + /// Returns the currently equipped boots; empty item if none + virtual cItem GetEquippedBoots(void) const { return cItem(); } + + /// Called when the health drops below zero. a_Killer may be NULL (environmental damage) + virtual void KilledBy(cEntity * a_Killer); + + /// Heals the specified amount of HPs + void Heal(int a_HitPoints); + + /// Returns the health of this entity + int GetHealth(void) const { return m_Health; } + + /// Sets the health of this entity; doesn't broadcast any hurt animation + void SetHealth(int a_Health); + + // tolua_end + + virtual void Tick(float a_Dt, cChunk & a_Chunk); + + /// Handles the physics of the entity - updates position based on speed, updates speed based on environment + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk); + + /// Updates the state related to this entity being on fire + virtual void TickBurning(cChunk & a_Chunk); + + /// Handles when the entity is in the void + virtual void TickInVoid(cChunk & a_Chunk); + + /// Called when the entity starts burning + virtual void OnStartedBurning(void); + + /// Called when the entity finishes burning + virtual void OnFinishedBurning(void); + + // tolua_begin + + /// Sets the maximum value for the health + void SetMaxHealth(int a_MaxHealth); + + int GetMaxHealth(void) const { return m_MaxHealth; } + + /// Puts the entity on fire for the specified amount of ticks + void StartBurning(int a_TicksLeftBurning); + + /// Stops the entity from burning, resets all burning timers + void StopBurning(void); + + // tolua_end + + /** Descendants override this function to send a command to the specified client to spawn the entity on the client. + To spawn on all eligible clients, use cChunkMap::BroadcastSpawnEntity() + */ + virtual void SpawnOn(cClientHandle & a_Client) = 0; + + // tolua_begin + + /// Teleports to the entity specified + virtual void TeleportToEntity(cEntity & a_Entity); + + /// Teleports to the coordinates specified + virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ); + + // tolua_end + + /// Updates clients of changes in the entity. + virtual void BroadcastMovementUpdate(const cClientHandle * a_Exclude = NULL); + + /// Attaches to the specified entity; detaches from any previous one first + void AttachTo(cEntity * a_AttachTo); + + /// Detaches from the currently attached entity, if any + void Detach(void); + + /// Makes sure head yaw is not over the specified range. + void WrapHeadYaw(); + + /// Makes sure rotation is not over the specified range. + void WrapRotation(); + + /// Makes speed is not over 20. Max speed is 20 blocks / second + void WrapSpeed(); + + // tolua_begin + + // COMMON metadata flags; descendants may override the defaults: + virtual bool IsOnFire (void) const {return (m_TicksLeftBurning > 0); } + virtual bool IsCrouched (void) const {return false; } + virtual bool IsRiding (void) const {return false; } + virtual bool IsSprinting(void) const {return false; } + virtual bool IsRclking (void) const {return false; } + virtual bool IsInvisible(void) const {return false; } + + // tolua_end + + /// Called when the specified player right-clicks this entity + virtual void OnRightClicked(cPlayer & a_Player) {}; + + /// Returns the list of drops for this pawn when it is killed. May check a_Killer for special handling (sword of looting etc.). Called from KilledBy(). + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) {} + +protected: + static cCriticalSection m_CSCount; + static int m_EntityCount; + + int m_UniqueID; + + int m_Health; + int m_MaxHealth; + + /// The entity to which this entity is attached (vehicle), NULL if none + cEntity * m_AttachedTo; + + /// The entity which is attached to this entity (rider), NULL if none + cEntity * m_Attachee; + + cReferenceManager* m_Referencers; + cReferenceManager* m_References; + + // Flags that signal that we haven't updated the clients with the latest. + bool m_bDirtyHead; + bool m_bDirtyOrientation; + bool m_bDirtyPosition; + bool m_bDirtySpeed; + + bool m_bOnGround; + float m_Gravity; + + // Last Position. + double m_LastPosX, m_LastPosY, m_LastPosZ; + + // This variables keep track of the last time a packet was sent + Int64 m_TimeLastTeleportPacket,m_TimeLastMoveReltPacket,m_TimeLastSpeedPacket; // In ticks + + bool m_IsInitialized; // Is set to true when it's initialized, until it's destroyed (Initialize() till Destroy() ) + + eEntityType m_EntityType; + + cWorld * m_World; + + /// Time, in ticks, since the last damage dealt by being on fire. Valid only if on fire (IsOnFire()) + int m_TicksSinceLastBurnDamage; + + /// Time, in ticks, since the last damage dealt by standing in lava. Reset to zero when moving out of lava. + int m_TicksSinceLastLavaDamage; + + /// Time, in ticks, since the last damage dealt by standing in fire. Reset to zero when moving out of fire. + int m_TicksSinceLastFireDamage; + + /// Time, in ticks, until the entity extinguishes its fire + int m_TicksLeftBurning; + + /// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void. + int m_TicksSinceLastVoidDamage; + + virtual void Destroyed(void) {} // Called after the entity has been destroyed + + void SetWorld(cWorld * a_World) { m_World = a_World; } + + friend class cReferenceManager; + void AddReference( cEntity*& a_EntityPtr ); + void ReferencedBy( cEntity*& a_EntityPtr ); + void Dereference( cEntity*& a_EntityPtr ); + +private: + // Measured in degrees (MAX 360°) + double m_HeadYaw; + // Measured in meter/second (m/s) + Vector3d m_Speed; + // Measured in degrees (MAX 360°) + Vector3d m_Rot; + + /// Position of the entity's XZ center and Y bottom + Vector3d m_Pos; + + // Measured in meter / second + Vector3d m_WaterSpeed; + + // Measured in Kilograms (Kg) + double m_Mass; + + /// Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter. + double m_Width; + + /// Height of the entity (Y axis) + double m_Height; +} ; // tolua_export + +typedef std::list cEntityList; + + + + diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp new file mode 100644 index 000000000..9fcd9ac80 --- /dev/null +++ b/src/Entities/FallingBlock.cpp @@ -0,0 +1,93 @@ +#include "Globals.h" + +#include "FallingBlock.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "../Simulator/SandSimulator.h" +#include "../Chunk.h" + + + + + +cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : + super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f, 0.98, 0.98), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta), + m_OriginalPosition(a_BlockPosition) +{ +} + + + + + +void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle) +{ + a_ClientHandle.SendSpawnFallingBlock(*this); +} + + + + + +void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) +{ + float MilliDt = a_Dt * 0.001f; + AddSpeedY(MilliDt * -9.8f); + AddPosY(GetSpeedY() * MilliDt); + + // GetWorld()->BroadcastTeleportEntity(*this); // Test position + + int BlockX = m_OriginalPosition.x; + int BlockY = (int)(GetPosY() - 0.5); + int BlockZ = m_OriginalPosition.z; + + if (BlockY < 0) + { + // Fallen out of this world, just continue falling until out of sight, then destroy: + if (BlockY < 100) + { + Destroy(true); + } + return; + } + + if (BlockY >= cChunkDef::Height) + { + // Above the world, just wait for it to fall back down + return; + } + + int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width); + BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx); + NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx); + if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta)) + { + // Fallen onto a block that breaks this into pickups (e. g. half-slab) + // Must finish the fall with coords one below the block: + cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta); + Destroy(true); + return; + } + else if (!cSandSimulator::CanContinueFallThrough(BlockBelow)) + { + // Fallen onto a solid block + /* + LOGD( + "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.", + BlockX, BlockY, BlockZ, + BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width, + ItemTypeToString(BlockBelow).c_str() + ); + */ + + cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta); + Destroy(true); + return; + } +} + + + + diff --git a/src/Entities/FallingBlock.h b/src/Entities/FallingBlock.h new file mode 100644 index 000000000..5ba9909bb --- /dev/null +++ b/src/Entities/FallingBlock.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "Entity.h" + + + + +class cPlayer; +class cItem; + + + + + + +class cFallingBlock : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cFallingBlock); + + /// Creates a new falling block. a_BlockPosition is expected in world coords + cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + BLOCKTYPE GetBlockType(void) const { return m_BlockType; } + NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; } + + // cEntity overrides: + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +private: + BLOCKTYPE m_BlockType; + NIBBLETYPE m_BlockMeta; + Vector3i m_OriginalPosition; // Position where the falling block has started, in world coords +} ; + + + + diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp new file mode 100644 index 000000000..f75e23d8b --- /dev/null +++ b/src/Entities/Minecart.cpp @@ -0,0 +1,541 @@ + +// Minecart.cpp + +// Implements the cMinecart class representing a minecart in the world +// Indiana Jones! + +#include "Globals.h" +#include "Minecart.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "../Chunk.h" +#include "Player.h" + + + + + +cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : + super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7), + m_Payload(a_Payload), + m_LastDamage(0) +{ + SetMass(20.f); + SetMaxHealth(6); + SetHealth(6); +} + + + + +void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) +{ + char SubType = 0; + switch (m_Payload) + { + case mpNone: SubType = 0; break; + case mpChest: SubType = 1; break; + case mpFurnace: SubType = 2; break; + case mpTNT: SubType = 3; break; + case mpHopper: SubType = 5; break; + default: + { + ASSERT(!"Unknown payload, cannot spawn on client"); + return; + } + } + a_ClientHandle.SendSpawnVehicle(*this, 10, SubType); // 10 = Minecarts, SubType = What type of Minecart +} + + + + + +void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + int PosY = (int)floor(GetPosY()); + if ((PosY <= 0) || (PosY >= cChunkDef::Height)) + { + // Outside the world, just process normal falling physics + super::HandlePhysics(a_Dt, a_Chunk); + BroadcastMovementUpdate(); + return; + } + + int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); + if (Chunk == NULL) + { + // Inside an unloaded chunk, bail out all processing + return; + } + BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ); + BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ); + + if (IsBlockRail(BelowType)) + { + HandleRailPhysics(a_Dt, *Chunk); + } + else + { + if (IsBlockRail(InsideType)) + { + SetPosY(PosY + 1); + HandleRailPhysics(a_Dt, *Chunk); + } + else + { + super::HandlePhysics(a_Dt, *Chunk); + BroadcastMovementUpdate(); + } + } +} + + + + + +static const double MAX_SPEED = 8; +static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED); + +void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) +{ + + super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling + + /* + NOTE: Please bear in mind that taking away from negatives make them even more negative, + adding to negatives make them positive, etc. + */ + + // Get block meta below the cart + int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; + NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ); + double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed + + switch (BelowMeta) + { + case E_META_RAIL_ZM_ZP: // NORTHSOUTH + { + SetRotation(270); + SpeedY = 0; // Don't move vertically as on ground + SpeedX = 0; // Correct diagonal movement from curved rails + + if (SpeedZ != 0) // Don't do anything if cart is stationary + { + if (SpeedZ > 0) + { + // Going SOUTH, slow down + SpeedZ = SpeedZ - 0.1; + } + else + { + // Going NORTH, slow down + SpeedZ = SpeedZ + 0.1; + } + } + break; + } + + case E_META_RAIL_XM_XP: // EASTWEST + { + SetRotation(180); + SpeedY = 0; + SpeedZ = 0; + + if (SpeedX != 0) + { + if (SpeedX > 0) + { + SpeedX = SpeedX - 0.1; + } + else + { + SpeedX = SpeedX + 0.1; + } + } + break; + } + + case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH + { + SetRotation(270); + SetPosY(floor(GetPosY()) + 0.2); // It seems it doesn't work without levitation :/ + SpeedX = 0; + + if (SpeedZ >= 0) + { + // SpeedZ POSITIVE, going SOUTH + if (SpeedZ <= MAX_SPEED) // Speed limit + { + SpeedZ = SpeedZ + 0.5; // Speed up + SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative) + } + else + { + SpeedZ = MAX_SPEED; // Enforce speed limit + SpeedY = (0 - SpeedZ); + } + } + else + { + // SpeedZ NEGATIVE, going NORTH + SpeedZ = SpeedZ + 0.4; // Slow down + SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number) + } + break; + } + + case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH + { + SetRotation(270); + SetPosY(floor(GetPosY()) + 0.2); + SpeedX = 0; + + if (SpeedZ > 0) + { + // SpeedZ POSITIVE, going SOUTH + SpeedZ = SpeedZ - 0.4; // Slow down + SpeedY = SpeedZ; // Upward movement positive + } + else + { + if (SpeedZ >= MAX_SPEED_NEGATIVE) // Speed limit + { + // SpeedZ NEGATIVE, going NORTH + SpeedZ = SpeedZ - 0.5; // Speed up + SpeedY = SpeedZ; // Downward movement negative + } + else + { + SpeedZ = MAX_SPEED_NEGATIVE; // Enforce speed limit + SpeedY = SpeedZ; + } + } + break; + } + + case E_META_RAIL_ASCEND_XM: // ASCEND EAST + { + SetRotation(180); + SetPosY(floor(GetPosY()) + 0.2); + SpeedZ = 0; + + if (SpeedX >= 0) + { + if (SpeedX <= MAX_SPEED) + { + SpeedX = SpeedX + 0.5; + SpeedY = (0 - SpeedX); + } + else + { + SpeedX = MAX_SPEED; + SpeedY = (0 - SpeedX); + } + } + else + { + SpeedX = SpeedX + 0.4; + SpeedY = (0 - SpeedX); + } + break; + } + + case E_META_RAIL_ASCEND_XP: // ASCEND WEST + { + SetRotation(180); + SetPosY(floor(GetPosY()) + 0.2); + SpeedZ = 0; + + if (SpeedX > 0) + { + SpeedX = SpeedX - 0.4; + SpeedY = SpeedX; + } + else + { + if (SpeedX >= MAX_SPEED_NEGATIVE) + { + SpeedX = SpeedX - 0.5; + SpeedY = SpeedX; + } + else + { + SpeedX = MAX_SPEED_NEGATIVE; + SpeedY = SpeedX; + } + } + break; + } + + case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST + { + SetRotation(315); // Set correct rotation server side + SetPosY(floor(GetPosY()) + 0.2); // Levitate dat cart + + if (SpeedZ > 0) // Cart moving south + { + SpeedX = (0 - SpeedZ); // Diagonally move southwest (which will make cart hit a southwest rail) + } + else if (SpeedX > 0) // Cart moving east + { + SpeedZ = (0 - SpeedX); // Diagonally move northeast + } + break; + } + + case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST + { + SetRotation(225); + SetPosY(floor(GetPosY()) + 0.2); + + if (SpeedZ > 0) + { + SpeedX = SpeedZ; + } + else if (SpeedX < 0) + { + SpeedZ = SpeedX; + } + break; + } + + case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST + { + SetRotation(135); + SetPosY(floor(GetPosY()) + 0.2); + + if (SpeedZ < 0) + { + SpeedX = SpeedZ; + } + else if (SpeedX > 0) + { + SpeedZ = SpeedX; + } + break; + } + + case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST + { + SetRotation(45); + SetPosY(floor(GetPosY()) + 0.2); + + if (SpeedZ < 0) + { + SpeedX = (0 - SpeedZ); + } + else if (SpeedX < 0) + { + SpeedZ = (0 - SpeedX); + } + break; + } + + default: + { + ASSERT(!"Unhandled rail meta!"); // Dun dun DUN! + break; + } + } + + // Set speed to speed variables + SetSpeedX(SpeedX); + SetSpeedY(SpeedY); + SetSpeedZ(SpeedZ); + + + // Broadcast position to client + BroadcastMovementUpdate(); +} + + + + + +void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) +{ + m_LastDamage = TDI.FinalDamage; + super::DoTakeDamage(TDI); + + m_World->BroadcastEntityMetadata(*this); + + if (GetHealth() <= 0) + { + Destroy(true); + + cItems Drops; + switch (m_Payload) + { + case mpNone: + { + Drops.push_back(cItem(E_ITEM_MINECART, 1, 0)); + break; + } + case mpChest: + { + Drops.push_back(cItem(E_ITEM_CHEST_MINECART, 1, 0)); + break; + } + case mpFurnace: + { + Drops.push_back(cItem(E_ITEM_FURNACE_MINECART, 1, 0)); + break; + } + case mpTNT: + { + Drops.push_back(cItem(E_ITEM_MINECART_WITH_TNT, 1, 0)); + break; + } + case mpHopper: + { + Drops.push_back(cItem(E_ITEM_MINECART_WITH_HOPPER, 1, 0)); + break; + } + default: + { + ASSERT(!"Unhandled minecart type when spawning pickup!"); + return; + } + } + + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cEmptyMinecart: + +cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) : + super(mpNone, a_X, a_Y, a_Z) +{ +} + + + + + +void cEmptyMinecart::OnRightClicked(cPlayer & a_Player) +{ + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + // This player is already sitting in, they want out. + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + // Another player is already sitting in here, cannot attach + return; + } + + // Detach whatever is sitting in this minecart now: + m_Attachee->Detach(); + } + + // Attach the player to this minecart + a_Player.AttachTo(this); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithChest: + +cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) : + super(mpChest, a_X, a_Y, a_Z) +{ +} + + + + + +void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item) +{ + ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items))); + + m_Items[a_Idx] = a_Item; +} + + + + + +void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) +{ + // Show the chest UI window to the player + // TODO +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithFurnace: + +cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : + super(mpFurnace, a_X, a_Y, a_Z), + m_IsFueled(false) +{ +} + + + + + +void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) +{ + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_COAL) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + m_IsFueled = true; + m_World->BroadcastEntityMetadata(*this); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithTNT: + +cMinecartWithTNT::cMinecartWithTNT(double a_X, double a_Y, double a_Z) : + super(mpTNT, a_X, a_Y, a_Z) +{ +} + +// TODO: Make it activate when passing over activator rail + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithHopper: + +cMinecartWithHopper::cMinecartWithHopper(double a_X, double a_Y, double a_Z) : + super(mpHopper, a_X, a_Y, a_Z) +{ +} + +// TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks +// AND AVARYTHING!! \ No newline at end of file diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h new file mode 100644 index 000000000..b1b48be4e --- /dev/null +++ b/src/Entities/Minecart.h @@ -0,0 +1,169 @@ + +// Minecart.h + +// Declares the cMinecart class representing a minecart in the world + + + + + +#pragma once + +#include "Entity.h" + + + + + +inline bool IsBlockRail(BLOCKTYPE a_BlockType) + { + return ( + (a_BlockType == E_BLOCK_RAIL) || + (a_BlockType == E_BLOCK_ACTIVATOR_RAIL) || + (a_BlockType == E_BLOCK_DETECTOR_RAIL) || + (a_BlockType == E_BLOCK_POWERED_RAIL) + ) ; + } + + + + + +class cMinecart : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cMinecart); + + enum ePayload + { + mpNone, // Empty minecart, ridable by player or mobs + mpChest, // Minecart-with-chest, can store a grid of 3*8 items + mpFurnace, // Minecart-with-furnace, can be powered + mpTNT, // Minecart-with-TNT, can be blown up with activator rail + mpHopper, // Minecart-with-hopper, can be hopper + // TODO: Spawner minecarts, (and possibly any block in a minecart with NBT editing) + } ; + + // cEntity overrides: + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + + int LastDamage(void) const { return m_LastDamage; } + void HandleRailPhysics(float a_Dt, cChunk & a_Chunk); + ePayload GetPayload(void) const { return m_Payload; } + +protected: + ePayload m_Payload; + + cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); + + int m_LastDamage; + +} ; + + + + + +class cEmptyMinecart : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cEmptyMinecart); + + cEmptyMinecart(double a_X, double a_Y, double a_Z); + + // cEntity overrides: + virtual void OnRightClicked(cPlayer & a_Player) override; +} ; + + + + + +class cMinecartWithChest : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithChest); + + /// Number of item slots in the chest + static const int NumSlots = 9 * 3; + + cMinecartWithChest(double a_X, double a_Y, double a_Z); + + const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; } + cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; } + + void SetSlot(int a_Idx, const cItem & a_Item); + +protected: + + /// The chest contents: + cItem m_Items[NumSlots]; + + // cEntity overrides: + virtual void OnRightClicked(cPlayer & a_Player) override; +} ; + + + + + +class cMinecartWithFurnace : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithFurnace); + + cMinecartWithFurnace(double a_X, double a_Y, double a_Z); + + // cEntity overrides: + virtual void OnRightClicked(cPlayer & a_Player) override; + bool IsFueled (void) const { return m_IsFueled; } + +private: + + bool m_IsFueled; + +} ; + + + + + +class cMinecartWithTNT : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithTNT); + + cMinecartWithTNT(double a_X, double a_Y, double a_Z); +} ; + + + + + +class cMinecartWithHopper : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithHopper); + + cMinecartWithHopper(double a_X, double a_Y, double a_Z); +} ; \ No newline at end of file diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp new file mode 100644 index 000000000..fffefd538 --- /dev/null +++ b/src/Entities/Pawn.cpp @@ -0,0 +1,19 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Pawn.h" + + + + + +cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) + : cEntity(a_EntityType, 0, 0, 0, a_Width, a_Height) + , m_bBurnable(true) +{ +} + + + + + diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h new file mode 100644 index 000000000..e76337d86 --- /dev/null +++ b/src/Entities/Pawn.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "Entity.h" + + + + + +// tolua_begin +class cPawn : + public cEntity +{ + // tolua_end + typedef cEntity super; + +public: + CLASS_PROTODEF(cPawn); + + cPawn(eEntityType a_EntityType, double a_Width, double a_Height); + +protected: + bool m_bBurnable; +} ; // tolua_export + + + + diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp new file mode 100644 index 000000000..f8aae9703 --- /dev/null +++ b/src/Entities/Pickup.cpp @@ -0,0 +1,166 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#ifndef _WIN32 +#include +#endif + +#include "Pickup.h" +#include "../ClientHandle.h" +#include "../Inventory.h" +#include "../World.h" +#include "../Simulator/FluidSimulator.h" +#include "../Server.h" +#include "Player.h" +#include "../PluginManager.h" +#include "../Item.h" +#include "../Root.h" +#include "../Chunk.h" + +#include "../Vector3d.h" +#include "../Vector3f.h" + + + + + +cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) + : cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2) + , m_Timer( 0.f ) + , m_Item(a_Item) + , m_bCollected( false ) + , m_bIsPlayerCreated( IsPlayerCreated ) +{ + SetGravity(-10.5f); + SetMaxHealth(5); + SetHealth(5); + SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); +} + + + + + +void cPickup::SpawnOn(cClientHandle & a_Client) +{ + a_Client.SendPickupSpawn(*this); +} + + + + + +void cPickup::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + BroadcastMovementUpdate(); //Notify clients of position + + m_Timer += a_Dt; + + if (!m_bCollected) + { + int BlockY = (int) floor(GetPosY()); + if ((BlockY >= 0) && (BlockY < cChunkDef::Height)) // Don't do anything except for falling when outside the world + { + int BlockX = (int) floor(GetPosX()); + int BlockZ = (int) floor(GetPosZ()); + // Position might have changed due to physics. So we have to make sure we have the correct chunk. + cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); + if (CurrentChunk != NULL) // Make sure the chunk is loaded + { + int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width); + int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width); + + // If the pickup is on the bottommost block position, make it think the void is made of air: (#131) + BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; + BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ); + + if ( + IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) || + IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE) + ) + { + m_bCollected = true; + m_Timer = 0; // We have to reset the timer. + m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick. + if (m_Timer > 500.f) + { + Destroy(true); + return; + } + } + } + } + } + else + { + if (m_Timer > 500.f) // 0.5 second + { + Destroy(true); + return; + } + } + + if (m_Timer > 1000 * 60 * 5) // 5 minutes + { + Destroy(true); + return; + } + + if (GetPosY() < -8) // Out of this world and no more visible! + { + Destroy(true); + return; + } +} + + + + + +bool cPickup::CollectedBy(cPlayer * a_Dest) +{ + ASSERT(a_Dest != NULL); + + if (m_bCollected) + { + // LOG("Pickup %d cannot be collected by \"%s\", because it has already been collected.", m_UniqueID, a_Dest->GetName().c_str()); + return false; // It's already collected! + } + + // Two seconds if player created the pickup (vomiting), half a second if anything else + if (m_Timer < (m_bIsPlayerCreated ? 2000.f : 500.f)) + { + // LOG("Pickup %d cannot be collected by \"%s\", because it is not old enough.", m_UniqueID, a_Dest->GetName().c_str()); + return false; // Not old enough + } + + if (cRoot::Get()->GetPluginManager()->CallHookCollectingPickup(a_Dest, *this)) + { + // LOG("Pickup %d cannot be collected by \"%s\", because a plugin has said no.", m_UniqueID, a_Dest->GetName().c_str()); + return false; + } + + int NumAdded = a_Dest->GetInventory().AddItem(m_Item); + if (NumAdded > 0) + { + m_Item.m_ItemCount -= NumAdded; + m_World->BroadcastCollectPickup(*this, *a_Dest); + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + m_World->BroadcastSoundEffect("random.pop",(int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + if (m_Item.m_ItemCount == 0) + { + // All of the pickup has been collected, schedule the pickup for destroying + m_bCollected = true; + } + m_Timer = 0; + return true; + } + + // LOG("Pickup %d cannot be collected by \"%s\", because there's no space in the inventory.", a_Dest->GetName().c_str(), m_UniqueID); + return false; +} + + + + diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h new file mode 100644 index 000000000..d39eda298 --- /dev/null +++ b/src/Entities/Pickup.h @@ -0,0 +1,64 @@ + +#pragma once + +#include "Entity.h" +#include "../Item.h" + + + + + +class cPlayer; + + + + + +// tolua_begin +class cPickup : + public cEntity +{ + // tolua_end + typedef cEntity super; + +public: + CLASS_PROTODEF(cPickup); + + cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export + + cItem & GetItem(void) {return m_Item; } // tolua_export + const cItem & GetItem(void) const {return m_Item; } + + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + + bool CollectedBy(cPlayer * a_Dest); // tolua_export + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + /// Returns the number of ticks that this entity has existed + int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export + + /// Returns true if the pickup has already been collected + bool IsCollected(void) const { return m_bCollected; } // tolua_export + + /// Returns true if created by player (i.e. vomiting), used for determining picking-up delay time + bool IsPlayerCreated(void) const { return m_bIsPlayerCreated; } // tolua_export + +private: + Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;) + + Vector3d m_WaterSpeed; + + /// The number of ticks that the entity has existed / timer between collect and destroy; in msec + float m_Timer; + + cItem m_Item; + + bool m_bCollected; + + bool m_bIsPlayerCreated; +}; // tolua_export + + + + diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp new file mode 100644 index 000000000..098417dc5 --- /dev/null +++ b/src/Entities/Player.cpp @@ -0,0 +1,1715 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Player.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "../UI/Window.h" +#include "../UI/WindowOwner.h" +#include "../World.h" +#include "Pickup.h" +#include "../PluginManager.h" +#include "../BlockEntities/BlockEntity.h" +#include "../GroupManager.h" +#include "../Group.h" +#include "../ChatColor.h" +#include "../Item.h" +#include "../Tracer.h" +#include "../Root.h" +#include "../OSSupport/Timer.h" +#include "../MersenneTwister.h" +#include "../Chunk.h" +#include "../Items/ItemHandler.h" + +#include "../Vector3d.h" +#include "../Vector3f.h" + +#include "../../iniFile/iniFile.h" +#include + +#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) + + + + + + +cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) + : super(etPlayer, 0.6, 1.8) + , m_GameMode(eGameMode_NotSet) + , m_IP("") + , m_LastBlockActionTime( 0 ) + , m_LastBlockActionCnt( 0 ) + , m_AirLevel( MAX_AIR_LEVEL ) + , m_AirTickTimer( DROWNING_TICKS ) + , m_bVisible( true ) + , m_LastGroundHeight( 0 ) + , m_bTouchGround( false ) + , m_Stance( 0.0 ) + , m_Inventory(*this) + , m_CurrentWindow(NULL) + , m_InventoryWindow(NULL) + , m_TimeLastPickupCheck( 0.f ) + , m_Color('-') + , m_ClientHandle( a_Client ) + , m_FoodLevel(MAX_FOOD_LEVEL) + , m_FoodSaturationLevel(5) + , m_FoodTickTimer(0) + , m_FoodExhaustionLevel(0) + , m_FoodPoisonedTicksRemaining(0) + , m_NormalMaxSpeed(0.1) + , m_SprintingMaxSpeed(0.13) + , m_IsCrouched(false) + , m_IsSprinting(false) + , m_IsSwimming(false) + , m_IsSubmerged(false) + , m_EatingFinishTick(-1) + , m_IsChargingBow(false) + , m_BowCharge(0) + , m_XpTotal(0) +{ + LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", + a_PlayerName.c_str(), a_Client->GetIPString().c_str(), + this, GetUniqueID() + ); + + m_InventoryWindow = new cInventoryWindow(*this); + m_CurrentWindow = m_InventoryWindow; + m_InventoryWindow->OpenedByPlayer(*this); + + SetMaxHealth(MAX_HEALTH); + m_Health = MAX_HEALTH; + + cTimer t1; + m_LastPlayerListTime = t1.GetNowTime(); + + m_TimeLastTeleportPacket = 0; + m_TimeLastPickupCheck = 0; + + m_PlayerName = a_PlayerName; + m_bDirtyPosition = true; // So chunks are streamed to player at spawn + + if (!LoadFromDisk()) + { + m_Inventory.Clear(); + SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX()); + SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY()); + SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ()); + + LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", + a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() + ); + } + m_LastJumpHeight = (float)(GetPosY()); + m_LastGroundHeight = (float)(GetPosY()); + m_Stance = GetPosY() + 1.62; + + cRoot::Get()->GetServer()->PlayerCreated(this); +} + + + + + +cPlayer::~cPlayer(void) +{ + LOGD("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID()); + + // Notify the server that the player is being destroyed + cRoot::Get()->GetServer()->PlayerDestroying(this); + + SaveToDisk(); + + m_World->RemovePlayer( this ); + + m_ClientHandle = NULL; + + delete m_InventoryWindow; + + LOGD("Player %p deleted", this); +} + + + + + +bool cPlayer::Initialize(cWorld * a_World) +{ + ASSERT(a_World != NULL); + + if (super::Initialize(a_World)) + { + // Remove the client handle from the server, it will be ticked from this object from now on + if (m_ClientHandle != NULL) + { + cRoot::Get()->GetServer()->ClientMovedToWorld(m_ClientHandle); + } + + GetWorld()->AddPlayer(this); + return true; + } + return false; +} + + + + + +void cPlayer::Destroyed() +{ + CloseWindow(false); + + m_ClientHandle = NULL; +} + + + + + +void cPlayer::SpawnOn(cClientHandle & a_Client) +{ + if (!m_bVisible || (m_ClientHandle == (&a_Client))) + { + return; + } + a_Client.SendPlayerSpawn(*this); + a_Client.SendEntityHeadLook(*this); + a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() ); + a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() ); + a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() ); + a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() ); + a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() ); +} + + + + + +void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (m_ClientHandle != NULL) + { + if (m_ClientHandle->IsDestroyed()) + { + // This should not happen, because destroying a client will remove it from the world, but just in case + m_ClientHandle = NULL; + return; + } + + if (!m_ClientHandle->IsPlaying()) + { + // We're not yet in the game, ignore everything + return; + } + } + + if (!a_Chunk.IsValid()) + { + // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83) + return; + } + + super::Tick(a_Dt, a_Chunk); + + // Set player swimming state + SetSwimState(a_Chunk); + + // Handle air drowning stuff + HandleAir(); + + // Handle charging the bow: + if (m_IsChargingBow) + { + m_BowCharge += 1; + } + + if (m_bDirtyPosition) + { + // Apply food exhaustion from movement: + ApplyFoodExhaustionFromMovement(); + + cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this); + BroadcastMovementUpdate(m_ClientHandle); + m_ClientHandle->StreamChunks(); + } + else + { + BroadcastMovementUpdate(m_ClientHandle); + } + + if (m_Health > 0) // make sure player is alive + { + m_World->CollectPickupsByPlayer(this); + + if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) + { + FinishEating(); + } + + HandleFood(); + } + + // Send Player List (Once per m_LastPlayerListTime/1000 ms) + cTimer t1; + if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) + { + m_World->SendPlayerList(this); + m_LastPlayerListTime = t1.GetNowTime(); + } +} + + + + + +int cPlayer::CalcLevelFromXp(int a_XpTotal) +{ + //level 0 to 15 + if(a_XpTotal <= XP_TO_LEVEL15) + { + return a_XpTotal / XP_PER_LEVEL_TO15; + } + + //level 30+ + if(a_XpTotal > XP_TO_LEVEL30) + { + return (int) (151.5 + sqrt( 22952.25 - (14 * (2220 - a_XpTotal)))) / 7; + } + + //level 16 to 30 + return (int) ( 29.5 + sqrt( 870.25 - (6 * ( 360 - a_XpTotal )))) / 3; +} + + + + + +int cPlayer::XpForLevel(int a_Level) +{ + //level 0 to 15 + if(a_Level <= 15) + { + return a_Level * XP_PER_LEVEL_TO15; + } + + //level 30+ + if(a_Level >= 31) + { + return (int) ( (3.5 * a_Level * a_Level) - (151.5 * a_Level) + 2220 ); + } + + //level 16 to 30 + return (int) ( (1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360 ); +} + + + + + +int cPlayer::XpGetLevel() +{ + return CalcLevelFromXp(m_XpTotal); +} + + + + + +float cPlayer::XpGetPercentage() +{ + int currentLevel = CalcLevelFromXp(m_XpTotal); + + return (float)m_XpTotal / (float)XpForLevel(1+currentLevel); +} + + + + + +bool cPlayer::SetExperience(int a_XpTotal) +{ + if(!(a_XpTotal >= 0) || (a_XpTotal > (INT_MAX - m_XpTotal))) + { + LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_XpTotal); + return false; //oops, they gave us a dodgey number + } + + m_XpTotal = a_XpTotal; + + return true; +} + + + + + +int cPlayer::AddExperience(int a_Xp_delta) +{ + if(a_Xp_delta < 0) + { + //value was negative, abort and report + LOGWARNING("Attempt was made to increment Xp by %d, must be positive", + a_Xp_delta); + return -1; //should we instead just return the current Xp? + } + + LOGD("Player \"%s\" earnt %d experience", m_PlayerName.c_str(), a_Xp_delta); + + m_XpTotal += a_Xp_delta; + + return m_XpTotal; +} + + + + + +void cPlayer::StartChargingBow(void) +{ + LOGD("Player \"%s\" started charging their bow", m_PlayerName.c_str()); + m_IsChargingBow = true; + m_BowCharge = 0; +} + + + + + +int cPlayer::FinishChargingBow(void) +{ + LOGD("Player \"%s\" finished charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge); + int res = m_BowCharge; + m_IsChargingBow = false; + m_BowCharge = 0; + return res; +} + + + + + +void cPlayer::CancelChargingBow(void) +{ + LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge); + m_IsChargingBow = false; + m_BowCharge = 0; +} + + + + + +void cPlayer::SetTouchGround(bool a_bTouchGround) +{ + m_bTouchGround = a_bTouchGround; + + if (!m_bTouchGround) + { + if (GetPosY() > m_LastJumpHeight) + { + m_LastJumpHeight = (float)GetPosY(); + } + cWorld * World = GetWorld(); + if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height)) + { + BLOCKTYPE BlockType = World->GetBlock(float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ())); + if (BlockType != E_BLOCK_AIR) + { + m_bTouchGround = true; + } + if ( + (BlockType == E_BLOCK_WATER) || + (BlockType == E_BLOCK_STATIONARY_WATER) || + (BlockType == E_BLOCK_LADDER) || + (BlockType == E_BLOCK_VINES) + ) + { + m_LastGroundHeight = (float)GetPosY(); + } + } + } + else + { + float Dist = (float)(m_LastGroundHeight - floor(GetPosY())); + int Damage = (int)(Dist - 3.f); + if (m_LastJumpHeight > m_LastGroundHeight) Damage++; + m_LastJumpHeight = (float)GetPosY(); + + if ((Damage > 0) && (!IsGameModeCreative())) + { + TakeDamage(dtFalling, NULL, Damage, Damage, 0); + } + + m_LastGroundHeight = (float)GetPosY(); + } +} + + + + + +void cPlayer::Heal(int a_Health) +{ + super::Heal(a_Health); + SendHealth(); +} + + + + + +void cPlayer::SetFoodLevel(int a_FoodLevel) +{ + m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL)); + SendHealth(); +} + + + + + +void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel) +{ + m_FoodSaturationLevel = std::max(0.0, std::min(a_FoodSaturationLevel, (double)m_FoodLevel)); +} + + + + + +void cPlayer::SetFoodTickTimer(int a_FoodTickTimer) +{ + m_FoodTickTimer = a_FoodTickTimer; +} + + + + + +void cPlayer::SetFoodExhaustionLevel(double a_FoodExhaustionLevel) +{ + m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodExhaustionLevel, 4.0)); +} + + + + + +void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining) +{ + m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining; +} + + + + + +bool cPlayer::Feed(int a_Food, double a_Saturation) +{ + if (m_FoodLevel >= MAX_FOOD_LEVEL) + { + return false; + } + + m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL); + m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel); + + SendHealth(); + return true; +} + + + + + +void cPlayer::FoodPoison(int a_NumTicks) +{ + bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0); + m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks); + if (!HasBeenFoodPoisoned) + { + // TODO: Send the poisoning indication to the client - how? + SendHealth(); + } +} + + + + + +void cPlayer::StartEating(void) +{ + // Set the timer: + m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS; + + // Send the packets: + m_World->BroadcastPlayerAnimation(*this, 5); + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cPlayer::FinishEating(void) +{ + // Reset the timer: + m_EatingFinishTick = -1; + + // Send the packets: + m_ClientHandle->SendEntityStatus(*this, ENTITY_STATUS_EATING_ACCEPTED); + m_World->BroadcastPlayerAnimation(*this, 0); + m_World->BroadcastEntityMetadata(*this); + + // consume the item: + cItem Item(GetEquippedItem()); + Item.m_ItemCount = 1; + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Item.m_ItemType); + if (!ItemHandler->EatItem(this, &Item)) + { + return; + } + ItemHandler->OnFoodEaten(m_World, this, &Item); + + GetInventory().RemoveOneEquippedItem(); + + //if the food is mushroom soup, return a bowl to the inventory + if( Item.m_ItemType == E_ITEM_MUSHROOM_SOUP ) { + cItem emptyBowl(E_ITEM_BOWL, 1, 0, ""); + GetInventory().AddItem(emptyBowl, true, true); + } +} + + + + + +void cPlayer::AbortEating(void) +{ + m_EatingFinishTick = -1; + m_World->BroadcastPlayerAnimation(*this, 0); + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cPlayer::SendHealth(void) +{ + if (m_ClientHandle != NULL) + { + m_ClientHandle->SendHealth(); + } +} + + + + + +void cPlayer::ClearInventoryPaintSlots(void) +{ + // Clear the list of slots that are being inventory-painted. Used by cWindow only + m_InventoryPaintSlots.clear(); +} + + + + + +void cPlayer::AddInventoryPaintSlot(int a_SlotNum) +{ + // Add a slot to the list for inventory painting. Used by cWindow only + m_InventoryPaintSlots.push_back(a_SlotNum); +} + + + + + +const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const +{ + // Return the list of slots currently stored for inventory painting. Used by cWindow only + return m_InventoryPaintSlots; +} + + + + + +double cPlayer::GetMaxSpeed(void) const +{ + return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed; +} + + + + + +void cPlayer::SetNormalMaxSpeed(double a_Speed) +{ + m_NormalMaxSpeed = a_Speed; + if (!m_IsSprinting) + { + m_ClientHandle->SendPlayerMaxSpeed(); + } +} + + + + + +void cPlayer::SetSprintingMaxSpeed(double a_Speed) +{ + m_SprintingMaxSpeed = a_Speed; + if (m_IsSprinting) + { + m_ClientHandle->SendPlayerMaxSpeed(); + } +} + + + + + +void cPlayer::SetCrouch(bool a_IsCrouched) +{ + // Set the crouch status, broadcast to all visible players + + if (a_IsCrouched == m_IsCrouched) + { + // No change + return; + } + m_IsCrouched = a_IsCrouched; + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cPlayer::SetSprint(bool a_IsSprinting) +{ + if (a_IsSprinting == m_IsSprinting) + { + // No change + return; + } + + m_IsSprinting = a_IsSprinting; + m_ClientHandle->SendPlayerMaxSpeed(); +} + + + + + +void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + if (a_TDI.DamageType != dtInVoid) + { + if (IsGameModeCreative()) + { + // No damage / health in creative mode + return; + } + } + + super::DoTakeDamage(a_TDI); + + // Any kind of damage adds food exhaustion + AddFoodExhaustion(0.3f); + + SendHealth(); +} + + + + + +void cPlayer::KilledBy(cEntity * a_Killer) +{ + super::KilledBy(a_Killer); + + if (m_Health > 0) + { + return; // not dead yet =] + } + + m_bVisible = false; // So new clients don't see the player + + // Puke out all the items + cItems Pickups; + m_Inventory.CopyToItems(Pickups); + m_Inventory.Clear(); + m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); + SaveToDisk(); // Save it, yeah the world is a tough place ! +} + + + + + +void cPlayer::Respawn(void) +{ + m_Health = GetMaxHealth(); + + // Reset food level: + m_FoodLevel = MAX_FOOD_LEVEL; + m_FoodSaturationLevel = 5; + + m_ClientHandle->SendRespawn(); + + // Extinguish the fire: + StopBurning(); + + TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ()); + + SetVisible(true); +} + + + + + +double cPlayer::GetEyeHeight(void) const +{ + return m_Stance; +} + + + + +Vector3d cPlayer::GetEyePosition(void) const +{ + return Vector3d( GetPosX(), m_Stance, GetPosZ() ); +} + + + + + +bool cPlayer::IsGameModeCreative(void) const +{ + return (m_GameMode == gmCreative) || // Either the player is explicitly in Creative + ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Creative +} + + + + + +bool cPlayer::IsGameModeSurvival(void) const +{ + return (m_GameMode == gmSurvival) || // Either the player is explicitly in Survival + ((m_GameMode == gmNotSet) && m_World->IsGameModeSurvival()); // or they inherit from the world and the world is Survival +} + + + + + +bool cPlayer::IsGameModeAdventure(void) const +{ + return (m_GameMode == gmCreative) || // Either the player is explicitly in Adventure + ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Adventure +} + + + + + +void cPlayer::OpenWindow(cWindow * a_Window) +{ + if (a_Window != m_CurrentWindow) + { + CloseWindow(false); + } + a_Window->OpenedByPlayer(*this); + m_CurrentWindow = a_Window; + a_Window->SendWholeWindow(*GetClientHandle()); +} + + + + + +void cPlayer::CloseWindow(bool a_CanRefuse) +{ + if (m_CurrentWindow == NULL) + { + m_CurrentWindow = m_InventoryWindow; + return; + } + + if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse) + { + // Close accepted, go back to inventory window (the default): + m_CurrentWindow = m_InventoryWindow; + } + else + { + // Re-open the window + m_CurrentWindow->OpenedByPlayer(*this); + m_CurrentWindow->SendWholeWindow(*GetClientHandle()); + } +} + + + + + +void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse) +{ + if ((m_CurrentWindow == NULL) || (m_CurrentWindow->GetWindowID() != a_WindowID)) + { + return; + } + CloseWindow(); +} + + + + + +void cPlayer::SetLastBlockActionTime() +{ + if (m_World != NULL) + { + m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f; + } +} + + + + + +void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) +{ + m_LastBlockActionCnt = a_LastBlockActionCnt; +} + + + + + +void cPlayer::SetGameMode(eGameMode a_GameMode) +{ + if ((a_GameMode < gmMin) || (a_GameMode >= gmMax)) + { + LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); + return; + } + + if (m_GameMode == a_GameMode) + { + // Gamemode already set + return; + } + + m_GameMode = a_GameMode; + m_ClientHandle->SendGameMode(a_GameMode); +} + + + + + +void cPlayer::LoginSetGameMode( eGameMode a_GameMode ) +{ + m_GameMode = a_GameMode; +} + + + + + +void cPlayer::SetIP(const AString & a_IP) +{ + m_IP = a_IP; +} + + + + + +void cPlayer::SendMessage(const AString & a_Message) +{ + m_ClientHandle->SendChat(a_Message); +} + + + + + +void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) +{ + SetPosition( a_PosX, a_PosY, a_PosZ ); + m_LastGroundHeight = (float)a_PosY; + + m_World->BroadcastTeleportEntity(*this, GetClientHandle()); + m_ClientHandle->SendPlayerMoveLook(); +} + + + + + +Vector3d cPlayer::GetThrowStartPos(void) const +{ + Vector3d res = GetEyePosition(); + + // Adjust the position to be just outside the player's bounding box: + res.x += 0.16 * cos(GetPitch()); + res.y += -0.1; + res.z += 0.16 * sin(GetPitch()); + + return res; +} + + + + + +Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const +{ + Vector3d res = GetLookVector(); + res.Normalize(); + + // TODO: Add a slight random change (+-0.0075 in each direction) + + return res * a_SpeedCoeff; +} + + + + + +void cPlayer::MoveTo( const Vector3d & a_NewPos ) +{ + if ((a_NewPos.y < -990) && (GetPosY() > -100)) + { + // When attached to an entity, the client sends position packets with weird coords: + // Y = -999 and X, Z = attempting to create speed, usually up to 0.03 + // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while + // the client may still send more of these nonsensical packets. + if (m_AttachedTo != NULL) + { + Vector3d AddSpeed(a_NewPos); + AddSpeed.y = 0; + m_AttachedTo->AddSpeed(AddSpeed); + } + return; + } + + // TODO: should do some checks to see if player is not moving through terrain + // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too + + SetPosition( a_NewPos ); + SetStance(a_NewPos.y + 1.62); +} + + + + + +void cPlayer::SetVisible(bool a_bVisible) +{ + if (a_bVisible && !m_bVisible) // Make visible + { + m_bVisible = true; + m_World->BroadcastSpawnEntity(*this); + } + if (!a_bVisible && m_bVisible) + { + m_bVisible = false; + m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients + } +} + + + + + +void cPlayer::AddToGroup( const AString & a_GroupName ) +{ + cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); + m_Groups.push_back( Group ); + LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); + ResolveGroups(); + ResolvePermissions(); +} + + + + + +void cPlayer::RemoveFromGroup( const AString & a_GroupName ) +{ + bool bRemoved = false; + for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) + { + if( (*itr)->GetName().compare(a_GroupName ) == 0 ) + { + m_Groups.erase( itr ); + bRemoved = true; + break; + } + } + + if( bRemoved ) + { + LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); + ResolveGroups(); + ResolvePermissions(); + } + else + { + LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() ); + } +} + + + + + +bool cPlayer::CanUseCommand( const AString & a_Command ) +{ + for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) + { + if( (*itr)->HasCommand( a_Command ) ) return true; + } + return false; +} + + + + + +bool cPlayer::HasPermission(const AString & a_Permission) +{ + if (a_Permission.empty()) + { + // Empty permission request is always granted + return true; + } + + AStringVector Split = StringSplit( a_Permission, "." ); + PermissionMap Possibilities = m_ResolvedPermissions; + // Now search the namespaces + while( Possibilities.begin() != Possibilities.end() ) + { + PermissionMap::iterator itr = Possibilities.begin(); + if( itr->second ) + { + AStringVector OtherSplit = StringSplit( itr->first, "." ); + if( OtherSplit.size() <= Split.size() ) + { + unsigned int i; + for( i = 0; i < OtherSplit.size(); ++i ) + { + if( OtherSplit[i].compare( Split[i] ) != 0 ) + { + if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard! + break; + } + } + if( i == Split.size() ) return true; + } + } + Possibilities.erase( itr ); + } + + // Nothing that matched :( + return false; +} + + + + + +bool cPlayer::IsInGroup( const AString & a_Group ) +{ + for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr ) + { + if( a_Group.compare( (*itr)->GetName().c_str() ) == 0 ) + return true; + } + return false; +} + + + + + +void cPlayer::ResolvePermissions() +{ + m_ResolvedPermissions.clear(); // Start with an empty map yo~ + + // Copy all player specific permissions into the resolved permissions map + for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + + for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr ) + { + const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); + for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + } +} + + + + + +void cPlayer::ResolveGroups() +{ + // Clear resolved groups first + m_ResolvedGroups.clear(); + + // Get a complete resolved list of all groups the player is in + std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates + GroupList ToIterate; + for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr ) + { + ToIterate.push_back( *GroupItr ); + } + while( ToIterate.begin() != ToIterate.end() ) + { + cGroup* CurrentGroup = *ToIterate.begin(); + if( AllGroups.find( CurrentGroup ) != AllGroups.end() ) + { + LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", + m_PlayerName.c_str(), CurrentGroup->GetName().c_str() + ); + } + else + { + AllGroups[ CurrentGroup ] = true; + m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list + const cGroup::GroupList & Inherits = CurrentGroup->GetInherits(); + for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr ) + { + if( AllGroups.find( *itr ) != AllGroups.end() ) + { + LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() ); + continue; + } + ToIterate.push_back( *itr ); + } + } + ToIterate.erase( ToIterate.begin() ); + } +} + + + + + +AString cPlayer::GetColor(void) const +{ + if ( m_Color != '-' ) + { + return cChatColor::MakeColor( m_Color ); + } + + if ( m_Groups.size() < 1 ) + { + return cChatColor::White; + } + + return (*m_Groups.begin())->GetColor(); +} + + + + + +void cPlayer::TossItem( + bool a_bDraggingItem, + char a_Amount /* = 1 */, + short a_CreateType /* = 0 */, + short a_CreateHealth /* = 0 */ +) +{ + cItems Drops; + if (a_CreateType != 0) + { + // Just create item without touching the inventory (used in creative mode) + Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth)); + } + else + { + // Drop an item from the inventory: + if (a_bDraggingItem) + { + cItem & Item = GetDraggingItem(); + if (!Item.IsEmpty()) + { + char OriginalItemAmount = Item.m_ItemCount; + Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount); + Drops.push_back(Item); + if (OriginalItemAmount > a_Amount) + { + Item.m_ItemCount = OriginalItemAmount - (char)a_Amount; + } + else + { + Item.Empty(); + } + } + } + else + { + // Else drop equipped item + cItem DroppedItem(GetInventory().GetEquippedItem()); + if (!DroppedItem.IsEmpty()) + { + if (GetInventory().RemoveOneEquippedItem()) + { + DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again + Drops.push_back(DroppedItem); + } + } + } + } + double vX = 0, vY = 0, vZ = 0; + EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); + vY = -vY * 2 + 1.f; + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player +} + + + + + +bool cPlayer::MoveToWorld(const char * a_WorldName) +{ + cWorld * World = cRoot::Get()->GetWorld(a_WorldName); + if (World == NULL) + { + LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName); + return false; + } + + eDimension OldDimension = m_World->GetDimension(); + + // Remove all links to the old world + m_World->RemovePlayer(this); + m_ClientHandle->RemoveFromAllChunks(); + m_World->RemoveEntity(this); + + // If the dimension is different, we can send the respawn packet + // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02 + m_ClientHandle->MoveToWorld(*World, (OldDimension != World->GetDimension())); + + // Add player to all the necessary parts of the new world + SetWorld(World); + World->AddEntity(this); + World->AddPlayer(this); + + return true; +} + + + + + +void cPlayer::LoadPermissionsFromDisk() +{ + m_Groups.clear(); + m_Permissions.clear(); + + cIniFile IniFile; + if (IniFile.ReadFile("users.ini")) + { + std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", ""); + if (!Groups.empty()) + { + AStringVector Split = StringSplit( Groups, "," ); + for( unsigned int i = 0; i < Split.size(); i++ ) + { + AddToGroup( Split[i].c_str() ); + } + } + else + { + AddToGroup("Default"); + } + + m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0]; + } + else + { + LOGWARN("Failed to read the users.ini file. The player will be added only to the Default group."); + AddToGroup("Default"); + } + ResolvePermissions(); +} + + + + +bool cPlayer::LoadFromDisk() +{ + LoadPermissionsFromDisk(); + + // Log player permissions, cause it's what the cool kids do + LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); + for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) LOGINFO("%s", itr->first.c_str() ); + } + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmRead)) + { + // This is a new player whom we haven't seen yet, bail out, let them have the defaults + return false; + } + + AString buffer; + if (f.ReadRestOfFile(buffer) != f.GetSize()) + { + LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str()); + return false; + } + f.Close(); //cool kids play nice + + Json::Value root; + Json::Reader reader; + if (!reader.parse(buffer, root, false)) + { + LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str()); + } + + Json::Value & JSON_PlayerPosition = root["position"]; + if (JSON_PlayerPosition.size() == 3) + { + SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); + SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); + SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); + m_LastPosX = GetPosX(); + m_LastPosY = GetPosY(); + m_LastPosZ = GetPosZ(); + m_LastFoodPos = GetPosition(); + } + + Json::Value & JSON_PlayerRotation = root["rotation"]; + if (JSON_PlayerRotation.size() == 3) + { + SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); + SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); + SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); + } + + m_Health = root.get("health", 0).asInt(); + m_AirLevel = root.get("air", MAX_AIR_LEVEL).asInt(); + m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt(); + m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); + m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); + m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); + + SetExperience(root.get("experience", 0).asInt()); + + m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); + + m_Inventory.LoadFromJson(root["inventory"]); + + m_LoadedWorldName = root.get("world", "world").asString(); + + LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", + m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() + ); + + return true; +} + + + + + +bool cPlayer::SaveToDisk() +{ + cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); + + // create the JSON data + Json::Value JSON_PlayerPosition; + JSON_PlayerPosition.append(Json::Value(GetPosX())); + JSON_PlayerPosition.append(Json::Value(GetPosY())); + JSON_PlayerPosition.append(Json::Value(GetPosZ())); + + Json::Value JSON_PlayerRotation; + JSON_PlayerRotation.append(Json::Value(GetRotation())); + JSON_PlayerRotation.append(Json::Value(GetPitch())); + JSON_PlayerRotation.append(Json::Value(GetRoll())); + + Json::Value JSON_Inventory; + m_Inventory.SaveToJson(JSON_Inventory); + + Json::Value root; + root["position"] = JSON_PlayerPosition; + root["rotation"] = JSON_PlayerRotation; + root["inventory"] = JSON_Inventory; + root["health"] = m_Health; + root["experience"] = m_XpTotal; + root["air"] = m_AirLevel; + root["food"] = m_FoodLevel; + root["foodSaturation"] = m_FoodSaturationLevel; + root["foodTickTimer"] = m_FoodTickTimer; + root["foodExhaustion"] = m_FoodExhaustionLevel; + root["world"] = GetWorld()->GetName(); + + if (m_GameMode == GetWorld()->GetGameMode()) + { + root["gamemode"] = (int) eGameMode_NotSet; + } + else + { + root["gamemode"] = (int) m_GameMode; + } + + Json::StyledWriter writer; + std::string JsonData = writer.write(root); + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmWrite)) + { + LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); + return false; + } + if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) + { + LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); + return false; + } + return true; +} + + + + + +cPlayer::StringList cPlayer::GetResolvedPermissions() +{ + StringList Permissions; + + const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; + for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) Permissions.push_back( itr->first ); + } + + return Permissions; +} + + + + + +void cPlayer::UseEquippedItem(void) +{ + if (IsGameModeCreative()) // No damage in creative + { + return; + } + + GetInventory().DamageEquippedItem(); +} + + + + + +void cPlayer::SetSwimState(cChunk & a_Chunk) +{ + int RelY = (int)floor(m_LastPosY + 0.1); + if ((RelY < 0) || (RelY >= cChunkDef::Height - 1)) + { + m_IsSwimming = false; + m_IsSubmerged = false; + return; + } + + BLOCKTYPE BlockIn; + int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width; + + // Check if the player is swimming: + // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk + if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn)) + { + // This sometimes happens on Linux machines + // Ref.: http://forum.mc-server.org/showthread.php?tid=1244 + LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}", + RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ() + ); + m_IsSwimming = false; + m_IsSubmerged = false; + return; + } + m_IsSwimming = IsBlockWater(BlockIn); + + // Check if the player is submerged: + VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn)); + m_IsSubmerged = IsBlockWater(BlockIn); +} + + + + + +void cPlayer::HandleAir(void) +{ + // Ref.: http://www.minecraftwiki.net/wiki/Chunk_format + // see if the player is /submerged/ water (block above is water) + // Get the type of block the player's standing in: + + if (IsSubmerged()) + { + // either reduce air level or damage player + if (m_AirLevel < 1) + { + if (m_AirTickTimer < 1) + { + // damage player + TakeDamage(dtDrowning, NULL, 1, 1, 0); + // reset timer + m_AirTickTimer = DROWNING_TICKS; + } + else + { + m_AirTickTimer -= 1; + } + } + else + { + // reduce air supply + m_AirLevel -= 1; + } + } + else + { + // set the air back to maximum + m_AirLevel = MAX_AIR_LEVEL; + m_AirTickTimer = DROWNING_TICKS; + } +} + + + + + +void cPlayer::HandleFood(void) +{ + // Ref.: http://www.minecraftwiki.net/wiki/Hunger + + // Remember the food level before processing, for later comparison + int LastFoodLevel = m_FoodLevel; + + // Heal or damage, based on the food level, using the m_FoodTickTimer: + if ((m_FoodLevel > 17) || (m_FoodLevel <= 0)) + { + m_FoodTickTimer++; + if (m_FoodTickTimer >= 80) + { + m_FoodTickTimer = 0; + + if (m_FoodLevel >= 17) + { + // Regenerate health from food, incur 3 pts of food exhaustion: + Heal(1); + m_FoodExhaustionLevel += 3; + } + else if (m_FoodLevel <= 0) + { + // Damage from starving + TakeDamage(dtStarving, NULL, 1, 1, 0); + } + } + } + + // Apply food poisoning food exhaustion: + if (m_FoodPoisonedTicksRemaining > 0) + { + m_FoodPoisonedTicksRemaining--; + m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick + } + + // Apply food exhaustion that has accumulated: + if (m_FoodExhaustionLevel >= 4) + { + m_FoodExhaustionLevel -= 4; + + if (m_FoodSaturationLevel >= 1) + { + m_FoodSaturationLevel -= 1; + } + else + { + m_FoodLevel = std::max(m_FoodLevel - 1, 0); + } + } + + if (m_FoodLevel != LastFoodLevel) + { + SendHealth(); + } +} + + + + + +void cPlayer::ApplyFoodExhaustionFromMovement() +{ + if (IsGameModeCreative()) + { + return; + } + + // Calculate the distance travelled, update the last pos: + Vector3d Movement(GetPosition() - m_LastFoodPos); + Movement.y = 0; // Only take XZ movement into account + m_LastFoodPos = GetPosition(); + + // If riding anything, apply no food exhaustion + if (m_AttachedTo != NULL) + { + return; + } + + // Apply the exhaustion based on distance travelled: + double BaseExhaustion = Movement.Length(); + if (IsSprinting()) + { + // 0.1 pt per meter sprinted + BaseExhaustion = BaseExhaustion * 0.1; + } + else if (IsSwimming()) + { + // 0.015 pt per meter swum + BaseExhaustion = BaseExhaustion * 0.015; + } + else + { + // 0.01 pt per meter walked / sneaked + BaseExhaustion = BaseExhaustion * 0.01; + } + m_FoodExhaustionLevel += BaseExhaustion; +} + + + + diff --git a/src/Entities/Player.h b/src/Entities/Player.h new file mode 100644 index 000000000..ab2f94d4c --- /dev/null +++ b/src/Entities/Player.h @@ -0,0 +1,447 @@ + +#pragma once + +#include "Pawn.h" +#include "../Inventory.h" +#include "../Defines.h" +#include "../World.h" + + + + + +class cGroup; +class cWindow; +class cClientHandle; + + + + + +// tolua_begin +class cPlayer : + public cPawn +{ + typedef cPawn super; + +public: + enum + { + MAX_HEALTH = 20, + MAX_FOOD_LEVEL = 20, + EATING_TICKS = 30, ///< Number of ticks it takes to eat an item + MAX_AIR_LEVEL = 300, + DROWNING_TICKS = 10, //number of ticks per heart of damage + } ; + // tolua_end + + CLASS_PROTODEF(cPlayer) + + + cPlayer(cClientHandle * a_Client, const AString & a_PlayerName); + virtual ~cPlayer(); + + virtual bool Initialize(cWorld * a_World) override; + + virtual void SpawnOn(cClientHandle & a_Client) override; + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override { }; + + /// Returns the curently equipped weapon; empty item if none + virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } + + /// Returns the currently equipped helmet; empty item if nonte + virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); } + + /// Returns the currently equipped chestplate; empty item if none + virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); } + + /// Returns the currently equipped leggings; empty item if none + virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); } + + /// Returns the currently equipped boots; empty item if none + virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); } + + + // tolua_begin + + /** Sets the experience total + Returns true on success + "should" really only be called at init or player death, plugins excepted + */ + bool SetExperience(int a_XpTotal); + + /* Adds Xp, "should" not inc more than MAX_EXPERIENCE_ORB_SIZE unless you're a plugin being funny, *cough* cheating + Returns the new total experience, -1 on error + */ + int AddExperience(int a_Xp_delta); + + /// Gets the experience total - XpTotal + inline int XpGetTotal(void) { return m_XpTotal; } + + /// Gets the current level - XpLevel + int XpGetLevel(void); + + /// Gets the experience bar percentage - XpP + float XpGetPercentage(void); + + // tolua_end + + /// Starts charging the equipped bow + void StartChargingBow(void); + + /// Finishes charging the current bow. Returns the number of ticks for which the bow has been charged + int FinishChargingBow(void); + + /// Cancels the current bow charging + void CancelChargingBow(void); + + /// Returns true if the player is currently charging the bow + bool IsChargingBow(void) const { return m_IsChargingBow; } + + void SetTouchGround( bool a_bTouchGround ); + inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; } + double GetEyeHeight(void) const; // tolua_export + Vector3d GetEyePosition(void) const; // tolua_export + inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export + inline const double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc. + inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export + inline const cInventory & GetInventory(void) const { return m_Inventory; } + + inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export + + virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override; + + // tolua_begin + + /// Returns the position where projectiles thrown by this player should start, player eye position + adjustment + Vector3d GetThrowStartPos(void) const; + + /// Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff. + Vector3d GetThrowSpeed(double a_SpeedCoeff) const; + + /// Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable + eGameMode GetGameMode(void) const { return m_GameMode; } + + /// Returns the current effective gamemode (inherited gamemode is resolved before returning) + eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; } + + /** Sets the gamemode for the player. + The gamemode may be gmNotSet, in that case the player inherits the world's gamemode. + Updates the gamemode on the client (sends the packet) + */ + void SetGameMode(eGameMode a_GameMode); + + /// Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world + bool IsGameModeCreative(void) const; + + /// Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world + bool IsGameModeSurvival(void) const; + + /// Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world + bool IsGameModeAdventure(void) const; + + AString GetIP(void) const { return m_IP; } // tolua_export + + // tolua_end + + void SetIP(const AString & a_IP); + + float GetLastBlockActionTime() { return m_LastBlockActionTime; } + int GetLastBlockActionCnt() { return m_LastBlockActionCnt; } + void SetLastBlockActionCnt( int ); + void SetLastBlockActionTime(); + + // Sets the current gamemode, doesn't check validity, doesn't send update packets to client + void LoginSetGameMode(eGameMode a_GameMode); + + /// Tries to move to a new position, with attachment-related checks (y == -999) + void MoveTo(const Vector3d & a_NewPos); // tolua_export + + cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export + const cWindow * GetWindow(void) const { return m_CurrentWindow; } + + /// Opens the specified window; closes the current one first using CloseWindow() + void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp + + // tolua_begin + + /// Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true + void CloseWindow(bool a_CanRefuse = true); + + /// Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow + void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true); + + cClientHandle * GetClientHandle(void) const { return m_ClientHandle; } + + void SendMessage(const AString & a_Message); + + const AString & GetName(void) const { return m_PlayerName; } + void SetName(const AString & a_Name) { m_PlayerName = a_Name; } + + // tolua_end + + typedef std::list< cGroup* > GroupList; + typedef std::list< std::string > StringList; + + /// Adds a player to existing group or creates a new group when it doesn't exist + void AddToGroup( const AString & a_GroupName ); // tolua_export + + /// Removes a player from the group, resolves permissions and group inheritance (case sensitive) + void RemoveFromGroup( const AString & a_GroupName ); // tolua_export + + bool CanUseCommand( const AString & a_Command ); // tolua_export + bool HasPermission( const AString & a_Permission ); // tolua_export + const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS << + StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS << + bool IsInGroup( const AString & a_Group ); // tolua_export + + // tolua_begin + + /// Returns the full color code to use for this player, based on their primary group or set in m_Color + AString GetColor(void) const; + + void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0); + + /// Heals the player by the specified amount of HPs (positive only); sends health update + void Heal(int a_Health); + + int GetFoodLevel (void) const { return m_FoodLevel; } + double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; } + int GetFoodTickTimer (void) const { return m_FoodTickTimer; } + double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; } + int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; } + + int GetAirLevel (void) const { return m_AirLevel; } + + /// Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore + bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); } + + void SetFoodLevel (int a_FoodLevel); + void SetFoodSaturationLevel (double a_FoodSaturationLevel); + void SetFoodTickTimer (int a_FoodTickTimer); + void SetFoodExhaustionLevel (double a_FoodExhaustionLevel); + void SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining); + + /// Adds to FoodLevel and FoodSaturationLevel, returns true if any food has been consumed, false if player "full" + bool Feed(int a_Food, double a_Saturation); + + /// Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. + void AddFoodExhaustion(double a_Exhaustion) + { + m_FoodExhaustionLevel += a_Exhaustion; + } + + /// Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two + void FoodPoison(int a_NumTicks); + + /// Returns true if the player is currently in the process of eating the currently equipped item + bool IsEating(void) const { return (m_EatingFinishTick >= 0); } + + // tolua_end + + /// Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet + void StartEating(void); + + /// Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets + void FinishEating(void); + + /// Aborts the current eating operation + void AbortEating(void); + + virtual void KilledBy(cEntity * a_Killer) override; + + void Respawn(void); // tolua_export + + void SetVisible( bool a_bVisible ); // tolua_export + bool IsVisible(void) const { return m_bVisible; } // tolua_export + + bool MoveToWorld(const char * a_WorldName); // tolua_export + + bool SaveToDisk(void); + bool LoadFromDisk(void); + void LoadPermissionsFromDisk(void); // tolua_export + + const AString & GetLoadedWorldName() { return m_LoadedWorldName; } + + void UseEquippedItem(void); + + void SendHealth(void); + + // In UI windows, the item that the player is dragging: + bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); } + cItem & GetDraggingItem(void) {return m_DraggingItem; } + + // In UI windows, when inventory-painting: + /// Clears the list of slots that are being inventory-painted. To be used by cWindow only + void ClearInventoryPaintSlots(void); + + /// Adds a slot to the list for inventory painting. To be used by cWindow only + void AddInventoryPaintSlot(int a_SlotNum); + + /// Returns the list of slots currently stored for inventory painting. To be used by cWindow only + const cSlotNums & GetInventoryPaintSlots(void) const; + + // tolua_begin + + /// Returns the current maximum speed, as reported in the 1.6.1+ protocol (takes current sprinting state into account) + double GetMaxSpeed(void) const; + + /// Gets the normal maximum speed, as reported in the 1.6.1+ protocol, in the protocol units + double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; } + + /// Gets the sprinting maximum speed, as reported in the 1.6.1+ protocol, in the protocol units + double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; } + + /// Sets the normal maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed. + void SetNormalMaxSpeed(double a_Speed); + + /// Sets the sprinting maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed. + void SetSprintingMaxSpeed(double a_Speed); + + /// Sets the crouch status, broadcasts to all visible players + void SetCrouch(bool a_IsCrouched); + + /// Starts or stops sprinting, sends the max speed update to the client, if needed + void SetSprint(bool a_IsSprinting); + + /// Returns whether the player is swimming or not + virtual bool IsSwimming(void) const{ return m_IsSwimming; } + + /// Return whether the player is under water or not + virtual bool IsSubmerged(void) const{ return m_IsSubmerged; } + + // tolua_end + + // cEntity overrides: + virtual bool IsCrouched (void) const { return m_IsCrouched; } + virtual bool IsSprinting(void) const { return m_IsSprinting; } + virtual bool IsRclking (void) const { return IsEating(); } + + + +protected: + typedef std::map< std::string, bool > PermissionMap; + PermissionMap m_ResolvedPermissions; + PermissionMap m_Permissions; + + GroupList m_ResolvedGroups; + GroupList m_Groups; + + std::string m_PlayerName; + std::string m_LoadedWorldName; + + /// Xp Level stuff + enum + { + XP_TO_LEVEL15 = 255, + XP_PER_LEVEL_TO15 = 17, + XP_TO_LEVEL30 = 825 + } ; + + /// Player's air level (for swimming) + int m_AirLevel; + + /// used to time ticks between damage taken via drowning/suffocation + int m_AirTickTimer; + + bool m_bVisible; + + // Food-related variables: + /// Represents the food bar, one point equals half a "drumstick" + int m_FoodLevel; + + /// "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel + double m_FoodSaturationLevel; + + /// Count-up to the healing or damaging action, based on m_FoodLevel + int m_FoodTickTimer; + + /// A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little + double m_FoodExhaustionLevel; + + /// Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned + int m_FoodPoisonedTicksRemaining; + + /// Last position that has been recorded for food-related processing: + Vector3d m_LastFoodPos; + + float m_LastJumpHeight; + float m_LastGroundHeight; + bool m_bTouchGround; + double m_Stance; + cInventory m_Inventory; + cWindow * m_CurrentWindow; + cWindow * m_InventoryWindow; + + float m_TimeLastPickupCheck; + + void ResolvePermissions(); + + void ResolveGroups(); + char m_Color; + + float m_LastBlockActionTime; + int m_LastBlockActionCnt; + eGameMode m_GameMode; + std::string m_IP; + + cItem m_DraggingItem; + + long long m_LastPlayerListTime; + static const unsigned short PLAYER_LIST_TIME_MS = 1000; // 1000 = once per second + + cClientHandle * m_ClientHandle; + + cSlotNums m_InventoryPaintSlots; + + /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is walking. 0.1 by default + double m_NormalMaxSpeed; + + /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is sprinting. 0.13 by default + double m_SprintingMaxSpeed; + + bool m_IsCrouched; + bool m_IsSprinting; + + bool m_IsSwimming; + bool m_IsSubmerged; + + /// The world tick in which eating will be finished. -1 if not eating + Int64 m_EatingFinishTick; + + /// Player Xp level + int m_XpTotal; + + /// Caculates the Xp needed for a given level, ref: http://minecraft.gamepedia.com/XP + static int XpForLevel(int a_Level); + + /// inverse of XpAtLevel, ref: http://minecraft.gamepedia.com/XP values are as per this with pre-calculations + static int CalcLevelFromXp(int a_XpTotal); + + bool m_IsChargingBow; + int m_BowCharge; + + virtual void Destroyed(void); + + /// Filters out damage for creative mode + virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + + /// Called in each tick to handle food-related processing + void HandleFood(void); + + /// Called in each tick to handle air-related processing i.e. drowning + void HandleAir(); + + /// Called once per tick to set IsSwimming and IsSubmerged + void SetSwimState(cChunk & a_Chunk); + + /// Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) + void ApplyFoodExhaustionFromMovement(); +} ; // tolua_export + + + + diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp new file mode 100644 index 000000000..c63b9523b --- /dev/null +++ b/src/Entities/ProjectileEntity.cpp @@ -0,0 +1,743 @@ + +// ProjectileEntity.cpp + +// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types + +#include "Globals.h" +#include "ProjectileEntity.h" +#include "../ClientHandle.h" +#include "Player.h" +#include "../LineBlockTracer.h" +#include "../BoundingBox.h" +#include "../ChunkMap.h" +#include "../Chunk.h" + + + + + +/// Converts an angle in radians into a byte representation used by the network protocol +#define ANGLE_TO_PROTO(X) (Byte)(X * 255 / 360) + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProjectileTracerCallback: + +class cProjectileTracerCallback : + public cBlockTracer::cCallbacks +{ +public: + cProjectileTracerCallback(cProjectileEntity * a_Projectile) : + m_Projectile(a_Projectile), + m_SlowdownCoeff(0.99) // Default slowdown when not in water + { + } + + double GetSlowdownCoeff(void) const { return m_SlowdownCoeff; } + +protected: + cProjectileEntity * m_Projectile; + double m_SlowdownCoeff; + + // cCallbacks overrides: + virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override + { + /* + // DEBUG: + LOGD("Hit block %d:%d at {%d, %d, %d} face %d, %s (%s)", + a_BlockType, a_BlockMeta, + a_BlockX, a_BlockY, a_BlockZ, a_EntryFace, + g_BlockIsSolid[a_BlockType] ? "solid" : "non-solid", + ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str() + ); + */ + + if (g_BlockIsSolid[a_BlockType]) + { + // The projectile hit a solid block + // Calculate the exact hit coords: + cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1); + Vector3d Line1 = m_Projectile->GetPosition(); + Vector3d Line2 = Line1 + m_Projectile->GetSpeed(); + double LineCoeff = 0; + char Face; + if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face)) + { + Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff; + m_Projectile->OnHitSolidBlock(Intersection, Face); + return true; + } + else + { + LOGD("WEIRD! block tracer reports a hit, but BBox tracer doesn't. Ignoring the hit."); + } + } + + // Convey some special effects from special blocks: + switch (a_BlockType) + { + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + m_Projectile->StartBurning(30); + m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.9); // Slow down to 0.9* the speed each tick when moving through lava + break; + } + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + m_Projectile->StopBurning(); + m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.8); // Slow down to 0.8* the speed each tick when moving through water + break; + } + } // switch (a_BlockType) + + // Continue tracing + return false; + } +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProjectileEntityCollisionCallback: + +class cProjectileEntityCollisionCallback : + public cEntityCallback +{ +public: + cProjectileEntityCollisionCallback(cProjectileEntity * a_Projectile, const Vector3d & a_Pos, const Vector3d & a_NextPos) : + m_Projectile(a_Projectile), + m_Pos(a_Pos), + m_NextPos(a_NextPos), + m_MinCoeff(1), + m_HitEntity(NULL) + { + } + + + virtual bool Item(cEntity * a_Entity) override + { + if ( + (a_Entity == m_Projectile) || // Do not check collisions with self + (a_Entity == m_Projectile->GetCreator()) // Do not check whoever shot the projectile + ) + { + // TODO: Don't check creator only for the first 5 ticks + // so that arrows stuck in ground and dug up can hurt the player + return false; + } + + cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); + + // Instead of colliding the bounding box with another bounding box in motion, we collide an enlarged bounding box with a hairline. + // The results should be good enough for our purposes + double LineCoeff; + char Face; + EntBox.Expand(m_Projectile->GetWidth() / 2, m_Projectile->GetHeight() / 2, m_Projectile->GetWidth() / 2); + if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face)) + { + // No intersection whatsoever + return false; + } + + // TODO: Some entities don't interact with the projectiles (pickups, falling blocks) + // TODO: Allow plugins to interfere about which entities can be hit + + if (LineCoeff < m_MinCoeff) + { + // The entity is closer than anything we've stored so far, replace it as the potential victim + m_MinCoeff = LineCoeff; + m_HitEntity = a_Entity; + } + + // Don't break the enumeration, we want all the entities + return false; + } + + /// Returns the nearest entity that was hit, after the enumeration has been completed + cEntity * GetHitEntity(void) const { return m_HitEntity; } + + /// Returns the line coeff where the hit was encountered, after the enumeration has been completed + double GetMinCoeff(void) const { return m_MinCoeff; } + + /// Returns true if the callback has encountered a true hit + bool HasHit(void) const { return (m_MinCoeff < 1); } + +protected: + cProjectileEntity * m_Projectile; + const Vector3d & m_Pos; + const Vector3d & m_NextPos; + double m_MinCoeff; // The coefficient of the nearest hit on the Pos line + + // Although it's bad(tm) to store entity ptrs from a callback, we can afford it here, because the entire callback + // is processed inside the tick thread, so the entities won't be removed in between the calls and the final processing + cEntity * m_HitEntity; // The nearest hit entity +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProjectileEntity: + +cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) : + super(etProjectile, a_X, a_Y, a_Z, a_Width, a_Height), + m_ProjectileKind(a_Kind), + m_Creator(a_Creator), + m_IsInGround(false) +{ +} + + + + + +cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) : + super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height), + m_ProjectileKind(a_Kind), + m_Creator(a_Creator), + m_IsInGround(false) +{ + SetSpeed(a_Speed); + SetRotationFromSpeed(); + SetPitchFromSpeed(); +} + + + + + +cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed) +{ + Vector3d Speed; + if (a_Speed != NULL) + { + Speed = *a_Speed; + } + + switch (a_Kind) + { + case pkArrow: return new cArrowEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkEgg: return new cThrownEggEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkEnderPearl: return new cThrownEnderPearlEntity(a_Creator, a_X, a_Y, a_Z, Speed); + case pkSnowball: return new cThrownSnowballEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed); + // TODO: the rest + } + + LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind); + return NULL; +} + + + + + +void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + // Set the position based on what face was hit: + SetPosition(a_HitPos); + SetSpeed(0, 0, 0); + + // DEBUG: + LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, hit solid block at face %d", + m_UniqueID, + a_HitPos.x, a_HitPos.y, a_HitPos.z, + a_HitFace + ); + + m_IsInGround = true; +} + + + + + +AString cProjectileEntity::GetMCAClassName(void) const +{ + switch (m_ProjectileKind) + { + case pkArrow: return "Arrow"; + case pkSnowball: return "Snowball"; + case pkEgg: return "Egg"; + case pkGhastFireball: return "Fireball"; + case pkFireCharge: return "SmallFireball"; + case pkEnderPearl: return "ThrownEnderPearl"; + case pkExpBottle: return "ThrownExpBottle"; + case pkSplashPotion: return "ThrownPotion"; + case pkWitherSkull: return "WitherSkull"; + case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this? + } + ASSERT(!"Unhandled projectile entity kind!"); + return ""; +} + + + + + +void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + BroadcastMovementUpdate(); +} + + + + + +void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + if (m_IsInGround) + { + // Already-grounded projectiles don't move at all + return; + } + + Vector3d PerTickSpeed = GetSpeed() / 20; + Vector3d Pos = GetPosition(); + + // Trace the tick's worth of movement as a line: + Vector3d NextPos = Pos + PerTickSpeed; + cProjectileTracerCallback TracerCallback(this); + if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) + { + // Something has been hit, abort all other processing + return; + } + // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff + + // Test for entity collisions: + cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos); + a_Chunk.ForEachEntity(EntityCollisionCallback); + if (EntityCollisionCallback.HasHit()) + { + // An entity was hit: + Vector3d HitPos = Pos + (NextPos - Pos) * EntityCollisionCallback.GetMinCoeff(); + + // DEBUG: + LOGD("Projectile %d has hit an entity %d (%s) at {%.02f, %.02f, %.02f} (coeff %.03f)", + m_UniqueID, + EntityCollisionCallback.GetHitEntity()->GetUniqueID(), + EntityCollisionCallback.GetHitEntity()->GetClass(), + HitPos.x, HitPos.y, HitPos.z, + EntityCollisionCallback.GetMinCoeff() + ); + + OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos); + } + // TODO: Test the entities in the neighboring chunks, too + + // Update the position: + SetPosition(NextPos); + + // Add slowdown and gravity effect to the speed: + Vector3d NewSpeed(GetSpeed()); + NewSpeed.y += m_Gravity / 20; + NewSpeed *= TracerCallback.GetSlowdownCoeff(); + SetSpeed(NewSpeed); + SetRotationFromSpeed(); + SetPitchFromSpeed(); + + // DEBUG: + LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}", + m_UniqueID, + GetPosX(), GetPosY(), GetPosZ(), + GetSpeedX(), GetSpeedY(), GetSpeedZ(), + GetRotation(), GetPitch() + ); +} + + + + + +void cProjectileEntity::SpawnOn(cClientHandle & a_Client) +{ + // Default spawning - use the projectile kind to spawn an object: + a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetRotation()), ANGLE_TO_PROTO(GetPitch())); + a_Client.SendEntityMetadata(*this); +} + + + + + +void cProjectileEntity::CollectedBy(cPlayer * a_Dest) +{ + // Overriden in arrow + UNUSED(a_Dest); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cArrowEntity: + +cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5), + m_PickupState(psNoPickup), + m_DamageCoeff(2), + m_IsCritical(false), + m_Timer(0), + m_bIsCollected(false), + m_HitBlockPos(Vector3i(0, 0, 0)) +{ + SetSpeed(a_Speed); + SetMass(0.1); + SetRotationFromSpeed(); + SetPitchFromSpeed(); + LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", + m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), + GetRotation(), GetPitch() + ); +} + + + + + +cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) : + super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5), + m_PickupState(psInSurvivalOrCreative), + m_DamageCoeff(2), + m_IsCritical((a_Force >= 1)), + m_Timer(0), + m_bIsCollected(false), + m_HitBlockPos(0, 0, 0) +{ +} + + + + + +bool cArrowEntity::CanPickup(const cPlayer & a_Player) const +{ + switch (m_PickupState) + { + case psNoPickup: return false; + case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative()); + case psInCreative: return a_Player.IsGameModeCreative(); + } + ASSERT(!"Unhandled pickup state"); + return false; +} + + + + + +void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + if (a_HitFace == BLOCK_FACE_NONE) + { + return; + } + + super::OnHitSolidBlock(a_HitPos, a_HitFace); + int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z; + + if (a_HitFace != BLOCK_FACE_YP) + { + AddFaceDirection(a_X, a_Y, a_Z, a_HitFace); + } + else if (a_HitFace == BLOCK_FACE_YP) // These conditions because xoft got a little confused on block face directions, so AddFace works with all but YP & YM + { + a_Y--; + } + else + { + a_Y++; + } + + m_HitBlockPos = Vector3i(a_X, a_Y, a_Z); + + // Broadcast arrow hit sound + m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + + // Broadcast the position and speed packets before teleporting: + BroadcastMovementUpdate(); + + // Teleport the entity to the exact hit coords: + m_World->BroadcastTeleportEntity(*this); +} + + + + + +void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat()) + { + // Not an entity that interacts with an arrow + return; + } + + int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5); + if (m_IsCritical) + { + Damage += m_World->GetTickRandomNumber(Damage / 2 + 2); + } + a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1); + + // Broadcast successful hit sound + m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + + Destroy(); +} + + + + + +void cArrowEntity::CollectedBy(cPlayer * a_Dest) +{ + if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest))) + { + int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW); + if (NumAdded > 0) // Only play effects if there was space in inventory + { + m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest); + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_bIsCollected = true; + } + } +} + + + + + +void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + m_Timer += a_Dt; + + if (m_bIsCollected) + { + if (m_Timer > 500.f) // 0.5 seconds + { + Destroy(); + return; + } + } + else if (m_Timer > 1000 * 60 * 5) // 5 minutes + { + Destroy(); + return; + } + + if (m_IsInGround) + { + int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width; + int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); + + if (Chunk == NULL) + { + // Inside an unloaded chunk, abort + return; + } + + if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed? + { + m_IsInGround = false; // Yes, begin simulating physics again + } + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cThrownEggEntity: + +cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + if (m_World->GetTickRandomNumber(7) == 1) + { + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + } + else if (m_World->GetTickRandomNumber(32) == 1) + { + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + } + Destroy(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cThrownEnderPearlEntity : + +cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + // Teleport the creator here, make them take 5 damage: + if (m_Creator != NULL) + { + // TODO: The coords might need some tweaking based on the block face + m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5); + m_Creator->TakeDamage(dtEnderPearl, this, 5, 0); + } + + Destroy(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cThrownSnowballEntity : + +cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + // TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs + + Destroy(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cGhastFireballEntity : + +cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1) +{ + SetSpeed(a_Speed); + SetGravity(0); +} + + + + + +void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this); +} + + + + + +void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); +} + + + + + +void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFireChargeEntity : + +cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125) +{ + SetSpeed(a_Speed); + SetGravity(0); +} + + + + + +void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) + { + m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1); + } +} + + + + + +void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); +} + + + + + +void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); + + // TODO: Some entities are immune to hits + a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning +} + + + + diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h new file mode 100644 index 000000000..28dd76935 --- /dev/null +++ b/src/Entities/ProjectileEntity.h @@ -0,0 +1,325 @@ + +// ProjectileEntity.h + +// Declares the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types + + + + + +#pragma once + +#include "Entity.h" + + + + + +// tolua_begin + +class cProjectileEntity : + public cEntity +{ + typedef cEntity super; + +public: + /// The kind of the projectile. The numbers correspond to the network type ID used for spawning via the 0x17 packet. + enum eKind + { + pkArrow = 60, + pkSnowball = 61, + pkEgg = 62, + pkGhastFireball = 63, + pkFireCharge = 64, + pkEnderPearl = 65, + pkExpBottle = 75, + pkSplashPotion = 73, + pkWitherSkull = 66, + pkFishingFloat = 90, + } ; + + // tolua_end + + CLASS_PROTODEF(cProjectileEntity); + + cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); + cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height); + + static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL); + + /// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace); + + /// Called by the physics blocktracer when the entity hits another entity + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) {} + + /// Called by Chunk when the projectile is eligible for player collection + virtual void CollectedBy(cPlayer * a_Dest); + + // tolua_begin + + /// Returns the kind of the projectile (fast class identification) + eKind GetProjectileKind(void) const { return m_ProjectileKind; } + + /// Returns the entity who created this projectile; may be NULL + cEntity * GetCreator(void) { return m_Creator; } + + /// Returns the string that is used as the entity type (class name) in MCA files + AString GetMCAClassName(void) const; + + /// Returns true if the projectile has hit the ground and is stuck there + bool IsInGround(void) const { return m_IsInGround; } + + // tolua_end + + /// Sets the internal InGround flag. To be used by MCA loader only! + void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; } + +protected: + eKind m_ProjectileKind; + + /// The entity who has created this projectile; may be NULL (e. g. for dispensers) + cEntity * m_Creator; + + /// True if the projectile has hit the ground and is stuck there + bool m_IsInGround; + + // cEntity overrides: + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void SpawnOn(cClientHandle & a_Client) override; + + // tolua_begin +} ; + + + + + +class cArrowEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + /// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field + enum ePickupState + { + psNoPickup = 0, + psInSurvivalOrCreative = 1, + psInCreative = 2, + } ; + + // tolua_end + + CLASS_PROTODEF(cArrowEntity); + + /// Creates a new arrow with psNoPickup state and default damage modifier coeff + cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + + /// Creates a new arrow as shot by a player, initializes it from the player object + cArrowEntity(cPlayer & a_Player, double a_Force); + + // tolua_begin + + /// Returns whether the arrow can be picked up by players + ePickupState GetPickupState(void) const { return m_PickupState; } + + /// Sets a new pickup state + void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; } + + /// Returns the damage modifier coeff. + double GetDamageCoeff(void) const { return m_DamageCoeff; } + + /// Sets the damage modifier coeff + void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; } + + /// Returns true if the specified player can pick the arrow up + bool CanPickup(const cPlayer & a_Player) const; + + /// Returns true if the arrow is set as critical + bool IsCritical(void) const { return m_IsCritical; } + + /// Sets the IsCritical flag + void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; } + + // tolua_end + +protected: + + /// Determines when the arrow can be picked up by players + ePickupState m_PickupState; + + /// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow + double m_DamageCoeff; + + /// If true, the arrow deals more damage + bool m_IsCritical; + + /// Timer for pickup collection animation or five minute timeout + float m_Timer; + + /// If true, the arrow is in the process of being collected - don't go to anyone else + bool m_bIsCollected; + + /// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air + Vector3i m_HitBlockPos; + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + virtual void CollectedBy(cPlayer * a_Player) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + // tolua_begin +} ; + + + + + +class cThrownEggEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cThrownEggEntity); + + cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // tolua_end + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + + // tolua_begin + +} ; + + + + + +class cThrownEnderPearlEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cThrownEnderPearlEntity); + + cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // tolua_end + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + + // tolua_begin + +} ; + + + + + +class cThrownSnowballEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cThrownSnowballEntity); + + cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + + // tolua_begin + +} ; + + + + + +class cGhastFireballEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cGhastFireballEntity); + + cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + + // TODO: Deflecting the fireballs by arrow- or sword- hits + + // tolua_begin + +} ; + + + + + +class cFireChargeEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cFireChargeEntity); + + cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + + // tolua_begin + +} ; + + + + +// tolua_end + + + diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp new file mode 100644 index 000000000..339107b2e --- /dev/null +++ b/src/Entities/TNTEntity.cpp @@ -0,0 +1,62 @@ +#include "Globals.h" + +#include "TNTEntity.h" +#include "../World.h" +#include "../ClientHandle.h" + + + + + +cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec) : + super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98), + m_Counter(0), + m_MaxFuseTime(a_FuseTimeInSec) +{ +} + + + + + +cTNTEntity::cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec) : + super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), + m_Counter(0), + m_MaxFuseTime(a_FuseTimeInSec) +{ +} + + + + +void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle) +{ + a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT + m_bDirtyPosition = false; + m_bDirtySpeed = false; + m_bDirtyOrientation = false; + m_bDirtyHead = false; +} + + + + + +void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + BroadcastMovementUpdate(); + float delta_time = a_Dt / 1000; // Convert miliseconds to seconds + m_Counter += delta_time; + if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM + { + Destroy(true); + LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ()); + m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this); + return; + } +} + + + + diff --git a/src/Entities/TNTEntity.h b/src/Entities/TNTEntity.h new file mode 100644 index 000000000..eb5040e8a --- /dev/null +++ b/src/Entities/TNTEntity.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "Entity.h" + + + + + +class cTNTEntity : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cTNTEntity); + + cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec); + cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec); + + // cEntity overrides: + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +protected: + double m_Counter; ///< How much time has elapsed since the object was created, in seconds + double m_MaxFuseTime; ///< How long the fuse is, in seconds +}; + + + + diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp new file mode 100644 index 000000000..887e4426d --- /dev/null +++ b/src/FastRandom.cpp @@ -0,0 +1,174 @@ + +// FastRandom.cpp + +// Implements the cFastRandom class representing a fast random number generator + +#include "Globals.h" +#include +#include "FastRandom.h" + + + + + +#if 0 && defined(_DEBUG) +// Self-test +// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs, +// and if it performs well in terms of distribution (checked by avg, expected to be in the range midpoint +class cFastRandomTest +{ +public: + cFastRandomTest(void) + { + TestInts(); + TestFloats(); + } + + + void TestInts(void) + { + printf("Testing ints...\n"); + cFastRandom rnd; + int sum = 0; + const int BUCKETS = 8; + int Counts[BUCKETS]; + memset(Counts, 0, sizeof(Counts)); + const int ITER = 10000; + for (int i = 0; i < ITER; i++) + { + int v = rnd.NextInt(1000); + ASSERT(v >= 0); + ASSERT(v < 1000); + Counts[v % BUCKETS]++; + sum += v; + } + double avg = (double)sum / ITER; + printf("avg: %f\n", avg); + for (int i = 0; i < BUCKETS; i++) + { + printf(" bucket %d: %d\n", i, Counts[i]); + } + } + + + void TestFloats(void) + { + printf("Testing floats...\n"); + cFastRandom rnd; + float sum = 0; + const int BUCKETS = 8; + int Counts[BUCKETS]; + memset(Counts, 0, sizeof(Counts)); + const int ITER = 10000; + for (int i = 0; i < ITER; i++) + { + float v = rnd.NextFloat(1000); + ASSERT(v >= 0); + ASSERT(v <= 1000); + Counts[((int)v) % BUCKETS]++; + sum += v; + } + sum = sum / ITER; + printf("avg: %f\n", sum); + for (int i = 0; i < BUCKETS; i++) + { + printf(" bucket %d: %d\n", i, Counts[i]); + } + } +} g_Test; + +#endif + + + + + + +int cFastRandom::m_SeedCounter = 0; + + + + + +cFastRandom::cFastRandom(void) : + m_Seed(m_SeedCounter++) +{ +} + + + + + +int cFastRandom::NextInt(int a_Range) +{ + ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges + ASSERT(a_Range > 0); + + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + return ((n / 11) % a_Range); +} + + + + + +int cFastRandom::NextInt(int a_Range, int a_Salt) +{ + ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges + ASSERT(a_Range > 0); + + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + return ((n / 11) % a_Range); +} + + + + + +float cFastRandom::NextFloat(float a_Range) +{ + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + + // Convert the integer into float with the specified range: + return (((float)n / (float)0x7fffffff) * a_Range); +} + + + + + +float cFastRandom::NextFloat(float a_Range, int a_Salt) +{ + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + + // Convert the integer into float with the specified range: + return (((float)n / (float)0x7fffffff) * a_Range); +} + + + + diff --git a/src/FastRandom.h b/src/FastRandom.h new file mode 100644 index 000000000..bf70822cf --- /dev/null +++ b/src/FastRandom.h @@ -0,0 +1,57 @@ + +// FastRandom.h + +// Declares the cFastRandom class representing a fast random number generator + +/* +The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator. +It is fast to instantiate, fast to query next, and partially multi-thread-safe. +It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may +yield duplicate numbers in that case. + +Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is +taken from a global counter and the random is calculated using a counter that is incremented on each use (hence +the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter, +and another that takes an additional "salt" parameter; this salt is used as an additional input to the random, +in order to avoid multi-thread duplication. If two threads both use the class at the same time with different +salts, the values they get will be different. +*/ + + + + + +#pragma once + + + + + +class cFastRandom +{ +public: + cFastRandom(void); + + /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M + int NextInt(int a_Range); + + /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness + int NextInt(int a_Range, int a_Salt); + + /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M + float NextFloat(float a_Range); + + /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness + float NextFloat(float a_Range, int a_Salt); + +protected: + int m_Seed; + int m_Counter; + + /// Counter that is used to initialize the seed, incremented for each object created + static int m_SeedCounter; +} ; + + + + diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp new file mode 100644 index 000000000..2e2276981 --- /dev/null +++ b/src/FurnaceRecipe.cpp @@ -0,0 +1,255 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FurnaceRecipe.h" +#include "Item.h" + +#include +#include + + + + + +typedef std::list< cFurnaceRecipe::Recipe > RecipeList; +typedef std::list< cFurnaceRecipe::Fuel > FuelList; + + + + + +struct cFurnaceRecipe::sFurnaceRecipeState +{ + RecipeList Recipes; + FuelList Fuel; +}; + + + + + +cFurnaceRecipe::cFurnaceRecipe() + : m_pState( new sFurnaceRecipeState ) +{ + ReloadRecipes(); +} + + + + + +cFurnaceRecipe::~cFurnaceRecipe() +{ + ClearRecipes(); + delete m_pState; +} + + + + + +void cFurnaceRecipe::ReloadRecipes(void) +{ + ClearRecipes(); + LOGD("Loading furnace recipes..."); + + std::ifstream f; + char a_File[] = "furnace.txt"; + f.open(a_File, std::ios::in); + std::string input; + + if (!f.good()) + { + f.close(); + LOG("Could not open the furnace recipes file \"%s\"", a_File); + return; + } + + // TODO: Replace this messy parse with a high-level-structured one (ReadLine / ProcessLine) + bool bSyntaxError = false; + while (f.good()) + { + char c; + + ////////////////////////////////////////////////////////////////////////// + // comments + f >> c; + f.unget(); + if( c == '#' ) + { + while( f.good() && c != '\n' ) + { + f.get( c ); + } + continue; + } + + + ////////////////////////////////////////////////////////////////////////// + // Line breaks + f.get( c ); + while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); } + if (f.eof()) + { + break; + } + f.unget(); + + ////////////////////////////////////////////////////////////////////////// + // Check for fuel + f >> c; + if( c == '!' ) // It's fuel :) + { + // Read item + int IItemID = 0, IItemCount = 0, IItemHealth = 0; + f >> IItemID; + f >> c; if( c != ':' ) { bSyntaxError = true; break; } + f >> IItemCount; + + // Optional health + f >> c; + if( c != ':' ) + f.unget(); + else + { + f >> IItemHealth; + } + + // Burn time + int BurnTime; + f >> c; if( c != '=' ) { bSyntaxError = true; break; } + f >> BurnTime; + + // Add to fuel list + Fuel F; + F.In = new cItem( (ENUM_ITEM_ID) IItemID, (char)IItemCount, (short)IItemHealth ); + F.BurnTime = BurnTime; + m_pState->Fuel.push_back( F ); + continue; + } + f.unget(); + + ////////////////////////////////////////////////////////////////////////// + // Read items + int IItemID = 0, IItemCount = 0, IItemHealth = 0; + f >> IItemID; + f >> c; if( c != ':' ) { bSyntaxError = true; break; } + f >> IItemCount; + + // Optional health + f >> c; + if( c != ':' ) + f.unget(); + else + { + f >> IItemHealth; + } + + int CookTime; + f >> c; if( c != '@' ) { bSyntaxError = true; break; } + f >> CookTime; + + int OItemID = 0, OItemCount = 0, OItemHealth = 0; + f >> c; if( c != '=' ) { bSyntaxError = true; break; } + f >> OItemID; + f >> c; if( c != ':' ) { bSyntaxError = true; break; } + f >> OItemCount; + + // Optional health + f >> c; + if( c != ':' ) + f.unget(); + else + { + f >> OItemHealth; + } + + // Add to recipe list + Recipe R; + R.In = new cItem( (ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth ); + R.Out = new cItem( (ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth ); + R.CookTime = CookTime; + m_pState->Recipes.push_back( R ); + } + if (bSyntaxError) + { + LOGERROR("ERROR: FurnaceRecipe, syntax error" ); + } + LOG("Loaded %u furnace recipes and %u fuels", m_pState->Recipes.size(), m_pState->Fuel.size()); +} + + + + + +void cFurnaceRecipe::ClearRecipes(void) +{ + for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr) + { + Recipe R = *itr; + delete R.In; + delete R.Out; + } + m_pState->Recipes.clear(); + + for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) + { + Fuel F = *itr; + delete F.In; + } + m_pState->Fuel.clear(); +} + + + + + +const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const +{ + const Recipe * BestRecipe = 0; + for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr) + { + const Recipe & R = *itr; + if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount)) + { + if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount)) + { + continue; + } + else + { + BestRecipe = &R; + } + } + } + return BestRecipe; +} + + + + + +int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const +{ + int BestFuel = 0; + for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) + { + const Fuel & F = *itr; + if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount)) + { + if (BestFuel > 0 && (BestFuel > F.BurnTime)) + { + continue; + } + else + { + BestFuel = F.BurnTime; + } + } + } + return BestFuel; +} + + + + diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h new file mode 100644 index 000000000..2f91e9bcb --- /dev/null +++ b/src/FurnaceRecipe.h @@ -0,0 +1,50 @@ + +#pragma once + + + + + +class cItem; + + + + + +class cFurnaceRecipe +{ +public: + cFurnaceRecipe(void); + ~cFurnaceRecipe(); + + void ReloadRecipes(void); + + struct Fuel + { + cItem * In; + int BurnTime; ///< How long this fuel burns, in ticks + }; + + struct Recipe + { + cItem * In; + cItem * Out; + int CookTime; ///< How long this recipe takes to smelt, in ticks + }; + + /// Returns a recipe for the specified input, NULL if no recipe found + const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const; + + /// Returns the amount of time that the specified fuel burns, in ticks + int GetBurnTime(const cItem & a_Fuel) const; + +private: + void ClearRecipes(void); + + struct sFurnaceRecipeState; + sFurnaceRecipeState * m_pState; +}; + + + + diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp new file mode 100644 index 000000000..926120afc --- /dev/null +++ b/src/Generating/BioGen.cpp @@ -0,0 +1,707 @@ + +// BioGen.cpp + +// Implements the various biome generators + +#include "Globals.h" +#include "BioGen.h" +#include "../../iniFile/iniFile.h" +#include "../LinearUpscale.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenConstant: + +void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) + { + a_BiomeMap[i] = m_Biome; + } +} + + + + + +void cBioGenConstant::InitializeBiomeGen(cIniFile & a_IniFile) +{ + AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "Plains"); + m_Biome = StringToBiome(Biome); + if (m_Biome == -1) + { + LOGWARN("[Generator]::ConstantBiome value \"%s\" not recognized, using \"Plains\".", Biome.c_str()); + m_Biome = biPlains; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenCache: + +cBioGenCache::cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize) : + m_BioGenToCache(a_BioGenToCache), + m_CacheSize(a_CacheSize), + m_CacheOrder(new int[a_CacheSize]), + m_CacheData(new sCacheData[a_CacheSize]), + m_NumHits(0), + m_NumMisses(0), + m_TotalChain(0) +{ + for (int i = 0; i < m_CacheSize; i++) + { + m_CacheOrder[i] = i; + m_CacheData[i].m_ChunkX = 0x7fffffff; + m_CacheData[i].m_ChunkZ = 0x7fffffff; + } +} + + + + + +cBioGenCache::~cBioGenCache() +{ + delete[] m_CacheData; + delete[] m_CacheOrder; +} + + + + + +void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + if (((m_NumHits + m_NumMisses) % 1024) == 10) + { + LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); + LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); + } + + for (int i = 0; i < m_CacheSize; i++) + { + if ( + (m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) || + (m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ) + ) + { + continue; + } + // Found it in the cache + int Idx = m_CacheOrder[i]; + + // Move to front: + for (int j = i; j > 0; j--) + { + m_CacheOrder[j] = m_CacheOrder[j - 1]; + } + m_CacheOrder[0] = Idx; + + // Use the cached data: + memcpy(a_BiomeMap, m_CacheData[Idx].m_BiomeMap, sizeof(a_BiomeMap)); + + m_NumHits++; + m_TotalChain += i; + return; + } // for i - cache + + // Not in the cache: + m_NumMisses++; + m_BioGenToCache->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); + + // Insert it as the first item in the MRU order: + int Idx = m_CacheOrder[m_CacheSize - 1]; + for (int i = m_CacheSize - 1; i > 0; i--) + { + m_CacheOrder[i] = m_CacheOrder[i - 1]; + } // for i - m_CacheOrder[] + m_CacheOrder[0] = Idx; + memcpy(m_CacheData[Idx].m_BiomeMap, a_BiomeMap, sizeof(a_BiomeMap)); + m_CacheData[Idx].m_ChunkX = a_ChunkX; + m_CacheData[Idx].m_ChunkZ = a_ChunkZ; +} + + + + + +void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile) +{ + super::InitializeBiomeGen(a_IniFile); + m_BioGenToCache->InitializeBiomeGen(a_IniFile); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBiomeGenList: + +void cBiomeGenList::InitializeBiomes(const AString & a_Biomes) +{ + AStringVector Split = StringSplit(a_Biomes, ","); + + // Convert each string in the list into biome: + for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr) + { + AStringVector Split2 = StringSplit(*itr, ":"); + if (Split2.size() < 1) + { + continue; + } + int Count = 1; + if (Split2.size() >= 2) + { + Count = atol(Split2[1].c_str()); + if (Count <= 0) + { + LOGWARNING("Cannot decode biome count: \"%s\"; using 1.", Split2[1].c_str()); + Count = 1; + } + } + EMCSBiome Biome = StringToBiome(Split2[0]); + if (Biome != -1) + { + for (int i = 0; i < Count; i++) + { + m_Biomes.push_back(Biome); + } + } + else + { + LOGWARNING("Cannot decode biome name: \"%s\"; skipping", Split2[0].c_str()); + } + } // for itr - Split[] + if (!m_Biomes.empty()) + { + m_BiomesCount = (int)m_Biomes.size(); + return; + } + + // There were no biomes, add default biomes: + static EMCSBiome Biomes[] = + { + biOcean, + biPlains, + biDesert, + biExtremeHills, + biForest, + biTaiga, + biSwampland, + biRiver, + biFrozenOcean, + biFrozenRiver, + biIcePlains, + biIceMountains, + biMushroomIsland, + biMushroomShore, + biBeach, + biDesertHills, + biForestHills, + biTaigaHills, + biExtremeHillsEdge, + biJungle, + biJungleHills, + } ; + m_Biomes.reserve(ARRAYCOUNT(Biomes)); + for (int i = 0; i < ARRAYCOUNT(Biomes); i++) + { + m_Biomes.push_back(Biomes[i]); + } + m_BiomesCount = (int)m_Biomes.size(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenCheckerboard: + +void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + int Base = cChunkDef::Width * a_ChunkZ + z; + for (int x = 0; x < cChunkDef::Width; x++) + { + int Add = cChunkDef::Width * a_ChunkX + x; + a_BiomeMap[x + cChunkDef::Width * z] = m_Biomes[(Base / m_BiomeSize + Add / m_BiomeSize) % m_BiomesCount]; + } + } +} + + + + + +void cBioGenCheckerboard::InitializeBiomeGen(cIniFile & a_IniFile) +{ + super::InitializeBiomeGen(a_IniFile); + AString Biomes = a_IniFile.GetValueSet ("Generator", "CheckerBoardBiomes", ""); + m_BiomeSize = a_IniFile.GetValueSetI("Generator", "CheckerboardBiomeSize", 64); + m_BiomeSize = (m_BiomeSize < 8) ? 8 : m_BiomeSize; + InitializeBiomes(Biomes); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenVoronoi : + +void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + int BaseZ = cChunkDef::Width * a_ChunkZ; + int BaseX = cChunkDef::Width * a_ChunkX; + for (int z = 0; z < cChunkDef::Width; z++) + { + int AbsoluteZ = BaseZ + z; + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(BaseX + x, AbsoluteZ)); + } // for x + } // for z +} + + + + + +void cBioGenVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) +{ + super::InitializeBiomeGen(a_IniFile); + m_CellSize = a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64); + AString Biomes = a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", ""); + InitializeBiomes(Biomes); +} + + + + + +EMCSBiome cBioGenVoronoi::VoronoiBiome(int a_BlockX, int a_BlockZ) +{ + int CellX = a_BlockX / m_CellSize; + int CellZ = a_BlockZ / m_CellSize; + + // Note that Noise values need to be divided by 8 to gain a uniform modulo-2^n distribution + + // Get 5x5 neighboring cell seeds, compare distance to each. Return the biome in the minumim-distance cell + int MinDist = m_CellSize * m_CellSize * 16; // There has to be a cell closer than this + EMCSBiome res = biPlains; // Will be overriden + for (int x = CellX - 2; x <= CellX + 2; x++) + { + int BaseX = x * m_CellSize; + for (int z = CellZ - 2; z < CellZ + 2; z++) + { + int OffsetX = (m_Noise.IntNoise3DInt(x, 16 * x + 32 * z, z) / 8) % m_CellSize; + int OffsetZ = (m_Noise.IntNoise3DInt(x, 32 * x - 16 * z, z) / 8) % m_CellSize; + int SeedX = BaseX + OffsetX; + int SeedZ = z * m_CellSize + OffsetZ; + + int Dist = (SeedX - a_BlockX) * (SeedX - a_BlockX) + (SeedZ - a_BlockZ) * (SeedZ - a_BlockZ); + if (Dist < MinDist) + { + MinDist = Dist; + res = m_Biomes[(m_Noise.IntNoise3DInt(x, x - z + 1000, z) / 8) % m_BiomesCount]; + } + } // for z + } // for x + + return res; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenDistortedVoronoi: + +void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + int BaseZ = cChunkDef::Width * a_ChunkZ; + int BaseX = cChunkDef::Width * a_ChunkX; + + // Distortions for linear interpolation: + int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1]; + int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1]; + for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++) + { + Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z]); + } + + LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + + for (int z = 0; z < cChunkDef::Width; z++) + { + int AbsoluteZ = BaseZ + z; + for (int x = 0; x < cChunkDef::Width; x++) + { + // Distort(BaseX + x, AbsoluteZ, DistX, DistZ); + cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(DistortX[x][z], DistortZ[x][z])); + } // for x + } // for z +} + + + + + +void cBioGenDistortedVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) +{ + // Do NOT call super::InitializeBiomeGen(), as it would try to read Voronoi params instead of DistortedVoronoi params + m_CellSize = a_IniFile.GetValueSetI("Generator", "DistortedVoronoiCellSize", 96); + AString Biomes = a_IniFile.GetValueSet ("Generator", "DistortedVoronoiBiomes", ""); + InitializeBiomes(Biomes); +} + + + + +void cBioGenDistortedVoronoi::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ) +{ + double NoiseX = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 1000); + NoiseX += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 2000); + NoiseX += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 3000); + double NoiseZ = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 4000); + NoiseZ += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 5000); + NoiseZ += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 6000); + + a_DistortedX = a_BlockX + (int)(m_CellSize * 0.5 * NoiseX); + a_DistortedZ = a_BlockZ + (int)(m_CellSize * 0.5 * NoiseZ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenMultiStepMap : + +cBioGenMultiStepMap::cBioGenMultiStepMap(int a_Seed) : + m_Noise1(a_Seed + 1000), + m_Noise2(a_Seed + 2000), + m_Noise3(a_Seed + 3000), + m_Noise4(a_Seed + 4000), + m_Noise5(a_Seed + 5000), + m_Noise6(a_Seed + 6000), + m_Seed(a_Seed), + m_OceanCellSize(384), + m_MushroomIslandSize(64), + m_RiverCellSize(384), + m_RiverWidthThreshold(0.125), + m_LandBiomesSize(1024) +{ +} + + + + + +void cBioGenMultiStepMap::InitializeBiomeGen(cIniFile & a_IniFile) +{ + m_OceanCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapOceanCellSize", m_OceanCellSize); + m_MushroomIslandSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapMushroomIslandSize", m_MushroomIslandSize); + m_RiverCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapRiverCellSize", m_RiverCellSize); + m_RiverWidthThreshold = a_IniFile.GetValueSetF("Generator", "MultiStepMapRiverWidth", m_RiverWidthThreshold); + m_LandBiomesSize = (float)a_IniFile.GetValueSetI("Generator", "MultiStepMapLandBiomeSize", (int)m_LandBiomesSize); +} + + + + + +void cBioGenMultiStepMap::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + DecideOceanLandMushroom(a_ChunkX, a_ChunkZ, a_BiomeMap); + AddRivers(a_ChunkX, a_ChunkZ, a_BiomeMap); + ApplyTemperatureHumidity(a_ChunkX, a_ChunkZ, a_BiomeMap); +} + + + + + +void cBioGenMultiStepMap::DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + // Distorted Voronoi over 3 biomes, with mushroom having only a special occurence. + + // Prepare a distortion lookup table, by distorting a 5x5 area and using that as 1:4 zoom (linear interpolate): + int BaseZ = cChunkDef::Width * a_ChunkZ; + int BaseX = cChunkDef::Width * a_ChunkX; + int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1]; + int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1]; + int DistortSize = m_OceanCellSize / 2; + for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++) + { + Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z], DistortSize); + } + LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + + // Prepare a 9x9 area of neighboring cell seeds + // (assuming that 7x7 cell area is larger than a chunk being generated) + const int NEIGHBORHOOD_SIZE = 4; // How many seeds in each direction to check + int CellX = BaseX / m_OceanCellSize; + int CellZ = BaseZ / m_OceanCellSize; + int SeedX[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1]; + int SeedZ[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1]; + EMCSBiome SeedV[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1]; + for (int xc = 0; xc < 2 * NEIGHBORHOOD_SIZE + 1; xc++) + { + int RealCellX = xc + CellX - NEIGHBORHOOD_SIZE; + int CellBlockX = RealCellX * m_OceanCellSize; + for (int zc = 0; zc < 2 * NEIGHBORHOOD_SIZE + 1; zc++) + { + int RealCellZ = zc + CellZ - NEIGHBORHOOD_SIZE; + int CellBlockZ = RealCellZ * m_OceanCellSize; + int OffsetX = (m_Noise2.IntNoise3DInt(RealCellX, 16 * RealCellX + 32 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize; + int OffsetZ = (m_Noise4.IntNoise3DInt(RealCellX, 32 * RealCellX - 16 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize; + SeedX[xc][zc] = CellBlockX + OffsetX; + SeedZ[xc][zc] = CellBlockZ + OffsetZ; + SeedV[xc][zc] = (((m_Noise6.IntNoise3DInt(RealCellX, RealCellX - RealCellZ + 1000, RealCellZ) / 11) % 256) > 90) ? biOcean : ((EMCSBiome)(-1)); + } // for z + } // for x + + for (int xc = 1; xc < 2 * NEIGHBORHOOD_SIZE; xc++) for (int zc = 1; zc < 2 * NEIGHBORHOOD_SIZE; zc++) + { + if ( + (SeedV[xc ][zc] == biOcean) && + (SeedV[xc - 1][zc] == biOcean) && + (SeedV[xc + 1][zc] == biOcean) && + (SeedV[xc ][zc - 1] == biOcean) && + (SeedV[xc ][zc + 1] == biOcean) && + (SeedV[xc - 1][zc - 1] == biOcean) && + (SeedV[xc + 1][zc - 1] == biOcean) && + (SeedV[xc - 1][zc + 1] == biOcean) && + (SeedV[xc + 1][zc + 1] == biOcean) + ) + { + SeedV[xc][zc] = biMushroomIsland; + } + } + + // For each column find the nearest distorted cell and use its value as the biome: + int MushroomOceanThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 1024; + int MushroomShoreThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 2048; + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int AbsoluteZ = DistortZ[x][z]; + int AbsoluteX = DistortX[x][z]; + int MinDist = m_OceanCellSize * m_OceanCellSize * 16; // There has to be a cell closer than this + EMCSBiome Biome = biPlains; + // Find the nearest cell seed: + for (int xs = 1; xs < 2 * NEIGHBORHOOD_SIZE; xs++) for (int zs = 1; zs < 2 * NEIGHBORHOOD_SIZE; zs++) + { + int Dist = (SeedX[xs][zs] - AbsoluteX) * (SeedX[xs][zs] - AbsoluteX) + (SeedZ[xs][zs] - AbsoluteZ) * (SeedZ[xs][zs] - AbsoluteZ); + if (Dist >= MinDist) + { + continue; + } + MinDist = Dist; + Biome = SeedV[xs][zs]; + // Shrink mushroom biome and add a shore: + if (Biome == biMushroomIsland) + { + if (Dist > MushroomOceanThreshold) + { + Biome = biOcean; + } + else if (Dist > MushroomShoreThreshold) + { + Biome = biMushroomShore; + } + } + } // for zs, xs + + cChunkDef::SetBiome(a_BiomeMap, x, z, Biome); + } // for x + } // for z +} + + + + + +void cBioGenMultiStepMap::AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_RiverCellSize; + for (int x = 0; x < cChunkDef::Width; x++) + { + if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1) + { + // Biome already set, skip this column + continue; + } + + float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_RiverCellSize; + + double Noise = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ); + Noise += 0.5 * m_Noise3.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ); + Noise += 0.1 * m_Noise5.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ); + + if ((Noise > 0) && (Noise < m_RiverWidthThreshold)) + { + cChunkDef::SetBiome(a_BiomeMap, x, z, biRiver); + } + } // for x + } // for z +} + + + + + +void cBioGenMultiStepMap::ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + IntMap TemperatureMap; + IntMap HumidityMap; + BuildTemperatureHumidityMaps(a_ChunkX, a_ChunkZ, TemperatureMap, HumidityMap); + + FreezeWaterBiomes(a_BiomeMap, TemperatureMap); + DecideLandBiomes(a_BiomeMap, TemperatureMap, HumidityMap); +} + + + + + +void cBioGenMultiStepMap::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize) +{ + double NoiseX = m_Noise3.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize); + NoiseX += 0.5 * m_Noise2.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize); + NoiseX += 0.1 * m_Noise1.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize); + double NoiseZ = m_Noise6.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize); + NoiseZ += 0.5 * m_Noise5.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize); + NoiseZ += 0.1 * m_Noise4.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize); + + a_DistortedX = a_BlockX + (int)(a_CellSize * 0.5 * NoiseX); + a_DistortedZ = a_BlockZ + (int)(a_CellSize * 0.5 * NoiseZ); +} + + + + + +void cBioGenMultiStepMap::BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap) +{ + // Linear interpolation over 8x8 blocks; use double for better precision: + DblMap TemperatureMap; + DblMap HumidityMap; + for (int z = 0; z < 17; z += 8) + { + float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_LandBiomesSize; + for (int x = 0; x < 17; x += 8) + { + float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_LandBiomesSize; + + double NoiseT = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ); + NoiseT += 0.5 * m_Noise2.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ); + NoiseT += 0.1 * m_Noise3.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ); + TemperatureMap[x + 17 * z] = NoiseT; + + double NoiseH = m_Noise4.CubicNoise2D( NoiseCoordX, NoiseCoordZ); + NoiseH += 0.5 * m_Noise5.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ); + NoiseH += 0.1 * m_Noise6.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ); + HumidityMap[x + 17 * z] = NoiseH; + } // for x + } // for z + LinearUpscale2DArrayInPlace(TemperatureMap, 17, 17, 8, 8); + LinearUpscale2DArrayInPlace(HumidityMap, 17, 17, 8, 8); + + // Re-map into integral values in [0 .. 255] range: + for (int idx = 0; idx < ARRAYCOUNT(a_TemperatureMap); idx++) + { + a_TemperatureMap[idx] = std::max(0, std::min(255, (int)(128 + TemperatureMap[idx] * 128))); + a_HumidityMap[idx] = std::max(0, std::min(255, (int)(128 + HumidityMap[idx] * 128))); + } +} + + + + + +void cBioGenMultiStepMap::DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap) +{ + static const EMCSBiome BiomeMap[] = + { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + /* 0 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, + /* 1 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, + /* 2 */ biTundra, biTundra, biTundra, biTundra, biPlains, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert, + /* 3 */ biTundra, biTundra, biTundra, biTundra, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert, + /* 4 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert, + /* 5 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert, + /* 6 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, + /* 7 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, + /* 8 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, + /* 9 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, + /* 10 */ biTaiga, biTaiga, biTaiga, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 11 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 12 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 13 */ biTaiga, biTaiga, biTaiga, biIceMountains, biJungleHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 14 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 15 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + } ; + for (int z = 0; z < cChunkDef::Width; z++) + { + int idxZ = 17 * z; + for (int x = 0; x < cChunkDef::Width; x++) + { + if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1) + { + // Already set before + continue; + } + int idx = idxZ + x; + int Temperature = a_TemperatureMap[idx] / 16; // -> [0..15] range + int Humidity = a_HumidityMap[idx] / 16; // -> [0..15] range + cChunkDef::SetBiome(a_BiomeMap, x, z, BiomeMap[Temperature + 16 * Humidity]); + } // for x + } // for z +} + + + + + +void cBioGenMultiStepMap::FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap) +{ + int idx = 0; + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++, idx++) + { + if (a_TemperatureMap[idx] > 4 * 16) + { + // Not frozen + continue; + } + switch (cChunkDef::GetBiome(a_BiomeMap, x, z)) + { + case biRiver: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenRiver); break; + case biOcean: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenOcean); break; + } + } // for x + idx += 1; + } // for z +} + + + + diff --git a/src/Generating/BioGen.h b/src/Generating/BioGen.h new file mode 100644 index 000000000..bc70bfab2 --- /dev/null +++ b/src/Generating/BioGen.h @@ -0,0 +1,230 @@ + +// BioGen.h + +/* +Interfaces to the various biome generators: + - cBioGenConstant + - cBioGenCheckerboard + - cBioGenDistortedVoronoi +*/ + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cBioGenConstant : + public cBiomeGen +{ +public: + cBioGenConstant(void) : m_Biome(biPlains) {} + +protected: + + EMCSBiome m_Biome; + + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; +} ; + + + + + +/// A simple cache that stores N most recently generated chunks' biomes; N being settable upon creation +class cBioGenCache : + public cBiomeGen +{ + typedef cBiomeGen super; + +public: + cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize); // Doesn't take ownership of a_BioGenToCache + ~cBioGenCache(); + +protected: + + cBiomeGen * m_BioGenToCache; + + struct sCacheData + { + int m_ChunkX; + int m_ChunkZ; + cChunkDef::BiomeMap m_BiomeMap; + } ; + + // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data + int m_CacheSize; + int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array + sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used + + // Cache statistics + int m_NumHits; + int m_NumMisses; + int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) + + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; +} ; + + + + + +/// Base class for generators that use a list of available biomes. This class takes care of the list. +class cBiomeGenList : + public cBiomeGen +{ + typedef cBiomeGen super; + +protected: + // List of biomes that the generator is allowed to generate: + typedef std::vector EMCSBiomes; + EMCSBiomes m_Biomes; + int m_BiomesCount; // Pulled out of m_Biomes for faster access + + /// Parses the INI file setting string into m_Biomes. + void InitializeBiomes(const AString & a_Biomes); +} ; + + + + + +class cBioGenCheckerboard : + public cBiomeGenList +{ + typedef cBiomeGenList super; + +protected: + int m_BiomeSize; + + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; +} ; + + + + + +class cBioGenVoronoi : + public cBiomeGenList +{ + typedef cBiomeGenList super; + +public: + cBioGenVoronoi(int a_Seed) : + m_Noise(a_Seed) + { + } + +protected: + int m_CellSize; + + cNoise m_Noise; + + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; + + EMCSBiome VoronoiBiome(int a_BlockX, int a_BlockZ); +} ; + + + + + +class cBioGenDistortedVoronoi : + public cBioGenVoronoi +{ + typedef cBioGenVoronoi super; +public: + cBioGenDistortedVoronoi(int a_Seed) : + cBioGenVoronoi(a_Seed) + { + } + +protected: + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; + + /// Distorts the coords using a Perlin-like noise + void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ); +} ; + + + + + +class cBioGenMultiStepMap : + public cBiomeGen +{ + typedef cBiomeGen super; + +public: + cBioGenMultiStepMap(int a_Seed); + +protected: + // Noises used for composing the perlin-noise: + cNoise m_Noise1; + cNoise m_Noise2; + cNoise m_Noise3; + cNoise m_Noise4; + cNoise m_Noise5; + cNoise m_Noise6; + + int m_Seed; + int m_OceanCellSize; + int m_MushroomIslandSize; + int m_RiverCellSize; + double m_RiverWidthThreshold; + float m_LandBiomesSize; + + typedef int IntMap[17 * 17]; // x + 17 * z, expected trimmed into [0..255] range + typedef double DblMap[17 * 17]; // x + 17 * z, expected trimmed into [0..1] range + + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; + + /** Step 1: Decides between ocean, land and mushroom, using a DistVoronoi with special conditions and post-processing for mushroom islands + Sets biomes to biOcean, -1 (i.e. land), biMushroomIsland or biMushroomShore + */ + void DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); + + /** Step 2: Add rivers to the land + Flips some "-1" biomes into biRiver + */ + void AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); + + /** Step 3: Decide land biomes using a temperature / humidity map; freeze ocean / river in low temperatures. + Flips all remaining "-1" biomes into land biomes. Also flips some biOcean and biRiver into biFrozenOcean, biFrozenRiver, based on temp map. + */ + void ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); + + /// Distorts the coords using a Perlin-like noise, with a specified cell-size + void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize); + + /// Builds two Perlin-noise maps, one for temperature, the other for humidity. Trims both into [0..255] range + void BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap); + + /// Flips all remaining "-1" biomes into land biomes using the two maps + void DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap); + + /// Flips biOcean and biRiver into biFrozenOcean and biFrozenRiver if the temperature is too low + void FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap); +} ; + + + + diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp new file mode 100644 index 000000000..4221ea187 --- /dev/null +++ b/src/Generating/Caves.cpp @@ -0,0 +1,970 @@ + +// Caves.cpp + +// Implements the various cave structure generators: +// - cStructGenWormNestCaves +// - cStructGenDualRidgeCaves +// - cStructGenMarbleCaves +// - cStructGenNetherCaves + +/* +WormNestCave generator: +Caves are generated in "nests" - groups of tunnels generated from a single point. +For each chunk, all the nests that could intersect it are generated. +For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...) +Then each tunnel is randomized by inserting points in between its ends. +Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other. +When the tunnels are ready, they are simply carved into the chunk, one by one. +To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it. + +MarbleCaves generator: +For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept. +Problem with this is the amount of CPU work needed for each chunk. +Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground. + +DualRidgeCaves generator: +Instead of evaluating a single noise function, two different noise functions are multiplied. This produces +regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be +reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best. +*/ + +#include "Globals.h" +#include "Caves.h" + + + + + +/// How many nests in each direction are generated for a given chunk. Must be an even number +#define NEIGHBORHOOD_SIZE 8 + + + + + +const int MIN_RADIUS = 3; +const int MAX_RADIUS = 8; + + + + + +struct cCaveDefPoint +{ + int m_BlockX; + int m_BlockY; + int m_BlockZ; + int m_Radius; + + cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) : + m_BlockX(a_BlockX), + m_BlockY(a_BlockY), + m_BlockZ(a_BlockZ), + m_Radius(a_Radius) + { + } +} ; + +typedef std::vector cCaveDefPoints; + + + + + +/// A single non-branching tunnel of a WormNestCave +class cCaveTunnel +{ + // The bounding box, including the radii around defpoints: + int m_MinBlockX, m_MaxBlockX; + int m_MinBlockY, m_MaxBlockY; + int m_MinBlockZ, m_MaxBlockZ; + + /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise + void Randomize(cNoise & a_Noise); + + /// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short) + bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst); + + /// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true + void Smooth(void); + + /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block + void FinishLinear(void); + + /// Calculates the bounding box of the points present + void CalcBoundingBox(void); + +public: + cCaveDefPoints m_Points; + + cCaveTunnel( + int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, + int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, + cNoise & a_Noise + ); + + /// Carves the tunnel into the chunk specified + void ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap + ); + + #ifdef _DEBUG + AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; + #endif // _DEBUG +} ; + +typedef std::vector cCaveTunnels; + + + + + +/// A collection of connected tunnels, possibly branching. +class cStructGenWormNestCaves::cCaveSystem +{ +public: + // The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk() + int m_BlockX; + int m_BlockZ; + + cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise); + ~cCaveSystem(); + + /// Carves the cave system into the chunk specified + void ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap + ); + + #ifdef _DEBUG + AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; + #endif // _DEBUG + +protected: + int m_Size; + cCaveTunnels m_Tunnels; + + void Clear(void); + + /// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments] + void GenerateTunnelsFromPoint( + int a_OriginX, int a_OriginY, int a_OriginZ, + cNoise & a_Noise, int a_Segments + ); + + /// Returns a radius based on the location provided. + int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ); +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCaveTunnel: + +cCaveTunnel::cCaveTunnel( + int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, + int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, + cNoise & a_Noise +) +{ + m_Points.push_back(cCaveDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius)); + m_Points.push_back(cCaveDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius)); + + if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0)) + { + // Don't bother detailing this cave, it's under the world anyway + return; + } + + Randomize(a_Noise); + Smooth(); + + // We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data: + CalcBoundingBox(); + + FinishLinear(); +} + + + + + +void cCaveTunnel::Randomize(cNoise & a_Noise) +{ + // Repeat 4 times: + for (int i = 0; i < 4; i++) + { + // For each already present point, insert a point in between it and its predecessor, shifted randomly. + int PrevX = m_Points.front().m_BlockX; + int PrevY = m_Points.front().m_BlockY; + int PrevZ = m_Points.front().m_BlockZ; + int PrevR = m_Points.front().m_Radius; + cCaveDefPoints Pts; + Pts.reserve(m_Points.size() * 2 + 1); + Pts.push_back(m_Points.front()); + for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) + { + int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11; + int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX); + len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY); + len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ); + len = 3 * (int)sqrt((double)len) / 4; + int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1)); + Random /= 4; + int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2); + Random /= 256; + int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4); + Random /= 256; + int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2); + Pts.push_back(cCaveDefPoint(x, y, z, Rad)); + Pts.push_back(*itr); + PrevX = itr->m_BlockX; + PrevY = itr->m_BlockY; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + } + std::swap(Pts, m_Points); + } +} + + + + + +bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst) +{ + // Smoothing: for each line segment, add points on its 1/4 lengths + bool res = false; + int Num = a_Src.size() - 2; // this many intermediary points + a_Dst.clear(); + a_Dst.reserve(Num * 2 + 2); + cCaveDefPoints::const_iterator itr = a_Src.begin() + 1; + a_Dst.push_back(a_Src.front()); + int PrevX = a_Src.front().m_BlockX; + int PrevY = a_Src.front().m_BlockY; + int PrevZ = a_Src.front().m_BlockZ; + int PrevR = a_Src.front().m_Radius; + for (int i = 0; i <= Num; ++i, ++itr) + { + int dx = itr->m_BlockX - PrevX; + int dy = itr->m_BlockY - PrevY; + int dz = itr->m_BlockZ - PrevZ; + if (abs(dx) + abs(dz) + abs(dy) < 6) + { + // Too short a segment to smooth-subdivide into quarters + PrevX = itr->m_BlockX; + PrevY = itr->m_BlockY; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + continue; + } + int dr = itr->m_Radius - PrevR; + int Rad1 = std::max(PrevR + 1 * dr / 4, 1); + int Rad2 = std::max(PrevR + 3 * dr / 4, 1); + a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1)); + a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2)); + PrevX = itr->m_BlockX; + PrevY = itr->m_BlockY; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + res = true; + } + a_Dst.push_back(a_Src.back()); + return res && (a_Src.size() < a_Dst.size()); +} + + + + + +void cCaveTunnel::Smooth(void) +{ + cCaveDefPoints Pts; + while (true) + { + if (!RefineDefPoints(m_Points, Pts)) + { + std::swap(Pts, m_Points); + return; + } + if (!RefineDefPoints(Pts, m_Points)) + { + return; + } + } +} + + + + + +void cCaveTunnel::FinishLinear(void) +{ + // For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints + cCaveDefPoints Pts; + std::swap(Pts, m_Points); + + m_Points.reserve(Pts.size() * 3); + int PrevX = Pts.front().m_BlockX; + int PrevY = Pts.front().m_BlockY; + int PrevZ = Pts.front().m_BlockZ; + for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) + { + int x1 = itr->m_BlockX; + int y1 = itr->m_BlockY; + int z1 = itr->m_BlockZ; + int dx = abs(x1 - PrevX); + int dy = abs(y1 - PrevY); + int dz = abs(z1 - PrevZ); + int sx = (PrevX < x1) ? 1 : -1; + int sy = (PrevY < y1) ? 1 : -1; + int sz = (PrevZ < z1) ? 1 : -1; + int err = dx - dz; + int R = itr->m_Radius; + + if (dx >= std::max(dy, dz)) // x dominant + { + int yd = dy - dx / 2; + int zd = dz - dx / 2; + + while (true) + { + m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); + + if (PrevX == x1) + { + break; + } + + if (yd >= 0) // move along y + { + PrevY += sy; + yd -= dx; + } + + if (zd >= 0) // move along z + { + PrevZ += sz; + zd -= dx; + } + + // move along x + PrevX += sx; + yd += dy; + zd += dz; + } + } + else if (dy >= std::max(dx, dz)) // y dominant + { + int xd = dx - dy / 2; + int zd = dz - dy / 2; + + while (true) + { + m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); + + if (PrevY == y1) + { + break; + } + + if (xd >= 0) // move along x + { + PrevX += sx; + xd -= dy; + } + + if (zd >= 0) // move along z + { + PrevZ += sz; + zd -= dy; + } + + // move along y + PrevY += sy; + xd += dx; + zd += dz; + } + } + else + { + // z dominant + ASSERT(dz >= std::max(dx, dy)); + int xd = dx - dz / 2; + int yd = dy - dz / 2; + + while (true) + { + m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); + + if (PrevZ == z1) + { + break; + } + + if (xd >= 0) // move along x + { + PrevX += sx; + xd -= dz; + } + + if (yd >= 0) // move along y + { + PrevY += sy; + yd -= dz; + } + + // move along z + PrevZ += sz; + xd += dx; + yd += dy; + } + } // if (which dimension is dominant) + } // for itr +} + + + + + +void cCaveTunnel::CalcBoundingBox(void) +{ + m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX; + m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY; + m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ; + for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) + { + m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius); + m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius); + m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius); + m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius); + m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius); + m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius); + } // for itr - m_Points[] +} + + + + + +void cCaveTunnel::ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap +) +{ + int BaseX = a_ChunkX * cChunkDef::Width; + int BaseZ = a_ChunkZ * cChunkDef::Width; + if ( + (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) || + (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) + ) + { + // Tunnel does not intersect the chunk at all, bail out + return; + } + + int BlockStartX = a_ChunkX * cChunkDef::Width; + int BlockStartZ = a_ChunkZ * cChunkDef::Width; + int BlockEndX = BlockStartX + cChunkDef::Width; + int BlockEndZ = BlockStartZ + cChunkDef::Width; + for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) + { + if ( + (itr->m_BlockX + itr->m_Radius < BlockStartX) || + (itr->m_BlockX - itr->m_Radius > BlockEndX) || + (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || + (itr->m_BlockZ - itr->m_Radius > BlockEndZ) + ) + { + // Cannot intersect, bail out early + continue; + } + + // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom: + int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc + int DifY = itr->m_BlockY; + int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc + int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1); + int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height)); + int SqRad = itr->m_Radius * itr->m_Radius; + for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) + { + for (int y = Bottom; y <= Top; y++) + { + int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z); + if (4 * SqDist <= SqRad) + { + switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) + { + // Only carve out these specific block types + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_GRAVEL: + case E_BLOCK_SAND: + case E_BLOCK_SANDSTONE: + case E_BLOCK_NETHERRACK: + case E_BLOCK_COAL_ORE: + case E_BLOCK_IRON_ORE: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); + break; + } + default: break; + } + } + } // for y + } // for x, z + } // for itr - m_Points[] + + /* + #ifdef _DEBUG + // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave: + for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) + { + int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc + int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc + if ( + (DifX >= 0) && (DifX < cChunkDef::Width) && + (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) && + (DifZ >= 0) && (DifZ < cChunkDef::Width) + ) + { + cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE); + } + } // for itr - m_Points[] + #endif // _DEBUG + //*/ +} + + + + + +#ifdef _DEBUG +AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const +{ + AString SVG; + SVG.reserve(m_Points.size() * 20 + 200); + AppendPrintf(SVG, "m_BlockX, a_OffsetZ + itr->m_BlockZ); + Prefix = 'L'; + } + SVG.append("\"/>\n"); + return SVG; +} +#endif // _DEBUG + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenWormNestCaves::cCaveSystem: + +cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ), + m_Size(a_Size) +{ + int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3; + for (int i = 0; i < Num; i++) + { + int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset; + int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset; + int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20; + + // Generate three branches from the origin point: + // The tunnels generated depend on X, Y, Z and Branches, + // for the same set of numbers it generates the same offsets! + // That's why we add a +1 to X in the third line + GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3); + GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2); + GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3); + } +} + + + + + +cStructGenWormNestCaves::cCaveSystem::~cCaveSystem() +{ + Clear(); +} + + + + + + +void cStructGenWormNestCaves::cCaveSystem::ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap +) +{ + for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) + { + (*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap); + } // for itr - m_Tunnels[] +} + + + + + +#ifdef _DEBUG +AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const +{ + AString SVG; + SVG.reserve(512 * 1024); + for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) + { + SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ)); + } // for itr - m_Tunnels[] + + // Base point highlight: + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ + ); + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 + ); + + // A gray line from the base point to the first point of the ravine, for identification: + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, + a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX, + a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ + ); + + // Offset guides: + if (a_OffsetX > 0) + { + AppendPrintf(SVG, "\n", + a_OffsetX, a_OffsetX + ); + } + if (a_OffsetZ > 0) + { + AppendPrintf(SVG, "\n", + a_OffsetZ, a_OffsetZ + ); + } + + return SVG; +} +#endif // _DEBUG + + + + + +void cStructGenWormNestCaves::cCaveSystem::Clear(void) +{ + for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) + { + delete *itr; + } + m_Tunnels.clear(); +} + + + + + +void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint( + int a_OriginX, int a_OriginY, int a_OriginZ, + cNoise & a_Noise, int a_NumSegments +) +{ + int DoubleSize = m_Size * 2; + int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX); + for (int i = a_NumSegments - 1; i >= 0; --i) + { + int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2; + int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4; + int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2; + int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX); + m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise)); + GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i); + a_OriginX = EndX; + a_OriginY = EndY; + a_OriginZ = EndZ; + Radius = EndR; + } // for i - a_NumSegments +} + + + + + +int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ) +{ + // Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large + int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11; + /* + // Not good enough: + // The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value. + // To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise + int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff); + // sum is now a gaussian-distribution noise within [0 .. 767], with center at 384. + // We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center + int res = 3 + abs((sum / 24) - 16); + */ + + // Algorithm of choice: random value in the range of zero to random value - heavily towards zero + int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1); + return res; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenWormNestCaves: + +cStructGenWormNestCaves::~cStructGenWormNestCaves() +{ + ClearCache(); +} + + + + + +void cStructGenWormNestCaves::ClearCache(void) +{ + for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Cache[] + m_Cache.clear(); +} + + + + + +void cStructGenWormNestCaves::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cCaveSystems Caves; + GetCavesForChunk(ChunkX, ChunkZ, Caves); + for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr) + { + (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); + } // for itr - Caves[] +} + + + + + +void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves) +{ + int BaseX = a_ChunkX * cChunkDef::Width / m_Grid; + int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid; + if (BaseX < 0) + { + --BaseX; + } + if (BaseZ < 0) + { + --BaseZ; + } + BaseX -= NEIGHBORHOOD_SIZE / 2; + BaseZ -= NEIGHBORHOOD_SIZE / 2; + + // Walk the cache, move each cave system that we want into a_Caves: + int StartX = BaseX * m_Grid; + int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid; + int StartZ = BaseZ * m_Grid; + int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid; + for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) + { + if ( + ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && + ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) + ) + { + // want + a_Caves.push_back(*itr); + itr = m_Cache.erase(itr); + } + else + { + // don't want + ++itr; + } + } // for itr - m_Cache[] + + for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) + { + int RealX = (BaseX + x) * m_Grid; + for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) + { + int RealZ = (BaseZ + z) * m_Grid; + bool Found = false; + for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) + { + if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) + { + Found = true; + break; + } + } + if (!Found) + { + a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise)); + } + } + } + + // Copy a_Caves into m_Cache to the beginning: + cCaveSystems CavesCopy(a_Caves); + m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end()); + + // Trim the cache if it's too long: + if (m_Cache.size() > 100) + { + cCaveSystems::iterator itr = m_Cache.begin(); + std::advance(itr, 100); + for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } + itr = m_Cache.begin(); + std::advance(itr, 100); + m_Cache.erase(itr, m_Cache.end()); + } + + /* + // Uncomment this block for debugging the caves' shapes in 2D using an SVG export + #ifdef _DEBUG + AString SVG; + SVG.append("\n\n"); + SVG.reserve(2 * 1024 * 1024); + for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) + { + int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid); + Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid); + SVG.append((*itr)->ExportAsSVG(Color, 512, 512)); + } + SVG.append("\n"); + + AString fnam; + Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); + cFile File(fnam, cFile::fmWrite); + File.Write(SVG.c_str(), SVG.size()); + #endif // _DEBUG + //*/ +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenMarbleCaves: + +static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise ) +{ + static const float PI_2 = 1.57079633f; + float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4; + + oct1 = oct1 * oct1 * oct1; + if (oct1 < 0.f) oct1 = PI_2; + if (oct1 > PI_2) oct1 = PI_2; + + return oct1; +} + + + + + +void cStructGenMarbleCaves::GenStructures(cChunkDesc & a_ChunkDesc) +{ + cNoise Noise(m_Seed); + for (int z = 0; z < cChunkDef::Width; z++) + { + const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z); + for (int x = 0; x < cChunkDef::Width; x++) + { + const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x); + + int Top = a_ChunkDesc.GetHeight(x, z); + for (int y = 1; y < Top; ++y ) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE) + { + continue; + } + + const float yy = (float)y; + const float WaveNoise = 1; + if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); + } + } // for y + } // for x + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenDualRidgeCaves: + +void cStructGenDualRidgeCaves::GenStructures(cChunkDesc & a_ChunkDesc) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10; + for (int x = 0; x < cChunkDef::Width; x++) + { + const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10; + + int Top = a_ChunkDesc.GetHeight(x, z); + for (int y = 1; y <= Top; ++y) + { + const float yy = (float)y / 10; + const float WaveNoise = 1; + float n1 = m_Noise1.CubicNoise3D(xx, yy, zz); + float n2 = m_Noise2.CubicNoise3D(xx, yy, zz); + float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; + float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; + if ((abs(n1 + n3) * abs(n2 + n4)) > m_Threshold) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); + } + } // for y + } // for x + } // for z +} + + + + diff --git a/src/Generating/Caves.h b/src/Generating/Caves.h new file mode 100644 index 000000000..70cf6fe8c --- /dev/null +++ b/src/Generating/Caves.h @@ -0,0 +1,102 @@ + +// Caves.h + +// Interfaces to the various cave structure generators: +// - cStructGenWormNestCaves +// - cStructGenMarbleCaves +// - cStructGenNetherCaves + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenMarbleCaves : + public cStructureGen +{ +public: + cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {} + +protected: + + int m_Seed; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenDualRidgeCaves : + public cStructureGen +{ +public: + cStructGenDualRidgeCaves(int a_Seed, float a_Threshold) : + m_Noise1(a_Seed), + m_Noise2(2 * a_Seed + 19999), + m_Seed(a_Seed), + m_Threshold(a_Threshold) + { + } + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + int m_Seed; + float m_Threshold; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenWormNestCaves : + public cStructureGen +{ +public: + cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) : + m_Noise(a_Seed), + m_Size(a_Size), + m_Grid(a_Grid), + m_MaxOffset(a_MaxOffset) + { + } + + ~cStructGenWormNestCaves(); + +protected: + class cCaveSystem; // fwd: Caves.cpp + typedef std::list cCaveSystems; + + cNoise m_Noise; + int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel + int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to + int m_Grid; // average spacing of the nests + cCaveSystems m_Cache; + + /// Clears everything from the cache + void ClearCache(void); + + /// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function. + void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves); + + // cStructGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp new file mode 100644 index 000000000..9fb306996 --- /dev/null +++ b/src/Generating/ChunkDesc.cpp @@ -0,0 +1,605 @@ + +// ChunkDesc.cpp + +// Implements the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING. + +#include "Globals.h" +#include "ChunkDesc.h" +#include "../BlockArea.h" +#include "../Cuboid.h" +#include "../Noise.h" +#include "../BlockEntities/BlockEntity.h" + + + + + +cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) : + m_ChunkX(a_ChunkX), + m_ChunkZ(a_ChunkZ), + m_bUseDefaultBiomes(true), + m_bUseDefaultHeight(true), + m_bUseDefaultComposition(true), + m_bUseDefaultStructures(true), + m_bUseDefaultFinish(true) +{ + m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width); + /* + memset(m_BlockTypes, 0, sizeof(cChunkDef::BlockTypes)); + memset(m_BlockMeta, 0, sizeof(cChunkDef::BlockNibbles)); + */ + memset(m_BiomeMap, 0, sizeof(cChunkDef::BiomeMap)); + memset(m_HeightMap, 0, sizeof(cChunkDef::HeightMap)); +} + + + + + +cChunkDesc::~cChunkDesc() +{ + // Nothing needed yet +} + + + + + +void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkX = a_ChunkX; + m_ChunkZ = a_ChunkZ; +} + + + + + +void cChunkDesc::FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + m_BlockArea.Fill(cBlockArea::baTypes | cBlockArea::baMetas, a_BlockType, a_BlockMeta); +} + + + + + +void cChunkDesc::SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + m_BlockArea.SetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); +} + + + + + +void cChunkDesc::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + m_BlockArea.GetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); +} + + + + + +void cChunkDesc::SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) +{ + cChunkDef::SetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ, a_BlockType); +} + + + + + +BLOCKTYPE cChunkDesc::GetBlockType(int a_RelX, int a_RelY, int a_RelZ) +{ + return cChunkDef::GetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ); +} + + + + + +NIBBLETYPE cChunkDesc::GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ) +{ + return m_BlockArea.GetRelBlockMeta(a_RelX, a_RelY, a_RelZ); +} + + + + + +void cChunkDesc::SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta) +{ + m_BlockArea.SetRelBlockMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta); +} + + + + + +void cChunkDesc::SetBiome(int a_RelX, int a_RelZ, int a_BiomeID) +{ + cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, (EMCSBiome)a_BiomeID); +} + + + + +EMCSBiome cChunkDesc::GetBiome(int a_RelX, int a_RelZ) +{ + return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); +} + + + + + +void cChunkDesc::SetHeight(int a_RelX, int a_RelZ, int a_Height) +{ + cChunkDef::SetHeight(m_HeightMap, a_RelX, a_RelZ, a_Height); +} + + + + + +int cChunkDesc::GetHeight(int a_RelX, int a_RelZ) +{ + return cChunkDef::GetHeight(m_HeightMap, a_RelX, a_RelZ); +} + + + + + +void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes) +{ + m_bUseDefaultBiomes = a_bUseDefaultBiomes; +} + + + + + +bool cChunkDesc::IsUsingDefaultBiomes(void) const +{ + return m_bUseDefaultBiomes; +} + + + + + +void cChunkDesc::SetUseDefaultHeight(bool a_bUseDefaultHeight) +{ + m_bUseDefaultHeight = a_bUseDefaultHeight; +} + + + + + +bool cChunkDesc::IsUsingDefaultHeight(void) const +{ + return m_bUseDefaultHeight; +} + + + + + +void cChunkDesc::SetUseDefaultComposition(bool a_bUseDefaultComposition) +{ + m_bUseDefaultComposition = a_bUseDefaultComposition; +} + + + + + +bool cChunkDesc::IsUsingDefaultComposition(void) const +{ + return m_bUseDefaultComposition; +} + + + + + +void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures) +{ + m_bUseDefaultStructures = a_bUseDefaultStructures; +} + + + + + +bool cChunkDesc::IsUsingDefaultStructures(void) const +{ + return m_bUseDefaultStructures; +} + + + + + +void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish) +{ + m_bUseDefaultFinish = a_bUseDefaultFinish; +} + + + + + +bool cChunkDesc::IsUsingDefaultFinish(void) const +{ + return m_bUseDefaultFinish; +} + + + + +void cChunkDesc::WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy) +{ + m_BlockArea.Merge(a_BlockArea, a_RelX, a_RelY, a_RelZ, a_MergeStrategy); +} + + + + + +void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ) +{ + // Normalize the coords: + if (a_MinRelX > a_MaxRelX) + { + std::swap(a_MinRelX, a_MaxRelX); + } + if (a_MinRelY > a_MaxRelY) + { + std::swap(a_MinRelY, a_MaxRelY); + } + if (a_MinRelZ > a_MaxRelZ) + { + std::swap(a_MinRelZ, a_MaxRelZ); + } + + // Include the Max coords: + a_MaxRelX += 1; + a_MaxRelY += 1; + a_MaxRelZ += 1; + + // Check coords validity: + if (a_MinRelX < 0) + { + LOGWARNING("%s: MinRelX less than zero, adjusting to zero", __FUNCTION__); + a_MinRelX = 0; + } + else if (a_MinRelX >= cChunkDef::Width) + { + LOGWARNING("%s: MinRelX more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MinRelX = cChunkDef::Width - 1; + } + if (a_MaxRelX < 0) + { + LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__); + a_MaxRelX = 0; + } + else if (a_MinRelX >= cChunkDef::Width) + { + LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MaxRelX = cChunkDef::Width - 1; + } + + if (a_MinRelY < 0) + { + LOGWARNING("%s: MinRelY less than zero, adjusting to zero", __FUNCTION__); + a_MinRelY = 0; + } + else if (a_MinRelY >= cChunkDef::Height) + { + LOGWARNING("%s: MinRelY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MinRelY = cChunkDef::Height - 1; + } + if (a_MaxRelY < 0) + { + LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__); + a_MaxRelY = 0; + } + else if (a_MinRelY >= cChunkDef::Height) + { + LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MaxRelY = cChunkDef::Height - 1; + } + + if (a_MinRelZ < 0) + { + LOGWARNING("%s: MinRelZ less than zero, adjusting to zero", __FUNCTION__); + a_MinRelZ = 0; + } + else if (a_MinRelZ >= cChunkDef::Width) + { + LOGWARNING("%s: MinRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MinRelZ = cChunkDef::Width - 1; + } + if (a_MaxRelZ < 0) + { + LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__); + a_MaxRelZ = 0; + } + else if (a_MinRelZ >= cChunkDef::Width) + { + LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MaxRelZ = cChunkDef::Width - 1; + } + + // Prepare the block area: + int SizeX = a_MaxRelX - a_MinRelX; + int SizeY = a_MaxRelY - a_MinRelY; + int SizeZ = a_MaxRelZ - a_MinRelZ; + a_Dest.Clear(); + a_Dest.m_OriginX = m_ChunkX * cChunkDef::Width + a_MinRelX; + a_Dest.m_OriginY = a_MinRelY; + a_Dest.m_OriginZ = m_ChunkZ * cChunkDef::Width + a_MinRelZ; + a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas); + + for (int y = 0; y < SizeY; y++) + { + int CDY = a_MinRelY + y; + for (int z = 0; z < SizeZ; z++) + { + int CDZ = a_MinRelZ + z; + for (int x = 0; x < SizeX; x++) + { + int CDX = a_MinRelX + x; + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + GetBlockTypeMeta(CDX, CDY, CDZ, BlockType, BlockMeta); + a_Dest.SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta); + } // for x + } // for z + } // for y +} + + + + + +HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const +{ + HEIGHTTYPE MaxHeight = m_HeightMap[0]; + for (int i = 1; i < ARRAYCOUNT(m_HeightMap); i++) + { + if (m_HeightMap[i] > MaxHeight) + { + MaxHeight = m_HeightMap[i]; + } + } + return MaxHeight; +} + + + + + +void cChunkDesc::FillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta +) +{ + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); + } + } // for z + } // for y +} + + + + + +void cChunkDesc::ReplaceRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta +) +{ + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + GetBlockTypeMeta(x, y, z, BlockType, BlockMeta); + if ((BlockType == a_SrcType) && (BlockMeta == a_SrcMeta)) + { + SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); + } + } + } // for z + } // for y +} + + + + + +void cChunkDesc::FloorRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta +) +{ + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + switch (GetBlockType(x, y, z)) + { + case E_BLOCK_AIR: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); + break; + } + } // switch (GetBlockType) + } // for x + } // for z + } // for y +} + + + + + +void cChunkDesc::RandomFillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + int a_RandomSeed, int a_ChanceOutOf10k +) +{ + cNoise Noise(a_RandomSeed); + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + int rnd = (Noise.IntNoise3DInt(x, y, z) / 7) % 10000; + if (rnd <= a_ChanceOutOf10k) + { + SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); + } + } + } // for z + } // for y +} + + + + + +cBlockEntity * cChunkDesc::GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ) +{ + int AbsX = a_RelX + m_ChunkX * cChunkDef::Width; + int AbsZ = a_RelZ + m_ChunkZ * cChunkDef::Width; + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr) + { + if (((*itr)->GetPosX() == AbsX) && ((*itr)->GetPosY() == a_RelY) && ((*itr)->GetPosZ() == AbsZ)) + { + // Already in the list: + if ((*itr)->GetBlockType() != GetBlockType(a_RelX, a_RelY, a_RelZ)) + { + // Wrong type, the block type has been overwritten. Erase and create new: + m_BlockEntities.erase(itr); + break; + } + // Correct type, already present. Return it: + return *itr; + } + } // for itr - m_BlockEntities[] + + // The block entity is not created yet, try to create it and add to list: + cBlockEntity * be = cBlockEntity::CreateByBlockType(GetBlockType(a_RelX, a_RelY, a_RelZ), GetBlockMeta(a_RelX, a_RelY, a_RelZ), AbsX, a_RelY, AbsZ); + if (be == NULL) + { + // No block entity for this block type + return NULL; + } + m_BlockEntities.push_back(be); + return be; +} + + + + + +void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas) +{ + const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas(); + for (int i = 0; i < ARRAYCOUNT(a_DestMetas); i++) + { + a_DestMetas[i] = AreaMetas[2 * i] | (AreaMetas[2 * i + 1] << 4); + } +} + + + + + +#ifdef _DEBUG + +void cChunkDesc::VerifyHeightmap(void) +{ + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = cChunkDef::Height - 1; y > 0; y--) + { + BLOCKTYPE BlockType = GetBlockType(x, y, z); + if (BlockType != E_BLOCK_AIR) + { + int Height = GetHeight(x, z); + ASSERT(Height == y); + break; + } + } // for y + } // for z + } // for x +} + +#endif // _DEBUG + + + + + diff --git a/src/Generating/ChunkDesc.h b/src/Generating/ChunkDesc.h new file mode 100644 index 000000000..e130c463f --- /dev/null +++ b/src/Generating/ChunkDesc.h @@ -0,0 +1,217 @@ + +// ChunkDesc.h + +// Declares the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING. + + + + + +#pragma once + +#include "../BlockArea.h" +#include "../ChunkDef.h" +#include "../Cuboid.h" + + + + + +// fwd: ../BlockArea.h +class cBlockArea; + + + + + +// tolua_begin +class cChunkDesc +{ +public: + // tolua_end + + /// Uncompressed block metas, 1 meta per byte + typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks]; + + cChunkDesc(int a_ChunkX, int a_ChunkZ); + ~cChunkDesc(); + + void SetChunkCoords(int a_ChunkX, int a_ChunkZ); + + // tolua_begin + + int GetChunkX(void) const { return m_ChunkX; } + int GetChunkZ(void) const { return m_ChunkZ; } + + void FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + + void SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType); + BLOCKTYPE GetBlockType(int a_RelX, int a_RelY, int a_RelZ); + + void SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta); + NIBBLETYPE GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ); + + void SetBiome(int a_RelX, int a_RelZ, int a_BiomeID); + EMCSBiome GetBiome(int a_RelX, int a_RelZ); + + void SetHeight(int a_RelX, int a_RelZ, int a_Height); + int GetHeight(int a_RelX, int a_RelZ); + + // Default generation: + void SetUseDefaultBiomes(bool a_bUseDefaultBiomes); + bool IsUsingDefaultBiomes(void) const; + void SetUseDefaultHeight(bool a_bUseDefaultHeight); + bool IsUsingDefaultHeight(void) const; + void SetUseDefaultComposition(bool a_bUseDefaultComposition); + bool IsUsingDefaultComposition(void) const; + void SetUseDefaultStructures(bool a_bUseDefaultStructures); + bool IsUsingDefaultStructures(void) const; + void SetUseDefaultFinish(bool a_bUseDefaultFinish); + bool IsUsingDefaultFinish(void) const; + + /// Writes the block area into the chunk, with its origin set at the specified relative coords. Area's data overwrite everything in the chunk. + void WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy = cBlockArea::msOverwrite); + + /// Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas + void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ); + + /// Returns the maximum height value in the heightmap + HEIGHTTYPE GetMaxHeight(void) const; + + /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk + void FillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ); + + /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk + void FillRelCuboid(const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) + { + FillRelCuboid( + a_RelCuboid.p1.x, a_RelCuboid.p2.x, + a_RelCuboid.p1.y, a_RelCuboid.p2.y, + a_RelCuboid.p1.z, a_RelCuboid.p2.z, + a_BlockType, a_BlockMeta + ); + } + + /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk + void ReplaceRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta + ); + + /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk + void ReplaceRelCuboid( + const cCuboid & a_RelCuboid, + BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta + ) + { + ReplaceRelCuboid( + a_RelCuboid.p1.x, a_RelCuboid.p2.x, + a_RelCuboid.p1.y, a_RelCuboid.p2.y, + a_RelCuboid.p1.z, a_RelCuboid.p2.z, + a_SrcType, a_SrcMeta, + a_DstType, a_DstMeta + ); + } + + /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk + void FloorRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta + ); + + /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk + void FloorRelCuboid( + const cCuboid & a_RelCuboid, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta + ) + { + FloorRelCuboid( + a_RelCuboid.p1.x, a_RelCuboid.p2.x, + a_RelCuboid.p1.y, a_RelCuboid.p2.y, + a_RelCuboid.p1.z, a_RelCuboid.p2.z, + a_DstType, a_DstMeta + ); + } + + /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk + void RandomFillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + int a_RandomSeed, int a_ChanceOutOf10k + ); + + /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk + void RandomFillRelCuboid( + const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + int a_RandomSeed, int a_ChanceOutOf10k + ) + { + RandomFillRelCuboid( + a_RelCuboid.p1.x, a_RelCuboid.p2.x, + a_RelCuboid.p1.y, a_RelCuboid.p2.y, + a_RelCuboid.p1.z, a_RelCuboid.p2.z, + a_BlockType, a_BlockMeta, + a_RandomSeed, a_ChanceOutOf10k + ); + } + + /// Returns the block entity at the specified coords. + /// If there is no block entity at those coords, tries to create one, based on the block type + /// If the blocktype doesn't support a block entity, returns NULL. + cBlockEntity * GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ); + + // tolua_end + + // Accessors used by cChunkGenerator::Generator descendants: + inline cChunkDef::BiomeMap & GetBiomeMap (void) { return m_BiomeMap; } + inline cChunkDef::BlockTypes & GetBlockTypes (void) { return *((cChunkDef::BlockTypes *)m_BlockArea.GetBlockTypes()); } + // CANNOT, different compression! + // inline cChunkDef::BlockNibbles & GetBlockMetas (void) { return *((cChunkDef::BlockNibbles *)m_BlockArea.GetBlockMetas()); } + inline BlockNibbleBytes & GetBlockMetasUncompressed(void) { return *((BlockNibbleBytes *)m_BlockArea.GetBlockMetas()); } + inline cChunkDef::HeightMap & GetHeightMap (void) { return m_HeightMap; } + inline cEntityList & GetEntities (void) { return m_Entities; } + inline cBlockEntityList & GetBlockEntities (void) { return m_BlockEntities; } + + /// Compresses the metas from the BlockArea format (1 meta per byte) into regular format (2 metas per byte) + void CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas); + + #ifdef _DEBUG + /// Verifies that the heightmap corresponds to blocktype contents; if not, asserts on that column + void VerifyHeightmap(void); + #endif // _DEBUG + +private: + int m_ChunkX; + int m_ChunkZ; + + cChunkDef::BiomeMap m_BiomeMap; + cBlockArea m_BlockArea; + cChunkDef::HeightMap m_HeightMap; + cEntityList m_Entities; // Individual entities are NOT owned by this object! + cBlockEntityList m_BlockEntities; // Individual block entities are NOT owned by this object! + + bool m_bUseDefaultBiomes; + bool m_bUseDefaultHeight; + bool m_bUseDefaultComposition; + bool m_bUseDefaultStructures; + bool m_bUseDefaultFinish; +} ; // tolua_export + + + + diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp new file mode 100644 index 000000000..59a00b540 --- /dev/null +++ b/src/Generating/ChunkGenerator.cpp @@ -0,0 +1,329 @@ + +#include "Globals.h" + +#include "ChunkGenerator.h" +#include "../World.h" +#include "../../iniFile/iniFile.h" +#include "../Root.h" +#include "../PluginManager.h" +#include "ChunkDesc.h" +#include "ComposableGenerator.h" +#include "Noise3DGenerator.h" + + + + + +/// If the generation queue size exceeds this number, a warning will be output +const int QUEUE_WARNING_LIMIT = 1000; + +/// If the generation queue size exceeds this number, chunks with no clients will be skipped +const int QUEUE_SKIP_LIMIT = 500; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkGenerator: + +cChunkGenerator::cChunkGenerator(void) : + super("cChunkGenerator"), + m_World(NULL), + m_Generator(NULL) +{ +} + + + + + +cChunkGenerator::~cChunkGenerator() +{ + Stop(); +} + + + + + +bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile) +{ + MTRand rnd; + m_World = a_World; + m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt()); + AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable"); + + if (NoCaseCompare(GeneratorName, "Noise3D") == 0) + { + m_Generator = new cNoise3DGenerator(*this); + } + else + { + if (NoCaseCompare(GeneratorName, "composable") != 0) + { + LOGWARN("[Generator]::Generator value \"%s\" not recognized, using \"Composable\".", GeneratorName.c_str()); + } + m_Generator = new cComposableGenerator(*this); + } + + if (m_Generator == NULL) + { + LOGERROR("Generator could not start, aborting the server"); + return false; + } + + m_Generator->Initialize(a_World, a_IniFile); + + return super::Start(); +} + + + + + +void cChunkGenerator::Stop(void) +{ + m_ShouldTerminate = true; + m_Event.Set(); + m_evtRemoved.Set(); // Wake up anybody waiting for empty queue + Wait(); + + delete m_Generator; + m_Generator = NULL; +} + + + + + +void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + { + cCSLock Lock(m_CS); + + // Check if it is already in the queue: + for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) + { + // Already in the queue, bail out + return; + } + } // for itr - m_Queue[] + + // Add to queue, issue a warning if too many: + if (m_Queue.size() >= QUEUE_WARNING_LIMIT) + { + LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%i)", a_ChunkX, a_ChunkZ, m_Queue.size()); + } + m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); + } + + m_Event.Set(); +} + + + + + +void cChunkGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + if (m_Generator != NULL) + { + m_Generator->GenerateBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); + } +} + + + + + +void cChunkGenerator::WaitForQueueEmpty(void) +{ + cCSLock Lock(m_CS); + while (!m_ShouldTerminate && !m_Queue.empty()) + { + cCSUnlock Unlock(Lock); + m_evtRemoved.Wait(); + } +} + + + + + +int cChunkGenerator::GetQueueLength(void) +{ + cCSLock Lock(m_CS); + return (int)m_Queue.size(); +} + + + + + +EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ) +{ + ASSERT(m_Generator != NULL); + return m_Generator->GetBiomeAt(a_BlockX, a_BlockZ); +} + + + + + +BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default) +{ + AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default); + BLOCKTYPE Block = BlockStringToType(BlockType); + if (Block < 0) + { + LOGWARN("[&s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(),a_Default.c_str()); + return BlockStringToType(a_Default); + } + return Block; +} + + + + + +void cChunkGenerator::Execute(void) +{ + // To be able to display performance information, the generator counts the chunks generated. + // When the queue gets empty, the count is reset, so that waiting for the queue is not counted into the total time. + int NumChunksGenerated = 0; // Number of chunks generated since the queue was last empty + clock_t GenerationStart = clock(); // Clock tick when the queue started to fill + clock_t LastReportTick = clock(); // Clock tick of the last report made (so that performance isn't reported too often) + + while (!m_ShouldTerminate) + { + cCSLock Lock(m_CS); + while (m_Queue.size() == 0) + { + if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC)) + { + LOG("Chunk generator performance: %.2f ch/s (%d ch total)", + (double)NumChunksGenerated * CLOCKS_PER_SEC/ (clock() - GenerationStart), + NumChunksGenerated + ); + } + cCSUnlock Unlock(Lock); + m_Event.Wait(); + if (m_ShouldTerminate) + { + return; + } + NumChunksGenerated = 0; + GenerationStart = clock(); + LastReportTick = clock(); + } + + cChunkCoords coords = m_Queue.front(); // Get next coord from queue + m_Queue.erase( m_Queue.begin() ); // Remove coordinate from queue + bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT); + Lock.Unlock(); // Unlock ASAP + m_evtRemoved.Set(); + + // Display perf info once in a while: + if ((NumChunksGenerated > 16) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC)) + { + LOG("Chunk generator performance: %.2f ch/s (%d ch total)", + (double)NumChunksGenerated * CLOCKS_PER_SEC / (clock() - GenerationStart), + NumChunksGenerated + ); + LastReportTick = clock(); + } + + // Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set + if ((coords.m_ChunkY == 0) && m_World->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ)) + { + LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ); + // Already generated, ignore request + continue; + } + + if (SkipEnabled && !m_World->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ)) + { + LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); + continue; + } + + LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); + DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); + + // Save the chunk right after generating, so that we don't have to generate it again on next run + m_World->GetStorage().QueueSaveChunk(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); + + NumChunksGenerated++; + } // while (!bStop) +} + + + + +void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ); + cRoot::Get()->GetPluginManager()->CallHookChunkGenerating(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc); + m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc); + cRoot::Get()->GetPluginManager()->CallHookChunkGenerated(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc); + + #ifdef _DEBUG + // Verify that the generator has produced valid data: + ChunkDesc.VerifyHeightmap(); + #endif + + cChunkDef::BlockNibbles BlockMetas; + ChunkDesc.CompressBlockMetas(BlockMetas); + + m_World->SetChunkData( + a_ChunkX, a_ChunkZ, + ChunkDesc.GetBlockTypes(), BlockMetas, + NULL, NULL, // We don't have lighting, chunk will be lighted when needed + &ChunkDesc.GetHeightMap(), &ChunkDesc.GetBiomeMap(), + ChunkDesc.GetEntities(), ChunkDesc.GetBlockEntities(), + true + ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkGenerator::cGenerator: + +cChunkGenerator::cGenerator::cGenerator(cChunkGenerator & a_ChunkGenerator) : + m_ChunkGenerator(a_ChunkGenerator) +{ +} + + + + + +void cChunkGenerator::cGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +{ + m_World = a_World; + UNUSED(a_IniFile); +} + + + + + +EMCSBiome cChunkGenerator::cGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ) +{ + cChunkDef::BiomeMap Biomes; + int Y = 0; + int ChunkX, ChunkZ; + cWorld::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, Y, ChunkZ); + GenerateBiomes(ChunkX, ChunkZ, Biomes); + return cChunkDef::GetBiome(Biomes, a_BlockX, a_BlockZ); +} + + + + diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h new file mode 100644 index 000000000..2d3bb8082 --- /dev/null +++ b/src/Generating/ChunkGenerator.h @@ -0,0 +1,113 @@ + +// ChunkGenerator.h + +// Interfaces to the cChunkGenerator class representing the thread that generates chunks + +/* +The object takes requests for generating chunks and processes them in a separate thread one by one. +The requests are not added to the queue if there is already a request with the same coords +Before generating, the thread checks if the chunk hasn't been already generated. +It is theoretically possible to have multiple generator threads by having multiple instances of this object, +but then it MAY happen that the chunk is generated twice. +If the generator queue is overloaded, the generator skips chunks with no clients in them +*/ + + + + + +#pragma once + +#include "../OSSupport/IsThread.h" +#include "../ChunkDef.h" + + + + + +// fwd: +class cWorld; +class cIniFile; +class cChunkDesc; + + + + + +class cChunkGenerator : + cIsThread +{ + typedef cIsThread super; + +public: + /// The interface that a class has to implement to become a generator + class cGenerator + { + public: + cGenerator(cChunkGenerator & a_ChunkGenerator); + virtual ~cGenerator() {} ; // Force a virtual destructor + + /// Called to initialize the generator on server startup. + virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile); + + /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. + virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; + + /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome. Default implementation uses GenerateBiomes(). + virtual EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); + + /// Called in a separate thread to do the actual chunk generation. Generator should generate into a_ChunkDesc. + virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) = 0; + + protected: + cChunkGenerator & m_ChunkGenerator; + cWorld * m_World; + } ; + + + cChunkGenerator (void); + ~cChunkGenerator(); + + bool Start(cWorld * a_World, cIniFile & a_IniFile); + void Stop(void); + + /// Queues the chunk for generation; removes duplicate requests + void QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. + void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); + + void WaitForQueueEmpty(void); + + int GetQueueLength(void); + + int GetSeed(void) const { return m_Seed; } + + /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome + EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); + + /// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. + static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default); + +private: + + cWorld * m_World; + + int m_Seed; + + cCriticalSection m_CS; + cChunkCoordsList m_Queue; + cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate + cEvent m_evtRemoved; ///< Set when an item is removed from the queue + + cGenerator * m_Generator; ///< The actual generator engine used to generate chunks + + // cIsThread override: + virtual void Execute(void) override; + + void DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ); +}; + + + + diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp new file mode 100644 index 000000000..cc2a203af --- /dev/null +++ b/src/Generating/CompoGen.cpp @@ -0,0 +1,634 @@ + +// CompoGen.cpp + +/* Implements the various terrain composition generators: + - cCompoGenSameBlock + - cCompoGenDebugBiomes + - cCompoGenClassic +*/ + +#include "Globals.h" +#include "CompoGen.h" +#include "../BlockID.h" +#include "../Item.h" +#include "../LinearUpscale.h" +#include "../../iniFile/iniFile.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenSameBlock: + +void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int Start; + if (m_IsBedrocked) + { + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + Start = 1; + } + else + { + Start = 0; + } + for (int y = a_ChunkDesc.GetHeight(x, z); y >= Start; y--) + { + a_ChunkDesc.SetBlockType(x, y, z, m_BlockType); + } // for y + } // for z + } // for x +} + + + + + +void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_BlockType = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "SameBlockType", "stone").m_ItemType); + m_IsBedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenDebugBiomes: + +void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + static BLOCKTYPE Blocks[] = + { + E_BLOCK_STONE, + E_BLOCK_COBBLESTONE, + E_BLOCK_LOG, + E_BLOCK_PLANKS, + E_BLOCK_SANDSTONE, + E_BLOCK_WOOL, + E_BLOCK_COAL_ORE, + E_BLOCK_IRON_ORE, + E_BLOCK_GOLD_ORE, + E_BLOCK_DIAMOND_ORE, + E_BLOCK_LAPIS_ORE, + E_BLOCK_REDSTONE_ORE, + E_BLOCK_IRON_BLOCK, + E_BLOCK_GOLD_BLOCK, + E_BLOCK_DIAMOND_BLOCK, + E_BLOCK_LAPIS_BLOCK, + E_BLOCK_BRICK, + E_BLOCK_MOSSY_COBBLESTONE, + E_BLOCK_OBSIDIAN, + E_BLOCK_NETHERRACK, + E_BLOCK_SOULSAND, + E_BLOCK_NETHER_BRICK, + E_BLOCK_BEDROCK, + } ; + + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + BLOCKTYPE BlockType = Blocks[a_ChunkDesc.GetBiome(x, z)]; + for (int y = a_ChunkDesc.GetHeight(x, z); y >= 0; y--) + { + a_ChunkDesc.SetBlockType(x, y, z, BlockType); + } // for y + } // for z + } // for x +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenClassic: + +cCompoGenClassic::cCompoGenClassic(void) : + m_SeaLevel(60), + m_BeachHeight(2), + m_BeachDepth(4), + m_BlockTop(E_BLOCK_GRASS), + m_BlockMiddle(E_BLOCK_DIRT), + m_BlockBottom(E_BLOCK_STONE), + m_BlockBeach(E_BLOCK_SAND), + m_BlockBeachBottom(E_BLOCK_SANDSTONE), + m_BlockSea(E_BLOCK_STATIONARY_WATER) +{ +} + + + + + +void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + /* The classic composition means: + - 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight + - 3 sand and a 1 sandstone, rest stone if between sealevel and sealevel + beachheight + - water from waterlevel to height, then 3 sand, 1 sandstone, the rest stone, if water depth < beachdepth + - water from waterlevel, then 3 dirt, the rest stone otherwise + - bedrock at the bottom + */ + + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + // The patterns to use for different situations, must be same length! + const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ; + const BLOCKTYPE PatternBeach[] = {m_BlockBeach, m_BlockBeach, m_BlockBeach, m_BlockBeachBottom} ; + const BLOCKTYPE PatternOcean[] = {m_BlockMiddle, m_BlockMiddle, m_BlockMiddle, m_BlockBottom} ; + static int PatternLength = ARRAYCOUNT(PatternGround); + ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternBeach)); + ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternOcean)); + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int Height = a_ChunkDesc.GetHeight(x, z); + const BLOCKTYPE * Pattern; + if (Height > m_SeaLevel + m_BeachHeight) + { + Pattern = PatternGround; + } + else if (Height > m_SeaLevel - m_BeachDepth) + { + Pattern = PatternBeach; + } + else + { + Pattern = PatternOcean; + } + + // Fill water from sealevel down to height (if any): + for (int y = m_SeaLevel; y >= Height; --y) + { + a_ChunkDesc.SetBlockType(x, y, z, m_BlockSea); + } + + // Fill from height till the bottom: + for (int y = Height; y >= 1; y--) + { + a_ChunkDesc.SetBlockType(x, y, z, (Height - y < PatternLength) ? Pattern[Height - y] : m_BlockBottom); + } + + // The last layer is always bedrock: + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel); + m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight); + m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth); + m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType); + m_BlockMiddle = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt").m_ItemType); + m_BlockBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBottom", "stone").m_ItemType); + m_BlockBeach = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeach", "sand").m_ItemType); + m_BlockBeachBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone").m_ItemType); + m_BlockSea = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater").m_ItemType); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenBiomal: + +void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + + /* + _X 2013_04_22: + There's no point in generating the whole cubic noise at once, because the noise values are used in + only about 20 % of the cases, so the speed gained by precalculating is lost by precalculating too much data + */ + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int Height = a_ChunkDesc.GetHeight(x, z); + if (Height > m_SeaLevel) + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biOcean: + case biPlains: + case biExtremeHills: + case biForest: + case biTaiga: + case biSwampland: + case biRiver: + case biFrozenOcean: + case biFrozenRiver: + case biIcePlains: + case biIceMountains: + case biForestHills: + case biTaigaHills: + case biExtremeHillsEdge: + case biJungle: + case biJungleHills: + { + FillColumnGrass(x, z, Height, a_ChunkDesc.GetBlockTypes()); + break; + } + case biDesertHills: + case biDesert: + case biBeach: + { + FillColumnSand(x, z, Height, a_ChunkDesc.GetBlockTypes()); + break; + } + case biMushroomIsland: + case biMushroomShore: + { + FillColumnMycelium(x, z, Height, a_ChunkDesc.GetBlockTypes()); + break; + } + default: + { + // TODO + ASSERT(!"CompoGenBiomal: Biome not implemented yet!"); + break; + } + } + } + else + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biDesert: + case biBeach: + { + // Fill with water, sand, sandstone and stone + FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes()); + break; + } + default: + { + // Fill with water, sand/dirt/clay mix and stone + if (m_Noise.CubicNoise2D(0.3f * (cChunkDef::Width * ChunkX + x), 0.3f * (cChunkDef::Width * ChunkZ + z)) < 0) + { + FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes()); + } + else + { + FillColumnWaterDirt(x, z, Height, a_ChunkDesc.GetBlockTypes()); + } + break; + } + } // switch (biome) + a_ChunkDesc.SetHeight(x, z, m_SeaLevel + 1); + } // else (under water) + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1; +} + + + + + +void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + BLOCKTYPE Pattern[] = + { + E_BLOCK_GRASS, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + } ; + FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); + + for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); + } +} + + + + + +void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + BLOCKTYPE Pattern[] = + { + E_BLOCK_SAND, + E_BLOCK_SAND, + E_BLOCK_SAND, + E_BLOCK_SANDSTONE, + } ; + FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); + + for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); + } +} + + + + + + +void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + BLOCKTYPE Pattern[] = + { + E_BLOCK_MYCELIUM, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + } ; + FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); + + for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); + } +} + + + + + +void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes); + for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER); + } +} + + + + + +void cCompoGenBiomal::FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + // Dirt + BLOCKTYPE Pattern[] = + { + E_BLOCK_DIRT, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + } ; + FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); + + for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); + } + for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER); + } +} + + + + + + +void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize) +{ + for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenNether: + +cCompoGenNether::cCompoGenNether(int a_Seed) : + m_Noise1(a_Seed + 10), + m_Noise2(a_Seed * a_Seed * 10 + a_Seed * 1000 + 6000), + m_Threshold(0) +{ +} + + + + + +void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight(); + + const int SEGMENT_HEIGHT = 8; + const int INTERPOL_X = 16; // Must be a divisor of 16 + const int INTERPOL_Z = 16; // Must be a divisor of 16 + // Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately. + // Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them + // Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it. + + int FloorBuf1[17 * 17]; + int FloorBuf2[17 * 17]; + int * FloorHi = FloorBuf1; + int * FloorLo = FloorBuf2; + int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + + // Interpolate the lowest floor: + for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) + { + FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] = + m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) * + m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) / + 256; + } // for x, z - FloorLo[] + LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); + + // Interpolate segments: + for (int Segment = 0; Segment < MaxHeight; Segment += SEGMENT_HEIGHT) + { + // First update the high floor: + for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) + { + FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] = + m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) * + m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / + 256; + } // for x, z - FloorLo[] + LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); + + // Interpolate between FloorLo and FloorHi: + for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) + { + int Lo = FloorLo[x + 17 * z] / 256; + int Hi = FloorHi[x + 17 * z] / 256; + for (int y = 0; y < SEGMENT_HEIGHT; y++) + { + int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; + a_ChunkDesc.SetBlockType(x, y + Segment, z, (Val < m_Threshold) ? E_BLOCK_NETHERRACK : E_BLOCK_AIR); + } + } + + // Swap the floors: + std::swap(FloorLo, FloorHi); + } + + // Bedrock at the bottom and at the top: + for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) + { + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + a_ChunkDesc.SetBlockType(x, a_ChunkDesc.GetHeight(x, z), z, E_BLOCK_BEDROCK); + } +} + + + + + +void cCompoGenNether::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_Threshold = a_IniFile.GetValueSetI("Generator", "NetherThreshold", m_Threshold); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenCache: + +cCompoGenCache::cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize) : + m_Underlying(a_Underlying), + m_CacheSize(a_CacheSize), + m_CacheOrder(new int[a_CacheSize]), + m_CacheData(new sCacheData[a_CacheSize]), + m_NumHits(0), + m_NumMisses(0), + m_TotalChain(0) +{ + for (int i = 0; i < m_CacheSize; i++) + { + m_CacheOrder[i] = i; + m_CacheData[i].m_ChunkX = 0x7fffffff; + m_CacheData[i].m_ChunkZ = 0x7fffffff; + } +} + + + + + +cCompoGenCache::~cCompoGenCache() +{ + delete[] m_CacheData; + delete[] m_CacheOrder; +} + + + + + +void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + #ifdef _DEBUG + if (((m_NumHits + m_NumMisses) % 1024) == 10) + { + LOGD("CompoGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); + LOGD("CompoGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); + } + #endif // _DEBUG + + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + + for (int i = 0; i < m_CacheSize; i++) + { + if ( + (m_CacheData[m_CacheOrder[i]].m_ChunkX != ChunkX) || + (m_CacheData[m_CacheOrder[i]].m_ChunkZ != ChunkZ) + ) + { + continue; + } + // Found it in the cache + int Idx = m_CacheOrder[i]; + + // Move to front: + for (int j = i; j > 0; j--) + { + m_CacheOrder[j] = m_CacheOrder[j - 1]; + } + m_CacheOrder[0] = Idx; + + // Use the cached data: + memcpy(a_ChunkDesc.GetBlockTypes(), m_CacheData[Idx].m_BlockTypes, sizeof(a_ChunkDesc.GetBlockTypes())); + memcpy(a_ChunkDesc.GetBlockMetasUncompressed(), m_CacheData[Idx].m_BlockMetas, sizeof(a_ChunkDesc.GetBlockMetasUncompressed())); + + m_NumHits++; + m_TotalChain += i; + return; + } // for i - cache + + // Not in the cache: + m_NumMisses++; + m_Underlying.ComposeTerrain(a_ChunkDesc); + + // Insert it as the first item in the MRU order: + int Idx = m_CacheOrder[m_CacheSize - 1]; + for (int i = m_CacheSize - 1; i > 0; i--) + { + m_CacheOrder[i] = m_CacheOrder[i - 1]; + } // for i - m_CacheOrder[] + m_CacheOrder[0] = Idx; + memcpy(m_CacheData[Idx].m_BlockTypes, a_ChunkDesc.GetBlockTypes(), sizeof(a_ChunkDesc.GetBlockTypes())); + memcpy(m_CacheData[Idx].m_BlockMetas, a_ChunkDesc.GetBlockMetasUncompressed(), sizeof(a_ChunkDesc.GetBlockMetasUncompressed())); + m_CacheData[Idx].m_ChunkX = ChunkX; + m_CacheData[Idx].m_ChunkZ = ChunkZ; +} + + + + + +void cCompoGenCache::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_Underlying.InitializeCompoGen(a_IniFile); +} + + + + diff --git a/src/Generating/CompoGen.h b/src/Generating/CompoGen.h new file mode 100644 index 000000000..2ee286b06 --- /dev/null +++ b/src/Generating/CompoGen.h @@ -0,0 +1,182 @@ + +// CompoGen.h + +/* Interfaces to the various terrain composition generators: + - cCompoGenSameBlock + - cCompoGenDebugBiomes + - cCompoGenClassic + - cCompoGenBiomal + - cCompoGenNether + - cCompoGenCache +*/ + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cCompoGenSameBlock : + public cTerrainCompositionGen +{ +public: + cCompoGenSameBlock(void) : + m_BlockType(E_BLOCK_STONE), + m_IsBedrocked(true) + {} + +protected: + + BLOCKTYPE m_BlockType; + bool m_IsBedrocked; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; +} ; + + + + + +class cCompoGenDebugBiomes : + public cTerrainCompositionGen +{ +public: + cCompoGenDebugBiomes(void) {} + +protected: + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cCompoGenClassic : + public cTerrainCompositionGen +{ +public: + cCompoGenClassic(void); + +protected: + + int m_SeaLevel; + int m_BeachHeight; + int m_BeachDepth; + BLOCKTYPE m_BlockTop; + BLOCKTYPE m_BlockMiddle; + BLOCKTYPE m_BlockBottom; + BLOCKTYPE m_BlockBeach; + BLOCKTYPE m_BlockBeachBottom; + BLOCKTYPE m_BlockSea; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; +} ; + + + + + +class cCompoGenBiomal : + public cTerrainCompositionGen +{ +public: + cCompoGenBiomal(int a_Seed) : + m_Noise(a_Seed + 1000), + m_SeaLevel(62) + { + } + +protected: + + cNoise m_Noise; + int m_SeaLevel; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; + + void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + void FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + void FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + void FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + + void FillColumnPattern (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize); +} ; + + + + + +class cCompoGenNether : + public cTerrainCompositionGen +{ +public: + cCompoGenNether(int a_Seed); + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + + int m_Threshold; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; +} ; + + + + + +/// Caches most-recently-used chunk composition of another composition generator. Caches only the types and metas +class cCompoGenCache : + public cTerrainCompositionGen +{ +public: + cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize); // Doesn't take ownership of a_Underlying + ~cCompoGenCache(); + + // cTerrainCompositionGen override: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; + +protected: + + cTerrainCompositionGen & m_Underlying; + + struct sCacheData + { + int m_ChunkX; + int m_ChunkZ; + cChunkDef::BlockTypes m_BlockTypes; + cChunkDesc::BlockNibbleBytes m_BlockMetas; // The metas are uncompressed, 1 meta per byte + } ; + + // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data + int m_CacheSize; + int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array + sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used + + // Cache statistics + int m_NumHits; + int m_NumMisses; + int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) +} ; + + + + diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp new file mode 100644 index 000000000..2637b64e7 --- /dev/null +++ b/src/Generating/ComposableGenerator.cpp @@ -0,0 +1,501 @@ + +// ComposableGenerator.cpp + +// Implements the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks + +#include "Globals.h" + +#include "ComposableGenerator.h" +#include "../World.h" +#include "../../iniFile/iniFile.h" +#include "../Root.h" + +// Individual composed algorithms: +#include "BioGen.h" +#include "HeiGen.h" +#include "CompoGen.h" +#include "StructGen.h" +#include "FinishGen.h" + +#include "Caves.h" +#include "DistortedHeightmap.h" +#include "EndGen.h" +#include "MineShafts.h" +#include "Noise3DGenerator.h" +#include "Ravines.h" + + + + + + + + + + +cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) : + super(a_ChunkGenerator), + m_BiomeGen(NULL), + m_HeightGen(NULL), + m_CompositionGen(NULL), + m_UnderlyingBiomeGen(NULL), + m_UnderlyingHeightGen(NULL), + m_UnderlyingCompositionGen(NULL) +{ +} + + + + + +cComposableGenerator::~cComposableGenerator() +{ + // Delete the generating composition: + for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) + { + delete *itr; + } + m_FinishGens.clear(); + for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) + { + delete *itr; + } + m_StructureGens.clear(); + + delete m_CompositionGen; + m_CompositionGen = NULL; + delete m_HeightGen; + m_HeightGen = NULL; + delete m_BiomeGen; + m_BiomeGen = NULL; + delete m_UnderlyingCompositionGen; + m_UnderlyingCompositionGen = NULL; + delete m_UnderlyingHeightGen; + m_UnderlyingHeightGen = NULL; + delete m_UnderlyingBiomeGen; + m_UnderlyingBiomeGen = NULL; +} + + + + + +void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +{ + super::Initialize(a_World, a_IniFile); + + InitBiomeGen(a_IniFile); + InitHeightGen(a_IniFile); + InitCompositionGen(a_IniFile); + InitStructureGens(a_IniFile); + InitFinishGens(a_IniFile); +} + + + + + +void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + if (m_BiomeGen != NULL) // Quick fix for generator deinitializing before the world storage finishes loading + { + m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); + } +} + + + + + +void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) +{ + if (a_ChunkDesc.IsUsingDefaultBiomes()) + { + m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap()); + } + + if (a_ChunkDesc.IsUsingDefaultHeight()) + { + m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap()); + } + + if (a_ChunkDesc.IsUsingDefaultComposition()) + { + m_CompositionGen->ComposeTerrain(a_ChunkDesc); + } + + if (a_ChunkDesc.IsUsingDefaultStructures()) + { + for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) + { + (*itr)->GenStructures(a_ChunkDesc); + } // for itr - m_StructureGens[] + } + + if (a_ChunkDesc.IsUsingDefaultFinish()) + { + for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) + { + (*itr)->GenFinish(a_ChunkDesc); + } // for itr - m_FinishGens[] + } +} + + + + + +void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile) +{ + AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", ""); + if (BiomeGenName.empty()) + { + LOGWARN("[Generator] BiomeGen value not set in world.ini, using \"MultiStepMap\"."); + BiomeGenName = "MultiStepMap"; + } + + int Seed = m_ChunkGenerator.GetSeed(); + bool CacheOffByDefault = false; + if (NoCaseCompare(BiomeGenName, "constant") == 0) + { + m_BiomeGen = new cBioGenConstant; + CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :) + } + else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0) + { + m_BiomeGen = new cBioGenCheckerboard; + CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data + } + else if (NoCaseCompare(BiomeGenName, "voronoi") == 0) + { + m_BiomeGen = new cBioGenVoronoi(Seed); + } + else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0) + { + m_BiomeGen = new cBioGenDistortedVoronoi(Seed); + } + else + { + if (NoCaseCompare(BiomeGenName, "multistepmap") != 0) + { + LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str()); + } + m_BiomeGen = new cBioGenMultiStepMap(Seed); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cBioGenMultiStepMap..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 5000; x++) + { + cChunkDef::BiomeMap Biomes; + m_BiomeGen->GenBiomes(x * 5, x * 5, Biomes); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + + // Add a cache, if requested: + int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64); + if (CacheSize > 0) + { + if (CacheSize < 4) + { + LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", + CacheSize, 4 + ); + CacheSize = 4; + } + LOGD("Using a cache for biomegen of size %d.", CacheSize); + m_UnderlyingBiomeGen = m_BiomeGen; + m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize); + } + m_BiomeGen->InitializeBiomeGen(a_IniFile); +} + + + + + +void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) +{ + AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", ""); + if (HeightGenName.empty()) + { + LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\"."); + HeightGenName = "Biomal"; + } + + int Seed = m_ChunkGenerator.GetSeed(); + bool CacheOffByDefault = false; + if (NoCaseCompare(HeightGenName, "flat") == 0) + { + m_HeightGen = new cHeiGenFlat; + CacheOffByDefault = true; // We're generating faster than a cache would retrieve data + } + else if (NoCaseCompare(HeightGenName, "classic") == 0) + { + m_HeightGen = new cHeiGenClassic(Seed); + } + else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) + { + m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen); + } + else if (NoCaseCompare(HeightGenName, "End") == 0) + { + m_HeightGen = new cEndGen(Seed); + } + else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) + { + m_HeightGen = new cNoise3DComposable(Seed); + } + else // "biomal" or + { + if (NoCaseCompare(HeightGenName, "biomal") != 0) + { + LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); + } + m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cHeiGenBiomal..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 500; x++) + { + cChunkDef::HeightMap Heights; + m_HeightGen->GenHeightMap(x * 5, x * 5, Heights); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + + // Read the settings: + m_HeightGen->InitializeHeightGen(a_IniFile); + + // Add a cache, if requested: + int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64); + if (CacheSize > 0) + { + if (CacheSize < 4) + { + LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", + CacheSize, 4 + ); + CacheSize = 4; + } + LOGD("Using a cache for Heightgen of size %d.", CacheSize); + m_UnderlyingHeightGen = m_HeightGen; + m_HeightGen = new cHeiGenCache(*m_UnderlyingHeightGen, CacheSize); + } +} + + + + + +void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) +{ + int Seed = m_ChunkGenerator.GetSeed(); + AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", ""); + if (CompoGenName.empty()) + { + LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\"."); + CompoGenName = "Biomal"; + } + if (NoCaseCompare(CompoGenName, "sameblock") == 0) + { + m_CompositionGen = new cCompoGenSameBlock; + } + else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) + { + m_CompositionGen = new cCompoGenDebugBiomes; + } + else if (NoCaseCompare(CompoGenName, "classic") == 0) + { + m_CompositionGen = new cCompoGenClassic; + } + else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) + { + m_CompositionGen = new cDistortedHeightmap(Seed, *m_BiomeGen); + } + else if (NoCaseCompare(CompoGenName, "end") == 0) + { + m_CompositionGen = new cEndGen(Seed); + } + else if (NoCaseCompare(CompoGenName, "nether") == 0) + { + m_CompositionGen = new cCompoGenNether(Seed); + } + else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) + { + m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed()); + } + else + { + if (NoCaseCompare(CompoGenName, "biomal") != 0) + { + LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str()); + } + m_CompositionGen = new cCompoGenBiomal(Seed); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cCompoGenBiomal..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 500; x++) + { + cChunkDesc Desc(200 + x * 8, 200 + x * 8); + m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap()); + m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap()); + m_CompositionGen->ComposeTerrain(Desc); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + + // Read the settings from the ini file: + m_CompositionGen->InitializeCompoGen(a_IniFile); + + int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64); + if (CompoGenCacheSize > 1) + { + m_UnderlyingCompositionGen = m_CompositionGen; + m_CompositionGen = new cCompoGenCache(*m_UnderlyingCompositionGen, 32); + } +} + + + + + +void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile) +{ + AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees"); + + int Seed = m_ChunkGenerator.GetSeed(); + AStringVector Str = StringSplitAndTrim(Structures, ","); + for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) + { + if (NoCaseCompare(*itr, "DualRidgeCaves") == 0) + { + float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3); + m_StructureGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold)); + } + else if (NoCaseCompare(*itr, "DirectOverhangs") == 0) + { + m_StructureGens.push_back(new cStructGenDirectOverhangs(Seed)); + } + else if (NoCaseCompare(*itr, "DistortedMembraneOverhangs") == 0) + { + m_StructureGens.push_back(new cStructGenDistortedMembraneOverhangs(Seed)); + } + else if (NoCaseCompare(*itr, "LavaLakes") == 0) + { + int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10); + m_StructureGens.push_back(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, *m_HeightGen, Probability)); + } + else if (NoCaseCompare(*itr, "MarbleCaves") == 0) + { + m_StructureGens.push_back(new cStructGenMarbleCaves(Seed)); + } + else if (NoCaseCompare(*itr, "MineShafts") == 0) + { + int GridSize = a_IniFile.GetValueSetI("Generator", "MineShaftsGridSize", 512); + int MaxSystemSize = a_IniFile.GetValueSetI("Generator", "MineShaftsMaxSystemSize", 160); + int ChanceCorridor = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCorridor", 600); + int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200); + int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200); + m_StructureGens.push_back(new cStructGenMineShafts( + Seed, GridSize, MaxSystemSize, + ChanceCorridor, ChanceCrossing, ChanceStaircase + )); + } + else if (NoCaseCompare(*itr, "OreNests") == 0) + { + m_StructureGens.push_back(new cStructGenOreNests(Seed)); + } + else if (NoCaseCompare(*itr, "Ravines") == 0) + { + m_StructureGens.push_back(new cStructGenRavines(Seed, 128)); + } + else if (NoCaseCompare(*itr, "Trees") == 0) + { + m_StructureGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen)); + } + else if (NoCaseCompare(*itr, "WaterLakes") == 0) + { + int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25); + m_StructureGens.push_back(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, *m_HeightGen, Probability)); + } + else if (NoCaseCompare(*itr, "WormNestCaves") == 0) + { + m_StructureGens.push_back(new cStructGenWormNestCaves(Seed)); + } + else + { + LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str()); + } + } // for itr - Str[] +} + + + + + +void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) +{ + int Seed = m_ChunkGenerator.GetSeed(); + AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator"); + + AStringVector Str = StringSplitAndTrim(Structures, ","); + for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) + { + // Finishers, alpha-sorted: + if (NoCaseCompare(*itr, "BottomLava") == 0) + { + int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10; + int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel); + m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel)); + } + else if (NoCaseCompare(*itr, "DeadBushes") == 0) + { + m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND)); + } + else if (NoCaseCompare(*itr, "Ice") == 0) + { + m_FinishGens.push_back(new cFinishGenIce); + } + else if (NoCaseCompare(*itr, "LavaSprings") == 0) + { + m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World)); + } + else if (NoCaseCompare(*itr, "Lilypads") == 0) + { + m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER)); + } + else if (NoCaseCompare(*itr, "PreSimulator") == 0) + { + m_FinishGens.push_back(new cFinishGenPreSimulator); + } + else if (NoCaseCompare(*itr, "Snow") == 0) + { + m_FinishGens.push_back(new cFinishGenSnow); + } + else if (NoCaseCompare(*itr, "SprinkleFoliage") == 0) + { + m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed)); + } + else if (NoCaseCompare(*itr, "WaterSprings") == 0) + { + m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World)); + } + } // for itr - Str[] +} + + + + diff --git a/src/Generating/ComposableGenerator.h b/src/Generating/ComposableGenerator.h new file mode 100644 index 000000000..d5e33a439 --- /dev/null +++ b/src/Generating/ComposableGenerator.h @@ -0,0 +1,181 @@ + +// ComposableGenerator.h + +// Declares the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks + +/* +Generating works by composing several algorithms: +Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage +Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others, +based on user's preferences in the world.ini. +See http://forum.mc-server.org/showthread.php?tid=409 for details. +*/ + + + + + +#pragma once + +#include "ChunkGenerator.h" +#include "ChunkDesc.h" + + + + + +// fwd: Noise3DGenerator.h +class cNoise3DComposable; + +// fwd: DistortedHeightmap.h +class cDistortedHeightmap; + + + + + +/** The interface that a biome generator must implement +A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output. +The output array is sequenced in the same way as the MapChunk packet's biome data. +*/ +class cBiomeGen +{ +public: + virtual ~cBiomeGen() {} // Force a virtual destructor in descendants + + /// Generates biomes for the given chunk + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeBiomeGen(cIniFile & a_IniFile) {} +} ; + + + + + +/** The interface that a terrain height generator must implement +A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk. +The output array is sequenced in the same way as the BiomeGen's biome data. +The generator may request biome information from the underlying BiomeGen, it may even request information for +other chunks than the one it's currently generating (possibly neighbors - for averaging) +*/ +class cTerrainHeightGen +{ +public: + virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants + + /// Generates heightmap for the given chunk + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeHeightGen(cIniFile & a_IniFile) {} +} ; + + + + + +/** The interface that a terrain composition generator must implement +Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with +the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose, +but it may request information for other chunks than the one it's currently generating from them. +*/ +class cTerrainCompositionGen +{ +public: + virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants + + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeCompoGen(cIniFile & a_IniFile) {} +} ; + + + + + +/** The interface that a structure generator must implement +Structures are generated after the terrain composition took place. It should modify the blocktype data to account +for whatever structures the generator is generating. +Note that ores are considered structures too, at least from the interface point of view. +Also note that a worldgenerator may contain multiple structure generators, one for each type of structure +*/ +class cStructureGen +{ +public: + virtual ~cStructureGen() {} // Force a virtual destructor in descendants + + virtual void GenStructures(cChunkDesc & a_ChunkDesc) = 0; +} ; + +typedef std::list cStructureGenList; + + + + + +/** The interface that a finisher must implement +Finisher implements small additions after all structures have been generated. +*/ +class cFinishGen +{ +public: + virtual ~cFinishGen() {} // Force a virtual destructor in descendants + + virtual void GenFinish(cChunkDesc & a_ChunkDesc) = 0; +} ; + +typedef std::list cFinishGenList; + + + + + +class cComposableGenerator : + public cChunkGenerator::cGenerator +{ + typedef cChunkGenerator::cGenerator super; + +public: + cComposableGenerator(cChunkGenerator & a_ChunkGenerator); + virtual ~cComposableGenerator(); + + virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; + virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; + +protected: + // The generation composition: + cBiomeGen * m_BiomeGen; + cTerrainHeightGen * m_HeightGen; + cTerrainCompositionGen * m_CompositionGen; + cStructureGenList m_StructureGens; + cFinishGenList m_FinishGens; + + // Generators underlying the caches: + cBiomeGen * m_UnderlyingBiomeGen; + cTerrainHeightGen * m_UnderlyingHeightGen; + cTerrainCompositionGen * m_UnderlyingCompositionGen; + + + /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly + void InitBiomeGen(cIniFile & a_IniFile); + + /// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly + void InitHeightGen(cIniFile & a_IniFile); + + /// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly + void InitCompositionGen(cIniFile & a_IniFile); + + /// Reads the structures to generate from the ini and initializes m_StructureGens accordingly + void InitStructureGens(cIniFile & a_IniFile); + + /// Reads the finishers from the ini and initializes m_FinishGens accordingly + void InitFinishGens(cIniFile & a_IniFile); +} ; + + + + diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp new file mode 100644 index 000000000..98eab31b5 --- /dev/null +++ b/src/Generating/DistortedHeightmap.cpp @@ -0,0 +1,444 @@ + +// DistortedHeightmap.cpp + +// Implements the cDistortedHeightmap class representing the height and composition generator capable of overhangs + +#include "Globals.h" + +#include "DistortedHeightmap.h" +#include "../OSSupport/File.h" +#include "../../iniFile/iniFile.h" +#include "../LinearUpscale.h" + + + + + +/** This table assigns a relative maximum overhang size in each direction to biomes. +Both numbers indicate a number which will multiply the noise value for each coord; +this means that you can have different-sized overhangs in each direction. +Usually you'd want to keep both numbers the same. +The numbers are "relative", not absolute maximum; overhangs of a slightly larger size are possible +due to the way that noise is calculated. +*/ +const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[biNumBiomes] = +{ + /* Biome | AmpX | AmpZ */ + /* biOcean */ { 1.5f, 1.5f}, + /* biPlains */ { 0.5f, 0.5f}, + /* biDesert */ { 0.5f, 0.5f}, + /* biExtremeHills */ {16.0f, 16.0f}, + /* biForest */ { 3.0f, 3.0f}, + /* biTaiga */ { 1.5f, 1.5f}, + + /* biSwampland */ { 0.0f, 0.0f}, + /* biRiver */ { 0.0f, 0.0f}, + /* biNether */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing + /* biSky */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing + /* biFrozenOcean */ { 0.0f, 0.0f}, + /* biFrozenRiver */ { 0.0f, 0.0f}, + /* biIcePlains */ { 0.0f, 0.0f}, + /* biIceMountains */ { 8.0f, 8.0f}, + /* biMushroomIsland */ { 4.0f, 4.0f}, + /* biMushroomShore */ { 0.0f, 0.0f}, + /* biBeach */ { 0.0f, 0.0f}, + /* biDesertHills */ { 5.0f, 5.0f}, + /* biForestHills */ { 6.0f, 6.0f}, + /* biTaigaHills */ { 8.0f, 8.0f}, + /* biExtremeHillsEdge */ { 7.0f, 7.0f}, + /* biJungle */ { 0.0f, 0.0f}, + /* biJungleHills */ { 8.0f, 8.0f}, +} ; + + + + + +cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) : + m_NoiseDistortX(a_Seed + 1000), + m_NoiseDistortZ(a_Seed + 2000), + m_OceanFloorSelect(a_Seed + 3000), + m_BiomeGen(a_BiomeGen), + m_UnderlyingHeiGen(a_Seed, a_BiomeGen), + m_HeightGen(m_UnderlyingHeiGen, 64) +{ + m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); + m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); + m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); + + m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); + m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); + m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); +} + + + + + +void cDistortedHeightmap::Initialize(cIniFile & a_IniFile) +{ + if (m_IsInitialized) + { + return; + } + + // Read the params from the INI file: + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62); + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10); + + m_IsInitialized = true; +} + + + + + +void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ) +{ + if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ)) + { + return; + } + m_CurChunkX = a_ChunkX; + m_CurChunkZ = a_ChunkZ; + + + m_HeightGen.GenHeightMap(a_ChunkX, a_ChunkZ, m_CurChunkHeights); + UpdateDistortAmps(); + GenerateHeightArray(); +} + + + + + +void cDistortedHeightmap::GenerateHeightArray(void) +{ + // Generate distortion noise: + NOISE_DATATYPE DistortNoiseX[DIM_X * DIM_Y * DIM_Z]; + NOISE_DATATYPE DistortNoiseZ[DIM_X * DIM_Y * DIM_Z]; + NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; + NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_CurChunkX + 1) * cChunkDef::Width - 1)) / m_FrequencyX; + NOISE_DATATYPE StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)(257)) / m_FrequencyY; + NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_CurChunkZ + 1) * cChunkDef::Width - 1)) / m_FrequencyZ; + + m_NoiseDistortX.Generate3D(DistortNoiseX, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); + m_NoiseDistortZ.Generate3D(DistortNoiseZ, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); + + // The distorted heightmap, before linear upscaling + NOISE_DATATYPE DistHei[DIM_X * DIM_Y * DIM_Z]; + + // Distort the heightmap using the distortion: + for (int z = 0; z < DIM_Z; z++) + { + int AmpIdx = z * DIM_X; + for (int y = 0; y < DIM_Y; y++) + { + int NoiseArrayIdx = z * DIM_X * DIM_Y + y * DIM_X; + for (int x = 0; x < DIM_X; x++) + { + NOISE_DATATYPE DistX = DistortNoiseX[NoiseArrayIdx + x] * m_DistortAmpX[AmpIdx + x]; + NOISE_DATATYPE DistZ = DistortNoiseZ[NoiseArrayIdx + x] * m_DistortAmpZ[AmpIdx + x]; + DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x * INTERPOL_X); + DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z * INTERPOL_Z); + // Adding 0.5 helps alleviate the interpolation artifacts + DistHei[NoiseArrayIdx + x] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5; + } + } + } + + // Upscale the distorted heightmap into full dimensions: + LinearUpscale3DArray( + DistHei, DIM_X, DIM_Y, DIM_Z, + m_DistortedHeightmap, INTERPOL_X, INTERPOL_Y, INTERPOL_Z + ); + + // DEBUG: Debug3DNoise(m_DistortedHeightmap, 17, 257, 17, Printf("DistortedHeightmap_%d_%d", m_CurChunkX, m_CurChunkZ)); +} + + + + + +void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + PrepareState(a_ChunkX, a_ChunkZ); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int NoiseArrayIdx = x + 17 * 257 * z; + cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1); + for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--) + { + int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; + if (y < HeightMapHeight) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile) +{ + Initialize(a_IniFile); +} + + + + + +void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + // Frequencies for the ocean floor selecting noise: + NOISE_DATATYPE FrequencyX = 3; + NOISE_DATATYPE FrequencyZ = 3; + + // Prepare the internal state for generating this chunk: + PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + // Compose: + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int NoiseArrayIdx = x + 17 * 257 * z; + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir - 1; y > 0; y--) + { + int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; + + if (y >= HeightMapHeight) + { + // "air" part + LastAir = y; + if (y < m_SeaLevel) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + HasHadWater = true; + } + continue; + } + // "ground" part: + if (y < LastAir - 4) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); + continue; + } + if (HasHadWater) + { + // Decide between clay, sand and dirt + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / FrequencyX; + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / FrequencyZ; + NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); + if (Val < -0.95) + { + // Clay: + switch (LastAir - y) + { + case 0: + case 1: + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_CLAY); + break; + } + case 2: + case 3: + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); + break; + } + case 4: + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SANDSTONE); + break; + } + } // switch (floor depth) + } + else if (Val < 0) + { + a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); + } + else + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_DIRT); + } + } + else + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biOcean: + case biPlains: + case biExtremeHills: + case biForest: + case biTaiga: + case biSwampland: + case biRiver: + case biFrozenOcean: + case biFrozenRiver: + case biIcePlains: + case biIceMountains: + case biForestHills: + case biTaigaHills: + case biExtremeHillsEdge: + case biJungle: + case biJungleHills: + { + a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); + break; + } + case biDesertHills: + case biDesert: + case biBeach: + { + a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); + break; + } + case biMushroomIsland: + case biMushroomShore: + { + a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_MYCELIUM : E_BLOCK_DIRT); + break; + } + } + } + } // for y + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile) +{ + Initialize(a_IniFile); +} + + + + + +int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z) +{ + int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16); + int ChunkZ = (int)floor(a_Z / (NOISE_DATATYPE)16); + int RelX = (int)(a_X - (NOISE_DATATYPE)ChunkX * cChunkDef::Width); + int RelZ = (int)(a_Z - (NOISE_DATATYPE)ChunkZ * cChunkDef::Width); + + // If we're withing the same chunk, return the pre-cached heightmap: + if ((ChunkX == m_CurChunkX) && (ChunkZ == m_CurChunkZ)) + { + return cChunkDef::GetHeight(m_CurChunkHeights, RelX, RelZ); + } + + // Ask the cache: + HEIGHTTYPE res = 0; + if (m_HeightGen.GetHeightAt(ChunkX, ChunkZ, RelX, RelZ, res)) + { + // The height was in the cache + return res; + } + + // The height is not in the cache, generate full heightmap and get it there: + cChunkDef::HeightMap Heightmap; + m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap); + return cChunkDef::GetHeight(Heightmap, RelX, RelZ); +} + + + + + +void cDistortedHeightmap::UpdateDistortAmps(void) +{ + BiomeNeighbors Biomes; + for (int z = -1; z <= 1; z++) + { + for (int x = -1; x <= 1; x++) + { + m_BiomeGen.GenBiomes(m_CurChunkX + x, m_CurChunkZ + z, Biomes[x + 1][z + 1]); + } // for x + } // for z + + for (int z = 0; z < DIM_Z; z++) + { + for (int x = 0; x < DIM_Z; x++) + { + GetDistortAmpsAt(Biomes, x * INTERPOL_X, z * INTERPOL_Z, m_DistortAmpX[x + DIM_X * z], m_DistortAmpZ[x + DIM_X * z]); + } + } +} + + + + + +void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ) +{ + // Sum up how many biomes of each type there are in the neighborhood: + int BiomeCounts[biNumBiomes]; + memset(BiomeCounts, 0, sizeof(BiomeCounts)); + int Sum = 0; + for (int z = -8; z <= 8; z++) + { + int FinalZ = a_RelZ + z + cChunkDef::Width; + int IdxZ = FinalZ / cChunkDef::Width; + int ModZ = FinalZ % cChunkDef::Width; + int WeightZ = 9 - abs(z); + for (int x = -8; x <= 8; x++) + { + int FinalX = a_RelX + x + cChunkDef::Width; + int IdxX = FinalX / cChunkDef::Width; + int ModX = FinalX % cChunkDef::Width; + EMCSBiome Biome = cChunkDef::GetBiome(a_Neighbors[IdxX][IdxZ], ModX, ModZ); + if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts))) + { + continue; + } + int WeightX = 9 - abs(x); + BiomeCounts[Biome] += WeightX + WeightZ; + Sum += WeightX + WeightZ; + } // for x + } // for z + + if (Sum <= 0) + { + // No known biome around? Weird. Return a bogus value: + ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around"); + a_DistortAmpX = 16; + a_DistortAmpZ = 16; + } + + // For each biome type that has a nonzero count, calc its amps and add it: + NOISE_DATATYPE AmpX = 0; + NOISE_DATATYPE AmpZ = 0; + for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++) + { + AmpX += BiomeCounts[i] * m_GenParam[i].m_DistortAmpX; + AmpZ += BiomeCounts[i] * m_GenParam[i].m_DistortAmpZ; + } + a_DistortAmpX = AmpX / Sum; + a_DistortAmpZ = AmpZ / Sum; +} + + + + diff --git a/src/Generating/DistortedHeightmap.h b/src/Generating/DistortedHeightmap.h new file mode 100644 index 000000000..6d7007375 --- /dev/null +++ b/src/Generating/DistortedHeightmap.h @@ -0,0 +1,108 @@ + +// DistortedHeightmap.h + +// Declares the cDistortedHeightmap class representing the height and composition generator capable of overhangs + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "HeiGen.h" +#include "../Noise.h" + + + + + +#define NOISE_SIZE_Y (257 + 32) + + + + + +class cDistortedHeightmap : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen); + +protected: + typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; + + // Linear upscaling step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: + static const int INTERPOL_X = 8; + static const int INTERPOL_Y = 4; + static const int INTERPOL_Z = 8; + + // Linear upscaling buffer dimensions, calculated from the step sizes: + static const int DIM_X = 1 + (17 / INTERPOL_X); + static const int DIM_Y = 1 + (257 / INTERPOL_Y); + static const int DIM_Z = 1 + (17 / INTERPOL_Z); + + cPerlinNoise m_NoiseDistortX; + cPerlinNoise m_NoiseDistortZ; + cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor + + int m_SeaLevel; + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + + int m_CurChunkX; + int m_CurChunkZ; + NOISE_DATATYPE m_DistortedHeightmap[17 * 257 * 17]; + + cBiomeGen & m_BiomeGen; + cHeiGenBiomal m_UnderlyingHeiGen; // This generator provides us with base heightmap (before distortion) + cHeiGenCache m_HeightGen; // Cache above m_UnderlyingHeiGen + + /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization. + cChunkDef::HeightMap m_CurChunkHeights; + + // Per-biome terrain generator parameters: + struct sGenParam + { + NOISE_DATATYPE m_DistortAmpX; + NOISE_DATATYPE m_DistortAmpZ; + } ; + static const sGenParam m_GenParam[biNumBiomes]; + + // Distortion amplitudes for each direction, before linear upscaling + NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z]; + NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z]; + + /// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen) + bool m_IsInitialized; + + + /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap) + void PrepareState(int a_ChunkX, int a_ChunkZ); + + /// Generates the m_DistortedHeightmap array for the current chunk + void GenerateHeightArray(void); + + /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords + int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z); + + /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ + void UpdateDistortAmps(void); + + /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes + void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ); + + /// Reads the settings from the ini file. Skips reading if already initialized + void Initialize(cIniFile & a_IniFile); + + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; +} ; diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp new file mode 100644 index 000000000..3eba5c47b --- /dev/null +++ b/src/Generating/EndGen.cpp @@ -0,0 +1,217 @@ + +// EndGen.cpp + +// Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen + +#include "Globals.h" +#include "EndGen.h" +#include "../../iniFile/iniFile.h" +#include "../LinearUpscale.h" + + + + + +enum +{ + // Interpolation cell size: + INTERPOL_X = 4, + INTERPOL_Y = 4, + INTERPOL_Z = 4, + + // Size of chunk data, downscaled before interpolation: + DIM_X = 16 / INTERPOL_X + 1, + DIM_Y = 256 / INTERPOL_Y + 1, + DIM_Z = 16 / INTERPOL_Z + 1, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cEndGen: + +cEndGen::cEndGen(int a_Seed) : + m_Seed(a_Seed), + m_IslandSizeX(256), + m_IslandSizeY(96), + m_IslandSizeZ(256), + m_FrequencyX(80), + m_FrequencyY(80), + m_FrequencyZ(80) +{ + m_Perlin.AddOctave(1, 1); + m_Perlin.AddOctave(2, 0.5); + m_Perlin.AddOctave(4, 0.25); +} + + + + + +void cEndGen::Initialize(cIniFile & a_IniFile) +{ + m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX); + m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY); + m_IslandSizeZ = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeZ", m_IslandSizeZ); + + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyX", m_FrequencyX); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyY", m_FrequencyY); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyZ", m_FrequencyZ); + + // Recalculate the min and max chunk coords of the island + m_MaxChunkX = (m_IslandSizeX + cChunkDef::Width - 1) / cChunkDef::Width; + m_MinChunkX = -m_MaxChunkX; + m_MaxChunkZ = (m_IslandSizeZ + cChunkDef::Width - 1) / cChunkDef::Width; + m_MinChunkZ = -m_MaxChunkZ; +} + + + + + +/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) +void cEndGen::PrepareState(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(!IsChunkOutsideRange(a_ChunkX, a_ChunkZ)); // Should be filtered before calling this function + + if ((m_LastChunkX == a_ChunkX) && (m_LastChunkZ == a_ChunkZ)) + { + return; + } + + m_LastChunkX = a_ChunkX; + m_LastChunkZ = a_ChunkZ; + + GenerateNoiseArray(); +} + + + + + +/// Generates the m_NoiseArray array for the current chunk +void cEndGen::GenerateNoiseArray(void) +{ + NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] + NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] + + // Generate the downscaled noise: + NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_LastChunkX + 1) * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_LastChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)257) / m_FrequencyY; + m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace); + + // Add distance: + int idx = 0; + for (int y = 0; y < DIM_Y; y++) + { + NOISE_DATATYPE ValY = (NOISE_DATATYPE)(2 * INTERPOL_Y * y - m_IslandSizeY) / m_IslandSizeY; + ValY = ValY * ValY; + for (int z = 0; z < DIM_Z; z++) + { + NOISE_DATATYPE ValZ = (NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width + (z * cChunkDef::Width / (DIM_Z - 1))) / m_IslandSizeZ; + ValZ = ValZ * ValZ; + for (int x = 0; x < DIM_X; x++) + { + // NOISE_DATATYPE ValX = StartX + (EndX - StartX) * x / (DIM_X - 1); + NOISE_DATATYPE ValX = (NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width + (x * cChunkDef::Width / (DIM_X - 1))) / m_IslandSizeX; + ValX = ValX * ValX; + NoiseData[idx++] += ValX + ValZ + ValY; + } // for x + } // for z + } // for y + + // Upscale into real chunk size: + LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y); +} + + + + + +/// Returns true if the chunk is outside of the island's dimensions +bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ) +{ + return ( + (a_ChunkX < m_MinChunkX) || (a_ChunkX > m_MaxChunkX) || + (a_ChunkZ < m_MinChunkZ) || (a_ChunkZ > m_MaxChunkZ) + ); +} + + + + + +void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ)) + { + for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++) + { + a_HeightMap[i] = 0; + } + return; + } + + PrepareState(a_ChunkX, a_ChunkZ); + + int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, MaxY); + for (int y = MaxY; y > 0; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ())) + { + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + return; + } + + PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int y = MaxY; y > 0; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) + { + a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0); + } + else + { + a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0); + } + } // for y + } // for x + } // for z +} + + + + diff --git a/src/Generating/EndGen.h b/src/Generating/EndGen.h new file mode 100644 index 000000000..4904a0e3d --- /dev/null +++ b/src/Generating/EndGen.h @@ -0,0 +1,69 @@ + +// EndGen.h + +// Declares the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cEndGen : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cEndGen(int a_Seed); + + void Initialize(cIniFile & a_IniFile); + +protected: + + /// Seed for the noise + int m_Seed; + + /// The Perlin noise used for generating + cPerlinNoise m_Perlin; + + // XYZ size of the "island", in blocks: + int m_IslandSizeX; + int m_IslandSizeY; + int m_IslandSizeZ; + + // XYZ Frequencies of the noise functions: + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + + // Minimum and maximum chunk coords for chunks inside the island area. Chunks outside won't get calculated at all + int m_MinChunkX, m_MaxChunkX; + int m_MinChunkZ, m_MaxChunkZ; + + // Noise array for the last chunk (in the noise range) + int m_LastChunkX; + int m_LastChunkZ; + NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y + + /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) + void PrepareState(int a_ChunkX, int a_ChunkZ); + + /// Generates the m_NoiseArray array for the current chunk + void GenerateNoiseArray(void); + + /// Returns true if the chunk is outside of the island's dimensions + bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp new file mode 100644 index 000000000..8899e4bd0 --- /dev/null +++ b/src/Generating/FinishGen.cpp @@ -0,0 +1,664 @@ + +// FinishGen.cpp + +/* Implements the various finishing generators: + - cFinishGenSnow + - cFinishGenIce + - cFinishGenSprinkleFoliage +*/ + +#include "Globals.h" + +#include "FinishGen.h" +#include "../Noise.h" +#include "../BlockID.h" +#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway() +#include "../World.h" + + + + + +#define DEF_NETHER_WATER_SPRINGS "0, 1; 255, 1" +#define DEF_NETHER_LAVA_SPRINGS "0, 0; 30, 0; 31, 50; 120, 50; 127, 0" +#define DEF_OVERWORLD_WATER_SPRINGS "0, 0; 10, 10; 11, 75; 16, 83; 20, 83; 24, 78; 32, 62; 40, 40; 44, 15; 48, 7; 56, 2; 64, 1; 255, 0" +#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0" +#define DEF_END_WATER_SPRINGS "0, 1; 255, 1" +#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1" + + + + + +static inline bool IsWater(BLOCKTYPE a_BlockType) +{ + return (a_BlockType == E_BLOCK_STATIONARY_WATER) || (a_BlockType == E_BLOCK_WATER); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenSprinkleFoliage: + +bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ) +{ + // We'll be doing comparison to neighbors, so require the coords to be 1 block away from the chunk edges: + if ( + (a_RelX < 1) || (a_RelX >= cChunkDef::Width - 1) || + (a_RelY < 1) || (a_RelY >= cChunkDef::Height - 2) || + (a_RelZ < 1) || (a_RelZ >= cChunkDef::Width - 1) + ) + { + return false; + } + + // Only allow dirt, grass or sand below sugarcane: + switch (a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ)) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_SAND: + { + break; + } + default: + { + return false; + } + } + + // Water is required next to the block below the sugarcane: + if ( + !IsWater(a_ChunkDesc.GetBlockType(a_RelX - 1, a_RelY, a_RelZ)) && + !IsWater(a_ChunkDesc.GetBlockType(a_RelX + 1, a_RelY, a_RelZ)) && + !IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ - 1)) && + !IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ + 1)) + ) + { + return false; + } + + // All conditions met, place a sugarcane here: + a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE); + return true; +} + + + + + +void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc) +{ + // Generate small foliage (1-block): + + // TODO: Update heightmap with 1-block-tall foliage + for (int z = 0; z < cChunkDef::Width; z++) + { + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z; + const float zz = (float)BlockZ; + for (int x = 0; x < cChunkDef::Width; x++) + { + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width + x; + if (((m_Noise.IntNoise2DInt(BlockX, BlockZ) / 8) % 128) < 124) + { + continue; + } + int Top = a_ChunkDesc.GetHeight(x, z); + if (Top > 250) + { + // Nothing grows above Y=250 + continue; + } + if (a_ChunkDesc.GetBlockType(x, Top + 1, z) != E_BLOCK_AIR) + { + // Space already taken by something else, don't grow here + // WEIRD, since we're using heightmap, so there should NOT be anything above it + continue; + } + + const float xx = (float)BlockX; + float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f ); + float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f ); + switch (a_ChunkDesc.GetBlockType(x, Top, z)) + { + case E_BLOCK_GRASS: + { + float val3 = m_Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10 ); + float val4 = m_Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20 ); + if (val1 + val2 > 0.2f) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_YELLOW_FLOWER); + } + else if (val2 + val3 > 0.2f) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_ROSE); + } + else if (val3 + val4 > 0.2f) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_MUSHROOM); + } + else if (val1 + val4 > 0.2f) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_BROWN_MUSHROOM); + } + else if (val1 + val2 + val3 + val4 < -0.1) + { + a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_TALL_GRASS, E_META_TALL_GRASS_GRASS); + } + else if (TryAddSugarcane(a_ChunkDesc, x, Top, z)) + { + ++Top; + } + else if ((val1 > 0.5) && (val2 < -0.5)) + { + a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_PUMPKIN, (int)(val3 * 8) % 4); + } + break; + } // case E_BLOCK_GRASS + + case E_BLOCK_SAND: + { + int y = Top + 1; + if ( + (x > 0) && (x < cChunkDef::Width - 1) && + (z > 0) && (z < cChunkDef::Width - 1) && + (val1 + val2 > 0.5f) && + (a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) && + (a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) && + (a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) && + (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR) + ) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS); + } + else if (TryAddSugarcane(a_ChunkDesc, x, Top, z)) + { + ++Top; + } + break; + } + } // switch (TopBlock) + a_ChunkDesc.SetHeight(x, z, Top); + } // for y + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenSnow: + +void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc) +{ + // Add a snow block in snowy biomes onto blocks that can be snowed over + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biIcePlains: + case biIceMountains: + case biTaiga: + case biTaigaHills: + case biFrozenRiver: + case biFrozenOcean: + { + int Height = a_ChunkDesc.GetHeight(x, z); + if (g_BlockIsSnowable[a_ChunkDesc.GetBlockType(x, Height, z)]) + { + a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW); + a_ChunkDesc.SetHeight(x, z, Height + 1); + } + break; + } + } + } + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenIce: + +void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc) +{ + // Turn surface water into ice in icy biomes + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biIcePlains: + case biIceMountains: + case biTaiga: + case biTaigaHills: + case biFrozenRiver: + case biFrozenOcean: + { + int Height = a_ChunkDesc.GetHeight(x, z); + switch (a_ChunkDesc.GetBlockType(x, Height, z)) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE); + break; + } + } + break; + } + } + } + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenLilypads: + +int cFinishGenSingleBiomeSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap) +{ + int res = 0; + for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) + { + if (a_BiomeMap[i] == m_Biome) + { + res++; + } + } // for i - a_BiomeMap[] + return m_Amount * res / 256; +} + + + + + +void cFinishGenSingleBiomeSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc) +{ + // Add Lilypads on top of water surface in Swampland + + int NumToGen = GetNumToGen(a_ChunkDesc.GetBiomeMap()); + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + for (int i = 0; i < NumToGen; i++) + { + int x = (m_Noise.IntNoise3DInt(ChunkX + ChunkZ, ChunkZ, i) / 13) % cChunkDef::Width; + int z = (m_Noise.IntNoise3DInt(ChunkX - ChunkZ, i, ChunkZ) / 11) % cChunkDef::Width; + + // Place the block at {x, z} if possible: + if (a_ChunkDesc.GetBiome(x, z) != m_Biome) + { + // Incorrect biome + continue; + } + int Height = a_ChunkDesc.GetHeight(x, z); + if (Height >= cChunkDef::Height) + { + // Too high up + continue; + } + if (a_ChunkDesc.GetBlockType(x, Height + 1, z) != E_BLOCK_AIR) + { + // Not an empty block + continue; + } + BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, Height, z); + if ((BlockBelow == m_AllowedBelow1) || (BlockBelow == m_AllowedBelow2)) + { + a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType); + a_ChunkDesc.SetHeight(x, z, Height + 1); + } + } // for i +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenBottomLava: + +void cFinishGenBottomLava::GenFinish(cChunkDesc & a_ChunkDesc) +{ + cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes(); + for (int y = m_Level; y > 0; y--) + { + for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) + { + int Index = cChunkDef::MakeIndexNoCheck(x, y, z); + if (BlockTypes[Index] == E_BLOCK_AIR) + { + BlockTypes[Index] = E_BLOCK_STATIONARY_LAVA; + } + } // for x, for z + } // for y +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenPreSimulator: + +cFinishGenPreSimulator::cFinishGenPreSimulator(void) +{ + // Nothing needed yet +} + + + + + +void cFinishGenPreSimulator::GenFinish(cChunkDesc & a_ChunkDesc) +{ + CollapseSandGravel(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); + StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); + StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); + // TODO: other operations +} + + + + + +void cFinishGenPreSimulator::CollapseSandGravel( + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data +) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int LastY = -1; + int HeightY = 0; + for (int y = 0; y < cChunkDef::Height; y++) + { + BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z); + switch (Block) + { + default: + { + // Set the last block onto which stuff can fall to this height: + LastY = y; + HeightY = y; + break; + } + case E_BLOCK_AIR: + { + // Do nothing + break; + } + case E_BLOCK_FIRE: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + // Do nothing, only remember this height as potentially highest + HeightY = y; + break; + } + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + { + if (LastY < y - 1) + { + cChunkDef::SetBlock(a_BlockTypes, x, LastY + 1, z, Block); + cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); + } + LastY++; + if (LastY > HeightY) + { + HeightY = LastY; + } + break; + } + } // switch (GetBlock) + } // for y + cChunkDef::SetHeight(a_HeightMap, x, z, HeightY); + } // for x + } // for z +} + + + + + +void cFinishGenPreSimulator::StationarizeFluid( + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::HeightMap & a_HeightMap, // Height map to read + BLOCKTYPE a_Fluid, + BLOCKTYPE a_StationaryFluid +) +{ + // Turn fluid in the middle to stationary, unless it has air or washable block next to it: + for (int z = 1; z < cChunkDef::Width - 1; z++) + { + for (int x = 1; x < cChunkDef::Width - 1; x++) + { + for (int y = cChunkDef::GetHeight(a_HeightMap, x, z); y >= 0; y--) + { + BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z); + if ((Block != a_Fluid) && (Block != a_StationaryFluid)) + { + continue; + } + static const struct + { + int x, y, z; + } Coords[] = + { + {1, 0, 0}, + {-1, 0, 0}, + {0, 0, 1}, + {0, 0, -1}, + {0, -1, 0} + } ; + BLOCKTYPE BlockToSet = a_StationaryFluid; // By default, don't simulate this block + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + if ((y == 0) && (Coords[i].y < 0)) + { + continue; + } + BLOCKTYPE Neighbor = cChunkDef::GetBlock(a_BlockTypes, x + Coords[i].x, y + Coords[i].y, z + Coords[i].z); + if ((Neighbor == E_BLOCK_AIR) || cFluidSimulator::CanWashAway(Neighbor)) + { + // There is an air / washable neighbor, simulate this block + BlockToSet = a_Fluid; + break; + } + } // for i - Coords[] + cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockToSet); + } // for y + } // for x + } // for z + + // Turn fluid at the chunk edges into non-stationary fluid: + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int i = 0; i < cChunkDef::Width; i++) // i stands for both x and z here + { + if (cChunkDef::GetBlock(a_BlockTypes, 0, y, i) == a_StationaryFluid) + { + cChunkDef::SetBlock(a_BlockTypes, 0, y, i, a_Fluid); + } + if (cChunkDef::GetBlock(a_BlockTypes, i, y, 0) == a_StationaryFluid) + { + cChunkDef::SetBlock(a_BlockTypes, i, y, 0, a_Fluid); + } + if (cChunkDef::GetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i) == a_StationaryFluid) + { + cChunkDef::SetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i, a_Fluid); + } + if (cChunkDef::GetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1) == a_StationaryFluid) + { + cChunkDef::SetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1, a_Fluid); + } + } + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenFluidSprings: + +cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World) : + m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other + m_HeightDistribution(255), + m_Fluid(a_Fluid) +{ + bool IsWater = (a_Fluid == E_BLOCK_WATER); + AString SectionName = IsWater ? "WaterSprings" : "LavaSprings"; + AString DefaultHeightDistribution; + int DefaultChance; + switch (a_World.GetDimension()) + { + case dimNether: + { + DefaultHeightDistribution = IsWater ? DEF_NETHER_WATER_SPRINGS : DEF_NETHER_LAVA_SPRINGS; + DefaultChance = IsWater ? 0 : 15; + break; + } + case dimOverworld: + { + DefaultHeightDistribution = IsWater ? DEF_OVERWORLD_WATER_SPRINGS : DEF_OVERWORLD_LAVA_SPRINGS; + DefaultChance = IsWater ? 24 : 9; + break; + } + case dimEnd: + { + DefaultHeightDistribution = IsWater ? DEF_END_WATER_SPRINGS : DEF_END_LAVA_SPRINGS; + DefaultChance = 0; + break; + } + default: + { + ASSERT(!"Unhandled world dimension"); + break; + } + } // switch (dimension) + AString HeightDistribution = a_IniFile.GetValueSet(SectionName, "HeightDistribution", DefaultHeightDistribution); + if (!m_HeightDistribution.SetDefString(HeightDistribution) || (m_HeightDistribution.GetSum() <= 0)) + { + LOGWARNING("[%sSprings]: HeightDistribution is invalid, using the default of \"%s\".", + (a_Fluid == E_BLOCK_WATER) ? "Water" : "Lava", + DefaultHeightDistribution.c_str() + ); + m_HeightDistribution.SetDefString(DefaultHeightDistribution); + } + m_Chance = a_IniFile.GetValueSetI(SectionName, "Chance", DefaultChance); +} + + + + + +void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc) +{ + int ChanceRnd = (m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 512, 256 * a_ChunkDesc.GetChunkZ()) / 13) % 100; + if (ChanceRnd > m_Chance) + { + // Not in this chunk + return; + } + + // Get the height at which to try: + int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11; + Height %= m_HeightDistribution.GetSum(); + Height = m_HeightDistribution.MapValue(Height); + + // Try adding the spring at the height, if unsuccessful, move lower: + for (int y = Height; y > 1; y--) + { + // TODO: randomize the order in which the coords are being checked + for (int z = 1; z < cChunkDef::Width - 1; z++) + { + for (int x = 1; x < cChunkDef::Width - 1; x++) + { + switch (a_ChunkDesc.GetBlockType(x, y, z)) + { + case E_BLOCK_NETHERRACK: + case E_BLOCK_STONE: + { + if (TryPlaceSpring(a_ChunkDesc, x, y, z)) + { + // Succeeded, bail out + return; + } + } + } // switch (BlockType) + } // for x + } // for y + } // for y +} + + + + + +bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z) +{ + // In order to place a spring, it needs exactly one of the XZ neighbors or a below neighbor to be air + // Also, its neighbor on top of it must be non-air + if (a_ChunkDesc.GetBlockType(x, y + 1, z) == E_BLOCK_AIR) + { + return false; + } + + static const struct + { + int x, y, z; + } Coords[] = + { + {-1, 0, 0}, + { 1, 0, 0}, + { 0, -1, 0}, + { 0, 0, -1}, + { 0, 0, 1}, + } ; + int NumAirNeighbors = 0; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + switch (a_ChunkDesc.GetBlockType(x + Coords[i].x, y + Coords[i].y, z + Coords[i].z)) + { + case E_BLOCK_AIR: + { + NumAirNeighbors += 1; + if (NumAirNeighbors > 1) + { + return false; + } + } + } + } + if (NumAirNeighbors == 0) + { + return false; + } + + // Has exactly one air neighbor, place a spring: + a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0); + return true; +} + + + + diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h new file mode 100644 index 000000000..ed7df5909 --- /dev/null +++ b/src/Generating/FinishGen.h @@ -0,0 +1,185 @@ + +// FinishGen.h + +/* Interfaces to the various finishing generators: + - cFinishGenSnow + - cFinishGenIce + - cFinishGenSprinkleFoliage + - cFinishGenLilypads + - cFinishGenBottomLava + - cFinishGenPreSimulator + - cFinishGenDeadBushes +*/ + + + + + +#include "ComposableGenerator.h" +#include "../Noise.h" +#include "../ProbabDistrib.h" + + + + + +class cFinishGenSnow : + public cFinishGen +{ +protected: + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenIce : + public cFinishGen +{ +protected: + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenSprinkleFoliage : + public cFinishGen +{ +public: + cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {} + +protected: + cNoise m_Noise; + int m_Seed; + + /// Tries to place sugarcane at the coords specified, returns true if successful + bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ); + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +/** This class adds a single top block in random positions in the specified biome on top of specified allowed blocks. +Used for: +- Lilypads finisher +- DeadBushes finisher +*/ +class cFinishGenSingleBiomeSingleTopBlock : + public cFinishGen +{ +public: + cFinishGenSingleBiomeSingleTopBlock( + int a_Seed, BLOCKTYPE a_BlockType, EMCSBiome a_Biome, int a_Amount, + BLOCKTYPE a_AllowedBelow1, BLOCKTYPE a_AllowedBelow2 + ) : + m_Noise(a_Seed), + m_BlockType(a_BlockType), + m_Biome(a_Biome), + m_Amount(a_Amount), + m_AllowedBelow1(a_AllowedBelow1), + m_AllowedBelow2(a_AllowedBelow2) + { + } + +protected: + cNoise m_Noise; + BLOCKTYPE m_BlockType; + EMCSBiome m_Biome; + int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns. + BLOCKTYPE m_AllowedBelow1; ///< First of the two blocktypes that are allowed below m_BlockType + BLOCKTYPE m_AllowedBelow2; ///< Second of the two blocktypes that are allowed below m_BlockType + + int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap); + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenBottomLava : + public cFinishGen +{ +public: + cFinishGenBottomLava(int a_Level) : + m_Level(a_Level) + { + } + +protected: + int m_Level; + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenPreSimulator : + public cFinishGen +{ +public: + cFinishGenPreSimulator(void); + +protected: + // Drops hanging sand and gravel down to the ground, recalculates heightmap + void CollapseSandGravel( + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data + ); + + /** For each fluid block: + - if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top) + - all fluid on the chunk's edge is made flowing + */ + void StationarizeFluid( + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::HeightMap & a_HeightMap, // Height map to read + BLOCKTYPE a_Fluid, + BLOCKTYPE a_StationaryFluid + ); + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenFluidSprings : + public cFinishGen +{ +public: + cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World); + +protected: + + cNoise m_Noise; + cProbabDistrib m_HeightDistribution; + BLOCKTYPE m_Fluid; + int m_Chance; ///< Chance, [0..100], that a spring will be generated in a chunk + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; + + /// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful + bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z); +} ; + + + + diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp new file mode 100644 index 000000000..5dee181b7 --- /dev/null +++ b/src/Generating/HeiGen.cpp @@ -0,0 +1,390 @@ + +// HeiGen.cpp + +// Implements the various terrain height generators + +#include "Globals.h" +#include "HeiGen.h" +#include "../LinearUpscale.h" +#include "../../iniFile/iniFile.h" + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHeiGenFlat: + +void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++) + { + a_HeightMap[i] = m_Height; + } +} + + + + + +void cHeiGenFlat::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", m_Height); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHeiGenCache: + +cHeiGenCache::cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize) : + m_HeiGenToCache(a_HeiGenToCache), + m_CacheSize(a_CacheSize), + m_CacheOrder(new int[a_CacheSize]), + m_CacheData(new sCacheData[a_CacheSize]), + m_NumHits(0), + m_NumMisses(0), + m_TotalChain(0) +{ + for (int i = 0; i < m_CacheSize; i++) + { + m_CacheOrder[i] = i; + m_CacheData[i].m_ChunkX = 0x7fffffff; + m_CacheData[i].m_ChunkZ = 0x7fffffff; + } +} + + + + + +cHeiGenCache::~cHeiGenCache() +{ + delete[] m_CacheData; + delete[] m_CacheOrder; +} + + + + + +void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + /* + if (((m_NumHits + m_NumMisses) % 1024) == 10) + { + LOGD("HeiGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); + LOGD("HeiGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); + } + //*/ + + for (int i = 0; i < m_CacheSize; i++) + { + if ( + (m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) || + (m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ) + ) + { + continue; + } + // Found it in the cache + int Idx = m_CacheOrder[i]; + + // Move to front: + for (int j = i; j > 0; j--) + { + m_CacheOrder[j] = m_CacheOrder[j - 1]; + } + m_CacheOrder[0] = Idx; + + // Use the cached data: + memcpy(a_HeightMap, m_CacheData[Idx].m_HeightMap, sizeof(a_HeightMap)); + + m_NumHits++; + m_TotalChain += i; + return; + } // for i - cache + + // Not in the cache: + m_NumMisses++; + m_HeiGenToCache.GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap); + + // Insert it as the first item in the MRU order: + int Idx = m_CacheOrder[m_CacheSize - 1]; + for (int i = m_CacheSize - 1; i > 0; i--) + { + m_CacheOrder[i] = m_CacheOrder[i - 1]; + } // for i - m_CacheOrder[] + m_CacheOrder[0] = Idx; + memcpy(m_CacheData[Idx].m_HeightMap, a_HeightMap, sizeof(a_HeightMap)); + m_CacheData[Idx].m_ChunkX = a_ChunkX; + m_CacheData[Idx].m_ChunkZ = a_ChunkZ; +} + + + + + +void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_HeiGenToCache.InitializeHeightGen(a_IniFile); +} + + + + + +bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height) +{ + for (int i = 0; i < m_CacheSize; i++) + { + if ((m_CacheData[i].m_ChunkX == a_ChunkX) && (m_CacheData[i].m_ChunkZ == a_ChunkZ)) + { + a_Height = cChunkDef::GetHeight(m_CacheData[i].m_HeightMap, a_RelX, a_RelZ); + return true; + } + } // for i - m_CacheData[] + return false; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHeiGenClassic: + +cHeiGenClassic::cHeiGenClassic(int a_Seed) : + m_Seed(a_Seed), + m_Noise(a_Seed) +{ +} + + + + + +float cHeiGenClassic::GetNoise(float x, float y) +{ + float oct1 = m_Noise.CubicNoise2D(x * m_HeightFreq1, y * m_HeightFreq1) * m_HeightAmp1; + float oct2 = m_Noise.CubicNoise2D(x * m_HeightFreq2, y * m_HeightFreq2) * m_HeightAmp2; + float oct3 = m_Noise.CubicNoise2D(x * m_HeightFreq3, y * m_HeightFreq3) * m_HeightAmp3; + + float height = m_Noise.CubicNoise2D(x * 0.1f, y * 0.1f ) * 2; + + float flatness = ((m_Noise.CubicNoise2D(x * 0.5f, y * 0.5f) + 1.f) * 0.5f) * 1.1f; // 0 ... 1.5 + flatness *= flatness * flatness; + + return (oct1 + oct2 + oct3) * flatness + height; +} + + + + + +void cHeiGenClassic::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + const float zz = (float)(a_ChunkZ * cChunkDef::Width + z); + for (int x = 0; x < cChunkDef::Width; x++) + { + const float xx = (float)(a_ChunkX * cChunkDef::Width + x); + + int hei = 64 + (int)(GetNoise(xx * 0.05f, zz * 0.05f) * 16); + if (hei < 10) + { + hei = 10; + } + if (hei > 250) + { + hei = 250; + } + cChunkDef::SetHeight(a_HeightMap, x , z, hei); + } // for x + } // for z +} + + + + + +void cHeiGenClassic::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1); + m_HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0); + m_HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0); + m_HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0); + m_HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5); + m_HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHeiGenBiomal: + +const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[biNumBiomes] = +{ + /* Fast-changing | Middle-changing | Slow-changing |*/ + /* Biome | Freq1 | Amp1 | Freq2 | Amp2 | Freq3 | Amp3 | BaseHeight */ + /* biOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, + /* biPlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, + /* biDesert */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, + /* biExtremeHills */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 100}, + /* biForest */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, + /* biTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, + /* biSwampland */ { 0.1f, 1.1f, 0.05f, 1.5f, 0.02f, 2.5f, 61.5}, + /* biRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56}, + /* biNether */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing + /* biSky */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing + /* biFrozenOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, + /* biFrozenRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56}, + /* biIcePlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, + /* biIceMountains */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80}, + /* biMushroomIsland */ { 0.1f, 2.0f, 0.05f, 8.0f, 0.01f, 6.0f, 80}, + /* biMushroomShore */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 64}, + /* biBeach */ { 0.1f, 0.5f, 0.05f, 1.0f, 0.01f, 1.0f, 64}, + /* biDesertHills */ { 0.2f, 2.0f, 0.05f, 5.0f, 0.01f, 4.0f, 75}, + /* biForestHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80}, + /* biTaigaHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80}, + /* biExtremeHillsEdge */ { 0.2f, 3.0f, 0.05f, 16.0f, 0.01f, 12.0f, 80}, + /* biJungle */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70}, + /* biJungleHills */ { 0.2f, 3.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80}, +} ; + + + + + +void cHeiGenBiomal::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + // Generate a 3x3 chunk area of biomes around this chunk: + BiomeNeighbors Biomes; + for (int z = -1; z <= 1; z++) + { + for (int x = -1; x <= 1; x++) + { + m_BiomeGen.GenBiomes(a_ChunkX + x, a_ChunkZ + z, Biomes[x + 1][z + 1]); + } // for x + } // for z + + /* + _X 2013_04_22: + There's no point in precalculating the entire perlin noise arrays, too many values are calculated uselessly, + resulting in speed DEcrease. + */ + + //* + // Linearly interpolate 4x4 blocks of heightmap: + // Must be done on a floating point datatype, else the results are ugly! + const int STEPZ = 4; // Must be a divisor of 16 + const int STEPX = 4; // Must be a divisor of 16 + NOISE_DATATYPE Height[17 * 17]; + for (int z = 0; z < 17; z += STEPZ) + { + for (int x = 0; x < 17; x += STEPX) + { + Height[x + 17 * z] = GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes); + } + } + LinearUpscale2DArrayInPlace(Height, 17, 17, STEPX, STEPZ); + + // Copy into the heightmap + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, (int)Height[x + 17 * z]); + } + } + //*/ + + /* + // For each height, go through neighboring biomes and add up their idea of height: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes)); + } // for x + } + //*/ +} + + + + + +void cHeiGenBiomal::InitializeHeightGen(cIniFile & a_IniFile) +{ + // No user-settable params +} + + + + + +NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const cHeiGenBiomal::BiomeNeighbors & a_BiomeNeighbors) +{ + // Sum up how many biomes of each type there are in the neighborhood: + int BiomeCounts[biNumBiomes]; + memset(BiomeCounts, 0, sizeof(BiomeCounts)); + int Sum = 0; + for (int z = -8; z <= 8; z++) + { + int FinalZ = a_RelZ + z + cChunkDef::Width; + int IdxZ = FinalZ / cChunkDef::Width; + int ModZ = FinalZ % cChunkDef::Width; + int WeightZ = 9 - abs(z); + for (int x = -8; x <= 8; x++) + { + int FinalX = a_RelX + x + cChunkDef::Width; + int IdxX = FinalX / cChunkDef::Width; + int ModX = FinalX % cChunkDef::Width; + EMCSBiome Biome = cChunkDef::GetBiome(a_BiomeNeighbors[IdxX][IdxZ], ModX, ModZ); + if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts))) + { + continue; + } + int WeightX = 9 - abs(x); + BiomeCounts[Biome] += WeightX + WeightZ; + Sum += WeightX + WeightZ; + } // for x + } // for z + + // For each biome type that has a nonzero count, calc its height and add it: + if (Sum > 0) + { + NOISE_DATATYPE Height = 0; + int BlockX = a_ChunkX * cChunkDef::Width + a_RelX; + int BlockZ = a_ChunkZ * cChunkDef::Width + a_RelZ; + for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++) + { + if (BiomeCounts[i] == 0) + { + continue; + } + NOISE_DATATYPE oct1 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq1, BlockZ * m_GenParam[i].m_HeightFreq1) * m_GenParam[i].m_HeightAmp1; + NOISE_DATATYPE oct2 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq2, BlockZ * m_GenParam[i].m_HeightFreq2) * m_GenParam[i].m_HeightAmp2; + NOISE_DATATYPE oct3 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq3, BlockZ * m_GenParam[i].m_HeightFreq3) * m_GenParam[i].m_HeightAmp3; + Height += BiomeCounts[i] * (m_GenParam[i].m_BaseHeight + oct1 + oct2 + oct3); + } + NOISE_DATATYPE res = Height / Sum; + return std::min((NOISE_DATATYPE)250, std::max(res, (NOISE_DATATYPE)5)); + } + + // No known biome around? Weird. Return a bogus value: + ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around"); + return 5; +} + + + + + diff --git a/src/Generating/HeiGen.h b/src/Generating/HeiGen.h new file mode 100644 index 000000000..1b246c70a --- /dev/null +++ b/src/Generating/HeiGen.h @@ -0,0 +1,145 @@ + +// HeiGen.h + +/* +Interfaces to the various height generators: + - cHeiGenFlat + - cHeiGenClassic + - cHeiGenBiomal +*/ + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cHeiGenFlat : + public cTerrainHeightGen +{ +public: + cHeiGenFlat(void) : m_Height(5) {} + +protected: + + int m_Height; + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; +} ; + + + + + +/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation +class cHeiGenCache : + public cTerrainHeightGen +{ +public: + cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize); // Doesn't take ownership of a_HeiGenToCache + ~cHeiGenCache(); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; + + /// Retrieves height at the specified point in the cache, returns true if found, false if not found + bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height); + +protected: + + cTerrainHeightGen & m_HeiGenToCache; + + struct sCacheData + { + int m_ChunkX; + int m_ChunkZ; + cChunkDef::HeightMap m_HeightMap; + } ; + + // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data + int m_CacheSize; + int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array + sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used + + // Cache statistics + int m_NumHits; + int m_NumMisses; + int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) +} ; + + + + + +class cHeiGenClassic : + public cTerrainHeightGen +{ +public: + cHeiGenClassic(int a_Seed); + +protected: + + int m_Seed; + cNoise m_Noise; + float m_HeightFreq1, m_HeightAmp1; + float m_HeightFreq2, m_HeightAmp2; + float m_HeightFreq3, m_HeightAmp3; + + float GetNoise(float x, float y); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; +} ; + + + + + +class cHeiGenBiomal : + public cTerrainHeightGen +{ +public: + cHeiGenBiomal(int a_Seed, cBiomeGen & a_BiomeGen) : + m_Noise(a_Seed), + m_BiomeGen(a_BiomeGen) + { + } + +protected: + + typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; + + cNoise m_Noise; + cBiomeGen & m_BiomeGen; + + // Per-biome terrain generator parameters: + struct sGenParam + { + float m_HeightFreq1, m_HeightAmp1; + float m_HeightFreq2, m_HeightAmp2; + float m_HeightFreq3, m_HeightAmp3; + float m_BaseHeight; + } ; + static const sGenParam m_GenParam[biNumBiomes]; + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; + + NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors); +} ; + + + + diff --git a/src/Generating/MineShafts.cpp b/src/Generating/MineShafts.cpp new file mode 100644 index 000000000..159e6b4ea --- /dev/null +++ b/src/Generating/MineShafts.cpp @@ -0,0 +1,1423 @@ + +// MineShafts.cpp + +// Implements the cStructGenMineShafts class representing the structure generator for abandoned mineshafts + +/* +Algorithm: +The cStructGenMineShafts::cMineShaftSystem class is the main controller, which knows what mineshaft +classes there are and their random weights. It gets asked to produce a new class everytime a connection is to be made. +The cMineShaft class is a base class for each mineshaft structure. +Each cMineShaft descendant knows how large it is, how to imprint itself into the chunk data and where to connect to +other descendants. Its PivotPoint is always a walkable column. Its Direction determines in which direction the structure +is facing. + +The generation starts with the central dirt room, from there corridors, crossings and staircases are added +in a depth-first processing. Each of the descendants will branch randomly, if not beyond the allowed recursion level +*/ + +#include "Globals.h" +#include "MineShafts.h" +#include "../Cuboid.h" +#include "../BlockEntities/ChestEntity.h" + + + + + +static const int NEIGHBORHOOD_SIZE = 3; + + + + + +class cMineShaft abstract +{ +public: + enum eKind + { + mskDirtRoom, + mskCorridor, + mskCrossing, + mskStaircase, + } ; + + + enum eDirection + { + dirXP, + dirZP, + dirXM, + dirZM, + } ; + + + cStructGenMineShafts::cMineShaftSystem & m_ParentSystem; + eKind m_Kind; + cCuboid m_BoundingBox; + + + cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind) : + m_ParentSystem(a_ParentSystem), + m_Kind(a_Kind) + { + } + + cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind, const cCuboid & a_BoundingBox) : + m_ParentSystem(a_ParentSystem), + m_Kind(a_Kind), + m_BoundingBox(a_BoundingBox) + { + } + + /// Returns true if this mineshaft intersects the specified cuboid + bool DoesIntersect(const cCuboid & a_Other) + { + return m_BoundingBox.DoesIntersect(a_Other); + } + + /** If recursion level is not too large, appends more branches to the parent system, + using exit points specific to this class. + */ + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) = 0; + + /// Imprints this shape into the specified chunk's data + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) = 0; +} ; + +typedef std::vector cMineShafts; + + + + + +class cMineShaftDirtRoom : + public cMineShaft +{ + typedef cMineShaft super; + +public: + cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cMineShaftCorridor : + public cMineShaft +{ + typedef cMineShaft super; + +public: + /** Creates a new Corridor attached to the specified pivot point and direction. + Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. + May return NULL if cannot fit. + */ + static cMineShaft * CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise + ); + +protected: + static const int MAX_SEGMENTS = 5; + + int m_NumSegments; + eDirection m_Direction; + bool m_HasFullBeam[MAX_SEGMENTS]; ///< If true, segment at that index has a full beam support (planks in the top center block) + int m_ChestPosition; ///< If <0, no chest; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction + int m_SpawnerPosition; ///< If <0, no spawner; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction + bool m_HasTracks; ///< If true, random tracks will be placed on the floor + + cMineShaftCorridor( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, + cNoise & a_Noise + ); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; + + /// Places a chest, if the corridor has one + void PlaceChest(cChunkDesc & a_ChunkDesc); + + /// If this corridor has tracks, places them randomly + void PlaceTracks(cChunkDesc & a_ChunkDesc); + + /// If this corridor has a spawner, places the spawner + void PlaceSpawner(cChunkDesc & a_ChunkDesc); + + /// Randomly places torches around the central beam block + void PlaceTorches(cChunkDesc & a_ChunkDesc); +} ; + + + + + +class cMineShaftCrossing : + public cMineShaft +{ + typedef cMineShaft super; + +public: + /** Creates a new Crossing attached to the specified pivot point and direction. + Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. + May return NULL if cannot fit. + */ + static cMineShaft * CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise + ); + +protected: + cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cMineShaftStaircase : + public cMineShaft +{ + typedef cMineShaft super; + +public: + enum eSlope + { + sUp, + sDown, + } ; + + /** Creates a new Staircase attached to the specified pivot point and direction. + Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. + May return NULL if cannot fit. + */ + static cMineShaft * CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise + ); + +protected: + eDirection m_Direction; + eSlope m_Slope; + + + cMineShaftStaircase( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, + eDirection a_Direction, + eSlope a_Slope + ); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenMineShafts::cMineShaftSystem +{ +public: + int m_BlockX, m_BlockZ; ///< The pivot point on which the system is generated + int m_GridSize; ///< Maximum offset of the dirtroom from grid center, * 2, in each direction + int m_MaxRecursion; ///< Maximum recursion level (initialized from cStructGenMineShafts::m_MaxRecursion) + int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor + int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor + int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing + int m_ChanceChest; ///< Chance [0 .. 250] that a corridor has a chest in it + int m_ChanceSpawner; ///< Chance [0 .. 250] that a corridor has a spawner in it + int m_ChanceTorch; ///< Chance [0 .. 10k] for a torch appearing attached to a corridor's beam + cMineShafts m_MineShafts; ///< List of cMineShaft descendants that comprise this system + cCuboid m_BoundingBox; ///< Bounding box into which all of the components need to fit + + /// Creates and generates the entire system + cMineShaftSystem( + int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, + int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase + ); + + ~cMineShaftSystem(); + + /// Carves the system into the chunk data + void ProcessChunk(cChunkDesc & a_Chunk); + + /** Creates new cMineShaft descendant connected at the specified point, heading the specified direction, + if it fits, appends it to the list and calls its AppendBranches() + */ + void AppendBranch( + int a_BlockX, int a_BlockY, int a_BlockZ, + cMineShaft::eDirection a_Direction, cNoise & a_Noise, + int a_RecursionLevel + ); + + /// Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid + bool CanAppend(const cCuboid & a_BoundingBox); +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenMineShafts::cMineShaftSystem: + +cStructGenMineShafts::cMineShaftSystem::cMineShaftSystem( + int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, + int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase +) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ), + m_GridSize(a_GridSize), + m_MaxRecursion(8), // TODO: settable + m_ProbLevelCorridor(a_ProbLevelCorridor), + m_ProbLevelCrossing(a_ProbLevelCrossing), + m_ProbLevelStaircase(a_ProbLevelStaircase + 1), + m_ChanceChest(12), // TODO: settable + m_ChanceSpawner(12), // TODO: settable + m_ChanceTorch(1000) // TODO: settable +{ + m_MineShafts.reserve(100); + + cMineShaft * Start = new cMineShaftDirtRoom(*this, a_Noise); + m_MineShafts.push_back(Start); + + m_BoundingBox.Assign( + Start->m_BoundingBox.p1.x - a_MaxSystemSize / 2, 2, Start->m_BoundingBox.p1.z - a_MaxSystemSize / 2, + Start->m_BoundingBox.p2.x + a_MaxSystemSize / 2, 50, Start->m_BoundingBox.p2.z + a_MaxSystemSize / 2 + ); + + Start->AppendBranches(0, a_Noise); + + for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + ASSERT((*itr)->m_BoundingBox.IsSorted()); + } // for itr - m_MineShafts[] +} + + + + + +cStructGenMineShafts::cMineShaftSystem::~cMineShaftSystem() +{ + for (cMineShafts::iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_MineShafts[] + m_MineShafts.clear(); +} + + + + + +void cStructGenMineShafts::cMineShaftSystem::ProcessChunk(cChunkDesc & a_Chunk) +{ + for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + (*itr)->ProcessChunk(a_Chunk); + } // for itr - m_MineShafts[] +} + + + + + +void cStructGenMineShafts::cMineShaftSystem::AppendBranch( + int a_PivotX, int a_PivotY, int a_PivotZ, + cMineShaft::eDirection a_Direction, cNoise & a_Noise, + int a_RecursionLevel +) +{ + if (a_RecursionLevel > m_MaxRecursion) + { + return; + } + + cMineShaft * Next = NULL; + int rnd = (a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_RecursionLevel * 16, a_PivotZ) / 13) % m_ProbLevelStaircase; + if (rnd < m_ProbLevelCorridor) + { + Next = cMineShaftCorridor::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); + } + else if (rnd < m_ProbLevelCrossing) + { + Next = cMineShaftCrossing::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); + } + else + { + Next = cMineShaftStaircase::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); + } + if (Next == NULL) + { + return; + } + m_MineShafts.push_back(Next); + Next->AppendBranches(a_RecursionLevel + 1, a_Noise); +} + + + + + +bool cStructGenMineShafts::cMineShaftSystem::CanAppend(const cCuboid & a_BoundingBox) +{ + if (!a_BoundingBox.IsCompletelyInside(m_BoundingBox)) + { + // Too far away, or too low / too high + return false; + } + + // Check intersections: + for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + if ((*itr)->DoesIntersect(a_BoundingBox)) + { + return false; + } + } // for itr - m_MineShafts[] + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftDirtRoom: + +cMineShaftDirtRoom::cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise) : + super(a_Parent, mskDirtRoom) +{ + // Make the room of random size, min 10 x 4 x 10; max 18 x 12 x 18: + int rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 0, a_Parent.m_BlockZ) / 7; + int OfsX = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; + rnd >>= 12; + int OfsZ = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; + rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 1000, a_Parent.m_BlockZ) / 11; + m_BoundingBox.p1.x = a_Parent.m_BlockX + OfsX; + m_BoundingBox.p2.x = m_BoundingBox.p1.x + 10 + (rnd % 8); + rnd >>= 4; + m_BoundingBox.p1.z = a_Parent.m_BlockZ + OfsZ; + m_BoundingBox.p2.z = m_BoundingBox.p1.z + 10 + (rnd % 8); + rnd >>= 4; + m_BoundingBox.p1.y = 20; + m_BoundingBox.p2.y = 24 + rnd % 8; +} + + + + + +void cMineShaftDirtRoom::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + int Height = m_BoundingBox.DifY() - 3; + for (int x = m_BoundingBox.p1.x + 1; x < m_BoundingBox.p2.x; x += 4) + { + int rnd = a_Noise.IntNoise3DInt(x, a_RecursionLevel, m_BoundingBox.p1.z) / 7; + m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + rnd >>= 4; + m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + } + + for (int z = m_BoundingBox.p1.z + 1; z < m_BoundingBox.p2.z; z += 4) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, a_RecursionLevel, z) / 13; + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXM, a_Noise, a_RecursionLevel); + rnd >>= 4; + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXP, a_Noise, a_RecursionLevel); + } +} + + + + + +void cMineShaftDirtRoom::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + if ( + (m_BoundingBox.p1.x > BlockX + cChunkDef::Width) || + (m_BoundingBox.p1.z > BlockZ + cChunkDef::Width) || + (m_BoundingBox.p2.x < BlockX) || + (m_BoundingBox.p2.z < BlockZ) + ) + { + // Early bailout - cannot intersect this chunk + return; + } + + // Chunk-relative coords of the boundaries: + int MinX = std::max(BlockX, m_BoundingBox.p1.x) - BlockX; + int MaxX = std::min(BlockX + cChunkDef::Width, m_BoundingBox.p2.x + 1) - BlockX; + int MinZ = std::max(BlockZ, m_BoundingBox.p1.z) - BlockZ; + int MaxZ = std::min(BlockZ + cChunkDef::Width, m_BoundingBox.p2.z + 1) - BlockZ; + + // Carve the room out: + for (int z = MinZ; z < MaxZ; z++) + { + for (int x = MinX; x < MaxX; x++) + { + for (int y = m_BoundingBox.p1.y + 1; y < m_BoundingBox.p2.y; y++) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); + } + if (a_ChunkDesc.GetBlockType(x, m_BoundingBox.p1.y, z) != E_BLOCK_AIR) + { + a_ChunkDesc.SetBlockType(x, m_BoundingBox.p1.y, z, E_BLOCK_DIRT); + } + } // for x + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftCorridor: + +cMineShaftCorridor::cMineShaftCorridor( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, + cNoise & a_Noise +) : + super(a_ParentSystem, mskCorridor, a_BoundingBox), + m_NumSegments(a_NumSegments), + m_Direction(a_Direction), + m_ChestPosition(-1), + m_SpawnerPosition(-1) +{ + int rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.x, a_BoundingBox.p1.y, a_BoundingBox.p1.z) / 7; + for (int i = 0; i < a_NumSegments; i++) + { + m_HasFullBeam[i] = (rnd % 4) < 3; // 75 % chance of full beam + rnd >>= 2; + } + m_HasTracks = ((rnd % 4) < 2); // 50 % chance of tracks + + rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.z, a_BoundingBox.p1.x, a_BoundingBox.p1.y) / 7; + int ChestCheck = rnd % 250; + rnd >>= 8; + int SpawnerCheck = rnd % 250; + rnd >>= 8; + if (ChestCheck < a_ParentSystem.m_ChanceChest) + { + m_ChestPosition = rnd % (a_NumSegments * 5); + } + if ((a_NumSegments < 4) && (SpawnerCheck < a_ParentSystem.m_ChanceSpawner)) + { + m_SpawnerPosition = rnd % (a_NumSegments * 5); + } +} + + + + + +cMineShaft * cMineShaftCorridor::CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise +) +{ + cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); + BoundingBox.p2.y += 3; + int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; + int NumSegments = 2 + (rnd) % (MAX_SEGMENTS - 1); // 2 .. MAX_SEGMENTS + switch (a_Direction) + { + case dirXP: BoundingBox.p2.x += NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; + case dirXM: BoundingBox.p1.x -= NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; + case dirZP: BoundingBox.p2.z += NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; + case dirZM: BoundingBox.p1.z -= NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; + } + if (!a_ParentSystem.CanAppend(BoundingBox)) + { + return NULL; + } + return new cMineShaftCorridor(a_ParentSystem, BoundingBox, NumSegments, a_Direction, a_Noise); +} + + + + + +void cMineShaftCorridor::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 7; + // Prefer the same height, but allow for up to one block height displacement: + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + switch (m_Direction) + { + case dirXM: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + } + break; + } + + case dirXP: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + } + break; + } + + case dirZM: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); + } + break; + } + + case dirZP: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); + } + break; + } + } // switch (m_Direction) +} + + + + + +void cMineShaftCorridor::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + cCuboid RelBoundingBox(m_BoundingBox); + RelBoundingBox.Move(-BlockX, 0, -BlockZ); + RelBoundingBox.p1.y += 1; + RelBoundingBox.p2.y -= 1; + cCuboid Top(RelBoundingBox); + Top.p2.y += 1; + Top.p1.y = Top.p2.y; + a_ChunkDesc.FillRelCuboid(RelBoundingBox, E_BLOCK_AIR, 0); + a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_AIR, 0, BlockX ^ BlockZ + BlockX, 8000); + if (m_SpawnerPosition >= 0) + { + // Cobwebs around the spider spawner + a_ChunkDesc.RandomFillRelCuboid(RelBoundingBox, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockZ, 8000); + a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX, 5000); + } + a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX + 10, 500); + RelBoundingBox.p1.y = m_BoundingBox.p1.y; + RelBoundingBox.p2.y = m_BoundingBox.p1.y; + a_ChunkDesc.FloorRelCuboid(RelBoundingBox, E_BLOCK_PLANKS, 0); + switch (m_Direction) + { + case dirXM: + case dirXP: + { + int y1 = m_BoundingBox.p1.y + 1; + int y2 = m_BoundingBox.p1.y + 2; + int y3 = m_BoundingBox.p1.y + 3; + int z1 = m_BoundingBox.p1.z - BlockZ; + int z2 = m_BoundingBox.p2.z - BlockZ; + for (int i = 0; i < m_NumSegments; i++) + { + int x = m_BoundingBox.p1.x + i * 5 + 2 - BlockX; + if ((x < 0) || (x >= cChunkDef::Width)) + { + continue; + } + if ((z1 >= 0) && (z1 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x, y1, z1, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y2, z1, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y3, z1, E_BLOCK_PLANKS, 0); + } + if ((z2 >= 0) && (z2 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x, y1, z2, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y2, z2, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y3, z2, E_BLOCK_PLANKS, 0); + } + if ((z1 >= -1) && (z1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) + { + a_ChunkDesc.SetBlockTypeMeta(x, y3, z1 + 1, E_BLOCK_PLANKS, 0); + } + } // for i - NumSegments + break; + } + + case dirZM: + case dirZP: + { + int y1 = m_BoundingBox.p1.y + 1; + int y2 = m_BoundingBox.p1.y + 2; + int y3 = m_BoundingBox.p1.y + 3; + int x1 = m_BoundingBox.p1.x - BlockX; + int x2 = m_BoundingBox.p2.x - BlockX; + for (int i = 0; i < m_NumSegments; i++) + { + int z = m_BoundingBox.p1.z + i * 5 + 2 - BlockZ; + if ((z < 0) || (z >= cChunkDef::Width)) + { + continue; + } + if ((x1 >= 0) && (x1 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x1, y1, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x1, y2, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x1, y3, z, E_BLOCK_PLANKS, 0); + } + if ((x2 >= 0) && (x2 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x2, y1, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x2, y2, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x2, y3, z, E_BLOCK_PLANKS, 0); + } + if ((x1 >= -1) && (x1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) + { + a_ChunkDesc.SetBlockTypeMeta(x1 + 1, y3, z, E_BLOCK_PLANKS, 0); + } + } // for i - NumSegments + break; + } // case dirZ? + } // for i + + PlaceChest(a_ChunkDesc); + PlaceTracks(a_ChunkDesc); + PlaceSpawner(a_ChunkDesc); // (must be after Tracks!) + PlaceTorches(a_ChunkDesc); +} + + + + + +void cMineShaftCorridor::PlaceChest(cChunkDesc & a_ChunkDesc) +{ + static const cLootProbab LootProbab[] = + { + // Item, MinAmount, MaxAmount, Weight + { cItem(E_ITEM_IRON), 1, 5, 10 }, + { cItem(E_ITEM_GOLD), 1, 3, 5 }, + { cItem(E_ITEM_REDSTONE_DUST), 4, 9, 5 }, + { cItem(E_ITEM_DIAMOND), 1, 2, 3 }, + { cItem(E_ITEM_DYE, 1, 4), 4, 9, 5 }, // lapis lazuli dye + { cItem(E_ITEM_COAL), 3, 8, 10 }, + { cItem(E_ITEM_BREAD), 1, 3, 15 }, + { cItem(E_ITEM_IRON_PICKAXE), 1, 1, 1 }, + { cItem(E_BLOCK_MINECART_TRACKS), 4, 8, 1 }, + { cItem(E_ITEM_MELON_SEEDS), 2, 4, 10 }, + { cItem(E_ITEM_PUMPKIN_SEEDS), 2, 4, 10 }, + } ; + + if (m_ChestPosition < 0) + { + return; + } + + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + int x, z; + NIBBLETYPE Meta = 0; + switch (m_Direction) + { + case dirXM: + case dirXP: + { + x = m_BoundingBox.p1.x + m_ChestPosition - BlockX; + z = m_BoundingBox.p1.z - BlockZ; + Meta = E_META_CHEST_FACING_ZP; + break; + } + + case dirZM: + case dirZP: + { + x = m_BoundingBox.p1.x - BlockX; + z = m_BoundingBox.p1.z + m_ChestPosition - BlockZ; + Meta = E_META_CHEST_FACING_XP; + break; + } + } // switch (Dir) + + if ( + (x >= 0) && (x < cChunkDef::Width) && + (z >= 0) && (z < cChunkDef::Width) + ) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p1.y + 1, z, E_BLOCK_CHEST, Meta); + cChestEntity * ChestEntity = (cChestEntity *)a_ChunkDesc.GetBlockEntity(x, m_BoundingBox.p1.y + 1, z); + ASSERT((ChestEntity != NULL) && (ChestEntity->GetBlockType() == E_BLOCK_CHEST)); + cNoise Noise(a_ChunkDesc.GetChunkX() ^ a_ChunkDesc.GetChunkZ()); + int NumSlots = 3 + ((Noise.IntNoise3DInt(x, m_BoundingBox.p1.y, z) / 11) % 4); + int Seed = Noise.IntNoise2DInt(x, z); + ChestEntity->GetContents().GenerateRandomLootWithBooks(LootProbab, ARRAYCOUNT(LootProbab), NumSlots, Seed); + } +} + + + + + +void cMineShaftCorridor::PlaceTracks(cChunkDesc & a_ChunkDesc) +{ + if (!m_HasTracks) + { + return; + } + cCuboid Box(m_BoundingBox); + Box.Move(-a_ChunkDesc.GetChunkX() * cChunkDef::Width, 1, -a_ChunkDesc.GetChunkZ() * cChunkDef::Width); + Box.p2.y = Box.p1.y; + Box.p1.x += 1; + Box.p2.x -= 1; + Box.p1.z += 1; + Box.p2.z -= 1; + NIBBLETYPE Meta = 0; + switch (m_Direction) + { + case dirXM: + case dirXP: + { + Meta = E_META_TRACKS_X; + break; + } + + case dirZM: + case dirZP: + { + Meta = E_META_TRACKS_Z; + break; + } + } // switch (direction) + a_ChunkDesc.RandomFillRelCuboid(Box, E_BLOCK_MINECART_TRACKS, Meta, a_ChunkDesc.GetChunkX() + a_ChunkDesc.GetChunkZ(), 6000); +} + + + + + +void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc) +{ + if (m_SpawnerPosition < 0) + { + // No spawner in this corridor + return; + } + int SpawnerRelX = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int SpawnerRelZ = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + switch (m_Direction) + { + case dirXM: + case dirXP: + { + SpawnerRelX += m_SpawnerPosition - 1; + break; + } + case dirZM: + case dirZP: + { + SpawnerRelZ += m_SpawnerPosition - 1; + break; + } + } + if ( + (SpawnerRelX >= 0) && (SpawnerRelX < cChunkDef::Width) && + (SpawnerRelZ >= 0) && (SpawnerRelZ < cChunkDef::Width) + ) + { + a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0); + // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented + } +} + + + + + +void cMineShaftCorridor::PlaceTorches(cChunkDesc & a_ChunkDesc) +{ + cNoise Noise(m_BoundingBox.p1.x); + switch (m_Direction) + { + case dirXM: + case dirXP: + { + int z = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + if ((z < 0) || (z >= cChunkDef::Width)) + { + return; + } + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + for (int i = 0; i < m_NumSegments; i++) + { + if (!m_HasFullBeam[i]) + { + continue; + } + int x = m_BoundingBox.p1.x + i * 5 + 1 - BlockX; + if ((x >= 0) && (x < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XP); + } + } + x += 2; + if ((x >= 0) && (x < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XM); + } + } + } // for i + break; + } + + case dirZM: + case dirZP: + { + int x = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; + if ((x < 0) || (x >= cChunkDef::Width)) + { + return; + } + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + for (int i = 0; i < m_NumSegments; i++) + { + if (!m_HasFullBeam[i]) + { + continue; + } + int z = m_BoundingBox.p1.z + i * 5 + 1 - BlockZ; + if ((z >= 0) && (z < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZP); + } + } + z += 2; + if ((z >= 0) && (z < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZM); + } + } + } // for i + break; + } + } // switch (direction) +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftCrossing: + +cMineShaftCrossing::cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox) : + super(a_ParentSystem, mskCrossing, a_BoundingBox) +{ +} + + + + + +cMineShaft * cMineShaftCrossing::CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise +) +{ + cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); + int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; + BoundingBox.p2.y += 3; + if ((rnd % 4) < 2) + { + // 2-level crossing: + BoundingBox.p2.y += 4; + rnd >>= 2; + if ((rnd % 4) < 2) + { + // This is the higher level: + BoundingBox.p1.y -= 4; + BoundingBox.p2.y -= 4; + } + } + rnd >>= 2; + switch (a_Direction) + { + case dirXP: BoundingBox.p2.x += 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; + case dirXM: BoundingBox.p1.x -= 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; + case dirZP: BoundingBox.p2.z += 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; + case dirZM: BoundingBox.p1.z -= 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; + } + if (!a_ParentSystem.CanAppend(BoundingBox)) + { + return NULL; + } + return new cMineShaftCrossing(a_ParentSystem, BoundingBox); +} + + + + + +void cMineShaftCrossing::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + struct + { + int x, y, z; + eDirection dir; + } Exits[] = + { + // Bottom level: + {-1, 1, 2, dirXM}, + { 2, 1, -1, dirZM}, + { 5, 1, 2, dirXP}, + { 2, 1, 5, dirZP}, + // Top level: + {-1, 5, 2, dirXM}, + { 2, 5, -1, dirZM}, + { 5, 5, 2, dirXP}, + { 2, 5, 5, dirZP}, + } ; + for (int i = 0; i < ARRAYCOUNT(Exits); i++) + { + if (m_BoundingBox.p1.y + Exits[i].y >= m_BoundingBox.p2.y) + { + // This exit is not available (two-level exit on a one-level crossing) + continue; + } + + int Height = m_BoundingBox.p1.y + Exits[i].y; + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Exits[i].x, Height, m_BoundingBox.p1.z + Exits[i].z, Exits[i].dir, a_Noise, a_RecursionLevel); + } // for i +} + + + + + +void cMineShaftCrossing::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + cCuboid box(m_BoundingBox); + box.Move(-BlockX, 0, -BlockZ); + if ((box.p2.x < 0) || (box.p2.z < 0) || (box.p1.x >= cChunkDef::Width) || (box.p1.z > cChunkDef::Width)) + { + // Does not intersect this chunk + return; + } + int Floor = box.p1.y + 1; + int Ceil = box.p2.y; + + // The supports: + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); + + // The air in between: + a_ChunkDesc.FillRelCuboid(box.p1.x + 2, box.p1.x + 2, Floor, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 2, box.p1.z + 2, E_BLOCK_AIR, 0); + + // The air on the edges: + int Mid = Floor + 2; + a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); + Mid += 2; + if (Mid < Ceil) + { + a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); + } + + // The floor, if needed: + box.p2.y = box.p1.y; + a_ChunkDesc.FloorRelCuboid(box, E_BLOCK_PLANKS, 0); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftStaircase: + +cMineShaftStaircase::cMineShaftStaircase( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, + eDirection a_Direction, + eSlope a_Slope +) : + super(a_ParentSystem, mskStaircase, a_BoundingBox), + m_Direction(a_Direction), + m_Slope(a_Slope) +{ +} + + + + + +cMineShaft * cMineShaftStaircase::CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise +) +{ + int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; + cCuboid Box; + switch (a_Direction) + { + case dirXM: + { + Box.Assign(a_PivotX - 7, a_PivotY - 1, a_PivotZ - 1, a_PivotX, a_PivotY + 6, a_PivotZ + 1); + break; + } + case dirXP: + { + Box.Assign(a_PivotX, a_PivotY - 1, a_PivotZ - 1, a_PivotX + 7, a_PivotY + 6, a_PivotZ + 1); + break; + } + case dirZM: + { + Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ - 7, a_PivotX + 1, a_PivotY + 6, a_PivotZ); + break; + } + case dirZP: + { + Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ, a_PivotX + 1, a_PivotY + 6, a_PivotZ + 7); + break; + } + } + eSlope Slope = sUp; + if ((rnd % 4) < 2) // 50 % + { + Slope = sDown; + Box.Move(0, -4, 0); + } + if (!a_ParentSystem.CanAppend(Box)) + { + return NULL; + } + return new cMineShaftStaircase(a_ParentSystem, Box, a_Direction, Slope); +} + + + + + +void cMineShaftStaircase::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + int Height = m_BoundingBox.p1.y + ((m_Slope == sDown) ? 1 : 5); + switch (m_Direction) + { + case dirXM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); break; + case dirXP: m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); break; + case dirZM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); break; + case dirZP: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); break; + } +} + + + + + +void cMineShaftStaircase::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + cCuboid RelB(m_BoundingBox); + RelB.Move(-BlockX, 0, -BlockZ); + if ( + (RelB.p1.x >= cChunkDef::Width) || + (RelB.p1.z >= cChunkDef::Width) || + (RelB.p2.x < 0) || + (RelB.p2.z < 0) + ) + { + // No intersection between this staircase and this chunk + return; + } + + int SFloor = RelB.p1.y + ((m_Slope == sDown) ? 5 : 1); + int DFloor = RelB.p1.y + ((m_Slope == sDown) ? 1 : 5); + int Add = (m_Slope == sDown) ? -1 : 1; + int InitAdd = (m_Slope == sDown) ? -1 : 0; + cCuboid Box; + switch (m_Direction) + { + case dirXM: + { + a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p2.x - 2, SFloor + InitAdd, RelB.p1.z, RelB.p2.x - 2, SFloor + 3 + InitAdd, RelB.p2.z); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(-1, Add, 0); + } + break; + } + + case dirXP: + { + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p1.x + 2, SFloor + InitAdd, RelB.p1.z, RelB.p1.x + 2, SFloor + 3 + InitAdd, RelB.p2.z); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(1, Add, 0); + } + break; + } + + case dirZM: + { + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p2.z - 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p2.z - 2); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(0, Add, -1); + } + break; + } + + case dirZP: + { + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p1.z + 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p1.z + 2); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(0, Add, 1); + } + break; + } + + } // switch (m_Direction) +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenMineShafts: + +cStructGenMineShafts::cStructGenMineShafts( + int a_Seed, int a_GridSize, int a_MaxSystemSize, + int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase +) : + m_Noise(a_Seed), + m_GridSize(a_GridSize), + m_MaxSystemSize(a_MaxSystemSize), + m_ProbLevelCorridor(std::max(0, a_ChanceCorridor)), + m_ProbLevelCrossing(std::max(0, a_ChanceCorridor + a_ChanceCrossing)), + m_ProbLevelStaircase(std::max(0, a_ChanceCorridor + a_ChanceCrossing + a_ChanceStaircase)) +{ +} + + + + + +cStructGenMineShafts::~cStructGenMineShafts() +{ + ClearCache(); +} + + + + + +void cStructGenMineShafts::ClearCache(void) +{ + for (cMineShaftSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Cache[] + m_Cache.clear(); +} + + + + + +void cStructGenMineShafts::GetMineShaftSystemsForChunk( + int a_ChunkX, int a_ChunkZ, + cStructGenMineShafts::cMineShaftSystems & a_MineShafts +) +{ + int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize; + int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize; + if (BaseX < 0) + { + --BaseX; + } + if (BaseZ < 0) + { + --BaseZ; + } + BaseX -= NEIGHBORHOOD_SIZE / 2; + BaseZ -= NEIGHBORHOOD_SIZE / 2; + + // Walk the cache, move each cave system that we want into a_Caves: + int StartX = BaseX * m_GridSize; + int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize; + int StartZ = BaseZ * m_GridSize; + int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize; + for (cMineShaftSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) + { + if ( + ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && + ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) + ) + { + // want + a_MineShafts.push_back(*itr); + itr = m_Cache.erase(itr); + } + else + { + // don't want + ++itr; + } + } // for itr - m_Cache[] + + for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) + { + int RealX = (BaseX + x) * m_GridSize; + for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) + { + int RealZ = (BaseZ + z) * m_GridSize; + bool Found = false; + for (cMineShaftSystems::const_iterator itr = a_MineShafts.begin(), end = a_MineShafts.end(); itr != end; ++itr) + { + if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) + { + Found = true; + break; + } + } // for itr - a_Mineshafts + if (!Found) + { + a_MineShafts.push_back(new cMineShaftSystem(RealX, RealZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase)); + } + } // for z + } // for x + + // Copy a_MineShafts into m_Cache to the beginning: + cMineShaftSystems MineShaftsCopy(a_MineShafts); + m_Cache.splice(m_Cache.begin(), MineShaftsCopy, MineShaftsCopy.begin(), MineShaftsCopy.end()); + + // Trim the cache if it's too long: + if (m_Cache.size() > 100) + { + cMineShaftSystems::iterator itr = m_Cache.begin(); + std::advance(itr, 100); + for (cMineShaftSystems::iterator end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } + itr = m_Cache.begin(); + std::advance(itr, 100); + m_Cache.erase(itr, m_Cache.end()); + } +} + + + + + + +void cStructGenMineShafts::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cMineShaftSystems MineShafts; + GetMineShaftSystemsForChunk(ChunkX, ChunkZ, MineShafts); + for (cMineShaftSystems::const_iterator itr = MineShafts.begin(); itr != MineShafts.end(); ++itr) + { + (*itr)->ProcessChunk(a_ChunkDesc); + } // for itr - MineShafts[] +} + + + + diff --git a/src/Generating/MineShafts.h b/src/Generating/MineShafts.h new file mode 100644 index 000000000..c53d3bc53 --- /dev/null +++ b/src/Generating/MineShafts.h @@ -0,0 +1,61 @@ + +// MineShafts.h + +// Declares the cStructGenMineShafts class representing the structure generator for abandoned mineshafts + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenMineShafts : + public cStructureGen +{ +public: + cStructGenMineShafts( + int a_Seed, int a_GridSize, int a_MaxSystemSize, + int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase + ); + + virtual ~cStructGenMineShafts(); + +protected: + friend class cMineShaft; + friend class cMineShaftDirtRoom; + friend class cMineShaftCorridor; + friend class cMineShaftCrossing; + friend class cMineShaftStaircase; + class cMineShaftSystem; // fwd: MineShafts.cpp + typedef std::list cMineShaftSystems; + + cNoise m_Noise; + int m_GridSize; ///< Average spacing of the systems + int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system + int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor + int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor + int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing + cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used. + + /// Clears everything from the cache + void ClearCache(void); + + /** Returns all systems that *may* intersect the given chunk. + All the systems are valid until the next call to this function (which may delete some of the pointers). + */ + void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems); + + // cStructureGen overrides: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp new file mode 100644 index 000000000..f47c64430 --- /dev/null +++ b/src/Generating/Noise3DGenerator.cpp @@ -0,0 +1,581 @@ + +// Nosie3DGenerator.cpp + +// Generates terrain using 3D noise, rather than composing. Is a test. + +#include "Globals.h" +#include "Noise3DGenerator.h" +#include "../OSSupport/File.h" +#include "../../iniFile/iniFile.h" +#include "../LinearInterpolation.h" +#include "../LinearUpscale.h" + + + + + +/* +// Perform an automatic test of upscaling upon program start (use breakpoints to debug): + +class Test +{ +public: + Test(void) + { + DoTest1(); + DoTest2(); + } + + + void DoTest1(void) + { + float In[3 * 3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + Debug3DNoise(In, 3, 3, 3, "Upscale3D in"); + float Out[17 * 33 * 35]; + LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17); + Debug3DNoise(Out, 17, 33, 35, "Upscale3D test"); + } + + + void DoTest2(void) + { + float In[3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + Debug2DNoise(In, 3, 3, "Upscale2D in"); + float Out[17 * 33]; + LinearUpscale2DArray(In, 3, 3, Out, 8, 16); + Debug2DNoise(Out, 17, 33, "Upscale2D test"); + } + +} gTest; +//*/ + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNoise3DGenerator: + +cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) : + super(a_ChunkGenerator), + m_Perlin(1000), + m_Cubic(1000) +{ + m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5); + m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1); + m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2); + + #if 0 + // DEBUG: Test the noise generation: + // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size + // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M + m_SeaLevel = 62; + m_HeightAmplification = 0; + m_MidPoint = 75; + m_FrequencyX = 4; + m_FrequencyY = 4; + m_FrequencyZ = 4; + m_AirThreshold = 0.5; + + const int NumChunks = 4; + NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height]; + for (int x = 0; x < NumChunks; x++) + { + GenerateNoiseArray(x, 5, Noise[x]); + } + + // Save in XY cuts: + cFile f1; + if (f1.Open("Test_XY.grab", cFile::fmWrite)) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int i = 0; i < NumChunks; i++) + { + int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; + unsigned char buf[cChunkDef::Width]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); + } + f1.Write(buf, cChunkDef::Width); + } + } // for y + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open("Test_XZ.grab", cFile::fmWrite)) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int i = 0; i < NumChunks; i++) + { + int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; + unsigned char buf[cChunkDef::Width]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); + } + f2.Write(buf, cChunkDef::Width); + } + } // for z + } // for y + } // if (XZ file open) + #endif // 0 +} + + + + + +cNoise3DGenerator::~cNoise3DGenerator() +{ + // Nothing needed yet +} + + + + + +void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +{ + m_World = a_World; + + // Params: + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); + m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); + m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8); + m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); +} + + + + + +void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) + { + a_BiomeMap[i] = biExtremeHills; + } +} + + + + + +void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) +{ + NOISE_DATATYPE Noise[17 * 257 * 17]; + GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise); + + // Output noise into chunk: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + int idx = z * 17 * 257 + y * 17; + for (int x = 0; x < cChunkDef::Width; x++) + { + NOISE_DATATYPE n = Noise[idx++]; + BLOCKTYPE BlockType; + if (n > m_AirThreshold) + { + BlockType = (y > m_SeaLevel) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER; + } + else + { + BlockType = E_BLOCK_STONE; + } + a_ChunkDesc.SetBlockType(x, y, z, BlockType); + } + } + } + + UpdateHeightmap(a_ChunkDesc); + ComposeTerrain (a_ChunkDesc); +} + + + + + +void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise) +{ + NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise + NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash + + // Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed" + NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX; + NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ; + NOISE_DATATYPE StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY; + + m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW); + + // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ)); + + // Precalculate a "height" array: + NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source") + m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25); + for (int i = 0; i < ARRAYCOUNT(Height); i++) + { + Height[i] = abs(Height[i]) * m_HeightAmplification + 1; + } + + // Modify the noise by height data: + for (int y = 0; y < DIM_Y; y++) + { + NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20; + AddHeight *= AddHeight * AddHeight; + for (int z = 0; z < DIM_Z; z++) + { + NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]); + for (int x = 0; x < DIM_X; x++) + { + CurRow[x] += AddHeight / Height[x + DIM_X * z]; + } + } + } + + // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ)); + + // Upscale the Perlin noise into full-blown chunk dimensions: + LinearUpscale3DArray( + NoiseO, DIM_X, DIM_Y, DIM_Z, + a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z + ); + + // DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ)); +} + + + + + +void cNoise3DGenerator::UpdateHeightmap(cChunkDesc & a_ChunkDesc) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int y = cChunkDef::Height - 1; y > 0; y--) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) + { + a_ChunkDesc.SetHeight(x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + // Make basic terrain composition: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir - 1; y > 0; y--) + { + switch (a_ChunkDesc.GetBlockType(x, y, z)) + { + case E_BLOCK_AIR: + { + LastAir = y; + break; + } + case E_BLOCK_STONE: + { + if (LastAir - y > 3) + { + break; + } + if (HasHadWater) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); + } + else + { + a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); + } + break; + } + case E_BLOCK_STATIONARY_WATER: + { + LastAir = y; + HasHadWater = true; + break; + } + } // switch (GetBlockType()) + } // for y + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNoise3DComposable: + +cNoise3DComposable::cNoise3DComposable(int a_Seed) : + m_Noise1(a_Seed + 1000), + m_Noise2(a_Seed + 2000), + m_Noise3(a_Seed + 3000) +{ +} + + + + + +void cNoise3DComposable::Initialize(cIniFile & a_IniFile) +{ + // Params: + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); + m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); + m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10); + m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); +} + + + + + +void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ) +{ + if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ)) + { + // The noise for this chunk is already generated in m_Noise + return; + } + m_LastChunkX = a_ChunkX; + m_LastChunkZ = a_ChunkZ; + + // Upscaling parameters: + const int UPSCALE_X = 8; + const int UPSCALE_Y = 4; + const int UPSCALE_Z = 8; + + const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; + const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; + const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; + + // Precalculate a "height" array: + NOISE_DATATYPE Height[17 * 17]; // x + 17 * z + for (int z = 0; z < 17; z += UPSCALE_Z) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; + for (int x = 0; x < 17; x += UPSCALE_X) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; + NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1; + Height[x + 17 * z] = val * val * val; + } + } + + int idx = 0; + for (int y = 0; y < 257; y += UPSCALE_Y) + { + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY; + NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20; + AddHeight *= AddHeight * AddHeight; + NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); + for (int z = 0; z < 17; z += UPSCALE_Z) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; + for (int x = 0; x < 17; x += UPSCALE_X) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; + CurFloor[x + 17 * z] = + m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 + + m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) + + m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 + + AddHeight / Height[x + 17 * z]; + } + } + // Linear-interpolate this XZ floor: + LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z); + } + + // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis + for (int y = 1; y < cChunkDef::Height; y++) + { + if ((y % UPSCALE_Y) == 0) + { + // This is the interpolation source floor, already calculated + continue; + } + int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y; + int HiFloorY = LoFloorY + UPSCALE_Y; + NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]); + NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]); + NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); + NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y; + int idx = 0; + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio; + idx += 1; + } + idx += 1; // Skipping one X column + } + } + + // The noise array is now fully interpolated + /* + // DEBUG: Output two images of the array, sliced by XY and XZ: + cFile f1; + if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + int idx = y * 17 * 17 + z * 17; + unsigned char buf[16]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); + } + f1.Write(buf, 16); + } // for y + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + int idx = y * 17 * 17 + z * 17; + unsigned char buf[16]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); + } + f2.Write(buf, 16); + } // for z + } // for y + } // if (XZ file open) + */ +} + + + + + +void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ); + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel); + for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + // Make basic terrain composition: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir; y < m_SeaLevel; y++) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + } + for (int y = LastAir - 1; y > 0; y--) + { + if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold) + { + // "air" part + LastAir = y; + if (y < m_SeaLevel) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + HasHadWater = true; + } + continue; + } + // "ground" part: + if (LastAir - y > 4) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); + continue; + } + if (HasHadWater) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); + } + else + { + a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); + } + } // for y + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h new file mode 100644 index 000000000..0d211cddc --- /dev/null +++ b/src/Generating/Noise3DGenerator.h @@ -0,0 +1,106 @@ + +// Noise3DGenerator.h + +// Generates terrain using 3D noise, rather than composing. Is a test. + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cNoise3DGenerator : + public cChunkGenerator::cGenerator +{ + typedef cChunkGenerator::cGenerator super; + +public: + cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator); + virtual ~cNoise3DGenerator(); + + virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; + virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; + +protected: + // Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: + static const int UPSCALE_X = 8; + static const int UPSCALE_Y = 4; + static const int UPSCALE_Z = 8; + + // Linear interpolation buffer dimensions, calculated from the step sizes: + static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; + static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; + static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; + + cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition + cCubicNoise m_Cubic; // The noise used for heightmap directing + + int m_SeaLevel; + NOISE_DATATYPE m_HeightAmplification; + NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + NOISE_DATATYPE m_AirThreshold; + + /// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size + void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise); + + /// Updates heightmap based on the chunk's contents + void UpdateHeightmap(cChunkDesc & a_ChunkDesc); + + /// Composes terrain - adds dirt, grass and sand + void ComposeTerrain(cChunkDesc & a_ChunkDesc); +} ; + + + + + +class cNoise3DComposable : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cNoise3DComposable(int a_Seed); + + void Initialize(cIniFile & a_IniFile); + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + cNoise m_Noise3; + + int m_SeaLevel; + NOISE_DATATYPE m_HeightAmplification; + NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + NOISE_DATATYPE m_AirThreshold; + + int m_LastChunkX; + int m_LastChunkZ; + NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y + + + /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given + void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/Ravines.cpp b/src/Generating/Ravines.cpp new file mode 100644 index 000000000..6413b963b --- /dev/null +++ b/src/Generating/Ravines.cpp @@ -0,0 +1,531 @@ + +// Ravines.cpp + +// Implements the cStructGenRavines class representing the ravine structure generator + +#include "Globals.h" +#include "Ravines.h" + + + + +/// How many ravines in each direction are generated for a given chunk. Must be an even number +static const int NEIGHBORHOOD_SIZE = 8; + +static const int NUM_RAVINE_POINTS = 4; + + + + + +struct cRavDefPoint +{ + int m_BlockX; + int m_BlockZ; + int m_Radius; + int m_Top; + int m_Bottom; + + cRavDefPoint(int a_BlockX, int a_BlockZ, int a_Radius, int a_Top, int a_Bottom) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ), + m_Radius(a_Radius), + m_Top (a_Top), + m_Bottom(a_Bottom) + { + } +} ; + +typedef std::vector cRavDefPoints; + + + + + +class cStructGenRavines::cRavine +{ + cRavDefPoints m_Points; + + /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise + void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); + + /// Refines (adds and smooths) defpoints from a_Src into a_Dst + void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst); + + /// Does one round of smoothing, two passes of RefineDefPoints() + void Smooth(void); + + /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block + void FinishLinear(void); + +public: + // Coords for which the ravine was generated (not necessarily the center) + int m_BlockX; + int m_BlockZ; + + cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); + + /// Carves the ravine into the chunk specified + void ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap + ); + + #ifdef _DEBUG + /// Exports itself as a SVG line definition + AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const; + #endif // _DEBUG +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenRavines: + +cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) : + m_Noise(a_Seed), + m_Size(a_Size) +{ +} + + + + + +cStructGenRavines::~cStructGenRavines() +{ + ClearCache(); +} + + + + + +void cStructGenRavines::ClearCache(void) +{ + for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Cache[] + m_Cache.clear(); +} + + + + + +void cStructGenRavines::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cRavines Ravines; + GetRavinesForChunk(ChunkX, ChunkZ, Ravines); + for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr) + { + (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); + } // for itr - Ravines[] +} + + + + + +void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines) +{ + int BaseX = a_ChunkX * cChunkDef::Width / m_Size; + int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size; + if (BaseX < 0) + { + --BaseX; + } + if (BaseZ < 0) + { + --BaseZ; + } + BaseX -= 4; + BaseZ -= 4; + + // Walk the cache, move each ravine that we want into a_Ravines: + int StartX = BaseX * m_Size; + int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size; + int StartZ = BaseZ * m_Size; + int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size; + for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) + { + if ( + ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && + ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) + ) + { + // want + a_Ravines.push_back(*itr); + itr = m_Cache.erase(itr); + } + else + { + // don't want + ++itr; + } + } // for itr - m_Cache[] + + for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) + { + int RealX = (BaseX + x) * m_Size; + for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) + { + int RealZ = (BaseZ + z) * m_Size; + bool Found = false; + for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) + { + if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) + { + Found = true; + break; + } + } + if (!Found) + { + a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise)); + } + } + } + + // Copy a_Ravines into m_Cache to the beginning: + cRavines RavinesCopy(a_Ravines); + m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end()); + + // Trim the cache if it's too long: + if (m_Cache.size() > 100) + { + cRavines::iterator itr = m_Cache.begin(); + std::advance(itr, 100); + for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } + itr = m_Cache.begin(); + std::advance(itr, 100); + m_Cache.erase(itr, m_Cache.end()); + } + + /* + #ifdef _DEBUG + // DEBUG: Export as SVG into a file specific for the chunk, for visual verification: + AString SVG; + SVG.append("\n\n"); + for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) + { + SVG.append((*itr)->ExportAsSVG(0, 512, 512)); + } + SVG.append("\n"); + + AString fnam; + Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); + cFile File(fnam, cFile::fmWrite); + File.Write(SVG.c_str(), SVG.size()); + #endif // _DEBUG + //*/ +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenRavines::cRavine + +cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ) +{ + // Calculate the ravine shape-defining points: + GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise); + + // Smooth the ravine. A two passes are needed: + Smooth(); + Smooth(); + + // Linearly interpolate the neighbors so that they're close enough together: + FinishLinear(); +} + + + + + +void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) +{ + // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size): + a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024; + + // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction + int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2; + int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2; + int CenterX = a_BlockX + OffsetX; + int CenterZ = a_BlockZ + OffsetZ; + + // Get the base angle in which the ravine "axis" goes: + float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653); + float xc = sin(Angle); + float zc = cos(Angle); + + // Calculate the definition points and radii: + int MaxRadius = (int)(sqrt(12.0 + ((a_Noise.IntNoise2DInt(61 * a_BlockX, 97 * a_BlockZ) / 13) % a_Size) / 16)); + int Top = 32 + ((a_Noise.IntNoise2DInt(13 * a_BlockX, 17 * a_BlockZ) / 23) % 32); + int Bottom = 5 + ((a_Noise.IntNoise2DInt(17 * a_BlockX, 29 * a_BlockZ) / 13) % 32); + int Mid = (Top + Bottom) / 2; + int PointX = CenterX - (int)(xc * a_Size / 2); + int PointZ = CenterZ - (int)(zc * a_Size / 2); + m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, (Mid + Top) / 2, (Mid + Bottom) / 2)); + for (int i = 1; i < NUM_RAVINE_POINTS - 1; i++) + { + int LineX = CenterX + (int)(xc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); + int LineZ = CenterZ + (int)(zc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); + // Amplitude is the amount of blocks that this point is away from the ravine "axis" + int Amplitude = (a_Noise.IntNoise3DInt(70 * a_BlockX, 20 * a_BlockZ + 31 * i, 10000 * i) / 9) % a_Size; + Amplitude = Amplitude / 4 - a_Size / 8; // Amplitude is in interval [-a_Size / 4, a_Size / 4] + int PointX = LineX + (int)(zc * Amplitude); + int PointZ = LineZ - (int)(xc * Amplitude); + int Radius = MaxRadius - abs(i - NUM_RAVINE_POINTS / 2); // TODO: better radius function + int ThisTop = Top + ((a_Noise.IntNoise3DInt(7 * a_BlockX, 19 * a_BlockZ, i * 31) / 13) % 8) - 4; + int ThisBottom = Bottom + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 7 * a_BlockZ, i * 31) / 13) % 8) - 4; + m_Points.push_back(cRavDefPoint(PointX, PointZ, Radius, ThisTop, ThisBottom)); + } // for i - m_Points[] + PointX = CenterX + (int)(xc * a_Size / 2); + PointZ = CenterZ + (int)(zc * a_Size / 2); + m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, Mid, Mid)); +} + + + + + +void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst) +{ + // Smoothing: for each line segment, add points on its 1/4 lengths + int Num = a_Src.size() - 2; // this many intermediary points + a_Dst.clear(); + a_Dst.reserve(Num * 2 + 2); + cRavDefPoints::const_iterator itr = a_Src.begin() + 1; + a_Dst.push_back(a_Src.front()); + int PrevX = a_Src.front().m_BlockX; + int PrevZ = a_Src.front().m_BlockZ; + int PrevR = a_Src.front().m_Radius; + int PrevT = a_Src.front().m_Top; + int PrevB = a_Src.front().m_Bottom; + for (int i = 0; i <= Num; ++i, ++itr) + { + int dx = itr->m_BlockX - PrevX; + int dz = itr->m_BlockZ - PrevZ; + if (abs(dx) + abs(dz) < 4) + { + // Too short a segment to smooth-subdivide into quarters + continue; + } + int dr = itr->m_Radius - PrevR; + int dt = itr->m_Top - PrevT; + int db = itr->m_Bottom - PrevB; + int Rad1 = std::max(PrevR + 1 * dr / 4, 1); + int Rad2 = std::max(PrevR + 3 * dr / 4, 1); + a_Dst.push_back(cRavDefPoint(PrevX + 1 * dx / 4, PrevZ + 1 * dz / 4, Rad1, PrevT + 1 * dt / 4, PrevB + 1 * db / 4)); + a_Dst.push_back(cRavDefPoint(PrevX + 3 * dx / 4, PrevZ + 3 * dz / 4, Rad2, PrevT + 3 * dt / 4, PrevB + 3 * db / 4)); + PrevX = itr->m_BlockX; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + PrevT = itr->m_Top; + PrevB = itr->m_Bottom; + } + a_Dst.push_back(a_Src.back()); +} + + + + + +void cStructGenRavines::cRavine::Smooth(void) +{ + cRavDefPoints Pts; + RefineDefPoints(m_Points, Pts); // Refine m_Points -> Pts + RefineDefPoints(Pts, m_Points); // Refine Pts -> m_Points +} + + + + + +void cStructGenRavines::cRavine::FinishLinear(void) +{ + // For each segment, use Bresenham's line algorithm to draw a "line" of defpoints + // _X 2012_07_20: I tried modifying this algorithm to produce "thick" lines (only one coord change per point) + // But the results were about the same as the original, so I disposed of it again - no need to use twice the count of points + + cRavDefPoints Pts; + std::swap(Pts, m_Points); + + m_Points.reserve(Pts.size() * 3); + int PrevX = Pts.front().m_BlockX; + int PrevZ = Pts.front().m_BlockZ; + for (cRavDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) + { + int x1 = itr->m_BlockX; + int z1 = itr->m_BlockZ; + int dx = abs(x1 - PrevX); + int dz = abs(z1 - PrevZ); + int sx = (PrevX < x1) ? 1 : -1; + int sz = (PrevZ < z1) ? 1 : -1; + int err = dx - dz; + int R = itr->m_Radius; + int T = itr->m_Top; + int B = itr->m_Bottom; + while (true) + { + m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B)); + if ((PrevX == x1) && (PrevZ == z1)) + { + break; + } + int e2 = 2 * err; + if (e2 > -dz) + { + err -= dz; + PrevX += sx; + } + if (e2 < dx) + { + err += dx; + PrevZ += sz; + } + } // while (true) + } // for itr +} + + + + + +#ifdef _DEBUG +AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const +{ + AString SVG; + AppendPrintf(SVG, "m_BlockX, a_OffsetZ + itr->m_BlockZ); + Prefix = 'L'; + } + SVG.append("\"/>\n"); + + // Base point highlight: + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ + ); + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 + ); + + // A gray line from the base point to the first point of the ravine, for identification: + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ + ); + + // Offset guides: + if (a_OffsetX > 0) + { + AppendPrintf(SVG, "\n", + a_OffsetX, a_OffsetX + ); + } + if (a_OffsetZ > 0) + { + AppendPrintf(SVG, "\n", + a_OffsetZ, a_OffsetZ + ); + } + return SVG; +} +#endif // _DEBUG + + + + + +void cStructGenRavines::cRavine::ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap +) +{ + int BlockStartX = a_ChunkX * cChunkDef::Width; + int BlockStartZ = a_ChunkZ * cChunkDef::Width; + int BlockEndX = BlockStartX + cChunkDef::Width; + int BlockEndZ = BlockStartZ + cChunkDef::Width; + for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) + { + if ( + (itr->m_BlockX + itr->m_Radius < BlockStartX) || + (itr->m_BlockX - itr->m_Radius > BlockEndX) || + (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || + (itr->m_BlockZ - itr->m_Radius > BlockEndZ) + ) + { + // Cannot intersect, bail out early + continue; + } + + // Carve out a cylinder around the xz point, m_Radius in diameter, from Bottom to Top: + int RadiusSq = itr->m_Radius * itr->m_Radius; // instead of doing sqrt for each distance, we do sqr of the radius + int DifX = BlockStartX - itr->m_BlockX; // substitution for faster calc + int DifZ = BlockStartZ - itr->m_BlockZ; // substitution for faster calc + for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) + { + #ifdef _DEBUG + // DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on) + if ((DifX + x == 0) && (DifZ + z == 0)) + { + cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE); + } + #endif // _DEBUG + + int DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z); + if (DistSq <= RadiusSq) + { + int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast + for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++) + { + switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) + { + // Only carve out these specific block types + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_GRAVEL: + case E_BLOCK_SAND: + case E_BLOCK_SANDSTONE: + case E_BLOCK_NETHERRACK: + case E_BLOCK_COAL_ORE: + case E_BLOCK_IRON_ORE: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); + break; + } + default: break; + } + } + } + } // for x, z - a_BlockTypes + } // for itr - m_Points[] +} + + + + diff --git a/src/Generating/Ravines.h b/src/Generating/Ravines.h new file mode 100644 index 000000000..05164a5b2 --- /dev/null +++ b/src/Generating/Ravines.h @@ -0,0 +1,46 @@ + +// Ravines.h + +// Interfaces to the cStructGenRavines class representing the ravine structure generator + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenRavines : + public cStructureGen +{ +public: + cStructGenRavines(int a_Seed, int a_Size); + ~cStructGenRavines(); + +protected: + class cRavine; // fwd: Ravines.cpp + typedef std::list cRavines; + + cNoise m_Noise; + int m_Size; // Max size, in blocks, of the ravines generated + cRavines m_Cache; + + /// Clears everything from the cache + void ClearCache(void); + + /// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function. + void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines); + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp new file mode 100644 index 000000000..2180261aa --- /dev/null +++ b/src/Generating/StructGen.cpp @@ -0,0 +1,675 @@ + +// StructGen.h + +#include "Globals.h" +#include "StructGen.h" +#include "../BlockID.h" +#include "Trees.h" +#include "../BlockArea.h" +#include "../LinearUpscale.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenOreNests configuration: + +const int MAX_HEIGHT_COAL = 127; +const int NUM_NESTS_COAL = 50; +const int NEST_SIZE_COAL = 10; + +const int MAX_HEIGHT_IRON = 64; +const int NUM_NESTS_IRON = 14; +const int NEST_SIZE_IRON = 6; + +const int MAX_HEIGHT_REDSTONE = 16; +const int NUM_NESTS_REDSTONE = 4; +const int NEST_SIZE_REDSTONE = 6; + +const int MAX_HEIGHT_GOLD = 32; +const int NUM_NESTS_GOLD = 2; +const int NEST_SIZE_GOLD = 6; + +const int MAX_HEIGHT_DIAMOND = 15; +const int NUM_NESTS_DIAMOND = 1; +const int NEST_SIZE_DIAMOND = 4; + +const int MAX_HEIGHT_LAPIS = 30; +const int NUM_NESTS_LAPIS = 2; +const int NEST_SIZE_LAPIS = 5; + +const int MAX_HEIGHT_DIRT = 127; +const int NUM_NESTS_DIRT = 20; +const int NEST_SIZE_DIRT = 32; + +const int MAX_HEIGHT_GRAVEL = 70; +const int NUM_NESTS_GRAVEL = 15; +const int NEST_SIZE_GRAVEL = 32; + + + + + +template T Clamp(T a_Value, T a_Min, T a_Max) +{ + return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value); +} + + + + + +static bool SortTreeBlocks(const sSetBlock & a_First, const sSetBlock & a_Second) +{ + return (a_First.BlockType == E_BLOCK_LOG) && (a_Second.BlockType != E_BLOCK_LOG); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenTrees: + +void cStructGenTrees::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + + cChunkDesc WorkerDesc(ChunkX, ChunkZ); + + // Generate trees: + for (int x = 0; x <= 2; x++) + { + int BaseX = ChunkX + x - 1; + for (int z = 0; z <= 2; z++) + { + int BaseZ = ChunkZ + z - 1; + + cChunkDesc * Dest; + + if ((x != 1) || (z != 1)) + { + Dest = &WorkerDesc; + WorkerDesc.SetChunkCoords(BaseX, BaseZ); + + m_BiomeGen->GenBiomes (BaseX, BaseZ, WorkerDesc.GetBiomeMap()); + m_HeightGen->GenHeightMap (BaseX, BaseZ, WorkerDesc.GetHeightMap()); + m_CompositionGen->ComposeTerrain(WorkerDesc); + // TODO: Free the entity lists + } + else + { + Dest = &a_ChunkDesc; + } + + int NumTrees = GetNumTrees(BaseX, BaseZ, Dest->GetBiomeMap()); + + sSetBlockVector OutsideLogs, OutsideOther; + for (int i = 0; i < NumTrees; i++) + { + GenerateSingleTree(BaseX, BaseZ, i, *Dest, OutsideLogs, OutsideOther); + } + + sSetBlockVector IgnoredOverflow; + IgnoredOverflow.reserve(OutsideOther.size()); + ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideOther, IgnoredOverflow); + IgnoredOverflow.clear(); + IgnoredOverflow.reserve(OutsideLogs.size()); + ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideLogs, IgnoredOverflow); + } // for z + } // for x + + // Update the heightmap: + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = cChunkDef::Height - 1; y >= 0; y--) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) + { + a_ChunkDesc.SetHeight(x, z, y); + break; + } + } // for y + } // for z + } // for x +} + + + + + +void cStructGenTrees::GenerateSingleTree( + int a_ChunkX, int a_ChunkZ, int a_Seq, + cChunkDesc & a_ChunkDesc, + sSetBlockVector & a_OutsideLogs, + sSetBlockVector & a_OutsideOther +) +{ + int x = (m_Noise.IntNoise3DInt(a_ChunkX + a_ChunkZ, a_ChunkZ, a_Seq) / 19) % cChunkDef::Width; + int z = (m_Noise.IntNoise3DInt(a_ChunkX - a_ChunkZ, a_Seq, a_ChunkZ) / 19) % cChunkDef::Width; + + int Height = a_ChunkDesc.GetHeight(x, z); + + if ((Height <= 0) || (Height > 240)) + { + return; + } + + // Check the block underneath the tree: + BLOCKTYPE TopBlock = a_ChunkDesc.GetBlockType(x, Height, z); + if ((TopBlock != E_BLOCK_DIRT) && (TopBlock != E_BLOCK_GRASS) && (TopBlock != E_BLOCK_FARMLAND)) + { + return; + } + + sSetBlockVector TreeLogs, TreeOther; + GetTreeImageByBiome( + a_ChunkX * cChunkDef::Width + x, Height + 1, a_ChunkZ * cChunkDef::Width + z, + m_Noise, a_Seq, + a_ChunkDesc.GetBiome(x, z), + TreeLogs, TreeOther + ); + + // Check if the generated image fits the terrain. Only the logs are checked: + for (sSetBlockVector::const_iterator itr = TreeLogs.begin(); itr != TreeLogs.end(); ++itr) + { + if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ)) + { + // Outside the chunk + continue; + } + + BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z); + switch (Block) + { + CASE_TREE_ALLOWED_BLOCKS: + { + break; + } + default: + { + // There's something in the way, abort this tree altogether + return; + } + } + } + + ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeOther, a_OutsideOther); + ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeLogs, a_OutsideLogs); +} + + + + + +void cStructGenTrees::ApplyTreeImage( + int a_ChunkX, int a_ChunkZ, + cChunkDesc & a_ChunkDesc, + const sSetBlockVector & a_Image, + sSetBlockVector & a_Overflow +) +{ + // Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks + for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr) + { + if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ)) + { + // Inside this chunk, integrate into a_ChunkDesc: + switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z)) + { + case E_BLOCK_LEAVES: + { + if (itr->BlockType != E_BLOCK_LOG) + { + break; + } + // fallthrough: + } + CASE_TREE_OVERWRITTEN_BLOCKS: + { + a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + break; + } + + } // switch (GetBlock()) + continue; + } + + // Outside the chunk, push into a_Overflow. + // Don't check if already present there, by separating logs and others we don't need the checks anymore: + a_Overflow.push_back(*itr); + } +} + + + + + +int cStructGenTrees::GetNumTrees( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BiomeMap & a_Biomes +) +{ + int NumTrees = 0; + for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) + { + int Add = 0; + switch (cChunkDef::GetBiome(a_Biomes, x, z)) + { + case biPlains: Add = 1; break; + case biExtremeHills: Add = 3; break; + case biForest: Add = 30; break; + case biTaiga: Add = 30; break; + case biSwampland: Add = 8; break; + case biIcePlains: Add = 1; break; + case biIceMountains: Add = 1; break; + case biMushroomIsland: Add = 3; break; + case biMushroomShore: Add = 3; break; + case biForestHills: Add = 20; break; + case biTaigaHills: Add = 20; break; + case biExtremeHillsEdge: Add = 5; break; + case biJungle: Add = 120; break; + case biJungleHills: Add = 90; break; + } + NumTrees += Add; + } + return NumTrees / 1024; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenOreNests: + +void cStructGenOreNests::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes(); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_COAL_ORE, MAX_HEIGHT_COAL, NUM_NESTS_COAL, NEST_SIZE_COAL, BlockTypes, 1); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_IRON_ORE, MAX_HEIGHT_IRON, NUM_NESTS_IRON, NEST_SIZE_IRON, BlockTypes, 2); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_REDSTONE_ORE, MAX_HEIGHT_REDSTONE, NUM_NESTS_REDSTONE, NEST_SIZE_REDSTONE, BlockTypes, 3); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_GOLD_ORE, MAX_HEIGHT_GOLD, NUM_NESTS_GOLD, NEST_SIZE_GOLD, BlockTypes, 4); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIAMOND_ORE, MAX_HEIGHT_DIAMOND, NUM_NESTS_DIAMOND, NEST_SIZE_DIAMOND, BlockTypes, 5); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_LAPIS_ORE, MAX_HEIGHT_LAPIS, NUM_NESTS_LAPIS, NEST_SIZE_LAPIS, BlockTypes, 6); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIRT, MAX_HEIGHT_DIRT, NUM_NESTS_DIRT, NEST_SIZE_DIRT, BlockTypes, 10); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_GRAVEL, MAX_HEIGHT_GRAVEL, NUM_NESTS_GRAVEL, NEST_SIZE_GRAVEL, BlockTypes, 11); +} + + + + + +void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq) +{ + // This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other. + // It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes + // Only stone gets replaced with ore, all other blocks stay (so the nest can actually be smaller than specified). + + for (int i = 0; i < a_NumNests; i++) + { + int rnd = m_Noise.IntNoise3DInt(a_ChunkX + i, a_Seq, a_ChunkZ + 64 * i) / 8; + int BaseX = rnd % cChunkDef::Width; + rnd /= cChunkDef::Width; + int BaseZ = rnd % cChunkDef::Width; + rnd /= cChunkDef::Width; + int BaseY = rnd % a_MaxHeight; + rnd /= a_MaxHeight; + int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1/4 larger + int Num = 0; + while (Num < NestSize) + { + // Put a cuboid around [BaseX, BaseY, BaseZ] + int rnd = m_Noise.IntNoise3DInt(a_ChunkX + 64 * i, 2 * a_Seq + Num, a_ChunkZ + 32 * i) / 8; + int xsize = rnd % 2; + int ysize = (rnd / 4) % 2; + int zsize = (rnd / 16) % 2; + rnd >>= 8; + for (int x = xsize; x >= 0; --x) + { + int BlockX = BaseX + x; + if ((BlockX < 0) || (BlockX >= cChunkDef::Width)) + { + Num++; // So that the cycle finishes even if the base coords wander away from the chunk + continue; + } + for (int y = ysize; y >= 0; --y) + { + int BlockY = BaseY + y; + if ((BlockY < 0) || (BlockY >= cChunkDef::Height)) + { + Num++; // So that the cycle finishes even if the base coords wander away from the chunk + continue; + } + for (int z = zsize; z >= 0; --z) + { + int BlockZ = BaseZ + z; + if ((BlockZ < 0) || (BlockZ >= cChunkDef::Width)) + { + Num++; // So that the cycle finishes even if the base coords wander away from the chunk + continue; + } + + int Index = cChunkDef::MakeIndexNoCheck(BlockX, BlockY, BlockZ); + if (a_BlockTypes[Index] == E_BLOCK_STONE) + { + a_BlockTypes[Index] = a_OreType; + } + Num++; + } // for z + } // for y + } // for x + + // Move the base to a neighbor voxel + switch (rnd % 4) + { + case 0: BaseX--; break; + case 1: BaseX++; break; + } + switch ((rnd >> 3) % 4) + { + case 0: BaseY--; break; + case 1: BaseY++; break; + } + switch ((rnd >> 6) % 4) + { + case 0: BaseZ--; break; + case 1: BaseZ++; break; + } + } // while (Num < NumBlocks) + } // for i - NumNests +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenLakes: + +void cStructGenLakes::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + + for (int z = -1; z < 2; z++) for (int x = -1; x < 2; x++) + { + if (((m_Noise.IntNoise2DInt(ChunkX + x, ChunkZ + z) / 17) % 100) > m_Probability) + { + continue; + } + + cBlockArea Lake; + CreateLakeImage(ChunkX + x, ChunkZ + z, Lake); + + int OfsX = Lake.GetOriginX() + x * cChunkDef::Width; + int OfsZ = Lake.GetOriginZ() + z * cChunkDef::Width; + + // Merge the lake into the current data + a_ChunkDesc.WriteBlockArea(Lake, OfsX, Lake.GetOriginY(), OfsZ, cBlockArea::msLake); + } // for x, z - neighbor chunks +} + + + + + +void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake) +{ + a_Lake.Create(16, 8, 16); + a_Lake.Fill(cBlockArea::baTypes, E_BLOCK_SPONGE); // Sponge is the NOP blocktype for lake merging strategy + + // Find the minimum height in this chunk: + cChunkDef::HeightMap HeightMap; + m_HeiGen.GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap); + HEIGHTTYPE MinHeight = HeightMap[0]; + for (int i = 1; i < ARRAYCOUNT(HeightMap); i++) + { + if (HeightMap[i] < MinHeight) + { + MinHeight = HeightMap[i]; + } + } + + // Make a random position in the chunk by using a random 16 block XZ offset and random height up to chunk's max height minus 6 + MinHeight = std::max(MinHeight - 6, 2); + int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 128, a_ChunkZ) / 11; + // Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range + int OffsetX = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8; + Rnd >>= 12; + // Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range + int OffsetZ = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8; + Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 512, a_ChunkZ) / 13; + // Random height [1 .. MinHeight] with preference to center heights + int HeightY = 1 + (((Rnd & 0x1ff) % MinHeight) + (((Rnd >> 9) & 0x1ff) % MinHeight)) / 2; + + a_Lake.SetOrigin(OffsetX, HeightY, OffsetZ); + + // Hollow out a few bubbles inside the blockarea: + int NumBubbles = 4 + ((Rnd >> 18) & 0x03); // 4 .. 7 bubbles + BLOCKTYPE * BlockTypes = a_Lake.GetBlockTypes(); + for (int i = 0; i < NumBubbles; i++) + { + int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, i, a_ChunkZ) / 13; + const int BubbleR = 2 + (Rnd & 0x03); // 2 .. 5 + const int Range = 16 - 2 * BubbleR; + const int BubbleX = BubbleR + (Rnd % Range); + Rnd >>= 4; + const int BubbleY = 4 + (Rnd & 0x01); // 4 .. 5 + Rnd >>= 1; + const int BubbleZ = BubbleR + (Rnd % Range); + Rnd >>= 4; + const int HalfR = BubbleR / 2; // 1 .. 2 + const int RSquared = BubbleR * BubbleR; + for (int y = -HalfR; y <= HalfR; y++) + { + // BubbleY + y is in the [0, 7] bounds + int DistY = 4 * y * y / 3; + int IdxY = (BubbleY + y) * 16 * 16; + for (int z = -BubbleR; z <= BubbleR; z++) + { + int DistYZ = DistY + z * z; + if (DistYZ >= RSquared) + { + continue; + } + int IdxYZ = BubbleX + IdxY + (BubbleZ + z) * 16; + for (int x = -BubbleR; x <= BubbleR; x++) + { + if (x * x + DistYZ < RSquared) + { + BlockTypes[x + IdxYZ] = E_BLOCK_AIR; + } + } // for x + } // for z + } // for y + } // for i - bubbles + + // Turn air in the bottom half into liquid: + for (int y = 0; y < 4; y++) + { + for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) + { + if (BlockTypes[x + z * 16 + y * 16 * 16] == E_BLOCK_AIR) + { + BlockTypes[x + z * 16 + y * 16 * 16] = m_Fluid; + } + } // for z, x + } // for y + + // TODO: Turn sponge next to lava into stone + + // a_Lake.SaveToSchematicFile(Printf("Lake_%d_%d.schematic", a_ChunkX, a_ChunkZ)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenDirectOverhangs: + +cStructGenDirectOverhangs::cStructGenDirectOverhangs(int a_Seed) : + m_Noise1(a_Seed), + m_Noise2(a_Seed + 1000) +{ +} + + + + + +void cStructGenDirectOverhangs::GenStructures(cChunkDesc & a_ChunkDesc) +{ + // If there is no column of the wanted biome, bail out: + if (!HasWantedBiome(a_ChunkDesc)) + { + return; + } + + HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight(); + + const int SEGMENT_HEIGHT = 8; + const int INTERPOL_X = 16; // Must be a divisor of 16 + const int INTERPOL_Z = 16; // Must be a divisor of 16 + // Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately. + // Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them + // Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it. + + int FloorBuf1[17 * 17]; + int FloorBuf2[17 * 17]; + int * FloorHi = FloorBuf1; + int * FloorLo = FloorBuf2; + int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + int BaseY = 63; + + // Interpolate the lowest floor: + for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) + { + FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] = + m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) * + m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) / + 256; + } // for x, z - FloorLo[] + LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); + + // Interpolate segments: + for (int Segment = BaseY; Segment < MaxHeight; Segment += SEGMENT_HEIGHT) + { + // First update the high floor: + for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) + { + FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] = + m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) * + m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / + 256; + } // for x, z - FloorLo[] + LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); + + // Interpolate between FloorLo and FloorHi: + for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biExtremeHills: + case biExtremeHillsEdge: + { + int Lo = FloorLo[x + 17 * z] / 256; + int Hi = FloorHi[x + 17 * z] / 256; + for (int y = 0; y < SEGMENT_HEIGHT; y++) + { + int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; + if (Val < 0) + { + a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_AIR); + } + } // for y + break; + } + } // switch (biome) + } // for z, x + + // Swap the floors: + std::swap(FloorLo, FloorHi); + } +} + + + + + +bool cStructGenDirectOverhangs::HasWantedBiome(cChunkDesc & a_ChunkDesc) const +{ + cChunkDef::BiomeMap & Biomes = a_ChunkDesc.GetBiomeMap(); + for (int i = 0; i < ARRAYCOUNT(Biomes); i++) + { + switch (Biomes[i]) + { + case biExtremeHills: + case biExtremeHillsEdge: + { + return true; + } + } + } // for i + return false; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenDistortedMembraneOverhangs: + +cStructGenDistortedMembraneOverhangs::cStructGenDistortedMembraneOverhangs(int a_Seed) : + m_NoiseX(a_Seed + 1000), + m_NoiseY(a_Seed + 2000), + m_NoiseZ(a_Seed + 3000), + m_NoiseH(a_Seed + 4000) +{ +} + + + + + +void cStructGenDistortedMembraneOverhangs::GenStructures(cChunkDesc & a_ChunkDesc) +{ + const NOISE_DATATYPE Frequency = (NOISE_DATATYPE)16; + const NOISE_DATATYPE Amount = (NOISE_DATATYPE)1; + for (int y = 50; y < 128; y++) + { + NOISE_DATATYPE NoiseY = (NOISE_DATATYPE)y / 32; + // TODO: proper water level - where to get? + BLOCKTYPE ReplacementBlock = (y > 62) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER; + for (int z = 0; z < cChunkDef::Width; z++) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z)) / Frequency; + for (int x = 0; x < cChunkDef::Width; x++) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x)) / Frequency; + NOISE_DATATYPE DistortX = m_NoiseX.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount; + NOISE_DATATYPE DistortY = m_NoiseY.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount; + NOISE_DATATYPE DistortZ = m_NoiseZ.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount; + int MembraneHeight = 96 - (int)((DistortY + m_NoiseH.CubicNoise2D(NoiseX + DistortX, NoiseZ + DistortZ)) * 30); + if (MembraneHeight < y) + { + a_ChunkDesc.SetBlockType(x, y, z, ReplacementBlock); + } + } // for y + } // for x + } // for z +} + + + + diff --git a/src/Generating/StructGen.h b/src/Generating/StructGen.h new file mode 100644 index 000000000..853748bb8 --- /dev/null +++ b/src/Generating/StructGen.h @@ -0,0 +1,165 @@ + +// StructGen.h + +/* Interfaces to the various structure generators: + - cStructGenTrees + - cStructGenMarbleCaves + - cStructGenOres +*/ + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenTrees : + public cStructureGen +{ +public: + cStructGenTrees(int a_Seed, cBiomeGen * a_BiomeGen, cTerrainHeightGen * a_HeightGen, cTerrainCompositionGen * a_CompositionGen) : + m_Seed(a_Seed), + m_Noise(a_Seed), + m_BiomeGen(a_BiomeGen), + m_HeightGen(a_HeightGen), + m_CompositionGen(a_CompositionGen) + {} + +protected: + + int m_Seed; + cNoise m_Noise; + cBiomeGen * m_BiomeGen; + cTerrainHeightGen * m_HeightGen; + cTerrainCompositionGen * m_CompositionGen; + + /** Generates and applies an image of a single tree. + Parts of the tree inside the chunk are applied to a_BlockX. + Parts of the tree outside the chunk are stored in a_OutsideX + */ + void GenerateSingleTree( + int a_ChunkX, int a_ChunkZ, int a_Seq, + cChunkDesc & a_ChunkDesc, + sSetBlockVector & a_OutsideLogs, + sSetBlockVector & a_OutsideOther + ) ; + + /// Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow + void ApplyTreeImage( + int a_ChunkX, int a_ChunkZ, + cChunkDesc & a_ChunkDesc, + const sSetBlockVector & a_Image, + sSetBlockVector & a_Overflow + ); + + int GetNumTrees( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BiomeMap & a_Biomes + ); + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenOreNests : + public cStructureGen +{ +public: + cStructGenOreNests(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {} + +protected: + cNoise m_Noise; + int m_Seed; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; + + void GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq); +} ; + + + + + +class cStructGenLakes : + public cStructureGen +{ +public: + cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainHeightGen & a_HeiGen, int a_Probability) : + m_Noise(a_Seed), + m_Seed(a_Seed), + m_Fluid(a_Fluid), + m_HeiGen(a_HeiGen), + m_Probability(a_Probability) + { + } + +protected: + cNoise m_Noise; + int m_Seed; + BLOCKTYPE m_Fluid; + cTerrainHeightGen & m_HeiGen; + int m_Probability; ///< Chance, 0 .. 100, of a chunk having the lake + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; + + /// Creates a lake image for the specified chunk into a_Lake + void CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake); +} ; + + + + + + +class cStructGenDirectOverhangs : + public cStructureGen +{ +public: + cStructGenDirectOverhangs(int a_Seed); + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; + + bool HasWantedBiome(cChunkDesc & a_ChunkDesc) const; +} ; + + + + + +class cStructGenDistortedMembraneOverhangs : + public cStructureGen +{ +public: + cStructGenDistortedMembraneOverhangs(int a_Seed); + +protected: + cNoise m_NoiseX; + cNoise m_NoiseY; + cNoise m_NoiseZ; + cNoise m_NoiseH; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp new file mode 100644 index 000000000..7ca30c60f --- /dev/null +++ b/src/Generating/Trees.cpp @@ -0,0 +1,684 @@ + +// Trees.cpp + +// Implements helper functions used for generating trees + +#include "Globals.h" +#include "Trees.h" +#include "../BlockID.h" + + + + +// DEBUG: +int gTotalLargeJungleTrees = 0; +int gOversizeLargeJungleTrees = 0; + + + + + +typedef struct +{ + int x, z; +} sCoords; + +typedef struct +{ + int x, z; + NIBBLETYPE Meta; +} sMetaCoords; + +static const sCoords Corners[] = +{ + {-1, -1}, + {-1, 1}, + {1, -1}, + {1, 1}, +} ; + +// BigO = a big ring of blocks, used for generating horz slices of treetops, the number indicates the radius + +static const sCoords BigO1[] = +{ + {0, -1}, + {-1, 0}, {1, 0}, + {0, 1}, +} ; + +static const sCoords BigO2[] = +{ + {-1, -2}, {0, -2}, {1, -2}, + {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, + {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, + {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, + {-1, 2}, {0, 2}, {1, 2}, +} ; + +static const sCoords BigO3[] = +{ + {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, + {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, + {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, + {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0}, + {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, + {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2}, + {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3}, +} ; + +static const sCoords BigO4[] = // Part of Big Jungle tree +{ + {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4}, + {-3, -3}, {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {3, -3}, + {-4, -2}, {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, {4, -2}, + {-4, -1}, {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, {4, -1}, + {-4, 0}, {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, + {-4, 1}, {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, + {-4, 2}, {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}, + {-3, 3}, {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, + {-2, 4}, {-1, 4}, {0, 4}, {1, 4}, {2, 4}, +} ; + + + + + +typedef struct +{ + const sCoords * Coords; + size_t Count; +} sCoordsArr; + +static const sCoordsArr BigOs[] = +{ + {BigO1, ARRAYCOUNT(BigO1)}, + {BigO2, ARRAYCOUNT(BigO2)}, + {BigO3, ARRAYCOUNT(BigO3)}, + {BigO4, ARRAYCOUNT(BigO4)}, +} ; + + + + + +/// Pushes a specified layer of blocks of the same type around (x, h, z) into a_Blocks +inline void PushCoordBlocks(int a_BlockX, int a_Height, int a_BlockZ, sSetBlockVector & a_Blocks, const sCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) +{ + for (size_t i = 0; i < a_NumCoords; i++) + { + a_Blocks.push_back(sSetBlock(a_BlockX + a_Coords[i].x, a_Height, a_BlockZ + a_Coords[i].z, a_BlockType, a_Meta)); + } +} + + + + +inline void PushCornerBlocks(int a_BlockX, int a_Height, int a_BlockZ, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, int a_CornersDist, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) +{ + for (size_t i = 0; i < ARRAYCOUNT(Corners); i++) + { + int x = a_BlockX + Corners[i].x; + int z = a_BlockZ + Corners[i].z; + if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height, z + 64 * a_Seq) <= a_Chance) + { + a_Blocks.push_back(sSetBlock(x, a_Height, z, a_BlockType, a_Meta)); + } + } // for i - Corners[] +} + + + + + +inline void PushSomeColumns(int a_BlockX, int a_Height, int a_BlockZ, int a_ColumnHeight, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, const sMetaCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType) +{ + for (size_t i = 0; i < a_NumCoords; i++) + { + int x = a_BlockX + a_Coords[i].x; + int z = a_BlockZ + a_Coords[i].z; + if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height + i, z + 64 * a_Seq) <= a_Chance) + { + for (int j = 0; j < a_ColumnHeight; j++) + { + a_Blocks.push_back(sSetBlock(x, a_Height - j, z, a_BlockType, a_Coords[i].Meta)); + } + } + } // for i - a_Coords[] +} + + + + + +void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + switch (a_Biome) + { + case biPlains: + case biExtremeHills: + case biExtremeHillsEdge: + case biForest: + case biMushroomIsland: + case biMushroomShore: + case biForestHills: + { + // Apple or birch trees: + if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x5fffffff) + { + GetAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + break; + } + + case biTaiga: + case biIcePlains: + case biIceMountains: + case biTaigaHills: + { + // Conifers + GetConiferTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + break; + } + + case biSwampland: + { + // Swamp trees: + GetSwampTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + break; + } + + case biJungle: + case biJungleHills: + { + // Apple bushes, large jungle trees, small jungle trees + if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x6fffffff) + { + GetAppleBushImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + } + } +} + + + + + +void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000) + { + GetSmallAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetLargeAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } +} + + + + + +void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + /* Small apple tree has: + - a top plus (no log) + - optional BigO1 + random corners (log) + - 2 layers of BigO2 + random corners (log) + - 1 to 3 blocks of trunk + */ + + int Random = a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) >> 3; + + int Heights[] = {1, 2, 2, 3} ; + int Height = 1 + Heights[Random & 3]; + Random >>= 2; + + // Pre-alloc so that we don't realloc too often later: + a_LogBlocks.reserve(Height + 5); + a_OtherBlocks.reserve(ARRAYCOUNT(BigO2) * 2 + ARRAYCOUNT(BigO1) + ARRAYCOUNT(Corners) * 3 + 3 + 5); + + // Trunk: + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); + } + int Hei = a_BlockY + Height; + + // 2 BigO2 + corners layers: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x5000000 - i * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); + Hei++; + } // for i - 2* + + // Optional BigO1 + corners layer: + if ((Random & 1) == 0) + { + PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x6000000, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); + Hei++; + } + + // Top plus: + PushCoordBlocks(a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); +} + + + + + +void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // TODO +} + + + + + +void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + int Height = 5 + (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) % 3); + + // Prealloc, so that we don't realloc too often later: + a_LogBlocks.reserve(Height); + a_OtherBlocks.reserve(80); + + // The entire trunk, out of logs: + for (int i = Height - 1; i >= 0; --i) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_BIRCH)); + } + int h = a_BlockY + Height; + + // Top layer - just the Plus: + PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH)); // There's no log at this layer + h--; + + // Second layer - log, Plus and maybe Corners: + PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + h--; + + // Third and fourth layers - BigO2 and maybe 2*Corners: + for (int Row = 0; Row < 2; Row++) + { + PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x3fffffff + Row * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + h--; + } // for Row - 2* +} + + + + + +void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Half chance for a spruce, half for a pine: + if (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) < 0x40000000) + { + GetSpruceTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetPineTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } +} + + + + + +void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Spruces have a top section with layer sizes of (0, 1, 0) or only (1, 0), + // then 1 - 3 sections of ascending sizes (1, 2) [most often], (1, 3) or (1, 2, 3) + // and an optional bottom section of size 1, followed by 1 - 3 clear trunk blocks + + // We'll use bits from this number as partial random numbers; but the noise function has mod8 irregularities + // (each of the mod8 remainders has a very different chance of occurrence) - that's why we divide by 8 + int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) / 8; + + static const int sHeights[] = {1, 2, 2, 3}; + int Height = sHeights[MyRandom & 3]; + MyRandom >>= 2; + + // Prealloc, so that we don't realloc too often later: + a_LogBlocks.reserve(Height); + a_OtherBlocks.reserve(180); + + // Clear trunk blocks: + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + } + Height += a_BlockY; + + // Optional size-1 bottom leaves layer: + if ((MyRandom & 1) == 0) + { + PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + Height++; + } + MyRandom >>= 1; + + // 1 to 3 sections of leaves layers: + static const int sNumSections[] = {1, 2, 2, 3}; + int NumSections = sNumSections[MyRandom & 3]; + MyRandom >>= 2; + for (int i = 0; i < NumSections; i++) + { + switch (MyRandom & 3) // SectionType; (1, 2) twice as often as the other two + { + case 0: + case 1: + { + PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + Height += 2; + break; + } + case 2: + { + PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + Height += 2; + break; + } + case 3: + { + PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + PushCoordBlocks(a_BlockX, Height + 2, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + Height += 3; + break; + } + } // switch (SectionType) + MyRandom >>= 2; + } // for i - Sections + + if ((MyRandom & 1) == 0) + { + // (0, 1, 0) top: + a_LogBlocks.push_back (sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + } + else + { + // (1, 0) top: + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + } +} + + + + + +void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Tall, little leaves on top. The top leaves are arranged in a shape of two cones joined by their bases. + // There can be one or two layers representing the cone bases (SameSizeMax) + + int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8; + int TrunkHeight = 8 + (MyRandom % 3); + int SameSizeMax = ((MyRandom & 8) == 0) ? 1 : 0; + MyRandom >>= 3; + int NumLeavesLayers = 2 + (MyRandom % 3); // Number of layers that have leaves in them + if (NumLeavesLayers == 2) + { + SameSizeMax = 0; + } + + // Pre-allocate the vector: + a_LogBlocks.reserve(TrunkHeight); + a_OtherBlocks.reserve(NumLeavesLayers * 25); + + // The entire trunk, out of logs: + for (int i = TrunkHeight; i >= 0; --i) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + } + int h = a_BlockY + TrunkHeight + 2; + + // Top layer - just a single leaves block: + a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + h--; + + // One more layer is above the trunk, push the central leaves: + a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + + // Layers expanding in size, then collapsing again: + // LOGD("Generating %d layers of pine leaves, SameSizeMax = %d", NumLeavesLayers, SameSizeMax); + for (int i = 0; i < NumLeavesLayers; ++i) + { + int LayerSize = std::min(i, NumLeavesLayers - i + SameSizeMax - 1); + // LOGD("LayerSize %d: %d", i, LayerSize); + if (LayerSize < 0) + { + break; + } + ASSERT(LayerSize < ARRAYCOUNT(BigOs)); + PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + h--; + } +} + + + + + +void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Vines are around the BigO3, but not in the corners; need proper meta for direction + static const sMetaCoords Vines[] = + { + {-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face + {-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face + {4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face + {-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, {-4, 2, 8}, // West face + } ; + + int Height = 3 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8) % 3; + + a_LogBlocks.reserve(Height); + a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO2) + 2 * ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 20); + + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); + } + int hei = a_BlockY + Height - 2; + + // Put vines around the lowermost leaves layer: + PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES); + + // The lower two leaves layers are BigO3 with log in the middle and possibly corners: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + hei++; + } // for i - 2* + + // The upper two leaves layers are BigO2 with leaves in the middle and possibly corners: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); + hei++; + } // for i - 2* +} + + + + + +void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + a_OtherBlocks.reserve(3 + ARRAYCOUNT(BigO2) + ARRAYCOUNT(BigO1)); + + int hei = a_BlockY; + a_LogBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + hei++; + + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + hei++; + + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); +} + + + + + +void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000) + { + GetSmallJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetLargeJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } +} + + + + + +void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // TODO: Generate proper jungle trees with branches + + // Vines are around the BigO4, but not in the corners; need proper meta for direction + static const sMetaCoords Vines[] = + { + {-2, -5, 1}, {-1, -5, 1}, {0, -5, 1}, {1, -5, 1}, {2, -5, 1}, // North face + {-2, 5, 4}, {-1, 5, 4}, {0, 5, 4}, {1, 5, 4}, {2, 5, 4}, // South face + {5, -2, 2}, {5, -1, 2}, {5, 0, 2}, {5, 1, 2}, {5, 2, 2}, // East face + {-5, -2, 8}, {-5, -1, 8}, {-5, 0, 8}, {-5, 1, 8}, {-5, 2, 8}, // West face + // TODO: vines around the trunk, proper metas and height + } ; + + int Height = 24 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 11) % 24; + + a_LogBlocks.reserve(Height * 4); + a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO4) + ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 50); + + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + } + int hei = a_BlockY + Height - 2; + + // Put vines around the lowermost leaves layer: + PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES); + + // The lower two leaves layers are BigO4 with log in the middle and possibly corners: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO4, ARRAYCOUNT(BigO4), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + hei++; + } // for i - 2* + + // The top leaves layer is a BigO3 with leaves in the middle and possibly corners: + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE)); +} + + + + + +void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Vines are around the BigO3, but not in the corners; need proper meta for direction + static const sMetaCoords Vines[] = + { + {-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face + {-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face + {4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face + {-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, // West face + // TODO: proper metas and height: {0, 1, 1}, {0, -1, 4}, {-1, 0, 2}, {1, 1, 8}, // Around the tunk + } ; + + int Height = 7 + (a_Noise.IntNoise3DInt(a_BlockX + 5 * a_Seq, a_BlockY, a_BlockZ + 5 * a_Seq) / 5) % 3; + + a_LogBlocks.reserve(Height); + a_OtherBlocks.reserve( + 2 * ARRAYCOUNT(BigO3) + // O3 layer, 2x + 2 * ARRAYCOUNT(BigO2) + // O2 layer, 2x + ARRAYCOUNT(BigO1) + 1 + // Plus on the top + Height * ARRAYCOUNT(Vines) + // Vines + 50 // some safety + ); + + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + } + int hei = a_BlockY + Height - 3; + + // Put vines around the lowermost leaves layer: + PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES); + + // The lower two leaves layers are BigO3 with log in the middle and possibly corners: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + hei++; + } // for i - 2* + + // Two layers of BigO2 leaves, possibly with corners: + for (int i = 0; i < 1; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + hei++; + } // for i - 2* + + // Top plus, all leaves: + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE)); +} + + + + diff --git a/src/Generating/Trees.h b/src/Generating/Trees.h new file mode 100644 index 000000000..f5148ad6f --- /dev/null +++ b/src/Generating/Trees.h @@ -0,0 +1,93 @@ + +// Trees.h + +// Interfaces to helper functions used for generating trees + +/* +Note that all of these functions must generate the same tree image for the same input (x, y, z, seq) + - cStructGenTrees depends on this +To generate a random image for the (x, y, z) coords, pass an arbitrary value as (seq). +Each function returns two arrays of blocks, "logs" and "other". The point is that logs are of higher priority, +logs can overwrite others(leaves), but others shouldn't overwrite logs. This is an optimization for the generator. +*/ + + + + + +#pragma once + +#include "../ChunkDef.h" +#include "../Noise.h" + + + + + +// Blocks that don't block tree growth: +#define CASE_TREE_ALLOWED_BLOCKS \ + case E_BLOCK_AIR: \ + case E_BLOCK_LEAVES: \ + case E_BLOCK_SNOW: \ + case E_BLOCK_TALL_GRASS: \ + case E_BLOCK_DEAD_BUSH: \ + case E_BLOCK_SAPLING: \ + case E_BLOCK_VINES + +// Blocks that a tree may overwrite when growing: +#define CASE_TREE_OVERWRITTEN_BLOCKS \ + case E_BLOCK_AIR: \ + /* case E_BLOCK_LEAVES: LEAVES are a special case, they can be overwritten only by log. Handled in cChunkMap::ReplaceTreeBlocks(). */ \ + case E_BLOCK_SNOW: \ + case E_BLOCK_TALL_GRASS: \ + case E_BLOCK_DEAD_BUSH: \ + case E_BLOCK_SAPLING: \ + case E_BLOCK_VINES + + + + + +/// Generates an image of a tree at the specified coords (lowest trunk block) in the specified biome +void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random apple tree +void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a small (nonbranching) apple tree +void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a large (branching) apple tree +void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random birch tree +void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random conifer tree +void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random spruce (short conifer, two layers of leaves) +void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random pine (tall conifer, little leaves at top) +void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random swampland tree +void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random apple bush (for jungles) +void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random jungle tree +void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a large jungle tree (2x2 trunk) +void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a small jungle tree (1x1 trunk) +void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + + + + + diff --git a/src/Globals.cpp b/src/Globals.cpp new file mode 100644 index 000000000..13c6ae709 --- /dev/null +++ b/src/Globals.cpp @@ -0,0 +1,10 @@ + +// Globals.cpp + +// This file is used for precompiled header generation in MSVC environments + +#include "Globals.h" + + + + diff --git a/src/Globals.h b/src/Globals.h new file mode 100644 index 000000000..ef79e4cf1 --- /dev/null +++ b/src/Globals.h @@ -0,0 +1,227 @@ + +// Globals.h + +// This file gets included from every module in the project, so that global symbols may be introduced easily +// Also used for precompiled header generation in MSVC environments + + + + + +// Compiler-dependent stuff: +#if defined(_MSC_VER) + // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether + #pragma warning(disable:4481) + + // Disable some warnings that we don't care about: + #pragma warning(disable:4100) + + #define OBSOLETE __declspec(deprecated) + + // No alignment needed in MSVC + #define ALIGN_8 + #define ALIGN_16 + +#elif defined(__GNUC__) + + // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? + #define abstract + + // TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + #define OBSOLETE __attribute__((deprecated)) + + #define ALIGN_8 __attribute__((aligned(8))) + #define ALIGN_16 __attribute__((aligned(16))) + + // Some portability macros :) + #define stricmp strcasecmp + +#else + + #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" + + /* + // Copy and uncomment this into another #elif section based on your compiler identification + + // Explicitly mark classes as abstract (no instances can be created) + #define abstract + + // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + // Mark functions as obsolete, so that their usage results in a compile-time warning + #define OBSOLETE + + // Mark types / variables for alignment. Do the platforms need it? + #define ALIGN_8 + #define ALIGN_16 + */ + +#endif + + + + + +// Integral types with predefined sizes: +typedef long long Int64; +typedef int Int32; +typedef short Int16; + +typedef unsigned long long UInt64; +typedef unsigned int UInt32; +typedef unsigned short UInt16; + + + + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for any class that shouldn't allow copying itself +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName &); \ + void operator=(const TypeName &) + +// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc +#define UNUSED(X) (void)(X) + + + + +// OS-dependent stuff: +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + + #define _WIN32_WINNT 0x501 // We want to target WinXP and higher + + #include + #include + #include // IPv6 stuff + + // Windows SDK defines min and max macros, messing up with our std::min and std::max usage + #undef min + #undef max + + // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant + #ifdef GetFreeSpace + #undef GetFreeSpace + #endif // GetFreeSpace +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include +#if !defined(ANDROID_NDK) + #include +#endif +#endif + +#if defined(ANDROID_NDK) + #define FILE_IO_PREFIX "/sdcard/mcserver/" +#else + #define FILE_IO_PREFIX "" +#endif + + + + + +// CRT stuff: +#include +#include +#include +#include +#include + + + + + +// STL stuff: +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + + +// Common headers (part 1, without macros): +#include "StringUtils.h" +#include "OSSupport/Sleep.h" +#include "OSSupport/CriticalSection.h" +#include "OSSupport/Semaphore.h" +#include "OSSupport/Event.h" +#include "OSSupport/Thread.h" +#include "OSSupport/File.h" +#include "MCLogger.h" + + + + + +// Common definitions: + +/// Evaluates to the number of elements in an array (compile-time!) +#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) + +/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" ) +#define KiB * 1024 +#define MiB * 1024 * 1024 + +/// Faster than (int)floorf((float)x / (float)div) +#define FAST_FLOOR_DIV( x, div ) (((x) - (((x) < 0) ? ((div) - 1) : 0)) / (div)) + +// Own version of assert() that writes failed assertions to the log for review +#ifdef _DEBUG + #define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) ) +#else + #define ASSERT(x) ((void)0) +#endif + +// Pretty much the same as ASSERT() but stays in Release builds +#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) ) + + + + + +/// A generic interface used mainly in ForEach() functions +template class cItemCallback +{ +public: + /// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating + virtual bool Item(Type * a_Type) = 0; +} ; + + + + + +// Common headers (part 2, with macros): +#include "ChunkDef.h" +#include "BlockID.h" + + + + diff --git a/src/Group.cpp b/src/Group.cpp new file mode 100644 index 000000000..448d29d87 --- /dev/null +++ b/src/Group.cpp @@ -0,0 +1,37 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Group.h" + +void cGroup::AddCommand( std::string a_Command ) +{ + m_Commands[ a_Command ] = true; +} + +void cGroup::AddPermission( std::string a_Permission ) +{ + m_Permissions[ a_Permission ] = true; +} + +bool cGroup::HasCommand( std::string a_Command ) +{ + if( m_Commands.find("*") != m_Commands.end() ) return true; + + CommandMap::iterator itr = m_Commands.find( a_Command ); + if( itr != m_Commands.end() ) + { + if( itr->second ) return true; + } + + for( GroupList::iterator itr = m_Inherits.begin(); itr != m_Inherits.end(); ++itr ) + { + if( (*itr)->HasCommand( a_Command ) ) return true; + } + return false; +} + +void cGroup::InheritFrom( cGroup* a_Group ) +{ + m_Inherits.remove( a_Group ); + m_Inherits.push_back( a_Group ); +} \ No newline at end of file diff --git a/src/Group.h b/src/Group.h new file mode 100644 index 000000000..65ee1a60a --- /dev/null +++ b/src/Group.h @@ -0,0 +1,40 @@ + +#pragma once + + + + + +class cGroup // tolua_export +{ // tolua_export +public: // tolua_export + cGroup() {} + ~cGroup() {} + + void SetName( std::string a_Name ) { m_Name = a_Name; } // tolua_export + const std::string & GetName() const { return m_Name; } // tolua_export + void SetColor( std::string a_Color ) { m_Color = a_Color; } // tolua_export + void AddCommand( std::string a_Command ); // tolua_export + void AddPermission( std::string a_Permission ); // tolua_export + void InheritFrom( cGroup* a_Group ); // tolua_export + + bool HasCommand( std::string a_Command ); // tolua_export + + typedef std::map< std::string, bool > PermissionMap; + const PermissionMap & GetPermissions() const { return m_Permissions; } + + typedef std::map< std::string, bool > CommandMap; + const CommandMap & GetCommands() const { return m_Commands; } + + const AString & GetColor() const { return m_Color; } // tolua_export + + typedef std::list< cGroup* > GroupList; + const GroupList & GetInherits() const { return m_Inherits; } +private: + std::string m_Name; + std::string m_Color; + + PermissionMap m_Permissions; + CommandMap m_Commands; + GroupList m_Inherits; +};// tolua_export diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp new file mode 100644 index 000000000..d7332fd0a --- /dev/null +++ b/src/GroupManager.cpp @@ -0,0 +1,122 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "GroupManager.h" +#include "Group.h" +#include "../iniFile/iniFile.h" +#include "ChatColor.h" +#include "Root.h" + + + + + +typedef std::map< AString, cGroup* > GroupMap; + + + + + +struct cGroupManager::sGroupManagerState +{ + GroupMap Groups; +}; + + + + + +cGroupManager::~cGroupManager() +{ + for( GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr ) + { + delete itr->second; + } + m_pState->Groups.clear(); + + delete m_pState; +} + + + + + +cGroupManager::cGroupManager() + : m_pState( new sGroupManagerState ) +{ + LOGD("-- Loading Groups --"); + cIniFile IniFile; + if (!IniFile.ReadFile("groups.ini")) + { + LOGWARNING("groups.ini inaccessible, no groups are defined"); + return; + } + + unsigned int NumKeys = IniFile.GetNumKeys(); + for( unsigned int i = 0; i < NumKeys; i++ ) + { + std::string KeyName = IniFile.GetKeyName( i ); + cGroup* Group = GetGroup( KeyName.c_str() ); + + LOGD("Loading group: %s", KeyName.c_str() ); + + Group->SetName( KeyName ); + char Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; + if( Color != '-' ) + Group->SetColor( cChatColor::MakeColor(Color) ); + else + Group->SetColor( cChatColor::White ); + + std::string Commands = IniFile.GetValue( KeyName, "Commands", "" ); + if( Commands.size() > 0 ) + { + AStringVector Split = StringSplit( Commands, "," ); + for( unsigned int i = 0; i < Split.size(); i++) + { + Group->AddCommand( Split[i] ); + } + } + + std::string Permissions = IniFile.GetValue( KeyName, "Permissions", "" ); + if( Permissions.size() > 0 ) + { + AStringVector Split = StringSplit( Permissions, "," ); + for( unsigned int i = 0; i < Split.size(); i++) + { + Group->AddPermission( Split[i] ); + } + } + + std::string Groups = IniFile.GetValue( KeyName, "Inherits", "" ); + if( Groups.size() > 0 ) + { + AStringVector Split = StringSplit( Groups, "," ); + for( unsigned int i = 0; i < Split.size(); i++) + { + Group->InheritFrom( GetGroup( Split[i].c_str() ) ); + } + } + } + LOGD("-- Groups Successfully Loaded --"); +} + + + + + +cGroup* cGroupManager::GetGroup( const AString & a_Name ) +{ + GroupMap::iterator itr = m_pState->Groups.find( a_Name ); + if( itr != m_pState->Groups.end() ) + { + return itr->second; + } + + cGroup* Group = new cGroup(); + m_pState->Groups[a_Name] = Group; + + return Group; +} + + + + diff --git a/src/GroupManager.h b/src/GroupManager.h new file mode 100644 index 000000000..d911f976c --- /dev/null +++ b/src/GroupManager.h @@ -0,0 +1,30 @@ + +#pragma once + + + + + +class cGroup; + + + + + +class cGroupManager +{ +public: + cGroup * GetGroup(const AString & a_Name); + +private: + friend class cRoot; + cGroupManager(); + ~cGroupManager(); + + struct sGroupManagerState; + sGroupManagerState * m_pState; +} ; + + + + diff --git a/src/HTTPServer/EnvelopeParser.cpp b/src/HTTPServer/EnvelopeParser.cpp new file mode 100644 index 000000000..8dbe05f14 --- /dev/null +++ b/src/HTTPServer/EnvelopeParser.cpp @@ -0,0 +1,132 @@ + +// EnvelopeParser.cpp + +// Implements the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME + +#include "Globals.h" +#include "EnvelopeParser.h" + + + + + +cEnvelopeParser::cEnvelopeParser(cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_IsInHeaders(true) +{ +} + + + + + +int cEnvelopeParser::Parse(const char * a_Data, int a_Size) +{ + if (!m_IsInHeaders) + { + return 0; + } + + // Start searching 1 char from the end of the already received data, if available: + size_t SearchStart = m_IncomingData.size(); + SearchStart = (SearchStart > 1) ? SearchStart - 1 : 0; + + m_IncomingData.append(a_Data, a_Size); + + size_t idxCRLF = m_IncomingData.find("\r\n", SearchStart); + if (idxCRLF == AString::npos) + { + // Not a complete line yet, all input consumed: + return a_Size; + } + + // Parse as many lines as found: + size_t Last = 0; + do + { + if (idxCRLF == Last) + { + // This was the last line of the data. Finish whatever value has been cached and return: + NotifyLast(); + m_IsInHeaders = false; + return a_Size - (m_IncomingData.size() - idxCRLF) + 2; + } + if (!ParseLine(m_IncomingData.c_str() + Last, idxCRLF - Last)) + { + // An error has occurred + m_IsInHeaders = false; + return -1; + } + Last = idxCRLF + 2; + idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2); + } while (idxCRLF != AString::npos); + m_IncomingData.erase(0, Last); + + // Parsed all lines and still expecting more + return a_Size; +} + + + + + +void cEnvelopeParser::Reset(void) +{ + m_IsInHeaders = true; + m_IncomingData.clear(); + m_LastKey.clear(); + m_LastValue.clear(); +} + + + + + +void cEnvelopeParser::NotifyLast(void) +{ + if (!m_LastKey.empty()) + { + m_Callbacks.OnHeaderLine(m_LastKey, m_LastValue); + m_LastKey.clear(); + } + m_LastValue.clear(); +} + + + + + +bool cEnvelopeParser::ParseLine(const char * a_Data, size_t a_Size) +{ + ASSERT(a_Size > 0); + if (a_Data[0] <= ' ') + { + // This line is a continuation for the previous line + if (m_LastKey.empty()) + { + return false; + } + // Append, including the whitespace in a_Data[0] + m_LastValue.append(a_Data, a_Size); + return true; + } + + // This is a line with a new key: + NotifyLast(); + for (size_t i = 0; i < a_Size; i++) + { + if (a_Data[i] == ':') + { + m_LastKey.assign(a_Data, i); + m_LastValue.assign(a_Data + i + 2, a_Size - i - 2); + return true; + } + } // for i - a_Data[] + + // No colon was found, key-less header?? + return false; +} + + + + diff --git a/src/HTTPServer/EnvelopeParser.h b/src/HTTPServer/EnvelopeParser.h new file mode 100644 index 000000000..6430fbebf --- /dev/null +++ b/src/HTTPServer/EnvelopeParser.h @@ -0,0 +1,69 @@ + +// EnvelopeParser.h + +// Declares the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME + + + + + +#pragma once + + + + + +class cEnvelopeParser +{ +public: + class cCallbacks + { + public: + /// Called when a full header line is parsed + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) = 0; + } ; + + + cEnvelopeParser(cCallbacks & a_Callbacks); + + /** Parses the incoming data. + Returns the number of bytes consumed from the input. The bytes not consumed are not part of the envelope header + */ + int Parse(const char * a_Data, int a_Size); + + /// Makes the parser forget everything parsed so far, so that it can be reused for parsing another datastream + void Reset(void); + + /// Returns true if more input is expected for the envelope header + bool IsInHeaders(void) const { return m_IsInHeaders; } + + /// Sets the IsInHeaders flag; used by cMultipartParser to simplify the parser initial conditions + void SetIsInHeaders(bool a_IsInHeaders) { m_IsInHeaders = a_IsInHeaders; } + +public: + /// Callbacks to call for the various events + cCallbacks & m_Callbacks; + + /// Set to true while the parser is still parsing the envelope headers. Once set to true, the parser will not consume any more data. + bool m_IsInHeaders; + + /// Buffer for the incoming data until it is parsed + AString m_IncomingData; + + /// Holds the last parsed key; used for line-wrapped values + AString m_LastKey; + + /// Holds the last parsed value; used for line-wrapped values + AString m_LastValue; + + + /// Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them + void NotifyLast(void); + + /// Parses one line of header data. Returns true if successful + bool ParseLine(const char * a_Data, size_t a_Size); +} ; + + + + diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp new file mode 100644 index 000000000..68afdfc11 --- /dev/null +++ b/src/HTTPServer/HTTPConnection.cpp @@ -0,0 +1,247 @@ + +// HTTPConnection.cpp + +// Implements the cHTTPConnection class representing a single persistent connection in the HTTP server. + +#include "Globals.h" +#include "HTTPConnection.h" +#include "HTTPMessage.h" +#include "HTTPServer.h" + + + + + +cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) : + m_HTTPServer(a_HTTPServer), + m_State(wcsRecvHeaders), + m_CurrentRequest(NULL) +{ + // LOGD("HTTP: New connection at %p", this); +} + + + + + +cHTTPConnection::~cHTTPConnection() +{ + // LOGD("HTTP: Del connection at %p", this); +} + + + + + +void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) +{ + AppendPrintf(m_OutgoingData, "%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str()); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPConnection::SendNeedAuth(const AString & a_Realm) +{ + AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str()); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPConnection::Send(const cHTTPResponse & a_Response) +{ + ASSERT(m_State = wcsRecvIdle); + a_Response.AppendToData(m_OutgoingData); + m_State = wcsSendingResp; + m_HTTPServer.NotifyConnectionWrite(*this); +} + + + + + +void cHTTPConnection::Send(const void * a_Data, int a_Size) +{ + ASSERT(m_State == wcsSendingResp); + AppendPrintf(m_OutgoingData, "%x\r\n", a_Size); + m_OutgoingData.append((const char *)a_Data, a_Size); + m_OutgoingData.append("\r\n"); + m_HTTPServer.NotifyConnectionWrite(*this); +} + + + + + +void cHTTPConnection::FinishResponse(void) +{ + ASSERT(m_State == wcsSendingResp); + m_OutgoingData.append("0\r\n\r\n"); + m_State = wcsRecvHeaders; + m_HTTPServer.NotifyConnectionWrite(*this); +} + + + + + +void cHTTPConnection::AwaitNextRequest(void) +{ + switch (m_State) + { + case wcsRecvHeaders: + { + // Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth() ) + break; + } + + case wcsRecvIdle: + { + // The client is waiting for a response, send an "Internal server error": + m_OutgoingData.append("HTTP/1.1 500 Internal Server Error\r\n\r\n"); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; + break; + } + + case wcsSendingResp: + { + // The response headers have been sent, we need to terminate the response body: + m_OutgoingData.append("0\r\n\r\n"); + m_State = wcsRecvHeaders; + break; + } + + default: + { + ASSERT(!"Unhandled state recovery"); + break; + } + } +} + + + + + +void cHTTPConnection::Terminate(void) +{ + if (m_CurrentRequest != NULL) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_HTTPServer.CloseConnection(*this); +} + + + + + +void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) +{ + switch (m_State) + { + case wcsRecvHeaders: + { + if (m_CurrentRequest == NULL) + { + m_CurrentRequest = new cHTTPRequest; + } + + int BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size); + if (BytesConsumed < 0) + { + delete m_CurrentRequest; + m_CurrentRequest = NULL; + m_State = wcsInvalid; + m_HTTPServer.CloseConnection(*this); + return; + } + if (m_CurrentRequest->IsInHeaders()) + { + // The request headers are not yet complete + return; + } + + // The request has finished parsing its headers successfully, notify of it: + m_State = wcsRecvBody; + m_HTTPServer.NewRequest(*this, *m_CurrentRequest); + m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength(); + if (m_CurrentRequestBodyRemaining < 0) + { + // The body length was not specified in the request, assume zero + m_CurrentRequestBodyRemaining = 0; + } + + // Process the rest of the incoming data into the request body: + if (a_Size > BytesConsumed) + { + DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); + } + else + { + DataReceived("", 0); // If the request has zero body length, let it be processed right-away + } + break; + } + + case wcsRecvBody: + { + ASSERT(m_CurrentRequest != NULL); + if (m_CurrentRequestBodyRemaining > 0) + { + int BytesToConsume = std::min(m_CurrentRequestBodyRemaining, a_Size); + m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume); + m_CurrentRequestBodyRemaining -= BytesToConsume; + } + if (m_CurrentRequestBodyRemaining == 0) + { + m_State = wcsRecvIdle; + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + delete m_CurrentRequest; + m_CurrentRequest = NULL; + } + break; + } + + default: + { + // TODO: Should we be receiving data in this state? + break; + } + } +} + + + + + +void cHTTPConnection::GetOutgoingData(AString & a_Data) +{ + std::swap(a_Data, m_OutgoingData); +} + + + + + +void cHTTPConnection::SocketClosed(void) +{ + if (m_CurrentRequest != NULL) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_HTTPServer.CloseConnection(*this); +} + + + + + diff --git a/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h new file mode 100644 index 000000000..14603bb70 --- /dev/null +++ b/src/HTTPServer/HTTPConnection.h @@ -0,0 +1,101 @@ + +// HTTPConnection.h + +// Declares the cHTTPConnection class representing a single persistent connection in the HTTP server. + + + + + +#pragma once + +#include "../OSSupport/SocketThreads.h" + + + + + +// fwd: +class cHTTPServer; +class cHTTPResponse; +class cHTTPRequest; + + + + + +class cHTTPConnection : + public cSocketThreads::cCallback +{ +public: + + enum eState + { + wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if NULL) + wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid) + wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == NULL) + wcsSendingResp, ///< Sending response body (m_CurrentRequest == NULL) + wcsInvalid, ///< The request was malformed, the connection is closing + } ; + + cHTTPConnection(cHTTPServer & a_HTTPServer); + ~cHTTPConnection(); + + /// Sends HTTP status code together with a_Reason (used for HTTP errors) + void SendStatusAndReason(int a_StatusCode, const AString & a_Reason); + + /// Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm + void SendNeedAuth(const AString & a_Realm); + + /// Sends the headers contained in a_Response + void Send(const cHTTPResponse & a_Response); + + /// Sends the data as the response (may be called multiple times) + void Send(const void * a_Data, int a_Size); + + /// Sends the data as the response (may be called multiple times) + void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); } + + /// Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) + void FinishResponse(void); + + /// Resets the connection for a new request. Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" + void AwaitNextRequest(void); + + /// Terminates the connection; finishes any request being currently processed + void Terminate(void); + +protected: + typedef std::map cNameValueMap; + + /// The parent webserver that is to be notified of events on this connection + cHTTPServer & m_HTTPServer; + + /// All the incoming data until the entire request header is parsed + AString m_IncomingHeaderData; + + /// Status in which the request currently is + eState m_State; + + /// Data that is queued for sending, once the socket becomes writable + AString m_OutgoingData; + + /// The request being currently received (valid only between having parsed the headers and finishing receiving the body) + cHTTPRequest * m_CurrentRequest; + + /// Number of bytes that remain to read for the complete body of the message to be received. Valid only in wcsRecvBody + int m_CurrentRequestBodyRemaining; + + + // cSocketThreads::cCallback overrides: + virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client + virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client + virtual void SocketClosed (void) override; // The socket has been closed for any reason +} ; + +typedef std::vector cHTTPConnections; + + + + + diff --git a/src/HTTPServer/HTTPFormParser.cpp b/src/HTTPServer/HTTPFormParser.cpp new file mode 100644 index 000000000..596db424e --- /dev/null +++ b/src/HTTPServer/HTTPFormParser.cpp @@ -0,0 +1,290 @@ + +// HTTPFormParser.cpp + +// Implements the cHTTPFormParser class representing a parser for forms sent over HTTP + +#include "Globals.h" +#include "HTTPFormParser.h" +#include "HTTPMessage.h" +#include "MultipartParser.h" +#include "NameValueParser.h" + + + + + +cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_IsValid(true) +{ + if (a_Request.GetMethod() == "GET") + { + m_Kind = fpkURL; + + // Directly parse the URL in the request: + const AString & URL = a_Request.GetURL(); + size_t idxQM = URL.find('?'); + if (idxQM != AString::npos) + { + Parse(URL.c_str() + idxQM + 1, URL.size() - idxQM - 1); + } + return; + } + if ((a_Request.GetMethod() == "POST") || (a_Request.GetMethod() == "PUT")) + { + if (strncmp(a_Request.GetContentType().c_str(), "application/x-www-form-urlencoded", 33) == 0) + { + m_Kind = fpkFormUrlEncoded; + return; + } + if (strncmp(a_Request.GetContentType().c_str(), "multipart/form-data", 19) == 0) + { + m_Kind = fpkMultipart; + BeginMultipart(a_Request); + return; + } + } + // Invalid method / content type combination, this is not a HTTP form + m_IsValid = false; +} + + + + + +cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_Kind(a_Kind), + m_IsValid(true) +{ + Parse(a_Data, a_Size); +} + + + + + +void cHTTPFormParser::Parse(const char * a_Data, int a_Size) +{ + if (!m_IsValid) + { + return; + } + + switch (m_Kind) + { + case fpkURL: + case fpkFormUrlEncoded: + { + // This format is used for smaller forms (not file uploads), so we can delay parsing it until Finish() + m_IncomingData.append(a_Data, a_Size); + break; + } + case fpkMultipart: + { + ASSERT(m_MultipartParser.get() != NULL); + m_MultipartParser->Parse(a_Data, a_Size); + break; + } + default: + { + ASSERT(!"Unhandled form kind"); + break; + } + } +} + + + + + +bool cHTTPFormParser::Finish(void) +{ + switch (m_Kind) + { + case fpkURL: + case fpkFormUrlEncoded: + { + // m_IncomingData has all the form data, parse it now: + ParseFormUrlEncoded(); + break; + } + } + return (m_IsValid && m_IncomingData.empty()); +} + + + + + +bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request) +{ + const AString & ContentType = a_Request.GetContentType(); + return ( + (ContentType == "application/x-www-form-urlencoded") || + (strncmp(ContentType.c_str(), "multipart/form-data", 19) == 0) || + ( + (a_Request.GetMethod() == "GET") && + (a_Request.GetURL().find('?') != AString::npos) + ) + ); + return false; +} + + + + + +void cHTTPFormParser::BeginMultipart(const cHTTPRequest & a_Request) +{ + ASSERT(m_MultipartParser.get() == NULL); + m_MultipartParser.reset(new cMultipartParser(a_Request.GetContentType(), *this)); +} + + + + + +void cHTTPFormParser::ParseFormUrlEncoded(void) +{ + // Parse m_IncomingData for all the variables; no more data is incoming, since this is called from Finish() + // This may not be the most performant version, but we don't care, the form data is small enough and we're not a full-fledged web server anyway + AStringVector Lines = StringSplit(m_IncomingData, "&"); + for (AStringVector::iterator itr = Lines.begin(), end = Lines.end(); itr != end; ++itr) + { + AStringVector Components = StringSplit(*itr, "="); + switch (Components.size()) + { + default: + { + // Neither name nor value, or too many "="s, mark this as invalid form: + m_IsValid = false; + return; + } + case 1: + { + // Only name present + (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = ""; + break; + } + case 2: + { + // name=value format: + (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = URLDecode(ReplaceAllCharOccurrences(Components[1], '+', ' ')); + break; + } + } + } // for itr - Lines[] + m_IncomingData.clear(); +} + + + + + +void cHTTPFormParser::OnPartStart(void) +{ + m_CurrentPartFileName.clear(); + m_CurrentPartName.clear(); + m_IsCurrentPartFile = false; + m_FileHasBeenAnnounced = false; +} + + + + + +void cHTTPFormParser::OnPartHeader(const AString & a_Key, const AString & a_Value) +{ + if (NoCaseCompare(a_Key, "Content-Disposition") == 0) + { + size_t len = a_Value.size(); + size_t ParamsStart = AString::npos; + for (size_t i = 0; i < len; ++i) + { + if (a_Value[i] > ' ') + { + if (strncmp(a_Value.c_str() + i, "form-data", 9) != 0) + { + // Content disposition is not "form-data", mark the whole form invalid + m_IsValid = false; + return; + } + ParamsStart = a_Value.find(';', i + 9); + break; + } + } + if (ParamsStart == AString::npos) + { + // There is data missing in the Content-Disposition field, mark the whole form invalid: + m_IsValid = false; + return; + } + + // Parse the field name and optional filename from this header: + cNameValueParser Parser(a_Value.data() + ParamsStart, a_Value.size() - ParamsStart); + Parser.Finish(); + m_CurrentPartName = Parser["name"]; + if (!Parser.IsValid() || m_CurrentPartName.empty()) + { + // The required parameter "name" is missing, mark the whole form invalid: + m_IsValid = false; + return; + } + m_CurrentPartFileName = Parser["filename"]; + } +} + + + + + +void cHTTPFormParser::OnPartData(const char * a_Data, int a_Size) +{ + if (m_CurrentPartName.empty()) + { + // Prologue, epilogue or invalid part + return; + } + if (m_CurrentPartFileName.empty()) + { + // This is a variable, store it in the map + iterator itr = find(m_CurrentPartName); + if (itr == end()) + { + (*this)[m_CurrentPartName] = AString(a_Data, a_Size); + } + else + { + itr->second.append(a_Data, a_Size); + } + } + else + { + // This is a file, pass it on through the callbacks + if (!m_FileHasBeenAnnounced) + { + m_Callbacks.OnFileStart(*this, m_CurrentPartFileName); + m_FileHasBeenAnnounced = true; + } + m_Callbacks.OnFileData(*this, a_Data, a_Size); + } +} + + + + + +void cHTTPFormParser::OnPartEnd(void) +{ + if (m_FileHasBeenAnnounced) + { + m_Callbacks.OnFileEnd(*this); + } + m_CurrentPartName.clear(); + m_CurrentPartFileName.clear(); +} + + + + diff --git a/src/HTTPServer/HTTPFormParser.h b/src/HTTPServer/HTTPFormParser.h new file mode 100644 index 000000000..a554ca5a4 --- /dev/null +++ b/src/HTTPServer/HTTPFormParser.h @@ -0,0 +1,112 @@ + +// HTTPFormParser.h + +// Declares the cHTTPFormParser class representing a parser for forms sent over HTTP + + + + +#pragma once + +#include "MultipartParser.h" + + + + + +// fwd: +class cHTTPRequest; + + + + + +class cHTTPFormParser : + public std::map, + public cMultipartParser::cCallbacks +{ +public: + enum eKind + { + fpkURL, ///< The form has been transmitted as parameters to a GET request + fpkFormUrlEncoded, ///< The form has been POSTed or PUT, with Content-Type of "application/x-www-form-urlencoded" + fpkMultipart, ///< The form has been POSTed or PUT, with Content-Type of "multipart/form-data" + } ; + + class cCallbacks + { + public: + /// Called when a new file part is encountered in the form data + virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) = 0; + + /// Called when more file data has come for the current file in the form data + virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) = 0; + + /// Called when the current file part has ended in the form data + virtual void OnFileEnd(cHTTPFormParser & a_Parser) = 0; + } ; + + + /// Creates a parser that is tied to a request and notifies of various events using a callback mechanism + cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks); + + /// Creates a parser with the specified content type that reads data from a string + cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks); + + /// Adds more data into the parser, as the request body is received + void Parse(const char * a_Data, int a_Size); + + /** Notifies that there's no more data incoming and the parser should finish its parsing. + Returns true if parsing successful + */ + bool Finish(void); + + /// Returns true if the headers suggest the request has form data parseable by this class + static bool HasFormData(const cHTTPRequest & a_Request); + +protected: + + /// The callbacks to call for incoming file data + cCallbacks & m_Callbacks; + + /// The kind of the parser (decided in the constructor, used in Parse() + eKind m_Kind; + + /// Buffer for the incoming data until it's parsed + AString m_IncomingData; + + /// True if the information received so far is a valid form; set to false on first problem. Further parsing is skipped when false. + bool m_IsValid; + + /// The parser for the multipart data, if used + std::auto_ptr m_MultipartParser; + + /// Name of the currently parsed part in multipart data + AString m_CurrentPartName; + + /// True if the currently parsed part in multipart data is a file + bool m_IsCurrentPartFile; + + /// Filename of the current parsed part in multipart data (for file uploads) + AString m_CurrentPartFileName; + + /// Set to true after m_Callbacks.OnFileStart() has been called, reset to false on PartEnd + bool m_FileHasBeenAnnounced; + + + /// Sets up the object for parsing a fpkMultipart request + void BeginMultipart(const cHTTPRequest & a_Request); + + /// Parses m_IncomingData as form-urlencoded data (fpkURL or fpkFormUrlEncoded kinds) + void ParseFormUrlEncoded(void); + + // cMultipartParser::cCallbacks overrides: + virtual void OnPartStart (void) override; + virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override; + virtual void OnPartData (const char * a_Data, int a_Size) override; + virtual void OnPartEnd (void) override; +} ; + + + + diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp new file mode 100644 index 000000000..ab23866e6 --- /dev/null +++ b/src/HTTPServer/HTTPMessage.cpp @@ -0,0 +1,279 @@ + +// HTTPMessage.cpp + +// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes + +#include "Globals.h" +#include "HTTPMessage.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPMessage: + +cHTTPMessage::cHTTPMessage(eKind a_Kind) : + m_Kind(a_Kind), + m_ContentLength(-1) +{ +} + + + + + +void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) +{ + AString Key = a_Key; + StrToLower(Key); + cNameValueMap::iterator itr = m_Headers.find(Key); + if (itr == m_Headers.end()) + { + m_Headers[Key] = a_Value; + } + else + { + // The header-field key is specified multiple times, combine into comma-separated list (RFC 2616 @ 4.2) + itr->second.append(", "); + itr->second.append(a_Value); + } + + // Special processing for well-known headers: + if (Key == "content-type") + { + m_ContentType = m_Headers[Key]; + } + else if (Key == "content-length") + { + m_ContentLength = atoi(m_Headers[Key].c_str()); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPRequest: + +cHTTPRequest::cHTTPRequest(void) : + super(mkRequest), + m_EnvelopeParser(*this), + m_IsValid(true), + m_UserData(NULL), + m_HasAuth(false) +{ +} + + + + + +int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size) +{ + if (!m_IsValid) + { + return -1; + } + + if (m_Method.empty()) + { + // The first line hasn't been processed yet + int res = ParseRequestLine(a_Data, a_Size); + if ((res < 0) || (res == a_Size)) + { + return res; + } + int res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res); + if (res2 < 0) + { + m_IsValid = false; + return res2; + } + return res2 + res; + } + + if (m_EnvelopeParser.IsInHeaders()) + { + int res = m_EnvelopeParser.Parse(a_Data, a_Size); + if (res < 0) + { + m_IsValid = false; + } + return res; + } + return 0; +} + + + + + +AString cHTTPRequest::GetBareURL(void) const +{ + size_t idxQM = m_URL.find('?'); + if (idxQM != AString::npos) + { + return m_URL.substr(0, idxQM); + } + else + { + return m_URL; + } +} + + + + + +int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size) +{ + m_IncomingHeaderData.append(a_Data, a_Size); + size_t IdxEnd = m_IncomingHeaderData.size(); + + // Ignore the initial CRLFs (HTTP spec's "should") + size_t LineStart = 0; + while ( + (LineStart < IdxEnd) && + ( + (m_IncomingHeaderData[LineStart] == '\r') || + (m_IncomingHeaderData[LineStart] == '\n') + ) + ) + { + LineStart++; + } + if (LineStart >= IdxEnd) + { + m_IsValid = false; + return -1; + } + + int NumSpaces = 0; + size_t MethodEnd = 0; + size_t URLEnd = 0; + for (size_t i = LineStart; i < IdxEnd; i++) + { + switch (m_IncomingHeaderData[i]) + { + case ' ': + { + switch (NumSpaces) + { + case 0: + { + MethodEnd = i; + break; + } + case 1: + { + URLEnd = i; + break; + } + default: + { + // Too many spaces in the request + m_IsValid = false; + return -1; + } + } + NumSpaces += 1; + break; + } + case '\n': + { + if ((i == 0) || (m_IncomingHeaderData[i - 1] != '\r') || (NumSpaces != 2) || (i < URLEnd + 7)) + { + // LF too early, without a CR, without two preceeding spaces or too soon after the second space + m_IsValid = false; + return -1; + } + // Check that there's HTTP/version at the end + if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0) + { + m_IsValid = false; + return -1; + } + m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart); + m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1); + return i + 1; + } + } // switch (m_IncomingHeaderData[i]) + } // for i - m_IncomingHeaderData[] + + // CRLF hasn't been encountered yet, consider all data consumed + return a_Size; +} + + + + + +void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value) +{ + if ( + (NoCaseCompare(a_Key, "Authorization") == 0) && + (strncmp(a_Value.c_str(), "Basic ", 6) == 0) + ) + { + AString UserPass = Base64Decode(a_Value.substr(6)); + size_t idxCol = UserPass.find(':'); + if (idxCol != AString::npos) + { + m_AuthUsername = UserPass.substr(0, idxCol); + m_AuthPassword = UserPass.substr(idxCol + 1); + m_HasAuth = true; + } + } + AddHeader(a_Key, a_Value); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPResponse: + +cHTTPResponse::cHTTPResponse(void) : + super(mkResponse) +{ +} + + + + + +void cHTTPResponse::AppendToData(AString & a_DataStream) const +{ + a_DataStream.append("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: "); + a_DataStream.append(m_ContentType); + a_DataStream.append("\r\n"); + for (cNameValueMap::const_iterator itr = m_Headers.begin(), end = m_Headers.end(); itr != end; ++itr) + { + if ((itr->first == "Content-Type") || (itr->first == "Content-Length")) + { + continue; + } + a_DataStream.append(itr->first); + a_DataStream.append(": "); + a_DataStream.append(itr->second); + a_DataStream.append("\r\n"); + } // for itr - m_Headers[] + a_DataStream.append("\r\n"); +} + + + + diff --git a/src/HTTPServer/HTTPMessage.h b/src/HTTPServer/HTTPMessage.h new file mode 100644 index 000000000..f5284c535 --- /dev/null +++ b/src/HTTPServer/HTTPMessage.h @@ -0,0 +1,164 @@ + +// HTTPMessage.h + +// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes + + + + + +#pragma once + +#include "EnvelopeParser.h" + + + + + +class cHTTPMessage +{ +public: + enum + { + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + } ; + + enum eKind + { + mkRequest, + mkResponse, + } ; + + cHTTPMessage(eKind a_Kind); + + /// Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length + void AddHeader(const AString & a_Key, const AString & a_Value); + + void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; } + void SetContentLength(int a_ContentLength) { m_ContentLength = a_ContentLength; } + + const AString & GetContentType (void) const { return m_ContentType; } + int GetContentLength(void) const { return m_ContentLength; } + +protected: + typedef std::map cNameValueMap; + + eKind m_Kind; + + cNameValueMap m_Headers; + + /// Type of the content; parsed by AddHeader(), set directly by SetContentLength() + AString m_ContentType; + + /// Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength() + int m_ContentLength; +} ; + + + + + +class cHTTPRequest : + public cHTTPMessage, + protected cEnvelopeParser::cCallbacks +{ + typedef cHTTPMessage super; + +public: + cHTTPRequest(void); + + /** Parses the request line and then headers from the received data. + Returns the number of bytes consumed or a negative number for error + */ + int ParseHeaders(const char * a_Data, int a_Size); + + /// Returns true if the request did contain a Content-Length header + bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); } + + /// Returns the method used in the request + const AString & GetMethod(void) const { return m_Method; } + + /// Returns the URL used in the request + const AString & GetURL(void) const { return m_URL; } + + /// Returns the URL used in the request, without any parameters + AString GetBareURL(void) const; + + /// Sets the UserData pointer that is stored within this request. The request doesn't touch this data (doesn't delete it)! + void SetUserData(void * a_UserData) { m_UserData = a_UserData; } + + /// Retrieves the UserData pointer that has been stored within this request. + void * GetUserData(void) const { return m_UserData; } + + /// Returns true if more data is expected for the request headers + bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); } + + /// Returns true if the request did present auth data that was understood by the parser + bool HasAuth(void) const { return m_HasAuth; } + + /// Returns the username that the request presented. Only valid if HasAuth() is true + const AString & GetAuthUsername(void) const { return m_AuthUsername; } + + /// Returns the password that the request presented. Only valid if HasAuth() is true + const AString & GetAuthPassword(void) const { return m_AuthPassword; } + +protected: + /// Parser for the envelope data + cEnvelopeParser m_EnvelopeParser; + + /// True if the data received so far is parsed successfully. When false, all further parsing is skipped + bool m_IsValid; + + /// Bufferred incoming data, while parsing for the request line + AString m_IncomingHeaderData; + + /// Method of the request (GET / PUT / POST / ...) + AString m_Method; + + /// Full URL of the request + AString m_URL; + + /// Data that the HTTPServer callbacks are allowed to store. + void * m_UserData; + + /// Set to true if the request contains auth data that was understood by the parser + bool m_HasAuth; + + /// The username used for auth + AString m_AuthUsername; + + /// The password used for auth + AString m_AuthPassword; + + + /** Parses the incoming data for the first line (RequestLine) + Returns the number of bytes consumed, or -1 for an error + */ + int ParseRequestLine(const char * a_Data, int a_Size); + + // cEnvelopeParser::cCallbacks overrides: + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override; +} ; + + + + + +class cHTTPResponse : + public cHTTPMessage +{ + typedef cHTTPMessage super; + +public: + cHTTPResponse(void); + + /** Appends the response to the specified datastream - response line and headers. + The body will be sent later directly through cConnection::Send() + */ + void AppendToData(AString & a_DataStream) const; +} ; + + + + diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp new file mode 100644 index 000000000..f6f5b0f8b --- /dev/null +++ b/src/HTTPServer/HTTPServer.cpp @@ -0,0 +1,258 @@ + +// HTTPServer.cpp + +// Implements the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing + +#include "Globals.h" +#include "HTTPServer.h" +#include "HTTPMessage.h" +#include "HTTPConnection.h" +#include "HTTPFormParser.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +class cDebugCallbacks : + public cHTTPServer::cCallbacks, + protected cHTTPFormParser::cCallbacks +{ + virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + { + if (cHTTPFormParser::HasFormData(a_Request)) + { + a_Request.SetUserData(new cHTTPFormParser(a_Request, *this)); + } + } + + + virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override + { + cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData()); + if (FormParser != NULL) + { + FormParser->Parse(a_Data, a_Size); + } + } + + + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + { + cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData()); + if (FormParser != NULL) + { + if (FormParser->Finish()) + { + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send("\r\n"); + for (cHTTPFormParser::iterator itr = FormParser->begin(), end = FormParser->end(); itr != end; ++itr) + { + a_Connection.Send(Printf("\r\n", itr->first.c_str(), itr->second.c_str())); + } // for itr - FormParser[] + a_Connection.Send("
NameValue
%s
%s
"); + return; + } + + // Parsing failed: + cHTTPResponse Resp; + Resp.SetContentType("text/plain"); + a_Connection.Send(Resp); + a_Connection.Send("Form parsing failed"); + return; + } + + // Test the auth failure and success: + if (a_Request.GetURL() == "/auth") + { + if (!a_Request.HasAuth() || (a_Request.GetAuthUsername() != "a") || (a_Request.GetAuthPassword() != "b")) + { + a_Connection.SendNeedAuth("MCServer WebAdmin"); + return; + } + } + + cHTTPResponse Resp; + Resp.SetContentType("text/plain"); + a_Connection.Send(Resp); + a_Connection.Send("Hello, world"); + } + + + virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override + { + // TODO + } + + + virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override + { + // TODO + } + + + virtual void OnFileEnd(cHTTPFormParser & a_Parser) override + { + // TODO + } + +} g_DebugCallbacks; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPServer: + +cHTTPServer::cHTTPServer(void) : + m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer IPv4"), + m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"), + m_Callbacks(NULL) +{ +} + + + + + +cHTTPServer::~cHTTPServer() +{ + Stop(); +} + + + + + +bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6) +{ + bool HasAnyPort; + HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4); + HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort; + if (!HasAnyPort) + { + return false; + } + + return true; +} + + + + + +bool cHTTPServer::Start(cCallbacks & a_Callbacks) +{ + m_Callbacks = &a_Callbacks; + if (!m_ListenThreadIPv4.Start()) + { + return false; + } + if (!m_ListenThreadIPv6.Start()) + { + m_ListenThreadIPv4.Stop(); + return false; + } + return true; +} + + + + + +void cHTTPServer::Stop(void) +{ + m_ListenThreadIPv4.Stop(); + m_ListenThreadIPv6.Stop(); + + // Drop all current connections: + cCSLock Lock(m_CSConnections); + while (!m_Connections.empty()) + { + m_Connections.front()->Terminate(); + } // for itr - m_Connections[] +} + + + + + +void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket) +{ + cHTTPConnection * Connection = new cHTTPConnection(*this); + m_SocketThreads.AddClient(a_Socket, Connection); + cCSLock Lock(m_CSConnections); + m_Connections.push_back(Connection); +} + + + + + +void cHTTPServer::CloseConnection(cHTTPConnection & a_Connection) +{ + m_SocketThreads.RemoveClient(&a_Connection); + cCSLock Lock(m_CSConnections); + for (cHTTPConnections::iterator itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (*itr == &a_Connection) + { + m_Connections.erase(itr); + break; + } + } + delete &a_Connection; +} + + + + + +void cHTTPServer::NotifyConnectionWrite(cHTTPConnection & a_Connection) +{ + m_SocketThreads.NotifyWrite(&a_Connection); +} + + + + + +void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + m_Callbacks->OnRequestBegun(a_Connection, a_Request); +} + + + + + +void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) +{ + m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size); +} + + + + + +void cHTTPServer::RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + m_Callbacks->OnRequestFinished(a_Connection, a_Request); + a_Connection.AwaitNextRequest(); +} + + + + diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h new file mode 100644 index 000000000..fea2a9029 --- /dev/null +++ b/src/HTTPServer/HTTPServer.h @@ -0,0 +1,101 @@ + +// HTTPServer.h + +// Declares the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing + + + + + +#pragma once + +#include "../OSSupport/ListenThread.h" +#include "../OSSupport/SocketThreads.h" +#include "../../iniFile/iniFile.h" + + + + + +// fwd: +class cHTTPMessage; +class cHTTPRequest; +class cHTTPResponse; +class cHTTPConnection; + +typedef std::vector cHTTPConnections; + + + + + + +class cHTTPServer : + public cListenThread::cCallback +{ +public: + class cCallbacks + { + public: + /** Called when a new request arrives over a connection and its headers have been parsed. + The request body needn't have arrived yet. + */ + virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; + + /// Called when another part of request body has arrived. + virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) = 0; + + /// Called when the request body has been fully received in previous calls to OnRequestBody() + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; + } ; + + cHTTPServer(void); + ~cHTTPServer(); + + /// Initializes the server on the specified ports + bool Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6); + + /// Starts the server and assigns the callbacks to use for incoming requests + bool Start(cCallbacks & a_Callbacks); + + /// Stops the server, drops all current connections + void Stop(void); + +protected: + friend class cHTTPConnection; + + cListenThread m_ListenThreadIPv4; + cListenThread m_ListenThreadIPv6; + + cSocketThreads m_SocketThreads; + + cCriticalSection m_CSConnections; + cHTTPConnections m_Connections; ///< All the connections that are currently being serviced + + /// The callbacks to call for various events + cCallbacks * m_Callbacks; + + + // cListenThread::cCallback overrides: + virtual void OnConnectionAccepted(cSocket & a_Socket) override; + + /// Called by cHTTPConnection to close the connection (presumably due to an error) + void CloseConnection(cHTTPConnection & a_Connection); + + /// Called by cHTTPConnection to notify SocketThreads that there's data to be sent for the connection + void NotifyConnectionWrite(cHTTPConnection & a_Connection); + + /// Called by cHTTPConnection when it finishes parsing the request header + void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + + /// Called by cHTTPConenction when it receives more data for the request body + void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size); + + /// Called by cHTTPConnection when it detects that the request has finished (all of its body has been received) + void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); +} ; + + + + + diff --git a/src/HTTPServer/MultipartParser.cpp b/src/HTTPServer/MultipartParser.cpp new file mode 100644 index 000000000..b49f6ec07 --- /dev/null +++ b/src/HTTPServer/MultipartParser.cpp @@ -0,0 +1,256 @@ + +// MultipartParser.cpp + +// Implements the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts + +#include "Globals.h" +#include "MultipartParser.h" +#include "NameValueParser.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// self-test: + +#if 0 + +class cMultipartParserTest : + public cMultipartParser::cCallbacks +{ +public: + cMultipartParserTest(void) + { + cMultipartParser Parser("multipart/mixed; boundary=\"MyBoundaryString\"; foo=bar", *this); + const char Data[] = +"ThisIsIgnoredPrologue\r\n\ +--MyBoundaryString\r\n\ +\r\n\ +Body with confusing strings\r\n\ +--NotABoundary\r\n\ +--MyBoundaryStringWithPostfix\r\n\ +--\r\n\ +--MyBoundaryString\r\n\ +content-disposition: inline\r\n\ +\r\n\ +This is body\r\n\ +--MyBoundaryString\r\n\ +\r\n\ +Headerless body with trailing CRLF\r\n\ +\r\n\ +--MyBoundaryString--\r\n\ +ThisIsIgnoredEpilogue"; + printf("Multipart parsing test commencing.\n"); + Parser.Parse(Data, sizeof(Data) - 1); + // DEBUG: Check if the onscreen output corresponds with the data above + printf("Multipart parsing test finished\n"); + } + + virtual void OnPartStart(void) override + { + printf("Starting a new part\n"); + } + + + virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override + { + printf(" Hdr: \"%s\"=\"%s\"\n", a_Key.c_str(), a_Value.c_str()); + } + + + virtual void OnPartData(const char * a_Data, int a_Size) override + { + printf(" Data: %d bytes, \"%.*s\"\n", a_Size, a_Size, a_Data); + } + + + virtual void OnPartEnd(void) override + { + printf("Part end\n"); + } +} g_Test; + +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMultipartParser: + + +cMultipartParser::cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_IsValid(true), + m_EnvelopeParser(*this), + m_HasHadData(false) +{ + static AString s_Multipart = "multipart/"; + + // Check that the content type is multipart: + AString ContentType(a_ContentType); + if (strncmp(ContentType.c_str(), "multipart/", 10) != 0) + { + m_IsValid = false; + return; + } + size_t idxSC = ContentType.find(';', 10); + if (idxSC == AString::npos) + { + m_IsValid = false; + return; + } + + // Find the multipart boundary: + ContentType.erase(0, idxSC + 1); + cNameValueParser CTParser(ContentType.c_str(), ContentType.size()); + CTParser.Finish(); + if (!CTParser.IsValid()) + { + m_IsValid = false; + return; + } + m_Boundary = CTParser["boundary"]; + m_IsValid = !m_Boundary.empty(); + if (!m_IsValid) + { + return; + } + + // Set the envelope parser for parsing the body, so that our Parse() function parses the ignored prefix data as a body + m_EnvelopeParser.SetIsInHeaders(false); + + // Append an initial CRLF to the incoming data, so that a body starting with the boundary line will get caught + m_IncomingData.assign("\r\n"); + + /* + m_Boundary = AString("\r\n--") + m_Boundary + m_BoundaryEnd = m_Boundary + "--\r\n"; + m_Boundary = m_Boundary + "\r\n"; + */ +} + + + + + +void cMultipartParser::Parse(const char * a_Data, int a_Size) +{ + // Skip parsing if invalid + if (!m_IsValid) + { + return; + } + + // Append to buffer, then parse it: + m_IncomingData.append(a_Data, a_Size); + while (true) + { + if (m_EnvelopeParser.IsInHeaders()) + { + int BytesConsumed = m_EnvelopeParser.Parse(m_IncomingData.data(), m_IncomingData.size()); + if (BytesConsumed < 0) + { + m_IsValid = false; + return; + } + if ((BytesConsumed == a_Size) && m_EnvelopeParser.IsInHeaders()) + { + // All the incoming data has been consumed and still waiting for more + return; + } + m_IncomingData.erase(0, BytesConsumed); + } + + // Search for boundary / boundary end: + size_t idxBoundary = m_IncomingData.find("\r\n--"); + if (idxBoundary == AString::npos) + { + // Boundary string start not present, present as much data to the part callback as possible + if (m_IncomingData.size() > m_Boundary.size() + 8) + { + size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8; + m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport); + m_IncomingData.erase(0, BytesToReport); + } + return; + } + if (idxBoundary > 0) + { + m_Callbacks.OnPartData(m_IncomingData.data(), idxBoundary); + m_IncomingData.erase(0, idxBoundary); + } + idxBoundary = 4; + size_t LineEnd = m_IncomingData.find("\r\n", idxBoundary); + if (LineEnd == AString::npos) + { + // Not a complete line yet, present as much data to the part callback as possible + if (m_IncomingData.size() > m_Boundary.size() + 8) + { + size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8; + m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport); + m_IncomingData.erase(0, BytesToReport); + } + return; + } + if ( + (LineEnd - idxBoundary != m_Boundary.size()) && // Line length not equal to boundary + (LineEnd - idxBoundary != m_Boundary.size() + 2) // Line length not equal to boundary end + ) + { + // Got a line, but it's not a boundary, report it as data: + m_Callbacks.OnPartData(m_IncomingData.data(), LineEnd); + m_IncomingData.erase(0, LineEnd); + continue; + } + + if (strncmp(m_IncomingData.c_str() + idxBoundary, m_Boundary.c_str(), m_Boundary.size()) == 0) + { + // Boundary or BoundaryEnd found: + m_Callbacks.OnPartEnd(); + size_t idxSlash = idxBoundary + m_Boundary.size(); + if ((m_IncomingData[idxSlash] == '-') && (m_IncomingData[idxSlash + 1] == '-')) + { + // This was the last part + m_Callbacks.OnPartData(m_IncomingData.data() + idxSlash + 4, m_IncomingData.size() - idxSlash - 4); + m_IncomingData.clear(); + return; + } + m_Callbacks.OnPartStart(); + m_IncomingData.erase(0, LineEnd + 2); + + // Keep parsing for the headers that may have come with this data: + m_EnvelopeParser.Reset(); + continue; + } + + // It's a line, but not a boundary. It can be fully sent to the data receiver, since a boundary cannot cross lines + m_Callbacks.OnPartData(m_IncomingData.c_str(), LineEnd); + m_IncomingData.erase(0, LineEnd); + } // while (true) +} + + + + + +void cMultipartParser::OnHeaderLine(const AString & a_Key, const AString & a_Value) +{ + m_Callbacks.OnPartHeader(a_Key, a_Value); +} + + + + diff --git a/src/HTTPServer/MultipartParser.h b/src/HTTPServer/MultipartParser.h new file mode 100644 index 000000000..d853929ed --- /dev/null +++ b/src/HTTPServer/MultipartParser.h @@ -0,0 +1,76 @@ + +// MultipartParser.h + +// Declares the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts + + + + + +#pragma once + +#include "EnvelopeParser.h" + + + + + +class cMultipartParser : + protected cEnvelopeParser::cCallbacks +{ +public: + class cCallbacks + { + public: + /// Called when a new part starts + virtual void OnPartStart(void) = 0; + + /// Called when a complete header line is received for a part + virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) = 0; + + /// Called when body for a part is received + virtual void OnPartData(const char * a_Data, int a_Size) = 0; + + /// Called when the current part ends + virtual void OnPartEnd(void) = 0; + } ; + + /// Creates the parser, expects to find the boundary in a_ContentType + cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks); + + /// Parses more incoming data + void Parse(const char * a_Data, int a_Size); + +protected: + /// The callbacks to call for various parsing events + cCallbacks & m_Callbacks; + + /// True if the data parsed so far is valid; if false, further parsing is skipped + bool m_IsValid; + + /// Parser for each part's envelope + cEnvelopeParser m_EnvelopeParser; + + /// Buffer for the incoming data until it is parsed + AString m_IncomingData; + + /// The boundary, excluding both the initial "--" and the terminating CRLF + AString m_Boundary; + + /// Set to true if some data for the current part has already been signalized to m_Callbacks. Used for proper CRLF inserting. + bool m_HasHadData; + + + /// Parse one line of incoming data. The CRLF has already been stripped from a_Data / a_Size + void ParseLine(const char * a_Data, int a_Size); + + /// Parse one line of incoming data in the headers section of a part. The CRLF has already been stripped from a_Data / a_Size + void ParseHeaderLine(const char * a_Data, int a_Size); + + // cEnvelopeParser overrides: + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override; +} ; + + + + diff --git a/src/HTTPServer/NameValueParser.cpp b/src/HTTPServer/NameValueParser.cpp new file mode 100644 index 000000000..a27f07d19 --- /dev/null +++ b/src/HTTPServer/NameValueParser.cpp @@ -0,0 +1,412 @@ + +// NameValueParser.cpp + +// Implements the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap + +#include "Globals.h" +#include "NameValueParser.h" + + + + + + +// DEBUG: Self-test + +#if 0 + +class cNameValueParserTest +{ +public: + cNameValueParserTest(void) + { + const char Data[] = " Name1=Value1;Name2 = Value 2; Name3 =\"Value 3\"; Name4 =\'Value 4\'; Name5=\"Confusing; isn\'t it?\""; + + // Now try parsing char-by-char, to debug transitions across datachunk boundaries: + cNameValueParser Parser2; + for (int i = 0; i < sizeof(Data) - 1; i++) + { + Parser2.Parse(Data + i, 1); + } + Parser2.Finish(); + + // Parse as a single chunk of data: + cNameValueParser Parser(Data, sizeof(Data) - 1); + + // Use the debugger to inspect the Parser variable + + // Check that the two parsers have the same content: + for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr) + { + ASSERT(Parser2[itr->first] == itr->second); + } // for itr - Parser[] + + // Try parsing in 2-char chunks: + cNameValueParser Parser3; + for (int i = 0; i < sizeof(Data) - 2; i += 2) + { + Parser3.Parse(Data + i, 2); + } + if ((sizeof(Data) % 2) == 0) // There are even number of chars, including the NUL, so the data has an odd length. Parse one more char + { + Parser3.Parse(Data + sizeof(Data) - 2, 1); + } + Parser3.Finish(); + + // Check that the third parser has the same content: + for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr) + { + ASSERT(Parser3[itr->first] == itr->second); + } // for itr - Parser[] + + printf("cNameValueParserTest done"); + } +} g_Test; + +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNameValueParser: + +cNameValueParser::cNameValueParser(bool a_AllowsKeyOnly) : + m_State(psKeySpace), + m_AllowsKeyOnly(a_AllowsKeyOnly) +{ +} + + + + + +cNameValueParser::cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly) : + m_State(psKeySpace), + m_AllowsKeyOnly(a_AllowsKeyOnly) +{ + Parse(a_Data, a_Size); +} + + + + + +void cNameValueParser::Parse(const char * a_Data, int a_Size) +{ + ASSERT(m_State != psFinished); // Calling Parse() after Finish() is wrong! + + if ((m_State == psInvalid) || (m_State == psFinished)) + { + return; + } + int Last = 0; + for (int i = 0; i < a_Size;) + { + switch (m_State) + { + case psKeySpace: + { + // Skip whitespace until a non-whitespace is found, then start the key: + while ((i < a_Size) && (a_Data[i] <= ' ')) + { + i++; + } + if ((i < a_Size) && (a_Data[i] > ' ')) + { + m_State = psKey; + Last = i; + } + break; + } + + case psKey: + { + // Read the key until whitespace or an equal sign: + while (i < a_Size) + { + if (a_Data[i] == '=') + { + m_CurrentKey.append(a_Data + Last, i - Last); + i++; + Last = i; + m_State = psEqual; + break; + } + else if (a_Data[i] <= ' ') + { + m_CurrentKey.append(a_Data + Last, i - Last); + i++; + Last = i; + m_State = psEqualSpace; + break; + } + else if (a_Data[i] == ';') + { + if (!m_AllowsKeyOnly) + { + m_State = psInvalid; + return; + } + m_CurrentKey.append(a_Data + Last, i - Last); + i++; + Last = i; + (*this)[m_CurrentKey] = ""; + m_CurrentKey.clear(); + m_State = psKeySpace; + break; + } + else if ((a_Data[i] == '\"') || (a_Data[i] == '\'')) + { + m_State = psInvalid; + return; + } + i++; + } // while (i < a_Size) + if (i == a_Size) + { + // Still the key, ran out of data to parse, store the part of the key parsed so far: + m_CurrentKey.append(a_Data + Last, a_Size - Last); + return; + } + break; + } + + case psEqualSpace: + { + // The space before the expected equal sign; the current key is already assigned + while (i < a_Size) + { + if (a_Data[i] == '=') + { + m_State = psEqual; + i++; + Last = i; + break; + } + else if (a_Data[i] == ';') + { + // Key-only + if (!m_AllowsKeyOnly) + { + m_State = psInvalid; + return; + } + i++; + Last = i; + (*this)[m_CurrentKey] = ""; + m_CurrentKey.clear(); + m_State = psKeySpace; + break; + } + else if (a_Data[i] > ' ') + { + m_State = psInvalid; + return; + } + i++; + } // while (i < a_Size) + break; + } // case psEqualSpace + + case psEqual: + { + // just parsed the equal-sign + while (i < a_Size) + { + if (a_Data[i] == ';') + { + if (!m_AllowsKeyOnly) + { + m_State = psInvalid; + return; + } + i++; + Last = i; + (*this)[m_CurrentKey] = ""; + m_CurrentKey.clear(); + m_State = psKeySpace; + break; + } + else if (a_Data[i] == '\"') + { + i++; + Last = i; + m_State = psValueInDQuotes; + break; + } + else if (a_Data[i] == '\'') + { + i++; + Last = i; + m_State = psValueInSQuotes; + break; + } + else + { + m_CurrentValue.push_back(a_Data[i]); + i++; + Last = i; + m_State = psValueRaw; + break; + } + i++; + } // while (i < a_Size) + break; + } // case psEqual + + case psValueInDQuotes: + { + while (i < a_Size) + { + if (a_Data[i] == '\"') + { + m_CurrentValue.append(a_Data + Last, i - Last); + (*this)[m_CurrentKey] = m_CurrentValue; + m_CurrentKey.clear(); + m_CurrentValue.clear(); + m_State = psAfterValue; + i++; + Last = i; + break; + } + i++; + } // while (i < a_Size) + if (i == a_Size) + { + m_CurrentValue.append(a_Data + Last, a_Size - Last); + } + break; + } // case psValueInDQuotes + + case psValueInSQuotes: + { + while (i < a_Size) + { + if (a_Data[i] == '\'') + { + m_CurrentValue.append(a_Data + Last, i - Last); + (*this)[m_CurrentKey] = m_CurrentValue; + m_CurrentKey.clear(); + m_CurrentValue.clear(); + m_State = psAfterValue; + i++; + Last = i; + break; + } + i++; + } // while (i < a_Size) + if (i == a_Size) + { + m_CurrentValue.append(a_Data + Last, a_Size - Last); + } + break; + } // case psValueInSQuotes + + case psValueRaw: + { + while (i < a_Size) + { + if (a_Data[i] == ';') + { + m_CurrentValue.append(a_Data + Last, i - Last); + (*this)[m_CurrentKey] = m_CurrentValue; + m_CurrentKey.clear(); + m_CurrentValue.clear(); + m_State = psKeySpace; + i++; + Last = i; + break; + } + i++; + } + if (i == a_Size) + { + m_CurrentValue.append(a_Data + Last, a_Size - Last); + } + break; + } // case psValueRaw + + case psAfterValue: + { + // Between the closing DQuote or SQuote and the terminating semicolon + while (i < a_Size) + { + if (a_Data[i] == ';') + { + m_State = psKeySpace; + i++; + Last = i; + break; + } + else if (a_Data[i] < ' ') + { + i++; + continue; + } + m_State = psInvalid; + return; + } // while (i < a_Size) + break; + } + } // switch (m_State) + } // for i - a_Data[] +} + + + + + +bool cNameValueParser::Finish(void) +{ + switch (m_State) + { + case psInvalid: + { + return false; + } + case psFinished: + { + return true; + } + case psKey: + case psEqualSpace: + case psEqual: + { + if ((m_AllowsKeyOnly) && !m_CurrentKey.empty()) + { + (*this)[m_CurrentKey] = ""; + m_State = psFinished; + return true; + } + m_State = psInvalid; + return false; + } + case psValueRaw: + { + (*this)[m_CurrentKey] = m_CurrentValue; + m_State = psFinished; + return true; + } + case psValueInDQuotes: + case psValueInSQuotes: + { + // Missing the terminating quotes, this is an error + m_State = psInvalid; + return false; + } + case psKeySpace: + case psAfterValue: + { + m_State = psFinished; + return true; + } + } + ASSERT(!"Unhandled parser state!"); + return false; +} + + + + diff --git a/src/HTTPServer/NameValueParser.h b/src/HTTPServer/NameValueParser.h new file mode 100644 index 000000000..07dc0b942 --- /dev/null +++ b/src/HTTPServer/NameValueParser.h @@ -0,0 +1,70 @@ + +// NameValueParser.h + +// Declares the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap + + + + + +#pragma once + + + + + +class cNameValueParser : + public std::map +{ +public: + /// Creates an empty parser + cNameValueParser(bool a_AllowsKeyOnly = true); + + /// Creates an empty parser, then parses the data given. Doesn't call Finish(), so more data can be parsed later + cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly = true); + + /// Parses the data given + void Parse(const char * a_Data, int a_Size); + + /// Notifies the parser that no more data will be coming. Returns true if the parser state is valid + bool Finish(void); + + /// Returns true if the data parsed so far was valid + bool IsValid(void) const { return (m_State != psInvalid); } + + /// Returns true if the parser expects no more data + bool IsFinished(void) const { return ((m_State == psInvalid) || (m_State == psFinished)); } + +protected: + enum eState + { + psKeySpace, ///< Parsing the space in front of the next key + psKey, ///< Currently adding more chars to the key in m_CurrentKey + psEqualSpace, ///< Space after m_CurrentKey + psEqual, ///< Just parsed the = sign after a name + psValueInSQuotes, ///< Just parsed a Single-quote sign after the Equal sign + psValueInDQuotes, ///< Just parsed a Double-quote sign after the Equal sign + psValueRaw, ///< Just parsed a raw value without a quote + psAfterValue, ///< Just finished parsing the value, waiting for semicolon or data end + psInvalid, ///< The parser has encountered an invalid input; further parsing is skipped + psFinished, ///< The parser has already been instructed to finish and doesn't expect any more data + } ; + + /// The current state of the parser + eState m_State; + + /// If true, the parser will accept keys without an equal sign and the value + bool m_AllowsKeyOnly; + + /// Buffer for the current Key + AString m_CurrentKey; + + /// Buffer for the current Value; + AString m_CurrentValue; + + +} ; + + + + diff --git a/src/Inventory.cpp b/src/Inventory.cpp new file mode 100644 index 000000000..90b998358 --- /dev/null +++ b/src/Inventory.cpp @@ -0,0 +1,682 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Inventory.h" +#include "Entities/Player.h" +#include "ClientHandle.h" +#include "UI/Window.h" +#include "Item.h" +#include "Root.h" +#include "World.h" + +#include + +#include "Items/ItemHandler.h" + + + + + +cInventory::cInventory(cPlayer & a_Owner) : + m_ArmorSlots (1, 4), // 1 x 4 slots + m_InventorySlots(9, 3), // 9 x 3 slots + m_HotbarSlots (9, 1), // 9 x 1 slots + m_Owner(a_Owner) +{ + // Ask each ItemGrid to report changes to us: + m_ArmorSlots.AddListener(*this); + m_InventorySlots.AddListener(*this); + m_HotbarSlots.AddListener(*this); + + SetEquippedSlotNum(0); +} + + + + + +void cInventory::Clear(void) +{ + m_ArmorSlots.Clear(); + m_InventorySlots.Clear(); + m_HotbarSlots.Clear(); +} + + + + + +int cInventory::HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots) +{ + return HowManyCanFit(a_ItemStack, 0, invNumSlots - 1, a_ConsiderEmptySlots); +} + + + + + +int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots) +{ + if ((a_BeginSlotNum < 0) || (a_BeginSlotNum >= invNumSlots)) + { + LOGWARNING("%s: Bad BeginSlotNum, got %d, there are %d slots; correcting to 0.", __FUNCTION__, a_BeginSlotNum, invNumSlots - 1); + a_BeginSlotNum = 0; + } + if ((a_EndSlotNum < 0) || (a_EndSlotNum >= invNumSlots)) + { + LOGWARNING("%s: Bad EndSlotNum, got %d, there are %d slots; correcting to %d.", __FUNCTION__, a_BeginSlotNum, invNumSlots, invNumSlots - 1); + a_EndSlotNum = invNumSlots - 1; + } + if (a_BeginSlotNum > a_EndSlotNum) + { + std::swap(a_BeginSlotNum, a_EndSlotNum); + } + + char NumLeft = a_ItemStack.m_ItemCount; + int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); + for (int i = a_BeginSlotNum; i <= a_EndSlotNum; i++) + { + const cItem & Slot = GetSlot(i); + if (Slot.IsEmpty()) + { + NumLeft -= MaxStack; + } + else if (Slot.IsStackableWith(a_ItemStack)) + { + NumLeft -= MaxStack - Slot.m_ItemCount; + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + return a_ItemStack.m_ItemCount - NumLeft; +} + + + + + +int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst) +{ + cItem ToAdd(a_Item); + int res = 0; + if (ItemCategory::IsArmor(a_Item.m_ItemType)) + { + res = m_ArmorSlots.AddItem(ToAdd, a_AllowNewStacks); + ToAdd.m_ItemCount -= res; + if (ToAdd.m_ItemCount == 0) + { + return res; + } + } + + res += m_HotbarSlots.AddItem(ToAdd, a_AllowNewStacks, a_tryToFillEquippedFirst ? m_EquippedSlotNum : -1); + ToAdd.m_ItemCount = a_Item.m_ItemCount - res; + if (ToAdd.m_ItemCount == 0) + { + return res; + } + + res += m_InventorySlots.AddItem(ToAdd, a_AllowNewStacks); + return res; +} + + + + + +int cInventory::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst) +{ + int TotalAdded = 0; + for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();) + { + int NumAdded = AddItem(*itr, a_AllowNewStacks, a_tryToFillEquippedFirst); + if (itr->m_ItemCount == NumAdded) + { + itr = a_ItemStackList.erase(itr); + } + else + { + itr->m_ItemCount -= NumAdded; + ++itr; + } + TotalAdded += NumAdded; + } + return TotalAdded; +} + + + + + +bool cInventory::RemoveOneEquippedItem(void) +{ + if (m_HotbarSlots.GetSlot(m_EquippedSlotNum).IsEmpty()) + { + return false; + } + + m_HotbarSlots.ChangeSlotCount(m_EquippedSlotNum, -1); + return true; +} + + + + + +int cInventory::HowManyItems(const cItem & a_Item) +{ + return + m_ArmorSlots.HowManyItems(a_Item) + + m_InventorySlots.HowManyItems(a_Item) + + m_HotbarSlots.HowManyItems(a_Item); +} + + + + + +bool cInventory::HasItems(const cItem & a_ItemStack) +{ + int CurrentlyHave = HowManyItems(a_ItemStack); + return (CurrentlyHave >= a_ItemStack.m_ItemCount); +} + + + + + +void cInventory::SetSlot(int a_SlotNum, const cItem & a_Item) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Ignoring.", __FUNCTION__, a_SlotNum, invNumSlots - 1); + return; + } + + int GridSlotNum = 0; + cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); + if (Grid == NULL) + { + LOGWARNING("%s(%d): requesting an invalid itemgrid. Ignoring.", __FUNCTION__, a_SlotNum); + return; + } + Grid->SetSlot(GridSlotNum, a_Item); +} + + + + + +void cInventory::SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item) +{ + m_ArmorSlots.SetSlot(a_ArmorSlotNum, a_Item); +} + + + + + +void cInventory::SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item) +{ + m_InventorySlots.SetSlot(a_InventorySlotNum, a_Item); +} + + + + + +void cInventory::SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item) +{ + m_HotbarSlots.SetSlot(a_HotBarSlotNum, a_Item); +} + + + + + +const cItem & cInventory::GetSlot(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first inventory slot instead.", __FUNCTION__, a_SlotNum, invNumSlots - 1); + return m_InventorySlots.GetSlot(0); + } + int GridSlotNum = 0; + const cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); + if (Grid == NULL) + { + // Something went wrong, but we don't know what. We must return a value, so return the first inventory slot + LOGWARNING("%s(%d): requesting an invalid ItemGrid, returning the first inventory slot instead.", __FUNCTION__, a_SlotNum); + return m_InventorySlots.GetSlot(0); + } + return Grid->GetSlot(GridSlotNum); +} + + + + + +const cItem & cInventory::GetArmorSlot(int a_ArmorSlotNum) const +{ + if ((a_ArmorSlotNum < 0) || (a_ArmorSlotNum >= invArmorCount)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_ArmorSlotNum, invArmorCount - 1); + return m_ArmorSlots.GetSlot(0); + } + return m_ArmorSlots.GetSlot(a_ArmorSlotNum); +} + + + + + +const cItem & cInventory::GetInventorySlot(int a_InventorySlotNum) const +{ + if ((a_InventorySlotNum < 0) || (a_InventorySlotNum >= invInventoryCount)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_InventorySlotNum, invInventoryCount - 1); + return m_InventorySlots.GetSlot(0); + } + return m_InventorySlots.GetSlot(a_InventorySlotNum); +} + + + + + +const cItem & cInventory::GetHotbarSlot(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_SlotNum, invHotbarCount - 1); + return m_HotbarSlots.GetSlot(0); + } + return m_HotbarSlots.GetSlot(a_SlotNum); +} + + + + + +const cItem & cInventory::GetEquippedItem(void) const +{ + return GetHotbarSlot(m_EquippedSlotNum); +} + + + + + +void cInventory::SetEquippedSlotNum(int a_SlotNum) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount)) + { + LOGWARNING("%s: requesting invalid slot index: %d out of %d. Setting 0 instead.", __FUNCTION__, a_SlotNum, invHotbarCount - 1); + m_EquippedSlotNum = 0; + } + else + { + m_EquippedSlotNum = a_SlotNum; + } +} + + + + + +bool cInventory::DamageEquippedItem(short a_Amount) +{ + return DamageItem(invHotbarOffset + m_EquippedSlotNum, a_Amount); +} + + + + + +int cInventory::ChangeSlotCount(int a_SlotNum, int a_AddToCount) +{ + int GridSlotNum = 0; + cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); + if (Grid == NULL) + { + LOGWARNING("%s: invalid slot number, expected 0 .. %d, got %d; ignoring", __FUNCTION__, invNumSlots, a_SlotNum); + return -1; + } + return Grid->ChangeSlotCount(GridSlotNum, a_AddToCount); +} + + + + + +bool cInventory::DamageItem(int a_SlotNum, short a_Amount) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d", __FUNCTION__, a_SlotNum, invNumSlots - 1); + return false; + } + + int GridSlotNum = 0; + cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); + if (Grid == NULL) + { + LOGWARNING("%s(%d, %d): requesting an invalid grid, ignoring.", __FUNCTION__, a_SlotNum, a_Amount); + return false; + } + if (!Grid->DamageItem(GridSlotNum, a_Amount)) + { + // The item has been damaged, but did not break yet + return false; + } + + // The item has broken, remove it: + Grid->EmptySlot(GridSlotNum); + return true; +} + + + + + +void cInventory::CopyToItems(cItems & a_Items) +{ + m_ArmorSlots.CopyToItems(a_Items); + m_InventorySlots.CopyToItems(a_Items); + m_HotbarSlots.CopyToItems(a_Items); +} + + + + + +void cInventory::SendSlot(int a_SlotNum) +{ + cItem Item(GetSlot(a_SlotNum)); + if (Item.IsEmpty()) + { + // Sanitize items that are not completely empty (ie. count == 0, but type != empty) + Item.Empty(); + } + m_Owner.GetClientHandle()->SendInventorySlot(0, a_SlotNum + 5, Item); // Slots in the client are numbered "+ 5" because of crafting grid and result +} + + + + + +/* +int cInventory::MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot) +{ + int res = 0; + for (int i = a_BeginSlot; i <= a_EndSlot; i++) + { + if ( + m_Slots[i].IsEmpty() || + ((m_Slots[i].m_ItemType == a_ItemType) && (m_Slots[i].m_ItemDamage == a_ItemDamage)) + ) + { + int MaxCount = ItemHandler(a_ItemType)->GetMaxStackSize(); + ASSERT(m_Slots[i].m_ItemCount <= MaxCount); + int NumToMove = std::min(a_Count, MaxCount - m_Slots[i].m_ItemCount); + m_Slots[i].m_ItemCount += NumToMove; + m_Slots[i].m_ItemDamage = a_ItemDamage; + m_Slots[i].m_ItemType = a_ItemType; + SendSlot(i); + res += NumToMove; + a_Count -= NumToMove; + if (a_Count <= 0) + { + // No more items to distribute + return res; + } + } + } // for i - m_Slots[] + // No more space to distribute to + return res; +} +*/ + + + + + +int cInventory::ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum) +{ + switch (a_ArmorSlotNum) + { + case 0: return 4; // Helmet + case 1: return 3; // Chestplate + case 2: return 2; // Leggings + case 3: return 1; // Boots + } + LOGWARN("%s: invalid armor slot number: %d", __FUNCTION__, a_ArmorSlotNum); + return 0; +} + + + + + +#if 0 +bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */ ) +{ + // Fill already present stacks + if( a_Mode < 2 ) + { + int MaxStackSize = cItemHandler::GetItemHandler(a_Item.m_ItemType)->GetMaxStackSize(); + for(int i = 0; i < a_Size; i++) + { + if( m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType && m_Slots[i + a_Offset].m_ItemCount < MaxStackSize && m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage ) + { + int NumFree = MaxStackSize - m_Slots[i + a_Offset].m_ItemCount; + if( NumFree >= a_Item.m_ItemCount ) + { + + //printf("1. Adding %i items ( free: %i )\n", a_Item.m_ItemCount, NumFree ); + m_Slots[i + a_Offset].m_ItemCount += a_Item.m_ItemCount; + a_Item.m_ItemCount = 0; + a_bChangedSlots[i + a_Offset] = true; + break; + } + else + { + //printf("2. Adding %i items\n", NumFree ); + m_Slots[i + a_Offset].m_ItemCount += (char)NumFree; + a_Item.m_ItemCount -= (char)NumFree; + a_bChangedSlots[i + a_Offset] = true; + } + } + } + } + + if( a_Mode > 0 ) + { + // If we got more left, find first empty slot + for(int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++) + { + if( m_Slots[i + a_Offset].m_ItemType == -1 ) + { + m_Slots[i + a_Offset] = a_Item; + a_Item.m_ItemCount = 0; + a_bChangedSlots[i + a_Offset] = true; + } + } + } + + return true; +} +#endif + + + + + +void cInventory::SaveToJson(Json::Value & a_Value) +{ + // The JSON originally included the 4 crafting slots and the result, so we have to put empty items there, too: + cItem EmptyItem; + Json::Value EmptyItemJson; + EmptyItem.GetJson(EmptyItemJson); + for (int i = 0; i < 5; i++) + { + a_Value.append(EmptyItemJson); + } + + // The 4 armor slots follow: + for (int i = 0; i < invArmorCount; i++) + { + Json::Value JSON_Item; + m_ArmorSlots.GetSlot(i).GetJson(JSON_Item); + a_Value.append(JSON_Item); + } + + // Next comes the main inventory: + for (int i = 0; i < invInventoryCount; i++) + { + Json::Value JSON_Item; + m_InventorySlots.GetSlot(i).GetJson(JSON_Item); + a_Value.append(JSON_Item); + } + + // The hotbar is the last: + for (int i = 0; i < invHotbarCount; i++) + { + Json::Value JSON_Item; + m_HotbarSlots.GetSlot(i).GetJson(JSON_Item); + a_Value.append(JSON_Item); + } +} + + + + + +bool cInventory::LoadFromJson(Json::Value & a_Value) +{ + int SlotIdx = 0; + + for (Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr, SlotIdx++) + { + cItem Item; + Item.FromJson(*itr); + + // The JSON originally included the 4 crafting slots and the result slot, so we need to skip the first 5 items: + if (SlotIdx < 5) + { + continue; + } + + // If we loaded all the slots, stop now, even if the JSON has more: + if (SlotIdx - 5 >= invNumSlots) + { + break; + } + + int GridSlotNum = 0; + cItemGrid * Grid = GetGridForSlotNum(SlotIdx - 5, GridSlotNum); + ASSERT(Grid != NULL); + Grid->SetSlot(GridSlotNum, Item); + } // for itr - a_Value[] + return true; +} + + + + + +const cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const +{ + ASSERT(a_SlotNum >= 0); + + if (a_SlotNum < invArmorCount) + { + a_GridSlotNum = a_SlotNum; + return &m_ArmorSlots; + } + a_SlotNum -= invArmorCount; + if (a_SlotNum < invInventoryCount) + { + a_GridSlotNum = a_SlotNum; + return &m_InventorySlots; + } + a_GridSlotNum = a_SlotNum - invInventoryCount; + return &m_HotbarSlots; +} + + + + + +cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) +{ + ASSERT(a_SlotNum >= 0); + + if (a_SlotNum < invArmorCount) + { + a_GridSlotNum = a_SlotNum; + return &m_ArmorSlots; + } + a_SlotNum -= invArmorCount; + if (a_SlotNum < invInventoryCount) + { + a_GridSlotNum = a_SlotNum; + return &m_InventorySlots; + } + a_GridSlotNum = a_SlotNum - invInventoryCount; + return &m_HotbarSlots; +} + + + + + +void cInventory::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + // Send the neccessary updates to whoever needs them + + if (m_Owner.IsDestroyed()) + { + // Owner is not (yet) valid, skip for now + return; + } + + // Armor update needs broadcast to other players: + cWorld * World = m_Owner.GetWorld(); + if ((a_ItemGrid == &m_ArmorSlots) && (World != NULL)) + { + World->BroadcastEntityEquipment( + m_Owner, ArmorSlotNumToEntityEquipmentID(a_SlotNum), + m_ArmorSlots.GetSlot(a_SlotNum), m_Owner.GetClientHandle() + ); + } + + // Convert the grid-local a_SlotNum to our global SlotNum: + int Base = 0; + if (a_ItemGrid == &m_ArmorSlots) + { + Base = invArmorOffset; + } + else if (a_ItemGrid == &m_InventorySlots) + { + Base = invInventoryOffset; + } + else if (a_ItemGrid == &m_HotbarSlots) + { + Base = invHotbarOffset; + } + else + { + ASSERT(!"Unknown ItemGrid calling OnSlotChanged()"); + return; + } + + SendSlot(Base + a_SlotNum); +} + + + + diff --git a/src/Inventory.h b/src/Inventory.h new file mode 100644 index 000000000..3c6a19de8 --- /dev/null +++ b/src/Inventory.h @@ -0,0 +1,182 @@ + +#pragma once + +#include "ItemGrid.h" + + + + + +namespace Json +{ + class Value; +}; + +class cClientHandle; +class cPlayer; + + + + +// tolua_begin + +/** This class represents the player's inventory +The slots are divided into three areas: +- armor slots (1 x 4) +- inventory slots (9 x 3) +- hotbar slots (9 x 1) +The generic GetSlot(), SetSlot() and HowManyCanFit() functions take the index of the slots, +as if armor slots, inventory slots and then hotbar slots were put one after another. +You can use the invArmorOffset, invInventoryOffset and invHotbarOffset constants. +*/ + +class cInventory : + public cItemGrid::cListener +{ +public: + + // Counts and offsets to individual parts of the inventory, as used by GetSlot() / SetSlot() / HowManyCanFit(): + enum + { + invArmorCount = 4, + invInventoryCount = 9 * 3, + invHotbarCount = 9, + + invArmorOffset = 0, + invInventoryOffset = invArmorOffset + invArmorCount, + invHotbarOffset = invInventoryOffset + invInventoryCount, + invNumSlots = invHotbarOffset + invHotbarCount + } ; + + // tolua_end + + cInventory(cPlayer & a_Owner); + + // tolua_begin + + /// Removes all items from the entire inventory + void Clear(void); + + /// Returns number of items out of a_ItemStack that can fit in the storage + int HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots); + + /// Returns how many items of the specified type would fit into the slot range specified + int HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots); + + /** Adds as many items out of a_ItemStack as can fit. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_tryToFillEquippedFirst is set to true, the currently equipped slot will be used first (if empty or + compatible with added items) + if a_tryToFillEquippedFirst is set to false, the regular order applies. + Returns the number of items that fit. + */ + int AddItem(const cItem & a_ItemStack, bool a_AllowNewStacks = true, bool a_tryToFillEquippedFirst = false); + + /** Same as AddItem, but works on an entire list of item stacks. + The a_ItemStackList is modified to reflect the leftover items. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_tryToFillEquippedFirst is set to true, the currently equipped slot will be used first (if empty or + compatible with added items) + if a_tryToFillEquippedFirst is set to false, the regular order applies. + Returns the total number of items that fit. + */ + int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst); + + /// Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed + bool RemoveOneEquippedItem(void); + + /// Returns the number of items of type a_Item that are stored + int HowManyItems(const cItem & a_Item); + + /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack + bool HasItems(const cItem & a_ItemStack); + + /// Returns the cItemGrid object representing the armor slots + cItemGrid & GetArmorGrid(void) { return m_ArmorSlots; } + + /// Returns the cItemGrid object representing the main inventory slots + cItemGrid & GetInventoryGrid(void) { return m_InventorySlots; } + + /// Returns the cItemGrid object representing the hotbar slots + cItemGrid & GetHotbarGrid(void) { return m_HotbarSlots; } + + /// Returns the player associated with this inventory + cPlayer & GetOwner(void) { return m_Owner; } + + /// Copies the non-empty slots into a_ItemStacks; preserves the original a_Items contents + void CopyToItems(cItems & a_Items); + + // tolua_end + + /// Returns the player associated with this inventory (const version) + const cPlayer & GetOwner(void) const { return m_Owner; } + + // tolua_begin + + const cItem & GetSlot(int a_SlotNum) const; + const cItem & GetArmorSlot(int a_ArmorSlotNum) const; + const cItem & GetInventorySlot(int a_InventorySlotNum) const; + const cItem & GetHotbarSlot(int a_HotBarSlotNum) const; + const cItem & GetEquippedItem(void) const; + void SetSlot(int a_SlotNum, const cItem & a_Item); + void SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item); + void SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item); + void SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item); + + void SetEquippedSlotNum(int a_SlotNum); + int GetEquippedSlotNum(void) { return m_EquippedSlotNum; } + + /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. + If the slot is empty, ignores the call. + Returns the new count, or -1 if the slot number is invalid. + */ + int ChangeSlotCount(int a_SlotNum, int a_AddToCount); + + /// Adds the specified damage to the specified item; deletes the item and returns true if the item broke. + bool DamageItem(int a_SlotNum, short a_Amount); + + /// Adds the specified damage to the currently held item; deletes the item and returns true if the item broke. + bool DamageEquippedItem(short a_Amount = 1); + + const cItem & GetEquippedHelmet (void) const { return m_ArmorSlots.GetSlot(0); } + const cItem & GetEquippedChestplate(void) const { return m_ArmorSlots.GetSlot(1); } + const cItem & GetEquippedLeggings (void) const { return m_ArmorSlots.GetSlot(2); } + const cItem & GetEquippedBoots (void) const { return m_ArmorSlots.GetSlot(3); } + + // tolua_end + + /// Sends the slot contents to the owner + void SendSlot(int a_SlotNum); + + /// Converts an armor slot number into the ID for the EntityEquipment packet + static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum); + + void SaveToJson(Json::Value & a_Value); + bool LoadFromJson(Json::Value & a_Value); + +protected: + bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0 ); + + cItemGrid m_ArmorSlots; + cItemGrid m_InventorySlots; + cItemGrid m_HotbarSlots; + + int m_EquippedSlotNum; + + cPlayer & m_Owner; + + /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum + const cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const; + + /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum + cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum); + + // cItemGrid::cListener override: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; +}; // tolua_export + + + + diff --git a/src/Item.cpp b/src/Item.cpp new file mode 100644 index 000000000..25664e4df --- /dev/null +++ b/src/Item.cpp @@ -0,0 +1,261 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Item.h" +#include +#include "Items/ItemHandler.h" + + + + + +cItem cItem::CopyOne(void) const +{ + cItem res(*this); + res.m_ItemCount = 1; + return res; +} + + + + + +cItem & cItem::AddCount(char a_AmountToAdd) +{ + m_ItemCount += a_AmountToAdd; + if (m_ItemCount <= 0) + { + Empty(); + } + return *this; +} + + + + + +short cItem::GetMaxDamage(void) const +{ + switch (m_ItemType) + { + case E_ITEM_BOW: return 384; + case E_ITEM_DIAMOND_AXE: return 1563; + case E_ITEM_DIAMOND_HOE: return 1563; + case E_ITEM_DIAMOND_PICKAXE: return 1563; + case E_ITEM_DIAMOND_SHOVEL: return 1563; + case E_ITEM_DIAMOND_SWORD: return 1563; + case E_ITEM_FLINT_AND_STEEL: return 65; + case E_ITEM_GOLD_AXE: return 32; + case E_ITEM_GOLD_HOE: return 32; + case E_ITEM_GOLD_PICKAXE: return 32; + case E_ITEM_GOLD_SHOVEL: return 32; + case E_ITEM_GOLD_SWORD: return 32; + case E_ITEM_IRON_AXE: return 251; + case E_ITEM_IRON_HOE: return 251; + case E_ITEM_IRON_PICKAXE: return 251; + case E_ITEM_IRON_SHOVEL: return 251; + case E_ITEM_IRON_SWORD: return 251; + case E_ITEM_SHEARS: return 251; + case E_ITEM_STONE_AXE: return 132; + case E_ITEM_STONE_HOE: return 132; + case E_ITEM_STONE_PICKAXE: return 132; + case E_ITEM_STONE_SHOVEL: return 132; + case E_ITEM_STONE_SWORD: return 132; + case E_ITEM_WOODEN_AXE: return 60; + case E_ITEM_WOODEN_HOE: return 60; + case E_ITEM_WOODEN_PICKAXE: return 60; + case E_ITEM_WOODEN_SHOVEL: return 60; + case E_ITEM_WOODEN_SWORD: return 60; + } + return 0; +} + + + + + +bool cItem::DamageItem(short a_Amount) +{ + short MaxDamage = GetMaxDamage(); + if (MaxDamage == 0) + { + // Item doesn't have damage + return false; + } + + m_ItemDamage += a_Amount; + return (m_ItemDamage >= MaxDamage); +} + + + + + +bool cItem::IsStackableWith(const cItem & a_OtherStack) const +{ + if (a_OtherStack.m_ItemType != m_ItemType) + { + return false; + } + if (a_OtherStack.m_ItemDamage != m_ItemDamage) + { + return false; + } + if (a_OtherStack.m_Enchantments != m_Enchantments) + { + return false; + } + + return true; +} + + + + + +bool cItem::IsFullStack(void) const +{ + return (m_ItemCount >= ItemHandler(m_ItemType)->GetMaxStackSize()); +} + + + + + +char cItem::GetMaxStackSize(void) const +{ + return ItemHandler(m_ItemType)->GetMaxStackSize(); +} + + + + + +/// Returns the cItemHandler responsible for this item type +cItemHandler * cItem::GetHandler(void) const +{ + return ItemHandler(m_ItemType); +} + + + + + +void cItem::GetJson(Json::Value & a_OutValue) const +{ + a_OutValue["ID"] = m_ItemType; + if (m_ItemType > 0) + { + a_OutValue["Count"] = m_ItemCount; + a_OutValue["Health"] = m_ItemDamage; + AString Enchantments(m_Enchantments.ToString()); + if (!Enchantments.empty()) + { + a_OutValue["ench"] = Enchantments; + } + } +} + + + + + +void cItem::FromJson(const Json::Value & a_Value) +{ + m_ItemType = (ENUM_ITEM_ID)a_Value.get("ID", -1 ).asInt(); + if (m_ItemType > 0) + { + m_ItemCount = (char)a_Value.get("Count", -1 ).asInt(); + m_ItemDamage = (short)a_Value.get("Health", -1 ).asInt(); + m_Enchantments.Clear(); + m_Enchantments.AddFromString(a_Value.get("ench", "").asString()); + } +} + + + + + +bool cItem::IsEnchantable(short item) +{ + if ((item >= 256) && (item <= 259)) + return true; + if ((item >= 267) && (item <= 279)) + return true; + if ((item >= 283) && (item <= 286)) + return true; + if ((item >= 290) && (item <= 294)) + return true; + if ((item >= 298) && (item <= 317)) + return true; + if ((item >= 290) && (item <= 294)) + return true; + + if ((item == 346) || (item == 359) || (item == 261)) + return true; + + return false; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cItems: + +cItem * cItems::Get(int a_Idx) +{ + if ((a_Idx < 0) || (a_Idx >= (int)size())) + { + LOGWARNING("cItems: Attempt to get an out-of-bounds item at index %d; there are currently %d items. Returning a nil.", a_Idx, size()); + return NULL; + } + return &at(a_Idx); +} + + + + + +void cItems::Set(int a_Idx, const cItem & a_Item) +{ + if ((a_Idx < 0) || (a_Idx >= (int)size())) + { + LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size()); + return; + } + at(a_Idx) = a_Item; +} + + + + + +void cItems::Delete(int a_Idx) +{ + if ((a_Idx < 0) || (a_Idx >= (int)size())) + { + LOGWARNING("cItems: Attempt to delete an item at an out-of-bounds index %d; there are currently %d items. Ignoring.", a_Idx, size()); + return; + } + erase(begin() + a_Idx); +} + + + + + +void cItems::Set(int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage) +{ + if ((a_Idx < 0) || (a_Idx >= (int)size())) + { + LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size()); + return; + } + at(a_Idx) = cItem(a_ItemType, a_ItemCount, a_ItemDamage); +} + + + + diff --git a/src/Item.h b/src/Item.h new file mode 100644 index 000000000..c60d0542c --- /dev/null +++ b/src/Item.h @@ -0,0 +1,210 @@ + +// Item.h + +// Declares the cItem class representing an item (in the inventory sense) + + + + + +#pragma once + +#include "Defines.h" +#include "Enchantments.h" + + + + + +// fwd: +class cItemHandler; + +namespace Json +{ + class Value; +} + + + + + +// tolua_begin +class cItem +{ +public: + /// Creates an empty item + cItem(void) : + m_ItemType(E_ITEM_EMPTY), + m_ItemCount(0), + m_ItemDamage(0) + { + } + + + /// Creates an item of the specified type, by default 1 piece with no damage and no enchantments + cItem( + short a_ItemType, + char a_ItemCount = 1, + short a_ItemDamage = 0, + const AString & a_Enchantments = "" + ) : + m_ItemType (a_ItemType), + m_ItemCount (a_ItemCount), + m_ItemDamage (a_ItemDamage), + m_Enchantments(a_Enchantments) + { + if (!IsValidItem(m_ItemType)) + { + if (m_ItemType != E_BLOCK_AIR) + { + LOGWARNING("%s: creating an invalid item type (%d), resetting to empty.", __FUNCTION__, a_ItemType); + } + Empty(); + } + } + + + /// Creates an exact copy of the item + cItem(const cItem & a_CopyFrom) : + m_ItemType (a_CopyFrom.m_ItemType), + m_ItemCount (a_CopyFrom.m_ItemCount), + m_ItemDamage (a_CopyFrom.m_ItemDamage), + m_Enchantments(a_CopyFrom.m_Enchantments) + { + } + + + void Empty(void) + { + m_ItemType = E_ITEM_EMPTY; + m_ItemCount = 0; + m_ItemDamage = 0; + m_Enchantments.Clear(); + } + + + void Clear(void) + { + m_ItemType = E_ITEM_EMPTY; + m_ItemCount = 0; + m_ItemDamage = 0; + } + + + bool IsEmpty(void) const + { + return ((m_ItemType <= 0) || (m_ItemCount <= 0)); + } + + + bool IsEqual(const cItem & a_Item) const + { + return ( + IsSameType(a_Item) && + (m_ItemDamage == a_Item.m_ItemDamage) && + (m_Enchantments == a_Item.m_Enchantments) + ); + } + + + bool IsSameType(const cItem & a_Item) const + { + return (m_ItemType == a_Item.m_ItemType) || (IsEmpty() && a_Item.IsEmpty()); + } + + + /// Returns a copy of this item with m_ItemCount set to 1. Useful to preserve enchantments etc. on stacked items + cItem CopyOne(void) const; + + /// Adds the specified count to this object and returns the reference to self (useful for chaining) + cItem & AddCount(char a_AmountToAdd); + + /// Returns the maximum damage value that this item can have; zero if damage is not applied + short GetMaxDamage(void) const; + + /// Damages a weapon / tool. Returns true when damage reaches max value and the item should be destroyed + bool DamageItem(short a_Amount = 1); + + inline bool IsDamageable(void) const { return (GetMaxDamage() > 0); } + + /// Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored! + bool IsStackableWith(const cItem & a_OtherStack) const; + + /// Returns true if the item is stacked up to its maximum stacking. + bool IsFullStack(void) const; + + /// Returns the maximum amount of stacked items of this type. + char GetMaxStackSize(void) const; + + // tolua_end + + /// Returns the cItemHandler responsible for this item type + cItemHandler * GetHandler(void) const; + + /// Saves the item data into JSON representation + void GetJson(Json::Value & a_OutValue) const; + + /// Loads the item data from JSON representation + void FromJson(const Json::Value & a_Value); + + /// Returns true if the specified item type is enchantable (as per 1.2.5 protocol requirements) + static bool IsEnchantable(short a_ItemType); + + // tolua_begin + + short m_ItemType; + char m_ItemCount; + short m_ItemDamage; + cEnchantments m_Enchantments; +}; +// tolua_end + + + + + +/** This class bridges a vector of cItem for safe access via Lua. It checks boundaries for all accesses +Note that this class is zero-indexed! +*/ +class cItems // tolua_export + : public std::vector +{ // tolua_export +public: + // tolua_begin + + /// Need a Lua-accessible constructor + cItems(void) {} + + cItem * Get (int a_Idx); + void Set (int a_Idx, const cItem & a_Item); + void Add (const cItem & a_Item) {push_back(a_Item); } + void Delete(int a_Idx); + void Clear (void) {clear(); } + int Size (void) {return size(); } + void Set (int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage); + + void Add (ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage) + { + push_back(cItem(a_ItemType, a_ItemCount, a_ItemDamage)); + } + + // tolua_end +} ; // tolua_export + + + + + +/// Used to store loot probability tables +class cLootProbab +{ +public: + cItem m_Item; + int m_MinAmount; + int m_MaxAmount; + int m_Weight; +} ; + + + + diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp new file mode 100644 index 000000000..e9b86173e --- /dev/null +++ b/src/ItemGrid.cpp @@ -0,0 +1,665 @@ + +// ItemGrid.cpp + +// Implements the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) + +#include "Globals.h" +#include "ItemGrid.h" +#include "Items/ItemHandler.h" +#include "Noise.h" + + + + + +cItemGrid::cItemGrid(int a_Width, int a_Height) : + m_Width(a_Width), + m_Height(a_Height), + m_NumSlots(a_Width * a_Height), + m_Slots(new cItem[a_Width * a_Height]), + m_IsInTriggerListeners(false) +{ +} + + + + + +cItemGrid::~cItemGrid() +{ + delete[] m_Slots; +} + + + + + +int cItemGrid::GetSlotNum(int a_X, int a_Y) const +{ + if ( + (a_X < 0) || (a_X >= m_Width) || + (a_Y < 0) || (a_Y >= m_Height) + ) + { + LOGWARNING("%s: coords out of range: (%d, %d) in grid of size (%d, %d)", + __FUNCTION__, a_X, a_Y, m_Width, m_Height + ); + return -1; + } + return a_X + m_Width * a_Y; +} + + + + + +void cItemGrid::GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: SlotNum out of range: %d in grid of range %d", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + a_X = -1; + a_Y = -1; + return; + } + a_X = a_SlotNum % m_Width; + a_Y = a_SlotNum / m_Width; +} + + + + + +const cItem & cItemGrid::GetSlot(int a_X, int a_Y) const +{ + return GetSlot(GetSlotNum(a_X, a_Y)); +} + + + + + +const cItem & cItemGrid::GetSlot(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number, %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return m_Slots[0]; + } + return m_Slots[a_SlotNum]; +} + + + + + +void cItemGrid::SetSlot(int a_X, int a_Y, const cItem & a_Item) +{ + SetSlot(GetSlotNum(a_X, a_Y), a_Item); +} + + + + + +void cItemGrid::SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage) +{ + SetSlot(GetSlotNum(a_X, a_Y), cItem(a_ItemType, a_ItemCount, a_ItemDamage)); +} + + + + + +void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return; + } + m_Slots[a_SlotNum] = a_Item; + TriggerListeners(a_SlotNum); +} + + + + + +void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage) +{ + SetSlot(a_SlotNum, cItem(a_ItemType, a_ItemCount, a_ItemDamage)); +} + + + + + +void cItemGrid::EmptySlot(int a_X, int a_Y) +{ + EmptySlot(GetSlotNum(a_X, a_Y)); +} + + + + + +void cItemGrid::EmptySlot(int a_SlotNum) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return; + } + + // Check if already empty: + if (m_Slots[a_SlotNum].IsEmpty()) + { + return; + } + + // Empty and notify + m_Slots[a_SlotNum].Empty(); + TriggerListeners(a_SlotNum); +} + + + + + +bool cItemGrid::IsSlotEmpty(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return true; + } + return m_Slots[a_SlotNum].IsEmpty(); +} + + + + + +bool cItemGrid::IsSlotEmpty(int a_X, int a_Y) const +{ + return IsSlotEmpty(GetSlotNum(a_X, a_Y)); +} + + + + + +void cItemGrid::Clear(void) +{ + for (int i = 0; i < m_NumSlots; i++) + { + m_Slots[i].Empty(); + TriggerListeners(i); + } +} + + + + + +int cItemGrid::HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks) +{ + char NumLeft = a_ItemStack.m_ItemCount; + int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsEmpty()) + { + if (a_AllowNewStacks) + { + NumLeft -= MaxStack; + } + } + else if (m_Slots[i].IsStackableWith(a_ItemStack)) + { + NumLeft -= MaxStack - m_Slots[i].m_ItemCount; + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + return a_ItemStack.m_ItemCount - NumLeft; +} + + + + + +int cItemGrid::AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack) +{ + int PrevCount = 0; + if (m_Slots[a_Slot].IsEmpty()) + { + m_Slots[a_Slot] = a_ItemStack; + PrevCount = 0; + } + else + { + PrevCount = m_Slots[a_Slot].m_ItemCount; + } + m_Slots[a_Slot].m_ItemCount = std::min(a_MaxStack, PrevCount + a_Num); + int toReturn = m_Slots[a_Slot].m_ItemCount - PrevCount; + TriggerListeners(a_Slot); + return toReturn; +} + + + + + +int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_PrioritarySlot) +{ + int NumLeft = a_ItemStack.m_ItemCount; + int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); + + // Try prioritarySlot first: + if ( + (a_PrioritarySlot != -1) && + ( + m_Slots[a_PrioritarySlot].IsEmpty() || + m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack) + ) + ) + { + NumLeft -= AddItemToSlot(a_ItemStack, a_PrioritarySlot, NumLeft, MaxStack); + } + + // Scan existing stacks: + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsStackableWith(a_ItemStack)) + { + NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + + if (!a_AllowNewStacks) + { + return (a_ItemStack.m_ItemCount - NumLeft); + } + + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsEmpty()) + { + NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + return (a_ItemStack.m_ItemCount - NumLeft); +} + + + + + +int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, int a_PrioritarySlot) +{ + int TotalAdded = 0; + for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();) + { + int NumAdded = AddItem(*itr, a_AllowNewStacks, a_PrioritarySlot); + if (itr->m_ItemCount == NumAdded) + { + itr = a_ItemStackList.erase(itr); + } + else + { + itr->m_ItemCount -= NumAdded; + ++itr; + } + TotalAdded += NumAdded; + } + return TotalAdded; +} + + + + + +int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning -1", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return -1; + } + + if (m_Slots[a_SlotNum].IsEmpty()) + { + // The item is empty, it's not gonna change + return 0; + } + + if (m_Slots[a_SlotNum].m_ItemCount <= -a_AddToCount) + { + // Trying to remove more items than there already are, make the item empty + m_Slots[a_SlotNum].Empty(); + TriggerListeners(a_SlotNum); + return 0; + } + + m_Slots[a_SlotNum].m_ItemCount += a_AddToCount; + TriggerListeners(a_SlotNum); + return m_Slots[a_SlotNum].m_ItemCount; +} + + + + + +int cItemGrid::ChangeSlotCount(int a_X, int a_Y, int a_AddToCount) +{ + return ChangeSlotCount(GetSlotNum(a_X, a_Y), a_AddToCount); +} + + + + + +cItem cItemGrid::RemoveOneItem(int a_SlotNum) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning empty item", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return cItem(); + } + + // If the slot is empty, return an empty item + if (m_Slots[a_SlotNum].IsEmpty()) + { + return cItem(); + } + + // Make a copy of the item in slot, set count to 1 and remove one from the slot + cItem res = m_Slots[a_SlotNum]; + res.m_ItemCount = 1; + m_Slots[a_SlotNum].m_ItemCount -= 1; + + // Emptying the slot correctly if appropriate + if (m_Slots[a_SlotNum].m_ItemCount == 0) + { + m_Slots[a_SlotNum].Empty(); + } + + // Notify everyone of the change + TriggerListeners(a_SlotNum); + + // Return the stored one item + return res; +} + + + + + +cItem cItemGrid::RemoveOneItem(int a_X, int a_Y) +{ + return RemoveOneItem(GetSlotNum(a_X, a_Y)); +} + + + + + +int cItemGrid::HowManyItems(const cItem & a_Item) +{ + int res = 0; + for (int i = 0; i < m_NumSlots; i++) + { + if (m_Slots[i].IsStackableWith(a_Item)) + { + res += m_Slots[i].m_ItemCount; + } + } + return res; +} + + + + + +bool cItemGrid::HasItems(const cItem & a_ItemStack) +{ + int CurrentlyHave = HowManyItems(a_ItemStack); + return (CurrentlyHave >= a_ItemStack.m_ItemCount); +} + + + + + +int cItemGrid::GetFirstEmptySlot(void) const +{ + return GetNextEmptySlot(-1); +} + + + + + +int cItemGrid::GetFirstUsedSlot(void) const +{ + return GetNextUsedSlot(-1); +} + + + + + +int cItemGrid::GetLastEmptySlot(void) const +{ + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +int cItemGrid::GetLastUsedSlot(void) const +{ + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (!m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +int cItemGrid::GetNextEmptySlot(int a_StartFrom) const +{ + for (int i = a_StartFrom + 1; i < m_NumSlots; i++) + { + if (m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +int cItemGrid::GetNextUsedSlot(int a_StartFrom) const +{ + for (int i = a_StartFrom + 1; i < m_NumSlots; i++) + { + if (!m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +void cItemGrid::CopyToItems(cItems & a_Items) const +{ + for (int i = 0; i < m_NumSlots; i++) + { + if (!m_Slots[i].IsEmpty()) + { + a_Items.push_back(m_Slots[i]); + } + } // for i - m_Slots[] +} + + + + + +bool cItemGrid::DamageItem(int a_SlotNum, short a_Amount) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: invalid slot number %d out of %d slots, ignoring.", __FUNCTION__, a_SlotNum, m_NumSlots); + return false; + } + return m_Slots[a_SlotNum].DamageItem(a_Amount); +} + + + + + +bool cItemGrid::DamageItem(int a_X, int a_Y, short a_Amount) +{ + return DamageItem(GetSlotNum(a_X, a_Y), a_Amount); +} + + + + + +void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed) +{ + // Calculate the total weight: + int TotalProbab = 1; + for (int i = 0; i < a_CountLootProbabs; i++) + { + TotalProbab += a_LootProbabs[i].m_Weight; + } + + // Pick the loot items: + cNoise Noise(a_Seed); + for (int i = 0; i < a_NumSlots; i++) + { + int Rnd = (Noise.IntNoise1DInt(i) / 7); + int LootRnd = Rnd % TotalProbab; + Rnd >>= 8; + cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment + for (int j = 0; j < a_CountLootProbabs; j++) + { + LootRnd -= a_LootProbabs[i].m_Weight; + if (LootRnd < 0) + { + CurrentLoot = a_LootProbabs[i].m_Item; + CurrentLoot.m_ItemCount = a_LootProbabs[i].m_MinAmount + (Rnd % (a_LootProbabs[i].m_MaxAmount - a_LootProbabs[i].m_MinAmount)); + Rnd >>= 8; + break; + } + } // for j - a_LootProbabs[] + SetSlot(Rnd % m_NumSlots, CurrentLoot); + } // for i - NumSlots +} + + + + + +void cItemGrid::AddListener(cListener & a_Listener) +{ + cCSLock Lock(m_CSListeners); + ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() + m_Listeners.push_back(&a_Listener); +} + + + + + +void cItemGrid::RemoveListener(cListener & a_Listener) +{ + cCSLock Lock(m_CSListeners); + ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() + for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr) + { + if (*itr == &a_Listener) + { + m_Listeners.erase(itr); + return; + } + } // for itr - m_Listeners[] +} + + + + + +void cItemGrid::TriggerListeners(int a_SlotNum) +{ + cListeners Listeners; + { + cCSLock Lock(m_CSListeners); + m_IsInTriggerListeners = true; + Listeners = m_Listeners; + } + for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr) + { + (*itr)->OnSlotChanged(this, a_SlotNum); + } // for itr - m_Listeners[] + m_IsInTriggerListeners = false; +} + + + + diff --git a/src/ItemGrid.h b/src/ItemGrid.h new file mode 100644 index 000000000..a4af523cf --- /dev/null +++ b/src/ItemGrid.h @@ -0,0 +1,191 @@ + +// ItemGrid.h + +// Declares the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) + + + + +#pragma once + +#include "Item.h" + + + + + +// tolua_begin +class cItemGrid +{ +public: + // tolua_end + + /// This class is used as a callback for when a slot changes + class cListener + { + public: + /// Called whenever a slot changes + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) = 0; + } ; + typedef std::vector cListeners; + + cItemGrid(int a_Width, int a_Height); + + ~cItemGrid(); + + // tolua_begin + int GetWidth (void) const { return m_Width; } + int GetHeight (void) const { return m_Height; } + int GetNumSlots(void) const { return m_NumSlots; } + + /// Converts XY coords into slot number; returns -1 on invalid coords + int GetSlotNum(int a_X, int a_Y) const; + + // tolua_end + + /// Converts slot number into XY coords; sets coords to -1 on invalid slot number. Exported in ManualBindings.cpp + void GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const; + + // tolua_begin + + // Retrieve slots by coords or slot number; Logs warning and returns the first slot on invalid coords / slotnum + const cItem & GetSlot(int a_X, int a_Y) const; + const cItem & GetSlot(int a_SlotNum) const; + + // Set slot by coords or slot number; Logs warning and doesn't set on invalid coords / slotnum + void SetSlot(int a_X, int a_Y, const cItem & a_Item); + void SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage); + void SetSlot(int a_SlotNum, const cItem & a_Item); + void SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage); + + // Empty the specified slot; Logs warning and doesn't set on invalid coords / slotnum + void EmptySlot(int a_X, int a_Y); + void EmptySlot(int a_SlotNum); + + /// Returns true if the specified slot is empty or the slot doesn't exist + bool IsSlotEmpty(int a_SlotNum) const; + + /// Returns true if the specified slot is empty or the slot doesn't exist + bool IsSlotEmpty(int a_X, int a_Y) const; + + /// Sets all items as empty + void Clear(void); + + /// Returns number of items out of a_ItemStack that can fit in the storage + int HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks = true); + + /** Adds as many items out of a_ItemStack as can fit. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in + first (if empty or compatible with added items) + if a_PrioritarySlot is set to -1, regular order apply + Returns the number of items that fit. + */ + int AddItem(cItem & a_ItemStack, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); + + /** Same as AddItem, but works on an entire list of item stacks. + The a_ItemStackList is modified to reflect the leftover items. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in + first (if empty or compatible with added items) + if a_PrioritarySlot is set to -1, regular order apply + Returns the total number of items that fit. + */ + int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); + + /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. + If the slot is empty, ignores the call. + Returns the new count. + */ + int ChangeSlotCount(int a_SlotNum, int a_AddToCount); + + /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. + If the slot is empty, ignores the call. + Returns the new count. + */ + int ChangeSlotCount(int a_X, int a_Y, int a_AddToCount); + + /** Removes one item from the stack in the specified slot, and returns it. + If the slot was empty, returns an empty item + */ + cItem RemoveOneItem(int a_SlotNum); + + /** Removes one item from the stack in the specified slot, and returns it. + If the slot was empty, returns an empty item + */ + cItem RemoveOneItem(int a_X, int a_Y); + + /// Returns the number of items of type a_Item that are stored + int HowManyItems(const cItem & a_Item); + + /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack + bool HasItems(const cItem & a_ItemStack); + + /// Returns the index of the first empty slot; -1 if all full + int GetFirstEmptySlot(void) const; + + /// Returns the index of the first non-empty slot; -1 if all empty + int GetFirstUsedSlot(void) const; + + /// Returns the index of the last empty slot; -1 if all full + int GetLastEmptySlot(void) const; + + /// Returns the index of the last used slot; -1 if all empty + int GetLastUsedSlot(void) const; + + /// Returns the index of the first empty slot following a_StartFrom (a_StartFrom is not checked) + int GetNextEmptySlot(int a_StartFrom) const; + + /// Returns the index of the first used slot following a_StartFrom (a_StartFrom is not checked) + int GetNextUsedSlot(int a_StartFrom) const; + + /// Copies the contents into a cItems object; preserves the original a_Items contents + void CopyToItems(cItems & a_Items) const; + + /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) + bool DamageItem(int a_SlotNum, short a_Amount); + + /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) + bool DamageItem(int a_X, int a_Y, short a_Amount); + + // tolua_end + + + /** Generates random loot from the specified loot probability table, with a chance of enchanted books added. + A total of a_NumSlots are taken by the loot. + Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs + */ + void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed); + + /// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback! + void AddListener(cListener & a_Listener); + + /// Removes a slot-change-callback. Must not be called from within the listener callback! + void RemoveListener(cListener & a_Listener); + + // tolua_begin + +protected: + int m_Width; + int m_Height; + int m_NumSlots; // m_Width * m_Height, for easier validity checking in the access functions + cItem * m_Slots; // x + m_Width * y + + cListeners m_Listeners; ///< Listeners which should be notified on slot changes; the pointers are not owned by this object + cCriticalSection m_CSListeners; ///< CS that guards the m_Listeners against multi-thread access + bool m_IsInTriggerListeners; ///< Set to true while TriggerListeners is running, to detect attempts to manipulate listener list while triggerring + + /// Calls all m_Listeners for the specified slot number + void TriggerListeners(int a_SlotNum); + + /** Adds up to a_Num items out of a_ItemStack, as many as can fit, in specified slot + Returns the number of items that did fit. + */ + int AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack); +} ; +// tolua_end + + + diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h new file mode 100644 index 000000000..ab4182eea --- /dev/null +++ b/src/Items/ItemBed.h @@ -0,0 +1,56 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Blocks/BlockBed.h" + + + + + +class cItemBedHandler : + public cItemHandler +{ +public: + cItemBedHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (a_BlockFace != BLOCK_FACE_TOP) + { + // Can only be placed on the floor + return false; + } + + a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation()); + + // Check if there is empty space for the foot section: + Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta); + if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) + { + return false; + } + + a_BlockType = E_BLOCK_BED; + return true; + } +} ; + + + + diff --git a/src/Items/ItemBoat.h b/src/Items/ItemBoat.h new file mode 100644 index 000000000..6e3395f1d --- /dev/null +++ b/src/Items/ItemBoat.h @@ -0,0 +1,54 @@ + +// ItemBoat.h + +// Declares the various boat ItemHandlers + + + + + +#pragma once + +#include "../Entities/Boat.h" + + + + + +class cItemBoatHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemBoatHandler(int a_ItemType) : + super(a_ItemType) + { + } + + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + if (a_Dir < 0) + { + return false; + } + + double x = (double)a_BlockX + 0.5; + double y = (double)a_BlockY + 0.5; + double z = (double)a_BlockZ + 0.5; + + cBoat * Boat = NULL; + + Boat = new cBoat (x, y, z); + Boat->Initialize(a_World); + + return true; + } + +} ; + + + + diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h new file mode 100644 index 000000000..d533c21fd --- /dev/null +++ b/src/Items/ItemBow.h @@ -0,0 +1,87 @@ + +// ItemBow.h + +// Declares the cItemBowHandler class representing the itemhandler for bows + + + + + +#pragma once + +#include "../Entities/ProjectileEntity.h" + + + + + +class cItemBowHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemBowHandler(void) : + super(E_ITEM_BOW) + { + } + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + ASSERT(a_Player != NULL); + + // Check if the player has an arrow in the inventory, or is in Creative: + if (!(a_Player->IsGameModeCreative() || a_Player->GetInventory().HasItems(cItem(E_ITEM_ARROW)))) + { + return false; + } + + a_Player->StartChargingBow(); + return true; + } + + + virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override + { + // Actual shot - produce the arrow with speed based on the ticks that the bow was charged + ASSERT(a_Player != NULL); + + int BowCharge = a_Player->FinishChargingBow(); + double Force = (double)BowCharge / 20; + Force = (Force * Force + 2 * Force) / 3; // This formula is used by the 1.6.2 client + if (Force < 0.1) + { + // Too little force, ignore the shot + return; + } + if (Force > 1) + { + Force = 1; + } + + // Create the arrow entity: + cArrowEntity * Arrow = new cArrowEntity(*a_Player, Force * 2); + if (Arrow == NULL) + { + return; + } + if (!Arrow->Initialize(a_Player->GetWorld())) + { + delete Arrow; + return; + } + a_Player->GetWorld()->BroadcastSpawnEntity(*Arrow); + a_Player->GetWorld()->BroadcastSoundEffect("random.bow", (int)a_Player->GetPosX() * 8, (int)a_Player->GetPosY() * 8, (int)a_Player->GetPosZ() * 8, 0.5, (float)Force); + + if (!a_Player->IsGameModeCreative()) + { + a_Player->UseEquippedItem(); + } + } +} ; + + + + + diff --git a/src/Items/ItemBrewingStand.h b/src/Items/ItemBrewingStand.h new file mode 100644 index 000000000..4ff14d4b4 --- /dev/null +++ b/src/Items/ItemBrewingStand.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemBrewingStandHandler : + public cItemHandler +{ +public: + cItemBrewingStandHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_BREWING_STAND; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h new file mode 100644 index 000000000..fa3d48da1 --- /dev/null +++ b/src/Items/ItemBucket.h @@ -0,0 +1,160 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Simulator/FluidSimulator.h" +#include "../Blocks/BlockHandler.h" + + + + + +class cItemBucketHandler : + public cItemHandler +{ +public: + cItemBucketHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + switch (m_ItemType) + { + case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir); + case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA); + case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER); + default: + { + ASSERT(!"Unhandled ItemType"); + return false; + } + } + } + + + + bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + { + if (a_BlockFace < 0) + { + return false; + } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + BLOCKTYPE ClickedBlock; + NIBBLETYPE ClickedMeta; + a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedMeta); + LOGD("Bucket Clicked BlockType %d, meta %d", ClickedBlock, ClickedMeta); + if (ClickedMeta != 0) + { + // Not a source block + return false; + } + + if (a_Player->GetGameMode() == gmCreative) + { + // In creative mode don't modify the inventory, just remove the fluid: + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + return true; + } + + ENUM_ITEM_ID NewItem = E_ITEM_EMPTY; + switch (ClickedBlock) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + NewItem = E_ITEM_WATER_BUCKET; + break; + } + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + NewItem = E_ITEM_LAVA_BUCKET; + break; + } + + default: return false; + } + + // Remove the bucket from the inventory + if (!a_Player->GetInventory().RemoveOneEquippedItem()) + { + LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?"); + ASSERT(!"Inventory bucket mismatch"); + return true; + } + + // Give new bucket, filled with fluid: + cItem Item(NewItem, 1); + a_Player->GetInventory().AddItem(Item, true, true); + + // Remove water / lava block + a_Player->GetWorld()->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + return true; + } + + + bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_FluidBlock) + { + if (a_BlockFace < 0) + { + return false; + } + + BLOCKTYPE CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + bool CanWashAway = cFluidSimulator::CanWashAway(CurrentBlock); + if (!CanWashAway) + { + // The block pointed at cannot be washed away, so put fluid on top of it / on its sides + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + } + if ( + !CanWashAway && + (CurrentBlock != E_BLOCK_AIR) && + (CurrentBlock != E_BLOCK_WATER) && + (CurrentBlock != E_BLOCK_STATIONARY_WATER) && + (CurrentBlock != E_BLOCK_LAVA) && + (CurrentBlock != E_BLOCK_STATIONARY_LAVA) + ) + { + // Cannot place water here + return false; + } + + if (a_Player->GetGameMode() != gmCreative) + { + // Remove fluid bucket, add empty bucket: + if (!a_Player->GetInventory().RemoveOneEquippedItem()) + { + LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?"); + ASSERT(!"Inventory bucket mismatch"); + return false; + } + cItem Item(E_ITEM_BUCKET, 1); + if (!a_Player->GetInventory().AddItem(Item,true,true)) + { + return false; + } + } + + // Wash away anything that was there prior to placing: + if (CanWashAway) + { + cBlockHandler * Handler = BlockHandler(CurrentBlock); + if (Handler->DoesDropOnUnsuitable()) + { + Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + } + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FluidBlock, 0); + + return true; + } + +}; diff --git a/src/Items/ItemCauldron.h b/src/Items/ItemCauldron.h new file mode 100644 index 000000000..8b2ddc29f --- /dev/null +++ b/src/Items/ItemCauldron.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemCauldronHandler : + public cItemHandler +{ +public: + cItemCauldronHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_CAULDRON; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemCloth.h b/src/Items/ItemCloth.h new file mode 100644 index 000000000..aca27a299 --- /dev/null +++ b/src/Items/ItemCloth.h @@ -0,0 +1,23 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemClothHandler : + public cItemHandler +{ +public: + cItemClothHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } +} ; + + + + diff --git a/src/Items/ItemComparator.h b/src/Items/ItemComparator.h new file mode 100644 index 000000000..53dbd020d --- /dev/null +++ b/src/Items/ItemComparator.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +class cItemComparatorHandler : + public cItemHandler +{ +public: + cItemComparatorHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_INACTIVE_COMPARATOR; + a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; + } +} ; + + + + diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h new file mode 100644 index 000000000..72ea0beed --- /dev/null +++ b/src/Items/ItemDoor.h @@ -0,0 +1,45 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" + + + + + +class cItemDoorHandler : + public cItemHandler +{ +public: + cItemDoorHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = (m_ItemType == E_ITEM_WOODEN_DOOR) ? E_BLOCK_WOODEN_DOOR : E_BLOCK_IRON_DOOR; + return BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); + } +} ; + + + + diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h new file mode 100644 index 000000000..99b8d2543 --- /dev/null +++ b/src/Items/ItemDye.h @@ -0,0 +1,44 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemDyeHandler : + public cItemHandler +{ +public: + cItemDyeHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + // TODO: Handle coloring the sheep, too (OnItemUseOnEntity maybe) + + // Handle growing the plants: + if (a_Item.m_ItemDamage == E_META_DYE_WHITE) + { + if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true)) + { + if (a_Player->GetGameMode() != gmCreative) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + return true; + } + } + } + return false; + } +} ; + + + + diff --git a/src/Items/ItemFlowerPot.h b/src/Items/ItemFlowerPot.h new file mode 100644 index 000000000..befa2ff21 --- /dev/null +++ b/src/Items/ItemFlowerPot.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemFlowerPotHandler : + public cItemHandler +{ +public: + cItemFlowerPotHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_FLOWER_POT; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemFood.h b/src/Items/ItemFood.h new file mode 100644 index 000000000..2ae572331 --- /dev/null +++ b/src/Items/ItemFood.h @@ -0,0 +1,63 @@ +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemFoodHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemFoodHandler(int a_ItemType) + : super(a_ItemType) + { + } + + + virtual bool IsFood(void) override + { + return true; + } + + + virtual FoodInfo GetFoodInfo(void) override + { + switch(m_ItemType) + { + // Please keep alpha-sorted. + case E_ITEM_BAKED_POTATO: return FoodInfo(6, 7.2); + case E_ITEM_BREAD: return FoodInfo(5, 6); + case E_ITEM_CARROT: return FoodInfo(4, 4.8); + case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2); + case E_ITEM_COOKED_FISH: return FoodInfo(5, 6); + case E_ITEM_COOKED_PORKCHOP: return FoodInfo(8, 12.8); + case E_ITEM_COOKIE: return FoodInfo(2, 0.4); + case E_ITEM_GOLDEN_APPLE: return FoodInfo(4, 9.6); + case E_ITEM_GOLDEN_CARROT: return FoodInfo(6, 14.4); + case E_ITEM_MELON_SLICE: return FoodInfo(2, 1.2); + case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2, 60); + case E_ITEM_POTATO: return FoodInfo(1, 0.6); + case E_ITEM_PUMPKIN_PIE: return FoodInfo(8, 4.8); + case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8); + case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2, 30); + case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2); + case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8); + case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4); + case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8, 80); + case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2, 100); + case E_ITEM_STEAK: return FoodInfo(8, 12.8); + case E_ITEM_MUSHROOM_SOUP: return FoodInfo(6, 7.2); + } + LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType); + return FoodInfo(0, 0.f); + } + +}; + + + + diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp new file mode 100644 index 000000000..13f5293b9 --- /dev/null +++ b/src/Items/ItemHandler.cpp @@ -0,0 +1,509 @@ + +#include "Globals.h" +#include "ItemHandler.h" +#include "../Item.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../FastRandom.h" + +// Handlers: +#include "ItemBed.h" +#include "ItemBoat.h" +#include "ItemBow.h" +#include "ItemBrewingStand.h" +#include "ItemBucket.h" +#include "ItemCauldron.h" +#include "ItemCloth.h" +#include "ItemComparator.h" +#include "ItemDoor.h" +#include "ItemDye.h" +#include "ItemFlowerPot.h" +#include "ItemFood.h" +#include "ItemHoe.h" +#include "ItemLeaves.h" +#include "ItemLighter.h" +#include "ItemMinecart.h" +#include "ItemPickaxe.h" +#include "ItemThrowable.h" +#include "ItemRedstoneDust.h" +#include "ItemRedstoneRepeater.h" +#include "ItemSapling.h" +#include "ItemSeeds.h" +#include "ItemShears.h" +#include "ItemShovel.h" +#include "ItemSign.h" +#include "ItemSpawnEgg.h" +#include "ItemSugarcane.h" +#include "ItemSword.h" + +#include "../Blocks/BlockHandler.h" + + + + + +bool cItemHandler::m_HandlerInitialized = false; +cItemHandler * cItemHandler::m_ItemHandler[2268]; + + + + + +cItemHandler * cItemHandler::GetItemHandler(int a_ItemType) +{ + if (a_ItemType < 0) + { + // Either nothing (-1), or bad value, both cases should return the air handler + if (a_ItemType < -1) + { + ASSERT(!"Bad item type"); + } + a_ItemType = 0; + } + + if (!m_HandlerInitialized) + { + // We need to initialize + memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); + m_HandlerInitialized = true; + } + if (m_ItemHandler[a_ItemType] == NULL) + { + m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType); + } + return m_ItemHandler[a_ItemType]; +} + + + + + +cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) +{ + switch(a_ItemType) + { + default: return new cItemHandler(a_ItemType); + + // Single item per handler, alphabetically sorted: + case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); + case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); + case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); + case E_ITEM_BED: return new cItemBedHandler(a_ItemType); + case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType); + case E_ITEM_BOW: return new cItemBowHandler; + case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); + case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); + case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); + case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); + case E_ITEM_EGG: return new cItemEggHandler(); + case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); + case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); + case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); + case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); + case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); + case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); + case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); + case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); + case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); + case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); + + case E_ITEM_WOODEN_HOE: + case E_ITEM_STONE_HOE: + case E_ITEM_IRON_HOE: + case E_ITEM_GOLD_HOE: + case E_ITEM_DIAMOND_HOE: + { + return new cItemHoeHandler(a_ItemType); + } + + case E_ITEM_WOODEN_PICKAXE: + case E_ITEM_STONE_PICKAXE: + case E_ITEM_IRON_PICKAXE: + case E_ITEM_GOLD_PICKAXE: + case E_ITEM_DIAMOND_PICKAXE: + { + return new cItemPickaxeHandler(a_ItemType); + } + + case E_ITEM_WOODEN_SHOVEL: + case E_ITEM_STONE_SHOVEL: + case E_ITEM_IRON_SHOVEL: + case E_ITEM_GOLD_SHOVEL: + case E_ITEM_DIAMOND_SHOVEL: + { + return new cItemShovelHandler(a_ItemType); + } + + case E_ITEM_WOODEN_SWORD: + case E_ITEM_STONE_SWORD: + case E_ITEM_IRON_SWORD: + case E_ITEM_GOLD_SWORD: + case E_ITEM_DIAMOND_SWORD: + { + return new cItemSwordHandler(a_ItemType); + } + + case E_ITEM_BUCKET: + case E_ITEM_WATER_BUCKET: + case E_ITEM_LAVA_BUCKET: + { + return new cItemBucketHandler(a_ItemType); + } + + case E_ITEM_CARROT: + case E_ITEM_MELON_SEEDS: + case E_ITEM_POTATO: + case E_ITEM_PUMPKIN_SEEDS: + case E_ITEM_SEEDS: + { + return new cItemSeedsHandler(a_ItemType); + } + + case E_ITEM_IRON_DOOR: + case E_ITEM_WOODEN_DOOR: + { + return new cItemDoorHandler(a_ItemType); + } + + case E_ITEM_MINECART: + case E_ITEM_CHEST_MINECART: + case E_ITEM_FURNACE_MINECART: + case E_ITEM_MINECART_WITH_TNT: + case E_ITEM_MINECART_WITH_HOPPER: + { + return new cItemMinecartHandler(a_ItemType); + } + + // Food: + case E_ITEM_BREAD: + case E_ITEM_COOKIE: + case E_ITEM_MELON_SLICE: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_RAW_BEEF: + case E_ITEM_RAW_PORKCHOP: + case E_ITEM_STEAK: + case E_ITEM_COOKED_PORKCHOP: + case E_ITEM_RAW_FISH: + case E_ITEM_COOKED_FISH: + case E_ITEM_RED_APPLE: + case E_ITEM_GOLDEN_APPLE: + case E_ITEM_ROTTEN_FLESH: + case E_ITEM_MUSHROOM_SOUP: + case E_ITEM_SPIDER_EYE: + { + return new cItemFoodHandler(a_ItemType); + } + } +} + + + + + +void cItemHandler::Deinit() +{ + for(int i = 0; i < 2267; i++) + { + delete m_ItemHandler[i]; + } + memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); // Don't leave any dangling pointers around, just in case + m_HandlerInitialized = false; +} + + + + + +cItemHandler::cItemHandler(int a_ItemType) +{ + m_ItemType = a_ItemType; +} + + + + + +bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) +{ + return false; +} + + + + + +bool cItemHandler::OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) +{ + return false; +} + + + + + +void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block); + + if (a_Player->IsGameModeSurvival()) + { + if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) + { + Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + } + + a_Player->UseEquippedItem(); +} + + + + + +void cItemHandler::OnFoodEaten(cWorld * a_World, cPlayer * a_Player, cItem * a_Item) +{ + +} + + + + + +char cItemHandler::GetMaxStackSize(void) +{ + if (m_ItemType < 256) + { + // All blocks can stack up to 64 + return 64; + } + + switch (m_ItemType) //sorted by id + { + case E_ITEM_ARROW: return 64; + case E_ITEM_BAKED_POTATO: return 64; + case E_ITEM_BLAZE_POWDER: return 64; + case E_ITEM_BLAZE_ROD: return 64; + case E_ITEM_BONE: return 64; + case E_ITEM_BOOK: return 64; + case E_ITEM_BOTTLE_O_ENCHANTING: return 64; + case E_ITEM_BOWL: return 64; + case E_ITEM_BREAD: return 64; + case E_ITEM_BREWING_STAND: return 64; + case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3 + case E_ITEM_CARROT: return 64; + case E_ITEM_CAULDRON: return 64; + case E_ITEM_CLAY: return 64; + case E_ITEM_CLAY_BRICK: return 64; + case E_ITEM_CLOCK: return 64; + case E_ITEM_COAL: return 64; + case E_ITEM_COMPARATOR: return 64; + case E_ITEM_COMPASS: return 64; + case E_ITEM_COOKED_CHICKEN: return 64; + case E_ITEM_COOKED_FISH: return 64; + case E_ITEM_COOKED_PORKCHOP: return 64; + case E_ITEM_COOKIE: return 64; + case E_ITEM_DIAMOND: return 64; + case E_ITEM_DYE: return 64; + case E_ITEM_EGG: return 16; + case E_ITEM_EMERALD: return 64; + case E_ITEM_ENDER_PEARL: return 16; + case E_ITEM_EYE_OF_ENDER: return 64; + case E_ITEM_FEATHER: return 64; + case E_ITEM_FERMENTED_SPIDER_EYE: return 64; + case E_ITEM_FIRE_CHARGE: return 64; + case E_ITEM_FIREWORK_ROCKET: return 64; + case E_ITEM_FIREWORK_STAR: return 64; + case E_ITEM_FLINT: return 64; + case E_ITEM_FLOWER_POT: return 64; + case E_ITEM_GHAST_TEAR: return 64; + case E_ITEM_GLASS_BOTTLE: return 64; + case E_ITEM_GLISTERING_MELON: return 64; + case E_ITEM_GLOWSTONE_DUST: return 64; + case E_ITEM_GOLD: return 64; + case E_ITEM_GOLDEN_APPLE: return 64; + case E_ITEM_GOLDEN_CARROT: return 64; + case E_ITEM_GOLD_NUGGET: return 64; + case E_ITEM_GUNPOWDER: return 64; + case E_ITEM_HEAD: return 64; + case E_ITEM_IRON: return 64; + case E_ITEM_LEATHER: return 64; + case E_ITEM_MAGMA_CREAM: return 64; + case E_ITEM_MAP: return 64; + case E_ITEM_MELON_SEEDS: return 64; + case E_ITEM_MELON_SLICE: return 64; + case E_ITEM_NETHER_BRICK: return 64; + case E_ITEM_NETHER_WART: return 64; + case E_ITEM_PAINTINGS: return 64; + case E_ITEM_PAPER: return 64; + case E_ITEM_POISONOUS_POTATO: return 64; + case E_ITEM_POTATO: return 64; + case E_ITEM_PUMPKIN_PIE: return 64; + case E_ITEM_PUMPKIN_SEEDS: return 64; + case E_ITEM_RAW_BEEF: return 64; + case E_ITEM_RAW_CHICKEN: return 64; + case E_ITEM_RAW_FISH: return 64; + case E_ITEM_RAW_PORKCHOP: return 64; + case E_ITEM_RED_APPLE: return 64; + case E_ITEM_REDSTONE_DUST: return 64; + case E_ITEM_REDSTONE_REPEATER: return 64; + case E_ITEM_ROTTEN_FLESH: return 64; + case E_ITEM_SEEDS: return 64; + case E_ITEM_SIGN: return 16; + case E_ITEM_SLIMEBALL: return 64; + case E_ITEM_SNOWBALL: return 16; + case E_ITEM_SPAWN_EGG: return 64; + case E_ITEM_SPIDER_EYE: return 64; + case E_ITEM_STEAK: return 64; + case E_ITEM_STICK: return 64; + case E_ITEM_STRING: return 64; + case E_ITEM_SUGAR: return 64; + case E_ITEM_SUGAR_CANE: return 64; + case E_ITEM_WHEAT: return 64; + } + // By default items don't stack: + return 1; +} + + + + + +bool cItemHandler::IsTool() +{ + // TODO: Rewrite this to list all tools specifically + return + (m_ItemType >= 256 && m_ItemType <= 259) + || (m_ItemType == 261) + || (m_ItemType >= 267 && m_ItemType <= 279) + || (m_ItemType >= 283 && m_ItemType <= 286) + || (m_ItemType >= 290 && m_ItemType <= 294) + || (m_ItemType >= 256 && m_ItemType <= 259) + || (m_ItemType == 325) + || (m_ItemType == 346); +} + + + + + +bool cItemHandler::IsFood(void) +{ + switch (m_ItemType) + { + case E_ITEM_RED_APPLE: + case E_ITEM_GOLDEN_APPLE: + case E_ITEM_MUSHROOM_SOUP: + case E_ITEM_BREAD: + case E_ITEM_RAW_PORKCHOP: + case E_ITEM_COOKED_PORKCHOP: + case E_ITEM_MILK: + case E_ITEM_RAW_FISH: + case E_ITEM_COOKED_FISH: + case E_ITEM_COOKIE: + case E_ITEM_MELON_SLICE: + case E_ITEM_RAW_BEEF: + case E_ITEM_STEAK: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_ROTTEN_FLESH: + case E_ITEM_SPIDER_EYE: + case E_ITEM_CARROT: + case E_ITEM_POTATO: + case E_ITEM_BAKED_POTATO: + case E_ITEM_POISONOUS_POTATO: + { + return true; + } + } // switch (m_ItemType) + return false; +} + + + + + +bool cItemHandler::IsPlaceable(void) +{ + // We can place any block that has a corresponding E_BLOCK_TYPE: + return (m_ItemType >= 1) && (m_ItemType <= E_BLOCK_MAX_TYPE_ID); +} + + + + + +bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType) +{ + return false; +} + + + + + +bool cItemHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers + + if (m_ItemType > 256) + { + LOGERROR("%s: Item %d has no valid block!", __FUNCTION__, m_ItemType); + return false; + } + + cBlockHandler * BlockH = BlockHandler(m_ItemType); + return BlockH->GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); +} + + + + + +bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item) +{ + FoodInfo Info = GetFoodInfo(); + + if ((Info.FoodLevel > 0) || (Info.Saturation > 0.f)) + { + bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation); + + // If consumed and there's chance of foodpoisoning, do it: + if (Success && (Info.PoisonChance > 0)) + { + cFastRandom r1; + if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0) + { + a_Player->FoodPoison(300); + } + } + + return Success; + } + + return false; +} + + + + + +cItemHandler::FoodInfo cItemHandler::GetFoodInfo() +{ + return FoodInfo(0, 0.f); +} + + + + diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h new file mode 100644 index 000000000..e39bb054b --- /dev/null +++ b/src/Items/ItemHandler.h @@ -0,0 +1,99 @@ + +#pragma once + +#include "../Defines.h" +#include "../Item.h" + + + + + +// fwd: +class cWorld; +class cPlayer; + + + + + +class cItemHandler +{ +public: + cItemHandler(int a_ItemType); + + /// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); + + /// Called when the client sends the SHOOT status in the lclk packet + virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) {} + + /// Called while the player diggs a block using this item + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace); + + /// Called when the player destroys a block using this item. This also calls the drop function for the destroyed block + virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z); + + /// Called after the player has eaten this item. + virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item); + + /// Returns the maximum stack size for a given item + virtual char GetMaxStackSize(void); + + struct FoodInfo + { + int FoodLevel; + double Saturation; + int PoisonChance; // 0 - 100, in percent. 0 = no chance of poisoning, 100 = sure poisoning + + FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) : + FoodLevel(a_FoodLevel), + Saturation(a_Saturation), + PoisonChance(a_PoisonChance) + { + } + } ; + + /// Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance) + virtual FoodInfo GetFoodInfo(); + + /// Lets the player eat a selected item. Returns true if the player ate the item + virtual bool EatItem(cPlayer *a_Player, cItem *a_Item); + + /// Indicates if this item is a tool + virtual bool IsTool(void); + + /// Indicates if this item is food + virtual bool IsFood(void); + + /// Blocks simply get placed + virtual bool IsPlaceable(void); + + /** Called before a block is placed into a world. + The handler should return true to allow placement, false to refuse. + Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. + */ + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ); + + /// Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can´t) DEFAULT: False + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType); + + static cItemHandler * GetItemHandler(int a_ItemType); + static cItemHandler * GetItemHandler(const cItem & a_Item) { return GetItemHandler(a_Item.m_ItemType); } + + static void Deinit(); + +protected: + int m_ItemType; + static cItemHandler *CreateItemHandler(int m_ItemType); + + static cItemHandler * m_ItemHandler[E_ITEM_LAST + 1]; + static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized +}; + +//Short function +inline cItemHandler *ItemHandler(int a_ItemType) { return cItemHandler::GetItemHandler(a_ItemType); } diff --git a/src/Items/ItemHoe.h b/src/Items/ItemHoe.h new file mode 100644 index 000000000..7b6b3e6ac --- /dev/null +++ b/src/Items/ItemHoe.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemHoeHandler : + public cItemHandler +{ +public: + cItemHoeHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + + if ((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS)) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0); + + a_Player->UseEquippedItem(); + return true; + + } + return false; + } +} ; + + + + diff --git a/src/Items/ItemLeaves.h b/src/Items/ItemLeaves.h new file mode 100644 index 000000000..60222eaa9 --- /dev/null +++ b/src/Items/ItemLeaves.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemLeavesHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemLeavesHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + bool res = super::GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); + a_BlockMeta = a_BlockMeta | 0x4; //0x4 bit set means this is a player-placed leaves block, not to be decayed + return res; + } +} ; + + + + diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h new file mode 100644 index 000000000..4281a2d0c --- /dev/null +++ b/src/Items/ItemLighter.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../Entities/TNTEntity.h" + + + + + +class cItemLighterHandler : + public cItemHandler +{ +public: + cItemLighterHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override + { + if (a_BlockFace < 0) + { + return false; + } + + a_Player->UseEquippedItem(); + + switch (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) + { + case E_BLOCK_TNT: + { + // Activate the TNT: + a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom + a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0); + break; + } + default: + { + // Light a fire next to/on top of the block if air: + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) + { + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0); + break; + } + } + } + + return false; + } +} ; + + + + diff --git a/src/Items/ItemMinecart.h b/src/Items/ItemMinecart.h new file mode 100644 index 000000000..f8eb31a49 --- /dev/null +++ b/src/Items/ItemMinecart.h @@ -0,0 +1,82 @@ + +// ItemMinecart.h + +// Declares the various minecart ItemHandlers + + + + + +#pragma once + +#include "../Entities/Minecart.h" + + + + + +class cItemMinecartHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemMinecartHandler(int a_ItemType) : + super(a_ItemType) + { + } + + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + if (a_Dir < 0) + { + return false; + } + + // Check that there's rail in there: + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + switch (Block) + { + case E_BLOCK_MINECART_TRACKS: + case E_BLOCK_POWERED_RAIL: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_ACTIVATOR_RAIL: + { + // These are allowed + break; + } + default: + { + LOGD("Used minecart on an unsuitable block %d (%s)", Block, ItemTypeToString(Block).c_str()); + return false; + } + } + + double x = (double)a_BlockX + 0.5; + double y = (double)a_BlockY + 0.5; + double z = (double)a_BlockZ + 0.5; + cMinecart * Minecart = NULL; + switch (m_ItemType) + { + case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break; + case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break; + case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace (x, y, z); break; + case E_ITEM_MINECART_WITH_TNT: Minecart = new cMinecartWithTNT (x, y, z); break; + case E_ITEM_MINECART_WITH_HOPPER: Minecart = new cMinecartWithHopper (x, y, z); break; + default: + { + ASSERT(!"Unhandled minecart item"); + return false; + } + } // switch (m_ItemType) + Minecart->Initialize(a_World); + return true; + } + +} ; + + + + diff --git a/src/Items/ItemPickaxe.h b/src/Items/ItemPickaxe.h new file mode 100644 index 000000000..bde7f0905 --- /dev/null +++ b/src/Items/ItemPickaxe.h @@ -0,0 +1,92 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + +class cItemPickaxeHandler : + public cItemHandler +{ +public: + cItemPickaxeHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + char PickaxeLevel() + { + switch(m_ItemType) + { + case E_ITEM_WOODEN_PICKAXE: + case E_ITEM_GOLD_PICKAXE: + return 1; + case E_ITEM_STONE_PICKAXE: + return 2; + case E_ITEM_IRON_PICKAXE: + return 3; + case E_ITEM_DIAMOND_PICKAXE: + return 4; + default: + return 0; + } + } + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + switch(a_BlockType) + { + case E_BLOCK_OBSIDIAN: + { + return PickaxeLevel() >= 4; + } + + case E_BLOCK_DIAMOND_BLOCK: + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_GOLD_BLOCK: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + case E_BLOCK_EMERALD_ORE: + { + return PickaxeLevel() >= 3; + } + + case E_BLOCK_IRON_BLOCK: + case E_BLOCK_IRON_ORE: + case E_BLOCK_LAPIS_ORE: + case E_BLOCK_LAPIS_BLOCK: + { + return PickaxeLevel() >= 2; + } + + case E_BLOCK_COAL_ORE: + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_END_STONE: + case E_BLOCK_MOSSY_COBBLESTONE: + case E_BLOCK_SANDSTONE_STAIRS: + case E_BLOCK_SANDSTONE: + case E_BLOCK_STONE_BRICKS: + case E_BLOCK_NETHER_BRICK: + case E_BLOCK_NETHERRACK: + case E_BLOCK_STONE_SLAB: + case E_BLOCK_DOUBLE_STONE_SLAB: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_BRICK: + case E_BLOCK_COBBLESTONE_STAIRS: + case E_BLOCK_STONE_BRICK_STAIRS: + case E_BLOCK_NETHER_BRICK_STAIRS: + case E_BLOCK_CAULDRON: + { + return PickaxeLevel() >= 1; + } + } + return false; + } +} ; + + + + diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h new file mode 100644 index 000000000..b7860b187 --- /dev/null +++ b/src/Items/ItemRedstoneDust.h @@ -0,0 +1,38 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemRedstoneDustHandler : public cItemHandler +{ +public: + cItemRedstoneDustHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_REDSTONE_WIRE; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemRedstoneRepeater.h b/src/Items/ItemRedstoneRepeater.h new file mode 100644 index 000000000..459070579 --- /dev/null +++ b/src/Items/ItemRedstoneRepeater.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +class cItemRedstoneRepeaterHandler : + public cItemHandler +{ +public: + cItemRedstoneRepeaterHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable() override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF; + a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; + } +} ; + + + + diff --git a/src/Items/ItemSapling.h b/src/Items/ItemSapling.h new file mode 100644 index 000000000..dc0810a45 --- /dev/null +++ b/src/Items/ItemSapling.h @@ -0,0 +1,42 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemSaplingHandler : public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemSaplingHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + bool res = super::GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); + // Only the lowest 3 bits are important + a_BlockMeta = a_BlockMeta & 0x7; + return res; + } +} ; + + + + diff --git a/src/Items/ItemSeeds.h b/src/Items/ItemSeeds.h new file mode 100644 index 000000000..8ca86663f --- /dev/null +++ b/src/Items/ItemSeeds.h @@ -0,0 +1,65 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" + + + + + +class cItemSeedsHandler : + public cItemHandler +{ +public: + cItemSeedsHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (a_BlockFace != BLOCK_FACE_TOP) + { + // Only allow planting seeds from the top side of the block + return false; + } + + // Only allow placement on farmland + int X = a_BlockX; + int Y = a_BlockY; + int Z = a_BlockZ; + AddFaceDirection(X, Y, Z, a_BlockFace, true); + if (a_World->GetBlock(X, Y, Z) != E_BLOCK_FARMLAND) + { + return false; + } + + a_BlockMeta = 0; + switch (m_ItemType) + { + case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true; + case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true; + case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true; + case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true; + case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true; + default: a_BlockType = E_BLOCK_AIR; return true; + } + return false; + } +} ; + + + + diff --git a/src/Items/ItemShears.h b/src/Items/ItemShears.h new file mode 100644 index 000000000..6a17607ee --- /dev/null +++ b/src/Items/ItemShears.h @@ -0,0 +1,62 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemShearsHandler : + public cItemHandler +{ +public: + cItemShearsHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsTool(void) override + { + return true; + } + + + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (Block == E_BLOCK_LEAVES) + { + cItems Drops; + Drops.push_back(cItem(E_BLOCK_LEAVES, 1, a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x03)); + a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_Player->UseEquippedItem(); + return true; + } + return false; + } + + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + switch (a_BlockType) + { + case E_BLOCK_COBWEB: + case E_BLOCK_VINES: + case E_BLOCK_LEAVES: + { + return true; + } + } // switch (a_BlockType) + return false; + } +} ; + + + + diff --git a/src/Items/ItemShovel.h b/src/Items/ItemShovel.h new file mode 100644 index 000000000..d0625ef1c --- /dev/null +++ b/src/Items/ItemShovel.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + +#include "../Blocks/BlockHandler.h" + + + + + +class cItemShovelHandler : public cItemHandler +{ +public: + cItemShovelHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (Block == E_BLOCK_SNOW) + { + BlockHandler(Block)->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_Player->UseEquippedItem(); + return true; + } + return false; + } + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + return (a_BlockType == E_BLOCK_SNOW); + } +}; \ No newline at end of file diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h new file mode 100644 index 000000000..5ccd79e29 --- /dev/null +++ b/src/Items/ItemSign.h @@ -0,0 +1,51 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Blocks/BlockSign.h" + + + + + +class cItemSignHandler : + public cItemHandler +{ +public: + cItemSignHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (a_BlockFace == BLOCK_FACE_TOP) + { + a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation()); + a_BlockType = E_BLOCK_SIGN_POST; + } + else + { + a_BlockMeta = cBlockSignHandler::DirectionToMetaData(a_BlockFace); + a_BlockType = E_BLOCK_WALLSIGN; + } + return true; + } +} ; + + + + diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h new file mode 100644 index 000000000..26dd15b7d --- /dev/null +++ b/src/Items/ItemSpawnEgg.h @@ -0,0 +1,52 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemSpawnEggHandler : public cItemHandler +{ +public: + cItemSpawnEggHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override + { + if (a_BlockFace < 0) + { + return false; + } + + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + a_BlockY--; + } + + if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, (cMonster::eType)(a_Item.m_ItemDamage)) >= 0) + { + if (a_Player->GetGameMode() != 1) + { + // The mob was spawned, "use" the item: + a_Player->GetInventory().RemoveOneEquippedItem(); + } + return true; + } + + return false; + } +} ; + + + + diff --git a/src/Items/ItemSugarcane.h b/src/Items/ItemSugarcane.h new file mode 100644 index 000000000..ce93aa3e5 --- /dev/null +++ b/src/Items/ItemSugarcane.h @@ -0,0 +1,39 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemSugarcaneHandler : + public cItemHandler +{ +public: + cItemSugarcaneHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_SUGARCANE; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemSword.h b/src/Items/ItemSword.h new file mode 100644 index 000000000..a7c1d2432 --- /dev/null +++ b/src/Items/ItemSword.h @@ -0,0 +1,30 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemSwordHandler : + public cItemHandler +{ +public: + cItemSwordHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + return (a_BlockType == E_BLOCK_COBWEB); + } +} ; + + + + diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h new file mode 100644 index 000000000..85579daf2 --- /dev/null +++ b/src/Items/ItemThrowable.h @@ -0,0 +1,96 @@ + +// ItemThrowable.h + +// Declares the itemhandlers for throwable items: eggs, snowballs and ender pearls + + + + + +#pragma once + + + + + +class cItemThrowableHandler : + public cItemHandler +{ + typedef cItemHandler super; +public: + cItemThrowableHandler(int a_ItemType, cProjectileEntity::eKind a_ProjectileKind, double a_SpeedCoeff) : + super(a_ItemType), + m_ProjectileKind(a_ProjectileKind), + m_SpeedCoeff(a_SpeedCoeff) + { + } + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + if (!a_Player->IsGameModeCreative()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + + Vector3d Pos = a_Player->GetThrowStartPos(); + Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff; + a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &Speed); + + return true; + } + +protected: + cProjectileEntity::eKind m_ProjectileKind; + double m_SpeedCoeff; +} ; + + + + + +class cItemEggHandler : + public cItemThrowableHandler +{ + typedef cItemThrowableHandler super; +public: + cItemEggHandler(void) : + super(E_ITEM_EGG, cProjectileEntity::pkEgg, 30) + { + } +} ; + + + + +class cItemSnowballHandler : + public cItemThrowableHandler +{ + typedef cItemThrowableHandler super; + +public: + cItemSnowballHandler(void) : + super(E_ITEM_SNOWBALL, cProjectileEntity::pkSnowball, 30) + { + } +} ; + + + + + +class cItemEnderPearlHandler : + public cItemThrowableHandler +{ + typedef cItemThrowableHandler super; + +public: + cItemEnderPearlHandler(void) : + super(E_ITEM_ENDER_PEARL, cProjectileEntity::pkEnderPearl, 30) + { + } +} ; + + + + diff --git a/src/LeakFinder.cpp b/src/LeakFinder.cpp new file mode 100644 index 000000000..0f84adb2b --- /dev/null +++ b/src/LeakFinder.cpp @@ -0,0 +1,1047 @@ + +// LeakFinder.cpp + +// Finds memory leaks rather effectively + +// _X: downloaded from http://www.codeproject.com/Articles/3134/Memory-Leak-and-Exception-Trace-CRT-and-COM-Leaks - the real link is in the comments, RC11 version + + + + + +/********************************************************************** + * + * LEAKFINDER.CPP + * + * + * + * History: + * 2010-04-15 RC10 - Updated to VC10 RTM + * Fixed Bug: Application Verifier, thanks to handsinmypocket! + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3439751#xx3439751xx + * 2008-08-04 RC6 - Updated to VC9 RTM + * Fixed Bug: Missing "ole32.lib" LIB + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2253980#xx2253980xx + * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN" + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx + * Fixed Bug: Compiling with "/Wall" + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx + * Removed "#pragma init_seg (compiler)" from h-file + * + * 2005-12-30 RC5 - Now again VC8 RTM compatible + * - Added Xml-Output (like in the old Leakfinder) + * YOu need to define XML_LEAK_FINDER to activate it + * So you can use the LeakAnalyseTool from + * http://www.codeproject.com/tools/leakfinder.asp + * + * 2005-12-13 RC4 - Merged with the new "StackWalker"-project on + * http://www.codeproject.com/threads/StackWalker.asp + * + * 2005-08-01 RC3 - Merged with the new "StackWalker"-project on + * http://www.codeproject.com/threads/StackWalker.asp + * + * 2005-07-05 RC2 - First version with x86, IA64 and x64 support + * + * 2005-07-04 RC1 - Added "OutputOptions" + * - New define "INIT_LEAK_FINDER_VERBOSE" to + * display more info (for error reporting) + * + * 2005-07-01 Beta3 - Workaround for a bug in the new dbghelp.dll + * (version 6.5.3.7 from 2005-05-30; StakWalk64 no + * refused to produce an callstack on x86 systems + * if the context is NULL or has some registers set + * to 0 (for example Esp). This is against the + * documented behaviour of StackWalk64...) + * - First version with x64-support + * + * 2005-06-16 Beta1 First public release with the following features: + * - Completely rewritten in C++ (object oriented) + * - CRT-Leak-Report + * - COM-Leak-Report + * - Report is done via "OutputDebugString" so + * the line can directly selected in the debugger + * and is opening the corresponding file/line of + * the allocation + * - Tried to support x64 systems, bud had some + * trouble wih StackWalk64 + * See: http://blog.kalmbachnet.de/?postid=43 + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ + +#include +#include // Needed if compiled with "WIN32_LEAN_AND_MEAN" +#include +#include +#include + +#include +#include + + +#include "LeakFinder.h" + +// Currently only tested with MS VC++ 5 to 10 +#if (_MSC_VER < 1100) || (_MSC_VER > 1800) +#error Only MS VC++ 5/6/7/7.1/8/9/10/11/12 supported. Check if the '_CrtMemBlockHeader' has not changed with this compiler! +#endif + + +/* _X: MSVC 2012 (MSC 1700) seems to use a different allocation scheme for STL containers, +* allocating lots of small objects and running out of memory very soon +* Thus for MSVC 2012 we cut the callstack buffer length in half +* +* _X 2013_08_25: The callstack tracking gets worse even for MSVC 2008, a single lua_state eats 50 MiB of RAM +* Therefore I decided to further reduce the buffers from 0x2000 to 0x1000 +*/ +// Controlling the callstack depth +#if (_MSC_VER < 1700) + #define MAX_CALLSTACK_LEN_BUF 0x1000 +#else + #define MAX_CALLSTACK_LEN_BUF 0x0800 +#endif + + + + + +#define IGNORE_CRT_ALLOC + +// disable 64-bit compatibility-checks (because we explicite have here either x86 or x64!) +#pragma warning(disable:4312) // warning C4312: 'type cast' : conversion from 'DWORD' to 'LPCVOID' of greater size +#pragma warning(disable:4826) + + +// secure-CRT_functions are only available starting with VC8 +#if _MSC_VER < 1400 +#define _snprintf_s _snprintf +#define _tcscat_s _tcscat +#endif + + + + + +static std::string SimpleXMLEncode(LPCSTR szText) +{ + std::string szRet; + for (size_t i=0; i': + szRet.append(">"); + break; + case '"': + szRet.append("""); + break; + case '\'': + szRet.append("'"); + break; + default: + szRet += szText[i]; + } + } + return szRet; +} + + + + + +LeakFinderOutput::LeakFinderOutput(int options, LPCSTR szSymPath) + : StackWalker(options, szSymPath) +{ +} + + + + + +void LeakFinderOutput::OnLeakSearchStart(LPCSTR szLeakFinderName) +{ + CHAR buffer[1024]; + _snprintf_s(buffer, 1024, "######## %s ########\n", szLeakFinderName); + this->OnOutput(buffer); +} + + + + + +void LeakFinderOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize) +{ + CHAR buffer[1024]; + _snprintf_s(buffer, 1024, "--------------- Key: %s, %d bytes ---------\n", szKeyName, nDataSize); + this->OnOutput(buffer); +} + + + + + +void LeakFinderOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + if ( (eType != lastEntry) && (entry.offset != 0) ) + { + if ( ((this->m_options & LeakFinderShowCompleteCallstack) == 0) && ( + (strstr(entry.lineFileName, "afxmem.cpp") != NULL) || + (strstr(entry.lineFileName, "dbgheap.c") != NULL) || + (strstr(entry.lineFileName, "new.cpp") != NULL) || + (strstr(entry.lineFileName, "newop.cpp") != NULL) || + (strstr(entry.lineFileName, "leakfinder.cpp") != NULL) || + (strstr(entry.lineFileName, "stackwalker.cpp") != NULL) + ) ) + { + return; + } + } + StackWalker::OnCallstackEntry(eType, entry); +} + + + + + +// #################################################################### +// XML-Output +LeakFinderXmlOutput::LeakFinderXmlOutput() +{ + TCHAR szXMLFileName[1024]; + + GetModuleFileName(NULL, szXMLFileName, sizeof(szXMLFileName) / sizeof(TCHAR)); + _tcscat_s(szXMLFileName, _T(".mem.xml-leaks")); +#if _MSC_VER < 1400 + m_fXmlFile = _tfopen(szXMLFileName, _T("w")); +#else + m_fXmlFile = NULL; + _tfopen_s(&m_fXmlFile, szXMLFileName, _T("w")); +#endif + if (m_fXmlFile != NULL) + { + SYSTEMTIME st; + GetLocalTime(&st); + fprintf(m_fXmlFile, "\n", + st.wMonth, st.wDay, st.wYear, + st.wHour, st.wMinute, st.wSecond); + } + else + { + MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND); + } +} + + + + + +LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName) : + m_Progress(10) +{ +#if _MSC_VER < 1400 + m_fXmlFile = _tfopen(szFileName, _T("w")); +#else + m_fXmlFile = NULL; + _tfopen_s(&m_fXmlFile, szFileName, _T("w")); +#endif + if (m_fXmlFile == NULL) + { + MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND); + } + else + { + fprintf(m_fXmlFile, "\n"); + } +} + + + + + +LeakFinderXmlOutput::~LeakFinderXmlOutput() +{ + if (m_fXmlFile != NULL) + { + // Write the ending-tags and close the file + fprintf(m_fXmlFile, "\n"); + fclose(m_fXmlFile); + } + m_fXmlFile = NULL; +} + + + + + +void LeakFinderXmlOutput::OnLeakSearchStart(LPCSTR sszLeakFinderName) +{ +} + + + + + +void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize) +{ + if (m_fXmlFile != NULL) + { + fprintf(m_fXmlFile, "\t\n", SimpleXMLEncode(szKeyName).c_str(), nDataSize); + } + if (--m_Progress == 0) + { + m_Progress = 100; + putc('.', stdout); + } +} + + + + + +void LeakFinderXmlOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + if (m_fXmlFile != NULL) + { + if (eType != lastEntry) + { + fprintf(m_fXmlFile, "\t\t\n"); + } + else + { + fprintf(m_fXmlFile, "\t\n"); + } + } +} + + + + + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Base class for storing contexts in a hashtable +template class ContextHashtableBase +{ +public: + ContextHashtableBase(SIZE_T sizeOfHastable, LPCSTR finderName) + { + SIZE_T s = sizeOfHastable*sizeof(AllocHashEntryType); + m_hHeap = HeapCreate(0, 10*1024 + s, 0); + if (m_hHeap == NULL) + throw; + pAllocHashTable = (AllocHashEntryType*) own_malloc(s); + sAllocEntries = sizeOfHastable; + m_finderName = own_strdup(finderName); + } + +protected: + virtual ~ContextHashtableBase() + { + if (pAllocHashTable != NULL) + own_free(pAllocHashTable); + pAllocHashTable = NULL; + + own_free(m_finderName); + m_finderName = NULL; + + if (m_hHeap != NULL) + HeapDestroy(m_hHeap); + } + + __inline LPVOID own_malloc(SIZE_T size) + { + return HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size); + } + __inline VOID own_free(LPVOID memblock) + { + HeapFree(m_hHeap, 0, memblock); + } + __inline CHAR *own_strdup(const char *str) + { + size_t len = strlen(str)+1; + CHAR *c = (CHAR*)own_malloc(len); +#if _MSC_VER >= 1400 + strcpy_s(c, len, str); +#else + strcpy(c, str); +#endif + return c; + } + + // Disables this leak-finder + virtual LONG Disable() = 0; + // enables the leak-finder again... + virtual LONG Enable() = 0; + +protected: + // Entry for each allocation + typedef struct AllocHashEntryType { + HASHTABLE_KEY key; + SIZE_T nDataSize; // Size of the allocated memory + struct AllocHashEntryType *Next; + CONTEXT c; + PVOID pStackBaseAddr; + SIZE_T nMaxStackSize; + + PVOID pCallstackOffset; + SIZE_T nCallstackLen; + char pcCallstackAddr[MAX_CALLSTACK_LEN_BUF]; // min of both values... + } AllocHashEntryType; + +protected: + virtual SIZE_T HashFunction(HASHTABLE_KEY &key) = 0; + virtual BOOL IsKeyEmpty(HASHTABLE_KEY &key) = 0; + virtual VOID SetEmptyKey(HASHTABLE_KEY &key) = 0; + virtual VOID GetKeyAsString(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) = 0; + //virtual SIZE_T GetNativeBytes(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) { return 0; } + +public: + VOID Insert(HASHTABLE_KEY &key, CONTEXT &context, SIZE_T nDataSize) + { + SIZE_T HashIdx; + AllocHashEntryType *pHashEntry; + + // generate hash-value + HashIdx = HashFunction(key); + + pHashEntry = &pAllocHashTable[HashIdx]; + if (IsKeyEmpty(pHashEntry->key) != FALSE) { + // Entry is empty... + } + else { + // Entry is not empy! make a list of entries for this hash value... + while(pHashEntry->Next != NULL) { + pHashEntry = pHashEntry->Next; + } + + pHashEntry->Next = (AllocHashEntryType*) own_malloc(sizeof(AllocHashEntryType)); + g_CurrentMemUsage += CRTTable::AllocHashEntryTypeSize; + pHashEntry = pHashEntry->Next; + if (pHashEntry == NULL) + { + // Exhausted the available memory? + return; + } + } + pHashEntry->key = key; + pHashEntry->nDataSize = nDataSize; + pHashEntry->Next = NULL; +#ifdef _M_IX86 + pHashEntry->pCallstackOffset = (LPVOID) min(context.Ebp, context.Esp); +#elif _M_X64 + pHashEntry->pCallstackOffset = (LPVOID) min(context.Rdi, context.Rsp); +#elif _M_IA64 + pHashEntry->pCallstackOffset = (LPVOID) min(context.IntSp, context.RsBSP); +#else +#error "Platform not supported!" +#endif + pHashEntry->c = context; + + // Query the max. stack-area: + MEMORY_BASIC_INFORMATION MemBuffer; + if(VirtualQuery((LPCVOID) pHashEntry->pCallstackOffset, &MemBuffer, sizeof(MemBuffer)) > 0) + { + pHashEntry->pStackBaseAddr = MemBuffer.BaseAddress; + pHashEntry->nMaxStackSize = MemBuffer.RegionSize; + } + else + { + pHashEntry->pStackBaseAddr = 0; + pHashEntry->nMaxStackSize = 0; + } + + SIZE_T bytesToRead = MAX_CALLSTACK_LEN_BUF; + if (pHashEntry->nMaxStackSize > 0) + { + SIZE_T len = ((SIZE_T) pHashEntry->pStackBaseAddr + pHashEntry->nMaxStackSize) - (SIZE_T)pHashEntry->pCallstackOffset; + bytesToRead = min(len, MAX_CALLSTACK_LEN_BUF); + } + // Now read the callstack: + if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) pHashEntry->pCallstackOffset, &(pHashEntry->pcCallstackAddr), bytesToRead, &(pHashEntry->nCallstackLen)) == 0) + { + // Could not read memory... + pHashEntry->nCallstackLen = 0; + pHashEntry->pCallstackOffset = 0; + } // read callstack + } // Insert + + BOOL Remove(HASHTABLE_KEY &key) + { + SIZE_T HashIdx; + AllocHashEntryType *pHashEntry, *pHashEntryLast; + + // get the Hash-Value + HashIdx = HashFunction(key); + + pHashEntryLast = NULL; + pHashEntry = &pAllocHashTable[HashIdx]; + while(pHashEntry != NULL) { + if (pHashEntry->key == key) { + // release my memory + if (pHashEntryLast == NULL) { + // It is an entry in the table, so do not release this memory + if (pHashEntry->Next == NULL) { + // It was the last entry, so empty the table entry + SetEmptyKey(pAllocHashTable[HashIdx].key); + //memset(&pAllocHashTable[HashIdx], 0, sizeof(pAllocHashTable[HashIdx])); + } + else { + // There are some more entries, so shorten the list + AllocHashEntryType *pTmp = pHashEntry->Next; + *pHashEntry = *(pHashEntry->Next); + own_free(pTmp); + g_CurrentMemUsage -= CRTTable::AllocHashEntryTypeSize; + } + return TRUE; + } + else { + // now, I am in an dynamic allocated entry (it was a collision) + pHashEntryLast->Next = pHashEntry->Next; + own_free(pHashEntry); + g_CurrentMemUsage -= CRTTable::AllocHashEntryTypeSize; + return TRUE; + } + } + pHashEntryLast = pHashEntry; + pHashEntry = pHashEntry->Next; + } + + // if we are here, we could not find the RequestID + return FALSE; + } + + AllocHashEntryType *Find(HASHTABLE_KEY &key) + { + SIZE_T HashIdx; + AllocHashEntryType *pHashEntry; + + // get the Hash-Value + HashIdx = HashFunction(key); + + pHashEntry = &pAllocHashTable[HashIdx]; + while(pHashEntry != NULL) { + if (pHashEntry->key == key) { + return pHashEntry; + } + pHashEntry = pHashEntry->Next; + } + + // entry was not found! + return NULL; + } + + // For the followong static-var See comment in "ShowCallstack"... + static BOOL CALLBACK ReadProcessMemoryFromHashEntry64( + HANDLE hProcess, // hProcess must be a pointer to an hash-entry! + DWORD64 lpBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead, + LPVOID pUserData // optional data, which was passed in "ShowCallstack" + ) + { + *lpNumberOfBytesRead = 0; + AllocHashEntryType *pHashEntry = (AllocHashEntryType*) pUserData; + if (pHashEntry == NULL) + { + return FALSE; + } + + if ( ( (DWORD64)lpBaseAddress >= (DWORD64)pHashEntry->pCallstackOffset) && ((DWORD64)lpBaseAddress <= ((DWORD64)pHashEntry->pCallstackOffset+pHashEntry->nCallstackLen)) ) { + // Memory is located in saved Callstack: + // Calculate the offset + DWORD dwOffset = (DWORD) ((DWORD64)lpBaseAddress - (DWORD64)pHashEntry->pCallstackOffset); + DWORD dwSize = __min(nSize, MAX_CALLSTACK_LEN_BUF-dwOffset); + memcpy(lpBuffer, &(pHashEntry->pcCallstackAddr[dwOffset]), dwSize); + *lpNumberOfBytesRead = dwSize; + if (dwSize != nSize) + { + return FALSE; + } + *lpNumberOfBytesRead = nSize; + return TRUE; + } + + if (*lpNumberOfBytesRead == 0) // Memory could not be found + { + if ( ( (DWORD64)lpBaseAddress < (DWORD64)pHashEntry->pStackBaseAddr) || ((DWORD64)lpBaseAddress > ((DWORD64)pHashEntry->pStackBaseAddr+pHashEntry->nMaxStackSize)) ) + { + // Stackwalking is done by reading the "real memory" (normally this happens when the StackWalk64 tries to read some code) + SIZE_T st = 0; + BOOL bRet = ReadProcessMemory(hProcess, (LPCVOID) lpBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD) st; + return bRet; + } + } + + return TRUE; + } + + VOID ShowLeaks(LeakFinderOutput &leakFinderOutput) + { + SIZE_T ulTemp; + AllocHashEntryType *pHashEntry; + ULONG ulCount = 0; + SIZE_T ulLeaksByte = 0; + + leakFinderOutput.OnLeakSearchStart(this->m_finderName); + + // Move throu every entry + CHAR keyName[1024]; + for(ulTemp = 0; ulTemp < this->sAllocEntries; ulTemp++) { + pHashEntry = &pAllocHashTable[ulTemp]; + if (IsKeyEmpty(pHashEntry->key) == FALSE) { + while(pHashEntry != NULL) { + ulCount++; + CONTEXT c; + memcpy(&c, &(pHashEntry->c), sizeof(CONTEXT)); + + this->GetKeyAsString(pHashEntry->key, keyName, 1024); + + leakFinderOutput.OnLeakStartEntry(keyName, pHashEntry->nDataSize); + leakFinderOutput.ShowCallstack(GetCurrentThread(), &c, ReadProcessMemoryFromHashEntry64, pHashEntry); + + // Count the number of leaky bytes + ulLeaksByte += pHashEntry->nDataSize; + + pHashEntry = pHashEntry->Next; + } // while + } + } + } + + AllocHashEntryType *pAllocHashTable; + SIZE_T sAllocEntries; + HANDLE m_hHeap; + LPSTR m_finderName; + bool m_bSupressUselessLines; +}; // template class ContextHashtableBase + + + + + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Specialization for CRT-Leaks: +// VC5 has excluded all types in release-builds +#ifdef _DEBUG + +// The follwoing is copied from dbgint.h: +// +/* +* For diagnostic purpose, blocks are allocated with extra information and +* stored in a doubly-linked list. This makes all blocks registered with +* how big they are, when they were allocated, and what they are used for. +*/ + +// forward declaration: +#ifndef _M_CEE_PURE +#define MyAllocHookCallingConvention __cdecl +#endif +#if _MSC_VER >= 1400 +#ifdef _M_CEE +#define MyAllocHookCallingConvention __clrcall +#endif +#endif + +static int MyAllocHookCallingConvention MyAllocHook(int nAllocType, void *pvData, + size_t nSize, int nBlockUse, long lRequest, +#if _MSC_VER <= 1100 // Special case for VC 5 and before + const char * szFileName, +#else + const unsigned char * szFileName, +#endif + int nLine); + +static _CRT_ALLOC_HOOK s_pfnOldCrtAllocHook = NULL; +static LONG s_CrtDisableCount = 0; +static LONG s_lMallocCalled = 0; + + + + + +class CRTTable : public ContextHashtableBase +{ +public: + CRTTable() : ContextHashtableBase(1021, "CRT-Leaks") + { + // save the previous alloc hook + s_pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook); + } + + virtual ~CRTTable() + { + _CrtSetAllocHook(s_pfnOldCrtAllocHook); + } + + virtual LONG Disable() + { + return InterlockedIncrement(&s_CrtDisableCount); + } + virtual LONG Enable() + { + return InterlockedDecrement(&s_CrtDisableCount); + } + + virtual SIZE_T HashFunction(LONG &key) + { + // I couldn´t find any better and faster + return key % sAllocEntries; + } + virtual BOOL IsKeyEmpty(LONG &key) + { + if (key == 0) + return TRUE; + return FALSE; + } + virtual VOID SetEmptyKey(LONG &key) + { + key = 0; + } + virtual VOID GetKeyAsString(LONG &key, CHAR *szName, SIZE_T nBufferLen) + { +#if _MSC_VER < 1400 + _snprintf_s(szName, nBufferLen, "%d", key); +#else + _snprintf_s(szName, nBufferLen, nBufferLen, "%d", key); +#endif + } + + static const int AllocHashEntryTypeSize = sizeof(AllocHashEntryType); + +protected: + CHAR *m_pBuffer; + SIZE_T m_maxBufferLen; + SIZE_T m_bufferLen; +}; // class CRTTable + + +#define nNoMansLandSize 4 + +typedef struct _CrtMemBlockHeader +{ + struct _CrtMemBlockHeader * pBlockHeaderNext; + struct _CrtMemBlockHeader * pBlockHeaderPrev; + char * szFileName; + int nLine; +#ifdef _WIN64 + /* These items are reversed on Win64 to eliminate gaps in the struct + * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is + * maintained in the debug heap. + */ + int nBlockUse; + size_t nDataSize; +#else /* _WIN64 */ + size_t nDataSize; + int nBlockUse; +#endif /* _WIN64 */ + long lRequest; + unsigned char gap[nNoMansLandSize]; + /* followed by: + * unsigned char data[nDataSize]; + * unsigned char anotherGap[nNoMansLandSize]; + */ +} _CrtMemBlockHeader; +#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1)) +#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1) +// + +static CRTTable *g_pCRTTable = NULL; + +size_t g_CurrentMemUsage = 0; + + + + + +// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function! +static int MyAllocHook(int nAllocType, void *pvData, + size_t nSize, int nBlockUse, long lRequest, +#if _MSC_VER <= 1100 // Special case for VC 5 + const char * szFileName, +#else + const unsigned char * szFileName, +#endif + int nLine) +{ + //static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") }; + //static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") }; + +#ifdef IGNORE_CRT_ALLOC + if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations + return TRUE; +#endif + extern int _crtDbgFlag; + if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) ) + { + // Someone has disabled that the runtime should log this allocation + // so we do not log this allocation + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; + } + + // Handle the Disable/Enable setting + if (InterlockedExchangeAdd(&s_CrtDisableCount, 0) != 0) + { + return TRUE; + } + + // Prevent from reentrat calls + if (InterlockedIncrement(&s_lMallocCalled) > 1) + { + // I was already called + InterlockedDecrement(&s_lMallocCalled); + // call the previous alloc hook + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; + } + + _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) ); + _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) ); + + if (nAllocType == _HOOK_FREE) + { + // freeing + // Try to get the header information + if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer + // get the ID + _CrtMemBlockHeader *pHead; + // get a pointer to memory block header + pHead = pHdr(pvData); + nSize = pHead->nDataSize; + lRequest = pHead->lRequest; // This is the ID! + + if (pHead->nBlockUse == _IGNORE_BLOCK) + { + InterlockedDecrement(&s_lMallocCalled); + if (s_pfnOldCrtAllocHook != NULL) + { + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + } + return TRUE; + } + } + if (lRequest != 0) + { + // RequestID was found + size_t temp = g_CurrentMemUsage; + g_CurrentMemUsage -= nSize ; + g_pCRTTable->Remove(lRequest); + if (g_CurrentMemUsage > temp) + { + printf("********************************************\n"); + printf("** Server detected underflow in memory **\n"); + printf("** usage counter. Something is not right. **\n"); + printf("** Writing memory dump into memdump.xml **\n"); + printf("********************************************\n"); + printf("Please wait\n"); + + LeakFinderXmlOutput Output("memdump.xml"); + DumpUsedMemory(&Output); + + printf("\nMemory dump complete. Server will now abort.\n"); + abort(); + } + } + } // freeing + + if (nAllocType == _HOOK_REALLOC) + { + // re-allocating + // Try to get the header information + if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer + BOOL bRet; + LONG lReallocRequest; + // get the ID + _CrtMemBlockHeader *pHead; + // get a pointer to memory block header + pHead = pHdr(pvData); + // Try to find the RequestID in the Hash-Table, mark it that it was freed + lReallocRequest = pHead->lRequest; + size_t temp = g_CurrentMemUsage; + g_CurrentMemUsage -= pHead->nDataSize; + bRet = g_pCRTTable->Remove(lReallocRequest); + if (g_CurrentMemUsage > temp) + { + printf("********************************************\n"); + printf("** Server detected underflow in memory **\n"); + printf("** usage counter. Something is not right. **\n"); + printf("** Writing memory dump into memdump.xml **\n"); + printf("********************************************\n"); + printf("Please wait\n"); + + LeakFinderXmlOutput Output("memdump.xml"); + DumpUsedMemory(&Output); + + printf("\nMemory dump complete. Server will now abort.\n"); + abort(); + } + } // ValidHeapPointer + } // re-allocating + + //if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) { + if (nAllocType == _HOOK_FREE) + { + InterlockedDecrement(&s_lMallocCalled); + // call the previous alloc hook + if (s_pfnOldCrtAllocHook != NULL) + { + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + } + return TRUE; + } + + CONTEXT c; + GET_CURRENT_CONTEXT(c, CONTEXT_FULL); + + // Only insert in the Hash-Table if it is not a "freeing" + if (nAllocType != _HOOK_FREE) + { + if (lRequest != 0) // Always a valid RequestID should be provided (see comments in the header) + { + //No need to check for overflow since we are checking if we are getting higher than 1gb. + //If we change this, then we probably would want an overflow check. + g_CurrentMemUsage += nSize ; + g_pCRTTable->Insert(lRequest, c, nSize); + + if (g_CurrentMemUsage > 1536 * 1024* 1024) + { + printf("******************************************\n"); + printf("** Server reached 1.5 GiB memory usage, **\n"); + printf("** something is probably wrong. **\n"); + printf("** Writing memory dump into memdump.xml **\n"); + printf("******************************************\n"); + printf("Please wait\n"); + + LeakFinderXmlOutput Output("memdump.xml"); + DumpUsedMemory(&Output); + + printf("\nMemory dump complete. Server will now abort.\n"); + abort(); + } + } + } + + InterlockedDecrement(&s_lMallocCalled); + // call the previous alloc hook + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; // allow the memory operation to proceed +} // MyAllocHook + +#endif // _DEBUG + + + + + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Init/Deinit functions + +HRESULT InitLeakFinder() +{ + #ifdef _DEBUG + g_pCRTTable = new CRTTable(); + #endif + return S_OK; +} + + + + + +void DumpUsedMemory(LeakFinderOutput * output) +{ + LeakFinderOutput *pLeakFinderOutput = output; + + #ifdef _DEBUG + g_pCRTTable->Disable(); + #endif + + if (pLeakFinderOutput == NULL) + { + pLeakFinderOutput = new LeakFinderOutput(); + } + + // explicitly load the modules: + pLeakFinderOutput->LoadModules(); + + #ifdef _DEBUG + g_pCRTTable->ShowLeaks(*pLeakFinderOutput); + #endif + + if (output == NULL) + { + delete pLeakFinderOutput; + } +} + + + + + +void DeinitLeakFinder(LeakFinderOutput *output) +{ + DumpUsedMemory(output); + + #ifdef _DEBUG + delete g_pCRTTable; + g_pCRTTable = NULL; + #endif +} + + + + + +void DeinitLeakFinder() +{ + DeinitLeakFinder(NULL); +} + + + + diff --git a/src/LeakFinder.h b/src/LeakFinder.h new file mode 100644 index 000000000..e63b9ec5d --- /dev/null +++ b/src/LeakFinder.h @@ -0,0 +1,156 @@ +/********************************************************************** + * + * LEAKFINDER.H + * + * + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ + +// #pragma once is supported starting with _MCS_VER 1000, +// so we need not to check the version (because we only support _MSC_VER >= 1100)! +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT InitLeakFinder(); +void DeinitLeakFinder(); + +#ifdef __cplusplus +} +#endif + + +// The following is only available if the file is CPP +#ifdef __cplusplus + +#include "StackWalker.h" + +// Interface for output... +class LeakFinderOutput : public StackWalker +{ +public: + typedef enum LeakFinderOptions + { + // No addition info will be retrived + // (only the address is available) + LeakFinderNone = 0, + LeakFinderShowCompleteCallstack = 0x1000 + } LeakFinderOptions; + + LeakFinderOutput(int options = OptionsAll, LPCSTR szSymPath = NULL); + virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName); + virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize); +protected: + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnOutput(LPCSTR szText) + { + printf(szText); + StackWalker::OnOutput(szText); + } + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) + { + if (strcmp(szFuncName, "SymGetLineFromAddr64") == 0) return; + StackWalker::OnDbgHelpErr(szFuncName, gle, addr); + } +}; + +class LeakFinderXmlOutput : public LeakFinderOutput +{ +public: + LeakFinderXmlOutput(); + virtual ~LeakFinderXmlOutput(); + LeakFinderXmlOutput(LPCTSTR szFileName); + virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName); + virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize); +protected: + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnOutput(LPCSTR szText) { } + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { } + + FILE * m_fXmlFile; + int m_Progress; +}; + +// C++ interface: +void DeinitLeakFinder(LeakFinderOutput *output); + +class ZZZ_LeakFinder +{ +public: + ZZZ_LeakFinder() + { + m_pXml = NULL; +#ifdef XML_LEAK_FINDER + m_pXml = new LeakFinderXmlOutput(); +#endif + InitLeakFinder(); + } + ~ZZZ_LeakFinder() + { + DeinitLeakFinder(m_pXml); + if (m_pXml != NULL) delete m_pXml; + } +protected: + LeakFinderXmlOutput *m_pXml; +}; + +#if defined(INIT_LEAK_FINDER) +#if _MSC_VER >= 1200 +#pragma warning(push) +#endif +#pragma warning (disable:4074) + +// WARNING: If you enable this option, the code might run without the CRT being initialized or after the CRT was deinitialized!!! +// Currently the code is not designed to bypass the CRT... +//#pragma init_seg (compiler) +ZZZ_LeakFinder zzz_LeakFinder; + +#if _MSC_VER >= 1200 +#pragma warning(pop) +#else +#pragma warning(default:4074) +#endif +#endif + +#endif // __cplusplus + + + + +extern void DumpUsedMemory(LeakFinderOutput * output = NULL); + + + + + diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp new file mode 100644 index 000000000..d7e60e458 --- /dev/null +++ b/src/LightingThread.cpp @@ -0,0 +1,562 @@ + +// LightingThread.cpp + +// Implements the cLightingThread class representing the thread that processes requests for lighting + +#include "Globals.h" +#include "LightingThread.h" +#include "ChunkMap.h" +#include "World.h" + + + + + +/// If more than this many chunks are in the queue, a warning is printed to the log +#define WARN_ON_QUEUE_SIZE 800 + + + + + +/// Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]: +class cReader : + public cChunkDataCallback +{ + virtual void BlockTypes(const BLOCKTYPE * a_Type) override + { + // ROW is a block of 16 Blocks, one whole row is copied at a time (hopefully the compiler will optimize that) + // C++ doesn't permit copying arrays, but arrays as a part of a struct is ok :) + typedef struct {BLOCKTYPE m_Row[16]; } ROW; + ROW * InputRows = (ROW *)a_Type; + ROW * OutputRows = (ROW *)m_BlockTypes; + int InputIdx = 0; + int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3; + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + OutputRows[OutputIdx] = InputRows[InputIdx++]; + OutputIdx += 3; + } // for z + // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows + // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip + OutputIdx += cChunkDef::Width * 6; + } // for y + } // BlockTypes() + + + virtual void HeightMap(const cChunkDef::HeightMap * a_Heightmap) override + { + typedef struct {HEIGHTTYPE m_Row[16]; } ROW; + ROW * InputRows = (ROW *)a_Heightmap; + ROW * OutputRows = (ROW *)m_HeightMap; + int InputIdx = 0; + int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3; + for (int z = 0; z < cChunkDef::Width; z++) + { + OutputRows[OutputIdx] = InputRows[InputIdx++]; + OutputIdx += 3; + } // for z + } + +public: + int m_ReadingChunkX; // 0, 1 or 2; x-offset of the chunk we're reading from the BlockTypes start + int m_ReadingChunkZ; // 0, 1 or 2; z-offset of the chunk we're reading from the BlockTypes start + BLOCKTYPE * m_BlockTypes; // 3x3 chunks of block types, organized as a single XZY blob of data (instead of 3x3 XZY blobs) + HEIGHTTYPE * m_HeightMap; // 3x3 chunks of height map, organized as a single XZY blob of data (instead of 3x3 XZY blobs) +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLightingThread: + +cLightingThread::cLightingThread(void) : + super("cLightingThread"), + m_World(NULL) +{ +} + + + + + +cLightingThread::~cLightingThread() +{ + Stop(); +} + + + + + +bool cLightingThread::Start(cWorld * a_World) +{ + ASSERT(m_World == NULL); // Not started yet + m_World = a_World; + + return super::Start(); +} + + + + + +void cLightingThread::Stop(void) +{ + { + cCSLock Lock(m_CS); + for (sItems::iterator itr = m_Queue.begin(), end = m_Queue.end(); itr != end; ++itr) + { + delete itr->m_ChunkStay; + } + m_Queue.clear(); + } + m_ShouldTerminate = true; + m_evtItemAdded.Set(); + + Wait(); +} + + + + + +void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter) +{ + ASSERT(m_World != NULL); // Did you call Start() properly? + + cChunkStay * ChunkStay = new cChunkStay(m_World); + ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ + 1); + ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ); + ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ - 1); + ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ + 1); + ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ - 1); + ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ + 1); + ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ); + ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ - 1); + ChunkStay->Enable(); + ChunkStay->Load(); + cCSLock Lock(m_CS); + m_Queue.push_back(sItem(a_ChunkX, a_ChunkZ, ChunkStay, a_CallbackAfter)); + if (m_Queue.size() > WARN_ON_QUEUE_SIZE) + { + LOGINFO("Lighting thread overloaded, %d items in queue", m_Queue.size()); + } + m_evtItemAdded.Set(); +} + + + + + +void cLightingThread::WaitForQueueEmpty(void) +{ + cCSLock Lock(m_CS); + while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PostponedQueue.empty())) + { + cCSUnlock Unlock(Lock); + m_evtQueueEmpty.Wait(); + } +} + + + + + +size_t cLightingThread::GetQueueLength(void) +{ + cCSLock Lock(m_CS); + return m_Queue.size() + m_PostponedQueue.size(); +} + + + + + +void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ) +{ + // Check all the items in the m_PostponedQueue, if the chunk is their neighbor, move the item to m_Queue + + bool NewlyAdded = false; + { + cCSLock Lock(m_CS); + for (sItems::iterator itr = m_PostponedQueue.begin(); itr != m_PostponedQueue.end(); ) + { + if ( + (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) && + (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) + ) + { + // It is a neighbor + m_Queue.push_back(*itr); + itr = m_PostponedQueue.erase(itr); + NewlyAdded = true; + } + else + { + ++itr; + } + } // for itr - m_PostponedQueue[] + } // Lock(m_CS) + + if (NewlyAdded) + { + m_evtItemAdded.Set(); // Notify the thread it has some work to do + } +} + + + + + +void cLightingThread::Execute(void) +{ + while (true) + { + { + cCSLock Lock(m_CS); + if (m_Queue.size() == 0) + { + cCSUnlock Unlock(Lock); + m_evtItemAdded.Wait(); + } + } + + if (m_ShouldTerminate) + { + return; + } + + // Process one items from the queue: + sItem Item; + { + cCSLock Lock(m_CS); + if (m_Queue.empty()) + { + continue; + } + Item = m_Queue.front(); + m_Queue.pop_front(); + if (m_Queue.empty()) + { + m_evtQueueEmpty.Set(); + } + } // CSLock(m_CS) + + LightChunk(Item); + } +} + + + + + + +void cLightingThread::LightChunk(cLightingThread::sItem & a_Item) +{ + cChunkDef::BlockNibbles BlockLight, SkyLight; + + if (!ReadChunks(a_Item.x, a_Item.z)) + { + // Neighbors not available. Re-queue in the postponed queue + cCSLock Lock(m_CS); + m_PostponedQueue.push_back(a_Item); + return; + } + + /* + // DEBUG: torch somewhere: + m_BlockTypes[19 + 24 * cChunkDef::Width * 3 + (m_HeightMap[24 + 24 * cChunkDef::Width * 3] / 2) * BlocksPerYLayer] = E_BLOCK_TORCH; + // m_HeightMap[24 + 24 * cChunkDef::Width * 3]++; + */ + + PrepareBlockLight(); + CalcLight(m_BlockLight); + + PrepareSkyLight(); + + /* + // DEBUG: Save chunk data with highlighted seeds for visual inspection: + cFile f4; + if ( + f4.Open(Printf("Chunk_%d_%d_seeds.grab", a_Item.x, a_Item.z), cFile::fmWrite) + ) + { + for (int z = 0; z < cChunkDef::Width * 3; z++) + { + for (int y = cChunkDef::Height / 2; y >= 0; y--) + { + unsigned char Seeds [cChunkDef::Width * 3]; + memcpy(Seeds, m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3); + for (int x = 0; x < cChunkDef::Width * 3; x++) + { + if (m_IsSeed1[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x]) + { + Seeds[x] = E_BLOCK_DIAMOND_BLOCK; + } + } + f4.Write(Seeds, cChunkDef::Width * 3); + } + } + } + //*/ + + CalcLight(m_SkyLight); + + /* + // DEBUG: Save XY slices of the chunk data and lighting for visual inspection: + cFile f1, f2, f3; + if ( + f1.Open(Printf("Chunk_%d_%d_data.grab", a_Item.x, a_Item.z), cFile::fmWrite) && + f2.Open(Printf("Chunk_%d_%d_sky.grab", a_Item.x, a_Item.z), cFile::fmWrite) && + f3.Open(Printf("Chunk_%d_%d_glow.grab", a_Item.x, a_Item.z), cFile::fmWrite) + ) + { + for (int z = 0; z < cChunkDef::Width * 3; z++) + { + for (int y = cChunkDef::Height / 2; y >= 0; y--) + { + f1.Write(m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3); + unsigned char SkyLight [cChunkDef::Width * 3]; + unsigned char BlockLight[cChunkDef::Width * 3]; + for (int x = 0; x < cChunkDef::Width * 3; x++) + { + SkyLight[x] = m_SkyLight [y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4; + BlockLight[x] = m_BlockLight[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4; + } + f2.Write(SkyLight, cChunkDef::Width * 3); + f3.Write(BlockLight, cChunkDef::Width * 3); + } + } + } + //*/ + + CompressLight(m_BlockLight, BlockLight); + CompressLight(m_SkyLight, SkyLight); + + m_World->ChunkLighted(a_Item.x, a_Item.z, BlockLight, SkyLight); + + if (a_Item.m_Callback != NULL) + { + a_Item.m_Callback->Call(a_Item.x, a_Item.z); + } + delete a_Item.m_ChunkStay; +} + + + + + +bool cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ) +{ + cReader Reader; + Reader.m_BlockTypes = m_BlockTypes; + Reader.m_HeightMap = m_HeightMap; + + for (int z = 0; z < 3; z++) + { + Reader.m_ReadingChunkZ = z; + for (int x = 0; x < 3; x++) + { + Reader.m_ReadingChunkX = x; + if (!m_World->GetChunkData(a_ChunkX + x - 1, a_ChunkZ + z - 1, Reader)) + { + return false; + } + } // for z + } // for x + + memset(m_BlockLight, 0, sizeof(m_BlockLight)); + memset(m_SkyLight, 0, sizeof(m_SkyLight)); + return true; +} + + + + + +void cLightingThread::PrepareSkyLight(void) +{ + // Clear seeds: + memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); + m_NumSeeds = 0; + + // Walk every column that has all XZ neighbors + for (int z = 1; z < cChunkDef::Width * 3 - 1; z++) + { + int BaseZ = z * cChunkDef::Width * 3; + for (int x = 1; x < cChunkDef::Width * 3 - 1; x++) + { + int idx = BaseZ + x; + int Current = m_HeightMap[idx] + 1; + int Neighbor1 = m_HeightMap[idx + 1] + 1; // X + 1 + int Neighbor2 = m_HeightMap[idx - 1] + 1; // X - 1 + int Neighbor3 = m_HeightMap[idx + cChunkDef::Width * 3] + 1; // Z + 1 + int Neighbor4 = m_HeightMap[idx - cChunkDef::Width * 3] + 1; // Z - 1 + int MaxNeighbor = std::max(std::max(Neighbor1, Neighbor2), std::max(Neighbor3, Neighbor4)); // Maximum of the four neighbors + + // Fill the column from the top down to Current with all-light: + for (int y = cChunkDef::Height - 1, Index = idx + y * BlocksPerYLayer; y >= Current; y--, Index -= BlocksPerYLayer) + { + m_SkyLight[Index] = 15; + } + + // Add Current as a seed: + if (Current < cChunkDef::Height) + { + int CurrentIdx = idx + Current * BlocksPerYLayer; + m_IsSeed1[CurrentIdx] = true; + m_SeedIdx1[m_NumSeeds++] = CurrentIdx; + } + + // Add seed from Current up to the highest neighbor: + for (int y = Current + 1, Index = idx + y * BlocksPerYLayer; y < MaxNeighbor; y++, Index += BlocksPerYLayer) + { + m_IsSeed1[Index] = true; + m_SeedIdx1[m_NumSeeds++] = Index; + } + } + } +} + + + + + +void cLightingThread::PrepareBlockLight(void) +{ + // Clear seeds: + memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); + memset(m_IsSeed2, 0, sizeof(m_IsSeed2)); + m_NumSeeds = 0; + + // Walk every column that has all XZ neighbors, make a seed for each light-emitting block: + for (int z = 1; z < cChunkDef::Width * 3 - 1; z++) + { + int BaseZ = z * cChunkDef::Width * 3; + for (int x = 1; x < cChunkDef::Width * 3 - 1; x++) + { + int idx = BaseZ + x; + for (int y = m_HeightMap[idx], Index = idx + y * BlocksPerYLayer; y >= 0; y--, Index -= BlocksPerYLayer) + { + if (g_BlockLightValue[m_BlockTypes[Index]] == 0) + { + continue; + } + + // Add current block as a seed: + m_IsSeed1[Index] = true; + m_SeedIdx1[m_NumSeeds++] = Index; + + // Light it up: + m_BlockLight[Index] = g_BlockLightValue[m_BlockTypes[Index]]; + } + } + } +} + + + + + +void cLightingThread::CalcLight(NIBBLETYPE * a_Light) +{ + int NumSeeds2 = 0; + while (m_NumSeeds > 0) + { + // Buffer 1 -> buffer 2 + memset(m_IsSeed2, 0, sizeof(m_IsSeed2)); + NumSeeds2 = 0; + CalcLightStep(a_Light, m_NumSeeds, m_IsSeed1, m_SeedIdx1, NumSeeds2, m_IsSeed2, m_SeedIdx2); + if (NumSeeds2 == 0) + { + return; + } + + // Buffer 2 -> buffer 1 + memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); + m_NumSeeds = 0; + CalcLightStep(a_Light, NumSeeds2, m_IsSeed2, m_SeedIdx2, m_NumSeeds, m_IsSeed1, m_SeedIdx1); + } +} + + + + + +void cLightingThread::CalcLightStep( + NIBBLETYPE * a_Light, + int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn, + int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut +) +{ + int NumSeedsOut = 0; + for (int i = 0; i < a_NumSeedsIn; i++) + { + int SeedIdx = a_SeedIdxIn[i]; + int SeedX = SeedIdx % (cChunkDef::Width * 3); + int SeedZ = (SeedIdx / (cChunkDef::Width * 3)) % (cChunkDef::Width * 3); + int SeedY = SeedIdx / BlocksPerYLayer; + + // Propagate seed: + if (SeedX < cChunkDef::Width * 3 - 1) + { + PropagateLight(a_Light, SeedIdx, SeedIdx + 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedX > 0) + { + PropagateLight(a_Light, SeedIdx, SeedIdx - 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedZ < cChunkDef::Width * 3 - 1) + { + PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedZ > 0) + { + PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedY < cChunkDef::Height - 1) + { + PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedY > 0) + { + PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + } // for i - a_SeedIdxIn[] + a_NumSeedsOut = NumSeedsOut; +} + + + + + +void cLightingThread::CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight) +{ + int InIdx = cChunkDef::Width * 49; // Index to the first nibble of the middle chunk in the a_LightArray + int OutIdx = 0; + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x += 2) + { + a_ChunkLight[OutIdx++] = (a_LightArray[InIdx + 1] << 4) | a_LightArray[InIdx]; + InIdx += 2; + } + InIdx += cChunkDef::Width * 2; + } + // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows + // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip + InIdx += cChunkDef::Width * cChunkDef::Width * 6; + } +} + + + + diff --git a/src/LightingThread.h b/src/LightingThread.h new file mode 100644 index 000000000..498755025 --- /dev/null +++ b/src/LightingThread.h @@ -0,0 +1,181 @@ + +// LightingThread.h + +// Interfaces to the cLightingThread class representing the thread that processes requests for lighting + +/* +Lighting is done on whole chunks. For each chunk to be lighted, the whole 3x3 chunk area around it is read, +then it is processed, so that the middle chunk area has valid lighting, and the lighting is copied into the ChunkMap. +Lighting is calculated in full char arrays instead of nibbles, so that accessing the arrays is fast. +Lighting is calculated in a flood-fill fashion: +1. Generate seeds from where the light spreads (full skylight / light-emitting blocks) +2. For each seed: + - Spread the light 1 block in each of the 6 cardinal directions, if the blocktype allows + - If the recipient block has had lower lighting value than that being spread, make it a new seed +3. Repeat step 2, until there are no more seeds +The seeds need two fast operations: + - Check if a block at [x, y, z] is already a seed + - Get the next seed in the row +For that reason it is stored in two arrays, one stores a bool saying a seed is in that position, +the other is an array of seed coords, encoded as a single int. +Step 2 needs two separate storages for old seeds and new seeds, so there are two actual storages for that purpose, +their content is swapped after each full step-2-cycle. + +The thread has two queues of chunks that are to be lighted. +The first queue, m_Queue, is the only one that is publicly visible, chunks get queued there by external requests. +The second one, m_PostponedQueue, is for chunks that have been taken out of m_Queue and didn't have neighbors ready. +Chunks from m_PostponedQueue are moved back into m_Queue when their neighbors get valid, using the ChunkReady callback. +*/ + + + +#pragma once + +#include "OSSupport/IsThread.h" +#include "ChunkDef.h" + + + + + +// fwd: "cWorld.h" +class cWorld; + +// fwd: "cChunkMap.h" +class cChunkStay; + + + + + +class cLightingThread : + public cIsThread +{ + typedef cIsThread super; + +public: + + cLightingThread(void); + ~cLightingThread(); + + bool Start(cWorld * a_World); + + void Stop(void); + + /// Queues the entire chunk for lighting + void QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter = NULL); + + /// Blocks until the queue is empty or the thread is terminated + void WaitForQueueEmpty(void); + + size_t GetQueueLength(void); + + /// Called from cWorld when a chunk gets valid. Chunks in m_PostponedQueue may need moving into m_Queue + void ChunkReady(int a_ChunkX, int a_ChunkZ); + +protected: + + struct sItem + { + int x, z; + cChunkStay * m_ChunkStay; + cChunkCoordCallback * m_Callback; + + sItem(void) {} // empty default constructor needed + sItem(int a_X, int a_Z, cChunkStay * a_ChunkStay, cChunkCoordCallback * a_Callback) : + x(a_X), + z(a_Z), + m_ChunkStay(a_ChunkStay), + m_Callback(a_Callback) + { + } + } ; + + typedef std::list sItems; + + cWorld * m_World; + cCriticalSection m_CS; + sItems m_Queue; + sItems m_PostponedQueue; // Chunks that have been postponed due to missing neighbors + cEvent m_evtItemAdded; // Set when queue is appended, or to stop the thread + cEvent m_evtQueueEmpty; // Set when the queue gets empty + + // Buffers for the 3x3 chunk data + // These buffers alone are 1.7 MiB in size, therefore they cannot be located on the stack safely - some architectures may have only 1 MiB for stack, or even less + // Placing the buffers into the object means that this object can light chunks only in one thread! + // The blobs are XZY organized as a whole, instead of 3x3 XZY-organized subarrays -> + // -> This means data has to be scatterred when reading and gathered when writing! + static const int BlocksPerYLayer = cChunkDef::Width * cChunkDef::Width * 3 * 3; + BLOCKTYPE m_BlockTypes[BlocksPerYLayer * cChunkDef::Height]; + NIBBLETYPE m_BlockLight[BlocksPerYLayer * cChunkDef::Height]; + NIBBLETYPE m_SkyLight [BlocksPerYLayer * cChunkDef::Height]; + HEIGHTTYPE m_HeightMap [BlocksPerYLayer]; + + // Seed management (5.7 MiB) + // Two buffers, in each calc step one is set as input and the other as output, then in the next step they're swapped + // Each seed is represented twice in this structure - both as a "list" and as a "position". + // "list" allows fast traversal from seed to seed + // "position" allows fast checking if a coord is already a seed + unsigned char m_IsSeed1 [BlocksPerYLayer * cChunkDef::Height]; + unsigned int m_SeedIdx1[BlocksPerYLayer * cChunkDef::Height]; + unsigned char m_IsSeed2 [BlocksPerYLayer * cChunkDef::Height]; + unsigned int m_SeedIdx2[BlocksPerYLayer * cChunkDef::Height]; + int m_NumSeeds; + + virtual void Execute(void) override; + + /// Lights the entire chunk. If neighbor chunks don't exist, touches them and re-queues the chunk + void LightChunk(sItem & a_Item); + + /// Prepares m_BlockTypes and m_HeightMap data; returns false if any of the chunks fail. Zeroes out the light arrays + bool ReadChunks(int a_ChunkX, int a_ChunkZ); + + /// Uses m_HeightMap to initialize the m_SkyLight[] data; fills in seeds for the skylight + void PrepareSkyLight(void); + + /// Uses m_BlockTypes to initialize the m_BlockLight[] data; fills in seeds for the blocklight + void PrepareBlockLight(void); + + /// Calculates light in the light array specified, using stored seeds + void CalcLight(NIBBLETYPE * a_Light); + + /// Does one step in the light calculation - one seed propagation and seed recalculation + void CalcLightStep( + NIBBLETYPE * a_Light, + int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn, + int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut + ); + + /// Compresses from 1-block-per-byte (faster calc) into 2-blocks-per-byte (MC storage): + void CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight); + + inline void PropagateLight( + NIBBLETYPE * a_Light, + int a_SrcIdx, int a_DstIdx, + int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut + ) + { + ASSERT(a_SrcIdx >= 0); + ASSERT(a_SrcIdx < ARRAYCOUNT(m_SkyLight)); + ASSERT(a_DstIdx >= 0); + ASSERT(a_DstIdx < ARRAYCOUNT(m_BlockTypes)); + + if (a_Light[a_SrcIdx] <= a_Light[a_DstIdx] + g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]]) + { + // We're not offering more light than the dest block already has + return; + } + + a_Light[a_DstIdx] = a_Light[a_SrcIdx] - g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]]; + if (!a_IsSeedOut[a_DstIdx]) + { + a_IsSeedOut[a_DstIdx] = true; + a_SeedIdxOut[a_NumSeedsOut++] = a_DstIdx; + } + } + +} ; + + + + diff --git a/src/LineBlockTracer.cpp b/src/LineBlockTracer.cpp new file mode 100644 index 000000000..9fcbca915 --- /dev/null +++ b/src/LineBlockTracer.cpp @@ -0,0 +1,262 @@ + +// LineBlockTracer.cpp + +// Implements the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points + +#include "Globals.h" +#include "LineBlockTracer.h" +#include "Vector3d.h" +#include "World.h" +#include "Chunk.h" + + + + + + +cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : + super(a_World, a_Callbacks) +{ +} + + + + + +bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End) +{ + cLineBlockTracer Tracer(a_World, a_Callbacks); + return Tracer.Trace(a_Start.x, a_Start.y, a_Start.z, a_End.x, a_End.y, a_End.z); +} + + + + + +bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks &a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ) +{ + cLineBlockTracer Tracer(a_World, a_Callbacks); + return Tracer.Trace(a_StartX, a_StartY, a_StartZ, a_EndX, a_EndY, a_EndZ); +} + + + + + +bool cLineBlockTracer::Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ) +{ + // Initialize the member veriables: + m_StartX = a_StartX; + m_StartY = a_StartY; + m_StartZ = a_StartZ; + m_EndX = a_EndX; + m_EndY = a_EndY; + m_EndZ = a_EndZ; + m_DirX = (m_StartX < m_EndX) ? 1 : -1; + m_DirY = (m_StartY < m_EndY) ? 1 : -1; + m_DirZ = (m_StartZ < m_EndZ) ? 1 : -1; + m_CurrentFace = BLOCK_FACE_NONE; + + // Check the start coords, adjust into the world: + if (m_StartY < 0) + { + if (m_EndY < 0) + { + // Nothing to trace + m_Callbacks->OnNoMoreHits(); + return true; + } + FixStartBelowWorld(); + m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ); + } + else if (m_StartY >= cChunkDef::Height) + { + if (m_EndY >= cChunkDef::Height) + { + m_Callbacks->OnNoMoreHits(); + return true; + } + FixStartAboveWorld(); + m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ); + } + + m_CurrentX = (int)floor(m_StartX); + m_CurrentY = (int)floor(m_StartY); + m_CurrentZ = (int)floor(m_StartZ); + + m_DiffX = m_EndX - m_StartX; + m_DiffY = m_EndY - m_StartY; + m_DiffZ = m_EndZ - m_StartZ; + + // The actual trace is handled with ChunkMapCS locked by calling our Item() for the specified chunk + int BlockX = (int)floor(m_StartX); + int BlockZ = (int)floor(m_StartZ); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); + return m_World->DoWithChunk(ChunkX, ChunkZ, *this); +} + + + + + +void cLineBlockTracer::FixStartAboveWorld(void) +{ + // We must set the start Y to less than cChunkDef::Height so that it is considered inside the world later on + // Therefore we use an EPS-offset from the height, as small as reasonably possible. + const double Height = (double)cChunkDef::Height - 0.00001; + CalcXZIntersection(Height, m_StartX, m_StartZ); + m_StartY = Height; +} + + + + + +void cLineBlockTracer::FixStartBelowWorld(void) +{ + CalcXZIntersection(0, m_StartX, m_StartZ); + m_StartY = 0; +} + + + + + +void cLineBlockTracer::CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ) +{ + double Ratio = (m_StartY - a_Y) / (m_StartY - m_EndY); + a_IntersectX = m_StartX + (m_EndX - m_StartX) * Ratio; + a_IntersectZ = m_StartZ + (m_EndZ - m_StartZ) * Ratio; +} + + + + + +bool cLineBlockTracer::MoveToNextBlock(void) +{ + // Find out which of the current block's walls gets hit by the path: + static const double EPS = 0.00001; + double Coeff = 1; + enum eDirection + { + dirNONE, + dirX, + dirY, + dirZ, + } Direction = dirNONE; + if (abs(m_DiffX) > EPS) + { + double DestX = (m_DirX > 0) ? (m_CurrentX + 1) : m_CurrentX; + Coeff = (DestX - m_StartX) / m_DiffX; + if (Coeff <= 1) + { + Direction = dirX; + } + } + if (abs(m_DiffY) > EPS) + { + double DestY = (m_DirY > 0) ? (m_CurrentY + 1) : m_CurrentY; + double CoeffY = (DestY - m_StartY) / m_DiffY; + if (CoeffY < Coeff) + { + Coeff = CoeffY; + Direction = dirY; + } + } + if (abs(m_DiffZ) > EPS) + { + double DestZ = (m_DirZ > 0) ? (m_CurrentZ + 1) : m_CurrentZ; + double CoeffZ = (DestZ - m_StartZ) / m_DiffZ; + if (CoeffZ < Coeff) + { + Coeff = CoeffZ; + Direction = dirZ; + } + } + + // Based on the wall hit, adjust the current coords + switch (Direction) + { + case dirX: m_CurrentX += m_DirX; m_CurrentFace = (m_DirX > 0) ? BLOCK_FACE_XM : BLOCK_FACE_XP; break; + case dirY: m_CurrentY += m_DirY; m_CurrentFace = (m_DirY > 0) ? BLOCK_FACE_YM : BLOCK_FACE_YP; break; + case dirZ: m_CurrentZ += m_DirZ; m_CurrentFace = (m_DirZ > 0) ? BLOCK_FACE_ZM : BLOCK_FACE_ZP; break; + case dirNONE: return false; + } + return true; +} + + + + + +bool cLineBlockTracer::Item(cChunk * a_Chunk) +{ + ASSERT((m_CurrentY >= 0) && (m_CurrentY < cChunkDef::Height)); // This should be provided by FixStartAboveWorld() / FixStartBelowWorld() + + // This is the actual line tracing loop. + bool Finished = false; + while (true) + { + // Report the current block through the callbacks: + if (a_Chunk == NULL) + { + m_Callbacks->OnNoChunk(); + return false; + } + if (a_Chunk->IsValid()) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + int RelX = m_CurrentX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = m_CurrentZ - a_Chunk->GetPosZ() * cChunkDef::Width; + a_Chunk->GetBlockTypeMeta(RelX, m_CurrentY, RelZ, BlockType, BlockMeta); + if (m_Callbacks->OnNextBlock(m_CurrentX, m_CurrentY, m_CurrentZ, BlockType, BlockMeta, m_CurrentFace)) + { + // The callback terminated the trace + return false; + } + } + else + { + if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ, m_CurrentFace)) + { + // The callback terminated the trace + return false; + } + } + + // Move to next block + if (!MoveToNextBlock()) + { + // We've reached the end + m_Callbacks->OnNoMoreHits(); + return true; + } + + // Update the current chunk + if (a_Chunk != NULL) + { + a_Chunk = a_Chunk->GetNeighborChunk(m_CurrentX, m_CurrentZ); + } + + if ((m_CurrentY < 0) || (m_CurrentY >= cChunkDef::Height)) + { + // We've gone out of the world, that's the end of this trace + double IntersectX, IntersectZ; + CalcXZIntersection(m_CurrentY, IntersectX, IntersectZ); + if (m_Callbacks->OnOutOfWorld(IntersectX, m_CurrentY, IntersectZ)) + { + // The callback terminated the trace + return false; + } + m_Callbacks->OnNoMoreHits(); + return true; + } + } +} + + + + diff --git a/src/LineBlockTracer.h b/src/LineBlockTracer.h new file mode 100644 index 000000000..ccbb70ea6 --- /dev/null +++ b/src/LineBlockTracer.h @@ -0,0 +1,87 @@ + +// LineBlockTracer.h + +// Declares the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points + + + + + +#pragma once + +#include "BlockTracer.h" + + + + + +// fwd: Chunk.h +class cChunk; + +// fwd: cChunkMap.h +typedef cItemCallback cChunkCallback; + + + + + + +class cLineBlockTracer : + public cBlockTracer, + public cChunkCallback +{ + typedef cBlockTracer super; + +public: + cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks); + + /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) + bool Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ); + + // Utility functions for simple one-line usage: + /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) + static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ); + + /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) + static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End); + +protected: + // The start point of the trace + double m_StartX, m_StartY, m_StartZ; + + // The end point of the trace + double m_EndX, m_EndY, m_EndZ; + + // The difference in coords, End - Start + double m_DiffX, m_DiffY, m_DiffZ; + + // The increment at which the block coords are going from Start to End; either +1 or -1 + int m_DirX, m_DirY, m_DirZ; + + // The current block + int m_CurrentX, m_CurrentY, m_CurrentZ; + + // The face through which the current block has been entered + char m_CurrentFace; + + + /// Adjusts the start point above the world to just at the world's top + void FixStartAboveWorld(void); + + /// Adjusts the start point below the world to just at the world's bottom + void FixStartBelowWorld(void); + + /// Calculates the XZ coords of an intersection with the specified Yconst plane; assumes that such an intersection exists + void CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ); + + /// Moves m_Current to the next block on the line; returns false if no move is possible (reached the end) + bool MoveToNextBlock(void); + + // cChunkCallback overrides: + virtual bool Item(cChunk * a_Chunk) override; +} ; + + + + + diff --git a/src/LinearInterpolation.cpp b/src/LinearInterpolation.cpp new file mode 100644 index 000000000..d4975418b --- /dev/null +++ b/src/LinearInterpolation.cpp @@ -0,0 +1,251 @@ + +// LinearInterpolation.cpp + +// Implements methods for linear interpolation over 1D, 2D and 3D arrays + +#include "Globals.h" +#include "LinearInterpolation.h" + + + + + +/* +// Perform an automatic test upon program start (use breakpoints to debug): + +extern void Debug3DNoise(float * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase); + +class Test +{ +public: + Test(void) + { + // DoTest1(); + DoTest2(); + } + + + void DoTest1(void) + { + float In[8] = {0, 1, 2, 3, 1, 2, 2, 2}; + float Out[3 * 3 * 3]; + LinearInterpolate1DArray(In, 4, Out, 9); + LinearInterpolate2DArray(In, 2, 2, Out, 3, 3); + LinearInterpolate3DArray(In, 2, 2, 2, Out, 3, 3, 3); + LOGD("Out[0]: %f", Out[0]); + } + + + void DoTest2(void) + { + float In[3 * 3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + float Out[15 * 16 * 17]; + LinearInterpolate3DArray(In, 3, 3, 3, Out, 15, 16, 17); + Debug3DNoise(Out, 15, 16, 17, "LERP test"); + } +} gTest; +//*/ + + + + + +// Puts linearly interpolated values from one array into another array. 1D version +void LinearInterpolate1DArray( + float * a_Src, + int a_SrcSizeX, + float * a_Dst, + int a_DstSizeX +) +{ + a_Dst[0] = a_Src[0]; + int DstSizeXm1 = a_DstSizeX - 1; + int SrcSizeXm1 = a_SrcSizeX - 1; + float fDstSizeXm1 = (float)DstSizeXm1; + float fSrcSizeXm1 = (float)SrcSizeXm1; + for (int x = 1; x < DstSizeXm1; x++) + { + int SrcIdx = x * SrcSizeXm1 / DstSizeXm1; + float ValLo = a_Src[SrcIdx]; + float ValHi = a_Src[SrcIdx + 1]; + float Ratio = (float)x * fSrcSizeXm1 / fDstSizeXm1 - SrcIdx; + a_Dst[x] = ValLo + (ValHi - ValLo) * Ratio; + } + a_Dst[a_DstSizeX - 1] = a_Src[a_SrcSizeX - 1]; +} + + + + + +// Puts linearly interpolated values from one array into another array. 2D version +void LinearInterpolate2DArray( + float * a_Src, + int a_SrcSizeX, int a_SrcSizeY, + float * a_Dst, + int a_DstSizeX, int a_DstSizeY +) +{ + ASSERT(a_DstSizeX > 0); + ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); + ASSERT(a_DstSizeY > 0); + ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); + + // Calculate interpolation ratios and src indices along each axis: + float RatioX[MAX_INTERPOL_SIZEX]; + float RatioY[MAX_INTERPOL_SIZEY]; + int SrcIdxX[MAX_INTERPOL_SIZEX]; + int SrcIdxY[MAX_INTERPOL_SIZEY]; + for (int x = 1; x < a_DstSizeX; x++) + { + SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); + RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; + } + for (int y = 1; y < a_DstSizeY; y++) + { + SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); + RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; + } + + // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: + SrcIdxX[0] = 0; + RatioX[0] = 0; + SrcIdxY[0] = 0; + RatioY[0] = 0; + SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; + RatioX[a_DstSizeX - 1] = 1; + SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; + RatioY[a_DstSizeY - 1] = 1; + + // Output all the dst array values using the indices and ratios: + int idx = 0; + for (int y = 0; y < a_DstSizeY; y++) + { + int idxLoY = a_SrcSizeX * SrcIdxY[y]; + int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); + float ry = RatioY[y]; + for (int x = 0; x < a_DstSizeX; x++) + { + // The four src corners of the current "cell": + float LoXLoY = a_Src[SrcIdxX[x] + idxLoY]; + float HiXLoY = a_Src[SrcIdxX[x] + 1 + idxLoY]; + float LoXHiY = a_Src[SrcIdxX[x] + idxHiY]; + float HiXHiY = a_Src[SrcIdxX[x] + 1 + idxHiY]; + + // Linear interpolation along the X axis: + float InterpXLoY = LoXLoY + (HiXLoY - LoXLoY) * RatioX[x]; + float InterpXHiY = LoXHiY + (HiXHiY - LoXHiY) * RatioX[x]; + + // Linear interpolation along the Y axis: + a_Dst[idx] = InterpXLoY + (InterpXHiY - InterpXLoY) * ry; + idx += 1; + } + } +} + + + + + +/// Puts linearly interpolated values from one array into another array. 3D version +void LinearInterpolate3DArray( + float * a_Src, + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, + float * a_Dst, + int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ +) +{ + ASSERT(a_DstSizeX > 0); + ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); + ASSERT(a_DstSizeY > 0); + ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); + ASSERT(a_DstSizeZ > 0); + ASSERT(a_DstSizeZ < MAX_INTERPOL_SIZEZ); + + // Calculate interpolation ratios and src indices along each axis: + float RatioX[MAX_INTERPOL_SIZEX]; + float RatioY[MAX_INTERPOL_SIZEY]; + float RatioZ[MAX_INTERPOL_SIZEZ]; + int SrcIdxX[MAX_INTERPOL_SIZEX]; + int SrcIdxY[MAX_INTERPOL_SIZEY]; + int SrcIdxZ[MAX_INTERPOL_SIZEZ]; + for (int x = 1; x < a_DstSizeX; x++) + { + SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); + RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; + } + for (int y = 1; y < a_DstSizeY; y++) + { + SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); + RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; + } + for (int z = 1; z < a_DstSizeZ; z++) + { + SrcIdxZ[z] = z * (a_SrcSizeZ - 1) / (a_DstSizeZ - 1); + RatioZ[z] = ((float)(z * (a_SrcSizeZ - 1)) / (a_DstSizeZ - 1)) - SrcIdxZ[z]; + } + + // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: + SrcIdxX[0] = 0; + RatioX[0] = 0; + SrcIdxY[0] = 0; + RatioY[0] = 0; + SrcIdxZ[0] = 0; + RatioZ[0] = 0; + SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; + RatioX[a_DstSizeX - 1] = 1; + SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; + RatioY[a_DstSizeY - 1] = 1; + SrcIdxZ[a_DstSizeZ - 1] = a_SrcSizeZ - 2; + RatioZ[a_DstSizeZ - 1] = 1; + + // Output all the dst array values using the indices and ratios: + int idx = 0; + for (int z = 0; z < a_DstSizeZ; z++) + { + int idxLoZ = a_SrcSizeX * a_SrcSizeY * SrcIdxZ[z]; + int idxHiZ = a_SrcSizeX * a_SrcSizeY * (SrcIdxZ[z] + 1); + float rz = RatioZ[z]; + for (int y = 0; y < a_DstSizeY; y++) + { + int idxLoY = a_SrcSizeX * SrcIdxY[y]; + int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); + float ry = RatioY[y]; + for (int x = 0; x < a_DstSizeX; x++) + { + // The eight src corners of the current "cell": + float LoXLoYLoZ = a_Src[SrcIdxX[x] + idxLoY + idxLoZ]; + float HiXLoYLoZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxLoZ]; + float LoXHiYLoZ = a_Src[SrcIdxX[x] + idxHiY + idxLoZ]; + float HiXHiYLoZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxLoZ]; + float LoXLoYHiZ = a_Src[SrcIdxX[x] + idxLoY + idxHiZ]; + float HiXLoYHiZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxHiZ]; + float LoXHiYHiZ = a_Src[SrcIdxX[x] + idxHiY + idxHiZ]; + float HiXHiYHiZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxHiZ]; + + // Linear interpolation along the Z axis: + float LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * rz; + float HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * rz; + float LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * rz; + float HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * rz; + + // Linear interpolation along the Y axis: + float LoXInYInZ = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * ry; + float HiXInYInZ = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * ry; + + // Linear interpolation along the X axis: + a_Dst[idx] = LoXInYInZ + (HiXInYInZ - LoXInYInZ) * RatioX[x]; + idx += 1; + } // for x + } // for y + } // for z +} + + + + + diff --git a/src/LinearInterpolation.h b/src/LinearInterpolation.h new file mode 100644 index 000000000..4b798d9bc --- /dev/null +++ b/src/LinearInterpolation.h @@ -0,0 +1,60 @@ + +// LinearInterpolation.h + +// Declares methods for linear interpolation over 1D, 2D and 3D arrays + + + + + +#pragma once + + + + + +// 2D and 3D Interpolation is optimized by precalculating the ratios into static-sized arrays +// These arrays enforce a max size of the dest array, but the limits are settable here: +const int MAX_INTERPOL_SIZEX = 256; ///< Maximum X-size of the interpolated array +const int MAX_INTERPOL_SIZEY = 512; ///< Maximum Y-size of the interpolated array +const int MAX_INTERPOL_SIZEZ = 256; ///< Maximum Z-size of the interpolated array + + + + + +/// Puts linearly interpolated values from one array into another array. 1D version +void LinearInterpolate1DArray( + float * a_Src, ///< Src array + int a_SrcSizeX, ///< Count of the src array + float * a_Dst, ///< Src array + int a_DstSizeX ///< Count of the dst array +); + + + + + +/// Puts linearly interpolated values from one array into another array. 2D version +void LinearInterpolate2DArray( + float * a_Src, ///< Src array, [x + a_SrcSizeX * y] + int a_SrcSizeX, int a_SrcSizeY, ///< Count of the src array, in each direction + float * a_Dst, ///< Dst array, [x + a_DstSizeX * y] + int a_DstSizeX, int a_DstSizeY ///< Count of the dst array, in each direction +); + + + + + +/// Puts linearly interpolated values from one array into another array. 3D version +void LinearInterpolate3DArray( + float * a_Src, ///< Src array, [x + a_SrcSizeX * y + a_SrcSizeX * a_SrcSizeY * z] + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Count of the src array, in each direction + float * a_Dst, ///< Dst array, [x + a_DstSizeX * y + a_DstSizeX * a_DstSizeY * z] + int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ ///< Count of the dst array, in each direction +); + + + + diff --git a/src/LinearUpscale.h b/src/LinearUpscale.h new file mode 100644 index 000000000..b7ac84c6a --- /dev/null +++ b/src/LinearUpscale.h @@ -0,0 +1,244 @@ + +// LinearUpscale.h + +// Declares the functions for linearly upscaling arrays + +/* +Upscaling means that the array is divided into same-size "cells", and each cell is +linearly interpolated between its corners. The array's dimensions are therefore +1 + CellSize * NumCells, for each direction. + +Upscaling is more efficient than linear interpolation, because the cell sizes are integral +and therefore the cells' boundaries are on the array points. + +However, upscaling usually requires generating the "1 +" in each direction. + +Upscaling is implemented in templates, so that it's compatible with multiple datatypes. +Therefore, there is no cpp file. + +InPlace upscaling works on a single array and assumes that the values to work on have already +been interspersed into the array to the cell boundaries. +Specifically, a_Array[x * a_AnchorStepX + y * a_AnchorStepY] contains the anchor value. + +Regular upscaling takes two arrays and "moves" the input from src to dst; src is expected packed. +*/ + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works in-place (input is already present at the correct output coords) +*/ +template void LinearUpscale2DArrayInPlace( + TYPE * a_Array, + int a_SizeX, int a_SizeY, // Dimensions of the array + int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction +) +{ + // First interpolate columns where the anchor points are: + int LastYCell = a_SizeY - a_AnchorStepY; + for (int y = 0; y < LastYCell; y += a_AnchorStepY) + { + int Idx = a_SizeX * y; + for (int x = 0; x < a_SizeX; x += a_AnchorStepX) + { + TYPE StartValue = a_Array[Idx]; + TYPE EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY]; + TYPE Diff = EndValue - StartValue; + for (int CellY = 1; CellY < a_AnchorStepY; CellY++) + { + a_Array[Idx + a_SizeX * CellY] = StartValue + Diff * CellY / a_AnchorStepY; + } // for CellY + Idx += a_AnchorStepX; + } // for x + } // for y + + // Now interpolate in rows, each row has values in the anchor columns + int LastXCell = a_SizeX - a_AnchorStepX; + for (int y = 0; y < a_SizeY; y++) + { + int Idx = a_SizeX * y; + for (int x = 0; x < LastXCell; x += a_AnchorStepX) + { + TYPE StartValue = a_Array[Idx]; + TYPE EndValue = a_Array[Idx + a_AnchorStepX]; + TYPE Diff = EndValue - StartValue; + for (int CellX = 1; CellX < a_AnchorStepX; CellX++) + { + a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX; + } // for CellY + Idx += a_AnchorStepX; + } + } +} + + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works on two arrays, input is packed and output is to be completely constructed. +*/ +template void LinearUpscale2DArray( + TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY + int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array + TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) + int a_UpscaleX, int a_UpscaleY ///< Upscale factor for each direction +) +{ + // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes + // Feel free to enlarge them if needed, but keep in mind that they're on the stack + const int MAX_UPSCALE_X = 128; + const int MAX_UPSCALE_Y = 128; + + ASSERT(a_Src != NULL); + ASSERT(a_Dst != NULL); + ASSERT(a_SrcSizeX > 0); + ASSERT(a_SrcSizeY > 0); + ASSERT(a_UpscaleX > 0); + ASSERT(a_UpscaleY > 0); + ASSERT(a_UpscaleX <= MAX_UPSCALE_X); + ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); + + // Pre-calculate the upscaling ratios: + TYPE RatioX[MAX_UPSCALE_X]; + TYPE RatioY[MAX_UPSCALE_Y]; + for (int x = 0; x <= a_UpscaleX; x++) + { + RatioX[x] = (TYPE)x / a_UpscaleX; + } + for (int y = 0; y <= a_UpscaleY; y++) + { + RatioY[y] = (TYPE)y / a_UpscaleY; + } + + // Interpolate each XY cell: + int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; + int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; + for (int y = 0; y < (a_SrcSizeY - 1); y++) + { + int DstY = y * a_UpscaleY; + int idx = y * a_SrcSizeX; + for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) + { + int DstX = x * a_UpscaleX; + TYPE LoXLoY = a_Src[idx]; + TYPE LoXHiY = a_Src[idx + a_SrcSizeX]; + TYPE HiXLoY = a_Src[idx + 1]; + TYPE HiXHiY = a_Src[idx + 1 + a_SrcSizeX]; + for (int CellY = 0; CellY <= a_UpscaleY; CellY++) + { + int DestIdx = (DstY + CellY) * DstSizeX + DstX; + ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY); + TYPE LoXInY = LoXLoY + (LoXHiY - LoXLoY) * RatioY[CellY]; + TYPE HiXInY = HiXLoY + (HiXHiY - HiXLoY) * RatioY[CellY]; + for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) + { + a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; + } + } // for CellY + } // for x + } // for y +} + + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works on two arrays, input is packed and output is to be completely constructed. +*/ +template void LinearUpscale3DArray( + TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array + TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1) + int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ ///< Upscale factor for each direction +) +{ + // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes + // Feel free to enlarge them if needed, but keep in mind that they're on the stack + const int MAX_UPSCALE_X = 128; + const int MAX_UPSCALE_Y = 128; + const int MAX_UPSCALE_Z = 128; + + ASSERT(a_Src != NULL); + ASSERT(a_Dst != NULL); + ASSERT(a_SrcSizeX > 0); + ASSERT(a_SrcSizeY > 0); + ASSERT(a_SrcSizeZ > 0); + ASSERT(a_UpscaleX > 0); + ASSERT(a_UpscaleY > 0); + ASSERT(a_UpscaleZ > 0); + ASSERT(a_UpscaleX <= MAX_UPSCALE_X); + ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); + ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z); + + // Pre-calculate the upscaling ratios: + TYPE RatioX[MAX_UPSCALE_X]; + TYPE RatioY[MAX_UPSCALE_Y]; + TYPE RatioZ[MAX_UPSCALE_Y]; + for (int x = 0; x <= a_UpscaleX; x++) + { + RatioX[x] = (TYPE)x / a_UpscaleX; + } + for (int y = 0; y <= a_UpscaleY; y++) + { + RatioY[y] = (TYPE)y / a_UpscaleY; + } + for (int z = 0; z <= a_UpscaleZ; z++) + { + RatioZ[z] = (TYPE)z / a_UpscaleZ; + } + + // Interpolate each XYZ cell: + int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; + int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; + int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1; + for (int z = 0; z < (a_SrcSizeZ - 1); z++) + { + int DstZ = z * a_UpscaleZ; + for (int y = 0; y < (a_SrcSizeY - 1); y++) + { + int DstY = y * a_UpscaleY; + int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY; + for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) + { + int DstX = x * a_UpscaleX; + TYPE LoXLoYLoZ = a_Src[idx]; + TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY]; + TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX]; + TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; + TYPE HiXLoYLoZ = a_Src[idx + 1]; + TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY]; + TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX]; + TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; + for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++) + { + TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ]; + TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ]; + TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ]; + TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ]; + for (int CellY = 0; CellY <= a_UpscaleY; CellY++) + { + int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX; + ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ); + TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY]; + TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY]; + for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) + { + a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; + } + } // for CellY + } // for CellZ + } // for x + } // for y + } // for z +} + + + + + diff --git a/src/Log.cpp b/src/Log.cpp new file mode 100644 index 000000000..fc19595db --- /dev/null +++ b/src/Log.cpp @@ -0,0 +1,169 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Log.h" + +#include +#include +#include "OSSupport/IsThread.h" + +#if defined(ANDROID_NDK) + #include + #include "ToJava.h" +#endif + + + + +cLog* cLog::s_Log = NULL; + +cLog::cLog(const AString & a_FileName ) + : m_File(NULL) +{ + s_Log = this; + + // create logs directory + cFile::CreateFolder(FILE_IO_PREFIX + AString("logs")); + + OpenLog((FILE_IO_PREFIX + AString("logs/") + a_FileName).c_str() ); +} + + + + + +cLog::~cLog() +{ + CloseLog(); + s_Log = NULL; +} + + + + + +cLog* cLog::GetInstance() +{ + if (s_Log != NULL) + { + return s_Log; + } + + new cLog("log.txt"); + return s_Log; +} + + + + + +void cLog::CloseLog() +{ + if( m_File ) + fclose (m_File); + m_File = 0; +} + + + + + +void cLog::OpenLog( const char* a_FileName ) +{ + if(m_File) fclose (m_File); + #ifdef _MSC_VER + fopen_s( &m_File, a_FileName, "a+" ); + #else + m_File = fopen(a_FileName, "a+" ); + #endif +} + + + + + +void cLog::ClearLog() +{ + #ifdef _MSC_VER + if( fopen_s( &m_File, "log.txt", "w" ) == 0) + fclose (m_File); + #else + m_File = fopen("log.txt", "w" ); + if( m_File ) + fclose (m_File); + #endif + m_File = 0; +} + + + + + +void cLog::Log(const char * a_Format, va_list argList) +{ + AString Message; + AppendVPrintf(Message, a_Format, argList); + + time_t rawtime; + time ( &rawtime ); + + struct tm* timeinfo; +#ifdef _MSC_VER + struct tm timeinforeal; + timeinfo = &timeinforeal; + localtime_s(timeinfo, &rawtime ); +#else + timeinfo = localtime( &rawtime ); +#endif + + AString Line; + #ifdef _DEBUG + Printf(Line, "[%04x|%02d:%02d:%02d] %s", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); + #else + Printf(Line, "[%02d:%02d:%02d] %s", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); + #endif + if (m_File) + { + fprintf(m_File, "%s\n", Line.c_str(), m_File); + fflush(m_File); + } + + // Print to console: +#if defined(ANDROID_NDK) + //__android_log_vprint(ANDROID_LOG_ERROR,"MCServer", a_Format, argList); + __android_log_print(ANDROID_LOG_ERROR, "MCServer", "%s", Line.c_str() ); + //CallJavaFunction_Void_String(g_JavaThread, "AddToLog", Line ); +#else + printf("%s", Line.c_str()); +#endif + + #if defined (_WIN32) && defined(_DEBUG) + // In a Windows Debug build, output the log to debug console as well: + OutputDebugStringA((Line + "\n").c_str()); + #endif // _WIN32 +} + + + + + +void cLog::Log(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + Log( a_Format, argList ); + va_end(argList); +} + + + + + +void cLog::SimpleLog(const char* a_String) +{ + Log("%s", a_String ); +} + + + + diff --git a/src/Log.h b/src/Log.h new file mode 100644 index 000000000..d00022c6f --- /dev/null +++ b/src/Log.h @@ -0,0 +1,30 @@ + +#pragma once + + + + + +class cLog +{ // tolua_export +private: + FILE * m_File; + static cLog * s_Log; + +public: + cLog(const AString & a_FileName); + ~cLog(); + void Log(const char* a_Format, va_list argList ); + void Log(const char* a_Format, ...); + // tolua_begin + void SimpleLog(const char* a_String); + void OpenLog( const char* a_FileName ); + void CloseLog(); + void ClearLog(); + static cLog* GetInstance(); +}; +// tolua_end + + + + diff --git a/src/LuaExpat/lxplib.c b/src/LuaExpat/lxplib.c new file mode 100644 index 000000000..e26343ce9 --- /dev/null +++ b/src/LuaExpat/lxplib.c @@ -0,0 +1,599 @@ +/* +** $Id: lxplib.c,v 1.16 2007/06/05 20:03:12 carregal Exp $ +** LuaExpat: Lua bind for Expat library +** See Copyright Notice in license.html +*/ + + +#include +#include +#include + +#include "expat.h" + +#include "lua.h" +#include "lauxlib.h" + + +#include "lxplib.h" + + +#if !defined(lua_pushliteral) +#define lua_pushliteral(L, s) \ + lua_pushstring(L, "" s, (sizeof(s)/sizeof(char))-1) +#endif + + +enum XPState { + XPSpre, /* parser just initialized */ + XPSok, /* state while parsing */ + XPSfinished, /* state after finished parsing */ + XPSerror, + XPSstring /* state while reading a string */ +}; + +struct lxp_userdata { + lua_State *L; + XML_Parser parser; /* associated expat parser */ + int tableref; /* table with callbacks for this parser */ + enum XPState state; + luaL_Buffer *b; /* to concatenate sequences of cdata pieces */ +}; + +typedef struct lxp_userdata lxp_userdata; + + +static int reporterror (lxp_userdata *xpu) { + lua_State *L = xpu->L; + XML_Parser p = xpu->parser; + lua_pushnil(L); + lua_pushstring(L, XML_ErrorString(XML_GetErrorCode(p))); + lua_pushnumber(L, XML_GetCurrentLineNumber(p)); + lua_pushnumber(L, XML_GetCurrentColumnNumber(p) + 1); + lua_pushnumber(L, XML_GetCurrentByteIndex(p) + 1); + return 5; +} + + +static lxp_userdata *createlxp (lua_State *L) { + lxp_userdata *xpu = (lxp_userdata *)lua_newuserdata(L, sizeof(lxp_userdata)); + xpu->tableref = LUA_REFNIL; /* in case of errors... */ + xpu->parser = NULL; + xpu->L = NULL; + xpu->state = XPSpre; + luaL_getmetatable(L, ParserType); + lua_setmetatable(L, -2); + return xpu; +} + + +static void lxpclose (lua_State *L, lxp_userdata *xpu) { + luaL_unref(L, LUA_REGISTRYINDEX, xpu->tableref); + xpu->tableref = LUA_REFNIL; + if (xpu->parser) + XML_ParserFree(xpu->parser); + xpu->parser = NULL; +} + + + + +/* +** Auxiliary function to call a Lua handle +*/ +static void docall (lxp_userdata *xpu, int nargs, int nres) { + lua_State *L = xpu->L; + assert(xpu->state == XPSok); + if (lua_pcall(L, nargs + 1, nres, 0) != 0) { + xpu->state = XPSerror; + luaL_unref(L, LUA_REGISTRYINDEX, xpu->tableref); + xpu->tableref = luaL_ref(L, LUA_REGISTRYINDEX); /* error message */ + } +} + + +/* +** Check whether there is pending Cdata, and call its handle if necessary +*/ +static void dischargestring (lxp_userdata *xpu) { + assert(xpu->state == XPSstring); + xpu->state = XPSok; + luaL_pushresult(xpu->b); + docall(xpu, 1, 0); +} + + +/* +** Check whether there is a Lua handle for a given event: If so, +** put it on the stack (to be called later), and also push `self' +*/ +static int getHandle (lxp_userdata *xpu, const char *handle) { + lua_State *L = xpu->L; + if (xpu->state == XPSstring) dischargestring(xpu); + if (xpu->state == XPSerror) + return 0; /* some error happened before; skip all handles */ + lua_pushstring(L, handle); + lua_gettable(L, 3); + if (lua_toboolean(L, -1) == 0) { + lua_pop(L, 1); + return 0; + } + if (!lua_isfunction(L, -1)) { + luaL_error(L, "lxp `%s' callback is not a function", handle); + } + lua_pushvalue(L, 1); /* first argument in every call (self) */ + return 1; +} + + + +/* +** {====================================================== +** Handles +** ======================================================= +*/ + + +static void f_StartCdata (void *ud) { + lxp_userdata *xpu = (lxp_userdata *)ud; + if (getHandle(xpu, StartCdataKey) == 0) return; /* no handle */ + docall(xpu, 0, 0); +} + + +static void f_EndCdataKey (void *ud) { + lxp_userdata *xpu = (lxp_userdata *)ud; + if (getHandle(xpu, EndCdataKey) == 0) return; /* no handle */ + docall(xpu, 0, 0); +} + + +static void f_CharData (void *ud, const char *s, int len) { + lxp_userdata *xpu = (lxp_userdata *)ud; + if (xpu->state == XPSok) { + if (getHandle(xpu, CharDataKey) == 0) return; /* no handle */ + xpu->state = XPSstring; + luaL_buffinit(xpu->L, xpu->b); + } + if (xpu->state == XPSstring) + luaL_addlstring(xpu->b, s, len); +} + + +static void f_Comment (void *ud, const char *data) { + lxp_userdata *xpu = (lxp_userdata *)ud; + if (getHandle(xpu, CommentKey) == 0) return; /* no handle */ + lua_pushstring(xpu->L, data); + docall(xpu, 1, 0); +} + + +static void f_Default (void *ud, const char *data, int len) { + lxp_userdata *xpu = (lxp_userdata *)ud; + if (getHandle(xpu, DefaultKey) == 0) return; /* no handle */ + lua_pushlstring(xpu->L, data, len); + docall(xpu, 1, 0); +} + + +static void f_DefaultExpand (void *ud, const char *data, int len) { + lxp_userdata *xpu = (lxp_userdata *)ud; + if (getHandle(xpu, DefaultExpandKey) == 0) return; /* no handle */ + lua_pushlstring(xpu->L, data, len); + docall(xpu, 1, 0); +} + + +static void f_StartElement (void *ud, const char *name, const char **attrs) { + lxp_userdata *xpu = (lxp_userdata *)ud; + lua_State *L = xpu->L; + int lastspec = XML_GetSpecifiedAttributeCount(xpu->parser) / 2; + int i = 1; + if (getHandle(xpu, StartElementKey) == 0) return; /* no handle */ + lua_pushstring(L, name); + lua_newtable(L); + while (*attrs) { + if (i <= lastspec) { + lua_pushnumber(L, i++); + lua_pushstring(L, *attrs); + lua_settable(L, -3); + } + lua_pushstring(L, *attrs++); + lua_pushstring(L, *attrs++); + lua_settable(L, -3); + } + docall(xpu, 2, 0); /* call function with self, name, and attributes */ +} + + +static void f_EndElement (void *ud, const char *name) { + lxp_userdata *xpu = (lxp_userdata *)ud; + if (getHandle(xpu, EndElementKey) == 0) return; /* no handle */ + lua_pushstring(xpu->L, name); + docall(xpu, 1, 0); +} + + +static int f_ExternaEntity (XML_Parser p, const char *context, + const char *base, + const char *systemId, + const char *publicId) { + lxp_userdata *xpu = (lxp_userdata *)XML_GetUserData(p); + lua_State *L = xpu->L; + lxp_userdata *child; + int status; + if (getHandle(xpu, ExternalEntityKey) == 0) return 1; /* no handle */ + child = createlxp(L); + child->parser = XML_ExternalEntityParserCreate(p, context, NULL); + if (!child->parser) + luaL_error(L, "XML_ParserCreate failed"); + lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /*lua_getref(L, xpu->tableref); */ /* child uses the same table of its father */ + child->tableref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushstring(L, base); + lua_pushstring(L, systemId); + lua_pushstring(L, publicId); + docall(xpu, 4, 1); + status = lua_toboolean(L, -1); + lua_pop(L, 1); + lxpclose(L, child); + return status; +} + + +static void f_StartNamespaceDecl (void *ud, const char *prefix, + const char *uri) { + lxp_userdata *xpu = (lxp_userdata *)ud; + lua_State *L = xpu->L; + if (getHandle(xpu, StartNamespaceDeclKey) == 0) return; /* no handle */ + lua_pushstring(L, prefix); + lua_pushstring(L, uri); + docall(xpu, 2, 0); +} + + +static void f_EndNamespaceDecl (void *ud, const char *prefix) { + lxp_userdata *xpu = (lxp_userdata *)ud; + if (getHandle(xpu, EndNamespaceDeclKey) == 0) return; /* no handle */ + lua_pushstring(xpu->L, prefix); + docall(xpu, 1, 0); +} + + +static void f_NotationDecl (void *ud, const char *notationName, + const char *base, + const char *systemId, + const char *publicId) { + lxp_userdata *xpu = (lxp_userdata *)ud; + lua_State *L = xpu->L; + if (getHandle(xpu, NotationDeclKey) == 0) return; /* no handle */ + lua_pushstring(L, notationName); + lua_pushstring(L, base); + lua_pushstring(L, systemId); + lua_pushstring(L, publicId); + docall(xpu, 4, 0); +} + + +static int f_NotStandalone (void *ud) { + int status; + lxp_userdata *xpu = (lxp_userdata *)ud; + lua_State *L = xpu->L; + if (getHandle(xpu, NotStandaloneKey) == 0) return 1; /* no handle */ + docall(xpu, 0, 1); + status = lua_toboolean(L, -1); + lua_pop(L, 1); + return status; +} + + +static void f_ProcessingInstruction (void *ud, const char *target, + const char *data) { + lxp_userdata *xpu = (lxp_userdata *)ud; + lua_State *L = xpu->L; + if (getHandle(xpu, ProcessingInstructionKey) == 0) return; /* no handle */ + lua_pushstring(L, target); + lua_pushstring(L, data); + docall(xpu, 2, 0); +} + + +static void f_UnparsedEntityDecl (void *ud, const char *entityName, + const char *base, + const char *systemId, + const char *publicId, + const char *notationName) { + lxp_userdata *xpu = (lxp_userdata *)ud; + lua_State *L = xpu->L; + if (getHandle(xpu, UnparsedEntityDeclKey) == 0) return; /* no handle */ + lua_pushstring(L, entityName); + lua_pushstring(L, base); + lua_pushstring(L, systemId); + lua_pushstring(L, publicId); + lua_pushstring(L, notationName); + docall(xpu, 5, 0); +} + +static void f_StartDoctypeDecl (void *ud, const XML_Char *doctypeName, + const XML_Char *sysid, + const XML_Char *pubid, + int has_internal_subset) { + lxp_userdata *xpu = (lxp_userdata *)ud; + if (getHandle(xpu, StartDoctypeDeclKey) == 0) return; /* no handle */ + lua_pushstring(xpu->L, doctypeName); + lua_pushstring(xpu->L, sysid); + lua_pushstring(xpu->L, pubid); + lua_pushboolean(xpu->L, has_internal_subset); + docall(xpu, 4, 0); +} + +/* }====================================================== */ + + + +static int hasfield (lua_State *L, const char *fname) { + int res; + lua_pushstring(L, fname); + lua_gettable(L, 1); + res = !lua_isnil(L, -1); + lua_pop(L, 1); + return res; +} + + +static void checkcallbacks (lua_State *L) { + static const char *const validkeys[] = { + "StartCdataSection", "EndCdataSection", "CharacterData", "Comment", + "Default", "DefaultExpand", "StartElement", "EndElement", + "ExternalEntityRef", "StartNamespaceDecl", "EndNamespaceDecl", + "NotationDecl", "NotStandalone", "ProcessingInstruction", + "UnparsedEntityDecl", "StartDoctypeDecl", NULL}; + if (hasfield(L, "_nonstrict")) return; + lua_pushnil(L); + while (lua_next(L, 1)) { + lua_pop(L, 1); /* remove value */ +#if ! defined (LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 + if (lua_type(L, -1) != LUA_TSTRING || + luaL_findstring(lua_tostring(L, -1), validkeys) < 0) + luaL_error(L, "invalid key `%s' in callback table", lua_tostring(L, -1)); +#else + luaL_checkoption(L, -1, NULL, validkeys); +#endif + } +} + + +static int lxp_make_parser (lua_State *L) { + XML_Parser p; + char sep = *luaL_optstring(L, 2, ""); + lxp_userdata *xpu = createlxp(L); + p = xpu->parser = (sep == '\0') ? XML_ParserCreate(NULL) : + XML_ParserCreateNS(NULL, sep); + if (!p) + luaL_error(L, "XML_ParserCreate failed"); + luaL_checktype(L, 1, LUA_TTABLE); + checkcallbacks(L); + lua_pushvalue(L, 1); + xpu->tableref = luaL_ref(L, LUA_REGISTRYINDEX); + XML_SetUserData(p, xpu); + if (hasfield(L, StartCdataKey) || hasfield(L, EndCdataKey)) + XML_SetCdataSectionHandler(p, f_StartCdata, f_EndCdataKey); + if (hasfield(L, CharDataKey)) + XML_SetCharacterDataHandler(p, f_CharData); + if (hasfield(L, CommentKey)) + XML_SetCommentHandler(p, f_Comment); + if (hasfield(L, DefaultKey)) + XML_SetDefaultHandler(p, f_Default); + if (hasfield(L, DefaultExpandKey)) + XML_SetDefaultHandlerExpand(p, f_DefaultExpand); + if (hasfield(L, StartElementKey) || hasfield(L, EndElementKey)) + XML_SetElementHandler(p, f_StartElement, f_EndElement); + if (hasfield(L, ExternalEntityKey)) + XML_SetExternalEntityRefHandler(p, f_ExternaEntity); + if (hasfield(L, StartNamespaceDeclKey) || hasfield(L, EndNamespaceDeclKey)) + XML_SetNamespaceDeclHandler(p, f_StartNamespaceDecl, f_EndNamespaceDecl); + if (hasfield(L, NotationDeclKey)) + XML_SetNotationDeclHandler(p, f_NotationDecl); + if (hasfield(L, NotStandaloneKey)) + XML_SetNotStandaloneHandler(p, f_NotStandalone); + if (hasfield(L, ProcessingInstructionKey)) + XML_SetProcessingInstructionHandler(p, f_ProcessingInstruction); + if (hasfield(L, UnparsedEntityDeclKey)) + XML_SetUnparsedEntityDeclHandler(p, f_UnparsedEntityDecl); + if (hasfield(L, StartDoctypeDeclKey)) + XML_SetStartDoctypeDeclHandler(p, f_StartDoctypeDecl); + return 1; +} + + +static lxp_userdata *checkparser (lua_State *L, int idx) { + lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, idx, ParserType); + luaL_argcheck(L, xpu, idx, "expat parser expected"); + luaL_argcheck(L, xpu->parser, idx, "parser is closed"); + return xpu; +} + + +static int parser_gc (lua_State *L) { + lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, 1, ParserType); + luaL_argcheck(L, xpu, 1, "expat parser expected"); + lxpclose(L, xpu); + return 0; +} + + +static int setbase (lua_State *L) { + lxp_userdata *xpu = checkparser(L, 1); + if (XML_SetBase(xpu->parser, luaL_checkstring(L, 2)) == 0) + luaL_error(L, "no memory to store base"); + return 0; +} + + +static int getbase (lua_State *L) { + lxp_userdata *xpu = checkparser(L, 1); + lua_pushstring(L, XML_GetBase(xpu->parser)); + return 1; +} + + +static int getcallbacks (lua_State *L) { + lxp_userdata *xpu = checkparser(L, 1); + lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); + return 1; +} + + +static int parse_aux (lua_State *L, lxp_userdata *xpu, const char *s, + size_t len) { + luaL_Buffer b; + int status; + xpu->L = L; + xpu->state = XPSok; + xpu->b = &b; + lua_settop(L, 2); + lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /*lua_getref(L, xpu->tableref);*/ /* to be used by handlers */ + status = XML_Parse(xpu->parser, s, (int)len, s == NULL); + if (xpu->state == XPSstring) dischargestring(xpu); + if (xpu->state == XPSerror) { /* callback error? */ + lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /* get original msg. */ + lua_error(L); + } + if (s == NULL) xpu->state = XPSfinished; + if (status) { + lua_pushboolean(L, 1); + return 1; + } + else { /* error */ + return reporterror(xpu); + } +} + + +static int lxp_parse (lua_State *L) { + lxp_userdata *xpu = checkparser(L, 1); + size_t len; + const char *s = luaL_optlstring(L, 2, NULL, &len); + if (xpu->state == XPSfinished && s != NULL) { + lua_pushnil(L); + lua_pushliteral(L, "cannot parse - document is finished"); + return 2; + } + return parse_aux(L, xpu, s, len); +} + + +static int lxp_close (lua_State *L) { + int status = 1; + lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, 1, ParserType); + luaL_argcheck(L, xpu, 1, "expat parser expected"); + if (xpu->state != XPSfinished) + status = parse_aux(L, xpu, NULL, 0); + lxpclose(L, xpu); + if (status > 1) luaL_error(L, "error closing parser: %s", + lua_tostring(L, -status+1)); + return 0; +} + + +static int lxp_pos (lua_State *L) { + lxp_userdata *xpu = checkparser(L, 1); + XML_Parser p = xpu->parser; + lua_pushnumber(L, XML_GetCurrentLineNumber(p)); + lua_pushnumber(L, XML_GetCurrentColumnNumber(p) + 1); + lua_pushnumber(L, XML_GetCurrentByteIndex(p) + 1); + return 3; +} + + +static int lxp_setencoding (lua_State *L) { + lxp_userdata *xpu = checkparser(L, 1); + const char *encoding = luaL_checkstring(L, 2); + luaL_argcheck(L, xpu->state == XPSpre, 1, "invalid parser state"); + XML_SetEncoding(xpu->parser, encoding); + return 0; +} + +static int lxp_stop (lua_State *L) { + lxp_userdata *xpu = checkparser(L, 1); + lua_pushboolean(L, XML_StopParser(xpu->parser, XML_FALSE) == XML_STATUS_OK); + return 1; +} + +#if !defined LUA_VERSION_NUM +/* Lua 5.0 */ +#define luaL_Reg luaL_reg +#endif + +static const struct luaL_Reg lxp_meths[] = { + {"parse", lxp_parse}, + {"close", lxp_close}, + {"__gc", parser_gc}, + {"pos", lxp_pos}, + {"setencoding", lxp_setencoding}, + {"getcallbacks", getcallbacks}, + {"getbase", getbase}, + {"setbase", setbase}, + {"stop", lxp_stop}, + {NULL, NULL} +}; + +static const struct luaL_Reg lxp_funcs[] = { + {"new", lxp_make_parser}, + {NULL, NULL} +}; + + +/* +** Assumes the table is on top of the stack. +*/ +static void set_info (lua_State *L) { + lua_pushliteral (L, "_COPYRIGHT"); + lua_pushliteral (L, "Copyright (C) 2003-2012 Kepler Project"); + lua_settable (L, -3); + lua_pushliteral (L, "_DESCRIPTION"); + lua_pushliteral (L, "LuaExpat is a SAX XML parser based on the Expat library"); + lua_settable (L, -3); + lua_pushliteral (L, "_VERSION"); + lua_pushliteral (L, "LuaExpat 1.3.0"); + lua_settable (L, -3); +} + + +#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 +/* +** Adapted from Lua 5.2.0 +*/ +static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushstring(L, l->name); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_settable(L, -(nup + 3)); + } + lua_pop(L, nup); /* remove upvalues */ +} +#endif + + +int luaopen_lxp (lua_State *L) { + luaL_newmetatable(L, ParserType); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -2); + lua_rawset(L, -3); + + luaL_setfuncs (L, lxp_meths, 0); + lua_pop (L, 1); /* remove metatable */ + + // _X 2013_04_09: Modified to allow embedding + luaL_openlib (L, "lxp", lxp_funcs, 0); + /* + lua_newtable (L); + luaL_setfuncs (L, lxp_funcs, 0); + */ + set_info (L); + return 1; +} diff --git a/src/LuaExpat/lxplib.h b/src/LuaExpat/lxplib.h new file mode 100644 index 000000000..9c0be4f78 --- /dev/null +++ b/src/LuaExpat/lxplib.h @@ -0,0 +1,24 @@ +/* +** See Copyright Notice in license.html +*/ + +#define ParserType "Expat" + +#define StartCdataKey "StartCdataSection" +#define EndCdataKey "EndCdataSection" +#define CharDataKey "CharacterData" +#define CommentKey "Comment" +#define DefaultKey "Default" +#define DefaultExpandKey "DefaultExpand" +#define StartElementKey "StartElement" +#define EndElementKey "EndElement" +#define ExternalEntityKey "ExternalEntityRef" +#define StartNamespaceDeclKey "StartNamespaceDecl" +#define EndNamespaceDeclKey "EndNamespaceDecl" +#define NotationDeclKey "NotationDecl" +#define NotStandaloneKey "NotStandalone" +#define ProcessingInstructionKey "ProcessingInstruction" +#define UnparsedEntityDeclKey "UnparsedEntityDecl" +#define StartDoctypeDeclKey "StartDoctypeDecl" + +int luaopen_lxp (lua_State *L); diff --git a/src/LuaFunctions.h b/src/LuaFunctions.h new file mode 100644 index 000000000..0ad420881 --- /dev/null +++ b/src/LuaFunctions.h @@ -0,0 +1,17 @@ +#pragma once + +#include "MCLogger.h" +#include +// tolua_begin + +unsigned int GetTime() +{ + return (unsigned int)time(0); +} + +std::string GetChar( std::string & a_Str, unsigned int a_Idx ) +{ + return std::string(1, a_Str[ a_Idx ]); +} + +// tolua_end diff --git a/src/LuaState.cpp b/src/LuaState.cpp new file mode 100644 index 000000000..8d2fa8eca --- /dev/null +++ b/src/LuaState.cpp @@ -0,0 +1,958 @@ + +// LuaState.cpp + +// Implements the cLuaState class representing the wrapper over lua_State *, provides associated helper functions + +#include "Globals.h" +#include "LuaState.h" + +extern "C" +{ + #include "lualib.h" +} + +#include "tolua++.h" +#include "Bindings.h" +#include "ManualBindings.h" + +// fwd: SQLite/lsqlite3.c +extern "C" +{ + LUALIB_API int luaopen_lsqlite3(lua_State * L); +} + +// fwd: LuaExpat/lxplib.c: +extern "C" +{ + int luaopen_lxp(lua_State * L); +} + + + + + + +const cLuaState::cRet cLuaState::Return = {}; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLuaState: + +cLuaState::cLuaState(const AString & a_SubsystemName) : + m_LuaState(NULL), + m_IsOwned(false), + m_SubsystemName(a_SubsystemName), + m_NumCurrentFunctionArgs(-1) +{ +} + + + + + +cLuaState::cLuaState(lua_State * a_AttachState) : + m_LuaState(a_AttachState), + m_IsOwned(false), + m_SubsystemName(""), + m_NumCurrentFunctionArgs(-1) +{ +} + + + + + +cLuaState::~cLuaState() +{ + if (IsValid()) + { + if (m_IsOwned) + { + Close(); + } + else + { + Detach(); + } + } +} + + + + + +void cLuaState::Create(void) +{ + if (m_LuaState != NULL) + { + LOGWARNING("%s: Trying to create an already-existing LuaState, ignoring.", __FUNCTION__); + return; + } + m_LuaState = lua_open(); + luaL_openlibs(m_LuaState); + tolua_AllToLua_open(m_LuaState); + ManualBindings::Bind(m_LuaState); + luaopen_lsqlite3(m_LuaState); + luaopen_lxp(m_LuaState); + m_IsOwned = true; +} + + + + + +void cLuaState::Close(void) +{ + if (m_LuaState == NULL) + { + LOGWARNING("%s: Trying to close an invalid LuaState, ignoring.", __FUNCTION__); + return; + } + if (!m_IsOwned) + { + LOGWARNING( + "%s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead.", + __FUNCTION__, m_LuaState + ); + Detach(); + return; + } + lua_close(m_LuaState); + m_LuaState = NULL; + m_IsOwned = false; +} + + + + + +void cLuaState::Attach(lua_State * a_State) +{ + if (m_LuaState != NULL) + { + LOGINFO("%s: Already contains a LuaState (0x%p), will be closed / detached.", __FUNCTION__, m_LuaState); + if (m_IsOwned) + { + Close(); + } + else + { + Detach(); + } + } + m_LuaState = a_State; + m_IsOwned = false; +} + + + + + +void cLuaState::Detach(void) +{ + if (m_LuaState == NULL) + { + return; + } + if (m_IsOwned) + { + LOGWARNING( + "%s: Detected a mis-use, calling Detach() when the state is owned. Closing the owned state (0x%p).", + __FUNCTION__, m_LuaState + ); + Close(); + return; + } + m_LuaState = NULL; +} + + + + + +bool cLuaState::LoadFile(const AString & a_FileName) +{ + ASSERT(IsValid()); + + // Load the file: + int s = luaL_loadfile(m_LuaState, a_FileName.c_str()); + if (ReportErrors(s)) + { + LOGWARNING("Can't load %s because of an error in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); + return false; + } + + // Execute the globals: + s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0); + if (ReportErrors(s)) + { + LOGWARNING("Error in %s in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); + return false; + } + + return true; +} + + + + + +bool cLuaState::HasFunction(const char * a_FunctionName) +{ + if (!IsValid()) + { + // This happens if cPlugin::Initialize() fails with an error + return false; + } + + lua_getglobal(m_LuaState, a_FunctionName); + bool res = (!lua_isnil(m_LuaState, -1) && lua_isfunction(m_LuaState, -1)); + lua_pop(m_LuaState, 1); + return res; +} + + + + + +bool cLuaState::PushFunction(const char * a_FunctionName) +{ + ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack + + if (!IsValid()) + { + // This happens if cPlugin::Initialize() fails with an error + return false; + } + + lua_getglobal(m_LuaState, a_FunctionName); + if (!lua_isfunction(m_LuaState, -1)) + { + LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName); + lua_pop(m_LuaState, 1); + return false; + } + m_CurrentFunctionName.assign(a_FunctionName); + m_NumCurrentFunctionArgs = 0; + return true; +} + + + + + +bool cLuaState::PushFunction(int a_FnRef) +{ + ASSERT(IsValid()); + ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack + + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref() + if (!lua_isfunction(m_LuaState, -1)) + { + lua_pop(m_LuaState, 1); + return false; + } + m_CurrentFunctionName = ""; + m_NumCurrentFunctionArgs = 0; + return true; +} + + + + + +bool cLuaState::PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName) +{ + ASSERT(IsValid()); + ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack + + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef); // Get the table ref + if (!lua_istable(m_LuaState, -1)) + { + // Not a table, bail out + lua_pop(m_LuaState, 1); + return false; + } + lua_getfield(m_LuaState, -1, a_FnName); + if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1)) + { + // Not a valid function, bail out + lua_pop(m_LuaState, 2); + return false; + } + lua_remove(m_LuaState, -2); // Remove the table ref from the stack + m_CurrentFunctionName = ""; + m_NumCurrentFunctionArgs = 0; + return true; +} + + + + + +void cLuaState::Push(const AString & a_String) +{ + ASSERT(IsValid()); + + tolua_pushcppstring(m_LuaState, a_String); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const AStringVector & a_Vector) +{ + ASSERT(IsValid()); + + lua_createtable(m_LuaState, a_Vector.size(), 0); + int newTable = lua_gettop(m_LuaState); + int index = 1; + for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index) + { + tolua_pushstring(m_LuaState, itr->c_str()); + lua_rawseti(m_LuaState, newTable, index); + } + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::PushUserType(void * a_Object, const char * a_Type) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Object, a_Type); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(int a_Value) +{ + ASSERT(IsValid()); + + tolua_pushnumber(m_LuaState, a_Value); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(double a_Value) +{ + ASSERT(IsValid()); + + tolua_pushnumber(m_LuaState, a_Value); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const char * a_Value) +{ + ASSERT(IsValid()); + + tolua_pushstring(m_LuaState, a_Value); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(bool a_Value) +{ + ASSERT(IsValid()); + + tolua_pushboolean(m_LuaState, a_Value ? 1 : 0); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cWorld * a_World) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_World, "cWorld"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cPlayer * a_Player) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const cPlayer * a_Player) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cEntity * a_Entity) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Entity, "cEntity"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cMonster * a_Monster) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Monster, "cMonster"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cItem * a_Item) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Item, "cItem"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cItems * a_Items) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Items, "cItems"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cClientHandle * a_Client) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Client, "cClientHandle"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cPickup * a_Pickup) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Pickup, "cPickup"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cChunkDesc * a_ChunkDesc) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const cCraftingGrid * a_Grid) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const cCraftingRecipe * a_Recipe) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(TakeDamageInfo * a_TDI) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cWindow * a_Window) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Window, "cWindow"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cPluginLua * a_Plugin) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Plugin, "cPluginLua"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const HTTPRequest * a_Request) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPRequest"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cWebAdmin * a_WebAdmin) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const HTTPTemplateRequest * a_Request) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPTemplateRequest"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cTNTEntity * a_TNTEntity) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_TNTEntity, "cTNTEntity"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cCreeper * a_Creeper) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Creeper, "cCreeper"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(Vector3i * a_Vector) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Vector, "Vector3i"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(void * a_Ptr) +{ + ASSERT(IsValid()); + + lua_pushnil(m_LuaState); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cHopperEntity * a_Hopper) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cBlockEntity * a_BlockEntity) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_BlockEntity, "cBlockEntity"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::GetReturn(int a_StackPos, bool & a_ReturnedVal) +{ + a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0); +} + + + + + +void cLuaState::GetReturn(int a_StackPos, AString & a_ReturnedVal) +{ + if (lua_isstring(m_LuaState, a_StackPos)) + { + a_ReturnedVal = tolua_tocppstring(m_LuaState, a_StackPos, a_ReturnedVal.c_str()); + } +} + + + + + +void cLuaState::GetReturn(int a_StackPos, int & a_ReturnedVal) +{ + if (lua_isnumber(m_LuaState, a_StackPos)) + { + a_ReturnedVal = (int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); + } +} + + + + + +void cLuaState::GetReturn(int a_StackPos, double & a_ReturnedVal) +{ + if (lua_isnumber(m_LuaState, a_StackPos)) + { + a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); + } +} + + + + + +bool cLuaState::CallFunction(int a_NumResults) +{ + ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first + ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); + + int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, 0); + if (ReportErrors(s)) + { + LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), m_CurrentFunctionName.c_str()); + m_NumCurrentFunctionArgs = -1; + m_CurrentFunctionName.clear(); + return false; + } + m_NumCurrentFunctionArgs = -1; + m_CurrentFunctionName.clear(); + return true; +} + + + + + +bool cLuaState::CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_isusertype(m_LuaState, i, a_UserType, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_istable(m_LuaState, i, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamNumber(int a_StartParam, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_isnumber(m_LuaState, i, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamEnd(int a_Param) +{ + tolua_Error tolua_err; + if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err)) + { + return true; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s': Too many arguments.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; +} + + + + + +bool cLuaState::ReportErrors(int a_Status) +{ + return ReportErrors(m_LuaState, a_Status); +} + + + + + +bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status) +{ + if (a_Status == 0) + { + // No error to report + return false; + } + + LOGWARNING("LUA: %d - %s", a_Status, lua_tostring(a_LuaState, -1)); + lua_pop(a_LuaState, 1); + return true; +} + + + + + +void cLuaState::LogStackTrace(void) +{ + LOGWARNING("Stack trace:"); + lua_Debug entry; + int depth = 0; + while (lua_getstack(m_LuaState, depth, &entry)) + { + int status = lua_getinfo(m_LuaState, "Sln", &entry); + assert(status); + + LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?"); + depth++; + } + LOGWARNING("Stack trace end"); +} + + + + + +AString cLuaState::GetTypeText(int a_StackPos) +{ + int Type = lua_type(m_LuaState, a_StackPos); + switch (Type) + { + case LUA_TNONE: return "TNONE"; + case LUA_TNIL: return "TNIL"; + case LUA_TBOOLEAN: return "TBOOLEAN"; + case LUA_TLIGHTUSERDATA: return "TLIGHTUSERDATA"; + case LUA_TNUMBER: return "TNUMBER"; + case LUA_TSTRING: return "TSTRING"; + case LUA_TTABLE: return "TTABLE"; + case LUA_TFUNCTION: return "TFUNCTION"; + case LUA_TUSERDATA: return "TUSERDATA"; + case LUA_TTHREAD: return "TTHREAD"; + } + return Printf("Unknown (%d)", Type); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLuaState::cRef: + +cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) : + m_LuaState(a_LuaState) +{ + ASSERT(m_LuaState.IsValid()); + + lua_pushvalue(m_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack + m_Ref = luaL_ref(m_LuaState, LUA_REGISTRYINDEX); +} + + + + + +cLuaState::cRef::~cRef() +{ + ASSERT(m_LuaState.IsValid()); + + if (IsValid()) + { + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref); + } +} + + + + diff --git a/src/LuaState.h b/src/LuaState.h new file mode 100644 index 000000000..caba2484d --- /dev/null +++ b/src/LuaState.h @@ -0,0 +1,811 @@ + +// LuaState.h + +// Declares the cLuaState class representing the wrapper over lua_State *, provides associated helper functions + +/* +The contained lua_State can be either owned or attached. +Owned lua_State is created by calling Create() and the cLuaState automatically closes the state +Or, lua_State can be attached by calling Attach(), the cLuaState doesn't close such a state +Attaching a state will automatically close an owned state. + +Calling a Lua function is done by pushing the function, either by PushFunction() or PushFunctionFromRegistry(), +then pushing the arguments (PushString(), PushNumber(), PushUserData() etc.) and finally +executing CallFunction(). cLuaState automatically keeps track of the number of arguments and the name of the +function (for logging purposes), which makes the call less error-prone. + +Reference management is provided by the cLuaState::cRef class. This is used when you need to hold a reference to +any Lua object across several function calls; usually this is used for callbacks. The class is RAII-like, with +automatic resource management. +*/ + + + + +#pragma once + +extern "C" +{ + #include "lauxlib.h" +} + + + + + +class cWorld; +class cPlayer; +class cEntity; +class cMonster; +class cItem; +class cItems; +class cClientHandle; +class cPickup; +class cChunkDesc; +class cCraftingGrid; +class cCraftingRecipe; +struct TakeDamageInfo; +class cWindow; +class cPluginLua; +struct HTTPRequest; +class cWebAdmin; +struct HTTPTemplateRequest; +class cTNTEntity; +class cCreeper; +class Vector3i; +class cHopperEntity; +class cBlockEntity; + + + + + +/// Encapsulates a Lua state and provides some syntactic sugar for common operations +class cLuaState +{ +public: + + /// Used for storing references to object in the global registry + class cRef + { + public: + /// Creates a reference in the specified LuaState for object at the specified StackPos + cRef(cLuaState & a_LuaState, int a_StackPos); + ~cRef(); + + /// Returns true if the reference is valid + bool IsValid(void) const {return (m_Ref != LUA_REFNIL); } + + /// Allows to use this class wherever an int (i. e. ref) is to be used + operator int(void) const { return m_Ref; } + + protected: + cLuaState & m_LuaState; + int m_Ref; + } ; + + + /// A dummy class that's used only to delimit function args from return values for cLuaState::Call() + class cRet + { + } ; + + static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call() + + + /** Creates a new instance. The LuaState is not initialized. + a_SubsystemName is used for reporting problems in the console, it is "plugin %s" for plugins, + or "LuaScript" for the cLuaScript template + */ + cLuaState(const AString & a_SubsystemName); + + /** Creates a new instance. The a_AttachState is attached. + Subsystem name is set to "". + */ + explicit cLuaState(lua_State * a_AttachState); + + ~cLuaState(); + + /// Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions + operator lua_State * (void) { return m_LuaState; } + + /// Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor + void Create(void); + + /// Closes the m_LuaState, if not closed already + void Close(void); + + /// Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor + void Attach(lua_State * a_State); + + /// Detaches a previously attached state. + void Detach(void); + + /// Returns true if the m_LuaState is valid + bool IsValid(void) const { return (m_LuaState != NULL); } + + /** Loads the specified file + Returns false and logs a warning to the console if not successful (but the LuaState is kept open). + m_SubsystemName is displayed in the warning log message. + */ + bool LoadFile(const AString & a_FileName); + + /// Returns true if a_FunctionName is a valid Lua function that can be called + bool HasFunction(const char * a_FunctionName); + + /** Pushes the function of the specified name onto the stack. + Returns true if successful. Logs a warning on failure (incl. m_SubsystemName) + */ + bool PushFunction(const char * a_FunctionName); + + /** Pushes a function that has been saved into the global registry, identified by a_FnRef. + Returns true if successful. Logs a warning on failure + */ + bool PushFunction(int a_FnRef); + + /** Pushes a function that is stored in a table ref. + Returns true if successful, false on failure. Doesn't log failure. + */ + bool PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName); + + /// Pushes a usertype of the specified class type onto the stack + void PushUserType(void * a_Object, const char * a_Type); + + // Push a value onto the stack + void Push(const AString & a_String); + void Push(const AStringVector & a_Vector); + void Push(int a_Value); + void Push(double a_Value); + void Push(const char * a_Value); + void Push(bool a_Value); + void Push(cWorld * a_World); + void Push(cPlayer * a_Player); + void Push(const cPlayer * a_Player); + void Push(cEntity * a_Entity); + void Push(cMonster * a_Monster); + void Push(cItem * a_Item); + void Push(cItems * a_Items); + void Push(cClientHandle * a_ClientHandle); + void Push(cPickup * a_Pickup); + void Push(cChunkDesc * a_ChunkDesc); + void Push(const cCraftingGrid * a_Grid); + void Push(const cCraftingRecipe * a_Recipe); + void Push(TakeDamageInfo * a_TDI); + void Push(cWindow * a_Window); + void Push(cPluginLua * a_Plugin); + void Push(const HTTPRequest * a_Request); + void Push(cWebAdmin * a_WebAdmin); + void Push(const HTTPTemplateRequest * a_Request); + void Push(cTNTEntity * a_TNTEntity); + void Push(cCreeper * a_Creeper); + void Push(Vector3i * a_Vector); + void Push(void * a_Ptr); + void Push(cHopperEntity * a_Hopper); + void Push(cBlockEntity * a_BlockEntity); + + /// Call any 0-param 0-return Lua function in a single line: + template + bool Call(FnT a_FnName) + { + if (!PushFunction(a_FnName)) + { + return false; + } + return CallFunction(0); + } + + /// Call any 1-param 0-return Lua function in a single line: + template< + typename FnT, + typename ArgT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + return CallFunction(0); + } + + /// Call any 2-param 0-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + return CallFunction(0); + } + + /// Call any 1-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 2-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 3-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 4-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 5-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 6-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 7-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename ArgT7, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 8-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename ArgT7, typename ArgT8, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 9-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename ArgT7, typename ArgT8, typename ArgT9, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + Push(a_Arg9); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 10-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename ArgT7, typename ArgT8, typename ArgT9, typename ArgT10, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, ArgT10 a_Arg10, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + Push(a_Arg9); + Push(a_Arg10); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 1-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 2-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 3-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 4-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 5-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 6-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 7-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, typename ArgT7, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 7-param 3-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, typename ArgT7, + typename RetT1, typename RetT2, typename RetT3 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + if (!CallFunction(3)) + { + return false; + } + GetReturn(-3, a_Ret1); + GetReturn(-2, a_Ret2); + GetReturn(-1, a_Ret3); + lua_pop(m_LuaState, 3); + return true; + } + + /// Call any 8-param 3-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, typename ArgT7, typename ArgT8, + typename RetT1, typename RetT2, typename RetT3 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + if (!CallFunction(3)) + { + return false; + } + GetReturn(-3, a_Ret1); + GetReturn(-2, a_Ret2); + GetReturn(-1, a_Ret3); + lua_pop(m_LuaState, 3); + return true; + } + + /// Call any 9-param 5-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9, + typename RetT1, typename RetT2, typename RetT3, typename RetT4, typename RetT5 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3, RetT4 & a_Ret4, RetT5 & a_Ret5) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + Push(a_Arg9); + if (!CallFunction(5)) + { + return false; + } + GetReturn(-5, a_Ret1); + GetReturn(-4, a_Ret2); + GetReturn(-3, a_Ret3); + GetReturn(-2, a_Ret4); + GetReturn(-1, a_Ret5); + lua_pop(m_LuaState, 5); + return true; + } + + + /// Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, bool & a_ReturnedVal); + + /// Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, AString & a_ReturnedVal); + + /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, int & a_ReturnedVal); + + /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, double & a_ReturnedVal); + + /** + Calls the function that has been pushed onto the stack by PushFunction(), + with arguments pushed by PushXXX(). + Returns true if successful, logs a warning on failure. + */ + bool CallFunction(int a_NumReturnValues); + + /// Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not + bool CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam = -1); + + /// Returns true if the specified parameters on the stack are a table; also logs warning if not + bool CheckParamTable(int a_StartParam, int a_EndParam = -1); + + /// Returns true if the specified parameters on the stack are a number; also logs warning if not + bool CheckParamNumber(int a_StartParam, int a_EndParam = -1); + + /// Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) + bool CheckParamEnd(int a_Param); + + /// If the status is nonzero, prints the text on the top of Lua stack and returns true + bool ReportErrors(int status); + + /// If the status is nonzero, prints the text on the top of Lua stack and returns true + static bool ReportErrors(lua_State * a_LuaState, int status); + + /// Logs all items in the current stack trace to the server console + void LogStackTrace(void); + + /// Returns the type of the item on the specified position in the stack + AString GetTypeText(int a_StackPos); + +protected: + lua_State * m_LuaState; + + /// If true, the state is owned by this object and will be auto-Closed. False => attached state + bool m_IsOwned; + + /** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript" + whatever is given to the constructor + */ + AString m_SubsystemName; + + /// Name of the currently pushed function (for the Push / Call chain) + AString m_CurrentFunctionName; + + /// Number of arguments currently pushed (for the Push / Call chain) + int m_NumCurrentFunctionArgs; +} ; + + + + diff --git a/src/LuaWindow.cpp b/src/LuaWindow.cpp new file mode 100644 index 000000000..9011d668c --- /dev/null +++ b/src/LuaWindow.cpp @@ -0,0 +1,185 @@ + +// LuaWindow.cpp + +// Implements the cLuaWindow class representing a virtual window that plugins may create and open for the player + +#include "Globals.h" +#include "LuaWindow.h" +#include "UI/SlotArea.h" +#include "PluginLua.h" +#include "Entities/Player.h" +#include "lauxlib.h" // Needed for LUA_REFNIL + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLuaWindow: + +cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : + super(a_WindowType, a_Title), + m_Contents(a_SlotsX, a_SlotsY), + m_Plugin(NULL), + m_LuaRef(LUA_REFNIL), + m_OnClosingFnRef(LUA_REFNIL), + m_OnSlotChangedFnRef(LUA_REFNIL) +{ + m_Contents.AddListener(*this); + m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this)); + + // If appropriate, add an Armor slot area: + switch (a_WindowType) + { + case cWindow::wtInventory: + case cWindow::wtWorkbench: + { + m_SlotAreas.push_back(new cSlotAreaArmor(*this)); + break; + } + } + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +cLuaWindow::~cLuaWindow() +{ + m_Contents.RemoveListener(*this); + + // Must delete slot areas now, because they are referencing this->m_Contents and would try to access it in cWindow's + // destructor, when the member is already gone. + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + delete *itr; + } + m_SlotAreas.clear(); + + ASSERT(m_OpenedBy.empty()); +} + + + + + +void cLuaWindow::SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + ASSERT(m_LuaRef == LUA_REFNIL); + m_Plugin = a_Plugin; + m_LuaRef = a_LuaRef; +} + + + + + +bool cLuaWindow::IsLuaReferenced(void) const +{ + return ((m_Plugin != NULL) && (m_LuaRef != LUA_REFNIL)); +} + + + + + +void cLuaWindow::SetOnClosing(cPluginLua * a_Plugin, int a_FnRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + + // If there already was a function, unreference it first + if (m_OnClosingFnRef != LUA_REFNIL) + { + m_Plugin->Unreference(m_OnClosingFnRef); + } + + // Store the new reference + m_Plugin = a_Plugin; + m_OnClosingFnRef = a_FnRef; +} + + + + + +void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + + // If there already was a function, unreference it first + if (m_OnSlotChangedFnRef != LUA_REFNIL) + { + m_Plugin->Unreference(m_OnSlotChangedFnRef); + } + + // Store the new reference + m_Plugin = a_Plugin; + m_OnSlotChangedFnRef = a_FnRef; +} + + + + + +bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) +{ + // First notify the plugin through the registered callback: + if (m_OnClosingFnRef != LUA_REFNIL) + { + ASSERT(m_Plugin != NULL); + if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse)) + { + // The callback disagrees (the higher levels check the CanRefuse flag compliance) + return false; + } + } + + return super::ClosedByPlayer(a_Player, a_CanRefuse); +} + + + + + +void cLuaWindow::Destroy(void) +{ + super::Destroy(); + + if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != NULL)) + { + // The object is referenced by Lua, un-reference it + m_Plugin->Unreference(m_LuaRef); + } + + // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it! + m_IsDestroyed = false; +} + + + + + +void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + if (a_ItemGrid != &m_Contents) + { + ASSERT(!"Invalid ItemGrid in callback"); + return; + } + + // If an OnSlotChanged callback has been registered, call it: + if (m_OnSlotChangedFnRef != LUA_REFNIL) + { + m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum); + } +} + + + + diff --git a/src/LuaWindow.h b/src/LuaWindow.h new file mode 100644 index 000000000..4c32c263e --- /dev/null +++ b/src/LuaWindow.h @@ -0,0 +1,95 @@ + +// LuaWindow.h + +// Declares the cLuaWindow class representing a virtual window that plugins may create and open for the player + + + + + +#pragma once + +#include "UI/Window.h" +#include "ItemGrid.h" + + + + + +// fwd: PluginLua.h +class cPluginLua; + + + + + +/** A window that has been created by a Lua plugin and is handled entirely by that plugin +This object needs extra care with its lifetime management: +- It is created by Lua, so Lua expects to garbage-collect it later +- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them +To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() +delete the window, but rather leaves it dangling, with only Lua having the reference to it. +Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for +cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object. +This reference needs to be unreferenced in the Destroy() function. +*/ +class cLuaWindow : // tolua_export + public cItemGrid::cListener, + // tolua_begin + public cWindow +{ + typedef cWindow super; + +public: + /// Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size + cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); + + virtual ~cLuaWindow(); + + /// Returns the internal representation of the contents that are manipulated by Lua + cItemGrid & GetContents(void) { return m_Contents; } + + // tolua_end + + /** Sets the plugin reference and the internal Lua object reference index + used for preventing Lua's GC to collect this class while the window is open + */ + void SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef); + + /// Returns true if SetLuaRef() has been called + bool IsLuaReferenced(void) const; + + /// Sets the callback function (Lua reference) to call when the window is about to close + void SetOnClosing(cPluginLua * a_Plugin, int a_FnRef); + + /// Sets the callback function (Lua reference) to call when a slot is changed + void SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef); + +protected: + /// Contents of the non-inventory part + cItemGrid m_Contents; + + /// The plugin that has opened the window and owns the m_LuaRef + cPluginLua * m_Plugin; + + /// The Lua object reference, used for keeping the object alive as long as any player has the window open + int m_LuaRef; + + /// The Lua reference for the callback to call when the window is closing for any player + int m_OnClosingFnRef; + + /// The Lua reference for the callback to call when a slot has changed + int m_OnSlotChangedFnRef; + + // cWindow overrides: + virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; + virtual void Destroy(void) override; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; +} ; // tolua_export + + + + + diff --git a/src/MCLogger.cpp b/src/MCLogger.cpp new file mode 100644 index 000000000..4f3e5dc0f --- /dev/null +++ b/src/MCLogger.cpp @@ -0,0 +1,261 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include +#include "Log.h" + + + + + +cMCLogger * cMCLogger::s_MCLogger = NULL; +bool g_ShouldColorOutput = false; + +#ifdef _WIN32 + #include // Needed for _isatty(), not available on Linux + + HANDLE g_Console = GetStdHandle(STD_OUTPUT_HANDLE); + WORD g_DefaultConsoleAttrib = 0x07; +#elif defined (__linux) && !defined(ANDROID_NDK) + #include // Needed for isatty() on Linux +#endif + + + + + +cMCLogger * cMCLogger::GetInstance(void) +{ + return s_MCLogger; +} + + + + + +cMCLogger::cMCLogger(void) +{ + AString FileName; + Printf(FileName, "LOG_%d.txt", (int)time(NULL)); + InitLog(FileName); +} + + + + + +cMCLogger::cMCLogger(const AString & a_FileName) +{ + InitLog(a_FileName); +} + + + + + +cMCLogger::~cMCLogger() +{ + m_Log->Log("--- Stopped Log ---\n"); + delete m_Log; + if (this == s_MCLogger) + { + s_MCLogger = NULL; + } +} + + + + + +void cMCLogger::InitLog(const AString & a_FileName) +{ + m_Log = new cLog(a_FileName); + m_Log->Log("--- Started Log ---\n"); + + s_MCLogger = this; + + #ifdef _WIN32 + // See whether we are writing to a console the default console attrib: + g_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0); + if (g_ShouldColorOutput) + { + CONSOLE_SCREEN_BUFFER_INFO sbi; + GetConsoleScreenBufferInfo(g_Console, &sbi); + g_DefaultConsoleAttrib = sbi.wAttributes; + } + #elif defined (__linux) && !defined(ANDROID_NDK) + g_ShouldColorOutput = isatty(fileno(stdout)); + // TODO: Check if the terminal supports colors, somehow? + #endif +} + + + + + +void cMCLogger::LogSimple(const char* a_Text, int a_LogType /* = 0 */ ) +{ + switch( a_LogType ) + { + case 0: + LOG("%s", a_Text); + break; + case 1: + LOGINFO("%s", a_Text); + break; + case 2: + LOGWARN("%s", a_Text); + break; + case 3: + LOGERROR("%s", a_Text); + break; + default: + LOG("(#%d#: %s", a_LogType, a_Text); + break; + } +} + + + + + +void cMCLogger::Log(const char * a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor(csRegular); + m_Log->Log(a_Format, a_ArgList); + ResetColor(); + puts(""); +} + + + + + +void cMCLogger::Info(const char * a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor(csInfo); + m_Log->Log(a_Format, a_ArgList); + ResetColor(); + puts(""); +} + + + + + +void cMCLogger::Warn(const char * a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor(csWarning); + m_Log->Log(a_Format, a_ArgList); + ResetColor(); + puts(""); +} + + + + + +void cMCLogger::Error(const char * a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor(csError); + m_Log->Log(a_Format, a_ArgList); + ResetColor(); + puts(""); +} + + + + + +void cMCLogger::SetColor(eColorScheme a_Scheme) +{ + if (!g_ShouldColorOutput) + { + return; + } + #ifdef _WIN32 + WORD Attrib = 0x07; // by default, gray on black + switch (a_Scheme) + { + case csRegular: Attrib = 0x07; break; // Gray on black + case csInfo: Attrib = 0x0e; break; // Yellow on black + case csWarning: Attrib = 0x0c; break; // Read on black + case csError: Attrib = 0xc0; break; // Black on red + default: ASSERT(!"Unhandled color scheme"); + } + SetConsoleTextAttribute(g_Console, Attrib); + #elif defined(__linux) && !defined(ANDROID_NDK) + switch (a_Scheme) + { + case csRegular: printf("\x1b[0m"); break; // Whatever the console default is + case csInfo: printf("\x1b[33;1m"); break; // Yellow on black + case csWarning: printf("\x1b[31;1m"); break; // Red on black + case csError: printf("\x1b[1;33;41;1m"); break; // Yellow on red + default: ASSERT(!"Unhandled color scheme"); + } + #endif +} + + + + + +void cMCLogger::ResetColor(void) +{ + if (!g_ShouldColorOutput) + { + return; + } + #ifdef _WIN32 + SetConsoleTextAttribute(g_Console, g_DefaultConsoleAttrib); + #elif defined(__linux) && !defined(ANDROID_NDK) + printf("\x1b[0m"); + #endif +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Global functions + +void LOG(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Log( a_Format, argList ); + va_end(argList); +} + +void LOGINFO(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Info( a_Format, argList ); + va_end(argList); +} + +void LOGWARN(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Warn( a_Format, argList ); + va_end(argList); +} + +void LOGERROR(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Error( a_Format, argList ); + va_end(argList); +} + + + + diff --git a/src/MCLogger.h b/src/MCLogger.h new file mode 100644 index 000000000..c949a4cdf --- /dev/null +++ b/src/MCLogger.h @@ -0,0 +1,84 @@ + +#pragma once + + + + +class cLog; + + + + + +class cMCLogger // tolua_export +{ // tolua_export +public: // tolua_export + /// Creates a logger with the default filename, "logs/LOG_.log" + cMCLogger(void); + + /// Creates a logger with the specified filename inside "logs" folder + cMCLogger(const AString & a_FileName); // tolua_export + + ~cMCLogger(); // tolua_export + + void Log(const char* a_Format, va_list a_ArgList); + void Info(const char* a_Format, va_list a_ArgList); + void Warn(const char* a_Format, va_list a_ArgList); + void Error(const char* a_Format, va_list a_ArgList); + + void LogSimple(const char* a_Text, int a_LogType = 0 ); // tolua_export + + static cMCLogger* GetInstance(); +private: + enum eColorScheme + { + csRegular, + csInfo, + csWarning, + csError, + } ; + + cCriticalSection m_CriticalSection; + cLog * m_Log; + static cMCLogger * s_MCLogger; + + + /// Sets the specified color scheme in the terminal (TODO: if coloring available) + void SetColor(eColorScheme a_Scheme); + + /// Resets the color back to whatever is the default in the terminal + void ResetColor(void); + + /// Common initialization for all constructors, creates a logfile with the specified name and assigns s_MCLogger to this + void InitLog(const AString & a_FileName); +}; // tolua_export + + + + + +extern void LOG(const char* a_Format, ...); +extern void LOGINFO(const char* a_Format, ...); +extern void LOGWARN(const char* a_Format, ...); +extern void LOGERROR(const char* a_Format, ...); + + + + + +// In debug builds, translate LOGD to LOG, otherwise leave it out altogether: +#ifdef _DEBUG + #define LOGD LOG +#else + #define LOGD(...) +#endif // _DEBUG + + + + + +#define LOGWARNING LOGWARN + + + + diff --git a/src/ManualBindings.cpp b/src/ManualBindings.cpp new file mode 100644 index 000000000..f98e25880 --- /dev/null +++ b/src/ManualBindings.cpp @@ -0,0 +1,2215 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ManualBindings.h" +#include "tolua++.h" + +#include "Root.h" +#include "World.h" +#include "Plugin.h" +#include "PluginLua.h" +#include "PluginManager.h" +#include "Entities/Player.h" +#include "WebAdmin.h" +#include "ClientHandle.h" +#include "BlockEntities/ChestEntity.h" +#include "BlockEntities/DispenserEntity.h" +#include "BlockEntities/DropperEntity.h" +#include "BlockEntities/FurnaceEntity.h" +#include "BlockEntities/HopperEntity.h" +#include "md5/md5.h" +#include "LuaWindow.h" +#include "LineBlockTracer.h" + + + + + +/**************************** + * Better error reporting for Lua + **/ +int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError) +{ + // Retrieve current function name + lua_Debug entry; + VERIFY(lua_getstack(L, 0, &entry)); + VERIFY(lua_getinfo(L, "n", &entry)); + + // Insert function name into error msg + AString msg(a_pMsg); + ReplaceString(msg, "#funcname#", entry.name?entry.name:"?"); + + // Send the error to Lua + tolua_error(L, msg.c_str(), a_pToLuaError); + return 0; +} + + + + + +int lua_do_error(lua_State* L, const char * a_pFormat, ...) +{ + // Retrieve current function name + lua_Debug entry; + VERIFY(lua_getstack(L, 0, &entry)); + VERIFY(lua_getinfo(L, "n", &entry)); + + // Insert function name into error msg + AString msg(a_pFormat); + ReplaceString(msg, "#funcname#", entry.name?entry.name:"?"); + + // Copied from luaL_error and modified + va_list argp; + va_start(argp, a_pFormat); + luaL_where(L, 1); + lua_pushvfstring(L, msg.c_str(), argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + + + + + +/**************************** + * Lua bound functions with special return types + **/ + +static int tolua_StringSplit(lua_State * tolua_S) +{ + cLuaState LuaState(tolua_S); + std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0); + std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0); + + AStringVector Split = StringSplit(str, delim); + LuaState.Push(Split); + return 1; +} + + + + + +static int tolua_StringSplitAndTrim(lua_State * tolua_S) +{ + cLuaState LuaState(tolua_S); + std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0); + std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0); + + AStringVector Split = StringSplitAndTrim(str, delim); + LuaState.Push(Split); + return 1; +} + + + + + +static int tolua_LOG(lua_State* tolua_S) +{ + const char* str = tolua_tocppstring(tolua_S,1,0); + cMCLogger::GetInstance()->LogSimple( str, 0 ); + return 0; +} + + + + + +static int tolua_LOGINFO(lua_State* tolua_S) +{ + const char* str = tolua_tocppstring(tolua_S,1,0); + cMCLogger::GetInstance()->LogSimple( str, 1 ); + return 0; +} + + + + + +static int tolua_LOGWARN(lua_State* tolua_S) +{ + const char* str = tolua_tocppstring(tolua_S,1,0); + cMCLogger::GetInstance()->LogSimple( str, 2 ); + return 0; +} + + + + + +static int tolua_LOGERROR(lua_State* tolua_S) +{ + const char* str = tolua_tocppstring(tolua_S,1,0); + cMCLogger::GetInstance()->LogSimple( str, 3 ); + return 0; +} + + + + + +cPluginLua * GetLuaPlugin(lua_State * L) +{ + // Get the plugin identification out of LuaState: + lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME); + if (!lua_islightuserdata(L, -1)) + { + LOGWARNING("%s: cannot get plugin instance, what have you done to my Lua state?", __FUNCTION__); + lua_pop(L, 1); + return NULL; + } + cPluginLua * Plugin = (cPluginLua *)lua_topointer(L, -1); + lua_pop(L, 1); + + return Plugin; +} + + + + + +template< + class Ty1, + class Ty2, + bool (Ty1::*Func1)(const AString &, cItemCallback &) + > +static int tolua_DoWith(lua_State* tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if ((NumArgs != 2) && (NumArgs != 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + + const char * ItemName = tolua_tocppstring(tolua_S, 2, ""); + if ((ItemName == NULL) || (ItemName[0] == 0)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1", NumArgs); + } + if (!lua_isfunction( tolua_S, 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 3) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs); + } + + class cLuaCallback : public cItemCallback + { + public: + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + , TableRef( a_TableRef ) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); + if (TableRef != LUA_REFNIL) + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; // Abort enumeration + } + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean(LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + + bool bRetVal = (self->*Func1)(ItemName, Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +template< + class Ty1, + class Ty2, + bool (Ty1::*Func1)(int, cItemCallback &) +> +static int tolua_DoWithID(lua_State* tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if ((NumArgs != 2) && (NumArgs != 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, 0); + + int ItemID = (int)tolua_tonumber(tolua_S, 2, 0); + if (!lua_isfunction(tolua_S, 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 3) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs); + } + + class cLuaCallback : public cItemCallback + { + public: + cLuaCallback(lua_State * a_LuaState, int a_FuncRef, int a_TableRef) : + LuaState(a_LuaState), + FuncRef(a_FuncRef), + TableRef(a_TableRef) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call + tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); // Push the item + if (TableRef != LUA_REFNIL) + { + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; // Abort enumeration + } + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean(LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + + bool bRetVal = (self->*Func1)(ItemID, Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +template< + class Ty1, + class Ty2, + bool (Ty1::*Func1)(int, int, int, cItemCallback &) +> +static int tolua_DoWithXYZ(lua_State* tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if ((NumArgs != 4) && (NumArgs != 5)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 4 or 5 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3) || !lua_isnumber(tolua_S, 4)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3"); + } + + int ItemX = ((int)tolua_tonumber(tolua_S, 2, 0)); + int ItemY = ((int)tolua_tonumber(tolua_S, 3, 0)); + int ItemZ = ((int)tolua_tonumber(tolua_S, 4, 0)); + LOG("x %i y %i z %i", ItemX, ItemY, ItemZ ); + if (!lua_isfunction( tolua_S, 5)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #4"); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 5) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #5"); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #4"); + } + + class cLuaCallback : public cItemCallback + { + public: + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + , TableRef( a_TableRef ) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); + if (TableRef != LUA_REFNIL) + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; // Abort enumeration + } + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean(LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + bool bRetVal = (self->*Func1)(ItemX, ItemY, ItemZ, Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +template< class Ty1, + class Ty2, + bool (Ty1::*Func1)(int, int, cItemCallback &) > +static int tolua_ForEachInChunk(lua_State* tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if ((NumArgs != 3) && (NumArgs != 4)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 3 or 4 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1 and #2"); + } + + int ChunkX = ((int)tolua_tonumber(tolua_S, 2, 0)); + int ChunkZ = ((int)tolua_tonumber(tolua_S, 3, 0)); + + if (!lua_isfunction( tolua_S, 4)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #3"); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 4) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #4"); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #3"); + } + + class cLuaCallback : public cItemCallback + { + public: + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + , TableRef( a_TableRef ) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); + if (TableRef != LUA_REFNIL) + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; /* Abort enumeration */ + } + + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean(LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + bool bRetVal = (self->*Func1)(ChunkX, ChunkZ, Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +template< class Ty1, + class Ty2, + bool (Ty1::*Func1)(cItemCallback &) > +static int tolua_ForEach(lua_State * tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if( NumArgs != 1 && NumArgs != 2) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 1 or 2 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + } + + if (!lua_isfunction( tolua_S, 2)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 2) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #2"); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + } + + class cLuaCallback : public cItemCallback + { + public: + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + , TableRef( a_TableRef ) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushusertype( LuaState, a_Item, Ty2::GetClassStatic() ); + if (TableRef != LUA_REFNIL) + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; /* Abort enumeration */ + } + + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean( LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + bool bRetVal = (self->*Func1)(Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight) + // Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight] + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 5, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'GetBlockInfo'", NULL); + } + #endif + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight; + bool res = self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight); + tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushnumber(tolua_S, BlockType); + tolua_pushnumber(tolua_S, BlockMeta); + tolua_pushnumber(tolua_S, BlockSkyLight); + tolua_pushnumber(tolua_S, BlockBlockLight); + return 5; + } + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'GetBlockInfo'.", &tolua_err); + return 0; + #endif +} + + + + + +static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta) + // Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta] + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 5, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'GetBlockTypeMeta'", NULL); + } + #endif + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + bool res = self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta); + tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushnumber(tolua_S, BlockType); + tolua_pushnumber(tolua_S, BlockMeta); + return 3; + } + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'GetBlockTypeMeta'.", &tolua_err); + return 0; + #endif +} + + + + + +static int tolua_cWorld_GetSignLines(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4) + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 10, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'GetSignLines'", NULL); + } + #endif + { + AString Line1, Line2, Line3, Line4; + bool res = self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushstring(tolua_S, Line1.c_str()); + tolua_pushstring(tolua_S, Line2.c_str()); + tolua_pushstring(tolua_S, Line3.c_str()); + tolua_pushstring(tolua_S, Line4.c_str()); + return 5; + } + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'GetSignLines'.", &tolua_err); + return 0; + #endif +} + + + + + +static int tolua_cWorld_SetSignLines(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4) + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_iscppstring(tolua_S, 5, 0, &tolua_err) || + !tolua_iscppstring(tolua_S, 6, 0, &tolua_err) || + !tolua_iscppstring(tolua_S, 7, 0, &tolua_err) || + !tolua_iscppstring(tolua_S, 8, 0, &tolua_err) || + !tolua_isusertype (tolua_S, 9, "cPlayer", 1, &tolua_err) || + !tolua_isnoobj (tolua_S, 10, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + const AString Line1 = tolua_tocppstring(tolua_S, 5, 0); + const AString Line2 = tolua_tocppstring(tolua_S, 6, 0); + const AString Line3 = tolua_tocppstring(tolua_S, 7, 0); + const AString Line4 = tolua_tocppstring(tolua_S, 8, 0); + cPlayer * Player = (cPlayer *)tolua_tousertype (tolua_S, 9, NULL); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines' / 'UpdateSign'", NULL); + } + #endif + { + bool res = self->UpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player); + tolua_pushboolean(tolua_S, res ? 1 : 0); + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'SetSignLines' / 'UpdateSign'.", &tolua_err); + return 0; + #endif +} + + + + +static int tolua_cWorld_TryGetHeight(lua_State * tolua_S) +{ + // Exported manually, because tolua would require the out-only param a_Height to be used when calling + // Takes (a_World,) a_BlockX, a_BlockZ + // Returns Height, IsValid + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 4, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 3, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "Invalid 'self' in function 'TryGetHeight'", NULL); + } + #endif + { + int Height = 0; + bool res = self->TryGetHeight(BlockX, BlockZ, Height); + tolua_pushnumber(tolua_S, Height); + tolua_pushboolean(tolua_S, res ? 1 : 0); + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'TryGetHeight'.", &tolua_err); + return 0; + #endif +} + + + + + +class cLuaWorldTask : + public cWorld::cTask +{ +public: + cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : + m_Plugin(a_Plugin), + m_FnRef(a_FnRef) + { + } + +protected: + cPluginLua & m_Plugin; + int m_FnRef; + + // cWorld::cTask overrides: + virtual void Run(cWorld & a_World) override + { + m_Plugin.Call(m_FnRef, &a_World); + } +} ; + + + + + +static int tolua_cWorld_QueueTask(lua_State * tolua_S) +{ + // Binding for cWorld::QueueTask + // Params: function + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Retrieve the args: + cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + } + if (!lua_isfunction(tolua_S, 2)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + } + + // Create a reference to the function: + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FnRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + } + + self->QueueTask(new cLuaWorldTask(*Plugin, FnRef)); + return 0; +} + + + + + +static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) +{ + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + + const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins(); + + lua_newtable(tolua_S); + //lua_createtable(tolua_S, AllPlugins.size(), 0); + int newTable = lua_gettop(tolua_S); + int index = 1; + cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin(); + while(iter != AllPlugins.end()) + { + const cPlugin* Plugin = iter->second; + tolua_pushstring( tolua_S, iter->first.c_str() ); + if( Plugin != NULL ) + { + tolua_pushusertype( tolua_S, (void*)Plugin, "const cPlugin" ); + } + else + { + tolua_pushboolean(tolua_S, 0); + } + //lua_rawseti(tolua_S, newTable, index); + lua_rawset(tolua_S, -3); + ++iter; + ++index; + } + return 1; +} + + + + + +static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx) +{ + // Helper function for cPluginmanager:AddHook() binding + // Takes care of the new case (#121): args are HOOK_TYPE and CallbackFunction + // The arg types have already been checked + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = GetLuaPlugin(S); + if (Plugin == NULL) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Retrieve and check the hook type + int HookType = (int)tolua_tonumber(S, a_ParamIdx, -1); + if (!a_PluginManager->IsValidHookType(HookType)) + { + LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); + S.LogStackTrace(); + return 0; + } + + // Add the hook to the plugin + if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1)) + { + LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType); + S.LogStackTrace(); + return 0; + } + a_PluginManager->AddHook(Plugin, HookType); + + // Success + return 0; +} + + + + + +static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx) +{ + // Helper function for cPluginmanager:AddHook() binding + // Takes care of the old case (#121): args are cPluginLua and HOOK_TYPE + // The arg types have already been checked + + // Retrieve and check the cPlugin parameter + cPluginLua * Plugin = (cPluginLua *)tolua_tousertype(S, a_ParamIdx, NULL); + if (Plugin == NULL) + { + LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, expected a valid cPlugin object. Hook not added"); + S.LogStackTrace(); + return 0; + } + if (Plugin != GetLuaPlugin(S)) + { + // The plugin parameter passed to us is not our stored plugin. Disallow this! + LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, cannot add hook to foreign plugins. Hook not added."); + S.LogStackTrace(); + return 0; + } + + // Retrieve and check the hook type + int HookType = (int)tolua_tonumber(S, a_ParamIdx + 1, -1); + if (!a_PluginManager->IsValidHookType(HookType)) + { + LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); + S.LogStackTrace(); + return 0; + } + + // Get the standard name for the callback function: + const char * FnName = cPluginLua::GetHookFnName(HookType); + if (FnName == NULL) + { + LOGWARNING("cPluginManager.AddHook(): Unknown hook type (%d). Hook not added.", HookType); + S.LogStackTrace(); + return 0; + } + + // Retrieve the function to call and add it to the plugin: + lua_pushstring(S, FnName); + bool res = Plugin->AddHookRef(HookType, 1); + lua_pop(S, 1); // Pop the function off the stack + if (!res) + { + LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName); + S.LogStackTrace(); + return 0; + } + a_PluginManager->AddHook(Plugin, HookType); + + // Success + return 0; +} + + + + + +static int tolua_cPluginManager_AddHook(lua_State * tolua_S) +{ + /* + Function signatures: + cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) -- (1) recommended + cPluginManager:Get():AddHook(HOOK_TYPE, CallbackFunction) -- (2) accepted silently + cPluginManager:Get():AddHook(Plugin, HOOK_TYPE) -- (3) old style (#121), accepted but complained about + cPluginManager.AddHook(Plugin, HOOK_TYPE) -- (4) old style (#121) mangled, accepted but complained about + */ + + cLuaState S(tolua_S); + cPluginManager * PlgMgr = cPluginManager::Get(); + + // If the first param is a cPluginManager, use it instead of the global one: + int ParamIdx = 1; + tolua_Error err; + if (tolua_isusertype(S, 1, "cPluginManager", 0, &err)) + { + // Style 2 or 3, retrieve the PlgMgr instance + PlgMgr = (cPluginManager *)tolua_tousertype(S, 1, NULL); + if (PlgMgr == NULL) + { + LOGWARNING("Malformed plugin, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction). Fixing the call for you."); + S.LogStackTrace(); + PlgMgr = cPluginManager::Get(); + } + ParamIdx += 1; + } + + if (lua_isnumber(S, ParamIdx) && lua_isfunction(S, ParamIdx + 1)) + { + // The next params are a number and a function, assume style 1 or 2 + return tolua_cPluginManager_AddHook_FnRef(PlgMgr, S, ParamIdx); + } + else if (tolua_isusertype(S, ParamIdx, "cPlugin", 0, &err) && lua_isnumber(S, ParamIdx + 1)) + { + // The next params are a cPlugin and a number, assume style 3 or 4 + LOGINFO("cPluginManager.AddHook(): Deprecated format used, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) instead. Fixing the call for you."); + S.LogStackTrace(); + return tolua_cPluginManager_AddHook_DefFn(PlgMgr, S, ParamIdx); + } + + AString ParamDesc; + Printf(ParamDesc, "%s, %s, %s", S.GetTypeText(1).c_str(), S.GetTypeText(2).c_str(), S.GetTypeText(3).c_str()); + LOGWARNING("cPluginManager.AddHook(): bad parameters. Expected HOOK_TYPE and CallbackFunction, got %s. Hook not added.", ParamDesc.c_str()); + S.LogStackTrace(); + return 0; +} + + + + + +static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if( NumArgs != 1) + { + LOGWARN("Error in function call 'ForEachCommand': Requires 1 argument, got %i", NumArgs); + return 0; + } + + cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + LOGWARN("Error in function call 'ForEachCommand': Not called on an object instance"); + return 0; + } + + if (!lua_isfunction(tolua_S, 2)) + { + LOGWARN("Error in function call 'ForEachCommand': Expected a function for parameter #1"); + return 0; + } + + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + LOGWARN("Error in function call 'ForEachCommand': Could not get function reference of parameter #1"); + return 0; + } + + class cLuaCallback : public cPluginManager::cCommandEnumCallback + { + public: + cLuaCallback(lua_State * a_LuaState, int a_FuncRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + {} + + private: + virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushcppstring(LuaState, a_Command); + tolua_pushcppstring(LuaState, a_Permission); + tolua_pushcppstring(LuaState, a_HelpString); + + int s = lua_pcall(LuaState, 3, 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; /* Abort enumeration */ + } + + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean( LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + } Callback(tolua_S, FuncRef); + + bool bRetVal = self->ForEachCommand(Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal); + return 1; +} + + + + + +static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if( NumArgs != 1) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Requires 1 argument, got %i", NumArgs); + return 0; + } + + cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance"); + return 0; + } + + if (!lua_isfunction(tolua_S, 2)) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Expected a function for parameter #1"); + return 0; + } + + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Could not get function reference of parameter #1"); + return 0; + } + + class cLuaCallback : public cPluginManager::cCommandEnumCallback + { + public: + cLuaCallback(lua_State * a_LuaState, int a_FuncRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + {} + + private: + virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushcppstring(LuaState, a_Command); + tolua_pushcppstring(LuaState, a_HelpString); + + int s = lua_pcall(LuaState, 2, 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; /* Abort enumeration */ + } + + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean( LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + } Callback(tolua_S, FuncRef); + + bool bRetVal = self->ForEachConsoleCommand(Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal); + return 1; +} + + + + + +static int tolua_cPluginManager_BindCommand(lua_State * L) +{ + /* Function signatures: + cPluginManager:BindCommand(Command, Permission, Function, HelpString) + cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param + */ + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == NULL) + { + return 0; + } + + // Read the arguments to this API call: + tolua_Error tolua_err; + int idx = 1; + if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err)) + { + idx++; + } + if ( + !tolua_iscppstring(L, idx, 0, &tolua_err) || + !tolua_iscppstring(L, idx + 1, 0, &tolua_err) || + !tolua_iscppstring(L, idx + 3, 0, &tolua_err) || + !tolua_isnoobj (L, idx + 4, &tolua_err) + ) + { + tolua_error(L, "#ferror in function 'BindCommand'.", &tolua_err); + return 0; + } + if (!lua_isfunction(L, idx + 2)) + { + luaL_error(L, "\"BindCommand\" function expects a function as its 3rd parameter. Command-binding aborted."); + return 0; + } + cPluginManager * self = cPluginManager::Get(); + AString Command (tolua_tocppstring(L, idx, "")); + AString Permission(tolua_tocppstring(L, idx + 1, "")); + AString HelpString(tolua_tocppstring(L, idx + 3, "")); + + // Store the function reference: + lua_pop(L, 1); // Pop the help string off the stack + int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference + if (FnRef == LUA_REFNIL) + { + LOGERROR("\"BindCommand\": Cannot create a function reference. Command \"%s\" not bound.", Command.c_str()); + return 0; + } + + if (!self->BindCommand(Command, Plugin, Permission, HelpString)) + { + // Refused. Possibly already bound. Error message has been given, display the callstack: + cLuaState LS(L); + LS.LogStackTrace(); + return 0; + } + + Plugin->BindCommand(Command, FnRef); + return 0; +} + + + + + +static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) +{ + /* Function signatures: + cPluginManager:BindConsoleCommand(Command, Function, HelpString) + cPluginManager.BindConsoleCommand(Command, Function, HelpString) -- without the "self" param + */ + + // Get the plugin identification out of LuaState: + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == NULL) + { + return 0; + } + + // Read the arguments to this API call: + tolua_Error tolua_err; + int idx = 1; + if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err)) + { + idx++; + } + if ( + !tolua_iscppstring(L, idx, 0, &tolua_err) || // Command + !tolua_iscppstring(L, idx + 2, 0, &tolua_err) || // HelpString + !tolua_isnoobj (L, idx + 3, &tolua_err) + ) + { + tolua_error(L, "#ferror in function 'BindConsoleCommand'.", &tolua_err); + return 0; + } + if (!lua_isfunction(L, idx + 1)) + { + luaL_error(L, "\"BindConsoleCommand\" function expects a function as its 2nd parameter. Command-binding aborted."); + return 0; + } + cPluginManager * self = cPluginManager::Get(); + AString Command (tolua_tocppstring(L, idx, "")); + AString HelpString(tolua_tocppstring(L, idx + 2, "")); + + // Store the function reference: + lua_pop(L, 1); // Pop the help string off the stack + int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference + if (FnRef == LUA_REFNIL) + { + LOGERROR("\"BindConsoleCommand\": Cannot create a function reference. Console Command \"%s\" not bound.", Command.c_str()); + return 0; + } + + if (!self->BindConsoleCommand(Command, Plugin, HelpString)) + { + // Refused. Possibly already bound. Error message has been given, display the callstack: + cLuaState LS(L); + LS.LogStackTrace(); + return 0; + } + + Plugin->BindConsoleCommand(Command, FnRef); + return 0; +} + + + + + +static int tolua_cPlayer_GetGroups(lua_State* tolua_S) +{ + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + + const cPlayer::GroupList & AllGroups = self->GetGroups(); + + lua_createtable(tolua_S, AllGroups.size(), 0); + int newTable = lua_gettop(tolua_S); + int index = 1; + cPlayer::GroupList::const_iterator iter = AllGroups.begin(); + while(iter != AllGroups.end()) + { + const cGroup* Group = *iter; + tolua_pushusertype( tolua_S, (void*)Group, "const cGroup" ); + lua_rawseti(tolua_S, newTable, index); + ++iter; + ++index; + } + return 1; +} + + + + + +static int tolua_cPlayer_GetResolvedPermissions(lua_State* tolua_S) +{ + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + + cPlayer::StringList AllPermissions = self->GetResolvedPermissions(); + + lua_createtable(tolua_S, AllPermissions.size(), 0); + int newTable = lua_gettop(tolua_S); + int index = 1; + cPlayer::StringList::iterator iter = AllPermissions.begin(); + while(iter != AllPermissions.end()) + { + std::string& Permission = *iter; + tolua_pushstring( tolua_S, Permission.c_str() ); + lua_rawseti(tolua_S, newTable, index); + ++iter; + ++index; + } + return 1; +} + + + + + +static int tolua_cPlayer_OpenWindow(lua_State * tolua_S) +{ + // Function signature: cPlayer:OpenWindow(Window) + + // Retrieve the plugin instance from the Lua state + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + return 0; + } + + // Get the parameters: + cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL); + cWindow * wnd = (cWindow *)tolua_tousertype(tolua_S, 2, NULL); + if ((self == NULL) || (wnd == NULL)) + { + LOGWARNING("%s: invalid self (%p) or wnd (%p)", __FUNCTION__, self, wnd); + return 0; + } + + // If cLuaWindow, add a reference, so that Lua won't delete the cLuaWindow object mid-processing + tolua_Error err; + if (tolua_isusertype(tolua_S, 2, "cLuaWindow", 0, &err)) + { + cLuaWindow * LuaWnd = (cLuaWindow *)wnd; + // Only if not already referenced + if (!LuaWnd->IsLuaReferenced()) + { + int LuaRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (LuaRef == LUA_REFNIL) + { + LOGWARNING("%s: Cannot create a window reference. Cannot open window \"%s\".", + __FUNCTION__, wnd->GetWindowTitle().c_str() + ); + return 0; + } + LuaWnd->SetLuaRef(Plugin, LuaRef); + } + } + + // Open the window + self->OpenWindow(wnd); + return 0; +} + + + + + +template < + class OBJTYPE, + void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef) +> +static int tolua_SetObjectCallback(lua_State * tolua_S) +{ + // Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction) + + // Retrieve the plugin instance from the Lua state + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + // Warning message has already been printed by GetLuaPlugin(), bail out silently + return 0; + } + + // Get the parameters - self and the function reference: + OBJTYPE * self = (OBJTYPE *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self); + return 0; + } + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); // Store function reference for later retrieval + if (FnRef == LUA_REFNIL) + { + LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__); + return 0; + } + + // Set the callback + (self->*SetCallback)(Plugin, FnRef); + return 0; +} + + + + + +static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) +{ + cPluginLua * self = (cPluginLua *)tolua_tousertype(tolua_S,1,0); + + tolua_Error tolua_err; + tolua_err.array = 0; + tolua_err.index = 3; + tolua_err.type = "function"; + + std::string Title = ""; + int Reference = LUA_REFNIL; + + if ( + tolua_isstring(tolua_S, 2, 0, &tolua_err ) && + lua_isfunction(tolua_S, 3 ) + ) + { + Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + Title = ((std::string) tolua_tocppstring(tolua_S,2,0)); + } + else + { + return tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err); + } + + if( Reference != LUA_REFNIL ) + { + if( !self->AddWebTab( Title.c_str(), tolua_S, Reference ) ) + { + luaL_unref( tolua_S, LUA_REGISTRYINDEX, Reference ); + } + } + else + { + LOGERROR("ERROR: cPluginLua:AddWebTab invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str() ); + } + + return 0; +} + + + + + +static int tolua_cPluginLua_AddTab(lua_State* tolua_S) +{ + cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); + LOGWARN("WARNING: Using deprecated function AddTab()! Use AddWebTab() instead. (plugin \"%s\" in folder \"%s\")", + self->GetName().c_str(), self->GetDirectory().c_str() + ); + return tolua_cPluginLua_AddWebTab( tolua_S ); +} + + + + +// Perhaps use this as well for copying tables https://github.com/keplerproject/rings/pull/1 +static int copy_lua_values(lua_State * a_Source, lua_State * a_Destination, int i, int top) +{ + for(; i <= top; ++i ) + { + int t = lua_type(a_Source, i); + switch (t) { + case LUA_TSTRING: /* strings */ + { + const char * s = lua_tostring(a_Source, i); + LOGD("%i push string: %s", i, s); + tolua_pushstring(a_Destination, s); + } + break; + case LUA_TBOOLEAN: /* booleans */ + { + int b = tolua_toboolean(a_Source, i, false); + LOGD("%i push bool: %i", i, b); + tolua_pushboolean(a_Destination, b ); + } + break; + case LUA_TNUMBER: /* numbers */ + { + lua_Number d = tolua_tonumber(a_Source, i, 0); + LOGD("%i push number: %0.2f", i, d); + tolua_pushnumber(a_Destination, d ); + } + break; + case LUA_TUSERDATA: + { + const char * type = 0; + if (lua_getmetatable(a_Source,i)) + { + lua_rawget(a_Source, LUA_REGISTRYINDEX); + type = lua_tostring(a_Source, -1); + lua_pop(a_Source, 1); // Pop.. something?! I don't knooow~~ T_T + } + + // don't need tolua_tousertype we already have the type + void * ud = tolua_touserdata(a_Source, i, 0); + LOGD("%i push usertype: %p of type '%s'", i, ud, type); + if( type == 0 ) + { + LOGERROR("Call(): Something went wrong when trying to get usertype name!"); + return 0; + } + tolua_pushusertype(a_Destination, ud, type); + } + break; + default: /* other values */ + LOGERROR("Call(): Unsupported value: '%s'. Can only use numbers and strings!", lua_typename(a_Source, t)); + return 0; + } + } + return 1; +} + + + + + +static int tolua_cPlugin_Call(lua_State* tolua_S) +{ + cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); + lua_State* targetState = self->GetLuaState(); + int targetTop = lua_gettop(targetState); + + int top = lua_gettop(tolua_S); + LOGD("total in stack: %i", top ); + + std::string funcName = tolua_tostring(tolua_S, 2, ""); + LOGD("Func name: %s", funcName.c_str() ); + + lua_getglobal(targetState, funcName.c_str()); + if(!lua_isfunction(targetState,-1)) + { + LOGWARN("Error could not find function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() ); + lua_pop(targetState,1); + return 0; + } + + if( copy_lua_values(tolua_S, targetState, 3, top) == 0 ) // Start at 3 because 1 and 2 are the plugin and function name respectively + { + // something went wrong, exit + return 0; + } + + int s = lua_pcall(targetState, top - 2, LUA_MULTRET, 0); + if (cLuaState::ReportErrors(targetState, s)) + { + LOGWARN("Error while calling function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() ); + return 0; + } + + int nresults = lua_gettop(targetState) - targetTop; + LOGD("num results: %i", nresults); + int ttop = lua_gettop(targetState); + if( copy_lua_values(targetState, tolua_S, targetTop+1, ttop) == 0 ) // Start at targetTop+1 and I have no idea why xD + { + // something went wrong, exit + return 0; + } + + lua_pop(targetState, nresults); // I have no idea what I'm doing, but it works + + return nresults; +} + + + + + +static int tolua_md5(lua_State* tolua_S) +{ + std::string SourceString = tolua_tostring(tolua_S, 1, 0); + std::string CryptedString = md5( SourceString ); + tolua_pushstring( tolua_S, CryptedString.c_str() ); + return 1; +} + + + + + +static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string, std::string >& a_StringStringMap ) +{ + lua_newtable(tolua_S); + int top = lua_gettop(tolua_S); + + for( std::map< std::string, std::string >::iterator it = a_StringStringMap.begin(); it != a_StringStringMap.end(); ++it ) + { + const char* key = it->first.c_str(); + const char* value = it->second.c_str(); + lua_pushstring(tolua_S, key); + lua_pushstring(tolua_S, value); + lua_settable(tolua_S, top); + } + + return 1; +} + + + + + +static int tolua_get_HTTPRequest_Params(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); + return tolua_push_StringStringMap(tolua_S, self->Params); +} + + + + + +static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); + return tolua_push_StringStringMap(tolua_S, self->PostParams); +} + + + + + +static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); + std::map< std::string, HTTPFormData >& FormData = self->FormData; + + lua_newtable(tolua_S); + int top = lua_gettop(tolua_S); + + for( std::map< std::string, HTTPFormData >::iterator it = FormData.begin(); it != FormData.end(); ++it ) + { + lua_pushstring(tolua_S, it->first.c_str() ); + tolua_pushusertype(tolua_S, &(it->second), "HTTPFormData" ); + //lua_pushlstring(tolua_S, it->second.Value.c_str(), it->second.Value.size() ); // Might contain binary data + lua_settable(tolua_S, top); + } + + return 1; +} + + + + + +static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) +{ + cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); + + const cWebAdmin::PluginList & AllPlugins = self->GetPlugins(); + + lua_createtable(tolua_S, AllPlugins.size(), 0); + int newTable = lua_gettop(tolua_S); + int index = 1; + cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin(); + while(iter != AllPlugins.end()) + { + const cWebPlugin* Plugin = *iter; + tolua_pushusertype( tolua_S, (void*)Plugin, "const cWebPlugin" ); + lua_rawseti(tolua_S, newTable, index); + ++iter; + ++index; + } + return 1; +} + + + + + +static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) +{ + cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0); + + const cWebPlugin::TabNameList & TabNames = self->GetTabNames(); + + lua_newtable(tolua_S); + int newTable = lua_gettop(tolua_S); + int index = 1; + cWebPlugin::TabNameList::const_iterator iter = TabNames.begin(); + while(iter != TabNames.end()) + { + const AString & FancyName = iter->first; + const AString & WebName = iter->second; + tolua_pushstring( tolua_S, WebName.c_str() ); // Because the WebName is supposed to be unique, use it as key + tolua_pushstring( tolua_S, FancyName.c_str() ); + // + lua_rawset(tolua_S, -3); + ++iter; + ++index; + } + return 1; +} + + + + + +static int Lua_ItemGrid_GetSlotCoords(lua_State * L) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(L, 1, "const cItemGrid", 0, &tolua_err) || + !tolua_isnumber (L, 2, 0, &tolua_err) || + !tolua_isnoobj (L, 3, &tolua_err) + ) + { + goto tolua_lerror; + } + + { + const cItemGrid * self = (const cItemGrid *)tolua_tousertype(L, 1, 0); + int SlotNum = (int)tolua_tonumber(L, 2, 0); + if (self == NULL) + { + tolua_error(L, "invalid 'self' in function 'cItemGrid:GetSlotCoords'", NULL); + return 0; + } + int X, Y; + self->GetSlotCoords(SlotNum, X, Y); + tolua_pushnumber(L, (lua_Number)X); + tolua_pushnumber(L, (lua_Number)Y); + return 2; + } + +tolua_lerror: + tolua_error(L, "#ferror in function 'cItemGrid:GetSlotCoords'.", &tolua_err); + return 0; +} + + + + + +/// Provides interface between a Lua table of callbacks and the cBlockTracer::cCallbacks +class cLuaBlockTracerCallbacks : + public cBlockTracer::cCallbacks +{ +public: + cLuaBlockTracerCallbacks(cLuaState & a_LuaState, int a_ParamNum) : + m_LuaState(a_LuaState), + m_TableRef(a_LuaState, a_ParamNum) + { + } + + virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlock")) + { + // No such function in the table, skip the callback + return false; + } + m_LuaState.Push(a_BlockX); + m_LuaState.Push(a_BlockY); + m_LuaState.Push(a_BlockZ); + m_LuaState.Push(a_BlockType); + m_LuaState.Push(a_BlockMeta); + m_LuaState.Push(a_EntryFace); + if (!m_LuaState.CallFunction(1)) + { + return false; + } + bool res = false; + if (lua_isboolean(m_LuaState, -1)) + { + res = (lua_toboolean(m_LuaState, -1) != 0); + } + lua_pop(m_LuaState, 1); + return res; + } + + virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlockNoData")) + { + // No such function in the table, skip the callback + return false; + } + m_LuaState.Push(a_BlockX); + m_LuaState.Push(a_BlockY); + m_LuaState.Push(a_BlockZ); + m_LuaState.Push(a_EntryFace); + if (!m_LuaState.CallFunction(1)) + { + return false; + } + bool res = false; + if (lua_isboolean(m_LuaState, -1)) + { + res = (lua_toboolean(m_LuaState, -1) != 0); + } + lua_pop(m_LuaState, 1); + return res; + } + + virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnOutOfWorld")) + { + // No such function in the table, skip the callback + return false; + } + m_LuaState.Push(a_BlockX); + m_LuaState.Push(a_BlockY); + m_LuaState.Push(a_BlockZ); + if (!m_LuaState.CallFunction(1)) + { + return false; + } + bool res = false; + if (lua_isboolean(m_LuaState, -1)) + { + res = (lua_toboolean(m_LuaState, -1) != 0); + } + lua_pop(m_LuaState, 1); + return res; + } + + virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnIntoWorld")) + { + // No such function in the table, skip the callback + return false; + } + m_LuaState.Push(a_BlockX); + m_LuaState.Push(a_BlockY); + m_LuaState.Push(a_BlockZ); + if (!m_LuaState.CallFunction(1)) + { + return false; + } + bool res = false; + if (lua_isboolean(m_LuaState, -1)) + { + res = (lua_toboolean(m_LuaState, -1) != 0); + } + lua_pop(m_LuaState, 1); + return res; + } + + virtual void OnNoMoreHits(void) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoMoreHits")) + { + // No such function in the table, skip the callback + return; + } + m_LuaState.CallFunction(0); + } + + virtual void OnNoChunk(void) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoChunk")) + { + // No such function in the table, skip the callback + return; + } + m_LuaState.CallFunction(0); + } + +protected: + cLuaState & m_LuaState; + cLuaState::cRef m_TableRef; +} ; + + + + + +static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) +{ + // cLineBlockTracer.Trace(World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ) + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamTable (2) || + !L.CheckParamNumber (3, 8) || + !L.CheckParamEnd (9) + ) + { + return 0; + } + + cWorld * World = (cWorld *)tolua_tousertype(L, 1, NULL); + cLuaBlockTracerCallbacks Callbacks(L, 2); + double StartX = tolua_tonumber(L, 3, 0); + double StartY = tolua_tonumber(L, 4, 0); + double StartZ = tolua_tonumber(L, 5, 0); + double EndX = tolua_tonumber(L, 6, 0); + double EndY = tolua_tonumber(L, 7, 0); + double EndZ = tolua_tonumber(L, 8, 0); + bool res = cLineBlockTracer::Trace(*World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ); + tolua_pushboolean(L, res ? 1 : 0); + return 1; +} + + + + + +static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S) +{ + // function cHopperEntity::GetOutputBlockPos() + // Exported manually because tolua would require meaningless params + + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cHopperEntity") || + !L.CheckParamNumber (2) || + !L.CheckParamEnd (3) + ) + { + return 0; + } + cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'cHopperEntity::GetOutputBlockPos()'", NULL); + return 0; + } + + NIBBLETYPE a_BlockMeta = ((NIBBLETYPE)tolua_tonumber(tolua_S, 2, 0)); + int a_OutputX, a_OutputY, a_OutputZ; + bool res = self->GetOutputBlockPos(a_BlockMeta, a_OutputX, a_OutputY, a_OutputZ); + tolua_pushboolean(tolua_S, res); + if (res) + { + tolua_pushnumber(tolua_S, (lua_Number)a_OutputX); + tolua_pushnumber(tolua_S, (lua_Number)a_OutputY); + tolua_pushnumber(tolua_S, (lua_Number)a_OutputZ); + return 4; + } + return 1; +} + + + + + +void ManualBindings::Bind(lua_State * tolua_S) +{ + tolua_beginmodule(tolua_S, NULL); + tolua_function(tolua_S, "StringSplit", tolua_StringSplit); + tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); + tolua_function(tolua_S, "LOG", tolua_LOG); + tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO); + tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); + tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); + tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); + + tolua_beginmodule(tolua_S, "cHopperEntity"); + tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cLineBlockTracer"); + tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cRoot"); + tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith ); + tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach); + tolua_function(tolua_S, "ForEachWorld", tolua_ForEach); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cWorld"); + tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ); + tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ); + tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ); + tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ); + tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>); + tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ); + tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>); + tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>); + tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk); + tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>); + tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk); + tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk); + tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>); + tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo); + tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); + tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); + tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); + tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); + tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); + tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cPlugin"); + tolua_function(tolua_S, "Call", tolua_cPlugin_Call); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cPluginManager"); + tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook); + tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand); + tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand); + tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand); + tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand); + tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cPlayer"); + tolua_function(tolua_S, "GetGroups", tolua_cPlayer_GetGroups); + tolua_function(tolua_S, "GetResolvedPermissions", tolua_cPlayer_GetResolvedPermissions); + tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cLuaWindow"); + tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback); + tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cPluginLua"); + tolua_function(tolua_S, "AddTab", tolua_cPluginLua_AddTab); + tolua_function(tolua_S, "AddWebTab", tolua_cPluginLua_AddWebTab); + tolua_endmodule(tolua_S); + + tolua_cclass(tolua_S,"HTTPRequest","HTTPRequest","",NULL); + tolua_beginmodule(tolua_S,"HTTPRequest"); + // tolua_variable(tolua_S,"Method",tolua_get_HTTPRequest_Method,tolua_set_HTTPRequest_Method); + // tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path); + tolua_variable(tolua_S,"FormData",tolua_get_HTTPRequest_FormData,0); + tolua_variable(tolua_S,"Params",tolua_get_HTTPRequest_Params,0); + tolua_variable(tolua_S,"PostParams",tolua_get_HTTPRequest_PostParams,0); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cWebAdmin"); + tolua_function(tolua_S, "GetPlugins", tolua_cWebAdmin_GetPlugins); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cWebPlugin"); + tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cClientHandle"); + tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE); + tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cItemGrid"); + tolua_function(tolua_S, "GetSlotCoords", Lua_ItemGrid_GetSlotCoords); + tolua_endmodule(tolua_S); + + tolua_function(tolua_S, "md5", tolua_md5); + + tolua_endmodule(tolua_S); +} + + + + diff --git a/src/ManualBindings.h b/src/ManualBindings.h new file mode 100644 index 000000000..e6594947e --- /dev/null +++ b/src/ManualBindings.h @@ -0,0 +1,8 @@ +#pragma once + +struct lua_State; +class ManualBindings +{ +public: + static void Bind( lua_State* tolua_S ); +}; \ No newline at end of file diff --git a/src/Matrix4f.cpp b/src/Matrix4f.cpp new file mode 100644 index 000000000..d0a407a99 --- /dev/null +++ b/src/Matrix4f.cpp @@ -0,0 +1,4 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +// _X: empty file?? diff --git a/src/Matrix4f.h b/src/Matrix4f.h new file mode 100644 index 000000000..249c92f5f --- /dev/null +++ b/src/Matrix4f.h @@ -0,0 +1,225 @@ +#pragma once + +#define _USE_MATH_DEFINES +#include +#include "Vector3f.h" + +class Matrix4f +{ +public: + enum + { + TX=3, + TY=7, + TZ=11, + D0=0, D1=5, D2=10, D3=15, + SX=D0, SY=D1, SZ=D2, + W=D3 + }; + Matrix4f() { Identity(); } + float& operator [] ( int a_N ) { return cell[a_N]; } + void Identity() + { + cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] = + cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0; + cell[D0] = cell[D1] = cell[D2] = cell[W] = 1; + } + void Init( Vector3f a_Pos, float a_RX, float a_RY, float a_RZ ) + { + Matrix4f t; + t.RotateX( a_RZ ); + RotateY( a_RY ); + Concatenate( t ); + t.RotateZ( a_RX ); + Concatenate( t ); + Translate( a_Pos ); + } + void RotateX( float a_RX ) + { + float sx = (float)sin( a_RX * M_PI / 180 ); + float cx = (float)cos( a_RX * M_PI / 180 ); + Identity(); + cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx; + } + void RotateY( float a_RY ) + { + float sy = (float)sin( a_RY * M_PI / 180 ); + float cy = (float)cos( a_RY * M_PI / 180 ); + Identity (); + cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy; + } + void RotateZ( float a_RZ ) + { + float sz = (float)sin( a_RZ * M_PI / 180 ); + float cz = (float)cos( a_RZ * M_PI / 180 ); + Identity (); + cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz; + } + void Translate( Vector3f a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; } + void SetTranslation( Vector3f a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; } + void Concatenate( const Matrix4f& m2 ) + { + Matrix4f res; + int c; + for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ ) + res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] + + cell[r * 4 + 1] * m2.cell[c + 4] + + cell[r * 4 + 2] * m2.cell[c + 8] + + cell[r * 4 + 3] * m2.cell[c + 12]; + for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c]; + } + Vector3f Transform( const Vector3f& v ) const + { + float x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3]; + float y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7]; + float z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11]; + return Vector3f( x, y, z ); + } + void Invert() + { + Matrix4f t; + int h, i; + float tx = -cell[3], ty = -cell[7], tz = -cell[11]; + for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4]; + for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i]; + cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2]; + cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6]; + cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10]; + } + Vector3f GetXColumn() { return Vector3f( cell[0], cell[1], cell[2] ); } + Vector3f GetYColumn() { return Vector3f( cell[4], cell[5], cell[6] ); } + Vector3f GetZColumn() { return Vector3f( cell[8], cell[9], cell[10] ); } + void SetXColumn( const Vector3f & a_X ) + { + cell[0] = a_X.x; + cell[1] = a_X.y; + cell[2] = a_X.z; + } + void SetYColumn( const Vector3f & a_Y ) + { + cell[4] = a_Y.x; + cell[5] = a_Y.y; + cell[6] = a_Y.z; + } + void SetZColumn( const Vector3f & a_Z ) + { + cell[8] = a_Z.x; + cell[9] = a_Z.y; + cell[10] = a_Z.z; + } + float cell[16]; +}; + + + + + +class Matrix4d +{ +public: + enum + { + TX=3, + TY=7, + TZ=11, + D0=0, D1=5, D2=10, D3=15, + SX=D0, SY=D1, SZ=D2, + W=D3 + }; + Matrix4d() { Identity(); } + double& operator [] ( int a_N ) { return cell[a_N]; } + void Identity() + { + cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] = + cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0; + cell[D0] = cell[D1] = cell[D2] = cell[W] = 1; + } + void Init( Vector3f a_Pos, double a_RX, double a_RY, double a_RZ ) + { + Matrix4d t; + t.RotateX( a_RZ ); + RotateY( a_RY ); + Concatenate( t ); + t.RotateZ( a_RX ); + Concatenate( t ); + Translate( a_Pos ); + } + void RotateX( double a_RX ) + { + double sx = (double)sin( a_RX * M_PI / 180 ); + double cx = (double)cos( a_RX * M_PI / 180 ); + Identity(); + cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx; + } + void RotateY( double a_RY ) + { + double sy = (double)sin( a_RY * M_PI / 180 ); + double cy = (double)cos( a_RY * M_PI / 180 ); + Identity (); + cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy; + } + void RotateZ( double a_RZ ) + { + double sz = (double)sin( a_RZ * M_PI / 180 ); + double cz = (double)cos( a_RZ * M_PI / 180 ); + Identity (); + cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz; + } + void Translate( Vector3d a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; } + void SetTranslation( Vector3d a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; } + void Concatenate( const Matrix4d & m2 ) + { + Matrix4d res; + int c; + for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ ) + res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] + + cell[r * 4 + 1] * m2.cell[c + 4] + + cell[r * 4 + 2] * m2.cell[c + 8] + + cell[r * 4 + 3] * m2.cell[c + 12]; + for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c]; + } + Vector3d Transform( const Vector3d & v ) const + { + double x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3]; + double y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7]; + double z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11]; + return Vector3d( x, y, z ); + } + void Invert() + { + Matrix4d t; + int h, i; + double tx = -cell[3], ty = -cell[7], tz = -cell[11]; + for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4]; + for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i]; + cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2]; + cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6]; + cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10]; + } + Vector3d GetXColumn() { return Vector3d( cell[0], cell[1], cell[2] ); } + Vector3d GetYColumn() { return Vector3d( cell[4], cell[5], cell[6] ); } + Vector3d GetZColumn() { return Vector3d( cell[8], cell[9], cell[10] ); } + void SetXColumn( const Vector3d & a_X ) + { + cell[0] = a_X.x; + cell[1] = a_X.y; + cell[2] = a_X.z; + } + void SetYColumn( const Vector3d & a_Y ) + { + cell[4] = a_Y.x; + cell[5] = a_Y.y; + cell[6] = a_Y.z; + } + void SetZColumn( const Vector3d & a_Z ) + { + cell[8] = a_Z.x; + cell[9] = a_Z.y; + cell[10] = a_Z.z; + } + double cell[16]; +} ; + + + + diff --git a/src/MemoryLeak.h b/src/MemoryLeak.h new file mode 100644 index 000000000..e9c0c34e3 --- /dev/null +++ b/src/MemoryLeak.h @@ -0,0 +1,19 @@ +#pragma once + +#ifdef _WIN32 + #ifdef _DEBUG + // Enable the CRT debugging features: + #define _CRTDBG_MAP_ALLOC + #include + #include + + // This works only in MSVC 2010+: + #if _MSC_VER >= 1600 + // Map the new operator + #ifndef DEBUG_NEW + #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) + #define new DEBUG_NEW + #endif // _CRTDBG_MAP_ALLOC + #endif // _MSC_VER + #endif // _DEBUG +#endif // _WIN32 diff --git a/src/MersenneTwister.h b/src/MersenneTwister.h new file mode 100644 index 000000000..dc7134a93 --- /dev/null +++ b/src/MersenneTwister.h @@ -0,0 +1,456 @@ +// MersenneTwister.h +// Mersenne Twister random number generator -- a C++ class MTRand +// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus +// Richard J. Wagner v1.1 28 September 2009 wagnerr@umich.edu + +// The Mersenne Twister is an algorithm for generating random numbers. It +// was designed with consideration of the flaws in various other generators. +// The period, 2^19937-1, and the order of equidistribution, 623 dimensions, +// are far greater. The generator is also fast; it avoids multiplication and +// division, and it benefits from caches and pipelines. For more information +// see the inventors' web page at +// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + +// Reference +// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally +// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on +// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. + +// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, +// Copyright (C) 2000 - 2009, Richard J. Wagner +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The names of its contributors may not be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef MERSENNETWISTER_H +#define MERSENNETWISTER_H + +// Not thread safe (unless auto-initialization is avoided and each thread has +// its own MTRand object) + +#include +#include +#include +#include +#include + +class MTRand { +// Data +public: + typedef long uint32; // unsigned integer type, at least 32 bits + + enum { N = 624 }; // length of state vector + enum { SAVE = N + 1 }; // length of array for save() + +protected: + enum { M = 397 }; // period parameter + + uint32 state[N]; // internal state + uint32 *pNext; // next value to get from state + int left; // number of values left before reload needed + +// Methods +public: + MTRand( const uint32 oneSeed ); // initialize with a simple uint32 + MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or array + MTRand(); // auto-initialize with /dev/urandom or time() and clock() + MTRand( const MTRand& o ); // copy + + // Do NOT use for CRYPTOGRAPHY without securely hashing several returned + // values together, otherwise the generator state can be learned after + // reading 624 consecutive values. + + // Access to 32-bit random numbers + uint32 randInt(); // integer in [0,2^32-1] + uint32 randInt( const uint32 n ); // integer in [0,n] for n < 2^32 + double rand(); // real number in [0,1] + double rand( const double n ); // real number in [0,n] + double randExc(); // real number in [0,1) + double randExc( const double n ); // real number in [0,n) + double randDblExc(); // real number in (0,1) + double randDblExc( const double n ); // real number in (0,n) + double operator()(); // same as rand() + + // Access to 53-bit random numbers (capacity of IEEE double precision) + double rand53(); // real number in [0,1) + + // Access to nonuniform random number distributions + double randNorm( const double mean = 0.0, const double stddev = 1.0 ); + + // Re-seeding functions with same behavior as initializers + void seed( const uint32 oneSeed ); + void seed( uint32 *const bigSeed, const uint32 seedLength = N ); + void seed(); + + // Saving and loading generator state + void save( uint32* saveArray ) const; // to array of size SAVE + void load( uint32 *const loadArray ); // from such array + friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); + friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); + MTRand& operator=( const MTRand& o ); + +protected: + void initialize( const uint32 oneSeed ); + void reload(); + uint32 hiBit( const uint32 u ) const { return u & 0x80000000UL; } + uint32 loBit( const uint32 u ) const { return u & 0x00000001UL; } + uint32 loBits( const uint32 u ) const { return u & 0x7fffffffUL; } + uint32 mixBits( const uint32 u, const uint32 v ) const + { return hiBit(u) | loBits(v); } + uint32 magic( const uint32 u ) const + { return loBit(u) ? 0x9908b0dfUL : 0x0UL; } + uint32 twist( const uint32 m, const uint32 s0, const uint32 s1 ) const + { return m ^ (mixBits(s0,s1)>>1) ^ magic(s1); } + static uint32 hash( time_t t, clock_t c ); +}; + +// Functions are defined in order of usage to assist inlining + +inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) +{ + // Get a uint32 from t and c + // Better than uint32(x) in case x is floating point in [0,1] + // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) + + static uint32 differ = 0; // guarantee time-based seeds will change + + uint32 h1 = 0; + unsigned char *p = (unsigned char *) &t; + for( size_t i = 0; i < sizeof(t); ++i ) + { + h1 *= UCHAR_MAX + 2U; + h1 += p[i]; + } + uint32 h2 = 0; + p = (unsigned char *) &c; + for( size_t j = 0; j < sizeof(c); ++j ) + { + h2 *= UCHAR_MAX + 2U; + h2 += p[j]; + } + return ( h1 + differ++ ) ^ h2; +} + +inline void MTRand::initialize( const uint32 seed ) +{ + // Initialize generator state with seed + // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + // In previous versions, most significant bits (MSBs) of the seed affect + // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. + register uint32 *s = state; + register uint32 *r = state; + register int i = 1; + *s++ = seed & 0xffffffffUL; + for( ; i < N; ++i ) + { + *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; + r++; + } +} + +inline void MTRand::reload() +{ + // Generate N new values in state + // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) + static const int MmN = int(M) - int(N); // in case enums are unsigned + register uint32 *p = state; + register int i; + for( i = N - M; i--; ++p ) + *p = twist( p[M], p[0], p[1] ); + for( i = M; --i; ++p ) + *p = twist( p[MmN], p[0], p[1] ); + *p = twist( p[MmN], p[0], state[0] ); + + left = N, pNext = state; +} + +inline void MTRand::seed( const uint32 oneSeed ) +{ + // Seed the generator with a simple uint32 + initialize(oneSeed); + reload(); +} + +inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) +{ + // Seed the generator with an array of uint32's + // There are 2^19937-1 possible initial states. This function allows + // all of those to be accessed by providing at least 19937 bits (with a + // default seed length of N = 624 uint32's). Any bits above the lower 32 + // in each element are discarded. + // Just call seed() if you want to get array from /dev/urandom + initialize(19650218UL); + register int i = 1; + register uint32 j = 0; + register int k = ( N > seedLength ? N : seedLength ); + for( ; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); + state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; + state[i] &= 0xffffffffUL; + ++i; ++j; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + if( j >= seedLength ) j = 0; + } + for( k = N - 1; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); + state[i] -= i; + state[i] &= 0xffffffffUL; + ++i; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + } + state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array + reload(); +} + +inline void MTRand::seed() +{ + // Seed the generator with an array from /dev/urandom if available + // Otherwise use a hash of time() and clock() values + + // First try getting an array from /dev/urandom + + /* // Commented out by FakeTruth because doing this 200 times a tick is SUUUUPEERRR SLOW!!~~!ÕNe + FILE* urandom = fopen( "/dev/urandom", "rb" ); + if( urandom ) + { + uint32 bigSeed[N]; + register uint32 *s = bigSeed; + register int i = N; + register bool success = true; + while( success && i-- ) + success = fread( s++, sizeof(uint32), 1, urandom ); + fclose(urandom); + if( success ) { seed( bigSeed, N ); return; } + } + */ + + // Was not successful, so use time() and clock() instead + seed( hash( time(NULL), clock() ) ); +} + +inline MTRand::MTRand( const uint32 oneSeed ) + { seed(oneSeed); } + +inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) + { seed(bigSeed,seedLength); } + +inline MTRand::MTRand() + { seed(); } + +inline MTRand::MTRand( const MTRand& o ) +{ + register const uint32 *t = o.state; + register uint32 *s = state; + register int i = N; + for( ; i--; *s++ = *t++ ) {} + left = o.left; + pNext = &state[N-left]; +} + +inline MTRand::uint32 MTRand::randInt() +{ + // Pull a 32-bit integer from the generator state + // Every other access function simply transforms the numbers extracted here + + if( left == 0 ) reload(); + --left; + + register uint32 s1; + s1 = *pNext++; + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9d2c5680UL; + s1 ^= (s1 << 15) & 0xefc60000UL; + return ( s1 ^ (s1 >> 18) ); +} + +inline MTRand::uint32 MTRand::randInt( const uint32 n ) +{ + // Find which bits are used in n + // Optimized by Magnus Jonsson (magnus@smartelectronix.com) + uint32 used = n; + used |= used >> 1; + used |= used >> 2; + used |= used >> 4; + used |= used >> 8; + used |= used >> 16; + + // Draw numbers until one is found in [0,n] + uint32 i; + do + i = randInt() & used; // toss unused bits to shorten search + while( i > n ); + return i; +} + +inline double MTRand::rand() + { return double(randInt()) * (1.0/4294967295.0); } + +inline double MTRand::rand( const double n ) + { return rand() * n; } + +inline double MTRand::randExc() + { return double(randInt()) * (1.0/4294967296.0); } + +inline double MTRand::randExc( const double n ) + { return randExc() * n; } + +inline double MTRand::randDblExc() + { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } + +inline double MTRand::randDblExc( const double n ) + { return randDblExc() * n; } + +inline double MTRand::rand53() +{ + uint32 a = randInt() >> 5, b = randInt() >> 6; + return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada +} + +inline double MTRand::randNorm( const double mean, const double stddev ) +{ + // Return a real number from a normal (Gaussian) distribution with given + // mean and standard deviation by polar form of Box-Muller transformation + double x, y, r; + do + { + x = 2.0 * rand() - 1.0; + y = 2.0 * rand() - 1.0; + r = x * x + y * y; + } + while ( r >= 1.0 || r == 0.0 ); + double s = sqrt( -2.0 * log(r) / r ); + return mean + x * s * stddev; +} + +inline double MTRand::operator()() +{ + return rand(); +} + +inline void MTRand::save( uint32* saveArray ) const +{ + register const uint32 *s = state; + register uint32 *sa = saveArray; + register int i = N; + for( ; i--; *sa++ = *s++ ) {} + *sa = left; +} + +inline void MTRand::load( uint32 *const loadArray ) +{ + register uint32 *s = state; + register uint32 *la = loadArray; + register int i = N; + for( ; i--; *s++ = *la++ ) {} + left = *la; + pNext = &state[N-left]; +} + +inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) +{ + register const MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; os << *s++ << "\t" ) {} + return os << mtrand.left; +} + +inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) +{ + register MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; is >> *s++ ) {} + is >> mtrand.left; + mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; + return is; +} + +inline MTRand& MTRand::operator=( const MTRand& o ) +{ + if( this == &o ) return (*this); + register const uint32 *t = o.state; + register uint32 *s = state; + register int i = N; + for( ; i--; *s++ = *t++ ) {} + left = o.left; + pNext = &state[N-left]; + return (*this); +} + +#endif // MERSENNETWISTER_H + +// Change log: +// +// v0.1 - First release on 15 May 2000 +// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus +// - Translated from C to C++ +// - Made completely ANSI compliant +// - Designed convenient interface for initialization, seeding, and +// obtaining numbers in default or user-defined ranges +// - Added automatic seeding from /dev/urandom or time() and clock() +// - Provided functions for saving and loading generator state +// +// v0.2 - Fixed bug which reloaded generator one step too late +// +// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew +// +// v0.4 - Removed trailing newline in saved generator format to be consistent +// with output format of built-in types +// +// v0.5 - Improved portability by replacing static const int's with enum's and +// clarifying return values in seed(); suggested by Eric Heimburg +// - Removed MAXINT constant; use 0xffffffffUL instead +// +// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits +// - Changed integer [0,n] generator to give better uniformity +// +// v0.7 - Fixed operator precedence ambiguity in reload() +// - Added access for real numbers in (0,1) and (0,n) +// +// v0.8 - Included time.h header to properly support time_t and clock_t +// +// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto +// - Allowed for seeding with arrays of any length +// - Added access for real numbers in [0,1) with 53-bit resolution +// - Added access for real numbers from normal (Gaussian) distributions +// - Increased overall speed by optimizing twist() +// - Doubled speed of integer [0,n] generation +// - Fixed out-of-range number generation on 64-bit machines +// - Improved portability by substituting literal constants for long enum's +// - Changed license from GNU LGPL to BSD +// +// v1.1 - Corrected parameter label in randNorm from "variance" to "stddev" +// - Changed randNorm algorithm from basic to polar form for efficiency +// - Updated includes from deprecated to standard forms +// - Cleaned declarations and definitions to please Intel compiler +// - Revised twist() operator to work on ones'-complement machines +// - Fixed reload() function to work when N and M are unsigned +// - Added copy constructor and copy operator from Salvador Espana diff --git a/src/MobCensus.cpp b/src/MobCensus.cpp new file mode 100644 index 000000000..66b5932bc --- /dev/null +++ b/src/MobCensus.cpp @@ -0,0 +1,92 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobCensus.h" + + + + + +void cMobCensus::CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Distance) +{ + m_ProximityCounter.CollectMob(a_Monster, a_Chunk, a_Distance); + m_MobFamilyCollecter.CollectMob(a_Monster); +} + + + + + +bool cMobCensus::IsCapped(cMonster::eFamily a_MobFamily) +{ + bool toReturn = true; + const int ratio = 319; // this should be 256 as we are only supposed to take account from chunks that are in 17x17 from a player + // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19*19) / (17*17) = 319 + // MG TODO : code the correct count + if ((GetCapMultiplier(a_MobFamily) * GetNumChunks()) / ratio >= m_MobFamilyCollecter.GetNumberOfCollectedMobs(a_MobFamily)) + { + return false; + } + return true; +} + + + + + +int cMobCensus::GetCapMultiplier(cMonster::eFamily a_MobFamily) +{ + switch (a_MobFamily) + { + case cMonster::mfHostile: return 79; + case cMonster::mfPassive: return 11; + case cMonster::mfAmbient: return 16; + case cMonster::mfWater: return 5; + } + ASSERT(!"Unhandled mob family"); + return -1; +} + + + + + +void cMobCensus::CollectSpawnableChunk(cChunk & a_Chunk) +{ + m_EligibleForSpawnChunks.insert(&a_Chunk); +} + + + + + +int cMobCensus::GetNumChunks(void) +{ + return m_EligibleForSpawnChunks.size(); +} + + + + + +cMobProximityCounter & cMobCensus::GetProximityCounter(void) +{ + return m_ProximityCounter; +} + + + + + +void cMobCensus::Logd() +{ + LOGD("Hostile mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfHostile), IsCapped(cMonster::mfHostile) ? "(capped)" : ""); + LOGD("Ambient mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfAmbient), IsCapped(cMonster::mfAmbient) ? "(capped)" : ""); + LOGD("Water mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfWater), IsCapped(cMonster::mfWater) ? "(capped)" : ""); + LOGD("Passive mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfPassive), IsCapped(cMonster::mfPassive) ? "(capped)" : ""); +} + + + + + diff --git a/src/MobCensus.h b/src/MobCensus.h new file mode 100644 index 000000000..e3892bec6 --- /dev/null +++ b/src/MobCensus.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "MobProximityCounter.h" +#include "MobFamilyCollecter.h" + + + + +// fwd: +class cChunk; +class cMonster; + + + + + +/** This class is used to collect information, for each Mob, what is the distance of the closest player +it was first being designed in order to make mobs spawn / despawn / act +as the behaviour and even life of mobs depends on the distance to closest player + +as side effect : it also collect the chunks that are elligible for spawning +as side effect 2 : it also know the caps for mobs number and can compare census to this numbers +*/ +class cMobCensus +{ +public: + /// Returns the nested proximity counter + cMobProximityCounter & GetProximityCounter(void); + + // collect an elligible Chunk for Mob Spawning + // MG TODO : code the correct rule (not loaded chunk but short distant from players) + void CollectSpawnableChunk(cChunk & a_Chunk); + + /// Collect a mob - it's distance to player, it's family ... + void CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance); + + /// Returns true if the family is capped (i.e. there are more mobs of this family than max) + bool IsCapped(cMonster::eFamily a_MobFamily); + + /// log the results of census to server console + void Logd(void); + +protected : + cMobProximityCounter m_ProximityCounter; + cMobFamilyCollecter m_MobFamilyCollecter; + + std::set m_EligibleForSpawnChunks; + + /// Returns the number of chunks that are elligible for spawning (for now, the loaded, valid chunks) + int GetNumChunks(); + + /// Returns the cap multiplier value of the given monster family + static int GetCapMultiplier(cMonster::eFamily a_MobFamily); +} ; + + + + diff --git a/src/MobFamilyCollecter.cpp b/src/MobFamilyCollecter.cpp new file mode 100644 index 000000000..e9c69e078 --- /dev/null +++ b/src/MobFamilyCollecter.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobFamilyCollecter.h" +#include "Mobs/Monster.h" + + + +void cMobFamilyCollecter::CollectMob(cMonster & a_Monster) +{ + cMonster::eFamily MobFamily = a_Monster.GetMobFamily(); + m_Mobs[MobFamily].insert(&a_Monster); +} + + + + + +int cMobFamilyCollecter::GetNumberOfCollectedMobs(cMonster::eFamily a_Family) +{ + return m_Mobs[a_Family].size(); +} + + + + diff --git a/src/MobFamilyCollecter.h b/src/MobFamilyCollecter.h new file mode 100644 index 000000000..6cef133b5 --- /dev/null +++ b/src/MobFamilyCollecter.h @@ -0,0 +1,39 @@ + +#pragma once + +#include +#include +#include "BlockID.h" +#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it + + + + +// fwd: +class cChunk; + + + + + +/** This class is used to collect the list of mobs for each family +*/ +class cMobFamilyCollecter +{ +public : + typedef const std::set tMobFamilyList; + + // collect a mob + void CollectMob(cMonster & a_Monster); + + // return the number of mobs for this family + int GetNumberOfCollectedMobs(cMonster::eFamily a_Family); + +protected : + std::map > m_Mobs; + +} ; + + + + diff --git a/src/MobProximityCounter.cpp b/src/MobProximityCounter.cpp new file mode 100644 index 000000000..583a71579 --- /dev/null +++ b/src/MobProximityCounter.cpp @@ -0,0 +1,83 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobProximityCounter.h" + +#include "Entities/Entity.h" +#include "Chunk.h" + +void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance) +{ +// LOGD("Collecting monster %s, with distance %f",a_Monster->GetClass(),a_Distance); + tMonsterToDistance::iterator it = m_MonsterToDistance.find(&a_Monster); + if (it == m_MonsterToDistance.end()) + { + sDistanceAndChunk newDistanceAndChunk(a_Distance,a_Chunk); + std::pair result = m_MonsterToDistance.insert(tMonsterToDistance::value_type(&a_Monster,newDistanceAndChunk)); + if (!result.second) + { + ASSERT(!"A collected Monster was not found inside distance map using find(), but insert() said there already is a key for it"); + } + } + else + { + if (a_Distance < it->second.m_Distance) + { + it->second.m_Distance = a_Distance; + it->second.m_Chunk = a_Chunk; + } + } + + m_EligibleForSpawnChunks.insert(&a_Chunk); + +} + +void cMobProximityCounter::convertMaps() +{ + for(tMonsterToDistance::const_iterator itr = m_MonsterToDistance.begin(); itr != m_MonsterToDistance.end(); itr++) + { + m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,itr->second.m_Chunk))); + } +} + +cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax) +{ + sIterablePair toReturn; + toReturn.m_Count = 0; + toReturn.m_Begin = m_DistanceToMonster.end(); + toReturn.m_End = m_DistanceToMonster.end(); + + a_DistanceMin *= a_DistanceMin;// this is because is use square distance + a_DistanceMax *= a_DistanceMax; + + if (m_DistanceToMonster.size() <= 0) + { + convertMaps(); + } + + for(tDistanceToMonster::const_iterator itr = m_DistanceToMonster.begin(); itr != m_DistanceToMonster.end(); itr++) + { + if (toReturn.m_Begin == m_DistanceToMonster.end()) + { + if (a_DistanceMin == -1 || itr->first > a_DistanceMin) + { + toReturn.m_Begin = itr; // this is the first one with distance > a_DistanceMin; + } + } + + if (toReturn.m_Begin != m_DistanceToMonster.end()) + { + if (a_DistanceMax != -1 && itr->first > a_DistanceMax) + { + toReturn.m_End = itr; // this is just after the last one with distance < a_DistanceMax + // Note : if we are not going through this, it's ok, toReturn.m_End will be end(); + break; + } + else + { + toReturn.m_Count ++; + } + } + } + return toReturn; +} diff --git a/src/MobProximityCounter.h b/src/MobProximityCounter.h new file mode 100644 index 000000000..8a67139aa --- /dev/null +++ b/src/MobProximityCounter.h @@ -0,0 +1,65 @@ + +#pragma once + +#include + +class cChunk; +class cEntity; + + +// This class is used to collect, for each Mob, what is the distance of the closest player +// it was first being designed in order to make mobs spawn / despawn / act +// as the behaviour and even life of mobs depends on the distance to closest player +class cMobProximityCounter +{ +protected : + // structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster) + struct sDistanceAndChunk + { + sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(a_Chunk) {} + double m_Distance; + cChunk& m_Chunk; + }; + struct sMonsterAndChunk + { + sMonsterAndChunk(cEntity& a_Monster, cChunk& a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {} + cEntity& m_Monster; + cChunk& m_Chunk; + }; + +public : + typedef std::map tMonsterToDistance; + typedef std::multimap tDistanceToMonster; + +protected : + // this map is filled during collection phase, it will be later transformed into DistanceToMonster + tMonsterToDistance m_MonsterToDistance; + + // this map is generated after collection phase, in order to access monster by distance to player + tDistanceToMonster m_DistanceToMonster; + + // this are the collected chunks. Used to determinate the number of elligible chunk for spawning. + std::set m_EligibleForSpawnChunks; + +protected : + // transform monsterToDistance map (that was usefull for collecting) into distanceToMonster + // that will be usefull for picking up. + void convertMaps(); + +public : + // count a mob on a specified chunk with specified distance to an unkown player + // if the distance is shortest than the one collected, this become the new closest + // distance and the chunk become the "hosting" chunk (that is the one that will perform the action) + void CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance); + + // return the mobs that are within the range of distance of the closest player they are + // that means that if a mob is 30 m from a player and 150 m from another one. It will be + // in the range [0..50] but not in [100..200] + struct sIterablePair{ + tDistanceToMonster::const_iterator m_Begin; + tDistanceToMonster::const_iterator m_End; + int m_Count; + }; + sIterablePair getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax); + +}; diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp new file mode 100644 index 000000000..4d0b2777b --- /dev/null +++ b/src/MobSpawner.cpp @@ -0,0 +1,361 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobSpawner.h" +#include "Mobs/IncludeAllMonsters.h" + + + + + +cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily,const std::set& a_AllowedTypes) : + m_MonsterFamily(a_MonsterFamily), + m_NewPack(true), + m_MobType(cMonster::mtInvalidType) +{ + for (std::set::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); itr++) + { + if (cMonster::FamilyFromType(*itr) == a_MonsterFamily) + { + m_AllowedTypes.insert(*itr); + } + } +} + + + + + +bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType) +{ + // Packs of non-water mobs can only be centered on an air block + // Packs of water mobs can only be centered on a water block + if (m_MonsterFamily == cMonster::mfWater) + { + return IsBlockWater(a_BlockType); + } + else + { + return a_BlockType == E_BLOCK_AIR; + } +} + + + + + +void cMobSpawner::addIfAllowed(cMonster::eType toAdd, std::set& toAddIn) +{ + std::set::iterator itr = m_AllowedTypes.find(toAdd); + if (itr != m_AllowedTypes.end()) + { + toAddIn.insert(toAdd); + } +} + + + + + +cMonster::eType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) +{ + std::set allowedMobs; + + if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore) + { + addIfAllowed(cMonster::mtMooshroom, allowedMobs); + } + else if (a_Biome == biNether) + { + addIfAllowed(cMonster::mtGhast, allowedMobs); + addIfAllowed(cMonster::mtZombiePigman, allowedMobs); + addIfAllowed(cMonster::mtMagmaCube, allowedMobs); + } + else if (a_Biome == biEnd) + { + addIfAllowed(cMonster::mtEnderman, allowedMobs); + } + else + { + addIfAllowed(cMonster::mtBat, allowedMobs); + addIfAllowed(cMonster::mtSpider, allowedMobs); + addIfAllowed(cMonster::mtZombie, allowedMobs); + addIfAllowed(cMonster::mtSkeleton, allowedMobs); + addIfAllowed(cMonster::mtCreeper, allowedMobs); + addIfAllowed(cMonster::mtSquid, allowedMobs); + + if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean) + { + addIfAllowed(cMonster::mtSheep, allowedMobs); + addIfAllowed(cMonster::mtPig, allowedMobs); + addIfAllowed(cMonster::mtCow, allowedMobs); + addIfAllowed(cMonster::mtChicken, allowedMobs); + addIfAllowed(cMonster::mtEnderman, allowedMobs); + addIfAllowed(cMonster::mtSlime, allowedMobs); // MG TODO : much more complicated rule + + if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills) + { + addIfAllowed(cMonster::mtWolf, allowedMobs); + } + else if (a_Biome == biJungle || a_Biome == biJungleHills) + { + addIfAllowed(cMonster::mtOcelot, allowedMobs); + } + } + } + + int allowedMobsSize = allowedMobs.size(); + if (allowedMobsSize > 0) + { + std::set::iterator itr = allowedMobs.begin(); + int iRandom = m_Random.NextInt(allowedMobsSize,a_Biome); + + for(int i = 0; i < iRandom; i++) + { + itr++; + } + + return *itr; + } + return cMonster::mtInvalidType; +} + + + + + +bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome) +{ + BLOCKTYPE TargetBlock; + if (m_AllowedTypes.find(a_MobType) != m_AllowedTypes.end() && a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock)) + { + NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); + NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); + BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ); + BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); + + SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight); + + switch(a_MobType) + { + case cMonster::mtSquid: + { + return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62); + } + + case cMonster::mtBat: + { + return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && (!g_BlockTransparent[BlockAbove]); + } + + case cMonster::mtChicken: + case cMonster::mtCow: + case cMonster::mtPig: + case cMonster::mtHorse: + case cMonster::mtSheep: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + (BlockBelow == E_BLOCK_GRASS) && + (SkyLight >= 9) + ); + } + + case cMonster::mtOcelot: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + ( + (BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) + ) && + (a_RelY >= 62) && + (m_Random.NextInt(3, a_Biome) != 0) + ); + } + + case cMonster::mtEnderman: + { + if (a_RelY < 250) + { + BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ); + if (BlockTop == E_BLOCK_AIR) + { + BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ); + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (BlockTop == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + (SkyLight <= 7) && + (BlockLight <= 7) + ); + } + } + break; + } + + case cMonster::mtSpider: + { + bool CanSpawn = true; + bool HaveFloor = false; + for (int x = 0; x < 2; ++x) + { + for(int z = 0; z < 2; ++z) + { + CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock); + CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR); + if (!CanSpawn) + { + return false; + } + HaveFloor = ( + HaveFloor || + ( + a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock) && + !g_BlockTransparent[TargetBlock] + ) + ); + } + } + return CanSpawn && HaveFloor && (SkyLight <= 7) && (BlockLight <= 7); + } + + case cMonster::mtCreeper: + case cMonster::mtSkeleton: + case cMonster::mtZombie: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + (SkyLight <= 7) && + (BlockLight <= 7) && + (m_Random.NextInt(2, a_Biome) == 0) + ); + } + + case cMonster::mtSlime: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + ( + (a_RelY <= 40) || (a_Biome == biSwampland) + ) + ); + } + + case cMonster::mtGhast: + case cMonster::mtZombiePigman: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + (m_Random.NextInt(20, a_Biome) == 0) + ); + } + + case cMonster::mtWolf: + { + return ( + (TargetBlock == E_BLOCK_GRASS) && + (BlockAbove == E_BLOCK_AIR) && + ( + (a_Biome == biTaiga) || + (a_Biome == biTaigaHills) || + (a_Biome == biForest) || + (a_Biome == biForestHills) || + (a_Biome == biColdTaiga) || + (a_Biome == biColdTaigaHills) || + (a_Biome == biTaigaM) || + (a_Biome == biMegaTaiga) || + (a_Biome == biMegaTaigaHills) + ) + ); + } + + default: + { + LOGD("MG TODO: Write spawning rule for mob type %d", a_MobType); + return false; + } + } + } + return false; +} + + + + + +cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize) +{ + cMonster* toReturn = NULL; + if (m_NewPack) + { + m_MobType = ChooseMobType(a_Biome); + if (m_MobType == cMonster::mtInvalidType) + { + return toReturn; + } + if (m_MobType == cMonster::mtWolf) + { + a_MaxPackSize = 8; + } + else if (m_MobType == cMonster::mtGhast) + { + a_MaxPackSize = 1; + } + m_NewPack = false; + } + + // Make sure we are looking at the right chunk to spawn in + a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + + if (CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome)) + { + cMonster * newMob = cMonster::NewMonsterFromType(m_MobType); + if (newMob) + { + m_Spawned.insert(newMob); + } + toReturn = newMob; + } + return toReturn; +} + + + + + +void cMobSpawner::NewPack() +{ + m_NewPack = true; +} + + + + + +cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void) +{ + return m_Spawned; +} + + + + + +bool cMobSpawner::CanSpawnAnything(void) +{ + return !m_AllowedTypes.empty(); +} + + + + diff --git a/src/MobSpawner.h b/src/MobSpawner.h new file mode 100644 index 000000000..ea6636310 --- /dev/null +++ b/src/MobSpawner.h @@ -0,0 +1,76 @@ + +#pragma once + +#include +#include "BlockID.h" +#include "ChunkDef.h" +#include "Chunk.h" +#include "FastRandom.h" +#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it + + + + +// fwd: +class cChunk; + + + + + +/** This class is used to determine which monster can be spawned in which place +it is essentially static (eg. Squids spawn in water, Zombies spawn in dark places) +but it also has dynamic part depending on the world.ini settings. +*/ +class cMobSpawner +{ +public : + // constructor + // a_MobFamily is the Family of mobs that this spawner will spawn + // a_AllowedTypes is the set of types allowed for mobs it will spawn. Empty set + // would result in no spawn at all + // Allowed mobs thah are not of the right Family will not be include (no warning) + cMobSpawner(cMonster::eFamily MobFamily, const std::set & a_AllowedTypes); + + /// Check if specified block can be a Pack center for this spawner + bool CheckPackCenter(BLOCKTYPE a_BlockType); + + // Try to create a monster here + // if this is the first of a Pack : determine the type of monster + // BlockType & BlockMeta are used to decide what kind of Mob can Spawn here + // MaxPackSize is set to the maximal size for a pack this type of mob + cMonster * TryToSpawnHere(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize); + + // mark the beginning of a new Pack + // all mobs of the same Pack are the same type + void NewPack(void); + + // return true if there is at least one allowed type + bool CanSpawnAnything(void); + + typedef const std::set tSpawnedContainer; + tSpawnedContainer & getSpawned(void); + +protected : + // return true if specified type of mob can spawn on specified block + bool CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome); + + // return a random type that can spawn on specified biome. + // returns E_ENTITY_TYPE_DONOTUSE if none is possible + cMonster::eType ChooseMobType(EMCSBiome a_Biome); + + // add toAdd inside toAddIn, if toAdd is in m_AllowedTypes + void addIfAllowed(cMonster::eType toAdd, std::set & toAddIn); + +protected : + cMonster::eFamily m_MonsterFamily; + std::set m_AllowedTypes; + bool m_NewPack; + cMonster::eType m_MobType; + std::set m_Spawned; + cFastRandom m_Random; +} ; + + + + diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp new file mode 100644 index 000000000..cc7e7da2b --- /dev/null +++ b/src/Mobs/AggressiveMonster.cpp @@ -0,0 +1,97 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "AggressiveMonster.h" + +#include "../World.h" +#include "../Vector3f.h" +#include "../Entities/Player.h" +#include "../MersenneTwister.h" + + + + + +cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height), + m_ChaseTime(999999) +{ + m_EMPersonality = AGGRESSIVE; +} + + + + + +// What to do if in Chasing State +void cAggressiveMonster::InStateChasing(float a_Dt) +{ + super::InStateChasing(a_Dt); + m_ChaseTime += a_Dt; + if (m_Target != NULL) + { + if (m_Target->IsPlayer()) + { + cPlayer * Player = (cPlayer *) m_Target; + if (Player->IsGameModeCreative()) + { + m_EMState = IDLE; + return; + } + } + + Vector3f Pos = Vector3f( GetPosition() ); + Vector3f Their = Vector3f( m_Target->GetPosition() ); + if ((Their - Pos).Length() <= m_AttackRange) + { + Attack(a_Dt); + } + MoveToPosition(Their + Vector3f(0, 0.65f, 0)); + } + else if (m_ChaseTime > 5.f) + { + m_ChaseTime = 0; + m_EMState = IDLE; + } +} + + + + + +void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity) +{ + super::EventSeePlayer(a_Entity); + m_EMState = CHASING; +} + + + + + +void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + m_SeePlayerInterval += a_Dt; + + if (m_SeePlayerInterval > 1) + { + int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally + + m_SeePlayerInterval = 0.0; + if (rem >= 2) + { + if (m_EMState == CHASING) + { + CheckEventLostPlayer(); + } + else + { + CheckEventSeePlayer(); + } + } + } +} + + diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h new file mode 100644 index 000000000..5a0d93f3d --- /dev/null +++ b/src/Mobs/AggressiveMonster.h @@ -0,0 +1,30 @@ + +#pragma once + +#include "Monster.h" + + + + + +class cAggressiveMonster : + public cMonster +{ + typedef cMonster super; + +public: + cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + + virtual void Tick (float a_Dt, cChunk & a_Chunk) override; + virtual void InStateChasing(float a_Dt) override; + + virtual void EventSeePlayer(cEntity *) override; + + +protected: + float m_ChaseTime; +} ; + + + + diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp new file mode 100644 index 000000000..b9c82996b --- /dev/null +++ b/src/Mobs/Bat.cpp @@ -0,0 +1,15 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Bat.h" +#include "../Vector3d.h" +#include "../Chunk.h" + + +cBat::cBat(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7) +{ +} + + diff --git a/src/Mobs/Bat.h b/src/Mobs/Bat.h new file mode 100644 index 000000000..e878d0ee8 --- /dev/null +++ b/src/Mobs/Bat.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cBat : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cBat(void); + + CLASS_PROTODEF(cBat); + + bool IsHanging(void) const {return false; } +} ; + + + + diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp new file mode 100644 index 000000000..f9c05b17a --- /dev/null +++ b/src/Mobs/Blaze.cpp @@ -0,0 +1,52 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Blaze.h" +#include "../World.h" + + + + +cBlaze::cBlaze(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8) +{ +} + + + + + +void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD); +} + + + + + +void cBlaze::Attack(float a_Dt) +{ + m_AttackInterval += a_Dt * m_AttackRate; + + if (m_Target != NULL && m_AttackInterval > 3.0) + { + // Setting this higher gives us more wiggle room for attackrate + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + cFireChargeEntity * FireCharge = new cFireChargeEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); + if (FireCharge == NULL) + { + return; + } + if (!FireCharge->Initialize(m_World)) + { + delete FireCharge; + return; + } + m_World->BroadcastSpawnEntity(*FireCharge); + m_AttackInterval = 0.0; + // ToDo: Shoot 3 fireballs instead of 1. + } +} \ No newline at end of file diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h new file mode 100644 index 000000000..cdb3a1306 --- /dev/null +++ b/src/Mobs/Blaze.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cBlaze : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cBlaze(void); + + CLASS_PROTODEF(cBlaze); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Attack(float a_Dt) override; +} ; + + + + diff --git a/src/Mobs/Cavespider.cpp b/src/Mobs/Cavespider.cpp new file mode 100644 index 000000000..aba1ff9f5 --- /dev/null +++ b/src/Mobs/Cavespider.cpp @@ -0,0 +1,40 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Cavespider.h" +#include "../World.h" + + + + + +cCavespider::cCavespider(void) : + super("Cavespider", mtCaveSpider, "mob.spider.say", "mob.spider.death", 0.7, 0.5) +{ +} + + + + + +void cCavespider::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + // TODO: Check vanilla if cavespiders really get passive during the day / in daylight + m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE; +} + + + + + +void cCavespider::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING); + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE); +} + + + + diff --git a/src/Mobs/Cavespider.h b/src/Mobs/Cavespider.h new file mode 100644 index 000000000..10ea03f7b --- /dev/null +++ b/src/Mobs/Cavespider.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cCavespider : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cCavespider(void); + + CLASS_PROTODEF(cCaveSpider); + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp new file mode 100644 index 000000000..087fd088a --- /dev/null +++ b/src/Mobs/Chicken.cpp @@ -0,0 +1,62 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Chicken.h" +#include "../World.h" + + + + + + + + +cChicken::cChicken(void) : + super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4), + m_EggDropTimer(0) +{ +} + + + + +void cChicken::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if ((m_EggDropTimer == 6000) && (m_World->GetTickRandomNumber(1) == 0)) + { + cItems Drops; + m_EggDropTimer = 0; + Drops.push_back(cItem(E_ITEM_EGG, 1)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + } + else if (m_EggDropTimer == 12000) + { + cItems Drops; + m_EggDropTimer = 0; + Drops.push_back(cItem(E_ITEM_EGG, 1)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + } + else + { + m_EggDropTimer++; + } +} + + + + + +void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_FEATHER); + a_Drops.push_back(cItem(IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN, 1)); +} + + + + + + + + diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h new file mode 100644 index 000000000..979c4d8a0 --- /dev/null +++ b/src/Mobs/Chicken.h @@ -0,0 +1,29 @@ +#pragma once + +#include "PassiveMonster.h" + + + + + +class cChicken : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cChicken(void); + + CLASS_PROTODEF(cChicken); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +private: + + + int m_EggDropTimer; +} ; + + + diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp new file mode 100644 index 000000000..9eb74dac2 --- /dev/null +++ b/src/Mobs/Cow.cpp @@ -0,0 +1,45 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Cow.h" +#include "../Entities/Player.h" + + + + + + + +cCow::cCow(void) : + super("Cow", mtCow, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) +{ +} + + + + + +void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); + AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); +} + + + + + +void cCow::OnRightClicked(cPlayer & a_Player) +{ + if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_BUCKET)) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + a_Player.GetInventory().AddItem(E_ITEM_MILK); + } + } +} + + + diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h new file mode 100644 index 000000000..0391d4a31 --- /dev/null +++ b/src/Mobs/Cow.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cCow : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cCow(); + + CLASS_PROTODEF(cCow); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void OnRightClicked(cPlayer & a_Player) override; +} ; + + + + diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp new file mode 100644 index 000000000..4e11ae13e --- /dev/null +++ b/src/Mobs/Creeper.cpp @@ -0,0 +1,47 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Creeper.h" +#include "../World.h" + + + + + +cCreeper::cCreeper(void) : + super("Creeper", mtCreeper, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8), + m_bIsBlowing(false), + m_bIsCharged(false) +{ +} + + + + + +void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER); + + // TODO Check if killed by a skeleton, then drop random music disk +} + + + + + +void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + + if (a_TDI.DamageType == dtLightning) + { + m_bIsCharged = true; + } + + m_World->BroadcastEntityMetadata(*this); +} + + + + diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h new file mode 100644 index 000000000..c3d4edeae --- /dev/null +++ b/src/Mobs/Creeper.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cCreeper : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cCreeper(void); + + CLASS_PROTODEF(cCreeper); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + + bool IsBlowing(void) const {return m_bIsBlowing; } + bool IsCharged(void) const {return m_bIsCharged; } + +private: + + bool m_bIsBlowing, m_bIsCharged; + +} ; + + + + diff --git a/src/Mobs/EnderDragon.cpp b/src/Mobs/EnderDragon.cpp new file mode 100644 index 000000000..acd81cde1 --- /dev/null +++ b/src/Mobs/EnderDragon.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "EnderDragon.h" + + + + + +cEnderDragon::cEnderDragon(void) : + // TODO: Vanilla source says this, but is it right? Dragons fly, they don't stand + super("EnderDragon", mtEnderDragon, "mob.enderdragon.hit", "mob.enderdragon.end", 16.0, 8.0) +{ +} + + + + + +void cEnderDragon::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + return; +} + + + + diff --git a/src/Mobs/EnderDragon.h b/src/Mobs/EnderDragon.h new file mode 100644 index 000000000..77177edfe --- /dev/null +++ b/src/Mobs/EnderDragon.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cEnderDragon : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cEnderDragon(void); + + CLASS_PROTODEF(cEnderDragon); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp new file mode 100644 index 000000000..a784131e4 --- /dev/null +++ b/src/Mobs/Enderman.cpp @@ -0,0 +1,29 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Enderman.h" + + + + + +cEnderman::cEnderman(void) : + super("Enderman", mtEnderman, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9), + m_bIsScreaming(false), + CarriedBlock(E_BLOCK_AIR), + CarriedMeta(0) +{ +} + + + + + +void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ENDER_PEARL); +} + + + + diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h new file mode 100644 index 000000000..32e40e70b --- /dev/null +++ b/src/Mobs/Enderman.h @@ -0,0 +1,36 @@ + +#pragma once + +#include "PassiveAggressiveMonster.h" + + + + + +class cEnderman : + public cPassiveAggressiveMonster +{ + typedef cPassiveAggressiveMonster super; + +public: + cEnderman(void); + + CLASS_PROTODEF(cEnderman); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + + bool IsScreaming(void) const {return m_bIsScreaming; } + BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } + NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; } + +private: + + bool m_bIsScreaming; + BLOCKTYPE CarriedBlock; + NIBBLETYPE CarriedMeta; + +} ; + + + + diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp new file mode 100644 index 000000000..96a29b2d8 --- /dev/null +++ b/src/Mobs/Ghast.cpp @@ -0,0 +1,54 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Ghast.h" +#include "../World.h" + + + + +cGhast::cGhast(void) : + super("Ghast", mtGhast, "mob.ghast.scream", "mob.ghast.death", 4, 4) +{ +} + + + + + +void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER); + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GHAST_TEAR); +} + + + + + +void cGhast::Attack(float a_Dt) +{ + m_AttackInterval += a_Dt * m_AttackRate; + + if (m_Target != NULL && m_AttackInterval > 3.0) + { + // Setting this higher gives us more wiggle room for attackrate + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + cGhastFireballEntity * GhastBall = new cGhastFireballEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); + if (GhastBall == NULL) + { + return; + } + if (!GhastBall->Initialize(m_World)) + { + delete GhastBall; + return; + } + m_World->BroadcastSpawnEntity(*GhastBall); + m_AttackInterval = 0.0; + } +} + + + diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h new file mode 100644 index 000000000..43e8bedb6 --- /dev/null +++ b/src/Mobs/Ghast.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cGhast : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cGhast(void); + + CLASS_PROTODEF(cGhast); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Attack(float a_Dt) override; + + bool IsCharging(void) const {return false; } +} ; + + + + diff --git a/src/Mobs/Giant.cpp b/src/Mobs/Giant.cpp new file mode 100644 index 000000000..f41977535 --- /dev/null +++ b/src/Mobs/Giant.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Giant.h" + + + + + +cGiant::cGiant(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Giant", mtGiant, "mob.zombie.hurt", "mob.zombie.death", 2.0, 13.5) +{ +} + + + + + +void cGiant::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 10, 50, E_ITEM_ROTTEN_FLESH); +} + + + + diff --git a/src/Mobs/Giant.h b/src/Mobs/Giant.h new file mode 100644 index 000000000..356dd4352 --- /dev/null +++ b/src/Mobs/Giant.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cGiant : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cGiant(void); + + CLASS_PROTODEF(cGiant); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp new file mode 100644 index 000000000..bb9a4e3f6 --- /dev/null +++ b/src/Mobs/Horse.cpp @@ -0,0 +1,152 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Horse.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : + super("Horse", mtHorse, "mob.horse.hit", "mob.horse.death", 1.4, 1.6), + m_bHasChest(false), + m_bIsEating(false), + m_bIsRearing(false), + m_bIsMouthOpen(false), + m_bIsTame(false), + m_bIsSaddled(false), + m_Type(Type), + m_Color(Color), + m_Style(Style), + m_Armour(0), + m_TimesToTame(TameTimes), + m_TameAttemptTimes(0), + m_RearTickCount(0) +{ +} + + + + + +void cHorse::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if (!m_bIsMouthOpen) + { + if (m_World->GetTickRandomNumber(50) == 25) + { + m_bIsMouthOpen = true; + } + } + else + { + if (m_World->GetTickRandomNumber(10) == 5) + { + m_bIsMouthOpen = false; + } + } + + if ((m_Attachee != NULL) && (!m_bIsTame)) + { + if (m_TameAttemptTimes < m_TimesToTame) + { + if (m_World->GetTickRandomNumber(50) == 25) + { + m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 0); + m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 2); + m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 6); + m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 8); + + m_Attachee->Detach(); + m_bIsRearing = true; + } + } + else + { + m_bIsTame = true; + } + } + + if (m_bIsRearing) + { + if (m_RearTickCount == 20) + { + m_bIsRearing = false; + m_RearTickCount = 0; + } + else + { + m_RearTickCount++; + } + } + + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cHorse::OnRightClicked(cPlayer & a_Player) +{ + if (!m_bIsSaddled && m_bIsTame) + { + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) + { + // Saddle the horse: + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_bIsSaddled = true; + m_World->BroadcastEntityMetadata(*this); + } + else if (!a_Player.GetEquippedItem().IsEmpty()) + { + // The horse doesn't like being hit, make it rear: + m_bIsRearing = true; + m_RearTickCount = 0; + } + } + else + { + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + return; + } + + m_Attachee->Detach(); + } + + m_TameAttemptTimes++; + a_Player.AttachTo(this); + } +} + + + + + +void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); + if (m_bIsSaddled) + { + a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); + } +} + + + + diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h new file mode 100644 index 000000000..be0c23f9b --- /dev/null +++ b/src/Mobs/Horse.h @@ -0,0 +1,44 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cHorse : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cHorse(int Type, int Color, int Style, int TameTimes); + + CLASS_PROTODEF(cHorse); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + + bool IsSaddled (void) const {return m_bIsSaddled; } + bool IsChested (void) const {return m_bHasChest; } + bool IsEating (void) const {return m_bIsEating; } + bool IsRearing (void) const {return m_bIsRearing; } + bool IsMthOpen (void) const {return m_bIsMouthOpen; } + bool IsTame (void) const {return m_bIsTame; } + int GetHorseType (void) const {return m_Type; } + int GetHorseColor (void) const {return m_Color; } + int GetHorseStyle (void) const {return m_Style; } + int GetHorseArmour (void) const {return m_Armour;} + +private: + + bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled; + int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; + +} ; + + + + diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h new file mode 100644 index 000000000..1b436a11f --- /dev/null +++ b/src/Mobs/IncludeAllMonsters.h @@ -0,0 +1,29 @@ +#include "Bat.h" +#include "Blaze.h" +#include "Cavespider.h" +#include "Chicken.h" +#include "Cow.h" +#include "Creeper.h" +#include "Enderman.h" +#include "EnderDragon.h" +#include "Ghast.h" +#include "Giant.h" +#include "Horse.h" +#include "IronGolem.h" +#include "Magmacube.h" +#include "Mooshroom.h" +#include "Ocelot.h" +#include "Pig.h" +#include "Sheep.h" +#include "Silverfish.h" +#include "Skeleton.h" +#include "Slime.h" +#include "SnowGolem.h" +#include "Spider.h" +#include "Squid.h" +#include "Villager.h" +#include "Witch.h" +#include "Wither.h" +#include "Wolf.h" +#include "Zombie.h" +#include "Zombiepigman.h" diff --git a/src/Mobs/IronGolem.cpp b/src/Mobs/IronGolem.cpp new file mode 100644 index 000000000..47c961098 --- /dev/null +++ b/src/Mobs/IronGolem.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "IronGolem.h" + + + + + +cIronGolem::cIronGolem(void) : + super("IronGolem", mtIronGolem, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9) +{ +} + + + + + +void cIronGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 5, E_ITEM_IRON); +} + + + + diff --git a/src/Mobs/IronGolem.h b/src/Mobs/IronGolem.h new file mode 100644 index 000000000..d49ff4cab --- /dev/null +++ b/src/Mobs/IronGolem.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "PassiveAggressiveMonster.h" + + + + + +class cIronGolem : + public cPassiveAggressiveMonster +{ + typedef cPassiveAggressiveMonster super; + +public: + cIronGolem(void); + + CLASS_PROTODEF(cIronGolem); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Magmacube.cpp b/src/Mobs/Magmacube.cpp new file mode 100644 index 000000000..86447ff6b --- /dev/null +++ b/src/Mobs/Magmacube.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Magmacube.h" + + + + + +cMagmaCube::cMagmaCube(int a_Size) : + super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size), + m_Size(a_Size) +{ +} + + + + + +void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM); +} + + + + diff --git a/src/Mobs/Magmacube.h b/src/Mobs/Magmacube.h new file mode 100644 index 000000000..130952970 --- /dev/null +++ b/src/Mobs/Magmacube.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cMagmaCube : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + /// Creates a MagmaCube of the specified size; size is 1 .. 3, with 1 being the smallest + cMagmaCube(int a_Size); + + CLASS_PROTODEF(cMagmaCube); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + int GetSize(void) const { return m_Size; } + +protected: + + /// Size of the MagmaCube, 1 .. 3, with 1 being the smallest + int m_Size; +} ; + + + + diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp new file mode 100644 index 000000000..8a5717e27 --- /dev/null +++ b/src/Mobs/Monster.cpp @@ -0,0 +1,758 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "IncludeAllMonsters.h" +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../Defines.h" +#include "../MonsterConfig.h" +#include "../MersenneTwister.h" + +#include "../Vector3f.h" +#include "../Vector3i.h" +#include "../Vector3d.h" +#include "../Tracer.h" +#include "../Chunk.h" +#include "../FastRandom.h" + + + + + +/** Map for eType <-> string +Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType() +The strings need to be lowercase (for more efficient comparisons in StringToMobType()) +*/ +static const struct +{ + cMonster::eType m_Type; + const char * m_lcName; +} g_MobTypeNames[] = +{ + {cMonster::mtBat, "bat"}, + {cMonster::mtBlaze, "blaze"}, + {cMonster::mtCaveSpider, "cavespider"}, + {cMonster::mtChicken, "chicken"}, + {cMonster::mtCow, "cow"}, + {cMonster::mtCreeper, "creeper"}, + {cMonster::mtEnderman, "enderman"}, + {cMonster::mtGhast, "ghast"}, + {cMonster::mtHorse, "horse"}, + {cMonster::mtMagmaCube, "magmacube"}, + {cMonster::mtMooshroom, "mooshroom"}, + {cMonster::mtOcelot, "ocelot"}, + {cMonster::mtPig, "pig"}, + {cMonster::mtSheep, "sheep"}, + {cMonster::mtSilverfish, "silverfish"}, + {cMonster::mtSkeleton, "skeleton"}, + {cMonster::mtSlime, "slime"}, + {cMonster::mtSpider, "spider"}, + {cMonster::mtSquid, "squid"}, + {cMonster::mtVillager, "villager"}, + {cMonster::mtWitch, "witch"}, + {cMonster::mtWolf, "wolf"}, + {cMonster::mtZombie, "zombie"}, + {cMonster::mtZombiePigman, "zombiepigman"}, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMonster: + +cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) + : super(etMonster, a_Width, a_Height) + , m_Target(NULL) + , m_AttackRate(3) + , idle_interval(0) + , m_bMovingToDestination(false) + , m_DestinationTime( 0 ) + , m_DestroyTimer( 0 ) + , m_Jump(0) + , m_MobType(a_MobType) + , m_SoundHurt(a_SoundHurt) + , m_SoundDeath(a_SoundDeath) + , m_EMState(IDLE) + , m_SightDistance(25) + , m_SeePlayerInterval (0) + , m_EMPersonality(AGGRESSIVE) + , m_AttackDamage(1.0f) + , m_AttackRange(2.0f) + , m_AttackInterval(0) + , m_BurnsInDaylight(false) +{ + if (!a_ConfigName.empty()) + { + GetMonsterConfig(a_ConfigName); + } +} + + + + + +void cMonster::SpawnOn(cClientHandle & a_Client) +{ + a_Client.SendSpawnMob(*this); +} + + + + + +void cMonster::MoveToPosition( const Vector3f & a_Position ) +{ + m_bMovingToDestination = true; + + m_Destination = a_Position; +} + + + + + +bool cMonster::ReachedDestination() +{ + Vector3f Distance = (m_Destination) - GetPosition(); + if( Distance.SqrLength() < 2.f ) + return true; + + return false; +} + + + + + +void cMonster::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if (m_Health <= 0) + { + // The mob is dead, but we're still animating the "puff" they leave when they die + m_DestroyTimer += a_Dt / 1000; + if (m_DestroyTimer > 1) + { + Destroy(true); + } + return; + } + + // Burning in daylight + HandleDaylightBurning(a_Chunk); + + HandlePhysics(a_Dt,a_Chunk); + BroadcastMovementUpdate(); + + a_Dt /= 1000; + + if (m_bMovingToDestination) + { + Vector3f Pos( GetPosition() ); + Vector3f Distance = m_Destination - Pos; + if( !ReachedDestination() ) + { + Distance.y = 0; + Distance.Normalize(); + Distance *= 3; + SetSpeedX( Distance.x ); + SetSpeedZ( Distance.z ); + + if (m_EMState == ESCAPING) + { //Runs Faster when escaping :D otherwise they just walk away + SetSpeedX (GetSpeedX() * 2.f); + SetSpeedZ (GetSpeedZ() * 2.f); + } + } + else + { + m_bMovingToDestination = false; + } + + if( GetSpeed().SqrLength() > 0.f ) + { + if( m_bOnGround ) + { + Vector3f NormSpeed = Vector3f(GetSpeed()).NormalizeCopy(); + Vector3f NextBlock = Vector3f( GetPosition() ) + NormSpeed; + int NextHeight; + if (!m_World->TryGetHeight((int)NextBlock.x, (int)NextBlock.z, NextHeight)) + { + // The chunk at NextBlock is not loaded + return; + } + if( NextHeight > (GetPosY() - 1.0) && (NextHeight - GetPosY()) < 2.5 ) + { + m_bOnGround = false; + SetSpeedY(5.f); // Jump!! + } + } + } + } + + Vector3d Distance = m_Destination - GetPosition(); + if (Distance.SqrLength() > 0.1f) + { + double Rotation, Pitch; + Distance.Normalize(); + VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch ); + SetHeadYaw (Rotation); + SetRotation( Rotation ); + SetPitch( -Pitch ); + } + + switch (m_EMState) + { + case IDLE: + { + // If enemy passive we ignore checks for player visibility + InStateIdle(a_Dt); + break; + } + + case CHASING: + { + // If we do not see a player anymore skip chasing action + InStateChasing(a_Dt); + break; + } + + case ESCAPING: + { + InStateEscaping(a_Dt); + break; + } + } // switch (m_EMState) +} + + + + + + +void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if((m_SoundHurt != "") && (m_Health > 0)) m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); + if (a_TDI.Attacker != NULL) + { + m_Target = a_TDI.Attacker; + AddReference(m_Target); + } +} + + + + + +void cMonster::KilledBy(cEntity * a_Killer) +{ + super::KilledBy(a_Killer); + if (m_SoundHurt != "") + { + m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); + } + m_DestroyTimer = 0; +} + + + + + +//----State Logic + +const char *cMonster::GetState() +{ + switch(m_EMState) + { + case IDLE: return "Idle"; + case ATTACKING: return "Attacking"; + case CHASING: return "Chasing"; + default: return "Unknown"; + } +} + + + + + +// for debugging +void cMonster::SetState(const AString & a_State) +{ + if (a_State.compare("Idle") == 0) + { + m_EMState = IDLE; + } + else if (a_State.compare("Attacking") == 0) + { + m_EMState = ATTACKING; + } + else if (a_State.compare("Chasing") == 0) + { + m_EMState = CHASING; + } + else + { + LOGD("cMonster::SetState(): Invalid state"); + ASSERT(!"Invalid state"); + } +} + + + + + +//Checks to see if EventSeePlayer should be fired +//monster sez: Do I see the player +void cMonster::CheckEventSeePlayer(void) +{ + // TODO: Rewrite this to use cWorld's DoWithPlayers() + cPlayer * Closest = FindClosestPlayer(); + + if (Closest != NULL) + { + EventSeePlayer(Closest); + } +} + + + + + +void cMonster::CheckEventLostPlayer(void) +{ + Vector3f pos; + cTracer LineOfSight(GetWorld()); + + if (m_Target != NULL) + { + pos = m_Target->GetPosition(); + if ((pos - GetPosition()).Length() > m_SightDistance || LineOfSight.Trace(GetPosition(),(pos - GetPosition()), (int)(pos - GetPosition()).Length())) + { + EventLosePlayer(); + } + } + else + { + EventLosePlayer(); + } +} + + + + + +// What to do if player is seen +// default to change state to chasing +void cMonster::EventSeePlayer(cEntity * a_SeenPlayer) +{ + m_Target = a_SeenPlayer; + AddReference(m_Target); +} + + + + + +void cMonster::EventLosePlayer(void) +{ + Dereference(m_Target); + m_Target = NULL; + m_EMState = IDLE; +} + + + + + +// What to do if in Idle State +void cMonster::InStateIdle(float a_Dt) +{ + idle_interval += a_Dt; + if (idle_interval > 1) + { + // at this interval the results are predictable + int rem = m_World->GetTickRandomNumber(6) + 1; + // LOGD("Moving: int: %3.3f rem: %i",idle_interval,rem); + idle_interval -= 1; // So nothing gets dropped when the server hangs for a few seconds + Vector3f Dist; + Dist.x = (float)(m_World->GetTickRandomNumber(10) - 5); + Dist.z = (float)(m_World->GetTickRandomNumber(10) - 5); + if ((Dist.SqrLength() > 2) && (rem >= 3)) + { + m_Destination.x = (float)(GetPosX() + Dist.x); + m_Destination.z = (float)(GetPosZ() + Dist.z); + int PosY; + if (m_World->TryGetHeight((int)m_Destination.x, (int)m_Destination.z, PosY)) + { + m_Destination.y = (float)PosY + 1.2f; + MoveToPosition(m_Destination); + } + } + } +} + + + + + +// What to do if in Chasing State +// This state should always be defined in each child class +void cMonster::InStateChasing(float a_Dt) +{ + UNUSED(a_Dt); +} + + + + + +// What to do if in Escaping State +void cMonster::InStateEscaping(float a_Dt) +{ + UNUSED(a_Dt); + + if (m_Target != NULL) + { + Vector3d newloc = GetPosition(); + newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); + newloc.z = (m_Target->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); + MoveToPosition(newloc); + } + else + { + m_EMState = IDLE; // This shouldnt be required but just to be safe + } +} + + + + + +// Do attack here +// a_Dt is passed so we can set attack rate +void cMonster::Attack(float a_Dt) +{ + m_AttackInterval += a_Dt * m_AttackRate; + if ((m_Target != NULL) && (m_AttackInterval > 3.0)) + { + // Setting this higher gives us more wiggle room for attackrate + m_AttackInterval = 0.0; + ((cPawn *)m_Target)->TakeDamage(*this); + } +} + + + + + +// Checks for Players close by and if they are visible return the closest +cPlayer * cMonster::FindClosestPlayer(void) +{ + return m_World->FindClosestPlayer(GetPosition(), m_SightDistance); +} + + + + + +void cMonster::GetMonsterConfig(const AString & a_Name) +{ + cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); +} + + + + + +void cMonster::SetAttackRate(int ar) +{ + m_AttackRate = (float)ar; +} + + + + + +void cMonster::SetAttackRange(float ar) +{ + m_AttackRange = ar; +} + + + + + +void cMonster::SetAttackDamage(float ad) +{ + m_AttackDamage = ad; +} + + + + + +void cMonster::SetSightDistance(float sd) +{ + m_SightDistance = sd; +} + + + + + +AString cMonster::MobTypeToString(cMonster::eType a_MobType) +{ + // Mob types aren't sorted, so we need to search linearly: + for (int i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (g_MobTypeNames[i].m_Type == a_MobType) + { + return g_MobTypeNames[i].m_lcName; + } + } + + // Not found: + return ""; +} + + + + + +cMonster::eType cMonster::StringToMobType(const AString & a_Name) +{ + AString lcName(a_Name); + StrToLower(lcName); + + // Binary-search for the lowercase name: + int lo = 0, hi = ARRAYCOUNT(g_MobTypeNames) - 1; + while (hi - lo > 1) + { + int mid = (lo + hi) / 2; + int res = strcmp(g_MobTypeNames[mid].m_lcName, lcName.c_str()); + if (res == 0) + { + return g_MobTypeNames[mid].m_Type; + } + if (res < 0) + { + lo = mid; + } + else + { + hi = mid; + } + } + // Range has collapsed to at most two elements, compare each: + if (strcmp(g_MobTypeNames[lo].m_lcName, lcName.c_str()) == 0) + { + return g_MobTypeNames[lo].m_Type; + } + if ((lo != hi) && (strcmp(g_MobTypeNames[hi].m_lcName, lcName.c_str()) == 0)) + { + return g_MobTypeNames[hi].m_Type; + } + + // Not found: + return mtInvalidType; +} + + + + + +cMonster::eFamily cMonster::FamilyFromType(eType a_Type) +{ + switch (a_Type) + { + case mtBat: return mfAmbient; + case mtBlaze: return mfHostile; + case mtCaveSpider: return mfHostile; + case mtChicken: return mfPassive; + case mtCow: return mfPassive; + case mtCreeper: return mfHostile; + case mtEnderman: return mfHostile; + case mtGhast: return mfHostile; + case mtHorse: return mfPassive; + case mtMagmaCube: return mfHostile; + case mtMooshroom: return mfHostile; + case mtOcelot: return mfHostile; + case mtPig: return mfPassive; + case mtSheep: return mfPassive; + case mtSilverfish: return mfHostile; + case mtSkeleton: return mfHostile; + case mtSlime: return mfHostile; + case mtSpider: return mfHostile; + case mtSquid: return mfWater; + case mtVillager: return mfPassive; + case mtWitch: return mfHostile; + case mtWolf: return mfHostile; + case mtZombie: return mfHostile; + case mtZombiePigman: return mfHostile; + } ; + ASSERT(!"Unhandled mob type"); + return mfMaxplusone; +} + + + + + +int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) +{ + switch (a_MobFamily) + { + case mfHostile: return 40; + case mfPassive: return 40; + case mfAmbient: return 40; + case mfWater: return 400; + } + ASSERT(!"Unhandled mob family"); + return -1; +} + + + + + +cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) +{ + cFastRandom Random; + cMonster * toReturn = NULL; + + // Create the mob entity + switch (a_MobType) + { + case mtMagmaCube: + case mtSlime: + { + toReturn = new cSlime (Random.NextInt(2) + 1); + break; + } + case mtSkeleton: + { + // TODO: Actual detection of spawning in Nether + toReturn = new cSkeleton(Random.NextInt(1) == 0 ? false : true); + break; + } + case mtVillager: + { + int VillagerType = Random.NextInt(6); + if (VillagerType == 6) + { + // Give farmers a better chance of spawning + VillagerType = 0; + } + + toReturn = new cVillager((cVillager::eVillagerType)VillagerType); + break; + } + case mtHorse: + { + // Horses take a type (species), a colour, and a style (dots, stripes, etc.) + int HorseType = Random.NextInt(7); + int HorseColor = Random.NextInt(6); + int HorseStyle = Random.NextInt(6); + int HorseTameTimes = Random.NextInt(6) + 1; + + if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7)) + { + // Increase chances of normal horse (zero) + HorseType = 0; + } + + toReturn = new cHorse(HorseType, HorseColor, HorseStyle, HorseTameTimes); + break; + } + + case mtBat: toReturn = new cBat(); break; + case mtBlaze: toReturn = new cBlaze(); break; + case mtCaveSpider: toReturn = new cCavespider(); break; + case mtChicken: toReturn = new cChicken(); break; + case mtCow: toReturn = new cCow(); break; + case mtCreeper: toReturn = new cCreeper(); break; + case mtEnderman: toReturn = new cEnderman(); break; + case mtGhast: toReturn = new cGhast(); break; + case mtMooshroom: toReturn = new cMooshroom(); break; + case mtOcelot: toReturn = new cOcelot(); break; + case mtPig: toReturn = new cPig(); break; + case mtSheep: toReturn = new cSheep (Random.NextInt(15)); break; // Colour parameter + case mtSilverfish: toReturn = new cSilverfish(); break; + case mtSpider: toReturn = new cSpider(); break; + case mtSquid: toReturn = new cSquid(); break; + case mtWitch: toReturn = new cWitch(); break; + case mtWolf: toReturn = new cWolf(); break; + case mtZombie: toReturn = new cZombie(false); break; // TODO: Infected zombie parameter + case mtZombiePigman: toReturn = new cZombiePigman(); break; + default: + { + ASSERT(!"Unhandled mob type whilst trying to spawn mob!"); + } + } + return toReturn; +} + + + + + +void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth) +{ + MTRand r1; + int Count = r1.randInt() % (a_Max + 1 - a_Min) + a_Min; + if (Count > 0) + { + a_Drops.push_back(cItem(a_Item, Count, a_ItemHealth)); + } +} + + + + + +void cMonster::HandleDaylightBurning(cChunk & a_Chunk) +{ + if (!m_BurnsInDaylight) + { + return; + } + + int RelY = (int)floor(GetPosY()); + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + // Outside the world + return; + } + + int RelX = (int)floor(GetPosX()) - GetChunkX() * cChunkDef::Width; + int RelZ = (int)floor(GetPosZ()) - GetChunkZ() * cChunkDef::Width; + if ( + (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight + (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand + (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime + !IsOnFire() // Not already burning + ) + { + // Burn for 100 ticks, then decide again + StartBurning(100); + } +} + + + + +cMonster::eFamily cMonster::GetMobFamily(void) const +{ + return FamilyFromType(m_MobType); +} + + + + diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h new file mode 100644 index 000000000..29a705d11 --- /dev/null +++ b/src/Mobs/Monster.h @@ -0,0 +1,195 @@ + +#pragma once + +#include "../Entities/Pawn.h" +#include "../Defines.h" +#include "../BlockID.h" +#include "../Item.h" + + + + + +class Vector3f; +class cClientHandle; +class cWorld; + + + + +// tolua_begin +class cMonster : + public cPawn +{ + typedef cPawn super; +public: + /// This identifies individual monster type, as well as their network type-ID + enum eType + { + mtInvalidType = -1, + + mtBat = E_META_SPAWN_EGG_BAT, + mtBlaze = E_META_SPAWN_EGG_BLAZE, + mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER, + mtChicken = E_META_SPAWN_EGG_CHICKEN, + mtCow = E_META_SPAWN_EGG_COW, + mtCreeper = E_META_SPAWN_EGG_CREEPER, + mtEnderDragon = E_META_SPAWN_EGG_ENDER_DRAGON, + mtEnderman = E_META_SPAWN_EGG_ENDERMAN, + mtGhast = E_META_SPAWN_EGG_GHAST, + mtGiant = E_META_SPAWN_EGG_GIANT, + mtHorse = E_META_SPAWN_EGG_HORSE, + mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM, + mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE, + mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM, + mtOcelot = E_META_SPAWN_EGG_OCELOT, + mtPig = E_META_SPAWN_EGG_PIG, + mtSheep = E_META_SPAWN_EGG_SHEEP, + mtSilverfish = E_META_SPAWN_EGG_SILVERFISH, + mtSkeleton = E_META_SPAWN_EGG_SKELETON, + mtSlime = E_META_SPAWN_EGG_SLIME, + mtSnowGolem = E_META_SPAWN_EGG_SNOW_GOLEM, + mtSpider = E_META_SPAWN_EGG_SPIDER, + mtSquid = E_META_SPAWN_EGG_SQUID, + mtVillager = E_META_SPAWN_EGG_VILLAGER, + mtWitch = E_META_SPAWN_EGG_WITCH, + mtWither = E_META_SPAWN_EGG_WITHER, + mtWolf = E_META_SPAWN_EGG_WOLF, + mtZombie = E_META_SPAWN_EGG_ZOMBIE, + mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, + } ; + + enum eFamily + { + mfHostile = 0, // Spider, Zombies ... + mfPassive = 1, // Cows, Pigs + mfAmbient = 2, // Bats + mfWater = 3, // Squid + + mfMaxplusone, // Nothing. Be sure this is the last and the others are in order + } ; + + // tolua_end + + enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; + enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality; + + float m_SightDistance; + + /** Creates the mob object. + * If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig() + * a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22)) + * a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively + */ + cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + + CLASS_PROTODEF(cMonster); + + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + + virtual void KilledBy(cEntity * a_Killer) override; + + virtual void MoveToPosition(const Vector3f & a_Position); + virtual bool ReachedDestination(void); + + // tolua_begin + eType GetMobType(void) const {return m_MobType; } + eFamily GetMobFamily(void) const; + // tolua_end + + + const char * GetState(); + void SetState(const AString & str); + + virtual void CheckEventSeePlayer(void); + virtual void EventSeePlayer(cEntity * a_Player); + virtual cPlayer * FindClosestPlayer(); // non static is easier. also virtual so other mobs can implement their own searching algo + + /// Reads the monster configuration for the specified monster name and assigns it to this object. + void GetMonsterConfig(const AString & a_Name); + + virtual void EventLosePlayer(void); + virtual void CheckEventLostPlayer(void); + + virtual void InStateIdle (float a_Dt); + virtual void InStateChasing (float a_Dt); + virtual void InStateEscaping(float a_Dt); + + virtual void Attack(float a_Dt); + + int GetAttackRate(){return (int)m_AttackRate;} + void SetAttackRate(int ar); + void SetAttackRange(float ar); + void SetAttackDamage(float ad); + void SetSightDistance(float sd); + + /// Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick + void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; } + + // Overridables to handle ageable mobs + virtual bool IsBaby (void) const { return false; } + virtual bool IsTame (void) const { return false; } + virtual bool IsSitting (void) const { return false; } + + // tolua_begin + + /// Translates MobType enum to a string, empty string if unknown + static AString MobTypeToString(eType a_MobType); + + /// Translates MobType string to the enum, mtInvalidType if not recognized + static eType StringToMobType(const AString & a_MobTypeName); + + /// Returns the mob family based on the type + static eFamily FamilyFromType(eType a_MobType); + + /// Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family + static int GetSpawnDelay(cMonster::eFamily a_MobFamily); + + // tolua_end + + /** Creates a new object of the specified mob. + a_MobType is the type of the mob to be created + Asserts and returns null if mob type is not specified + */ + static cMonster * NewMonsterFromType(eType a_MobType); + +protected: + + cEntity * m_Target; + float m_AttackRate; + float idle_interval; + + Vector3f m_Destination; + bool m_bMovingToDestination; + bool m_bPassiveAggressive; + + float m_DestinationTime; + + float m_DestroyTimer; + float m_Jump; + + eType m_MobType; + + AString m_SoundHurt; + AString m_SoundDeath; + + float m_SeePlayerInterval; + float m_AttackDamage; + float m_AttackRange; + float m_AttackInterval; + + bool m_BurnsInDaylight; + + void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0); + + void HandleDaylightBurning(cChunk & a_Chunk); + +} ; // tolua_export + + + + diff --git a/src/Mobs/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp new file mode 100644 index 000000000..940e2db44 --- /dev/null +++ b/src/Mobs/Mooshroom.cpp @@ -0,0 +1,33 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Mooshroom.h" + + + + + +// TODO: Milk Cow + + + + + +cMooshroom::cMooshroom(void) : + super("Mooshroom", mtMooshroom, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) +{ +} + + + + + +void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); + AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); +} + + + + diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h new file mode 100644 index 000000000..73f6348b6 --- /dev/null +++ b/src/Mobs/Mooshroom.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cMooshroom : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cMooshroom(void); + + CLASS_PROTODEF(cMooshroom); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Ocelot.h b/src/Mobs/Ocelot.h new file mode 100644 index 000000000..adb7a1f75 --- /dev/null +++ b/src/Mobs/Ocelot.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cOcelot : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cOcelot(void) : + super("Ocelot", mtOcelot, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8) + { + } + + CLASS_PROTODEF(cOcelot); +} ; + + + + diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp new file mode 100644 index 000000000..28de65905 --- /dev/null +++ b/src/Mobs/PassiveAggressiveMonster.cpp @@ -0,0 +1,38 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "PassiveAggressiveMonster.h" + +#include "../Entities/Player.h" + + + + + +cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) +{ + m_EMPersonality = PASSIVE; +} + + + + + +void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + + if ((m_Target != NULL) && (m_Target->IsPlayer())) + { + cPlayer * Player = (cPlayer *) m_Target; + if (Player->GetGameMode() != 1) + { + m_EMState = CHASING; + } + } +} + + + + diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h new file mode 100644 index 000000000..2c5ef30b1 --- /dev/null +++ b/src/Mobs/PassiveAggressiveMonster.h @@ -0,0 +1,23 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cPassiveAggressiveMonster : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; +} ; + + + + diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp new file mode 100644 index 000000000..91ceb5a53 --- /dev/null +++ b/src/Mobs/PassiveMonster.cpp @@ -0,0 +1,59 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "PassiveMonster.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) +{ + m_EMPersonality = PASSIVE; +} + + + + + +void cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if ((a_TDI.Attacker != this) && (a_TDI.Attacker != NULL)) + { + m_EMState = ESCAPING; + } +} + + + + + +void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + m_SeePlayerInterval += a_Dt; + + if (m_SeePlayerInterval > 1) // Check every second + { + int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally + + m_SeePlayerInterval = 0.0; + if (rem >= 2) + { + if (m_EMState == ESCAPING) + { + CheckEventLostPlayer(); + } + } + } +} + + + + + diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h new file mode 100644 index 000000000..14a6be6b1 --- /dev/null +++ b/src/Mobs/PassiveMonster.h @@ -0,0 +1,27 @@ + +#pragma once + +#include "Monster.h" + + + + + +class cPassiveMonster : + public cMonster +{ + typedef cMonster super; + +public: + cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + /// When hit by someone, run away + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + +} ; + + + + diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp new file mode 100644 index 000000000..0871a38a9 --- /dev/null +++ b/src/Mobs/Pig.cpp @@ -0,0 +1,77 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Pig.h" +#include "../Entities/Player.h" +#include "../World.h" + + + + + +cPig::cPig(void) : + super("Pig", mtPig, "mob.pig.say", "mob.pig.death", 0.9, 0.9), + m_bIsSaddled(false) +{ +} + + + + + +void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP); + if (m_bIsSaddled) + { + a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); + } +} + + + + + +void cPig::OnRightClicked(cPlayer & a_Player) +{ + if (m_bIsSaddled) + { + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + // This player is already sitting in, they want out. + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + // Another player is already sitting in here, cannot attach + return; + } + + // Detach whatever is sitting in this pig now: + m_Attachee->Detach(); + } + + // Attach the player to this pig + a_Player.AttachTo(this); + } + else if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + // Set saddle state & broadcast metadata + m_bIsSaddled = true; + m_World->BroadcastEntityMetadata(*this); + } +} + + + + + diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h new file mode 100644 index 000000000..4fd0d8db8 --- /dev/null +++ b/src/Mobs/Pig.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cPig : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cPig(void); + + CLASS_PROTODEF(cPig); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + bool IsSaddled(void) const { return m_bIsSaddled; } + +private: + + bool m_bIsSaddled; + +} ; + + + + diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp new file mode 100644 index 000000000..bda4ccff8 --- /dev/null +++ b/src/Mobs/Sheep.cpp @@ -0,0 +1,62 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Sheep.h" +#include "../BlockID.h" +#include "../Entities/Player.h" +#include "../World.h" + + + + + +cSheep::cSheep(int a_Color) : + super("Sheep", mtSheep, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3), + m_IsSheared(false), + m_WoolColor(a_Color) +{ +} + + + + + +void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + if (!m_IsSheared) + { + a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, m_WoolColor)); + } +} + + + + + +void cSheep::OnRightClicked(cPlayer & a_Player) +{ + if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_SHEARS) && (!m_IsSheared)) + { + m_IsSheared = true; + m_World->BroadcastEntityMetadata(*this); + + if (!a_Player.IsGameModeCreative()) + { + a_Player.UseEquippedItem(); + } + + cItems Drops; + int NumDrops = m_World->GetTickRandomNumber(2) + 1; + Drops.push_back(cItem(E_BLOCK_WOOL, NumDrops, m_WoolColor)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + } + if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - a_Player.GetEquippedItem().m_ItemDamage)) + { + m_WoolColor = 15 - a_Player.GetEquippedItem().m_ItemDamage; + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_World->BroadcastEntityMetadata(*this); + } +} diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h new file mode 100644 index 000000000..8293a2c05 --- /dev/null +++ b/src/Mobs/Sheep.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cSheep : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cSheep(int a_Color); + + CLASS_PROTODEF(cSheep); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + bool IsSheared(void) const { return m_IsSheared; } + int GetFurColor(void) const { return m_WoolColor; } + +private: + + bool m_IsSheared; + int m_WoolColor; + +} ; + + + + diff --git a/src/Mobs/Silverfish.h b/src/Mobs/Silverfish.h new file mode 100644 index 000000000..a6e11c49d --- /dev/null +++ b/src/Mobs/Silverfish.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSilverfish : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cSilverfish(void) : + super("Silverfish", mtSilverfish, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7) + { + } + + CLASS_PROTODEF(cSilverfish); +} ; + + + + diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp new file mode 100644 index 000000000..509c2191e --- /dev/null +++ b/src/Mobs/Skeleton.cpp @@ -0,0 +1,70 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Skeleton.h" +#include "../World.h" + + + + +cSkeleton::cSkeleton(bool IsWither) : + super("Skeleton", mtSkeleton, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8), + m_bIsWither(IsWither) +{ + SetBurnsInDaylight(true); +} + + + + + +void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ARROW); + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_BONE); +} + + + + + +void cSkeleton::MoveToPosition(const Vector3f & a_Position) +{ + m_Destination = a_Position; + + // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement. + if (!IsOnFire() && m_World->GetTimeOfDay() < 13187 && m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) + { + m_bMovingToDestination = false; + return; + } + m_bMovingToDestination = true; +} + + + + + +void cSkeleton::Attack(float a_Dt) +{ + m_AttackInterval += a_Dt * m_AttackRate; + + if (m_Target != NULL && m_AttackInterval > 3.0) + { + // Setting this higher gives us more wiggle room for attackrate + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); + if (Arrow == NULL) + { + return; + } + if (!Arrow->Initialize(m_World)) + { + delete Arrow; + return; + } + m_World->BroadcastSpawnEntity(*Arrow); + m_AttackInterval = 0.0; + } +} \ No newline at end of file diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h new file mode 100644 index 000000000..8f31b42e1 --- /dev/null +++ b/src/Mobs/Skeleton.h @@ -0,0 +1,33 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSkeleton : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cSkeleton(bool IsWither); + + CLASS_PROTODEF(cSkeleton); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void MoveToPosition(const Vector3f & a_Position) override; + virtual void Attack(float a_Dt) override; + bool IsWither(void) const { return m_bIsWither; }; + +private: + + bool m_bIsWither; + +} ; + + + + diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp new file mode 100644 index 000000000..19f376c21 --- /dev/null +++ b/src/Mobs/Slime.cpp @@ -0,0 +1,29 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Slime.h" + + + + + +/// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest +cSlime::cSlime(int a_Size) : + super("Slime", mtSlime, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size), + m_Size(a_Size) +{ +} + + + + + +void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + // TODO: only when tiny + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_SLIMEBALL); +} + + + + diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h new file mode 100644 index 000000000..782c3113f --- /dev/null +++ b/src/Mobs/Slime.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSlime : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + /// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest + cSlime(int a_Size); + + CLASS_PROTODEF(cSlime); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + int GetSize(void) const { return m_Size; } + +protected: + + /// Size of the slime, 1 .. 3, with 1 being the smallest + int m_Size; +} ; + + + + diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp new file mode 100644 index 000000000..9e199f87e --- /dev/null +++ b/src/Mobs/SnowGolem.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SnowGolem.h" + + + + + +cSnowGolem::cSnowGolem(void) : + super("SnowGolem", mtSnowGolem, "", "", 0.4, 1.8) +{ +} + + + + + +void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 5, E_ITEM_SNOWBALL); +} + + + + diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h new file mode 100644 index 000000000..d1344adfd --- /dev/null +++ b/src/Mobs/SnowGolem.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSnowGolem : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cSnowGolem(void); + + CLASS_PROTODEF(cSnowGolem); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp new file mode 100644 index 000000000..b19a5dcef --- /dev/null +++ b/src/Mobs/Spider.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Spider.h" + + + + + +cSpider::cSpider(void) : + super("Spider", mtSpider, "mob.spider.say", "mob.spider.death", 1.4, 0.9) +{ +} + + + + + +void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING); + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE); +} + + + + diff --git a/src/Mobs/Spider.h b/src/Mobs/Spider.h new file mode 100644 index 000000000..51e65d028 --- /dev/null +++ b/src/Mobs/Spider.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSpider : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cSpider(void); + + CLASS_PROTODEF(cSpider); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp new file mode 100644 index 000000000..a311108ae --- /dev/null +++ b/src/Mobs/Squid.cpp @@ -0,0 +1,56 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Squid.h" +#include "../Vector3d.h" +#include "../Chunk.h" + + + + + +cSquid::cSquid(void) : + super("Squid", mtSquid, "", "", 0.95, 0.95) +{ +} + + + + + +void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + // Drops 0-3 Ink Sacs + AddRandomDropItem(a_Drops, 0, 3, E_ITEM_DYE, E_META_DYE_BLACK); +} + + + + + +void cSquid::Tick(float a_Dt, cChunk & a_Chunk) +{ + // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk + // that is not where the entity currently resides (FS #411) + + Vector3d Pos = GetPosition(); + + // TODO: Not a real behavior, but cool :D + int RelY = (int)floor(Pos.y); + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + return; + } + int RelX = (int)floor(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = (int)floor(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width; + if (!IsBlockWater(a_Chunk.GetBlock(RelX, RelY, RelZ)) && !IsOnFire()) + { + // Burn for 10 ticks, then decide again + StartBurning(10); + } + + super::Tick(a_Dt, a_Chunk); +} + + + diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h new file mode 100644 index 000000000..ad299b95c --- /dev/null +++ b/src/Mobs/Squid.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cSquid : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cSquid(); + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + CLASS_PROTODEF(cSquid); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + +} ; + + + + diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp new file mode 100644 index 000000000..7f89fb6cc --- /dev/null +++ b/src/Mobs/Villager.cpp @@ -0,0 +1,35 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Villager.h" +#include "../World.h" + + + + + +cVillager::cVillager(eVillagerType VillagerType) : + super("Villager", mtVillager, "", "", 0.6, 1.8), + m_Type(VillagerType) +{ +} + + + + + +void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if (a_TDI.Attacker->IsPlayer()) + { + if (m_World->GetTickRandomNumber(5) == 3) + { + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_VILLAGER_ANGRY); + } + } +} + + + + diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h new file mode 100644 index 000000000..4cd9aaa8e --- /dev/null +++ b/src/Mobs/Villager.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cVillager : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + + enum eVillagerType + { + vtFarmer = 0, + vtLibrarian = 1, + vtPriest = 2, + vtBlacksmith = 3, + vtButcher = 4, + vtGeneric = 5, + vtMax + } ; + + cVillager(eVillagerType VillagerType); + + CLASS_PROTODEF(cVillager); + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + int GetVilType(void) const { return m_Type; } + +private: + + int m_Type; + +} ; + + + + diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp new file mode 100644 index 000000000..25d27041f --- /dev/null +++ b/src/Mobs/Witch.cpp @@ -0,0 +1,32 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Witch.h" + + + + + +cWitch::cWitch(void) : + super("Witch", mtWitch, "", "", 0.6, 1.8) +{ +} + + + + + +void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR); +} + + + + diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h new file mode 100644 index 000000000..4e637beea --- /dev/null +++ b/src/Mobs/Witch.h @@ -0,0 +1,27 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cWitch : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cWitch(); + + CLASS_PROTODEF(cWitch); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + + bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); } +} ; + + + + diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp new file mode 100644 index 000000000..c46e0beab --- /dev/null +++ b/src/Mobs/Wither.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Wither.h" + + + + + +cWither::cWither(void) : + super("Wither", mtWither, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0) +{ +} + + + + + +void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 1, 1, E_ITEM_NETHER_STAR); +} + + + + diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h new file mode 100644 index 000000000..56effc6bb --- /dev/null +++ b/src/Mobs/Wither.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cWither : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cWither(void); + + CLASS_PROTODEF(cWither); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp new file mode 100644 index 000000000..c86250142 --- /dev/null +++ b/src/Mobs/Wolf.cpp @@ -0,0 +1,189 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Wolf.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +cWolf::cWolf(void) : + super("Wolf", mtWolf, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8), + m_IsAngry(false), + m_IsTame(false), + m_IsSitting(false), + m_IsBegging(false), + m_OwnerName(""), + m_CollarColor(14) +{ +} + + + + + +void cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if (!m_IsTame) + { + m_IsAngry = true; + } + m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face +} + + + + + +void cWolf::OnRightClicked(cPlayer & a_Player) +{ + if (!IsTame() && !IsAngry()) + { + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_BONE) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + if (m_World->GetTickRandomNumber(7) == 0) + { + SetMaxHealth(20); + SetIsTame(true); + SetOwner(a_Player.GetName()); + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMED); + } + else + { + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMING); + } + } + } + else if (IsTame()) + { + if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? + { + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE) + { + SetCollarColor(15 - a_Player.GetEquippedItem().m_ItemDamage); + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + } + else if (IsSitting()) + { + SetIsSitting(false); + } + else + { + SetIsSitting(true); + } + } + } + + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cWolf::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (!IsAngry()) + { + cMonster::Tick(a_Dt, a_Chunk); + } + else + { + super::Tick(a_Dt, a_Chunk); + } + + if (IsSitting()) + { + m_bMovingToDestination = false; + } + + cPlayer * a_Closest_Player = FindClosestPlayer(); + if (a_Closest_Player != NULL) + { + switch (a_Closest_Player->GetEquippedItem().m_ItemType) + { + case E_ITEM_BONE: + case E_ITEM_RAW_BEEF: + case E_ITEM_STEAK: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_ROTTEN_FLESH: + { + if (!IsBegging()) + { + SetIsBegging(true); + m_World->BroadcastEntityMetadata(*this); + } + Vector3f a_NewDestination = a_Closest_Player->GetPosition(); + a_NewDestination.y = a_NewDestination.y + 1; // Look at the head of the player, not his feet. + m_Destination = Vector3f(a_NewDestination); + m_bMovingToDestination = false; + break; + } + default: + { + if (IsBegging()) + { + SetIsBegging(false); + m_World->BroadcastEntityMetadata(*this); + } + } + } + } + + if (IsTame()) + { + TickFollowPlayer(); + } +} + + + + + +void cWolf::TickFollowPlayer() +{ + class cCallback : + public cPlayerListCallback + { + virtual bool Item(cPlayer * a_Player) override + { + OwnerPos = a_Player->GetPosition(); + return false; + } + public: + Vector3f OwnerPos; + } Callback; + if (m_World->DoWithPlayer(m_OwnerName, Callback)) + { + // The player is present in the world, follow them: + double Distance = (Callback.OwnerPos - GetPosition()).Length(); + if (Distance < 3) + { + m_bMovingToDestination = false; + } + else if ((Distance > 30) && (!IsSitting())) + { + TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z); + } + else + { + m_Destination = Callback.OwnerPos; + } + } +} + + + + diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h new file mode 100644 index 000000000..040e2cf7a --- /dev/null +++ b/src/Mobs/Wolf.h @@ -0,0 +1,54 @@ + +#pragma once + +#include "PassiveAggressiveMonster.h" +#include "../Entities/Entity.h" + + + + + +class cWolf : + public cPassiveAggressiveMonster +{ + typedef cPassiveAggressiveMonster super; + +public: + cWolf(void); + + CLASS_PROTODEF(cWolf); + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void TickFollowPlayer(); + + // Get functions + bool IsSitting (void) const { return m_IsSitting; } + bool IsTame (void) const { return m_IsTame; } + bool IsBegging (void) const { return m_IsBegging; } + bool IsAngry (void) const { return m_IsAngry; } + AString GetOwner (void) const { return m_OwnerName; } + int GetCollarColor(void) const { return m_CollarColor; } + + // Set functions + void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; } + void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; } + void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; } + void SetIsAngry (bool a_IsAngry) { m_IsAngry = a_IsAngry; } + void SetOwner (AString a_NewOwner) { m_OwnerName = a_NewOwner; } + void SetCollarColor(int a_CollarColor) { m_CollarColor = a_CollarColor; } + +protected: + + bool m_IsSitting; + bool m_IsTame; + bool m_IsBegging; + bool m_IsAngry; + AString m_OwnerName; + int m_CollarColor; +} ; + + + + diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp new file mode 100644 index 000000000..a485d2b55 --- /dev/null +++ b/src/Mobs/Zombie.cpp @@ -0,0 +1,47 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Zombie.h" +#include "../World.h" +#include "../LineBlockTracer.h" + + + + +cZombie::cZombie(bool IsVillagerZombie) : + super("Zombie", mtZombie, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8), + m_bIsConverting(false), + m_bIsVillagerZombie(IsVillagerZombie) +{ + SetBurnsInDaylight(true); +} + + + + + +void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ROTTEN_FLESH); + + // TODO: Rare drops +} + + + + + +void cZombie::MoveToPosition(const Vector3f & a_Position) +{ + m_Destination = a_Position; + + // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement. + if ((m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) && (m_World->GetTimeOfDay() < 13187) && !IsOnFire()) + { + m_bMovingToDestination = false; + return; + } + m_bMovingToDestination = true; +} + + diff --git a/src/Mobs/Zombie.h b/src/Mobs/Zombie.h new file mode 100644 index 000000000..7e14fe42f --- /dev/null +++ b/src/Mobs/Zombie.h @@ -0,0 +1,33 @@ +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cZombie : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cZombie(bool IsVillagerZombie); + + CLASS_PROTODEF(cZombie); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void MoveToPosition(const Vector3f & a_Position) override; + + bool IsVillagerZombie(void) const {return m_bIsVillagerZombie; } + bool IsConverting(void) const {return m_bIsConverting; } + +private: + + bool m_bIsVillagerZombie, m_bIsConverting; + +} ; + + + + diff --git a/src/Mobs/Zombiepigman.cpp b/src/Mobs/Zombiepigman.cpp new file mode 100644 index 000000000..6ac89ed4c --- /dev/null +++ b/src/Mobs/Zombiepigman.cpp @@ -0,0 +1,45 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Zombiepigman.h" +#include "../World.h" + + + + + +cZombiePigman::cZombiePigman(void) : + super("ZombiePigman", mtZombiePigman, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8) +{ +} + + + + + +void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ROTTEN_FLESH); + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GOLD_NUGGET); + + // TODO: Rare drops +} + + + + + +void cZombiePigman::KilledBy(cEntity * a_Killer) +{ + super::KilledBy(a_Killer); + + if ((a_Killer != NULL) && (a_Killer->IsPlayer())) + { + // TODO: Anger all nearby zombie pigmen + // TODO: In vanilla, if one player angers ZPs, do they attack any nearby player, or only that one attacker? + } +} + + + + diff --git a/src/Mobs/Zombiepigman.h b/src/Mobs/Zombiepigman.h new file mode 100644 index 000000000..67991d56a --- /dev/null +++ b/src/Mobs/Zombiepigman.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "PassiveAggressiveMonster.h" + + + + + +class cZombiePigman : + public cPassiveAggressiveMonster +{ + typedef cPassiveAggressiveMonster super; + +public: + cZombiePigman(void); + + CLASS_PROTODEF(cZombiePigman); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void KilledBy(cEntity * a_Killer) override; +} ; + + + + diff --git a/src/MonsterConfig.cpp b/src/MonsterConfig.cpp new file mode 100644 index 000000000..a5a1ebd49 --- /dev/null +++ b/src/MonsterConfig.cpp @@ -0,0 +1,104 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MonsterConfig.h" +#include "Mobs/Monster.h" +#include "../iniFile/iniFile.h" +//#include + + + + + +struct cMonsterConfig::sAttributesStruct +{ + AString m_Name; + double m_SightDistance; + double m_AttackDamage; + double m_AttackRange; + double m_AttackRate; + int m_MaxHealth; +}; + + + + + +struct cMonsterConfig::sMonsterConfigState +{ + AString MonsterTypes; + std::list< sAttributesStruct > AttributesList; +}; + + + + + +cMonsterConfig::cMonsterConfig(void) + : m_pState( new sMonsterConfigState ) +{ + Initialize(); +} + + + + + +cMonsterConfig::~cMonsterConfig() +{ + delete m_pState; +} + + + + + +void cMonsterConfig::Initialize() +{ + cIniFile MonstersIniFile; + + if (!MonstersIniFile.ReadFile("monsters.ini")) + { + LOGWARNING("%s: Cannot read monsters.ini file, monster attributes not available", __FUNCTION__); + return; + } + + for (int i = (int)MonstersIniFile.GetNumKeys(); i >= 0; i--) + { + sAttributesStruct Attributes; + AString Name = MonstersIniFile.GetKeyName(i); + Attributes.m_Name = Name; + Attributes.m_AttackDamage = MonstersIniFile.GetValueF(Name, "AttackDamage", 0); + Attributes.m_AttackRange = MonstersIniFile.GetValueF(Name, "AttackRange", 0); + Attributes.m_SightDistance = MonstersIniFile.GetValueF(Name, "SightDistance", 0); + Attributes.m_AttackRate = MonstersIniFile.GetValueF(Name, "AttackRate", 0); + Attributes.m_MaxHealth = MonstersIniFile.GetValueI(Name, "MaxHealth", 1); + m_pState->AttributesList.push_front(Attributes); + } // for i - SplitList[] +} + + + + + +void cMonsterConfig::AssignAttributes(cMonster * a_Monster, const AString & a_Name) +{ + std::list::const_iterator itr; + for (itr = m_pState->AttributesList.begin(); itr != m_pState->AttributesList.end(); ++itr) + { + if (itr->m_Name.compare(a_Name) == 0) + { + a_Monster->SetAttackDamage ((float)itr->m_AttackDamage); + a_Monster->SetAttackRange ((float)itr->m_AttackRange); + a_Monster->SetSightDistance((float)itr->m_SightDistance); + a_Monster->SetAttackRate ((int)itr->m_AttackRate); + a_Monster->SetMaxHealth (itr->m_MaxHealth); + return; + } + } // for itr - m_pState->AttributesList[] +} + + + + + diff --git a/src/MonsterConfig.h b/src/MonsterConfig.h new file mode 100644 index 000000000..371d324c2 --- /dev/null +++ b/src/MonsterConfig.h @@ -0,0 +1,32 @@ + +#pragma once + + + + + +// fwd: +class cMonster; + + + + + +class cMonsterConfig +{ +public: + cMonsterConfig(void); + ~cMonsterConfig(); + + void AssignAttributes(cMonster * a_Monster, const AString & a_Name); + +private: + struct sAttributesStruct; + struct sMonsterConfigState; + sMonsterConfigState* m_pState; + void Initialize(); +} ; + + + + diff --git a/src/Noise.cpp b/src/Noise.cpp new file mode 100644 index 000000000..729641961 --- /dev/null +++ b/src/Noise.cpp @@ -0,0 +1,951 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Noise.h" + + + + + +#if NOISE_USE_SSE + #include //_mm_mul_epi32 +#endif + +#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x)) + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Globals: + +void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase) +{ + const int BUF_SIZE = 512; + ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed + + // Save in XY cuts: + cFile f1; + if (f1.Open(Printf("%s_XY (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) + { + for (int z = 0; z < a_SizeZ; z++) + { + for (int y = 0; y < a_SizeY; y++) + { + int idx = y * a_SizeX + z * a_SizeX * a_SizeY; + unsigned char buf[BUF_SIZE]; + for (int x = 0; x < a_SizeX; x++) + { + buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + } + f1.Write(buf, a_SizeX); + } // for y + unsigned char buf[BUF_SIZE]; + memset(buf, 0, a_SizeX); + f1.Write(buf, a_SizeX); + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open(Printf("%s_XZ (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) + { + for (int y = 0; y < a_SizeY; y++) + { + for (int z = 0; z < a_SizeZ; z++) + { + int idx = y * a_SizeX + z * a_SizeX * a_SizeY; + unsigned char buf[BUF_SIZE]; + for (int x = 0; x < a_SizeX; x++) + { + buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + } + f2.Write(buf, a_SizeX); + } // for z + unsigned char buf[BUF_SIZE]; + memset(buf, 0, a_SizeX); + f2.Write(buf, a_SizeX); + } // for y + } // if (XZ file open) +} + + + + + +void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase) +{ + const int BUF_SIZE = 512; + ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed + + cFile f1; + if (f1.Open(Printf("%s (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) + { + for (int y = 0; y < a_SizeY; y++) + { + int idx = y * a_SizeX; + unsigned char buf[BUF_SIZE]; + for (int x = 0; x < a_SizeX; x++) + { + buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + } + f1.Write(buf, a_SizeX); + } // for y + } // if (file open) +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCubicCell2D: + +class cCubicCell2D +{ +public: + cCubicCell2D( + const cNoise & a_Noise, ///< Noise to use for generating the random values + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values + const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values + ); + + /// Uses current m_WorkRnds[] to generate part of the array + void Generate( + int a_FromX, int a_ToX, + int a_FromY, int a_ToY + ); + + /// Initializes m_WorkRnds[] with the specified Floor values + void InitWorkRnds(int a_FloorX, int a_FloorY); + + /// Updates m_WorkRnds[] for the new Floor values. + void Move(int a_NewFloorX, int a_NewFloorY); + +protected: + typedef NOISE_DATATYPE Workspace[4][4]; + + const cNoise & m_Noise; + + Workspace * m_WorkRnds; ///< The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) + Workspace m_Workspace1; ///< Buffer 1 for workspace doublebuffering, used in Move() + Workspace m_Workspace2; ///< Buffer 2 for workspace doublebuffering, used in Move() + int m_CurFloorX; + int m_CurFloorY; + + NOISE_DATATYPE * m_Array; + int m_SizeX, m_SizeY; + const NOISE_DATATYPE * m_FracX; + const NOISE_DATATYPE * m_FracY; +} ; + + + + + +cCubicCell2D::cCubicCell2D( + const cNoise & a_Noise, ///< Noise to use for generating the random values + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values + const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values +) : + m_Noise(a_Noise), + m_WorkRnds(&m_Workspace1), + m_Array(a_Array), + m_SizeX(a_SizeX), + m_SizeY(a_SizeY), + m_FracX(a_FracX), + m_FracY(a_FracY) +{ +} + + + + + +void cCubicCell2D::Generate( + int a_FromX, int a_ToX, + int a_FromY, int a_ToY +) +{ + for (int y = a_FromY; y < a_ToY; y++) + { + NOISE_DATATYPE Interp[4]; + NOISE_DATATYPE FracY = m_FracY[y]; + Interp[0] = cNoise::CubicInterpolate((*m_WorkRnds)[0][0], (*m_WorkRnds)[0][1], (*m_WorkRnds)[0][2], (*m_WorkRnds)[0][3], FracY); + Interp[1] = cNoise::CubicInterpolate((*m_WorkRnds)[1][0], (*m_WorkRnds)[1][1], (*m_WorkRnds)[1][2], (*m_WorkRnds)[1][3], FracY); + Interp[2] = cNoise::CubicInterpolate((*m_WorkRnds)[2][0], (*m_WorkRnds)[2][1], (*m_WorkRnds)[2][2], (*m_WorkRnds)[2][3], FracY); + Interp[3] = cNoise::CubicInterpolate((*m_WorkRnds)[3][0], (*m_WorkRnds)[3][1], (*m_WorkRnds)[3][2], (*m_WorkRnds)[3][3], FracY); + int idx = y * m_SizeX + a_FromX; + for (int x = a_FromX; x < a_ToX; x++) + { + m_Array[idx++] = cNoise::CubicInterpolate(Interp[0], Interp[1], Interp[2], Interp[3], m_FracX[x]); + } // for x + } // for y +} + + + + + +void cCubicCell2D::InitWorkRnds(int a_FloorX, int a_FloorY) +{ + m_CurFloorX = a_FloorX; + m_CurFloorY = a_FloorY; + for (int x = 0; x < 4; x++) + { + int cx = a_FloorX + x - 1; + for (int y = 0; y < 4; y++) + { + int cy = a_FloorY + y - 1; + (*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy); + } + } +} + + + + + +void cCubicCell2D::Move(int a_NewFloorX, int a_NewFloorY) +{ + // Swap the doublebuffer: + int OldFloorX = m_CurFloorX; + int OldFloorY = m_CurFloorY; + Workspace * OldWorkRnds = m_WorkRnds; + m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1; + + // Reuse as much of the old workspace as possible: + int DiffX = OldFloorX - a_NewFloorX; + int DiffY = OldFloorY - a_NewFloorY; + for (int x = 0; x < 4; x++) + { + int cx = a_NewFloorX + x - 1; + int OldX = x - DiffX; // Where would this X be in the old grid? + for (int y = 0; y < 4; y++) + { + int cy = a_NewFloorY + y - 1; + int OldY = y - DiffY; // Where would this Y be in the old grid? + if ((OldX >= 0) && (OldX < 4) && (OldY >= 0) && (OldY < 4)) + { + (*m_WorkRnds)[x][y] = (*OldWorkRnds)[OldX][OldY]; + } + else + { + (*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy); + } + } + } + m_CurFloorX = a_NewFloorX; + m_CurFloorY = a_NewFloorY; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCubicCell3D: + +class cCubicCell3D +{ +public: + cCubicCell3D( + const cNoise & a_Noise, ///< Noise to use for generating the random values + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values + const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values + const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values + ); + + /// Uses current m_WorkRnds[] to generate part of the array + void Generate( + int a_FromX, int a_ToX, + int a_FromY, int a_ToY, + int a_FromZ, int a_ToZ + ); + + /// Initializes m_WorkRnds[] with the specified Floor values + void InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ); + + /// Updates m_WorkRnds[] for the new Floor values. + void Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ); + +protected: + typedef NOISE_DATATYPE Workspace[4][4][4]; + + const cNoise & m_Noise; + + Workspace * m_WorkRnds; ///< The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) + Workspace m_Workspace1; ///< Buffer 1 for workspace doublebuffering, used in Move() + Workspace m_Workspace2; ///< Buffer 2 for workspace doublebuffering, used in Move() + int m_CurFloorX; + int m_CurFloorY; + int m_CurFloorZ; + + NOISE_DATATYPE * m_Array; + int m_SizeX, m_SizeY, m_SizeZ; + const NOISE_DATATYPE * m_FracX; + const NOISE_DATATYPE * m_FracY; + const NOISE_DATATYPE * m_FracZ; +} ; + + + + + +cCubicCell3D::cCubicCell3D( + const cNoise & a_Noise, ///< Noise to use for generating the random values + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values + const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values + const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values +) : + m_Noise(a_Noise), + m_WorkRnds(&m_Workspace1), + m_Array(a_Array), + m_SizeX(a_SizeX), + m_SizeY(a_SizeY), + m_SizeZ(a_SizeZ), + m_FracX(a_FracX), + m_FracY(a_FracY), + m_FracZ(a_FracZ) +{ +} + + + + + +void cCubicCell3D::Generate( + int a_FromX, int a_ToX, + int a_FromY, int a_ToY, + int a_FromZ, int a_ToZ +) +{ + for (int z = a_FromZ; z < a_ToZ; z++) + { + int idxZ = z * m_SizeX * m_SizeY; + NOISE_DATATYPE Interp2[4][4]; + NOISE_DATATYPE FracZ = m_FracZ[z]; + for (int x = 0; x < 4; x++) + { + for (int y = 0; y < 4; y++) + { + Interp2[x][y] = cNoise::CubicInterpolate((*m_WorkRnds)[x][y][0], (*m_WorkRnds)[x][y][1], (*m_WorkRnds)[x][y][2], (*m_WorkRnds)[x][y][3], FracZ); + } + } + for (int y = a_FromY; y < a_ToY; y++) + { + NOISE_DATATYPE Interp[4]; + NOISE_DATATYPE FracY = m_FracY[y]; + Interp[0] = cNoise::CubicInterpolate(Interp2[0][0], Interp2[0][1], Interp2[0][2], Interp2[0][3], FracY); + Interp[1] = cNoise::CubicInterpolate(Interp2[1][0], Interp2[1][1], Interp2[1][2], Interp2[1][3], FracY); + Interp[2] = cNoise::CubicInterpolate(Interp2[2][0], Interp2[2][1], Interp2[2][2], Interp2[2][3], FracY); + Interp[3] = cNoise::CubicInterpolate(Interp2[3][0], Interp2[3][1], Interp2[3][2], Interp2[3][3], FracY); + int idx = idxZ + y * m_SizeX + a_FromX; + for (int x = a_FromX; x < a_ToX; x++) + { + m_Array[idx++] = cNoise::CubicInterpolate(Interp[0], Interp[1], Interp[2], Interp[3], m_FracX[x]); + } // for x + } // for y + } // for z +} + + + + + +void cCubicCell3D::InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ) +{ + m_CurFloorX = a_FloorX; + m_CurFloorY = a_FloorY; + m_CurFloorZ = a_FloorZ; + for (int x = 0; x < 4; x++) + { + int cx = a_FloorX + x - 1; + for (int y = 0; y < 4; y++) + { + int cy = a_FloorY + y - 1; + for (int z = 0; z < 4; z++) + { + int cz = a_FloorZ + z - 1; + (*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz); + } + } + } +} + + + + + +void cCubicCell3D::Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ) +{ + // Swap the doublebuffer: + int OldFloorX = m_CurFloorX; + int OldFloorY = m_CurFloorY; + int OldFloorZ = m_CurFloorZ; + Workspace * OldWorkRnds = m_WorkRnds; + m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1; + + // Reuse as much of the old workspace as possible: + int DiffX = OldFloorX - a_NewFloorX; + int DiffY = OldFloorY - a_NewFloorY; + int DiffZ = OldFloorZ - a_NewFloorZ; + for (int x = 0; x < 4; x++) + { + int cx = a_NewFloorX + x - 1; + int OldX = x - DiffX; // Where would this X be in the old grid? + for (int y = 0; y < 4; y++) + { + int cy = a_NewFloorY + y - 1; + int OldY = y - DiffY; // Where would this Y be in the old grid? + for (int z = 0; z < 4; z++) + { + int cz = a_NewFloorZ + z - 1; + int OldZ = z - DiffZ; + if ((OldX >= 0) && (OldX < 4) && (OldY >= 0) && (OldY < 4) && (OldZ >= 0) && (OldZ < 4)) + { + (*m_WorkRnds)[x][y][z] = (*OldWorkRnds)[OldX][OldY][OldZ]; + } + else + { + (*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz); + } + } // for z + } // for y + } // for x + m_CurFloorX = a_NewFloorX; + m_CurFloorY = a_NewFloorY; + m_CurFloorZ = a_NewFloorZ; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNoise: + +cNoise::cNoise(unsigned int a_Seed) : + m_Seed(a_Seed) +{ +} + + + + + +cNoise::cNoise(const cNoise & a_Noise) : + m_Seed(a_Noise.m_Seed) +{ +} + + + + + +NOISE_DATATYPE cNoise::LinearNoise1D(NOISE_DATATYPE a_X) const +{ + int BaseX = FAST_FLOOR(a_X); + NOISE_DATATYPE FracX = a_X - BaseX; + return LinearInterpolate(IntNoise1D(BaseX), IntNoise1D(BaseX + 1), FracX); +} + + + + + +NOISE_DATATYPE cNoise::CosineNoise1D(NOISE_DATATYPE a_X) const +{ + int BaseX = FAST_FLOOR(a_X); + NOISE_DATATYPE FracX = a_X - BaseX; + return CosineInterpolate(IntNoise1D(BaseX), IntNoise1D(BaseX + 1), FracX); +} + + + + + +NOISE_DATATYPE cNoise::CubicNoise1D(NOISE_DATATYPE a_X) const +{ + int BaseX = FAST_FLOOR(a_X); + NOISE_DATATYPE FracX = a_X - BaseX; + return CubicInterpolate(IntNoise1D(BaseX - 1), IntNoise1D(BaseX), IntNoise1D(BaseX + 1), IntNoise1D(BaseX + 2), FracX); +} + + + + + +NOISE_DATATYPE cNoise::SmoothNoise1D(int a_X) const +{ + return IntNoise1D(a_X) / 2 + IntNoise1D(a_X - 1) / 4 + IntNoise1D(a_X + 1) / 4; +} + + + + + +NOISE_DATATYPE cNoise::CubicNoise2D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const +{ + const int BaseX = FAST_FLOOR(a_X); + const int BaseY = FAST_FLOOR(a_Y); + + const NOISE_DATATYPE points[4][4] = + { + IntNoise2D(BaseX - 1, BaseY - 1), IntNoise2D(BaseX, BaseY - 1), IntNoise2D(BaseX + 1, BaseY - 1), IntNoise2D(BaseX + 2, BaseY - 1), + IntNoise2D(BaseX - 1, BaseY), IntNoise2D(BaseX, BaseY), IntNoise2D(BaseX + 1, BaseY), IntNoise2D(BaseX + 2, BaseY), + IntNoise2D(BaseX - 1, BaseY + 1), IntNoise2D(BaseX, BaseY + 1), IntNoise2D(BaseX + 1, BaseY + 1), IntNoise2D(BaseX + 2, BaseY + 1), + IntNoise2D(BaseX - 1, BaseY + 2), IntNoise2D(BaseX, BaseY + 2), IntNoise2D(BaseX + 1, BaseY + 2), IntNoise2D(BaseX + 2, BaseY + 2), + }; + + const NOISE_DATATYPE FracX = a_X - BaseX; + const NOISE_DATATYPE interp1 = CubicInterpolate(points[0][0], points[0][1], points[0][2], points[0][3], FracX); + const NOISE_DATATYPE interp2 = CubicInterpolate(points[1][0], points[1][1], points[1][2], points[1][3], FracX); + const NOISE_DATATYPE interp3 = CubicInterpolate(points[2][0], points[2][1], points[2][2], points[2][3], FracX); + const NOISE_DATATYPE interp4 = CubicInterpolate(points[3][0], points[3][1], points[3][2], points[3][3], FracX); + + + const NOISE_DATATYPE FracY = a_Y - BaseY; + return CubicInterpolate(interp1, interp2, interp3, interp4, FracY); +} + + + + + +NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const +{ + const int BaseX = FAST_FLOOR(a_X); + const int BaseY = FAST_FLOOR(a_Y); + const int BaseZ = FAST_FLOOR(a_Z); + + const NOISE_DATATYPE points1[4][4] = { + IntNoise3D(BaseX - 1, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY - 1, BaseZ - 1), + IntNoise3D(BaseX - 1, BaseY, BaseZ - 1), IntNoise3D(BaseX, BaseY, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY, BaseZ - 1), + IntNoise3D(BaseX - 1, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY + 1, BaseZ - 1), + IntNoise3D(BaseX - 1, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ - 1), + }; + + const NOISE_DATATYPE FracX = (a_X) - BaseX; + const NOISE_DATATYPE x1interp1 = CubicInterpolate( points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX ); + const NOISE_DATATYPE x1interp2 = CubicInterpolate( points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX ); + const NOISE_DATATYPE x1interp3 = CubicInterpolate( points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX ); + const NOISE_DATATYPE x1interp4 = CubicInterpolate( points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX ); + + const NOISE_DATATYPE points2[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ ), IntNoise3D( BaseX, BaseY-1, BaseZ ), IntNoise3D( BaseX+1, BaseY-1, BaseZ ), IntNoise3D( BaseX+2, BaseY-1, BaseZ ), + IntNoise3D( BaseX-1, BaseY, BaseZ ), IntNoise3D( BaseX, BaseY, BaseZ ), IntNoise3D( BaseX+1, BaseY, BaseZ ), IntNoise3D( BaseX+2, BaseY, BaseZ ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ ), IntNoise3D( BaseX, BaseY+1, BaseZ ), IntNoise3D( BaseX+1, BaseY+1, BaseZ ), IntNoise3D( BaseX+2, BaseY+1, BaseZ ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ ), IntNoise3D( BaseX, BaseY+2, BaseZ ), IntNoise3D( BaseX+1, BaseY+2, BaseZ ), IntNoise3D( BaseX+2, BaseY+2, BaseZ ), + }; + + const NOISE_DATATYPE x2interp1 = CubicInterpolate( points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX ); + const NOISE_DATATYPE x2interp2 = CubicInterpolate( points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX ); + const NOISE_DATATYPE x2interp3 = CubicInterpolate( points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX ); + const NOISE_DATATYPE x2interp4 = CubicInterpolate( points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX ); + + const NOISE_DATATYPE points3[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+1 ), + IntNoise3D( BaseX-1, BaseY, BaseZ+1 ), IntNoise3D( BaseX, BaseY, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY, BaseZ+1 ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+1 ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+1 ), + }; + + const NOISE_DATATYPE x3interp1 = CubicInterpolate( points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX ); + const NOISE_DATATYPE x3interp2 = CubicInterpolate( points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX ); + const NOISE_DATATYPE x3interp3 = CubicInterpolate( points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX ); + const NOISE_DATATYPE x3interp4 = CubicInterpolate( points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX ); + + const NOISE_DATATYPE points4[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+2 ), + IntNoise3D( BaseX-1, BaseY, BaseZ+2 ), IntNoise3D( BaseX, BaseY, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY, BaseZ+2 ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+2 ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+2 ), + }; + + const NOISE_DATATYPE x4interp1 = CubicInterpolate( points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX ); + const NOISE_DATATYPE x4interp2 = CubicInterpolate( points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX ); + const NOISE_DATATYPE x4interp3 = CubicInterpolate( points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX ); + const NOISE_DATATYPE x4interp4 = CubicInterpolate( points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX ); + + const NOISE_DATATYPE FracY = (a_Y) - BaseY; + const NOISE_DATATYPE yinterp1 = CubicInterpolate( x1interp1, x1interp2, x1interp3, x1interp4, FracY ); + const NOISE_DATATYPE yinterp2 = CubicInterpolate( x2interp1, x2interp2, x2interp3, x2interp4, FracY ); + const NOISE_DATATYPE yinterp3 = CubicInterpolate( x3interp1, x3interp2, x3interp3, x3interp4, FracY ); + const NOISE_DATATYPE yinterp4 = CubicInterpolate( x4interp1, x4interp2, x4interp3, x4interp4, FracY ); + + const NOISE_DATATYPE FracZ = (a_Z) - BaseZ; + return CubicInterpolate( yinterp1, yinterp2, yinterp3, yinterp4, FracZ ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCubicNoise: + +#ifdef _DEBUG + int cCubicNoise::m_NumSingleX = 0; + int cCubicNoise::m_NumSingleXY = 0; + int cCubicNoise::m_NumSingleY = 0; + int cCubicNoise::m_NumCalls = 0; +#endif // _DEBUG + +cCubicNoise::cCubicNoise(int a_Seed) : + m_Noise(a_Seed) +{ +} + + + + + +void cCubicNoise::Generate2D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Size of the array (num doubles), in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction +) const +{ + ASSERT(a_SizeX < MAX_SIZE); + ASSERT(a_SizeY < MAX_SIZE); + ASSERT(a_StartX < a_EndX); + ASSERT(a_StartY < a_EndY); + + // Calculate the integral and fractional parts of each coord: + int FloorX[MAX_SIZE]; + int FloorY[MAX_SIZE]; + NOISE_DATATYPE FracX[MAX_SIZE]; + NOISE_DATATYPE FracY[MAX_SIZE]; + int SameX[MAX_SIZE]; + int SameY[MAX_SIZE]; + int NumSameX, NumSameY; + CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX); + CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY); + + cCubicCell2D Cell(m_Noise, a_Array, a_SizeX, a_SizeY, FracX, FracY); + + Cell.InitWorkRnds(FloorX[0], FloorY[0]); + + #ifdef _DEBUG + // Statistics on the noise-space coords: + if (NumSameX == 1) + { + m_NumSingleX++; + if (NumSameY == 1) + { + m_NumSingleXY++; + } + } + if (NumSameY == 1) + { + m_NumSingleY++; + } + m_NumCalls++; + #endif // _DEBUG + + // Calculate query values using Cell: + int FromY = 0; + for (int y = 0; y < NumSameY; y++) + { + int ToY = FromY + SameY[y]; + int FromX = 0; + int CurFloorY = FloorY[FromY]; + for (int x = 0; x < NumSameX; x++) + { + int ToX = FromX + SameX[x]; + Cell.Generate(FromX, ToX, FromY, ToY); + Cell.Move(FloorX[ToX], CurFloorY); + FromX = ToX; + } + Cell.Move(FloorX[0], FloorY[ToY]); + FromY = ToY; + } +} + + + + + +void cCubicNoise::Generate3D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Size of the array (num doubles), in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Y direction +) const +{ + ASSERT(a_SizeX < MAX_SIZE); + ASSERT(a_SizeY < MAX_SIZE); + ASSERT(a_SizeZ < MAX_SIZE); + ASSERT(a_StartX < a_EndX); + ASSERT(a_StartY < a_EndY); + ASSERT(a_StartZ < a_EndZ); + + // Calculate the integral and fractional parts of each coord: + int FloorX[MAX_SIZE]; + int FloorY[MAX_SIZE]; + int FloorZ[MAX_SIZE]; + NOISE_DATATYPE FracX[MAX_SIZE]; + NOISE_DATATYPE FracY[MAX_SIZE]; + NOISE_DATATYPE FracZ[MAX_SIZE]; + int SameX[MAX_SIZE]; + int SameY[MAX_SIZE]; + int SameZ[MAX_SIZE]; + int NumSameX, NumSameY, NumSameZ; + CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX); + CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY); + CalcFloorFrac(a_SizeZ, a_StartZ, a_EndZ, FloorZ, FracZ, SameZ, NumSameZ); + + cCubicCell3D Cell( + m_Noise, a_Array, + a_SizeX, a_SizeY, a_SizeZ, + FracX, FracY, FracZ + ); + + Cell.InitWorkRnds(FloorX[0], FloorY[0], FloorZ[0]); + + // Calculate query values using Cell: + int FromZ = 0; + for (int z = 0; z < NumSameZ; z++) + { + int ToZ = FromZ + SameZ[z]; + int CurFloorZ = FloorZ[FromZ]; + int FromY = 0; + for (int y = 0; y < NumSameY; y++) + { + int ToY = FromY + SameY[y]; + int CurFloorY = FloorY[FromY]; + int FromX = 0; + for (int x = 0; x < NumSameX; x++) + { + int ToX = FromX + SameX[x]; + Cell.Generate(FromX, ToX, FromY, ToY, FromZ, ToZ); + Cell.Move(FloorX[ToX], CurFloorY, CurFloorZ); + FromX = ToX; + } + Cell.Move(FloorX[0], FloorY[ToY], CurFloorZ); + FromY = ToY; + } // for y + Cell.Move(FloorX[0], FloorY[0], FloorZ[ToZ]); + FromZ = ToZ; + } // for z +} + + + + + +void cCubicNoise::CalcFloorFrac( + int a_Size, + NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End, + int * a_Floor, NOISE_DATATYPE * a_Frac, + int * a_Same, int & a_NumSame +) const +{ + NOISE_DATATYPE val = a_Start; + NOISE_DATATYPE dif = (a_End - a_Start) / (a_Size - 1); + for (int i = 0; i < a_Size; i++) + { + a_Floor[i] = FAST_FLOOR(val); + a_Frac[i] = val - a_Floor[i]; + val += dif; + } + + // Mark up the same floor values into a_Same / a_NumSame: + int CurFloor = a_Floor[0]; + int LastSame = 0; + a_NumSame = 0; + for (int i = 1; i < a_Size; i++) + { + if (a_Floor[i] != CurFloor) + { + a_Same[a_NumSame] = i - LastSame; + LastSame = i; + a_NumSame += 1; + CurFloor = a_Floor[i]; + } + } // for i - a_Floor[] + if (LastSame < a_Size) + { + a_Same[a_NumSame] = a_Size - LastSame; + a_NumSame += 1; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cPerlinNoise: + +cPerlinNoise::cPerlinNoise(void) : + m_Seed(0) +{ +} + + + + + +cPerlinNoise::cPerlinNoise(int a_Seed) : + m_Seed(a_Seed) +{ +} + + + + + +void cPerlinNoise::SetSeed(int a_Seed) +{ + m_Seed = a_Seed; +} + + + + + +void cPerlinNoise::AddOctave(float a_Frequency, float a_Amplitude) +{ + m_Octaves.push_back(cOctave(m_Seed * (m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude)); +} + + + + + +void cPerlinNoise::Generate2D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash +) const +{ + if (m_Octaves.empty()) + { + // No work to be done + ASSERT(!"Perlin: No octaves to generate!"); + return; + } + + bool ShouldFreeWorkspace = (a_Workspace == NULL); + int ArrayCount = a_SizeX * a_SizeY; + if (ShouldFreeWorkspace) + { + a_Workspace = new NOISE_DATATYPE[ArrayCount]; + } + + // Generate the first octave directly into array: + m_Octaves.front().m_Noise.Generate2D( + a_Workspace, a_SizeX, a_SizeY, + a_StartX * m_Octaves.front().m_Frequency, a_EndX * m_Octaves.front().m_Frequency, + a_StartY * m_Octaves.front().m_Frequency, a_EndY * m_Octaves.front().m_Frequency + ); + NOISE_DATATYPE Amplitude = m_Octaves.front().m_Amplitude; + for (int i = 0; i < ArrayCount; i++) + { + a_Array[i] *= Amplitude; + } + + // Add each octave: + for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr) + { + // Generate cubic noise for the octave: + itr->m_Noise.Generate2D( + a_Workspace, a_SizeX, a_SizeY, + a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency, + a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency + ); + // Add the cubic noise into the output: + NOISE_DATATYPE Amplitude = itr->m_Amplitude; + for (int i = 0; i < ArrayCount; i++) + { + a_Array[i] += a_Workspace[i] * Amplitude; + } + } + + if (ShouldFreeWorkspace) + { + delete[] a_Workspace; + } +} + + + + + +void cPerlinNoise::Generate3D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction + NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash +) const +{ + if (m_Octaves.empty()) + { + // No work to be done + ASSERT(!"Perlin: No octaves to generate!"); + return; + } + + bool ShouldFreeWorkspace = (a_Workspace == NULL); + int ArrayCount = a_SizeX * a_SizeY * a_SizeZ; + if (ShouldFreeWorkspace) + { + a_Workspace = new NOISE_DATATYPE[ArrayCount]; + } + + // Generate the first octave directly into array: + m_Octaves.front().m_Noise.Generate3D( + a_Workspace, a_SizeX, a_SizeY, a_SizeZ, + a_StartX * m_Octaves.front().m_Frequency, a_EndX * m_Octaves.front().m_Frequency, + a_StartY * m_Octaves.front().m_Frequency, a_EndY * m_Octaves.front().m_Frequency, + a_StartZ * m_Octaves.front().m_Frequency, a_EndZ * m_Octaves.front().m_Frequency + ); + NOISE_DATATYPE Amplitude = m_Octaves.front().m_Amplitude; + for (int i = 0; i < ArrayCount; i++) + { + a_Array[i] = a_Workspace[i] * Amplitude; + } + + // Add each octave: + for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr) + { + // Generate cubic noise for the octave: + itr->m_Noise.Generate3D( + a_Workspace, a_SizeX, a_SizeY, a_SizeZ, + a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency, + a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency, + a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency + ); + // Add the cubic noise into the output: + NOISE_DATATYPE Amplitude = itr->m_Amplitude; + for (int i = 0; i < ArrayCount; i++) + { + a_Array[i] += a_Workspace[i] * Amplitude; + } + } + + if (ShouldFreeWorkspace) + { + delete[] a_Workspace; + } +} + + + + diff --git a/src/Noise.h b/src/Noise.h new file mode 100644 index 000000000..ea72c64e9 --- /dev/null +++ b/src/Noise.h @@ -0,0 +1,308 @@ + +// Noise.h + +// Declares the cNoise, cCubicNoise and cPerlinNoise classes for generating noise + +#pragma once + +// Some settings +#define NOISE_DATATYPE float + + + + + +#ifdef _MSC_VER + #define INLINE __forceinline +#else + #define INLINE inline +#endif + + + + + +class cNoise +{ +public: + cNoise(unsigned int a_Seed); + cNoise(const cNoise & a_Noise); + + // The following functions, if not marked INLINE, are about 20 % slower + INLINE NOISE_DATATYPE IntNoise1D(int a_X) const; + INLINE NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const; + INLINE NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const; + + // Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify. + INLINE int IntNoise1DInt(int a_X) const; + INLINE int IntNoise2DInt(int a_X, int a_Y) const; + INLINE int IntNoise3DInt(int a_X, int a_Y, int a_Z) const; + + NOISE_DATATYPE LinearNoise1D(NOISE_DATATYPE a_X) const; + NOISE_DATATYPE CosineNoise1D(NOISE_DATATYPE a_X) const; + NOISE_DATATYPE CubicNoise1D (NOISE_DATATYPE a_X) const; + NOISE_DATATYPE SmoothNoise1D(int a_X) const; + + NOISE_DATATYPE CubicNoise2D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const; + + NOISE_DATATYPE CubicNoise3D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const; + + void SetSeed(unsigned int a_Seed) { m_Seed = a_Seed; } + + INLINE static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct); + INLINE static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct); + INLINE static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct); + +private: + unsigned int m_Seed; +} ; + + + + + +class cCubicNoise +{ +public: + static const int MAX_SIZE = 512; ///< Maximum size of each dimension of the query arrays. + + + cCubicNoise(int a_Seed); + + + void Generate1D( + NOISE_DATATYPE * a_Array, ///< Array to generate into + int a_SizeX, ///< Count of the array + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX ///< Noise-space coords of the array + ) const; + + + void Generate2D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction + ) const; + + + void Generate3D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction + ) const; + +protected: + typedef NOISE_DATATYPE Workspace1D[4]; + typedef NOISE_DATATYPE Workspace2D[4][4]; + + cNoise m_Noise; // Used for integral rnd values + + #ifdef _DEBUG + // Statistics on the noise-space coords: + static int m_NumSingleX; + static int m_NumSingleXY; + static int m_NumSingleY; + static int m_NumCalls; + #endif // _DEBUG + + /// Calculates the integral and fractional parts along one axis. + void CalcFloorFrac( + int a_Size, + NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End, + int * a_Floor, NOISE_DATATYPE * a_Frac, + int * a_Same, int & a_NumSame + ) const; + + void UpdateWorkRnds2DX( + Workspace2D & a_WorkRnds, + Workspace1D & a_Interps, + int a_LastFloorX, int a_NewFloorX, + int a_FloorY, + NOISE_DATATYPE a_FractionY + ) const; +} ; + + + + + +class cPerlinNoise +{ +public: + cPerlinNoise(void); + cPerlinNoise(int a_Seed); + + + void SetSeed(int a_Seed); + + void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude); + + void Generate1D( + NOISE_DATATYPE * a_Array, ///< Array to generate into + int a_SizeX, ///< Count of the array + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array + NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash + ) const; + + + void Generate2D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash + ) const; + + + void Generate3D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction + NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash + ) const; + +protected: + class cOctave + { + public: + cCubicNoise m_Noise; + + NOISE_DATATYPE m_Frequency; // Coord multiplier + NOISE_DATATYPE m_Amplitude; // Value multiplier + + cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) : + m_Noise(a_Seed), + m_Frequency(a_Frequency), + m_Amplitude(a_Amplitude) + { + } + } ; + + typedef std::vector cOctaves; + + int m_Seed; + cOctaves m_Octaves; +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Inline function definitions: +// These need to be in the header, otherwise linker error occur in MSVC + +NOISE_DATATYPE cNoise::IntNoise1D(int a_X) const +{ + int x = ((a_X * m_Seed) << 13) ^ a_X; + return (1 - (NOISE_DATATYPE)((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824); + // returns a float number in the range of [-1, 1] +} + + + + + +NOISE_DATATYPE cNoise::IntNoise2D(int a_X, int a_Y) const +{ + int n = a_X + a_Y * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + return (1 - (NOISE_DATATYPE)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824); + // returns a float number in the range of [-1, 1] +} + + + + + +NOISE_DATATYPE cNoise::IntNoise3D(int a_X, int a_Y, int a_Z) const +{ + int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57; + n = (n << 13) ^ n; + return ((NOISE_DATATYPE)1 - (NOISE_DATATYPE)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f); + // returns a float number in the range of [-1, 1] +} + + + + + +int cNoise::IntNoise1DInt(int a_X) const +{ + int x = ((a_X * m_Seed) << 13) ^ a_X; + return ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff); +} + + + + + +int cNoise::IntNoise2DInt(int a_X, int a_Y) const +{ + int n = a_X + a_Y * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); +} + + + + + +int cNoise::IntNoise3DInt(int a_X, int a_Y, int a_Z) const +{ + int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57; + n = (n << 13) ^ n; + return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); +} + + + + + +NOISE_DATATYPE cNoise::CubicInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct) +{ + NOISE_DATATYPE P = (a_D - a_C) - (a_A - a_B); + NOISE_DATATYPE Q = (a_A - a_B) - P; + NOISE_DATATYPE R = a_C - a_A; + NOISE_DATATYPE S = a_B; + + return ((P * a_Pct + Q) * a_Pct + R) * a_Pct + S; +} + + + + + +NOISE_DATATYPE cNoise::CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct) +{ + const NOISE_DATATYPE ft = a_Pct * (NOISE_DATATYPE)3.1415927; + const NOISE_DATATYPE f = (1 - cos(ft)) * (NOISE_DATATYPE)0.5; + return a_A * (1 - f) + a_B * f; +} + + + + + +NOISE_DATATYPE cNoise::LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct) +{ + return a_A * (1 - a_Pct) + a_B * a_Pct; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Global functions: + +extern void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase); +extern void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase); + + + + diff --git a/src/OSSupport/BlockingTCPLink.cpp b/src/OSSupport/BlockingTCPLink.cpp new file mode 100644 index 000000000..55454a4b5 --- /dev/null +++ b/src/OSSupport/BlockingTCPLink.cpp @@ -0,0 +1,149 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BlockingTCPLink.h" + + + + + +#ifdef _WIN32 + #define MSG_NOSIGNAL (0) +#endif +#ifdef __MACH__ + #define MSG_NOSIGNAL (0) +#endif + + + + + +cBlockingTCPLink::cBlockingTCPLink(void) +{ +} + + + + + +cBlockingTCPLink::~cBlockingTCPLink() +{ + CloseSocket(); +} + + + + + +void cBlockingTCPLink::CloseSocket() +{ + if (!m_Socket.IsValid()) + { + m_Socket.CloseSocket(); + } +} + + + + + +bool cBlockingTCPLink::Connect(const char * iAddress, unsigned int iPort) +{ + ASSERT(!m_Socket.IsValid()); + if (m_Socket.IsValid()) + { + LOGWARN("WARNING: cTCPLink Connect() called while still connected."); + m_Socket.CloseSocket(); + } + + struct hostent *hp; + unsigned int addr; + struct sockaddr_in server; + + m_Socket = socket(AF_INET, SOCK_STREAM, 0); + if (!m_Socket.IsValid()) + { + LOGERROR("cTCPLink: Cannot create a socket"); + return false; + } + + addr = inet_addr(iAddress); + hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); + if (hp == NULL) + { + //LOGWARN("cTCPLink: gethostbyaddr returned NULL"); + hp = gethostbyname(iAddress); + if (hp == NULL) + { + LOGWARN("cTCPLink: Could not resolve %s", iAddress); + CloseSocket(); + return false; + } + } + + server.sin_addr.s_addr = *((unsigned long *)hp->h_addr); + server.sin_family = AF_INET; + server.sin_port = htons( (unsigned short)iPort); + if (connect(m_Socket, (struct sockaddr *)&server, sizeof(server))) + { + LOGWARN("cTCPLink: Connection to \"%s:%d\" failed (%s)", iAddress, iPort, cSocket::GetErrorString( cSocket::GetLastError() ).c_str() ); + CloseSocket(); + return false; + } + + return true; +} + + + + + +int cBlockingTCPLink::Send(char * a_Data, unsigned int a_Size, int a_Flags /* = 0 */ ) +{ + ASSERT(m_Socket.IsValid()); + if (!m_Socket.IsValid()) + { + LOGERROR("cBlockingTCPLink: Trying to send data without a valid connection!"); + return -1; + } + return m_Socket.Send(a_Data, a_Size); +} + + + + + +int cBlockingTCPLink::SendMessage( const char* a_Message, int a_Flags /* = 0 */ ) +{ + ASSERT(m_Socket.IsValid()); + if (!m_Socket.IsValid()) + { + LOGWARN("cBlockingTCPLink: Trying to send message without a valid connection!"); + return -1; + } + return m_Socket.Send(a_Message, strlen(a_Message)); +} + + + + + +void cBlockingTCPLink::ReceiveData(AString & oData) +{ + ASSERT(m_Socket.IsValid()); + if (!m_Socket.IsValid()) + { + return; + } + + int Received = 0; + char Buffer[256]; + while ((Received = recv(m_Socket, Buffer, sizeof(Buffer), 0)) > 0) + { + oData.append(Buffer, Received); + } +} + + + + diff --git a/src/OSSupport/BlockingTCPLink.h b/src/OSSupport/BlockingTCPLink.h new file mode 100644 index 000000000..cb5f9e3f4 --- /dev/null +++ b/src/OSSupport/BlockingTCPLink.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "Socket.h" + + + + + +class cBlockingTCPLink // tolua_export +{ // tolua_export +public: // tolua_export + cBlockingTCPLink(void); // tolua_export + ~cBlockingTCPLink(); // tolua_export + + bool Connect( const char* a_Address, unsigned int a_Port ); // tolua_export + int Send( char* a_Data, unsigned int a_Size, int a_Flags = 0 ); // tolua_export + int SendMessage( const char* a_Message, int a_Flags = 0 ); // tolua_export + void CloseSocket(); // tolua_export + void ReceiveData(AString & oData); // tolua_export +protected: + + cSocket m_Socket; +}; // tolua_export + + + + diff --git a/src/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp new file mode 100644 index 000000000..bda97e3a1 --- /dev/null +++ b/src/OSSupport/CriticalSection.cpp @@ -0,0 +1,188 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "IsThread.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCriticalSection: + +cCriticalSection::cCriticalSection() +{ + #ifdef _WIN32 + InitializeCriticalSection(&m_CriticalSection); + #else + pthread_mutexattr_init(&m_Attributes); + pthread_mutexattr_settype(&m_Attributes, PTHREAD_MUTEX_RECURSIVE); + + if (pthread_mutex_init(&m_CriticalSection, &m_Attributes) != 0) + { + LOGERROR("Could not initialize Critical Section!"); + } + #endif + + #ifdef _DEBUG + m_IsLocked = 0; + #endif // _DEBUG +} + + + + + +cCriticalSection::~cCriticalSection() +{ + #ifdef _WIN32 + DeleteCriticalSection(&m_CriticalSection); + #else + if (pthread_mutex_destroy(&m_CriticalSection) != 0) + { + LOGWARNING("Could not destroy Critical Section!"); + } + pthread_mutexattr_destroy(&m_Attributes); + #endif +} + + + + + +void cCriticalSection::Lock() +{ + #ifdef _WIN32 + EnterCriticalSection(&m_CriticalSection); + #else + pthread_mutex_lock(&m_CriticalSection); + #endif + + #ifdef _DEBUG + m_IsLocked += 1; + m_OwningThreadID = cIsThread::GetCurrentID(); + #endif // _DEBUG +} + + + + + +void cCriticalSection::Unlock() +{ + #ifdef _DEBUG + ASSERT(m_IsLocked > 0); + m_IsLocked -= 1; + #endif // _DEBUG + + #ifdef _WIN32 + LeaveCriticalSection(&m_CriticalSection); + #else + pthread_mutex_unlock(&m_CriticalSection); + #endif +} + + + + + +#ifdef _DEBUG +bool cCriticalSection::IsLocked(void) +{ + return (m_IsLocked > 0); +} + + + + + +bool cCriticalSection::IsLockedByCurrentThread(void) +{ + return ((m_IsLocked > 0) && (m_OwningThreadID == cIsThread::GetCurrentID())); +} +#endif // _DEBUG + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCSLock + +cCSLock::cCSLock(cCriticalSection * a_CS) + : m_CS(a_CS) + , m_IsLocked(false) +{ + Lock(); +} + + + + + +cCSLock::cCSLock(cCriticalSection & a_CS) + : m_CS(&a_CS) + , m_IsLocked(false) +{ + Lock(); +} + + + + + +cCSLock::~cCSLock() +{ + if (!m_IsLocked) + { + return; + } + Unlock(); +} + + + + + +void cCSLock::Lock(void) +{ + ASSERT(!m_IsLocked); + m_IsLocked = true; + m_CS->Lock(); +} + + + + + +void cCSLock::Unlock(void) +{ + ASSERT(m_IsLocked); + m_IsLocked = false; + m_CS->Unlock(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCSUnlock: + +cCSUnlock::cCSUnlock(cCSLock & a_Lock) : + m_Lock(a_Lock) +{ + m_Lock.Unlock(); +} + + + + + +cCSUnlock::~cCSUnlock() +{ + m_Lock.Lock(); +} + + + + diff --git a/src/OSSupport/CriticalSection.h b/src/OSSupport/CriticalSection.h new file mode 100644 index 000000000..1bfe81439 --- /dev/null +++ b/src/OSSupport/CriticalSection.h @@ -0,0 +1,80 @@ + +#pragma once + + + + + +class cCriticalSection +{ +public: + cCriticalSection(void); + ~cCriticalSection(); + + void Lock(void); + void Unlock(void); + + #ifdef _DEBUG + bool IsLocked(void); + bool IsLockedByCurrentThread(void); + #endif // _DEBUG + +private: + #ifdef _DEBUG + int m_IsLocked; // Number of times this CS is locked + unsigned long m_OwningThreadID; + #endif // _DEBUG + + #ifdef _WIN32 + CRITICAL_SECTION m_CriticalSection; + #else // _WIN32 + pthread_mutex_t m_CriticalSection; + pthread_mutexattr_t m_Attributes; + #endif // else _WIN32 +} ALIGN_8; + + + + +/// RAII for cCriticalSection - locks the CS on creation, unlocks on destruction +class cCSLock +{ + cCriticalSection * m_CS; + + // Unlike a cCriticalSection, this object should be used from a single thread, therefore access to m_IsLocked is not threadsafe + // In Windows, it is an error to call cCriticalSection::Unlock() multiple times if the lock is not held, + // therefore we need to check this value whether we are locked or not. + bool m_IsLocked; + +public: + cCSLock(cCriticalSection * a_CS); + cCSLock(cCriticalSection & a_CS); + ~cCSLock(); + + // Temporarily unlock or re-lock: + void Lock(void); + void Unlock(void); + +private: + DISALLOW_COPY_AND_ASSIGN(cCSLock); +} ; + + + + + +/// Temporary RAII unlock for a cCSLock. Useful for unlock-wait-relock scenarios +class cCSUnlock +{ + cCSLock & m_Lock; +public: + cCSUnlock(cCSLock & a_Lock); + ~cCSUnlock(); + +private: + DISALLOW_COPY_AND_ASSIGN(cCSUnlock); +} ; + + + + diff --git a/src/OSSupport/Event.cpp b/src/OSSupport/Event.cpp new file mode 100644 index 000000000..cbacbba17 --- /dev/null +++ b/src/OSSupport/Event.cpp @@ -0,0 +1,118 @@ + +// Event.cpp + +// Implements the cEvent object representing an OS-specific synchronization primitive that can be waited-for +// Implemented as an Event on Win and as a 1-semaphore on *nix + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Event.h" + + + + + +cEvent::cEvent(void) +{ +#ifdef _WIN32 + m_Event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (m_Event == NULL) + { + LOGERROR("cEvent: cannot create event, GLE = %d. Aborting server.", GetLastError()); + abort(); + } +#else // *nix + m_bIsNamed = false; + m_Event = new sem_t; + if (sem_init(m_Event, 0, 0)) + { + // This path is used by MacOS, because it doesn't support unnamed semaphores. + delete m_Event; + m_bIsNamed = true; + + AString EventName; + Printf(EventName, "cEvent%p", this); + m_Event = sem_open(EventName.c_str(), O_CREAT, 777, 0 ); + if (m_Event == SEM_FAILED) + { + LOGERROR("cEvent: Cannot create event, errno = %i. Aborting server.", errno); + abort(); + } + // Unlink the semaphore immediately - it will continue to function but will not pollute the namespace + // We don't store the name, so can't call this in the destructor + if (sem_unlink(EventName.c_str()) != 0) + { + LOGWARN("ERROR: Could not unlink cEvent. (%i)", errno); + } + } +#endif // *nix +} + + + + + +cEvent::~cEvent() +{ +#ifdef _WIN32 + CloseHandle(m_Event); +#else + if (m_bIsNamed) + { + if (sem_close(m_Event) != 0) + { + LOGERROR("ERROR: Could not close cEvent. (%i)", errno); + } + } + else + { + sem_destroy(m_Event); + delete m_Event; + } +#endif +} + + + + + +void cEvent::Wait(void) +{ + #ifdef _WIN32 + DWORD res = WaitForSingleObject(m_Event, INFINITE); + if (res != WAIT_OBJECT_0) + { + LOGWARN("cEvent: waiting for the event failed: %d, GLE = %d. Continuing, but server may be unstable.", res, GetLastError()); + } + #else + int res = sem_wait(m_Event); + if (res != 0 ) + { + LOGWARN("cEvent: waiting for the event failed: %i, errno = %i. Continuing, but server may be unstable.", res, errno); + } + #endif +} + + + + + +void cEvent::Set(void) +{ + #ifdef _WIN32 + if (!SetEvent(m_Event)) + { + LOGWARN("cEvent: Could not set cEvent: GLE = %d", GetLastError()); + } + #else + int res = sem_post(m_Event); + if (res != 0) + { + LOGWARN("cEvent: Could not set cEvent: %i, errno = %d", res, errno); + } + #endif +} + + + + diff --git a/src/OSSupport/Event.h b/src/OSSupport/Event.h new file mode 100644 index 000000000..71f418c0c --- /dev/null +++ b/src/OSSupport/Event.h @@ -0,0 +1,47 @@ + +// Event.h + +// Interfaces to the cEvent object representing an OS-specific synchronization primitive that can be waited-for +// Implemented as an Event on Win and as a 1-semaphore on *nix + + + + + +#pragma once +#ifndef CEVENT_H_INCLUDED +#define CEVENT_H_INCLUDED + + + + + +class cEvent +{ +public: + cEvent(void); + ~cEvent(); + + void Wait(void); + void Set (void); + +private: + + #ifdef _WIN32 + HANDLE m_Event; + #else + sem_t * m_Event; + bool m_bIsNamed; + #endif +} ; + + + + + + +#endif // CEVENT_H_INCLUDED + + + + diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp new file mode 100644 index 000000000..d2eea498a --- /dev/null +++ b/src/OSSupport/File.cpp @@ -0,0 +1,375 @@ + +// cFile.cpp + +// Implements the cFile class providing an OS-independent abstraction of a file. + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "File.h" +#include + + + + + +cFile::cFile(void) : + #ifdef USE_STDIO_FILE + m_File(NULL) + #else + m_File(INVALID_HANDLE_VALUE) + #endif // USE_STDIO_FILE +{ + // Nothing needed yet +} + + + + + +cFile::cFile(const AString & iFileName, eMode iMode) : + #ifdef USE_STDIO_FILE + m_File(NULL) + #else + m_File(INVALID_HANDLE_VALUE) + #endif // USE_STDIO_FILE +{ + Open(iFileName, iMode); +} + + + + + +cFile::~cFile() +{ + if (IsOpen()) + { + Close(); + } +} + + + + + +bool cFile::Open(const AString & iFileName, eMode iMode) +{ + ASSERT(!IsOpen()); // You should close the file before opening another one + + if (IsOpen()) + { + Close(); + } + + const char * Mode = NULL; + switch (iMode) + { + case fmRead: Mode = "rb"; break; + case fmWrite: Mode = "wb"; break; + case fmReadWrite: Mode = "rb+"; break; + default: + { + ASSERT(!"Unhandled file mode"); + return false; + } + } + m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), Mode); + if ((m_File == NULL) && (iMode == fmReadWrite)) + { + // Fix for MS not following C spec, opening "a" mode files for writing at the end only + // The file open operation has been tried with "read update", fails if file not found + // So now we know either the file doesn't exist or we don't have rights, no need to worry about file contents. + // Simply re-open for read-writing, erasing existing contents: + m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), "wb+"); + } + return (m_File != NULL); +} + + + + + +void cFile::Close(void) +{ + if (!IsOpen()) + { + // Closing an unopened file is a legal nop + return; + } + + fclose(m_File); + m_File = NULL; +} + + + + + +bool cFile::IsOpen(void) const +{ + return (m_File != NULL); +} + + + + + +bool cFile::IsEOF(void) const +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + // Unopened files behave as at EOF + return true; + } + + return (feof(m_File) != 0); +} + + + + + +int cFile::Read (void * iBuffer, int iNumBytes) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + return fread(iBuffer, 1, iNumBytes, m_File); // fread() returns the portion of Count parameter actually read, so we need to send iNumBytes as Count +} + + + + + +int cFile::Write(const void * iBuffer, int iNumBytes) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int res = fwrite(iBuffer, 1, iNumBytes, m_File); // fwrite() returns the portion of Count parameter actually written, so we need to send iNumBytes as Count + return res; +} + + + + + +int cFile::Seek (int iPosition) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + if (fseek(m_File, iPosition, SEEK_SET) != 0) + { + return -1; + } + return ftell(m_File); +} + + + + + + +int cFile::Tell (void) const +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + return ftell(m_File); +} + + + + + +int cFile::GetSize(void) const +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int CurPos = ftell(m_File); + if (CurPos < 0) + { + return -1; + } + if (fseek(m_File, 0, SEEK_END) != 0) + { + return -1; + } + int res = ftell(m_File); + if (fseek(m_File, CurPos, SEEK_SET) != 0) + { + return -1; + } + return res; +} + + + + + +int cFile::ReadRestOfFile(AString & a_Contents) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int DataSize = GetSize() - Tell(); + + // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly + a_Contents.assign(DataSize, '\0'); + return Read((void *)a_Contents.data(), DataSize); +} + + + + + +bool cFile::Exists(const AString & a_FileName) +{ + cFile test(a_FileName, fmRead); + return test.IsOpen(); +} + + + + + +bool cFile::Delete(const AString & a_FileName) +{ + return (remove(a_FileName.c_str()) == 0); +} + + + + + +bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName) +{ + return (rename(a_OrigFileName.c_str(), a_NewFileName.c_str()) == 0); +} + + + + + +bool cFile::Copy(const AString & a_SrcFileName, const AString & a_DstFileName) +{ + #ifdef _WIN32 + return (CopyFile(a_SrcFileName.c_str(), a_DstFileName.c_str(), true) != 0); + #else + // Other OSs don't have a direct CopyFile equivalent, do it the harder way: + std::ifstream src(a_SrcFileName.c_str(), std::ios::binary); + std::ofstream dst(a_DstFileName.c_str(), std::ios::binary); + if (dst.good()) + { + dst << src.rdbuf(); + return true; + } + else + { + return false; + } + #endif +} + + + + + +bool cFile::IsFolder(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISDIR(st.st_mode)); + #endif +} + + + + + +bool cFile::IsFile(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISREG(st.st_mode)); + #endif +} + + + + + +int cFile::GetSize(const AString & a_FileName) +{ + struct stat st; + if (stat(a_FileName.c_str(), &st) == 0) + { + return st.st_size; + } + return -1; +} + + + + + +bool cFile::CreateFolder(const AString & a_FolderPath) +{ + #ifdef _WIN32 + return (CreateDirectory(a_FolderPath.c_str(), NULL) != 0); + #else + return (mkdir(a_FolderPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0); + #endif +} + + + + + +int cFile::Printf(const char * a_Fmt, ...) +{ + AString buf; + va_list args; + va_start(args, a_Fmt); + AppendVPrintf(buf, a_Fmt, args); + va_end(args); + return Write(buf.c_str(), buf.length()); +} + + + + diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h new file mode 100644 index 000000000..cfb3a2019 --- /dev/null +++ b/src/OSSupport/File.h @@ -0,0 +1,138 @@ + +// cFile.h + +// Interfaces to the cFile class providing an OS-independent abstraction of a file. + +/* +The object is optimized towards binary reads. +The object has no multithreading locks, don't use from multiple threads! +Usage: +1, Construct a cFile instance (no-param constructor) +2, Open a file using Open(), check return value for success +3, Read / write +4, Destroy the instance + +-- OR -- + +1, Construct a cFile instance opening the file (filename-param constructor) +2, Check if the file was opened using IsOpen() +3, Read / write +4, Destroy the instance +*/ + + + + + +#pragma once + + + + + +#ifndef _WIN32 + #define USE_STDIO_FILE +#endif // _WIN32 + +// DEBUG: +#define USE_STDIO_FILE + + + + + +// tolua_begin + +class cFile +{ +public: + + // tolua_end + + #ifdef _WIN32 + static const char PathSeparator = '\\'; + #else + static const char PathSeparator = '/'; + #endif + + /// The mode in which to open the file + enum eMode + { + fmRead, // Read-only. If the file doesn't exist, object will not be valid + fmWrite, // Write-only. If the file already exists, it will be overwritten + fmReadWrite // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning + } ; + + /// Simple constructor - creates an unopened file object, use Open() to open / create a real file + cFile(void); + + /// Constructs and opens / creates the file specified, use IsOpen() to check for success + cFile(const AString & iFileName, eMode iMode); + + /// Auto-closes the file, if open + ~cFile(); + + bool Open(const AString & iFileName, eMode iMode); + void Close(void); + bool IsOpen(void) const; + bool IsEOF(void) const; + + /// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open + int Read (void * iBuffer, int iNumBytes); + + /// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open + int Write(const void * iBuffer, int iNumBytes); + + /// Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open + int Seek (int iPosition); + + /// Returns the current position (bytes from file start) or -1 for failure; asserts if not open + int Tell (void) const; + + /// Returns the size of file, in bytes, or -1 for failure; asserts if not open + int GetSize(void) const; + + /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error + int ReadRestOfFile(AString & a_Contents); + + // tolua_begin + + /// Returns true if the file specified exists + static bool Exists(const AString & a_FileName); + + /// Deletes a file, returns true if successful + static bool Delete(const AString & a_FileName); + + /// Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)! + static bool Rename(const AString & a_OrigPath, const AString & a_NewPath); + + /// Copies a file, returns true if successful. + static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName); + + /// Returns true if the specified path is a folder + static bool IsFolder(const AString & a_Path); + + /// Returns true if the specified path is a regular file + static bool IsFile(const AString & a_Path); + + /// Returns the size of the file, or a negative number on error + static int GetSize(const AString & a_FileName); + + /// Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute + static bool CreateFolder(const AString & a_FolderPath); + + // tolua_end + + int Printf(const char * a_Fmt, ...); + +private: + #ifdef USE_STDIO_FILE + FILE * m_File; + #else + HANDLE m_File; + #endif +} ; // tolua_export + + + + diff --git a/src/OSSupport/GZipFile.cpp b/src/OSSupport/GZipFile.cpp new file mode 100644 index 000000000..cbf6be6c4 --- /dev/null +++ b/src/OSSupport/GZipFile.cpp @@ -0,0 +1,107 @@ + +// GZipFile.cpp + +// Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines + +#include "Globals.h" +#include "GZipFile.h" + + + + + +cGZipFile::cGZipFile(void) : + m_File(NULL) +{ +} + + + + + +cGZipFile::~cGZipFile() +{ + Close(); +} + + + + + +bool cGZipFile::Open(const AString & a_FileName, eMode a_Mode) +{ + if (m_File != NULL) + { + ASSERT(!"A file is already open in this object"); + return false; + } + m_File = gzopen(a_FileName.c_str(), (a_Mode == fmRead) ? "r" : "w"); + m_Mode = a_Mode; + return (m_File != NULL); +} + + + + + +void cGZipFile::Close(void) +{ + if (m_File != NULL) + { + gzclose(m_File); + m_File = NULL; + } +} + + + + + +int cGZipFile::ReadRestOfFile(AString & a_Contents) +{ + if (m_File == NULL) + { + ASSERT(!"No file has been opened"); + return -1; + } + + if (m_Mode != fmRead) + { + ASSERT(!"Bad file mode, cannot read"); + return -1; + } + + // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck! + int NumBytesRead = 0; + char Buffer[64 KiB]; + while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0) + { + a_Contents.append(Buffer, NumBytesRead); + } + return NumBytesRead; +} + + + + + +bool cGZipFile::Write(const char * a_Contents, int a_Size) +{ + if (m_File == NULL) + { + ASSERT(!"No file has been opened"); + return false; + } + + if (m_Mode != fmWrite) + { + ASSERT(!"Bad file mode, cannot write"); + return false; + } + + return (gzwrite(m_File, a_Contents, a_Size) != 0); +} + + + + diff --git a/src/OSSupport/GZipFile.h b/src/OSSupport/GZipFile.h new file mode 100644 index 000000000..e5aa68afa --- /dev/null +++ b/src/OSSupport/GZipFile.h @@ -0,0 +1,52 @@ + +// GZipFile.h + +// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines + + + + + +#pragma once + +#include "zlib.h" + + + + + +class cGZipFile +{ +public: + enum eMode + { + fmRead, // Read-only. If the file doesn't exist, object will not be valid + fmWrite, // Write-only. If the file already exists, it will be overwritten + } ; + + cGZipFile(void); + ~cGZipFile(); + + /// Opens the file. Returns true if successful. Fails if a file has already been opened through this object. + bool Open(const AString & a_FileName, eMode a_Mode); + + /// Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode + void Close(void); + + /// Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error + int ReadRestOfFile(AString & a_Contents); + + /// Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported. + bool Write(const AString & a_Contents) { return Write(a_Contents.data(), (int)(a_Contents.size())); } + + bool Write(const char * a_Data, int a_Size); + +protected: + gzFile m_File; + eMode m_Mode; +} ; + + + + + diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp new file mode 100644 index 000000000..4da9f9949 --- /dev/null +++ b/src/OSSupport/IsThread.cpp @@ -0,0 +1,172 @@ + +// IsThread.cpp + +// Implements the cIsThread class representing an OS-independent wrapper for a class that implements a thread. +// This class will eventually suupersede the old cThread class + +#include "Globals.h" + +#include "IsThread.h" + + + + + +// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here: +#if defined(_MSC_VER) && defined(_DEBUG) +// +// Usage: SetThreadName (-1, "MainThread"); +// + +static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) +{ + struct + { + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero + } info; + + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +} +#endif // _MSC_VER && _DEBUG + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cIsThread: + +cIsThread::cIsThread(const AString & iThreadName) : + m_ThreadName(iThreadName), + m_ShouldTerminate(false), + m_Handle(NULL_HANDLE) +{ +} + + + + + +cIsThread::~cIsThread() +{ + m_ShouldTerminate = true; + Wait(); +} + + + + + +bool cIsThread::Start(void) +{ + ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread? + #ifdef _WIN32 + // Create the thread suspended, so that the mHandle variable is valid in the thread procedure + DWORD ThreadID = 0; + m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID); + if (m_Handle == NULL) + { + LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError()); + return false; + } + ResumeThread(m_Handle); + + #if defined(_DEBUG) && defined(_MSC_VER) + // Thread naming is available only in MSVC + if (!m_ThreadName.empty()) + { + SetThreadName(ThreadID, m_ThreadName.c_str()); + } + #endif // _DEBUG and _MSC_VER + + #else // _WIN32 + if (pthread_create(&m_Handle, NULL, thrExecute, this)) + { + LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str()); + return false; + } + #endif // else _WIN32 + + return true; +} + + + + + +void cIsThread::Stop(void) +{ + if (m_Handle == NULL_HANDLE) + { + return; + } + m_ShouldTerminate = true; + Wait(); +} + + + + + +bool cIsThread::Wait(void) +{ + if (m_Handle == NULL) + { + return true; + } + + #ifdef LOGD // ProtoProxy doesn't have LOGD + LOGD("Waiting for thread %s to finish", m_ThreadName.c_str()); + #endif // LOGD + + #ifdef _WIN32 + int res = WaitForSingleObject(m_Handle, INFINITE); + m_Handle = NULL; + + #ifdef LOGD // ProtoProxy doesn't have LOGD + LOGD("Thread %s finished", m_ThreadName.c_str()); + #endif // LOGD + + return (res == WAIT_OBJECT_0); + #else // _WIN32 + int res = pthread_join(m_Handle, NULL); + m_Handle = NULL; + + #ifdef LOGD // ProtoProxy doesn't have LOGD + LOGD("Thread %s finished", m_ThreadName.c_str()); + #endif // LOGD + + return (res == 0); + #endif // else _WIN32 +} + + + + + +unsigned long cIsThread::GetCurrentID(void) +{ + #ifdef _WIN32 + return (unsigned long) GetCurrentThreadId(); + #else + return (unsigned long) pthread_self(); + #endif +} + + + + diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h new file mode 100644 index 000000000..b8784ea33 --- /dev/null +++ b/src/OSSupport/IsThread.h @@ -0,0 +1,100 @@ + +// IsThread.h + +// Interfaces to the cIsThread class representing an OS-independent wrapper for a class that implements a thread. +// This class will eventually suupersede the old cThread class + +/* +Usage: +To have a new thread, declare a class descending from cIsClass. +Then override its Execute() method to provide your thread processing. +In the descending class' constructor call the Start() method to start the thread once you're finished with initialization. +*/ + + + + + +#pragma once +#ifndef CISTHREAD_H_INCLUDED +#define CISTHREAD_H_INCLUDED + + + + + +class cIsThread +{ +protected: + /// This is the main thread entrypoint + virtual void Execute(void) = 0; + + /// The overriden Execute() method should check this value periodically and terminate if this is true + volatile bool m_ShouldTerminate; + +public: + cIsThread(const AString & iThreadName); + ~cIsThread(); + + /// Starts the thread; returns without waiting for the actual start + bool Start(void); + + /// Signals the thread to terminate and waits until it's finished + void Stop(void); + + /// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag + bool Wait(void); + + /// Returns the OS-dependent thread ID for the caller's thread + static unsigned long GetCurrentID(void); + +protected: + AString m_ThreadName; + + // Value used for "no handle": + #ifdef _WIN32 + #define NULL_HANDLE NULL + #else + #define NULL_HANDLE 0 + #endif + + #ifdef _WIN32 + + HANDLE m_Handle; + + static DWORD_PTR __stdcall thrExecute(LPVOID a_Param) + { + // Create a window so that the thread can be identified by 3rd party tools: + HWND IdentificationWnd = CreateWindow("STATIC", ((cIsThread *)a_Param)->m_ThreadName.c_str(), 0, 0, 0, 0, WS_OVERLAPPED, NULL, NULL, NULL, NULL); + + // Run the thread: + ((cIsThread *)a_Param)->Execute(); + + // Destroy the identification window: + DestroyWindow(IdentificationWnd); + + return 0; + } + + #else // _WIN32 + + pthread_t m_Handle; + + static void * thrExecute(void * a_Param) + { + ((cIsThread *)a_Param)->Execute(); + return NULL; + } + + #endif // else _WIN32 +} ; + + + + + +#endif // CISTHREAD_H_INCLUDED + + + + diff --git a/src/OSSupport/ListenThread.cpp b/src/OSSupport/ListenThread.cpp new file mode 100644 index 000000000..ba3198764 --- /dev/null +++ b/src/OSSupport/ListenThread.cpp @@ -0,0 +1,238 @@ + +// ListenThread.cpp + +// Implements the cListenThread class representing the thread that listens for client connections + +#include "Globals.h" +#include "ListenThread.h" + + + + + +cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) : + super(Printf("ListenThread %s", a_ServiceName.c_str())), + m_Callback(a_Callback), + m_Family(a_Family), + m_ShouldReuseAddr(false), + m_ServiceName(a_ServiceName) +{ +} + + + + + +cListenThread::~cListenThread() +{ + Stop(); +} + + + + + +bool cListenThread::Initialize(const AString & a_PortsString) +{ + ASSERT(m_Sockets.empty()); // Not yet started + + if (!CreateSockets(a_PortsString)) + { + return false; + } + + return true; +} + + + + + +bool cListenThread::Start(void) +{ + if (m_Sockets.empty()) + { + // There are no sockets listening, either forgotten to initialize or the user specified no listening ports + // Report as successful, though + return true; + } + return super::Start(); +} + + + + + +void cListenThread::Stop(void) +{ + if (m_Sockets.empty()) + { + // No sockets means no thread was running in the first place + return; + } + + m_ShouldTerminate = true; + + // Close one socket to wake the thread up from the select() call + m_Sockets[0].CloseSocket(); + + // Wait for the thread to finish + super::Wait(); + + // Close all the listening sockets: + for (cSockets::iterator itr = m_Sockets.begin() + 1, end = m_Sockets.end(); itr != end; ++itr) + { + itr->CloseSocket(); + } // for itr - m_Sockets[] + m_Sockets.clear(); +} + + + + + +void cListenThread::SetReuseAddr(bool a_Reuse) +{ + ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet + + m_ShouldReuseAddr = a_Reuse; +} + + + + + +bool cListenThread::CreateSockets(const AString & a_PortsString) +{ + AStringVector Ports = StringSplitAndTrim(a_PortsString, ","); + + if (Ports.empty()) + { + return false; + } + + AString FamilyStr = m_ServiceName; + switch (m_Family) + { + case cSocket::IPv4: FamilyStr.append(" IPv4"); break; + case cSocket::IPv6: FamilyStr.append(" IPv6"); break; + default: + { + ASSERT(!"Unknown address family"); + break; + } + } + + for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr) + { + int Port = atoi(itr->c_str()); + if ((Port <= 0) || (Port > 65535)) + { + LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str()); + continue; + } + m_Sockets.push_back(cSocket::CreateSocket(m_Family)); + if (!m_Sockets.back().IsValid()) + { + LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + if (m_ShouldReuseAddr) + { + if (!m_Sockets.back().SetReuseAddress()) + { + LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + } + } + + // Bind to port: + bool res = false; + switch (m_Family) + { + case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break; + case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break; + default: + { + ASSERT(!"Unknown address family"); + res = false; + } + } + if (!res) + { + LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + if (!m_Sockets.back().Listen()) + { + LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port); + } // for itr - Ports[] + + return !(m_Sockets.empty()); +} + + + + + +void cListenThread::Execute(void) +{ + if (m_Sockets.empty()) + { + LOGD("Empty cListenThread, ending thread now."); + return; + } + + // Find the highest socket number: + cSocket::xSocket Highest = m_Sockets[0].GetSocket(); + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + if (itr->GetSocket() > Highest) + { + Highest = itr->GetSocket(); + } + } // for itr - m_Sockets[] + + while (!m_ShouldTerminate) + { + // Put all sockets into a FD set: + fd_set fdRead; + FD_ZERO(&fdRead); + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + FD_SET(itr->GetSocket(), &fdRead); + } // for itr - m_Sockets[] + + timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait: + tv.tv_sec = 1; + tv.tv_usec = 0; + if (select(Highest + 1, &fdRead, NULL, NULL, &tv) == -1) + { + LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str()); + continue; + } + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead)) + { + cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6(); + if (Client.IsValid()) + { + m_Callback.OnConnectionAccepted(Client); + } + } + } // for itr - m_Sockets[] + } // while (!m_ShouldTerminate) +} + + + + diff --git a/src/OSSupport/ListenThread.h b/src/OSSupport/ListenThread.h new file mode 100644 index 000000000..4e337d814 --- /dev/null +++ b/src/OSSupport/ListenThread.h @@ -0,0 +1,83 @@ + +// ListenThread.h + +// Declares the cListenThread class representing the thread that listens for client connections + + + + + +#pragma once + +#include "IsThread.h" +#include "Socket.h" + + + + + +// fwd: +class cServer; + + + + + +class cListenThread : + public cIsThread +{ + typedef cIsThread super; + +public: + /// Used as the callback for connection events + class cCallback + { + public: + /// This callback is called whenever a socket connection is accepted + virtual void OnConnectionAccepted(cSocket & a_Socket) = 0; + } ; + + cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName = ""); + ~cListenThread(); + + /// Creates all the sockets, returns trus if successful, false if not. + bool Initialize(const AString & a_PortsString); + + bool Start(void); + + void Stop(void); + + /// Call before Initialize() to set the "reuse" flag on the sockets + void SetReuseAddr(bool a_Reuse = true); + +protected: + typedef std::vector cSockets; + + /// The callback which to notify of incoming connections + cCallback & m_Callback; + + /// Socket address family to use + cSocket::eFamily m_Family; + + /// Sockets that are being monitored + cSockets m_Sockets; + + /// If set to true, the SO_REUSEADDR socket option is set to true + bool m_ShouldReuseAddr; + + /// Name of the service that's listening on the ports; for logging purposes only + AString m_ServiceName; + + + /** Fills in m_Sockets with individual sockets, each for one port specified in a_PortsString. + Returns true if successful and at least one socket has been created + */ + bool CreateSockets(const AString & a_PortsString); + + // cIsThread override: + virtual void Execute(void) override; +} ; + + + + diff --git a/src/OSSupport/Semaphore.cpp b/src/OSSupport/Semaphore.cpp new file mode 100644 index 000000000..468de6858 --- /dev/null +++ b/src/OSSupport/Semaphore.cpp @@ -0,0 +1,91 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + + + + + +cSemaphore::cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount /* = 0 */ ) +#ifndef _WIN32 + : m_bNamed( false ) +#endif +{ +#ifndef _WIN32 + (void)a_MaxCount; + m_Handle = new sem_t; + if (sem_init( (sem_t*)m_Handle, 0, 0)) + { + LOG("WARNING cSemaphore: Could not create unnamed semaphore, fallback to named."); + delete (sem_t*)m_Handle; // named semaphores return their own address + m_bNamed = true; + + AString Name; + Printf(Name, "cSemaphore%p", this ); + m_Handle = sem_open(Name.c_str(), O_CREAT, 777, a_InitialCount); + if( m_Handle == SEM_FAILED ) + { + LOG("ERROR: Could not create Semaphore. (%i)", errno ); + } + else + { + if( sem_unlink(Name.c_str()) != 0 ) + { + LOG("ERROR: Could not unlink cSemaphore. (%i)", errno); + } + } + } +#else + m_Handle = CreateSemaphore( + NULL, // security attribute + a_InitialCount, // initial count + a_MaxCount, // maximum count + 0 // name (optional) + ); +#endif +} + +cSemaphore::~cSemaphore() +{ +#ifdef _WIN32 + CloseHandle( m_Handle ); +#else + if( m_bNamed ) + { + if( sem_close( (sem_t*)m_Handle ) != 0 ) + { + LOG("ERROR: Could not close cSemaphore. (%i)", errno); + } + } + else + { + sem_destroy( (sem_t*)m_Handle ); + delete (sem_t*)m_Handle; + } + m_Handle = 0; + +#endif +} + +void cSemaphore::Wait() +{ +#ifndef _WIN32 + if( sem_wait( (sem_t*)m_Handle ) != 0) + { + LOG("ERROR: Could not wait for cSemaphore. (%i)", errno); + } +#else + WaitForSingleObject( m_Handle, INFINITE); +#endif +} + +void cSemaphore::Signal() +{ +#ifndef _WIN32 + if( sem_post( (sem_t*)m_Handle ) != 0 ) + { + LOG("ERROR: Could not signal cSemaphore. (%i)", errno); + } +#else + ReleaseSemaphore( m_Handle, 1, NULL ); +#endif +} diff --git a/src/OSSupport/Semaphore.h b/src/OSSupport/Semaphore.h new file mode 100644 index 000000000..fbe8907f1 --- /dev/null +++ b/src/OSSupport/Semaphore.h @@ -0,0 +1,17 @@ +#pragma once + +class cSemaphore +{ +public: + cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount = 0 ); + ~cSemaphore(); + + void Wait(); + void Signal(); +private: + void* m_Handle; // HANDLE pointer + +#ifndef _WIN32 + bool m_bNamed; +#endif +}; diff --git a/src/OSSupport/Sleep.cpp b/src/OSSupport/Sleep.cpp new file mode 100644 index 000000000..70fb06b40 --- /dev/null +++ b/src/OSSupport/Sleep.cpp @@ -0,0 +1,19 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#ifndef _WIN32 + #include +#endif + + + + + +void cSleep::MilliSleep( unsigned int a_MilliSeconds ) +{ +#ifdef _WIN32 + Sleep(a_MilliSeconds); // Don't tick too much +#else + usleep(a_MilliSeconds*1000); +#endif +} diff --git a/src/OSSupport/Sleep.h b/src/OSSupport/Sleep.h new file mode 100644 index 000000000..5298c15da --- /dev/null +++ b/src/OSSupport/Sleep.h @@ -0,0 +1,7 @@ +#pragma once + +class cSleep +{ +public: + static void MilliSleep( unsigned int a_MilliSeconds ); +}; \ No newline at end of file diff --git a/src/OSSupport/Socket.cpp b/src/OSSupport/Socket.cpp new file mode 100644 index 000000000..48b5d704d --- /dev/null +++ b/src/OSSupport/Socket.cpp @@ -0,0 +1,396 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Socket.h" + +#ifndef _WIN32 + #include + #include + #include //inet_ntoa() +#else + #define socklen_t int +#endif + + + + + +cSocket::cSocket(xSocket a_Socket) + : m_Socket(a_Socket) +{ +} + + + + + +cSocket::~cSocket() +{ + // Do NOT close the socket; this class is an API wrapper, not a RAII! +} + + + + + +cSocket::operator cSocket::xSocket() const +{ + return m_Socket; +} + + + + + +cSocket::xSocket cSocket::GetSocket() const +{ + return m_Socket; +} + + + + + +bool cSocket::IsValidSocket(cSocket::xSocket a_Socket) +{ + #ifdef _WIN32 + return (a_Socket != INVALID_SOCKET); + #else // _WIN32 + return (a_Socket >= 0); + #endif // else _WIN32 +} + + + + + +void cSocket::CloseSocket() +{ + #ifdef _WIN32 + + closesocket(m_Socket); + + #else // _WIN32 + + if (shutdown(m_Socket, SHUT_RDWR) != 0)//SD_BOTH); + { + LOGWARN("Error on shutting down socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); + } + if (close(m_Socket) != 0) + { + LOGWARN("Error closing socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); + } + + #endif // else _WIN32 + + // Invalidate the socket so that this object can be re-used for another connection + m_Socket = INVALID_SOCKET; +} + + + + + +AString cSocket::GetErrorString( int a_ErrNo ) +{ + char buffer[ 1024 ]; + AString Out; + + #ifdef _WIN32 + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, a_ErrNo, 0, buffer, ARRAYCOUNT(buffer), NULL); + Printf(Out, "%d: %s", a_ErrNo, buffer); + if (!Out.empty() && (Out[Out.length() - 1] == '\n')) + { + Out.erase(Out.length() - 2); + } + return Out; + + #else // _WIN32 + + // According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r(): + + #if ( _GNU_SOURCE ) && !defined(ANDROID_NDK) // GNU version of strerror_r() + + char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) ); + if( res != NULL ) + { + Printf(Out, "%d: %s", a_ErrNo, res); + return Out; + } + + #else // XSI version of strerror_r(): + + int res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) ); + if( res == 0 ) + { + Printf(Out, "%d: %s", a_ErrNo, buffer); + return Out; + } + + #endif // strerror_r() version + + else + { + Printf(Out, "Error %d while getting error string for error #%d!", errno, a_ErrNo); + return Out; + } + + #endif // else _WIN32 +} + + + + +int cSocket::GetLastError() +{ +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + + + + + +bool cSocket::SetReuseAddress(void) +{ + #if defined(_WIN32) || defined(ANDROID_NDK) + char yes = 1; + #else + int yes = 1; + #endif + return (setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == 0); +} + + + + + +int cSocket::WSAStartup() +{ +#ifdef _WIN32 + WSADATA wsaData; + memset(&wsaData, 0, sizeof(wsaData)); + return ::WSAStartup(MAKEWORD(2, 2),&wsaData); +#else + return 0; +#endif +} + + + + + +cSocket cSocket::CreateSocket(eFamily a_Family) +{ + return socket((int)a_Family, SOCK_STREAM, 0); +} + + + + + +bool cSocket::BindToAnyIPv4(unsigned short a_Port) +{ + sockaddr_in local; + memset(&local, 0, sizeof(local)); + + local.sin_family = AF_INET; + local.sin_port = htons((u_short)a_Port); + + return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0); +} + + + + + +bool cSocket::BindToAnyIPv6(unsigned short a_Port) +{ + // Cannot use socckaddr_in6, because it is not defined in the default VS2008 SDK + // Must jump through hoops here + + sockaddr_in6 local; + memset(&local, 0, sizeof(local)); + + local.sin6_family = AF_INET6; + local.sin6_port = htons((u_short)a_Port); + + return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0); +} + + + + + +bool cSocket::BindToLocalhostIPv4(unsigned short a_Port) +{ + sockaddr_in local; + memset(&local, 0, sizeof(local)); + + local.sin_family = AF_INET;; + local.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + local.sin_port = htons((u_short)a_Port); + + return (bind(m_Socket, (sockaddr*)&local, sizeof(local)) == 0); +} + + + + + +bool cSocket::Listen(int a_Backlog) +{ + return (listen(m_Socket, a_Backlog) == 0); +} + + + + + +cSocket cSocket::AcceptIPv4(void) +{ + sockaddr_in from; + socklen_t fromlen = sizeof(from); + + cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen); + + if (SClient.IsValid() && (from.sin_addr.s_addr != 0)) // Get IP in string form + { + SClient.m_IPString = inet_ntoa(from.sin_addr); + } + return SClient; +} + + + + + +cSocket cSocket::AcceptIPv6(void) +{ + sockaddr_in6 from; + socklen_t fromlen = sizeof(from); + + cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen); + + // Get IP in string form: + if (SClient.IsValid()) + { + #if defined(_WIN32) + // Windows XP doesn't have inet_ntop, so we need to improvise. And MSVC has different headers than GCC + #ifdef _MSC_VER + // MSVC version + Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x", + from.sin6_addr.u.Word[0], + from.sin6_addr.u.Word[1], + from.sin6_addr.u.Word[2], + from.sin6_addr.u.Word[3], + from.sin6_addr.u.Word[4], + from.sin6_addr.u.Word[5], + from.sin6_addr.u.Word[6], + from.sin6_addr.u.Word[7] + ); + #else // _MSC_VER + // MinGW + Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x", + from.sin6_addr.s6_addr16[0], + from.sin6_addr.s6_addr16[1], + from.sin6_addr.s6_addr16[2], + from.sin6_addr.s6_addr16[3], + from.sin6_addr.s6_addr16[4], + from.sin6_addr.s6_addr16[5], + from.sin6_addr.s6_addr16[6], + from.sin6_addr.s6_addr16[7] + ); + #endif // else _MSC_VER + #else + char buffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(from.sin6_addr), buffer, sizeof(buffer)); + SClient.m_IPString.assign(buffer); + #endif // _WIN32 + } + return SClient; +} + + + + + +bool cSocket::ConnectToLocalhostIPv4(unsigned short a_Port) +{ + sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + server.sin_port = htons(a_Port); + return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0); +} + + + + + +bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port) +{ + // First try IP Address string to hostent conversion, because it's faster + unsigned long addr = inet_addr(a_HostNameOrAddr.c_str()); + hostent * hp = gethostbyaddr((char*)&addr, sizeof(addr), AF_INET); + if (hp == NULL) + { + // It is not an IP Address string, but rather a regular hostname, resolve: + hp = gethostbyname(a_HostNameOrAddr.c_str()); + if (hp == NULL) + { + LOGWARN("cTCPLink: Could not resolve hostname \"%s\"", a_HostNameOrAddr.c_str()); + CloseSocket(); + return false; + } + } + + sockaddr_in server; + server.sin_addr.s_addr = *((unsigned long*)hp->h_addr); + server.sin_family = AF_INET; + server.sin_port = htons( (unsigned short)a_Port ); + return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0); +} + + + + + +int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags) +{ + return recv(m_Socket, a_Buffer, a_Length, a_Flags); +} + + + + + +int cSocket::Send(const char * a_Buffer, unsigned int a_Length) +{ + return send(m_Socket, a_Buffer, a_Length, 0); +} + + + + + +unsigned short cSocket::GetPort(void) const +{ + ASSERT(IsValid()); + + sockaddr_in Addr; + socklen_t AddrSize = sizeof(Addr); + if (getsockname(m_Socket, (sockaddr *)&Addr, &AddrSize) != 0) + { + return 0; + } + return ntohs(Addr.sin_port); +} + + + + diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h new file mode 100644 index 000000000..34f09cc74 --- /dev/null +++ b/src/OSSupport/Socket.h @@ -0,0 +1,101 @@ + +#pragma once + + + + + +class cSocket +{ +public: + enum eFamily + { + IPv4 = AF_INET, + IPv6 = AF_INET6, + } ; + +#ifdef _WIN32 + typedef SOCKET xSocket; +#else + typedef int xSocket; + static const int INVALID_SOCKET = -1; +#endif + + cSocket(void) : m_Socket(INVALID_SOCKET) {} + cSocket(xSocket a_Socket); + ~cSocket(); + + bool IsValid(void) const { return IsValidSocket(m_Socket); } + void CloseSocket(void); + + operator xSocket(void) const; + xSocket GetSocket(void) const; + + bool operator == (const cSocket & a_Other) {return m_Socket == a_Other.m_Socket; } + + void SetSocket(xSocket a_Socket); + + /// Sets the address-reuse socket flag; returns true on success + bool SetReuseAddress(void); + + static int WSAStartup(void); + + static AString GetErrorString(int a_ErrNo); + static int GetLastError(); + static AString GetLastErrorString(void) + { + return GetErrorString(GetLastError()); + } + + /// Creates a new socket of the specified address family + static cSocket CreateSocket(eFamily a_Family); + + inline static bool IsSocketError(int a_ReturnedValue) + { + #ifdef _WIN32 + return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0); + #else + return (a_ReturnedValue <= 0); + #endif + } + + static bool IsValidSocket(xSocket a_Socket); + + static const unsigned short ANY_PORT = 0; // When given to Bind() functions, they will find a free port + static const int DEFAULT_BACKLOG = 10; + + /// Binds to the specified port on "any" interface (0.0.0.0). Returns true if successful. + bool BindToAnyIPv4(unsigned short a_Port); + + /// Binds to the specified port on "any" interface (::/128). Returns true if successful. + bool BindToAnyIPv6(unsigned short a_Port); + + /// Binds to the specified port on localhost interface (127.0.0.1) through IPv4. Returns true if successful. + bool BindToLocalhostIPv4(unsigned short a_Port); + + /// Sets the socket to listen for incoming connections. Returns true if successful. + bool Listen(int a_Backlog = DEFAULT_BACKLOG); + + /// Accepts an IPv4 incoming connection. Blocks if none available. + cSocket AcceptIPv4(void); + + /// Accepts an IPv6 incoming connection. Blocks if none available. + cSocket AcceptIPv6(void); + + /// Connects to a localhost socket on the specified port using IPv4; returns true if successful. + bool ConnectToLocalhostIPv4(unsigned short a_Port); + + /// Connects to the specified host or string IP address and port, using IPv4. Returns true if successful. + bool ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port); + + int Receive(char * a_Buffer, unsigned int a_Length, unsigned int a_Flags); + int Send (const char * a_Buffer, unsigned int a_Length); + + unsigned short GetPort(void) const; // Returns 0 on failure + + const AString & GetIPString(void) const { return m_IPString; } + +private: + xSocket m_Socket; + AString m_IPString; +}; \ No newline at end of file diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp new file mode 100644 index 000000000..3e505616c --- /dev/null +++ b/src/OSSupport/SocketThreads.cpp @@ -0,0 +1,675 @@ + +// cSocketThreads.cpp + +// Implements the cSocketThreads class representing the heart of MCS's client networking. +// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support +// For more detail, see http://forum.mc-server.org/showthread.php?tid=327 + +#include "Globals.h" +#include "SocketThreads.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSocketThreads: + +cSocketThreads::cSocketThreads(void) +{ +} + + + + + +cSocketThreads::~cSocketThreads() +{ + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + delete *itr; + } // for itr - m_Threads[] + m_Threads.clear(); +} + + + + + + +bool cSocketThreads::AddClient(const cSocket & a_Socket, cCallback * a_Client) +{ + // Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client + + // Try to add to existing threads: + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->IsValid() && (*itr)->HasEmptySlot()) + { + (*itr)->AddClient(a_Socket, a_Client); + return true; + } + } + + // No thread has free space, create a new one: + LOGD("Creating a new cSocketThread (currently have %d)", m_Threads.size()); + cSocketThread * Thread = new cSocketThread(this); + if (!Thread->Start()) + { + // There was an error launching the thread (but it was already logged along with the reason) + LOGERROR("A new cSocketThread failed to start"); + delete Thread; + return false; + } + Thread->AddClient(a_Socket, a_Client); + m_Threads.push_back(Thread); + return true; +} + + + + + +/* +void cSocketThreads::RemoveClient(const cSocket * a_Socket) +{ + // Remove the socket (and associated client) from processing + + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->RemoveSocket(a_Socket)) + { + return; + } + } // for itr - m_Threads[] + + // Cannot assert here, this may actually happen legally, since cClientHandle has to clean up the socket and it may have already closed in the meantime + // ASSERT(!"Removing an unknown socket"); +} +*/ + + + + + +void cSocketThreads::RemoveClient(const cCallback * a_Client) +{ + // Remove the associated socket and the client from processing + + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->RemoveClient(a_Client)) + { + return; + } + } // for itr - m_Threads[] + + ASSERT(!"Removing an unknown client"); +} + + + + + +void cSocketThreads::NotifyWrite(const cCallback * a_Client) +{ + // Notifies the thread responsible for a_Client that the client has something to write + + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->NotifyWrite(a_Client)) + { + return; + } + } // for itr - m_Threads[] + + // Cannot assert - this normally happens if a client disconnects and has pending packets, the cServer::cNotifyWriteThread will call this on invalid clients too + // ASSERT(!"Notifying write to an unknown client"); +} + + + + + +void cSocketThreads::Write(const cCallback * a_Client, const AString & a_Data) +{ + // Puts a_Data into outgoing data queue for a_Client + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->Write(a_Client, a_Data)) + { + return; + } + } // for itr - m_Threads[] + + // This may be perfectly legal, if the socket has been destroyed and the client is finishing up + // ASSERT(!"Writing to an unknown socket"); +} + + + + + +/// Stops reading from the socket - when this call returns, no more calls to the callbacks are made +void cSocketThreads::StopReading(const cCallback * a_Client) +{ + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->StopReading(a_Client)) + { + return; + } + } // for itr - m_Threads[] + + // Cannot assert, this normally happens if the socket is closed before the client deinitializes + // ASSERT(!"Stopping reading on an unknown client"); +} + + + + + +/// Queues the socket for closing, as soon as its outgoing data is sent +void cSocketThreads::QueueClose(const cCallback * a_Client) +{ + LOGD("QueueClose(client %p)", a_Client); + + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->QueueClose(a_Client)) + { + return; + } + } // for itr - m_Threads[] + + ASSERT(!"Queueing close of an unknown client"); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cSocketThreads::cSocketThread: + +cSocketThreads::cSocketThread::cSocketThread(cSocketThreads * a_Parent) : + cIsThread("cSocketThread"), + m_Parent(a_Parent), + m_NumSlots(0) +{ + // Nothing needed yet +} + + + + + +cSocketThreads::cSocketThread::~cSocketThread() +{ + m_ShouldTerminate = true; + + // Notify the thread: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("a", 1); + + // Wait for the thread to finish: + Wait(); + + // Close the control sockets: + m_ControlSocket1.CloseSocket(); + m_ControlSocket2.CloseSocket(); +} + + + + + +void cSocketThreads::cSocketThread::AddClient(const cSocket & a_Socket, cCallback * a_Client) +{ + ASSERT(m_NumSlots < MAX_SLOTS); // Use HasEmptySlot() to check before adding + + m_Slots[m_NumSlots].m_Client = a_Client; + m_Slots[m_NumSlots].m_Socket = a_Socket; + m_Slots[m_NumSlots].m_Outgoing.clear(); + m_Slots[m_NumSlots].m_ShouldClose = false; + m_Slots[m_NumSlots].m_ShouldCallClient = true; + m_NumSlots++; + + // Notify the thread of the change: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("a", 1); +} + + + + + +bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client) +{ + // Returns true if removed, false if not found + + if (m_NumSlots == 0) + { + return false; + } + + for (int i = m_NumSlots - 1; i >= 0 ; --i) + { + if (m_Slots[i].m_Client != a_Client) + { + continue; + } + + // Found, remove it: + m_Slots[i] = m_Slots[--m_NumSlots]; + + // Notify the thread of the change: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("r", 1); + return true; + } // for i - m_Slots[] + + // Not found + return false; +} + + + + + +bool cSocketThreads::cSocketThread::RemoveSocket(const cSocket * a_Socket) +{ + // Returns true if removed, false if not found + + for (int i = m_NumSlots - 1; i >= 0 ; --i) + { + if (m_Slots[i].m_Socket != *a_Socket) + { + continue; + } + + // Found, remove it: + m_Slots[i] = m_Slots[--m_NumSlots]; + + // Notify the thread of the change: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("r", 1); + return true; + } // for i - m_Slots[] + + // Not found + return false; +} + + + + + +bool cSocketThreads::cSocketThread::HasClient(const cCallback * a_Client) const +{ + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Client == a_Client) + { + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::HasSocket(const cSocket * a_Socket) const +{ + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Socket == *a_Socket) + { + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::NotifyWrite(const cCallback * a_Client) +{ + if (HasClient(a_Client)) + { + // Notify the thread that there's another packet in the queue: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("q", 1); + return true; + } + return false; +} + + + + + +bool cSocketThreads::cSocketThread::Write(const cCallback * a_Client, const AString & a_Data) +{ + // Returns true if socket handled by this thread + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Client == a_Client) + { + m_Slots[i].m_Outgoing.append(a_Data); + + // Notify the thread that there's data in the queue: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("q", 1); + + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::StopReading (const cCallback * a_Client) +{ + // Returns true if client handled by this thread + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Client == a_Client) + { + m_Slots[i].m_ShouldCallClient = false; + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::QueueClose(const cCallback * a_Client) +{ + // Returns true if socket handled by this thread + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Client == a_Client) + { + m_Slots[i].m_ShouldClose = true; + + // Notify the thread that there's a close queued (in case its conditions are already met): + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("c", 1); + + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::Start(void) +{ + // Create the control socket listener + m_ControlSocket2 = cSocket::CreateSocket(cSocket::IPv4); + if (!m_ControlSocket2.IsValid()) + { + LOGERROR("Cannot create a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + return false; + } + if (!m_ControlSocket2.BindToLocalhostIPv4(cSocket::ANY_PORT)) + { + LOGERROR("Cannot bind a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return false; + } + if (!m_ControlSocket2.Listen(1)) + { + LOGERROR("Cannot listen on a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return false; + } + if (m_ControlSocket2.GetPort() == 0) + { + LOGERROR("Cannot determine Control socket port (\"%s\"); conitnuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return false; + } + + // Start the thread + if (!super::Start()) + { + LOGERROR("Cannot start new cSocketThread"); + m_ControlSocket2.CloseSocket(); + return false; + } + + // Finish connecting the control socket by accepting connection from the thread's socket + cSocket tmp = m_ControlSocket2.AcceptIPv4(); + if (!tmp.IsValid()) + { + LOGERROR("Cannot link Control sockets for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return false; + } + m_ControlSocket2.CloseSocket(); + m_ControlSocket2 = tmp; + + return true; +} + + + + + +void cSocketThreads::cSocketThread::Execute(void) +{ + // Connect the "client" part of the Control socket: + m_ControlSocket1 = cSocket::CreateSocket(cSocket::IPv4); + ASSERT(m_ControlSocket2.GetPort() != 0); // We checked in the Start() method, but let's be sure + if (!m_ControlSocket1.ConnectToLocalhostIPv4(m_ControlSocket2.GetPort())) + { + LOGERROR("Cannot connect Control sockets for a cSocketThread (\"%s\"); continuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return; + } + + // The main thread loop: + while (!m_ShouldTerminate) + { + // Put all sockets into the Read set: + fd_set fdRead; + cSocket::xSocket Highest = m_ControlSocket1.GetSocket(); + + PrepareSet(&fdRead, Highest); + + // Wait for the sockets: + if (select(Highest + 1, &fdRead, NULL, NULL, NULL) == -1) + { + LOG("select(R) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str()); + continue; + } + + ReadFromSockets(&fdRead); + + // Test sockets for writing: + fd_set fdWrite; + Highest = m_ControlSocket1.GetSocket(); + PrepareSet(&fdWrite, Highest); + timeval Timeout; + Timeout.tv_sec = 0; + Timeout.tv_usec = 0; + if (select(Highest + 1, NULL, &fdWrite, NULL, &Timeout) == -1) + { + LOG("select(W) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str()); + continue; + } + + WriteToSockets(&fdWrite); + } // while (!mShouldTerminate) +} + + + + + +void cSocketThreads::cSocketThread::PrepareSet(fd_set * a_Set, cSocket::xSocket & a_Highest) +{ + FD_ZERO(a_Set); + FD_SET(m_ControlSocket1.GetSocket(), a_Set); + + cCSLock Lock(m_Parent->m_CS); + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (!m_Slots[i].m_Socket.IsValid()) + { + continue; + } + cSocket::xSocket s = m_Slots[i].m_Socket.GetSocket(); + FD_SET(s, a_Set); + if (s > a_Highest) + { + a_Highest = s; + } + } // for i - m_Slots[] +} + + + + + +void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read) +{ + // Read on available sockets: + + // Reset Control socket state: + if (FD_ISSET(m_ControlSocket1.GetSocket(), a_Read)) + { + char Dummy[128]; + m_ControlSocket1.Receive(Dummy, sizeof(Dummy), 0); + } + + // Read from clients: + cCSLock Lock(m_Parent->m_CS); + for (int i = m_NumSlots - 1; i >= 0; --i) + { + cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket(); + if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Read)) + { + continue; + } + char Buffer[1024]; + int Received = m_Slots[i].m_Socket.Receive(Buffer, ARRAYCOUNT(Buffer), 0); + if (Received == 0) + { + // The socket has been closed by the remote party, close our socket and let it be removed after we process all reading + m_Slots[i].m_Socket.CloseSocket(); + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->SocketClosed(); + } + } + else if (Received > 0) + { + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->DataReceived(Buffer, Received); + } + } + else + { + // The socket has encountered an error, close it and let it be removed after we process all reading + m_Slots[i].m_Socket.CloseSocket(); + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->SocketClosed(); + } + } + } // for i - m_Slots[] +} + + + + + +void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write) +{ + // Write to available client sockets: + cCSLock Lock(m_Parent->m_CS); + for (int i = m_NumSlots - 1; i >= 0; --i) + { + cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket(); + if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Write)) + { + continue; + } + if (m_Slots[i].m_Outgoing.empty()) + { + // Request another chunk of outgoing data: + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->GetOutgoingData(m_Slots[i].m_Outgoing); + } + if (m_Slots[i].m_Outgoing.empty()) + { + // Nothing ready + if (m_Slots[i].m_ShouldClose) + { + // Socket was queued for closing and there's no more data to send, close it now: + + // DEBUG + LOGD("Socket was queued for closing, closing now. Slot %d, client %p, socket %d", i, m_Slots[i].m_Client, m_Slots[i].m_Socket.GetSocket()); + + m_Slots[i].m_Socket.CloseSocket(); + // The slot must be freed actively by the client, using RemoveClient() + } + continue; + } + } // if (outgoing data is empty) + + int Sent = m_Slots[i].m_Socket.Send(m_Slots[i].m_Outgoing.data(), m_Slots[i].m_Outgoing.size()); + if (Sent < 0) + { + int Err = cSocket::GetLastError(); + LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket.GetIPString().c_str(), cSocket::GetErrorString(Err).c_str()); + m_Slots[i].m_Socket.CloseSocket(); + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->SocketClosed(); + } + return; + } + m_Slots[i].m_Outgoing.erase(0, Sent); + + // _X: If there's data left, it means the client is not reading fast enough, the server would unnecessarily spin in the main loop with zero actions taken; so signalling is disabled + // This means that if there's data left, it will be sent only when there's incoming data or someone queues another packet (for any socket handled by this thread) + /* + // If there's any data left, signalize the Control socket: + if (!m_Slots[i].m_Outgoing.empty()) + { + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("q", 1); + } + */ + } // for i - m_Slots[i] +} + + + + diff --git a/src/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h new file mode 100644 index 000000000..ecbac3aeb --- /dev/null +++ b/src/OSSupport/SocketThreads.h @@ -0,0 +1,169 @@ + +// SocketThreads.h + +// Interfaces to the cSocketThreads class representing the heart of MCS's client networking. +// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support +// For more detail, see http://forum.mc-server.org/showthread.php?tid=327 + +/* +Additional details: +When a client is terminating a connection: +- they call the StopReading() method to disable callbacks for the incoming data +- they call the Write() method to queue any outstanding outgoing data +- they call the QueueClose() method to queue the socket to close after outgoing data has been sent. +When a socket slot is marked as having no callback, it is kept alive until its outgoing data queue is empty and its m_ShouldClose flag is set. +This means that the socket can be written to several times before finally closing it via QueueClose() +*/ + + + + + +/// How many clients should one thread handle? (must be less than FD_SETSIZE for your platform) +#define MAX_SLOTS 63 + + + + + +#pragma once +#ifndef CSOCKETTHREADS_H_INCLUDED +#define CSOCKETTHREADS_H_INCLUDED + +#include "Socket.h" +#include "IsThread.h" + + + + +// Check MAX_SLOTS: +#if MAX_SLOTS >= FD_SETSIZE + #error "MAX_SLOTS must be less than FD_SETSIZE for your platform! (otherwise select() won't work)" +#endif + + + + + +// fwd: +class cSocket; +class cClientHandle; + + + + + +class cSocketThreads +{ +public: + + // Clients of cSocketThreads must implement this interface to be able to communicate + class cCallback + { + public: + /// Called when data is received from the remote party + virtual void DataReceived(const char * a_Data, int a_Size) = 0; + + /// Called when data can be sent to remote party; the function is supposed to append outgoing data to a_Data + virtual void GetOutgoingData(AString & a_Data) = 0; + + /// Called when the socket has been closed for any reason + virtual void SocketClosed(void) = 0; + } ; + + + cSocketThreads(void); + ~cSocketThreads(); + + /// Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client; returns true if successful + bool AddClient(const cSocket & a_Socket, cCallback * a_Client); + + /// Remove the associated socket and the client from processing. The socket is left to send its data and is removed only after all its m_OutgoingData is sent + void RemoveClient(const cCallback * a_Client); + + /// Notify the thread responsible for a_Client that the client has something to write + void NotifyWrite(const cCallback * a_Client); + + /// Puts a_Data into outgoing data queue for a_Client + void Write(const cCallback * a_Client, const AString & a_Data); + + /// Stops reading from the client - when this call returns, no more calls to the callbacks are made + void StopReading(const cCallback * a_Client); + + /// Queues the client for closing, as soon as its outgoing data is sent + void QueueClose(const cCallback * a_Client); + +private: + + class cSocketThread : + public cIsThread + { + typedef cIsThread super; + + public: + + cSocketThread(cSocketThreads * a_Parent); + ~cSocketThread(); + + // All these methods assume parent's m_CS is locked + bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; } + bool IsEmpty (void) const {return m_NumSlots == 0; } + + void AddClient (const cSocket & a_Socket, cCallback * a_Client); // Takes ownership of the socket + bool RemoveClient(const cCallback * a_Client); // Returns true if removed, false if not found + bool RemoveSocket(const cSocket * a_Socket); // Returns true if removed, false if not found + bool HasClient (const cCallback * a_Client) const; + bool HasSocket (const cSocket * a_Socket) const; + bool NotifyWrite (const cCallback * a_Client); // Returns true if client handled by this thread + bool Write (const cCallback * a_Client, const AString & a_Data); // Returns true if client handled by this thread + bool StopReading (const cCallback * a_Client); // Returns true if client handled by this thread + bool QueueClose (const cCallback * a_Client); // Returns true if client handled by this thread + + bool Start(void); // Hide the cIsThread's Start method, we need to provide our own startup to create the control socket + + bool IsValid(void) const {return m_ControlSocket2.IsValid(); } // If the Control socket dies, the thread is not valid anymore + + private: + + cSocketThreads * m_Parent; + + // Two ends of the control socket, the first is select()-ed, the second is written to for notifications + cSocket m_ControlSocket1; + cSocket m_ControlSocket2; + + // Socket-client-packetqueues triplets. + // Manipulation with these assumes that the parent's m_CS is locked + struct sSlot + { + cSocket m_Socket; // The socket is primarily owned by this + cCallback * m_Client; + AString m_Outgoing; // If sending writes only partial data, the rest is stored here for another send + bool m_ShouldClose; // If true, the socket is to be closed after sending all outgoing data + bool m_ShouldCallClient; // If true, the client callbacks are called. Set to false in StopReading() + } ; + sSlot m_Slots[MAX_SLOTS]; + int m_NumSlots; // Number of slots actually used + + virtual void Execute(void) override; + + void PrepareSet (fd_set * a_Set, cSocket::xSocket & a_Highest); // Puts all sockets into the set, along with m_ControlSocket1 + void ReadFromSockets(fd_set * a_Read); // Reads from sockets indicated in a_Read + void WriteToSockets (fd_set * a_Write); // Writes to sockets indicated in a_Write + } ; + + typedef std::list cSocketThreadList; + + + cCriticalSection m_CS; + cSocketThreadList m_Threads; +} ; + + + + + +#endif // CSOCKETTHREADS_H_INCLUDED + + + + diff --git a/src/OSSupport/Thread.cpp b/src/OSSupport/Thread.cpp new file mode 100644 index 000000000..3df75f0e7 --- /dev/null +++ b/src/OSSupport/Thread.cpp @@ -0,0 +1,128 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + + + + + +// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here: +#ifdef _MSC_VER +// +// Usage: SetThreadName (-1, "MainThread"); +// +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero +} THREADNAME_INFO; + +void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info ); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +} +#endif // _MSC_VER + + + + + +cThread::cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName /* = 0 */ ) + : m_ThreadFunction( a_ThreadFunction ) + , m_Param( a_Param ) + , m_Event( new cEvent() ) + , m_StopEvent( 0 ) +{ + if( a_ThreadName ) + { + m_ThreadName.assign(a_ThreadName); + } +} + + + + + +cThread::~cThread() +{ + delete m_Event; + + if( m_StopEvent ) + { + m_StopEvent->Wait(); + delete m_StopEvent; + } +} + + + + + +void cThread::Start( bool a_bWaitOnDelete /* = true */ ) +{ + if( a_bWaitOnDelete ) + m_StopEvent = new cEvent(); + +#ifndef _WIN32 + pthread_t SndThread; + if( pthread_create( &SndThread, NULL, MyThread, this) ) + LOGERROR("ERROR: Could not create thread!"); +#else + DWORD ThreadID = 0; + HANDLE hThread = CreateThread( 0 // security + ,0 // stack size + , (LPTHREAD_START_ROUTINE) MyThread // function name + ,this // parameters + ,0 // flags + ,&ThreadID ); // thread id + CloseHandle( hThread ); + + #ifdef _MSC_VER + if (!m_ThreadName.empty()) + { + SetThreadName(ThreadID, m_ThreadName.c_str()); + } + #endif // _MSC_VER +#endif + + // Wait until thread has actually been created + m_Event->Wait(); +} + + + + + +#ifdef _WIN32 +unsigned long cThread::MyThread(void* a_Param ) +#else +void *cThread::MyThread( void *a_Param ) +#endif +{ + cThread* self = (cThread*)a_Param; + cEvent* StopEvent = self->m_StopEvent; + + ThreadFunc* ThreadFunction = self->m_ThreadFunction; + void* ThreadParam = self->m_Param; + + // Set event to let other thread know this thread has been created and it's safe to delete the cThread object + self->m_Event->Set(); + + ThreadFunction( ThreadParam ); + + if( StopEvent ) StopEvent->Set(); + return 0; +} diff --git a/src/OSSupport/Thread.h b/src/OSSupport/Thread.h new file mode 100644 index 000000000..3c9316424 --- /dev/null +++ b/src/OSSupport/Thread.h @@ -0,0 +1,26 @@ +#pragma once + +class cThread +{ +public: + typedef void (ThreadFunc)(void*); + cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName = 0 ); + ~cThread(); + + void Start( bool a_bWaitOnDelete = true ); + void WaitForThread(); +private: + ThreadFunc* m_ThreadFunction; + +#ifdef _WIN32 + static unsigned long MyThread(void* a_Param ); +#else + static void *MyThread( void *lpParam ); +#endif + + void* m_Param; + cEvent* m_Event; + cEvent* m_StopEvent; + + AString m_ThreadName; +}; \ No newline at end of file diff --git a/src/OSSupport/Timer.cpp b/src/OSSupport/Timer.cpp new file mode 100644 index 000000000..ed16f9e3a --- /dev/null +++ b/src/OSSupport/Timer.cpp @@ -0,0 +1,37 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Timer.h" + + + + + + +cTimer::cTimer(void) +{ + #ifdef _WIN32 + QueryPerformanceFrequency(&m_TicksPerSecond); + #endif +} + + + + + +long long cTimer::GetNowTime(void) +{ + #ifdef _WIN32 + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return ((now.QuadPart * 1000) / m_TicksPerSecond.QuadPart); + #else + struct timeval now; + gettimeofday(&now, NULL); + return (long long)(now.tv_sec * 1000 + now.tv_usec / 1000); + #endif +} + + + + diff --git a/src/OSSupport/Timer.h b/src/OSSupport/Timer.h new file mode 100644 index 000000000..a059daa41 --- /dev/null +++ b/src/OSSupport/Timer.h @@ -0,0 +1,32 @@ + +// Timer.h + +// Declares the cTimer class representing an OS-independent of retrieving current time with msec accuracy + + + + + +#pragma once + + + + + +class cTimer +{ +public: + cTimer(void); + + // Returns the current time expressed in milliseconds + long long GetNowTime(void); +private: + + #ifdef _WIN32 + LARGE_INTEGER m_TicksPerSecond; + #endif +} ; + + + + diff --git a/src/Piston.cpp b/src/Piston.cpp new file mode 100644 index 000000000..136100922 --- /dev/null +++ b/src/Piston.cpp @@ -0,0 +1,306 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Piston.h" +#include "ChunkDef.h" +#include "Entities/Pickup.h" +#include "Item.h" +#include "Root.h" +#include "ClientHandle.h" +#include "World.h" +#include "Server.h" +#include "Blocks/BlockHandler.h" + + + + + +extern bool g_BlockPistonBreakable[]; + + + + + +/// Number of ticks that the piston extending / retracting waits before setting the block +const int PISTON_TICK_DELAY = 20; + + + + + +cPiston::cPiston(cWorld * a_World) + : m_World(a_World) +{ + +} + + + + + +int cPiston::FirstPassthroughBlock(int pistonX, int pistonY, int pistonZ, NIBBLETYPE pistonmeta) +{ + // Examine each of the 12 blocks ahead of the piston: + for (int ret = 0; ret < 12; ret++) + { + BLOCKTYPE currBlock; + NIBBLETYPE currMeta; + AddDir(pistonX, pistonY, pistonZ, pistonmeta, 1); + m_World->GetBlockTypeMeta(pistonX, pistonY, pistonZ, currBlock, currMeta); + if (CanBreakPush(currBlock, currMeta)) + { + // This block breaks when pushed, extend up to here + return ret; + } + if (!CanPush(currBlock, currMeta)) + { + // This block cannot be pushed at all, the piston can't extend + return -1; + } + } + // There is no space for the blocks to move, piston can't extend + return -1; +} + + + + + +void cPiston::ExtendPiston(int pistx, int pisty, int pistz) +{ + BLOCKTYPE pistonBlock; + NIBBLETYPE pistonMeta; + m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta); + + if (IsExtended(pistonMeta)) + { + // Already extended, bail out + return; + } + + m_World->BroadcastBlockAction(pistx, pisty, pistz, 0, pistonMeta, pistonBlock); + m_World->BroadcastSoundEffect("tile.piston.out", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f); + + int dist = FirstPassthroughBlock(pistx, pisty, pistz, pistonMeta); + if (dist < 0) + { + // FirstPassthroughBlock says piston can't push anything, bail out + return; + } + + // Drop the breakable block in the line, if appropriate: + AddDir(pistx, pisty, pistz, pistonMeta, dist + 1); // "pist" now at the breakable / empty block + BLOCKTYPE currBlock; + NIBBLETYPE currMeta; + m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currMeta); + if (currBlock != E_BLOCK_AIR) + { + cBlockHandler * Handler = BlockHandler(currBlock); + if (Handler->DoesDropOnUnsuitable()) + { + Handler->DropBlock(m_World, NULL, pistx, pisty, pistz); + } + } + + // Push blocks, from the furthest to the nearest: + int oldx = pistx, oldy = pisty, oldz = pistz; + NIBBLETYPE currBlockMeta; + for (int i = dist + 1; i > 1; i--) + { + AddDir(pistx, pisty, pistz, pistonMeta, -1); + m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currBlockMeta); + m_World->QueueSetBlock( oldx, oldy, oldz, currBlock, currBlockMeta, PISTON_TICK_DELAY); + oldx = pistx; + oldy = pisty; + oldz = pistz; + } + + int extx = pistx; + int exty = pisty; + int extz = pistz; + AddDir(pistx, pisty, pistz, pistonMeta, -1); + // "pist" now at piston body, "ext" at future extension + + m_World->QueueSetBlock( pistx, pisty, pistz, pistonBlock, pistonMeta | 0x8, PISTON_TICK_DELAY); + m_World->QueueSetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), PISTON_TICK_DELAY); +} + + + + + +void cPiston::RetractPiston(int pistx, int pisty, int pistz) +{ + BLOCKTYPE pistonBlock; + NIBBLETYPE pistonMeta; + m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta); + if (!IsExtended(pistonMeta)) + { + // Already retracted, bail out + return; + } + + m_World->BroadcastBlockAction(pistx, pisty, pistz, 1, pistonMeta & ~(8), pistonBlock); + m_World->BroadcastSoundEffect("tile.piston.in", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f); + m_World->QueueSetBlock(pistx, pisty, pistz, pistonBlock, pistonMeta & ~(8), PISTON_TICK_DELAY); + + // Check the extension: + AddDir(pistx, pisty, pistz, pistonMeta, 1); + if (m_World->GetBlock(pistx, pisty, pistz) != E_BLOCK_PISTON_EXTENSION) + { + LOGD("%s: Piston without an extension?", __FUNCTION__); + return; + } + + // Retract the extension, pull block if appropriate + if (IsSticky(pistonBlock)) + { + int tempx = pistx, tempy = pisty, tempz = pistz; + AddDir( tempx, tempy, tempz, pistonMeta, 1); + BLOCKTYPE tempBlock; + NIBBLETYPE tempMeta; + m_World->GetBlockTypeMeta(tempx, tempy, tempz, tempBlock, tempMeta); + if (CanPull(tempBlock, tempMeta)) + { + // Pull the block + m_World->QueueSetBlock(pistx, pisty, pistz, tempBlock, tempMeta, PISTON_TICK_DELAY); + m_World->QueueSetBlock(tempx, tempy, tempz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY); + } + else + { + // Retract without pulling + m_World->QueueSetBlock(pistx, pisty, pistz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY); + } + } + else + { + m_World->QueueSetBlock(pistx, pisty, pistz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY); + } +} + + + + + +bool cPiston::IsExtended(NIBBLETYPE a_PistonMeta) +{ + return ((a_PistonMeta & 0x8) != 0x0); +} + + + + + +bool cPiston::IsSticky(BLOCKTYPE a_BlockType) +{ + return (a_BlockType == E_BLOCK_STICKY_PISTON); +} + + + + + +bool cPiston::IsStickyExtension(NIBBLETYPE a_ExtMeta) +{ + return ((a_ExtMeta & 0x08) != 0); +} + + + + + +bool cPiston::CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + switch (a_BlockType) + { + case E_BLOCK_ANVIL: + case E_BLOCK_BED: + case E_BLOCK_BEDROCK: + case E_BLOCK_BREWING_STAND: + case E_BLOCK_CHEST: + case E_BLOCK_COMMAND_BLOCK: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_ENCHANTMENT_TABLE: + case E_BLOCK_END_PORTAL: + case E_BLOCK_END_PORTAL_FRAME: + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_HOPPER: + case E_BLOCK_JUKEBOX: + case E_BLOCK_MOB_SPAWNER: + case E_BLOCK_NETHER_PORTAL: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_OBSIDIAN: + case E_BLOCK_PISTON_EXTENSION: + { + return false; + } + case E_BLOCK_STICKY_PISTON: + case E_BLOCK_PISTON: + { + // A piston can only be pushed if retracted: + return !IsExtended(a_BlockMeta); + } + } + return true; +} + + + + + +bool cPiston::CanBreakPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + return g_BlockPistonBreakable[a_BlockType]; +} + + + + + +bool cPiston::CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + switch (a_BlockType) + { + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + return false; + } + } + + if (CanBreakPush(a_BlockType, a_BlockMeta)) + { + return false; // CanBreakPush returns true, but we need false to prevent pulling + } + + return CanPush(a_BlockType, a_BlockMeta); +} + + + + + +void cPiston::AddDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_PistonMeta, int a_Amount) +{ + switch (a_PistonMeta & 0x07) + { + case 0: a_BlockY -= a_Amount; break; + case 1: a_BlockY += a_Amount; break; + case 2: a_BlockZ -= a_Amount; break; + case 3: a_BlockZ += a_Amount; break; + case 4: a_BlockX -= a_Amount; break; + case 5: a_BlockX += a_Amount; break; + default: + { + LOGWARNING("%s: invalid direction %d, ignoring", __FUNCTION__, a_PistonMeta & 0x07); + break; + } + } +} + + + + diff --git a/src/Piston.h b/src/Piston.h new file mode 100644 index 000000000..cc051e454 --- /dev/null +++ b/src/Piston.h @@ -0,0 +1,94 @@ + +#pragma once + + + + + +// fwd: World.h +class cWorld; + + + + + +class cPiston +{ +public: + + cPiston(cWorld * a_World); + + static NIBBLETYPE RotationPitchToMetaData(double a_Rotation, double a_Pitch) + { + if (a_Pitch >= 50) + { + return 0x1; + } + else if (a_Pitch <= -50) + { + return 0x0; + } + else + { + a_Rotation += 90 + 45; // So its not aligned with axis + + if (a_Rotation > 360) + { + a_Rotation -= 360; + } + if ((a_Rotation >= 0) && (a_Rotation < 90)) + { + return 0x4; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x5; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x3; + } + } + } + + void ExtendPiston( int, int, int ); + void RetractPiston( int, int, int ); + + /// Returns true if the piston (specified by blocktype) is a sticky piston + static bool IsSticky(BLOCKTYPE a_BlockType); + + /// Returns true if the piston (with the specified meta) is extended + static bool IsExtended(NIBBLETYPE a_PistonMeta); + + /// Returns true if the extension (with the specified meta) is sticky + static bool IsStickyExtension(NIBBLETYPE a_ExtMeta); + + /// Returns true if the specified block can be pushed by a piston (and left intact) + static bool CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Returns true if the specified block can be pushed by a piston and broken / replaced + static bool CanBreakPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Returns true if the specified block can be pulled by a sticky piston + static bool CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Updates the coords by the specified amount in the direction a piston of the specified meta is facing + static void AddDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_PistonMeta, int a_Amount); + + + cWorld * m_World; + +private: + void ChainMove( int, int, int, char, unsigned short * ); + + /// Returns how many blocks the piston has to push (where the first free space is); <0 when unpushable + int FirstPassthroughBlock(int a_PistonX, int a_PistonY, int a_PistonZ, NIBBLETYPE a_PistonMeta); +} ; + + + + diff --git a/src/Plugin.cpp b/src/Plugin.cpp new file mode 100644 index 000000000..98ccfb88c --- /dev/null +++ b/src/Plugin.cpp @@ -0,0 +1,38 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Plugin.h" + + + + + +cPlugin::cPlugin(const AString & a_PluginDirectory) : + m_Language(E_CPP), + m_Name(a_PluginDirectory), + m_Version(0), + m_Directory(a_PluginDirectory) +{ +} + + + + + +cPlugin::~cPlugin() +{ + LOGD("Destroying plugin \"%s\".", m_Name.c_str()); +} + + + + + +AString cPlugin::GetLocalFolder(void) const +{ + return std::string("Plugins/") + m_Directory; +} + + + + diff --git a/src/Plugin.h b/src/Plugin.h new file mode 100644 index 000000000..06e5819df --- /dev/null +++ b/src/Plugin.h @@ -0,0 +1,149 @@ + +#pragma once + +#include "Item.h" +#include "PluginManager.h" + + + + + +class cClientHandle; +class cPlayer; +class cPickup; +class cItem; +class cEntity; +class cWorld; +class cChunkDesc; +struct TakeDamageInfo; + +// fwd: cPlayer.h +class cPlayer; + +// fwd: CraftingRecipes.h +class cCraftingGrid; +class cCraftingRecipe; + + + + + +// tolua_begin +class cPlugin +{ +public: + // tolua_end + + cPlugin( const AString & a_PluginDirectory ); + virtual ~cPlugin(); + + virtual void OnDisable(void) {} + virtual bool Initialize(void) = 0; + + // Called each tick + virtual void Tick(float a_Dt) = 0; + + /** + * On all these functions, return true if you want to override default behavior and not call other plugins on that callback. + * You can also return false, so default behavior is used. + **/ + virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) = 0; + virtual bool OnChat (cPlayer * a_Player, AString & a_Message) = 0; + virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; + virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) = 0; + virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) = 0; + virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; + virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; + virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) = 0; + virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; + virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) = 0; + virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; + virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; + virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; + virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) = 0; + virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) = 0; + virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) = 0; + virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) = 0; + virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) = 0; + virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) = 0; + virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerEating (cPlayer & a_Player) = 0; + virtual bool OnPlayerJoined (cPlayer & a_Player) = 0; + virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0; + virtual bool OnPlayerMoved (cPlayer & a_Player) = 0; + virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; + virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) = 0; + virtual bool OnPlayerShooting (cPlayer & a_Player) = 0; + virtual bool OnPlayerSpawned (cPlayer & a_Player) = 0; + virtual bool OnPlayerTossingItem (cPlayer & a_Player) = 0; + virtual bool OnPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; + virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; + virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; + virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; + virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0; + virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) = 0; + virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) = 0; + virtual bool OnSpawningMonster (cWorld & a_World, cMonster & a_Monster) = 0; + virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) = 0; + virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) = 0; + virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) = 0; + virtual bool OnWeatherChanged (cWorld & a_World) = 0; + virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) = 0; + virtual bool OnWorldTick (cWorld & a_World, float a_Dt) = 0; + + /** Handles the command split into a_Split, issued by player a_Player. + Command permissions have already been checked. + Returns true if command handled successfully + */ + virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) = 0; + + /** Handles the console command split into a_Split. + Returns true if command handled successfully. Output is to be sent to the a_Output callback. + */ + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) = 0; + + /// All bound commands are to be removed, do any language-dependent cleanup here + virtual void ClearCommands(void) {} ; + + /// All bound console commands are to be removed, do any language-dependent cleanup here + virtual void ClearConsoleCommands(void) {} ; + + // tolua_begin + const AString & GetName(void) const { return m_Name; } + void SetName(const AString & a_Name) { m_Name = a_Name; } + + int GetVersion(void) const { return m_Version; } + void SetVersion(int a_Version) { m_Version = a_Version; } + + const AString & GetDirectory(void) const {return m_Directory; } + AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead + AString GetLocalFolder(void) const; + // tolua_end + + + /* This should not be exposed to scripting languages */ + enum PluginLanguage + { + E_CPP, + E_LUA, + E_SQUIRREL, // OBSOLETE, but kept in place to remind us of the horrors lurking in the history + }; + PluginLanguage GetLanguage() { return m_Language; } + void SetLanguage( PluginLanguage a_Language ) { m_Language = a_Language; } + +private: + PluginLanguage m_Language; + AString m_Name; + int m_Version; + + AString m_Directory; +}; // tolua_export + + + + diff --git a/src/PluginLua.cpp b/src/PluginLua.cpp new file mode 100644 index 000000000..03aefb098 --- /dev/null +++ b/src/PluginLua.cpp @@ -0,0 +1,1471 @@ + +// PluginLua.cpp + +// Implements the cPluginLua class representing a plugin written in Lua + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#define LUA_USE_POSIX +#include "PluginLua.h" +#include "CommandOutput.h" + +extern "C" +{ + #include "lualib.h" +} + +#include "tolua++.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cPluginLua: + +cPluginLua::cPluginLua(const AString & a_PluginDirectory) : + cPlugin(a_PluginDirectory), + m_LuaState(Printf("plugin %s", a_PluginDirectory.c_str())) +{ +} + + + + + +cPluginLua::~cPluginLua() +{ + cCSLock Lock(m_CriticalSection); + Close(); +} + + + + + +void cPluginLua::Close(void) +{ + if (m_LuaState.IsValid()) + { + // Release all the references in the hook map: + for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH) + { + for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR) + { + delete *itrR; + } // for itrR - itrH->second[] + } // for itrH - m_HookMap[] + m_HookMap.clear(); + + m_LuaState.Close(); + } + else + { + ASSERT(m_HookMap.empty()); + } +} + + + + + +bool cPluginLua::Initialize(void) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + m_LuaState.Create(); + + // Inject the identification global variables into the state: + lua_pushlightuserdata(m_LuaState, this); + lua_setglobal(m_LuaState, LUA_PLUGIN_INSTANCE_VAR_NAME); + lua_pushstring(m_LuaState, GetName().c_str()); + lua_setglobal(m_LuaState, LUA_PLUGIN_NAME_VAR_NAME); + + tolua_pushusertype(m_LuaState, this, "cPluginLua"); + lua_setglobal(m_LuaState, "g_Plugin"); + } + + std::string PluginPath = FILE_IO_PREFIX + GetLocalDirectory() + "/"; + + // Load all files for this plugin, and execute them + AStringList Files = GetDirectoryContents(PluginPath.c_str()); + for (AStringList::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) + { + if (itr->rfind(".lua") == AString::npos) + { + continue; + } + AString Path = PluginPath + *itr; + if (!m_LuaState.LoadFile(Path)) + { + Close(); + return false; + } + } // for itr - Files[] + + // Call intialize function + bool res = false; + if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res)) + { + LOGWARNING("Error in plugin %s: Cannot call the Initialize() function. Plugin is temporarily disabled.", GetName().c_str()); + Close(); + return false; + } + + if (!res) + { + LOGINFO("Plugin %s: Initialize() call failed, plugin is temporarily disabled.", GetName().c_str()); + Close(); + return false; + } + + return true; +} + + + + + +void cPluginLua::OnDisable(void) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.HasFunction("OnDisable")) + { + return; + } + m_LuaState.Call("OnDisable"); +} + + + + + +void cPluginLua::Tick(float a_Dt) +{ + cCSLock Lock(m_CriticalSection); + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TICK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Dt); + } +} + + + + + +bool cPluginLua::OnBlockToPickups(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_TO_PICKUPS]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChat(cPlayer * a_Player, AString & a_Message) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Message, cLuaState::Return, res, a_Message); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkAvailable(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_AVAILABLE]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkUnloaded(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkUnloading(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnCollectingPickup(cPlayer * a_Player, cPickup * a_Pickup) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_COLLECTING_PICKUP]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Pickup, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CRAFTING_NO_RECIPE]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), (cPlayer *)a_Player, a_Grid, a_Recipe, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnDisconnect(cPlayer * a_Player, const AString & a_Reason) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Reason, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Split, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + switch (a_Source) + { + case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res); break; + case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res); break; + case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break; + case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break; + case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + case esWitherSkullBlack: + case esWitherSkullBlue: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + case esWitherBirth: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + case esPlugin: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + default: + { + ASSERT(!"Unhandled ExplosionSource"); + return false; + } + } + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + switch (a_Source) + { + case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherSkullBlack: + case esWitherSkullBlue: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherBirth: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPlugin: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + default: + { + ASSERT(!"Unhandled ExplosionSource"); + return false; + } + } + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnHandshake(cClientHandle * a_Client, const AString & a_Username) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HANDSHAKE]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Client, a_Username, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PULLING_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PUSHING_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Victim, a_Killer, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_LOGIN]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Client, a_ProtocolVersion, a_Username, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_ANIMATION]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_Animation, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BREAKING_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BROKEN_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerEating(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_EATING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_JOINED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_LEFT_CLICK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerMoved(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, &a_Entity, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SHOOTING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SPAWNED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_TOSSING_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_POST_CRAFTING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Grid, a_Recipe, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PRE_CRAFTING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Grid, a_Recipe, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_ENTITY]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Entity, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_MONSTER]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Monster, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_ENTITY]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Entity, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_MONSTER]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Monster, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TAKE_DAMAGE]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Receiver, &a_TDI, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnUpdatedSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATED_SIGN]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnUpdatingSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnWeatherChanged(cWorld & a_World) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + int NewWeather = a_NewWeather; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, NewWeather, cLuaState::Return, res, NewWeather); + if (res) + { + a_NewWeather = (eWeather)NewWeather; + return true; + } + } + a_NewWeather = (eWeather)NewWeather; + return false; +} + + + + + +bool cPluginLua::OnWorldTick(cWorld & a_World, float a_Dt) +{ + cCSLock Lock(m_CriticalSection); + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, a_Dt); + } + return false; +} + + + + + +bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) +{ + ASSERT(!a_Split.empty()); + CommandMap::iterator cmd = m_Commands.find(a_Split[0]); + if (cmd == m_Commands.end()) + { + LOGWARNING("Command handler is registered in cPluginManager but not in cPlugin, wtf? Command \"%s\".", a_Split[0].c_str()); + return false; + } + + cCSLock Lock(m_CriticalSection); + bool res = false; + m_LuaState.Call(cmd->second, a_Split, a_Player, cLuaState::Return, res); + return res; +} + + + + + +bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) +{ + ASSERT(!a_Split.empty()); + CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); + if (cmd == m_ConsoleCommands.end()) + { + LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\", plugin \"%s\".", + a_Split[0].c_str(), GetName().c_str() + ); + return false; + } + + cCSLock Lock(m_CriticalSection); + bool res = false; + AString str; + m_LuaState.Call(cmd->second, a_Split, cLuaState::Return, res, str); + if (res && !str.empty()) + { + a_Output.Out(str); + } + return res; +} + + + + + +void cPluginLua::ClearCommands(void) +{ + cCSLock Lock(m_CriticalSection); + + // Unreference the bound functions so that Lua can GC them + if (m_LuaState != NULL) + { + for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) + { + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); + } + } + m_Commands.clear(); +} + + + + + +void cPluginLua::ClearConsoleCommands(void) +{ + cCSLock Lock(m_CriticalSection); + + // Unreference the bound functions so that Lua can GC them + if (m_LuaState != NULL) + { + for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr) + { + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); + } + } + m_ConsoleCommands.clear(); +} + + + + + +bool cPluginLua::CanAddOldStyleHook(int a_HookType) +{ + const char * FnName = GetHookFnName(a_HookType); + if (FnName == NULL) + { + // Unknown hook ID + LOGWARNING("Plugin %s wants to add an unknown hook ID (%d). The plugin need not work properly.", + GetName().c_str(), a_HookType + ); + m_LuaState.LogStackTrace(); + return false; + } + + // Check if the function is available + if (m_LuaState.HasFunction(FnName)) + { + return true; + } + + LOGWARNING("Plugin %s wants to add a hook (%d), but it doesn't provide the callback function \"%s\" for it. The plugin need not work properly.", + GetName().c_str(), a_HookType, FnName + ); + m_LuaState.LogStackTrace(); + return false; +} + + + + + +const char * cPluginLua::GetHookFnName(int a_HookType) +{ + switch (a_HookType) + { + case cPluginManager::HOOK_BLOCK_TO_PICKUPS: return "OnBlockToPickups"; + case cPluginManager::HOOK_CHAT: return "OnChat"; + case cPluginManager::HOOK_CHUNK_AVAILABLE: return "OnChunkAvailable"; + case cPluginManager::HOOK_CHUNK_GENERATED: return "OnChunkGenerated"; + case cPluginManager::HOOK_CHUNK_GENERATING: return "OnChunkGenerating"; + case cPluginManager::HOOK_CHUNK_UNLOADED: return "OnChunkUnloaded"; + case cPluginManager::HOOK_CHUNK_UNLOADING: return "OnChunkUnloading"; + case cPluginManager::HOOK_COLLECTING_PICKUP: return "OnCollectingPickup"; + case cPluginManager::HOOK_CRAFTING_NO_RECIPE: return "OnCraftingNoRecipe"; + case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; + case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; + case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; + case cPluginManager::HOOK_KILLING: return "OnKilling"; + case cPluginManager::HOOK_LOGIN: return "OnLogin"; + case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; + case cPluginManager::HOOK_PLAYER_BREAKING_BLOCK: return "OnPlayerBreakingBlock"; + case cPluginManager::HOOK_PLAYER_BROKEN_BLOCK: return "OnPlayerBrokenBlock"; + case cPluginManager::HOOK_PLAYER_EATING: return "OnPlayerEating"; + case cPluginManager::HOOK_PLAYER_JOINED: return "OnPlayerJoined"; + case cPluginManager::HOOK_PLAYER_LEFT_CLICK: return "OnPlayerLeftClick"; + case cPluginManager::HOOK_PLAYER_MOVING: return "OnPlayerMoving"; + case cPluginManager::HOOK_PLAYER_PLACED_BLOCK: return "OnPlayerPlacedBlock"; + case cPluginManager::HOOK_PLAYER_PLACING_BLOCK: return "OnPlayerPlacingBlock"; + case cPluginManager::HOOK_PLAYER_RIGHT_CLICK: return "OnPlayerRightClick"; + case cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY: return "OnPlayerRightClickingEntity"; + case cPluginManager::HOOK_PLAYER_SHOOTING: return "OnPlayerShooting"; + case cPluginManager::HOOK_PLAYER_SPAWNED: return "OnPlayerSpawned"; + case cPluginManager::HOOK_PLAYER_TOSSING_ITEM: return "OnPlayerTossingItem"; + case cPluginManager::HOOK_PLAYER_USED_BLOCK: return "OnPlayerUsedBlock"; + case cPluginManager::HOOK_PLAYER_USED_ITEM: return "OnPlayerUsedItem"; + case cPluginManager::HOOK_PLAYER_USING_BLOCK: return "OnPlayerUsingBlock"; + case cPluginManager::HOOK_PLAYER_USING_ITEM: return "OnPlayerUsingItem"; + case cPluginManager::HOOK_POST_CRAFTING: return "OnPostCrafting"; + case cPluginManager::HOOK_PRE_CRAFTING: return "OnPreCrafting"; + case cPluginManager::HOOK_SPAWNED_ENTITY: return "OnSpawnedEntity"; + case cPluginManager::HOOK_SPAWNED_MONSTER: return "OnSpawnedMonster"; + case cPluginManager::HOOK_SPAWNING_ENTITY: return "OnSpawningEntity"; + case cPluginManager::HOOK_SPAWNING_MONSTER: return "OnSpawningMonster"; + case cPluginManager::HOOK_TAKE_DAMAGE: return "OnTakeDamage"; + case cPluginManager::HOOK_TICK: return "OnTick"; + case cPluginManager::HOOK_UPDATED_SIGN: return "OnUpdatedSign"; + case cPluginManager::HOOK_UPDATING_SIGN: return "OnUpdatingSign"; + case cPluginManager::HOOK_WEATHER_CHANGED: return "OnWeatherChanged"; + case cPluginManager::HOOK_WEATHER_CHANGING: return "OnWeatherChanging"; + case cPluginManager::HOOK_WORLD_TICK: return "OnWorldTick"; + default: return NULL; + } // switch (a_Hook) +} + + + + + +bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx) +{ + ASSERT(m_CriticalSection.IsLockedByCurrentThread()); // It probably has to be, how else would we have a LuaState? + + // Check if the function reference is valid: + cLuaState::cRef * Ref = new cLuaState::cRef(m_LuaState, a_FnRefIdx); + if ((Ref == NULL) || !Ref->IsValid()) + { + LOGWARNING("Plugin %s tried to add a hook %d with bad handler function.", GetName().c_str(), a_HookType); + m_LuaState.LogStackTrace(); + delete Ref; + return false; + } + + m_HookMap[a_HookType].push_back(Ref); + return true; +} + + + + + +AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request ) +{ + cCSLock Lock(m_CriticalSection); + std::string RetVal = ""; + + std::pair< std::string, std::string > TabName = GetTabNameForRequest(a_Request); + std::string SafeTabName = TabName.second; + if (SafeTabName.empty()) + { + return ""; + } + + sWebPluginTab * Tab = 0; + for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr) + { + if ((*itr)->SafeTitle.compare(SafeTabName) == 0) // This is the one! Rawr + { + Tab = *itr; + break; + } + } + + if (Tab != NULL) + { + AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->Title.c_str()); + if (!m_LuaState.Call(Tab->UserData, a_Request, cLuaState::Return, Contents)) + { + return "Lua encountered error while processing the page request"; + } + + RetVal += Contents; + } + + return RetVal; +} + + + + + +bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference) +{ + cCSLock Lock(m_CriticalSection); + if (a_LuaState != m_LuaState) + { + LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!"); + return false; + } + sWebPluginTab * Tab = new sWebPluginTab(); + Tab->Title = a_Title; + Tab->SafeTitle = SafeString(a_Title); + + Tab->UserData = a_FunctionReference; + + GetTabs().push_back(Tab); + return true; +} + + + + + +void cPluginLua::BindCommand(const AString & a_Command, int a_FnRef) +{ + ASSERT(m_Commands.find(a_Command) == m_Commands.end()); + m_Commands[a_Command] = a_FnRef; +} + + + + + +void cPluginLua::BindConsoleCommand(const AString & a_Command, int a_FnRef) +{ + ASSERT(m_ConsoleCommands.find(a_Command) == m_ConsoleCommands.end()); + m_ConsoleCommands[a_Command] = a_FnRef; +} + + + + + +void cPluginLua::Unreference(int a_LuaRef) +{ + cCSLock Lock(m_CriticalSection); + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, a_LuaRef); +} + + + + + +bool cPluginLua::CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse) +{ + ASSERT(a_FnRef != LUA_REFNIL); + + cCSLock Lock(m_CriticalSection); + bool res; + m_LuaState.Call(a_FnRef, &a_Window, &a_Player, a_CanRefuse, cLuaState::Return, res); + return res; +} + + + + + +void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum) +{ + ASSERT(a_FnRef != LUA_REFNIL); + + cCSLock Lock(m_CriticalSection); + m_LuaState.Call(a_FnRef, &a_Window, a_SlotNum); +} + + + + diff --git a/src/PluginLua.h b/src/PluginLua.h new file mode 100644 index 000000000..908466966 --- /dev/null +++ b/src/PluginLua.h @@ -0,0 +1,202 @@ + +// PluginLua.h + +// Declares the cPluginLua class representing a plugin written in Lua + + + + + +#pragma once + +#include "Plugin.h" +#include "WebPlugin.h" +#include "LuaState.h" + +// Names for the global variables through which the plugin is identified in its LuaState +#define LUA_PLUGIN_NAME_VAR_NAME "_MCServerInternal_PluginName" +#define LUA_PLUGIN_INSTANCE_VAR_NAME "_MCServerInternal_PluginInstance" + + + + +// fwd: UI/Window.h +class cWindow; + + + + + +// tolua_begin +class cPluginLua : + public cPlugin, + public cWebPlugin +{ +public: + // tolua_end + + cPluginLua( const AString & a_PluginDirectory ); + ~cPluginLua(); + + virtual void OnDisable(void) override; + virtual bool Initialize(void) override; + + virtual void Tick(float a_Dt) override; + + virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) override; + virtual bool OnChat (cPlayer * a_Player, AString & a_Message) override; + virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; + virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override; + virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override; + virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; + virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; + virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override; + virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; + virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override; + virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; + virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; + virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) override; + virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) override; + virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) override; + virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) override; + virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; + virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) override; + virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerEating (cPlayer & a_Player) override; + virtual bool OnPlayerJoined (cPlayer & a_Player) override; + virtual bool OnPlayerMoved (cPlayer & a_Player) override; + virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override; + virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override; + virtual bool OnPlayerShooting (cPlayer & a_Player) override; + virtual bool OnPlayerSpawned (cPlayer & a_Player) override; + virtual bool OnPlayerTossingItem (cPlayer & a_Player) override; + virtual bool OnPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) override; + virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) override; + virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) override; + virtual bool OnSpawningMonster (cWorld & a_World, cMonster & a_Monster) override; + virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) override; + virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnWeatherChanged (cWorld & a_World) override; + virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) override; + virtual bool OnWorldTick (cWorld & a_World, float a_Dt) override; + + virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) override; + + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override; + + virtual void ClearCommands(void) override; + + virtual void ClearConsoleCommands(void) override; + + /// Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) + bool CanAddOldStyleHook(int a_HookType); + + // cWebPlugin override + virtual const AString GetWebTitle(void) const {return GetName(); } + + // cWebPlugin and WebAdmin stuff + virtual AString HandleWebRequest(const HTTPRequest * a_Request ) override; + bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS << + + /// Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. + void BindCommand(const AString & a_Command, int a_FnRef); + + /// Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap. + void BindConsoleCommand(const AString & a_Command, int a_FnRef); + + cLuaState & GetLuaState(void) { return m_LuaState; } + + cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; } + + /// Removes a previously referenced object (luaL_unref()) + void Unreference(int a_LuaRef); + + /// Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true + bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse); + + /// Calls the plugin-specified "cLuaWindow slot changed" callback. + void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum); + + /// Returns the name of Lua function that should handle the specified hook type in the older (#121) API + static const char * GetHookFnName(int a_HookType); + + /** Adds a Lua function to be called for the specified hook. + The function has to be on the Lua stack at the specified index a_FnRefIdx + Returns true if the hook was added successfully. + */ + bool AddHookRef(int a_HookType, int a_FnRefIdx); + + // The following templates allow calls to arbitrary Lua functions residing in the plugin: + + /// Call a Lua function with 0 args + template bool Call(FnT a_Fn) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn); + } + + /// Call a Lua function with 1 arg + template bool Call(FnT a_Fn, ArgT0 a_Arg0) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0); + } + + /// Call a Lua function with 2 args + template bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1); + } + + /// Call a Lua function with 3 args + template bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2); + } + + /// Call a Lua function with 4 args + template bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2, a_Arg3); + } + +protected: + /// Maps command name into Lua function reference + typedef std::map CommandMap; + + /// Provides an array of Lua function references + typedef std::vector cLuaRefs; + + /// Maps hook types into arrays of Lua function references to call for each hook type + typedef std::map cHookMap; + + cCriticalSection m_CriticalSection; + cLuaState m_LuaState; + + CommandMap m_Commands; + CommandMap m_ConsoleCommands; + + cHookMap m_HookMap; + + /// Releases all Lua references and closes the LuaState + void Close(void); +} ; // tolua_export + + + + diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp new file mode 100644 index 000000000..c1f695163 --- /dev/null +++ b/src/PluginManager.cpp @@ -0,0 +1,1664 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "PluginManager.h" +#include "Plugin.h" +#include "PluginLua.h" +#include "WebAdmin.h" +#include "Item.h" +#include "Root.h" +#include "Server.h" +#include "CommandOutput.h" + +#include "../iniFile/iniFile.h" +#include "tolua++.h" +#include "Entities/Player.h" + + + + + +cPluginManager * cPluginManager::Get(void) +{ + return cRoot::Get()->GetPluginManager(); +} + + + + + +cPluginManager::cPluginManager(void) : + m_bReloadPlugins(false) +{ +} + + + + + +cPluginManager::~cPluginManager() +{ + UnloadPluginsNow(); +} + + + + + +void cPluginManager::ReloadPlugins(void) +{ + m_bReloadPlugins = true; +} + + + + + +void cPluginManager::FindPlugins(void) +{ + AString PluginsPath = FILE_IO_PREFIX + AString( "Plugins/" ); + + // First get a clean list of only the currently running plugins, we don't want to mess those up + for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();) + { + if (itr->second == NULL) + { + PluginMap::iterator thiz = itr; + ++thiz; + m_Plugins.erase( itr ); + itr = thiz; + continue; + } + ++itr; + } + + AStringList Files = GetDirectoryContents(PluginsPath.c_str()); + for (AStringList::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) + { + if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr))) + { + // We only want folders, and don't want "." or ".." + continue; + } + + // Add plugin name/directory to the list + if (m_Plugins.find(*itr) == m_Plugins.end()) + { + m_Plugins[*itr] = NULL; + } + } +} + + + + + +void cPluginManager::ReloadPluginsNow(void) +{ + cIniFile a_SettingsIni; + a_SettingsIni.ReadFile("settings.ini"); + ReloadPluginsNow(a_SettingsIni); +} + + + + + +void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) +{ + LOG("-- Loading Plugins --"); + m_bReloadPlugins = false; + UnloadPluginsNow(); + + FindPlugins(); + + cServer::BindBuiltInConsoleCommands(); + + unsigned int KeyNum = a_SettingsIni.FindKey("Plugins"); + unsigned int NumPlugins = ((KeyNum != -1) ? (a_SettingsIni.GetNumValues(KeyNum)) : 0); + if (KeyNum == -1) + { + InsertDefaultPlugins(a_SettingsIni); + } + else if (NumPlugins > 0) + { + for(unsigned int i = 0; i < NumPlugins; i++) + { + AString ValueName = a_SettingsIni.GetValueName(KeyNum, i); + if (ValueName.compare("Plugin") == 0) + { + AString PluginFile = a_SettingsIni.GetValue(KeyNum, i); + if (!PluginFile.empty()) + { + if (m_Plugins.find(PluginFile) != m_Plugins.end()) + { + LoadPlugin( PluginFile ); + } + } + } + } + } + + if (GetNumPlugins() == 0) + { + LOG("-- No Plugins Loaded --"); + } + else if (GetNumPlugins() > 1) + { + LOG("-- Loaded %i Plugins --", GetNumPlugins()); + } + else + { + LOG("-- Loaded 1 Plugin --"); + } +} + + + + + +void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni) +{ + a_SettingsIni.AddKeyName("Plugins"); + a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers"); + a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify"); + a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx"); + a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump"); + a_SettingsIni.SetValue("Plugins", "Plugin", "Core"); + a_SettingsIni.SetValue("Plugins", "Plugin", "TransAPI"); + a_SettingsIni.SetValue("Plugins", "Plugin", "ChatLog"); +} + + + + + +void cPluginManager::Tick(float a_Dt) +{ + while (!m_DisablePluginList.empty()) + { + RemovePlugin(m_DisablePluginList.front()); + m_DisablePluginList.pop_front(); + } + + if (m_bReloadPlugins) + { + ReloadPluginsNow(); + } + + HookMap::iterator Plugins = m_Hooks.find(HOOK_TICK); + if (Plugins != m_Hooks.end()) + { + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + (*itr)->Tick(a_Dt); + } + } +} + + + + + +bool cPluginManager::CallHookBlockToPickups( + cWorld * a_World, cEntity * a_Digger, + int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + cItems & a_Pickups +) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_TO_PICKUPS); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_Pickups)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChat(cPlayer * a_Player, AString & a_Message) +{ + if (ExecuteCommand(a_Player, a_Message)) + { + return true; + } + + // Check if it was a standard command (starts with a slash) + if (!a_Message.empty() && (a_Message[0] == '/')) + { + AStringVector Split(StringSplit(a_Message, " ")); + ASSERT(!Split.empty()); // This should not happen - we know there's at least one char in the message so the split needs to be at least one item long + a_Player->SendMessage(Printf("Unknown Command: \"%s\"", Split[0].c_str())); + LOGINFO("Player \"%s\" issued an unknown command: \"%s\"", a_Player->GetName().c_str(), a_Message.c_str()); + return true; // Cancel sending + } + + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHAT); + if (Plugins == m_Hooks.end()) + { + return false; + } + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChat(a_Player, a_Message)) + { + return true; + } + } + + return false; +} + + + + + +bool cPluginManager::CallHookChunkAvailable(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_AVAILABLE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkAvailable(a_World, a_ChunkX, a_ChunkZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkGenerating(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChunkUnloaded(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_UNLOADED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkUnloaded(a_World, a_ChunkX, a_ChunkZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChunkUnloading(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_UNLOADING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkUnloading(a_World, a_ChunkX, a_ChunkZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookCollectingPickup(cPlayer * a_Player, cPickup & a_Pickup) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_COLLECTING_PICKUP); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnCollectingPickup(a_Player, &a_Pickup)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CRAFTING_NO_RECIPE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnCraftingNoRecipe(a_Player, a_Grid, a_Recipe)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Reason) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnDisconnect(a_Player, a_Reason)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_EXECUTE_COMMAND); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnExecuteCommand(a_Player, a_Split)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_EXPLODED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnExploded(a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_EXPLODING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnExploding(a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookHandshake(cClientHandle * a_ClientHandle, const AString & a_Username) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_HANDSHAKE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnHandshake(a_ClientHandle, a_Username)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_HOPPER_PULLING_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnHopperPullingItem(a_World, a_Hopper, a_DstSlotNum, a_SrcEntity, a_SrcSlotNum)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_HOPPER_PUSHING_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnHopperPushingItem(a_World, a_Hopper, a_SrcSlotNum, a_DstEntity, a_DstSlotNum)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookKilling(cEntity & a_Victim, cEntity * a_Killer) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_KILLING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnKilling(a_Victim, a_Killer)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_LOGIN); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnLogin(a_Client, a_ProtocolVersion, a_Username)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerAnimation(cPlayer & a_Player, int a_Animation) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_ANIMATION); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerAnimation(a_Player, a_Animation)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_BREAKING_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerBreakingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_BROKEN_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerBrokenBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerEating(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_EATING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerEating(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerJoined(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_JOINED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerJoined(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_LEFT_CLICK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerLeftClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_MOVING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerMoved(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_PLACED_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_PLACING_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_RIGHT_CLICK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerRightClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_RIGHT_CLICKING_ENTITY); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerRightClickingEntity(a_Player, a_Entity)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerShooting(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SHOOTING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerShooting(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerSpawned(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SPAWNED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerSpawned(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerTossingItem(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_TOSSING_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerTossingItem(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USED_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerUsedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USED_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerUsedItem(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USING_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerUsingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USING_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerUsingItem(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPostCrafting(a_Player, a_Grid, a_Recipe)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PRE_CRAFTING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPreCrafting(a_Player, a_Grid, a_Recipe)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookSpawnedEntity(cWorld & a_World, cEntity & a_Entity) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNED_ENTITY); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnSpawnedEntity(a_World, a_Entity)) + { + return true; + } + } + return false; +} + + + + +bool cPluginManager::CallHookSpawnedMonster(cWorld & a_World, cMonster & a_Monster) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNED_MONSTER); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnSpawnedMonster(a_World, a_Monster)) + { + return true; + } + } + return false; +} + + + + +bool cPluginManager::CallHookSpawningEntity(cWorld & a_World, cEntity & a_Entity) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNING_ENTITY); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnSpawningEntity(a_World, a_Entity)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookSpawningMonster(cWorld & a_World, cMonster & a_Monster) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNING_MONSTER); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnSpawningMonster(a_World, a_Monster)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_TAKE_DAMAGE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnTakeDamage(a_Receiver, a_TDI)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookUpdatingSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATING_SIGN); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnUpdatingSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookUpdatedSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATED_SIGN); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnUpdatedSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookWeatherChanged(cWorld & a_World) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnWeatherChanged(a_World)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnWeatherChanging(a_World, a_NewWeather)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookWorldTick(cWorld & a_World, float a_Dt) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_WORLD_TICK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnWorldTick(a_World, a_Dt)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions) +{ + ASSERT(a_Player != NULL); + + AStringVector Split(StringSplit(a_Command, " ")); + if (Split.empty()) + { + return false; + } + + CommandMap::iterator cmd = m_Commands.find(Split[0]); + if (cmd == m_Commands.end()) + { + // Command not found + return false; + } + + // Ask plugins first if a command is okay to execute the command: + if (CallHookExecuteCommand(a_Player, Split)) + { + LOGINFO("Player \"%s\" tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player->GetName().c_str(), Split[0].c_str()); + return false; + } + + if ( + a_ShouldCheckPermissions && + !cmd->second.m_Permission.empty() && + !a_Player->HasPermission(cmd->second.m_Permission) + ) + { + LOGINFO("Player \"%s\" tried to execute forbidden command \"%s\".", a_Player->GetName().c_str(), Split[0].c_str()); + return false; + } + + ASSERT(cmd->second.m_Plugin != NULL); + + return cmd->second.m_Plugin->HandleCommand(Split, a_Player); +} + + + + + +cPlugin * cPluginManager::GetPlugin( const AString & a_Plugin ) const +{ + for( PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr ) + { + if (itr->second == NULL ) continue; + if (itr->second->GetName().compare(a_Plugin) == 0) + { + return itr->second; + } + } + return 0; +} + + + + + +const cPluginManager::PluginMap & cPluginManager::GetAllPlugins() const +{ + return m_Plugins; +} + + + + + +void cPluginManager::UnloadPluginsNow() +{ + m_Hooks.clear(); + + while (!m_Plugins.empty()) + { + RemovePlugin(m_Plugins.begin()->second); + } + + m_Commands.clear(); + m_ConsoleCommands.clear(); +} + + + + + +bool cPluginManager::DisablePlugin(const AString & a_PluginName) +{ + PluginMap::iterator itr = m_Plugins.find(a_PluginName); + if (itr == m_Plugins.end()) + { + return false; + } + + if (itr->first.compare(a_PluginName) == 0) // _X 2013_02_01: wtf? Isn't this supposed to be what find() does? + { + m_DisablePluginList.push_back(itr->second); + itr->second = NULL; // Get rid of this thing right away + return true; + } + return false; +} + + + + + +bool cPluginManager::LoadPlugin(const AString & a_PluginName) +{ + return AddPlugin(new cPluginLua(a_PluginName.c_str())); +} + + + + + +void cPluginManager::RemoveHooks(cPlugin * a_Plugin) +{ + for (HookMap::iterator itr = m_Hooks.begin(), end = m_Hooks.end(); itr != end; ++itr) + { + itr->second.remove(a_Plugin); + } +} + + + + + +void cPluginManager::RemovePlugin(cPlugin * a_Plugin) +{ + for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) + { + if (itr->second == a_Plugin) + { + m_Plugins.erase(itr); + break; + } + } + + RemovePluginCommands(a_Plugin); + RemovePluginConsoleCommands(a_Plugin); + RemoveHooks(a_Plugin); + if (a_Plugin != NULL) + { + a_Plugin->OnDisable(); + } + delete a_Plugin; +} + + + + + +void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin) +{ + if (a_Plugin != NULL) + { + a_Plugin->ClearCommands(); + } + + for (CommandMap::iterator itr = m_Commands.begin(); itr != m_Commands.end();) + { + if (itr->second.m_Plugin == a_Plugin) + { + CommandMap::iterator EraseMe = itr; // Stupid GCC doesn't have a std::map::erase() that would return the next iterator + ++itr; + m_Commands.erase(EraseMe); + } + else + { + ++itr; + } + } // for itr - m_Commands[] +} + + + + + +bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) +{ + CommandMap::iterator cmd = m_Commands.find(a_Command); + if (cmd != m_Commands.end()) + { + LOGWARNING("Command \"%s\" is already bound to plugin \"%s\".", a_Command.c_str(), cmd->second.m_Plugin->GetName().c_str()); + return false; + } + + m_Commands[a_Command].m_Plugin = a_Plugin; + m_Commands[a_Command].m_Permission = a_Permission; + m_Commands[a_Command].m_HelpString = a_HelpString; + return true; +} + + + + + +bool cPluginManager::ForEachCommand(cCommandEnumCallback & a_Callback) +{ + for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) + { + if (a_Callback.Command(itr->first, itr->second.m_Plugin, itr->second.m_Permission, itr->second.m_HelpString)) + { + return false; + } + } // for itr - m_Commands[] + return true; +} + + + + + +bool cPluginManager::IsCommandBound(const AString & a_Command) +{ + return (m_Commands.find(a_Command) != m_Commands.end()); +} + + + + + +AString cPluginManager::GetCommandPermission(const AString & a_Command) +{ + CommandMap::iterator cmd = m_Commands.find(a_Command); + return (cmd == m_Commands.end()) ? "" : cmd->second.m_Permission; +} + + + + + +bool cPluginManager::ExecuteCommand(cPlayer * a_Player, const AString & a_Command) +{ + return HandleCommand(a_Player, a_Command, true); +} + + + + + +bool cPluginManager::ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command) +{ + return HandleCommand(a_Player, a_Command, false); +} + + + + + +void cPluginManager::RemovePluginConsoleCommands(cPlugin * a_Plugin) +{ + if (a_Plugin != NULL) + { + a_Plugin->ClearConsoleCommands(); + } + + for (CommandMap::iterator itr = m_ConsoleCommands.begin(); itr != m_ConsoleCommands.end();) + { + if (itr->second.m_Plugin == a_Plugin) + { + CommandMap::iterator EraseMe = itr; // Stupid GCC doesn't have a std::map::erase() that would return the next iterator + ++itr; + m_ConsoleCommands.erase(EraseMe); + } + else + { + ++itr; + } + } // for itr - m_Commands[] +} + + + + + +bool cPluginManager::BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString) +{ + CommandMap::iterator cmd = m_ConsoleCommands.find(a_Command); + if (cmd != m_ConsoleCommands.end()) + { + if (cmd->second.m_Plugin == NULL) + { + LOGWARNING("Console command \"%s\" is already bound internally by MCServer, cannot bind in plugin \"%s\".", a_Command.c_str(), a_Plugin->GetName().c_str()); + } + else + { + LOGWARNING("Console command \"%s\" is already bound to plugin \"%s\", cannot bind in plugin \"%s\".", a_Command.c_str(), cmd->second.m_Plugin->GetName().c_str(), a_Plugin->GetName().c_str()); + } + return false; + } + + m_ConsoleCommands[a_Command].m_Plugin = a_Plugin; + m_ConsoleCommands[a_Command].m_Permission = ""; + m_ConsoleCommands[a_Command].m_HelpString = a_HelpString; + return true; +} + + + + + +bool cPluginManager::ForEachConsoleCommand(cCommandEnumCallback & a_Callback) +{ + for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr) + { + if (a_Callback.Command(itr->first, itr->second.m_Plugin, "", itr->second.m_HelpString)) + { + return false; + } + } // for itr - m_Commands[] + return true; +} + + + + + +bool cPluginManager::IsConsoleCommandBound(const AString & a_Command) +{ + return (m_ConsoleCommands.find(a_Command) != m_ConsoleCommands.end()); +} + + + + + +bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) +{ + if (a_Split.empty()) + { + return false; + } + + CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); + if (cmd == m_ConsoleCommands.end()) + { + // Command not found + return false; + } + + if (cmd->second.m_Plugin == NULL) + { + // This is a built-in command + return false; + } + + // Ask plugins first if a command is okay to execute the console command: + if (CallHookExecuteCommand(NULL, a_Split)) + { + a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str()); + return false; + } + + return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output); +} + + + + + +void cPluginManager::TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player) +{ + for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) + { + if (NoCaseCompare(itr->first.substr(0, a_Text.length()), a_Text) != 0) + { + // Command name doesn't match + continue; + } + if ((a_Player != NULL) && !a_Player->HasPermission(itr->second.m_Permission)) + { + // Player doesn't have permission for the command + continue; + } + a_Results.push_back(itr->first); + } +} + + + + + +bool cPluginManager::IsValidHookType(int a_HookType) +{ + return ((a_HookType >= 0) && (a_HookType <= HOOK_MAX)); +} + + + + + +bool cPluginManager::AddPlugin(cPlugin * a_Plugin) +{ + m_Plugins[a_Plugin->GetDirectory()] = a_Plugin; + if (a_Plugin->Initialize()) + { + // Initialization OK + return true; + } + + // Initialization failed + RemovePlugin(a_Plugin); // Also undoes any registrations that Initialize() might have made + return false; +} + + + + + +void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook) +{ + if (!a_Plugin) + { + LOGWARN("Called cPluginManager::AddHook() with a_Plugin == NULL"); + return; + } + PluginList & Plugins = m_Hooks[a_Hook]; + Plugins.remove(a_Plugin); + Plugins.push_back(a_Plugin); +} + + + + + +unsigned int cPluginManager::GetNumPlugins() const +{ + return m_Plugins.size(); +} + + + + diff --git a/src/PluginManager.h b/src/PluginManager.h new file mode 100644 index 000000000..4140bffb5 --- /dev/null +++ b/src/PluginManager.h @@ -0,0 +1,295 @@ + +#pragma once + +#include "Item.h" + + + + + +class cPlugin; + +// fwd: World.h +class cWorld; + +// fwd: ChunkDesc.h +class cChunkDesc; + +// fwd: Entities/Entity.h +class cEntity; + +// fwd: Mobs/Monster.h +class cMonster; + +// fwd: Player.h +class cPlayer; + +// fwd: CraftingRecipes.h +class cCraftingGrid; +class cCraftingRecipe; + +// fwd: Pickup.h +class cPickup; + +// fwd: Pawn.h +struct TakeDamageInfo; +class cPawn; + +// fwd: CommandOutput.h +class cCommandOutputCallback; + +// fwd: BlockEntities/HopperEntity.h +class cHopperEntity; + +// fwd: BlockEntities/BlockEntityWithItems.h +class cBlockEntityWithItems; + + + + + +class cPluginManager // tolua_export +{ // tolua_export +public: // tolua_export + + // Called each tick + virtual void Tick(float a_Dt); + + // tolua_begin + enum PluginHook + { + HOOK_BLOCK_TO_PICKUPS, + HOOK_CHAT, + HOOK_CHUNK_AVAILABLE, + HOOK_CHUNK_GENERATED, + HOOK_CHUNK_GENERATING, + HOOK_CHUNK_UNLOADED, + HOOK_CHUNK_UNLOADING, + HOOK_COLLECTING_PICKUP, + HOOK_CRAFTING_NO_RECIPE, + HOOK_DISCONNECT, + HOOK_EXECUTE_COMMAND, + HOOK_EXPLODED, + HOOK_EXPLODING, + HOOK_HANDSHAKE, + HOOK_HOPPER_PULLING_ITEM, + HOOK_HOPPER_PUSHING_ITEM, + HOOK_KILLING, + HOOK_LOGIN, + HOOK_PLAYER_ANIMATION, + HOOK_PLAYER_BREAKING_BLOCK, + HOOK_PLAYER_BROKEN_BLOCK, + HOOK_PLAYER_EATING, + HOOK_PLAYER_JOINED, + HOOK_PLAYER_LEFT_CLICK, + HOOK_PLAYER_MOVING, + HOOK_PLAYER_PLACED_BLOCK, + HOOK_PLAYER_PLACING_BLOCK, + HOOK_PLAYER_RIGHT_CLICK, + HOOK_PLAYER_RIGHT_CLICKING_ENTITY, + HOOK_PLAYER_SHOOTING, + HOOK_PLAYER_SPAWNED, + HOOK_PLAYER_TOSSING_ITEM, + HOOK_PLAYER_USED_BLOCK, + HOOK_PLAYER_USED_ITEM, + HOOK_PLAYER_USING_BLOCK, + HOOK_PLAYER_USING_ITEM, + HOOK_POST_CRAFTING, + HOOK_PRE_CRAFTING, + HOOK_SPAWNED_ENTITY, + HOOK_SPAWNED_MONSTER, + HOOK_SPAWNING_ENTITY, + HOOK_SPAWNING_MONSTER, + HOOK_TAKE_DAMAGE, + HOOK_TICK, + HOOK_UPDATED_SIGN, + HOOK_UPDATING_SIGN, + HOOK_WEATHER_CHANGED, + HOOK_WEATHER_CHANGING, + HOOK_WORLD_TICK, + + // Note that if a hook type is added, it may need processing in cPlugin::CanAddHook() descendants, + // and it definitely needs adding in cPluginLua::GetHookFnName() ! + + // Keep these two as the last items, they are used for validity checking and get their values automagically + HOOK_NUM_HOOKS, + HOOK_MAX = HOOK_NUM_HOOKS - 1, + } ; + // tolua_end + + /// Used as a callback for enumerating bound commands + class cCommandEnumCallback + { + public: + /** Called for each command; return true to abort enumeration + For console commands, a_Permission is not used (set to empty string) + */ + virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0; + } ; + + /// Returns the instance of the Plugin Manager (there is only ever one) + static cPluginManager * Get(void); // tolua_export + + typedef std::map< AString, cPlugin * > PluginMap; + typedef std::list< cPlugin * > PluginList; + cPlugin * GetPlugin( const AString & a_Plugin ) const; // tolua_export + const PluginMap & GetAllPlugins() const; // >> EXPORTED IN MANUALBINDINGS << + + void FindPlugins(); // tolua_export + void ReloadPlugins(); // tolua_export + + /// Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add + void AddHook(cPlugin * a_Plugin, int a_HookType); + + unsigned int GetNumPlugins() const; // tolua_export + + // Calls for individual hooks. Each returns false if the action is to continue or true if the plugin wants to abort + bool CallHookBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups); + bool CallHookChat (cPlayer * a_Player, AString & a_Message); + bool CallHookChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ); + bool CallHookChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc); + bool CallHookChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc); + bool CallHookChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ); + bool CallHookChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ); + bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup); + bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason); + bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd + bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); + bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); + bool CallHookHandshake (cClientHandle * a_ClientHandle, const AString & a_Username); + bool CallHookHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum); + bool CallHookHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum); + bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer); + bool CallHookLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username); + bool CallHookPlayerAnimation (cPlayer & a_Player, int a_Animation); + bool CallHookPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerEating (cPlayer & a_Player); + bool CallHookPlayerJoined (cPlayer & a_Player); + bool CallHookPlayerMoving (cPlayer & a_Player); + bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); + bool CallHookPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity); + bool CallHookPlayerShooting (cPlayer & a_Player); + bool CallHookPlayerSpawned (cPlayer & a_Player); + bool CallHookPlayerTossingItem (cPlayer & a_Player); + bool CallHookPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + bool CallHookPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity); + bool CallHookSpawnedMonster (cWorld & a_World, cMonster & a_Monster); + bool CallHookSpawningEntity (cWorld & a_World, cEntity & a_Entity); + bool CallHookSpawningMonster (cWorld & a_World, cMonster & a_Monster); + bool CallHookTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TDI); + bool CallHookUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player); + bool CallHookUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player); + bool CallHookWeatherChanged (cWorld & a_World); + bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather); + bool CallHookWorldTick (cWorld & a_World, float a_Dt); + + bool DisablePlugin(const AString & a_PluginName); // tolua_export + bool LoadPlugin (const AString & a_PluginName); // tolua_export + + /// Removes all hooks the specified plugin has registered + void RemoveHooks(cPlugin * a_Plugin); + + /// Removes the plugin from the internal structures and deletes its object. + void RemovePlugin(cPlugin * a_Plugin); + + /// Removes all command bindings that the specified plugin has made + void RemovePluginCommands(cPlugin * a_Plugin); + + /// Binds a command to the specified plugin. Returns true if successful, false if command already bound. + bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param + + /// Calls a_Callback for each bound command, returns true if all commands were enumerated + bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Returns true if the command is in the command map + bool IsCommandBound(const AString & a_Command); // tolua_export + + /// Returns the permission needed for the specified command; empty string if command not found + AString GetCommandPermission(const AString & a_Command); // tolua_export + + /// Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed. + bool ExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export + + /// Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found) + bool ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export + + /// Removes all console command bindings that the specified plugin has made + void RemovePluginConsoleCommands(cPlugin * a_Plugin); + + /// Binds a console command to the specified plugin. Returns true if successful, false if command already bound. + bool BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param + + /// Calls a_Callback for each bound console command, returns true if all commands were enumerated + bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Returns true if the console command is in the command map + bool IsConsoleCommandBound(const AString & a_Command); // tolua_export + + /// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback + bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output); + + /** Appends all commands beginning with a_Text (case-insensitive) into a_Results. + If a_Player is not NULL, only commands for which the player has permissions are added. + */ + void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player); + + /// Returns true if the specified hook type is within the allowed range + static bool IsValidHookType(int a_HookType); + +private: + friend class cRoot; + + class cCommandReg + { + public: + cPlugin * m_Plugin; + AString m_Permission; // Not used for console commands + AString m_HelpString; + } ; + + typedef std::map HookMap; + typedef std::map CommandMap; + + PluginList m_DisablePluginList; + PluginMap m_Plugins; + HookMap m_Hooks; + CommandMap m_Commands; + CommandMap m_ConsoleCommands; + + bool m_bReloadPlugins; + + cPluginManager(); + ~cPluginManager(); + + /// Reloads all plugins, defaulting to settings.ini for settings location + void ReloadPluginsNow(void); + + /// Reloads all plugins with a cIniFile object expected to be initialised to settings.ini + void ReloadPluginsNow(cIniFile & a_SettingsIni); + + /// Unloads all plugins + void UnloadPluginsNow(void); + + /// Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini + void InsertDefaultPlugins(cIniFile & a_SettingsIni); + + /// Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. + bool AddPlugin(cPlugin * a_Plugin); + + /// Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled. + bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions); +} ; // tolua_export + + + + diff --git a/src/ProbabDistrib.cpp b/src/ProbabDistrib.cpp new file mode 100644 index 000000000..5fa17c276 --- /dev/null +++ b/src/ProbabDistrib.cpp @@ -0,0 +1,142 @@ + +// ProbabDistrib.cpp + +// Implements the cProbabDistrib class representing a discrete probability distribution curve and random generator + +#include "Globals.h" +#include "ProbabDistrib.h" +#include "MersenneTwister.h" + + + + + + +cProbabDistrib::cProbabDistrib(int a_MaxValue) : + m_MaxValue(a_MaxValue), + m_Sum(-1) +{ +} + + + + + + +void cProbabDistrib::SetPoints(const cProbabDistrib::cPoints & a_Points) +{ + ASSERT(!a_Points.empty()); + m_Sum = 0; + m_Cumulative.clear(); + m_Cumulative.reserve(a_Points.size() + 1); + int ProbSum = 0; + int LastProb = 0; + int LastValue = -1; + if (a_Points[0].m_Value != 0) + { + m_Cumulative.push_back(cPoint(0, 0)); // Always push in the [0, 0] point for easier search algorithm bounds + LastValue = 0; + } + for (cPoints::const_iterator itr = a_Points.begin(), end = a_Points.end(); itr != end; ++itr) + { + if (itr->m_Value == LastValue) + { + continue; + } + + // Add the current trapezoid to the sum: + ProbSum += (LastProb + itr->m_Probability) * (itr->m_Value - LastValue) / 2; + LastProb = itr->m_Probability; + LastValue = itr->m_Value; + m_Cumulative.push_back(cPoint(itr->m_Value, ProbSum)); + } // for itr - a_Points[] + if (LastValue != m_MaxValue) + { + m_Cumulative.push_back(cPoint(m_MaxValue, 0)); // Always push in the last point for easier search algorithm bounds + } + m_Sum = ProbSum; +} + + + + + +bool cProbabDistrib::SetDefString(const AString & a_DefString) +{ + AStringVector Points = StringSplitAndTrim(a_DefString, ";"); + if (Points.empty()) + { + return false; + } + cPoints Pts; + for (AStringVector::const_iterator itr = Points.begin(), end = Points.end(); itr != end; ++itr) + { + AStringVector Split = StringSplitAndTrim(*itr, ","); + if (Split.size() != 2) + { + // Bad format + return false; + } + int Value = atoi(Split[0].c_str()); + int Prob = atoi(Split[1].c_str()); + if ( + ((Value == 0) && (Split[0] != "0")) || + ((Prob == 0) && (Split[1] != "0")) + ) + { + // Number parse error + return false; + } + Pts.push_back(cPoint(Value, Prob)); + } // for itr - Points[] + + SetPoints(Pts); + return true; +} + + + + + +int cProbabDistrib::Random(MTRand & a_Rand) const +{ + int v = a_Rand.randInt(m_Sum); + return MapValue(v); +} + + + + + +int cProbabDistrib::MapValue(int a_OrigValue) const +{ + ASSERT(a_OrigValue >= 0); + ASSERT(a_OrigValue < m_Sum); + + // Binary search through m_Cumulative for placement: + size_t Lo = 0; + size_t Hi = m_Cumulative.size() - 1; + while (Hi - Lo > 1) + { + int Mid = (Lo + Hi) / 2; + int MidProbab = m_Cumulative[Mid].m_Probability; + if (MidProbab < a_OrigValue) + { + Lo = Mid; + } + else + { + Hi = Mid; + } + } + ASSERT(Hi - Lo == 1); + + // Linearly interpolate between Lo and Hi: + int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability; + int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value; + return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif; +} + + + + diff --git a/src/ProbabDistrib.h b/src/ProbabDistrib.h new file mode 100644 index 000000000..ddaadd9b7 --- /dev/null +++ b/src/ProbabDistrib.h @@ -0,0 +1,74 @@ + +// ProbabDistrib.h + +// Declares the cProbabDistrib class representing a discrete probability distribution curve and random generator + +/* +Usage: +1, Create a cProbabDistrib instance +2, Initialize the distribution either programmatically, using the SetPoints() function, or using a definition string +3, Ask for random numbers in that probability distribution using the Random() function +*/ + + + + + +#pragma once + + + + + +// fwd: +class MTRand; + + + + + +class cProbabDistrib +{ +public: + class cPoint + { + public: + int m_Value; + int m_Probability; + + cPoint(int a_Value, int a_Probability) : + m_Value(a_Value), + m_Probability(a_Probability) + { + } + } ; + + typedef std::vector cPoints; + + + cProbabDistrib(int a_MaxValue); + + /// Sets the distribution curve using an array of [value, probability] points, linearly interpolated. a_Points must not be empty. + void SetPoints(const cPoints & a_Points); + + /// Sets the distribution curve using a definition string; returns true on successful parse + bool SetDefString(const AString & a_DefString); + + /// Gets a random value from a_Rand, shapes it into the distribution curve and returns the value. + int Random(MTRand & a_Rand) const; + + /// Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability + int MapValue(int a_OrigValue) const; + + int GetSum(void) const { return m_Sum; } + +protected: + + int m_MaxValue; + cPoints m_Cumulative; ///< Cumulative probability of the values, sorted, for fast bsearch lookup + int m_Sum; ///< Sum of all the probabilities across all values in the domain; -1 if not set +} ; + + + + diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp new file mode 100644 index 000000000..2a9230fee --- /dev/null +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -0,0 +1,176 @@ + +// ChunkDataSerializer.cpp + +// Implements the cChunkDataSerializer class representing the object that can: +// - serialize chunk data to different protocol versions +// - cache such serialized data for multiple clients + +#include "Globals.h" +#include "ChunkDataSerializer.h" +#include "zlib.h" + + + + +cChunkDataSerializer::cChunkDataSerializer( + const cChunkDef::BlockTypes & a_BlockTypes, + const cChunkDef::BlockNibbles & a_BlockMetas, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_BlockSkyLight, + const unsigned char * a_BiomeData +) : + m_BlockTypes(a_BlockTypes), + m_BlockMetas(a_BlockMetas), + m_BlockLight(a_BlockLight), + m_BlockSkyLight(a_BlockSkyLight), + m_BiomeData(a_BiomeData) +{ +} + + + + +const AString & cChunkDataSerializer::Serialize(int a_Version) +{ + Serializations::const_iterator itr = m_Serializations.find(a_Version); + if (itr != m_Serializations.end()) + { + return itr->second; + } + + AString data; + switch (a_Version) + { + case RELEASE_1_2_5: Serialize29(data); break; + case RELEASE_1_3_2: Serialize39(data); break; + // TODO: Other protocol versions may serialize the data differently; implement here + + default: + { + LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version); + ASSERT(!"Unknown chunk data serialization version"); + break; + } + } + m_Serializations[a_Version] = data; + return m_Serializations[a_Version]; +} + + + + + +void cChunkDataSerializer::Serialize29(AString & a_Data) +{ + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + + const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); + const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); + const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); + const int DataSize = BiomeOffset + BiomeDataSize; + + // Temporary buffer for the composed data: + char AllData [DataSize]; + + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); + memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); + memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); + memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); + + // Compress the data: + // In order not to use allocation, use a fixed-size buffer, with the size + // that uses the same calculation as compressBound(): + const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; + char CompressedBlockData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(DataSize); + + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + + compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); + + // Now put all those data into a_Data: + + // "Ground-up continuous", or rather, "biome data present" flag: + a_Data.push_back('\x01'); + + // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively + // Also, no endian flipping is needed because of the const values + unsigned short BitMap1 = 0xffff; + unsigned short BitMap2 = 0; + a_Data.append((const char *)&BitMap1, sizeof(short)); + a_Data.append((const char *)&BitMap2, sizeof(short)); + + Int32 CompressedSizeBE = htonl(CompressedSize); + a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); + + Int32 UnusedInt32 = 0; + a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32)); + + a_Data.append(CompressedBlockData, CompressedSize); +} + + + + + +void cChunkDataSerializer::Serialize39(AString & a_Data) +{ + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + + const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); + const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); + const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); + const int DataSize = BiomeOffset + BiomeDataSize; + + // Temporary buffer for the composed data: + char AllData [DataSize]; + + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); + memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); + memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); + memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); + + // Compress the data: + // In order not to use allocation, use a fixed-size buffer, with the size + // that uses the same calculation as compressBound(): + const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; + char CompressedBlockData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(DataSize); + + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + + compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); + + // Now put all those data into a_Data: + + // "Ground-up continuous", or rather, "biome data present" flag: + a_Data.push_back('\x01'); + + // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively + // Also, no endian flipping is needed because of the const values + unsigned short BitMap1 = 0xffff; + unsigned short BitMap2 = 0; + a_Data.append((const char *)&BitMap1, sizeof(short)); + a_Data.append((const char *)&BitMap2, sizeof(short)); + + Int32 CompressedSizeBE = htonl(CompressedSize); + a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); + + // Unlike 29, 39 doesn't have the "unused" int + + a_Data.append(CompressedBlockData, CompressedSize); +} + + + + diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h new file mode 100644 index 000000000..a42856356 --- /dev/null +++ b/src/Protocol/ChunkDataSerializer.h @@ -0,0 +1,48 @@ + +// ChunkDataSerializer.h + +// Interfaces to the cChunkDataSerializer class representing the object that can: +// - serialize chunk data to different protocol versions +// - cache such serialized data for multiple clients + + + + + +class cChunkDataSerializer +{ +protected: + const cChunkDef::BlockTypes & m_BlockTypes; + const cChunkDef::BlockNibbles & m_BlockMetas; + const cChunkDef::BlockNibbles & m_BlockLight; + const cChunkDef::BlockNibbles & m_BlockSkyLight; + const unsigned char * m_BiomeData; + + typedef std::map Serializations; + + Serializations m_Serializations; + + void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5 + void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2 + +public: + enum + { + RELEASE_1_2_5 = 29, + RELEASE_1_3_2 = 39, + } ; + + cChunkDataSerializer( + const cChunkDef::BlockTypes & a_BlockTypes, + const cChunkDef::BlockNibbles & a_BlockMetas, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_BlockSkyLight, + const unsigned char * a_BiomeData + ); + + const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[] +} ; + + + + diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h new file mode 100644 index 000000000..5023ea227 --- /dev/null +++ b/src/Protocol/Protocol.h @@ -0,0 +1,215 @@ + +// Protocol.h + +// Interfaces to the cProtocol class representing the generic interface that a protocol +// parser and serializer must implement + + + + + +#pragma once + +#include "../Defines.h" +#include "../Endianness.h" + + + + +class cPlayer; +class cEntity; +class cWindow; +class cInventory; +class cPawn; +class cPickup; +class cMonster; +class cChunkDataSerializer; +class cWorld; +class cFallingBlock; + + + + + +typedef unsigned char Byte; + + + + + +class cProtocol +{ +public: + cProtocol(cClientHandle * a_Client) : + m_Client(a_Client) + { + } + virtual ~cProtocol() {} + + /// Called when client sends some data + virtual void DataReceived(const char * a_Data, int a_Size) = 0; + + // Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0; + virtual void SendChat (const AString & a_Message) = 0; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0; + virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; + virtual void SendDisconnect (const AString & a_Reason) = 0; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0; + virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0; + virtual void SendEntityLook (const cEntity & a_Entity) = 0; + virtual void SendEntityMetadata (const cEntity & a_Entity) = 0; + virtual void SendEntityProperties (const cEntity & a_Entity) = 0; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0; + virtual void SendEntityVelocity (const cEntity & a_Entity) = 0; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0; + virtual void SendGameMode (eGameMode a_GameMode) = 0; + virtual void SendHealth (void) = 0; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0; + virtual void SendKeepAlive (int a_PingID) = 0; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0; + virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; + virtual void SendPlayerAbilities (void) = 0; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0; + virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+) + virtual void SendPlayerMoveLook (void) = 0; + virtual void SendPlayerPosition (void) = 0; + virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; + virtual void SendRespawn (void) = 0; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; + virtual void SendSpawnMob (const cMonster & a_Mob) = 0; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) = 0; + virtual void SendTabCompletionResults(const AStringVector & a_Results) = 0; + virtual void SendTeleportEntity (const cEntity & a_Entity) = 0; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0; + virtual void SendWeather (eWeather a_Weather) = 0; + virtual void SendWholeInventory (const cWindow & a_Window) = 0; + virtual void SendWindowClose (const cWindow & a_Window) = 0; + virtual void SendWindowOpen (const cWindow & a_Window) = 0; + virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) = 0; + + /// Returns the ServerID used for authentication through session.minecraft.net + virtual AString GetAuthServerID(void) = 0; + +protected: + cClientHandle * m_Client; + cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once + + /// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it + virtual void SendData(const char * a_Data, int a_Size) = 0; + + /// Called after writing each packet, enables descendants to flush their buffers + virtual void Flush(void) {}; + + // Helpers for writing partial packet data, write using SendData() + void WriteByte(Byte a_Value) + { + SendData((const char *)&a_Value, 1); + } + + void WriteShort(short a_Value) + { + a_Value = htons(a_Value); + SendData((const char *)&a_Value, 2); + } + + /* + void WriteShort(unsigned short a_Value) + { + a_Value = htons(a_Value); + SendData((const char *)&a_Value, 2); + } + */ + + void WriteInt(int a_Value) + { + a_Value = htonl(a_Value); + SendData((const char *)&a_Value, 4); + } + + void WriteUInt(unsigned int a_Value) + { + a_Value = htonl(a_Value); + SendData((const char *)&a_Value, 4); + } + + void WriteInt64 (Int64 a_Value) + { + a_Value = HostToNetwork8(&a_Value); + SendData((const char *)&a_Value, 8); + } + + void WriteFloat (float a_Value) + { + unsigned int val = HostToNetwork4(&a_Value); + SendData((const char *)&val, 4); + } + + void WriteDouble(double a_Value) + { + unsigned long long val = HostToNetwork8(&a_Value); + SendData((const char *)&val, 8); + } + + void WriteString(const AString & a_Value) + { + AString UTF16; + UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16); + WriteShort((unsigned short)(UTF16.size() / 2)); + SendData(UTF16.data(), UTF16.size()); + } + + void WriteBool(bool a_Value) + { + WriteByte(a_Value ? 1 : 0); + } + + void WriteVectorI(const Vector3i & a_Vector) + { + WriteInt(a_Vector.x); + WriteInt(a_Vector.y); + WriteInt(a_Vector.z); + } + + void WriteVarInt(UInt32 a_Value) + { + // A 32-bit integer can be encoded by at most 5 bytes: + unsigned char b[5]; + int idx = 0; + do + { + b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00); + a_Value = a_Value >> 7; + idx++; + } while (a_Value > 0); + + SendData((const char *)b, idx); + } + + void WriteVarUTF8String(const AString & a_String) + { + WriteVarInt(a_String.size()); + SendData(a_String.data(), a_String.size()); + } +} ; + + + + + diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp new file mode 100644 index 000000000..9f2770815 --- /dev/null +++ b/src/Protocol/Protocol125.cpp @@ -0,0 +1,1869 @@ + +// Protocol125.cpp + +// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29) +/* +Documentation: + - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513 + - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262 + - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152 +*/ + +#include "Globals.h" + +#include "Protocol125.h" + +#include "../ClientHandle.h" +#include "../World.h" +#include "ChunkDataSerializer.h" +#include "../Entities/Entity.h" +#include "../Mobs/Monster.h" +#include "../Entities/Pickup.h" +#include "../Entities/Player.h" +#include "../ChatColor.h" +#include "../UI/Window.h" +#include "../Root.h" +#include "../Server.h" + +#include "../Entities/ProjectileEntity.h" +#include "../Entities/Minecart.h" +#include "../Entities/FallingBlock.h" + +#include "../Mobs/IncludeAllMonsters.h" + + + + + +enum +{ + PACKET_KEEP_ALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_HANDSHAKE = 0x02, + PACKET_CHAT = 0x03, + PACKET_UPDATE_TIME = 0x04, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_USE_ENTITY = 0x07, + PACKET_UPDATE_HEALTH = 0x08, + PACKET_RESPAWN = 0x09, + PACKET_PLAYER_ON_GROUND = 0x0a, + PACKET_PLAYER_POS = 0x0b, + PACKET_PLAYER_LOOK = 0x0c, + PACKET_PLAYER_MOVE_LOOK = 0x0d, + PACKET_BLOCK_DIG = 0x0e, + PACKET_BLOCK_PLACE = 0x0f, + PACKET_SLOT_SELECTED = 0x10, + PACKET_USE_BED = 0x11, + PACKET_ANIMATION = 0x12, + PACKET_PACKET_ENTITY_ACTION = 0x13, + PACKET_PLAYER_SPAWN = 0x14, + PACKET_PICKUP_SPAWN = 0x15, + PACKET_COLLECT_PICKUP = 0x16, + PACKET_SPAWN_OBJECT = 0x17, + PACKET_SPAWN_MOB = 0x18, + PACKET_ENTITY_VELOCITY = 0x1c, + PACKET_DESTROY_ENTITY = 0x1d, + PACKET_ENTITY = 0x1e, + PACKET_ENT_REL_MOVE = 0x1f, + PACKET_ENT_LOOK = 0x20, + PACKET_ENT_REL_MOVE_LOOK = 0x21, + PACKET_ENT_TELEPORT = 0x22, + PACKET_ENT_HEAD_LOOK = 0x23, + PACKET_ENT_STATUS = 0x26, + PACKET_ATTACH_ENTITY = 0x27, + PACKET_METADATA = 0x28, + PACKET_PRE_CHUNK = 0x32, + PACKET_MAP_CHUNK = 0x33, + PACKET_MULTI_BLOCK = 0x34, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_EXPLOSION = 0x3C, + PACKET_SOUND_EFFECT = 0x3e, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d, + PACKET_CHANGE_GAME_STATE = 0x46, + PACKET_THUNDERBOLT = 0x47, + PACKET_WINDOW_OPEN = 0x64, + PACKET_WINDOW_CLOSE = 0x65, + PACKET_WINDOW_CLICK = 0x66, + PACKET_INVENTORY_SLOT = 0x67, + PACKET_INVENTORY_WHOLE = 0x68, + PACKET_WINDOW_PROPERTY = 0x69, + PACKET_CREATIVE_INVENTORY_ACTION = 0x6B, + PACKET_UPDATE_SIGN = 0x82, + PACKET_PLAYER_LIST_ITEM = 0xC9, + PACKET_PLAYER_ABILITIES = 0xca, + PACKET_PLUGIN_MESSAGE = 0xfa, + PACKET_PING = 0xfe, + PACKET_DISCONNECT = 0xff +} ; + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + +typedef unsigned char Byte; + + + + + +cProtocol125::cProtocol125(cClientHandle * a_Client) : + super(a_Client), + m_ReceivedData(32 KiB) +{ +} + + + + + +void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ATTACH_ENTITY); + WriteInt(a_Entity.GetUniqueID()); + WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + UNUSED(a_BlockType); + + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_ACTION); + WriteInt (a_BlockX); + WriteShort((short)a_BlockY); + WriteInt (a_BlockZ); + WriteByte (a_Byte1); + WriteByte (a_Byte2); + Flush(); +} + + + + + +void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + // Not supported in this protocol version +} + + + + + +void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_BLOCK_CHANGE); + WriteInt (a_BlockX); + WriteByte((unsigned char)a_BlockY); + WriteInt (a_BlockZ); + WriteByte(a_BlockType); + WriteByte(a_BlockMeta); + Flush(); +} + + + + + +void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + cCSLock Lock(m_CSPacket); + if (a_Changes.size() == 1) + { + // Special packet for single-block changes + const sSetBlock & blk = a_Changes.front(); + SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta); + return; + } + + WriteByte (PACKET_MULTI_BLOCK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteShort((unsigned short)a_Changes.size()); + WriteUInt (sizeof(int) * a_Changes.size()); + for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) + { + unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); + unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); + WriteUInt(Coords << 16 | Blocks); + } + Flush(); +} + + + + + +void cProtocol125::SendChat(const AString & a_Message) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_CHAT); + WriteString(a_Message); + Flush(); +} + + + + + +void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + cCSLock Lock(m_CSPacket); + + // Send the pre-chunk: + SendPreChunk(a_ChunkX, a_ChunkZ, true); + + // Send the chunk data: + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5); + WriteByte(PACKET_MAP_CHUNK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + SendData(Serialized.data(), Serialized.size()); + Flush(); +} + + + + + +void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COLLECT_PICKUP); + WriteInt (a_Pickup.GetUniqueID()); + WriteInt (a_Player.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendDestroyEntity(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_DESTROY_ENTITY); + WriteInt (a_Entity.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendDisconnect(const AString & a_Reason) +{ + cCSLock Lock(m_CSPacket); + WriteByte ((unsigned char)PACKET_DISCONNECT); + WriteString(a_Reason); + Flush(); +} + + + + + +void cProtocol125::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // This protocol version doesn't support this packet, sign editor is invoked by the client automatically + UNUSED(a_BlockX); + UNUSED(a_BlockY); + UNUSED(a_BlockZ); +} + + + + + +void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENTITY_EQUIPMENT); + WriteInt (a_Entity.GetUniqueID()); + WriteShort(a_SlotNum); + WriteShort(a_Item.m_ItemType); + WriteShort(a_Item.m_ItemDamage); + Flush(); +} + + + + + +void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_HEAD_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte((char)((a_Entity.GetHeadYaw() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityMetadata(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_METADATA); + WriteInt (a_Entity.GetUniqueID()); + + WriteCommonMetadata(a_Entity); + if (a_Entity.IsMob()) + { + WriteMobMetadata(((const cMonster &)a_Entity)); + } + else + { + WriteEntityMetadata(a_Entity); + } + WriteByte(0x7f); + + Flush(); +} + + + + + +void cProtocol125::SendEntityProperties(const cEntity & a_Entity) +{ + // Not supported in this protocol version +} + + + + + +void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_REL_MOVE); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_RelX); + WriteByte(a_RelY); + WriteByte(a_RelZ); + Flush(); +} + + + + + +void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_REL_MOVE_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_RelX); + WriteByte(a_RelY); + WriteByte(a_RelZ); + WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_STATUS); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_Status); + Flush(); +} + + + + + +void cProtocol125::SendEntityVelocity(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENTITY_VELOCITY); + WriteInt (a_Entity.GetUniqueID()); + WriteShort((short) (a_Entity.GetSpeedX() * 400)); //400 = 8000 / 20 + WriteShort((short) (a_Entity.GetSpeedY() * 400)); + WriteShort((short) (a_Entity.GetSpeedZ() * 400)); + Flush(); +} + + + + + +void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_EXPLOSION); + WriteDouble (a_BlockX); + WriteDouble (a_BlockY); + WriteDouble (a_BlockZ); + WriteFloat (a_Radius); + WriteInt (a_BlocksAffected.size()); + int BlockX = (int)a_BlockX; + int BlockY = (int)a_BlockY; + int BlockZ = (int)a_BlockZ; + for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr) + { + WriteByte((Byte)(itr->x - BlockX)); + WriteByte((Byte)(itr->y - BlockY)); + WriteByte((Byte)(itr->z - BlockZ)); + } + WriteFloat((float)a_PlayerMotion.x); + WriteFloat((float)a_PlayerMotion.y); + WriteFloat((float)a_PlayerMotion.z); + Flush(); +} + + + + + +void cProtocol125::SendGameMode(eGameMode a_GameMode) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(3); + WriteByte((char)a_GameMode); + Flush(); +} + + + + + +void cProtocol125::SendHandshake(const AString & a_ConnectionHash) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_HANDSHAKE); + WriteString(a_ConnectionHash); + Flush(); +} + + + + + +void cProtocol125::SendHealth(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_HEALTH); + WriteShort((short)m_Client->GetPlayer()->GetHealth()); + WriteShort(m_Client->GetPlayer()->GetFoodLevel()); + WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); + Flush(); +} + + + + + +void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_INVENTORY_SLOT); + WriteByte (a_WindowID); + WriteShort(a_SlotNum); + WriteItem (a_Item); + Flush(); +} + + + + + +void cProtocol125::SendKeepAlive(int a_PingID) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_KEEP_ALIVE); + WriteInt (a_PingID); + Flush(); +} + + + + + +void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + UNUSED(a_World); + cCSLock Lock(m_CSPacket); + + WriteByte (PACKET_LOGIN); + WriteInt (a_Player.GetUniqueID()); // EntityID of the player + WriteString(""); // Username, not used + WriteString("default"); // Level type + WriteInt ((int)a_Player.GetGameMode()); + WriteInt ((int)(a_World.GetDimension())); + WriteByte (2); // TODO: Difficulty + WriteByte (0); // Unused + WriteByte (60); // Client list width or something + Flush(); +} + + + + + +void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PICKUP_SPAWN); + WriteInt (a_Pickup.GetUniqueID()); + WriteShort (a_Pickup.GetItem().m_ItemType); + WriteByte (a_Pickup.GetItem().m_ItemCount); + WriteShort (a_Pickup.GetItem().m_ItemDamage); + WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); + WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); + Flush(); +} + + + + + +void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ANIMATION); + WriteInt (a_Player.GetUniqueID()); + WriteByte(a_Animation); + Flush(); +} + + + + + +void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + cCSLock Lock(m_CSPacket); + AString PlayerName(a_Player.GetColor()); + PlayerName.append(a_Player.GetName()); + if (PlayerName.length() > 14) + { + PlayerName.erase(14); + } + PlayerName += cChatColor::White; + + WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM); + WriteString(PlayerName); + WriteBool (a_IsOnline); + WriteShort (a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0); + Flush(); +} + + + + + +void cProtocol125::SendPlayerMaxSpeed(void) +{ + // Not supported by this protocol version +} + + + + + +void cProtocol125::SendPlayerMoveLook(void) +{ + cCSLock Lock(m_CSPacket); + + /* + LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", + m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 + ); + */ + + WriteByte (PACKET_PLAYER_MOVE_LOOK); + cPlayer * Player = m_Client->GetPlayer(); + WriteDouble(Player->GetPosX()); + WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block + WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block + WriteDouble(Player->GetPosZ()); + WriteFloat ((float)(Player->GetRotation())); + WriteFloat ((float)(Player->GetPitch())); + WriteBool (Player->IsOnGround()); + Flush(); +} + + + + + +void cProtocol125::SendPlayerPosition(void) +{ + cCSLock Lock(m_CSPacket); + LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now +} + + + + + +void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player) +{ + const cItem & HeldItem = a_Player.GetEquippedItem(); + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PLAYER_SPAWN); + WriteInt (a_Player.GetUniqueID()); + WriteString(a_Player.GetName()); + WriteInt ((int)(a_Player.GetPosX() * 32)); + WriteInt ((int)(a_Player.GetPosY() * 32)); + WriteInt ((int)(a_Player.GetPosZ() * 32)); + WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); + WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); + Flush(); +} + + + + + +void cProtocol125::SendRespawn(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_RESPAWN); + WriteInt ((int)(m_Client->GetPlayer()->GetWorld()->GetDimension())); + WriteByte (2); // TODO: Difficulty; 2 = Normal + WriteByte ((char)m_Client->GetPlayer()->GetGameMode()); + WriteShort (256); // Current world height + WriteString("default"); +} + + + + + +void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + // Not needed in this protocol version +} + + + + + +void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + // Not implemented in this protocol version +} + + + + + +void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + // This protocol version implements falling blocks using the spawn object / vehicle packet: + SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0); +} + + + + + +void cProtocol125::SendSpawnMob(const cMonster & a_Mob) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_MOB); + WriteInt (a_Mob.GetUniqueID()); + WriteByte (a_Mob.GetMobType()); + WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); + WriteByte (0); + WriteByte (0); + WriteByte (0); + + WriteCommonMetadata(a_Mob); + WriteMobMetadata(a_Mob); + WriteByte(0x7f); + + Flush(); +} + + + + + +void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + UNUSED(a_Yaw); + UNUSED(a_Pitch); + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_ObjectType); + WriteInt ((int)(a_Entity.GetPosX() * 32)); + WriteInt ((int)(a_Entity.GetPosY() * 32)); + WriteInt ((int)(a_Entity.GetPosZ() * 32)); + WriteByte(a_Pitch); + WriteByte(a_Yaw); + WriteInt (a_ObjectData); + if (a_ObjectData != 0) + { + WriteShort((short)(a_Entity.GetSpeedX() * 400)); + WriteShort((short)(a_Entity.GetSpeedY() * 400)); + WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + +void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_OBJECT); + WriteInt (a_Vehicle.GetUniqueID()); + WriteByte (a_VehicleType); + WriteInt ((int)(a_Vehicle.GetPosX() * 32)); + WriteInt ((int)(a_Vehicle.GetPosY() * 32)); + WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); + WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); + WriteInt (a_VehicleSubType); + if (a_VehicleSubType != 0) + { + WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + +void cProtocol125::SendTabCompletionResults(const AStringVector & a_Results) +{ + // This protocol version doesn't support tab completion + UNUSED(a_Results); +} + + + + + +void cProtocol125::SendTeleportEntity(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENT_TELEPORT); + WriteInt (a_Entity.GetUniqueID()); + WriteInt ((int)(floor(a_Entity.GetPosX() * 32))); + WriteInt ((int)(floor(a_Entity.GetPosY() * 32))); + WriteInt ((int)(floor(a_Entity.GetPosZ() * 32))); + WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_THUNDERBOLT); + WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one + WriteBool(true); // Unknown bool + WriteInt (a_BlockX * 32); + WriteInt (a_BlockY * 32); + WriteInt (a_BlockZ * 32); + Flush(); +} + + + + + +void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_TIME); + // Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day: + WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000)); + Flush(); +} + + + + + +void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSPacket); + SendPreChunk(a_ChunkX, a_ChunkZ, false); +} + + + + + +void cProtocol125::SendUpdateSign( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 +) +{ + cCSLock Lock(m_CSPacket); + WriteByte ((unsigned char)PACKET_UPDATE_SIGN); + WriteInt (a_BlockX); + WriteShort ((short)a_BlockY); + WriteInt (a_BlockZ); + WriteString(a_Line1); + WriteString(a_Line2); + WriteString(a_Line3); + WriteString(a_Line4); + Flush(); +} + + + + + +void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_USE_BED); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(0); // Unknown byte only 0 has been observed + WriteInt (a_BlockX); + WriteByte(a_BlockY); + WriteInt (a_BlockZ); + Flush(); +} + + + + + +void cProtocol125::SendWeather(eWeather a_Weather) +{ + cCSLock Lock(m_CSPacket); + switch( a_Weather ) + { + case eWeather_Sunny: + { + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(2); // Stop rain + WriteByte(0); // Unused + Flush(); + break; + } + + case eWeather_Rain: + case eWeather_ThunderStorm: + { + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(1); // Begin rain + WriteByte(0); // Unused + Flush(); + break; + } + } +} + + + + + +void cProtocol125::SendWholeInventory(const cWindow & a_Window) +{ + cCSLock Lock(m_CSPacket); + cItems Slots; + a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); + SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0])); +} + + + + + +void cProtocol125::SendWindowClose(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() == cWindow::wtInventory) + { + // Do not send inventory-window-close + return; + } + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_WINDOW_CLOSE); + WriteByte(a_Window.GetWindowID()); + Flush(); +} + + + + + +void cProtocol125::SendWindowOpen(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_Window.GetWindowID()); + WriteByte (a_Window.GetWindowType()); + WriteString(a_Window.GetWindowTitle()); + WriteByte (a_Window.GetNumNonInventorySlots()); + Flush(); +} + + + + + +void cProtocol125::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_PROPERTY); + WriteByte (a_Window.GetWindowID()); + WriteShort(a_Property); + WriteShort(a_Value); + Flush(); +} + + + + + +AString cProtocol125::GetAuthServerID(void) +{ + // http://wiki.vg/wiki/index.php?title=Session&oldid=2262 + // The server generates a random hash and that is used for all clients, unmodified + return cRoot::Get()->GetServer()->GetServerID(); +} + + + + + +void cProtocol125::SendData(const char * a_Data, int a_Size) +{ + m_Client->SendData(a_Data, a_Size); +} + + + + + +void cProtocol125::DataReceived(const char * a_Data, int a_Size) +{ + if (!m_ReceivedData.Write(a_Data, a_Size)) + { + // Too much data in the incoming queue, report to caller: + m_Client->PacketBufferFull(); + return; + } + + // Parse and handle all complete packets in m_ReceivedData: + while (m_ReceivedData.CanReadBytes(1)) + { + unsigned char PacketType; + m_ReceivedData.ReadByte(PacketType); + switch (ParsePacket(PacketType)) + { + case PARSE_UNKNOWN: + { + // An unknown packet has been received, notify the client and abort: + m_Client->PacketUnknown(PacketType); + return; + } + case PARSE_ERROR: + { + // An error occurred while parsing a known packet, notify the client and abort: + m_Client->PacketError(PacketType); + return; + } + case PARSE_INCOMPLETE: + { + // Incomplete packet, bail out and process with the next batch of data + m_ReceivedData.ResetRead(); + return; + } + default: + { + // Packet successfully parsed, commit the read data and try again one more packet + m_ReceivedData.CommitRead(); + break; + } + } + } +} + + + + + +int cProtocol125::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + default: return PARSE_UNKNOWN; + case PACKET_ANIMATION: return ParseArmAnim(); + case PACKET_BLOCK_DIG: return ParseBlockDig(); + case PACKET_BLOCK_PLACE: return ParseBlockPlace(); + case PACKET_CHAT: return ParseChat(); + case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction(); + case PACKET_DISCONNECT: return ParseDisconnect(); + case PACKET_HANDSHAKE: return ParseHandshake(); + case PACKET_KEEP_ALIVE: return ParseKeepAlive(); + case PACKET_LOGIN: return ParseLogin(); + case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction(); + case PACKET_PING: return ParsePing(); + case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities(); + case PACKET_PLAYER_LOOK: return ParsePlayerLook(); + case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook(); + case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround(); + case PACKET_PLAYER_POS: return ParsePlayerPosition(); + case PACKET_PLUGIN_MESSAGE: return ParsePluginMessage(); + case PACKET_RESPAWN: return ParseRespawn(); + case PACKET_SLOT_SELECTED: return ParseSlotSelected(); + case PACKET_UPDATE_SIGN: return ParseUpdateSign(); + case PACKET_USE_ENTITY: return ParseUseEntity(); + case PACKET_WINDOW_CLICK: return ParseWindowClick(); + case PACKET_WINDOW_CLOSE: return ParseWindowClose(); + } +} + + + + + +#define HANDLE_PACKET_PARSE(Packet) \ + { \ + int res = Packet.Parse(m_ReceivedData); \ + if (res < 0) \ + { \ + return res; \ + } \ + } + + + + + +int cProtocol125::ParseArmAnim(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, Animation); + m_Client->HandleAnimation(Animation); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseBlockDig(void) +{ + HANDLE_PACKET_READ(ReadChar, char, Status); + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, BlockFace); + m_Client->HandleLeftClick(PosX, PosY, PosZ, BlockFace, Status); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseBlockPlace(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, BlockFace); + + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + // 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable. + m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, 8, 8, 8, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseChat(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message); + m_Client->HandleChat(Message); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseCreativeInventoryAction(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + m_Client->HandleCreativeInventory(SlotNum, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseDisconnect(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason); + m_Client->HandleDisconnect(Reason); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseEntityAction(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, ActionID); + m_Client->HandleEntityAction(EntityID, ActionID); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseHandshake(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + + AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565" + if (UserData.empty()) + { + m_Client->Kick("Did not receive username"); + return PARSE_OK; + } + m_Username = UserData[0]; + + LOGD("HANDSHAKE %s", Username.c_str()); + + if (!m_Client->HandleHandshake( m_Username )) + { + return PARSE_OK; // Player is not allowed into the server + } + + SendHandshake(cRoot::Get()->GetServer()->GetServerID()); + LOGD("User \"%s\" was sent a handshake response", m_Username.c_str()); + + return PARSE_OK; +} + + + + + +int cProtocol125::ParseKeepAlive(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID); + m_Client->HandleKeepAlive(KeepAliveID); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseLogin(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + HANDLE_PACKET_READ(ReadBEInt, int, ServerMode); + HANDLE_PACKET_READ(ReadBEInt, int, Dimension); + HANDLE_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight); + HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers); + + if (ProtocolVersion < 29) + { + m_Client->Kick("Your client is outdated!"); + return PARSE_OK; + } + else if (ProtocolVersion > 29) + { + m_Client->Kick("Your client version is higher than the server!"); + return PARSE_OK; + } + + if (m_Username.compare(Username) != 0) + { + LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking", + Username.c_str(), + m_Username.c_str(), + m_Client->GetIPString().c_str() + ); + m_Client->Kick("Hacked client"); // Don't tell them why we don't want them + return PARSE_OK; + } + + m_Client->HandleLogin(ProtocolVersion, Username); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePing(void) +{ + // Packet has no more data + m_Client->HandlePing(); + return PARSE_OK; +} + + + + + + +int cProtocol125::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, Invulnerable); + HANDLE_PACKET_READ(ReadBool, bool, IsFlying); + HANDLE_PACKET_READ(ReadBool, bool, CanFly); + HANDLE_PACKET_READ(ReadBool, bool, InstaMine); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerLook(void) +{ + HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); + HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerMoveLook(void) +{ + HANDLE_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); + HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0); + m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerOnGround(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + // TODO: m_Client->HandleFlying(IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerPosition(void) +{ + HANDLE_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePluginMessage(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); + HANDLE_PACKET_READ(ReadBEShort, short, Length); + AString Data; + if (!m_ReceivedData.ReadString(Data, Length)) + { + m_ReceivedData.CheckValid(); + return PARSE_INCOMPLETE; + } + m_ReceivedData.CheckValid(); + + // TODO: Process the data + LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str()); + + return PARSE_OK; +} + + + + + +int cProtocol125::ParseRespawn(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, Dimension); + HANDLE_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_PACKET_READ(ReadChar, char, CreativeMode); + HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + m_Client->HandleRespawn(); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseSlotSelected(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + m_Client->HandleSlotSelected(SlotNum); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseUpdateSign(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4); + m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseUseEntity(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID); + HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID); + HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick); + m_Client->HandleUseEntity(TargetEntityID, IsLeftClick); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseWindowClick(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + HANDLE_PACKET_READ(ReadBool, bool, IsRightClick); + HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); + HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + // Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols: + eClickAction Action; + if (IsRightClick) + { + if (IsShiftPressed) + { + Action = caShiftRightClick; + } + else + { + if (SlotNum == -999) + { + Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside; + } + else + { + Action = caRightClick; + } + } + } + else + { + // IsLeftClick + if (IsShiftPressed) + { + Action = caShiftLeftClick; + } + else + { + if (SlotNum == -999) + { + Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside; + } + else + { + Action = caLeftClick; + } + } + } + m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseWindowClose(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + m_Client->HandleWindowClose(WindowID); + return PARSE_OK; +} + + + + + +void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad) +{ + WriteByte(PACKET_PRE_CHUNK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteBool(a_ShouldLoad); + Flush(); +} + + + + + +void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items) +{ + WriteByte (PACKET_INVENTORY_WHOLE); + WriteByte (a_WindowID); + WriteShort((short)a_NumItems); + + for (int j = 0; j < a_NumItems; j++) + { + WriteItem(a_Items[j]); + } + Flush(); +} + + + + + +void cProtocol125::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + WriteShort(ItemType); + if (a_Item.IsEmpty()) + { + return; + } + + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + if (cItem::IsEnchantable(a_Item.m_ItemType)) + { + // TODO: Implement enchantments + WriteShort(-1); + } +} + + + + + +int cProtocol125::ParseItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + + if (ItemType <= -1) + { + a_Item.Empty(); + return PARSE_OK; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + if (!cItem::IsEnchantable(ItemType)) + { + return PARSE_OK; + } + + HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes); + + if (EnchantNumBytes <= 0) + { + return PARSE_OK; + } + + // TODO: Enchantment not implemented yet! + if (!m_ReceivedData.SkipRead(EnchantNumBytes)) + { + return PARSE_INCOMPLETE; + } + + return PARSE_OK; +} + + + + + +void cProtocol125::WriteCommonMetadata(const cEntity & a_Entity) +{ + Byte CommonMetadata = 0; + + if (a_Entity.IsOnFire()) + { + CommonMetadata |= 0x1; + } + if (a_Entity.IsCrouched()) + { + CommonMetadata |= 0x2; + } + if (a_Entity.IsRiding()) + { + CommonMetadata |= 0x4; + } + if (a_Entity.IsSprinting()) + { + CommonMetadata |= 0x8; + } + if (a_Entity.IsRclking()) + { + CommonMetadata |= 0x10; + } + if (a_Entity.IsInvisible()) + { + CommonMetadata |= 0x20; + } + + WriteByte(0x0); + WriteByte(CommonMetadata); +} + + + + + +void cProtocol125::WriteEntityMetadata(const cEntity & a_Entity) +{ + if (a_Entity.IsMinecart()) + { + WriteByte(0x51); + // No idea how Mojang makes their carts shakey shakey, so here is a complicated one-liner expression that does something similar + WriteInt( (((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4 ); + WriteByte(0x52); + WriteInt(1); // Shaking direction, doesn't seem to affect anything + WriteByte(0x73); + WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer + + if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) + { + WriteByte(0x10); + WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); // Fueled? + } + } + else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow)) + { + WriteByte(0x10); + WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); // Critical hitting arrow? + } +} + + + + + +void cProtocol125::WriteMobMetadata(const cMonster & a_Mob) +{ + switch (a_Mob.GetMobType()) + { + case cMonster::mtCreeper: + { + WriteByte(0x10); + WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); // Blowing up? + WriteByte(0x11); + WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); // Lightning-charged? + break; + } + case cMonster::mtBat: + { + WriteByte(0x10); + WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); // Upside down? + break; + } + case cMonster::mtPig: + { + WriteByte(0x10); + WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); // Saddled? + break; + } + case cMonster::mtVillager: + { + WriteByte(0x50); + WriteInt(((const cVillager &)a_Mob).GetVilType()); // What sort of TESTIFICATE? + break; + } + case cMonster::mtZombie: + { + WriteByte(0xC); + WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); // Babby zombie? + WriteByte(0xD); + WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); // Converted zombie? + WriteByte(0xE); + WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); // Converted-but-converting-back zombllager? + break; + } + case cMonster::mtGhast: + { + WriteByte(0x10); + WriteByte(((const cGhast &)a_Mob).IsCharging()); // About to eject un flamé-bol? :P + break; + } + case cMonster::mtWolf: + { + Byte WolfStatus = 0; + if (((const cWolf &)a_Mob).IsSitting()) + { + WolfStatus |= 0x1; + } + if (((const cWolf &)a_Mob).IsAngry()) + { + WolfStatus |= 0x2; + } + if (((const cWolf &)a_Mob).IsTame()) + { + WolfStatus |= 0x4; + } + WriteByte(0x10); + WriteByte(WolfStatus); + + WriteByte(0x72); + WriteFloat((float)(a_Mob.GetHealth())); // Tail health-o-meter (only shown when tamed, by the way) + WriteByte(0x13); + WriteByte(((const cWolf &)a_Mob).IsBegging() ? 1 : 0); // Ultra cute mode? + break; + } + case cMonster::mtSheep: + { + // [1](1111) + // [] = Is sheared? () = Color, from 0 to 15 + + WriteByte(0x10); + Byte SheepMetadata = 0; + SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); // Fur colour + + if (((const cSheep &)a_Mob).IsSheared()) // Is sheared? + { + SheepMetadata |= 0x16; + } + WriteByte(SheepMetadata); + break; + } + case cMonster::mtEnderman: + { + WriteByte(0x10); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); // Block that he stole from your house + WriteByte(0x11); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); // Meta of block that he stole from your house + WriteByte(0x12); + WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); // Screaming at your face? + break; + } + case cMonster::mtSkeleton: + { + WriteByte(0xD); + WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); // It's a skeleton, but it's not + break; + } + case cMonster::mtWitch: + { + WriteByte(0x15); + WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); // Aggravated? Doesn't seem to do anything + break; + } + case cMonster::mtSlime: + case cMonster::mtMagmaCube: + { + WriteByte(0x10); + if (a_Mob.GetMobType() == cMonster::mtSlime) + { + WriteByte(((const cSlime &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME + } + else + { + WriteByte(((const cMagmaCube &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME + } + break; + } + case cMonster::mtHorse: + { + int Flags = 0; + if (((const cHorse &)a_Mob).IsTame()) + { + Flags |= 0x2; + } + if (((const cHorse &)a_Mob).IsSaddled()) + { + Flags |= 0x4; + } + if (((const cHorse &)a_Mob).IsChested()) + { + Flags |= 0x8; + } + if (((const cHorse &)a_Mob).IsBaby()) + { + Flags |= 0x10; // IsBred flag, according to wiki.vg - don't think it does anything in multiplayer + } + if (((const cHorse &)a_Mob).IsEating()) + { + Flags |= 0x20; + } + if (((const cHorse &)a_Mob).IsRearing()) + { + Flags |= 0x40; + } + if (((const cHorse &)a_Mob).IsMthOpen()) + { + Flags |= 0x80; + } + WriteByte(0x50); + WriteInt(Flags); + + WriteByte(0x13); + WriteByte(((const cHorse &)a_Mob).GetHorseType()); // Type of horse (donkey, chestnut, etc.) + + WriteByte(0x54); + int Appearance = 0; + Appearance = ((const cHorse &)a_Mob).GetHorseColor(); // Mask FF + Appearance |= ((const cHorse &)a_Mob).GetHorseStyle() * 256; // Mask FF00, so multiply by 256 + WriteInt(Appearance); + + WriteByte(0x56); + WriteInt(((const cHorse &)a_Mob).GetHorseArmour()); // Horshey armour + break; + } + } +} + + + + diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h new file mode 100644 index 000000000..db913bb57 --- /dev/null +++ b/src/Protocol/Protocol125.h @@ -0,0 +1,157 @@ + +// Protocol125.h + +// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29) + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" + + + + + +class cProtocol125 : + public cProtocol +{ + typedef cProtocol super; +public: + cProtocol125(cClientHandle * a_Client); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityHeadLook (const cEntity & a_Entity) override; + virtual void SendEntityLook (const cEntity & a_Entity) override; + virtual void SendEntityMetadata (const cEntity & a_Entity) override; + virtual void SendEntityProperties (const cEntity & a_Entity) override; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntityVelocity (const cEntity & a_Entity) override; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMaxSpeed (void) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; + virtual void SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (const cWindow & a_Window) override; + virtual void SendWindowOpen (const cWindow & a_Window) override; + virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + + virtual AString GetAuthServerID(void) override; + +protected: + /// Results of packet-parsing: + enum { + PARSE_OK = 1, + PARSE_ERROR = -1, + PARSE_UNKNOWN = -2, + PARSE_INCOMPLETE = -3, + } ; + + cByteBuffer m_ReceivedData; ///< Buffer for the received data + + AString m_Username; ///< Stored in ParseHandshake(), compared to Login username + + virtual void SendData(const char * a_Data, int a_Size) override; + + /// Sends the Handshake packet + void SendHandshake(const AString & a_ConnectionHash); + + /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() ) + virtual int ParsePacket(unsigned char a_PacketType); + + // Specific packet parsers: + virtual int ParseArmAnim (void); + virtual int ParseBlockDig (void); + virtual int ParseBlockPlace (void); + virtual int ParseChat (void); + virtual int ParseCreativeInventoryAction(void); + virtual int ParseDisconnect (void); + virtual int ParseEntityAction (void); + virtual int ParseHandshake (void); + virtual int ParseKeepAlive (void); + virtual int ParseLogin (void); + virtual int ParsePing (void); + virtual int ParsePlayerAbilities (void); + virtual int ParsePlayerLook (void); + virtual int ParsePlayerMoveLook (void); + virtual int ParsePlayerOnGround (void); + virtual int ParsePlayerPosition (void); + virtual int ParsePluginMessage (void); + virtual int ParseRespawn (void); + virtual int ParseSlotSelected (void); + virtual int ParseUpdateSign (void); + virtual int ParseUseEntity (void); + virtual int ParseWindowClick (void); + virtual int ParseWindowClose (void); + + // Utility functions: + /// Writes a "pre-chunk" packet + void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad); + + /// Writes a "set window items" packet with the specified params + void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items); + + /// Writes one item, "slot" as the protocol wiki calls it + virtual void WriteItem(const cItem & a_Item); + + /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes + virtual int ParseItem(cItem & a_Item); + + /// Writes the COMMON entity metadata + void WriteCommonMetadata(const cEntity & a_Entity); + + /// Writes normal entity metadata + void WriteEntityMetadata(const cEntity & a_Entity); + + /// Writes mobile entity metadata + void WriteMobMetadata(const cMonster & a_Mob); +} ; + + + + diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp new file mode 100644 index 000000000..22eac4312 --- /dev/null +++ b/src/Protocol/Protocol132.cpp @@ -0,0 +1,950 @@ + +// Protocol132.cpp + +// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39) + +#include "Globals.h" +#include "Protocol132.h" +#include "../Root.h" +#include "../Server.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "../../CryptoPP/randpool.h" +#include "../Item.h" +#include "ChunkDataSerializer.h" +#include "../Entities/Player.h" +#include "../Mobs/Monster.h" +#include "../UI/Window.h" +#include "../Entities/Pickup.h" +#include "../WorldStorage/FastNBT.h" +#include "../StringCompression.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + +typedef unsigned char Byte; + + + + + +using namespace CryptoPP; + + + + + +const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows... + + + + + +enum +{ + PACKET_KEEP_ALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_COMPASS = 0x06, + PACKET_PLAYER_SPAWN = 0x14, + PACKET_COLLECT_PICKUP = 0x16, + PACKET_SPAWN_MOB = 0x18, + PACKET_DESTROY_ENTITIES = 0x1d, + PACKET_CHUNK_DATA = 0x33, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_BLOCK_BREAK_ANIM = 0x37, + PACKET_SOUND_EFFECT = 0x3e, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d, + PACKET_TAB_COMPLETION = 0xcb, + PACKET_LOCALE_VIEW_DISTANCE = 0xcc, + PACKET_CLIENT_STATUSES = 0xcd, + PACKET_ENCRYPTION_KEY_RESP = 0xfc, +} ; + + + + + +// Converts a raw 160-bit SHA1 digest into a Java Hex representation +// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802 +static void DigestToJava(byte a_Digest[20], AString & a_Out) +{ + bool IsNegative = (a_Digest[0] >= 0x80); + if (IsNegative) + { + // Two's complement: + bool carry = true; // Add one to the whole number + for (int i = 19; i >= 0; i--) + { + a_Digest[i] = ~a_Digest[i]; + if (carry) + { + carry = (a_Digest[i] == 0xff); + a_Digest[i]++; + } + } + } + a_Out.clear(); + a_Out.reserve(40); + for (int i = 0; i < 20; i++) + { + AppendPrintf(a_Out, "%02x", a_Digest[i]); + } + while ((a_Out.length() > 0) && (a_Out[0] == '0')) + { + a_Out.erase(0, 1); + } + if (IsNegative) + { + a_Out.insert(0, "-"); + } +} + + + + + +/* +// Self-test the hash formatting for known values: +// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48 +// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1 +// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6 + +class Test +{ +public: + Test(void) + { + AString DigestNotch, DigestJeb, DigestSimon; + byte Digest[20]; + CryptoPP::SHA1 Checksum; + Checksum.Update((const byte *)"Notch", 5); + Checksum.Final(Digest); + DigestToJava(Digest, DigestNotch); + Checksum.Restart(); + Checksum.Update((const byte *)"jeb_", 4); + Checksum.Final(Digest); + DigestToJava(Digest, DigestJeb); + Checksum.Restart(); + Checksum.Update((const byte *)"simon", 5); + Checksum.Final(Digest); + DigestToJava(Digest, DigestSimon); + printf("Notch: \"%s\"", DigestNotch.c_str()); + printf("jeb_: \"%s\"", DigestJeb.c_str()); + printf("simon: \"%s\"", DigestSimon.c_str()); + } +} test; +*/ + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol132: + +cProtocol132::cProtocol132(cClientHandle * a_Client) : + super(a_Client), + m_IsEncrypted(false) +{ +} + + + + + +cProtocol132::~cProtocol132() +{ + if (!m_DataToSend.empty()) + { + LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size()); + } +} + + + + + +void cProtocol132::DataReceived(const char * a_Data, int a_Size) +{ + if (m_IsEncrypted) + { + byte Decrypted[512]; + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; + m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); + super::DataReceived((const char *)Decrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + super::DataReceived(a_Data, a_Size); + } +} + + + + + +void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_ACTION); + WriteInt (a_BlockX); + WriteShort((short)a_BlockY); + WriteInt (a_BlockZ); + WriteByte (a_Byte1); + WriteByte (a_Byte2); + WriteShort(a_BlockType); + Flush(); +} + + + + + +void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_BREAK_ANIM); + WriteInt (a_entityID); + WriteInt (a_BlockX); + WriteInt (a_BlockY); + WriteInt (a_BlockZ); + WriteByte (stage); + Flush(); +} + + + + + +void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_CHANGE); + WriteInt (a_BlockX); + WriteByte ((unsigned char)a_BlockY); + WriteInt (a_BlockZ); + WriteShort(a_BlockType); + WriteByte (a_BlockMeta); + Flush(); +} + + + + + +void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + cCSLock Lock(m_CSPacket); + + // Pre-chunk not used in 1.3.2. Finally. + + // Send the chunk data: + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); + WriteByte(PACKET_CHUNK_DATA); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + SendData(Serialized.data(), Serialized.size()); + Flush(); +} + + + + + +void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COLLECT_PICKUP); + WriteInt (a_Pickup.GetUniqueID()); + WriteInt (a_Player.GetUniqueID()); + Flush(); + + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + SendSoundEffect( + "random.pop", + (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8), + 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64) + ); +} + + + + + +void cProtocol132::SendDestroyEntity(const cEntity & a_Entity) +{ + if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID()) + { + // Do not send "destroy self" to the client, the client would crash (FS #254) + return; + } + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_DESTROY_ENTITIES); + WriteByte(1); // entity count + WriteInt (a_Entity.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENTITY_EQUIPMENT); + WriteInt (a_Entity.GetUniqueID()); + WriteShort(a_SlotNum); + WriteItem (a_Item); + Flush(); +} + + + + + +void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_LOGIN); + WriteInt (a_Player.GetUniqueID()); // EntityID of the player + WriteString("default"); // Level type + WriteByte ((int)a_Player.GetGameMode()); + WriteByte ((Byte)(a_World.GetDimension())); + WriteByte (2); // TODO: Difficulty + WriteByte (0); // Unused, used to be world height + WriteByte (8); // Client list width or something + Flush(); + + SendCompass(a_World); +} + + + + + +void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) +{ + const cItem & HeldItem = a_Player.GetEquippedItem(); + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PLAYER_SPAWN); + WriteInt (a_Player.GetUniqueID()); + WriteString(a_Player.GetName()); + WriteInt ((int)(a_Player.GetPosX() * 32)); + WriteInt ((int)(a_Player.GetPosY() * 32)); + WriteInt ((int)(a_Player.GetPosZ() * 32)); + WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); + WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); + // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata: + WriteByte (0); // Index 0, byte (flags) + WriteByte (0); // Flags, empty + WriteByte (0x7f); // End of metadata + Flush(); +} + + + + + +void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SOUND_EFFECT); + WriteString (a_SoundName); + WriteInt (a_SrcX); + WriteInt (a_SrcY); + WriteInt (a_SrcZ); + WriteFloat (a_Volume); + WriteByte ((char)(a_Pitch * 63.0f)); + Flush(); +} + + + + + +void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SOUND_PARTICLE_EFFECT); + WriteInt (a_EffectID); + WriteInt (a_SrcX); + WriteByte(a_SrcY); + WriteInt (a_SrcZ); + WriteInt (a_Data); + Flush(); +} + + + + + +void cProtocol132::SendSpawnMob(const cMonster & a_Mob) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_MOB); + WriteInt (a_Mob.GetUniqueID()); + WriteByte (a_Mob.GetMobType()); + WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); + WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256)); + WriteShort ((short)(a_Mob.GetSpeedX() * 400)); + WriteShort ((short)(a_Mob.GetSpeedY() * 400)); + WriteShort ((short)(a_Mob.GetSpeedZ() * 400)); + + WriteCommonMetadata(a_Mob); + WriteMobMetadata(a_Mob); + WriteByte(0x7f); + + Flush(); +} + + + + + +void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results) +{ + if (a_Results.empty()) + { + // No results to send + return; + } + + AString Serialized(a_Results[0]); + for (AStringVector::const_iterator itr = a_Results.begin() + 1, end = a_Results.end(); itr != end; ++itr) + { + Serialized.push_back(0); + Serialized.append(*itr); + } // for itr - a_Results[] + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_TAB_COMPLETION); + WriteString(Serialized); + Flush(); +} + + + + + +void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + // Unloading the chunk is done by sending a "map chunk" packet + // with IncludeInitialize set to true and primary bitmap set to 0: + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_CHUNK_DATA); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteBool(true); // IncludeInitialize + WriteShort(0); // Primary bitmap + WriteShort(0); // Add bitmap + WriteInt(0); + Flush(); +} + + + + + +void cProtocol132::SendWholeInventory(const cWindow & a_Window) +{ + // 1.3.2 requires player inventory slots to be sent as SetSlot packets, + // otherwise it sometimes fails to update the window + + // Send the entire window: + super::SendWholeInventory(a_Window); + + // Send the player inventory and hotbar: + const cInventory & Inventory = m_Client->GetPlayer()->GetInventory(); + int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots + char WindowID = a_Window.GetWindowID(); + for (int i = 0; i < cInventory::invInventoryCount; i++) + { + SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i)); + } // for i - Inventory[] + BaseOffset += cInventory::invInventoryCount; + for (int i = 0; i < cInventory::invHotbarCount; i++) + { + SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i)); + } // for i - Hotbar[] + + // Send even the item being dragged: + SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem()); +} + + + + + +AString cProtocol132::GetAuthServerID(void) +{ + // http://wiki.vg/wiki/index.php?title=Session&oldid=2615 + // Server uses SHA1 to mix ServerID, Client secret and server public key together + // The mixing is done in StartEncryption, the result is in m_AuthServerID + + return m_AuthServerID; +} + + + + + +int cProtocol132::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125 + case PACKET_CLIENT_STATUSES: return ParseClientStatuses(); + case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse(); + case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance(); + case PACKET_TAB_COMPLETION: return ParseTabCompletion(); + } +} + + + + + +int cProtocol132::ParseBlockPlace(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, BlockFace); + + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + HANDLE_PACKET_READ(ReadChar, char, CursorX); + HANDLE_PACKET_READ(ReadChar, char, CursorY); + HANDLE_PACKET_READ(ReadChar, char, CursorZ); + + m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseHandshake(void) +{ + HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost); + HANDLE_PACKET_READ(ReadBEInt, int, ServerPort); + m_Username = Username; + + if (!m_Client->HandleHandshake( m_Username )) + { + return PARSE_OK; // Player is not allowed into the server + } + + // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD + CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs + cRoot::Get()->GetServer()->GetPublicKey().Save(sink); + SendEncryptionKeyRequest(); + + return PARSE_OK; +} + + + + + +int cProtocol132::ParseClientStatuses(void) +{ + HANDLE_PACKET_READ(ReadByte, byte, Status); + if ((Status & 1) == 0) + { + m_Client->HandleLogin(39, m_Username); + } + else + { + m_Client->HandleRespawn(); + } + return PARSE_OK; +} + + + + + +int cProtocol132::ParseEncryptionKeyResponse(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength); + AString EncKey; + if (!m_ReceivedData.ReadString(EncKey, EncKeyLength)) + { + return PARSE_INCOMPLETE; + } + HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength); + AString EncNonce; + if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength)) + { + return PARSE_INCOMPLETE; + } + if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) + { + LOGD("Too long encryption"); + m_Client->Kick("Hacked client"); + return PARSE_OK; + } + + HandleEncryptionKeyResponse(EncKey, EncNonce); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseLocaleViewDistance(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); + HANDLE_PACKET_READ(ReadChar, char, ViewDistance); + HANDLE_PACKET_READ(ReadChar, char, ChatFlags); + HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); + // TODO: m_Client->HandleLocale(Locale); + // TODO: m_Client->HandleViewDistance(ViewDistance); + // TODO: m_Client->HandleChatFlags(ChatFlags); + // Ignoring client difficulty + return PARSE_OK; +} + + + + + +int cProtocol132::ParseLogin(void) +{ + // Login packet not used in 1.3.2 + return PARSE_ERROR; +} + + + + + +int cProtocol132::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, Flags); + HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed); + HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseTabCompletion(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Text); + m_Client->HandleTabCompletion(Text); + return PARSE_OK; +} + + + + + +void cProtocol132::SendData(const char * a_Data, int a_Size) +{ + m_DataToSend.append(a_Data, a_Size); +} + + + + + +void cProtocol132::Flush(void) +{ + ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly? + + if (m_DataToSend.empty()) + { + LOGD("Flushing empty"); + return; + } + const char * a_Data = m_DataToSend.data(); + int a_Size = m_DataToSend.size(); + if (m_IsEncrypted) + { + byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; + m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); + super::SendData((const char *)Encrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + super::SendData(a_Data, a_Size); + } + m_DataToSend.clear(); +} + + + + + +void cProtocol132::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + if (a_Item.IsEmpty()) + { + WriteShort(-1); + return; + } + + WriteShort(ItemType); + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + if (a_Item.m_Enchantments.IsEmpty()) + { + WriteShort(-1); + return; + } + + // Send the enchantments: + cFastNBTWriter Writer; + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); + Writer.Finish(); + AString Compressed; + CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); + WriteShort(Compressed.size()); + SendData(Compressed.data(), Compressed.size()); +} + + + + + +int cProtocol132::ParseItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + + if (ItemType <= -1) + { + a_Item.Empty(); + return PARSE_OK; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength); + if (MetadataLength <= 0) + { + return PARSE_OK; + } + + // Read the metadata + AString Metadata; + Metadata.resize(MetadataLength); + if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength)) + { + return PARSE_INCOMPLETE; + } + + return ParseItemMetadata(a_Item, Metadata); +} + + + + + +int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +{ + // Uncompress the GZIPped data: + AString Uncompressed; + if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) + { + AString HexDump; + CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); + LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str()); + return PARSE_ERROR; + } + + // Parse into NBT: + cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); + if (!NBT.IsValid()) + { + AString HexDump; + CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); + LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str()); + return PARSE_ERROR; + } + + // Load enchantments from the NBT: + for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) + { + if ( + (NBT.GetType(tag) == TAG_List) && + ( + (NBT.GetName(tag) == "ench") || + (NBT.GetName(tag) == "StoredEnchantments") + ) + ) + { + a_Item.m_Enchantments.ParseFromNBT(NBT, tag); + } + } + + return PARSE_OK; +} + + + + + +void cProtocol132::SendCompass(const cWorld & a_World) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COMPASS); + WriteInt((int)(a_World.GetSpawnX())); + WriteInt((int)(a_World.GetSpawnY())); + WriteInt((int)(a_World.GetSpawnZ())); + Flush(); +} + + + + + +void cProtocol132::SendEncryptionKeyRequest(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte((char)0xfd); + WriteString(cRoot::Get()->GetServer()->GetServerID()); + WriteShort((short)m_ServerPublicKey.size()); + SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size()); + WriteShort(4); + WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) + Flush(); +} + + + + + +void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce) +{ + // Decrypt EncNonce using privkey + RSAES::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey()); + time_t CurTime = time(NULL); + CryptoPP::RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + byte DecryptedNonce[MAX_ENC_LEN]; + DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce); + if (!res.isValidCoding || (res.messageLength != 4)) + { + LOGD("Bad nonce length"); + m_Client->Kick("Hacked client"); + return; + } + if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this) + { + LOGD("Bad nonce value"); + m_Client->Kick("Hacked client"); + return; + } + + // Decrypt the symmetric encryption key using privkey: + byte DecryptedKey[MAX_ENC_LEN]; + res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey); + if (!res.isValidCoding || (res.messageLength != 16)) + { + LOGD("Bad key length"); + m_Client->Kick("Hacked client"); + return; + } + + { + // Send encryption key response: + cCSLock Lock(m_CSPacket); + WriteByte((char)0xfc); + WriteShort(0); + WriteShort(0); + Flush(); + } + + StartEncryption(DecryptedKey); + return; +} + + + + + +void cProtocol132::StartEncryption(const byte * a_Key) +{ + m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); + m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); + m_IsEncrypted = true; + + // Prepare the m_AuthServerID: + CryptoPP::SHA1 Checksum; + AString ServerID = cRoot::Get()->GetServer()->GetServerID(); + Checksum.Update((const byte *)ServerID.c_str(), ServerID.length()); + Checksum.Update(a_Key, 16); + Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length()); + byte Digest[20]; + Checksum.Final(Digest); + DigestToJava(Digest, m_AuthServerID); +} + + + + diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h new file mode 100644 index 000000000..dc4d8aeef --- /dev/null +++ b/src/Protocol/Protocol132.h @@ -0,0 +1,102 @@ + +// Protocol132.h + +// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39) + + + + + +#pragma once + +#include "Protocol125.h" +#include "../../CryptoPP/modes.h" +#include "../../CryptoPP/aes.h" + + + + + +class cProtocol132 : + public cProtocol125 +{ + typedef cProtocol125 super; +public: + + cProtocol132(cClientHandle * a_Client); + virtual ~cProtocol132(); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + // Sending commands (alphabetically sorted): + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + + virtual AString GetAuthServerID(void) override; + + /// Handling of the additional packets: + virtual int ParsePacket(unsigned char a_PacketType) override; + + // Modified packets: + virtual int ParseBlockPlace (void) override; + virtual int ParseHandshake (void) override; + virtual int ParseLogin (void) override; + virtual int ParsePlayerAbilities(void) override; + + // New packets: + virtual int ParseClientStatuses (void); + virtual int ParseEncryptionKeyResponse(void); + virtual int ParseLocaleViewDistance (void); + virtual int ParseTabCompletion (void); + +protected: + bool m_IsEncrypted; + CryptoPP::CFB_Mode::Decryption m_Decryptor; + CryptoPP::CFB_Mode::Encryption m_Encryptor; + AString m_DataToSend; + + /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID() + AString m_AuthServerID; + + /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption() + AString m_ServerPublicKey; + + virtual void SendData(const char * a_Data, int a_Size) override; + + // DEBUG: + virtual void Flush(void) override; + + // Items in slots are sent differently + virtual void WriteItem(const cItem & a_Item) override; + virtual int ParseItem(cItem & a_Item) override; + + /// Parses the metadata that may come with the item. + int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); + + virtual void SendCompass(const cWorld & a_World); + virtual void SendEncryptionKeyRequest(void); + + /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption + void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce); + + /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID + void StartEncryption(const byte * a_Key); +} ; + + + + diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp new file mode 100644 index 000000000..d2582458b --- /dev/null +++ b/src/Protocol/Protocol14x.cpp @@ -0,0 +1,256 @@ + +// Protocol14x.cpp + +/* +Implements the 1.4.x protocol classes representing these protocols: +- cProtocol142: + - release 1.4.2 protocol (#47) + - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) + - release 1.4.5 protocol (same as 1.4.4) +- cProtocol146: + - release 1.4.6 protocol (#51) +*/ + +#include "Globals.h" +#include "Protocol14x.h" +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "../../CryptoPP/randpool.h" +#include "../Item.h" +#include "ChunkDataSerializer.h" +#include "../Entities/Player.h" +#include "../Mobs/Monster.h" +#include "../UI/Window.h" +#include "../Entities/Pickup.h" +#include "../Entities/FallingBlock.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_UPDATE_TIME = 0x04, + PACKET_PICKUP_SPAWN = 0x15, + PACKET_SPAWN_OBJECT = 0x17, + PACKET_ENTITY_METADATA = 0x28, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol142: + +cProtocol142::cProtocol142(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +int cProtocol142::ParseLocaleViewDistance(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); + HANDLE_PACKET_READ(ReadChar, char, ViewDistance); + HANDLE_PACKET_READ(ReadChar, char, ChatFlags); + HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); + HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2 + // TODO: m_Client->HandleLocale(Locale); + // TODO: m_Client->HandleViewDistance(ViewDistance); + // TODO: m_Client->HandleChatFlags(ChatFlags); + // Ignoring client difficulty + return PARSE_OK; +} + + + + + +void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PICKUP_SPAWN); + WriteInt (a_Pickup.GetUniqueID()); + WriteItem (a_Pickup.GetItem()); + WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); + WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); + Flush(); +} + + + + + +void cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SOUND_PARTICLE_EFFECT); + WriteInt (a_EffectID); + WriteInt (a_SrcX); + WriteByte(a_SrcY); + WriteInt (a_SrcZ); + WriteInt (a_Data); + WriteBool(0); + Flush(); +} + + + + + +void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_TIME); + WriteInt64(a_WorldAge); + WriteInt64(a_TimeOfDay); + Flush(); +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol146: + +cProtocol146::cProtocol146(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup) +{ + ASSERT(!a_Pickup.GetItem().IsEmpty()); + + cCSLock Lock(m_CSPacket); + + // Send a SPAWN_OBJECT packet for the base entity: + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Pickup.GetUniqueID()); + WriteByte(0x02); + WriteInt ((int)(a_Pickup.GetPosX() * 32)); + WriteInt ((int)(a_Pickup.GetPosY() * 32)); + WriteInt ((int)(a_Pickup.GetPosZ() * 32)); + WriteInt (1); + WriteShort((short)(a_Pickup.GetSpeed().x * 32)); + WriteShort((short)(a_Pickup.GetSpeed().y * 32)); + WriteShort((short)(a_Pickup.GetSpeed().z * 32)); + WriteByte(0); + WriteByte(0); + + // Send a ENTITY_METADATA packet with the slot info: + WriteByte(PACKET_ENTITY_METADATA); + WriteInt(a_Pickup.GetUniqueID()); + WriteByte(0xaa); // a slot value at index 10 + WriteItem(a_Pickup.GetItem()); + WriteByte(0x7f); // End of metadata + Flush(); +} + + + + + +void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + // Send a spawn object / vehicle packet + cCSLock Lock(m_CSPacket); + + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_FallingBlock.GetUniqueID()); + WriteByte(70); + WriteInt ((int)(a_FallingBlock.GetPosX() * 32)); + WriteInt ((int)(a_FallingBlock.GetPosY() * 32)); + WriteInt ((int)(a_FallingBlock.GetPosZ() * 32)); + WriteByte (0); // Pitch + WriteByte (0); // Yaw + WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype + WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); + WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); + WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); + Flush(); +} + + + + + +void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_ObjectType); + WriteInt ((int)(a_Entity.GetPosX() * 32)); + WriteInt ((int)(a_Entity.GetPosY() * 32)); + WriteInt ((int)(a_Entity.GetPosZ() * 32)); + WriteByte(a_Pitch); + WriteByte(a_Yaw); + WriteInt (a_ObjectData); + if (a_ObjectData != 0) + { + WriteShort((short)(a_Entity.GetSpeedX() * 400)); + WriteShort((short)(a_Entity.GetSpeedY() * 400)); + WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + +void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_OBJECT); + WriteInt (a_Vehicle.GetUniqueID()); + WriteByte (a_VehicleType); + WriteInt ((int)(a_Vehicle.GetPosX() * 32)); + WriteInt ((int)(a_Vehicle.GetPosY() * 32)); + WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); + WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); + WriteInt (a_VehicleSubType); + if (a_VehicleSubType != 0) + { + WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + diff --git a/src/Protocol/Protocol14x.h b/src/Protocol/Protocol14x.h new file mode 100644 index 000000000..ca497bbc1 --- /dev/null +++ b/src/Protocol/Protocol14x.h @@ -0,0 +1,63 @@ + +// Protocol14x.h + +/* +Interfaces to the 1.4.x protocol classes representing these protocols: +- cProtocol142: + - release 1.4.2 protocol (#47) + - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) + - release 1.4.5 protocol (same as 1.4.4) +- cProtocol146: + - release 1.4.6 protocol (#51) +*/ + + + + + +#pragma once + +#include "Protocol132.h" + + + + + +class cProtocol142 : + public cProtocol132 +{ + typedef cProtocol132 super; + +public: + cProtocol142(cClientHandle * a_Client); + + // Sending commands (alphabetically sorted): + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + + // Specific packet parsers: + virtual int ParseLocaleViewDistance(void) override; +} ; + + + + + +class cProtocol146 : + public cProtocol142 +{ + typedef cProtocol142 super; + +public: + cProtocol146(cClientHandle * a_Client); + + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; +} ; + + + + diff --git a/src/Protocol/Protocol15x.cpp b/src/Protocol/Protocol15x.cpp new file mode 100644 index 000000000..c337d26e7 --- /dev/null +++ b/src/Protocol/Protocol15x.cpp @@ -0,0 +1,138 @@ + +// Protocol15x.cpp + +/* +Implements the 1.5.x protocol classes: + - cProtocol150 + - release 1.5 protocol (#60) + - release 1.5.2 protocol (#61, no relevant changes found) +*/ + +#include "Globals.h" +#include "Protocol15x.h" +#include "../ClientHandle.h" +#include "../Item.h" +#include "../UI/Window.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_WINDOW_OPEN = 0x64, +} ; + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol150: + +cProtocol150::cProtocol150(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol150::SendWindowOpen(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_Window.GetWindowID()); + WriteByte (a_Window.GetWindowType()); + WriteString(a_Window.GetWindowTitle()); + WriteByte (a_Window.GetNumNonInventorySlots()); + WriteByte (1); // Use title + Flush(); +} + + + + + +int cProtocol150::ParseWindowClick(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + HANDLE_PACKET_READ(ReadByte, Byte, Button); + HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); + HANDLE_PACKET_READ(ReadByte, Byte, Mode); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + // Convert Button, Mode, SlotNum and HeldItem into eClickAction: + eClickAction Action; + switch ((Mode << 8) | Button) + { + case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break; + case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break; + case 0x0100: Action = caShiftLeftClick; break; + case 0x0101: Action = caShiftRightClick; break; + case 0x0200: Action = caNumber1; break; + case 0x0201: Action = caNumber2; break; + case 0x0202: Action = caNumber3; break; + case 0x0203: Action = caNumber4; break; + case 0x0204: Action = caNumber5; break; + case 0x0205: Action = caNumber6; break; + case 0x0206: Action = caNumber7; break; + case 0x0207: Action = caNumber8; break; + case 0x0208: Action = caNumber9; break; + case 0x0300: Action = caMiddleClick; break; + case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break; + case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break; + case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break; + case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break; + case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break; + case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break; + case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break; + case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break; + case 0x0600: Action = caDblClick; break; + } + + if (Action == caUnknown) + { + LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.", + Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str() + ); + ASSERT(!"Unknown click action"); + return PARSE_OK; + } + + m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); + return PARSE_OK; +} + + + + + diff --git a/src/Protocol/Protocol15x.h b/src/Protocol/Protocol15x.h new file mode 100644 index 000000000..e554fe130 --- /dev/null +++ b/src/Protocol/Protocol15x.h @@ -0,0 +1,38 @@ + +// Protocol15x.h + +/* +Declares the 1.5.x protocol classes: + - cProtocol150 + - release 1.5 and 1.5.1 protocol (#60) + - release 1.5.2 protocol (#61; no relevant changes found) +*/ + + + + + +#pragma once + +#include "Protocol14x.h" + + + + + +class cProtocol150 : + public cProtocol146 +{ + typedef cProtocol146 super; + +public: + cProtocol150(cClientHandle * a_Client); + + virtual void SendWindowOpen(const cWindow & a_Window) override; + + virtual int ParseWindowClick(void); +} ; + + + + diff --git a/src/Protocol/Protocol16x.cpp b/src/Protocol/Protocol16x.cpp new file mode 100644 index 000000000..cfa27b3c4 --- /dev/null +++ b/src/Protocol/Protocol16x.cpp @@ -0,0 +1,268 @@ + +// Protocol16x.cpp + +/* +Implements the 1.6.x protocol classes: + - cProtocol161 + - release 1.6.1 protocol (#73) + - cProtocol162 + - release 1.6.2 protocol (#74) + - release 1.6.3 protocol (#77) - no relevant changes + - release 1.6.4 protocol (#78) - no relevant changes +(others may be added later in the future for the 1.6 release series) +*/ + +#include "Globals.h" +#include "Protocol16x.h" +#include "../ClientHandle.h" +#include "../Entities/Entity.h" +#include "../Entities/Player.h" +#include "../UI/Window.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_CHAT = 0x03, + PACKET_UPDATE_HEALTH = 0x08, + PACKET_STEER_VEHICLE = 0x1b, + PACKET_ATTACH_ENTITY = 0x27, + PACKET_ENTITY_PROPERTIES = 0x2c, + PACKET_WINDOW_OPEN = 0x64, + PACKET_TILE_EDITOR_OPEN = 0x85, + PACKET_PLAYER_ABILITIES = 0xca, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol161: + +cProtocol161::cProtocol161(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ATTACH_ENTITY); + WriteInt(a_Entity.GetUniqueID()); + WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); + WriteBool(false); // TODO: "Should use leash?" -> no + Flush(); +} + + + + + +void cProtocol161::SendChat(const AString & a_Message) +{ + super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str())); +} + + + + + +void cProtocol161::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_TILE_EDITOR_OPEN); + WriteByte(0); + WriteInt(a_BlockX); + WriteInt(a_BlockY); + WriteInt(a_BlockZ); + Flush(); +} + + + + + +void cProtocol161::SendGameMode(eGameMode a_GameMode) +{ + super::SendGameMode(a_GameMode); + SendPlayerMaxSpeed(); +} + + + + + +void cProtocol161::SendHealth(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_HEALTH); + WriteFloat((float)m_Client->GetPlayer()->GetHealth()); + WriteShort(m_Client->GetPlayer()->GetFoodLevel()); + WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); + Flush(); +} + + + + + +void cProtocol161::SendPlayerMaxSpeed(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENTITY_PROPERTIES); + WriteInt(m_Client->GetPlayer()->GetUniqueID()); + WriteInt(1); + WriteString("generic.movementSpeed"); + WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); + Flush(); +} + + + + + +void cProtocol161::SendRespawn(void) +{ + // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast + super::SendRespawn(); + SendPlayerMaxSpeed(); +} + + + + + +void cProtocol161::SendWindowOpen(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_Window.GetWindowID()); + WriteByte (a_Window.GetWindowType()); + WriteString(a_Window.GetWindowTitle()); + WriteByte (a_Window.GetNumNonInventorySlots()); + WriteByte (1); // Use title + if (a_Window.GetWindowType() == cWindow::wtAnimalChest) + { + WriteInt(0); // TODO: The animal's EntityID + } + Flush(); +} + + + + + +int cProtocol161::ParseEntityAction(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, ActionID); + HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal); + m_Client->HandleEntityAction(EntityID, ActionID); + return PARSE_OK; +} + + + + + +int cProtocol161::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadByte, Byte, Flags); + HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed); + HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +int cProtocol161::ParseSteerVehicle(void) +{ + HANDLE_PACKET_READ(ReadBEFloat, float, Sideways); + HANDLE_PACKET_READ(ReadBEFloat, float, Forward); + HANDLE_PACKET_READ(ReadBool, bool, Jump); + HANDLE_PACKET_READ(ReadBool, bool, Unmount); + if (Unmount) + { + m_Client->HandleUnmount(); + } + else + { + m_Client->HandleSteerVehicle(Forward, Sideways); + } + return PARSE_OK; +} + + + + + +int cProtocol161::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + case PACKET_STEER_VEHICLE: return ParseSteerVehicle(); + default: return super::ParsePacket(a_PacketType); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol162: + +cProtocol162::cProtocol162(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol162::SendPlayerMaxSpeed(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENTITY_PROPERTIES); + WriteInt(m_Client->GetPlayer()->GetUniqueID()); + WriteInt(1); + WriteString("generic.movementSpeed"); + WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); + WriteShort(0); + Flush(); +} + + + + diff --git a/src/Protocol/Protocol16x.h b/src/Protocol/Protocol16x.h new file mode 100644 index 000000000..325e41c5a --- /dev/null +++ b/src/Protocol/Protocol16x.h @@ -0,0 +1,76 @@ + +// Protocol16x.h + +/* +Declares the 1.6.x protocol classes: + - cProtocol161 + - release 1.6.1 protocol (#73) + - cProtocol162 + - release 1.6.2 protocol (#74) + - release 1.6.3 protocol (#77) - no relevant changes + - release 1.6.4 protocol (#78) - no relevant changes +(others may be added later in the future for the 1.6 release series) +*/ + + + + + +#pragma once + +#include "Protocol15x.h" + + + + + +class cProtocol161 : + public cProtocol150 +{ + typedef cProtocol150 super; + +public: + cProtocol161(cClientHandle * a_Client); + +protected: + + // cProtocol150 overrides: + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendPlayerMaxSpeed(void) override; + virtual void SendRespawn (void) override; + virtual void SendWindowOpen (const cWindow & a_Window) override; + + virtual int ParseEntityAction (void) override; + virtual int ParsePlayerAbilities(void) override; + + // New packets: + virtual int ParseSteerVehicle(void); + + // Enable new packets' handling + virtual int ParsePacket(unsigned char a_PacketType) override; +} ; + + + + + +class cProtocol162 : + public cProtocol161 +{ + typedef cProtocol161 super; + +public: + cProtocol162(cClientHandle * a_Client); + +protected: + // cProtocol161 overrides: + virtual void SendPlayerMaxSpeed(void) override; +} ; + + + + diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp new file mode 100644 index 000000000..628b8e071 --- /dev/null +++ b/src/Protocol/Protocol17x.cpp @@ -0,0 +1,1917 @@ + +// Protocol17x.cpp + +/* +Implements the 1.7.x protocol classes: + - cProtocol172 + - release 1.7.2 protocol (#4) +(others may be added later in the future for the 1.7 release series) +*/ + +#include "Globals.h" +#include "Protocol17x.h" +#include "ChunkDataSerializer.h" +#include "../ClientHandle.h" +#include "../Root.h" +#include "../Server.h" +#include "../World.h" +#include "../WorldStorage/FastNBT.h" +#include "../StringCompression.h" +#include "../Entities/Minecart.h" +#include "../Entities/FallingBlock.h" +#include "../Entities/Pickup.h" +#include "../Entities/Player.h" +#include "../Mobs/IncludeAllMonsters.h" +#include "../UI/Window.h" + + + + + +#define HANDLE_READ(Proc, Type, Var) \ + Type Var; \ + m_ReceivedData.Proc(Var); + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return false; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) : + super(a_Client), + m_ServerAddress(a_ServerAddress), + m_ServerPort(a_ServerPort), + m_State(a_State), + m_ReceivedData(32 KiB), + m_OutPacketBuffer(64 KiB), + m_OutPacketLenBuffer(20), // 20 bytes is more than enough for one VarInt + m_IsEncrypted(false) +{ +} + + + + + +void cProtocol172::DataReceived(const char * a_Data, int a_Size) +{ + if (m_IsEncrypted) + { + byte Decrypted[512]; + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; + m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); + AddReceivedData((const char *)Decrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + AddReceivedData(a_Data, a_Size); + } +} + + + + + +void cProtocol172::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cPacketizer Pkt(*this, 0x1b); // Attach Entity packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteInt((a_Vehicle != NULL) ? a_Vehicle->GetUniqueID() : 0); + Pkt.WriteBool(false); +} + + + + + +void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + cPacketizer Pkt(*this, 0x24); // Block Action packet + Pkt.WriteInt(a_BlockX); + Pkt.WriteShort(a_BlockY); + Pkt.WriteInt(a_BlockZ); + Pkt.WriteByte(a_Byte1); + Pkt.WriteByte(a_Byte2); + Pkt.WriteVarInt(a_BlockType); +} + + + + + +void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) +{ + cPacketizer Pkt(*this, 0x24); // Block Break Animation packet + Pkt.WriteInt(a_EntityID); + Pkt.WriteInt(a_BlockX); + Pkt.WriteInt(a_BlockY); + Pkt.WriteInt(a_BlockZ); + Pkt.WriteChar(a_Stage); +} + + + + + +void cProtocol172::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cPacketizer Pkt(*this, 0x23); // Block Change packet + Pkt.WriteInt(a_BlockX); + Pkt.WriteByte(a_BlockY); + Pkt.WriteInt(a_BlockZ); + Pkt.WriteVarInt(a_BlockType); + Pkt.WriteByte(a_BlockMeta); +} + + + + + +void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + cPacketizer Pkt(*this, 0x22); // Multi Block Change packet + Pkt.WriteInt(a_ChunkX); + Pkt.WriteInt(a_ChunkZ); + Pkt.WriteShort((short)a_Changes.size()); + Pkt.WriteInt(a_Changes.size() * 4); + for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) + { + unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); + unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); + Pkt.WriteInt((Coords << 16) | Blocks); + } // for itr - a_Changes[] +} + + + + + +void cProtocol172::SendChat(const AString & a_Message) +{ + cPacketizer Pkt(*this, 0x02); // Chat Message packet + Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str())); +} + + + + + +void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + // Serialize first, before creating the Packetizer (the packetizer locks a CS) + // This contains the flags and bitmasks, too + const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); + + cPacketizer Pkt(*this, 0x21); // Chunk Data packet + Pkt.WriteInt(a_ChunkX); + Pkt.WriteInt(a_ChunkZ); + Pkt.WriteBuf(ChunkData.data(), ChunkData.size()); +} + + + + + +void cProtocol172::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + cPacketizer Pkt(*this, 0x0d); // Collect Item packet + Pkt.WriteInt(a_Pickup.GetUniqueID()); + Pkt.WriteInt(a_Player.GetUniqueID()); +} + + + + + +void cProtocol172::SendDestroyEntity(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x13); // Destroy Entities packet + Pkt.WriteByte(1); + Pkt.WriteInt(a_Entity.GetUniqueID()); +} + + + + + +void cProtocol172::SendDisconnect(const AString & a_Reason) +{ + cPacketizer Pkt(*this, 0x40); + Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str())); +} + + + + + +void cProtocol172::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cPacketizer Pkt(*this, 0x36); // Sign Editor Open packet + Pkt.WriteInt(a_BlockX); + Pkt.WriteInt(a_BlockY); + Pkt.WriteInt(a_BlockZ); +} + + + + + +void cProtocol172::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cPacketizer Pkt(*this, 0x04); // Entity Equipment packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteShort(a_SlotNum); + Pkt.WriteItem(a_Item); +} + + + + + +void cProtocol172::SendEntityHeadLook(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x19); // Entity Head Look packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteByteAngle(a_Entity.GetHeadYaw()); +} + + + + + +void cProtocol172::SendEntityLook(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x16); // Entity Look packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteByteAngle(a_Entity.GetYaw()); + Pkt.WriteByteAngle(a_Entity.GetPitch()); +} + + + + + +void cProtocol172::SendEntityMetadata(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteEntityMetadata(a_Entity); + Pkt.WriteByte(0x7f); // The termination byte +} + + + + + +void cProtocol172::SendEntityProperties(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x20); // Entity Properties packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteEntityProperties(a_Entity); +} + + + + + +void cProtocol172::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + cPacketizer Pkt(*this, 0x15); // Entity Relative Move packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteByte(a_RelX); + Pkt.WriteByte(a_RelY); + Pkt.WriteByte(a_RelZ); +} + + + + + +void cProtocol172::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + cPacketizer Pkt(*this, 0x17); // Entity Look And Relative Move packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteByte(a_RelX); + Pkt.WriteByte(a_RelY); + Pkt.WriteByte(a_RelZ); + Pkt.WriteByteAngle(a_Entity.GetYaw()); + Pkt.WriteByteAngle(a_Entity.GetPitch()); +} + + + + + +void cProtocol172::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + cPacketizer Pkt(*this, 0x1a); // Entity Status packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteChar(a_Status); +} + + + + + +void cProtocol172::SendEntityVelocity(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x12); // Entity Velocity packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + // 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick + Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400)); +} + + + + + +void cProtocol172::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + cPacketizer Pkt(*this, 0x27); // Explosion packet + Pkt.WriteFloat((float)a_BlockX); + Pkt.WriteFloat((float)a_BlockY); + Pkt.WriteFloat((float)a_BlockZ); + Pkt.WriteFloat((float)a_Radius); + Pkt.WriteInt(a_BlocksAffected.size()); + for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(), end = a_BlocksAffected.end(); itr != end; ++itr) + { + Pkt.WriteChar((char)itr->x); + Pkt.WriteChar((char)itr->y); + Pkt.WriteChar((char)itr->z); + } // for itr - a_BlockAffected[] + Pkt.WriteFloat((float)a_PlayerMotion.x); + Pkt.WriteFloat((float)a_PlayerMotion.y); + Pkt.WriteFloat((float)a_PlayerMotion.z); +} + + + + + +void cProtocol172::SendGameMode(eGameMode a_GameMode) +{ + cPacketizer Pkt(*this, 0x2b); // Change Game State packet + Pkt.WriteByte(3); // Reason: Change game mode + Pkt.WriteFloat((float)a_GameMode); +} + + + + + +void cProtocol172::SendHealth(void) +{ + cPacketizer Pkt(*this, 0x06); // Update Health packet + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetHealth()); + Pkt.WriteShort(m_Client->GetPlayer()->GetFoodLevel()); + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); +} + + + + + +void cProtocol172::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + cPacketizer Pkt(*this, 0x2f); // Set Slot packet + Pkt.WriteChar(a_WindowID); + Pkt.WriteShort(a_SlotNum); + Pkt.WriteItem(a_Item); +} + + + + + +void cProtocol172::SendKeepAlive(int a_PingID) +{ + cPacketizer Pkt(*this, 0x00); // Keep Alive packet + Pkt.WriteInt(a_PingID); +} + + + + + +void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + // Send the Join Game packet: + { + cPacketizer Pkt(*this, 0x01); // Join Game packet + Pkt.WriteInt(a_Player.GetUniqueID()); + Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (cRoot::Get()->GetServer()->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 + Pkt.WriteChar((char)a_World.GetDimension()); + Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) + Pkt.WriteByte(cRoot::Get()->GetServer()->GetMaxPlayers()); + Pkt.WriteString("default"); // Level type - wtf? + } + + // Send the spawn position: + { + cPacketizer Pkt(*this, 0x05); // Spawn Position packet + Pkt.WriteInt((int)a_World.GetSpawnX()); + Pkt.WriteInt((int)a_World.GetSpawnY()); + Pkt.WriteInt((int)a_World.GetSpawnZ()); + } + + // Send player abilities: + SendPlayerAbilities(); +} + + + + + +void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup) +{ + { + cPacketizer Pkt(*this, 0x0e); // Spawn Object packet + Pkt.WriteVarInt(a_Pickup.GetUniqueID()); + Pkt.WriteByte(2); // Type = Pickup + Pkt.WriteFPInt(a_Pickup.GetPosX()); + Pkt.WriteFPInt(a_Pickup.GetPosY()); + Pkt.WriteFPInt(a_Pickup.GetPosZ()); + Pkt.WriteByteAngle(a_Pickup.GetYaw()); + Pkt.WriteByteAngle(a_Pickup.GetPitch()); + Pkt.WriteInt(0); // No object data + } + { + cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet + Pkt.WriteInt(a_Pickup.GetUniqueID()); + Pkt.WriteByte((0x05 << 5) | 10); // Slot type + index 10 + Pkt.WriteItem(a_Pickup.GetItem()); + Pkt.WriteByte(0x7f); // End of metadata + } +} + + + + + +void cProtocol172::SendPlayerAbilities(void) +{ + cPacketizer Pkt(*this, 0x39); // Player Abilities packet + Byte Flags = 0; + if (m_Client->GetPlayer()->IsGameModeCreative()) + { + Flags |= 0x01; + } + // TODO: Other flags (god mode, flying, can fly + Pkt.WriteByte(Flags); + // TODO: Pkt.WriteFloat(m_Client->GetPlayer()->GetMaxFlyingSpeed()); + Pkt.WriteFloat(0.05f); + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetMaxSpeed()); +} + + + + + +void cProtocol172::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + cPacketizer Pkt(*this, 0x0b); // Animation packet + Pkt.WriteVarInt(a_Player.GetUniqueID()); + Pkt.WriteChar(a_Animation); +} + + + + + +void cProtocol172::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + cPacketizer Pkt(*this, 0x38); // Playerlist Item packet + Pkt.WriteString(a_Player.GetName()); + Pkt.WriteBool(a_IsOnline); + Pkt.WriteShort(a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0); +} + + + + + +void cProtocol172::SendPlayerMaxSpeed(void) +{ + cPacketizer Pkt(*this, 0x20); // Entity Properties + Pkt.WriteInt(m_Client->GetPlayer()->GetUniqueID()); + Pkt.WriteInt(1); // Count + Pkt.WriteString("generic.movementSpeed"); + Pkt.WriteDouble(0.1); + if (m_Client->GetPlayer()->IsSprinting()) + { + Pkt.WriteShort(1); // Modifier count + Pkt.WriteInt64(0x662a6b8dda3e4c1c); + Pkt.WriteInt64(0x881396ea6097278d); // UUID of the modifier + Pkt.WriteDouble(0.3); + Pkt.WriteByte(2); + } + else + { + Pkt.WriteShort(0); // Modifier count + } +} + + + + + +void cProtocol172::SendPlayerMoveLook(void) +{ + cPacketizer Pkt(*this, 0x08); // Player Position And Look packet + Pkt.WriteDouble(m_Client->GetPlayer()->GetPosX()); + Pkt.WriteDouble(m_Client->GetPlayer()->GetPosY()); + Pkt.WriteDouble(m_Client->GetPlayer()->GetPosZ()); + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetYaw()); + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetPitch()); + Pkt.WriteBool(m_Client->GetPlayer()->IsOnGround()); +} + + + + + +void cProtocol172::SendPlayerPosition(void) +{ + // There is no dedicated packet for this, send the whole thing: + SendPlayerMoveLook(); +} + + + + + +void cProtocol172::SendPlayerSpawn(const cPlayer & a_Player) +{ + // Called to spawn another player for the client + cPacketizer Pkt(*this, 0x0c); // Spawn Player packet + Pkt.WriteVarInt(a_Player.GetUniqueID()); + Pkt.WriteString(Printf("%d", a_Player.GetUniqueID())); // TODO: Proper UUID + Pkt.WriteString(a_Player.GetName()); + Pkt.WriteFPInt(a_Player.GetPosX()); + Pkt.WriteFPInt(a_Player.GetPosY()); + Pkt.WriteFPInt(a_Player.GetPosZ()); + Pkt.WriteByteAngle(a_Player.GetYaw()); + Pkt.WriteByteAngle(a_Player.GetPitch()); + short ItemType = a_Player.GetEquippedItem().IsEmpty() ? 0 : a_Player.GetEquippedItem().m_ItemType; + Pkt.WriteShort(ItemType); + Pkt.WriteByte((3 << 5) | 6); // Metadata: float + index 6 + Pkt.WriteFloat((float)a_Player.GetHealth()); + Pkt.WriteByte(0x7f); // Metadata: end +} + + + + + +void cProtocol172::SendRespawn(void) +{ + cPacketizer Pkt(*this, 0x07); // Respawn packet + Pkt.WriteInt(m_Client->GetPlayer()->GetWorld()->GetDimension()); + Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) + Pkt.WriteByte((Byte)m_Client->GetPlayer()->GetEffectiveGameMode()); + Pkt.WriteString("default"); +} + + + + + +void cProtocol172::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) // a_Src coords are Block * 8 +{ + cPacketizer Pkt(*this, 0x29); // Sound Effect packet + Pkt.WriteString(a_SoundName); + Pkt.WriteInt(a_SrcX); + Pkt.WriteInt(a_SrcY); + Pkt.WriteInt(a_SrcZ); + Pkt.WriteFloat(a_Volume); + Pkt.WriteByte((Byte)(a_Pitch * 63)); +} + + + + + +void cProtocol172::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cPacketizer Pkt(*this, 0x28); // Effect packet + Pkt.WriteInt(a_EffectID); + Pkt.WriteInt(a_SrcX); + Pkt.WriteByte(a_SrcY); + Pkt.WriteInt(a_SrcZ); + Pkt.WriteInt(a_Data); + Pkt.WriteBool(false); +} + + + + + +void cProtocol172::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + cPacketizer Pkt(*this, 0x0e); // Spawn Object packet + Pkt.WriteVarInt(a_FallingBlock.GetUniqueID()); + Pkt.WriteByte(70); // Falling block + Pkt.WriteFPInt(a_FallingBlock.GetPosX()); + Pkt.WriteFPInt(a_FallingBlock.GetPosY()); + Pkt.WriteFPInt(a_FallingBlock.GetPosZ()); + Pkt.WriteByteAngle(a_FallingBlock.GetYaw()); + Pkt.WriteByteAngle(a_FallingBlock.GetPitch()); + Pkt.WriteInt(((int)a_FallingBlock.GetBlockType()) | (((int)a_FallingBlock.GetBlockMeta()) << 12)); + Pkt.WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); +} + + + + + +void cProtocol172::SendSpawnMob(const cMonster & a_Mob) +{ + cPacketizer Pkt(*this, 0x0f); // Spawn Mob packet + Pkt.WriteVarInt(a_Mob.GetUniqueID()); + Pkt.WriteByte((Byte)a_Mob.GetMobType()); + Pkt.WriteFPInt(a_Mob.GetPosX()); + Pkt.WriteFPInt(a_Mob.GetPosY()); + Pkt.WriteFPInt(a_Mob.GetPosZ()); + Pkt.WriteByteAngle(a_Mob.GetPitch()); + Pkt.WriteByteAngle(a_Mob.GetHeadYaw()); + Pkt.WriteByteAngle(a_Mob.GetYaw()); + Pkt.WriteShort((short)(a_Mob.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_Mob.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_Mob.GetSpeedZ() * 400)); + Pkt.WriteEntityMetadata(a_Mob); + Pkt.WriteByte(0x7f); // Metadata terminator +} + + + + + +void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + cPacketizer Pkt(*this, 0xe); // Spawn Object packet + Pkt.WriteVarInt(a_Entity.GetUniqueID()); + Pkt.WriteByte(a_ObjectType); + Pkt.WriteFPInt(a_Entity.GetPosX()); + Pkt.WriteFPInt(a_Entity.GetPosY()); + Pkt.WriteFPInt(a_Entity.GetPosZ()); + Pkt.WriteByteAngle(a_Entity.GetYaw()); + Pkt.WriteByteAngle(a_Entity.GetPitch()); + Pkt.WriteInt(a_ObjectData); + if (a_ObjectData != 0) + { + Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + } +} + + + + + +void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) +{ + cPacketizer Pkt(*this, 0xe); // Spawn Object packet + Pkt.WriteVarInt(a_Vehicle.GetUniqueID()); + Pkt.WriteByte(a_VehicleType); + Pkt.WriteFPInt(a_Vehicle.GetPosX()); + Pkt.WriteFPInt(a_Vehicle.GetPosY()); + Pkt.WriteFPInt(a_Vehicle.GetPosZ()); + Pkt.WriteByteAngle(a_Vehicle.GetYaw()); + Pkt.WriteByteAngle(a_Vehicle.GetPitch()); + Pkt.WriteInt(a_VehicleSubType); + if (a_VehicleSubType != 0) + { + Pkt.WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + } +} + + + + + +void cProtocol172::SendTabCompletionResults(const AStringVector & a_Results) +{ + AString Results; + Results.reserve(500); // Make a moderate reservation to avoid excessive reallocations + for (AStringVector::const_iterator itr = a_Results.begin(), end = a_Results.end(); itr != end; ++itr) + { + Results.append(*itr); + Results.push_back(0); + } + + cPacketizer Pkt(*this, 0x3a); // Tab-Complete packet + Pkt.WriteVarInt(a_Results.size()); + Pkt.WriteString(Results); +} + + + + + +void cProtocol172::SendTeleportEntity(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x18); + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteFPInt(a_Entity.GetPosX()); + Pkt.WriteFPInt(a_Entity.GetPosY()); + Pkt.WriteFPInt(a_Entity.GetPosZ()); + Pkt.WriteByteAngle(a_Entity.GetYaw()); + Pkt.WriteByteAngle(a_Entity.GetPitch()); +} + + + + + +void cProtocol172::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cPacketizer Pkt(*this, 0x2c); // Spawn Global Entity packet + Pkt.WriteVarInt(0); // EntityID = 0, always + Pkt.WriteByte(1); // Type = Thunderbolt + Pkt.WriteFPInt(a_BlockX); + Pkt.WriteFPInt(a_BlockY); + Pkt.WriteFPInt(a_BlockZ); +} + + + + + +void cProtocol172::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + cPacketizer Pkt(*this, 0x03); + Pkt.WriteInt64(a_WorldAge); + Pkt.WriteInt64(a_TimeOfDay); +} + + + + + +void cProtocol172::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + cPacketizer Pkt(*this, 0x21); // Chunk Data packet + Pkt.WriteInt(a_ChunkX); + Pkt.WriteInt(a_ChunkZ); + Pkt.WriteBool(true); + Pkt.WriteShort(0); // Primary bitmap + Pkt.WriteShort(0); // Add bitmap + Pkt.WriteInt(0); // Compressed data size +} + + + + + +void cProtocol172::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + cPacketizer Pkt(*this, 0x33); + Pkt.WriteInt(a_BlockX); + Pkt.WriteShort((short)a_BlockY); + Pkt.WriteInt(a_BlockZ); + Pkt.WriteString(a_Line1); + Pkt.WriteString(a_Line2); + Pkt.WriteString(a_Line3); + Pkt.WriteString(a_Line4); +} + + + + + +void cProtocol172::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cPacketizer Pkt(*this, 0x0a); + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteInt(a_BlockX); + Pkt.WriteByte((Byte)a_BlockY); + Pkt.WriteInt(a_BlockZ); +} + + + + + +void cProtocol172::SendWeather(eWeather a_Weather) +{ + { + cPacketizer Pkt(*this, 0x2b); // Change Game State packet + Pkt.WriteByte((a_Weather == wSunny) ? 1 : 2); // End rain / begin rain + Pkt.WriteFloat(0); // Unused for weather + } + + // TODO: Fade effect, somehow +} + + + + + +void cProtocol172::SendWholeInventory(const cWindow & a_Window) +{ + cPacketizer Pkt(*this, 0x30); // Window Items packet + Pkt.WriteChar(a_Window.GetWindowID()); + Pkt.WriteShort(a_Window.GetNumSlots()); + cItems Slots; + a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); + for (cItems::const_iterator itr = Slots.begin(), end = Slots.end(); itr != end; ++itr) + { + Pkt.WriteItem(*itr); + } // for itr - Slots[] +} + + + + + +void cProtocol172::SendWindowClose(const cWindow & a_Window) +{ + cPacketizer Pkt(*this, 0x2e); + Pkt.WriteChar(a_Window.GetWindowID()); +} + + + + + +void cProtocol172::SendWindowOpen(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() < 0) + { + // Do not send this packet for player inventory windows + return; + } + + cPacketizer Pkt(*this, 0x2d); + Pkt.WriteChar(a_Window.GetWindowID()); + Pkt.WriteChar(a_Window.GetWindowType()); + Pkt.WriteString(a_Window.GetWindowTitle()); + Pkt.WriteChar(a_Window.GetNumNonInventorySlots()); + Pkt.WriteBool(true); + if (a_Window.GetWindowType() == cWindow::wtAnimalChest) + { + Pkt.WriteInt(0); // TODO: The animal's EntityID + } +} + + + + + +void cProtocol172::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) +{ + cPacketizer Pkt(*this, 0x31); // Window Property packet + Pkt.WriteChar(a_Window.GetWindowID()); + Pkt.WriteShort(a_Property); + Pkt.WriteShort(a_Value); +} + + + + + +void cProtocol172::AddReceivedData(const char * a_Data, int a_Size) +{ + if (!m_ReceivedData.Write(a_Data, a_Size)) + { + // Too much data in the incoming queue, report to caller: + m_Client->PacketBufferFull(); + return; + } + + // Handle all complete packets: + while (true) + { + UInt32 PacketLen; + if (!m_ReceivedData.ReadVarInt(PacketLen)) + { + // Not enough data + return; + } + if (!m_ReceivedData.CanReadBytes(PacketLen)) + { + // The full packet hasn't been received yet + return; + } + UInt32 PacketType; + UInt32 Mark1 = m_ReceivedData.GetReadableSpace(); + if (!m_ReceivedData.ReadVarInt(PacketType)) + { + // Not enough data + return; + } + + UInt32 NumBytesRead = Mark1 - m_ReceivedData.GetReadableSpace(); + HandlePacket(PacketType, PacketLen - NumBytesRead); + + if (Mark1 - m_ReceivedData.GetReadableSpace() > PacketLen) + { + // Read more than packet length, report as error + m_Client->PacketError(PacketType); + } + + // Go to packet end in any case: + m_ReceivedData.ResetRead(); + m_ReceivedData.ReadVarInt(PacketType); + m_ReceivedData.SkipRead(PacketLen); + m_ReceivedData.CommitRead(); + } // while (true) +} + + + + +void cProtocol172::HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes) +{ + switch (m_State) + { + case 1: + { + // Status + switch (a_PacketType) + { + case 0x00: HandlePacketStatusRequest(a_RemainingBytes); return; + case 0x01: HandlePacketStatusPing (a_RemainingBytes); return; + } + break; + } + + case 2: + { + // Login + switch (a_PacketType) + { + case 0x00: HandlePacketLoginStart(a_RemainingBytes); return; + case 0x01: HandlePacketLoginEncryptionResponse(a_RemainingBytes); return; + } + break; + } + + case 3: + { + // Game + switch (a_PacketType) + { + case 0x00: HandlePacketKeepAlive (a_RemainingBytes); return; + case 0x01: HandlePacketChatMessage (a_RemainingBytes); return; + case 0x02: HandlePacketUseEntity (a_RemainingBytes); return; + case 0x03: HandlePacketPlayer (a_RemainingBytes); return; + case 0x04: HandlePacketPlayerPos (a_RemainingBytes); return; + case 0x05: HandlePacketPlayerLook (a_RemainingBytes); return; + case 0x06: HandlePacketPlayerPosLook (a_RemainingBytes); return; + case 0x07: HandlePacketBlockDig (a_RemainingBytes); return; + case 0x08: HandlePacketBlockPlace (a_RemainingBytes); return; + case 0x09: HandlePacketSlotSelect (a_RemainingBytes); return; + case 0x0a: HandlePacketAnimation (a_RemainingBytes); return; + case 0x0b: HandlePacketEntityAction (a_RemainingBytes); return; + case 0x0c: HandlePacketSteerVehicle (a_RemainingBytes); return; + case 0x0d: HandlePacketWindowClose (a_RemainingBytes); return; + case 0x0e: HandlePacketWindowClick (a_RemainingBytes); return; + case 0x0f: // Confirm transaction - not used in MCS + case 0x10: HandlePacketCreativeInventoryAction(a_RemainingBytes); return; + case 0x12: HandlePacketUpdateSign (a_RemainingBytes); return; + case 0x13: HandlePacketPlayerAbilities (a_RemainingBytes); return; + case 0x14: HandlePacketTabComplete (a_RemainingBytes); return; + case 0x15: HandlePacketClientSettings (a_RemainingBytes); return; + case 0x16: HandlePacketClientStatus (a_RemainingBytes); return; + case 0x17: HandlePacketPluginMessage (a_RemainingBytes); return; + } + break; + } + } // switch (m_State) + + // Unknown packet type, report to the client: + m_Client->PacketUnknown(a_PacketType); + m_ReceivedData.SkipRead(a_RemainingBytes); + m_ReceivedData.CommitRead(); +} + + + + + +void cProtocol172::HandlePacketStatusPing(UInt32 a_RemainingBytes) +{ + ASSERT(a_RemainingBytes == 8); + if (a_RemainingBytes != 8) + { + m_Client->PacketError(0x01); + m_ReceivedData.SkipRead(a_RemainingBytes); + m_ReceivedData.CommitRead(); + return; + } + Int64 Timestamp; + m_ReceivedData.ReadBEInt64(Timestamp); + m_ReceivedData.CommitRead(); + + cPacketizer Pkt(*this, 0x01); // Ping packet + Pkt.WriteInt64(Timestamp); +} + + + + + +void cProtocol172::HandlePacketStatusRequest(UInt32 a_RemainingBytes) +{ + // No more bytes in this packet + ASSERT(a_RemainingBytes == 0); + m_ReceivedData.CommitRead(); + + // Send the response: + AString Response = "{\"version\":{\"name\":\"1.7.2\",\"protocol\":4},\"players\":{"; + AppendPrintf(Response, "\"max\":%u,\"online\":%u,\"sample\":[]},", + cRoot::Get()->GetServer()->GetMaxPlayers(), + cRoot::Get()->GetServer()->GetNumPlayers() + ); + AppendPrintf(Response, "\"description\":{\"text\":\"%s\"}", + cRoot::Get()->GetServer()->GetDescription().c_str() + ); + Response.append("}"); + + cPacketizer Pkt(*this, 0x00); // Response packet + Pkt.WriteString(Response); +} + + + + + +void cProtocol172::HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes) +{ + // TODO: Add protocol encryption +} + + + + + +void cProtocol172::HandlePacketLoginStart(UInt32 a_RemainingBytes) +{ + AString Username; + m_ReceivedData.ReadVarUTF8String(Username); + + // TODO: Protocol encryption should be set up here if not localhost / auth + + // Send login success: + { + cPacketizer Pkt(*this, 0x02); // Login success packet + Pkt.WriteString(Printf("%d", m_Client->GetUniqueID())); // TODO: proper UUID + Pkt.WriteString(Username); + } + + m_State = 3; // State = Game + m_Client->HandleLogin(4, Username); +} + + + + + +void cProtocol172::HandlePacketAnimation(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, EntityID); + HANDLE_READ(ReadByte, Byte, Animation); + m_Client->HandleAnimation(Animation); +} + + + + + +void cProtocol172::HandlePacketBlockDig(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadByte, Byte, Status); + HANDLE_READ(ReadBEInt, int, BlockX); + HANDLE_READ(ReadByte, Byte, BlockY); + HANDLE_READ(ReadBEInt, int, BlockZ); + HANDLE_READ(ReadByte, Byte, Face); + m_Client->HandleLeftClick(BlockX, BlockY, BlockZ, Face, Status); +} + + + + + +void cProtocol172::HandlePacketBlockPlace(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, BlockX); + HANDLE_READ(ReadByte, Byte, BlockY); + HANDLE_READ(ReadBEInt, int, BlockZ); + HANDLE_READ(ReadByte, Byte, Face); + HANDLE_READ(ReadByte, Byte, CursorX); + HANDLE_READ(ReadByte, Byte, CursorY); + HANDLE_READ(ReadByte, Byte, CursorZ); + m_Client->HandleRightClick(BlockX, BlockY, BlockZ, Face, CursorX, CursorY, CursorZ, m_Client->GetPlayer()->GetEquippedItem()); +} + + + + + +void cProtocol172::HandlePacketChatMessage(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadVarUTF8String, AString, Message); + m_Client->HandleChat(Message); +} + + + + + +void cProtocol172::HandlePacketClientSettings(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadVarUTF8String, AString, Locale); + HANDLE_READ(ReadByte, Byte, ViewDistance); + HANDLE_READ(ReadByte, Byte, ChatFlags); + HANDLE_READ(ReadByte, Byte, Unused); + HANDLE_READ(ReadByte, Byte, Difficulty); + HANDLE_READ(ReadByte, Byte, ShowCape); + // TODO: handle in m_Client +} + + + + + +void cProtocol172::HandlePacketClientStatus(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadByte, Byte, ActionID); + switch (ActionID) + { + case 0: + { + // Respawn + m_Client->HandleRespawn(); + break; + } + case 1: + { + // Request stats + // TODO + break; + } + case 2: + { + // Open Inventory achievement + // TODO + break; + } + } +} + + + + + +void cProtocol172::HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEShort, short, SlotNum); + cItem Item; + if (!ReadItem(Item)) + { + return; + } + m_Client->HandleCreativeInventory(SlotNum, Item); +} + + + + + +void cProtocol172::HandlePacketEntityAction(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, PlayerID); + HANDLE_READ(ReadByte, Byte, Action); + HANDLE_READ(ReadBEInt, int, JumpBoost); + m_Client->HandleEntityAction(PlayerID, Action); +} + + + + + +void cProtocol172::HandlePacketKeepAlive(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, KeepAliveID); + m_Client->HandleKeepAlive(KeepAliveID); +} + + + + + +void cProtocol172::HandlePacketPlayer(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBool, bool, IsOnGround); + // TODO: m_Client->HandlePlayerOnGround(IsOnGround); +} + + + + + +void cProtocol172::HandlePacketPlayerAbilities(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadByte, Byte, Flags); + HANDLE_READ(ReadBEFloat, float, FlyingSpeed); + HANDLE_READ(ReadBEFloat, float, WalkingSpeed); + // TODO: m_Client->HandlePlayerAbilities(); +} + + + + + +void cProtocol172::HandlePacketPlayerLook(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEFloat, float, Yaw); + HANDLE_READ(ReadBEFloat, float, Pitch); + HANDLE_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerLook(Yaw, Pitch, IsOnGround); +} + + + + + +void cProtocol172::HandlePacketPlayerPos(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEDouble, double, PosX); + HANDLE_READ(ReadBEDouble, double, PosY); + HANDLE_READ(ReadBEDouble, double, Stance); + HANDLE_READ(ReadBEDouble, double, PosZ); + HANDLE_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround); +} + + + + + +void cProtocol172::HandlePacketPlayerPosLook(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEDouble, double, PosX); + HANDLE_READ(ReadBEDouble, double, PosY); + HANDLE_READ(ReadBEDouble, double, Stance); + HANDLE_READ(ReadBEDouble, double, PosZ); + HANDLE_READ(ReadBEFloat, float, Yaw); + HANDLE_READ(ReadBEFloat, float, Pitch); + HANDLE_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Yaw, Pitch, IsOnGround); +} + + + + + +void cProtocol172::HandlePacketPluginMessage(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadVarUTF8String, AString, Channel); + HANDLE_READ(ReadBEShort, short, Length); + AString Data; + m_ReceivedData.ReadString(Data, Length); + // TODO: m_Client->HandlePluginMessage(Channel, Data); +} + + + + + +void cProtocol172::HandlePacketSlotSelect(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEShort, short, SlotNum); + m_Client->HandleSlotSelected(SlotNum); +} + + + + + +void cProtocol172::HandlePacketSteerVehicle(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEFloat, float, Forward); + HANDLE_READ(ReadBEFloat, float, Sideways); + HANDLE_READ(ReadBool, bool, ShouldJump); + HANDLE_READ(ReadBool, bool, ShouldUnmount); + if (ShouldUnmount) + { + m_Client->HandleUnmount(); + } + else + { + m_Client->HandleSteerVehicle(Forward, Sideways); + } +} + + + + + +void cProtocol172::HandlePacketTabComplete(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadVarUTF8String, AString, Text); + m_Client->HandleTabCompletion(Text); +} + + + + + +void cProtocol172::HandlePacketUpdateSign(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, BlockX); + HANDLE_READ(ReadBEShort, short, BlockY); + HANDLE_READ(ReadBEInt, int, BlockZ); + HANDLE_READ(ReadVarUTF8String, AString, Line1); + HANDLE_READ(ReadVarUTF8String, AString, Line2); + HANDLE_READ(ReadVarUTF8String, AString, Line3); + HANDLE_READ(ReadVarUTF8String, AString, Line4); + m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); +} + + + + + +void cProtocol172::HandlePacketUseEntity(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, EntityID); + HANDLE_READ(ReadByte, Byte, MouseButton); + m_Client->HandleUseEntity(EntityID, (MouseButton == 1)); +} + + + + + +void cProtocol172::HandlePacketWindowClick(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadChar, char, WindowID); + HANDLE_READ(ReadBEShort, short, SlotNum); + HANDLE_READ(ReadByte, Byte, Button); + HANDLE_READ(ReadBEShort, short, TransactionID); + HANDLE_READ(ReadByte, Byte, Mode); + cItem Item; + ReadItem(Item); + + // Convert Button, Mode, SlotNum and HeldItem into eClickAction: + eClickAction Action; + switch ((Mode << 8) | Button) + { + case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break; + case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break; + case 0x0100: Action = caShiftLeftClick; break; + case 0x0101: Action = caShiftRightClick; break; + case 0x0200: Action = caNumber1; break; + case 0x0201: Action = caNumber2; break; + case 0x0202: Action = caNumber3; break; + case 0x0203: Action = caNumber4; break; + case 0x0204: Action = caNumber5; break; + case 0x0205: Action = caNumber6; break; + case 0x0206: Action = caNumber7; break; + case 0x0207: Action = caNumber8; break; + case 0x0208: Action = caNumber9; break; + case 0x0300: Action = caMiddleClick; break; + case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break; + case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break; + case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break; + case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break; + case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break; + case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break; + case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break; + case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break; + case 0x0600: Action = caDblClick; break; + } + + m_Client->HandleWindowClick(WindowID, SlotNum, Action, Item); +} + + + + + +void cProtocol172::HandlePacketWindowClose(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadChar, char, WindowID); + m_Client->HandleWindowClose(WindowID); +} + + + + + +void cProtocol172::WritePacket(cByteBuffer & a_Packet) +{ + cCSLock Lock(m_CSPacket); + AString Pkt; + a_Packet.ReadAll(Pkt); + WriteVarInt(Pkt.size()); + SendData(Pkt.data(), Pkt.size()); + Flush(); +} + + + + + +void cProtocol172::SendData(const char * a_Data, int a_Size) +{ + if (m_IsEncrypted) + { + byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; + m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); + m_Client->SendData((const char *)Encrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + m_Client->SendData(a_Data, a_Size); + } +} + + + + + + +bool cProtocol172::ReadItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + if (ItemType == -1) + { + // The item is empty, no more data follows + a_Item.Empty(); + return true; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength); + if (MetadataLength <= 0) + { + return true; + } + + // Read the metadata + AString Metadata; + if (!m_ReceivedData.ReadString(Metadata, MetadataLength)) + { + return false; + } + + ParseItemMetadata(a_Item, Metadata); + return true; +} + + + + + +void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +{ + // Uncompress the GZIPped data: + AString Uncompressed; + if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) + { + AString HexDump; + CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); + LOGWARNING("Cannot unGZIP item metadata (%u bytes):\n%s", a_Metadata.size(), HexDump.c_str()); + return; + } + + // Parse into NBT: + cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); + if (!NBT.IsValid()) + { + AString HexDump; + CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); + LOGWARNING("Cannot parse NBT item metadata: (%u bytes)\n%s", Uncompressed.size(), HexDump.c_str()); + return; + } + + // Load enchantments from the NBT: + for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) + { + if ( + (NBT.GetType(tag) == TAG_List) && + ( + (NBT.GetName(tag) == "ench") || + (NBT.GetName(tag) == "StoredEnchantments") + ) + ) + { + a_Item.m_Enchantments.ParseFromNBT(NBT, tag); + } + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol172::cPacketizer: + +cProtocol172::cPacketizer::~cPacketizer() +{ + AString DataToSend; + + // Send the packet length + UInt32 PacketLen = m_Out.GetUsedSpace(); + m_Protocol.m_OutPacketLenBuffer.WriteVarInt(PacketLen); + m_Protocol.m_OutPacketLenBuffer.ReadAll(DataToSend); + m_Protocol.SendData(DataToSend.data(), DataToSend.size()); + m_Protocol.m_OutPacketLenBuffer.CommitRead(); + + // Send the packet data: + m_Out.ReadAll(DataToSend); + m_Protocol.SendData(DataToSend.data(), DataToSend.size()); + m_Out.CommitRead(); +} + + + + + +void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + if (a_Item.IsEmpty()) + { + WriteShort(-1); + return; + } + + WriteShort(ItemType); + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + if (a_Item.m_Enchantments.IsEmpty()) + { + WriteShort(-1); + return; + } + + // Send the enchantments: + cFastNBTWriter Writer; + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); + Writer.Finish(); + AString Compressed; + CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); + WriteShort(Compressed.size()); + WriteBuf(Compressed.data(), Compressed.size()); +} + + + + + +void cProtocol172::cPacketizer::WriteByteAngle(double a_Angle) +{ + WriteByte((char)(255 * a_Angle / 360)); +} + + + + + +void cProtocol172::cPacketizer::WriteFPInt(double a_Value) +{ + int Value = (int)(a_Value * 32); + WriteInt(Value); +} + + + + + +void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity) +{ + // Common metadata: + Byte Flags = 0; + if (a_Entity.IsOnFire()) + { + Flags |= 0x01; + } + if (a_Entity.IsCrouched()) + { + Flags |= 0x02; + } + if (a_Entity.IsSprinting()) + { + Flags |= 0x08; + } + if (a_Entity.IsRclking()) + { + Flags |= 0x10; + } + if (a_Entity.IsInvisible()) + { + Flags |= 0x20; + } + WriteByte(0); // Byte(0) + index 0 + WriteByte(Flags); + + switch (a_Entity.GetEntityType()) + { + case cEntity::etPlayer: break; // TODO? + case cEntity::etPickup: + { + WriteByte((5 << 5) | 10); // Slot(5) + index 10 + WriteItem(((const cPickup &)a_Entity).GetItem()); + break; + } + case cEntity::etMinecart: + { + WriteByte(0x51); + + // The following expression makes Minecarts shake more with less health or higher damage taken + // It gets half the maximum health, and takes it away from the current health minus the half health: + /* Health: 5 | 3 - (5 - 3) = 1 (shake power) + Health: 3 | 3 - (3 - 3) = 3 + Health: 1 | 3 - (1 - 3) = 5 + */ + WriteInt((((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4); + WriteByte(0x52); + WriteInt(1); // Shaking direction, doesn't seem to affect anything + WriteByte(0x73); + WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer + + if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) + { + WriteByte(0x10); + WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); + } + break; + } + case cEntity::etProjectile: + { + if (((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow) + { + WriteByte(0x10); + WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); + } + break; + } + case cEntity::etMonster: + { + WriteMobMetadata((const cMonster &)a_Entity); + break; + } + } +} + + + + + +void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) +{ + switch (a_Mob.GetMobType()) + { + case cMonster::mtCreeper: + { + WriteByte(0x10); + WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); + WriteByte(0x11); + WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); + break; + } + + case cMonster::mtBat: + { + WriteByte(0x10); + WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); + break; + } + + case cMonster::mtPig: + { + WriteByte(0x10); + WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); + break; + } + + case cMonster::mtVillager: + { + WriteByte(0x50); + WriteInt(((const cVillager &)a_Mob).GetVilType()); + break; + } + + case cMonster::mtZombie: + { + WriteByte(0x0c); + WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); + WriteByte(0x0d); + WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); + WriteByte(0x0e); + WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); + break; + } + + case cMonster::mtGhast: + { + WriteByte(0x10); + WriteByte(((const cGhast &)a_Mob).IsCharging()); + break; + } + + case cMonster::mtWolf: + { + const cWolf & Wolf = (const cWolf &)a_Mob; + Byte WolfStatus = 0; + if (Wolf.IsSitting()) + { + WolfStatus |= 0x1; + } + if (Wolf.IsAngry()) + { + WolfStatus |= 0x2; + } + if (Wolf.IsTame()) + { + WolfStatus |= 0x4; + } + WriteByte(0x10); + WriteByte(WolfStatus); + + WriteByte(0x72); + WriteFloat((float)(a_Mob.GetHealth())); + WriteByte(0x13); + WriteByte(Wolf.IsBegging() ? 1 : 0); + WriteByte(0x14); + WriteByte(Wolf.GetCollarColor()); + break; + } + + case cMonster::mtSheep: + { + WriteByte(0x10); + Byte SheepMetadata = 0; + SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); + if (((const cSheep &)a_Mob).IsSheared()) + { + SheepMetadata |= 0x10; + } + WriteByte(SheepMetadata); + break; + } + + case cMonster::mtEnderman: + { + WriteByte(0x10); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); + WriteByte(0x11); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); + WriteByte(0x12); + WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); + break; + } + + case cMonster::mtSkeleton: + { + WriteByte(0x0d); + WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); + break; + } + + case cMonster::mtWitch: + { + WriteByte(0x15); + WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); + break; + } + + case cMonster::mtSlime: + { + WriteByte(0x10); + WriteByte(((const cSlime &)a_Mob).GetSize()); + break; + } + + case cMonster::mtMagmaCube: + { + WriteByte(0x10); + WriteByte(((const cMagmaCube &)a_Mob).GetSize()); + break; + } + + case cMonster::mtHorse: + { + const cHorse & Horse = (const cHorse &)a_Mob; + int Flags = 0; + if (Horse.IsTame()) + { + Flags |= 0x02; + } + if (Horse.IsSaddled()) + { + Flags |= 0x04; + } + if (Horse.IsChested()) + { + Flags |= 0x08; + } + if (Horse.IsBaby()) + { + Flags |= 0x10; + } + if (Horse.IsEating()) + { + Flags |= 0x20; + } + if (Horse.IsRearing()) + { + Flags |= 0x40; + } + if (Horse.IsMthOpen()) + { + Flags |= 0x80; + } + WriteByte(0x50); // Int at index 16 + WriteInt(Flags); + WriteByte(0x13); // Byte at index 19 + WriteByte(Horse.GetHorseType()); + WriteByte(0x54); // Int at index 20 + int Appearance = 0; + Appearance = Horse.GetHorseColor(); + Appearance |= Horse.GetHorseStyle() << 8; + WriteInt(Appearance); + WriteByte(0x56); // Int at index 22 + WriteInt(Horse.GetHorseArmour()); + break; + } + } // switch (a_Mob.GetType()) +} + + + + + +void cProtocol172::cPacketizer::WriteEntityProperties(const cEntity & a_Entity) +{ + if (!a_Entity.IsMob()) + { + // No properties for anything else than mobs + WriteInt(0); + return; + } + const cMonster & Mob = (const cMonster &)a_Entity; + + // TODO: Send properties and modifiers based on the mob type + + WriteInt(0); // NumProperties +} + + + + diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h new file mode 100644 index 000000000..844069403 --- /dev/null +++ b/src/Protocol/Protocol17x.h @@ -0,0 +1,258 @@ + +// Protocol17x.h + +/* +Declares the 1.7.x protocol classes: + - cProtocol172 + - release 1.7.2 protocol (#4) +(others may be added later in the future for the 1.7 release series) +*/ + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" +#include "../../CryptoPP/modes.h" +#include "../../CryptoPP/aes.h" + + + + + +class cProtocol172 : + public cProtocol // TODO +{ + typedef cProtocol super; // TODO + +public: + + cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityHeadLook (const cEntity & a_Entity) override; + virtual void SendEntityLook (const cEntity & a_Entity) override; + virtual void SendEntityMetadata (const cEntity & a_Entity) override; + virtual void SendEntityProperties (const cEntity & a_Entity) override; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntityVelocity (const cEntity & a_Entity) override; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAbilities (void) override; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMaxSpeed (void) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; + virtual void SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (const cWindow & a_Window) override; + virtual void SendWindowOpen (const cWindow & a_Window) override; + virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + + virtual AString GetAuthServerID(void) override { return m_AuthServerID; } + +protected: + + /// Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed + class cPacketizer + { + public: + cPacketizer(cProtocol172 & a_Protocol, UInt32 a_PacketType) : + m_Protocol(a_Protocol), + m_Out(a_Protocol.m_OutPacketBuffer), + m_Lock(a_Protocol.m_CSPacket) + { + m_Out.WriteVarInt(a_PacketType); + } + + ~cPacketizer(); + + void WriteBool(bool a_Value) + { + m_Out.WriteBool(a_Value); + } + + void WriteByte(Byte a_Value) + { + m_Out.WriteByte(a_Value); + } + + void WriteChar(char a_Value) + { + m_Out.WriteChar(a_Value); + } + + void WriteShort(short a_Value) + { + m_Out.WriteBEShort(a_Value); + } + + void WriteInt(int a_Value) + { + m_Out.WriteBEInt(a_Value); + } + + void WriteInt64(Int64 a_Value) + { + m_Out.WriteBEInt64(a_Value); + } + + void WriteFloat(float a_Value) + { + m_Out.WriteBEFloat(a_Value); + } + + void WriteDouble(double a_Value) + { + m_Out.WriteBEDouble(a_Value); + } + + void WriteVarInt(UInt32 a_Value) + { + m_Out.WriteVarInt(a_Value); + } + + void WriteString(const AString & a_Value) + { + m_Out.WriteVarUTF8String(a_Value); + } + + void WriteBuf(const char * a_Data, int a_Size) + { + m_Out.Write(a_Data, a_Size); + } + + void WriteItem(const cItem & a_Item); + void WriteByteAngle(double a_Angle); // Writes the specified angle using a single byte + void WriteFPInt(double a_Value); // Writes the double value as a 27:5 fixed-point integer + void WriteEntityMetadata(const cEntity & a_Entity); // Writes the metadata for the specified entity, not including the terminating 0x7f + void WriteMobMetadata(const cMonster & a_Mob); // Writes the mob-specific metadata for the specified mob + void WriteEntityProperties(const cEntity & a_Entity); // Writes the entity properties for the specified entity, including the Count field + + protected: + cProtocol172 & m_Protocol; + cByteBuffer & m_Out; + cCSLock m_Lock; + } ; + + AString m_ServerAddress; + + UInt16 m_ServerPort; + + AString m_AuthServerID; + + /// State of the protocol. 1 = status, 2 = login, 3 = game + UInt32 m_State; + + /// Buffer for the received data + cByteBuffer m_ReceivedData; + + /// Buffer for composing the outgoing packets, through cPacketizer + cByteBuffer m_OutPacketBuffer; + + /// Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) + cByteBuffer m_OutPacketLenBuffer; + + bool m_IsEncrypted; + CryptoPP::CFB_Mode::Decryption m_Decryptor; + CryptoPP::CFB_Mode::Encryption m_Encryptor; + + + /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets + void AddReceivedData(const char * a_Data, int a_Size); + + /// Reads and handles the packet. The packet length and type have already been read. + void HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes); + + // Packet handlers while in the Status state (m_State == 1): + void HandlePacketStatusPing (UInt32 a_RemainingBytes); + void HandlePacketStatusRequest(UInt32 a_RemainingBytes); + + // Packet handlers while in the Login state (m_State == 2): + void HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes); + void HandlePacketLoginStart (UInt32 a_RemainingBytes); + + // Packet handlers while in the Game state (m_State == 3): + void HandlePacketAnimation (UInt32 a_RemainingBytes); + void HandlePacketBlockDig (UInt32 a_RemainingBytes); + void HandlePacketBlockPlace (UInt32 a_RemainingBytes); + void HandlePacketChatMessage (UInt32 a_RemainingBytes); + void HandlePacketClientSettings (UInt32 a_RemainingBytes); + void HandlePacketClientStatus (UInt32 a_RemainingBytes); + void HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes); + void HandlePacketEntityAction (UInt32 a_RemainingBytes); + void HandlePacketKeepAlive (UInt32 a_RemainingBytes); + void HandlePacketPlayer (UInt32 a_RemainingBytes); + void HandlePacketPlayerAbilities (UInt32 a_RemainingBytes); + void HandlePacketPlayerLook (UInt32 a_RemainingBytes); + void HandlePacketPlayerPos (UInt32 a_RemainingBytes); + void HandlePacketPlayerPosLook (UInt32 a_RemainingBytes); + void HandlePacketPluginMessage (UInt32 a_RemainingBytes); + void HandlePacketSlotSelect (UInt32 a_RemainingBytes); + void HandlePacketSteerVehicle (UInt32 a_RemainingBytes); + void HandlePacketTabComplete (UInt32 a_RemainingBytes); + void HandlePacketUpdateSign (UInt32 a_RemainingBytes); + void HandlePacketUseEntity (UInt32 a_RemainingBytes); + void HandlePacketWindowClick (UInt32 a_RemainingBytes); + void HandlePacketWindowClose (UInt32 a_RemainingBytes); + + + /// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. + void WritePacket(cByteBuffer & a_Packet); + + /// Sends the data to the client, encrypting them if needed. + virtual void SendData(const char * a_Data, int a_Size) override; + + void SendCompass(const cWorld & a_World); + + /// Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data + bool ReadItem(cItem & a_Item); + + /// Parses item metadata as read by ReadItem(), into the item enchantments. + void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); +} ; + + + + diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp new file mode 100644 index 000000000..9234785b5 --- /dev/null +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -0,0 +1,905 @@ + +// ProtocolRecognizer.cpp + +// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple +// protocol versions and redirects everything to them + +#include "Globals.h" + +#include "ProtocolRecognizer.h" +#include "Protocol125.h" +#include "Protocol132.h" +#include "Protocol14x.h" +#include "Protocol15x.h" +#include "Protocol16x.h" +#include "Protocol17x.h" +#include "../ClientHandle.h" +#include "../Root.h" +#include "../Server.h" +#include "../World.h" +#include "../ChatColor.h" + + + + + +cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) : + super(a_Client), + m_Protocol(NULL), + m_Buffer(512) +{ +} + + + + + +cProtocolRecognizer::~cProtocolRecognizer() +{ + delete m_Protocol; +} + + + + + +AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) +{ + switch (a_ProtocolVersion) + { + case PROTO_VERSION_1_2_5: return "1.2.5"; + case PROTO_VERSION_1_3_2: return "1.3.2"; + case PROTO_VERSION_1_4_2: return "1.4.2"; + case PROTO_VERSION_1_4_4: return "1.4.4"; + case PROTO_VERSION_1_4_6: return "1.4.6"; + case PROTO_VERSION_1_5_0: return "1.5"; + case PROTO_VERSION_1_5_2: return "1.5.2"; + case PROTO_VERSION_1_6_1: return "1.6.1"; + case PROTO_VERSION_1_6_2: return "1.6.2"; + case PROTO_VERSION_1_6_3: return "1.6.3"; + case PROTO_VERSION_1_6_4: return "1.6.4"; + case PROTO_VERSION_1_7_2: return "1.7.2"; + } + ASSERT(!"Unknown protocol version"); + return Printf("Unknown protocol (%d)", a_ProtocolVersion); +} + + + + + +void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size) +{ + if (m_Protocol == NULL) + { + if (!m_Buffer.Write(a_Data, a_Size)) + { + m_Client->Kick("Unsupported protocol version"); + return; + } + + if (!TryRecognizeProtocol()) + { + return; + } + + // The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing: + AString Dump; + m_Buffer.ResetRead(); + m_Buffer.ReadAll(Dump); + m_Protocol->DataReceived(Dump.data(), Dump.size()); + } + else + { + m_Protocol->DataReceived(a_Data, a_Size); + } +} + + + + + +void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendAttachEntity(a_Entity, a_Vehicle); +} + + + + + +void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); +} + + + + + +void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage); +} + + + + + +void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); +} + + + + + +void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); +} + + + + + +void cProtocolRecognizer::SendChat(const AString & a_Message) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChat(a_Message); +} + + + + + +void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); +} + + + + + +void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendCollectPickup(a_Pickup, a_Player); +} + + + + + +void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendDestroyEntity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendDisconnect(const AString & a_Reason) +{ + if (m_Protocol != NULL) + { + m_Protocol->SendDisconnect(a_Reason); + } + else + { + // This is used when the client sends a server-ping, respond with the default packet: + WriteByte ((char)0xff); // PACKET_DISCONNECT + WriteString(a_Reason); + } +} + + + + +void cProtocolRecognizer::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); +} + + + + + +void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityHeadLook(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityLook(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityMetadata(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityProperties(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityStatus(a_Entity, a_Status); +} + + + + + +void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityVelocity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion); +} + + + + + +void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendGameMode(a_GameMode); +} + + + + + +void cProtocolRecognizer::SendHealth(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendHealth(); +} + + + + + +void cProtocolRecognizer::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value); +} + + + + + +void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); +} + + + + + +void cProtocolRecognizer::SendKeepAlive(int a_PingID) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendKeepAlive(a_PingID); +} + + + + + +void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendLogin(a_Player, a_World); +} + + + + + +void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPickupSpawn(a_Pickup); +} + + + + + +void cProtocolRecognizer::SendPlayerAbilities(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerAbilities(); +} + + + + + +void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerAnimation(a_Player, a_Animation); +} + + + + + +void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); +} + + + + + +void cProtocolRecognizer::SendPlayerMaxSpeed(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerMaxSpeed(); +} + + + + + +void cProtocolRecognizer::SendPlayerMoveLook(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerMoveLook(); +} + + + + + +void cProtocolRecognizer::SendPlayerPosition(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerPosition(); +} + + + + + +void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerSpawn(a_Player); +} + + + + + +void cProtocolRecognizer::SendRespawn(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendRespawn(); +} + + + + + +void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); +} + + + + + +void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); +} + + + + + +void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnFallingBlock(a_FallingBlock); +} + + + + + +void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnMob(a_Mob); +} + + + + + +void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch); +} + + + + + +void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType); +} + + + + + +void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTabCompletionResults(a_Results); +} + + + + + +void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTeleportEntity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay); +} + + + + + +void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); +} + + + + + +void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocolRecognizer::SendWeather(eWeather a_Weather) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWeather(a_Weather); +} + + + + + +void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWholeInventory(a_Window); +} + + + + + +void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowClose(a_Window); +} + + + + + +void cProtocolRecognizer::SendWindowOpen(const cWindow & a_Window) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowOpen(a_Window); +} + + + + + +AString cProtocolRecognizer::GetAuthServerID(void) +{ + ASSERT(m_Protocol != NULL); + return m_Protocol->GetAuthServerID(); +} + + + + + +void cProtocolRecognizer::SendData(const char * a_Data, int a_Size) +{ + // This is used only when handling the server ping + m_Client->SendData(a_Data, a_Size); +} + + + + + +bool cProtocolRecognizer::TryRecognizeProtocol(void) +{ + // NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and + // MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro + + // The first packet should be a Handshake, 0x02: + unsigned char PacketType; + if (!m_Buffer.ReadByte(PacketType)) + { + return false; + } + switch (PacketType) + { + case 0x02: return TryRecognizeLengthlessProtocol(); // Handshake, continue recognizing + case 0xfe: + { + // This may be either a packet length or the length-less Ping packet + Byte NextByte; + if (!m_Buffer.ReadByte(NextByte)) + { + // Not enough data for either protocol + // This could actually happen with the 1.2 / 1.3 client, but their support is fading out anyway + return false; + } + if (NextByte != 0x01) + { + // This is definitely NOT a length-less Ping packet, handle as lengthed protocol: + break; + } + if (!m_Buffer.ReadByte(NextByte)) + { + // There is no more data. Although this *could* mean TCP fragmentation, it is highly unlikely + // and rather this is a 1.4 client sending a regular Ping packet (without the following Plugin message) + SendLengthlessServerPing(); + return false; + } + if (NextByte == 0xfa) + { + // Definitely a length-less Ping followed by a Plugin message + SendLengthlessServerPing(); + return false; + } + // Definitely a lengthed Initial handshake, handle below: + break; + } + } // switch (PacketType) + + // This must be a lengthed protocol, try if it has the entire initial handshake packet: + m_Buffer.ResetRead(); + UInt32 PacketLen; + UInt32 ReadSoFar = m_Buffer.GetReadableSpace(); + if (!m_Buffer.ReadVarInt(PacketLen)) + { + // Not enough bytes for the packet length, keep waiting + return false; + } + ReadSoFar -= m_Buffer.GetReadableSpace(); + if (!m_Buffer.CanReadBytes(PacketLen)) + { + // Not enough bytes for the packet, keep waiting + return false; + } + return TryRecognizeLengthedProtocol(PacketLen - ReadSoFar); +} + + + + + +bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void) +{ + // The comm started with 0x02, which is a Handshake packet in the length-less protocol family + // 1.3.2 starts with 0x02 0x39 + // 1.2.5 starts with 0x02 and name is expected to less than 0x3900 long :) + char ch; + if (!m_Buffer.ReadChar(ch)) + { + return false; + } + switch (ch) + { + case PROTO_VERSION_1_3_2: + { + m_Protocol = new cProtocol132(m_Client); + return true; + } + case PROTO_VERSION_1_4_2: + case PROTO_VERSION_1_4_4: + { + m_Protocol = new cProtocol142(m_Client); + return true; + } + case PROTO_VERSION_1_4_6: + { + m_Protocol = new cProtocol146(m_Client); + return true; + } + case PROTO_VERSION_1_5_0: + case PROTO_VERSION_1_5_2: + { + m_Protocol = new cProtocol150(m_Client); + return true; + } + case PROTO_VERSION_1_6_1: + { + m_Protocol = new cProtocol161(m_Client); + return true; + } + case PROTO_VERSION_1_6_2: + case PROTO_VERSION_1_6_3: + case PROTO_VERSION_1_6_4: + { + m_Protocol = new cProtocol162(m_Client); + return true; + } + } + m_Protocol = new cProtocol125(m_Client); + return true; +} + + + + + +bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining) +{ + UInt32 PacketType; + UInt32 NumBytesRead = m_Buffer.GetReadableSpace(); + if (!m_Buffer.ReadVarInt(PacketType)) + { + return false; + } + if (PacketType != 0x00) + { + // Not an initial handshake packet, we don't know how to talk to them + LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, initial packet %u)", + m_Client->GetIPString().c_str(), PacketType + ); + m_Client->Kick("Unsupported protocol version"); + return false; + } + UInt32 ProtocolVersion; + if (!m_Buffer.ReadVarInt(ProtocolVersion)) + { + return false; + } + NumBytesRead -= m_Buffer.GetReadableSpace(); + switch (ProtocolVersion) + { + case PROTO_VERSION_1_7_2: + { + AString ServerAddress; + short ServerPort; + UInt32 NextState; + m_Buffer.ReadVarUTF8String(ServerAddress); + m_Buffer.ReadBEShort(ServerPort); + m_Buffer.ReadVarInt(NextState); + m_Buffer.CommitRead(); + m_Protocol = new cProtocol172(m_Client, ServerAddress, ServerPort, NextState); + return true; + } + } + LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u)", + m_Client->GetIPString().c_str(), ProtocolVersion + ); + m_Client->Kick("Unsupported protocol version"); + return false; +} + + + + + +void cProtocolRecognizer::SendLengthlessServerPing(void) +{ + AString Reply; + switch (cRoot::Get()->GetPrimaryServerVersion()) + { + case PROTO_VERSION_1_2_5: + case PROTO_VERSION_1_3_2: + { + // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29 + Printf(Reply, "%s%s%i%s%i", + cRoot::Get()->GetServer()->GetDescription().c_str(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetServer()->GetNumPlayers(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetServer()->GetMaxPlayers() + ); + break; + } + + case PROTO_VERSION_1_4_2: + case PROTO_VERSION_1_4_4: + case PROTO_VERSION_1_4_6: + case PROTO_VERSION_1_5_0: + case PROTO_VERSION_1_5_2: + case PROTO_VERSION_1_6_1: + case PROTO_VERSION_1_6_2: + case PROTO_VERSION_1_6_3: + case PROTO_VERSION_1_6_4: + { + // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff. + // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29 + // _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit. + // Who cares? We're disconnecting anyway. + m_Buffer.ResetRead(); + if (m_Buffer.CanReadBytes(2)) + { + byte val; + m_Buffer.ReadByte(val); // Packet type - Serverlist ping + m_Buffer.ReadByte(val); // 0x01 magic value + ASSERT(val == 0x01); + } + + // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100 + AString NumPlayers; + Printf(NumPlayers, "%d", cRoot::Get()->GetServer()->GetNumPlayers()); + AString MaxPlayers; + Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers()); + + AString ProtocolVersionNum; + Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion()); + AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion())); + + // Cannot use Printf() because of in-string NUL bytes. + Reply = cChatColor::Delimiter; + Reply.append("1"); + Reply.push_back(0); + Reply.append(ProtocolVersionNum); + Reply.push_back(0); + Reply.append(ProtocolVersionTxt); + Reply.push_back(0); + Reply.append(cRoot::Get()->GetServer()->GetDescription()); + Reply.push_back(0); + Reply.append(NumPlayers); + Reply.push_back(0); + Reply.append(MaxPlayers); + break; + } + } // switch (m_PrimaryServerVersion) + m_Client->Kick(Reply); +} + + + + diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h new file mode 100644 index 000000000..c085e2cd8 --- /dev/null +++ b/src/Protocol/ProtocolRecognizer.h @@ -0,0 +1,152 @@ + +// ProtocolRecognizer.h + +// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple +// protocol versions and redirects everything to them + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" + + + + + +// Adjust these if a new protocol is added or an old one is removed: +#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2" +#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4" + + + + + +class cProtocolRecognizer : + public cProtocol +{ + typedef cProtocol super; + +public: + enum + { + PROTO_VERSION_1_2_5 = 29, + PROTO_VERSION_1_3_2 = 39, + PROTO_VERSION_1_4_2 = 47, + PROTO_VERSION_1_4_4 = 49, + PROTO_VERSION_1_4_6 = 51, + PROTO_VERSION_1_5_0 = 60, + PROTO_VERSION_1_5_2 = 61, + PROTO_VERSION_1_6_1 = 73, + PROTO_VERSION_1_6_2 = 74, + PROTO_VERSION_1_6_3 = 77, + PROTO_VERSION_1_6_4 = 78, + + PROTO_VERSION_NEXT, + PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion + + // These will be kept "under" the next / latest, because the next and latest are only needed for previous protocols + PROTO_VERSION_1_7_2 = 4, + } ; + + cProtocolRecognizer(cClientHandle * a_Client); + virtual ~cProtocolRecognizer(); + + /// Translates protocol version number into protocol version text: 49 -> "1.4.4" + static AString GetVersionTextFromInt(int a_ProtocolVersion); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityHeadLook (const cEntity & a_Entity) override; + virtual void SendEntityLook (const cEntity & a_Entity) override; + virtual void SendEntityMetadata (const cEntity & a_Entity) override; + virtual void SendEntityProperties (const cEntity & a_Entity) override; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntityVelocity (const cEntity & a_Entity) override; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAbilities (void) override; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMaxSpeed (void) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; + virtual void SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (const cWindow & a_Window) override; + virtual void SendWindowOpen (const cWindow & a_Window) override; + virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + + virtual AString GetAuthServerID(void) override; + + virtual void SendData(const char * a_Data, int a_Size) override; + +protected: + cProtocol * m_Protocol; //< The recognized protocol + cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol + + /// Tries to recognize protocol based on m_Buffer contents; returns true if recognized + bool TryRecognizeProtocol(void); + + /** Tries to recognize a protocol in the length-less family, based on m_Buffer; returns true if recognized. + Handles protocols before release 1.7, that didn't include packet lengths, and started with a 0x02 handshake packet + Note that length-less server ping is handled directly in TryRecognizeProtocol(), this function is called only + when the 0x02 Handshake packet has been received + */ + bool TryRecognizeLengthlessProtocol(void); + + /** Tries to recognize a protocol in the leghted family (1.7+), based on m_Buffer; returns true if recognized. + The packet length and type have already been read, type is 0 + The number of bytes remaining in the packet is passed as a_PacketLengthRemaining + **/ + bool TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining); + + /** Called when the recognizer gets a length-less protocol's server ping packet + Responds with server stats and destroys the client. + */ + void SendLengthlessServerPing(void); +} ; + + + + + diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp new file mode 100644 index 000000000..93f2ccdd3 --- /dev/null +++ b/src/RCONServer.cpp @@ -0,0 +1,332 @@ + +// RCONServer.cpp + +// Implements the cRCONServer class representing the RCON server + +#include "Globals.h" +#include "../iniFile/iniFile.h" +#include "RCONServer.h" +#include "Server.h" +#include "Root.h" +#include "CommandOutput.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +enum +{ + // Client -> Server: + RCON_PACKET_COMMAND = 2, + RCON_PACKET_LOGIN = 3, + + // Server -> Client: + RCON_PACKET_RESPONSE = 2, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONCommandOutput: + +class cRCONCommandOutput : + public cCommandOutputCallback +{ +public: + cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) : + m_Connection(a_Connection), + m_RequestID(a_RequestID) + { + } + + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override + { + m_Buffer.append(a_Text); + } + + virtual void Finished(void) override + { + m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str()); + delete this; + } + +protected: + cRCONServer::cConnection & m_Connection; + int m_RequestID; + AString m_Buffer; +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONServer: + +cRCONServer::cRCONServer(cServer & a_Server) : + m_Server(a_Server), + m_ListenThread4(*this, cSocket::IPv4, "RCON IPv4"), + m_ListenThread6(*this, cSocket::IPv6, "RCON IPv6") +{ +} + + + + + +cRCONServer::~cRCONServer() +{ + m_ListenThread4.Stop(); + m_ListenThread6.Stop(); +} + + + + + +void cRCONServer::Initialize(cIniFile & a_IniFile) +{ + if (!a_IniFile.GetValueSetB("RCON", "Enabled", false)) + { + return; + } + + // Read the password, don't allow an empty one: + m_Password = a_IniFile.GetValueSet("RCON", "Password", ""); + if (m_Password.empty()) + { + LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled."); + return; + } + + // Read and initialize both IPv4 and IPv6 ports for RCON + bool HasAnyPorts = false; + AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575"); + if (m_ListenThread4.Initialize(Ports4)) + { + HasAnyPorts = true; + m_ListenThread4.Start(); + } + AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575"); + if (m_ListenThread6.Initialize(Ports6)) + { + HasAnyPorts = true; + m_ListenThread6.Start(); + } + if (!HasAnyPorts) + { + LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled."); + return; + } +} + + + + + +void cRCONServer::OnConnectionAccepted(cSocket & a_Socket) +{ + if (!a_Socket.IsValid()) + { + return; + } + + LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str()); + + // Create a new cConnection object, it will be deleted when the connection is closed + m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONServer::cConnection: + +cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) : + m_IsAuthenticated(false), + m_RCONServer(a_RCONServer), + m_Socket(a_Socket), + m_IPAddress(a_Socket.GetIPString()) +{ +} + + + + + +void cRCONServer::cConnection::DataReceived(const char * a_Data, int a_Size) +{ + // Append data to the buffer: + m_Buffer.append(a_Data, a_Size); + + // Process the packets in the buffer: + while (m_Buffer.size() >= 14) + { + int Length = IntFromBuffer(m_Buffer.data()); + if (Length > 1500) + { + // Too long, drop the connection + LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.", + Length, m_IPAddress.c_str() + ); + m_RCONServer.m_SocketThreads.RemoveClient(this); + m_Socket.CloseSocket(); + delete this; + return; + } + if (Length > (int)(m_Buffer.size() + 4)) + { + // Incomplete packet yet, wait for more data to come + return; + } + + int RequestID = IntFromBuffer(m_Buffer.data() + 4); + int PacketType = IntFromBuffer(m_Buffer.data() + 8); + if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12)) + { + m_RCONServer.m_SocketThreads.RemoveClient(this); + m_Socket.CloseSocket(); + delete this; + return; + } + m_Buffer.erase(0, Length + 4); + } // while (m_Buffer.size() >= 14) +} + + + + + +void cRCONServer::cConnection::GetOutgoingData(AString & a_Data) +{ + a_Data.assign(m_Outgoing); + m_Outgoing.clear(); +} + + + + + +void cRCONServer::cConnection::SocketClosed(void) +{ + m_RCONServer.m_SocketThreads.RemoveClient(this); + delete this; +} + + + + + +bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) +{ + switch (a_PacketType) + { + case RCON_PACKET_LOGIN: + { + if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0) + { + LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str()); + return false; + } + m_IsAuthenticated = true; + + LOGD("RCON: Client at %s has successfully authenticated", m_IPAddress.c_str()); + + // Send OK response: + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); + return true; + } + + case RCON_PACKET_COMMAND: + { + if (!m_IsAuthenticated) + { + char AuthNeeded[] = "You need to authenticate first!"; + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded); + return false; + } + + AString cmd(a_Payload, a_PayloadLength); + LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str()); + cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID))); + + // Send an empty response: + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); + return true; + } + } + + // Unknown packet type, drop the connection: + LOGWARNING("RCON: Client at %s has sent an unknown packet type %d, dropping connection.", + m_IPAddress.c_str(), a_PacketType + ); + return false; +} + + + + + +/// Reads 4 bytes from a_Buffer and returns the int they represent +int cRCONServer::cConnection::IntFromBuffer(const char * a_Buffer) +{ + return ((unsigned char)a_Buffer[3] << 24) | ((unsigned char)a_Buffer[2] << 16) | ((unsigned char)a_Buffer[1] << 8) | (unsigned char)a_Buffer[0]; +} + + + + + +/// Puts 4 bytes representing the int into the buffer +void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer) +{ + a_Buffer[0] = a_Value & 0xff; + a_Buffer[1] = (a_Value >> 8) & 0xff; + a_Buffer[2] = (a_Value >> 16) & 0xff; + a_Buffer[3] = (a_Value >> 24) & 0xff; +} + + + + + +/// Sends a RCON packet back to the client +void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) +{ + ASSERT((a_PayloadLength == 0) || (a_Payload != NULL)); // Either zero data to send, or a valid payload ptr + + char Buffer[4]; + int Length = a_PayloadLength + 10; + IntToBuffer(Length, Buffer); + m_Outgoing.append(Buffer, 4); + IntToBuffer(a_RequestID, Buffer); + m_Outgoing.append(Buffer, 4); + IntToBuffer(a_PacketType, Buffer); + m_Outgoing.append(Buffer, 4); + if (a_PayloadLength > 0) + { + m_Outgoing.append(a_Payload, a_PayloadLength); + } + m_Outgoing.push_back(0); + m_Outgoing.push_back(0); + m_RCONServer.m_SocketThreads.NotifyWrite(this); +} + + + + diff --git a/src/RCONServer.h b/src/RCONServer.h new file mode 100644 index 000000000..0e89800a2 --- /dev/null +++ b/src/RCONServer.h @@ -0,0 +1,109 @@ + +// RCONServer.h + +// Declares the cRCONServer class representing the RCON server + + + + + +#pragma once + +#include "OSSupport/SocketThreads.h" +#include "OSSupport/ListenThread.h" + + + + + +// fwd: +class cServer; +class cIniFile; + + + + + +class cRCONServer : + public cListenThread::cCallback +{ +public: + cRCONServer(cServer & a_Server); + ~cRCONServer(); + + void Initialize(cIniFile & a_IniFile); + +protected: + friend class cRCONCommandOutput; + + class cConnection : + public cSocketThreads::cCallback + { + public: + cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket); + + protected: + friend class cRCONCommandOutput; + + /// Set to true if the client has successfully authenticated + bool m_IsAuthenticated; + + /// Buffer for the incoming data + AString m_Buffer; + + /// Buffer for the outgoing data + AString m_Outgoing; + + /// Server that owns this connection and processes requests + cRCONServer & m_RCONServer; + + /// The socket belonging to the client + cSocket & m_Socket; + + /// Address of the client + AString m_IPAddress; + + + // cSocketThreads::cCallback overrides: + virtual void DataReceived(const char * a_Data, int a_Size) override; + virtual void GetOutgoingData(AString & a_Data) override; + virtual void SocketClosed(void) override; + + /// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped + bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); + + /// Reads 4 bytes from a_Buffer and returns the int they represent + int IntFromBuffer(const char * a_Buffer); + + /// Puts 4 bytes representing the int into the buffer + void IntToBuffer(int a_Value, char * a_Buffer); + + /// Sends a RCON packet back to the client + void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); + } ; + + + /// The server object that will process the commands received + cServer & m_Server; + + /// The thread(s) that take care of all the traffic on the RCON ports + cSocketThreads m_SocketThreads; + + /// The thread for accepting IPv4 RCON connections + cListenThread m_ListenThread4; + + /// The thread for accepting IPv6 RCON connections + cListenThread m_ListenThread6; + + /// Password for authentication + AString m_Password; + + + // cListenThread::cCallback overrides: + virtual void OnConnectionAccepted(cSocket & a_Socket) override; +} ; + + + + + diff --git a/src/ReferenceManager.cpp b/src/ReferenceManager.cpp new file mode 100644 index 000000000..6a9ed0e43 --- /dev/null +++ b/src/ReferenceManager.cpp @@ -0,0 +1,43 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ReferenceManager.h" +#include "Entities/Entity.h" + + + + + +cReferenceManager::cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ) + : m_Type( a_Type ) +{ +} + +cReferenceManager::~cReferenceManager() +{ + if( m_Type == RFMNGR_REFERENCERS ) + { + for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) + { + *(*itr) = 0; // Set referenced pointer to 0 + } + } + else + { + for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) + { + cEntity* Ptr = (*(*itr)); + if( Ptr ) Ptr->Dereference( *(*itr) ); + } + } +} + +void cReferenceManager::AddReference( cEntity*& a_EntityPtr ) +{ + m_References.push_back( &a_EntityPtr ); +} + +void cReferenceManager::Dereference( cEntity*& a_EntityPtr ) +{ + m_References.remove( &a_EntityPtr ); +} \ No newline at end of file diff --git a/src/ReferenceManager.h b/src/ReferenceManager.h new file mode 100644 index 000000000..bcd451f72 --- /dev/null +++ b/src/ReferenceManager.h @@ -0,0 +1,34 @@ + +#pragma once + + + + + +class cEntity; + + + + + +class cReferenceManager +{ +public: + enum ENUM_REFERENCE_MANAGER_TYPE + { + RFMNGR_REFERENCERS, + RFMNGR_REFERENCES, + }; + cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ); + ~cReferenceManager(); + + void AddReference( cEntity*& a_EntityPtr ); + void Dereference( cEntity*& a_EntityPtr ); +private: + ENUM_REFERENCE_MANAGER_TYPE m_Type; + std::list< cEntity** > m_References; +}; + + + + diff --git a/src/Root.cpp b/src/Root.cpp new file mode 100644 index 000000000..be5a0553c --- /dev/null +++ b/src/Root.cpp @@ -0,0 +1,744 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Root.h" +#include "Server.h" +#include "World.h" +#include "WebAdmin.h" +#include "FurnaceRecipe.h" +#include "GroupManager.h" +#include "CraftingRecipes.h" +#include "PluginManager.h" +#include "MonsterConfig.h" +#include "Entities/Player.h" +#include "Blocks/BlockHandler.h" +#include "Items/ItemHandler.h" +#include "Chunk.h" +#include "Protocol/ProtocolRecognizer.h" // for protocol version constants +#include "CommandOutput.h" +#include "DeadlockDetect.h" +#include "OSSupport/Timer.h" + +#include "../iniFile/iniFile.h" + +#ifdef _WIN32 + #include +#elif defined(__linux__) + #include +#elif defined(__APPLE__) + #include +#endif + + + + + +cRoot* cRoot::s_Root = NULL; + + + + + +cRoot::cRoot() + : m_Server( NULL ) + , m_MonsterConfig( NULL ) + , m_GroupManager( NULL ) + , m_CraftingRecipes(NULL) + , m_FurnaceRecipe( NULL ) + , m_WebAdmin( NULL ) + , m_PluginManager( NULL ) + , m_Log( NULL ) + , m_bStop( false ) + , m_bRestart( false ) + , m_InputThread( NULL ) + , m_pDefaultWorld( NULL ) +{ + s_Root = this; +} + + + + + +cRoot::~cRoot() +{ + s_Root = 0; +} + + + + + +void cRoot::InputThread(void * a_Params) +{ + cRoot & self = *(cRoot*)a_Params; + + cLogCommandOutputCallback Output; + + while (!(self.m_bStop || self.m_bRestart) && std::cin.good()) + { + std::string Command; + std::getline(std::cin, Command); + if (!Command.empty()) + { + self.ExecuteConsoleCommand(Command, Output); + } + } + + if (!(self.m_bStop || self.m_bRestart)) + { + // We have come here because the std::cin has received an EOF and the server is still running; stop the server: + self.m_bStop = true; + } +} + + + + + +void cRoot::Start(void) +{ + cDeadlockDetect dd; + delete m_Log; + m_Log = new cMCLogger(); + + m_bStop = false; + while (!m_bStop) + { + cTimer Time; + long long mseconds = Time.GetNowTime(); + + m_bRestart = false; + + LoadGlobalSettings(); + + LOG("Creating new server instance..."); + m_Server = new cServer(); + + LOG("Reading server config..."); + cIniFile IniFile; + if (!IniFile.ReadFile("settings.ini")) + { + LOGWARN("Regenerating settings.ini, all settings will be reset"); + IniFile.AddHeaderComment(" This is the main server configuration"); + IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); + IniFile.AddHeaderComment(" See: http://www.mc-server.org/wiki/doku.php?id=configure:settings.ini for further configuration help"); + } + + m_PrimaryServerVersion = IniFile.GetValueI("Server", "PrimaryServerVersion", 0); + if (m_PrimaryServerVersion == 0) + { + m_PrimaryServerVersion = cProtocolRecognizer::PROTO_VERSION_LATEST; + } + else + { + // Make a note in the log that the primary server version is explicitly set in the ini file + LOGINFO("Primary server version set explicitly to %d.", m_PrimaryServerVersion); + } + + LOG("Starting server..."); + if (!m_Server->InitServer(IniFile)) + { + LOGERROR("Failure starting server, aborting..."); + return; + } + + m_WebAdmin = new cWebAdmin(); + m_WebAdmin->Init(); + + LOGD("Loading settings..."); + m_GroupManager = new cGroupManager(); + m_CraftingRecipes = new cCraftingRecipes; + m_FurnaceRecipe = new cFurnaceRecipe(); + + LOGD("Loading worlds..."); + LoadWorlds(IniFile); + + LOGD("Loading plugin manager..."); + m_PluginManager = new cPluginManager(); + m_PluginManager->ReloadPluginsNow(IniFile); + + LOGD("Loading MonsterConfig..."); + m_MonsterConfig = new cMonsterConfig; + + // This sets stuff in motion + LOGD("Starting Authenticator..."); + m_Authenticator.Start(IniFile); + + IniFile.WriteFile("settings.ini"); + + LOGD("Starting worlds..."); + StartWorlds(); + + LOGD("Starting deadlock detector..."); + dd.Start(); + + LOGD("Finalising startup..."); + m_Server->Start(); + + m_WebAdmin->Start(); + + #if !defined(ANDROID_NDK) + LOGD("Starting InputThread..."); + m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" ); + m_InputThread->Start( false ); // We should NOT wait? Otherwise we can´t stop the server from other threads than the input thread + #endif + + long long finishmseconds = Time.GetNowTime(); + finishmseconds -= mseconds; + + LOG("Startup complete, took %i ms!", finishmseconds); + + while (!m_bStop && !m_bRestart) // These are modified by external threads + { + cSleep::MilliSleep(1000); + } + + #if !defined(ANDROID_NDK) + delete m_InputThread; m_InputThread = NULL; + #endif + + // Deallocate stuffs + LOG("Shutting down server..."); + m_Server->Shutdown(); + + LOGD("Shutting down deadlock detector..."); + dd.Stop(); + + LOGD("Stopping world threads..."); + StopWorlds(); + + LOGD("Stopping authenticator..."); + m_Authenticator.Stop(); + + LOGD("Freeing MonsterConfig..."); + delete m_MonsterConfig; m_MonsterConfig = NULL; + delete m_WebAdmin; m_WebAdmin = NULL; + LOGD("Unloading recipes..."); + delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; + delete m_CraftingRecipes; m_CraftingRecipes = NULL; + LOGD("Forgetting groups..."); + delete m_GroupManager; m_GroupManager = 0; + LOGD("Unloading worlds..."); + UnloadWorlds(); + + LOGD("Stopping plugin manager..."); + delete m_PluginManager; m_PluginManager = NULL; + + cItemHandler::Deinit(); + cBlockHandler::Deinit(); + + LOG("Cleaning up..."); + //delete HeartBeat; HeartBeat = 0; + delete m_Server; m_Server = 0; + LOG("Shutdown successful!"); + } + + delete m_Log; m_Log = 0; +} + + + + + +void cRoot::LoadGlobalSettings() +{ + // Nothing needed yet +} + + + + + +void cRoot::LoadWorlds(cIniFile & IniFile) +{ + // First get the default world + AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world"); + m_pDefaultWorld = new cWorld( DefaultWorldName.c_str() ); + m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld; + + // Then load the other worlds + unsigned int KeyNum = IniFile.FindKey("Worlds"); + unsigned int NumWorlds = IniFile.GetNumValues( KeyNum ); + if (NumWorlds <= 0) + { + return; + } + + bool FoundAdditionalWorlds = false; + for (unsigned int i = 0; i < NumWorlds; i++) + { + AString ValueName = IniFile.GetValueName(KeyNum, i ); + if (ValueName.compare("World") != 0) + { + continue; + } + AString WorldName = IniFile.GetValue(KeyNum, i ); + if (WorldName.empty()) + { + continue; + } + FoundAdditionalWorlds = true; + cWorld* NewWorld = new cWorld( WorldName.c_str() ); + m_WorldsByName[ WorldName ] = NewWorld; + } // for i - Worlds + + if (!FoundAdditionalWorlds) + { + if (IniFile.GetKeyComment("Worlds", 0) != " World=secondworld") + { + IniFile.AddKeyComment("Worlds", " World=secondworld"); + } + } +} + + + + + +void cRoot::StartWorlds(void) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) + { + itr->second->Start(); + itr->second->InitializeSpawn(); + } +} + + + + + +void cRoot::StopWorlds(void) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) + { + itr->second->Stop(); + } +} + + + + + +void cRoot::UnloadWorlds(void) +{ + m_pDefaultWorld = NULL; + for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + { + delete itr->second; + } + m_WorldsByName.clear(); +} + + + + + +cWorld* cRoot::GetDefaultWorld() +{ + return m_pDefaultWorld; +} + + + + + +cWorld* cRoot::GetWorld( const AString & a_WorldName ) +{ + WorldMap::iterator itr = m_WorldsByName.find( a_WorldName ); + if( itr != m_WorldsByName.end() ) + return itr->second; + return 0; +} + + + + + +bool cRoot::ForEachWorld(cWorldListCallback & a_Callback) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(itr->second)) + { + return false; + } + } + return true; +} + + + + + +void cRoot::TickCommands(void) +{ + // Execute any pending commands: + cCommandQueue PendingCommands; + { + cCSLock Lock(m_CSPendingCommands); + std::swap(PendingCommands, m_PendingCommands); + } + for (cCommandQueue::iterator itr = PendingCommands.begin(), end = PendingCommands.end(); itr != end; ++itr) + { + ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output)); + } +} + + + + + +void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) +{ + // Some commands are built-in: + if (a_Cmd == "stop") + { + m_bStop = true; + } + else if (a_Cmd == "restart") + { + m_bRestart = true; + } + + // Put the command into a queue (Alleviates FS #363): + cCSLock Lock(m_CSPendingCommands); + m_PendingCommands.push_back(cCommand(a_Cmd, &a_Output)); +} + + + + + +void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd) +{ + // Some commands are built-in: + if (a_Cmd == "stop") + { + m_bStop = true; + } + else if (a_Cmd == "restart") + { + m_bRestart = true; + } + + // Put the command into a queue (Alleviates FS #363): + cCSLock Lock(m_CSPendingCommands); + m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback)); +} + + + + + +void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) +{ + // Some commands are built-in: + if (a_Cmd == "stop") + { + m_bStop = true; + } + else if (a_Cmd == "restart") + { + m_bRestart = true; + } + + LOG("Executing console command: \"%s\"", a_Cmd.c_str()); + m_Server->ExecuteConsoleCommand(a_Cmd, a_Output); +} + + + + + +void cRoot::KickUser(int a_ClientID, const AString & a_Reason) +{ + m_Server->KickUser(a_ClientID, a_Reason); +} + + + + + +void cRoot::AuthenticateUser(int a_ClientID) +{ + m_Server->AuthenticateUser(a_ClientID); +} + + + + + +int cRoot::GetTotalChunkCount(void) +{ + int res = 0; + for ( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + { + res += itr->second->GetNumChunks(); + } + return res; +} + + + + + +void cRoot::SaveAllChunks(void) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) + { + itr->second->QueueSaveAllChunks(); + } +} + + + + + +void cRoot::BroadcastChat(const AString & a_Message) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) + { + itr->second->BroadcastChat(a_Message); + } // for itr - m_WorldsByName[] +} + + + + + +bool cRoot::ForEachPlayer(cPlayerListCallback & a_Callback) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) + { + ++itr2; + if (!itr->second->ForEachPlayer(a_Callback)) + { + return false; + } + } + return true; +} + + + + + +bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + class cCallback : public cPlayerListCallback + { + unsigned int BestRating; + unsigned int NameLength; + const AString PlayerName; + + cPlayerListCallback & m_Callback; + virtual bool Item (cPlayer * a_pPlayer) + { + unsigned int Rating = RateCompareString (PlayerName, a_pPlayer->GetName()); + if (Rating > 0 && Rating >= BestRating) + { + BestMatch = a_pPlayer; + if( Rating > BestRating ) NumMatches = 0; + BestRating = Rating; + ++NumMatches; + } + if (Rating == NameLength) // Perfect match + { + return true; + } + return false; + } + + public: + cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback) + : m_Callback( a_Callback ) + , BestMatch( NULL ) + , BestRating( 0 ) + , NumMatches( 0 ) + , NameLength( a_PlayerName.length() ) + , PlayerName( a_PlayerName ) + {} + + cPlayer * BestMatch; + unsigned int NumMatches; + } Callback (a_PlayerName, a_Callback); + ForEachPlayer( Callback ); + + if (Callback.NumMatches == 1) + { + return a_Callback.Item (Callback.BestMatch); + } + return false; +} + + + + + +AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion) +{ + return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion); +} + + + + + +int cRoot::GetVirtualRAMUsage(void) +{ + #ifdef _WIN32 + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS *)&pmc, sizeof(pmc))) + { + return (int)(pmc.PrivateUsage / 1024); + } + return -1; + #elif defined(__linux__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + std::ifstream StatFile("/proc/self/status"); + if (!StatFile.good()) + { + return -1; + } + while (StatFile.good()) + { + AString Line; + std::getline(StatFile, Line); + if (strncmp(Line.c_str(), "VmSize:", 7) == 0) + { + int res = atoi(Line.c_str() + 8); + return (res == 0) ? -1 : res; // If parsing failed, return -1 + } + } + return -1; + #elif defined (__APPLE__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + if (KERN_SUCCESS == task_info( + mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&t_info, + &t_info_count + )) + { + return (int)(t_info.virtual_size / 1024); + } + return -1; + #else + LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__); + return -1; + #endif +} + + + + + +int cRoot::GetPhysicalRAMUsage(void) +{ + #ifdef _WIN32 + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + { + return (int)(pmc.WorkingSetSize / 1024); + } + return -1; + #elif defined(__linux__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + std::ifstream StatFile("/proc/self/status"); + if (!StatFile.good()) + { + return -1; + } + while (StatFile.good()) + { + AString Line; + std::getline(StatFile, Line); + if (strncmp(Line.c_str(), "VmRSS:", 7) == 0) + { + int res = atoi(Line.c_str() + 8); + return (res == 0) ? -1 : res; // If parsing failed, return -1 + } + } + return -1; + #elif defined (__APPLE__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + if (KERN_SUCCESS == task_info( + mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&t_info, + &t_info_count + )) + { + return (int)(t_info.resident_size / 1024); + } + return -1; + #else + LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__); + return -1; + #endif +} + + + + + +void cRoot::LogChunkStats(cCommandOutputCallback & a_Output) +{ + int SumNumValid = 0; + int SumNumDirty = 0; + int SumNumInLighting = 0; + int SumNumInGenerator = 0; + int SumMem = 0; + for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) + { + cWorld * World = itr->second; + int NumInGenerator = World->GetGeneratorQueueLength(); + int NumInSaveQueue = World->GetStorageSaveQueueLength(); + int NumInLoadQueue = World->GetStorageLoadQueueLength(); + int NumValid = 0; + int NumDirty = 0; + int NumInLighting = 0; + World->GetChunkStats(NumValid, NumDirty, NumInLighting); + a_Output.Out("World %s:", World->GetName().c_str()); + a_Output.Out(" Num loaded chunks: %d", NumValid); + a_Output.Out(" Num dirty chunks: %d", NumDirty); + a_Output.Out(" Num chunks in lighting queue: %d", NumInLighting); + a_Output.Out(" Num chunks in generator queue: %d", NumInGenerator); + a_Output.Out(" Num chunks in storage load queue: %d", NumInLoadQueue); + a_Output.Out(" Num chunks in storage save queue: %d", NumInSaveQueue); + int Mem = NumValid * sizeof(cChunk); + a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024)); + a_Output.Out(" Per-chunk memory size breakdown:"); + a_Output.Out(" block types: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024); + a_Output.Out(" block metadata: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockNibbles), (sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); + a_Output.Out(" block lighting: %6d bytes (%3d KiB)", 2 * sizeof(cChunkDef::BlockNibbles), (2 * sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); + a_Output.Out(" heightmap: %6d bytes (%3d KiB)", sizeof(cChunkDef::HeightMap), (sizeof(cChunkDef::HeightMap) + 1023) / 1024); + a_Output.Out(" biomemap: %6d bytes (%3d KiB)", sizeof(cChunkDef::BiomeMap), (sizeof(cChunkDef::BiomeMap) + 1023) / 1024); + int Rest = sizeof(cChunk) - sizeof(cChunkDef::BlockTypes) - 3 * sizeof(cChunkDef::BlockNibbles) - sizeof(cChunkDef::HeightMap) - sizeof(cChunkDef::BiomeMap); + a_Output.Out(" other: %6d bytes (%3d KiB)", Rest, (Rest + 1023) / 1024); + SumNumValid += NumValid; + SumNumDirty += NumDirty; + SumNumInLighting += NumInLighting; + SumNumInGenerator += NumInGenerator; + SumMem += Mem; + } + a_Output.Out("Totals:"); + a_Output.Out(" Num loaded chunks: %d", SumNumValid); + a_Output.Out(" Num dirty chunks: %d", SumNumDirty); + a_Output.Out(" Num chunks in lighting queue: %d", SumNumInLighting); + a_Output.Out(" Num chunks in generator queue: %d", SumNumInGenerator); + a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024)); +} + + + + diff --git a/src/Root.h b/src/Root.h new file mode 100644 index 000000000..175084c53 --- /dev/null +++ b/src/Root.h @@ -0,0 +1,186 @@ + +#pragma once + +#include "Authenticator.h" +#include "HTTPServer/HTTPServer.h" + + + + + +// fwd: +class cThread; +class cMonsterConfig; +class cGroupManager; +class cCraftingRecipes; +class cFurnaceRecipe; +class cWebAdmin; +class cPluginManager; +class cServer; +class cWorld; +class cPlayer; +class cCommandOutputCallback ; + +typedef cItemCallback cPlayerListCallback; +typedef cItemCallback cWorldListCallback; + + + + + +/// The root of the object hierarchy +class cRoot // tolua_export +{ // tolua_export +public: + static cRoot * Get() { return s_Root; } // tolua_export + + cRoot(void); + ~cRoot(); + + void Start(void); + + cServer * GetServer(void) { return m_Server; } // tolua_export + cWorld * GetDefaultWorld(void); // tolua_export + cWorld * GetWorld(const AString & a_WorldName); // tolua_export + + /// Calls the callback for each world; returns true if the callback didn't abort (return true) + bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings << + + /// Writes chunkstats, for each world and totals, to the output callback + void LogChunkStats(cCommandOutputCallback & a_Output); + + int GetPrimaryServerVersion(void) const { return m_PrimaryServerVersion; } // tolua_export + void SetPrimaryServerVersion(int a_Version) { m_PrimaryServerVersion = a_Version; } // tolua_export + + cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; } + + cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export + cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export + cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // tolua_export + cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export + cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export + cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } + + /** Queues a console command for execution through the cServer class. + The command will be executed in the tick thread + The command's output will be written to the a_Output callback + "stop" and "restart" commands have special handling. + */ + void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); + + /** Queues a console command for execution through the cServer class. + The command will be executed in the tick thread + The command's output will be sent to console + "stop" and "restart" commands have special handling. + */ + void QueueExecuteConsoleCommand(const AString & a_Cmd); // tolua_export + + /// Executes a console command through the cServer class; does special handling for "stop" and "restart". + void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); + + /// Kicks the user, no matter in what world they are. Used from cAuthenticator + void KickUser(int a_ClientID, const AString & a_Reason); + + /// Called by cAuthenticator to auth the specified user + void AuthenticateUser(int a_ClientID); + + /// Executes commands queued in the command queue + void TickCommands(void); + + /// Returns the number of chunks loaded + int GetTotalChunkCount(void); // tolua_export + + /// Saves all chunks in all worlds + void SaveAllChunks(void); // tolua_export + + /// Sends a chat message to all connected clients (in all worlds) + void BroadcastChat(const AString & a_Message); // tolua_export + + /// Calls the callback for each player in all worlds + bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Finds a player from a partial or complete player name and calls the callback - case-insensitive + bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + // tolua_begin + + /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API + static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); + + /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error + static int GetVirtualRAMUsage(void); + + /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error + static int GetPhysicalRAMUsage(void); + + // tolua_end + +private: + class cCommand + { + public: + cCommand(const AString & a_Command, cCommandOutputCallback * a_Output) : + m_Command(a_Command), + m_Output(a_Output) + { + } + + AString m_Command; + cCommandOutputCallback * m_Output; + } ; + + typedef std::map WorldMap; + typedef std::vector cCommandQueue; + + /// The version of the protocol that is primary for the server (reported in the server list). All versions are still supported. + int m_PrimaryServerVersion; + + cWorld * m_pDefaultWorld; + WorldMap m_WorldsByName; + + cCriticalSection m_CSPendingCommands; + cCommandQueue m_PendingCommands; + + cThread * m_InputThread; + + cServer * m_Server; + cMonsterConfig * m_MonsterConfig; + + cGroupManager * m_GroupManager; + cCraftingRecipes * m_CraftingRecipes; + cFurnaceRecipe * m_FurnaceRecipe; + cWebAdmin * m_WebAdmin; + cPluginManager * m_PluginManager; + cAuthenticator m_Authenticator; + cHTTPServer m_HTTPServer; + + cMCLogger * m_Log; + + bool m_bStop; + bool m_bRestart; + + void LoadGlobalSettings(); + + /// Loads the worlds from settings.ini, creates the worldmap + void LoadWorlds(cIniFile & IniFile); + + /// Starts each world's life + void StartWorlds(void); + + /// Stops each world's threads, so that it's safe to unload them + void StopWorlds(void); + + /// Unloads all worlds from memory + void UnloadWorlds(void); + + /// Does the actual work of executing a command + void DoExecuteConsoleCommand(const AString & a_Cmd); + + static void InputThread(void* a_Params); + + static cRoot* s_Root; +}; // tolua_export + + + + diff --git a/src/SQLite/lsqlite3.c b/src/SQLite/lsqlite3.c new file mode 100644 index 000000000..4c81b5878 --- /dev/null +++ b/src/SQLite/lsqlite3.c @@ -0,0 +1,2175 @@ +/************************************************************************ +* lsqlite3 * +* Copyright (C) 2002-2013 Tiago Dionizio, Doug Currie * +* All rights reserved. * +* Author : Tiago Dionizio * +* Author : Doug Currie * +* Library : lsqlite3 - a SQLite 3 database binding for Lua 5 * +* * +* 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. * +************************************************************************/ +// Slightly modified by _Xoft to compile in MSVC + + + + +// 2013_04_07 _X: Added the following #define-s so that MSVC doesn't complain about non-secure stuff: +#define _CRT_SECURE_NO_WARNINGS +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + + + +#include +#include +#include + +#define LUA_LIB +#include "lua.h" +#include "lauxlib.h" + +#if LUA_VERSION_NUM > 501 +// +// Lua 5.2 +// +#define lua_strlen lua_rawlen +// luaL_typerror always used with arg at ndx == NULL +#define luaL_typerror(L,ndx,str) luaL_error(L,"bad argument %d (%s expected, got nil)",ndx,str) +// luaL_register used once, so below expansion is OK for this case +#define luaL_register(L,name,reg) lua_newtable(L);luaL_setfuncs(L,reg,0) +// luaL_openlib always used with name == NULL +#define luaL_openlib(L,name,reg,nup) luaL_setfuncs(L,reg,nup) +#endif + +#include "sqlite3.h" + +/* compile time features */ +#if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) + #define SQLITE_OMIT_PROGRESS_CALLBACK 0 +#endif +#if !defined(LSQLITE_OMIT_UPDATE_HOOK) + #define LSQLITE_OMIT_UPDATE_HOOK 0 +#endif + +typedef struct sdb sdb; +typedef struct sdb_vm sdb_vm; +typedef struct sdb_func sdb_func; + +/* to use as C user data so i know what function sqlite is calling */ +struct sdb_func { + /* references to associated lua values */ + int fn_step; + int fn_finalize; + int udata; + + sdb *db; + char aggregate; + + sdb_func *next; +}; + +/* information about database */ +struct sdb { + /* associated lua state */ + lua_State *L; + /* sqlite database handle */ + sqlite3 *db; + + /* sql functions stack usage */ + sdb_func *func; /* top SQL function being called */ + + /* references */ + int busy_cb; /* busy callback */ + int busy_udata; + + int progress_cb; /* progress handler */ + int progress_udata; + + int trace_cb; /* trace callback */ + int trace_udata; + +#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK + + int update_hook_cb; /* update_hook callback */ + int update_hook_udata; + + int commit_hook_cb; /* commit_hook callback */ + int commit_hook_udata; + + int rollback_hook_cb; /* rollback_hook callback */ + int rollback_hook_udata; + +#endif +}; + +static const char *sqlite_meta = ":sqlite3"; +static const char *sqlite_vm_meta = ":sqlite3:vm"; +static const char *sqlite_ctx_meta = ":sqlite3:ctx"; +static int sqlite_ctx_meta_ref; + +/* +** ======================================================= +** Database Virtual Machine Operations +** ======================================================= +*/ + +static void vm_push_column(lua_State *L, sqlite3_stmt *vm, int idx) { + switch (sqlite3_column_type(vm, idx)) { + case SQLITE_INTEGER: + { + sqlite_int64 i64 = sqlite3_column_int64(vm, idx); + lua_Number n = (lua_Number)i64; + if (n == i64) + lua_pushnumber(L, n); + else + lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx), sqlite3_column_bytes(vm, idx)); + } + break; + case SQLITE_FLOAT: + lua_pushnumber(L, sqlite3_column_double(vm, idx)); + break; + case SQLITE_TEXT: + lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx), sqlite3_column_bytes(vm, idx)); + break; + case SQLITE_BLOB: + lua_pushlstring(L, sqlite3_column_blob(vm, idx), sqlite3_column_bytes(vm, idx)); + break; + case SQLITE_NULL: + lua_pushnil(L); + break; + default: + lua_pushnil(L); + break; + } +} + +/* virtual machine information */ +struct sdb_vm { + sdb *db; /* associated database handle */ + sqlite3_stmt *vm; /* virtual machine */ + + /* sqlite3_step info */ + int columns; /* number of columns in result */ + char has_values; /* true when step succeeds */ + + char temp; /* temporary vm used in db:rows */ +}; + +/* called with sql text on the lua stack */ +static sdb_vm *newvm(lua_State *L, sdb *db) { + sdb_vm *svm = (sdb_vm*)lua_newuserdata(L, sizeof(sdb_vm)); + + luaL_getmetatable(L, sqlite_vm_meta); + lua_setmetatable(L, -2); /* set metatable */ + + svm->db = db; + svm->columns = 0; + svm->has_values = 0; + svm->vm = NULL; + svm->temp = 0; + + /* add an entry on the database table: svm -> sql text */ + lua_pushlightuserdata(L, db); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(L, svm); + lua_pushvalue(L, -4); /* the sql text */ + lua_rawset(L, -3); + lua_pop(L, 1); + + return svm; +} + +static int cleanupvm(lua_State *L, sdb_vm *svm) { + /* remove entry in database table - no harm if not present in the table */ + lua_pushlightuserdata(L, svm->db); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(L, svm); + lua_pushnil(L); + lua_rawset(L, -3); + lua_pop(L, 1); + + svm->columns = 0; + svm->has_values = 0; + + if (!svm->vm) return 0; + + lua_pushnumber(L, sqlite3_finalize(svm->vm)); + svm->vm = NULL; + return 1; +} + +static int stepvm(lua_State *L, sdb_vm *svm) { + int result; + int loop_limit = 3; + while ( loop_limit-- ) { + result = sqlite3_step(svm->vm); + if ( result==SQLITE_ERROR ) { + result = sqlite3_reset (svm->vm); + } + if ( result==SQLITE_SCHEMA ) { + sqlite3_stmt *vn; + const char *sql; + /* recover sql text */ + lua_pushlightuserdata(L, svm->db); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(L, svm); + lua_rawget(L, -2); /* sql text */ + sql = luaL_checkstring(L, -1); + /* re-prepare */ + result = sqlite3_prepare(svm->db->db, sql, -1, &vn, NULL); + if (result != SQLITE_OK) break; + sqlite3_transfer_bindings(svm->vm, vn); + sqlite3_finalize(svm->vm); + svm->vm = vn; + lua_pop(L,2); + } else { + break; + } + } + return result; +} + +static sdb_vm *lsqlite_getvm(lua_State *L, int index) { + sdb_vm *svm = (sdb_vm*)luaL_checkudata(L, index, sqlite_vm_meta); + if (svm == NULL) luaL_argerror(L, index, "bad sqlite virtual machine"); + return svm; +} + +static sdb_vm *lsqlite_checkvm(lua_State *L, int index) { + sdb_vm *svm = lsqlite_getvm(L, index); + if (svm->vm == NULL) luaL_argerror(L, index, "attempt to use closed sqlite virtual machine"); + return svm; +} + +static int dbvm_isopen(lua_State *L) { + sdb_vm *svm = lsqlite_getvm(L, 1); + lua_pushboolean(L, svm->vm != NULL ? 1 : 0); + return 1; +} + +static int dbvm_tostring(lua_State *L) { + char buff[39]; + sdb_vm *svm = lsqlite_getvm(L, 1); + if (svm->vm == NULL) + strcpy(buff, "closed"); + else + sprintf(buff, "%p", svm); + lua_pushfstring(L, "sqlite virtual machine (%s)", buff); + return 1; +} + +static int dbvm_gc(lua_State *L) { + sdb_vm *svm = lsqlite_getvm(L, 1); + if (svm->vm != NULL) /* ignore closed vms */ + cleanupvm(L, svm); + return 0; +} + +static int dbvm_step(lua_State *L) { + int result; + sdb_vm *svm = lsqlite_checkvm(L, 1); + + result = stepvm(L, svm); + svm->has_values = result == SQLITE_ROW ? 1 : 0; + svm->columns = sqlite3_data_count(svm->vm); + + lua_pushnumber(L, result); + return 1; +} + +static int dbvm_finalize(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + return cleanupvm(L, svm); +} + +static int dbvm_reset(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_reset(svm->vm); + lua_pushnumber(L, sqlite3_errcode(svm->db->db)); + return 1; +} + +static void dbvm_check_contents(lua_State *L, sdb_vm *svm) { + if (!svm->has_values) { + luaL_error(L, "misuse of function"); + } +} + +static void dbvm_check_index(lua_State *L, sdb_vm *svm, int index) { + if (index < 0 || index >= svm->columns) { + luaL_error(L, "index out of range [0..%d]", svm->columns - 1); + } +} + +static void dbvm_check_bind_index(lua_State *L, sdb_vm *svm, int index) { + if (index < 1 || index > sqlite3_bind_parameter_count(svm->vm)) { + luaL_error(L, "bind index out of range [1..%d]", sqlite3_bind_parameter_count(svm->vm)); + } +} + +/* +** ======================================================= +** Virtual Machine - generic info +** ======================================================= +*/ +static int dbvm_columns(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + lua_pushnumber(L, sqlite3_column_count(svm->vm)); + return 1; +} + +/* +** ======================================================= +** Virtual Machine - getters +** ======================================================= +*/ + +static int dbvm_get_value(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + int index = luaL_checkint(L, 2); + dbvm_check_contents(L, svm); + dbvm_check_index(L, svm, index); + vm_push_column(L, svm->vm, index); + return 1; +} + +static int dbvm_get_name(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + int index = (int)luaL_checknumber(L, 2); + dbvm_check_index(L, svm, index); + lua_pushstring(L, sqlite3_column_name(svm->vm, index)); + return 1; +} + +static int dbvm_get_type(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + int index = (int)luaL_checknumber(L, 2); + dbvm_check_index(L, svm, index); + lua_pushstring(L, sqlite3_column_decltype(svm->vm, index)); + return 1; +} + +static int dbvm_get_values(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int columns = svm->columns; + int n; + dbvm_check_contents(L, svm); + + lua_newtable(L); + for (n = 0; n < columns;) { + vm_push_column(L, vm, n++); + lua_rawseti(L, -2, n); + } + return 1; +} + +static int dbvm_get_names(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ + int n; + + lua_newtable(L); + for (n = 0; n < columns;) { + lua_pushstring(L, sqlite3_column_name(vm, n++)); + lua_rawseti(L, -2, n); + } + return 1; +} + +static int dbvm_get_types(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ + int n; + + lua_newtable(L); + for (n = 0; n < columns;) { + lua_pushstring(L, sqlite3_column_decltype(vm, n++)); + lua_rawseti(L, -2, n); + } + return 1; +} + +static int dbvm_get_uvalues(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int columns = svm->columns; + int n; + dbvm_check_contents(L, svm); + + lua_checkstack(L, columns); + for (n = 0; n < columns; ++n) + vm_push_column(L, vm, n); + return columns; +} + +static int dbvm_get_unames(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ + int n; + + lua_checkstack(L, columns); + for (n = 0; n < columns; ++n) + lua_pushstring(L, sqlite3_column_name(vm, n)); + return columns; +} + +static int dbvm_get_utypes(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ + int n; + + lua_checkstack(L, columns); + for (n = 0; n < columns; ++n) + lua_pushstring(L, sqlite3_column_decltype(vm, n)); + return columns; +} + +static int dbvm_get_named_values(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int columns = svm->columns; + int n; + dbvm_check_contents(L, svm); + + lua_newtable(L); + for (n = 0; n < columns; ++n) { + lua_pushstring(L, sqlite3_column_name(vm, n)); + vm_push_column(L, vm, n); + lua_rawset(L, -3); + } + return 1; +} + +static int dbvm_get_named_types(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int columns = sqlite3_column_count(vm); + int n; + + lua_newtable(L); + for (n = 0; n < columns; ++n) { + lua_pushstring(L, sqlite3_column_name(vm, n)); + lua_pushstring(L, sqlite3_column_decltype(vm, n)); + lua_rawset(L, -3); + } + return 1; +} + +/* +** ======================================================= +** Virtual Machine - Bind +** ======================================================= +*/ + +static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex) { + switch (lua_type(L, lindex)) { + case LUA_TSTRING: + return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), lua_strlen(L, lindex), SQLITE_TRANSIENT); + case LUA_TNUMBER: + return sqlite3_bind_double(vm, index, lua_tonumber(L, lindex)); + case LUA_TBOOLEAN: + return sqlite3_bind_int(vm, index, lua_toboolean(L, lindex) ? 1 : 0); + case LUA_TNONE: + case LUA_TNIL: + return sqlite3_bind_null(vm, index); + default: + luaL_error(L, "index (%d) - invalid data type for bind (%s)", index, lua_typename(L, lua_type(L, lindex))); + return SQLITE_MISUSE; /*!*/ + } +} + + +static int dbvm_bind_parameter_count(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + lua_pushnumber(L, sqlite3_bind_parameter_count(svm->vm)); + return 1; +} + +static int dbvm_bind_parameter_name(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + int index = (int)luaL_checknumber(L, 2); + dbvm_check_bind_index(L, svm, index); + lua_pushstring(L, sqlite3_bind_parameter_name(svm->vm, index)); + return 1; +} + +static int dbvm_bind(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int index = luaL_checkint(L, 2); + int result; + + dbvm_check_bind_index(L, svm, index); + result = dbvm_bind_index(L, vm, index, 3); + + lua_pushnumber(L, result); + return 1; +} + +static int dbvm_bind_blob(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + int index = luaL_checkint(L, 2); + const char *value = luaL_checkstring(L, 3); + int len = lua_strlen(L, 3); + + lua_pushnumber(L, sqlite3_bind_blob(svm->vm, index, value, len, SQLITE_TRANSIENT)); + return 1; +} + +static int dbvm_bind_values(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int top = lua_gettop(L); + int result, n; + + if (top - 1 != sqlite3_bind_parameter_count(vm)) + luaL_error(L, + "incorrect number of parameters to bind (%d given, %d to bind)", + top - 1, + sqlite3_bind_parameter_count(vm) + ); + + for (n = 2; n <= top; ++n) { + if ((result = dbvm_bind_index(L, vm, n - 1, n)) != SQLITE_OK) { + lua_pushnumber(L, result); + return 1; + } + } + + lua_pushnumber(L, SQLITE_OK); + return 1; +} + +static int dbvm_bind_names(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm = svm->vm; + int count = sqlite3_bind_parameter_count(vm); + const char *name; + int result, n; + luaL_checktype(L, 2, LUA_TTABLE); + + for (n = 1; n <= count; ++n) { + name = sqlite3_bind_parameter_name(vm, n); + if (name && (name[0] == ':' || name[0] == '$')) { + lua_pushstring(L, ++name); + lua_gettable(L, 2); + result = dbvm_bind_index(L, vm, n, -1); + lua_pop(L, 1); + } + else { + lua_pushnumber(L, n); + lua_gettable(L, 2); + result = dbvm_bind_index(L, vm, n, -1); + lua_pop(L, 1); + } + + if (result != SQLITE_OK) { + lua_pushnumber(L, result); + return 1; + } + } + + lua_pushnumber(L, SQLITE_OK); + return 1; +} + +/* +** ======================================================= +** Database (internal management) +** ======================================================= +*/ + +/* +** When creating database handles, always creates a `closed' database handle +** before opening the actual database; so, if there is a memory error, the +** database is not left opened. +** +** Creates a new 'table' and leaves it in the stack +*/ +static sdb *newdb (lua_State *L) { + sdb *db = (sdb*)lua_newuserdata(L, sizeof(sdb)); + db->L = L; + db->db = NULL; /* database handle is currently `closed' */ + db->func = NULL; + + db->busy_cb = + db->busy_udata = + db->progress_cb = + db->progress_udata = + db->trace_cb = + db->trace_udata = +#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK + db->update_hook_cb = + db->update_hook_udata = + db->commit_hook_cb = + db->commit_hook_udata = + db->rollback_hook_cb = + db->rollback_hook_udata = +#endif + LUA_NOREF; + + luaL_getmetatable(L, sqlite_meta); + lua_setmetatable(L, -2); /* set metatable */ + + /* to keep track of 'open' virtual machines */ + lua_pushlightuserdata(L, db); + lua_newtable(L); + lua_rawset(L, LUA_REGISTRYINDEX); + + return db; +} + +static int cleanupdb(lua_State *L, sdb *db) { + sdb_func *func; + sdb_func *func_next; + int top; + int result; + + /* free associated virtual machines */ + lua_pushlightuserdata(L, db); + lua_rawget(L, LUA_REGISTRYINDEX); + + /* close all used handles */ + top = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, -2)) { + sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */ + cleanupvm(L, svm); + + lua_settop(L, top); + lua_pushnil(L); + } + + lua_pop(L, 1); /* pop vm table */ + + /* remove entry in lua registry table */ + lua_pushlightuserdata(L, db); + lua_pushnil(L); + lua_rawset(L, LUA_REGISTRYINDEX); + + /* 'free' all references */ + luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); + luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata); + luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata); +#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK + luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata); + luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata); + luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); +#endif + + /* close database */ + result = sqlite3_close(db->db); + db->db = NULL; + + /* free associated memory with created functions */ + func = db->func; + while (func) { + func_next = func->next; + luaL_unref(L, LUA_REGISTRYINDEX, func->fn_step); + luaL_unref(L, LUA_REGISTRYINDEX, func->fn_finalize); + luaL_unref(L, LUA_REGISTRYINDEX, func->udata); + free(func); + func = func_next; + } + db->func = NULL; + return result; +} + +static sdb *lsqlite_getdb(lua_State *L, int index) { + sdb *db = (sdb*)luaL_checkudata(L, index, sqlite_meta); + if (db == NULL) luaL_typerror(L, index, "sqlite database"); + return db; +} + +static sdb *lsqlite_checkdb(lua_State *L, int index) { + sdb *db = lsqlite_getdb(L, index); + if (db->db == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database"); + return db; +} + + +/* +** ======================================================= +** User Defined Functions - Context Methods +** ======================================================= +*/ +typedef struct { + sqlite3_context *ctx; + int ud; +} lcontext; + +static lcontext *lsqlite_make_context(lua_State *L) { + lcontext *ctx = (lcontext*)lua_newuserdata(L, sizeof(lcontext)); + lua_rawgeti(L, LUA_REGISTRYINDEX, sqlite_ctx_meta_ref); + lua_setmetatable(L, -2); + ctx->ctx = NULL; + ctx->ud = LUA_NOREF; + return ctx; +} + +static lcontext *lsqlite_getcontext(lua_State *L, int index) { + lcontext *ctx = (lcontext*)luaL_checkudata(L, index, sqlite_ctx_meta); + if (ctx == NULL) luaL_typerror(L, index, "sqlite context"); + return ctx; +} + +static lcontext *lsqlite_checkcontext(lua_State *L, int index) { + lcontext *ctx = lsqlite_getcontext(L, index); + if (ctx->ctx == NULL) luaL_argerror(L, index, "invalid sqlite context"); + return ctx; +} + +static int lcontext_tostring(lua_State *L) { + char buff[39]; + lcontext *ctx = lsqlite_getcontext(L, 1); + if (ctx->ctx == NULL) + strcpy(buff, "closed"); + else + sprintf(buff, "%p", ctx->ctx); + lua_pushfstring(L, "sqlite function context (%s)", buff); + return 1; +} + +static void lcontext_check_aggregate(lua_State *L, lcontext *ctx) { + sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx); + if (!func->aggregate) { + luaL_error(L, "attempt to call aggregate method from scalar function"); + } +} + +static int lcontext_user_data(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx); + lua_rawgeti(L, LUA_REGISTRYINDEX, func->udata); + return 1; +} + +static int lcontext_get_aggregate_context(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + lcontext_check_aggregate(L, ctx); + lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ud); + return 1; +} + +static int lcontext_set_aggregate_context(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + lcontext_check_aggregate(L, ctx); + lua_settop(L, 2); + luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); + ctx->ud = luaL_ref(L, LUA_REGISTRYINDEX); + return 0; +} + +static int lcontext_aggregate_count(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + lcontext_check_aggregate(L, ctx); + lua_pushnumber(L, sqlite3_aggregate_count(ctx->ctx)); + return 1; +} + +#if 0 +void *sqlite3_get_auxdata(sqlite3_context*, int); +void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*)); +#endif + +static int lcontext_result(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + switch (lua_type(L, 2)) { + case LUA_TNUMBER: + sqlite3_result_double(ctx->ctx, luaL_checknumber(L, 2)); + break; + case LUA_TSTRING: + sqlite3_result_text(ctx->ctx, luaL_checkstring(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT); + break; + case LUA_TNIL: + case LUA_TNONE: + sqlite3_result_null(ctx->ctx); + break; + default: + luaL_error(L, "invalid result type %s", lua_typename(L, 2)); + break; + } + + return 0; +} + +static int lcontext_result_blob(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + const char *blob = luaL_checkstring(L, 2); + int size = lua_strlen(L, 2); + sqlite3_result_blob(ctx->ctx, (const void*)blob, size, SQLITE_TRANSIENT); + return 0; +} + +static int lcontext_result_double(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + double d = luaL_checknumber(L, 2); + sqlite3_result_double(ctx->ctx, d); + return 0; +} + +static int lcontext_result_error(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + const char *err = luaL_checkstring(L, 2); + int size = lua_strlen(L, 2); + sqlite3_result_error(ctx->ctx, err, size); + return 0; +} + +static int lcontext_result_int(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + int i = luaL_checkint(L, 2); + sqlite3_result_int(ctx->ctx, i); + return 0; +} + +static int lcontext_result_null(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + sqlite3_result_null(ctx->ctx); + return 0; +} + +static int lcontext_result_text(lua_State *L) { + lcontext *ctx = lsqlite_checkcontext(L, 1); + const char *text = luaL_checkstring(L, 2); + int size = lua_strlen(L, 2); + sqlite3_result_text(ctx->ctx, text, size, SQLITE_TRANSIENT); + return 0; +} + +/* +** ======================================================= +** Database Methods +** ======================================================= +*/ + +static int db_isopen(lua_State *L) { + sdb *db = lsqlite_getdb(L, 1); + lua_pushboolean(L, db->db != NULL ? 1 : 0); + return 1; +} + +static int db_last_insert_rowid(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + /* conversion warning: int64 -> luaNumber */ + sqlite_int64 rowid = sqlite3_last_insert_rowid(db->db); + lua_Number n = (lua_Number)rowid; + if (n == rowid) + lua_pushnumber(L, n); + else + lua_pushfstring(L, "%ll", rowid); + return 1; +} + +static int db_changes(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + lua_pushnumber(L, sqlite3_changes(db->db)); + return 1; +} + +static int db_total_changes(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + lua_pushnumber(L, sqlite3_total_changes(db->db)); + return 1; +} + +static int db_errcode(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + lua_pushnumber(L, sqlite3_errcode(db->db)); + return 1; +} + +static int db_errmsg(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + lua_pushstring(L, sqlite3_errmsg(db->db)); + return 1; +} + +static int db_interrupt(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + sqlite3_interrupt(db->db); + return 0; +} + +/* +** Registering SQL functions: +*/ + +static void db_push_value(lua_State *L, sqlite3_value *value) { + switch (sqlite3_value_type(value)) { + case SQLITE_TEXT: + lua_pushlstring(L, (const char*)sqlite3_value_text(value), sqlite3_value_bytes(value)); + break; + + case SQLITE_INTEGER: + { + sqlite_int64 i64 = sqlite3_value_int64(value); + lua_Number n = (lua_Number)i64; + if (n == i64) + lua_pushnumber(L, n); + else + lua_pushlstring(L, (const char*)sqlite3_value_text(value), sqlite3_value_bytes(value)); + } + break; + + case SQLITE_FLOAT: + lua_pushnumber(L, sqlite3_value_double(value)); + break; + + case SQLITE_BLOB: + lua_pushlstring(L, sqlite3_value_blob(value), sqlite3_value_bytes(value)); + break; + + case SQLITE_NULL: + lua_pushnil(L); + break; + + default: + /* things done properly (SQLite + Lua SQLite) + ** this should never happen */ + lua_pushnil(L); + break; + } +} + +/* +** callback functions used when calling registered sql functions +*/ + +/* scalar function to be called +** callback params: context, values... */ +static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_value **argv) { + sdb_func *func = (sdb_func*)sqlite3_user_data(context); + lua_State *L = func->db->L; + int n; + lcontext *ctx; + + int top = lua_gettop(L); + + /* ensure there is enough space in the stack */ + lua_checkstack(L, argc + 3); + + lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_step); /* function to call */ + + if (!func->aggregate) { + ctx = lsqlite_make_context(L); /* push context - used to set results */ + } + else { + /* reuse context userdata value */ + void *p = sqlite3_aggregate_context(context, 1); + /* i think it is OK to use assume that using a light user data + ** as an entry on LUA REGISTRY table will be unique */ + lua_pushlightuserdata(L, p); + lua_rawget(L, LUA_REGISTRYINDEX); /* context table */ + + if (lua_isnil(L, -1)) { /* not yet created? */ + lua_pop(L, 1); + ctx = lsqlite_make_context(L); + lua_pushlightuserdata(L, p); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } + else + ctx = lsqlite_getcontext(L, -1); + } + + /* push params */ + for (n = 0; n < argc; ++n) { + db_push_value(L, argv[n]); + } + + /* set context */ + ctx->ctx = context; + + if (lua_pcall(L, argc + 1, 0, 0)) { + const char *errmsg = lua_tostring(L, -1); + int size = lua_strlen(L, -1); + sqlite3_result_error(context, errmsg, size); + } + + /* invalidate context */ + ctx->ctx = NULL; + + if (!func->aggregate) { + luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); + } + + lua_settop(L, top); +} + +static void db_sql_finalize_function(sqlite3_context *context) { + sdb_func *func = (sdb_func*)sqlite3_user_data(context); + lua_State *L = func->db->L; + void *p = sqlite3_aggregate_context(context, 1); /* minimal mem usage */ + lcontext *ctx; + int top = lua_gettop(L); + + lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_finalize); /* function to call */ + + /* i think it is OK to use assume that using a light user data + ** as an entry on LUA REGISTRY table will be unique */ + lua_pushlightuserdata(L, p); + lua_rawget(L, LUA_REGISTRYINDEX); /* context table */ + + if (lua_isnil(L, -1)) { /* not yet created? - shouldn't happen in finalize function */ + lua_pop(L, 1); + ctx = lsqlite_make_context(L); + lua_pushlightuserdata(L, p); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } + else + ctx = lsqlite_getcontext(L, -1); + + /* set context */ + ctx->ctx = context; + + if (lua_pcall(L, 1, 0, 0)) { + sqlite3_result_error(context, lua_tostring(L, -1), -1); + } + + /* invalidate context */ + ctx->ctx = NULL; + + /* cleanup context */ + luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); + /* remove it from registry */ + lua_pushlightuserdata(L, p); + lua_pushnil(L); + lua_rawset(L, LUA_REGISTRYINDEX); + + lua_settop(L, top); +} + +/* +** Register a normal function +** Params: db, function name, number arguments, [ callback | step, finalize], user data +** Returns: true on sucess +** +** Normal function: +** Params: context, params +** +** Aggregate function: +** Params of step: context, params +** Params of finalize: context +*/ +static int db_register_function(lua_State *L, int aggregate) { + sdb *db = lsqlite_checkdb(L, 1); + const char *name; + int args; + int result; + sdb_func *func; + + /* safety measure */ + if (aggregate) aggregate = 1; + + name = luaL_checkstring(L, 2); + args = luaL_checkint(L, 3); + luaL_checktype(L, 4, LUA_TFUNCTION); + if (aggregate) luaL_checktype(L, 5, LUA_TFUNCTION); + + /* maybe an alternative way to allocate memory should be used/avoided */ + func = (sdb_func*)malloc(sizeof(sdb_func)); + if (func == NULL) { + luaL_error(L, "out of memory"); + } + + result = sqlite3_create_function( + db->db, name, args, SQLITE_UTF8, func, + aggregate ? NULL : db_sql_normal_function, + aggregate ? db_sql_normal_function : NULL, + aggregate ? db_sql_finalize_function : NULL + ); + + if (result == SQLITE_OK) { + /* safety measures for userdata field to be present in the stack */ + lua_settop(L, 5 + aggregate); + + /* save registered function in db function list */ + func->db = db; + func->aggregate = aggregate; + func->next = db->func; + db->func = func; + + /* save the setp/normal function callback */ + lua_pushvalue(L, 4); + func->fn_step = luaL_ref(L, LUA_REGISTRYINDEX); + /* save user data */ + lua_pushvalue(L, 5+aggregate); + func->udata = luaL_ref(L, LUA_REGISTRYINDEX); + + if (aggregate) { + lua_pushvalue(L, 5); + func->fn_finalize = luaL_ref(L, LUA_REGISTRYINDEX); + } + else + func->fn_finalize = LUA_NOREF; + } + else { + /* free allocated memory */ + free(func); + } + + lua_pushboolean(L, result == SQLITE_OK ? 1 : 0); + return 1; +} + +static int db_create_function(lua_State *L) { + return db_register_function(L, 0); +} + +static int db_create_aggregate(lua_State *L) { + return db_register_function(L, 1); +} + +/* create_collation; contributed by Thomas Lauer +*/ + +typedef struct { + lua_State *L; + int ref; +} scc; + +static int collwrapper(scc *co,int l1,const void *p1, + int l2,const void *p2) { + int res=0; + lua_State *L=co->L; + lua_rawgeti(L,LUA_REGISTRYINDEX,co->ref); + lua_pushlstring(L,p1,l1); + lua_pushlstring(L,p2,l2); + if (lua_pcall(L,2,1,0)==0) res=(int)lua_tonumber(L,-1); + lua_pop(L,1); + return res; +} + +static void collfree(scc *co) { + if (co) { + luaL_unref(co->L,LUA_REGISTRYINDEX,co->ref); + free(co); + } +} + +static int db_create_collation(lua_State *L) { + sdb *db=lsqlite_checkdb(L,1); + const char *collname=luaL_checkstring(L,2); + scc *co=NULL; + int (*collfunc)(scc *,int,const void *,int,const void *)=NULL; + lua_settop(L,3); /* default args to nil, and exclude extras */ + if (lua_isfunction(L,3)) collfunc=collwrapper; + else if (!lua_isnil(L,3)) + luaL_error(L,"create_collation: function or nil expected"); + if (collfunc != NULL) { + co=(scc *)malloc(sizeof(scc)); /* userdata is a no-no as it + will be garbage-collected */ + if (co) { + co->L=L; + /* lua_settop(L,3) above means we don't need: lua_pushvalue(L,3); */ + co->ref=luaL_ref(L,LUA_REGISTRYINDEX); + } + else luaL_error(L,"create_collation: could not allocate callback"); + } + sqlite3_create_collation_v2(db->db, collname, SQLITE_UTF8, + (void *)co, + (int(*)(void*,int,const void*,int,const void*))collfunc, + (void(*)(void*))collfree); + return 0; +} + +/* +** trace callback: +** Params: database, callback function, userdata +** +** callback function: +** Params: userdata, sql +*/ +static void db_trace_callback(void *user, const char *sql) { + sdb *db = (sdb*)user; + lua_State *L = db->L; + int top = lua_gettop(L); + + /* setup lua callback call */ + lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_cb); /* get callback */ + lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_udata); /* get callback user data */ + lua_pushstring(L, sql); /* traced sql statement */ + + /* call lua function */ + lua_pcall(L, 2, 0, 0); + /* ignore any error generated by this function */ + + lua_settop(L, top); +} + +static int db_trace(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + + if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { + luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata); + + db->trace_cb = + db->trace_udata = LUA_NOREF; + + /* clear trace handler */ + sqlite3_trace(db->db, NULL, NULL); + } + else { + luaL_checktype(L, 2, LUA_TFUNCTION); + + /* make sure we have an userdata field (even if nil) */ + lua_settop(L, 3); + + luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata); + + db->trace_udata = luaL_ref(L, LUA_REGISTRYINDEX); + db->trace_cb = luaL_ref(L, LUA_REGISTRYINDEX); + + /* set trace handler */ + sqlite3_trace(db->db, db_trace_callback, db); + } + + return 0; +} + +#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK + +/* +** update_hook callback: +** Params: database, callback function, userdata +** +** callback function: +** Params: userdata, {one of SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE}, +** database name, table name (containing the affected row), rowid of the row +*/ +static void db_update_hook_callback(void *user, int op, char const *dbname, char const *tblname, sqlite3_int64 rowid) { + sdb *db = (sdb*)user; + lua_State *L = db->L; + int top = lua_gettop(L); + lua_Number n = (lua_Number)rowid; + + /* setup lua callback call */ + lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_cb); /* get callback */ + lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_udata); /* get callback user data */ + lua_pushnumber(L, (lua_Number )op); + lua_pushstring(L, dbname); /* update_hook database name */ + lua_pushstring(L, tblname); /* update_hook database name */ + if (n == rowid) + lua_pushnumber(L, n); + else + lua_pushfstring(L, "%ll", rowid); + + /* call lua function */ + lua_pcall(L, 5, 0, 0); + /* ignore any error generated by this function */ + + lua_settop(L, top); +} + +static int db_update_hook(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + + if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { + luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata); + + db->update_hook_cb = + db->update_hook_udata = LUA_NOREF; + + /* clear update_hook handler */ + sqlite3_update_hook(db->db, NULL, NULL); + } + else { + luaL_checktype(L, 2, LUA_TFUNCTION); + + /* make sure we have an userdata field (even if nil) */ + lua_settop(L, 3); + + luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata); + + db->update_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX); + db->update_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX); + + /* set update_hook handler */ + sqlite3_update_hook(db->db, db_update_hook_callback, db); + } + + return 0; +} + +/* +** commit_hook callback: +** Params: database, callback function, userdata +** +** callback function: +** Params: userdata +** Returned value: Return false or nil to continue the COMMIT operation normally. +** return true (non false, non nil), then the COMMIT is converted into a ROLLBACK. +*/ +static int db_commit_hook_callback(void *user) { + sdb *db = (sdb*)user; + lua_State *L = db->L; + int top = lua_gettop(L); + int rollback = 0; + + /* setup lua callback call */ + lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_cb); /* get callback */ + lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_udata); /* get callback user data */ + + /* call lua function */ + if (!lua_pcall(L, 1, 1, 0)) + rollback = lua_toboolean(L, -1); /* use result if there was no error */ + + lua_settop(L, top); + return rollback; +} + +static int db_commit_hook(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + + if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { + luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata); + + db->commit_hook_cb = + db->commit_hook_udata = LUA_NOREF; + + /* clear commit_hook handler */ + sqlite3_commit_hook(db->db, NULL, NULL); + } + else { + luaL_checktype(L, 2, LUA_TFUNCTION); + + /* make sure we have an userdata field (even if nil) */ + lua_settop(L, 3); + + luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata); + + db->commit_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX); + db->commit_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX); + + /* set commit_hook handler */ + sqlite3_commit_hook(db->db, db_commit_hook_callback, db); + } + + return 0; +} + +/* +** rollback hook callback: +** Params: database, callback function, userdata +** +** callback function: +** Params: userdata +*/ +static void db_rollback_hook_callback(void *user) { + sdb *db = (sdb*)user; + lua_State *L = db->L; + int top = lua_gettop(L); + + /* setup lua callback call */ + lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); /* get callback */ + lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); /* get callback user data */ + + /* call lua function */ + lua_pcall(L, 1, 0, 0); + /* ignore any error generated by this function */ + + lua_settop(L, top); +} + +static int db_rollback_hook(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + + if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { + luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); + + db->rollback_hook_cb = + db->rollback_hook_udata = LUA_NOREF; + + /* clear rollback_hook handler */ + sqlite3_rollback_hook(db->db, NULL, NULL); + } + else { + luaL_checktype(L, 2, LUA_TFUNCTION); + + /* make sure we have an userdata field (even if nil) */ + lua_settop(L, 3); + + luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); + + db->rollback_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX); + db->rollback_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX); + + /* set rollback_hook handler */ + sqlite3_rollback_hook(db->db, db_rollback_hook_callback, db); + } + + return 0; +} + +#endif /* #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK */ + +#if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK + +/* +** progress handler: +** Params: database, number of opcodes, callback function, userdata +** +** callback function: +** Params: userdata +** returns: 0 to return immediatly and return SQLITE_ABORT, non-zero to continue +*/ +static int db_progress_callback(void *user) { + int result = 1; /* abort by default */ + sdb *db = (sdb*)user; + lua_State *L = db->L; + int top = lua_gettop(L); + + lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_cb); + lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_udata); + + /* call lua function */ + if (!lua_pcall(L, 1, 1, 0)) + result = lua_toboolean(L, -1); + + lua_settop(L, top); + return result; +} + +static int db_progress_handler(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + + if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { + luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata); + + db->progress_cb = + db->progress_udata = LUA_NOREF; + + /* clear busy handler */ + sqlite3_progress_handler(db->db, 0, NULL, NULL); + } + else { + int nop = luaL_checkint(L, 2); /* number of opcodes */ + luaL_checktype(L, 3, LUA_TFUNCTION); + + /* make sure we have an userdata field (even if nil) */ + lua_settop(L, 4); + + luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata); + + db->progress_udata = luaL_ref(L, LUA_REGISTRYINDEX); + db->progress_cb = luaL_ref(L, LUA_REGISTRYINDEX); + + /* set progress callback */ + sqlite3_progress_handler(db->db, nop, db_progress_callback, db); + } + + return 0; +} + +#else /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */ + +static int db_progress_handler(lua_State *L) { + lua_pushliteral(L, "progress callback support disabled at compile time"); + lua_error(L); + return 0; +} + +#endif /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */ + +/* +** busy handler: +** Params: database, callback function, userdata +** +** callback function: +** Params: userdata, number of tries +** returns: 0 to return immediatly and return SQLITE_BUSY, non-zero to try again +*/ +static int db_busy_callback(void *user, int tries) { + int retry = 0; /* abort by default */ + sdb *db = (sdb*)user; + lua_State *L = db->L; + int top = lua_gettop(L); + + lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_cb); + lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_udata); + lua_pushnumber(L, tries); + + /* call lua function */ + if (!lua_pcall(L, 2, 1, 0)) + retry = lua_toboolean(L, -1); + + lua_settop(L, top); + return retry; +} + +static int db_busy_handler(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + + if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { + luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); + + db->busy_cb = + db->busy_udata = LUA_NOREF; + + /* clear busy handler */ + sqlite3_busy_handler(db->db, NULL, NULL); + } + else { + luaL_checktype(L, 2, LUA_TFUNCTION); + /* make sure we have an userdata field (even if nil) */ + lua_settop(L, 3); + + luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); + + db->busy_udata = luaL_ref(L, LUA_REGISTRYINDEX); + db->busy_cb = luaL_ref(L, LUA_REGISTRYINDEX); + + /* set busy handler */ + sqlite3_busy_handler(db->db, db_busy_callback, db); + } + + return 0; +} + +static int db_busy_timeout(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + int timeout = luaL_checkint(L, 2); + sqlite3_busy_timeout(db->db, timeout); + + /* if there was a timeout callback registered, it is now + ** invalid/useless. free any references we may have */ + luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); + luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); + db->busy_cb = + db->busy_udata = LUA_NOREF; + + return 0; +} + +/* +** Params: db, sql, callback, user +** returns: code [, errmsg] +** +** Callback: +** Params: user, number of columns, values, names +** Returns: 0 to continue, other value will cause abort +*/ +static int db_exec_callback(void* user, int columns, char **data, char **names) { + int result = SQLITE_ABORT; /* abort by default */ + lua_State *L = (lua_State*)user; + int n; + + int top = lua_gettop(L); + + lua_pushvalue(L, 3); /* function to call */ + lua_pushvalue(L, 4); /* user data */ + lua_pushnumber(L, columns); /* total number of rows in result */ + + /* column values */ + lua_pushvalue(L, 6); + for (n = 0; n < columns;) { + lua_pushstring(L, data[n++]); + lua_rawseti(L, -2, n); + } + + /* columns names */ + lua_pushvalue(L, 5); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_replace(L, 5); + for (n = 0; n < columns;) { + lua_pushstring(L, names[n++]); + lua_rawseti(L, -2, n); + } + } + + /* call lua function */ + if (!lua_pcall(L, 4, 1, 0)) { + if (lua_isnumber(L, -1)) + result = (int)lua_tonumber(L, -1); + } + + lua_settop(L, top); + return result; +} + +static int db_exec(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + const char *sql = luaL_checkstring(L, 2); + int result; + + if (!lua_isnoneornil(L, 3)) { + /* stack: + ** 3: callback function + ** 4: userdata + ** 5: column names + ** 6: reusable column values + */ + luaL_checktype(L, 3, LUA_TFUNCTION); + lua_settop(L, 4); /* 'trap' userdata - nil extra parameters */ + lua_pushnil(L); /* column names not known at this point */ + lua_newtable(L); /* column values table */ + + result = sqlite3_exec(db->db, sql, db_exec_callback, L, NULL); + } + else { + /* no callbacks */ + result = sqlite3_exec(db->db, sql, NULL, NULL, NULL); + } + + lua_pushnumber(L, result); + return 1; +} + +/* +** Params: db, sql +** returns: code, compiled length or error message +*/ +static int db_prepare(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + const char *sql = luaL_checkstring(L, 2); + int sql_len = lua_strlen(L, 2); + const char *sqltail; + sdb_vm *svm; + lua_settop(L,2); /* sql is on top of stack for call to newvm */ + svm = newvm(L, db); + + if (sqlite3_prepare(db->db, sql, sql_len, &svm->vm, &sqltail) != SQLITE_OK) { + cleanupvm(L, svm); + + lua_pushnil(L); + lua_pushnumber(L, sqlite3_errcode(db->db)); + return 2; + } + + /* vm already in the stack */ + lua_pushstring(L, sqltail); + return 2; +} + +static int db_do_next_row(lua_State *L, int packed) { + int result; + sdb_vm *svm = lsqlite_checkvm(L, 1); + sqlite3_stmt *vm; + int columns; + int i; + + result = stepvm(L, svm); + vm = svm->vm; /* stepvm may change svm->vm if re-prepare is needed */ + svm->has_values = result == SQLITE_ROW ? 1 : 0; + svm->columns = columns = sqlite3_data_count(vm); + + if (result == SQLITE_ROW) { + if (packed) { + lua_newtable(L); + if (packed == 1) { + for (i = 0; i < columns;) { + vm_push_column(L, vm, i); + lua_rawseti(L, -2, ++i); + } + } + else { + for (i = 0; i < columns; ++i) { + lua_pushstring(L, sqlite3_column_name(vm, i)); + vm_push_column(L, vm, i); + lua_rawset(L, -3); + } + } + return 1; + } + else { + lua_checkstack(L, columns); + for (i = 0; i < columns; ++i) + vm_push_column(L, vm, i); + return svm->columns; + } + } + + if (svm->temp) { + /* finalize and check for errors */ + result = sqlite3_finalize(vm); + svm->vm = NULL; + cleanupvm(L, svm); + } + else if (result == SQLITE_DONE) { + result = sqlite3_reset(vm); + } + + if (result != SQLITE_OK) { + lua_pushstring(L, sqlite3_errmsg(svm->db->db)); + lua_error(L); + } + return 0; +} + +static int db_next_row(lua_State *L) { + return db_do_next_row(L, 0); +} + +static int db_next_packed_row(lua_State *L) { + return db_do_next_row(L, 1); +} + +static int db_next_named_row(lua_State *L) { + return db_do_next_row(L, 2); +} + +static int dbvm_do_rows(lua_State *L, int(*f)(lua_State *)) { + /* sdb_vm *svm = */ + lsqlite_checkvm(L, 1); + lua_pushvalue(L,1); + lua_pushcfunction(L, f); + lua_insert(L, -2); + return 2; +} + +static int dbvm_rows(lua_State *L) { + return dbvm_do_rows(L, db_next_packed_row); +} + +static int dbvm_nrows(lua_State *L) { + return dbvm_do_rows(L, db_next_named_row); +} + +static int dbvm_urows(lua_State *L) { + return dbvm_do_rows(L, db_next_row); +} + +static int db_do_rows(lua_State *L, int(*f)(lua_State *)) { + sdb *db = lsqlite_checkdb(L, 1); + const char *sql = luaL_checkstring(L, 2); + sdb_vm *svm; + lua_settop(L,2); /* sql is on top of stack for call to newvm */ + svm = newvm(L, db); + svm->temp = 1; + + if (sqlite3_prepare(db->db, sql, -1, &svm->vm, NULL) != SQLITE_OK) { + cleanupvm(L, svm); + + lua_pushstring(L, sqlite3_errmsg(svm->db->db)); + lua_error(L); + } + + lua_pushcfunction(L, f); + lua_insert(L, -2); + return 2; +} + +static int db_rows(lua_State *L) { + return db_do_rows(L, db_next_packed_row); +} + +static int db_nrows(lua_State *L) { + return db_do_rows(L, db_next_named_row); +} + +/* unpacked version of db:rows */ +static int db_urows(lua_State *L) { + return db_do_rows(L, db_next_row); +} + +static int db_tostring(lua_State *L) { + char buff[32]; + sdb *db = lsqlite_getdb(L, 1); + if (db->db == NULL) + strcpy(buff, "closed"); + else + sprintf(buff, "%p", lua_touserdata(L, 1)); + lua_pushfstring(L, "sqlite database (%s)", buff); + return 1; +} + +static int db_close(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + lua_pushnumber(L, cleanupdb(L, db)); + return 1; +} + +static int db_close_vm(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + /* cleanup temporary only tables? */ + int temp = lua_toboolean(L, 2); + + /* free associated virtual machines */ + lua_pushlightuserdata(L, db); + lua_rawget(L, LUA_REGISTRYINDEX); + + /* close all used handles */ + lua_pushnil(L); + while (lua_next(L, -2)) { + sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */ + + if ((!temp || svm->temp) && svm->vm) + { + sqlite3_finalize(svm->vm); + svm->vm = NULL; + } + + /* leave key in the stack */ + lua_pop(L, 1); + } + return 0; +} + +static int db_gc(lua_State *L) { + sdb *db = lsqlite_getdb(L, 1); + if (db->db != NULL) /* ignore closed databases */ + cleanupdb(L, db); + return 0; +} + +/* +** ======================================================= +** General library functions +** ======================================================= +*/ + +static int lsqlite_version(lua_State *L) { + lua_pushstring(L, sqlite3_libversion()); + return 1; +} + +static int lsqlite_complete(lua_State *L) { + const char *sql = luaL_checkstring(L, 1); + lua_pushboolean(L, sqlite3_complete(sql)); + return 1; +} + +#ifndef WIN32 +static int lsqlite_temp_directory(lua_State *L) { + const char *oldtemp = sqlite3_temp_directory; + + if (!lua_isnone(L, 1)) { + const char *temp = luaL_optstring(L, 1, NULL); + if (sqlite3_temp_directory) { + sqlite3_free((char*)sqlite3_temp_directory); + } + if (temp) { + sqlite3_temp_directory = sqlite3_mprintf("%s", temp); + } + else { + sqlite3_temp_directory = NULL; + } + } + lua_pushstring(L, oldtemp); + return 1; +} +#endif + +static int lsqlite_do_open(lua_State *L, const char *filename) { + sdb *db = newdb(L); /* create and leave in stack */ + + if (sqlite3_open(filename, &db->db) == SQLITE_OK) { + /* database handle already in the stack - return it */ + return 1; + } + + /* failed to open database */ + lua_pushnil(L); /* push nil */ + lua_pushnumber(L, sqlite3_errcode(db->db)); + lua_pushstring(L, sqlite3_errmsg(db->db)); /* push error message */ + + /* clean things up */ + cleanupdb(L, db); + + /* return */ + return 3; +} + +static int lsqlite_open(lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return lsqlite_do_open(L, filename); +} + +static int lsqlite_open_memory(lua_State *L) { + return lsqlite_do_open(L, ":memory:"); +} + +static int lsqlite_newindex(lua_State *L) { + lua_pushliteral(L, "attempt to change readonly table"); + lua_error(L); + return 0; +} + +/* +** ======================================================= +** Register functions +** ======================================================= +*/ + +#define SC(s) { #s, SQLITE_ ## s }, +#define LSC(s) { #s, LSQLITE_ ## s }, + +static const struct { + const char* name; + int value; +} sqlite_constants[] = { + /* error codes */ + SC(OK) SC(ERROR) SC(INTERNAL) SC(PERM) + SC(ABORT) SC(BUSY) SC(LOCKED) SC(NOMEM) + SC(READONLY) SC(INTERRUPT) SC(IOERR) SC(CORRUPT) + SC(NOTFOUND) SC(FULL) SC(CANTOPEN) SC(PROTOCOL) + SC(EMPTY) SC(SCHEMA) SC(TOOBIG) SC(CONSTRAINT) + SC(MISMATCH) SC(MISUSE) SC(NOLFS) + SC(FORMAT) SC(NOTADB) + + /* sqlite_step specific return values */ + SC(RANGE) SC(ROW) SC(DONE) + + /* column types */ + SC(INTEGER) SC(FLOAT) SC(TEXT) SC(BLOB) + SC(NULL) + + /* Authorizer Action Codes */ + SC(CREATE_INDEX ) + SC(CREATE_TABLE ) + SC(CREATE_TEMP_INDEX ) + SC(CREATE_TEMP_TABLE ) + SC(CREATE_TEMP_TRIGGER) + SC(CREATE_TEMP_VIEW ) + SC(CREATE_TRIGGER ) + SC(CREATE_VIEW ) + SC(DELETE ) + SC(DROP_INDEX ) + SC(DROP_TABLE ) + SC(DROP_TEMP_INDEX ) + SC(DROP_TEMP_TABLE ) + SC(DROP_TEMP_TRIGGER ) + SC(DROP_TEMP_VIEW ) + SC(DROP_TRIGGER ) + SC(DROP_VIEW ) + SC(INSERT ) + SC(PRAGMA ) + SC(READ ) + SC(SELECT ) + SC(TRANSACTION ) + SC(UPDATE ) + SC(ATTACH ) + SC(DETACH ) + SC(ALTER_TABLE ) + SC(REINDEX ) + SC(ANALYZE ) + SC(CREATE_VTABLE ) + SC(DROP_VTABLE ) + SC(FUNCTION ) + SC(SAVEPOINT ) + + /* terminator */ + { NULL, 0 } +}; + +/* ======================================================= */ + +static const luaL_Reg dblib[] = { + {"isopen", db_isopen }, + {"last_insert_rowid", db_last_insert_rowid }, + {"changes", db_changes }, + {"total_changes", db_total_changes }, + {"errcode", db_errcode }, + {"error_code", db_errcode }, + {"errmsg", db_errmsg }, + {"error_message", db_errmsg }, + {"interrupt", db_interrupt }, + + {"create_function", db_create_function }, + {"create_aggregate", db_create_aggregate }, + {"create_collation", db_create_collation }, + + {"trace", db_trace }, + {"progress_handler", db_progress_handler }, + {"busy_timeout", db_busy_timeout }, + {"busy_handler", db_busy_handler }, +#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK + {"update_hook", db_update_hook }, + {"commit_hook", db_commit_hook }, + {"rollback_hook", db_rollback_hook }, +#endif + + {"prepare", db_prepare }, + {"rows", db_rows }, + {"urows", db_urows }, + {"nrows", db_nrows }, + + {"exec", db_exec }, + {"execute", db_exec }, + {"close", db_close }, + {"close_vm", db_close_vm }, + + {"__tostring", db_tostring }, + {"__gc", db_gc }, + + {NULL, NULL} +}; + +static const luaL_Reg vmlib[] = { + {"isopen", dbvm_isopen }, + + {"step", dbvm_step }, + {"reset", dbvm_reset }, + {"finalize", dbvm_finalize }, + + {"columns", dbvm_columns }, + + {"bind", dbvm_bind }, + {"bind_values", dbvm_bind_values }, + {"bind_names", dbvm_bind_names }, + {"bind_blob", dbvm_bind_blob }, + {"bind_parameter_count",dbvm_bind_parameter_count}, + {"bind_parameter_name", dbvm_bind_parameter_name}, + + {"get_value", dbvm_get_value }, + {"get_values", dbvm_get_values }, + {"get_name", dbvm_get_name }, + {"get_names", dbvm_get_names }, + {"get_type", dbvm_get_type }, + {"get_types", dbvm_get_types }, + {"get_uvalues", dbvm_get_uvalues }, + {"get_unames", dbvm_get_unames }, + {"get_utypes", dbvm_get_utypes }, + + {"get_named_values", dbvm_get_named_values }, + {"get_named_types", dbvm_get_named_types }, + + {"rows", dbvm_rows }, + {"urows", dbvm_urows }, + {"nrows", dbvm_nrows }, + + /* compatibility names (added by request) */ + {"idata", dbvm_get_values }, + {"inames", dbvm_get_names }, + {"itypes", dbvm_get_types }, + {"data", dbvm_get_named_values }, + {"type", dbvm_get_named_types }, + + {"__tostring", dbvm_tostring }, + {"__gc", dbvm_gc }, + + { NULL, NULL } +}; + +static const luaL_Reg ctxlib[] = { + {"user_data", lcontext_user_data }, + + {"get_aggregate_data", lcontext_get_aggregate_context }, + {"set_aggregate_data", lcontext_set_aggregate_context }, + {"aggregate_count", lcontext_aggregate_count }, + + {"result", lcontext_result }, + {"result_null", lcontext_result_null }, + {"result_number", lcontext_result_double }, + {"result_double", lcontext_result_double }, + {"result_int", lcontext_result_int }, + {"result_text", lcontext_result_text }, + {"result_blob", lcontext_result_blob }, + {"result_error", lcontext_result_error }, + + {"__tostring", lcontext_tostring }, + {NULL, NULL} +}; + +static const luaL_Reg sqlitelib[] = { + {"version", lsqlite_version }, + {"complete", lsqlite_complete }, +#ifndef WIN32 + {"temp_directory", lsqlite_temp_directory }, +#endif + {"open", lsqlite_open }, + {"open_memory", lsqlite_open_memory }, + + {"__newindex", lsqlite_newindex }, + {NULL, NULL} +}; + +static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) { + luaL_newmetatable(L, name); + lua_pushstring(L, "__index"); + lua_pushvalue(L, -2); /* push metatable */ + lua_rawset(L, -3); /* metatable.__index = metatable */ + + /* register metatable functions */ + luaL_openlib(L, NULL, lib, 0); + + /* remove metatable from stack */ + lua_pop(L, 1); +} + +LUALIB_API int luaopen_lsqlite3(lua_State *L) { + create_meta(L, sqlite_meta, dblib); + create_meta(L, sqlite_vm_meta, vmlib); + create_meta(L, sqlite_ctx_meta, ctxlib); + + luaL_getmetatable(L, sqlite_ctx_meta); + sqlite_ctx_meta_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + /* register (local) sqlite metatable */ + luaL_register(L, "sqlite3", sqlitelib); + + { + int i = 0; + /* add constants to global table */ + while (sqlite_constants[i].name) { + lua_pushstring(L, sqlite_constants[i].name); + lua_pushnumber(L, sqlite_constants[i].value); + lua_rawset(L, -3); + ++i; + } + } + + /* set sqlite's metatable to itself - set as readonly (__newindex) */ + lua_pushvalue(L, -1); + lua_setmetatable(L, -2); + + return 1; +} + + + + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + + + + diff --git a/src/SQLite/sqlite3.c b/src/SQLite/sqlite3.c new file mode 100644 index 000000000..37ee4ad38 --- /dev/null +++ b/src/SQLite/sqlite3.c @@ -0,0 +1,138114 @@ +/****************************************************************************** +** This file is an amalgamation of many separate C source files from SQLite +** version 3.7.16.1. By combining all the individual C code files into this +** single large file, the entire code can be compiled as a single translation +** unit. This allows many compilers to do optimizations that would not be +** possible if the files were compiled separately. Performance improvements +** of 5% or more are commonly seen when SQLite is compiled as a single +** translation unit. +** +** This file is all you need to compile SQLite. To use SQLite in other +** programs, you need this file and the "sqlite3.h" header file that defines +** the programming interface to the SQLite library. (If you do not have +** the "sqlite3.h" header file at hand, you will find a copy embedded within +** the text of this file. Search for "Begin file sqlite3.h" to find the start +** of the embedded sqlite3.h header file.) Additional code files may be needed +** if you want a wrapper to interface SQLite with your choice of programming +** language. The code for the "sqlite3" command-line shell is also in a +** separate file. This file contains only code for the core SQLite library. +*/ +#define SQLITE_CORE 1 +#define SQLITE_AMALGAMATION 1 +#ifndef SQLITE_PRIVATE +# define SQLITE_PRIVATE static +#endif +#ifndef SQLITE_API +# define SQLITE_API +#endif +/************** Begin file sqliteInt.h ***************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Internal interface definitions for SQLite. +** +*/ +#ifndef _SQLITEINT_H_ +#define _SQLITEINT_H_ + +/* +** These #defines should enable >2GB file support on POSIX if the +** underlying operating system supports it. If the OS lacks +** large file support, or if the OS is windows, these should be no-ops. +** +** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any +** system #includes. Hence, this block of code must be the very first +** code in all source files. +** +** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch +** on the compiler command line. This is necessary if you are compiling +** on a recent machine (ex: Red Hat 7.2) but you want your code to work +** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 +** without this option, LFS is enable. But LFS does not exist in the kernel +** in Red Hat 6.0, so the code won't work. Hence, for maximum binary +** portability you should omit LFS. +** +** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. +*/ +#ifndef SQLITE_DISABLE_LFS +# define _LARGE_FILE 1 +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +# define _LARGEFILE_SOURCE 1 +#endif + +/* +** Include the configuration header output by 'configure' if we're using the +** autoconf-based build +*/ +#ifdef _HAVE_SQLITE_CONFIG_H +#include "config.h" +#endif + +/************** Include sqliteLimit.h in the middle of sqliteInt.h ***********/ +/************** Begin file sqliteLimit.h *************************************/ +/* +** 2007 May 7 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file defines various limits of what SQLite can process. +*/ + +/* +** The maximum length of a TEXT or BLOB in bytes. This also +** limits the size of a row in a table or index. +** +** The hard limit is the ability of a 32-bit signed integer +** to count the size: 2^31-1 or 2147483647. +*/ +#ifndef SQLITE_MAX_LENGTH +# define SQLITE_MAX_LENGTH 1000000000 +#endif + +/* +** This is the maximum number of +** +** * Columns in a table +** * Columns in an index +** * Columns in a view +** * Terms in the SET clause of an UPDATE statement +** * Terms in the result set of a SELECT statement +** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement. +** * Terms in the VALUES clause of an INSERT statement +** +** The hard upper limit here is 32676. Most database people will +** tell you that in a well-normalized database, you usually should +** not have more than a dozen or so columns in any table. And if +** that is the case, there is no point in having more than a few +** dozen values in any of the other situations described above. +*/ +#ifndef SQLITE_MAX_COLUMN +# define SQLITE_MAX_COLUMN 2000 +#endif + +/* +** The maximum length of a single SQL statement in bytes. +** +** It used to be the case that setting this value to zero would +** turn the limit off. That is no longer true. It is not possible +** to turn this limit off. +*/ +#ifndef SQLITE_MAX_SQL_LENGTH +# define SQLITE_MAX_SQL_LENGTH 1000000000 +#endif + +/* +** The maximum depth of an expression tree. This is limited to +** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might +** want to place more severe limits on the complexity of an +** expression. +** +** A value of 0 used to mean that the limit was not enforced. +** But that is no longer true. The limit is now strictly enforced +** at all times. +*/ +#ifndef SQLITE_MAX_EXPR_DEPTH +# define SQLITE_MAX_EXPR_DEPTH 1000 +#endif + +/* +** The maximum number of terms in a compound SELECT statement. +** The code generator for compound SELECT statements does one +** level of recursion for each term. A stack overflow can result +** if the number of terms is too large. In practice, most SQL +** never has more than 3 or 4 terms. Use a value of 0 to disable +** any limit on the number of terms in a compount SELECT. +*/ +#ifndef SQLITE_MAX_COMPOUND_SELECT +# define SQLITE_MAX_COMPOUND_SELECT 500 +#endif + +/* +** The maximum number of opcodes in a VDBE program. +** Not currently enforced. +*/ +#ifndef SQLITE_MAX_VDBE_OP +# define SQLITE_MAX_VDBE_OP 25000 +#endif + +/* +** The maximum number of arguments to an SQL function. +*/ +#ifndef SQLITE_MAX_FUNCTION_ARG +# define SQLITE_MAX_FUNCTION_ARG 127 +#endif + +/* +** The maximum number of in-memory pages to use for the main database +** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE +*/ +#ifndef SQLITE_DEFAULT_CACHE_SIZE +# define SQLITE_DEFAULT_CACHE_SIZE 2000 +#endif +#ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE +# define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500 +#endif + +/* +** The default number of frames to accumulate in the log file before +** checkpointing the database in WAL mode. +*/ +#ifndef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT +# define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000 +#endif + +/* +** The maximum number of attached databases. This must be between 0 +** and 62. The upper bound on 62 is because a 64-bit integer bitmap +** is used internally to track attached databases. +*/ +#ifndef SQLITE_MAX_ATTACHED +# define SQLITE_MAX_ATTACHED 10 +#endif + + +/* +** The maximum value of a ?nnn wildcard that the parser will accept. +*/ +#ifndef SQLITE_MAX_VARIABLE_NUMBER +# define SQLITE_MAX_VARIABLE_NUMBER 999 +#endif + +/* Maximum page size. The upper bound on this value is 65536. This a limit +** imposed by the use of 16-bit offsets within each page. +** +** Earlier versions of SQLite allowed the user to change this value at +** compile time. This is no longer permitted, on the grounds that it creates +** a library that is technically incompatible with an SQLite library +** compiled with a different limit. If a process operating on a database +** with a page-size of 65536 bytes crashes, then an instance of SQLite +** compiled with the default page-size limit will not be able to rollback +** the aborted transaction. This could lead to database corruption. +*/ +#ifdef SQLITE_MAX_PAGE_SIZE +# undef SQLITE_MAX_PAGE_SIZE +#endif +#define SQLITE_MAX_PAGE_SIZE 65536 + + +/* +** The default size of a database page. +*/ +#ifndef SQLITE_DEFAULT_PAGE_SIZE +# define SQLITE_DEFAULT_PAGE_SIZE 1024 +#endif +#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE +# undef SQLITE_DEFAULT_PAGE_SIZE +# define SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE +#endif + +/* +** Ordinarily, if no value is explicitly provided, SQLite creates databases +** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain +** device characteristics (sector-size and atomic write() support), +** SQLite may choose a larger value. This constant is the maximum value +** SQLite will choose on its own. +*/ +#ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE +# define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192 +#endif +#if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE +# undef SQLITE_MAX_DEFAULT_PAGE_SIZE +# define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE +#endif + + +/* +** Maximum number of pages in one database file. +** +** This is really just the default value for the max_page_count pragma. +** This value can be lowered (or raised) at run-time using that the +** max_page_count macro. +*/ +#ifndef SQLITE_MAX_PAGE_COUNT +# define SQLITE_MAX_PAGE_COUNT 1073741823 +#endif + +/* +** Maximum length (in bytes) of the pattern in a LIKE or GLOB +** operator. +*/ +#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH +# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 +#endif + +/* +** Maximum depth of recursion for triggers. +** +** A value of 1 means that a trigger program will not be able to itself +** fire any triggers. A value of 0 means that no trigger programs at all +** may be executed. +*/ +#ifndef SQLITE_MAX_TRIGGER_DEPTH +# define SQLITE_MAX_TRIGGER_DEPTH 1000 +#endif + +/************** End of sqliteLimit.h *****************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + +/* Disable nuisance warnings on Borland compilers */ +#if defined(__BORLANDC__) +#pragma warn -rch /* unreachable code */ +#pragma warn -ccc /* Condition is always true or false */ +#pragma warn -aus /* Assigned value is never used */ +#pragma warn -csu /* Comparing signed and unsigned */ +#pragma warn -spa /* Suspicious pointer arithmetic */ +#endif + +/* Needed for various definitions... */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#if defined(__OpenBSD__) && !defined(_BSD_SOURCE) +# define _BSD_SOURCE +#endif + +/* +** Include standard header files as necessary +*/ +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_INTTYPES_H +#include +#endif + +/* +** The following macros are used to cast pointers to integers and +** integers to pointers. The way you do this varies from one compiler +** to the next, so we have developed the following set of #if statements +** to generate appropriate macros for a wide range of compilers. +** +** The correct "ANSI" way to do this is to use the intptr_t type. +** Unfortunately, that typedef is not available on all compilers, or +** if it is available, it requires an #include of specific headers +** that vary from one machine to the next. +** +** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on +** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)). +** So we have to define the macros in different ways depending on the +** compiler. +*/ +#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ +# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) +# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) +#elif !defined(__GNUC__) /* Works for compilers other than LLVM */ +# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) +# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) +#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */ +# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X)) +# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) +#else /* Generates a warning - but it always works */ +# define SQLITE_INT_TO_PTR(X) ((void*)(X)) +# define SQLITE_PTR_TO_INT(X) ((int)(X)) +#endif + +/* +** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. +** 0 means mutexes are permanently disable and the library is never +** threadsafe. 1 means the library is serialized which is the highest +** level of threadsafety. 2 means the libary is multithreaded - multiple +** threads can use SQLite as long as no two threads try to use the same +** database connection at the same time. +** +** Older versions of SQLite used an optional THREADSAFE macro. +** We support that for legacy. +*/ +#if !defined(SQLITE_THREADSAFE) +#if defined(THREADSAFE) +# define SQLITE_THREADSAFE THREADSAFE +#else +# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ +#endif +#endif + +/* +** Powersafe overwrite is on by default. But can be turned off using +** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option. +*/ +#ifndef SQLITE_POWERSAFE_OVERWRITE +# define SQLITE_POWERSAFE_OVERWRITE 1 +#endif + +/* +** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1. +** It determines whether or not the features related to +** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can +** be overridden at runtime using the sqlite3_config() API. +*/ +#if !defined(SQLITE_DEFAULT_MEMSTATUS) +# define SQLITE_DEFAULT_MEMSTATUS 1 +#endif + +/* +** Exactly one of the following macros must be defined in order to +** specify which memory allocation subsystem to use. +** +** SQLITE_SYSTEM_MALLOC // Use normal system malloc() +** SQLITE_WIN32_MALLOC // Use Win32 native heap API +** SQLITE_ZERO_MALLOC // Use a stub allocator that always fails +** SQLITE_MEMDEBUG // Debugging version of system malloc() +** +** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the +** assert() macro is enabled, each call into the Win32 native heap subsystem +** will cause HeapValidate to be called. If heap validation should fail, an +** assertion will be triggered. +** +** (Historical note: There used to be several other options, but we've +** pared it down to just these three.) +** +** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as +** the default. +*/ +#if defined(SQLITE_SYSTEM_MALLOC) \ + + defined(SQLITE_WIN32_MALLOC) \ + + defined(SQLITE_ZERO_MALLOC) \ + + defined(SQLITE_MEMDEBUG)>1 +# error "Two or more of the following compile-time configuration options\ + are defined but at most one is allowed:\ + SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG,\ + SQLITE_ZERO_MALLOC" +#endif +#if defined(SQLITE_SYSTEM_MALLOC) \ + + defined(SQLITE_WIN32_MALLOC) \ + + defined(SQLITE_ZERO_MALLOC) \ + + defined(SQLITE_MEMDEBUG)==0 +# define SQLITE_SYSTEM_MALLOC 1 +#endif + +/* +** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the +** sizes of memory allocations below this value where possible. +*/ +#if !defined(SQLITE_MALLOC_SOFT_LIMIT) +# define SQLITE_MALLOC_SOFT_LIMIT 1024 +#endif + +/* +** We need to define _XOPEN_SOURCE as follows in order to enable +** recursive mutexes on most Unix systems. But Mac OS X is different. +** The _XOPEN_SOURCE define causes problems for Mac OS X we are told, +** so it is omitted there. See ticket #2673. +** +** Later we learn that _XOPEN_SOURCE is poorly or incorrectly +** implemented on some systems. So we avoid defining it at all +** if it is already defined or if it is unneeded because we are +** not doing a threadsafe build. Ticket #2681. +** +** See also ticket #2741. +*/ +#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) \ + && !defined(__APPLE__) && SQLITE_THREADSAFE +# define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */ +#endif + +/* +** The TCL headers are only needed when compiling the TCL bindings. +*/ +#if defined(SQLITE_TCL) || defined(TCLSH) +# include +#endif + +/* +** NDEBUG and SQLITE_DEBUG are opposites. It should always be true that +** defined(NDEBUG)==!defined(SQLITE_DEBUG). If this is not currently true, +** make it true by defining or undefining NDEBUG. +** +** Setting NDEBUG makes the code smaller and run faster by disabling the +** number assert() statements in the code. So we want the default action +** to be for NDEBUG to be set and NDEBUG to be undefined only if SQLITE_DEBUG +** is set. Thus NDEBUG becomes an opt-in rather than an opt-out +** feature. +*/ +#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) +# define NDEBUG 1 +#endif +#if defined(NDEBUG) && defined(SQLITE_DEBUG) +# undef NDEBUG +#endif + +/* +** The testcase() macro is used to aid in coverage testing. When +** doing coverage testing, the condition inside the argument to +** testcase() must be evaluated both true and false in order to +** get full branch coverage. The testcase() macro is inserted +** to help ensure adequate test coverage in places where simple +** condition/decision coverage is inadequate. For example, testcase() +** can be used to make sure boundary values are tested. For +** bitmask tests, testcase() can be used to make sure each bit +** is significant and used at least once. On switch statements +** where multiple cases go to the same block of code, testcase() +** can insure that all cases are evaluated. +** +*/ +#ifdef SQLITE_COVERAGE_TEST +SQLITE_PRIVATE void sqlite3Coverage(int); +# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); } +#else +# define testcase(X) +#endif + +/* +** The TESTONLY macro is used to enclose variable declarations or +** other bits of code that are needed to support the arguments +** within testcase() and assert() macros. +*/ +#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST) +# define TESTONLY(X) X +#else +# define TESTONLY(X) +#endif + +/* +** Sometimes we need a small amount of code such as a variable initialization +** to setup for a later assert() statement. We do not want this code to +** appear when assert() is disabled. The following macro is therefore +** used to contain that setup code. The "VVA" acronym stands for +** "Verification, Validation, and Accreditation". In other words, the +** code within VVA_ONLY() will only run during verification processes. +*/ +#ifndef NDEBUG +# define VVA_ONLY(X) X +#else +# define VVA_ONLY(X) +#endif + +/* +** The ALWAYS and NEVER macros surround boolean expressions which +** are intended to always be true or false, respectively. Such +** expressions could be omitted from the code completely. But they +** are included in a few cases in order to enhance the resilience +** of SQLite to unexpected behavior - to make the code "self-healing" +** or "ductile" rather than being "brittle" and crashing at the first +** hint of unplanned behavior. +** +** In other words, ALWAYS and NEVER are added for defensive code. +** +** When doing coverage testing ALWAYS and NEVER are hard-coded to +** be true and false so that the unreachable code then specify will +** not be counted as untested code. +*/ +#if defined(SQLITE_COVERAGE_TEST) +# define ALWAYS(X) (1) +# define NEVER(X) (0) +#elif !defined(NDEBUG) +# define ALWAYS(X) ((X)?1:(assert(0),0)) +# define NEVER(X) ((X)?(assert(0),1):0) +#else +# define ALWAYS(X) (X) +# define NEVER(X) (X) +#endif + +/* +** Return true (non-zero) if the input is a integer that is too large +** to fit in 32-bits. This macro is used inside of various testcase() +** macros to verify that we have tested SQLite for large-file support. +*/ +#define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0) + +/* +** The macro unlikely() is a hint that surrounds a boolean +** expression that is usually false. Macro likely() surrounds +** a boolean expression that is usually true. GCC is able to +** use these hints to generate better code, sometimes. +*/ +#if defined(__GNUC__) && 0 +# define likely(X) __builtin_expect((X),1) +# define unlikely(X) __builtin_expect((X),0) +#else +# define likely(X) !!(X) +# define unlikely(X) !!(X) +#endif + +/************** Include sqlite3.h in the middle of sqliteInt.h ***************/ +/************** Begin file sqlite3.h *****************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the SQLite library +** presents to client programs. If a C-function, structure, datatype, +** or constant definition does not appear in this file, then it is +** not a published API of SQLite, is subject to change without +** notice, and should not be referenced by programs that use SQLite. +** +** Some of the definitions that are in this file are marked as +** "experimental". Experimental interfaces are normally new +** features recently added to SQLite. We do not anticipate changes +** to experimental interfaces but reserve the right to make minor changes +** if experience from use "in the wild" suggest such changes are prudent. +** +** The official C-language API documentation for SQLite is derived +** from comments in this file. This file is the authoritative source +** on how SQLite interfaces are suppose to operate. +** +** The name of this file under configuration management is "sqlite.h.in". +** The makefile makes some minor changes to this file (such as inserting +** the version number) and changes its name to "sqlite3.h" as +** part of the build process. +*/ +#ifndef _SQLITE3_H_ +#define _SQLITE3_H_ +#include /* Needed for the definition of va_list */ + +/* +** Make sure we can call this stuff from C++. +*/ +#if 0 +extern "C" { +#endif + + +/* +** Add the ability to override 'extern' +*/ +#ifndef SQLITE_EXTERN +# define SQLITE_EXTERN extern +#endif + +#ifndef SQLITE_API +# define SQLITE_API +#endif + + +/* +** These no-op macros are used in front of interfaces to mark those +** interfaces as either deprecated or experimental. New applications +** should not use deprecated interfaces - they are support for backwards +** compatibility only. Application writers should be aware that +** experimental interfaces are subject to change in point releases. +** +** These macros used to resolve to various kinds of compiler magic that +** would generate warning messages when they were used. But that +** compiler magic ended up generating such a flurry of bug reports +** that we have taken it all out and gone back to using simple +** noop macros. +*/ +#define SQLITE_DEPRECATED +#define SQLITE_EXPERIMENTAL + +/* +** Ensure these symbols were not defined by some previous header file. +*/ +#ifdef SQLITE_VERSION +# undef SQLITE_VERSION +#endif +#ifdef SQLITE_VERSION_NUMBER +# undef SQLITE_VERSION_NUMBER +#endif + +/* +** CAPI3REF: Compile-Time Library Version Numbers +** +** ^(The [SQLITE_VERSION] C preprocessor macro in the sqlite3.h header +** evaluates to a string literal that is the SQLite version in the +** format "X.Y.Z" where X is the major version number (always 3 for +** SQLite3) and Y is the minor version number and Z is the release number.)^ +** ^(The [SQLITE_VERSION_NUMBER] C preprocessor macro resolves to an integer +** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same +** numbers used in [SQLITE_VERSION].)^ +** The SQLITE_VERSION_NUMBER for any given release of SQLite will also +** be larger than the release from which it is derived. Either Y will +** be held constant and Z will be incremented or else Y will be incremented +** and Z will be reset to zero. +** +** Since version 3.6.18, SQLite source code has been stored in the +** Fossil configuration management +** system. ^The SQLITE_SOURCE_ID macro evaluates to +** a string which identifies a particular check-in of SQLite +** within its configuration management system. ^The SQLITE_SOURCE_ID +** string contains the date and time of the check-in (UTC) and an SHA1 +** hash of the entire source tree. +** +** See also: [sqlite3_libversion()], +** [sqlite3_libversion_number()], [sqlite3_sourceid()], +** [sqlite_version()] and [sqlite_source_id()]. +*/ +#define SQLITE_VERSION "3.7.16.1" +#define SQLITE_VERSION_NUMBER 3007016 +#define SQLITE_SOURCE_ID "2013-03-29 13:44:34 527231bc67285f01fb18d4451b28f61da3c4e39d" + +/* +** CAPI3REF: Run-Time Library Version Numbers +** KEYWORDS: sqlite3_version, sqlite3_sourceid +** +** These interfaces provide the same information as the [SQLITE_VERSION], +** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros +** but are associated with the library instead of the header file. ^(Cautious +** programmers might include assert() statements in their application to +** verify that values returned by these interfaces match the macros in +** the header, and thus insure that the application is +** compiled with matching library and header files. +** +**
+** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
+** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );
+** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
+** 
)^ +** +** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION] +** macro. ^The sqlite3_libversion() function returns a pointer to the +** to the sqlite3_version[] string constant. The sqlite3_libversion() +** function is provided for use in DLLs since DLL users usually do not have +** direct access to string constants within the DLL. ^The +** sqlite3_libversion_number() function returns an integer equal to +** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns +** a pointer to a string constant whose value is the same as the +** [SQLITE_SOURCE_ID] C preprocessor macro. +** +** See also: [sqlite_version()] and [sqlite_source_id()]. +*/ +SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; +SQLITE_API const char *sqlite3_libversion(void); +SQLITE_API const char *sqlite3_sourceid(void); +SQLITE_API int sqlite3_libversion_number(void); + +/* +** CAPI3REF: Run-Time Library Compilation Options Diagnostics +** +** ^The sqlite3_compileoption_used() function returns 0 or 1 +** indicating whether the specified option was defined at +** compile time. ^The SQLITE_ prefix may be omitted from the +** option name passed to sqlite3_compileoption_used(). +** +** ^The sqlite3_compileoption_get() function allows iterating +** over the list of options that were defined at compile time by +** returning the N-th compile time option string. ^If N is out of range, +** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ +** prefix is omitted from any strings returned by +** sqlite3_compileoption_get(). +** +** ^Support for the diagnostic functions sqlite3_compileoption_used() +** and sqlite3_compileoption_get() may be omitted by specifying the +** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. +** +** See also: SQL functions [sqlite_compileoption_used()] and +** [sqlite_compileoption_get()] and the [compile_options pragma]. +*/ +#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS +SQLITE_API int sqlite3_compileoption_used(const char *zOptName); +SQLITE_API const char *sqlite3_compileoption_get(int N); +#endif + +/* +** CAPI3REF: Test To See If The Library Is Threadsafe +** +** ^The sqlite3_threadsafe() function returns zero if and only if +** SQLite was compiled with mutexing code omitted due to the +** [SQLITE_THREADSAFE] compile-time option being set to 0. +** +** SQLite can be compiled with or without mutexes. When +** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes +** are enabled and SQLite is threadsafe. When the +** [SQLITE_THREADSAFE] macro is 0, +** the mutexes are omitted. Without the mutexes, it is not safe +** to use SQLite concurrently from more than one thread. +** +** Enabling mutexes incurs a measurable performance penalty. +** So if speed is of utmost importance, it makes sense to disable +** the mutexes. But for maximum safety, mutexes should be enabled. +** ^The default behavior is for mutexes to be enabled. +** +** This interface can be used by an application to make sure that the +** version of SQLite that it is linking against was compiled with +** the desired setting of the [SQLITE_THREADSAFE] macro. +** +** This interface only reports on the compile-time mutex setting +** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with +** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but +** can be fully or partially disabled using a call to [sqlite3_config()] +** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], +** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the +** sqlite3_threadsafe() function shows only the compile-time setting of +** thread safety, not any run-time changes to that setting made by +** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() +** is unchanged by calls to sqlite3_config().)^ +** +** See the [threading mode] documentation for additional information. +*/ +SQLITE_API int sqlite3_threadsafe(void); + +/* +** CAPI3REF: Database Connection Handle +** KEYWORDS: {database connection} {database connections} +** +** Each open SQLite database is represented by a pointer to an instance of +** the opaque structure named "sqlite3". It is useful to think of an sqlite3 +** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and +** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()] +** and [sqlite3_close_v2()] are its destructors. There are many other +** interfaces (such as +** [sqlite3_prepare_v2()], [sqlite3_create_function()], and +** [sqlite3_busy_timeout()] to name but three) that are methods on an +** sqlite3 object. +*/ +typedef struct sqlite3 sqlite3; + +/* +** CAPI3REF: 64-Bit Integer Types +** KEYWORDS: sqlite_int64 sqlite_uint64 +** +** Because there is no cross-platform way to specify 64-bit integer types +** SQLite includes typedefs for 64-bit signed and unsigned integers. +** +** The sqlite3_int64 and sqlite3_uint64 are the preferred type definitions. +** The sqlite_int64 and sqlite_uint64 types are supported for backwards +** compatibility only. +** +** ^The sqlite3_int64 and sqlite_int64 types can store integer values +** between -9223372036854775808 and +9223372036854775807 inclusive. ^The +** sqlite3_uint64 and sqlite_uint64 types can store integer values +** between 0 and +18446744073709551615 inclusive. +*/ +#ifdef SQLITE_INT64_TYPE + typedef SQLITE_INT64_TYPE sqlite_int64; + typedef unsigned SQLITE_INT64_TYPE sqlite_uint64; +#elif defined(_MSC_VER) || defined(__BORLANDC__) + typedef __int64 sqlite_int64; + typedef unsigned __int64 sqlite_uint64; +#else + typedef long long int sqlite_int64; + typedef unsigned long long int sqlite_uint64; +#endif +typedef sqlite_int64 sqlite3_int64; +typedef sqlite_uint64 sqlite3_uint64; + +/* +** If compiling for a processor that lacks floating point support, +** substitute integer for floating-point. +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# define double sqlite3_int64 +#endif + +/* +** CAPI3REF: Closing A Database Connection +** +** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors +** for the [sqlite3] object. +** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if +** the [sqlite3] object is successfully destroyed and all associated +** resources are deallocated. +** +** ^If the database connection is associated with unfinalized prepared +** statements or unfinished sqlite3_backup objects then sqlite3_close() +** will leave the database connection open and return [SQLITE_BUSY]. +** ^If sqlite3_close_v2() is called with unfinalized prepared statements +** and unfinished sqlite3_backups, then the database connection becomes +** an unusable "zombie" which will automatically be deallocated when the +** last prepared statement is finalized or the last sqlite3_backup is +** finished. The sqlite3_close_v2() interface is intended for use with +** host languages that are garbage collected, and where the order in which +** destructors are called is arbitrary. +** +** Applications should [sqlite3_finalize | finalize] all [prepared statements], +** [sqlite3_blob_close | close] all [BLOB handles], and +** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated +** with the [sqlite3] object prior to attempting to close the object. ^If +** sqlite3_close_v2() is called on a [database connection] that still has +** outstanding [prepared statements], [BLOB handles], and/or +** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation +** of resources is deferred until all [prepared statements], [BLOB handles], +** and [sqlite3_backup] objects are also destroyed. +** +** ^If an [sqlite3] object is destroyed while a transaction is open, +** the transaction is automatically rolled back. +** +** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] +** must be either a NULL +** pointer or an [sqlite3] object pointer obtained +** from [sqlite3_open()], [sqlite3_open16()], or +** [sqlite3_open_v2()], and not previously closed. +** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer +** argument is a harmless no-op. +*/ +SQLITE_API int sqlite3_close(sqlite3*); +SQLITE_API int sqlite3_close_v2(sqlite3*); + +/* +** The type for a callback function. +** This is legacy and deprecated. It is included for historical +** compatibility and is not documented. +*/ +typedef int (*sqlite3_callback)(void*,int,char**, char**); + +/* +** CAPI3REF: One-Step Query Execution Interface +** +** The sqlite3_exec() interface is a convenience wrapper around +** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], +** that allows an application to run multiple statements of SQL +** without having to use a lot of C code. +** +** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, +** semicolon-separate SQL statements passed into its 2nd argument, +** in the context of the [database connection] passed in as its 1st +** argument. ^If the callback function of the 3rd argument to +** sqlite3_exec() is not NULL, then it is invoked for each result row +** coming out of the evaluated SQL statements. ^The 4th argument to +** sqlite3_exec() is relayed through to the 1st argument of each +** callback invocation. ^If the callback pointer to sqlite3_exec() +** is NULL, then no callback is ever invoked and result rows are +** ignored. +** +** ^If an error occurs while evaluating the SQL statements passed into +** sqlite3_exec(), then execution of the current statement stops and +** subsequent statements are skipped. ^If the 5th parameter to sqlite3_exec() +** is not NULL then any error message is written into memory obtained +** from [sqlite3_malloc()] and passed back through the 5th parameter. +** To avoid memory leaks, the application should invoke [sqlite3_free()] +** on error message strings returned through the 5th parameter of +** of sqlite3_exec() after the error message string is no longer needed. +** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors +** occur, then sqlite3_exec() sets the pointer in its 5th parameter to +** NULL before returning. +** +** ^If an sqlite3_exec() callback returns non-zero, the sqlite3_exec() +** routine returns SQLITE_ABORT without invoking the callback again and +** without running any subsequent SQL statements. +** +** ^The 2nd argument to the sqlite3_exec() callback function is the +** number of columns in the result. ^The 3rd argument to the sqlite3_exec() +** callback is an array of pointers to strings obtained as if from +** [sqlite3_column_text()], one for each column. ^If an element of a +** result row is NULL then the corresponding string pointer for the +** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the +** sqlite3_exec() callback is an array of pointers to strings where each +** entry represents the name of corresponding result column as obtained +** from [sqlite3_column_name()]. +** +** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer +** to an empty string, or a pointer that contains only whitespace and/or +** SQL comments, then no SQL statements are evaluated and the database +** is not changed. +** +** Restrictions: +** +**
    +**
  • The application must insure that the 1st parameter to sqlite3_exec() +** is a valid and open [database connection]. +**
  • The application must not close [database connection] specified by +** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. +**
  • The application must not modify the SQL statement text passed into +** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. +**
+*/ +SQLITE_API int sqlite3_exec( + sqlite3*, /* An open database */ + const char *sql, /* SQL to be evaluated */ + int (*callback)(void*,int,char**,char**), /* Callback function */ + void *, /* 1st argument to callback */ + char **errmsg /* Error msg written here */ +); + +/* +** CAPI3REF: Result Codes +** KEYWORDS: SQLITE_OK {error code} {error codes} +** KEYWORDS: {result code} {result codes} +** +** Many SQLite functions return an integer result code from the set shown +** here in order to indicate success or failure. +** +** New error codes may be added in future versions of SQLite. +** +** See also: [SQLITE_IOERR_READ | extended result codes], +** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes]. +*/ +#define SQLITE_OK 0 /* Successful result */ +/* beginning-of-error-codes */ +#define SQLITE_ERROR 1 /* SQL error or missing database */ +#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ +#define SQLITE_PERM 3 /* Access permission denied */ +#define SQLITE_ABORT 4 /* Callback routine requested an abort */ +#define SQLITE_BUSY 5 /* The database file is locked */ +#define SQLITE_LOCKED 6 /* A table in the database is locked */ +#define SQLITE_NOMEM 7 /* A malloc() failed */ +#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ +#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ +#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ +#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ +#define SQLITE_FULL 13 /* Insertion failed because database is full */ +#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ +#define SQLITE_EMPTY 16 /* Database is empty */ +#define SQLITE_SCHEMA 17 /* The database schema changed */ +#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ +#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ +#define SQLITE_MISMATCH 20 /* Data type mismatch */ +#define SQLITE_MISUSE 21 /* Library used incorrectly */ +#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ +#define SQLITE_AUTH 23 /* Authorization denied */ +#define SQLITE_FORMAT 24 /* Auxiliary database format error */ +#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ +#define SQLITE_NOTADB 26 /* File opened that is not a database file */ +#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ +#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ +/* end-of-error-codes */ + +/* +** CAPI3REF: Extended Result Codes +** KEYWORDS: {extended error code} {extended error codes} +** KEYWORDS: {extended result code} {extended result codes} +** +** In its default configuration, SQLite API routines return one of 26 integer +** [SQLITE_OK | result codes]. However, experience has shown that many of +** these result codes are too coarse-grained. They do not provide as +** much information about problems as programmers might like. In an effort to +** address this, newer versions of SQLite (version 3.3.8 and later) include +** support for additional result codes that provide more detailed information +** about errors. The extended result codes are enabled or disabled +** on a per database connection basis using the +** [sqlite3_extended_result_codes()] API. +** +** Some of the available extended result codes are listed here. +** One may expect the number of extended result codes will be expand +** over time. Software that uses extended result codes should expect +** to see new result codes in future releases of SQLite. +** +** The SQLITE_OK result code will never be extended. It will always +** be exactly zero. +*/ +#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) +#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) +#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) +#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) +#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) +#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) +#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) +#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8)) +#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8)) +#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8)) +#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8)) +#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8)) +#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) +#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) +#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) +#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) +#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) +#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) +#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) +#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) +#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) +#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) +#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) +#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) +#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) +#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) +#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) +#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) +#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) +#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) +#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) +#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) +#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) +#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8)) +#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8)) +#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8)) +#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8)) +#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) +#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) +#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) +#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) +#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) + +/* +** CAPI3REF: Flags For File Open Operations +** +** These bit values are intended for use in the +** 3rd parameter to the [sqlite3_open_v2()] interface and +** in the 4th parameter to the [sqlite3_vfs.xOpen] method. +*/ +#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ +#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ +#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */ +#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_MEMORY 0x00000080 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */ +#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */ +#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */ +#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800 /* VFS only */ +#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */ +#define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */ +#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ +#define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ + +/* Reserved: 0x00F00000 */ + +/* +** CAPI3REF: Device Characteristics +** +** The xDeviceCharacteristics method of the [sqlite3_io_methods] +** object returns an integer which is a vector of these +** bit values expressing I/O characteristics of the mass storage +** device that holds the file that the [sqlite3_io_methods] +** refers to. +** +** The SQLITE_IOCAP_ATOMIC property means that all writes of +** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values +** mean that writes of blocks that are nnn bytes in size and +** are aligned to an address which is an integer multiple of +** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means +** that when data is appended to a file, the data is appended +** first then the size of the file is extended, never the other +** way around. The SQLITE_IOCAP_SEQUENTIAL property means that +** information is written to disk in the same order as calls +** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that +** after reboot following a crash or power loss, the only bytes in a +** file that were written at the application level might have changed +** and that adjacent bytes, even bytes within the same sector are +** guaranteed to be unchanged. +*/ +#define SQLITE_IOCAP_ATOMIC 0x00000001 +#define SQLITE_IOCAP_ATOMIC512 0x00000002 +#define SQLITE_IOCAP_ATOMIC1K 0x00000004 +#define SQLITE_IOCAP_ATOMIC2K 0x00000008 +#define SQLITE_IOCAP_ATOMIC4K 0x00000010 +#define SQLITE_IOCAP_ATOMIC8K 0x00000020 +#define SQLITE_IOCAP_ATOMIC16K 0x00000040 +#define SQLITE_IOCAP_ATOMIC32K 0x00000080 +#define SQLITE_IOCAP_ATOMIC64K 0x00000100 +#define SQLITE_IOCAP_SAFE_APPEND 0x00000200 +#define SQLITE_IOCAP_SEQUENTIAL 0x00000400 +#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 +#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 + +/* +** CAPI3REF: File Locking Levels +** +** SQLite uses one of these integer values as the second +** argument to calls it makes to the xLock() and xUnlock() methods +** of an [sqlite3_io_methods] object. +*/ +#define SQLITE_LOCK_NONE 0 +#define SQLITE_LOCK_SHARED 1 +#define SQLITE_LOCK_RESERVED 2 +#define SQLITE_LOCK_PENDING 3 +#define SQLITE_LOCK_EXCLUSIVE 4 + +/* +** CAPI3REF: Synchronization Type Flags +** +** When SQLite invokes the xSync() method of an +** [sqlite3_io_methods] object it uses a combination of +** these integer values as the second argument. +** +** When the SQLITE_SYNC_DATAONLY flag is used, it means that the +** sync operation only needs to flush data to mass storage. Inode +** information need not be flushed. If the lower four bits of the flag +** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics. +** If the lower four bits equal SQLITE_SYNC_FULL, that means +** to use Mac OS X style fullsync instead of fsync(). +** +** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags +** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL +** settings. The [synchronous pragma] determines when calls to the +** xSync VFS method occur and applies uniformly across all platforms. +** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how +** energetic or rigorous or forceful the sync operations are and +** only make a difference on Mac OSX for the default SQLite code. +** (Third-party VFS implementations might also make the distinction +** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the +** operating systems natively supported by SQLite, only Mac OSX +** cares about the difference.) +*/ +#define SQLITE_SYNC_NORMAL 0x00002 +#define SQLITE_SYNC_FULL 0x00003 +#define SQLITE_SYNC_DATAONLY 0x00010 + +/* +** CAPI3REF: OS Interface Open File Handle +** +** An [sqlite3_file] object represents an open file in the +** [sqlite3_vfs | OS interface layer]. Individual OS interface +** implementations will +** want to subclass this object by appending additional fields +** for their own use. The pMethods entry is a pointer to an +** [sqlite3_io_methods] object that defines methods for performing +** I/O operations on the open file. +*/ +typedef struct sqlite3_file sqlite3_file; +struct sqlite3_file { + const struct sqlite3_io_methods *pMethods; /* Methods for an open file */ +}; + +/* +** CAPI3REF: OS Interface File Virtual Methods Object +** +** Every file opened by the [sqlite3_vfs.xOpen] method populates an +** [sqlite3_file] object (or, more commonly, a subclass of the +** [sqlite3_file] object) with a pointer to an instance of this object. +** This object defines the methods used to perform various operations +** against the open file represented by the [sqlite3_file] object. +** +** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element +** to a non-NULL pointer, then the sqlite3_io_methods.xClose method +** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The +** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen] +** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element +** to NULL. +** +** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or +** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). +** The second choice is a Mac OS X style fullsync. The [SQLITE_SYNC_DATAONLY] +** flag may be ORed in to indicate that only the data of the file +** and not its inode needs to be synced. +** +** The integer values to xLock() and xUnlock() are one of +**
    +**
  • [SQLITE_LOCK_NONE], +**
  • [SQLITE_LOCK_SHARED], +**
  • [SQLITE_LOCK_RESERVED], +**
  • [SQLITE_LOCK_PENDING], or +**
  • [SQLITE_LOCK_EXCLUSIVE]. +**
+** xLock() increases the lock. xUnlock() decreases the lock. +** The xCheckReservedLock() method checks whether any database connection, +** either in this process or in some other process, is holding a RESERVED, +** PENDING, or EXCLUSIVE lock on the file. It returns true +** if such a lock exists and false otherwise. +** +** The xFileControl() method is a generic interface that allows custom +** VFS implementations to directly control an open file using the +** [sqlite3_file_control()] interface. The second "op" argument is an +** integer opcode. The third argument is a generic pointer intended to +** point to a structure that may contain arguments or space in which to +** write return values. Potential uses for xFileControl() might be +** functions to enable blocking locks with timeouts, to change the +** locking strategy (for example to use dot-file locks), to inquire +** about the status of a lock, or to break stale locks. The SQLite +** core reserves all opcodes less than 100 for its own use. +** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available. +** Applications that define a custom xFileControl method should use opcodes +** greater than 100 to avoid conflicts. VFS implementations should +** return [SQLITE_NOTFOUND] for file control opcodes that they do not +** recognize. +** +** The xSectorSize() method returns the sector size of the +** device that underlies the file. The sector size is the +** minimum write that can be performed without disturbing +** other bytes in the file. The xDeviceCharacteristics() +** method returns a bit vector describing behaviors of the +** underlying device: +** +**
    +**
  • [SQLITE_IOCAP_ATOMIC] +**
  • [SQLITE_IOCAP_ATOMIC512] +**
  • [SQLITE_IOCAP_ATOMIC1K] +**
  • [SQLITE_IOCAP_ATOMIC2K] +**
  • [SQLITE_IOCAP_ATOMIC4K] +**
  • [SQLITE_IOCAP_ATOMIC8K] +**
  • [SQLITE_IOCAP_ATOMIC16K] +**
  • [SQLITE_IOCAP_ATOMIC32K] +**
  • [SQLITE_IOCAP_ATOMIC64K] +**
  • [SQLITE_IOCAP_SAFE_APPEND] +**
  • [SQLITE_IOCAP_SEQUENTIAL] +**
+** +** The SQLITE_IOCAP_ATOMIC property means that all writes of +** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values +** mean that writes of blocks that are nnn bytes in size and +** are aligned to an address which is an integer multiple of +** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means +** that when data is appended to a file, the data is appended +** first then the size of the file is extended, never the other +** way around. The SQLITE_IOCAP_SEQUENTIAL property means that +** information is written to disk in the same order as calls +** to xWrite(). +** +** If xRead() returns SQLITE_IOERR_SHORT_READ it must also fill +** in the unread portions of the buffer with zeros. A VFS that +** fails to zero-fill short reads might seem to work. However, +** failure to zero-fill short reads will eventually lead to +** database corruption. +*/ +typedef struct sqlite3_io_methods sqlite3_io_methods; +struct sqlite3_io_methods { + int iVersion; + int (*xClose)(sqlite3_file*); + int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); + int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst); + int (*xTruncate)(sqlite3_file*, sqlite3_int64 size); + int (*xSync)(sqlite3_file*, int flags); + int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize); + int (*xLock)(sqlite3_file*, int); + int (*xUnlock)(sqlite3_file*, int); + int (*xCheckReservedLock)(sqlite3_file*, int *pResOut); + int (*xFileControl)(sqlite3_file*, int op, void *pArg); + int (*xSectorSize)(sqlite3_file*); + int (*xDeviceCharacteristics)(sqlite3_file*); + /* Methods above are valid for version 1 */ + int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**); + int (*xShmLock)(sqlite3_file*, int offset, int n, int flags); + void (*xShmBarrier)(sqlite3_file*); + int (*xShmUnmap)(sqlite3_file*, int deleteFlag); + /* Methods above are valid for version 2 */ + /* Additional methods may be added in future releases */ +}; + +/* +** CAPI3REF: Standard File Control Opcodes +** +** These integer constants are opcodes for the xFileControl method +** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] +** interface. +** +** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This +** opcode causes the xFileControl method to write the current state of +** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], +** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) +** into an integer that the pArg argument points to. This capability +** is used during testing and only needs to be supported when SQLITE_TEST +** is defined. +**
    +**
  • [[SQLITE_FCNTL_SIZE_HINT]] +** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS +** layer a hint of how large the database file will grow to be during the +** current transaction. This hint is not guaranteed to be accurate but it +** is often close. The underlying VFS might choose to preallocate database +** file space based on this hint in order to help writes to the database +** file run faster. +** +**
  • [[SQLITE_FCNTL_CHUNK_SIZE]] +** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS +** extends and truncates the database file in chunks of a size specified +** by the user. The fourth argument to [sqlite3_file_control()] should +** point to an integer (type int) containing the new chunk-size to use +** for the nominated database. Allocating database file space in large +** chunks (say 1MB at a time), may reduce file-system fragmentation and +** improve performance on some systems. +** +**
  • [[SQLITE_FCNTL_FILE_POINTER]] +** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer +** to the [sqlite3_file] object associated with a particular database +** connection. See the [sqlite3_file_control()] documentation for +** additional information. +** +**
  • [[SQLITE_FCNTL_SYNC_OMITTED]] +** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by +** SQLite and sent to all VFSes in place of a call to the xSync method +** when the database connection has [PRAGMA synchronous] set to OFF.)^ +** Some specialized VFSes need this signal in order to operate correctly +** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most +** VFSes do not need this signal and should silently ignore this opcode. +** Applications should not call [sqlite3_file_control()] with this +** opcode as doing so may disrupt the operation of the specialized VFSes +** that do require it. +** +**
  • [[SQLITE_FCNTL_WIN32_AV_RETRY]] +** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic +** retry counts and intervals for certain disk I/O operations for the +** windows [VFS] in order to provide robustness in the presence of +** anti-virus programs. By default, the windows VFS will retry file read, +** file write, and file delete operations up to 10 times, with a delay +** of 25 milliseconds before the first retry and with the delay increasing +** by an additional 25 milliseconds with each subsequent retry. This +** opcode allows these two values (10 retries and 25 milliseconds of delay) +** to be adjusted. The values are changed for all database connections +** within the same process. The argument is a pointer to an array of two +** integers where the first integer i the new retry count and the second +** integer is the delay. If either integer is negative, then the setting +** is not changed but instead the prior value of that setting is written +** into the array entry, allowing the current retry settings to be +** interrogated. The zDbName parameter is ignored. +** +**
  • [[SQLITE_FCNTL_PERSIST_WAL]] +** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the +** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary +** write ahead log and shared memory files used for transaction control +** are automatically deleted when the latest connection to the database +** closes. Setting persistent WAL mode causes those files to persist after +** close. Persisting the files is useful when other processes that do not +** have write permission on the directory containing the database file want +** to read the database file, as the WAL and shared memory files must exist +** in order for the database to be readable. The fourth parameter to +** [sqlite3_file_control()] for this opcode should be a pointer to an integer. +** That integer is 0 to disable persistent WAL mode or 1 to enable persistent +** WAL mode. If the integer is -1, then it is overwritten with the current +** WAL persistence setting. +** +**
  • [[SQLITE_FCNTL_POWERSAFE_OVERWRITE]] +** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the +** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting +** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the +** xDeviceCharacteristics methods. The fourth parameter to +** [sqlite3_file_control()] for this opcode should be a pointer to an integer. +** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage +** mode. If the integer is -1, then it is overwritten with the current +** zero-damage mode setting. +** +**
  • [[SQLITE_FCNTL_OVERWRITE]] +** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening +** a write transaction to indicate that, unless it is rolled back for some +** reason, the entire database file will be overwritten by the current +** transaction. This is used by VACUUM operations. +** +**
  • [[SQLITE_FCNTL_VFSNAME]] +** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of +** all [VFSes] in the VFS stack. The names are of all VFS shims and the +** final bottom-level VFS are written into memory obtained from +** [sqlite3_malloc()] and the result is stored in the char* variable +** that the fourth parameter of [sqlite3_file_control()] points to. +** The caller is responsible for freeing the memory when done. As with +** all file-control actions, there is no guarantee that this will actually +** do anything. Callers should initialize the char* variable to a NULL +** pointer in case this file-control is not implemented. This file-control +** is intended for diagnostic use only. +** +**
  • [[SQLITE_FCNTL_PRAGMA]] +** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA] +** file control is sent to the open [sqlite3_file] object corresponding +** to the database file to which the pragma statement refers. ^The argument +** to the [SQLITE_FCNTL_PRAGMA] file control is an array of +** pointers to strings (char**) in which the second element of the array +** is the name of the pragma and the third element is the argument to the +** pragma or NULL if the pragma has no argument. ^The handler for an +** [SQLITE_FCNTL_PRAGMA] file control can optionally make the first element +** of the char** argument point to a string obtained from [sqlite3_mprintf()] +** or the equivalent and that string will become the result of the pragma or +** the error message if the pragma fails. ^If the +** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal +** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA] +** file control returns [SQLITE_OK], then the parser assumes that the +** VFS has handled the PRAGMA itself and the parser generates a no-op +** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns +** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means +** that the VFS encountered an error while handling the [PRAGMA] and the +** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] +** file control occurs at the beginning of pragma statement analysis and so +** it is able to override built-in [PRAGMA] statements. +** +**
  • [[SQLITE_FCNTL_BUSYHANDLER]] +** ^This file-control may be invoked by SQLite on the database file handle +** shortly after it is opened in order to provide a custom VFS with access +** to the connections busy-handler callback. The argument is of type (void **) +** - an array of two (void *) values. The first (void *) actually points +** to a function of type (int (*)(void *)). In order to invoke the connections +** busy-handler, this function should be invoked with the second (void *) in +** the array as the only argument. If it returns non-zero, then the operation +** should be retried. If it returns zero, the custom VFS should abandon the +** current operation. +** +**
  • [[SQLITE_FCNTL_TEMPFILENAME]] +** ^Application can invoke this file-control to have SQLite generate a +** temporary filename using the same algorithm that is followed to generate +** temporary filenames for TEMP tables and other internal uses. The +** argument should be a char** which will be filled with the filename +** written into memory obtained from [sqlite3_malloc()]. The caller should +** invoke [sqlite3_free()] on the result to avoid a memory leak. +** +**
+*/ +#define SQLITE_FCNTL_LOCKSTATE 1 +#define SQLITE_GET_LOCKPROXYFILE 2 +#define SQLITE_SET_LOCKPROXYFILE 3 +#define SQLITE_LAST_ERRNO 4 +#define SQLITE_FCNTL_SIZE_HINT 5 +#define SQLITE_FCNTL_CHUNK_SIZE 6 +#define SQLITE_FCNTL_FILE_POINTER 7 +#define SQLITE_FCNTL_SYNC_OMITTED 8 +#define SQLITE_FCNTL_WIN32_AV_RETRY 9 +#define SQLITE_FCNTL_PERSIST_WAL 10 +#define SQLITE_FCNTL_OVERWRITE 11 +#define SQLITE_FCNTL_VFSNAME 12 +#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 +#define SQLITE_FCNTL_PRAGMA 14 +#define SQLITE_FCNTL_BUSYHANDLER 15 +#define SQLITE_FCNTL_TEMPFILENAME 16 + +/* +** CAPI3REF: Mutex Handle +** +** The mutex module within SQLite defines [sqlite3_mutex] to be an +** abstract type for a mutex object. The SQLite core never looks +** at the internal representation of an [sqlite3_mutex]. It only +** deals with pointers to the [sqlite3_mutex] object. +** +** Mutexes are created using [sqlite3_mutex_alloc()]. +*/ +typedef struct sqlite3_mutex sqlite3_mutex; + +/* +** CAPI3REF: OS Interface Object +** +** An instance of the sqlite3_vfs object defines the interface between +** the SQLite core and the underlying operating system. The "vfs" +** in the name of the object stands for "virtual file system". See +** the [VFS | VFS documentation] for further information. +** +** The value of the iVersion field is initially 1 but may be larger in +** future versions of SQLite. Additional fields may be appended to this +** object when the iVersion value is increased. Note that the structure +** of the sqlite3_vfs object changes in the transaction between +** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not +** modified. +** +** The szOsFile field is the size of the subclassed [sqlite3_file] +** structure used by this VFS. mxPathname is the maximum length of +** a pathname in this VFS. +** +** Registered sqlite3_vfs objects are kept on a linked list formed by +** the pNext pointer. The [sqlite3_vfs_register()] +** and [sqlite3_vfs_unregister()] interfaces manage this list +** in a thread-safe way. The [sqlite3_vfs_find()] interface +** searches the list. Neither the application code nor the VFS +** implementation should use the pNext pointer. +** +** The pNext field is the only field in the sqlite3_vfs +** structure that SQLite will ever modify. SQLite will only access +** or modify this field while holding a particular static mutex. +** The application should never modify anything within the sqlite3_vfs +** object once the object has been registered. +** +** The zName field holds the name of the VFS module. The name must +** be unique across all VFS modules. +** +** [[sqlite3_vfs.xOpen]] +** ^SQLite guarantees that the zFilename parameter to xOpen +** is either a NULL pointer or string obtained +** from xFullPathname() with an optional suffix added. +** ^If a suffix is added to the zFilename parameter, it will +** consist of a single "-" character followed by no more than +** 11 alphanumeric and/or "-" characters. +** ^SQLite further guarantees that +** the string will be valid and unchanged until xClose() is +** called. Because of the previous sentence, +** the [sqlite3_file] can safely store a pointer to the +** filename if it needs to remember the filename for some reason. +** If the zFilename parameter to xOpen is a NULL pointer then xOpen +** must invent its own temporary name for the file. ^Whenever the +** xFilename parameter is NULL it will also be the case that the +** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. +** +** The flags argument to xOpen() includes all bits set in +** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] +** or [sqlite3_open16()] is used, then flags includes at least +** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. +** If xOpen() opens a file read-only then it sets *pOutFlags to +** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. +** +** ^(SQLite will also add one of the following flags to the xOpen() +** call, depending on the object being opened: +** +**
    +**
  • [SQLITE_OPEN_MAIN_DB] +**
  • [SQLITE_OPEN_MAIN_JOURNAL] +**
  • [SQLITE_OPEN_TEMP_DB] +**
  • [SQLITE_OPEN_TEMP_JOURNAL] +**
  • [SQLITE_OPEN_TRANSIENT_DB] +**
  • [SQLITE_OPEN_SUBJOURNAL] +**
  • [SQLITE_OPEN_MASTER_JOURNAL] +**
  • [SQLITE_OPEN_WAL] +**
)^ +** +** The file I/O implementation can use the object type flags to +** change the way it deals with files. For example, an application +** that does not care about crash recovery or rollback might make +** the open of a journal file a no-op. Writes to this journal would +** also be no-ops, and any attempt to read the journal would return +** SQLITE_IOERR. Or the implementation might recognize that a database +** file will be doing page-aligned sector reads and writes in a random +** order and set up its I/O subsystem accordingly. +** +** SQLite might also add one of the following flags to the xOpen method: +** +**
    +**
  • [SQLITE_OPEN_DELETEONCLOSE] +**
  • [SQLITE_OPEN_EXCLUSIVE] +**
+** +** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be +** deleted when it is closed. ^The [SQLITE_OPEN_DELETEONCLOSE] +** will be set for TEMP databases and their journals, transient +** databases, and subjournals. +** +** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction +** with the [SQLITE_OPEN_CREATE] flag, which are both directly +** analogous to the O_EXCL and O_CREAT flags of the POSIX open() +** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the +** SQLITE_OPEN_CREATE, is used to indicate that file should always +** be created, and that it is an error if it already exists. +** It is not used to indicate the file should be opened +** for exclusive access. +** +** ^At least szOsFile bytes of memory are allocated by SQLite +** to hold the [sqlite3_file] structure passed as the third +** argument to xOpen. The xOpen method does not have to +** allocate the structure; it should just fill it in. Note that +** the xOpen method must set the sqlite3_file.pMethods to either +** a valid [sqlite3_io_methods] object or to NULL. xOpen must do +** this even if the open fails. SQLite expects that the sqlite3_file.pMethods +** element will be valid after xOpen returns regardless of the success +** or failure of the xOpen call. +** +** [[sqlite3_vfs.xAccess]] +** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] +** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to +** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] +** to test whether a file is at least readable. The file can be a +** directory. +** +** ^SQLite will always allocate at least mxPathname+1 bytes for the +** output buffer xFullPathname. The exact size of the output buffer +** is also passed as a parameter to both methods. If the output buffer +** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is +** handled as a fatal error by SQLite, vfs implementations should endeavor +** to prevent this by setting mxPathname to a sufficiently large value. +** +** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() +** interfaces are not strictly a part of the filesystem, but they are +** included in the VFS structure for completeness. +** The xRandomness() function attempts to return nBytes bytes +** of good-quality randomness into zOut. The return value is +** the actual number of bytes of randomness obtained. +** The xSleep() method causes the calling thread to sleep for at +** least the number of microseconds given. ^The xCurrentTime() +** method returns a Julian Day Number for the current date and time as +** a floating point value. +** ^The xCurrentTimeInt64() method returns, as an integer, the Julian +** Day Number multiplied by 86400000 (the number of milliseconds in +** a 24-hour day). +** ^SQLite will use the xCurrentTimeInt64() method to get the current +** date and time if that method is available (if iVersion is 2 or +** greater and the function pointer is not NULL) and will fall back +** to xCurrentTime() if xCurrentTimeInt64() is unavailable. +** +** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces +** are not used by the SQLite core. These optional interfaces are provided +** by some VFSes to facilitate testing of the VFS code. By overriding +** system calls with functions under its control, a test program can +** simulate faults and error conditions that would otherwise be difficult +** or impossible to induce. The set of system calls that can be overridden +** varies from one VFS to another, and from one version of the same VFS to the +** next. Applications that use these interfaces must be prepared for any +** or all of these interfaces to be NULL or for their behavior to change +** from one release to the next. Applications must not attempt to access +** any of these methods if the iVersion of the VFS is less than 3. +*/ +typedef struct sqlite3_vfs sqlite3_vfs; +typedef void (*sqlite3_syscall_ptr)(void); +struct sqlite3_vfs { + int iVersion; /* Structure version number (currently 3) */ + int szOsFile; /* Size of subclassed sqlite3_file */ + int mxPathname; /* Maximum file pathname length */ + sqlite3_vfs *pNext; /* Next registered VFS */ + const char *zName; /* Name of this virtual file system */ + void *pAppData; /* Pointer to application-specific data */ + int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, + int flags, int *pOutFlags); + int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); + int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); + int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut); + void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename); + void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg); + void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void); + void (*xDlClose)(sqlite3_vfs*, void*); + int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut); + int (*xSleep)(sqlite3_vfs*, int microseconds); + int (*xCurrentTime)(sqlite3_vfs*, double*); + int (*xGetLastError)(sqlite3_vfs*, int, char *); + /* + ** The methods above are in version 1 of the sqlite_vfs object + ** definition. Those that follow are added in version 2 or later + */ + int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); + /* + ** The methods above are in versions 1 and 2 of the sqlite_vfs object. + ** Those below are for version 3 and greater. + */ + int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr); + sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName); + const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName); + /* + ** The methods above are in versions 1 through 3 of the sqlite_vfs object. + ** New fields may be appended in figure versions. The iVersion + ** value will increment whenever this happens. + */ +}; + +/* +** CAPI3REF: Flags for the xAccess VFS method +** +** These integer constants can be used as the third parameter to +** the xAccess method of an [sqlite3_vfs] object. They determine +** what kind of permissions the xAccess method is looking for. +** With SQLITE_ACCESS_EXISTS, the xAccess method +** simply checks whether the file exists. +** With SQLITE_ACCESS_READWRITE, the xAccess method +** checks whether the named directory is both readable and writable +** (in other words, if files can be added, removed, and renamed within +** the directory). +** The SQLITE_ACCESS_READWRITE constant is currently used only by the +** [temp_store_directory pragma], though this could change in a future +** release of SQLite. +** With SQLITE_ACCESS_READ, the xAccess method +** checks whether the file is readable. The SQLITE_ACCESS_READ constant is +** currently unused, though it might be used in a future release of +** SQLite. +*/ +#define SQLITE_ACCESS_EXISTS 0 +#define SQLITE_ACCESS_READWRITE 1 /* Used by PRAGMA temp_store_directory */ +#define SQLITE_ACCESS_READ 2 /* Unused */ + +/* +** CAPI3REF: Flags for the xShmLock VFS method +** +** These integer constants define the various locking operations +** allowed by the xShmLock method of [sqlite3_io_methods]. The +** following are the only legal combinations of flags to the +** xShmLock method: +** +**
    +**
  • SQLITE_SHM_LOCK | SQLITE_SHM_SHARED +**
  • SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE +**
  • SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED +**
  • SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE +**
+** +** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as +** was given no the corresponding lock. +** +** The xShmLock method can transition between unlocked and SHARED or +** between unlocked and EXCLUSIVE. It cannot transition between SHARED +** and EXCLUSIVE. +*/ +#define SQLITE_SHM_UNLOCK 1 +#define SQLITE_SHM_LOCK 2 +#define SQLITE_SHM_SHARED 4 +#define SQLITE_SHM_EXCLUSIVE 8 + +/* +** CAPI3REF: Maximum xShmLock index +** +** The xShmLock method on [sqlite3_io_methods] may use values +** between 0 and this upper bound as its "offset" argument. +** The SQLite core will never attempt to acquire or release a +** lock outside of this range +*/ +#define SQLITE_SHM_NLOCK 8 + + +/* +** CAPI3REF: Initialize The SQLite Library +** +** ^The sqlite3_initialize() routine initializes the +** SQLite library. ^The sqlite3_shutdown() routine +** deallocates any resources that were allocated by sqlite3_initialize(). +** These routines are designed to aid in process initialization and +** shutdown on embedded systems. Workstation applications using +** SQLite normally do not need to invoke either of these routines. +** +** A call to sqlite3_initialize() is an "effective" call if it is +** the first time sqlite3_initialize() is invoked during the lifetime of +** the process, or if it is the first time sqlite3_initialize() is invoked +** following a call to sqlite3_shutdown(). ^(Only an effective call +** of sqlite3_initialize() does any initialization. All other calls +** are harmless no-ops.)^ +** +** A call to sqlite3_shutdown() is an "effective" call if it is the first +** call to sqlite3_shutdown() since the last sqlite3_initialize(). ^(Only +** an effective call to sqlite3_shutdown() does any deinitialization. +** All other valid calls to sqlite3_shutdown() are harmless no-ops.)^ +** +** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown() +** is not. The sqlite3_shutdown() interface must only be called from a +** single thread. All open [database connections] must be closed and all +** other SQLite resources must be deallocated prior to invoking +** sqlite3_shutdown(). +** +** Among other things, ^sqlite3_initialize() will invoke +** sqlite3_os_init(). Similarly, ^sqlite3_shutdown() +** will invoke sqlite3_os_end(). +** +** ^The sqlite3_initialize() routine returns [SQLITE_OK] on success. +** ^If for some reason, sqlite3_initialize() is unable to initialize +** the library (perhaps it is unable to allocate a needed resource such +** as a mutex) it returns an [error code] other than [SQLITE_OK]. +** +** ^The sqlite3_initialize() routine is called internally by many other +** SQLite interfaces so that an application usually does not need to +** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] +** calls sqlite3_initialize() so the SQLite library will be automatically +** initialized when [sqlite3_open()] is called if it has not be initialized +** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] +** compile-time option, then the automatic calls to sqlite3_initialize() +** are omitted and the application must call sqlite3_initialize() directly +** prior to using any other SQLite interface. For maximum portability, +** it is recommended that applications always invoke sqlite3_initialize() +** directly prior to using any other SQLite interface. Future releases +** of SQLite may require this. In other words, the behavior exhibited +** when SQLite is compiled with [SQLITE_OMIT_AUTOINIT] might become the +** default behavior in some future release of SQLite. +** +** The sqlite3_os_init() routine does operating-system specific +** initialization of the SQLite library. The sqlite3_os_end() +** routine undoes the effect of sqlite3_os_init(). Typical tasks +** performed by these routines include allocation or deallocation +** of static resources, initialization of global variables, +** setting up a default [sqlite3_vfs] module, or setting up +** a default configuration using [sqlite3_config()]. +** +** The application should never invoke either sqlite3_os_init() +** or sqlite3_os_end() directly. The application should only invoke +** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init() +** interface is called automatically by sqlite3_initialize() and +** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate +** implementations for sqlite3_os_init() and sqlite3_os_end() +** are built into SQLite when it is compiled for Unix, Windows, or OS/2. +** When [custom builds | built for other platforms] +** (using the [SQLITE_OS_OTHER=1] compile-time +** option) the application must supply a suitable implementation for +** sqlite3_os_init() and sqlite3_os_end(). An application-supplied +** implementation of sqlite3_os_init() or sqlite3_os_end() +** must return [SQLITE_OK] on success and some other [error code] upon +** failure. +*/ +SQLITE_API int sqlite3_initialize(void); +SQLITE_API int sqlite3_shutdown(void); +SQLITE_API int sqlite3_os_init(void); +SQLITE_API int sqlite3_os_end(void); + +/* +** CAPI3REF: Configuring The SQLite Library +** +** The sqlite3_config() interface is used to make global configuration +** changes to SQLite in order to tune SQLite to the specific needs of +** the application. The default configuration is recommended for most +** applications and so this routine is usually not necessary. It is +** provided to support rare applications with unusual needs. +** +** The sqlite3_config() interface is not threadsafe. The application +** must insure that no other SQLite interfaces are invoked by other +** threads while sqlite3_config() is running. Furthermore, sqlite3_config() +** may only be invoked prior to library initialization using +** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. +** ^If sqlite3_config() is called after [sqlite3_initialize()] and before +** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. +** Note, however, that ^sqlite3_config() can be called as part of the +** implementation of an application-defined [sqlite3_os_init()]. +** +** The first argument to sqlite3_config() is an integer +** [configuration option] that determines +** what property of SQLite is to be configured. Subsequent arguments +** vary depending on the [configuration option] +** in the first argument. +** +** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. +** ^If the option is unknown or SQLite is unable to set the option +** then this routine returns a non-zero [error code]. +*/ +SQLITE_API int sqlite3_config(int, ...); + +/* +** CAPI3REF: Configure database connections +** +** The sqlite3_db_config() interface is used to make configuration +** changes to a [database connection]. The interface is similar to +** [sqlite3_config()] except that the changes apply to a single +** [database connection] (specified in the first argument). +** +** The second argument to sqlite3_db_config(D,V,...) is the +** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code +** that indicates what aspect of the [database connection] is being configured. +** Subsequent arguments vary depending on the configuration verb. +** +** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if +** the call is considered successful. +*/ +SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); + +/* +** CAPI3REF: Memory Allocation Routines +** +** An instance of this object defines the interface between SQLite +** and low-level memory allocation routines. +** +** This object is used in only one place in the SQLite interface. +** A pointer to an instance of this object is the argument to +** [sqlite3_config()] when the configuration option is +** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC]. +** By creating an instance of this object +** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC]) +** during configuration, an application can specify an alternative +** memory allocation subsystem for SQLite to use for all of its +** dynamic memory needs. +** +** Note that SQLite comes with several [built-in memory allocators] +** that are perfectly adequate for the overwhelming majority of applications +** and that this object is only useful to a tiny minority of applications +** with specialized memory allocation requirements. This object is +** also used during testing of SQLite in order to specify an alternative +** memory allocator that simulates memory out-of-memory conditions in +** order to verify that SQLite recovers gracefully from such +** conditions. +** +** The xMalloc, xRealloc, and xFree methods must work like the +** malloc(), realloc() and free() functions from the standard C library. +** ^SQLite guarantees that the second argument to +** xRealloc is always a value returned by a prior call to xRoundup. +** +** xSize should return the allocated size of a memory allocation +** previously obtained from xMalloc or xRealloc. The allocated size +** is always at least as big as the requested size but may be larger. +** +** The xRoundup method returns what would be the allocated size of +** a memory allocation given a particular requested size. Most memory +** allocators round up memory allocations at least to the next multiple +** of 8. Some allocators round up to a larger multiple or to a power of 2. +** Every memory allocation request coming in through [sqlite3_malloc()] +** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, +** that causes the corresponding memory allocation to fail. +** +** The xInit method initializes the memory allocator. (For example, +** it might allocate any require mutexes or initialize internal data +** structures. The xShutdown method is invoked (indirectly) by +** [sqlite3_shutdown()] and should deallocate any resources acquired +** by xInit. The pAppData pointer is used as the only parameter to +** xInit and xShutdown. +** +** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes +** the xInit method, so the xInit method need not be threadsafe. The +** xShutdown method is only called from [sqlite3_shutdown()] so it does +** not need to be threadsafe either. For all other methods, SQLite +** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the +** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which +** it is by default) and so the methods are automatically serialized. +** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other +** methods must be threadsafe or else make their own arrangements for +** serialization. +** +** SQLite will never invoke xInit() more than once without an intervening +** call to xShutdown(). +*/ +typedef struct sqlite3_mem_methods sqlite3_mem_methods; +struct sqlite3_mem_methods { + void *(*xMalloc)(int); /* Memory allocation function */ + void (*xFree)(void*); /* Free a prior allocation */ + void *(*xRealloc)(void*,int); /* Resize an allocation */ + int (*xSize)(void*); /* Return the size of an allocation */ + int (*xRoundup)(int); /* Round up request size to allocation size */ + int (*xInit)(void*); /* Initialize the memory allocator */ + void (*xShutdown)(void*); /* Deinitialize the memory allocator */ + void *pAppData; /* Argument to xInit() and xShutdown() */ +}; + +/* +** CAPI3REF: Configuration Options +** KEYWORDS: {configuration option} +** +** These constants are the available integer configuration options that +** can be passed as the first argument to the [sqlite3_config()] interface. +** +** New configuration options may be added in future releases of SQLite. +** Existing configuration options might be discontinued. Applications +** should check the return code from [sqlite3_config()] to make sure that +** the call worked. The [sqlite3_config()] interface will return a +** non-zero [error code] if a discontinued or unsupported configuration option +** is invoked. +** +**
+** [[SQLITE_CONFIG_SINGLETHREAD]]
SQLITE_CONFIG_SINGLETHREAD
+**
There are no arguments to this option. ^This option sets the +** [threading mode] to Single-thread. In other words, it disables +** all mutexing and puts SQLite into a mode where it can only be used +** by a single thread. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** it is not possible to change the [threading mode] from its default +** value of Single-thread and so [sqlite3_config()] will return +** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD +** configuration option.
+** +** [[SQLITE_CONFIG_MULTITHREAD]]
SQLITE_CONFIG_MULTITHREAD
+**
There are no arguments to this option. ^This option sets the +** [threading mode] to Multi-thread. In other words, it disables +** mutexing on [database connection] and [prepared statement] objects. +** The application is responsible for serializing access to +** [database connections] and [prepared statements]. But other mutexes +** are enabled so that SQLite will be safe to use in a multi-threaded +** environment as long as no two threads attempt to use the same +** [database connection] at the same time. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** it is not possible to set the Multi-thread [threading mode] and +** [sqlite3_config()] will return [SQLITE_ERROR] if called with the +** SQLITE_CONFIG_MULTITHREAD configuration option.
+** +** [[SQLITE_CONFIG_SERIALIZED]]
SQLITE_CONFIG_SERIALIZED
+**
There are no arguments to this option. ^This option sets the +** [threading mode] to Serialized. In other words, this option enables +** all mutexes including the recursive +** mutexes on [database connection] and [prepared statement] objects. +** In this mode (which is the default when SQLite is compiled with +** [SQLITE_THREADSAFE=1]) the SQLite library will itself serialize access +** to [database connections] and [prepared statements] so that the +** application is free to use the same [database connection] or the +** same [prepared statement] in different threads at the same time. +** ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** it is not possible to set the Serialized [threading mode] and +** [sqlite3_config()] will return [SQLITE_ERROR] if called with the +** SQLITE_CONFIG_SERIALIZED configuration option.
+** +** [[SQLITE_CONFIG_MALLOC]]
SQLITE_CONFIG_MALLOC
+**
^(This option takes a single argument which is a pointer to an +** instance of the [sqlite3_mem_methods] structure. The argument specifies +** alternative low-level memory allocation routines to be used in place of +** the memory allocation routines built into SQLite.)^ ^SQLite makes +** its own private copy of the content of the [sqlite3_mem_methods] structure +** before the [sqlite3_config()] call returns.
+** +** [[SQLITE_CONFIG_GETMALLOC]]
SQLITE_CONFIG_GETMALLOC
+**
^(This option takes a single argument which is a pointer to an +** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] +** structure is filled with the currently defined memory allocation routines.)^ +** This option can be used to overload the default memory allocation +** routines with a wrapper that simulations memory allocation failure or +** tracks memory usage, for example.
+** +** [[SQLITE_CONFIG_MEMSTATUS]]
SQLITE_CONFIG_MEMSTATUS
+**
^This option takes single argument of type int, interpreted as a +** boolean, which enables or disables the collection of memory allocation +** statistics. ^(When memory allocation statistics are disabled, the +** following SQLite interfaces become non-operational: +**
    +**
  • [sqlite3_memory_used()] +**
  • [sqlite3_memory_highwater()] +**
  • [sqlite3_soft_heap_limit64()] +**
  • [sqlite3_status()] +**
)^ +** ^Memory allocation statistics are enabled by default unless SQLite is +** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory +** allocation statistics are disabled by default. +**
+** +** [[SQLITE_CONFIG_SCRATCH]]
SQLITE_CONFIG_SCRATCH
+**
^This option specifies a static memory buffer that SQLite can use for +** scratch memory. There are three arguments: A pointer an 8-byte +** aligned memory buffer from which the scratch allocations will be +** drawn, the size of each scratch allocation (sz), +** and the maximum number of scratch allocations (N). The sz +** argument must be a multiple of 16. +** The first argument must be a pointer to an 8-byte aligned buffer +** of at least sz*N bytes of memory. +** ^SQLite will use no more than two scratch buffers per thread. So +** N should be set to twice the expected maximum number of threads. +** ^SQLite will never require a scratch buffer that is more than 6 +** times the database page size. ^If SQLite needs needs additional +** scratch memory beyond what is provided by this configuration option, then +** [sqlite3_malloc()] will be used to obtain the memory needed.
+** +** [[SQLITE_CONFIG_PAGECACHE]]
SQLITE_CONFIG_PAGECACHE
+**
^This option specifies a static memory buffer that SQLite can use for +** the database page cache with the default page cache implementation. +** This configuration should not be used if an application-define page +** cache implementation is loaded using the SQLITE_CONFIG_PCACHE2 option. +** There are three arguments to this option: A pointer to 8-byte aligned +** memory, the size of each page buffer (sz), and the number of pages (N). +** The sz argument should be the size of the largest database page +** (a power of two between 512 and 32768) plus a little extra for each +** page header. ^The page header size is 20 to 40 bytes depending on +** the host architecture. ^It is harmless, apart from the wasted memory, +** to make sz a little too large. The first +** argument should point to an allocation of at least sz*N bytes of memory. +** ^SQLite will use the memory provided by the first argument to satisfy its +** memory needs for the first N pages that it adds to cache. ^If additional +** page cache memory is needed beyond what is provided by this option, then +** SQLite goes to [sqlite3_malloc()] for the additional storage space. +** The pointer in the first argument must +** be aligned to an 8-byte boundary or subsequent behavior of SQLite +** will be undefined.
+** +** [[SQLITE_CONFIG_HEAP]]
SQLITE_CONFIG_HEAP
+**
^This option specifies a static memory buffer that SQLite will use +** for all of its dynamic memory allocation needs beyond those provided +** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. +** There are three arguments: An 8-byte aligned pointer to the memory, +** the number of bytes in the memory buffer, and the minimum allocation size. +** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts +** to using its default memory allocator (the system malloc() implementation), +** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the +** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or +** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory +** allocator is engaged to handle all of SQLites memory allocation needs. +** The first pointer (the memory pointer) must be aligned to an 8-byte +** boundary or subsequent behavior of SQLite will be undefined. +** The minimum allocation size is capped at 2**12. Reasonable values +** for the minimum allocation size are 2**5 through 2**8.
+** +** [[SQLITE_CONFIG_MUTEX]]
SQLITE_CONFIG_MUTEX
+**
^(This option takes a single argument which is a pointer to an +** instance of the [sqlite3_mutex_methods] structure. The argument specifies +** alternative low-level mutex routines to be used in place +** the mutex routines built into SQLite.)^ ^SQLite makes a copy of the +** content of the [sqlite3_mutex_methods] structure before the call to +** [sqlite3_config()] returns. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** the entire mutexing subsystem is omitted from the build and hence calls to +** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will +** return [SQLITE_ERROR].
+** +** [[SQLITE_CONFIG_GETMUTEX]]
SQLITE_CONFIG_GETMUTEX
+**
^(This option takes a single argument which is a pointer to an +** instance of the [sqlite3_mutex_methods] structure. The +** [sqlite3_mutex_methods] +** structure is filled with the currently defined mutex routines.)^ +** This option can be used to overload the default mutex allocation +** routines with a wrapper used to track mutex usage for performance +** profiling or testing, for example. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** the entire mutexing subsystem is omitted from the build and hence calls to +** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will +** return [SQLITE_ERROR].
+** +** [[SQLITE_CONFIG_LOOKASIDE]]
SQLITE_CONFIG_LOOKASIDE
+**
^(This option takes two arguments that determine the default +** memory allocation for the lookaside memory allocator on each +** [database connection]. The first argument is the +** size of each lookaside buffer slot and the second is the number of +** slots allocated to each database connection.)^ ^(This option sets the +** default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] +** verb to [sqlite3_db_config()] can be used to change the lookaside +** configuration on individual connections.)^
+** +** [[SQLITE_CONFIG_PCACHE2]]
SQLITE_CONFIG_PCACHE2
+**
^(This option takes a single argument which is a pointer to +** an [sqlite3_pcache_methods2] object. This object specifies the interface +** to a custom page cache implementation.)^ ^SQLite makes a copy of the +** object and uses it for page cache memory allocations.
+** +** [[SQLITE_CONFIG_GETPCACHE2]]
SQLITE_CONFIG_GETPCACHE2
+**
^(This option takes a single argument which is a pointer to an +** [sqlite3_pcache_methods2] object. SQLite copies of the current +** page cache implementation into that object.)^
+** +** [[SQLITE_CONFIG_LOG]]
SQLITE_CONFIG_LOG
+**
^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a +** function with a call signature of void(*)(void*,int,const char*), +** and a pointer to void. ^If the function pointer is not NULL, it is +** invoked by [sqlite3_log()] to process each logging event. ^If the +** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op. +** ^The void pointer that is the second argument to SQLITE_CONFIG_LOG is +** passed through as the first parameter to the application-defined logger +** function whenever that function is invoked. ^The second parameter to +** the logger function is a copy of the first parameter to the corresponding +** [sqlite3_log()] call and is intended to be a [result code] or an +** [extended result code]. ^The third parameter passed to the logger is +** log message after formatting via [sqlite3_snprintf()]. +** The SQLite logging interface is not reentrant; the logger function +** supplied by the application must not invoke any SQLite interface. +** In a multi-threaded application, the application-defined logger +** function must be threadsafe.
+** +** [[SQLITE_CONFIG_URI]]
SQLITE_CONFIG_URI +**
This option takes a single argument of type int. If non-zero, then +** URI handling is globally enabled. If the parameter is zero, then URI handling +** is globally disabled. If URI handling is globally enabled, all filenames +** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or +** specified as part of [ATTACH] commands are interpreted as URIs, regardless +** of whether or not the [SQLITE_OPEN_URI] flag is set when the database +** connection is opened. If it is globally disabled, filenames are +** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the +** database connection is opened. By default, URI handling is globally +** disabled. The default value may be changed by compiling with the +** [SQLITE_USE_URI] symbol defined. +** +** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]]
SQLITE_CONFIG_COVERING_INDEX_SCAN +**
This option takes a single integer argument which is interpreted as +** a boolean in order to enable or disable the use of covering indices for +** full table scans in the query optimizer. The default setting is determined +** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on" +** if that compile-time option is omitted. +** The ability to disable the use of covering indices for full table scans +** is because some incorrectly coded legacy applications might malfunction +** malfunction when the optimization is enabled. Providing the ability to +** disable the optimization allows the older, buggy application code to work +** without change even with newer versions of SQLite. +** +** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]] +**
SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE +**
These options are obsolete and should not be used by new code. +** They are retained for backwards compatibility but are now no-ops. +**
+** +** [[SQLITE_CONFIG_SQLLOG]] +**
SQLITE_CONFIG_SQLLOG +**
This option is only available if sqlite is compiled with the +** SQLITE_ENABLE_SQLLOG pre-processor macro defined. The first argument should +** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int). +** The second should be of type (void*). The callback is invoked by the library +** in three separate circumstances, identified by the value passed as the +** fourth parameter. If the fourth parameter is 0, then the database connection +** passed as the second argument has just been opened. The third argument +** points to a buffer containing the name of the main database file. If the +** fourth parameter is 1, then the SQL statement that the third parameter +** points to has just been executed. Or, if the fourth parameter is 2, then +** the connection being passed as the second parameter is being closed. The +** third parameter is passed NULL In this case. +** +*/ +#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ +#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ +#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ +#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */ +#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ +#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ +#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ +#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ +#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ +/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ +#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ +#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +#define SQLITE_CONFIG_URI 17 /* int */ +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ +#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ + +/* +** CAPI3REF: Database Connection Configuration Options +** +** These constants are the available integer configuration options that +** can be passed as the second argument to the [sqlite3_db_config()] interface. +** +** New configuration options may be added in future releases of SQLite. +** Existing configuration options might be discontinued. Applications +** should check the return code from [sqlite3_db_config()] to make sure that +** the call worked. ^The [sqlite3_db_config()] interface will return a +** non-zero [error code] if a discontinued or unsupported configuration option +** is invoked. +** +**
+**
SQLITE_DBCONFIG_LOOKASIDE
+**
^This option takes three additional arguments that determine the +** [lookaside memory allocator] configuration for the [database connection]. +** ^The first argument (the third parameter to [sqlite3_db_config()] is a +** pointer to a memory buffer to use for lookaside memory. +** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb +** may be NULL in which case SQLite will allocate the +** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the +** size of each lookaside buffer slot. ^The third argument is the number of +** slots. The size of the buffer in the first argument must be greater than +** or equal to the product of the second and third arguments. The buffer +** must be aligned to an 8-byte boundary. ^If the second argument to +** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally +** rounded down to the next smaller multiple of 8. ^(The lookaside memory +** configuration for a database connection can only be changed when that +** connection is not currently using lookaside memory, or in other words +** when the "current value" returned by +** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. +** Any attempt to change the lookaside memory configuration when lookaside +** memory is in use leaves the configuration unchanged and returns +** [SQLITE_BUSY].)^
+** +**
SQLITE_DBCONFIG_ENABLE_FKEY
+**
^This option is used to enable or disable the enforcement of +** [foreign key constraints]. There should be two additional arguments. +** The first argument is an integer which is 0 to disable FK enforcement, +** positive to enable FK enforcement or negative to leave FK enforcement +** unchanged. The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether FK enforcement is off or on +** following this call. The second parameter may be a NULL pointer, in +** which case the FK enforcement setting is not reported back.
+** +**
SQLITE_DBCONFIG_ENABLE_TRIGGER
+**
^This option is used to enable or disable [CREATE TRIGGER | triggers]. +** There should be two additional arguments. +** The first argument is an integer which is 0 to disable triggers, +** positive to enable triggers or negative to leave the setting unchanged. +** The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether triggers are disabled or enabled +** following this call. The second parameter may be a NULL pointer, in +** which case the trigger setting is not reported back.
+** +**
+*/ +#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ + + +/* +** CAPI3REF: Enable Or Disable Extended Result Codes +** +** ^The sqlite3_extended_result_codes() routine enables or disables the +** [extended result codes] feature of SQLite. ^The extended result +** codes are disabled by default for historical compatibility. +*/ +SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); + +/* +** CAPI3REF: Last Insert Rowid +** +** ^Each entry in an SQLite table has a unique 64-bit signed +** integer key called the [ROWID | "rowid"]. ^The rowid is always available +** as an undeclared column named ROWID, OID, or _ROWID_ as long as those +** names are not also used by explicitly declared columns. ^If +** the table has a column of type [INTEGER PRIMARY KEY] then that column +** is another alias for the rowid. +** +** ^This routine returns the [rowid] of the most recent +** successful [INSERT] into the database from the [database connection] +** in the first argument. ^As of SQLite version 3.7.7, this routines +** records the last insert rowid of both ordinary tables and [virtual tables]. +** ^If no successful [INSERT]s +** have ever occurred on that database connection, zero is returned. +** +** ^(If an [INSERT] occurs within a trigger or within a [virtual table] +** method, then this routine will return the [rowid] of the inserted +** row as long as the trigger or virtual table method is running. +** But once the trigger or virtual table method ends, the value returned +** by this routine reverts to what it was before the trigger or virtual +** table method began.)^ +** +** ^An [INSERT] that fails due to a constraint violation is not a +** successful [INSERT] and does not change the value returned by this +** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, +** and INSERT OR ABORT make no changes to the return value of this +** routine when their insertion fails. ^(When INSERT OR REPLACE +** encounters a constraint violation, it does not fail. The +** INSERT continues to completion after deleting rows that caused +** the constraint problem so INSERT OR REPLACE will always change +** the return value of this interface.)^ +** +** ^For the purposes of this routine, an [INSERT] is considered to +** be successful even if it is subsequently rolled back. +** +** This function is accessible to SQL statements via the +** [last_insert_rowid() SQL function]. +** +** If a separate thread performs a new [INSERT] on the same +** database connection while the [sqlite3_last_insert_rowid()] +** function is running and thus changes the last insert [rowid], +** then the value returned by [sqlite3_last_insert_rowid()] is +** unpredictable and might not equal either the old or the new +** last insert [rowid]. +*/ +SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); + +/* +** CAPI3REF: Count The Number Of Rows Modified +** +** ^This function returns the number of database rows that were changed +** or inserted or deleted by the most recently completed SQL statement +** on the [database connection] specified by the first parameter. +** ^(Only changes that are directly specified by the [INSERT], [UPDATE], +** or [DELETE] statement are counted. Auxiliary changes caused by +** triggers or [foreign key actions] are not counted.)^ Use the +** [sqlite3_total_changes()] function to find the total number of changes +** including changes caused by triggers and foreign key actions. +** +** ^Changes to a view that are simulated by an [INSTEAD OF trigger] +** are not counted. Only real table changes are counted. +** +** ^(A "row change" is a change to a single row of a single table +** caused by an INSERT, DELETE, or UPDATE statement. Rows that +** are changed as side effects of [REPLACE] constraint resolution, +** rollback, ABORT processing, [DROP TABLE], or by any other +** mechanisms do not count as direct row changes.)^ +** +** A "trigger context" is a scope of execution that begins and +** ends with the script of a [CREATE TRIGGER | trigger]. +** Most SQL statements are +** evaluated outside of any trigger. This is the "top level" +** trigger context. If a trigger fires from the top level, a +** new trigger context is entered for the duration of that one +** trigger. Subtriggers create subcontexts for their duration. +** +** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does +** not create a new trigger context. +** +** ^This function returns the number of direct row changes in the +** most recent INSERT, UPDATE, or DELETE statement within the same +** trigger context. +** +** ^Thus, when called from the top level, this function returns the +** number of changes in the most recent INSERT, UPDATE, or DELETE +** that also occurred at the top level. ^(Within the body of a trigger, +** the sqlite3_changes() interface can be called to find the number of +** changes in the most recently completed INSERT, UPDATE, or DELETE +** statement within the body of the same trigger. +** However, the number returned does not include changes +** caused by subtriggers since those have their own context.)^ +** +** See also the [sqlite3_total_changes()] interface, the +** [count_changes pragma], and the [changes() SQL function]. +** +** If a separate thread makes changes on the same database connection +** while [sqlite3_changes()] is running then the value returned +** is unpredictable and not meaningful. +*/ +SQLITE_API int sqlite3_changes(sqlite3*); + +/* +** CAPI3REF: Total Number Of Rows Modified +** +** ^This function returns the number of row changes caused by [INSERT], +** [UPDATE] or [DELETE] statements since the [database connection] was opened. +** ^(The count returned by sqlite3_total_changes() includes all changes +** from all [CREATE TRIGGER | trigger] contexts and changes made by +** [foreign key actions]. However, +** the count does not include changes used to implement [REPLACE] constraints, +** do rollbacks or ABORT processing, or [DROP TABLE] processing. The +** count does not include rows of views that fire an [INSTEAD OF trigger], +** though if the INSTEAD OF trigger makes changes of its own, those changes +** are counted.)^ +** ^The sqlite3_total_changes() function counts the changes as soon as +** the statement that makes them is completed (when the statement handle +** is passed to [sqlite3_reset()] or [sqlite3_finalize()]). +** +** See also the [sqlite3_changes()] interface, the +** [count_changes pragma], and the [total_changes() SQL function]. +** +** If a separate thread makes changes on the same database connection +** while [sqlite3_total_changes()] is running then the value +** returned is unpredictable and not meaningful. +*/ +SQLITE_API int sqlite3_total_changes(sqlite3*); + +/* +** CAPI3REF: Interrupt A Long-Running Query +** +** ^This function causes any pending database operation to abort and +** return at its earliest opportunity. This routine is typically +** called in response to a user action such as pressing "Cancel" +** or Ctrl-C where the user wants a long query operation to halt +** immediately. +** +** ^It is safe to call this routine from a thread different from the +** thread that is currently running the database operation. But it +** is not safe to call this routine with a [database connection] that +** is closed or might close before sqlite3_interrupt() returns. +** +** ^If an SQL operation is very nearly finished at the time when +** sqlite3_interrupt() is called, then it might not have an opportunity +** to be interrupted and might continue to completion. +** +** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. +** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE +** that is inside an explicit transaction, then the entire transaction +** will be rolled back automatically. +** +** ^The sqlite3_interrupt(D) call is in effect until all currently running +** SQL statements on [database connection] D complete. ^Any new SQL statements +** that are started after the sqlite3_interrupt() call and before the +** running statements reaches zero are interrupted as if they had been +** running prior to the sqlite3_interrupt() call. ^New SQL statements +** that are started after the running statement count reaches zero are +** not effected by the sqlite3_interrupt(). +** ^A call to sqlite3_interrupt(D) that occurs when there are no running +** SQL statements is a no-op and has no effect on SQL statements +** that are started after the sqlite3_interrupt() call returns. +** +** If the database connection closes while [sqlite3_interrupt()] +** is running then bad things will likely happen. +*/ +SQLITE_API void sqlite3_interrupt(sqlite3*); + +/* +** CAPI3REF: Determine If An SQL Statement Is Complete +** +** These routines are useful during command-line input to determine if the +** currently entered text seems to form a complete SQL statement or +** if additional input is needed before sending the text into +** SQLite for parsing. ^These routines return 1 if the input string +** appears to be a complete SQL statement. ^A statement is judged to be +** complete if it ends with a semicolon token and is not a prefix of a +** well-formed CREATE TRIGGER statement. ^Semicolons that are embedded within +** string literals or quoted identifier names or comments are not +** independent tokens (they are part of the token in which they are +** embedded) and thus do not count as a statement terminator. ^Whitespace +** and comments that follow the final semicolon are ignored. +** +** ^These routines return 0 if the statement is incomplete. ^If a +** memory allocation fails, then SQLITE_NOMEM is returned. +** +** ^These routines do not parse the SQL statements thus +** will not detect syntactically incorrect SQL. +** +** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior +** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked +** automatically by sqlite3_complete16(). If that initialization fails, +** then the return value from sqlite3_complete16() will be non-zero +** regardless of whether or not the input SQL is complete.)^ +** +** The input to [sqlite3_complete()] must be a zero-terminated +** UTF-8 string. +** +** The input to [sqlite3_complete16()] must be a zero-terminated +** UTF-16 string in native byte order. +*/ +SQLITE_API int sqlite3_complete(const char *sql); +SQLITE_API int sqlite3_complete16(const void *sql); + +/* +** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors +** +** ^This routine sets a callback function that might be invoked whenever +** an attempt is made to open a database table that another thread +** or process has locked. +** +** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] +** is returned immediately upon encountering the lock. ^If the busy callback +** is not NULL, then the callback might be invoked with two arguments. +** +** ^The first argument to the busy handler is a copy of the void* pointer which +** is the third argument to sqlite3_busy_handler(). ^The second argument to +** the busy handler callback is the number of times that the busy handler has +** been invoked for this locking event. ^If the +** busy callback returns 0, then no additional attempts are made to +** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned. +** ^If the callback returns non-zero, then another attempt +** is made to open the database for reading and the cycle repeats. +** +** The presence of a busy handler does not guarantee that it will be invoked +** when there is lock contention. ^If SQLite determines that invoking the busy +** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] +** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler. +** Consider a scenario where one process is holding a read lock that +** it is trying to promote to a reserved lock and +** a second process is holding a reserved lock that it is trying +** to promote to an exclusive lock. The first process cannot proceed +** because it is blocked by the second and the second process cannot +** proceed because it is blocked by the first. If both processes +** invoke the busy handlers, neither will make any progress. Therefore, +** SQLite returns [SQLITE_BUSY] for the first process, hoping that this +** will induce the first process to release its read lock and allow +** the second process to proceed. +** +** ^The default busy callback is NULL. +** +** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] +** when SQLite is in the middle of a large transaction where all the +** changes will not fit into the in-memory cache. SQLite will +** already hold a RESERVED lock on the database file, but it needs +** to promote this lock to EXCLUSIVE so that it can spill cache +** pages into the database file without harm to concurrent +** readers. ^If it is unable to promote the lock, then the in-memory +** cache will be left in an inconsistent state and so the error +** code is promoted from the relatively benign [SQLITE_BUSY] to +** the more severe [SQLITE_IOERR_BLOCKED]. ^This error code promotion +** forces an automatic rollback of the changes. See the +** +** CorruptionFollowingBusyError wiki page for a discussion of why +** this is important. +** +** ^(There can only be a single busy handler defined for each +** [database connection]. Setting a new busy handler clears any +** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] +** will also set or clear the busy handler. +** +** The busy callback should not take any actions which modify the +** database connection that invoked the busy handler. Any such actions +** result in undefined behavior. +** +** A busy handler must not close the database connection +** or [prepared statement] that invoked the busy handler. +*/ +SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); + +/* +** CAPI3REF: Set A Busy Timeout +** +** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps +** for a specified amount of time when a table is locked. ^The handler +** will sleep multiple times until at least "ms" milliseconds of sleeping +** have accumulated. ^After at least "ms" milliseconds of sleeping, +** the handler returns 0 which causes [sqlite3_step()] to return +** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]. +** +** ^Calling this routine with an argument less than or equal to zero +** turns off all busy handlers. +** +** ^(There can only be a single busy handler for a particular +** [database connection] any any given moment. If another busy handler +** was defined (using [sqlite3_busy_handler()]) prior to calling +** this routine, that other busy handler is cleared.)^ +*/ +SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); + +/* +** CAPI3REF: Convenience Routines For Running Queries +** +** This is a legacy interface that is preserved for backwards compatibility. +** Use of this interface is not recommended. +** +** Definition: A result table is memory data structure created by the +** [sqlite3_get_table()] interface. A result table records the +** complete query results from one or more queries. +** +** The table conceptually has a number of rows and columns. But +** these numbers are not part of the result table itself. These +** numbers are obtained separately. Let N be the number of rows +** and M be the number of columns. +** +** A result table is an array of pointers to zero-terminated UTF-8 strings. +** There are (N+1)*M elements in the array. The first M pointers point +** to zero-terminated strings that contain the names of the columns. +** The remaining entries all point to query results. NULL values result +** in NULL pointers. All other values are in their UTF-8 zero-terminated +** string representation as returned by [sqlite3_column_text()]. +** +** A result table might consist of one or more memory allocations. +** It is not safe to pass a result table directly to [sqlite3_free()]. +** A result table should be deallocated using [sqlite3_free_table()]. +** +** ^(As an example of the result table format, suppose a query result +** is as follows: +** +**
+**        Name        | Age
+**        -----------------------
+**        Alice       | 43
+**        Bob         | 28
+**        Cindy       | 21
+** 
+** +** There are two column (M==2) and three rows (N==3). Thus the +** result table has 8 entries. Suppose the result table is stored +** in an array names azResult. Then azResult holds this content: +** +**
+**        azResult[0] = "Name";
+**        azResult[1] = "Age";
+**        azResult[2] = "Alice";
+**        azResult[3] = "43";
+**        azResult[4] = "Bob";
+**        azResult[5] = "28";
+**        azResult[6] = "Cindy";
+**        azResult[7] = "21";
+** 
)^ +** +** ^The sqlite3_get_table() function evaluates one or more +** semicolon-separated SQL statements in the zero-terminated UTF-8 +** string of its 2nd parameter and returns a result table to the +** pointer given in its 3rd parameter. +** +** After the application has finished with the result from sqlite3_get_table(), +** it must pass the result table pointer to sqlite3_free_table() in order to +** release the memory that was malloced. Because of the way the +** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling +** function must not try to call [sqlite3_free()] directly. Only +** [sqlite3_free_table()] is able to release the memory properly and safely. +** +** The sqlite3_get_table() interface is implemented as a wrapper around +** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access +** to any internal data structures of SQLite. It uses only the public +** interface defined here. As a consequence, errors that occur in the +** wrapper layer outside of the internal [sqlite3_exec()] call are not +** reflected in subsequent calls to [sqlite3_errcode()] or +** [sqlite3_errmsg()]. +*/ +SQLITE_API int sqlite3_get_table( + sqlite3 *db, /* An open database */ + const char *zSql, /* SQL to be evaluated */ + char ***pazResult, /* Results of the query */ + int *pnRow, /* Number of result rows written here */ + int *pnColumn, /* Number of result columns written here */ + char **pzErrmsg /* Error msg written here */ +); +SQLITE_API void sqlite3_free_table(char **result); + +/* +** CAPI3REF: Formatted String Printing Functions +** +** These routines are work-alikes of the "printf()" family of functions +** from the standard C library. +** +** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their +** results into memory obtained from [sqlite3_malloc()]. +** The strings returned by these two routines should be +** released by [sqlite3_free()]. ^Both routines return a +** NULL pointer if [sqlite3_malloc()] is unable to allocate enough +** memory to hold the resulting string. +** +** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from +** the standard C library. The result is written into the +** buffer supplied as the second parameter whose size is given by +** the first parameter. Note that the order of the +** first two parameters is reversed from snprintf().)^ This is an +** historical accident that cannot be fixed without breaking +** backwards compatibility. ^(Note also that sqlite3_snprintf() +** returns a pointer to its buffer instead of the number of +** characters actually written into the buffer.)^ We admit that +** the number of characters written would be a more useful return +** value but we cannot change the implementation of sqlite3_snprintf() +** now without breaking compatibility. +** +** ^As long as the buffer size is greater than zero, sqlite3_snprintf() +** guarantees that the buffer is always zero-terminated. ^The first +** parameter "n" is the total size of the buffer, including space for +** the zero terminator. So the longest string that can be completely +** written will be n-1 characters. +** +** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). +** +** These routines all implement some additional formatting +** options that are useful for constructing SQL statements. +** All of the usual printf() formatting options apply. In addition, there +** is are "%q", "%Q", and "%z" options. +** +** ^(The %q option works like %s in that it substitutes a nul-terminated +** string from the argument list. But %q also doubles every '\'' character. +** %q is designed for use inside a string literal.)^ By doubling each '\'' +** character it escapes that character and allows it to be inserted into +** the string. +** +** For example, assume the string variable zText contains text as follows: +** +**
+**  char *zText = "It's a happy day!";
+** 
+** +** One can use this text in an SQL statement as follows: +** +**
+**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
+**  sqlite3_exec(db, zSQL, 0, 0, 0);
+**  sqlite3_free(zSQL);
+** 
+** +** Because the %q format string is used, the '\'' character in zText +** is escaped and the SQL generated is as follows: +** +**
+**  INSERT INTO table1 VALUES('It''s a happy day!')
+** 
+** +** This is correct. Had we used %s instead of %q, the generated SQL +** would have looked like this: +** +**
+**  INSERT INTO table1 VALUES('It's a happy day!');
+** 
+** +** This second example is an SQL syntax error. As a general rule you should +** always use %q instead of %s when inserting text into a string literal. +** +** ^(The %Q option works like %q except it also adds single quotes around +** the outside of the total string. Additionally, if the parameter in the +** argument list is a NULL pointer, %Q substitutes the text "NULL" (without +** single quotes).)^ So, for example, one could say: +** +**
+**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
+**  sqlite3_exec(db, zSQL, 0, 0, 0);
+**  sqlite3_free(zSQL);
+** 
+** +** The code above will render a correct SQL statement in the zSQL +** variable even if the zText variable is a NULL pointer. +** +** ^(The "%z" formatting option works like "%s" but with the +** addition that after the string has been read and copied into +** the result, [sqlite3_free()] is called on the input string.)^ +*/ +SQLITE_API char *sqlite3_mprintf(const char*,...); +SQLITE_API char *sqlite3_vmprintf(const char*, va_list); +SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); + +/* +** CAPI3REF: Memory Allocation Subsystem +** +** The SQLite core uses these three routines for all of its own +** internal memory allocation needs. "Core" in the previous sentence +** does not include operating-system specific VFS implementation. The +** Windows VFS uses native malloc() and free() for some operations. +** +** ^The sqlite3_malloc() routine returns a pointer to a block +** of memory at least N bytes in length, where N is the parameter. +** ^If sqlite3_malloc() is unable to obtain sufficient free +** memory, it returns a NULL pointer. ^If the parameter N to +** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns +** a NULL pointer. +** +** ^Calling sqlite3_free() with a pointer previously returned +** by sqlite3_malloc() or sqlite3_realloc() releases that memory so +** that it might be reused. ^The sqlite3_free() routine is +** a no-op if is called with a NULL pointer. Passing a NULL pointer +** to sqlite3_free() is harmless. After being freed, memory +** should neither be read nor written. Even reading previously freed +** memory might result in a segmentation fault or other severe error. +** Memory corruption, a segmentation fault, or other severe error +** might result if sqlite3_free() is called with a non-NULL pointer that +** was not obtained from sqlite3_malloc() or sqlite3_realloc(). +** +** ^(The sqlite3_realloc() interface attempts to resize a +** prior memory allocation to be at least N bytes, where N is the +** second parameter. The memory allocation to be resized is the first +** parameter.)^ ^ If the first parameter to sqlite3_realloc() +** is a NULL pointer then its behavior is identical to calling +** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc(). +** ^If the second parameter to sqlite3_realloc() is zero or +** negative then the behavior is exactly the same as calling +** sqlite3_free(P) where P is the first parameter to sqlite3_realloc(). +** ^sqlite3_realloc() returns a pointer to a memory allocation +** of at least N bytes in size or NULL if sufficient memory is unavailable. +** ^If M is the size of the prior allocation, then min(N,M) bytes +** of the prior allocation are copied into the beginning of buffer returned +** by sqlite3_realloc() and the prior allocation is freed. +** ^If sqlite3_realloc() returns NULL, then the prior allocation +** is not freed. +** +** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() +** is always aligned to at least an 8 byte boundary, or to a +** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time +** option is used. +** +** In SQLite version 3.5.0 and 3.5.1, it was possible to define +** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in +** implementation of these routines to be omitted. That capability +** is no longer provided. Only built-in memory allocators can be used. +** +** Prior to SQLite version 3.7.10, the Windows OS interface layer called +** the system malloc() and free() directly when converting +** filenames between the UTF-8 encoding used by SQLite +** and whatever filename encoding is used by the particular Windows +** installation. Memory allocation errors were detected, but +** they were reported back as [SQLITE_CANTOPEN] or +** [SQLITE_IOERR] rather than [SQLITE_NOMEM]. +** +** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] +** must be either NULL or else pointers obtained from a prior +** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have +** not yet been released. +** +** The application must not read or write any part of +** a block of memory after it has been released using +** [sqlite3_free()] or [sqlite3_realloc()]. +*/ +SQLITE_API void *sqlite3_malloc(int); +SQLITE_API void *sqlite3_realloc(void*, int); +SQLITE_API void sqlite3_free(void*); + +/* +** CAPI3REF: Memory Allocator Statistics +** +** SQLite provides these two interfaces for reporting on the status +** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] +** routines, which form the built-in memory allocation subsystem. +** +** ^The [sqlite3_memory_used()] routine returns the number of bytes +** of memory currently outstanding (malloced but not freed). +** ^The [sqlite3_memory_highwater()] routine returns the maximum +** value of [sqlite3_memory_used()] since the high-water mark +** was last reset. ^The values returned by [sqlite3_memory_used()] and +** [sqlite3_memory_highwater()] include any overhead +** added by SQLite in its implementation of [sqlite3_malloc()], +** but not overhead added by the any underlying system library +** routines that [sqlite3_malloc()] may call. +** +** ^The memory high-water mark is reset to the current value of +** [sqlite3_memory_used()] if and only if the parameter to +** [sqlite3_memory_highwater()] is true. ^The value returned +** by [sqlite3_memory_highwater(1)] is the high-water mark +** prior to the reset. +*/ +SQLITE_API sqlite3_int64 sqlite3_memory_used(void); +SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); + +/* +** CAPI3REF: Pseudo-Random Number Generator +** +** SQLite contains a high-quality pseudo-random number generator (PRNG) used to +** select random [ROWID | ROWIDs] when inserting new records into a table that +** already uses the largest possible [ROWID]. The PRNG is also used for +** the build-in random() and randomblob() SQL functions. This interface allows +** applications to access the same PRNG for other purposes. +** +** ^A call to this routine stores N bytes of randomness into buffer P. +** +** ^The first time this routine is invoked (either internally or by +** the application) the PRNG is seeded using randomness obtained +** from the xRandomness method of the default [sqlite3_vfs] object. +** ^On all subsequent invocations, the pseudo-randomness is generated +** internally and without recourse to the [sqlite3_vfs] xRandomness +** method. +*/ +SQLITE_API void sqlite3_randomness(int N, void *P); + +/* +** CAPI3REF: Compile-Time Authorization Callbacks +** +** ^This routine registers an authorizer callback with a particular +** [database connection], supplied in the first argument. +** ^The authorizer callback is invoked as SQL statements are being compiled +** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], +** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. ^At various +** points during the compilation process, as logic is being created +** to perform various actions, the authorizer callback is invoked to +** see if those actions are allowed. ^The authorizer callback should +** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the +** specific action but allow the SQL statement to continue to be +** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be +** rejected with an error. ^If the authorizer callback returns +** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY] +** then the [sqlite3_prepare_v2()] or equivalent call that triggered +** the authorizer will fail with an error message. +** +** When the callback returns [SQLITE_OK], that means the operation +** requested is ok. ^When the callback returns [SQLITE_DENY], the +** [sqlite3_prepare_v2()] or equivalent call that triggered the +** authorizer will fail with an error message explaining that +** access is denied. +** +** ^The first parameter to the authorizer callback is a copy of the third +** parameter to the sqlite3_set_authorizer() interface. ^The second parameter +** to the callback is an integer [SQLITE_COPY | action code] that specifies +** the particular action to be authorized. ^The third through sixth parameters +** to the callback are zero-terminated strings that contain additional +** details about the action to be authorized. +** +** ^If the action code is [SQLITE_READ] +** and the callback returns [SQLITE_IGNORE] then the +** [prepared statement] statement is constructed to substitute +** a NULL value in place of the table column that would have +** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE] +** return can be used to deny an untrusted user access to individual +** columns of a table. +** ^If the action code is [SQLITE_DELETE] and the callback returns +** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the +** [truncate optimization] is disabled and all rows are deleted individually. +** +** An authorizer is used when [sqlite3_prepare | preparing] +** SQL statements from an untrusted source, to ensure that the SQL statements +** do not try to access data they are not allowed to see, or that they do not +** try to execute malicious statements that damage the database. For +** example, an application may allow a user to enter arbitrary +** SQL queries for evaluation by a database. But the application does +** not want the user to be able to make arbitrary changes to the +** database. An authorizer could then be put in place while the +** user-entered SQL is being [sqlite3_prepare | prepared] that +** disallows everything except [SELECT] statements. +** +** Applications that need to process SQL from untrusted sources +** might also consider lowering resource limits using [sqlite3_limit()] +** and limiting database size using the [max_page_count] [PRAGMA] +** in addition to using an authorizer. +** +** ^(Only a single authorizer can be in place on a database connection +** at a time. Each call to sqlite3_set_authorizer overrides the +** previous call.)^ ^Disable the authorizer by installing a NULL callback. +** The authorizer is disabled by default. +** +** The authorizer callback must not do anything that will modify +** the database connection that invoked the authorizer callback. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the +** statement might be re-prepared during [sqlite3_step()] due to a +** schema change. Hence, the application should ensure that the +** correct authorizer callback remains in place during the [sqlite3_step()]. +** +** ^Note that the authorizer callback is invoked only during +** [sqlite3_prepare()] or its variants. Authorization is not +** performed during statement evaluation in [sqlite3_step()], unless +** as stated in the previous paragraph, sqlite3_step() invokes +** sqlite3_prepare_v2() to reprepare a statement after a schema change. +*/ +SQLITE_API int sqlite3_set_authorizer( + sqlite3*, + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), + void *pUserData +); + +/* +** CAPI3REF: Authorizer Return Codes +** +** The [sqlite3_set_authorizer | authorizer callback function] must +** return either [SQLITE_OK] or one of these two constants in order +** to signal SQLite whether or not the action is permitted. See the +** [sqlite3_set_authorizer | authorizer documentation] for additional +** information. +** +** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code] +** from the [sqlite3_vtab_on_conflict()] interface. +*/ +#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ +#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ + +/* +** CAPI3REF: Authorizer Action Codes +** +** The [sqlite3_set_authorizer()] interface registers a callback function +** that is invoked to authorize certain SQL statement actions. The +** second parameter to the callback is an integer code that specifies +** what action is being authorized. These are the integer action codes that +** the authorizer callback may be passed. +** +** These action code values signify what kind of operation is to be +** authorized. The 3rd and 4th parameters to the authorization +** callback function will be parameters or NULL depending on which of these +** codes is used as the second parameter. ^(The 5th parameter to the +** authorizer callback is the name of the database ("main", "temp", +** etc.) if applicable.)^ ^The 6th parameter to the authorizer callback +** is the name of the inner-most trigger or view that is responsible for +** the access attempt or NULL if this access attempt is directly from +** top-level SQL code. +*/ +/******************************************* 3rd ************ 4th ***********/ +#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ +#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ +#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ +#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ +#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ +#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ +#define SQLITE_DELETE 9 /* Table Name NULL */ +#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ +#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ +#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ +#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ +#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ +#define SQLITE_DROP_VIEW 17 /* View Name NULL */ +#define SQLITE_INSERT 18 /* Table Name NULL */ +#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ +#define SQLITE_READ 20 /* Table Name Column Name */ +#define SQLITE_SELECT 21 /* NULL NULL */ +#define SQLITE_TRANSACTION 22 /* Operation NULL */ +#define SQLITE_UPDATE 23 /* Table Name Column Name */ +#define SQLITE_ATTACH 24 /* Filename NULL */ +#define SQLITE_DETACH 25 /* Database Name NULL */ +#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */ +#define SQLITE_REINDEX 27 /* Index Name NULL */ +#define SQLITE_ANALYZE 28 /* Table Name NULL */ +#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ +#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ +#define SQLITE_FUNCTION 31 /* NULL Function Name */ +#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ +#define SQLITE_COPY 0 /* No longer used */ + +/* +** CAPI3REF: Tracing And Profiling Functions +** +** These routines register callback functions that can be used for +** tracing and profiling the execution of SQL statements. +** +** ^The callback function registered by sqlite3_trace() is invoked at +** various times when an SQL statement is being run by [sqlite3_step()]. +** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the +** SQL statement text as the statement first begins executing. +** ^(Additional sqlite3_trace() callbacks might occur +** as each triggered subprogram is entered. The callbacks for triggers +** contain a UTF-8 SQL comment that identifies the trigger.)^ +** +** ^The callback function registered by sqlite3_profile() is invoked +** as each SQL statement finishes. ^The profile callback contains +** the original statement text and an estimate of wall-clock time +** of how long that statement took to run. ^The profile callback +** time is in units of nanoseconds, however the current implementation +** is only capable of millisecond resolution so the six least significant +** digits in the time are meaningless. Future versions of SQLite +** might provide greater resolution on the profiler callback. The +** sqlite3_profile() function is considered experimental and is +** subject to change in future versions of SQLite. +*/ +SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); +SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, + void(*xProfile)(void*,const char*,sqlite3_uint64), void*); + +/* +** CAPI3REF: Query Progress Callbacks +** +** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback +** function X to be invoked periodically during long running calls to +** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for +** database connection D. An example use for this +** interface is to keep a GUI updated during a large query. +** +** ^The parameter P is passed through as the only parameter to the +** callback function X. ^The parameter N is the number of +** [virtual machine instructions] that are evaluated between successive +** invocations of the callback X. +** +** ^Only a single progress handler may be defined at one time per +** [database connection]; setting a new progress handler cancels the +** old one. ^Setting parameter X to NULL disables the progress handler. +** ^The progress handler is also disabled by setting N to a value less +** than 1. +** +** ^If the progress callback returns non-zero, the operation is +** interrupted. This feature can be used to implement a +** "Cancel" button on a GUI progress dialog box. +** +** The progress handler callback must not do anything that will modify +** the database connection that invoked the progress handler. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +*/ +SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + +/* +** CAPI3REF: Opening A New Database Connection +** +** ^These routines open an SQLite database file as specified by the +** filename argument. ^The filename argument is interpreted as UTF-8 for +** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte +** order for sqlite3_open16(). ^(A [database connection] handle is usually +** returned in *ppDb, even if an error occurs. The only exception is that +** if SQLite is unable to allocate memory to hold the [sqlite3] object, +** a NULL will be written into *ppDb instead of a pointer to the [sqlite3] +** object.)^ ^(If the database is opened (and/or created) successfully, then +** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The +** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain +** an English language description of the error following a failure of any +** of the sqlite3_open() routines. +** +** ^The default encoding for the database will be UTF-8 if +** sqlite3_open() or sqlite3_open_v2() is called and +** UTF-16 in the native byte order if sqlite3_open16() is used. +** +** Whether or not an error occurs when it is opened, resources +** associated with the [database connection] handle should be released by +** passing it to [sqlite3_close()] when it is no longer required. +** +** The sqlite3_open_v2() interface works like sqlite3_open() +** except that it accepts two additional parameters for additional control +** over the new database connection. ^(The flags parameter to +** sqlite3_open_v2() can take one of +** the following three values, optionally combined with the +** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE], +** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^ +** +**
+** ^(
[SQLITE_OPEN_READONLY]
+**
The database is opened in read-only mode. If the database does not +** already exist, an error is returned.
)^ +** +** ^(
[SQLITE_OPEN_READWRITE]
+**
The database is opened for reading and writing if possible, or reading +** only if the file is write protected by the operating system. In either +** case the database must already exist, otherwise an error is returned.
)^ +** +** ^(
[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
+**
The database is opened for reading and writing, and is created if +** it does not already exist. This is the behavior that is always used for +** sqlite3_open() and sqlite3_open16().
)^ +**
+** +** If the 3rd parameter to sqlite3_open_v2() is not one of the +** combinations shown above optionally combined with other +** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] +** then the behavior is undefined. +** +** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection +** opens in the multi-thread [threading mode] as long as the single-thread +** mode has not been set at compile-time or start-time. ^If the +** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens +** in the serialized [threading mode] unless single-thread was +** previously selected at compile-time or start-time. +** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be +** eligible to use [shared cache mode], regardless of whether or not shared +** cache is enabled using [sqlite3_enable_shared_cache()]. ^The +** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not +** participate in [shared cache mode] even if it is enabled. +** +** ^The fourth parameter to sqlite3_open_v2() is the name of the +** [sqlite3_vfs] object that defines the operating system interface that +** the new database connection should use. ^If the fourth parameter is +** a NULL pointer then the default [sqlite3_vfs] object is used. +** +** ^If the filename is ":memory:", then a private, temporary in-memory database +** is created for the connection. ^This in-memory database will vanish when +** the database connection is closed. Future versions of SQLite might +** make use of additional special filenames that begin with the ":" character. +** It is recommended that when a database filename actually does begin with +** a ":" character you should prefix the filename with a pathname such as +** "./" to avoid ambiguity. +** +** ^If the filename is an empty string, then a private, temporary +** on-disk database will be created. ^This private database will be +** automatically deleted as soon as the database connection is closed. +** +** [[URI filenames in sqlite3_open()]]

URI Filenames

+** +** ^If [URI filename] interpretation is enabled, and the filename argument +** begins with "file:", then the filename is interpreted as a URI. ^URI +** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is +** set in the fourth argument to sqlite3_open_v2(), or if it has +** been enabled globally using the [SQLITE_CONFIG_URI] option with the +** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. +** As of SQLite version 3.7.7, URI filename interpretation is turned off +** by default, but future releases of SQLite might enable URI filename +** interpretation by default. See "[URI filenames]" for additional +** information. +** +** URI filenames are parsed according to RFC 3986. ^If the URI contains an +** authority, then it must be either an empty string or the string +** "localhost". ^If the authority is not an empty string or "localhost", an +** error is returned to the caller. ^The fragment component of a URI, if +** present, is ignored. +** +** ^SQLite uses the path component of the URI as the name of the disk file +** which contains the database. ^If the path begins with a '/' character, +** then it is interpreted as an absolute path. ^If the path does not begin +** with a '/' (meaning that the authority section is omitted from the URI) +** then the path is interpreted as a relative path. +** ^On windows, the first component of an absolute path +** is a drive specification (e.g. "C:"). +** +** [[core URI query parameters]] +** The query component of a URI may contain parameters that are interpreted +** either by SQLite itself, or by a [VFS | custom VFS implementation]. +** SQLite interprets the following three query parameters: +** +**
    +**
  • vfs: ^The "vfs" parameter may be used to specify the name of +** a VFS object that provides the operating system interface that should +** be used to access the database file on disk. ^If this option is set to +** an empty string the default VFS object is used. ^Specifying an unknown +** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is +** present, then the VFS specified by the option takes precedence over +** the value passed as the fourth parameter to sqlite3_open_v2(). +** +**
  • mode: ^(The mode parameter may be set to either "ro", "rw", +** "rwc", or "memory". Attempting to set it to any other value is +** an error)^. +** ^If "ro" is specified, then the database is opened for read-only +** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the +** third argument to sqlite3_open_v2(). ^If the mode option is set to +** "rw", then the database is opened for read-write (but not create) +** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had +** been set. ^Value "rwc" is equivalent to setting both +** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is +** set to "memory" then a pure [in-memory database] that never reads +** or writes from disk is used. ^It is an error to specify a value for +** the mode parameter that is less restrictive than that specified by +** the flags passed in the third parameter to sqlite3_open_v2(). +** +**
  • cache: ^The cache parameter may be set to either "shared" or +** "private". ^Setting it to "shared" is equivalent to setting the +** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to +** sqlite3_open_v2(). ^Setting the cache parameter to "private" is +** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. +** ^If sqlite3_open_v2() is used and the "cache" parameter is present in +** a URI filename, its value overrides any behavior requested by setting +** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. +**
+** +** ^Specifying an unknown parameter in the query component of a URI is not an +** error. Future versions of SQLite might understand additional query +** parameters. See "[query parameters with special meaning to SQLite]" for +** additional information. +** +** [[URI filename examples]]

URI filename examples

+** +** +**
URI filenames Results +**
file:data.db +** Open the file "data.db" in the current directory. +**
file:/home/fred/data.db
+** file:///home/fred/data.db
+** file://localhost/home/fred/data.db
+** Open the database file "/home/fred/data.db". +**
file://darkstar/home/fred/data.db +** An error. "darkstar" is not a recognized authority. +**
+** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db +** Windows only: Open the file "data.db" on fred's desktop on drive +** C:. Note that the %20 escaping in this example is not strictly +** necessary - space characters can be used literally +** in URI filenames. +**
file:data.db?mode=ro&cache=private +** Open file "data.db" in the current directory for read-only access. +** Regardless of whether or not shared-cache mode is enabled by +** default, use a private cache. +**
file:/home/fred/data.db?vfs=unix-nolock +** Open file "/home/fred/data.db". Use the special VFS "unix-nolock". +**
file:data.db?mode=readonly +** An error. "readonly" is not a valid option for the "mode" parameter. +**
+** +** ^URI hexadecimal escape sequences (%HH) are supported within the path and +** query components of a URI. A hexadecimal escape sequence consists of a +** percent sign - "%" - followed by exactly two hexadecimal digits +** specifying an octet value. ^Before the path or query components of a +** URI filename are interpreted, they are encoded using UTF-8 and all +** hexadecimal escape sequences replaced by a single byte containing the +** corresponding octet. If this process generates an invalid UTF-8 encoding, +** the results are undefined. +** +** Note to Windows users: The encoding used for the filename argument +** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever +** codepage is currently defined. Filenames containing international +** characters must be converted to UTF-8 prior to passing them into +** sqlite3_open() or sqlite3_open_v2(). +** +** Note to Windows Runtime users: The temporary directory must be set +** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various +** features that require the use of temporary files may fail. +** +** See also: [sqlite3_temp_directory] +*/ +SQLITE_API int sqlite3_open( + const char *filename, /* Database filename (UTF-8) */ + sqlite3 **ppDb /* OUT: SQLite db handle */ +); +SQLITE_API int sqlite3_open16( + const void *filename, /* Database filename (UTF-16) */ + sqlite3 **ppDb /* OUT: SQLite db handle */ +); +SQLITE_API int sqlite3_open_v2( + const char *filename, /* Database filename (UTF-8) */ + sqlite3 **ppDb, /* OUT: SQLite db handle */ + int flags, /* Flags */ + const char *zVfs /* Name of VFS module to use */ +); + +/* +** CAPI3REF: Obtain Values For URI Parameters +** +** These are utility routines, useful to VFS implementations, that check +** to see if a database file was a URI that contained a specific query +** parameter, and if so obtains the value of that query parameter. +** +** If F is the database filename pointer passed into the xOpen() method of +** a VFS implementation when the flags parameter to xOpen() has one or +** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and +** P is the name of the query parameter, then +** sqlite3_uri_parameter(F,P) returns the value of the P +** parameter if it exists or a NULL pointer if P does not appear as a +** query parameter on F. If P is a query parameter of F +** has no explicit value, then sqlite3_uri_parameter(F,P) returns +** a pointer to an empty string. +** +** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean +** parameter and returns true (1) or false (0) according to the value +** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the +** value of query parameter P is one of "yes", "true", or "on" in any +** case or if the value begins with a non-zero number. The +** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of +** query parameter P is one of "no", "false", or "off" in any case or +** if the value begins with a numeric zero. If P is not a query +** parameter on F or if the value of P is does not match any of the +** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0). +** +** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a +** 64-bit signed integer and returns that integer, or D if P does not +** exist. If the value of P is something other than an integer, then +** zero is returned. +** +** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and +** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and +** is not a database file pathname pointer that SQLite passed into the xOpen +** VFS method, then the behavior of this routine is undefined and probably +** undesirable. +*/ +SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); +SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); + + +/* +** CAPI3REF: Error Codes And Messages +** +** ^The sqlite3_errcode() interface returns the numeric [result code] or +** [extended result code] for the most recent failed sqlite3_* API call +** associated with a [database connection]. If a prior API call failed +** but the most recent API call succeeded, the return value from +** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() +** interface is the same except that it always returns the +** [extended result code] even when extended result codes are +** disabled. +** +** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language +** text that describes the error, as either UTF-8 or UTF-16 respectively. +** ^(Memory to hold the error message string is managed internally. +** The application does not need to worry about freeing the result. +** However, the error string might be overwritten or deallocated by +** subsequent calls to other SQLite interface functions.)^ +** +** ^The sqlite3_errstr() interface returns the English-language text +** that describes the [result code], as UTF-8. +** ^(Memory to hold the error message string is managed internally +** and must not be freed by the application)^. +** +** When the serialized [threading mode] is in use, it might be the +** case that a second error occurs on a separate thread in between +** the time of the first error and the call to these interfaces. +** When that happens, the second error will be reported since these +** interfaces always report the most recent result. To avoid +** this, each thread can obtain exclusive use of the [database connection] D +** by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning +** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after +** all calls to the interfaces listed here are completed. +** +** If an interface fails with SQLITE_MISUSE, that means the interface +** was invoked incorrectly by the application. In that case, the +** error code and message may or may not be set. +*/ +SQLITE_API int sqlite3_errcode(sqlite3 *db); +SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); +SQLITE_API const char *sqlite3_errmsg(sqlite3*); +SQLITE_API const void *sqlite3_errmsg16(sqlite3*); +SQLITE_API const char *sqlite3_errstr(int); + +/* +** CAPI3REF: SQL Statement Object +** KEYWORDS: {prepared statement} {prepared statements} +** +** An instance of this object represents a single SQL statement. +** This object is variously known as a "prepared statement" or a +** "compiled SQL statement" or simply as a "statement". +** +** The life of a statement object goes something like this: +** +**
    +**
  1. Create the object using [sqlite3_prepare_v2()] or a related +** function. +**
  2. Bind values to [host parameters] using the sqlite3_bind_*() +** interfaces. +**
  3. Run the SQL by calling [sqlite3_step()] one or more times. +**
  4. Reset the statement using [sqlite3_reset()] then go back +** to step 2. Do this zero or more times. +**
  5. Destroy the object using [sqlite3_finalize()]. +**
+** +** Refer to documentation on individual methods above for additional +** information. +*/ +typedef struct sqlite3_stmt sqlite3_stmt; + +/* +** CAPI3REF: Run-time Limits +** +** ^(This interface allows the size of various constructs to be limited +** on a connection by connection basis. The first parameter is the +** [database connection] whose limit is to be set or queried. The +** second parameter is one of the [limit categories] that define a +** class of constructs to be size limited. The third parameter is the +** new limit for that construct.)^ +** +** ^If the new limit is a negative number, the limit is unchanged. +** ^(For each limit category SQLITE_LIMIT_NAME there is a +** [limits | hard upper bound] +** set at compile-time by a C preprocessor macro called +** [limits | SQLITE_MAX_NAME]. +** (The "_LIMIT_" in the name is changed to "_MAX_".))^ +** ^Attempts to increase a limit above its hard upper bound are +** silently truncated to the hard upper bound. +** +** ^Regardless of whether or not the limit was changed, the +** [sqlite3_limit()] interface returns the prior value of the limit. +** ^Hence, to find the current value of a limit without changing it, +** simply invoke this interface with the third parameter set to -1. +** +** Run-time limits are intended for use in applications that manage +** both their own internal database and also databases that are controlled +** by untrusted external sources. An example application might be a +** web browser that has its own databases for storing history and +** separate databases controlled by JavaScript applications downloaded +** off the Internet. The internal databases can be given the +** large, default limits. Databases managed by external sources can +** be given much smaller limits designed to prevent a denial of service +** attack. Developers might also want to use the [sqlite3_set_authorizer()] +** interface to further control untrusted SQL. The size of the database +** created by an untrusted script can be contained using the +** [max_page_count] [PRAGMA]. +** +** New run-time limit categories may be added in future releases. +*/ +SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); + +/* +** CAPI3REF: Run-Time Limit Categories +** KEYWORDS: {limit category} {*limit categories} +** +** These constants define various performance limits +** that can be lowered at run-time using [sqlite3_limit()]. +** The synopsis of the meanings of the various limits is shown below. +** Additional information is available at [limits | Limits in SQLite]. +** +**
+** [[SQLITE_LIMIT_LENGTH]] ^(
SQLITE_LIMIT_LENGTH
+**
The maximum size of any string or BLOB or table row, in bytes.
)^ +** +** [[SQLITE_LIMIT_SQL_LENGTH]] ^(
SQLITE_LIMIT_SQL_LENGTH
+**
The maximum length of an SQL statement, in bytes.
)^ +** +** [[SQLITE_LIMIT_COLUMN]] ^(
SQLITE_LIMIT_COLUMN
+**
The maximum number of columns in a table definition or in the +** result set of a [SELECT] or the maximum number of columns in an index +** or in an ORDER BY or GROUP BY clause.
)^ +** +** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(
SQLITE_LIMIT_EXPR_DEPTH
+**
The maximum depth of the parse tree on any expression.
)^ +** +** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(
SQLITE_LIMIT_COMPOUND_SELECT
+**
The maximum number of terms in a compound SELECT statement.
)^ +** +** [[SQLITE_LIMIT_VDBE_OP]] ^(
SQLITE_LIMIT_VDBE_OP
+**
The maximum number of instructions in a virtual machine program +** used to implement an SQL statement. This limit is not currently +** enforced, though that might be added in some future release of +** SQLite.
)^ +** +** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(
SQLITE_LIMIT_FUNCTION_ARG
+**
The maximum number of arguments on a function.
)^ +** +** [[SQLITE_LIMIT_ATTACHED]] ^(
SQLITE_LIMIT_ATTACHED
+**
The maximum number of [ATTACH | attached databases].)^
+** +** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]] +** ^(
SQLITE_LIMIT_LIKE_PATTERN_LENGTH
+**
The maximum length of the pattern argument to the [LIKE] or +** [GLOB] operators.
)^ +** +** [[SQLITE_LIMIT_VARIABLE_NUMBER]] +** ^(
SQLITE_LIMIT_VARIABLE_NUMBER
+**
The maximum index number of any [parameter] in an SQL statement.)^ +** +** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(
SQLITE_LIMIT_TRIGGER_DEPTH
+**
The maximum depth of recursion for triggers.
)^ +**
+*/ +#define SQLITE_LIMIT_LENGTH 0 +#define SQLITE_LIMIT_SQL_LENGTH 1 +#define SQLITE_LIMIT_COLUMN 2 +#define SQLITE_LIMIT_EXPR_DEPTH 3 +#define SQLITE_LIMIT_COMPOUND_SELECT 4 +#define SQLITE_LIMIT_VDBE_OP 5 +#define SQLITE_LIMIT_FUNCTION_ARG 6 +#define SQLITE_LIMIT_ATTACHED 7 +#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 +#define SQLITE_LIMIT_VARIABLE_NUMBER 9 +#define SQLITE_LIMIT_TRIGGER_DEPTH 10 + +/* +** CAPI3REF: Compiling An SQL Statement +** KEYWORDS: {SQL statement compiler} +** +** To execute an SQL query, it must first be compiled into a byte-code +** program using one of these routines. +** +** The first argument, "db", is a [database connection] obtained from a +** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or +** [sqlite3_open16()]. The database connection must not have been closed. +** +** The second argument, "zSql", is the statement to be compiled, encoded +** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2() +** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() +** use UTF-16. +** +** ^If the nByte argument is less than zero, then zSql is read up to the +** first zero terminator. ^If nByte is non-negative, then it is the maximum +** number of bytes read from zSql. ^When nByte is non-negative, the +** zSql string ends at either the first '\000' or '\u0000' character or +** the nByte-th byte, whichever comes first. If the caller knows +** that the supplied string is nul-terminated, then there is a small +** performance advantage to be gained by passing an nByte parameter that +** is equal to the number of bytes in the input string including +** the nul-terminator bytes as this saves SQLite from having to +** make a copy of the input string. +** +** ^If pzTail is not NULL then *pzTail is made to point to the first byte +** past the end of the first SQL statement in zSql. These routines only +** compile the first statement in zSql, so *pzTail is left pointing to +** what remains uncompiled. +** +** ^*ppStmt is left pointing to a compiled [prepared statement] that can be +** executed using [sqlite3_step()]. ^If there is an error, *ppStmt is set +** to NULL. ^If the input text contains no SQL (if the input is an empty +** string or a comment) then *ppStmt is set to NULL. +** The calling procedure is responsible for deleting the compiled +** SQL statement using [sqlite3_finalize()] after it has finished with it. +** ppStmt may not be NULL. +** +** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; +** otherwise an [error code] is returned. +** +** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are +** recommended for all new programs. The two older interfaces are retained +** for backwards compatibility, but their use is discouraged. +** ^In the "v2" interfaces, the prepared statement +** that is returned (the [sqlite3_stmt] object) contains a copy of the +** original SQL text. This causes the [sqlite3_step()] interface to +** behave differently in three ways: +** +**
    +**
  1. +** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it +** always used to do, [sqlite3_step()] will automatically recompile the SQL +** statement and try to run it again. +**
  2. +** +**
  3. +** ^When an error occurs, [sqlite3_step()] will return one of the detailed +** [error codes] or [extended error codes]. ^The legacy behavior was that +** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code +** and the application would have to make a second call to [sqlite3_reset()] +** in order to find the underlying cause of the problem. With the "v2" prepare +** interfaces, the underlying reason for the error is returned immediately. +**
  4. +** +**
  5. +** ^If the specific value bound to [parameter | host parameter] in the +** WHERE clause might influence the choice of query plan for a statement, +** then the statement will be automatically recompiled, as if there had been +** a schema change, on the first [sqlite3_step()] call following any change +** to the [sqlite3_bind_text | bindings] of that [parameter]. +** ^The specific value of WHERE-clause [parameter] might influence the +** choice of query plan if the parameter is the left-hand side of a [LIKE] +** or [GLOB] operator or if the parameter is compared to an indexed column +** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. +** the +**
  6. +**
+*/ +SQLITE_API int sqlite3_prepare( + sqlite3 *db, /* Database handle */ + const char *zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const char **pzTail /* OUT: Pointer to unused portion of zSql */ +); +SQLITE_API int sqlite3_prepare_v2( + sqlite3 *db, /* Database handle */ + const char *zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const char **pzTail /* OUT: Pointer to unused portion of zSql */ +); +SQLITE_API int sqlite3_prepare16( + sqlite3 *db, /* Database handle */ + const void *zSql, /* SQL statement, UTF-16 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const void **pzTail /* OUT: Pointer to unused portion of zSql */ +); +SQLITE_API int sqlite3_prepare16_v2( + sqlite3 *db, /* Database handle */ + const void *zSql, /* SQL statement, UTF-16 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const void **pzTail /* OUT: Pointer to unused portion of zSql */ +); + +/* +** CAPI3REF: Retrieving Statement SQL +** +** ^This interface can be used to retrieve a saved copy of the original +** SQL text used to create a [prepared statement] if that statement was +** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. +*/ +SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Determine If An SQL Statement Writes The Database +** +** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if +** and only if the [prepared statement] X makes no direct changes to +** the content of the database file. +** +** Note that [application-defined SQL functions] or +** [virtual tables] might change the database indirectly as a side effect. +** ^(For example, if an application defines a function "eval()" that +** calls [sqlite3_exec()], then the following SQL statement would +** change the database file through side-effects: +** +**
+**    SELECT eval('DELETE FROM t1') FROM t2;
+** 
+** +** But because the [SELECT] statement does not change the database file +** directly, sqlite3_stmt_readonly() would still return true.)^ +** +** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK], +** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, +** since the statements themselves do not actually modify the database but +** rather they control the timing of when other statements modify the +** database. ^The [ATTACH] and [DETACH] statements also cause +** sqlite3_stmt_readonly() to return true since, while those statements +** change the configuration of a database connection, they do not make +** changes to the content of the database files on disk. +*/ +SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Determine If A Prepared Statement Has Been Reset +** +** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the +** [prepared statement] S has been stepped at least once using +** [sqlite3_step(S)] but has not run to completion and/or has not +** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) +** interface returns false if S is a NULL pointer. If S is not a +** NULL pointer and is not a pointer to a valid [prepared statement] +** object, then the behavior is undefined and probably undesirable. +** +** This interface can be used in combination [sqlite3_next_stmt()] +** to locate all prepared statements associated with a database +** connection that are in need of being reset. This can be used, +** for example, in diagnostic routines to search for prepared +** statements that are holding a transaction open. +*/ +SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); + +/* +** CAPI3REF: Dynamically Typed Value Object +** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} +** +** SQLite uses the sqlite3_value object to represent all values +** that can be stored in a database table. SQLite uses dynamic typing +** for the values it stores. ^Values stored in sqlite3_value objects +** can be integers, floating point values, strings, BLOBs, or NULL. +** +** An sqlite3_value object may be either "protected" or "unprotected". +** Some interfaces require a protected sqlite3_value. Other interfaces +** will accept either a protected or an unprotected sqlite3_value. +** Every interface that accepts sqlite3_value arguments specifies +** whether or not it requires a protected sqlite3_value. +** +** The terms "protected" and "unprotected" refer to whether or not +** a mutex is held. An internal mutex is held for a protected +** sqlite3_value object but no mutex is held for an unprotected +** sqlite3_value object. If SQLite is compiled to be single-threaded +** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) +** or if SQLite is run in one of reduced mutex modes +** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] +** then there is no distinction between protected and unprotected +** sqlite3_value objects and they can be used interchangeably. However, +** for maximum code portability it is recommended that applications +** still make the distinction between protected and unprotected +** sqlite3_value objects even when not strictly required. +** +** ^The sqlite3_value objects that are passed as parameters into the +** implementation of [application-defined SQL functions] are protected. +** ^The sqlite3_value object returned by +** [sqlite3_column_value()] is unprotected. +** Unprotected sqlite3_value objects may only be used with +** [sqlite3_result_value()] and [sqlite3_bind_value()]. +** The [sqlite3_value_blob | sqlite3_value_type()] family of +** interfaces require protected sqlite3_value objects. +*/ +typedef struct Mem sqlite3_value; + +/* +** CAPI3REF: SQL Function Context Object +** +** The context in which an SQL function executes is stored in an +** sqlite3_context object. ^A pointer to an sqlite3_context object +** is always first parameter to [application-defined SQL functions]. +** The application-defined SQL function implementation will pass this +** pointer through into calls to [sqlite3_result_int | sqlite3_result()], +** [sqlite3_aggregate_context()], [sqlite3_user_data()], +** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()], +** and/or [sqlite3_set_auxdata()]. +*/ +typedef struct sqlite3_context sqlite3_context; + +/* +** CAPI3REF: Binding Values To Prepared Statements +** KEYWORDS: {host parameter} {host parameters} {host parameter name} +** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} +** +** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, +** literals may be replaced by a [parameter] that matches one of following +** templates: +** +**
    +**
  • ? +**
  • ?NNN +**
  • :VVV +**
  • @VVV +**
  • $VVV +**
+** +** In the templates above, NNN represents an integer literal, +** and VVV represents an alphanumeric identifier.)^ ^The values of these +** parameters (also called "host parameter names" or "SQL parameters") +** can be set using the sqlite3_bind_*() routines defined here. +** +** ^The first argument to the sqlite3_bind_*() routines is always +** a pointer to the [sqlite3_stmt] object returned from +** [sqlite3_prepare_v2()] or its variants. +** +** ^The second argument is the index of the SQL parameter to be set. +** ^The leftmost SQL parameter has an index of 1. ^When the same named +** SQL parameter is used more than once, second and subsequent +** occurrences have the same index as the first occurrence. +** ^The index for named parameters can be looked up using the +** [sqlite3_bind_parameter_index()] API if desired. ^The index +** for "?NNN" parameters is the value of NNN. +** ^The NNN value must be between 1 and the [sqlite3_limit()] +** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). +** +** ^The third argument is the value to bind to the parameter. +** +** ^(In those routines that have a fourth argument, its value is the +** number of bytes in the parameter. To be clear: the value is the +** number of bytes in the value, not the number of characters.)^ +** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() +** is negative, then the length of the string is +** the number of bytes up to the first zero terminator. +** If the fourth parameter to sqlite3_bind_blob() is negative, then +** the behavior is undefined. +** If a non-negative fourth parameter is provided to sqlite3_bind_text() +** or sqlite3_bind_text16() then that parameter must be the byte offset +** where the NUL terminator would occur assuming the string were NUL +** terminated. If any NUL characters occur at byte offsets less than +** the value of the fourth parameter then the resulting string value will +** contain embedded NULs. The result of expressions involving strings +** with embedded NULs is undefined. +** +** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and +** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or +** string after SQLite has finished with it. ^The destructor is called +** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), +** sqlite3_bind_text(), or sqlite3_bind_text16() fails. +** ^If the fifth argument is +** the special value [SQLITE_STATIC], then SQLite assumes that the +** information is in static, unmanaged space and does not need to be freed. +** ^If the fifth argument has the value [SQLITE_TRANSIENT], then +** SQLite makes its own private copy of the data immediately, before +** the sqlite3_bind_*() routine returns. +** +** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that +** is filled with zeroes. ^A zeroblob uses a fixed amount of memory +** (just an integer to hold its size) while it is being processed. +** Zeroblobs are intended to serve as placeholders for BLOBs whose +** content is later written using +** [sqlite3_blob_open | incremental BLOB I/O] routines. +** ^A negative value for the zeroblob results in a zero-length BLOB. +** +** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer +** for the [prepared statement] or with a prepared statement for which +** [sqlite3_step()] has been called more recently than [sqlite3_reset()], +** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() +** routine is passed a [prepared statement] that has been finalized, the +** result is undefined and probably harmful. +** +** ^Bindings are not cleared by the [sqlite3_reset()] routine. +** ^Unbound parameters are interpreted as NULL. +** +** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an +** [error code] if anything goes wrong. +** ^[SQLITE_RANGE] is returned if the parameter +** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. +** +** See also: [sqlite3_bind_parameter_count()], +** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. +*/ +SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); +SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); +SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); +SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); +SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); +SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); +SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); + +/* +** CAPI3REF: Number Of SQL Parameters +** +** ^This routine can be used to find the number of [SQL parameters] +** in a [prepared statement]. SQL parameters are tokens of the +** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as +** placeholders for values that are [sqlite3_bind_blob | bound] +** to the parameters at a later time. +** +** ^(This routine actually returns the index of the largest (rightmost) +** parameter. For all forms except ?NNN, this will correspond to the +** number of unique parameters. If parameters of the ?NNN form are used, +** there may be gaps in the list.)^ +** +** See also: [sqlite3_bind_blob|sqlite3_bind()], +** [sqlite3_bind_parameter_name()], and +** [sqlite3_bind_parameter_index()]. +*/ +SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); + +/* +** CAPI3REF: Name Of A Host Parameter +** +** ^The sqlite3_bind_parameter_name(P,N) interface returns +** the name of the N-th [SQL parameter] in the [prepared statement] P. +** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" +** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" +** respectively. +** In other words, the initial ":" or "$" or "@" or "?" +** is included as part of the name.)^ +** ^Parameters of the form "?" without a following integer have no name +** and are referred to as "nameless" or "anonymous parameters". +** +** ^The first host parameter has an index of 1, not 0. +** +** ^If the value N is out of range or if the N-th parameter is +** nameless, then NULL is returned. ^The returned string is +** always in UTF-8 encoding even if the named parameter was +** originally specified as UTF-16 in [sqlite3_prepare16()] or +** [sqlite3_prepare16_v2()]. +** +** See also: [sqlite3_bind_blob|sqlite3_bind()], +** [sqlite3_bind_parameter_count()], and +** [sqlite3_bind_parameter_index()]. +*/ +SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); + +/* +** CAPI3REF: Index Of A Parameter With A Given Name +** +** ^Return the index of an SQL parameter given its name. ^The +** index value returned is suitable for use as the second +** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero +** is returned if no matching parameter is found. ^The parameter +** name must be given in UTF-8 even if the original statement +** was prepared from UTF-16 text using [sqlite3_prepare16_v2()]. +** +** See also: [sqlite3_bind_blob|sqlite3_bind()], +** [sqlite3_bind_parameter_count()], and +** [sqlite3_bind_parameter_index()]. +*/ +SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); + +/* +** CAPI3REF: Reset All Bindings On A Prepared Statement +** +** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset +** the [sqlite3_bind_blob | bindings] on a [prepared statement]. +** ^Use this routine to reset all host parameters to NULL. +*/ +SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); + +/* +** CAPI3REF: Number Of Columns In A Result Set +** +** ^Return the number of columns in the result set returned by the +** [prepared statement]. ^This routine returns 0 if pStmt is an SQL +** statement that does not return data (for example an [UPDATE]). +** +** See also: [sqlite3_data_count()] +*/ +SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Column Names In A Result Set +** +** ^These routines return the name assigned to a particular column +** in the result set of a [SELECT] statement. ^The sqlite3_column_name() +** interface returns a pointer to a zero-terminated UTF-8 string +** and sqlite3_column_name16() returns a pointer to a zero-terminated +** UTF-16 string. ^The first parameter is the [prepared statement] +** that implements the [SELECT] statement. ^The second parameter is the +** column number. ^The leftmost column is number 0. +** +** ^The returned string pointer is valid until either the [prepared statement] +** is destroyed by [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the next call to +** sqlite3_column_name() or sqlite3_column_name16() on the same column. +** +** ^If sqlite3_malloc() fails during the processing of either routine +** (for example during a conversion from UTF-8 to UTF-16) then a +** NULL pointer is returned. +** +** ^The name of a result column is the value of the "AS" clause for +** that column, if there is an AS clause. If there is no AS clause +** then the name of the column is unspecified and may change from +** one release of SQLite to the next. +*/ +SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); +SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); + +/* +** CAPI3REF: Source Of Data In A Query Result +** +** ^These routines provide a means to determine the database, table, and +** table column that is the origin of a particular result column in +** [SELECT] statement. +** ^The name of the database or table or column can be returned as +** either a UTF-8 or UTF-16 string. ^The _database_ routines return +** the database name, the _table_ routines return the table name, and +** the origin_ routines return the column name. +** ^The returned string is valid until the [prepared statement] is destroyed +** using [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the same information is requested +** again in a different encoding. +** +** ^The names returned are the original un-aliased names of the +** database, table, and column. +** +** ^The first argument to these interfaces is a [prepared statement]. +** ^These functions return information about the Nth result column returned by +** the statement, where N is the second function argument. +** ^The left-most column is column 0 for these routines. +** +** ^If the Nth column returned by the statement is an expression or +** subquery and is not a column value, then all of these functions return +** NULL. ^These routine might also return NULL if a memory allocation error +** occurs. ^Otherwise, they return the name of the attached database, table, +** or column that query result column was extracted from. +** +** ^As with all other SQLite APIs, those whose names end with "16" return +** UTF-16 encoded strings and the other functions return UTF-8. +** +** ^These APIs are only available if the library was compiled with the +** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol. +** +** If two or more threads call one or more of these routines against the same +** prepared statement and column at the same time then the results are +** undefined. +** +** If two or more threads call one or more +** [sqlite3_column_database_name | column metadata interfaces] +** for the same [prepared statement] and result column +** at the same time then the results are undefined. +*/ +SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); + +/* +** CAPI3REF: Declared Datatype Of A Query Result +** +** ^(The first parameter is a [prepared statement]. +** If this statement is a [SELECT] statement and the Nth column of the +** returned result set of that [SELECT] is a table column (not an +** expression or subquery) then the declared type of the table +** column is returned.)^ ^If the Nth column of the result set is an +** expression or subquery, then a NULL pointer is returned. +** ^The returned string is always UTF-8 encoded. +** +** ^(For example, given the database schema: +** +** CREATE TABLE t1(c1 VARIANT); +** +** and the following statement to be compiled: +** +** SELECT c1 + 1, c1 FROM t1; +** +** this routine would return the string "VARIANT" for the second result +** column (i==1), and a NULL pointer for the first result column (i==0).)^ +** +** ^SQLite uses dynamic run-time typing. ^So just because a column +** is declared to contain a particular type does not mean that the +** data stored in that column is of the declared type. SQLite is +** strongly typed, but the typing is dynamic not static. ^Type +** is associated with individual values, not with the containers +** used to hold those values. +*/ +SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); + +/* +** CAPI3REF: Evaluate An SQL Statement +** +** After a [prepared statement] has been prepared using either +** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy +** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function +** must be called one or more times to evaluate the statement. +** +** The details of the behavior of the sqlite3_step() interface depend +** on whether the statement was prepared using the newer "v2" interface +** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy +** interface [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the +** new "v2" interface is recommended for new applications but the legacy +** interface will continue to be supported. +** +** ^In the legacy interface, the return value will be either [SQLITE_BUSY], +** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. +** ^With the "v2" interface, any of the other [result codes] or +** [extended result codes] might be returned as well. +** +** ^[SQLITE_BUSY] means that the database engine was unable to acquire the +** database locks it needs to do its job. ^If the statement is a [COMMIT] +** or occurs outside of an explicit transaction, then you can retry the +** statement. If the statement is not a [COMMIT] and occurs within an +** explicit transaction then you should rollback the transaction before +** continuing. +** +** ^[SQLITE_DONE] means that the statement has finished executing +** successfully. sqlite3_step() should not be called again on this virtual +** machine without first calling [sqlite3_reset()] to reset the virtual +** machine back to its initial state. +** +** ^If the SQL statement being executed returns any data, then [SQLITE_ROW] +** is returned each time a new row of data is ready for processing by the +** caller. The values may be accessed using the [column access functions]. +** sqlite3_step() is called again to retrieve the next row of data. +** +** ^[SQLITE_ERROR] means that a run-time error (such as a constraint +** violation) has occurred. sqlite3_step() should not be called again on +** the VM. More information may be found by calling [sqlite3_errmsg()]. +** ^With the legacy interface, a more specific error code (for example, +** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) +** can be obtained by calling [sqlite3_reset()] on the +** [prepared statement]. ^In the "v2" interface, +** the more specific error code is returned directly by sqlite3_step(). +** +** [SQLITE_MISUSE] means that the this routine was called inappropriately. +** Perhaps it was called on a [prepared statement] that has +** already been [sqlite3_finalize | finalized] or on one that had +** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could +** be the case that the same database connection is being used by two or +** more threads at the same moment in time. +** +** For all versions of SQLite up to and including 3.6.23.1, a call to +** [sqlite3_reset()] was required after sqlite3_step() returned anything +** other than [SQLITE_ROW] before any subsequent invocation of +** sqlite3_step(). Failure to reset the prepared statement using +** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from +** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began +** calling [sqlite3_reset()] automatically in this circumstance rather +** than returning [SQLITE_MISUSE]. This is not considered a compatibility +** break because any application that ever receives an SQLITE_MISUSE error +** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option +** can be used to restore the legacy behavior. +** +** Goofy Interface Alert: In the legacy interface, the sqlite3_step() +** API always returns a generic error code, [SQLITE_ERROR], following any +** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call +** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the +** specific [error codes] that better describes the error. +** We admit that this is a goofy design. The problem has been fixed +** with the "v2" interface. If you prepare all of your SQL statements +** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead +** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, +** then the more specific [error codes] are returned directly +** by sqlite3_step(). The use of the "v2" interface is recommended. +*/ +SQLITE_API int sqlite3_step(sqlite3_stmt*); + +/* +** CAPI3REF: Number of columns in a result set +** +** ^The sqlite3_data_count(P) interface returns the number of columns in the +** current row of the result set of [prepared statement] P. +** ^If prepared statement P does not have results ready to return +** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of +** interfaces) then sqlite3_data_count(P) returns 0. +** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. +** ^The sqlite3_data_count(P) routine returns 0 if the previous call to +** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) +** will return non-zero if previous call to [sqlite3_step](P) returned +** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] +** where it always returns zero since each step of that multi-step +** pragma returns 0 columns of data. +** +** See also: [sqlite3_column_count()] +*/ +SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Fundamental Datatypes +** KEYWORDS: SQLITE_TEXT +** +** ^(Every value in SQLite has one of five fundamental datatypes: +** +**
    +**
  • 64-bit signed integer +**
  • 64-bit IEEE floating point number +**
  • string +**
  • BLOB +**
  • NULL +**
)^ +** +** These constants are codes for each of those types. +** +** Note that the SQLITE_TEXT constant was also used in SQLite version 2 +** for a completely different meaning. Software that links against both +** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not +** SQLITE_TEXT. +*/ +#define SQLITE_INTEGER 1 +#define SQLITE_FLOAT 2 +#define SQLITE_BLOB 4 +#define SQLITE_NULL 5 +#ifdef SQLITE_TEXT +# undef SQLITE_TEXT +#else +# define SQLITE_TEXT 3 +#endif +#define SQLITE3_TEXT 3 + +/* +** CAPI3REF: Result Values From A Query +** KEYWORDS: {column access functions} +** +** These routines form the "result set" interface. +** +** ^These routines return information about a single column of the current +** result row of a query. ^In every case the first argument is a pointer +** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] +** that was returned from [sqlite3_prepare_v2()] or one of its variants) +** and the second argument is the index of the column for which information +** should be returned. ^The leftmost column of the result set has the index 0. +** ^The number of columns in the result can be determined using +** [sqlite3_column_count()]. +** +** If the SQL statement does not currently point to a valid row, or if the +** column index is out of range, the result is undefined. +** These routines may only be called when the most recent call to +** [sqlite3_step()] has returned [SQLITE_ROW] and neither +** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently. +** If any of these routines are called after [sqlite3_reset()] or +** [sqlite3_finalize()] or after [sqlite3_step()] has returned +** something other than [SQLITE_ROW], the results are undefined. +** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] +** are called from a different thread while any of these routines +** are pending, then the results are undefined. +** +** ^The sqlite3_column_type() routine returns the +** [SQLITE_INTEGER | datatype code] for the initial data type +** of the result column. ^The returned value is one of [SQLITE_INTEGER], +** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value +** returned by sqlite3_column_type() is only meaningful if no type +** conversions have occurred as described below. After a type conversion, +** the value returned by sqlite3_column_type() is undefined. Future +** versions of SQLite may change the behavior of sqlite3_column_type() +** following a type conversion. +** +** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() +** routine returns the number of bytes in that BLOB or string. +** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts +** the string to UTF-8 and then returns the number of bytes. +** ^If the result is a numeric value then sqlite3_column_bytes() uses +** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns +** the number of bytes in that string. +** ^If the result is NULL, then sqlite3_column_bytes() returns zero. +** +** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() +** routine returns the number of bytes in that BLOB or string. +** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts +** the string to UTF-16 and then returns the number of bytes. +** ^If the result is a numeric value then sqlite3_column_bytes16() uses +** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns +** the number of bytes in that string. +** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. +** +** ^The values returned by [sqlite3_column_bytes()] and +** [sqlite3_column_bytes16()] do not include the zero terminators at the end +** of the string. ^For clarity: the values returned by +** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of +** bytes in the string, not the number of characters. +** +** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), +** even empty strings, are always zero-terminated. ^The return +** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. +** +** ^The object returned by [sqlite3_column_value()] is an +** [unprotected sqlite3_value] object. An unprotected sqlite3_value object +** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. +** If the [unprotected sqlite3_value] object returned by +** [sqlite3_column_value()] is used in any other way, including calls +** to routines like [sqlite3_value_int()], [sqlite3_value_text()], +** or [sqlite3_value_bytes()], then the behavior is undefined. +** +** These routines attempt to convert the value where appropriate. ^For +** example, if the internal representation is FLOAT and a text result +** is requested, [sqlite3_snprintf()] is used internally to perform the +** conversion automatically. ^(The following table details the conversions +** that are applied: +** +**
+** +**
Internal
Type
Requested
Type
Conversion +** +**
NULL INTEGER Result is 0 +**
NULL FLOAT Result is 0.0 +**
NULL TEXT Result is NULL pointer +**
NULL BLOB Result is NULL pointer +**
INTEGER FLOAT Convert from integer to float +**
INTEGER TEXT ASCII rendering of the integer +**
INTEGER BLOB Same as INTEGER->TEXT +**
FLOAT INTEGER Convert from float to integer +**
FLOAT TEXT ASCII rendering of the float +**
FLOAT BLOB Same as FLOAT->TEXT +**
TEXT INTEGER Use atoi() +**
TEXT FLOAT Use atof() +**
TEXT BLOB No change +**
BLOB INTEGER Convert to TEXT then use atoi() +**
BLOB FLOAT Convert to TEXT then use atof() +**
BLOB TEXT Add a zero terminator if needed +**
+**
)^ +** +** The table above makes reference to standard C library functions atoi() +** and atof(). SQLite does not really use these functions. It has its +** own equivalent internal routines. The atoi() and atof() names are +** used in the table for brevity and because they are familiar to most +** C programmers. +** +** Note that when type conversions occur, pointers returned by prior +** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or +** sqlite3_column_text16() may be invalidated. +** Type conversions and pointer invalidations might occur +** in the following cases: +** +**
    +**
  • The initial content is a BLOB and sqlite3_column_text() or +** sqlite3_column_text16() is called. A zero-terminator might +** need to be added to the string.
  • +**
  • The initial content is UTF-8 text and sqlite3_column_bytes16() or +** sqlite3_column_text16() is called. The content must be converted +** to UTF-16.
  • +**
  • The initial content is UTF-16 text and sqlite3_column_bytes() or +** sqlite3_column_text() is called. The content must be converted +** to UTF-8.
  • +**
+** +** ^Conversions between UTF-16be and UTF-16le are always done in place and do +** not invalidate a prior pointer, though of course the content of the buffer +** that the prior pointer references will have been modified. Other kinds +** of conversion are done in place when it is possible, but sometimes they +** are not possible and in those cases prior pointers are invalidated. +** +** The safest and easiest to remember policy is to invoke these routines +** in one of the following ways: +** +**
    +**
  • sqlite3_column_text() followed by sqlite3_column_bytes()
  • +**
  • sqlite3_column_blob() followed by sqlite3_column_bytes()
  • +**
  • sqlite3_column_text16() followed by sqlite3_column_bytes16()
  • +**
+** +** In other words, you should call sqlite3_column_text(), +** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result +** into the desired format, then invoke sqlite3_column_bytes() or +** sqlite3_column_bytes16() to find the size of the result. Do not mix calls +** to sqlite3_column_text() or sqlite3_column_blob() with calls to +** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() +** with calls to sqlite3_column_bytes(). +** +** ^The pointers returned are valid until a type conversion occurs as +** described above, or until [sqlite3_step()] or [sqlite3_reset()] or +** [sqlite3_finalize()] is called. ^The memory space used to hold strings +** and BLOBs is freed automatically. Do not pass the pointers returned +** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into +** [sqlite3_free()]. +** +** ^(If a memory allocation error occurs during the evaluation of any +** of these routines, a default value is returned. The default value +** is either the integer 0, the floating point number 0.0, or a NULL +** pointer. Subsequent calls to [sqlite3_errcode()] will return +** [SQLITE_NOMEM].)^ +*/ +SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); +SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); +SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); +SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); + +/* +** CAPI3REF: Destroy A Prepared Statement Object +** +** ^The sqlite3_finalize() function is called to delete a [prepared statement]. +** ^If the most recent evaluation of the statement encountered no errors +** or if the statement is never been evaluated, then sqlite3_finalize() returns +** SQLITE_OK. ^If the most recent evaluation of statement S failed, then +** sqlite3_finalize(S) returns the appropriate [error code] or +** [extended error code]. +** +** ^The sqlite3_finalize(S) routine can be called at any point during +** the life cycle of [prepared statement] S: +** before statement S is ever evaluated, after +** one or more calls to [sqlite3_reset()], or after any call +** to [sqlite3_step()] regardless of whether or not the statement has +** completed execution. +** +** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. +** +** The application must finalize every [prepared statement] in order to avoid +** resource leaks. It is a grievous error for the application to try to use +** a prepared statement after it has been finalized. Any use of a prepared +** statement after it has been finalized can result in undefined and +** undesirable behavior such as segfaults and heap corruption. +*/ +SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Reset A Prepared Statement Object +** +** The sqlite3_reset() function is called to reset a [prepared statement] +** object back to its initial state, ready to be re-executed. +** ^Any SQL statement variables that had values bound to them using +** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. +** Use [sqlite3_clear_bindings()] to reset the bindings. +** +** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S +** back to the beginning of its program. +** +** ^If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], +** or if [sqlite3_step(S)] has never before been called on S, +** then [sqlite3_reset(S)] returns [SQLITE_OK]. +** +** ^If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S indicated an error, then +** [sqlite3_reset(S)] returns an appropriate [error code]. +** +** ^The [sqlite3_reset(S)] interface does not change the values +** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. +*/ +SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Create Or Redefine SQL Functions +** KEYWORDS: {function creation routines} +** KEYWORDS: {application-defined SQL function} +** KEYWORDS: {application-defined SQL functions} +** +** ^These functions (collectively known as "function creation routines") +** are used to add SQL functions or aggregates or to redefine the behavior +** of existing SQL functions or aggregates. The only differences between +** these routines are the text encoding expected for +** the second parameter (the name of the function being created) +** and the presence or absence of a destructor callback for +** the application data pointer. +** +** ^The first parameter is the [database connection] to which the SQL +** function is to be added. ^If an application uses more than one database +** connection then application-defined SQL functions must be added +** to each database connection separately. +** +** ^The second parameter is the name of the SQL function to be created or +** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 +** representation, exclusive of the zero-terminator. ^Note that the name +** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. +** ^Any attempt to create a function with a longer name +** will result in [SQLITE_MISUSE] being returned. +** +** ^The third parameter (nArg) +** is the number of arguments that the SQL function or +** aggregate takes. ^If this parameter is -1, then the SQL function or +** aggregate may take any number of arguments between 0 and the limit +** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third +** parameter is less than -1 or greater than 127 then the behavior is +** undefined. +** +** ^The fourth parameter, eTextRep, specifies what +** [SQLITE_UTF8 | text encoding] this SQL function prefers for +** its parameters. Every SQL function implementation must be able to work +** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be +** more efficient with one encoding than another. ^An application may +** invoke sqlite3_create_function() or sqlite3_create_function16() multiple +** times with the same function but with different values of eTextRep. +** ^When multiple implementations of the same function are available, SQLite +** will pick the one that involves the least amount of data conversion. +** If there is only a single implementation which does not care what text +** encoding is used, then the fourth argument should be [SQLITE_ANY]. +** +** ^(The fifth parameter is an arbitrary pointer. The implementation of the +** function can gain access to this pointer using [sqlite3_user_data()].)^ +** +** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are +** pointers to C-language functions that implement the SQL function or +** aggregate. ^A scalar SQL function requires an implementation of the xFunc +** callback only; NULL pointers must be passed as the xStep and xFinal +** parameters. ^An aggregate SQL function requires an implementation of xStep +** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing +** SQL function or aggregate, pass NULL pointers for all three function +** callbacks. +** +** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL, +** then it is destructor for the application data pointer. +** The destructor is invoked when the function is deleted, either by being +** overloaded or when the database connection closes.)^ +** ^The destructor is also invoked if the call to +** sqlite3_create_function_v2() fails. +** ^When the destructor callback of the tenth parameter is invoked, it +** is passed a single argument which is a copy of the application data +** pointer which was the fifth parameter to sqlite3_create_function_v2(). +** +** ^It is permitted to register multiple implementations of the same +** functions with the same name but with either differing numbers of +** arguments or differing preferred text encodings. ^SQLite will use +** the implementation that most closely matches the way in which the +** SQL function is used. ^A function implementation with a non-negative +** nArg parameter is a better match than a function implementation with +** a negative nArg. ^A function where the preferred text encoding +** matches the database encoding is a better +** match than a function where the encoding is different. +** ^A function where the encoding difference is between UTF16le and UTF16be +** is a closer match than a function where the encoding difference is +** between UTF8 and UTF16. +** +** ^Built-in functions may be overloaded by new application-defined functions. +** +** ^An application-defined function is permitted to call other +** SQLite interfaces. However, such calls must not +** close the database connection nor finalize or reset the prepared +** statement in which the function is running. +*/ +SQLITE_API int sqlite3_create_function( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*) +); +SQLITE_API int sqlite3_create_function16( + sqlite3 *db, + const void *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*) +); +SQLITE_API int sqlite3_create_function_v2( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void(*xDestroy)(void*) +); + +/* +** CAPI3REF: Text Encodings +** +** These constant define integer codes that represent the various +** text encodings supported by SQLite. +*/ +#define SQLITE_UTF8 1 +#define SQLITE_UTF16LE 2 +#define SQLITE_UTF16BE 3 +#define SQLITE_UTF16 4 /* Use native byte order */ +#define SQLITE_ANY 5 /* sqlite3_create_function only */ +#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ + +/* +** CAPI3REF: Deprecated Functions +** DEPRECATED +** +** These functions are [deprecated]. In order to maintain +** backwards compatibility with older code, these functions continue +** to be supported. However, new applications should avoid +** the use of these functions. To help encourage people to avoid +** using these functions, we are not going to tell you what they do. +*/ +#ifndef SQLITE_OMIT_DEPRECATED +SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); +SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); +SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), + void*,sqlite3_int64); +#endif + +/* +** CAPI3REF: Obtaining SQL Function Parameter Values +** +** The C-language implementation of SQL functions and aggregates uses +** this set of interface routines to access the parameter values on +** the function or aggregate. +** +** The xFunc (for scalar functions) or xStep (for aggregates) parameters +** to [sqlite3_create_function()] and [sqlite3_create_function16()] +** define callbacks that implement the SQL functions and aggregates. +** The 3rd parameter to these callbacks is an array of pointers to +** [protected sqlite3_value] objects. There is one [sqlite3_value] object for +** each parameter to the SQL function. These routines are used to +** extract values from the [sqlite3_value] objects. +** +** These routines work only with [protected sqlite3_value] objects. +** Any attempt to use these routines on an [unprotected sqlite3_value] +** object results in undefined behavior. +** +** ^These routines work just like the corresponding [column access functions] +** except that these routines take a single [protected sqlite3_value] object +** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. +** +** ^The sqlite3_value_text16() interface extracts a UTF-16 string +** in the native byte-order of the host machine. ^The +** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces +** extract UTF-16 strings as big-endian and little-endian respectively. +** +** ^(The sqlite3_value_numeric_type() interface attempts to apply +** numeric affinity to the value. This means that an attempt is +** made to convert the value to an integer or floating point. If +** such a conversion is possible without loss of information (in other +** words, if the value is a string that looks like a number) +** then the conversion is performed. Otherwise no conversion occurs. +** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ +** +** Please pay particular attention to the fact that the pointer returned +** from [sqlite3_value_blob()], [sqlite3_value_text()], or +** [sqlite3_value_text16()] can be invalidated by a subsequent call to +** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], +** or [sqlite3_value_text16()]. +** +** These routines must be called from the same thread as +** the SQL function that supplied the [sqlite3_value*] parameters. +*/ +SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API double sqlite3_value_double(sqlite3_value*); +SQLITE_API int sqlite3_value_int(sqlite3_value*); +SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); +SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); +SQLITE_API int sqlite3_value_type(sqlite3_value*); +SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); + +/* +** CAPI3REF: Obtain Aggregate Function Context +** +** Implementations of aggregate SQL functions use this +** routine to allocate memory for storing their state. +** +** ^The first time the sqlite3_aggregate_context(C,N) routine is called +** for a particular aggregate function, SQLite +** allocates N of memory, zeroes out that memory, and returns a pointer +** to the new memory. ^On second and subsequent calls to +** sqlite3_aggregate_context() for the same aggregate function instance, +** the same buffer is returned. Sqlite3_aggregate_context() is normally +** called once for each invocation of the xStep callback and then one +** last time when the xFinal callback is invoked. ^(When no rows match +** an aggregate query, the xStep() callback of the aggregate function +** implementation is never called and xFinal() is called exactly once. +** In those cases, sqlite3_aggregate_context() might be called for the +** first time from within xFinal().)^ +** +** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer +** when first called if N is less than or equal to zero or if a memory +** allocate error occurs. +** +** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is +** determined by the N parameter on first successful call. Changing the +** value of N in subsequent call to sqlite3_aggregate_context() within +** the same aggregate function instance will not resize the memory +** allocation.)^ Within the xFinal callback, it is customary to set +** N=0 in calls to sqlite3_aggregate_context(C,N) so that no +** pointless memory allocations occur. +** +** ^SQLite automatically frees the memory allocated by +** sqlite3_aggregate_context() when the aggregate query concludes. +** +** The first parameter must be a copy of the +** [sqlite3_context | SQL function context] that is the first parameter +** to the xStep or xFinal callback routine that implements the aggregate +** function. +** +** This routine must be called from the same thread in which +** the aggregate SQL function is running. +*/ +SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); + +/* +** CAPI3REF: User Data For Functions +** +** ^The sqlite3_user_data() interface returns a copy of +** the pointer that was the pUserData parameter (the 5th parameter) +** of the [sqlite3_create_function()] +** and [sqlite3_create_function16()] routines that originally +** registered the application defined function. +** +** This routine must be called from the same thread in which +** the application-defined function is running. +*/ +SQLITE_API void *sqlite3_user_data(sqlite3_context*); + +/* +** CAPI3REF: Database Connection For Functions +** +** ^The sqlite3_context_db_handle() interface returns a copy of +** the pointer to the [database connection] (the 1st parameter) +** of the [sqlite3_create_function()] +** and [sqlite3_create_function16()] routines that originally +** registered the application defined function. +*/ +SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); + +/* +** CAPI3REF: Function Auxiliary Data +** +** The following two functions may be used by scalar SQL functions to +** associate metadata with argument values. If the same value is passed to +** multiple invocations of the same SQL function during query execution, under +** some circumstances the associated metadata may be preserved. This may +** be used, for example, to add a regular-expression matching scalar +** function. The compiled version of the regular expression is stored as +** metadata associated with the SQL value passed as the regular expression +** pattern. The compiled regular expression can be reused on multiple +** invocations of the same function so that the original pattern string +** does not need to be recompiled on each invocation. +** +** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata +** associated by the sqlite3_set_auxdata() function with the Nth argument +** value to the application-defined function. ^If no metadata has been ever +** been set for the Nth argument of the function, or if the corresponding +** function parameter has changed since the meta-data was set, +** then sqlite3_get_auxdata() returns a NULL pointer. +** +** ^The sqlite3_set_auxdata() interface saves the metadata +** pointed to by its 3rd parameter as the metadata for the N-th +** argument of the application-defined function. Subsequent +** calls to sqlite3_get_auxdata() might return this data, if it has +** not been destroyed. +** ^If it is not NULL, SQLite will invoke the destructor +** function given by the 4th parameter to sqlite3_set_auxdata() on +** the metadata when the corresponding function parameter changes +** or when the SQL statement completes, whichever comes first. +** +** SQLite is free to call the destructor and drop metadata on any +** parameter of any function at any time. ^The only guarantee is that +** the destructor will be called before the metadata is dropped. +** +** ^(In practice, metadata is preserved between function calls for +** expressions that are constant at compile time. This includes literal +** values and [parameters].)^ +** +** These routines must be called from the same thread in which +** the SQL function is running. +*/ +SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); + + +/* +** CAPI3REF: Constants Defining Special Destructor Behavior +** +** These are special values for the destructor that is passed in as the +** final argument to routines like [sqlite3_result_blob()]. ^If the destructor +** argument is SQLITE_STATIC, it means that the content pointer is constant +** and will never change. It does not need to be destroyed. ^The +** SQLITE_TRANSIENT value means that the content will likely change in +** the near future and that SQLite should make its own private copy of +** the content before returning. +** +** The typedef is necessary to work around problems in certain +** C++ compilers. See ticket #2191. +*/ +typedef void (*sqlite3_destructor_type)(void*); +#define SQLITE_STATIC ((sqlite3_destructor_type)0) +#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) + +/* +** CAPI3REF: Setting The Result Of An SQL Function +** +** These routines are used by the xFunc or xFinal callbacks that +** implement SQL functions and aggregates. See +** [sqlite3_create_function()] and [sqlite3_create_function16()] +** for additional information. +** +** These functions work very much like the [parameter binding] family of +** functions used to bind values to host parameters in prepared statements. +** Refer to the [SQL parameter] documentation for additional information. +** +** ^The sqlite3_result_blob() interface sets the result from +** an application-defined function to be the BLOB whose content is pointed +** to by the second parameter and which is N bytes long where N is the +** third parameter. +** +** ^The sqlite3_result_zeroblob() interfaces set the result of +** the application-defined function to be a BLOB containing all zero +** bytes and N bytes in size, where N is the value of the 2nd parameter. +** +** ^The sqlite3_result_double() interface sets the result from +** an application-defined function to be a floating point value specified +** by its 2nd argument. +** +** ^The sqlite3_result_error() and sqlite3_result_error16() functions +** cause the implemented SQL function to throw an exception. +** ^SQLite uses the string pointed to by the +** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() +** as the text of an error message. ^SQLite interprets the error +** message string from sqlite3_result_error() as UTF-8. ^SQLite +** interprets the string from sqlite3_result_error16() as UTF-16 in native +** byte order. ^If the third parameter to sqlite3_result_error() +** or sqlite3_result_error16() is negative then SQLite takes as the error +** message all text up through the first zero character. +** ^If the third parameter to sqlite3_result_error() or +** sqlite3_result_error16() is non-negative then SQLite takes that many +** bytes (not characters) from the 2nd parameter as the error message. +** ^The sqlite3_result_error() and sqlite3_result_error16() +** routines make a private copy of the error message text before +** they return. Hence, the calling function can deallocate or +** modify the text after they return without harm. +** ^The sqlite3_result_error_code() function changes the error code +** returned by SQLite as a result of an error in a function. ^By default, +** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() +** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. +** +** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an +** error indicating that a string or BLOB is too long to represent. +** +** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an +** error indicating that a memory allocation failed. +** +** ^The sqlite3_result_int() interface sets the return value +** of the application-defined function to be the 32-bit signed integer +** value given in the 2nd argument. +** ^The sqlite3_result_int64() interface sets the return value +** of the application-defined function to be the 64-bit signed integer +** value given in the 2nd argument. +** +** ^The sqlite3_result_null() interface sets the return value +** of the application-defined function to be NULL. +** +** ^The sqlite3_result_text(), sqlite3_result_text16(), +** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces +** set the return value of the application-defined function to be +** a text string which is represented as UTF-8, UTF-16 native byte order, +** UTF-16 little endian, or UTF-16 big endian, respectively. +** ^SQLite takes the text result from the application from +** the 2nd parameter of the sqlite3_result_text* interfaces. +** ^If the 3rd parameter to the sqlite3_result_text* interfaces +** is negative, then SQLite takes result text from the 2nd parameter +** through the first zero character. +** ^If the 3rd parameter to the sqlite3_result_text* interfaces +** is non-negative, then as many bytes (not characters) of the text +** pointed to by the 2nd parameter are taken as the application-defined +** function result. If the 3rd parameter is non-negative, then it +** must be the byte offset into the string where the NUL terminator would +** appear if the string where NUL terminated. If any NUL characters occur +** in the string at a byte offset that is less than the value of the 3rd +** parameter, then the resulting string will contain embedded NULs and the +** result of expressions operating on strings with embedded NULs is undefined. +** ^If the 4th parameter to the sqlite3_result_text* interfaces +** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that +** function as the destructor on the text or BLOB result when it has +** finished using that result. +** ^If the 4th parameter to the sqlite3_result_text* interfaces or to +** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite +** assumes that the text or BLOB result is in constant space and does not +** copy the content of the parameter nor call a destructor on the content +** when it has finished using that result. +** ^If the 4th parameter to the sqlite3_result_text* interfaces +** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT +** then SQLite makes a copy of the result into space obtained from +** from [sqlite3_malloc()] before it returns. +** +** ^The sqlite3_result_value() interface sets the result of +** the application-defined function to be a copy the +** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The +** sqlite3_result_value() interface makes a copy of the [sqlite3_value] +** so that the [sqlite3_value] specified in the parameter may change or +** be deallocated after sqlite3_result_value() returns without harm. +** ^A [protected sqlite3_value] object may always be used where an +** [unprotected sqlite3_value] object is required, so either +** kind of [sqlite3_value] object can be used with this interface. +** +** If these routines are called from within the different thread +** than the one containing the application-defined function that received +** the [sqlite3_context] pointer, the results are undefined. +*/ +SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_double(sqlite3_context*, double); +SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); +SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); +SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); +SQLITE_API void sqlite3_result_null(sqlite3_context*); +SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); +SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); + +/* +** CAPI3REF: Define New Collating Sequences +** +** ^These functions add, remove, or modify a [collation] associated +** with the [database connection] specified as the first argument. +** +** ^The name of the collation is a UTF-8 string +** for sqlite3_create_collation() and sqlite3_create_collation_v2() +** and a UTF-16 string in native byte order for sqlite3_create_collation16(). +** ^Collation names that compare equal according to [sqlite3_strnicmp()] are +** considered to be the same name. +** +** ^(The third argument (eTextRep) must be one of the constants: +**
    +**
  • [SQLITE_UTF8], +**
  • [SQLITE_UTF16LE], +**
  • [SQLITE_UTF16BE], +**
  • [SQLITE_UTF16], or +**
  • [SQLITE_UTF16_ALIGNED]. +**
)^ +** ^The eTextRep argument determines the encoding of strings passed +** to the collating function callback, xCallback. +** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep +** force strings to be UTF16 with native byte order. +** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin +** on an even byte address. +** +** ^The fourth argument, pArg, is an application data pointer that is passed +** through as the first argument to the collating function callback. +** +** ^The fifth argument, xCallback, is a pointer to the collating function. +** ^Multiple collating functions can be registered using the same name but +** with different eTextRep parameters and SQLite will use whichever +** function requires the least amount of data transformation. +** ^If the xCallback argument is NULL then the collating function is +** deleted. ^When all collating functions having the same name are deleted, +** that collation is no longer usable. +** +** ^The collating function callback is invoked with a copy of the pArg +** application data pointer and with two strings in the encoding specified +** by the eTextRep argument. The collating function must return an +** integer that is negative, zero, or positive +** if the first string is less than, equal to, or greater than the second, +** respectively. A collating function must always return the same answer +** given the same inputs. If two or more collating functions are registered +** to the same collation name (using different eTextRep values) then all +** must give an equivalent answer when invoked with equivalent strings. +** The collating function must obey the following properties for all +** strings A, B, and C: +** +**
    +**
  1. If A==B then B==A. +**
  2. If A==B and B==C then A==C. +**
  3. If A<B THEN B>A. +**
  4. If A<B and B<C then A<C. +**
+** +** If a collating function fails any of the above constraints and that +** collating function is registered and used, then the behavior of SQLite +** is undefined. +** +** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() +** with the addition that the xDestroy callback is invoked on pArg when +** the collating function is deleted. +** ^Collating functions are deleted when they are overridden by later +** calls to the collation creation functions or when the +** [database connection] is closed using [sqlite3_close()]. +** +** ^The xDestroy callback is not called if the +** sqlite3_create_collation_v2() function fails. Applications that invoke +** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should +** check the return code and dispose of the application data pointer +** themselves rather than expecting SQLite to deal with it for them. +** This is different from every other SQLite interface. The inconsistency +** is unfortunate but cannot be changed without breaking backwards +** compatibility. +** +** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. +*/ +SQLITE_API int sqlite3_create_collation( + sqlite3*, + const char *zName, + int eTextRep, + void *pArg, + int(*xCompare)(void*,int,const void*,int,const void*) +); +SQLITE_API int sqlite3_create_collation_v2( + sqlite3*, + const char *zName, + int eTextRep, + void *pArg, + int(*xCompare)(void*,int,const void*,int,const void*), + void(*xDestroy)(void*) +); +SQLITE_API int sqlite3_create_collation16( + sqlite3*, + const void *zName, + int eTextRep, + void *pArg, + int(*xCompare)(void*,int,const void*,int,const void*) +); + +/* +** CAPI3REF: Collation Needed Callbacks +** +** ^To avoid having to register all collation sequences before a database +** can be used, a single callback function may be registered with the +** [database connection] to be invoked whenever an undefined collation +** sequence is required. +** +** ^If the function is registered using the sqlite3_collation_needed() API, +** then it is passed the names of undefined collation sequences as strings +** encoded in UTF-8. ^If sqlite3_collation_needed16() is used, +** the names are passed as UTF-16 in machine native byte order. +** ^A call to either function replaces the existing collation-needed callback. +** +** ^(When the callback is invoked, the first argument passed is a copy +** of the second argument to sqlite3_collation_needed() or +** sqlite3_collation_needed16(). The second argument is the database +** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], +** or [SQLITE_UTF16LE], indicating the most desirable form of the collation +** sequence function required. The fourth parameter is the name of the +** required collation sequence.)^ +** +** The callback function should register the desired collation using +** [sqlite3_create_collation()], [sqlite3_create_collation16()], or +** [sqlite3_create_collation_v2()]. +*/ +SQLITE_API int sqlite3_collation_needed( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const char*) +); +SQLITE_API int sqlite3_collation_needed16( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const void*) +); + +#ifdef SQLITE_HAS_CODEC +/* +** Specify the key for an encrypted database. This routine should be +** called right after sqlite3_open(). +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +SQLITE_API int sqlite3_key( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The key */ +); + +/* +** Change the key on an open database. If the current database is not +** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the +** database is decrypted. +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +SQLITE_API int sqlite3_rekey( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The new key */ +); + +/* +** Specify the activation key for a SEE database. Unless +** activated, none of the SEE routines will work. +*/ +SQLITE_API void sqlite3_activate_see( + const char *zPassPhrase /* Activation phrase */ +); +#endif + +#ifdef SQLITE_ENABLE_CEROD +/* +** Specify the activation key for a CEROD database. Unless +** activated, none of the CEROD routines will work. +*/ +SQLITE_API void sqlite3_activate_cerod( + const char *zPassPhrase /* Activation phrase */ +); +#endif + +/* +** CAPI3REF: Suspend Execution For A Short Time +** +** The sqlite3_sleep() function causes the current thread to suspend execution +** for at least a number of milliseconds specified in its parameter. +** +** If the operating system does not support sleep requests with +** millisecond time resolution, then the time will be rounded up to +** the nearest second. The number of milliseconds of sleep actually +** requested from the operating system is returned. +** +** ^SQLite implements this interface by calling the xSleep() +** method of the default [sqlite3_vfs] object. If the xSleep() method +** of the default VFS is not implemented correctly, or not implemented at +** all, then the behavior of sqlite3_sleep() may deviate from the description +** in the previous paragraphs. +*/ +SQLITE_API int sqlite3_sleep(int); + +/* +** CAPI3REF: Name Of The Folder Holding Temporary Files +** +** ^(If this global variable is made to point to a string which is +** the name of a folder (a.k.a. directory), then all temporary files +** created by SQLite when using a built-in [sqlite3_vfs | VFS] +** will be placed in that directory.)^ ^If this variable +** is a NULL pointer, then SQLite performs a search for an appropriate +** temporary file directory. +** +** It is not safe to read or modify this variable in more than one +** thread at a time. It is not safe to read or modify this variable +** if a [database connection] is being used at the same time in a separate +** thread. +** It is intended that this variable be set once +** as part of process initialization and before any SQLite interface +** routines have been called and that this variable remain unchanged +** thereafter. +** +** ^The [temp_store_directory pragma] may modify this variable and cause +** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, +** the [temp_store_directory pragma] always assumes that any string +** that this variable points to is held in memory obtained from +** [sqlite3_malloc] and the pragma may attempt to free that memory +** using [sqlite3_free]. +** Hence, if this variable is modified directly, either it should be +** made NULL or made to point to memory obtained from [sqlite3_malloc] +** or else the use of the [temp_store_directory pragma] should be avoided. +** +** Note to Windows Runtime users: The temporary directory must be set +** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various +** features that require the use of temporary files may fail. Here is an +** example of how to do this using C++ with the Windows Runtime: +** +**
+** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
+**       TemporaryFolder->Path->Data();
+** char zPathBuf[MAX_PATH + 1];
+** memset(zPathBuf, 0, sizeof(zPathBuf));
+** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
+**       NULL, NULL);
+** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
+** 
+*/ +SQLITE_API char *sqlite3_temp_directory; + +/* +** CAPI3REF: Name Of The Folder Holding Database Files +** +** ^(If this global variable is made to point to a string which is +** the name of a folder (a.k.a. directory), then all database files +** specified with a relative pathname and created or accessed by +** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed +** to be relative to that directory.)^ ^If this variable is a NULL +** pointer, then SQLite assumes that all database files specified +** with a relative pathname are relative to the current directory +** for the process. Only the windows VFS makes use of this global +** variable; it is ignored by the unix VFS. +** +** Changing the value of this variable while a database connection is +** open can result in a corrupt database. +** +** It is not safe to read or modify this variable in more than one +** thread at a time. It is not safe to read or modify this variable +** if a [database connection] is being used at the same time in a separate +** thread. +** It is intended that this variable be set once +** as part of process initialization and before any SQLite interface +** routines have been called and that this variable remain unchanged +** thereafter. +** +** ^The [data_store_directory pragma] may modify this variable and cause +** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, +** the [data_store_directory pragma] always assumes that any string +** that this variable points to is held in memory obtained from +** [sqlite3_malloc] and the pragma may attempt to free that memory +** using [sqlite3_free]. +** Hence, if this variable is modified directly, either it should be +** made NULL or made to point to memory obtained from [sqlite3_malloc] +** or else the use of the [data_store_directory pragma] should be avoided. +*/ +SQLITE_API char *sqlite3_data_directory; + +/* +** CAPI3REF: Test For Auto-Commit Mode +** KEYWORDS: {autocommit mode} +** +** ^The sqlite3_get_autocommit() interface returns non-zero or +** zero if the given database connection is or is not in autocommit mode, +** respectively. ^Autocommit mode is on by default. +** ^Autocommit mode is disabled by a [BEGIN] statement. +** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. +** +** If certain kinds of errors occur on a statement within a multi-statement +** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], +** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the +** transaction might be rolled back automatically. The only way to +** find out whether SQLite automatically rolled back the transaction after +** an error is to use this function. +** +** If another thread changes the autocommit status of the database +** connection while this routine is running, then the return value +** is undefined. +*/ +SQLITE_API int sqlite3_get_autocommit(sqlite3*); + +/* +** CAPI3REF: Find The Database Handle Of A Prepared Statement +** +** ^The sqlite3_db_handle interface returns the [database connection] handle +** to which a [prepared statement] belongs. ^The [database connection] +** returned by sqlite3_db_handle is the same [database connection] +** that was the first argument +** to the [sqlite3_prepare_v2()] call (or its variants) that was used to +** create the statement in the first place. +*/ +SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); + +/* +** CAPI3REF: Return The Filename For A Database Connection +** +** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename +** associated with database N of connection D. ^The main database file +** has the name "main". If there is no attached database N on the database +** connection D, or if database N is a temporary or in-memory database, then +** a NULL pointer is returned. +** +** ^The filename returned by this function is the output of the +** xFullPathname method of the [VFS]. ^In other words, the filename +** will be an absolute pathname, even if the filename used +** to open the database originally was a URI or relative pathname. +*/ +SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); + +/* +** CAPI3REF: Determine if a database is read-only +** +** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N +** of connection D is read-only, 0 if it is read/write, or -1 if N is not +** the name of a database on connection D. +*/ +SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); + +/* +** CAPI3REF: Find the next prepared statement +** +** ^This interface returns a pointer to the next [prepared statement] after +** pStmt associated with the [database connection] pDb. ^If pStmt is NULL +** then this interface returns a pointer to the first prepared statement +** associated with the database connection pDb. ^If no prepared statement +** satisfies the conditions of this routine, it returns NULL. +** +** The [database connection] pointer D in a call to +** [sqlite3_next_stmt(D,S)] must refer to an open database +** connection and in particular must not be a NULL pointer. +*/ +SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Commit And Rollback Notification Callbacks +** +** ^The sqlite3_commit_hook() interface registers a callback +** function to be invoked whenever a transaction is [COMMIT | committed]. +** ^Any callback set by a previous call to sqlite3_commit_hook() +** for the same database connection is overridden. +** ^The sqlite3_rollback_hook() interface registers a callback +** function to be invoked whenever a transaction is [ROLLBACK | rolled back]. +** ^Any callback set by a previous call to sqlite3_rollback_hook() +** for the same database connection is overridden. +** ^The pArg argument is passed through to the callback. +** ^If the callback on a commit hook function returns non-zero, +** then the commit is converted into a rollback. +** +** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions +** return the P argument from the previous call of the same function +** on the same [database connection] D, or NULL for +** the first call for each function on D. +** +** The commit and rollback hook callbacks are not reentrant. +** The callback implementation must not do anything that will modify +** the database connection that invoked the callback. Any actions +** to modify the database connection must be deferred until after the +** completion of the [sqlite3_step()] call that triggered the commit +** or rollback hook in the first place. +** Note that running any other SQL statements, including SELECT statements, +** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify +** the database connections for the meaning of "modify" in this paragraph. +** +** ^Registering a NULL function disables the callback. +** +** ^When the commit hook callback routine returns zero, the [COMMIT] +** operation is allowed to continue normally. ^If the commit hook +** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK]. +** ^The rollback hook is invoked on a rollback that results from a commit +** hook returning non-zero, just as it would be with any other rollback. +** +** ^For the purposes of this API, a transaction is said to have been +** rolled back if an explicit "ROLLBACK" statement is executed, or +** an error or constraint causes an implicit rollback to occur. +** ^The rollback callback is not invoked if a transaction is +** automatically rolled back because the database connection is closed. +** +** See also the [sqlite3_update_hook()] interface. +*/ +SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); +SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + +/* +** CAPI3REF: Data Change Notification Callbacks +** +** ^The sqlite3_update_hook() interface registers a callback function +** with the [database connection] identified by the first argument +** to be invoked whenever a row is updated, inserted or deleted. +** ^Any callback set by a previous call to this function +** for the same database connection is overridden. +** +** ^The second argument is a pointer to the function to invoke when a +** row is updated, inserted or deleted. +** ^The first argument to the callback is a copy of the third argument +** to sqlite3_update_hook(). +** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], +** or [SQLITE_UPDATE], depending on the operation that caused the callback +** to be invoked. +** ^The third and fourth arguments to the callback contain pointers to the +** database and table name containing the affected row. +** ^The final callback parameter is the [rowid] of the row. +** ^In the case of an update, this is the [rowid] after the update takes place. +** +** ^(The update hook is not invoked when internal system tables are +** modified (i.e. sqlite_master and sqlite_sequence).)^ +** +** ^In the current implementation, the update hook +** is not invoked when duplication rows are deleted because of an +** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook +** invoked when rows are deleted using the [truncate optimization]. +** The exceptions defined in this paragraph might change in a future +** release of SQLite. +** +** The update hook implementation must not do anything that will modify +** the database connection that invoked the update hook. Any actions +** to modify the database connection must be deferred until after the +** completion of the [sqlite3_step()] call that triggered the update hook. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +** ^The sqlite3_update_hook(D,C,P) function +** returns the P argument from the previous call +** on the same [database connection] D, or NULL for +** the first call on D. +** +** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] +** interfaces. +*/ +SQLITE_API void *sqlite3_update_hook( + sqlite3*, + void(*)(void *,int ,char const *,char const *,sqlite3_int64), + void* +); + +/* +** CAPI3REF: Enable Or Disable Shared Pager Cache +** +** ^(This routine enables or disables the sharing of the database cache +** and schema data structures between [database connection | connections] +** to the same database. Sharing is enabled if the argument is true +** and disabled if the argument is false.)^ +** +** ^Cache sharing is enabled and disabled for an entire process. +** This is a change as of SQLite version 3.5.0. In prior versions of SQLite, +** sharing was enabled or disabled for each thread separately. +** +** ^(The cache sharing mode set by this interface effects all subsequent +** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. +** Existing database connections continue use the sharing mode +** that was in effect at the time they were opened.)^ +** +** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled +** successfully. An [error code] is returned otherwise.)^ +** +** ^Shared cache is disabled by default. But this might change in +** future releases of SQLite. Applications that care about shared +** cache setting should set it explicitly. +** +** This interface is threadsafe on processors where writing a +** 32-bit integer is atomic. +** +** See Also: [SQLite Shared-Cache Mode] +*/ +SQLITE_API int sqlite3_enable_shared_cache(int); + +/* +** CAPI3REF: Attempt To Free Heap Memory +** +** ^The sqlite3_release_memory() interface attempts to free N bytes +** of heap memory by deallocating non-essential memory allocations +** held by the database library. Memory used to cache database +** pages to improve performance is an example of non-essential memory. +** ^sqlite3_release_memory() returns the number of bytes actually freed, +** which might be more or less than the amount requested. +** ^The sqlite3_release_memory() routine is a no-op returning zero +** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. +** +** See also: [sqlite3_db_release_memory()] +*/ +SQLITE_API int sqlite3_release_memory(int); + +/* +** CAPI3REF: Free Memory Used By A Database Connection +** +** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap +** memory as possible from database connection D. Unlike the +** [sqlite3_release_memory()] interface, this interface is effect even +** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is +** omitted. +** +** See also: [sqlite3_release_memory()] +*/ +SQLITE_API int sqlite3_db_release_memory(sqlite3*); + +/* +** CAPI3REF: Impose A Limit On Heap Size +** +** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the +** soft limit on the amount of heap memory that may be allocated by SQLite. +** ^SQLite strives to keep heap memory utilization below the soft heap +** limit by reducing the number of pages held in the page cache +** as heap memory usages approaches the limit. +** ^The soft heap limit is "soft" because even though SQLite strives to stay +** below the limit, it will exceed the limit rather than generate +** an [SQLITE_NOMEM] error. In other words, the soft heap limit +** is advisory only. +** +** ^The return value from sqlite3_soft_heap_limit64() is the size of +** the soft heap limit prior to the call, or negative in the case of an +** error. ^If the argument N is negative +** then no change is made to the soft heap limit. Hence, the current +** size of the soft heap limit can be determined by invoking +** sqlite3_soft_heap_limit64() with a negative argument. +** +** ^If the argument N is zero then the soft heap limit is disabled. +** +** ^(The soft heap limit is not enforced in the current implementation +** if one or more of following conditions are true: +** +**
    +**
  • The soft heap limit is set to zero. +**
  • Memory accounting is disabled using a combination of the +** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and +** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. +**
  • An alternative page cache implementation is specified using +** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). +**
  • The page cache allocates from its own memory pool supplied +** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than +** from the heap. +**
)^ +** +** Beginning with SQLite version 3.7.3, the soft heap limit is enforced +** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT] +** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT], +** the soft heap limit is enforced on every memory allocation. Without +** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced +** when memory is allocated by the page cache. Testing suggests that because +** the page cache is the predominate memory user in SQLite, most +** applications will achieve adequate soft heap limit enforcement without +** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. +** +** The circumstances under which SQLite will enforce the soft heap limit may +** changes in future releases of SQLite. +*/ +SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); + +/* +** CAPI3REF: Deprecated Soft Heap Limit Interface +** DEPRECATED +** +** This is a deprecated version of the [sqlite3_soft_heap_limit64()] +** interface. This routine is provided for historical compatibility +** only. All new applications should use the +** [sqlite3_soft_heap_limit64()] interface rather than this one. +*/ +SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); + + +/* +** CAPI3REF: Extract Metadata About A Column Of A Table +** +** ^This routine returns metadata about a specific column of a specific +** database table accessible using the [database connection] handle +** passed as the first function argument. +** +** ^The column is identified by the second, third and fourth parameters to +** this function. ^The second parameter is either the name of the database +** (i.e. "main", "temp", or an attached database) containing the specified +** table or NULL. ^If it is NULL, then all attached databases are searched +** for the table using the same algorithm used by the database engine to +** resolve unqualified table references. +** +** ^The third and fourth parameters to this function are the table and column +** name of the desired column, respectively. Neither of these parameters +** may be NULL. +** +** ^Metadata is returned by writing to the memory locations passed as the 5th +** and subsequent parameters to this function. ^Any of these arguments may be +** NULL, in which case the corresponding element of metadata is omitted. +** +** ^(
+** +**
Parameter Output
Type
Description +** +**
5th const char* Data type +**
6th const char* Name of default collation sequence +**
7th int True if column has a NOT NULL constraint +**
8th int True if column is part of the PRIMARY KEY +**
9th int True if column is [AUTOINCREMENT] +**
+**
)^ +** +** ^The memory pointed to by the character pointers returned for the +** declaration type and collation sequence is valid only until the next +** call to any SQLite API function. +** +** ^If the specified table is actually a view, an [error code] is returned. +** +** ^If the specified column is "rowid", "oid" or "_rowid_" and an +** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output +** parameters are set for the explicitly declared column. ^(If there is no +** explicitly declared [INTEGER PRIMARY KEY] column, then the output +** parameters are set as follows: +** +**
+**     data type: "INTEGER"
+**     collation sequence: "BINARY"
+**     not null: 0
+**     primary key: 1
+**     auto increment: 0
+** 
)^ +** +** ^(This function may load one or more schemas from database files. If an +** error occurs during this process, or if the requested table or column +** cannot be found, an [error code] is returned and an error message left +** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^ +** +** ^This API is only available if the library was compiled with the +** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. +*/ +SQLITE_API int sqlite3_table_column_metadata( + sqlite3 *db, /* Connection handle */ + const char *zDbName, /* Database name or NULL */ + const char *zTableName, /* Table name */ + const char *zColumnName, /* Column name */ + char const **pzDataType, /* OUTPUT: Declared data type */ + char const **pzCollSeq, /* OUTPUT: Collation sequence name */ + int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ + int *pPrimaryKey, /* OUTPUT: True if column part of PK */ + int *pAutoinc /* OUTPUT: True if column is auto-increment */ +); + +/* +** CAPI3REF: Load An Extension +** +** ^This interface loads an SQLite extension library from the named file. +** +** ^The sqlite3_load_extension() interface attempts to load an +** SQLite extension library contained in the file zFile. +** +** ^The entry point is zProc. +** ^zProc may be 0, in which case the name of the entry point +** defaults to "sqlite3_extension_init". +** ^The sqlite3_load_extension() interface returns +** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. +** ^If an error occurs and pzErrMsg is not 0, then the +** [sqlite3_load_extension()] interface shall attempt to +** fill *pzErrMsg with error message text stored in memory +** obtained from [sqlite3_malloc()]. The calling function +** should free this memory by calling [sqlite3_free()]. +** +** ^Extension loading must be enabled using +** [sqlite3_enable_load_extension()] prior to calling this API, +** otherwise an error will be returned. +** +** See also the [load_extension() SQL function]. +*/ +SQLITE_API int sqlite3_load_extension( + sqlite3 *db, /* Load the extension into this database connection */ + const char *zFile, /* Name of the shared library containing extension */ + const char *zProc, /* Entry point. Derived from zFile if 0 */ + char **pzErrMsg /* Put error message here if not 0 */ +); + +/* +** CAPI3REF: Enable Or Disable Extension Loading +** +** ^So as not to open security holes in older applications that are +** unprepared to deal with extension loading, and as a means of disabling +** extension loading while evaluating user-entered SQL, the following API +** is provided to turn the [sqlite3_load_extension()] mechanism on and off. +** +** ^Extension loading is off by default. See ticket #1863. +** ^Call the sqlite3_enable_load_extension() routine with onoff==1 +** to turn extension loading on and call it with onoff==0 to turn +** it back off again. +*/ +SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); + +/* +** CAPI3REF: Automatically Load Statically Linked Extensions +** +** ^This interface causes the xEntryPoint() function to be invoked for +** each new [database connection] that is created. The idea here is that +** xEntryPoint() is the entry point for a statically linked SQLite extension +** that is to be automatically loaded into all new database connections. +** +** ^(Even though the function prototype shows that xEntryPoint() takes +** no arguments and returns void, SQLite invokes xEntryPoint() with three +** arguments and expects and integer result as if the signature of the +** entry point where as follows: +** +**
+**    int xEntryPoint(
+**      sqlite3 *db,
+**      const char **pzErrMsg,
+**      const struct sqlite3_api_routines *pThunk
+**    );
+** 
)^ +** +** If the xEntryPoint routine encounters an error, it should make *pzErrMsg +** point to an appropriate error message (obtained from [sqlite3_mprintf()]) +** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg +** is NULL before calling the xEntryPoint(). ^SQLite will invoke +** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any +** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], +** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. +** +** ^Calling sqlite3_auto_extension(X) with an entry point X that is already +** on the list of automatic extensions is a harmless no-op. ^No entry point +** will be called more than once for each database connection that is opened. +** +** See also: [sqlite3_reset_auto_extension()]. +*/ +SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); + +/* +** CAPI3REF: Reset Automatic Extension Loading +** +** ^This interface disables all automatic extensions previously +** registered using [sqlite3_auto_extension()]. +*/ +SQLITE_API void sqlite3_reset_auto_extension(void); + +/* +** The interface to the virtual-table mechanism is currently considered +** to be experimental. The interface might change in incompatible ways. +** If this is a problem for you, do not use the interface at this time. +** +** When the virtual-table mechanism stabilizes, we will declare the +** interface fixed, support it indefinitely, and remove this comment. +*/ + +/* +** Structures used by the virtual table interface +*/ +typedef struct sqlite3_vtab sqlite3_vtab; +typedef struct sqlite3_index_info sqlite3_index_info; +typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; +typedef struct sqlite3_module sqlite3_module; + +/* +** CAPI3REF: Virtual Table Object +** KEYWORDS: sqlite3_module {virtual table module} +** +** This structure, sometimes called a "virtual table module", +** defines the implementation of a [virtual tables]. +** This structure consists mostly of methods for the module. +** +** ^A virtual table module is created by filling in a persistent +** instance of this structure and passing a pointer to that instance +** to [sqlite3_create_module()] or [sqlite3_create_module_v2()]. +** ^The registration remains valid until it is replaced by a different +** module or until the [database connection] closes. The content +** of this structure must not change while it is registered with +** any database connection. +*/ +struct sqlite3_module { + int iVersion; + int (*xCreate)(sqlite3*, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**); + int (*xConnect)(sqlite3*, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**); + int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*); + int (*xDisconnect)(sqlite3_vtab *pVTab); + int (*xDestroy)(sqlite3_vtab *pVTab); + int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor); + int (*xClose)(sqlite3_vtab_cursor*); + int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, + int argc, sqlite3_value **argv); + int (*xNext)(sqlite3_vtab_cursor*); + int (*xEof)(sqlite3_vtab_cursor*); + int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int); + int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid); + int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *); + int (*xBegin)(sqlite3_vtab *pVTab); + int (*xSync)(sqlite3_vtab *pVTab); + int (*xCommit)(sqlite3_vtab *pVTab); + int (*xRollback)(sqlite3_vtab *pVTab); + int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName, + void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), + void **ppArg); + int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); + /* The methods above are in version 1 of the sqlite_module object. Those + ** below are for version 2 and greater. */ + int (*xSavepoint)(sqlite3_vtab *pVTab, int); + int (*xRelease)(sqlite3_vtab *pVTab, int); + int (*xRollbackTo)(sqlite3_vtab *pVTab, int); +}; + +/* +** CAPI3REF: Virtual Table Indexing Information +** KEYWORDS: sqlite3_index_info +** +** The sqlite3_index_info structure and its substructures is used as part +** of the [virtual table] interface to +** pass information into and receive the reply from the [xBestIndex] +** method of a [virtual table module]. The fields under **Inputs** are the +** inputs to xBestIndex and are read-only. xBestIndex inserts its +** results into the **Outputs** fields. +** +** ^(The aConstraint[] array records WHERE clause constraints of the form: +** +**
column OP expr
+** +** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is +** stored in aConstraint[].op using one of the +** [SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_ values].)^ +** ^(The index of the column is stored in +** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the +** expr on the right-hand side can be evaluated (and thus the constraint +** is usable) and false if it cannot.)^ +** +** ^The optimizer automatically inverts terms of the form "expr OP column" +** and makes other simplifications to the WHERE clause in an attempt to +** get as many WHERE clause terms into the form shown above as possible. +** ^The aConstraint[] array only reports WHERE clause terms that are +** relevant to the particular virtual table being queried. +** +** ^Information about the ORDER BY clause is stored in aOrderBy[]. +** ^Each term of aOrderBy records a column of the ORDER BY clause. +** +** The [xBestIndex] method must fill aConstraintUsage[] with information +** about what parameters to pass to xFilter. ^If argvIndex>0 then +** the right-hand side of the corresponding aConstraint[] is evaluated +** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit +** is true, then the constraint is assumed to be fully handled by the +** virtual table and is not checked again by SQLite.)^ +** +** ^The idxNum and idxPtr values are recorded and passed into the +** [xFilter] method. +** ^[sqlite3_free()] is used to free idxPtr if and only if +** needToFreeIdxPtr is true. +** +** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in +** the correct order to satisfy the ORDER BY clause so that no separate +** sorting step is required. +** +** ^The estimatedCost value is an estimate of the cost of doing the +** particular lookup. A full scan of a table with N entries should have +** a cost of N. A binary search of a table of N entries should have a +** cost of approximately log(N). +*/ +struct sqlite3_index_info { + /* Inputs */ + int nConstraint; /* Number of entries in aConstraint */ + struct sqlite3_index_constraint { + int iColumn; /* Column on left-hand side of constraint */ + unsigned char op; /* Constraint operator */ + unsigned char usable; /* True if this constraint is usable */ + int iTermOffset; /* Used internally - xBestIndex should ignore */ + } *aConstraint; /* Table of WHERE clause constraints */ + int nOrderBy; /* Number of terms in the ORDER BY clause */ + struct sqlite3_index_orderby { + int iColumn; /* Column number */ + unsigned char desc; /* True for DESC. False for ASC. */ + } *aOrderBy; /* The ORDER BY clause */ + /* Outputs */ + struct sqlite3_index_constraint_usage { + int argvIndex; /* if >0, constraint is part of argv to xFilter */ + unsigned char omit; /* Do not code a test for this constraint */ + } *aConstraintUsage; + int idxNum; /* Number used to identify the index */ + char *idxStr; /* String, possibly obtained from sqlite3_malloc */ + int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ + int orderByConsumed; /* True if output is already ordered */ + double estimatedCost; /* Estimated cost of using this index */ +}; + +/* +** CAPI3REF: Virtual Table Constraint Operator Codes +** +** These macros defined the allowed values for the +** [sqlite3_index_info].aConstraint[].op field. Each value represents +** an operator that is part of a constraint term in the wHERE clause of +** a query that uses a [virtual table]. +*/ +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 + +/* +** CAPI3REF: Register A Virtual Table Implementation +** +** ^These routines are used to register a new [virtual table module] name. +** ^Module names must be registered before +** creating a new [virtual table] using the module and before using a +** preexisting [virtual table] for the module. +** +** ^The module name is registered on the [database connection] specified +** by the first parameter. ^The name of the module is given by the +** second parameter. ^The third parameter is a pointer to +** the implementation of the [virtual table module]. ^The fourth +** parameter is an arbitrary client data pointer that is passed through +** into the [xCreate] and [xConnect] methods of the virtual table module +** when a new virtual table is be being created or reinitialized. +** +** ^The sqlite3_create_module_v2() interface has a fifth parameter which +** is a pointer to a destructor for the pClientData. ^SQLite will +** invoke the destructor function (if it is not NULL) when SQLite +** no longer needs the pClientData pointer. ^The destructor will also +** be invoked if the call to sqlite3_create_module_v2() fails. +** ^The sqlite3_create_module() +** interface is equivalent to sqlite3_create_module_v2() with a NULL +** destructor. +*/ +SQLITE_API int sqlite3_create_module( + sqlite3 *db, /* SQLite connection to register module with */ + const char *zName, /* Name of the module */ + const sqlite3_module *p, /* Methods for the module */ + void *pClientData /* Client data for xCreate/xConnect */ +); +SQLITE_API int sqlite3_create_module_v2( + sqlite3 *db, /* SQLite connection to register module with */ + const char *zName, /* Name of the module */ + const sqlite3_module *p, /* Methods for the module */ + void *pClientData, /* Client data for xCreate/xConnect */ + void(*xDestroy)(void*) /* Module destructor function */ +); + +/* +** CAPI3REF: Virtual Table Instance Object +** KEYWORDS: sqlite3_vtab +** +** Every [virtual table module] implementation uses a subclass +** of this object to describe a particular instance +** of the [virtual table]. Each subclass will +** be tailored to the specific needs of the module implementation. +** The purpose of this superclass is to define certain fields that are +** common to all module implementations. +** +** ^Virtual tables methods can set an error message by assigning a +** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should +** take care that any prior string is freed by a call to [sqlite3_free()] +** prior to assigning a new string to zErrMsg. ^After the error message +** is delivered up to the client application, the string will be automatically +** freed by sqlite3_free() and the zErrMsg field will be zeroed. +*/ +struct sqlite3_vtab { + const sqlite3_module *pModule; /* The module for this virtual table */ + int nRef; /* NO LONGER USED */ + char *zErrMsg; /* Error message from sqlite3_mprintf() */ + /* Virtual table implementations will typically add additional fields */ +}; + +/* +** CAPI3REF: Virtual Table Cursor Object +** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} +** +** Every [virtual table module] implementation uses a subclass of the +** following structure to describe cursors that point into the +** [virtual table] and are used +** to loop through the virtual table. Cursors are created using the +** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed +** by the [sqlite3_module.xClose | xClose] method. Cursors are used +** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods +** of the module. Each module implementation will define +** the content of a cursor structure to suit its own needs. +** +** This superclass exists in order to define fields of the cursor that +** are common to all implementations. +*/ +struct sqlite3_vtab_cursor { + sqlite3_vtab *pVtab; /* Virtual table of this cursor */ + /* Virtual table implementations will typically add additional fields */ +}; + +/* +** CAPI3REF: Declare The Schema Of A Virtual Table +** +** ^The [xCreate] and [xConnect] methods of a +** [virtual table module] call this interface +** to declare the format (the names and datatypes of the columns) of +** the virtual tables they implement. +*/ +SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); + +/* +** CAPI3REF: Overload A Function For A Virtual Table +** +** ^(Virtual tables can provide alternative implementations of functions +** using the [xFindFunction] method of the [virtual table module]. +** But global versions of those functions +** must exist in order to be overloaded.)^ +** +** ^(This API makes sure a global version of a function with a particular +** name and number of parameters exists. If no such function exists +** before this API is called, a new function is created.)^ ^The implementation +** of the new function always causes an exception to be thrown. So +** the new function is not good for anything by itself. Its only +** purpose is to be a placeholder function that can be overloaded +** by a [virtual table]. +*/ +SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); + +/* +** The interface to the virtual-table mechanism defined above (back up +** to a comment remarkably similar to this one) is currently considered +** to be experimental. The interface might change in incompatible ways. +** If this is a problem for you, do not use the interface at this time. +** +** When the virtual-table mechanism stabilizes, we will declare the +** interface fixed, support it indefinitely, and remove this comment. +*/ + +/* +** CAPI3REF: A Handle To An Open BLOB +** KEYWORDS: {BLOB handle} {BLOB handles} +** +** An instance of this object represents an open BLOB on which +** [sqlite3_blob_open | incremental BLOB I/O] can be performed. +** ^Objects of this type are created by [sqlite3_blob_open()] +** and destroyed by [sqlite3_blob_close()]. +** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces +** can be used to read or write small subsections of the BLOB. +** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. +*/ +typedef struct sqlite3_blob sqlite3_blob; + +/* +** CAPI3REF: Open A BLOB For Incremental I/O +** +** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located +** in row iRow, column zColumn, table zTable in database zDb; +** in other words, the same BLOB that would be selected by: +** +**
+**     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
+** 
)^ +** +** ^If the flags parameter is non-zero, then the BLOB is opened for read +** and write access. ^If it is zero, the BLOB is opened for read access. +** ^It is not possible to open a column that is part of an index or primary +** key for writing. ^If [foreign key constraints] are enabled, it is +** not possible to open a column that is part of a [child key] for writing. +** +** ^Note that the database name is not the filename that contains +** the database but rather the symbolic name of the database that +** appears after the AS keyword when the database is connected using [ATTACH]. +** ^For the main database file, the database name is "main". +** ^For TEMP tables, the database name is "temp". +** +** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written +** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set +** to be a null pointer.)^ +** ^This function sets the [database connection] error code and message +** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related +** functions. ^Note that the *ppBlob variable is always initialized in a +** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob +** regardless of the success or failure of this routine. +** +** ^(If the row that a BLOB handle points to is modified by an +** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects +** then the BLOB handle is marked as "expired". +** This is true if any column of the row is changed, even a column +** other than the one the BLOB handle is open on.)^ +** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for +** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. +** ^(Changes written into a BLOB prior to the BLOB expiring are not +** rolled back by the expiration of the BLOB. Such changes will eventually +** commit if the transaction continues to completion.)^ +** +** ^Use the [sqlite3_blob_bytes()] interface to determine the size of +** the opened blob. ^The size of a blob may not be changed by this +** interface. Use the [UPDATE] SQL command to change the size of a +** blob. +** +** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces +** and the built-in [zeroblob] SQL function can be used, if desired, +** to create an empty, zero-filled blob in which to read or write using +** this interface. +** +** To avoid a resource leak, every open [BLOB handle] should eventually +** be released by a call to [sqlite3_blob_close()]. +*/ +SQLITE_API int sqlite3_blob_open( + sqlite3*, + const char *zDb, + const char *zTable, + const char *zColumn, + sqlite3_int64 iRow, + int flags, + sqlite3_blob **ppBlob +); + +/* +** CAPI3REF: Move a BLOB Handle to a New Row +** +** ^This function is used to move an existing blob handle so that it points +** to a different row of the same database table. ^The new row is identified +** by the rowid value passed as the second argument. Only the row can be +** changed. ^The database, table and column on which the blob handle is open +** remain the same. Moving an existing blob handle to a new row can be +** faster than closing the existing handle and opening a new one. +** +** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] - +** it must exist and there must be either a blob or text value stored in +** the nominated column.)^ ^If the new row is not present in the table, or if +** it does not contain a blob or text value, or if another error occurs, an +** SQLite error code is returned and the blob handle is considered aborted. +** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or +** [sqlite3_blob_reopen()] on an aborted blob handle immediately return +** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle +** always returns zero. +** +** ^This function sets the database handle error code and message. +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); + +/* +** CAPI3REF: Close A BLOB Handle +** +** ^Closes an open [BLOB handle]. +** +** ^Closing a BLOB shall cause the current transaction to commit +** if there are no other BLOBs, no pending prepared statements, and the +** database connection is in [autocommit mode]. +** ^If any writes were made to the BLOB, they might be held in cache +** until the close operation if they will fit. +** +** ^(Closing the BLOB often forces the changes +** out to disk and so if any I/O errors occur, they will likely occur +** at the time when the BLOB is closed. Any errors that occur during +** closing are reported as a non-zero return value.)^ +** +** ^(The BLOB is closed unconditionally. Even if this routine returns +** an error code, the BLOB is still closed.)^ +** +** ^Calling this routine with a null pointer (such as would be returned +** by a failed call to [sqlite3_blob_open()]) is a harmless no-op. +*/ +SQLITE_API int sqlite3_blob_close(sqlite3_blob *); + +/* +** CAPI3REF: Return The Size Of An Open BLOB +** +** ^Returns the size in bytes of the BLOB accessible via the +** successfully opened [BLOB handle] in its only argument. ^The +** incremental blob I/O routines can only read or overwriting existing +** blob content; they cannot change the size of a blob. +** +** This routine only works on a [BLOB handle] which has been created +** by a prior successful call to [sqlite3_blob_open()] and which has not +** been closed by [sqlite3_blob_close()]. Passing any other pointer in +** to this routine results in undefined and probably undesirable behavior. +*/ +SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); + +/* +** CAPI3REF: Read Data From A BLOB Incrementally +** +** ^(This function is used to read data from an open [BLOB handle] into a +** caller-supplied buffer. N bytes of data are copied into buffer Z +** from the open BLOB, starting at offset iOffset.)^ +** +** ^If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is +** less than zero, [SQLITE_ERROR] is returned and no data is read. +** ^The size of the blob (and hence the maximum value of N+iOffset) +** can be determined using the [sqlite3_blob_bytes()] interface. +** +** ^An attempt to read from an expired [BLOB handle] fails with an +** error code of [SQLITE_ABORT]. +** +** ^(On success, sqlite3_blob_read() returns SQLITE_OK. +** Otherwise, an [error code] or an [extended error code] is returned.)^ +** +** This routine only works on a [BLOB handle] which has been created +** by a prior successful call to [sqlite3_blob_open()] and which has not +** been closed by [sqlite3_blob_close()]. Passing any other pointer in +** to this routine results in undefined and probably undesirable behavior. +** +** See also: [sqlite3_blob_write()]. +*/ +SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); + +/* +** CAPI3REF: Write Data Into A BLOB Incrementally +** +** ^This function is used to write data into an open [BLOB handle] from a +** caller-supplied buffer. ^N bytes of data are copied from the buffer Z +** into the open BLOB, starting at offset iOffset. +** +** ^If the [BLOB handle] passed as the first argument was not opened for +** writing (the flags parameter to [sqlite3_blob_open()] was zero), +** this function returns [SQLITE_READONLY]. +** +** ^This function may only modify the contents of the BLOB; it is +** not possible to increase the size of a BLOB using this API. +** ^If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is written. ^If N is +** less than zero [SQLITE_ERROR] is returned and no data is written. +** The size of the BLOB (and hence the maximum value of N+iOffset) +** can be determined using the [sqlite3_blob_bytes()] interface. +** +** ^An attempt to write to an expired [BLOB handle] fails with an +** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred +** before the [BLOB handle] expired are not rolled back by the +** expiration of the handle, though of course those changes might +** have been overwritten by the statement that expired the BLOB handle +** or by other independent statements. +** +** ^(On success, sqlite3_blob_write() returns SQLITE_OK. +** Otherwise, an [error code] or an [extended error code] is returned.)^ +** +** This routine only works on a [BLOB handle] which has been created +** by a prior successful call to [sqlite3_blob_open()] and which has not +** been closed by [sqlite3_blob_close()]. Passing any other pointer in +** to this routine results in undefined and probably undesirable behavior. +** +** See also: [sqlite3_blob_read()]. +*/ +SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); + +/* +** CAPI3REF: Virtual File System Objects +** +** A virtual filesystem (VFS) is an [sqlite3_vfs] object +** that SQLite uses to interact +** with the underlying operating system. Most SQLite builds come with a +** single default VFS that is appropriate for the host computer. +** New VFSes can be registered and existing VFSes can be unregistered. +** The following interfaces are provided. +** +** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. +** ^Names are case sensitive. +** ^Names are zero-terminated UTF-8 strings. +** ^If there is no match, a NULL pointer is returned. +** ^If zVfsName is NULL then the default VFS is returned. +** +** ^New VFSes are registered with sqlite3_vfs_register(). +** ^Each new VFS becomes the default VFS if the makeDflt flag is set. +** ^The same VFS can be registered multiple times without injury. +** ^To make an existing VFS into the default VFS, register it again +** with the makeDflt flag set. If two different VFSes with the +** same name are registered, the behavior is undefined. If a +** VFS is registered with a name that is NULL or an empty string, +** then the behavior is undefined. +** +** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. +** ^(If the default VFS is unregistered, another VFS is chosen as +** the default. The choice for the new VFS is arbitrary.)^ +*/ +SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); +SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); +SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); + +/* +** CAPI3REF: Mutexes +** +** The SQLite core uses these routines for thread +** synchronization. Though they are intended for internal +** use by SQLite, code that links against SQLite is +** permitted to use any of these routines. +** +** The SQLite source code contains multiple implementations +** of these mutex routines. An appropriate implementation +** is selected automatically at compile-time. ^(The following +** implementations are available in the SQLite core: +** +**
    +**
  • SQLITE_MUTEX_PTHREADS +**
  • SQLITE_MUTEX_W32 +**
  • SQLITE_MUTEX_NOOP +**
)^ +** +** ^The SQLITE_MUTEX_NOOP implementation is a set of routines +** that does no real locking and is appropriate for use in +** a single-threaded application. ^The SQLITE_MUTEX_PTHREADS and +** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix +** and Windows. +** +** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor +** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex +** implementation is included with the library. In this case the +** application must supply a custom mutex implementation using the +** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function +** before calling sqlite3_initialize() or any other public sqlite3_ +** function that calls sqlite3_initialize().)^ +** +** ^The sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. ^If it returns NULL +** that means that a mutex could not be allocated. ^SQLite +** will unwind its stack and return an error. ^(The argument +** to sqlite3_mutex_alloc() is one of these integer constants: +** +**
    +**
  • SQLITE_MUTEX_FAST +**
  • SQLITE_MUTEX_RECURSIVE +**
  • SQLITE_MUTEX_STATIC_MASTER +**
  • SQLITE_MUTEX_STATIC_MEM +**
  • SQLITE_MUTEX_STATIC_MEM2 +**
  • SQLITE_MUTEX_STATIC_PRNG +**
  • SQLITE_MUTEX_STATIC_LRU +**
  • SQLITE_MUTEX_STATIC_LRU2 +**
)^ +** +** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) +** cause sqlite3_mutex_alloc() to create +** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE +** is used but not necessarily so when SQLITE_MUTEX_FAST is used. +** The mutex implementation does not need to make a distinction +** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does +** not want to. ^SQLite will only request a recursive mutex in +** cases where it really needs one. ^If a faster non-recursive mutex +** implementation is available on the host platform, the mutex subsystem +** might return such a mutex in response to SQLITE_MUTEX_FAST. +** +** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other +** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return +** a pointer to a static preexisting mutex. ^Six static mutexes are +** used by the current version of SQLite. Future versions of SQLite +** may add additional static mutexes. Static mutexes are for internal +** use by SQLite only. Applications that use SQLite mutexes should +** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or +** SQLITE_MUTEX_RECURSIVE. +** +** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST +** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() +** returns a different mutex on every call. ^But for the static +** mutex types, the same mutex is returned on every call that has +** the same type number. +** +** ^The sqlite3_mutex_free() routine deallocates a previously +** allocated dynamic mutex. ^SQLite is careful to deallocate every +** dynamic mutex that it allocates. The dynamic mutexes must not be in +** use when they are deallocated. Attempting to deallocate a static +** mutex results in undefined behavior. ^SQLite never deallocates +** a static mutex. +** +** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt +** to enter a mutex. ^If another thread is already within the mutex, +** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return +** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] +** upon successful entry. ^(Mutexes created using +** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. +** In such cases the, +** mutex must be exited an equal number of times before another thread +** can enter.)^ ^(If the same thread tries to enter any other +** kind of mutex more than once, the behavior is undefined. +** SQLite will never exhibit +** such behavior in its own use of mutexes.)^ +** +** ^(Some systems (for example, Windows 95) do not support the operation +** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() +** will always return SQLITE_BUSY. The SQLite core only ever uses +** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^ +** +** ^The sqlite3_mutex_leave() routine exits a mutex that was +** previously entered by the same thread. ^(The behavior +** is undefined if the mutex is not currently entered by the +** calling thread or is not currently allocated. SQLite will +** never do either.)^ +** +** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or +** sqlite3_mutex_leave() is a NULL pointer, then all three routines +** behave as no-ops. +** +** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. +*/ +SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); +SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); + +/* +** CAPI3REF: Mutex Methods Object +** +** An instance of this structure defines the low-level routines +** used to allocate and use mutexes. +** +** Usually, the default mutex implementations provided by SQLite are +** sufficient, however the user has the option of substituting a custom +** implementation for specialized deployments or systems for which SQLite +** does not provide a suitable implementation. In this case, the user +** creates and populates an instance of this structure to pass +** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. +** Additionally, an instance of this structure can be used as an +** output variable when querying the system for the current mutex +** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. +** +** ^The xMutexInit method defined by this structure is invoked as +** part of system initialization by the sqlite3_initialize() function. +** ^The xMutexInit routine is called by SQLite exactly once for each +** effective call to [sqlite3_initialize()]. +** +** ^The xMutexEnd method defined by this structure is invoked as +** part of system shutdown by the sqlite3_shutdown() function. The +** implementation of this method is expected to release all outstanding +** resources obtained by the mutex methods implementation, especially +** those obtained by the xMutexInit method. ^The xMutexEnd() +** interface is invoked exactly once for each call to [sqlite3_shutdown()]. +** +** ^(The remaining seven methods defined by this structure (xMutexAlloc, +** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and +** xMutexNotheld) implement the following interfaces (respectively): +** +**
    +**
  • [sqlite3_mutex_alloc()]
  • +**
  • [sqlite3_mutex_free()]
  • +**
  • [sqlite3_mutex_enter()]
  • +**
  • [sqlite3_mutex_try()]
  • +**
  • [sqlite3_mutex_leave()]
  • +**
  • [sqlite3_mutex_held()]
  • +**
  • [sqlite3_mutex_notheld()]
  • +**
)^ +** +** The only difference is that the public sqlite3_XXX functions enumerated +** above silently ignore any invocations that pass a NULL pointer instead +** of a valid mutex handle. The implementations of the methods defined +** by this structure are not required to handle this case, the results +** of passing a NULL pointer instead of a valid mutex handle are undefined +** (i.e. it is acceptable to provide an implementation that segfaults if +** it is passed a NULL pointer). +** +** The xMutexInit() method must be threadsafe. ^It must be harmless to +** invoke xMutexInit() multiple times within the same process and without +** intervening calls to xMutexEnd(). Second and subsequent calls to +** xMutexInit() must be no-ops. +** +** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] +** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory +** allocation for a static mutex. ^However xMutexAlloc() may use SQLite +** memory allocation for a fast or recursive mutex. +** +** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is +** called, but only if the prior call to xMutexInit returned SQLITE_OK. +** If xMutexInit fails in any way, it is expected to clean up after itself +** prior to returning. +*/ +typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; +struct sqlite3_mutex_methods { + int (*xMutexInit)(void); + int (*xMutexEnd)(void); + sqlite3_mutex *(*xMutexAlloc)(int); + void (*xMutexFree)(sqlite3_mutex *); + void (*xMutexEnter)(sqlite3_mutex *); + int (*xMutexTry)(sqlite3_mutex *); + void (*xMutexLeave)(sqlite3_mutex *); + int (*xMutexHeld)(sqlite3_mutex *); + int (*xMutexNotheld)(sqlite3_mutex *); +}; + +/* +** CAPI3REF: Mutex Verification Routines +** +** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines +** are intended for use inside assert() statements. ^The SQLite core +** never uses these routines except inside an assert() and applications +** are advised to follow the lead of the core. ^The SQLite core only +** provides implementations for these routines when it is compiled +** with the SQLITE_DEBUG flag. ^External mutex implementations +** are only required to provide these routines if SQLITE_DEBUG is +** defined and if NDEBUG is not defined. +** +** ^These routines should return true if the mutex in their argument +** is held or not held, respectively, by the calling thread. +** +** ^The implementation is not required to provide versions of these +** routines that actually work. If the implementation does not provide working +** versions of these routines, it should at least provide stubs that always +** return true so that one does not get spurious assertion failures. +** +** ^If the argument to sqlite3_mutex_held() is a NULL pointer then +** the routine should return 1. This seems counter-intuitive since +** clearly the mutex cannot be held if it does not exist. But +** the reason the mutex does not exist is because the build is not +** using mutexes. And we do not want the assert() containing the +** call to sqlite3_mutex_held() to fail, so a non-zero return is +** the appropriate thing to do. ^The sqlite3_mutex_notheld() +** interface should also return 1 when given a NULL pointer. +*/ +#ifndef NDEBUG +SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); +#endif + +/* +** CAPI3REF: Mutex Types +** +** The [sqlite3_mutex_alloc()] interface takes a single argument +** which is one of these integer constants. +** +** The set of static mutexes may change from one SQLite release to the +** next. Applications that override the built-in mutex logic must be +** prepared to accommodate additional static mutexes. +*/ +#define SQLITE_MUTEX_FAST 0 +#define SQLITE_MUTEX_RECURSIVE 1 +#define SQLITE_MUTEX_STATIC_MASTER 2 +#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ +#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ +#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ +#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ +#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ + +/* +** CAPI3REF: Retrieve the mutex for a database connection +** +** ^This interface returns a pointer the [sqlite3_mutex] object that +** serializes access to the [database connection] given in the argument +** when the [threading mode] is Serialized. +** ^If the [threading mode] is Single-thread or Multi-thread then this +** routine returns a NULL pointer. +*/ +SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); + +/* +** CAPI3REF: Low-Level Control Of Database Files +** +** ^The [sqlite3_file_control()] interface makes a direct call to the +** xFileControl method for the [sqlite3_io_methods] object associated +** with a particular database identified by the second argument. ^The +** name of the database is "main" for the main database or "temp" for the +** TEMP database, or the name that appears after the AS keyword for +** databases that are added using the [ATTACH] SQL command. +** ^A NULL pointer can be used in place of "main" to refer to the +** main database file. +** ^The third and fourth parameters to this routine +** are passed directly through to the second and third parameters of +** the xFileControl method. ^The return value of the xFileControl +** method becomes the return value of this routine. +** +** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes +** a pointer to the underlying [sqlite3_file] object to be written into +** the space pointed to by the 4th parameter. ^The SQLITE_FCNTL_FILE_POINTER +** case is a short-circuit path which does not actually invoke the +** underlying sqlite3_io_methods.xFileControl method. +** +** ^If the second parameter (zDbName) does not match the name of any +** open database file, then SQLITE_ERROR is returned. ^This error +** code is not remembered and will not be recalled by [sqlite3_errcode()] +** or [sqlite3_errmsg()]. The underlying xFileControl method might +** also return SQLITE_ERROR. There is no way to distinguish between +** an incorrect zDbName and an SQLITE_ERROR return from the underlying +** xFileControl method. +** +** See also: [SQLITE_FCNTL_LOCKSTATE] +*/ +SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); + +/* +** CAPI3REF: Testing Interface +** +** ^The sqlite3_test_control() interface is used to read out internal +** state of SQLite and to inject faults into SQLite for testing +** purposes. ^The first parameter is an operation code that determines +** the number, meaning, and operation of all subsequent parameters. +** +** This interface is not for use by applications. It exists solely +** for verifying the correct operation of the SQLite library. Depending +** on how the SQLite library is compiled, this interface might not exist. +** +** The details of the operation codes, their meanings, the parameters +** they take, and what they do are all subject to change without notice. +** Unlike most of the SQLite API, this function is not guaranteed to +** operate consistently from one release to the next. +*/ +SQLITE_API int sqlite3_test_control(int op, ...); + +/* +** CAPI3REF: Testing Interface Operation Codes +** +** These constants are the valid operation code parameters used +** as the first argument to [sqlite3_test_control()]. +** +** These parameters and their meanings are subject to change +** without notice. These values are for testing purposes only. +** Applications should not use any of these parameters or the +** [sqlite3_test_control()] interface. +*/ +#define SQLITE_TESTCTRL_FIRST 5 +#define SQLITE_TESTCTRL_PRNG_SAVE 5 +#define SQLITE_TESTCTRL_PRNG_RESTORE 6 +#define SQLITE_TESTCTRL_PRNG_RESET 7 +#define SQLITE_TESTCTRL_BITVEC_TEST 8 +#define SQLITE_TESTCTRL_FAULT_INSTALL 9 +#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 +#define SQLITE_TESTCTRL_PENDING_BYTE 11 +#define SQLITE_TESTCTRL_ASSERT 12 +#define SQLITE_TESTCTRL_ALWAYS 13 +#define SQLITE_TESTCTRL_RESERVE 14 +#define SQLITE_TESTCTRL_OPTIMIZATIONS 15 +#define SQLITE_TESTCTRL_ISKEYWORD 16 +#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 +#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 +#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 +#define SQLITE_TESTCTRL_LAST 19 + +/* +** CAPI3REF: SQLite Runtime Status +** +** ^This interface is used to retrieve runtime status information +** about the performance of SQLite, and optionally to reset various +** highwater marks. ^The first argument is an integer code for +** the specific parameter to measure. ^(Recognized integer codes +** are of the form [status parameters | SQLITE_STATUS_...].)^ +** ^The current value of the parameter is returned into *pCurrent. +** ^The highest recorded value is returned in *pHighwater. ^If the +** resetFlag is true, then the highest record value is reset after +** *pHighwater is written. ^(Some parameters do not record the highest +** value. For those parameters +** nothing is written into *pHighwater and the resetFlag is ignored.)^ +** ^(Other parameters record only the highwater mark and not the current +** value. For these latter parameters nothing is written into *pCurrent.)^ +** +** ^The sqlite3_status() routine returns SQLITE_OK on success and a +** non-zero [error code] on failure. +** +** This routine is threadsafe but is not atomic. This routine can be +** called while other threads are running the same or different SQLite +** interfaces. However the values returned in *pCurrent and +** *pHighwater reflect the status of SQLite at different points in time +** and it is possible that another thread might change the parameter +** in between the times when *pCurrent and *pHighwater are written. +** +** See also: [sqlite3_db_status()] +*/ +SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); + + +/* +** CAPI3REF: Status Parameters +** KEYWORDS: {status parameters} +** +** These integer constants designate various run-time status parameters +** that can be returned by [sqlite3_status()]. +** +**
+** [[SQLITE_STATUS_MEMORY_USED]] ^(
SQLITE_STATUS_MEMORY_USED
+**
This parameter is the current amount of memory checked out +** using [sqlite3_malloc()], either directly or indirectly. The +** figure includes calls made to [sqlite3_malloc()] by the application +** and internal memory usage by the SQLite library. Scratch memory +** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache +** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in +** this parameter. The amount returned is the sum of the allocation +** sizes as reported by the xSize method in [sqlite3_mem_methods].
)^ +** +** [[SQLITE_STATUS_MALLOC_SIZE]] ^(
SQLITE_STATUS_MALLOC_SIZE
+**
This parameter records the largest memory allocation request +** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their +** internal equivalents). Only the value returned in the +** *pHighwater parameter to [sqlite3_status()] is of interest. +** The value written into the *pCurrent parameter is undefined.
)^ +** +** [[SQLITE_STATUS_MALLOC_COUNT]] ^(
SQLITE_STATUS_MALLOC_COUNT
+**
This parameter records the number of separate memory allocations +** currently checked out.
)^ +** +** [[SQLITE_STATUS_PAGECACHE_USED]] ^(
SQLITE_STATUS_PAGECACHE_USED
+**
This parameter returns the number of pages used out of the +** [pagecache memory allocator] that was configured using +** [SQLITE_CONFIG_PAGECACHE]. The +** value returned is in pages, not in bytes.
)^ +** +** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] +** ^(
SQLITE_STATUS_PAGECACHE_OVERFLOW
+**
This parameter returns the number of bytes of page cache +** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] +** buffer and where forced to overflow to [sqlite3_malloc()]. The +** returned value includes allocations that overflowed because they +** where too large (they were larger than the "sz" parameter to +** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because +** no space was left in the page cache.
)^ +** +** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(
SQLITE_STATUS_PAGECACHE_SIZE
+**
This parameter records the largest memory allocation request +** handed to [pagecache memory allocator]. Only the value returned in the +** *pHighwater parameter to [sqlite3_status()] is of interest. +** The value written into the *pCurrent parameter is undefined.
)^ +** +** [[SQLITE_STATUS_SCRATCH_USED]] ^(
SQLITE_STATUS_SCRATCH_USED
+**
This parameter returns the number of allocations used out of the +** [scratch memory allocator] configured using +** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not +** in bytes. Since a single thread may only have one scratch allocation +** outstanding at time, this parameter also reports the number of threads +** using scratch memory at the same time.
)^ +** +** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(
SQLITE_STATUS_SCRATCH_OVERFLOW
+**
This parameter returns the number of bytes of scratch memory +** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] +** buffer and where forced to overflow to [sqlite3_malloc()]. The values +** returned include overflows because the requested allocation was too +** larger (that is, because the requested allocation was larger than the +** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer +** slots were available. +**
)^ +** +** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(
SQLITE_STATUS_SCRATCH_SIZE
+**
This parameter records the largest memory allocation request +** handed to [scratch memory allocator]. Only the value returned in the +** *pHighwater parameter to [sqlite3_status()] is of interest. +** The value written into the *pCurrent parameter is undefined.
)^ +** +** [[SQLITE_STATUS_PARSER_STACK]] ^(
SQLITE_STATUS_PARSER_STACK
+**
This parameter records the deepest parser stack. It is only +** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].
)^ +**
+** +** New status parameters may be added from time to time. +*/ +#define SQLITE_STATUS_MEMORY_USED 0 +#define SQLITE_STATUS_PAGECACHE_USED 1 +#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2 +#define SQLITE_STATUS_SCRATCH_USED 3 +#define SQLITE_STATUS_SCRATCH_OVERFLOW 4 +#define SQLITE_STATUS_MALLOC_SIZE 5 +#define SQLITE_STATUS_PARSER_STACK 6 +#define SQLITE_STATUS_PAGECACHE_SIZE 7 +#define SQLITE_STATUS_SCRATCH_SIZE 8 +#define SQLITE_STATUS_MALLOC_COUNT 9 + +/* +** CAPI3REF: Database Connection Status +** +** ^This interface is used to retrieve runtime status information +** about a single [database connection]. ^The first argument is the +** database connection object to be interrogated. ^The second argument +** is an integer constant, taken from the set of +** [SQLITE_DBSTATUS options], that +** determines the parameter to interrogate. The set of +** [SQLITE_DBSTATUS options] is likely +** to grow in future releases of SQLite. +** +** ^The current value of the requested parameter is written into *pCur +** and the highest instantaneous value is written into *pHiwtr. ^If +** the resetFlg is true, then the highest instantaneous value is +** reset back down to the current value. +** +** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a +** non-zero [error code] on failure. +** +** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. +*/ +SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); + +/* +** CAPI3REF: Status Parameters for database connections +** KEYWORDS: {SQLITE_DBSTATUS options} +** +** These constants are the available integer "verbs" that can be passed as +** the second argument to the [sqlite3_db_status()] interface. +** +** New verbs may be added in future releases of SQLite. Existing verbs +** might be discontinued. Applications should check the return code from +** [sqlite3_db_status()] to make sure that the call worked. +** The [sqlite3_db_status()] interface will return a non-zero error code +** if a discontinued or unsupported verb is invoked. +** +**
+** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(
SQLITE_DBSTATUS_LOOKASIDE_USED
+**
This parameter returns the number of lookaside memory slots currently +** checked out.
)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(
SQLITE_DBSTATUS_LOOKASIDE_HIT
+**
This parameter returns the number malloc attempts that were +** satisfied using lookaside memory. Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] +** ^(
SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
+**
This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to the amount of +** memory requested being larger than the lookaside slot size. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] +** ^(
SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
+**
This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to all lookaside +** memory already being in use. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_CACHE_USED]] ^(
SQLITE_DBSTATUS_CACHE_USED
+**
This parameter returns the approximate number of of bytes of heap +** memory used by all pager caches associated with the database connection.)^ +** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. +** +** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
SQLITE_DBSTATUS_SCHEMA_USED
+**
This parameter returns the approximate number of of bytes of heap +** memory used to store the schema for all databases associated +** with the connection - main, temp, and any [ATTACH]-ed databases.)^ +** ^The full amount of memory used by the schemas is reported, even if the +** schema memory is shared with other database connections due to +** [shared cache mode] being enabled. +** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. +** +** [[SQLITE_DBSTATUS_STMT_USED]] ^(
SQLITE_DBSTATUS_STMT_USED
+**
This parameter returns the approximate number of of bytes of heap +** and lookaside memory used by all prepared statements associated with +** the database connection.)^ +** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0. +**
+** +** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(
SQLITE_DBSTATUS_CACHE_HIT
+**
This parameter returns the number of pager cache hits that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT +** is always 0. +**
+** +** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(
SQLITE_DBSTATUS_CACHE_MISS
+**
This parameter returns the number of pager cache misses that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS +** is always 0. +**
+** +** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(
SQLITE_DBSTATUS_CACHE_WRITE
+**
This parameter returns the number of dirty cache entries that have +** been written to disk. Specifically, the number of pages written to the +** wal file in wal mode databases, or the number of pages written to the +** database file in rollback mode databases. Any pages written as part of +** transaction rollback or database recovery operations are not included. +** If an IO or other error occurs while writing a page to disk, the effect +** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The +** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. +**
+**
+*/ +#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 +#define SQLITE_DBSTATUS_CACHE_USED 1 +#define SQLITE_DBSTATUS_SCHEMA_USED 2 +#define SQLITE_DBSTATUS_STMT_USED 3 +#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 +#define SQLITE_DBSTATUS_CACHE_HIT 7 +#define SQLITE_DBSTATUS_CACHE_MISS 8 +#define SQLITE_DBSTATUS_CACHE_WRITE 9 +#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */ + + +/* +** CAPI3REF: Prepared Statement Status +** +** ^(Each prepared statement maintains various +** [SQLITE_STMTSTATUS counters] that measure the number +** of times it has performed specific operations.)^ These counters can +** be used to monitor the performance characteristics of the prepared +** statements. For example, if the number of table steps greatly exceeds +** the number of table searches or result rows, that would tend to indicate +** that the prepared statement is using a full table scan rather than +** an index. +** +** ^(This interface is used to retrieve and reset counter values from +** a [prepared statement]. The first argument is the prepared statement +** object to be interrogated. The second argument +** is an integer code for a specific [SQLITE_STMTSTATUS counter] +** to be interrogated.)^ +** ^The current value of the requested counter is returned. +** ^If the resetFlg is true, then the counter is reset to zero after this +** interface call returns. +** +** See also: [sqlite3_status()] and [sqlite3_db_status()]. +*/ +SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); + +/* +** CAPI3REF: Status Parameters for prepared statements +** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters} +** +** These preprocessor macros define integer codes that name counter +** values associated with the [sqlite3_stmt_status()] interface. +** The meanings of the various counters are as follows: +** +**
+** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]]
SQLITE_STMTSTATUS_FULLSCAN_STEP
+**
^This is the number of times that SQLite has stepped forward in +** a table as part of a full table scan. Large numbers for this counter +** may indicate opportunities for performance improvement through +** careful use of indices.
+** +** [[SQLITE_STMTSTATUS_SORT]]
SQLITE_STMTSTATUS_SORT
+**
^This is the number of sort operations that have occurred. +** A non-zero value in this counter may indicate an opportunity to +** improvement performance through careful use of indices.
+** +** [[SQLITE_STMTSTATUS_AUTOINDEX]]
SQLITE_STMTSTATUS_AUTOINDEX
+**
^This is the number of rows inserted into transient indices that +** were created automatically in order to help joins run faster. +** A non-zero value in this counter may indicate an opportunity to +** improvement performance by adding permanent indices that do not +** need to be reinitialized each time the statement is run.
+**
+*/ +#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 +#define SQLITE_STMTSTATUS_SORT 2 +#define SQLITE_STMTSTATUS_AUTOINDEX 3 + +/* +** CAPI3REF: Custom Page Cache Object +** +** The sqlite3_pcache type is opaque. It is implemented by +** the pluggable module. The SQLite core has no knowledge of +** its size or internal structure and never deals with the +** sqlite3_pcache object except by holding and passing pointers +** to the object. +** +** See [sqlite3_pcache_methods2] for additional information. +*/ +typedef struct sqlite3_pcache sqlite3_pcache; + +/* +** CAPI3REF: Custom Page Cache Object +** +** The sqlite3_pcache_page object represents a single page in the +** page cache. The page cache will allocate instances of this +** object. Various methods of the page cache use pointers to instances +** of this object as parameters or as their return value. +** +** See [sqlite3_pcache_methods2] for additional information. +*/ +typedef struct sqlite3_pcache_page sqlite3_pcache_page; +struct sqlite3_pcache_page { + void *pBuf; /* The content of the page */ + void *pExtra; /* Extra information associated with the page */ +}; + +/* +** CAPI3REF: Application Defined Page Cache. +** KEYWORDS: {page cache} +** +** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can +** register an alternative page cache implementation by passing in an +** instance of the sqlite3_pcache_methods2 structure.)^ +** In many applications, most of the heap memory allocated by +** SQLite is used for the page cache. +** By implementing a +** custom page cache using this API, an application can better control +** the amount of memory consumed by SQLite, the way in which +** that memory is allocated and released, and the policies used to +** determine exactly which parts of a database file are cached and for +** how long. +** +** The alternative page cache mechanism is an +** extreme measure that is only needed by the most demanding applications. +** The built-in page cache is recommended for most uses. +** +** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an +** internal buffer by SQLite within the call to [sqlite3_config]. Hence +** the application may discard the parameter after the call to +** [sqlite3_config()] returns.)^ +** +** [[the xInit() page cache method]] +** ^(The xInit() method is called once for each effective +** call to [sqlite3_initialize()])^ +** (usually only once during the lifetime of the process). ^(The xInit() +** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^ +** The intent of the xInit() method is to set up global data structures +** required by the custom page cache implementation. +** ^(If the xInit() method is NULL, then the +** built-in default page cache is used instead of the application defined +** page cache.)^ +** +** [[the xShutdown() page cache method]] +** ^The xShutdown() method is called by [sqlite3_shutdown()]. +** It can be used to clean up +** any outstanding resources before process shutdown, if required. +** ^The xShutdown() method may be NULL. +** +** ^SQLite automatically serializes calls to the xInit method, +** so the xInit method need not be threadsafe. ^The +** xShutdown method is only called from [sqlite3_shutdown()] so it does +** not need to be threadsafe either. All other methods must be threadsafe +** in multithreaded applications. +** +** ^SQLite will never invoke xInit() more than once without an intervening +** call to xShutdown(). +** +** [[the xCreate() page cache methods]] +** ^SQLite invokes the xCreate() method to construct a new cache instance. +** SQLite will typically create one cache instance for each open database file, +** though this is not guaranteed. ^The +** first parameter, szPage, is the size in bytes of the pages that must +** be allocated by the cache. ^szPage will always a power of two. ^The +** second parameter szExtra is a number of bytes of extra storage +** associated with each page cache entry. ^The szExtra parameter will +** a number less than 250. SQLite will use the +** extra szExtra bytes on each page to store metadata about the underlying +** database page on disk. The value passed into szExtra depends +** on the SQLite version, the target platform, and how SQLite was compiled. +** ^The third argument to xCreate(), bPurgeable, is true if the cache being +** created will be used to cache database pages of a file stored on disk, or +** false if it is used for an in-memory database. The cache implementation +** does not have to do anything special based with the value of bPurgeable; +** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will +** never invoke xUnpin() except to deliberately delete a page. +** ^In other words, calls to xUnpin() on a cache with bPurgeable set to +** false will always have the "discard" flag set to true. +** ^Hence, a cache created with bPurgeable false will +** never contain any unpinned pages. +** +** [[the xCachesize() page cache method]] +** ^(The xCachesize() method may be called at any time by SQLite to set the +** suggested maximum cache-size (number of pages stored by) the cache +** instance passed as the first argument. This is the value configured using +** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable +** parameter, the implementation is not required to do anything with this +** value; it is advisory only. +** +** [[the xPagecount() page cache methods]] +** The xPagecount() method must return the number of pages currently +** stored in the cache, both pinned and unpinned. +** +** [[the xFetch() page cache methods]] +** The xFetch() method locates a page in the cache and returns a pointer to +** an sqlite3_pcache_page object associated with that page, or a NULL pointer. +** The pBuf element of the returned sqlite3_pcache_page object will be a +** pointer to a buffer of szPage bytes used to store the content of a +** single database page. The pExtra element of sqlite3_pcache_page will be +** a pointer to the szExtra bytes of extra storage that SQLite has requested +** for each entry in the page cache. +** +** The page to be fetched is determined by the key. ^The minimum key value +** is 1. After it has been retrieved using xFetch, the page is considered +** to be "pinned". +** +** If the requested page is already in the page cache, then the page cache +** implementation must return a pointer to the page buffer with its content +** intact. If the requested page is not already in the cache, then the +** cache implementation should use the value of the createFlag +** parameter to help it determined what action to take: +** +** +**
createFlag Behavior when page is not already in cache +**
0 Do not allocate a new page. Return NULL. +**
1 Allocate a new page if it easy and convenient to do so. +** Otherwise return NULL. +**
2 Make every effort to allocate a new page. Only return +** NULL if allocating a new page is effectively impossible. +**
+** +** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite +** will only use a createFlag of 2 after a prior call with a createFlag of 1 +** failed.)^ In between the to xFetch() calls, SQLite may +** attempt to unpin one or more cache pages by spilling the content of +** pinned pages to disk and synching the operating system disk cache. +** +** [[the xUnpin() page cache method]] +** ^xUnpin() is called by SQLite with a pointer to a currently pinned page +** as its second argument. If the third parameter, discard, is non-zero, +** then the page must be evicted from the cache. +** ^If the discard parameter is +** zero, then the page may be discarded or retained at the discretion of +** page cache implementation. ^The page cache implementation +** may choose to evict unpinned pages at any time. +** +** The cache must not perform any reference counting. A single +** call to xUnpin() unpins the page regardless of the number of prior calls +** to xFetch(). +** +** [[the xRekey() page cache methods]] +** The xRekey() method is used to change the key value associated with the +** page passed as the second argument. If the cache +** previously contains an entry associated with newKey, it must be +** discarded. ^Any prior cache entry associated with newKey is guaranteed not +** to be pinned. +** +** When SQLite calls the xTruncate() method, the cache must discard all +** existing cache entries with page numbers (keys) greater than or equal +** to the value of the iLimit parameter passed to xTruncate(). If any +** of these pages are pinned, they are implicitly unpinned, meaning that +** they can be safely discarded. +** +** [[the xDestroy() page cache method]] +** ^The xDestroy() method is used to delete a cache allocated by xCreate(). +** All resources associated with the specified cache should be freed. ^After +** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] +** handle invalid, and will not use it with any other sqlite3_pcache_methods2 +** functions. +** +** [[the xShrink() page cache method]] +** ^SQLite invokes the xShrink() method when it wants the page cache to +** free up as much of heap memory as possible. The page cache implementation +** is not obligated to free any memory, but well-behaved implementations should +** do their best. +*/ +typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; +struct sqlite3_pcache_methods2 { + int iVersion; + void *pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); + void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, + unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); + void (*xShrink)(sqlite3_pcache*); +}; + +/* +** This is the obsolete pcache_methods object that has now been replaced +** by sqlite3_pcache_methods2. This object is not used by SQLite. It is +** retained in the header file for backwards compatibility only. +*/ +typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; +struct sqlite3_pcache_methods { + void *pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, void*, int discard); + void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); +}; + + +/* +** CAPI3REF: Online Backup Object +** +** The sqlite3_backup object records state information about an ongoing +** online backup operation. ^The sqlite3_backup object is created by +** a call to [sqlite3_backup_init()] and is destroyed by a call to +** [sqlite3_backup_finish()]. +** +** See Also: [Using the SQLite Online Backup API] +*/ +typedef struct sqlite3_backup sqlite3_backup; + +/* +** CAPI3REF: Online Backup API. +** +** The backup API copies the content of one database into another. +** It is useful either for creating backups of databases or +** for copying in-memory databases to or from persistent files. +** +** See Also: [Using the SQLite Online Backup API] +** +** ^SQLite holds a write transaction open on the destination database file +** for the duration of the backup operation. +** ^The source database is read-locked only while it is being read; +** it is not locked continuously for the entire backup operation. +** ^Thus, the backup may be performed on a live source database without +** preventing other database connections from +** reading or writing to the source database while the backup is underway. +** +** ^(To perform a backup operation: +**
    +**
  1. sqlite3_backup_init() is called once to initialize the +** backup, +**
  2. sqlite3_backup_step() is called one or more times to transfer +** the data between the two databases, and finally +**
  3. sqlite3_backup_finish() is called to release all resources +** associated with the backup operation. +**
)^ +** There should be exactly one call to sqlite3_backup_finish() for each +** successful call to sqlite3_backup_init(). +** +** [[sqlite3_backup_init()]] sqlite3_backup_init() +** +** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the +** [database connection] associated with the destination database +** and the database name, respectively. +** ^The database name is "main" for the main database, "temp" for the +** temporary database, or the name specified after the AS keyword in +** an [ATTACH] statement for an attached database. +** ^The S and M arguments passed to +** sqlite3_backup_init(D,N,S,M) identify the [database connection] +** and database name of the source database, respectively. +** ^The source and destination [database connections] (parameters S and D) +** must be different or else sqlite3_backup_init(D,N,S,M) will fail with +** an error. +** +** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is +** returned and an error code and error message are stored in the +** destination [database connection] D. +** ^The error code and message for the failed call to sqlite3_backup_init() +** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or +** [sqlite3_errmsg16()] functions. +** ^A successful call to sqlite3_backup_init() returns a pointer to an +** [sqlite3_backup] object. +** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and +** sqlite3_backup_finish() functions to perform the specified backup +** operation. +** +** [[sqlite3_backup_step()]] sqlite3_backup_step() +** +** ^Function sqlite3_backup_step(B,N) will copy up to N pages between +** the source and destination databases specified by [sqlite3_backup] object B. +** ^If N is negative, all remaining source pages are copied. +** ^If sqlite3_backup_step(B,N) successfully copies N pages and there +** are still more pages to be copied, then the function returns [SQLITE_OK]. +** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages +** from source to destination, then it returns [SQLITE_DONE]. +** ^If an error occurs while running sqlite3_backup_step(B,N), +** then an [error code] is returned. ^As well as [SQLITE_OK] and +** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], +** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an +** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. +** +** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if +**
    +**
  1. the destination database was opened read-only, or +**
  2. the destination database is using write-ahead-log journaling +** and the destination and source page sizes differ, or +**
  3. the destination database is an in-memory database and the +** destination and source page sizes differ. +**
)^ +** +** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then +** the [sqlite3_busy_handler | busy-handler function] +** is invoked (if one is specified). ^If the +** busy-handler returns non-zero before the lock is available, then +** [SQLITE_BUSY] is returned to the caller. ^In this case the call to +** sqlite3_backup_step() can be retried later. ^If the source +** [database connection] +** is being used to write to the source database when sqlite3_backup_step() +** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this +** case the call to sqlite3_backup_step() can be retried later on. ^(If +** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or +** [SQLITE_READONLY] is returned, then +** there is no point in retrying the call to sqlite3_backup_step(). These +** errors are considered fatal.)^ The application must accept +** that the backup operation has failed and pass the backup operation handle +** to the sqlite3_backup_finish() to release associated resources. +** +** ^The first call to sqlite3_backup_step() obtains an exclusive lock +** on the destination file. ^The exclusive lock is not released until either +** sqlite3_backup_finish() is called or the backup operation is complete +** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to +** sqlite3_backup_step() obtains a [shared lock] on the source database that +** lasts for the duration of the sqlite3_backup_step() call. +** ^Because the source database is not locked between calls to +** sqlite3_backup_step(), the source database may be modified mid-way +** through the backup process. ^If the source database is modified by an +** external process or via a database connection other than the one being +** used by the backup operation, then the backup will be automatically +** restarted by the next call to sqlite3_backup_step(). ^If the source +** database is modified by the using the same database connection as is used +** by the backup operation, then the backup database is automatically +** updated at the same time. +** +** [[sqlite3_backup_finish()]] sqlite3_backup_finish() +** +** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the +** application wishes to abandon the backup operation, the application +** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). +** ^The sqlite3_backup_finish() interfaces releases all +** resources associated with the [sqlite3_backup] object. +** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any +** active write-transaction on the destination database is rolled back. +** The [sqlite3_backup] object is invalid +** and may not be used following a call to sqlite3_backup_finish(). +** +** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no +** sqlite3_backup_step() errors occurred, regardless or whether or not +** sqlite3_backup_step() completed. +** ^If an out-of-memory condition or IO error occurred during any prior +** sqlite3_backup_step() call on the same [sqlite3_backup] object, then +** sqlite3_backup_finish() returns the corresponding [error code]. +** +** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() +** is not a permanent error and does not affect the return value of +** sqlite3_backup_finish(). +** +** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] +** sqlite3_backup_remaining() and sqlite3_backup_pagecount() +** +** ^Each call to sqlite3_backup_step() sets two values inside +** the [sqlite3_backup] object: the number of pages still to be backed +** up and the total number of pages in the source database file. +** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces +** retrieve these two values, respectively. +** +** ^The values returned by these functions are only updated by +** sqlite3_backup_step(). ^If the source database is modified during a backup +** operation, then the values are not updated to account for any extra +** pages that need to be updated or the size of the source database file +** changing. +** +** Concurrent Usage of Database Handles +** +** ^The source [database connection] may be used by the application for other +** purposes while a backup operation is underway or being initialized. +** ^If SQLite is compiled and configured to support threadsafe database +** connections, then the source database connection may be used concurrently +** from within other threads. +** +** However, the application must guarantee that the destination +** [database connection] is not passed to any other API (by any thread) after +** sqlite3_backup_init() is called and before the corresponding call to +** sqlite3_backup_finish(). SQLite does not currently check to see +** if the application incorrectly accesses the destination [database connection] +** and so no error code is reported, but the operations may malfunction +** nevertheless. Use of the destination database connection while a +** backup is in progress might also also cause a mutex deadlock. +** +** If running in [shared cache mode], the application must +** guarantee that the shared cache used by the destination database +** is not accessed while the backup is running. In practice this means +** that the application must guarantee that the disk file being +** backed up to is not accessed by any connection within the process, +** not just the specific connection that was passed to sqlite3_backup_init(). +** +** The [sqlite3_backup] object itself is partially threadsafe. Multiple +** threads may safely make multiple concurrent calls to sqlite3_backup_step(). +** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() +** APIs are not strictly speaking threadsafe. If they are invoked at the +** same time as another thread is invoking sqlite3_backup_step() it is +** possible that they return invalid values. +*/ +SQLITE_API sqlite3_backup *sqlite3_backup_init( + sqlite3 *pDest, /* Destination database handle */ + const char *zDestName, /* Destination database name */ + sqlite3 *pSource, /* Source database handle */ + const char *zSourceName /* Source database name */ +); +SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); +SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); +SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); +SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); + +/* +** CAPI3REF: Unlock Notification +** +** ^When running in shared-cache mode, a database operation may fail with +** an [SQLITE_LOCKED] error if the required locks on the shared-cache or +** individual tables within the shared-cache cannot be obtained. See +** [SQLite Shared-Cache Mode] for a description of shared-cache locking. +** ^This API may be used to register a callback that SQLite will invoke +** when the connection currently holding the required lock relinquishes it. +** ^This API is only available if the library was compiled with the +** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. +** +** See Also: [Using the SQLite Unlock Notification Feature]. +** +** ^Shared-cache locks are released when a database connection concludes +** its current transaction, either by committing it or rolling it back. +** +** ^When a connection (known as the blocked connection) fails to obtain a +** shared-cache lock and SQLITE_LOCKED is returned to the caller, the +** identity of the database connection (the blocking connection) that +** has locked the required resource is stored internally. ^After an +** application receives an SQLITE_LOCKED error, it may call the +** sqlite3_unlock_notify() method with the blocked connection handle as +** the first argument to register for a callback that will be invoked +** when the blocking connections current transaction is concluded. ^The +** callback is invoked from within the [sqlite3_step] or [sqlite3_close] +** call that concludes the blocking connections transaction. +** +** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, +** there is a chance that the blocking connection will have already +** concluded its transaction by the time sqlite3_unlock_notify() is invoked. +** If this happens, then the specified callback is invoked immediately, +** from within the call to sqlite3_unlock_notify().)^ +** +** ^If the blocked connection is attempting to obtain a write-lock on a +** shared-cache table, and more than one other connection currently holds +** a read-lock on the same table, then SQLite arbitrarily selects one of +** the other connections to use as the blocking connection. +** +** ^(There may be at most one unlock-notify callback registered by a +** blocked connection. If sqlite3_unlock_notify() is called when the +** blocked connection already has a registered unlock-notify callback, +** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is +** called with a NULL pointer as its second argument, then any existing +** unlock-notify callback is canceled. ^The blocked connections +** unlock-notify callback may also be canceled by closing the blocked +** connection using [sqlite3_close()]. +** +** The unlock-notify callback is not reentrant. If an application invokes +** any sqlite3_xxx API functions from within an unlock-notify callback, a +** crash or deadlock may be the result. +** +** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always +** returns SQLITE_OK. +** +** Callback Invocation Details +** +** When an unlock-notify callback is registered, the application provides a +** single void* pointer that is passed to the callback when it is invoked. +** However, the signature of the callback function allows SQLite to pass +** it an array of void* context pointers. The first argument passed to +** an unlock-notify callback is a pointer to an array of void* pointers, +** and the second is the number of entries in the array. +** +** When a blocking connections transaction is concluded, there may be +** more than one blocked connection that has registered for an unlock-notify +** callback. ^If two or more such blocked connections have specified the +** same callback function, then instead of invoking the callback function +** multiple times, it is invoked once with the set of void* context pointers +** specified by the blocked connections bundled together into an array. +** This gives the application an opportunity to prioritize any actions +** related to the set of unblocked database connections. +** +** Deadlock Detection +** +** Assuming that after registering for an unlock-notify callback a +** database waits for the callback to be issued before taking any further +** action (a reasonable assumption), then using this API may cause the +** application to deadlock. For example, if connection X is waiting for +** connection Y's transaction to be concluded, and similarly connection +** Y is waiting on connection X's transaction, then neither connection +** will proceed and the system may remain deadlocked indefinitely. +** +** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock +** detection. ^If a given call to sqlite3_unlock_notify() would put the +** system in a deadlocked state, then SQLITE_LOCKED is returned and no +** unlock-notify callback is registered. The system is said to be in +** a deadlocked state if connection A has registered for an unlock-notify +** callback on the conclusion of connection B's transaction, and connection +** B has itself registered for an unlock-notify callback when connection +** A's transaction is concluded. ^Indirect deadlock is also detected, so +** the system is also considered to be deadlocked if connection B has +** registered for an unlock-notify callback on the conclusion of connection +** C's transaction, where connection C is waiting on connection A. ^Any +** number of levels of indirection are allowed. +** +** The "DROP TABLE" Exception +** +** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost +** always appropriate to call sqlite3_unlock_notify(). There is however, +** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement, +** SQLite checks if there are any currently executing SELECT statements +** that belong to the same connection. If there are, SQLITE_LOCKED is +** returned. In this case there is no "blocking connection", so invoking +** sqlite3_unlock_notify() results in the unlock-notify callback being +** invoked immediately. If the application then re-attempts the "DROP TABLE" +** or "DROP INDEX" query, an infinite loop might be the result. +** +** One way around this problem is to check the extended error code returned +** by an sqlite3_step() call. ^(If there is a blocking connection, then the +** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in +** the special "DROP TABLE/INDEX" case, the extended error code is just +** SQLITE_LOCKED.)^ +*/ +SQLITE_API int sqlite3_unlock_notify( + sqlite3 *pBlocked, /* Waiting connection */ + void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ + void *pNotifyArg /* Argument to pass to xNotify */ +); + + +/* +** CAPI3REF: String Comparison +** +** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications +** and extensions to compare the contents of two buffers containing UTF-8 +** strings in a case-independent fashion, using the same definition of "case +** independence" that SQLite uses internally when comparing identifiers. +*/ +SQLITE_API int sqlite3_stricmp(const char *, const char *); +SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); + +/* +** CAPI3REF: Error Logging Interface +** +** ^The [sqlite3_log()] interface writes a message into the error log +** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. +** ^If logging is enabled, the zFormat string and subsequent arguments are +** used with [sqlite3_snprintf()] to generate the final output string. +** +** The sqlite3_log() interface is intended for use by extensions such as +** virtual tables, collating functions, and SQL functions. While there is +** nothing to prevent an application from calling sqlite3_log(), doing so +** is considered bad form. +** +** The zFormat string must not be NULL. +** +** To avoid deadlocks and other threading problems, the sqlite3_log() routine +** will not use dynamically allocated memory. The log message is stored in +** a fixed-length buffer on the stack. If the log message is longer than +** a few hundred characters, it will be truncated to the length of the +** buffer. +*/ +SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); + +/* +** CAPI3REF: Write-Ahead Log Commit Hook +** +** ^The [sqlite3_wal_hook()] function is used to register a callback that +** will be invoked each time a database connection commits data to a +** [write-ahead log] (i.e. whenever a transaction is committed in +** [journal_mode | journal_mode=WAL mode]). +** +** ^The callback is invoked by SQLite after the commit has taken place and +** the associated write-lock on the database released, so the implementation +** may read, write or [checkpoint] the database as required. +** +** ^The first parameter passed to the callback function when it is invoked +** is a copy of the third parameter passed to sqlite3_wal_hook() when +** registering the callback. ^The second is a copy of the database handle. +** ^The third parameter is the name of the database that was written to - +** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter +** is the number of pages currently in the write-ahead log file, +** including those that were just committed. +** +** The callback function should normally return [SQLITE_OK]. ^If an error +** code is returned, that error will propagate back up through the +** SQLite code base to cause the statement that provoked the callback +** to report an error, though the commit will have still occurred. If the +** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value +** that does not correspond to any valid SQLite error code, the results +** are undefined. +** +** A single database handle may have at most a single write-ahead log callback +** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any +** previously registered write-ahead log callback. ^Note that the +** [sqlite3_wal_autocheckpoint()] interface and the +** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will +** those overwrite any prior [sqlite3_wal_hook()] settings. +*/ +SQLITE_API void *sqlite3_wal_hook( + sqlite3*, + int(*)(void *,sqlite3*,const char*,int), + void* +); + +/* +** CAPI3REF: Configure an auto-checkpoint +** +** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around +** [sqlite3_wal_hook()] that causes any database on [database connection] D +** to automatically [checkpoint] +** after committing a transaction if there are N or +** more frames in the [write-ahead log] file. ^Passing zero or +** a negative value as the nFrame parameter disables automatic +** checkpoints entirely. +** +** ^The callback registered by this function replaces any existing callback +** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback +** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism +** configured by this function. +** +** ^The [wal_autocheckpoint pragma] can be used to invoke this interface +** from SQL. +** +** ^Every new [database connection] defaults to having the auto-checkpoint +** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] +** pages. The use of this interface +** is only necessary if the default setting is found to be suboptimal +** for a particular application. +*/ +SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); + +/* +** CAPI3REF: Checkpoint a database +** +** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X +** on [database connection] D to be [checkpointed]. ^If X is NULL or an +** empty string, then a checkpoint is run on all databases of +** connection D. ^If the database connection D is not in +** [WAL | write-ahead log mode] then this interface is a harmless no-op. +** +** ^The [wal_checkpoint pragma] can be used to invoke this interface +** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the +** [wal_autocheckpoint pragma] can be used to cause this interface to be +** run whenever the WAL reaches a certain size threshold. +** +** See also: [sqlite3_wal_checkpoint_v2()] +*/ +SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); + +/* +** CAPI3REF: Checkpoint a database +** +** Run a checkpoint operation on WAL database zDb attached to database +** handle db. The specific operation is determined by the value of the +** eMode parameter: +** +**
+**
SQLITE_CHECKPOINT_PASSIVE
+** Checkpoint as many frames as possible without waiting for any database +** readers or writers to finish. Sync the db file if all frames in the log +** are checkpointed. This mode is the same as calling +** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked. +** +**
SQLITE_CHECKPOINT_FULL
+** This mode blocks (calls the busy-handler callback) until there is no +** database writer and all readers are reading from the most recent database +** snapshot. It then checkpoints all frames in the log file and syncs the +** database file. This call blocks database writers while it is running, +** but not database readers. +** +**
SQLITE_CHECKPOINT_RESTART
+** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after +** checkpointing the log file it blocks (calls the busy-handler callback) +** until all readers are reading from the database file only. This ensures +** that the next client to write to the database file restarts the log file +** from the beginning. This call blocks database writers while it is running, +** but not database readers. +**
+** +** If pnLog is not NULL, then *pnLog is set to the total number of frames in +** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to +** the total number of checkpointed frames (including any that were already +** checkpointed when this function is called). *pnLog and *pnCkpt may be +** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK. +** If no values are available because of an error, they are both set to -1 +** before returning to communicate this to the caller. +** +** All calls obtain an exclusive "checkpoint" lock on the database file. If +** any other process is running a checkpoint operation at the same time, the +** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a +** busy-handler configured, it will not be invoked in this case. +** +** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive +** "writer" lock on the database file. If the writer lock cannot be obtained +** immediately, and a busy-handler is configured, it is invoked and the writer +** lock retried until either the busy-handler returns 0 or the lock is +** successfully obtained. The busy-handler is also invoked while waiting for +** database readers as described above. If the busy-handler returns 0 before +** the writer lock is obtained or while waiting for database readers, the +** checkpoint operation proceeds from that point in the same way as +** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible +** without blocking any further. SQLITE_BUSY is returned in this case. +** +** If parameter zDb is NULL or points to a zero length string, then the +** specified operation is attempted on all WAL databases. In this case the +** values written to output parameters *pnLog and *pnCkpt are undefined. If +** an SQLITE_BUSY error is encountered when processing one or more of the +** attached WAL databases, the operation is still attempted on any remaining +** attached databases and SQLITE_BUSY is returned to the caller. If any other +** error occurs while processing an attached database, processing is abandoned +** and the error code returned to the caller immediately. If no error +** (SQLITE_BUSY or otherwise) is encountered while processing the attached +** databases, SQLITE_OK is returned. +** +** If database zDb is the name of an attached database that is not in WAL +** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If +** zDb is not NULL (or a zero length string) and is not the name of any +** attached database, SQLITE_ERROR is returned to the caller. +*/ +SQLITE_API int sqlite3_wal_checkpoint_v2( + sqlite3 *db, /* Database handle */ + const char *zDb, /* Name of attached database (or NULL) */ + int eMode, /* SQLITE_CHECKPOINT_* value */ + int *pnLog, /* OUT: Size of WAL log in frames */ + int *pnCkpt /* OUT: Total number of frames checkpointed */ +); + +/* +** CAPI3REF: Checkpoint operation parameters +** +** These constants can be used as the 3rd parameter to +** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] +** documentation for additional information about the meaning and use of +** each of these values. +*/ +#define SQLITE_CHECKPOINT_PASSIVE 0 +#define SQLITE_CHECKPOINT_FULL 1 +#define SQLITE_CHECKPOINT_RESTART 2 + +/* +** CAPI3REF: Virtual Table Interface Configuration +** +** This function may be called by either the [xConnect] or [xCreate] method +** of a [virtual table] implementation to configure +** various facets of the virtual table interface. +** +** If this interface is invoked outside the context of an xConnect or +** xCreate virtual table method then the behavior is undefined. +** +** At present, there is only one option that may be configured using +** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options +** may be added in the future. +*/ +SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + +/* +** CAPI3REF: Virtual Table Configuration Options +** +** These macros define the various options to the +** [sqlite3_vtab_config()] interface that [virtual table] implementations +** can use to customize and optimize their behavior. +** +**
+**
SQLITE_VTAB_CONSTRAINT_SUPPORT +**
Calls of the form +** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, +** where X is an integer. If X is zero, then the [virtual table] whose +** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not +** support constraints. In this configuration (which is the default) if +** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire +** statement is rolled back as if [ON CONFLICT | OR ABORT] had been +** specified as part of the users SQL statement, regardless of the actual +** ON CONFLICT mode specified. +** +** If X is non-zero, then the virtual table implementation guarantees +** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before +** any modifications to internal or persistent data structures have been made. +** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite +** is able to roll back a statement or database transaction, and abandon +** or continue processing the current SQL statement as appropriate. +** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns +** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode +** had been ABORT. +** +** Virtual table implementations that are required to handle OR REPLACE +** must do so within the [xUpdate] method. If a call to the +** [sqlite3_vtab_on_conflict()] function indicates that the current ON +** CONFLICT policy is REPLACE, the virtual table implementation should +** silently replace the appropriate rows within the xUpdate callback and +** return SQLITE_OK. Or, if this is not possible, it may return +** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT +** constraint handling. +**
+*/ +#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 + +/* +** CAPI3REF: Determine The Virtual Table Conflict Policy +** +** This function may only be called from within a call to the [xUpdate] method +** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The +** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], +** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode +** of the SQL statement that triggered the call to the [xUpdate] method of the +** [virtual table]. +*/ +SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); + +/* +** CAPI3REF: Conflict resolution modes +** +** These constants are returned by [sqlite3_vtab_on_conflict()] to +** inform a [virtual table] implementation what the [ON CONFLICT] mode +** is for the SQL statement being evaluated. +** +** Note that the [SQLITE_IGNORE] constant is also used as a potential +** return value from the [sqlite3_set_authorizer()] callback and that +** [SQLITE_ABORT] is also a [result code]. +*/ +#define SQLITE_ROLLBACK 1 +/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ +#define SQLITE_FAIL 3 +/* #define SQLITE_ABORT 4 // Also an error code */ +#define SQLITE_REPLACE 5 + + + +/* +** Undo the hack that converts floating point types to integer for +** builds on processors without floating point support. +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# undef double +#endif + +#if 0 +} /* End of the 'extern "C"' block */ +#endif +#endif + +/* +** 2010 August 30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +*/ + +#ifndef _SQLITE3RTREE_H_ +#define _SQLITE3RTREE_H_ + + +#if 0 +extern "C" { +#endif + +typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; + +/* +** Register a geometry callback named zGeom that can be used as part of an +** R-Tree geometry query as follows: +** +** SELECT ... FROM WHERE MATCH $zGeom(... params ...) +*/ +SQLITE_API int sqlite3_rtree_geometry_callback( + sqlite3 *db, + const char *zGeom, +#ifdef SQLITE_RTREE_INT_ONLY + int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes), +#else + int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes), +#endif + void *pContext +); + + +/* +** A pointer to a structure of the following type is passed as the first +** argument to callbacks registered using rtree_geometry_callback(). +*/ +struct sqlite3_rtree_geometry { + void *pContext; /* Copy of pContext passed to s_r_g_c() */ + int nParam; /* Size of array aParam[] */ + double *aParam; /* Parameters passed to SQL geom function */ + void *pUser; /* Callback implementation user data */ + void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ +}; + + +#if 0 +} /* end of the 'extern "C"' block */ +#endif + +#endif /* ifndef _SQLITE3RTREE_H_ */ + + +/************** End of sqlite3.h *********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include hash.h in the middle of sqliteInt.h ******************/ +/************** Begin file hash.h ********************************************/ +/* +** 2001 September 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This is the header file for the generic hash-table implementation +** used in SQLite. +*/ +#ifndef _SQLITE_HASH_H_ +#define _SQLITE_HASH_H_ + +/* Forward declarations of structures. */ +typedef struct Hash Hash; +typedef struct HashElem HashElem; + +/* A complete hash table is an instance of the following structure. +** The internals of this structure are intended to be opaque -- client +** code should not attempt to access or modify the fields of this structure +** directly. Change this structure only by using the routines below. +** However, some of the "procedures" and "functions" for modifying and +** accessing this structure are really macros, so we can't really make +** this structure opaque. +** +** All elements of the hash table are on a single doubly-linked list. +** Hash.first points to the head of this list. +** +** There are Hash.htsize buckets. Each bucket points to a spot in +** the global doubly-linked list. The contents of the bucket are the +** element pointed to plus the next _ht.count-1 elements in the list. +** +** Hash.htsize and Hash.ht may be zero. In that case lookup is done +** by a linear search of the global list. For small tables, the +** Hash.ht table is never allocated because if there are few elements +** in the table, it is faster to do a linear search than to manage +** the hash table. +*/ +struct Hash { + unsigned int htsize; /* Number of buckets in the hash table */ + unsigned int count; /* Number of entries in this table */ + HashElem *first; /* The first element of the array */ + struct _ht { /* the hash table */ + int count; /* Number of entries with this hash */ + HashElem *chain; /* Pointer to first entry with this hash */ + } *ht; +}; + +/* Each element in the hash table is an instance of the following +** structure. All elements are stored on a single doubly-linked list. +** +** Again, this structure is intended to be opaque, but it can't really +** be opaque because it is used by macros. +*/ +struct HashElem { + HashElem *next, *prev; /* Next and previous elements in the table */ + void *data; /* Data associated with this element */ + const char *pKey; int nKey; /* Key associated with this element */ +}; + +/* +** Access routines. To delete, insert a NULL pointer. +*/ +SQLITE_PRIVATE void sqlite3HashInit(Hash*); +SQLITE_PRIVATE void *sqlite3HashInsert(Hash*, const char *pKey, int nKey, void *pData); +SQLITE_PRIVATE void *sqlite3HashFind(const Hash*, const char *pKey, int nKey); +SQLITE_PRIVATE void sqlite3HashClear(Hash*); + +/* +** Macros for looping over all elements of a hash table. The idiom is +** like this: +** +** Hash h; +** HashElem *p; +** ... +** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){ +** SomeStructure *pData = sqliteHashData(p); +** // do something with pData +** } +*/ +#define sqliteHashFirst(H) ((H)->first) +#define sqliteHashNext(E) ((E)->next) +#define sqliteHashData(E) ((E)->data) +/* #define sqliteHashKey(E) ((E)->pKey) // NOT USED */ +/* #define sqliteHashKeysize(E) ((E)->nKey) // NOT USED */ + +/* +** Number of entries in a hash table +*/ +/* #define sqliteHashCount(H) ((H)->count) // NOT USED */ + +#endif /* _SQLITE_HASH_H_ */ + +/************** End of hash.h ************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include parse.h in the middle of sqliteInt.h *****************/ +/************** Begin file parse.h *******************************************/ +#define TK_SEMI 1 +#define TK_EXPLAIN 2 +#define TK_QUERY 3 +#define TK_PLAN 4 +#define TK_BEGIN 5 +#define TK_TRANSACTION 6 +#define TK_DEFERRED 7 +#define TK_IMMEDIATE 8 +#define TK_EXCLUSIVE 9 +#define TK_COMMIT 10 +#define TK_END 11 +#define TK_ROLLBACK 12 +#define TK_SAVEPOINT 13 +#define TK_RELEASE 14 +#define TK_TO 15 +#define TK_TABLE 16 +#define TK_CREATE 17 +#define TK_IF 18 +#define TK_NOT 19 +#define TK_EXISTS 20 +#define TK_TEMP 21 +#define TK_LP 22 +#define TK_RP 23 +#define TK_AS 24 +#define TK_COMMA 25 +#define TK_ID 26 +#define TK_INDEXED 27 +#define TK_ABORT 28 +#define TK_ACTION 29 +#define TK_AFTER 30 +#define TK_ANALYZE 31 +#define TK_ASC 32 +#define TK_ATTACH 33 +#define TK_BEFORE 34 +#define TK_BY 35 +#define TK_CASCADE 36 +#define TK_CAST 37 +#define TK_COLUMNKW 38 +#define TK_CONFLICT 39 +#define TK_DATABASE 40 +#define TK_DESC 41 +#define TK_DETACH 42 +#define TK_EACH 43 +#define TK_FAIL 44 +#define TK_FOR 45 +#define TK_IGNORE 46 +#define TK_INITIALLY 47 +#define TK_INSTEAD 48 +#define TK_LIKE_KW 49 +#define TK_MATCH 50 +#define TK_NO 51 +#define TK_KEY 52 +#define TK_OF 53 +#define TK_OFFSET 54 +#define TK_PRAGMA 55 +#define TK_RAISE 56 +#define TK_REPLACE 57 +#define TK_RESTRICT 58 +#define TK_ROW 59 +#define TK_TRIGGER 60 +#define TK_VACUUM 61 +#define TK_VIEW 62 +#define TK_VIRTUAL 63 +#define TK_REINDEX 64 +#define TK_RENAME 65 +#define TK_CTIME_KW 66 +#define TK_ANY 67 +#define TK_OR 68 +#define TK_AND 69 +#define TK_IS 70 +#define TK_BETWEEN 71 +#define TK_IN 72 +#define TK_ISNULL 73 +#define TK_NOTNULL 74 +#define TK_NE 75 +#define TK_EQ 76 +#define TK_GT 77 +#define TK_LE 78 +#define TK_LT 79 +#define TK_GE 80 +#define TK_ESCAPE 81 +#define TK_BITAND 82 +#define TK_BITOR 83 +#define TK_LSHIFT 84 +#define TK_RSHIFT 85 +#define TK_PLUS 86 +#define TK_MINUS 87 +#define TK_STAR 88 +#define TK_SLASH 89 +#define TK_REM 90 +#define TK_CONCAT 91 +#define TK_COLLATE 92 +#define TK_BITNOT 93 +#define TK_STRING 94 +#define TK_JOIN_KW 95 +#define TK_CONSTRAINT 96 +#define TK_DEFAULT 97 +#define TK_NULL 98 +#define TK_PRIMARY 99 +#define TK_UNIQUE 100 +#define TK_CHECK 101 +#define TK_REFERENCES 102 +#define TK_AUTOINCR 103 +#define TK_ON 104 +#define TK_INSERT 105 +#define TK_DELETE 106 +#define TK_UPDATE 107 +#define TK_SET 108 +#define TK_DEFERRABLE 109 +#define TK_FOREIGN 110 +#define TK_DROP 111 +#define TK_UNION 112 +#define TK_ALL 113 +#define TK_EXCEPT 114 +#define TK_INTERSECT 115 +#define TK_SELECT 116 +#define TK_DISTINCT 117 +#define TK_DOT 118 +#define TK_FROM 119 +#define TK_JOIN 120 +#define TK_USING 121 +#define TK_ORDER 122 +#define TK_GROUP 123 +#define TK_HAVING 124 +#define TK_LIMIT 125 +#define TK_WHERE 126 +#define TK_INTO 127 +#define TK_VALUES 128 +#define TK_INTEGER 129 +#define TK_FLOAT 130 +#define TK_BLOB 131 +#define TK_REGISTER 132 +#define TK_VARIABLE 133 +#define TK_CASE 134 +#define TK_WHEN 135 +#define TK_THEN 136 +#define TK_ELSE 137 +#define TK_INDEX 138 +#define TK_ALTER 139 +#define TK_ADD 140 +#define TK_TO_TEXT 141 +#define TK_TO_BLOB 142 +#define TK_TO_NUMERIC 143 +#define TK_TO_INT 144 +#define TK_TO_REAL 145 +#define TK_ISNOT 146 +#define TK_END_OF_FILE 147 +#define TK_ILLEGAL 148 +#define TK_SPACE 149 +#define TK_UNCLOSED_STRING 150 +#define TK_FUNCTION 151 +#define TK_COLUMN 152 +#define TK_AGG_FUNCTION 153 +#define TK_AGG_COLUMN 154 +#define TK_CONST_FUNC 155 +#define TK_UMINUS 156 +#define TK_UPLUS 157 + +/************** End of parse.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +#include +#include +#include +#include +#include + +/* +** If compiling for a processor that lacks floating point support, +** substitute integer for floating-point +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# define double sqlite_int64 +# define float sqlite_int64 +# define LONGDOUBLE_TYPE sqlite_int64 +# ifndef SQLITE_BIG_DBL +# define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) +# endif +# define SQLITE_OMIT_DATETIME_FUNCS 1 +# define SQLITE_OMIT_TRACE 1 +# undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +# undef SQLITE_HAVE_ISNAN +#endif +#ifndef SQLITE_BIG_DBL +# define SQLITE_BIG_DBL (1e99) +#endif + +/* +** OMIT_TEMPDB is set to 1 if SQLITE_OMIT_TEMPDB is defined, or 0 +** afterward. Having this macro allows us to cause the C compiler +** to omit code used by TEMP tables without messy #ifndef statements. +*/ +#ifdef SQLITE_OMIT_TEMPDB +#define OMIT_TEMPDB 1 +#else +#define OMIT_TEMPDB 0 +#endif + +/* +** The "file format" number is an integer that is incremented whenever +** the VDBE-level file format changes. The following macros define the +** the default file format for new databases and the maximum file format +** that the library can read. +*/ +#define SQLITE_MAX_FILE_FORMAT 4 +#ifndef SQLITE_DEFAULT_FILE_FORMAT +# define SQLITE_DEFAULT_FILE_FORMAT 4 +#endif + +/* +** Determine whether triggers are recursive by default. This can be +** changed at run-time using a pragma. +*/ +#ifndef SQLITE_DEFAULT_RECURSIVE_TRIGGERS +# define SQLITE_DEFAULT_RECURSIVE_TRIGGERS 0 +#endif + +/* +** Provide a default value for SQLITE_TEMP_STORE in case it is not specified +** on the command-line +*/ +#ifndef SQLITE_TEMP_STORE +# define SQLITE_TEMP_STORE 1 +#endif + +/* +** GCC does not define the offsetof() macro so we'll have to do it +** ourselves. +*/ +#ifndef offsetof +#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) +#endif + +/* +** Check to see if this machine uses EBCDIC. (Yes, believe it or +** not, there are still machines out there that use EBCDIC.) +*/ +#if 'A' == '\301' +# define SQLITE_EBCDIC 1 +#else +# define SQLITE_ASCII 1 +#endif + +/* +** Integers of known sizes. These typedefs might change for architectures +** where the sizes very. Preprocessor macros are available so that the +** types can be conveniently redefined at compile-type. Like this: +** +** cc '-DUINTPTR_TYPE=long long int' ... +*/ +#ifndef UINT32_TYPE +# ifdef HAVE_UINT32_T +# define UINT32_TYPE uint32_t +# else +# define UINT32_TYPE unsigned int +# endif +#endif +#ifndef UINT16_TYPE +# ifdef HAVE_UINT16_T +# define UINT16_TYPE uint16_t +# else +# define UINT16_TYPE unsigned short int +# endif +#endif +#ifndef INT16_TYPE +# ifdef HAVE_INT16_T +# define INT16_TYPE int16_t +# else +# define INT16_TYPE short int +# endif +#endif +#ifndef UINT8_TYPE +# ifdef HAVE_UINT8_T +# define UINT8_TYPE uint8_t +# else +# define UINT8_TYPE unsigned char +# endif +#endif +#ifndef INT8_TYPE +# ifdef HAVE_INT8_T +# define INT8_TYPE int8_t +# else +# define INT8_TYPE signed char +# endif +#endif +#ifndef LONGDOUBLE_TYPE +# define LONGDOUBLE_TYPE long double +#endif +typedef sqlite_int64 i64; /* 8-byte signed integer */ +typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ +typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ +typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ +typedef INT16_TYPE i16; /* 2-byte signed integer */ +typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ +typedef INT8_TYPE i8; /* 1-byte signed integer */ + +/* +** SQLITE_MAX_U32 is a u64 constant that is the maximum u64 value +** that can be stored in a u32 without loss of data. The value +** is 0x00000000ffffffff. But because of quirks of some compilers, we +** have to specify the value in the less intuitive manner shown: +*/ +#define SQLITE_MAX_U32 ((((u64)1)<<32)-1) + +/* +** The datatype used to store estimates of the number of rows in a +** table or index. This is an unsigned integer type. For 99.9% of +** the world, a 32-bit integer is sufficient. But a 64-bit integer +** can be used at compile-time if desired. +*/ +#ifdef SQLITE_64BIT_STATS + typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */ +#else + typedef u32 tRowcnt; /* 32-bit is the default */ +#endif + +/* +** Macros to determine whether the machine is big or little endian, +** evaluated at runtime. +*/ +#ifdef SQLITE_AMALGAMATION +SQLITE_PRIVATE const int sqlite3one = 1; +#else +SQLITE_PRIVATE const int sqlite3one; +#endif +#if defined(i386) || defined(__i386__) || defined(_M_IX86)\ + || defined(__x86_64) || defined(__x86_64__) +# define SQLITE_BIGENDIAN 0 +# define SQLITE_LITTLEENDIAN 1 +# define SQLITE_UTF16NATIVE SQLITE_UTF16LE +#else +# define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0) +# define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1) +# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) +#endif + +/* +** Constants for the largest and smallest possible 64-bit signed integers. +** These macros are designed to work correctly on both 32-bit and 64-bit +** compilers. +*/ +#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) +#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) + +/* +** Round up a number to the next larger multiple of 8. This is used +** to force 8-byte alignment on 64-bit architectures. +*/ +#define ROUND8(x) (((x)+7)&~7) + +/* +** Round down to the nearest multiple of 8 +*/ +#define ROUNDDOWN8(x) ((x)&~7) + +/* +** Assert that the pointer X is aligned to an 8-byte boundary. This +** macro is used only within assert() to verify that the code gets +** all alignment restrictions correct. +** +** Except, if SQLITE_4_BYTE_ALIGNED_MALLOC is defined, then the +** underlying malloc() implemention might return us 4-byte aligned +** pointers. In that case, only verify 4-byte alignment. +*/ +#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC +# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0) +#else +# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) +#endif + + +/* +** An instance of the following structure is used to store the busy-handler +** callback for a given sqlite handle. +** +** The sqlite.busyHandler member of the sqlite struct contains the busy +** callback for the database handle. Each pager opened via the sqlite +** handle is passed a pointer to sqlite.busyHandler. The busy-handler +** callback is currently invoked only from within pager.c. +*/ +typedef struct BusyHandler BusyHandler; +struct BusyHandler { + int (*xFunc)(void *,int); /* The busy callback */ + void *pArg; /* First arg to busy callback */ + int nBusy; /* Incremented with each busy call */ +}; + +/* +** Name of the master database table. The master database table +** is a special table that holds the names and attributes of all +** user tables and indices. +*/ +#define MASTER_NAME "sqlite_master" +#define TEMP_MASTER_NAME "sqlite_temp_master" + +/* +** The root-page of the master database table. +*/ +#define MASTER_ROOT 1 + +/* +** The name of the schema table. +*/ +#define SCHEMA_TABLE(x) ((!OMIT_TEMPDB)&&(x==1)?TEMP_MASTER_NAME:MASTER_NAME) + +/* +** A convenience macro that returns the number of elements in +** an array. +*/ +#define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) + +/* +** Determine if the argument is a power of two +*/ +#define IsPowerOfTwo(X) (((X)&((X)-1))==0) + +/* +** The following value as a destructor means to use sqlite3DbFree(). +** The sqlite3DbFree() routine requires two parameters instead of the +** one parameter that destructors normally want. So we have to introduce +** this magic value that the code knows to handle differently. Any +** pointer will work here as long as it is distinct from SQLITE_STATIC +** and SQLITE_TRANSIENT. +*/ +#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize) + +/* +** When SQLITE_OMIT_WSD is defined, it means that the target platform does +** not support Writable Static Data (WSD) such as global and static variables. +** All variables must either be on the stack or dynamically allocated from +** the heap. When WSD is unsupported, the variable declarations scattered +** throughout the SQLite code must become constants instead. The SQLITE_WSD +** macro is used for this purpose. And instead of referencing the variable +** directly, we use its constant as a key to lookup the run-time allocated +** buffer that holds real variable. The constant is also the initializer +** for the run-time allocated buffer. +** +** In the usual case where WSD is supported, the SQLITE_WSD and GLOBAL +** macros become no-ops and have zero performance impact. +*/ +#ifdef SQLITE_OMIT_WSD + #define SQLITE_WSD const + #define GLOBAL(t,v) (*(t*)sqlite3_wsd_find((void*)&(v), sizeof(v))) + #define sqlite3GlobalConfig GLOBAL(struct Sqlite3Config, sqlite3Config) +SQLITE_API int sqlite3_wsd_init(int N, int J); +SQLITE_API void *sqlite3_wsd_find(void *K, int L); +#else + #define SQLITE_WSD + #define GLOBAL(t,v) v + #define sqlite3GlobalConfig sqlite3Config +#endif + +/* +** The following macros are used to suppress compiler warnings and to +** make it clear to human readers when a function parameter is deliberately +** left unused within the body of a function. This usually happens when +** a function is called via a function pointer. For example the +** implementation of an SQL aggregate step callback may not use the +** parameter indicating the number of arguments passed to the aggregate, +** if it knows that this is enforced elsewhere. +** +** When a function parameter is not used at all within the body of a function, +** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. +** However, these macros may also be used to suppress warnings related to +** parameters that may or may not be used depending on compilation options. +** For example those parameters only used in assert() statements. In these +** cases the parameters are named as per the usual conventions. +*/ +#define UNUSED_PARAMETER(x) (void)(x) +#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) + +/* +** Forward references to structures +*/ +typedef struct AggInfo AggInfo; +typedef struct AuthContext AuthContext; +typedef struct AutoincInfo AutoincInfo; +typedef struct Bitvec Bitvec; +typedef struct CollSeq CollSeq; +typedef struct Column Column; +typedef struct Db Db; +typedef struct Schema Schema; +typedef struct Expr Expr; +typedef struct ExprList ExprList; +typedef struct ExprSpan ExprSpan; +typedef struct FKey FKey; +typedef struct FuncDestructor FuncDestructor; +typedef struct FuncDef FuncDef; +typedef struct FuncDefHash FuncDefHash; +typedef struct IdList IdList; +typedef struct Index Index; +typedef struct IndexSample IndexSample; +typedef struct KeyClass KeyClass; +typedef struct KeyInfo KeyInfo; +typedef struct Lookaside Lookaside; +typedef struct LookasideSlot LookasideSlot; +typedef struct Module Module; +typedef struct NameContext NameContext; +typedef struct Parse Parse; +typedef struct RowSet RowSet; +typedef struct Savepoint Savepoint; +typedef struct Select Select; +typedef struct SelectDest SelectDest; +typedef struct SrcList SrcList; +typedef struct StrAccum StrAccum; +typedef struct Table Table; +typedef struct TableLock TableLock; +typedef struct Token Token; +typedef struct Trigger Trigger; +typedef struct TriggerPrg TriggerPrg; +typedef struct TriggerStep TriggerStep; +typedef struct UnpackedRecord UnpackedRecord; +typedef struct VTable VTable; +typedef struct VtabCtx VtabCtx; +typedef struct Walker Walker; +typedef struct WherePlan WherePlan; +typedef struct WhereInfo WhereInfo; +typedef struct WhereLevel WhereLevel; + +/* +** Defer sourcing vdbe.h and btree.h until after the "u8" and +** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque +** pointer types (i.e. FuncDef) defined above. +*/ +/************** Include btree.h in the middle of sqliteInt.h *****************/ +/************** Begin file btree.h *******************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite B-Tree file +** subsystem. See comments in the source code for a detailed description +** of what each interface routine does. +*/ +#ifndef _BTREE_H_ +#define _BTREE_H_ + +/* TODO: This definition is just included so other modules compile. It +** needs to be revisited. +*/ +#define SQLITE_N_BTREE_META 10 + +/* +** If defined as non-zero, auto-vacuum is enabled by default. Otherwise +** it must be turned on for each database using "PRAGMA auto_vacuum = 1". +*/ +#ifndef SQLITE_DEFAULT_AUTOVACUUM + #define SQLITE_DEFAULT_AUTOVACUUM 0 +#endif + +#define BTREE_AUTOVACUUM_NONE 0 /* Do not do auto-vacuum */ +#define BTREE_AUTOVACUUM_FULL 1 /* Do full auto-vacuum */ +#define BTREE_AUTOVACUUM_INCR 2 /* Incremental vacuum */ + +/* +** Forward declarations of structure +*/ +typedef struct Btree Btree; +typedef struct BtCursor BtCursor; +typedef struct BtShared BtShared; + + +SQLITE_PRIVATE int sqlite3BtreeOpen( + sqlite3_vfs *pVfs, /* VFS to use with this b-tree */ + const char *zFilename, /* Name of database file to open */ + sqlite3 *db, /* Associated database connection */ + Btree **ppBtree, /* Return open Btree* here */ + int flags, /* Flags */ + int vfsFlags /* Flags passed through to VFS open */ +); + +/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the +** following values. +** +** NOTE: These values must match the corresponding PAGER_ values in +** pager.h. +*/ +#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */ +#define BTREE_MEMORY 2 /* This is an in-memory DB */ +#define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */ +#define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */ + +SQLITE_PRIVATE int sqlite3BtreeClose(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int); +SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); +SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); +SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); +SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); +#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); +#endif +SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); +SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); +SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); +SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); +SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags); +SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); +SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); +SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); +SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); +SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); +SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); +SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int); + +SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); +SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); +SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); + +SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); + +/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR +** of the flags shown below. +** +** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set. +** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data +** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With +** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored +** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL +** indices.) +*/ +#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ +#define BTREE_BLOBKEY 2 /* Table has keys only - no data */ + +SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); +SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); +SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int); + +SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); +SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); + +SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p); + +/* +** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta +** should be one of the following values. The integer values are assigned +** to constants so that the offset of the corresponding field in an +** SQLite database header may be found using the following formula: +** +** offset = 36 + (idx * 4) +** +** For example, the free-page-count field is located at byte offset 36 of +** the database file header. The incr-vacuum-flag field is located at +** byte offset 64 (== 36+4*7). +*/ +#define BTREE_FREE_PAGE_COUNT 0 +#define BTREE_SCHEMA_VERSION 1 +#define BTREE_FILE_FORMAT 2 +#define BTREE_DEFAULT_CACHE_SIZE 3 +#define BTREE_LARGEST_ROOT_PAGE 4 +#define BTREE_TEXT_ENCODING 5 +#define BTREE_USER_VERSION 6 +#define BTREE_INCR_VACUUM 7 + +/* +** Values that may be OR'd together to form the second argument of an +** sqlite3BtreeCursorHints() call. +*/ +#define BTREE_BULKLOAD 0x00000001 + +SQLITE_PRIVATE int sqlite3BtreeCursor( + Btree*, /* BTree containing table to open */ + int iTable, /* Index of root page */ + int wrFlag, /* 1 for writing. 0 for read-only */ + struct KeyInfo*, /* First argument to compare function */ + BtCursor *pCursor /* Space to write cursor structure */ +); +SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); +SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); + +SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( + BtCursor*, + UnpackedRecord *pUnKey, + i64 intKey, + int bias, + int *pRes +); +SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*, int*); +SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, + const void *pData, int nData, + int nZero, int bias, int seekResult); +SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); +SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); +SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); +SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); +SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64); +SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*); + +SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); +SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); + +SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *); +SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *); +SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); +SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask); + +#ifndef NDEBUG +SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); +#endif + +#ifndef SQLITE_OMIT_BTREECOUNT +SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *, i64 *); +#endif + +#ifdef SQLITE_TEST +SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); +SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); +#endif + +#ifndef SQLITE_OMIT_WAL +SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); +#endif + +/* +** If we are not using shared cache, then there is no need to +** use mutexes to access the BtShared structures. So make the +** Enter and Leave procedures no-ops. +*/ +#ifndef SQLITE_OMIT_SHARED_CACHE +SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*); +SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); +#else +# define sqlite3BtreeEnter(X) +# define sqlite3BtreeEnterAll(X) +#endif + +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE +SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*); +SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); +SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); +SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); +SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); +#ifndef NDEBUG + /* These routines are used inside assert() statements only. */ +SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); +SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); +SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); +#endif +#else + +# define sqlite3BtreeSharable(X) 0 +# define sqlite3BtreeLeave(X) +# define sqlite3BtreeEnterCursor(X) +# define sqlite3BtreeLeaveCursor(X) +# define sqlite3BtreeLeaveAll(X) + +# define sqlite3BtreeHoldsMutex(X) 1 +# define sqlite3BtreeHoldsAllMutexes(X) 1 +# define sqlite3SchemaMutexHeld(X,Y,Z) 1 +#endif + + +#endif /* _BTREE_H_ */ + +/************** End of btree.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include vdbe.h in the middle of sqliteInt.h ******************/ +/************** Begin file vdbe.h ********************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Header file for the Virtual DataBase Engine (VDBE) +** +** This header defines the interface to the virtual database engine +** or VDBE. The VDBE implements an abstract machine that runs a +** simple program to access and modify the underlying database. +*/ +#ifndef _SQLITE_VDBE_H_ +#define _SQLITE_VDBE_H_ +/* #include */ + +/* +** A single VDBE is an opaque structure named "Vdbe". Only routines +** in the source file sqliteVdbe.c are allowed to see the insides +** of this structure. +*/ +typedef struct Vdbe Vdbe; + +/* +** The names of the following types declared in vdbeInt.h are required +** for the VdbeOp definition. +*/ +typedef struct VdbeFunc VdbeFunc; +typedef struct Mem Mem; +typedef struct SubProgram SubProgram; + +/* +** A single instruction of the virtual machine has an opcode +** and as many as three operands. The instruction is recorded +** as an instance of the following structure: +*/ +struct VdbeOp { + u8 opcode; /* What operation to perform */ + signed char p4type; /* One of the P4_xxx constants for p4 */ + u8 opflags; /* Mask of the OPFLG_* flags in opcodes.h */ + u8 p5; /* Fifth parameter is an unsigned character */ + int p1; /* First operand */ + int p2; /* Second parameter (often the jump destination) */ + int p3; /* The third parameter */ + union { /* fourth parameter */ + int i; /* Integer value if p4type==P4_INT32 */ + void *p; /* Generic pointer */ + char *z; /* Pointer to data for string (char array) types */ + i64 *pI64; /* Used when p4type is P4_INT64 */ + double *pReal; /* Used when p4type is P4_REAL */ + FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ + VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */ + CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ + Mem *pMem; /* Used when p4type is P4_MEM */ + VTable *pVtab; /* Used when p4type is P4_VTAB */ + KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ + int *ai; /* Used when p4type is P4_INTARRAY */ + SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ + int (*xAdvance)(BtCursor *, int *); + } p4; +#ifdef SQLITE_DEBUG + char *zComment; /* Comment to improve readability */ +#endif +#ifdef VDBE_PROFILE + int cnt; /* Number of times this instruction was executed */ + u64 cycles; /* Total time spent executing this instruction */ +#endif +}; +typedef struct VdbeOp VdbeOp; + + +/* +** A sub-routine used to implement a trigger program. +*/ +struct SubProgram { + VdbeOp *aOp; /* Array of opcodes for sub-program */ + int nOp; /* Elements in aOp[] */ + int nMem; /* Number of memory cells required */ + int nCsr; /* Number of cursors required */ + int nOnce; /* Number of OP_Once instructions */ + void *token; /* id that may be used to recursive triggers */ + SubProgram *pNext; /* Next sub-program already visited */ +}; + +/* +** A smaller version of VdbeOp used for the VdbeAddOpList() function because +** it takes up less space. +*/ +struct VdbeOpList { + u8 opcode; /* What operation to perform */ + signed char p1; /* First operand */ + signed char p2; /* Second parameter (often the jump destination) */ + signed char p3; /* Third parameter */ +}; +typedef struct VdbeOpList VdbeOpList; + +/* +** Allowed values of VdbeOp.p4type +*/ +#define P4_NOTUSED 0 /* The P4 parameter is not used */ +#define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ +#define P4_STATIC (-2) /* Pointer to a static string */ +#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */ +#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */ +#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */ +#define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */ +#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */ +#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */ +#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */ +#define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ +#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ +#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ +#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ +#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ +#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ +#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */ + +/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure +** is made. That copy is freed when the Vdbe is finalized. But if the +** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still +** gets freed when the Vdbe is finalized so it still should be obtained +** from a single sqliteMalloc(). But no copy is made and the calling +** function should *not* try to free the KeyInfo. +*/ +#define P4_KEYINFO_HANDOFF (-16) +#define P4_KEYINFO_STATIC (-17) + +/* +** The Vdbe.aColName array contains 5n Mem structures, where n is the +** number of columns of data returned by the statement. +*/ +#define COLNAME_NAME 0 +#define COLNAME_DECLTYPE 1 +#define COLNAME_DATABASE 2 +#define COLNAME_TABLE 3 +#define COLNAME_COLUMN 4 +#ifdef SQLITE_ENABLE_COLUMN_METADATA +# define COLNAME_N 5 /* Number of COLNAME_xxx symbols */ +#else +# ifdef SQLITE_OMIT_DECLTYPE +# define COLNAME_N 1 /* Store only the name */ +# else +# define COLNAME_N 2 /* Store the name and decltype */ +# endif +#endif + +/* +** The following macro converts a relative address in the p2 field +** of a VdbeOp structure into a negative number so that +** sqlite3VdbeAddOpList() knows that the address is relative. Calling +** the macro again restores the address. +*/ +#define ADDR(X) (-1-(X)) + +/* +** The makefile scans the vdbe.c source file and creates the "opcodes.h" +** header file that defines a number for each opcode used by the VDBE. +*/ +/************** Include opcodes.h in the middle of vdbe.h ********************/ +/************** Begin file opcodes.h *****************************************/ +/* Automatically generated. Do not edit */ +/* See the mkopcodeh.awk script for details */ +#define OP_Goto 1 +#define OP_Gosub 2 +#define OP_Return 3 +#define OP_Yield 4 +#define OP_HaltIfNull 5 +#define OP_Halt 6 +#define OP_Integer 7 +#define OP_Int64 8 +#define OP_Real 130 /* same as TK_FLOAT */ +#define OP_String8 94 /* same as TK_STRING */ +#define OP_String 9 +#define OP_Null 10 +#define OP_Blob 11 +#define OP_Variable 12 +#define OP_Move 13 +#define OP_Copy 14 +#define OP_SCopy 15 +#define OP_ResultRow 16 +#define OP_Concat 91 /* same as TK_CONCAT */ +#define OP_Add 86 /* same as TK_PLUS */ +#define OP_Subtract 87 /* same as TK_MINUS */ +#define OP_Multiply 88 /* same as TK_STAR */ +#define OP_Divide 89 /* same as TK_SLASH */ +#define OP_Remainder 90 /* same as TK_REM */ +#define OP_CollSeq 17 +#define OP_Function 18 +#define OP_BitAnd 82 /* same as TK_BITAND */ +#define OP_BitOr 83 /* same as TK_BITOR */ +#define OP_ShiftLeft 84 /* same as TK_LSHIFT */ +#define OP_ShiftRight 85 /* same as TK_RSHIFT */ +#define OP_AddImm 20 +#define OP_MustBeInt 21 +#define OP_RealAffinity 22 +#define OP_ToText 141 /* same as TK_TO_TEXT */ +#define OP_ToBlob 142 /* same as TK_TO_BLOB */ +#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/ +#define OP_ToInt 144 /* same as TK_TO_INT */ +#define OP_ToReal 145 /* same as TK_TO_REAL */ +#define OP_Eq 76 /* same as TK_EQ */ +#define OP_Ne 75 /* same as TK_NE */ +#define OP_Lt 79 /* same as TK_LT */ +#define OP_Le 78 /* same as TK_LE */ +#define OP_Gt 77 /* same as TK_GT */ +#define OP_Ge 80 /* same as TK_GE */ +#define OP_Permutation 23 +#define OP_Compare 24 +#define OP_Jump 25 +#define OP_And 69 /* same as TK_AND */ +#define OP_Or 68 /* same as TK_OR */ +#define OP_Not 19 /* same as TK_NOT */ +#define OP_BitNot 93 /* same as TK_BITNOT */ +#define OP_Once 26 +#define OP_If 27 +#define OP_IfNot 28 +#define OP_IsNull 73 /* same as TK_ISNULL */ +#define OP_NotNull 74 /* same as TK_NOTNULL */ +#define OP_Column 29 +#define OP_Affinity 30 +#define OP_MakeRecord 31 +#define OP_Count 32 +#define OP_Savepoint 33 +#define OP_AutoCommit 34 +#define OP_Transaction 35 +#define OP_ReadCookie 36 +#define OP_SetCookie 37 +#define OP_VerifyCookie 38 +#define OP_OpenRead 39 +#define OP_OpenWrite 40 +#define OP_OpenAutoindex 41 +#define OP_OpenEphemeral 42 +#define OP_SorterOpen 43 +#define OP_OpenPseudo 44 +#define OP_Close 45 +#define OP_SeekLt 46 +#define OP_SeekLe 47 +#define OP_SeekGe 48 +#define OP_SeekGt 49 +#define OP_Seek 50 +#define OP_NotFound 51 +#define OP_Found 52 +#define OP_IsUnique 53 +#define OP_NotExists 54 +#define OP_Sequence 55 +#define OP_NewRowid 56 +#define OP_Insert 57 +#define OP_InsertInt 58 +#define OP_Delete 59 +#define OP_ResetCount 60 +#define OP_SorterCompare 61 +#define OP_SorterData 62 +#define OP_RowKey 63 +#define OP_RowData 64 +#define OP_Rowid 65 +#define OP_NullRow 66 +#define OP_Last 67 +#define OP_SorterSort 70 +#define OP_Sort 71 +#define OP_Rewind 72 +#define OP_SorterNext 81 +#define OP_Prev 92 +#define OP_Next 95 +#define OP_SorterInsert 96 +#define OP_IdxInsert 97 +#define OP_IdxDelete 98 +#define OP_IdxRowid 99 +#define OP_IdxLT 100 +#define OP_IdxGE 101 +#define OP_Destroy 102 +#define OP_Clear 103 +#define OP_CreateIndex 104 +#define OP_CreateTable 105 +#define OP_ParseSchema 106 +#define OP_LoadAnalysis 107 +#define OP_DropTable 108 +#define OP_DropIndex 109 +#define OP_DropTrigger 110 +#define OP_IntegrityCk 111 +#define OP_RowSetAdd 112 +#define OP_RowSetRead 113 +#define OP_RowSetTest 114 +#define OP_Program 115 +#define OP_Param 116 +#define OP_FkCounter 117 +#define OP_FkIfZero 118 +#define OP_MemMax 119 +#define OP_IfPos 120 +#define OP_IfNeg 121 +#define OP_IfZero 122 +#define OP_AggStep 123 +#define OP_AggFinal 124 +#define OP_Checkpoint 125 +#define OP_JournalMode 126 +#define OP_Vacuum 127 +#define OP_IncrVacuum 128 +#define OP_Expire 129 +#define OP_TableLock 131 +#define OP_VBegin 132 +#define OP_VCreate 133 +#define OP_VDestroy 134 +#define OP_VOpen 135 +#define OP_VFilter 136 +#define OP_VColumn 137 +#define OP_VNext 138 +#define OP_VRename 139 +#define OP_VUpdate 140 +#define OP_Pagecount 146 +#define OP_MaxPgcnt 147 +#define OP_Trace 148 +#define OP_Noop 149 +#define OP_Explain 150 + + +/* Properties such as "out2" or "jump" that are specified in +** comments following the "case" for each opcode in the vdbe.c +** are encoded into bitvectors as follows: +*/ +#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */ +#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */ +#define OPFLG_IN1 0x0004 /* in1: P1 is an input */ +#define OPFLG_IN2 0x0008 /* in2: P2 is an input */ +#define OPFLG_IN3 0x0010 /* in3: P3 is an input */ +#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */ +#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ +#define OPFLG_INITIALIZER {\ +/* 0 */ 0x00, 0x01, 0x01, 0x04, 0x04, 0x10, 0x00, 0x02,\ +/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x24,\ +/* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\ +/* 24 */ 0x00, 0x01, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00,\ +/* 32 */ 0x02, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00,\ +/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\ +/* 48 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x02,\ +/* 56 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 64 */ 0x00, 0x02, 0x00, 0x01, 0x4c, 0x4c, 0x01, 0x01,\ +/* 72 */ 0x01, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\ +/* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\ +/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x01, 0x24, 0x02, 0x01,\ +/* 96 */ 0x08, 0x08, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\ +/* 104 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 112 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\ +/* 120 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00,\ +/* 128 */ 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 136 */ 0x01, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x04,\ +/* 144 */ 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,} + +/************** End of opcodes.h *********************************************/ +/************** Continuing where we left off in vdbe.h ***********************/ + +/* +** Prototypes for the VDBE interface. See comments on the implementation +** for a description of what each of these routines does. +*/ +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); +SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); +SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); +SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1); +SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2); +SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); +SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); +SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); +SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr); +SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); +SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); +SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); +SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3*,Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*); +SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); +SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *, int); +SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); +#endif +SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe*); +SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe*,int); +SQLITE_PRIVATE int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); +SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe*); +SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); +SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); +SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); +SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8); +SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int); +#ifndef SQLITE_OMIT_TRACE +SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*); +#endif + +SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); +SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); +SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **); + +#ifndef SQLITE_OMIT_TRIGGER +SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); +#endif + + +#ifndef NDEBUG +SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...); +# define VdbeComment(X) sqlite3VdbeComment X +SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); +# define VdbeNoopComment(X) sqlite3VdbeNoopComment X +#else +# define VdbeComment(X) +# define VdbeNoopComment(X) +#endif + +#endif + +/************** End of vdbe.h ************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include pager.h in the middle of sqliteInt.h *****************/ +/************** Begin file pager.h *******************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite page cache +** subsystem. The page cache subsystem reads and writes a file a page +** at a time and provides a journal for rollback. +*/ + +#ifndef _PAGER_H_ +#define _PAGER_H_ + +/* +** Default maximum size for persistent journal files. A negative +** value means no limit. This value may be overridden using the +** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". +*/ +#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT + #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 +#endif + +/* +** The type used to represent a page number. The first page in a file +** is called page 1. 0 is used to represent "not a page". +*/ +typedef u32 Pgno; + +/* +** Each open file is managed by a separate instance of the "Pager" structure. +*/ +typedef struct Pager Pager; + +/* +** Handle type for pages. +*/ +typedef struct PgHdr DbPage; + +/* +** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is +** reserved for working around a windows/posix incompatibility). It is +** used in the journal to signify that the remainder of the journal file +** is devoted to storing a master journal name - there are no more pages to +** roll back. See comments for function writeMasterJournal() in pager.c +** for details. +*/ +#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) + +/* +** Allowed values for the flags parameter to sqlite3PagerOpen(). +** +** NOTE: These values must match the corresponding BTREE_ values in btree.h. +*/ +#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ +#define PAGER_MEMORY 0x0002 /* In-memory database */ + +/* +** Valid values for the second argument to sqlite3PagerLockingMode(). +*/ +#define PAGER_LOCKINGMODE_QUERY -1 +#define PAGER_LOCKINGMODE_NORMAL 0 +#define PAGER_LOCKINGMODE_EXCLUSIVE 1 + +/* +** Numeric constants that encode the journalmode. +*/ +#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ +#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ +#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ +#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ +#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ +#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ +#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ + +/* +** The remainder of this file contains the declarations of the functions +** that make up the Pager sub-system API. See source code comments for +** a detailed description of each routine. +*/ + +/* Open and close a Pager connection. */ +SQLITE_PRIVATE int sqlite3PagerOpen( + sqlite3_vfs*, + Pager **ppPager, + const char*, + int, + int, + int, + void(*)(DbPage*) +); +SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); + +/* Functions used to configure a Pager object. */ +SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); +SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); +SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); +SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); +SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); +SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int,int); +SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); +SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); +SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); +SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); +SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); +SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); + +/* Functions used to obtain and release page references. */ +SQLITE_PRIVATE int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); +#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0) +SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); +SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); +SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); + +/* Operations on page references. */ +SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); +SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); +SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); +SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); +SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); +SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); + +/* Functions used to manage pager transactions and savepoints. */ +SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); +SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); +SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); +SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); +SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); +SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); +SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); +SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); +SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); + +#ifndef SQLITE_OMIT_WAL +SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*); +SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); +SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager); +#endif + +#ifdef SQLITE_ENABLE_ZIPVFS +SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); +#endif + +/* Functions used to query pager state and configuration. */ +SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); +SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); +SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); +SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); +SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); +SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); +SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); +SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); +SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); +SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); +SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); +SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); +SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); + +/* Functions used to truncate the database file. */ +SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); + +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) +SQLITE_PRIVATE void *sqlite3PagerCodec(DbPage *); +#endif + +/* Functions to support testing and debugging. */ +#if !defined(NDEBUG) || defined(SQLITE_TEST) +SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); +SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); +#endif +#ifdef SQLITE_TEST +SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); +SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); + void disable_simulated_io_errors(void); + void enable_simulated_io_errors(void); +#else +# define disable_simulated_io_errors() +# define enable_simulated_io_errors() +#endif + +#endif /* _PAGER_H_ */ + +/************** End of pager.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include pcache.h in the middle of sqliteInt.h ****************/ +/************** Begin file pcache.h ******************************************/ +/* +** 2008 August 05 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite page cache +** subsystem. +*/ + +#ifndef _PCACHE_H_ + +typedef struct PgHdr PgHdr; +typedef struct PCache PCache; + +/* +** Every page in the cache is controlled by an instance of the following +** structure. +*/ +struct PgHdr { + sqlite3_pcache_page *pPage; /* Pcache object page handle */ + void *pData; /* Page data */ + void *pExtra; /* Extra content */ + PgHdr *pDirty; /* Transient list of dirty pages */ + Pager *pPager; /* The pager this page is part of */ + Pgno pgno; /* Page number for this page */ +#ifdef SQLITE_CHECK_PAGES + u32 pageHash; /* Hash of page content */ +#endif + u16 flags; /* PGHDR flags defined below */ + + /********************************************************************** + ** Elements above are public. All that follows is private to pcache.c + ** and should not be accessed by other modules. + */ + i16 nRef; /* Number of users of this page */ + PCache *pCache; /* Cache that owns this page */ + + PgHdr *pDirtyNext; /* Next element in list of dirty pages */ + PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ +}; + +/* Bit values for PgHdr.flags */ +#define PGHDR_DIRTY 0x002 /* Page has changed */ +#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before + ** writing this page to the database */ +#define PGHDR_NEED_READ 0x008 /* Content is unread */ +#define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */ +#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ + +/* Initialize and shutdown the page cache subsystem */ +SQLITE_PRIVATE int sqlite3PcacheInitialize(void); +SQLITE_PRIVATE void sqlite3PcacheShutdown(void); + +/* Page cache buffer management: +** These routines implement SQLITE_CONFIG_PAGECACHE. +*/ +SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *, int sz, int n); + +/* Create a new pager cache. +** Under memory stress, invoke xStress to try to make pages clean. +** Only clean and unpinned pages can be reclaimed. +*/ +SQLITE_PRIVATE void sqlite3PcacheOpen( + int szPage, /* Size of every page */ + int szExtra, /* Extra space associated with each page */ + int bPurgeable, /* True if pages are on backing store */ + int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */ + void *pStress, /* Argument to xStress */ + PCache *pToInit /* Preallocated space for the PCache */ +); + +/* Modify the page-size after the cache has been created. */ +SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *, int); + +/* Return the size in bytes of a PCache object. Used to preallocate +** storage space. +*/ +SQLITE_PRIVATE int sqlite3PcacheSize(void); + +/* One release per successful fetch. Page is pinned until released. +** Reference counted. +*/ +SQLITE_PRIVATE int sqlite3PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**); +SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr*); + +SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */ +SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */ +SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */ +SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */ + +/* Change a page number. Used by incr-vacuum. */ +SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr*, Pgno); + +/* Remove all pages with pgno>x. Reset the cache if x==0 */ +SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache*, Pgno x); + +/* Get a list of all dirty pages in the cache, sorted by page number */ +SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache*); + +/* Reset and close the cache object */ +SQLITE_PRIVATE void sqlite3PcacheClose(PCache*); + +/* Clear flags from pages of the page cache */ +SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); + +/* Discard the contents of the cache */ +SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); + +/* Return the total number of outstanding page references */ +SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*); + +/* Increment the reference count of an existing page */ +SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); + +SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*); + +/* Return the total number of pages stored in the cache */ +SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); + +#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) +/* Iterate through all dirty pages currently stored in the cache. This +** interface is only available if SQLITE_CHECK_PAGES is defined when the +** library is built. +*/ +SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)); +#endif + +/* Set and get the suggested cache-size for the specified pager-cache. +** +** If no global maximum is configured, then the system attempts to limit +** the total number of pages cached by purgeable pager-caches to the sum +** of the suggested cache-sizes. +*/ +SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int); +#ifdef SQLITE_TEST +SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *); +#endif + +/* Free up as much memory as possible from the page cache */ +SQLITE_PRIVATE void sqlite3PcacheShrink(PCache*); + +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +/* Try to return memory used by the pcache module to the main memory heap */ +SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int); +#endif + +#ifdef SQLITE_TEST +SQLITE_PRIVATE void sqlite3PcacheStats(int*,int*,int*,int*); +#endif + +SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); + +#endif /* _PCACHE_H_ */ + +/************** End of pcache.h **********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + +/************** Include os.h in the middle of sqliteInt.h ********************/ +/************** Begin file os.h **********************************************/ +/* +** 2001 September 16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This header file (together with is companion C source-code file +** "os.c") attempt to abstract the underlying operating system so that +** the SQLite library will work on both POSIX and windows systems. +** +** This header file is #include-ed by sqliteInt.h and thus ends up +** being included by every source file. +*/ +#ifndef _SQLITE_OS_H_ +#define _SQLITE_OS_H_ + +/* +** Figure out if we are dealing with Unix, Windows, or some other +** operating system. After the following block of preprocess macros, +** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, and SQLITE_OS_OTHER +** will defined to either 1 or 0. One of the four will be 1. The other +** three will be 0. +*/ +#if defined(SQLITE_OS_OTHER) +# if SQLITE_OS_OTHER==1 +# undef SQLITE_OS_UNIX +# define SQLITE_OS_UNIX 0 +# undef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +# else +# undef SQLITE_OS_OTHER +# endif +#endif +#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER) +# define SQLITE_OS_OTHER 0 +# ifndef SQLITE_OS_WIN +# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) +# define SQLITE_OS_WIN 1 +# define SQLITE_OS_UNIX 0 +# else +# define SQLITE_OS_WIN 0 +# define SQLITE_OS_UNIX 1 +# endif +# else +# define SQLITE_OS_UNIX 0 +# endif +#else +# ifndef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +# endif +#endif + +#if SQLITE_OS_WIN +# include +#endif + +/* +** Determine if we are dealing with Windows NT. +** +** We ought to be able to determine if we are compiling for win98 or winNT +** using the _WIN32_WINNT macro as follows: +** +** #if defined(_WIN32_WINNT) +** # define SQLITE_OS_WINNT 1 +** #else +** # define SQLITE_OS_WINNT 0 +** #endif +** +** However, vs2005 does not set _WIN32_WINNT by default, as it ought to, +** so the above test does not work. We'll just assume that everything is +** winNT unless the programmer explicitly says otherwise by setting +** SQLITE_OS_WINNT to 0. +*/ +#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT) +# define SQLITE_OS_WINNT 1 +#endif + +/* +** Determine if we are dealing with WindowsCE - which has a much +** reduced API. +*/ +#if defined(_WIN32_WCE) +# define SQLITE_OS_WINCE 1 +#else +# define SQLITE_OS_WINCE 0 +#endif + +/* +** Determine if we are dealing with WinRT, which provides only a subset of +** the full Win32 API. +*/ +#if !defined(SQLITE_OS_WINRT) +# define SQLITE_OS_WINRT 0 +#endif + +/* +** When compiled for WinCE or WinRT, there is no concept of the current +** directory. + */ +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +# define SQLITE_CURDIR 1 +#endif + +/* If the SET_FULLSYNC macro is not defined above, then make it +** a no-op +*/ +#ifndef SET_FULLSYNC +# define SET_FULLSYNC(x,y) +#endif + +/* +** The default size of a disk sector +*/ +#ifndef SQLITE_DEFAULT_SECTOR_SIZE +# define SQLITE_DEFAULT_SECTOR_SIZE 4096 +#endif + +/* +** Temporary files are named starting with this prefix followed by 16 random +** alphanumeric characters, and no file extension. They are stored in the +** OS's standard temporary file directory, and are deleted prior to exit. +** If sqlite is being embedded in another program, you may wish to change the +** prefix to reflect your program's name, so that if your program exits +** prematurely, old temporary files can be easily identified. This can be done +** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. +** +** 2006-10-31: The default prefix used to be "sqlite_". But then +** Mcafee started using SQLite in their anti-virus product and it +** started putting files with the "sqlite" name in the c:/temp folder. +** This annoyed many windows users. Those users would then do a +** Google search for "sqlite", find the telephone numbers of the +** developers and call to wake them up at night and complain. +** For this reason, the default name prefix is changed to be "sqlite" +** spelled backwards. So the temp files are still identified, but +** anybody smart enough to figure out the code is also likely smart +** enough to know that calling the developer will not help get rid +** of the file. +*/ +#ifndef SQLITE_TEMP_FILE_PREFIX +# define SQLITE_TEMP_FILE_PREFIX "etilqs_" +#endif + +/* +** The following values may be passed as the second argument to +** sqlite3OsLock(). The various locks exhibit the following semantics: +** +** SHARED: Any number of processes may hold a SHARED lock simultaneously. +** RESERVED: A single process may hold a RESERVED lock on a file at +** any time. Other processes may hold and obtain new SHARED locks. +** PENDING: A single process may hold a PENDING lock on a file at +** any one time. Existing SHARED locks may persist, but no new +** SHARED locks may be obtained by other processes. +** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. +** +** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a +** process that requests an EXCLUSIVE lock may actually obtain a PENDING +** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to +** sqlite3OsLock(). +*/ +#define NO_LOCK 0 +#define SHARED_LOCK 1 +#define RESERVED_LOCK 2 +#define PENDING_LOCK 3 +#define EXCLUSIVE_LOCK 4 + +/* +** File Locking Notes: (Mostly about windows but also some info for Unix) +** +** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because +** those functions are not available. So we use only LockFile() and +** UnlockFile(). +** +** LockFile() prevents not just writing but also reading by other processes. +** A SHARED_LOCK is obtained by locking a single randomly-chosen +** byte out of a specific range of bytes. The lock byte is obtained at +** random so two separate readers can probably access the file at the +** same time, unless they are unlucky and choose the same lock byte. +** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. +** There can only be one writer. A RESERVED_LOCK is obtained by locking +** a single byte of the file that is designated as the reserved lock byte. +** A PENDING_LOCK is obtained by locking a designated byte different from +** the RESERVED_LOCK byte. +** +** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, +** which means we can use reader/writer locks. When reader/writer locks +** are used, the lock is placed on the same range of bytes that is used +** for probabilistic locking in Win95/98/ME. Hence, the locking scheme +** will support two or more Win95 readers or two or more WinNT readers. +** But a single Win95 reader will lock out all WinNT readers and a single +** WinNT reader will lock out all other Win95 readers. +** +** The following #defines specify the range of bytes used for locking. +** SHARED_SIZE is the number of bytes available in the pool from which +** a random byte is selected for a shared lock. The pool of bytes for +** shared locks begins at SHARED_FIRST. +** +** The same locking strategy and +** byte ranges are used for Unix. This leaves open the possiblity of having +** clients on win95, winNT, and unix all talking to the same shared file +** and all locking correctly. To do so would require that samba (or whatever +** tool is being used for file sharing) implements locks correctly between +** windows and unix. I'm guessing that isn't likely to happen, but by +** using the same locking range we are at least open to the possibility. +** +** Locking in windows is manditory. For this reason, we cannot store +** actual data in the bytes used for locking. The pager never allocates +** the pages involved in locking therefore. SHARED_SIZE is selected so +** that all locks will fit on a single page even at the minimum page size. +** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE +** is set high so that we don't have to allocate an unused page except +** for very large databases. But one should test the page skipping logic +** by setting PENDING_BYTE low and running the entire regression suite. +** +** Changing the value of PENDING_BYTE results in a subtly incompatible +** file format. Depending on how it is changed, you might not notice +** the incompatibility right away, even running a full regression test. +** The default location of PENDING_BYTE is the first byte past the +** 1GB boundary. +** +*/ +#ifdef SQLITE_OMIT_WSD +# define PENDING_BYTE (0x40000000) +#else +# define PENDING_BYTE sqlite3PendingByte +#endif +#define RESERVED_BYTE (PENDING_BYTE+1) +#define SHARED_FIRST (PENDING_BYTE+2) +#define SHARED_SIZE 510 + +/* +** Wrapper around OS specific sqlite3_os_init() function. +*/ +SQLITE_PRIVATE int sqlite3OsInit(void); + +/* +** Functions for accessing sqlite3_file methods +*/ +SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file*); +SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); +SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); +SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); +SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); +SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); +SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); +SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*); +#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 +SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); +SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int); +SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int); + + +/* +** Functions for accessing sqlite3_vfs methods +*/ +SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); +SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); +SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); +SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); +#ifndef SQLITE_OMIT_LOAD_EXTENSION +SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); +SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); +SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); +SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); +#endif /* SQLITE_OMIT_LOAD_EXTENSION */ +SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); +SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); +SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*); + +/* +** Convenience functions for opening and closing files using +** sqlite3_malloc() to obtain space for the file-handle structure. +*/ +SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); +SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *); + +#endif /* _SQLITE_OS_H_ */ + +/************** End of os.h **************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include mutex.h in the middle of sqliteInt.h *****************/ +/************** Begin file mutex.h *******************************************/ +/* +** 2007 August 28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains the common header for all mutex implementations. +** The sqliteInt.h header #includes this file so that it is available +** to all source files. We break it out in an effort to keep the code +** better organized. +** +** NOTE: source files should *not* #include this header file directly. +** Source files should #include the sqliteInt.h file and let that file +** include this one indirectly. +*/ + + +/* +** Figure out what version of the code to use. The choices are +** +** SQLITE_MUTEX_OMIT No mutex logic. Not even stubs. The +** mutexes implemention cannot be overridden +** at start-time. +** +** SQLITE_MUTEX_NOOP For single-threaded applications. No +** mutual exclusion is provided. But this +** implementation can be overridden at +** start-time. +** +** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix. +** +** SQLITE_MUTEX_W32 For multi-threaded applications on Win32. +*/ +#if !SQLITE_THREADSAFE +# define SQLITE_MUTEX_OMIT +#endif +#if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP) +# if SQLITE_OS_UNIX +# define SQLITE_MUTEX_PTHREADS +# elif SQLITE_OS_WIN +# define SQLITE_MUTEX_W32 +# else +# define SQLITE_MUTEX_NOOP +# endif +#endif + +#ifdef SQLITE_MUTEX_OMIT +/* +** If this is a no-op implementation, implement everything as macros. +*/ +#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) +#define sqlite3_mutex_free(X) +#define sqlite3_mutex_enter(X) +#define sqlite3_mutex_try(X) SQLITE_OK +#define sqlite3_mutex_leave(X) +#define sqlite3_mutex_held(X) ((void)(X),1) +#define sqlite3_mutex_notheld(X) ((void)(X),1) +#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) +#define sqlite3MutexInit() SQLITE_OK +#define sqlite3MutexEnd() +#define MUTEX_LOGIC(X) +#else +#define MUTEX_LOGIC(X) X +#endif /* defined(SQLITE_MUTEX_OMIT) */ + +/************** End of mutex.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + + +/* +** Each database file to be accessed by the system is an instance +** of the following structure. There are normally two of these structures +** in the sqlite.aDb[] array. aDb[0] is the main database file and +** aDb[1] is the database file used to hold temporary tables. Additional +** databases may be attached. +*/ +struct Db { + char *zName; /* Name of this database */ + Btree *pBt; /* The B*Tree structure for this database file */ + u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ + u8 safety_level; /* How aggressive at syncing data to disk */ + Schema *pSchema; /* Pointer to database schema (possibly shared) */ +}; + +/* +** An instance of the following structure stores a database schema. +** +** Most Schema objects are associated with a Btree. The exception is +** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. +** In shared cache mode, a single Schema object can be shared by multiple +** Btrees that refer to the same underlying BtShared object. +** +** Schema objects are automatically deallocated when the last Btree that +** references them is destroyed. The TEMP Schema is manually freed by +** sqlite3_close(). +* +** A thread must be holding a mutex on the corresponding Btree in order +** to access Schema content. This implies that the thread must also be +** holding a mutex on the sqlite3 connection pointer that owns the Btree. +** For a TEMP Schema, only the connection mutex is required. +*/ +struct Schema { + int schema_cookie; /* Database schema version number for this file */ + int iGeneration; /* Generation counter. Incremented with each change */ + Hash tblHash; /* All tables indexed by name */ + Hash idxHash; /* All (named) indices indexed by name */ + Hash trigHash; /* All triggers indexed by name */ + Hash fkeyHash; /* All foreign keys by referenced table name */ + Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ + u8 file_format; /* Schema format version for this file */ + u8 enc; /* Text encoding used by this database */ + u16 flags; /* Flags associated with this schema */ + int cache_size; /* Number of pages to use in the cache */ +}; + +/* +** These macros can be used to test, set, or clear bits in the +** Db.pSchema->flags field. +*/ +#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P)) +#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0) +#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P) +#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P) + +/* +** Allowed values for the DB.pSchema->flags field. +** +** The DB_SchemaLoaded flag is set after the database schema has been +** read into internal hash tables. +** +** DB_UnresetViews means that one or more views have column names that +** have been filled out. If the schema changes, these column names might +** changes and so the view will need to be reset. +*/ +#define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ +#define DB_UnresetViews 0x0002 /* Some views have defined column names */ +#define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */ + +/* +** The number of different kinds of things that can be limited +** using the sqlite3_limit() interface. +*/ +#define SQLITE_N_LIMIT (SQLITE_LIMIT_TRIGGER_DEPTH+1) + +/* +** Lookaside malloc is a set of fixed-size buffers that can be used +** to satisfy small transient memory allocation requests for objects +** associated with a particular database connection. The use of +** lookaside malloc provides a significant performance enhancement +** (approx 10%) by avoiding numerous malloc/free requests while parsing +** SQL statements. +** +** The Lookaside structure holds configuration information about the +** lookaside malloc subsystem. Each available memory allocation in +** the lookaside subsystem is stored on a linked list of LookasideSlot +** objects. +** +** Lookaside allocations are only allowed for objects that are associated +** with a particular database connection. Hence, schema information cannot +** be stored in lookaside because in shared cache mode the schema information +** is shared by multiple database connections. Therefore, while parsing +** schema information, the Lookaside.bEnabled flag is cleared so that +** lookaside allocations are not used to construct the schema objects. +*/ +struct Lookaside { + u16 sz; /* Size of each buffer in bytes */ + u8 bEnabled; /* False to disable new lookaside allocations */ + u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ + int nOut; /* Number of buffers currently checked out */ + int mxOut; /* Highwater mark for nOut */ + int anStat[3]; /* 0: hits. 1: size misses. 2: full misses */ + LookasideSlot *pFree; /* List of available buffers */ + void *pStart; /* First byte of available memory space */ + void *pEnd; /* First byte past end of available space */ +}; +struct LookasideSlot { + LookasideSlot *pNext; /* Next buffer in the list of free buffers */ +}; + +/* +** A hash table for function definitions. +** +** Hash each FuncDef structure into one of the FuncDefHash.a[] slots. +** Collisions are on the FuncDef.pHash chain. +*/ +struct FuncDefHash { + FuncDef *a[23]; /* Hash table for functions */ +}; + +/* +** Each database connection is an instance of the following structure. +*/ +struct sqlite3 { + sqlite3_vfs *pVfs; /* OS Interface */ + struct Vdbe *pVdbe; /* List of active virtual machines */ + CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ + sqlite3_mutex *mutex; /* Connection mutex */ + Db *aDb; /* All backends */ + int nDb; /* Number of backends currently in use */ + int flags; /* Miscellaneous flags. See below */ + i64 lastRowid; /* ROWID of most recent insert (see above) */ + unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ + int errCode; /* Most recent error code (SQLITE_*) */ + int errMask; /* & result codes with this before returning */ + u16 dbOptFlags; /* Flags to enable/disable optimizations */ + u8 autoCommit; /* The auto-commit flag. */ + u8 temp_store; /* 1: file 2: memory 0: default */ + u8 mallocFailed; /* True if we have seen a malloc failure */ + u8 dfltLockMode; /* Default locking-mode for attached dbs */ + signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ + u8 suppressErr; /* Do not issue error messages if true */ + u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ + u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ + int nextPagesize; /* Pagesize after VACUUM if >0 */ + u32 magic; /* Magic number for detect library misuse */ + int nChange; /* Value returned by sqlite3_changes() */ + int nTotalChange; /* Value returned by sqlite3_total_changes() */ + int aLimit[SQLITE_N_LIMIT]; /* Limits */ + struct sqlite3InitInfo { /* Information used during initialization */ + int newTnum; /* Rootpage of table being initialized */ + u8 iDb; /* Which db file is being initialized */ + u8 busy; /* TRUE if currently initializing */ + u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ + } init; + int activeVdbeCnt; /* Number of VDBEs currently executing */ + int writeVdbeCnt; /* Number of active VDBEs that are writing */ + int vdbeExecCnt; /* Number of nested calls to VdbeExec() */ + int nExtension; /* Number of loaded extensions */ + void **aExtension; /* Array of shared library handles */ + void (*xTrace)(void*,const char*); /* Trace function */ + void *pTraceArg; /* Argument to the trace function */ + void (*xProfile)(void*,const char*,u64); /* Profiling function */ + void *pProfileArg; /* Argument to profile function */ + void *pCommitArg; /* Argument to xCommitCallback() */ + int (*xCommitCallback)(void*); /* Invoked at every commit. */ + void *pRollbackArg; /* Argument to xRollbackCallback() */ + void (*xRollbackCallback)(void*); /* Invoked at every commit. */ + void *pUpdateArg; + void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); +#ifndef SQLITE_OMIT_WAL + int (*xWalCallback)(void *, sqlite3 *, const char *, int); + void *pWalArg; +#endif + void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); + void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); + void *pCollNeededArg; + sqlite3_value *pErr; /* Most recent error message */ + char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ + char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ + union { + volatile int isInterrupted; /* True if sqlite3_interrupt has been called */ + double notUsed1; /* Spacer */ + } u1; + Lookaside lookaside; /* Lookaside malloc configuration */ +#ifndef SQLITE_OMIT_AUTHORIZATION + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); + /* Access authorization function */ + void *pAuthArg; /* 1st argument to the access auth function */ +#endif +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + int (*xProgress)(void *); /* The progress callback */ + void *pProgressArg; /* Argument to the progress callback */ + int nProgressOps; /* Number of opcodes for progress callback */ +#endif +#ifndef SQLITE_OMIT_VIRTUALTABLE + int nVTrans; /* Allocated size of aVTrans */ + Hash aModule; /* populated by sqlite3_create_module() */ + VtabCtx *pVtabCtx; /* Context for active vtab connect/create */ + VTable **aVTrans; /* Virtual tables with open transactions */ + VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ +#endif + FuncDefHash aFunc; /* Hash table of connection functions */ + Hash aCollSeq; /* All collating sequences */ + BusyHandler busyHandler; /* Busy callback */ + Db aDbStatic[2]; /* Static space for the 2 default backends */ + Savepoint *pSavepoint; /* List of active savepoints */ + int busyTimeout; /* Busy handler timeout, in msec */ + int nSavepoint; /* Number of non-transaction savepoints */ + int nStatement; /* Number of nested statement-transactions */ + i64 nDeferredCons; /* Net deferred constraints this transaction. */ + int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ + +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + /* The following variables are all protected by the STATIC_MASTER + ** mutex, not by sqlite3.mutex. They are used by code in notify.c. + ** + ** When X.pUnlockConnection==Y, that means that X is waiting for Y to + ** unlock so that it can proceed. + ** + ** When X.pBlockingConnection==Y, that means that something that X tried + ** tried to do recently failed with an SQLITE_LOCKED error due to locks + ** held by Y. + */ + sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ + sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ + void *pUnlockArg; /* Argument to xUnlockNotify */ + void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ + sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ +#endif +}; + +/* +** A macro to discover the encoding of a database. +*/ +#define ENC(db) ((db)->aDb[0].pSchema->enc) + +/* +** Possible values for the sqlite3.flags. +*/ +#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ +#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */ +#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */ +#define SQLITE_ShortColNames 0x00000008 /* Show short columns names */ +#define SQLITE_CountRows 0x00000010 /* Count rows changed by INSERT, */ + /* DELETE, or UPDATE and return */ + /* the count using a callback. */ +#define SQLITE_NullCallback 0x00000020 /* Invoke the callback once if the */ + /* result set is empty */ +#define SQLITE_SqlTrace 0x00000040 /* Debug print SQL as it executes */ +#define SQLITE_VdbeListing 0x00000080 /* Debug listings of VDBE programs */ +#define SQLITE_WriteSchema 0x00000100 /* OK to update SQLITE_MASTER */ +#define SQLITE_VdbeAddopTrace 0x00000200 /* Trace sqlite3VdbeAddOp() calls */ +#define SQLITE_IgnoreChecks 0x00000400 /* Do not enforce check constraints */ +#define SQLITE_ReadUncommitted 0x0000800 /* For shared-cache mode */ +#define SQLITE_LegacyFileFmt 0x00001000 /* Create new databases in format 1 */ +#define SQLITE_FullFSync 0x00002000 /* Use full fsync on the backend */ +#define SQLITE_CkptFullFSync 0x00004000 /* Use full fsync for checkpoint */ +#define SQLITE_RecoveryMode 0x00008000 /* Ignore schema errors */ +#define SQLITE_ReverseOrder 0x00010000 /* Reverse unordered SELECTs */ +#define SQLITE_RecTriggers 0x00020000 /* Enable recursive triggers */ +#define SQLITE_ForeignKeys 0x00040000 /* Enforce foreign key constraints */ +#define SQLITE_AutoIndex 0x00080000 /* Enable automatic indexes */ +#define SQLITE_PreferBuiltin 0x00100000 /* Preference to built-in funcs */ +#define SQLITE_LoadExtension 0x00200000 /* Enable load_extension */ +#define SQLITE_EnableTrigger 0x00400000 /* True to enable triggers */ + +/* +** Bits of the sqlite3.dbOptFlags field that are used by the +** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to +** selectively disable various optimizations. +*/ +#define SQLITE_QueryFlattener 0x0001 /* Query flattening */ +#define SQLITE_ColumnCache 0x0002 /* Column cache */ +#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ +#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ +#define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ +#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ +#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ +#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ +#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ +#define SQLITE_Transitive 0x0200 /* Transitive constraints */ +#define SQLITE_AllOpts 0xffff /* All optimizations */ + +/* +** Macros for testing whether or not optimizations are enabled or disabled. +*/ +#ifndef SQLITE_OMIT_BUILTIN_TEST +#define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) +#define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0) +#else +#define OptimizationDisabled(db, mask) 0 +#define OptimizationEnabled(db, mask) 1 +#endif + +/* +** Possible values for the sqlite.magic field. +** The numbers are obtained at random and have no special meaning, other +** than being distinct from one another. +*/ +#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ +#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ +#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ +#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ +#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ +#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */ + +/* +** Each SQL function is defined by an instance of the following +** structure. A pointer to this structure is stored in the sqlite.aFunc +** hash table. When multiple functions have the same name, the hash table +** points to a linked list of these structures. +*/ +struct FuncDef { + i16 nArg; /* Number of arguments. -1 means unlimited */ + u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */ + u8 flags; /* Some combination of SQLITE_FUNC_* */ + void *pUserData; /* User data parameter */ + FuncDef *pNext; /* Next function with same name */ + void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ + void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ + void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ + char *zName; /* SQL name of the function. */ + FuncDef *pHash; /* Next with a different name but the same hash */ + FuncDestructor *pDestructor; /* Reference counted destructor function */ +}; + +/* +** This structure encapsulates a user-function destructor callback (as +** configured using create_function_v2()) and a reference counter. When +** create_function_v2() is called to create a function with a destructor, +** a single object of this type is allocated. FuncDestructor.nRef is set to +** the number of FuncDef objects created (either 1 or 3, depending on whether +** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor +** member of each of the new FuncDef objects is set to point to the allocated +** FuncDestructor. +** +** Thereafter, when one of the FuncDef objects is deleted, the reference +** count on this object is decremented. When it reaches 0, the destructor +** is invoked and the FuncDestructor structure freed. +*/ +struct FuncDestructor { + int nRef; + void (*xDestroy)(void *); + void *pUserData; +}; + +/* +** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF +** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There +** are assert() statements in the code to verify this. +*/ +#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ +#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ +#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ +#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ +#define SQLITE_FUNC_COUNT 0x10 /* Built-in count(*) aggregate */ +#define SQLITE_FUNC_COALESCE 0x20 /* Built-in coalesce() or ifnull() function */ +#define SQLITE_FUNC_LENGTH 0x40 /* Built-in length() function */ +#define SQLITE_FUNC_TYPEOF 0x80 /* Built-in typeof() function */ + +/* +** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are +** used to create the initializers for the FuncDef structures. +** +** FUNCTION(zName, nArg, iArg, bNC, xFunc) +** Used to create a scalar function definition of a function zName +** implemented by C function xFunc that accepts nArg arguments. The +** value passed as iArg is cast to a (void*) and made available +** as the user-data (sqlite3_user_data()) for the function. If +** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set. +** +** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal) +** Used to create an aggregate function definition implemented by +** the C functions xStep and xFinal. The first four parameters +** are interpreted in the same way as the first 4 parameters to +** FUNCTION(). +** +** LIKEFUNC(zName, nArg, pArg, flags) +** Used to create a scalar function definition of a function zName +** that accepts nArg arguments and is implemented by a call to C +** function likeFunc. Argument pArg is cast to a (void *) and made +** available as the function user-data (sqlite3_user_data()). The +** FuncDef.flags variable is set to the value passed as the flags +** parameter. +*/ +#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ + {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL), \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} +#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ + {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} +#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ + {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ + pArg, 0, xFunc, 0, 0, #zName, 0, 0} +#define LIKEFUNC(zName, nArg, arg, flags) \ + {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} +#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ + {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \ + SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0} + +/* +** All current savepoints are stored in a linked list starting at +** sqlite3.pSavepoint. The first element in the list is the most recently +** opened savepoint. Savepoints are added to the list by the vdbe +** OP_Savepoint instruction. +*/ +struct Savepoint { + char *zName; /* Savepoint name (nul-terminated) */ + i64 nDeferredCons; /* Number of deferred fk violations */ + Savepoint *pNext; /* Parent savepoint (if any) */ +}; + +/* +** The following are used as the second parameter to sqlite3Savepoint(), +** and as the P1 argument to the OP_Savepoint instruction. +*/ +#define SAVEPOINT_BEGIN 0 +#define SAVEPOINT_RELEASE 1 +#define SAVEPOINT_ROLLBACK 2 + + +/* +** Each SQLite module (virtual table definition) is defined by an +** instance of the following structure, stored in the sqlite3.aModule +** hash table. +*/ +struct Module { + const sqlite3_module *pModule; /* Callback pointers */ + const char *zName; /* Name passed to create_module() */ + void *pAux; /* pAux passed to create_module() */ + void (*xDestroy)(void *); /* Module destructor function */ +}; + +/* +** information about each column of an SQL table is held in an instance +** of this structure. +*/ +struct Column { + char *zName; /* Name of this column */ + Expr *pDflt; /* Default value of this column */ + char *zDflt; /* Original text of the default value */ + char *zType; /* Data type for this column */ + char *zColl; /* Collating sequence. If NULL, use the default */ + u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ + char affinity; /* One of the SQLITE_AFF_... values */ + u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ +}; + +/* Allowed values for Column.colFlags: +*/ +#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */ +#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */ + +/* +** A "Collating Sequence" is defined by an instance of the following +** structure. Conceptually, a collating sequence consists of a name and +** a comparison routine that defines the order of that sequence. +** +** If CollSeq.xCmp is NULL, it means that the +** collating sequence is undefined. Indices built on an undefined +** collating sequence may not be read or written. +*/ +struct CollSeq { + char *zName; /* Name of the collating sequence, UTF-8 encoded */ + u8 enc; /* Text encoding handled by xCmp() */ + void *pUser; /* First argument to xCmp() */ + int (*xCmp)(void*,int, const void*, int, const void*); + void (*xDel)(void*); /* Destructor for pUser */ +}; + +/* +** A sort order can be either ASC or DESC. +*/ +#define SQLITE_SO_ASC 0 /* Sort in ascending order */ +#define SQLITE_SO_DESC 1 /* Sort in ascending order */ + +/* +** Column affinity types. +** +** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and +** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve +** the speed a little by numbering the values consecutively. +** +** But rather than start with 0 or 1, we begin with 'a'. That way, +** when multiple affinity types are concatenated into a string and +** used as the P4 operand, they will be more readable. +** +** Note also that the numeric types are grouped together so that testing +** for a numeric type is a single comparison. +*/ +#define SQLITE_AFF_TEXT 'a' +#define SQLITE_AFF_NONE 'b' +#define SQLITE_AFF_NUMERIC 'c' +#define SQLITE_AFF_INTEGER 'd' +#define SQLITE_AFF_REAL 'e' + +#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) + +/* +** The SQLITE_AFF_MASK values masks off the significant bits of an +** affinity value. +*/ +#define SQLITE_AFF_MASK 0x67 + +/* +** Additional bit values that can be ORed with an affinity without +** changing the affinity. +*/ +#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ +#define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ +#define SQLITE_NULLEQ 0x80 /* NULL=NULL */ + +/* +** An object of this type is created for each virtual table present in +** the database schema. +** +** If the database schema is shared, then there is one instance of this +** structure for each database connection (sqlite3*) that uses the shared +** schema. This is because each database connection requires its own unique +** instance of the sqlite3_vtab* handle used to access the virtual table +** implementation. sqlite3_vtab* handles can not be shared between +** database connections, even when the rest of the in-memory database +** schema is shared, as the implementation often stores the database +** connection handle passed to it via the xConnect() or xCreate() method +** during initialization internally. This database connection handle may +** then be used by the virtual table implementation to access real tables +** within the database. So that they appear as part of the callers +** transaction, these accesses need to be made via the same database +** connection as that used to execute SQL operations on the virtual table. +** +** All VTable objects that correspond to a single table in a shared +** database schema are initially stored in a linked-list pointed to by +** the Table.pVTable member variable of the corresponding Table object. +** When an sqlite3_prepare() operation is required to access the virtual +** table, it searches the list for the VTable that corresponds to the +** database connection doing the preparing so as to use the correct +** sqlite3_vtab* handle in the compiled query. +** +** When an in-memory Table object is deleted (for example when the +** schema is being reloaded for some reason), the VTable objects are not +** deleted and the sqlite3_vtab* handles are not xDisconnect()ed +** immediately. Instead, they are moved from the Table.pVTable list to +** another linked list headed by the sqlite3.pDisconnect member of the +** corresponding sqlite3 structure. They are then deleted/xDisconnected +** next time a statement is prepared using said sqlite3*. This is done +** to avoid deadlock issues involving multiple sqlite3.mutex mutexes. +** Refer to comments above function sqlite3VtabUnlockList() for an +** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect +** list without holding the corresponding sqlite3.mutex mutex. +** +** The memory for objects of this type is always allocated by +** sqlite3DbMalloc(), using the connection handle stored in VTable.db as +** the first argument. +*/ +struct VTable { + sqlite3 *db; /* Database connection associated with this table */ + Module *pMod; /* Pointer to module implementation */ + sqlite3_vtab *pVtab; /* Pointer to vtab instance */ + int nRef; /* Number of pointers to this structure */ + u8 bConstraint; /* True if constraints are supported */ + int iSavepoint; /* Depth of the SAVEPOINT stack */ + VTable *pNext; /* Next in linked list (see above) */ +}; + +/* +** Each SQL table is represented in memory by an instance of the +** following structure. +** +** Table.zName is the name of the table. The case of the original +** CREATE TABLE statement is stored, but case is not significant for +** comparisons. +** +** Table.nCol is the number of columns in this table. Table.aCol is a +** pointer to an array of Column structures, one for each column. +** +** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of +** the column that is that key. Otherwise Table.iPKey is negative. Note +** that the datatype of the PRIMARY KEY must be INTEGER for this field to +** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of +** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid +** is generated for each row of the table. TF_HasPrimaryKey is set if +** the table has any PRIMARY KEY, INTEGER or otherwise. +** +** Table.tnum is the page number for the root BTree page of the table in the +** database file. If Table.iDb is the index of the database table backend +** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that +** holds temporary tables and indices. If TF_Ephemeral is set +** then the table is stored in a file that is automatically deleted +** when the VDBE cursor to the table is closed. In this case Table.tnum +** refers VDBE cursor number that holds the table open, not to the root +** page number. Transient tables are used to hold the results of a +** sub-query that appears instead of a real table name in the FROM clause +** of a SELECT statement. +*/ +struct Table { + char *zName; /* Name of the table or view */ + Column *aCol; /* Information about each column */ + Index *pIndex; /* List of SQL indexes on this table. */ + Select *pSelect; /* NULL for tables. Points to definition if a view. */ + FKey *pFKey; /* Linked list of all foreign keys in this table */ + char *zColAff; /* String defining the affinity of each column */ +#ifndef SQLITE_OMIT_CHECK + ExprList *pCheck; /* All CHECK constraints */ +#endif + tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ + int tnum; /* Root BTree node for this table (see note above) */ + i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */ + i16 nCol; /* Number of columns in this table */ + u16 nRef; /* Number of pointers to this Table */ + u8 tabFlags; /* Mask of TF_* values */ + u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ +#ifndef SQLITE_OMIT_ALTERTABLE + int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ +#endif +#ifndef SQLITE_OMIT_VIRTUALTABLE + int nModuleArg; /* Number of arguments to the module */ + char **azModuleArg; /* Text of all module args. [0] is module name */ + VTable *pVTable; /* List of VTable objects. */ +#endif + Trigger *pTrigger; /* List of triggers stored in pSchema */ + Schema *pSchema; /* Schema that contains this table */ + Table *pNextZombie; /* Next on the Parse.pZombieTab list */ +}; + +/* +** Allowed values for Tabe.tabFlags. +*/ +#define TF_Readonly 0x01 /* Read-only system table */ +#define TF_Ephemeral 0x02 /* An ephemeral table */ +#define TF_HasPrimaryKey 0x04 /* Table has a primary key */ +#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ +#define TF_Virtual 0x10 /* Is a virtual table */ + + +/* +** Test to see whether or not a table is a virtual table. This is +** done as a macro so that it will be optimized out when virtual +** table support is omitted from the build. +*/ +#ifndef SQLITE_OMIT_VIRTUALTABLE +# define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0) +# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0) +#else +# define IsVirtual(X) 0 +# define IsHiddenColumn(X) 0 +#endif + +/* +** Each foreign key constraint is an instance of the following structure. +** +** A foreign key is associated with two tables. The "from" table is +** the table that contains the REFERENCES clause that creates the foreign +** key. The "to" table is the table that is named in the REFERENCES clause. +** Consider this example: +** +** CREATE TABLE ex1( +** a INTEGER PRIMARY KEY, +** b INTEGER CONSTRAINT fk1 REFERENCES ex2(x) +** ); +** +** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2". +** +** Each REFERENCES clause generates an instance of the following structure +** which is attached to the from-table. The to-table need not exist when +** the from-table is created. The existence of the to-table is not checked. +*/ +struct FKey { + Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */ + FKey *pNextFrom; /* Next foreign key in pFrom */ + char *zTo; /* Name of table that the key points to (aka: Parent) */ + FKey *pNextTo; /* Next foreign key on table named zTo */ + FKey *pPrevTo; /* Previous foreign key on table named zTo */ + int nCol; /* Number of columns in this key */ + /* EV: R-30323-21917 */ + u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ + u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ + Trigger *apTrigger[2]; /* Triggers for aAction[] actions */ + struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ + int iFrom; /* Index of column in pFrom */ + char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ + } aCol[1]; /* One entry for each of nCol column s */ +}; + +/* +** SQLite supports many different ways to resolve a constraint +** error. ROLLBACK processing means that a constraint violation +** causes the operation in process to fail and for the current transaction +** to be rolled back. ABORT processing means the operation in process +** fails and any prior changes from that one operation are backed out, +** but the transaction is not rolled back. FAIL processing means that +** the operation in progress stops and returns an error code. But prior +** changes due to the same operation are not backed out and no rollback +** occurs. IGNORE means that the particular row that caused the constraint +** error is not inserted or updated. Processing continues and no error +** is returned. REPLACE means that preexisting database rows that caused +** a UNIQUE constraint violation are removed so that the new insert or +** update can proceed. Processing continues and no error is reported. +** +** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. +** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the +** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign +** key is set to NULL. CASCADE means that a DELETE or UPDATE of the +** referenced table row is propagated into the row that holds the +** foreign key. +** +** The following symbolic values are used to record which type +** of action to take. +*/ +#define OE_None 0 /* There is no constraint to check */ +#define OE_Rollback 1 /* Fail the operation and rollback the transaction */ +#define OE_Abort 2 /* Back out changes but do no rollback transaction */ +#define OE_Fail 3 /* Stop the operation but leave all prior changes */ +#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ +#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ + +#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */ +#define OE_SetNull 7 /* Set the foreign key value to NULL */ +#define OE_SetDflt 8 /* Set the foreign key value to its default */ +#define OE_Cascade 9 /* Cascade the changes */ + +#define OE_Default 99 /* Do whatever the default action is */ + + +/* +** An instance of the following structure is passed as the first +** argument to sqlite3VdbeKeyCompare and is used to control the +** comparison of the two index keys. +*/ +struct KeyInfo { + sqlite3 *db; /* The database connection */ + u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ + u16 nField; /* Number of entries in aColl[] */ + u8 *aSortOrder; /* Sort order for each column. May be NULL */ + CollSeq *aColl[1]; /* Collating sequence for each term of the key */ +}; + +/* +** An instance of the following structure holds information about a +** single index record that has already been parsed out into individual +** values. +** +** A record is an object that contains one or more fields of data. +** Records are used to store the content of a table row and to store +** the key of an index. A blob encoding of a record is created by +** the OP_MakeRecord opcode of the VDBE and is disassembled by the +** OP_Column opcode. +** +** This structure holds a record that has already been disassembled +** into its constituent fields. +*/ +struct UnpackedRecord { + KeyInfo *pKeyInfo; /* Collation and sort-order information */ + u16 nField; /* Number of entries in apMem[] */ + u8 flags; /* Boolean settings. UNPACKED_... below */ + i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */ + Mem *aMem; /* Values */ +}; + +/* +** Allowed values of UnpackedRecord.flags +*/ +#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */ +#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */ +#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */ + +/* +** Each SQL index is represented in memory by an +** instance of the following structure. +** +** The columns of the table that are to be indexed are described +** by the aiColumn[] field of this structure. For example, suppose +** we have the following table and index: +** +** CREATE TABLE Ex1(c1 int, c2 int, c3 text); +** CREATE INDEX Ex2 ON Ex1(c3,c1); +** +** In the Table structure describing Ex1, nCol==3 because there are +** three columns in the table. In the Index structure describing +** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed. +** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the +** first column to be indexed (c3) has an index of 2 in Ex1.aCol[]. +** The second column to be indexed (c1) has an index of 0 in +** Ex1.aCol[], hence Ex2.aiColumn[1]==0. +** +** The Index.onError field determines whether or not the indexed columns +** must be unique and what to do if they are not. When Index.onError=OE_None, +** it means this is not a unique index. Otherwise it is a unique index +** and the value of Index.onError indicate the which conflict resolution +** algorithm to employ whenever an attempt is made to insert a non-unique +** element. +*/ +struct Index { + char *zName; /* Name of this index */ + int *aiColumn; /* Which columns are used by this index. 1st is 0 */ + tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ + Table *pTable; /* The SQL table being indexed */ + char *zColAff; /* String defining the affinity of each column */ + Index *pNext; /* The next index associated with the same table */ + Schema *pSchema; /* Schema containing this index */ + u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ + char **azColl; /* Array of collation sequence names for index */ + int tnum; /* DB Page containing root of this index */ + u16 nColumn; /* Number of columns in table used by this index */ + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ + unsigned bUnordered:1; /* Use this index for == or IN queries only */ +#ifdef SQLITE_ENABLE_STAT3 + int nSample; /* Number of elements in aSample[] */ + tRowcnt avgEq; /* Average nEq value for key values not in aSample */ + IndexSample *aSample; /* Samples of the left-most key */ +#endif +}; + +/* +** Each sample stored in the sqlite_stat3 table is represented in memory +** using a structure of this type. See documentation at the top of the +** analyze.c source file for additional information. +*/ +struct IndexSample { + union { + char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ + double r; /* Value if eType is SQLITE_FLOAT */ + i64 i; /* Value if eType is SQLITE_INTEGER */ + } u; + u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */ + int nByte; /* Size in byte of text or blob. */ + tRowcnt nEq; /* Est. number of rows where the key equals this sample */ + tRowcnt nLt; /* Est. number of rows where key is less than this sample */ + tRowcnt nDLt; /* Est. number of distinct keys less than this sample */ +}; + +/* +** Each token coming out of the lexer is an instance of +** this structure. Tokens are also used as part of an expression. +** +** Note if Token.z==0 then Token.dyn and Token.n are undefined and +** may contain random values. Do not make any assumptions about Token.dyn +** and Token.n when Token.z==0. +*/ +struct Token { + const char *z; /* Text of the token. Not NULL-terminated! */ + unsigned int n; /* Number of characters in this token */ +}; + +/* +** An instance of this structure contains information needed to generate +** code for a SELECT that contains aggregate functions. +** +** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a +** pointer to this structure. The Expr.iColumn field is the index in +** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate +** code for that node. +** +** AggInfo.pGroupBy and AggInfo.aFunc.pExpr point to fields within the +** original Select structure that describes the SELECT statement. These +** fields do not need to be freed when deallocating the AggInfo structure. +*/ +struct AggInfo { + u8 directMode; /* Direct rendering mode means take data directly + ** from source tables rather than from accumulators */ + u8 useSortingIdx; /* In direct mode, reference the sorting index rather + ** than the source table */ + int sortingIdx; /* Cursor number of the sorting index */ + int sortingIdxPTab; /* Cursor number of pseudo-table */ + int nSortingColumn; /* Number of columns in the sorting index */ + ExprList *pGroupBy; /* The group by clause */ + struct AggInfo_col { /* For each column used in source tables */ + Table *pTab; /* Source table */ + int iTable; /* Cursor number of the source table */ + int iColumn; /* Column number within the source table */ + int iSorterColumn; /* Column number in the sorting index */ + int iMem; /* Memory location that acts as accumulator */ + Expr *pExpr; /* The original expression */ + } *aCol; + int nColumn; /* Number of used entries in aCol[] */ + int nAccumulator; /* Number of columns that show through to the output. + ** Additional columns are used only as parameters to + ** aggregate functions */ + struct AggInfo_func { /* For each aggregate function */ + Expr *pExpr; /* Expression encoding the function */ + FuncDef *pFunc; /* The aggregate function implementation */ + int iMem; /* Memory location that acts as accumulator */ + int iDistinct; /* Ephemeral table used to enforce DISTINCT */ + } *aFunc; + int nFunc; /* Number of entries in aFunc[] */ +}; + +/* +** The datatype ynVar is a signed integer, either 16-bit or 32-bit. +** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater +** than 32767 we have to make it 32-bit. 16-bit is preferred because +** it uses less memory in the Expr object, which is a big memory user +** in systems with lots of prepared statements. And few applications +** need more than about 10 or 20 variables. But some extreme users want +** to have prepared statements with over 32767 variables, and for them +** the option is available (at compile-time). +*/ +#if SQLITE_MAX_VARIABLE_NUMBER<=32767 +typedef i16 ynVar; +#else +typedef int ynVar; +#endif + +/* +** Each node of an expression in the parse tree is an instance +** of this structure. +** +** Expr.op is the opcode. The integer parser token codes are reused +** as opcodes here. For example, the parser defines TK_GE to be an integer +** code representing the ">=" operator. This same integer code is reused +** to represent the greater-than-or-equal-to operator in the expression +** tree. +** +** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, +** or TK_STRING), then Expr.token contains the text of the SQL literal. If +** the expression is a variable (TK_VARIABLE), then Expr.token contains the +** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), +** then Expr.token contains the name of the function. +** +** Expr.pRight and Expr.pLeft are the left and right subexpressions of a +** binary operator. Either or both may be NULL. +** +** Expr.x.pList is a list of arguments if the expression is an SQL function, +** a CASE expression or an IN expression of the form " IN (, ...)". +** Expr.x.pSelect is used if the expression is a sub-select or an expression of +** the form " IN (SELECT ...)". If the EP_xIsSelect bit is set in the +** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is +** valid. +** +** An expression of the form ID or ID.ID refers to a column in a table. +** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is +** the integer cursor number of a VDBE cursor pointing to that table and +** Expr.iColumn is the column number for the specific column. If the +** expression is used as a result in an aggregate SELECT, then the +** value is also stored in the Expr.iAgg column in the aggregate so that +** it can be accessed after all aggregates are computed. +** +** If the expression is an unbound variable marker (a question mark +** character '?' in the original SQL) then the Expr.iTable holds the index +** number for that variable. +** +** If the expression is a subquery then Expr.iColumn holds an integer +** register number containing the result of the subquery. If the +** subquery gives a constant result, then iTable is -1. If the subquery +** gives a different answer at different times during statement processing +** then iTable is the address of a subroutine that computes the subquery. +** +** If the Expr is of type OP_Column, and the table it is selecting from +** is a disk table or the "old.*" pseudo-table, then pTab points to the +** corresponding table definition. +** +** ALLOCATION NOTES: +** +** Expr objects can use a lot of memory space in database schema. To +** help reduce memory requirements, sometimes an Expr object will be +** truncated. And to reduce the number of memory allocations, sometimes +** two or more Expr objects will be stored in a single memory allocation, +** together with Expr.zToken strings. +** +** If the EP_Reduced and EP_TokenOnly flags are set when +** an Expr object is truncated. When EP_Reduced is set, then all +** the child Expr objects in the Expr.pLeft and Expr.pRight subtrees +** are contained within the same memory allocation. Note, however, that +** the subtrees in Expr.x.pList or Expr.x.pSelect are always separately +** allocated, regardless of whether or not EP_Reduced is set. +*/ +struct Expr { + u8 op; /* Operation performed by this node */ + char affinity; /* The affinity of the column or 0 if not a column */ + u16 flags; /* Various flags. EP_* See below */ + union { + char *zToken; /* Token value. Zero terminated and dequoted */ + int iValue; /* Non-negative integer value if EP_IntValue */ + } u; + + /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no + ** space is allocated for the fields below this point. An attempt to + ** access them will result in a segfault or malfunction. + *********************************************************************/ + + Expr *pLeft; /* Left subnode */ + Expr *pRight; /* Right subnode */ + union { + ExprList *pList; /* Function arguments or in " IN ( IN (" \ + "" \ + ""; + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send(LoginForm, sizeof(LoginForm) - 1); + a_Connection.FinishResponse(); +} + + + + + +sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) +{ + sWebAdminPage Page; + AStringVector Split = StringSplit(a_Request.Path, "/"); + + // Find the plugin that corresponds to the requested path + AString FoundPlugin; + if (Split.size() > 1) + { + for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) + { + if ((*itr)->GetWebTitle() == Split[1]) + { + Page.Content = (*itr)->HandleWebRequest(&a_Request); + cWebPlugin * WebPlugin = *itr; + FoundPlugin = WebPlugin->GetWebTitle(); + AString TabName = WebPlugin->GetTabNameForRequest(&a_Request).first; + Page.PluginName = FoundPlugin; + Page.TabName = TabName; + break; + } + } + } + + // Return the page contents + return Page; +} + + + + + +AString cWebAdmin::GetDefaultPage(void) +{ + AString Content; + Content += "

Server Name:

"; + Content += "

" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "

"; + + Content += "

Plugins:

    "; + cPluginManager * PM = cPluginManager::Get(); + const cPluginManager::PluginMap & List = PM->GetAllPlugins(); + for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr) + { + if (itr->second == NULL) + { + continue; + } + AString VersionNum; + AppendPrintf(Content, "
  • %s V.%i
  • ", itr->second->GetName().c_str(), itr->second->GetVersion()); + } + Content += "
"; + Content += "

Players:

    "; + + cPlayerAccum PlayerAccum; + cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players + if( World != NULL ) + { + World->ForEachPlayer(PlayerAccum); + Content.append(PlayerAccum.m_Contents); + } + Content += "

"; + return Content; +} + + + + +AString cWebAdmin::GetBaseURL( const AString& a_URL ) +{ + return GetBaseURL(StringSplit(a_URL, "/")); +} + + + + + +AString cWebAdmin::GetHTMLEscapedString(const AString & a_Input) +{ + AString dst; + dst.reserve(a_Input.length()); + + // Loop over input and substitute HTML characters for their alternatives: + size_t len = a_Input.length(); + for (size_t i = 0; i < len; i++) + { + switch (a_Input[i]) + { + case '&': dst.append("&"); break; + case '\'': dst.append("'"); break; + case '"': dst.append("""); break; + case '<': dst.append("<"); break; + case '>': dst.append(">"); break; + default: + { + dst.push_back(a_Input[i]); + break; + } + } // switch (a_Input[i]) + } // for i - a_Input[] + + return dst; +} + + + + + +AString cWebAdmin::GetBaseURL(const AStringVector & a_URLSplit) +{ + AString BaseURL = "./"; + if (a_URLSplit.size() > 1) + { + for (unsigned int i = 0; i < a_URLSplit.size(); i++) + { + BaseURL += "../"; + } + BaseURL += "webadmin/"; + } + return BaseURL; +} + + + + + +void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + const AString & URL = a_Request.GetURL(); + if ( + (strncmp(URL.c_str(), "/webadmin", 9) == 0) || + (strncmp(URL.c_str(), "/~webadmin", 10) == 0) + ) + { + a_Request.SetUserData(new cWebadminRequestData(a_Request)); + return; + } + if (URL == "/") + { + // The root needs no body handler and is fully handled in the OnRequestFinished() call + return; + } + // TODO: Handle other requests +} + + + + + +void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) +{ + cRequestData * Data = (cRequestData *)(a_Request.GetUserData()); + if (Data == NULL) + { + return; + } + Data->OnBody(a_Data, a_Size); +} + + + + + +void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + const AString & URL = a_Request.GetURL(); + if ( + (strncmp(URL.c_str(), "/webadmin", 9) == 0) || + (strncmp(URL.c_str(), "/~webadmin", 10) == 0) + ) + { + HandleWebadminRequest(a_Connection, a_Request); + } + else if (URL == "/") + { + // The root needs no body handler and is fully handled in the OnRequestFinished() call + HandleRootRequest(a_Connection, a_Request); + } + else + { + // TODO: Handle other requests + } + + // Delete any request data assigned to the request: + cRequestData * Data = (cRequestData *)(a_Request.GetUserData()); + delete Data; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWebAdmin::cWebadminRequestData + +void cWebAdmin::cWebadminRequestData::OnBody(const char * a_Data, int a_Size) +{ + m_Form.Parse(a_Data, a_Size); +} + + + + diff --git a/src/WebAdmin.h b/src/WebAdmin.h new file mode 100644 index 000000000..dc6ea850e --- /dev/null +++ b/src/WebAdmin.h @@ -0,0 +1,215 @@ + +// WebAdmin.h + +// Declares the cWebAdmin class representing the admin interface over http protocol, and related services (API) + +#pragma once + +#include "OSSupport/Socket.h" +#include "LuaState.h" +#include "../iniFile/iniFile.h" +#include "HTTPServer/HTTPServer.h" +#include "HTTPServer/HTTPFormParser.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +// fwd: +class cEvent; +class cWebPlugin; + + + + + +// tolua_begin +struct HTTPFormData +{ + std::string Name; + std::string Value; + std::string Type; +} ; +// tolua_end + + + + +// tolua_begin +struct HTTPRequest +{ + typedef std::map< std::string, std::string > StringStringMap; + typedef std::map< std::string, HTTPFormData > FormDataMap; + + AString Method; + AString Path; + AString Username; + // tolua_end + + /// Parameters given in the URL, after the questionmark + StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS << + + /// Parameters posted as a part of a form - either in the URL (GET method) or in the body (POST method) + StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS << + + /// Same as PostParams + FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS << +} ; // tolua_export + + + + + +// tolua_begin +struct HTTPTemplateRequest +{ + HTTPRequest Request; +} ; +// tolua_end + + + + + +// tolua_begin +struct sWebAdminPage +{ + AString Content; + AString PluginName; + AString TabName; +}; +// tolua_end + + + + + +// tolua_begin +class cWebAdmin : + public cHTTPServer::cCallbacks +{ +public: + // tolua_end + + typedef std::list< cWebPlugin* > PluginList; + + + cWebAdmin(void); + ~cWebAdmin(); + + /// Initializes the object. Returns true if successfully initialized and ready to start + bool Init(void); + + /// Starts the HTTP server taking care of the admin. Returns true if successful + bool Start(void); + + void AddPlugin( cWebPlugin* a_Plugin ); + void RemovePlugin( cWebPlugin* a_Plugin ); + + // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such + PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS << + + // tolua_begin + + sWebAdminPage GetPage(const HTTPRequest & a_Request); + + /// Returns the contents of the default page - the list of plugins and players + AString GetDefaultPage(void); + + /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) + AString GetBaseURL(const AString & a_URL); + + /// Escapes text passed into it, so it can be embedded into html. + static AString GetHTMLEscapedString(const AString & a_Input); + + // tolua_end + + /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) + AString GetBaseURL(const AStringVector& a_URLSplit); + +protected: + /// Common base class for request body data handlers + class cRequestData + { + public: + virtual ~cRequestData() {} // Force a virtual destructor in all descendants + + /// Called when a new chunk of body data is received + virtual void OnBody(const char * a_Data, int a_Size) = 0; + } ; + + /// The body handler for requests in the "/webadmin" and "/~webadmin" paths + class cWebadminRequestData : + public cRequestData, + public cHTTPFormParser::cCallbacks + { + public: + cHTTPFormParser m_Form; + + + cWebadminRequestData(cHTTPRequest & a_Request) : + m_Form(a_Request, *this) + { + } + + // cRequestData overrides: + virtual void OnBody(const char * a_Data, int a_Size) override; + + // cHTTPFormParser::cCallbacks overrides. Files are ignored: + virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override {} + virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override {} + virtual void OnFileEnd(cHTTPFormParser & a_Parser) override {} + } ; + + + /// Set to true if Init() succeeds and the webadmin isn't to be disabled + bool m_IsInitialized; + + /// The webadmin.ini file, used for the settings and allowed logins + cIniFile m_IniFile; + + PluginList m_Plugins; + + /// The Lua template script to provide templates: + cLuaState m_TemplateScript; + + /// The HTTP server which provides the underlying HTTP parsing, serialization and events + cHTTPServer m_HTTPServer; + + + AString GetTemplate(void); + + /// Handles requests coming to the "/webadmin" or "/~webadmin" URLs + void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + + /// Handles requests for the root page + void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + + // cHTTPServer::cCallbacks overrides: + virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; + virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override; + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; +} ; // tolua_export + + + + + +// Revert MSVC warnings back to orignal state: +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + + + + diff --git a/src/WebPlugin.cpp b/src/WebPlugin.cpp new file mode 100644 index 000000000..48ddb2076 --- /dev/null +++ b/src/WebPlugin.cpp @@ -0,0 +1,113 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "WebPlugin.h" +#include "WebAdmin.h" +#include "Server.h" +#include "Root.h" + + + + + +cWebPlugin::cWebPlugin() +{ + cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); + if (WebAdmin != NULL) + { + WebAdmin->AddPlugin(this); + } +} + + + + + +cWebPlugin::~cWebPlugin() +{ + cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); + if (WebAdmin != NULL) + { + WebAdmin->RemovePlugin(this); + } + + for (TabList::iterator itr = m_Tabs.begin(); itr != m_Tabs.end(); ++itr) + { + delete *itr; + } + m_Tabs.clear(); +} + + + + + +std::list > cWebPlugin::GetTabNames(void) +{ + std::list< std::pair< AString, AString > > NameList; + for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) + { + std::pair< AString, AString > StringPair; + StringPair.first = (*itr)->Title; + StringPair.second = (*itr)->SafeTitle; + NameList.push_back( StringPair ); + } + return NameList; +} + + + + + +std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest * a_Request) +{ + std::pair< AString, AString > Names; + AStringVector Split = StringSplit(a_Request->Path, "/"); + + if( Split.size() > 1 ) + { + sWebPluginTab* Tab = 0; + if( Split.size() > 2 ) // If we got the tab name, show that page + { + for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) + { + if( (*itr)->SafeTitle.compare( Split[2] ) == 0 ) // This is the one! Rawr + { + Tab = *itr; + break; + } + } + } + else // Otherwise show the first tab + { + if( GetTabs().size() > 0 ) + Tab = *GetTabs().begin(); + } + + if( Tab ) + { + Names.first = Tab->Title; + Names.second = Tab->SafeTitle; + } + } + + return Names; +} + + + + +AString cWebPlugin::SafeString( const AString & a_String ) +{ + AString RetVal; + for( unsigned int i = 0; i < a_String.size(); ++i ) + { + char c = a_String[i]; + if( c == ' ' ) + { + c = '_'; + } + RetVal.push_back( c ); + } + return RetVal; +} \ No newline at end of file diff --git a/src/WebPlugin.h b/src/WebPlugin.h new file mode 100644 index 000000000..22587b892 --- /dev/null +++ b/src/WebPlugin.h @@ -0,0 +1,48 @@ + +#pragma once + +struct lua_State; +struct HTTPRequest; + + + + + +// tolua_begin +class cWebPlugin +{ +public: + // tolua_end + cWebPlugin(); + virtual ~cWebPlugin(); + + // tolua_begin + virtual const AString GetWebTitle(void) const = 0; + + virtual AString HandleWebRequest(const HTTPRequest * a_Request ) = 0; + + static AString SafeString( const AString & a_String ); + // tolua_end + + struct sWebPluginTab + { + std::string Title; + std::string SafeTitle; + + int UserData; + }; + + typedef std::list< sWebPluginTab* > TabList; + TabList & GetTabs() { return m_Tabs; } + + typedef std::list< std::pair > TabNameList; + TabNameList GetTabNames(); // >> EXPORTED IN MANUALBINDINGS << + std::pair< AString, AString > GetTabNameForRequest(const HTTPRequest* a_Request ); + +private: + TabList m_Tabs; +}; // tolua_export + + + + diff --git a/src/World.cpp b/src/World.cpp new file mode 100644 index 000000000..0f9df8a62 --- /dev/null +++ b/src/World.cpp @@ -0,0 +1,2715 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BlockID.h" +#include "World.h" +#include "ChunkDef.h" +#include "ClientHandle.h" +#include "Server.h" +#include "Item.h" +#include "Root.h" +#include "../iniFile/iniFile.h" +#include "ChunkMap.h" +#include "OSSupport/Timer.h" + +// Entities (except mobs): +#include "Entities/Pickup.h" +#include "Entities/Player.h" +#include "Entities/TNTEntity.h" + +// Simulators: +#include "Simulator/SimulatorManager.h" +#include "Simulator/FloodyFluidSimulator.h" +#include "Simulator/FluidSimulator.h" +#include "Simulator/FireSimulator.h" +#include "Simulator/NoopFluidSimulator.h" +#include "Simulator/SandSimulator.h" +#include "Simulator/RedstoneSimulator.h" +#include "Simulator/VaporizeFluidSimulator.h" + +// Mobs: +#include "Mobs/IncludeAllMonsters.h" +#include "MobCensus.h" +#include "MobSpawner.h" + +#include "MersenneTwister.h" +#include "Generating/Trees.h" +#include "PluginManager.h" +#include "Blocks/BlockHandler.h" +#include "Vector3d.h" + +#include "Tracer.h" +#include "tolua++.h" + +// DEBUG: Test out the cLineBlockTracer class by tracing a few lines: +#include "LineBlockTracer.h" + +#ifndef _WIN32 + #include +#endif + + + + + +/// Up to this many m_SpreadQueue elements are handled each world tick +const int MAX_LIGHTING_SPREAD_PER_TICK = 10; + +const int TIME_SUNSET = 12000; +const int TIME_NIGHT_START = 13187; +const int TIME_NIGHT_END = 22812; +const int TIME_SUNRISE = 23999; +const int TIME_SPAWN_DIVISOR = 148; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorldLoadProgress: + +/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn() +class cWorldLoadProgress : + public cIsThread +{ +public: + cWorldLoadProgress(cWorld * a_World) : + cIsThread("cWorldLoadProgress"), + m_World(a_World) + { + Start(); + } + + void Stop(void) + { + m_ShouldTerminate = true; + Wait(); + } + +protected: + + cWorld * m_World; + + virtual void Execute(void) override + { + for (;;) + { + LOG("%d chunks to load, %d chunks to generate", + m_World->GetStorage().GetLoadQueueLength(), + m_World->GetGenerator().GetQueueLength() + ); + + // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish + for (int i = 0; i < 20; i++) + { + cSleep::MilliSleep(100); + if (m_ShouldTerminate) + { + return; + } + } + } // for (-ever) + } + +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorldLightingProgress: + +/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn() +class cWorldLightingProgress : + public cIsThread +{ +public: + cWorldLightingProgress(cLightingThread * a_Lighting) : + cIsThread("cWorldLightingProgress"), + m_Lighting(a_Lighting) + { + Start(); + } + + void Stop(void) + { + m_ShouldTerminate = true; + Wait(); + } + +protected: + + cLightingThread * m_Lighting; + + virtual void Execute(void) override + { + for (;;) + { + LOG("%d chunks remaining to light", m_Lighting->GetQueueLength() + ); + + // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish + for (int i = 0; i < 20; i++) + { + cSleep::MilliSleep(100); + if (m_ShouldTerminate) + { + return; + } + } + } // for (-ever) + } + +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld::cLock: + +cWorld::cLock::cLock(cWorld & a_World) : + super(&(a_World.m_ChunkMap->GetCS())) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld::cTickThread: + +cWorld::cTickThread::cTickThread(cWorld & a_World) : + super(Printf("WorldTickThread: %s", a_World.GetName().c_str())), + m_World(a_World) +{ +} + + + + + +void cWorld::cTickThread::Execute(void) +{ + cTimer Timer; + + long long msPerTick = 50; + long long LastTime = Timer.GetNowTime(); + + while (!m_ShouldTerminate) + { + long long NowTime = Timer.GetNowTime(); + float DeltaTime = (float)(NowTime - LastTime); + m_World.Tick(DeltaTime); + long long TickTime = Timer.GetNowTime() - NowTime; + + if (TickTime < msPerTick) + { + // Stretch tick time until it's at least msPerTick + cSleep::MilliSleep((unsigned int)(msPerTick - TickTime)); + } + + LastTime = NowTime; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld: + +cWorld::cWorld(const AString & a_WorldName) : + m_WorldName(a_WorldName), + m_IniFileName(m_WorldName + "/world.ini"), + m_StorageSchema("Default"), + m_WorldAgeSecs(0), + m_TimeOfDaySecs(0), + m_WorldAge(0), + m_TimeOfDay(0), + m_LastTimeUpdate(0), + m_RSList(0), + m_Weather(eWeather_Sunny), + m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :) + m_TickThread(*this), + m_SkyDarkness(0) +{ + LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); + + cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName); +} + + + + + +cWorld::~cWorld() +{ + delete m_SimulatorManager; + delete m_SandSimulator; + delete m_WaterSimulator; + delete m_LavaSimulator; + delete m_FireSimulator; + delete m_RedstoneSimulator; + + UnloadUnusedChunks(); + + m_Storage.WaitForFinish(); + + delete m_ChunkMap; +} + + + + + +void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) +{ + BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cWorld::SetWeather(eWeather a_NewWeather) +{ + // Do the plugins agree? Do they want a different weather? + cRoot::Get()->GetPluginManager()->CallHookWeatherChanging(*this, a_NewWeather); + + // Set new period for the selected weather: + switch (a_NewWeather) + { + case eWeather_Sunny: m_WeatherInterval = 14400 + (m_TickRand.randInt() % 4800); break; // 12 - 16 minutes + case eWeather_Rain: m_WeatherInterval = 9600 + (m_TickRand.randInt() % 7200); break; // 8 - 14 minutes + case eWeather_ThunderStorm: m_WeatherInterval = 2400 + (m_TickRand.randInt() % 4800); break; // 2 - 6 minutes + default: + { + LOGWARNING("Requested unknown weather %d, setting sunny for a minute instead.", a_NewWeather); + a_NewWeather = eWeather_Sunny; + m_WeatherInterval = 1200; + break; + } + } // switch (NewWeather) + m_Weather = a_NewWeather; + BroadcastWeather(m_Weather); + + // Let the plugins know about the change: + cPluginManager::Get()->CallHookWeatherChanged(*this); +} + + + + + +void cWorld::ChangeWeather(void) +{ + // In the next tick the weather will be changed + m_WeatherInterval = 0; +} + + + + + +void cWorld::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + return m_ChunkMap->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cWorld::InitializeSpawn(void) +{ + int ChunkX = 0, ChunkY = 0, ChunkZ = 0; + BlockToChunk((int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ); + + // For the debugging builds, don't make the server build too much world upon start: + #if defined(_DEBUG) || defined(ANDROID_NDK) + int ViewDist = 9; + #else + int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is + #endif // _DEBUG + + LOG("Preparing spawn area in world \"%s\"...", m_WorldName.c_str()); + for (int x = 0; x < ViewDist; x++) + { + for (int z = 0; z < ViewDist; z++) + { + m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, ZERO_CHUNK_Y, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader + } + } + + { + // Display progress during this process: + cWorldLoadProgress Progress(this); + + // Wait for the loader to finish loading + m_Storage.WaitForQueuesEmpty(); + + // Wait for the generator to finish generating + m_Generator.WaitForQueueEmpty(); + + Progress.Stop(); + } + + // Light all chunks that have been newly generated: + LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str()); + + for (int x = 0; x < ViewDist; x++) + { + int ChX = x + ChunkX-(ViewDist - 1) / 2; + for (int z = 0; z < ViewDist; z++) + { + int ChZ = z + ChunkZ-(ViewDist - 1) / 2; + if (!m_ChunkMap->IsChunkLighted(ChX, ChZ)) + { + m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread + } + } // for z + } // for x + + { + cWorldLightingProgress Progress(&m_Lighting); + m_Lighting.WaitForQueueEmpty(); + Progress.Stop(); + } + + // TODO: Better spawn detection - move spawn out of the water if it isn't set in the INI already + m_SpawnY = (double)GetHeight((int)m_SpawnX, (int)m_SpawnZ) + 1.6f; // +1.6f eye height + + + #ifdef TEST_LINEBLOCKTRACER + // DEBUG: Test out the cLineBlockTracer class by tracing a few lines: + class cTracerCallbacks : + public cBlockTracer::cCallbacks + { + virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + LOGD("Block {%d, %d, %d}: %d:%d (%s)", + a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, + ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str() + ); + return false; + } + + virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ) override + { + LOGD("Block {%d, %d, %d}: no data available", + a_BlockX, a_BlockY, a_BlockZ + ); + return false; + } + + virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override + { + LOGD("Out of world at {%f, %f, %f}", a_BlockX, a_BlockY, a_BlockZ); + return false; + } + + virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override + { + LOGD("Into world at {%f, %f, %f}", a_BlockX, a_BlockY, a_BlockZ); + return false; + } + + virtual void OnNoMoreHits(void) override + { + LOGD("No more hits"); + } + } Callbacks; + LOGD("Spawn is at {%f, %f, %f}", m_SpawnX, m_SpawnY, m_SpawnZ); + LOGD("Tracing a line along +X:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 10, m_SpawnY, m_SpawnZ, m_SpawnX + 10, m_SpawnY, m_SpawnZ); + LOGD("Tracing a line along -Z:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX, m_SpawnY, m_SpawnZ + 10, m_SpawnX, m_SpawnY, m_SpawnZ - 10); + LOGD("Tracing a line along -Y, out of world:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX, 260, m_SpawnZ, m_SpawnX, -5, m_SpawnZ); + LOGD("Tracing a line along XY:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 10, m_SpawnY - 10, m_SpawnZ, m_SpawnX + 10, m_SpawnY + 10, m_SpawnZ); + LOGD("Tracing a line in generic direction:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 15, m_SpawnY - 5, m_SpawnZ + 7.5, m_SpawnX + 13, m_SpawnY - 10, m_SpawnZ + 8.5); + LOGD("Tracing tests done"); + #endif // TEST_LINEBLOCKTRACER +} + + + + + +void cWorld::Start(void) +{ + // TODO: Find a proper spawn location, based on the biomes (not in ocean) + m_SpawnX = (double)((m_TickRand.randInt() % 1000) - 500); + m_SpawnY = cChunkDef::Height; + m_SpawnZ = (double)((m_TickRand.randInt() % 1000) - 500); + m_GameMode = eGameMode_Creative; + + cIniFile IniFile; + if (!IniFile.ReadFile(m_IniFileName)) + { + LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str()); + } + AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld"); + m_Dimension = StringToDimension(Dimension); + switch (m_Dimension) + { + case dimNether: + case dimOverworld: + case dimEnd: + { + break; + } + default: + { + LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", Dimension.c_str()); + m_Dimension = dimOverworld; + break; + } + } // switch (m_Dimension) + m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX); + m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY); + m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ); + m_StorageSchema = IniFile.GetValueSet ("Storage", "Schema", m_StorageSchema); + m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3); + m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3); + m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false); + m_IsCarrotsBonemealable = IniFile.GetValueSetB("Plants", "IsCarrotsBonemealable", true); + m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true); + m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true); + m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true); + m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false); + m_IsPotatoesBonemealable = IniFile.GetValueSetB("Plants", "IsPotatoesBonemealable", true); + m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true); + m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false); + m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true); + m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false); + m_bEnabledPVP = IniFile.GetValueSetB("PVP", "Enabled", true); + m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false); + + m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode); + + // Load allowed mobs: + const char * DefaultMonsters = ""; + switch (m_Dimension) + { + case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; + case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break; + case dimEnd: DefaultMonsters = "enderman"; break; + default: + { + ASSERT(!"Unhandled world dimension"); + DefaultMonsters = "wither"; + break; + } + } + m_bAnimals = IniFile.GetValueSetB("Monsters", "AnimalsOn", true); + AString AllMonsters = IniFile.GetValueSet("Monsters", "Types", DefaultMonsters); + AStringVector SplitList = StringSplitAndTrim(AllMonsters, ","); + for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr) + { + cMonster::eType ToAdd = cMonster::StringToMobType(*itr); + if (ToAdd != cMonster::mtInvalidType) + { + m_AllowedMobs.insert(ToAdd); + LOGD("Allowed mob: %s", itr->c_str()); + } + else + { + LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str()); + } + } + + m_ChunkMap = new cChunkMap(this); + + m_LastSave = 0; + m_LastUnload = 0; + + // preallocate some memory for ticking blocks so we don't need to allocate that often + m_BlockTickQueue.reserve(1000); + m_BlockTickQueueCopy.reserve(1000); + + // Simulators: + m_SimulatorManager = new cSimulatorManager(*this); + m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); + m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); + m_SandSimulator = new cSandSimulator(*this, IniFile); + m_FireSimulator = new cFireSimulator(*this, IniFile); + m_RedstoneSimulator = new cRedstoneSimulator(*this); + + // Water and Lava simulators get registered in InitializeFluidSimulator() + m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1); + m_SimulatorManager->RegisterSimulator(m_FireSimulator, 1); + m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1); + + m_Lighting.Start(this); + m_Storage.Start(this, m_StorageSchema); + m_Generator.Start(this, IniFile); + m_ChunkSender.Start(this); + m_TickThread.Start(); + + // Init of the spawn monster time (as they are supposed to have different spawn rate) + m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfHostile, 0)); + m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfPassive, 0)); + m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfAmbient, 0)); + m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfWater, 0)); + + + // Save any changes that the defaults may have done to the ini file: + if (!IniFile.WriteFile(m_IniFileName)) + { + LOGWARNING("Could not write world config to %s", m_IniFileName.c_str()); + } + +} + + + + + +void cWorld::Stop(void) +{ + // Delete the clients that have been in this world: + { + cCSLock Lock(m_CSClients); + for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + { + (*itr)->Destroy(); + delete *itr; + } // for itr - m_Clients[] + m_Clients.clear(); + } + + m_TickThread.Stop(); + m_Lighting.Stop(); + m_Generator.Stop(); + m_ChunkSender.Stop(); + m_Storage.Stop(); +} + + + + + +void cWorld::Tick(float a_Dt) +{ + // Call the plugins + cPluginManager::Get()->CallHookWorldTick(*this, a_Dt); + + // We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it + m_WorldAgeSecs += (double)a_Dt / 1000.0; + m_TimeOfDaySecs += (double)a_Dt / 1000.0; + + // Wrap time of day each 20 minutes (1200 seconds) + if (m_TimeOfDaySecs > 1200.0) + { + m_TimeOfDaySecs -= 1200.0; + } + + m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0); + m_TimeOfDay = (Int64)(m_TimeOfDaySecs * 20.0); + + // Updates the sky darkness based on current time of day + UpdateSkyDarkness(); + + // Broadcast time update every 40 ticks (2 seconds) + if (m_LastTimeUpdate < m_WorldAge - 40) + { + BroadcastTimeUpdate(); + m_LastTimeUpdate = m_WorldAge; + } + + m_ChunkMap->Tick(a_Dt); + + TickClients(a_Dt); + TickQueuedBlocks(); + TickQueuedTasks(); + + GetSimulatorManager()->Simulate(a_Dt); + + TickWeather(a_Dt); + + // Asynchronously set blocks: + sSetBlockList FastSetBlockQueueCopy; + { + cCSLock Lock(m_CSFastSetBlock); + std::swap(FastSetBlockQueueCopy, m_FastSetBlockQueue); + } + m_ChunkMap->FastSetBlocks(FastSetBlockQueueCopy); + if (!FastSetBlockQueueCopy.empty()) + { + // Some blocks failed, store them for next tick: + cCSLock Lock(m_CSFastSetBlock); + m_FastSetBlockQueue.splice(m_FastSetBlockQueue.end(), FastSetBlockQueueCopy); + } + + if (m_WorldAge - m_LastSave > 60 * 5 * 20) // Save each 5 minutes + { + SaveAllChunks(); + } + + if (m_WorldAge - m_LastUnload > 10 * 20) // Unload every 10 seconds + { + UnloadUnusedChunks(); + } + + TickMobs(a_Dt); + + std::vector m_RSList_copy(m_RSList); + + m_RSList.clear(); + + std::vector::const_iterator cii; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter) + for (cii = m_RSList_copy.begin(); cii != m_RSList_copy.end();) + { + int tempX = *cii; cii++; + int tempY = *cii; cii++; + int tempZ = *cii; cii++; + int state = *cii; cii++; + + if ((state == 11111) && ((int)GetBlock(tempX, tempY, tempZ) == E_BLOCK_REDSTONE_TORCH_OFF)) + { + FastSetBlock(tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_ON, (int)GetBlockMeta(tempX, tempY, tempZ)); + } + else if ((state == 00000) && ((int)GetBlock(tempX, tempY, tempZ) == E_BLOCK_REDSTONE_TORCH_ON)) + { + FastSetBlock(tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_OFF, (int)GetBlockMeta(tempX, tempY, tempZ)); + } + } + m_RSList_copy.erase(m_RSList_copy.begin(),m_RSList_copy.end()); +} + + + + + +void cWorld::TickWeather(float a_Dt) +{ + // There are no weather changes anywhere but in the Overworld: + if (GetDimension() != dimOverworld) + { + return; + } + + if (m_WeatherInterval > 0) + { + // Not yet, wait for the weather period to end + m_WeatherInterval--; + } + else + { + // Change weather: + + // Pick a new weather. Only reasonable transitions allowed: + eWeather NewWeather = m_Weather; + switch (m_Weather) + { + case eWeather_Sunny: NewWeather = eWeather_Rain; break; + case eWeather_ThunderStorm: NewWeather = eWeather_Rain; break; + case eWeather_Rain: + { + // 1/8 chance of turning into a thunderstorm + NewWeather = ((m_TickRand.randInt() % 256) < 32) ? eWeather_ThunderStorm : eWeather_Sunny; + break; + } + + default: + { + LOGWARNING("Unknown current weather: %d. Setting sunny.", m_Weather); + ASSERT(!"Unknown weather"); + NewWeather = eWeather_Sunny; + } + } + + SetWeather(NewWeather); + } // else (m_WeatherInterval > 0) + + if (m_Weather == eWeather_ThunderStorm) + { + // 0.5% chance per tick of thunderbolt + if (m_TickRand.randInt() % 199 == 0) + { + CastThunderbolt(0, 0, 0); // TODO: find random possitions near players to cast thunderbolts. + } + } +} + + + + + +void cWorld::TickMobs(float a_Dt) +{ + // _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs + cWorld::cLock Lock(*this); + + // before every Mob action, we have to count them depending on the distance to players, on their family ... + cMobCensus MobCensus; + m_ChunkMap->CollectMobCensus(MobCensus); + if (m_bAnimals) + { + // Spawning is enabled, spawn now: + static const cMonster::eFamily AllFamilies[] = + { + cMonster::mfHostile, + cMonster::mfPassive, + cMonster::mfAmbient, + cMonster::mfWater, + } ; + for (int i = 0; i < ARRAYCOUNT(AllFamilies); i++) + { + cMonster::eFamily Family = AllFamilies[i]; + int SpawnDelay = cMonster::GetSpawnDelay(Family); + if ( + (m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks before the next round + MobCensus.IsCapped(Family) + ) + { + continue; + } + m_LastSpawnMonster[Family] = m_WorldAge; + cMobSpawner Spawner(Family, m_AllowedMobs); + if (Spawner.CanSpawnAnything()) + { + m_ChunkMap->SpawnMobs(Spawner); + // do the spawn + for (cMobSpawner::tSpawnedContainer::const_iterator itr2 = Spawner.getSpawned().begin(); itr2 != Spawner.getSpawned().end(); itr2++) + { + SpawnMobFinalize(*itr2); + } + } + } // for i - AllFamilies[] + } // if (Spawning enabled) + + // move close mobs + cMobProximityCounter::sIterablePair allCloseEnoughToMoveMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(-1, 64 * 16);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allCloseEnoughToMoveMobs.m_Begin; itr != allCloseEnoughToMoveMobs.m_End; itr++) + { + itr->second.m_Monster.Tick(a_Dt, itr->second.m_Chunk); + } + + // remove too far mobs + cMobProximityCounter::sIterablePair allTooFarMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(128 * 16, -1);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allTooFarMobs.m_Begin; itr != allTooFarMobs.m_End; itr++) + { + itr->second.m_Monster.Destroy(true); + } +} + + + + + +void cWorld::TickQueuedTasks(void) +{ + // Make a copy of the tasks to avoid deadlocks on accessing m_Tasks + cTasks Tasks; + { + cCSLock Lock(m_CSTasks); + std::swap(Tasks, m_Tasks); + } + + // Execute and delete each task: + for (cTasks::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr) + { + (*itr)->Run(*this); + delete *itr; + } // for itr - m_Tasks[] +} + + + + + +void cWorld::TickClients(float a_Dt) +{ + cClientHandleList RemoveClients; + { + cCSLock Lock(m_CSClients); + + // Remove clients scheduled for removal: + for (cClientHandleList::iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) + { + m_Clients.remove(*itr); + } // for itr - m_ClientsToRemove[] + m_ClientsToRemove.clear(); + + // Add clients scheduled for adding: + for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr) + { + if (std::find(m_Clients.begin(), m_Clients.end(), *itr) != m_Clients.end()) + { + ASSERT(!"Adding a client that is already in the clientlist"); + continue; + } + m_Clients.push_back(*itr); + } // for itr - m_ClientsToRemove[] + m_ClientsToAdd.clear(); + + // Tick the clients, take out those that have been destroyed into RemoveClients + for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();) + { + if ((*itr)->IsDestroyed()) + { + // Remove the client later, when CS is not held, to avoid deadlock + RemoveClients.push_back(*itr); + itr = m_Clients.erase(itr); + continue; + } + (*itr)->Tick(a_Dt); + ++itr; + } // for itr - m_Clients[] + } + + // Delete the clients that have been destroyed + for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr) + { + delete *itr; + } // for itr - RemoveClients[] +} + + + + + +void cWorld::UpdateSkyDarkness(void) +{ + int TempTime = (int)m_TimeOfDay; + if (TempTime <= TIME_SUNSET) + { + m_SkyDarkness = 0; + } + else if (TempTime <= TIME_NIGHT_START) + { + m_SkyDarkness = (TIME_NIGHT_START - TempTime) / TIME_SPAWN_DIVISOR; + } + else if (TempTime <= TIME_NIGHT_END) + { + m_SkyDarkness = 8; + } + else + { + m_SkyDarkness = (TIME_SUNRISE - TempTime) / TIME_SPAWN_DIVISOR; + } +} + + + + + +void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +/// Wakes up the simulators for the specified area of blocks +void cWorld::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ) +{ + return m_ChunkMap->WakeUpSimulatorsInArea(a_MinBlockX, a_MaxBlockX, a_MinBlockY, a_MaxBlockY, a_MinBlockZ, a_MaxBlockZ); +} + + + + + +bool cWorld::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) +{ + return m_ChunkMap->ForEachChestInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback) +{ + return m_ChunkMap->ForEachDispenserInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback) +{ + return m_ChunkMap->ForEachDropperInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback) +{ + return m_ChunkMap->ForEachDropSpenserInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) +{ + return m_ChunkMap->ForEachFurnaceInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData) +{ + if (cPluginManager::Get()->CallHookExploding(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData) || (a_ExplosionSize <= 0)) + { + return; + } + + // TODO: Add damage to entities, add support for pickups, and implement block hardiness + Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ); + cVector3iArray BlocksAffected; + m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected); + BroadcastSoundEffect("random.explode", (int)floor(a_BlockX * 8), (int)floor(a_BlockY * 8), (int)floor(a_BlockZ * 8), 1.0f, 0.6f); + { + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos; + if (distance_explosion.SqrLength() < 4096.0) + { + double real_distance = std::max(0.004, sqrt(distance_explosion.SqrLength())); + double power = a_ExplosionSize / real_distance; + if (power <= 1) + { + power = 0; + } + distance_explosion.Normalize(); + distance_explosion *= power; + ch->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, (float)a_ExplosionSize, BlocksAffected, distance_explosion); + } + } + } + cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData); +} + + + + + +bool cWorld::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) +{ + return m_ChunkMap->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) +{ + return m_ChunkMap->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback) +{ + return m_ChunkMap->DoWithDropperAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback) +{ + return m_ChunkMap->DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) +{ + return m_ChunkMap->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) +{ + return m_ChunkMap->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback) +{ + return m_ChunkMap->DoWithChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +void cWorld::GrowTree(int a_X, int a_Y, int a_Z) +{ + if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING) + { + // There is a sapling here, grow a tree according to its type: + GrowTreeFromSapling(a_X, a_Y, a_Z, GetBlockMeta(a_X, a_Y, a_Z)); + } + else + { + // There is nothing here, grow a tree based on the current biome here: + GrowTreeByBiome(a_X, a_Y, a_Z); + } +} + + + + + +void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_SaplingMeta) +{ + cNoise Noise(m_Generator.GetSeed()); + sSetBlockVector Logs, Other; + switch (a_SaplingMeta & 0x07) + { + case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + } + Other.insert(Other.begin(), Logs.begin(), Logs.end()); + Logs.clear(); + GrowTreeImage(Other); +} + + + + + +void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z) +{ + cNoise Noise(m_Generator.GetSeed()); + sSetBlockVector Logs, Other; + GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), (EMCSBiome)GetBiomeAt(a_X, a_Z), Logs, Other); + Other.insert(Other.begin(), Logs.begin(), Logs.end()); + Logs.clear(); + GrowTreeImage(Other); +} + + + + + +void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) +{ + // Check that the tree has place to grow + + // Make a copy of the log blocks: + sSetBlockVector b2; + for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + if (itr->BlockType == E_BLOCK_LOG) + { + b2.push_back(*itr); + } + } // for itr - a_Blocks[] + + // Query blocktypes and metas at those log blocks: + if (!GetBlocks(b2, false)) + { + return; + } + + // Check that at each log's coord there's an block allowed to be overwritten: + for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr) + { + switch (itr->BlockType) + { + CASE_TREE_ALLOWED_BLOCKS: + { + break; + } + default: + { + return; + } + } + } // for itr - b2[] + + // All ok, replace blocks with the tree image: + m_ChunkMap->ReplaceTreeBlocks(a_Blocks); +} + + + + + +bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal) +{ + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + switch (BlockType) + { + case E_BLOCK_CARROTS: + { + if (a_IsByBonemeal && !m_IsCarrotsBonemealable) + { + return false; + } + if (BlockMeta < 7) + { + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + return true; + } + + case E_BLOCK_CROPS: + { + if (a_IsByBonemeal && !m_IsCropsBonemealable) + { + return false; + } + if (BlockMeta < 7) + { + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + return true; + } + + case E_BLOCK_MELON_STEM: + { + if (BlockMeta < 7) + { + if (a_IsByBonemeal && !m_IsMelonStemBonemealable) + { + return false; + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + else + { + if (a_IsByBonemeal && !m_IsMelonBonemealable) + { + return false; + } + GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); + } + return true; + } + + case E_BLOCK_POTATOES: + { + if (a_IsByBonemeal && !m_IsPotatoesBonemealable) + { + return false; + } + if (BlockMeta < 7) + { + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + return true; + } + + case E_BLOCK_PUMPKIN_STEM: + { + if (BlockMeta < 7) + { + if (a_IsByBonemeal && !m_IsPumpkinStemBonemealable) + { + return false; + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + else + { + if (a_IsByBonemeal && !m_IsPumpkinBonemealable) + { + return false; + } + GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); + } + return true; + } + + case E_BLOCK_SAPLING: + { + if (a_IsByBonemeal && !m_IsSaplingBonemealable) + { + return false; + } + GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); + return true; + } + + case E_BLOCK_GRASS: + { + if (a_IsByBonemeal && !m_IsGrassBonemealable) + { + return false; + } + MTRand r1; + for (int i = 0; i < 60; i++) + { + int OfsX = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; + int OfsY = r1.randInt(3) + r1.randInt(3) - 3; + int OfsZ = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; + BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ); + if (Ground != E_BLOCK_GRASS) + { + continue; + } + BLOCKTYPE Above = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ); + if (Above != E_BLOCK_AIR) + { + continue; + } + BLOCKTYPE SpawnType; + NIBBLETYPE SpawnMeta = 0; + switch (r1.randInt(10)) + { + case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break; + case 1: SpawnType = E_BLOCK_RED_ROSE; break; + default: + { + SpawnType = E_BLOCK_TALL_GRASS; + SpawnMeta = E_META_TALL_GRASS_GRASS; + break; + } + } // switch (random spawn block type) + FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, SpawnType, SpawnMeta); + } // for i - 50 times + return true; + } + + case E_BLOCK_SUGARCANE: + { + if (a_IsByBonemeal && !m_IsSugarcaneBonemealable) + { + return false; + } + m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, m_MaxSugarcaneHeight); + return true; + } + + case E_BLOCK_CACTUS: + { + if (a_IsByBonemeal && !m_IsCactusBonemealable) + { + return false; + } + m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, m_MaxCactusHeight); + return true; + } + } // switch (BlockType) + return false; +} + + + + + +void cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); +} + + + + + +void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) +{ + MTRand Rand; + m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand); +} + + + + + +void cWorld::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); +} + + + + + +int cWorld::GetBiomeAt (int a_BlockX, int a_BlockZ) +{ + return m_ChunkMap->GetBiomeAt(a_BlockX, a_BlockZ); +} + + + + + +void cWorld::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if (a_BlockType == E_BLOCK_AIR) + { + BlockHandler(GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnDestroyed(this, a_BlockX, a_BlockY, a_BlockZ); + } + m_ChunkMap->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + + BlockHandler(a_BlockType)->OnPlaced(this, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); +} + + + + + +void cWorld::FastSetBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSFastSetBlock); + m_FastSetBlockQueue.push_back(sSetBlock(a_X, a_Y, a_Z, a_BlockType, a_BlockMeta)); +} + + + + + +void cWorld::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_TickDelay) +{ + m_ChunkMap->QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, GetWorldAge() + a_TickDelay); +} + + + + + +BLOCKTYPE cWorld::GetBlock(int a_X, int a_Y, int a_Z) +{ + // First check if it isn't queued in the m_FastSetBlockQueue: + { + int X = a_X, Y = a_Y, Z = a_Z; + int ChunkX, ChunkY, ChunkZ; + AbsoluteToRelative(X, Y, Z, ChunkX, ChunkY, ChunkZ); + + cCSLock Lock(m_CSFastSetBlock); + for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) + { + if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + { + return itr->BlockType; + } + } // for itr - m_FastSetBlockQueue[] + } + + return m_ChunkMap->GetBlock(a_X, a_Y, a_Z); +} + + + + + +NIBBLETYPE cWorld::GetBlockMeta(int a_X, int a_Y, int a_Z) +{ + // First check if it isn't queued in the m_FastSetBlockQueue: + { + cCSLock Lock(m_CSFastSetBlock); + for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) + { + if ((itr->x == a_X) && (itr->y == a_Y) && (itr->y == a_Y)) + { + return itr->BlockMeta; + } + } // for itr - m_FastSetBlockQueue[] + } + + return m_ChunkMap->GetBlockMeta(a_X, a_Y, a_Z); +} + + + + + +void cWorld::SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData) +{ + m_ChunkMap->SetBlockMeta(a_X, a_Y, a_Z, a_MetaData); +} + + + + + +NIBBLETYPE cWorld::GetBlockSkyLight(int a_X, int a_Y, int a_Z) +{ + return m_ChunkMap->GetBlockSkyLight(a_X, a_Y, a_Z); +} + + + + + +NIBBLETYPE cWorld::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + return m_ChunkMap->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +bool cWorld::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + return m_ChunkMap->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, (BLOCKTYPE &)a_BlockType, (NIBBLETYPE &)a_BlockMeta); +} + + + + + +bool cWorld::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) +{ + return m_ChunkMap->GetBlockInfo(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); +} + + + + + +bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +{ + return m_ChunkMap->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); +} + + + + + +void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed, bool IsPlayerCreated) +{ + MTRand r1; + a_FlyAwaySpeed /= 1000; // Pre-divide, so that we don't have to divide each time inside the loop + for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) + { + float SpeedX = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); + float SpeedY = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); + float SpeedZ = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); + + cPickup * Pickup = new cPickup( + a_BlockX, a_BlockY, a_BlockZ, + *itr, IsPlayerCreated, SpeedX, SpeedY, SpeedZ + ); + Pickup->Initialize(this); + } +} + + + + + +void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated) +{ + for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) + { + cPickup * Pickup = new cPickup( + a_BlockX, a_BlockY, a_BlockZ, + *itr, IsPlayerCreated, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ + ); + Pickup->Initialize(this); + } +} + + + + + +void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff) +{ + cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTimeInSec); + TNT->Initialize(this); + // TODO: Add a bit of speed in horiz and vert axes, based on the a_InitialVelocityCoeff +} + + + + + +void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) +{ + m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType); +} + + + + + +bool cWorld::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) +{ + return m_ChunkMap->GetBlocks(a_Blocks, a_ContinueOnFailure); +} + + + + + +bool cWorld::DigBlock(int a_X, int a_Y, int a_Z) +{ + cBlockHandler *Handler = cBlockHandler::GetBlockHandler(GetBlock(a_X, a_Y, a_Z)); + Handler->OnDestroyed(this, a_X, a_Y, a_Z); + return m_ChunkMap->DigBlock(a_X, a_Y, a_Z); +} + + + + + +void cWorld::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) +{ + m_ChunkMap->SendBlockTo(a_X, a_Y, a_Z, a_Player); +} + + + + + +int cWorld::GetHeight(int a_X, int a_Z) +{ + return m_ChunkMap->GetHeight(a_X, a_Z); +} + + + + + +bool cWorld::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height) +{ + return m_ChunkMap->TryGetHeight(a_BlockX, a_BlockZ, a_Height); +} + + + + + +void cWorld::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + return m_ChunkMap->BroadcastAttachEntity(a_Entity, a_Vehicle); +} + + + + + +void cWorld::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); +} + + + + + +void cWorld::BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastBlockBreakAnimation(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage, a_Exclude); +} + + + + + +void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cWorld::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendChat(a_Message); + } +} + + + + + +void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude); +} + + + + + +void cWorld::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); +} + + + + + +void cWorld::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastDestroyEntity(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityHeadLook(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityLook(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityMetadata(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityVelocity(a_Entity, a_Exclude); +} + + + + +void cWorld::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); +} + + + + + +void cWorld::BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendPlayerListItem(a_Player, a_IsOnline); + } +} + + + + + +void cWorld::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); +} + + + + + +void cWorld::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data, a_Exclude); +} + + + + + +void cWorld::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastSpawnEntity(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastTeleportEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendTeleportEntity(a_Entity); + } +} + + + + + +void cWorld::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendTimeUpdate(m_WorldAge, m_TimeOfDay); + } +} + + + + + +void cWorld::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_ChunkMap->BroadcastUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cWorld::BroadcastWeather(eWeather a_Weather, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendWeather(a_Weather); + } +} + + + + + +void cWorld::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) +{ + m_ChunkMap->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); +} + + + + + +void cWorld::MarkChunkDirty (int a_ChunkX, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkDirty (a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::MarkChunkSaving(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkSaving(a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkSaved (a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::SetChunkData( + int a_ChunkX, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap * a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty +) +{ + // Validate biomes, if needed: + cChunkDef::BiomeMap BiomeMap; + const cChunkDef::BiomeMap * Biomes = a_BiomeMap; + if (a_BiomeMap == NULL) + { + // The biomes are not assigned, get them from the generator: + Biomes = &BiomeMap; + m_Generator.GenerateBiomes(a_ChunkX, a_ChunkZ, BiomeMap); + } + + m_ChunkMap->SetChunkData( + a_ChunkX, a_ChunkZ, + a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, + a_HeightMap, *Biomes, + a_BlockEntities, + a_MarkDirty + ); + + // Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347): + for (cEntityList::iterator itr = a_Entities.begin(), end = a_Entities.end(); itr != end; ++itr) + { + (*itr)->Initialize(this); + } + + // If a client is requesting this chunk, send it to them: + if (m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkZ)) + { + m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkZ); + } + + // Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk): + m_Lighting.ChunkReady(a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight +) +{ + m_ChunkMap->ChunkLighted(a_ChunkX, a_ChunkZ, a_BlockLight, a_SkyLight); +} + + + + + +bool cWorld::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback) +{ + return m_ChunkMap->GetChunkData(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) +{ + return m_ChunkMap->GetChunkBlockTypes(a_ChunkX, a_ChunkZ, a_BlockTypes); +} + + + + + +bool cWorld::IsChunkValid(int a_ChunkX, int a_ChunkZ) const +{ + return m_ChunkMap->IsChunkValid(a_ChunkX, a_ChunkZ); +} + + + + + +bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const +{ + return m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::UnloadUnusedChunks(void) +{ + m_LastUnload = m_WorldAge; + m_ChunkMap->UnloadUnusedChunks(); +} + + + + + +void cWorld::CollectPickupsByPlayer(cPlayer * a_Player) +{ + m_ChunkMap->CollectPickupsByPlayer(a_Player); +} + + + + + +void cWorld::AddPlayer(cPlayer * a_Player) +{ + { + cCSLock Lock(m_CSPlayers); + + ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW? + + m_Players.remove(a_Player); // Make sure the player is registered only once + m_Players.push_back(a_Player); + } + + // Add the player's client to the list of clients to be ticked: + if (a_Player->GetClientHandle() != NULL) + { + cCSLock Lock(m_CSClients); + m_ClientsToAdd.push_back(a_Player->GetClientHandle()); + } + + // The player has already been added to the chunkmap as the entity, do NOT add again! +} + + + + + +void cWorld::RemovePlayer(cPlayer * a_Player) +{ + m_ChunkMap->RemoveEntity(a_Player); + { + cCSLock Lock(m_CSPlayers); + m_Players.remove(a_Player); + } + + // Remove the player's client from the list of clients to be ticked: + if (a_Player->GetClientHandle() != NULL) + { + cCSLock Lock(m_CSClients); + m_ClientsToRemove.push_back(a_Player->GetClientHandle()); + } +} + + + + + +bool cWorld::ForEachPlayer(cPlayerListCallback & a_Callback) +{ + // Calls the callback for each player in the list + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(), itr2 = itr; itr != m_Players.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_Players[] + return true; +} + + + + + +bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + // Calls the callback for each player in the list + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (NoCaseCompare((*itr)->GetName(), a_PlayerName) == 0) + { + a_Callback.Item(*itr); + return true; + } + } // for itr - m_Players[] + return false; +} + + + + + +bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback) +{ + cPlayer * BestMatch = NULL; + unsigned int BestRating = 0; + unsigned int NameLength = a_PlayerNameHint.length(); + + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + unsigned int Rating = RateCompareString (a_PlayerNameHint, (*itr)->GetName()); + if (Rating >= BestRating) + { + BestMatch = *itr; + BestRating = Rating; + } + if (Rating == NameLength) // Perfect match + { + break; + } + } // for itr - m_Players[] + + if (BestMatch != NULL) + { + LOG("Compared %s and %s with rating %i", a_PlayerNameHint.c_str(), BestMatch->GetName().c_str(), BestRating); + return a_Callback.Item (BestMatch); + } + return false; +} + + + + + +// TODO: This interface is dangerous! +cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit) +{ + cTracer LineOfSight(this); + + float ClosestDistance = a_SightLimit; + cPlayer* ClosestPlayer = NULL; + + cCSLock Lock(m_CSPlayers); + for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Vector3f Pos = (*itr)->GetPosition(); + float Distance = (Pos - a_Pos).Length(); + + if (Distance < ClosestDistance) + { + if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length())) + { + ClosestDistance = Distance; + ClosestPlayer = *itr; + } + } + } + return ClosestPlayer; +} + + + + + +void cWorld::SendPlayerList(cPlayer * a_DestPlayer) +{ + // Sends the playerlist to a_DestPlayer + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch != NULL) && !ch->IsDestroyed()) + { + a_DestPlayer->GetClientHandle()->SendPlayerListItem(*(*itr), true); + } + } +} + + + + + +bool cWorld::ForEachEntity(cEntityCallback & a_Callback) +{ + return m_ChunkMap->ForEachEntity(a_Callback); +} + + + + + +bool cWorld::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) +{ + return m_ChunkMap->ForEachEntityInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback) +{ + return m_ChunkMap->DoWithEntityByID(a_UniqueID, a_Callback); +} + + + + + +void cWorld::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback) +{ + m_ChunkMap->CompareChunkClients(a_ChunkX1, a_ChunkZ1, a_ChunkX2, a_ChunkZ2, a_Callback); +} + + + + + +bool cWorld::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + return m_ChunkMap->AddChunkClient(a_ChunkX, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + m_ChunkMap->RemoveChunkClient(a_ChunkX, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveClientFromChunks(cClientHandle * a_Client) +{ + m_ChunkMap->RemoveClientFromChunks(a_Client); +} + + + + + +void cWorld::SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + m_ChunkSender.QueueSendChunkTo(a_ChunkX, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client) +{ + m_ChunkSender.RemoveClient(a_Client); +} + + + + + +void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks) +{ + m_ChunkMap->LoadChunks(a_Chunks); +} + + + + + +void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cWorld::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) +{ + AString Line1(a_Line1); + AString Line2(a_Line2); + AString Line3(a_Line3); + AString Line4(a_Line4); + if (cRoot::Get()->GetPluginManager()->CallHookUpdatingSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player)) + { + return false; + } + if (m_ChunkMap->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4)) + { + cRoot::Get()->GetPluginManager()->CallHookUpdatedSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player); + return true; + } + return false; +} + + + + + +bool cWorld::UpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) +{ + return SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player); +} + + + + + +void cWorld::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) +{ + m_ChunkMap->ChunksStay(a_Chunks, a_Stay); +} + + + + + +void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ); + + // Trick: use Y=1 to force the chunk generation even though the chunk data is already present + m_Generator.QueueGenerateChunk(a_ChunkX, 1, a_ChunkZ); +} + + + + + +void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ) +{ + m_Generator.QueueGenerateChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); +} + + + + + +void cWorld::QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) +{ + m_Lighting.QueueChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::IsChunkLighted(int a_ChunkX, int a_ChunkZ) +{ + return m_ChunkMap->IsChunkLighted(a_ChunkX, a_ChunkZ); +} + + + + + +bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) +{ + return m_ChunkMap->ForEachChunkInRect(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ, a_Callback); +} + + + + + +void cWorld::SaveAllChunks(void) +{ + LOGINFO("Saving all chunks..."); + m_LastSave = m_WorldAge; + m_ChunkMap->SaveAllChunks(); + m_Storage.QueueSavedMessage(); +} + + + + + +void cWorld::QueueSaveAllChunks(void) +{ + QueueTask(new cWorld::cTaskSaveAllChunks); +} + + + + + +void cWorld::QueueTask(cTask * a_Task) +{ + cCSLock Lock(m_CSTasks); + m_Tasks.push_back(a_Task); +} + + + + + +void cWorld::AddEntity(cEntity * a_Entity) +{ + m_ChunkMap->AddEntity(a_Entity); +} + + + + + +bool cWorld::HasEntity(int a_UniqueID) +{ + return m_ChunkMap->HasEntity(a_UniqueID); +} + + + + + +void cWorld::RemoveEntity(cEntity * a_Entity) +{ + m_ChunkMap->RemoveEntity(a_Entity); +} + + + + + +/* +unsigned int cWorld::GetNumPlayers(void) +{ + cCSLock Lock(m_CSPlayers); + return m_Players.size(); +} +*/ + + + + + +int cWorld::GetNumChunks(void) const +{ + return m_ChunkMap->GetNumChunks(); +} + + + + + +void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue) +{ + m_ChunkMap->GetChunkStats(a_NumValid, a_NumDirty); + a_NumInLightingQueue = (int) m_Lighting.GetQueueLength(); +} + + + + + +void cWorld::TickQueuedBlocks(void) +{ + if (m_BlockTickQueue.empty()) + { + return; + } + m_BlockTickQueueCopy.clear(); + m_BlockTickQueue.swap(m_BlockTickQueueCopy); + + for (std::vector::iterator itr = m_BlockTickQueueCopy.begin(); itr != m_BlockTickQueueCopy.end(); itr++) + { + BlockTickQueueItem *Block = (*itr); + Block->TicksToWait -= 1; + if (Block->TicksToWait <= 0) + { + // TODO: Handle the case when the chunk is already unloaded + BlockHandler(GetBlock(Block->X, Block->Y, Block->Z))->OnUpdate(this, Block->X, Block->Y, Block->Z); + delete Block; // We don't have to remove it from the vector, this will happen automatically on the next tick + } + else + { + m_BlockTickQueue.push_back(Block); // Keep the block in the queue + } + } // for itr - m_BlockTickQueueCopy[] +} + + + + + +void cWorld::QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait) +{ + BlockTickQueueItem * Block = new BlockTickQueueItem; + Block->X = a_BlockX; + Block->Y = a_BlockY; + Block->Z = a_BlockZ; + Block->TicksToWait = a_TicksToWait; + + m_BlockTickQueue.push_back(Block); +} + + + + + +bool cWorld::IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + return ( + IsBlockWater(GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ)) || + IsBlockWater(GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ)) || + IsBlockWater(GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1)) || + IsBlockWater(GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1)) + ); +} + + + + + +int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType) +{ + cMonster * Monster = NULL; + + Monster = cMonster::NewMonsterFromType(a_MonsterType); + if (Monster != NULL) + { + Monster->SetPosition(a_PosX, a_PosY, a_PosZ); + } + + // Because it's logical that ALL mob spawns need spawn effects, not just spawners + BroadcastSoundParticleEffect(2004, (int)a_PosX, (int)a_PosY, (int)a_PosZ, 0); + + return SpawnMobFinalize(Monster); +} + + + + +int cWorld::SpawnMobFinalize(cMonster * a_Monster) +{ + if (!a_Monster) + return -1; + a_Monster->SetHealth(a_Monster->GetMaxHealth()); + if (cPluginManager::Get()->CallHookSpawningMonster(*this, *a_Monster)) + { + delete a_Monster; + return -1; + } + if (!a_Monster->Initialize(this)) + { + delete a_Monster; + return -1; + } + BroadcastSpawnEntity(*a_Monster); + cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster); + + return a_Monster->GetUniqueID(); +} + + + + + +int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed) +{ + cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Speed); + if (Projectile == NULL) + { + return -1; + } + if (!Projectile->Initialize(this)) + { + delete Projectile; + return -1; + } + BroadcastSpawnEntity(*Projectile); + return Projectile->GetUniqueID(); +} + + + + + +void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Results) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(), end = m_Players.end(); itr != end; ++itr) + { + size_t LastSpace = a_Text.find_last_of(" "); //Find the position of the last space + + std::string LastWord = a_Text.substr(LastSpace + 1, a_Text.length()); //Find the last word + std::string PlayerName ((*itr)->GetName()); + std::size_t Found = PlayerName.find(LastWord); //Try to find last word in playername + + if (Found!=0) + { + continue; //No match + } + + a_Results.push_back((*itr)->GetName()); //Match! + } +} + + + + + +cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock) +{ + AString SimulatorNameKey; + Printf(SimulatorNameKey, "%sSimulator", a_FluidName); + AString SimulatorSectionName; + Printf(SimulatorSectionName, "%sSimulator", a_FluidName); + AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, ""); + if (SimulatorName.empty()) + { + LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Floody\".", SimulatorNameKey.c_str(), GetIniFileName().c_str()); + SimulatorName = "Floody"; + } + + cFluidSimulator * res = NULL; + bool IsWater = (strcmp(a_FluidName, "Water") == 0); // Used for defaults + int Rate = 1; + if ( + (NoCaseCompare(SimulatorName, "vaporize") == 0) || + (NoCaseCompare(SimulatorName, "vaporise") == 0) + ) + { + res = new cVaporizeFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock); + } + else if ( + (NoCaseCompare(SimulatorName, "noop") == 0) || + (NoCaseCompare(SimulatorName, "nop") == 0) || + (NoCaseCompare(SimulatorName, "null") == 0) || + (NoCaseCompare(SimulatorName, "nil") == 0) + ) + { + res = new cNoopFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock); + } + else + { + if (NoCaseCompare(SimulatorName, "floody") != 0) + { + // The simulator name doesn't match anything we have, issue a warning: + LOGWARNING("%s [Physics]:%s specifies an unknown simulator, using the default \"Floody\".", GetIniFileName().c_str(), SimulatorNameKey.c_str()); + } + int Falloff = a_IniFile.GetValueSetI(SimulatorSectionName, "Falloff", IsWater ? 1 : 2); + int TickDelay = a_IniFile.GetValueSetI(SimulatorSectionName, "TickDelay", IsWater ? 5 : 30); + int NumNeighborsForSource = a_IniFile.GetValueSetI(SimulatorSectionName, "NumNeighborsForSource", IsWater ? 2 : -1); + res = new cFloodyFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource); + } + + m_SimulatorManager->RegisterSimulator(res, Rate); + + return res; +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld::cTaskSaveAllChunks: + +void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World) +{ + a_World.SaveAllChunks(); +} + + + + + diff --git a/src/World.h b/src/World.h new file mode 100644 index 000000000..ee4a23b14 --- /dev/null +++ b/src/World.h @@ -0,0 +1,744 @@ + +#pragma once + +#ifndef _WIN32 + #include "BlockID.h" +#else + enum ENUM_ITEM_ID; +#endif + +#define MAX_PLAYERS 65535 + +#include "Simulator/SimulatorManager.h" +#include "MersenneTwister.h" +#include "ChunkMap.h" +#include "WorldStorage/WorldStorage.h" +#include "Generating/ChunkGenerator.h" +#include "Vector3i.h" +#include "Vector3f.h" +#include "ChunkSender.h" +#include "Defines.h" +#include "LightingThread.h" +#include "Item.h" +#include "Mobs/Monster.h" +#include "Entities/ProjectileEntity.h" + + + + + +class cRedstone; +class cFireSimulator; +class cFluidSimulator; +class cSandSimulator; +class cRedstoneSimulator; +class cItem; +class cPlayer; +class cClientHandle; +class cEntity; +class cBlockEntity; +class cWorldGenerator; // The generator that actually generates the chunks for a single world +class cChunkGenerator; // The thread responsible for generating chunks +class cChestEntity; +class cDispenserEntity; +class cFurnaceEntity; +class cMobCensus; + +typedef std::list< cPlayer * > cPlayerList; + +typedef cItemCallback cPlayerListCallback; +typedef cItemCallback cEntityCallback; +typedef cItemCallback cChestCallback; +typedef cItemCallback cDispenserCallback; +typedef cItemCallback cFurnaceCallback; + + + + + + +// tolua_begin +class cWorld +{ +public: + + // tolua_end + + /// A simple RAII locker for the chunkmap - locks the chunkmap in its constructor, unlocks it in the destructor + class cLock : + public cCSLock + { + typedef cCSLock super; + public: + cLock(cWorld & a_World); + } ; + + /// A common ancestor for all tasks queued onto the tick thread + class cTask + { + public: + virtual void Run(cWorld & a_World) = 0; + } ; + + typedef std::vector cTasks; + + class cTaskSaveAllChunks : + public cTask + { + protected: + // cTask overrides: + virtual void Run(cWorld & a_World) override; + } ; + + + static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates + { + return "cWorld"; + } + + // tolua_begin + + int GetTicksUntilWeatherChange(void) const { return m_WeatherInterval; } + Int64 GetWorldAge(void) const { return m_WorldAge; } + Int64 GetTimeOfDay(void) const { return m_TimeOfDay; } + + void SetTicksUntilWeatherChange(int a_WeatherInterval) + { + m_WeatherInterval = a_WeatherInterval; + } + + void SetTimeOfDay(Int64 a_TimeOfDay) + { + m_TimeOfDay = a_TimeOfDay; + m_TimeOfDaySecs = (double)a_TimeOfDay / 20.0; + BroadcastTimeUpdate(); + } + + /// Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable + eGameMode GetGameMode(void) const { return m_GameMode; } + + /// Returns true if the world is in Creative mode + bool IsGameModeCreative(void) const { return (m_GameMode == gmCreative); } + + /// Returns true if the world is in Survival mode + bool IsGameModeSurvival(void) const { return (m_GameMode == gmSurvival); } + + /// Returns true if the world is in Adventure mode + bool IsGameModeAdventure(void) const { return (m_GameMode == gmAdventure); } + + bool IsPVPEnabled(void) const { return m_bEnabledPVP; } + bool IsDeepSnowEnabled(void) const { return m_IsDeepSnowEnabled; } + + eDimension GetDimension(void) const { return m_Dimension; } + + /// Returns the world height at the specified coords; waits for the chunk to get loaded / generated + int GetHeight(int a_BlockX, int a_BlockZ); + + // tolua_end + + /// Retrieves the world height at the specified coords; returns false if chunk not loaded / generated + bool TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height); // Exported in ManualBindings.cpp + + // Broadcast respective packets to all clients of the chunk where the event is taking place + // (Please keep these alpha-sorted) + void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle); + void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude + void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_export + void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityMetadata (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerAnimation (const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL); + void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // tolua_export a_Src coords are Block * 8 + void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); // tolua_export + void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastTimeUpdate (const cClientHandle * a_Exclude = NULL); + void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); + void BroadcastWeather (eWeather a_Weather, const cClientHandle * a_Exclude = NULL); + + /// If there is a block entity at the specified coords, sends it to the client specified + void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); + + void MarkChunkDirty (int a_ChunkX, int a_ChunkZ); + void MarkChunkSaving(int a_ChunkX, int a_ChunkZ); + void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); + + /** Sets the chunk data as either loaded from the storage or generated. + a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. + a_BiomeMap is optional, if not present, biomes will be calculated by the generator + a_HeightMap is optional, if not present, will be calculated. + If a_MarkDirty is set, the chunk is set as dirty (used after generating) + */ + void SetChunkData( + int a_ChunkX, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap * a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty + ); + + void ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight + ); + + bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback); + + /// Gets the chunk's blocks, only the block types + bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes); + + bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const; + bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const; + + void UnloadUnusedChunks(void); // tolua_export + + void CollectPickupsByPlayer(cPlayer * a_Player); + + void AddPlayer( cPlayer* a_Player ); + void RemovePlayer( cPlayer* a_Player ); + + /// Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true + bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored + bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Finds a player from a partial or complete player name and calls the callback - case-insensitive + bool FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + // TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action) + cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit); + + void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player + + /// Adds the entity into its appropriate chunk; takes ownership of the entity ptr + void AddEntity(cEntity * a_Entity); + + bool HasEntity(int a_UniqueID); + + /// Removes the entity, the entity ptr ownership is assumed taken by the caller + void RemoveEntity(cEntity * a_Entity); + + /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. + bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Compares clients of two chunks, calls the callback accordingly + void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback); + + /// Adds client to a chunk, if not already present; returns true if added, false if present + bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes client from the chunk specified + void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the client from all chunks it is present in + void RemoveClientFromChunks(cClientHandle * a_Client); + + /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted) + void SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes client from ChunkSender's queue of chunks to be sent + void RemoveClientFromChunkSender(cClientHandle * a_Client); + + /// Touches the chunk, causing it to be loaded or generated + void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) + bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() + void LoadChunks(const cChunkCoordsList & a_Chunks); + + /// Marks the chunk as failed-to-load: + void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as UpdateSign() + bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp + + /// Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as SetSignLines() + bool UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp + + /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay! + void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); + + /// Regenerate the given chunk: + void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export + + /// Generates the given chunk, if not already generated + void GenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export + + /// Queues a chunk for lighting; a_Callback is called after the chunk is lighted + void QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = NULL); + + bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); + + /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully + bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); + + // tolua_begin + + /** Sets the block at the specified coords to the specified value. + Full processing, incl. updating neighbors, is performed. + */ + void SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** Sets the block at the specified coords to the specified value. + The replacement doesn't trigger block updates. + The replaced blocks aren't checked for block entities (block entity is leaked if it exists at this block) + */ + void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** Queues a SetBlock() with the specified parameters after the specified number of ticks. + Calls SetBlock(), so performs full processing of the replaced block. + */ + void QueueSetBlock(int a_BlockX, int a_BLockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_TickDelay); + + BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ); + void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_MetaData); + NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ); + + // tolua_end + + bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); // TODO: Exported in ManualBindings.cpp + bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); // TODO: Exported in ManualBindings.cpp + // TODO: NIBBLETYPE GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ); + + // tolua_begin + + // Vector3i variants: + void FastSetBlock(const Vector3i & a_Pos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { FastSetBlock( a_Pos.x, a_Pos.y, a_Pos.z, a_BlockType, a_BlockMeta ); } + BLOCKTYPE GetBlock (const Vector3i & a_Pos ) { return GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); } + NIBBLETYPE GetBlockMeta(const Vector3i & a_Pos ) { return GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z ); } + void SetBlockMeta(const Vector3i & a_Pos, NIBBLETYPE a_MetaData ) { SetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z, a_MetaData ); } + // tolua_end + + /** Writes the block area into the specified coords. + Returns true if all chunks have been processed. + Prefer cBlockArea::Write() instead, this is the internal implementation; cBlockArea does error checking, too. + a_DataTypes is a bitmask of cBlockArea::baXXX constants ORed together. + */ + bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); + + // tolua_begin + + /// Spawns item pickups for each item in the list. May compress pickups if too many entities: + void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false); + + /// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified: + void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false); + + /// Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided + void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff = 1); + + // tolua_end + + /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType + void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); + + /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. + bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); + + // tolua_begin + bool DigBlock (int a_X, int a_Y, int a_Z); + void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player ); + + double GetSpawnX(void) const { return m_SpawnX; } + double GetSpawnY(void) const { return m_SpawnY; } + double GetSpawnZ(void) const { return m_SpawnZ; } + + /// Wakes up the simulators for the specified block + void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Wakes up the simulators for the specified area of blocks + void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ); + + // tolua_end + + inline cSimulatorManager * GetSimulatorManager(void) { return m_SimulatorManager; } + + inline cFluidSimulator * GetWaterSimulator(void) { return m_WaterSimulator; } + inline cFluidSimulator * GetLavaSimulator (void) { return m_LavaSimulator; } + + /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true + bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true + bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback); + + /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true + bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback); + + /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true + bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback); + + /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true + bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp + + /** Does an explosion with the specified strength at the specified coordinate + a_SourceData exact type depends on the a_Source: + | esOther | void * | + | esPrimedTNT | cTNTEntity * | + | esCreeper | cCreeper * | + | esBed | cVector3i * | + | esEnderCrystal | Vector3i * | + | esGhastFireball | cGhastFireball * | + | esWitherSkullBlack | TBD | + | esWitherSkullBlue | TBD | + | esWitherBirth | TBD | + | esPlugin | void * | + */ + void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData); // tolua_export + + /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found + bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found + bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found + bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found + bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found + bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found + bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp + + /// a_Player is using block entity at [x, y, z], handle that: + void UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {m_ChunkMap->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); } // tolua_export + + /// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback + bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback); + + void GrowTreeImage(const sSetBlockVector & a_Blocks); + + // tolua_begin + + /// Grows a tree at the specified coords, either from a sapling there, or based on the biome + void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Grows a tree at the specified coords, based on the sapling meta provided + void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_SaplingMeta); + + /// Grows a tree at the specified coords, based on the biome in the place + void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini + bool GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false); + + /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config + void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) + void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + + /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config + void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value + int GetBiomeAt(int a_BlockX, int a_BlockZ); + + /// Returns the name of the world + const AString & GetName(void) const { return m_WorldName; } + + /// Returns the name of the world.ini file used by this world + const AString & GetIniFileName(void) const {return m_IniFileName; } + + // tolua_end + + inline static void AbsoluteToRelative( int & a_X, int & a_Y, int & a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) + { + // TODO: Use floor() instead of weird if statements + // Also fix Y + a_ChunkX = a_X/cChunkDef::Width; + if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; + a_ChunkY = 0; + a_ChunkZ = a_Z/cChunkDef::Width; + if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; + + a_X = a_X - a_ChunkX*cChunkDef::Width; + a_Y = a_Y - a_ChunkY*cChunkDef::Height; + a_Z = a_Z - a_ChunkZ*cChunkDef::Width; + } + + inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) + { + // TODO: Use floor() instead of weird if statements + // Also fix Y + (void)a_Y; // not unused anymore + a_ChunkX = a_X/cChunkDef::Width; + if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; + a_ChunkY = 0; + a_ChunkZ = a_Z/cChunkDef::Width; + if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; + } + + /// Saves all chunks immediately. Dangerous interface, may deadlock, use QueueSaveAllChunks() instead + void SaveAllChunks(void); + + /// Queues a task to save all chunks onto the tick thread. The prefferred way of saving chunks from external sources + void QueueSaveAllChunks(void); // tolua_export + + /// Queues a task onto the tick thread. The task object will be deleted once the task is finished + void QueueTask(cTask * a_Task); // Exported in ManualBindings.cpp + + /// Returns the number of chunks loaded + int GetNumChunks() const; // tolua_export + + /// Returns the number of chunks loaded and dirty, and in the lighting queue + void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue); + + // Various queues length queries (cannot be const, they lock their CS): + inline int GetGeneratorQueueLength (void) { return m_Generator.GetQueueLength(); } // tolua_export + inline int GetLightingQueueLength (void) { return m_Lighting.GetQueueLength(); } // tolua_export + inline int GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export + inline int GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export + + void InitializeSpawn(void); + + /// Starts threads that belong to this world + void Start(void); + + /// Stops threads that belong to this world (part of deinit) + void Stop(void); + + /// Processes the blocks queued for ticking with a delay (m_BlockTickQueue[]) + void TickQueuedBlocks(void); + + struct BlockTickQueueItem + { + int X; + int Y; + int Z; + int TicksToWait; + }; + + /// Queues the block to be ticked after the specified number of game ticks + void QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait); // tolua_export + + // tolua_begin + /// Casts a thunderbolt at the specified coords + void CastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Sets the specified weather; resets weather interval; asks and notifies plugins of the change + void SetWeather (eWeather a_NewWeather); + + /// Forces a weather change in the next game tick + void ChangeWeather (void); + + /// Returns the current weather. Instead of comparing values directly to the weather constants, use IsWeatherXXX() functions, if possible + eWeather GetWeather (void) const { return m_Weather; }; + + bool IsWeatherSunny(void) const { return (m_Weather == wSunny); } + bool IsWeatherRain (void) const { return (m_Weather == wRain); } + bool IsWeatherStorm(void) const { return (m_Weather == wStorm); } + + /// Returns true if the current weather has any precipitation - rain or storm + bool IsWeatherWet (void) const { return (m_Weather != wSunny); } + + // tolua_end + + cChunkGenerator & GetGenerator(void) { return m_Generator; } + cWorldStorage & GetStorage (void) { return m_Storage; } + cChunkMap * GetChunkMap (void) { return m_ChunkMap; } + + /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call + void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export + + int GetMaxSugarcaneHeight(void) const { return m_MaxSugarcaneHeight; } // tolua_export + int GetMaxCactusHeight (void) const { return m_MaxCactusHeight; } // tolua_export + + bool IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export + + /// Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise + int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType); // tolua_export + int SpawnMobFinalize(cMonster* a_Monster); + + /// Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise + int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed = NULL); // tolua_export + + /// Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! + int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); } + + /// Appends all usernames starting with a_Text (case-insensitive) into Results + void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results); + + /// Get the current darkness level based on the time + NIBBLETYPE GetSkyDarkness() { return m_SkyDarkness; } + +private: + + friend class cRoot; + + class cTickThread : + public cIsThread + { + typedef cIsThread super; + public: + cTickThread(cWorld & a_World); + + protected: + cWorld & m_World; + + // cIsThread overrides: + virtual void Execute(void) override; + } ; + + + AString m_WorldName; + AString m_IniFileName; + + /// Name of the storage schema used to load and save chunks + AString m_StorageSchema; + + /// The dimension of the world, used by the client to provide correct lighting scheme + eDimension m_Dimension; + + /// This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe) + MTRand m_TickRand; + + double m_SpawnX; + double m_SpawnY; + double m_SpawnZ; + + double m_WorldAgeSecs; // World age, in seconds. Is only incremented, cannot be set by plugins. + double m_TimeOfDaySecs; // Time of day in seconds. Can be adjusted. Is wrapped to zero each day. + Int64 m_WorldAge; // World age in ticks, calculated off of m_WorldAgeSecs + Int64 m_TimeOfDay; // Time in ticks, calculated off of m_TimeOfDaySecs + Int64 m_LastTimeUpdate; // The tick in which the last time update has been sent. + Int64 m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred + Int64 m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred + std::map m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed) + + NIBBLETYPE m_SkyDarkness; + + eGameMode m_GameMode; + bool m_bEnabledPVP; + bool m_IsDeepSnowEnabled; + + // The cRedstone class simulates redstone and needs access to m_RSList + // friend class cRedstone; + std::vector m_RSList; + + std::vector m_BlockTickQueue; + std::vector m_BlockTickQueueCopy; // Second is for safely removing the objects from the queue + + cSimulatorManager * m_SimulatorManager; + cSandSimulator * m_SandSimulator; + cFluidSimulator * m_WaterSimulator; + cFluidSimulator * m_LavaSimulator; + cFireSimulator * m_FireSimulator; + cRedstoneSimulator * m_RedstoneSimulator; + + cCriticalSection m_CSPlayers; + cPlayerList m_Players; + + cWorldStorage m_Storage; + + unsigned int m_MaxPlayers; + + cChunkMap * m_ChunkMap; + + bool m_bAnimals; + std::set m_AllowedMobs; + + eWeather m_Weather; + int m_WeatherInterval; + + int m_MaxCactusHeight; + int m_MaxSugarcaneHeight; + bool m_IsCactusBonemealable; + bool m_IsCarrotsBonemealable; + bool m_IsCropsBonemealable; + bool m_IsGrassBonemealable; + bool m_IsMelonStemBonemealable; + bool m_IsMelonBonemealable; + bool m_IsPotatoesBonemealable; + bool m_IsPumpkinStemBonemealable; + bool m_IsPumpkinBonemealable; + bool m_IsSaplingBonemealable; + bool m_IsSugarcaneBonemealable; + + cCriticalSection m_CSFastSetBlock; + sSetBlockList m_FastSetBlockQueue; + + cChunkGenerator m_Generator; + + cChunkSender m_ChunkSender; + cLightingThread m_Lighting; + cTickThread m_TickThread; + + /// Guards the m_Tasks + cCriticalSection m_CSTasks; + + /// Tasks that have been queued onto the tick thread; guarded by m_CSTasks + cTasks m_Tasks; + + /// Guards m_Clients + cCriticalSection m_CSClients; + + /// List of clients in this world, these will be ticked by this world + cClientHandleList m_Clients; + + /// Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them + cClientHandleList m_ClientsToRemove; + + /// Clients that are scheduled for adding, waiting for TickClients to add them + cClientHandleList m_ClientsToAdd; + + + cWorld(const AString & a_WorldName); + ~cWorld(); + + void Tick(float a_Dt); + + /// Handles the weather in each tick + void TickWeather(float a_Dt); + + /// Handles the mob spawning/moving/destroying each tick + void TickMobs(float a_Dt); + + /// Executes all tasks queued onto the tick thread + void TickQueuedTasks(void); + + /// Ticks all clients that are in this world + void TickClients(float a_Dt); + + void UpdateSkyDarkness(); + + /// Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section) + cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock); +}; // tolua_export + + + + diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp new file mode 100644 index 000000000..e55011069 --- /dev/null +++ b/src/WorldStorage/FastNBT.cpp @@ -0,0 +1,547 @@ + +// FastNBT.cpp + +// Implements the fast NBT parser and writer + +#include "Globals.h" +#include "FastNBT.h" + + + + + +// The number of NBT tags that are reserved when an NBT parsing is started. +// You can override this by using a cmdline define +#ifndef NBT_RESERVE_SIZE + #define NBT_RESERVE_SIZE 200 +#endif // NBT_RESERVE_SIZE + +#define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while (0) + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cParsedNBT: + +#define NEEDBYTES(N) \ + if (m_Length - m_Pos < N) \ + { \ + return false; \ + } + + + + + +cParsedNBT::cParsedNBT(const char * a_Data, int a_Length) : + m_Data(a_Data), + m_Length(a_Length), + m_Pos(0) +{ + m_IsValid = Parse(); +} + + + + + +bool cParsedNBT::Parse(void) +{ + if (m_Length < 3) + { + // Data too short + return false; + } + if (m_Data[0] != TAG_Compound) + { + // The top-level tag must be a Compound + return false; + } + + m_Tags.reserve(NBT_RESERVE_SIZE); + + m_Tags.push_back(cFastNBTTag(TAG_Compound, -1)); + + m_Pos = 1; + + RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength)); + RETURN_FALSE_IF_FALSE(ReadCompound()); + + return true; +} + + + + + +bool cParsedNBT::ReadString(int & a_StringStart, int & a_StringLen) +{ + NEEDBYTES(2); + a_StringStart = m_Pos + 2; + a_StringLen = ntohs(*((short *)(m_Data + m_Pos))); + if (a_StringLen < 0) + { + // Invalid string length + return false; + } + m_Pos += 2 + a_StringLen; + return true; +} + + + + + +bool cParsedNBT::ReadCompound(void) +{ + // Reads the latest tag as a compound + int ParentIdx = m_Tags.size() - 1; + int PrevSibling = -1; + while (true) + { + NEEDBYTES(1); + eTagType TagType = (eTagType)(m_Data[m_Pos]); + m_Pos++; + if (TagType == TAG_End) + { + break; + } + m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling)); + if (PrevSibling >= 0) + { + m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1; + } + else + { + m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1; + } + PrevSibling = m_Tags.size() - 1; + RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength)); + RETURN_FALSE_IF_FALSE(ReadTag()); + } // while (true) + m_Tags[ParentIdx].m_LastChild = PrevSibling; + return true; +} + + + + + +bool cParsedNBT::ReadList(eTagType a_ChildrenType) +{ + // Reads the latest tag as a list of items of type a_ChildrenType + + // Read the count: + NEEDBYTES(4); + int Count = ntohl(*((int *)(m_Data + m_Pos))); + m_Pos += 4; + if (Count < 0) + { + return false; + } + + // Read items: + int ParentIdx = m_Tags.size() - 1; + int PrevSibling = -1; + for (int i = 0; i < Count; i++) + { + m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling)); + if (PrevSibling >= 0) + { + m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1; + } + else + { + m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1; + } + PrevSibling = m_Tags.size() - 1; + RETURN_FALSE_IF_FALSE(ReadTag()); + } // for (i) + m_Tags[ParentIdx].m_LastChild = PrevSibling; + return true; +} + + + + + +#define CASE_SIMPLE_TAG(TAGTYPE, LEN) \ + case TAG_##TAGTYPE: \ + { \ + NEEDBYTES(LEN); \ + Tag.m_DataStart = m_Pos; \ + Tag.m_DataLength = LEN; \ + m_Pos += LEN; \ + return true; \ + } + +bool cParsedNBT::ReadTag(void) +{ + cFastNBTTag & Tag = m_Tags.back(); + switch (Tag.m_Type) + { + CASE_SIMPLE_TAG(Byte, 1) + CASE_SIMPLE_TAG(Short, 2) + CASE_SIMPLE_TAG(Int, 4) + CASE_SIMPLE_TAG(Long, 8) + CASE_SIMPLE_TAG(Float, 4) + CASE_SIMPLE_TAG(Double, 8) + + case TAG_String: + { + return ReadString(Tag.m_DataStart, Tag.m_DataLength); + } + + case TAG_ByteArray: + { + NEEDBYTES(4); + int len = ntohl(*((int *)(m_Data + m_Pos))); + m_Pos += 4; + if (len < 0) + { + // Invalid length + return false; + } + NEEDBYTES(len); + Tag.m_DataLength = len; + Tag.m_DataStart = m_Pos; + m_Pos += len; + return true; + } + + case TAG_List: + { + NEEDBYTES(1); + eTagType ItemType = (eTagType)m_Data[m_Pos]; + m_Pos++; + RETURN_FALSE_IF_FALSE(ReadList(ItemType)); + return true; + } + + case TAG_Compound: + { + RETURN_FALSE_IF_FALSE(ReadCompound()); + return true; + } + + case TAG_IntArray: + { + NEEDBYTES(4); + int len = ntohl(*((int *)(m_Data + m_Pos))); + m_Pos += 4; + if (len < 0) + { + // Invalid length + return false; + } + len *= 4; + NEEDBYTES(len); + Tag.m_DataLength = len; + Tag.m_DataStart = m_Pos; + m_Pos += len; + return true; + } + + default: + { + ASSERT(!"Unhandled NBT tag type"); + return false; + } + } // switch (iType) +} + +#undef CASE_SIMPLE_TAG + + + + + +int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength) const +{ + if (a_Tag < 0) + { + return -1; + } + if (m_Tags[a_Tag].m_Type != TAG_Compound) + { + return -1; + } + + if (a_NameLength == 0) + { + a_NameLength = strlen(a_Name); + } + for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling) + { + if ( + (m_Tags[Child].m_NameLength == a_NameLength) && + (memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0) + ) + { + return Child; + } + } // for Child - children of a_Tag + return -1; +} + + + + + +int cParsedNBT::FindTagByPath(int a_Tag, const AString & a_Path) const +{ + if (a_Tag < 0) + { + return -1; + } + size_t Begin = 0; + size_t Length = a_Path.length(); + int Tag = a_Tag; + for (size_t i = 0; i < Length; i++) + { + if (a_Path[i] != '\\') + { + continue; + } + Tag = FindChildByName(Tag, a_Path.c_str() + Begin, i - Begin - 1); + if (Tag < 0) + { + return -1; + } + Begin = i + 1; + } // for i - a_Path[] + + if (Begin < Length) + { + Tag = FindChildByName(Tag, a_Path.c_str() + Begin, Length - Begin); + } + return Tag; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFastNBTWriter: + +cFastNBTWriter::cFastNBTWriter(const AString & a_RootTagName) : + m_CurrentStack(0) +{ + m_Stack[0].m_Type = TAG_Compound; + m_Result.reserve(100 * 1024); + m_Result.push_back(TAG_Compound); + WriteString(a_RootTagName.data(), a_RootTagName.size()); +} + + + + + +void cFastNBTWriter::BeginCompound(const AString & a_Name) +{ + if (m_CurrentStack >= MAX_STACK) + { + ASSERT(!"Stack overflow"); + return; + } + + TagCommon(a_Name, TAG_Compound); + + ++m_CurrentStack; + m_Stack[m_CurrentStack].m_Type = TAG_Compound; +} + + + + + +void cFastNBTWriter::EndCompound(void) +{ + ASSERT(m_CurrentStack > 0); + ASSERT(IsStackTopCompound()); + + m_Result.push_back(TAG_End); + --m_CurrentStack; +} + + + + + +void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType) +{ + if (m_CurrentStack >= MAX_STACK) + { + ASSERT(!"Stack overflow"); + return; + } + + TagCommon(a_Name, TAG_List); + + m_Result.push_back((char)a_ChildrenType); + m_Result.append(4, (char)0); + + ++m_CurrentStack; + m_Stack[m_CurrentStack].m_Type = TAG_List; + m_Stack[m_CurrentStack].m_Pos = m_Result.size() - 4; + m_Stack[m_CurrentStack].m_Count = 0; + m_Stack[m_CurrentStack].m_ItemType = a_ChildrenType; +} + + + + + +void cFastNBTWriter::EndList(void) +{ + ASSERT(m_CurrentStack > 0); + ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List); + + // Update the list count: + *((int *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos)) = htonl(m_Stack[m_CurrentStack].m_Count); + + --m_CurrentStack; +} + + + + + +void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value) +{ + TagCommon(a_Name, TAG_Byte); + m_Result.push_back(a_Value); +} + + + + + +void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value) +{ + TagCommon(a_Name, TAG_Short); + Int16 Value = htons(a_Value); + m_Result.append((const char *)&Value, 2); +} + + + + + +void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value) +{ + TagCommon(a_Name, TAG_Int); + Int32 Value = htonl(a_Value); + m_Result.append((const char *)&Value, 4); +} + + + + + +void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value) +{ + TagCommon(a_Name, TAG_Long); + Int64 Value = HostToNetwork8(&a_Value); + m_Result.append((const char *)&Value, 8); +} + + + + + +void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value) +{ + TagCommon(a_Name, TAG_Float); + Int32 Value = HostToNetwork4(&a_Value); + m_Result.append((const char *)&Value, 4); +} + + + + + +void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value) +{ + TagCommon(a_Name, TAG_Double); + Int64 Value = HostToNetwork8(&a_Value); + m_Result.append((const char *)&Value, 8); +} + + + + + +void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value) +{ + TagCommon(a_Name, TAG_String); + Int16 len = htons((short)(a_Value.size())); + m_Result.append((const char *)&len, 2); + m_Result.append(a_Value.c_str(), a_Value.size()); +} + + + + + +void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements) +{ + TagCommon(a_Name, TAG_ByteArray); + Int32 len = htonl(a_NumElements); + m_Result.append((const char *)&len, 4); + m_Result.append(a_Value, a_NumElements); +} + + + + + +void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements) +{ + TagCommon(a_Name, TAG_IntArray); + Int32 len = htonl(a_NumElements); + m_Result.append((const char *)&len, 4); +#if defined(ANDROID_NDK) + // Android has alignment issues - cannot byteswap (htonl) an int that is not 32-bit-aligned, which happens in the regular version + for (size_t i = 0; i < a_NumElements; i++) + { + int Element = htonl(a_Value[i]); + m_Result.append((const char *)&Element, 4); + } +#else + int * Elements = (int *)(m_Result.data() + m_Result.size()); + m_Result.append(a_NumElements * 4, (char)0); + for (size_t i = 0; i < a_NumElements; i++) + { + Elements[i] = htonl(a_Value[i]); + } +#endif +} + + + + + +void cFastNBTWriter::Finish(void) +{ + ASSERT(m_CurrentStack == 0); + m_Result.push_back(TAG_End); +} + + + + + +void cFastNBTWriter::WriteString(const char * a_Data, short a_Length) +{ + Int16 Len = htons(a_Length); + m_Result.append((const char *)&Len, 2); + m_Result.append(a_Data, a_Length); +} + + + + diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h new file mode 100644 index 000000000..7323c29cb --- /dev/null +++ b/src/WorldStorage/FastNBT.h @@ -0,0 +1,293 @@ + +// FastNBT.h + +// Interfaces to the fast NBT parser and writer + +/* +The fast parser parses the data into a vector of cFastNBTTag structures. These structures describe the NBT tree, +but themselves are allocated in a vector, thus minimizing reallocation. +The structures have a minimal constructor, setting all member "pointers" to "invalid". + +The fast writer doesn't need a NBT tree structure built beforehand, it is commanded to open, append and close tags +(just like XML); it keeps the internal tag stack and reports errors in usage. +It directly outputs a string containing the serialized NBT data. +*/ + + + + + +#pragma once + +#include "../Endianness.h" + + + + + +enum eTagType +{ + TAG_Min = 0, // The minimum value for a tag type + TAG_End = 0, + TAG_Byte = 1, + TAG_Short = 2, + TAG_Int = 3, + TAG_Long = 4, + TAG_Float = 5, + TAG_Double = 6, + TAG_ByteArray = 7, + TAG_String = 8, + TAG_List = 9, + TAG_Compound = 10, + TAG_IntArray = 11, + TAG_Max = 11, // The maximum value for a tag type +} ; + + + + + +/** This structure is used for all NBT tags. +It contains indices to the parent array of tags, building the NBT tree this way. +Also contains indices into the data stream being parsed, used for values; +NO dynamically allocated memory is used! +Structure (all with the tree structure it describes) supports moving in memory (std::vector reallocation) +*/ +struct cFastNBTTag +{ +public: + + eTagType m_Type; + + // The following members are indices into the data stream. m_DataLength == 0 if no data available + // They must not be pointers, because the datastream may be copied into another AString object in the meantime. + int m_NameStart; + int m_NameLength; + int m_DataStart; + int m_DataLength; + + // The following members are indices into the array returned; -1 if not valid + // They must not be pointers, because pointers would not survive std::vector reallocation + int m_Parent; + int m_PrevSibling; + int m_NextSibling; + int m_FirstChild; + int m_LastChild; + + cFastNBTTag(eTagType a_Type, int a_Parent) : + m_Type(a_Type), + m_NameLength(0), + m_DataLength(0), + m_Parent(a_Parent), + m_PrevSibling(-1), + m_NextSibling(-1), + m_FirstChild(-1), + m_LastChild(-1) + { + } + + cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling) : + m_Type(a_Type), + m_NameLength(0), + m_DataLength(0), + m_Parent(a_Parent), + m_PrevSibling(a_PrevSibling), + m_NextSibling(-1), + m_FirstChild(-1), + m_LastChild(-1) + { + } +} ; + + + + + +/** Parses and contains the parsed data +Also implements data accessor functions for tree traversal and value getters +The data pointer passed in the constructor is assumed to be valid throughout the object's life. Care must be taken not to initialize from a temporary. +*/ +class cParsedNBT +{ +public: + cParsedNBT(const char * a_Data, int a_Length); + + bool IsValid(void) const {return m_IsValid; } + + int GetRoot(void) const {return 0; } + int GetFirstChild (int a_Tag) const { return m_Tags[a_Tag].m_FirstChild; } + int GetLastChild (int a_Tag) const { return m_Tags[a_Tag].m_LastChild; } + int GetNextSibling(int a_Tag) const { return m_Tags[a_Tag].m_NextSibling; } + int GetPrevSibling(int a_Tag) const { return m_Tags[a_Tag].m_PrevSibling; } + int GetDataLength (int a_Tag) const { return m_Tags[a_Tag].m_DataLength; } + + const char * GetData(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type != TAG_List); + ASSERT(m_Tags[a_Tag].m_Type != TAG_Compound); + return m_Data + m_Tags[a_Tag].m_DataStart; + } + + int FindChildByName(int a_Tag, const AString & a_Name) const + { + return FindChildByName(a_Tag, a_Name.c_str(), a_Name.length()); + } + + int FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength = 0) const; + int FindTagByPath (int a_Tag, const AString & a_Path) const; + + eTagType GetType(int a_Tag) const { return m_Tags[a_Tag].m_Type; } + + /// Returns the children type for a list tag; undefined on other tags. If list empty, returns TAG_End + eTagType GetChildrenType(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_List); + return (m_Tags[a_Tag].m_FirstChild < 0) ? TAG_End : m_Tags[m_Tags[a_Tag].m_FirstChild].m_Type; + } + + inline unsigned char GetByte(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Byte); + return (unsigned char)(m_Data[m_Tags[a_Tag].m_DataStart]); + } + + inline Int16 GetShort(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Short); + return ntohs(*((Int16 *)(m_Data + m_Tags[a_Tag].m_DataStart))); + } + + inline Int32 GetInt(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Int); + return ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart))); + } + + inline Int64 GetLong(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Long); + return NetworkToHostLong8(m_Data + m_Tags[a_Tag].m_DataStart); + } + + inline float GetFloat(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Float); + Int32 tmp = ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart))); + return *((float *)&tmp); + } + + inline double GetDouble(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Double); + return NetworkToHostDouble8(m_Data + m_Tags[a_Tag].m_DataStart); + } + + inline AString GetString(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_String); + AString res; + res.assign(m_Data + m_Tags[a_Tag].m_DataStart, m_Tags[a_Tag].m_DataLength); + return res; + } + + inline AString GetName(int a_Tag) const + { + AString res; + res.assign(m_Data + m_Tags[a_Tag].m_NameStart, m_Tags[a_Tag].m_NameLength); + return res; + } + +protected: + const char * m_Data; + int m_Length; + std::vector m_Tags; + bool m_IsValid; // True if parsing succeeded + + // Used while parsing: + int m_Pos; + + bool Parse(void); + bool ReadString(int & a_StringStart, int & a_StringLen); // Reads a simple string (2 bytes length + data), sets the string descriptors + bool ReadCompound(void); // Reads the latest tag as a compound + bool ReadList(eTagType a_ChildrenType); // Reads the latest tag as a list of items of type a_ChildrenType + bool ReadTag(void); // Reads the latest tag, depending on its m_Type setting +} ; + + + + + +class cFastNBTWriter +{ +public: + cFastNBTWriter(const AString & a_RootTagName = ""); + + void BeginCompound(const AString & a_Name); + void EndCompound(void); + + void BeginList(const AString & a_Name, eTagType a_ChildrenType); + void EndList(void); + + void AddByte (const AString & a_Name, unsigned char a_Value); + void AddShort (const AString & a_Name, Int16 a_Value); + void AddInt (const AString & a_Name, Int32 a_Value); + void AddLong (const AString & a_Name, Int64 a_Value); + void AddFloat (const AString & a_Name, float a_Value); + void AddDouble (const AString & a_Name, double a_Value); + void AddString (const AString & a_Name, const AString & a_Value); + void AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements); + void AddIntArray (const AString & a_Name, const int * a_Value, size_t a_NumElements); + + void AddByteArray(const AString & a_Name, const AString & a_Value) + { + AddByteArray(a_Name, a_Value.data(), a_Value.size()); + } + + const AString & GetResult(void) const {return m_Result; } + + void Finish(void); + +protected: + + struct sParent + { + int m_Type; // TAG_Compound or TAG_List + int m_Pos; // for TAG_List, the position of the list count + int m_Count; // for TAG_List, the element count + eTagType m_ItemType; // for TAG_List, the element type + } ; + + static const int MAX_STACK = 50; // Highliy doubtful that an NBT would be constructed this many levels deep + + // These two fields emulate a stack. A raw array is used due to speed issues - no reallocations are allowed. + sParent m_Stack[MAX_STACK]; + int m_CurrentStack; + + AString m_Result; + + bool IsStackTopCompound(void) const { return (m_Stack[m_CurrentStack].m_Type == TAG_Compound); } + + void WriteString(const char * a_Data, short a_Length); + + inline void TagCommon(const AString & a_Name, eTagType a_Type) + { + // If we're directly inside a list, check that the list is of the correct type: + ASSERT((m_Stack[m_CurrentStack].m_Type != TAG_List) || (m_Stack[m_CurrentStack].m_ItemType == a_Type)); + + if (IsStackTopCompound()) + { + // Compound: add the type and name: + m_Result.push_back((char)a_Type); + WriteString(a_Name.c_str(), (short)a_Name.length()); + } + else + { + // List: add to the counter + m_Stack[m_CurrentStack].m_Count++; + } + } +} ; + + + + diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp new file mode 100644 index 000000000..c9013b1b3 --- /dev/null +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -0,0 +1,533 @@ + +// NBTChunkSerializer.cpp + + +#include "Globals.h" +#include "NBTChunkSerializer.h" +#include "../BlockID.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DispenserEntity.h" +#include "../BlockEntities/DropperEntity.h" +#include "../BlockEntities/FurnaceEntity.h" +#include "../BlockEntities/HopperEntity.h" +#include "../BlockEntities/JukeboxEntity.h" +#include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/SignEntity.h" +#include "../ItemGrid.h" +#include "../StringCompression.h" +#include "../Entities/Entity.h" +#include "FastNBT.h" +#include "../Entities/FallingBlock.h" +#include "../Entities/Boat.h" +#include "../Entities/Minecart.h" +#include "../Mobs/Monster.h" +#include "../Entities/Pickup.h" +#include "../Entities/ProjectileEntity.h" + + + + + +cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) : + m_BiomesAreValid(false), + m_Writer(a_Writer), + m_IsTagOpen(false), + m_HasHadEntity(false), + m_HasHadBlockEntity(false), + m_IsLightValid(false) +{ +} + + + + + +void cNBTChunkSerializer::Finish(void) +{ + if (m_IsTagOpen) + { + m_Writer.EndList(); + } + + // If light not valid, reset it to all zeroes: + if (!m_IsLightValid) + { + memset(m_BlockLight, 0, sizeof(m_BlockLight)); + memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight)); + } +} + + + + + +void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName) +{ + m_Writer.BeginCompound(a_CompoundName); + m_Writer.AddShort("id", (short)(a_Item.m_ItemType)); + m_Writer.AddShort("Damage", a_Item.m_ItemDamage); + m_Writer.AddByte ("Count", a_Item.m_ItemCount); + if (a_Slot >= 0) + { + m_Writer.AddByte ("Slot", (unsigned char)a_Slot); + } + + // Write the enchantments: + if (!a_Item.m_Enchantments.IsEmpty()) + { + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + m_Writer.BeginCompound("tag"); + a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName); + m_Writer.EndCompound(); + } + + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum) +{ + int NumSlots = a_Grid.GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + const cItem & Item = a_Grid.GetSlot(i); + if (Item.IsEmpty()) + { + continue; + } + AddItem(Item, i + a_BeginSlotNum); + } // for i - chest slots[] +} + + + + + +void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID) +{ + m_Writer.AddInt ("x", a_Entity->GetPosX()); + m_Writer.AddInt ("y", a_Entity->GetPosY()); + m_Writer.AddInt ("z", a_Entity->GetPosZ()); + m_Writer.AddString("id", a_EntityTypeID); +} + + + + + +void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Chest"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Trap"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddDropperEntity(cDropperEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Dropper"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Furnace, "Furnace"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Furnace->GetContents()); + m_Writer.EndList(); + m_Writer.AddShort("BurnTime", a_Furnace->GetFuelBurnTimeLeft()); + m_Writer.AddShort("CookTime", a_Furnace->GetTimeCooked()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Hopper"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Jukebox, "RecordPlayer"); + m_Writer.AddInt("Record", a_Jukebox->GetRecord()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Note, "Music"); + m_Writer.AddByte("note", a_Note->GetPitch()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Sign, "Sign"); + m_Writer.AddString("Text1", a_Sign->GetLine(0)); + m_Writer.AddString("Text2", a_Sign->GetLine(1)); + m_Writer.AddString("Text3", a_Sign->GetLine(2)); + m_Writer.AddString("Text4", a_Sign->GetLine(3)); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName) +{ + m_Writer.AddString("id", a_ClassName); + m_Writer.BeginList("Pos", TAG_Double); + m_Writer.AddDouble("", a_Entity->GetPosX()); + m_Writer.AddDouble("", a_Entity->GetPosY()); + m_Writer.AddDouble("", a_Entity->GetPosZ()); + m_Writer.EndList(); + m_Writer.BeginList("Motion", TAG_Double); + m_Writer.AddDouble("", a_Entity->GetSpeedX()); + m_Writer.AddDouble("", a_Entity->GetSpeedY()); + m_Writer.AddDouble("", a_Entity->GetSpeedZ()); + m_Writer.EndList(); + m_Writer.BeginList("Rotation", TAG_Double); + m_Writer.AddDouble("", a_Entity->GetRotation()); + m_Writer.AddDouble("", a_Entity->GetPitch()); + m_Writer.EndList(); +} + + + + + +void cNBTChunkSerializer::AddBoatEntity(cBoat * a_Boat) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_Boat, "Boat"); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_FallingBlock, "FallingSand"); + m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType()); + m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta()); + m_Writer.AddByte("Time", 1); // Unused in MCServer, Vanilla said to need nonzero + m_Writer.AddByte("DropItem", 1); + m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart) +{ + const char * EntityClass = NULL; + switch (a_Minecart->GetPayload()) + { + case cMinecart::mpNone: EntityClass = "MinecartRideable"; break; + case cMinecart::mpChest: EntityClass = "MinecartChest"; break; + case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break; + case cMinecart::mpTNT: EntityClass = "MinecartTNT"; break; + case cMinecart::mpHopper: EntityClass = "MinecartHopper"; break; + default: + { + ASSERT(!"Unhandled minecart payload type"); + return; + } + } // switch (payload) + + m_Writer.BeginCompound(""); + AddBasicEntity(a_Minecart, EntityClass); + switch (a_Minecart->GetPayload()) + { + case cMinecart::mpChest: + { + // Add chest contents into the Items tag: + AddMinecartChestContents((cMinecartWithChest *)a_Minecart); + break; + } + + case cMinecart::mpFurnace: + { + // TODO: Add "Push" and "Fuel" tags + break; + } + } // switch (Payload) + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) +{ + // TODO +} + + + + + +void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_Pickup, "Item"); + AddItem(a_Pickup->GetItem(), -1, "Item"); + m_Writer.AddShort("Health", a_Pickup->GetHealth()); + m_Writer.AddShort("Age", a_Pickup->GetAge()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName()); + Vector3d Pos = a_Projectile->GetPosition(); + m_Writer.AddShort("xTile", (Int16)floor(Pos.x)); + m_Writer.AddShort("yTile", (Int16)floor(Pos.y)); + m_Writer.AddShort("zTile", (Int16)floor(Pos.z)); + m_Writer.AddShort("inTile", 0); // TODO: Query the block type + m_Writer.AddShort("shake", 0); // TODO: Any shake? + m_Writer.AddByte ("inGround", a_Projectile->IsInGround() ? 1 : 0); + + switch (a_Projectile->GetProjectileKind()) + { + case cProjectileEntity::pkArrow: + { + m_Writer.AddByte("inData", 0); // TODO: Query the block meta (is it needed?) + m_Writer.AddByte("pickup", ((cArrowEntity *)a_Projectile)->GetPickupState()); + m_Writer.AddDouble("damage", ((cArrowEntity *)a_Projectile)->GetDamageCoeff()); + break; + } + case cProjectileEntity::pkGhastFireball: + { + m_Writer.AddInt("ExplosionPower", 1); + // fall-through: + } + case cProjectileEntity::pkFireCharge: + case cProjectileEntity::pkWitherSkull: + case cProjectileEntity::pkEnderPearl: + { + m_Writer.BeginList("Motion", TAG_Double); + m_Writer.AddDouble("", a_Projectile->GetSpeedX()); + m_Writer.AddDouble("", a_Projectile->GetSpeedY()); + m_Writer.AddDouble("", a_Projectile->GetSpeedZ()); + m_Writer.EndList(); + break; + } + default: + { + ASSERT(!"Unsaved projectile entity!"); + } + } // switch (ProjectileKind) + cEntity * Creator = a_Projectile->GetCreator(); + if (Creator != NULL) + { + if (Creator->GetEntityType() == cEntity::etPlayer) + { + m_Writer.AddString("ownerName", ((cPlayer *)Creator)->GetName()); + } + } + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart) +{ + m_Writer.BeginList("Items", TAG_Compound); + for (int i = 0; i < cMinecartWithChest::NumSlots; i++) + { + const cItem & Item = a_Minecart->GetSlot(i); + if (Item.IsEmpty()) + { + continue; + } + AddItem(Item, i); + } + m_Writer.EndList(); +} + + + + + +bool cNBTChunkSerializer::LightIsValid(bool a_IsLightValid) +{ + m_IsLightValid = a_IsLightValid; + return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother +} + + + + + +void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) +{ + memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes)); + for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++) + { + if ((*a_BiomeMap)[i] < 255) + { + // Normal MC biome, copy as-is: + m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]); + } + else + { + // TODO: MCS-specific biome, need to map to some basic MC biome: + ASSERT(!"Unimplemented MCS-specific biome"); + return; + } + } // for i - m_BiomeMap[] + m_BiomesAreValid = true; +} + + + + + +void cNBTChunkSerializer::Entity(cEntity * a_Entity) +{ + // Add entity into NBT: + if (m_IsTagOpen) + { + if (!m_HasHadEntity) + { + m_Writer.EndList(); + m_Writer.BeginList("Entities", TAG_Compound); + } + } + else + { + m_Writer.BeginList("Entities", TAG_Compound); + } + m_IsTagOpen = true; + m_HasHadEntity = true; + + switch (a_Entity->GetEntityType()) + { + case cEntity::etBoat: AddBoatEntity ((cBoat *) a_Entity); break; + case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *) a_Entity); break; + case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break; + case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break; + case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break; + case cEntity::etProjectile: AddProjectileEntity ((cProjectileEntity *)a_Entity); break; + case cEntity::etPlayer: return; // Players aren't saved into the world + default: + { + ASSERT(!"Unhandled entity type is being saved"); + break; + } + } +} + + + + + +void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) +{ + if (m_IsTagOpen) + { + if (!m_HasHadBlockEntity) + { + m_Writer.EndList(); + m_Writer.BeginList("TileEntities", TAG_Compound); + } + } + else + { + m_Writer.BeginList("TileEntities", TAG_Compound); + } + m_IsTagOpen = true; + + // Add tile-entity into NBT: + switch (a_Entity->GetBlockType()) + { + case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break; + case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break; + case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break; + case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; + case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break; + case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break; + case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break; + default: + { + ASSERT(!"Unhandled block entity saved into Anvil"); + } + } + m_HasHadBlockEntity = true; +} + + + + diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h new file mode 100644 index 000000000..9d4ac208c --- /dev/null +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -0,0 +1,116 @@ + +// NBTChunkSerializer.h + +// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil + + + + + +#pragma once + +#include "../ChunkDef.h" + + + + + +// fwd: +class cFastNBTWriter; +class cEntity; +class cBlockEntity; +class cBoat; +class cChestEntity; +class cDispenserEntity; +class cDropperEntity; +class cFurnaceEntity; +class cHopperEntity; +class cJukeboxEntity; +class cNoteEntity; +class cSignEntity; +class cFallingBlock; +class cMinecart; +class cMinecartWithChest; +class cMinecartWithFurnace; +class cMinecartWithTNT; +class cMinecartWithHopper; +class cMonster; +class cPickup; +class cItemGrid; +class cProjectileEntity; + + + + + +class cNBTChunkSerializer : + public cChunkDataSeparateCollector +{ +public: + cChunkDef::BiomeMap m_Biomes; + unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width]; + bool m_BiomesAreValid; + + + cNBTChunkSerializer(cFastNBTWriter & a_Writer); + + /// Close NBT tags that we've opened + void Finish(void); + + bool IsLightValid(void) const {return m_IsLightValid; } + +protected: + + /* From cChunkDataSeparateCollector we inherit: + - m_BlockTypes[] + - m_BlockMetas[] + - m_BlockLight[] + - m_BlockSkyLight[] + */ + + cFastNBTWriter & m_Writer; + + bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed. + bool m_HasHadEntity; // True if any Entity has already been received and processed + bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed + bool m_IsLightValid; // True if the chunk lighting is valid + + + /// Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested. + void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = ""); + + /// Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag + void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0); + + // Block entities: + void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID); + void AddChestEntity (cChestEntity * a_Entity); + void AddDispenserEntity(cDispenserEntity * a_Entity); + void AddDropperEntity (cDropperEntity * a_Entity); + void AddFurnaceEntity (cFurnaceEntity * a_Furnace); + void AddHopperEntity (cHopperEntity * a_Entity); + void AddJukeboxEntity (cJukeboxEntity * a_Jukebox); + void AddNoteEntity (cNoteEntity * a_Note); + void AddSignEntity (cSignEntity * a_Sign); + + // Entities: + void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName); + void AddBoatEntity (cBoat * a_Boat); + void AddFallingBlockEntity(cFallingBlock * a_FallingBlock); + void AddMinecartEntity (cMinecart * a_Minecart); + void AddMonsterEntity (cMonster * a_Monster); + void AddPickupEntity (cPickup * a_Pickup); + void AddProjectileEntity (cProjectileEntity * a_Projectile); + + void AddMinecartChestContents(cMinecartWithChest * a_Minecart); + + // cChunkDataSeparateCollector overrides: + virtual bool LightIsValid(bool a_IsLightValid) override; + virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override; + virtual void Entity(cEntity * a_Entity) override; + virtual void BlockEntity(cBlockEntity * a_Entity) override; +} ; // class cNBTChunkSerializer + + + + diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp new file mode 100644 index 000000000..b2e104a78 --- /dev/null +++ b/src/WorldStorage/WSSAnvil.cpp @@ -0,0 +1,1555 @@ + +// WSSAnvil.cpp + +// Implements the cWSSAnvil class representing the Anvil world storage scheme + +#include "Globals.h" +#include "WSSAnvil.h" +#include "NBTChunkSerializer.h" +#include "../World.h" +#include "zlib.h" +#include "../BlockID.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DispenserEntity.h" +#include "../BlockEntities/DropperEntity.h" +#include "../BlockEntities/FurnaceEntity.h" +#include "../BlockEntities/HopperEntity.h" +#include "../BlockEntities/JukeboxEntity.h" +#include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/SignEntity.h" +#include "../Item.h" +#include "../ItemGrid.h" +#include "../StringCompression.h" +#include "FastNBT.h" +#include "../Mobs/Monster.h" +#include "../Entities/Boat.h" +#include "../Entities/FallingBlock.h" +#include "../Entities/Minecart.h" +#include "../Entities/Pickup.h" +#include "../Entities/ProjectileEntity.h" + + + + + +/** If defined, the BlockSkyLight values will be copied over to BlockLight upon chunk saving, +thus making skylight visible in Minutor's Lighting mode +*/ +// #define DEBUG_SKYLIGHT + +/** Maximum number of MCA files that are cached in memory. +Since only the header is actually in the memory, this number can be high, but still, each file means an OS FS handle. +*/ +#define MAX_MCA_FILES 32 + +/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities +#define CHUNK_INFLATE_MAX 256 KiB + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSAnvil: + +cWSSAnvil::cWSSAnvil(cWorld * a_World) : + super(a_World) +{ + // Create a level.dat file for mapping tools, if it doesn't already exist: + AString fnam; + Printf(fnam, "%s/level.dat", a_World->GetName().c_str()); + if (!cFile::Exists(fnam)) + { + cFastNBTWriter Writer; + Writer.BeginCompound(""); + Writer.AddInt("SpawnX", (int)(a_World->GetSpawnX())); + Writer.AddInt("SpawnY", (int)(a_World->GetSpawnY())); + Writer.AddInt("SpawnZ", (int)(a_World->GetSpawnZ())); + Writer.EndCompound(); + Writer.Finish(); + + #ifdef _DEBUG + cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); + ASSERT(TestParse.IsValid()); + #endif // _DEBUG + + gzFile gz = gzopen((FILE_IO_PREFIX + fnam).c_str(), "wb"); + if (gz != NULL) + { + gzwrite(gz, Writer.GetResult().data(), Writer.GetResult().size()); + } + gzclose(gz); + } +} + + + + + +cWSSAnvil::~cWSSAnvil() +{ + cCSLock Lock(m_CS); + for (cMCAFiles::iterator itr = m_Files.begin(); itr != m_Files.end(); ++itr) + { + delete *itr; + } // for itr - m_Files[] +} + + + + + +bool cWSSAnvil::LoadChunk(const cChunkCoords & a_Chunk) +{ + AString ChunkData; + if (!GetChunkData(a_Chunk, ChunkData)) + { + // The reason for failure is already printed in GetChunkData() + return false; + } + + return LoadChunkFromData(a_Chunk, ChunkData); +} + + + + + +bool cWSSAnvil::SaveChunk(const cChunkCoords & a_Chunk) +{ + AString ChunkData; + if (!SaveChunkToData(a_Chunk, ChunkData)) + { + LOGWARNING("Cannot serialize chunk [%d, %d] into data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + if (!SetChunkData(a_Chunk, ChunkData)) + { + LOGWARNING("Cannot store chunk [%d, %d] data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + + // Everything successful + return true; +} + + + + + +bool cWSSAnvil::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) +{ + cCSLock Lock(m_CS); + cMCAFile * File = LoadMCAFile(a_Chunk); + if (File == NULL) + { + return false; + } + return File->GetChunkData(a_Chunk, a_Data); +} + + + + + +bool cWSSAnvil::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) +{ + cCSLock Lock(m_CS); + cMCAFile * File = LoadMCAFile(a_Chunk); + if (File == NULL) + { + return false; + } + return File->SetChunkData(a_Chunk, a_Data); +} + + + + + +cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) +{ + // ASSUME m_CS is locked + ASSERT(m_CS.IsLocked()); + + const int RegionX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32); + const int RegionZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32); + ASSERT(a_Chunk.m_ChunkX - RegionX * 32 >= 0); + ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 >= 0); + ASSERT(a_Chunk.m_ChunkX - RegionX * 32 < 32); + ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 < 32); + + // Is it already cached? + for (cMCAFiles::iterator itr = m_Files.begin(); itr != m_Files.end(); ++itr) + { + if (((*itr) != NULL) && ((*itr)->GetRegionX() == RegionX) && ((*itr)->GetRegionZ() == RegionZ)) + { + // Move the file to front and return it: + cMCAFile * f = *itr; + if (itr != m_Files.begin()) + { + m_Files.erase(itr); + m_Files.push_front(f); + } + return f; + } + } + + // Load it anew: + AString FileName; + Printf(FileName, "%s/region", m_World->GetName().c_str()); + cFile::CreateFolder(FILE_IO_PREFIX + FileName); + AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ); + cMCAFile * f = new cMCAFile(FileName, RegionX, RegionZ); + if (f == NULL) + { + return NULL; + } + m_Files.push_front(f); + + // If there are too many MCA files cached, delete the last one used: + if (m_Files.size() > MAX_MCA_FILES) + { + delete m_Files.back(); + m_Files.pop_back(); + } + return f; +} + + + + + +bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data) +{ + // Decompress the data: + char Uncompressed[CHUNK_INFLATE_MAX]; + z_stream strm; + strm.zalloc = (alloc_func)NULL; + strm.zfree = (free_func)NULL; + strm.opaque = NULL; + inflateInit(&strm); + strm.next_out = (Bytef *)Uncompressed; + strm.avail_out = sizeof(Uncompressed); + strm.next_in = (Bytef *)a_Data.data(); + strm.avail_in = a_Data.size(); + int res = inflate(&strm, Z_FINISH); + inflateEnd(&strm); + if (res != Z_STREAM_END) + { + return false; + } + + // Parse the NBT data: + cParsedNBT NBT(Uncompressed, strm.total_out); + if (!NBT.IsValid()) + { + // NBT Parsing failed + return false; + } + + // Load the data from NBT: + return LoadChunkFromNBT(a_Chunk, NBT); +} + + + + + +bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data) +{ + cFastNBTWriter Writer; + if (!SaveChunkToNBT(a_Chunk, Writer)) + { + LOGWARNING("Cannot save chunk [%d, %d] to NBT", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + Writer.Finish(); + + CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data); + return true; +} + + + + + +bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT) +{ + // The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data) + cChunkDef::BlockTypes BlockTypes; + cChunkDef::BlockNibbles MetaData; + cChunkDef::BlockNibbles BlockLight; + cChunkDef::BlockNibbles SkyLight; + + memset(BlockTypes, E_BLOCK_AIR, sizeof(BlockTypes)); + memset(MetaData, 0, sizeof(MetaData)); + memset(SkyLight, 0xff, sizeof(SkyLight)); // By default, data not present in the NBT means air, which means full skylight + memset(BlockLight, 0x00, sizeof(BlockLight)); + + // Load the blockdata, blocklight and skylight: + int Level = a_NBT.FindChildByName(0, "Level"); + if (Level < 0) + { + return false; + } + int Sections = a_NBT.FindChildByName(Level, "Sections"); + if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound)) + { + return false; + } + for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child)) + { + int y = 0; + int SectionY = a_NBT.FindChildByName(Child, "Y"); + if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte)) + { + continue; + } + y = a_NBT.GetByte(SectionY); + if ((y < 0) || (y > 15)) + { + continue; + } + CopyNBTData(a_NBT, Child, "Blocks", (char *)&(BlockTypes[y * 4096]), 4096); + CopyNBTData(a_NBT, Child, "Data", (char *)&(MetaData[y * 2048]), 2048); + CopyNBTData(a_NBT, Child, "SkyLight", (char *)&(SkyLight[y * 2048]), 2048); + CopyNBTData(a_NBT, Child, "BlockLight", (char *)&(BlockLight[y * 2048]), 2048); + } // for itr - LevelSections[] + + // Load the biomes from NBT, if present and valid. First try MCS-style, then Vanilla-style: + cChunkDef::BiomeMap BiomeMap; + cChunkDef::BiomeMap * Biomes = LoadBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "MCSBiomes")); + if (Biomes == NULL) + { + // MCS-style biomes not available, load vanilla-style: + Biomes = LoadVanillaBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "Biomes")); + } + + // Load the entities from NBT: + cEntityList Entities; + cBlockEntityList BlockEntities; + LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities")); + LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"), BlockTypes, MetaData); + + bool IsLightValid = (a_NBT.FindChildByName(Level, "MCSIsLightValid") > 0); + + /* + // Uncomment this block for really cool stuff :) + // DEBUG magic: Invert the underground, so that we can see the MC generator in action :) + bool ShouldInvert[cChunkDef::Width * cChunkDef::Width]; + memset(ShouldInvert, 0, sizeof(ShouldInvert)); + for (int y = cChunkDef::Height - 1; y >= 0; y--) + { + for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) + { + int Index = cChunkDef::MakeIndexNoCheck(x, y, z); + if (ShouldInvert[x + cChunkDef::Width * z]) + { + BlockTypes[Index] = (BlockTypes[Index] == E_BLOCK_AIR) ? E_BLOCK_STONE : E_BLOCK_AIR; + } + else + { + switch (BlockTypes[Index]) + { + case E_BLOCK_AIR: + case E_BLOCK_LEAVES: + { + // nothing needed + break; + } + default: + { + ShouldInvert[x + cChunkDef::Width * z] = true; + } + } + BlockTypes[Index] = E_BLOCK_AIR; + } + } + } // for y + //*/ + + m_World->SetChunkData( + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, + BlockTypes, MetaData, + IsLightValid ? BlockLight : NULL, + IsLightValid ? SkyLight : NULL, + NULL, Biomes, + Entities, BlockEntities, + false + ); + return true; +} + + + + +void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length) +{ + int Child = a_NBT.FindChildByName(a_Tag, a_ChildName); + if ((Child >= 0) && (a_NBT.GetType(Child) == TAG_ByteArray) && (a_NBT.GetDataLength(Child) == a_Length)) + { + memcpy(a_Destination, a_NBT.GetData(Child), a_Length); + } +} + + + + + +bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer) +{ + a_Writer.BeginCompound("Level"); + a_Writer.AddInt("xPos", a_Chunk.m_ChunkX); + a_Writer.AddInt("zPos", a_Chunk.m_ChunkZ); + cNBTChunkSerializer Serializer(a_Writer); + if (!m_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer)) + { + LOGWARNING("Cannot get chunk [%d, %d] data for NBT saving", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + Serializer.Finish(); // Close NBT tags + + // Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray): + if (Serializer.m_BiomesAreValid) + { + a_Writer.AddByteArray("Biomes", (const char *)(Serializer.m_VanillaBiomes), ARRAYCOUNT(Serializer.m_VanillaBiomes)); + a_Writer.AddIntArray ("MCSBiomes", (const int *)(Serializer.m_Biomes), ARRAYCOUNT(Serializer.m_Biomes)); + } + + // Save blockdata: + a_Writer.BeginList("Sections", TAG_Compound); + int SliceSizeBlock = cChunkDef::Width * cChunkDef::Width * 16; + int SliceSizeNibble = SliceSizeBlock / 2; + const char * BlockTypes = (const char *)(Serializer.m_BlockTypes); + const char * BlockMetas = (const char *)(Serializer.m_BlockMetas); + #ifdef DEBUG_SKYLIGHT + const char * BlockLight = (const char *)(Serializer.m_BlockSkyLight); + #else + const char * BlockLight = (const char *)(Serializer.m_BlockLight); + #endif + const char * BlockSkyLight = (const char *)(Serializer.m_BlockSkyLight); + for (int Y = 0; Y < 16; Y++) + { + a_Writer.BeginCompound(""); + a_Writer.AddByteArray("Blocks", BlockTypes + Y * SliceSizeBlock, SliceSizeBlock); + a_Writer.AddByteArray("Data", BlockMetas + Y * SliceSizeNibble, SliceSizeNibble); + a_Writer.AddByteArray("SkyLight", BlockSkyLight + Y * SliceSizeNibble, SliceSizeNibble); + a_Writer.AddByteArray("BlockLight", BlockLight + Y * SliceSizeNibble, SliceSizeNibble); + a_Writer.AddByte("Y", (unsigned char)Y); + a_Writer.EndCompound(); + } + a_Writer.EndList(); // "Sections" + + // Store the information that the lighting is valid. + // For compatibility reason, the default is "invalid" (missing) - this means older data is re-lighted upon loading. + if (Serializer.IsLightValid()) + { + a_Writer.AddByte("MCSIsLightValid", 1); + } + + a_Writer.EndCompound(); // "Level" + return true; +} + + + + + +cChunkDef::BiomeMap * cWSSAnvil::LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_ByteArray)) + { + return NULL; + } + if (a_NBT.GetDataLength(a_TagIdx) != 16 * 16) + { + // The biomes stored don't match in size + return NULL; + } + const unsigned char * VanillaBiomeData = (const unsigned char *)(a_NBT.GetData(a_TagIdx)); + for (int i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++) + { + if ((VanillaBiomeData)[i] == 0xff) + { + // Unassigned biomes + return NULL; + } + (*a_BiomeMap)[i] = (EMCSBiome)(VanillaBiomeData[i]); + } + return a_BiomeMap; +} + + + + + +cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_IntArray)) + { + return NULL; + } + if (a_NBT.GetDataLength(a_TagIdx) != sizeof(*a_BiomeMap)) + { + // The biomes stored don't match in size + return NULL; + } + const int * BiomeData = (const int *)(a_NBT.GetData(a_TagIdx)); + for (int i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++) + { + (*a_BiomeMap)[i] = (EMCSBiome)(ntohl(BiomeData[i])); + if ((*a_BiomeMap)[i] == 0xff) + { + // Unassigned biomes + return NULL; + } + } + return a_BiomeMap; +} + + + + + +void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List)) + { + return; + } + + for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + if (a_NBT.GetType(Child) != TAG_Compound) + { + continue; + } + int sID = a_NBT.FindChildByName(Child, "id"); + if (sID < 0) + { + continue; + } + LoadEntityFromNBT(a_Entities, a_NBT, Child, a_NBT.GetData(sID), a_NBT.GetDataLength(sID)); + } // for Child - a_NBT[] +} + + + + + +void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List)) + { + return; + } + + for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + if (a_NBT.GetType(Child) != TAG_Compound) + { + continue; + } + int sID = a_NBT.FindChildByName(Child, "id"); + if (sID < 0) + { + continue; + } + if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0) + { + LoadChestFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0) + { + LoadDropperFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0) + { + LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas); + } + else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0) + { + LoadHopperFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0) + { + LoadNoteFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "RecordPlayer", a_NBT.GetDataLength(sID)) == 0) + { + LoadJukeboxFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Sign", a_NBT.GetDataLength(sID)) == 0) + { + LoadSignFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0) + { + LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child); + } + // TODO: Other block entities + } // for Child - tag children +} + + + + + +bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int ID = a_NBT.FindChildByName(a_TagIdx, "id"); + if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short)) + { + return false; + } + a_Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID)); + + int Damage = a_NBT.FindChildByName(a_TagIdx, "Damage"); + if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short)) + { + return false; + } + a_Item.m_ItemDamage = a_NBT.GetShort(Damage); + + int Count = a_NBT.FindChildByName(a_TagIdx, "Count"); + if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte)) + { + return false; + } + a_Item.m_ItemCount = a_NBT.GetByte(Count); + + // Find the "tag" tag, used for enchantments and other extra data + int TagTag = a_NBT.FindChildByName(a_TagIdx, "tag"); + if (TagTag <= 0) + { + // No extra data + return true; + } + + // Load enchantments: + const char * EnchName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + int EnchTag = a_NBT.FindChildByName(TagTag, EnchName); + if (EnchTag > 0) + { + a_Item.m_Enchantments.ParseFromNBT(a_NBT, EnchTag); + } + + return true; +} + + + + + +void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int a_SlotOffset) +{ + int NumSlots = a_ItemGrid.GetNumSlots(); + for (int Child = a_NBT.GetFirstChild(a_ItemsTagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + int SlotTag = a_NBT.FindChildByName(Child, "Slot"); + if ((SlotTag < 0) || (a_NBT.GetType(SlotTag) != TAG_Byte)) + { + continue; + } + int SlotNum = (int)(a_NBT.GetByte(SlotTag)) - a_SlotOffset; + if ((SlotNum < 0) || (SlotNum >= NumSlots)) + { + // SlotNum outside of the range + continue; + } + cItem Item; + if (LoadItemFromNBT(Item, a_NBT, Child)) + { + a_ItemGrid.SetSlot(SlotNum, Item); + } + } // for itr - ItemDefs[] +} + + + + + +void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this + } + std::auto_ptr Chest(new cChestEntity(x, y, z, m_World)); + LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items); + a_BlockEntities.push_back(Chest.release()); +} + + + + + +void cWSSAnvil::LoadDispenserFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this + } + std::auto_ptr Dispenser(new cDispenserEntity(x, y, z, m_World)); + LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items); + a_BlockEntities.push_back(Dispenser.release()); +} + + + + + +void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this + } + std::auto_ptr Dropper(new cDropperEntity(x, y, z, m_World)); + LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items); + a_BlockEntities.push_back(Dropper.release()); +} + + + + + +void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this + } + + // Convert coords to relative: + int RelX = x; + int RelZ = z; + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(RelX, y, RelZ, ChunkX, ChunkZ); + + // Create the furnace entity, with proper BlockType and BlockMeta info: + BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, y, RelZ); + NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, y, RelZ); + std::auto_ptr Furnace(new cFurnaceEntity(x, y, z, BlockType, BlockMeta, m_World)); + + // Load slots: + for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + int Slot = a_NBT.FindChildByName(Child, "Slot"); + if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte)) + { + continue; + } + cItem Item; + if (LoadItemFromNBT(Item, a_NBT, Child)) + { + Furnace->SetSlot(a_NBT.GetByte(Slot), Item); + } + } // for itr - ItemDefs[] + + // Load burn time: + int BurnTime = a_NBT.FindChildByName(a_TagIdx, "BurnTime"); + if (BurnTime >= 0) + { + Int16 bt = a_NBT.GetShort(BurnTime); + // Anvil doesn't store the time that the fuel can burn. We simply "reset" the current value to be the 100% + Furnace->SetBurnTimes(bt, 0); + } + + // Load cook time: + int CookTime = a_NBT.FindChildByName(a_TagIdx, "CookTime"); + if (CookTime >= 0) + { + Int16 ct = a_NBT.GetShort(CookTime); + // Anvil doesn't store the time that an item takes to cook. We simply use the default - 10 seconds (200 ticks) + Furnace->SetCookTimes(200, ct); + } + + // Restart cooking: + Furnace->ContinueCooking(); + a_BlockEntities.push_back(Furnace.release()); +} + + + + + +void cWSSAnvil::LoadHopperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this + } + std::auto_ptr Hopper(new cHopperEntity(x, y, z, m_World)); + LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items); + a_BlockEntities.push_back(Hopper.release()); +} + + + + + +void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + std::auto_ptr Jukebox(new cJukeboxEntity(x, y, z, m_World)); + int Record = a_NBT.FindChildByName(a_TagIdx, "Record"); + if (Record >= 0) + { + Jukebox->SetRecord(a_NBT.GetInt(Record)); + } + a_BlockEntities.push_back(Jukebox.release()); +} + + + + + +void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + std::auto_ptr Note(new cNoteEntity(x, y, z, m_World)); + int note = a_NBT.FindChildByName(a_TagIdx, "note"); + if (note >= 0) + { + Note->SetPitch(a_NBT.GetByte(note)); + } + a_BlockEntities.push_back(Note.release()); +} + + + + + +void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + std::auto_ptr Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World)); + + int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1"); + if (currentLine >= 0) + { + Sign->SetLine(0, a_NBT.GetString(currentLine)); + } + + currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2"); + if (currentLine >= 0) + { + Sign->SetLine(1, a_NBT.GetString(currentLine)); + } + + currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3"); + if (currentLine >= 0) + { + Sign->SetLine(2, a_NBT.GetString(currentLine)); + } + + currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4"); + if (currentLine >= 0) + { + Sign->SetLine(3, a_NBT.GetString(currentLine)); + } + + a_BlockEntities.push_back(Sign.release()); +} + + + + + +void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength) +{ + if (strncmp(a_IDTag, "Boat", a_IDTagLength) == 0) + { + LoadBoatFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "FallingBlock", a_IDTagLength) == 0) + { + LoadFallingBlockFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Minecart", a_IDTagLength) == 0) + { + // It is a minecart, old style, find out the type: + int TypeTag = a_NBT.FindChildByName(a_EntityTagIdx, "Type"); + if ((TypeTag < 0) || (a_NBT.GetType(TypeTag) != TAG_Int)) + { + return; + } + switch (a_NBT.GetInt(TypeTag)) + { + case 0: LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Rideable minecart + case 1: LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with chest + case 2: LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with furnace + case 3: LoadMinecartTFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with TNT + case 4: LoadMinecartHFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with Hopper + } + } + else if (strncmp(a_IDTag, "MinecartRideable", a_IDTagLength) == 0) + { + LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "MinecartChest", a_IDTagLength) == 0) + { + LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "MinecartFurnace", a_IDTagLength) == 0) + { + LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "MinecartTNT", a_IDTagLength) == 0) + { + LoadMinecartTFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "MinecartHopper", a_IDTagLength) == 0) + { + LoadMinecartHFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Item", a_IDTagLength) == 0) + { + LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Arrow", a_IDTagLength) == 0) + { + LoadArrowFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Snowball", a_IDTagLength) == 0) + { + LoadSnowballFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Egg", a_IDTagLength) == 0) + { + LoadEggFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Fireball", a_IDTagLength) == 0) + { + LoadFireballFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "SmallFireball", a_IDTagLength) == 0) + { + LoadFireChargeFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "ThrownEnderpearl", a_IDTagLength) == 0) + { + LoadThrownEnderpearlFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + // TODO: other entities +} + + + + + +void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Boat(new cBoat(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Boat.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Boat.release()); +} + + + + + +void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + // TODO +} + + + + + +void cWSSAnvil::LoadMinecartRFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Minecart(new cEmptyMinecart(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this + } + std::auto_ptr Minecart(new cMinecartWithChest(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + int Slot = a_NBT.FindChildByName(Child, "Slot"); + if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte)) + { + continue; + } + cItem Item; + if (LoadItemFromNBT(Item, a_NBT, Child)) + { + Minecart->SetSlot(a_NBT.GetByte(Slot), Item); + } + } // for itr - ItemDefs[] + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Minecart(new cMinecartWithFurnace(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + + // TODO: Load the Push and Fuel tags + + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Minecart(new cMinecartWithTNT(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + + // TODO: Everything to do with TNT carts + + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadMinecartHFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Minecart(new cMinecartWithHopper(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + + // TODO: Everything to do with hopper carts + + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int ItemTag = a_NBT.FindChildByName(a_TagIdx, "Item"); + if ((ItemTag < 0) || (a_NBT.GetType(ItemTag) != TAG_Compound)) + { + return; + } + cItem Item; + if (!LoadItemFromNBT(Item, a_NBT, ItemTag)) + { + return; + } + std::auto_ptr Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false + if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Pickup.release()); +} + + + + + +void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Arrow(new cArrowEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Load pickup state: + int PickupIdx = a_NBT.FindChildByName(a_TagIdx, "pickup"); + if (PickupIdx > 0) + { + Arrow->SetPickupState((cArrowEntity::ePickupState)a_NBT.GetByte(PickupIdx)); + } + else + { + // Try the older "player" tag: + int PlayerIdx = a_NBT.FindChildByName(a_TagIdx, "player"); + if (PlayerIdx > 0) + { + Arrow->SetPickupState((a_NBT.GetByte(PlayerIdx) == 0) ? cArrowEntity::psNoPickup : cArrowEntity::psInSurvivalOrCreative); + } + } + + // Load damage: + int DamageIdx = a_NBT.FindChildByName(a_TagIdx, "damage"); + if (DamageIdx > 0) + { + Arrow->SetDamageCoeff(a_NBT.GetDouble(DamageIdx)); + } + + // Store the new arrow in the entities list: + a_Entities.push_back(Arrow.release()); +} + + + + + +void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Snowball(new cThrownSnowballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Snowball.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new snowball in the entities list: + a_Entities.push_back(Snowball.release()); +} + + + + + +void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Egg(new cThrownEggEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Egg.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new egg in the entities list: + a_Entities.push_back(Egg.release()); +} + + + + + +void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Fireball(new cGhastFireballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Fireball.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new fireball in the entities list: + a_Entities.push_back(Fireball.release()); +} + + + + + +void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr FireCharge(new cFireChargeEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*FireCharge.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new FireCharge in the entities list: + a_Entities.push_back(FireCharge.release()); +} + + + + + +void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Enderpearl(new cThrownEnderPearlEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Enderpearl.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new enderpearl in the entities list: + a_Entities.push_back(Enderpearl.release()); +} + + + + + +bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx) +{ + double Pos[3]; + if (!LoadDoublesListFromNBT(Pos, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Pos"))) + { + return false; + } + a_Entity.SetPosition(Pos[0], Pos[1], Pos[2]); + + double Speed[3]; + if (!LoadDoublesListFromNBT(Speed, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Motion"))) + { + return false; + } + a_Entity.SetSpeed(Speed[0], Speed[1], Speed[2]); + + double Rotation[3]; + if (!LoadDoublesListFromNBT(Rotation, 2, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Rotation"))) + { + return false; + } + a_Entity.SetRotation(Rotation[0]); + a_Entity.SetRoll (Rotation[1]); + + return true; +} + + + + + +bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if (!LoadEntityBaseFromNBT(a_Entity, a_NBT, a_TagIdx)) + { + return false; + } + + bool IsInGround = false; + int InGroundIdx = a_NBT.FindChildByName(a_TagIdx, "inGround"); + if (InGroundIdx > 0) + { + IsInGround = (a_NBT.GetByte(InGroundIdx) != 0); + } + a_Entity.SetIsInGround(IsInGround); + + // TODO: Load inTile, TileCoords + + return true; +} + + + + + +bool cWSSAnvil::LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List) || (a_NBT.GetChildrenType(a_TagIdx) != TAG_Double)) + { + return false; + } + int idx = 0; + for (int Tag = a_NBT.GetFirstChild(a_TagIdx); (Tag > 0) && (idx < a_NumDoubles); Tag = a_NBT.GetNextSibling(Tag), ++idx) + { + a_Doubles[idx] = a_NBT.GetDouble(Tag); + } // for Tag - PosTag[] + return (idx == a_NumDoubles); // Did we read enough doubles? +} + + + + + +bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z) +{ + int x = a_NBT.FindChildByName(a_TagIdx, "x"); + if ((x < 0) || (a_NBT.GetType(x) != TAG_Int)) + { + return false; + } + int y = a_NBT.FindChildByName(a_TagIdx, "y"); + if ((y < 0) || (a_NBT.GetType(y) != TAG_Int)) + { + return false; + } + int z = a_NBT.FindChildByName(a_TagIdx, "z"); + if ((z < 0) || (a_NBT.GetType(z) != TAG_Int)) + { + return false; + } + a_X = a_NBT.GetInt(x); + a_Y = a_NBT.GetInt(y); + a_Z = a_NBT.GetInt(z); + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSAnvil::cMCAFile: + +cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ) : + m_RegionX(a_RegionX), + m_RegionZ(a_RegionZ), + m_FileName(a_FileName) +{ +} + + + + + +bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading) +{ + if (m_File.IsOpen()) + { + // Already open + return true; + } + + if (a_IsForReading) + { + if (!cFile::Exists(m_FileName)) + { + // We want to read and the file doesn't exist. Fail. + return false; + } + } + + if (!m_File.Open(m_FileName, cFile::fmReadWrite)) + { + // The file failed to open + return false; + } + + // Load the header: + if (m_File.Read(m_Header, sizeof(m_Header)) != sizeof(m_Header)) + { + // Cannot read the header - perhaps the file has just been created? + // Try writing a NULL header (both chunk offsets and timestamps): + memset(m_Header, 0, sizeof(m_Header)); + if ( + (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Real header - chunk offsets + (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) // Bogus data for the chunk timestamps + ) + { + LOGWARNING("Cannot process MCA header in file \"%s\", chunks in that file will be lost", m_FileName.c_str()); + m_File.Close(); + return false; + } + } + return true; +} + + + + + +bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) +{ + if (!OpenFile(true)) + { + return false; + } + + int LocalX = a_Chunk.m_ChunkX % 32; + if (LocalX < 0) + { + LocalX = 32 + LocalX; + } + int LocalZ = a_Chunk.m_ChunkZ % 32; + if (LocalZ < 0) + { + LocalZ = 32 + LocalZ; + } + unsigned ChunkLocation = ntohl(m_Header[LocalX + 32 * LocalZ]); + unsigned ChunkOffset = ChunkLocation >> 8; + + m_File.Seek(ChunkOffset * 4096); + + int ChunkSize = 0; + if (m_File.Read(&ChunkSize, 4) != 4) + { + return false; + } + ChunkSize = ntohl(ChunkSize); + char CompressionType = 0; + if (m_File.Read(&CompressionType, 1) != 1) + { + return false; + } + if (CompressionType != 2) + { + // Chunk is in an unknown compression + return false; + } + ChunkSize--; + + // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly + a_Data.assign(ChunkSize, '\0'); + return (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize); +} + + + + + +bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) +{ + if (!OpenFile(false)) + { + LOGWARNING("Cannot save chunk [%d, %d], opening file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + + int LocalX = a_Chunk.m_ChunkX % 32; + if (LocalX < 0) + { + LocalX = 32 + LocalX; + } + int LocalZ = a_Chunk.m_ChunkZ % 32; + if (LocalZ < 0) + { + LocalZ = 32 + LocalZ; + } + + unsigned ChunkSector = FindFreeLocation(LocalX, LocalZ, a_Data); + + // Store the chunk data: + m_File.Seek(ChunkSector * 4096); + unsigned ChunkSize = htonl(a_Data.size() + 1); + if (m_File.Write(&ChunkSize, 4) != 4) + { + LOGWARNING("Cannot save chunk [%d, %d], writing(1) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + char CompressionType = 2; + if (m_File.Write(&CompressionType, 1) != 1) + { + LOGWARNING("Cannot save chunk [%d, %d], writing(2) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + if (m_File.Write(a_Data.data(), a_Data.size()) != (int)(a_Data.size())) + { + LOGWARNING("Cannot save chunk [%d, %d], writing(3) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + + // Store the header: + ChunkSize = (a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number + ASSERT(ChunkSize < 256); + m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize); + if (m_File.Seek(0) < 0) + { + LOGWARNING("Cannot save chunk [%d, %d], seeking in file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + if (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) + { + LOGWARNING("Cannot save chunk [%d, %d], writing header to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + + return true; +} + + + + + +unsigned cWSSAnvil::cMCAFile::FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data) +{ + // See if it fits the current location: + unsigned ChunkLocation = ntohl(m_Header[a_LocalX + 32 * a_LocalZ]); + unsigned ChunkLen = ChunkLocation & 0xff; + if (a_Data.size() + MCA_CHUNK_HEADER_LENGTH <= (ChunkLen * 4096)) + { + return ChunkLocation >> 8; + } + + // Doesn't fit, append to the end of file (we're wasting a lot of space, TODO: fix this later) + unsigned MaxLocation = 2 << 8; // Minimum sector is #2 - after the headers + for (int i = 0; i < ARRAYCOUNT(m_Header); i++) + { + ChunkLocation = ntohl(m_Header[i]); + ChunkLocation = ChunkLocation + ((ChunkLocation & 0xff) << 8); // Add the number of sectors used; don't care about the 4th byte + if (MaxLocation < ChunkLocation) + { + MaxLocation = ChunkLocation; + } + } // for i - m_Header[] + return MaxLocation >> 8; +} + + + + diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h new file mode 100644 index 000000000..7685d2236 --- /dev/null +++ b/src/WorldStorage/WSSAnvil.h @@ -0,0 +1,184 @@ + +// WSSAnvil.h + +// Interfaces to the cWSSAnvil class representing the Anvil world storage scheme + + + + +#pragma once + +#include "WorldStorage.h" +#include "FastNBT.h" + + + + + +// fwd: ItemGrid.h +class cItemGrid; + +class cProjectileEntity; + + + + + +enum +{ + /// Maximum number of chunks in an MCA file - also the count of the header items + MCA_MAX_CHUNKS = 32 * 32, + + /// The MCA header is 8 KiB + MCA_HEADER_SIZE = MCA_MAX_CHUNKS * 8, + + /// There are 5 bytes of header in front of each chunk + MCA_CHUNK_HEADER_LENGTH = 5, +} ; + + + + + +class cWSSAnvil : + public cWSSchema +{ + typedef cWSSchema super; + +public: + + cWSSAnvil(cWorld * a_World); + virtual ~cWSSAnvil(); + +protected: + + class cMCAFile + { + public: + + cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ); + + bool GetChunkData (const cChunkCoords & a_Chunk, AString & a_Data); + bool SetChunkData (const cChunkCoords & a_Chunk, const AString & a_Data); + bool EraseChunkData(const cChunkCoords & a_Chunk); + + int GetRegionX (void) const {return m_RegionX; } + int GetRegionZ (void) const {return m_RegionZ; } + const AString & GetFileName(void) const {return m_FileName; } + + protected: + + int m_RegionX; + int m_RegionZ; + cFile m_File; + AString m_FileName; + + // The header, copied from the file so we don't have to seek to it all the time + // First 1024 entries are chunk locations - the 3 + 1 byte sector-offset and sector-count + unsigned m_Header[MCA_MAX_CHUNKS]; + + // Chunk timestamps, following the chunk headers, are unused by MCS + + /// Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number. + unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data); + + /// Opens a MCA file either for a Read operation (fails if doesn't exist) or for a Write operation (creates new if not found) + bool OpenFile(bool a_IsForReading); + } ; + typedef std::list cMCAFiles; + + cCriticalSection m_CS; + cMCAFiles m_Files; // a MRU cache of MCA files + + /// Gets chunk data from the correct file; locks file CS as needed + bool GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data); + + /// Sets chunk data into the correct file; locks file CS as needed + bool SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data); + + /// Loads the chunk from the data (no locking needed) + bool LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data); + + /// Saves the chunk into datastream (no locking needed) + bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data); + + /// Loads the chunk from NBT data (no locking needed) + bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT); + + /// Saves the chunk into NBT data using a_Writer; returns true on success + bool SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer); + + /// Loads the chunk's biome map from vanilla-format; returns a_BiomeMap if biomes present and valid, NULL otherwise + cChunkDef::BiomeMap * LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Loads the chunk's biome map from MCS format; returns a_BiomeMap if biomes present and valid, NULL otherwise + cChunkDef::BiomeMap * LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Loads the chunk's entities from NBT data (a_Tag is the Level\\Entities list tag; may be -1) + void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_Tag); + + /// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1) + void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); + + /// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag + bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx); + + /** Loads contentents of an Items[] list tag into a cItemGrid + ItemGrid begins at the specified slot offset + Slots outside the ItemGrid range are ignored + */ + void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0); + + void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); + void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + + void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength); + + void LoadBoatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadFallingBlockFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartFFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSnowballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadEggFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadFireballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadFireChargeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Loads entity common data from the NBT compound; returns true if successful + bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Loads projectile common data from the NBT compound; returns true if successful + bool LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIx); + + /// Loads an array of doubles of the specified length from the specified NBT list tag a_TagIdx; returns true if successful + bool LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Helper function for extracting the X, Y, and Z int subtags of a NBT compound; returns true if successful + bool GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z); + + /// Gets the correct MCA file either from cache or from disk, manages the m_MCAFiles cache; assumes m_CS is locked + cMCAFile * LoadMCAFile(const cChunkCoords & a_Chunk); + + /// Copies a_Length bytes of data from the specified NBT Tag's Child into the a_Destination buffer + void CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length); + + // cWSSchema overrides: + virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; + virtual bool SaveChunk(const cChunkCoords & a_Chunk) override; + virtual const AString GetName(void) const override {return "anvil"; } +} ; + + + + diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp new file mode 100644 index 000000000..694f3ed1d --- /dev/null +++ b/src/WorldStorage/WSSCompact.cpp @@ -0,0 +1,1009 @@ + +// WSSCompact.cpp + +// Interfaces to the cWSSCompact class representing the "compact" storage schema (PAK-files) + +#include "Globals.h" +#include "WSSCompact.h" +#include "../World.h" +#include "zlib.h" +#include +#include "../StringCompression.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DispenserEntity.h" +#include "../BlockEntities/FurnaceEntity.h" +#include "../BlockEntities/JukeboxEntity.h" +#include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/SignEntity.h" + + + + + +#pragma pack(push, 1) +/// The chunk header, as stored in the file: +struct cWSSCompact::sChunkHeader +{ + int m_ChunkX; + int m_ChunkZ; + int m_CompressedSize; + int m_UncompressedSize; +} ; +#pragma pack(pop) + + + + + +/// The maximum number of PAK files that are cached +const int MAX_PAK_FILES = 16; + +/// The maximum number of unsaved chunks before the cPAKFile saves them to disk +const int MAX_DIRTY_CHUNKS = 16; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cJsonChunkSerializer: + +cJsonChunkSerializer::cJsonChunkSerializer(void) : + m_HasJsonData(false) +{ +} + + + + + +void cJsonChunkSerializer::Entity(cEntity * a_Entity) +{ + // TODO: a_Entity->SaveToJson(m_Root); +} + + + + + +void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity) +{ + const char * SaveInto = NULL; + switch (a_BlockEntity->GetBlockType()) + { + case E_BLOCK_CHEST: SaveInto = "Chests"; break; + case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break; + case E_BLOCK_DROPPER: SaveInto = "Droppers"; break; + case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break; + case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break; + case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break; + case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break; + case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break; + + default: + { + ASSERT(!"Unhandled blocktype in BlockEntities list while saving to JSON"); + break; + } + } // switch (BlockEntity->GetBlockType()) + if (SaveInto == NULL) + { + return; + } + + Json::Value val; + a_BlockEntity->SaveToJson(val); + m_Root[SaveInto].append(val); + m_HasJsonData = true; +} + + + + + +bool cJsonChunkSerializer::LightIsValid(bool a_IsLightValid) +{ + if (!a_IsLightValid) + { + return false; + } + m_Root["IsLightValid"] = true; + m_HasJsonData = true; + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSCompact: + +cWSSCompact::~cWSSCompact() +{ + for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr) + { + delete *itr; + } +} + + + + + +bool cWSSCompact::LoadChunk(const cChunkCoords & a_Chunk) +{ + AString ChunkData; + int UncompressedSize = 0; + if (!GetChunkData(a_Chunk, UncompressedSize, ChunkData)) + { + // The reason for failure is already printed in GetChunkData() + return false; + } + + return LoadChunkFromData(a_Chunk, UncompressedSize, ChunkData, m_World); +} + + + + + +bool cWSSCompact::SaveChunk(const cChunkCoords & a_Chunk) +{ + cCSLock Lock(m_CS); + + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + // For some reason we couldn't locate the file + LOG("Cannot locate a proper PAK file for chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + return f->SaveChunk(a_Chunk, m_World); +} + + + + + +cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkCoords & a_Chunk) +{ + // ASSUMES that m_CS has been locked + + // We need to retain this weird conversion code, because some edge chunks are in the wrong PAK file + const int LayerX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32); + const int LayerZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32); + + // Is it already cached? + for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr) + { + if (((*itr) != NULL) && ((*itr)->GetLayerX() == LayerX) && ((*itr)->GetLayerZ() == LayerZ)) + { + // Move the file to front and return it: + cPAKFile * f = *itr; + if (itr != m_PAKFiles.begin()) + { + m_PAKFiles.erase(itr); + m_PAKFiles.push_front(f); + } + return f; + } + } + + // Load it anew: + AString FileName; + Printf(FileName, "%s/X%i_Z%i.pak", m_World->GetName().c_str(), LayerX, LayerZ ); + cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ); + if (f == NULL) + { + return NULL; + } + m_PAKFiles.push_front(f); + + // If there are too many PAK files cached, delete the last one used: + if (m_PAKFiles.size() > MAX_PAK_FILES) + { + delete m_PAKFiles.back(); + m_PAKFiles.pop_back(); + } + return f; +} + + + + + +bool cWSSCompact::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data) +{ + cCSLock Lock(m_CS); + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + return false; + } + return f->GetChunkData(a_Chunk, a_UncompressedSize, a_Data); +} + + + + + +/* +// TODO: Rewrite saving to use the same principles as loading +bool cWSSCompact::SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data) +{ + cCSLock Lock(m_CS); + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + return false; + } + return f->SetChunkData(a_Chunk, a_UncompressedSize, a_Data); +} +*/ + + + + + +bool cWSSCompact::EraseChunkData(const cChunkCoords & a_Chunk) +{ + cCSLock Lock(m_CS); + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + return false; + } + return f->EraseChunkData(a_Chunk); +} + + + + + +void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World) +{ + // Load chests + Json::Value AllChests = a_Value.get("Chests", Json::nullValue); + if (!AllChests.empty()) + { + for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr ) + { + Json::Value & Chest = *itr; + cChestEntity * ChestEntity = new cChestEntity(0,0,0, a_World); + if (!ChestEntity->LoadFromJson( Chest ) ) + { + LOGERROR("ERROR READING CHEST FROM JSON!" ); + delete ChestEntity; + } + else + { + a_BlockEntities.push_back( ChestEntity ); + } + } // for itr - AllChests[] + } + + // Load dispensers + Json::Value AllDispensers = a_Value.get("Dispensers", Json::nullValue); + if( !AllDispensers.empty() ) + { + for( Json::Value::iterator itr = AllDispensers.begin(); itr != AllDispensers.end(); ++itr ) + { + Json::Value & Dispenser = *itr; + cDispenserEntity * DispenserEntity = new cDispenserEntity(0,0,0, a_World); + if( !DispenserEntity->LoadFromJson( Dispenser ) ) + { + LOGERROR("ERROR READING DISPENSER FROM JSON!" ); + delete DispenserEntity; + } + else + { + a_BlockEntities.push_back( DispenserEntity ); + } + } // for itr - AllDispensers[] + } + + // Load furnaces + Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue); + if( !AllFurnaces.empty() ) + { + for( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr ) + { + Json::Value & Furnace = *itr; + // TODO: The block type and meta aren't correct, there's no way to get them here + cFurnaceEntity * FurnaceEntity = new cFurnaceEntity(0, 0, 0, E_BLOCK_FURNACE, 0, a_World); + if (!FurnaceEntity->LoadFromJson(Furnace)) + { + LOGERROR("ERROR READING FURNACE FROM JSON!" ); + delete FurnaceEntity; + } + else + { + a_BlockEntities.push_back(FurnaceEntity); + } + } // for itr - AllFurnaces[] + } + + // Load signs + Json::Value AllSigns = a_Value.get("Signs", Json::nullValue); + if( !AllSigns.empty() ) + { + for( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr ) + { + Json::Value & Sign = *itr; + cSignEntity * SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0,0,0, a_World); + if ( !SignEntity->LoadFromJson( Sign ) ) + { + LOGERROR("ERROR READING SIGN FROM JSON!" ); + delete SignEntity; + } + else + { + a_BlockEntities.push_back( SignEntity ); + } + } // for itr - AllSigns[] + } + + // Load note blocks + Json::Value AllNotes = a_Value.get("Notes", Json::nullValue); + if( !AllNotes.empty() ) + { + for( Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr ) + { + Json::Value & Note = *itr; + cNoteEntity * NoteEntity = new cNoteEntity(0, 0, 0, a_World); + if ( !NoteEntity->LoadFromJson( Note ) ) + { + LOGERROR("ERROR READING NOTE BLOCK FROM JSON!" ); + delete NoteEntity; + } + else + { + a_BlockEntities.push_back( NoteEntity ); + } + } // for itr - AllNotes[] + } + + // Load jukeboxes + Json::Value AllJukeboxes = a_Value.get("Jukeboxes", Json::nullValue); + if( !AllJukeboxes.empty() ) + { + for( Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr ) + { + Json::Value & Jukebox = *itr; + cJukeboxEntity * JukeboxEntity = new cJukeboxEntity(0, 0, 0, a_World); + if ( !JukeboxEntity->LoadFromJson( Jukebox ) ) + { + LOGERROR("ERROR READING JUKEBOX FROM JSON!" ); + delete JukeboxEntity; + } + else + { + a_BlockEntities.push_back( JukeboxEntity ); + } + } // for itr - AllJukeboxes[] + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSCompact::cPAKFile + +#define READ(Var) \ + if (f.Read(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("ERROR READING %s FROM FILE %s (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \ + return; \ + } + +cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ) : + m_FileName(a_FileName), + m_LayerX(a_LayerX), + m_LayerZ(a_LayerZ), + m_NumDirty(0), + m_ChunkVersion( CHUNK_VERSION ), // Init with latest version + m_PakVersion( PAK_VERSION ) +{ + cFile f; + if (!f.Open(m_FileName, cFile::fmRead)) + { + return; + } + + // Read headers: + READ(m_PakVersion); + if (m_PakVersion != 1) + { + LOGERROR("File \"%s\" is in an unknown pak format (%d)", m_FileName.c_str(), m_PakVersion); + return; + } + + READ(m_ChunkVersion); + switch( m_ChunkVersion ) + { + case 1: + m_ChunkSize.Set(16, 128, 16); + break; + case 2: + case 3: + m_ChunkSize.Set(16, 256, 16); + break; + default: + LOGERROR("File \"%s\" is in an unknown chunk format (%d)", m_FileName.c_str(), m_ChunkVersion); + return; + }; + + short NumChunks = 0; + READ(NumChunks); + + // Read chunk headers: + for (int i = 0; i < NumChunks; i++) + { + sChunkHeader * Header = new sChunkHeader; + READ(*Header); + m_ChunkHeaders.push_back(Header); + } // for i - chunk headers + + // Read chunk data: + if (f.ReadRestOfFile(m_DataContents) == -1) + { + LOGERROR("Cannot read file \"%s\" contents", m_FileName.c_str()); + return; + } + + if( m_ChunkVersion == 1 ) // Convert chunks to version 2 + { + UpdateChunk1To2(); + } +#if AXIS_ORDER == AXIS_ORDER_XZY + if( m_ChunkVersion == 2 ) // Convert chunks to version 3 + { + UpdateChunk2To3(); + } +#endif +} + + + + + +cWSSCompact::cPAKFile::~cPAKFile() +{ + if (m_NumDirty > 0) + { + SynchronizeFile(); + } + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + delete *itr; + } +} + + + + + +bool cWSSCompact::cPAKFile::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data) +{ + int ChunkX = a_Chunk.m_ChunkX; + int ChunkZ = a_Chunk.m_ChunkZ; + sChunkHeader * Header = NULL; + int Offset = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ)) + { + Header = *itr; + break; + } + Offset += (*itr)->m_CompressedSize; + } + if ((Header == NULL) || (Offset + Header->m_CompressedSize > (int)m_DataContents.size())) + { + // Chunk not found / data invalid + return false; + } + + a_UncompressedSize = Header->m_UncompressedSize; + a_Data.assign(m_DataContents, Offset, Header->m_CompressedSize); + return true; +} + + + + + +bool cWSSCompact::cPAKFile::SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World) +{ + if (!SaveChunkToData(a_Chunk, a_World)) + { + return false; + } + if (m_NumDirty > MAX_DIRTY_CHUNKS) + { + SynchronizeFile(); + } + return true; +} + + + + + +void cWSSCompact::cPAKFile::UpdateChunk1To2() +{ + int Offset = 0; + AString NewDataContents; + int ChunksConverted = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + sChunkHeader * Header = *itr; + + if( ChunksConverted % 32 == 0 ) + { + LOGINFO("Updating \"%s\" version 1 to version 2: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() ); + } + ChunksConverted++; + + AString Data; + int UncompressedSize = Header->m_UncompressedSize; + Data.assign(m_DataContents, Offset, Header->m_CompressedSize); + Offset += Header->m_CompressedSize; + + // Crude data integrity check: + int ExpectedSize = (16*128*16)*2 + (16*128*16)/2; // For version 1 + if (UncompressedSize < ExpectedSize) + { + LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing", + Header->m_ChunkX, Header->m_ChunkZ, + UncompressedSize, ExpectedSize + ); + Offset += Header->m_CompressedSize; + continue; + } + + // Decompress the data: + AString UncompressedData; + { + int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize); + if (errorcode != Z_OK) + { + LOGERROR("Error %d decompressing data for chunk [%d, %d]", + errorcode, + Header->m_ChunkX, Header->m_ChunkZ + ); + Offset += Header->m_CompressedSize; + continue; + } + } + + if (UncompressedSize != (int)UncompressedData.size()) + { + LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]", + UncompressedSize, UncompressedData.size(), + Header->m_ChunkX, Header->m_ChunkZ + ); + Offset += Header->m_CompressedSize; + continue; + } + + + // Old version is 128 blocks high with YZX axis order + char ConvertedData[cChunkDef::BlockDataSize]; + int Index = 0; + unsigned int InChunkOffset = 0; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) + { + for( int y = 0; y < 128; ++y ) + { + ConvertedData[Index++] = UncompressedData[y + z * 128 + x * 128 * 16 + InChunkOffset]; + } + // Add 128 empty blocks after an old y column + memset(ConvertedData + Index, E_BLOCK_AIR, 128); + Index += 128; + } + InChunkOffset += (16 * 128 * 16); + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Metadata + { + for( int y = 0; y < 64; ++y ) + { + ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset]; + } + memset(ConvertedData + Index, 0, 64); + Index += 64; + } + InChunkOffset += (16 * 128 * 16) / 2; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Block light + { + for( int y = 0; y < 64; ++y ) + { + ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset]; + } + memset(ConvertedData + Index, 0, 64); + Index += 64; + } + InChunkOffset += (16*128*16)/2; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Sky light + { + for( int y = 0; y < 64; ++y ) + { + ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset]; + } + memset(ConvertedData + Index, 0, 64); + Index += 64; + } + InChunkOffset += (16 * 128 * 16) / 2; + + AString Converted(ConvertedData, ARRAYCOUNT(ConvertedData)); + + // Add JSON data afterwards + if (UncompressedData.size() > InChunkOffset) + { + Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() ); + } + + // Re-compress data + AString CompressedData; + { + int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData); + if (errorcode != Z_OK) + { + LOGERROR("Error %d compressing data for chunk [%d, %d]", + errorcode, + Header->m_ChunkX, Header->m_ChunkZ + ); + continue; + } + } + + // Save into file's cache + Header->m_UncompressedSize = Converted.size(); + Header->m_CompressedSize = CompressedData.size(); + NewDataContents.append( CompressedData ); + } + + // Done converting + m_DataContents = NewDataContents; + m_ChunkVersion = 2; + SynchronizeFile(); + + LOGINFO("Updated \"%s\" version 1 to version 2", m_FileName.c_str() ); +} + + + + + +void cWSSCompact::cPAKFile::UpdateChunk2To3() +{ + int Offset = 0; + AString NewDataContents; + int ChunksConverted = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + sChunkHeader * Header = *itr; + + if( ChunksConverted % 32 == 0 ) + { + LOGINFO("Updating \"%s\" version 2 to version 3: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() ); + } + ChunksConverted++; + + AString Data; + int UncompressedSize = Header->m_UncompressedSize; + Data.assign(m_DataContents, Offset, Header->m_CompressedSize); + Offset += Header->m_CompressedSize; + + // Crude data integrity check: + const int ExpectedSize = (16*256*16)*2 + (16*256*16)/2; // For version 2 + if (UncompressedSize < ExpectedSize) + { + LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing", + Header->m_ChunkX, Header->m_ChunkZ, + UncompressedSize, ExpectedSize + ); + Offset += Header->m_CompressedSize; + continue; + } + + // Decompress the data: + AString UncompressedData; + { + int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize); + if (errorcode != Z_OK) + { + LOGERROR("Error %d decompressing data for chunk [%d, %d]", + errorcode, + Header->m_ChunkX, Header->m_ChunkZ + ); + Offset += Header->m_CompressedSize; + continue; + } + } + + if (UncompressedSize != (int)UncompressedData.size()) + { + LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]", + UncompressedSize, UncompressedData.size(), + Header->m_ChunkX, Header->m_ChunkZ + ); + Offset += Header->m_CompressedSize; + continue; + } + + char ConvertedData[ExpectedSize]; + memset(ConvertedData, 0, ExpectedSize); + + // Cannot use cChunk::MakeIndex because it might change again????????? + // For compatibility, use what we know is current + #define MAKE_2_INDEX( x, y, z ) ( y + (z * 256) + (x * 256 * 16) ) + #define MAKE_3_INDEX( x, y, z ) ( x + (z * 16) + (y * 16 * 16) ) + + unsigned int InChunkOffset = 0; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) // YZX Loop order is important, in 1.1 Y was first then Z then X + { + ConvertedData[ MAKE_3_INDEX(x, y, z) ] = UncompressedData[InChunkOffset]; + ++InChunkOffset; + } // for y, z, x + + + unsigned int index2 = 0; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) + { + ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4); + ++index2; + } + InChunkOffset += index2 / 2; + index2 = 0; + + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) + { + ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4); + ++index2; + } + InChunkOffset += index2 / 2; + index2 = 0; + + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) + { + ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4); + ++index2; + } + InChunkOffset += index2 / 2; + index2 = 0; + + AString Converted(ConvertedData, ExpectedSize); + + // Add JSON data afterwards + if (UncompressedData.size() > InChunkOffset) + { + Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() ); + } + + // Re-compress data + AString CompressedData; + { + int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData); + if (errorcode != Z_OK) + { + LOGERROR("Error %d compressing data for chunk [%d, %d]", + errorcode, + Header->m_ChunkX, Header->m_ChunkZ + ); + continue; + } + } + + // Save into file's cache + Header->m_UncompressedSize = Converted.size(); + Header->m_CompressedSize = CompressedData.size(); + NewDataContents.append( CompressedData ); + } + + // Done converting + m_DataContents = NewDataContents; + m_ChunkVersion = 3; + SynchronizeFile(); + + LOGINFO("Updated \"%s\" version 2 to version 3", m_FileName.c_str() ); +} + + + + + +bool cWSSCompact::LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World) +{ + // Crude data integrity check: + if (a_UncompressedSize < cChunkDef::BlockDataSize) + { + LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing", + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, + a_UncompressedSize, cChunkDef::BlockDataSize + ); + EraseChunkData(a_Chunk); + return false; + } + + // Decompress the data: + AString UncompressedData; + int errorcode = UncompressString(a_Data.data(), a_Data.size(), UncompressedData, a_UncompressedSize); + if (errorcode != Z_OK) + { + LOGERROR("Error %d decompressing data for chunk [%d, %d]", + errorcode, + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ + ); + return false; + } + + if (a_UncompressedSize != (int)UncompressedData.size()) + { + LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]", + a_UncompressedSize, UncompressedData.size(), + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ + ); + return false; + } + + cEntityList Entities; + cBlockEntityList BlockEntities; + bool IsLightValid = false; + + if (a_UncompressedSize > cChunkDef::BlockDataSize) + { + Json::Value root; // will contain the root value after parsing. + Json::Reader reader; + if ( !reader.parse( UncompressedData.data() + cChunkDef::BlockDataSize, root, false ) ) + { + LOGERROR("Failed to parse trailing JSON in chunk [%d, %d]!", + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ + ); + } + else + { + LoadEntitiesFromJson(root, Entities, BlockEntities, a_World); + IsLightValid = root.get("IsLightValid", false).asBool(); + } + } + + BLOCKTYPE * BlockData = (BLOCKTYPE *)UncompressedData.data(); + NIBBLETYPE * MetaData = (NIBBLETYPE *)(BlockData + cChunkDef::MetaOffset); + NIBBLETYPE * BlockLight = (NIBBLETYPE *)(BlockData + cChunkDef::LightOffset); + NIBBLETYPE * SkyLight = (NIBBLETYPE *)(BlockData + cChunkDef::SkyLightOffset); + + a_World->SetChunkData( + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, + BlockData, MetaData, + IsLightValid ? BlockLight : NULL, + IsLightValid ? SkyLight : NULL, + NULL, NULL, + Entities, BlockEntities, + false + ); + + return true; +} + + + + + +bool cWSSCompact::cPAKFile::EraseChunkData(const cChunkCoords & a_Chunk) +{ + int ChunkX = a_Chunk.m_ChunkX; + int ChunkZ = a_Chunk.m_ChunkZ; + int Offset = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ)) + { + m_DataContents.erase(Offset, (*itr)->m_CompressedSize); + delete *itr; + itr = m_ChunkHeaders.erase(itr); + return true; + } + Offset += (*itr)->m_CompressedSize; + } + + return false; +} + + + + + +bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World) +{ + // Serialize the chunk: + cJsonChunkSerializer Serializer; + if (!a_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer)) + { + // Chunk not valid + LOG("cWSSCompact: Trying to save chunk [%d, %d, %d] that has no data, ignoring request.", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); + return false; + } + + AString Data; + Data.assign((const char *)Serializer.GetBlockData(), cChunkDef::BlockDataSize); + if (Serializer.HasJsonData()) + { + AString JsonData; + Json::StyledWriter writer; + JsonData = writer.write(Serializer.GetRoot()); + Data.append(JsonData); + } + + // Compress the data: + AString CompressedData; + int errorcode = CompressString(Data.data(), Data.size(), CompressedData); + if ( errorcode != Z_OK ) + { + LOGERROR("Error %i compressing data for chunk [%d, %d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); + return false; + } + + // Erase any existing data for the chunk: + EraseChunkData(a_Chunk); + + // Save the header: + sChunkHeader * Header = new sChunkHeader; + if (Header == NULL) + { + LOGWARNING("Cannot create a new chunk header to save chunk [%d, %d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); + return false; + } + Header->m_CompressedSize = (int)CompressedData.size(); + Header->m_ChunkX = a_Chunk.m_ChunkX; + Header->m_ChunkZ = a_Chunk.m_ChunkZ; + Header->m_UncompressedSize = (int)Data.size(); + m_ChunkHeaders.push_back(Header); + + m_DataContents.append(CompressedData.data(), CompressedData.size()); + + m_NumDirty++; + return true; +} + + + + + +#define WRITE(Var) \ + if (f.Write(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("cWSSCompact: ERROR writing %s to file \"%s\" (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \ + return; \ + } + +void cWSSCompact::cPAKFile::SynchronizeFile(void) +{ + cFile f; + if (!f.Open(m_FileName, cFile::fmWrite)) + { + LOGERROR("Cannot open PAK file \"%s\" for writing", m_FileName.c_str()); + return; + } + + WRITE(m_PakVersion); + WRITE(m_ChunkVersion); + short NumChunks = (short)m_ChunkHeaders.size(); + WRITE(NumChunks); + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + WRITE(**itr); + } + if (f.Write(m_DataContents.data(), m_DataContents.size()) != (int)m_DataContents.size()) + { + LOGERROR("cWSSCompact: ERROR writing chunk contents to file \"%s\" (line %d); file offset %d", m_FileName.c_str(), __LINE__, f.Tell()); + return; + } + m_NumDirty = 0; +} + + + + diff --git a/src/WorldStorage/WSSCompact.h b/src/WorldStorage/WSSCompact.h new file mode 100644 index 000000000..e6a013eaf --- /dev/null +++ b/src/WorldStorage/WSSCompact.h @@ -0,0 +1,144 @@ + +// WSSCompact.h + +// Interfaces to the cWSSCompact class representing the "Compact" storage schema (PAK-files) + + + + + +#pragma once +#ifndef WSSCOMPACT_H_INCLUDED +#define WSSCOMPACT_H_INCLUDED + +#include "WorldStorage.h" +#include "../Vector3i.h" + + + + + +/// Helper class for serializing a chunk into Json +class cJsonChunkSerializer : + public cChunkDataCollector +{ +public: + + cJsonChunkSerializer(void); + + Json::Value & GetRoot (void) {return m_Root; } + BLOCKTYPE * GetBlockData(void) {return (BLOCKTYPE *)m_BlockData; } + bool HasJsonData (void) const {return m_HasJsonData; } + +protected: + + // NOTE: block data is serialized into inherited cChunkDataCollector's m_BlockData[] array + + // Entities and BlockEntities are serialized to Json + Json::Value m_Root; + bool m_HasJsonData; + + // cChunkDataCollector overrides: + virtual void Entity (cEntity * a_Entity) override; + virtual void BlockEntity (cBlockEntity * a_Entity) override; + virtual bool LightIsValid (bool a_IsLightValid) override; +} ; + + + + + +class cWSSCompact : + public cWSSchema +{ +public: + cWSSCompact(cWorld * a_World) : cWSSchema(a_World) {} + virtual ~cWSSCompact(); + +protected: + + struct sChunkHeader; + typedef std::vector sChunkHeaders; + + /// Implements a cache for a single PAK file; implements lazy-write in order to be able to write multiple chunks fast + class cPAKFile + { + public: + + cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ); + ~cPAKFile(); + + bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data); + bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data); + bool EraseChunkData(const cChunkCoords & a_Chunk); + + bool SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World); + + int GetLayerX(void) const {return m_LayerX; } + int GetLayerZ(void) const {return m_LayerZ; } + + static const int PAK_VERSION = 1; +#if AXIS_ORDER == AXIS_ORDER_XZY + static const int CHUNK_VERSION = 3; +#elif AXIS_ORDER == AXIS_ORDER_YZX + static const int CHUNK_VERSION = 2; +#endif + protected: + + AString m_FileName; + int m_LayerX; + int m_LayerZ; + + sChunkHeaders m_ChunkHeaders; + AString m_DataContents; // Data contents of the file, cached + + int m_NumDirty; // Number of chunks that were written into m_DataContents but not into the file + + Vector3i m_ChunkSize; // Is related to m_ChunkVersion + char m_ChunkVersion; + char m_PakVersion; + + bool SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World); // Saves the chunk to m_DataContents, updates headers and m_NumDirty + void SynchronizeFile(void); // Writes m_DataContents along with the headers to file, resets m_NumDirty + + void UpdateChunk1To2(void); // Height from 128 to 256 + void UpdateChunk2To3(void); // Axis order from YZX to XZY + } ; + + typedef std::list cPAKFiles; + + cCriticalSection m_CS; + cPAKFiles m_PAKFiles; // A MRU cache of PAK files + + /// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache + cPAKFile * LoadPAKFile(const cChunkCoords & a_Chunk); + + /// Gets chunk data from the correct file; locks CS as needed + bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data); + + /// Sets chunk data to the correct file; locks CS as needed + bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data); + + /// Erases chunk data from the correct file; locks CS as needed + bool EraseChunkData(const cChunkCoords & a_Chunk); + + /// Loads the chunk from the data (no locking needed) + bool LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World); + + void LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World); + + // cWSSchema overrides: + virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; + virtual bool SaveChunk(const cChunkCoords & a_Chunk) override; + virtual const AString GetName(void) const override {return "compact"; } +} ; + + + + + +#endif // WSSCOMPACT_H_INCLUDED + + + + diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp new file mode 100644 index 000000000..f290ec128 --- /dev/null +++ b/src/WorldStorage/WorldStorage.cpp @@ -0,0 +1,409 @@ + +// WorldStorage.cpp + +// Implements the cWorldStorage class representing the chunk loading / saving thread + +// To add a new storage schema, implement a cWSSchema descendant and add it to cWorldStorage::InitSchemas() + +#include "Globals.h" +#include "WorldStorage.h" +#include "WSSCompact.h" +#include "WSSAnvil.h" +#include "../World.h" +#include "../Generating/ChunkGenerator.h" +#include "../Entities/Entity.h" +#include "../BlockEntities/BlockEntity.h" + + + + + +/// If a chunk with this Y coord is de-queued, it is a signal to emit the saved-all message (cWorldStorage::QueueSavedMessage()) +#define CHUNK_Y_MESSAGE 2 + + + + + +/// Example storage schema - forgets all chunks ;) +class cWSSForgetful : + public cWSSchema +{ +public: + cWSSForgetful(cWorld * a_World) : cWSSchema(a_World) {} + +protected: + // cWSSchema overrides: + virtual bool LoadChunk(const cChunkCoords & a_Chunk) override {return false; } + virtual bool SaveChunk(const cChunkCoords & a_Chunk) override {return true; } + virtual const AString GetName(void) const override {return "forgetful"; } +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorldStorage: + +cWorldStorage::cWorldStorage(void) : + super("cWorldStorage"), + m_World(NULL), + m_SaveSchema(NULL) +{ +} + + + + + +cWorldStorage::~cWorldStorage() +{ + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + delete *itr; + } // for itr - m_Schemas[] + m_LoadQueue.clear(); + m_SaveQueue.clear(); +} + + + + + +bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName) +{ + m_World = a_World; + m_StorageSchemaName = a_StorageSchemaName; + InitSchemas(); + + return super::Start(); +} + + + + + +void cWorldStorage::Stop(void) +{ + WaitForFinish(); +} + + + + + +void cWorldStorage::WaitForFinish(void) +{ + LOG("Waiting for the world storage to finish saving"); + + { + // Cancel all loading requests: + cCSLock Lock(m_CSQueues); + m_LoadQueue.clear(); + } + + // Wait for the saving to finish: + WaitForQueuesEmpty(); + + // Wait for the thread to finish: + m_ShouldTerminate = true; + m_Event.Set(); + m_evtRemoved.Set(); // Wake up anybody waiting in the WaitForQueuesEmpty() method + super::Wait(); + LOG("World storage thread finished"); +} + + + + + +void cWorldStorage::WaitForQueuesEmpty(void) +{ + cCSLock Lock(m_CSQueues); + while (!m_ShouldTerminate && (!m_LoadQueue.empty() || !m_SaveQueue.empty())) + { + cCSUnlock Unlock(Lock); + m_evtRemoved.Wait(); + } +} + + + + + +int cWorldStorage::GetLoadQueueLength(void) +{ + cCSLock Lock(m_CSQueues); + return (int)m_LoadQueue.size(); +} + + + + + +int cWorldStorage::GetSaveQueueLength(void) +{ + cCSLock Lock(m_CSQueues); + return (int)m_SaveQueue.size(); +} + + + + + +void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) +{ + // Queues the chunk for loading; if not loaded, the chunk will be generated + { + cCSLock Lock(m_CSQueues); + + // Check if already in the queue: + for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_Generate == a_Generate)) + { + return; + } + } + m_LoadQueue.push_back(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate)); + } + + m_Event.Set(); +} + + + + + +void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + { + cCSLock Lock(m_CSQueues); + m_SaveQueue.remove (cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); // Don't add twice + m_SaveQueue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); + } + m_Event.Set(); +} + + + + + +void cWorldStorage::QueueSavedMessage(void) +{ + // Pushes a special coord pair into the queue, signalizing a message instead: + { + cCSLock Lock(m_CSQueues); + m_SaveQueue.push_back(cChunkCoords(0, CHUNK_Y_MESSAGE, 0)); + } + m_Event.Set(); +} + + + + + +void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSQueues); + for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr) + { + if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkY != a_ChunkY) || (itr->m_ChunkZ != a_ChunkZ)) + { + continue; + } + m_LoadQueue.erase(itr); + Lock.Unlock(); + m_evtRemoved.Set(); + return; + } // for itr - m_LoadQueue[] +} + + + + + +void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk) +{ + { + cCSLock Lock(m_CSQueues); + m_SaveQueue.remove(a_Chunk); + } + m_evtRemoved.Set(); +} + + + + + +void cWorldStorage::InitSchemas(void) +{ + // The first schema added is considered the default + m_Schemas.push_back(new cWSSAnvil (m_World)); + m_Schemas.push_back(new cWSSCompact (m_World)); + m_Schemas.push_back(new cWSSForgetful(m_World)); + // Add new schemas here + + if (NoCaseCompare(m_StorageSchemaName, "default") == 0) + { + m_SaveSchema = m_Schemas.front(); + return; + } + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + if (NoCaseCompare((*itr)->GetName(), m_StorageSchemaName) == 0) + { + m_SaveSchema = *itr; + return; + } + } // for itr - m_Schemas[] + + // Unknown schema selected, let the admin know: + LOGWARNING("Unknown storage schema name \"%s\". Using default (\"%s\"). Available schemas:", + m_StorageSchemaName.c_str(), m_SaveSchema->GetName().c_str() + ); + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + LOGWARNING("\t\"%s\"", (*itr)->GetName().c_str()); + } + m_SaveSchema = m_Schemas.front(); +} + + + + + +void cWorldStorage::Execute(void) +{ + while (!m_ShouldTerminate) + { + m_Event.Wait(); + + // Process both queues until they are empty again: + bool HasMore; + do + { + HasMore = false; + if (m_ShouldTerminate) + { + return; + } + + HasMore = LoadOneChunk(); + HasMore = HasMore | SaveOneChunk(); + m_evtRemoved.Set(); + } while (HasMore); + } +} + + + + + +bool cWorldStorage::LoadOneChunk(void) +{ + sChunkLoad ToLoad(0, 0, 0, false); + bool HasMore; + bool ShouldLoad = false; + { + cCSLock Lock(m_CSQueues); + if (!m_LoadQueue.empty()) + { + ToLoad = m_LoadQueue.front(); + m_LoadQueue.pop_front(); + ShouldLoad = true; + } + HasMore = !m_LoadQueue.empty(); + } + + if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ)) + { + if (ToLoad.m_Generate) + { + // The chunk couldn't be loaded, generate it: + m_World->GetGenerator().QueueGenerateChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ); + } + else + { + // TODO: Notify the world that the load has failed: + // m_World->ChunkLoadFailed(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ); + } + } + return HasMore; +} + + + + + +bool cWorldStorage::SaveOneChunk(void) +{ + cChunkCoords Save(0, 0, 0); + bool HasMore; + bool ShouldSave = false; + { + cCSLock Lock(m_CSQueues); + if (!m_SaveQueue.empty()) + { + Save = m_SaveQueue.front(); + m_SaveQueue.pop_front(); + ShouldSave = true; + } + HasMore = !m_SaveQueue.empty(); + } + if (Save.m_ChunkY == CHUNK_Y_MESSAGE) + { + LOGINFO("Saved all chunks in world %s", m_World->GetName().c_str()); + return HasMore; + } + if (ShouldSave && m_World->IsChunkValid(Save.m_ChunkX, Save.m_ChunkZ)) + { + m_World->MarkChunkSaving(Save.m_ChunkX, Save.m_ChunkZ); + if (m_SaveSchema->SaveChunk(Save)) + { + m_World->MarkChunkSaved(Save.m_ChunkX, Save.m_ChunkZ); + } + } + return HasMore; +} + + + + + +bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + if (m_World->IsChunkValid(a_ChunkX, a_ChunkZ)) + { + // Already loaded (can happen, since the queue is async) + return true; + } + + cChunkCoords Coords(a_ChunkX, a_ChunkY, a_ChunkZ); + + // First try the schema that is used for saving + if (m_SaveSchema->LoadChunk(Coords)) + { + return true; + } + + // If it didn't have the chunk, try all the other schemas: + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + if (((*itr) != m_SaveSchema) && (*itr)->LoadChunk(Coords)) + { + return true; + } + } + + // Notify the chunk owner that the chunk failed to load (sets cChunk::m_HasLoadFailed to true): + m_World->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); + + return false; +} + + + + + diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h new file mode 100644 index 000000000..bf8dbd3d5 --- /dev/null +++ b/src/WorldStorage/WorldStorage.h @@ -0,0 +1,135 @@ + +// WorldStorage.h + +// Interfaces to the cWorldStorage class representing the chunk loading / saving thread +// This class decides which storage schema to use for saving; it queries all available schemas for loading +// Also declares the base class for all storage schemas, cWSSchema +// Helper serialization class cJsonChunkSerializer is declared as well + + + + + +#pragma once +#ifndef WORLDSTORAGE_H_INCLUDED +#define WORLDSTORAGE_H_INCLUDED + +#include "../ChunkDef.h" +#include "../OSSupport/IsThread.h" +#include + + + + + +// fwd: +class cWorld; + + + + + +/// Interface that all the world storage schemas need to implement +class cWSSchema abstract +{ +public: + cWSSchema(cWorld * a_World) : m_World(a_World) {} + virtual ~cWSSchema() {} // Force the descendants' destructors to be virtual + + virtual bool LoadChunk(const cChunkCoords & a_Chunk) = 0; + virtual bool SaveChunk(const cChunkCoords & a_Chunk) = 0; + virtual const AString GetName(void) const = 0; + +protected: + + cWorld * m_World; +} ; + +typedef std::list cWSSchemaList; + + + + + +/// The actual world storage class +class cWorldStorage : + public cIsThread +{ + typedef cIsThread super; + +public: + + cWorldStorage(void); + ~cWorldStorage(); + + void QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate); // Queues the chunk for loading; if not loaded, the chunk will be generated if a_Generate is true + void QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Signals that a message should be output to the console when all the chunks have been saved + void QueueSavedMessage(void); + + /// Loads the chunk specified; returns true on success, false on failure + bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + void UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void UnqueueSave(const cChunkCoords & a_Chunk); + + bool Start(cWorld * a_World, const AString & a_StorageSchemaName); // Hide the cIsThread's Start() method, we need to provide args + void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event + void WaitForFinish(void); + void WaitForQueuesEmpty(void); + + int GetLoadQueueLength(void); + int GetSaveQueueLength(void); + +protected: + + struct sChunkLoad + { + int m_ChunkX; + int m_ChunkY; + int m_ChunkZ; + bool m_Generate; // If true, the chunk will be generated if it cannot be loaded + + sChunkLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ), m_Generate(a_Generate) {} + } ; + + typedef std::list sChunkLoadQueue; + + cWorld * m_World; + AString m_StorageSchemaName; + + // Both queues are locked by the same CS + cCriticalSection m_CSQueues; + sChunkLoadQueue m_LoadQueue; + cChunkCoordsList m_SaveQueue; + + cEvent m_Event; // Set when there's any addition to the queues + cEvent m_evtRemoved; // Set when an item has been removed from the queue, either by the worker thread or the Unqueue methods + + /// All the storage schemas (all used for loading) + cWSSchemaList m_Schemas; + + /// The one storage schema used for saving + cWSSchema * m_SaveSchema; + + void InitSchemas(void); + + virtual void Execute(void) override; + + /// Loads one chunk from the queue (if any queued); returns true if there are more chunks in the load queue + bool LoadOneChunk(void); + + /// Saves one chunk from the queue (if any queued); returns true if there are more chunks in the save queue + bool SaveOneChunk(void); +} ; + + + + + +#endif // WORLDSTORAGE_H_INCLUDED + + + + diff --git a/src/XMLParser.h b/src/XMLParser.h new file mode 100644 index 000000000..f492d1a5d --- /dev/null +++ b/src/XMLParser.h @@ -0,0 +1,701 @@ + +// XMLParser.h + +// Interfaces to the CXMLParser class representing the base class for XML parsing + +// To use, derive a class from this base and override its OnStartElement(), OnEndElement() and OnCharacters() functions + + + + + +#pragma once + +#include "expat/expat.h" + + + + + +class CXMLParser +{ +public: + CXMLParser(void); + virtual ~CXMLParser(); + + // The actual parsing, may be called several times; the last time needs iIsFinal == true (-> flush) + int Parse(const char * iData, size_t iLength, bool iIsFinal = false); + +private: + // LibExpat stuff: + XML_Parser mParser; + + static void StartElementHandler(void * iContext, const XML_Char * iElement, const XML_Char ** iAttributes) + { + ((CXMLParser *)iContext)->OnStartElement(iElement, iAttributes); + } + + static void EndElementHandler (void * iContext, const XML_Char * iElement) + { + ((CXMLParser *)iContext)->OnEndElement(iElement); + } + + static void CharacterDataHandler (void * iContext, const XML_Char * iData, int iLength) + { + ((CXMLParser *)iContext)->OnCharacters(iData, iLength); + } + +protected: + virtual void OnStartElement(const XML_Char * iElement, const XML_Char ** iAttributes) = 0; + virtual void OnEndElement (const XML_Char * iElement) = 0; + virtual void OnCharacters (const XML_Char * iCharacters, int iLength) = 0; +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// The following template has been modified from code available at +// http://www.codeproject.com/Articles/1847/C-Wrappers-for-the-Expat-XML-Parser +// It uses templates to remove the virtual function call penalty (both size and speed) for each callback + +/* Usage: +1, Declare a subclass: + class CMyParser : public CExpatImpl +2, Declare handlers that you want in that subclass: + void CMyParser::OnEndElement(const XML_Char * iTagName); +3, Create an instance of your class: + CMyParser Parser; +4, Call Create(): + Parser.Create(NULL, NULL); +4, Call Parse(), repeatedly: + Parser.Parse(Buffer, Length); +*/ + +template +class CExpatImpl +{ + +// @access Constructors and destructors +public: + + // @cmember General constructor + + CExpatImpl () + { + m_p = NULL; + } + + // @cmember Destructor + + ~CExpatImpl () + { + Destroy (); + } + +// @access Parser creation and deletion methods +public: + + // @cmember Create a parser + + bool Create (const XML_Char * pszEncoding = NULL, const XML_Char * pszSep = NULL) + { + // Destroy the old parser + Destroy (); + + // If the encoding or seperator are empty, then NULL + if (pszEncoding != NULL && pszEncoding [0] == 0) + { + pszEncoding = NULL; + } + if (pszSep != NULL && pszSep [0] == 0) + { + pszSep = NULL; + } + + // Create the new parser + m_p = XML_ParserCreate_MM (pszEncoding, NULL, pszSep); + if (m_p == NULL) + { + return false; + } + + // Invoke the post create routine + _T * pThis = static_cast <_T *> (this); + pThis ->OnPostCreate (); + + // Set the user data used in callbacks + XML_SetUserData (m_p, (void *) this); + return true; + } + + // @cmember Destroy the parser + + void Destroy (void) + { + if (m_p != NULL) + { + XML_ParserFree (m_p); + } + m_p = NULL; + } + + + // @cmember Parse a block of data + + bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true) + { + assert (m_p != NULL); + return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0; + } + + // @cmember Parse internal buffer + + bool ParseBuffer (int nLength, bool fIsFinal = true) + { + assert (m_p != NULL); + return XML_ParseBuffer (m_p, nLength, fIsFinal) != 0; + } + + // @cmember Get the internal buffer + + void *GetBuffer (int nLength) + { + assert (m_p != NULL); + return XML_GetBuffer (m_p, nLength); + } + + +protected: + // Parser callback enable/disable methods: + + // @cmember Enable/Disable the start element handler + + void EnableStartElementHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : NULL); + } + + // @cmember Enable/Disable the end element handler + + void EnableEndElementHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : NULL); + } + + // @cmember Enable/Disable the element handlers + + void EnableElementHandler (bool fEnable = true) + { + assert (m_p != NULL); + EnableStartElementHandler (fEnable); + EnableEndElementHandler (fEnable); + } + + // @cmember Enable/Disable the character data handler + + void EnableCharacterDataHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : NULL); + } + + // @cmember Enable/Disable the processing instruction handler + + void EnableProcessingInstructionHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : NULL); + } + + // @cmember Enable/Disable the comment handler + + void EnableCommentHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetCommentHandler (m_p, fEnable ? CommentHandler : NULL); + } + + // @cmember Enable/Disable the start CDATA section handler + + void EnableStartCdataSectionHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : NULL); + } + + // @cmember Enable/Disable the end CDATA section handler + + void EnableEndCdataSectionHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : NULL); + } + + // @cmember Enable/Disable the CDATA section handlers + + void EnableCdataSectionHandler (bool fEnable = true) + { + assert (m_p != NULL); + EnableStartCdataSectionHandler (fEnable); + EnableEndCdataSectionHandler (fEnable); + } + + // @cmember Enable/Disable default handler + + void EnableDefaultHandler (bool fEnable = true, bool fExpand = true) + { + assert (m_p != NULL); + if (fExpand) + { + XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : NULL); + } + else + XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : NULL); + } + + // @cmember Enable/Disable external entity ref handler + + void EnableExternalEntityRefHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : NULL); + } + + // @cmember Enable/Disable unknown encoding handler + + void EnableUnknownEncodingHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : NULL); + } + + // @cmember Enable/Disable start namespace handler + + void EnableStartNamespaceDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : NULL); + } + + // @cmember Enable/Disable end namespace handler + + void EnableEndNamespaceDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : NULL); + } + + // @cmember Enable/Disable namespace handlers + + void EnableNamespaceDeclHandler (bool fEnable = true) + { + EnableStartNamespaceDeclHandler (fEnable); + EnableEndNamespaceDeclHandler (fEnable); + } + + // @cmember Enable/Disable the XML declaration handler + + void EnableXmlDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : NULL); + } + + // @cmember Enable/Disable the start DOCTYPE declaration handler + + void EnableStartDoctypeDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : NULL); + } + + // @cmember Enable/Disable the end DOCTYPE declaration handler + + void EnableEndDoctypeDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndDoctypeDeclHandler (m_p, + fEnable ? EndDoctypeDeclHandler : NULL); + } + + // @cmember Enable/Disable the DOCTYPE declaration handler + + void EnableDoctypeDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + EnableStartDoctypeDeclHandler (fEnable); + EnableEndDoctypeDeclHandler (fEnable); + } + +public: + // Parser error reporting methods + + // @cmember Get last error + + enum XML_Error GetErrorCode () + { + assert (m_p != NULL); + return XML_GetErrorCode (m_p); + } + + // @cmember Get the current byte index + + long GetCurrentByteIndex () + { + assert (m_p != NULL); + return XML_GetCurrentByteIndex (m_p); + } + + // @cmember Get the current line number + + int GetCurrentLineNumber () + { + assert (m_p != NULL); + return XML_GetCurrentLineNumber (m_p); + } + + // @cmember Get the current column number + + int GetCurrentColumnNumber () + { + assert (m_p != NULL); + return XML_GetCurrentColumnNumber (m_p); + } + + // @cmember Get the current byte count + + int GetCurrentByteCount () + { + assert (m_p != NULL); + return XML_GetCurrentByteCount (m_p); + } + + // @cmember Get the input context + + const char *GetInputContext (int *pnOffset, int *pnSize) + { + assert (m_p != NULL); + return XML_GetInputContext (m_p, pnOffset, pnSize); + } + + // @cmember Get last error string + + const XML_LChar *GetErrorString () + { + return XML_ErrorString (GetErrorCode ()); + } + + // @cmember Return the version string + + static const XML_LChar *GetExpatVersion () + { + return XML_ExpatVersion (); + } + + // @cmember Get the version information + + static void GetExpatVersion (int *pnMajor, int *pnMinor, int *pnMicro) + { + XML_expat_version v = XML_ExpatVersionInfo (); + if (pnMajor) + *pnMajor = v .major; + if (pnMinor) + *pnMinor = v .minor; + if (pnMicro) + *pnMicro = v .micro; + } + + // @cmember Get last error string + + static const XML_LChar *GetErrorString (enum XML_Error nError) + { + return XML_ErrorString (nError); + } + + + // Public handler methods: + // The template parameter should provide their own implementation for those handlers that they want + + // @cmember Start element handler + + void OnStartElement (const XML_Char *pszName, const XML_Char **papszAttrs) + { + return; + } + + // @cmember End element handler + + void OnEndElement (const XML_Char *pszName) + { + return; + } + + // @cmember Character data handler + + void OnCharacterData (const XML_Char *pszData, int nLength) + { + return; + } + + // @cmember Processing instruction handler + + void OnProcessingInstruction (const XML_Char *pszTarget, + const XML_Char *pszData) + { + return; + } + + // @cmember Comment handler + + void OnComment (const XML_Char *pszData) + { + return; + } + + // @cmember Start CDATA section handler + + void OnStartCdataSection () + { + return; + } + + // @cmember End CDATA section handler + + void OnEndCdataSection () + { + return; + } + + // @cmember Default handler + + void OnDefault (const XML_Char *pszData, int nLength) + { + return; + } + + // @cmember External entity ref handler + + bool OnExternalEntityRef (const XML_Char *pszContext, + const XML_Char *pszBase, const XML_Char *pszSystemID, + const XML_Char *pszPublicID) + { + return false; + } + + // @cmember Unknown encoding handler + + bool OnUnknownEncoding (const XML_Char *pszName, XML_Encoding *pInfo) + { + return false; + } + + // @cmember Start namespace declaration handler + + void OnStartNamespaceDecl (const XML_Char *pszPrefix, + const XML_Char *pszURI) + { + return; + } + + // @cmember End namespace declaration handler + + void OnEndNamespaceDecl (const XML_Char *pszPrefix) + { + return; + } + + // @cmember XML declaration handler + + void OnXmlDecl (const XML_Char *pszVersion, const XML_Char *pszEncoding, + bool fStandalone) + { + return; + } + + // @cmember Start DOCTYPE declaration handler + + void OnStartDoctypeDecl (const XML_Char *pszDoctypeName, + const XML_Char *pszSysID, const XML_Char *pszPubID, + bool fHasInternalSubset) + { + return; + } + + // @cmember End DOCTYPE declaration handler + + void OnEndDoctypeDecl () + { + return; + } + +// @access Protected methods +protected: + + // @cmember Handle any post creation + + void OnPostCreate () + { + } + +// @access Protected static methods +protected: + + // @cmember Start element handler wrapper + + static void __cdecl StartElementHandler (void *pUserData, + const XML_Char *pszName, const XML_Char **papszAttrs) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartElement (pszName, papszAttrs); + } + + // @cmember End element handler wrapper + + static void __cdecl EndElementHandler (void *pUserData, + const XML_Char *pszName) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndElement (pszName); + } + + // @cmember Character data handler wrapper + + static void __cdecl CharacterDataHandler (void *pUserData, + const XML_Char *pszData, int nLength) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnCharacterData (pszData, nLength); + } + + // @cmember Processing instruction handler wrapper + + static void __cdecl ProcessingInstructionHandler (void *pUserData, + const XML_Char *pszTarget, const XML_Char *pszData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnProcessingInstruction (pszTarget, pszData); + } + + // @cmember Comment handler wrapper + + static void __cdecl CommentHandler (void *pUserData, + const XML_Char *pszData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnComment (pszData); + } + + // @cmember Start CDATA section wrapper + + static void __cdecl StartCdataSectionHandler (void *pUserData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartCdataSection (); + } + + // @cmember End CDATA section wrapper + + static void __cdecl EndCdataSectionHandler (void *pUserData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndCdataSection (); + } + + // @cmember Default wrapper + + static void __cdecl DefaultHandler (void *pUserData, + const XML_Char *pszData, int nLength) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnDefault (pszData, nLength); + } + + // @cmember External entity ref wrapper + + static int __cdecl ExternalEntityRefHandler (void *pUserData, + const XML_Char *pszContext, const XML_Char *pszBase, + const XML_Char *pszSystemID, const XML_Char *pszPublicID) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + return pThis ->OnExternalEntityRef (pszContext, + pszBase, pszSystemID, pszPublicID) ? 1 : 0; + } + + // @cmember Unknown encoding wrapper + + static int __cdecl UnknownEncodingHandler (void * pUserData, const XML_Char * pszName, XML_Encoding * pInfo) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + return pThis ->OnUnknownEncoding (pszName, pInfo) ? 1 : 0; + } + + // @cmember Start namespace decl wrapper + + static void __cdecl StartNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix, const XML_Char * pszURI) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartNamespaceDecl (pszPrefix, pszURI); + } + + // @cmember End namespace decl wrapper + + static void __cdecl EndNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndNamespaceDecl (pszPrefix); + } + + // @cmember XML declaration wrapper + + static void __cdecl XmlDeclHandler (void *pUserData, const XML_Char *pszVersion, const XML_Char *pszEncoding, int nStandalone) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnXmlDecl (pszVersion, pszEncoding, nStandalone != 0); + } + + // @cmember Start Doctype declaration wrapper + + static void __cdecl StartDoctypeDeclHandler ( + void *pUserData, const XML_Char *pszDoctypeName, const XML_Char *pszSysID, + const XML_Char *pszPubID, int nHasInternalSubset + ) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartDoctypeDecl (pszDoctypeName, pszSysID, + pszPubID, nHasInternalSubset != 0); + } + + // @cmember End Doctype declaration wrapper + + static void __cdecl EndDoctypeDeclHandler (void *pUserData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndDoctypeDecl (); + } + + +protected: + + XML_Parser m_p; + + /// Returns the value of the specified attribute, if found; NULL otherwise + static const XML_Char * FindAttr(const XML_Char ** iAttrs, const XML_Char * iAttrToFind) + { + for (const XML_Char ** Attr = iAttrs; *Attr != NULL; Attr += 2) + { + if (strcmp(*Attr, iAttrToFind) == 0) + { + return *(Attr + 1); + } + } // for Attr - iAttrs[] + return NULL; + } +} ; + + + + diff --git a/src/lua5.1.dll b/src/lua5.1.dll new file mode 100644 index 000000000..515cf8b30 Binary files /dev/null and b/src/lua5.1.dll differ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 000000000..1f6aad24f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,197 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Root.h" + +#include //std::exception +#include //std::signal +#include //exit() + +#ifdef _MSC_VER + #include +#endif // _MSC_VER + + + + + +/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window +#define ENABLE_LEAK_FINDER + + + + + +#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + #pragma warning(push) + #pragma warning(disable:4100) + #include "LeakFinder.h" + #pragma warning(pop) +#endif + + + + + + +void ShowCrashReport(int) +{ + std::signal(SIGSEGV, SIG_DFL); + + printf("\n\nMCServer has crashed!\n"); + + exit(-1); +} + + + + + +#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Windows 32-bit stuff: when the server crashes, create a "dump file" containing the callstack of each thread and some variables; let the user send us that crash file for analysis + +typedef BOOL (WINAPI *pMiniDumpWriteDump)( + HANDLE hProcess, + DWORD ProcessId, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + PMINIDUMP_CALLBACK_INFORMATION CallbackParam +); + +pMiniDumpWriteDump g_WriteMiniDump; // The function in dbghlp DLL that creates dump files + +char g_DumpFileName[MAX_PATH]; // Filename of the dump file; hes to be created before the dump handler kicks in +char g_ExceptionStack[128 * 1024]; // Substitute stack, just in case the handler kicks in because of "insufficient stack space" +MINIDUMP_TYPE g_DumpFlags = MiniDumpNormal; // By default dump only the stack and some helpers + + + + + +/** This function gets called just before the "program executed an illegal instruction and will be terminated" or similar. +Its purpose is to create the crashdump using the dbghlp DLLs +*/ +LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_ExceptionInfo) +{ + char * newStack = &g_ExceptionStack[sizeof(g_ExceptionStack)]; + char * oldStack; + + // Use the substitute stack: + // This code is the reason why we don't support 64-bit (yet) + _asm + { + mov oldStack, esp + mov esp, newStack + } + + MINIDUMP_EXCEPTION_INFORMATION ExcInformation; + ExcInformation.ThreadId = GetCurrentThreadId(); + ExcInformation.ExceptionPointers = a_ExceptionInfo; + ExcInformation.ClientPointers = 0; + + // Write the dump file: + HANDLE dumpFile = CreateFile(g_DumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + g_WriteMiniDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, g_DumpFlags, (a_ExceptionInfo) ? &ExcInformation : NULL, NULL, NULL); + CloseHandle(dumpFile); + + // Revert to old stack: + _asm + { + mov esp, oldStack + } + + return 0; +} + +#endif // _WIN32 && !_WIN64 + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// main: + +int main( int argc, char **argv ) +{ + (void)argc; + (void)argv; + + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + InitLeakFinder(); + #endif + + // Magic code to produce dump-files on Windows if the server crashes: + #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) + HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL"); + g_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(hDbgHelp, "MiniDumpWriteDump"); + if (g_WriteMiniDump != NULL) + { + _snprintf_s(g_DumpFileName, ARRAYCOUNT(g_DumpFileName), _TRUNCATE, "crash_mcs_%x.dmp", GetCurrentProcessId()); + SetUnhandledExceptionFilter(LastChanceExceptionFilter); + + // Parse arguments for minidump flags: + for (int i = 0; i < argc; i++) + { + if (_stricmp(argv[i], "/cdg") == 0) + { + // Add globals to the dump + g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithDataSegs); + } + else if (_stricmp(argv[i], "/cdf") == 0) + { + // Add full memory to the dump (HUUUGE file) + g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithFullMemory); + } + } // for i - argv[] + } + #endif // _WIN32 && !_WIN64 + // End of dump-file magic + + #if defined(_DEBUG) && defined(_MSC_VER) + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); + + // _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output) + // Only useful when the leak is in the same sequence all the time + // _CrtSetBreakAlloc(85950); + + #endif // _DEBUG && _MSC_VER + + #ifndef _DEBUG + std::signal(SIGSEGV, ShowCrashReport); + #endif + + // DEBUG: test the dumpfile creation: + // *((int *)0) = 0; + + #if !defined(ANDROID_NDK) + try + #endif + { + cRoot Root; + Root.Start(); + } + #if !defined(ANDROID_NDK) + catch( std::exception& e ) + { + LOGERROR("Standard exception: %s", e.what() ); + } + catch( ... ) + { + LOGERROR("Unknown exception!"); + } + #endif + + + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + DeinitLeakFinder(); + #endif + + return 0; +} + + + + diff --git a/src/md5/md5.cpp b/src/md5/md5.cpp new file mode 100644 index 000000000..eae0fc3f2 --- /dev/null +++ b/src/md5/md5.cpp @@ -0,0 +1,369 @@ +/* MD5 + converted to C++ class by Frank Thilo (thilo@unix-ag.org) + for bzflag (http://www.bzflag.org) + + based on: + + md5.h and md5.c + reference implemantion of RFC 1321 + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +/* interface header */ +#include "md5.h" + +/* system implementation headers */ +#include + +#ifndef _WIN32 + #include +#endif + + + + + +// Constants for MD5Transform routine. +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +/////////////////////////////////////////////// + +// F, G, H and I are basic MD5 functions. +inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) { + return x&y | ~x&z; +} + +inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) { + return x&z | y&~z; +} + +inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) { + return x^y^z; +} + +inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) { + return y ^ (x | ~z); +} + +// rotate_left rotates x left n bits. +inline MD5::uint4 MD5::rotate_left(uint4 x, int n) { + return (x << n) | (x >> (32-n)); +} + +// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +// Rotation is separate from addition to prevent recomputation. +inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; +} + +inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a + G(b,c,d) + x + ac, s) + b; +} + +inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a + H(b,c,d) + x + ac, s) + b; +} + +inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a + I(b,c,d) + x + ac, s) + b; +} + +////////////////////////////////////////////// + +// default ctor, just initailize +MD5::MD5() +{ + init(); +} + +////////////////////////////////////////////// + +// nifty shortcut ctor, compute MD5 for string and finalize it right away +MD5::MD5(const std::string &text) +{ + init(); + update(text.c_str(), text.length()); + finalize(); +} + +////////////////////////////// + +void MD5::init() +{ + finalized=false; + + count[0] = 0; + count[1] = 0; + + // load magic initialization constants. + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; +} + +////////////////////////////// + +// decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. +void MD5::decode(uint4 output[], const uint1 input[], size_type len) +{ + for (unsigned int i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | + (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); +} + +////////////////////////////// + +// encodes input (uint4) into output (unsigned char). Assumes len is +// a multiple of 4. +void MD5::encode(uint1 output[], const uint4 input[], size_type len) +{ + for (size_type i = 0, j = 0; j < len; i++, j += 4) { + output[j] = input[i] & 0xff; + output[j+1] = (input[i] >> 8) & 0xff; + output[j+2] = (input[i] >> 16) & 0xff; + output[j+3] = (input[i] >> 24) & 0xff; + } +} + +////////////////////////////// + +// apply MD5 algo on a block +void MD5::transform(const uint1 block[blocksize]) +{ + uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + decode (x, block, blocksize); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + // Zeroize sensitive information. + memset(x, 0, sizeof x); +} + +////////////////////////////// + +// MD5 block update operation. Continues an MD5 message-digest +// operation, processing another message block +void MD5::update(const unsigned char input[], size_type length) +{ + // compute number of bytes mod 64 + size_type index = count[0] / 8 % blocksize; + + // Update number of bits + if ((count[0] += (length << 3)) < (length << 3)) + count[1]++; + count[1] += (length >> 29); + + // number of bytes we need to fill in buffer + size_type firstpart = 64 - index; + + size_type i; + + // transform as many times as possible. + if (length >= firstpart) + { + // fill buffer first, transform + memcpy(&buffer[index], input, firstpart); + transform(buffer); + + // transform chunks of blocksize (64 bytes) + for (i = firstpart; i + blocksize <= length; i += blocksize) + transform(&input[i]); + + index = 0; + } + else + i = 0; + + // buffer remaining input + memcpy(&buffer[index], &input[i], length-i); +} + +////////////////////////////// + +// for convenience provide a verson with signed char +void MD5::update(const char input[], size_type length) +{ + update((const unsigned char*)input, length); +} + +////////////////////////////// + +// MD5 finalization. Ends an MD5 message-digest operation, writing the +// the message digest and zeroizing the context. +MD5& MD5::finalize() +{ + static unsigned char padding[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + if (!finalized) { + // Save number of bits + unsigned char bits[8]; + encode(bits, count, 8); + + // pad out to 56 mod 64. + size_type index = count[0] / 8 % 64; + size_type padLen = (index < 56) ? (56 - index) : (120 - index); + update(padding, padLen); + + // Append length (before padding) + update(bits, 8); + + // Store state in digest + encode(digest, state, 16); + + // Zeroize sensitive information. + memset(buffer, 0, sizeof buffer); + memset(count, 0, sizeof count); + + finalized=true; + } + + return *this; +} + +////////////////////////////// + +// return hex representation of digest as string +std::string MD5::hexdigest() const +{ + if (!finalized) + return ""; + + char buf[33]; + for (int i=0; i<16; i++) + sprintf(buf+i*2, "%02x", digest[i]); + buf[32]=0; + + return std::string(buf); +} + +////////////////////////////// + +std::ostream& operator<<(std::ostream& out, MD5 md5) +{ + return out << md5.hexdigest(); +} + +////////////////////////////// + +std::string md5(const std::string & str) +{ + MD5 md5 = MD5(str); + + return md5.hexdigest(); +} diff --git a/src/md5/md5.h b/src/md5/md5.h new file mode 100644 index 000000000..ad5ad5384 --- /dev/null +++ b/src/md5/md5.h @@ -0,0 +1,93 @@ +/* MD5 + converted to C++ class by Frank Thilo (thilo@unix-ag.org) + for bzflag (http://www.bzflag.org) + + based on: + + md5.h and md5.c + reference implementation of RFC 1321 + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +#ifndef BZF_MD5_H +#define BZF_MD5_H + +#include +#include + + +// a small class for calculating MD5 hashes of strings or byte arrays +// it is not meant to be fast or secure +// +// usage: 1) feed it blocks of uchars with update() +// 2) finalize() +// 3) get hexdigest() string +// or +// MD5(std::string).hexdigest() +// +// assumes that char is 8 bit and int is 32 bit +class MD5 +{ +public: + typedef unsigned int size_type; // must be 32bit + + MD5(); + MD5(const std::string& text); + void update(const unsigned char *buf, size_type length); + void update(const char *buf, size_type length); + MD5& finalize(); + std::string hexdigest() const; + friend std::ostream& operator<<(std::ostream&, MD5 md5); + +private: + void init(); + typedef unsigned char uint1; // 8bit + typedef unsigned int uint4; // 32bit + enum {blocksize = 64}; // VC6 won't eat a const static int here + + void transform(const uint1 block[blocksize]); + static void decode(uint4 output[], const uint1 input[], size_type len); + static void encode(uint1 output[], const uint4 input[], size_type len); + + bool finalized; + uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk + uint4 count[2]; // 64bit counter for number of bits (lo, hi) + uint4 state[4]; // digest so far + uint1 digest[16]; // the result + + // low level logic operations + static inline uint4 F(uint4 x, uint4 y, uint4 z); + static inline uint4 G(uint4 x, uint4 y, uint4 z); + static inline uint4 H(uint4 x, uint4 y, uint4 z); + static inline uint4 I(uint4 x, uint4 y, uint4 z); + static inline uint4 rotate_left(uint4 x, int n); + static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); +}; + +std::string md5(const std::string & str); + +#endif \ No newline at end of file diff --git a/src/tolua++.exe b/src/tolua++.exe new file mode 100644 index 000000000..e5cec6d78 Binary files /dev/null and b/src/tolua++.exe differ diff --git a/src/tolua++.h b/src/tolua++.h new file mode 100644 index 000000000..ed5344926 --- /dev/null +++ b/src/tolua++.h @@ -0,0 +1,186 @@ +/* tolua +** Support code for Lua bindings. +** Written by Waldemar Celes +** TeCGraf/PUC-Rio +** Apr 2003 +** $Id: $ +*/ + +/* This code is free software; you can redistribute it and/or modify it. +** The software provided hereunder is on an "as is" basis, and +** the author has no obligation to provide maintenance, support, updates, +** enhancements, or modifications. +*/ + + +#ifndef TOLUA_H +#define TOLUA_H + +#ifndef TOLUA_API +#define TOLUA_API extern +#endif + +#define TOLUA_VERSION "tolua++-1.0.92" + +#ifdef __cplusplus +extern "C" { +#endif + +#define tolua_pushcppstring(x,y) tolua_pushstring(x,y.c_str()) +#define tolua_iscppstring tolua_isstring + +#define tolua_iscppstringarray tolua_isstringarray +#define tolua_pushfieldcppstring(L,lo,idx,s) tolua_pushfieldstring(L, lo, idx, s.c_str()) + +#ifndef TEMPLATE_BIND + #define TEMPLATE_BIND(p) +#endif + +#define TOLUA_TEMPLATE_BIND(p) + +#define TOLUA_PROTECTED_DESTRUCTOR +#define TOLUA_PROPERTY_TYPE(p) + +typedef int lua_Object; + +#include "lua.h" +#include "lauxlib.h" + +struct tolua_Error +{ + int index; + int array; + const char* type; +}; +typedef struct tolua_Error tolua_Error; + +#define TOLUA_NOPEER LUA_REGISTRYINDEX /* for lua 5.1 */ + +TOLUA_API const char* tolua_typename (lua_State* L, int lo); +TOLUA_API void tolua_error (lua_State* L, const char* msg, tolua_Error* err); +TOLUA_API int tolua_isnoobj (lua_State* L, int lo, tolua_Error* err); +TOLUA_API int tolua_isvalue (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isvaluenil (lua_State* L, int lo, tolua_Error* err); +TOLUA_API int tolua_isboolean (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isnumber (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isstring (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_istable (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isusertable (lua_State* L, int lo, const char* type, int def, tolua_Error* err); +TOLUA_API int tolua_isuserdata (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isusertype (lua_State* L, int lo, const char* type, int def, tolua_Error* err); +TOLUA_API int tolua_isvaluearray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isbooleanarray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isnumberarray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isstringarray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_istablearray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isuserdataarray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isusertypearray + (lua_State* L, int lo, const char* type, int dim, int def, tolua_Error* err); + +TOLUA_API void tolua_open (lua_State* L); + +TOLUA_API void* tolua_copy (lua_State* L, void* value, unsigned int size); +TOLUA_API int tolua_register_gc (lua_State* L, int lo); +TOLUA_API int tolua_default_collect (lua_State* tolua_S); + +TOLUA_API void tolua_usertype (lua_State* L, const char* type); +TOLUA_API void tolua_beginmodule (lua_State* L, const char* name); +TOLUA_API void tolua_endmodule (lua_State* L); +TOLUA_API void tolua_module (lua_State* L, const char* name, int hasvar); +TOLUA_API void tolua_class (lua_State* L, const char* name, const char* base); +TOLUA_API void tolua_cclass (lua_State* L, const char* lname, const char* name, const char* base, lua_CFunction col); +TOLUA_API void tolua_function (lua_State* L, const char* name, lua_CFunction func); +TOLUA_API void tolua_constant (lua_State* L, const char* name, lua_Number value); +TOLUA_API void tolua_variable (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set); +TOLUA_API void tolua_array (lua_State* L,const char* name, lua_CFunction get, lua_CFunction set); + +/* TOLUA_API void tolua_set_call_event(lua_State* L, lua_CFunction func, char* type); */ +/* TOLUA_API void tolua_addbase(lua_State* L, char* name, char* base); */ + +TOLUA_API void tolua_pushvalue (lua_State* L, int lo); +TOLUA_API void tolua_pushboolean (lua_State* L, int value); +TOLUA_API void tolua_pushnumber (lua_State* L, lua_Number value); +TOLUA_API void tolua_pushstring (lua_State* L, const char* value); +TOLUA_API void tolua_pushuserdata (lua_State* L, void* value); +TOLUA_API void tolua_pushusertype (lua_State* L, void* value, const char* type); +TOLUA_API void tolua_pushusertype_and_takeownership(lua_State* L, void* value, const char* type); +TOLUA_API void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v); +TOLUA_API void tolua_pushfieldboolean (lua_State* L, int lo, int index, int v); +TOLUA_API void tolua_pushfieldnumber (lua_State* L, int lo, int index, lua_Number v); +TOLUA_API void tolua_pushfieldstring (lua_State* L, int lo, int index, const char* v); +TOLUA_API void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v); +TOLUA_API void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, const char* type); +TOLUA_API void tolua_pushfieldusertype_and_takeownership (lua_State* L, int lo, int index, void* v, const char* type); + +TOLUA_API lua_Number tolua_tonumber (lua_State* L, int narg, lua_Number def); +TOLUA_API const char* tolua_tostring (lua_State* L, int narg, const char* def); +TOLUA_API void* tolua_touserdata (lua_State* L, int narg, void* def); +TOLUA_API void* tolua_tousertype (lua_State* L, int narg, void* def); +TOLUA_API int tolua_tovalue (lua_State* L, int narg, int def); +TOLUA_API int tolua_toboolean (lua_State* L, int narg, int def); +TOLUA_API lua_Number tolua_tofieldnumber (lua_State* L, int lo, int index, lua_Number def); +TOLUA_API const char* tolua_tofieldstring (lua_State* L, int lo, int index, const char* def); +TOLUA_API void* tolua_tofielduserdata (lua_State* L, int lo, int index, void* def); +TOLUA_API void* tolua_tofieldusertype (lua_State* L, int lo, int index, void* def); +TOLUA_API int tolua_tofieldvalue (lua_State* L, int lo, int index, int def); +TOLUA_API int tolua_getfieldboolean (lua_State* L, int lo, int index, int def); + +TOLUA_API void tolua_dobuffer(lua_State* L, char* B, unsigned int size, const char* name); + +TOLUA_API int class_gc_event (lua_State* L); + +#ifdef __cplusplus +static inline const char* tolua_tocppstring (lua_State* L, int narg, const char* def) { + + const char* s = tolua_tostring(L, narg, def); + return s?s:""; +}; + +static inline const char* tolua_tofieldcppstring (lua_State* L, int lo, int index, const char* def) { + + const char* s = tolua_tofieldstring(L, lo, index, def); + return s?s:""; +}; + +#else +#define tolua_tocppstring tolua_tostring +#define tolua_tofieldcppstring tolua_tofieldstring +#endif + +TOLUA_API int tolua_fast_isa(lua_State *L, int mt_indexa, int mt_indexb, int super_index); + +#ifndef Mtolua_new +#define Mtolua_new(EXP) new EXP +#endif + +#ifndef Mtolua_delete +#define Mtolua_delete(EXP) delete EXP +#endif + +#ifndef Mtolua_new_dim +#define Mtolua_new_dim(EXP, len) new EXP[len] +#endif + +#ifndef Mtolua_delete_dim +#define Mtolua_delete_dim(EXP) delete [] EXP +#endif + +#ifndef tolua_outside +#define tolua_outside +#endif + +#ifndef tolua_owned +#define tolua_owned +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/tolua_base.h b/src/tolua_base.h new file mode 100644 index 000000000..4f1038c09 --- /dev/null +++ b/src/tolua_base.h @@ -0,0 +1,128 @@ +#ifndef TOLUA_BASE_H +#define TOLUA_BASE_H + +#pragma warning(disable:4800) // This file is ONLY included by Bindings.cpp and it throws lots of C4800 warnings + +#include "tolua++.h" + + + + + +class ToluaBase { + + int lua_instance; + +protected: + + lua_State* lua_state; + + void lua_stacktrace(lua_State* L) const + { + lua_Debug entry; + int depth = 0; + + while (lua_getstack(L, depth, &entry)) + { + lua_getinfo(L, "Sln", &entry); + + LOGERROR("%s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?"); + depth++; + } + } + + + bool report_errors(int status) const + { + if ( status!=0 ) + { + const char* s = lua_tostring(lua_state, -1); + LOGERROR("-- %s", s ); + //lua_pop(lua_state, 1); + LOGERROR("Stack:"); + lua_stacktrace( lua_state ); + return true; + } + return false; + } + + bool push_method(const char* name, lua_CFunction f) const { + + if (!lua_state) return false; + + lua_getref(lua_state, lua_instance); + lua_pushstring(lua_state, name); + //LOGINFO("1. push_method() Stack size: %i", lua_gettop( lua_state ) ); + lua_gettable(lua_state, -2); + //LOGINFO("2. push_method() Stack size: %i", lua_gettop( lua_state ) ); + + if (lua_isnil(lua_state, -1)) { + + // pop the table + lua_pop(lua_state, 2); + return false; + + } else { + + if (f) { + if (lua_iscfunction(lua_state, -1)) { + lua_pop(lua_state, 2); + return false; + }; + /* // not for now + lua_pushcfunction(lua_state, f); + if (lua_rawequal(lua_state, -1, -2)) { + + // avoid recursion, pop both functions and the table + lua_pop(lua_state, 3); + return false; + }; + + // pop f + lua_pop(lua_state, 1); + */ + }; + + // swap table with function + lua_insert(lua_state, -2); + }; + + return true; + }; + + void dbcall(lua_State* L, int nargs, int nresults) const { + + // using lua_call for now + int s = lua_pcall(L, nargs, nresults, 0); + report_errors( s ); + }; +public: + + int GetInstance() { return lua_instance; } + lua_State* GetLuaState() { return lua_state; } + + void tolua__set_instance(lua_State* L, lua_Object lo) { + + lua_state = L; + + lua_pushvalue(L, lo); + lua_instance = lua_ref(lua_state, 1); + }; + + ToluaBase() { + + lua_state = NULL; + }; + + ~ToluaBase() { + + if (lua_state) { + + lua_unref(lua_state, lua_instance); + }; + }; +}; + +#endif + + diff --git a/src/virtual_method_hooks.lua b/src/virtual_method_hooks.lua new file mode 100644 index 000000000..15ff1d7f8 --- /dev/null +++ b/src/virtual_method_hooks.lua @@ -0,0 +1,506 @@ +-- flags +local disable_virtual_hooks = true +local enable_pure_virtual = true +local default_private_access = false + +local access = {public = 0, protected = 1, private = 2} + +function preparse_hook(p) + + if default_private_access then + -- we need to make all structs 'public' by default + p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n") + end +end + + +function parser_hook(s) + + local container = classContainer.curr -- get the current container + + if default_private_access then + if not container.curr_member_access and container.classtype == 'class' then + -- default access for classes is private + container.curr_member_access = access.private + end + end + + -- try labels (public, private, etc) + do + local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type' + if b then + + -- found a label, get the new access value from the global 'access' table + if access[label] then + container.curr_member_access = access[label] + end -- else ? + + return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:] + end + end + + + local ret = nil + + if disable_virtual_hooks then + + return ret + end + + local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())") + local const + if b then + local ret = string.sub(s, e+1) + if string.find(ret, "^%s*const") then + const = "const" + ret = string.gsub(ret, "^%s*const", "") + end + local purev = false + if string.find(ret, "^%s*=%s*0") then + purev = true + ret = string.gsub(ret, "^%s*=%s*0", "") + end + ret = string.gsub(ret, "^%s*%b{}", "") + + local func = Function(decl, arg, const) + func.pure_virtual = purev + --func.access = access + func.original_sig = decl + + local curflags = classContainer.curr.flags + if not curflags.virtual_class then + + curflags.virtual_class = VirtualClass() + end + curflags.virtual_class:add(func) + curflags.pure_virtual = curflags.pure_virtual or purev + + return ret + end + + return ret +end + + +-- class VirtualClass +classVirtualClass = { + classtype = 'class', + name = '', + base = '', + type = '', + btype = '', + ctype = '', +} +classVirtualClass.__index = classVirtualClass +setmetatable(classVirtualClass,classClass) + +function classVirtualClass:add(f) + + local parent = classContainer.curr + pop() + + table.insert(self.methods, {f=f}) + + local name,sig + + -- doble negative means positive + if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then + + name = self.original_name + elseif f.name == 'delete' then + name = '~'..self.original_name + else + if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then + name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name + end + end + + if name then + sig = name..self:get_arg_list(f, true)..";\n" + push(self) + sig = preprocess(sig) + self:parse(sig) + pop() + end + + push(parent) +end + +function preprocess(sig) + + sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' + sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' + sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*' + sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*' + + return sig +end + +function classVirtualClass:get_arg_list(f, decl) + + local ret = "" + local sep = "" + local i=1 + while f.args[i] do + + local arg = f.args[i] + if decl then + local ptr + if arg.ret ~= '' then + ptr = arg.ret + else + ptr = arg.ptr + end + local def = "" + if arg.def and arg.def ~= "" then + + def = " = "..arg.def + end + ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def + else + ret = ret..sep..arg.name + end + + sep = "," + i = i+1 + end + + return "("..ret..")" +end + +function classVirtualClass:add_parent_virtual_methods(parent) + + parent = parent or _global_classes[self.flags.parent_object.btype] + + if not parent then return end + + if parent.flags.virtual_class then + + local vclass = parent.flags.virtual_class + for k,v in ipairs(vclass.methods) do + if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then + table.insert(self.methods, {f=v.f}) + end + end + end + + parent = _global_classes[parent.btype] + if parent then + self:add_parent_virtual_methods(parent) + end +end + +function classVirtualClass:has_method(f) + + for k,v in pairs(self.methods) do + -- just match name for now + if v.f.name == f.name then + return true + end + end + + return false +end + +function classVirtualClass:add_constructors() + + local i=1 + while self.flags.parent_object[i] do + + local v = self.flags.parent_object[i] + if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then + + self:add(v) + end + + i = i+1 + end + +end + +--[[ +function classVirtualClass:requirecollection(t) + + self:add_constructors() + local req = classClass.requirecollection(self, t) + if req then + output('class ',self.name,";") + end + return req +end +--]] + +function classVirtualClass:supcode() + + -- pure virtual classes can have no default constructors on gcc 4 + + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n') + end + + local ns + if self.prox.classtype == 'namespace' then + output('namespace ',self.prox.name, " {") + ns = true + end + + output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {") + + output("public:\n") + + self:add_parent_virtual_methods() + + self:output_methods(self.btype) + self:output_parent_methods() + + self:add_constructors() + + -- no constructor for pure virtual classes + if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then + + self:output_constructors() + end + + output("};\n\n") + + if ns then + output("};") + end + + classClass.supcode(self) + + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#endif // __GNUC__ >= 4\n') + end + + -- output collector for custom class if required + if self:requirecollection(_collect) and _collect[self.type] then + + output('\n') + output('/* function to release collected object via destructor */') + output('#ifdef __cplusplus\n') + --for i,v in pairs(collect) do + i,v = self.type, _collect[self.type] + output('\nstatic int '..v..' (lua_State* tolua_S)') + output('{') + output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);') + output(' delete self;') + output(' return 0;') + output('}') + --end + output('#endif\n\n') + end + +end + +function classVirtualClass:register(pre) + + -- pure virtual classes can have no default constructors on gcc 4 + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n') + end + + classClass.register(self, pre) + + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#endif // __GNUC__ >= 4\n') + end +end + + +--function classVirtualClass:requirecollection(_c) +-- if self.flags.parent_object.flags.pure_virtual then +-- return false +-- end +-- return classClass.requirecollection(self, _c) +--end + +function classVirtualClass:output_parent_methods() + + for k,v in ipairs(self.methods) do + + if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then + + local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." " + local parent_name = rettype..self.btype.."__"..v.f.name + + local par_list = self:get_arg_list(v.f, true) + local var_list = self:get_arg_list(v.f, false) + + -- the parent's virtual function + output("\t"..parent_name..par_list.." {") + + output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";") + output("\t};") + end + end +end + +function classVirtualClass:output_methods(btype) + + for k,v in ipairs(self.methods) do + + if v.f.name ~= 'new' and v.f.name ~= 'delete' then + + self:output_method(v.f, btype) + end + end + output("\n") +end + +function classVirtualClass:output_constructors() + + for k,v in ipairs(self.methods) do + + if v.f.name == 'new' then + + local par_list = self:get_arg_list(v.f, true) + local var_list = self:get_arg_list(v.f, false) + + output("\t",self.original_name,par_list,":",self.btype,var_list,"{};") + end + end +end + +function classVirtualClass:output_method(f, btype) + + if f.access == 2 then -- private + return + end + + local ptr + if f.ret ~= '' then + ptr = f.ret + else + ptr = f.ptr + end + + local rettype = f.mod.." "..f.type..f.ptr.." " + local par_list = self:get_arg_list(f, true) + local var_list = self:get_arg_list(f, false) + + if string.find(rettype, "%s*LuaQtGenericFlags%s*") then + + _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+") + end + + -- the caller of the lua method + output("\t"..rettype.." "..f.name..par_list..f.const.." {") + local fn = f.cname + if f.access == 1 then + fn = "NULL" + end + output('\t\tif (push_method("',f.lname,'", ',fn,')) {') + + --if f.type ~= 'void' then + -- output("\t\t\tint top = lua_gettop(lua_state)-1;") + --end + + -- push the parameters + local argn = 0 + for i,arg in ipairs(f.args) do + if arg.type ~= 'void' then + local t,ct = isbasic(arg.type) + if t and t ~= '' then + if arg.ret == "*" then + t = 'userdata' + ct = 'void*' + end + output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");"); + else + local m = arg.ptr + if m and m~= "" then + if m == "*" then m = "" end + output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");") + else + output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n") + output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n') + end + end + argn = argn+1 + end + end + + -- call the function + output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ") + + -- return value + if f.type ~= 'void' then + output("1);") + + local t,ct = isbasic(f.type) + if t and t ~= '' then + --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);") + output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);") + else + + local mod = "" + if f.ptr ~= "*" then + mod = "*("..f.type.."*)" + end + + --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);") + output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);") + end + output("\t\t\tlua_pop(lua_state, 1);") + output("\t\t\treturn tolua_ret;") + else + output("0);") + end + + -- handle non-implemeted function + output("\t\t} else {") + + if f.pure_virtual then + + output('\t\t\tif (lua_state)') + --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);') + output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");') + output('\t\t\telse {') + output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");') + output('\t\t\t\t::abort();') + output('\t\t\t};') + if( rettype == " std::string " ) then + output('\t\t\treturn "";') + else + output('\t\t\treturn (',rettype,')0;') + end + else + + output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';') + end + + output("\t\t};") + + output("\t};") +end + +function VirtualClass() + + local parent = classContainer.curr + pop() + + local name = "Lua__"..parent.original_name + + local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil}) + setmetatable(c, classVirtualClass) + + local ft = getnamespace(c.parent)..c.original_name + append_global_type(ft, c) + + push(parent) + + c.flags.parent_object = parent + c.methods = {} + + push(c) + c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n") + pop() + + return c +end + + + + + -- cgit v1.2.3 From 3438e5d3ddf8444f0e31009ffbe8237ef3752c22 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sun, 24 Nov 2013 14:21:13 +0000 Subject: move cryptopp into lib --- CryptoPP/Doxyfile | 1634 ----------------- CryptoPP/License.txt | 51 - CryptoPP/Readme.txt | 452 ----- CryptoPP/adler32.cpp | 77 - CryptoPP/adler32.h | 28 - CryptoPP/aes.h | 16 - CryptoPP/algebra.cpp | 340 ---- CryptoPP/algebra.h | 285 --- CryptoPP/algparam.cpp | 75 - CryptoPP/algparam.h | 398 ----- CryptoPP/argnames.h | 81 - CryptoPP/asn.cpp | 597 ------- CryptoPP/asn.h | 369 ---- CryptoPP/authenc.cpp | 180 -- CryptoPP/authenc.h | 49 - CryptoPP/base32.cpp | 39 - CryptoPP/base32.h | 38 - CryptoPP/base64.cpp | 42 - CryptoPP/base64.h | 36 - CryptoPP/basecode.cpp | 238 --- CryptoPP/basecode.h | 86 - CryptoPP/cbcmac.cpp | 62 - CryptoPP/cbcmac.h | 50 - CryptoPP/ccm.cpp | 140 -- CryptoPP/ccm.h | 101 -- CryptoPP/channels.cpp | 309 ---- CryptoPP/channels.h | 123 -- CryptoPP/cmac.cpp | 122 -- CryptoPP/cmac.h | 52 - CryptoPP/config.h | 462 ----- CryptoPP/cpu.cpp | 199 --- CryptoPP/cpu.h | 345 ---- CryptoPP/crc.cpp | 160 -- CryptoPP/crc.h | 42 - CryptoPP/cryptlib.cpp | 828 --------- CryptoPP/cryptlib.h | 1655 ------------------ CryptoPP/default.cpp | 258 --- CryptoPP/default.h | 104 -- CryptoPP/des.cpp | 449 ----- CryptoPP/des.h | 144 -- CryptoPP/dessp.cpp | 95 - CryptoPP/dh.cpp | 19 - CryptoPP/dh.h | 99 -- CryptoPP/dh2.cpp | 22 - CryptoPP/dh2.h | 58 - CryptoPP/dll.cpp | 146 -- CryptoPP/dll.h | 70 - CryptoPP/dmac.h | 93 - CryptoPP/dsa.cpp | 63 - CryptoPP/dsa.h | 35 - CryptoPP/eax.cpp | 59 - CryptoPP/eax.h | 91 - CryptoPP/ec2n.cpp | 292 ---- CryptoPP/ec2n.h | 113 -- CryptoPP/eccrypto.cpp | 694 -------- CryptoPP/eccrypto.h | 280 --- CryptoPP/ecp.cpp | 473 ----- CryptoPP/ecp.h | 126 -- CryptoPP/elgamal.cpp | 17 - CryptoPP/elgamal.h | 121 -- CryptoPP/emsa2.cpp | 34 - CryptoPP/emsa2.h | 86 - CryptoPP/eprecomp.cpp | 112 -- CryptoPP/eprecomp.h | 75 - CryptoPP/esign.cpp | 210 --- CryptoPP/esign.h | 128 -- CryptoPP/factory.h | 136 -- CryptoPP/files.cpp | 259 --- CryptoPP/files.h | 112 -- CryptoPP/filters.cpp | 1120 ------------ CryptoPP/filters.h | 810 --------- CryptoPP/fips140.cpp | 84 - CryptoPP/fips140.h | 59 - CryptoPP/fltrimpl.h | 67 - CryptoPP/gcm.cpp | 828 --------- CryptoPP/gcm.h | 106 -- CryptoPP/gf256.cpp | 34 - CryptoPP/gf256.h | 66 - CryptoPP/gf2_32.cpp | 99 -- CryptoPP/gf2_32.h | 66 - CryptoPP/gf2n.cpp | 882 ---------- CryptoPP/gf2n.h | 369 ---- CryptoPP/gfpcrypt.cpp | 273 --- CryptoPP/gfpcrypt.h | 528 ------ CryptoPP/gzip.h | 65 - CryptoPP/hex.cpp | 44 - CryptoPP/hex.h | 36 - CryptoPP/hmac.cpp | 86 - CryptoPP/hmac.h | 61 - CryptoPP/hrtimer.cpp | 138 -- CryptoPP/hrtimer.h | 61 - CryptoPP/integer.cpp | 4235 --------------------------------------------- CryptoPP/integer.h | 420 ----- CryptoPP/iterhash.cpp | 160 -- CryptoPP/iterhash.h | 106 -- CryptoPP/lubyrack.h | 141 -- CryptoPP/luc.cpp | 210 --- CryptoPP/luc.h | 236 --- CryptoPP/md2.cpp | 120 -- CryptoPP/md2.h | 46 - CryptoPP/md4.cpp | 110 -- CryptoPP/md4.h | 35 - CryptoPP/md5.cpp | 118 -- CryptoPP/md5.h | 33 - CryptoPP/mdc.h | 72 - CryptoPP/misc.cpp | 189 -- CryptoPP/misc.h | 1282 -------------- CryptoPP/modarith.h | 158 -- CryptoPP/modes.cpp | 245 --- CryptoPP/modes.h | 422 ----- CryptoPP/modexppc.h | 34 - CryptoPP/mqueue.cpp | 174 -- CryptoPP/mqueue.h | 100 -- CryptoPP/mqv.cpp | 13 - CryptoPP/mqv.h | 141 -- CryptoPP/nbtheory.cpp | 1123 ------------ CryptoPP/nbtheory.h | 131 -- CryptoPP/network.cpp | 550 ------ CryptoPP/network.h | 235 --- CryptoPP/nr.h | 6 - CryptoPP/oaep.cpp | 97 -- CryptoPP/oaep.h | 42 - CryptoPP/oids.h | 123 -- CryptoPP/osrng.cpp | 192 -- CryptoPP/osrng.h | 156 -- CryptoPP/pch.cpp | 1 - CryptoPP/pch.h | 21 - CryptoPP/pkcspad.cpp | 124 -- CryptoPP/pkcspad.h | 94 - CryptoPP/polynomi.cpp | 577 ------ CryptoPP/polynomi.h | 459 ----- CryptoPP/pssr.cpp | 145 -- CryptoPP/pssr.h | 66 - CryptoPP/pubkey.cpp | 165 -- CryptoPP/pubkey.h | 1678 ------------------ CryptoPP/pwdbased.h | 214 --- CryptoPP/queue.cpp | 565 ------ CryptoPP/queue.h | 144 -- CryptoPP/rabin.cpp | 221 --- CryptoPP/rabin.h | 107 -- CryptoPP/randpool.cpp | 63 - CryptoPP/randpool.h | 33 - CryptoPP/rdtables.cpp | 172 -- CryptoPP/resource.h | 15 - CryptoPP/rijndael.cpp | 1261 -------------- CryptoPP/rijndael.h | 68 - CryptoPP/rng.cpp | 155 -- CryptoPP/rng.h | 77 - CryptoPP/rsa.cpp | 304 ---- CryptoPP/rsa.h | 174 -- CryptoPP/rw.cpp | 196 --- CryptoPP/rw.h | 102 -- CryptoPP/safer.cpp | 153 -- CryptoPP/safer.h | 86 - CryptoPP/seal.cpp | 213 --- CryptoPP/seal.h | 44 - CryptoPP/secblock.h | 467 ----- CryptoPP/seckey.h | 221 --- CryptoPP/seed.cpp | 104 -- CryptoPP/seed.h | 38 - CryptoPP/sha.cpp | 900 ---------- CryptoPP/sha.h | 63 - CryptoPP/shacal2.cpp | 140 -- CryptoPP/shacal2.h | 54 - CryptoPP/simple.cpp | 14 - CryptoPP/simple.h | 209 --- CryptoPP/smartptr.h | 223 --- CryptoPP/socketft.cpp | 531 ------ CryptoPP/socketft.h | 224 --- CryptoPP/square.cpp | 177 -- CryptoPP/square.h | 58 - CryptoPP/squaretb.cpp | 582 ------- CryptoPP/stdcpp.h | 41 - CryptoPP/strciphr.cpp | 252 --- CryptoPP/strciphr.h | 306 ---- CryptoPP/tea.cpp | 159 -- CryptoPP/tea.h | 132 -- CryptoPP/tiger.cpp | 265 --- CryptoPP/tiger.h | 24 - CryptoPP/tigertab.cpp | 525 ------ CryptoPP/trdlocal.cpp | 73 - CryptoPP/trdlocal.h | 44 - CryptoPP/trunhash.h | 48 - CryptoPP/ttmac.cpp | 338 ---- CryptoPP/ttmac.h | 38 - CryptoPP/validate.h | 81 - CryptoPP/vmac.cpp | 832 --------- CryptoPP/vmac.h | 68 - CryptoPP/wait.cpp | 397 ----- CryptoPP/wait.h | 208 --- CryptoPP/winpipes.cpp | 205 --- CryptoPP/winpipes.h | 142 -- CryptoPP/words.h | 103 -- lib/cryptopp/Doxyfile | 1634 +++++++++++++++++ lib/cryptopp/License.txt | 51 + lib/cryptopp/Readme.txt | 452 +++++ lib/cryptopp/adler32.cpp | 77 + lib/cryptopp/adler32.h | 28 + lib/cryptopp/aes.h | 16 + lib/cryptopp/algebra.cpp | 340 ++++ lib/cryptopp/algebra.h | 285 +++ lib/cryptopp/algparam.cpp | 75 + lib/cryptopp/algparam.h | 398 +++++ lib/cryptopp/argnames.h | 81 + lib/cryptopp/asn.cpp | 597 +++++++ lib/cryptopp/asn.h | 369 ++++ lib/cryptopp/authenc.cpp | 180 ++ lib/cryptopp/authenc.h | 49 + lib/cryptopp/base32.cpp | 39 + lib/cryptopp/base32.h | 38 + lib/cryptopp/base64.cpp | 42 + lib/cryptopp/base64.h | 36 + lib/cryptopp/basecode.cpp | 238 +++ lib/cryptopp/basecode.h | 86 + lib/cryptopp/cbcmac.cpp | 62 + lib/cryptopp/cbcmac.h | 50 + lib/cryptopp/ccm.cpp | 140 ++ lib/cryptopp/ccm.h | 101 ++ lib/cryptopp/channels.cpp | 309 ++++ lib/cryptopp/channels.h | 123 ++ lib/cryptopp/cmac.cpp | 122 ++ lib/cryptopp/cmac.h | 52 + lib/cryptopp/config.h | 462 +++++ lib/cryptopp/cpu.cpp | 199 +++ lib/cryptopp/cpu.h | 345 ++++ lib/cryptopp/crc.cpp | 160 ++ lib/cryptopp/crc.h | 42 + lib/cryptopp/cryptlib.cpp | 828 +++++++++ lib/cryptopp/cryptlib.h | 1655 ++++++++++++++++++ lib/cryptopp/default.cpp | 258 +++ lib/cryptopp/default.h | 104 ++ lib/cryptopp/des.cpp | 449 +++++ lib/cryptopp/des.h | 144 ++ lib/cryptopp/dessp.cpp | 95 + lib/cryptopp/dh.cpp | 19 + lib/cryptopp/dh.h | 99 ++ lib/cryptopp/dh2.cpp | 22 + lib/cryptopp/dh2.h | 58 + lib/cryptopp/dll.cpp | 146 ++ lib/cryptopp/dll.h | 70 + lib/cryptopp/dmac.h | 93 + lib/cryptopp/dsa.cpp | 63 + lib/cryptopp/dsa.h | 35 + lib/cryptopp/eax.cpp | 59 + lib/cryptopp/eax.h | 91 + lib/cryptopp/ec2n.cpp | 292 ++++ lib/cryptopp/ec2n.h | 113 ++ lib/cryptopp/eccrypto.cpp | 694 ++++++++ lib/cryptopp/eccrypto.h | 280 +++ lib/cryptopp/ecp.cpp | 473 +++++ lib/cryptopp/ecp.h | 126 ++ lib/cryptopp/elgamal.cpp | 17 + lib/cryptopp/elgamal.h | 121 ++ lib/cryptopp/emsa2.cpp | 34 + lib/cryptopp/emsa2.h | 86 + lib/cryptopp/eprecomp.cpp | 112 ++ lib/cryptopp/eprecomp.h | 75 + lib/cryptopp/esign.cpp | 210 +++ lib/cryptopp/esign.h | 128 ++ lib/cryptopp/factory.h | 136 ++ lib/cryptopp/files.cpp | 259 +++ lib/cryptopp/files.h | 112 ++ lib/cryptopp/filters.cpp | 1120 ++++++++++++ lib/cryptopp/filters.h | 810 +++++++++ lib/cryptopp/fips140.cpp | 84 + lib/cryptopp/fips140.h | 59 + lib/cryptopp/fltrimpl.h | 67 + lib/cryptopp/gcm.cpp | 828 +++++++++ lib/cryptopp/gcm.h | 106 ++ lib/cryptopp/gf256.cpp | 34 + lib/cryptopp/gf256.h | 66 + lib/cryptopp/gf2_32.cpp | 99 ++ lib/cryptopp/gf2_32.h | 66 + lib/cryptopp/gf2n.cpp | 882 ++++++++++ lib/cryptopp/gf2n.h | 369 ++++ lib/cryptopp/gfpcrypt.cpp | 273 +++ lib/cryptopp/gfpcrypt.h | 528 ++++++ lib/cryptopp/gzip.h | 65 + lib/cryptopp/hex.cpp | 44 + lib/cryptopp/hex.h | 36 + lib/cryptopp/hmac.cpp | 86 + lib/cryptopp/hmac.h | 61 + lib/cryptopp/hrtimer.cpp | 138 ++ lib/cryptopp/hrtimer.h | 61 + lib/cryptopp/integer.cpp | 4235 +++++++++++++++++++++++++++++++++++++++++++++ lib/cryptopp/integer.h | 420 +++++ lib/cryptopp/iterhash.cpp | 160 ++ lib/cryptopp/iterhash.h | 106 ++ lib/cryptopp/lubyrack.h | 141 ++ lib/cryptopp/luc.cpp | 210 +++ lib/cryptopp/luc.h | 236 +++ lib/cryptopp/md2.cpp | 120 ++ lib/cryptopp/md2.h | 46 + lib/cryptopp/md4.cpp | 110 ++ lib/cryptopp/md4.h | 35 + lib/cryptopp/md5.cpp | 118 ++ lib/cryptopp/md5.h | 33 + lib/cryptopp/mdc.h | 72 + lib/cryptopp/misc.cpp | 189 ++ lib/cryptopp/misc.h | 1282 ++++++++++++++ lib/cryptopp/modarith.h | 158 ++ lib/cryptopp/modes.cpp | 245 +++ lib/cryptopp/modes.h | 422 +++++ lib/cryptopp/modexppc.h | 34 + lib/cryptopp/mqueue.cpp | 174 ++ lib/cryptopp/mqueue.h | 100 ++ lib/cryptopp/mqv.cpp | 13 + lib/cryptopp/mqv.h | 141 ++ lib/cryptopp/nbtheory.cpp | 1123 ++++++++++++ lib/cryptopp/nbtheory.h | 131 ++ lib/cryptopp/network.cpp | 550 ++++++ lib/cryptopp/network.h | 235 +++ lib/cryptopp/nr.h | 6 + lib/cryptopp/oaep.cpp | 97 ++ lib/cryptopp/oaep.h | 42 + lib/cryptopp/oids.h | 123 ++ lib/cryptopp/osrng.cpp | 192 ++ lib/cryptopp/osrng.h | 156 ++ lib/cryptopp/pch.cpp | 1 + lib/cryptopp/pch.h | 21 + lib/cryptopp/pkcspad.cpp | 124 ++ lib/cryptopp/pkcspad.h | 94 + lib/cryptopp/polynomi.cpp | 577 ++++++ lib/cryptopp/polynomi.h | 459 +++++ lib/cryptopp/pssr.cpp | 145 ++ lib/cryptopp/pssr.h | 66 + lib/cryptopp/pubkey.cpp | 165 ++ lib/cryptopp/pubkey.h | 1678 ++++++++++++++++++ lib/cryptopp/pwdbased.h | 214 +++ lib/cryptopp/queue.cpp | 565 ++++++ lib/cryptopp/queue.h | 144 ++ lib/cryptopp/rabin.cpp | 221 +++ lib/cryptopp/rabin.h | 107 ++ lib/cryptopp/randpool.cpp | 63 + lib/cryptopp/randpool.h | 33 + lib/cryptopp/rdtables.cpp | 172 ++ lib/cryptopp/resource.h | 15 + lib/cryptopp/rijndael.cpp | 1261 ++++++++++++++ lib/cryptopp/rijndael.h | 68 + lib/cryptopp/rng.cpp | 155 ++ lib/cryptopp/rng.h | 77 + lib/cryptopp/rsa.cpp | 304 ++++ lib/cryptopp/rsa.h | 174 ++ lib/cryptopp/rw.cpp | 196 +++ lib/cryptopp/rw.h | 102 ++ lib/cryptopp/safer.cpp | 153 ++ lib/cryptopp/safer.h | 86 + lib/cryptopp/seal.cpp | 213 +++ lib/cryptopp/seal.h | 44 + lib/cryptopp/secblock.h | 467 +++++ lib/cryptopp/seckey.h | 221 +++ lib/cryptopp/seed.cpp | 104 ++ lib/cryptopp/seed.h | 38 + lib/cryptopp/sha.cpp | 900 ++++++++++ lib/cryptopp/sha.h | 63 + lib/cryptopp/shacal2.cpp | 140 ++ lib/cryptopp/shacal2.h | 54 + lib/cryptopp/simple.cpp | 14 + lib/cryptopp/simple.h | 209 +++ lib/cryptopp/smartptr.h | 223 +++ lib/cryptopp/socketft.cpp | 531 ++++++ lib/cryptopp/socketft.h | 224 +++ lib/cryptopp/square.cpp | 177 ++ lib/cryptopp/square.h | 58 + lib/cryptopp/squaretb.cpp | 582 +++++++ lib/cryptopp/stdcpp.h | 41 + lib/cryptopp/strciphr.cpp | 252 +++ lib/cryptopp/strciphr.h | 306 ++++ lib/cryptopp/tea.cpp | 159 ++ lib/cryptopp/tea.h | 132 ++ lib/cryptopp/tiger.cpp | 265 +++ lib/cryptopp/tiger.h | 24 + lib/cryptopp/tigertab.cpp | 525 ++++++ lib/cryptopp/trdlocal.cpp | 73 + lib/cryptopp/trdlocal.h | 44 + lib/cryptopp/trunhash.h | 48 + lib/cryptopp/ttmac.cpp | 338 ++++ lib/cryptopp/ttmac.h | 38 + lib/cryptopp/validate.h | 81 + lib/cryptopp/vmac.cpp | 832 +++++++++ lib/cryptopp/vmac.h | 68 + lib/cryptopp/wait.cpp | 397 +++++ lib/cryptopp/wait.h | 208 +++ lib/cryptopp/winpipes.cpp | 205 +++ lib/cryptopp/winpipes.h | 142 ++ lib/cryptopp/words.h | 103 ++ 386 files changed, 48154 insertions(+), 48154 deletions(-) delete mode 100644 CryptoPP/Doxyfile delete mode 100644 CryptoPP/License.txt delete mode 100644 CryptoPP/Readme.txt delete mode 100644 CryptoPP/adler32.cpp delete mode 100644 CryptoPP/adler32.h delete mode 100644 CryptoPP/aes.h delete mode 100644 CryptoPP/algebra.cpp delete mode 100644 CryptoPP/algebra.h delete mode 100644 CryptoPP/algparam.cpp delete mode 100644 CryptoPP/algparam.h delete mode 100644 CryptoPP/argnames.h delete mode 100644 CryptoPP/asn.cpp delete mode 100644 CryptoPP/asn.h delete mode 100644 CryptoPP/authenc.cpp delete mode 100644 CryptoPP/authenc.h delete mode 100644 CryptoPP/base32.cpp delete mode 100644 CryptoPP/base32.h delete mode 100644 CryptoPP/base64.cpp delete mode 100644 CryptoPP/base64.h delete mode 100644 CryptoPP/basecode.cpp delete mode 100644 CryptoPP/basecode.h delete mode 100644 CryptoPP/cbcmac.cpp delete mode 100644 CryptoPP/cbcmac.h delete mode 100644 CryptoPP/ccm.cpp delete mode 100644 CryptoPP/ccm.h delete mode 100644 CryptoPP/channels.cpp delete mode 100644 CryptoPP/channels.h delete mode 100644 CryptoPP/cmac.cpp delete mode 100644 CryptoPP/cmac.h delete mode 100644 CryptoPP/config.h delete mode 100644 CryptoPP/cpu.cpp delete mode 100644 CryptoPP/cpu.h delete mode 100644 CryptoPP/crc.cpp delete mode 100644 CryptoPP/crc.h delete mode 100644 CryptoPP/cryptlib.cpp delete mode 100644 CryptoPP/cryptlib.h delete mode 100644 CryptoPP/default.cpp delete mode 100644 CryptoPP/default.h delete mode 100644 CryptoPP/des.cpp delete mode 100644 CryptoPP/des.h delete mode 100644 CryptoPP/dessp.cpp delete mode 100644 CryptoPP/dh.cpp delete mode 100644 CryptoPP/dh.h delete mode 100644 CryptoPP/dh2.cpp delete mode 100644 CryptoPP/dh2.h delete mode 100644 CryptoPP/dll.cpp delete mode 100644 CryptoPP/dll.h delete mode 100644 CryptoPP/dmac.h delete mode 100644 CryptoPP/dsa.cpp delete mode 100644 CryptoPP/dsa.h delete mode 100644 CryptoPP/eax.cpp delete mode 100644 CryptoPP/eax.h delete mode 100644 CryptoPP/ec2n.cpp delete mode 100644 CryptoPP/ec2n.h delete mode 100644 CryptoPP/eccrypto.cpp delete mode 100644 CryptoPP/eccrypto.h delete mode 100644 CryptoPP/ecp.cpp delete mode 100644 CryptoPP/ecp.h delete mode 100644 CryptoPP/elgamal.cpp delete mode 100644 CryptoPP/elgamal.h delete mode 100644 CryptoPP/emsa2.cpp delete mode 100644 CryptoPP/emsa2.h delete mode 100644 CryptoPP/eprecomp.cpp delete mode 100644 CryptoPP/eprecomp.h delete mode 100644 CryptoPP/esign.cpp delete mode 100644 CryptoPP/esign.h delete mode 100644 CryptoPP/factory.h delete mode 100644 CryptoPP/files.cpp delete mode 100644 CryptoPP/files.h delete mode 100644 CryptoPP/filters.cpp delete mode 100644 CryptoPP/filters.h delete mode 100644 CryptoPP/fips140.cpp delete mode 100644 CryptoPP/fips140.h delete mode 100644 CryptoPP/fltrimpl.h delete mode 100644 CryptoPP/gcm.cpp delete mode 100644 CryptoPP/gcm.h delete mode 100644 CryptoPP/gf256.cpp delete mode 100644 CryptoPP/gf256.h delete mode 100644 CryptoPP/gf2_32.cpp delete mode 100644 CryptoPP/gf2_32.h delete mode 100644 CryptoPP/gf2n.cpp delete mode 100644 CryptoPP/gf2n.h delete mode 100644 CryptoPP/gfpcrypt.cpp delete mode 100644 CryptoPP/gfpcrypt.h delete mode 100644 CryptoPP/gzip.h delete mode 100644 CryptoPP/hex.cpp delete mode 100644 CryptoPP/hex.h delete mode 100644 CryptoPP/hmac.cpp delete mode 100644 CryptoPP/hmac.h delete mode 100644 CryptoPP/hrtimer.cpp delete mode 100644 CryptoPP/hrtimer.h delete mode 100644 CryptoPP/integer.cpp delete mode 100644 CryptoPP/integer.h delete mode 100644 CryptoPP/iterhash.cpp delete mode 100644 CryptoPP/iterhash.h delete mode 100644 CryptoPP/lubyrack.h delete mode 100644 CryptoPP/luc.cpp delete mode 100644 CryptoPP/luc.h delete mode 100644 CryptoPP/md2.cpp delete mode 100644 CryptoPP/md2.h delete mode 100644 CryptoPP/md4.cpp delete mode 100644 CryptoPP/md4.h delete mode 100644 CryptoPP/md5.cpp delete mode 100644 CryptoPP/md5.h delete mode 100644 CryptoPP/mdc.h delete mode 100644 CryptoPP/misc.cpp delete mode 100644 CryptoPP/misc.h delete mode 100644 CryptoPP/modarith.h delete mode 100644 CryptoPP/modes.cpp delete mode 100644 CryptoPP/modes.h delete mode 100644 CryptoPP/modexppc.h delete mode 100644 CryptoPP/mqueue.cpp delete mode 100644 CryptoPP/mqueue.h delete mode 100644 CryptoPP/mqv.cpp delete mode 100644 CryptoPP/mqv.h delete mode 100644 CryptoPP/nbtheory.cpp delete mode 100644 CryptoPP/nbtheory.h delete mode 100644 CryptoPP/network.cpp delete mode 100644 CryptoPP/network.h delete mode 100644 CryptoPP/nr.h delete mode 100644 CryptoPP/oaep.cpp delete mode 100644 CryptoPP/oaep.h delete mode 100644 CryptoPP/oids.h delete mode 100644 CryptoPP/osrng.cpp delete mode 100644 CryptoPP/osrng.h delete mode 100644 CryptoPP/pch.cpp delete mode 100644 CryptoPP/pch.h delete mode 100644 CryptoPP/pkcspad.cpp delete mode 100644 CryptoPP/pkcspad.h delete mode 100644 CryptoPP/polynomi.cpp delete mode 100644 CryptoPP/polynomi.h delete mode 100644 CryptoPP/pssr.cpp delete mode 100644 CryptoPP/pssr.h delete mode 100644 CryptoPP/pubkey.cpp delete mode 100644 CryptoPP/pubkey.h delete mode 100644 CryptoPP/pwdbased.h delete mode 100644 CryptoPP/queue.cpp delete mode 100644 CryptoPP/queue.h delete mode 100644 CryptoPP/rabin.cpp delete mode 100644 CryptoPP/rabin.h delete mode 100644 CryptoPP/randpool.cpp delete mode 100644 CryptoPP/randpool.h delete mode 100644 CryptoPP/rdtables.cpp delete mode 100644 CryptoPP/resource.h delete mode 100644 CryptoPP/rijndael.cpp delete mode 100644 CryptoPP/rijndael.h delete mode 100644 CryptoPP/rng.cpp delete mode 100644 CryptoPP/rng.h delete mode 100644 CryptoPP/rsa.cpp delete mode 100644 CryptoPP/rsa.h delete mode 100644 CryptoPP/rw.cpp delete mode 100644 CryptoPP/rw.h delete mode 100644 CryptoPP/safer.cpp delete mode 100644 CryptoPP/safer.h delete mode 100644 CryptoPP/seal.cpp delete mode 100644 CryptoPP/seal.h delete mode 100644 CryptoPP/secblock.h delete mode 100644 CryptoPP/seckey.h delete mode 100644 CryptoPP/seed.cpp delete mode 100644 CryptoPP/seed.h delete mode 100644 CryptoPP/sha.cpp delete mode 100644 CryptoPP/sha.h delete mode 100644 CryptoPP/shacal2.cpp delete mode 100644 CryptoPP/shacal2.h delete mode 100644 CryptoPP/simple.cpp delete mode 100644 CryptoPP/simple.h delete mode 100644 CryptoPP/smartptr.h delete mode 100644 CryptoPP/socketft.cpp delete mode 100644 CryptoPP/socketft.h delete mode 100644 CryptoPP/square.cpp delete mode 100644 CryptoPP/square.h delete mode 100644 CryptoPP/squaretb.cpp delete mode 100644 CryptoPP/stdcpp.h delete mode 100644 CryptoPP/strciphr.cpp delete mode 100644 CryptoPP/strciphr.h delete mode 100644 CryptoPP/tea.cpp delete mode 100644 CryptoPP/tea.h delete mode 100644 CryptoPP/tiger.cpp delete mode 100644 CryptoPP/tiger.h delete mode 100644 CryptoPP/tigertab.cpp delete mode 100644 CryptoPP/trdlocal.cpp delete mode 100644 CryptoPP/trdlocal.h delete mode 100644 CryptoPP/trunhash.h delete mode 100644 CryptoPP/ttmac.cpp delete mode 100644 CryptoPP/ttmac.h delete mode 100644 CryptoPP/validate.h delete mode 100644 CryptoPP/vmac.cpp delete mode 100644 CryptoPP/vmac.h delete mode 100644 CryptoPP/wait.cpp delete mode 100644 CryptoPP/wait.h delete mode 100644 CryptoPP/winpipes.cpp delete mode 100644 CryptoPP/winpipes.h delete mode 100644 CryptoPP/words.h create mode 100644 lib/cryptopp/Doxyfile create mode 100644 lib/cryptopp/License.txt create mode 100644 lib/cryptopp/Readme.txt create mode 100644 lib/cryptopp/adler32.cpp create mode 100644 lib/cryptopp/adler32.h create mode 100644 lib/cryptopp/aes.h create mode 100644 lib/cryptopp/algebra.cpp create mode 100644 lib/cryptopp/algebra.h create mode 100644 lib/cryptopp/algparam.cpp create mode 100644 lib/cryptopp/algparam.h create mode 100644 lib/cryptopp/argnames.h create mode 100644 lib/cryptopp/asn.cpp create mode 100644 lib/cryptopp/asn.h create mode 100644 lib/cryptopp/authenc.cpp create mode 100644 lib/cryptopp/authenc.h create mode 100644 lib/cryptopp/base32.cpp create mode 100644 lib/cryptopp/base32.h create mode 100644 lib/cryptopp/base64.cpp create mode 100644 lib/cryptopp/base64.h create mode 100644 lib/cryptopp/basecode.cpp create mode 100644 lib/cryptopp/basecode.h create mode 100644 lib/cryptopp/cbcmac.cpp create mode 100644 lib/cryptopp/cbcmac.h create mode 100644 lib/cryptopp/ccm.cpp create mode 100644 lib/cryptopp/ccm.h create mode 100644 lib/cryptopp/channels.cpp create mode 100644 lib/cryptopp/channels.h create mode 100644 lib/cryptopp/cmac.cpp create mode 100644 lib/cryptopp/cmac.h create mode 100644 lib/cryptopp/config.h create mode 100644 lib/cryptopp/cpu.cpp create mode 100644 lib/cryptopp/cpu.h create mode 100644 lib/cryptopp/crc.cpp create mode 100644 lib/cryptopp/crc.h create mode 100644 lib/cryptopp/cryptlib.cpp create mode 100644 lib/cryptopp/cryptlib.h create mode 100644 lib/cryptopp/default.cpp create mode 100644 lib/cryptopp/default.h create mode 100644 lib/cryptopp/des.cpp create mode 100644 lib/cryptopp/des.h create mode 100644 lib/cryptopp/dessp.cpp create mode 100644 lib/cryptopp/dh.cpp create mode 100644 lib/cryptopp/dh.h create mode 100644 lib/cryptopp/dh2.cpp create mode 100644 lib/cryptopp/dh2.h create mode 100644 lib/cryptopp/dll.cpp create mode 100644 lib/cryptopp/dll.h create mode 100644 lib/cryptopp/dmac.h create mode 100644 lib/cryptopp/dsa.cpp create mode 100644 lib/cryptopp/dsa.h create mode 100644 lib/cryptopp/eax.cpp create mode 100644 lib/cryptopp/eax.h create mode 100644 lib/cryptopp/ec2n.cpp create mode 100644 lib/cryptopp/ec2n.h create mode 100644 lib/cryptopp/eccrypto.cpp create mode 100644 lib/cryptopp/eccrypto.h create mode 100644 lib/cryptopp/ecp.cpp create mode 100644 lib/cryptopp/ecp.h create mode 100644 lib/cryptopp/elgamal.cpp create mode 100644 lib/cryptopp/elgamal.h create mode 100644 lib/cryptopp/emsa2.cpp create mode 100644 lib/cryptopp/emsa2.h create mode 100644 lib/cryptopp/eprecomp.cpp create mode 100644 lib/cryptopp/eprecomp.h create mode 100644 lib/cryptopp/esign.cpp create mode 100644 lib/cryptopp/esign.h create mode 100644 lib/cryptopp/factory.h create mode 100644 lib/cryptopp/files.cpp create mode 100644 lib/cryptopp/files.h create mode 100644 lib/cryptopp/filters.cpp create mode 100644 lib/cryptopp/filters.h create mode 100644 lib/cryptopp/fips140.cpp create mode 100644 lib/cryptopp/fips140.h create mode 100644 lib/cryptopp/fltrimpl.h create mode 100644 lib/cryptopp/gcm.cpp create mode 100644 lib/cryptopp/gcm.h create mode 100644 lib/cryptopp/gf256.cpp create mode 100644 lib/cryptopp/gf256.h create mode 100644 lib/cryptopp/gf2_32.cpp create mode 100644 lib/cryptopp/gf2_32.h create mode 100644 lib/cryptopp/gf2n.cpp create mode 100644 lib/cryptopp/gf2n.h create mode 100644 lib/cryptopp/gfpcrypt.cpp create mode 100644 lib/cryptopp/gfpcrypt.h create mode 100644 lib/cryptopp/gzip.h create mode 100644 lib/cryptopp/hex.cpp create mode 100644 lib/cryptopp/hex.h create mode 100644 lib/cryptopp/hmac.cpp create mode 100644 lib/cryptopp/hmac.h create mode 100644 lib/cryptopp/hrtimer.cpp create mode 100644 lib/cryptopp/hrtimer.h create mode 100644 lib/cryptopp/integer.cpp create mode 100644 lib/cryptopp/integer.h create mode 100644 lib/cryptopp/iterhash.cpp create mode 100644 lib/cryptopp/iterhash.h create mode 100644 lib/cryptopp/lubyrack.h create mode 100644 lib/cryptopp/luc.cpp create mode 100644 lib/cryptopp/luc.h create mode 100644 lib/cryptopp/md2.cpp create mode 100644 lib/cryptopp/md2.h create mode 100644 lib/cryptopp/md4.cpp create mode 100644 lib/cryptopp/md4.h create mode 100644 lib/cryptopp/md5.cpp create mode 100644 lib/cryptopp/md5.h create mode 100644 lib/cryptopp/mdc.h create mode 100644 lib/cryptopp/misc.cpp create mode 100644 lib/cryptopp/misc.h create mode 100644 lib/cryptopp/modarith.h create mode 100644 lib/cryptopp/modes.cpp create mode 100644 lib/cryptopp/modes.h create mode 100644 lib/cryptopp/modexppc.h create mode 100644 lib/cryptopp/mqueue.cpp create mode 100644 lib/cryptopp/mqueue.h create mode 100644 lib/cryptopp/mqv.cpp create mode 100644 lib/cryptopp/mqv.h create mode 100644 lib/cryptopp/nbtheory.cpp create mode 100644 lib/cryptopp/nbtheory.h create mode 100644 lib/cryptopp/network.cpp create mode 100644 lib/cryptopp/network.h create mode 100644 lib/cryptopp/nr.h create mode 100644 lib/cryptopp/oaep.cpp create mode 100644 lib/cryptopp/oaep.h create mode 100644 lib/cryptopp/oids.h create mode 100644 lib/cryptopp/osrng.cpp create mode 100644 lib/cryptopp/osrng.h create mode 100644 lib/cryptopp/pch.cpp create mode 100644 lib/cryptopp/pch.h create mode 100644 lib/cryptopp/pkcspad.cpp create mode 100644 lib/cryptopp/pkcspad.h create mode 100644 lib/cryptopp/polynomi.cpp create mode 100644 lib/cryptopp/polynomi.h create mode 100644 lib/cryptopp/pssr.cpp create mode 100644 lib/cryptopp/pssr.h create mode 100644 lib/cryptopp/pubkey.cpp create mode 100644 lib/cryptopp/pubkey.h create mode 100644 lib/cryptopp/pwdbased.h create mode 100644 lib/cryptopp/queue.cpp create mode 100644 lib/cryptopp/queue.h create mode 100644 lib/cryptopp/rabin.cpp create mode 100644 lib/cryptopp/rabin.h create mode 100644 lib/cryptopp/randpool.cpp create mode 100644 lib/cryptopp/randpool.h create mode 100644 lib/cryptopp/rdtables.cpp create mode 100644 lib/cryptopp/resource.h create mode 100644 lib/cryptopp/rijndael.cpp create mode 100644 lib/cryptopp/rijndael.h create mode 100644 lib/cryptopp/rng.cpp create mode 100644 lib/cryptopp/rng.h create mode 100644 lib/cryptopp/rsa.cpp create mode 100644 lib/cryptopp/rsa.h create mode 100644 lib/cryptopp/rw.cpp create mode 100644 lib/cryptopp/rw.h create mode 100644 lib/cryptopp/safer.cpp create mode 100644 lib/cryptopp/safer.h create mode 100644 lib/cryptopp/seal.cpp create mode 100644 lib/cryptopp/seal.h create mode 100644 lib/cryptopp/secblock.h create mode 100644 lib/cryptopp/seckey.h create mode 100644 lib/cryptopp/seed.cpp create mode 100644 lib/cryptopp/seed.h create mode 100644 lib/cryptopp/sha.cpp create mode 100644 lib/cryptopp/sha.h create mode 100644 lib/cryptopp/shacal2.cpp create mode 100644 lib/cryptopp/shacal2.h create mode 100644 lib/cryptopp/simple.cpp create mode 100644 lib/cryptopp/simple.h create mode 100644 lib/cryptopp/smartptr.h create mode 100644 lib/cryptopp/socketft.cpp create mode 100644 lib/cryptopp/socketft.h create mode 100644 lib/cryptopp/square.cpp create mode 100644 lib/cryptopp/square.h create mode 100644 lib/cryptopp/squaretb.cpp create mode 100644 lib/cryptopp/stdcpp.h create mode 100644 lib/cryptopp/strciphr.cpp create mode 100644 lib/cryptopp/strciphr.h create mode 100644 lib/cryptopp/tea.cpp create mode 100644 lib/cryptopp/tea.h create mode 100644 lib/cryptopp/tiger.cpp create mode 100644 lib/cryptopp/tiger.h create mode 100644 lib/cryptopp/tigertab.cpp create mode 100644 lib/cryptopp/trdlocal.cpp create mode 100644 lib/cryptopp/trdlocal.h create mode 100644 lib/cryptopp/trunhash.h create mode 100644 lib/cryptopp/ttmac.cpp create mode 100644 lib/cryptopp/ttmac.h create mode 100644 lib/cryptopp/validate.h create mode 100644 lib/cryptopp/vmac.cpp create mode 100644 lib/cryptopp/vmac.h create mode 100644 lib/cryptopp/wait.cpp create mode 100644 lib/cryptopp/wait.h create mode 100644 lib/cryptopp/winpipes.cpp create mode 100644 lib/cryptopp/winpipes.h create mode 100644 lib/cryptopp/words.h diff --git a/CryptoPP/Doxyfile b/CryptoPP/Doxyfile deleted file mode 100644 index c221fdf56..000000000 --- a/CryptoPP/Doxyfile +++ /dev/null @@ -1,1634 +0,0 @@ -# Doxyfile 1.7.1 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = Crypto++ - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = doc - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = YES - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = NO - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions -# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = NO - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = NO - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = NO - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = . - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.h \ - *.cpp - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = adhoc.cpp - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = . - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = YES - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = NO - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 3 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = YES - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -FORMULA_TRANSPARENT = YES - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = NO - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvances is that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = NO - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = NO - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = YES - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = . - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = _WIN32 \ - _WINDOWS \ - __FreeBSD__ \ - CRYPTOPP_DOXYGEN_PROCESSING - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance -# between CPU load and processing speed. - -DOT_NUM_THREADS = 0 - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans.ttf - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/CryptoPP/License.txt b/CryptoPP/License.txt deleted file mode 100644 index c5d3f34b1..000000000 --- a/CryptoPP/License.txt +++ /dev/null @@ -1,51 +0,0 @@ -Compilation Copyright (c) 1995-2013 by Wei Dai. All rights reserved. -This copyright applies only to this software distribution package -as a compilation, and does not imply a copyright on any particular -file in the package. - -All individual files in this compilation are placed in the public domain by -Wei Dai and other contributors. - -I would like to thank the following authors for placing their works into -the public domain: - -Joan Daemen - 3way.cpp -Leonard Janke - cast.cpp, seal.cpp -Steve Reid - cast.cpp -Phil Karn - des.cpp -Andrew M. Kuchling - md2.cpp, md4.cpp -Colin Plumb - md5.cpp -Seal Woods - rc6.cpp -Chris Morgan - rijndael.cpp -Paulo Baretto - rijndael.cpp, skipjack.cpp, square.cpp -Richard De Moliner - safer.cpp -Matthew Skala - twofish.cpp -Kevin Springle - camellia.cpp, shacal2.cpp, ttmac.cpp, whrlpool.cpp, ripemd.cpp -Ronny Van Keer - sha3.cpp - -The Crypto++ Library (as a compilation) is currently licensed under the Boost -Software License 1.0 (http://www.boost.org/users/license.html). - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -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, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/CryptoPP/Readme.txt b/CryptoPP/Readme.txt deleted file mode 100644 index 5f3b4525d..000000000 --- a/CryptoPP/Readme.txt +++ /dev/null @@ -1,452 +0,0 @@ -Crypto++: a C++ Class Library of Cryptographic Schemes -Version 5.6.2 - 2/20/2013 - -Crypto++ Library is a free C++ class library of cryptographic schemes. -Currently the library contains the following algorithms: - - algorithm type name - - authenticated encryption schemes GCM, CCM, EAX - - high speed stream ciphers Panama, Sosemanuk, Salsa20, XSalsa20 - - AES and AES candidates AES (Rijndael), RC6, MARS, Twofish, Serpent, - CAST-256 - - IDEA, Triple-DES (DES-EDE2 and DES-EDE3), - other block ciphers Camellia, SEED, RC5, Blowfish, TEA, XTEA, - Skipjack, SHACAL-2 - - block cipher modes of operation ECB, CBC, CBC ciphertext stealing (CTS), - CFB, OFB, counter mode (CTR) - - message authentication codes VMAC, HMAC, GMAC, CMAC, CBC-MAC, DMAC, - Two-Track-MAC - - SHA-1, SHA-2 (SHA-224, SHA-256, SHA-384, and - hash functions SHA-512), SHA-3, Tiger, WHIRLPOOL, RIPEMD-128, - RIPEMD-256, RIPEMD-160, RIPEMD-320 - - RSA, DSA, ElGamal, Nyberg-Rueppel (NR), - public-key cryptography Rabin-Williams (RW), LUC, LUCELG, - DLIES (variants of DHAES), ESIGN - - padding schemes for public-key PKCS#1 v2.0, OAEP, PSS, PSSR, IEEE P1363 - systems EMSA2 and EMSA5 - - Diffie-Hellman (DH), Unified Diffie-Hellman - key agreement schemes (DH2), Menezes-Qu-Vanstone (MQV), LUCDIF, - XTR-DH - - elliptic curve cryptography ECDSA, ECNR, ECIES, ECDH, ECMQV - - insecure or obsolescent MD2, MD4, MD5, Panama Hash, DES, ARC4, SEAL -algorithms retained for backwards 3.0, WAKE-OFB, DESX (DES-XEX3), RC2, - compatibility and historical SAFER, 3-WAY, GOST, SHARK, CAST-128, Square - value - -Other features include: - - * pseudo random number generators (PRNG): ANSI X9.17 appendix C, RandomPool - * password based key derivation functions: PBKDF1 and PBKDF2 from PKCS #5, - PBKDF from PKCS #12 appendix B - * Shamir's secret sharing scheme and Rabin's information dispersal algorithm - (IDA) - * fast multi-precision integer (bignum) and polynomial operations - * finite field arithmetics, including GF(p) and GF(2^n) - * prime number generation and verification - * useful non-cryptographic algorithms - + DEFLATE (RFC 1951) compression/decompression with gzip (RFC 1952) and - zlib (RFC 1950) format support - + hex, base-32, and base-64 coding/decoding - + 32-bit CRC and Adler32 checksum - * class wrappers for these operating system features (optional): - + high resolution timers on Windows, Unix, and Mac OS - + Berkeley and Windows style sockets - + Windows named pipes - + /dev/random, /dev/urandom, /dev/srandom - + Microsoft's CryptGenRandom on Windows - * A high level interface for most of the above, using a filter/pipeline - metaphor - * benchmarks and validation testing - * x86, x86-64 (x64), MMX, and SSE2 assembly code for the most commonly used - algorithms, with run-time CPU feature detection and code selection - * some versions are available in FIPS 140-2 validated form - -You are welcome to use it for any purpose without paying me, but see -License.txt for the fine print. - -The following compilers are supported for this release. Please visit -http://www.cryptopp.com the most up to date build instructions and porting notes. - - * MSVC 6.0 - 2010 - * GCC 3.3 - 4.5 - * C++Builder 2010 - * Intel C++ Compiler 9 - 11.1 - * Sun Studio 12u1, Express 11/08, Express 06/10 - -*** Important Usage Notes *** - -1. If a constructor for A takes a pointer to an object B (except primitive -types such as int and char), then A owns B and will delete B at A's -destruction. If a constructor for A takes a reference to an object B, -then the caller retains ownership of B and should not destroy it until -A no longer needs it. - -2. Crypto++ is thread safe at the class level. This means you can use -Crypto++ safely in a multithreaded application, but you must provide -synchronization when multiple threads access a common Crypto++ object. - -*** MSVC-Specific Information *** - -On Windows, Crypto++ can be compiled into 3 forms: a static library -including all algorithms, a DLL with only FIPS Approved algorithms, and -a static library with only algorithms not in the DLL. -(FIPS Approved means Approved according to the FIPS 140-2 standard.) -The DLL may be used by itself, or it may be used together with the second -form of the static library. MSVC project files are included to build -all three forms, and sample applications using each of the three forms -are also included. - -To compile Crypto++ with MSVC, open the "cryptest.dsw" (for MSVC 6 and MSVC .NET -2003) or "cryptest.sln" (for MSVC 2005 - 2010) workspace file and build one or -more of the following projects: - -cryptopp - This builds the DLL. Please note that if you wish to use Crypto++ - as a FIPS validated module, you must use a pre-built DLL that has undergone - the FIPS validation process instead of building your own. -dlltest - This builds a sample application that only uses the DLL. -cryptest Non-DLL-Import Configuration - This builds the full static library - along with a full test driver. -cryptest DLL-Import Configuration - This builds a static library containing - only algorithms not in the DLL, along with a full test driver that uses - both the DLL and the static library. - -To use the Crypto++ DLL in your application, #include "dll.h" before including -any other Crypto++ header files, and place the DLL in the same directory as -your .exe file. dll.h includes the line #pragma comment(lib, "cryptopp") -so you don't have to explicitly list the import library in your project -settings. To use a static library form of Crypto++, make the "cryptlib" -project a dependency of your application project, or specify it as -an additional library to link with in your project settings. -In either case you should check the compiler options to -make sure that the library and your application are using the same C++ -run-time libraries and calling conventions. - -*** DLL Memory Management *** - -Because it's possible for the Crypto++ DLL to delete objects allocated -by the calling application, they must use the same C++ memory heap. Three -methods are provided to achieve this. -1. The calling application can tell Crypto++ what heap to use. This method - is required when the calling application uses a non-standard heap. -2. Crypto++ can tell the calling application what heap to use. This method - is required when the calling application uses a statically linked C++ Run - Time Library. (Method 1 does not work in this case because the Crypto++ DLL - is initialized before the calling application's heap is initialized.) -3. Crypto++ can automatically use the heap provided by the calling application's - dynamically linked C++ Run Time Library. The calling application must - make sure that the dynamically linked C++ Run Time Library is initialized - before Crypto++ is loaded. (At this time it is not clear if it is possible - to control the order in which DLLs are initialized on Windows 9x machines, - so it might be best to avoid using this method.) - -When Crypto++ attaches to a new process, it searches all modules loaded -into the process space for exported functions "GetNewAndDeleteForCryptoPP" -and "SetNewAndDeleteFromCryptoPP". If one of these functions is found, -Crypto++ uses methods 1 or 2, respectively, by calling the function. -Otherwise, method 3 is used. - -*** GCC-Specific Information *** - -A makefile is included for you to compile Crypto++ with GCC. Make sure -you are using GNU Make and GNU ld. The make process will produce two files, -libcryptopp.a and cryptest.exe. Run "cryptest.exe v" for the validation -suite. - -*** Documentation and Support *** - -Crypto++ is documented through inline comments in header files, which are -processed through Doxygen to produce an HTML reference manual. You can find -a link to the manual from http://www.cryptopp.com. Also at that site is -the Crypto++ FAQ, which you should browse through before attempting to -use this library, because it will likely answer many of questions that -may come up. - -If you run into any problems, please try the Crypto++ mailing list. -The subscription information and the list archive are available on -http://www.cryptopp.com. You can also email me directly by visiting -http://www.weidai.com, but you will probably get a faster response through -the mailing list. - -*** History *** - -1.0 - First public release. Withdrawn at the request of RSA DSI. - - included Blowfish, BBS, DES, DH, Diamond, DSA, ElGamal, IDEA, - MD5, RC4, RC5, RSA, SHA, WAKE, secret sharing, DEFLATE compression - - had a serious bug in the RSA key generation code. - -1.1 - Removed RSA, RC4, RC5 - - Disabled calls to RSAREF's non-public functions - - Minor bugs fixed - -2.0 - a completely new, faster multiprecision integer class - - added MD5-MAC, HAVAL, 3-WAY, TEA, SAFER, LUC, Rabin, BlumGoldwasser, - elliptic curve algorithms - - added the Lucas strong probable primality test - - ElGamal encryption and signature schemes modified to avoid weaknesses - - Diamond changed to Diamond2 because of key schedule weakness - - fixed bug in WAKE key setup - - SHS class renamed to SHA - - lots of miscellaneous optimizations - -2.1 - added Tiger, HMAC, GOST, RIPE-MD160, LUCELG, LUCDIF, XOR-MAC, - OAEP, PSSR, SHARK - - added precomputation to DH, ElGamal, DSA, and elliptic curve algorithms - - added back RC5 and a new RSA - - optimizations in elliptic curves over GF(p) - - changed Rabin to use OAEP and PSSR - - changed many classes to allow copy constructors to work correctly - - improved exception generation and handling - -2.2 - added SEAL, CAST-128, Square - - fixed bug in HAVAL (padding problem) - - fixed bug in triple-DES (decryption order was reversed) - - fixed bug in RC5 (couldn't handle key length not a multiple of 4) - - changed HMAC to conform to RFC-2104 (which is not compatible - with the original HMAC) - - changed secret sharing and information dispersal to use GF(2^32) - instead of GF(65521) - - removed zero knowledge prover/verifier for graph isomorphism - - removed several utility classes in favor of the C++ standard library - -2.3 - ported to EGCS - - fixed incomplete workaround of min/max conflict in MSVC - -3.0 - placed all names into the "CryptoPP" namespace - - added MD2, RC2, RC6, MARS, RW, DH2, MQV, ECDHC, CBC-CTS - - added abstract base classes PK_SimpleKeyAgreementDomain and - PK_AuthenticatedKeyAgreementDomain - - changed DH and LUCDIF to implement the PK_SimpleKeyAgreementDomain - interface and to perform domain parameter and key validation - - changed interfaces of PK_Signer and PK_Verifier to sign and verify - messages instead of message digests - - changed OAEP to conform to PKCS#1 v2.0 - - changed benchmark code to produce HTML tables as output - - changed PSSR to track IEEE P1363a - - renamed ElGamalSignature to NR and changed it to track IEEE P1363 - - renamed ECKEP to ECMQVC and changed it to track IEEE P1363 - - renamed several other classes for clarity - - removed support for calling RSAREF - - removed option to compile old SHA (SHA-0) - - removed option not to throw exceptions - -3.1 - added ARC4, Rijndael, Twofish, Serpent, CBC-MAC, DMAC - - added interface for querying supported key lengths of symmetric ciphers - and MACs - - added sample code for RSA signature and verification - - changed CBC-CTS to be compatible with RFC 2040 - - updated SEAL to version 3.0 of the cipher specification - - optimized multiprecision squaring and elliptic curves over GF(p) - - fixed bug in MARS key setup - - fixed bug with attaching objects to Deflator - -3.2 - added DES-XEX3, ECDSA, DefaultEncryptorWithMAC - - renamed DES-EDE to DES-EDE2 and TripleDES to DES-EDE3 - - optimized ARC4 - - generalized DSA to allow keys longer than 1024 bits - - fixed bugs in GF2N and ModularArithmetic that can cause calculation errors - - fixed crashing bug in Inflator when given invalid inputs - - fixed endian bug in Serpent - - fixed padding bug in Tiger - -4.0 - added Skipjack, CAST-256, Panama, SHA-2 (SHA-256, SHA-384, and SHA-512), - and XTR-DH - - added a faster variant of Rabin's Information Dispersal Algorithm (IDA) - - added class wrappers for these operating system features: - - high resolution timers on Windows, Unix, and MacOS - - Berkeley and Windows style sockets - - Windows named pipes - - /dev/random and /dev/urandom on Linux and FreeBSD - - Microsoft's CryptGenRandom on Windows - - added support for SEC 1 elliptic curve key format and compressed points - - added support for X.509 public key format (subjectPublicKeyInfo) for - RSA, DSA, and elliptic curve schemes - - added support for DER and OpenPGP signature format for DSA - - added support for ZLIB compressed data format (RFC 1950) - - changed elliptic curve encryption to use ECIES (as defined in SEC 1) - - changed MARS key schedule to reflect the latest specification - - changed BufferedTransformation interface to support multiple channels - and messages - - changed CAST and SHA-1 implementations to use public domain source code - - fixed bug in StringSource - - optmized multi-precision integer code for better performance - -4.1 - added more support for the recommended elliptic curve parameters in SEC 2 - - added Panama MAC, MARC4 - - added IV stealing feature to CTS mode - - added support for PKCS #8 private key format for RSA, DSA, and elliptic - curve schemes - - changed Deflate, MD5, Rijndael, and Twofish to use public domain code - - fixed a bug with flushing compressed streams - - fixed a bug with decompressing stored blocks - - fixed a bug with EC point decompression using non-trinomial basis - - fixed a bug in NetworkSource::GeneralPump() - - fixed a performance issue with EC over GF(p) decryption - - fixed syntax to allow GCC to compile without -fpermissive - - relaxed some restrictions in the license - -4.2 - added support for longer HMAC keys - - added MD4 (which is not secure so use for compatibility purposes only) - - added compatibility fixes/workarounds for STLport 4.5, GCC 3.0.2, - and MSVC 7.0 - - changed MD2 to use public domain code - - fixed a bug with decompressing multiple messages with the same object - - fixed a bug in CBC-MAC with MACing multiple messages with the same object - - fixed a bug in RC5 and RC6 with zero-length keys - - fixed a bug in Adler32 where incorrect checksum may be generated - -5.0 - added ESIGN, DLIES, WAKE-OFB, PBKDF1 and PBKDF2 from PKCS #5 - - added key validation for encryption and signature public/private keys - - renamed StreamCipher interface to SymmetricCipher, which is now implemented - by both stream ciphers and block cipher modes including ECB and CBC - - added keying interfaces to support resetting of keys and IVs without - having to destroy and recreate objects - - changed filter interface to support non-blocking input/output - - changed SocketSource and SocketSink to use overlapped I/O on Microsoft Windows - - grouped related classes inside structs to help templates, for example - AESEncryption and AESDecryption are now AES::Encryption and AES::Decryption - - where possible, typedefs have been added to improve backwards - compatibility when the CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY macro is defined - - changed Serpent, HAVAL and IDEA to use public domain code - - implemented SSE2 optimizations for Integer operations - - fixed a bug in HMAC::TruncatedFinal() - - fixed SKIPJACK byte ordering following NIST clarification dated 5/9/02 - -5.01 - added known answer test for X9.17 RNG in FIPS 140 power-up self test - - submitted to NIST/CSE, but not publicly released - -5.02 - changed EDC test to MAC integrity check using HMAC/SHA1 - - improved performance of integrity check - - added blinding to defend against RSA timing attack - -5.03 - created DLL version of Crypto++ for FIPS 140-2 validation - - fixed vulnerabilities in GetNextIV for CTR and OFB modes - -5.0.4 - Removed DES, SHA-256, SHA-384, SHA-512 from DLL - -5.1 - added PSS padding and changed PSSR to track IEEE P1363a draft standard - - added blinding for RSA and Rabin to defend against timing attacks - on decryption operations - - changed signing and decryption APIs to support the above - - changed WaitObjectContainer to allow waiting for more than 64 - objects at a time on Win32 platforms - - fixed a bug in CBC and ECB modes with processing non-aligned data - - fixed standard conformance bugs in DLIES (DHAES mode) and RW/EMSA2 - signature scheme (these fixes are not backwards compatible) - - fixed a number of compiler warnings, minor bugs, and portability problems - - removed Sapphire - -5.2 - merged in changes for 5.01 - 5.0.4 - - added support for using encoding parameters and key derivation parameters - with public key encryption (implemented by OAEP and DL/ECIES) - - added Camellia, SHACAL-2, Two-Track-MAC, Whirlpool, RIPEMD-320, - RIPEMD-128, RIPEMD-256, Base-32 coding, FIPS variant of CFB mode - - added ThreadUserTimer for timing thread CPU usage - - added option for password-based key derivation functions - to iterate until a mimimum elapsed thread CPU time is reached - - added option (on by default) for DEFLATE compression to detect - uncompressible files and process them more quickly - - improved compatibility and performance on 64-bit platforms, - including Alpha, IA-64, x86-64, PPC64, Sparc64, and MIPS64 - - fixed ONE_AND_ZEROS_PADDING to use 0x80 instead 0x01 as padding. - - fixed encoding/decoding of PKCS #8 privateKeyInfo to properly - handle optional attributes - -5.2.1 - fixed bug in the "dlltest" DLL testing program - - fixed compiling with STLport using VC .NET - - fixed compiling with -fPIC using GCC - - fixed compiling with -msse2 on systems without memalign() - - fixed inability to instantiate PanamaMAC - - fixed problems with inline documentation - -5.2.2 - added SHA-224 - - put SHA-256, SHA-384, SHA-512, RSASSA-PSS into DLL - -5.2.3 - fixed issues with FIPS algorithm test vectors - - put RSASSA-ISO into DLL - -5.3 - ported to MSVC 2005 with support for x86-64 - - added defense against AES timing attacks, and more AES test vectors - - changed StaticAlgorithmName() of Rijndael to "AES", CTR to "CTR" - -5.4 - added Salsa20 - - updated Whirlpool to version 3.0 - - ported to GCC 4.1, Sun C++ 5.8, and Borland C++Builder 2006 - -5.5 - added VMAC and Sosemanuk (with x86-64 and SSE2 assembly) - - improved speed of integer arithmetic, AES, SHA-512, Tiger, Salsa20, - Whirlpool, and PANAMA cipher using assembly (x86-64, MMX, SSE2) - - optimized Camellia and added defense against timing attacks - - updated benchmarks code to show cycles per byte and to time key/IV setup - - started using OpenMP for increased multi-core speed - - enabled GCC optimization flags by default in GNUmakefile - - added blinding and computational error checking for RW signing - - changed RandomPool, X917RNG, GetNextIV, DSA/NR/ECDSA/ECNR to reduce - the risk of reusing random numbers and IVs after virtual machine state - rollback - - changed default FIPS mode RNG from AutoSeededX917RNG to - AutoSeededX917RNG - - fixed PANAMA cipher interface to accept 256-bit key and 256-bit IV - - moved MD2, MD4, MD5, PanamaHash, ARC4, WAKE_CFB into the namespace "Weak" - - removed HAVAL, MD5-MAC, XMAC - -5.5.1 - fixed VMAC validation failure on 32-bit big-endian machines - -5.5.2 - ported x64 assembly language code for AES, Salsa20, Sosemanuk, and Panama - to MSVC 2005 (using MASM since MSVC doesn't support inline assembly on x64) - - fixed Salsa20 initialization crash on non-SSE2 machines - - fixed Whirlpool crash on Pentium 2 machines - - fixed possible branch prediction analysis (BPA) vulnerability in - MontgomeryReduce(), which may affect security of RSA, RW, LUC - - fixed link error with MSVC 2003 when using "debug DLL" form of runtime library - - fixed crash in SSE2_Add on P4 machines when compiled with - MSVC 6.0 SP5 with Processor Pack - - ported to MSVC 2008, GCC 4.2, Sun CC 5.9, Intel C++ Compiler 10.0, - and Borland C++Builder 2007 - -5.6.0 - added AuthenticatedSymmetricCipher interface class and Filter wrappers - - added CCM, GCM (with SSE2 assembly), EAX, CMAC, XSalsa20, and SEED - - added support for variable length IVs - - added OIDs for Brainpool elliptic curve parameters - - improved AES and SHA-256 speed on x86 and x64 - - changed BlockTransformation interface to no longer assume data alignment - - fixed incorrect VMAC computation on message lengths - that are >64 mod 128 (x86 assembly version is not affected) - - fixed compiler error in vmac.cpp on x86 with GCC -fPIC - - fixed run-time validation error on x86-64 with GCC 4.3.2 -O2 - - fixed HashFilter bug when putMessage=true - - fixed AES-CTR data alignment bug that causes incorrect encryption on ARM - - removed WORD64_AVAILABLE; compiler support for 64-bit int is now required - - ported to GCC 4.3, C++Builder 2009, Sun CC 5.10, Intel C++ Compiler 11 - -5.6.1 - added support for AES-NI and CLMUL instruction sets in AES and GMAC/GCM - - removed WAKE-CFB - - fixed several bugs in the SHA-256 x86/x64 assembly code: - * incorrect hash on non-SSE2 x86 machines on non-aligned input - * incorrect hash on x86 machines when input crosses 0x80000000 - * incorrect hash on x64 when compiled with GCC with optimizations enabled - - fixed bugs in AES x86 and x64 assembly causing crashes in some MSVC build configurations - - switched to a public domain implementation of MARS - - ported to MSVC 2010, GCC 4.5.1, Sun Studio 12u1, C++Builder 2010, Intel C++ Compiler 11.1 - - renamed the MSVC DLL project to "cryptopp" for compatibility with MSVC 2010 - -5.6.2 - changed license to Boost Software License 1.0 - - added SHA-3 (Keccak) - - updated DSA to FIPS 186-3 (see DSA2 class) - - fixed Blowfish minimum keylength to be 4 bytes (32 bits) - - fixed Salsa validation failure when compiling with GCC 4.6 - - fixed infinite recursion when on x64, assembly disabled, and no AESNI - - ported to MSVC 2012, GCC 4.7, Clang 3.2, Solaris Studio 12.3, Intel C++ Compiler 13.0 - -Written by Wei Dai diff --git a/CryptoPP/adler32.cpp b/CryptoPP/adler32.cpp deleted file mode 100644 index 0d52c0838..000000000 --- a/CryptoPP/adler32.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// adler32.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "adler32.h" - -NAMESPACE_BEGIN(CryptoPP) - -void Adler32::Update(const byte *input, size_t length) -{ - const unsigned long BASE = 65521; - - unsigned long s1 = m_s1; - unsigned long s2 = m_s2; - - if (length % 8 != 0) - { - do - { - s1 += *input++; - s2 += s1; - length--; - } while (length % 8 != 0); - - if (s1 >= BASE) - s1 -= BASE; - s2 %= BASE; - } - - while (length > 0) - { - s1 += input[0]; s2 += s1; - s1 += input[1]; s2 += s1; - s1 += input[2]; s2 += s1; - s1 += input[3]; s2 += s1; - s1 += input[4]; s2 += s1; - s1 += input[5]; s2 += s1; - s1 += input[6]; s2 += s1; - s1 += input[7]; s2 += s1; - - length -= 8; - input += 8; - - if (s1 >= BASE) - s1 -= BASE; - if (length % 0x8000 == 0) - s2 %= BASE; - } - - assert(s1 < BASE); - assert(s2 < BASE); - - m_s1 = (word16)s1; - m_s2 = (word16)s2; -} - -void Adler32::TruncatedFinal(byte *hash, size_t size) -{ - ThrowIfInvalidTruncatedSize(size); - - switch (size) - { - default: - hash[3] = byte(m_s1); - case 3: - hash[2] = byte(m_s1 >> 8); - case 2: - hash[1] = byte(m_s2); - case 1: - hash[0] = byte(m_s2 >> 8); - case 0: - ; - } - - Reset(); -} - -NAMESPACE_END diff --git a/CryptoPP/adler32.h b/CryptoPP/adler32.h deleted file mode 100644 index 0ed803da9..000000000 --- a/CryptoPP/adler32.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef CRYPTOPP_ADLER32_H -#define CRYPTOPP_ADLER32_H - -#include "cryptlib.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! ADLER-32 checksum calculations -class Adler32 : public HashTransformation -{ -public: - CRYPTOPP_CONSTANT(DIGESTSIZE = 4) - Adler32() {Reset();} - void Update(const byte *input, size_t length); - void TruncatedFinal(byte *hash, size_t size); - unsigned int DigestSize() const {return DIGESTSIZE;} - static const char * StaticAlgorithmName() {return "Adler32";} - std::string AlgorithmName() const {return StaticAlgorithmName();} - -private: - void Reset() {m_s1 = 1; m_s2 = 0;} - - word16 m_s1, m_s2; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/aes.h b/CryptoPP/aes.h deleted file mode 100644 index 008754256..000000000 --- a/CryptoPP/aes.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef CRYPTOPP_AES_H -#define CRYPTOPP_AES_H - -#include "rijndael.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! AES winner, announced on 10/2/2000 -DOCUMENTED_TYPEDEF(Rijndael, AES); - -typedef RijndaelEncryption AESEncryption; -typedef RijndaelDecryption AESDecryption; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/algebra.cpp b/CryptoPP/algebra.cpp deleted file mode 100644 index 958e63701..000000000 --- a/CryptoPP/algebra.cpp +++ /dev/null @@ -1,340 +0,0 @@ -// algebra.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_ALGEBRA_CPP // SunCC workaround: compiler could cause this file to be included twice -#define CRYPTOPP_ALGEBRA_CPP - -#include "algebra.h" -#include "integer.h" - -#include - -NAMESPACE_BEGIN(CryptoPP) - -template const T& AbstractGroup::Double(const Element &a) const -{ - return this->Add(a, a); -} - -template const T& AbstractGroup::Subtract(const Element &a, const Element &b) const -{ - // make copy of a in case Inverse() overwrites it - Element a1(a); - return this->Add(a1, Inverse(b)); -} - -template T& AbstractGroup::Accumulate(Element &a, const Element &b) const -{ - return a = this->Add(a, b); -} - -template T& AbstractGroup::Reduce(Element &a, const Element &b) const -{ - return a = this->Subtract(a, b); -} - -template const T& AbstractRing::Square(const Element &a) const -{ - return this->Multiply(a, a); -} - -template const T& AbstractRing::Divide(const Element &a, const Element &b) const -{ - // make copy of a in case MultiplicativeInverse() overwrites it - Element a1(a); - return this->Multiply(a1, this->MultiplicativeInverse(b)); -} - -template const T& AbstractEuclideanDomain::Mod(const Element &a, const Element &b) const -{ - Element q; - this->DivisionAlgorithm(result, q, a, b); - return result; -} - -template const T& AbstractEuclideanDomain::Gcd(const Element &a, const Element &b) const -{ - Element g[3]={b, a}; - unsigned int i0=0, i1=1, i2=2; - - while (!this->Equal(g[i1], this->Identity())) - { - g[i2] = this->Mod(g[i0], g[i1]); - unsigned int t = i0; i0 = i1; i1 = i2; i2 = t; - } - - return result = g[i0]; -} - -template const typename QuotientRing::Element& QuotientRing::MultiplicativeInverse(const Element &a) const -{ - Element g[3]={m_modulus, a}; - Element v[3]={m_domain.Identity(), m_domain.MultiplicativeIdentity()}; - Element y; - unsigned int i0=0, i1=1, i2=2; - - while (!this->Equal(g[i1], this->Identity())) - { - // y = g[i0] / g[i1]; - // g[i2] = g[i0] % g[i1]; - m_domain.DivisionAlgorithm(g[i2], y, g[i0], g[i1]); - // v[i2] = v[i0] - (v[i1] * y); - v[i2] = m_domain.Subtract(v[i0], m_domain.Multiply(v[i1], y)); - unsigned int t = i0; i0 = i1; i1 = i2; i2 = t; - } - - return m_domain.IsUnit(g[i0]) ? m_domain.Divide(v[i0], g[i0]) : m_domain.Identity(); -} - -template T AbstractGroup::ScalarMultiply(const Element &base, const Integer &exponent) const -{ - Element result; - this->SimultaneousMultiply(&result, base, &exponent, 1); - return result; -} - -template T AbstractGroup::CascadeScalarMultiply(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const -{ - const unsigned expLen = STDMAX(e1.BitCount(), e2.BitCount()); - if (expLen==0) - return this->Identity(); - - const unsigned w = (expLen <= 46 ? 1 : (expLen <= 260 ? 2 : 3)); - const unsigned tableSize = 1< powerTable(tableSize << w); - - powerTable[1] = x; - powerTable[tableSize] = y; - if (w==1) - powerTable[3] = this->Add(x,y); - else - { - powerTable[2] = this->Double(x); - powerTable[2*tableSize] = this->Double(y); - - unsigned i, j; - - for (i=3; i=0; i--) - { - power1 = 2*power1 + e1.GetBit(i); - power2 = 2*power2 + e2.GetBit(i); - - if (i==0 || 2*power1 >= tableSize || 2*power2 >= tableSize) - { - unsigned squaresBefore = prevPosition-i; - unsigned squaresAfter = 0; - prevPosition = i; - while ((power1 || power2) && power1%2 == 0 && power2%2==0) - { - power1 /= 2; - power2 /= 2; - squaresBefore--; - squaresAfter++; - } - if (firstTime) - { - result = powerTable[(power2<Double(result); - if (power1 || power2) - Accumulate(result, powerTable[(power2<Double(result); - power1 = power2 = 0; - } - } - return result; -} - -template Element GeneralCascadeMultiplication(const AbstractGroup &group, Iterator begin, Iterator end) -{ - if (end-begin == 1) - return group.ScalarMultiply(begin->base, begin->exponent); - else if (end-begin == 2) - return group.CascadeScalarMultiply(begin->base, begin->exponent, (begin+1)->base, (begin+1)->exponent); - else - { - Integer q, t; - Iterator last = end; - --last; - - std::make_heap(begin, end); - std::pop_heap(begin, end); - - while (!!begin->exponent) - { - // last->exponent is largest exponent, begin->exponent is next largest - t = last->exponent; - Integer::Divide(last->exponent, q, t, begin->exponent); - - if (q == Integer::One()) - group.Accumulate(begin->base, last->base); // avoid overhead of ScalarMultiply() - else - group.Accumulate(begin->base, group.ScalarMultiply(last->base, q)); - - std::push_heap(begin, end); - std::pop_heap(begin, end); - } - - return group.ScalarMultiply(last->base, last->exponent); - } -} - -struct WindowSlider -{ - WindowSlider(const Integer &expIn, bool fastNegate, unsigned int windowSizeIn=0) - : exp(expIn), windowModulus(Integer::One()), windowSize(windowSizeIn), windowBegin(0), fastNegate(fastNegate), firstTime(true), finished(false) - { - if (windowSize == 0) - { - unsigned int expLen = exp.BitCount(); - windowSize = expLen <= 17 ? 1 : (expLen <= 24 ? 2 : (expLen <= 70 ? 3 : (expLen <= 197 ? 4 : (expLen <= 539 ? 5 : (expLen <= 1434 ? 6 : 7))))); - } - windowModulus <<= windowSize; - } - - void FindNextWindow() - { - unsigned int expLen = exp.WordCount() * WORD_BITS; - unsigned int skipCount = firstTime ? 0 : windowSize; - firstTime = false; - while (!exp.GetBit(skipCount)) - { - if (skipCount >= expLen) - { - finished = true; - return; - } - skipCount++; - } - - exp >>= skipCount; - windowBegin += skipCount; - expWindow = word32(exp % (word(1) << windowSize)); - - if (fastNegate && exp.GetBit(windowSize)) - { - negateNext = true; - expWindow = (word32(1) << windowSize) - expWindow; - exp += windowModulus; - } - else - negateNext = false; - } - - Integer exp, windowModulus; - unsigned int windowSize, windowBegin; - word32 expWindow; - bool fastNegate, negateNext, firstTime, finished; -}; - -template -void AbstractGroup::SimultaneousMultiply(T *results, const T &base, const Integer *expBegin, unsigned int expCount) const -{ - std::vector > buckets(expCount); - std::vector exponents; - exponents.reserve(expCount); - unsigned int i; - - for (i=0; iNotNegative()); - exponents.push_back(WindowSlider(*expBegin++, InversionIsFast(), 0)); - exponents[i].FindNextWindow(); - buckets[i].resize(1<<(exponents[i].windowSize-1), Identity()); - } - - unsigned int expBitPosition = 0; - Element g = base; - bool notDone = true; - - while (notDone) - { - notDone = false; - for (i=0; i 1) - { - for (int j = (int)buckets[i].size()-2; j >= 1; j--) - { - Accumulate(buckets[i][j], buckets[i][j+1]); - Accumulate(r, buckets[i][j]); - } - Accumulate(buckets[i][0], buckets[i][1]); - r = Add(Double(r), buckets[i][0]); - } - } -} - -template T AbstractRing::Exponentiate(const Element &base, const Integer &exponent) const -{ - Element result; - SimultaneousExponentiate(&result, base, &exponent, 1); - return result; -} - -template T AbstractRing::CascadeExponentiate(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const -{ - return MultiplicativeGroup().AbstractGroup::CascadeScalarMultiply(x, e1, y, e2); -} - -template Element GeneralCascadeExponentiation(const AbstractRing &ring, Iterator begin, Iterator end) -{ - return GeneralCascadeMultiplication(ring.MultiplicativeGroup(), begin, end); -} - -template -void AbstractRing::SimultaneousExponentiate(T *results, const T &base, const Integer *exponents, unsigned int expCount) const -{ - MultiplicativeGroup().AbstractGroup::SimultaneousMultiply(results, base, exponents, expCount); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/algebra.h b/CryptoPP/algebra.h deleted file mode 100644 index 13038bd80..000000000 --- a/CryptoPP/algebra.h +++ /dev/null @@ -1,285 +0,0 @@ -#ifndef CRYPTOPP_ALGEBRA_H -#define CRYPTOPP_ALGEBRA_H - -#include "config.h" - -NAMESPACE_BEGIN(CryptoPP) - -class Integer; - -// "const Element&" returned by member functions are references -// to internal data members. Since each object may have only -// one such data member for holding results, the following code -// will produce incorrect results: -// abcd = group.Add(group.Add(a,b), group.Add(c,d)); -// But this should be fine: -// abcd = group.Add(a, group.Add(b, group.Add(c,d)); - -//! Abstract Group -template class CRYPTOPP_NO_VTABLE AbstractGroup -{ -public: - typedef T Element; - - virtual ~AbstractGroup() {} - - virtual bool Equal(const Element &a, const Element &b) const =0; - virtual const Element& Identity() const =0; - virtual const Element& Add(const Element &a, const Element &b) const =0; - virtual const Element& Inverse(const Element &a) const =0; - virtual bool InversionIsFast() const {return false;} - - virtual const Element& Double(const Element &a) const; - virtual const Element& Subtract(const Element &a, const Element &b) const; - virtual Element& Accumulate(Element &a, const Element &b) const; - virtual Element& Reduce(Element &a, const Element &b) const; - - virtual Element ScalarMultiply(const Element &a, const Integer &e) const; - virtual Element CascadeScalarMultiply(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const; - - virtual void SimultaneousMultiply(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const; -}; - -//! Abstract Ring -template class CRYPTOPP_NO_VTABLE AbstractRing : public AbstractGroup -{ -public: - typedef T Element; - - AbstractRing() {m_mg.m_pRing = this;} - AbstractRing(const AbstractRing &source) {m_mg.m_pRing = this;} - AbstractRing& operator=(const AbstractRing &source) {return *this;} - - virtual bool IsUnit(const Element &a) const =0; - virtual const Element& MultiplicativeIdentity() const =0; - virtual const Element& Multiply(const Element &a, const Element &b) const =0; - virtual const Element& MultiplicativeInverse(const Element &a) const =0; - - virtual const Element& Square(const Element &a) const; - virtual const Element& Divide(const Element &a, const Element &b) const; - - virtual Element Exponentiate(const Element &a, const Integer &e) const; - virtual Element CascadeExponentiate(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const; - - virtual void SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const; - - virtual const AbstractGroup& MultiplicativeGroup() const - {return m_mg;} - -private: - class MultiplicativeGroupT : public AbstractGroup - { - public: - const AbstractRing& GetRing() const - {return *m_pRing;} - - bool Equal(const Element &a, const Element &b) const - {return GetRing().Equal(a, b);} - - const Element& Identity() const - {return GetRing().MultiplicativeIdentity();} - - const Element& Add(const Element &a, const Element &b) const - {return GetRing().Multiply(a, b);} - - Element& Accumulate(Element &a, const Element &b) const - {return a = GetRing().Multiply(a, b);} - - const Element& Inverse(const Element &a) const - {return GetRing().MultiplicativeInverse(a);} - - const Element& Subtract(const Element &a, const Element &b) const - {return GetRing().Divide(a, b);} - - Element& Reduce(Element &a, const Element &b) const - {return a = GetRing().Divide(a, b);} - - const Element& Double(const Element &a) const - {return GetRing().Square(a);} - - Element ScalarMultiply(const Element &a, const Integer &e) const - {return GetRing().Exponentiate(a, e);} - - Element CascadeScalarMultiply(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const - {return GetRing().CascadeExponentiate(x, e1, y, e2);} - - void SimultaneousMultiply(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const - {GetRing().SimultaneousExponentiate(results, base, exponents, exponentsCount);} - - const AbstractRing *m_pRing; - }; - - MultiplicativeGroupT m_mg; -}; - -// ******************************************************** - -//! Base and Exponent -template -struct BaseAndExponent -{ -public: - BaseAndExponent() {} - BaseAndExponent(const T &base, const E &exponent) : base(base), exponent(exponent) {} - bool operator<(const BaseAndExponent &rhs) const {return exponent < rhs.exponent;} - T base; - E exponent; -}; - -// VC60 workaround: incomplete member template support -template - Element GeneralCascadeMultiplication(const AbstractGroup &group, Iterator begin, Iterator end); -template - Element GeneralCascadeExponentiation(const AbstractRing &ring, Iterator begin, Iterator end); - -// ******************************************************** - -//! Abstract Euclidean Domain -template class CRYPTOPP_NO_VTABLE AbstractEuclideanDomain : public AbstractRing -{ -public: - typedef T Element; - - virtual void DivisionAlgorithm(Element &r, Element &q, const Element &a, const Element &d) const =0; - - virtual const Element& Mod(const Element &a, const Element &b) const =0; - virtual const Element& Gcd(const Element &a, const Element &b) const; - -protected: - mutable Element result; -}; - -// ******************************************************** - -//! EuclideanDomainOf -template class EuclideanDomainOf : public AbstractEuclideanDomain -{ -public: - typedef T Element; - - EuclideanDomainOf() {} - - bool Equal(const Element &a, const Element &b) const - {return a==b;} - - const Element& Identity() const - {return Element::Zero();} - - const Element& Add(const Element &a, const Element &b) const - {return result = a+b;} - - Element& Accumulate(Element &a, const Element &b) const - {return a+=b;} - - const Element& Inverse(const Element &a) const - {return result = -a;} - - const Element& Subtract(const Element &a, const Element &b) const - {return result = a-b;} - - Element& Reduce(Element &a, const Element &b) const - {return a-=b;} - - const Element& Double(const Element &a) const - {return result = a.Doubled();} - - const Element& MultiplicativeIdentity() const - {return Element::One();} - - const Element& Multiply(const Element &a, const Element &b) const - {return result = a*b;} - - const Element& Square(const Element &a) const - {return result = a.Squared();} - - bool IsUnit(const Element &a) const - {return a.IsUnit();} - - const Element& MultiplicativeInverse(const Element &a) const - {return result = a.MultiplicativeInverse();} - - const Element& Divide(const Element &a, const Element &b) const - {return result = a/b;} - - const Element& Mod(const Element &a, const Element &b) const - {return result = a%b;} - - void DivisionAlgorithm(Element &r, Element &q, const Element &a, const Element &d) const - {Element::Divide(r, q, a, d);} - - bool operator==(const EuclideanDomainOf &rhs) const - {return true;} - -private: - mutable Element result; -}; - -//! Quotient Ring -template class QuotientRing : public AbstractRing -{ -public: - typedef T EuclideanDomain; - typedef typename T::Element Element; - - QuotientRing(const EuclideanDomain &domain, const Element &modulus) - : m_domain(domain), m_modulus(modulus) {} - - const EuclideanDomain & GetDomain() const - {return m_domain;} - - const Element& GetModulus() const - {return m_modulus;} - - bool Equal(const Element &a, const Element &b) const - {return m_domain.Equal(m_domain.Mod(m_domain.Subtract(a, b), m_modulus), m_domain.Identity());} - - const Element& Identity() const - {return m_domain.Identity();} - - const Element& Add(const Element &a, const Element &b) const - {return m_domain.Add(a, b);} - - Element& Accumulate(Element &a, const Element &b) const - {return m_domain.Accumulate(a, b);} - - const Element& Inverse(const Element &a) const - {return m_domain.Inverse(a);} - - const Element& Subtract(const Element &a, const Element &b) const - {return m_domain.Subtract(a, b);} - - Element& Reduce(Element &a, const Element &b) const - {return m_domain.Reduce(a, b);} - - const Element& Double(const Element &a) const - {return m_domain.Double(a);} - - bool IsUnit(const Element &a) const - {return m_domain.IsUnit(m_domain.Gcd(a, m_modulus));} - - const Element& MultiplicativeIdentity() const - {return m_domain.MultiplicativeIdentity();} - - const Element& Multiply(const Element &a, const Element &b) const - {return m_domain.Mod(m_domain.Multiply(a, b), m_modulus);} - - const Element& Square(const Element &a) const - {return m_domain.Mod(m_domain.Square(a), m_modulus);} - - const Element& MultiplicativeInverse(const Element &a) const; - - bool operator==(const QuotientRing &rhs) const - {return m_domain == rhs.m_domain && m_modulus == rhs.m_modulus;} - -protected: - EuclideanDomain m_domain; - Element m_modulus; -}; - -NAMESPACE_END - -#ifdef CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES -#include "algebra.cpp" -#endif - -#endif diff --git a/CryptoPP/algparam.cpp b/CryptoPP/algparam.cpp deleted file mode 100644 index a70d5dd95..000000000 --- a/CryptoPP/algparam.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// algparam.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "algparam.h" - -NAMESPACE_BEGIN(CryptoPP) - -PAssignIntToInteger g_pAssignIntToInteger = NULL; - -bool CombinedNameValuePairs::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - if (strcmp(name, "ValueNames") == 0) - return m_pairs1.GetVoidValue(name, valueType, pValue) && m_pairs2.GetVoidValue(name, valueType, pValue); - else - return m_pairs1.GetVoidValue(name, valueType, pValue) || m_pairs2.GetVoidValue(name, valueType, pValue); -} - -void AlgorithmParametersBase::operator=(const AlgorithmParametersBase& rhs) -{ - assert(false); -} - -bool AlgorithmParametersBase::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - if (strcmp(name, "ValueNames") == 0) - { - NameValuePairs::ThrowIfTypeMismatch(name, typeid(std::string), valueType); - if (m_next.get()) - m_next->GetVoidValue(name, valueType, pValue); - (*reinterpret_cast(pValue) += m_name) += ";"; - return true; - } - else if (strcmp(name, m_name) == 0) - { - AssignValue(name, valueType, pValue); - m_used = true; - return true; - } - else if (m_next.get()) - return m_next->GetVoidValue(name, valueType, pValue); - else - return false; -} - -AlgorithmParameters::AlgorithmParameters() - : m_defaultThrowIfNotUsed(true) -{ -} - -AlgorithmParameters::AlgorithmParameters(const AlgorithmParameters &x) - : m_defaultThrowIfNotUsed(x.m_defaultThrowIfNotUsed) -{ - m_next.reset(const_cast(x).m_next.release()); -} - -AlgorithmParameters & AlgorithmParameters::operator=(const AlgorithmParameters &x) -{ - m_next.reset(const_cast(x).m_next.release()); - return *this; -} - -bool AlgorithmParameters::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - if (m_next.get()) - return m_next->GetVoidValue(name, valueType, pValue); - else - return false; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/algparam.h b/CryptoPP/algparam.h deleted file mode 100644 index ea5129c22..000000000 --- a/CryptoPP/algparam.h +++ /dev/null @@ -1,398 +0,0 @@ -#ifndef CRYPTOPP_ALGPARAM_H -#define CRYPTOPP_ALGPARAM_H - -#include "cryptlib.h" -#include "smartptr.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! used to pass byte array input as part of a NameValuePairs object -/*! the deepCopy option is used when the NameValuePairs object can't - keep a copy of the data available */ -class ConstByteArrayParameter -{ -public: - ConstByteArrayParameter(const char *data = NULL, bool deepCopy = false) - { - Assign((const byte *)data, data ? strlen(data) : 0, deepCopy); - } - ConstByteArrayParameter(const byte *data, size_t size, bool deepCopy = false) - { - Assign(data, size, deepCopy); - } - template ConstByteArrayParameter(const T &string, bool deepCopy = false) - { - CRYPTOPP_COMPILE_ASSERT(sizeof(CPP_TYPENAME T::value_type) == 1); - Assign((const byte *)string.data(), string.size(), deepCopy); - } - - void Assign(const byte *data, size_t size, bool deepCopy) - { - if (deepCopy) - m_block.Assign(data, size); - else - { - m_data = data; - m_size = size; - } - m_deepCopy = deepCopy; - } - - const byte *begin() const {return m_deepCopy ? m_block.begin() : m_data;} - const byte *end() const {return m_deepCopy ? m_block.end() : m_data + m_size;} - size_t size() const {return m_deepCopy ? m_block.size() : m_size;} - -private: - bool m_deepCopy; - const byte *m_data; - size_t m_size; - SecByteBlock m_block; -}; - -class ByteArrayParameter -{ -public: - ByteArrayParameter(byte *data = NULL, unsigned int size = 0) - : m_data(data), m_size(size) {} - ByteArrayParameter(SecByteBlock &block) - : m_data(block.begin()), m_size(block.size()) {} - - byte *begin() const {return m_data;} - byte *end() const {return m_data + m_size;} - size_t size() const {return m_size;} - -private: - byte *m_data; - size_t m_size; -}; - -class CRYPTOPP_DLL CombinedNameValuePairs : public NameValuePairs -{ -public: - CombinedNameValuePairs(const NameValuePairs &pairs1, const NameValuePairs &pairs2) - : m_pairs1(pairs1), m_pairs2(pairs2) {} - - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - -private: - const NameValuePairs &m_pairs1, &m_pairs2; -}; - -template -class GetValueHelperClass -{ -public: - GetValueHelperClass(const T *pObject, const char *name, const std::type_info &valueType, void *pValue, const NameValuePairs *searchFirst) - : m_pObject(pObject), m_name(name), m_valueType(&valueType), m_pValue(pValue), m_found(false), m_getValueNames(false) - { - if (strcmp(m_name, "ValueNames") == 0) - { - m_found = m_getValueNames = true; - NameValuePairs::ThrowIfTypeMismatch(m_name, typeid(std::string), *m_valueType); - if (searchFirst) - searchFirst->GetVoidValue(m_name, valueType, pValue); - if (typeid(T) != typeid(BASE)) - pObject->BASE::GetVoidValue(m_name, valueType, pValue); - ((*reinterpret_cast(m_pValue) += "ThisPointer:") += typeid(T).name()) += ';'; - } - - if (!m_found && strncmp(m_name, "ThisPointer:", 12) == 0 && strcmp(m_name+12, typeid(T).name()) == 0) - { - NameValuePairs::ThrowIfTypeMismatch(m_name, typeid(T *), *m_valueType); - *reinterpret_cast(pValue) = pObject; - m_found = true; - return; - } - - if (!m_found && searchFirst) - m_found = searchFirst->GetVoidValue(m_name, valueType, pValue); - - if (!m_found && typeid(T) != typeid(BASE)) - m_found = pObject->BASE::GetVoidValue(m_name, valueType, pValue); - } - - operator bool() const {return m_found;} - - template - GetValueHelperClass & operator()(const char *name, const R & (T::*pm)() const) - { - if (m_getValueNames) - (*reinterpret_cast(m_pValue) += name) += ";"; - if (!m_found && strcmp(name, m_name) == 0) - { - NameValuePairs::ThrowIfTypeMismatch(name, typeid(R), *m_valueType); - *reinterpret_cast(m_pValue) = (m_pObject->*pm)(); - m_found = true; - } - return *this; - } - - GetValueHelperClass &Assignable() - { -#ifndef __INTEL_COMPILER // ICL 9.1 workaround: Intel compiler copies the vTable pointer for some reason - if (m_getValueNames) - ((*reinterpret_cast(m_pValue) += "ThisObject:") += typeid(T).name()) += ';'; - if (!m_found && strncmp(m_name, "ThisObject:", 11) == 0 && strcmp(m_name+11, typeid(T).name()) == 0) - { - NameValuePairs::ThrowIfTypeMismatch(m_name, typeid(T), *m_valueType); - *reinterpret_cast(m_pValue) = *m_pObject; - m_found = true; - } -#endif - return *this; - } - -private: - const T *m_pObject; - const char *m_name; - const std::type_info *m_valueType; - void *m_pValue; - bool m_found, m_getValueNames; -}; - -template -GetValueHelperClass GetValueHelper(const T *pObject, const char *name, const std::type_info &valueType, void *pValue, const NameValuePairs *searchFirst=NULL, BASE *dummy=NULL) -{ - return GetValueHelperClass(pObject, name, valueType, pValue, searchFirst); -} - -template -GetValueHelperClass GetValueHelper(const T *pObject, const char *name, const std::type_info &valueType, void *pValue, const NameValuePairs *searchFirst=NULL) -{ - return GetValueHelperClass(pObject, name, valueType, pValue, searchFirst); -} - -// ******************************************************** - -template -R Hack_DefaultValueFromConstReferenceType(const R &) -{ - return R(); -} - -template -bool Hack_GetValueIntoConstReference(const NameValuePairs &source, const char *name, const R &value) -{ - return source.GetValue(name, const_cast(value)); -} - -template -class AssignFromHelperClass -{ -public: - AssignFromHelperClass(T *pObject, const NameValuePairs &source) - : m_pObject(pObject), m_source(source), m_done(false) - { - if (source.GetThisObject(*pObject)) - m_done = true; - else if (typeid(BASE) != typeid(T)) - pObject->BASE::AssignFrom(source); - } - - template - AssignFromHelperClass & operator()(const char *name, void (T::*pm)(R)) // VC60 workaround: "const R &" here causes compiler error - { - if (!m_done) - { - R value = Hack_DefaultValueFromConstReferenceType(reinterpret_cast(*(int *)NULL)); - if (!Hack_GetValueIntoConstReference(m_source, name, value)) - throw InvalidArgument(std::string(typeid(T).name()) + ": Missing required parameter '" + name + "'"); - (m_pObject->*pm)(value); - } - return *this; - } - - template - AssignFromHelperClass & operator()(const char *name1, const char *name2, void (T::*pm)(R, S)) // VC60 workaround: "const R &" here causes compiler error - { - if (!m_done) - { - R value1 = Hack_DefaultValueFromConstReferenceType(reinterpret_cast(*(int *)NULL)); - if (!Hack_GetValueIntoConstReference(m_source, name1, value1)) - throw InvalidArgument(std::string(typeid(T).name()) + ": Missing required parameter '" + name1 + "'"); - S value2 = Hack_DefaultValueFromConstReferenceType(reinterpret_cast(*(int *)NULL)); - if (!Hack_GetValueIntoConstReference(m_source, name2, value2)) - throw InvalidArgument(std::string(typeid(T).name()) + ": Missing required parameter '" + name2 + "'"); - (m_pObject->*pm)(value1, value2); - } - return *this; - } - -private: - T *m_pObject; - const NameValuePairs &m_source; - bool m_done; -}; - -template -AssignFromHelperClass AssignFromHelper(T *pObject, const NameValuePairs &source, BASE *dummy=NULL) -{ - return AssignFromHelperClass(pObject, source); -} - -template -AssignFromHelperClass AssignFromHelper(T *pObject, const NameValuePairs &source) -{ - return AssignFromHelperClass(pObject, source); -} - -// ******************************************************** - -// to allow the linker to discard Integer code if not needed. -typedef bool (CRYPTOPP_API * PAssignIntToInteger)(const std::type_info &valueType, void *pInteger, const void *pInt); -CRYPTOPP_DLL extern PAssignIntToInteger g_pAssignIntToInteger; - -CRYPTOPP_DLL const std::type_info & CRYPTOPP_API IntegerTypeId(); - -class CRYPTOPP_DLL AlgorithmParametersBase -{ -public: - class ParameterNotUsed : public Exception - { - public: - ParameterNotUsed(const char *name) : Exception(OTHER_ERROR, std::string("AlgorithmParametersBase: parameter \"") + name + "\" not used") {} - }; - - // this is actually a move, not a copy - AlgorithmParametersBase(const AlgorithmParametersBase &x) - : m_name(x.m_name), m_throwIfNotUsed(x.m_throwIfNotUsed), m_used(x.m_used) - { - m_next.reset(const_cast(x).m_next.release()); - x.m_used = true; - } - - AlgorithmParametersBase(const char *name, bool throwIfNotUsed) - : m_name(name), m_throwIfNotUsed(throwIfNotUsed), m_used(false) {} - - virtual ~AlgorithmParametersBase() - { -#ifdef CRYPTOPP_UNCAUGHT_EXCEPTION_AVAILABLE - if (!std::uncaught_exception()) -#else - try -#endif - { - if (m_throwIfNotUsed && !m_used) - throw ParameterNotUsed(m_name); - } -#ifndef CRYPTOPP_UNCAUGHT_EXCEPTION_AVAILABLE - catch(...) - { - } -#endif - } - - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - -protected: - friend class AlgorithmParameters; - void operator=(const AlgorithmParametersBase& rhs); // assignment not allowed, declare this for VC60 - - virtual void AssignValue(const char *name, const std::type_info &valueType, void *pValue) const =0; - virtual void MoveInto(void *p) const =0; // not really const - - const char *m_name; - bool m_throwIfNotUsed; - mutable bool m_used; - member_ptr m_next; -}; - -template -class AlgorithmParametersTemplate : public AlgorithmParametersBase -{ -public: - AlgorithmParametersTemplate(const char *name, const T &value, bool throwIfNotUsed) - : AlgorithmParametersBase(name, throwIfNotUsed), m_value(value) - { - } - - void AssignValue(const char *name, const std::type_info &valueType, void *pValue) const - { - // special case for retrieving an Integer parameter when an int was passed in - if (!(g_pAssignIntToInteger != NULL && typeid(T) == typeid(int) && g_pAssignIntToInteger(valueType, pValue, &m_value))) - { - NameValuePairs::ThrowIfTypeMismatch(name, typeid(T), valueType); - *reinterpret_cast(pValue) = m_value; - } - } - - void MoveInto(void *buffer) const - { - AlgorithmParametersTemplate* p = new(buffer) AlgorithmParametersTemplate(*this); - } - -protected: - T m_value; -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS AlgorithmParametersTemplate; -CRYPTOPP_DLL_TEMPLATE_CLASS AlgorithmParametersTemplate; -CRYPTOPP_DLL_TEMPLATE_CLASS AlgorithmParametersTemplate; - -class CRYPTOPP_DLL AlgorithmParameters : public NameValuePairs -{ -public: - AlgorithmParameters(); - -#ifdef __BORLANDC__ - template - AlgorithmParameters(const char *name, const T &value, bool throwIfNotUsed=true) - : m_next(new AlgorithmParametersTemplate(name, value, throwIfNotUsed)) - , m_defaultThrowIfNotUsed(throwIfNotUsed) - { - } -#endif - - AlgorithmParameters(const AlgorithmParameters &x); - - AlgorithmParameters & operator=(const AlgorithmParameters &x); - - template - AlgorithmParameters & operator()(const char *name, const T &value, bool throwIfNotUsed) - { - member_ptr p(new AlgorithmParametersTemplate(name, value, throwIfNotUsed)); - p->m_next.reset(m_next.release()); - m_next.reset(p.release()); - m_defaultThrowIfNotUsed = throwIfNotUsed; - return *this; - } - - template - AlgorithmParameters & operator()(const char *name, const T &value) - { - return operator()(name, value, m_defaultThrowIfNotUsed); - } - - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - -protected: - member_ptr m_next; - bool m_defaultThrowIfNotUsed; -}; - -//! Create an object that implements NameValuePairs for passing parameters -/*! \param throwIfNotUsed if true, the object will throw an exception if the value is not accessed - \note throwIfNotUsed is ignored if using a compiler that does not support std::uncaught_exception(), - such as MSVC 7.0 and earlier. - \note A NameValuePairs object containing an arbitrary number of name value pairs may be constructed by - repeatedly using operator() on the object returned by MakeParameters, for example: - AlgorithmParameters parameters = MakeParameters(name1, value1)(name2, value2)(name3, value3); -*/ -#ifdef __BORLANDC__ -typedef AlgorithmParameters MakeParameters; -#else -template -AlgorithmParameters MakeParameters(const char *name, const T &value, bool throwIfNotUsed = true) -{ - return AlgorithmParameters()(name, value, throwIfNotUsed); -} -#endif - -#define CRYPTOPP_GET_FUNCTION_ENTRY(name) (Name::name(), &ThisClass::Get##name) -#define CRYPTOPP_SET_FUNCTION_ENTRY(name) (Name::name(), &ThisClass::Set##name) -#define CRYPTOPP_SET_FUNCTION_ENTRY2(name1, name2) (Name::name1(), Name::name2(), &ThisClass::Set##name1##And##name2) - -NAMESPACE_END - -#endif diff --git a/CryptoPP/argnames.h b/CryptoPP/argnames.h deleted file mode 100644 index e96172521..000000000 --- a/CryptoPP/argnames.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef CRYPTOPP_ARGNAMES_H -#define CRYPTOPP_ARGNAMES_H - -#include "cryptlib.h" - -NAMESPACE_BEGIN(CryptoPP) - -DOCUMENTED_NAMESPACE_BEGIN(Name) - -#define CRYPTOPP_DEFINE_NAME_STRING(name) inline const char *name() {return #name;} - -CRYPTOPP_DEFINE_NAME_STRING(ValueNames) //!< string, a list of value names with a semicolon (';') after each name -CRYPTOPP_DEFINE_NAME_STRING(Version) //!< int -CRYPTOPP_DEFINE_NAME_STRING(Seed) //!< ConstByteArrayParameter -CRYPTOPP_DEFINE_NAME_STRING(Key) //!< ConstByteArrayParameter -CRYPTOPP_DEFINE_NAME_STRING(IV) //!< ConstByteArrayParameter, also accepts const byte * for backwards compatibility -CRYPTOPP_DEFINE_NAME_STRING(StolenIV) //!< byte * -CRYPTOPP_DEFINE_NAME_STRING(Rounds) //!< int -CRYPTOPP_DEFINE_NAME_STRING(FeedbackSize) //!< int -CRYPTOPP_DEFINE_NAME_STRING(WordSize) //!< int, in bytes -CRYPTOPP_DEFINE_NAME_STRING(BlockSize) //!< int, in bytes -CRYPTOPP_DEFINE_NAME_STRING(EffectiveKeyLength) //!< int, in bits -CRYPTOPP_DEFINE_NAME_STRING(KeySize) //!< int, in bits -CRYPTOPP_DEFINE_NAME_STRING(ModulusSize) //!< int, in bits -CRYPTOPP_DEFINE_NAME_STRING(SubgroupOrderSize) //!< int, in bits -CRYPTOPP_DEFINE_NAME_STRING(PrivateExponentSize)//!< int, in bits -CRYPTOPP_DEFINE_NAME_STRING(Modulus) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(PublicExponent) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(PrivateExponent) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(PublicElement) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(SubgroupOrder) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(Cofactor) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(SubgroupGenerator) //!< Integer, ECP::Point, or EC2N::Point -CRYPTOPP_DEFINE_NAME_STRING(Curve) //!< ECP or EC2N -CRYPTOPP_DEFINE_NAME_STRING(GroupOID) //!< OID -CRYPTOPP_DEFINE_NAME_STRING(PointerToPrimeSelector) //!< const PrimeSelector * -CRYPTOPP_DEFINE_NAME_STRING(Prime1) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(Prime2) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(ModPrime1PrivateExponent) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(ModPrime2PrivateExponent) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(MultiplicativeInverseOfPrime2ModPrime1) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(QuadraticResidueModPrime1) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(QuadraticResidueModPrime2) //!< Integer -CRYPTOPP_DEFINE_NAME_STRING(PutMessage) //!< bool -CRYPTOPP_DEFINE_NAME_STRING(TruncatedDigestSize) //!< int -CRYPTOPP_DEFINE_NAME_STRING(BlockPaddingScheme) //!< StreamTransformationFilter::BlockPaddingScheme -CRYPTOPP_DEFINE_NAME_STRING(HashVerificationFilterFlags) //!< word32 -CRYPTOPP_DEFINE_NAME_STRING(AuthenticatedDecryptionFilterFlags) //!< word32 -CRYPTOPP_DEFINE_NAME_STRING(SignatureVerificationFilterFlags) //!< word32 -CRYPTOPP_DEFINE_NAME_STRING(InputBuffer) //!< ConstByteArrayParameter -CRYPTOPP_DEFINE_NAME_STRING(OutputBuffer) //!< ByteArrayParameter -CRYPTOPP_DEFINE_NAME_STRING(InputFileName) //!< const char * -CRYPTOPP_DEFINE_NAME_STRING(InputFileNameWide) //!< const wchar_t * -CRYPTOPP_DEFINE_NAME_STRING(InputStreamPointer) //!< std::istream * -CRYPTOPP_DEFINE_NAME_STRING(InputBinaryMode) //!< bool -CRYPTOPP_DEFINE_NAME_STRING(OutputFileName) //!< const char * -CRYPTOPP_DEFINE_NAME_STRING(OutputFileNameWide) //!< const wchar_t * -CRYPTOPP_DEFINE_NAME_STRING(OutputStreamPointer) //!< std::ostream * -CRYPTOPP_DEFINE_NAME_STRING(OutputBinaryMode) //!< bool -CRYPTOPP_DEFINE_NAME_STRING(EncodingParameters) //!< ConstByteArrayParameter -CRYPTOPP_DEFINE_NAME_STRING(KeyDerivationParameters) //!< ConstByteArrayParameter -CRYPTOPP_DEFINE_NAME_STRING(Separator) //< ConstByteArrayParameter -CRYPTOPP_DEFINE_NAME_STRING(Terminator) //< ConstByteArrayParameter -CRYPTOPP_DEFINE_NAME_STRING(Uppercase) //< bool -CRYPTOPP_DEFINE_NAME_STRING(GroupSize) //< int -CRYPTOPP_DEFINE_NAME_STRING(Pad) //< bool -CRYPTOPP_DEFINE_NAME_STRING(PaddingByte) //< byte -CRYPTOPP_DEFINE_NAME_STRING(Log2Base) //< int -CRYPTOPP_DEFINE_NAME_STRING(EncodingLookupArray) //< const byte * -CRYPTOPP_DEFINE_NAME_STRING(DecodingLookupArray) //< const byte * -CRYPTOPP_DEFINE_NAME_STRING(InsertLineBreaks) //< bool -CRYPTOPP_DEFINE_NAME_STRING(MaxLineLength) //< int -CRYPTOPP_DEFINE_NAME_STRING(DigestSize) //!< int, in bytes -CRYPTOPP_DEFINE_NAME_STRING(L1KeyLength) //!< int, in bytes -CRYPTOPP_DEFINE_NAME_STRING(TableSize) //!< int, in bytes - -DOCUMENTED_NAMESPACE_END - -NAMESPACE_END - -#endif diff --git a/CryptoPP/asn.cpp b/CryptoPP/asn.cpp deleted file mode 100644 index 8ae1ad65a..000000000 --- a/CryptoPP/asn.cpp +++ /dev/null @@ -1,597 +0,0 @@ -// asn.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "asn.h" - -#include -#include - -NAMESPACE_BEGIN(CryptoPP) -USING_NAMESPACE(std) - -/// DER Length -size_t DERLengthEncode(BufferedTransformation &bt, lword length) -{ - size_t i=0; - if (length <= 0x7f) - { - bt.Put(byte(length)); - i++; - } - else - { - bt.Put(byte(BytePrecision(length) | 0x80)); - i++; - for (int j=BytePrecision(length); j; --j) - { - bt.Put(byte(length >> (j-1)*8)); - i++; - } - } - return i; -} - -bool BERLengthDecode(BufferedTransformation &bt, lword &length, bool &definiteLength) -{ - byte b; - - if (!bt.Get(b)) - return false; - - if (!(b & 0x80)) - { - definiteLength = true; - length = b; - } - else - { - unsigned int lengthBytes = b & 0x7f; - - if (lengthBytes == 0) - { - definiteLength = false; - return true; - } - - definiteLength = true; - length = 0; - while (lengthBytes--) - { - if (length >> (8*(sizeof(length)-1))) - BERDecodeError(); // length about to overflow - - if (!bt.Get(b)) - return false; - - length = (length << 8) | b; - } - } - return true; -} - -bool BERLengthDecode(BufferedTransformation &bt, size_t &length) -{ - lword lw; - bool definiteLength; - if (!BERLengthDecode(bt, lw, definiteLength)) - BERDecodeError(); - if (!SafeConvert(lw, length)) - BERDecodeError(); - return definiteLength; -} - -void DEREncodeNull(BufferedTransformation &out) -{ - out.Put(TAG_NULL); - out.Put(0); -} - -void BERDecodeNull(BufferedTransformation &in) -{ - byte b; - if (!in.Get(b) || b != TAG_NULL) - BERDecodeError(); - size_t length; - if (!BERLengthDecode(in, length) || length != 0) - BERDecodeError(); -} - -/// ASN Strings -size_t DEREncodeOctetString(BufferedTransformation &bt, const byte *str, size_t strLen) -{ - bt.Put(OCTET_STRING); - size_t lengthBytes = DERLengthEncode(bt, strLen); - bt.Put(str, strLen); - return 1+lengthBytes+strLen; -} - -size_t DEREncodeOctetString(BufferedTransformation &bt, const SecByteBlock &str) -{ - return DEREncodeOctetString(bt, str.begin(), str.size()); -} - -size_t BERDecodeOctetString(BufferedTransformation &bt, SecByteBlock &str) -{ - byte b; - if (!bt.Get(b) || b != OCTET_STRING) - BERDecodeError(); - - size_t bc; - if (!BERLengthDecode(bt, bc)) - BERDecodeError(); - - str.resize(bc); - if (bc != bt.Get(str, bc)) - BERDecodeError(); - return bc; -} - -size_t BERDecodeOctetString(BufferedTransformation &bt, BufferedTransformation &str) -{ - byte b; - if (!bt.Get(b) || b != OCTET_STRING) - BERDecodeError(); - - size_t bc; - if (!BERLengthDecode(bt, bc)) - BERDecodeError(); - - bt.TransferTo(str, bc); - return bc; -} - -size_t DEREncodeTextString(BufferedTransformation &bt, const std::string &str, byte asnTag) -{ - bt.Put(asnTag); - size_t lengthBytes = DERLengthEncode(bt, str.size()); - bt.Put((const byte *)str.data(), str.size()); - return 1+lengthBytes+str.size(); -} - -size_t BERDecodeTextString(BufferedTransformation &bt, std::string &str, byte asnTag) -{ - byte b; - if (!bt.Get(b) || b != asnTag) - BERDecodeError(); - - size_t bc; - if (!BERLengthDecode(bt, bc)) - BERDecodeError(); - - SecByteBlock temp(bc); - if (bc != bt.Get(temp, bc)) - BERDecodeError(); - str.assign((char *)temp.begin(), bc); - return bc; -} - -/// ASN BitString -size_t DEREncodeBitString(BufferedTransformation &bt, const byte *str, size_t strLen, unsigned int unusedBits) -{ - bt.Put(BIT_STRING); - size_t lengthBytes = DERLengthEncode(bt, strLen+1); - bt.Put((byte)unusedBits); - bt.Put(str, strLen); - return 2+lengthBytes+strLen; -} - -size_t BERDecodeBitString(BufferedTransformation &bt, SecByteBlock &str, unsigned int &unusedBits) -{ - byte b; - if (!bt.Get(b) || b != BIT_STRING) - BERDecodeError(); - - size_t bc; - if (!BERLengthDecode(bt, bc)) - BERDecodeError(); - - byte unused; - if (!bt.Get(unused)) - BERDecodeError(); - unusedBits = unused; - str.resize(bc-1); - if ((bc-1) != bt.Get(str, bc-1)) - BERDecodeError(); - return bc-1; -} - -void DERReencode(BufferedTransformation &source, BufferedTransformation &dest) -{ - byte tag; - source.Peek(tag); - BERGeneralDecoder decoder(source, tag); - DERGeneralEncoder encoder(dest, tag); - if (decoder.IsDefiniteLength()) - decoder.TransferTo(encoder, decoder.RemainingLength()); - else - { - while (!decoder.EndReached()) - DERReencode(decoder, encoder); - } - decoder.MessageEnd(); - encoder.MessageEnd(); -} - -void OID::EncodeValue(BufferedTransformation &bt, word32 v) -{ - for (unsigned int i=RoundUpToMultipleOf(STDMAX(7U,BitPrecision(v)), 7U)-7; i != 0; i-=7) - bt.Put((byte)(0x80 | ((v >> i) & 0x7f))); - bt.Put((byte)(v & 0x7f)); -} - -size_t OID::DecodeValue(BufferedTransformation &bt, word32 &v) -{ - byte b; - size_t i=0; - v = 0; - while (true) - { - if (!bt.Get(b)) - BERDecodeError(); - i++; - if (v >> (8*sizeof(v)-7)) // v about to overflow - BERDecodeError(); - v <<= 7; - v += b & 0x7f; - if (!(b & 0x80)) - return i; - } -} - -void OID::DEREncode(BufferedTransformation &bt) const -{ - assert(m_values.size() >= 2); - ByteQueue temp; - temp.Put(byte(m_values[0] * 40 + m_values[1])); - for (size_t i=2; i 0) - { - word32 v; - size_t valueLen = DecodeValue(bt, v); - if (valueLen > length) - BERDecodeError(); - m_values.push_back(v); - length -= valueLen; - } -} - -void OID::BERDecodeAndCheck(BufferedTransformation &bt) const -{ - OID oid(bt); - if (*this != oid) - BERDecodeError(); -} - -inline BufferedTransformation & EncodedObjectFilter::CurrentTarget() -{ - if (m_flags & PUT_OBJECTS) - return *AttachedTransformation(); - else - return TheBitBucket(); -} - -void EncodedObjectFilter::Put(const byte *inString, size_t length) -{ - if (m_nCurrentObject == m_nObjects) - { - AttachedTransformation()->Put(inString, length); - return; - } - - LazyPutter lazyPutter(m_queue, inString, length); - - while (m_queue.AnyRetrievable()) - { - switch (m_state) - { - case IDENTIFIER: - if (!m_queue.Get(m_id)) - return; - m_queue.TransferTo(CurrentTarget(), 1); - m_state = LENGTH; // fall through - case LENGTH: - { - byte b; - if (m_level > 0 && m_id == 0 && m_queue.Peek(b) && b == 0) - { - m_queue.TransferTo(CurrentTarget(), 1); - m_level--; - m_state = IDENTIFIER; - break; - } - ByteQueue::Walker walker(m_queue); - bool definiteLength; - if (!BERLengthDecode(walker, m_lengthRemaining, definiteLength)) - return; - m_queue.TransferTo(CurrentTarget(), walker.GetCurrentPosition()); - if (!((m_id & CONSTRUCTED) || definiteLength)) - BERDecodeError(); - if (!definiteLength) - { - if (!(m_id & CONSTRUCTED)) - BERDecodeError(); - m_level++; - m_state = IDENTIFIER; - break; - } - m_state = BODY; // fall through - } - case BODY: - m_lengthRemaining -= m_queue.TransferTo(CurrentTarget(), m_lengthRemaining); - - if (m_lengthRemaining == 0) - m_state = IDENTIFIER; - } - - if (m_state == IDENTIFIER && m_level == 0) - { - // just finished processing a level 0 object - ++m_nCurrentObject; - - if (m_flags & PUT_MESSANGE_END_AFTER_EACH_OBJECT) - AttachedTransformation()->MessageEnd(); - - if (m_nCurrentObject == m_nObjects) - { - if (m_flags & PUT_MESSANGE_END_AFTER_ALL_OBJECTS) - AttachedTransformation()->MessageEnd(); - - if (m_flags & PUT_MESSANGE_SERIES_END_AFTER_ALL_OBJECTS) - AttachedTransformation()->MessageSeriesEnd(); - - m_queue.TransferAllTo(*AttachedTransformation()); - return; - } - } - } -} - -BERGeneralDecoder::BERGeneralDecoder(BufferedTransformation &inQueue, byte asnTag) - : m_inQueue(inQueue), m_finished(false) -{ - Init(asnTag); -} - -BERGeneralDecoder::BERGeneralDecoder(BERGeneralDecoder &inQueue, byte asnTag) - : m_inQueue(inQueue), m_finished(false) -{ - Init(asnTag); -} - -void BERGeneralDecoder::Init(byte asnTag) -{ - byte b; - if (!m_inQueue.Get(b) || b != asnTag) - BERDecodeError(); - - if (!BERLengthDecode(m_inQueue, m_length, m_definiteLength)) - BERDecodeError(); - - if (!m_definiteLength && !(asnTag & CONSTRUCTED)) - BERDecodeError(); // cannot be primitive and have indefinite length -} - -BERGeneralDecoder::~BERGeneralDecoder() -{ - try // avoid throwing in constructor - { - if (!m_finished) - MessageEnd(); - } - catch (...) - { - } -} - -bool BERGeneralDecoder::EndReached() const -{ - if (m_definiteLength) - return m_length == 0; - else - { // check end-of-content octets - word16 i; - return (m_inQueue.PeekWord16(i)==2 && i==0); - } -} - -byte BERGeneralDecoder::PeekByte() const -{ - byte b; - if (!Peek(b)) - BERDecodeError(); - return b; -} - -void BERGeneralDecoder::CheckByte(byte check) -{ - byte b; - if (!Get(b) || b != check) - BERDecodeError(); -} - -void BERGeneralDecoder::MessageEnd() -{ - m_finished = true; - if (m_definiteLength) - { - if (m_length != 0) - BERDecodeError(); - } - else - { // remove end-of-content octets - word16 i; - if (m_inQueue.GetWord16(i) != 2 || i != 0) - BERDecodeError(); - } -} - -size_t BERGeneralDecoder::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - if (m_definiteLength && transferBytes > m_length) - transferBytes = m_length; - size_t blockedBytes = m_inQueue.TransferTo2(target, transferBytes, channel, blocking); - ReduceLength(transferBytes); - return blockedBytes; -} - -size_t BERGeneralDecoder::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const -{ - if (m_definiteLength) - end = STDMIN(m_length, end); - return m_inQueue.CopyRangeTo2(target, begin, end, channel, blocking); -} - -lword BERGeneralDecoder::ReduceLength(lword delta) -{ - if (m_definiteLength) - { - if (m_length < delta) - BERDecodeError(); - m_length -= delta; - } - return delta; -} - -DERGeneralEncoder::DERGeneralEncoder(BufferedTransformation &outQueue, byte asnTag) - : m_outQueue(outQueue), m_finished(false), m_asnTag(asnTag) -{ -} - -DERGeneralEncoder::DERGeneralEncoder(DERGeneralEncoder &outQueue, byte asnTag) - : m_outQueue(outQueue), m_finished(false), m_asnTag(asnTag) -{ -} - -DERGeneralEncoder::~DERGeneralEncoder() -{ - try // avoid throwing in constructor - { - if (!m_finished) - MessageEnd(); - } - catch (...) - { - } -} - -void DERGeneralEncoder::MessageEnd() -{ - m_finished = true; - lword length = CurrentSize(); - m_outQueue.Put(m_asnTag); - DERLengthEncode(m_outQueue, length); - TransferTo(m_outQueue); -} - -// ************************************************************* - -void X509PublicKey::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder subjectPublicKeyInfo(bt); - BERSequenceDecoder algorithm(subjectPublicKeyInfo); - GetAlgorithmID().BERDecodeAndCheck(algorithm); - bool parametersPresent = algorithm.EndReached() ? false : BERDecodeAlgorithmParameters(algorithm); - algorithm.MessageEnd(); - - BERGeneralDecoder subjectPublicKey(subjectPublicKeyInfo, BIT_STRING); - subjectPublicKey.CheckByte(0); // unused bits - BERDecodePublicKey(subjectPublicKey, parametersPresent, (size_t)subjectPublicKey.RemainingLength()); - subjectPublicKey.MessageEnd(); - subjectPublicKeyInfo.MessageEnd(); -} - -void X509PublicKey::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder subjectPublicKeyInfo(bt); - - DERSequenceEncoder algorithm(subjectPublicKeyInfo); - GetAlgorithmID().DEREncode(algorithm); - DEREncodeAlgorithmParameters(algorithm); - algorithm.MessageEnd(); - - DERGeneralEncoder subjectPublicKey(subjectPublicKeyInfo, BIT_STRING); - subjectPublicKey.Put(0); // unused bits - DEREncodePublicKey(subjectPublicKey); - subjectPublicKey.MessageEnd(); - - subjectPublicKeyInfo.MessageEnd(); -} - -void PKCS8PrivateKey::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder privateKeyInfo(bt); - word32 version; - BERDecodeUnsigned(privateKeyInfo, version, INTEGER, 0, 0); // check version - - BERSequenceDecoder algorithm(privateKeyInfo); - GetAlgorithmID().BERDecodeAndCheck(algorithm); - bool parametersPresent = algorithm.EndReached() ? false : BERDecodeAlgorithmParameters(algorithm); - algorithm.MessageEnd(); - - BERGeneralDecoder octetString(privateKeyInfo, OCTET_STRING); - BERDecodePrivateKey(octetString, parametersPresent, (size_t)privateKeyInfo.RemainingLength()); - octetString.MessageEnd(); - - if (!privateKeyInfo.EndReached()) - BERDecodeOptionalAttributes(privateKeyInfo); - privateKeyInfo.MessageEnd(); -} - -void PKCS8PrivateKey::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder privateKeyInfo(bt); - DEREncodeUnsigned(privateKeyInfo, 0); // version - - DERSequenceEncoder algorithm(privateKeyInfo); - GetAlgorithmID().DEREncode(algorithm); - DEREncodeAlgorithmParameters(algorithm); - algorithm.MessageEnd(); - - DERGeneralEncoder octetString(privateKeyInfo, OCTET_STRING); - DEREncodePrivateKey(octetString); - octetString.MessageEnd(); - - DEREncodeOptionalAttributes(privateKeyInfo); - privateKeyInfo.MessageEnd(); -} - -void PKCS8PrivateKey::BERDecodeOptionalAttributes(BufferedTransformation &bt) -{ - DERReencode(bt, m_optionalAttributes); -} - -void PKCS8PrivateKey::DEREncodeOptionalAttributes(BufferedTransformation &bt) const -{ - m_optionalAttributes.CopyTo(bt); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/asn.h b/CryptoPP/asn.h deleted file mode 100644 index c35126bc3..000000000 --- a/CryptoPP/asn.h +++ /dev/null @@ -1,369 +0,0 @@ -#ifndef CRYPTOPP_ASN_H -#define CRYPTOPP_ASN_H - -#include "filters.h" -#include "queue.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -// these tags and flags are not complete -enum ASNTag -{ - BOOLEAN = 0x01, - INTEGER = 0x02, - BIT_STRING = 0x03, - OCTET_STRING = 0x04, - TAG_NULL = 0x05, - OBJECT_IDENTIFIER = 0x06, - OBJECT_DESCRIPTOR = 0x07, - EXTERNAL = 0x08, - REAL = 0x09, - ENUMERATED = 0x0a, - UTF8_STRING = 0x0c, - SEQUENCE = 0x10, - SET = 0x11, - NUMERIC_STRING = 0x12, - PRINTABLE_STRING = 0x13, - T61_STRING = 0x14, - VIDEOTEXT_STRING = 0x15, - IA5_STRING = 0x16, - UTC_TIME = 0x17, - GENERALIZED_TIME = 0x18, - GRAPHIC_STRING = 0x19, - VISIBLE_STRING = 0x1a, - GENERAL_STRING = 0x1b -}; - -enum ASNIdFlag -{ - UNIVERSAL = 0x00, -// DATA = 0x01, -// HEADER = 0x02, - CONSTRUCTED = 0x20, - APPLICATION = 0x40, - CONTEXT_SPECIFIC = 0x80, - PRIVATE = 0xc0 -}; - -inline void BERDecodeError() {throw BERDecodeErr();} - -class CRYPTOPP_DLL UnknownOID : public BERDecodeErr -{ -public: - UnknownOID() : BERDecodeErr("BER decode error: unknown object identifier") {} - UnknownOID(const char *err) : BERDecodeErr(err) {} -}; - -// unsigned int DERLengthEncode(unsigned int length, byte *output=0); -CRYPTOPP_DLL size_t CRYPTOPP_API DERLengthEncode(BufferedTransformation &out, lword length); -// returns false if indefinite length -CRYPTOPP_DLL bool CRYPTOPP_API BERLengthDecode(BufferedTransformation &in, size_t &length); - -CRYPTOPP_DLL void CRYPTOPP_API DEREncodeNull(BufferedTransformation &out); -CRYPTOPP_DLL void CRYPTOPP_API BERDecodeNull(BufferedTransformation &in); - -CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeOctetString(BufferedTransformation &out, const byte *str, size_t strLen); -CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeOctetString(BufferedTransformation &out, const SecByteBlock &str); -CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeOctetString(BufferedTransformation &in, SecByteBlock &str); -CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeOctetString(BufferedTransformation &in, BufferedTransformation &str); - -// for UTF8_STRING, PRINTABLE_STRING, and IA5_STRING -CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeTextString(BufferedTransformation &out, const std::string &str, byte asnTag); -CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeTextString(BufferedTransformation &in, std::string &str, byte asnTag); - -CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeBitString(BufferedTransformation &out, const byte *str, size_t strLen, unsigned int unusedBits=0); -CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeBitString(BufferedTransformation &in, SecByteBlock &str, unsigned int &unusedBits); - -// BER decode from source and DER reencode into dest -CRYPTOPP_DLL void CRYPTOPP_API DERReencode(BufferedTransformation &source, BufferedTransformation &dest); - -//! Object Identifier -class CRYPTOPP_DLL OID -{ -public: - OID() {} - OID(word32 v) : m_values(1, v) {} - OID(BufferedTransformation &bt) {BERDecode(bt);} - - inline OID & operator+=(word32 rhs) {m_values.push_back(rhs); return *this;} - - void DEREncode(BufferedTransformation &bt) const; - void BERDecode(BufferedTransformation &bt); - - // throw BERDecodeErr() if decoded value doesn't equal this OID - void BERDecodeAndCheck(BufferedTransformation &bt) const; - - std::vector m_values; - -private: - static void EncodeValue(BufferedTransformation &bt, word32 v); - static size_t DecodeValue(BufferedTransformation &bt, word32 &v); -}; - -class EncodedObjectFilter : public Filter -{ -public: - enum Flag {PUT_OBJECTS=1, PUT_MESSANGE_END_AFTER_EACH_OBJECT=2, PUT_MESSANGE_END_AFTER_ALL_OBJECTS=4, PUT_MESSANGE_SERIES_END_AFTER_ALL_OBJECTS=8}; - EncodedObjectFilter(BufferedTransformation *attachment = NULL, unsigned int nObjects = 1, word32 flags = 0); - - void Put(const byte *inString, size_t length); - - unsigned int GetNumberOfCompletedObjects() const {return m_nCurrentObject;} - unsigned long GetPositionOfObject(unsigned int i) const {return m_positions[i];} - -private: - BufferedTransformation & CurrentTarget(); - - word32 m_flags; - unsigned int m_nObjects, m_nCurrentObject, m_level; - std::vector m_positions; - ByteQueue m_queue; - enum State {IDENTIFIER, LENGTH, BODY, TAIL, ALL_DONE} m_state; - byte m_id; - lword m_lengthRemaining; -}; - -//! BER General Decoder -class CRYPTOPP_DLL BERGeneralDecoder : public Store -{ -public: - explicit BERGeneralDecoder(BufferedTransformation &inQueue, byte asnTag); - explicit BERGeneralDecoder(BERGeneralDecoder &inQueue, byte asnTag); - ~BERGeneralDecoder(); - - bool IsDefiniteLength() const {return m_definiteLength;} - lword RemainingLength() const {assert(m_definiteLength); return m_length;} - bool EndReached() const; - byte PeekByte() const; - void CheckByte(byte b); - - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const; - - // call this to denote end of sequence - void MessageEnd(); - -protected: - BufferedTransformation &m_inQueue; - bool m_finished, m_definiteLength; - lword m_length; - -private: - void Init(byte asnTag); - void StoreInitialize(const NameValuePairs ¶meters) {assert(false);} - lword ReduceLength(lword delta); -}; - -//! DER General Encoder -class CRYPTOPP_DLL DERGeneralEncoder : public ByteQueue -{ -public: - explicit DERGeneralEncoder(BufferedTransformation &outQueue, byte asnTag = SEQUENCE | CONSTRUCTED); - explicit DERGeneralEncoder(DERGeneralEncoder &outQueue, byte asnTag = SEQUENCE | CONSTRUCTED); - ~DERGeneralEncoder(); - - // call this to denote end of sequence - void MessageEnd(); - -private: - BufferedTransformation &m_outQueue; - bool m_finished; - - byte m_asnTag; -}; - -//! BER Sequence Decoder -class CRYPTOPP_DLL BERSequenceDecoder : public BERGeneralDecoder -{ -public: - explicit BERSequenceDecoder(BufferedTransformation &inQueue, byte asnTag = SEQUENCE | CONSTRUCTED) - : BERGeneralDecoder(inQueue, asnTag) {} - explicit BERSequenceDecoder(BERSequenceDecoder &inQueue, byte asnTag = SEQUENCE | CONSTRUCTED) - : BERGeneralDecoder(inQueue, asnTag) {} -}; - -//! DER Sequence Encoder -class CRYPTOPP_DLL DERSequenceEncoder : public DERGeneralEncoder -{ -public: - explicit DERSequenceEncoder(BufferedTransformation &outQueue, byte asnTag = SEQUENCE | CONSTRUCTED) - : DERGeneralEncoder(outQueue, asnTag) {} - explicit DERSequenceEncoder(DERSequenceEncoder &outQueue, byte asnTag = SEQUENCE | CONSTRUCTED) - : DERGeneralEncoder(outQueue, asnTag) {} -}; - -//! BER Set Decoder -class CRYPTOPP_DLL BERSetDecoder : public BERGeneralDecoder -{ -public: - explicit BERSetDecoder(BufferedTransformation &inQueue, byte asnTag = SET | CONSTRUCTED) - : BERGeneralDecoder(inQueue, asnTag) {} - explicit BERSetDecoder(BERSetDecoder &inQueue, byte asnTag = SET | CONSTRUCTED) - : BERGeneralDecoder(inQueue, asnTag) {} -}; - -//! DER Set Encoder -class CRYPTOPP_DLL DERSetEncoder : public DERGeneralEncoder -{ -public: - explicit DERSetEncoder(BufferedTransformation &outQueue, byte asnTag = SET | CONSTRUCTED) - : DERGeneralEncoder(outQueue, asnTag) {} - explicit DERSetEncoder(DERSetEncoder &outQueue, byte asnTag = SET | CONSTRUCTED) - : DERGeneralEncoder(outQueue, asnTag) {} -}; - -template -class ASNOptional : public member_ptr -{ -public: - void BERDecode(BERSequenceDecoder &seqDecoder, byte tag, byte mask = ~CONSTRUCTED) - { - byte b; - if (seqDecoder.Peek(b) && (b & mask) == tag) - reset(new T(seqDecoder)); - } - void DEREncode(BufferedTransformation &out) - { - if (this->get() != NULL) - this->get()->DEREncode(out); - } -}; - -//! _ -template -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE ASN1CryptoMaterial : public ASN1Object, public BASE -{ -public: - void Save(BufferedTransformation &bt) const - {BEREncode(bt);} - void Load(BufferedTransformation &bt) - {BERDecode(bt);} -}; - -//! encodes/decodes subjectPublicKeyInfo -class CRYPTOPP_DLL X509PublicKey : public ASN1CryptoMaterial -{ -public: - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - virtual OID GetAlgorithmID() const =0; - virtual bool BERDecodeAlgorithmParameters(BufferedTransformation &bt) - {BERDecodeNull(bt); return false;} - virtual bool DEREncodeAlgorithmParameters(BufferedTransformation &bt) const - {DEREncodeNull(bt); return false;} // see RFC 2459, section 7.3.1 - - //! decode subjectPublicKey part of subjectPublicKeyInfo, without the BIT STRING header - virtual void BERDecodePublicKey(BufferedTransformation &bt, bool parametersPresent, size_t size) =0; - //! encode subjectPublicKey part of subjectPublicKeyInfo, without the BIT STRING header - virtual void DEREncodePublicKey(BufferedTransformation &bt) const =0; -}; - -//! encodes/decodes privateKeyInfo -class CRYPTOPP_DLL PKCS8PrivateKey : public ASN1CryptoMaterial -{ -public: - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - virtual OID GetAlgorithmID() const =0; - virtual bool BERDecodeAlgorithmParameters(BufferedTransformation &bt) - {BERDecodeNull(bt); return false;} - virtual bool DEREncodeAlgorithmParameters(BufferedTransformation &bt) const - {DEREncodeNull(bt); return false;} // see RFC 2459, section 7.3.1 - - //! decode privateKey part of privateKeyInfo, without the OCTET STRING header - virtual void BERDecodePrivateKey(BufferedTransformation &bt, bool parametersPresent, size_t size) =0; - //! encode privateKey part of privateKeyInfo, without the OCTET STRING header - virtual void DEREncodePrivateKey(BufferedTransformation &bt) const =0; - - //! decode optional attributes including context-specific tag - /*! /note default implementation stores attributes to be output in DEREncodeOptionalAttributes */ - virtual void BERDecodeOptionalAttributes(BufferedTransformation &bt); - //! encode optional attributes including context-specific tag - virtual void DEREncodeOptionalAttributes(BufferedTransformation &bt) const; - -protected: - ByteQueue m_optionalAttributes; -}; - -// ******************************************************** - -//! DER Encode Unsigned -/*! for INTEGER, BOOLEAN, and ENUM */ -template -size_t DEREncodeUnsigned(BufferedTransformation &out, T w, byte asnTag = INTEGER) -{ - byte buf[sizeof(w)+1]; - unsigned int bc; - if (asnTag == BOOLEAN) - { - buf[sizeof(w)] = w ? 0xff : 0; - bc = 1; - } - else - { - buf[0] = 0; - for (unsigned int i=0; i> (sizeof(w)-1-i)*8); - bc = sizeof(w); - while (bc > 1 && buf[sizeof(w)+1-bc] == 0) - --bc; - if (buf[sizeof(w)+1-bc] & 0x80) - ++bc; - } - out.Put(asnTag); - size_t lengthBytes = DERLengthEncode(out, bc); - out.Put(buf+sizeof(w)+1-bc, bc); - return 1+lengthBytes+bc; -} - -//! BER Decode Unsigned -// VC60 workaround: std::numeric_limits::max conflicts with MFC max macro -// CW41 workaround: std::numeric_limits::max causes a template error -template -void BERDecodeUnsigned(BufferedTransformation &in, T &w, byte asnTag = INTEGER, - T minValue = 0, T maxValue = 0xffffffff) -{ - byte b; - if (!in.Get(b) || b != asnTag) - BERDecodeError(); - - size_t bc; - BERLengthDecode(in, bc); - - SecByteBlock buf(bc); - - if (bc != in.Get(buf, bc)) - BERDecodeError(); - - const byte *ptr = buf; - while (bc > sizeof(w) && *ptr == 0) - { - bc--; - ptr++; - } - if (bc > sizeof(w)) - BERDecodeError(); - - w = 0; - for (unsigned int i=0; i maxValue) - BERDecodeError(); -} - -inline bool operator==(const ::CryptoPP::OID &lhs, const ::CryptoPP::OID &rhs) - {return lhs.m_values == rhs.m_values;} -inline bool operator!=(const ::CryptoPP::OID &lhs, const ::CryptoPP::OID &rhs) - {return lhs.m_values != rhs.m_values;} -inline bool operator<(const ::CryptoPP::OID &lhs, const ::CryptoPP::OID &rhs) - {return std::lexicographical_compare(lhs.m_values.begin(), lhs.m_values.end(), rhs.m_values.begin(), rhs.m_values.end());} -inline ::CryptoPP::OID operator+(const ::CryptoPP::OID &lhs, unsigned long rhs) - {return ::CryptoPP::OID(lhs)+=rhs;} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/authenc.cpp b/CryptoPP/authenc.cpp deleted file mode 100644 index f93662efb..000000000 --- a/CryptoPP/authenc.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// authenc.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "authenc.h" - -NAMESPACE_BEGIN(CryptoPP) - -void AuthenticatedSymmetricCipherBase::AuthenticateData(const byte *input, size_t len) -{ - unsigned int blockSize = AuthenticationBlockSize(); - unsigned int &num = m_bufferedDataLength; - byte* data = m_buffer.begin(); - - if (num != 0) // process left over data - { - if (num+len >= blockSize) - { - memcpy(data+num, input, blockSize-num); - AuthenticateBlocks(data, blockSize); - input += (blockSize-num); - len -= (blockSize-num); - num = 0; - // drop through and do the rest - } - else - { - memcpy(data+num, input, len); - num += (unsigned int)len; - return; - } - } - - // now process the input data in blocks of blockSize bytes and save the leftovers to m_data - if (len >= blockSize) - { - size_t leftOver = AuthenticateBlocks(input, len); - input += (len - leftOver); - len = leftOver; - } - - memcpy(data, input, len); - num = (unsigned int)len; -} - -void AuthenticatedSymmetricCipherBase::SetKey(const byte *userKey, size_t keylength, const NameValuePairs ¶ms) -{ - m_bufferedDataLength = 0; - m_state = State_Start; - - SetKeyWithoutResync(userKey, keylength, params); - m_state = State_KeySet; - - size_t length; - const byte *iv = GetIVAndThrowIfInvalid(params, length); - if (iv) - Resynchronize(iv, (int)length); -} - -void AuthenticatedSymmetricCipherBase::Resynchronize(const byte *iv, int length) -{ - if (m_state < State_KeySet) - throw BadState(AlgorithmName(), "Resynchronize", "key is set"); - - m_bufferedDataLength = 0; - m_totalHeaderLength = m_totalMessageLength = m_totalFooterLength = 0; - m_state = State_KeySet; - - Resync(iv, this->ThrowIfInvalidIVLength(length)); - m_state = State_IVSet; -} - -void AuthenticatedSymmetricCipherBase::Update(const byte *input, size_t length) -{ - if (length == 0) - return; - - switch (m_state) - { - case State_Start: - case State_KeySet: - throw BadState(AlgorithmName(), "Update", "setting key and IV"); - case State_IVSet: - AuthenticateData(input, length); - m_totalHeaderLength += length; - break; - case State_AuthUntransformed: - case State_AuthTransformed: - AuthenticateLastConfidentialBlock(); - m_bufferedDataLength = 0; - m_state = State_AuthFooter; - // fall through - case State_AuthFooter: - AuthenticateData(input, length); - m_totalFooterLength += length; - break; - default: - assert(false); - } -} - -void AuthenticatedSymmetricCipherBase::ProcessData(byte *outString, const byte *inString, size_t length) -{ - m_totalMessageLength += length; - if (m_state >= State_IVSet && m_totalMessageLength > MaxMessageLength()) - throw InvalidArgument(AlgorithmName() + ": message length exceeds maximum"); - -reswitch: - switch (m_state) - { - case State_Start: - case State_KeySet: - throw BadState(AlgorithmName(), "ProcessData", "setting key and IV"); - case State_AuthFooter: - throw BadState(AlgorithmName(), "ProcessData was called after footer input has started"); - case State_IVSet: - AuthenticateLastHeaderBlock(); - m_bufferedDataLength = 0; - m_state = AuthenticationIsOnPlaintext()==IsForwardTransformation() ? State_AuthUntransformed : State_AuthTransformed; - goto reswitch; - case State_AuthUntransformed: - AuthenticateData(inString, length); - AccessSymmetricCipher().ProcessData(outString, inString, length); - break; - case State_AuthTransformed: - AccessSymmetricCipher().ProcessData(outString, inString, length); - AuthenticateData(outString, length); - break; - default: - assert(false); - } -} - -void AuthenticatedSymmetricCipherBase::TruncatedFinal(byte *mac, size_t macSize) -{ - if (m_totalHeaderLength > MaxHeaderLength()) - throw InvalidArgument(AlgorithmName() + ": header length of " + IntToString(m_totalHeaderLength) + " exceeds the maximum of " + IntToString(MaxHeaderLength())); - - if (m_totalFooterLength > MaxFooterLength()) - { - if (MaxFooterLength() == 0) - throw InvalidArgument(AlgorithmName() + ": additional authenticated data (AAD) cannot be input after data to be encrypted or decrypted"); - else - throw InvalidArgument(AlgorithmName() + ": footer length of " + IntToString(m_totalFooterLength) + " exceeds the maximum of " + IntToString(MaxFooterLength())); - } - - switch (m_state) - { - case State_Start: - case State_KeySet: - throw BadState(AlgorithmName(), "TruncatedFinal", "setting key and IV"); - - case State_IVSet: - AuthenticateLastHeaderBlock(); - m_bufferedDataLength = 0; - // fall through - - case State_AuthUntransformed: - case State_AuthTransformed: - AuthenticateLastConfidentialBlock(); - m_bufferedDataLength = 0; - // fall through - - case State_AuthFooter: - AuthenticateLastFooterBlock(mac, macSize); - m_bufferedDataLength = 0; - break; - - default: - assert(false); - } - - m_state = State_KeySet; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/authenc.h b/CryptoPP/authenc.h deleted file mode 100644 index 5bb2a51c8..000000000 --- a/CryptoPP/authenc.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef CRYPTOPP_AUTHENC_H -#define CRYPTOPP_AUTHENC_H - -#include "cryptlib.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! . -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedSymmetricCipherBase : public AuthenticatedSymmetricCipher -{ -public: - AuthenticatedSymmetricCipherBase() : m_state(State_Start) {} - - bool IsRandomAccess() const {return false;} - bool IsSelfInverting() const {return true;} - void UncheckedSetKey(const byte *,unsigned int,const CryptoPP::NameValuePairs &) {assert(false);} - - void SetKey(const byte *userKey, size_t keylength, const NameValuePairs ¶ms); - void Restart() {if (m_state > State_KeySet) m_state = State_KeySet;} - void Resynchronize(const byte *iv, int length=-1); - void Update(const byte *input, size_t length); - void ProcessData(byte *outString, const byte *inString, size_t length); - void TruncatedFinal(byte *mac, size_t macSize); - -protected: - void AuthenticateData(const byte *data, size_t len); - const SymmetricCipher & GetSymmetricCipher() const {return const_cast(this)->AccessSymmetricCipher();}; - - virtual SymmetricCipher & AccessSymmetricCipher() =0; - virtual bool AuthenticationIsOnPlaintext() const =0; - virtual unsigned int AuthenticationBlockSize() const =0; - virtual void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms) =0; - virtual void Resync(const byte *iv, size_t len) =0; - virtual size_t AuthenticateBlocks(const byte *data, size_t len) =0; - virtual void AuthenticateLastHeaderBlock() =0; - virtual void AuthenticateLastConfidentialBlock() {} - virtual void AuthenticateLastFooterBlock(byte *mac, size_t macSize) =0; - - enum State {State_Start, State_KeySet, State_IVSet, State_AuthUntransformed, State_AuthTransformed, State_AuthFooter}; - State m_state; - unsigned int m_bufferedDataLength; - lword m_totalHeaderLength, m_totalMessageLength, m_totalFooterLength; - AlignedSecByteBlock m_buffer; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/base32.cpp b/CryptoPP/base32.cpp deleted file mode 100644 index 0568f0729..000000000 --- a/CryptoPP/base32.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// base32.cpp - written and placed in the public domain by Frank Palazzolo, based on hex.cpp by Wei Dai - -#include "pch.h" -#include "base32.h" - -NAMESPACE_BEGIN(CryptoPP) - -static const byte s_vecUpper[] = "ABCDEFGHIJKMNPQRSTUVWXYZ23456789"; -static const byte s_vecLower[] = "abcdefghijkmnpqrstuvwxyz23456789"; - -void Base32Encoder::IsolatedInitialize(const NameValuePairs ¶meters) -{ - bool uppercase = parameters.GetValueWithDefault(Name::Uppercase(), true); - m_filter->Initialize(CombinedNameValuePairs( - parameters, - MakeParameters(Name::EncodingLookupArray(), uppercase ? &s_vecUpper[0] : &s_vecLower[0], false)(Name::Log2Base(), 5, true))); -} - -void Base32Decoder::IsolatedInitialize(const NameValuePairs ¶meters) -{ - BaseN_Decoder::Initialize(CombinedNameValuePairs( - parameters, - MakeParameters(Name::DecodingLookupArray(), GetDefaultDecodingLookupArray(), false)(Name::Log2Base(), 5, true))); -} - -const int *Base32Decoder::GetDefaultDecodingLookupArray() -{ - static volatile bool s_initialized = false; - static int s_array[256]; - - if (!s_initialized) - { - InitializeDecodingLookupArray(s_array, s_vecUpper, 32, true); - s_initialized = true; - } - return s_array; -} - -NAMESPACE_END diff --git a/CryptoPP/base32.h b/CryptoPP/base32.h deleted file mode 100644 index cb1e1af8d..000000000 --- a/CryptoPP/base32.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CRYPTOPP_BASE32_H -#define CRYPTOPP_BASE32_H - -#include "basecode.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! Converts given data to base 32, the default code is based on draft-ietf-idn-dude-02.txt -/*! To specify alternative code, call Initialize() with EncodingLookupArray parameter. */ -class Base32Encoder : public SimpleProxyFilter -{ -public: - Base32Encoder(BufferedTransformation *attachment = NULL, bool uppercase = true, int outputGroupSize = 0, const std::string &separator = ":", const std::string &terminator = "") - : SimpleProxyFilter(new BaseN_Encoder(new Grouper), attachment) - { - IsolatedInitialize(MakeParameters(Name::Uppercase(), uppercase)(Name::GroupSize(), outputGroupSize)(Name::Separator(), ConstByteArrayParameter(separator))); - } - - void IsolatedInitialize(const NameValuePairs ¶meters); -}; - -//! Decode base 32 data back to bytes, the default code is based on draft-ietf-idn-dude-02.txt -/*! To specify alternative code, call Initialize() with DecodingLookupArray parameter. */ -class Base32Decoder : public BaseN_Decoder -{ -public: - Base32Decoder(BufferedTransformation *attachment = NULL) - : BaseN_Decoder(GetDefaultDecodingLookupArray(), 5, attachment) {} - - void IsolatedInitialize(const NameValuePairs ¶meters); - -private: - static const int * CRYPTOPP_API GetDefaultDecodingLookupArray(); -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/base64.cpp b/CryptoPP/base64.cpp deleted file mode 100644 index 7571f2b8c..000000000 --- a/CryptoPP/base64.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// base64.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "base64.h" - -NAMESPACE_BEGIN(CryptoPP) - -static const byte s_vec[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const byte s_padding = '='; - -void Base64Encoder::IsolatedInitialize(const NameValuePairs ¶meters) -{ - bool insertLineBreaks = parameters.GetValueWithDefault(Name::InsertLineBreaks(), true); - int maxLineLength = parameters.GetIntValueWithDefault(Name::MaxLineLength(), 72); - - const char *lineBreak = insertLineBreaks ? "\n" : ""; - - m_filter->Initialize(CombinedNameValuePairs( - parameters, - MakeParameters(Name::EncodingLookupArray(), &s_vec[0], false) - (Name::PaddingByte(), s_padding) - (Name::GroupSize(), insertLineBreaks ? maxLineLength : 0) - (Name::Separator(), ConstByteArrayParameter(lineBreak)) - (Name::Terminator(), ConstByteArrayParameter(lineBreak)) - (Name::Log2Base(), 6, true))); -} - -const int *Base64Decoder::GetDecodingLookupArray() -{ - static volatile bool s_initialized = false; - static int s_array[256]; - - if (!s_initialized) - { - InitializeDecodingLookupArray(s_array, s_vec, 64, false); - s_initialized = true; - } - return s_array; -} - -NAMESPACE_END diff --git a/CryptoPP/base64.h b/CryptoPP/base64.h deleted file mode 100644 index 5a9e184b2..000000000 --- a/CryptoPP/base64.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef CRYPTOPP_BASE64_H -#define CRYPTOPP_BASE64_H - -#include "basecode.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! Base64 Encoder Class -class Base64Encoder : public SimpleProxyFilter -{ -public: - Base64Encoder(BufferedTransformation *attachment = NULL, bool insertLineBreaks = true, int maxLineLength = 72) - : SimpleProxyFilter(new BaseN_Encoder(new Grouper), attachment) - { - IsolatedInitialize(MakeParameters(Name::InsertLineBreaks(), insertLineBreaks)(Name::MaxLineLength(), maxLineLength)); - } - - void IsolatedInitialize(const NameValuePairs ¶meters); -}; - -//! Base64 Decoder Class -class Base64Decoder : public BaseN_Decoder -{ -public: - Base64Decoder(BufferedTransformation *attachment = NULL) - : BaseN_Decoder(GetDecodingLookupArray(), 6, attachment) {} - - void IsolatedInitialize(const NameValuePairs ¶meters) {} - -private: - static const int * CRYPTOPP_API GetDecodingLookupArray(); -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/basecode.cpp b/CryptoPP/basecode.cpp deleted file mode 100644 index 0c98b2271..000000000 --- a/CryptoPP/basecode.cpp +++ /dev/null @@ -1,238 +0,0 @@ -// basecode.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "basecode.h" -#include "fltrimpl.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -void BaseN_Encoder::IsolatedInitialize(const NameValuePairs ¶meters) -{ - parameters.GetRequiredParameter("BaseN_Encoder", Name::EncodingLookupArray(), m_alphabet); - - parameters.GetRequiredIntParameter("BaseN_Encoder", Name::Log2Base(), m_bitsPerChar); - if (m_bitsPerChar <= 0 || m_bitsPerChar >= 8) - throw InvalidArgument("BaseN_Encoder: Log2Base must be between 1 and 7 inclusive"); - - byte padding; - bool pad; - if (parameters.GetValue(Name::PaddingByte(), padding)) - pad = parameters.GetValueWithDefault(Name::Pad(), true); - else - pad = false; - m_padding = pad ? padding : -1; - - m_bytePos = m_bitPos = 0; - - int i = 8; - while (i%m_bitsPerChar != 0) - i += 8; - m_outputBlockSize = i/m_bitsPerChar; - - m_outBuf.New(m_outputBlockSize); -} - -size_t BaseN_Encoder::Put2(const byte *begin, size_t length, int messageEnd, bool blocking) -{ - FILTER_BEGIN; - while (m_inputPosition < length) - { - if (m_bytePos == 0) - memset(m_outBuf, 0, m_outputBlockSize); - - { - unsigned int b = begin[m_inputPosition++], bitsLeftInSource = 8; - while (true) - { - assert(m_bitPos < m_bitsPerChar); - unsigned int bitsLeftInTarget = m_bitsPerChar-m_bitPos; - m_outBuf[m_bytePos] |= b >> (8-bitsLeftInTarget); - if (bitsLeftInSource >= bitsLeftInTarget) - { - m_bitPos = 0; - ++m_bytePos; - bitsLeftInSource -= bitsLeftInTarget; - if (bitsLeftInSource == 0) - break; - b <<= bitsLeftInTarget; - b &= 0xff; - } - else - { - m_bitPos += bitsLeftInSource; - break; - } - } - } - - assert(m_bytePos <= m_outputBlockSize); - if (m_bytePos == m_outputBlockSize) - { - int i; - for (i=0; i 0) - ++m_bytePos; - - int i; - for (i=0; i 0) - { - memset(m_outBuf+m_bytePos, m_padding, m_outputBlockSize-m_bytePos); - m_bytePos = m_outputBlockSize; - } - FILTER_OUTPUT(2, m_outBuf, m_bytePos, messageEnd); - m_bytePos = m_bitPos = 0; - } - FILTER_END_NO_MESSAGE_END; -} - -void BaseN_Decoder::IsolatedInitialize(const NameValuePairs ¶meters) -{ - parameters.GetRequiredParameter("BaseN_Decoder", Name::DecodingLookupArray(), m_lookup); - - parameters.GetRequiredIntParameter("BaseN_Decoder", Name::Log2Base(), m_bitsPerChar); - if (m_bitsPerChar <= 0 || m_bitsPerChar >= 8) - throw InvalidArgument("BaseN_Decoder: Log2Base must be between 1 and 7 inclusive"); - - m_bytePos = m_bitPos = 0; - - int i = m_bitsPerChar; - while (i%8 != 0) - i += m_bitsPerChar; - m_outputBlockSize = i/8; - - m_outBuf.New(m_outputBlockSize); -} - -size_t BaseN_Decoder::Put2(const byte *begin, size_t length, int messageEnd, bool blocking) -{ - FILTER_BEGIN; - while (m_inputPosition < length) - { - unsigned int value; - value = m_lookup[begin[m_inputPosition++]]; - if (value >= 256) - continue; - - if (m_bytePos == 0 && m_bitPos == 0) - memset(m_outBuf, 0, m_outputBlockSize); - - { - int newBitPos = m_bitPos + m_bitsPerChar; - if (newBitPos <= 8) - m_outBuf[m_bytePos] |= value << (8-newBitPos); - else - { - m_outBuf[m_bytePos] |= value >> (newBitPos-8); - m_outBuf[m_bytePos+1] |= value << (16-newBitPos); - } - - m_bitPos = newBitPos; - while (m_bitPos >= 8) - { - m_bitPos -= 8; - ++m_bytePos; - } - } - - if (m_bytePos == m_outputBlockSize) - { - FILTER_OUTPUT(1, m_outBuf, m_outputBlockSize, 0); - m_bytePos = m_bitPos = 0; - } - } - if (messageEnd) - { - FILTER_OUTPUT(2, m_outBuf, m_bytePos, messageEnd); - m_bytePos = m_bitPos = 0; - } - FILTER_END_NO_MESSAGE_END; -} - -void BaseN_Decoder::InitializeDecodingLookupArray(int *lookup, const byte *alphabet, unsigned int base, bool caseInsensitive) -{ - std::fill(lookup, lookup+256, -1); - - for (unsigned int i=0; i -{ -public: - BaseN_Encoder(BufferedTransformation *attachment=NULL) - {Detach(attachment);} - - BaseN_Encoder(const byte *alphabet, int log2base, BufferedTransformation *attachment=NULL, int padding=-1) - { - Detach(attachment); - IsolatedInitialize(MakeParameters(Name::EncodingLookupArray(), alphabet) - (Name::Log2Base(), log2base) - (Name::Pad(), padding != -1) - (Name::PaddingByte(), byte(padding))); - } - - void IsolatedInitialize(const NameValuePairs ¶meters); - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); - -private: - const byte *m_alphabet; - int m_padding, m_bitsPerChar, m_outputBlockSize; - int m_bytePos, m_bitPos; - SecByteBlock m_outBuf; -}; - -//! base n decoder, where n is a power of 2 -class CRYPTOPP_DLL BaseN_Decoder : public Unflushable -{ -public: - BaseN_Decoder(BufferedTransformation *attachment=NULL) - {Detach(attachment);} - - BaseN_Decoder(const int *lookup, int log2base, BufferedTransformation *attachment=NULL) - { - Detach(attachment); - IsolatedInitialize(MakeParameters(Name::DecodingLookupArray(), lookup)(Name::Log2Base(), log2base)); - } - - void IsolatedInitialize(const NameValuePairs ¶meters); - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); - - static void CRYPTOPP_API InitializeDecodingLookupArray(int *lookup, const byte *alphabet, unsigned int base, bool caseInsensitive); - -private: - const int *m_lookup; - int m_padding, m_bitsPerChar, m_outputBlockSize; - int m_bytePos, m_bitPos; - SecByteBlock m_outBuf; -}; - -//! filter that breaks input stream into groups of fixed size -class CRYPTOPP_DLL Grouper : public Bufferless -{ -public: - Grouper(BufferedTransformation *attachment=NULL) - {Detach(attachment);} - - Grouper(int groupSize, const std::string &separator, const std::string &terminator, BufferedTransformation *attachment=NULL) - { - Detach(attachment); - IsolatedInitialize(MakeParameters(Name::GroupSize(), groupSize) - (Name::Separator(), ConstByteArrayParameter(separator)) - (Name::Terminator(), ConstByteArrayParameter(terminator))); - } - - void IsolatedInitialize(const NameValuePairs ¶meters); - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); - -private: - SecByteBlock m_separator, m_terminator; - size_t m_groupSize, m_counter; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/cbcmac.cpp b/CryptoPP/cbcmac.cpp deleted file mode 100644 index 6b0e8858e..000000000 --- a/CryptoPP/cbcmac.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "cbcmac.h" - -NAMESPACE_BEGIN(CryptoPP) - -void CBC_MAC_Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) -{ - AccessCipher().SetKey(key, length, params); - m_reg.CleanNew(AccessCipher().BlockSize()); - m_counter = 0; -} - -void CBC_MAC_Base::Update(const byte *input, size_t length) -{ - unsigned int blockSize = AccessCipher().BlockSize(); - - while (m_counter && length) - { - m_reg[m_counter++] ^= *input++; - if (m_counter == blockSize) - ProcessBuf(); - length--; - } - - if (length >= blockSize) - { - size_t leftOver = AccessCipher().AdvancedProcessBlocks(m_reg, input, m_reg, length, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput); - input += (length - leftOver); - length = leftOver; - } - - while (length--) - { - m_reg[m_counter++] ^= *input++; - if (m_counter == blockSize) - ProcessBuf(); - } -} - -void CBC_MAC_Base::TruncatedFinal(byte *mac, size_t size) -{ - ThrowIfInvalidTruncatedSize(size); - - if (m_counter) - ProcessBuf(); - - memcpy(mac, m_reg, size); - memset(m_reg, 0, AccessCipher().BlockSize()); -} - -void CBC_MAC_Base::ProcessBuf() -{ - AccessCipher().ProcessBlock(m_reg); - m_counter = 0; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/cbcmac.h b/CryptoPP/cbcmac.h deleted file mode 100644 index 4675dcb3d..000000000 --- a/CryptoPP/cbcmac.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef CRYPTOPP_CBCMAC_H -#define CRYPTOPP_CBCMAC_H - -#include "seckey.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CBC_MAC_Base : public MessageAuthenticationCode -{ -public: - CBC_MAC_Base() {} - - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms); - void Update(const byte *input, size_t length); - void TruncatedFinal(byte *mac, size_t size); - unsigned int DigestSize() const {return const_cast(this)->AccessCipher().BlockSize();} - -protected: - virtual BlockCipher & AccessCipher() =0; - -private: - void ProcessBuf(); - SecByteBlock m_reg; - unsigned int m_counter; -}; - -//! CBC-MAC -/*! Compatible with FIPS 113. T should be a class derived from BlockCipherDocumentation. - Secure only for fixed length messages. For variable length messages use CMAC or DMAC. -*/ -template -class CBC_MAC : public MessageAuthenticationCodeImpl >, public SameKeyLengthAs -{ -public: - CBC_MAC() {} - CBC_MAC(const byte *key, size_t length=SameKeyLengthAs::DEFAULT_KEYLENGTH) - {this->SetKey(key, length);} - - static std::string StaticAlgorithmName() {return std::string("CBC-MAC(") + T::StaticAlgorithmName() + ")";} - -private: - BlockCipher & AccessCipher() {return m_cipher;} - typename T::Encryption m_cipher; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/ccm.cpp b/CryptoPP/ccm.cpp deleted file mode 100644 index 030828ad8..000000000 --- a/CryptoPP/ccm.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// ccm.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "ccm.h" - -NAMESPACE_BEGIN(CryptoPP) - -void CCM_Base::SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms) -{ - BlockCipher &blockCipher = AccessBlockCipher(); - - blockCipher.SetKey(userKey, keylength, params); - - if (blockCipher.BlockSize() != REQUIRED_BLOCKSIZE) - throw InvalidArgument(AlgorithmName() + ": block size of underlying block cipher is not 16"); - - m_digestSize = params.GetIntValueWithDefault(Name::DigestSize(), DefaultDigestSize()); - if (m_digestSize % 2 > 0 || m_digestSize < 4 || m_digestSize > 16) - throw InvalidArgument(AlgorithmName() + ": DigestSize must be 4, 6, 8, 10, 12, 14, or 16"); - - m_buffer.Grow(2*REQUIRED_BLOCKSIZE); - m_L = 8; -} - -void CCM_Base::Resync(const byte *iv, size_t len) -{ - BlockCipher &cipher = AccessBlockCipher(); - - m_L = REQUIRED_BLOCKSIZE-1-(int)len; - assert(m_L >= 2); - if (m_L > 8) - m_L = 8; - - m_buffer[0] = byte(m_L-1); // flag - memcpy(m_buffer+1, iv, len); - memset(m_buffer+1+len, 0, REQUIRED_BLOCKSIZE-1-len); - - if (m_state >= State_IVSet) - m_ctr.Resynchronize(m_buffer, REQUIRED_BLOCKSIZE); - else - m_ctr.SetCipherWithIV(cipher, m_buffer); - - m_ctr.Seek(REQUIRED_BLOCKSIZE); - m_aadLength = 0; - m_messageLength = 0; -} - -void CCM_Base::UncheckedSpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength) -{ - if (m_state != State_IVSet) - throw BadState(AlgorithmName(), "SpecifyDataLengths", "or after State_IVSet"); - - m_aadLength = headerLength; - m_messageLength = messageLength; - - byte *cbcBuffer = CBC_Buffer(); - const BlockCipher &cipher = GetBlockCipher(); - - cbcBuffer[0] = byte(64*(headerLength>0) + 8*((m_digestSize-2)/2) + (m_L-1)); // flag - PutWord(true, BIG_ENDIAN_ORDER, cbcBuffer+REQUIRED_BLOCKSIZE-8, m_messageLength); - memcpy(cbcBuffer+1, m_buffer+1, REQUIRED_BLOCKSIZE-1-m_L); - cipher.ProcessBlock(cbcBuffer); - - if (headerLength>0) - { - assert(m_bufferedDataLength == 0); - - if (headerLength < ((1<<16) - (1<<8))) - { - PutWord(true, BIG_ENDIAN_ORDER, m_buffer, (word16)headerLength); - m_bufferedDataLength = 2; - } - else if (headerLength < (W64LIT(1)<<32)) - { - m_buffer[0] = 0xff; - m_buffer[1] = 0xfe; - PutWord(false, BIG_ENDIAN_ORDER, m_buffer+2, (word32)headerLength); - m_bufferedDataLength = 6; - } - else - { - m_buffer[0] = 0xff; - m_buffer[1] = 0xff; - PutWord(false, BIG_ENDIAN_ORDER, m_buffer+2, headerLength); - m_bufferedDataLength = 10; - } - } -} - -size_t CCM_Base::AuthenticateBlocks(const byte *data, size_t len) -{ - byte *cbcBuffer = CBC_Buffer(); - const BlockCipher &cipher = GetBlockCipher(); - return cipher.AdvancedProcessBlocks(cbcBuffer, data, cbcBuffer, len, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput); -} - -void CCM_Base::AuthenticateLastHeaderBlock() -{ - byte *cbcBuffer = CBC_Buffer(); - const BlockCipher &cipher = GetBlockCipher(); - - if (m_aadLength != m_totalHeaderLength) - throw InvalidArgument(AlgorithmName() + ": header length doesn't match that given in SpecifyDataLengths"); - - if (m_bufferedDataLength > 0) - { - xorbuf(cbcBuffer, m_buffer, m_bufferedDataLength); - cipher.ProcessBlock(cbcBuffer); - m_bufferedDataLength = 0; - } -} - -void CCM_Base::AuthenticateLastConfidentialBlock() -{ - byte *cbcBuffer = CBC_Buffer(); - const BlockCipher &cipher = GetBlockCipher(); - - if (m_messageLength != m_totalMessageLength) - throw InvalidArgument(AlgorithmName() + ": message length doesn't match that given in SpecifyDataLengths"); - - if (m_bufferedDataLength > 0) - { - xorbuf(cbcBuffer, m_buffer, m_bufferedDataLength); - cipher.ProcessBlock(cbcBuffer); - m_bufferedDataLength = 0; - } -} - -void CCM_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize) -{ - m_ctr.Seek(0); - m_ctr.ProcessData(mac, CBC_Buffer(), macSize); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/ccm.h b/CryptoPP/ccm.h deleted file mode 100644 index b1e5f00b9..000000000 --- a/CryptoPP/ccm.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef CRYPTOPP_CCM_H -#define CRYPTOPP_CCM_H - -#include "authenc.h" -#include "modes.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! . -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CCM_Base : public AuthenticatedSymmetricCipherBase -{ -public: - CCM_Base() - : m_digestSize(0), m_L(0) {} - - // AuthenticatedSymmetricCipher - std::string AlgorithmName() const - {return GetBlockCipher().AlgorithmName() + std::string("/CCM");} - size_t MinKeyLength() const - {return GetBlockCipher().MinKeyLength();} - size_t MaxKeyLength() const - {return GetBlockCipher().MaxKeyLength();} - size_t DefaultKeyLength() const - {return GetBlockCipher().DefaultKeyLength();} - size_t GetValidKeyLength(size_t n) const - {return GetBlockCipher().GetValidKeyLength(n);} - bool IsValidKeyLength(size_t n) const - {return GetBlockCipher().IsValidKeyLength(n);} - unsigned int OptimalDataAlignment() const - {return GetBlockCipher().OptimalDataAlignment();} - IV_Requirement IVRequirement() const - {return UNIQUE_IV;} - unsigned int IVSize() const - {return 8;} - unsigned int MinIVLength() const - {return 7;} - unsigned int MaxIVLength() const - {return 13;} - unsigned int DigestSize() const - {return m_digestSize;} - lword MaxHeaderLength() const - {return W64LIT(0)-1;} - lword MaxMessageLength() const - {return m_L<8 ? (W64LIT(1)<<(8*m_L))-1 : W64LIT(0)-1;} - bool NeedsPrespecifiedDataLengths() const - {return true;} - void UncheckedSpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength); - -protected: - // AuthenticatedSymmetricCipherBase - bool AuthenticationIsOnPlaintext() const - {return true;} - unsigned int AuthenticationBlockSize() const - {return GetBlockCipher().BlockSize();} - void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms); - void Resync(const byte *iv, size_t len); - size_t AuthenticateBlocks(const byte *data, size_t len); - void AuthenticateLastHeaderBlock(); - void AuthenticateLastConfidentialBlock(); - void AuthenticateLastFooterBlock(byte *mac, size_t macSize); - SymmetricCipher & AccessSymmetricCipher() {return m_ctr;} - - virtual BlockCipher & AccessBlockCipher() =0; - virtual int DefaultDigestSize() const =0; - - const BlockCipher & GetBlockCipher() const {return const_cast(this)->AccessBlockCipher();}; - byte *CBC_Buffer() {return m_buffer+REQUIRED_BLOCKSIZE;} - - enum {REQUIRED_BLOCKSIZE = 16}; - int m_digestSize, m_L; - word64 m_messageLength, m_aadLength; - CTR_Mode_ExternalCipher::Encryption m_ctr; -}; - -//! . -template -class CCM_Final : public CCM_Base -{ -public: - static std::string StaticAlgorithmName() - {return T_BlockCipher::StaticAlgorithmName() + std::string("/CCM");} - bool IsForwardTransformation() const - {return T_IsEncryption;} - -private: - BlockCipher & AccessBlockCipher() {return m_cipher;} - int DefaultDigestSize() const {return T_DefaultDigestSize;} - typename T_BlockCipher::Encryption m_cipher; -}; - -/// CCM -template -struct CCM : public AuthenticatedSymmetricCipherDocumentation -{ - typedef CCM_Final Encryption; - typedef CCM_Final Decryption; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/channels.cpp b/CryptoPP/channels.cpp deleted file mode 100644 index 7359f54f7..000000000 --- a/CryptoPP/channels.cpp +++ /dev/null @@ -1,309 +0,0 @@ -// channels.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "channels.h" - -NAMESPACE_BEGIN(CryptoPP) -USING_NAMESPACE(std) - -#if 0 -void MessageSwitch::AddDefaultRoute(BufferedTransformation &destination, const std::string &channel) -{ - m_defaultRoutes.push_back(Route(&destination, channel)); -} - -void MessageSwitch::AddRoute(unsigned int begin, unsigned int end, BufferedTransformation &destination, const std::string &channel) -{ - RangeRoute route(begin, end, Route(&destination, channel)); - RouteList::iterator it = upper_bound(m_routes.begin(), m_routes.end(), route); - m_routes.insert(it, route); -} - -/* -class MessageRouteIterator -{ -public: - typedef MessageSwitch::RouteList::const_iterator RouteIterator; - typedef MessageSwitch::DefaultRouteList::const_iterator DefaultIterator; - - bool m_useDefault; - RouteIterator m_itRouteCurrent, m_itRouteEnd; - DefaultIterator m_itDefaultCurrent, m_itDefaultEnd; - - MessageRouteIterator(MessageSwitch &ms, const std::string &channel) - : m_channel(channel) - { - pair range = cs.m_routeMap.equal_range(channel); - if (range.first == range.second) - { - m_useDefault = true; - m_itListCurrent = cs.m_defaultRoutes.begin(); - m_itListEnd = cs.m_defaultRoutes.end(); - } - else - { - m_useDefault = false; - m_itMapCurrent = range.first; - m_itMapEnd = range.second; - } - } - - bool End() const - { - return m_useDefault ? m_itListCurrent == m_itListEnd : m_itMapCurrent == m_itMapEnd; - } - - void Next() - { - if (m_useDefault) - ++m_itListCurrent; - else - ++m_itMapCurrent; - } - - BufferedTransformation & Destination() - { - return m_useDefault ? *m_itListCurrent->first : *m_itMapCurrent->second.first; - } - - const std::string & Message() - { - if (m_useDefault) - return m_itListCurrent->second.get() ? *m_itListCurrent->second.get() : m_channel; - else - return m_itMapCurrent->second.second; - } -}; - -void MessageSwitch::Put(byte inByte); -void MessageSwitch::Put(const byte *inString, unsigned int length); - -void MessageSwitch::Flush(bool completeFlush, int propagation=-1); -void MessageSwitch::MessageEnd(int propagation=-1); -void MessageSwitch::PutMessageEnd(const byte *inString, unsigned int length, int propagation=-1); -void MessageSwitch::MessageSeriesEnd(int propagation=-1); -*/ -#endif - - -// -// ChannelRouteIterator -////////////////////////// - -void ChannelRouteIterator::Reset(const std::string &channel) -{ - m_channel = channel; - pair range = m_cs.m_routeMap.equal_range(channel); - if (range.first == range.second) - { - m_useDefault = true; - m_itListCurrent = m_cs.m_defaultRoutes.begin(); - m_itListEnd = m_cs.m_defaultRoutes.end(); - } - else - { - m_useDefault = false; - m_itMapCurrent = range.first; - m_itMapEnd = range.second; - } -} - -bool ChannelRouteIterator::End() const -{ - return m_useDefault ? m_itListCurrent == m_itListEnd : m_itMapCurrent == m_itMapEnd; -} - -void ChannelRouteIterator::Next() -{ - if (m_useDefault) - ++m_itListCurrent; - else - ++m_itMapCurrent; -} - -BufferedTransformation & ChannelRouteIterator::Destination() -{ - return m_useDefault ? *m_itListCurrent->first : *m_itMapCurrent->second.first; -} - -const std::string & ChannelRouteIterator::Channel() -{ - if (m_useDefault) - return m_itListCurrent->second.get() ? *m_itListCurrent->second.get() : m_channel; - else - return m_itMapCurrent->second.second; -} - - -// -// ChannelSwitch -/////////////////// - -size_t ChannelSwitch::ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking) -{ - if (m_blocked) - { - m_blocked = false; - goto WasBlocked; - } - - m_it.Reset(channel); - - while (!m_it.End()) - { -WasBlocked: - if (m_it.Destination().ChannelPut2(m_it.Channel(), begin, length, messageEnd, blocking)) - { - m_blocked = true; - return 1; - } - - m_it.Next(); - } - - return 0; -} - -void ChannelSwitch::IsolatedInitialize(const NameValuePairs ¶meters/* =g_nullNameValuePairs */) -{ - m_routeMap.clear(); - m_defaultRoutes.clear(); - m_blocked = false; -} - -bool ChannelSwitch::ChannelFlush(const std::string &channel, bool completeFlush, int propagation, bool blocking) -{ - if (m_blocked) - { - m_blocked = false; - goto WasBlocked; - } - - m_it.Reset(channel); - - while (!m_it.End()) - { - WasBlocked: - if (m_it.Destination().ChannelFlush(m_it.Channel(), completeFlush, propagation, blocking)) - { - m_blocked = true; - return true; - } - - m_it.Next(); - } - - return false; -} - -bool ChannelSwitch::ChannelMessageSeriesEnd(const std::string &channel, int propagation, bool blocking) -{ - if (m_blocked) - { - m_blocked = false; - goto WasBlocked; - } - - m_it.Reset(channel); - - while (!m_it.End()) - { - WasBlocked: - if (m_it.Destination().ChannelMessageSeriesEnd(m_it.Channel(), propagation)) - { - m_blocked = true; - return true; - } - - m_it.Next(); - } - - return false; -} - -byte * ChannelSwitch::ChannelCreatePutSpace(const std::string &channel, size_t &size) -{ - m_it.Reset(channel); - if (!m_it.End()) - { - BufferedTransformation &target = m_it.Destination(); - const std::string &channel = m_it.Channel(); - m_it.Next(); - if (m_it.End()) // there is only one target channel - return target.ChannelCreatePutSpace(channel, size); - } - size = 0; - return NULL; -} - -size_t ChannelSwitch::ChannelPutModifiable2(const std::string &channel, byte *inString, size_t length, int messageEnd, bool blocking) -{ - ChannelRouteIterator it(*this); - it.Reset(channel); - - if (!it.End()) - { - BufferedTransformation &target = it.Destination(); - const std::string &targetChannel = it.Channel(); - it.Next(); - if (it.End()) // there is only one target channel - return target.ChannelPutModifiable2(targetChannel, inString, length, messageEnd, blocking); - } - - return ChannelPut2(channel, inString, length, messageEnd, blocking); -} - -void ChannelSwitch::AddDefaultRoute(BufferedTransformation &destination) -{ - m_defaultRoutes.push_back(DefaultRoute(&destination, value_ptr(NULL))); -} - -void ChannelSwitch::RemoveDefaultRoute(BufferedTransformation &destination) -{ - for (DefaultRouteList::iterator it = m_defaultRoutes.begin(); it != m_defaultRoutes.end(); ++it) - if (it->first == &destination && !it->second.get()) - { - m_defaultRoutes.erase(it); - break; - } -} - -void ChannelSwitch::AddDefaultRoute(BufferedTransformation &destination, const std::string &outChannel) -{ - m_defaultRoutes.push_back(DefaultRoute(&destination, outChannel)); -} - -void ChannelSwitch::RemoveDefaultRoute(BufferedTransformation &destination, const std::string &outChannel) -{ - for (DefaultRouteList::iterator it = m_defaultRoutes.begin(); it != m_defaultRoutes.end(); ++it) - if (it->first == &destination && (it->second.get() && *it->second == outChannel)) - { - m_defaultRoutes.erase(it); - break; - } -} - -void ChannelSwitch::AddRoute(const std::string &inChannel, BufferedTransformation &destination, const std::string &outChannel) -{ - m_routeMap.insert(RouteMap::value_type(inChannel, Route(&destination, outChannel))); -} - -void ChannelSwitch::RemoveRoute(const std::string &inChannel, BufferedTransformation &destination, const std::string &outChannel) -{ - typedef ChannelSwitch::RouteMap::iterator MapIterator; - pair range = m_routeMap.equal_range(inChannel); - - for (MapIterator it = range.first; it != range.second; ++it) - if (it->second.first == &destination && it->second.second == outChannel) - { - m_routeMap.erase(it); - break; - } -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/channels.h b/CryptoPP/channels.h deleted file mode 100644 index 837415615..000000000 --- a/CryptoPP/channels.h +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef CRYPTOPP_CHANNELS_H -#define CRYPTOPP_CHANNELS_H - -#include "simple.h" -#include "smartptr.h" -#include -#include - -NAMESPACE_BEGIN(CryptoPP) - -#if 0 -//! Route input on default channel to different and/or multiple channels based on message sequence number -class MessageSwitch : public Sink -{ -public: - void AddDefaultRoute(BufferedTransformation &destination, const std::string &channel); - void AddRoute(unsigned int begin, unsigned int end, BufferedTransformation &destination, const std::string &channel); - - void Put(byte inByte); - void Put(const byte *inString, unsigned int length); - - void Flush(bool completeFlush, int propagation=-1); - void MessageEnd(int propagation=-1); - void PutMessageEnd(const byte *inString, unsigned int length, int propagation=-1); - void MessageSeriesEnd(int propagation=-1); - -private: - typedef std::pair Route; - struct RangeRoute - { - RangeRoute(unsigned int begin, unsigned int end, const Route &route) - : begin(begin), end(end), route(route) {} - bool operator<(const RangeRoute &rhs) const {return begin < rhs.begin;} - unsigned int begin, end; - Route route; - }; - - typedef std::list RouteList; - typedef std::list DefaultRouteList; - - RouteList m_routes; - DefaultRouteList m_defaultRoutes; - unsigned int m_nCurrentMessage; -}; -#endif - -class ChannelSwitchTypedefs -{ -public: - typedef std::pair Route; - typedef std::multimap RouteMap; - - typedef std::pair > DefaultRoute; - typedef std::list DefaultRouteList; - - // SunCC workaround: can't use const_iterator here - typedef RouteMap::iterator MapIterator; - typedef DefaultRouteList::iterator ListIterator; -}; - -class ChannelSwitch; - -class ChannelRouteIterator : public ChannelSwitchTypedefs -{ -public: - ChannelSwitch& m_cs; - std::string m_channel; - bool m_useDefault; - MapIterator m_itMapCurrent, m_itMapEnd; - ListIterator m_itListCurrent, m_itListEnd; - - ChannelRouteIterator(ChannelSwitch &cs) : m_cs(cs) {} - void Reset(const std::string &channel); - bool End() const; - void Next(); - BufferedTransformation & Destination(); - const std::string & Channel(); -}; - -//! Route input to different and/or multiple channels based on channel ID -class CRYPTOPP_DLL ChannelSwitch : public Multichannel, public ChannelSwitchTypedefs -{ -public: - ChannelSwitch() : m_it(*this), m_blocked(false) {} - ChannelSwitch(BufferedTransformation &destination) : m_it(*this), m_blocked(false) - { - AddDefaultRoute(destination); - } - ChannelSwitch(BufferedTransformation &destination, const std::string &outChannel) : m_it(*this), m_blocked(false) - { - AddDefaultRoute(destination, outChannel); - } - - void IsolatedInitialize(const NameValuePairs ¶meters=g_nullNameValuePairs); - - size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking); - size_t ChannelPutModifiable2(const std::string &channel, byte *begin, size_t length, int messageEnd, bool blocking); - - bool ChannelFlush(const std::string &channel, bool completeFlush, int propagation=-1, bool blocking=true); - bool ChannelMessageSeriesEnd(const std::string &channel, int propagation=-1, bool blocking=true); - - byte * ChannelCreatePutSpace(const std::string &channel, size_t &size); - - void AddDefaultRoute(BufferedTransformation &destination); - void RemoveDefaultRoute(BufferedTransformation &destination); - void AddDefaultRoute(BufferedTransformation &destination, const std::string &outChannel); - void RemoveDefaultRoute(BufferedTransformation &destination, const std::string &outChannel); - void AddRoute(const std::string &inChannel, BufferedTransformation &destination, const std::string &outChannel); - void RemoveRoute(const std::string &inChannel, BufferedTransformation &destination, const std::string &outChannel); - -private: - RouteMap m_routeMap; - DefaultRouteList m_defaultRoutes; - - ChannelRouteIterator m_it; - bool m_blocked; - - friend class ChannelRouteIterator; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/cmac.cpp b/CryptoPP/cmac.cpp deleted file mode 100644 index a31d5f8b0..000000000 --- a/CryptoPP/cmac.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// cmac.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "cmac.h" - -NAMESPACE_BEGIN(CryptoPP) - -static void MulU(byte *k, unsigned int length) -{ - byte carry = 0; - - for (int i=length-1; i>=1; i-=2) - { - byte carry2 = k[i] >> 7; - k[i] += k[i] + carry; - carry = k[i-1] >> 7; - k[i-1] += k[i-1] + carry2; - } - - if (carry) - { - switch (length) - { - case 8: - k[7] ^= 0x1b; - break; - case 16: - k[15] ^= 0x87; - break; - case 32: - k[30] ^= 4; - k[31] ^= 0x23; - break; - default: - throw InvalidArgument("CMAC: " + IntToString(length) + " is not a supported cipher block size"); - } - } -} - -void CMAC_Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) -{ - BlockCipher &cipher = AccessCipher(); - unsigned int blockSize = cipher.BlockSize(); - - cipher.SetKey(key, length, params); - m_reg.CleanNew(3*blockSize); - m_counter = 0; - - cipher.ProcessBlock(m_reg, m_reg+blockSize); - MulU(m_reg+blockSize, blockSize); - memcpy(m_reg+2*blockSize, m_reg+blockSize, blockSize); - MulU(m_reg+2*blockSize, blockSize); -} - -void CMAC_Base::Update(const byte *input, size_t length) -{ - if (!length) - return; - - BlockCipher &cipher = AccessCipher(); - unsigned int blockSize = cipher.BlockSize(); - - if (m_counter > 0) - { - unsigned int len = UnsignedMin(blockSize - m_counter, length); - xorbuf(m_reg+m_counter, input, len); - length -= len; - input += len; - m_counter += len; - - if (m_counter == blockSize && length > 0) - { - cipher.ProcessBlock(m_reg); - m_counter = 0; - } - } - - if (length > blockSize) - { - assert(m_counter == 0); - size_t leftOver = 1 + cipher.AdvancedProcessBlocks(m_reg, input, m_reg, length-1, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput); - input += (length - leftOver); - length = leftOver; - } - - if (length > 0) - { - assert(m_counter + length <= blockSize); - xorbuf(m_reg+m_counter, input, length); - m_counter += (unsigned int)length; - } - - assert(m_counter > 0); -} - -void CMAC_Base::TruncatedFinal(byte *mac, size_t size) -{ - ThrowIfInvalidTruncatedSize(size); - - BlockCipher &cipher = AccessCipher(); - unsigned int blockSize = cipher.BlockSize(); - - if (m_counter < blockSize) - { - m_reg[m_counter] ^= 0x80; - cipher.AdvancedProcessBlocks(m_reg, m_reg+2*blockSize, m_reg, blockSize, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput); - } - else - cipher.AdvancedProcessBlocks(m_reg, m_reg+blockSize, m_reg, blockSize, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput); - - memcpy(mac, m_reg, size); - - m_counter = 0; - memset(m_reg, 0, blockSize); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/cmac.h b/CryptoPP/cmac.h deleted file mode 100644 index d8a1b391d..000000000 --- a/CryptoPP/cmac.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef CRYPTOPP_CMAC_H -#define CRYPTOPP_CMAC_H - -#include "seckey.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CMAC_Base : public MessageAuthenticationCode -{ -public: - CMAC_Base() {} - - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms); - void Update(const byte *input, size_t length); - void TruncatedFinal(byte *mac, size_t size); - unsigned int DigestSize() const {return GetCipher().BlockSize();} - unsigned int OptimalBlockSize() const {return GetCipher().BlockSize();} - unsigned int OptimalDataAlignment() const {return GetCipher().OptimalDataAlignment();} - -protected: - friend class EAX_Base; - - const BlockCipher & GetCipher() const {return const_cast(this)->AccessCipher();} - virtual BlockCipher & AccessCipher() =0; - - void ProcessBuf(); - SecByteBlock m_reg; - unsigned int m_counter; -}; - -/// CMAC -/*! Template parameter T should be a class derived from BlockCipherDocumentation, for example AES, with a block size of 8, 16, or 32 */ -template -class CMAC : public MessageAuthenticationCodeImpl >, public SameKeyLengthAs -{ -public: - CMAC() {} - CMAC(const byte *key, size_t length=SameKeyLengthAs::DEFAULT_KEYLENGTH) - {this->SetKey(key, length);} - - static std::string StaticAlgorithmName() {return std::string("CMAC(") + T::StaticAlgorithmName() + ")";} - -private: - BlockCipher & AccessCipher() {return m_cipher;} - typename T::Encryption m_cipher; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/config.h b/CryptoPP/config.h deleted file mode 100644 index edbfd00ef..000000000 --- a/CryptoPP/config.h +++ /dev/null @@ -1,462 +0,0 @@ -#ifndef CRYPTOPP_CONFIG_H -#define CRYPTOPP_CONFIG_H - -// ***************** Important Settings ******************** - -// define this if running on a big-endian CPU -#if !defined(IS_LITTLE_ENDIAN) && (defined(__BIG_ENDIAN__) || defined(__sparc) || defined(__sparc__) || defined(__hppa__) || defined(__MIPSEB__) || defined(__ARMEB__) || (defined(__MWERKS__) && !defined(__INTEL__))) -# define IS_BIG_ENDIAN -#endif - -// define this if running on a little-endian CPU -// big endian will be assumed if IS_LITTLE_ENDIAN is not defined -#ifndef IS_BIG_ENDIAN -# define IS_LITTLE_ENDIAN -#endif - -// define this if you want to disable all OS-dependent features, -// such as sockets and OS-provided random number generators -#define NO_OS_DEPENDENCE - -// Define this to use features provided by Microsoft's CryptoAPI. -// Currently the only feature used is random number generation. -// This macro will be ignored if NO_OS_DEPENDENCE is defined. -// #define USE_MS_CRYPTOAPI - -// Define this to 1 to enforce the requirement in FIPS 186-2 Change Notice 1 that only 1024 bit moduli be used -#ifndef DSA_1024_BIT_MODULUS_ONLY -# define DSA_1024_BIT_MODULUS_ONLY 1 -#endif - -// ***************** Less Important Settings *************** - -// define this to retain (as much as possible) old deprecated function and class names -// #define CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - -#define GZIP_OS_CODE 0 - -// Try this if your CPU has 256K internal cache or a slow multiply instruction -// and you want a (possibly) faster IDEA implementation using log tables -// #define IDEA_LARGECACHE - -// Define this if, for the linear congruential RNG, you want to use -// the original constants as specified in S.K. Park and K.W. Miller's -// CACM paper. -// #define LCRNG_ORIGINAL_NUMBERS - -// choose which style of sockets to wrap (mostly useful for cygwin which has both) -#define PREFER_BERKELEY_STYLE_SOCKETS -// #define PREFER_WINDOWS_STYLE_SOCKETS - -// set the name of Rijndael cipher, was "Rijndael" before version 5.3 -#define CRYPTOPP_RIJNDAEL_NAME "AES" - -// ***************** Important Settings Again ******************** -// But the defaults should be ok. - -// namespace support is now required -#ifdef NO_NAMESPACE -# error namespace support is now required -#endif - -// Define this to workaround a Microsoft CryptoAPI bug where -// each call to CryptAcquireContext causes a 100 KB memory leak. -// Defining this will cause Crypto++ to make only one call to CryptAcquireContext. -#define WORKAROUND_MS_BUG_Q258000 - -#ifdef CRYPTOPP_DOXYGEN_PROCESSING -// Avoid putting "CryptoPP::" in front of everything in Doxygen output -# define CryptoPP -# define NAMESPACE_BEGIN(x) -# define NAMESPACE_END -// Get Doxygen to generate better documentation for these typedefs -# define DOCUMENTED_TYPEDEF(x, y) class y : public x {}; -#else -# define NAMESPACE_BEGIN(x) namespace x { -# define NAMESPACE_END } -# define DOCUMENTED_TYPEDEF(x, y) typedef x y; -#endif -#define ANONYMOUS_NAMESPACE_BEGIN namespace { -#define USING_NAMESPACE(x) using namespace x; -#define DOCUMENTED_NAMESPACE_BEGIN(x) namespace x { -#define DOCUMENTED_NAMESPACE_END } - -// What is the type of the third parameter to bind? -// For Unix, the new standard is ::socklen_t (typically unsigned int), and the old standard is int. -// Unfortunately there is no way to tell whether or not socklen_t is defined. -// To work around this, TYPE_OF_SOCKLEN_T is a macro so that you can change it from the makefile. -#ifndef TYPE_OF_SOCKLEN_T -# if defined(_WIN32) || defined(__CYGWIN__) -# define TYPE_OF_SOCKLEN_T int -# else -# define TYPE_OF_SOCKLEN_T ::socklen_t -# endif -#endif - -#if defined(__CYGWIN__) && defined(PREFER_WINDOWS_STYLE_SOCKETS) -# define __USE_W32_SOCKETS -#endif - -typedef unsigned char byte; // put in global namespace to avoid ambiguity with other byte typedefs - -NAMESPACE_BEGIN(CryptoPP) - -typedef unsigned short word16; -typedef unsigned int word32; - -#if defined(_MSC_VER) || defined(__BORLANDC__) - typedef unsigned __int64 word64; - #define W64LIT(x) x##ui64 -#else - typedef unsigned long long word64; - #define W64LIT(x) x##ULL -#endif - -// define large word type, used for file offsets and such -typedef word64 lword; -const lword LWORD_MAX = W64LIT(0xffffffffffffffff); - -#ifdef __GNUC__ - #define CRYPTOPP_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#endif - -// define hword, word, and dword. these are used for multiprecision integer arithmetic -// Intel compiler won't have _umul128 until version 10.0. See http://softwarecommunity.intel.com/isn/Community/en-US/forums/thread/30231625.aspx -#if (defined(_MSC_VER) && (!defined(__INTEL_COMPILER) || __INTEL_COMPILER >= 1000) && (defined(_M_X64) || defined(_M_IA64))) || (defined(__DECCXX) && defined(__alpha__)) || (defined(__INTEL_COMPILER) && defined(__x86_64__)) || (defined(__SUNPRO_CC) && defined(__x86_64__)) - typedef word32 hword; - typedef word64 word; -#else - #define CRYPTOPP_NATIVE_DWORD_AVAILABLE - #if defined(__alpha__) || defined(__ia64__) || defined(_ARCH_PPC64) || defined(__x86_64__) || defined(__mips64) || defined(__sparc64__) - #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !(CRYPTOPP_GCC_VERSION == 40001 && defined(__APPLE__)) && CRYPTOPP_GCC_VERSION >= 30400 - // GCC 4.0.1 on MacOS X is missing __umodti3 and __udivti3 - // mode(TI) division broken on amd64 with GCC earlier than GCC 3.4 - typedef word32 hword; - typedef word64 word; - typedef __uint128_t dword; - typedef __uint128_t word128; - #define CRYPTOPP_WORD128_AVAILABLE - #else - // if we're here, it means we're on a 64-bit CPU but we don't have a way to obtain 128-bit multiplication results - typedef word16 hword; - typedef word32 word; - typedef word64 dword; - #endif - #else - // being here means the native register size is probably 32 bits or less - #define CRYPTOPP_BOOL_SLOW_WORD64 1 - typedef word16 hword; - typedef word32 word; - typedef word64 dword; - #endif -#endif -#ifndef CRYPTOPP_BOOL_SLOW_WORD64 - #define CRYPTOPP_BOOL_SLOW_WORD64 0 -#endif - -const unsigned int WORD_SIZE = sizeof(word); -const unsigned int WORD_BITS = WORD_SIZE * 8; - -NAMESPACE_END - -#ifndef CRYPTOPP_L1_CACHE_LINE_SIZE - // This should be a lower bound on the L1 cache line size. It's used for defense against timing attacks. - #if defined(_M_X64) || defined(__x86_64__) - #define CRYPTOPP_L1_CACHE_LINE_SIZE 64 - #else - // L1 cache line size is 32 on Pentium III and earlier - #define CRYPTOPP_L1_CACHE_LINE_SIZE 32 - #endif -#endif - -#if defined(_MSC_VER) - #if _MSC_VER == 1200 - #include - #endif - #if _MSC_VER > 1200 || defined(_mm_free) - #define CRYPTOPP_MSVC6PP_OR_LATER // VC 6 processor pack or later - #else - #define CRYPTOPP_MSVC6_NO_PP // VC 6 without processor pack - #endif -#endif - -#ifndef CRYPTOPP_ALIGN_DATA - #if defined(CRYPTOPP_MSVC6PP_OR_LATER) - #define CRYPTOPP_ALIGN_DATA(x) __declspec(align(x)) - #elif defined(__GNUC__) - #define CRYPTOPP_ALIGN_DATA(x) __attribute__((aligned(x))) - #else - #define CRYPTOPP_ALIGN_DATA(x) - #endif -#endif - -#ifndef CRYPTOPP_SECTION_ALIGN16 - #if defined(__GNUC__) && !defined(__APPLE__) - // the alignment attribute doesn't seem to work without this section attribute when -fdata-sections is turned on - #define CRYPTOPP_SECTION_ALIGN16 __attribute__((section ("CryptoPP_Align16"))) - #else - #define CRYPTOPP_SECTION_ALIGN16 - #endif -#endif - -#if defined(_MSC_VER) || defined(__fastcall) - #define CRYPTOPP_FASTCALL __fastcall -#else - #define CRYPTOPP_FASTCALL -#endif - -// VC60 workaround: it doesn't allow typename in some places -#if defined(_MSC_VER) && (_MSC_VER < 1300) -#define CPP_TYPENAME -#else -#define CPP_TYPENAME typename -#endif - -// VC60 workaround: can't cast unsigned __int64 to float or double -#if defined(_MSC_VER) && !defined(CRYPTOPP_MSVC6PP_OR_LATER) -#define CRYPTOPP_VC6_INT64 (__int64) -#else -#define CRYPTOPP_VC6_INT64 -#endif - -#ifdef _MSC_VER -#define CRYPTOPP_NO_VTABLE __declspec(novtable) -#else -#define CRYPTOPP_NO_VTABLE -#endif - -#ifdef _MSC_VER - // 4231: nonstandard extension used : 'extern' before template explicit instantiation - // 4250: dominance - // 4251: member needs to have dll-interface - // 4275: base needs to have dll-interface - // 4660: explicitly instantiating a class that's already implicitly instantiated - // 4661: no suitable definition provided for explicit template instantiation request - // 4786: identifer was truncated in debug information - // 4355: 'this' : used in base member initializer list - // 4910: '__declspec(dllexport)' and 'extern' are incompatible on an explicit instantiation -# pragma warning(disable: 4231 4250 4251 4275 4660 4661 4786 4355 4910) -#endif - -#ifdef __BORLANDC__ -// 8037: non-const function called for const object. needed to work around BCB2006 bug -# pragma warn -8037 -#endif - -#if (defined(_MSC_VER) && _MSC_VER <= 1300) || defined(__MWERKS__) || defined(_STLPORT_VERSION) || defined(ANDROID_NDK) -#define CRYPTOPP_DISABLE_UNCAUGHT_EXCEPTION -#endif - -#ifndef CRYPTOPP_DISABLE_UNCAUGHT_EXCEPTION -#define CRYPTOPP_UNCAUGHT_EXCEPTION_AVAILABLE -#endif - -#ifdef CRYPTOPP_DISABLE_X86ASM // for backwards compatibility: this macro had both meanings -#define CRYPTOPP_DISABLE_ASM -#define CRYPTOPP_DISABLE_SSE2 -#endif - -#if !defined(CRYPTOPP_DISABLE_ASM) && ((defined(_MSC_VER) && defined(_M_IX86)) || (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))) - // C++Builder 2010 does not allow "call label" where label is defined within inline assembly - #define CRYPTOPP_X86_ASM_AVAILABLE - - #if !defined(CRYPTOPP_DISABLE_SSE2) && (defined(CRYPTOPP_MSVC6PP_OR_LATER) || CRYPTOPP_GCC_VERSION >= 30300) - #define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 1 - #else - #define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 0 - #endif - - // SSSE3 was actually introduced in GNU as 2.17, which was released 6/23/2006, but we can't tell what version of binutils is installed. - // GCC 4.1.2 was released on 2/13/2007, so we'll use that as a proxy for the binutils version. - #if !defined(CRYPTOPP_DISABLE_SSSE3) && (_MSC_VER >= 1400 || CRYPTOPP_GCC_VERSION >= 40102) - #define CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE 1 - #else - #define CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE 0 - #endif -#endif - -#if !defined(CRYPTOPP_DISABLE_ASM) && defined(_MSC_VER) && defined(_M_X64) - #define CRYPTOPP_X64_MASM_AVAILABLE -#endif - -#if !defined(CRYPTOPP_DISABLE_ASM) && defined(__GNUC__) && defined(__x86_64__) - #define CRYPTOPP_X64_ASM_AVAILABLE -#endif - -#if !defined(CRYPTOPP_DISABLE_SSE2) && (defined(CRYPTOPP_MSVC6PP_OR_LATER) || defined(__SSE2__)) - #define CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE 1 -#else - #define CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE 0 -#endif - -#if !defined(CRYPTOPP_DISABLE_SSSE3) && !defined(CRYPTOPP_DISABLE_AESNI) && CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE && (CRYPTOPP_GCC_VERSION >= 40400 || _MSC_FULL_VER >= 150030729 || __INTEL_COMPILER >= 1110) - #define CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE 1 -#else - #define CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE 0 -#endif - -#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) - #define CRYPTOPP_BOOL_ALIGN16_ENABLED 1 -#else - #define CRYPTOPP_BOOL_ALIGN16_ENABLED 0 -#endif - -// how to allocate 16-byte aligned memory (for SSE2) -#if defined(CRYPTOPP_MSVC6PP_OR_LATER) - #define CRYPTOPP_MM_MALLOC_AVAILABLE -#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define CRYPTOPP_MALLOC_ALIGNMENT_IS_16 -#elif defined(__linux__) || defined(__sun__) || defined(__CYGWIN__) - #define CRYPTOPP_MEMALIGN_AVAILABLE -#else - #define CRYPTOPP_NO_ALIGNED_ALLOC -#endif - -// how to disable inlining -#if defined(_MSC_VER) && _MSC_VER >= 1300 -# define CRYPTOPP_NOINLINE_DOTDOTDOT -# define CRYPTOPP_NOINLINE __declspec(noinline) -#elif defined(__GNUC__) -# define CRYPTOPP_NOINLINE_DOTDOTDOT -# define CRYPTOPP_NOINLINE __attribute__((noinline)) -#else -# define CRYPTOPP_NOINLINE_DOTDOTDOT ... -# define CRYPTOPP_NOINLINE -#endif - -// how to declare class constants -#if (defined(_MSC_VER) && _MSC_VER <= 1300) || defined(__INTEL_COMPILER) -# define CRYPTOPP_CONSTANT(x) enum {x}; -#else -# define CRYPTOPP_CONSTANT(x) static const int x; -#endif - -#if defined(_M_X64) || defined(__x86_64__) - #define CRYPTOPP_BOOL_X64 1 -#else - #define CRYPTOPP_BOOL_X64 0 -#endif - -// see http://predef.sourceforge.net/prearch.html -#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) - #define CRYPTOPP_BOOL_X86 1 -#else - #define CRYPTOPP_BOOL_X86 0 -#endif - -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86 || defined(__powerpc__) - #define CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS -#endif - -#define CRYPTOPP_VERSION 562 - -// ***************** determine availability of OS features ******************** - -#ifndef NO_OS_DEPENDENCE - -#if defined(_WIN32) || defined(__CYGWIN__) -#define CRYPTOPP_WIN32_AVAILABLE -#endif - -#if defined(__unix__) || defined(__MACH__) || defined(__NetBSD__) || defined(__sun) -#define CRYPTOPP_UNIX_AVAILABLE -#endif - -#if defined(CRYPTOPP_WIN32_AVAILABLE) || defined(CRYPTOPP_UNIX_AVAILABLE) -# define HIGHRES_TIMER_AVAILABLE -#endif - -#ifdef CRYPTOPP_UNIX_AVAILABLE -# define HAS_BERKELEY_STYLE_SOCKETS -#endif - -#ifdef CRYPTOPP_WIN32_AVAILABLE -# define HAS_WINDOWS_STYLE_SOCKETS -#endif - -#if defined(HIGHRES_TIMER_AVAILABLE) && (defined(HAS_BERKELEY_STYLE_SOCKETS) || defined(HAS_WINDOWS_STYLE_SOCKETS)) -# define SOCKETS_AVAILABLE -#endif - -#if defined(HAS_WINDOWS_STYLE_SOCKETS) && (!defined(HAS_BERKELEY_STYLE_SOCKETS) || defined(PREFER_WINDOWS_STYLE_SOCKETS)) -# define USE_WINDOWS_STYLE_SOCKETS -#else -# define USE_BERKELEY_STYLE_SOCKETS -#endif - -#if defined(HIGHRES_TIMER_AVAILABLE) && defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_BERKELEY_STYLE_SOCKETS) -# define WINDOWS_PIPES_AVAILABLE -#endif - -#if defined(CRYPTOPP_WIN32_AVAILABLE) && defined(USE_MS_CRYPTOAPI) -# define NONBLOCKING_RNG_AVAILABLE -# define OS_RNG_AVAILABLE -#endif - -#if defined(CRYPTOPP_UNIX_AVAILABLE) || defined(CRYPTOPP_DOXYGEN_PROCESSING) -# define NONBLOCKING_RNG_AVAILABLE -# define BLOCKING_RNG_AVAILABLE -# define OS_RNG_AVAILABLE -# define HAS_PTHREADS -# define THREADS_AVAILABLE -#endif - -#ifdef CRYPTOPP_WIN32_AVAILABLE -# define HAS_WINTHREADS -# define THREADS_AVAILABLE -#endif - -#endif // NO_OS_DEPENDENCE - -// ***************** DLL related ******************** - -#if defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(CRYPTOPP_DOXYGEN_PROCESSING) - -#ifdef CRYPTOPP_EXPORTS -#define CRYPTOPP_IS_DLL -#define CRYPTOPP_DLL __declspec(dllexport) -#elif defined(CRYPTOPP_IMPORTS) -#define CRYPTOPP_IS_DLL -#define CRYPTOPP_DLL __declspec(dllimport) -#else -#define CRYPTOPP_DLL -#endif - -#define CRYPTOPP_API __cdecl - -#else // CRYPTOPP_WIN32_AVAILABLE - -#define CRYPTOPP_DLL -#define CRYPTOPP_API - -#endif // CRYPTOPP_WIN32_AVAILABLE - -#if defined(__MWERKS__) -#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS extern class CRYPTOPP_DLL -#elif defined(__BORLANDC__) || defined(__SUNPRO_CC) -#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS template class CRYPTOPP_DLL -#else -#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS extern template class CRYPTOPP_DLL -#endif - -#if defined(CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES) && !defined(CRYPTOPP_IMPORTS) -#define CRYPTOPP_DLL_TEMPLATE_CLASS template class CRYPTOPP_DLL -#else -#define CRYPTOPP_DLL_TEMPLATE_CLASS CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS -#endif - -#if defined(__MWERKS__) -#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS extern class -#elif defined(__BORLANDC__) || defined(__SUNPRO_CC) -#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS template class -#else -#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS extern template class -#endif - -#if defined(CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES) && !defined(CRYPTOPP_EXPORTS) -#define CRYPTOPP_STATIC_TEMPLATE_CLASS template class -#else -#define CRYPTOPP_STATIC_TEMPLATE_CLASS CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS -#endif - -#endif diff --git a/CryptoPP/cpu.cpp b/CryptoPP/cpu.cpp deleted file mode 100644 index 3610a7c8e..000000000 --- a/CryptoPP/cpu.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// cpu.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "cpu.h" -#include "misc.h" -#include - -#ifndef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY -#include -#include -#endif - -#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -#ifdef CRYPTOPP_CPUID_AVAILABLE - -#if _MSC_VER >= 1400 && CRYPTOPP_BOOL_X64 - -bool CpuId(word32 input, word32 *output) -{ - __cpuid((int *)output, input); - return true; -} - -#else - -#ifndef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY -extern "C" { -typedef void (*SigHandler)(int); - -static jmp_buf s_jmpNoCPUID; -static void SigIllHandlerCPUID(int) -{ - longjmp(s_jmpNoCPUID, 1); -} - -static jmp_buf s_jmpNoSSE2; -static void SigIllHandlerSSE2(int) -{ - longjmp(s_jmpNoSSE2, 1); -} -} -#endif - -bool CpuId(word32 input, word32 *output) -{ -#ifdef CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY - __try - { - __asm - { - mov eax, input - cpuid - mov edi, output - mov [edi], eax - mov [edi+4], ebx - mov [edi+8], ecx - mov [edi+12], edx - } - } - __except (1) - { - return false; - } - return true; -#else - SigHandler oldHandler = signal(SIGILL, SigIllHandlerCPUID); - if (oldHandler == SIG_ERR) - return false; - - bool result = true; - if (setjmp(s_jmpNoCPUID)) - result = false; - else - { - asm - ( - // save ebx in case -fPIC is being used -#if CRYPTOPP_BOOL_X86 - "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx" -#else - "pushq %%rbx; cpuid; mov %%ebx, %%edi; popq %%rbx" -#endif - : "=a" (output[0]), "=D" (output[1]), "=c" (output[2]), "=d" (output[3]) - : "a" (input) - ); - } - - signal(SIGILL, oldHandler); - return result; -#endif -} - -#endif - -static bool TrySSE2() -{ -#if CRYPTOPP_BOOL_X64 - return true; -#elif defined(CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY) - __try - { -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - AS2(por xmm0, xmm0) // executing SSE2 instruction -#elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE - __m128i x = _mm_setzero_si128(); - return _mm_cvtsi128_si32(x) == 0; -#endif - } - __except (1) - { - return false; - } - return true; -#else - SigHandler oldHandler = signal(SIGILL, SigIllHandlerSSE2); - if (oldHandler == SIG_ERR) - return false; - - bool result = true; - if (setjmp(s_jmpNoSSE2)) - result = false; - else - { -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - __asm __volatile ("por %xmm0, %xmm0"); -#elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE - __m128i x = _mm_setzero_si128(); - result = _mm_cvtsi128_si32(x) == 0; -#endif - } - - signal(SIGILL, oldHandler); - return result; -#endif -} - -bool g_x86DetectionDone = false; -bool g_hasISSE = false, g_hasSSE2 = false, g_hasSSSE3 = false, g_hasMMX = false, g_hasAESNI = false, g_hasCLMUL = false, g_isP4 = false; -word32 g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE; - -void DetectX86Features() -{ - word32 cpuid[4], cpuid1[4]; - if (!CpuId(0, cpuid)) - return; - if (!CpuId(1, cpuid1)) - return; - - g_hasMMX = (cpuid1[3] & (1 << 23)) != 0; - if ((cpuid1[3] & (1 << 26)) != 0) - g_hasSSE2 = TrySSE2(); - g_hasSSSE3 = g_hasSSE2 && (cpuid1[2] & (1<<9)); - g_hasAESNI = g_hasSSE2 && (cpuid1[2] & (1<<25)); - g_hasCLMUL = g_hasSSE2 && (cpuid1[2] & (1<<1)); - - if ((cpuid1[3] & (1 << 25)) != 0) - g_hasISSE = true; - else - { - word32 cpuid2[4]; - CpuId(0x080000000, cpuid2); - if (cpuid2[0] >= 0x080000001) - { - CpuId(0x080000001, cpuid2); - g_hasISSE = (cpuid2[3] & (1 << 22)) != 0; - } - } - - std::swap(cpuid[2], cpuid[3]); - if (memcmp(cpuid+1, "GenuineIntel", 12) == 0) - { - g_isP4 = ((cpuid1[0] >> 8) & 0xf) == 0xf; - g_cacheLineSize = 8 * GETBYTE(cpuid1[1], 1); - } - else if (memcmp(cpuid+1, "AuthenticAMD", 12) == 0) - { - CpuId(0x80000005, cpuid); - g_cacheLineSize = GETBYTE(cpuid[2], 0); - } - - if (!g_cacheLineSize) - g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE; - - g_x86DetectionDone = true; -} - -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/cpu.h b/CryptoPP/cpu.h deleted file mode 100644 index 65029d338..000000000 --- a/CryptoPP/cpu.h +++ /dev/null @@ -1,345 +0,0 @@ -#ifndef CRYPTOPP_CPU_H -#define CRYPTOPP_CPU_H - -#ifdef CRYPTOPP_GENERATE_X64_MASM - -#define CRYPTOPP_X86_ASM_AVAILABLE -#define CRYPTOPP_BOOL_X64 1 -#define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 1 -#define NAMESPACE_END - -#else - -#include "config.h" - -#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE -#include -#endif - -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE -#if !defined(__GNUC__) || defined(__SSSE3__) || defined(__INTEL_COMPILER) -#include -#else -__inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_shuffle_epi8 (__m128i a, __m128i b) -{ - asm ("pshufb %1, %0" : "+x"(a) : "xm"(b)); - return a; -} -#endif -#if !defined(__GNUC__) || defined(__SSE4_1__) || defined(__INTEL_COMPILER) -#include -#else -__inline int __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_extract_epi32 (__m128i a, const int i) -{ - int r; - asm ("pextrd %2, %1, %0" : "=rm"(r) : "x"(a), "i"(i)); - return r; -} -__inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_insert_epi32 (__m128i a, int b, const int i) -{ - asm ("pinsrd %2, %1, %0" : "+x"(a) : "rm"(b), "i"(i)); - return a; -} -#endif -#if !defined(__GNUC__) || (defined(__AES__) && defined(__PCLMUL__)) || defined(__INTEL_COMPILER) -#include -#else -__inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_clmulepi64_si128 (__m128i a, __m128i b, const int i) -{ - asm ("pclmulqdq %2, %1, %0" : "+x"(a) : "xm"(b), "i"(i)); - return a; -} -__inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_aeskeygenassist_si128 (__m128i a, const int i) -{ - __m128i r; - asm ("aeskeygenassist %2, %1, %0" : "=x"(r) : "xm"(a), "i"(i)); - return r; -} -__inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_aesimc_si128 (__m128i a) -{ - __m128i r; - asm ("aesimc %1, %0" : "=x"(r) : "xm"(a)); - return r; -} -__inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_aesenc_si128 (__m128i a, __m128i b) -{ - asm ("aesenc %1, %0" : "+x"(a) : "xm"(b)); - return a; -} -__inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_aesenclast_si128 (__m128i a, __m128i b) -{ - asm ("aesenclast %1, %0" : "+x"(a) : "xm"(b)); - return a; -} -__inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_aesdec_si128 (__m128i a, __m128i b) -{ - asm ("aesdec %1, %0" : "+x"(a) : "xm"(b)); - return a; -} -__inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__)) -_mm_aesdeclast_si128 (__m128i a, __m128i b) -{ - asm ("aesdeclast %1, %0" : "+x"(a) : "xm"(b)); - return a; -} -#endif -#endif - -NAMESPACE_BEGIN(CryptoPP) - -#if CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X64 - -#define CRYPTOPP_CPUID_AVAILABLE - -// these should not be used directly -extern CRYPTOPP_DLL bool g_x86DetectionDone; -extern CRYPTOPP_DLL bool g_hasSSSE3; -extern CRYPTOPP_DLL bool g_hasAESNI; -extern CRYPTOPP_DLL bool g_hasCLMUL; -extern CRYPTOPP_DLL bool g_isP4; -extern CRYPTOPP_DLL word32 g_cacheLineSize; -CRYPTOPP_DLL void CRYPTOPP_API DetectX86Features(); -CRYPTOPP_DLL bool CRYPTOPP_API CpuId(word32 input, word32 *output); - -#if CRYPTOPP_BOOL_X64 -inline bool HasSSE2() {return true;} -inline bool HasISSE() {return true;} -inline bool HasMMX() {return true;} -#else - -extern CRYPTOPP_DLL bool g_hasSSE2; -extern CRYPTOPP_DLL bool g_hasISSE; -extern CRYPTOPP_DLL bool g_hasMMX; - -inline bool HasSSE2() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasSSE2; -} - -inline bool HasISSE() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasISSE; -} - -inline bool HasMMX() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasMMX; -} - -#endif - -inline bool HasSSSE3() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasSSSE3; -} - -inline bool HasAESNI() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasAESNI; -} - -inline bool HasCLMUL() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasCLMUL; -} - -inline bool IsP4() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_isP4; -} - -inline int GetCacheLineSize() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_cacheLineSize; -} - -#else - -inline int GetCacheLineSize() -{ - return CRYPTOPP_L1_CACHE_LINE_SIZE; -} - -#endif - -#endif - -#ifdef CRYPTOPP_GENERATE_X64_MASM - #define AS1(x) x*newline* - #define AS2(x, y) x, y*newline* - #define AS3(x, y, z) x, y, z*newline* - #define ASS(x, y, a, b, c, d) x, y, a*64+b*16+c*4+d*newline* - #define ASL(x) label##x:*newline* - #define ASJ(x, y, z) x label##y*newline* - #define ASC(x, y) x label##y*newline* - #define AS_HEX(y) 0##y##h -#elif defined(_MSC_VER) || defined(__BORLANDC__) - #define CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY - #define AS1(x) __asm {x} - #define AS2(x, y) __asm {x, y} - #define AS3(x, y, z) __asm {x, y, z} - #define ASS(x, y, a, b, c, d) __asm {x, y, (a)*64+(b)*16+(c)*4+(d)} - #define ASL(x) __asm {label##x:} - #define ASJ(x, y, z) __asm {x label##y} - #define ASC(x, y) __asm {x label##y} - #define CRYPTOPP_NAKED __declspec(naked) - #define AS_HEX(y) 0x##y -#else - #define CRYPTOPP_GNU_STYLE_INLINE_ASSEMBLY - // define these in two steps to allow arguments to be expanded - #define GNU_AS1(x) #x ";" - #define GNU_AS2(x, y) #x ", " #y ";" - #define GNU_AS3(x, y, z) #x ", " #y ", " #z ";" - #define GNU_ASL(x) "\n" #x ":" - #define GNU_ASJ(x, y, z) #x " " #y #z ";" - #define AS1(x) GNU_AS1(x) - #define AS2(x, y) GNU_AS2(x, y) - #define AS3(x, y, z) GNU_AS3(x, y, z) - #define ASS(x, y, a, b, c, d) #x ", " #y ", " #a "*64+" #b "*16+" #c "*4+" #d ";" - #define ASL(x) GNU_ASL(x) - #define ASJ(x, y, z) GNU_ASJ(x, y, z) - #define ASC(x, y) #x " " #y ";" - #define CRYPTOPP_NAKED - #define AS_HEX(y) 0x##y -#endif - -#define IF0(y) -#define IF1(y) y - -#ifdef CRYPTOPP_GENERATE_X64_MASM -#define ASM_MOD(x, y) ((x) MOD (y)) -#define XMMWORD_PTR XMMWORD PTR -#else -// GNU assembler doesn't seem to have mod operator -#define ASM_MOD(x, y) ((x)-((x)/(y))*(y)) -// GAS 2.15 doesn't support XMMWORD PTR. it seems necessary only for MASM -#define XMMWORD_PTR -#endif - -#if CRYPTOPP_BOOL_X86 - #define AS_REG_1 ecx - #define AS_REG_2 edx - #define AS_REG_3 esi - #define AS_REG_4 edi - #define AS_REG_5 eax - #define AS_REG_6 ebx - #define AS_REG_7 ebp - #define AS_REG_1d ecx - #define AS_REG_2d edx - #define AS_REG_3d esi - #define AS_REG_4d edi - #define AS_REG_5d eax - #define AS_REG_6d ebx - #define AS_REG_7d ebp - #define WORD_SZ 4 - #define WORD_REG(x) e##x - #define WORD_PTR DWORD PTR - #define AS_PUSH_IF86(x) AS1(push e##x) - #define AS_POP_IF86(x) AS1(pop e##x) - #define AS_JCXZ jecxz -#elif CRYPTOPP_BOOL_X64 - #ifdef CRYPTOPP_GENERATE_X64_MASM - #define AS_REG_1 rcx - #define AS_REG_2 rdx - #define AS_REG_3 r8 - #define AS_REG_4 r9 - #define AS_REG_5 rax - #define AS_REG_6 r10 - #define AS_REG_7 r11 - #define AS_REG_1d ecx - #define AS_REG_2d edx - #define AS_REG_3d r8d - #define AS_REG_4d r9d - #define AS_REG_5d eax - #define AS_REG_6d r10d - #define AS_REG_7d r11d - #else - #define AS_REG_1 rdi - #define AS_REG_2 rsi - #define AS_REG_3 rdx - #define AS_REG_4 rcx - #define AS_REG_5 r8 - #define AS_REG_6 r9 - #define AS_REG_7 r10 - #define AS_REG_1d edi - #define AS_REG_2d esi - #define AS_REG_3d edx - #define AS_REG_4d ecx - #define AS_REG_5d r8d - #define AS_REG_6d r9d - #define AS_REG_7d r10d - #endif - #define WORD_SZ 8 - #define WORD_REG(x) r##x - #define WORD_PTR QWORD PTR - #define AS_PUSH_IF86(x) - #define AS_POP_IF86(x) - #define AS_JCXZ jrcxz -#endif - -// helper macro for stream cipher output -#define AS_XMM_OUTPUT4(labelPrefix, inputPtr, outputPtr, x0, x1, x2, x3, t, p0, p1, p2, p3, increment)\ - AS2( test inputPtr, inputPtr)\ - ASC( jz, labelPrefix##3)\ - AS2( test inputPtr, 15)\ - ASC( jnz, labelPrefix##7)\ - AS2( pxor xmm##x0, [inputPtr+p0*16])\ - AS2( pxor xmm##x1, [inputPtr+p1*16])\ - AS2( pxor xmm##x2, [inputPtr+p2*16])\ - AS2( pxor xmm##x3, [inputPtr+p3*16])\ - AS2( add inputPtr, increment*16)\ - ASC( jmp, labelPrefix##3)\ - ASL(labelPrefix##7)\ - AS2( movdqu xmm##t, [inputPtr+p0*16])\ - AS2( pxor xmm##x0, xmm##t)\ - AS2( movdqu xmm##t, [inputPtr+p1*16])\ - AS2( pxor xmm##x1, xmm##t)\ - AS2( movdqu xmm##t, [inputPtr+p2*16])\ - AS2( pxor xmm##x2, xmm##t)\ - AS2( movdqu xmm##t, [inputPtr+p3*16])\ - AS2( pxor xmm##x3, xmm##t)\ - AS2( add inputPtr, increment*16)\ - ASL(labelPrefix##3)\ - AS2( test outputPtr, 15)\ - ASC( jnz, labelPrefix##8)\ - AS2( movdqa [outputPtr+p0*16], xmm##x0)\ - AS2( movdqa [outputPtr+p1*16], xmm##x1)\ - AS2( movdqa [outputPtr+p2*16], xmm##x2)\ - AS2( movdqa [outputPtr+p3*16], xmm##x3)\ - ASC( jmp, labelPrefix##9)\ - ASL(labelPrefix##8)\ - AS2( movdqu [outputPtr+p0*16], xmm##x0)\ - AS2( movdqu [outputPtr+p1*16], xmm##x1)\ - AS2( movdqu [outputPtr+p2*16], xmm##x2)\ - AS2( movdqu [outputPtr+p3*16], xmm##x3)\ - ASL(labelPrefix##9)\ - AS2( add outputPtr, increment*16) - -NAMESPACE_END - -#endif diff --git a/CryptoPP/crc.cpp b/CryptoPP/crc.cpp deleted file mode 100644 index 10c25c257..000000000 --- a/CryptoPP/crc.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// crc.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "crc.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -/* Table of CRC-32's of all single byte values (made by makecrc.c) */ -const word32 CRC32::m_tab[] = { -#ifdef IS_LITTLE_ENDIAN - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL -#else - 0x00000000L, 0x96300777L, 0x2c610eeeL, 0xba510999L, 0x19c46d07L, - 0x8ff46a70L, 0x35a563e9L, 0xa395649eL, 0x3288db0eL, 0xa4b8dc79L, - 0x1ee9d5e0L, 0x88d9d297L, 0x2b4cb609L, 0xbd7cb17eL, 0x072db8e7L, - 0x911dbf90L, 0x6410b71dL, 0xf220b06aL, 0x4871b9f3L, 0xde41be84L, - 0x7dd4da1aL, 0xebe4dd6dL, 0x51b5d4f4L, 0xc785d383L, 0x56986c13L, - 0xc0a86b64L, 0x7af962fdL, 0xecc9658aL, 0x4f5c0114L, 0xd96c0663L, - 0x633d0ffaL, 0xf50d088dL, 0xc8206e3bL, 0x5e10694cL, 0xe44160d5L, - 0x727167a2L, 0xd1e4033cL, 0x47d4044bL, 0xfd850dd2L, 0x6bb50aa5L, - 0xfaa8b535L, 0x6c98b242L, 0xd6c9bbdbL, 0x40f9bcacL, 0xe36cd832L, - 0x755cdf45L, 0xcf0dd6dcL, 0x593dd1abL, 0xac30d926L, 0x3a00de51L, - 0x8051d7c8L, 0x1661d0bfL, 0xb5f4b421L, 0x23c4b356L, 0x9995bacfL, - 0x0fa5bdb8L, 0x9eb80228L, 0x0888055fL, 0xb2d90cc6L, 0x24e90bb1L, - 0x877c6f2fL, 0x114c6858L, 0xab1d61c1L, 0x3d2d66b6L, 0x9041dc76L, - 0x0671db01L, 0xbc20d298L, 0x2a10d5efL, 0x8985b171L, 0x1fb5b606L, - 0xa5e4bf9fL, 0x33d4b8e8L, 0xa2c90778L, 0x34f9000fL, 0x8ea80996L, - 0x18980ee1L, 0xbb0d6a7fL, 0x2d3d6d08L, 0x976c6491L, 0x015c63e6L, - 0xf4516b6bL, 0x62616c1cL, 0xd8306585L, 0x4e0062f2L, 0xed95066cL, - 0x7ba5011bL, 0xc1f40882L, 0x57c40ff5L, 0xc6d9b065L, 0x50e9b712L, - 0xeab8be8bL, 0x7c88b9fcL, 0xdf1ddd62L, 0x492dda15L, 0xf37cd38cL, - 0x654cd4fbL, 0x5861b24dL, 0xce51b53aL, 0x7400bca3L, 0xe230bbd4L, - 0x41a5df4aL, 0xd795d83dL, 0x6dc4d1a4L, 0xfbf4d6d3L, 0x6ae96943L, - 0xfcd96e34L, 0x468867adL, 0xd0b860daL, 0x732d0444L, 0xe51d0333L, - 0x5f4c0aaaL, 0xc97c0dddL, 0x3c710550L, 0xaa410227L, 0x10100bbeL, - 0x86200cc9L, 0x25b56857L, 0xb3856f20L, 0x09d466b9L, 0x9fe461ceL, - 0x0ef9de5eL, 0x98c9d929L, 0x2298d0b0L, 0xb4a8d7c7L, 0x173db359L, - 0x810db42eL, 0x3b5cbdb7L, 0xad6cbac0L, 0x2083b8edL, 0xb6b3bf9aL, - 0x0ce2b603L, 0x9ad2b174L, 0x3947d5eaL, 0xaf77d29dL, 0x1526db04L, - 0x8316dc73L, 0x120b63e3L, 0x843b6494L, 0x3e6a6d0dL, 0xa85a6a7aL, - 0x0bcf0ee4L, 0x9dff0993L, 0x27ae000aL, 0xb19e077dL, 0x44930ff0L, - 0xd2a30887L, 0x68f2011eL, 0xfec20669L, 0x5d5762f7L, 0xcb676580L, - 0x71366c19L, 0xe7066b6eL, 0x761bd4feL, 0xe02bd389L, 0x5a7ada10L, - 0xcc4add67L, 0x6fdfb9f9L, 0xf9efbe8eL, 0x43beb717L, 0xd58eb060L, - 0xe8a3d6d6L, 0x7e93d1a1L, 0xc4c2d838L, 0x52f2df4fL, 0xf167bbd1L, - 0x6757bca6L, 0xdd06b53fL, 0x4b36b248L, 0xda2b0dd8L, 0x4c1b0aafL, - 0xf64a0336L, 0x607a0441L, 0xc3ef60dfL, 0x55df67a8L, 0xef8e6e31L, - 0x79be6946L, 0x8cb361cbL, 0x1a8366bcL, 0xa0d26f25L, 0x36e26852L, - 0x95770cccL, 0x03470bbbL, 0xb9160222L, 0x2f260555L, 0xbe3bbac5L, - 0x280bbdb2L, 0x925ab42bL, 0x046ab35cL, 0xa7ffd7c2L, 0x31cfd0b5L, - 0x8b9ed92cL, 0x1daede5bL, 0xb0c2649bL, 0x26f263ecL, 0x9ca36a75L, - 0x0a936d02L, 0xa906099cL, 0x3f360eebL, 0x85670772L, 0x13570005L, - 0x824abf95L, 0x147ab8e2L, 0xae2bb17bL, 0x381bb60cL, 0x9b8ed292L, - 0x0dbed5e5L, 0xb7efdc7cL, 0x21dfdb0bL, 0xd4d2d386L, 0x42e2d4f1L, - 0xf8b3dd68L, 0x6e83da1fL, 0xcd16be81L, 0x5b26b9f6L, 0xe177b06fL, - 0x7747b718L, 0xe65a0888L, 0x706a0fffL, 0xca3b0666L, 0x5c0b0111L, - 0xff9e658fL, 0x69ae62f8L, 0xd3ff6b61L, 0x45cf6c16L, 0x78e20aa0L, - 0xeed20dd7L, 0x5483044eL, 0xc2b30339L, 0x612667a7L, 0xf71660d0L, - 0x4d476949L, 0xdb776e3eL, 0x4a6ad1aeL, 0xdc5ad6d9L, 0x660bdf40L, - 0xf03bd837L, 0x53aebca9L, 0xc59ebbdeL, 0x7fcfb247L, 0xe9ffb530L, - 0x1cf2bdbdL, 0x8ac2bacaL, 0x3093b353L, 0xa6a3b424L, 0x0536d0baL, - 0x9306d7cdL, 0x2957de54L, 0xbf67d923L, 0x2e7a66b3L, 0xb84a61c4L, - 0x021b685dL, 0x942b6f2aL, 0x37be0bb4L, 0xa18e0cc3L, 0x1bdf055aL, - 0x8def022dL -#endif -}; - -CRC32::CRC32() -{ - Reset(); -} - -void CRC32::Update(const byte *s, size_t n) -{ - word32 crc = m_crc; - - for(; !IsAligned(s) && n > 0; n--) - crc = m_tab[CRC32_INDEX(crc) ^ *s++] ^ CRC32_SHIFTED(crc); - - while (n >= 4) - { - crc ^= *(const word32 *)s; - crc = m_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); - crc = m_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); - crc = m_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); - crc = m_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); - n -= 4; - s += 4; - } - - while (n--) - crc = m_tab[CRC32_INDEX(crc) ^ *s++] ^ CRC32_SHIFTED(crc); - - m_crc = crc; -} - -void CRC32::TruncatedFinal(byte *hash, size_t size) -{ - ThrowIfInvalidTruncatedSize(size); - - m_crc ^= CRC32_NEGL; - for (size_t i=0; i> 8) -#else -#define CRC32_INDEX(c) (c >> 24) -#define CRC32_SHIFTED(c) (c << 8) -#endif - -//! CRC Checksum Calculation -class CRC32 : public HashTransformation -{ -public: - CRYPTOPP_CONSTANT(DIGESTSIZE = 4) - CRC32(); - void Update(const byte *input, size_t length); - void TruncatedFinal(byte *hash, size_t size); - unsigned int DigestSize() const {return DIGESTSIZE;} - static const char * StaticAlgorithmName() {return "CRC32";} - std::string AlgorithmName() const {return StaticAlgorithmName();} - - void UpdateByte(byte b) {m_crc = m_tab[CRC32_INDEX(m_crc) ^ b] ^ CRC32_SHIFTED(m_crc);} - byte GetCrcByte(size_t i) const {return ((byte *)&(m_crc))[i];} - -private: - void Reset() {m_crc = CRC32_NEGL;} - - static const word32 m_tab[256]; - word32 m_crc; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/cryptlib.cpp b/CryptoPP/cryptlib.cpp deleted file mode 100644 index df138ddb0..000000000 --- a/CryptoPP/cryptlib.cpp +++ /dev/null @@ -1,828 +0,0 @@ -// cryptlib.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "cryptlib.h" -#include "misc.h" -#include "filters.h" -#include "algparam.h" -#include "fips140.h" -#include "argnames.h" -#include "fltrimpl.h" -#include "trdlocal.h" -#include "osrng.h" - -#include - -NAMESPACE_BEGIN(CryptoPP) - -CRYPTOPP_COMPILE_ASSERT(sizeof(byte) == 1); -CRYPTOPP_COMPILE_ASSERT(sizeof(word16) == 2); -CRYPTOPP_COMPILE_ASSERT(sizeof(word32) == 4); -CRYPTOPP_COMPILE_ASSERT(sizeof(word64) == 8); -#ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE -CRYPTOPP_COMPILE_ASSERT(sizeof(dword) == 2*sizeof(word)); -#endif - -const std::string DEFAULT_CHANNEL; -const std::string AAD_CHANNEL = "AAD"; -const std::string &BufferedTransformation::NULL_CHANNEL = DEFAULT_CHANNEL; - -class NullNameValuePairs : public NameValuePairs -{ -public: - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const {return false;} -}; - -simple_ptr s_pNullNameValuePairs(new NullNameValuePairs); -const NameValuePairs &g_nullNameValuePairs = *s_pNullNameValuePairs.m_p; - -BufferedTransformation & TheBitBucket() -{ - static BitBucket bitBucket; - return bitBucket; -} - -Algorithm::Algorithm(bool checkSelfTestStatus) -{ - if (checkSelfTestStatus && FIPS_140_2_ComplianceEnabled()) - { - if (GetPowerUpSelfTestStatus() == POWER_UP_SELF_TEST_NOT_DONE && !PowerUpSelfTestInProgressOnThisThread()) - throw SelfTestFailure("Cryptographic algorithms are disabled before the power-up self tests are performed."); - - if (GetPowerUpSelfTestStatus() == POWER_UP_SELF_TEST_FAILED) - throw SelfTestFailure("Cryptographic algorithms are disabled after a power-up self test failed."); - } -} - -void SimpleKeyingInterface::SetKey(const byte *key, size_t length, const NameValuePairs ¶ms) -{ - this->ThrowIfInvalidKeyLength(length); - this->UncheckedSetKey(key, (unsigned int)length, params); -} - -void SimpleKeyingInterface::SetKeyWithRounds(const byte *key, size_t length, int rounds) -{ - SetKey(key, length, MakeParameters(Name::Rounds(), rounds)); -} - -void SimpleKeyingInterface::SetKeyWithIV(const byte *key, size_t length, const byte *iv, size_t ivLength) -{ - SetKey(key, length, MakeParameters(Name::IV(), ConstByteArrayParameter(iv, ivLength))); -} - -void SimpleKeyingInterface::ThrowIfInvalidKeyLength(size_t length) -{ - if (!IsValidKeyLength(length)) - throw InvalidKeyLength(GetAlgorithm().AlgorithmName(), length); -} - -void SimpleKeyingInterface::ThrowIfResynchronizable() -{ - if (IsResynchronizable()) - throw InvalidArgument(GetAlgorithm().AlgorithmName() + ": this object requires an IV"); -} - -void SimpleKeyingInterface::ThrowIfInvalidIV(const byte *iv) -{ - if (!iv && IVRequirement() == UNPREDICTABLE_RANDOM_IV) - throw InvalidArgument(GetAlgorithm().AlgorithmName() + ": this object cannot use a null IV"); -} - -size_t SimpleKeyingInterface::ThrowIfInvalidIVLength(int size) -{ - if (size < 0) - return IVSize(); - else if ((size_t)size < MinIVLength()) - throw InvalidArgument(GetAlgorithm().AlgorithmName() + ": IV length " + IntToString(size) + " is less than the minimum of " + IntToString(MinIVLength())); - else if ((size_t)size > MaxIVLength()) - throw InvalidArgument(GetAlgorithm().AlgorithmName() + ": IV length " + IntToString(size) + " exceeds the maximum of " + IntToString(MaxIVLength())); - else - return size; -} - -const byte * SimpleKeyingInterface::GetIVAndThrowIfInvalid(const NameValuePairs ¶ms, size_t &size) -{ - ConstByteArrayParameter ivWithLength; - const byte *iv; - bool found = false; - - try {found = params.GetValue(Name::IV(), ivWithLength);} - catch (const NameValuePairs::ValueTypeMismatch &) {} - - if (found) - { - iv = ivWithLength.begin(); - ThrowIfInvalidIV(iv); - size = ThrowIfInvalidIVLength((int)ivWithLength.size()); - return iv; - } - else if (params.GetValue(Name::IV(), iv)) - { - ThrowIfInvalidIV(iv); - size = IVSize(); - return iv; - } - else - { - ThrowIfResynchronizable(); - size = 0; - return NULL; - } -} - -void SimpleKeyingInterface::GetNextIV(RandomNumberGenerator &rng, byte *IV) -{ - rng.GenerateBlock(IV, IVSize()); -} - -size_t BlockTransformation::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const -{ - size_t blockSize = BlockSize(); - size_t inIncrement = (flags & (BT_InBlockIsCounter|BT_DontIncrementInOutPointers)) ? 0 : blockSize; - size_t xorIncrement = xorBlocks ? blockSize : 0; - size_t outIncrement = (flags & BT_DontIncrementInOutPointers) ? 0 : blockSize; - - if (flags & BT_ReverseDirection) - { - assert(length % blockSize == 0); - inBlocks += length - blockSize; - xorBlocks += length - blockSize; - outBlocks += length - blockSize; - inIncrement = 0-inIncrement; - xorIncrement = 0-xorIncrement; - outIncrement = 0-outIncrement; - } - - while (length >= blockSize) - { - if (flags & BT_XorInput) - { - xorbuf(outBlocks, xorBlocks, inBlocks, blockSize); - ProcessBlock(outBlocks); - } - else - ProcessAndXorBlock(inBlocks, xorBlocks, outBlocks); - if (flags & BT_InBlockIsCounter) - const_cast(inBlocks)[blockSize-1]++; - inBlocks += inIncrement; - outBlocks += outIncrement; - xorBlocks += xorIncrement; - length -= blockSize; - } - - return length; -} - -unsigned int BlockTransformation::OptimalDataAlignment() const -{ - return GetAlignmentOf(); -} - -unsigned int StreamTransformation::OptimalDataAlignment() const -{ - return GetAlignmentOf(); -} - -unsigned int HashTransformation::OptimalDataAlignment() const -{ - return GetAlignmentOf(); -} - -void StreamTransformation::ProcessLastBlock(byte *outString, const byte *inString, size_t length) -{ - assert(MinLastBlockSize() == 0); // this function should be overriden otherwise - - if (length == MandatoryBlockSize()) - ProcessData(outString, inString, length); - else if (length != 0) - throw NotImplemented(AlgorithmName() + ": this object does't support a special last block"); -} - -void AuthenticatedSymmetricCipher::SpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength) -{ - if (headerLength > MaxHeaderLength()) - throw InvalidArgument(GetAlgorithm().AlgorithmName() + ": header length " + IntToString(headerLength) + " exceeds the maximum of " + IntToString(MaxHeaderLength())); - - if (messageLength > MaxMessageLength()) - throw InvalidArgument(GetAlgorithm().AlgorithmName() + ": message length " + IntToString(messageLength) + " exceeds the maximum of " + IntToString(MaxMessageLength())); - - if (footerLength > MaxFooterLength()) - throw InvalidArgument(GetAlgorithm().AlgorithmName() + ": footer length " + IntToString(footerLength) + " exceeds the maximum of " + IntToString(MaxFooterLength())); - - UncheckedSpecifyDataLengths(headerLength, messageLength, footerLength); -} - -void AuthenticatedSymmetricCipher::EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *message, size_t messageLength) -{ - Resynchronize(iv, ivLength); - SpecifyDataLengths(headerLength, messageLength); - Update(header, headerLength); - ProcessString(ciphertext, message, messageLength); - TruncatedFinal(mac, macSize); -} - -bool AuthenticatedSymmetricCipher::DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *ciphertext, size_t ciphertextLength) -{ - Resynchronize(iv, ivLength); - SpecifyDataLengths(headerLength, ciphertextLength); - Update(header, headerLength); - ProcessString(message, ciphertext, ciphertextLength); - return TruncatedVerify(mac, macLength); -} - -unsigned int RandomNumberGenerator::GenerateBit() -{ - return GenerateByte() & 1; -} - -byte RandomNumberGenerator::GenerateByte() -{ - byte b; - GenerateBlock(&b, 1); - return b; -} - -word32 RandomNumberGenerator::GenerateWord32(word32 min, word32 max) -{ - word32 range = max-min; - const int maxBits = BitPrecision(range); - - word32 value; - - do - { - GenerateBlock((byte *)&value, sizeof(value)); - value = Crop(value, maxBits); - } while (value > range); - - return value+min; -} - -void RandomNumberGenerator::GenerateBlock(byte *output, size_t size) -{ - ArraySink s(output, size); - GenerateIntoBufferedTransformation(s, DEFAULT_CHANNEL, size); -} - -void RandomNumberGenerator::DiscardBytes(size_t n) -{ - GenerateIntoBufferedTransformation(TheBitBucket(), DEFAULT_CHANNEL, n); -} - -void RandomNumberGenerator::GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword length) -{ - FixedSizeSecBlock buffer; - while (length) - { - size_t len = UnsignedMin(buffer.size(), length); - GenerateBlock(buffer, len); - target.ChannelPut(channel, buffer, len); - length -= len; - } -} - -//! see NullRNG() -class ClassNullRNG : public RandomNumberGenerator -{ -public: - std::string AlgorithmName() const {return "NullRNG";} - void GenerateBlock(byte *output, size_t size) {throw NotImplemented("NullRNG: NullRNG should only be passed to functions that don't need to generate random bytes");} -}; - -RandomNumberGenerator & NullRNG() -{ - static ClassNullRNG s_nullRNG; - return s_nullRNG; -} - -bool HashTransformation::TruncatedVerify(const byte *digestIn, size_t digestLength) -{ - ThrowIfInvalidTruncatedSize(digestLength); - SecByteBlock digest(digestLength); - TruncatedFinal(digest, digestLength); - return VerifyBufsEqual(digest, digestIn, digestLength); -} - -void HashTransformation::ThrowIfInvalidTruncatedSize(size_t size) const -{ - if (size > DigestSize()) - throw InvalidArgument("HashTransformation: can't truncate a " + IntToString(DigestSize()) + " byte digest to " + IntToString(size) + " bytes"); -} - -unsigned int BufferedTransformation::GetMaxWaitObjectCount() const -{ - const BufferedTransformation *t = AttachedTransformation(); - return t ? t->GetMaxWaitObjectCount() : 0; -} - -void BufferedTransformation::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) -{ - BufferedTransformation *t = AttachedTransformation(); - if (t) - t->GetWaitObjects(container, callStack); // reduce clutter by not adding to stack here -} - -void BufferedTransformation::Initialize(const NameValuePairs ¶meters, int propagation) -{ - assert(!AttachedTransformation()); - IsolatedInitialize(parameters); -} - -bool BufferedTransformation::Flush(bool hardFlush, int propagation, bool blocking) -{ - assert(!AttachedTransformation()); - return IsolatedFlush(hardFlush, blocking); -} - -bool BufferedTransformation::MessageSeriesEnd(int propagation, bool blocking) -{ - assert(!AttachedTransformation()); - return IsolatedMessageSeriesEnd(blocking); -} - -byte * BufferedTransformation::ChannelCreatePutSpace(const std::string &channel, size_t &size) -{ - if (channel.empty()) - return CreatePutSpace(size); - else - throw NoChannelSupport(AlgorithmName()); -} - -size_t BufferedTransformation::ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking) -{ - if (channel.empty()) - return Put2(begin, length, messageEnd, blocking); - else - throw NoChannelSupport(AlgorithmName()); -} - -size_t BufferedTransformation::ChannelPutModifiable2(const std::string &channel, byte *begin, size_t length, int messageEnd, bool blocking) -{ - if (channel.empty()) - return PutModifiable2(begin, length, messageEnd, blocking); - else - return ChannelPut2(channel, begin, length, messageEnd, blocking); -} - -bool BufferedTransformation::ChannelFlush(const std::string &channel, bool completeFlush, int propagation, bool blocking) -{ - if (channel.empty()) - return Flush(completeFlush, propagation, blocking); - else - throw NoChannelSupport(AlgorithmName()); -} - -bool BufferedTransformation::ChannelMessageSeriesEnd(const std::string &channel, int propagation, bool blocking) -{ - if (channel.empty()) - return MessageSeriesEnd(propagation, blocking); - else - throw NoChannelSupport(AlgorithmName()); -} - -lword BufferedTransformation::MaxRetrievable() const -{ - if (AttachedTransformation()) - return AttachedTransformation()->MaxRetrievable(); - else - return CopyTo(TheBitBucket()); -} - -bool BufferedTransformation::AnyRetrievable() const -{ - if (AttachedTransformation()) - return AttachedTransformation()->AnyRetrievable(); - else - { - byte b; - return Peek(b) != 0; - } -} - -size_t BufferedTransformation::Get(byte &outByte) -{ - if (AttachedTransformation()) - return AttachedTransformation()->Get(outByte); - else - return Get(&outByte, 1); -} - -size_t BufferedTransformation::Get(byte *outString, size_t getMax) -{ - if (AttachedTransformation()) - return AttachedTransformation()->Get(outString, getMax); - else - { - ArraySink arraySink(outString, getMax); - return (size_t)TransferTo(arraySink, getMax); - } -} - -size_t BufferedTransformation::Peek(byte &outByte) const -{ - if (AttachedTransformation()) - return AttachedTransformation()->Peek(outByte); - else - return Peek(&outByte, 1); -} - -size_t BufferedTransformation::Peek(byte *outString, size_t peekMax) const -{ - if (AttachedTransformation()) - return AttachedTransformation()->Peek(outString, peekMax); - else - { - ArraySink arraySink(outString, peekMax); - return (size_t)CopyTo(arraySink, peekMax); - } -} - -lword BufferedTransformation::Skip(lword skipMax) -{ - if (AttachedTransformation()) - return AttachedTransformation()->Skip(skipMax); - else - return TransferTo(TheBitBucket(), skipMax); -} - -lword BufferedTransformation::TotalBytesRetrievable() const -{ - if (AttachedTransformation()) - return AttachedTransformation()->TotalBytesRetrievable(); - else - return MaxRetrievable(); -} - -unsigned int BufferedTransformation::NumberOfMessages() const -{ - if (AttachedTransformation()) - return AttachedTransformation()->NumberOfMessages(); - else - return CopyMessagesTo(TheBitBucket()); -} - -bool BufferedTransformation::AnyMessages() const -{ - if (AttachedTransformation()) - return AttachedTransformation()->AnyMessages(); - else - return NumberOfMessages() != 0; -} - -bool BufferedTransformation::GetNextMessage() -{ - if (AttachedTransformation()) - return AttachedTransformation()->GetNextMessage(); - else - { - assert(!AnyMessages()); - return false; - } -} - -unsigned int BufferedTransformation::SkipMessages(unsigned int count) -{ - if (AttachedTransformation()) - return AttachedTransformation()->SkipMessages(count); - else - return TransferMessagesTo(TheBitBucket(), count); -} - -size_t BufferedTransformation::TransferMessagesTo2(BufferedTransformation &target, unsigned int &messageCount, const std::string &channel, bool blocking) -{ - if (AttachedTransformation()) - return AttachedTransformation()->TransferMessagesTo2(target, messageCount, channel, blocking); - else - { - unsigned int maxMessages = messageCount; - for (messageCount=0; messageCount < maxMessages && AnyMessages(); messageCount++) - { - size_t blockedBytes; - lword transferredBytes; - - while (AnyRetrievable()) - { - transferredBytes = LWORD_MAX; - blockedBytes = TransferTo2(target, transferredBytes, channel, blocking); - if (blockedBytes > 0) - return blockedBytes; - } - - if (target.ChannelMessageEnd(channel, GetAutoSignalPropagation(), blocking)) - return 1; - - bool result = GetNextMessage(); - assert(result); - } - return 0; - } -} - -unsigned int BufferedTransformation::CopyMessagesTo(BufferedTransformation &target, unsigned int count, const std::string &channel) const -{ - if (AttachedTransformation()) - return AttachedTransformation()->CopyMessagesTo(target, count, channel); - else - return 0; -} - -void BufferedTransformation::SkipAll() -{ - if (AttachedTransformation()) - AttachedTransformation()->SkipAll(); - else - { - while (SkipMessages()) {} - while (Skip()) {} - } -} - -size_t BufferedTransformation::TransferAllTo2(BufferedTransformation &target, const std::string &channel, bool blocking) -{ - if (AttachedTransformation()) - return AttachedTransformation()->TransferAllTo2(target, channel, blocking); - else - { - assert(!NumberOfMessageSeries()); - - unsigned int messageCount; - do - { - messageCount = UINT_MAX; - size_t blockedBytes = TransferMessagesTo2(target, messageCount, channel, blocking); - if (blockedBytes) - return blockedBytes; - } - while (messageCount != 0); - - lword byteCount; - do - { - byteCount = ULONG_MAX; - size_t blockedBytes = TransferTo2(target, byteCount, channel, blocking); - if (blockedBytes) - return blockedBytes; - } - while (byteCount != 0); - - return 0; - } -} - -void BufferedTransformation::CopyAllTo(BufferedTransformation &target, const std::string &channel) const -{ - if (AttachedTransformation()) - AttachedTransformation()->CopyAllTo(target, channel); - else - { - assert(!NumberOfMessageSeries()); - while (CopyMessagesTo(target, UINT_MAX, channel)) {} - } -} - -void BufferedTransformation::SetRetrievalChannel(const std::string &channel) -{ - if (AttachedTransformation()) - AttachedTransformation()->SetRetrievalChannel(channel); -} - -size_t BufferedTransformation::ChannelPutWord16(const std::string &channel, word16 value, ByteOrder order, bool blocking) -{ - PutWord(false, order, m_buf, value); - return ChannelPut(channel, m_buf, 2, blocking); -} - -size_t BufferedTransformation::ChannelPutWord32(const std::string &channel, word32 value, ByteOrder order, bool blocking) -{ - PutWord(false, order, m_buf, value); - return ChannelPut(channel, m_buf, 4, blocking); -} - -size_t BufferedTransformation::PutWord16(word16 value, ByteOrder order, bool blocking) -{ - return ChannelPutWord16(DEFAULT_CHANNEL, value, order, blocking); -} - -size_t BufferedTransformation::PutWord32(word32 value, ByteOrder order, bool blocking) -{ - return ChannelPutWord32(DEFAULT_CHANNEL, value, order, blocking); -} - -size_t BufferedTransformation::PeekWord16(word16 &value, ByteOrder order) const -{ - byte buf[2] = {0, 0}; - size_t len = Peek(buf, 2); - - if (order) - value = (buf[0] << 8) | buf[1]; - else - value = (buf[1] << 8) | buf[0]; - - return len; -} - -size_t BufferedTransformation::PeekWord32(word32 &value, ByteOrder order) const -{ - byte buf[4] = {0, 0, 0, 0}; - size_t len = Peek(buf, 4); - - if (order) - value = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf [3]; - else - value = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf [0]; - - return len; -} - -size_t BufferedTransformation::GetWord16(word16 &value, ByteOrder order) -{ - return (size_t)Skip(PeekWord16(value, order)); -} - -size_t BufferedTransformation::GetWord32(word32 &value, ByteOrder order) -{ - return (size_t)Skip(PeekWord32(value, order)); -} - -void BufferedTransformation::Attach(BufferedTransformation *newOut) -{ - if (AttachedTransformation() && AttachedTransformation()->Attachable()) - AttachedTransformation()->Attach(newOut); - else - Detach(newOut); -} - -void GeneratableCryptoMaterial::GenerateRandomWithKeySize(RandomNumberGenerator &rng, unsigned int keySize) -{ - GenerateRandom(rng, MakeParameters("KeySize", (int)keySize)); -} - -class PK_DefaultEncryptionFilter : public Unflushable -{ -public: - PK_DefaultEncryptionFilter(RandomNumberGenerator &rng, const PK_Encryptor &encryptor, BufferedTransformation *attachment, const NameValuePairs ¶meters) - : m_rng(rng), m_encryptor(encryptor), m_parameters(parameters) - { - Detach(attachment); - } - - size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking) - { - FILTER_BEGIN; - m_plaintextQueue.Put(inString, length); - - if (messageEnd) - { - { - size_t plaintextLength; - if (!SafeConvert(m_plaintextQueue.CurrentSize(), plaintextLength)) - throw InvalidArgument("PK_DefaultEncryptionFilter: plaintext too long"); - size_t ciphertextLength = m_encryptor.CiphertextLength(plaintextLength); - - SecByteBlock plaintext(plaintextLength); - m_plaintextQueue.Get(plaintext, plaintextLength); - m_ciphertext.resize(ciphertextLength); - m_encryptor.Encrypt(m_rng, plaintext, plaintextLength, m_ciphertext, m_parameters); - } - - FILTER_OUTPUT(1, m_ciphertext, m_ciphertext.size(), messageEnd); - } - FILTER_END_NO_MESSAGE_END; - } - - RandomNumberGenerator &m_rng; - const PK_Encryptor &m_encryptor; - const NameValuePairs &m_parameters; - ByteQueue m_plaintextQueue; - SecByteBlock m_ciphertext; -}; - -BufferedTransformation * PK_Encryptor::CreateEncryptionFilter(RandomNumberGenerator &rng, BufferedTransformation *attachment, const NameValuePairs ¶meters) const -{ - return new PK_DefaultEncryptionFilter(rng, *this, attachment, parameters); -} - -class PK_DefaultDecryptionFilter : public Unflushable -{ -public: - PK_DefaultDecryptionFilter(RandomNumberGenerator &rng, const PK_Decryptor &decryptor, BufferedTransformation *attachment, const NameValuePairs ¶meters) - : m_rng(rng), m_decryptor(decryptor), m_parameters(parameters) - { - Detach(attachment); - } - - size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking) - { - FILTER_BEGIN; - m_ciphertextQueue.Put(inString, length); - - if (messageEnd) - { - { - size_t ciphertextLength; - if (!SafeConvert(m_ciphertextQueue.CurrentSize(), ciphertextLength)) - throw InvalidArgument("PK_DefaultDecryptionFilter: ciphertext too long"); - size_t maxPlaintextLength = m_decryptor.MaxPlaintextLength(ciphertextLength); - - SecByteBlock ciphertext(ciphertextLength); - m_ciphertextQueue.Get(ciphertext, ciphertextLength); - m_plaintext.resize(maxPlaintextLength); - m_result = m_decryptor.Decrypt(m_rng, ciphertext, ciphertextLength, m_plaintext, m_parameters); - if (!m_result.isValidCoding) - throw InvalidCiphertext(m_decryptor.AlgorithmName() + ": invalid ciphertext"); - } - - FILTER_OUTPUT(1, m_plaintext, m_result.messageLength, messageEnd); - } - FILTER_END_NO_MESSAGE_END; - } - - RandomNumberGenerator &m_rng; - const PK_Decryptor &m_decryptor; - const NameValuePairs &m_parameters; - ByteQueue m_ciphertextQueue; - SecByteBlock m_plaintext; - DecodingResult m_result; -}; - -BufferedTransformation * PK_Decryptor::CreateDecryptionFilter(RandomNumberGenerator &rng, BufferedTransformation *attachment, const NameValuePairs ¶meters) const -{ - return new PK_DefaultDecryptionFilter(rng, *this, attachment, parameters); -} - -size_t PK_Signer::Sign(RandomNumberGenerator &rng, PK_MessageAccumulator *messageAccumulator, byte *signature) const -{ - std::auto_ptr m(messageAccumulator); - return SignAndRestart(rng, *m, signature, false); -} - -size_t PK_Signer::SignMessage(RandomNumberGenerator &rng, const byte *message, size_t messageLen, byte *signature) const -{ - std::auto_ptr m(NewSignatureAccumulator(rng)); - m->Update(message, messageLen); - return SignAndRestart(rng, *m, signature, false); -} - -size_t PK_Signer::SignMessageWithRecovery(RandomNumberGenerator &rng, const byte *recoverableMessage, size_t recoverableMessageLength, - const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, byte *signature) const -{ - std::auto_ptr m(NewSignatureAccumulator(rng)); - InputRecoverableMessage(*m, recoverableMessage, recoverableMessageLength); - m->Update(nonrecoverableMessage, nonrecoverableMessageLength); - return SignAndRestart(rng, *m, signature, false); -} - -bool PK_Verifier::Verify(PK_MessageAccumulator *messageAccumulator) const -{ - std::auto_ptr m(messageAccumulator); - return VerifyAndRestart(*m); -} - -bool PK_Verifier::VerifyMessage(const byte *message, size_t messageLen, const byte *signature, size_t signatureLength) const -{ - std::auto_ptr m(NewVerificationAccumulator()); - InputSignature(*m, signature, signatureLength); - m->Update(message, messageLen); - return VerifyAndRestart(*m); -} - -DecodingResult PK_Verifier::Recover(byte *recoveredMessage, PK_MessageAccumulator *messageAccumulator) const -{ - std::auto_ptr m(messageAccumulator); - return RecoverAndRestart(recoveredMessage, *m); -} - -DecodingResult PK_Verifier::RecoverMessage(byte *recoveredMessage, - const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, - const byte *signature, size_t signatureLength) const -{ - std::auto_ptr m(NewVerificationAccumulator()); - InputSignature(*m, signature, signatureLength); - m->Update(nonrecoverableMessage, nonrecoverableMessageLength); - return RecoverAndRestart(recoveredMessage, *m); -} - -void SimpleKeyAgreementDomain::GenerateKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const -{ - GeneratePrivateKey(rng, privateKey); - GeneratePublicKey(rng, privateKey, publicKey); -} - -void AuthenticatedKeyAgreementDomain::GenerateStaticKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const -{ - GenerateStaticPrivateKey(rng, privateKey); - GenerateStaticPublicKey(rng, privateKey, publicKey); -} - -void AuthenticatedKeyAgreementDomain::GenerateEphemeralKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const -{ - GenerateEphemeralPrivateKey(rng, privateKey); - GenerateEphemeralPublicKey(rng, privateKey, publicKey); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/cryptlib.h b/CryptoPP/cryptlib.h deleted file mode 100644 index 406872232..000000000 --- a/CryptoPP/cryptlib.h +++ /dev/null @@ -1,1655 +0,0 @@ -// cryptlib.h - written and placed in the public domain by Wei Dai -/*! \file - This file contains the declarations for the abstract base - classes that provide a uniform interface to this library. -*/ - -/*! \mainpage Crypto++ Library 5.6.2 API Reference -
-
Abstract Base Classes
- cryptlib.h -
Authenticated Encryption
- AuthenticatedSymmetricCipherDocumentation -
Symmetric Ciphers
- SymmetricCipherDocumentation -
Hash Functions
- SHA1, SHA224, SHA256, SHA384, SHA512, Tiger, Whirlpool, RIPEMD160, RIPEMD320, RIPEMD128, RIPEMD256, Weak1::MD2, Weak1::MD4, Weak1::MD5 -
Non-Cryptographic Checksums
- CRC32, Adler32 -
Message Authentication Codes
- VMAC, HMAC, CBC_MAC, CMAC, DMAC, TTMAC, GCM (GMAC) -
Random Number Generators
- NullRNG(), LC_RNG, RandomPool, BlockingRng, NonblockingRng, AutoSeededRandomPool, AutoSeededX917RNG, #DefaultAutoSeededRNG -
Password-based Cryptography
- PasswordBasedKeyDerivationFunction -
Public Key Cryptosystems
- DLIES, ECIES, LUCES, RSAES, RabinES, LUC_IES -
Public Key Signature Schemes
- DSA2, GDSA, ECDSA, NR, ECNR, LUCSS, RSASS, RSASS_ISO, RabinSS, RWSS, ESIGN -
Key Agreement
- #DH, DH2, #MQV, ECDH, ECMQV, XTR_DH -
Algebraic Structures
- Integer, PolynomialMod2, PolynomialOver, RingOfPolynomialsOver, - ModularArithmetic, MontgomeryRepresentation, GFP2_ONB, - GF2NP, GF256, GF2_32, EC2N, ECP -
Secret Sharing and Information Dispersal
- SecretSharing, SecretRecovery, InformationDispersal, InformationRecovery -
Compression
- Deflator, Inflator, Gzip, Gunzip, ZlibCompressor, ZlibDecompressor -
Input Source Classes
- StringSource, #ArraySource, FileSource, SocketSource, WindowsPipeSource, RandomNumberSource -
Output Sink Classes
- StringSinkTemplate, ArraySink, FileSink, SocketSink, WindowsPipeSink, RandomNumberSink -
Filter Wrappers
- StreamTransformationFilter, HashFilter, HashVerificationFilter, SignerFilter, SignatureVerificationFilter -
Binary to Text Encoders and Decoders
- HexEncoder, HexDecoder, Base64Encoder, Base64Decoder, Base32Encoder, Base32Decoder -
Wrappers for OS features
- Timer, Socket, WindowsHandle, ThreadLocalStorage, ThreadUserTimer -
FIPS 140 related
- fips140.h -
- -In the DLL version of Crypto++, only the following implementation class are available. -
-
Block Ciphers
- AES, DES_EDE2, DES_EDE3, SKIPJACK -
Cipher Modes (replace template parameter BC with one of the block ciphers above)
- ECB_Mode\, CTR_Mode\, CBC_Mode\, CFB_FIPS_Mode\, OFB_Mode\, GCM\ -
Hash Functions
- SHA1, SHA224, SHA256, SHA384, SHA512 -
Public Key Signature Schemes (replace template parameter H with one of the hash functions above)
- RSASS\, RSASS\, RSASS_ISO\, RWSS\, DSA, ECDSA\, ECDSA\ -
Message Authentication Codes (replace template parameter H with one of the hash functions above)
- HMAC\, CBC_MAC\, CBC_MAC\, GCM\ -
Random Number Generators
- #DefaultAutoSeededRNG (AutoSeededX917RNG\) -
Key Agreement
- #DH -
Public Key Cryptosystems
- RSAES\ \> -
- -

This reference manual is a work in progress. Some classes are still lacking detailed descriptions. -

Click here to download a zip archive containing this manual. -

Thanks to Ryan Phillips for providing the Doxygen configuration file -and getting me started with this manual. -*/ - -#ifndef CRYPTOPP_CRYPTLIB_H -#define CRYPTOPP_CRYPTLIB_H - -#include "config.h" -#include "stdcpp.h" - -NAMESPACE_BEGIN(CryptoPP) - -// forward declarations -class Integer; -class RandomNumberGenerator; -class BufferedTransformation; - -//! used to specify a direction for a cipher to operate in (encrypt or decrypt) -enum CipherDir {ENCRYPTION, DECRYPTION}; - -//! used to represent infinite time -const unsigned long INFINITE_TIME = ULONG_MAX; - -// VC60 workaround: using enums as template parameters causes problems -template -struct EnumToType -{ - static ENUM_TYPE ToEnum() {return (ENUM_TYPE)VALUE;} -}; - -enum ByteOrder {LITTLE_ENDIAN_ORDER = 0, BIG_ENDIAN_ORDER = 1}; -typedef EnumToType LittleEndian; -typedef EnumToType BigEndian; - -//! base class for all exceptions thrown by Crypto++ -class CRYPTOPP_DLL Exception : public std::exception -{ -public: - //! error types - enum ErrorType { - //! a method is not implemented - NOT_IMPLEMENTED, - //! invalid function argument - INVALID_ARGUMENT, - //! BufferedTransformation received a Flush(true) signal but can't flush buffers - CANNOT_FLUSH, - //! data integerity check (such as CRC or MAC) failed - DATA_INTEGRITY_CHECK_FAILED, - //! received input data that doesn't conform to expected format - INVALID_DATA_FORMAT, - //! error reading from input device or writing to output device - IO_ERROR, - //! some error not belong to any of the above categories - OTHER_ERROR - }; - - explicit Exception(ErrorType errorType, const std::string &s) : m_errorType(errorType), m_what(s) {} - virtual ~Exception() throw() {} - const char *what() const throw() {return (m_what.c_str());} - const std::string &GetWhat() const {return m_what;} - void SetWhat(const std::string &s) {m_what = s;} - ErrorType GetErrorType() const {return m_errorType;} - void SetErrorType(ErrorType errorType) {m_errorType = errorType;} - -private: - ErrorType m_errorType; - std::string m_what; -}; - -//! exception thrown when an invalid argument is detected -class CRYPTOPP_DLL InvalidArgument : public Exception -{ -public: - explicit InvalidArgument(const std::string &s) : Exception(INVALID_ARGUMENT, s) {} -}; - -//! exception thrown when input data is received that doesn't conform to expected format -class CRYPTOPP_DLL InvalidDataFormat : public Exception -{ -public: - explicit InvalidDataFormat(const std::string &s) : Exception(INVALID_DATA_FORMAT, s) {} -}; - -//! exception thrown by decryption filters when trying to decrypt an invalid ciphertext -class CRYPTOPP_DLL InvalidCiphertext : public InvalidDataFormat -{ -public: - explicit InvalidCiphertext(const std::string &s) : InvalidDataFormat(s) {} -}; - -//! exception thrown by a class if a non-implemented method is called -class CRYPTOPP_DLL NotImplemented : public Exception -{ -public: - explicit NotImplemented(const std::string &s) : Exception(NOT_IMPLEMENTED, s) {} -}; - -//! exception thrown by a class when Flush(true) is called but it can't completely flush its buffers -class CRYPTOPP_DLL CannotFlush : public Exception -{ -public: - explicit CannotFlush(const std::string &s) : Exception(CANNOT_FLUSH, s) {} -}; - -//! error reported by the operating system -class CRYPTOPP_DLL OS_Error : public Exception -{ -public: - OS_Error(ErrorType errorType, const std::string &s, const std::string& operation, int errorCode) - : Exception(errorType, s), m_operation(operation), m_errorCode(errorCode) {} - ~OS_Error() throw() {} - - // the operating system API that reported the error - const std::string & GetOperation() const {return m_operation;} - // the error code return by the operating system - int GetErrorCode() const {return m_errorCode;} - -protected: - std::string m_operation; - int m_errorCode; -}; - -//! used to return decoding results -struct CRYPTOPP_DLL DecodingResult -{ - explicit DecodingResult() : isValidCoding(false), messageLength(0) {} - explicit DecodingResult(size_t len) : isValidCoding(true), messageLength(len) {} - - bool operator==(const DecodingResult &rhs) const {return isValidCoding == rhs.isValidCoding && messageLength == rhs.messageLength;} - bool operator!=(const DecodingResult &rhs) const {return !operator==(rhs);} - - bool isValidCoding; - size_t messageLength; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - operator size_t() const {return isValidCoding ? messageLength : 0;} -#endif -}; - -//! interface for retrieving values given their names -/*! \note This class is used to safely pass a variable number of arbitrarily typed arguments to functions - and to read values from keys and crypto parameters. - \note To obtain an object that implements NameValuePairs for the purpose of parameter - passing, use the MakeParameters() function. - \note To get a value from NameValuePairs, you need to know the name and the type of the value. - Call GetValueNames() on a NameValuePairs object to obtain a list of value names that it supports. - Then look at the Name namespace documentation to see what the type of each value is, or - alternatively, call GetIntValue() with the value name, and if the type is not int, a - ValueTypeMismatch exception will be thrown and you can get the actual type from the exception object. -*/ -class CRYPTOPP_NO_VTABLE NameValuePairs -{ -public: - virtual ~NameValuePairs() {} - - //! exception thrown when trying to retrieve a value using a different type than expected - class CRYPTOPP_DLL ValueTypeMismatch : public InvalidArgument - { - public: - ValueTypeMismatch(const std::string &name, const std::type_info &stored, const std::type_info &retrieving) - : InvalidArgument("NameValuePairs: type mismatch for '" + name + "', stored '" + stored.name() + "', trying to retrieve '" + retrieving.name() + "'") - , m_stored(stored), m_retrieving(retrieving) {} - - const std::type_info & GetStoredTypeInfo() const {return m_stored;} - const std::type_info & GetRetrievingTypeInfo() const {return m_retrieving;} - - private: - const std::type_info &m_stored; - const std::type_info &m_retrieving; - }; - - //! get a copy of this object or a subobject of it - template - bool GetThisObject(T &object) const - { - return GetValue((std::string("ThisObject:")+typeid(T).name()).c_str(), object); - } - - //! get a pointer to this object, as a pointer to T - template - bool GetThisPointer(T *&p) const - { - return GetValue((std::string("ThisPointer:")+typeid(T).name()).c_str(), p); - } - - //! get a named value, returns true if the name exists - template - bool GetValue(const char *name, T &value) const - { - return GetVoidValue(name, typeid(T), &value); - } - - //! get a named value, returns the default if the name doesn't exist - template - T GetValueWithDefault(const char *name, T defaultValue) const - { - GetValue(name, defaultValue); - return defaultValue; - } - - //! get a list of value names that can be retrieved - CRYPTOPP_DLL std::string GetValueNames() const - {std::string result; GetValue("ValueNames", result); return result;} - - //! get a named value with type int - /*! used to ensure we don't accidentally try to get an unsigned int - or some other type when we mean int (which is the most common case) */ - CRYPTOPP_DLL bool GetIntValue(const char *name, int &value) const - {return GetValue(name, value);} - - //! get a named value with type int, with default - CRYPTOPP_DLL int GetIntValueWithDefault(const char *name, int defaultValue) const - {return GetValueWithDefault(name, defaultValue);} - - //! used by derived classes to check for type mismatch - CRYPTOPP_DLL static void CRYPTOPP_API ThrowIfTypeMismatch(const char *name, const std::type_info &stored, const std::type_info &retrieving) - {if (stored != retrieving) throw ValueTypeMismatch(name, stored, retrieving);} - - template - void GetRequiredParameter(const char *className, const char *name, T &value) const - { - if (!GetValue(name, value)) - throw InvalidArgument(std::string(className) + ": missing required parameter '" + name + "'"); - } - - CRYPTOPP_DLL void GetRequiredIntParameter(const char *className, const char *name, int &value) const - { - if (!GetIntValue(name, value)) - throw InvalidArgument(std::string(className) + ": missing required parameter '" + name + "'"); - } - - //! to be implemented by derived classes, users should use one of the above functions instead - CRYPTOPP_DLL virtual bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const =0; -}; - -//! namespace containing value name definitions -/*! value names, types and semantics: - - ThisObject:ClassName (ClassName, copy of this object or a subobject) - ThisPointer:ClassName (const ClassName *, pointer to this object or a subobject) -*/ -DOCUMENTED_NAMESPACE_BEGIN(Name) -// more names defined in argnames.h -DOCUMENTED_NAMESPACE_END - -//! empty set of name-value pairs -extern CRYPTOPP_DLL const NameValuePairs &g_nullNameValuePairs; - -// ******************************************************** - -//! interface for cloning objects, this is not implemented by most classes yet -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Clonable -{ -public: - virtual ~Clonable() {} - //! this is not implemented by most classes yet - virtual Clonable* Clone() const {throw NotImplemented("Clone() is not implemented yet.");} // TODO: make this =0 -}; - -//! interface for all crypto algorithms - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Algorithm : public Clonable -{ -public: - /*! When FIPS 140-2 compliance is enabled and checkSelfTestStatus == true, - this constructor throws SelfTestFailure if the self test hasn't been run or fails. */ - Algorithm(bool checkSelfTestStatus = true); - //! returns name of this algorithm, not universally implemented yet - virtual std::string AlgorithmName() const {return "unknown";} -}; - -//! keying interface for crypto algorithms that take byte strings as keys -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SimpleKeyingInterface -{ -public: - virtual ~SimpleKeyingInterface() {} - - //! returns smallest valid key length in bytes */ - virtual size_t MinKeyLength() const =0; - //! returns largest valid key length in bytes */ - virtual size_t MaxKeyLength() const =0; - //! returns default (recommended) key length in bytes */ - virtual size_t DefaultKeyLength() const =0; - - //! returns the smallest valid key length in bytes that is >= min(n, GetMaxKeyLength()) - virtual size_t GetValidKeyLength(size_t n) const =0; - - //! returns whether n is a valid key length - virtual bool IsValidKeyLength(size_t n) const - {return n == GetValidKeyLength(n);} - - //! set or reset the key of this object - /*! \param params is used to specify Rounds, BlockSize, etc. */ - virtual void SetKey(const byte *key, size_t length, const NameValuePairs ¶ms = g_nullNameValuePairs); - - //! calls SetKey() with an NameValuePairs object that just specifies "Rounds" - void SetKeyWithRounds(const byte *key, size_t length, int rounds); - - //! calls SetKey() with an NameValuePairs object that just specifies "IV" - void SetKeyWithIV(const byte *key, size_t length, const byte *iv, size_t ivLength); - - //! calls SetKey() with an NameValuePairs object that just specifies "IV" - void SetKeyWithIV(const byte *key, size_t length, const byte *iv) - {SetKeyWithIV(key, length, iv, IVSize());} - - enum IV_Requirement {UNIQUE_IV = 0, RANDOM_IV, UNPREDICTABLE_RANDOM_IV, INTERNALLY_GENERATED_IV, NOT_RESYNCHRONIZABLE}; - //! returns the minimal requirement for secure IVs - virtual IV_Requirement IVRequirement() const =0; - - //! returns whether this object can be resynchronized (i.e. supports initialization vectors) - /*! If this function returns true, and no IV is passed to SetKey() and CanUseStructuredIVs()==true, an IV of all 0's will be assumed. */ - bool IsResynchronizable() const {return IVRequirement() < NOT_RESYNCHRONIZABLE;} - //! returns whether this object can use random IVs (in addition to ones returned by GetNextIV) - bool CanUseRandomIVs() const {return IVRequirement() <= UNPREDICTABLE_RANDOM_IV;} - //! returns whether this object can use random but possibly predictable IVs (in addition to ones returned by GetNextIV) - bool CanUsePredictableIVs() const {return IVRequirement() <= RANDOM_IV;} - //! returns whether this object can use structured IVs, for example a counter (in addition to ones returned by GetNextIV) - bool CanUseStructuredIVs() const {return IVRequirement() <= UNIQUE_IV;} - - virtual unsigned int IVSize() const {throw NotImplemented(GetAlgorithm().AlgorithmName() + ": this object doesn't support resynchronization");} - //! returns default length of IVs accepted by this object - unsigned int DefaultIVLength() const {return IVSize();} - //! returns minimal length of IVs accepted by this object - virtual unsigned int MinIVLength() const {return IVSize();} - //! returns maximal length of IVs accepted by this object - virtual unsigned int MaxIVLength() const {return IVSize();} - //! resynchronize with an IV. ivLength=-1 means use IVSize() - virtual void Resynchronize(const byte *iv, int ivLength=-1) {throw NotImplemented(GetAlgorithm().AlgorithmName() + ": this object doesn't support resynchronization");} - //! get a secure IV for the next message - /*! This method should be called after you finish encrypting one message and are ready to start the next one. - After calling it, you must call SetKey() or Resynchronize() before using this object again. - This method is not implemented on decryption objects. */ - virtual void GetNextIV(RandomNumberGenerator &rng, byte *IV); - -protected: - virtual const Algorithm & GetAlgorithm() const =0; - virtual void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) =0; - - void ThrowIfInvalidKeyLength(size_t length); - void ThrowIfResynchronizable(); // to be called when no IV is passed - void ThrowIfInvalidIV(const byte *iv); // check for NULL IV if it can't be used - size_t ThrowIfInvalidIVLength(int size); - const byte * GetIVAndThrowIfInvalid(const NameValuePairs ¶ms, size_t &size); - inline void AssertValidKeyLength(size_t length) const - {assert(IsValidKeyLength(length));} -}; - -//! interface for the data processing part of block ciphers - -/*! Classes derived from BlockTransformation are block ciphers - in ECB mode (for example the DES::Encryption class), which are stateless. - These classes should not be used directly, but only in combination with - a mode class (see CipherModeDocumentation in modes.h). -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BlockTransformation : public Algorithm -{ -public: - //! encrypt or decrypt inBlock, xor with xorBlock, and write to outBlock - virtual void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const =0; - - //! encrypt or decrypt one block - /*! \pre size of inBlock and outBlock == BlockSize() */ - void ProcessBlock(const byte *inBlock, byte *outBlock) const - {ProcessAndXorBlock(inBlock, NULL, outBlock);} - - //! encrypt or decrypt one block in place - void ProcessBlock(byte *inoutBlock) const - {ProcessAndXorBlock(inoutBlock, NULL, inoutBlock);} - - //! block size of the cipher in bytes - virtual unsigned int BlockSize() const =0; - - //! returns how inputs and outputs should be aligned for optimal performance - virtual unsigned int OptimalDataAlignment() const; - - //! returns true if this is a permutation (i.e. there is an inverse transformation) - virtual bool IsPermutation() const {return true;} - - //! returns true if this is an encryption object - virtual bool IsForwardTransformation() const =0; - - //! return number of blocks that can be processed in parallel, for bit-slicing implementations - virtual unsigned int OptimalNumberOfParallelBlocks() const {return 1;} - - enum {BT_InBlockIsCounter=1, BT_DontIncrementInOutPointers=2, BT_XorInput=4, BT_ReverseDirection=8, BT_AllowParallel=16} FlagsForAdvancedProcessBlocks; - - //! encrypt and xor blocks according to flags (see FlagsForAdvancedProcessBlocks) - /*! /note If BT_InBlockIsCounter is set, last byte of inBlocks may be modified. */ - virtual size_t AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const; - - inline CipherDir GetCipherDirection() const {return IsForwardTransformation() ? ENCRYPTION : DECRYPTION;} -}; - -//! interface for the data processing part of stream ciphers - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE StreamTransformation : public Algorithm -{ -public: - //! return a reference to this object, useful for passing a temporary object to a function that takes a non-const reference - StreamTransformation& Ref() {return *this;} - - //! returns block size, if input must be processed in blocks, otherwise 1 - virtual unsigned int MandatoryBlockSize() const {return 1;} - - //! returns the input block size that is most efficient for this cipher - /*! \note optimal input length is n * OptimalBlockSize() - GetOptimalBlockSizeUsed() for any n > 0 */ - virtual unsigned int OptimalBlockSize() const {return MandatoryBlockSize();} - //! returns how much of the current block is used up - virtual unsigned int GetOptimalBlockSizeUsed() const {return 0;} - - //! returns how input should be aligned for optimal performance - virtual unsigned int OptimalDataAlignment() const; - - //! encrypt or decrypt an array of bytes of specified length - /*! \note either inString == outString, or they don't overlap */ - virtual void ProcessData(byte *outString, const byte *inString, size_t length) =0; - - //! for ciphers where the last block of data is special, encrypt or decrypt the last block of data - /*! For now the only use of this function is for CBC-CTS mode. */ - virtual void ProcessLastBlock(byte *outString, const byte *inString, size_t length); - //! returns the minimum size of the last block, 0 indicating the last block is not special - virtual unsigned int MinLastBlockSize() const {return 0;} - - //! same as ProcessData(inoutString, inoutString, length) - inline void ProcessString(byte *inoutString, size_t length) - {ProcessData(inoutString, inoutString, length);} - //! same as ProcessData(outString, inString, length) - inline void ProcessString(byte *outString, const byte *inString, size_t length) - {ProcessData(outString, inString, length);} - //! implemented as {ProcessData(&input, &input, 1); return input;} - inline byte ProcessByte(byte input) - {ProcessData(&input, &input, 1); return input;} - - //! returns whether this cipher supports random access - virtual bool IsRandomAccess() const =0; - //! for random access ciphers, seek to an absolute position - virtual void Seek(lword n) - { - assert(!IsRandomAccess()); - throw NotImplemented("StreamTransformation: this object doesn't support random access"); - } - - //! returns whether this transformation is self-inverting (e.g. xor with a keystream) - virtual bool IsSelfInverting() const =0; - //! returns whether this is an encryption object - virtual bool IsForwardTransformation() const =0; -}; - -//! interface for hash functions and data processing part of MACs - -/*! HashTransformation objects are stateful. They are created in an initial state, - change state as Update() is called, and return to the initial - state when Final() is called. This interface allows a large message to - be hashed in pieces by calling Update() on each piece followed by - calling Final(). -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE HashTransformation : public Algorithm -{ -public: - //! return a reference to this object, useful for passing a temporary object to a function that takes a non-const reference - HashTransformation& Ref() {return *this;} - - //! process more input - virtual void Update(const byte *input, size_t length) =0; - - //! request space to write input into - virtual byte * CreateUpdateSpace(size_t &size) {size=0; return NULL;} - - //! compute hash for current message, then restart for a new message - /*! \pre size of digest == DigestSize(). */ - virtual void Final(byte *digest) - {TruncatedFinal(digest, DigestSize());} - - //! discard the current state, and restart with a new message - virtual void Restart() - {TruncatedFinal(NULL, 0);} - - //! size of the hash/digest/MAC returned by Final() - virtual unsigned int DigestSize() const =0; - - //! same as DigestSize() - unsigned int TagSize() const {return DigestSize();} - - - //! block size of underlying compression function, or 0 if not block based - virtual unsigned int BlockSize() const {return 0;} - - //! input to Update() should have length a multiple of this for optimal speed - virtual unsigned int OptimalBlockSize() const {return 1;} - - //! returns how input should be aligned for optimal performance - virtual unsigned int OptimalDataAlignment() const; - - //! use this if your input is in one piece and you don't want to call Update() and Final() separately - virtual void CalculateDigest(byte *digest, const byte *input, size_t length) - {Update(input, length); Final(digest);} - - //! verify that digest is a valid digest for the current message, then reinitialize the object - /*! Default implementation is to call Final() and do a bitwise comparison - between its output and digest. */ - virtual bool Verify(const byte *digest) - {return TruncatedVerify(digest, DigestSize());} - - //! use this if your input is in one piece and you don't want to call Update() and Verify() separately - virtual bool VerifyDigest(const byte *digest, const byte *input, size_t length) - {Update(input, length); return Verify(digest);} - - //! truncated version of Final() - virtual void TruncatedFinal(byte *digest, size_t digestSize) =0; - - //! truncated version of CalculateDigest() - virtual void CalculateTruncatedDigest(byte *digest, size_t digestSize, const byte *input, size_t length) - {Update(input, length); TruncatedFinal(digest, digestSize);} - - //! truncated version of Verify() - virtual bool TruncatedVerify(const byte *digest, size_t digestLength); - - //! truncated version of VerifyDigest() - virtual bool VerifyTruncatedDigest(const byte *digest, size_t digestLength, const byte *input, size_t length) - {Update(input, length); return TruncatedVerify(digest, digestLength);} - -protected: - void ThrowIfInvalidTruncatedSize(size_t size) const; -}; - -typedef HashTransformation HashFunction; - -//! interface for one direction (encryption or decryption) of a block cipher -/*! \note These objects usually should not be used directly. See BlockTransformation for more details. */ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BlockCipher : public SimpleKeyingInterface, public BlockTransformation -{ -protected: - const Algorithm & GetAlgorithm() const {return *this;} -}; - -//! interface for one direction (encryption or decryption) of a stream cipher or cipher mode -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SymmetricCipher : public SimpleKeyingInterface, public StreamTransformation -{ -protected: - const Algorithm & GetAlgorithm() const {return *this;} -}; - -//! interface for message authentication codes -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE MessageAuthenticationCode : public SimpleKeyingInterface, public HashTransformation -{ -protected: - const Algorithm & GetAlgorithm() const {return *this;} -}; - -//! interface for for one direction (encryption or decryption) of a stream cipher or block cipher mode with authentication -/*! The StreamTransformation part of this interface is used to encrypt/decrypt the data, and the MessageAuthenticationCode part of this - interface is used to input additional authenticated data (AAD, which is MAC'ed but not encrypted), and to generate/verify the MAC. */ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedSymmetricCipher : public MessageAuthenticationCode, public StreamTransformation -{ -public: - //! this indicates that a member function was called in the wrong state, for example trying to encrypt a message before having set the key or IV - class BadState : public Exception - { - public: - explicit BadState(const std::string &name, const char *message) : Exception(OTHER_ERROR, name + ": " + message) {} - explicit BadState(const std::string &name, const char *function, const char *state) : Exception(OTHER_ERROR, name + ": " + function + " was called before " + state) {} - }; - - //! the maximum length of AAD that can be input before the encrypted data - virtual lword MaxHeaderLength() const =0; - //! the maximum length of encrypted data - virtual lword MaxMessageLength() const =0; - //! the maximum length of AAD that can be input after the encrypted data - virtual lword MaxFooterLength() const {return 0;} - //! if this function returns true, SpecifyDataLengths() must be called before attempting to input data - /*! This is the case for some schemes, such as CCM. */ - virtual bool NeedsPrespecifiedDataLengths() const {return false;} - //! this function only needs to be called if NeedsPrespecifiedDataLengths() returns true - void SpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength=0); - //! encrypt and generate MAC in one call. will truncate MAC if macSize < TagSize() - virtual void EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *message, size_t messageLength); - //! decrypt and verify MAC in one call, returning true iff MAC is valid. will assume MAC is truncated if macLength < TagSize() - virtual bool DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *ciphertext, size_t ciphertextLength); - - // redeclare this to avoid compiler ambiguity errors - virtual std::string AlgorithmName() const =0; - -protected: - const Algorithm & GetAlgorithm() const {return *static_cast(this);} - virtual void UncheckedSpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength) {} -}; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY -typedef SymmetricCipher StreamCipher; -#endif - -//! interface for random number generators -/*! All return values are uniformly distributed over the range specified. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE RandomNumberGenerator : public Algorithm -{ -public: - //! update RNG state with additional unpredictable values - virtual void IncorporateEntropy(const byte *input, size_t length) {throw NotImplemented("RandomNumberGenerator: IncorporateEntropy not implemented");} - - //! returns true if IncorporateEntropy is implemented - virtual bool CanIncorporateEntropy() const {return false;} - - //! generate new random byte and return it - virtual byte GenerateByte(); - - //! generate new random bit and return it - /*! Default implementation is to call GenerateByte() and return its lowest bit. */ - virtual unsigned int GenerateBit(); - - //! generate a random 32 bit word in the range min to max, inclusive - virtual word32 GenerateWord32(word32 a=0, word32 b=0xffffffffL); - - //! generate random array of bytes - virtual void GenerateBlock(byte *output, size_t size); - - //! generate and discard n bytes - virtual void DiscardBytes(size_t n); - - //! generate random bytes as input to a BufferedTransformation - virtual void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword length); - - //! randomly shuffle the specified array, resulting permutation is uniformly distributed - template void Shuffle(IT begin, IT end) - { - for (; begin != end; ++begin) - std::iter_swap(begin, begin + GenerateWord32(0, end-begin-1)); - } - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - byte GetByte() {return GenerateByte();} - unsigned int GetBit() {return GenerateBit();} - word32 GetLong(word32 a=0, word32 b=0xffffffffL) {return GenerateWord32(a, b);} - word16 GetShort(word16 a=0, word16 b=0xffff) {return (word16)GenerateWord32(a, b);} - void GetBlock(byte *output, size_t size) {GenerateBlock(output, size);} -#endif -}; - -//! returns a reference that can be passed to functions that ask for a RNG but doesn't actually use it -CRYPTOPP_DLL RandomNumberGenerator & CRYPTOPP_API NullRNG(); - -class WaitObjectContainer; -class CallStack; - -//! interface for objects that you can wait for - -class CRYPTOPP_NO_VTABLE Waitable -{ -public: - virtual ~Waitable() {} - - //! maximum number of wait objects that this object can return - virtual unsigned int GetMaxWaitObjectCount() const =0; - //! put wait objects into container - /*! \param callStack is used for tracing no wait loops, example: - something.GetWaitObjects(c, CallStack("my func after X", 0)); - - or in an outer GetWaitObjects() method that itself takes a callStack parameter: - innerThing.GetWaitObjects(c, CallStack("MyClass::GetWaitObjects at X", &callStack)); */ - virtual void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) =0; - //! wait on this object - /*! same as creating an empty container, calling GetWaitObjects(), and calling Wait() on the container */ - bool Wait(unsigned long milliseconds, CallStack const& callStack); -}; - -//! the default channel for BufferedTransformation, equal to the empty string -extern CRYPTOPP_DLL const std::string DEFAULT_CHANNEL; - -//! channel for additional authenticated data, equal to "AAD" -extern CRYPTOPP_DLL const std::string AAD_CHANNEL; - -//! interface for buffered transformations - -/*! BufferedTransformation is a generalization of BlockTransformation, - StreamTransformation, and HashTransformation. - - A buffered transformation is an object that takes a stream of bytes - as input (this may be done in stages), does some computation on them, and - then places the result into an internal buffer for later retrieval. Any - partial result already in the output buffer is not modified by further - input. - - If a method takes a "blocking" parameter, and you - pass "false" for it, the method will return before all input has been processed if - the input cannot be processed without waiting (for network buffers to become available, for example). - In this case the method will return true - or a non-zero integer value. When this happens you must continue to call the method with the same - parameters until it returns false or zero, before calling any other method on it or - attached BufferedTransformation. The integer return value in this case is approximately - the number of bytes left to be processed, and can be used to implement a progress bar. - - For functions that take a "propagation" parameter, propagation != 0 means pass on the signal to attached - BufferedTransformation objects, with propagation decremented at each step until it reaches 0. - -1 means unlimited propagation. - - \nosubgrouping -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BufferedTransformation : public Algorithm, public Waitable -{ -public: - // placed up here for CW8 - static const std::string &NULL_CHANNEL; // same as DEFAULT_CHANNEL, for backwards compatibility - - BufferedTransformation() : Algorithm(false) {} - - //! return a reference to this object, useful for passing a temporary object to a function that takes a non-const reference - BufferedTransformation& Ref() {return *this;} - - //! \name INPUT - //@{ - //! input a byte for processing - size_t Put(byte inByte, bool blocking=true) - {return Put(&inByte, 1, blocking);} - //! input multiple bytes - size_t Put(const byte *inString, size_t length, bool blocking=true) - {return Put2(inString, length, 0, blocking);} - - //! input a 16-bit word - size_t PutWord16(word16 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); - //! input a 32-bit word - size_t PutWord32(word32 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); - - //! request space which can be written into by the caller, and then used as input to Put() - /*! \param size is requested size (as a hint) for input, and size of the returned space for output */ - /*! \note The purpose of this method is to help avoid doing extra memory allocations. */ - virtual byte * CreatePutSpace(size_t &size) {size=0; return NULL;} - - virtual bool CanModifyInput() const {return false;} - - //! input multiple bytes that may be modified by callee - size_t PutModifiable(byte *inString, size_t length, bool blocking=true) - {return PutModifiable2(inString, length, 0, blocking);} - - bool MessageEnd(int propagation=-1, bool blocking=true) - {return !!Put2(NULL, 0, propagation < 0 ? -1 : propagation+1, blocking);} - size_t PutMessageEnd(const byte *inString, size_t length, int propagation=-1, bool blocking=true) - {return Put2(inString, length, propagation < 0 ? -1 : propagation+1, blocking);} - - //! input multiple bytes for blocking or non-blocking processing - /*! \param messageEnd means how many filters to signal MessageEnd to, including this one */ - virtual size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking) =0; - //! input multiple bytes that may be modified by callee for blocking or non-blocking processing - /*! \param messageEnd means how many filters to signal MessageEnd to, including this one */ - virtual size_t PutModifiable2(byte *inString, size_t length, int messageEnd, bool blocking) - {return Put2(inString, length, messageEnd, blocking);} - - //! thrown by objects that have not implemented nonblocking input processing - struct BlockingInputOnly : public NotImplemented - {BlockingInputOnly(const std::string &s) : NotImplemented(s + ": Nonblocking input is not implemented by this object.") {}}; - //@} - - //! \name WAITING - //@{ - unsigned int GetMaxWaitObjectCount() const; - void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); - //@} - - //! \name SIGNALS - //@{ - virtual void IsolatedInitialize(const NameValuePairs ¶meters) {throw NotImplemented("BufferedTransformation: this object can't be reinitialized");} - virtual bool IsolatedFlush(bool hardFlush, bool blocking) =0; - virtual bool IsolatedMessageSeriesEnd(bool blocking) {return false;} - - //! initialize or reinitialize this object - virtual void Initialize(const NameValuePairs ¶meters=g_nullNameValuePairs, int propagation=-1); - //! flush buffered input and/or output - /*! \param hardFlush is used to indicate whether all data should be flushed - \note Hard flushes must be used with care. It means try to process and output everything, even if - there may not be enough data to complete the action. For example, hard flushing a HexDecoder would - cause an error if you do it after inputing an odd number of hex encoded characters. - For some types of filters, for example ZlibDecompressor, hard flushes can only - be done at "synchronization points". These synchronization points are positions in the data - stream that are created by hard flushes on the corresponding reverse filters, in this - example ZlibCompressor. This is useful when zlib compressed data is moved across a - network in packets and compression state is preserved across packets, as in the ssh2 protocol. - */ - virtual bool Flush(bool hardFlush, int propagation=-1, bool blocking=true); - //! mark end of a series of messages - /*! There should be a MessageEnd immediately before MessageSeriesEnd. */ - virtual bool MessageSeriesEnd(int propagation=-1, bool blocking=true); - - //! set propagation of automatically generated and transferred signals - /*! propagation == 0 means do not automaticly generate signals */ - virtual void SetAutoSignalPropagation(int propagation) {} - - //! - virtual int GetAutoSignalPropagation() const {return 0;} -public: - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - void Close() {MessageEnd();} -#endif - //@} - - //! \name RETRIEVAL OF ONE MESSAGE - //@{ - //! returns number of bytes that is currently ready for retrieval - /*! All retrieval functions return the actual number of bytes - retrieved, which is the lesser of the request number and - MaxRetrievable(). */ - virtual lword MaxRetrievable() const; - - //! returns whether any bytes are currently ready for retrieval - virtual bool AnyRetrievable() const; - - //! try to retrieve a single byte - virtual size_t Get(byte &outByte); - //! try to retrieve multiple bytes - virtual size_t Get(byte *outString, size_t getMax); - - //! peek at the next byte without removing it from the output buffer - virtual size_t Peek(byte &outByte) const; - //! peek at multiple bytes without removing them from the output buffer - virtual size_t Peek(byte *outString, size_t peekMax) const; - - //! try to retrieve a 16-bit word - size_t GetWord16(word16 &value, ByteOrder order=BIG_ENDIAN_ORDER); - //! try to retrieve a 32-bit word - size_t GetWord32(word32 &value, ByteOrder order=BIG_ENDIAN_ORDER); - - //! try to peek at a 16-bit word - size_t PeekWord16(word16 &value, ByteOrder order=BIG_ENDIAN_ORDER) const; - //! try to peek at a 32-bit word - size_t PeekWord32(word32 &value, ByteOrder order=BIG_ENDIAN_ORDER) const; - - //! move transferMax bytes of the buffered output to target as input - lword TransferTo(BufferedTransformation &target, lword transferMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) - {TransferTo2(target, transferMax, channel); return transferMax;} - - //! discard skipMax bytes from the output buffer - virtual lword Skip(lword skipMax=LWORD_MAX); - - //! copy copyMax bytes of the buffered output to target as input - lword CopyTo(BufferedTransformation &target, lword copyMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) const - {return CopyRangeTo(target, 0, copyMax, channel);} - - //! copy copyMax bytes of the buffered output, starting at position (relative to current position), to target as input - lword CopyRangeTo(BufferedTransformation &target, lword position, lword copyMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) const - {lword i = position; CopyRangeTo2(target, i, i+copyMax, channel); return i-position;} - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - unsigned long MaxRetrieveable() const {return MaxRetrievable();} -#endif - //@} - - //! \name RETRIEVAL OF MULTIPLE MESSAGES - //@{ - //! - virtual lword TotalBytesRetrievable() const; - //! number of times MessageEnd() has been received minus messages retrieved or skipped - virtual unsigned int NumberOfMessages() const; - //! returns true if NumberOfMessages() > 0 - virtual bool AnyMessages() const; - //! start retrieving the next message - /*! - Returns false if no more messages exist or this message - is not completely retrieved. - */ - virtual bool GetNextMessage(); - //! skip count number of messages - virtual unsigned int SkipMessages(unsigned int count=UINT_MAX); - //! - unsigned int TransferMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) - {TransferMessagesTo2(target, count, channel); return count;} - //! - unsigned int CopyMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) const; - - //! - virtual void SkipAll(); - //! - void TransferAllTo(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL) - {TransferAllTo2(target, channel);} - //! - void CopyAllTo(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL) const; - - virtual bool GetNextMessageSeries() {return false;} - virtual unsigned int NumberOfMessagesInThisSeries() const {return NumberOfMessages();} - virtual unsigned int NumberOfMessageSeries() const {return 0;} - //@} - - //! \name NON-BLOCKING TRANSFER OF OUTPUT - //@{ - //! upon return, byteCount contains number of bytes that have finished being transfered, and returns the number of bytes left in the current transfer block - virtual size_t TransferTo2(BufferedTransformation &target, lword &byteCount, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) =0; - //! upon return, begin contains the start position of data yet to be finished copying, and returns the number of bytes left in the current transfer block - virtual size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const =0; - //! upon return, messageCount contains number of messages that have finished being transfered, and returns the number of bytes left in the current transfer block - size_t TransferMessagesTo2(BufferedTransformation &target, unsigned int &messageCount, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - //! returns the number of bytes left in the current transfer block - size_t TransferAllTo2(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - //@} - - //! \name CHANNELS - //@{ - struct NoChannelSupport : public NotImplemented - {NoChannelSupport(const std::string &name) : NotImplemented(name + ": this object doesn't support multiple channels") {}}; - struct InvalidChannelName : public InvalidArgument - {InvalidChannelName(const std::string &name, const std::string &channel) : InvalidArgument(name + ": unexpected channel name \"" + channel + "\"") {}}; - - size_t ChannelPut(const std::string &channel, byte inByte, bool blocking=true) - {return ChannelPut(channel, &inByte, 1, blocking);} - size_t ChannelPut(const std::string &channel, const byte *inString, size_t length, bool blocking=true) - {return ChannelPut2(channel, inString, length, 0, blocking);} - - size_t ChannelPutModifiable(const std::string &channel, byte *inString, size_t length, bool blocking=true) - {return ChannelPutModifiable2(channel, inString, length, 0, blocking);} - - size_t ChannelPutWord16(const std::string &channel, word16 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); - size_t ChannelPutWord32(const std::string &channel, word32 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); - - bool ChannelMessageEnd(const std::string &channel, int propagation=-1, bool blocking=true) - {return !!ChannelPut2(channel, NULL, 0, propagation < 0 ? -1 : propagation+1, blocking);} - size_t ChannelPutMessageEnd(const std::string &channel, const byte *inString, size_t length, int propagation=-1, bool blocking=true) - {return ChannelPut2(channel, inString, length, propagation < 0 ? -1 : propagation+1, blocking);} - - virtual byte * ChannelCreatePutSpace(const std::string &channel, size_t &size); - - virtual size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking); - virtual size_t ChannelPutModifiable2(const std::string &channel, byte *begin, size_t length, int messageEnd, bool blocking); - - virtual bool ChannelFlush(const std::string &channel, bool hardFlush, int propagation=-1, bool blocking=true); - virtual bool ChannelMessageSeriesEnd(const std::string &channel, int propagation=-1, bool blocking=true); - - virtual void SetRetrievalChannel(const std::string &channel); - //@} - - //! \name ATTACHMENT - /*! Some BufferedTransformation objects (e.g. Filter objects) - allow other BufferedTransformation objects to be attached. When - this is done, the first object instead of buffering its output, - sents that output to the attached object as input. The entire - attachment chain is deleted when the anchor object is destructed. - */ - //@{ - //! returns whether this object allows attachment - virtual bool Attachable() {return false;} - //! returns the object immediately attached to this object or NULL for no attachment - virtual BufferedTransformation *AttachedTransformation() {assert(!Attachable()); return 0;} - //! - virtual const BufferedTransformation *AttachedTransformation() const - {return const_cast(this)->AttachedTransformation();} - //! delete the current attachment chain and replace it with newAttachment - virtual void Detach(BufferedTransformation *newAttachment = 0) - {assert(!Attachable()); throw NotImplemented("BufferedTransformation: this object is not attachable");} - //! add newAttachment to the end of attachment chain - virtual void Attach(BufferedTransformation *newAttachment); - //@} - -protected: - static int DecrementPropagation(int propagation) - {return propagation != 0 ? propagation - 1 : 0;} - -private: - byte m_buf[4]; // for ChannelPutWord16 and ChannelPutWord32, to ensure buffer isn't deallocated before non-blocking operation completes -}; - -//! returns a reference to a BufferedTransformation object that discards all input -BufferedTransformation & TheBitBucket(); - -//! interface for crypto material, such as public and private keys, and crypto parameters - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CryptoMaterial : public NameValuePairs -{ -public: - //! exception thrown when invalid crypto material is detected - class CRYPTOPP_DLL InvalidMaterial : public InvalidDataFormat - { - public: - explicit InvalidMaterial(const std::string &s) : InvalidDataFormat(s) {} - }; - - //! assign values from source to this object - /*! \note This function can be used to create a public key from a private key. */ - virtual void AssignFrom(const NameValuePairs &source) =0; - - //! check this object for errors - /*! \param level denotes the level of thoroughness: - 0 - using this object won't cause a crash or exception (rng is ignored) - 1 - this object will probably function (encrypt, sign, etc.) correctly (but may not check for weak keys and such) - 2 - make sure this object will function correctly, and do reasonable security checks - 3 - do checks that may take a long time - \return true if the tests pass */ - virtual bool Validate(RandomNumberGenerator &rng, unsigned int level) const =0; - - //! throws InvalidMaterial if this object fails Validate() test - virtual void ThrowIfInvalid(RandomNumberGenerator &rng, unsigned int level) const - {if (!Validate(rng, level)) throw InvalidMaterial("CryptoMaterial: this object contains invalid values");} - -// virtual std::vector GetSupportedFormats(bool includeSaveOnly=false, bool includeLoadOnly=false); - - //! save key into a BufferedTransformation - virtual void Save(BufferedTransformation &bt) const - {throw NotImplemented("CryptoMaterial: this object does not support saving");} - - //! load key from a BufferedTransformation - /*! \throws KeyingErr if decode fails - \note Generally does not check that the key is valid. - Call ValidateKey() or ThrowIfInvalidKey() to check that. */ - virtual void Load(BufferedTransformation &bt) - {throw NotImplemented("CryptoMaterial: this object does not support loading");} - - //! \return whether this object supports precomputation - virtual bool SupportsPrecomputation() const {return false;} - //! do precomputation - /*! The exact semantics of Precompute() is varies, but - typically it means calculate a table of n objects - that can be used later to speed up computation. */ - virtual void Precompute(unsigned int n) - {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} - //! retrieve previously saved precomputation - virtual void LoadPrecomputation(BufferedTransformation &storedPrecomputation) - {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} - //! save precomputation for later use - virtual void SavePrecomputation(BufferedTransformation &storedPrecomputation) const - {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} - - // for internal library use - void DoQuickSanityCheck() const {ThrowIfInvalid(NullRNG(), 0);} - -#if (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) - // Sun Studio 11/CC 5.8 workaround: it generates incorrect code when casting to an empty virtual base class - char m_sunCCworkaround; -#endif -}; - -//! interface for generatable crypto material, such as private keys and crypto parameters - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE GeneratableCryptoMaterial : virtual public CryptoMaterial -{ -public: - //! generate a random key or crypto parameters - /*! \throws KeyingErr if algorithm parameters are invalid, or if a key can't be generated - (e.g., if this is a public key object) */ - virtual void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶ms = g_nullNameValuePairs) - {throw NotImplemented("GeneratableCryptoMaterial: this object does not support key/parameter generation");} - - //! calls the above function with a NameValuePairs object that just specifies "KeySize" - void GenerateRandomWithKeySize(RandomNumberGenerator &rng, unsigned int keySize); -}; - -//! interface for public keys - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PublicKey : virtual public CryptoMaterial -{ -}; - -//! interface for private keys - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PrivateKey : public GeneratableCryptoMaterial -{ -}; - -//! interface for crypto prameters - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CryptoParameters : public GeneratableCryptoMaterial -{ -}; - -//! interface for asymmetric algorithms - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AsymmetricAlgorithm : public Algorithm -{ -public: - //! returns a reference to the crypto material used by this object - virtual CryptoMaterial & AccessMaterial() =0; - //! returns a const reference to the crypto material used by this object - virtual const CryptoMaterial & GetMaterial() const =0; - - //! for backwards compatibility, calls AccessMaterial().Load(bt) - void BERDecode(BufferedTransformation &bt) - {AccessMaterial().Load(bt);} - //! for backwards compatibility, calls GetMaterial().Save(bt) - void DEREncode(BufferedTransformation &bt) const - {GetMaterial().Save(bt);} -}; - -//! interface for asymmetric algorithms using public keys - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PublicKeyAlgorithm : public AsymmetricAlgorithm -{ -public: - // VC60 workaround: no co-variant return type - CryptoMaterial & AccessMaterial() {return AccessPublicKey();} - const CryptoMaterial & GetMaterial() const {return GetPublicKey();} - - virtual PublicKey & AccessPublicKey() =0; - virtual const PublicKey & GetPublicKey() const {return const_cast(this)->AccessPublicKey();} -}; - -//! interface for asymmetric algorithms using private keys - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PrivateKeyAlgorithm : public AsymmetricAlgorithm -{ -public: - CryptoMaterial & AccessMaterial() {return AccessPrivateKey();} - const CryptoMaterial & GetMaterial() const {return GetPrivateKey();} - - virtual PrivateKey & AccessPrivateKey() =0; - virtual const PrivateKey & GetPrivateKey() const {return const_cast(this)->AccessPrivateKey();} -}; - -//! interface for key agreement algorithms - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE KeyAgreementAlgorithm : public AsymmetricAlgorithm -{ -public: - CryptoMaterial & AccessMaterial() {return AccessCryptoParameters();} - const CryptoMaterial & GetMaterial() const {return GetCryptoParameters();} - - virtual CryptoParameters & AccessCryptoParameters() =0; - virtual const CryptoParameters & GetCryptoParameters() const {return const_cast(this)->AccessCryptoParameters();} -}; - -//! interface for public-key encryptors and decryptors - -/*! This class provides an interface common to encryptors and decryptors - for querying their plaintext and ciphertext lengths. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_CryptoSystem -{ -public: - virtual ~PK_CryptoSystem() {} - - //! maximum length of plaintext for a given ciphertext length - /*! \note This function returns 0 if ciphertextLength is not valid (too long or too short). */ - virtual size_t MaxPlaintextLength(size_t ciphertextLength) const =0; - - //! calculate length of ciphertext given length of plaintext - /*! \note This function returns 0 if plaintextLength is not valid (too long). */ - virtual size_t CiphertextLength(size_t plaintextLength) const =0; - - //! this object supports the use of the parameter with the given name - /*! some possible parameter names: EncodingParameters, KeyDerivationParameters */ - virtual bool ParameterSupported(const char *name) const =0; - - //! return fixed ciphertext length, if one exists, otherwise return 0 - /*! \note "Fixed" here means length of ciphertext does not depend on length of plaintext. - It usually does depend on the key length. */ - virtual size_t FixedCiphertextLength() const {return 0;} - - //! return maximum plaintext length given the fixed ciphertext length, if one exists, otherwise return 0 - virtual size_t FixedMaxPlaintextLength() const {return 0;} - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - size_t MaxPlainTextLength(size_t cipherTextLength) const {return MaxPlaintextLength(cipherTextLength);} - size_t CipherTextLength(size_t plainTextLength) const {return CiphertextLength(plainTextLength);} -#endif -}; - -//! interface for public-key encryptors -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Encryptor : public PK_CryptoSystem, public PublicKeyAlgorithm -{ -public: - //! exception thrown when trying to encrypt plaintext of invalid length - class CRYPTOPP_DLL InvalidPlaintextLength : public Exception - { - public: - InvalidPlaintextLength() : Exception(OTHER_ERROR, "PK_Encryptor: invalid plaintext length") {} - }; - - //! encrypt a byte string - /*! \pre CiphertextLength(plaintextLength) != 0 (i.e., plaintext isn't too long) - \pre size of ciphertext == CiphertextLength(plaintextLength) - */ - virtual void Encrypt(RandomNumberGenerator &rng, - const byte *plaintext, size_t plaintextLength, - byte *ciphertext, const NameValuePairs ¶meters = g_nullNameValuePairs) const =0; - - //! create a new encryption filter - /*! \note The caller is responsible for deleting the returned pointer. - \note Encoding parameters should be passed in the "EP" channel. - */ - virtual BufferedTransformation * CreateEncryptionFilter(RandomNumberGenerator &rng, - BufferedTransformation *attachment=NULL, const NameValuePairs ¶meters = g_nullNameValuePairs) const; -}; - -//! interface for public-key decryptors - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Decryptor : public PK_CryptoSystem, public PrivateKeyAlgorithm -{ -public: - //! decrypt a byte string, and return the length of plaintext - /*! \pre size of plaintext == MaxPlaintextLength(ciphertextLength) bytes. - \return the actual length of the plaintext, indication that decryption failed. - */ - virtual DecodingResult Decrypt(RandomNumberGenerator &rng, - const byte *ciphertext, size_t ciphertextLength, - byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const =0; - - //! create a new decryption filter - /*! \note caller is responsible for deleting the returned pointer - */ - virtual BufferedTransformation * CreateDecryptionFilter(RandomNumberGenerator &rng, - BufferedTransformation *attachment=NULL, const NameValuePairs ¶meters = g_nullNameValuePairs) const; - - //! decrypt a fixed size ciphertext - DecodingResult FixedLengthDecrypt(RandomNumberGenerator &rng, const byte *ciphertext, byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const - {return Decrypt(rng, ciphertext, FixedCiphertextLength(), plaintext, parameters);} -}; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY -typedef PK_CryptoSystem PK_FixedLengthCryptoSystem; -typedef PK_Encryptor PK_FixedLengthEncryptor; -typedef PK_Decryptor PK_FixedLengthDecryptor; -#endif - -//! interface for public-key signers and verifiers - -/*! This class provides an interface common to signers and verifiers - for querying scheme properties. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_SignatureScheme -{ -public: - //! invalid key exception, may be thrown by any function in this class if the private or public key has a length that can't be used - class CRYPTOPP_DLL InvalidKeyLength : public Exception - { - public: - InvalidKeyLength(const std::string &message) : Exception(OTHER_ERROR, message) {} - }; - - //! key too short exception, may be thrown by any function in this class if the private or public key is too short to sign or verify anything - class CRYPTOPP_DLL KeyTooShort : public InvalidKeyLength - { - public: - KeyTooShort() : InvalidKeyLength("PK_Signer: key too short for this signature scheme") {} - }; - - virtual ~PK_SignatureScheme() {} - - //! signature length if it only depends on the key, otherwise 0 - virtual size_t SignatureLength() const =0; - - //! maximum signature length produced for a given length of recoverable message part - virtual size_t MaxSignatureLength(size_t recoverablePartLength = 0) const {return SignatureLength();} - - //! length of longest message that can be recovered, or 0 if this signature scheme does not support message recovery - virtual size_t MaxRecoverableLength() const =0; - - //! length of longest message that can be recovered from a signature of given length, or 0 if this signature scheme does not support message recovery - virtual size_t MaxRecoverableLengthFromSignatureLength(size_t signatureLength) const =0; - - //! requires a random number generator to sign - /*! if this returns false, NullRNG() can be passed to functions that take RandomNumberGenerator & */ - virtual bool IsProbabilistic() const =0; - - //! whether or not a non-recoverable message part can be signed - virtual bool AllowNonrecoverablePart() const =0; - - //! if this function returns true, during verification you must input the signature before the message, otherwise you can input it at anytime */ - virtual bool SignatureUpfront() const {return false;} - - //! whether you must input the recoverable part before the non-recoverable part during signing - virtual bool RecoverablePartFirst() const =0; -}; - -//! interface for accumulating messages to be signed or verified -/*! Only Update() should be called - on this class. No other functions inherited from HashTransformation should be called. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_MessageAccumulator : public HashTransformation -{ -public: - //! should not be called on PK_MessageAccumulator - unsigned int DigestSize() const - {throw NotImplemented("PK_MessageAccumulator: DigestSize() should not be called");} - //! should not be called on PK_MessageAccumulator - void TruncatedFinal(byte *digest, size_t digestSize) - {throw NotImplemented("PK_MessageAccumulator: TruncatedFinal() should not be called");} -}; - -//! interface for public-key signers - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Signer : public PK_SignatureScheme, public PrivateKeyAlgorithm -{ -public: - //! create a new HashTransformation to accumulate the message to be signed - virtual PK_MessageAccumulator * NewSignatureAccumulator(RandomNumberGenerator &rng) const =0; - - virtual void InputRecoverableMessage(PK_MessageAccumulator &messageAccumulator, const byte *recoverableMessage, size_t recoverableMessageLength) const =0; - - //! sign and delete messageAccumulator (even in case of exception thrown) - /*! \pre size of signature == MaxSignatureLength() - \return actual signature length - */ - virtual size_t Sign(RandomNumberGenerator &rng, PK_MessageAccumulator *messageAccumulator, byte *signature) const; - - //! sign and restart messageAccumulator - /*! \pre size of signature == MaxSignatureLength() - \return actual signature length - */ - virtual size_t SignAndRestart(RandomNumberGenerator &rng, PK_MessageAccumulator &messageAccumulator, byte *signature, bool restart=true) const =0; - - //! sign a message - /*! \pre size of signature == MaxSignatureLength() - \return actual signature length - */ - virtual size_t SignMessage(RandomNumberGenerator &rng, const byte *message, size_t messageLen, byte *signature) const; - - //! sign a recoverable message - /*! \pre size of signature == MaxSignatureLength(recoverableMessageLength) - \return actual signature length - */ - virtual size_t SignMessageWithRecovery(RandomNumberGenerator &rng, const byte *recoverableMessage, size_t recoverableMessageLength, - const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, byte *signature) const; -}; - -//! interface for public-key signature verifiers -/*! The Recover* functions throw NotImplemented if the signature scheme does not support - message recovery. - The Verify* functions throw InvalidDataFormat if the scheme does support message - recovery and the signature contains a non-empty recoverable message part. The - Recovery* functions should be used in that case. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Verifier : public PK_SignatureScheme, public PublicKeyAlgorithm -{ -public: - //! create a new HashTransformation to accumulate the message to be verified - virtual PK_MessageAccumulator * NewVerificationAccumulator() const =0; - - //! input signature into a message accumulator - virtual void InputSignature(PK_MessageAccumulator &messageAccumulator, const byte *signature, size_t signatureLength) const =0; - - //! check whether messageAccumulator contains a valid signature and message, and delete messageAccumulator (even in case of exception thrown) - virtual bool Verify(PK_MessageAccumulator *messageAccumulator) const; - - //! check whether messageAccumulator contains a valid signature and message, and restart messageAccumulator - virtual bool VerifyAndRestart(PK_MessageAccumulator &messageAccumulator) const =0; - - //! check whether input signature is a valid signature for input message - virtual bool VerifyMessage(const byte *message, size_t messageLen, - const byte *signature, size_t signatureLength) const; - - //! recover a message from its signature - /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) - */ - virtual DecodingResult Recover(byte *recoveredMessage, PK_MessageAccumulator *messageAccumulator) const; - - //! recover a message from its signature - /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) - */ - virtual DecodingResult RecoverAndRestart(byte *recoveredMessage, PK_MessageAccumulator &messageAccumulator) const =0; - - //! recover a message from its signature - /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) - */ - virtual DecodingResult RecoverMessage(byte *recoveredMessage, - const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, - const byte *signature, size_t signatureLength) const; -}; - -//! interface for domains of simple key agreement protocols - -/*! A key agreement domain is a set of parameters that must be shared - by two parties in a key agreement protocol, along with the algorithms - for generating key pairs and deriving agreed values. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SimpleKeyAgreementDomain : public KeyAgreementAlgorithm -{ -public: - //! return length of agreed value produced - virtual unsigned int AgreedValueLength() const =0; - //! return length of private keys in this domain - virtual unsigned int PrivateKeyLength() const =0; - //! return length of public keys in this domain - virtual unsigned int PublicKeyLength() const =0; - //! generate private key - /*! \pre size of privateKey == PrivateKeyLength() */ - virtual void GeneratePrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; - //! generate public key - /*! \pre size of publicKey == PublicKeyLength() */ - virtual void GeneratePublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; - //! generate private/public key pair - /*! \note equivalent to calling GeneratePrivateKey() and then GeneratePublicKey() */ - virtual void GenerateKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; - //! derive agreed value from your private key and couterparty's public key, return false in case of failure - /*! \note If you have previously validated the public key, use validateOtherPublicKey=false to save time. - \pre size of agreedValue == AgreedValueLength() - \pre length of privateKey == PrivateKeyLength() - \pre length of otherPublicKey == PublicKeyLength() - */ - virtual bool Agree(byte *agreedValue, const byte *privateKey, const byte *otherPublicKey, bool validateOtherPublicKey=true) const =0; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - bool ValidateDomainParameters(RandomNumberGenerator &rng) const - {return GetCryptoParameters().Validate(rng, 2);} -#endif -}; - -//! interface for domains of authenticated key agreement protocols - -/*! In an authenticated key agreement protocol, each party has two - key pairs. The long-lived key pair is called the static key pair, - and the short-lived key pair is called the ephemeral key pair. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedKeyAgreementDomain : public KeyAgreementAlgorithm -{ -public: - //! return length of agreed value produced - virtual unsigned int AgreedValueLength() const =0; - - //! return length of static private keys in this domain - virtual unsigned int StaticPrivateKeyLength() const =0; - //! return length of static public keys in this domain - virtual unsigned int StaticPublicKeyLength() const =0; - //! generate static private key - /*! \pre size of privateKey == PrivateStaticKeyLength() */ - virtual void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; - //! generate static public key - /*! \pre size of publicKey == PublicStaticKeyLength() */ - virtual void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; - //! generate private/public key pair - /*! \note equivalent to calling GenerateStaticPrivateKey() and then GenerateStaticPublicKey() */ - virtual void GenerateStaticKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; - - //! return length of ephemeral private keys in this domain - virtual unsigned int EphemeralPrivateKeyLength() const =0; - //! return length of ephemeral public keys in this domain - virtual unsigned int EphemeralPublicKeyLength() const =0; - //! generate ephemeral private key - /*! \pre size of privateKey == PrivateEphemeralKeyLength() */ - virtual void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; - //! generate ephemeral public key - /*! \pre size of publicKey == PublicEphemeralKeyLength() */ - virtual void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; - //! generate private/public key pair - /*! \note equivalent to calling GenerateEphemeralPrivateKey() and then GenerateEphemeralPublicKey() */ - virtual void GenerateEphemeralKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; - - //! derive agreed value from your private keys and couterparty's public keys, return false in case of failure - /*! \note The ephemeral public key will always be validated. - If you have previously validated the static public key, use validateStaticOtherPublicKey=false to save time. - \pre size of agreedValue == AgreedValueLength() - \pre length of staticPrivateKey == StaticPrivateKeyLength() - \pre length of ephemeralPrivateKey == EphemeralPrivateKeyLength() - \pre length of staticOtherPublicKey == StaticPublicKeyLength() - \pre length of ephemeralOtherPublicKey == EphemeralPublicKeyLength() - */ - virtual bool Agree(byte *agreedValue, - const byte *staticPrivateKey, const byte *ephemeralPrivateKey, - const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey, - bool validateStaticOtherPublicKey=true) const =0; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - bool ValidateDomainParameters(RandomNumberGenerator &rng) const - {return GetCryptoParameters().Validate(rng, 2);} -#endif -}; - -// interface for password authenticated key agreement protocols, not implemented yet -#if 0 -//! interface for protocol sessions -/*! The methods should be called in the following order: - - InitializeSession(rng, parameters); // or call initialize method in derived class - while (true) - { - if (OutgoingMessageAvailable()) - { - length = GetOutgoingMessageLength(); - GetOutgoingMessage(message); - ; // send outgoing message - } - - if (LastMessageProcessed()) - break; - - ; // receive incoming message - ProcessIncomingMessage(message); - } - ; // call methods in derived class to obtain result of protocol session -*/ -class ProtocolSession -{ -public: - //! exception thrown when an invalid protocol message is processed - class ProtocolError : public Exception - { - public: - ProtocolError(ErrorType errorType, const std::string &s) : Exception(errorType, s) {} - }; - - //! exception thrown when a function is called unexpectedly - /*! for example calling ProcessIncomingMessage() when ProcessedLastMessage() == true */ - class UnexpectedMethodCall : public Exception - { - public: - UnexpectedMethodCall(const std::string &s) : Exception(OTHER_ERROR, s) {} - }; - - ProtocolSession() : m_rng(NULL), m_throwOnProtocolError(true), m_validState(false) {} - virtual ~ProtocolSession() {} - - virtual void InitializeSession(RandomNumberGenerator &rng, const NameValuePairs ¶meters) =0; - - bool GetThrowOnProtocolError() const {return m_throwOnProtocolError;} - void SetThrowOnProtocolError(bool throwOnProtocolError) {m_throwOnProtocolError = throwOnProtocolError;} - - bool HasValidState() const {return m_validState;} - - virtual bool OutgoingMessageAvailable() const =0; - virtual unsigned int GetOutgoingMessageLength() const =0; - virtual void GetOutgoingMessage(byte *message) =0; - - virtual bool LastMessageProcessed() const =0; - virtual void ProcessIncomingMessage(const byte *message, unsigned int messageLength) =0; - -protected: - void HandleProtocolError(Exception::ErrorType errorType, const std::string &s) const; - void CheckAndHandleInvalidState() const; - void SetValidState(bool valid) {m_validState = valid;} - - RandomNumberGenerator *m_rng; - -private: - bool m_throwOnProtocolError, m_validState; -}; - -class KeyAgreementSession : public ProtocolSession -{ -public: - virtual unsigned int GetAgreedValueLength() const =0; - virtual void GetAgreedValue(byte *agreedValue) const =0; -}; - -class PasswordAuthenticatedKeyAgreementSession : public KeyAgreementSession -{ -public: - void InitializePasswordAuthenticatedKeyAgreementSession(RandomNumberGenerator &rng, - const byte *myId, unsigned int myIdLength, - const byte *counterPartyId, unsigned int counterPartyIdLength, - const byte *passwordOrVerifier, unsigned int passwordOrVerifierLength); -}; - -class PasswordAuthenticatedKeyAgreementDomain : public KeyAgreementAlgorithm -{ -public: - //! return whether the domain parameters stored in this object are valid - virtual bool ValidateDomainParameters(RandomNumberGenerator &rng) const - {return GetCryptoParameters().Validate(rng, 2);} - - virtual unsigned int GetPasswordVerifierLength(const byte *password, unsigned int passwordLength) const =0; - virtual void GeneratePasswordVerifier(RandomNumberGenerator &rng, const byte *userId, unsigned int userIdLength, const byte *password, unsigned int passwordLength, byte *verifier) const =0; - - enum RoleFlags {CLIENT=1, SERVER=2, INITIATOR=4, RESPONDER=8}; - - virtual bool IsValidRole(unsigned int role) =0; - virtual PasswordAuthenticatedKeyAgreementSession * CreateProtocolSession(unsigned int role) const =0; -}; -#endif - -//! BER Decode Exception Class, may be thrown during an ASN1 BER decode operation -class CRYPTOPP_DLL BERDecodeErr : public InvalidArgument -{ -public: - BERDecodeErr() : InvalidArgument("BER decode error") {} - BERDecodeErr(const std::string &s) : InvalidArgument(s) {} -}; - -//! interface for encoding and decoding ASN1 objects -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE ASN1Object -{ -public: - virtual ~ASN1Object() {} - //! decode this object from a BufferedTransformation, using BER (Basic Encoding Rules) - virtual void BERDecode(BufferedTransformation &bt) =0; - //! encode this object into a BufferedTransformation, using DER (Distinguished Encoding Rules) - virtual void DEREncode(BufferedTransformation &bt) const =0; - //! encode this object into a BufferedTransformation, using BER - /*! this may be useful if DEREncode() would be too inefficient */ - virtual void BEREncode(BufferedTransformation &bt) const {DEREncode(bt);} -}; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY -typedef PK_SignatureScheme PK_SignatureSystem; -typedef SimpleKeyAgreementDomain PK_SimpleKeyAgreementDomain; -typedef AuthenticatedKeyAgreementDomain PK_AuthenticatedKeyAgreementDomain; -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/default.cpp b/CryptoPP/default.cpp deleted file mode 100644 index 72940784d..000000000 --- a/CryptoPP/default.cpp +++ /dev/null @@ -1,258 +0,0 @@ -// default.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "default.h" -#include "queue.h" -#include -#include - -NAMESPACE_BEGIN(CryptoPP) - -static const unsigned int MASH_ITERATIONS = 200; -static const unsigned int SALTLENGTH = 8; -static const unsigned int BLOCKSIZE = Default_BlockCipher::Encryption::BLOCKSIZE; -static const unsigned int KEYLENGTH = Default_BlockCipher::Encryption::DEFAULT_KEYLENGTH; - -// The purpose of this function Mash() is to take an arbitrary length input -// string and *deterministicly* produce an arbitrary length output string such -// that (1) it looks random, (2) no information about the input is -// deducible from it, and (3) it contains as much entropy as it can hold, or -// the amount of entropy in the input string, whichever is smaller. - -static void Mash(const byte *in, size_t inLen, byte *out, size_t outLen, int iterations) -{ - if (BytePrecision(outLen) > 2) - throw InvalidArgument("Mash: output legnth too large"); - - size_t bufSize = RoundUpToMultipleOf(outLen, (size_t)DefaultHashModule::DIGESTSIZE); - byte b[2]; - SecByteBlock buf(bufSize); - SecByteBlock outBuf(bufSize); - DefaultHashModule hash; - - unsigned int i; - for(i=0; i> 8); - b[1] = (byte) i; - hash.Update(b, 2); - hash.Update(in, inLen); - hash.Final(outBuf+i); - } - - while (iterations-- > 1) - { - memcpy(buf, outBuf, bufSize); - for (i=0; i> 8); - b[1] = (byte) i; - hash.Update(b, 2); - hash.Update(buf, bufSize); - hash.Final(outBuf+i); - } - } - - memcpy(out, outBuf, outLen); -} - -static void GenerateKeyIV(const byte *passphrase, size_t passphraseLength, const byte *salt, size_t saltLength, byte *key, byte *IV) -{ - SecByteBlock temp(passphraseLength+saltLength); - memcpy(temp, passphrase, passphraseLength); - memcpy(temp+passphraseLength, salt, saltLength); - SecByteBlock keyIV(KEYLENGTH+BLOCKSIZE); - Mash(temp, passphraseLength + saltLength, keyIV, KEYLENGTH+BLOCKSIZE, MASH_ITERATIONS); - memcpy(key, keyIV, KEYLENGTH); - memcpy(IV, keyIV+KEYLENGTH, BLOCKSIZE); -} - -// ******************************************************** - -DefaultEncryptor::DefaultEncryptor(const char *passphrase, BufferedTransformation *attachment) - : ProxyFilter(NULL, 0, 0, attachment), m_passphrase((const byte *)passphrase, strlen(passphrase)) -{ -} - -DefaultEncryptor::DefaultEncryptor(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment) - : ProxyFilter(NULL, 0, 0, attachment), m_passphrase(passphrase, passphraseLength) -{ -} - - -void DefaultEncryptor::FirstPut(const byte *) -{ - // VC60 workaround: __LINE__ expansion bug - CRYPTOPP_COMPILE_ASSERT_INSTANCE(SALTLENGTH <= DefaultHashModule::DIGESTSIZE, 1); - CRYPTOPP_COMPILE_ASSERT_INSTANCE(BLOCKSIZE <= DefaultHashModule::DIGESTSIZE, 2); - - SecByteBlock salt(DefaultHashModule::DIGESTSIZE), keyCheck(DefaultHashModule::DIGESTSIZE); - DefaultHashModule hash; - - // use hash(passphrase | time | clock) as salt - hash.Update(m_passphrase, m_passphrase.size()); - time_t t=time(0); - hash.Update((byte *)&t, sizeof(t)); - clock_t c=clock(); - hash.Update((byte *)&c, sizeof(c)); - hash.Final(salt); - - // use hash(passphrase | salt) as key check - hash.Update(m_passphrase, m_passphrase.size()); - hash.Update(salt, SALTLENGTH); - hash.Final(keyCheck); - - AttachedTransformation()->Put(salt, SALTLENGTH); - - // mash passphrase and salt together into key and IV - SecByteBlock key(KEYLENGTH); - SecByteBlock IV(BLOCKSIZE); - GenerateKeyIV(m_passphrase, m_passphrase.size(), salt, SALTLENGTH, key, IV); - - m_cipher.SetKeyWithIV(key, key.size(), IV); - SetFilter(new StreamTransformationFilter(m_cipher)); - - m_filter->Put(keyCheck, BLOCKSIZE); -} - -void DefaultEncryptor::LastPut(const byte *inString, size_t length) -{ - m_filter->MessageEnd(); -} - -// ******************************************************** - -DefaultDecryptor::DefaultDecryptor(const char *p, BufferedTransformation *attachment, bool throwException) - : ProxyFilter(NULL, SALTLENGTH+BLOCKSIZE, 0, attachment) - , m_state(WAITING_FOR_KEYCHECK) - , m_passphrase((const byte *)p, strlen(p)) - , m_throwException(throwException) -{ -} - -DefaultDecryptor::DefaultDecryptor(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment, bool throwException) - : ProxyFilter(NULL, SALTLENGTH+BLOCKSIZE, 0, attachment) - , m_state(WAITING_FOR_KEYCHECK) - , m_passphrase(passphrase, passphraseLength) - , m_throwException(throwException) -{ -} - -void DefaultDecryptor::FirstPut(const byte *inString) -{ - CheckKey(inString, inString+SALTLENGTH); -} - -void DefaultDecryptor::LastPut(const byte *inString, size_t length) -{ - if (m_filter.get() == NULL) - { - m_state = KEY_BAD; - if (m_throwException) - throw KeyBadErr(); - } - else - { - m_filter->MessageEnd(); - m_state = WAITING_FOR_KEYCHECK; - } -} - -void DefaultDecryptor::CheckKey(const byte *salt, const byte *keyCheck) -{ - SecByteBlock check(STDMAX((unsigned int)2*BLOCKSIZE, (unsigned int)DefaultHashModule::DIGESTSIZE)); - - DefaultHashModule hash; - hash.Update(m_passphrase, m_passphrase.size()); - hash.Update(salt, SALTLENGTH); - hash.Final(check); - - SecByteBlock key(KEYLENGTH); - SecByteBlock IV(BLOCKSIZE); - GenerateKeyIV(m_passphrase, m_passphrase.size(), salt, SALTLENGTH, key, IV); - - m_cipher.SetKeyWithIV(key, key.size(), IV); - std::auto_ptr decryptor(new StreamTransformationFilter(m_cipher)); - - decryptor->Put(keyCheck, BLOCKSIZE); - decryptor->ForceNextPut(); - decryptor->Get(check+BLOCKSIZE, BLOCKSIZE); - - SetFilter(decryptor.release()); - - if (!VerifyBufsEqual(check, check+BLOCKSIZE, BLOCKSIZE)) - { - m_state = KEY_BAD; - if (m_throwException) - throw KeyBadErr(); - } - else - m_state = KEY_GOOD; -} - -// ******************************************************** - -static DefaultMAC * NewDefaultEncryptorMAC(const byte *passphrase, size_t passphraseLength) -{ - size_t macKeyLength = DefaultMAC::StaticGetValidKeyLength(16); - SecByteBlock macKey(macKeyLength); - // since the MAC is encrypted there is no reason to mash the passphrase for many iterations - Mash(passphrase, passphraseLength, macKey, macKeyLength, 1); - return new DefaultMAC(macKey, macKeyLength); -} - -DefaultEncryptorWithMAC::DefaultEncryptorWithMAC(const char *passphrase, BufferedTransformation *attachment) - : ProxyFilter(NULL, 0, 0, attachment) - , m_mac(NewDefaultEncryptorMAC((const byte *)passphrase, strlen(passphrase))) -{ - SetFilter(new HashFilter(*m_mac, new DefaultEncryptor(passphrase), true)); -} - -DefaultEncryptorWithMAC::DefaultEncryptorWithMAC(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment) - : ProxyFilter(NULL, 0, 0, attachment) - , m_mac(NewDefaultEncryptorMAC(passphrase, passphraseLength)) -{ - SetFilter(new HashFilter(*m_mac, new DefaultEncryptor(passphrase, passphraseLength), true)); -} - -void DefaultEncryptorWithMAC::LastPut(const byte *inString, size_t length) -{ - m_filter->MessageEnd(); -} - -// ******************************************************** - -DefaultDecryptorWithMAC::DefaultDecryptorWithMAC(const char *passphrase, BufferedTransformation *attachment, bool throwException) - : ProxyFilter(NULL, 0, 0, attachment) - , m_mac(NewDefaultEncryptorMAC((const byte *)passphrase, strlen(passphrase))) - , m_throwException(throwException) -{ - SetFilter(new DefaultDecryptor(passphrase, m_hashVerifier=new HashVerifier(*m_mac, NULL, HashVerifier::PUT_MESSAGE), throwException)); -} - -DefaultDecryptorWithMAC::DefaultDecryptorWithMAC(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment, bool throwException) - : ProxyFilter(NULL, 0, 0, attachment) - , m_mac(NewDefaultEncryptorMAC(passphrase, passphraseLength)) - , m_throwException(throwException) -{ - SetFilter(new DefaultDecryptor(passphrase, passphraseLength, m_hashVerifier=new HashVerifier(*m_mac, NULL, HashVerifier::PUT_MESSAGE), throwException)); -} - -DefaultDecryptor::State DefaultDecryptorWithMAC::CurrentState() const -{ - return static_cast(m_filter.get())->CurrentState(); -} - -bool DefaultDecryptorWithMAC::CheckLastMAC() const -{ - return m_hashVerifier->GetLastResult(); -} - -void DefaultDecryptorWithMAC::LastPut(const byte *inString, size_t length) -{ - m_filter->MessageEnd(); - if (m_throwException && !CheckLastMAC()) - throw MACBadErr(); -} - -NAMESPACE_END diff --git a/CryptoPP/default.h b/CryptoPP/default.h deleted file mode 100644 index fb5364152..000000000 --- a/CryptoPP/default.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef CRYPTOPP_DEFAULT_H -#define CRYPTOPP_DEFAULT_H - -#include "sha.h" -#include "hmac.h" -#include "des.h" -#include "filters.h" -#include "modes.h" - -NAMESPACE_BEGIN(CryptoPP) - -typedef DES_EDE2 Default_BlockCipher; -typedef SHA DefaultHashModule; -typedef HMAC DefaultMAC; - -//! Password-Based Encryptor using DES-EDE2 -class DefaultEncryptor : public ProxyFilter -{ -public: - DefaultEncryptor(const char *passphrase, BufferedTransformation *attachment = NULL); - DefaultEncryptor(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment = NULL); - -protected: - void FirstPut(const byte *); - void LastPut(const byte *inString, size_t length); - -private: - SecByteBlock m_passphrase; - CBC_Mode::Encryption m_cipher; -}; - -//! Password-Based Decryptor using DES-EDE2 -class DefaultDecryptor : public ProxyFilter -{ -public: - DefaultDecryptor(const char *passphrase, BufferedTransformation *attachment = NULL, bool throwException=true); - DefaultDecryptor(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment = NULL, bool throwException=true); - - class Err : public Exception - { - public: - Err(const std::string &s) - : Exception(DATA_INTEGRITY_CHECK_FAILED, s) {} - }; - class KeyBadErr : public Err {public: KeyBadErr() : Err("DefaultDecryptor: cannot decrypt message with this passphrase") {}}; - - enum State {WAITING_FOR_KEYCHECK, KEY_GOOD, KEY_BAD}; - State CurrentState() const {return m_state;} - -protected: - void FirstPut(const byte *inString); - void LastPut(const byte *inString, size_t length); - - State m_state; - -private: - void CheckKey(const byte *salt, const byte *keyCheck); - - SecByteBlock m_passphrase; - CBC_Mode::Decryption m_cipher; - member_ptr m_decryptor; - bool m_throwException; -}; - -//! Password-Based Encryptor using DES-EDE2 and HMAC/SHA-1 -class DefaultEncryptorWithMAC : public ProxyFilter -{ -public: - DefaultEncryptorWithMAC(const char *passphrase, BufferedTransformation *attachment = NULL); - DefaultEncryptorWithMAC(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment = NULL); - -protected: - void FirstPut(const byte *inString) {} - void LastPut(const byte *inString, size_t length); - -private: - member_ptr m_mac; -}; - -//! Password-Based Decryptor using DES-EDE2 and HMAC/SHA-1 -class DefaultDecryptorWithMAC : public ProxyFilter -{ -public: - class MACBadErr : public DefaultDecryptor::Err {public: MACBadErr() : DefaultDecryptor::Err("DefaultDecryptorWithMAC: MAC check failed") {}}; - - DefaultDecryptorWithMAC(const char *passphrase, BufferedTransformation *attachment = NULL, bool throwException=true); - DefaultDecryptorWithMAC(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment = NULL, bool throwException=true); - - DefaultDecryptor::State CurrentState() const; - bool CheckLastMAC() const; - -protected: - void FirstPut(const byte *inString) {} - void LastPut(const byte *inString, size_t length); - -private: - member_ptr m_mac; - HashVerifier *m_hashVerifier; - bool m_throwException; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/des.cpp b/CryptoPP/des.cpp deleted file mode 100644 index a6e0c514d..000000000 --- a/CryptoPP/des.cpp +++ /dev/null @@ -1,449 +0,0 @@ -// des.cpp - modified by Wei Dai from Phil Karn's des.c -// The original code and all modifications are in the public domain. - -/* - * This is a major rewrite of my old public domain DES code written - * circa 1987, which in turn borrowed heavily from Jim Gillogly's 1977 - * public domain code. I pretty much kept my key scheduling code, but - * the actual encrypt/decrypt routines are taken from from Richard - * Outerbridge's DES code as printed in Schneier's "Applied Cryptography." - * - * This code is in the public domain. I would appreciate bug reports and - * enhancements. - * - * Phil Karn KA9Q, karn@unix.ka9q.ampr.org, August 1994. - */ - -#include "pch.h" -#include "misc.h" -#include "des.h" - -NAMESPACE_BEGIN(CryptoPP) - -typedef BlockGetAndPut Block; - -// Richard Outerbridge's initial permutation algorithm -/* -inline void IPERM(word32 &left, word32 &right) -{ - word32 work; - - work = ((left >> 4) ^ right) & 0x0f0f0f0f; - right ^= work; - left ^= work << 4; - work = ((left >> 16) ^ right) & 0xffff; - right ^= work; - left ^= work << 16; - work = ((right >> 2) ^ left) & 0x33333333; - left ^= work; - right ^= (work << 2); - work = ((right >> 8) ^ left) & 0xff00ff; - left ^= work; - right ^= (work << 8); - right = rotl(right, 1); - work = (left ^ right) & 0xaaaaaaaa; - left ^= work; - right ^= work; - left = rotl(left, 1); -} -inline void FPERM(word32 &left, word32 &right) -{ - word32 work; - - right = rotr(right, 1); - work = (left ^ right) & 0xaaaaaaaa; - left ^= work; - right ^= work; - left = rotr(left, 1); - work = ((left >> 8) ^ right) & 0xff00ff; - right ^= work; - left ^= work << 8; - work = ((left >> 2) ^ right) & 0x33333333; - right ^= work; - left ^= work << 2; - work = ((right >> 16) ^ left) & 0xffff; - left ^= work; - right ^= work << 16; - work = ((right >> 4) ^ left) & 0x0f0f0f0f; - left ^= work; - right ^= work << 4; -} -*/ - -// Wei Dai's modification to Richard Outerbridge's initial permutation -// algorithm, this one is faster if you have access to rotate instructions -// (like in MSVC) -static inline void IPERM(word32 &left, word32 &right) -{ - word32 work; - - right = rotlFixed(right, 4U); - work = (left ^ right) & 0xf0f0f0f0; - left ^= work; - right = rotrFixed(right^work, 20U); - work = (left ^ right) & 0xffff0000; - left ^= work; - right = rotrFixed(right^work, 18U); - work = (left ^ right) & 0x33333333; - left ^= work; - right = rotrFixed(right^work, 6U); - work = (left ^ right) & 0x00ff00ff; - left ^= work; - right = rotlFixed(right^work, 9U); - work = (left ^ right) & 0xaaaaaaaa; - left = rotlFixed(left^work, 1U); - right ^= work; -} - -static inline void FPERM(word32 &left, word32 &right) -{ - word32 work; - - right = rotrFixed(right, 1U); - work = (left ^ right) & 0xaaaaaaaa; - right ^= work; - left = rotrFixed(left^work, 9U); - work = (left ^ right) & 0x00ff00ff; - right ^= work; - left = rotlFixed(left^work, 6U); - work = (left ^ right) & 0x33333333; - right ^= work; - left = rotlFixed(left^work, 18U); - work = (left ^ right) & 0xffff0000; - right ^= work; - left = rotlFixed(left^work, 20U); - work = (left ^ right) & 0xf0f0f0f0; - right ^= work; - left = rotrFixed(left^work, 4U); -} - -void DES::Base::UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs &) -{ - AssertValidKeyLength(length); - - RawSetKey(GetCipherDirection(), userKey); -} - -#ifndef CRYPTOPP_IMPORTS - -/* Tables defined in the Data Encryption Standard documents - * Three of these tables, the initial permutation, the final - * permutation and the expansion operator, are regular enough that - * for speed, we hard-code them. They're here for reference only. - * Also, the S and P boxes are used by a separate program, gensp.c, - * to build the combined SP box, Spbox[]. They're also here just - * for reference. - */ -#ifdef notdef -/* initial permutation IP */ -static byte ip[] = { - 58, 50, 42, 34, 26, 18, 10, 2, - 60, 52, 44, 36, 28, 20, 12, 4, - 62, 54, 46, 38, 30, 22, 14, 6, - 64, 56, 48, 40, 32, 24, 16, 8, - 57, 49, 41, 33, 25, 17, 9, 1, - 59, 51, 43, 35, 27, 19, 11, 3, - 61, 53, 45, 37, 29, 21, 13, 5, - 63, 55, 47, 39, 31, 23, 15, 7 -}; - -/* final permutation IP^-1 */ -static byte fp[] = { - 40, 8, 48, 16, 56, 24, 64, 32, - 39, 7, 47, 15, 55, 23, 63, 31, - 38, 6, 46, 14, 54, 22, 62, 30, - 37, 5, 45, 13, 53, 21, 61, 29, - 36, 4, 44, 12, 52, 20, 60, 28, - 35, 3, 43, 11, 51, 19, 59, 27, - 34, 2, 42, 10, 50, 18, 58, 26, - 33, 1, 41, 9, 49, 17, 57, 25 -}; -/* expansion operation matrix */ -static byte ei[] = { - 32, 1, 2, 3, 4, 5, - 4, 5, 6, 7, 8, 9, - 8, 9, 10, 11, 12, 13, - 12, 13, 14, 15, 16, 17, - 16, 17, 18, 19, 20, 21, - 20, 21, 22, 23, 24, 25, - 24, 25, 26, 27, 28, 29, - 28, 29, 30, 31, 32, 1 -}; -/* The (in)famous S-boxes */ -static byte sbox[8][64] = { - /* S1 */ - 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, - 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, - 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, - 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13, - - /* S2 */ - 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, - 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, - 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, - 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9, - - /* S3 */ - 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, - 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, - 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, - 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12, - - /* S4 */ - 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, - 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, - 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, - 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14, - - /* S5 */ - 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, - 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, - 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, - 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3, - - /* S6 */ - 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, - 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, - 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, - 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13, - - /* S7 */ - 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, - 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, - 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, - 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12, - - /* S8 */ - 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, - 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, - 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, - 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 -}; - -/* 32-bit permutation function P used on the output of the S-boxes */ -static byte p32i[] = { - 16, 7, 20, 21, - 29, 12, 28, 17, - 1, 15, 23, 26, - 5, 18, 31, 10, - 2, 8, 24, 14, - 32, 27, 3, 9, - 19, 13, 30, 6, - 22, 11, 4, 25 -}; -#endif - -/* permuted choice table (key) */ -static const byte pc1[] = { - 57, 49, 41, 33, 25, 17, 9, - 1, 58, 50, 42, 34, 26, 18, - 10, 2, 59, 51, 43, 35, 27, - 19, 11, 3, 60, 52, 44, 36, - - 63, 55, 47, 39, 31, 23, 15, - 7, 62, 54, 46, 38, 30, 22, - 14, 6, 61, 53, 45, 37, 29, - 21, 13, 5, 28, 20, 12, 4 -}; - -/* number left rotations of pc1 */ -static const byte totrot[] = { - 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 -}; - -/* permuted choice key (table) */ -static const byte pc2[] = { - 14, 17, 11, 24, 1, 5, - 3, 28, 15, 6, 21, 10, - 23, 19, 12, 4, 26, 8, - 16, 7, 27, 20, 13, 2, - 41, 52, 31, 37, 47, 55, - 30, 40, 51, 45, 33, 48, - 44, 49, 39, 56, 34, 53, - 46, 42, 50, 36, 29, 32 -}; - -/* End of DES-defined tables */ - -/* bit 0 is left-most in byte */ -static const int bytebit[] = { - 0200,0100,040,020,010,04,02,01 -}; - -/* Set key (initialize key schedule array) */ -void RawDES::RawSetKey(CipherDir dir, const byte *key) -{ - SecByteBlock buffer(56+56+8); - byte *const pc1m=buffer; /* place to modify pc1 into */ - byte *const pcr=pc1m+56; /* place to rotate pc1 into */ - byte *const ks=pcr+56; - register int i,j,l; - int m; - - for (j=0; j<56; j++) { /* convert pc1 to bits of key */ - l=pc1[j]-1; /* integer bit location */ - m = l & 07; /* find bit */ - pc1m[j]=(key[l>>3] & /* find which key byte l is in */ - bytebit[m]) /* and which bit of that byte */ - ? 1 : 0; /* and store 1-bit result */ - } - for (i=0; i<16; i++) { /* key chunk for each iteration */ - memset(ks,0,8); /* Clear key schedule */ - for (j=0; j<56; j++) /* rotate pc1 the right amount */ - pcr[j] = pc1m[(l=j+totrot[i])<(j<28? 28 : 56) ? l: l-28]; - /* rotate left and right halves independently */ - for (j=0; j<48; j++){ /* select bits individually */ - /* check bit that goes to ks[j] */ - if (pcr[pc2[j]-1]){ - /* mask it in if it's there */ - l= j % 6; - ks[j/6] |= bytebit[l] >> 2; - } - } - /* Now convert to odd/even interleaved form for use in F */ - k[2*i] = ((word32)ks[0] << 24) - | ((word32)ks[2] << 16) - | ((word32)ks[4] << 8) - | ((word32)ks[6]); - k[2*i+1] = ((word32)ks[1] << 24) - | ((word32)ks[3] << 16) - | ((word32)ks[5] << 8) - | ((word32)ks[7]); - } - - if (dir==DECRYPTION) // reverse key schedule order - for (i=0; i<16; i+=2) - { - std::swap(k[i], k[32-2-i]); - std::swap(k[i+1], k[32-1-i]); - } -} - -void RawDES::RawProcessBlock(word32 &l_, word32 &r_) const -{ - word32 l = l_, r = r_; - const word32 *kptr=k; - - for (unsigned i=0; i<8; i++) - { - word32 work = rotrFixed(r, 4U) ^ kptr[4*i+0]; - l ^= Spbox[6][(work) & 0x3f] - ^ Spbox[4][(work >> 8) & 0x3f] - ^ Spbox[2][(work >> 16) & 0x3f] - ^ Spbox[0][(work >> 24) & 0x3f]; - work = r ^ kptr[4*i+1]; - l ^= Spbox[7][(work) & 0x3f] - ^ Spbox[5][(work >> 8) & 0x3f] - ^ Spbox[3][(work >> 16) & 0x3f] - ^ Spbox[1][(work >> 24) & 0x3f]; - - work = rotrFixed(l, 4U) ^ kptr[4*i+2]; - r ^= Spbox[6][(work) & 0x3f] - ^ Spbox[4][(work >> 8) & 0x3f] - ^ Spbox[2][(work >> 16) & 0x3f] - ^ Spbox[0][(work >> 24) & 0x3f]; - work = l ^ kptr[4*i+3]; - r ^= Spbox[7][(work) & 0x3f] - ^ Spbox[5][(work >> 8) & 0x3f] - ^ Spbox[3][(work >> 16) & 0x3f] - ^ Spbox[1][(work >> 24) & 0x3f]; - } - - l_ = l; r_ = r; -} - -void DES_EDE2::Base::UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs &) -{ - AssertValidKeyLength(length); - - m_des1.RawSetKey(GetCipherDirection(), userKey); - m_des2.RawSetKey(ReverseCipherDir(GetCipherDirection()), userKey+8); -} - -void DES_EDE2::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 l,r; - Block::Get(inBlock)(l)(r); - IPERM(l,r); - m_des1.RawProcessBlock(l, r); - m_des2.RawProcessBlock(r, l); - m_des1.RawProcessBlock(l, r); - FPERM(l,r); - Block::Put(xorBlock, outBlock)(r)(l); -} - -void DES_EDE3::Base::UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs &) -{ - AssertValidKeyLength(length); - - m_des1.RawSetKey(GetCipherDirection(), userKey + (IsForwardTransformation() ? 0 : 16)); - m_des2.RawSetKey(ReverseCipherDir(GetCipherDirection()), userKey + 8); - m_des3.RawSetKey(GetCipherDirection(), userKey + (IsForwardTransformation() ? 16 : 0)); -} - -void DES_EDE3::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 l,r; - Block::Get(inBlock)(l)(r); - IPERM(l,r); - m_des1.RawProcessBlock(l, r); - m_des2.RawProcessBlock(r, l); - m_des3.RawProcessBlock(l, r); - FPERM(l,r); - Block::Put(xorBlock, outBlock)(r)(l); -} - -#endif // #ifndef CRYPTOPP_IMPORTS - -static inline bool CheckParity(byte b) -{ - unsigned int a = b ^ (b >> 4); - return ((a ^ (a>>1) ^ (a>>2) ^ (a>>3)) & 1) == 1; -} - -bool DES::CheckKeyParityBits(const byte *key) -{ - for (unsigned int i=0; i<8; i++) - if (!CheckParity(key[i])) - return false; - return true; -} - -void DES::CorrectKeyParityBits(byte *key) -{ - for (unsigned int i=0; i<8; i++) - if (!CheckParity(key[i])) - key[i] ^= 1; -} - -// Encrypt or decrypt a block of data in ECB mode -void DES::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 l,r; - Block::Get(inBlock)(l)(r); - IPERM(l,r); - RawProcessBlock(l, r); - FPERM(l,r); - Block::Put(xorBlock, outBlock)(r)(l); -} - -void DES_XEX3::Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &) -{ - AssertValidKeyLength(length); - - if (!m_des.get()) - m_des.reset(new DES::Encryption); - - memcpy(m_x1, key + (IsForwardTransformation() ? 0 : 16), BLOCKSIZE); - m_des->RawSetKey(GetCipherDirection(), key + 8); - memcpy(m_x3, key + (IsForwardTransformation() ? 16 : 0), BLOCKSIZE); -} - -void DES_XEX3::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - xorbuf(outBlock, inBlock, m_x1, BLOCKSIZE); - m_des->ProcessAndXorBlock(outBlock, xorBlock, outBlock); - xorbuf(outBlock, m_x3, BLOCKSIZE); -} - -NAMESPACE_END diff --git a/CryptoPP/des.h b/CryptoPP/des.h deleted file mode 100644 index 62f628824..000000000 --- a/CryptoPP/des.h +++ /dev/null @@ -1,144 +0,0 @@ -#ifndef CRYPTOPP_DES_H -#define CRYPTOPP_DES_H - -/** \file -*/ - -#include "seckey.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -class CRYPTOPP_DLL RawDES -{ -public: - void RawSetKey(CipherDir direction, const byte *userKey); - void RawProcessBlock(word32 &l, word32 &r) const; - -protected: - static const word32 Spbox[8][64]; - - FixedSizeSecBlock k; -}; - -//! _ -struct DES_Info : public FixedBlockSize<8>, public FixedKeyLength<8> -{ - // disable DES in DLL version by not exporting this function - static const char * StaticAlgorithmName() {return "DES";} -}; - -/// DES -/*! The DES implementation in Crypto++ ignores the parity bits - (the least significant bits of each byte) in the key. However - you can use CheckKeyParityBits() and CorrectKeyParityBits() to - check or correct the parity bits if you wish. */ -class DES : public DES_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl, public RawDES - { - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms); - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - -public: - //! check DES key parity bits - static bool CheckKeyParityBits(const byte *key); - //! correct DES key parity bits - static void CorrectKeyParityBits(byte *key); - - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -//! _ -struct DES_EDE2_Info : public FixedBlockSize<8>, public FixedKeyLength<16> -{ - CRYPTOPP_DLL static const char * CRYPTOPP_API StaticAlgorithmName() {return "DES-EDE2";} -}; - -/// DES-EDE2 -class DES_EDE2 : public DES_EDE2_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl - { - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms); - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - - protected: - RawDES m_des1, m_des2; - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -//! _ -struct DES_EDE3_Info : public FixedBlockSize<8>, public FixedKeyLength<24> -{ - CRYPTOPP_DLL static const char * CRYPTOPP_API StaticAlgorithmName() {return "DES-EDE3";} -}; - -/// DES-EDE3 -class DES_EDE3 : public DES_EDE3_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl - { - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms); - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - - protected: - RawDES m_des1, m_des2, m_des3; - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -//! _ -struct DES_XEX3_Info : public FixedBlockSize<8>, public FixedKeyLength<24> -{ - static const char *StaticAlgorithmName() {return "DES-XEX3";} -}; - -/// DES-XEX3, AKA DESX -class DES_XEX3 : public DES_XEX3_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl - { - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms); - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - - protected: - FixedSizeSecBlock m_x1, m_x3; - // VS2005 workaround: calling modules compiled with /clr gets unresolved external symbol DES::Base::ProcessAndXorBlock - // if we use DES::Encryption here directly without value_ptr. - value_ptr m_des; - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -typedef DES::Encryption DESEncryption; -typedef DES::Decryption DESDecryption; - -typedef DES_EDE2::Encryption DES_EDE2_Encryption; -typedef DES_EDE2::Decryption DES_EDE2_Decryption; - -typedef DES_EDE3::Encryption DES_EDE3_Encryption; -typedef DES_EDE3::Decryption DES_EDE3_Decryption; - -typedef DES_XEX3::Encryption DES_XEX3_Encryption; -typedef DES_XEX3::Decryption DES_XEX3_Decryption; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/dessp.cpp b/CryptoPP/dessp.cpp deleted file mode 100644 index 49ed1d26d..000000000 --- a/CryptoPP/dessp.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// This file is mostly generated by Phil Karn's gensp.c - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "des.h" - -NAMESPACE_BEGIN(CryptoPP) - -// VC60 workaround: gives a C4786 warning without this function -// when runtime lib is set to multithread debug DLL -// even though warning 4786 is disabled! -void DES_VC60Workaround() -{ -} - -const word32 RawDES::Spbox[8][64] = { -{ -0x01010400,0x00000000,0x00010000,0x01010404, 0x01010004,0x00010404,0x00000004,0x00010000, -0x00000400,0x01010400,0x01010404,0x00000400, 0x01000404,0x01010004,0x01000000,0x00000004, -0x00000404,0x01000400,0x01000400,0x00010400, 0x00010400,0x01010000,0x01010000,0x01000404, -0x00010004,0x01000004,0x01000004,0x00010004, 0x00000000,0x00000404,0x00010404,0x01000000, -0x00010000,0x01010404,0x00000004,0x01010000, 0x01010400,0x01000000,0x01000000,0x00000400, -0x01010004,0x00010000,0x00010400,0x01000004, 0x00000400,0x00000004,0x01000404,0x00010404, -0x01010404,0x00010004,0x01010000,0x01000404, 0x01000004,0x00000404,0x00010404,0x01010400, -0x00000404,0x01000400,0x01000400,0x00000000, 0x00010004,0x00010400,0x00000000,0x01010004}, -{ -0x80108020,0x80008000,0x00008000,0x00108020, 0x00100000,0x00000020,0x80100020,0x80008020, -0x80000020,0x80108020,0x80108000,0x80000000, 0x80008000,0x00100000,0x00000020,0x80100020, -0x00108000,0x00100020,0x80008020,0x00000000, 0x80000000,0x00008000,0x00108020,0x80100000, -0x00100020,0x80000020,0x00000000,0x00108000, 0x00008020,0x80108000,0x80100000,0x00008020, -0x00000000,0x00108020,0x80100020,0x00100000, 0x80008020,0x80100000,0x80108000,0x00008000, -0x80100000,0x80008000,0x00000020,0x80108020, 0x00108020,0x00000020,0x00008000,0x80000000, -0x00008020,0x80108000,0x00100000,0x80000020, 0x00100020,0x80008020,0x80000020,0x00100020, -0x00108000,0x00000000,0x80008000,0x00008020, 0x80000000,0x80100020,0x80108020,0x00108000}, -{ -0x00000208,0x08020200,0x00000000,0x08020008, 0x08000200,0x00000000,0x00020208,0x08000200, -0x00020008,0x08000008,0x08000008,0x00020000, 0x08020208,0x00020008,0x08020000,0x00000208, -0x08000000,0x00000008,0x08020200,0x00000200, 0x00020200,0x08020000,0x08020008,0x00020208, -0x08000208,0x00020200,0x00020000,0x08000208, 0x00000008,0x08020208,0x00000200,0x08000000, -0x08020200,0x08000000,0x00020008,0x00000208, 0x00020000,0x08020200,0x08000200,0x00000000, -0x00000200,0x00020008,0x08020208,0x08000200, 0x08000008,0x00000200,0x00000000,0x08020008, -0x08000208,0x00020000,0x08000000,0x08020208, 0x00000008,0x00020208,0x00020200,0x08000008, -0x08020000,0x08000208,0x00000208,0x08020000, 0x00020208,0x00000008,0x08020008,0x00020200}, -{ -0x00802001,0x00002081,0x00002081,0x00000080, 0x00802080,0x00800081,0x00800001,0x00002001, -0x00000000,0x00802000,0x00802000,0x00802081, 0x00000081,0x00000000,0x00800080,0x00800001, -0x00000001,0x00002000,0x00800000,0x00802001, 0x00000080,0x00800000,0x00002001,0x00002080, -0x00800081,0x00000001,0x00002080,0x00800080, 0x00002000,0x00802080,0x00802081,0x00000081, -0x00800080,0x00800001,0x00802000,0x00802081, 0x00000081,0x00000000,0x00000000,0x00802000, -0x00002080,0x00800080,0x00800081,0x00000001, 0x00802001,0x00002081,0x00002081,0x00000080, -0x00802081,0x00000081,0x00000001,0x00002000, 0x00800001,0x00002001,0x00802080,0x00800081, -0x00002001,0x00002080,0x00800000,0x00802001, 0x00000080,0x00800000,0x00002000,0x00802080}, -{ -0x00000100,0x02080100,0x02080000,0x42000100, 0x00080000,0x00000100,0x40000000,0x02080000, -0x40080100,0x00080000,0x02000100,0x40080100, 0x42000100,0x42080000,0x00080100,0x40000000, -0x02000000,0x40080000,0x40080000,0x00000000, 0x40000100,0x42080100,0x42080100,0x02000100, -0x42080000,0x40000100,0x00000000,0x42000000, 0x02080100,0x02000000,0x42000000,0x00080100, -0x00080000,0x42000100,0x00000100,0x02000000, 0x40000000,0x02080000,0x42000100,0x40080100, -0x02000100,0x40000000,0x42080000,0x02080100, 0x40080100,0x00000100,0x02000000,0x42080000, -0x42080100,0x00080100,0x42000000,0x42080100, 0x02080000,0x00000000,0x40080000,0x42000000, -0x00080100,0x02000100,0x40000100,0x00080000, 0x00000000,0x40080000,0x02080100,0x40000100}, -{ -0x20000010,0x20400000,0x00004000,0x20404010, 0x20400000,0x00000010,0x20404010,0x00400000, -0x20004000,0x00404010,0x00400000,0x20000010, 0x00400010,0x20004000,0x20000000,0x00004010, -0x00000000,0x00400010,0x20004010,0x00004000, 0x00404000,0x20004010,0x00000010,0x20400010, -0x20400010,0x00000000,0x00404010,0x20404000, 0x00004010,0x00404000,0x20404000,0x20000000, -0x20004000,0x00000010,0x20400010,0x00404000, 0x20404010,0x00400000,0x00004010,0x20000010, -0x00400000,0x20004000,0x20000000,0x00004010, 0x20000010,0x20404010,0x00404000,0x20400000, -0x00404010,0x20404000,0x00000000,0x20400010, 0x00000010,0x00004000,0x20400000,0x00404010, -0x00004000,0x00400010,0x20004010,0x00000000, 0x20404000,0x20000000,0x00400010,0x20004010}, -{ -0x00200000,0x04200002,0x04000802,0x00000000, 0x00000800,0x04000802,0x00200802,0x04200800, -0x04200802,0x00200000,0x00000000,0x04000002, 0x00000002,0x04000000,0x04200002,0x00000802, -0x04000800,0x00200802,0x00200002,0x04000800, 0x04000002,0x04200000,0x04200800,0x00200002, -0x04200000,0x00000800,0x00000802,0x04200802, 0x00200800,0x00000002,0x04000000,0x00200800, -0x04000000,0x00200800,0x00200000,0x04000802, 0x04000802,0x04200002,0x04200002,0x00000002, -0x00200002,0x04000000,0x04000800,0x00200000, 0x04200800,0x00000802,0x00200802,0x04200800, -0x00000802,0x04000002,0x04200802,0x04200000, 0x00200800,0x00000000,0x00000002,0x04200802, -0x00000000,0x00200802,0x04200000,0x00000800, 0x04000002,0x04000800,0x00000800,0x00200002}, -{ -0x10001040,0x00001000,0x00040000,0x10041040, 0x10000000,0x10001040,0x00000040,0x10000000, -0x00040040,0x10040000,0x10041040,0x00041000, 0x10041000,0x00041040,0x00001000,0x00000040, -0x10040000,0x10000040,0x10001000,0x00001040, 0x00041000,0x00040040,0x10040040,0x10041000, -0x00001040,0x00000000,0x00000000,0x10040040, 0x10000040,0x10001000,0x00041040,0x00040000, -0x00041040,0x00040000,0x10041000,0x00001000, 0x00000040,0x10040040,0x00001000,0x00041040, -0x10001000,0x00000040,0x10000040,0x10040000, 0x10040040,0x10000000,0x00040000,0x10001040, -0x00000000,0x10041040,0x00040040,0x10000040, 0x10040000,0x10001000,0x10001040,0x00000000, -0x10041040,0x00041000,0x00041000,0x00001040, 0x00001040,0x00040040,0x10000000,0x10041000} -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/dh.cpp b/CryptoPP/dh.cpp deleted file mode 100644 index 22097a051..000000000 --- a/CryptoPP/dh.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// dh.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "dh.h" - -NAMESPACE_BEGIN(CryptoPP) - -void DH_TestInstantiations() -{ - DH dh1; - DH dh2(NullRNG(), 10); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/dh.h b/CryptoPP/dh.h deleted file mode 100644 index 10e8d142e..000000000 --- a/CryptoPP/dh.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef CRYPTOPP_DH_H -#define CRYPTOPP_DH_H - -/** \file -*/ - -#include "gfpcrypt.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! , -template -class DH_Domain : public DL_SimpleKeyAgreementDomainBase -{ - typedef DL_SimpleKeyAgreementDomainBase Base; - -public: - typedef GROUP_PARAMETERS GroupParameters; - typedef typename GroupParameters::Element Element; - typedef DL_KeyAgreementAlgorithm_DH DH_Algorithm; - typedef DH_Domain Domain; - - DH_Domain() {} - - DH_Domain(const GroupParameters ¶ms) - : m_groupParameters(params) {} - - DH_Domain(BufferedTransformation &bt) - {m_groupParameters.BERDecode(bt);} - - template - DH_Domain(RandomNumberGenerator &v1, const T2 &v2) - {m_groupParameters.Initialize(v1, v2);} - - template - DH_Domain(RandomNumberGenerator &v1, const T2 &v2, const T3 &v3) - {m_groupParameters.Initialize(v1, v2, v3);} - - template - DH_Domain(RandomNumberGenerator &v1, const T2 &v2, const T3 &v3, const T4 &v4) - {m_groupParameters.Initialize(v1, v2, v3, v4);} - - template - DH_Domain(const T1 &v1, const T2 &v2) - {m_groupParameters.Initialize(v1, v2);} - - template - DH_Domain(const T1 &v1, const T2 &v2, const T3 &v3) - {m_groupParameters.Initialize(v1, v2, v3);} - - template - DH_Domain(const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4) - {m_groupParameters.Initialize(v1, v2, v3, v4);} - - const GroupParameters & GetGroupParameters() const {return m_groupParameters;} - GroupParameters & AccessGroupParameters() {return m_groupParameters;} - - void GeneratePublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const - { - Base::GeneratePublicKey(rng, privateKey, publicKey); - - if (FIPS_140_2_ComplianceEnabled()) - { - SecByteBlock privateKey2(this->PrivateKeyLength()); - this->GeneratePrivateKey(rng, privateKey2); - - SecByteBlock publicKey2(this->PublicKeyLength()); - Base::GeneratePublicKey(rng, privateKey2, publicKey2); - - SecByteBlock agreedValue(this->AgreedValueLength()), agreedValue2(this->AgreedValueLength()); - bool agreed1 = this->Agree(agreedValue, privateKey, publicKey2); - bool agreed2 = this->Agree(agreedValue2, privateKey2, publicKey); - - if (!agreed1 || !agreed2 || agreedValue != agreedValue2) - throw SelfTestFailure(this->AlgorithmName() + ": pairwise consistency test failed"); - } - } - - static std::string CRYPTOPP_API StaticAlgorithmName() - {return GroupParameters::StaticAlgorithmNamePrefix() + DH_Algorithm::StaticAlgorithmName();} - std::string AlgorithmName() const {return StaticAlgorithmName();} - -private: - const DL_KeyAgreementAlgorithm & GetKeyAgreementAlgorithm() const - {return Singleton().Ref();} - DL_GroupParameters & AccessAbstractGroupParameters() - {return m_groupParameters;} - - GroupParameters m_groupParameters; -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS DH_Domain; - -//! Diffie-Hellman in GF(p) with key validation -typedef DH_Domain DH; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/dh2.cpp b/CryptoPP/dh2.cpp deleted file mode 100644 index 98175ee28..000000000 --- a/CryptoPP/dh2.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// dh2.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "dh2.h" - -NAMESPACE_BEGIN(CryptoPP) - -void DH2_TestInstantiations() -{ - DH2 dh(*(SimpleKeyAgreementDomain*)NULL); -} - -bool DH2::Agree(byte *agreedValue, - const byte *staticSecretKey, const byte *ephemeralSecretKey, - const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey, - bool validateStaticOtherPublicKey) const -{ - return d1.Agree(agreedValue, staticSecretKey, staticOtherPublicKey, validateStaticOtherPublicKey) - && d2.Agree(agreedValue+d1.AgreedValueLength(), ephemeralSecretKey, ephemeralOtherPublicKey, true); -} - -NAMESPACE_END diff --git a/CryptoPP/dh2.h b/CryptoPP/dh2.h deleted file mode 100644 index af9d342d6..000000000 --- a/CryptoPP/dh2.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef CRYPTOPP_DH2_H -#define CRYPTOPP_DH2_H - -/** \file -*/ - -#include "cryptlib.h" - -NAMESPACE_BEGIN(CryptoPP) - -/// Unified Diffie-Hellman -class DH2 : public AuthenticatedKeyAgreementDomain -{ -public: - DH2(SimpleKeyAgreementDomain &domain) - : d1(domain), d2(domain) {} - DH2(SimpleKeyAgreementDomain &staticDomain, SimpleKeyAgreementDomain &ephemeralDomain) - : d1(staticDomain), d2(ephemeralDomain) {} - - CryptoParameters & AccessCryptoParameters() {return d1.AccessCryptoParameters();} - - unsigned int AgreedValueLength() const - {return d1.AgreedValueLength() + d2.AgreedValueLength();} - - unsigned int StaticPrivateKeyLength() const - {return d1.PrivateKeyLength();} - unsigned int StaticPublicKeyLength() const - {return d1.PublicKeyLength();} - void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const - {d1.GeneratePrivateKey(rng, privateKey);} - void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const - {d1.GeneratePublicKey(rng, privateKey, publicKey);} - void GenerateStaticKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const - {d1.GenerateKeyPair(rng, privateKey, publicKey);} - - unsigned int EphemeralPrivateKeyLength() const - {return d2.PrivateKeyLength();} - unsigned int EphemeralPublicKeyLength() const - {return d2.PublicKeyLength();} - void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const - {d2.GeneratePrivateKey(rng, privateKey);} - void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const - {d2.GeneratePublicKey(rng, privateKey, publicKey);} - void GenerateEphemeralKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const - {d2.GenerateKeyPair(rng, privateKey, publicKey);} - - bool Agree(byte *agreedValue, - const byte *staticPrivateKey, const byte *ephemeralPrivateKey, - const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey, - bool validateStaticOtherPublicKey=true) const; - -protected: - SimpleKeyAgreementDomain &d1, &d2; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/dll.cpp b/CryptoPP/dll.cpp deleted file mode 100644 index 2b4ef7ade..000000000 --- a/CryptoPP/dll.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// dll.cpp - written and placed in the public domain by Wei Dai - -#define CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES -#define CRYPTOPP_DEFAULT_NO_DLL - -#include "dll.h" -#pragma warning(default: 4660) - -#if defined(CRYPTOPP_EXPORTS) && defined(CRYPTOPP_WIN32_AVAILABLE) -#include -#endif - -#ifndef CRYPTOPP_IMPORTS - -NAMESPACE_BEGIN(CryptoPP) - -template<> const byte PKCS_DigestDecoration::decoration[] = {0x30,0x21,0x30,0x09,0x06,0x05,0x2B,0x0E,0x03,0x02,0x1A,0x05,0x00,0x04,0x14}; -template<> const unsigned int PKCS_DigestDecoration::length = sizeof(PKCS_DigestDecoration::decoration); - -template<> const byte PKCS_DigestDecoration::decoration[] = {0x30,0x2d,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x04,0x05,0x00,0x04,0x1c}; -template<> const unsigned int PKCS_DigestDecoration::length = sizeof(PKCS_DigestDecoration::decoration); - -template<> const byte PKCS_DigestDecoration::decoration[] = {0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20}; -template<> const unsigned int PKCS_DigestDecoration::length = sizeof(PKCS_DigestDecoration::decoration); - -template<> const byte PKCS_DigestDecoration::decoration[] = {0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,0x30}; -template<> const unsigned int PKCS_DigestDecoration::length = sizeof(PKCS_DigestDecoration::decoration); - -template<> const byte PKCS_DigestDecoration::decoration[] = {0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40}; -template<> const unsigned int PKCS_DigestDecoration::length = sizeof(PKCS_DigestDecoration::decoration); - -template<> const byte EMSA2HashId::id = 0x33; -template<> const byte EMSA2HashId::id = 0x38; -template<> const byte EMSA2HashId::id = 0x34; -template<> const byte EMSA2HashId::id = 0x36; -template<> const byte EMSA2HashId::id = 0x35; - -NAMESPACE_END - -#endif - -#ifdef CRYPTOPP_EXPORTS - -USING_NAMESPACE(CryptoPP) - -#if !(defined(_MSC_VER) && (_MSC_VER < 1300)) -using std::set_new_handler; -#endif - -static PNew s_pNew = NULL; -static PDelete s_pDelete = NULL; - -static void * New (size_t size) -{ - void *p; - while (!(p = malloc(size))) - CallNewHandler(); - - return p; -} - -static void SetNewAndDeleteFunctionPointers() -{ - void *p = NULL; - HMODULE hModule = NULL; - MEMORY_BASIC_INFORMATION mbi; - - while (true) - { - VirtualQuery(p, &mbi, sizeof(mbi)); - - if (p >= (char *)mbi.BaseAddress + mbi.RegionSize) - break; - - p = (char *)mbi.BaseAddress + mbi.RegionSize; - - if (!mbi.AllocationBase || mbi.AllocationBase == hModule) - continue; - - hModule = HMODULE(mbi.AllocationBase); - - PGetNewAndDelete pGetNewAndDelete = (PGetNewAndDelete)GetProcAddress(hModule, "GetNewAndDeleteForCryptoPP"); - if (pGetNewAndDelete) - { - pGetNewAndDelete(s_pNew, s_pDelete); - return; - } - - PSetNewAndDelete pSetNewAndDelete = (PSetNewAndDelete)GetProcAddress(hModule, "SetNewAndDeleteFromCryptoPP"); - if (pSetNewAndDelete) - { - s_pNew = &New; - s_pDelete = &free; - pSetNewAndDelete(s_pNew, s_pDelete, &set_new_handler); - return; - } - } - - // try getting these directly using mangled names of new and delete operators - - hModule = GetModuleHandle("msvcrtd"); - if (!hModule) - hModule = GetModuleHandle("msvcrt"); - if (hModule) - { - // 32-bit versions - s_pNew = (PNew)GetProcAddress(hModule, "??2@YAPAXI@Z"); - s_pDelete = (PDelete)GetProcAddress(hModule, "??3@YAXPAX@Z"); - if (s_pNew && s_pDelete) - return; - - // 64-bit versions - s_pNew = (PNew)GetProcAddress(hModule, "??2@YAPEAX_K@Z"); - s_pDelete = (PDelete)GetProcAddress(hModule, "??3@YAXPEAX@Z"); - if (s_pNew && s_pDelete) - return; - } - - OutputDebugString("Crypto++ was not able to obtain new and delete function pointers.\n"); - throw 0; -} - -void * operator new (size_t size) -{ - if (!s_pNew) - SetNewAndDeleteFunctionPointers(); - - return s_pNew(size); -} - -void operator delete (void * p) -{ - s_pDelete(p); -} - -void * operator new [] (size_t size) -{ - return operator new (size); -} - -void operator delete [] (void * p) -{ - operator delete (p); -} - -#endif // #ifdef CRYPTOPP_EXPORTS diff --git a/CryptoPP/dll.h b/CryptoPP/dll.h deleted file mode 100644 index 50775e98b..000000000 --- a/CryptoPP/dll.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef CRYPTOPP_DLL_H -#define CRYPTOPP_DLL_H - -#if !defined(CRYPTOPP_IMPORTS) && !defined(CRYPTOPP_EXPORTS) && !defined(CRYPTOPP_DEFAULT_NO_DLL) -#ifdef CRYPTOPP_CONFIG_H -#error To use the DLL version of Crypto++, this file must be included before any other Crypto++ header files. -#endif -#define CRYPTOPP_IMPORTS -#endif - -#include "aes.h" -#include "cbcmac.h" -#include "ccm.h" -#include "cmac.h" -#include "channels.h" -#include "des.h" -#include "dh.h" -#include "dsa.h" -#include "ec2n.h" -#include "eccrypto.h" -#include "ecp.h" -#include "files.h" -#include "fips140.h" -#include "gcm.h" -#include "hex.h" -#include "hmac.h" -#include "modes.h" -#include "mqueue.h" -#include "nbtheory.h" -#include "osrng.h" -#include "pkcspad.h" -#include "pssr.h" -#include "randpool.h" -#include "rsa.h" -#include "rw.h" -#include "sha.h" -#include "trdlocal.h" - -#ifdef CRYPTOPP_IMPORTS - -#ifdef _DLL -// cause CRT DLL to be initialized before Crypto++ so that we can use malloc and free during DllMain() -#ifdef NDEBUG -#pragma comment(lib, "msvcrt") -#else -#pragma comment(lib, "msvcrtd") -#endif -#endif - -#pragma comment(lib, "cryptopp") - -#endif // #ifdef CRYPTOPP_IMPORTS - -#include // for new_handler - -NAMESPACE_BEGIN(CryptoPP) - -#if !(defined(_MSC_VER) && (_MSC_VER < 1300)) -using std::new_handler; -#endif - -typedef void * (CRYPTOPP_API * PNew)(size_t); -typedef void (CRYPTOPP_API * PDelete)(void *); -typedef void (CRYPTOPP_API * PGetNewAndDelete)(PNew &, PDelete &); -typedef new_handler (CRYPTOPP_API * PSetNewHandler)(new_handler); -typedef void (CRYPTOPP_API * PSetNewAndDelete)(PNew, PDelete, PSetNewHandler); - -NAMESPACE_END - -#endif diff --git a/CryptoPP/dmac.h b/CryptoPP/dmac.h deleted file mode 100644 index 80b54ac2f..000000000 --- a/CryptoPP/dmac.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef CRYPTOPP_DMAC_H -#define CRYPTOPP_DMAC_H - -#include "cbcmac.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -template -class CRYPTOPP_NO_VTABLE DMAC_Base : public SameKeyLengthAs, public MessageAuthenticationCode -{ -public: - static std::string StaticAlgorithmName() {return std::string("DMAC(") + T::StaticAlgorithmName() + ")";} - - CRYPTOPP_CONSTANT(DIGESTSIZE=T::BLOCKSIZE) - - DMAC_Base() {} - - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms); - void Update(const byte *input, size_t length); - void TruncatedFinal(byte *mac, size_t size); - unsigned int DigestSize() const {return DIGESTSIZE;} - -private: - byte *GenerateSubKeys(const byte *key, size_t keylength); - - size_t m_subkeylength; - SecByteBlock m_subkeys; - CBC_MAC m_mac1; - typename T::Encryption m_f2; - unsigned int m_counter; -}; - -//! DMAC -/*! Based on "CBC MAC for Real-Time Data Sources" by Erez Petrank - and Charles Rackoff. T should be a class derived from BlockCipherDocumentation. -*/ -template -class DMAC : public MessageAuthenticationCodeFinal > -{ -public: - DMAC() {} - DMAC(const byte *key, size_t length=DMAC_Base::DEFAULT_KEYLENGTH) - {this->SetKey(key, length);} -}; - -template -void DMAC_Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) -{ - m_subkeylength = T::StaticGetValidKeyLength(T::BLOCKSIZE); - m_subkeys.resize(2*UnsignedMin((unsigned int)T::BLOCKSIZE, m_subkeylength)); - m_mac1.SetKey(GenerateSubKeys(key, length), m_subkeylength, params); - m_f2.SetKey(m_subkeys+m_subkeys.size()/2, m_subkeylength, params); - m_counter = 0; - m_subkeys.resize(0); -} - -template -void DMAC_Base::Update(const byte *input, size_t length) -{ - m_mac1.Update(input, length); - m_counter = (unsigned int)((m_counter + length) % T::BLOCKSIZE); -} - -template -void DMAC_Base::TruncatedFinal(byte *mac, size_t size) -{ - ThrowIfInvalidTruncatedSize(size); - - byte pad[T::BLOCKSIZE]; - byte padByte = byte(T::BLOCKSIZE-m_counter); - memset(pad, padByte, padByte); - m_mac1.Update(pad, padByte); - m_mac1.TruncatedFinal(mac, size); - m_f2.ProcessBlock(mac); - - m_counter = 0; // reset for next message -} - -template -byte *DMAC_Base::GenerateSubKeys(const byte *key, size_t keylength) -{ - typename T::Encryption cipher(key, keylength); - memset(m_subkeys, 0, m_subkeys.size()); - cipher.ProcessBlock(m_subkeys); - m_subkeys[m_subkeys.size()/2 + T::BLOCKSIZE - 1] = 1; - cipher.ProcessBlock(m_subkeys+m_subkeys.size()/2); - return m_subkeys; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/dsa.cpp b/CryptoPP/dsa.cpp deleted file mode 100644 index 5aace4857..000000000 --- a/CryptoPP/dsa.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// dsa.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "dsa.h" -#include "nbtheory.h" - -NAMESPACE_BEGIN(CryptoPP) - -size_t DSAConvertSignatureFormat(byte *buffer, size_t bufferSize, DSASignatureFormat toFormat, const byte *signature, size_t signatureLen, DSASignatureFormat fromFormat) -{ - Integer r, s; - StringStore store(signature, signatureLen); - ArraySink sink(buffer, bufferSize); - - switch (fromFormat) - { - case DSA_P1363: - r.Decode(store, signatureLen/2); - s.Decode(store, signatureLen/2); - break; - case DSA_DER: - { - BERSequenceDecoder seq(store); - r.BERDecode(seq); - s.BERDecode(seq); - seq.MessageEnd(); - break; - } - case DSA_OPENPGP: - r.OpenPGPDecode(store); - s.OpenPGPDecode(store); - break; - } - - switch (toFormat) - { - case DSA_P1363: - r.Encode(sink, bufferSize/2); - s.Encode(sink, bufferSize/2); - break; - case DSA_DER: - { - DERSequenceEncoder seq(sink); - r.DEREncode(seq); - s.DEREncode(seq); - seq.MessageEnd(); - break; - } - case DSA_OPENPGP: - r.OpenPGPEncode(sink); - s.OpenPGPEncode(sink); - break; - } - - return (size_t)sink.TotalPutLength(); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/dsa.h b/CryptoPP/dsa.h deleted file mode 100644 index 6ae03877c..000000000 --- a/CryptoPP/dsa.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CRYPTOPP_DSA_H -#define CRYPTOPP_DSA_H - -/** \file -*/ - -#include "gfpcrypt.h" - -NAMESPACE_BEGIN(CryptoPP) - -/*! The DSA signature format used by Crypto++ is as defined by IEEE P1363. - Java uses the DER format, and OpenPGP uses the OpenPGP format. */ -enum DSASignatureFormat {DSA_P1363, DSA_DER, DSA_OPENPGP}; -/** This function converts between these formats, and returns length of signature in the target format. - If toFormat == DSA_P1363, bufferSize must equal publicKey.SignatureLength() */ -size_t DSAConvertSignatureFormat(byte *buffer, size_t bufferSize, DSASignatureFormat toFormat, - const byte *signature, size_t signatureLen, DSASignatureFormat fromFormat); - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - -typedef DSA::Signer DSAPrivateKey; -typedef DSA::Verifier DSAPublicKey; - -const int MIN_DSA_PRIME_LENGTH = DSA::MIN_PRIME_LENGTH; -const int MAX_DSA_PRIME_LENGTH = DSA::MAX_PRIME_LENGTH; -const int DSA_PRIME_LENGTH_MULTIPLE = DSA::PRIME_LENGTH_MULTIPLE; - -inline bool GenerateDSAPrimes(const byte *seed, size_t seedLength, int &counter, Integer &p, unsigned int primeLength, Integer &q) - {return DSA::GeneratePrimes(seed, seedLength, counter, p, primeLength, q);} - -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/eax.cpp b/CryptoPP/eax.cpp deleted file mode 100644 index 2728c9bcd..000000000 --- a/CryptoPP/eax.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// eax.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "eax.h" - -NAMESPACE_BEGIN(CryptoPP) - -void EAX_Base::SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms) -{ - AccessMAC().SetKey(userKey, keylength, params); - m_buffer.New(2*AccessMAC().TagSize()); -} - -void EAX_Base::Resync(const byte *iv, size_t len) -{ - MessageAuthenticationCode &mac = AccessMAC(); - unsigned int blockSize = mac.TagSize(); - - memset(m_buffer, 0, blockSize); - mac.Update(m_buffer, blockSize); - mac.CalculateDigest(m_buffer+blockSize, iv, len); - - m_buffer[blockSize-1] = 1; - mac.Update(m_buffer, blockSize); - - m_ctr.SetCipherWithIV(AccessMAC().AccessCipher(), m_buffer+blockSize, blockSize); -} - -size_t EAX_Base::AuthenticateBlocks(const byte *data, size_t len) -{ - AccessMAC().Update(data, len); - return 0; -} - -void EAX_Base::AuthenticateLastHeaderBlock() -{ - assert(m_bufferedDataLength == 0); - MessageAuthenticationCode &mac = AccessMAC(); - unsigned int blockSize = mac.TagSize(); - - mac.Final(m_buffer); - xorbuf(m_buffer+blockSize, m_buffer, blockSize); - - memset(m_buffer, 0, blockSize); - m_buffer[blockSize-1] = 2; - mac.Update(m_buffer, blockSize); -} - -void EAX_Base::AuthenticateLastFooterBlock(byte *tag, size_t macSize) -{ - assert(m_bufferedDataLength == 0); - MessageAuthenticationCode &mac = AccessMAC(); - unsigned int blockSize = mac.TagSize(); - - mac.TruncatedFinal(m_buffer, macSize); - xorbuf(tag, m_buffer, m_buffer+blockSize, macSize); -} - -NAMESPACE_END diff --git a/CryptoPP/eax.h b/CryptoPP/eax.h deleted file mode 100644 index e48ee92b5..000000000 --- a/CryptoPP/eax.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef CRYPTOPP_EAX_H -#define CRYPTOPP_EAX_H - -#include "authenc.h" -#include "modes.h" -#include "cmac.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! . -class CRYPTOPP_NO_VTABLE EAX_Base : public AuthenticatedSymmetricCipherBase -{ -public: - // AuthenticatedSymmetricCipher - std::string AlgorithmName() const - {return GetMAC().GetCipher().AlgorithmName() + std::string("/EAX");} - size_t MinKeyLength() const - {return GetMAC().MinKeyLength();} - size_t MaxKeyLength() const - {return GetMAC().MaxKeyLength();} - size_t DefaultKeyLength() const - {return GetMAC().DefaultKeyLength();} - size_t GetValidKeyLength(size_t n) const - {return GetMAC().GetValidKeyLength(n);} - bool IsValidKeyLength(size_t n) const - {return GetMAC().IsValidKeyLength(n);} - unsigned int OptimalDataAlignment() const - {return GetMAC().OptimalDataAlignment();} - IV_Requirement IVRequirement() const - {return UNIQUE_IV;} - unsigned int IVSize() const - {return GetMAC().TagSize();} - unsigned int MinIVLength() const - {return 0;} - unsigned int MaxIVLength() const - {return UINT_MAX;} - unsigned int DigestSize() const - {return GetMAC().TagSize();} - lword MaxHeaderLength() const - {return LWORD_MAX;} - lword MaxMessageLength() const - {return LWORD_MAX;} - -protected: - // AuthenticatedSymmetricCipherBase - bool AuthenticationIsOnPlaintext() const - {return false;} - unsigned int AuthenticationBlockSize() const - {return 1;} - void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms); - void Resync(const byte *iv, size_t len); - size_t AuthenticateBlocks(const byte *data, size_t len); - void AuthenticateLastHeaderBlock(); - void AuthenticateLastFooterBlock(byte *mac, size_t macSize); - SymmetricCipher & AccessSymmetricCipher() {return m_ctr;} - const CMAC_Base & GetMAC() const {return const_cast(this)->AccessMAC();} - virtual CMAC_Base & AccessMAC() =0; - - CTR_Mode_ExternalCipher::Encryption m_ctr; -}; - -//! . -template -class EAX_Final : public EAX_Base -{ -public: - static std::string StaticAlgorithmName() - {return T_BlockCipher::StaticAlgorithmName() + std::string("/EAX");} - bool IsForwardTransformation() const - {return T_IsEncryption;} - -private: - CMAC_Base & AccessMAC() {return m_cmac;} - CMAC m_cmac; -}; - -#ifdef EAX // EAX is defined to 11 on GCC 3.4.3, OpenSolaris 8.11 -#undef EAX -#endif - -/// EAX -template -struct EAX : public AuthenticatedSymmetricCipherDocumentation -{ - typedef EAX_Final Encryption; - typedef EAX_Final Decryption; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/ec2n.cpp b/CryptoPP/ec2n.cpp deleted file mode 100644 index b513b2cb8..000000000 --- a/CryptoPP/ec2n.cpp +++ /dev/null @@ -1,292 +0,0 @@ -// ec2n.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "ec2n.h" -#include "asn.h" - -#include "algebra.cpp" -#include "eprecomp.cpp" - -NAMESPACE_BEGIN(CryptoPP) - -EC2N::EC2N(BufferedTransformation &bt) - : m_field(BERDecodeGF2NP(bt)) -{ - BERSequenceDecoder seq(bt); - m_field->BERDecodeElement(seq, m_a); - m_field->BERDecodeElement(seq, m_b); - // skip optional seed - if (!seq.EndReached()) - { - SecByteBlock seed; - unsigned int unused; - BERDecodeBitString(seq, seed, unused); - } - seq.MessageEnd(); -} - -void EC2N::DEREncode(BufferedTransformation &bt) const -{ - m_field->DEREncode(bt); - DERSequenceEncoder seq(bt); - m_field->DEREncodeElement(seq, m_a); - m_field->DEREncodeElement(seq, m_b); - seq.MessageEnd(); -} - -bool EC2N::DecodePoint(EC2N::Point &P, const byte *encodedPoint, size_t encodedPointLen) const -{ - StringStore store(encodedPoint, encodedPointLen); - return DecodePoint(P, store, encodedPointLen); -} - -bool EC2N::DecodePoint(EC2N::Point &P, BufferedTransformation &bt, size_t encodedPointLen) const -{ - byte type; - if (encodedPointLen < 1 || !bt.Get(type)) - return false; - - switch (type) - { - case 0: - P.identity = true; - return true; - case 2: - case 3: - { - if (encodedPointLen != EncodedPointSize(true)) - return false; - - P.identity = false; - P.x.Decode(bt, m_field->MaxElementByteLength()); - - if (P.x.IsZero()) - { - P.y = m_field->SquareRoot(m_b); - return true; - } - - FieldElement z = m_field->Square(P.x); - assert(P.x == m_field->SquareRoot(z)); - P.y = m_field->Divide(m_field->Add(m_field->Multiply(z, m_field->Add(P.x, m_a)), m_b), z); - assert(P.x == m_field->Subtract(m_field->Divide(m_field->Subtract(m_field->Multiply(P.y, z), m_b), z), m_a)); - z = m_field->SolveQuadraticEquation(P.y); - assert(m_field->Add(m_field->Square(z), z) == P.y); - z.SetCoefficient(0, type & 1); - - P.y = m_field->Multiply(z, P.x); - return true; - } - case 4: - { - if (encodedPointLen != EncodedPointSize(false)) - return false; - - unsigned int len = m_field->MaxElementByteLength(); - P.identity = false; - P.x.Decode(bt, len); - P.y.Decode(bt, len); - return true; - } - default: - return false; - } -} - -void EC2N::EncodePoint(BufferedTransformation &bt, const Point &P, bool compressed) const -{ - if (P.identity) - NullStore().TransferTo(bt, EncodedPointSize(compressed)); - else if (compressed) - { - bt.Put(2 + (!P.x ? 0 : m_field->Divide(P.y, P.x).GetBit(0))); - P.x.Encode(bt, m_field->MaxElementByteLength()); - } - else - { - unsigned int len = m_field->MaxElementByteLength(); - bt.Put(4); // uncompressed - P.x.Encode(bt, len); - P.y.Encode(bt, len); - } -} - -void EC2N::EncodePoint(byte *encodedPoint, const Point &P, bool compressed) const -{ - ArraySink sink(encodedPoint, EncodedPointSize(compressed)); - EncodePoint(sink, P, compressed); - assert(sink.TotalPutLength() == EncodedPointSize(compressed)); -} - -EC2N::Point EC2N::BERDecodePoint(BufferedTransformation &bt) const -{ - SecByteBlock str; - BERDecodeOctetString(bt, str); - Point P; - if (!DecodePoint(P, str, str.size())) - BERDecodeError(); - return P; -} - -void EC2N::DEREncodePoint(BufferedTransformation &bt, const Point &P, bool compressed) const -{ - SecByteBlock str(EncodedPointSize(compressed)); - EncodePoint(str, P, compressed); - DEREncodeOctetString(bt, str); -} - -bool EC2N::ValidateParameters(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = !!m_b; - pass = pass && m_a.CoefficientCount() <= m_field->MaxElementBitLength(); - pass = pass && m_b.CoefficientCount() <= m_field->MaxElementBitLength(); - - if (level >= 1) - pass = pass && m_field->GetModulus().IsIrreducible(); - - return pass; -} - -bool EC2N::VerifyPoint(const Point &P) const -{ - const FieldElement &x = P.x, &y = P.y; - return P.identity || - (x.CoefficientCount() <= m_field->MaxElementBitLength() - && y.CoefficientCount() <= m_field->MaxElementBitLength() - && !(((x+m_a)*x*x+m_b-(x+y)*y)%m_field->GetModulus())); -} - -bool EC2N::Equal(const Point &P, const Point &Q) const -{ - if (P.identity && Q.identity) - return true; - - if (P.identity && !Q.identity) - return false; - - if (!P.identity && Q.identity) - return false; - - return (m_field->Equal(P.x,Q.x) && m_field->Equal(P.y,Q.y)); -} - -const EC2N::Point& EC2N::Identity() const -{ - return Singleton().Ref(); -} - -const EC2N::Point& EC2N::Inverse(const Point &P) const -{ - if (P.identity) - return P; - else - { - m_R.identity = false; - m_R.y = m_field->Add(P.x, P.y); - m_R.x = P.x; - return m_R; - } -} - -const EC2N::Point& EC2N::Add(const Point &P, const Point &Q) const -{ - if (P.identity) return Q; - if (Q.identity) return P; - if (Equal(P, Q)) return Double(P); - if (m_field->Equal(P.x, Q.x) && m_field->Equal(P.y, m_field->Add(Q.x, Q.y))) return Identity(); - - FieldElement t = m_field->Add(P.y, Q.y); - t = m_field->Divide(t, m_field->Add(P.x, Q.x)); - FieldElement x = m_field->Square(t); - m_field->Accumulate(x, t); - m_field->Accumulate(x, Q.x); - m_field->Accumulate(x, m_a); - m_R.y = m_field->Add(P.y, m_field->Multiply(t, x)); - m_field->Accumulate(x, P.x); - m_field->Accumulate(m_R.y, x); - - m_R.x.swap(x); - m_R.identity = false; - return m_R; -} - -const EC2N::Point& EC2N::Double(const Point &P) const -{ - if (P.identity) return P; - if (!m_field->IsUnit(P.x)) return Identity(); - - FieldElement t = m_field->Divide(P.y, P.x); - m_field->Accumulate(t, P.x); - m_R.y = m_field->Square(P.x); - m_R.x = m_field->Square(t); - m_field->Accumulate(m_R.x, t); - m_field->Accumulate(m_R.x, m_a); - m_field->Accumulate(m_R.y, m_field->Multiply(t, m_R.x)); - m_field->Accumulate(m_R.y, m_R.x); - - m_R.identity = false; - return m_R; -} - -// ******************************************************** - -/* -EcPrecomputation& EcPrecomputation::operator=(const EcPrecomputation &rhs) -{ - m_ec = rhs.m_ec; - m_ep = rhs.m_ep; - m_ep.m_group = m_ec.get(); - return *this; -} - -void EcPrecomputation::SetCurveAndBase(const EC2N &ec, const EC2N::Point &base) -{ - m_ec.reset(new EC2N(ec)); - m_ep.SetGroupAndBase(*m_ec, base); -} - -void EcPrecomputation::Precompute(unsigned int maxExpBits, unsigned int storage) -{ - m_ep.Precompute(maxExpBits, storage); -} - -void EcPrecomputation::Load(BufferedTransformation &bt) -{ - BERSequenceDecoder seq(bt); - word32 version; - BERDecodeUnsigned(seq, version, INTEGER, 1, 1); - m_ep.m_exponentBase.BERDecode(seq); - m_ep.m_windowSize = m_ep.m_exponentBase.BitCount() - 1; - m_ep.m_bases.clear(); - while (!seq.EndReached()) - m_ep.m_bases.push_back(m_ec->BERDecodePoint(seq)); - seq.MessageEnd(); -} - -void EcPrecomputation::Save(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - DEREncodeUnsigned(seq, 1); // version - m_ep.m_exponentBase.DEREncode(seq); - for (unsigned i=0; iDEREncodePoint(seq, m_ep.m_bases[i]); - seq.MessageEnd(); -} - -EC2N::Point EcPrecomputation::Exponentiate(const Integer &exponent) const -{ - return m_ep.Exponentiate(exponent); -} - -EC2N::Point EcPrecomputation::CascadeExponentiate(const Integer &exponent, const DL_FixedBasePrecomputation &pc2, const Integer &exponent2) const -{ - return m_ep.CascadeExponentiate(exponent, static_cast &>(pc2).m_ep, exponent2); -} -*/ - -NAMESPACE_END - -#endif diff --git a/CryptoPP/ec2n.h b/CryptoPP/ec2n.h deleted file mode 100644 index ae4007cd6..000000000 --- a/CryptoPP/ec2n.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef CRYPTOPP_EC2N_H -#define CRYPTOPP_EC2N_H - -#include "gf2n.h" -#include "eprecomp.h" -#include "smartptr.h" -#include "pubkey.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! Elliptic Curve Point -struct CRYPTOPP_DLL EC2NPoint -{ - EC2NPoint() : identity(true) {} - EC2NPoint(const PolynomialMod2 &x, const PolynomialMod2 &y) - : identity(false), x(x), y(y) {} - - bool operator==(const EC2NPoint &t) const - {return (identity && t.identity) || (!identity && !t.identity && x==t.x && y==t.y);} - bool operator< (const EC2NPoint &t) const - {return identity ? !t.identity : (!t.identity && (x; - -//! Elliptic Curve over GF(2^n) -class CRYPTOPP_DLL EC2N : public AbstractGroup -{ -public: - typedef GF2NP Field; - typedef Field::Element FieldElement; - typedef EC2NPoint Point; - - EC2N() {} - EC2N(const Field &field, const Field::Element &a, const Field::Element &b) - : m_field(field), m_a(a), m_b(b) {} - // construct from BER encoded parameters - // this constructor will decode and extract the the fields fieldID and curve of the sequence ECParameters - EC2N(BufferedTransformation &bt); - - // encode the fields fieldID and curve of the sequence ECParameters - void DEREncode(BufferedTransformation &bt) const; - - bool Equal(const Point &P, const Point &Q) const; - const Point& Identity() const; - const Point& Inverse(const Point &P) const; - bool InversionIsFast() const {return true;} - const Point& Add(const Point &P, const Point &Q) const; - const Point& Double(const Point &P) const; - - Point Multiply(const Integer &k, const Point &P) const - {return ScalarMultiply(P, k);} - Point CascadeMultiply(const Integer &k1, const Point &P, const Integer &k2, const Point &Q) const - {return CascadeScalarMultiply(P, k1, Q, k2);} - - bool ValidateParameters(RandomNumberGenerator &rng, unsigned int level=3) const; - bool VerifyPoint(const Point &P) const; - - unsigned int EncodedPointSize(bool compressed = false) const - {return 1 + (compressed?1:2)*m_field->MaxElementByteLength();} - // returns false if point is compressed and not valid (doesn't check if uncompressed) - bool DecodePoint(Point &P, BufferedTransformation &bt, size_t len) const; - bool DecodePoint(Point &P, const byte *encodedPoint, size_t len) const; - void EncodePoint(byte *encodedPoint, const Point &P, bool compressed) const; - void EncodePoint(BufferedTransformation &bt, const Point &P, bool compressed) const; - - Point BERDecodePoint(BufferedTransformation &bt) const; - void DEREncodePoint(BufferedTransformation &bt, const Point &P, bool compressed) const; - - Integer FieldSize() const {return Integer::Power2(m_field->MaxElementBitLength());} - const Field & GetField() const {return *m_field;} - const FieldElement & GetA() const {return m_a;} - const FieldElement & GetB() const {return m_b;} - - bool operator==(const EC2N &rhs) const - {return GetField() == rhs.GetField() && m_a == rhs.m_a && m_b == rhs.m_b;} - -private: - clonable_ptr m_field; - FieldElement m_a, m_b; - mutable Point m_R; -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS DL_FixedBasePrecomputationImpl; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_GroupPrecomputation; - -template class EcPrecomputation; - -//! EC2N precomputation -template<> class EcPrecomputation : public DL_GroupPrecomputation -{ -public: - typedef EC2N EllipticCurve; - - // DL_GroupPrecomputation - const AbstractGroup & GetGroup() const {return m_ec;} - Element BERDecodeElement(BufferedTransformation &bt) const {return m_ec.BERDecodePoint(bt);} - void DEREncodeElement(BufferedTransformation &bt, const Element &v) const {m_ec.DEREncodePoint(bt, v, false);} - - // non-inherited - void SetCurve(const EC2N &ec) {m_ec = ec;} - const EC2N & GetCurve() const {return m_ec;} - -private: - EC2N m_ec; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/eccrypto.cpp b/CryptoPP/eccrypto.cpp deleted file mode 100644 index 922104c4d..000000000 --- a/CryptoPP/eccrypto.cpp +++ /dev/null @@ -1,694 +0,0 @@ -// eccrypto.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "eccrypto.h" -#include "nbtheory.h" -#include "oids.h" -#include "hex.h" -#include "argnames.h" -#include "ec2n.h" - -NAMESPACE_BEGIN(CryptoPP) - -#if 0 -static void ECDSA_TestInstantiations() -{ - ECDSA::Signer t1; - ECDSA::Verifier t2(t1); - ECNR::Signer t3; - ECNR::Verifier t4(t3); - ECIES::Encryptor t5; - ECIES::Decryptor t6; - ECDH::Domain t7; - ECMQV::Domain t8; -} -#endif - -// VC60 workaround: complains when these functions are put into an anonymous namespace -static Integer ConvertToInteger(const PolynomialMod2 &x) -{ - unsigned int l = x.ByteCount(); - SecByteBlock temp(l); - x.Encode(temp, l); - return Integer(temp, l); -} - -static inline Integer ConvertToInteger(const Integer &x) -{ - return x; -} - -static bool CheckMOVCondition(const Integer &q, const Integer &r) -{ - // see "Updated standards for validating elliptic curves", http://eprint.iacr.org/2007/343 - Integer t = 1; - unsigned int n = q.IsEven() ? 1 : q.BitCount(), m = r.BitCount(); - - for (unsigned int i=n; DiscreteLogWorkFactor(i) struct EcRecommendedParameters; - -template<> struct EcRecommendedParameters -{ - EcRecommendedParameters(const OID &oid, unsigned int t2, unsigned int t3, unsigned int t4, const char *a, const char *b, const char *g, const char *n, unsigned int h) - : oid(oid), t0(0), t1(0), t2(t2), t3(t3), t4(t4), a(a), b(b), g(g), n(n), h(h) {} - EcRecommendedParameters(const OID &oid, unsigned int t0, unsigned int t1, unsigned int t2, unsigned int t3, unsigned int t4, const char *a, const char *b, const char *g, const char *n, unsigned int h) - : oid(oid), t0(t0), t1(t1), t2(t2), t3(t3), t4(t4), a(a), b(b), g(g), n(n), h(h) {} - EC2N *NewEC() const - { - StringSource ssA(a, true, new HexDecoder); - StringSource ssB(b, true, new HexDecoder); - if (t0 == 0) - return new EC2N(GF2NT(t2, t3, t4), EC2N::FieldElement(ssA, (size_t)ssA.MaxRetrievable()), EC2N::FieldElement(ssB, (size_t)ssB.MaxRetrievable())); - else - return new EC2N(GF2NPP(t0, t1, t2, t3, t4), EC2N::FieldElement(ssA, (size_t)ssA.MaxRetrievable()), EC2N::FieldElement(ssB, (size_t)ssB.MaxRetrievable())); - }; - - OID oid; - unsigned int t0, t1, t2, t3, t4; - const char *a, *b, *g, *n; - unsigned int h; -}; - -template<> struct EcRecommendedParameters -{ - EcRecommendedParameters(const OID &oid, const char *p, const char *a, const char *b, const char *g, const char *n, unsigned int h) - : oid(oid), p(p), a(a), b(b), g(g), n(n), h(h) {} - ECP *NewEC() const - { - StringSource ssP(p, true, new HexDecoder); - StringSource ssA(a, true, new HexDecoder); - StringSource ssB(b, true, new HexDecoder); - return new ECP(Integer(ssP, (size_t)ssP.MaxRetrievable()), ECP::FieldElement(ssA, (size_t)ssA.MaxRetrievable()), ECP::FieldElement(ssB, (size_t)ssB.MaxRetrievable())); - }; - - OID oid; - const char *p; - const char *a, *b, *g, *n; - unsigned int h; -}; - -struct OIDLessThan -{ - template - inline bool operator()(const EcRecommendedParameters& a, const OID& b) {return a.oid < b;} - template - inline bool operator()(const OID& a, const EcRecommendedParameters& b) {return a < b.oid;} - template - inline bool operator()(const EcRecommendedParameters& a, const EcRecommendedParameters& b) {return a.oid < b.oid;} -}; - -static void GetRecommendedParameters(const EcRecommendedParameters *&begin, const EcRecommendedParameters *&end) -{ - // this array must be sorted by OID - static const EcRecommendedParameters rec[] = { - EcRecommendedParameters(ASN1::sect163k1(), - 163, 7, 6, 3, 0, - "000000000000000000000000000000000000000001", - "000000000000000000000000000000000000000001", - "0402FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE80289070FB05D38FF58321F2E800536D538CCDAA3D9", - "04000000000000000000020108A2E0CC0D99F8A5EF", - 2), - EcRecommendedParameters(ASN1::sect163r1(), - 163, 7, 6, 3, 0, - "07B6882CAAEFA84F9554FF8428BD88E246D2782AE2", - "0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9", - "040369979697AB43897789566789567F787A7876A65400435EDB42EFAFB2989D51FEFCE3C80988F41FF883", - "03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B", - 2), - EcRecommendedParameters(ASN1::sect239k1(), - 239, 158, 0, - "000000000000000000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000000000000000000000001", - "0429A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA", - "2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5", - 4), - EcRecommendedParameters(ASN1::sect113r1(), - 113, 9, 0, - "003088250CA6E7C7FE649CE85820F7", - "00E8BEE4D3E2260744188BE0E9C723", - "04009D73616F35F4AB1407D73562C10F00A52830277958EE84D1315ED31886", - "0100000000000000D9CCEC8A39E56F", - 2), - EcRecommendedParameters(ASN1::sect113r2(), - 113, 9, 0, - "00689918DBEC7E5A0DD6DFC0AA55C7", - "0095E9A9EC9B297BD4BF36E059184F", - "0401A57A6A7B26CA5EF52FCDB816479700B3ADC94ED1FE674C06E695BABA1D", - "010000000000000108789B2496AF93", - 2), - EcRecommendedParameters(ASN1::sect163r2(), - 163, 7, 6, 3, 0, - "000000000000000000000000000000000000000001", - "020A601907B8C953CA1481EB10512F78744A3205FD", - "0403F0EBA16286A2D57EA0991168D4994637E8343E3600D51FBC6C71A0094FA2CDD545B11C5C0C797324F1", - "040000000000000000000292FE77E70C12A4234C33", - 2), - EcRecommendedParameters(ASN1::sect283k1(), - 283, 12, 7, 5, 0, - "000000000000000000000000000000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000000000000000000000000000000000001", - "040503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC245849283601CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259", - "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61", - 4), - EcRecommendedParameters(ASN1::sect283r1(), - 283, 12, 7, 5, 0, - "000000000000000000000000000000000000000000000000000000000000000000000001", - "027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5", - "0405F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B1205303676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4", - "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307", - 2), - EcRecommendedParameters(ASN1::sect131r1(), - 131, 8, 3, 2, 0, - "07A11B09A76B562144418FF3FF8C2570B8", - "0217C05610884B63B9C6C7291678F9D341", - "040081BAF91FDF9833C40F9C181343638399078C6E7EA38C001F73C8134B1B4EF9E150", - "0400000000000000023123953A9464B54D", - 2), - EcRecommendedParameters(ASN1::sect131r2(), - 131, 8, 3, 2, 0, - "03E5A88919D7CAFCBF415F07C2176573B2", - "04B8266A46C55657AC734CE38F018F2192", - "040356DCD8F2F95031AD652D23951BB366A80648F06D867940A5366D9E265DE9EB240F", - "0400000000000000016954A233049BA98F", - 2), - EcRecommendedParameters(ASN1::sect193r1(), - 193, 15, 0, - "0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01", - "00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814", - "0401F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E10025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05", - "01000000000000000000000000C7F34A778F443ACC920EBA49", - 2), - EcRecommendedParameters(ASN1::sect193r2(), - 193, 15, 0, - "0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B", - "00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE", - "0400D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C", - "010000000000000000000000015AAB561B005413CCD4EE99D5", - 2), - EcRecommendedParameters(ASN1::sect233k1(), - 233, 74, 0, - "000000000000000000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000000000000000000000001", - "04017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD612601DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3", - "8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF", - 4), - EcRecommendedParameters(ASN1::sect233r1(), - 233, 74, 0, - "000000000000000000000000000000000000000000000000000000000001", - "0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD", - "0400FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052", - "01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7", - 2), - EcRecommendedParameters(ASN1::sect409k1(), - 409, 87, 0, - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", - "040060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE902374601E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B", - "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF", - 4), - EcRecommendedParameters(ASN1::sect409r1(), - 409, 87, 0, - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", - "0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F", - "04015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A70061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706", - "010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173", - 2), - EcRecommendedParameters(ASN1::sect571k1(), - 571, 10, 5, 2, 0, - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", - "04026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C89720349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3", - "020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001", - 4), - EcRecommendedParameters(ASN1::sect571r1(), - 571, 10, 5, 2, 0, - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", - "02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A", - "040303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B", - "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47", - 2), - }; - begin = rec; - end = rec + sizeof(rec)/sizeof(rec[0]); -} - -static void GetRecommendedParameters(const EcRecommendedParameters *&begin, const EcRecommendedParameters *&end) -{ - // this array must be sorted by OID - static const EcRecommendedParameters rec[] = { - EcRecommendedParameters(ASN1::secp192r1(), - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", - "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", - "04188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811", - "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", - 1), - EcRecommendedParameters(ASN1::secp256r1(), - "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", - "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", - "046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", - "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", - 1), - EcRecommendedParameters(ASN1::brainpoolP160r1(), - "E95E4A5F737059DC60DFC7AD95B3D8139515620F", - "340E7BE2A280EB74E2BE61BADA745D97E8F7C300", - "1E589A8595423412134FAA2DBDEC95C8D8675E58", - "04BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC31667CB477A1A8EC338F94741669C976316DA6321", - "E95E4A5F737059DC60DF5991D45029409E60FC09", - 1), - EcRecommendedParameters(ASN1::brainpoolP192r1(), - "C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", - "6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF", - "469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9", - "04C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD614B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F", - "C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1", - 1), - EcRecommendedParameters(ASN1::brainpoolP224r1(), - "D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", - "68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43", - "2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B", - "040D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD", - "D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F", - 1), - EcRecommendedParameters(ASN1::brainpoolP256r1(), - "A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", - "7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", - "26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", - "048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", - "A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", - 1), - EcRecommendedParameters(ASN1::brainpoolP320r1(), - "D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", - "3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4", - "520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6", - "0443BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E2061114FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1", - "D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311", - 1), - EcRecommendedParameters(ASN1::brainpoolP384r1(), - "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", - "7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826", - "04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11", - "041D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315", - "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", - 1), - EcRecommendedParameters(ASN1::brainpoolP512r1(), - "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", - "7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA", - "3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723", - "0481AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F8227DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892", - "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", - 1), - EcRecommendedParameters(ASN1::secp112r1(), - "DB7C2ABF62E35E668076BEAD208B", - "DB7C2ABF62E35E668076BEAD2088", - "659EF8BA043916EEDE8911702B22", - "0409487239995A5EE76B55F9C2F098A89CE5AF8724C0A23E0E0FF77500", - "DB7C2ABF62E35E7628DFAC6561C5", - 1), - EcRecommendedParameters(ASN1::secp112r2(), - "DB7C2ABF62E35E668076BEAD208B", - "6127C24C05F38A0AAAF65C0EF02C", - "51DEF1815DB5ED74FCC34C85D709", - "044BA30AB5E892B4E1649DD0928643ADCD46F5882E3747DEF36E956E97", - "36DF0AAFD8B8D7597CA10520D04B", - 4), - EcRecommendedParameters(ASN1::secp160r1(), - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC", - "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", - "044A96B5688EF573284664698968C38BB913CBFC8223A628553168947D59DCC912042351377AC5FB32", - "0100000000000000000001F4C8F927AED3CA752257", - 1), - EcRecommendedParameters(ASN1::secp160k1(), - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", - "0000000000000000000000000000000000000000", - "0000000000000000000000000000000000000007", - "043B4C382CE37AA192A4019E763036F4F5DD4D7EBB938CF935318FDCED6BC28286531733C3F03C4FEE", - "0100000000000000000001B8FA16DFAB9ACA16B6B3", - 1), - EcRecommendedParameters(ASN1::secp256k1(), - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000007", - "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", - 1), - EcRecommendedParameters(ASN1::secp128r1(), - "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC", - "E87579C11079F43DD824993C2CEE5ED3", - "04161FF7528B899B2D0C28607CA52C5B86CF5AC8395BAFEB13C02DA292DDED7A83", - "FFFFFFFE0000000075A30D1B9038A115", - 1), - EcRecommendedParameters(ASN1::secp128r2(), - "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", - "D6031998D1B3BBFEBF59CC9BBFF9AEE1", - "5EEEFCA380D02919DC2C6558BB6D8A5D", - "047B6AA5D85E572983E6FB32A7CDEBC14027B6916A894D3AEE7106FE805FC34B44", - "3FFFFFFF7FFFFFFFBE0024720613B5A3", - 4), - EcRecommendedParameters(ASN1::secp160r2(), - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70", - "B4E134D3FB59EB8BAB57274904664D5AF50388BA", - "0452DCB034293A117E1F4FF11B30F7199D3144CE6DFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E", - "0100000000000000000000351EE786A818F3A1A16B", - 1), - EcRecommendedParameters(ASN1::secp192k1(), - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", - "000000000000000000000000000000000000000000000000", - "000000000000000000000000000000000000000000000003", - "04DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", - "FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", - 1), - EcRecommendedParameters(ASN1::secp224k1(), - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", - "00000000000000000000000000000000000000000000000000000000", - "00000000000000000000000000000000000000000000000000000005", - "04A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", - "010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", - 1), - EcRecommendedParameters(ASN1::secp224r1(), - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", - "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", - "04B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", - 1), - EcRecommendedParameters(ASN1::secp384r1(), - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", - "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", - "04AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB73617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", - 1), - EcRecommendedParameters(ASN1::secp521r1(), - "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", - "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", - "0400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", - "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", - 1), - }; - begin = rec; - end = rec + sizeof(rec)/sizeof(rec[0]); -} - -template OID DL_GroupParameters_EC::GetNextRecommendedParametersOID(const OID &oid) -{ - const EcRecommendedParameters *begin, *end; - GetRecommendedParameters(begin, end); - const EcRecommendedParameters *it = std::upper_bound(begin, end, oid, OIDLessThan()); - return (it == end ? OID() : it->oid); -} - -template void DL_GroupParameters_EC::Initialize(const OID &oid) -{ - const EcRecommendedParameters *begin, *end; - GetRecommendedParameters(begin, end); - const EcRecommendedParameters *it = std::lower_bound(begin, end, oid, OIDLessThan()); - if (it == end || it->oid != oid) - throw UnknownOID(); - - const EcRecommendedParameters ¶m = *it; - m_oid = oid; - std::auto_ptr ec(param.NewEC()); - this->m_groupPrecomputation.SetCurve(*ec); - - StringSource ssG(param.g, true, new HexDecoder); - Element G; - bool result = GetCurve().DecodePoint(G, ssG, (size_t)ssG.MaxRetrievable()); - this->SetSubgroupGenerator(G); - assert(result); - - StringSource ssN(param.n, true, new HexDecoder); - m_n.Decode(ssN, (size_t)ssN.MaxRetrievable()); - m_k = param.h; -} - -template -bool DL_GroupParameters_EC::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - if (strcmp(name, Name::GroupOID()) == 0) - { - if (m_oid.m_values.empty()) - return false; - - this->ThrowIfTypeMismatch(name, typeid(OID), valueType); - *reinterpret_cast(pValue) = m_oid; - return true; - } - else - return GetValueHelper >(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Curve); -} - -template -void DL_GroupParameters_EC::AssignFrom(const NameValuePairs &source) -{ - OID oid; - if (source.GetValue(Name::GroupOID(), oid)) - Initialize(oid); - else - { - EllipticCurve ec; - Point G; - Integer n; - - source.GetRequiredParameter("DL_GroupParameters_EC", Name::Curve(), ec); - source.GetRequiredParameter("DL_GroupParameters_EC", Name::SubgroupGenerator(), G); - source.GetRequiredParameter("DL_GroupParameters_EC", Name::SubgroupOrder(), n); - Integer k = source.GetValueWithDefault(Name::Cofactor(), Integer::Zero()); - - Initialize(ec, G, n, k); - } -} - -template -void DL_GroupParameters_EC::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg) -{ - try - { - AssignFrom(alg); - } - catch (InvalidArgument &) - { - throw NotImplemented("DL_GroupParameters_EC: curve generation is not implemented yet"); - } -} - -template -void DL_GroupParameters_EC::BERDecode(BufferedTransformation &bt) -{ - byte b; - if (!bt.Peek(b)) - BERDecodeError(); - if (b == OBJECT_IDENTIFIER) - Initialize(OID(bt)); - else - { - BERSequenceDecoder seq(bt); - word32 version; - BERDecodeUnsigned(seq, version, INTEGER, 1, 1); // check version - EllipticCurve ec(seq); - Point G = ec.BERDecodePoint(seq); - Integer n(seq); - Integer k; - bool cofactorPresent = !seq.EndReached(); - if (cofactorPresent) - k.BERDecode(seq); - else - k = Integer::Zero(); - seq.MessageEnd(); - - Initialize(ec, G, n, k); - } -} - -template -void DL_GroupParameters_EC::DEREncode(BufferedTransformation &bt) const -{ - if (m_encodeAsOID && !m_oid.m_values.empty()) - m_oid.DEREncode(bt); - else - { - DERSequenceEncoder seq(bt); - DEREncodeUnsigned(seq, 1); // version - GetCurve().DEREncode(seq); - GetCurve().DEREncodePoint(seq, this->GetSubgroupGenerator(), m_compress); - m_n.DEREncode(seq); - if (m_k.NotZero()) - m_k.DEREncode(seq); - seq.MessageEnd(); - } -} - -template -Integer DL_GroupParameters_EC::GetCofactor() const -{ - if (!m_k) - { - Integer q = GetCurve().FieldSize(); - Integer qSqrt = q.SquareRoot(); - m_k = (q+2*qSqrt+1)/m_n; - } - - return m_k; -} - -template -Integer DL_GroupParameters_EC::ConvertElementToInteger(const Element &element) const -{ - return ConvertToInteger(element.x); -}; - -template -bool DL_GroupParameters_EC::ValidateGroup(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = GetCurve().ValidateParameters(rng, level); - - Integer q = GetCurve().FieldSize(); - pass = pass && m_n!=q; - - if (level >= 2) - { - Integer qSqrt = q.SquareRoot(); - pass = pass && m_n>4*qSqrt; - pass = pass && VerifyPrime(rng, m_n, level-2); - pass = pass && (m_k.IsZero() || m_k == (q+2*qSqrt+1)/m_n); - pass = pass && CheckMOVCondition(q, m_n); - } - - return pass; -} - -template -bool DL_GroupParameters_EC::ValidateElement(unsigned int level, const Element &g, const DL_FixedBasePrecomputation *gpc) const -{ - bool pass = !IsIdentity(g) && GetCurve().VerifyPoint(g); - if (level >= 1) - { - if (gpc) - pass = pass && gpc->Exponentiate(this->GetGroupPrecomputation(), Integer::One()) == g; - } - if (level >= 2 && pass) - { - const Integer &q = GetSubgroupOrder(); - Element gq = gpc ? gpc->Exponentiate(this->GetGroupPrecomputation(), q) : this->ExponentiateElement(g, q); - pass = pass && IsIdentity(gq); - } - return pass; -} - -template -void DL_GroupParameters_EC::SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const -{ - GetCurve().SimultaneousMultiply(results, base, exponents, exponentsCount); -} - -template -CPP_TYPENAME DL_GroupParameters_EC::Element DL_GroupParameters_EC::MultiplyElements(const Element &a, const Element &b) const -{ - return GetCurve().Add(a, b); -} - -template -CPP_TYPENAME DL_GroupParameters_EC::Element DL_GroupParameters_EC::CascadeExponentiate(const Element &element1, const Integer &exponent1, const Element &element2, const Integer &exponent2) const -{ - return GetCurve().CascadeMultiply(exponent1, element1, exponent2, element2); -} - -template -OID DL_GroupParameters_EC::GetAlgorithmID() const -{ - return ASN1::id_ecPublicKey(); -} - -// ****************************************************************** - -template -void DL_PublicKey_EC::BERDecodePublicKey(BufferedTransformation &bt, bool parametersPresent, size_t size) -{ - typename EC::Point P; - if (!this->GetGroupParameters().GetCurve().DecodePoint(P, bt, size)) - BERDecodeError(); - this->SetPublicElement(P); -} - -template -void DL_PublicKey_EC::DEREncodePublicKey(BufferedTransformation &bt) const -{ - this->GetGroupParameters().GetCurve().EncodePoint(bt, this->GetPublicElement(), this->GetGroupParameters().GetPointCompression()); -} - -// ****************************************************************** - -template -void DL_PrivateKey_EC::BERDecodePrivateKey(BufferedTransformation &bt, bool parametersPresent, size_t size) -{ - BERSequenceDecoder seq(bt); - word32 version; - BERDecodeUnsigned(seq, version, INTEGER, 1, 1); // check version - - BERGeneralDecoder dec(seq, OCTET_STRING); - if (!dec.IsDefiniteLength()) - BERDecodeError(); - Integer x; - x.Decode(dec, (size_t)dec.RemainingLength()); - dec.MessageEnd(); - if (!parametersPresent && seq.PeekByte() != (CONTEXT_SPECIFIC | CONSTRUCTED | 0)) - BERDecodeError(); - if (!seq.EndReached() && seq.PeekByte() == (CONTEXT_SPECIFIC | CONSTRUCTED | 0)) - { - BERGeneralDecoder parameters(seq, CONTEXT_SPECIFIC | CONSTRUCTED | 0); - this->AccessGroupParameters().BERDecode(parameters); - parameters.MessageEnd(); - } - if (!seq.EndReached()) - { - // skip over the public element - SecByteBlock subjectPublicKey; - unsigned int unusedBits; - BERGeneralDecoder publicKey(seq, CONTEXT_SPECIFIC | CONSTRUCTED | 1); - BERDecodeBitString(publicKey, subjectPublicKey, unusedBits); - publicKey.MessageEnd(); - Element Q; - if (!(unusedBits == 0 && this->GetGroupParameters().GetCurve().DecodePoint(Q, subjectPublicKey, subjectPublicKey.size()))) - BERDecodeError(); - } - seq.MessageEnd(); - - this->SetPrivateExponent(x); -} - -template -void DL_PrivateKey_EC::DEREncodePrivateKey(BufferedTransformation &bt) const -{ - DERSequenceEncoder privateKey(bt); - DEREncodeUnsigned(privateKey, 1); // version - // SEC 1 ver 1.0 says privateKey (m_d) has the same length as order of the curve - // this will be changed to order of base point in a future version - this->GetPrivateExponent().DEREncodeAsOctetString(privateKey, this->GetGroupParameters().GetSubgroupOrder().ByteCount()); - privateKey.MessageEnd(); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/eccrypto.h b/CryptoPP/eccrypto.h deleted file mode 100644 index 3530455a3..000000000 --- a/CryptoPP/eccrypto.h +++ /dev/null @@ -1,280 +0,0 @@ -#ifndef CRYPTOPP_ECCRYPTO_H -#define CRYPTOPP_ECCRYPTO_H - -/*! \file -*/ - -#include "pubkey.h" -#include "integer.h" -#include "asn.h" -#include "hmac.h" -#include "sha.h" -#include "gfpcrypt.h" -#include "dh.h" -#include "mqv.h" -#include "ecp.h" -#include "ec2n.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! Elliptic Curve Parameters -/*! This class corresponds to the ASN.1 sequence of the same name - in ANSI X9.62 (also SEC 1). -*/ -template -class DL_GroupParameters_EC : public DL_GroupParametersImpl > -{ - typedef DL_GroupParameters_EC ThisClass; - -public: - typedef EC EllipticCurve; - typedef typename EllipticCurve::Point Point; - typedef Point Element; - typedef IncompatibleCofactorMultiplication DefaultCofactorOption; - - DL_GroupParameters_EC() : m_compress(false), m_encodeAsOID(false) {} - DL_GroupParameters_EC(const OID &oid) - : m_compress(false), m_encodeAsOID(false) {Initialize(oid);} - DL_GroupParameters_EC(const EllipticCurve &ec, const Point &G, const Integer &n, const Integer &k = Integer::Zero()) - : m_compress(false), m_encodeAsOID(false) {Initialize(ec, G, n, k);} - DL_GroupParameters_EC(BufferedTransformation &bt) - : m_compress(false), m_encodeAsOID(false) {BERDecode(bt);} - - void Initialize(const EllipticCurve &ec, const Point &G, const Integer &n, const Integer &k = Integer::Zero()) - { - this->m_groupPrecomputation.SetCurve(ec); - this->SetSubgroupGenerator(G); - m_n = n; - m_k = k; - } - void Initialize(const OID &oid); - - // NameValuePairs - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - - // GeneratibleCryptoMaterial interface - //! this implementation doesn't actually generate a curve, it just initializes the parameters with existing values - /*! parameters: (Curve, SubgroupGenerator, SubgroupOrder, Cofactor (optional)), or (GroupOID) */ - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg); - - // DL_GroupParameters - const DL_FixedBasePrecomputation & GetBasePrecomputation() const {return this->m_gpc;} - DL_FixedBasePrecomputation & AccessBasePrecomputation() {return this->m_gpc;} - const Integer & GetSubgroupOrder() const {return m_n;} - Integer GetCofactor() const; - bool ValidateGroup(RandomNumberGenerator &rng, unsigned int level) const; - bool ValidateElement(unsigned int level, const Element &element, const DL_FixedBasePrecomputation *precomp) const; - bool FastSubgroupCheckAvailable() const {return false;} - void EncodeElement(bool reversible, const Element &element, byte *encoded) const - { - if (reversible) - GetCurve().EncodePoint(encoded, element, m_compress); - else - element.x.Encode(encoded, GetEncodedElementSize(false)); - } - unsigned int GetEncodedElementSize(bool reversible) const - { - if (reversible) - return GetCurve().EncodedPointSize(m_compress); - else - return GetCurve().GetField().MaxElementByteLength(); - } - Element DecodeElement(const byte *encoded, bool checkForGroupMembership) const - { - Point result; - if (!GetCurve().DecodePoint(result, encoded, GetEncodedElementSize(true))) - throw DL_BadElement(); - if (checkForGroupMembership && !ValidateElement(1, result, NULL)) - throw DL_BadElement(); - return result; - } - Integer ConvertElementToInteger(const Element &element) const; - Integer GetMaxExponent() const {return GetSubgroupOrder()-1;} - bool IsIdentity(const Element &element) const {return element.identity;} - void SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const; - static std::string CRYPTOPP_API StaticAlgorithmNamePrefix() {return "EC";} - - // ASN1Key - OID GetAlgorithmID() const; - - // used by MQV - Element MultiplyElements(const Element &a, const Element &b) const; - Element CascadeExponentiate(const Element &element1, const Integer &exponent1, const Element &element2, const Integer &exponent2) const; - - // non-inherited - - // enumerate OIDs for recommended parameters, use OID() to get first one - static OID CRYPTOPP_API GetNextRecommendedParametersOID(const OID &oid); - - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - void SetPointCompression(bool compress) {m_compress = compress;} - bool GetPointCompression() const {return m_compress;} - - void SetEncodeAsOID(bool encodeAsOID) {m_encodeAsOID = encodeAsOID;} - bool GetEncodeAsOID() const {return m_encodeAsOID;} - - const EllipticCurve& GetCurve() const {return this->m_groupPrecomputation.GetCurve();} - - bool operator==(const ThisClass &rhs) const - {return this->m_groupPrecomputation.GetCurve() == rhs.m_groupPrecomputation.GetCurve() && this->m_gpc.GetBase(this->m_groupPrecomputation) == rhs.m_gpc.GetBase(rhs.m_groupPrecomputation);} - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - const Point& GetBasePoint() const {return GetSubgroupGenerator();} - const Integer& GetBasePointOrder() const {return GetSubgroupOrder();} - void LoadRecommendedParameters(const OID &oid) {Initialize(oid);} -#endif - -protected: - unsigned int FieldElementLength() const {return GetCurve().GetField().MaxElementByteLength();} - unsigned int ExponentLength() const {return m_n.ByteCount();} - - OID m_oid; // set if parameters loaded from a recommended curve - Integer m_n; // order of base point - bool m_compress, m_encodeAsOID; - mutable Integer m_k; // cofactor -}; - -//! EC public key -template -class DL_PublicKey_EC : public DL_PublicKeyImpl > -{ -public: - typedef typename EC::Point Element; - - void Initialize(const DL_GroupParameters_EC ¶ms, const Element &Q) - {this->AccessGroupParameters() = params; this->SetPublicElement(Q);} - void Initialize(const EC &ec, const Element &G, const Integer &n, const Element &Q) - {this->AccessGroupParameters().Initialize(ec, G, n); this->SetPublicElement(Q);} - - // X509PublicKey - void BERDecodePublicKey(BufferedTransformation &bt, bool parametersPresent, size_t size); - void DEREncodePublicKey(BufferedTransformation &bt) const; -}; - -//! EC private key -template -class DL_PrivateKey_EC : public DL_PrivateKeyImpl > -{ -public: - typedef typename EC::Point Element; - - void Initialize(const DL_GroupParameters_EC ¶ms, const Integer &x) - {this->AccessGroupParameters() = params; this->SetPrivateExponent(x);} - void Initialize(const EC &ec, const Element &G, const Integer &n, const Integer &x) - {this->AccessGroupParameters().Initialize(ec, G, n); this->SetPrivateExponent(x);} - void Initialize(RandomNumberGenerator &rng, const DL_GroupParameters_EC ¶ms) - {this->GenerateRandom(rng, params);} - void Initialize(RandomNumberGenerator &rng, const EC &ec, const Element &G, const Integer &n) - {this->GenerateRandom(rng, DL_GroupParameters_EC(ec, G, n));} - - // PKCS8PrivateKey - void BERDecodePrivateKey(BufferedTransformation &bt, bool parametersPresent, size_t size); - void DEREncodePrivateKey(BufferedTransformation &bt) const; -}; - -//! Elliptic Curve Diffie-Hellman, AKA ECDH -template ::DefaultCofactorOption> -struct ECDH -{ - typedef DH_Domain, COFACTOR_OPTION> Domain; -}; - -/// Elliptic Curve Menezes-Qu-Vanstone, AKA ECMQV -template ::DefaultCofactorOption> -struct ECMQV -{ - typedef MQV_Domain, COFACTOR_OPTION> Domain; -}; - -//! EC keys -template -struct DL_Keys_EC -{ - typedef DL_PublicKey_EC PublicKey; - typedef DL_PrivateKey_EC PrivateKey; -}; - -template -struct ECDSA; - -//! ECDSA keys -template -struct DL_Keys_ECDSA -{ - typedef DL_PublicKey_EC PublicKey; - typedef DL_PrivateKey_WithSignaturePairwiseConsistencyTest, ECDSA > PrivateKey; -}; - -//! ECDSA algorithm -template -class DL_Algorithm_ECDSA : public DL_Algorithm_GDSA -{ -public: - static const char * CRYPTOPP_API StaticAlgorithmName() {return "ECDSA";} -}; - -//! ECNR algorithm -template -class DL_Algorithm_ECNR : public DL_Algorithm_NR -{ -public: - static const char * CRYPTOPP_API StaticAlgorithmName() {return "ECNR";} -}; - -//! ECDSA -template -struct ECDSA : public DL_SS, DL_Algorithm_ECDSA, DL_SignatureMessageEncodingMethod_DSA, H> -{ -}; - -//! ECNR -template -struct ECNR : public DL_SS, DL_Algorithm_ECNR, DL_SignatureMessageEncodingMethod_NR, H> -{ -}; - -//! Elliptic Curve Integrated Encryption Scheme, AKA ECIES -/*! Default to (NoCofactorMultiplication and DHAES_MODE = false) for compatibilty with SEC1 and Crypto++ 4.2. - The combination of (IncompatibleCofactorMultiplication and DHAES_MODE = true) is recommended for best - efficiency and security. */ -template -struct ECIES - : public DL_ES< - DL_Keys_EC, - DL_KeyAgreementAlgorithm_DH, - DL_KeyDerivationAlgorithm_P1363 >, - DL_EncryptionAlgorithm_Xor, DHAES_MODE>, - ECIES > -{ - static std::string CRYPTOPP_API StaticAlgorithmName() {return "ECIES";} // TODO: fix this after name is standardized -}; - -NAMESPACE_END - -#ifdef CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES -#include "eccrypto.cpp" -#endif - -NAMESPACE_BEGIN(CryptoPP) - -CRYPTOPP_DLL_TEMPLATE_CLASS DL_GroupParameters_EC; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_GroupParameters_EC; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PublicKeyImpl >; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PublicKeyImpl >; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PublicKey_EC; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PublicKey_EC; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKeyImpl >; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKeyImpl >; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKey_EC; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKey_EC; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_Algorithm_GDSA; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_Algorithm_GDSA; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKey_WithSignaturePairwiseConsistencyTest, ECDSA >; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKey_WithSignaturePairwiseConsistencyTest, ECDSA >; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/ecp.cpp b/CryptoPP/ecp.cpp deleted file mode 100644 index 55a7cc15b..000000000 --- a/CryptoPP/ecp.cpp +++ /dev/null @@ -1,473 +0,0 @@ -// ecp.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "ecp.h" -#include "asn.h" -#include "nbtheory.h" - -#include "algebra.cpp" - -NAMESPACE_BEGIN(CryptoPP) - -ANONYMOUS_NAMESPACE_BEGIN -static inline ECP::Point ToMontgomery(const ModularArithmetic &mr, const ECP::Point &P) -{ - return P.identity ? P : ECP::Point(mr.ConvertIn(P.x), mr.ConvertIn(P.y)); -} - -static inline ECP::Point FromMontgomery(const ModularArithmetic &mr, const ECP::Point &P) -{ - return P.identity ? P : ECP::Point(mr.ConvertOut(P.x), mr.ConvertOut(P.y)); -} -NAMESPACE_END - -ECP::ECP(const ECP &ecp, bool convertToMontgomeryRepresentation) -{ - if (convertToMontgomeryRepresentation && !ecp.GetField().IsMontgomeryRepresentation()) - { - m_fieldPtr.reset(new MontgomeryRepresentation(ecp.GetField().GetModulus())); - m_a = GetField().ConvertIn(ecp.m_a); - m_b = GetField().ConvertIn(ecp.m_b); - } - else - operator=(ecp); -} - -ECP::ECP(BufferedTransformation &bt) - : m_fieldPtr(new Field(bt)) -{ - BERSequenceDecoder seq(bt); - GetField().BERDecodeElement(seq, m_a); - GetField().BERDecodeElement(seq, m_b); - // skip optional seed - if (!seq.EndReached()) - { - SecByteBlock seed; - unsigned int unused; - BERDecodeBitString(seq, seed, unused); - } - seq.MessageEnd(); -} - -void ECP::DEREncode(BufferedTransformation &bt) const -{ - GetField().DEREncode(bt); - DERSequenceEncoder seq(bt); - GetField().DEREncodeElement(seq, m_a); - GetField().DEREncodeElement(seq, m_b); - seq.MessageEnd(); -} - -bool ECP::DecodePoint(ECP::Point &P, const byte *encodedPoint, size_t encodedPointLen) const -{ - StringStore store(encodedPoint, encodedPointLen); - return DecodePoint(P, store, encodedPointLen); -} - -bool ECP::DecodePoint(ECP::Point &P, BufferedTransformation &bt, size_t encodedPointLen) const -{ - byte type; - if (encodedPointLen < 1 || !bt.Get(type)) - return false; - - switch (type) - { - case 0: - P.identity = true; - return true; - case 2: - case 3: - { - if (encodedPointLen != EncodedPointSize(true)) - return false; - - Integer p = FieldSize(); - - P.identity = false; - P.x.Decode(bt, GetField().MaxElementByteLength()); - P.y = ((P.x*P.x+m_a)*P.x+m_b) % p; - - if (Jacobi(P.y, p) !=1) - return false; - - P.y = ModularSquareRoot(P.y, p); - - if ((type & 1) != P.y.GetBit(0)) - P.y = p-P.y; - - return true; - } - case 4: - { - if (encodedPointLen != EncodedPointSize(false)) - return false; - - unsigned int len = GetField().MaxElementByteLength(); - P.identity = false; - P.x.Decode(bt, len); - P.y.Decode(bt, len); - return true; - } - default: - return false; - } -} - -void ECP::EncodePoint(BufferedTransformation &bt, const Point &P, bool compressed) const -{ - if (P.identity) - NullStore().TransferTo(bt, EncodedPointSize(compressed)); - else if (compressed) - { - bt.Put(2 + P.y.GetBit(0)); - P.x.Encode(bt, GetField().MaxElementByteLength()); - } - else - { - unsigned int len = GetField().MaxElementByteLength(); - bt.Put(4); // uncompressed - P.x.Encode(bt, len); - P.y.Encode(bt, len); - } -} - -void ECP::EncodePoint(byte *encodedPoint, const Point &P, bool compressed) const -{ - ArraySink sink(encodedPoint, EncodedPointSize(compressed)); - EncodePoint(sink, P, compressed); - assert(sink.TotalPutLength() == EncodedPointSize(compressed)); -} - -ECP::Point ECP::BERDecodePoint(BufferedTransformation &bt) const -{ - SecByteBlock str; - BERDecodeOctetString(bt, str); - Point P; - if (!DecodePoint(P, str, str.size())) - BERDecodeError(); - return P; -} - -void ECP::DEREncodePoint(BufferedTransformation &bt, const Point &P, bool compressed) const -{ - SecByteBlock str(EncodedPointSize(compressed)); - EncodePoint(str, P, compressed); - DEREncodeOctetString(bt, str); -} - -bool ECP::ValidateParameters(RandomNumberGenerator &rng, unsigned int level) const -{ - Integer p = FieldSize(); - - bool pass = p.IsOdd(); - pass = pass && !m_a.IsNegative() && m_a

= 1) - pass = pass && ((4*m_a*m_a*m_a+27*m_b*m_b)%p).IsPositive(); - - if (level >= 2) - pass = pass && VerifyPrime(rng, p); - - return pass; -} - -bool ECP::VerifyPoint(const Point &P) const -{ - const FieldElement &x = P.x, &y = P.y; - Integer p = FieldSize(); - return P.identity || - (!x.IsNegative() && x

().Ref(); -} - -const ECP::Point& ECP::Inverse(const Point &P) const -{ - if (P.identity) - return P; - else - { - m_R.identity = false; - m_R.x = P.x; - m_R.y = GetField().Inverse(P.y); - return m_R; - } -} - -const ECP::Point& ECP::Add(const Point &P, const Point &Q) const -{ - if (P.identity) return Q; - if (Q.identity) return P; - if (GetField().Equal(P.x, Q.x)) - return GetField().Equal(P.y, Q.y) ? Double(P) : Identity(); - - FieldElement t = GetField().Subtract(Q.y, P.y); - t = GetField().Divide(t, GetField().Subtract(Q.x, P.x)); - FieldElement x = GetField().Subtract(GetField().Subtract(GetField().Square(t), P.x), Q.x); - m_R.y = GetField().Subtract(GetField().Multiply(t, GetField().Subtract(P.x, x)), P.y); - - m_R.x.swap(x); - m_R.identity = false; - return m_R; -} - -const ECP::Point& ECP::Double(const Point &P) const -{ - if (P.identity || P.y==GetField().Identity()) return Identity(); - - FieldElement t = GetField().Square(P.x); - t = GetField().Add(GetField().Add(GetField().Double(t), t), m_a); - t = GetField().Divide(t, GetField().Double(P.y)); - FieldElement x = GetField().Subtract(GetField().Subtract(GetField().Square(t), P.x), P.x); - m_R.y = GetField().Subtract(GetField().Multiply(t, GetField().Subtract(P.x, x)), P.y); - - m_R.x.swap(x); - m_R.identity = false; - return m_R; -} - -template void ParallelInvert(const AbstractRing &ring, Iterator begin, Iterator end) -{ - size_t n = end-begin; - if (n == 1) - *begin = ring.MultiplicativeInverse(*begin); - else if (n > 1) - { - std::vector vec((n+1)/2); - unsigned int i; - Iterator it; - - for (i=0, it=begin; i::iterator it) : it(it) {} - Integer& operator*() {return it->z;} - int operator-(ZIterator it2) {return int(it-it2.it);} - ZIterator operator+(int i) {return ZIterator(it+i);} - ZIterator& operator+=(int i) {it+=i; return *this;} - std::vector::iterator it; -}; - -ECP::Point ECP::ScalarMultiply(const Point &P, const Integer &k) const -{ - Element result; - if (k.BitCount() <= 5) - AbstractGroup::SimultaneousMultiply(&result, P, &k, 1); - else - ECP::SimultaneousMultiply(&result, P, &k, 1); - return result; -} - -void ECP::SimultaneousMultiply(ECP::Point *results, const ECP::Point &P, const Integer *expBegin, unsigned int expCount) const -{ - if (!GetField().IsMontgomeryRepresentation()) - { - ECP ecpmr(*this, true); - const ModularArithmetic &mr = ecpmr.GetField(); - ecpmr.SimultaneousMultiply(results, ToMontgomery(mr, P), expBegin, expCount); - for (unsigned int i=0; i bases; - std::vector exponents; - exponents.reserve(expCount); - std::vector > baseIndices(expCount); - std::vector > negateBase(expCount); - std::vector > exponentWindows(expCount); - unsigned int i; - - for (i=0; iNotNegative()); - exponents.push_back(WindowSlider(*expBegin++, InversionIsFast(), 5)); - exponents[i].FindNextWindow(); - } - - unsigned int expBitPosition = 0; - bool notDone = true; - - while (notDone) - { - notDone = false; - bool baseAdded = false; - for (i=0; i > finalCascade; - for (i=0; i::CascadeScalarMultiply(P, k1, Q, k2); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/ecp.h b/CryptoPP/ecp.h deleted file mode 100644 index d946be63a..000000000 --- a/CryptoPP/ecp.h +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef CRYPTOPP_ECP_H -#define CRYPTOPP_ECP_H - -#include "modarith.h" -#include "eprecomp.h" -#include "smartptr.h" -#include "pubkey.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! Elliptical Curve Point -struct CRYPTOPP_DLL ECPPoint -{ - ECPPoint() : identity(true) {} - ECPPoint(const Integer &x, const Integer &y) - : identity(false), x(x), y(y) {} - - bool operator==(const ECPPoint &t) const - {return (identity && t.identity) || (!identity && !t.identity && x==t.x && y==t.y);} - bool operator< (const ECPPoint &t) const - {return identity ? !t.identity : (!t.identity && (x; - -//! Elliptic Curve over GF(p), where p is prime -class CRYPTOPP_DLL ECP : public AbstractGroup -{ -public: - typedef ModularArithmetic Field; - typedef Integer FieldElement; - typedef ECPPoint Point; - - ECP() {} - ECP(const ECP &ecp, bool convertToMontgomeryRepresentation = false); - ECP(const Integer &modulus, const FieldElement &a, const FieldElement &b) - : m_fieldPtr(new Field(modulus)), m_a(a.IsNegative() ? modulus+a : a), m_b(b) {} - // construct from BER encoded parameters - // this constructor will decode and extract the the fields fieldID and curve of the sequence ECParameters - ECP(BufferedTransformation &bt); - - // encode the fields fieldID and curve of the sequence ECParameters - void DEREncode(BufferedTransformation &bt) const; - - bool Equal(const Point &P, const Point &Q) const; - const Point& Identity() const; - const Point& Inverse(const Point &P) const; - bool InversionIsFast() const {return true;} - const Point& Add(const Point &P, const Point &Q) const; - const Point& Double(const Point &P) const; - Point ScalarMultiply(const Point &P, const Integer &k) const; - Point CascadeScalarMultiply(const Point &P, const Integer &k1, const Point &Q, const Integer &k2) const; - void SimultaneousMultiply(Point *results, const Point &base, const Integer *exponents, unsigned int exponentsCount) const; - - Point Multiply(const Integer &k, const Point &P) const - {return ScalarMultiply(P, k);} - Point CascadeMultiply(const Integer &k1, const Point &P, const Integer &k2, const Point &Q) const - {return CascadeScalarMultiply(P, k1, Q, k2);} - - bool ValidateParameters(RandomNumberGenerator &rng, unsigned int level=3) const; - bool VerifyPoint(const Point &P) const; - - unsigned int EncodedPointSize(bool compressed = false) const - {return 1 + (compressed?1:2)*GetField().MaxElementByteLength();} - // returns false if point is compressed and not valid (doesn't check if uncompressed) - bool DecodePoint(Point &P, BufferedTransformation &bt, size_t len) const; - bool DecodePoint(Point &P, const byte *encodedPoint, size_t len) const; - void EncodePoint(byte *encodedPoint, const Point &P, bool compressed) const; - void EncodePoint(BufferedTransformation &bt, const Point &P, bool compressed) const; - - Point BERDecodePoint(BufferedTransformation &bt) const; - void DEREncodePoint(BufferedTransformation &bt, const Point &P, bool compressed) const; - - Integer FieldSize() const {return GetField().GetModulus();} - const Field & GetField() const {return *m_fieldPtr;} - const FieldElement & GetA() const {return m_a;} - const FieldElement & GetB() const {return m_b;} - - bool operator==(const ECP &rhs) const - {return GetField() == rhs.GetField() && m_a == rhs.m_a && m_b == rhs.m_b;} - -private: - clonable_ptr m_fieldPtr; - FieldElement m_a, m_b; - mutable Point m_R; -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS DL_FixedBasePrecomputationImpl; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_GroupPrecomputation; - -template class EcPrecomputation; - -//! ECP precomputation -template<> class EcPrecomputation : public DL_GroupPrecomputation -{ -public: - typedef ECP EllipticCurve; - - // DL_GroupPrecomputation - bool NeedConversions() const {return true;} - Element ConvertIn(const Element &P) const - {return P.identity ? P : ECP::Point(m_ec->GetField().ConvertIn(P.x), m_ec->GetField().ConvertIn(P.y));}; - Element ConvertOut(const Element &P) const - {return P.identity ? P : ECP::Point(m_ec->GetField().ConvertOut(P.x), m_ec->GetField().ConvertOut(P.y));} - const AbstractGroup & GetGroup() const {return *m_ec;} - Element BERDecodeElement(BufferedTransformation &bt) const {return m_ec->BERDecodePoint(bt);} - void DEREncodeElement(BufferedTransformation &bt, const Element &v) const {m_ec->DEREncodePoint(bt, v, false);} - - // non-inherited - void SetCurve(const ECP &ec) - { - m_ec.reset(new ECP(ec, true)); - m_ecOriginal = ec; - } - const ECP & GetCurve() const {return *m_ecOriginal;} - -private: - value_ptr m_ec, m_ecOriginal; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/elgamal.cpp b/CryptoPP/elgamal.cpp deleted file mode 100644 index b58fe7c06..000000000 --- a/CryptoPP/elgamal.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// elgamal.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "elgamal.h" -#include "asn.h" -#include "nbtheory.h" - -NAMESPACE_BEGIN(CryptoPP) - -void ElGamal_TestInstantiations() -{ - ElGamalEncryptor test1(1, 1, 1); - ElGamalDecryptor test2(NullRNG(), 123); - ElGamalEncryptor test3(test2); -} - -NAMESPACE_END diff --git a/CryptoPP/elgamal.h b/CryptoPP/elgamal.h deleted file mode 100644 index 9afc30eee..000000000 --- a/CryptoPP/elgamal.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef CRYPTOPP_ELGAMAL_H -#define CRYPTOPP_ELGAMAL_H - -#include "modexppc.h" -#include "dsa.h" - -NAMESPACE_BEGIN(CryptoPP) - -class CRYPTOPP_NO_VTABLE ElGamalBase : public DL_KeyAgreementAlgorithm_DH, - public DL_KeyDerivationAlgorithm, - public DL_SymmetricEncryptionAlgorithm -{ -public: - void Derive(const DL_GroupParameters &groupParams, byte *derivedKey, size_t derivedLength, const Integer &agreedElement, const Integer &ephemeralPublicKey, const NameValuePairs &derivationParams) const - { - agreedElement.Encode(derivedKey, derivedLength); - } - - size_t GetSymmetricKeyLength(size_t plainTextLength) const - { - return GetGroupParameters().GetModulus().ByteCount(); - } - - size_t GetSymmetricCiphertextLength(size_t plainTextLength) const - { - unsigned int len = GetGroupParameters().GetModulus().ByteCount(); - if (plainTextLength <= GetMaxSymmetricPlaintextLength(len)) - return len; - else - return 0; - } - - size_t GetMaxSymmetricPlaintextLength(size_t cipherTextLength) const - { - unsigned int len = GetGroupParameters().GetModulus().ByteCount(); - if (cipherTextLength == len) - return STDMIN(255U, len-3); - else - return 0; - } - - void SymmetricEncrypt(RandomNumberGenerator &rng, const byte *key, const byte *plainText, size_t plainTextLength, byte *cipherText, const NameValuePairs ¶meters) const - { - const Integer &p = GetGroupParameters().GetModulus(); - unsigned int modulusLen = p.ByteCount(); - - SecByteBlock block(modulusLen-1); - rng.GenerateBlock(block, modulusLen-2-plainTextLength); - memcpy(block+modulusLen-2-plainTextLength, plainText, plainTextLength); - block[modulusLen-2] = (byte)plainTextLength; - - a_times_b_mod_c(Integer(key, modulusLen), Integer(block, modulusLen-1), p).Encode(cipherText, modulusLen); - } - - DecodingResult SymmetricDecrypt(const byte *key, const byte *cipherText, size_t cipherTextLength, byte *plainText, const NameValuePairs ¶meters) const - { - const Integer &p = GetGroupParameters().GetModulus(); - unsigned int modulusLen = p.ByteCount(); - - if (cipherTextLength != modulusLen) - return DecodingResult(); - - Integer m = a_times_b_mod_c(Integer(cipherText, modulusLen), Integer(key, modulusLen).InverseMod(p), p); - - m.Encode(plainText, 1); - unsigned int plainTextLength = plainText[0]; - if (plainTextLength > GetMaxSymmetricPlaintextLength(modulusLen)) - return DecodingResult(); - m >>= 8; - m.Encode(plainText, plainTextLength); - return DecodingResult(plainTextLength); - } - - virtual const DL_GroupParameters_GFP & GetGroupParameters() const =0; -}; - -template -class ElGamalObjectImpl : public DL_ObjectImplBase, public ElGamalBase -{ -public: - size_t FixedMaxPlaintextLength() const {return this->MaxPlaintextLength(FixedCiphertextLength());} - size_t FixedCiphertextLength() const {return this->CiphertextLength(0);} - - const DL_GroupParameters_GFP & GetGroupParameters() const {return this->GetKey().GetGroupParameters();} - - DecodingResult FixedLengthDecrypt(RandomNumberGenerator &rng, const byte *cipherText, byte *plainText) const - {return Decrypt(rng, cipherText, FixedCiphertextLength(), plainText);} - -protected: - const DL_KeyAgreementAlgorithm & GetKeyAgreementAlgorithm() const {return *this;} - const DL_KeyDerivationAlgorithm & GetKeyDerivationAlgorithm() const {return *this;} - const DL_SymmetricEncryptionAlgorithm & GetSymmetricEncryptionAlgorithm() const {return *this;} -}; - -struct ElGamalKeys -{ - typedef DL_CryptoKeys_GFP::GroupParameters GroupParameters; - typedef DL_PrivateKey_GFP_OldFormat PrivateKey; - typedef DL_PublicKey_GFP_OldFormat PublicKey; -}; - -//! ElGamal encryption scheme with non-standard padding -struct ElGamal -{ - typedef DL_CryptoSchemeOptions SchemeOptions; - - static const char * StaticAlgorithmName() {return "ElgamalEnc/Crypto++Padding";} - - typedef SchemeOptions::GroupParameters GroupParameters; - //! implements PK_Encryptor interface - typedef PK_FinalTemplate, SchemeOptions, SchemeOptions::PublicKey> > Encryptor; - //! implements PK_Decryptor interface - typedef PK_FinalTemplate, SchemeOptions, SchemeOptions::PrivateKey> > Decryptor; -}; - -typedef ElGamal::Encryptor ElGamalEncryptor; -typedef ElGamal::Decryptor ElGamalDecryptor; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/emsa2.cpp b/CryptoPP/emsa2.cpp deleted file mode 100644 index 3dbb7e8c0..000000000 --- a/CryptoPP/emsa2.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// emsa2.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "emsa2.h" - -#ifndef CRYPTOPP_IMPORTS - -NAMESPACE_BEGIN(CryptoPP) - -void EMSA2Pad::ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const -{ - assert(representativeBitLength >= MinRepresentativeBitLength(hashIdentifier.second, hash.DigestSize())); - - if (representativeBitLength % 8 != 7) - throw PK_SignatureScheme::InvalidKeyLength("EMSA2: EMSA2 requires a key length that is a multiple of 8"); - - size_t digestSize = hash.DigestSize(); - size_t representativeByteLength = BitsToBytes(representativeBitLength); - - representative[0] = messageEmpty ? 0x4b : 0x6b; - memset(representative+1, 0xbb, representativeByteLength-digestSize-4); // pad with 0xbb - byte *afterP2 = representative+representativeByteLength-digestSize-3; - afterP2[0] = 0xba; - hash.Final(afterP2+1); - representative[representativeByteLength-2] = *hashIdentifier.first; - representative[representativeByteLength-1] = 0xcc; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/emsa2.h b/CryptoPP/emsa2.h deleted file mode 100644 index 49109e6db..000000000 --- a/CryptoPP/emsa2.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef CRYPTOPP_EMSA2_H -#define CRYPTOPP_EMSA2_H - -/** \file - This file contains various padding schemes for public key algorithms. -*/ - -#include "cryptlib.h" -#include "pubkey.h" - -#ifdef CRYPTOPP_IS_DLL -#include "sha.h" -#endif - -NAMESPACE_BEGIN(CryptoPP) - -template class EMSA2HashId -{ -public: - static const byte id; -}; - -template -class EMSA2HashIdLookup : public BASE -{ -public: - struct HashIdentifierLookup - { - template struct HashIdentifierLookup2 - { - static HashIdentifier Lookup() - { - return HashIdentifier(&EMSA2HashId::id, 1); - } - }; - }; -}; - -// EMSA2HashId can be instantiated with the following classes. -class SHA1; -class RIPEMD160; -class RIPEMD128; -class SHA256; -class SHA384; -class SHA512; -class Whirlpool; -class SHA224; -// end of list - -#ifdef CRYPTOPP_IS_DLL -CRYPTOPP_DLL_TEMPLATE_CLASS EMSA2HashId; -CRYPTOPP_DLL_TEMPLATE_CLASS EMSA2HashId; -CRYPTOPP_DLL_TEMPLATE_CLASS EMSA2HashId; -CRYPTOPP_DLL_TEMPLATE_CLASS EMSA2HashId; -CRYPTOPP_DLL_TEMPLATE_CLASS EMSA2HashId; -#endif - -//! _ -class CRYPTOPP_DLL EMSA2Pad : public EMSA2HashIdLookup -{ -public: - static const char * CRYPTOPP_API StaticAlgorithmName() {return "EMSA2";} - - size_t MinRepresentativeBitLength(size_t hashIdentifierLength, size_t digestLength) const - {return 8*digestLength + 31;} - - void ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const; -}; - -//! EMSA2, for use with RWSS and RSA_ISO -/*! Only the following hash functions are supported by this signature standard: - \dontinclude emsa2.h - \skip EMSA2HashId can be instantiated - \until end of list -*/ -struct P1363_EMSA2 : public SignatureStandard -{ - typedef EMSA2Pad SignatureMessageEncodingMethod; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/eprecomp.cpp b/CryptoPP/eprecomp.cpp deleted file mode 100644 index a061cf6cd..000000000 --- a/CryptoPP/eprecomp.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// eprecomp.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "eprecomp.h" -#include "asn.h" - -NAMESPACE_BEGIN(CryptoPP) - -template void DL_FixedBasePrecomputationImpl::SetBase(const DL_GroupPrecomputation &group, const Element &i_base) -{ - m_base = group.NeedConversions() ? group.ConvertIn(i_base) : i_base; - - if (m_bases.empty() || !(m_base == m_bases[0])) - { - m_bases.resize(1); - m_bases[0] = m_base; - } - - if (group.NeedConversions()) - m_base = i_base; -} - -template void DL_FixedBasePrecomputationImpl::Precompute(const DL_GroupPrecomputation &group, unsigned int maxExpBits, unsigned int storage) -{ - assert(m_bases.size() > 0); - assert(storage <= maxExpBits); - - if (storage > 1) - { - m_windowSize = (maxExpBits+storage-1)/storage; - m_exponentBase = Integer::Power2(m_windowSize); - } - - m_bases.resize(storage); - for (unsigned i=1; i void DL_FixedBasePrecomputationImpl::Load(const DL_GroupPrecomputation &group, BufferedTransformation &bt) -{ - BERSequenceDecoder seq(bt); - word32 version; - BERDecodeUnsigned(seq, version, INTEGER, 1, 1); - m_exponentBase.BERDecode(seq); - m_windowSize = m_exponentBase.BitCount() - 1; - m_bases.clear(); - while (!seq.EndReached()) - m_bases.push_back(group.BERDecodeElement(seq)); - if (!m_bases.empty() && group.NeedConversions()) - m_base = group.ConvertOut(m_bases[0]); - seq.MessageEnd(); -} - -template void DL_FixedBasePrecomputationImpl::Save(const DL_GroupPrecomputation &group, BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - DEREncodeUnsigned(seq, 1); // version - m_exponentBase.DEREncode(seq); - for (unsigned i=0; i void DL_FixedBasePrecomputationImpl::PrepareCascade(const DL_GroupPrecomputation &i_group, std::vector > &eb, const Integer &exponent) const -{ - const AbstractGroup &group = i_group.GetGroup(); - - Integer r, q, e = exponent; - bool fastNegate = group.InversionIsFast() && m_windowSize > 1; - unsigned int i; - - for (i=0; i+1(group.Inverse(m_bases[i]), m_exponentBase - r)); - } - else - eb.push_back(BaseAndExponent(m_bases[i], r)); - } - eb.push_back(BaseAndExponent(m_bases[i], e)); -} - -template T DL_FixedBasePrecomputationImpl::Exponentiate(const DL_GroupPrecomputation &group, const Integer &exponent) const -{ - std::vector > eb; // array of segments of the exponent and precalculated bases - eb.reserve(m_bases.size()); - PrepareCascade(group, eb, exponent); - return group.ConvertOut(GeneralCascadeMultiplication(group.GetGroup(), eb.begin(), eb.end())); -} - -template T - DL_FixedBasePrecomputationImpl::CascadeExponentiate(const DL_GroupPrecomputation &group, const Integer &exponent, - const DL_FixedBasePrecomputation &i_pc2, const Integer &exponent2) const -{ - std::vector > eb; // array of segments of the exponent and precalculated bases - const DL_FixedBasePrecomputationImpl &pc2 = static_cast &>(i_pc2); - eb.reserve(m_bases.size() + pc2.m_bases.size()); - PrepareCascade(group, eb, exponent); - pc2.PrepareCascade(group, eb, exponent2); - return group.ConvertOut(GeneralCascadeMultiplication(group.GetGroup(), eb.begin(), eb.end())); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/eprecomp.h b/CryptoPP/eprecomp.h deleted file mode 100644 index 1f3256766..000000000 --- a/CryptoPP/eprecomp.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef CRYPTOPP_EPRECOMP_H -#define CRYPTOPP_EPRECOMP_H - -#include "integer.h" -#include "algebra.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -template -class DL_GroupPrecomputation -{ -public: - typedef T Element; - - virtual bool NeedConversions() const {return false;} - virtual Element ConvertIn(const Element &v) const {return v;} - virtual Element ConvertOut(const Element &v) const {return v;} - virtual const AbstractGroup & GetGroup() const =0; - virtual Element BERDecodeElement(BufferedTransformation &bt) const =0; - virtual void DEREncodeElement(BufferedTransformation &bt, const Element &P) const =0; -}; - -template -class DL_FixedBasePrecomputation -{ -public: - typedef T Element; - - virtual bool IsInitialized() const =0; - virtual void SetBase(const DL_GroupPrecomputation &group, const Element &base) =0; - virtual const Element & GetBase(const DL_GroupPrecomputation &group) const =0; - virtual void Precompute(const DL_GroupPrecomputation &group, unsigned int maxExpBits, unsigned int storage) =0; - virtual void Load(const DL_GroupPrecomputation &group, BufferedTransformation &storedPrecomputation) =0; - virtual void Save(const DL_GroupPrecomputation &group, BufferedTransformation &storedPrecomputation) const =0; - virtual Element Exponentiate(const DL_GroupPrecomputation &group, const Integer &exponent) const =0; - virtual Element CascadeExponentiate(const DL_GroupPrecomputation &group, const Integer &exponent, const DL_FixedBasePrecomputation &pc2, const Integer &exponent2) const =0; -}; - -template -class DL_FixedBasePrecomputationImpl : public DL_FixedBasePrecomputation -{ -public: - typedef T Element; - - DL_FixedBasePrecomputationImpl() : m_windowSize(0) {} - - // DL_FixedBasePrecomputation - bool IsInitialized() const - {return !m_bases.empty();} - void SetBase(const DL_GroupPrecomputation &group, const Element &base); - const Element & GetBase(const DL_GroupPrecomputation &group) const - {return group.NeedConversions() ? m_base : m_bases[0];} - void Precompute(const DL_GroupPrecomputation &group, unsigned int maxExpBits, unsigned int storage); - void Load(const DL_GroupPrecomputation &group, BufferedTransformation &storedPrecomputation); - void Save(const DL_GroupPrecomputation &group, BufferedTransformation &storedPrecomputation) const; - Element Exponentiate(const DL_GroupPrecomputation &group, const Integer &exponent) const; - Element CascadeExponentiate(const DL_GroupPrecomputation &group, const Integer &exponent, const DL_FixedBasePrecomputation &pc2, const Integer &exponent2) const; - -private: - void PrepareCascade(const DL_GroupPrecomputation &group, std::vector > &eb, const Integer &exponent) const; - - Element m_base; - unsigned int m_windowSize; - Integer m_exponentBase; // what base to represent the exponent in - std::vector m_bases; // precalculated bases -}; - -NAMESPACE_END - -#ifdef CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES -#include "eprecomp.cpp" -#endif - -#endif diff --git a/CryptoPP/esign.cpp b/CryptoPP/esign.cpp deleted file mode 100644 index 8b42c1fa4..000000000 --- a/CryptoPP/esign.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// esign.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "esign.h" -#include "asn.h" -#include "modarith.h" -#include "nbtheory.h" -#include "sha.h" -#include "algparam.h" - -NAMESPACE_BEGIN(CryptoPP) - -void ESIGN_TestInstantiations() -{ - ESIGN::Verifier x1(1, 1); - ESIGN::Signer x2(NullRNG(), 1); - ESIGN::Verifier x3(x2); - ESIGN::Verifier x4(x2.GetKey()); - ESIGN::Verifier x5(x3); - ESIGN::Signer x6 = x2; - - x6 = x2; - x3 = ESIGN::Verifier(x2); - x4 = x2.GetKey(); -} - -void ESIGNFunction::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder seq(bt); - m_n.BERDecode(seq); - m_e.BERDecode(seq); - seq.MessageEnd(); -} - -void ESIGNFunction::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - m_n.DEREncode(seq); - m_e.DEREncode(seq); - seq.MessageEnd(); -} - -Integer ESIGNFunction::ApplyFunction(const Integer &x) const -{ - DoQuickSanityCheck(); - return STDMIN(a_exp_b_mod_c(x, m_e, m_n) >> (2*GetK()+2), MaxImage()); -} - -bool ESIGNFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = true; - pass = pass && m_n > Integer::One() && m_n.IsOdd(); - pass = pass && m_e >= 8 && m_e < m_n; - return pass; -} - -bool ESIGNFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Modulus) - CRYPTOPP_GET_FUNCTION_ENTRY(PublicExponent) - ; -} - -void ESIGNFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Modulus) - CRYPTOPP_SET_FUNCTION_ENTRY(PublicExponent) - ; -} - -// ***************************************************************************** - -void InvertibleESIGNFunction::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶m) -{ - int modulusSize = 1023*2; - param.GetIntValue("ModulusSize", modulusSize) || param.GetIntValue("KeySize", modulusSize); - - if (modulusSize < 24) - throw InvalidArgument("InvertibleESIGNFunction: specified modulus size is too small"); - - if (modulusSize % 3 != 0) - throw InvalidArgument("InvertibleESIGNFunction: modulus size must be divisible by 3"); - - m_e = param.GetValueWithDefault("PublicExponent", Integer(32)); - - if (m_e < 8) - throw InvalidArgument("InvertibleESIGNFunction: public exponents less than 8 may not be secure"); - - // VC70 workaround: putting these after primeParam causes overlapped stack allocation - ConstByteArrayParameter seedParam; - SecByteBlock seed; - - const Integer minP = Integer(204) << (modulusSize/3-8); - const Integer maxP = Integer::Power2(modulusSize/3)-1; - AlgorithmParameters primeParam = MakeParameters("Min", minP)("Max", maxP)("RandomNumberType", Integer::PRIME); - - if (param.GetValue("Seed", seedParam)) - { - seed.resize(seedParam.size() + 4); - memcpy(seed + 4, seedParam.begin(), seedParam.size()); - - PutWord(false, BIG_ENDIAN_ORDER, seed, (word32)0); - m_p.GenerateRandom(rng, CombinedNameValuePairs(primeParam, MakeParameters("Seed", ConstByteArrayParameter(seed)))); - PutWord(false, BIG_ENDIAN_ORDER, seed, (word32)1); - m_q.GenerateRandom(rng, CombinedNameValuePairs(primeParam, MakeParameters("Seed", ConstByteArrayParameter(seed)))); - } - else - { - m_p.GenerateRandom(rng, primeParam); - m_q.GenerateRandom(rng, primeParam); - } - - m_n = m_p * m_p * m_q; - - assert(m_n.BitCount() == modulusSize); -} - -void InvertibleESIGNFunction::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder privateKey(bt); - m_n.BERDecode(privateKey); - m_e.BERDecode(privateKey); - m_p.BERDecode(privateKey); - m_q.BERDecode(privateKey); - privateKey.MessageEnd(); -} - -void InvertibleESIGNFunction::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder privateKey(bt); - m_n.DEREncode(privateKey); - m_e.DEREncode(privateKey); - m_p.DEREncode(privateKey); - m_q.DEREncode(privateKey); - privateKey.MessageEnd(); -} - -Integer InvertibleESIGNFunction::CalculateRandomizedInverse(RandomNumberGenerator &rng, const Integer &x) const -{ - DoQuickSanityCheck(); - - Integer pq = m_p * m_q; - Integer p2 = m_p * m_p; - Integer r, z, re, a, w0, w1; - - do - { - r.Randomize(rng, Integer::Zero(), pq); - z = x << (2*GetK()+2); - re = a_exp_b_mod_c(r, m_e, m_n); - a = (z - re) % m_n; - Integer::Divide(w1, w0, a, pq); - if (w1.NotZero()) - { - ++w0; - w1 = pq - w1; - } - } - while ((w1 >> 2*GetK()+1).IsPositive()); - - ModularArithmetic modp(m_p); - Integer t = modp.Divide(w0 * r % m_p, m_e * re % m_p); - Integer s = r + t*pq; - assert(s < m_n); -/* - using namespace std; - cout << "f = " << x << endl; - cout << "r = " << r << endl; - cout << "z = " << z << endl; - cout << "a = " << a << endl; - cout << "w0 = " << w0 << endl; - cout << "w1 = " << w1 << endl; - cout << "t = " << t << endl; - cout << "s = " << s << endl; -*/ - return s; -} - -bool InvertibleESIGNFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = ESIGNFunction::Validate(rng, level); - pass = pass && m_p > Integer::One() && m_p.IsOdd() && m_p < m_n; - pass = pass && m_q > Integer::One() && m_q.IsOdd() && m_q < m_n; - pass = pass && m_p.BitCount() == m_q.BitCount(); - if (level >= 1) - pass = pass && m_p * m_p * m_q == m_n; - if (level >= 2) - pass = pass && VerifyPrime(rng, m_p, level-2) && VerifyPrime(rng, m_q, level-2); - return pass; -} - -bool InvertibleESIGNFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_GET_FUNCTION_ENTRY(Prime2) - ; -} - -void InvertibleESIGNFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime2) - ; -} - -NAMESPACE_END diff --git a/CryptoPP/esign.h b/CryptoPP/esign.h deleted file mode 100644 index 8eecbc5a1..000000000 --- a/CryptoPP/esign.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef CRYPTOPP_ESIGN_H -#define CRYPTOPP_ESIGN_H - -/** \file - This file contains classes that implement the - ESIGN signature schemes as defined in IEEE P1363a. -*/ - -#include "pubkey.h" -#include "integer.h" -#include "asn.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -class ESIGNFunction : public TrapdoorFunction, public ASN1CryptoMaterial -{ - typedef ESIGNFunction ThisClass; - -public: - void Initialize(const Integer &n, const Integer &e) - {m_n = n; m_e = e;} - - // PublicKey - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - // CryptoMaterial - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - - // TrapdoorFunction - Integer ApplyFunction(const Integer &x) const; - Integer PreimageBound() const {return m_n;} - Integer ImageBound() const {return Integer::Power2(GetK());} - - // non-derived - const Integer & GetModulus() const {return m_n;} - const Integer & GetPublicExponent() const {return m_e;} - - void SetModulus(const Integer &n) {m_n = n;} - void SetPublicExponent(const Integer &e) {m_e = e;} - -protected: - unsigned int GetK() const {return m_n.BitCount()/3-1;} - - Integer m_n, m_e; -}; - -//! _ -class InvertibleESIGNFunction : public ESIGNFunction, public RandomizedTrapdoorFunctionInverse, public PrivateKey -{ - typedef InvertibleESIGNFunction ThisClass; - -public: - void Initialize(const Integer &n, const Integer &e, const Integer &p, const Integer &q) - {m_n = n; m_e = e; m_p = p; m_q = q;} - // generate a random private key - void Initialize(RandomNumberGenerator &rng, unsigned int modulusBits) - {GenerateRandomWithKeySize(rng, modulusBits);} - - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - Integer CalculateRandomizedInverse(RandomNumberGenerator &rng, const Integer &x) const; - - // GeneratibleCryptoMaterial - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - /*! parameters: (ModulusSize) */ - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg); - - const Integer& GetPrime1() const {return m_p;} - const Integer& GetPrime2() const {return m_q;} - - void SetPrime1(const Integer &p) {m_p = p;} - void SetPrime2(const Integer &q) {m_q = q;} - -protected: - Integer m_p, m_q; -}; - -//! _ -template -class EMSA5Pad : public PK_DeterministicSignatureMessageEncodingMethod -{ -public: - static const char *StaticAlgorithmName() {return "EMSA5";} - - void ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const - { - SecByteBlock digest(hash.DigestSize()); - hash.Final(digest); - size_t representativeByteLength = BitsToBytes(representativeBitLength); - T mgf; - mgf.GenerateAndMask(hash, representative, representativeByteLength, digest, digest.size(), false); - if (representativeBitLength % 8 != 0) - representative[0] = (byte)Crop(representative[0], representativeBitLength % 8); - } -}; - -//! EMSA5, for use with ESIGN -struct P1363_EMSA5 : public SignatureStandard -{ - typedef EMSA5Pad SignatureMessageEncodingMethod; -}; - -struct ESIGN_Keys -{ - static std::string StaticAlgorithmName() {return "ESIGN";} - typedef ESIGNFunction PublicKey; - typedef InvertibleESIGNFunction PrivateKey; -}; - -//! ESIGN, as defined in IEEE P1363a -template -struct ESIGN : public TF_SS -{ -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/factory.h b/CryptoPP/factory.h deleted file mode 100644 index 5b65db3da..000000000 --- a/CryptoPP/factory.h +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef CRYPTOPP_OBJFACT_H -#define CRYPTOPP_OBJFACT_H - -#include "cryptlib.h" -#include -#include - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -template -class ObjectFactory -{ -public: - virtual ~ObjectFactory () {} - virtual AbstractClass * CreateObject() const =0; -}; - -//! _ -template -class DefaultObjectFactory : public ObjectFactory -{ -public: - AbstractClass * CreateObject() const - { - return new ConcreteClass; - } - -}; - -//! _ -template -class ObjectFactoryRegistry -{ -public: - class FactoryNotFound : public Exception - { - public: - FactoryNotFound(const char *name) : Exception(OTHER_ERROR, std::string("ObjectFactoryRegistry: could not find factory for algorithm ") + name) {} - }; - - ~ObjectFactoryRegistry() - { - for (CPP_TYPENAME Map::iterator i = m_map.begin(); i != m_map.end(); ++i) - { - delete (ObjectFactory *)i->second; - i->second = NULL; - } - } - - void RegisterFactory(const std::string &name, ObjectFactory *factory) - { - m_map[name] = factory; - } - - const ObjectFactory * GetFactory(const char *name) const - { - CPP_TYPENAME Map::const_iterator i = m_map.find(name); - return i == m_map.end() ? NULL : (ObjectFactory *)i->second; - } - - AbstractClass *CreateObject(const char *name) const - { - const ObjectFactory *factory = GetFactory(name); - if (!factory) - throw FactoryNotFound(name); - return factory->CreateObject(); - } - - // Return a vector containing the factory names. This is easier than returning an iterator. - // from Andrew Pitonyak - std::vector GetFactoryNames() const - { - std::vector names; - CPP_TYPENAME Map::const_iterator iter; - for (iter = m_map.begin(); iter != m_map.end(); ++iter) - names.push_back(iter->first); - return names; - } - - CRYPTOPP_NOINLINE static ObjectFactoryRegistry & Registry(CRYPTOPP_NOINLINE_DOTDOTDOT); - -private: - // use void * instead of ObjectFactory * to save code size - typedef std::map Map; - Map m_map; -}; - -template -ObjectFactoryRegistry & ObjectFactoryRegistry::Registry(CRYPTOPP_NOINLINE_DOTDOTDOT) -{ - static ObjectFactoryRegistry s_registry; - return s_registry; -} - -template -struct RegisterDefaultFactoryFor { -RegisterDefaultFactoryFor(const char *name=NULL) -{ - // BCB2006 workaround - std::string n = name ? std::string(name) : std::string(ConcreteClass::StaticAlgorithmName()); - ObjectFactoryRegistry::Registry(). - RegisterFactory(n, new DefaultObjectFactory); -}}; - -template -void RegisterAsymmetricCipherDefaultFactories(const char *name=NULL, SchemeClass *dummy=NULL) -{ - RegisterDefaultFactoryFor((const char *)name); - RegisterDefaultFactoryFor((const char *)name); -} - -template -void RegisterSignatureSchemeDefaultFactories(const char *name=NULL, SchemeClass *dummy=NULL) -{ - RegisterDefaultFactoryFor((const char *)name); - RegisterDefaultFactoryFor((const char *)name); -} - -template -void RegisterSymmetricCipherDefaultFactories(const char *name=NULL, SchemeClass *dummy=NULL) -{ - RegisterDefaultFactoryFor((const char *)name); - RegisterDefaultFactoryFor((const char *)name); -} - -template -void RegisterAuthenticatedSymmetricCipherDefaultFactories(const char *name=NULL, SchemeClass *dummy=NULL) -{ - RegisterDefaultFactoryFor((const char *)name); - RegisterDefaultFactoryFor((const char *)name); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/files.cpp b/CryptoPP/files.cpp deleted file mode 100644 index 453b56248..000000000 --- a/CryptoPP/files.cpp +++ /dev/null @@ -1,259 +0,0 @@ -// files.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "files.h" - -#include - -NAMESPACE_BEGIN(CryptoPP) - -using namespace std; - -#ifndef NDEBUG -void Files_TestInstantiations() -{ - FileStore f0; - FileSource f1; - FileSink f2; -} -#endif - -void FileStore::StoreInitialize(const NameValuePairs ¶meters) -{ - m_waiting = false; - m_stream = NULL; - m_file.release(); - - const char *fileName = NULL; -#if defined(CRYPTOPP_UNIX_AVAILABLE) || _MSC_VER >= 1400 - const wchar_t *fileNameWide = NULL; - if (!parameters.GetValue(Name::InputFileNameWide(), fileNameWide)) -#endif - if (!parameters.GetValue(Name::InputFileName(), fileName)) - { - parameters.GetValue(Name::InputStreamPointer(), m_stream); - return; - } - - ios::openmode binary = parameters.GetValueWithDefault(Name::InputBinaryMode(), true) ? ios::binary : ios::openmode(0); - m_file.reset(new std::ifstream); -#ifdef CRYPTOPP_UNIX_AVAILABLE - std::string narrowed; - if (fileNameWide) - fileName = (narrowed = StringNarrow(fileNameWide)).c_str(); -#endif -#if _MSC_VER >= 1400 - if (fileNameWide) - { - m_file->open(fileNameWide, ios::in | binary); - if (!*m_file) - throw OpenErr(StringNarrow(fileNameWide, false)); - } -#endif - if (fileName) - { - m_file->open(fileName, ios::in | binary); - if (!*m_file) - throw OpenErr(fileName); - } - m_stream = m_file.get(); -} - -lword FileStore::MaxRetrievable() const -{ - if (!m_stream) - return 0; - - streampos current = m_stream->tellg(); - streampos end = m_stream->seekg(0, ios::end).tellg(); - m_stream->seekg(current); - return end-current; -} - -size_t FileStore::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - if (!m_stream) - { - transferBytes = 0; - return 0; - } - - lword size=transferBytes; - transferBytes = 0; - - if (m_waiting) - goto output; - - while (size && m_stream->good()) - { - { - size_t spaceSize = 1024; - m_space = HelpCreatePutSpace(target, channel, 1, UnsignedMin(size_t(0)-1, size), spaceSize); - - m_stream->read((char *)m_space, (unsigned int)STDMIN(size, (lword)spaceSize)); - } - m_len = (size_t)m_stream->gcount(); - size_t blockedBytes; -output: - blockedBytes = target.ChannelPutModifiable2(channel, m_space, m_len, 0, blocking); - m_waiting = blockedBytes > 0; - if (m_waiting) - return blockedBytes; - size -= m_len; - transferBytes += m_len; - } - - if (!m_stream->good() && !m_stream->eof()) - throw ReadErr(); - - return 0; -} - -size_t FileStore::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const -{ - if (!m_stream) - return 0; - - if (begin == 0 && end == 1) - { - int result = m_stream->peek(); - if (result == char_traits::eof()) - return 0; - else - { - size_t blockedBytes = target.ChannelPut(channel, byte(result), blocking); - begin += 1-blockedBytes; - return blockedBytes; - } - } - - // TODO: figure out what happens on cin - streampos current = m_stream->tellg(); - streampos endPosition = m_stream->seekg(0, ios::end).tellg(); - streampos newPosition = current + (streamoff)begin; - - if (newPosition >= endPosition) - { - m_stream->seekg(current); - return 0; // don't try to seek beyond the end of file - } - m_stream->seekg(newPosition); - try - { - assert(!m_waiting); - lword copyMax = end-begin; - size_t blockedBytes = const_cast(this)->TransferTo2(target, copyMax, channel, blocking); - begin += copyMax; - if (blockedBytes) - { - const_cast(this)->m_waiting = false; - return blockedBytes; - } - } - catch(...) - { - m_stream->clear(); - m_stream->seekg(current); - throw; - } - m_stream->clear(); - m_stream->seekg(current); - - return 0; -} - -lword FileStore::Skip(lword skipMax) -{ - if (!m_stream) - return 0; - - lword oldPos = m_stream->tellg(); - std::istream::off_type offset; - if (!SafeConvert(skipMax, offset)) - throw InvalidArgument("FileStore: maximum seek offset exceeded"); - m_stream->seekg(offset, ios::cur); - return (lword)m_stream->tellg() - oldPos; -} - -void FileSink::IsolatedInitialize(const NameValuePairs ¶meters) -{ - m_stream = NULL; - m_file.release(); - - const char *fileName = NULL; -#if defined(CRYPTOPP_UNIX_AVAILABLE) || _MSC_VER >= 1400 - const wchar_t *fileNameWide = NULL; - if (!parameters.GetValue(Name::OutputFileNameWide(), fileNameWide)) -#endif - if (!parameters.GetValue(Name::OutputFileName(), fileName)) - { - parameters.GetValue(Name::OutputStreamPointer(), m_stream); - return; - } - - ios::openmode binary = parameters.GetValueWithDefault(Name::OutputBinaryMode(), true) ? ios::binary : ios::openmode(0); - m_file.reset(new std::ofstream); -#ifdef CRYPTOPP_UNIX_AVAILABLE - std::string narrowed; - if (fileNameWide) - fileName = (narrowed = StringNarrow(fileNameWide)).c_str(); -#endif -#if _MSC_VER >= 1400 - if (fileNameWide) - { - m_file->open(fileNameWide, ios::out | ios::trunc | binary); - if (!*m_file) - throw OpenErr(StringNarrow(fileNameWide, false)); - } -#endif - if (fileName) - { - m_file->open(fileName, ios::out | ios::trunc | binary); - if (!*m_file) - throw OpenErr(fileName); - } - m_stream = m_file.get(); -} - -bool FileSink::IsolatedFlush(bool hardFlush, bool blocking) -{ - if (!m_stream) - throw Err("FileSink: output stream not opened"); - - m_stream->flush(); - if (!m_stream->good()) - throw WriteErr(); - - return false; -} - -size_t FileSink::Put2(const byte *inString, size_t length, int messageEnd, bool blocking) -{ - if (!m_stream) - throw Err("FileSink: output stream not opened"); - - while (length > 0) - { - std::streamsize size; - if (!SafeConvert(length, size)) - size = numeric_limits::max(); - m_stream->write((const char *)inString, size); - inString += size; - length -= (size_t)size; - } - - if (messageEnd) - m_stream->flush(); - - if (!m_stream->good()) - throw WriteErr(); - - return 0; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/files.h b/CryptoPP/files.h deleted file mode 100644 index a47e856bf..000000000 --- a/CryptoPP/files.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef CRYPTOPP_FILES_H -#define CRYPTOPP_FILES_H - -#include "cryptlib.h" -#include "filters.h" -#include "argnames.h" - -#include -#include - -NAMESPACE_BEGIN(CryptoPP) - -//! file-based implementation of Store interface -class CRYPTOPP_DLL FileStore : public Store, private FilterPutSpaceHelper, public NotCopyable -{ -public: - class Err : public Exception - { - public: - Err(const std::string &s) : Exception(IO_ERROR, s) {} - }; - class OpenErr : public Err {public: OpenErr(const std::string &filename) : Err("FileStore: error opening file for reading: " + filename) {}}; - class ReadErr : public Err {public: ReadErr() : Err("FileStore: error reading file") {}}; - - FileStore() : m_stream(NULL) {} - FileStore(std::istream &in) - {StoreInitialize(MakeParameters(Name::InputStreamPointer(), &in));} - FileStore(const char *filename) - {StoreInitialize(MakeParameters(Name::InputFileName(), filename));} -#if defined(CRYPTOPP_UNIX_AVAILABLE) || _MSC_VER >= 1400 - //! specify file with Unicode name. On non-Windows OS, this function assumes that setlocale() has been called. - FileStore(const wchar_t *filename) - {StoreInitialize(MakeParameters(Name::InputFileNameWide(), filename));} -#endif - - std::istream* GetStream() {return m_stream;} - - lword MaxRetrievable() const; - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const; - lword Skip(lword skipMax=ULONG_MAX); - -private: - void StoreInitialize(const NameValuePairs ¶meters); - - member_ptr m_file; - std::istream *m_stream; - byte *m_space; - size_t m_len; - bool m_waiting; -}; - -//! file-based implementation of Source interface -class CRYPTOPP_DLL FileSource : public SourceTemplate -{ -public: - typedef FileStore::Err Err; - typedef FileStore::OpenErr OpenErr; - typedef FileStore::ReadErr ReadErr; - - FileSource(BufferedTransformation *attachment = NULL) - : SourceTemplate(attachment) {} - FileSource(std::istream &in, bool pumpAll, BufferedTransformation *attachment = NULL) - : SourceTemplate(attachment) {SourceInitialize(pumpAll, MakeParameters(Name::InputStreamPointer(), &in));} - FileSource(const char *filename, bool pumpAll, BufferedTransformation *attachment = NULL, bool binary=true) - : SourceTemplate(attachment) {SourceInitialize(pumpAll, MakeParameters(Name::InputFileName(), filename)(Name::InputBinaryMode(), binary));} -#if defined(CRYPTOPP_UNIX_AVAILABLE) || _MSC_VER >= 1400 - //! specify file with Unicode name. On non-Windows OS, this function assumes that setlocale() has been called. - FileSource(const wchar_t *filename, bool pumpAll, BufferedTransformation *attachment = NULL, bool binary=true) - : SourceTemplate(attachment) {SourceInitialize(pumpAll, MakeParameters(Name::InputFileNameWide(), filename)(Name::InputBinaryMode(), binary));} -#endif - - std::istream* GetStream() {return m_store.GetStream();} -}; - -//! file-based implementation of Sink interface -class CRYPTOPP_DLL FileSink : public Sink, public NotCopyable -{ -public: - class Err : public Exception - { - public: - Err(const std::string &s) : Exception(IO_ERROR, s) {} - }; - class OpenErr : public Err {public: OpenErr(const std::string &filename) : Err("FileSink: error opening file for writing: " + filename) {}}; - class WriteErr : public Err {public: WriteErr() : Err("FileSink: error writing file") {}}; - - FileSink() : m_stream(NULL) {} - FileSink(std::ostream &out) - {IsolatedInitialize(MakeParameters(Name::OutputStreamPointer(), &out));} - FileSink(const char *filename, bool binary=true) - {IsolatedInitialize(MakeParameters(Name::OutputFileName(), filename)(Name::OutputBinaryMode(), binary));} -#if defined(CRYPTOPP_UNIX_AVAILABLE) || _MSC_VER >= 1400 - //! specify file with Unicode name. On non-Windows OS, this function assumes that setlocale() has been called. - FileSink(const wchar_t *filename, bool binary=true) - {IsolatedInitialize(MakeParameters(Name::OutputFileNameWide(), filename)(Name::OutputBinaryMode(), binary));} -#endif - - std::ostream* GetStream() {return m_stream;} - - void IsolatedInitialize(const NameValuePairs ¶meters); - size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking); - bool IsolatedFlush(bool hardFlush, bool blocking); - -private: - member_ptr m_file; - std::ostream *m_stream; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/filters.cpp b/CryptoPP/filters.cpp deleted file mode 100644 index 083dfd361..000000000 --- a/CryptoPP/filters.cpp +++ /dev/null @@ -1,1120 +0,0 @@ -// filters.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "filters.h" -#include "mqueue.h" -#include "fltrimpl.h" -#include "argnames.h" -#include -#include - -NAMESPACE_BEGIN(CryptoPP) - -Filter::Filter(BufferedTransformation *attachment) - : m_attachment(attachment), m_continueAt(0) -{ -} - -BufferedTransformation * Filter::NewDefaultAttachment() const -{ - return new MessageQueue; -} - -BufferedTransformation * Filter::AttachedTransformation() -{ - if (m_attachment.get() == NULL) - m_attachment.reset(NewDefaultAttachment()); - return m_attachment.get(); -} - -const BufferedTransformation *Filter::AttachedTransformation() const -{ - if (m_attachment.get() == NULL) - const_cast(this)->m_attachment.reset(NewDefaultAttachment()); - return m_attachment.get(); -} - -void Filter::Detach(BufferedTransformation *newOut) -{ - m_attachment.reset(newOut); -} - -void Filter::Insert(Filter *filter) -{ - filter->m_attachment.reset(m_attachment.release()); - m_attachment.reset(filter); -} - -size_t Filter::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const -{ - return AttachedTransformation()->CopyRangeTo2(target, begin, end, channel, blocking); -} - -size_t Filter::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - return AttachedTransformation()->TransferTo2(target, transferBytes, channel, blocking); -} - -void Filter::Initialize(const NameValuePairs ¶meters, int propagation) -{ - m_continueAt = 0; - IsolatedInitialize(parameters); - PropagateInitialize(parameters, propagation); -} - -bool Filter::Flush(bool hardFlush, int propagation, bool blocking) -{ - switch (m_continueAt) - { - case 0: - if (IsolatedFlush(hardFlush, blocking)) - return true; - case 1: - if (OutputFlush(1, hardFlush, propagation, blocking)) - return true; - } - return false; -} - -bool Filter::MessageSeriesEnd(int propagation, bool blocking) -{ - switch (m_continueAt) - { - case 0: - if (IsolatedMessageSeriesEnd(blocking)) - return true; - case 1: - if (ShouldPropagateMessageSeriesEnd() && OutputMessageSeriesEnd(1, propagation, blocking)) - return true; - } - return false; -} - -void Filter::PropagateInitialize(const NameValuePairs ¶meters, int propagation) -{ - if (propagation) - AttachedTransformation()->Initialize(parameters, propagation-1); -} - -size_t Filter::OutputModifiable(int outputSite, byte *inString, size_t length, int messageEnd, bool blocking, const std::string &channel) -{ - if (messageEnd) - messageEnd--; - size_t result = AttachedTransformation()->ChannelPutModifiable2(channel, inString, length, messageEnd, blocking); - m_continueAt = result ? outputSite : 0; - return result; -} - -size_t Filter::Output(int outputSite, const byte *inString, size_t length, int messageEnd, bool blocking, const std::string &channel) -{ - if (messageEnd) - messageEnd--; - size_t result = AttachedTransformation()->ChannelPut2(channel, inString, length, messageEnd, blocking); - m_continueAt = result ? outputSite : 0; - return result; -} - -bool Filter::OutputFlush(int outputSite, bool hardFlush, int propagation, bool blocking, const std::string &channel) -{ - if (propagation && AttachedTransformation()->ChannelFlush(channel, hardFlush, propagation-1, blocking)) - { - m_continueAt = outputSite; - return true; - } - m_continueAt = 0; - return false; -} - -bool Filter::OutputMessageSeriesEnd(int outputSite, int propagation, bool blocking, const std::string &channel) -{ - if (propagation && AttachedTransformation()->ChannelMessageSeriesEnd(channel, propagation-1, blocking)) - { - m_continueAt = outputSite; - return true; - } - m_continueAt = 0; - return false; -} - -// ************************************************************* - -void MeterFilter::ResetMeter() -{ - m_currentMessageBytes = m_totalBytes = m_currentSeriesMessages = m_totalMessages = m_totalMessageSeries = 0; - m_rangesToSkip.clear(); -} - -void MeterFilter::AddRangeToSkip(unsigned int message, lword position, lword size, bool sortNow) -{ - MessageRange r = {message, position, size}; - m_rangesToSkip.push_back(r); - if (sortNow) - std::sort(m_rangesToSkip.begin(), m_rangesToSkip.end()); -} - -size_t MeterFilter::PutMaybeModifiable(byte *begin, size_t length, int messageEnd, bool blocking, bool modifiable) -{ - if (!m_transparent) - return 0; - - size_t t; - FILTER_BEGIN; - - m_begin = begin; - m_length = length; - - while (m_length > 0 || messageEnd) - { - if (m_length > 0 && !m_rangesToSkip.empty() && m_rangesToSkip.front().message == m_totalMessages && m_currentMessageBytes + m_length > m_rangesToSkip.front().position) - { - FILTER_OUTPUT_MAYBE_MODIFIABLE(1, m_begin, t = (size_t)SaturatingSubtract(m_rangesToSkip.front().position, m_currentMessageBytes), false, modifiable); - - assert(t < m_length); - m_begin += t; - m_length -= t; - m_currentMessageBytes += t; - m_totalBytes += t; - - if (m_currentMessageBytes + m_length < m_rangesToSkip.front().position + m_rangesToSkip.front().size) - t = m_length; - else - { - t = (size_t)SaturatingSubtract(m_rangesToSkip.front().position + m_rangesToSkip.front().size, m_currentMessageBytes); - assert(t <= m_length); - m_rangesToSkip.pop_front(); - } - - m_begin += t; - m_length -= t; - m_currentMessageBytes += t; - m_totalBytes += t; - } - else - { - FILTER_OUTPUT_MAYBE_MODIFIABLE(2, m_begin, m_length, messageEnd, modifiable); - - m_currentMessageBytes += m_length; - m_totalBytes += m_length; - m_length = 0; - - if (messageEnd) - { - m_currentMessageBytes = 0; - m_currentSeriesMessages++; - m_totalMessages++; - messageEnd = false; - } - } - } - - FILTER_END_NO_MESSAGE_END; -} - -size_t MeterFilter::Put2(const byte *begin, size_t length, int messageEnd, bool blocking) -{ - return PutMaybeModifiable(const_cast(begin), length, messageEnd, blocking, false); -} - -size_t MeterFilter::PutModifiable2(byte *begin, size_t length, int messageEnd, bool blocking) -{ - return PutMaybeModifiable(begin, length, messageEnd, blocking, true); -} - -bool MeterFilter::IsolatedMessageSeriesEnd(bool blocking) -{ - m_currentMessageBytes = 0; - m_currentSeriesMessages = 0; - m_totalMessageSeries++; - return false; -} - -// ************************************************************* - -void FilterWithBufferedInput::BlockQueue::ResetQueue(size_t blockSize, size_t maxBlocks) -{ - m_buffer.New(blockSize * maxBlocks); - m_blockSize = blockSize; - m_maxBlocks = maxBlocks; - m_size = 0; - m_begin = m_buffer; -} - -byte *FilterWithBufferedInput::BlockQueue::GetBlock() -{ - if (m_size >= m_blockSize) - { - byte *ptr = m_begin; - if ((m_begin+=m_blockSize) == m_buffer.end()) - m_begin = m_buffer; - m_size -= m_blockSize; - return ptr; - } - else - return NULL; -} - -byte *FilterWithBufferedInput::BlockQueue::GetContigousBlocks(size_t &numberOfBytes) -{ - numberOfBytes = STDMIN(numberOfBytes, STDMIN(size_t(m_buffer.end()-m_begin), m_size)); - byte *ptr = m_begin; - m_begin += numberOfBytes; - m_size -= numberOfBytes; - if (m_size == 0 || m_begin == m_buffer.end()) - m_begin = m_buffer; - return ptr; -} - -size_t FilterWithBufferedInput::BlockQueue::GetAll(byte *outString) -{ - size_t size = m_size; - size_t numberOfBytes = m_maxBlocks*m_blockSize; - const byte *ptr = GetContigousBlocks(numberOfBytes); - memcpy(outString, ptr, numberOfBytes); - memcpy(outString+numberOfBytes, m_begin, m_size); - m_size = 0; - return size; -} - -void FilterWithBufferedInput::BlockQueue::Put(const byte *inString, size_t length) -{ - assert(m_size + length <= m_buffer.size()); - byte *end = (m_size < size_t(m_buffer.end()-m_begin)) ? m_begin + m_size : m_begin + m_size - m_buffer.size(); - size_t len = STDMIN(length, size_t(m_buffer.end()-end)); - memcpy(end, inString, len); - if (len < length) - memcpy(m_buffer, inString+len, length-len); - m_size += length; -} - -FilterWithBufferedInput::FilterWithBufferedInput(BufferedTransformation *attachment) - : Filter(attachment) -{ -} - -FilterWithBufferedInput::FilterWithBufferedInput(size_t firstSize, size_t blockSize, size_t lastSize, BufferedTransformation *attachment) - : Filter(attachment), m_firstSize(firstSize), m_blockSize(blockSize), m_lastSize(lastSize) - , m_firstInputDone(false) -{ - if (m_firstSize < 0 || m_blockSize < 1 || m_lastSize < 0) - throw InvalidArgument("FilterWithBufferedInput: invalid buffer size"); - - m_queue.ResetQueue(1, m_firstSize); -} - -void FilterWithBufferedInput::IsolatedInitialize(const NameValuePairs ¶meters) -{ - InitializeDerivedAndReturnNewSizes(parameters, m_firstSize, m_blockSize, m_lastSize); - if (m_firstSize < 0 || m_blockSize < 1 || m_lastSize < 0) - throw InvalidArgument("FilterWithBufferedInput: invalid buffer size"); - m_queue.ResetQueue(1, m_firstSize); - m_firstInputDone = false; -} - -bool FilterWithBufferedInput::IsolatedFlush(bool hardFlush, bool blocking) -{ - if (!blocking) - throw BlockingInputOnly("FilterWithBufferedInput"); - - if (hardFlush) - ForceNextPut(); - FlushDerived(); - - return false; -} - -size_t FilterWithBufferedInput::PutMaybeModifiable(byte *inString, size_t length, int messageEnd, bool blocking, bool modifiable) -{ - if (!blocking) - throw BlockingInputOnly("FilterWithBufferedInput"); - - if (length != 0) - { - size_t newLength = m_queue.CurrentSize() + length; - - if (!m_firstInputDone && newLength >= m_firstSize) - { - size_t len = m_firstSize - m_queue.CurrentSize(); - m_queue.Put(inString, len); - FirstPut(m_queue.GetContigousBlocks(m_firstSize)); - assert(m_queue.CurrentSize() == 0); - m_queue.ResetQueue(m_blockSize, (2*m_blockSize+m_lastSize-2)/m_blockSize); - - inString += len; - newLength -= m_firstSize; - m_firstInputDone = true; - } - - if (m_firstInputDone) - { - if (m_blockSize == 1) - { - while (newLength > m_lastSize && m_queue.CurrentSize() > 0) - { - size_t len = newLength - m_lastSize; - byte *ptr = m_queue.GetContigousBlocks(len); - NextPutModifiable(ptr, len); - newLength -= len; - } - - if (newLength > m_lastSize) - { - size_t len = newLength - m_lastSize; - NextPutMaybeModifiable(inString, len, modifiable); - inString += len; - newLength -= len; - } - } - else - { - while (newLength >= m_blockSize + m_lastSize && m_queue.CurrentSize() >= m_blockSize) - { - NextPutModifiable(m_queue.GetBlock(), m_blockSize); - newLength -= m_blockSize; - } - - if (newLength >= m_blockSize + m_lastSize && m_queue.CurrentSize() > 0) - { - assert(m_queue.CurrentSize() < m_blockSize); - size_t len = m_blockSize - m_queue.CurrentSize(); - m_queue.Put(inString, len); - inString += len; - NextPutModifiable(m_queue.GetBlock(), m_blockSize); - newLength -= m_blockSize; - } - - if (newLength >= m_blockSize + m_lastSize) - { - size_t len = RoundDownToMultipleOf(newLength - m_lastSize, m_blockSize); - NextPutMaybeModifiable(inString, len, modifiable); - inString += len; - newLength -= len; - } - } - } - - m_queue.Put(inString, newLength - m_queue.CurrentSize()); - } - - if (messageEnd) - { - if (!m_firstInputDone && m_firstSize==0) - FirstPut(NULL); - - SecByteBlock temp(m_queue.CurrentSize()); - m_queue.GetAll(temp); - LastPut(temp, temp.size()); - - m_firstInputDone = false; - m_queue.ResetQueue(1, m_firstSize); - - Output(1, NULL, 0, messageEnd, blocking); - } - return 0; -} - -void FilterWithBufferedInput::ForceNextPut() -{ - if (!m_firstInputDone) - return; - - if (m_blockSize > 1) - { - while (m_queue.CurrentSize() >= m_blockSize) - NextPutModifiable(m_queue.GetBlock(), m_blockSize); - } - else - { - size_t len; - while ((len = m_queue.CurrentSize()) > 0) - NextPutModifiable(m_queue.GetContigousBlocks(len), len); - } -} - -void FilterWithBufferedInput::NextPutMultiple(const byte *inString, size_t length) -{ - assert(m_blockSize > 1); // m_blockSize = 1 should always override this function - while (length > 0) - { - assert(length >= m_blockSize); - NextPutSingle(inString); - inString += m_blockSize; - length -= m_blockSize; - } -} - -// ************************************************************* - -void Redirector::Initialize(const NameValuePairs ¶meters, int propagation) -{ - m_target = parameters.GetValueWithDefault("RedirectionTargetPointer", (BufferedTransformation*)NULL); - m_behavior = parameters.GetIntValueWithDefault("RedirectionBehavior", PASS_EVERYTHING); - - if (m_target && GetPassSignals()) - m_target->Initialize(parameters, propagation); -} - -// ************************************************************* - -ProxyFilter::ProxyFilter(BufferedTransformation *filter, size_t firstSize, size_t lastSize, BufferedTransformation *attachment) - : FilterWithBufferedInput(firstSize, 1, lastSize, attachment), m_filter(filter) -{ - if (m_filter.get()) - m_filter->Attach(new OutputProxy(*this, false)); -} - -bool ProxyFilter::IsolatedFlush(bool hardFlush, bool blocking) -{ - return m_filter.get() ? m_filter->Flush(hardFlush, -1, blocking) : false; -} - -void ProxyFilter::SetFilter(Filter *filter) -{ - m_filter.reset(filter); - if (filter) - { - OutputProxy *proxy; - std::auto_ptr temp(proxy = new OutputProxy(*this, false)); - m_filter->TransferAllTo(*proxy); - m_filter->Attach(temp.release()); - } -} - -void ProxyFilter::NextPutMultiple(const byte *s, size_t len) -{ - if (m_filter.get()) - m_filter->Put(s, len); -} - -void ProxyFilter::NextPutModifiable(byte *s, size_t len) -{ - if (m_filter.get()) - m_filter->PutModifiable(s, len); -} - -// ************************************************************* - -void RandomNumberSink::IsolatedInitialize(const NameValuePairs ¶meters) -{ - parameters.GetRequiredParameter("RandomNumberSink", "RandomNumberGeneratorPointer", m_rng); -} - -size_t RandomNumberSink::Put2(const byte *begin, size_t length, int messageEnd, bool blocking) -{ - m_rng->IncorporateEntropy(begin, length); - return 0; -} - -size_t ArraySink::Put2(const byte *begin, size_t length, int messageEnd, bool blocking) -{ - if (m_buf+m_total != begin) - memcpy(m_buf+m_total, begin, STDMIN(length, SaturatingSubtract(m_size, m_total))); - m_total += length; - return 0; -} - -byte * ArraySink::CreatePutSpace(size_t &size) -{ - size = SaturatingSubtract(m_size, m_total); - return m_buf + m_total; -} - -void ArraySink::IsolatedInitialize(const NameValuePairs ¶meters) -{ - ByteArrayParameter array; - if (!parameters.GetValue(Name::OutputBuffer(), array)) - throw InvalidArgument("ArraySink: missing OutputBuffer argument"); - m_buf = array.begin(); - m_size = array.size(); - m_total = 0; -} - -size_t ArrayXorSink::Put2(const byte *begin, size_t length, int messageEnd, bool blocking) -{ - xorbuf(m_buf+m_total, begin, STDMIN(length, SaturatingSubtract(m_size, m_total))); - m_total += length; - return 0; -} - -// ************************************************************* - -StreamTransformationFilter::StreamTransformationFilter(StreamTransformation &c, BufferedTransformation *attachment, BlockPaddingScheme padding, bool allowAuthenticatedSymmetricCipher) - : FilterWithBufferedInput(attachment) - , m_cipher(c) -{ - assert(c.MinLastBlockSize() == 0 || c.MinLastBlockSize() > c.MandatoryBlockSize()); - - if (!allowAuthenticatedSymmetricCipher && dynamic_cast(&c) != 0) - throw InvalidArgument("StreamTransformationFilter: please use AuthenticatedEncryptionFilter and AuthenticatedDecryptionFilter for AuthenticatedSymmetricCipher"); - - IsolatedInitialize(MakeParameters(Name::BlockPaddingScheme(), padding)); -} - -size_t StreamTransformationFilter::LastBlockSize(StreamTransformation &c, BlockPaddingScheme padding) -{ - if (c.MinLastBlockSize() > 0) - return c.MinLastBlockSize(); - else if (c.MandatoryBlockSize() > 1 && !c.IsForwardTransformation() && padding != NO_PADDING && padding != ZEROS_PADDING) - return c.MandatoryBlockSize(); - else - return 0; -} - -void StreamTransformationFilter::InitializeDerivedAndReturnNewSizes(const NameValuePairs ¶meters, size_t &firstSize, size_t &blockSize, size_t &lastSize) -{ - BlockPaddingScheme padding = parameters.GetValueWithDefault(Name::BlockPaddingScheme(), DEFAULT_PADDING); - bool isBlockCipher = (m_cipher.MandatoryBlockSize() > 1 && m_cipher.MinLastBlockSize() == 0); - - if (padding == DEFAULT_PADDING) - m_padding = isBlockCipher ? PKCS_PADDING : NO_PADDING; - else - m_padding = padding; - - if (!isBlockCipher && (m_padding == PKCS_PADDING || m_padding == ONE_AND_ZEROS_PADDING)) - throw InvalidArgument("StreamTransformationFilter: PKCS_PADDING and ONE_AND_ZEROS_PADDING cannot be used with " + m_cipher.AlgorithmName()); - - firstSize = 0; - blockSize = m_cipher.MandatoryBlockSize(); - lastSize = LastBlockSize(m_cipher, m_padding); -} - -void StreamTransformationFilter::FirstPut(const byte *inString) -{ - m_optimalBufferSize = m_cipher.OptimalBlockSize(); - m_optimalBufferSize = (unsigned int)STDMAX(m_optimalBufferSize, RoundDownToMultipleOf(4096U, m_optimalBufferSize)); -} - -void StreamTransformationFilter::NextPutMultiple(const byte *inString, size_t length) -{ - if (!length) - return; - - size_t s = m_cipher.MandatoryBlockSize(); - - do - { - size_t len = m_optimalBufferSize; - byte *space = HelpCreatePutSpace(*AttachedTransformation(), DEFAULT_CHANNEL, s, length, len); - if (len < length) - { - if (len == m_optimalBufferSize) - len -= m_cipher.GetOptimalBlockSizeUsed(); - len = RoundDownToMultipleOf(len, s); - } - else - len = length; - m_cipher.ProcessString(space, inString, len); - AttachedTransformation()->PutModifiable(space, len); - inString += len; - length -= len; - } - while (length > 0); -} - -void StreamTransformationFilter::NextPutModifiable(byte *inString, size_t length) -{ - m_cipher.ProcessString(inString, length); - AttachedTransformation()->PutModifiable(inString, length); -} - -void StreamTransformationFilter::LastPut(const byte *inString, size_t length) -{ - byte *space = NULL; - - switch (m_padding) - { - case NO_PADDING: - case ZEROS_PADDING: - if (length > 0) - { - size_t minLastBlockSize = m_cipher.MinLastBlockSize(); - bool isForwardTransformation = m_cipher.IsForwardTransformation(); - - if (isForwardTransformation && m_padding == ZEROS_PADDING && (minLastBlockSize == 0 || length < minLastBlockSize)) - { - // do padding - size_t blockSize = STDMAX(minLastBlockSize, (size_t)m_cipher.MandatoryBlockSize()); - space = HelpCreatePutSpace(*AttachedTransformation(), DEFAULT_CHANNEL, blockSize); - memcpy(space, inString, length); - memset(space + length, 0, blockSize - length); - m_cipher.ProcessLastBlock(space, space, blockSize); - AttachedTransformation()->Put(space, blockSize); - } - else - { - if (minLastBlockSize == 0) - { - if (isForwardTransformation) - throw InvalidDataFormat("StreamTransformationFilter: plaintext length is not a multiple of block size and NO_PADDING is specified"); - else - throw InvalidCiphertext("StreamTransformationFilter: ciphertext length is not a multiple of block size"); - } - - space = HelpCreatePutSpace(*AttachedTransformation(), DEFAULT_CHANNEL, length, m_optimalBufferSize); - m_cipher.ProcessLastBlock(space, inString, length); - AttachedTransformation()->Put(space, length); - } - } - break; - - case PKCS_PADDING: - case ONE_AND_ZEROS_PADDING: - unsigned int s; - s = m_cipher.MandatoryBlockSize(); - assert(s > 1); - space = HelpCreatePutSpace(*AttachedTransformation(), DEFAULT_CHANNEL, s, m_optimalBufferSize); - if (m_cipher.IsForwardTransformation()) - { - assert(length < s); - memcpy(space, inString, length); - if (m_padding == PKCS_PADDING) - { - assert(s < 256); - byte pad = byte(s-length); - memset(space+length, pad, s-length); - } - else - { - space[length] = 0x80; - memset(space+length+1, 0, s-length-1); - } - m_cipher.ProcessData(space, space, s); - AttachedTransformation()->Put(space, s); - } - else - { - if (length != s) - throw InvalidCiphertext("StreamTransformationFilter: ciphertext length is not a multiple of block size"); - m_cipher.ProcessData(space, inString, s); - if (m_padding == PKCS_PADDING) - { - byte pad = space[s-1]; - if (pad < 1 || pad > s || std::find_if(space+s-pad, space+s, std::bind2nd(std::not_equal_to(), pad)) != space+s) - throw InvalidCiphertext("StreamTransformationFilter: invalid PKCS #7 block padding found"); - length = s-pad; - } - else - { - while (length > 1 && space[length-1] == 0) - --length; - if (space[--length] != 0x80) - throw InvalidCiphertext("StreamTransformationFilter: invalid ones-and-zeros padding found"); - } - AttachedTransformation()->Put(space, length); - } - break; - - default: - assert(false); - } -} - -// ************************************************************* - -HashFilter::HashFilter(HashTransformation &hm, BufferedTransformation *attachment, bool putMessage, int truncatedDigestSize, const std::string &messagePutChannel, const std::string &hashPutChannel) - : m_hashModule(hm), m_putMessage(putMessage), m_messagePutChannel(messagePutChannel), m_hashPutChannel(hashPutChannel) -{ - m_digestSize = truncatedDigestSize < 0 ? m_hashModule.DigestSize() : truncatedDigestSize; - Detach(attachment); -} - -void HashFilter::IsolatedInitialize(const NameValuePairs ¶meters) -{ - m_putMessage = parameters.GetValueWithDefault(Name::PutMessage(), false); - int s = parameters.GetIntValueWithDefault(Name::TruncatedDigestSize(), -1); - m_digestSize = s < 0 ? m_hashModule.DigestSize() : s; -} - -size_t HashFilter::Put2(const byte *inString, size_t length, int messageEnd, bool blocking) -{ - FILTER_BEGIN; - if (m_putMessage) - FILTER_OUTPUT3(1, 0, inString, length, 0, m_messagePutChannel); - m_hashModule.Update(inString, length); - if (messageEnd) - { - { - size_t size; - m_space = HelpCreatePutSpace(*AttachedTransformation(), m_hashPutChannel, m_digestSize, m_digestSize, size = m_digestSize); - m_hashModule.TruncatedFinal(m_space, m_digestSize); - } - FILTER_OUTPUT3(2, 0, m_space, m_digestSize, messageEnd, m_hashPutChannel); - } - FILTER_END_NO_MESSAGE_END; -} - -// ************************************************************* - -HashVerificationFilter::HashVerificationFilter(HashTransformation &hm, BufferedTransformation *attachment, word32 flags, int truncatedDigestSize) - : FilterWithBufferedInput(attachment) - , m_hashModule(hm) -{ - IsolatedInitialize(MakeParameters(Name::HashVerificationFilterFlags(), flags)(Name::TruncatedDigestSize(), truncatedDigestSize)); -} - -void HashVerificationFilter::InitializeDerivedAndReturnNewSizes(const NameValuePairs ¶meters, size_t &firstSize, size_t &blockSize, size_t &lastSize) -{ - m_flags = parameters.GetValueWithDefault(Name::HashVerificationFilterFlags(), (word32)DEFAULT_FLAGS); - int s = parameters.GetIntValueWithDefault(Name::TruncatedDigestSize(), -1); - m_digestSize = s < 0 ? m_hashModule.DigestSize() : s; - m_verified = false; - firstSize = m_flags & HASH_AT_BEGIN ? m_digestSize : 0; - blockSize = 1; - lastSize = m_flags & HASH_AT_BEGIN ? 0 : m_digestSize; -} - -void HashVerificationFilter::FirstPut(const byte *inString) -{ - if (m_flags & HASH_AT_BEGIN) - { - m_expectedHash.New(m_digestSize); - memcpy(m_expectedHash, inString, m_expectedHash.size()); - if (m_flags & PUT_HASH) - AttachedTransformation()->Put(inString, m_expectedHash.size()); - } -} - -void HashVerificationFilter::NextPutMultiple(const byte *inString, size_t length) -{ - m_hashModule.Update(inString, length); - if (m_flags & PUT_MESSAGE) - AttachedTransformation()->Put(inString, length); -} - -void HashVerificationFilter::LastPut(const byte *inString, size_t length) -{ - if (m_flags & HASH_AT_BEGIN) - { - assert(length == 0); - m_verified = m_hashModule.TruncatedVerify(m_expectedHash, m_digestSize); - } - else - { - m_verified = (length==m_digestSize && m_hashModule.TruncatedVerify(inString, length)); - if (m_flags & PUT_HASH) - AttachedTransformation()->Put(inString, length); - } - - if (m_flags & PUT_RESULT) - AttachedTransformation()->Put(m_verified); - - if ((m_flags & THROW_EXCEPTION) && !m_verified) - throw HashVerificationFailed(); -} - -// ************************************************************* - -AuthenticatedEncryptionFilter::AuthenticatedEncryptionFilter(AuthenticatedSymmetricCipher &c, BufferedTransformation *attachment, - bool putAAD, int truncatedDigestSize, const std::string &macChannel, BlockPaddingScheme padding) - : StreamTransformationFilter(c, attachment, padding, true) - , m_hf(c, new OutputProxy(*this, false), putAAD, truncatedDigestSize, AAD_CHANNEL, macChannel) -{ - assert(c.IsForwardTransformation()); -} - -void AuthenticatedEncryptionFilter::IsolatedInitialize(const NameValuePairs ¶meters) -{ - m_hf.IsolatedInitialize(parameters); - StreamTransformationFilter::IsolatedInitialize(parameters); -} - -byte * AuthenticatedEncryptionFilter::ChannelCreatePutSpace(const std::string &channel, size_t &size) -{ - if (channel.empty()) - return StreamTransformationFilter::CreatePutSpace(size); - - if (channel == AAD_CHANNEL) - return m_hf.CreatePutSpace(size); - - throw InvalidChannelName("AuthenticatedEncryptionFilter", channel); -} - -size_t AuthenticatedEncryptionFilter::ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking) -{ - if (channel.empty()) - return StreamTransformationFilter::Put2(begin, length, messageEnd, blocking); - - if (channel == AAD_CHANNEL) - return m_hf.Put2(begin, length, 0, blocking); - - throw InvalidChannelName("AuthenticatedEncryptionFilter", channel); -} - -void AuthenticatedEncryptionFilter::LastPut(const byte *inString, size_t length) -{ - StreamTransformationFilter::LastPut(inString, length); - m_hf.MessageEnd(); -} - -// ************************************************************* - -AuthenticatedDecryptionFilter::AuthenticatedDecryptionFilter(AuthenticatedSymmetricCipher &c, BufferedTransformation *attachment, word32 flags, int truncatedDigestSize, BlockPaddingScheme padding) - : FilterWithBufferedInput(attachment) - , m_hashVerifier(c, new OutputProxy(*this, false)) - , m_streamFilter(c, new OutputProxy(*this, false), padding, true) -{ - assert(!c.IsForwardTransformation() || c.IsSelfInverting()); - IsolatedInitialize(MakeParameters(Name::BlockPaddingScheme(), padding)(Name::AuthenticatedDecryptionFilterFlags(), flags)(Name::TruncatedDigestSize(), truncatedDigestSize)); -} - -void AuthenticatedDecryptionFilter::InitializeDerivedAndReturnNewSizes(const NameValuePairs ¶meters, size_t &firstSize, size_t &blockSize, size_t &lastSize) -{ - word32 flags = parameters.GetValueWithDefault(Name::AuthenticatedDecryptionFilterFlags(), (word32)DEFAULT_FLAGS); - - m_hashVerifier.Initialize(CombinedNameValuePairs(parameters, MakeParameters(Name::HashVerificationFilterFlags(), flags))); - m_streamFilter.Initialize(parameters); - - firstSize = m_hashVerifier.m_firstSize; - blockSize = 1; - lastSize = m_hashVerifier.m_lastSize; -} - -byte * AuthenticatedDecryptionFilter::ChannelCreatePutSpace(const std::string &channel, size_t &size) -{ - if (channel.empty()) - return m_streamFilter.CreatePutSpace(size); - - if (channel == AAD_CHANNEL) - return m_hashVerifier.CreatePutSpace(size); - - throw InvalidChannelName("AuthenticatedDecryptionFilter", channel); -} - -size_t AuthenticatedDecryptionFilter::ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking) -{ - if (channel.empty()) - { - if (m_lastSize > 0) - m_hashVerifier.ForceNextPut(); - return FilterWithBufferedInput::Put2(begin, length, messageEnd, blocking); - } - - if (channel == AAD_CHANNEL) - return m_hashVerifier.Put2(begin, length, 0, blocking); - - throw InvalidChannelName("AuthenticatedDecryptionFilter", channel); -} - -void AuthenticatedDecryptionFilter::FirstPut(const byte *inString) -{ - m_hashVerifier.Put(inString, m_firstSize); -} - -void AuthenticatedDecryptionFilter::NextPutMultiple(const byte *inString, size_t length) -{ - m_streamFilter.Put(inString, length); -} - -void AuthenticatedDecryptionFilter::LastPut(const byte *inString, size_t length) -{ - m_streamFilter.MessageEnd(); - m_hashVerifier.PutMessageEnd(inString, length); -} - -// ************************************************************* - -void SignerFilter::IsolatedInitialize(const NameValuePairs ¶meters) -{ - m_putMessage = parameters.GetValueWithDefault(Name::PutMessage(), false); - m_messageAccumulator.reset(m_signer.NewSignatureAccumulator(m_rng)); -} - -size_t SignerFilter::Put2(const byte *inString, size_t length, int messageEnd, bool blocking) -{ - FILTER_BEGIN; - m_messageAccumulator->Update(inString, length); - if (m_putMessage) - FILTER_OUTPUT(1, inString, length, 0); - if (messageEnd) - { - m_buf.New(m_signer.SignatureLength()); - m_signer.Sign(m_rng, m_messageAccumulator.release(), m_buf); - FILTER_OUTPUT(2, m_buf, m_buf.size(), messageEnd); - m_messageAccumulator.reset(m_signer.NewSignatureAccumulator(m_rng)); - } - FILTER_END_NO_MESSAGE_END; -} - -SignatureVerificationFilter::SignatureVerificationFilter(const PK_Verifier &verifier, BufferedTransformation *attachment, word32 flags) - : FilterWithBufferedInput(attachment) - , m_verifier(verifier) -{ - IsolatedInitialize(MakeParameters(Name::SignatureVerificationFilterFlags(), flags)); -} - -void SignatureVerificationFilter::InitializeDerivedAndReturnNewSizes(const NameValuePairs ¶meters, size_t &firstSize, size_t &blockSize, size_t &lastSize) -{ - m_flags = parameters.GetValueWithDefault(Name::SignatureVerificationFilterFlags(), (word32)DEFAULT_FLAGS); - m_messageAccumulator.reset(m_verifier.NewVerificationAccumulator()); - size_t size = m_verifier.SignatureLength(); - assert(size != 0); // TODO: handle recoverable signature scheme - m_verified = false; - firstSize = m_flags & SIGNATURE_AT_BEGIN ? size : 0; - blockSize = 1; - lastSize = m_flags & SIGNATURE_AT_BEGIN ? 0 : size; -} - -void SignatureVerificationFilter::FirstPut(const byte *inString) -{ - if (m_flags & SIGNATURE_AT_BEGIN) - { - if (m_verifier.SignatureUpfront()) - m_verifier.InputSignature(*m_messageAccumulator, inString, m_verifier.SignatureLength()); - else - { - m_signature.New(m_verifier.SignatureLength()); - memcpy(m_signature, inString, m_signature.size()); - } - - if (m_flags & PUT_SIGNATURE) - AttachedTransformation()->Put(inString, m_signature.size()); - } - else - { - assert(!m_verifier.SignatureUpfront()); - } -} - -void SignatureVerificationFilter::NextPutMultiple(const byte *inString, size_t length) -{ - m_messageAccumulator->Update(inString, length); - if (m_flags & PUT_MESSAGE) - AttachedTransformation()->Put(inString, length); -} - -void SignatureVerificationFilter::LastPut(const byte *inString, size_t length) -{ - if (m_flags & SIGNATURE_AT_BEGIN) - { - assert(length == 0); - m_verifier.InputSignature(*m_messageAccumulator, m_signature, m_signature.size()); - m_verified = m_verifier.VerifyAndRestart(*m_messageAccumulator); - } - else - { - m_verifier.InputSignature(*m_messageAccumulator, inString, length); - m_verified = m_verifier.VerifyAndRestart(*m_messageAccumulator); - if (m_flags & PUT_SIGNATURE) - AttachedTransformation()->Put(inString, length); - } - - if (m_flags & PUT_RESULT) - AttachedTransformation()->Put(m_verified); - - if ((m_flags & THROW_EXCEPTION) && !m_verified) - throw SignatureVerificationFailed(); -} - -// ************************************************************* - -size_t Source::PumpAll2(bool blocking) -{ - unsigned int messageCount = UINT_MAX; - do { - RETURN_IF_NONZERO(PumpMessages2(messageCount, blocking)); - } while(messageCount == UINT_MAX); - - return 0; -} - -bool Store::GetNextMessage() -{ - if (!m_messageEnd && !AnyRetrievable()) - { - m_messageEnd=true; - return true; - } - else - return false; -} - -unsigned int Store::CopyMessagesTo(BufferedTransformation &target, unsigned int count, const std::string &channel) const -{ - if (m_messageEnd || count == 0) - return 0; - else - { - CopyTo(target, ULONG_MAX, channel); - if (GetAutoSignalPropagation()) - target.ChannelMessageEnd(channel, GetAutoSignalPropagation()-1); - return 1; - } -} - -void StringStore::StoreInitialize(const NameValuePairs ¶meters) -{ - ConstByteArrayParameter array; - if (!parameters.GetValue(Name::InputBuffer(), array)) - throw InvalidArgument("StringStore: missing InputBuffer argument"); - m_store = array.begin(); - m_length = array.size(); - m_count = 0; -} - -size_t StringStore::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - lword position = 0; - size_t blockedBytes = CopyRangeTo2(target, position, transferBytes, channel, blocking); - m_count += (size_t)position; - transferBytes = position; - return blockedBytes; -} - -size_t StringStore::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const -{ - size_t i = UnsignedMin(m_length, m_count+begin); - size_t len = UnsignedMin(m_length-i, end-begin); - size_t blockedBytes = target.ChannelPut2(channel, m_store+i, len, 0, blocking); - if (!blockedBytes) - begin += len; - return blockedBytes; -} - -void RandomNumberStore::StoreInitialize(const NameValuePairs ¶meters) -{ - parameters.GetRequiredParameter("RandomNumberStore", "RandomNumberGeneratorPointer", m_rng); - int length; - parameters.GetRequiredIntParameter("RandomNumberStore", "RandomNumberStoreSize", length); - m_length = length; -} - -size_t RandomNumberStore::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - if (!blocking) - throw NotImplemented("RandomNumberStore: nonblocking transfer is not implemented by this object"); - - transferBytes = UnsignedMin(transferBytes, m_length - m_count); - m_rng->GenerateIntoBufferedTransformation(target, channel, transferBytes); - m_count += transferBytes; - - return 0; -} - -size_t NullStore::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const -{ - static const byte nullBytes[128] = {0}; - while (begin < end) - { - size_t len = (size_t)STDMIN(end-begin, lword(128)); - size_t blockedBytes = target.ChannelPut2(channel, nullBytes, len, 0, blocking); - if (blockedBytes) - return blockedBytes; - begin += len; - } - return 0; -} - -size_t NullStore::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - lword begin = 0; - size_t blockedBytes = NullStore::CopyRangeTo2(target, begin, transferBytes, channel, blocking); - transferBytes = begin; - m_size -= begin; - return blockedBytes; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/filters.h b/CryptoPP/filters.h deleted file mode 100644 index c72a4ece3..000000000 --- a/CryptoPP/filters.h +++ /dev/null @@ -1,810 +0,0 @@ -#ifndef CRYPTOPP_FILTERS_H -#define CRYPTOPP_FILTERS_H - -//! \file - -#include "simple.h" -#include "secblock.h" -#include "misc.h" -#include "smartptr.h" -#include "queue.h" -#include "algparam.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -/// provides an implementation of BufferedTransformation's attachment interface -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Filter : public BufferedTransformation, public NotCopyable -{ -public: - Filter(BufferedTransformation *attachment = NULL); - - bool Attachable() {return true;} - BufferedTransformation *AttachedTransformation(); - const BufferedTransformation *AttachedTransformation() const; - void Detach(BufferedTransformation *newAttachment = NULL); - - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const; - - void Initialize(const NameValuePairs ¶meters=g_nullNameValuePairs, int propagation=-1); - bool Flush(bool hardFlush, int propagation=-1, bool blocking=true); - bool MessageSeriesEnd(int propagation=-1, bool blocking=true); - -protected: - virtual BufferedTransformation * NewDefaultAttachment() const; - void Insert(Filter *nextFilter); // insert filter after this one - - virtual bool ShouldPropagateMessageEnd() const {return true;} - virtual bool ShouldPropagateMessageSeriesEnd() const {return true;} - - void PropagateInitialize(const NameValuePairs ¶meters, int propagation); - - size_t Output(int outputSite, const byte *inString, size_t length, int messageEnd, bool blocking, const std::string &channel=DEFAULT_CHANNEL); - size_t OutputModifiable(int outputSite, byte *inString, size_t length, int messageEnd, bool blocking, const std::string &channel=DEFAULT_CHANNEL); - bool OutputMessageEnd(int outputSite, int propagation, bool blocking, const std::string &channel=DEFAULT_CHANNEL); - bool OutputFlush(int outputSite, bool hardFlush, int propagation, bool blocking, const std::string &channel=DEFAULT_CHANNEL); - bool OutputMessageSeriesEnd(int outputSite, int propagation, bool blocking, const std::string &channel=DEFAULT_CHANNEL); - -private: - member_ptr m_attachment; - -protected: - size_t m_inputPosition; - int m_continueAt; -}; - -struct CRYPTOPP_DLL FilterPutSpaceHelper -{ - // desiredSize is how much to ask target, bufferSize is how much to allocate in m_tempSpace - byte *HelpCreatePutSpace(BufferedTransformation &target, const std::string &channel, size_t minSize, size_t desiredSize, size_t &bufferSize) - { - assert(desiredSize >= minSize && bufferSize >= minSize); - if (m_tempSpace.size() < minSize) - { - byte *result = target.ChannelCreatePutSpace(channel, desiredSize); - if (desiredSize >= minSize) - { - bufferSize = desiredSize; - return result; - } - m_tempSpace.New(bufferSize); - } - - bufferSize = m_tempSpace.size(); - return m_tempSpace.begin(); - } - byte *HelpCreatePutSpace(BufferedTransformation &target, const std::string &channel, size_t minSize) - {return HelpCreatePutSpace(target, channel, minSize, minSize, minSize);} - byte *HelpCreatePutSpace(BufferedTransformation &target, const std::string &channel, size_t minSize, size_t bufferSize) - {return HelpCreatePutSpace(target, channel, minSize, minSize, bufferSize);} - SecByteBlock m_tempSpace; -}; - -//! measure how many byte and messages pass through, also serves as valve -class CRYPTOPP_DLL MeterFilter : public Bufferless -{ -public: - MeterFilter(BufferedTransformation *attachment=NULL, bool transparent=true) - : m_transparent(transparent) {Detach(attachment); ResetMeter();} - - void SetTransparent(bool transparent) {m_transparent = transparent;} - void AddRangeToSkip(unsigned int message, lword position, lword size, bool sortNow = true); - void ResetMeter(); - void IsolatedInitialize(const NameValuePairs ¶meters) {ResetMeter();} - - lword GetCurrentMessageBytes() const {return m_currentMessageBytes;} - lword GetTotalBytes() {return m_totalBytes;} - unsigned int GetCurrentSeriesMessages() {return m_currentSeriesMessages;} - unsigned int GetTotalMessages() {return m_totalMessages;} - unsigned int GetTotalMessageSeries() {return m_totalMessageSeries;} - - byte * CreatePutSpace(size_t &size) - {return AttachedTransformation()->CreatePutSpace(size);} - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); - size_t PutModifiable2(byte *inString, size_t length, int messageEnd, bool blocking); - bool IsolatedMessageSeriesEnd(bool blocking); - -private: - size_t PutMaybeModifiable(byte *inString, size_t length, int messageEnd, bool blocking, bool modifiable); - bool ShouldPropagateMessageEnd() const {return m_transparent;} - bool ShouldPropagateMessageSeriesEnd() const {return m_transparent;} - - struct MessageRange - { - inline bool operator<(const MessageRange &b) const // BCB2006 workaround: this has to be a member function - {return message < b.message || (message == b.message && position < b.position);} - unsigned int message; lword position; lword size; - }; - - bool m_transparent; - lword m_currentMessageBytes, m_totalBytes; - unsigned int m_currentSeriesMessages, m_totalMessages, m_totalMessageSeries; - std::deque m_rangesToSkip; - byte *m_begin; - size_t m_length; -}; - -//! _ -class CRYPTOPP_DLL TransparentFilter : public MeterFilter -{ -public: - TransparentFilter(BufferedTransformation *attachment=NULL) : MeterFilter(attachment, true) {} -}; - -//! _ -class CRYPTOPP_DLL OpaqueFilter : public MeterFilter -{ -public: - OpaqueFilter(BufferedTransformation *attachment=NULL) : MeterFilter(attachment, false) {} -}; - -/*! FilterWithBufferedInput divides up the input stream into - a first block, a number of middle blocks, and a last block. - First and last blocks are optional, and middle blocks may - be a stream instead (i.e. blockSize == 1). -*/ -class CRYPTOPP_DLL FilterWithBufferedInput : public Filter -{ -public: - FilterWithBufferedInput(BufferedTransformation *attachment); - //! firstSize and lastSize may be 0, blockSize must be at least 1 - FilterWithBufferedInput(size_t firstSize, size_t blockSize, size_t lastSize, BufferedTransformation *attachment); - - void IsolatedInitialize(const NameValuePairs ¶meters); - size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking) - { - return PutMaybeModifiable(const_cast(inString), length, messageEnd, blocking, false); - } - size_t PutModifiable2(byte *inString, size_t length, int messageEnd, bool blocking) - { - return PutMaybeModifiable(inString, length, messageEnd, blocking, true); - } - /*! calls ForceNextPut() if hardFlush is true */ - bool IsolatedFlush(bool hardFlush, bool blocking); - - /*! The input buffer may contain more than blockSize bytes if lastSize != 0. - ForceNextPut() forces a call to NextPut() if this is the case. - */ - void ForceNextPut(); - -protected: - bool DidFirstPut() {return m_firstInputDone;} - - virtual void InitializeDerivedAndReturnNewSizes(const NameValuePairs ¶meters, size_t &firstSize, size_t &blockSize, size_t &lastSize) - {InitializeDerived(parameters);} - virtual void InitializeDerived(const NameValuePairs ¶meters) {} - // FirstPut() is called if (firstSize != 0 and totalLength >= firstSize) - // or (firstSize == 0 and (totalLength > 0 or a MessageEnd() is received)) - virtual void FirstPut(const byte *inString) =0; - // NextPut() is called if totalLength >= firstSize+blockSize+lastSize - virtual void NextPutSingle(const byte *inString) {assert(false);} - // Same as NextPut() except length can be a multiple of blockSize - // Either NextPut() or NextPutMultiple() must be overriden - virtual void NextPutMultiple(const byte *inString, size_t length); - // Same as NextPutMultiple(), but inString can be modified - virtual void NextPutModifiable(byte *inString, size_t length) - {NextPutMultiple(inString, length);} - // LastPut() is always called - // if totalLength < firstSize then length == totalLength - // else if totalLength <= firstSize+lastSize then length == totalLength-firstSize - // else lastSize <= length < lastSize+blockSize - virtual void LastPut(const byte *inString, size_t length) =0; - virtual void FlushDerived() {} - -protected: - size_t PutMaybeModifiable(byte *begin, size_t length, int messageEnd, bool blocking, bool modifiable); - void NextPutMaybeModifiable(byte *inString, size_t length, bool modifiable) - { - if (modifiable) NextPutModifiable(inString, length); - else NextPutMultiple(inString, length); - } - - // This function should no longer be used, put this here to cause a compiler error - // if someone tries to override NextPut(). - virtual int NextPut(const byte *inString, size_t length) {assert(false); return 0;} - - class BlockQueue - { - public: - void ResetQueue(size_t blockSize, size_t maxBlocks); - byte *GetBlock(); - byte *GetContigousBlocks(size_t &numberOfBytes); - size_t GetAll(byte *outString); - void Put(const byte *inString, size_t length); - size_t CurrentSize() const {return m_size;} - size_t MaxSize() const {return m_buffer.size();} - - private: - SecByteBlock m_buffer; - size_t m_blockSize, m_maxBlocks, m_size; - byte *m_begin; - }; - - size_t m_firstSize, m_blockSize, m_lastSize; - bool m_firstInputDone; - BlockQueue m_queue; -}; - -//! _ -class CRYPTOPP_DLL FilterWithInputQueue : public Filter -{ -public: - FilterWithInputQueue(BufferedTransformation *attachment=NULL) : Filter(attachment) {} - - size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking) - { - if (!blocking) - throw BlockingInputOnly("FilterWithInputQueue"); - - m_inQueue.Put(inString, length); - if (messageEnd) - { - IsolatedMessageEnd(blocking); - Output(0, NULL, 0, messageEnd, blocking); - } - return 0; - } - -protected: - virtual bool IsolatedMessageEnd(bool blocking) =0; - void IsolatedInitialize(const NameValuePairs ¶meters) {m_inQueue.Clear();} - - ByteQueue m_inQueue; -}; - -struct BlockPaddingSchemeDef -{ - enum BlockPaddingScheme {NO_PADDING, ZEROS_PADDING, PKCS_PADDING, ONE_AND_ZEROS_PADDING, DEFAULT_PADDING}; -}; - -//! Filter Wrapper for StreamTransformation, optionally handling padding/unpadding when needed -class CRYPTOPP_DLL StreamTransformationFilter : public FilterWithBufferedInput, public BlockPaddingSchemeDef, private FilterPutSpaceHelper -{ -public: - /*! DEFAULT_PADDING means PKCS_PADDING if c.MandatoryBlockSize() > 1 && c.MinLastBlockSize() == 0 (e.g. ECB or CBC mode), - otherwise NO_PADDING (OFB, CFB, CTR, CBC-CTS modes). - See http://www.weidai.com/scan-mirror/csp.html for details of the padding schemes. */ - StreamTransformationFilter(StreamTransformation &c, BufferedTransformation *attachment = NULL, BlockPaddingScheme padding = DEFAULT_PADDING, bool allowAuthenticatedSymmetricCipher = false); - - std::string AlgorithmName() const {return m_cipher.AlgorithmName();} - -protected: - void InitializeDerivedAndReturnNewSizes(const NameValuePairs ¶meters, size_t &firstSize, size_t &blockSize, size_t &lastSize); - void FirstPut(const byte *inString); - void NextPutMultiple(const byte *inString, size_t length); - void NextPutModifiable(byte *inString, size_t length); - void LastPut(const byte *inString, size_t length); - - static size_t LastBlockSize(StreamTransformation &c, BlockPaddingScheme padding); - - StreamTransformation &m_cipher; - BlockPaddingScheme m_padding; - unsigned int m_optimalBufferSize; -}; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY -typedef StreamTransformationFilter StreamCipherFilter; -#endif - -//! Filter Wrapper for HashTransformation -class CRYPTOPP_DLL HashFilter : public Bufferless, private FilterPutSpaceHelper -{ -public: - HashFilter(HashTransformation &hm, BufferedTransformation *attachment = NULL, bool putMessage=false, int truncatedDigestSize=-1, const std::string &messagePutChannel=DEFAULT_CHANNEL, const std::string &hashPutChannel=DEFAULT_CHANNEL); - - std::string AlgorithmName() const {return m_hashModule.AlgorithmName();} - void IsolatedInitialize(const NameValuePairs ¶meters); - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); - byte * CreatePutSpace(size_t &size) {return m_hashModule.CreateUpdateSpace(size);} - -private: - HashTransformation &m_hashModule; - bool m_putMessage; - unsigned int m_digestSize; - byte *m_space; - std::string m_messagePutChannel, m_hashPutChannel; -}; - -//! Filter Wrapper for HashTransformation -class CRYPTOPP_DLL HashVerificationFilter : public FilterWithBufferedInput -{ -public: - class HashVerificationFailed : public Exception - { - public: - HashVerificationFailed() - : Exception(DATA_INTEGRITY_CHECK_FAILED, "HashVerificationFilter: message hash or MAC not valid") {} - }; - - enum Flags {HASH_AT_END=0, HASH_AT_BEGIN=1, PUT_MESSAGE=2, PUT_HASH=4, PUT_RESULT=8, THROW_EXCEPTION=16, DEFAULT_FLAGS = HASH_AT_BEGIN | PUT_RESULT}; - HashVerificationFilter(HashTransformation &hm, BufferedTransformation *attachment = NULL, word32 flags = DEFAULT_FLAGS, int truncatedDigestSize=-1); - - std::string AlgorithmName() const {return m_hashModule.AlgorithmName();} - bool GetLastResult() const {return m_verified;} - -protected: - void InitializeDerivedAndReturnNewSizes(const NameValuePairs ¶meters, size_t &firstSize, size_t &blockSize, size_t &lastSize); - void FirstPut(const byte *inString); - void NextPutMultiple(const byte *inString, size_t length); - void LastPut(const byte *inString, size_t length); - -private: - friend class AuthenticatedDecryptionFilter; - - HashTransformation &m_hashModule; - word32 m_flags; - unsigned int m_digestSize; - bool m_verified; - SecByteBlock m_expectedHash; -}; - -typedef HashVerificationFilter HashVerifier; // for backwards compatibility - -//! Filter wrapper for encrypting with AuthenticatedSymmetricCipher, optionally handling padding/unpadding when needed -/*! Additional authenticated data should be given in channel "AAD". If putAAD is true, AAD will be Put() to the attached BufferedTransformation in channel "AAD". */ -class CRYPTOPP_DLL AuthenticatedEncryptionFilter : public StreamTransformationFilter -{ -public: - /*! See StreamTransformationFilter for documentation on BlockPaddingScheme */ - AuthenticatedEncryptionFilter(AuthenticatedSymmetricCipher &c, BufferedTransformation *attachment = NULL, bool putAAD=false, int truncatedDigestSize=-1, const std::string &macChannel=DEFAULT_CHANNEL, BlockPaddingScheme padding = DEFAULT_PADDING); - - void IsolatedInitialize(const NameValuePairs ¶meters); - byte * ChannelCreatePutSpace(const std::string &channel, size_t &size); - size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking); - void LastPut(const byte *inString, size_t length); - -protected: - HashFilter m_hf; -}; - -//! Filter wrapper for decrypting with AuthenticatedSymmetricCipher, optionally handling padding/unpadding when needed -/*! Additional authenticated data should be given in channel "AAD". */ -class CRYPTOPP_DLL AuthenticatedDecryptionFilter : public FilterWithBufferedInput, public BlockPaddingSchemeDef -{ -public: - enum Flags {MAC_AT_END=0, MAC_AT_BEGIN=1, THROW_EXCEPTION=16, DEFAULT_FLAGS = THROW_EXCEPTION}; - - /*! See StreamTransformationFilter for documentation on BlockPaddingScheme */ - AuthenticatedDecryptionFilter(AuthenticatedSymmetricCipher &c, BufferedTransformation *attachment = NULL, word32 flags = DEFAULT_FLAGS, int truncatedDigestSize=-1, BlockPaddingScheme padding = DEFAULT_PADDING); - - std::string AlgorithmName() const {return m_hashVerifier.AlgorithmName();} - byte * ChannelCreatePutSpace(const std::string &channel, size_t &size); - size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking); - bool GetLastResult() const {return m_hashVerifier.GetLastResult();} - -protected: - void InitializeDerivedAndReturnNewSizes(const NameValuePairs ¶meters, size_t &firstSize, size_t &blockSize, size_t &lastSize); - void FirstPut(const byte *inString); - void NextPutMultiple(const byte *inString, size_t length); - void LastPut(const byte *inString, size_t length); - - HashVerificationFilter m_hashVerifier; - StreamTransformationFilter m_streamFilter; -}; - -//! Filter Wrapper for PK_Signer -class CRYPTOPP_DLL SignerFilter : public Unflushable -{ -public: - SignerFilter(RandomNumberGenerator &rng, const PK_Signer &signer, BufferedTransformation *attachment = NULL, bool putMessage=false) - : m_rng(rng), m_signer(signer), m_messageAccumulator(signer.NewSignatureAccumulator(rng)), m_putMessage(putMessage) {Detach(attachment);} - - std::string AlgorithmName() const {return m_signer.AlgorithmName();} - - void IsolatedInitialize(const NameValuePairs ¶meters); - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); - -private: - RandomNumberGenerator &m_rng; - const PK_Signer &m_signer; - member_ptr m_messageAccumulator; - bool m_putMessage; - SecByteBlock m_buf; -}; - -//! Filter Wrapper for PK_Verifier -class CRYPTOPP_DLL SignatureVerificationFilter : public FilterWithBufferedInput -{ -public: - class SignatureVerificationFailed : public Exception - { - public: - SignatureVerificationFailed() - : Exception(DATA_INTEGRITY_CHECK_FAILED, "VerifierFilter: digital signature not valid") {} - }; - - enum Flags {SIGNATURE_AT_END=0, SIGNATURE_AT_BEGIN=1, PUT_MESSAGE=2, PUT_SIGNATURE=4, PUT_RESULT=8, THROW_EXCEPTION=16, DEFAULT_FLAGS = SIGNATURE_AT_BEGIN | PUT_RESULT}; - SignatureVerificationFilter(const PK_Verifier &verifier, BufferedTransformation *attachment = NULL, word32 flags = DEFAULT_FLAGS); - - std::string AlgorithmName() const {return m_verifier.AlgorithmName();} - - bool GetLastResult() const {return m_verified;} - -protected: - void InitializeDerivedAndReturnNewSizes(const NameValuePairs ¶meters, size_t &firstSize, size_t &blockSize, size_t &lastSize); - void FirstPut(const byte *inString); - void NextPutMultiple(const byte *inString, size_t length); - void LastPut(const byte *inString, size_t length); - -private: - const PK_Verifier &m_verifier; - member_ptr m_messageAccumulator; - word32 m_flags; - SecByteBlock m_signature; - bool m_verified; -}; - -typedef SignatureVerificationFilter VerifierFilter; // for backwards compatibility - -//! Redirect input to another BufferedTransformation without owning it -class CRYPTOPP_DLL Redirector : public CustomSignalPropagation -{ -public: - enum Behavior - { - DATA_ONLY = 0x00, - PASS_SIGNALS = 0x01, - PASS_WAIT_OBJECTS = 0x02, - PASS_EVERYTHING = PASS_SIGNALS | PASS_WAIT_OBJECTS - }; - - Redirector() : m_target(NULL), m_behavior(PASS_EVERYTHING) {} - Redirector(BufferedTransformation &target, Behavior behavior=PASS_EVERYTHING) - : m_target(&target), m_behavior(behavior) {} - - void Redirect(BufferedTransformation &target) {m_target = ⌖} - void StopRedirection() {m_target = NULL;} - - Behavior GetBehavior() {return (Behavior) m_behavior;} - void SetBehavior(Behavior behavior) {m_behavior=behavior;} - bool GetPassSignals() const {return (m_behavior & PASS_SIGNALS) != 0;} - void SetPassSignals(bool pass) { if (pass) m_behavior |= PASS_SIGNALS; else m_behavior &= ~(word32) PASS_SIGNALS; } - bool GetPassWaitObjects() const {return (m_behavior & PASS_WAIT_OBJECTS) != 0;} - void SetPassWaitObjects(bool pass) { if (pass) m_behavior |= PASS_WAIT_OBJECTS; else m_behavior &= ~(word32) PASS_WAIT_OBJECTS; } - - bool CanModifyInput() const - {return m_target ? m_target->CanModifyInput() : false;} - - void Initialize(const NameValuePairs ¶meters, int propagation); - byte * CreatePutSpace(size_t &size) - {return m_target ? m_target->CreatePutSpace(size) : (byte *)(size=0, NULL);} - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking) - {return m_target ? m_target->Put2(begin, length, GetPassSignals() ? messageEnd : 0, blocking) : 0;} - bool Flush(bool hardFlush, int propagation=-1, bool blocking=true) - {return m_target && GetPassSignals() ? m_target->Flush(hardFlush, propagation, blocking) : false;} - bool MessageSeriesEnd(int propagation=-1, bool blocking=true) - {return m_target && GetPassSignals() ? m_target->MessageSeriesEnd(propagation, blocking) : false;} - - byte * ChannelCreatePutSpace(const std::string &channel, size_t &size) - {return m_target ? m_target->ChannelCreatePutSpace(channel, size) : (byte *)(size=0, NULL);} - size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking) - {return m_target ? m_target->ChannelPut2(channel, begin, length, GetPassSignals() ? messageEnd : 0, blocking) : 0;} - size_t ChannelPutModifiable2(const std::string &channel, byte *begin, size_t length, int messageEnd, bool blocking) - {return m_target ? m_target->ChannelPutModifiable2(channel, begin, length, GetPassSignals() ? messageEnd : 0, blocking) : 0;} - bool ChannelFlush(const std::string &channel, bool completeFlush, int propagation=-1, bool blocking=true) - {return m_target && GetPassSignals() ? m_target->ChannelFlush(channel, completeFlush, propagation, blocking) : false;} - bool ChannelMessageSeriesEnd(const std::string &channel, int propagation=-1, bool blocking=true) - {return m_target && GetPassSignals() ? m_target->ChannelMessageSeriesEnd(channel, propagation, blocking) : false;} - - unsigned int GetMaxWaitObjectCount() const - { return m_target && GetPassWaitObjects() ? m_target->GetMaxWaitObjectCount() : 0; } - void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) - { if (m_target && GetPassWaitObjects()) m_target->GetWaitObjects(container, callStack); } - -private: - BufferedTransformation *m_target; - word32 m_behavior; -}; - -// Used By ProxyFilter -class CRYPTOPP_DLL OutputProxy : public CustomSignalPropagation -{ -public: - OutputProxy(BufferedTransformation &owner, bool passSignal) : m_owner(owner), m_passSignal(passSignal) {} - - bool GetPassSignal() const {return m_passSignal;} - void SetPassSignal(bool passSignal) {m_passSignal = passSignal;} - - byte * CreatePutSpace(size_t &size) - {return m_owner.AttachedTransformation()->CreatePutSpace(size);} - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking) - {return m_owner.AttachedTransformation()->Put2(begin, length, m_passSignal ? messageEnd : 0, blocking);} - size_t PutModifiable2(byte *begin, size_t length, int messageEnd, bool blocking) - {return m_owner.AttachedTransformation()->PutModifiable2(begin, length, m_passSignal ? messageEnd : 0, blocking);} - void Initialize(const NameValuePairs ¶meters=g_nullNameValuePairs, int propagation=-1) - {if (m_passSignal) m_owner.AttachedTransformation()->Initialize(parameters, propagation);} - bool Flush(bool hardFlush, int propagation=-1, bool blocking=true) - {return m_passSignal ? m_owner.AttachedTransformation()->Flush(hardFlush, propagation, blocking) : false;} - bool MessageSeriesEnd(int propagation=-1, bool blocking=true) - {return m_passSignal ? m_owner.AttachedTransformation()->MessageSeriesEnd(propagation, blocking) : false;} - - byte * ChannelCreatePutSpace(const std::string &channel, size_t &size) - {return m_owner.AttachedTransformation()->ChannelCreatePutSpace(channel, size);} - size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking) - {return m_owner.AttachedTransformation()->ChannelPut2(channel, begin, length, m_passSignal ? messageEnd : 0, blocking);} - size_t ChannelPutModifiable2(const std::string &channel, byte *begin, size_t length, int messageEnd, bool blocking) - {return m_owner.AttachedTransformation()->ChannelPutModifiable2(channel, begin, length, m_passSignal ? messageEnd : 0, blocking);} - bool ChannelFlush(const std::string &channel, bool completeFlush, int propagation=-1, bool blocking=true) - {return m_passSignal ? m_owner.AttachedTransformation()->ChannelFlush(channel, completeFlush, propagation, blocking) : false;} - bool ChannelMessageSeriesEnd(const std::string &channel, int propagation=-1, bool blocking=true) - {return m_passSignal ? m_owner.AttachedTransformation()->ChannelMessageSeriesEnd(channel, propagation, blocking) : false;} - -private: - BufferedTransformation &m_owner; - bool m_passSignal; -}; - -//! Base class for Filter classes that are proxies for a chain of other filters. -class CRYPTOPP_DLL ProxyFilter : public FilterWithBufferedInput -{ -public: - ProxyFilter(BufferedTransformation *filter, size_t firstSize, size_t lastSize, BufferedTransformation *attachment); - - bool IsolatedFlush(bool hardFlush, bool blocking); - - void SetFilter(Filter *filter); - void NextPutMultiple(const byte *s, size_t len); - void NextPutModifiable(byte *inString, size_t length); - -protected: - member_ptr m_filter; -}; - -//! simple proxy filter that doesn't modify the underlying filter's input or output -class CRYPTOPP_DLL SimpleProxyFilter : public ProxyFilter -{ -public: - SimpleProxyFilter(BufferedTransformation *filter, BufferedTransformation *attachment) - : ProxyFilter(filter, 0, 0, attachment) {} - - void FirstPut(const byte *) {} - void LastPut(const byte *, size_t) {m_filter->MessageEnd();} -}; - -//! proxy for the filter created by PK_Encryptor::CreateEncryptionFilter -/*! This class is here just to provide symmetry with VerifierFilter. */ -class CRYPTOPP_DLL PK_EncryptorFilter : public SimpleProxyFilter -{ -public: - PK_EncryptorFilter(RandomNumberGenerator &rng, const PK_Encryptor &encryptor, BufferedTransformation *attachment = NULL) - : SimpleProxyFilter(encryptor.CreateEncryptionFilter(rng), attachment) {} -}; - -//! proxy for the filter created by PK_Decryptor::CreateDecryptionFilter -/*! This class is here just to provide symmetry with SignerFilter. */ -class CRYPTOPP_DLL PK_DecryptorFilter : public SimpleProxyFilter -{ -public: - PK_DecryptorFilter(RandomNumberGenerator &rng, const PK_Decryptor &decryptor, BufferedTransformation *attachment = NULL) - : SimpleProxyFilter(decryptor.CreateDecryptionFilter(rng), attachment) {} -}; - -//! Append input to a string object -template -class StringSinkTemplate : public Bufferless -{ -public: - // VC60 workaround: no T::char_type - typedef typename T::traits_type::char_type char_type; - - StringSinkTemplate(T &output) - : m_output(&output) {assert(sizeof(output[0])==1);} - - void IsolatedInitialize(const NameValuePairs ¶meters) - {if (!parameters.GetValue("OutputStringPointer", m_output)) throw InvalidArgument("StringSink: OutputStringPointer not specified");} - - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking) - { - if (length > 0) - { - typename T::size_type size = m_output->size(); - if (length < size && size + length > m_output->capacity()) - m_output->reserve(2*size); - m_output->append((const char_type *)begin, (const char_type *)begin+length); - } - return 0; - } - -private: - T *m_output; -}; - -//! Append input to an std::string -CRYPTOPP_DLL_TEMPLATE_CLASS StringSinkTemplate; -typedef StringSinkTemplate StringSink; - -//! incorporates input into RNG as additional entropy -class RandomNumberSink : public Bufferless -{ -public: - RandomNumberSink() - : m_rng(NULL) {} - - RandomNumberSink(RandomNumberGenerator &rng) - : m_rng(&rng) {} - - void IsolatedInitialize(const NameValuePairs ¶meters); - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); - -private: - RandomNumberGenerator *m_rng; -}; - -//! Copy input to a memory buffer -class CRYPTOPP_DLL ArraySink : public Bufferless -{ -public: - ArraySink(const NameValuePairs ¶meters = g_nullNameValuePairs) {IsolatedInitialize(parameters);} - ArraySink(byte *buf, size_t size) : m_buf(buf), m_size(size), m_total(0) {} - - size_t AvailableSize() {return SaturatingSubtract(m_size, m_total);} - lword TotalPutLength() {return m_total;} - - void IsolatedInitialize(const NameValuePairs ¶meters); - byte * CreatePutSpace(size_t &size); - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); - -protected: - byte *m_buf; - size_t m_size; - lword m_total; -}; - -//! Xor input to a memory buffer -class CRYPTOPP_DLL ArrayXorSink : public ArraySink -{ -public: - ArrayXorSink(byte *buf, size_t size) - : ArraySink(buf, size) {} - - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); - byte * CreatePutSpace(size_t &size) {return BufferedTransformation::CreatePutSpace(size);} -}; - -//! string-based implementation of Store interface -class StringStore : public Store -{ -public: - StringStore(const char *string = NULL) - {StoreInitialize(MakeParameters("InputBuffer", ConstByteArrayParameter(string)));} - StringStore(const byte *string, size_t length) - {StoreInitialize(MakeParameters("InputBuffer", ConstByteArrayParameter(string, length)));} - template StringStore(const T &string) - {StoreInitialize(MakeParameters("InputBuffer", ConstByteArrayParameter(string)));} - - CRYPTOPP_DLL size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - CRYPTOPP_DLL size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const; - -private: - CRYPTOPP_DLL void StoreInitialize(const NameValuePairs ¶meters); - - const byte *m_store; - size_t m_length, m_count; -}; - -//! RNG-based implementation of Source interface -class CRYPTOPP_DLL RandomNumberStore : public Store -{ -public: - RandomNumberStore() - : m_rng(NULL), m_length(0), m_count(0) {} - - RandomNumberStore(RandomNumberGenerator &rng, lword length) - : m_rng(&rng), m_length(length), m_count(0) {} - - bool AnyRetrievable() const {return MaxRetrievable() != 0;} - lword MaxRetrievable() const {return m_length-m_count;} - - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const - { - throw NotImplemented("RandomNumberStore: CopyRangeTo2() is not supported by this store"); - } - -private: - void StoreInitialize(const NameValuePairs ¶meters); - - RandomNumberGenerator *m_rng; - lword m_length, m_count; -}; - -//! empty store -class CRYPTOPP_DLL NullStore : public Store -{ -public: - NullStore(lword size = ULONG_MAX) : m_size(size) {} - void StoreInitialize(const NameValuePairs ¶meters) {} - lword MaxRetrievable() const {return m_size;} - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const; - -private: - lword m_size; -}; - -//! A Filter that pumps data into its attachment as input -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Source : public InputRejecting -{ -public: - Source(BufferedTransformation *attachment = NULL) - {Source::Detach(attachment);} - - lword Pump(lword pumpMax=size_t(0)-1) - {Pump2(pumpMax); return pumpMax;} - unsigned int PumpMessages(unsigned int count=UINT_MAX) - {PumpMessages2(count); return count;} - void PumpAll() - {PumpAll2();} - virtual size_t Pump2(lword &byteCount, bool blocking=true) =0; - virtual size_t PumpMessages2(unsigned int &messageCount, bool blocking=true) =0; - virtual size_t PumpAll2(bool blocking=true); - virtual bool SourceExhausted() const =0; - -protected: - void SourceInitialize(bool pumpAll, const NameValuePairs ¶meters) - { - IsolatedInitialize(parameters); - if (pumpAll) - PumpAll(); - } -}; - -//! Turn a Store into a Source -template -class SourceTemplate : public Source -{ -public: - SourceTemplate(BufferedTransformation *attachment) - : Source(attachment) {} - void IsolatedInitialize(const NameValuePairs ¶meters) - {m_store.IsolatedInitialize(parameters);} - size_t Pump2(lword &byteCount, bool blocking=true) - {return m_store.TransferTo2(*AttachedTransformation(), byteCount, DEFAULT_CHANNEL, blocking);} - size_t PumpMessages2(unsigned int &messageCount, bool blocking=true) - {return m_store.TransferMessagesTo2(*AttachedTransformation(), messageCount, DEFAULT_CHANNEL, blocking);} - size_t PumpAll2(bool blocking=true) - {return m_store.TransferAllTo2(*AttachedTransformation(), DEFAULT_CHANNEL, blocking);} - bool SourceExhausted() const - {return !m_store.AnyRetrievable() && !m_store.AnyMessages();} - void SetAutoSignalPropagation(int propagation) - {m_store.SetAutoSignalPropagation(propagation);} - int GetAutoSignalPropagation() const - {return m_store.GetAutoSignalPropagation();} - -protected: - T m_store; -}; - -//! string-based implementation of Source interface -class CRYPTOPP_DLL StringSource : public SourceTemplate -{ -public: - StringSource(BufferedTransformation *attachment = NULL) - : SourceTemplate(attachment) {} - //! zero terminated string as source - StringSource(const char *string, bool pumpAll, BufferedTransformation *attachment = NULL) - : SourceTemplate(attachment) {SourceInitialize(pumpAll, MakeParameters("InputBuffer", ConstByteArrayParameter(string)));} - //! binary byte array as source - StringSource(const byte *string, size_t length, bool pumpAll, BufferedTransformation *attachment = NULL) - : SourceTemplate(attachment) {SourceInitialize(pumpAll, MakeParameters("InputBuffer", ConstByteArrayParameter(string, length)));} - //! std::string as source - StringSource(const std::string &string, bool pumpAll, BufferedTransformation *attachment = NULL) - : SourceTemplate(attachment) {SourceInitialize(pumpAll, MakeParameters("InputBuffer", ConstByteArrayParameter(string)));} -}; - -//! use the third constructor for an array source -typedef StringSource ArraySource; - -//! RNG-based implementation of Source interface -class CRYPTOPP_DLL RandomNumberSource : public SourceTemplate -{ -public: - RandomNumberSource(RandomNumberGenerator &rng, int length, bool pumpAll, BufferedTransformation *attachment = NULL) - : SourceTemplate(attachment) - {SourceInitialize(pumpAll, MakeParameters("RandomNumberGeneratorPointer", &rng)("RandomNumberStoreSize", length));} -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/fips140.cpp b/CryptoPP/fips140.cpp deleted file mode 100644 index 1fcf59014..000000000 --- a/CryptoPP/fips140.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// fips140.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "fips140.h" -#include "trdlocal.h" // needs to be included last for cygwin - -NAMESPACE_BEGIN(CryptoPP) - -// Define this to 1 to turn on FIPS 140-2 compliance features, including additional tests during -// startup, random number generation, and key generation. These tests may affect performance. -#ifndef CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 -#define CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 0 -#endif - -#if (CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 && !defined(THREADS_AVAILABLE)) -#error FIPS 140-2 compliance requires the availability of thread local storage. -#endif - -#if (CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 && !defined(OS_RNG_AVAILABLE)) -#error FIPS 140-2 compliance requires the availability of OS provided RNG. -#endif - -PowerUpSelfTestStatus g_powerUpSelfTestStatus = POWER_UP_SELF_TEST_NOT_DONE; - -bool FIPS_140_2_ComplianceEnabled() -{ - return CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2; -} - -void SimulatePowerUpSelfTestFailure() -{ - g_powerUpSelfTestStatus = POWER_UP_SELF_TEST_FAILED; -} - -PowerUpSelfTestStatus CRYPTOPP_API GetPowerUpSelfTestStatus() -{ - return g_powerUpSelfTestStatus; -} - -#if CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 -ThreadLocalStorage & AccessPowerUpSelfTestInProgress() -{ - static ThreadLocalStorage selfTestInProgress; - return selfTestInProgress; -} -#endif - -bool PowerUpSelfTestInProgressOnThisThread() -{ -#if CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 - return AccessPowerUpSelfTestInProgress().GetValue() != NULL; -#else - assert(false); // should not be called - return false; -#endif -} - -void SetPowerUpSelfTestInProgressOnThisThread(bool inProgress) -{ -#if CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 - AccessPowerUpSelfTestInProgress().SetValue((void *)inProgress); -#endif -} - -void EncryptionPairwiseConsistencyTest_FIPS_140_Only(const PK_Encryptor &encryptor, const PK_Decryptor &decryptor) -{ -#if CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 - EncryptionPairwiseConsistencyTest(encryptor, decryptor); -#endif -} - -void SignaturePairwiseConsistencyTest_FIPS_140_Only(const PK_Signer &signer, const PK_Verifier &verifier) -{ -#if CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 - SignaturePairwiseConsistencyTest(signer, verifier); -#endif -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/fips140.h b/CryptoPP/fips140.h deleted file mode 100644 index a3e538613..000000000 --- a/CryptoPP/fips140.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef CRYPTOPP_FIPS140_H -#define CRYPTOPP_FIPS140_H - -/*! \file - FIPS 140 related functions and classes. -*/ - -#include "cryptlib.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! exception thrown when a crypto algorithm is used after a self test fails -class CRYPTOPP_DLL SelfTestFailure : public Exception -{ -public: - explicit SelfTestFailure(const std::string &s) : Exception(OTHER_ERROR, s) {} -}; - -//! returns whether FIPS 140-2 compliance features were enabled at compile time -CRYPTOPP_DLL bool CRYPTOPP_API FIPS_140_2_ComplianceEnabled(); - -//! enum values representing status of the power-up self test -enum PowerUpSelfTestStatus {POWER_UP_SELF_TEST_NOT_DONE, POWER_UP_SELF_TEST_FAILED, POWER_UP_SELF_TEST_PASSED}; - -//! perform the power-up self test, and set the self test status -CRYPTOPP_DLL void CRYPTOPP_API DoPowerUpSelfTest(const char *moduleFilename, const byte *expectedModuleMac); - -//! perform the power-up self test using the filename of this DLL and the embedded module MAC -CRYPTOPP_DLL void CRYPTOPP_API DoDllPowerUpSelfTest(); - -//! set the power-up self test status to POWER_UP_SELF_TEST_FAILED -CRYPTOPP_DLL void CRYPTOPP_API SimulatePowerUpSelfTestFailure(); - -//! return the current power-up self test status -CRYPTOPP_DLL PowerUpSelfTestStatus CRYPTOPP_API GetPowerUpSelfTestStatus(); - -typedef PowerUpSelfTestStatus (CRYPTOPP_API * PGetPowerUpSelfTestStatus)(); - -CRYPTOPP_DLL MessageAuthenticationCode * CRYPTOPP_API NewIntegrityCheckingMAC(); - -CRYPTOPP_DLL bool CRYPTOPP_API IntegrityCheckModule(const char *moduleFilename, const byte *expectedModuleMac, SecByteBlock *pActualMac = NULL, unsigned long *pMacFileLocation = NULL); - -// this is used by Algorithm constructor to allow Algorithm objects to be constructed for the self test -bool PowerUpSelfTestInProgressOnThisThread(); - -void SetPowerUpSelfTestInProgressOnThisThread(bool inProgress); - -void SignaturePairwiseConsistencyTest(const PK_Signer &signer, const PK_Verifier &verifier); -void EncryptionPairwiseConsistencyTest(const PK_Encryptor &encryptor, const PK_Decryptor &decryptor); - -void SignaturePairwiseConsistencyTest_FIPS_140_Only(const PK_Signer &signer, const PK_Verifier &verifier); -void EncryptionPairwiseConsistencyTest_FIPS_140_Only(const PK_Encryptor &encryptor, const PK_Decryptor &decryptor); - -#define CRYPTOPP_DUMMY_DLL_MAC "MAC_51f34b8db820ae8" - -NAMESPACE_END - -#endif diff --git a/CryptoPP/fltrimpl.h b/CryptoPP/fltrimpl.h deleted file mode 100644 index 4087d7d9f..000000000 --- a/CryptoPP/fltrimpl.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef CRYPTOPP_FLTRIMPL_H -#define CRYPTOPP_FLTRIMPL_H - -#define FILTER_BEGIN \ - switch (m_continueAt) \ - { \ - case 0: \ - m_inputPosition = 0; - -#define FILTER_END_NO_MESSAGE_END_NO_RETURN \ - break; \ - default: \ - assert(false); \ - } - -#define FILTER_END_NO_MESSAGE_END \ - FILTER_END_NO_MESSAGE_END_NO_RETURN \ - return 0; - -/* -#define FILTER_END \ - case -1: \ - if (messageEnd && Output(-1, NULL, 0, messageEnd, blocking)) \ - return 1; \ - FILTER_END_NO_MESSAGE_END -*/ - -#define FILTER_OUTPUT3(site, statement, output, length, messageEnd, channel) \ - {\ - case site: \ - statement; \ - if (Output(site, output, length, messageEnd, blocking, channel)) \ - return STDMAX(size_t(1), length-m_inputPosition);\ - } - -#define FILTER_OUTPUT2(site, statement, output, length, messageEnd) \ - FILTER_OUTPUT3(site, statement, output, length, messageEnd, DEFAULT_CHANNEL) - -#define FILTER_OUTPUT(site, output, length, messageEnd) \ - FILTER_OUTPUT2(site, 0, output, length, messageEnd) - -#define FILTER_OUTPUT_BYTE(site, output) \ - FILTER_OUTPUT(site, &(const byte &)(byte)output, 1, 0) - -#define FILTER_OUTPUT2_MODIFIABLE(site, statement, output, length, messageEnd) \ - {\ - case site: \ - statement; \ - if (OutputModifiable(site, output, length, messageEnd, blocking)) \ - return STDMAX(size_t(1), length-m_inputPosition);\ - } - -#define FILTER_OUTPUT_MODIFIABLE(site, output, length, messageEnd) \ - FILTER_OUTPUT2_MODIFIABLE(site, 0, output, length, messageEnd) - -#define FILTER_OUTPUT2_MAYBE_MODIFIABLE(site, statement, output, length, messageEnd, modifiable) \ - {\ - case site: \ - statement; \ - if (modifiable ? OutputModifiable(site, output, length, messageEnd, blocking) : Output(site, output, length, messageEnd, blocking)) \ - return STDMAX(size_t(1), length-m_inputPosition);\ - } - -#define FILTER_OUTPUT_MAYBE_MODIFIABLE(site, output, length, messageEnd, modifiable) \ - FILTER_OUTPUT2_MAYBE_MODIFIABLE(site, 0, output, length, messageEnd, modifiable) - -#endif diff --git a/CryptoPP/gcm.cpp b/CryptoPP/gcm.cpp deleted file mode 100644 index 2304f96d8..000000000 --- a/CryptoPP/gcm.cpp +++ /dev/null @@ -1,828 +0,0 @@ -// gcm.cpp - written and placed in the public domain by Wei Dai - -// use "cl /EP /P /DCRYPTOPP_GENERATE_X64_MASM gcm.cpp" to generate MASM code - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS -#ifndef CRYPTOPP_GENERATE_X64_MASM - -#include "gcm.h" -#include "cpu.h" - -NAMESPACE_BEGIN(CryptoPP) - -word16 GCM_Base::s_reductionTable[256]; -volatile bool GCM_Base::s_reductionTableInitialized = false; - -void GCM_Base::GCTR::IncrementCounterBy256() -{ - IncrementCounterByOne(m_counterArray+BlockSize()-4, 3); -} - -#if 0 -// preserved for testing -void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c) -{ - word64 Z0=0, Z1=0, V0, V1; - - typedef BlockGetAndPut Block; - Block::Get(a)(V0)(V1); - - for (int i=0; i<16; i++) - { - for (int j=0x80; j!=0; j>>=1) - { - int x = b[i] & j; - Z0 ^= x ? V0 : 0; - Z1 ^= x ? V1 : 0; - x = (int)V1 & 1; - V1 = (V1>>1) | (V0<<63); - V0 = (V0>>1) ^ (x ? W64LIT(0xe1) << 56 : 0); - } - } - Block::Put(NULL, c)(Z0)(Z1); -} - -__m128i _mm_clmulepi64_si128(const __m128i &a, const __m128i &b, int i) -{ - word64 A[1] = {ByteReverse(((word64*)&a)[i&1])}; - word64 B[1] = {ByteReverse(((word64*)&b)[i>>4])}; - - PolynomialMod2 pa((byte *)A, 8); - PolynomialMod2 pb((byte *)B, 8); - PolynomialMod2 c = pa*pb; - - __m128i output; - for (int i=0; i<16; i++) - ((byte *)&output)[i] = c.GetByte(i); - return output; -} -#endif - -#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE -inline static void SSE2_Xor16(byte *a, const byte *b, const byte *c) -{ -#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE - *(__m128i *)a = _mm_xor_si128(*(__m128i *)b, *(__m128i *)c); -#else - asm ("movdqa %1, %%xmm0; pxor %2, %%xmm0; movdqa %%xmm0, %0;" : "=m" (a[0]) : "m"(b[0]), "m"(c[0])); -#endif -} -#endif - -inline static void Xor16(byte *a, const byte *b, const byte *c) -{ - ((word64 *)a)[0] = ((word64 *)b)[0] ^ ((word64 *)c)[0]; - ((word64 *)a)[1] = ((word64 *)b)[1] ^ ((word64 *)c)[1]; -} - -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE -static CRYPTOPP_ALIGN_DATA(16) const word64 s_clmulConstants64[] = { - W64LIT(0xe100000000000000), W64LIT(0xc200000000000000), - W64LIT(0x08090a0b0c0d0e0f), W64LIT(0x0001020304050607), - W64LIT(0x0001020304050607), W64LIT(0x08090a0b0c0d0e0f)}; -static const __m128i *s_clmulConstants = (const __m128i *)s_clmulConstants64; -static const unsigned int s_clmulTableSizeInBlocks = 8; - -inline __m128i CLMUL_Reduce(__m128i c0, __m128i c1, __m128i c2, const __m128i &r) -{ - /* - The polynomial to be reduced is c0 * x^128 + c1 * x^64 + c2. c0t below refers to the most - significant half of c0 as a polynomial, which, due to GCM's bit reflection, are in the - rightmost bit positions, and the lowest byte addresses. - - c1 ^= c0t * 0xc200000000000000 - c2t ^= c0t - t = shift (c1t ^ c0b) left 1 bit - c2 ^= t * 0xe100000000000000 - c2t ^= c1b - shift c2 left 1 bit and xor in lowest bit of c1t - */ -#if 0 // MSVC 2010 workaround: see http://connect.microsoft.com/VisualStudio/feedback/details/575301 - c2 = _mm_xor_si128(c2, _mm_move_epi64(c0)); -#else - c1 = _mm_xor_si128(c1, _mm_slli_si128(c0, 8)); -#endif - c1 = _mm_xor_si128(c1, _mm_clmulepi64_si128(c0, r, 0x10)); - c0 = _mm_srli_si128(c0, 8); - c0 = _mm_xor_si128(c0, c1); - c0 = _mm_slli_epi64(c0, 1); - c0 = _mm_clmulepi64_si128(c0, r, 0); - c2 = _mm_xor_si128(c2, c0); - c2 = _mm_xor_si128(c2, _mm_srli_si128(c1, 8)); - c1 = _mm_unpacklo_epi64(c1, c2); - c1 = _mm_srli_epi64(c1, 63); - c2 = _mm_slli_epi64(c2, 1); - return _mm_xor_si128(c2, c1); -} - -inline __m128i CLMUL_GF_Mul(const __m128i &x, const __m128i &h, const __m128i &r) -{ - __m128i c0 = _mm_clmulepi64_si128(x,h,0); - __m128i c1 = _mm_xor_si128(_mm_clmulepi64_si128(x,h,1), _mm_clmulepi64_si128(x,h,0x10)); - __m128i c2 = _mm_clmulepi64_si128(x,h,0x11); - - return CLMUL_Reduce(c0, c1, c2, r); -} -#endif - -void GCM_Base::SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms) -{ - BlockCipher &blockCipher = AccessBlockCipher(); - blockCipher.SetKey(userKey, keylength, params); - - if (blockCipher.BlockSize() != REQUIRED_BLOCKSIZE) - throw InvalidArgument(AlgorithmName() + ": block size of underlying block cipher is not 16"); - - int tableSize, i, j, k; - -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - if (HasCLMUL()) - { - params.GetIntValue(Name::TableSize(), tableSize); // avoid "parameter not used" error - tableSize = s_clmulTableSizeInBlocks * REQUIRED_BLOCKSIZE; - } - else -#endif - { - if (params.GetIntValue(Name::TableSize(), tableSize)) - tableSize = (tableSize >= 64*1024) ? 64*1024 : 2*1024; - else - tableSize = (GetTablesOption() == GCM_64K_Tables) ? 64*1024 : 2*1024; - -#if defined(_MSC_VER) && (_MSC_VER >= 1300 && _MSC_VER < 1400) - // VC 2003 workaround: compiler generates bad code for 64K tables - tableSize = 2*1024; -#endif - } - - m_buffer.resize(3*REQUIRED_BLOCKSIZE + tableSize); - byte *table = MulTable(); - byte *hashKey = HashKey(); - memset(hashKey, 0, REQUIRED_BLOCKSIZE); - blockCipher.ProcessBlock(hashKey); - -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - if (HasCLMUL()) - { - const __m128i r = s_clmulConstants[0]; - __m128i h0 = _mm_shuffle_epi8(_mm_load_si128((__m128i *)hashKey), s_clmulConstants[1]); - __m128i h = h0; - - for (i=0; i Block; - Block::Get(hashKey)(V0)(V1); - - if (tableSize == 64*1024) - { - for (i=0; i<128; i++) - { - k = i%8; - Block::Put(NULL, table+(i/8)*256*16+(size_t(1)<<(11-k)))(V0)(V1); - - int x = (int)V1 & 1; - V1 = (V1>>1) | (V0<<63); - V0 = (V0>>1) ^ (x ? W64LIT(0xe1) << 56 : 0); - } - - for (i=0; i<16; i++) - { - memset(table+i*256*16, 0, 16); -#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - if (HasSSE2()) - for (j=2; j<=0x80; j*=2) - for (k=1; k>1) | (V0<<63); - V0 = (V0>>1) ^ (x ? W64LIT(0xe1) << 56 : 0); - } - - for (i=0; i<4; i++) - { - memset(table+i*256, 0, 16); - memset(table+1024+i*256, 0, 16); -#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - if (HasSSE2()) - for (j=2; j<=8; j*=2) - for (k=1; k= HASH_BLOCKSIZE) - { - len = GCM_Base::AuthenticateBlocks(iv, len); - iv += (origLen - len); - } - - if (len > 0) - { - memcpy(m_buffer, iv, len); - memset(m_buffer+len, 0, HASH_BLOCKSIZE-len); - GCM_Base::AuthenticateBlocks(m_buffer, HASH_BLOCKSIZE); - } - - PutBlock(NULL, m_buffer)(0)(origLen*8); - GCM_Base::AuthenticateBlocks(m_buffer, HASH_BLOCKSIZE); - - ReverseHashBufferIfNeeded(); - } - - if (m_state >= State_IVSet) - m_ctr.Resynchronize(hashBuffer, REQUIRED_BLOCKSIZE); - else - m_ctr.SetCipherWithIV(cipher, hashBuffer); - - m_ctr.Seek(HASH_BLOCKSIZE); - - memset(hashBuffer, 0, HASH_BLOCKSIZE); -} - -unsigned int GCM_Base::OptimalDataAlignment() const -{ - return -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) - HasSSE2() ? 16 : -#endif - GetBlockCipher().OptimalDataAlignment(); -} - -#pragma warning(disable: 4731) // frame pointer register 'ebp' modified by inline assembly code - -#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM - -#ifdef CRYPTOPP_X64_MASM_AVAILABLE -extern "C" { -void GCM_AuthenticateBlocks_2K(const byte *data, size_t blocks, word64 *hashBuffer, const word16 *reductionTable); -void GCM_AuthenticateBlocks_64K(const byte *data, size_t blocks, word64 *hashBuffer); -} -#endif - -#ifndef CRYPTOPP_GENERATE_X64_MASM - -size_t GCM_Base::AuthenticateBlocks(const byte *data, size_t len) -{ -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - if (HasCLMUL()) - { - const __m128i *table = (const __m128i *)MulTable(); - __m128i x = _mm_load_si128((__m128i *)HashBuffer()); - const __m128i r = s_clmulConstants[0], bswapMask = s_clmulConstants[1], bswapMask2 = s_clmulConstants[2]; - - while (len >= 16) - { - size_t s = UnsignedMin(len/16, s_clmulTableSizeInBlocks), i=0; - __m128i d, d2 = _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)(data+(s-1)*16)), bswapMask2);; - __m128i c0 = _mm_setzero_si128(); - __m128i c1 = _mm_setzero_si128(); - __m128i c2 = _mm_setzero_si128(); - - while (true) - { - __m128i h0 = _mm_load_si128(table+i); - __m128i h1 = _mm_load_si128(table+i+1); - __m128i h01 = _mm_xor_si128(h0, h1); - - if (++i == s) - { - d = _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)data), bswapMask); - d = _mm_xor_si128(d, x); - c0 = _mm_xor_si128(c0, _mm_clmulepi64_si128(d, h0, 0)); - c2 = _mm_xor_si128(c2, _mm_clmulepi64_si128(d, h1, 1)); - d = _mm_xor_si128(d, _mm_shuffle_epi32(d, _MM_SHUFFLE(1, 0, 3, 2))); - c1 = _mm_xor_si128(c1, _mm_clmulepi64_si128(d, h01, 0)); - break; - } - - d = _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)(data+(s-i)*16-8)), bswapMask2); - c0 = _mm_xor_si128(c0, _mm_clmulepi64_si128(d2, h0, 1)); - c2 = _mm_xor_si128(c2, _mm_clmulepi64_si128(d, h1, 1)); - d2 = _mm_xor_si128(d2, d); - c1 = _mm_xor_si128(c1, _mm_clmulepi64_si128(d2, h01, 1)); - - if (++i == s) - { - d = _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)data), bswapMask); - d = _mm_xor_si128(d, x); - c0 = _mm_xor_si128(c0, _mm_clmulepi64_si128(d, h0, 0x10)); - c2 = _mm_xor_si128(c2, _mm_clmulepi64_si128(d, h1, 0x11)); - d = _mm_xor_si128(d, _mm_shuffle_epi32(d, _MM_SHUFFLE(1, 0, 3, 2))); - c1 = _mm_xor_si128(c1, _mm_clmulepi64_si128(d, h01, 0x10)); - break; - } - - d2 = _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)(data+(s-i)*16-8)), bswapMask); - c0 = _mm_xor_si128(c0, _mm_clmulepi64_si128(d, h0, 0x10)); - c2 = _mm_xor_si128(c2, _mm_clmulepi64_si128(d2, h1, 0x10)); - d = _mm_xor_si128(d, d2); - c1 = _mm_xor_si128(c1, _mm_clmulepi64_si128(d, h01, 0x10)); - } - data += s*16; - len -= s*16; - - c1 = _mm_xor_si128(_mm_xor_si128(c1, c0), c2); - x = CLMUL_Reduce(c0, c1, c2, r); - } - - _mm_store_si128((__m128i *)HashBuffer(), x); - return len; - } -#endif - - typedef BlockGetAndPut Block; - word64 *hashBuffer = (word64 *)HashBuffer(); - - switch (2*(m_buffer.size()>=64*1024) -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) - + HasSSE2() -#endif - ) - { - case 0: // non-SSE2 and 2K tables - { - byte *table = MulTable(); - word64 x0 = hashBuffer[0], x1 = hashBuffer[1]; - - do - { - word64 y0, y1, a0, a1, b0, b1, c0, c1, d0, d1; - Block::Get(data)(y0)(y1); - x0 ^= y0; - x1 ^= y1; - - data += HASH_BLOCKSIZE; - len -= HASH_BLOCKSIZE; - - #define READ_TABLE_WORD64_COMMON(a, b, c, d) *(word64 *)(table+(a*1024)+(b*256)+c+d*8) - - #ifdef IS_LITTLE_ENDIAN - #if CRYPTOPP_BOOL_SLOW_WORD64 - word32 z0 = (word32)x0; - word32 z1 = (word32)(x0>>32); - word32 z2 = (word32)x1; - word32 z3 = (word32)(x1>>32); - #define READ_TABLE_WORD64(a, b, c, d, e) READ_TABLE_WORD64_COMMON((d%2), c, (d?(z##c>>((d?d-1:0)*4))&0xf0:(z##c&0xf)<<4), e) - #else - #define READ_TABLE_WORD64(a, b, c, d, e) READ_TABLE_WORD64_COMMON((d%2), c, ((d+8*b)?(x##a>>(((d+8*b)?(d+8*b)-1:1)*4))&0xf0:(x##a&0xf)<<4), e) - #endif - #define GF_MOST_SIG_8BITS(a) (a##1 >> 7*8) - #define GF_SHIFT_8(a) a##1 = (a##1 << 8) ^ (a##0 >> 7*8); a##0 <<= 8; - #else - #define READ_TABLE_WORD64(a, b, c, d, e) READ_TABLE_WORD64_COMMON((1-d%2), c, ((15-d-8*b)?(x##a>>(((15-d-8*b)?(15-d-8*b)-1:0)*4))&0xf0:(x##a&0xf)<<4), e) - #define GF_MOST_SIG_8BITS(a) (a##1 & 0xff) - #define GF_SHIFT_8(a) a##1 = (a##1 >> 8) ^ (a##0 << 7*8); a##0 >>= 8; - #endif - - #define GF_MUL_32BY128(op, a, b, c) \ - a0 op READ_TABLE_WORD64(a, b, c, 0, 0) ^ READ_TABLE_WORD64(a, b, c, 1, 0);\ - a1 op READ_TABLE_WORD64(a, b, c, 0, 1) ^ READ_TABLE_WORD64(a, b, c, 1, 1);\ - b0 op READ_TABLE_WORD64(a, b, c, 2, 0) ^ READ_TABLE_WORD64(a, b, c, 3, 0);\ - b1 op READ_TABLE_WORD64(a, b, c, 2, 1) ^ READ_TABLE_WORD64(a, b, c, 3, 1);\ - c0 op READ_TABLE_WORD64(a, b, c, 4, 0) ^ READ_TABLE_WORD64(a, b, c, 5, 0);\ - c1 op READ_TABLE_WORD64(a, b, c, 4, 1) ^ READ_TABLE_WORD64(a, b, c, 5, 1);\ - d0 op READ_TABLE_WORD64(a, b, c, 6, 0) ^ READ_TABLE_WORD64(a, b, c, 7, 0);\ - d1 op READ_TABLE_WORD64(a, b, c, 6, 1) ^ READ_TABLE_WORD64(a, b, c, 7, 1);\ - - GF_MUL_32BY128(=, 0, 0, 0) - GF_MUL_32BY128(^=, 0, 1, 1) - GF_MUL_32BY128(^=, 1, 0, 2) - GF_MUL_32BY128(^=, 1, 1, 3) - - word32 r = (word32)s_reductionTable[GF_MOST_SIG_8BITS(d)] << 16; - GF_SHIFT_8(d) - c0 ^= d0; c1 ^= d1; - r ^= (word32)s_reductionTable[GF_MOST_SIG_8BITS(c)] << 8; - GF_SHIFT_8(c) - b0 ^= c0; b1 ^= c1; - r ^= s_reductionTable[GF_MOST_SIG_8BITS(b)]; - GF_SHIFT_8(b) - a0 ^= b0; a1 ^= b1; - a0 ^= ConditionalByteReverse(LITTLE_ENDIAN_ORDER, r); - x0 = a0; x1 = a1; - } - while (len >= HASH_BLOCKSIZE); - - hashBuffer[0] = x0; hashBuffer[1] = x1; - return len; - } - - case 2: // non-SSE2 and 64K tables - { - byte *table = MulTable(); - word64 x0 = hashBuffer[0], x1 = hashBuffer[1]; - - do - { - word64 y0, y1, a0, a1; - Block::Get(data)(y0)(y1); - x0 ^= y0; - x1 ^= y1; - - data += HASH_BLOCKSIZE; - len -= HASH_BLOCKSIZE; - - #undef READ_TABLE_WORD64_COMMON - #undef READ_TABLE_WORD64 - - #define READ_TABLE_WORD64_COMMON(a, c, d) *(word64 *)(table+(a)*256*16+(c)+(d)*8) - - #ifdef IS_LITTLE_ENDIAN - #if CRYPTOPP_BOOL_SLOW_WORD64 - word32 z0 = (word32)x0; - word32 z1 = (word32)(x0>>32); - word32 z2 = (word32)x1; - word32 z3 = (word32)(x1>>32); - #define READ_TABLE_WORD64(b, c, d, e) READ_TABLE_WORD64_COMMON(c*4+d, (d?(z##c>>((d?d:1)*8-4))&0xff0:(z##c&0xff)<<4), e) - #else - #define READ_TABLE_WORD64(b, c, d, e) READ_TABLE_WORD64_COMMON(c*4+d, ((d+4*(c%2))?(x##b>>(((d+4*(c%2))?(d+4*(c%2)):1)*8-4))&0xff0:(x##b&0xff)<<4), e) - #endif - #else - #define READ_TABLE_WORD64(b, c, d, e) READ_TABLE_WORD64_COMMON(c*4+d, ((7-d-4*(c%2))?(x##b>>(((7-d-4*(c%2))?(7-d-4*(c%2)):1)*8-4))&0xff0:(x##b&0xff)<<4), e) - #endif - - #define GF_MUL_8BY128(op, b, c, d) \ - a0 op READ_TABLE_WORD64(b, c, d, 0);\ - a1 op READ_TABLE_WORD64(b, c, d, 1);\ - - GF_MUL_8BY128(=, 0, 0, 0) - GF_MUL_8BY128(^=, 0, 0, 1) - GF_MUL_8BY128(^=, 0, 0, 2) - GF_MUL_8BY128(^=, 0, 0, 3) - GF_MUL_8BY128(^=, 0, 1, 0) - GF_MUL_8BY128(^=, 0, 1, 1) - GF_MUL_8BY128(^=, 0, 1, 2) - GF_MUL_8BY128(^=, 0, 1, 3) - GF_MUL_8BY128(^=, 1, 2, 0) - GF_MUL_8BY128(^=, 1, 2, 1) - GF_MUL_8BY128(^=, 1, 2, 2) - GF_MUL_8BY128(^=, 1, 2, 3) - GF_MUL_8BY128(^=, 1, 3, 0) - GF_MUL_8BY128(^=, 1, 3, 1) - GF_MUL_8BY128(^=, 1, 3, 2) - GF_MUL_8BY128(^=, 1, 3, 3) - - x0 = a0; x1 = a1; - } - while (len >= HASH_BLOCKSIZE); - - hashBuffer[0] = x0; hashBuffer[1] = x1; - return len; - } -#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM - -#ifdef CRYPTOPP_X64_MASM_AVAILABLE - case 1: // SSE2 and 2K tables - GCM_AuthenticateBlocks_2K(data, len/16, hashBuffer, s_reductionTable); - return len % 16; - case 3: // SSE2 and 64K tables - GCM_AuthenticateBlocks_64K(data, len/16, hashBuffer); - return len % 16; -#endif - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - case 1: // SSE2 and 2K tables - { - #ifdef __GNUC__ - __asm__ __volatile__ - ( - ".intel_syntax noprefix;" - #elif defined(CRYPTOPP_GENERATE_X64_MASM) - ALIGN 8 - GCM_AuthenticateBlocks_2K PROC FRAME - rex_push_reg rsi - push_reg rdi - push_reg rbx - .endprolog - mov rsi, r8 - mov r11, r9 - #else - AS2( mov WORD_REG(cx), data ) - AS2( mov WORD_REG(dx), len ) - AS2( mov WORD_REG(si), hashBuffer ) - AS2( shr WORD_REG(dx), 4 ) - #endif - - AS_PUSH_IF86( bx) - AS_PUSH_IF86( bp) - - #ifdef __GNUC__ - AS2( mov AS_REG_7, WORD_REG(di)) - #elif CRYPTOPP_BOOL_X86 - AS2( lea AS_REG_7, s_reductionTable) - #endif - - AS2( movdqa xmm0, [WORD_REG(si)] ) - - #define MUL_TABLE_0 WORD_REG(si) + 32 - #define MUL_TABLE_1 WORD_REG(si) + 32 + 1024 - #define RED_TABLE AS_REG_7 - - ASL(0) - AS2( movdqu xmm4, [WORD_REG(cx)] ) - AS2( pxor xmm0, xmm4 ) - - AS2( movd ebx, xmm0 ) - AS2( mov eax, AS_HEX(f0f0f0f0) ) - AS2( and eax, ebx ) - AS2( shl ebx, 4 ) - AS2( and ebx, AS_HEX(f0f0f0f0) ) - AS2( movzx edi, ah ) - AS2( movdqa xmm5, XMMWORD_PTR [MUL_TABLE_1 + WORD_REG(di)] ) - AS2( movzx edi, al ) - AS2( movdqa xmm4, XMMWORD_PTR [MUL_TABLE_1 + WORD_REG(di)] ) - AS2( shr eax, 16 ) - AS2( movzx edi, ah ) - AS2( movdqa xmm3, XMMWORD_PTR [MUL_TABLE_1 + WORD_REG(di)] ) - AS2( movzx edi, al ) - AS2( movdqa xmm2, XMMWORD_PTR [MUL_TABLE_1 + WORD_REG(di)] ) - - #define SSE2_MUL_32BITS(i) \ - AS2( psrldq xmm0, 4 )\ - AS2( movd eax, xmm0 )\ - AS2( and eax, AS_HEX(f0f0f0f0) )\ - AS2( movzx edi, bh )\ - AS2( pxor xmm5, XMMWORD_PTR [MUL_TABLE_0 + (i-1)*256 + WORD_REG(di)] )\ - AS2( movzx edi, bl )\ - AS2( pxor xmm4, XMMWORD_PTR [MUL_TABLE_0 + (i-1)*256 + WORD_REG(di)] )\ - AS2( shr ebx, 16 )\ - AS2( movzx edi, bh )\ - AS2( pxor xmm3, XMMWORD_PTR [MUL_TABLE_0 + (i-1)*256 + WORD_REG(di)] )\ - AS2( movzx edi, bl )\ - AS2( pxor xmm2, XMMWORD_PTR [MUL_TABLE_0 + (i-1)*256 + WORD_REG(di)] )\ - AS2( movd ebx, xmm0 )\ - AS2( shl ebx, 4 )\ - AS2( and ebx, AS_HEX(f0f0f0f0) )\ - AS2( movzx edi, ah )\ - AS2( pxor xmm5, XMMWORD_PTR [MUL_TABLE_1 + i*256 + WORD_REG(di)] )\ - AS2( movzx edi, al )\ - AS2( pxor xmm4, XMMWORD_PTR [MUL_TABLE_1 + i*256 + WORD_REG(di)] )\ - AS2( shr eax, 16 )\ - AS2( movzx edi, ah )\ - AS2( pxor xmm3, XMMWORD_PTR [MUL_TABLE_1 + i*256 + WORD_REG(di)] )\ - AS2( movzx edi, al )\ - AS2( pxor xmm2, XMMWORD_PTR [MUL_TABLE_1 + i*256 + WORD_REG(di)] )\ - - SSE2_MUL_32BITS(1) - SSE2_MUL_32BITS(2) - SSE2_MUL_32BITS(3) - - AS2( movzx edi, bh ) - AS2( pxor xmm5, XMMWORD_PTR [MUL_TABLE_0 + 3*256 + WORD_REG(di)] ) - AS2( movzx edi, bl ) - AS2( pxor xmm4, XMMWORD_PTR [MUL_TABLE_0 + 3*256 + WORD_REG(di)] ) - AS2( shr ebx, 16 ) - AS2( movzx edi, bh ) - AS2( pxor xmm3, XMMWORD_PTR [MUL_TABLE_0 + 3*256 + WORD_REG(di)] ) - AS2( movzx edi, bl ) - AS2( pxor xmm2, XMMWORD_PTR [MUL_TABLE_0 + 3*256 + WORD_REG(di)] ) - - AS2( movdqa xmm0, xmm3 ) - AS2( pslldq xmm3, 1 ) - AS2( pxor xmm2, xmm3 ) - AS2( movdqa xmm1, xmm2 ) - AS2( pslldq xmm2, 1 ) - AS2( pxor xmm5, xmm2 ) - - AS2( psrldq xmm0, 15 ) - AS2( movd WORD_REG(di), xmm0 ) - AS2( movzx eax, WORD PTR [RED_TABLE + WORD_REG(di)*2] ) - AS2( shl eax, 8 ) - - AS2( movdqa xmm0, xmm5 ) - AS2( pslldq xmm5, 1 ) - AS2( pxor xmm4, xmm5 ) - - AS2( psrldq xmm1, 15 ) - AS2( movd WORD_REG(di), xmm1 ) - AS2( xor ax, WORD PTR [RED_TABLE + WORD_REG(di)*2] ) - AS2( shl eax, 8 ) - - AS2( psrldq xmm0, 15 ) - AS2( movd WORD_REG(di), xmm0 ) - AS2( xor ax, WORD PTR [RED_TABLE + WORD_REG(di)*2] ) - - AS2( movd xmm0, eax ) - AS2( pxor xmm0, xmm4 ) - - AS2( add WORD_REG(cx), 16 ) - AS2( sub WORD_REG(dx), 1 ) - ASJ( jnz, 0, b ) - AS2( movdqa [WORD_REG(si)], xmm0 ) - - AS_POP_IF86( bp) - AS_POP_IF86( bx) - - #ifdef __GNUC__ - ".att_syntax prefix;" - : - : "c" (data), "d" (len/16), "S" (hashBuffer), "D" (s_reductionTable) - : "memory", "cc", "%eax" - #if CRYPTOPP_BOOL_X64 - , "%ebx", "%r11" - #endif - ); - #elif defined(CRYPTOPP_GENERATE_X64_MASM) - pop rbx - pop rdi - pop rsi - ret - GCM_AuthenticateBlocks_2K ENDP - #endif - - return len%16; - } - case 3: // SSE2 and 64K tables - { - #ifdef __GNUC__ - __asm__ __volatile__ - ( - ".intel_syntax noprefix;" - #elif defined(CRYPTOPP_GENERATE_X64_MASM) - ALIGN 8 - GCM_AuthenticateBlocks_64K PROC FRAME - rex_push_reg rsi - push_reg rdi - .endprolog - mov rsi, r8 - #else - AS2( mov WORD_REG(cx), data ) - AS2( mov WORD_REG(dx), len ) - AS2( mov WORD_REG(si), hashBuffer ) - AS2( shr WORD_REG(dx), 4 ) - #endif - - AS2( movdqa xmm0, [WORD_REG(si)] ) - - #undef MUL_TABLE - #define MUL_TABLE(i,j) WORD_REG(si) + 32 + (i*4+j)*256*16 - - ASL(1) - AS2( movdqu xmm1, [WORD_REG(cx)] ) - AS2( pxor xmm1, xmm0 ) - AS2( pxor xmm0, xmm0 ) - - #undef SSE2_MUL_32BITS - #define SSE2_MUL_32BITS(i) \ - AS2( movd eax, xmm1 )\ - AS2( psrldq xmm1, 4 )\ - AS2( movzx edi, al )\ - AS2( add WORD_REG(di), WORD_REG(di) )\ - AS2( pxor xmm0, [MUL_TABLE(i,0) + WORD_REG(di)*8] )\ - AS2( movzx edi, ah )\ - AS2( add WORD_REG(di), WORD_REG(di) )\ - AS2( pxor xmm0, [MUL_TABLE(i,1) + WORD_REG(di)*8] )\ - AS2( shr eax, 16 )\ - AS2( movzx edi, al )\ - AS2( add WORD_REG(di), WORD_REG(di) )\ - AS2( pxor xmm0, [MUL_TABLE(i,2) + WORD_REG(di)*8] )\ - AS2( movzx edi, ah )\ - AS2( add WORD_REG(di), WORD_REG(di) )\ - AS2( pxor xmm0, [MUL_TABLE(i,3) + WORD_REG(di)*8] )\ - - SSE2_MUL_32BITS(0) - SSE2_MUL_32BITS(1) - SSE2_MUL_32BITS(2) - SSE2_MUL_32BITS(3) - - AS2( add WORD_REG(cx), 16 ) - AS2( sub WORD_REG(dx), 1 ) - ASJ( jnz, 1, b ) - AS2( movdqa [WORD_REG(si)], xmm0 ) - - #ifdef __GNUC__ - ".att_syntax prefix;" - : - : "c" (data), "d" (len/16), "S" (hashBuffer) - : "memory", "cc", "%edi", "%eax" - ); - #elif defined(CRYPTOPP_GENERATE_X64_MASM) - pop rdi - pop rsi - ret - GCM_AuthenticateBlocks_64K ENDP - #endif - - return len%16; - } -#endif -#ifndef CRYPTOPP_GENERATE_X64_MASM - } - - return len%16; -} - -void GCM_Base::AuthenticateLastHeaderBlock() -{ - if (m_bufferedDataLength > 0) - { - memset(m_buffer+m_bufferedDataLength, 0, HASH_BLOCKSIZE-m_bufferedDataLength); - m_bufferedDataLength = 0; - GCM_Base::AuthenticateBlocks(m_buffer, HASH_BLOCKSIZE); - } -} - -void GCM_Base::AuthenticateLastConfidentialBlock() -{ - GCM_Base::AuthenticateLastHeaderBlock(); - PutBlock(NULL, m_buffer)(m_totalHeaderLength*8)(m_totalMessageLength*8); - GCM_Base::AuthenticateBlocks(m_buffer, HASH_BLOCKSIZE); -} - -void GCM_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize) -{ - m_ctr.Seek(0); - ReverseHashBufferIfNeeded(); - m_ctr.ProcessData(mac, HashBuffer(), macSize); -} - -NAMESPACE_END - -#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM -#endif diff --git a/CryptoPP/gcm.h b/CryptoPP/gcm.h deleted file mode 100644 index 272a51c9c..000000000 --- a/CryptoPP/gcm.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef CRYPTOPP_GCM_H -#define CRYPTOPP_GCM_H - -#include "authenc.h" -#include "modes.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! . -enum GCM_TablesOption {GCM_2K_Tables, GCM_64K_Tables}; - -//! . -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE GCM_Base : public AuthenticatedSymmetricCipherBase -{ -public: - // AuthenticatedSymmetricCipher - std::string AlgorithmName() const - {return GetBlockCipher().AlgorithmName() + std::string("/GCM");} - size_t MinKeyLength() const - {return GetBlockCipher().MinKeyLength();} - size_t MaxKeyLength() const - {return GetBlockCipher().MaxKeyLength();} - size_t DefaultKeyLength() const - {return GetBlockCipher().DefaultKeyLength();} - size_t GetValidKeyLength(size_t n) const - {return GetBlockCipher().GetValidKeyLength(n);} - bool IsValidKeyLength(size_t n) const - {return GetBlockCipher().IsValidKeyLength(n);} - unsigned int OptimalDataAlignment() const; - IV_Requirement IVRequirement() const - {return UNIQUE_IV;} - unsigned int IVSize() const - {return 12;} - unsigned int MinIVLength() const - {return 1;} - unsigned int MaxIVLength() const - {return UINT_MAX;} // (W64LIT(1)<<61)-1 in the standard - unsigned int DigestSize() const - {return 16;} - lword MaxHeaderLength() const - {return (W64LIT(1)<<61)-1;} - lword MaxMessageLength() const - {return ((W64LIT(1)<<39)-256)/8;} - -protected: - // AuthenticatedSymmetricCipherBase - bool AuthenticationIsOnPlaintext() const - {return false;} - unsigned int AuthenticationBlockSize() const - {return HASH_BLOCKSIZE;} - void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms); - void Resync(const byte *iv, size_t len); - size_t AuthenticateBlocks(const byte *data, size_t len); - void AuthenticateLastHeaderBlock(); - void AuthenticateLastConfidentialBlock(); - void AuthenticateLastFooterBlock(byte *mac, size_t macSize); - SymmetricCipher & AccessSymmetricCipher() {return m_ctr;} - - virtual BlockCipher & AccessBlockCipher() =0; - virtual GCM_TablesOption GetTablesOption() const =0; - - const BlockCipher & GetBlockCipher() const {return const_cast(this)->AccessBlockCipher();}; - byte *HashBuffer() {return m_buffer+REQUIRED_BLOCKSIZE;} - byte *HashKey() {return m_buffer+2*REQUIRED_BLOCKSIZE;} - byte *MulTable() {return m_buffer+3*REQUIRED_BLOCKSIZE;} - inline void ReverseHashBufferIfNeeded(); - - class CRYPTOPP_DLL GCTR : public CTR_Mode_ExternalCipher::Encryption - { - protected: - void IncrementCounterBy256(); - }; - - GCTR m_ctr; - static word16 s_reductionTable[256]; - static volatile bool s_reductionTableInitialized; - enum {REQUIRED_BLOCKSIZE = 16, HASH_BLOCKSIZE = 16}; -}; - -//! . -template -class GCM_Final : public GCM_Base -{ -public: - static std::string StaticAlgorithmName() - {return T_BlockCipher::StaticAlgorithmName() + std::string("/GCM");} - bool IsForwardTransformation() const - {return T_IsEncryption;} - -private: - GCM_TablesOption GetTablesOption() const {return T_TablesOption;} - BlockCipher & AccessBlockCipher() {return m_cipher;} - typename T_BlockCipher::Encryption m_cipher; -}; - -//! GCM -template -struct GCM : public AuthenticatedSymmetricCipherDocumentation -{ - typedef GCM_Final Encryption; - typedef GCM_Final Decryption; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/gf256.cpp b/CryptoPP/gf256.cpp deleted file mode 100644 index 72026d1e1..000000000 --- a/CryptoPP/gf256.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// gf256.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "gf256.h" - -NAMESPACE_BEGIN(CryptoPP) - -GF256::Element GF256::Multiply(Element a, Element b) const -{ - word result = 0, t = b; - - for (unsigned int i=0; i<8; i++) - { - result <<= 1; - if (result & 0x100) - result ^= m_modulus; - - t <<= 1; - if (t & 0x100) - result ^= a; - } - - return (GF256::Element) result; -} - -GF256::Element GF256::MultiplicativeInverse(Element a) const -{ - Element result = a; - for (int i=1; i<7; i++) - result = Multiply(Square(result), a); - return Square(result); -} - -NAMESPACE_END diff --git a/CryptoPP/gf256.h b/CryptoPP/gf256.h deleted file mode 100644 index e0ea74826..000000000 --- a/CryptoPP/gf256.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef CRYPTOPP_GF256_H -#define CRYPTOPP_GF256_H - -#include "cryptlib.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! GF(256) with polynomial basis -class GF256 -{ -public: - typedef byte Element; - typedef int RandomizationParameter; - - GF256(byte modulus) : m_modulus(modulus) {} - - Element RandomElement(RandomNumberGenerator &rng, int ignored = 0) const - {return rng.GenerateByte();} - - bool Equal(Element a, Element b) const - {return a==b;} - - Element Zero() const - {return 0;} - - Element Add(Element a, Element b) const - {return a^b;} - - Element& Accumulate(Element &a, Element b) const - {return a^=b;} - - Element Inverse(Element a) const - {return a;} - - Element Subtract(Element a, Element b) const - {return a^b;} - - Element& Reduce(Element &a, Element b) const - {return a^=b;} - - Element Double(Element a) const - {return 0;} - - Element One() const - {return 1;} - - Element Multiply(Element a, Element b) const; - - Element Square(Element a) const - {return Multiply(a, a);} - - bool IsUnit(Element a) const - {return a != 0;} - - Element MultiplicativeInverse(Element a) const; - - Element Divide(Element a, Element b) const - {return Multiply(a, MultiplicativeInverse(b));} - -private: - word m_modulus; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/gf2_32.cpp b/CryptoPP/gf2_32.cpp deleted file mode 100644 index ae4874a40..000000000 --- a/CryptoPP/gf2_32.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// gf2_32.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "misc.h" -#include "gf2_32.h" - -NAMESPACE_BEGIN(CryptoPP) - -GF2_32::Element GF2_32::Multiply(Element a, Element b) const -{ - word32 table[4]; - table[0] = 0; - table[1] = m_modulus; - if (a & 0x80000000) - { - table[2] = m_modulus ^ (a<<1); - table[3] = a<<1; - } - else - { - table[2] = a<<1; - table[3] = m_modulus ^ (a<<1); - } - -#if CRYPTOPP_FAST_ROTATE(32) - b = rotrFixed(b, 30U); - word32 result = table[b&2]; - - for (int i=29; i>=0; --i) - { - b = rotlFixed(b, 1U); - result = (result<<1) ^ table[(b&2) + (result>>31)]; - } - - return (b&1) ? result ^ a : result; -#else - word32 result = table[(b>>30) & 2]; - - for (int i=29; i>=0; --i) - result = (result<<1) ^ table[((b>>i)&2) + (result>>31)]; - - return (b&1) ? result ^ a : result; -#endif -} - -GF2_32::Element GF2_32::MultiplicativeInverse(Element a) const -{ - if (a <= 1) // 1 is a special case - return a; - - // warning - don't try to adapt this algorithm for another situation - word32 g0=m_modulus, g1=a, g2=a; - word32 v0=0, v1=1, v2=1; - - assert(g1); - - while (!(g2 & 0x80000000)) - { - g2 <<= 1; - v2 <<= 1; - } - - g2 <<= 1; - v2 <<= 1; - - g0 ^= g2; - v0 ^= v2; - - while (g0 != 1) - { - if (g1 < g0 || ((g0^g1) < g0 && (g0^g1) < g1)) - { - assert(BitPrecision(g1) <= BitPrecision(g0)); - g2 = g1; - v2 = v1; - } - else - { - assert(BitPrecision(g1) > BitPrecision(g0)); - g2 = g0; g0 = g1; g1 = g2; - v2 = v0; v0 = v1; v1 = v2; - } - - while ((g0^g2) >= g2) - { - assert(BitPrecision(g0) > BitPrecision(g2)); - g2 <<= 1; - v2 <<= 1; - } - - assert(BitPrecision(g0) == BitPrecision(g2)); - g0 ^= g2; - v0 ^= v2; - } - - return v0; -} - -NAMESPACE_END diff --git a/CryptoPP/gf2_32.h b/CryptoPP/gf2_32.h deleted file mode 100644 index 31713f4c0..000000000 --- a/CryptoPP/gf2_32.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef CRYPTOPP_GF2_32_H -#define CRYPTOPP_GF2_32_H - -#include "cryptlib.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! GF(2^32) with polynomial basis -class GF2_32 -{ -public: - typedef word32 Element; - typedef int RandomizationParameter; - - GF2_32(word32 modulus=0x0000008D) : m_modulus(modulus) {} - - Element RandomElement(RandomNumberGenerator &rng, int ignored = 0) const - {return rng.GenerateWord32();} - - bool Equal(Element a, Element b) const - {return a==b;} - - Element Identity() const - {return 0;} - - Element Add(Element a, Element b) const - {return a^b;} - - Element& Accumulate(Element &a, Element b) const - {return a^=b;} - - Element Inverse(Element a) const - {return a;} - - Element Subtract(Element a, Element b) const - {return a^b;} - - Element& Reduce(Element &a, Element b) const - {return a^=b;} - - Element Double(Element a) const - {return 0;} - - Element MultiplicativeIdentity() const - {return 1;} - - Element Multiply(Element a, Element b) const; - - Element Square(Element a) const - {return Multiply(a, a);} - - bool IsUnit(Element a) const - {return a != 0;} - - Element MultiplicativeInverse(Element a) const; - - Element Divide(Element a, Element b) const - {return Multiply(a, MultiplicativeInverse(b));} - -private: - word32 m_modulus; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/gf2n.cpp b/CryptoPP/gf2n.cpp deleted file mode 100644 index bcc56071a..000000000 --- a/CryptoPP/gf2n.cpp +++ /dev/null @@ -1,882 +0,0 @@ -// gf2n.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "gf2n.h" -#include "algebra.h" -#include "words.h" -#include "randpool.h" -#include "asn.h" -#include "oids.h" - -#include - -NAMESPACE_BEGIN(CryptoPP) - -PolynomialMod2::PolynomialMod2() -{ -} - -PolynomialMod2::PolynomialMod2(word value, size_t bitLength) - : reg(BitsToWords(bitLength)) -{ - assert(value==0 || reg.size()>0); - - if (reg.size() > 0) - { - reg[0] = value; - SetWords(reg+1, 0, reg.size()-1); - } -} - -PolynomialMod2::PolynomialMod2(const PolynomialMod2& t) - : reg(t.reg.size()) -{ - CopyWords(reg, t.reg, reg.size()); -} - -void PolynomialMod2::Randomize(RandomNumberGenerator &rng, size_t nbits) -{ - const size_t nbytes = nbits/8 + 1; - SecByteBlock buf(nbytes); - rng.GenerateBlock(buf, nbytes); - buf[0] = (byte)Crop(buf[0], nbits % 8); - Decode(buf, nbytes); -} - -PolynomialMod2 PolynomialMod2::AllOnes(size_t bitLength) -{ - PolynomialMod2 result((word)0, bitLength); - SetWords(result.reg, ~(word)0, result.reg.size()); - if (bitLength%WORD_BITS) - result.reg[result.reg.size()-1] = (word)Crop(result.reg[result.reg.size()-1], bitLength%WORD_BITS); - return result; -} - -void PolynomialMod2::SetBit(size_t n, int value) -{ - if (value) - { - reg.CleanGrow(n/WORD_BITS + 1); - reg[n/WORD_BITS] |= (word(1) << (n%WORD_BITS)); - } - else - { - if (n/WORD_BITS < reg.size()) - reg[n/WORD_BITS] &= ~(word(1) << (n%WORD_BITS)); - } -} - -byte PolynomialMod2::GetByte(size_t n) const -{ - if (n/WORD_SIZE >= reg.size()) - return 0; - else - return byte(reg[n/WORD_SIZE] >> ((n%WORD_SIZE)*8)); -} - -void PolynomialMod2::SetByte(size_t n, byte value) -{ - reg.CleanGrow(BytesToWords(n+1)); - reg[n/WORD_SIZE] &= ~(word(0xff) << 8*(n%WORD_SIZE)); - reg[n/WORD_SIZE] |= (word(value) << 8*(n%WORD_SIZE)); -} - -PolynomialMod2 PolynomialMod2::Monomial(size_t i) -{ - PolynomialMod2 r((word)0, i+1); - r.SetBit(i); - return r; -} - -PolynomialMod2 PolynomialMod2::Trinomial(size_t t0, size_t t1, size_t t2) -{ - PolynomialMod2 r((word)0, t0+1); - r.SetBit(t0); - r.SetBit(t1); - r.SetBit(t2); - return r; -} - -PolynomialMod2 PolynomialMod2::Pentanomial(size_t t0, size_t t1, size_t t2, size_t t3, size_t t4) -{ - PolynomialMod2 r((word)0, t0+1); - r.SetBit(t0); - r.SetBit(t1); - r.SetBit(t2); - r.SetBit(t3); - r.SetBit(t4); - return r; -} - -template -struct NewPolynomialMod2 -{ - PolynomialMod2 * operator()() const - { - return new PolynomialMod2(i); - } -}; - -const PolynomialMod2 &PolynomialMod2::Zero() -{ - return Singleton().Ref(); -} - -const PolynomialMod2 &PolynomialMod2::One() -{ - return Singleton >().Ref(); -} - -void PolynomialMod2::Decode(const byte *input, size_t inputLen) -{ - StringStore store(input, inputLen); - Decode(store, inputLen); -} - -void PolynomialMod2::Encode(byte *output, size_t outputLen) const -{ - ArraySink sink(output, outputLen); - Encode(sink, outputLen); -} - -void PolynomialMod2::Decode(BufferedTransformation &bt, size_t inputLen) -{ - reg.CleanNew(BytesToWords(inputLen)); - - for (size_t i=inputLen; i > 0; i--) - { - byte b; - bt.Get(b); - reg[(i-1)/WORD_SIZE] |= word(b) << ((i-1)%WORD_SIZE)*8; - } -} - -void PolynomialMod2::Encode(BufferedTransformation &bt, size_t outputLen) const -{ - for (size_t i=outputLen; i > 0; i--) - bt.Put(GetByte(i-1)); -} - -void PolynomialMod2::DEREncodeAsOctetString(BufferedTransformation &bt, size_t length) const -{ - DERGeneralEncoder enc(bt, OCTET_STRING); - Encode(enc, length); - enc.MessageEnd(); -} - -void PolynomialMod2::BERDecodeAsOctetString(BufferedTransformation &bt, size_t length) -{ - BERGeneralDecoder dec(bt, OCTET_STRING); - if (!dec.IsDefiniteLength() || dec.RemainingLength() != length) - BERDecodeError(); - Decode(dec, length); - dec.MessageEnd(); -} - -unsigned int PolynomialMod2::WordCount() const -{ - return (unsigned int)CountWords(reg, reg.size()); -} - -unsigned int PolynomialMod2::ByteCount() const -{ - unsigned wordCount = WordCount(); - if (wordCount) - return (wordCount-1)*WORD_SIZE + BytePrecision(reg[wordCount-1]); - else - return 0; -} - -unsigned int PolynomialMod2::BitCount() const -{ - unsigned wordCount = WordCount(); - if (wordCount) - return (wordCount-1)*WORD_BITS + BitPrecision(reg[wordCount-1]); - else - return 0; -} - -unsigned int PolynomialMod2::Parity() const -{ - unsigned i; - word temp=0; - for (i=0; i= reg.size()) - { - PolynomialMod2 result((word)0, b.reg.size()*WORD_BITS); - XorWords(result.reg, reg, b.reg, reg.size()); - CopyWords(result.reg+reg.size(), b.reg+reg.size(), b.reg.size()-reg.size()); - return result; - } - else - { - PolynomialMod2 result((word)0, reg.size()*WORD_BITS); - XorWords(result.reg, reg, b.reg, b.reg.size()); - CopyWords(result.reg+b.reg.size(), reg+b.reg.size(), reg.size()-b.reg.size()); - return result; - } -} - -PolynomialMod2 PolynomialMod2::And(const PolynomialMod2 &b) const -{ - PolynomialMod2 result((word)0, WORD_BITS*STDMIN(reg.size(), b.reg.size())); - AndWords(result.reg, reg, b.reg, result.reg.size()); - return result; -} - -PolynomialMod2 PolynomialMod2::Times(const PolynomialMod2 &b) const -{ - PolynomialMod2 result((word)0, BitCount() + b.BitCount()); - - for (int i=b.Degree(); i>=0; i--) - { - result <<= 1; - if (b[i]) - XorWords(result.reg, reg, reg.size()); - } - return result; -} - -PolynomialMod2 PolynomialMod2::Squared() const -{ - static const word map[16] = {0, 1, 4, 5, 16, 17, 20, 21, 64, 65, 68, 69, 80, 81, 84, 85}; - - PolynomialMod2 result((word)0, 2*reg.size()*WORD_BITS); - - for (unsigned i=0; i> (j/2)) % 16] << j; - - for (j=0; j> (j/2 + WORD_BITS/2)) % 16] << j; - } - - return result; -} - -void PolynomialMod2::Divide(PolynomialMod2 &remainder, PolynomialMod2 "ient, - const PolynomialMod2 ÷nd, const PolynomialMod2 &divisor) -{ - if (!divisor) - throw PolynomialMod2::DivideByZero(); - - int degree = divisor.Degree(); - remainder.reg.CleanNew(BitsToWords(degree+1)); - if (dividend.BitCount() >= divisor.BitCount()) - quotient.reg.CleanNew(BitsToWords(dividend.BitCount() - divisor.BitCount() + 1)); - else - quotient.reg.CleanNew(0); - - for (int i=dividend.Degree(); i>=0; i--) - { - remainder <<= 1; - remainder.reg[0] |= dividend[i]; - if (remainder[degree]) - { - remainder -= divisor; - quotient.SetBit(i); - } - } -} - -PolynomialMod2 PolynomialMod2::DividedBy(const PolynomialMod2 &b) const -{ - PolynomialMod2 remainder, quotient; - PolynomialMod2::Divide(remainder, quotient, *this, b); - return quotient; -} - -PolynomialMod2 PolynomialMod2::Modulo(const PolynomialMod2 &b) const -{ - PolynomialMod2 remainder, quotient; - PolynomialMod2::Divide(remainder, quotient, *this, b); - return remainder; -} - -PolynomialMod2& PolynomialMod2::operator<<=(unsigned int n) -{ - if (!reg.size()) - return *this; - - int i; - word u; - word carry=0; - word *r=reg; - - if (n==1) // special case code for most frequent case - { - i = (int)reg.size(); - while (i--) - { - u = *r; - *r = (u << 1) | carry; - carry = u >> (WORD_BITS-1); - r++; - } - - if (carry) - { - reg.Grow(reg.size()+1); - reg[reg.size()-1] = carry; - } - - return *this; - } - - int shiftWords = n / WORD_BITS; - int shiftBits = n % WORD_BITS; - - if (shiftBits) - { - i = (int)reg.size(); - while (i--) - { - u = *r; - *r = (u << shiftBits) | carry; - carry = u >> (WORD_BITS-shiftBits); - r++; - } - } - - if (carry) - { - reg.Grow(reg.size()+shiftWords+1); - reg[reg.size()-1] = carry; - } - else - reg.Grow(reg.size()+shiftWords); - - if (shiftWords) - { - for (i = (int)reg.size()-1; i>=shiftWords; i--) - reg[i] = reg[i-shiftWords]; - for (; i>=0; i--) - reg[i] = 0; - } - - return *this; -} - -PolynomialMod2& PolynomialMod2::operator>>=(unsigned int n) -{ - if (!reg.size()) - return *this; - - int shiftWords = n / WORD_BITS; - int shiftBits = n % WORD_BITS; - - size_t i; - word u; - word carry=0; - word *r=reg+reg.size()-1; - - if (shiftBits) - { - i = reg.size(); - while (i--) - { - u = *r; - *r = (u >> shiftBits) | carry; - carry = u << (WORD_BITS-shiftBits); - r--; - } - } - - if (shiftWords) - { - for (i=0; i>(unsigned int n) const -{ - PolynomialMod2 result(*this); - return result>>=n; -} - -bool PolynomialMod2::operator!() const -{ - for (unsigned i=0; i s(a.BitCount()/bits+1); - unsigned i; - - static const char upper[]="0123456789ABCDEF"; - static const char lower[]="0123456789abcdef"; - const char* vec = (out.flags() & std::ios::uppercase) ? upper : lower; - - for (i=0; i*bits < a.BitCount(); i++) - { - int digit=0; - for (int j=0; j().Gcd(a, b); -} - -PolynomialMod2 PolynomialMod2::InverseMod(const PolynomialMod2 &modulus) const -{ - typedef EuclideanDomainOf Domain; - return QuotientRing(Domain(), modulus).MultiplicativeInverse(*this); -} - -bool PolynomialMod2::IsIrreducible() const -{ - signed int d = Degree(); - if (d <= 0) - return false; - - PolynomialMod2 t(2), u(t); - for (int i=1; i<=d/2; i++) - { - u = u.Squared()%(*this); - if (!Gcd(u+t, *this).IsUnit()) - return false; - } - return true; -} - -// ******************************************************** - -GF2NP::GF2NP(const PolynomialMod2 &modulus) - : QuotientRing >(EuclideanDomainOf(), modulus), m(modulus.Degree()) -{ -} - -GF2NP::Element GF2NP::SquareRoot(const Element &a) const -{ - Element r = a; - for (unsigned int i=1; i t1 && t1 > t2 && t2==0); -} - -const GF2NT::Element& GF2NT::MultiplicativeInverse(const Element &a) const -{ - if (t0-t1 < WORD_BITS) - return GF2NP::MultiplicativeInverse(a); - - SecWordBlock T(m_modulus.reg.size() * 4); - word *b = T; - word *c = T+m_modulus.reg.size(); - word *f = T+2*m_modulus.reg.size(); - word *g = T+3*m_modulus.reg.size(); - size_t bcLen=1, fgLen=m_modulus.reg.size(); - unsigned int k=0; - - SetWords(T, 0, 3*m_modulus.reg.size()); - b[0]=1; - assert(a.reg.size() <= m_modulus.reg.size()); - CopyWords(f, a.reg, a.reg.size()); - CopyWords(g, m_modulus.reg, m_modulus.reg.size()); - - while (1) - { - word t=f[0]; - while (!t) - { - ShiftWordsRightByWords(f, fgLen, 1); - if (c[bcLen-1]) - bcLen++; - assert(bcLen <= m_modulus.reg.size()); - ShiftWordsLeftByWords(c, bcLen, 1); - k+=WORD_BITS; - t=f[0]; - } - - unsigned int i=0; - while (t%2 == 0) - { - t>>=1; - i++; - } - k+=i; - - if (t==1 && CountWords(f, fgLen)==1) - break; - - if (i==1) - { - ShiftWordsRightByBits(f, fgLen, 1); - t=ShiftWordsLeftByBits(c, bcLen, 1); - } - else - { - ShiftWordsRightByBits(f, fgLen, i); - t=ShiftWordsLeftByBits(c, bcLen, i); - } - if (t) - { - c[bcLen] = t; - bcLen++; - assert(bcLen <= m_modulus.reg.size()); - } - - if (f[fgLen-1]==0 && g[fgLen-1]==0) - fgLen--; - - if (f[fgLen-1] < g[fgLen-1]) - { - std::swap(f, g); - std::swap(b, c); - } - - XorWords(f, g, fgLen); - XorWords(b, c, bcLen); - } - - while (k >= WORD_BITS) - { - word temp = b[0]; - // right shift b - for (unsigned i=0; i+1> j) & 1) << (t1 + j); - else - b[t1/WORD_BITS-1] ^= temp << t1%WORD_BITS; - - if (t1 % WORD_BITS) - b[t1/WORD_BITS] ^= temp >> (WORD_BITS - t1%WORD_BITS); - - if (t0%WORD_BITS) - { - b[t0/WORD_BITS-1] ^= temp << t0%WORD_BITS; - b[t0/WORD_BITS] ^= temp >> (WORD_BITS - t0%WORD_BITS); - } - else - b[t0/WORD_BITS-1] ^= temp; - - k -= WORD_BITS; - } - - if (k) - { - word temp = b[0] << (WORD_BITS - k); - ShiftWordsRightByBits(b, BitsToWords(m), k); - - if (t1 < WORD_BITS) - for (unsigned int j=0; j> j) & 1) << (t1 + j); - else - b[t1/WORD_BITS-1] ^= temp << t1%WORD_BITS; - - if (t1 % WORD_BITS) - b[t1/WORD_BITS] ^= temp >> (WORD_BITS - t1%WORD_BITS); - - if (t0%WORD_BITS) - { - b[t0/WORD_BITS-1] ^= temp << t0%WORD_BITS; - b[t0/WORD_BITS] ^= temp >> (WORD_BITS - t0%WORD_BITS); - } - else - b[t0/WORD_BITS-1] ^= temp; - } - - CopyWords(result.reg.begin(), b, result.reg.size()); - return result; -} - -const GF2NT::Element& GF2NT::Multiply(const Element &a, const Element &b) const -{ - size_t aSize = STDMIN(a.reg.size(), result.reg.size()); - Element r((word)0, m); - - for (int i=m-1; i>=0; i--) - { - if (r[m-1]) - { - ShiftWordsLeftByBits(r.reg.begin(), r.reg.size(), 1); - XorWords(r.reg.begin(), m_modulus.reg, r.reg.size()); - } - else - ShiftWordsLeftByBits(r.reg.begin(), r.reg.size(), 1); - - if (b[i]) - XorWords(r.reg.begin(), a.reg, aSize); - } - - if (m%WORD_BITS) - r.reg.begin()[r.reg.size()-1] = (word)Crop(r.reg[r.reg.size()-1], m%WORD_BITS); - - CopyWords(result.reg.begin(), r.reg.begin(), result.reg.size()); - return result; -} - -const GF2NT::Element& GF2NT::Reduced(const Element &a) const -{ - if (t0-t1 < WORD_BITS) - return m_domain.Mod(a, m_modulus); - - SecWordBlock b(a.reg); - - size_t i; - for (i=b.size()-1; i>=BitsToWords(t0); i--) - { - word temp = b[i]; - - if (t0%WORD_BITS) - { - b[i-t0/WORD_BITS] ^= temp >> t0%WORD_BITS; - b[i-t0/WORD_BITS-1] ^= temp << (WORD_BITS - t0%WORD_BITS); - } - else - b[i-t0/WORD_BITS] ^= temp; - - if ((t0-t1)%WORD_BITS) - { - b[i-(t0-t1)/WORD_BITS] ^= temp >> (t0-t1)%WORD_BITS; - b[i-(t0-t1)/WORD_BITS-1] ^= temp << (WORD_BITS - (t0-t1)%WORD_BITS); - } - else - b[i-(t0-t1)/WORD_BITS] ^= temp; - } - - if (i==BitsToWords(t0)-1 && t0%WORD_BITS) - { - word mask = ((word)1<<(t0%WORD_BITS))-1; - word temp = b[i] & ~mask; - b[i] &= mask; - - b[i-t0/WORD_BITS] ^= temp >> t0%WORD_BITS; - - if ((t0-t1)%WORD_BITS) - { - b[i-(t0-t1)/WORD_BITS] ^= temp >> (t0-t1)%WORD_BITS; - if ((t0-t1)%WORD_BITS > t0%WORD_BITS) - b[i-(t0-t1)/WORD_BITS-1] ^= temp << (WORD_BITS - (t0-t1)%WORD_BITS); - else - assert(temp << (WORD_BITS - (t0-t1)%WORD_BITS) == 0); - } - else - b[i-(t0-t1)/WORD_BITS] ^= temp; - } - - SetWords(result.reg.begin(), 0, result.reg.size()); - CopyWords(result.reg.begin(), b, STDMIN(b.size(), result.reg.size())); - return result; -} - -void GF2NP::DEREncodeElement(BufferedTransformation &out, const Element &a) const -{ - a.DEREncodeAsOctetString(out, MaxElementByteLength()); -} - -void GF2NP::BERDecodeElement(BufferedTransformation &in, Element &a) const -{ - a.BERDecodeAsOctetString(in, MaxElementByteLength()); -} - -void GF2NT::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - ASN1::characteristic_two_field().DEREncode(seq); - DERSequenceEncoder parameters(seq); - DEREncodeUnsigned(parameters, m); - ASN1::tpBasis().DEREncode(parameters); - DEREncodeUnsigned(parameters, t1); - parameters.MessageEnd(); - seq.MessageEnd(); -} - -void GF2NPP::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - ASN1::characteristic_two_field().DEREncode(seq); - DERSequenceEncoder parameters(seq); - DEREncodeUnsigned(parameters, m); - ASN1::ppBasis().DEREncode(parameters); - DERSequenceEncoder pentanomial(parameters); - DEREncodeUnsigned(pentanomial, t3); - DEREncodeUnsigned(pentanomial, t2); - DEREncodeUnsigned(pentanomial, t1); - pentanomial.MessageEnd(); - parameters.MessageEnd(); - seq.MessageEnd(); -} - -GF2NP * BERDecodeGF2NP(BufferedTransformation &bt) -{ - // VC60 workaround: auto_ptr lacks reset() - member_ptr result; - - BERSequenceDecoder seq(bt); - if (OID(seq) != ASN1::characteristic_two_field()) - BERDecodeError(); - BERSequenceDecoder parameters(seq); - unsigned int m; - BERDecodeUnsigned(parameters, m); - OID oid(parameters); - if (oid == ASN1::tpBasis()) - { - unsigned int t1; - BERDecodeUnsigned(parameters, t1); - result.reset(new GF2NT(m, t1, 0)); - } - else if (oid == ASN1::ppBasis()) - { - unsigned int t1, t2, t3; - BERSequenceDecoder pentanomial(parameters); - BERDecodeUnsigned(pentanomial, t3); - BERDecodeUnsigned(pentanomial, t2); - BERDecodeUnsigned(pentanomial, t1); - pentanomial.MessageEnd(); - result.reset(new GF2NPP(m, t3, t2, t1, 0)); - } - else - { - BERDecodeError(); - return NULL; - } - parameters.MessageEnd(); - seq.MessageEnd(); - - return result.release(); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/gf2n.h b/CryptoPP/gf2n.h deleted file mode 100644 index 67ade641e..000000000 --- a/CryptoPP/gf2n.h +++ /dev/null @@ -1,369 +0,0 @@ -#ifndef CRYPTOPP_GF2N_H -#define CRYPTOPP_GF2N_H - -/*! \file */ - -#include "cryptlib.h" -#include "secblock.h" -#include "misc.h" -#include "algebra.h" - -#include - -NAMESPACE_BEGIN(CryptoPP) - -//! Polynomial with Coefficients in GF(2) -/*! \nosubgrouping */ -class CRYPTOPP_DLL PolynomialMod2 -{ -public: - //! \name ENUMS, EXCEPTIONS, and TYPEDEFS - //@{ - //! divide by zero exception - class DivideByZero : public Exception - { - public: - DivideByZero() : Exception(OTHER_ERROR, "PolynomialMod2: division by zero") {} - }; - - typedef unsigned int RandomizationParameter; - //@} - - //! \name CREATORS - //@{ - //! creates the zero polynomial - PolynomialMod2(); - //! copy constructor - PolynomialMod2(const PolynomialMod2& t); - - //! convert from word - /*! value should be encoded with the least significant bit as coefficient to x^0 - and most significant bit as coefficient to x^(WORD_BITS-1) - bitLength denotes how much memory to allocate initially - */ - PolynomialMod2(word value, size_t bitLength=WORD_BITS); - - //! convert from big-endian byte array - PolynomialMod2(const byte *encodedPoly, size_t byteCount) - {Decode(encodedPoly, byteCount);} - - //! convert from big-endian form stored in a BufferedTransformation - PolynomialMod2(BufferedTransformation &encodedPoly, size_t byteCount) - {Decode(encodedPoly, byteCount);} - - //! create a random polynomial uniformly distributed over all polynomials with degree less than bitcount - PolynomialMod2(RandomNumberGenerator &rng, size_t bitcount) - {Randomize(rng, bitcount);} - - //! return x^i - static PolynomialMod2 CRYPTOPP_API Monomial(size_t i); - //! return x^t0 + x^t1 + x^t2 - static PolynomialMod2 CRYPTOPP_API Trinomial(size_t t0, size_t t1, size_t t2); - //! return x^t0 + x^t1 + x^t2 + x^t3 + x^t4 - static PolynomialMod2 CRYPTOPP_API Pentanomial(size_t t0, size_t t1, size_t t2, size_t t3, size_t t4); - //! return x^(n-1) + ... + x + 1 - static PolynomialMod2 CRYPTOPP_API AllOnes(size_t n); - - //! - static const PolynomialMod2 & CRYPTOPP_API Zero(); - //! - static const PolynomialMod2 & CRYPTOPP_API One(); - //@} - - //! \name ENCODE/DECODE - //@{ - //! minimum number of bytes to encode this polynomial - /*! MinEncodedSize of 0 is 1 */ - unsigned int MinEncodedSize() const {return STDMAX(1U, ByteCount());} - - //! encode in big-endian format - /*! if outputLen < MinEncodedSize, the most significant bytes will be dropped - if outputLen > MinEncodedSize, the most significant bytes will be padded - */ - void Encode(byte *output, size_t outputLen) const; - //! - void Encode(BufferedTransformation &bt, size_t outputLen) const; - - //! - void Decode(const byte *input, size_t inputLen); - //! - //* Precondition: bt.MaxRetrievable() >= inputLen - void Decode(BufferedTransformation &bt, size_t inputLen); - - //! encode value as big-endian octet string - void DEREncodeAsOctetString(BufferedTransformation &bt, size_t length) const; - //! decode value as big-endian octet string - void BERDecodeAsOctetString(BufferedTransformation &bt, size_t length); - //@} - - //! \name ACCESSORS - //@{ - //! number of significant bits = Degree() + 1 - unsigned int BitCount() const; - //! number of significant bytes = ceiling(BitCount()/8) - unsigned int ByteCount() const; - //! number of significant words = ceiling(ByteCount()/sizeof(word)) - unsigned int WordCount() const; - - //! return the n-th bit, n=0 being the least significant bit - bool GetBit(size_t n) const {return GetCoefficient(n)!=0;} - //! return the n-th byte - byte GetByte(size_t n) const; - - //! the zero polynomial will return a degree of -1 - signed int Degree() const {return BitCount()-1;} - //! degree + 1 - unsigned int CoefficientCount() const {return BitCount();} - //! return coefficient for x^i - int GetCoefficient(size_t i) const - {return (i/WORD_BITS < reg.size()) ? int(reg[i/WORD_BITS] >> (i % WORD_BITS)) & 1 : 0;} - //! return coefficient for x^i - int operator[](unsigned int i) const {return GetCoefficient(i);} - - //! - bool IsZero() const {return !*this;} - //! - bool Equals(const PolynomialMod2 &rhs) const; - //@} - - //! \name MANIPULATORS - //@{ - //! - PolynomialMod2& operator=(const PolynomialMod2& t); - //! - PolynomialMod2& operator&=(const PolynomialMod2& t); - //! - PolynomialMod2& operator^=(const PolynomialMod2& t); - //! - PolynomialMod2& operator+=(const PolynomialMod2& t) {return *this ^= t;} - //! - PolynomialMod2& operator-=(const PolynomialMod2& t) {return *this ^= t;} - //! - PolynomialMod2& operator*=(const PolynomialMod2& t); - //! - PolynomialMod2& operator/=(const PolynomialMod2& t); - //! - PolynomialMod2& operator%=(const PolynomialMod2& t); - //! - PolynomialMod2& operator<<=(unsigned int); - //! - PolynomialMod2& operator>>=(unsigned int); - - //! - void Randomize(RandomNumberGenerator &rng, size_t bitcount); - - //! - void SetBit(size_t i, int value = 1); - //! set the n-th byte to value - void SetByte(size_t n, byte value); - - //! - void SetCoefficient(size_t i, int value) {SetBit(i, value);} - - //! - void swap(PolynomialMod2 &a) {reg.swap(a.reg);} - //@} - - //! \name UNARY OPERATORS - //@{ - //! - bool operator!() const; - //! - PolynomialMod2 operator+() const {return *this;} - //! - PolynomialMod2 operator-() const {return *this;} - //@} - - //! \name BINARY OPERATORS - //@{ - //! - PolynomialMod2 And(const PolynomialMod2 &b) const; - //! - PolynomialMod2 Xor(const PolynomialMod2 &b) const; - //! - PolynomialMod2 Plus(const PolynomialMod2 &b) const {return Xor(b);} - //! - PolynomialMod2 Minus(const PolynomialMod2 &b) const {return Xor(b);} - //! - PolynomialMod2 Times(const PolynomialMod2 &b) const; - //! - PolynomialMod2 DividedBy(const PolynomialMod2 &b) const; - //! - PolynomialMod2 Modulo(const PolynomialMod2 &b) const; - - //! - PolynomialMod2 operator>>(unsigned int n) const; - //! - PolynomialMod2 operator<<(unsigned int n) const; - //@} - - //! \name OTHER ARITHMETIC FUNCTIONS - //@{ - //! sum modulo 2 of all coefficients - unsigned int Parity() const; - - //! check for irreducibility - bool IsIrreducible() const; - - //! is always zero since we're working modulo 2 - PolynomialMod2 Doubled() const {return Zero();} - //! - PolynomialMod2 Squared() const; - - //! only 1 is a unit - bool IsUnit() const {return Equals(One());} - //! return inverse if *this is a unit, otherwise return 0 - PolynomialMod2 MultiplicativeInverse() const {return IsUnit() ? One() : Zero();} - - //! greatest common divisor - static PolynomialMod2 CRYPTOPP_API Gcd(const PolynomialMod2 &a, const PolynomialMod2 &n); - //! calculate multiplicative inverse of *this mod n - PolynomialMod2 InverseMod(const PolynomialMod2 &) const; - - //! calculate r and q such that (a == d*q + r) && (deg(r) < deg(d)) - static void CRYPTOPP_API Divide(PolynomialMod2 &r, PolynomialMod2 &q, const PolynomialMod2 &a, const PolynomialMod2 &d); - //@} - - //! \name INPUT/OUTPUT - //@{ - //! - friend std::ostream& operator<<(std::ostream& out, const PolynomialMod2 &a); - //@} - -private: - friend class GF2NT; - - SecWordBlock reg; -}; - -//! -inline bool operator==(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) -{return a.Equals(b);} -//! -inline bool operator!=(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) -{return !(a==b);} -//! compares degree -inline bool operator> (const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) -{return a.Degree() > b.Degree();} -//! compares degree -inline bool operator>=(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) -{return a.Degree() >= b.Degree();} -//! compares degree -inline bool operator< (const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) -{return a.Degree() < b.Degree();} -//! compares degree -inline bool operator<=(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) -{return a.Degree() <= b.Degree();} -//! -inline CryptoPP::PolynomialMod2 operator&(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) {return a.And(b);} -//! -inline CryptoPP::PolynomialMod2 operator^(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) {return a.Xor(b);} -//! -inline CryptoPP::PolynomialMod2 operator+(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) {return a.Plus(b);} -//! -inline CryptoPP::PolynomialMod2 operator-(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) {return a.Minus(b);} -//! -inline CryptoPP::PolynomialMod2 operator*(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) {return a.Times(b);} -//! -inline CryptoPP::PolynomialMod2 operator/(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) {return a.DividedBy(b);} -//! -inline CryptoPP::PolynomialMod2 operator%(const CryptoPP::PolynomialMod2 &a, const CryptoPP::PolynomialMod2 &b) {return a.Modulo(b);} - -// CodeWarrior 8 workaround: put these template instantiations after overloaded operator declarations, -// but before the use of QuotientRing > for VC .NET 2003 -CRYPTOPP_DLL_TEMPLATE_CLASS AbstractGroup; -CRYPTOPP_DLL_TEMPLATE_CLASS AbstractRing; -CRYPTOPP_DLL_TEMPLATE_CLASS AbstractEuclideanDomain; -CRYPTOPP_DLL_TEMPLATE_CLASS EuclideanDomainOf; -CRYPTOPP_DLL_TEMPLATE_CLASS QuotientRing >; - -//! GF(2^n) with Polynomial Basis -class CRYPTOPP_DLL GF2NP : public QuotientRing > -{ -public: - GF2NP(const PolynomialMod2 &modulus); - - virtual GF2NP * Clone() const {return new GF2NP(*this);} - virtual void DEREncode(BufferedTransformation &bt) const - {assert(false);} // no ASN.1 syntax yet for general polynomial basis - - void DEREncodeElement(BufferedTransformation &out, const Element &a) const; - void BERDecodeElement(BufferedTransformation &in, Element &a) const; - - bool Equal(const Element &a, const Element &b) const - {assert(a.Degree() < m_modulus.Degree() && b.Degree() < m_modulus.Degree()); return a.Equals(b);} - - bool IsUnit(const Element &a) const - {assert(a.Degree() < m_modulus.Degree()); return !!a;} - - unsigned int MaxElementBitLength() const - {return m;} - - unsigned int MaxElementByteLength() const - {return (unsigned int)BitsToBytes(MaxElementBitLength());} - - Element SquareRoot(const Element &a) const; - - Element HalfTrace(const Element &a) const; - - // returns z such that z^2 + z == a - Element SolveQuadraticEquation(const Element &a) const; - -protected: - unsigned int m; -}; - -//! GF(2^n) with Trinomial Basis -class CRYPTOPP_DLL GF2NT : public GF2NP -{ -public: - // polynomial modulus = x^t0 + x^t1 + x^t2, t0 > t1 > t2 - GF2NT(unsigned int t0, unsigned int t1, unsigned int t2); - - GF2NP * Clone() const {return new GF2NT(*this);} - void DEREncode(BufferedTransformation &bt) const; - - const Element& Multiply(const Element &a, const Element &b) const; - - const Element& Square(const Element &a) const - {return Reduced(a.Squared());} - - const Element& MultiplicativeInverse(const Element &a) const; - -private: - const Element& Reduced(const Element &a) const; - - unsigned int t0, t1; - mutable PolynomialMod2 result; -}; - -//! GF(2^n) with Pentanomial Basis -class CRYPTOPP_DLL GF2NPP : public GF2NP -{ -public: - // polynomial modulus = x^t0 + x^t1 + x^t2 + x^t3 + x^t4, t0 > t1 > t2 > t3 > t4 - GF2NPP(unsigned int t0, unsigned int t1, unsigned int t2, unsigned int t3, unsigned int t4) - : GF2NP(PolynomialMod2::Pentanomial(t0, t1, t2, t3, t4)), t0(t0), t1(t1), t2(t2), t3(t3) {} - - GF2NP * Clone() const {return new GF2NPP(*this);} - void DEREncode(BufferedTransformation &bt) const; - -private: - unsigned int t0, t1, t2, t3; -}; - -// construct new GF2NP from the ASN.1 sequence Characteristic-two -CRYPTOPP_DLL GF2NP * CRYPTOPP_API BERDecodeGF2NP(BufferedTransformation &bt); - -NAMESPACE_END - -#ifndef __BORLANDC__ -NAMESPACE_BEGIN(std) -template<> inline void swap(CryptoPP::PolynomialMod2 &a, CryptoPP::PolynomialMod2 &b) -{ - a.swap(b); -} -NAMESPACE_END -#endif - -#endif diff --git a/CryptoPP/gfpcrypt.cpp b/CryptoPP/gfpcrypt.cpp deleted file mode 100644 index e293fc598..000000000 --- a/CryptoPP/gfpcrypt.cpp +++ /dev/null @@ -1,273 +0,0 @@ -// dsa.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "gfpcrypt.h" -#include "asn.h" -#include "oids.h" -#include "nbtheory.h" - -NAMESPACE_BEGIN(CryptoPP) - -void TestInstantiations_gfpcrypt() -{ - GDSA::Signer test; - GDSA::Verifier test1; - DSA::Signer test5(NullRNG(), 100); - DSA::Signer test2(test5); - NR::Signer test3; - NR::Verifier test4; - DLIES<>::Encryptor test6; - DLIES<>::Decryptor test7; -} - -void DL_GroupParameters_DSA::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg) -{ - Integer p, q, g; - - if (alg.GetValue("Modulus", p) && alg.GetValue("SubgroupGenerator", g)) - { - q = alg.GetValueWithDefault("SubgroupOrder", ComputeGroupOrder(p)/2); - Initialize(p, q, g); - } - else - { - int modulusSize = 1024, defaultSubgroupOrderSize; - alg.GetIntValue("ModulusSize", modulusSize) || alg.GetIntValue("KeySize", modulusSize); - - switch (modulusSize) - { - case 1024: - defaultSubgroupOrderSize = 160; - break; - case 2048: - defaultSubgroupOrderSize = 224; - break; - case 3072: - defaultSubgroupOrderSize = 256; - break; - default: - throw InvalidArgument("DSA: not a valid prime length"); - } - - DL_GroupParameters_GFP::GenerateRandom(rng, CombinedNameValuePairs(alg, MakeParameters(Name::SubgroupOrderSize(), defaultSubgroupOrderSize, false))); - } -} - -bool DL_GroupParameters_DSA::ValidateGroup(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = DL_GroupParameters_GFP::ValidateGroup(rng, level); - int pSize = GetModulus().BitCount(), qSize = GetSubgroupOrder().BitCount(); - pass = pass && ((pSize==1024 && qSize==160) || (pSize==2048 && qSize==224) || (pSize==2048 && qSize==256) || (pSize==3072 && qSize==256)); - return pass; -} - -void DL_SignatureMessageEncodingMethod_DSA::ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const -{ - assert(recoverableMessageLength == 0); - assert(hashIdentifier.second == 0); - const size_t representativeByteLength = BitsToBytes(representativeBitLength); - const size_t digestSize = hash.DigestSize(); - const size_t paddingLength = SaturatingSubtract(representativeByteLength, digestSize); - - memset(representative, 0, paddingLength); - hash.TruncatedFinal(representative+paddingLength, STDMIN(representativeByteLength, digestSize)); - - if (digestSize*8 > representativeBitLength) - { - Integer h(representative, representativeByteLength); - h >>= representativeByteLength*8 - representativeBitLength; - h.Encode(representative, representativeByteLength); - } -} - -void DL_SignatureMessageEncodingMethod_NR::ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const -{ - assert(recoverableMessageLength == 0); - assert(hashIdentifier.second == 0); - const size_t representativeByteLength = BitsToBytes(representativeBitLength); - const size_t digestSize = hash.DigestSize(); - const size_t paddingLength = SaturatingSubtract(representativeByteLength, digestSize); - - memset(representative, 0, paddingLength); - hash.TruncatedFinal(representative+paddingLength, STDMIN(representativeByteLength, digestSize)); - - if (digestSize*8 >= representativeBitLength) - { - Integer h(representative, representativeByteLength); - h >>= representativeByteLength*8 - representativeBitLength + 1; - h.Encode(representative, representativeByteLength); - } -} - -bool DL_GroupParameters_IntegerBased::ValidateGroup(RandomNumberGenerator &rng, unsigned int level) const -{ - const Integer &p = GetModulus(), &q = GetSubgroupOrder(); - - bool pass = true; - pass = pass && p > Integer::One() && p.IsOdd(); - pass = pass && q > Integer::One() && q.IsOdd(); - - if (level >= 1) - pass = pass && GetCofactor() > Integer::One() && GetGroupOrder() % q == Integer::Zero(); - if (level >= 2) - pass = pass && VerifyPrime(rng, q, level-2) && VerifyPrime(rng, p, level-2); - - return pass; -} - -bool DL_GroupParameters_IntegerBased::ValidateElement(unsigned int level, const Integer &g, const DL_FixedBasePrecomputation *gpc) const -{ - const Integer &p = GetModulus(), &q = GetSubgroupOrder(); - - bool pass = true; - pass = pass && GetFieldType() == 1 ? g.IsPositive() : g.NotNegative(); - pass = pass && g < p && !IsIdentity(g); - - if (level >= 1) - { - if (gpc) - pass = pass && gpc->Exponentiate(GetGroupPrecomputation(), Integer::One()) == g; - } - if (level >= 2) - { - if (GetFieldType() == 2) - pass = pass && Jacobi(g*g-4, p)==-1; - - // verifying that Lucas((p+1)/2, w, p)==2 is omitted because it's too costly - // and at most 1 bit is leaked if it's false - bool fullValidate = (GetFieldType() == 2 && level >= 3) || !FastSubgroupCheckAvailable(); - - if (fullValidate && pass) - { - Integer gp = gpc ? gpc->Exponentiate(GetGroupPrecomputation(), q) : ExponentiateElement(g, q); - pass = pass && IsIdentity(gp); - } - else if (GetFieldType() == 1) - pass = pass && Jacobi(g, p) == 1; - } - - return pass; -} - -void DL_GroupParameters_IntegerBased::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg) -{ - Integer p, q, g; - - if (alg.GetValue("Modulus", p) && alg.GetValue("SubgroupGenerator", g)) - { - q = alg.GetValueWithDefault("SubgroupOrder", ComputeGroupOrder(p)/2); - } - else - { - int modulusSize, subgroupOrderSize; - - if (!alg.GetIntValue("ModulusSize", modulusSize)) - modulusSize = alg.GetIntValueWithDefault("KeySize", 2048); - - if (!alg.GetIntValue("SubgroupOrderSize", subgroupOrderSize)) - subgroupOrderSize = GetDefaultSubgroupOrderSize(modulusSize); - - PrimeAndGenerator pg; - pg.Generate(GetFieldType() == 1 ? 1 : -1, rng, modulusSize, subgroupOrderSize); - p = pg.Prime(); - q = pg.SubPrime(); - g = pg.Generator(); - } - - Initialize(p, q, g); -} - -Integer DL_GroupParameters_IntegerBased::DecodeElement(const byte *encoded, bool checkForGroupMembership) const -{ - Integer g(encoded, GetModulus().ByteCount()); - if (!ValidateElement(1, g, NULL)) - throw DL_BadElement(); - return g; -} - -void DL_GroupParameters_IntegerBased::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder parameters(bt); - Integer p(parameters); - Integer q(parameters); - Integer g; - if (parameters.EndReached()) - { - g = q; - q = ComputeGroupOrder(p) / 2; - } - else - g.BERDecode(parameters); - parameters.MessageEnd(); - - SetModulusAndSubgroupGenerator(p, g); - SetSubgroupOrder(q); -} - -void DL_GroupParameters_IntegerBased::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder parameters(bt); - GetModulus().DEREncode(parameters); - m_q.DEREncode(parameters); - GetSubgroupGenerator().DEREncode(parameters); - parameters.MessageEnd(); -} - -bool DL_GroupParameters_IntegerBased::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper >(this, name, valueType, pValue) - CRYPTOPP_GET_FUNCTION_ENTRY(Modulus); -} - -void DL_GroupParameters_IntegerBased::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY2(Modulus, SubgroupGenerator) - CRYPTOPP_SET_FUNCTION_ENTRY(SubgroupOrder) - ; -} - -OID DL_GroupParameters_IntegerBased::GetAlgorithmID() const -{ - return ASN1::id_dsa(); -} - -void DL_GroupParameters_GFP::SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const -{ - ModularArithmetic ma(GetModulus()); - ma.SimultaneousExponentiate(results, base, exponents, exponentsCount); -} - -DL_GroupParameters_GFP::Element DL_GroupParameters_GFP::MultiplyElements(const Element &a, const Element &b) const -{ - return a_times_b_mod_c(a, b, GetModulus()); -} - -DL_GroupParameters_GFP::Element DL_GroupParameters_GFP::CascadeExponentiate(const Element &element1, const Integer &exponent1, const Element &element2, const Integer &exponent2) const -{ - ModularArithmetic ma(GetModulus()); - return ma.CascadeExponentiate(element1, exponent1, element2, exponent2); -} - -Integer DL_GroupParameters_IntegerBased::GetMaxExponent() const -{ - return STDMIN(GetSubgroupOrder()-1, Integer::Power2(2*DiscreteLogWorkFactor(GetFieldType()*GetModulus().BitCount()))); -} - -unsigned int DL_GroupParameters_IntegerBased::GetDefaultSubgroupOrderSize(unsigned int modulusSize) const -{ - return 2*DiscreteLogWorkFactor(GetFieldType()*modulusSize); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/gfpcrypt.h b/CryptoPP/gfpcrypt.h deleted file mode 100644 index 7af993fb3..000000000 --- a/CryptoPP/gfpcrypt.h +++ /dev/null @@ -1,528 +0,0 @@ -#ifndef CRYPTOPP_GFPCRYPT_H -#define CRYPTOPP_GFPCRYPT_H - -/** \file - Implementation of schemes based on DL over GF(p) -*/ - -#include "pubkey.h" -#include "modexppc.h" -#include "sha.h" -#include "algparam.h" -#include "asn.h" -#include "smartptr.h" -#include "hmac.h" - -#include - -NAMESPACE_BEGIN(CryptoPP) - -CRYPTOPP_DLL_TEMPLATE_CLASS DL_GroupParameters; - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE DL_GroupParameters_IntegerBased : public ASN1CryptoMaterial > -{ - typedef DL_GroupParameters_IntegerBased ThisClass; - -public: - void Initialize(const DL_GroupParameters_IntegerBased ¶ms) - {Initialize(params.GetModulus(), params.GetSubgroupOrder(), params.GetSubgroupGenerator());} - void Initialize(RandomNumberGenerator &rng, unsigned int pbits) - {GenerateRandom(rng, MakeParameters("ModulusSize", (int)pbits));} - void Initialize(const Integer &p, const Integer &g) - {SetModulusAndSubgroupGenerator(p, g); SetSubgroupOrder(ComputeGroupOrder(p)/2);} - void Initialize(const Integer &p, const Integer &q, const Integer &g) - {SetModulusAndSubgroupGenerator(p, g); SetSubgroupOrder(q);} - - // ASN1Object interface - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - // GeneratibleCryptoMaterial interface - /*! parameters: (ModulusSize, SubgroupOrderSize (optional)) */ - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg); - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - - // DL_GroupParameters - const Integer & GetSubgroupOrder() const {return m_q;} - Integer GetGroupOrder() const {return GetFieldType() == 1 ? GetModulus()-Integer::One() : GetModulus()+Integer::One();} - bool ValidateGroup(RandomNumberGenerator &rng, unsigned int level) const; - bool ValidateElement(unsigned int level, const Integer &element, const DL_FixedBasePrecomputation *precomp) const; - bool FastSubgroupCheckAvailable() const {return GetCofactor() == 2;} - void EncodeElement(bool reversible, const Element &element, byte *encoded) const - {element.Encode(encoded, GetModulus().ByteCount());} - unsigned int GetEncodedElementSize(bool reversible) const {return GetModulus().ByteCount();} - Integer DecodeElement(const byte *encoded, bool checkForGroupMembership) const; - Integer ConvertElementToInteger(const Element &element) const - {return element;} - Integer GetMaxExponent() const; - static std::string CRYPTOPP_API StaticAlgorithmNamePrefix() {return "";} - - OID GetAlgorithmID() const; - - virtual const Integer & GetModulus() const =0; - virtual void SetModulusAndSubgroupGenerator(const Integer &p, const Integer &g) =0; - - void SetSubgroupOrder(const Integer &q) - {m_q = q; ParametersChanged();} - -protected: - Integer ComputeGroupOrder(const Integer &modulus) const - {return modulus-(GetFieldType() == 1 ? 1 : -1);} - - // GF(p) = 1, GF(p^2) = 2 - virtual int GetFieldType() const =0; - virtual unsigned int GetDefaultSubgroupOrderSize(unsigned int modulusSize) const; - -private: - Integer m_q; -}; - -//! _ -template > -class CRYPTOPP_NO_VTABLE DL_GroupParameters_IntegerBasedImpl : public DL_GroupParametersImpl -{ - typedef DL_GroupParameters_IntegerBasedImpl ThisClass; - -public: - typedef typename GROUP_PRECOMP::Element Element; - - // GeneratibleCryptoMaterial interface - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const - {return GetValueHelper(this, name, valueType, pValue).Assignable();} - - void AssignFrom(const NameValuePairs &source) - {AssignFromHelper(this, source);} - - // DL_GroupParameters - const DL_FixedBasePrecomputation & GetBasePrecomputation() const {return this->m_gpc;} - DL_FixedBasePrecomputation & AccessBasePrecomputation() {return this->m_gpc;} - - // IntegerGroupParameters - const Integer & GetModulus() const {return this->m_groupPrecomputation.GetModulus();} - const Integer & GetGenerator() const {return this->m_gpc.GetBase(this->GetGroupPrecomputation());} - - void SetModulusAndSubgroupGenerator(const Integer &p, const Integer &g) // these have to be set together - {this->m_groupPrecomputation.SetModulus(p); this->m_gpc.SetBase(this->GetGroupPrecomputation(), g); this->ParametersChanged();} - - // non-inherited - bool operator==(const DL_GroupParameters_IntegerBasedImpl &rhs) const - {return GetModulus() == rhs.GetModulus() && GetGenerator() == rhs.GetGenerator() && this->GetSubgroupOrder() == rhs.GetSubgroupOrder();} - bool operator!=(const DL_GroupParameters_IntegerBasedImpl &rhs) const - {return !operator==(rhs);} -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS DL_GroupParameters_IntegerBasedImpl; - -//! GF(p) group parameters -class CRYPTOPP_DLL DL_GroupParameters_GFP : public DL_GroupParameters_IntegerBasedImpl -{ -public: - // DL_GroupParameters - bool IsIdentity(const Integer &element) const {return element == Integer::One();} - void SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const; - - // NameValuePairs interface - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const - { - return GetValueHelper(this, name, valueType, pValue).Assignable(); - } - - // used by MQV - Element MultiplyElements(const Element &a, const Element &b) const; - Element CascadeExponentiate(const Element &element1, const Integer &exponent1, const Element &element2, const Integer &exponent2) const; - -protected: - int GetFieldType() const {return 1;} -}; - -//! GF(p) group parameters that default to same primes -class CRYPTOPP_DLL DL_GroupParameters_GFP_DefaultSafePrime : public DL_GroupParameters_GFP -{ -public: - typedef NoCofactorMultiplication DefaultCofactorOption; - -protected: - unsigned int GetDefaultSubgroupOrderSize(unsigned int modulusSize) const {return modulusSize-1;} -}; - -//! GDSA algorithm -template -class DL_Algorithm_GDSA : public DL_ElgamalLikeSignatureAlgorithm -{ -public: - static const char * CRYPTOPP_API StaticAlgorithmName() {return "DSA-1363";} - - void Sign(const DL_GroupParameters ¶ms, const Integer &x, const Integer &k, const Integer &e, Integer &r, Integer &s) const - { - const Integer &q = params.GetSubgroupOrder(); - r %= q; - Integer kInv = k.InverseMod(q); - s = (kInv * (x*r + e)) % q; - assert(!!r && !!s); - } - - bool Verify(const DL_GroupParameters ¶ms, const DL_PublicKey &publicKey, const Integer &e, const Integer &r, const Integer &s) const - { - const Integer &q = params.GetSubgroupOrder(); - if (r>=q || r<1 || s>=q || s<1) - return false; - - Integer w = s.InverseMod(q); - Integer u1 = (e * w) % q; - Integer u2 = (r * w) % q; - // verify r == (g^u1 * y^u2 mod p) mod q - return r == params.ConvertElementToInteger(publicKey.CascadeExponentiateBaseAndPublicElement(u1, u2)) % q; - } -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS DL_Algorithm_GDSA; - -//! NR algorithm -template -class DL_Algorithm_NR : public DL_ElgamalLikeSignatureAlgorithm -{ -public: - static const char * CRYPTOPP_API StaticAlgorithmName() {return "NR";} - - void Sign(const DL_GroupParameters ¶ms, const Integer &x, const Integer &k, const Integer &e, Integer &r, Integer &s) const - { - const Integer &q = params.GetSubgroupOrder(); - r = (r + e) % q; - s = (k - x*r) % q; - assert(!!r); - } - - bool Verify(const DL_GroupParameters ¶ms, const DL_PublicKey &publicKey, const Integer &e, const Integer &r, const Integer &s) const - { - const Integer &q = params.GetSubgroupOrder(); - if (r>=q || r<1 || s>=q) - return false; - - // check r == (m_g^s * m_y^r + m) mod m_q - return r == (params.ConvertElementToInteger(publicKey.CascadeExponentiateBaseAndPublicElement(s, r)) + e) % q; - } -}; - -/*! DSA public key format is defined in 7.3.3 of RFC 2459. The - private key format is defined in 12.9 of PKCS #11 v2.10. */ -template -class DL_PublicKey_GFP : public DL_PublicKeyImpl -{ -public: - void Initialize(const DL_GroupParameters_IntegerBased ¶ms, const Integer &y) - {this->AccessGroupParameters().Initialize(params); this->SetPublicElement(y);} - void Initialize(const Integer &p, const Integer &g, const Integer &y) - {this->AccessGroupParameters().Initialize(p, g); this->SetPublicElement(y);} - void Initialize(const Integer &p, const Integer &q, const Integer &g, const Integer &y) - {this->AccessGroupParameters().Initialize(p, q, g); this->SetPublicElement(y);} - - // X509PublicKey - void BERDecodePublicKey(BufferedTransformation &bt, bool, size_t) - {this->SetPublicElement(Integer(bt));} - void DEREncodePublicKey(BufferedTransformation &bt) const - {this->GetPublicElement().DEREncode(bt);} -}; - -//! DL private key (in GF(p) groups) -template -class DL_PrivateKey_GFP : public DL_PrivateKeyImpl -{ -public: - void Initialize(RandomNumberGenerator &rng, unsigned int modulusBits) - {this->GenerateRandomWithKeySize(rng, modulusBits);} - void Initialize(RandomNumberGenerator &rng, const Integer &p, const Integer &g) - {this->GenerateRandom(rng, MakeParameters("Modulus", p)("SubgroupGenerator", g));} - void Initialize(RandomNumberGenerator &rng, const Integer &p, const Integer &q, const Integer &g) - {this->GenerateRandom(rng, MakeParameters("Modulus", p)("SubgroupOrder", q)("SubgroupGenerator", g));} - void Initialize(const DL_GroupParameters_IntegerBased ¶ms, const Integer &x) - {this->AccessGroupParameters().Initialize(params); this->SetPrivateExponent(x);} - void Initialize(const Integer &p, const Integer &g, const Integer &x) - {this->AccessGroupParameters().Initialize(p, g); this->SetPrivateExponent(x);} - void Initialize(const Integer &p, const Integer &q, const Integer &g, const Integer &x) - {this->AccessGroupParameters().Initialize(p, q, g); this->SetPrivateExponent(x);} -}; - -//! DL signing/verification keys (in GF(p) groups) -struct DL_SignatureKeys_GFP -{ - typedef DL_GroupParameters_GFP GroupParameters; - typedef DL_PublicKey_GFP PublicKey; - typedef DL_PrivateKey_GFP PrivateKey; -}; - -//! DL encryption/decryption keys (in GF(p) groups) -struct DL_CryptoKeys_GFP -{ - typedef DL_GroupParameters_GFP_DefaultSafePrime GroupParameters; - typedef DL_PublicKey_GFP PublicKey; - typedef DL_PrivateKey_GFP PrivateKey; -}; - -//! provided for backwards compatibility, this class uses the old non-standard Crypto++ key format -template -class DL_PublicKey_GFP_OldFormat : public BASE -{ -public: - void BERDecode(BufferedTransformation &bt) - { - BERSequenceDecoder seq(bt); - Integer v1(seq); - Integer v2(seq); - Integer v3(seq); - - if (seq.EndReached()) - { - this->AccessGroupParameters().Initialize(v1, v1/2, v2); - this->SetPublicElement(v3); - } - else - { - Integer v4(seq); - this->AccessGroupParameters().Initialize(v1, v2, v3); - this->SetPublicElement(v4); - } - - seq.MessageEnd(); - } - - void DEREncode(BufferedTransformation &bt) const - { - DERSequenceEncoder seq(bt); - this->GetGroupParameters().GetModulus().DEREncode(seq); - if (this->GetGroupParameters().GetCofactor() != 2) - this->GetGroupParameters().GetSubgroupOrder().DEREncode(seq); - this->GetGroupParameters().GetGenerator().DEREncode(seq); - this->GetPublicElement().DEREncode(seq); - seq.MessageEnd(); - } -}; - -//! provided for backwards compatibility, this class uses the old non-standard Crypto++ key format -template -class DL_PrivateKey_GFP_OldFormat : public BASE -{ -public: - void BERDecode(BufferedTransformation &bt) - { - BERSequenceDecoder seq(bt); - Integer v1(seq); - Integer v2(seq); - Integer v3(seq); - Integer v4(seq); - - if (seq.EndReached()) - { - this->AccessGroupParameters().Initialize(v1, v1/2, v2); - this->SetPrivateExponent(v4 % (v1/2)); // some old keys may have x >= q - } - else - { - Integer v5(seq); - this->AccessGroupParameters().Initialize(v1, v2, v3); - this->SetPrivateExponent(v5); - } - - seq.MessageEnd(); - } - - void DEREncode(BufferedTransformation &bt) const - { - DERSequenceEncoder seq(bt); - this->GetGroupParameters().GetModulus().DEREncode(seq); - if (this->GetGroupParameters().GetCofactor() != 2) - this->GetGroupParameters().GetSubgroupOrder().DEREncode(seq); - this->GetGroupParameters().GetGenerator().DEREncode(seq); - this->GetGroupParameters().ExponentiateBase(this->GetPrivateExponent()).DEREncode(seq); - this->GetPrivateExponent().DEREncode(seq); - seq.MessageEnd(); - } -}; - -//! DSA-1363 -template -struct GDSA : public DL_SS< - DL_SignatureKeys_GFP, - DL_Algorithm_GDSA, - DL_SignatureMessageEncodingMethod_DSA, - H> -{ -}; - -//! NR -template -struct NR : public DL_SS< - DL_SignatureKeys_GFP, - DL_Algorithm_NR, - DL_SignatureMessageEncodingMethod_NR, - H> -{ -}; - -//! DSA group parameters, these are GF(p) group parameters that are allowed by the DSA standard -class CRYPTOPP_DLL DL_GroupParameters_DSA : public DL_GroupParameters_GFP -{ -public: - /*! also checks that the lengths of p and q are allowed by the DSA standard */ - bool ValidateGroup(RandomNumberGenerator &rng, unsigned int level) const; - /*! parameters: (ModulusSize), or (Modulus, SubgroupOrder, SubgroupGenerator) */ - /*! ModulusSize must be between DSA::MIN_PRIME_LENGTH and DSA::MAX_PRIME_LENGTH, and divisible by DSA::PRIME_LENGTH_MULTIPLE */ - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg); - - static bool CRYPTOPP_API IsValidPrimeLength(unsigned int pbits) - {return pbits >= MIN_PRIME_LENGTH && pbits <= MAX_PRIME_LENGTH && pbits % PRIME_LENGTH_MULTIPLE == 0;} - - enum {MIN_PRIME_LENGTH = 1024, MAX_PRIME_LENGTH = 3072, PRIME_LENGTH_MULTIPLE = 1024}; -}; - -template -class DSA2; - -//! DSA keys -struct DL_Keys_DSA -{ - typedef DL_PublicKey_GFP PublicKey; - typedef DL_PrivateKey_WithSignaturePairwiseConsistencyTest, DSA2 > PrivateKey; -}; - -//! DSA, as specified in FIPS 186-3 -// class named DSA2 instead of DSA for backwards compatibility (DSA was a non-template class) -template -class DSA2 : public DL_SS< - DL_Keys_DSA, - DL_Algorithm_GDSA, - DL_SignatureMessageEncodingMethod_DSA, - H, - DSA2 > -{ -public: - static std::string CRYPTOPP_API StaticAlgorithmName() {return "DSA/" + (std::string)H::StaticAlgorithmName();} -}; - -//! DSA with SHA-1, typedef'd for backwards compatibility -typedef DSA2 DSA; - -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PublicKey_GFP; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKey_GFP; -CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKey_WithSignaturePairwiseConsistencyTest, DSA2 >; - -//! the XOR encryption method, for use with DL-based cryptosystems -template -class DL_EncryptionAlgorithm_Xor : public DL_SymmetricEncryptionAlgorithm -{ -public: - bool ParameterSupported(const char *name) const {return strcmp(name, Name::EncodingParameters()) == 0;} - size_t GetSymmetricKeyLength(size_t plaintextLength) const - {return plaintextLength + MAC::DEFAULT_KEYLENGTH;} - size_t GetSymmetricCiphertextLength(size_t plaintextLength) const - {return plaintextLength + MAC::DIGESTSIZE;} - size_t GetMaxSymmetricPlaintextLength(size_t ciphertextLength) const - {return (unsigned int)SaturatingSubtract(ciphertextLength, (unsigned int)MAC::DIGESTSIZE);} - void SymmetricEncrypt(RandomNumberGenerator &rng, const byte *key, const byte *plaintext, size_t plaintextLength, byte *ciphertext, const NameValuePairs ¶meters) const - { - const byte *cipherKey, *macKey; - if (DHAES_MODE) - { - macKey = key; - cipherKey = key + MAC::DEFAULT_KEYLENGTH; - } - else - { - cipherKey = key; - macKey = key + plaintextLength; - } - - ConstByteArrayParameter encodingParameters; - parameters.GetValue(Name::EncodingParameters(), encodingParameters); - - xorbuf(ciphertext, plaintext, cipherKey, plaintextLength); - MAC mac(macKey); - mac.Update(ciphertext, plaintextLength); - mac.Update(encodingParameters.begin(), encodingParameters.size()); - if (DHAES_MODE) - { - byte L[8] = {0,0,0,0}; - PutWord(false, BIG_ENDIAN_ORDER, L+4, word32(encodingParameters.size())); - mac.Update(L, 8); - } - mac.Final(ciphertext + plaintextLength); - } - DecodingResult SymmetricDecrypt(const byte *key, const byte *ciphertext, size_t ciphertextLength, byte *plaintext, const NameValuePairs ¶meters) const - { - size_t plaintextLength = GetMaxSymmetricPlaintextLength(ciphertextLength); - const byte *cipherKey, *macKey; - if (DHAES_MODE) - { - macKey = key; - cipherKey = key + MAC::DEFAULT_KEYLENGTH; - } - else - { - cipherKey = key; - macKey = key + plaintextLength; - } - - ConstByteArrayParameter encodingParameters; - parameters.GetValue(Name::EncodingParameters(), encodingParameters); - - MAC mac(macKey); - mac.Update(ciphertext, plaintextLength); - mac.Update(encodingParameters.begin(), encodingParameters.size()); - if (DHAES_MODE) - { - byte L[8] = {0,0,0,0}; - PutWord(false, BIG_ENDIAN_ORDER, L+4, word32(encodingParameters.size())); - mac.Update(L, 8); - } - if (!mac.Verify(ciphertext + plaintextLength)) - return DecodingResult(); - - xorbuf(plaintext, ciphertext, cipherKey, plaintextLength); - return DecodingResult(plaintextLength); - } -}; - -//! _ -template -class DL_KeyDerivationAlgorithm_P1363 : public DL_KeyDerivationAlgorithm -{ -public: - bool ParameterSupported(const char *name) const {return strcmp(name, Name::KeyDerivationParameters()) == 0;} - void Derive(const DL_GroupParameters ¶ms, byte *derivedKey, size_t derivedLength, const T &agreedElement, const T &ephemeralPublicKey, const NameValuePairs ¶meters) const - { - SecByteBlock agreedSecret; - if (DHAES_MODE) - { - agreedSecret.New(params.GetEncodedElementSize(true) + params.GetEncodedElementSize(false)); - params.EncodeElement(true, ephemeralPublicKey, agreedSecret); - params.EncodeElement(false, agreedElement, agreedSecret + params.GetEncodedElementSize(true)); - } - else - { - agreedSecret.New(params.GetEncodedElementSize(false)); - params.EncodeElement(false, agreedElement, agreedSecret); - } - - ConstByteArrayParameter derivationParameters; - parameters.GetValue(Name::KeyDerivationParameters(), derivationParameters); - KDF::DeriveKey(derivedKey, derivedLength, agreedSecret, agreedSecret.size(), derivationParameters.begin(), derivationParameters.size()); - } -}; - -//! Discrete Log Integrated Encryption Scheme, AKA DLIES -template -struct DLIES - : public DL_ES< - DL_CryptoKeys_GFP, - DL_KeyAgreementAlgorithm_DH, - DL_KeyDerivationAlgorithm_P1363 >, - DL_EncryptionAlgorithm_Xor, DHAES_MODE>, - DLIES<> > -{ - static std::string CRYPTOPP_API StaticAlgorithmName() {return "DLIES";} // TODO: fix this after name is standardized -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/gzip.h b/CryptoPP/gzip.h deleted file mode 100644 index f3148ad71..000000000 --- a/CryptoPP/gzip.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef CRYPTOPP_GZIP_H -#define CRYPTOPP_GZIP_H - -#include "zdeflate.h" -#include "zinflate.h" -#include "crc.h" - -NAMESPACE_BEGIN(CryptoPP) - -/// GZIP Compression (RFC 1952) -class Gzip : public Deflator -{ -public: - Gzip(BufferedTransformation *attachment=NULL, unsigned int deflateLevel=DEFAULT_DEFLATE_LEVEL, unsigned int log2WindowSize=DEFAULT_LOG2_WINDOW_SIZE, bool detectUncompressible=true) - : Deflator(attachment, deflateLevel, log2WindowSize, detectUncompressible) {} - Gzip(const NameValuePairs ¶meters, BufferedTransformation *attachment=NULL) - : Deflator(parameters, attachment) {} - -protected: - enum {MAGIC1=0x1f, MAGIC2=0x8b, // flags for the header - DEFLATED=8, FAST=4, SLOW=2}; - - void WritePrestreamHeader(); - void ProcessUncompressedData(const byte *string, size_t length); - void WritePoststreamTail(); - - word32 m_totalLen; - CRC32 m_crc; -}; - -/// GZIP Decompression (RFC 1952) -class Gunzip : public Inflator -{ -public: - typedef Inflator::Err Err; - class HeaderErr : public Err {public: HeaderErr() : Err(INVALID_DATA_FORMAT, "Gunzip: header decoding error") {}}; - class TailErr : public Err {public: TailErr() : Err(INVALID_DATA_FORMAT, "Gunzip: tail too short") {}}; - class CrcErr : public Err {public: CrcErr() : Err(DATA_INTEGRITY_CHECK_FAILED, "Gunzip: CRC check error") {}}; - class LengthErr : public Err {public: LengthErr() : Err(DATA_INTEGRITY_CHECK_FAILED, "Gunzip: length check error") {}}; - - /*! \param repeat decompress multiple compressed streams in series - \param autoSignalPropagation 0 to turn off MessageEnd signal - */ - Gunzip(BufferedTransformation *attachment = NULL, bool repeat = false, int autoSignalPropagation = -1); - -protected: - enum {MAGIC1=0x1f, MAGIC2=0x8b, // flags for the header - DEFLATED=8}; - - enum FLAG_MASKS { - CONTINUED=2, EXTRA_FIELDS=4, FILENAME=8, COMMENTS=16, ENCRYPTED=32}; - - unsigned int MaxPrestreamHeaderSize() const {return 1024;} - void ProcessPrestreamHeader(); - void ProcessDecompressedData(const byte *string, size_t length); - unsigned int MaxPoststreamTailSize() const {return 8;} - void ProcessPoststreamTail(); - - word32 m_length; - CRC32 m_crc; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/hex.cpp b/CryptoPP/hex.cpp deleted file mode 100644 index 5731df550..000000000 --- a/CryptoPP/hex.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// hex.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "hex.h" - -NAMESPACE_BEGIN(CryptoPP) - -static const byte s_vecUpper[] = "0123456789ABCDEF"; -static const byte s_vecLower[] = "0123456789abcdef"; - -void HexEncoder::IsolatedInitialize(const NameValuePairs ¶meters) -{ - bool uppercase = parameters.GetValueWithDefault(Name::Uppercase(), true); - m_filter->Initialize(CombinedNameValuePairs( - parameters, - MakeParameters(Name::EncodingLookupArray(), uppercase ? &s_vecUpper[0] : &s_vecLower[0], false)(Name::Log2Base(), 4, true))); -} - -void HexDecoder::IsolatedInitialize(const NameValuePairs ¶meters) -{ - BaseN_Decoder::IsolatedInitialize(CombinedNameValuePairs( - parameters, - MakeParameters(Name::DecodingLookupArray(), GetDefaultDecodingLookupArray(), false)(Name::Log2Base(), 4, true))); -} - -const int *HexDecoder::GetDefaultDecodingLookupArray() -{ - static volatile bool s_initialized = false; - static int s_array[256]; - - if (!s_initialized) - { - InitializeDecodingLookupArray(s_array, s_vecUpper, 16, true); - s_initialized = true; - } - return s_array; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/hex.h b/CryptoPP/hex.h deleted file mode 100644 index 006914c5a..000000000 --- a/CryptoPP/hex.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef CRYPTOPP_HEX_H -#define CRYPTOPP_HEX_H - -#include "basecode.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! Converts given data to base 16 -class CRYPTOPP_DLL HexEncoder : public SimpleProxyFilter -{ -public: - HexEncoder(BufferedTransformation *attachment = NULL, bool uppercase = true, int outputGroupSize = 0, const std::string &separator = ":", const std::string &terminator = "") - : SimpleProxyFilter(new BaseN_Encoder(new Grouper), attachment) - { - IsolatedInitialize(MakeParameters(Name::Uppercase(), uppercase)(Name::GroupSize(), outputGroupSize)(Name::Separator(), ConstByteArrayParameter(separator))(Name::Terminator(), ConstByteArrayParameter(terminator))); - } - - void IsolatedInitialize(const NameValuePairs ¶meters); -}; - -//! Decode base 16 data back to bytes -class CRYPTOPP_DLL HexDecoder : public BaseN_Decoder -{ -public: - HexDecoder(BufferedTransformation *attachment = NULL) - : BaseN_Decoder(GetDefaultDecodingLookupArray(), 4, attachment) {} - - void IsolatedInitialize(const NameValuePairs ¶meters); - -private: - static const int * CRYPTOPP_API GetDefaultDecodingLookupArray(); -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/hmac.cpp b/CryptoPP/hmac.cpp deleted file mode 100644 index d4a649c08..000000000 --- a/CryptoPP/hmac.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// hmac.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "hmac.h" - -NAMESPACE_BEGIN(CryptoPP) - -void HMAC_Base::UncheckedSetKey(const byte *userKey, unsigned int keylength, const NameValuePairs &) -{ - AssertValidKeyLength(keylength); - - Restart(); - - HashTransformation &hash = AccessHash(); - unsigned int blockSize = hash.BlockSize(); - - if (!blockSize) - throw InvalidArgument("HMAC: can only be used with a block-based hash function"); - - m_buf.resize(2*AccessHash().BlockSize() + AccessHash().DigestSize()); - - if (keylength <= blockSize) - memcpy(AccessIpad(), userKey, keylength); - else - { - AccessHash().CalculateDigest(AccessIpad(), userKey, keylength); - keylength = hash.DigestSize(); - } - - assert(keylength <= blockSize); - memset(AccessIpad()+keylength, 0, blockSize-keylength); - - for (unsigned int i=0; i, public MessageAuthenticationCode -{ -public: - HMAC_Base() : m_innerHashKeyed(false) {} - void UncheckedSetKey(const byte *userKey, unsigned int keylength, const NameValuePairs ¶ms); - - void Restart(); - void Update(const byte *input, size_t length); - void TruncatedFinal(byte *mac, size_t size); - unsigned int OptimalBlockSize() const {return const_cast(this)->AccessHash().OptimalBlockSize();} - unsigned int DigestSize() const {return const_cast(this)->AccessHash().DigestSize();} - -protected: - virtual HashTransformation & AccessHash() =0; - byte * AccessIpad() {return m_buf;} - byte * AccessOpad() {return m_buf + AccessHash().BlockSize();} - byte * AccessInnerHash() {return m_buf + 2*AccessHash().BlockSize();} - -private: - void KeyInnerHash(); - - SecByteBlock m_buf; - bool m_innerHashKeyed; -}; - -//! HMAC -/*! HMAC(K, text) = H(K XOR opad, H(K XOR ipad, text)) */ -template -class HMAC : public MessageAuthenticationCodeImpl > -{ -public: - CRYPTOPP_CONSTANT(DIGESTSIZE=T::DIGESTSIZE) - CRYPTOPP_CONSTANT(BLOCKSIZE=T::BLOCKSIZE) - - HMAC() {} - HMAC(const byte *key, size_t length=HMAC_Base::DEFAULT_KEYLENGTH) - {this->SetKey(key, length);} - - static std::string StaticAlgorithmName() {return std::string("HMAC(") + T::StaticAlgorithmName() + ")";} - std::string AlgorithmName() const {return std::string("HMAC(") + m_hash.AlgorithmName() + ")";} - -private: - HashTransformation & AccessHash() {return m_hash;} - - T m_hash; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/hrtimer.cpp b/CryptoPP/hrtimer.cpp deleted file mode 100644 index 6871a15dc..000000000 --- a/CryptoPP/hrtimer.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// hrtimer.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "hrtimer.h" -#include "misc.h" -#include // for NULL -#include - -#if defined(CRYPTOPP_WIN32_AVAILABLE) -#include -#elif defined(CRYPTOPP_UNIX_AVAILABLE) -#include -#include -#include -#endif - -#include - -NAMESPACE_BEGIN(CryptoPP) - -#ifndef CRYPTOPP_IMPORTS - -double TimerBase::ConvertTo(TimerWord t, Unit unit) -{ - static unsigned long unitsPerSecondTable[] = {1, 1000, 1000*1000, 1000*1000*1000}; - - assert(unit < sizeof(unitsPerSecondTable) / sizeof(unitsPerSecondTable[0])); - return (double)CRYPTOPP_VC6_INT64 t * unitsPerSecondTable[unit] / CRYPTOPP_VC6_INT64 TicksPerSecond(); -} - -void TimerBase::StartTimer() -{ - m_last = m_start = GetCurrentTimerValue(); - m_started = true; -} - -double TimerBase::ElapsedTimeAsDouble() -{ - if (m_stuckAtZero) - return 0; - - if (m_started) - { - TimerWord now = GetCurrentTimerValue(); - if (m_last < now) // protect against OS bugs where time goes backwards - m_last = now; - return ConvertTo(m_last - m_start, m_timerUnit); - } - - StartTimer(); - return 0; -} - -unsigned long TimerBase::ElapsedTime() -{ - double elapsed = ElapsedTimeAsDouble(); - assert(elapsed <= ULONG_MAX); - return (unsigned long)elapsed; -} - -TimerWord Timer::GetCurrentTimerValue() -{ -#if defined(CRYPTOPP_WIN32_AVAILABLE) - LARGE_INTEGER now; - if (!QueryPerformanceCounter(&now)) - throw Exception(Exception::OTHER_ERROR, "Timer: QueryPerformanceCounter failed with error " + IntToString(GetLastError())); - return now.QuadPart; -#elif defined(CRYPTOPP_UNIX_AVAILABLE) - timeval now; - gettimeofday(&now, NULL); - return (TimerWord)now.tv_sec * 1000000 + now.tv_usec; -#else - return clock(); -#endif -} - -TimerWord Timer::TicksPerSecond() -{ -#if defined(CRYPTOPP_WIN32_AVAILABLE) - static LARGE_INTEGER freq = {0}; - if (freq.QuadPart == 0) - { - if (!QueryPerformanceFrequency(&freq)) - throw Exception(Exception::OTHER_ERROR, "Timer: QueryPerformanceFrequency failed with error " + IntToString(GetLastError())); - } - return freq.QuadPart; -#elif defined(CRYPTOPP_UNIX_AVAILABLE) - return 1000000; -#else - return CLOCKS_PER_SEC; -#endif -} - -#endif // #ifndef CRYPTOPP_IMPORTS - -TimerWord ThreadUserTimer::GetCurrentTimerValue() -{ -#if defined(CRYPTOPP_WIN32_AVAILABLE) - static bool getCurrentThreadImplemented = true; - if (getCurrentThreadImplemented) - { - FILETIME now, ignored; - if (!GetThreadTimes(GetCurrentThread(), &ignored, &ignored, &ignored, &now)) - { - DWORD lastError = GetLastError(); - if (lastError == ERROR_CALL_NOT_IMPLEMENTED) - { - getCurrentThreadImplemented = false; - goto GetCurrentThreadNotImplemented; - } - throw Exception(Exception::OTHER_ERROR, "ThreadUserTimer: GetThreadTimes failed with error " + IntToString(lastError)); - } - return now.dwLowDateTime + ((TimerWord)now.dwHighDateTime << 32); - } -GetCurrentThreadNotImplemented: - return (TimerWord)clock() * (10*1000*1000 / CLOCKS_PER_SEC); -#elif defined(CRYPTOPP_UNIX_AVAILABLE) - tms now; - times(&now); - return now.tms_utime; -#else - return clock(); -#endif -} - -TimerWord ThreadUserTimer::TicksPerSecond() -{ -#if defined(CRYPTOPP_WIN32_AVAILABLE) - return 10*1000*1000; -#elif defined(CRYPTOPP_UNIX_AVAILABLE) - static const long ticksPerSecond = sysconf(_SC_CLK_TCK); - return ticksPerSecond; -#else - return CLOCKS_PER_SEC; -#endif -} - -NAMESPACE_END diff --git a/CryptoPP/hrtimer.h b/CryptoPP/hrtimer.h deleted file mode 100644 index 858dbd226..000000000 --- a/CryptoPP/hrtimer.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef CRYPTOPP_HRTIMER_H -#define CRYPTOPP_HRTIMER_H - -#include "config.h" -#ifndef HIGHRES_TIMER_AVAILABLE -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -#ifdef HIGHRES_TIMER_AVAILABLE - typedef word64 TimerWord; -#else - typedef clock_t TimerWord; -#endif - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TimerBase -{ -public: - enum Unit {SECONDS = 0, MILLISECONDS, MICROSECONDS, NANOSECONDS}; - TimerBase(Unit unit, bool stuckAtZero) : m_timerUnit(unit), m_stuckAtZero(stuckAtZero), m_started(false) {} - - virtual TimerWord GetCurrentTimerValue() =0; // GetCurrentTime is a macro in MSVC 6.0 - virtual TimerWord TicksPerSecond() =0; // this is not the resolution, just a conversion factor into seconds - - void StartTimer(); - double ElapsedTimeAsDouble(); - unsigned long ElapsedTime(); - -private: - double ConvertTo(TimerWord t, Unit unit); - - Unit m_timerUnit; // HPUX workaround: m_unit is a system macro on HPUX - bool m_stuckAtZero, m_started; - TimerWord m_start, m_last; -}; - -//! measure CPU time spent executing instructions of this thread (if supported by OS) -/*! /note This only works correctly on Windows NT or later. On Unix it reports process time, and others wall clock time. -*/ -class ThreadUserTimer : public TimerBase -{ -public: - ThreadUserTimer(Unit unit = TimerBase::SECONDS, bool stuckAtZero = false) : TimerBase(unit, stuckAtZero) {} - TimerWord GetCurrentTimerValue(); - TimerWord TicksPerSecond(); -}; - -//! high resolution timer -class CRYPTOPP_DLL Timer : public TimerBase -{ -public: - Timer(Unit unit = TimerBase::SECONDS, bool stuckAtZero = false) : TimerBase(unit, stuckAtZero) {} - TimerWord GetCurrentTimerValue(); - TimerWord TicksPerSecond(); -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/integer.cpp b/CryptoPP/integer.cpp deleted file mode 100644 index f07cce873..000000000 --- a/CryptoPP/integer.cpp +++ /dev/null @@ -1,4235 +0,0 @@ -// integer.cpp - written and placed in the public domain by Wei Dai -// contains public domain code contributed by Alister Lee and Leonard Janke - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "integer.h" -#include "modarith.h" -#include "nbtheory.h" -#include "asn.h" -#include "oids.h" -#include "words.h" -#include "algparam.h" -#include "pubkey.h" // for P1363_KDF2 -#include "sha.h" -#include "cpu.h" - -#include - -#if _MSC_VER >= 1400 - #include -#endif - -#ifdef __DECCXX - #include -#endif - -#ifdef CRYPTOPP_MSVC6_NO_PP - #pragma message("You do not seem to have the Visual C++ Processor Pack installed, so use of SSE2 instructions will be disabled.") -#endif - -#define CRYPTOPP_INTEGER_SSE2 (CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86) - -NAMESPACE_BEGIN(CryptoPP) - -bool AssignIntToInteger(const std::type_info &valueType, void *pInteger, const void *pInt) -{ - if (valueType != typeid(Integer)) - return false; - *reinterpret_cast(pInteger) = *reinterpret_cast(pInt); - return true; -} - -inline static int Compare(const word *A, const word *B, size_t N) -{ - while (N--) - if (A[N] > B[N]) - return 1; - else if (A[N] < B[N]) - return -1; - - return 0; -} - -inline static int Increment(word *A, size_t N, word B=1) -{ - assert(N); - word t = A[0]; - A[0] = t+B; - if (A[0] >= t) - return 0; - for (unsigned i=1; i>(WORD_BITS-1)); d##0 = 2*d##0 + (c>>(WORD_BITS-1)); c *= 2; - #endif - #ifndef Acc2WordsBy2 - #define Acc2WordsBy2(a, b) a##0 += b##0; a##1 += a##0 < b##0; a##1 += b##1; - #endif - #define AddWithCarry(u, a, b) {word t = a+b; u##0 = t + u##1; u##1 = (ta) + (u##0>t);} - #define GetCarry(u) u##1 - #define GetBorrow(u) u##1 -#else - #define Declare2Words(x) dword x; - #if _MSC_VER >= 1400 && !defined(__INTEL_COMPILER) - #define MultiplyWords(p, a, b) p = __emulu(a, b); - #else - #define MultiplyWords(p, a, b) p = (dword)a*b; - #endif - #define AssignWord(a, b) a = b; - #define Add2WordsBy1(a, b, c) a = b + c; - #define Acc2WordsBy2(a, b) a += b; - #define LowWord(a) word(a) - #define HighWord(a) word(a>>WORD_BITS) - #define Double3Words(c, d) d = 2*d + (c>>(WORD_BITS-1)); c *= 2; - #define AddWithCarry(u, a, b) u = dword(a) + b + GetCarry(u); - #define SubtractWithBorrow(u, a, b) u = dword(a) - b - GetBorrow(u); - #define GetCarry(u) HighWord(u) - #define GetBorrow(u) word(u>>(WORD_BITS*2-1)) -#endif -#ifndef MulAcc - #define MulAcc(c, d, a, b) MultiplyWords(p, a, b); Acc2WordsBy1(p, c); c = LowWord(p); Acc2WordsBy1(d, HighWord(p)); -#endif -#ifndef Acc2WordsBy1 - #define Acc2WordsBy1(a, b) Add2WordsBy1(a, a, b) -#endif -#ifndef Acc3WordsBy2 - #define Acc3WordsBy2(c, d, e) Acc2WordsBy1(e, c); c = LowWord(e); Add2WordsBy1(e, d, HighWord(e)); -#endif - -class DWord -{ -public: - DWord() {} - -#ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - explicit DWord(word low) - { - m_whole = low; - } -#else - explicit DWord(word low) - { - m_halfs.low = low; - m_halfs.high = 0; - } -#endif - - DWord(word low, word high) - { - m_halfs.low = low; - m_halfs.high = high; - } - - static DWord Multiply(word a, word b) - { - DWord r; - #ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - r.m_whole = (dword)a * b; - #elif defined(MultiplyWordsLoHi) - MultiplyWordsLoHi(r.m_halfs.low, r.m_halfs.high, a, b); - #endif - return r; - } - - static DWord MultiplyAndAdd(word a, word b, word c) - { - DWord r = Multiply(a, b); - return r += c; - } - - DWord & operator+=(word a) - { - #ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - m_whole = m_whole + a; - #else - m_halfs.low += a; - m_halfs.high += (m_halfs.low < a); - #endif - return *this; - } - - DWord operator+(word a) - { - DWord r; - #ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - r.m_whole = m_whole + a; - #else - r.m_halfs.low = m_halfs.low + a; - r.m_halfs.high = m_halfs.high + (r.m_halfs.low < a); - #endif - return r; - } - - DWord operator-(DWord a) - { - DWord r; - #ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - r.m_whole = m_whole - a.m_whole; - #else - r.m_halfs.low = m_halfs.low - a.m_halfs.low; - r.m_halfs.high = m_halfs.high - a.m_halfs.high - (r.m_halfs.low > m_halfs.low); - #endif - return r; - } - - DWord operator-(word a) - { - DWord r; - #ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - r.m_whole = m_whole - a; - #else - r.m_halfs.low = m_halfs.low - a; - r.m_halfs.high = m_halfs.high - (r.m_halfs.low > m_halfs.low); - #endif - return r; - } - - // returns quotient, which must fit in a word - word operator/(word divisor); - - word operator%(word a); - - bool operator!() const - { - #ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - return !m_whole; - #else - return !m_halfs.high && !m_halfs.low; - #endif - } - - word GetLowHalf() const {return m_halfs.low;} - word GetHighHalf() const {return m_halfs.high;} - word GetHighHalfAsBorrow() const {return 0-m_halfs.high;} - -private: - union - { - #ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - dword m_whole; - #endif - struct - { - #ifdef IS_LITTLE_ENDIAN - word low; - word high; - #else - word high; - word low; - #endif - } m_halfs; - }; -}; - -class Word -{ -public: - Word() {} - - Word(word value) - { - m_whole = value; - } - - Word(hword low, hword high) - { - m_whole = low | (word(high) << (WORD_BITS/2)); - } - - static Word Multiply(hword a, hword b) - { - Word r; - r.m_whole = (word)a * b; - return r; - } - - Word operator-(Word a) - { - Word r; - r.m_whole = m_whole - a.m_whole; - return r; - } - - Word operator-(hword a) - { - Word r; - r.m_whole = m_whole - a; - return r; - } - - // returns quotient, which must fit in a word - hword operator/(hword divisor) - { - return hword(m_whole / divisor); - } - - bool operator!() const - { - return !m_whole; - } - - word GetWhole() const {return m_whole;} - hword GetLowHalf() const {return hword(m_whole);} - hword GetHighHalf() const {return hword(m_whole>>(WORD_BITS/2));} - hword GetHighHalfAsBorrow() const {return 0-hword(m_whole>>(WORD_BITS/2));} - -private: - word m_whole; -}; - -// do a 3 word by 2 word divide, returns quotient and leaves remainder in A -template -S DivideThreeWordsByTwo(S *A, S B0, S B1, D *dummy=NULL) -{ - // assert {A[2],A[1]} < {B1,B0}, so quotient can fit in a S - assert(A[2] < B1 || (A[2]==B1 && A[1] < B0)); - - // estimate the quotient: do a 2 S by 1 S divide - S Q; - if (S(B1+1) == 0) - Q = A[2]; - else if (B1 > 0) - Q = D(A[1], A[2]) / S(B1+1); - else - Q = D(A[0], A[1]) / B0; - - // now subtract Q*B from A - D p = D::Multiply(B0, Q); - D u = (D) A[0] - p.GetLowHalf(); - A[0] = u.GetLowHalf(); - u = (D) A[1] - p.GetHighHalf() - u.GetHighHalfAsBorrow() - D::Multiply(B1, Q); - A[1] = u.GetLowHalf(); - A[2] += u.GetHighHalf(); - - // Q <= actual quotient, so fix it - while (A[2] || A[1] > B1 || (A[1]==B1 && A[0]>=B0)) - { - u = (D) A[0] - B0; - A[0] = u.GetLowHalf(); - u = (D) A[1] - B1 - u.GetHighHalfAsBorrow(); - A[1] = u.GetLowHalf(); - A[2] += u.GetHighHalf(); - Q++; - assert(Q); // shouldn't overflow - } - - return Q; -} - -// do a 4 word by 2 word divide, returns 2 word quotient in Q0 and Q1 -template -inline D DivideFourWordsByTwo(S *T, const D &Al, const D &Ah, const D &B) -{ - if (!B) // if divisor is 0, we assume divisor==2**(2*WORD_BITS) - return D(Ah.GetLowHalf(), Ah.GetHighHalf()); - else - { - S Q[2]; - T[0] = Al.GetLowHalf(); - T[1] = Al.GetHighHalf(); - T[2] = Ah.GetLowHalf(); - T[3] = Ah.GetHighHalf(); - Q[1] = DivideThreeWordsByTwo(T+1, B.GetLowHalf(), B.GetHighHalf()); - Q[0] = DivideThreeWordsByTwo(T, B.GetLowHalf(), B.GetHighHalf()); - return D(Q[0], Q[1]); - } -} - -// returns quotient, which must fit in a word -inline word DWord::operator/(word a) -{ - #ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - return word(m_whole / a); - #else - hword r[4]; - return DivideFourWordsByTwo(r, m_halfs.low, m_halfs.high, a).GetWhole(); - #endif -} - -inline word DWord::operator%(word a) -{ - #ifdef CRYPTOPP_NATIVE_DWORD_AVAILABLE - return word(m_whole % a); - #else - if (a < (word(1) << (WORD_BITS/2))) - { - hword h = hword(a); - word r = m_halfs.high % h; - r = ((m_halfs.low >> (WORD_BITS/2)) + (r << (WORD_BITS/2))) % h; - return hword((hword(m_halfs.low) + (r << (WORD_BITS/2))) % h); - } - else - { - hword r[4]; - DivideFourWordsByTwo(r, m_halfs.low, m_halfs.high, a); - return Word(r[0], r[1]).GetWhole(); - } - #endif -} - -// ******************************************************** - -// use some tricks to share assembly code between MSVC and GCC -#if defined(__GNUC__) - #define AddPrologue \ - int result; \ - __asm__ __volatile__ \ - ( \ - ".intel_syntax noprefix;" - #define AddEpilogue \ - ".att_syntax prefix;" \ - : "=a" (result)\ - : "d" (C), "a" (A), "D" (B), "c" (N) \ - : "%esi", "memory", "cc" \ - );\ - return result; - #define MulPrologue \ - __asm__ __volatile__ \ - ( \ - ".intel_syntax noprefix;" \ - AS1( push ebx) \ - AS2( mov ebx, edx) - #define MulEpilogue \ - AS1( pop ebx) \ - ".att_syntax prefix;" \ - : \ - : "d" (s_maskLow16), "c" (C), "a" (A), "D" (B) \ - : "%esi", "memory", "cc" \ - ); - #define SquPrologue MulPrologue - #define SquEpilogue \ - AS1( pop ebx) \ - ".att_syntax prefix;" \ - : \ - : "d" (s_maskLow16), "c" (C), "a" (A) \ - : "%esi", "%edi", "memory", "cc" \ - ); - #define TopPrologue MulPrologue - #define TopEpilogue \ - AS1( pop ebx) \ - ".att_syntax prefix;" \ - : \ - : "d" (s_maskLow16), "c" (C), "a" (A), "D" (B), "S" (L) \ - : "memory", "cc" \ - ); -#else - #define AddPrologue \ - __asm push edi \ - __asm push esi \ - __asm mov eax, [esp+12] \ - __asm mov edi, [esp+16] - #define AddEpilogue \ - __asm pop esi \ - __asm pop edi \ - __asm ret 8 -#if _MSC_VER < 1300 - #define SaveEBX __asm push ebx - #define RestoreEBX __asm pop ebx -#else - #define SaveEBX - #define RestoreEBX -#endif - #define SquPrologue \ - AS2( mov eax, A) \ - AS2( mov ecx, C) \ - SaveEBX \ - AS2( lea ebx, s_maskLow16) - #define MulPrologue \ - AS2( mov eax, A) \ - AS2( mov edi, B) \ - AS2( mov ecx, C) \ - SaveEBX \ - AS2( lea ebx, s_maskLow16) - #define TopPrologue \ - AS2( mov eax, A) \ - AS2( mov edi, B) \ - AS2( mov ecx, C) \ - AS2( mov esi, L) \ - SaveEBX \ - AS2( lea ebx, s_maskLow16) - #define SquEpilogue RestoreEBX - #define MulEpilogue RestoreEBX - #define TopEpilogue RestoreEBX -#endif - -#ifdef CRYPTOPP_X64_MASM_AVAILABLE -extern "C" { -int Baseline_Add(size_t N, word *C, const word *A, const word *B); -int Baseline_Sub(size_t N, word *C, const word *A, const word *B); -} -#elif defined(CRYPTOPP_X64_ASM_AVAILABLE) && defined(__GNUC__) && defined(CRYPTOPP_WORD128_AVAILABLE) -int Baseline_Add(size_t N, word *C, const word *A, const word *B) -{ - word result; - __asm__ __volatile__ - ( - ".intel_syntax;" - AS1( neg %1) - ASJ( jz, 1, f) - AS2( mov %0,[%3+8*%1]) - AS2( add %0,[%4+8*%1]) - AS2( mov [%2+8*%1],%0) - ASL(0) - AS2( mov %0,[%3+8*%1+8]) - AS2( adc %0,[%4+8*%1+8]) - AS2( mov [%2+8*%1+8],%0) - AS2( lea %1,[%1+2]) - ASJ( jrcxz, 1, f) - AS2( mov %0,[%3+8*%1]) - AS2( adc %0,[%4+8*%1]) - AS2( mov [%2+8*%1],%0) - ASJ( jmp, 0, b) - ASL(1) - AS2( mov %0, 0) - AS2( adc %0, %0) - ".att_syntax;" - : "=&r" (result), "+c" (N) - : "r" (C+N), "r" (A+N), "r" (B+N) - : "memory", "cc" - ); - return (int)result; -} - -int Baseline_Sub(size_t N, word *C, const word *A, const word *B) -{ - word result; - __asm__ __volatile__ - ( - ".intel_syntax;" - AS1( neg %1) - ASJ( jz, 1, f) - AS2( mov %0,[%3+8*%1]) - AS2( sub %0,[%4+8*%1]) - AS2( mov [%2+8*%1],%0) - ASL(0) - AS2( mov %0,[%3+8*%1+8]) - AS2( sbb %0,[%4+8*%1+8]) - AS2( mov [%2+8*%1+8],%0) - AS2( lea %1,[%1+2]) - ASJ( jrcxz, 1, f) - AS2( mov %0,[%3+8*%1]) - AS2( sbb %0,[%4+8*%1]) - AS2( mov [%2+8*%1],%0) - ASJ( jmp, 0, b) - ASL(1) - AS2( mov %0, 0) - AS2( adc %0, %0) - ".att_syntax;" - : "=&r" (result), "+c" (N) - : "r" (C+N), "r" (A+N), "r" (B+N) - : "memory", "cc" - ); - return (int)result; -} -#elif defined(CRYPTOPP_X86_ASM_AVAILABLE) && CRYPTOPP_BOOL_X86 -CRYPTOPP_NAKED int CRYPTOPP_FASTCALL Baseline_Add(size_t N, word *C, const word *A, const word *B) -{ - AddPrologue - - // now: eax = A, edi = B, edx = C, ecx = N - AS2( lea eax, [eax+4*ecx]) - AS2( lea edi, [edi+4*ecx]) - AS2( lea edx, [edx+4*ecx]) - - AS1( neg ecx) // ecx is negative index - AS2( test ecx, 2) // this clears carry flag - ASJ( jz, 0, f) - AS2( sub ecx, 2) - ASJ( jmp, 1, f) - - ASL(0) - ASJ( jecxz, 2, f) // loop until ecx overflows and becomes zero - AS2( mov esi,[eax+4*ecx]) - AS2( adc esi,[edi+4*ecx]) - AS2( mov [edx+4*ecx],esi) - AS2( mov esi,[eax+4*ecx+4]) - AS2( adc esi,[edi+4*ecx+4]) - AS2( mov [edx+4*ecx+4],esi) - ASL(1) - AS2( mov esi,[eax+4*ecx+8]) - AS2( adc esi,[edi+4*ecx+8]) - AS2( mov [edx+4*ecx+8],esi) - AS2( mov esi,[eax+4*ecx+12]) - AS2( adc esi,[edi+4*ecx+12]) - AS2( mov [edx+4*ecx+12],esi) - - AS2( lea ecx,[ecx+4]) // advance index, avoid inc which causes slowdown on Intel Core 2 - ASJ( jmp, 0, b) - - ASL(2) - AS2( mov eax, 0) - AS1( setc al) // store carry into eax (return result register) - - AddEpilogue -} - -CRYPTOPP_NAKED int CRYPTOPP_FASTCALL Baseline_Sub(size_t N, word *C, const word *A, const word *B) -{ - AddPrologue - - // now: eax = A, edi = B, edx = C, ecx = N - AS2( lea eax, [eax+4*ecx]) - AS2( lea edi, [edi+4*ecx]) - AS2( lea edx, [edx+4*ecx]) - - AS1( neg ecx) // ecx is negative index - AS2( test ecx, 2) // this clears carry flag - ASJ( jz, 0, f) - AS2( sub ecx, 2) - ASJ( jmp, 1, f) - - ASL(0) - ASJ( jecxz, 2, f) // loop until ecx overflows and becomes zero - AS2( mov esi,[eax+4*ecx]) - AS2( sbb esi,[edi+4*ecx]) - AS2( mov [edx+4*ecx],esi) - AS2( mov esi,[eax+4*ecx+4]) - AS2( sbb esi,[edi+4*ecx+4]) - AS2( mov [edx+4*ecx+4],esi) - ASL(1) - AS2( mov esi,[eax+4*ecx+8]) - AS2( sbb esi,[edi+4*ecx+8]) - AS2( mov [edx+4*ecx+8],esi) - AS2( mov esi,[eax+4*ecx+12]) - AS2( sbb esi,[edi+4*ecx+12]) - AS2( mov [edx+4*ecx+12],esi) - - AS2( lea ecx,[ecx+4]) // advance index, avoid inc which causes slowdown on Intel Core 2 - ASJ( jmp, 0, b) - - ASL(2) - AS2( mov eax, 0) - AS1( setc al) // store carry into eax (return result register) - - AddEpilogue -} - -#if CRYPTOPP_INTEGER_SSE2 -CRYPTOPP_NAKED int CRYPTOPP_FASTCALL SSE2_Add(size_t N, word *C, const word *A, const word *B) -{ - AddPrologue - - // now: eax = A, edi = B, edx = C, ecx = N - AS2( lea eax, [eax+4*ecx]) - AS2( lea edi, [edi+4*ecx]) - AS2( lea edx, [edx+4*ecx]) - - AS1( neg ecx) // ecx is negative index - AS2( pxor mm2, mm2) - ASJ( jz, 2, f) - AS2( test ecx, 2) // this clears carry flag - ASJ( jz, 0, f) - AS2( sub ecx, 2) - ASJ( jmp, 1, f) - - ASL(0) - AS2( movd mm0, DWORD PTR [eax+4*ecx]) - AS2( movd mm1, DWORD PTR [edi+4*ecx]) - AS2( paddq mm0, mm1) - AS2( paddq mm2, mm0) - AS2( movd DWORD PTR [edx+4*ecx], mm2) - AS2( psrlq mm2, 32) - - AS2( movd mm0, DWORD PTR [eax+4*ecx+4]) - AS2( movd mm1, DWORD PTR [edi+4*ecx+4]) - AS2( paddq mm0, mm1) - AS2( paddq mm2, mm0) - AS2( movd DWORD PTR [edx+4*ecx+4], mm2) - AS2( psrlq mm2, 32) - - ASL(1) - AS2( movd mm0, DWORD PTR [eax+4*ecx+8]) - AS2( movd mm1, DWORD PTR [edi+4*ecx+8]) - AS2( paddq mm0, mm1) - AS2( paddq mm2, mm0) - AS2( movd DWORD PTR [edx+4*ecx+8], mm2) - AS2( psrlq mm2, 32) - - AS2( movd mm0, DWORD PTR [eax+4*ecx+12]) - AS2( movd mm1, DWORD PTR [edi+4*ecx+12]) - AS2( paddq mm0, mm1) - AS2( paddq mm2, mm0) - AS2( movd DWORD PTR [edx+4*ecx+12], mm2) - AS2( psrlq mm2, 32) - - AS2( add ecx, 4) - ASJ( jnz, 0, b) - - ASL(2) - AS2( movd eax, mm2) - AS1( emms) - - AddEpilogue -} -CRYPTOPP_NAKED int CRYPTOPP_FASTCALL SSE2_Sub(size_t N, word *C, const word *A, const word *B) -{ - AddPrologue - - // now: eax = A, edi = B, edx = C, ecx = N - AS2( lea eax, [eax+4*ecx]) - AS2( lea edi, [edi+4*ecx]) - AS2( lea edx, [edx+4*ecx]) - - AS1( neg ecx) // ecx is negative index - AS2( pxor mm2, mm2) - ASJ( jz, 2, f) - AS2( test ecx, 2) // this clears carry flag - ASJ( jz, 0, f) - AS2( sub ecx, 2) - ASJ( jmp, 1, f) - - ASL(0) - AS2( movd mm0, DWORD PTR [eax+4*ecx]) - AS2( movd mm1, DWORD PTR [edi+4*ecx]) - AS2( psubq mm0, mm1) - AS2( psubq mm0, mm2) - AS2( movd DWORD PTR [edx+4*ecx], mm0) - AS2( psrlq mm0, 63) - - AS2( movd mm2, DWORD PTR [eax+4*ecx+4]) - AS2( movd mm1, DWORD PTR [edi+4*ecx+4]) - AS2( psubq mm2, mm1) - AS2( psubq mm2, mm0) - AS2( movd DWORD PTR [edx+4*ecx+4], mm2) - AS2( psrlq mm2, 63) - - ASL(1) - AS2( movd mm0, DWORD PTR [eax+4*ecx+8]) - AS2( movd mm1, DWORD PTR [edi+4*ecx+8]) - AS2( psubq mm0, mm1) - AS2( psubq mm0, mm2) - AS2( movd DWORD PTR [edx+4*ecx+8], mm0) - AS2( psrlq mm0, 63) - - AS2( movd mm2, DWORD PTR [eax+4*ecx+12]) - AS2( movd mm1, DWORD PTR [edi+4*ecx+12]) - AS2( psubq mm2, mm1) - AS2( psubq mm2, mm0) - AS2( movd DWORD PTR [edx+4*ecx+12], mm2) - AS2( psrlq mm2, 63) - - AS2( add ecx, 4) - ASJ( jnz, 0, b) - - ASL(2) - AS2( movd eax, mm2) - AS1( emms) - - AddEpilogue -} -#endif // #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE -#else -int CRYPTOPP_FASTCALL Baseline_Add(size_t N, word *C, const word *A, const word *B) -{ - assert (N%2 == 0); - - Declare2Words(u); - AssignWord(u, 0); - for (size_t i=0; i=2 && N%2==0); - - if (N <= s_recursionLimit) - s_pMul[N/4](R, A, B); - else - { - const size_t N2 = N/2; - - size_t AN2 = Compare(A0, A1, N2) > 0 ? 0 : N2; - Subtract(R0, A + AN2, A + (N2 ^ AN2), N2); - - size_t BN2 = Compare(B0, B1, N2) > 0 ? 0 : N2; - Subtract(R1, B + BN2, B + (N2 ^ BN2), N2); - - RecursiveMultiply(R2, T2, A1, B1, N2); - RecursiveMultiply(T0, T2, R0, R1, N2); - RecursiveMultiply(R0, T2, A0, B0, N2); - - // now T[01] holds (A1-A0)*(B0-B1), R[01] holds A0*B0, R[23] holds A1*B1 - - int c2 = Add(R2, R2, R1, N2); - int c3 = c2; - c2 += Add(R1, R2, R0, N2); - c3 += Add(R2, R2, R3, N2); - - if (AN2 == BN2) - c3 -= Subtract(R1, R1, T0, N); - else - c3 += Add(R1, R1, T0, N); - - c3 += Increment(R2, N2, c2); - assert (c3 >= 0 && c3 <= 2); - Increment(R3, N2, c3); - } -} - -// R[2*N] - result = A*A -// T[2*N] - temporary work space -// A[N] --- number to be squared - -void RecursiveSquare(word *R, word *T, const word *A, size_t N) -{ - assert(N && N%2==0); - - if (N <= s_recursionLimit) - s_pSqu[N/4](R, A); - else - { - const size_t N2 = N/2; - - RecursiveSquare(R0, T2, A0, N2); - RecursiveSquare(R2, T2, A1, N2); - RecursiveMultiply(T0, T2, A0, A1, N2); - - int carry = Add(R1, R1, T0, N); - carry += Add(R1, R1, T0, N); - Increment(R3, N2, carry); - } -} - -// R[N] - bottom half of A*B -// T[3*N/2] - temporary work space -// A[N] - multiplier -// B[N] - multiplicant - -void RecursiveMultiplyBottom(word *R, word *T, const word *A, const word *B, size_t N) -{ - assert(N>=2 && N%2==0); - - if (N <= s_recursionLimit) - s_pBot[N/4](R, A, B); - else - { - const size_t N2 = N/2; - - RecursiveMultiply(R, T, A0, B0, N2); - RecursiveMultiplyBottom(T0, T1, A1, B0, N2); - Add(R1, R1, T0, N2); - RecursiveMultiplyBottom(T0, T1, A0, B1, N2); - Add(R1, R1, T0, N2); - } -} - -// R[N] --- upper half of A*B -// T[2*N] - temporary work space -// L[N] --- lower half of A*B -// A[N] --- multiplier -// B[N] --- multiplicant - -void MultiplyTop(word *R, word *T, const word *L, const word *A, const word *B, size_t N) -{ - assert(N>=2 && N%2==0); - - if (N <= s_recursionLimit) - s_pTop[N/4](R, A, B, L[N-1]); - else - { - const size_t N2 = N/2; - - size_t AN2 = Compare(A0, A1, N2) > 0 ? 0 : N2; - Subtract(R0, A + AN2, A + (N2 ^ AN2), N2); - - size_t BN2 = Compare(B0, B1, N2) > 0 ? 0 : N2; - Subtract(R1, B + BN2, B + (N2 ^ BN2), N2); - - RecursiveMultiply(T0, T2, R0, R1, N2); - RecursiveMultiply(R0, T2, A1, B1, N2); - - // now T[01] holds (A1-A0)*(B0-B1) = A1*B0+A0*B1-A1*B1-A0*B0, R[01] holds A1*B1 - - int t, c3; - int c2 = Subtract(T2, L+N2, L, N2); - - if (AN2 == BN2) - { - c2 -= Add(T2, T2, T0, N2); - t = (Compare(T2, R0, N2) == -1); - c3 = t - Subtract(T2, T2, T1, N2); - } - else - { - c2 += Subtract(T2, T2, T0, N2); - t = (Compare(T2, R0, N2) == -1); - c3 = t + Add(T2, T2, T1, N2); - } - - c2 += t; - if (c2 >= 0) - c3 += Increment(T2, N2, c2); - else - c3 -= Decrement(T2, N2, -c2); - c3 += Add(R0, T2, R1, N2); - - assert (c3 >= 0 && c3 <= 2); - Increment(R1, N2, c3); - } -} - -inline void Multiply(word *R, word *T, const word *A, const word *B, size_t N) -{ - RecursiveMultiply(R, T, A, B, N); -} - -inline void Square(word *R, word *T, const word *A, size_t N) -{ - RecursiveSquare(R, T, A, N); -} - -inline void MultiplyBottom(word *R, word *T, const word *A, const word *B, size_t N) -{ - RecursiveMultiplyBottom(R, T, A, B, N); -} - -// R[NA+NB] - result = A*B -// T[NA+NB] - temporary work space -// A[NA] ---- multiplier -// B[NB] ---- multiplicant - -void AsymmetricMultiply(word *R, word *T, const word *A, size_t NA, const word *B, size_t NB) -{ - if (NA == NB) - { - if (A == B) - Square(R, T, A, NA); - else - Multiply(R, T, A, B, NA); - - return; - } - - if (NA > NB) - { - std::swap(A, B); - std::swap(NA, NB); - } - - assert(NB % NA == 0); - - if (NA==2 && !A[1]) - { - switch (A[0]) - { - case 0: - SetWords(R, 0, NB+2); - return; - case 1: - CopyWords(R, B, NB); - R[NB] = R[NB+1] = 0; - return; - default: - R[NB] = LinearMultiply(R, B, A[0], NB); - R[NB+1] = 0; - return; - } - } - - size_t i; - if ((NB/NA)%2 == 0) - { - Multiply(R, T, A, B, NA); - CopyWords(T+2*NA, R+NA, NA); - - for (i=2*NA; i=4); - -#define M0 M -#define M1 (M+N2) -#define V0 V -#define V1 (V+N2) - -#define X0 X -#define X1 (X+N2) -#define X2 (X+N) -#define X3 (X+N+N2) - - const size_t N2 = N/2; - Multiply(T0, T2, V0, X3, N2); - int c2 = Add(T0, T0, X0, N); - MultiplyBottom(T3, T2, T0, U, N2); - MultiplyTop(T2, R, T0, T3, M0, N2); - c2 -= Subtract(T2, T1, T2, N2); - Multiply(T0, R, T3, M1, N2); - c2 -= Subtract(T0, T2, T0, N2); - int c3 = -(int)Subtract(T1, X2, T1, N2); - Multiply(R0, T2, V1, X3, N2); - c3 += Add(R, R, T, N); - - if (c2>0) - c3 += Increment(R1, N2); - else if (c2<0) - c3 -= Decrement(R1, N2, -c2); - - assert(c3>=-1 && c3<=1); - if (c3>0) - Subtract(R, R, M, N); - else if (c3<0) - Add(R, R, M, N); - -#undef M0 -#undef M1 -#undef V0 -#undef V1 - -#undef X0 -#undef X1 -#undef X2 -#undef X3 -} - -#undef A0 -#undef A1 -#undef B0 -#undef B1 - -#undef T0 -#undef T1 -#undef T2 -#undef T3 - -#undef R0 -#undef R1 -#undef R2 -#undef R3 - -/* -// do a 3 word by 2 word divide, returns quotient and leaves remainder in A -static word SubatomicDivide(word *A, word B0, word B1) -{ - // assert {A[2],A[1]} < {B1,B0}, so quotient can fit in a word - assert(A[2] < B1 || (A[2]==B1 && A[1] < B0)); - - // estimate the quotient: do a 2 word by 1 word divide - word Q; - if (B1+1 == 0) - Q = A[2]; - else - Q = DWord(A[1], A[2]).DividedBy(B1+1); - - // now subtract Q*B from A - DWord p = DWord::Multiply(B0, Q); - DWord u = (DWord) A[0] - p.GetLowHalf(); - A[0] = u.GetLowHalf(); - u = (DWord) A[1] - p.GetHighHalf() - u.GetHighHalfAsBorrow() - DWord::Multiply(B1, Q); - A[1] = u.GetLowHalf(); - A[2] += u.GetHighHalf(); - - // Q <= actual quotient, so fix it - while (A[2] || A[1] > B1 || (A[1]==B1 && A[0]>=B0)) - { - u = (DWord) A[0] - B0; - A[0] = u.GetLowHalf(); - u = (DWord) A[1] - B1 - u.GetHighHalfAsBorrow(); - A[1] = u.GetLowHalf(); - A[2] += u.GetHighHalf(); - Q++; - assert(Q); // shouldn't overflow - } - - return Q; -} - -// do a 4 word by 2 word divide, returns 2 word quotient in Q0 and Q1 -static inline void AtomicDivide(word *Q, const word *A, const word *B) -{ - if (!B[0] && !B[1]) // if divisor is 0, we assume divisor==2**(2*WORD_BITS) - { - Q[0] = A[2]; - Q[1] = A[3]; - } - else - { - word T[4]; - T[0] = A[0]; T[1] = A[1]; T[2] = A[2]; T[3] = A[3]; - Q[1] = SubatomicDivide(T+1, B[0], B[1]); - Q[0] = SubatomicDivide(T, B[0], B[1]); - -#ifndef NDEBUG - // multiply quotient and divisor and add remainder, make sure it equals dividend - assert(!T[2] && !T[3] && (T[1] < B[1] || (T[1]==B[1] && T[0](T, DWord(A[0], A[1]), DWord(A[2], A[3]), DWord(B[0], B[1])); - Q[0] = q.GetLowHalf(); - Q[1] = q.GetHighHalf(); - -#ifndef NDEBUG - if (B[0] || B[1]) - { - // multiply quotient and divisor and add remainder, make sure it equals dividend - assert(!T[2] && !T[3] && (T[1] < B[1] || (T[1]==B[1] && T[0]= 0) - { - R[N] -= Subtract(R, R, B, N); - Q[1] += (++Q[0]==0); - assert(Q[0] || Q[1]); // no overflow - } -} - -// R[NB] -------- remainder = A%B -// Q[NA-NB+2] --- quotient = A/B -// T[NA+3*(NB+2)] - temp work space -// A[NA] -------- dividend -// B[NB] -------- divisor - -void Divide(word *R, word *Q, word *T, const word *A, size_t NA, const word *B, size_t NB) -{ - assert(NA && NB && NA%2==0 && NB%2==0); - assert(B[NB-1] || B[NB-2]); - assert(NB <= NA); - - // set up temporary work space - word *const TA=T; - word *const TB=T+NA+2; - word *const TP=T+NA+2+NB; - - // copy B into TB and normalize it so that TB has highest bit set to 1 - unsigned shiftWords = (B[NB-1]==0); - TB[0] = TB[NB-1] = 0; - CopyWords(TB+shiftWords, B, NB-shiftWords); - unsigned shiftBits = WORD_BITS - BitPrecision(TB[NB-1]); - assert(shiftBits < WORD_BITS); - ShiftWordsLeftByBits(TB, NB, shiftBits); - - // copy A into TA and normalize it - TA[0] = TA[NA] = TA[NA+1] = 0; - CopyWords(TA+shiftWords, A, NA); - ShiftWordsLeftByBits(TA, NA+2, shiftBits); - - if (TA[NA+1]==0 && TA[NA] <= 1) - { - Q[NA-NB+1] = Q[NA-NB] = 0; - while (TA[NA] || Compare(TA+NA-NB, TB, NB) >= 0) - { - TA[NA] -= Subtract(TA+NA-NB, TA+NA-NB, TB, NB); - ++Q[NA-NB]; - } - } - else - { - NA+=2; - assert(Compare(TA+NA-NB, TB, NB) < 0); - } - - word BT[2]; - BT[0] = TB[NB-2] + 1; - BT[1] = TB[NB-1] + (BT[0]==0); - - // start reducing TA mod TB, 2 words at a time - for (size_t i=NA-2; i>=NB; i-=2) - { - AtomicDivide(Q+i-NB, TA+i-2, BT); - CorrectQuotientEstimate(TA+i-NB, TP, Q+i-NB, TB, NB); - } - - // copy TA into R, and denormalize it - CopyWords(R, TA+shiftWords, NB); - ShiftWordsRightByBits(R, NB, shiftBits); -} - -static inline size_t EvenWordCount(const word *X, size_t N) -{ - while (N && X[N-2]==0 && X[N-1]==0) - N-=2; - return N; -} - -// return k -// R[N] --- result = A^(-1) * 2^k mod M -// T[4*N] - temporary work space -// A[NA] -- number to take inverse of -// M[N] --- modulus - -unsigned int AlmostInverse(word *R, word *T, const word *A, size_t NA, const word *M, size_t N) -{ - assert(NA<=N && N && N%2==0); - - word *b = T; - word *c = T+N; - word *f = T+2*N; - word *g = T+3*N; - size_t bcLen=2, fgLen=EvenWordCount(M, N); - unsigned int k=0; - bool s=false; - - SetWords(T, 0, 3*N); - b[0]=1; - CopyWords(f, A, NA); - CopyWords(g, M, N); - - while (1) - { - word t=f[0]; - while (!t) - { - if (EvenWordCount(f, fgLen)==0) - { - SetWords(R, 0, N); - return 0; - } - - ShiftWordsRightByWords(f, fgLen, 1); - bcLen += 2 * (c[bcLen-1] != 0); - assert(bcLen <= N); - ShiftWordsLeftByWords(c, bcLen, 1); - k+=WORD_BITS; - t=f[0]; - } - - unsigned int i = TrailingZeros(t); - t >>= i; - k += i; - - if (t==1 && f[1]==0 && EvenWordCount(f+2, fgLen-2)==0) - { - if (s) - Subtract(R, M, b, N); - else - CopyWords(R, b, N); - return k; - } - - ShiftWordsRightByBits(f, fgLen, i); - t = ShiftWordsLeftByBits(c, bcLen, i); - c[bcLen] += t; - bcLen += 2 * (t!=0); - assert(bcLen <= N); - - bool swap = Compare(f, g, fgLen)==-1; - ConditionalSwapPointers(swap, f, g); - ConditionalSwapPointers(swap, b, c); - s ^= swap; - - fgLen -= 2 * !(f[fgLen-2] | f[fgLen-1]); - - Subtract(f, f, g, fgLen); - t = Add(b, b, c, bcLen); - b[bcLen] += t; - bcLen += 2*t; - assert(bcLen <= N); - } -} - -// R[N] - result = A/(2^k) mod M -// A[N] - input -// M[N] - modulus - -void DivideByPower2Mod(word *R, const word *A, size_t k, const word *M, size_t N) -{ - CopyWords(R, A, N); - - while (k--) - { - if (R[0]%2==0) - ShiftWordsRightByBits(R, N, 1); - else - { - word carry = Add(R, R, M, N); - ShiftWordsRightByBits(R, N, 1); - R[N-1] += carry<<(WORD_BITS-1); - } - } -} - -// R[N] - result = A*(2^k) mod M -// A[N] - input -// M[N] - modulus - -void MultiplyByPower2Mod(word *R, const word *A, size_t k, const word *M, size_t N) -{ - CopyWords(R, A, N); - - while (k--) - if (ShiftWordsLeftByBits(R, N, 1) || Compare(R, M, N)>=0) - Subtract(R, R, M, N); -} - -// ****************************************************************** - -InitializeInteger::InitializeInteger() -{ - if (!g_pAssignIntToInteger) - { - SetFunctionPointers(); - g_pAssignIntToInteger = AssignIntToInteger; - } -} - -static const unsigned int RoundupSizeTable[] = {2, 2, 2, 4, 4, 8, 8, 8, 8}; - -static inline size_t RoundupSize(size_t n) -{ - if (n<=8) - return RoundupSizeTable[n]; - else if (n<=16) - return 16; - else if (n<=32) - return 32; - else if (n<=64) - return 64; - else return size_t(1) << BitPrecision(n-1); -} - -Integer::Integer() - : reg(2), sign(POSITIVE) -{ - reg[0] = reg[1] = 0; -} - -Integer::Integer(const Integer& t) - : reg(RoundupSize(t.WordCount())), sign(t.sign) -{ - CopyWords(reg, t.reg, reg.size()); -} - -Integer::Integer(Sign s, lword value) - : reg(2), sign(s) -{ - reg[0] = word(value); - reg[1] = word(SafeRightShift(value)); -} - -Integer::Integer(signed long value) - : reg(2) -{ - if (value >= 0) - sign = POSITIVE; - else - { - sign = NEGATIVE; - value = -value; - } - reg[0] = word(value); - reg[1] = word(SafeRightShift((unsigned long)value)); -} - -Integer::Integer(Sign s, word high, word low) - : reg(2), sign(s) -{ - reg[0] = low; - reg[1] = high; -} - -bool Integer::IsConvertableToLong() const -{ - if (ByteCount() > sizeof(long)) - return false; - - unsigned long value = (unsigned long)reg[0]; - value += SafeLeftShift((unsigned long)reg[1]); - - if (sign==POSITIVE) - return (signed long)value >= 0; - else - return -(signed long)value < 0; -} - -signed long Integer::ConvertToLong() const -{ - assert(IsConvertableToLong()); - - unsigned long value = (unsigned long)reg[0]; - value += SafeLeftShift((unsigned long)reg[1]); - return sign==POSITIVE ? value : -(signed long)value; -} - -Integer::Integer(BufferedTransformation &encodedInteger, size_t byteCount, Signedness s) -{ - Decode(encodedInteger, byteCount, s); -} - -Integer::Integer(const byte *encodedInteger, size_t byteCount, Signedness s) -{ - Decode(encodedInteger, byteCount, s); -} - -Integer::Integer(BufferedTransformation &bt) -{ - BERDecode(bt); -} - -Integer::Integer(RandomNumberGenerator &rng, size_t bitcount) -{ - Randomize(rng, bitcount); -} - -Integer::Integer(RandomNumberGenerator &rng, const Integer &min, const Integer &max, RandomNumberType rnType, const Integer &equiv, const Integer &mod) -{ - if (!Randomize(rng, min, max, rnType, equiv, mod)) - throw Integer::RandomNumberNotFound(); -} - -Integer Integer::Power2(size_t e) -{ - Integer r((word)0, BitsToWords(e+1)); - r.SetBit(e); - return r; -} - -template -struct NewInteger -{ - Integer * operator()() const - { - return new Integer(i); - } -}; - -const Integer &Integer::Zero() -{ - return Singleton().Ref(); -} - -const Integer &Integer::One() -{ - return Singleton >().Ref(); -} - -const Integer &Integer::Two() -{ - return Singleton >().Ref(); -} - -bool Integer::operator!() const -{ - return IsNegative() ? false : (reg[0]==0 && WordCount()==0); -} - -Integer& Integer::operator=(const Integer& t) -{ - if (this != &t) - { - if (reg.size() != t.reg.size() || t.reg[t.reg.size()/2] == 0) - reg.New(RoundupSize(t.WordCount())); - CopyWords(reg, t.reg, reg.size()); - sign = t.sign; - } - return *this; -} - -bool Integer::GetBit(size_t n) const -{ - if (n/WORD_BITS >= reg.size()) - return 0; - else - return bool((reg[n/WORD_BITS] >> (n % WORD_BITS)) & 1); -} - -void Integer::SetBit(size_t n, bool value) -{ - if (value) - { - reg.CleanGrow(RoundupSize(BitsToWords(n+1))); - reg[n/WORD_BITS] |= (word(1) << (n%WORD_BITS)); - } - else - { - if (n/WORD_BITS < reg.size()) - reg[n/WORD_BITS] &= ~(word(1) << (n%WORD_BITS)); - } -} - -byte Integer::GetByte(size_t n) const -{ - if (n/WORD_SIZE >= reg.size()) - return 0; - else - return byte(reg[n/WORD_SIZE] >> ((n%WORD_SIZE)*8)); -} - -void Integer::SetByte(size_t n, byte value) -{ - reg.CleanGrow(RoundupSize(BytesToWords(n+1))); - reg[n/WORD_SIZE] &= ~(word(0xff) << 8*(n%WORD_SIZE)); - reg[n/WORD_SIZE] |= (word(value) << 8*(n%WORD_SIZE)); -} - -lword Integer::GetBits(size_t i, size_t n) const -{ - lword v = 0; - assert(n <= sizeof(v)*8); - for (unsigned int j=0; j -static Integer StringToInteger(const T *str) -{ - int radix; - // GCC workaround - // std::char_traits::length() not defined in GCC 3.2 and STLport 4.5.3 - unsigned int length; - for (length = 0; str[length] != 0; length++) {} - - Integer v; - - if (length == 0) - return v; - - switch (str[length-1]) - { - case 'h': - case 'H': - radix=16; - break; - case 'o': - case 'O': - radix=8; - break; - case 'b': - case 'B': - radix=2; - break; - default: - radix=10; - } - - if (length > 2 && str[0] == '0' && str[1] == 'x') - radix = 16; - - for (unsigned i=0; i= '0' && str[i] <= '9') - digit = str[i] - '0'; - else if (str[i] >= 'A' && str[i] <= 'F') - digit = str[i] - 'A' + 10; - else if (str[i] >= 'a' && str[i] <= 'f') - digit = str[i] - 'a' + 10; - else - digit = radix; - - if (digit < radix) - { - v *= radix; - v += digit; - } - } - - if (str[0] == '-') - v.Negate(); - - return v; -} - -Integer::Integer(const char *str) - : reg(2), sign(POSITIVE) -{ - *this = StringToInteger(str); -} - -Integer::Integer(const wchar_t *str) - : reg(2), sign(POSITIVE) -{ - *this = StringToInteger(str); -} - -unsigned int Integer::WordCount() const -{ - return (unsigned int)CountWords(reg, reg.size()); -} - -unsigned int Integer::ByteCount() const -{ - unsigned wordCount = WordCount(); - if (wordCount) - return (wordCount-1)*WORD_SIZE + BytePrecision(reg[wordCount-1]); - else - return 0; -} - -unsigned int Integer::BitCount() const -{ - unsigned wordCount = WordCount(); - if (wordCount) - return (wordCount-1)*WORD_BITS + BitPrecision(reg[wordCount-1]); - else - return 0; -} - -void Integer::Decode(const byte *input, size_t inputLen, Signedness s) -{ - StringStore store(input, inputLen); - Decode(store, inputLen, s); -} - -void Integer::Decode(BufferedTransformation &bt, size_t inputLen, Signedness s) -{ - assert(bt.MaxRetrievable() >= inputLen); - - byte b; - bt.Peek(b); - sign = ((s==SIGNED) && (b & 0x80)) ? NEGATIVE : POSITIVE; - - while (inputLen>0 && (sign==POSITIVE ? b==0 : b==0xff)) - { - bt.Skip(1); - inputLen--; - bt.Peek(b); - } - - reg.CleanNew(RoundupSize(BytesToWords(inputLen))); - - for (size_t i=inputLen; i > 0; i--) - { - bt.Get(b); - reg[(i-1)/WORD_SIZE] |= word(b) << ((i-1)%WORD_SIZE)*8; - } - - if (sign == NEGATIVE) - { - for (size_t i=inputLen; i 0; i--) - bt.Put(GetByte(i-1)); - } - else - { - // take two's complement of *this - Integer temp = Integer::Power2(8*STDMAX((size_t)ByteCount(), outputLen)) + *this; - temp.Encode(bt, outputLen, UNSIGNED); - } -} - -void Integer::DEREncode(BufferedTransformation &bt) const -{ - DERGeneralEncoder enc(bt, INTEGER); - Encode(enc, MinEncodedSize(SIGNED), SIGNED); - enc.MessageEnd(); -} - -void Integer::BERDecode(const byte *input, size_t len) -{ - StringStore store(input, len); - BERDecode(store); -} - -void Integer::BERDecode(BufferedTransformation &bt) -{ - BERGeneralDecoder dec(bt, INTEGER); - if (!dec.IsDefiniteLength() || dec.MaxRetrievable() < dec.RemainingLength()) - BERDecodeError(); - Decode(dec, (size_t)dec.RemainingLength(), SIGNED); - dec.MessageEnd(); -} - -void Integer::DEREncodeAsOctetString(BufferedTransformation &bt, size_t length) const -{ - DERGeneralEncoder enc(bt, OCTET_STRING); - Encode(enc, length); - enc.MessageEnd(); -} - -void Integer::BERDecodeAsOctetString(BufferedTransformation &bt, size_t length) -{ - BERGeneralDecoder dec(bt, OCTET_STRING); - if (!dec.IsDefiniteLength() || dec.RemainingLength() != length) - BERDecodeError(); - Decode(dec, length); - dec.MessageEnd(); -} - -size_t Integer::OpenPGPEncode(byte *output, size_t len) const -{ - ArraySink sink(output, len); - return OpenPGPEncode(sink); -} - -size_t Integer::OpenPGPEncode(BufferedTransformation &bt) const -{ - word16 bitCount = BitCount(); - bt.PutWord16(bitCount); - size_t byteCount = BitsToBytes(bitCount); - Encode(bt, byteCount); - return 2 + byteCount; -} - -void Integer::OpenPGPDecode(const byte *input, size_t len) -{ - StringStore store(input, len); - OpenPGPDecode(store); -} - -void Integer::OpenPGPDecode(BufferedTransformation &bt) -{ - word16 bitCount; - if (bt.GetWord16(bitCount) != 2 || bt.MaxRetrievable() < BitsToBytes(bitCount)) - throw OpenPGPDecodeErr(); - Decode(bt, BitsToBytes(bitCount)); -} - -void Integer::Randomize(RandomNumberGenerator &rng, size_t nbits) -{ - const size_t nbytes = nbits/8 + 1; - SecByteBlock buf(nbytes); - rng.GenerateBlock(buf, nbytes); - if (nbytes) - buf[0] = (byte)Crop(buf[0], nbits % 8); - Decode(buf, nbytes, UNSIGNED); -} - -void Integer::Randomize(RandomNumberGenerator &rng, const Integer &min, const Integer &max) -{ - if (min > max) - throw InvalidArgument("Integer: Min must be no greater than Max"); - - Integer range = max - min; - const unsigned int nbits = range.BitCount(); - - do - { - Randomize(rng, nbits); - } - while (*this > range); - - *this += min; -} - -bool Integer::Randomize(RandomNumberGenerator &rng, const Integer &min, const Integer &max, RandomNumberType rnType, const Integer &equiv, const Integer &mod) -{ - return GenerateRandomNoThrow(rng, MakeParameters("Min", min)("Max", max)("RandomNumberType", rnType)("EquivalentTo", equiv)("Mod", mod)); -} - -class KDF2_RNG : public RandomNumberGenerator -{ -public: - KDF2_RNG(const byte *seed, size_t seedSize) - : m_counter(0), m_counterAndSeed(seedSize + 4) - { - memcpy(m_counterAndSeed + 4, seed, seedSize); - } - - void GenerateBlock(byte *output, size_t size) - { - PutWord(false, BIG_ENDIAN_ORDER, m_counterAndSeed, m_counter); - ++m_counter; - P1363_KDF2::DeriveKey(output, size, m_counterAndSeed, m_counterAndSeed.size(), NULL, 0); - } - -private: - word32 m_counter; - SecByteBlock m_counterAndSeed; -}; - -bool Integer::GenerateRandomNoThrow(RandomNumberGenerator &i_rng, const NameValuePairs ¶ms) -{ - Integer min = params.GetValueWithDefault("Min", Integer::Zero()); - Integer max; - if (!params.GetValue("Max", max)) - { - int bitLength; - if (params.GetIntValue("BitLength", bitLength)) - max = Integer::Power2(bitLength); - else - throw InvalidArgument("Integer: missing Max argument"); - } - if (min > max) - throw InvalidArgument("Integer: Min must be no greater than Max"); - - Integer equiv = params.GetValueWithDefault("EquivalentTo", Integer::Zero()); - Integer mod = params.GetValueWithDefault("Mod", Integer::One()); - - if (equiv.IsNegative() || equiv >= mod) - throw InvalidArgument("Integer: invalid EquivalentTo and/or Mod argument"); - - Integer::RandomNumberType rnType = params.GetValueWithDefault("RandomNumberType", Integer::ANY); - - member_ptr kdf2Rng; - ConstByteArrayParameter seed; - if (params.GetValue(Name::Seed(), seed)) - { - ByteQueue bq; - DERSequenceEncoder seq(bq); - min.DEREncode(seq); - max.DEREncode(seq); - equiv.DEREncode(seq); - mod.DEREncode(seq); - DEREncodeUnsigned(seq, rnType); - DEREncodeOctetString(seq, seed.begin(), seed.size()); - seq.MessageEnd(); - - SecByteBlock finalSeed((size_t)bq.MaxRetrievable()); - bq.Get(finalSeed, finalSeed.size()); - kdf2Rng.reset(new KDF2_RNG(finalSeed.begin(), finalSeed.size())); - } - RandomNumberGenerator &rng = kdf2Rng.get() ? (RandomNumberGenerator &)*kdf2Rng : i_rng; - - switch (rnType) - { - case ANY: - if (mod == One()) - Randomize(rng, min, max); - else - { - Integer min1 = min + (equiv-min)%mod; - if (max < min1) - return false; - Randomize(rng, Zero(), (max - min1) / mod); - *this *= mod; - *this += min1; - } - return true; - - case PRIME: - { - const PrimeSelector *pSelector = params.GetValueWithDefault(Name::PointerToPrimeSelector(), (const PrimeSelector *)NULL); - - int i; - i = 0; - while (1) - { - if (++i==16) - { - // check if there are any suitable primes in [min, max] - Integer first = min; - if (FirstPrime(first, max, equiv, mod, pSelector)) - { - // if there is only one suitable prime, we're done - *this = first; - if (!FirstPrime(first, max, equiv, mod, pSelector)) - return true; - } - else - return false; - } - - Randomize(rng, min, max); - if (FirstPrime(*this, STDMIN(*this+mod*PrimeSearchInterval(max), max), equiv, mod, pSelector)) - return true; - } - } - - default: - throw InvalidArgument("Integer: invalid RandomNumberType argument"); - } -} - -std::istream& operator>>(std::istream& in, Integer &a) -{ - char c; - unsigned int length = 0; - SecBlock str(length + 16); - - std::ws(in); - - do - { - in.read(&c, 1); - str[length++] = c; - if (length >= str.size()) - str.Grow(length + 16); - } - while (in && (c=='-' || c=='x' || (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F') || c=='h' || c=='H' || c=='o' || c=='O' || c==',' || c=='.')); - - if (in.gcount()) - in.putback(c); - str[length-1] = '\0'; - a = Integer(str); - - return in; -} - -std::ostream& operator<<(std::ostream& out, const Integer &a) -{ - // Get relevant conversion specifications from ostream. - long f = out.flags() & std::ios::basefield; // Get base digits. - int base, block; - char suffix; - switch(f) - { - case std::ios::oct : - base = 8; - block = 8; - suffix = 'o'; - break; - case std::ios::hex : - base = 16; - block = 4; - suffix = 'h'; - break; - default : - base = 10; - block = 3; - suffix = '.'; - } - - Integer temp1=a, temp2; - - if (a.IsNegative()) - { - out << '-'; - temp1.Negate(); - } - - if (!a) - out << '0'; - - static const char upper[]="0123456789ABCDEF"; - static const char lower[]="0123456789abcdef"; - - const char* vec = (out.flags() & std::ios::uppercase) ? upper : lower; - unsigned i=0; - SecBlock s(a.BitCount() / (BitPrecision(base)-1) + 1); - - while (!!temp1) - { - word digit; - Integer::Divide(digit, temp2, temp1, base); - s[i++]=vec[digit]; - temp1.swap(temp2); - } - - while (i--) - { - out << s[i]; -// if (i && !(i%block)) -// out << ","; - } - return out << suffix; -} - -Integer& Integer::operator++() -{ - if (NotNegative()) - { - if (Increment(reg, reg.size())) - { - reg.CleanGrow(2*reg.size()); - reg[reg.size()/2]=1; - } - } - else - { - word borrow = Decrement(reg, reg.size()); - assert(!borrow); - if (WordCount()==0) - *this = Zero(); - } - return *this; -} - -Integer& Integer::operator--() -{ - if (IsNegative()) - { - if (Increment(reg, reg.size())) - { - reg.CleanGrow(2*reg.size()); - reg[reg.size()/2]=1; - } - } - else - { - if (Decrement(reg, reg.size())) - *this = -One(); - } - return *this; -} - -void PositiveAdd(Integer &sum, const Integer &a, const Integer& b) -{ - int carry; - if (a.reg.size() == b.reg.size()) - carry = Add(sum.reg, a.reg, b.reg, a.reg.size()); - else if (a.reg.size() > b.reg.size()) - { - carry = Add(sum.reg, a.reg, b.reg, b.reg.size()); - CopyWords(sum.reg+b.reg.size(), a.reg+b.reg.size(), a.reg.size()-b.reg.size()); - carry = Increment(sum.reg+b.reg.size(), a.reg.size()-b.reg.size(), carry); - } - else - { - carry = Add(sum.reg, a.reg, b.reg, a.reg.size()); - CopyWords(sum.reg+a.reg.size(), b.reg+a.reg.size(), b.reg.size()-a.reg.size()); - carry = Increment(sum.reg+a.reg.size(), b.reg.size()-a.reg.size(), carry); - } - - if (carry) - { - sum.reg.CleanGrow(2*sum.reg.size()); - sum.reg[sum.reg.size()/2] = 1; - } - sum.sign = Integer::POSITIVE; -} - -void PositiveSubtract(Integer &diff, const Integer &a, const Integer& b) -{ - unsigned aSize = a.WordCount(); - aSize += aSize%2; - unsigned bSize = b.WordCount(); - bSize += bSize%2; - - if (aSize == bSize) - { - if (Compare(a.reg, b.reg, aSize) >= 0) - { - Subtract(diff.reg, a.reg, b.reg, aSize); - diff.sign = Integer::POSITIVE; - } - else - { - Subtract(diff.reg, b.reg, a.reg, aSize); - diff.sign = Integer::NEGATIVE; - } - } - else if (aSize > bSize) - { - word borrow = Subtract(diff.reg, a.reg, b.reg, bSize); - CopyWords(diff.reg+bSize, a.reg+bSize, aSize-bSize); - borrow = Decrement(diff.reg+bSize, aSize-bSize, borrow); - assert(!borrow); - diff.sign = Integer::POSITIVE; - } - else - { - word borrow = Subtract(diff.reg, b.reg, a.reg, aSize); - CopyWords(diff.reg+aSize, b.reg+aSize, bSize-aSize); - borrow = Decrement(diff.reg+aSize, bSize-aSize, borrow); - assert(!borrow); - diff.sign = Integer::NEGATIVE; - } -} - -// MSVC .NET 2003 workaround -template inline const T& STDMAX2(const T& a, const T& b) -{ - return a < b ? b : a; -} - -Integer Integer::Plus(const Integer& b) const -{ - Integer sum((word)0, STDMAX2(reg.size(), b.reg.size())); - if (NotNegative()) - { - if (b.NotNegative()) - PositiveAdd(sum, *this, b); - else - PositiveSubtract(sum, *this, b); - } - else - { - if (b.NotNegative()) - PositiveSubtract(sum, b, *this); - else - { - PositiveAdd(sum, *this, b); - sum.sign = Integer::NEGATIVE; - } - } - return sum; -} - -Integer& Integer::operator+=(const Integer& t) -{ - reg.CleanGrow(t.reg.size()); - if (NotNegative()) - { - if (t.NotNegative()) - PositiveAdd(*this, *this, t); - else - PositiveSubtract(*this, *this, t); - } - else - { - if (t.NotNegative()) - PositiveSubtract(*this, t, *this); - else - { - PositiveAdd(*this, *this, t); - sign = Integer::NEGATIVE; - } - } - return *this; -} - -Integer Integer::Minus(const Integer& b) const -{ - Integer diff((word)0, STDMAX2(reg.size(), b.reg.size())); - if (NotNegative()) - { - if (b.NotNegative()) - PositiveSubtract(diff, *this, b); - else - PositiveAdd(diff, *this, b); - } - else - { - if (b.NotNegative()) - { - PositiveAdd(diff, *this, b); - diff.sign = Integer::NEGATIVE; - } - else - PositiveSubtract(diff, b, *this); - } - return diff; -} - -Integer& Integer::operator-=(const Integer& t) -{ - reg.CleanGrow(t.reg.size()); - if (NotNegative()) - { - if (t.NotNegative()) - PositiveSubtract(*this, *this, t); - else - PositiveAdd(*this, *this, t); - } - else - { - if (t.NotNegative()) - { - PositiveAdd(*this, *this, t); - sign = Integer::NEGATIVE; - } - else - PositiveSubtract(*this, t, *this); - } - return *this; -} - -Integer& Integer::operator<<=(size_t n) -{ - const size_t wordCount = WordCount(); - const size_t shiftWords = n / WORD_BITS; - const unsigned int shiftBits = (unsigned int)(n % WORD_BITS); - - reg.CleanGrow(RoundupSize(wordCount+BitsToWords(n))); - ShiftWordsLeftByWords(reg, wordCount + shiftWords, shiftWords); - ShiftWordsLeftByBits(reg+shiftWords, wordCount+BitsToWords(shiftBits), shiftBits); - return *this; -} - -Integer& Integer::operator>>=(size_t n) -{ - const size_t wordCount = WordCount(); - const size_t shiftWords = n / WORD_BITS; - const unsigned int shiftBits = (unsigned int)(n % WORD_BITS); - - ShiftWordsRightByWords(reg, wordCount, shiftWords); - if (wordCount > shiftWords) - ShiftWordsRightByBits(reg, wordCount-shiftWords, shiftBits); - if (IsNegative() && WordCount()==0) // avoid -0 - *this = Zero(); - return *this; -} - -void PositiveMultiply(Integer &product, const Integer &a, const Integer &b) -{ - size_t aSize = RoundupSize(a.WordCount()); - size_t bSize = RoundupSize(b.WordCount()); - - product.reg.CleanNew(RoundupSize(aSize+bSize)); - product.sign = Integer::POSITIVE; - - IntegerSecBlock workspace(aSize + bSize); - AsymmetricMultiply(product.reg, workspace, a.reg, aSize, b.reg, bSize); -} - -void Multiply(Integer &product, const Integer &a, const Integer &b) -{ - PositiveMultiply(product, a, b); - - if (a.NotNegative() != b.NotNegative()) - product.Negate(); -} - -Integer Integer::Times(const Integer &b) const -{ - Integer product; - Multiply(product, *this, b); - return product; -} - -/* -void PositiveDivide(Integer &remainder, Integer "ient, - const Integer ÷nd, const Integer &divisor) -{ - remainder.reg.CleanNew(divisor.reg.size()); - remainder.sign = Integer::POSITIVE; - quotient.reg.New(0); - quotient.sign = Integer::POSITIVE; - unsigned i=dividend.BitCount(); - while (i--) - { - word overflow = ShiftWordsLeftByBits(remainder.reg, remainder.reg.size(), 1); - remainder.reg[0] |= dividend[i]; - if (overflow || remainder >= divisor) - { - Subtract(remainder.reg, remainder.reg, divisor.reg, remainder.reg.size()); - quotient.SetBit(i); - } - } -} -*/ - -void PositiveDivide(Integer &remainder, Integer "ient, - const Integer &a, const Integer &b) -{ - unsigned aSize = a.WordCount(); - unsigned bSize = b.WordCount(); - - if (!bSize) - throw Integer::DivideByZero(); - - if (aSize < bSize) - { - remainder = a; - remainder.sign = Integer::POSITIVE; - quotient = Integer::Zero(); - return; - } - - aSize += aSize%2; // round up to next even number - bSize += bSize%2; - - remainder.reg.CleanNew(RoundupSize(bSize)); - remainder.sign = Integer::POSITIVE; - quotient.reg.CleanNew(RoundupSize(aSize-bSize+2)); - quotient.sign = Integer::POSITIVE; - - IntegerSecBlock T(aSize+3*(bSize+2)); - Divide(remainder.reg, quotient.reg, T, a.reg, aSize, b.reg, bSize); -} - -void Integer::Divide(Integer &remainder, Integer "ient, const Integer ÷nd, const Integer &divisor) -{ - PositiveDivide(remainder, quotient, dividend, divisor); - - if (dividend.IsNegative()) - { - quotient.Negate(); - if (remainder.NotZero()) - { - --quotient; - remainder = divisor.AbsoluteValue() - remainder; - } - } - - if (divisor.IsNegative()) - quotient.Negate(); -} - -void Integer::DivideByPowerOf2(Integer &r, Integer &q, const Integer &a, unsigned int n) -{ - q = a; - q >>= n; - - const size_t wordCount = BitsToWords(n); - if (wordCount <= a.WordCount()) - { - r.reg.resize(RoundupSize(wordCount)); - CopyWords(r.reg, a.reg, wordCount); - SetWords(r.reg+wordCount, 0, r.reg.size()-wordCount); - if (n % WORD_BITS != 0) - r.reg[wordCount-1] %= (word(1) << (n % WORD_BITS)); - } - else - { - r.reg.resize(RoundupSize(a.WordCount())); - CopyWords(r.reg, a.reg, r.reg.size()); - } - r.sign = POSITIVE; - - if (a.IsNegative() && r.NotZero()) - { - --q; - r = Power2(n) - r; - } -} - -Integer Integer::DividedBy(const Integer &b) const -{ - Integer remainder, quotient; - Integer::Divide(remainder, quotient, *this, b); - return quotient; -} - -Integer Integer::Modulo(const Integer &b) const -{ - Integer remainder, quotient; - Integer::Divide(remainder, quotient, *this, b); - return remainder; -} - -void Integer::Divide(word &remainder, Integer "ient, const Integer ÷nd, word divisor) -{ - if (!divisor) - throw Integer::DivideByZero(); - - assert(divisor); - - if ((divisor & (divisor-1)) == 0) // divisor is a power of 2 - { - quotient = dividend >> (BitPrecision(divisor)-1); - remainder = dividend.reg[0] & (divisor-1); - return; - } - - unsigned int i = dividend.WordCount(); - quotient.reg.CleanNew(RoundupSize(i)); - remainder = 0; - while (i--) - { - quotient.reg[i] = DWord(dividend.reg[i], remainder) / divisor; - remainder = DWord(dividend.reg[i], remainder) % divisor; - } - - if (dividend.NotNegative()) - quotient.sign = POSITIVE; - else - { - quotient.sign = NEGATIVE; - if (remainder) - { - --quotient; - remainder = divisor - remainder; - } - } -} - -Integer Integer::DividedBy(word b) const -{ - word remainder; - Integer quotient; - Integer::Divide(remainder, quotient, *this, b); - return quotient; -} - -word Integer::Modulo(word divisor) const -{ - if (!divisor) - throw Integer::DivideByZero(); - - assert(divisor); - - word remainder; - - if ((divisor & (divisor-1)) == 0) // divisor is a power of 2 - remainder = reg[0] & (divisor-1); - else - { - unsigned int i = WordCount(); - - if (divisor <= 5) - { - DWord sum(0, 0); - while (i--) - sum += reg[i]; - remainder = sum % divisor; - } - else - { - remainder = 0; - while (i--) - remainder = DWord(reg[i], remainder) % divisor; - } - } - - if (IsNegative() && remainder) - remainder = divisor - remainder; - - return remainder; -} - -void Integer::Negate() -{ - if (!!(*this)) // don't flip sign if *this==0 - sign = Sign(1-sign); -} - -int Integer::PositiveCompare(const Integer& t) const -{ - unsigned size = WordCount(), tSize = t.WordCount(); - - if (size == tSize) - return CryptoPP::Compare(reg, t.reg, size); - else - return size > tSize ? 1 : -1; -} - -int Integer::Compare(const Integer& t) const -{ - if (NotNegative()) - { - if (t.NotNegative()) - return PositiveCompare(t); - else - return 1; - } - else - { - if (t.NotNegative()) - return -1; - else - return -PositiveCompare(t); - } -} - -Integer Integer::SquareRoot() const -{ - if (!IsPositive()) - return Zero(); - - // overestimate square root - Integer x, y = Power2((BitCount()+1)/2); - assert(y*y >= *this); - - do - { - x = y; - y = (x + *this/x) >> 1; - } while (y().Gcd(a, b); -} - -Integer Integer::InverseMod(const Integer &m) const -{ - assert(m.NotNegative()); - - if (IsNegative()) - return Modulo(m).InverseMod(m); - - if (m.IsEven()) - { - if (!m || IsEven()) - return Zero(); // no inverse - if (*this == One()) - return One(); - - Integer u = m.Modulo(*this).InverseMod(*this); - return !u ? Zero() : (m*(*this-u)+1)/(*this); - } - - SecBlock T(m.reg.size() * 4); - Integer r((word)0, m.reg.size()); - unsigned k = AlmostInverse(r.reg, T, reg, reg.size(), m.reg, m.reg.size()); - DivideByPower2Mod(r.reg, r.reg, k, m.reg, m.reg.size()); - return r; -} - -word Integer::InverseMod(word mod) const -{ - word g0 = mod, g1 = *this % mod; - word v0 = 0, v1 = 1; - word y; - - while (g1) - { - if (g1 == 1) - return v1; - y = g0 / g1; - g0 = g0 % g1; - v0 += y * v1; - - if (!g0) - break; - if (g0 == 1) - return mod-v0; - y = g1 / g0; - g1 = g1 % g0; - v1 += y * v0; - } - return 0; -} - -// ******************************************************** - -ModularArithmetic::ModularArithmetic(BufferedTransformation &bt) -{ - BERSequenceDecoder seq(bt); - OID oid(seq); - if (oid != ASN1::prime_field()) - BERDecodeError(); - m_modulus.BERDecode(seq); - seq.MessageEnd(); - m_result.reg.resize(m_modulus.reg.size()); -} - -void ModularArithmetic::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - ASN1::prime_field().DEREncode(seq); - m_modulus.DEREncode(seq); - seq.MessageEnd(); -} - -void ModularArithmetic::DEREncodeElement(BufferedTransformation &out, const Element &a) const -{ - a.DEREncodeAsOctetString(out, MaxElementByteLength()); -} - -void ModularArithmetic::BERDecodeElement(BufferedTransformation &in, Element &a) const -{ - a.BERDecodeAsOctetString(in, MaxElementByteLength()); -} - -const Integer& ModularArithmetic::Half(const Integer &a) const -{ - if (a.reg.size()==m_modulus.reg.size()) - { - CryptoPP::DivideByPower2Mod(m_result.reg.begin(), a.reg, 1, m_modulus.reg, a.reg.size()); - return m_result; - } - else - return m_result1 = (a.IsEven() ? (a >> 1) : ((a+m_modulus) >> 1)); -} - -const Integer& ModularArithmetic::Add(const Integer &a, const Integer &b) const -{ - if (a.reg.size()==m_modulus.reg.size() && b.reg.size()==m_modulus.reg.size()) - { - if (CryptoPP::Add(m_result.reg.begin(), a.reg, b.reg, a.reg.size()) - || Compare(m_result.reg, m_modulus.reg, a.reg.size()) >= 0) - { - CryptoPP::Subtract(m_result.reg.begin(), m_result.reg, m_modulus.reg, a.reg.size()); - } - return m_result; - } - else - { - m_result1 = a+b; - if (m_result1 >= m_modulus) - m_result1 -= m_modulus; - return m_result1; - } -} - -Integer& ModularArithmetic::Accumulate(Integer &a, const Integer &b) const -{ - if (a.reg.size()==m_modulus.reg.size() && b.reg.size()==m_modulus.reg.size()) - { - if (CryptoPP::Add(a.reg, a.reg, b.reg, a.reg.size()) - || Compare(a.reg, m_modulus.reg, a.reg.size()) >= 0) - { - CryptoPP::Subtract(a.reg, a.reg, m_modulus.reg, a.reg.size()); - } - } - else - { - a+=b; - if (a>=m_modulus) - a-=m_modulus; - } - - return a; -} - -const Integer& ModularArithmetic::Subtract(const Integer &a, const Integer &b) const -{ - if (a.reg.size()==m_modulus.reg.size() && b.reg.size()==m_modulus.reg.size()) - { - if (CryptoPP::Subtract(m_result.reg.begin(), a.reg, b.reg, a.reg.size())) - CryptoPP::Add(m_result.reg.begin(), m_result.reg, m_modulus.reg, a.reg.size()); - return m_result; - } - else - { - m_result1 = a-b; - if (m_result1.IsNegative()) - m_result1 += m_modulus; - return m_result1; - } -} - -Integer& ModularArithmetic::Reduce(Integer &a, const Integer &b) const -{ - if (a.reg.size()==m_modulus.reg.size() && b.reg.size()==m_modulus.reg.size()) - { - if (CryptoPP::Subtract(a.reg, a.reg, b.reg, a.reg.size())) - CryptoPP::Add(a.reg, a.reg, m_modulus.reg, a.reg.size()); - } - else - { - a-=b; - if (a.IsNegative()) - a+=m_modulus; - } - - return a; -} - -const Integer& ModularArithmetic::Inverse(const Integer &a) const -{ - if (!a) - return a; - - CopyWords(m_result.reg.begin(), m_modulus.reg, m_modulus.reg.size()); - if (CryptoPP::Subtract(m_result.reg.begin(), m_result.reg, a.reg, a.reg.size())) - Decrement(m_result.reg.begin()+a.reg.size(), m_modulus.reg.size()-a.reg.size()); - - return m_result; -} - -Integer ModularArithmetic::CascadeExponentiate(const Integer &x, const Integer &e1, const Integer &y, const Integer &e2) const -{ - if (m_modulus.IsOdd()) - { - MontgomeryRepresentation dr(m_modulus); - return dr.ConvertOut(dr.CascadeExponentiate(dr.ConvertIn(x), e1, dr.ConvertIn(y), e2)); - } - else - return AbstractRing::CascadeExponentiate(x, e1, y, e2); -} - -void ModularArithmetic::SimultaneousExponentiate(Integer *results, const Integer &base, const Integer *exponents, unsigned int exponentsCount) const -{ - if (m_modulus.IsOdd()) - { - MontgomeryRepresentation dr(m_modulus); - dr.SimultaneousExponentiate(results, dr.ConvertIn(base), exponents, exponentsCount); - for (unsigned int i=0; i::SimultaneousExponentiate(results, base, exponents, exponentsCount); -} - -MontgomeryRepresentation::MontgomeryRepresentation(const Integer &m) // modulus must be odd - : ModularArithmetic(m), - m_u((word)0, m_modulus.reg.size()), - m_workspace(5*m_modulus.reg.size()) -{ - if (!m_modulus.IsOdd()) - throw InvalidArgument("MontgomeryRepresentation: Montgomery representation requires an odd modulus"); - - RecursiveInverseModPower2(m_u.reg, m_workspace, m_modulus.reg, m_modulus.reg.size()); -} - -const Integer& MontgomeryRepresentation::Multiply(const Integer &a, const Integer &b) const -{ - word *const T = m_workspace.begin(); - word *const R = m_result.reg.begin(); - const size_t N = m_modulus.reg.size(); - assert(a.reg.size()<=N && b.reg.size()<=N); - - AsymmetricMultiply(T, T+2*N, a.reg, a.reg.size(), b.reg, b.reg.size()); - SetWords(T+a.reg.size()+b.reg.size(), 0, 2*N-a.reg.size()-b.reg.size()); - MontgomeryReduce(R, T+2*N, T, m_modulus.reg, m_u.reg, N); - return m_result; -} - -const Integer& MontgomeryRepresentation::Square(const Integer &a) const -{ - word *const T = m_workspace.begin(); - word *const R = m_result.reg.begin(); - const size_t N = m_modulus.reg.size(); - assert(a.reg.size()<=N); - - CryptoPP::Square(T, T+2*N, a.reg, a.reg.size()); - SetWords(T+2*a.reg.size(), 0, 2*N-2*a.reg.size()); - MontgomeryReduce(R, T+2*N, T, m_modulus.reg, m_u.reg, N); - return m_result; -} - -Integer MontgomeryRepresentation::ConvertOut(const Integer &a) const -{ - word *const T = m_workspace.begin(); - word *const R = m_result.reg.begin(); - const size_t N = m_modulus.reg.size(); - assert(a.reg.size()<=N); - - CopyWords(T, a.reg, a.reg.size()); - SetWords(T+a.reg.size(), 0, 2*N-a.reg.size()); - MontgomeryReduce(R, T+2*N, T, m_modulus.reg, m_u.reg, N); - return m_result; -} - -const Integer& MontgomeryRepresentation::MultiplicativeInverse(const Integer &a) const -{ -// return (EuclideanMultiplicativeInverse(a, modulus)<<(2*WORD_BITS*modulus.reg.size()))%modulus; - word *const T = m_workspace.begin(); - word *const R = m_result.reg.begin(); - const size_t N = m_modulus.reg.size(); - assert(a.reg.size()<=N); - - CopyWords(T, a.reg, a.reg.size()); - SetWords(T+a.reg.size(), 0, 2*N-a.reg.size()); - MontgomeryReduce(R, T+2*N, T, m_modulus.reg, m_u.reg, N); - unsigned k = AlmostInverse(R, T, R, N, m_modulus.reg, N); - -// cout << "k=" << k << " N*32=" << 32*N << endl; - - if (k>N*WORD_BITS) - DivideByPower2Mod(R, R, k-N*WORD_BITS, m_modulus.reg, N); - else - MultiplyByPower2Mod(R, R, N*WORD_BITS-k, m_modulus.reg, N); - - return m_result; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/integer.h b/CryptoPP/integer.h deleted file mode 100644 index 6d844fa57..000000000 --- a/CryptoPP/integer.h +++ /dev/null @@ -1,420 +0,0 @@ -#ifndef CRYPTOPP_INTEGER_H -#define CRYPTOPP_INTEGER_H - -/** \file */ - -#include "cryptlib.h" -#include "secblock.h" - -#include -#include - -NAMESPACE_BEGIN(CryptoPP) - -struct InitializeInteger // used to initialize static variables -{ - InitializeInteger(); -}; - -typedef SecBlock > IntegerSecBlock; - -//! multiple precision integer and basic arithmetics -/*! This class can represent positive and negative integers - with absolute value less than (256**sizeof(word)) ** (256**sizeof(int)). - \nosubgrouping -*/ -class CRYPTOPP_DLL Integer : private InitializeInteger, public ASN1Object -{ -public: - //! \name ENUMS, EXCEPTIONS, and TYPEDEFS - //@{ - //! division by zero exception - class DivideByZero : public Exception - { - public: - DivideByZero() : Exception(OTHER_ERROR, "Integer: division by zero") {} - }; - - //! - class RandomNumberNotFound : public Exception - { - public: - RandomNumberNotFound() : Exception(OTHER_ERROR, "Integer: no integer satisfies the given parameters") {} - }; - - //! - enum Sign {POSITIVE=0, NEGATIVE=1}; - - //! - enum Signedness { - //! - UNSIGNED, - //! - SIGNED}; - - //! - enum RandomNumberType { - //! - ANY, - //! - PRIME}; - //@} - - //! \name CREATORS - //@{ - //! creates the zero integer - Integer(); - - //! copy constructor - Integer(const Integer& t); - - //! convert from signed long - Integer(signed long value); - - //! convert from lword - Integer(Sign s, lword value); - - //! convert from two words - Integer(Sign s, word highWord, word lowWord); - - //! convert from string - /*! str can be in base 2, 8, 10, or 16. Base is determined by a - case insensitive suffix of 'h', 'o', or 'b'. No suffix means base 10. - */ - explicit Integer(const char *str); - explicit Integer(const wchar_t *str); - - //! convert from big-endian byte array - Integer(const byte *encodedInteger, size_t byteCount, Signedness s=UNSIGNED); - - //! convert from big-endian form stored in a BufferedTransformation - Integer(BufferedTransformation &bt, size_t byteCount, Signedness s=UNSIGNED); - - //! convert from BER encoded byte array stored in a BufferedTransformation object - explicit Integer(BufferedTransformation &bt); - - //! create a random integer - /*! The random integer created is uniformly distributed over [0, 2**bitcount). */ - Integer(RandomNumberGenerator &rng, size_t bitcount); - - //! avoid calling constructors for these frequently used integers - static const Integer & CRYPTOPP_API Zero(); - //! avoid calling constructors for these frequently used integers - static const Integer & CRYPTOPP_API One(); - //! avoid calling constructors for these frequently used integers - static const Integer & CRYPTOPP_API Two(); - - //! create a random integer of special type - /*! Ideally, the random integer created should be uniformly distributed - over {x | min <= x <= max and x is of rnType and x % mod == equiv}. - However the actual distribution may not be uniform because sequential - search is used to find an appropriate number from a random starting - point. - May return (with very small probability) a pseudoprime when a prime - is requested and max > lastSmallPrime*lastSmallPrime (lastSmallPrime - is declared in nbtheory.h). - \throw RandomNumberNotFound if the set is empty. - */ - Integer(RandomNumberGenerator &rng, const Integer &min, const Integer &max, RandomNumberType rnType=ANY, const Integer &equiv=Zero(), const Integer &mod=One()); - - //! return the integer 2**e - static Integer CRYPTOPP_API Power2(size_t e); - //@} - - //! \name ENCODE/DECODE - //@{ - //! minimum number of bytes to encode this integer - /*! MinEncodedSize of 0 is 1 */ - size_t MinEncodedSize(Signedness=UNSIGNED) const; - //! encode in big-endian format - /*! unsigned means encode absolute value, signed means encode two's complement if negative. - if outputLen < MinEncodedSize, the most significant bytes will be dropped - if outputLen > MinEncodedSize, the most significant bytes will be padded - */ - void Encode(byte *output, size_t outputLen, Signedness=UNSIGNED) const; - //! - void Encode(BufferedTransformation &bt, size_t outputLen, Signedness=UNSIGNED) const; - - //! encode using Distinguished Encoding Rules, put result into a BufferedTransformation object - void DEREncode(BufferedTransformation &bt) const; - - //! encode absolute value as big-endian octet string - void DEREncodeAsOctetString(BufferedTransformation &bt, size_t length) const; - - //! encode absolute value in OpenPGP format, return length of output - size_t OpenPGPEncode(byte *output, size_t bufferSize) const; - //! encode absolute value in OpenPGP format, put result into a BufferedTransformation object - size_t OpenPGPEncode(BufferedTransformation &bt) const; - - //! - void Decode(const byte *input, size_t inputLen, Signedness=UNSIGNED); - //! - //* Precondition: bt.MaxRetrievable() >= inputLen - void Decode(BufferedTransformation &bt, size_t inputLen, Signedness=UNSIGNED); - - //! - void BERDecode(const byte *input, size_t inputLen); - //! - void BERDecode(BufferedTransformation &bt); - - //! decode nonnegative value as big-endian octet string - void BERDecodeAsOctetString(BufferedTransformation &bt, size_t length); - - class OpenPGPDecodeErr : public Exception - { - public: - OpenPGPDecodeErr() : Exception(INVALID_DATA_FORMAT, "OpenPGP decode error") {} - }; - - //! - void OpenPGPDecode(const byte *input, size_t inputLen); - //! - void OpenPGPDecode(BufferedTransformation &bt); - //@} - - //! \name ACCESSORS - //@{ - //! return true if *this can be represented as a signed long - bool IsConvertableToLong() const; - //! return equivalent signed long if possible, otherwise undefined - signed long ConvertToLong() const; - - //! number of significant bits = floor(log2(abs(*this))) + 1 - unsigned int BitCount() const; - //! number of significant bytes = ceiling(BitCount()/8) - unsigned int ByteCount() const; - //! number of significant words = ceiling(ByteCount()/sizeof(word)) - unsigned int WordCount() const; - - //! return the i-th bit, i=0 being the least significant bit - bool GetBit(size_t i) const; - //! return the i-th byte - byte GetByte(size_t i) const; - //! return n lowest bits of *this >> i - lword GetBits(size_t i, size_t n) const; - - //! - bool IsZero() const {return !*this;} - //! - bool NotZero() const {return !IsZero();} - //! - bool IsNegative() const {return sign == NEGATIVE;} - //! - bool NotNegative() const {return !IsNegative();} - //! - bool IsPositive() const {return NotNegative() && NotZero();} - //! - bool NotPositive() const {return !IsPositive();} - //! - bool IsEven() const {return GetBit(0) == 0;} - //! - bool IsOdd() const {return GetBit(0) == 1;} - //@} - - //! \name MANIPULATORS - //@{ - //! - Integer& operator=(const Integer& t); - - //! - Integer& operator+=(const Integer& t); - //! - Integer& operator-=(const Integer& t); - //! - Integer& operator*=(const Integer& t) {return *this = Times(t);} - //! - Integer& operator/=(const Integer& t) {return *this = DividedBy(t);} - //! - Integer& operator%=(const Integer& t) {return *this = Modulo(t);} - //! - Integer& operator/=(word t) {return *this = DividedBy(t);} - //! - Integer& operator%=(word t) {return *this = Integer(POSITIVE, 0, Modulo(t));} - - //! - Integer& operator<<=(size_t); - //! - Integer& operator>>=(size_t); - - //! - void Randomize(RandomNumberGenerator &rng, size_t bitcount); - //! - void Randomize(RandomNumberGenerator &rng, const Integer &min, const Integer &max); - //! set this Integer to a random element of {x | min <= x <= max and x is of rnType and x % mod == equiv} - /*! returns false if the set is empty */ - bool Randomize(RandomNumberGenerator &rng, const Integer &min, const Integer &max, RandomNumberType rnType, const Integer &equiv=Zero(), const Integer &mod=One()); - - bool GenerateRandomNoThrow(RandomNumberGenerator &rng, const NameValuePairs ¶ms = g_nullNameValuePairs); - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶ms = g_nullNameValuePairs) - { - if (!GenerateRandomNoThrow(rng, params)) - throw RandomNumberNotFound(); - } - - //! set the n-th bit to value - void SetBit(size_t n, bool value=1); - //! set the n-th byte to value - void SetByte(size_t n, byte value); - - //! - void Negate(); - //! - void SetPositive() {sign = POSITIVE;} - //! - void SetNegative() {if (!!(*this)) sign = NEGATIVE;} - - //! - void swap(Integer &a); - //@} - - //! \name UNARY OPERATORS - //@{ - //! - bool operator!() const; - //! - Integer operator+() const {return *this;} - //! - Integer operator-() const; - //! - Integer& operator++(); - //! - Integer& operator--(); - //! - Integer operator++(int) {Integer temp = *this; ++*this; return temp;} - //! - Integer operator--(int) {Integer temp = *this; --*this; return temp;} - //@} - - //! \name BINARY OPERATORS - //@{ - //! signed comparison - /*! \retval -1 if *this < a - \retval 0 if *this = a - \retval 1 if *this > a - */ - int Compare(const Integer& a) const; - - //! - Integer Plus(const Integer &b) const; - //! - Integer Minus(const Integer &b) const; - //! - Integer Times(const Integer &b) const; - //! - Integer DividedBy(const Integer &b) const; - //! - Integer Modulo(const Integer &b) const; - //! - Integer DividedBy(word b) const; - //! - word Modulo(word b) const; - - //! - Integer operator>>(size_t n) const {return Integer(*this)>>=n;} - //! - Integer operator<<(size_t n) const {return Integer(*this)<<=n;} - //@} - - //! \name OTHER ARITHMETIC FUNCTIONS - //@{ - //! - Integer AbsoluteValue() const; - //! - Integer Doubled() const {return Plus(*this);} - //! - Integer Squared() const {return Times(*this);} - //! extract square root, if negative return 0, else return floor of square root - Integer SquareRoot() const; - //! return whether this integer is a perfect square - bool IsSquare() const; - - //! is 1 or -1 - bool IsUnit() const; - //! return inverse if 1 or -1, otherwise return 0 - Integer MultiplicativeInverse() const; - - //! modular multiplication - CRYPTOPP_DLL friend Integer CRYPTOPP_API a_times_b_mod_c(const Integer &x, const Integer& y, const Integer& m); - //! modular exponentiation - CRYPTOPP_DLL friend Integer CRYPTOPP_API a_exp_b_mod_c(const Integer &x, const Integer& e, const Integer& m); - - //! calculate r and q such that (a == d*q + r) && (0 <= r < abs(d)) - static void CRYPTOPP_API Divide(Integer &r, Integer &q, const Integer &a, const Integer &d); - //! use a faster division algorithm when divisor is short - static void CRYPTOPP_API Divide(word &r, Integer &q, const Integer &a, word d); - - //! returns same result as Divide(r, q, a, Power2(n)), but faster - static void CRYPTOPP_API DivideByPowerOf2(Integer &r, Integer &q, const Integer &a, unsigned int n); - - //! greatest common divisor - static Integer CRYPTOPP_API Gcd(const Integer &a, const Integer &n); - //! calculate multiplicative inverse of *this mod n - Integer InverseMod(const Integer &n) const; - //! - word InverseMod(word n) const; - //@} - - //! \name INPUT/OUTPUT - //@{ - //! - friend CRYPTOPP_DLL std::istream& CRYPTOPP_API operator>>(std::istream& in, Integer &a); - //! - friend CRYPTOPP_DLL std::ostream& CRYPTOPP_API operator<<(std::ostream& out, const Integer &a); - //@} - -private: - friend class ModularArithmetic; - friend class MontgomeryRepresentation; - friend class HalfMontgomeryRepresentation; - - Integer(word value, size_t length); - - int PositiveCompare(const Integer &t) const; - friend void PositiveAdd(Integer &sum, const Integer &a, const Integer &b); - friend void PositiveSubtract(Integer &diff, const Integer &a, const Integer &b); - friend void PositiveMultiply(Integer &product, const Integer &a, const Integer &b); - friend void PositiveDivide(Integer &remainder, Integer "ient, const Integer ÷nd, const Integer &divisor); - - IntegerSecBlock reg; - Sign sign; -}; - -//! -inline bool operator==(const CryptoPP::Integer& a, const CryptoPP::Integer& b) {return a.Compare(b)==0;} -//! -inline bool operator!=(const CryptoPP::Integer& a, const CryptoPP::Integer& b) {return a.Compare(b)!=0;} -//! -inline bool operator> (const CryptoPP::Integer& a, const CryptoPP::Integer& b) {return a.Compare(b)> 0;} -//! -inline bool operator>=(const CryptoPP::Integer& a, const CryptoPP::Integer& b) {return a.Compare(b)>=0;} -//! -inline bool operator< (const CryptoPP::Integer& a, const CryptoPP::Integer& b) {return a.Compare(b)< 0;} -//! -inline bool operator<=(const CryptoPP::Integer& a, const CryptoPP::Integer& b) {return a.Compare(b)<=0;} -//! -inline CryptoPP::Integer operator+(const CryptoPP::Integer &a, const CryptoPP::Integer &b) {return a.Plus(b);} -//! -inline CryptoPP::Integer operator-(const CryptoPP::Integer &a, const CryptoPP::Integer &b) {return a.Minus(b);} -//! -inline CryptoPP::Integer operator*(const CryptoPP::Integer &a, const CryptoPP::Integer &b) {return a.Times(b);} -//! -inline CryptoPP::Integer operator/(const CryptoPP::Integer &a, const CryptoPP::Integer &b) {return a.DividedBy(b);} -//! -inline CryptoPP::Integer operator%(const CryptoPP::Integer &a, const CryptoPP::Integer &b) {return a.Modulo(b);} -//! -inline CryptoPP::Integer operator/(const CryptoPP::Integer &a, CryptoPP::word b) {return a.DividedBy(b);} -//! -inline CryptoPP::word operator%(const CryptoPP::Integer &a, CryptoPP::word b) {return a.Modulo(b);} - -NAMESPACE_END - -#ifndef __BORLANDC__ -NAMESPACE_BEGIN(std) -inline void swap(CryptoPP::Integer &a, CryptoPP::Integer &b) -{ - a.swap(b); -} -NAMESPACE_END -#endif - -#endif diff --git a/CryptoPP/iterhash.cpp b/CryptoPP/iterhash.cpp deleted file mode 100644 index 1e31e9fb3..000000000 --- a/CryptoPP/iterhash.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// iterhash.cpp - written and placed in the public domain by Wei Dai - -#ifndef __GNUC__ -#define CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES -#endif - -#include "iterhash.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -template void IteratedHashBase::Update(const byte *input, size_t len) -{ - HashWordType oldCountLo = m_countLo, oldCountHi = m_countHi; - if ((m_countLo = oldCountLo + HashWordType(len)) < oldCountLo) - m_countHi++; // carry from low to high - m_countHi += (HashWordType)SafeRightShift<8*sizeof(HashWordType)>(len); - if (m_countHi < oldCountHi || SafeRightShift<2*8*sizeof(HashWordType)>(len) != 0) - throw HashInputTooLong(this->AlgorithmName()); - - unsigned int blockSize = this->BlockSize(); - unsigned int num = ModPowerOf2(oldCountLo, blockSize); - T* dataBuf = this->DataBuf(); - byte* data = (byte *)dataBuf; - - if (num != 0) // process left over data - { - if (num+len >= blockSize) - { - memcpy(data+num, input, blockSize-num); - HashBlock(dataBuf); - input += (blockSize-num); - len -= (blockSize-num); - num = 0; - // drop through and do the rest - } - else - { - memcpy(data+num, input, len); - return; - } - } - - // now process the input data in blocks of blockSize bytes and save the leftovers to m_data - if (len >= blockSize) - { - if (input == data) - { - assert(len == blockSize); - HashBlock(dataBuf); - return; - } - else if (IsAligned(input)) - { - size_t leftOver = HashMultipleBlocks((T *)input, len); - input += (len - leftOver); - len = leftOver; - } - else - do - { // copy input first if it's not aligned correctly - memcpy(data, input, blockSize); - HashBlock(dataBuf); - input+=blockSize; - len-=blockSize; - } while (len >= blockSize); - } - - if (len && data != input) - memcpy(data, input, len); -} - -template byte * IteratedHashBase::CreateUpdateSpace(size_t &size) -{ - unsigned int blockSize = this->BlockSize(); - unsigned int num = ModPowerOf2(m_countLo, blockSize); - size = blockSize - num; - return (byte *)DataBuf() + num; -} - -template size_t IteratedHashBase::HashMultipleBlocks(const T *input, size_t length) -{ - unsigned int blockSize = this->BlockSize(); - bool noReverse = NativeByteOrderIs(this->GetByteOrder()); - T* dataBuf = this->DataBuf(); - do - { - if (noReverse) - this->HashEndianCorrectedBlock(input); - else - { - ByteReverse(dataBuf, input, this->BlockSize()); - this->HashEndianCorrectedBlock(dataBuf); - } - - input += blockSize/sizeof(T); - length -= blockSize; - } - while (length >= blockSize); - return length; -} - -template void IteratedHashBase::PadLastBlock(unsigned int lastBlockSize, byte padFirst) -{ - unsigned int blockSize = this->BlockSize(); - unsigned int num = ModPowerOf2(m_countLo, blockSize); - T* dataBuf = this->DataBuf(); - byte* data = (byte *)dataBuf; - data[num++] = padFirst; - if (num <= lastBlockSize) - memset(data+num, 0, lastBlockSize-num); - else - { - memset(data+num, 0, blockSize-num); - HashBlock(dataBuf); - memset(data, 0, lastBlockSize); - } -} - -template void IteratedHashBase::Restart() -{ - m_countLo = m_countHi = 0; - Init(); -} - -template void IteratedHashBase::TruncatedFinal(byte *digest, size_t size) -{ - this->ThrowIfInvalidTruncatedSize(size); - - T* dataBuf = this->DataBuf(); - T* stateBuf = this->StateBuf(); - unsigned int blockSize = this->BlockSize(); - ByteOrder order = this->GetByteOrder(); - - PadLastBlock(blockSize - 2*sizeof(HashWordType)); - dataBuf[blockSize/sizeof(T)-2+order] = ConditionalByteReverse(order, this->GetBitCountLo()); - dataBuf[blockSize/sizeof(T)-1-order] = ConditionalByteReverse(order, this->GetBitCountHi()); - - HashBlock(dataBuf); - - if (IsAligned(digest) && size%sizeof(HashWordType)==0) - ConditionalByteReverse(order, (HashWordType *)digest, stateBuf, size); - else - { - ConditionalByteReverse(order, stateBuf, stateBuf, this->DigestSize()); - memcpy(digest, stateBuf, size); - } - - this->Restart(); // reinit for next use -} - -#ifdef __GNUC__ - template class IteratedHashBase; - template class IteratedHashBase; - - template class IteratedHashBase; - template class IteratedHashBase; -#endif - -NAMESPACE_END diff --git a/CryptoPP/iterhash.h b/CryptoPP/iterhash.h deleted file mode 100644 index cce9e8211..000000000 --- a/CryptoPP/iterhash.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef CRYPTOPP_ITERHASH_H -#define CRYPTOPP_ITERHASH_H - -#include "cryptlib.h" -#include "secblock.h" -#include "misc.h" -#include "simple.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! exception thrown when trying to hash more data than is allowed by a hash function -class CRYPTOPP_DLL HashInputTooLong : public InvalidDataFormat -{ -public: - explicit HashInputTooLong(const std::string &alg) - : InvalidDataFormat("IteratedHashBase: input data exceeds maximum allowed by hash function " + alg) {} -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE IteratedHashBase : public BASE -{ -public: - typedef T HashWordType; - - IteratedHashBase() : m_countLo(0), m_countHi(0) {} - unsigned int OptimalBlockSize() const {return this->BlockSize();} - unsigned int OptimalDataAlignment() const {return GetAlignmentOf();} - void Update(const byte *input, size_t length); - byte * CreateUpdateSpace(size_t &size); - void Restart(); - void TruncatedFinal(byte *digest, size_t size); - -protected: - inline T GetBitCountHi() const {return (m_countLo >> (8*sizeof(T)-3)) + (m_countHi << 3);} - inline T GetBitCountLo() const {return m_countLo << 3;} - - void PadLastBlock(unsigned int lastBlockSize, byte padFirst=0x80); - virtual void Init() =0; - - virtual ByteOrder GetByteOrder() const =0; - virtual void HashEndianCorrectedBlock(const HashWordType *data) =0; - virtual size_t HashMultipleBlocks(const T *input, size_t length); - void HashBlock(const HashWordType *input) {HashMultipleBlocks(input, this->BlockSize());} - - virtual T* DataBuf() =0; - virtual T* StateBuf() =0; - -private: - T m_countLo, m_countHi; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE IteratedHash : public IteratedHashBase -{ -public: - typedef T_Endianness ByteOrderClass; - typedef T_HashWordType HashWordType; - - CRYPTOPP_CONSTANT(BLOCKSIZE = T_BlockSize) - // BCB2006 workaround: can't use BLOCKSIZE here - CRYPTOPP_COMPILE_ASSERT((T_BlockSize & (T_BlockSize - 1)) == 0); // blockSize is a power of 2 - unsigned int BlockSize() const {return T_BlockSize;} - - ByteOrder GetByteOrder() const {return T_Endianness::ToEnum();} - - inline static void CorrectEndianess(HashWordType *out, const HashWordType *in, size_t byteCount) - { - ConditionalByteReverse(T_Endianness::ToEnum(), out, in, byteCount); - } - -protected: - T_HashWordType* DataBuf() {return this->m_data;} - FixedSizeSecBlock m_data; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE IteratedHashWithStaticTransform - : public ClonableImpl, T_Transform> > -{ -public: - CRYPTOPP_CONSTANT(DIGESTSIZE = T_DigestSize ? T_DigestSize : T_StateSize) - unsigned int DigestSize() const {return DIGESTSIZE;}; - -protected: - IteratedHashWithStaticTransform() {this->Init();} - void HashEndianCorrectedBlock(const T_HashWordType *data) {T_Transform::Transform(this->m_state, data);} - void Init() {T_Transform::InitState(this->m_state);} - - T_HashWordType* StateBuf() {return this->m_state;} - FixedSizeAlignedSecBlock m_state; -}; - -#ifndef __GNUC__ - CRYPTOPP_DLL_TEMPLATE_CLASS IteratedHashBase; - CRYPTOPP_STATIC_TEMPLATE_CLASS IteratedHashBase; - - CRYPTOPP_DLL_TEMPLATE_CLASS IteratedHashBase; - CRYPTOPP_STATIC_TEMPLATE_CLASS IteratedHashBase; -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/lubyrack.h b/CryptoPP/lubyrack.h deleted file mode 100644 index e8fd2f748..000000000 --- a/CryptoPP/lubyrack.h +++ /dev/null @@ -1,141 +0,0 @@ -// lubyrack.h - written and placed in the public domain by Wei Dai - -#ifndef CRYPTOPP_LUBYRACK_H -#define CRYPTOPP_LUBYRACK_H - -/** \file */ - -#include "simple.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -template struct DigestSizeDoubleWorkaround // VC60 workaround -{ - CRYPTOPP_CONSTANT(RESULT = 2*T::DIGESTSIZE) -}; - -//! algorithm info -template -struct LR_Info : public VariableKeyLength<16, 0, 2*(INT_MAX/2), 2>, public FixedBlockSize::RESULT> -{ - static std::string StaticAlgorithmName() {return std::string("LR/")+T::StaticAlgorithmName();} -}; - -//! Luby-Rackoff -template -class LR : public LR_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl > - { - public: - // VC60 workaround: have to define these functions within class definition - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms) - { - this->AssertValidKeyLength(length); - - L = length/2; - buffer.New(2*S); - digest.New(S); - key.Assign(userKey, 2*L); - } - - protected: - CRYPTOPP_CONSTANT(S=T::DIGESTSIZE) - unsigned int L; // key length / 2 - SecByteBlock key; - - mutable T hm; - mutable SecByteBlock buffer, digest; - }; - - class CRYPTOPP_NO_VTABLE Enc : public Base - { - public: - -#define KL this->key -#define KR this->key+this->L -#define BL this->buffer -#define BR this->buffer+this->S -#define IL inBlock -#define IR inBlock+this->S -#define OL outBlock -#define OR outBlock+this->S - - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const - { - this->hm.Update(KL, this->L); - this->hm.Update(IL, this->S); - this->hm.Final(BR); - xorbuf(BR, IR, this->S); - - this->hm.Update(KR, this->L); - this->hm.Update(BR, this->S); - this->hm.Final(BL); - xorbuf(BL, IL, this->S); - - this->hm.Update(KL, this->L); - this->hm.Update(BL, this->S); - this->hm.Final(this->digest); - xorbuf(BR, this->digest, this->S); - - this->hm.Update(KR, this->L); - this->hm.Update(OR, this->S); - this->hm.Final(this->digest); - xorbuf(BL, this->digest, this->S); - - if (xorBlock) - xorbuf(outBlock, xorBlock, this->buffer, 2*this->S); - else - memcpy_s(outBlock, 2*this->S, this->buffer, 2*this->S); - } - }; - - class CRYPTOPP_NO_VTABLE Dec : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const - { - this->hm.Update(KR, this->L); - this->hm.Update(IR, this->S); - this->hm.Final(BL); - xorbuf(BL, IL, this->S); - - this->hm.Update(KL, this->L); - this->hm.Update(BL, this->S); - this->hm.Final(BR); - xorbuf(BR, IR, this->S); - - this->hm.Update(KR, this->L); - this->hm.Update(BR, this->S); - this->hm.Final(this->digest); - xorbuf(BL, this->digest, this->S); - - this->hm.Update(KL, this->L); - this->hm.Update(OL, this->S); - this->hm.Final(this->digest); - xorbuf(BR, this->digest, this->S); - - if (xorBlock) - xorbuf(outBlock, xorBlock, this->buffer, 2*this->S); - else - memcpy(outBlock, this->buffer, 2*this->S); - } -#undef KL -#undef KR -#undef BL -#undef BR -#undef IL -#undef IR -#undef OL -#undef OR - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/luc.cpp b/CryptoPP/luc.cpp deleted file mode 100644 index 43cd2ed21..000000000 --- a/CryptoPP/luc.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// luc.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "luc.h" -#include "asn.h" -#include "nbtheory.h" -#include "sha.h" -#include "algparam.h" - -NAMESPACE_BEGIN(CryptoPP) - -void LUC_TestInstantiations() -{ - LUC_HMP::Signer t1; - LUCFunction t2; - InvertibleLUCFunction t3; -} - -void DL_Algorithm_LUC_HMP::Sign(const DL_GroupParameters ¶ms, const Integer &x, const Integer &k, const Integer &e, Integer &r, Integer &s) const -{ - const Integer &q = params.GetSubgroupOrder(); - r = params.ExponentiateBase(k); - s = (k + x*(r+e)) % q; -} - -bool DL_Algorithm_LUC_HMP::Verify(const DL_GroupParameters ¶ms, const DL_PublicKey &publicKey, const Integer &e, const Integer &r, const Integer &s) const -{ - Integer p = params.GetGroupOrder()-1; - const Integer &q = params.GetSubgroupOrder(); - - Integer Vsg = params.ExponentiateBase(s); - Integer Vry = publicKey.ExponentiatePublicElement((r+e)%q); - return (Vsg*Vsg + Vry*Vry + r*r) % p == (Vsg * Vry * r + 4) % p; -} - -Integer DL_BasePrecomputation_LUC::Exponentiate(const DL_GroupPrecomputation &group, const Integer &exponent) const -{ - return Lucas(exponent, m_g, static_cast(group).GetModulus()); -} - -void DL_GroupParameters_LUC::SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const -{ - for (unsigned int i=0; i Integer::One() && m_n.IsOdd(); - pass = pass && m_e > Integer::One() && m_e.IsOdd() && m_e < m_n; - return pass; -} - -bool LUCFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Modulus) - CRYPTOPP_GET_FUNCTION_ENTRY(PublicExponent) - ; -} - -void LUCFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Modulus) - CRYPTOPP_SET_FUNCTION_ENTRY(PublicExponent) - ; -} - -// ***************************************************************************** -// private key operations: - -class LUCPrimeSelector : public PrimeSelector -{ -public: - LUCPrimeSelector(const Integer &e) : m_e(e) {} - bool IsAcceptable(const Integer &candidate) const - { - return RelativelyPrime(m_e, candidate+1) && RelativelyPrime(m_e, candidate-1); - } - Integer m_e; -}; - -void InvertibleLUCFunction::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg) -{ - int modulusSize = 2048; - alg.GetIntValue("ModulusSize", modulusSize) || alg.GetIntValue("KeySize", modulusSize); - - if (modulusSize < 16) - throw InvalidArgument("InvertibleLUCFunction: specified modulus size is too small"); - - m_e = alg.GetValueWithDefault("PublicExponent", Integer(17)); - - if (m_e < 5 || m_e.IsEven()) - throw InvalidArgument("InvertibleLUCFunction: invalid public exponent"); - - LUCPrimeSelector selector(m_e); - AlgorithmParameters primeParam = MakeParametersForTwoPrimesOfEqualSize(modulusSize) - ("PointerToPrimeSelector", selector.GetSelectorPointer()); - m_p.GenerateRandom(rng, primeParam); - m_q.GenerateRandom(rng, primeParam); - - m_n = m_p * m_q; - m_u = m_q.InverseMod(m_p); -} - -void InvertibleLUCFunction::Initialize(RandomNumberGenerator &rng, unsigned int keybits, const Integer &e) -{ - GenerateRandom(rng, MakeParameters("ModulusSize", (int)keybits)("PublicExponent", e)); -} - -void InvertibleLUCFunction::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder seq(bt); - - Integer version(seq); - if (!!version) // make sure version is 0 - BERDecodeError(); - - m_n.BERDecode(seq); - m_e.BERDecode(seq); - m_p.BERDecode(seq); - m_q.BERDecode(seq); - m_u.BERDecode(seq); - seq.MessageEnd(); -} - -void InvertibleLUCFunction::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - - const byte version[] = {INTEGER, 1, 0}; - seq.Put(version, sizeof(version)); - m_n.DEREncode(seq); - m_e.DEREncode(seq); - m_p.DEREncode(seq); - m_q.DEREncode(seq); - m_u.DEREncode(seq); - seq.MessageEnd(); -} - -Integer InvertibleLUCFunction::CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const -{ - // not clear how to do blinding with LUC - DoQuickSanityCheck(); - return InverseLucas(m_e, x, m_q, m_p, m_u); -} - -bool InvertibleLUCFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = LUCFunction::Validate(rng, level); - pass = pass && m_p > Integer::One() && m_p.IsOdd() && m_p < m_n; - pass = pass && m_q > Integer::One() && m_q.IsOdd() && m_q < m_n; - pass = pass && m_u.IsPositive() && m_u < m_p; - if (level >= 1) - { - pass = pass && m_p * m_q == m_n; - pass = pass && RelativelyPrime(m_e, m_p+1); - pass = pass && RelativelyPrime(m_e, m_p-1); - pass = pass && RelativelyPrime(m_e, m_q+1); - pass = pass && RelativelyPrime(m_e, m_q-1); - pass = pass && m_u * m_q % m_p == 1; - } - if (level >= 2) - pass = pass && VerifyPrime(rng, m_p, level-2) && VerifyPrime(rng, m_q, level-2); - return pass; -} - -bool InvertibleLUCFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_GET_FUNCTION_ENTRY(Prime2) - CRYPTOPP_GET_FUNCTION_ENTRY(MultiplicativeInverseOfPrime2ModPrime1) - ; -} - -void InvertibleLUCFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime2) - CRYPTOPP_SET_FUNCTION_ENTRY(MultiplicativeInverseOfPrime2ModPrime1) - ; -} - -NAMESPACE_END diff --git a/CryptoPP/luc.h b/CryptoPP/luc.h deleted file mode 100644 index 730776d57..000000000 --- a/CryptoPP/luc.h +++ /dev/null @@ -1,236 +0,0 @@ -#ifndef CRYPTOPP_LUC_H -#define CRYPTOPP_LUC_H - -/** \file -*/ - -#include "pkcspad.h" -#include "oaep.h" -#include "integer.h" -#include "dh.h" - -#include - -NAMESPACE_BEGIN(CryptoPP) - -//! The LUC function. -/*! This class is here for historical and pedagogical interest. It has no - practical advantages over other trapdoor functions and probably shouldn't - be used in production software. The discrete log based LUC schemes - defined later in this .h file may be of more practical interest. -*/ -class LUCFunction : public TrapdoorFunction, public PublicKey -{ - typedef LUCFunction ThisClass; - -public: - void Initialize(const Integer &n, const Integer &e) - {m_n = n; m_e = e;} - - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - Integer ApplyFunction(const Integer &x) const; - Integer PreimageBound() const {return m_n;} - Integer ImageBound() const {return m_n;} - - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - - // non-derived interface - const Integer & GetModulus() const {return m_n;} - const Integer & GetPublicExponent() const {return m_e;} - - void SetModulus(const Integer &n) {m_n = n;} - void SetPublicExponent(const Integer &e) {m_e = e;} - -protected: - Integer m_n, m_e; -}; - -//! _ -class InvertibleLUCFunction : public LUCFunction, public TrapdoorFunctionInverse, public PrivateKey -{ - typedef InvertibleLUCFunction ThisClass; - -public: - void Initialize(RandomNumberGenerator &rng, unsigned int modulusBits, const Integer &eStart=17); - void Initialize(const Integer &n, const Integer &e, const Integer &p, const Integer &q, const Integer &u) - {m_n = n; m_e = e; m_p = p; m_q = q; m_u = u;} - - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - Integer CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const; - - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - /*! parameters: (ModulusSize, PublicExponent (default 17)) */ - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg); - - // non-derived interface - const Integer& GetPrime1() const {return m_p;} - const Integer& GetPrime2() const {return m_q;} - const Integer& GetMultiplicativeInverseOfPrime2ModPrime1() const {return m_u;} - - void SetPrime1(const Integer &p) {m_p = p;} - void SetPrime2(const Integer &q) {m_q = q;} - void SetMultiplicativeInverseOfPrime2ModPrime1(const Integer &u) {m_u = u;} - -protected: - Integer m_p, m_q, m_u; -}; - -struct LUC -{ - static std::string StaticAlgorithmName() {return "LUC";} - typedef LUCFunction PublicKey; - typedef InvertibleLUCFunction PrivateKey; -}; - -//! LUC cryptosystem -template -struct LUCES : public TF_ES -{ -}; - -//! LUC signature scheme with appendix -template -struct LUCSS : public TF_SS -{ -}; - -// analagous to the RSA schemes defined in PKCS #1 v2.0 -typedef LUCES >::Decryptor LUCES_OAEP_SHA_Decryptor; -typedef LUCES >::Encryptor LUCES_OAEP_SHA_Encryptor; - -typedef LUCSS::Signer LUCSSA_PKCS1v15_SHA_Signer; -typedef LUCSS::Verifier LUCSSA_PKCS1v15_SHA_Verifier; - -// ******************************************************** - -// no actual precomputation -class DL_GroupPrecomputation_LUC : public DL_GroupPrecomputation -{ -public: - const AbstractGroup & GetGroup() const {assert(false); throw 0;} - Element BERDecodeElement(BufferedTransformation &bt) const {return Integer(bt);} - void DEREncodeElement(BufferedTransformation &bt, const Element &v) const {v.DEREncode(bt);} - - // non-inherited - void SetModulus(const Integer &v) {m_p = v;} - const Integer & GetModulus() const {return m_p;} - -private: - Integer m_p; -}; - -//! _ -class DL_BasePrecomputation_LUC : public DL_FixedBasePrecomputation -{ -public: - // DL_FixedBasePrecomputation - bool IsInitialized() const {return m_g.NotZero();} - void SetBase(const DL_GroupPrecomputation &group, const Integer &base) {m_g = base;} - const Integer & GetBase(const DL_GroupPrecomputation &group) const {return m_g;} - void Precompute(const DL_GroupPrecomputation &group, unsigned int maxExpBits, unsigned int storage) {} - void Load(const DL_GroupPrecomputation &group, BufferedTransformation &storedPrecomputation) {} - void Save(const DL_GroupPrecomputation &group, BufferedTransformation &storedPrecomputation) const {} - Integer Exponentiate(const DL_GroupPrecomputation &group, const Integer &exponent) const; - Integer CascadeExponentiate(const DL_GroupPrecomputation &group, const Integer &exponent, const DL_FixedBasePrecomputation &pc2, const Integer &exponent2) const - {throw NotImplemented("DL_BasePrecomputation_LUC: CascadeExponentiate not implemented");} // shouldn't be called - -private: - Integer m_g; -}; - -//! _ -class DL_GroupParameters_LUC : public DL_GroupParameters_IntegerBasedImpl -{ -public: - // DL_GroupParameters - bool IsIdentity(const Integer &element) const {return element == Integer::Two();} - void SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const; - Element MultiplyElements(const Element &a, const Element &b) const - {throw NotImplemented("LUC_GroupParameters: MultiplyElements can not be implemented");} - Element CascadeExponentiate(const Element &element1, const Integer &exponent1, const Element &element2, const Integer &exponent2) const - {throw NotImplemented("LUC_GroupParameters: MultiplyElements can not be implemented");} - - // NameValuePairs interface - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const - { - return GetValueHelper(this, name, valueType, pValue).Assignable(); - } - -private: - int GetFieldType() const {return 2;} -}; - -//! _ -class DL_GroupParameters_LUC_DefaultSafePrime : public DL_GroupParameters_LUC -{ -public: - typedef NoCofactorMultiplication DefaultCofactorOption; - -protected: - unsigned int GetDefaultSubgroupOrderSize(unsigned int modulusSize) const {return modulusSize-1;} -}; - -//! _ -class DL_Algorithm_LUC_HMP : public DL_ElgamalLikeSignatureAlgorithm -{ -public: - static const char * StaticAlgorithmName() {return "LUC-HMP";} - - void Sign(const DL_GroupParameters ¶ms, const Integer &x, const Integer &k, const Integer &e, Integer &r, Integer &s) const; - bool Verify(const DL_GroupParameters ¶ms, const DL_PublicKey &publicKey, const Integer &e, const Integer &r, const Integer &s) const; - - size_t RLen(const DL_GroupParameters ¶ms) const - {return params.GetGroupOrder().ByteCount();} -}; - -//! _ -struct DL_SignatureKeys_LUC -{ - typedef DL_GroupParameters_LUC GroupParameters; - typedef DL_PublicKey_GFP PublicKey; - typedef DL_PrivateKey_GFP PrivateKey; -}; - -//! LUC-HMP, based on "Digital signature schemes based on Lucas functions" by Patrick Horster, Markus Michels, Holger Petersen -template -struct LUC_HMP : public DL_SS -{ -}; - -//! _ -struct DL_CryptoKeys_LUC -{ - typedef DL_GroupParameters_LUC_DefaultSafePrime GroupParameters; - typedef DL_PublicKey_GFP PublicKey; - typedef DL_PrivateKey_GFP PrivateKey; -}; - -//! LUC-IES -template -struct LUC_IES - : public DL_ES< - DL_CryptoKeys_LUC, - DL_KeyAgreementAlgorithm_DH, - DL_KeyDerivationAlgorithm_P1363 >, - DL_EncryptionAlgorithm_Xor, DHAES_MODE>, - LUC_IES<> > -{ - static std::string StaticAlgorithmName() {return "LUC-IES";} // non-standard name -}; - -// ******************************************************** - -//! LUC-DH -typedef DH_Domain LUC_DH; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/md2.cpp b/CryptoPP/md2.cpp deleted file mode 100644 index 41f714b59..000000000 --- a/CryptoPP/md2.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// md2.cpp - modified by Wei Dai from Andrew M. Kuchling's md2.c -// The original code and all modifications are in the public domain. - -// This is the original introductory comment: - -/* - * md2.c : MD2 hash algorithm. - * - * Part of the Python Cryptography Toolkit, version 1.1 - * - * Distribute and use freely; there are no restrictions on further - * dissemination and usage except those imposed by the laws of your - * country of residence. - * - */ - -#include "pch.h" -#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 -#include "md2.h" - -NAMESPACE_BEGIN(CryptoPP) -namespace Weak1 { - -MD2::MD2() - : m_X(48), m_C(16), m_buf(16) -{ - Init(); -} - -void MD2::Init() -{ - memset(m_X, 0, 48); - memset(m_C, 0, 16); - memset(m_buf, 0, 16); - m_count = 0; -} - -void MD2::Update(const byte *buf, size_t len) -{ - static const byte S[256] = { - 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, - 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, - 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, - 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, - 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, - 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, - 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, - 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, - 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, - 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, - 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, - 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, - 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, - 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, - 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, - 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, - 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, - 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 - }; - - while (len) - { - unsigned int L = UnsignedMin(16U-m_count, len); - memcpy(m_buf+m_count, buf, L); - m_count+=L; - buf+=L; - len-=L; - if (m_count==16) - { - byte t; - int i,j; - - m_count=0; - memcpy(m_X+16, m_buf, 16); - t=m_C[15]; - for(i=0; i<16; i++) - { - m_X[32+i]=m_X[16+i]^m_X[i]; - t=m_C[i]^=S[m_buf[i]^t]; - } - - t=0; - for(i=0; i<18; i++) - { - for(j=0; j<48; j+=8) - { - t=m_X[j+0]^=S[t]; - t=m_X[j+1]^=S[t]; - t=m_X[j+2]^=S[t]; - t=m_X[j+3]^=S[t]; - t=m_X[j+4]^=S[t]; - t=m_X[j+5]^=S[t]; - t=m_X[j+6]^=S[t]; - t=m_X[j+7]^=S[t]; - } - t=(t+i) & 0xFF; - } - } - } -} - -void MD2::TruncatedFinal(byte *hash, size_t size) -{ - ThrowIfInvalidTruncatedSize(size); - - byte padding[16]; - word32 padlen; - unsigned int i; - - padlen= 16-m_count; - for(i=0; iMD2 -class MD2 : public HashTransformation -{ -public: - MD2(); - void Update(const byte *input, size_t length); - void TruncatedFinal(byte *hash, size_t size); - unsigned int DigestSize() const {return DIGESTSIZE;} - unsigned int BlockSize() const {return BLOCKSIZE;} - static const char * StaticAlgorithmName() {return "MD2";} - - CRYPTOPP_CONSTANT(DIGESTSIZE = 16) - CRYPTOPP_CONSTANT(BLOCKSIZE = 16) - -private: - void Transform(); - void Init(); - SecByteBlock m_X, m_C, m_buf; - unsigned int m_count; -}; - -} -#if CRYPTOPP_ENABLE_NAMESPACE_WEAK >= 1 -namespace Weak {using namespace Weak1;} // import Weak1 into CryptoPP::Weak -#else -using namespace Weak1; // import Weak1 into CryptoPP with warning -#ifdef __GNUC__ -#warning "You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning." -#else -#pragma message("You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning.") -#endif -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/md4.cpp b/CryptoPP/md4.cpp deleted file mode 100644 index 9ed639cb9..000000000 --- a/CryptoPP/md4.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// md4.cpp - modified by Wei Dai from Andrew M. Kuchling's md4.c -// The original code and all modifications are in the public domain. - -// This is the original introductory comment: - -/* - * md4.c : MD4 hash algorithm. - * - * Part of the Python Cryptography Toolkit, version 1.1 - * - * Distribute and use freely; there are no restrictions on further - * dissemination and usage except those imposed by the laws of your - * country of residence. - * - */ - -#include "pch.h" -#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 -#include "md4.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) -namespace Weak1 { - -void MD4::InitState(HashWordType *state) -{ - state[0] = 0x67452301L; - state[1] = 0xefcdab89L; - state[2] = 0x98badcfeL; - state[3] = 0x10325476L; -} - -void MD4::Transform (word32 *digest, const word32 *in) -{ -// #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) - - word32 A, B, C, D; - - A=digest[0]; - B=digest[1]; - C=digest[2]; - D=digest[3]; - -#define function(a,b,c,d,k,s) a=rotlFixed(a+F(b,c,d)+in[k],s); - function(A,B,C,D, 0, 3); - function(D,A,B,C, 1, 7); - function(C,D,A,B, 2,11); - function(B,C,D,A, 3,19); - function(A,B,C,D, 4, 3); - function(D,A,B,C, 5, 7); - function(C,D,A,B, 6,11); - function(B,C,D,A, 7,19); - function(A,B,C,D, 8, 3); - function(D,A,B,C, 9, 7); - function(C,D,A,B,10,11); - function(B,C,D,A,11,19); - function(A,B,C,D,12, 3); - function(D,A,B,C,13, 7); - function(C,D,A,B,14,11); - function(B,C,D,A,15,19); - -#undef function -#define function(a,b,c,d,k,s) a=rotlFixed(a+G(b,c,d)+in[k]+0x5a827999,s); - function(A,B,C,D, 0, 3); - function(D,A,B,C, 4, 5); - function(C,D,A,B, 8, 9); - function(B,C,D,A,12,13); - function(A,B,C,D, 1, 3); - function(D,A,B,C, 5, 5); - function(C,D,A,B, 9, 9); - function(B,C,D,A,13,13); - function(A,B,C,D, 2, 3); - function(D,A,B,C, 6, 5); - function(C,D,A,B,10, 9); - function(B,C,D,A,14,13); - function(A,B,C,D, 3, 3); - function(D,A,B,C, 7, 5); - function(C,D,A,B,11, 9); - function(B,C,D,A,15,13); - -#undef function -#define function(a,b,c,d,k,s) a=rotlFixed(a+H(b,c,d)+in[k]+0x6ed9eba1,s); - function(A,B,C,D, 0, 3); - function(D,A,B,C, 8, 9); - function(C,D,A,B, 4,11); - function(B,C,D,A,12,15); - function(A,B,C,D, 2, 3); - function(D,A,B,C,10, 9); - function(C,D,A,B, 6,11); - function(B,C,D,A,14,15); - function(A,B,C,D, 1, 3); - function(D,A,B,C, 9, 9); - function(C,D,A,B, 5,11); - function(B,C,D,A,13,15); - function(A,B,C,D, 3, 3); - function(D,A,B,C,11, 9); - function(C,D,A,B, 7,11); - function(B,C,D,A,15,15); - - digest[0]+=A; - digest[1]+=B; - digest[2]+=C; - digest[3]+=D; -} - -} -NAMESPACE_END diff --git a/CryptoPP/md4.h b/CryptoPP/md4.h deleted file mode 100644 index 53387003c..000000000 --- a/CryptoPP/md4.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CRYPTOPP_MD4_H -#define CRYPTOPP_MD4_H - -#include "iterhash.h" - -NAMESPACE_BEGIN(CryptoPP) - -namespace Weak1 { - -//! MD4 -/*! \warning MD4 is considered insecure, and should not be used - unless you absolutely need it for compatibility. */ -class MD4 : public IteratedHashWithStaticTransform -{ -public: - static void InitState(HashWordType *state); - static void Transform(word32 *digest, const word32 *data); - static const char *StaticAlgorithmName() {return "MD4";} -}; - -} -#if CRYPTOPP_ENABLE_NAMESPACE_WEAK >= 1 -namespace Weak {using namespace Weak1;} // import Weak1 into CryptoPP::Weak -#else -using namespace Weak1; // import Weak1 into CryptoPP with warning -#ifdef __GNUC__ -#warning "You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning." -#else -#pragma message("You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning.") -#endif -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/md5.cpp b/CryptoPP/md5.cpp deleted file mode 100644 index a52297816..000000000 --- a/CryptoPP/md5.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// md5.cpp - modified by Wei Dai from Colin Plumb's public domain md5.c -// any modifications are placed in the public domain - -#include "pch.h" -#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 -#include "md5.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) -namespace Weak1 { - -void MD5_TestInstantiations() -{ - MD5 x; -} - -void MD5::InitState(HashWordType *state) -{ - state[0] = 0x67452301L; - state[1] = 0xefcdab89L; - state[2] = 0x98badcfeL; - state[3] = 0x10325476L; -} - -void MD5::Transform (word32 *digest, const word32 *in) -{ -// #define F1(x, y, z) (x & y | ~x & z) -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -#define MD5STEP(f, w, x, y, z, data, s) \ - w = rotlFixed(w + f(x, y, z) + data, s) + x - - word32 a, b, c, d; - - a=digest[0]; - b=digest[1]; - c=digest[2]; - d=digest[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - digest[0]+=a; - digest[1]+=b; - digest[2]+=c; - digest[3]+=d; -} - -} -NAMESPACE_END diff --git a/CryptoPP/md5.h b/CryptoPP/md5.h deleted file mode 100644 index 73ec5326c..000000000 --- a/CryptoPP/md5.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef CRYPTOPP_MD5_H -#define CRYPTOPP_MD5_H - -#include "iterhash.h" - -NAMESPACE_BEGIN(CryptoPP) - -namespace Weak1 { - -//! MD5 -class MD5 : public IteratedHashWithStaticTransform -{ -public: - static void InitState(HashWordType *state); - static void Transform(word32 *digest, const word32 *data); - static const char * StaticAlgorithmName() {return "MD5";} -}; - -} -#if CRYPTOPP_ENABLE_NAMESPACE_WEAK >= 1 -namespace Weak {using namespace Weak1;} // import Weak1 into CryptoPP::Weak -#else -using namespace Weak1; // import Weak1 into CryptoPP with warning -#ifdef __GNUC__ -#warning "You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning." -#else -#pragma message("You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning.") -#endif -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/mdc.h b/CryptoPP/mdc.h deleted file mode 100644 index cc90cdc45..000000000 --- a/CryptoPP/mdc.h +++ /dev/null @@ -1,72 +0,0 @@ - // mdc.h - written and placed in the public domain by Wei Dai - -#ifndef CRYPTOPP_MDC_H -#define CRYPTOPP_MDC_H - -/** \file -*/ - -#include "seckey.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -template -struct MDC_Info : public FixedBlockSize, public FixedKeyLength -{ - static std::string StaticAlgorithmName() {return std::string("MDC/")+T::StaticAlgorithmName();} -}; - -//! MDC -/*! a construction by Peter Gutmann to turn an iterated hash function into a PRF */ -template -class MDC : public MDC_Info -{ - class CRYPTOPP_NO_VTABLE Enc : public BlockCipherImpl > - { - typedef typename T::HashWordType HashWordType; - - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms) - { - this->AssertValidKeyLength(length); - memcpy_s(m_key, m_key.size(), userKey, this->KEYLENGTH); - T::CorrectEndianess(Key(), Key(), this->KEYLENGTH); - } - - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const - { - T::CorrectEndianess(Buffer(), (HashWordType *)inBlock, this->BLOCKSIZE); - T::Transform(Buffer(), Key()); - if (xorBlock) - { - T::CorrectEndianess(Buffer(), Buffer(), this->BLOCKSIZE); - xorbuf(outBlock, xorBlock, m_buffer, this->BLOCKSIZE); - } - else - T::CorrectEndianess((HashWordType *)outBlock, Buffer(), this->BLOCKSIZE); - } - - bool IsPermutation() const {return false;} - - unsigned int OptimalDataAlignment() const {return sizeof(HashWordType);} - - private: - HashWordType *Key() {return (HashWordType *)m_key.data();} - const HashWordType *Key() const {return (const HashWordType *)m_key.data();} - HashWordType *Buffer() const {return (HashWordType *)m_buffer.data();} - - // VC60 workaround: bug triggered if using FixedSizeAllocatorWithCleanup - FixedSizeSecBlock::KEYLENGTH, AllocatorWithCleanup > m_key; - mutable FixedSizeSecBlock::BLOCKSIZE, AllocatorWithCleanup > m_buffer; - }; - -public: - //! use BlockCipher interface - typedef BlockCipherFinal Encryption; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/misc.cpp b/CryptoPP/misc.cpp deleted file mode 100644 index 93760e3a3..000000000 --- a/CryptoPP/misc.cpp +++ /dev/null @@ -1,189 +0,0 @@ -// misc.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "misc.h" -#include "words.h" -#include - -#if defined(CRYPTOPP_MEMALIGN_AVAILABLE) || defined(CRYPTOPP_MM_MALLOC_AVAILABLE) || defined(QNX) -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -void xorbuf(byte *buf, const byte *mask, size_t count) -{ - size_t i; - - if (IsAligned(buf) && IsAligned(mask)) - { - if (!CRYPTOPP_BOOL_SLOW_WORD64 && IsAligned(buf) && IsAligned(mask)) - { - for (i=0; i(output) && IsAligned(input) && IsAligned(mask)) - { - if (!CRYPTOPP_BOOL_SLOW_WORD64 && IsAligned(output) && IsAligned(input) && IsAligned(mask)) - { - for (i=0; i(buf) && IsAligned(mask)) - { - word32 acc32 = 0; - if (!CRYPTOPP_BOOL_SLOW_WORD64 && IsAligned(buf) && IsAligned(mask)) - { - word64 acc64 = 0; - for (i=0; i>32); - } - - for (i=0; i>8) | byte(acc32>>16) | byte(acc32>>24); - } - - for (i=0; i // for memcpy and memmove - -#ifdef _MSC_VER - #if _MSC_VER >= 1400 - // VC2005 workaround: disable declarations that conflict with winnt.h - #define _interlockedbittestandset CRYPTOPP_DISABLED_INTRINSIC_1 - #define _interlockedbittestandreset CRYPTOPP_DISABLED_INTRINSIC_2 - #define _interlockedbittestandset64 CRYPTOPP_DISABLED_INTRINSIC_3 - #define _interlockedbittestandreset64 CRYPTOPP_DISABLED_INTRINSIC_4 - #include - #undef _interlockedbittestandset - #undef _interlockedbittestandreset - #undef _interlockedbittestandset64 - #undef _interlockedbittestandreset64 - #define CRYPTOPP_FAST_ROTATE(x) 1 - #elif _MSC_VER >= 1300 - #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32 | (x) == 64) - #else - #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) - #endif -#elif (defined(__MWERKS__) && TARGET_CPU_PPC) || \ - (defined(__GNUC__) && (defined(_ARCH_PWR2) || defined(_ARCH_PWR) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_ARCH_COM))) - #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) -#elif defined(__GNUC__) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86) // depend on GCC's peephole optimization to generate rotate instructions - #define CRYPTOPP_FAST_ROTATE(x) 1 -#else - #define CRYPTOPP_FAST_ROTATE(x) 0 -#endif - -#ifdef __BORLANDC__ -#include -#endif - -#if defined(__GNUC__) && defined(__linux__) -#define CRYPTOPP_BYTESWAP_AVAILABLE -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -// ************** compile-time assertion *************** - -template -struct CompileAssert -{ - static char dummy[2*b-1]; -}; - -#define CRYPTOPP_COMPILE_ASSERT(assertion) CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, __LINE__) -#if defined(CRYPTOPP_EXPORTS) || defined(CRYPTOPP_IMPORTS) -#define CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, instance) -#else -#define CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, instance) static CompileAssert<(assertion)> CRYPTOPP_ASSERT_JOIN(cryptopp_assert_, instance) -#endif -#define CRYPTOPP_ASSERT_JOIN(X, Y) CRYPTOPP_DO_ASSERT_JOIN(X, Y) -#define CRYPTOPP_DO_ASSERT_JOIN(X, Y) X##Y - -// ************** misc classes *************** - -class CRYPTOPP_DLL Empty -{ -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE TwoBases : public BASE1, public BASE2 -{ -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE ThreeBases : public BASE1, public BASE2, public BASE3 -{ -}; - -template -class ObjectHolder -{ -protected: - T m_object; -}; - -class NotCopyable -{ -public: - NotCopyable() {} -private: - NotCopyable(const NotCopyable &); - void operator=(const NotCopyable &); -}; - -template -struct NewObject -{ - T* operator()() const {return new T;} -}; - -/*! This function safely initializes a static object in a multithreaded environment without using locks (for portability). - Note that if two threads call Ref() at the same time, they may get back different references, and one object - may end up being memory leaked. This is by design. -*/ -template , int instance=0> -class Singleton -{ -public: - Singleton(F objectFactory = F()) : m_objectFactory(objectFactory) {} - - // prevent this function from being inlined - CRYPTOPP_NOINLINE const T & Ref(CRYPTOPP_NOINLINE_DOTDOTDOT) const; - -private: - F m_objectFactory; -}; - -template -const T & Singleton::Ref(CRYPTOPP_NOINLINE_DOTDOTDOT) const -{ - static volatile simple_ptr s_pObject; - T *p = s_pObject.m_p; - - if (p) - return *p; - - T *newObject = m_objectFactory(); - p = s_pObject.m_p; - - if (p) - { - delete newObject; - return *p; - } - - s_pObject.m_p = newObject; - return *newObject; -} - -// ************** misc functions *************** - -#if (!__STDC_WANT_SECURE_LIB__ && !defined(_MEMORY_S_DEFINED)) -inline void memcpy_s(void *dest, size_t sizeInBytes, const void *src, size_t count) -{ - if (count > sizeInBytes) - throw InvalidArgument("memcpy_s: buffer overflow"); - memcpy(dest, src, count); -} - -inline void memmove_s(void *dest, size_t sizeInBytes, const void *src, size_t count) -{ - if (count > sizeInBytes) - throw InvalidArgument("memmove_s: buffer overflow"); - memmove(dest, src, count); -} - -#if __BORLANDC__ >= 0x620 -// C++Builder 2010 workaround: can't use std::memcpy_s because it doesn't allow 0 lengths -#define memcpy_s CryptoPP::memcpy_s -#define memmove_s CryptoPP::memmove_s -#endif -#endif - -inline void * memset_z(void *ptr, int value, size_t num) -{ -// avoid extranous warning on GCC 4.3.2 Ubuntu 8.10 -#if CRYPTOPP_GCC_VERSION >= 30001 - if (__builtin_constant_p(num) && num==0) - return ptr; -#endif - return memset(ptr, value, num); -} - -// can't use std::min or std::max in MSVC60 or Cygwin 1.1.0 -template inline const T& STDMIN(const T& a, const T& b) -{ - return b < a ? b : a; -} - -template inline const T1 UnsignedMin(const T1& a, const T2& b) -{ - CRYPTOPP_COMPILE_ASSERT((sizeof(T1)<=sizeof(T2) && T2(-1)>0) || (sizeof(T1)>sizeof(T2) && T1(-1)>0)); - assert(a==0 || a>0); // GCC workaround: get rid of the warning "comparison is always true due to limited range of data type" - assert(b>=0); - - if (sizeof(T1)<=sizeof(T2)) - return b < (T2)a ? (T1)b : a; - else - return (T1)b < a ? (T1)b : a; -} - -template inline const T& STDMAX(const T& a, const T& b) -{ - return a < b ? b : a; -} - -#define RETURN_IF_NONZERO(x) size_t returnedValue = x; if (returnedValue) return returnedValue - -// this version of the macro is fastest on Pentium 3 and Pentium 4 with MSVC 6 SP5 w/ Processor Pack -#define GETBYTE(x, y) (unsigned int)byte((x)>>(8*(y))) -// these may be faster on other CPUs/compilers -// #define GETBYTE(x, y) (unsigned int)(((x)>>(8*(y)))&255) -// #define GETBYTE(x, y) (((byte *)&(x))[y]) - -#define CRYPTOPP_GET_BYTE_AS_BYTE(x, y) byte((x)>>(8*(y))) - -template -unsigned int Parity(T value) -{ - for (unsigned int i=8*sizeof(value)/2; i>0; i/=2) - value ^= value >> i; - return (unsigned int)value&1; -} - -template -unsigned int BytePrecision(const T &value) -{ - if (!value) - return 0; - - unsigned int l=0, h=8*sizeof(value); - - while (h-l > 8) - { - unsigned int t = (l+h)/2; - if (value >> t) - l = t; - else - h = t; - } - - return h/8; -} - -template -unsigned int BitPrecision(const T &value) -{ - if (!value) - return 0; - - unsigned int l=0, h=8*sizeof(value); - - while (h-l > 1) - { - unsigned int t = (l+h)/2; - if (value >> t) - l = t; - else - h = t; - } - - return h; -} - -inline unsigned int TrailingZeros(word32 v) -{ -#if defined(__GNUC__) && CRYPTOPP_GCC_VERSION >= 30400 - return __builtin_ctz(v); -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - unsigned long result; - _BitScanForward(&result, v); - return result; -#else - // from http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup - static const int MultiplyDeBruijnBitPosition[32] = - { - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 - }; - return MultiplyDeBruijnBitPosition[((word32)((v & -v) * 0x077CB531U)) >> 27]; -#endif -} - -inline unsigned int TrailingZeros(word64 v) -{ -#if defined(__GNUC__) && CRYPTOPP_GCC_VERSION >= 30400 - return __builtin_ctzll(v); -#elif defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(_M_X64) || defined(_M_IA64)) - unsigned long result; - _BitScanForward64(&result, v); - return result; -#else - return word32(v) ? TrailingZeros(word32(v)) : 32 + TrailingZeros(word32(v>>32)); -#endif -} - -template -inline T Crop(T value, size_t size) -{ - if (size < 8*sizeof(value)) - return T(value & ((T(1) << size) - 1)); - else - return value; -} - -template -inline bool SafeConvert(T1 from, T2 &to) -{ - to = (T2)from; - if (from != to || (from > 0) != (to > 0)) - return false; - return true; -} - -inline size_t BitsToBytes(size_t bitCount) -{ - return ((bitCount+7)/(8)); -} - -inline size_t BytesToWords(size_t byteCount) -{ - return ((byteCount+WORD_SIZE-1)/WORD_SIZE); -} - -inline size_t BitsToWords(size_t bitCount) -{ - return ((bitCount+WORD_BITS-1)/(WORD_BITS)); -} - -inline size_t BitsToDwords(size_t bitCount) -{ - return ((bitCount+2*WORD_BITS-1)/(2*WORD_BITS)); -} - -CRYPTOPP_DLL void CRYPTOPP_API xorbuf(byte *buf, const byte *mask, size_t count); -CRYPTOPP_DLL void CRYPTOPP_API xorbuf(byte *output, const byte *input, const byte *mask, size_t count); - -CRYPTOPP_DLL bool CRYPTOPP_API VerifyBufsEqual(const byte *buf1, const byte *buf2, size_t count); - -template -inline bool IsPowerOf2(const T &n) -{ - return n > 0 && (n & (n-1)) == 0; -} - -template -inline T2 ModPowerOf2(const T1 &a, const T2 &b) -{ - assert(IsPowerOf2(b)); - return T2(a) & (b-1); -} - -template -inline T1 RoundDownToMultipleOf(const T1 &n, const T2 &m) -{ - if (IsPowerOf2(m)) - return n - ModPowerOf2(n, m); - else - return n - n%m; -} - -template -inline T1 RoundUpToMultipleOf(const T1 &n, const T2 &m) -{ - if (n+m-1 < n) - throw InvalidArgument("RoundUpToMultipleOf: integer overflow"); - return RoundDownToMultipleOf(n+m-1, m); -} - -template -inline unsigned int GetAlignmentOf(T *dummy=NULL) // VC60 workaround -{ -#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - if (sizeof(T) < 16) - return 1; -#endif - -#if (_MSC_VER >= 1300) - return __alignof(T); -#elif defined(__GNUC__) - return __alignof__(T); -#elif CRYPTOPP_BOOL_SLOW_WORD64 - return UnsignedMin(4U, sizeof(T)); -#else - return sizeof(T); -#endif -} - -inline bool IsAlignedOn(const void *p, unsigned int alignment) -{ - return alignment==1 || (IsPowerOf2(alignment) ? ModPowerOf2((size_t)p, alignment) == 0 : (size_t)p % alignment == 0); -} - -template -inline bool IsAligned(const void *p, T *dummy=NULL) // VC60 workaround -{ - return IsAlignedOn(p, GetAlignmentOf()); -} - -#ifdef IS_LITTLE_ENDIAN - typedef LittleEndian NativeByteOrder; -#else - typedef BigEndian NativeByteOrder; -#endif - -inline ByteOrder GetNativeByteOrder() -{ - return NativeByteOrder::ToEnum(); -} - -inline bool NativeByteOrderIs(ByteOrder order) -{ - return order == GetNativeByteOrder(); -} - -template -std::string IntToString(T a, unsigned int base = 10) -{ - if (a == 0) - return "0"; - bool negate = false; - if (a < 0) - { - negate = true; - a = 0-a; // VC .NET does not like -a - } - std::string result; - while (a > 0) - { - T digit = a % base; - result = char((digit < 10 ? '0' : ('a' - 10)) + digit) + result; - a /= base; - } - if (negate) - result = "-" + result; - return result; -} - -template -inline T1 SaturatingSubtract(const T1 &a, const T2 &b) -{ - return T1((a > b) ? (a - b) : 0); -} - -template -inline CipherDir GetCipherDir(const T &obj) -{ - return obj.IsForwardTransformation() ? ENCRYPTION : DECRYPTION; -} - -CRYPTOPP_DLL void CRYPTOPP_API CallNewHandler(); - -inline void IncrementCounterByOne(byte *inout, unsigned int s) -{ - for (int i=s-1, carry=1; i>=0 && carry; i--) - carry = !++inout[i]; -} - -inline void IncrementCounterByOne(byte *output, const byte *input, unsigned int s) -{ - int i, carry; - for (i=s-1, carry=1; i>=0 && carry; i--) - carry = ((output[i] = input[i]+1) == 0); - memcpy_s(output, s, input, i+1); -} - -template -inline void ConditionalSwap(bool c, T &a, T &b) -{ - T t = c * (a ^ b); - a ^= t; - b ^= t; -} - -template -inline void ConditionalSwapPointers(bool c, T &a, T &b) -{ - ptrdiff_t t = c * (a - b); - a -= t; - b += t; -} - -// see http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html -// and https://www.securecoding.cert.org/confluence/display/cplusplus/MSC06-CPP.+Be+aware+of+compiler+optimization+when+dealing+with+sensitive+data -template -void SecureWipeBuffer(T *buf, size_t n) -{ - // GCC 4.3.2 on Cygwin optimizes away the first store if this loop is done in the forward direction - volatile T *p = buf+n; - while (n--) - *(--p) = 0; -} - -#if (_MSC_VER >= 1400 || defined(__GNUC__)) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86) - -template<> inline void SecureWipeBuffer(byte *buf, size_t n) -{ - volatile byte *p = buf; -#ifdef __GNUC__ - asm volatile("rep stosb" : "+c"(n), "+D"(p) : "a"(0) : "memory"); -#else - __stosb((byte *)(size_t)p, 0, n); -#endif -} - -template<> inline void SecureWipeBuffer(word16 *buf, size_t n) -{ - volatile word16 *p = buf; -#ifdef __GNUC__ - asm volatile("rep stosw" : "+c"(n), "+D"(p) : "a"(0) : "memory"); -#else - __stosw((word16 *)(size_t)p, 0, n); -#endif -} - -template<> inline void SecureWipeBuffer(word32 *buf, size_t n) -{ - volatile word32 *p = buf; -#ifdef __GNUC__ - asm volatile("rep stosl" : "+c"(n), "+D"(p) : "a"(0) : "memory"); -#else - __stosd((unsigned long *)(size_t)p, 0, n); -#endif -} - -template<> inline void SecureWipeBuffer(word64 *buf, size_t n) -{ -#if CRYPTOPP_BOOL_X64 - volatile word64 *p = buf; -#ifdef __GNUC__ - asm volatile("rep stosq" : "+c"(n), "+D"(p) : "a"(0) : "memory"); -#else - __stosq((word64 *)(size_t)p, 0, n); -#endif -#else - SecureWipeBuffer((word32 *)buf, 2*n); -#endif -} - -#endif // #if (_MSC_VER >= 1400 || defined(__GNUC__)) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86) - -template -inline void SecureWipeArray(T *buf, size_t n) -{ - if (sizeof(T) % 8 == 0 && GetAlignmentOf() % GetAlignmentOf() == 0) - SecureWipeBuffer((word64 *)buf, n * (sizeof(T)/8)); - else if (sizeof(T) % 4 == 0 && GetAlignmentOf() % GetAlignmentOf() == 0) - SecureWipeBuffer((word32 *)buf, n * (sizeof(T)/4)); - else if (sizeof(T) % 2 == 0 && GetAlignmentOf() % GetAlignmentOf() == 0) - SecureWipeBuffer((word16 *)buf, n * (sizeof(T)/2)); - else - SecureWipeBuffer((byte *)buf, n * sizeof(T)); -} - -// this function uses wcstombs(), which assumes that setlocale() has been called -static std::string StringNarrow(const wchar_t *str, bool throwOnError = true) -{ -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4996) // 'wcstombs': This function or variable may be unsafe. -#endif - size_t size = wcstombs(NULL, str, 0); - if (size == size_t(0)-1) - { - if (throwOnError) - throw InvalidArgument("StringNarrow: wcstombs() call failed"); - else - return std::string(); - } - std::string result(size, 0); - wcstombs(&result[0], str, size); - return result; -#ifdef _MSC_VER -#pragma warning(pop) -#endif -} - -#if CRYPTOPP_BOOL_ALIGN16_ENABLED -CRYPTOPP_DLL void * CRYPTOPP_API AlignedAllocate(size_t size); -CRYPTOPP_DLL void CRYPTOPP_API AlignedDeallocate(void *p); -#endif - -CRYPTOPP_DLL void * CRYPTOPP_API UnalignedAllocate(size_t size); -CRYPTOPP_DLL void CRYPTOPP_API UnalignedDeallocate(void *p); - -// ************** rotate functions *************** - -template inline T rotlFixed(T x, unsigned int y) -{ - assert(y < sizeof(T)*8); - return y ? T((x<>(sizeof(T)*8-y))) : x; -} - -template inline T rotrFixed(T x, unsigned int y) -{ - assert(y < sizeof(T)*8); - return y ? T((x>>y) | (x<<(sizeof(T)*8-y))) : x; -} - -template inline T rotlVariable(T x, unsigned int y) -{ - assert(y < sizeof(T)*8); - return T((x<>(sizeof(T)*8-y))); -} - -template inline T rotrVariable(T x, unsigned int y) -{ - assert(y < sizeof(T)*8); - return T((x>>y) | (x<<(sizeof(T)*8-y))); -} - -template inline T rotlMod(T x, unsigned int y) -{ - y %= sizeof(T)*8; - return T((x<>(sizeof(T)*8-y))); -} - -template inline T rotrMod(T x, unsigned int y) -{ - y %= sizeof(T)*8; - return T((x>>y) | (x<<(sizeof(T)*8-y))); -} - -#ifdef _MSC_VER - -template<> inline word32 rotlFixed(word32 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _lrotl(x, y) : x; -} - -template<> inline word32 rotrFixed(word32 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _lrotr(x, y) : x; -} - -template<> inline word32 rotlVariable(word32 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _lrotl(x, y); -} - -template<> inline word32 rotrVariable(word32 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _lrotr(x, y); -} - -template<> inline word32 rotlMod(word32 x, unsigned int y) -{ - return _lrotl(x, y); -} - -template<> inline word32 rotrMod(word32 x, unsigned int y) -{ - return _lrotr(x, y); -} - -#endif // #ifdef _MSC_VER - -#if _MSC_VER >= 1300 && !defined(__INTEL_COMPILER) -// Intel C++ Compiler 10.0 calls a function instead of using the rotate instruction when using these instructions - -template<> inline word64 rotlFixed(word64 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotl64(x, y) : x; -} - -template<> inline word64 rotrFixed(word64 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotr64(x, y) : x; -} - -template<> inline word64 rotlVariable(word64 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotl64(x, y); -} - -template<> inline word64 rotrVariable(word64 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotr64(x, y); -} - -template<> inline word64 rotlMod(word64 x, unsigned int y) -{ - return _rotl64(x, y); -} - -template<> inline word64 rotrMod(word64 x, unsigned int y) -{ - return _rotr64(x, y); -} - -#endif // #if _MSC_VER >= 1310 - -#if _MSC_VER >= 1400 && !defined(__INTEL_COMPILER) -// Intel C++ Compiler 10.0 gives undefined externals with these - -template<> inline word16 rotlFixed(word16 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotl16(x, y) : x; -} - -template<> inline word16 rotrFixed(word16 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotr16(x, y) : x; -} - -template<> inline word16 rotlVariable(word16 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotl16(x, y); -} - -template<> inline word16 rotrVariable(word16 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotr16(x, y); -} - -template<> inline word16 rotlMod(word16 x, unsigned int y) -{ - return _rotl16(x, y); -} - -template<> inline word16 rotrMod(word16 x, unsigned int y) -{ - return _rotr16(x, y); -} - -template<> inline byte rotlFixed(byte x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotl8(x, y) : x; -} - -template<> inline byte rotrFixed(byte x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotr8(x, y) : x; -} - -template<> inline byte rotlVariable(byte x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotl8(x, y); -} - -template<> inline byte rotrVariable(byte x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotr8(x, y); -} - -template<> inline byte rotlMod(byte x, unsigned int y) -{ - return _rotl8(x, y); -} - -template<> inline byte rotrMod(byte x, unsigned int y) -{ - return _rotr8(x, y); -} - -#endif // #if _MSC_VER >= 1400 - -#if (defined(__MWERKS__) && TARGET_CPU_PPC) - -template<> inline word32 rotlFixed(word32 x, unsigned int y) -{ - assert(y < 32); - return y ? __rlwinm(x,y,0,31) : x; -} - -template<> inline word32 rotrFixed(word32 x, unsigned int y) -{ - assert(y < 32); - return y ? __rlwinm(x,32-y,0,31) : x; -} - -template<> inline word32 rotlVariable(word32 x, unsigned int y) -{ - assert(y < 32); - return (__rlwnm(x,y,0,31)); -} - -template<> inline word32 rotrVariable(word32 x, unsigned int y) -{ - assert(y < 32); - return (__rlwnm(x,32-y,0,31)); -} - -template<> inline word32 rotlMod(word32 x, unsigned int y) -{ - return (__rlwnm(x,y,0,31)); -} - -template<> inline word32 rotrMod(word32 x, unsigned int y) -{ - return (__rlwnm(x,32-y,0,31)); -} - -#endif // #if (defined(__MWERKS__) && TARGET_CPU_PPC) - -// ************** endian reversal *************** - -template -inline unsigned int GetByte(ByteOrder order, T value, unsigned int index) -{ - if (order == LITTLE_ENDIAN_ORDER) - return GETBYTE(value, index); - else - return GETBYTE(value, sizeof(T)-index-1); -} - -inline byte ByteReverse(byte value) -{ - return value; -} - -inline word16 ByteReverse(word16 value) -{ -#ifdef CRYPTOPP_BYTESWAP_AVAILABLE - return bswap_16(value); -#elif defined(_MSC_VER) && _MSC_VER >= 1300 - return _byteswap_ushort(value); -#else - return rotlFixed(value, 8U); -#endif -} - -inline word32 ByteReverse(word32 value) -{ -#if defined(__GNUC__) && defined(CRYPTOPP_X86_ASM_AVAILABLE) - __asm__ ("bswap %0" : "=r" (value) : "0" (value)); - return value; -#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) - return bswap_32(value); -#elif defined(__MWERKS__) && TARGET_CPU_PPC - return (word32)__lwbrx(&value,0); -#elif _MSC_VER >= 1400 || (_MSC_VER >= 1300 && !defined(_DLL)) - return _byteswap_ulong(value); -#elif CRYPTOPP_FAST_ROTATE(32) - // 5 instructions with rotate instruction, 9 without - return (rotrFixed(value, 8U) & 0xff00ff00) | (rotlFixed(value, 8U) & 0x00ff00ff); -#else - // 6 instructions with rotate instruction, 8 without - value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); - return rotlFixed(value, 16U); -#endif -} - -inline word64 ByteReverse(word64 value) -{ -#if defined(__GNUC__) && defined(CRYPTOPP_X86_ASM_AVAILABLE) && defined(__x86_64__) - __asm__ ("bswap %0" : "=r" (value) : "0" (value)); - return value; -#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) - return bswap_64(value); -#elif defined(_MSC_VER) && _MSC_VER >= 1300 - return _byteswap_uint64(value); -#elif CRYPTOPP_BOOL_SLOW_WORD64 - return (word64(ByteReverse(word32(value))) << 32) | ByteReverse(word32(value>>32)); -#else - value = ((value & W64LIT(0xFF00FF00FF00FF00)) >> 8) | ((value & W64LIT(0x00FF00FF00FF00FF)) << 8); - value = ((value & W64LIT(0xFFFF0000FFFF0000)) >> 16) | ((value & W64LIT(0x0000FFFF0000FFFF)) << 16); - return rotlFixed(value, 32U); -#endif -} - -inline byte BitReverse(byte value) -{ - value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); - value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); - return rotlFixed(value, 4U); -} - -inline word16 BitReverse(word16 value) -{ - value = ((value & 0xAAAA) >> 1) | ((value & 0x5555) << 1); - value = ((value & 0xCCCC) >> 2) | ((value & 0x3333) << 2); - value = ((value & 0xF0F0) >> 4) | ((value & 0x0F0F) << 4); - return ByteReverse(value); -} - -inline word32 BitReverse(word32 value) -{ - value = ((value & 0xAAAAAAAA) >> 1) | ((value & 0x55555555) << 1); - value = ((value & 0xCCCCCCCC) >> 2) | ((value & 0x33333333) << 2); - value = ((value & 0xF0F0F0F0) >> 4) | ((value & 0x0F0F0F0F) << 4); - return ByteReverse(value); -} - -inline word64 BitReverse(word64 value) -{ -#if CRYPTOPP_BOOL_SLOW_WORD64 - return (word64(BitReverse(word32(value))) << 32) | BitReverse(word32(value>>32)); -#else - value = ((value & W64LIT(0xAAAAAAAAAAAAAAAA)) >> 1) | ((value & W64LIT(0x5555555555555555)) << 1); - value = ((value & W64LIT(0xCCCCCCCCCCCCCCCC)) >> 2) | ((value & W64LIT(0x3333333333333333)) << 2); - value = ((value & W64LIT(0xF0F0F0F0F0F0F0F0)) >> 4) | ((value & W64LIT(0x0F0F0F0F0F0F0F0F)) << 4); - return ByteReverse(value); -#endif -} - -template -inline T BitReverse(T value) -{ - if (sizeof(T) == 1) - return (T)BitReverse((byte)value); - else if (sizeof(T) == 2) - return (T)BitReverse((word16)value); - else if (sizeof(T) == 4) - return (T)BitReverse((word32)value); - else - { - assert(sizeof(T) == 8); - return (T)BitReverse((word64)value); - } -} - -template -inline T ConditionalByteReverse(ByteOrder order, T value) -{ - return NativeByteOrderIs(order) ? value : ByteReverse(value); -} - -template -void ByteReverse(T *out, const T *in, size_t byteCount) -{ - assert(byteCount % sizeof(T) == 0); - size_t count = byteCount/sizeof(T); - for (size_t i=0; i -inline void ConditionalByteReverse(ByteOrder order, T *out, const T *in, size_t byteCount) -{ - if (!NativeByteOrderIs(order)) - ByteReverse(out, in, byteCount); - else if (in != out) - memcpy_s(out, byteCount, in, byteCount); -} - -template -inline void GetUserKey(ByteOrder order, T *out, size_t outlen, const byte *in, size_t inlen) -{ - const size_t U = sizeof(T); - assert(inlen <= outlen*U); - memcpy_s(out, outlen*U, in, inlen); - memset_z((byte *)out+inlen, 0, outlen*U-inlen); - ConditionalByteReverse(order, out, out, RoundUpToMultipleOf(inlen, U)); -} - -#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS -inline byte UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const byte *) -{ - return block[0]; -} - -inline word16 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word16 *) -{ - return (order == BIG_ENDIAN_ORDER) - ? block[1] | (block[0] << 8) - : block[0] | (block[1] << 8); -} - -inline word32 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word32 *) -{ - return (order == BIG_ENDIAN_ORDER) - ? word32(block[3]) | (word32(block[2]) << 8) | (word32(block[1]) << 16) | (word32(block[0]) << 24) - : word32(block[0]) | (word32(block[1]) << 8) | (word32(block[2]) << 16) | (word32(block[3]) << 24); -} - -inline word64 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word64 *) -{ - return (order == BIG_ENDIAN_ORDER) - ? - (word64(block[7]) | - (word64(block[6]) << 8) | - (word64(block[5]) << 16) | - (word64(block[4]) << 24) | - (word64(block[3]) << 32) | - (word64(block[2]) << 40) | - (word64(block[1]) << 48) | - (word64(block[0]) << 56)) - : - (word64(block[0]) | - (word64(block[1]) << 8) | - (word64(block[2]) << 16) | - (word64(block[3]) << 24) | - (word64(block[4]) << 32) | - (word64(block[5]) << 40) | - (word64(block[6]) << 48) | - (word64(block[7]) << 56)); -} - -inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, byte value, const byte *xorBlock) -{ - block[0] = xorBlock ? (value ^ xorBlock[0]) : value; -} - -inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word16 value, const byte *xorBlock) -{ - if (order == BIG_ENDIAN_ORDER) - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - } - else - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - } - } -} - -inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word32 value, const byte *xorBlock) -{ - if (order == BIG_ENDIAN_ORDER) - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - } - else - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - } - } -} - -inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word64 value, const byte *xorBlock) -{ - if (order == BIG_ENDIAN_ORDER) - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); - block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); - block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); - block[4] = xorBlock[4] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[5] = xorBlock[5] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[6] = xorBlock[6] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[7] = xorBlock[7] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); - block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); - block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); - block[4] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[5] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[6] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[7] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - } - else - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[4] = xorBlock[4] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); - block[5] = xorBlock[5] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); - block[6] = xorBlock[6] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); - block[7] = xorBlock[7] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[4] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); - block[5] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); - block[6] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); - block[7] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); - } - } -} -#endif // #ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - -template -inline T GetWord(bool assumeAligned, ByteOrder order, const byte *block) -{ -#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - if (!assumeAligned) - return UnalignedGetWordNonTemplate(order, block, (T*)NULL); - assert(IsAligned(block)); -#endif - return ConditionalByteReverse(order, *reinterpret_cast(block)); -} - -template -inline void GetWord(bool assumeAligned, ByteOrder order, T &result, const byte *block) -{ - result = GetWord(assumeAligned, order, block); -} - -template -inline void PutWord(bool assumeAligned, ByteOrder order, byte *block, T value, const byte *xorBlock = NULL) -{ -#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - if (!assumeAligned) - return UnalignedPutWordNonTemplate(order, block, value, xorBlock); - assert(IsAligned(block)); - assert(IsAligned(xorBlock)); -#endif - *reinterpret_cast(block) = ConditionalByteReverse(order, value) ^ (xorBlock ? *reinterpret_cast(xorBlock) : 0); -} - -template -class GetBlock -{ -public: - GetBlock(const void *block) - : m_block((const byte *)block) {} - - template - inline GetBlock & operator()(U &x) - { - CRYPTOPP_COMPILE_ASSERT(sizeof(U) >= sizeof(T)); - x = GetWord(A, B::ToEnum(), m_block); - m_block += sizeof(T); - return *this; - } - -private: - const byte *m_block; -}; - -template -class PutBlock -{ -public: - PutBlock(const void *xorBlock, void *block) - : m_xorBlock((const byte *)xorBlock), m_block((byte *)block) {} - - template - inline PutBlock & operator()(U x) - { - PutWord(A, B::ToEnum(), m_block, (T)x, m_xorBlock); - m_block += sizeof(T); - if (m_xorBlock) - m_xorBlock += sizeof(T); - return *this; - } - -private: - const byte *m_xorBlock; - byte *m_block; -}; - -template -struct BlockGetAndPut -{ - // function needed because of C++ grammatical ambiguity between expression-statements and declarations - static inline GetBlock Get(const void *block) {return GetBlock(block);} - typedef PutBlock Put; -}; - -template -std::string WordToString(T value, ByteOrder order = BIG_ENDIAN_ORDER) -{ - if (!NativeByteOrderIs(order)) - value = ByteReverse(value); - - return std::string((char *)&value, sizeof(value)); -} - -template -T StringToWord(const std::string &str, ByteOrder order = BIG_ENDIAN_ORDER) -{ - T value = 0; - memcpy_s(&value, sizeof(value), str.data(), UnsignedMin(str.size(), sizeof(value))); - return NativeByteOrderIs(order) ? value : ByteReverse(value); -} - -// ************** help remove warning on g++ *************** - -template struct SafeShifter; - -template<> struct SafeShifter -{ - template - static inline T RightShift(T value, unsigned int bits) - { - return 0; - } - - template - static inline T LeftShift(T value, unsigned int bits) - { - return 0; - } -}; - -template<> struct SafeShifter -{ - template - static inline T RightShift(T value, unsigned int bits) - { - return value >> bits; - } - - template - static inline T LeftShift(T value, unsigned int bits) - { - return value << bits; - } -}; - -template -inline T SafeRightShift(T value) -{ - return SafeShifter<(bits>=(8*sizeof(T)))>::RightShift(value, bits); -} - -template -inline T SafeLeftShift(T value) -{ - return SafeShifter<(bits>=(8*sizeof(T)))>::LeftShift(value, bits); -} - -// ************** use one buffer for multiple data members *************** - -#define CRYPTOPP_BLOCK_1(n, t, s) t* m_##n() {return (t *)(m_aggregate+0);} size_t SS1() {return sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_2(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS1());} size_t SS2() {return SS1()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_3(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS2());} size_t SS3() {return SS2()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_4(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS3());} size_t SS4() {return SS3()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_5(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS4());} size_t SS5() {return SS4()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_6(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS5());} size_t SS6() {return SS5()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_7(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS6());} size_t SS7() {return SS6()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_8(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS7());} size_t SS8() {return SS7()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCKS_END(i) size_t SST() {return SS##i();} void AllocateBlocks() {m_aggregate.New(SST());} AlignedSecByteBlock m_aggregate; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/modarith.h b/CryptoPP/modarith.h deleted file mode 100644 index c0368e3fb..000000000 --- a/CryptoPP/modarith.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef CRYPTOPP_MODARITH_H -#define CRYPTOPP_MODARITH_H - -// implementations are in integer.cpp - -#include "cryptlib.h" -#include "misc.h" -#include "integer.h" -#include "algebra.h" - -NAMESPACE_BEGIN(CryptoPP) - -CRYPTOPP_DLL_TEMPLATE_CLASS AbstractGroup; -CRYPTOPP_DLL_TEMPLATE_CLASS AbstractRing; -CRYPTOPP_DLL_TEMPLATE_CLASS AbstractEuclideanDomain; - -//! ring of congruence classes modulo n -/*! \note this implementation represents each congruence class as the smallest non-negative integer in that class */ -class CRYPTOPP_DLL ModularArithmetic : public AbstractRing -{ -public: - - typedef int RandomizationParameter; - typedef Integer Element; - - ModularArithmetic(const Integer &modulus = Integer::One()) - : m_modulus(modulus), m_result((word)0, modulus.reg.size()) {} - - ModularArithmetic(const ModularArithmetic &ma) - : m_modulus(ma.m_modulus), m_result((word)0, m_modulus.reg.size()) {} - - ModularArithmetic(BufferedTransformation &bt); // construct from BER encoded parameters - - virtual ModularArithmetic * Clone() const {return new ModularArithmetic(*this);} - - void DEREncode(BufferedTransformation &bt) const; - - void DEREncodeElement(BufferedTransformation &out, const Element &a) const; - void BERDecodeElement(BufferedTransformation &in, Element &a) const; - - const Integer& GetModulus() const {return m_modulus;} - void SetModulus(const Integer &newModulus) {m_modulus = newModulus; m_result.reg.resize(m_modulus.reg.size());} - - virtual bool IsMontgomeryRepresentation() const {return false;} - - virtual Integer ConvertIn(const Integer &a) const - {return a%m_modulus;} - - virtual Integer ConvertOut(const Integer &a) const - {return a;} - - const Integer& Half(const Integer &a) const; - - bool Equal(const Integer &a, const Integer &b) const - {return a==b;} - - const Integer& Identity() const - {return Integer::Zero();} - - const Integer& Add(const Integer &a, const Integer &b) const; - - Integer& Accumulate(Integer &a, const Integer &b) const; - - const Integer& Inverse(const Integer &a) const; - - const Integer& Subtract(const Integer &a, const Integer &b) const; - - Integer& Reduce(Integer &a, const Integer &b) const; - - const Integer& Double(const Integer &a) const - {return Add(a, a);} - - const Integer& MultiplicativeIdentity() const - {return Integer::One();} - - const Integer& Multiply(const Integer &a, const Integer &b) const - {return m_result1 = a*b%m_modulus;} - - const Integer& Square(const Integer &a) const - {return m_result1 = a.Squared()%m_modulus;} - - bool IsUnit(const Integer &a) const - {return Integer::Gcd(a, m_modulus).IsUnit();} - - const Integer& MultiplicativeInverse(const Integer &a) const - {return m_result1 = a.InverseMod(m_modulus);} - - const Integer& Divide(const Integer &a, const Integer &b) const - {return Multiply(a, MultiplicativeInverse(b));} - - Integer CascadeExponentiate(const Integer &x, const Integer &e1, const Integer &y, const Integer &e2) const; - - void SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const; - - unsigned int MaxElementBitLength() const - {return (m_modulus-1).BitCount();} - - unsigned int MaxElementByteLength() const - {return (m_modulus-1).ByteCount();} - - Element RandomElement( RandomNumberGenerator &rng , const RandomizationParameter &ignore_for_now = 0 ) const - // left RandomizationParameter arg as ref in case RandomizationParameter becomes a more complicated struct - { - return Element( rng , Integer( (long) 0) , m_modulus - Integer( (long) 1 ) ) ; - } - - bool operator==(const ModularArithmetic &rhs) const - {return m_modulus == rhs.m_modulus;} - - static const RandomizationParameter DefaultRandomizationParameter ; - -protected: - Integer m_modulus; - mutable Integer m_result, m_result1; - -}; - -// const ModularArithmetic::RandomizationParameter ModularArithmetic::DefaultRandomizationParameter = 0 ; - -//! do modular arithmetics in Montgomery representation for increased speed -/*! \note the Montgomery representation represents each congruence class [a] as a*r%n, where r is a convenient power of 2 */ -class CRYPTOPP_DLL MontgomeryRepresentation : public ModularArithmetic -{ -public: - MontgomeryRepresentation(const Integer &modulus); // modulus must be odd - - virtual ModularArithmetic * Clone() const {return new MontgomeryRepresentation(*this);} - - bool IsMontgomeryRepresentation() const {return true;} - - Integer ConvertIn(const Integer &a) const - {return (a<<(WORD_BITS*m_modulus.reg.size()))%m_modulus;} - - Integer ConvertOut(const Integer &a) const; - - const Integer& MultiplicativeIdentity() const - {return m_result1 = Integer::Power2(WORD_BITS*m_modulus.reg.size())%m_modulus;} - - const Integer& Multiply(const Integer &a, const Integer &b) const; - - const Integer& Square(const Integer &a) const; - - const Integer& MultiplicativeInverse(const Integer &a) const; - - Integer CascadeExponentiate(const Integer &x, const Integer &e1, const Integer &y, const Integer &e2) const - {return AbstractRing::CascadeExponentiate(x, e1, y, e2);} - - void SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const - {AbstractRing::SimultaneousExponentiate(results, base, exponents, exponentsCount);} - -private: - Integer m_u; - mutable IntegerSecBlock m_workspace; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/modes.cpp b/CryptoPP/modes.cpp deleted file mode 100644 index 46332284b..000000000 --- a/CryptoPP/modes.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// modes.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "modes.h" - -#ifndef NDEBUG -#include "des.h" -#endif - -NAMESPACE_BEGIN(CryptoPP) - -#ifndef NDEBUG -void Modes_TestInstantiations() -{ - CFB_Mode::Encryption m0; - CFB_Mode::Decryption m1; - OFB_Mode::Encryption m2; - CTR_Mode::Encryption m3; - ECB_Mode::Encryption m4; - CBC_Mode::Encryption m5; -} -#endif - -void CFB_ModePolicy::Iterate(byte *output, const byte *input, CipherDir dir, size_t iterationCount) -{ - assert(m_cipher->IsForwardTransformation()); // CFB mode needs the "encrypt" direction of the underlying block cipher, even to decrypt - assert(m_feedbackSize == BlockSize()); - - unsigned int s = BlockSize(); - if (dir == ENCRYPTION) - { - m_cipher->ProcessAndXorBlock(m_register, input, output); - m_cipher->AdvancedProcessBlocks(output, input+s, output+s, (iterationCount-1)*s, 0); - memcpy(m_register, output+(iterationCount-1)*s, s); - } - else - { - memcpy(m_temp, input+(iterationCount-1)*s, s); // make copy first in case of in-place decryption - m_cipher->AdvancedProcessBlocks(input, input+s, output+s, (iterationCount-1)*s, BlockTransformation::BT_ReverseDirection); - m_cipher->ProcessAndXorBlock(m_register, input, output); - memcpy(m_register, m_temp, s); - } -} - -void CFB_ModePolicy::TransformRegister() -{ - assert(m_cipher->IsForwardTransformation()); // CFB mode needs the "encrypt" direction of the underlying block cipher, even to decrypt - m_cipher->ProcessBlock(m_register, m_temp); - unsigned int updateSize = BlockSize()-m_feedbackSize; - memmove_s(m_register, m_register.size(), m_register+m_feedbackSize, updateSize); - memcpy_s(m_register+updateSize, m_register.size()-updateSize, m_temp, m_feedbackSize); -} - -void CFB_ModePolicy::CipherResynchronize(const byte *iv, size_t length) -{ - assert(length == BlockSize()); - CopyOrZero(m_register, iv, length); - TransformRegister(); -} - -void CFB_ModePolicy::SetFeedbackSize(unsigned int feedbackSize) -{ - if (feedbackSize > BlockSize()) - throw InvalidArgument("CFB_Mode: invalid feedback size"); - m_feedbackSize = feedbackSize ? feedbackSize : BlockSize(); -} - -void CFB_ModePolicy::ResizeBuffers() -{ - CipherModeBase::ResizeBuffers(); - m_temp.New(BlockSize()); -} - -void OFB_ModePolicy::WriteKeystream(byte *keystreamBuffer, size_t iterationCount) -{ - assert(m_cipher->IsForwardTransformation()); // OFB mode needs the "encrypt" direction of the underlying block cipher, even to decrypt - unsigned int s = BlockSize(); - m_cipher->ProcessBlock(m_register, keystreamBuffer); - if (iterationCount > 1) - m_cipher->AdvancedProcessBlocks(keystreamBuffer, NULL, keystreamBuffer+s, s*(iterationCount-1), 0); - memcpy(m_register, keystreamBuffer+s*(iterationCount-1), s); -} - -void OFB_ModePolicy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length) -{ - assert(length == BlockSize()); - CopyOrZero(m_register, iv, length); -} - -void CTR_ModePolicy::SeekToIteration(lword iterationCount) -{ - int carry=0; - for (int i=BlockSize()-1; i>=0; i--) - { - unsigned int sum = m_register[i] + byte(iterationCount) + carry; - m_counterArray[i] = (byte) sum; - carry = sum >> 8; - iterationCount >>= 8; - } -} - -void CTR_ModePolicy::IncrementCounterBy256() -{ - IncrementCounterByOne(m_counterArray, BlockSize()-1); -} - -void CTR_ModePolicy::OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount) -{ - assert(m_cipher->IsForwardTransformation()); // CTR mode needs the "encrypt" direction of the underlying block cipher, even to decrypt - unsigned int s = BlockSize(); - unsigned int inputIncrement = input ? s : 0; - - while (iterationCount) - { - byte lsb = m_counterArray[s-1]; - size_t blocks = UnsignedMin(iterationCount, 256U-lsb); - m_cipher->AdvancedProcessBlocks(m_counterArray, input, output, blocks*s, BlockTransformation::BT_InBlockIsCounter|BlockTransformation::BT_AllowParallel); - if ((m_counterArray[s-1] = lsb + (byte)blocks) == 0) - IncrementCounterBy256(); - - output += blocks*s; - input += blocks*inputIncrement; - iterationCount -= blocks; - } -} - -void CTR_ModePolicy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length) -{ - assert(length == BlockSize()); - CopyOrZero(m_register, iv, length); - m_counterArray = m_register; -} - -void BlockOrientedCipherModeBase::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) -{ - m_cipher->SetKey(key, length, params); - ResizeBuffers(); - if (IsResynchronizable()) - { - size_t ivLength; - const byte *iv = GetIVAndThrowIfInvalid(params, ivLength); - Resynchronize(iv, (int)ivLength); - } -} - -void ECB_OneWay::ProcessData(byte *outString, const byte *inString, size_t length) -{ - assert(length%BlockSize()==0); - m_cipher->AdvancedProcessBlocks(inString, NULL, outString, length, BlockTransformation::BT_AllowParallel); -} - -void CBC_Encryption::ProcessData(byte *outString, const byte *inString, size_t length) -{ - if (!length) - return; - assert(length%BlockSize()==0); - - unsigned int blockSize = BlockSize(); - m_cipher->AdvancedProcessBlocks(inString, m_register, outString, blockSize, BlockTransformation::BT_XorInput); - if (length > blockSize) - m_cipher->AdvancedProcessBlocks(inString+blockSize, outString, outString+blockSize, length-blockSize, BlockTransformation::BT_XorInput); - memcpy(m_register, outString + length - blockSize, blockSize); -} - -void CBC_CTS_Encryption::ProcessLastBlock(byte *outString, const byte *inString, size_t length) -{ - if (length <= BlockSize()) - { - if (!m_stolenIV) - throw InvalidArgument("CBC_Encryption: message is too short for ciphertext stealing"); - - // steal from IV - memcpy(outString, m_register, length); - outString = m_stolenIV; - } - else - { - // steal from next to last block - xorbuf(m_register, inString, BlockSize()); - m_cipher->ProcessBlock(m_register); - inString += BlockSize(); - length -= BlockSize(); - memcpy(outString+BlockSize(), m_register, length); - } - - // output last full ciphertext block - xorbuf(m_register, inString, length); - m_cipher->ProcessBlock(m_register); - memcpy(outString, m_register, BlockSize()); -} - -void CBC_Decryption::ProcessData(byte *outString, const byte *inString, size_t length) -{ - if (!length) - return; - assert(length%BlockSize()==0); - - unsigned int blockSize = BlockSize(); - memcpy(m_temp, inString+length-blockSize, blockSize); // save copy now in case of in-place decryption - if (length > blockSize) - m_cipher->AdvancedProcessBlocks(inString+blockSize, inString, outString+blockSize, length-blockSize, BlockTransformation::BT_ReverseDirection|BlockTransformation::BT_AllowParallel); - m_cipher->ProcessAndXorBlock(inString, m_register, outString); - m_register.swap(m_temp); -} - -void CBC_CTS_Decryption::ProcessLastBlock(byte *outString, const byte *inString, size_t length) -{ - const byte *pn, *pn1; - bool stealIV = length <= BlockSize(); - - if (stealIV) - { - pn = inString; - pn1 = m_register; - } - else - { - pn = inString + BlockSize(); - pn1 = inString; - length -= BlockSize(); - } - - // decrypt last partial plaintext block - memcpy(m_temp, pn1, BlockSize()); - m_cipher->ProcessBlock(m_temp); - xorbuf(m_temp, pn, length); - - if (stealIV) - memcpy(outString, m_temp, length); - else - { - memcpy(outString+BlockSize(), m_temp, length); - // decrypt next to last plaintext block - memcpy(m_temp, pn, length); - m_cipher->ProcessBlock(m_temp); - xorbuf(outString, m_temp, m_register, BlockSize()); - } -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/modes.h b/CryptoPP/modes.h deleted file mode 100644 index c0c30c476..000000000 --- a/CryptoPP/modes.h +++ /dev/null @@ -1,422 +0,0 @@ -#ifndef CRYPTOPP_MODES_H -#define CRYPTOPP_MODES_H - -/*! \file -*/ - -#include "cryptlib.h" -#include "secblock.h" -#include "misc.h" -#include "strciphr.h" -#include "argnames.h" -#include "algparam.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! Cipher modes documentation. See NIST SP 800-38A for definitions of these modes. See AuthenticatedSymmetricCipherDocumentation for authenticated encryption modes. - -/*! Each class derived from this one defines two types, Encryption and Decryption, - both of which implement the SymmetricCipher interface. - For each mode there are two classes, one of which is a template class, - and the other one has a name that ends in "_ExternalCipher". - The "external cipher" mode objects hold a reference to the underlying block cipher, - instead of holding an instance of it. The reference must be passed in to the constructor. - For the "cipher holder" classes, the CIPHER template parameter should be a class - derived from BlockCipherDocumentation, for example DES or AES. -*/ -struct CipherModeDocumentation : public SymmetricCipherDocumentation -{ -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CipherModeBase : public SymmetricCipher -{ -public: - size_t MinKeyLength() const {return m_cipher->MinKeyLength();} - size_t MaxKeyLength() const {return m_cipher->MaxKeyLength();} - size_t DefaultKeyLength() const {return m_cipher->DefaultKeyLength();} - size_t GetValidKeyLength(size_t n) const {return m_cipher->GetValidKeyLength(n);} - bool IsValidKeyLength(size_t n) const {return m_cipher->IsValidKeyLength(n);} - - unsigned int OptimalDataAlignment() const {return m_cipher->OptimalDataAlignment();} - - unsigned int IVSize() const {return BlockSize();} - virtual IV_Requirement IVRequirement() const =0; - - void SetCipher(BlockCipher &cipher) - { - this->ThrowIfResynchronizable(); - this->m_cipher = &cipher; - this->ResizeBuffers(); - } - - void SetCipherWithIV(BlockCipher &cipher, const byte *iv, int feedbackSize = 0) - { - this->ThrowIfInvalidIV(iv); - this->m_cipher = &cipher; - this->ResizeBuffers(); - this->SetFeedbackSize(feedbackSize); - if (this->IsResynchronizable()) - this->Resynchronize(iv); - } - -protected: - CipherModeBase() : m_cipher(NULL) {} - inline unsigned int BlockSize() const {assert(m_register.size() > 0); return (unsigned int)m_register.size();} - virtual void SetFeedbackSize(unsigned int feedbackSize) - { - if (!(feedbackSize == 0 || feedbackSize == BlockSize())) - throw InvalidArgument("CipherModeBase: feedback size cannot be specified for this cipher mode"); - } - virtual void ResizeBuffers() - { - m_register.New(m_cipher->BlockSize()); - } - - BlockCipher *m_cipher; - AlignedSecByteBlock m_register; -}; - -template -class CRYPTOPP_NO_VTABLE ModePolicyCommonTemplate : public CipherModeBase, public POLICY_INTERFACE -{ - unsigned int GetAlignment() const {return m_cipher->OptimalDataAlignment();} - void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length); -}; - -template -void ModePolicyCommonTemplate::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length) -{ - m_cipher->SetKey(key, length, params); - ResizeBuffers(); - int feedbackSize = params.GetIntValueWithDefault(Name::FeedbackSize(), 0); - SetFeedbackSize(feedbackSize); -} - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CFB_ModePolicy : public ModePolicyCommonTemplate -{ -public: - IV_Requirement IVRequirement() const {return RANDOM_IV;} - static const char * CRYPTOPP_API StaticAlgorithmName() {return "CFB";} - -protected: - unsigned int GetBytesPerIteration() const {return m_feedbackSize;} - byte * GetRegisterBegin() {return m_register + BlockSize() - m_feedbackSize;} - bool CanIterate() const {return m_feedbackSize == BlockSize();} - void Iterate(byte *output, const byte *input, CipherDir dir, size_t iterationCount); - void TransformRegister(); - void CipherResynchronize(const byte *iv, size_t length); - void SetFeedbackSize(unsigned int feedbackSize); - void ResizeBuffers(); - - SecByteBlock m_temp; - unsigned int m_feedbackSize; -}; - -inline void CopyOrZero(void *dest, const void *src, size_t s) -{ - if (src) - memcpy_s(dest, s, src, s); - else - memset(dest, 0, s); -} - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE OFB_ModePolicy : public ModePolicyCommonTemplate -{ -public: - bool CipherIsRandomAccess() const {return false;} - IV_Requirement IVRequirement() const {return UNIQUE_IV;} - static const char * CRYPTOPP_API StaticAlgorithmName() {return "OFB";} - -private: - unsigned int GetBytesPerIteration() const {return BlockSize();} - unsigned int GetIterationsToBuffer() const {return m_cipher->OptimalNumberOfParallelBlocks();} - void WriteKeystream(byte *keystreamBuffer, size_t iterationCount); - void CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length); -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CTR_ModePolicy : public ModePolicyCommonTemplate -{ -public: - bool CipherIsRandomAccess() const {return true;} - IV_Requirement IVRequirement() const {return RANDOM_IV;} - static const char * CRYPTOPP_API StaticAlgorithmName() {return "CTR";} - -protected: - virtual void IncrementCounterBy256(); - - unsigned int GetAlignment() const {return m_cipher->OptimalDataAlignment();} - unsigned int GetBytesPerIteration() const {return BlockSize();} - unsigned int GetIterationsToBuffer() const {return m_cipher->OptimalNumberOfParallelBlocks();} - void WriteKeystream(byte *buffer, size_t iterationCount) - {OperateKeystream(WRITE_KEYSTREAM, buffer, NULL, iterationCount);} - bool CanOperateKeystream() const {return true;} - void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount); - void CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length); - void SeekToIteration(lword iterationCount); - - AlignedSecByteBlock m_counterArray; -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BlockOrientedCipherModeBase : public CipherModeBase -{ -public: - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms); - unsigned int MandatoryBlockSize() const {return BlockSize();} - bool IsRandomAccess() const {return false;} - bool IsSelfInverting() const {return false;} - bool IsForwardTransformation() const {return m_cipher->IsForwardTransformation();} - void Resynchronize(const byte *iv, int length=-1) {memcpy_s(m_register, m_register.size(), iv, ThrowIfInvalidIVLength(length));} - -protected: - bool RequireAlignedInput() const {return true;} - void ResizeBuffers() - { - CipherModeBase::ResizeBuffers(); - m_buffer.New(BlockSize()); - } - - SecByteBlock m_buffer; -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE ECB_OneWay : public BlockOrientedCipherModeBase -{ -public: - void SetKey(const byte *key, size_t length, const NameValuePairs ¶ms = g_nullNameValuePairs) - {m_cipher->SetKey(key, length, params); BlockOrientedCipherModeBase::ResizeBuffers();} - IV_Requirement IVRequirement() const {return NOT_RESYNCHRONIZABLE;} - unsigned int OptimalBlockSize() const {return BlockSize() * m_cipher->OptimalNumberOfParallelBlocks();} - void ProcessData(byte *outString, const byte *inString, size_t length); - static const char * CRYPTOPP_API StaticAlgorithmName() {return "ECB";} -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CBC_ModeBase : public BlockOrientedCipherModeBase -{ -public: - IV_Requirement IVRequirement() const {return UNPREDICTABLE_RANDOM_IV;} - bool RequireAlignedInput() const {return false;} - unsigned int MinLastBlockSize() const {return 0;} - static const char * CRYPTOPP_API StaticAlgorithmName() {return "CBC";} -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CBC_Encryption : public CBC_ModeBase -{ -public: - void ProcessData(byte *outString, const byte *inString, size_t length); -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CBC_CTS_Encryption : public CBC_Encryption -{ -public: - void SetStolenIV(byte *iv) {m_stolenIV = iv;} - unsigned int MinLastBlockSize() const {return BlockSize()+1;} - void ProcessLastBlock(byte *outString, const byte *inString, size_t length); - static const char * CRYPTOPP_API StaticAlgorithmName() {return "CBC/CTS";} - -protected: - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) - { - CBC_Encryption::UncheckedSetKey(key, length, params); - m_stolenIV = params.GetValueWithDefault(Name::StolenIV(), (byte *)NULL); - } - - byte *m_stolenIV; -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CBC_Decryption : public CBC_ModeBase -{ -public: - void ProcessData(byte *outString, const byte *inString, size_t length); - -protected: - void ResizeBuffers() - { - BlockOrientedCipherModeBase::ResizeBuffers(); - m_temp.New(BlockSize()); - } - AlignedSecByteBlock m_temp; -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CBC_CTS_Decryption : public CBC_Decryption -{ -public: - unsigned int MinLastBlockSize() const {return BlockSize()+1;} - void ProcessLastBlock(byte *outString, const byte *inString, size_t length); -}; - -//! _ -template -class CipherModeFinalTemplate_CipherHolder : protected ObjectHolder, public AlgorithmImpl > -{ -public: - CipherModeFinalTemplate_CipherHolder() - { - this->m_cipher = &this->m_object; - this->ResizeBuffers(); - } - CipherModeFinalTemplate_CipherHolder(const byte *key, size_t length) - { - this->m_cipher = &this->m_object; - this->SetKey(key, length); - } - CipherModeFinalTemplate_CipherHolder(const byte *key, size_t length, const byte *iv) - { - this->m_cipher = &this->m_object; - this->SetKey(key, length, MakeParameters(Name::IV(), ConstByteArrayParameter(iv, this->m_cipher->BlockSize()))); - } - CipherModeFinalTemplate_CipherHolder(const byte *key, size_t length, const byte *iv, int feedbackSize) - { - this->m_cipher = &this->m_object; - this->SetKey(key, length, MakeParameters(Name::IV(), ConstByteArrayParameter(iv, this->m_cipher->BlockSize()))(Name::FeedbackSize(), feedbackSize)); - } - - static std::string CRYPTOPP_API StaticAlgorithmName() - {return CIPHER::StaticAlgorithmName() + "/" + BASE::StaticAlgorithmName();} -}; - -//! _ -template -class CipherModeFinalTemplate_ExternalCipher : public BASE -{ -public: - CipherModeFinalTemplate_ExternalCipher() {} - CipherModeFinalTemplate_ExternalCipher(BlockCipher &cipher) - {this->SetCipher(cipher);} - CipherModeFinalTemplate_ExternalCipher(BlockCipher &cipher, const byte *iv, int feedbackSize = 0) - {this->SetCipherWithIV(cipher, iv, feedbackSize);} - - std::string AlgorithmName() const - {return (this->m_cipher ? this->m_cipher->AlgorithmName() + "/" : std::string("")) + BASE::StaticAlgorithmName();} -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS CFB_CipherTemplate >; -CRYPTOPP_DLL_TEMPLATE_CLASS CFB_EncryptionTemplate >; -CRYPTOPP_DLL_TEMPLATE_CLASS CFB_DecryptionTemplate >; - -//! CFB mode -template -struct CFB_Mode : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_CipherHolder > > > Encryption; - typedef CipherModeFinalTemplate_CipherHolder > > > Decryption; -}; - -//! CFB mode, external cipher -struct CFB_Mode_ExternalCipher : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_ExternalCipher > > > Encryption; - typedef CipherModeFinalTemplate_ExternalCipher > > > Decryption; -}; - -//! CFB mode FIPS variant, requiring full block plaintext according to FIPS 800-38A -template -struct CFB_FIPS_Mode : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_CipherHolder > > > > Encryption; - typedef CipherModeFinalTemplate_CipherHolder > > > > Decryption; -}; - -//! CFB mode FIPS variant, requiring full block plaintext according to FIPS 800-38A, external cipher -struct CFB_FIPS_Mode_ExternalCipher : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_ExternalCipher > > > > Encryption; - typedef CipherModeFinalTemplate_ExternalCipher > > > > Decryption; -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS AdditiveCipherTemplate >; - -//! OFB mode -template -struct OFB_Mode : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_CipherHolder > > > Encryption; - typedef Encryption Decryption; -}; - -//! OFB mode, external cipher -struct OFB_Mode_ExternalCipher : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_ExternalCipher > > > Encryption; - typedef Encryption Decryption; -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS AdditiveCipherTemplate >; -CRYPTOPP_DLL_TEMPLATE_CLASS CipherModeFinalTemplate_ExternalCipher > > >; - -//! CTR mode -template -struct CTR_Mode : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_CipherHolder > > > Encryption; - typedef Encryption Decryption; -}; - -//! CTR mode, external cipher -struct CTR_Mode_ExternalCipher : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_ExternalCipher > > > Encryption; - typedef Encryption Decryption; -}; - -//! ECB mode -template -struct ECB_Mode : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_CipherHolder Encryption; - typedef CipherModeFinalTemplate_CipherHolder Decryption; -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS CipherModeFinalTemplate_ExternalCipher; - -//! ECB mode, external cipher -struct ECB_Mode_ExternalCipher : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_ExternalCipher Encryption; - typedef Encryption Decryption; -}; - -//! CBC mode -template -struct CBC_Mode : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_CipherHolder Encryption; - typedef CipherModeFinalTemplate_CipherHolder Decryption; -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS CipherModeFinalTemplate_ExternalCipher; -CRYPTOPP_DLL_TEMPLATE_CLASS CipherModeFinalTemplate_ExternalCipher; - -//! CBC mode, external cipher -struct CBC_Mode_ExternalCipher : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_ExternalCipher Encryption; - typedef CipherModeFinalTemplate_ExternalCipher Decryption; -}; - -//! CBC mode with ciphertext stealing -template -struct CBC_CTS_Mode : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_CipherHolder Encryption; - typedef CipherModeFinalTemplate_CipherHolder Decryption; -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS CipherModeFinalTemplate_ExternalCipher; -CRYPTOPP_DLL_TEMPLATE_CLASS CipherModeFinalTemplate_ExternalCipher; - -//! CBC mode with ciphertext stealing, external cipher -struct CBC_CTS_Mode_ExternalCipher : public CipherModeDocumentation -{ - typedef CipherModeFinalTemplate_ExternalCipher Encryption; - typedef CipherModeFinalTemplate_ExternalCipher Decryption; -}; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY -typedef CFB_Mode_ExternalCipher::Encryption CFBEncryption; -typedef CFB_Mode_ExternalCipher::Decryption CFBDecryption; -typedef OFB_Mode_ExternalCipher::Encryption OFB; -typedef CTR_Mode_ExternalCipher::Encryption CounterMode; -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/modexppc.h b/CryptoPP/modexppc.h deleted file mode 100644 index fbe701279..000000000 --- a/CryptoPP/modexppc.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef CRYPTOPP_MODEXPPC_H -#define CRYPTOPP_MODEXPPC_H - -#include "modarith.h" -#include "eprecomp.h" -#include "smartptr.h" -#include "pubkey.h" - -NAMESPACE_BEGIN(CryptoPP) - -CRYPTOPP_DLL_TEMPLATE_CLASS DL_FixedBasePrecomputationImpl; - -class ModExpPrecomputation : public DL_GroupPrecomputation -{ -public: - // DL_GroupPrecomputation - bool NeedConversions() const {return true;} - Element ConvertIn(const Element &v) const {return m_mr->ConvertIn(v);} - virtual Element ConvertOut(const Element &v) const {return m_mr->ConvertOut(v);} - const AbstractGroup & GetGroup() const {return m_mr->MultiplicativeGroup();} - Element BERDecodeElement(BufferedTransformation &bt) const {return Integer(bt);} - void DEREncodeElement(BufferedTransformation &bt, const Element &v) const {v.DEREncode(bt);} - - // non-inherited - void SetModulus(const Integer &v) {m_mr.reset(new MontgomeryRepresentation(v));} - const Integer & GetModulus() const {return m_mr->GetModulus();} - -private: - value_ptr m_mr; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/mqueue.cpp b/CryptoPP/mqueue.cpp deleted file mode 100644 index 1d645d83d..000000000 --- a/CryptoPP/mqueue.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// mqueue.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "mqueue.h" - -NAMESPACE_BEGIN(CryptoPP) - -MessageQueue::MessageQueue(unsigned int nodeSize) - : m_queue(nodeSize), m_lengths(1, 0U), m_messageCounts(1, 0U) -{ -} - -size_t MessageQueue::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const -{ - if (begin >= MaxRetrievable()) - return 0; - - return m_queue.CopyRangeTo2(target, begin, STDMIN(MaxRetrievable(), end), channel, blocking); -} - -size_t MessageQueue::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - transferBytes = STDMIN(MaxRetrievable(), transferBytes); - size_t blockedBytes = m_queue.TransferTo2(target, transferBytes, channel, blocking); - m_lengths.front() -= transferBytes; - return blockedBytes; -} - -bool MessageQueue::GetNextMessage() -{ - if (NumberOfMessages() > 0 && !AnyRetrievable()) - { - m_lengths.pop_front(); - if (m_messageCounts[0] == 0 && m_messageCounts.size() > 1) - m_messageCounts.pop_front(); - return true; - } - else - return false; -} - -unsigned int MessageQueue::CopyMessagesTo(BufferedTransformation &target, unsigned int count, const std::string &channel) const -{ - ByteQueue::Walker walker(m_queue); - std::deque::const_iterator it = m_lengths.begin(); - unsigned int i; - for (i=0; i 0 && q2.AnyRetrievable()) - { - size_t len = length; - const byte *data = q2.Spy(len); - len = STDMIN(len, length); - if (memcmp(inString, data, len) != 0) - goto mismatch; - inString += len; - length -= len; - q2.Skip(len); - } - - q1.Put(inString, length); - - if (messageEnd) - { - if (q2.AnyRetrievable()) - goto mismatch; - else if (q2.AnyMessages()) - q2.GetNextMessage(); - else if (q2.NumberOfMessageSeries() > 0) - goto mismatch; - else - q1.MessageEnd(); - } - - return 0; - -mismatch: - return HandleMismatchDetected(blocking); - } -} - -bool EqualityComparisonFilter::ChannelMessageSeriesEnd(const std::string &channel, int propagation, bool blocking) -{ - unsigned int i = MapChannel(channel); - - if (i == 2) - { - OutputMessageSeriesEnd(4, propagation, blocking, channel); - return false; - } - else if (m_mismatchDetected) - return false; - else - { - MessageQueue &q1 = m_q[i], &q2 = m_q[1-i]; - - if (q2.AnyRetrievable() || q2.AnyMessages()) - goto mismatch; - else if (q2.NumberOfMessageSeries() > 0) - return Output(2, (const byte *)"\1", 1, 0, blocking) != 0; - else - q1.MessageSeriesEnd(); - - return false; - -mismatch: - return HandleMismatchDetected(blocking); - } -} - -bool EqualityComparisonFilter::HandleMismatchDetected(bool blocking) -{ - m_mismatchDetected = true; - if (m_throwIfNotEqual) - throw MismatchDetected(); - return Output(1, (const byte *)"\0", 1, 0, blocking) != 0; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/mqueue.h b/CryptoPP/mqueue.h deleted file mode 100644 index efa57a7cf..000000000 --- a/CryptoPP/mqueue.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef CRYPTOPP_MQUEUE_H -#define CRYPTOPP_MQUEUE_H - -#include "queue.h" -#include "filters.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -//! Message Queue -class CRYPTOPP_DLL MessageQueue : public AutoSignaling -{ -public: - MessageQueue(unsigned int nodeSize=256); - - void IsolatedInitialize(const NameValuePairs ¶meters) - {m_queue.IsolatedInitialize(parameters); m_lengths.assign(1, 0U); m_messageCounts.assign(1, 0U);} - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking) - { - m_queue.Put(begin, length); - m_lengths.back() += length; - if (messageEnd) - { - m_lengths.push_back(0); - m_messageCounts.back()++; - } - return 0; - } - bool IsolatedFlush(bool hardFlush, bool blocking) {return false;} - bool IsolatedMessageSeriesEnd(bool blocking) - {m_messageCounts.push_back(0); return false;} - - lword MaxRetrievable() const - {return m_lengths.front();} - bool AnyRetrievable() const - {return m_lengths.front() > 0;} - - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const; - - lword TotalBytesRetrievable() const - {return m_queue.MaxRetrievable();} - unsigned int NumberOfMessages() const - {return (unsigned int)m_lengths.size()-1;} - bool GetNextMessage(); - - unsigned int NumberOfMessagesInThisSeries() const - {return m_messageCounts[0];} - unsigned int NumberOfMessageSeries() const - {return (unsigned int)m_messageCounts.size()-1;} - - unsigned int CopyMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) const; - - const byte * Spy(size_t &contiguousSize) const; - - void swap(MessageQueue &rhs); - -private: - ByteQueue m_queue; - std::deque m_lengths; - std::deque m_messageCounts; -}; - - -//! A filter that checks messages on two channels for equality -class CRYPTOPP_DLL EqualityComparisonFilter : public Unflushable > -{ -public: - struct MismatchDetected : public Exception {MismatchDetected() : Exception(DATA_INTEGRITY_CHECK_FAILED, "EqualityComparisonFilter: did not receive the same data on two channels") {}}; - - /*! if throwIfNotEqual is false, this filter will output a '\\0' byte when it detects a mismatch, '\\1' otherwise */ - EqualityComparisonFilter(BufferedTransformation *attachment=NULL, bool throwIfNotEqual=true, const std::string &firstChannel="0", const std::string &secondChannel="1") - : m_throwIfNotEqual(throwIfNotEqual), m_mismatchDetected(false) - , m_firstChannel(firstChannel), m_secondChannel(secondChannel) - {Detach(attachment);} - - size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking); - bool ChannelMessageSeriesEnd(const std::string &channel, int propagation=-1, bool blocking=true); - -private: - unsigned int MapChannel(const std::string &channel) const; - bool HandleMismatchDetected(bool blocking); - - bool m_throwIfNotEqual, m_mismatchDetected; - std::string m_firstChannel, m_secondChannel; - MessageQueue m_q[2]; -}; - -NAMESPACE_END - -#ifndef __BORLANDC__ -NAMESPACE_BEGIN(std) -template<> inline void swap(CryptoPP::MessageQueue &a, CryptoPP::MessageQueue &b) -{ - a.swap(b); -} -NAMESPACE_END -#endif - -#endif diff --git a/CryptoPP/mqv.cpp b/CryptoPP/mqv.cpp deleted file mode 100644 index c427561b2..000000000 --- a/CryptoPP/mqv.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// mqv.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "mqv.h" - -NAMESPACE_BEGIN(CryptoPP) - -void TestInstantiations_MQV() -{ - MQV mqv; -} - -NAMESPACE_END diff --git a/CryptoPP/mqv.h b/CryptoPP/mqv.h deleted file mode 100644 index 2683817b0..000000000 --- a/CryptoPP/mqv.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef CRYPTOPP_MQV_H -#define CRYPTOPP_MQV_H - -/** \file -*/ - -#include "gfpcrypt.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -template -class MQV_Domain : public AuthenticatedKeyAgreementDomain -{ -public: - typedef GROUP_PARAMETERS GroupParameters; - typedef typename GroupParameters::Element Element; - typedef MQV_Domain Domain; - - MQV_Domain() {} - - MQV_Domain(const GroupParameters ¶ms) - : m_groupParameters(params) {} - - MQV_Domain(BufferedTransformation &bt) - {m_groupParameters.BERDecode(bt);} - - template - MQV_Domain(T1 v1, T2 v2) - {m_groupParameters.Initialize(v1, v2);} - - template - MQV_Domain(T1 v1, T2 v2, T3 v3) - {m_groupParameters.Initialize(v1, v2, v3);} - - template - MQV_Domain(T1 v1, T2 v2, T3 v3, T4 v4) - {m_groupParameters.Initialize(v1, v2, v3, v4);} - - const GroupParameters & GetGroupParameters() const {return m_groupParameters;} - GroupParameters & AccessGroupParameters() {return m_groupParameters;} - - CryptoParameters & AccessCryptoParameters() {return AccessAbstractGroupParameters();} - - unsigned int AgreedValueLength() const {return GetAbstractGroupParameters().GetEncodedElementSize(false);} - unsigned int StaticPrivateKeyLength() const {return GetAbstractGroupParameters().GetSubgroupOrder().ByteCount();} - unsigned int StaticPublicKeyLength() const {return GetAbstractGroupParameters().GetEncodedElementSize(true);} - - void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const - { - Integer x(rng, Integer::One(), GetAbstractGroupParameters().GetMaxExponent()); - x.Encode(privateKey, StaticPrivateKeyLength()); - } - - void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const - { - const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); - Integer x(privateKey, StaticPrivateKeyLength()); - Element y = params.ExponentiateBase(x); - params.EncodeElement(true, y, publicKey); - } - - unsigned int EphemeralPrivateKeyLength() const {return StaticPrivateKeyLength() + StaticPublicKeyLength();} - unsigned int EphemeralPublicKeyLength() const {return StaticPublicKeyLength();} - - void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const - { - const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); - Integer x(rng, Integer::One(), params.GetMaxExponent()); - x.Encode(privateKey, StaticPrivateKeyLength()); - Element y = params.ExponentiateBase(x); - params.EncodeElement(true, y, privateKey+StaticPrivateKeyLength()); - } - - void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const - { - memcpy(publicKey, privateKey+StaticPrivateKeyLength(), EphemeralPublicKeyLength()); - } - - bool Agree(byte *agreedValue, - const byte *staticPrivateKey, const byte *ephemeralPrivateKey, - const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey, - bool validateStaticOtherPublicKey=true) const - { - try - { - const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); - Element WW = params.DecodeElement(staticOtherPublicKey, validateStaticOtherPublicKey); - Element VV = params.DecodeElement(ephemeralOtherPublicKey, true); - - Integer s(staticPrivateKey, StaticPrivateKeyLength()); - Integer u(ephemeralPrivateKey, StaticPrivateKeyLength()); - Element V = params.DecodeElement(ephemeralPrivateKey+StaticPrivateKeyLength(), false); - - const Integer &r = params.GetSubgroupOrder(); - Integer h2 = Integer::Power2((r.BitCount()+1)/2); - Integer e = ((h2+params.ConvertElementToInteger(V)%h2)*s+u) % r; - Integer tt = h2 + params.ConvertElementToInteger(VV) % h2; - - if (COFACTOR_OPTION::ToEnum() == NO_COFACTOR_MULTIPLICTION) - { - Element P = params.ExponentiateElement(WW, tt); - P = m_groupParameters.MultiplyElements(P, VV); - Element R[2]; - const Integer e2[2] = {r, e}; - params.SimultaneousExponentiate(R, P, e2, 2); - if (!params.IsIdentity(R[0]) || params.IsIdentity(R[1])) - return false; - params.EncodeElement(false, R[1], agreedValue); - } - else - { - const Integer &k = params.GetCofactor(); - if (COFACTOR_OPTION::ToEnum() == COMPATIBLE_COFACTOR_MULTIPLICTION) - e = ModularArithmetic(r).Divide(e, k); - Element P = m_groupParameters.CascadeExponentiate(VV, k*e, WW, k*(e*tt%r)); - if (params.IsIdentity(P)) - return false; - params.EncodeElement(false, P, agreedValue); - } - } - catch (DL_BadElement &) - { - return false; - } - return true; - } - -private: - DL_GroupParameters & AccessAbstractGroupParameters() {return m_groupParameters;} - const DL_GroupParameters & GetAbstractGroupParameters() const {return m_groupParameters;} - - GroupParameters m_groupParameters; -}; - -//! Menezes-Qu-Vanstone in GF(p) with key validation, AKA MQV -typedef MQV_Domain MQV; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/nbtheory.cpp b/CryptoPP/nbtheory.cpp deleted file mode 100644 index 3fdea4e69..000000000 --- a/CryptoPP/nbtheory.cpp +++ /dev/null @@ -1,1123 +0,0 @@ -// nbtheory.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "nbtheory.h" -#include "modarith.h" -#include "algparam.h" - -#include -#include - -#ifdef _OPENMP -// needed in MSVC 2005 to generate correct manifest -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -const word s_lastSmallPrime = 32719; - -struct NewPrimeTable -{ - std::vector * operator()() const - { - const unsigned int maxPrimeTableSize = 3511; - - std::auto_ptr > pPrimeTable(new std::vector); - std::vector &primeTable = *pPrimeTable; - primeTable.reserve(maxPrimeTableSize); - - primeTable.push_back(2); - unsigned int testEntriesEnd = 1; - - for (unsigned int p=3; p<=s_lastSmallPrime; p+=2) - { - unsigned int j; - for (j=1; j &primeTable = Singleton, NewPrimeTable>().Ref(); - size = (unsigned int)primeTable.size(); - return &primeTable[0]; -} - -bool IsSmallPrime(const Integer &p) -{ - unsigned int primeTableSize; - const word16 * primeTable = GetPrimeTable(primeTableSize); - - if (p.IsPositive() && p <= primeTable[primeTableSize-1]) - return std::binary_search(primeTable, primeTable+primeTableSize, (word16)p.ConvertToLong()); - else - return false; -} - -bool TrialDivision(const Integer &p, unsigned bound) -{ - unsigned int primeTableSize; - const word16 * primeTable = GetPrimeTable(primeTableSize); - - assert(primeTable[primeTableSize-1] >= bound); - - unsigned int i; - for (i = 0; primeTable[i]3 && b>1 && b3 && b>1 && b>a; - - Integer z = a_exp_b_mod_c(b, m, n); - if (z==1 || z==nminus1) - return true; - for (unsigned j=1; j3); - - Integer b; - for (unsigned int i=0; i2); - - Integer b=3; - unsigned int i=0; - int j; - - while ((j=Jacobi(b.Squared()-4, n)) == 1) - { - if (++i==64 && n.IsSquare()) // avoid infinite loop if n is a square - return false; - ++b; ++b; - } - - if (j==0) - return false; - else - return Lucas(n+1, b, n)==2; -} - -bool IsStrongLucasProbablePrime(const Integer &n) -{ - if (n <= 1) - return false; - - if (n.IsEven()) - return n==2; - - assert(n>2); - - Integer b=3; - unsigned int i=0; - int j; - - while ((j=Jacobi(b.Squared()-4, n)) == 1) - { - if (++i==64 && n.IsSquare()) // avoid infinite loop if n is a square - return false; - ++b; ++b; - } - - if (j==0) - return false; - - Integer n1 = n+1; - unsigned int a; - - // calculate a = largest power of 2 that divides n1 - for (a=0; ; a++) - if (n1.GetBit(a)) - break; - Integer m = n1>>a; - - Integer z = Lucas(m, b, n); - if (z==2 || z==n-2) - return true; - for (i=1; i().Ref()) - return SmallDivisorsTest(p); - else - return SmallDivisorsTest(p) && IsStrongProbablePrime(p, 3) && IsStrongLucasProbablePrime(p); -} - -bool VerifyPrime(RandomNumberGenerator &rng, const Integer &p, unsigned int level) -{ - bool pass = IsPrime(p) && RabinMillerTest(rng, p, 1); - if (level >= 1) - pass = pass && RabinMillerTest(rng, p, 10); - return pass; -} - -unsigned int PrimeSearchInterval(const Integer &max) -{ - return max.BitCount(); -} - -static inline bool FastProbablePrimeTest(const Integer &n) -{ - return IsStrongProbablePrime(n,2); -} - -AlgorithmParameters MakeParametersForTwoPrimesOfEqualSize(unsigned int productBitLength) -{ - if (productBitLength < 16) - throw InvalidArgument("invalid bit length"); - - Integer minP, maxP; - - if (productBitLength%2==0) - { - minP = Integer(182) << (productBitLength/2-8); - maxP = Integer::Power2(productBitLength/2)-1; - } - else - { - minP = Integer::Power2((productBitLength-1)/2); - maxP = Integer(181) << ((productBitLength+1)/2-8); - } - - return MakeParameters("RandomNumberType", Integer::PRIME)("Min", minP)("Max", maxP); -} - -class PrimeSieve -{ -public: - // delta == 1 or -1 means double sieve with p = 2*q + delta - PrimeSieve(const Integer &first, const Integer &last, const Integer &step, signed int delta=0); - bool NextCandidate(Integer &c); - - void DoSieve(); - static void SieveSingle(std::vector &sieve, word16 p, const Integer &first, const Integer &step, word16 stepInv); - - Integer m_first, m_last, m_step; - signed int m_delta; - word m_next; - std::vector m_sieve; -}; - -PrimeSieve::PrimeSieve(const Integer &first, const Integer &last, const Integer &step, signed int delta) - : m_first(first), m_last(last), m_step(step), m_delta(delta), m_next(0) -{ - DoSieve(); -} - -bool PrimeSieve::NextCandidate(Integer &c) -{ - bool safe = SafeConvert(std::find(m_sieve.begin()+m_next, m_sieve.end(), false) - m_sieve.begin(), m_next); - assert(safe); - if (m_next == m_sieve.size()) - { - m_first += long(m_sieve.size())*m_step; - if (m_first > m_last) - return false; - else - { - m_next = 0; - DoSieve(); - return NextCandidate(c); - } - } - else - { - c = m_first + long(m_next)*m_step; - ++m_next; - return true; - } -} - -void PrimeSieve::SieveSingle(std::vector &sieve, word16 p, const Integer &first, const Integer &step, word16 stepInv) -{ - if (stepInv) - { - size_t sieveSize = sieve.size(); - size_t j = (word32(p-(first%p))*stepInv) % p; - // if the first multiple of p is p, skip it - if (first.WordCount() <= 1 && first + step*long(j) == p) - j += p; - for (; j < sieveSize; j += p) - sieve[j] = true; - } -} - -void PrimeSieve::DoSieve() -{ - unsigned int primeTableSize; - const word16 * primeTable = GetPrimeTable(primeTableSize); - - const unsigned int maxSieveSize = 32768; - unsigned int sieveSize = STDMIN(Integer(maxSieveSize), (m_last-m_first)/m_step+1).ConvertToLong(); - - m_sieve.clear(); - m_sieve.resize(sieveSize, false); - - if (m_delta == 0) - { - for (unsigned int i = 0; i < primeTableSize; ++i) - SieveSingle(m_sieve, primeTable[i], m_first, m_step, (word16)m_step.InverseMod(primeTable[i])); - } - else - { - assert(m_step%2==0); - Integer qFirst = (m_first-m_delta) >> 1; - Integer halfStep = m_step >> 1; - for (unsigned int i = 0; i < primeTableSize; ++i) - { - word16 p = primeTable[i]; - word16 stepInv = (word16)m_step.InverseMod(p); - SieveSingle(m_sieve, p, m_first, m_step, stepInv); - - word16 halfStepInv = 2*stepInv < p ? 2*stepInv : 2*stepInv-p; - SieveSingle(m_sieve, p, qFirst, halfStep, halfStepInv); - } - } -} - -bool FirstPrime(Integer &p, const Integer &max, const Integer &equiv, const Integer &mod, const PrimeSelector *pSelector) -{ - assert(!equiv.IsNegative() && equiv < mod); - - Integer gcd = GCD(equiv, mod); - if (gcd != Integer::One()) - { - // the only possible prime p such that p%mod==equiv where GCD(mod,equiv)!=1 is GCD(mod,equiv) - if (p <= gcd && gcd <= max && IsPrime(gcd) && (!pSelector || pSelector->IsAcceptable(gcd))) - { - p = gcd; - return true; - } - else - return false; - } - - unsigned int primeTableSize; - const word16 * primeTable = GetPrimeTable(primeTableSize); - - if (p <= primeTable[primeTableSize-1]) - { - const word16 *pItr; - - --p; - if (p.IsPositive()) - pItr = std::upper_bound(primeTable, primeTable+primeTableSize, (word)p.ConvertToLong()); - else - pItr = primeTable; - - while (pItr < primeTable+primeTableSize && !(*pItr%mod == equiv && (!pSelector || pSelector->IsAcceptable(*pItr)))) - ++pItr; - - if (pItr < primeTable+primeTableSize) - { - p = *pItr; - return p <= max; - } - - p = primeTable[primeTableSize-1]+1; - } - - assert(p > primeTable[primeTableSize-1]); - - if (mod.IsOdd()) - return FirstPrime(p, max, CRT(equiv, mod, 1, 2, 1), mod<<1, pSelector); - - p += (equiv-p)%mod; - - if (p>max) - return false; - - PrimeSieve sieve(p, max, mod); - - while (sieve.NextCandidate(p)) - { - if ((!pSelector || pSelector->IsAcceptable(p)) && FastProbablePrimeTest(p) && IsPrime(p)) - return true; - } - - return false; -} - -// the following two functions are based on code and comments provided by Preda Mihailescu -static bool ProvePrime(const Integer &p, const Integer &q) -{ - assert(p < q*q*q); - assert(p % q == 1); - -// this is the Quisquater test. Numbers p having passed the Lucas - Lehmer test -// for q and verifying p < q^3 can only be built up of two factors, both = 1 mod q, -// or be prime. The next two lines build the discriminant of a quadratic equation -// which holds iff p is built up of two factors (excercise ... ) - - Integer r = (p-1)/q; - if (((r%q).Squared()-4*(r/q)).IsSquare()) - return false; - - unsigned int primeTableSize; - const word16 * primeTable = GetPrimeTable(primeTableSize); - - assert(primeTableSize >= 50); - for (int i=0; i<50; i++) - { - Integer b = a_exp_b_mod_c(primeTable[i], r, p); - if (b != 1) - return a_exp_b_mod_c(b, q, p) == 1; - } - return false; -} - -Integer MihailescuProvablePrime(RandomNumberGenerator &rng, unsigned int pbits) -{ - Integer p; - Integer minP = Integer::Power2(pbits-1); - Integer maxP = Integer::Power2(pbits) - 1; - - if (maxP <= Integer(s_lastSmallPrime).Squared()) - { - // Randomize() will generate a prime provable by trial division - p.Randomize(rng, minP, maxP, Integer::PRIME); - return p; - } - - unsigned int qbits = (pbits+2)/3 + 1 + rng.GenerateWord32(0, pbits/36); - Integer q = MihailescuProvablePrime(rng, qbits); - Integer q2 = q<<1; - - while (true) - { - // this initializes the sieve to search in the arithmetic - // progression p = p_0 + \lambda * q2 = p_0 + 2 * \lambda * q, - // with q the recursively generated prime above. We will be able - // to use Lucas tets for proving primality. A trick of Quisquater - // allows taking q > cubic_root(p) rather then square_root: this - // decreases the recursion. - - p.Randomize(rng, minP, maxP, Integer::ANY, 1, q2); - PrimeSieve sieve(p, STDMIN(p+PrimeSearchInterval(maxP)*q2, maxP), q2); - - while (sieve.NextCandidate(p)) - { - if (FastProbablePrimeTest(p) && ProvePrime(p, q)) - return p; - } - } - - // not reached - return p; -} - -Integer MaurerProvablePrime(RandomNumberGenerator &rng, unsigned int bits) -{ - const unsigned smallPrimeBound = 29, c_opt=10; - Integer p; - - unsigned int primeTableSize; - const word16 * primeTable = GetPrimeTable(primeTableSize); - - if (bits < smallPrimeBound) - { - do - p.Randomize(rng, Integer::Power2(bits-1), Integer::Power2(bits)-1, Integer::ANY, 1, 2); - while (TrialDivision(p, 1 << ((bits+1)/2))); - } - else - { - const unsigned margin = bits > 50 ? 20 : (bits-10)/2; - double relativeSize; - do - relativeSize = pow(2.0, double(rng.GenerateWord32())/0xffffffff - 1); - while (bits * relativeSize >= bits - margin); - - Integer a,b; - Integer q = MaurerProvablePrime(rng, unsigned(bits*relativeSize)); - Integer I = Integer::Power2(bits-2)/q; - Integer I2 = I << 1; - unsigned int trialDivisorBound = (unsigned int)STDMIN((unsigned long)primeTable[primeTableSize-1], (unsigned long)bits*bits/c_opt); - bool success = false; - while (!success) - { - p.Randomize(rng, I, I2, Integer::ANY); - p *= q; p <<= 1; ++p; - if (!TrialDivision(p, trialDivisorBound)) - { - a.Randomize(rng, 2, p-1, Integer::ANY); - b = a_exp_b_mod_c(a, (p-1)/q, p); - success = (GCD(b-1, p) == 1) && (a_exp_b_mod_c(b, q, p) == 1); - } - } - } - return p; -} - -Integer CRT(const Integer &xp, const Integer &p, const Integer &xq, const Integer &q, const Integer &u) -{ - // isn't operator overloading great? - return p * (u * (xq-xp) % q) + xp; -/* - Integer t1 = xq-xp; - cout << hex << t1 << endl; - Integer t2 = u * t1; - cout << hex << t2 << endl; - Integer t3 = t2 % q; - cout << hex << t3 << endl; - Integer t4 = p * t3; - cout << hex << t4 << endl; - Integer t5 = t4 + xp; - cout << hex << t5 << endl; - return t5; -*/ -} - -Integer ModularSquareRoot(const Integer &a, const Integer &p) -{ - if (p%4 == 3) - return a_exp_b_mod_c(a, (p+1)/4, p); - - Integer q=p-1; - unsigned int r=0; - while (q.IsEven()) - { - r++; - q >>= 1; - } - - Integer n=2; - while (Jacobi(n, p) != -1) - ++n; - - Integer y = a_exp_b_mod_c(n, q, p); - Integer x = a_exp_b_mod_c(a, (q-1)/2, p); - Integer b = (x.Squared()%p)*a%p; - x = a*x%p; - Integer tempb, t; - - while (b != 1) - { - unsigned m=0; - tempb = b; - do - { - m++; - b = b.Squared()%p; - if (m==r) - return Integer::Zero(); - } - while (b != 1); - - t = y; - for (unsigned i=0; i>= 1; - b >>= 1; - k++; - } - - while (a[0]==0) - a >>= 1; - - while (b[0]==0) - b >>= 1; - - while (1) - { - switch (a.Compare(b)) - { - case -1: - b -= a; - while (b[0]==0) - b >>= 1; - break; - - case 0: - return (a <<= k); - - case 1: - a -= b; - while (a[0]==0) - a >>= 1; - break; - - default: - assert(false); - } - } -} - -Integer EuclideanMultiplicativeInverse(const Integer &a, const Integer &b) -{ - assert(b.Positive()); - - if (a.Negative()) - return EuclideanMultiplicativeInverse(a%b, b); - - if (b[0]==0) - { - if (!b || a[0]==0) - return Integer::Zero(); // no inverse - if (a==1) - return 1; - Integer u = EuclideanMultiplicativeInverse(b, a); - if (!u) - return Integer::Zero(); // no inverse - else - return (b*(a-u)+1)/a; - } - - Integer u=1, d=a, v1=b, v3=b, t1, t3, b2=(b+1)>>1; - - if (a[0]) - { - t1 = Integer::Zero(); - t3 = -b; - } - else - { - t1 = b2; - t3 = a>>1; - } - - while (!!t3) - { - while (t3[0]==0) - { - t3 >>= 1; - if (t1[0]==0) - t1 >>= 1; - else - { - t1 >>= 1; - t1 += b2; - } - } - if (t3.Positive()) - { - u = t1; - d = t3; - } - else - { - v1 = b-t1; - v3 = -t3; - } - t1 = u-v1; - t3 = d-v3; - if (t1.Negative()) - t1 += b; - } - if (d==1) - return u; - else - return Integer::Zero(); // no inverse -} -*/ - -int Jacobi(const Integer &aIn, const Integer &bIn) -{ - assert(bIn.IsOdd()); - - Integer b = bIn, a = aIn%bIn; - int result = 1; - - while (!!a) - { - unsigned i=0; - while (a.GetBit(i)==0) - i++; - a>>=i; - - if (i%2==1 && (b%8==3 || b%8==5)) - result = -result; - - if (a%4==3 && b%4==3) - result = -result; - - std::swap(a, b); - a %= b; - } - - return (b==1) ? result : 0; -} - -Integer Lucas(const Integer &e, const Integer &pIn, const Integer &n) -{ - unsigned i = e.BitCount(); - if (i==0) - return Integer::Two(); - - MontgomeryRepresentation m(n); - Integer p=m.ConvertIn(pIn%n), two=m.ConvertIn(Integer::Two()); - Integer v=p, v1=m.Subtract(m.Square(p), two); - - i--; - while (i--) - { - if (e.GetBit(i)) - { - // v = (v*v1 - p) % m; - v = m.Subtract(m.Multiply(v,v1), p); - // v1 = (v1*v1 - 2) % m; - v1 = m.Subtract(m.Square(v1), two); - } - else - { - // v1 = (v*v1 - p) % m; - v1 = m.Subtract(m.Multiply(v,v1), p); - // v = (v*v - 2) % m; - v = m.Subtract(m.Square(v), two); - } - } - return m.ConvertOut(v); -} - -// This is Peter Montgomery's unpublished Lucas sequence evalutation algorithm. -// The total number of multiplies and squares used is less than the binary -// algorithm (see above). Unfortunately I can't get it to run as fast as -// the binary algorithm because of the extra overhead. -/* -Integer Lucas(const Integer &n, const Integer &P, const Integer &modulus) -{ - if (!n) - return 2; - -#define f(A, B, C) m.Subtract(m.Multiply(A, B), C) -#define X2(A) m.Subtract(m.Square(A), two) -#define X3(A) m.Multiply(A, m.Subtract(m.Square(A), three)) - - MontgomeryRepresentation m(modulus); - Integer two=m.ConvertIn(2), three=m.ConvertIn(3); - Integer A=m.ConvertIn(P), B, C, p, d=n, e, r, t, T, U; - - while (d!=1) - { - p = d; - unsigned int b = WORD_BITS * p.WordCount(); - Integer alpha = (Integer(5)<<(2*b-2)).SquareRoot() - Integer::Power2(b-1); - r = (p*alpha)>>b; - e = d-r; - B = A; - C = two; - d = r; - - while (d!=e) - { - if (d>2)) - if ((dm3+em3==0 || dm3+em3==3) && (t = e, t >>= 2, t += e, d <= t)) - { - // #1 -// t = (d+d-e)/3; -// t = d; t += d; t -= e; t /= 3; -// e = (e+e-d)/3; -// e += e; e -= d; e /= 3; -// d = t; - -// t = (d+e)/3 - t = d; t += e; t /= 3; - e -= t; - d -= t; - - T = f(A, B, C); - U = f(T, A, B); - B = f(T, B, A); - A = U; - continue; - } - -// if (dm6 == em6 && d <= e + (e>>2)) - if (dm3 == em3 && dm2 == em2 && (t = e, t >>= 2, t += e, d <= t)) - { - // #2 -// d = (d-e)>>1; - d -= e; d >>= 1; - B = f(A, B, C); - A = X2(A); - continue; - } - -// if (d <= (e<<2)) - if (d <= (t = e, t <<= 2)) - { - // #3 - d -= e; - C = f(A, B, C); - swap(B, C); - continue; - } - - if (dm2 == em2) - { - // #4 -// d = (d-e)>>1; - d -= e; d >>= 1; - B = f(A, B, C); - A = X2(A); - continue; - } - - if (dm2 == 0) - { - // #5 - d >>= 1; - C = f(A, C, B); - A = X2(A); - continue; - } - - if (dm3 == 0) - { - // #6 -// d = d/3 - e; - d /= 3; d -= e; - T = X2(A); - C = f(T, f(A, B, C), C); - swap(B, C); - A = f(T, A, A); - continue; - } - - if (dm3+em3==0 || dm3+em3==3) - { - // #7 -// d = (d-e-e)/3; - d -= e; d -= e; d /= 3; - T = f(A, B, C); - B = f(T, A, B); - A = X3(A); - continue; - } - - if (dm3 == em3) - { - // #8 -// d = (d-e)/3; - d -= e; d /= 3; - T = f(A, B, C); - C = f(A, C, B); - B = T; - A = X3(A); - continue; - } - - assert(em2 == 0); - // #9 - e >>= 1; - C = f(C, B, A); - B = X2(B); - } - - A = f(A, B, C); - } - -#undef f -#undef X2 -#undef X3 - - return m.ConvertOut(A); -} -*/ - -Integer InverseLucas(const Integer &e, const Integer &m, const Integer &p, const Integer &q, const Integer &u) -{ - Integer d = (m*m-4); - Integer p2, q2; - #pragma omp parallel - #pragma omp sections - { - #pragma omp section - { - p2 = p-Jacobi(d,p); - p2 = Lucas(EuclideanMultiplicativeInverse(e,p2), m, p); - } - #pragma omp section - { - q2 = q-Jacobi(d,q); - q2 = Lucas(EuclideanMultiplicativeInverse(e,q2), m, q); - } - } - return CRT(p2, p, q2, q, u); -} - -unsigned int FactoringWorkFactor(unsigned int n) -{ - // extrapolated from the table in Odlyzko's "The Future of Integer Factorization" - // updated to reflect the factoring of RSA-130 - if (n<5) return 0; - else return (unsigned int)(2.4 * pow((double)n, 1.0/3.0) * pow(log(double(n)), 2.0/3.0) - 5); -} - -unsigned int DiscreteLogWorkFactor(unsigned int n) -{ - // assuming discrete log takes about the same time as factoring - if (n<5) return 0; - else return (unsigned int)(2.4 * pow((double)n, 1.0/3.0) * pow(log(double(n)), 2.0/3.0) - 5); -} - -// ******************************************************** - -void PrimeAndGenerator::Generate(signed int delta, RandomNumberGenerator &rng, unsigned int pbits, unsigned int qbits) -{ - // no prime exists for delta = -1, qbits = 4, and pbits = 5 - assert(qbits > 4); - assert(pbits > qbits); - - if (qbits+1 == pbits) - { - Integer minP = Integer::Power2(pbits-1); - Integer maxP = Integer::Power2(pbits) - 1; - bool success = false; - - while (!success) - { - p.Randomize(rng, minP, maxP, Integer::ANY, 6+5*delta, 12); - PrimeSieve sieve(p, STDMIN(p+PrimeSearchInterval(maxP)*12, maxP), 12, delta); - - while (sieve.NextCandidate(p)) - { - assert(IsSmallPrime(p) || SmallDivisorsTest(p)); - q = (p-delta) >> 1; - assert(IsSmallPrime(q) || SmallDivisorsTest(q)); - if (FastProbablePrimeTest(q) && FastProbablePrimeTest(p) && IsPrime(q) && IsPrime(p)) - { - success = true; - break; - } - } - } - - if (delta == 1) - { - // find g such that g is a quadratic residue mod p, then g has order q - // g=4 always works, but this way we get the smallest quadratic residue (other than 1) - for (g=2; Jacobi(g, p) != 1; ++g) {} - // contributed by Walt Tuvell: g should be the following according to the Law of Quadratic Reciprocity - assert((p%8==1 || p%8==7) ? g==2 : (p%12==1 || p%12==11) ? g==3 : g==4); - } - else - { - assert(delta == -1); - // find g such that g*g-4 is a quadratic non-residue, - // and such that g has order q - for (g=3; ; ++g) - if (Jacobi(g*g-4, p)==-1 && Lucas(q, g, p)==2) - break; - } - } - else - { - Integer minQ = Integer::Power2(qbits-1); - Integer maxQ = Integer::Power2(qbits) - 1; - Integer minP = Integer::Power2(pbits-1); - Integer maxP = Integer::Power2(pbits) - 1; - - do - { - q.Randomize(rng, minQ, maxQ, Integer::PRIME); - } while (!p.Randomize(rng, minP, maxP, Integer::PRIME, delta%q, q)); - - // find a random g of order q - if (delta==1) - { - do - { - Integer h(rng, 2, p-2, Integer::ANY); - g = a_exp_b_mod_c(h, (p-1)/q, p); - } while (g <= 1); - assert(a_exp_b_mod_c(g, q, p)==1); - } - else - { - assert(delta==-1); - do - { - Integer h(rng, 3, p-1, Integer::ANY); - if (Jacobi(h*h-4, p)==1) - continue; - g = Lucas((p+1)/q, h, p); - } while (g <= 2); - assert(Lucas(q, g, p) == 2); - } - } -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/nbtheory.h b/CryptoPP/nbtheory.h deleted file mode 100644 index 636479269..000000000 --- a/CryptoPP/nbtheory.h +++ /dev/null @@ -1,131 +0,0 @@ -// nbtheory.h - written and placed in the public domain by Wei Dai - -#ifndef CRYPTOPP_NBTHEORY_H -#define CRYPTOPP_NBTHEORY_H - -#include "integer.h" -#include "algparam.h" - -NAMESPACE_BEGIN(CryptoPP) - -// obtain pointer to small prime table and get its size -CRYPTOPP_DLL const word16 * CRYPTOPP_API GetPrimeTable(unsigned int &size); - -// ************ primality testing **************** - -// generate a provable prime -CRYPTOPP_DLL Integer CRYPTOPP_API MaurerProvablePrime(RandomNumberGenerator &rng, unsigned int bits); -CRYPTOPP_DLL Integer CRYPTOPP_API MihailescuProvablePrime(RandomNumberGenerator &rng, unsigned int bits); - -CRYPTOPP_DLL bool CRYPTOPP_API IsSmallPrime(const Integer &p); - -// returns true if p is divisible by some prime less than bound -// bound not be greater than the largest entry in the prime table -CRYPTOPP_DLL bool CRYPTOPP_API TrialDivision(const Integer &p, unsigned bound); - -// returns true if p is NOT divisible by small primes -CRYPTOPP_DLL bool CRYPTOPP_API SmallDivisorsTest(const Integer &p); - -// These is no reason to use these two, use the ones below instead -CRYPTOPP_DLL bool CRYPTOPP_API IsFermatProbablePrime(const Integer &n, const Integer &b); -CRYPTOPP_DLL bool CRYPTOPP_API IsLucasProbablePrime(const Integer &n); - -CRYPTOPP_DLL bool CRYPTOPP_API IsStrongProbablePrime(const Integer &n, const Integer &b); -CRYPTOPP_DLL bool CRYPTOPP_API IsStrongLucasProbablePrime(const Integer &n); - -// Rabin-Miller primality test, i.e. repeating the strong probable prime test -// for several rounds with random bases -CRYPTOPP_DLL bool CRYPTOPP_API RabinMillerTest(RandomNumberGenerator &rng, const Integer &w, unsigned int rounds); - -// primality test, used to generate primes -CRYPTOPP_DLL bool CRYPTOPP_API IsPrime(const Integer &p); - -// more reliable than IsPrime(), used to verify primes generated by others -CRYPTOPP_DLL bool CRYPTOPP_API VerifyPrime(RandomNumberGenerator &rng, const Integer &p, unsigned int level = 1); - -class CRYPTOPP_DLL PrimeSelector -{ -public: - const PrimeSelector *GetSelectorPointer() const {return this;} - virtual bool IsAcceptable(const Integer &candidate) const =0; -}; - -// use a fast sieve to find the first probable prime in {x | p<=x<=max and x%mod==equiv} -// returns true iff successful, value of p is undefined if no such prime exists -CRYPTOPP_DLL bool CRYPTOPP_API FirstPrime(Integer &p, const Integer &max, const Integer &equiv, const Integer &mod, const PrimeSelector *pSelector); - -CRYPTOPP_DLL unsigned int CRYPTOPP_API PrimeSearchInterval(const Integer &max); - -CRYPTOPP_DLL AlgorithmParameters CRYPTOPP_API MakeParametersForTwoPrimesOfEqualSize(unsigned int productBitLength); - -// ********** other number theoretic functions ************ - -inline Integer GCD(const Integer &a, const Integer &b) - {return Integer::Gcd(a,b);} -inline bool RelativelyPrime(const Integer &a, const Integer &b) - {return Integer::Gcd(a,b) == Integer::One();} -inline Integer LCM(const Integer &a, const Integer &b) - {return a/Integer::Gcd(a,b)*b;} -inline Integer EuclideanMultiplicativeInverse(const Integer &a, const Integer &b) - {return a.InverseMod(b);} - -// use Chinese Remainder Theorem to calculate x given x mod p and x mod q, and u = inverse of p mod q -CRYPTOPP_DLL Integer CRYPTOPP_API CRT(const Integer &xp, const Integer &p, const Integer &xq, const Integer &q, const Integer &u); - -// if b is prime, then Jacobi(a, b) returns 0 if a%b==0, 1 if a is quadratic residue mod b, -1 otherwise -// check a number theory book for what Jacobi symbol means when b is not prime -CRYPTOPP_DLL int CRYPTOPP_API Jacobi(const Integer &a, const Integer &b); - -// calculates the Lucas function V_e(p, 1) mod n -CRYPTOPP_DLL Integer CRYPTOPP_API Lucas(const Integer &e, const Integer &p, const Integer &n); -// calculates x such that m==Lucas(e, x, p*q), p q primes, u=inverse of p mod q -CRYPTOPP_DLL Integer CRYPTOPP_API InverseLucas(const Integer &e, const Integer &m, const Integer &p, const Integer &q, const Integer &u); - -inline Integer ModularExponentiation(const Integer &a, const Integer &e, const Integer &m) - {return a_exp_b_mod_c(a, e, m);} -// returns x such that x*x%p == a, p prime -CRYPTOPP_DLL Integer CRYPTOPP_API ModularSquareRoot(const Integer &a, const Integer &p); -// returns x such that a==ModularExponentiation(x, e, p*q), p q primes, -// and e relatively prime to (p-1)*(q-1) -// dp=d%(p-1), dq=d%(q-1), (d is inverse of e mod (p-1)*(q-1)) -// and u=inverse of p mod q -CRYPTOPP_DLL Integer CRYPTOPP_API ModularRoot(const Integer &a, const Integer &dp, const Integer &dq, const Integer &p, const Integer &q, const Integer &u); - -// find r1 and r2 such that ax^2 + bx + c == 0 (mod p) for x in {r1, r2}, p prime -// returns true if solutions exist -CRYPTOPP_DLL bool CRYPTOPP_API SolveModularQuadraticEquation(Integer &r1, Integer &r2, const Integer &a, const Integer &b, const Integer &c, const Integer &p); - -// returns log base 2 of estimated number of operations to calculate discrete log or factor a number -CRYPTOPP_DLL unsigned int CRYPTOPP_API DiscreteLogWorkFactor(unsigned int bitlength); -CRYPTOPP_DLL unsigned int CRYPTOPP_API FactoringWorkFactor(unsigned int bitlength); - -// ******************************************************** - -//! generator of prime numbers of special forms -class CRYPTOPP_DLL PrimeAndGenerator -{ -public: - PrimeAndGenerator() {} - // generate a random prime p of the form 2*q+delta, where delta is 1 or -1 and q is also prime - // Precondition: pbits > 5 - // warning: this is slow, because primes of this form are harder to find - PrimeAndGenerator(signed int delta, RandomNumberGenerator &rng, unsigned int pbits) - {Generate(delta, rng, pbits, pbits-1);} - // generate a random prime p of the form 2*r*q+delta, where q is also prime - // Precondition: qbits > 4 && pbits > qbits - PrimeAndGenerator(signed int delta, RandomNumberGenerator &rng, unsigned int pbits, unsigned qbits) - {Generate(delta, rng, pbits, qbits);} - - void Generate(signed int delta, RandomNumberGenerator &rng, unsigned int pbits, unsigned qbits); - - const Integer& Prime() const {return p;} - const Integer& SubPrime() const {return q;} - const Integer& Generator() const {return g;} - -private: - Integer p, q, g; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/network.cpp b/CryptoPP/network.cpp deleted file mode 100644 index 9b7198d16..000000000 --- a/CryptoPP/network.cpp +++ /dev/null @@ -1,550 +0,0 @@ -// network.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "network.h" -#include "wait.h" - -#define CRYPTOPP_TRACE_NETWORK 0 - -NAMESPACE_BEGIN(CryptoPP) - -#ifdef HIGHRES_TIMER_AVAILABLE - -lword LimitedBandwidth::ComputeCurrentTransceiveLimit() -{ - if (!m_maxBytesPerSecond) - return ULONG_MAX; - - double curTime = GetCurTimeAndCleanUp(); - lword total = 0; - for (OpQueue::size_type i=0; i!=m_ops.size(); ++i) - total += m_ops[i].second; - return SaturatingSubtract(m_maxBytesPerSecond, total); -} - -double LimitedBandwidth::TimeToNextTransceive() -{ - if (!m_maxBytesPerSecond) - return 0; - - if (!m_nextTransceiveTime) - ComputeNextTransceiveTime(); - - return SaturatingSubtract(m_nextTransceiveTime, m_timer.ElapsedTimeAsDouble()); -} - -void LimitedBandwidth::NoteTransceive(lword size) -{ - if (m_maxBytesPerSecond) - { - double curTime = GetCurTimeAndCleanUp(); - m_ops.push_back(std::make_pair(curTime, size)); - m_nextTransceiveTime = 0; - } -} - -void LimitedBandwidth::ComputeNextTransceiveTime() -{ - double curTime = GetCurTimeAndCleanUp(); - lword total = 0; - for (unsigned int i=0; i!=m_ops.size(); ++i) - total += m_ops[i].second; - m_nextTransceiveTime = - (total < m_maxBytesPerSecond) ? curTime : m_ops.front().first + 1000; -} - -double LimitedBandwidth::GetCurTimeAndCleanUp() -{ - if (!m_maxBytesPerSecond) - return 0; - - double curTime = m_timer.ElapsedTimeAsDouble(); - while (m_ops.size() && (m_ops.front().first + 1000 < curTime)) - m_ops.pop_front(); - return curTime; -} - -void LimitedBandwidth::GetWaitObjects(WaitObjectContainer &container, const CallStack &callStack) -{ - double nextTransceiveTime = TimeToNextTransceive(); - if (nextTransceiveTime) - container.ScheduleEvent(nextTransceiveTime, CallStack("LimitedBandwidth::GetWaitObjects()", &callStack)); -} - -// ************************************************************* - -size_t NonblockingSource::GeneralPump2( - lword& byteCount, bool blockingOutput, - unsigned long maxTime, bool checkDelimiter, byte delimiter) -{ - m_blockedBySpeedLimit = false; - - if (!GetMaxBytesPerSecond()) - { - size_t ret = DoPump(byteCount, blockingOutput, maxTime, checkDelimiter, delimiter); - m_doPumpBlocked = (ret != 0); - return ret; - } - - bool forever = (maxTime == INFINITE_TIME); - unsigned long timeToGo = maxTime; - Timer timer(Timer::MILLISECONDS, forever); - lword maxSize = byteCount; - byteCount = 0; - - timer.StartTimer(); - - while (true) - { - lword curMaxSize = UnsignedMin(ComputeCurrentTransceiveLimit(), maxSize - byteCount); - - if (curMaxSize || m_doPumpBlocked) - { - if (!forever) timeToGo = SaturatingSubtract(maxTime, timer.ElapsedTime()); - size_t ret = DoPump(curMaxSize, blockingOutput, timeToGo, checkDelimiter, delimiter); - m_doPumpBlocked = (ret != 0); - if (curMaxSize) - { - NoteTransceive(curMaxSize); - byteCount += curMaxSize; - } - if (ret) - return ret; - } - - if (maxSize != ULONG_MAX && byteCount >= maxSize) - break; - - if (!forever) - { - timeToGo = SaturatingSubtract(maxTime, timer.ElapsedTime()); - if (!timeToGo) - break; - } - - double waitTime = TimeToNextTransceive(); - if (!forever && waitTime > timeToGo) - { - m_blockedBySpeedLimit = true; - break; - } - - WaitObjectContainer container; - LimitedBandwidth::GetWaitObjects(container, CallStack("NonblockingSource::GeneralPump2() - speed limit", 0)); - container.Wait((unsigned long)waitTime); - } - - return 0; -} - -size_t NonblockingSource::PumpMessages2(unsigned int &messageCount, bool blocking) -{ - if (messageCount == 0) - return 0; - - messageCount = 0; - - lword byteCount; - do { - byteCount = LWORD_MAX; - RETURN_IF_NONZERO(Pump2(byteCount, blocking)); - } while(byteCount == LWORD_MAX); - - if (!m_messageEndSent && SourceExhausted()) - { - RETURN_IF_NONZERO(AttachedTransformation()->Put2(NULL, 0, GetAutoSignalPropagation(), true)); - m_messageEndSent = true; - messageCount = 1; - } - return 0; -} - -lword NonblockingSink::TimedFlush(unsigned long maxTime, size_t targetSize) -{ - m_blockedBySpeedLimit = false; - - size_t curBufSize = GetCurrentBufferSize(); - if (curBufSize <= targetSize && (targetSize || !EofPending())) - return 0; - - if (!GetMaxBytesPerSecond()) - return DoFlush(maxTime, targetSize); - - bool forever = (maxTime == INFINITE_TIME); - unsigned long timeToGo = maxTime; - Timer timer(Timer::MILLISECONDS, forever); - lword totalFlushed = 0; - - timer.StartTimer(); - - while (true) - { - size_t flushSize = UnsignedMin(curBufSize - targetSize, ComputeCurrentTransceiveLimit()); - if (flushSize || EofPending()) - { - if (!forever) timeToGo = SaturatingSubtract(maxTime, timer.ElapsedTime()); - size_t ret = (size_t)DoFlush(timeToGo, curBufSize - flushSize); - if (ret) - { - NoteTransceive(ret); - curBufSize -= ret; - totalFlushed += ret; - } - } - - if (curBufSize <= targetSize && (targetSize || !EofPending())) - break; - - if (!forever) - { - timeToGo = SaturatingSubtract(maxTime, timer.ElapsedTime()); - if (!timeToGo) - break; - } - - double waitTime = TimeToNextTransceive(); - if (!forever && waitTime > timeToGo) - { - m_blockedBySpeedLimit = true; - break; - } - - WaitObjectContainer container; - LimitedBandwidth::GetWaitObjects(container, CallStack("NonblockingSink::TimedFlush() - speed limit", 0)); - container.Wait((unsigned long)waitTime); - } - - return totalFlushed; -} - -bool NonblockingSink::IsolatedFlush(bool hardFlush, bool blocking) -{ - TimedFlush(blocking ? INFINITE_TIME : 0); - return hardFlush && (!!GetCurrentBufferSize() || EofPending()); -} - -// ************************************************************* - -NetworkSource::NetworkSource(BufferedTransformation *attachment) - : NonblockingSource(attachment), m_buf(1024*16) - , m_waitingForResult(false), m_outputBlocked(false) - , m_dataBegin(0), m_dataEnd(0) -{ -} - -unsigned int NetworkSource::GetMaxWaitObjectCount() const -{ - return LimitedBandwidth::GetMaxWaitObjectCount() - + GetReceiver().GetMaxWaitObjectCount() - + AttachedTransformation()->GetMaxWaitObjectCount(); -} - -void NetworkSource::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) -{ - if (BlockedBySpeedLimit()) - LimitedBandwidth::GetWaitObjects(container, CallStack("NetworkSource::GetWaitObjects() - speed limit", &callStack)); - else if (!m_outputBlocked) - { - if (m_dataBegin == m_dataEnd) - AccessReceiver().GetWaitObjects(container, CallStack("NetworkSource::GetWaitObjects() - no data", &callStack)); - else - container.SetNoWait(CallStack("NetworkSource::GetWaitObjects() - have data", &callStack)); - } - - AttachedTransformation()->GetWaitObjects(container, CallStack("NetworkSource::GetWaitObjects() - attachment", &callStack)); -} - -size_t NetworkSource::DoPump(lword &byteCount, bool blockingOutput, unsigned long maxTime, bool checkDelimiter, byte delimiter) -{ - NetworkReceiver &receiver = AccessReceiver(); - - lword maxSize = byteCount; - byteCount = 0; - bool forever = maxTime == INFINITE_TIME; - Timer timer(Timer::MILLISECONDS, forever); - BufferedTransformation *t = AttachedTransformation(); - - if (m_outputBlocked) - goto DoOutput; - - while (true) - { - if (m_dataBegin == m_dataEnd) - { - if (receiver.EofReceived()) - break; - - if (m_waitingForResult) - { - if (receiver.MustWaitForResult() && - !receiver.Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()), - CallStack("NetworkSource::DoPump() - wait receive result", 0))) - break; - - unsigned int recvResult = receiver.GetReceiveResult(); -#if CRYPTOPP_TRACE_NETWORK - OutputDebugString((IntToString((unsigned int)this) + ": Received " + IntToString(recvResult) + " bytes\n").c_str()); -#endif - m_dataEnd += recvResult; - m_waitingForResult = false; - - if (!receiver.MustWaitToReceive() && !receiver.EofReceived() && m_dataEnd != m_buf.size()) - goto ReceiveNoWait; - } - else - { - m_dataEnd = m_dataBegin = 0; - - if (receiver.MustWaitToReceive()) - { - if (!receiver.Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()), - CallStack("NetworkSource::DoPump() - wait receive", 0))) - break; - - receiver.Receive(m_buf+m_dataEnd, m_buf.size()-m_dataEnd); - m_waitingForResult = true; - } - else - { -ReceiveNoWait: - m_waitingForResult = true; - // call Receive repeatedly as long as data is immediately available, - // because some receivers tend to return data in small pieces -#if CRYPTOPP_TRACE_NETWORK - OutputDebugString((IntToString((unsigned int)this) + ": Receiving " + IntToString(m_buf.size()-m_dataEnd) + " bytes\n").c_str()); -#endif - while (receiver.Receive(m_buf+m_dataEnd, m_buf.size()-m_dataEnd)) - { - unsigned int recvResult = receiver.GetReceiveResult(); -#if CRYPTOPP_TRACE_NETWORK - OutputDebugString((IntToString((unsigned int)this) + ": Received " + IntToString(recvResult) + " bytes\n").c_str()); -#endif - m_dataEnd += recvResult; - if (receiver.EofReceived() || m_dataEnd > m_buf.size() /2) - { - m_waitingForResult = false; - break; - } - } - } - } - } - else - { - m_putSize = UnsignedMin(m_dataEnd - m_dataBegin, maxSize - byteCount); - - if (checkDelimiter) - m_putSize = std::find(m_buf+m_dataBegin, m_buf+m_dataBegin+m_putSize, delimiter) - (m_buf+m_dataBegin); - -DoOutput: - size_t result = t->PutModifiable2(m_buf+m_dataBegin, m_putSize, 0, forever || blockingOutput); - if (result) - { - if (t->Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()), - CallStack("NetworkSource::DoPump() - wait attachment", 0))) - goto DoOutput; - else - { - m_outputBlocked = true; - return result; - } - } - m_outputBlocked = false; - - byteCount += m_putSize; - m_dataBegin += m_putSize; - if (checkDelimiter && m_dataBegin < m_dataEnd && m_buf[m_dataBegin] == delimiter) - break; - if (maxSize != ULONG_MAX && byteCount == maxSize) - break; - // once time limit is reached, return even if there is more data waiting - // but make 0 a special case so caller can request a large amount of data to be - // pumped as long as it is immediately available - if (maxTime > 0 && timer.ElapsedTime() > maxTime) - break; - } - } - - return 0; -} - -// ************************************************************* - -NetworkSink::NetworkSink(unsigned int maxBufferSize, unsigned int autoFlushBound) - : m_maxBufferSize(maxBufferSize), m_autoFlushBound(autoFlushBound) - , m_needSendResult(false), m_wasBlocked(false), m_eofState(EOF_NONE) - , m_buffer(STDMIN(16U*1024U+256, maxBufferSize)), m_skipBytes(0) - , m_speedTimer(Timer::MILLISECONDS), m_byteCountSinceLastTimerReset(0) - , m_currentSpeed(0), m_maxObservedSpeed(0) -{ -} - -float NetworkSink::ComputeCurrentSpeed() -{ - if (m_speedTimer.ElapsedTime() > 1000) - { - m_currentSpeed = m_byteCountSinceLastTimerReset * 1000 / m_speedTimer.ElapsedTime(); - m_maxObservedSpeed = STDMAX(m_currentSpeed, m_maxObservedSpeed * 0.98f); - m_byteCountSinceLastTimerReset = 0; - m_speedTimer.StartTimer(); -// OutputDebugString(("max speed: " + IntToString((int)m_maxObservedSpeed) + " current speed: " + IntToString((int)m_currentSpeed) + "\n").c_str()); - } - return m_currentSpeed; -} - -float NetworkSink::GetMaxObservedSpeed() const -{ - lword m = GetMaxBytesPerSecond(); - return m ? STDMIN(m_maxObservedSpeed, float(CRYPTOPP_VC6_INT64 m)) : m_maxObservedSpeed; -} - -unsigned int NetworkSink::GetMaxWaitObjectCount() const -{ - return LimitedBandwidth::GetMaxWaitObjectCount() + GetSender().GetMaxWaitObjectCount(); -} - -void NetworkSink::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) -{ - if (BlockedBySpeedLimit()) - LimitedBandwidth::GetWaitObjects(container, CallStack("NetworkSink::GetWaitObjects() - speed limit", &callStack)); - else if (m_wasBlocked) - AccessSender().GetWaitObjects(container, CallStack("NetworkSink::GetWaitObjects() - was blocked", &callStack)); - else if (!m_buffer.IsEmpty()) - AccessSender().GetWaitObjects(container, CallStack("NetworkSink::GetWaitObjects() - buffer not empty", &callStack)); - else if (EofPending()) - AccessSender().GetWaitObjects(container, CallStack("NetworkSink::GetWaitObjects() - EOF pending", &callStack)); -} - -size_t NetworkSink::Put2(const byte *inString, size_t length, int messageEnd, bool blocking) -{ - if (m_eofState == EOF_DONE) - { - if (length || messageEnd) - throw Exception(Exception::OTHER_ERROR, "NetworkSink::Put2() being called after EOF had been sent"); - - return 0; - } - - if (m_eofState > EOF_NONE) - goto EofSite; - - { - if (m_skipBytes) - { - assert(length >= m_skipBytes); - inString += m_skipBytes; - length -= m_skipBytes; - } - - m_buffer.Put(inString, length); - - if (!blocking || m_buffer.CurrentSize() > m_autoFlushBound) - TimedFlush(0, 0); - - size_t targetSize = messageEnd ? 0 : m_maxBufferSize; - if (blocking) - TimedFlush(INFINITE_TIME, targetSize); - - if (m_buffer.CurrentSize() > targetSize) - { - assert(!blocking); - m_wasBlocked = true; - m_skipBytes += length; - size_t blockedBytes = UnsignedMin(length, m_buffer.CurrentSize() - targetSize); - return STDMAX(blockedBytes, 1); - } - - m_wasBlocked = false; - m_skipBytes = 0; - } - - if (messageEnd) - { - m_eofState = EOF_PENDING_SEND; - - EofSite: - TimedFlush(blocking ? INFINITE_TIME : 0, 0); - if (m_eofState != EOF_DONE) - return 1; - } - - return 0; -} - -lword NetworkSink::DoFlush(unsigned long maxTime, size_t targetSize) -{ - NetworkSender &sender = AccessSender(); - - bool forever = maxTime == INFINITE_TIME; - Timer timer(Timer::MILLISECONDS, forever); - unsigned int totalFlushSize = 0; - - while (true) - { - if (m_buffer.CurrentSize() <= targetSize) - break; - - if (m_needSendResult) - { - if (sender.MustWaitForResult() && - !sender.Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()), - CallStack("NetworkSink::DoFlush() - wait send result", 0))) - break; - - unsigned int sendResult = sender.GetSendResult(); -#if CRYPTOPP_TRACE_NETWORK - OutputDebugString((IntToString((unsigned int)this) + ": Sent " + IntToString(sendResult) + " bytes\n").c_str()); -#endif - m_buffer.Skip(sendResult); - totalFlushSize += sendResult; - m_needSendResult = false; - - if (!m_buffer.AnyRetrievable()) - break; - } - - unsigned long timeOut = maxTime ? SaturatingSubtract(maxTime, timer.ElapsedTime()) : 0; - if (sender.MustWaitToSend() && !sender.Wait(timeOut, CallStack("NetworkSink::DoFlush() - wait send", 0))) - break; - - size_t contiguousSize = 0; - const byte *block = m_buffer.Spy(contiguousSize); - -#if CRYPTOPP_TRACE_NETWORK - OutputDebugString((IntToString((unsigned int)this) + ": Sending " + IntToString(contiguousSize) + " bytes\n").c_str()); -#endif - sender.Send(block, contiguousSize); - m_needSendResult = true; - - if (maxTime > 0 && timeOut == 0) - break; // once time limit is reached, return even if there is more data waiting - } - - m_byteCountSinceLastTimerReset += totalFlushSize; - ComputeCurrentSpeed(); - - if (m_buffer.IsEmpty() && !m_needSendResult) - { - if (m_eofState == EOF_PENDING_SEND) - { - sender.SendEof(); - m_eofState = sender.MustWaitForEof() ? EOF_PENDING_DELIVERY : EOF_DONE; - } - - while (m_eofState == EOF_PENDING_DELIVERY) - { - unsigned long timeOut = maxTime ? SaturatingSubtract(maxTime, timer.ElapsedTime()) : 0; - if (!sender.Wait(timeOut, CallStack("NetworkSink::DoFlush() - wait EOF", 0))) - break; - - if (sender.EofSent()) - m_eofState = EOF_DONE; - } - } - - return totalFlushSize; -} - -#endif // #ifdef HIGHRES_TIMER_AVAILABLE - -NAMESPACE_END diff --git a/CryptoPP/network.h b/CryptoPP/network.h deleted file mode 100644 index 96cd4567e..000000000 --- a/CryptoPP/network.h +++ /dev/null @@ -1,235 +0,0 @@ -#ifndef CRYPTOPP_NETWORK_H -#define CRYPTOPP_NETWORK_H - -#include "config.h" - -#ifdef HIGHRES_TIMER_AVAILABLE - -#include "filters.h" -#include "hrtimer.h" - -#include - -NAMESPACE_BEGIN(CryptoPP) - -class LimitedBandwidth -{ -public: - LimitedBandwidth(lword maxBytesPerSecond = 0) - : m_maxBytesPerSecond(maxBytesPerSecond), m_timer(Timer::MILLISECONDS) - , m_nextTransceiveTime(0) - { m_timer.StartTimer(); } - - lword GetMaxBytesPerSecond() const - { return m_maxBytesPerSecond; } - - void SetMaxBytesPerSecond(lword v) - { m_maxBytesPerSecond = v; } - - lword ComputeCurrentTransceiveLimit(); - - double TimeToNextTransceive(); - - void NoteTransceive(lword size); - -public: - /*! GetWaitObjects() must be called despite the 0 return from GetMaxWaitObjectCount(); - the 0 is because the ScheduleEvent() method is used instead of adding a wait object */ - unsigned int GetMaxWaitObjectCount() const { return 0; } - void GetWaitObjects(WaitObjectContainer &container, const CallStack &callStack); - -private: - lword m_maxBytesPerSecond; - - typedef std::deque > OpQueue; - OpQueue m_ops; - - Timer m_timer; - double m_nextTransceiveTime; - - void ComputeNextTransceiveTime(); - double GetCurTimeAndCleanUp(); -}; - -//! a Source class that can pump from a device for a specified amount of time. -class CRYPTOPP_NO_VTABLE NonblockingSource : public AutoSignaling, public LimitedBandwidth -{ -public: - NonblockingSource(BufferedTransformation *attachment) - : m_messageEndSent(false) , m_doPumpBlocked(false), m_blockedBySpeedLimit(false) {Detach(attachment);} - - //! \name NONBLOCKING SOURCE - //@{ - - //! pump up to maxSize bytes using at most maxTime milliseconds - /*! If checkDelimiter is true, pump up to delimiter, which itself is not extracted or pumped. */ - size_t GeneralPump2(lword &byteCount, bool blockingOutput=true, unsigned long maxTime=INFINITE_TIME, bool checkDelimiter=false, byte delimiter='\n'); - - lword GeneralPump(lword maxSize=LWORD_MAX, unsigned long maxTime=INFINITE_TIME, bool checkDelimiter=false, byte delimiter='\n') - { - GeneralPump2(maxSize, true, maxTime, checkDelimiter, delimiter); - return maxSize; - } - lword TimedPump(unsigned long maxTime) - {return GeneralPump(LWORD_MAX, maxTime);} - lword PumpLine(byte delimiter='\n', lword maxSize=1024) - {return GeneralPump(maxSize, INFINITE_TIME, true, delimiter);} - - size_t Pump2(lword &byteCount, bool blocking=true) - {return GeneralPump2(byteCount, blocking, blocking ? INFINITE_TIME : 0);} - size_t PumpMessages2(unsigned int &messageCount, bool blocking=true); - //@} - -protected: - virtual size_t DoPump(lword &byteCount, bool blockingOutput, - unsigned long maxTime, bool checkDelimiter, byte delimiter) =0; - - bool BlockedBySpeedLimit() const { return m_blockedBySpeedLimit; } - -private: - bool m_messageEndSent, m_doPumpBlocked, m_blockedBySpeedLimit; -}; - -//! Network Receiver -class CRYPTOPP_NO_VTABLE NetworkReceiver : public Waitable -{ -public: - virtual bool MustWaitToReceive() {return false;} - virtual bool MustWaitForResult() {return false;} - //! receive data from network source, returns whether result is immediately available - virtual bool Receive(byte* buf, size_t bufLen) =0; - virtual unsigned int GetReceiveResult() =0; - virtual bool EofReceived() const =0; -}; - -class CRYPTOPP_NO_VTABLE NonblockingSinkInfo -{ -public: - virtual ~NonblockingSinkInfo() {} - virtual size_t GetMaxBufferSize() const =0; - virtual size_t GetCurrentBufferSize() const =0; - virtual bool EofPending() const =0; - //! compute the current speed of this sink in bytes per second - virtual float ComputeCurrentSpeed() =0; - //! get the maximum observed speed of this sink in bytes per second - virtual float GetMaxObservedSpeed() const =0; -}; - -//! a Sink class that queues input and can flush to a device for a specified amount of time. -class CRYPTOPP_NO_VTABLE NonblockingSink : public Sink, public NonblockingSinkInfo, public LimitedBandwidth -{ -public: - NonblockingSink() : m_blockedBySpeedLimit(false) {} - - bool IsolatedFlush(bool hardFlush, bool blocking); - - //! flush to device for no more than maxTime milliseconds - /*! This function will repeatedly attempt to flush data to some device, until - the queue is empty, or a total of maxTime milliseconds have elapsed. - If maxTime == 0, at least one attempt will be made to flush some data, but - it is likely that not all queued data will be flushed, even if the device - is ready to receive more data without waiting. If you want to flush as much data - as possible without waiting for the device, call this function in a loop. - For example: while (sink.TimedFlush(0) > 0) {} - \return number of bytes flushed - */ - lword TimedFlush(unsigned long maxTime, size_t targetSize = 0); - - virtual void SetMaxBufferSize(size_t maxBufferSize) =0; - //! set a bound which will cause sink to flush if exceeded by GetCurrentBufferSize() - virtual void SetAutoFlushBound(size_t bound) =0; - -protected: - virtual lword DoFlush(unsigned long maxTime, size_t targetSize) = 0; - - bool BlockedBySpeedLimit() const { return m_blockedBySpeedLimit; } - -private: - bool m_blockedBySpeedLimit; -}; - -//! Network Sender -class CRYPTOPP_NO_VTABLE NetworkSender : public Waitable -{ -public: - virtual bool MustWaitToSend() {return false;} - virtual bool MustWaitForResult() {return false;} - virtual void Send(const byte* buf, size_t bufLen) =0; - virtual unsigned int GetSendResult() =0; - virtual bool MustWaitForEof() {return false;} - virtual void SendEof() =0; - virtual bool EofSent() {return false;} // implement if MustWaitForEof() == true -}; - -//! Network Source -class CRYPTOPP_NO_VTABLE NetworkSource : public NonblockingSource -{ -public: - NetworkSource(BufferedTransformation *attachment); - - unsigned int GetMaxWaitObjectCount() const; - void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); - - bool SourceExhausted() const {return m_dataBegin == m_dataEnd && GetReceiver().EofReceived();} - -protected: - size_t DoPump(lword &byteCount, bool blockingOutput, unsigned long maxTime, bool checkDelimiter, byte delimiter); - - virtual NetworkReceiver & AccessReceiver() =0; - const NetworkReceiver & GetReceiver() const {return const_cast(this)->AccessReceiver();} - -private: - SecByteBlock m_buf; - size_t m_putSize, m_dataBegin, m_dataEnd; - bool m_waitingForResult, m_outputBlocked; -}; - -//! Network Sink -class CRYPTOPP_NO_VTABLE NetworkSink : public NonblockingSink -{ -public: - NetworkSink(unsigned int maxBufferSize, unsigned int autoFlushBound); - - unsigned int GetMaxWaitObjectCount() const; - void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); - - size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking); - - void SetMaxBufferSize(size_t maxBufferSize) {m_maxBufferSize = maxBufferSize; m_buffer.SetNodeSize(UnsignedMin(maxBufferSize, 16U*1024U+256U));} - void SetAutoFlushBound(size_t bound) {m_autoFlushBound = bound;} - - size_t GetMaxBufferSize() const {return m_maxBufferSize;} - size_t GetCurrentBufferSize() const {return (size_t)m_buffer.CurrentSize();} - - void ClearBuffer() { m_buffer.Clear(); } - - bool EofPending() const { return m_eofState > EOF_NONE && m_eofState < EOF_DONE; } - - //! compute the current speed of this sink in bytes per second - float ComputeCurrentSpeed(); - //! get the maximum observed speed of this sink in bytes per second - float GetMaxObservedSpeed() const; - -protected: - lword DoFlush(unsigned long maxTime, size_t targetSize); - - virtual NetworkSender & AccessSender() =0; - const NetworkSender & GetSender() const {return const_cast(this)->AccessSender();} - -private: - enum EofState { EOF_NONE, EOF_PENDING_SEND, EOF_PENDING_DELIVERY, EOF_DONE }; - - size_t m_maxBufferSize, m_autoFlushBound; - bool m_needSendResult, m_wasBlocked; - EofState m_eofState; - ByteQueue m_buffer; - size_t m_skipBytes; - Timer m_speedTimer; - float m_byteCountSinceLastTimerReset, m_currentSpeed, m_maxObservedSpeed; -}; - -NAMESPACE_END - -#endif // #ifdef HIGHRES_TIMER_AVAILABLE - -#endif diff --git a/CryptoPP/nr.h b/CryptoPP/nr.h deleted file mode 100644 index c398e3550..000000000 --- a/CryptoPP/nr.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef CRYPTOPP_NR_H -#define CRYPTOPP_NR_H - -#include "gfpcrypt.h" - -#endif diff --git a/CryptoPP/oaep.cpp b/CryptoPP/oaep.cpp deleted file mode 100644 index 1d474be52..000000000 --- a/CryptoPP/oaep.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// oaep.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "oaep.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -// ******************************************************** - -size_t OAEP_Base::MaxUnpaddedLength(size_t paddedLength) const -{ - return SaturatingSubtract(paddedLength/8, 1+2*DigestSize()); -} - -void OAEP_Base::Pad(RandomNumberGenerator &rng, const byte *input, size_t inputLength, byte *oaepBlock, size_t oaepBlockLen, const NameValuePairs ¶meters) const -{ - assert (inputLength <= MaxUnpaddedLength(oaepBlockLen)); - - // convert from bit length to byte length - if (oaepBlockLen % 8 != 0) - { - oaepBlock[0] = 0; - oaepBlock++; - } - oaepBlockLen /= 8; - - std::auto_ptr pHash(NewHash()); - const size_t hLen = pHash->DigestSize(); - const size_t seedLen = hLen, dbLen = oaepBlockLen-seedLen; - byte *const maskedSeed = oaepBlock; - byte *const maskedDB = oaepBlock+seedLen; - - ConstByteArrayParameter encodingParameters; - parameters.GetValue(Name::EncodingParameters(), encodingParameters); - - // DB = pHash || 00 ... || 01 || M - pHash->CalculateDigest(maskedDB, encodingParameters.begin(), encodingParameters.size()); - memset(maskedDB+hLen, 0, dbLen-hLen-inputLength-1); - maskedDB[dbLen-inputLength-1] = 0x01; - memcpy(maskedDB+dbLen-inputLength, input, inputLength); - - rng.GenerateBlock(maskedSeed, seedLen); - std::auto_ptr pMGF(NewMGF()); - pMGF->GenerateAndMask(*pHash, maskedDB, dbLen, maskedSeed, seedLen); - pMGF->GenerateAndMask(*pHash, maskedSeed, seedLen, maskedDB, dbLen); -} - -DecodingResult OAEP_Base::Unpad(const byte *oaepBlock, size_t oaepBlockLen, byte *output, const NameValuePairs ¶meters) const -{ - bool invalid = false; - - // convert from bit length to byte length - if (oaepBlockLen % 8 != 0) - { - invalid = (oaepBlock[0] != 0) || invalid; - oaepBlock++; - } - oaepBlockLen /= 8; - - std::auto_ptr pHash(NewHash()); - const size_t hLen = pHash->DigestSize(); - const size_t seedLen = hLen, dbLen = oaepBlockLen-seedLen; - - invalid = (oaepBlockLen < 2*hLen+1) || invalid; - - SecByteBlock t(oaepBlock, oaepBlockLen); - byte *const maskedSeed = t; - byte *const maskedDB = t+seedLen; - - std::auto_ptr pMGF(NewMGF()); - pMGF->GenerateAndMask(*pHash, maskedSeed, seedLen, maskedDB, dbLen); - pMGF->GenerateAndMask(*pHash, maskedDB, dbLen, maskedSeed, seedLen); - - ConstByteArrayParameter encodingParameters; - parameters.GetValue(Name::EncodingParameters(), encodingParameters); - - // DB = pHash' || 00 ... || 01 || M - byte *M = std::find(maskedDB+hLen, maskedDB+dbLen, 0x01); - invalid = (M == maskedDB+dbLen) || invalid; - invalid = (std::find_if(maskedDB+hLen, M, std::bind2nd(std::not_equal_to(), 0)) != M) || invalid; - invalid = !pHash->VerifyDigest(maskedDB, encodingParameters.begin(), encodingParameters.size()) || invalid; - - if (invalid) - return DecodingResult(); - - M++; - memcpy(output, M, maskedDB+dbLen-M); - return DecodingResult(maskedDB+dbLen-M); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/oaep.h b/CryptoPP/oaep.h deleted file mode 100644 index 4bf6b0d83..000000000 --- a/CryptoPP/oaep.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef CRYPTOPP_OAEP_H -#define CRYPTOPP_OAEP_H - -#include "pubkey.h" -#include "sha.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -class CRYPTOPP_DLL OAEP_Base : public PK_EncryptionMessageEncodingMethod -{ -public: - bool ParameterSupported(const char *name) const {return strcmp(name, Name::EncodingParameters()) == 0;} - size_t MaxUnpaddedLength(size_t paddedLength) const; - void Pad(RandomNumberGenerator &rng, const byte *raw, size_t inputLength, byte *padded, size_t paddedLength, const NameValuePairs ¶meters) const; - DecodingResult Unpad(const byte *padded, size_t paddedLength, byte *raw, const NameValuePairs ¶meters) const; - -protected: - virtual unsigned int DigestSize() const =0; - virtual HashTransformation * NewHash() const =0; - virtual MaskGeneratingFunction * NewMGF() const =0; -}; - -//! EME-OAEP, for use with classes derived from TF_ES -template -class OAEP : public OAEP_Base, public EncryptionStandard -{ -public: - static std::string CRYPTOPP_API StaticAlgorithmName() {return std::string("OAEP-") + MGF::StaticAlgorithmName() + "(" + H::StaticAlgorithmName() + ")";} - typedef OAEP EncryptionMessageEncodingMethod; - -protected: - unsigned int DigestSize() const {return H::DIGESTSIZE;} - HashTransformation * NewHash() const {return new H;} - MaskGeneratingFunction * NewMGF() const {return new MGF;} -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS OAEP; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/oids.h b/CryptoPP/oids.h deleted file mode 100644 index 8b1030150..000000000 --- a/CryptoPP/oids.h +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef CRYPTOPP_OIDS_H -#define CRYPTOPP_OIDS_H - -// crypto-related ASN.1 object identifiers - -#include "asn.h" - -NAMESPACE_BEGIN(CryptoPP) - -NAMESPACE_BEGIN(ASN1) - -#define DEFINE_OID(value, name) inline OID name() {return value;} - -DEFINE_OID(1, iso) - DEFINE_OID(iso()+2, member_body) - DEFINE_OID(member_body()+840, iso_us) - DEFINE_OID(iso_us()+10040, ansi_x9_57) - DEFINE_OID(ansi_x9_57()+4+1, id_dsa) - DEFINE_OID(iso_us()+10045, ansi_x9_62) - DEFINE_OID(ansi_x9_62()+1, id_fieldType) - DEFINE_OID(id_fieldType()+1, prime_field) - DEFINE_OID(id_fieldType()+2, characteristic_two_field) - DEFINE_OID(characteristic_two_field()+3, id_characteristic_two_basis) - DEFINE_OID(id_characteristic_two_basis()+1, gnBasis) - DEFINE_OID(id_characteristic_two_basis()+2, tpBasis) - DEFINE_OID(id_characteristic_two_basis()+3, ppBasis) - DEFINE_OID(ansi_x9_62()+2, id_publicKeyType) - DEFINE_OID(id_publicKeyType()+1, id_ecPublicKey) - DEFINE_OID(ansi_x9_62()+3, ansi_x9_62_curves) - DEFINE_OID(ansi_x9_62_curves()+1, ansi_x9_62_curves_prime) - DEFINE_OID(ansi_x9_62_curves_prime()+1, secp192r1) - DEFINE_OID(ansi_x9_62_curves_prime()+7, secp256r1) - DEFINE_OID(iso_us()+113549, rsadsi) - DEFINE_OID(rsadsi()+1, pkcs) - DEFINE_OID(pkcs()+1, pkcs_1) - DEFINE_OID(pkcs_1()+1, rsaEncryption); - DEFINE_OID(rsadsi()+2, rsadsi_digestAlgorithm) - DEFINE_OID(rsadsi_digestAlgorithm()+2, id_md2) - DEFINE_OID(rsadsi_digestAlgorithm()+5, id_md5) - DEFINE_OID(iso()+3, identified_organization); - DEFINE_OID(identified_organization()+14, oiw); - DEFINE_OID(oiw()+3, oiw_secsig); - DEFINE_OID(oiw_secsig()+2, oiw_secsig_algorithms); - DEFINE_OID(oiw_secsig_algorithms()+26, id_sha1); - - DEFINE_OID(identified_organization()+36, teletrust); - DEFINE_OID(teletrust()+3, teletrust_algorithm) - DEFINE_OID(teletrust_algorithm()+2+1, id_ripemd160) - DEFINE_OID(teletrust_algorithm()+3+2+8+1, teletrust_ellipticCurve) - DEFINE_OID(teletrust_ellipticCurve()+1+1, brainpoolP160r1) - DEFINE_OID(teletrust_ellipticCurve()+1+3, brainpoolP192r1) - DEFINE_OID(teletrust_ellipticCurve()+1+5, brainpoolP224r1) - DEFINE_OID(teletrust_ellipticCurve()+1+7, brainpoolP256r1) - DEFINE_OID(teletrust_ellipticCurve()+1+9, brainpoolP320r1) - DEFINE_OID(teletrust_ellipticCurve()+1+11, brainpoolP384r1) - DEFINE_OID(teletrust_ellipticCurve()+1+13, brainpoolP512r1) - - DEFINE_OID(identified_organization()+132, certicom); - DEFINE_OID(certicom()+0, certicom_ellipticCurve); - // these are sorted by curve type and then by OID - // first curves based on GF(p) - DEFINE_OID(certicom_ellipticCurve()+6, secp112r1); - DEFINE_OID(certicom_ellipticCurve()+7, secp112r2); - DEFINE_OID(certicom_ellipticCurve()+8, secp160r1); - DEFINE_OID(certicom_ellipticCurve()+9, secp160k1); - DEFINE_OID(certicom_ellipticCurve()+10, secp256k1); - DEFINE_OID(certicom_ellipticCurve()+28, secp128r1); - DEFINE_OID(certicom_ellipticCurve()+29, secp128r2); - DEFINE_OID(certicom_ellipticCurve()+30, secp160r2); - DEFINE_OID(certicom_ellipticCurve()+31, secp192k1); - DEFINE_OID(certicom_ellipticCurve()+32, secp224k1); - DEFINE_OID(certicom_ellipticCurve()+33, secp224r1); - DEFINE_OID(certicom_ellipticCurve()+34, secp384r1); - DEFINE_OID(certicom_ellipticCurve()+35, secp521r1); - // then curves based on GF(2^n) - DEFINE_OID(certicom_ellipticCurve()+1, sect163k1); - DEFINE_OID(certicom_ellipticCurve()+2, sect163r1); - DEFINE_OID(certicom_ellipticCurve()+3, sect239k1); - DEFINE_OID(certicom_ellipticCurve()+4, sect113r1); - DEFINE_OID(certicom_ellipticCurve()+5, sect113r2); - DEFINE_OID(certicom_ellipticCurve()+15, sect163r2); - DEFINE_OID(certicom_ellipticCurve()+16, sect283k1); - DEFINE_OID(certicom_ellipticCurve()+17, sect283r1); - DEFINE_OID(certicom_ellipticCurve()+22, sect131r1); - DEFINE_OID(certicom_ellipticCurve()+23, sect131r2); - DEFINE_OID(certicom_ellipticCurve()+24, sect193r1); - DEFINE_OID(certicom_ellipticCurve()+25, sect193r2); - DEFINE_OID(certicom_ellipticCurve()+26, sect233k1); - DEFINE_OID(certicom_ellipticCurve()+27, sect233r1); - DEFINE_OID(certicom_ellipticCurve()+36, sect409k1); - DEFINE_OID(certicom_ellipticCurve()+37, sect409r1); - DEFINE_OID(certicom_ellipticCurve()+38, sect571k1); - DEFINE_OID(certicom_ellipticCurve()+39, sect571r1); -DEFINE_OID(2, joint_iso_ccitt) - DEFINE_OID(joint_iso_ccitt()+16, country) - DEFINE_OID(country()+840, joint_iso_ccitt_us) - DEFINE_OID(joint_iso_ccitt_us()+1, us_organization) - DEFINE_OID(us_organization()+101, us_gov) - DEFINE_OID(us_gov()+3, csor) - DEFINE_OID(csor()+4, nistalgorithms) - DEFINE_OID(nistalgorithms()+1, aes) - DEFINE_OID(aes()+1, id_aes128_ECB) - DEFINE_OID(aes()+2, id_aes128_cbc) - DEFINE_OID(aes()+3, id_aes128_ofb) - DEFINE_OID(aes()+4, id_aes128_cfb) - DEFINE_OID(aes()+21, id_aes192_ECB) - DEFINE_OID(aes()+22, id_aes192_cbc) - DEFINE_OID(aes()+23, id_aes192_ofb) - DEFINE_OID(aes()+24, id_aes192_cfb) - DEFINE_OID(aes()+41, id_aes256_ECB) - DEFINE_OID(aes()+42, id_aes256_cbc) - DEFINE_OID(aes()+43, id_aes256_ofb) - DEFINE_OID(aes()+44, id_aes256_cfb) - DEFINE_OID(nistalgorithms()+2, nist_hashalgs) - DEFINE_OID(nist_hashalgs()+1, id_sha256) - DEFINE_OID(nist_hashalgs()+2, id_sha384) - DEFINE_OID(nist_hashalgs()+3, id_sha512) - -NAMESPACE_END - -NAMESPACE_END - -#endif diff --git a/CryptoPP/osrng.cpp b/CryptoPP/osrng.cpp deleted file mode 100644 index 76e486b4e..000000000 --- a/CryptoPP/osrng.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// osrng.cpp - written and placed in the public domain by Wei Dai - -// Thanks to Leonard Janke for the suggestion for AutoSeededRandomPool. - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "osrng.h" - -#ifdef OS_RNG_AVAILABLE - -#include "rng.h" - -#ifdef CRYPTOPP_WIN32_AVAILABLE -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0400 -#endif -#include -#include -#endif - -#ifdef CRYPTOPP_UNIX_AVAILABLE -#include -#include -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -#if defined(NONBLOCKING_RNG_AVAILABLE) || defined(BLOCKING_RNG_AVAILABLE) -OS_RNG_Err::OS_RNG_Err(const std::string &operation) - : Exception(OTHER_ERROR, "OS_Rng: " + operation + " operation failed with error " + -#ifdef CRYPTOPP_WIN32_AVAILABLE - "0x" + IntToString(GetLastError(), 16) -#else - IntToString(errno) -#endif - ) -{ -} -#endif - -#ifdef NONBLOCKING_RNG_AVAILABLE - -#ifdef CRYPTOPP_WIN32_AVAILABLE - -MicrosoftCryptoProvider::MicrosoftCryptoProvider() -{ - if(!CryptAcquireContext(&m_hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) - throw OS_RNG_Err("CryptAcquireContext"); -} - -MicrosoftCryptoProvider::~MicrosoftCryptoProvider() -{ - CryptReleaseContext(m_hProvider, 0); -} - -#endif - -NonblockingRng::NonblockingRng() -{ -#ifndef CRYPTOPP_WIN32_AVAILABLE - m_fd = open("/dev/urandom",O_RDONLY); - if (m_fd == -1) - throw OS_RNG_Err("open /dev/urandom"); -#endif -} - -NonblockingRng::~NonblockingRng() -{ -#ifndef CRYPTOPP_WIN32_AVAILABLE - close(m_fd); -#endif -} - -void NonblockingRng::GenerateBlock(byte *output, size_t size) -{ -#ifdef CRYPTOPP_WIN32_AVAILABLE -# ifdef WORKAROUND_MS_BUG_Q258000 - const MicrosoftCryptoProvider &m_Provider = Singleton().Ref(); -# endif - if (!CryptGenRandom(m_Provider.GetProviderHandle(), (DWORD)size, output)) - throw OS_RNG_Err("CryptGenRandom"); -#else - while (size) - { - ssize_t len = read(m_fd, output, size); - - if (len < 0) - { - // /dev/urandom reads CAN give EAGAIN errors! (maybe EINTR as well) - if (errno != EINTR && errno != EAGAIN) - throw OS_RNG_Err("read /dev/urandom"); - - continue; - } - - output += len; - size -= len; - } -#endif -} - -#endif - -// ************************************************************* - -#ifdef BLOCKING_RNG_AVAILABLE - -#ifndef CRYPTOPP_BLOCKING_RNG_FILENAME -#ifdef __OpenBSD__ -#define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/srandom" -#else -#define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/random" -#endif -#endif - -BlockingRng::BlockingRng() -{ - m_fd = open(CRYPTOPP_BLOCKING_RNG_FILENAME,O_RDONLY); - if (m_fd == -1) - throw OS_RNG_Err("open " CRYPTOPP_BLOCKING_RNG_FILENAME); -} - -BlockingRng::~BlockingRng() -{ - close(m_fd); -} - -void BlockingRng::GenerateBlock(byte *output, size_t size) -{ - while (size) - { - // on some systems /dev/random will block until all bytes - // are available, on others it returns immediately - ssize_t len = read(m_fd, output, size); - if (len < 0) - { - // /dev/random reads CAN give EAGAIN errors! (maybe EINTR as well) - if (errno != EINTR && errno != EAGAIN) - throw OS_RNG_Err("read " CRYPTOPP_BLOCKING_RNG_FILENAME); - - continue; - } - - size -= len; - output += len; - if (size) - sleep(1); - } -} - -#endif - -// ************************************************************* - -void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size) -{ -#ifdef NONBLOCKING_RNG_AVAILABLE - if (blocking) -#endif - { -#ifdef BLOCKING_RNG_AVAILABLE - BlockingRng rng; - rng.GenerateBlock(output, size); -#endif - } - -#ifdef BLOCKING_RNG_AVAILABLE - if (!blocking) -#endif - { -#ifdef NONBLOCKING_RNG_AVAILABLE - NonblockingRng rng; - rng.GenerateBlock(output, size); -#endif - } -} - -void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize) -{ - SecByteBlock seed(seedSize); - OS_GenerateRandomBlock(blocking, seed, seedSize); - IncorporateEntropy(seed, seedSize); -} - -NAMESPACE_END - -#endif - -#endif diff --git a/CryptoPP/osrng.h b/CryptoPP/osrng.h deleted file mode 100644 index ae07d057b..000000000 --- a/CryptoPP/osrng.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef CRYPTOPP_OSRNG_H -#define CRYPTOPP_OSRNG_H - -//! \file - -#include "config.h" - -#ifdef OS_RNG_AVAILABLE - -#include "randpool.h" -#include "rng.h" -#include "aes.h" -#include "sha.h" -#include "fips140.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! Exception class for Operating-System Random Number Generator. -class CRYPTOPP_DLL OS_RNG_Err : public Exception -{ -public: - OS_RNG_Err(const std::string &operation); -}; - -#ifdef NONBLOCKING_RNG_AVAILABLE - -#ifdef CRYPTOPP_WIN32_AVAILABLE -class CRYPTOPP_DLL MicrosoftCryptoProvider -{ -public: - MicrosoftCryptoProvider(); - ~MicrosoftCryptoProvider(); -#if defined(_WIN64) - typedef unsigned __int64 ProviderHandle; // type HCRYPTPROV, avoid #include -#else - typedef unsigned long ProviderHandle; -#endif - ProviderHandle GetProviderHandle() const {return m_hProvider;} -private: - ProviderHandle m_hProvider; -}; - -#pragma comment(lib, "advapi32.lib") -#endif - -//! encapsulate CryptoAPI's CryptGenRandom or /dev/urandom -class CRYPTOPP_DLL NonblockingRng : public RandomNumberGenerator -{ -public: - NonblockingRng(); - ~NonblockingRng(); - void GenerateBlock(byte *output, size_t size); - -protected: -#ifdef CRYPTOPP_WIN32_AVAILABLE -# ifndef WORKAROUND_MS_BUG_Q258000 - MicrosoftCryptoProvider m_Provider; -# endif -#else - int m_fd; -#endif -}; - -#endif - -#ifdef BLOCKING_RNG_AVAILABLE - -//! encapsulate /dev/random, or /dev/srandom on OpenBSD -class CRYPTOPP_DLL BlockingRng : public RandomNumberGenerator -{ -public: - BlockingRng(); - ~BlockingRng(); - void GenerateBlock(byte *output, size_t size); - -protected: - int m_fd; -}; - -#endif - -CRYPTOPP_DLL void CRYPTOPP_API OS_GenerateRandomBlock(bool blocking, byte *output, size_t size); - -//! Automaticly Seeded Randomness Pool -/*! This class seeds itself using an operating system provided RNG. */ -class CRYPTOPP_DLL AutoSeededRandomPool : public RandomPool -{ -public: - //! use blocking to choose seeding with BlockingRng or NonblockingRng. the parameter is ignored if only one of these is available - explicit AutoSeededRandomPool(bool blocking = false, unsigned int seedSize = 32) - {Reseed(blocking, seedSize);} - void Reseed(bool blocking = false, unsigned int seedSize = 32); -}; - -//! RNG from ANSI X9.17 Appendix C, seeded using an OS provided RNG -template -class AutoSeededX917RNG : public RandomNumberGenerator, public NotCopyable -{ -public: - //! use blocking to choose seeding with BlockingRng or NonblockingRng. the parameter is ignored if only one of these is available - explicit AutoSeededX917RNG(bool blocking = false, bool autoSeed = true) - {if (autoSeed) Reseed(blocking);} - void Reseed(bool blocking = false, const byte *additionalEntropy = NULL, size_t length = 0); - // exposed for testing - void Reseed(const byte *key, size_t keylength, const byte *seed, const byte *timeVector); - - bool CanIncorporateEntropy() const {return true;} - void IncorporateEntropy(const byte *input, size_t length) {Reseed(false, input, length);} - void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword length) {m_rng->GenerateIntoBufferedTransformation(target, channel, length);} - -private: - member_ptr m_rng; -}; - -template -void AutoSeededX917RNG::Reseed(const byte *key, size_t keylength, const byte *seed, const byte *timeVector) -{ - m_rng.reset(new X917RNG(new typename BLOCK_CIPHER::Encryption(key, keylength), seed, timeVector)); -} - -template -void AutoSeededX917RNG::Reseed(bool blocking, const byte *input, size_t length) -{ - SecByteBlock seed(BLOCK_CIPHER::BLOCKSIZE + BLOCK_CIPHER::DEFAULT_KEYLENGTH); - const byte *key; - do - { - OS_GenerateRandomBlock(blocking, seed, seed.size()); - if (length > 0) - { - SHA256 hash; - hash.Update(seed, seed.size()); - hash.Update(input, length); - hash.TruncatedFinal(seed, UnsignedMin(hash.DigestSize(), seed.size())); - } - key = seed + BLOCK_CIPHER::BLOCKSIZE; - } // check that seed and key don't have same value - while (memcmp(key, seed, STDMIN((unsigned int)BLOCK_CIPHER::BLOCKSIZE, (unsigned int)BLOCK_CIPHER::DEFAULT_KEYLENGTH)) == 0); - - Reseed(key, BLOCK_CIPHER::DEFAULT_KEYLENGTH, seed, NULL); -} - -CRYPTOPP_DLL_TEMPLATE_CLASS AutoSeededX917RNG; - -//! this is AutoSeededX917RNG\ in FIPS mode, otherwise it's AutoSeededRandomPool -#if CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2 -typedef AutoSeededX917RNG DefaultAutoSeededRNG; -#else -typedef AutoSeededRandomPool DefaultAutoSeededRNG; -#endif - -NAMESPACE_END - -#endif - -#endif diff --git a/CryptoPP/pch.cpp b/CryptoPP/pch.cpp deleted file mode 100644 index 1d9f38c57..000000000 --- a/CryptoPP/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" diff --git a/CryptoPP/pch.h b/CryptoPP/pch.h deleted file mode 100644 index 418c39076..000000000 --- a/CryptoPP/pch.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef CRYPTOPP_PCH_H -#define CRYPTOPP_PCH_H - -#ifdef CRYPTOPP_GENERATE_X64_MASM - - #include "cpu.h" - -#else - - #include "config.h" - - #ifdef USE_PRECOMPILED_HEADERS - #include "simple.h" - #include "secblock.h" - #include "misc.h" - #include "smartptr.h" - #endif - -#endif - -#endif diff --git a/CryptoPP/pkcspad.cpp b/CryptoPP/pkcspad.cpp deleted file mode 100644 index e1f1d1e23..000000000 --- a/CryptoPP/pkcspad.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// pkcspad.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_PKCSPAD_CPP // SunCC workaround: compiler could cause this file to be included twice -#define CRYPTOPP_PKCSPAD_CPP - -#include "pkcspad.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -// more in dll.cpp -template<> const byte PKCS_DigestDecoration::decoration[] = {0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x02,0x05,0x00,0x04,0x10}; -template<> const unsigned int PKCS_DigestDecoration::length = sizeof(PKCS_DigestDecoration::decoration); - -template<> const byte PKCS_DigestDecoration::decoration[] = {0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x04,0x10}; -template<> const unsigned int PKCS_DigestDecoration::length = sizeof(PKCS_DigestDecoration::decoration); - -template<> const byte PKCS_DigestDecoration::decoration[] = {0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x24,0x03,0x02,0x01,0x05,0x00,0x04,0x14}; -template<> const unsigned int PKCS_DigestDecoration::length = sizeof(PKCS_DigestDecoration::decoration); - -template<> const byte PKCS_DigestDecoration::decoration[] = {0x30,0x29,0x30,0x0D,0x06,0x09,0x2B,0x06,0x01,0x04,0x01,0xDA,0x47,0x0C,0x02,0x05,0x00,0x04,0x18}; -template<> const unsigned int PKCS_DigestDecoration::length = sizeof(PKCS_DigestDecoration::decoration); - -size_t PKCS_EncryptionPaddingScheme::MaxUnpaddedLength(size_t paddedLength) const -{ - return SaturatingSubtract(paddedLength/8, 10U); -} - -void PKCS_EncryptionPaddingScheme::Pad(RandomNumberGenerator &rng, const byte *input, size_t inputLen, byte *pkcsBlock, size_t pkcsBlockLen, const NameValuePairs ¶meters) const -{ - assert (inputLen <= MaxUnpaddedLength(pkcsBlockLen)); // this should be checked by caller - - // convert from bit length to byte length - if (pkcsBlockLen % 8 != 0) - { - pkcsBlock[0] = 0; - pkcsBlock++; - } - pkcsBlockLen /= 8; - - pkcsBlock[0] = 2; // block type 2 - - // pad with non-zero random bytes - for (unsigned i = 1; i < pkcsBlockLen-inputLen-1; i++) - pkcsBlock[i] = (byte)rng.GenerateWord32(1, 0xff); - - pkcsBlock[pkcsBlockLen-inputLen-1] = 0; // separator - memcpy(pkcsBlock+pkcsBlockLen-inputLen, input, inputLen); -} - -DecodingResult PKCS_EncryptionPaddingScheme::Unpad(const byte *pkcsBlock, size_t pkcsBlockLen, byte *output, const NameValuePairs ¶meters) const -{ - bool invalid = false; - size_t maxOutputLen = MaxUnpaddedLength(pkcsBlockLen); - - // convert from bit length to byte length - if (pkcsBlockLen % 8 != 0) - { - invalid = (pkcsBlock[0] != 0) || invalid; - pkcsBlock++; - } - pkcsBlockLen /= 8; - - // Require block type 2. - invalid = (pkcsBlock[0] != 2) || invalid; - - // skip past the padding until we find the separator - size_t i=1; - while (i maxOutputLen) || invalid; - - if (invalid) - return DecodingResult(); - - memcpy (output, pkcsBlock+i, outputLen); - return DecodingResult(outputLen); -} - -// ******************************************************** - -#ifndef CRYPTOPP_IMPORTS - -void PKCS1v15_SignatureMessageEncodingMethod::ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const -{ - assert(representativeBitLength >= MinRepresentativeBitLength(hashIdentifier.second, hash.DigestSize())); - - size_t pkcsBlockLen = representativeBitLength; - // convert from bit length to byte length - if (pkcsBlockLen % 8 != 0) - { - representative[0] = 0; - representative++; - } - pkcsBlockLen /= 8; - - representative[0] = 1; // block type 1 - - unsigned int digestSize = hash.DigestSize(); - byte *pPadding = representative + 1; - byte *pDigest = representative + pkcsBlockLen - digestSize; - byte *pHashId = pDigest - hashIdentifier.second; - byte *pSeparator = pHashId - 1; - - // pad with 0xff - memset(pPadding, 0xff, pSeparator-pPadding); - *pSeparator = 0; - memcpy(pHashId, hashIdentifier.first, hashIdentifier.second); - hash.Final(pDigest); -} - -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/pkcspad.h b/CryptoPP/pkcspad.h deleted file mode 100644 index 6371c7698..000000000 --- a/CryptoPP/pkcspad.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef CRYPTOPP_PKCSPAD_H -#define CRYPTOPP_PKCSPAD_H - -#include "cryptlib.h" -#include "pubkey.h" - -#ifdef CRYPTOPP_IS_DLL -#include "sha.h" -#endif - -NAMESPACE_BEGIN(CryptoPP) - -//! EME-PKCS1-v1_5 -class PKCS_EncryptionPaddingScheme : public PK_EncryptionMessageEncodingMethod -{ -public: - static const char * StaticAlgorithmName() {return "EME-PKCS1-v1_5";} - - size_t MaxUnpaddedLength(size_t paddedLength) const; - void Pad(RandomNumberGenerator &rng, const byte *raw, size_t inputLength, byte *padded, size_t paddedLength, const NameValuePairs ¶meters) const; - DecodingResult Unpad(const byte *padded, size_t paddedLength, byte *raw, const NameValuePairs ¶meters) const; -}; - -template class PKCS_DigestDecoration -{ -public: - static const byte decoration[]; - static const unsigned int length; -}; - -// PKCS_DigestDecoration can be instantiated with the following -// classes as specified in PKCS#1 v2.0 and P1363a -class SHA1; -class RIPEMD160; -class Tiger; -class SHA224; -class SHA256; -class SHA384; -class SHA512; -namespace Weak1 { -class MD2; -class MD5; -} -// end of list - -#ifdef CRYPTOPP_IS_DLL -CRYPTOPP_DLL_TEMPLATE_CLASS PKCS_DigestDecoration; -CRYPTOPP_DLL_TEMPLATE_CLASS PKCS_DigestDecoration; -CRYPTOPP_DLL_TEMPLATE_CLASS PKCS_DigestDecoration; -CRYPTOPP_DLL_TEMPLATE_CLASS PKCS_DigestDecoration; -CRYPTOPP_DLL_TEMPLATE_CLASS PKCS_DigestDecoration; -#endif - -//! EMSA-PKCS1-v1_5 -class CRYPTOPP_DLL PKCS1v15_SignatureMessageEncodingMethod : public PK_DeterministicSignatureMessageEncodingMethod -{ -public: - static const char * CRYPTOPP_API StaticAlgorithmName() {return "EMSA-PKCS1-v1_5";} - - size_t MinRepresentativeBitLength(size_t hashIdentifierSize, size_t digestSize) const - {return 8 * (digestSize + hashIdentifierSize + 10);} - - void ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const; - - struct HashIdentifierLookup - { - template struct HashIdentifierLookup2 - { - static HashIdentifier Lookup() - { - return HashIdentifier(PKCS_DigestDecoration::decoration, PKCS_DigestDecoration::length); - } - }; - }; -}; - -//! PKCS #1 version 1.5, for use with RSAES and RSASS -/*! Only the following hash functions are supported by this signature standard: - \dontinclude pkcspad.h - \skip can be instantiated - \until end of list -*/ -struct PKCS1v15 : public SignatureStandard, public EncryptionStandard -{ - typedef PKCS_EncryptionPaddingScheme EncryptionMessageEncodingMethod; - typedef PKCS1v15_SignatureMessageEncodingMethod SignatureMessageEncodingMethod; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/polynomi.cpp b/CryptoPP/polynomi.cpp deleted file mode 100644 index 734cae926..000000000 --- a/CryptoPP/polynomi.cpp +++ /dev/null @@ -1,577 +0,0 @@ -// polynomi.cpp - written and placed in the public domain by Wei Dai - -// Part of the code for polynomial evaluation and interpolation -// originally came from Hal Finney's public domain secsplit.c. - -#include "pch.h" -#include "polynomi.h" -#include "secblock.h" - -#include -#include - -NAMESPACE_BEGIN(CryptoPP) - -template -void PolynomialOver::Randomize(RandomNumberGenerator &rng, const RandomizationParameter ¶meter, const Ring &ring) -{ - m_coefficients.resize(parameter.m_coefficientCount); - for (unsigned int i=0; i -void PolynomialOver::FromStr(const char *str, const Ring &ring) -{ - std::istringstream in((char *)str); - bool positive = true; - CoefficientType coef; - unsigned int power; - - while (in) - { - std::ws(in); - if (in.peek() == 'x') - coef = ring.MultiplicativeIdentity(); - else - in >> coef; - - std::ws(in); - if (in.peek() == 'x') - { - in.get(); - std::ws(in); - if (in.peek() == '^') - { - in.get(); - in >> power; - } - else - power = 1; - } - else - power = 0; - - if (!positive) - coef = ring.Inverse(coef); - - SetCoefficient(power, coef, ring); - - std::ws(in); - switch (in.get()) - { - case '+': - positive = true; - break; - case '-': - positive = false; - break; - default: - return; // something's wrong with the input string - } - } -} - -template -unsigned int PolynomialOver::CoefficientCount(const Ring &ring) const -{ - unsigned count = m_coefficients.size(); - while (count && ring.Equal(m_coefficients[count-1], ring.Identity())) - count--; - const_cast &>(m_coefficients).resize(count); - return count; -} - -template -typename PolynomialOver::CoefficientType PolynomialOver::GetCoefficient(unsigned int i, const Ring &ring) const -{ - return (i < m_coefficients.size()) ? m_coefficients[i] : ring.Identity(); -} - -template -PolynomialOver& PolynomialOver::operator=(const PolynomialOver& t) -{ - if (this != &t) - { - m_coefficients.resize(t.m_coefficients.size()); - for (unsigned int i=0; i -PolynomialOver& PolynomialOver::Accumulate(const PolynomialOver& t, const Ring &ring) -{ - unsigned int count = t.CoefficientCount(ring); - - if (count > CoefficientCount(ring)) - m_coefficients.resize(count, ring.Identity()); - - for (unsigned int i=0; i -PolynomialOver& PolynomialOver::Reduce(const PolynomialOver& t, const Ring &ring) -{ - unsigned int count = t.CoefficientCount(ring); - - if (count > CoefficientCount(ring)) - m_coefficients.resize(count, ring.Identity()); - - for (unsigned int i=0; i -typename PolynomialOver::CoefficientType PolynomialOver::EvaluateAt(const CoefficientType &x, const Ring &ring) const -{ - int degree = Degree(ring); - - if (degree < 0) - return ring.Identity(); - - CoefficientType result = m_coefficients[degree]; - for (int j=degree-1; j>=0; j--) - { - result = ring.Multiply(result, x); - ring.Accumulate(result, m_coefficients[j]); - } - return result; -} - -template -PolynomialOver& PolynomialOver::ShiftLeft(unsigned int n, const Ring &ring) -{ - unsigned int i = CoefficientCount(ring) + n; - m_coefficients.resize(i, ring.Identity()); - while (i > n) - { - i--; - m_coefficients[i] = m_coefficients[i-n]; - } - while (i) - { - i--; - m_coefficients[i] = ring.Identity(); - } - return *this; -} - -template -PolynomialOver& PolynomialOver::ShiftRight(unsigned int n, const Ring &ring) -{ - unsigned int count = CoefficientCount(ring); - if (count > n) - { - for (unsigned int i=0; i -void PolynomialOver::SetCoefficient(unsigned int i, const CoefficientType &value, const Ring &ring) -{ - if (i >= m_coefficients.size()) - m_coefficients.resize(i+1, ring.Identity()); - m_coefficients[i] = value; -} - -template -void PolynomialOver::Negate(const Ring &ring) -{ - unsigned int count = CoefficientCount(ring); - for (unsigned int i=0; i -void PolynomialOver::swap(PolynomialOver &t) -{ - m_coefficients.swap(t.m_coefficients); -} - -template -bool PolynomialOver::Equals(const PolynomialOver& t, const Ring &ring) const -{ - unsigned int count = CoefficientCount(ring); - - if (count != t.CoefficientCount(ring)) - return false; - - for (unsigned int i=0; i -PolynomialOver PolynomialOver::Plus(const PolynomialOver& t, const Ring &ring) const -{ - unsigned int i; - unsigned int count = CoefficientCount(ring); - unsigned int tCount = t.CoefficientCount(ring); - - if (count > tCount) - { - PolynomialOver result(ring, count); - - for (i=0; i result(ring, tCount); - - for (i=0; i -PolynomialOver PolynomialOver::Minus(const PolynomialOver& t, const Ring &ring) const -{ - unsigned int i; - unsigned int count = CoefficientCount(ring); - unsigned int tCount = t.CoefficientCount(ring); - - if (count > tCount) - { - PolynomialOver result(ring, count); - - for (i=0; i result(ring, tCount); - - for (i=0; i -PolynomialOver PolynomialOver::Inverse(const Ring &ring) const -{ - unsigned int count = CoefficientCount(ring); - PolynomialOver result(ring, count); - - for (unsigned int i=0; i -PolynomialOver PolynomialOver::Times(const PolynomialOver& t, const Ring &ring) const -{ - if (IsZero(ring) || t.IsZero(ring)) - return PolynomialOver(); - - unsigned int count1 = CoefficientCount(ring), count2 = t.CoefficientCount(ring); - PolynomialOver result(ring, count1 + count2 - 1); - - for (unsigned int i=0; i -PolynomialOver PolynomialOver::DividedBy(const PolynomialOver& t, const Ring &ring) const -{ - PolynomialOver remainder, quotient; - Divide(remainder, quotient, *this, t, ring); - return quotient; -} - -template -PolynomialOver PolynomialOver::Modulo(const PolynomialOver& t, const Ring &ring) const -{ - PolynomialOver remainder, quotient; - Divide(remainder, quotient, *this, t, ring); - return remainder; -} - -template -PolynomialOver PolynomialOver::MultiplicativeInverse(const Ring &ring) const -{ - return Degree(ring)==0 ? ring.MultiplicativeInverse(m_coefficients[0]) : ring.Identity(); -} - -template -bool PolynomialOver::IsUnit(const Ring &ring) const -{ - return Degree(ring)==0 && ring.IsUnit(m_coefficients[0]); -} - -template -std::istream& PolynomialOver::Input(std::istream &in, const Ring &ring) -{ - char c; - unsigned int length = 0; - SecBlock str(length + 16); - bool paren = false; - - std::ws(in); - - if (in.peek() == '(') - { - paren = true; - in.get(); - } - - do - { - in.read(&c, 1); - str[length++] = c; - if (length >= str.size()) - str.Grow(length + 16); - } - // if we started with a left paren, then read until we find a right paren, - // otherwise read until the end of the line - while (in && ((paren && c != ')') || (!paren && c != '\n'))); - - str[length-1] = '\0'; - *this = PolynomialOver(str, ring); - - return in; -} - -template -std::ostream& PolynomialOver::Output(std::ostream &out, const Ring &ring) const -{ - unsigned int i = CoefficientCount(ring); - if (i) - { - bool firstTerm = true; - - while (i--) - { - if (m_coefficients[i] != ring.Identity()) - { - if (firstTerm) - { - firstTerm = false; - if (!i || !ring.Equal(m_coefficients[i], ring.MultiplicativeIdentity())) - out << m_coefficients[i]; - } - else - { - CoefficientType inverse = ring.Inverse(m_coefficients[i]); - std::ostringstream pstr, nstr; - - pstr << m_coefficients[i]; - nstr << inverse; - - if (pstr.str().size() <= nstr.str().size()) - { - out << " + "; - if (!i || !ring.Equal(m_coefficients[i], ring.MultiplicativeIdentity())) - out << m_coefficients[i]; - } - else - { - out << " - "; - if (!i || !ring.Equal(inverse, ring.MultiplicativeIdentity())) - out << inverse; - } - } - - switch (i) - { - case 0: - break; - case 1: - out << "x"; - break; - default: - out << "x^" << i; - } - } - } - } - else - { - out << ring.Identity(); - } - return out; -} - -template -void PolynomialOver::Divide(PolynomialOver &r, PolynomialOver &q, const PolynomialOver &a, const PolynomialOver &d, const Ring &ring) -{ - unsigned int i = a.CoefficientCount(ring); - const int dDegree = d.Degree(ring); - - if (dDegree < 0) - throw DivideByZero(); - - r = a; - q.m_coefficients.resize(STDMAX(0, int(i - dDegree))); - - while (i > (unsigned int)dDegree) - { - --i; - q.m_coefficients[i-dDegree] = ring.Divide(r.m_coefficients[i], d.m_coefficients[dDegree]); - for (int j=0; j<=dDegree; j++) - ring.Reduce(r.m_coefficients[i-dDegree+j], ring.Multiply(q.m_coefficients[i-dDegree], d.m_coefficients[j])); - } - - r.CoefficientCount(ring); // resize r.m_coefficients -} - -// ******************************************************** - -// helper function for Interpolate() and InterpolateAt() -template -void RingOfPolynomialsOver::CalculateAlpha(std::vector &alpha, const CoefficientType x[], const CoefficientType y[], unsigned int n) const -{ - for (unsigned int j=0; j=k; --j) - { - m_ring.Reduce(alpha[j], alpha[j-1]); - - CoefficientType d = m_ring.Subtract(x[j], x[j-k]); - if (!m_ring.IsUnit(d)) - throw InterpolationFailed(); - alpha[j] = m_ring.Divide(alpha[j], d); - } - } -} - -template -typename RingOfPolynomialsOver::Element RingOfPolynomialsOver::Interpolate(const CoefficientType x[], const CoefficientType y[], unsigned int n) const -{ - assert(n > 0); - - std::vector alpha(n); - CalculateAlpha(alpha, x, y, n); - - std::vector coefficients((size_t)n, m_ring.Identity()); - coefficients[0] = alpha[n-1]; - - for (int j=n-2; j>=0; --j) - { - for (unsigned int i=n-j-1; i>0; i--) - coefficients[i] = m_ring.Subtract(coefficients[i-1], m_ring.Multiply(coefficients[i], x[j])); - - coefficients[0] = m_ring.Subtract(alpha[j], m_ring.Multiply(coefficients[0], x[j])); - } - - return PolynomialOver(coefficients.begin(), coefficients.end()); -} - -template -typename RingOfPolynomialsOver::CoefficientType RingOfPolynomialsOver::InterpolateAt(const CoefficientType &position, const CoefficientType x[], const CoefficientType y[], unsigned int n) const -{ - assert(n > 0); - - std::vector alpha(n); - CalculateAlpha(alpha, x, y, n); - - CoefficientType result = alpha[n-1]; - for (int j=n-2; j>=0; --j) - { - result = m_ring.Multiply(result, m_ring.Subtract(position, x[j])); - m_ring.Accumulate(result, alpha[j]); - } - return result; -} - -template -void PrepareBulkPolynomialInterpolation(const Ring &ring, Element *w, const Element x[], unsigned int n) -{ - for (unsigned int i=0; i -void PrepareBulkPolynomialInterpolationAt(const Ring &ring, Element *v, const Element &position, const Element x[], const Element w[], unsigned int n) -{ - assert(n > 0); - - std::vector a(2*n-1); - unsigned int i; - - for (i=0; i1; i--) - a[i-1] = ring.Multiply(a[2*i], a[2*i-1]); - - a[0] = ring.MultiplicativeIdentity(); - - for (i=0; i -Element BulkPolynomialInterpolateAt(const Ring &ring, const Element y[], const Element v[], unsigned int n) -{ - Element result = ring.Identity(); - for (unsigned int i=0; i -const PolynomialOverFixedRing &PolynomialOverFixedRing::Zero() -{ - return Singleton().Ref(); -} - -template -const PolynomialOverFixedRing &PolynomialOverFixedRing::One() -{ - return Singleton().Ref(); -} - -NAMESPACE_END diff --git a/CryptoPP/polynomi.h b/CryptoPP/polynomi.h deleted file mode 100644 index cddadaeaf..000000000 --- a/CryptoPP/polynomi.h +++ /dev/null @@ -1,459 +0,0 @@ -#ifndef CRYPTOPP_POLYNOMI_H -#define CRYPTOPP_POLYNOMI_H - -/*! \file */ - -#include "cryptlib.h" -#include "misc.h" -#include "algebra.h" - -#include -#include - -NAMESPACE_BEGIN(CryptoPP) - -//! represents single-variable polynomials over arbitrary rings -/*! \nosubgrouping */ -template class PolynomialOver -{ -public: - //! \name ENUMS, EXCEPTIONS, and TYPEDEFS - //@{ - //! division by zero exception - class DivideByZero : public Exception - { - public: - DivideByZero() : Exception(OTHER_ERROR, "PolynomialOver: division by zero") {} - }; - - //! specify the distribution for randomization functions - class RandomizationParameter - { - public: - RandomizationParameter(unsigned int coefficientCount, const typename T::RandomizationParameter &coefficientParameter ) - : m_coefficientCount(coefficientCount), m_coefficientParameter(coefficientParameter) {} - - private: - unsigned int m_coefficientCount; - typename T::RandomizationParameter m_coefficientParameter; - friend class PolynomialOver; - }; - - typedef T Ring; - typedef typename T::Element CoefficientType; - //@} - - //! \name CREATORS - //@{ - //! creates the zero polynomial - PolynomialOver() {} - - //! - PolynomialOver(const Ring &ring, unsigned int count) - : m_coefficients((size_t)count, ring.Identity()) {} - - //! copy constructor - PolynomialOver(const PolynomialOver &t) - : m_coefficients(t.m_coefficients.size()) {*this = t;} - - //! construct constant polynomial - PolynomialOver(const CoefficientType &element) - : m_coefficients(1, element) {} - - //! construct polynomial with specified coefficients, starting from coefficient of x^0 - template PolynomialOver(Iterator begin, Iterator end) - : m_coefficients(begin, end) {} - - //! convert from string - PolynomialOver(const char *str, const Ring &ring) {FromStr(str, ring);} - - //! convert from big-endian byte array - PolynomialOver(const byte *encodedPolynomialOver, unsigned int byteCount); - - //! convert from Basic Encoding Rules encoded byte array - explicit PolynomialOver(const byte *BEREncodedPolynomialOver); - - //! convert from BER encoded byte array stored in a BufferedTransformation object - explicit PolynomialOver(BufferedTransformation &bt); - - //! create a random PolynomialOver - PolynomialOver(RandomNumberGenerator &rng, const RandomizationParameter ¶meter, const Ring &ring) - {Randomize(rng, parameter, ring);} - //@} - - //! \name ACCESSORS - //@{ - //! the zero polynomial will return a degree of -1 - int Degree(const Ring &ring) const {return int(CoefficientCount(ring))-1;} - //! - unsigned int CoefficientCount(const Ring &ring) const; - //! return coefficient for x^i - CoefficientType GetCoefficient(unsigned int i, const Ring &ring) const; - //@} - - //! \name MANIPULATORS - //@{ - //! - PolynomialOver& operator=(const PolynomialOver& t); - - //! - void Randomize(RandomNumberGenerator &rng, const RandomizationParameter ¶meter, const Ring &ring); - - //! set the coefficient for x^i to value - void SetCoefficient(unsigned int i, const CoefficientType &value, const Ring &ring); - - //! - void Negate(const Ring &ring); - - //! - void swap(PolynomialOver &t); - //@} - - - //! \name BASIC ARITHMETIC ON POLYNOMIALS - //@{ - bool Equals(const PolynomialOver &t, const Ring &ring) const; - bool IsZero(const Ring &ring) const {return CoefficientCount(ring)==0;} - - PolynomialOver Plus(const PolynomialOver& t, const Ring &ring) const; - PolynomialOver Minus(const PolynomialOver& t, const Ring &ring) const; - PolynomialOver Inverse(const Ring &ring) const; - - PolynomialOver Times(const PolynomialOver& t, const Ring &ring) const; - PolynomialOver DividedBy(const PolynomialOver& t, const Ring &ring) const; - PolynomialOver Modulo(const PolynomialOver& t, const Ring &ring) const; - PolynomialOver MultiplicativeInverse(const Ring &ring) const; - bool IsUnit(const Ring &ring) const; - - PolynomialOver& Accumulate(const PolynomialOver& t, const Ring &ring); - PolynomialOver& Reduce(const PolynomialOver& t, const Ring &ring); - - //! - PolynomialOver Doubled(const Ring &ring) const {return Plus(*this, ring);} - //! - PolynomialOver Squared(const Ring &ring) const {return Times(*this, ring);} - - CoefficientType EvaluateAt(const CoefficientType &x, const Ring &ring) const; - - PolynomialOver& ShiftLeft(unsigned int n, const Ring &ring); - PolynomialOver& ShiftRight(unsigned int n, const Ring &ring); - - //! calculate r and q such that (a == d*q + r) && (0 <= degree of r < degree of d) - static void Divide(PolynomialOver &r, PolynomialOver &q, const PolynomialOver &a, const PolynomialOver &d, const Ring &ring); - //@} - - //! \name INPUT/OUTPUT - //@{ - std::istream& Input(std::istream &in, const Ring &ring); - std::ostream& Output(std::ostream &out, const Ring &ring) const; - //@} - -private: - void FromStr(const char *str, const Ring &ring); - - std::vector m_coefficients; -}; - -//! Polynomials over a fixed ring -/*! Having a fixed ring allows overloaded operators */ -template class PolynomialOverFixedRing : private PolynomialOver -{ - typedef PolynomialOver B; - typedef PolynomialOverFixedRing ThisType; - -public: - typedef T Ring; - typedef typename T::Element CoefficientType; - typedef typename B::DivideByZero DivideByZero; - typedef typename B::RandomizationParameter RandomizationParameter; - - //! \name CREATORS - //@{ - //! creates the zero polynomial - PolynomialOverFixedRing(unsigned int count = 0) : B(ms_fixedRing, count) {} - - //! copy constructor - PolynomialOverFixedRing(const ThisType &t) : B(t) {} - - explicit PolynomialOverFixedRing(const B &t) : B(t) {} - - //! construct constant polynomial - PolynomialOverFixedRing(const CoefficientType &element) : B(element) {} - - //! construct polynomial with specified coefficients, starting from coefficient of x^0 - template PolynomialOverFixedRing(Iterator first, Iterator last) - : B(first, last) {} - - //! convert from string - explicit PolynomialOverFixedRing(const char *str) : B(str, ms_fixedRing) {} - - //! convert from big-endian byte array - PolynomialOverFixedRing(const byte *encodedPoly, unsigned int byteCount) : B(encodedPoly, byteCount) {} - - //! convert from Basic Encoding Rules encoded byte array - explicit PolynomialOverFixedRing(const byte *BEREncodedPoly) : B(BEREncodedPoly) {} - - //! convert from BER encoded byte array stored in a BufferedTransformation object - explicit PolynomialOverFixedRing(BufferedTransformation &bt) : B(bt) {} - - //! create a random PolynomialOverFixedRing - PolynomialOverFixedRing(RandomNumberGenerator &rng, const RandomizationParameter ¶meter) : B(rng, parameter, ms_fixedRing) {} - - static const ThisType &Zero(); - static const ThisType &One(); - //@} - - //! \name ACCESSORS - //@{ - //! the zero polynomial will return a degree of -1 - int Degree() const {return B::Degree(ms_fixedRing);} - //! degree + 1 - unsigned int CoefficientCount() const {return B::CoefficientCount(ms_fixedRing);} - //! return coefficient for x^i - CoefficientType GetCoefficient(unsigned int i) const {return B::GetCoefficient(i, ms_fixedRing);} - //! return coefficient for x^i - CoefficientType operator[](unsigned int i) const {return B::GetCoefficient(i, ms_fixedRing);} - //@} - - //! \name MANIPULATORS - //@{ - //! - ThisType& operator=(const ThisType& t) {B::operator=(t); return *this;} - //! - ThisType& operator+=(const ThisType& t) {Accumulate(t, ms_fixedRing); return *this;} - //! - ThisType& operator-=(const ThisType& t) {Reduce(t, ms_fixedRing); return *this;} - //! - ThisType& operator*=(const ThisType& t) {return *this = *this*t;} - //! - ThisType& operator/=(const ThisType& t) {return *this = *this/t;} - //! - ThisType& operator%=(const ThisType& t) {return *this = *this%t;} - - //! - ThisType& operator<<=(unsigned int n) {ShiftLeft(n, ms_fixedRing); return *this;} - //! - ThisType& operator>>=(unsigned int n) {ShiftRight(n, ms_fixedRing); return *this;} - - //! set the coefficient for x^i to value - void SetCoefficient(unsigned int i, const CoefficientType &value) {B::SetCoefficient(i, value, ms_fixedRing);} - - //! - void Randomize(RandomNumberGenerator &rng, const RandomizationParameter ¶meter) {B::Randomize(rng, parameter, ms_fixedRing);} - - //! - void Negate() {B::Negate(ms_fixedRing);} - - void swap(ThisType &t) {B::swap(t);} - //@} - - //! \name UNARY OPERATORS - //@{ - //! - bool operator!() const {return CoefficientCount()==0;} - //! - ThisType operator+() const {return *this;} - //! - ThisType operator-() const {return ThisType(Inverse(ms_fixedRing));} - //@} - - //! \name BINARY OPERATORS - //@{ - //! - friend ThisType operator>>(ThisType a, unsigned int n) {return ThisType(a>>=n);} - //! - friend ThisType operator<<(ThisType a, unsigned int n) {return ThisType(a<<=n);} - //@} - - //! \name OTHER ARITHMETIC FUNCTIONS - //@{ - //! - ThisType MultiplicativeInverse() const {return ThisType(B::MultiplicativeInverse(ms_fixedRing));} - //! - bool IsUnit() const {return B::IsUnit(ms_fixedRing);} - - //! - ThisType Doubled() const {return ThisType(B::Doubled(ms_fixedRing));} - //! - ThisType Squared() const {return ThisType(B::Squared(ms_fixedRing));} - - CoefficientType EvaluateAt(const CoefficientType &x) const {return B::EvaluateAt(x, ms_fixedRing);} - - //! calculate r and q such that (a == d*q + r) && (0 <= r < abs(d)) - static void Divide(ThisType &r, ThisType &q, const ThisType &a, const ThisType &d) - {B::Divide(r, q, a, d, ms_fixedRing);} - //@} - - //! \name INPUT/OUTPUT - //@{ - //! - friend std::istream& operator>>(std::istream& in, ThisType &a) - {return a.Input(in, ms_fixedRing);} - //! - friend std::ostream& operator<<(std::ostream& out, const ThisType &a) - {return a.Output(out, ms_fixedRing);} - //@} - -private: - struct NewOnePolynomial - { - ThisType * operator()() const - { - return new ThisType(ms_fixedRing.MultiplicativeIdentity()); - } - }; - - static const Ring ms_fixedRing; -}; - -//! Ring of polynomials over another ring -template class RingOfPolynomialsOver : public AbstractEuclideanDomain > -{ -public: - typedef T CoefficientRing; - typedef PolynomialOver Element; - typedef typename Element::CoefficientType CoefficientType; - typedef typename Element::RandomizationParameter RandomizationParameter; - - RingOfPolynomialsOver(const CoefficientRing &ring) : m_ring(ring) {} - - Element RandomElement(RandomNumberGenerator &rng, const RandomizationParameter ¶meter) - {return Element(rng, parameter, m_ring);} - - bool Equal(const Element &a, const Element &b) const - {return a.Equals(b, m_ring);} - - const Element& Identity() const - {return this->result = m_ring.Identity();} - - const Element& Add(const Element &a, const Element &b) const - {return this->result = a.Plus(b, m_ring);} - - Element& Accumulate(Element &a, const Element &b) const - {a.Accumulate(b, m_ring); return a;} - - const Element& Inverse(const Element &a) const - {return this->result = a.Inverse(m_ring);} - - const Element& Subtract(const Element &a, const Element &b) const - {return this->result = a.Minus(b, m_ring);} - - Element& Reduce(Element &a, const Element &b) const - {return a.Reduce(b, m_ring);} - - const Element& Double(const Element &a) const - {return this->result = a.Doubled(m_ring);} - - const Element& MultiplicativeIdentity() const - {return this->result = m_ring.MultiplicativeIdentity();} - - const Element& Multiply(const Element &a, const Element &b) const - {return this->result = a.Times(b, m_ring);} - - const Element& Square(const Element &a) const - {return this->result = a.Squared(m_ring);} - - bool IsUnit(const Element &a) const - {return a.IsUnit(m_ring);} - - const Element& MultiplicativeInverse(const Element &a) const - {return this->result = a.MultiplicativeInverse(m_ring);} - - const Element& Divide(const Element &a, const Element &b) const - {return this->result = a.DividedBy(b, m_ring);} - - const Element& Mod(const Element &a, const Element &b) const - {return this->result = a.Modulo(b, m_ring);} - - void DivisionAlgorithm(Element &r, Element &q, const Element &a, const Element &d) const - {Element::Divide(r, q, a, d, m_ring);} - - class InterpolationFailed : public Exception - { - public: - InterpolationFailed() : Exception(OTHER_ERROR, "RingOfPolynomialsOver: interpolation failed") {} - }; - - Element Interpolate(const CoefficientType x[], const CoefficientType y[], unsigned int n) const; - - // a faster version of Interpolate(x, y, n).EvaluateAt(position) - CoefficientType InterpolateAt(const CoefficientType &position, const CoefficientType x[], const CoefficientType y[], unsigned int n) const; -/* - void PrepareBulkInterpolation(CoefficientType *w, const CoefficientType x[], unsigned int n) const; - void PrepareBulkInterpolationAt(CoefficientType *v, const CoefficientType &position, const CoefficientType x[], const CoefficientType w[], unsigned int n) const; - CoefficientType BulkInterpolateAt(const CoefficientType y[], const CoefficientType v[], unsigned int n) const; -*/ -protected: - void CalculateAlpha(std::vector &alpha, const CoefficientType x[], const CoefficientType y[], unsigned int n) const; - - CoefficientRing m_ring; -}; - -template -void PrepareBulkPolynomialInterpolation(const Ring &ring, Element *w, const Element x[], unsigned int n); -template -void PrepareBulkPolynomialInterpolationAt(const Ring &ring, Element *v, const Element &position, const Element x[], const Element w[], unsigned int n); -template -Element BulkPolynomialInterpolateAt(const Ring &ring, const Element y[], const Element v[], unsigned int n); - -//! -template -inline bool operator==(const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return a.Equals(b, a.ms_fixedRing);} -//! -template -inline bool operator!=(const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return !(a==b);} - -//! -template -inline bool operator> (const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return a.Degree() > b.Degree();} -//! -template -inline bool operator>=(const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return a.Degree() >= b.Degree();} -//! -template -inline bool operator< (const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return a.Degree() < b.Degree();} -//! -template -inline bool operator<=(const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return a.Degree() <= b.Degree();} - -//! -template -inline CryptoPP::PolynomialOverFixedRing operator+(const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return CryptoPP::PolynomialOverFixedRing(a.Plus(b, a.ms_fixedRing));} -//! -template -inline CryptoPP::PolynomialOverFixedRing operator-(const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return CryptoPP::PolynomialOverFixedRing(a.Minus(b, a.ms_fixedRing));} -//! -template -inline CryptoPP::PolynomialOverFixedRing operator*(const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return CryptoPP::PolynomialOverFixedRing(a.Times(b, a.ms_fixedRing));} -//! -template -inline CryptoPP::PolynomialOverFixedRing operator/(const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return CryptoPP::PolynomialOverFixedRing(a.DividedBy(b, a.ms_fixedRing));} -//! -template -inline CryptoPP::PolynomialOverFixedRing operator%(const CryptoPP::PolynomialOverFixedRing &a, const CryptoPP::PolynomialOverFixedRing &b) - {return CryptoPP::PolynomialOverFixedRing(a.Modulo(b, a.ms_fixedRing));} - -NAMESPACE_END - -NAMESPACE_BEGIN(std) -template inline void swap(CryptoPP::PolynomialOver &a, CryptoPP::PolynomialOver &b) -{ - a.swap(b); -} -template inline void swap(CryptoPP::PolynomialOverFixedRing &a, CryptoPP::PolynomialOverFixedRing &b) -{ - a.swap(b); -} -NAMESPACE_END - -#endif diff --git a/CryptoPP/pssr.cpp b/CryptoPP/pssr.cpp deleted file mode 100644 index ccbe4ee27..000000000 --- a/CryptoPP/pssr.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// pssr.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "pssr.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -// more in dll.cpp -template<> const byte EMSA2HashId::id = 0x31; -template<> const byte EMSA2HashId::id = 0x32; -template<> const byte EMSA2HashId::id = 0x37; - -#ifndef CRYPTOPP_IMPORTS - -size_t PSSR_MEM_Base::MinRepresentativeBitLength(size_t hashIdentifierLength, size_t digestLength) const -{ - size_t saltLen = SaltLen(digestLength); - size_t minPadLen = MinPadLen(digestLength); - return 9 + 8*(minPadLen + saltLen + digestLength + hashIdentifierLength); -} - -size_t PSSR_MEM_Base::MaxRecoverableLength(size_t representativeBitLength, size_t hashIdentifierLength, size_t digestLength) const -{ - if (AllowRecovery()) - return SaturatingSubtract(representativeBitLength, MinRepresentativeBitLength(hashIdentifierLength, digestLength)) / 8; - return 0; -} - -bool PSSR_MEM_Base::IsProbabilistic() const -{ - return SaltLen(1) > 0; -} - -bool PSSR_MEM_Base::AllowNonrecoverablePart() const -{ - return true; -} - -bool PSSR_MEM_Base::RecoverablePartFirst() const -{ - return false; -} - -void PSSR_MEM_Base::ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const -{ - assert(representativeBitLength >= MinRepresentativeBitLength(hashIdentifier.second, hash.DigestSize())); - - const size_t u = hashIdentifier.second + 1; - const size_t representativeByteLength = BitsToBytes(representativeBitLength); - const size_t digestSize = hash.DigestSize(); - const size_t saltSize = SaltLen(digestSize); - byte *const h = representative + representativeByteLength - u - digestSize; - - SecByteBlock digest(digestSize), salt(saltSize); - hash.Final(digest); - rng.GenerateBlock(salt, saltSize); - - // compute H = hash of M' - byte c[8]; - PutWord(false, BIG_ENDIAN_ORDER, c, (word32)SafeRightShift<29>(recoverableMessageLength)); - PutWord(false, BIG_ENDIAN_ORDER, c+4, word32(recoverableMessageLength << 3)); - hash.Update(c, 8); - hash.Update(recoverableMessage, recoverableMessageLength); - hash.Update(digest, digestSize); - hash.Update(salt, saltSize); - hash.Final(h); - - // compute representative - GetMGF().GenerateAndMask(hash, representative, representativeByteLength - u - digestSize, h, digestSize, false); - byte *xorStart = representative + representativeByteLength - u - digestSize - salt.size() - recoverableMessageLength - 1; - xorStart[0] ^= 1; - xorbuf(xorStart + 1, recoverableMessage, recoverableMessageLength); - xorbuf(xorStart + 1 + recoverableMessageLength, salt, salt.size()); - memcpy(representative + representativeByteLength - u, hashIdentifier.first, hashIdentifier.second); - representative[representativeByteLength - 1] = hashIdentifier.second ? 0xcc : 0xbc; - if (representativeBitLength % 8 != 0) - representative[0] = (byte)Crop(representative[0], representativeBitLength % 8); -} - -DecodingResult PSSR_MEM_Base::RecoverMessageFromRepresentative( - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength, - byte *recoverableMessage) const -{ - assert(representativeBitLength >= MinRepresentativeBitLength(hashIdentifier.second, hash.DigestSize())); - - const size_t u = hashIdentifier.second + 1; - const size_t representativeByteLength = BitsToBytes(representativeBitLength); - const size_t digestSize = hash.DigestSize(); - const size_t saltSize = SaltLen(digestSize); - const byte *const h = representative + representativeByteLength - u - digestSize; - - SecByteBlock digest(digestSize); - hash.Final(digest); - - DecodingResult result(0); - bool &valid = result.isValidCoding; - size_t &recoverableMessageLength = result.messageLength; - - valid = (representative[representativeByteLength - 1] == (hashIdentifier.second ? 0xcc : 0xbc)) && valid; - valid = VerifyBufsEqual(representative + representativeByteLength - u, hashIdentifier.first, hashIdentifier.second) && valid; - - GetMGF().GenerateAndMask(hash, representative, representativeByteLength - u - digestSize, h, digestSize); - if (representativeBitLength % 8 != 0) - representative[0] = (byte)Crop(representative[0], representativeBitLength % 8); - - // extract salt and recoverableMessage from DB = 00 ... || 01 || M || salt - byte *salt = representative + representativeByteLength - u - digestSize - saltSize; - byte *M = std::find_if(representative, salt-1, std::bind2nd(std::not_equal_to(), 0)); - recoverableMessageLength = salt-M-1; - if (*M == 0x01 - && (size_t)(M - representative - (representativeBitLength % 8 != 0)) >= MinPadLen(digestSize) - && recoverableMessageLength <= MaxRecoverableLength(representativeBitLength, hashIdentifier.second, digestSize)) - { - memcpy(recoverableMessage, M+1, recoverableMessageLength); - } - else - { - recoverableMessageLength = 0; - valid = false; - } - - // verify H = hash of M' - byte c[8]; - PutWord(false, BIG_ENDIAN_ORDER, c, (word32)SafeRightShift<29>(recoverableMessageLength)); - PutWord(false, BIG_ENDIAN_ORDER, c+4, word32(recoverableMessageLength << 3)); - hash.Update(c, 8); - hash.Update(recoverableMessage, recoverableMessageLength); - hash.Update(digest, digestSize); - hash.Update(salt, saltSize); - valid = hash.Verify(h) && valid; - - if (!AllowRecovery() && valid && recoverableMessageLength != 0) - {throw NotImplemented("PSSR_MEM: message recovery disabled");} - - return result; -} - -#endif - -NAMESPACE_END diff --git a/CryptoPP/pssr.h b/CryptoPP/pssr.h deleted file mode 100644 index 6ec6936e5..000000000 --- a/CryptoPP/pssr.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef CRYPTOPP_PSSR_H -#define CRYPTOPP_PSSR_H - -#include "pubkey.h" -#include "emsa2.h" - -#ifdef CRYPTOPP_IS_DLL -#include "sha.h" -#endif - -NAMESPACE_BEGIN(CryptoPP) - -class CRYPTOPP_DLL PSSR_MEM_Base : public PK_RecoverableSignatureMessageEncodingMethod -{ - virtual bool AllowRecovery() const =0; - virtual size_t SaltLen(size_t hashLen) const =0; - virtual size_t MinPadLen(size_t hashLen) const =0; - virtual const MaskGeneratingFunction & GetMGF() const =0; - -public: - size_t MinRepresentativeBitLength(size_t hashIdentifierLength, size_t digestLength) const; - size_t MaxRecoverableLength(size_t representativeBitLength, size_t hashIdentifierLength, size_t digestLength) const; - bool IsProbabilistic() const; - bool AllowNonrecoverablePart() const; - bool RecoverablePartFirst() const; - void ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const; - DecodingResult RecoverMessageFromRepresentative( - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength, - byte *recoverableMessage) const; -}; - -template class PSSR_MEM_BaseWithHashId; -template<> class PSSR_MEM_BaseWithHashId : public EMSA2HashIdLookup {}; -template<> class PSSR_MEM_BaseWithHashId : public PSSR_MEM_Base {}; - -template -class PSSR_MEM : public PSSR_MEM_BaseWithHashId -{ - virtual bool AllowRecovery() const {return ALLOW_RECOVERY;} - virtual size_t SaltLen(size_t hashLen) const {return SALT_LEN < 0 ? hashLen : SALT_LEN;} - virtual size_t MinPadLen(size_t hashLen) const {return MIN_PAD_LEN < 0 ? hashLen : MIN_PAD_LEN;} - virtual const MaskGeneratingFunction & GetMGF() const {static MGF mgf; return mgf;} - -public: - static std::string CRYPTOPP_API StaticAlgorithmName() {return std::string(ALLOW_RECOVERY ? "PSSR-" : "PSS-") + MGF::StaticAlgorithmName();} -}; - -//! PSSR-MGF1 -struct PSSR : public SignatureStandard -{ - typedef PSSR_MEM SignatureMessageEncodingMethod; -}; - -//! PSS-MGF1 -struct PSS : public SignatureStandard -{ - typedef PSSR_MEM SignatureMessageEncodingMethod; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/pubkey.cpp b/CryptoPP/pubkey.cpp deleted file mode 100644 index 1159e5343..000000000 --- a/CryptoPP/pubkey.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// pubkey.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "pubkey.h" - -NAMESPACE_BEGIN(CryptoPP) - -void P1363_MGF1KDF2_Common(HashTransformation &hash, byte *output, size_t outputLength, const byte *input, size_t inputLength, const byte *derivationParams, size_t derivationParamsLength, bool mask, unsigned int counterStart) -{ - ArraySink *sink; - HashFilter filter(hash, sink = mask ? new ArrayXorSink(output, outputLength) : new ArraySink(output, outputLength)); - word32 counter = counterStart; - while (sink->AvailableSize() > 0) - { - filter.Put(input, inputLength); - filter.PutWord32(counter++); - filter.Put(derivationParams, derivationParamsLength); - filter.MessageEnd(); - } -} - -bool PK_DeterministicSignatureMessageEncodingMethod::VerifyMessageRepresentative( - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const -{ - SecByteBlock computedRepresentative(BitsToBytes(representativeBitLength)); - ComputeMessageRepresentative(NullRNG(), NULL, 0, hash, hashIdentifier, messageEmpty, computedRepresentative, representativeBitLength); - return VerifyBufsEqual(representative, computedRepresentative, computedRepresentative.size()); -} - -bool PK_RecoverableSignatureMessageEncodingMethod::VerifyMessageRepresentative( - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const -{ - SecByteBlock recoveredMessage(MaxRecoverableLength(representativeBitLength, hashIdentifier.second, hash.DigestSize())); - DecodingResult result = RecoverMessageFromRepresentative( - hash, hashIdentifier, messageEmpty, representative, representativeBitLength, recoveredMessage); - return result.isValidCoding && result.messageLength == 0; -} - -void TF_SignerBase::InputRecoverableMessage(PK_MessageAccumulator &messageAccumulator, const byte *recoverableMessage, size_t recoverableMessageLength) const -{ - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - HashIdentifier id = GetHashIdentifier(); - const MessageEncodingInterface &encoding = GetMessageEncodingInterface(); - - if (MessageRepresentativeBitLength() < encoding.MinRepresentativeBitLength(id.second, ma.AccessHash().DigestSize())) - throw PK_SignatureScheme::KeyTooShort(); - - size_t maxRecoverableLength = encoding.MaxRecoverableLength(MessageRepresentativeBitLength(), GetHashIdentifier().second, ma.AccessHash().DigestSize()); - - if (maxRecoverableLength == 0) - {throw NotImplemented("TF_SignerBase: this algorithm does not support messsage recovery or the key is too short");} - if (recoverableMessageLength > maxRecoverableLength) - throw InvalidArgument("TF_SignerBase: the recoverable message part is too long for the given key and algorithm"); - - ma.m_recoverableMessage.Assign(recoverableMessage, recoverableMessageLength); - encoding.ProcessRecoverableMessage( - ma.AccessHash(), - recoverableMessage, recoverableMessageLength, - NULL, 0, ma.m_semisignature); -} - -size_t TF_SignerBase::SignAndRestart(RandomNumberGenerator &rng, PK_MessageAccumulator &messageAccumulator, byte *signature, bool restart) const -{ - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - HashIdentifier id = GetHashIdentifier(); - const MessageEncodingInterface &encoding = GetMessageEncodingInterface(); - - if (MessageRepresentativeBitLength() < encoding.MinRepresentativeBitLength(id.second, ma.AccessHash().DigestSize())) - throw PK_SignatureScheme::KeyTooShort(); - - SecByteBlock representative(MessageRepresentativeLength()); - encoding.ComputeMessageRepresentative(rng, - ma.m_recoverableMessage, ma.m_recoverableMessage.size(), - ma.AccessHash(), id, ma.m_empty, - representative, MessageRepresentativeBitLength()); - ma.m_empty = true; - - Integer r(representative, representative.size()); - size_t signatureLength = SignatureLength(); - GetTrapdoorFunctionInterface().CalculateRandomizedInverse(rng, r).Encode(signature, signatureLength); - return signatureLength; -} - -void TF_VerifierBase::InputSignature(PK_MessageAccumulator &messageAccumulator, const byte *signature, size_t signatureLength) const -{ - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - HashIdentifier id = GetHashIdentifier(); - const MessageEncodingInterface &encoding = GetMessageEncodingInterface(); - - if (MessageRepresentativeBitLength() < encoding.MinRepresentativeBitLength(id.second, ma.AccessHash().DigestSize())) - throw PK_SignatureScheme::KeyTooShort(); - - ma.m_representative.New(MessageRepresentativeLength()); - Integer x = GetTrapdoorFunctionInterface().ApplyFunction(Integer(signature, signatureLength)); - if (x.BitCount() > MessageRepresentativeBitLength()) - x = Integer::Zero(); // don't return false here to prevent timing attack - x.Encode(ma.m_representative, ma.m_representative.size()); -} - -bool TF_VerifierBase::VerifyAndRestart(PK_MessageAccumulator &messageAccumulator) const -{ - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - HashIdentifier id = GetHashIdentifier(); - const MessageEncodingInterface &encoding = GetMessageEncodingInterface(); - - if (MessageRepresentativeBitLength() < encoding.MinRepresentativeBitLength(id.second, ma.AccessHash().DigestSize())) - throw PK_SignatureScheme::KeyTooShort(); - - bool result = encoding.VerifyMessageRepresentative( - ma.AccessHash(), id, ma.m_empty, ma.m_representative, MessageRepresentativeBitLength()); - ma.m_empty = true; - return result; -} - -DecodingResult TF_VerifierBase::RecoverAndRestart(byte *recoveredMessage, PK_MessageAccumulator &messageAccumulator) const -{ - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - HashIdentifier id = GetHashIdentifier(); - const MessageEncodingInterface &encoding = GetMessageEncodingInterface(); - - if (MessageRepresentativeBitLength() < encoding.MinRepresentativeBitLength(id.second, ma.AccessHash().DigestSize())) - throw PK_SignatureScheme::KeyTooShort(); - - DecodingResult result = encoding.RecoverMessageFromRepresentative( - ma.AccessHash(), id, ma.m_empty, ma.m_representative, MessageRepresentativeBitLength(), recoveredMessage); - ma.m_empty = true; - return result; -} - -DecodingResult TF_DecryptorBase::Decrypt(RandomNumberGenerator &rng, const byte *ciphertext, size_t ciphertextLength, byte *plaintext, const NameValuePairs ¶meters) const -{ - if (ciphertextLength != FixedCiphertextLength()) - throw InvalidArgument(AlgorithmName() + ": ciphertext length of " + IntToString(ciphertextLength) + " doesn't match the required length of " + IntToString(FixedCiphertextLength()) + " for this key"); - - SecByteBlock paddedBlock(PaddedBlockByteLength()); - Integer x = GetTrapdoorFunctionInterface().CalculateInverse(rng, Integer(ciphertext, ciphertextLength)); - if (x.ByteCount() > paddedBlock.size()) - x = Integer::Zero(); // don't return false here to prevent timing attack - x.Encode(paddedBlock, paddedBlock.size()); - return GetMessageEncodingInterface().Unpad(paddedBlock, PaddedBlockBitLength(), plaintext, parameters); -} - -void TF_EncryptorBase::Encrypt(RandomNumberGenerator &rng, const byte *plaintext, size_t plaintextLength, byte *ciphertext, const NameValuePairs ¶meters) const -{ - if (plaintextLength > FixedMaxPlaintextLength()) - { - if (FixedMaxPlaintextLength() < 1) - throw InvalidArgument(AlgorithmName() + ": this key is too short to encrypt any messages"); - else - throw InvalidArgument(AlgorithmName() + ": message length of " + IntToString(plaintextLength) + " exceeds the maximum of " + IntToString(FixedMaxPlaintextLength()) + " for this public key"); - } - - SecByteBlock paddedBlock(PaddedBlockByteLength()); - GetMessageEncodingInterface().Pad(rng, plaintext, plaintextLength, paddedBlock, PaddedBlockBitLength(), parameters); - GetTrapdoorFunctionInterface().ApplyRandomizedFunction(rng, Integer(paddedBlock, paddedBlock.size())).Encode(ciphertext, FixedCiphertextLength()); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/pubkey.h b/CryptoPP/pubkey.h deleted file mode 100644 index 3a3f3bcde..000000000 --- a/CryptoPP/pubkey.h +++ /dev/null @@ -1,1678 +0,0 @@ -// pubkey.h - written and placed in the public domain by Wei Dai - -#ifndef CRYPTOPP_PUBKEY_H -#define CRYPTOPP_PUBKEY_H - -/** \file - - This file contains helper classes/functions for implementing public key algorithms. - - The class hierachies in this .h file tend to look like this: -

-                  x1
-                 / \
-                y1  z1
-                 |  |
-            x2  x2
-                 |  |
-                y2  z2
-                 |  |
-            x3  x3
-                 |  |
-                y3  z3
-
- - x1, y1, z1 are abstract interface classes defined in cryptlib.h - - x2, y2, z2 are implementations of the interfaces using "abstract policies", which - are pure virtual functions that should return interfaces to interchangeable algorithms. - These classes have "Base" suffixes. - - x3, y3, z3 hold actual algorithms and implement those virtual functions. - These classes have "Impl" suffixes. - - The "TF_" prefix means an implementation using trapdoor functions on integers. - The "DL_" prefix means an implementation using group operations (in groups where discrete log is hard). -*/ - -#include "modarith.h" -#include "filters.h" -#include "eprecomp.h" -#include "fips140.h" -#include "argnames.h" -#include - -// VC60 workaround: this macro is defined in shlobj.h and conflicts with a template parameter used in this file -#undef INTERFACE - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TrapdoorFunctionBounds -{ -public: - virtual ~TrapdoorFunctionBounds() {} - - virtual Integer PreimageBound() const =0; - virtual Integer ImageBound() const =0; - virtual Integer MaxPreimage() const {return --PreimageBound();} - virtual Integer MaxImage() const {return --ImageBound();} -}; - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE RandomizedTrapdoorFunction : public TrapdoorFunctionBounds -{ -public: - virtual Integer ApplyRandomizedFunction(RandomNumberGenerator &rng, const Integer &x) const =0; - virtual bool IsRandomized() const {return true;} -}; - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TrapdoorFunction : public RandomizedTrapdoorFunction -{ -public: - Integer ApplyRandomizedFunction(RandomNumberGenerator &rng, const Integer &x) const - {return ApplyFunction(x);} - bool IsRandomized() const {return false;} - - virtual Integer ApplyFunction(const Integer &x) const =0; -}; - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE RandomizedTrapdoorFunctionInverse -{ -public: - virtual ~RandomizedTrapdoorFunctionInverse() {} - - virtual Integer CalculateRandomizedInverse(RandomNumberGenerator &rng, const Integer &x) const =0; - virtual bool IsRandomized() const {return true;} -}; - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TrapdoorFunctionInverse : public RandomizedTrapdoorFunctionInverse -{ -public: - virtual ~TrapdoorFunctionInverse() {} - - Integer CalculateRandomizedInverse(RandomNumberGenerator &rng, const Integer &x) const - {return CalculateInverse(rng, x);} - bool IsRandomized() const {return false;} - - virtual Integer CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const =0; -}; - -// ******************************************************** - -//! message encoding method for public key encryption -class CRYPTOPP_NO_VTABLE PK_EncryptionMessageEncodingMethod -{ -public: - virtual ~PK_EncryptionMessageEncodingMethod() {} - - virtual bool ParameterSupported(const char *name) const {return false;} - - //! max size of unpadded message in bytes, given max size of padded message in bits (1 less than size of modulus) - virtual size_t MaxUnpaddedLength(size_t paddedLength) const =0; - - virtual void Pad(RandomNumberGenerator &rng, const byte *raw, size_t inputLength, byte *padded, size_t paddedBitLength, const NameValuePairs ¶meters) const =0; - - virtual DecodingResult Unpad(const byte *padded, size_t paddedBitLength, byte *raw, const NameValuePairs ¶meters) const =0; -}; - -// ******************************************************** - -//! _ -template -class CRYPTOPP_NO_VTABLE TF_Base -{ -protected: - virtual const TrapdoorFunctionBounds & GetTrapdoorFunctionBounds() const =0; - - typedef TFI TrapdoorFunctionInterface; - virtual const TrapdoorFunctionInterface & GetTrapdoorFunctionInterface() const =0; - - typedef MEI MessageEncodingInterface; - virtual const MessageEncodingInterface & GetMessageEncodingInterface() const =0; -}; - -// ******************************************************** - -//! _ -template -class CRYPTOPP_NO_VTABLE PK_FixedLengthCryptoSystemImpl : public BASE -{ -public: - size_t MaxPlaintextLength(size_t ciphertextLength) const - {return ciphertextLength == FixedCiphertextLength() ? FixedMaxPlaintextLength() : 0;} - size_t CiphertextLength(size_t plaintextLength) const - {return plaintextLength <= FixedMaxPlaintextLength() ? FixedCiphertextLength() : 0;} - - virtual size_t FixedMaxPlaintextLength() const =0; - virtual size_t FixedCiphertextLength() const =0; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE TF_CryptoSystemBase : public PK_FixedLengthCryptoSystemImpl, protected BASE -{ -public: - bool ParameterSupported(const char *name) const {return this->GetMessageEncodingInterface().ParameterSupported(name);} - size_t FixedMaxPlaintextLength() const {return this->GetMessageEncodingInterface().MaxUnpaddedLength(PaddedBlockBitLength());} - size_t FixedCiphertextLength() const {return this->GetTrapdoorFunctionBounds().MaxImage().ByteCount();} - -protected: - size_t PaddedBlockByteLength() const {return BitsToBytes(PaddedBlockBitLength());} - size_t PaddedBlockBitLength() const {return this->GetTrapdoorFunctionBounds().PreimageBound().BitCount()-1;} -}; - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TF_DecryptorBase : public TF_CryptoSystemBase > -{ -public: - DecodingResult Decrypt(RandomNumberGenerator &rng, const byte *ciphertext, size_t ciphertextLength, byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const; -}; - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TF_EncryptorBase : public TF_CryptoSystemBase > -{ -public: - void Encrypt(RandomNumberGenerator &rng, const byte *plaintext, size_t plaintextLength, byte *ciphertext, const NameValuePairs ¶meters = g_nullNameValuePairs) const; -}; - -// ******************************************************** - -typedef std::pair HashIdentifier; - -//! interface for message encoding method for public key signature schemes -class CRYPTOPP_NO_VTABLE PK_SignatureMessageEncodingMethod -{ -public: - virtual ~PK_SignatureMessageEncodingMethod() {} - - virtual size_t MinRepresentativeBitLength(size_t hashIdentifierLength, size_t digestLength) const - {return 0;} - virtual size_t MaxRecoverableLength(size_t representativeBitLength, size_t hashIdentifierLength, size_t digestLength) const - {return 0;} - - bool IsProbabilistic() const - {return true;} - bool AllowNonrecoverablePart() const - {throw NotImplemented("PK_MessageEncodingMethod: this signature scheme does not support message recovery");} - virtual bool RecoverablePartFirst() const - {throw NotImplemented("PK_MessageEncodingMethod: this signature scheme does not support message recovery");} - - // for verification, DL - virtual void ProcessSemisignature(HashTransformation &hash, const byte *semisignature, size_t semisignatureLength) const {} - - // for signature - virtual void ProcessRecoverableMessage(HashTransformation &hash, - const byte *recoverableMessage, size_t recoverableMessageLength, - const byte *presignature, size_t presignatureLength, - SecByteBlock &semisignature) const - { - if (RecoverablePartFirst()) - assert(!"ProcessRecoverableMessage() not implemented"); - } - - virtual void ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const =0; - - virtual bool VerifyMessageRepresentative( - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const =0; - - virtual DecodingResult RecoverMessageFromRepresentative( // for TF - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength, - byte *recoveredMessage) const - {throw NotImplemented("PK_MessageEncodingMethod: this signature scheme does not support message recovery");} - - virtual DecodingResult RecoverMessageFromSemisignature( // for DL - HashTransformation &hash, HashIdentifier hashIdentifier, - const byte *presignature, size_t presignatureLength, - const byte *semisignature, size_t semisignatureLength, - byte *recoveredMessage) const - {throw NotImplemented("PK_MessageEncodingMethod: this signature scheme does not support message recovery");} - - // VC60 workaround - struct HashIdentifierLookup - { - template struct HashIdentifierLookup2 - { - static HashIdentifier CRYPTOPP_API Lookup() - { - return HashIdentifier((const byte *)NULL, 0); - } - }; - }; -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_DeterministicSignatureMessageEncodingMethod : public PK_SignatureMessageEncodingMethod -{ -public: - bool VerifyMessageRepresentative( - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const; -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_RecoverableSignatureMessageEncodingMethod : public PK_SignatureMessageEncodingMethod -{ -public: - bool VerifyMessageRepresentative( - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const; -}; - -class CRYPTOPP_DLL DL_SignatureMessageEncodingMethod_DSA : public PK_DeterministicSignatureMessageEncodingMethod -{ -public: - void ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const; -}; - -class CRYPTOPP_DLL DL_SignatureMessageEncodingMethod_NR : public PK_DeterministicSignatureMessageEncodingMethod -{ -public: - void ComputeMessageRepresentative(RandomNumberGenerator &rng, - const byte *recoverableMessage, size_t recoverableMessageLength, - HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty, - byte *representative, size_t representativeBitLength) const; -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_MessageAccumulatorBase : public PK_MessageAccumulator -{ -public: - PK_MessageAccumulatorBase() : m_empty(true) {} - - virtual HashTransformation & AccessHash() =0; - - void Update(const byte *input, size_t length) - { - AccessHash().Update(input, length); - m_empty = m_empty && length == 0; - } - - SecByteBlock m_recoverableMessage, m_representative, m_presignature, m_semisignature; - Integer m_k, m_s; - bool m_empty; -}; - -template -class PK_MessageAccumulatorImpl : public PK_MessageAccumulatorBase, protected ObjectHolder -{ -public: - HashTransformation & AccessHash() {return this->m_object;} -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE TF_SignatureSchemeBase : public INTERFACE, protected BASE -{ -public: - size_t SignatureLength() const - {return this->GetTrapdoorFunctionBounds().MaxPreimage().ByteCount();} - size_t MaxRecoverableLength() const - {return this->GetMessageEncodingInterface().MaxRecoverableLength(MessageRepresentativeBitLength(), GetHashIdentifier().second, GetDigestSize());} - size_t MaxRecoverableLengthFromSignatureLength(size_t signatureLength) const - {return this->MaxRecoverableLength();} - - bool IsProbabilistic() const - {return this->GetTrapdoorFunctionInterface().IsRandomized() || this->GetMessageEncodingInterface().IsProbabilistic();} - bool AllowNonrecoverablePart() const - {return this->GetMessageEncodingInterface().AllowNonrecoverablePart();} - bool RecoverablePartFirst() const - {return this->GetMessageEncodingInterface().RecoverablePartFirst();} - -protected: - size_t MessageRepresentativeLength() const {return BitsToBytes(MessageRepresentativeBitLength());} - size_t MessageRepresentativeBitLength() const {return this->GetTrapdoorFunctionBounds().ImageBound().BitCount()-1;} - virtual HashIdentifier GetHashIdentifier() const =0; - virtual size_t GetDigestSize() const =0; -}; - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TF_SignerBase : public TF_SignatureSchemeBase > -{ -public: - void InputRecoverableMessage(PK_MessageAccumulator &messageAccumulator, const byte *recoverableMessage, size_t recoverableMessageLength) const; - size_t SignAndRestart(RandomNumberGenerator &rng, PK_MessageAccumulator &messageAccumulator, byte *signature, bool restart=true) const; -}; - -//! _ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TF_VerifierBase : public TF_SignatureSchemeBase > -{ -public: - void InputSignature(PK_MessageAccumulator &messageAccumulator, const byte *signature, size_t signatureLength) const; - bool VerifyAndRestart(PK_MessageAccumulator &messageAccumulator) const; - DecodingResult RecoverAndRestart(byte *recoveredMessage, PK_MessageAccumulator &recoveryAccumulator) const; -}; - -// ******************************************************** - -//! _ -template -struct TF_CryptoSchemeOptions -{ - typedef T1 AlgorithmInfo; - typedef T2 Keys; - typedef typename Keys::PrivateKey PrivateKey; - typedef typename Keys::PublicKey PublicKey; - typedef T3 MessageEncodingMethod; -}; - -//! _ -template -struct TF_SignatureSchemeOptions : public TF_CryptoSchemeOptions -{ - typedef T4 HashFunction; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE TF_ObjectImplBase : public AlgorithmImpl -{ -public: - typedef SCHEME_OPTIONS SchemeOptions; - typedef KEY_CLASS KeyClass; - - PublicKey & AccessPublicKey() {return AccessKey();} - const PublicKey & GetPublicKey() const {return GetKey();} - - PrivateKey & AccessPrivateKey() {return AccessKey();} - const PrivateKey & GetPrivateKey() const {return GetKey();} - - virtual const KeyClass & GetKey() const =0; - virtual KeyClass & AccessKey() =0; - - const KeyClass & GetTrapdoorFunction() const {return GetKey();} - - PK_MessageAccumulator * NewSignatureAccumulator(RandomNumberGenerator &rng) const - { - return new PK_MessageAccumulatorImpl; - } - PK_MessageAccumulator * NewVerificationAccumulator() const - { - return new PK_MessageAccumulatorImpl; - } - -protected: - const typename BASE::MessageEncodingInterface & GetMessageEncodingInterface() const - {return Singleton().Ref();} - const TrapdoorFunctionBounds & GetTrapdoorFunctionBounds() const - {return GetKey();} - const typename BASE::TrapdoorFunctionInterface & GetTrapdoorFunctionInterface() const - {return GetKey();} - - // for signature scheme - HashIdentifier GetHashIdentifier() const - { - typedef CPP_TYPENAME SchemeOptions::MessageEncodingMethod::HashIdentifierLookup::template HashIdentifierLookup2 L; - return L::Lookup(); - } - size_t GetDigestSize() const - { - typedef CPP_TYPENAME SchemeOptions::HashFunction H; - return H::DIGESTSIZE; - } -}; - -//! _ -template -class TF_ObjectImplExtRef : public TF_ObjectImplBase -{ -public: - TF_ObjectImplExtRef(const KEY *pKey = NULL) : m_pKey(pKey) {} - void SetKeyPtr(const KEY *pKey) {m_pKey = pKey;} - - const KEY & GetKey() const {return *m_pKey;} - KEY & AccessKey() {throw NotImplemented("TF_ObjectImplExtRef: cannot modify refererenced key");} - -private: - const KEY * m_pKey; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE TF_ObjectImpl : public TF_ObjectImplBase -{ -public: - typedef KEY_CLASS KeyClass; - - const KeyClass & GetKey() const {return m_trapdoorFunction;} - KeyClass & AccessKey() {return m_trapdoorFunction;} - -private: - KeyClass m_trapdoorFunction; -}; - -//! _ -template -class TF_DecryptorImpl : public TF_ObjectImpl -{ -}; - -//! _ -template -class TF_EncryptorImpl : public TF_ObjectImpl -{ -}; - -//! _ -template -class TF_SignerImpl : public TF_ObjectImpl -{ -}; - -//! _ -template -class TF_VerifierImpl : public TF_ObjectImpl -{ -}; - -// ******************************************************** - -//! _ -class CRYPTOPP_NO_VTABLE MaskGeneratingFunction -{ -public: - virtual ~MaskGeneratingFunction() {} - virtual void GenerateAndMask(HashTransformation &hash, byte *output, size_t outputLength, const byte *input, size_t inputLength, bool mask = true) const =0; -}; - -CRYPTOPP_DLL void CRYPTOPP_API P1363_MGF1KDF2_Common(HashTransformation &hash, byte *output, size_t outputLength, const byte *input, size_t inputLength, const byte *derivationParams, size_t derivationParamsLength, bool mask, unsigned int counterStart); - -//! _ -class P1363_MGF1 : public MaskGeneratingFunction -{ -public: - static const char * CRYPTOPP_API StaticAlgorithmName() {return "MGF1";} - void GenerateAndMask(HashTransformation &hash, byte *output, size_t outputLength, const byte *input, size_t inputLength, bool mask = true) const - { - P1363_MGF1KDF2_Common(hash, output, outputLength, input, inputLength, NULL, 0, mask, 0); - } -}; - -// ******************************************************** - -//! _ -template -class P1363_KDF2 -{ -public: - static void CRYPTOPP_API DeriveKey(byte *output, size_t outputLength, const byte *input, size_t inputLength, const byte *derivationParams, size_t derivationParamsLength) - { - H h; - P1363_MGF1KDF2_Common(h, output, outputLength, input, inputLength, derivationParams, derivationParamsLength, false, 1); - } -}; - -// ******************************************************** - -//! to be thrown by DecodeElement and AgreeWithStaticPrivateKey -class DL_BadElement : public InvalidDataFormat -{ -public: - DL_BadElement() : InvalidDataFormat("CryptoPP: invalid group element") {} -}; - -//! interface for DL group parameters -template -class CRYPTOPP_NO_VTABLE DL_GroupParameters : public CryptoParameters -{ - typedef DL_GroupParameters ThisClass; - -public: - typedef T Element; - - DL_GroupParameters() : m_validationLevel(0) {} - - // CryptoMaterial - bool Validate(RandomNumberGenerator &rng, unsigned int level) const - { - if (!GetBasePrecomputation().IsInitialized()) - return false; - - if (m_validationLevel > level) - return true; - - bool pass = ValidateGroup(rng, level); - pass = pass && ValidateElement(level, GetSubgroupGenerator(), &GetBasePrecomputation()); - - m_validationLevel = pass ? level+1 : 0; - - return pass; - } - - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const - { - return GetValueHelper(this, name, valueType, pValue) - CRYPTOPP_GET_FUNCTION_ENTRY(SubgroupOrder) - CRYPTOPP_GET_FUNCTION_ENTRY(SubgroupGenerator) - ; - } - - bool SupportsPrecomputation() const {return true;} - - void Precompute(unsigned int precomputationStorage=16) - { - AccessBasePrecomputation().Precompute(GetGroupPrecomputation(), GetSubgroupOrder().BitCount(), precomputationStorage); - } - - void LoadPrecomputation(BufferedTransformation &storedPrecomputation) - { - AccessBasePrecomputation().Load(GetGroupPrecomputation(), storedPrecomputation); - m_validationLevel = 0; - } - - void SavePrecomputation(BufferedTransformation &storedPrecomputation) const - { - GetBasePrecomputation().Save(GetGroupPrecomputation(), storedPrecomputation); - } - - // non-inherited - virtual const Element & GetSubgroupGenerator() const {return GetBasePrecomputation().GetBase(GetGroupPrecomputation());} - virtual void SetSubgroupGenerator(const Element &base) {AccessBasePrecomputation().SetBase(GetGroupPrecomputation(), base);} - virtual Element ExponentiateBase(const Integer &exponent) const - { - return GetBasePrecomputation().Exponentiate(GetGroupPrecomputation(), exponent); - } - virtual Element ExponentiateElement(const Element &base, const Integer &exponent) const - { - Element result; - SimultaneousExponentiate(&result, base, &exponent, 1); - return result; - } - - virtual const DL_GroupPrecomputation & GetGroupPrecomputation() const =0; - virtual const DL_FixedBasePrecomputation & GetBasePrecomputation() const =0; - virtual DL_FixedBasePrecomputation & AccessBasePrecomputation() =0; - virtual const Integer & GetSubgroupOrder() const =0; // order of subgroup generated by base element - virtual Integer GetMaxExponent() const =0; - virtual Integer GetGroupOrder() const {return GetSubgroupOrder()*GetCofactor();} // one of these two needs to be overriden - virtual Integer GetCofactor() const {return GetGroupOrder()/GetSubgroupOrder();} - virtual unsigned int GetEncodedElementSize(bool reversible) const =0; - virtual void EncodeElement(bool reversible, const Element &element, byte *encoded) const =0; - virtual Element DecodeElement(const byte *encoded, bool checkForGroupMembership) const =0; - virtual Integer ConvertElementToInteger(const Element &element) const =0; - virtual bool ValidateGroup(RandomNumberGenerator &rng, unsigned int level) const =0; - virtual bool ValidateElement(unsigned int level, const Element &element, const DL_FixedBasePrecomputation *precomp) const =0; - virtual bool FastSubgroupCheckAvailable() const =0; - virtual bool IsIdentity(const Element &element) const =0; - virtual void SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const =0; - -protected: - void ParametersChanged() {m_validationLevel = 0;} - -private: - mutable unsigned int m_validationLevel; -}; - -//! _ -template , class BASE = DL_GroupParameters > -class DL_GroupParametersImpl : public BASE -{ -public: - typedef GROUP_PRECOMP GroupPrecomputation; - typedef typename GROUP_PRECOMP::Element Element; - typedef BASE_PRECOMP BasePrecomputation; - - const DL_GroupPrecomputation & GetGroupPrecomputation() const {return m_groupPrecomputation;} - const DL_FixedBasePrecomputation & GetBasePrecomputation() const {return m_gpc;} - DL_FixedBasePrecomputation & AccessBasePrecomputation() {return m_gpc;} - -protected: - GROUP_PRECOMP m_groupPrecomputation; - BASE_PRECOMP m_gpc; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_Key -{ -public: - virtual const DL_GroupParameters & GetAbstractGroupParameters() const =0; - virtual DL_GroupParameters & AccessAbstractGroupParameters() =0; -}; - -//! interface for DL public keys -template -class CRYPTOPP_NO_VTABLE DL_PublicKey : public DL_Key -{ - typedef DL_PublicKey ThisClass; - -public: - typedef T Element; - - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const - { - return GetValueHelper(this, name, valueType, pValue, &this->GetAbstractGroupParameters()) - CRYPTOPP_GET_FUNCTION_ENTRY(PublicElement); - } - - void AssignFrom(const NameValuePairs &source); - - // non-inherited - virtual const Element & GetPublicElement() const {return GetPublicPrecomputation().GetBase(this->GetAbstractGroupParameters().GetGroupPrecomputation());} - virtual void SetPublicElement(const Element &y) {AccessPublicPrecomputation().SetBase(this->GetAbstractGroupParameters().GetGroupPrecomputation(), y);} - virtual Element ExponentiatePublicElement(const Integer &exponent) const - { - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - return GetPublicPrecomputation().Exponentiate(params.GetGroupPrecomputation(), exponent); - } - virtual Element CascadeExponentiateBaseAndPublicElement(const Integer &baseExp, const Integer &publicExp) const - { - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - return params.GetBasePrecomputation().CascadeExponentiate(params.GetGroupPrecomputation(), baseExp, GetPublicPrecomputation(), publicExp); - } - - virtual const DL_FixedBasePrecomputation & GetPublicPrecomputation() const =0; - virtual DL_FixedBasePrecomputation & AccessPublicPrecomputation() =0; -}; - -//! interface for DL private keys -template -class CRYPTOPP_NO_VTABLE DL_PrivateKey : public DL_Key -{ - typedef DL_PrivateKey ThisClass; - -public: - typedef T Element; - - void MakePublicKey(DL_PublicKey &pub) const - { - pub.AccessAbstractGroupParameters().AssignFrom(this->GetAbstractGroupParameters()); - pub.SetPublicElement(this->GetAbstractGroupParameters().ExponentiateBase(GetPrivateExponent())); - } - - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const - { - return GetValueHelper(this, name, valueType, pValue, &this->GetAbstractGroupParameters()) - CRYPTOPP_GET_FUNCTION_ENTRY(PrivateExponent); - } - - void AssignFrom(const NameValuePairs &source) - { - this->AccessAbstractGroupParameters().AssignFrom(source); - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(PrivateExponent); - } - - virtual const Integer & GetPrivateExponent() const =0; - virtual void SetPrivateExponent(const Integer &x) =0; -}; - -template -void DL_PublicKey::AssignFrom(const NameValuePairs &source) -{ - DL_PrivateKey *pPrivateKey = NULL; - if (source.GetThisPointer(pPrivateKey)) - pPrivateKey->MakePublicKey(*this); - else - { - this->AccessAbstractGroupParameters().AssignFrom(source); - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(PublicElement); - } -} - -class OID; - -//! _ -template -class DL_KeyImpl : public PK -{ -public: - typedef GP GroupParameters; - - O GetAlgorithmID() const {return GetGroupParameters().GetAlgorithmID();} -// void BERDecode(BufferedTransformation &bt) -// {PK::BERDecode(bt);} -// void DEREncode(BufferedTransformation &bt) const -// {PK::DEREncode(bt);} - bool BERDecodeAlgorithmParameters(BufferedTransformation &bt) - {AccessGroupParameters().BERDecode(bt); return true;} - bool DEREncodeAlgorithmParameters(BufferedTransformation &bt) const - {GetGroupParameters().DEREncode(bt); return true;} - - const GP & GetGroupParameters() const {return m_groupParameters;} - GP & AccessGroupParameters() {return m_groupParameters;} - -private: - GP m_groupParameters; -}; - -class X509PublicKey; -class PKCS8PrivateKey; - -//! _ -template -class DL_PrivateKeyImpl : public DL_PrivateKey, public DL_KeyImpl -{ -public: - typedef typename GP::Element Element; - - // GeneratableCryptoMaterial - bool Validate(RandomNumberGenerator &rng, unsigned int level) const - { - bool pass = GetAbstractGroupParameters().Validate(rng, level); - - const Integer &q = GetAbstractGroupParameters().GetSubgroupOrder(); - const Integer &x = GetPrivateExponent(); - - pass = pass && x.IsPositive() && x < q; - if (level >= 1) - pass = pass && Integer::Gcd(x, q) == Integer::One(); - return pass; - } - - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const - { - return GetValueHelper >(this, name, valueType, pValue).Assignable(); - } - - void AssignFrom(const NameValuePairs &source) - { - AssignFromHelper >(this, source); - } - - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶ms) - { - if (!params.GetThisObject(this->AccessGroupParameters())) - this->AccessGroupParameters().GenerateRandom(rng, params); -// std::pair seed; - Integer x(rng, Integer::One(), GetAbstractGroupParameters().GetMaxExponent()); -// Integer::ANY, Integer::Zero(), Integer::One(), -// params.GetValue("DeterministicKeyGenerationSeed", seed) ? &seed : NULL); - SetPrivateExponent(x); - } - - bool SupportsPrecomputation() const {return true;} - - void Precompute(unsigned int precomputationStorage=16) - {AccessAbstractGroupParameters().Precompute(precomputationStorage);} - - void LoadPrecomputation(BufferedTransformation &storedPrecomputation) - {AccessAbstractGroupParameters().LoadPrecomputation(storedPrecomputation);} - - void SavePrecomputation(BufferedTransformation &storedPrecomputation) const - {GetAbstractGroupParameters().SavePrecomputation(storedPrecomputation);} - - // DL_Key - const DL_GroupParameters & GetAbstractGroupParameters() const {return this->GetGroupParameters();} - DL_GroupParameters & AccessAbstractGroupParameters() {return this->AccessGroupParameters();} - - // DL_PrivateKey - const Integer & GetPrivateExponent() const {return m_x;} - void SetPrivateExponent(const Integer &x) {m_x = x;} - - // PKCS8PrivateKey - void BERDecodePrivateKey(BufferedTransformation &bt, bool, size_t) - {m_x.BERDecode(bt);} - void DEREncodePrivateKey(BufferedTransformation &bt) const - {m_x.DEREncode(bt);} - -private: - Integer m_x; -}; - -//! _ -template -class DL_PrivateKey_WithSignaturePairwiseConsistencyTest : public BASE -{ -public: - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶ms) - { - BASE::GenerateRandom(rng, params); - - if (FIPS_140_2_ComplianceEnabled()) - { - typename SIGNATURE_SCHEME::Signer signer(*this); - typename SIGNATURE_SCHEME::Verifier verifier(signer); - SignaturePairwiseConsistencyTest_FIPS_140_Only(signer, verifier); - } - } -}; - -//! _ -template -class DL_PublicKeyImpl : public DL_PublicKey, public DL_KeyImpl -{ -public: - typedef typename GP::Element Element; - - // CryptoMaterial - bool Validate(RandomNumberGenerator &rng, unsigned int level) const - { - bool pass = GetAbstractGroupParameters().Validate(rng, level); - pass = pass && GetAbstractGroupParameters().ValidateElement(level, this->GetPublicElement(), &GetPublicPrecomputation()); - return pass; - } - - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const - { - return GetValueHelper >(this, name, valueType, pValue).Assignable(); - } - - void AssignFrom(const NameValuePairs &source) - { - AssignFromHelper >(this, source); - } - - bool SupportsPrecomputation() const {return true;} - - void Precompute(unsigned int precomputationStorage=16) - { - AccessAbstractGroupParameters().Precompute(precomputationStorage); - AccessPublicPrecomputation().Precompute(GetAbstractGroupParameters().GetGroupPrecomputation(), GetAbstractGroupParameters().GetSubgroupOrder().BitCount(), precomputationStorage); - } - - void LoadPrecomputation(BufferedTransformation &storedPrecomputation) - { - AccessAbstractGroupParameters().LoadPrecomputation(storedPrecomputation); - AccessPublicPrecomputation().Load(GetAbstractGroupParameters().GetGroupPrecomputation(), storedPrecomputation); - } - - void SavePrecomputation(BufferedTransformation &storedPrecomputation) const - { - GetAbstractGroupParameters().SavePrecomputation(storedPrecomputation); - GetPublicPrecomputation().Save(GetAbstractGroupParameters().GetGroupPrecomputation(), storedPrecomputation); - } - - // DL_Key - const DL_GroupParameters & GetAbstractGroupParameters() const {return this->GetGroupParameters();} - DL_GroupParameters & AccessAbstractGroupParameters() {return this->AccessGroupParameters();} - - // DL_PublicKey - const DL_FixedBasePrecomputation & GetPublicPrecomputation() const {return m_ypc;} - DL_FixedBasePrecomputation & AccessPublicPrecomputation() {return m_ypc;} - - // non-inherited - bool operator==(const DL_PublicKeyImpl &rhs) const - {return this->GetGroupParameters() == rhs.GetGroupParameters() && this->GetPublicElement() == rhs.GetPublicElement();} - -private: - typename GP::BasePrecomputation m_ypc; -}; - -//! interface for Elgamal-like signature algorithms -template -class CRYPTOPP_NO_VTABLE DL_ElgamalLikeSignatureAlgorithm -{ -public: - virtual void Sign(const DL_GroupParameters ¶ms, const Integer &privateKey, const Integer &k, const Integer &e, Integer &r, Integer &s) const =0; - virtual bool Verify(const DL_GroupParameters ¶ms, const DL_PublicKey &publicKey, const Integer &e, const Integer &r, const Integer &s) const =0; - virtual Integer RecoverPresignature(const DL_GroupParameters ¶ms, const DL_PublicKey &publicKey, const Integer &r, const Integer &s) const - {throw NotImplemented("DL_ElgamalLikeSignatureAlgorithm: this signature scheme does not support message recovery");} - virtual size_t RLen(const DL_GroupParameters ¶ms) const - {return params.GetSubgroupOrder().ByteCount();} - virtual size_t SLen(const DL_GroupParameters ¶ms) const - {return params.GetSubgroupOrder().ByteCount();} -}; - -//! interface for DL key agreement algorithms -template -class CRYPTOPP_NO_VTABLE DL_KeyAgreementAlgorithm -{ -public: - typedef T Element; - - virtual Element AgreeWithEphemeralPrivateKey(const DL_GroupParameters ¶ms, const DL_FixedBasePrecomputation &publicPrecomputation, const Integer &privateExponent) const =0; - virtual Element AgreeWithStaticPrivateKey(const DL_GroupParameters ¶ms, const Element &publicElement, bool validateOtherPublicKey, const Integer &privateExponent) const =0; -}; - -//! interface for key derivation algorithms used in DL cryptosystems -template -class CRYPTOPP_NO_VTABLE DL_KeyDerivationAlgorithm -{ -public: - virtual bool ParameterSupported(const char *name) const {return false;} - virtual void Derive(const DL_GroupParameters &groupParams, byte *derivedKey, size_t derivedLength, const T &agreedElement, const T &ephemeralPublicKey, const NameValuePairs &derivationParams) const =0; -}; - -//! interface for symmetric encryption algorithms used in DL cryptosystems -class CRYPTOPP_NO_VTABLE DL_SymmetricEncryptionAlgorithm -{ -public: - virtual bool ParameterSupported(const char *name) const {return false;} - virtual size_t GetSymmetricKeyLength(size_t plaintextLength) const =0; - virtual size_t GetSymmetricCiphertextLength(size_t plaintextLength) const =0; - virtual size_t GetMaxSymmetricPlaintextLength(size_t ciphertextLength) const =0; - virtual void SymmetricEncrypt(RandomNumberGenerator &rng, const byte *key, const byte *plaintext, size_t plaintextLength, byte *ciphertext, const NameValuePairs ¶meters) const =0; - virtual DecodingResult SymmetricDecrypt(const byte *key, const byte *ciphertext, size_t ciphertextLength, byte *plaintext, const NameValuePairs ¶meters) const =0; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_Base -{ -protected: - typedef KI KeyInterface; - typedef typename KI::Element Element; - - const DL_GroupParameters & GetAbstractGroupParameters() const {return GetKeyInterface().GetAbstractGroupParameters();} - DL_GroupParameters & AccessAbstractGroupParameters() {return AccessKeyInterface().AccessAbstractGroupParameters();} - - virtual KeyInterface & AccessKeyInterface() =0; - virtual const KeyInterface & GetKeyInterface() const =0; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_SignatureSchemeBase : public INTERFACE, public DL_Base -{ -public: - size_t SignatureLength() const - { - return GetSignatureAlgorithm().RLen(this->GetAbstractGroupParameters()) - + GetSignatureAlgorithm().SLen(this->GetAbstractGroupParameters()); - } - size_t MaxRecoverableLength() const - {return GetMessageEncodingInterface().MaxRecoverableLength(0, GetHashIdentifier().second, GetDigestSize());} - size_t MaxRecoverableLengthFromSignatureLength(size_t signatureLength) const - {assert(false); return 0;} // TODO - - bool IsProbabilistic() const - {return true;} - bool AllowNonrecoverablePart() const - {return GetMessageEncodingInterface().AllowNonrecoverablePart();} - bool RecoverablePartFirst() const - {return GetMessageEncodingInterface().RecoverablePartFirst();} - -protected: - size_t MessageRepresentativeLength() const {return BitsToBytes(MessageRepresentativeBitLength());} - size_t MessageRepresentativeBitLength() const {return this->GetAbstractGroupParameters().GetSubgroupOrder().BitCount();} - - virtual const DL_ElgamalLikeSignatureAlgorithm & GetSignatureAlgorithm() const =0; - virtual const PK_SignatureMessageEncodingMethod & GetMessageEncodingInterface() const =0; - virtual HashIdentifier GetHashIdentifier() const =0; - virtual size_t GetDigestSize() const =0; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_SignerBase : public DL_SignatureSchemeBase > -{ -public: - // for validation testing - void RawSign(const Integer &k, const Integer &e, Integer &r, Integer &s) const - { - const DL_ElgamalLikeSignatureAlgorithm &alg = this->GetSignatureAlgorithm(); - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - const DL_PrivateKey &key = this->GetKeyInterface(); - - r = params.ConvertElementToInteger(params.ExponentiateBase(k)); - alg.Sign(params, key.GetPrivateExponent(), k, e, r, s); - } - - void InputRecoverableMessage(PK_MessageAccumulator &messageAccumulator, const byte *recoverableMessage, size_t recoverableMessageLength) const - { - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - ma.m_recoverableMessage.Assign(recoverableMessage, recoverableMessageLength); - this->GetMessageEncodingInterface().ProcessRecoverableMessage(ma.AccessHash(), - recoverableMessage, recoverableMessageLength, - ma.m_presignature, ma.m_presignature.size(), - ma.m_semisignature); - } - - size_t SignAndRestart(RandomNumberGenerator &rng, PK_MessageAccumulator &messageAccumulator, byte *signature, bool restart) const - { - this->GetMaterial().DoQuickSanityCheck(); - - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - const DL_ElgamalLikeSignatureAlgorithm &alg = this->GetSignatureAlgorithm(); - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - const DL_PrivateKey &key = this->GetKeyInterface(); - - SecByteBlock representative(this->MessageRepresentativeLength()); - this->GetMessageEncodingInterface().ComputeMessageRepresentative( - rng, - ma.m_recoverableMessage, ma.m_recoverableMessage.size(), - ma.AccessHash(), this->GetHashIdentifier(), ma.m_empty, - representative, this->MessageRepresentativeBitLength()); - ma.m_empty = true; - Integer e(representative, representative.size()); - - // hash message digest into random number k to prevent reusing the same k on a different messages - // after virtual machine rollback - if (rng.CanIncorporateEntropy()) - rng.IncorporateEntropy(representative, representative.size()); - Integer k(rng, 1, params.GetSubgroupOrder()-1); - Integer r, s; - r = params.ConvertElementToInteger(params.ExponentiateBase(k)); - alg.Sign(params, key.GetPrivateExponent(), k, e, r, s); - - /* - Integer r, s; - if (this->MaxRecoverableLength() > 0) - r.Decode(ma.m_semisignature, ma.m_semisignature.size()); - else - r.Decode(ma.m_presignature, ma.m_presignature.size()); - alg.Sign(params, key.GetPrivateExponent(), ma.m_k, e, r, s); - */ - - size_t rLen = alg.RLen(params); - r.Encode(signature, rLen); - s.Encode(signature+rLen, alg.SLen(params)); - - if (restart) - RestartMessageAccumulator(rng, ma); - - return this->SignatureLength(); - } - -protected: - void RestartMessageAccumulator(RandomNumberGenerator &rng, PK_MessageAccumulatorBase &ma) const - { - // k needs to be generated before hashing for signature schemes with recovery - // but to defend against VM rollbacks we need to generate k after hashing. - // so this code is commented out, since no DL-based signature scheme with recovery - // has been implemented in Crypto++ anyway - /* - const DL_ElgamalLikeSignatureAlgorithm &alg = this->GetSignatureAlgorithm(); - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - ma.m_k.Randomize(rng, 1, params.GetSubgroupOrder()-1); - ma.m_presignature.New(params.GetEncodedElementSize(false)); - params.ConvertElementToInteger(params.ExponentiateBase(ma.m_k)).Encode(ma.m_presignature, ma.m_presignature.size()); - */ - } -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_VerifierBase : public DL_SignatureSchemeBase > -{ -public: - void InputSignature(PK_MessageAccumulator &messageAccumulator, const byte *signature, size_t signatureLength) const - { - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - const DL_ElgamalLikeSignatureAlgorithm &alg = this->GetSignatureAlgorithm(); - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - - size_t rLen = alg.RLen(params); - ma.m_semisignature.Assign(signature, rLen); - ma.m_s.Decode(signature+rLen, alg.SLen(params)); - - this->GetMessageEncodingInterface().ProcessSemisignature(ma.AccessHash(), ma.m_semisignature, ma.m_semisignature.size()); - } - - bool VerifyAndRestart(PK_MessageAccumulator &messageAccumulator) const - { - this->GetMaterial().DoQuickSanityCheck(); - - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - const DL_ElgamalLikeSignatureAlgorithm &alg = this->GetSignatureAlgorithm(); - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - const DL_PublicKey &key = this->GetKeyInterface(); - - SecByteBlock representative(this->MessageRepresentativeLength()); - this->GetMessageEncodingInterface().ComputeMessageRepresentative(NullRNG(), ma.m_recoverableMessage, ma.m_recoverableMessage.size(), - ma.AccessHash(), this->GetHashIdentifier(), ma.m_empty, - representative, this->MessageRepresentativeBitLength()); - ma.m_empty = true; - Integer e(representative, representative.size()); - - Integer r(ma.m_semisignature, ma.m_semisignature.size()); - return alg.Verify(params, key, e, r, ma.m_s); - } - - DecodingResult RecoverAndRestart(byte *recoveredMessage, PK_MessageAccumulator &messageAccumulator) const - { - this->GetMaterial().DoQuickSanityCheck(); - - PK_MessageAccumulatorBase &ma = static_cast(messageAccumulator); - const DL_ElgamalLikeSignatureAlgorithm &alg = this->GetSignatureAlgorithm(); - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - const DL_PublicKey &key = this->GetKeyInterface(); - - SecByteBlock representative(this->MessageRepresentativeLength()); - this->GetMessageEncodingInterface().ComputeMessageRepresentative( - NullRNG(), - ma.m_recoverableMessage, ma.m_recoverableMessage.size(), - ma.AccessHash(), this->GetHashIdentifier(), ma.m_empty, - representative, this->MessageRepresentativeBitLength()); - ma.m_empty = true; - Integer e(representative, representative.size()); - - ma.m_presignature.New(params.GetEncodedElementSize(false)); - Integer r(ma.m_semisignature, ma.m_semisignature.size()); - alg.RecoverPresignature(params, key, r, ma.m_s).Encode(ma.m_presignature, ma.m_presignature.size()); - - return this->GetMessageEncodingInterface().RecoverMessageFromSemisignature( - ma.AccessHash(), this->GetHashIdentifier(), - ma.m_presignature, ma.m_presignature.size(), - ma.m_semisignature, ma.m_semisignature.size(), - recoveredMessage); - } -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_CryptoSystemBase : public PK, public DL_Base -{ -public: - typedef typename DL_Base::Element Element; - - size_t MaxPlaintextLength(size_t ciphertextLength) const - { - unsigned int minLen = this->GetAbstractGroupParameters().GetEncodedElementSize(true); - return ciphertextLength < minLen ? 0 : GetSymmetricEncryptionAlgorithm().GetMaxSymmetricPlaintextLength(ciphertextLength - minLen); - } - - size_t CiphertextLength(size_t plaintextLength) const - { - size_t len = GetSymmetricEncryptionAlgorithm().GetSymmetricCiphertextLength(plaintextLength); - return len == 0 ? 0 : this->GetAbstractGroupParameters().GetEncodedElementSize(true) + len; - } - - bool ParameterSupported(const char *name) const - {return GetKeyDerivationAlgorithm().ParameterSupported(name) || GetSymmetricEncryptionAlgorithm().ParameterSupported(name);} - -protected: - virtual const DL_KeyAgreementAlgorithm & GetKeyAgreementAlgorithm() const =0; - virtual const DL_KeyDerivationAlgorithm & GetKeyDerivationAlgorithm() const =0; - virtual const DL_SymmetricEncryptionAlgorithm & GetSymmetricEncryptionAlgorithm() const =0; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_DecryptorBase : public DL_CryptoSystemBase > -{ -public: - typedef T Element; - - DecodingResult Decrypt(RandomNumberGenerator &rng, const byte *ciphertext, size_t ciphertextLength, byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const - { - try - { - const DL_KeyAgreementAlgorithm &agreeAlg = this->GetKeyAgreementAlgorithm(); - const DL_KeyDerivationAlgorithm &derivAlg = this->GetKeyDerivationAlgorithm(); - const DL_SymmetricEncryptionAlgorithm &encAlg = this->GetSymmetricEncryptionAlgorithm(); - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - const DL_PrivateKey &key = this->GetKeyInterface(); - - Element q = params.DecodeElement(ciphertext, true); - size_t elementSize = params.GetEncodedElementSize(true); - ciphertext += elementSize; - ciphertextLength -= elementSize; - - Element z = agreeAlg.AgreeWithStaticPrivateKey(params, q, true, key.GetPrivateExponent()); - - SecByteBlock derivedKey(encAlg.GetSymmetricKeyLength(encAlg.GetMaxSymmetricPlaintextLength(ciphertextLength))); - derivAlg.Derive(params, derivedKey, derivedKey.size(), z, q, parameters); - - return encAlg.SymmetricDecrypt(derivedKey, ciphertext, ciphertextLength, plaintext, parameters); - } - catch (DL_BadElement &) - { - return DecodingResult(); - } - } -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_EncryptorBase : public DL_CryptoSystemBase > -{ -public: - typedef T Element; - - void Encrypt(RandomNumberGenerator &rng, const byte *plaintext, size_t plaintextLength, byte *ciphertext, const NameValuePairs ¶meters = g_nullNameValuePairs) const - { - const DL_KeyAgreementAlgorithm &agreeAlg = this->GetKeyAgreementAlgorithm(); - const DL_KeyDerivationAlgorithm &derivAlg = this->GetKeyDerivationAlgorithm(); - const DL_SymmetricEncryptionAlgorithm &encAlg = this->GetSymmetricEncryptionAlgorithm(); - const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); - const DL_PublicKey &key = this->GetKeyInterface(); - - Integer x(rng, Integer::One(), params.GetMaxExponent()); - Element q = params.ExponentiateBase(x); - params.EncodeElement(true, q, ciphertext); - unsigned int elementSize = params.GetEncodedElementSize(true); - ciphertext += elementSize; - - Element z = agreeAlg.AgreeWithEphemeralPrivateKey(params, key.GetPublicPrecomputation(), x); - - SecByteBlock derivedKey(encAlg.GetSymmetricKeyLength(plaintextLength)); - derivAlg.Derive(params, derivedKey, derivedKey.size(), z, q, parameters); - - encAlg.SymmetricEncrypt(rng, derivedKey, plaintext, plaintextLength, ciphertext, parameters); - } -}; - -//! _ -template -struct DL_SchemeOptionsBase -{ - typedef T1 AlgorithmInfo; - typedef T2 GroupParameters; - typedef typename GroupParameters::Element Element; -}; - -//! _ -template -struct DL_KeyedSchemeOptions : public DL_SchemeOptionsBase -{ - typedef T2 Keys; - typedef typename Keys::PrivateKey PrivateKey; - typedef typename Keys::PublicKey PublicKey; -}; - -//! _ -template -struct DL_SignatureSchemeOptions : public DL_KeyedSchemeOptions -{ - typedef T3 SignatureAlgorithm; - typedef T4 MessageEncodingMethod; - typedef T5 HashFunction; -}; - -//! _ -template -struct DL_CryptoSchemeOptions : public DL_KeyedSchemeOptions -{ - typedef T3 KeyAgreementAlgorithm; - typedef T4 KeyDerivationAlgorithm; - typedef T5 SymmetricEncryptionAlgorithm; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_ObjectImplBase : public AlgorithmImpl -{ -public: - typedef SCHEME_OPTIONS SchemeOptions; - typedef typename KEY::Element Element; - - PrivateKey & AccessPrivateKey() {return m_key;} - PublicKey & AccessPublicKey() {return m_key;} - - // KeyAccessor - const KEY & GetKey() const {return m_key;} - KEY & AccessKey() {return m_key;} - -protected: - typename BASE::KeyInterface & AccessKeyInterface() {return m_key;} - const typename BASE::KeyInterface & GetKeyInterface() const {return m_key;} - - // for signature scheme - HashIdentifier GetHashIdentifier() const - { - typedef typename SchemeOptions::MessageEncodingMethod::HashIdentifierLookup HashLookup; - return HashLookup::template HashIdentifierLookup2::Lookup(); - } - size_t GetDigestSize() const - { - typedef CPP_TYPENAME SchemeOptions::HashFunction H; - return H::DIGESTSIZE; - } - -private: - KEY m_key; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_ObjectImpl : public DL_ObjectImplBase -{ -public: - typedef typename KEY::Element Element; - -protected: - const DL_ElgamalLikeSignatureAlgorithm & GetSignatureAlgorithm() const - {return Singleton().Ref();} - const DL_KeyAgreementAlgorithm & GetKeyAgreementAlgorithm() const - {return Singleton().Ref();} - const DL_KeyDerivationAlgorithm & GetKeyDerivationAlgorithm() const - {return Singleton().Ref();} - const DL_SymmetricEncryptionAlgorithm & GetSymmetricEncryptionAlgorithm() const - {return Singleton().Ref();} - HashIdentifier GetHashIdentifier() const - {return HashIdentifier();} - const PK_SignatureMessageEncodingMethod & GetMessageEncodingInterface() const - {return Singleton().Ref();} -}; - -//! _ -template -class DL_SignerImpl : public DL_ObjectImpl, SCHEME_OPTIONS, typename SCHEME_OPTIONS::PrivateKey> -{ -public: - PK_MessageAccumulator * NewSignatureAccumulator(RandomNumberGenerator &rng) const - { - std::auto_ptr p(new PK_MessageAccumulatorImpl); - this->RestartMessageAccumulator(rng, *p); - return p.release(); - } -}; - -//! _ -template -class DL_VerifierImpl : public DL_ObjectImpl, SCHEME_OPTIONS, typename SCHEME_OPTIONS::PublicKey> -{ -public: - PK_MessageAccumulator * NewVerificationAccumulator() const - { - return new PK_MessageAccumulatorImpl; - } -}; - -//! _ -template -class DL_EncryptorImpl : public DL_ObjectImpl, SCHEME_OPTIONS, typename SCHEME_OPTIONS::PublicKey> -{ -}; - -//! _ -template -class DL_DecryptorImpl : public DL_ObjectImpl, SCHEME_OPTIONS, typename SCHEME_OPTIONS::PrivateKey> -{ -}; - -// ******************************************************** - -//! _ -template -class CRYPTOPP_NO_VTABLE DL_SimpleKeyAgreementDomainBase : public SimpleKeyAgreementDomain -{ -public: - typedef T Element; - - CryptoParameters & AccessCryptoParameters() {return AccessAbstractGroupParameters();} - unsigned int AgreedValueLength() const {return GetAbstractGroupParameters().GetEncodedElementSize(false);} - unsigned int PrivateKeyLength() const {return GetAbstractGroupParameters().GetSubgroupOrder().ByteCount();} - unsigned int PublicKeyLength() const {return GetAbstractGroupParameters().GetEncodedElementSize(true);} - - void GeneratePrivateKey(RandomNumberGenerator &rng, byte *privateKey) const - { - Integer x(rng, Integer::One(), GetAbstractGroupParameters().GetMaxExponent()); - x.Encode(privateKey, PrivateKeyLength()); - } - - void GeneratePublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const - { - const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); - Integer x(privateKey, PrivateKeyLength()); - Element y = params.ExponentiateBase(x); - params.EncodeElement(true, y, publicKey); - } - - bool Agree(byte *agreedValue, const byte *privateKey, const byte *otherPublicKey, bool validateOtherPublicKey=true) const - { - try - { - const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); - Integer x(privateKey, PrivateKeyLength()); - Element w = params.DecodeElement(otherPublicKey, validateOtherPublicKey); - - Element z = GetKeyAgreementAlgorithm().AgreeWithStaticPrivateKey( - GetAbstractGroupParameters(), w, validateOtherPublicKey, x); - params.EncodeElement(false, z, agreedValue); - } - catch (DL_BadElement &) - { - return false; - } - return true; - } - - const Element &GetGenerator() const {return GetAbstractGroupParameters().GetSubgroupGenerator();} - -protected: - virtual const DL_KeyAgreementAlgorithm & GetKeyAgreementAlgorithm() const =0; - virtual DL_GroupParameters & AccessAbstractGroupParameters() =0; - const DL_GroupParameters & GetAbstractGroupParameters() const {return const_cast *>(this)->AccessAbstractGroupParameters();} -}; - -enum CofactorMultiplicationOption {NO_COFACTOR_MULTIPLICTION, COMPATIBLE_COFACTOR_MULTIPLICTION, INCOMPATIBLE_COFACTOR_MULTIPLICTION}; -typedef EnumToType NoCofactorMultiplication; -typedef EnumToType CompatibleCofactorMultiplication; -typedef EnumToType IncompatibleCofactorMultiplication; - -//! DH key agreement algorithm -template -class DL_KeyAgreementAlgorithm_DH : public DL_KeyAgreementAlgorithm -{ -public: - typedef ELEMENT Element; - - static const char * CRYPTOPP_API StaticAlgorithmName() - {return COFACTOR_OPTION::ToEnum() == INCOMPATIBLE_COFACTOR_MULTIPLICTION ? "DHC" : "DH";} - - Element AgreeWithEphemeralPrivateKey(const DL_GroupParameters ¶ms, const DL_FixedBasePrecomputation &publicPrecomputation, const Integer &privateExponent) const - { - return publicPrecomputation.Exponentiate(params.GetGroupPrecomputation(), - COFACTOR_OPTION::ToEnum() == INCOMPATIBLE_COFACTOR_MULTIPLICTION ? privateExponent*params.GetCofactor() : privateExponent); - } - - Element AgreeWithStaticPrivateKey(const DL_GroupParameters ¶ms, const Element &publicElement, bool validateOtherPublicKey, const Integer &privateExponent) const - { - if (COFACTOR_OPTION::ToEnum() == COMPATIBLE_COFACTOR_MULTIPLICTION) - { - const Integer &k = params.GetCofactor(); - return params.ExponentiateElement(publicElement, - ModularArithmetic(params.GetSubgroupOrder()).Divide(privateExponent, k)*k); - } - else if (COFACTOR_OPTION::ToEnum() == INCOMPATIBLE_COFACTOR_MULTIPLICTION) - return params.ExponentiateElement(publicElement, privateExponent*params.GetCofactor()); - else - { - assert(COFACTOR_OPTION::ToEnum() == NO_COFACTOR_MULTIPLICTION); - - if (!validateOtherPublicKey) - return params.ExponentiateElement(publicElement, privateExponent); - - if (params.FastSubgroupCheckAvailable()) - { - if (!params.ValidateElement(2, publicElement, NULL)) - throw DL_BadElement(); - return params.ExponentiateElement(publicElement, privateExponent); - } - else - { - const Integer e[2] = {params.GetSubgroupOrder(), privateExponent}; - Element r[2]; - params.SimultaneousExponentiate(r, publicElement, e, 2); - if (!params.IsIdentity(r[0])) - throw DL_BadElement(); - return r[1]; - } - } - } -}; - -// ******************************************************** - -//! A template implementing constructors for public key algorithm classes -template -class CRYPTOPP_NO_VTABLE PK_FinalTemplate : public BASE -{ -public: - PK_FinalTemplate() {} - - PK_FinalTemplate(const CryptoMaterial &key) - {this->AccessKey().AssignFrom(key);} - - PK_FinalTemplate(BufferedTransformation &bt) - {this->AccessKey().BERDecode(bt);} - - PK_FinalTemplate(const AsymmetricAlgorithm &algorithm) - {this->AccessKey().AssignFrom(algorithm.GetMaterial());} - - PK_FinalTemplate(const Integer &v1) - {this->AccessKey().Initialize(v1);} - -#if (defined(_MSC_VER) && _MSC_VER < 1300) - - template - PK_FinalTemplate(T1 &v1, T2 &v2) - {this->AccessKey().Initialize(v1, v2);} - - template - PK_FinalTemplate(T1 &v1, T2 &v2, T3 &v3) - {this->AccessKey().Initialize(v1, v2, v3);} - - template - PK_FinalTemplate(T1 &v1, T2 &v2, T3 &v3, T4 &v4) - {this->AccessKey().Initialize(v1, v2, v3, v4);} - - template - PK_FinalTemplate(T1 &v1, T2 &v2, T3 &v3, T4 &v4, T5 &v5) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5);} - - template - PK_FinalTemplate(T1 &v1, T2 &v2, T3 &v3, T4 &v4, T5 &v5, T6 &v6) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5, v6);} - - template - PK_FinalTemplate(T1 &v1, T2 &v2, T3 &v3, T4 &v4, T5 &v5, T6 &v6, T7 &v7) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5, v6, v7);} - - template - PK_FinalTemplate(T1 &v1, T2 &v2, T3 &v3, T4 &v4, T5 &v5, T6 &v6, T7 &v7, T8 &v8) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5, v6, v7, v8);} - -#else - - template - PK_FinalTemplate(const T1 &v1, const T2 &v2) - {this->AccessKey().Initialize(v1, v2);} - - template - PK_FinalTemplate(const T1 &v1, const T2 &v2, const T3 &v3) - {this->AccessKey().Initialize(v1, v2, v3);} - - template - PK_FinalTemplate(const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4) - {this->AccessKey().Initialize(v1, v2, v3, v4);} - - template - PK_FinalTemplate(const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5);} - - template - PK_FinalTemplate(const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, const T6 &v6) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5, v6);} - - template - PK_FinalTemplate(const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, const T6 &v6, const T7 &v7) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5, v6, v7);} - - template - PK_FinalTemplate(const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, const T6 &v6, const T7 &v7, const T8 &v8) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5, v6, v7, v8);} - - template - PK_FinalTemplate(T1 &v1, const T2 &v2) - {this->AccessKey().Initialize(v1, v2);} - - template - PK_FinalTemplate(T1 &v1, const T2 &v2, const T3 &v3) - {this->AccessKey().Initialize(v1, v2, v3);} - - template - PK_FinalTemplate(T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4) - {this->AccessKey().Initialize(v1, v2, v3, v4);} - - template - PK_FinalTemplate(T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5);} - - template - PK_FinalTemplate(T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, const T6 &v6) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5, v6);} - - template - PK_FinalTemplate(T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, const T6 &v6, const T7 &v7) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5, v6, v7);} - - template - PK_FinalTemplate(T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, const T6 &v6, const T7 &v7, const T8 &v8) - {this->AccessKey().Initialize(v1, v2, v3, v4, v5, v6, v7, v8);} - -#endif -}; - -//! Base class for public key encryption standard classes. These classes are used to select from variants of algorithms. Note that not all standards apply to all algorithms. -struct EncryptionStandard {}; - -//! Base class for public key signature standard classes. These classes are used to select from variants of algorithms. Note that not all standards apply to all algorithms. -struct SignatureStandard {}; - -template -class TF_ES; - -//! Trapdoor Function Based Encryption Scheme -template > -class TF_ES : public KEYS -{ - typedef typename STANDARD::EncryptionMessageEncodingMethod MessageEncodingMethod; - -public: - //! see EncryptionStandard for a list of standards - typedef STANDARD Standard; - typedef TF_CryptoSchemeOptions SchemeOptions; - - static std::string CRYPTOPP_API StaticAlgorithmName() {return std::string(KEYS::StaticAlgorithmName()) + "/" + MessageEncodingMethod::StaticAlgorithmName();} - - //! implements PK_Decryptor interface - typedef PK_FinalTemplate > Decryptor; - //! implements PK_Encryptor interface - typedef PK_FinalTemplate > Encryptor; -}; - -template // VC60 workaround: doesn't work if KEYS is first parameter -class TF_SS; - -//! Trapdoor Function Based Signature Scheme -template > // VC60 workaround: doesn't work if KEYS is first parameter -class TF_SS : public KEYS -{ -public: - //! see SignatureStandard for a list of standards - typedef STANDARD Standard; - typedef typename Standard::SignatureMessageEncodingMethod MessageEncodingMethod; - typedef TF_SignatureSchemeOptions SchemeOptions; - - static std::string CRYPTOPP_API StaticAlgorithmName() {return std::string(KEYS::StaticAlgorithmName()) + "/" + MessageEncodingMethod::StaticAlgorithmName() + "(" + H::StaticAlgorithmName() + ")";} - - //! implements PK_Signer interface - typedef PK_FinalTemplate > Signer; - //! implements PK_Verifier interface - typedef PK_FinalTemplate > Verifier; -}; - -template -class DL_SS; - -//! Discrete Log Based Signature Scheme -template > -class DL_SS : public KEYS -{ - typedef DL_SignatureSchemeOptions SchemeOptions; - -public: - static std::string StaticAlgorithmName() {return SA::StaticAlgorithmName() + std::string("/EMSA1(") + H::StaticAlgorithmName() + ")";} - - //! implements PK_Signer interface - typedef PK_FinalTemplate > Signer; - //! implements PK_Verifier interface - typedef PK_FinalTemplate > Verifier; -}; - -//! Discrete Log Based Encryption Scheme -template -class DL_ES : public KEYS -{ - typedef DL_CryptoSchemeOptions SchemeOptions; - -public: - //! implements PK_Decryptor interface - typedef PK_FinalTemplate > Decryptor; - //! implements PK_Encryptor interface - typedef PK_FinalTemplate > Encryptor; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/pwdbased.h b/CryptoPP/pwdbased.h deleted file mode 100644 index f755724b1..000000000 --- a/CryptoPP/pwdbased.h +++ /dev/null @@ -1,214 +0,0 @@ -// pwdbased.h - written and placed in the public domain by Wei Dai - -#ifndef CRYPTOPP_PWDBASED_H -#define CRYPTOPP_PWDBASED_H - -#include "cryptlib.h" -#include "hmac.h" -#include "hrtimer.h" -#include "integer.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! abstract base class for password based key derivation function -class PasswordBasedKeyDerivationFunction -{ -public: - virtual size_t MaxDerivedKeyLength() const =0; - virtual bool UsesPurposeByte() const =0; - //! derive key from password - /*! If timeInSeconds != 0, will iterate until time elapsed, as measured by ThreadUserTimer - Returns actual iteration count, which is equal to iterations if timeInSeconds == 0, and not less than iterations otherwise. */ - virtual unsigned int DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds=0) const =0; -}; - -//! PBKDF1 from PKCS #5, T should be a HashTransformation class -template -class PKCS5_PBKDF1 : public PasswordBasedKeyDerivationFunction -{ -public: - size_t MaxDerivedKeyLength() const {return T::DIGESTSIZE;} - bool UsesPurposeByte() const {return false;} - // PKCS #5 says PBKDF1 should only take 8-byte salts. This implementation allows salts of any length. - unsigned int DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds=0) const; -}; - -//! PBKDF2 from PKCS #5, T should be a HashTransformation class -template -class PKCS5_PBKDF2_HMAC : public PasswordBasedKeyDerivationFunction -{ -public: - size_t MaxDerivedKeyLength() const {return 0xffffffffU;} // should multiply by T::DIGESTSIZE, but gets overflow that way - bool UsesPurposeByte() const {return false;} - unsigned int DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds=0) const; -}; - -/* -class PBKDF2Params -{ -public: - SecByteBlock m_salt; - unsigned int m_interationCount; - ASNOptional > m_keyLength; -}; -*/ - -template -unsigned int PKCS5_PBKDF1::DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const -{ - assert(derivedLen <= MaxDerivedKeyLength()); - assert(iterations > 0 || timeInSeconds > 0); - - if (!iterations) - iterations = 1; - - T hash; - hash.Update(password, passwordLen); - hash.Update(salt, saltLen); - - SecByteBlock buffer(hash.DigestSize()); - hash.Final(buffer); - - unsigned int i; - ThreadUserTimer timer; - - if (timeInSeconds) - timer.StartTimer(); - - for (i=1; i -unsigned int PKCS5_PBKDF2_HMAC::DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const -{ - assert(derivedLen <= MaxDerivedKeyLength()); - assert(iterations > 0 || timeInSeconds > 0); - - if (!iterations) - iterations = 1; - - HMAC hmac(password, passwordLen); - SecByteBlock buffer(hmac.DigestSize()); - ThreadUserTimer timer; - - unsigned int i=1; - while (derivedLen > 0) - { - hmac.Update(salt, saltLen); - unsigned int j; - for (j=0; j<4; j++) - { - byte b = byte(i >> ((3-j)*8)); - hmac.Update(&b, 1); - } - hmac.Final(buffer); - - size_t segmentLen = STDMIN(derivedLen, buffer.size()); - memcpy(derived, buffer, segmentLen); - - if (timeInSeconds) - { - timeInSeconds = timeInSeconds / ((derivedLen + buffer.size() - 1) / buffer.size()); - timer.StartTimer(); - } - - for (j=1; j -class PKCS12_PBKDF : public PasswordBasedKeyDerivationFunction -{ -public: - size_t MaxDerivedKeyLength() const {return size_t(0)-1;} - bool UsesPurposeByte() const {return true;} - unsigned int DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const; -}; - -template -unsigned int PKCS12_PBKDF::DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const -{ - assert(derivedLen <= MaxDerivedKeyLength()); - assert(iterations > 0 || timeInSeconds > 0); - - if (!iterations) - iterations = 1; - - const size_t v = T::BLOCKSIZE; // v is in bytes rather than bits as in PKCS #12 - const size_t DLen = v, SLen = RoundUpToMultipleOf(saltLen, v); - const size_t PLen = RoundUpToMultipleOf(passwordLen, v), ILen = SLen + PLen; - SecByteBlock buffer(DLen + SLen + PLen); - byte *D = buffer, *S = buffer+DLen, *P = buffer+DLen+SLen, *I = S; - - memset(D, purpose, DLen); - size_t i; - for (i=0; i 0) - { - hash.CalculateDigest(Ai, buffer, buffer.size()); - - if (timeInSeconds) - { - timeInSeconds = timeInSeconds / ((derivedLen + Ai.size() - 1) / Ai.size()); - timer.StartTimer(); - } - - for (i=1; inext; current; current=current->next) - { - m_tail->next = new ByteQueueNode(*current); - m_tail = m_tail->next; - } - - m_tail->next = NULL; - - Put(copy.m_lazyString, copy.m_lazyLength); -} - -ByteQueue::~ByteQueue() -{ - Destroy(); -} - -void ByteQueue::Destroy() -{ - for (ByteQueueNode *next, *current=m_head; current; current=next) - { - next=current->next; - delete current; - } -} - -void ByteQueue::IsolatedInitialize(const NameValuePairs ¶meters) -{ - m_nodeSize = parameters.GetIntValueWithDefault("NodeSize", 256); - Clear(); -} - -lword ByteQueue::CurrentSize() const -{ - lword size=0; - - for (ByteQueueNode *current=m_head; current; current=current->next) - size += current->CurrentSize(); - - return size + m_lazyLength; -} - -bool ByteQueue::IsEmpty() const -{ - return m_head==m_tail && m_head->CurrentSize()==0 && m_lazyLength==0; -} - -void ByteQueue::Clear() -{ - for (ByteQueueNode *next, *current=m_head->next; current; current=next) - { - next=current->next; - delete current; - } - - m_tail = m_head; - m_head->Clear(); - m_head->next = NULL; - m_lazyLength = 0; -} - -size_t ByteQueue::Put2(const byte *inString, size_t length, int messageEnd, bool blocking) -{ - if (m_lazyLength > 0) - FinalizeLazyPut(); - - size_t len; - while ((len=m_tail->Put(inString, length)) < length) - { - inString += len; - length -= len; - if (m_autoNodeSize && m_nodeSize < s_maxAutoNodeSize) - do - { - m_nodeSize *= 2; - } - while (m_nodeSize < length && m_nodeSize < s_maxAutoNodeSize); - m_tail->next = new ByteQueueNode(STDMAX(m_nodeSize, length)); - m_tail = m_tail->next; - } - - return 0; -} - -void ByteQueue::CleanupUsedNodes() -{ - while (m_head != m_tail && m_head->UsedUp()) - { - ByteQueueNode *temp=m_head; - m_head=m_head->next; - delete temp; - } - - if (m_head->CurrentSize() == 0) - m_head->Clear(); -} - -void ByteQueue::LazyPut(const byte *inString, size_t size) -{ - if (m_lazyLength > 0) - FinalizeLazyPut(); - - if (inString == m_tail->buf+m_tail->m_tail) - Put(inString, size); - else - { - m_lazyString = const_cast(inString); - m_lazyLength = size; - m_lazyStringModifiable = false; - } -} - -void ByteQueue::LazyPutModifiable(byte *inString, size_t size) -{ - if (m_lazyLength > 0) - FinalizeLazyPut(); - m_lazyString = inString; - m_lazyLength = size; - m_lazyStringModifiable = true; -} - -void ByteQueue::UndoLazyPut(size_t size) -{ - if (m_lazyLength < size) - throw InvalidArgument("ByteQueue: size specified for UndoLazyPut is too large"); - - m_lazyLength -= size; -} - -void ByteQueue::FinalizeLazyPut() -{ - size_t len = m_lazyLength; - m_lazyLength = 0; - if (len) - Put(m_lazyString, len); -} - -size_t ByteQueue::Get(byte &outByte) -{ - if (m_head->Get(outByte)) - { - if (m_head->UsedUp()) - CleanupUsedNodes(); - return 1; - } - else if (m_lazyLength > 0) - { - outByte = *m_lazyString++; - m_lazyLength--; - return 1; - } - else - return 0; -} - -size_t ByteQueue::Get(byte *outString, size_t getMax) -{ - ArraySink sink(outString, getMax); - return (size_t)TransferTo(sink, getMax); -} - -size_t ByteQueue::Peek(byte &outByte) const -{ - if (m_head->Peek(outByte)) - return 1; - else if (m_lazyLength > 0) - { - outByte = *m_lazyString; - return 1; - } - else - return 0; -} - -size_t ByteQueue::Peek(byte *outString, size_t peekMax) const -{ - ArraySink sink(outString, peekMax); - return (size_t)CopyTo(sink, peekMax); -} - -size_t ByteQueue::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - if (blocking) - { - lword bytesLeft = transferBytes; - for (ByteQueueNode *current=m_head; bytesLeft && current; current=current->next) - bytesLeft -= current->TransferTo(target, bytesLeft, channel); - CleanupUsedNodes(); - - size_t len = (size_t)STDMIN(bytesLeft, (lword)m_lazyLength); - if (len) - { - if (m_lazyStringModifiable) - target.ChannelPutModifiable(channel, m_lazyString, len); - else - target.ChannelPut(channel, m_lazyString, len); - m_lazyString += len; - m_lazyLength -= len; - bytesLeft -= len; - } - transferBytes -= bytesLeft; - return 0; - } - else - { - Walker walker(*this); - size_t blockedBytes = walker.TransferTo2(target, transferBytes, channel, blocking); - Skip(transferBytes); - return blockedBytes; - } -} - -size_t ByteQueue::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const -{ - Walker walker(*this); - walker.Skip(begin); - lword transferBytes = end-begin; - size_t blockedBytes = walker.TransferTo2(target, transferBytes, channel, blocking); - begin += transferBytes; - return blockedBytes; -} - -void ByteQueue::Unget(byte inByte) -{ - Unget(&inByte, 1); -} - -void ByteQueue::Unget(const byte *inString, size_t length) -{ - size_t len = STDMIN(length, m_head->m_head); - length -= len; - m_head->m_head -= len; - memcpy(m_head->buf + m_head->m_head, inString + length, len); - - if (length > 0) - { - ByteQueueNode *newHead = new ByteQueueNode(length); - newHead->next = m_head; - m_head = newHead; - m_head->Put(inString, length); - } -} - -const byte * ByteQueue::Spy(size_t &contiguousSize) const -{ - contiguousSize = m_head->m_tail - m_head->m_head; - if (contiguousSize == 0 && m_lazyLength > 0) - { - contiguousSize = m_lazyLength; - return m_lazyString; - } - else - return m_head->buf + m_head->m_head; -} - -byte * ByteQueue::CreatePutSpace(size_t &size) -{ - if (m_lazyLength > 0) - FinalizeLazyPut(); - - if (m_tail->m_tail == m_tail->MaxSize()) - { - m_tail->next = new ByteQueueNode(STDMAX(m_nodeSize, size)); - m_tail = m_tail->next; - } - - size = m_tail->MaxSize() - m_tail->m_tail; - return m_tail->buf + m_tail->m_tail; -} - -ByteQueue & ByteQueue::operator=(const ByteQueue &rhs) -{ - Destroy(); - CopyFrom(rhs); - return *this; -} - -bool ByteQueue::operator==(const ByteQueue &rhs) const -{ - const lword currentSize = CurrentSize(); - - if (currentSize != rhs.CurrentSize()) - return false; - - Walker walker1(*this), walker2(rhs); - byte b1, b2; - - while (walker1.Get(b1) && walker2.Get(b2)) - if (b1 != b2) - return false; - - return true; -} - -byte ByteQueue::operator[](lword i) const -{ - for (ByteQueueNode *current=m_head; current; current=current->next) - { - if (i < current->CurrentSize()) - return (*current)[(size_t)i]; - - i -= current->CurrentSize(); - } - - assert(i < m_lazyLength); - return m_lazyString[i]; -} - -void ByteQueue::swap(ByteQueue &rhs) -{ - std::swap(m_autoNodeSize, rhs.m_autoNodeSize); - std::swap(m_nodeSize, rhs.m_nodeSize); - std::swap(m_head, rhs.m_head); - std::swap(m_tail, rhs.m_tail); - std::swap(m_lazyString, rhs.m_lazyString); - std::swap(m_lazyLength, rhs.m_lazyLength); - std::swap(m_lazyStringModifiable, rhs.m_lazyStringModifiable); -} - -// ******************************************************** - -void ByteQueue::Walker::IsolatedInitialize(const NameValuePairs ¶meters) -{ - m_node = m_queue.m_head; - m_position = 0; - m_offset = 0; - m_lazyString = m_queue.m_lazyString; - m_lazyLength = m_queue.m_lazyLength; -} - -size_t ByteQueue::Walker::Get(byte &outByte) -{ - ArraySink sink(&outByte, 1); - return (size_t)TransferTo(sink, 1); -} - -size_t ByteQueue::Walker::Get(byte *outString, size_t getMax) -{ - ArraySink sink(outString, getMax); - return (size_t)TransferTo(sink, getMax); -} - -size_t ByteQueue::Walker::Peek(byte &outByte) const -{ - ArraySink sink(&outByte, 1); - return (size_t)CopyTo(sink, 1); -} - -size_t ByteQueue::Walker::Peek(byte *outString, size_t peekMax) const -{ - ArraySink sink(outString, peekMax); - return (size_t)CopyTo(sink, peekMax); -} - -size_t ByteQueue::Walker::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - lword bytesLeft = transferBytes; - size_t blockedBytes = 0; - - while (m_node) - { - size_t len = (size_t)STDMIN(bytesLeft, (lword)m_node->CurrentSize()-m_offset); - blockedBytes = target.ChannelPut2(channel, m_node->buf+m_node->m_head+m_offset, len, 0, blocking); - - if (blockedBytes) - goto done; - - m_position += len; - bytesLeft -= len; - - if (!bytesLeft) - { - m_offset += len; - goto done; - } - - m_node = m_node->next; - m_offset = 0; - } - - if (bytesLeft && m_lazyLength) - { - size_t len = (size_t)STDMIN(bytesLeft, (lword)m_lazyLength); - blockedBytes = target.ChannelPut2(channel, m_lazyString, len, 0, blocking); - if (blockedBytes) - goto done; - - m_lazyString += len; - m_lazyLength -= len; - bytesLeft -= len; - } - -done: - transferBytes -= bytesLeft; - return blockedBytes; -} - -size_t ByteQueue::Walker::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const -{ - Walker walker(*this); - walker.Skip(begin); - lword transferBytes = end-begin; - size_t blockedBytes = walker.TransferTo2(target, transferBytes, channel, blocking); - begin += transferBytes; - return blockedBytes; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/queue.h b/CryptoPP/queue.h deleted file mode 100644 index ab89dbdf1..000000000 --- a/CryptoPP/queue.h +++ /dev/null @@ -1,144 +0,0 @@ -// specification file for an unlimited queue for storing bytes - -#ifndef CRYPTOPP_QUEUE_H -#define CRYPTOPP_QUEUE_H - -#include "simple.h" -//#include - -NAMESPACE_BEGIN(CryptoPP) - -/** The queue is implemented as a linked list of byte arrays, but you don't need to - know about that. So just ignore this next line. :) */ -class ByteQueueNode; - -//! Byte Queue -class CRYPTOPP_DLL ByteQueue : public Bufferless -{ -public: - ByteQueue(size_t nodeSize=0); - ByteQueue(const ByteQueue ©); - ~ByteQueue(); - - lword MaxRetrievable() const - {return CurrentSize();} - bool AnyRetrievable() const - {return !IsEmpty();} - - void IsolatedInitialize(const NameValuePairs ¶meters); - byte * CreatePutSpace(size_t &size); - size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking); - - size_t Get(byte &outByte); - size_t Get(byte *outString, size_t getMax); - - size_t Peek(byte &outByte) const; - size_t Peek(byte *outString, size_t peekMax) const; - - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const; - - // these member functions are not inherited - void SetNodeSize(size_t nodeSize); - - lword CurrentSize() const; - bool IsEmpty() const; - - void Clear(); - - void Unget(byte inByte); - void Unget(const byte *inString, size_t length); - - const byte * Spy(size_t &contiguousSize) const; - - void LazyPut(const byte *inString, size_t size); - void LazyPutModifiable(byte *inString, size_t size); - void UndoLazyPut(size_t size); - void FinalizeLazyPut(); - - ByteQueue & operator=(const ByteQueue &rhs); - bool operator==(const ByteQueue &rhs) const; - bool operator!=(const ByteQueue &rhs) const {return !operator==(rhs);} - byte operator[](lword i) const; - void swap(ByteQueue &rhs); - - class Walker : public InputRejecting - { - public: - Walker(const ByteQueue &queue) - : m_queue(queue) {Initialize();} - - lword GetCurrentPosition() {return m_position;} - - lword MaxRetrievable() const - {return m_queue.CurrentSize() - m_position;} - - void IsolatedInitialize(const NameValuePairs ¶meters); - - size_t Get(byte &outByte); - size_t Get(byte *outString, size_t getMax); - - size_t Peek(byte &outByte) const; - size_t Peek(byte *outString, size_t peekMax) const; - - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const; - - private: - const ByteQueue &m_queue; - const ByteQueueNode *m_node; - lword m_position; - size_t m_offset; - const byte *m_lazyString; - size_t m_lazyLength; - }; - - friend class Walker; - -private: - void CleanupUsedNodes(); - void CopyFrom(const ByteQueue ©); - void Destroy(); - - bool m_autoNodeSize; - size_t m_nodeSize; - ByteQueueNode *m_head, *m_tail; - byte *m_lazyString; - size_t m_lazyLength; - bool m_lazyStringModifiable; -}; - -//! use this to make sure LazyPut is finalized in event of exception -class CRYPTOPP_DLL LazyPutter -{ -public: - LazyPutter(ByteQueue &bq, const byte *inString, size_t size) - : m_bq(bq) {bq.LazyPut(inString, size);} - ~LazyPutter() - {try {m_bq.FinalizeLazyPut();} catch(...) {}} -protected: - LazyPutter(ByteQueue &bq) : m_bq(bq) {} -private: - ByteQueue &m_bq; -}; - -//! like LazyPutter, but does a LazyPutModifiable instead -class LazyPutterModifiable : public LazyPutter -{ -public: - LazyPutterModifiable(ByteQueue &bq, byte *inString, size_t size) - : LazyPutter(bq) {bq.LazyPutModifiable(inString, size);} -}; - -NAMESPACE_END - -#ifndef __BORLANDC__ -NAMESPACE_BEGIN(std) -template<> inline void swap(CryptoPP::ByteQueue &a, CryptoPP::ByteQueue &b) -{ - a.swap(b); -} -NAMESPACE_END -#endif - -#endif diff --git a/CryptoPP/rabin.cpp b/CryptoPP/rabin.cpp deleted file mode 100644 index d496333b5..000000000 --- a/CryptoPP/rabin.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// rabin.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "rabin.h" -#include "nbtheory.h" -#include "asn.h" -#include "sha.h" -#include "modarith.h" - -NAMESPACE_BEGIN(CryptoPP) - -void RabinFunction::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder seq(bt); - m_n.BERDecode(seq); - m_r.BERDecode(seq); - m_s.BERDecode(seq); - seq.MessageEnd(); -} - -void RabinFunction::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - m_n.DEREncode(seq); - m_r.DEREncode(seq); - m_s.DEREncode(seq); - seq.MessageEnd(); -} - -Integer RabinFunction::ApplyFunction(const Integer &in) const -{ - DoQuickSanityCheck(); - - Integer out = in.Squared()%m_n; - if (in.IsOdd()) - out = out*m_r%m_n; - if (Jacobi(in, m_n)==-1) - out = out*m_s%m_n; - return out; -} - -bool RabinFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = true; - pass = pass && m_n > Integer::One() && m_n%4 == 1; - pass = pass && m_r > Integer::One() && m_r < m_n; - pass = pass && m_s > Integer::One() && m_s < m_n; - if (level >= 1) - pass = pass && Jacobi(m_r, m_n) == -1 && Jacobi(m_s, m_n) == -1; - return pass; -} - -bool RabinFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Modulus) - CRYPTOPP_GET_FUNCTION_ENTRY(QuadraticResidueModPrime1) - CRYPTOPP_GET_FUNCTION_ENTRY(QuadraticResidueModPrime2) - ; -} - -void RabinFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Modulus) - CRYPTOPP_SET_FUNCTION_ENTRY(QuadraticResidueModPrime1) - CRYPTOPP_SET_FUNCTION_ENTRY(QuadraticResidueModPrime2) - ; -} - -// ***************************************************************************** -// private key operations: - -// generate a random private key -void InvertibleRabinFunction::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg) -{ - int modulusSize = 2048; - alg.GetIntValue("ModulusSize", modulusSize) || alg.GetIntValue("KeySize", modulusSize); - - if (modulusSize < 16) - throw InvalidArgument("InvertibleRabinFunction: specified modulus size is too small"); - - // VC70 workaround: putting these after primeParam causes overlapped stack allocation - bool rFound=false, sFound=false; - Integer t=2; - - AlgorithmParameters primeParam = MakeParametersForTwoPrimesOfEqualSize(modulusSize) - ("EquivalentTo", 3)("Mod", 4); - m_p.GenerateRandom(rng, primeParam); - m_q.GenerateRandom(rng, primeParam); - - while (!(rFound && sFound)) - { - int jp = Jacobi(t, m_p); - int jq = Jacobi(t, m_q); - - if (!rFound && jp==1 && jq==-1) - { - m_r = t; - rFound = true; - } - - if (!sFound && jp==-1 && jq==1) - { - m_s = t; - sFound = true; - } - - ++t; - } - - m_n = m_p * m_q; - m_u = m_q.InverseMod(m_p); -} - -void InvertibleRabinFunction::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder seq(bt); - m_n.BERDecode(seq); - m_r.BERDecode(seq); - m_s.BERDecode(seq); - m_p.BERDecode(seq); - m_q.BERDecode(seq); - m_u.BERDecode(seq); - seq.MessageEnd(); -} - -void InvertibleRabinFunction::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - m_n.DEREncode(seq); - m_r.DEREncode(seq); - m_s.DEREncode(seq); - m_p.DEREncode(seq); - m_q.DEREncode(seq); - m_u.DEREncode(seq); - seq.MessageEnd(); -} - -Integer InvertibleRabinFunction::CalculateInverse(RandomNumberGenerator &rng, const Integer &in) const -{ - DoQuickSanityCheck(); - - ModularArithmetic modn(m_n); - Integer r(rng, Integer::One(), m_n - Integer::One()); - r = modn.Square(r); - Integer r2 = modn.Square(r); - Integer c = modn.Multiply(in, r2); // blind - - Integer cp=c%m_p, cq=c%m_q; - - int jp = Jacobi(cp, m_p); - int jq = Jacobi(cq, m_q); - - if (jq==-1) - { - cp = cp*EuclideanMultiplicativeInverse(m_r, m_p)%m_p; - cq = cq*EuclideanMultiplicativeInverse(m_r, m_q)%m_q; - } - - if (jp==-1) - { - cp = cp*EuclideanMultiplicativeInverse(m_s, m_p)%m_p; - cq = cq*EuclideanMultiplicativeInverse(m_s, m_q)%m_q; - } - - cp = ModularSquareRoot(cp, m_p); - cq = ModularSquareRoot(cq, m_q); - - if (jp==-1) - cp = m_p-cp; - - Integer out = CRT(cq, m_q, cp, m_p, m_u); - - out = modn.Divide(out, r); // unblind - - if ((jq==-1 && out.IsEven()) || (jq==1 && out.IsOdd())) - out = m_n-out; - - return out; -} - -bool InvertibleRabinFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = RabinFunction::Validate(rng, level); - pass = pass && m_p > Integer::One() && m_p%4 == 3 && m_p < m_n; - pass = pass && m_q > Integer::One() && m_q%4 == 3 && m_q < m_n; - pass = pass && m_u.IsPositive() && m_u < m_p; - if (level >= 1) - { - pass = pass && m_p * m_q == m_n; - pass = pass && m_u * m_q % m_p == 1; - pass = pass && Jacobi(m_r, m_p) == 1; - pass = pass && Jacobi(m_r, m_q) == -1; - pass = pass && Jacobi(m_s, m_p) == -1; - pass = pass && Jacobi(m_s, m_q) == 1; - } - if (level >= 2) - pass = pass && VerifyPrime(rng, m_p, level-2) && VerifyPrime(rng, m_q, level-2); - return pass; -} - -bool InvertibleRabinFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_GET_FUNCTION_ENTRY(Prime2) - CRYPTOPP_GET_FUNCTION_ENTRY(MultiplicativeInverseOfPrime2ModPrime1) - ; -} - -void InvertibleRabinFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime2) - CRYPTOPP_SET_FUNCTION_ENTRY(MultiplicativeInverseOfPrime2ModPrime1) - ; -} - -NAMESPACE_END diff --git a/CryptoPP/rabin.h b/CryptoPP/rabin.h deleted file mode 100644 index 1c9bcbb49..000000000 --- a/CryptoPP/rabin.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef CRYPTOPP_RABIN_H -#define CRYPTOPP_RABIN_H - -/** \file -*/ - -#include "oaep.h" -#include "pssr.h" -#include "integer.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -class RabinFunction : public TrapdoorFunction, public PublicKey -{ - typedef RabinFunction ThisClass; - -public: - void Initialize(const Integer &n, const Integer &r, const Integer &s) - {m_n = n; m_r = r; m_s = s;} - - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - Integer ApplyFunction(const Integer &x) const; - Integer PreimageBound() const {return m_n;} - Integer ImageBound() const {return m_n;} - - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - - const Integer& GetModulus() const {return m_n;} - const Integer& GetQuadraticResidueModPrime1() const {return m_r;} - const Integer& GetQuadraticResidueModPrime2() const {return m_s;} - - void SetModulus(const Integer &n) {m_n = n;} - void SetQuadraticResidueModPrime1(const Integer &r) {m_r = r;} - void SetQuadraticResidueModPrime2(const Integer &s) {m_s = s;} - -protected: - Integer m_n, m_r, m_s; -}; - -//! _ -class InvertibleRabinFunction : public RabinFunction, public TrapdoorFunctionInverse, public PrivateKey -{ - typedef InvertibleRabinFunction ThisClass; - -public: - void Initialize(const Integer &n, const Integer &r, const Integer &s, - const Integer &p, const Integer &q, const Integer &u) - {m_n = n; m_r = r; m_s = s; m_p = p; m_q = q; m_u = u;} - void Initialize(RandomNumberGenerator &rng, unsigned int keybits) - {GenerateRandomWithKeySize(rng, keybits);} - - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - Integer CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const; - - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - /*! parameters: (ModulusSize) */ - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg); - - const Integer& GetPrime1() const {return m_p;} - const Integer& GetPrime2() const {return m_q;} - const Integer& GetMultiplicativeInverseOfPrime2ModPrime1() const {return m_u;} - - void SetPrime1(const Integer &p) {m_p = p;} - void SetPrime2(const Integer &q) {m_q = q;} - void SetMultiplicativeInverseOfPrime2ModPrime1(const Integer &u) {m_u = u;} - -protected: - Integer m_p, m_q, m_u; -}; - -//! Rabin -struct Rabin -{ - static std::string StaticAlgorithmName() {return "Rabin-Crypto++Variant";} - typedef RabinFunction PublicKey; - typedef InvertibleRabinFunction PrivateKey; -}; - -//! Rabin encryption -template -struct RabinES : public TF_ES -{ -}; - -//! Rabin signature -template -struct RabinSS : public TF_SS -{ -}; - -// More typedefs for backwards compatibility -class SHA1; -typedef RabinES >::Decryptor RabinDecryptor; -typedef RabinES >::Encryptor RabinEncryptor; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/randpool.cpp b/CryptoPP/randpool.cpp deleted file mode 100644 index a063c8996..000000000 --- a/CryptoPP/randpool.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// randpool.cpp - written and placed in the public domain by Wei Dai -// RandomPool used to follow the design of randpool in PGP 2.6.x, -// but as of version 5.5 it has been redesigned to reduce the risk -// of reusing random numbers after state rollback (which may occur -// when running in a virtual machine like VMware). - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "randpool.h" -#include "aes.h" -#include "sha.h" -#include "hrtimer.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -RandomPool::RandomPool() - : m_pCipher(new AES::Encryption), m_keySet(false) -{ - memset(m_key, 0, m_key.SizeInBytes()); - memset(m_seed, 0, m_seed.SizeInBytes()); -} - -void RandomPool::IncorporateEntropy(const byte *input, size_t length) -{ - SHA256 hash; - hash.Update(m_key, 32); - hash.Update(input, length); - hash.Final(m_key); - m_keySet = false; -} - -void RandomPool::GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size) -{ - if (size > 0) - { - if (!m_keySet) - m_pCipher->SetKey(m_key, 32); - - Timer timer; - TimerWord tw = timer.GetCurrentTimerValue(); - CRYPTOPP_COMPILE_ASSERT(sizeof(tw) <= 16); - *(TimerWord *)m_seed.data() += tw; - - time_t t = time(NULL); - CRYPTOPP_COMPILE_ASSERT(sizeof(t) <= 8); - *(time_t *)(m_seed.data()+8) += t; - - do - { - m_pCipher->ProcessBlock(m_seed); - size_t len = UnsignedMin(16, size); - target.ChannelPut(channel, m_seed, len); - size -= len; - } while (size > 0); - } -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/randpool.h b/CryptoPP/randpool.h deleted file mode 100644 index c25bc9bb1..000000000 --- a/CryptoPP/randpool.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef CRYPTOPP_RANDPOOL_H -#define CRYPTOPP_RANDPOOL_H - -#include "cryptlib.h" -#include "filters.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! Randomness Pool -/*! This class can be used to generate cryptographic quality - pseudorandom bytes after seeding the pool with IncorporateEntropy() */ -class CRYPTOPP_DLL RandomPool : public RandomNumberGenerator, public NotCopyable -{ -public: - RandomPool(); - - bool CanIncorporateEntropy() const {return true;} - void IncorporateEntropy(const byte *input, size_t length); - void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size); - - // for backwards compatibility. use RandomNumberSource, RandomNumberStore, and RandomNumberSink for other BufferTransformation functionality - void Put(const byte *input, size_t length) {IncorporateEntropy(input, length);} - -private: - FixedSizeSecBlock m_key; - FixedSizeSecBlock m_seed; - member_ptr m_pCipher; - bool m_keySet; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/rdtables.cpp b/CryptoPP/rdtables.cpp deleted file mode 100644 index 493793252..000000000 --- a/CryptoPP/rdtables.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// Rijndael tables - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "rijndael.h" - -// VC60 workaround: gives a C4786 warning without this function -// when runtime lib is set to multithread debug DLL -// even though warning 4786 is disabled! -void Rijndael_VC60Workaround() -{ -} - -NAMESPACE_BEGIN(CryptoPP) - -/* -Te0[x] = S [x].[02, 01, 01, 03]; -Te1[x] = S [x].[03, 02, 01, 01]; -Te2[x] = S [x].[01, 03, 02, 01]; -Te3[x] = S [x].[01, 01, 03, 02]; - -Td0[x] = Si[x].[0e, 09, 0d, 0b]; -Td1[x] = Si[x].[0b, 0e, 09, 0d]; -Td2[x] = Si[x].[0d, 0b, 0e, 09]; -Td3[x] = Si[x].[09, 0d, 0b, 0e]; -*/ - -const byte Rijndael::Base::Se[256] = { - 0x63, 0x7c, 0x77, 0x7b, - 0xf2, 0x6b, 0x6f, 0xc5, - 0x30, 0x01, 0x67, 0x2b, - 0xfe, 0xd7, 0xab, 0x76, - 0xca, 0x82, 0xc9, 0x7d, - 0xfa, 0x59, 0x47, 0xf0, - 0xad, 0xd4, 0xa2, 0xaf, - 0x9c, 0xa4, 0x72, 0xc0, - 0xb7, 0xfd, 0x93, 0x26, - 0x36, 0x3f, 0xf7, 0xcc, - 0x34, 0xa5, 0xe5, 0xf1, - 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, - 0x18, 0x96, 0x05, 0x9a, - 0x07, 0x12, 0x80, 0xe2, - 0xeb, 0x27, 0xb2, 0x75, - 0x09, 0x83, 0x2c, 0x1a, - 0x1b, 0x6e, 0x5a, 0xa0, - 0x52, 0x3b, 0xd6, 0xb3, - 0x29, 0xe3, 0x2f, 0x84, - 0x53, 0xd1, 0x00, 0xed, - 0x20, 0xfc, 0xb1, 0x5b, - 0x6a, 0xcb, 0xbe, 0x39, - 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, - 0x43, 0x4d, 0x33, 0x85, - 0x45, 0xf9, 0x02, 0x7f, - 0x50, 0x3c, 0x9f, 0xa8, - 0x51, 0xa3, 0x40, 0x8f, - 0x92, 0x9d, 0x38, 0xf5, - 0xbc, 0xb6, 0xda, 0x21, - 0x10, 0xff, 0xf3, 0xd2, - 0xcd, 0x0c, 0x13, 0xec, - 0x5f, 0x97, 0x44, 0x17, - 0xc4, 0xa7, 0x7e, 0x3d, - 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, - 0x22, 0x2a, 0x90, 0x88, - 0x46, 0xee, 0xb8, 0x14, - 0xde, 0x5e, 0x0b, 0xdb, - 0xe0, 0x32, 0x3a, 0x0a, - 0x49, 0x06, 0x24, 0x5c, - 0xc2, 0xd3, 0xac, 0x62, - 0x91, 0x95, 0xe4, 0x79, - 0xe7, 0xc8, 0x37, 0x6d, - 0x8d, 0xd5, 0x4e, 0xa9, - 0x6c, 0x56, 0xf4, 0xea, - 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, - 0x1c, 0xa6, 0xb4, 0xc6, - 0xe8, 0xdd, 0x74, 0x1f, - 0x4b, 0xbd, 0x8b, 0x8a, - 0x70, 0x3e, 0xb5, 0x66, - 0x48, 0x03, 0xf6, 0x0e, - 0x61, 0x35, 0x57, 0xb9, - 0x86, 0xc1, 0x1d, 0x9e, - 0xe1, 0xf8, 0x98, 0x11, - 0x69, 0xd9, 0x8e, 0x94, - 0x9b, 0x1e, 0x87, 0xe9, - 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, - 0xbf, 0xe6, 0x42, 0x68, - 0x41, 0x99, 0x2d, 0x0f, - 0xb0, 0x54, 0xbb, 0x16, -}; - -const byte Rijndael::Base::Sd[256] = { - 0x52, 0x09, 0x6a, 0xd5, - 0x30, 0x36, 0xa5, 0x38, - 0xbf, 0x40, 0xa3, 0x9e, - 0x81, 0xf3, 0xd7, 0xfb, - 0x7c, 0xe3, 0x39, 0x82, - 0x9b, 0x2f, 0xff, 0x87, - 0x34, 0x8e, 0x43, 0x44, - 0xc4, 0xde, 0xe9, 0xcb, - 0x54, 0x7b, 0x94, 0x32, - 0xa6, 0xc2, 0x23, 0x3d, - 0xee, 0x4c, 0x95, 0x0b, - 0x42, 0xfa, 0xc3, 0x4e, - 0x08, 0x2e, 0xa1, 0x66, - 0x28, 0xd9, 0x24, 0xb2, - 0x76, 0x5b, 0xa2, 0x49, - 0x6d, 0x8b, 0xd1, 0x25, - 0x72, 0xf8, 0xf6, 0x64, - 0x86, 0x68, 0x98, 0x16, - 0xd4, 0xa4, 0x5c, 0xcc, - 0x5d, 0x65, 0xb6, 0x92, - 0x6c, 0x70, 0x48, 0x50, - 0xfd, 0xed, 0xb9, 0xda, - 0x5e, 0x15, 0x46, 0x57, - 0xa7, 0x8d, 0x9d, 0x84, - 0x90, 0xd8, 0xab, 0x00, - 0x8c, 0xbc, 0xd3, 0x0a, - 0xf7, 0xe4, 0x58, 0x05, - 0xb8, 0xb3, 0x45, 0x06, - 0xd0, 0x2c, 0x1e, 0x8f, - 0xca, 0x3f, 0x0f, 0x02, - 0xc1, 0xaf, 0xbd, 0x03, - 0x01, 0x13, 0x8a, 0x6b, - 0x3a, 0x91, 0x11, 0x41, - 0x4f, 0x67, 0xdc, 0xea, - 0x97, 0xf2, 0xcf, 0xce, - 0xf0, 0xb4, 0xe6, 0x73, - 0x96, 0xac, 0x74, 0x22, - 0xe7, 0xad, 0x35, 0x85, - 0xe2, 0xf9, 0x37, 0xe8, - 0x1c, 0x75, 0xdf, 0x6e, - 0x47, 0xf1, 0x1a, 0x71, - 0x1d, 0x29, 0xc5, 0x89, - 0x6f, 0xb7, 0x62, 0x0e, - 0xaa, 0x18, 0xbe, 0x1b, - 0xfc, 0x56, 0x3e, 0x4b, - 0xc6, 0xd2, 0x79, 0x20, - 0x9a, 0xdb, 0xc0, 0xfe, - 0x78, 0xcd, 0x5a, 0xf4, - 0x1f, 0xdd, 0xa8, 0x33, - 0x88, 0x07, 0xc7, 0x31, - 0xb1, 0x12, 0x10, 0x59, - 0x27, 0x80, 0xec, 0x5f, - 0x60, 0x51, 0x7f, 0xa9, - 0x19, 0xb5, 0x4a, 0x0d, - 0x2d, 0xe5, 0x7a, 0x9f, - 0x93, 0xc9, 0x9c, 0xef, - 0xa0, 0xe0, 0x3b, 0x4d, - 0xae, 0x2a, 0xf5, 0xb0, - 0xc8, 0xeb, 0xbb, 0x3c, - 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2b, 0x04, 0x7e, - 0xba, 0x77, 0xd6, 0x26, - 0xe1, 0x69, 0x14, 0x63, - 0x55, 0x21, 0x0c, 0x7d, -}; - -const word32 Rijndael::Base::rcon[] = { - 0x01000000, 0x02000000, 0x04000000, 0x08000000, - 0x10000000, 0x20000000, 0x40000000, 0x80000000, - 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/resource.h b/CryptoPP/resource.h deleted file mode 100644 index 861e22ba3..000000000 --- a/CryptoPP/resource.h +++ /dev/null @@ -1,15 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by cryptopp.rc -// - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/CryptoPP/rijndael.cpp b/CryptoPP/rijndael.cpp deleted file mode 100644 index c185032cf..000000000 --- a/CryptoPP/rijndael.cpp +++ /dev/null @@ -1,1261 +0,0 @@ -// rijndael.cpp - modified by Chris Morgan -// and Wei Dai from Paulo Baretto's Rijndael implementation -// The original code and all modifications are in the public domain. - -// use "cl /EP /P /DCRYPTOPP_GENERATE_X64_MASM rijndael.cpp" to generate MASM code - -/* -July 2010: Added support for AES-NI instructions via compiler intrinsics. -*/ - -/* -Feb 2009: The x86/x64 assembly code was rewritten in by Wei Dai to do counter mode -caching, which was invented by Hongjun Wu and popularized by Daniel J. Bernstein -and Peter Schwabe in their paper "New AES software speed records". The round -function was also modified to include a trick similar to one in Brian Gladman's -x86 assembly code, doing an 8-bit register move to minimize the number of -register spills. Also switched to compressed tables and copying round keys to -the stack. - -The C++ implementation now uses compressed tables if -CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS is defined. -*/ - -/* -July 2006: Defense against timing attacks was added in by Wei Dai. - -The code now uses smaller tables in the first and last rounds, -and preloads them into L1 cache before usage (by loading at least -one element in each cache line). - -We try to delay subsequent accesses to each table (used in the first -and last rounds) until all of the table has been preloaded. Hopefully -the compiler isn't smart enough to optimize that code away. - -After preloading the table, we also try not to access any memory location -other than the table and the stack, in order to prevent table entries from -being unloaded from L1 cache, until that round is finished. -(Some popular CPUs have 2-way associative caches.) -*/ - -// This is the original introductory comment: - -/** - * version 3.0 (December 2000) - * - * Optimised ANSI C code for the Rijndael cipher (now AES) - * - * author Vincent Rijmen - * author Antoon Bosselaers - * author Paulo Barreto - * - * This code is hereby placed in the public domain. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS -#ifndef CRYPTOPP_GENERATE_X64_MASM - -#include "rijndael.h" -#include "misc.h" -#include "cpu.h" - -NAMESPACE_BEGIN(CryptoPP) - -#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) -namespace rdtable {CRYPTOPP_ALIGN_DATA(16) word64 Te[256+2];} -using namespace rdtable; -#else -static word64 Te[256]; -#endif -static word64 Td[256]; -#else -static word32 Te[256*4], Td[256*4]; -#endif -static volatile bool s_TeFilled = false, s_TdFilled = false; - -// ************************* Portable Code ************************************ - -#define QUARTER_ROUND(L, T, t, a, b, c, d) \ - a ^= L(T, 3, byte(t)); t >>= 8;\ - b ^= L(T, 2, byte(t)); t >>= 8;\ - c ^= L(T, 1, byte(t)); t >>= 8;\ - d ^= L(T, 0, t); - -#define QUARTER_ROUND_LE(t, a, b, c, d) \ - tempBlock[a] = ((byte *)(Te+byte(t)))[1]; t >>= 8;\ - tempBlock[b] = ((byte *)(Te+byte(t)))[1]; t >>= 8;\ - tempBlock[c] = ((byte *)(Te+byte(t)))[1]; t >>= 8;\ - tempBlock[d] = ((byte *)(Te+t))[1]; - -#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - #define QUARTER_ROUND_LD(t, a, b, c, d) \ - tempBlock[a] = ((byte *)(Td+byte(t)))[GetNativeByteOrder()*7]; t >>= 8;\ - tempBlock[b] = ((byte *)(Td+byte(t)))[GetNativeByteOrder()*7]; t >>= 8;\ - tempBlock[c] = ((byte *)(Td+byte(t)))[GetNativeByteOrder()*7]; t >>= 8;\ - tempBlock[d] = ((byte *)(Td+t))[GetNativeByteOrder()*7]; -#else - #define QUARTER_ROUND_LD(t, a, b, c, d) \ - tempBlock[a] = Sd[byte(t)]; t >>= 8;\ - tempBlock[b] = Sd[byte(t)]; t >>= 8;\ - tempBlock[c] = Sd[byte(t)]; t >>= 8;\ - tempBlock[d] = Sd[t]; -#endif - -#define QUARTER_ROUND_E(t, a, b, c, d) QUARTER_ROUND(TL_M, Te, t, a, b, c, d) -#define QUARTER_ROUND_D(t, a, b, c, d) QUARTER_ROUND(TL_M, Td, t, a, b, c, d) - -#ifdef IS_LITTLE_ENDIAN - #define QUARTER_ROUND_FE(t, a, b, c, d) QUARTER_ROUND(TL_F, Te, t, d, c, b, a) - #define QUARTER_ROUND_FD(t, a, b, c, d) QUARTER_ROUND(TL_F, Td, t, d, c, b, a) - #ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - #define TL_F(T, i, x) (*(word32 *)((byte *)T + x*8 + (6-i)%4+1)) - #define TL_M(T, i, x) (*(word32 *)((byte *)T + x*8 + (i+3)%4+1)) - #else - #define TL_F(T, i, x) rotrFixed(T[x], (3-i)*8) - #define TL_M(T, i, x) T[i*256 + x] - #endif -#else - #define QUARTER_ROUND_FE(t, a, b, c, d) QUARTER_ROUND(TL_F, Te, t, a, b, c, d) - #define QUARTER_ROUND_FD(t, a, b, c, d) QUARTER_ROUND(TL_F, Td, t, a, b, c, d) - #ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - #define TL_F(T, i, x) (*(word32 *)((byte *)T + x*8 + (4-i)%4)) - #define TL_M TL_F - #else - #define TL_F(T, i, x) rotrFixed(T[x], i*8) - #define TL_M(T, i, x) T[i*256 + x] - #endif -#endif - - -#define f2(x) ((x<<1)^(((x>>7)&1)*0x11b)) -#define f4(x) ((x<<2)^(((x>>6)&1)*0x11b)^(((x>>6)&2)*0x11b)) -#define f8(x) ((x<<3)^(((x>>5)&1)*0x11b)^(((x>>5)&2)*0x11b)^(((x>>5)&4)*0x11b)) - -#define f3(x) (f2(x) ^ x) -#define f9(x) (f8(x) ^ x) -#define fb(x) (f8(x) ^ f2(x) ^ x) -#define fd(x) (f8(x) ^ f4(x) ^ x) -#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) - -void Rijndael::Base::FillEncTable() -{ - for (int i=0; i<256; i++) - { - byte x = Se[i]; -#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - word32 y = word32(x)<<8 | word32(x)<<16 | word32(f2(x))<<24; - Te[i] = word64(y | f3(x))<<32 | y; -#else - word32 y = f3(x) | word32(x)<<8 | word32(x)<<16 | word32(f2(x))<<24; - for (int j=0; j<4; j++) - { - Te[i+j*256] = y; - y = rotrFixed(y, 8); - } -#endif - } -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) - Te[256] = Te[257] = 0; -#endif - s_TeFilled = true; -} - -void Rijndael::Base::FillDecTable() -{ - for (int i=0; i<256; i++) - { - byte x = Sd[i]; -#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - word32 y = word32(fd(x))<<8 | word32(f9(x))<<16 | word32(fe(x))<<24; - Td[i] = word64(y | fb(x))<<32 | y | x; -#else - word32 y = fb(x) | word32(fd(x))<<8 | word32(f9(x))<<16 | word32(fe(x))<<24;; - for (int j=0; j<4; j++) - { - Td[i+j*256] = y; - y = rotrFixed(y, 8); - } -#endif - } - s_TdFilled = true; -} - -void Rijndael::Base::UncheckedSetKey(const byte *userKey, unsigned int keylen, const NameValuePairs &) -{ - AssertValidKeyLength(keylen); - - m_rounds = keylen/4 + 6; - m_key.New(4*(m_rounds+1)); - - word32 *rk = m_key; - -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE && (!defined(_MSC_VER) || _MSC_VER >= 1600 || CRYPTOPP_BOOL_X86) - // MSVC 2008 SP1 generates bad code for _mm_extract_epi32() when compiling for X64 - if (HasAESNI()) - { - static const word32 rcLE[] = { - 0x01, 0x02, 0x04, 0x08, - 0x10, 0x20, 0x40, 0x80, - 0x1B, 0x36, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ - }; - const word32 *rc = rcLE; - - __m128i temp = _mm_loadu_si128((__m128i *)(userKey+keylen-16)); - memcpy(rk, userKey, keylen); - - while (true) - { - rk[keylen/4] = rk[0] ^ _mm_extract_epi32(_mm_aeskeygenassist_si128(temp, 0), 3) ^ *(rc++); - rk[keylen/4+1] = rk[1] ^ rk[keylen/4]; - rk[keylen/4+2] = rk[2] ^ rk[keylen/4+1]; - rk[keylen/4+3] = rk[3] ^ rk[keylen/4+2]; - - if (rk + keylen/4 + 4 == m_key.end()) - break; - - if (keylen == 24) - { - rk[10] = rk[ 4] ^ rk[ 9]; - rk[11] = rk[ 5] ^ rk[10]; - temp = _mm_insert_epi32(temp, rk[11], 3); - } - else if (keylen == 32) - { - temp = _mm_insert_epi32(temp, rk[11], 3); - rk[12] = rk[ 4] ^ _mm_extract_epi32(_mm_aeskeygenassist_si128(temp, 0), 2); - rk[13] = rk[ 5] ^ rk[12]; - rk[14] = rk[ 6] ^ rk[13]; - rk[15] = rk[ 7] ^ rk[14]; - temp = _mm_insert_epi32(temp, rk[15], 3); - } - else - temp = _mm_insert_epi32(temp, rk[7], 3); - - rk += keylen/4; - } - - if (!IsForwardTransformation()) - { - rk = m_key; - unsigned int i, j; - - std::swap(*(__m128i *)(rk), *(__m128i *)(rk+4*m_rounds)); - - for (i = 4, j = 4*m_rounds-4; i < j; i += 4, j -= 4) - { - temp = _mm_aesimc_si128(*(__m128i *)(rk+i)); - *(__m128i *)(rk+i) = _mm_aesimc_si128(*(__m128i *)(rk+j)); - *(__m128i *)(rk+j) = temp; - } - - *(__m128i *)(rk+i) = _mm_aesimc_si128(*(__m128i *)(rk+i)); - } - - return; - } -#endif - - GetUserKey(BIG_ENDIAN_ORDER, rk, keylen/4, userKey, keylen); - const word32 *rc = rcon; - word32 temp; - - while (true) - { - temp = rk[keylen/4-1]; - word32 x = (word32(Se[GETBYTE(temp, 2)]) << 24) ^ (word32(Se[GETBYTE(temp, 1)]) << 16) ^ (word32(Se[GETBYTE(temp, 0)]) << 8) ^ Se[GETBYTE(temp, 3)]; - rk[keylen/4] = rk[0] ^ x ^ *(rc++); - rk[keylen/4+1] = rk[1] ^ rk[keylen/4]; - rk[keylen/4+2] = rk[2] ^ rk[keylen/4+1]; - rk[keylen/4+3] = rk[3] ^ rk[keylen/4+2]; - - if (rk + keylen/4 + 4 == m_key.end()) - break; - - if (keylen == 24) - { - rk[10] = rk[ 4] ^ rk[ 9]; - rk[11] = rk[ 5] ^ rk[10]; - } - else if (keylen == 32) - { - temp = rk[11]; - rk[12] = rk[ 4] ^ (word32(Se[GETBYTE(temp, 3)]) << 24) ^ (word32(Se[GETBYTE(temp, 2)]) << 16) ^ (word32(Se[GETBYTE(temp, 1)]) << 8) ^ Se[GETBYTE(temp, 0)]; - rk[13] = rk[ 5] ^ rk[12]; - rk[14] = rk[ 6] ^ rk[13]; - rk[15] = rk[ 7] ^ rk[14]; - } - rk += keylen/4; - } - - rk = m_key; - - if (IsForwardTransformation()) - { - if (!s_TeFilled) - FillEncTable(); - - ConditionalByteReverse(BIG_ENDIAN_ORDER, rk, rk, 16); - ConditionalByteReverse(BIG_ENDIAN_ORDER, rk + m_rounds*4, rk + m_rounds*4, 16); - } - else - { - if (!s_TdFilled) - FillDecTable(); - - unsigned int i, j; - -#define InverseMixColumn(x) TL_M(Td, 0, Se[GETBYTE(x, 3)]) ^ TL_M(Td, 1, Se[GETBYTE(x, 2)]) ^ TL_M(Td, 2, Se[GETBYTE(x, 1)]) ^ TL_M(Td, 3, Se[GETBYTE(x, 0)]) - - for (i = 4, j = 4*m_rounds-4; i < j; i += 4, j -= 4) - { - temp = InverseMixColumn(rk[i ]); rk[i ] = InverseMixColumn(rk[j ]); rk[j ] = temp; - temp = InverseMixColumn(rk[i + 1]); rk[i + 1] = InverseMixColumn(rk[j + 1]); rk[j + 1] = temp; - temp = InverseMixColumn(rk[i + 2]); rk[i + 2] = InverseMixColumn(rk[j + 2]); rk[j + 2] = temp; - temp = InverseMixColumn(rk[i + 3]); rk[i + 3] = InverseMixColumn(rk[j + 3]); rk[j + 3] = temp; - } - - rk[i+0] = InverseMixColumn(rk[i+0]); - rk[i+1] = InverseMixColumn(rk[i+1]); - rk[i+2] = InverseMixColumn(rk[i+2]); - rk[i+3] = InverseMixColumn(rk[i+3]); - - temp = ConditionalByteReverse(BIG_ENDIAN_ORDER, rk[0]); rk[0] = ConditionalByteReverse(BIG_ENDIAN_ORDER, rk[4*m_rounds+0]); rk[4*m_rounds+0] = temp; - temp = ConditionalByteReverse(BIG_ENDIAN_ORDER, rk[1]); rk[1] = ConditionalByteReverse(BIG_ENDIAN_ORDER, rk[4*m_rounds+1]); rk[4*m_rounds+1] = temp; - temp = ConditionalByteReverse(BIG_ENDIAN_ORDER, rk[2]); rk[2] = ConditionalByteReverse(BIG_ENDIAN_ORDER, rk[4*m_rounds+2]); rk[4*m_rounds+2] = temp; - temp = ConditionalByteReverse(BIG_ENDIAN_ORDER, rk[3]); rk[3] = ConditionalByteReverse(BIG_ENDIAN_ORDER, rk[4*m_rounds+3]); rk[4*m_rounds+3] = temp; - } - -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - if (HasAESNI()) - ConditionalByteReverse(BIG_ENDIAN_ORDER, rk+4, rk+4, (m_rounds-1)*16); -#endif -} - -void Rijndael::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) || CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) - if (HasSSE2()) -#else - if (HasAESNI()) -#endif - { - Rijndael::Enc::AdvancedProcessBlocks(inBlock, xorBlock, outBlock, 16, 0); - return; - } -#endif - - typedef BlockGetAndPut Block; - - word32 s0, s1, s2, s3, t0, t1, t2, t3; - Block::Get(inBlock)(s0)(s1)(s2)(s3); - - const word32 *rk = m_key; - s0 ^= rk[0]; - s1 ^= rk[1]; - s2 ^= rk[2]; - s3 ^= rk[3]; - t0 = rk[4]; - t1 = rk[5]; - t2 = rk[6]; - t3 = rk[7]; - rk += 8; - - // timing attack countermeasure. see comments at top for more details - const int cacheLineSize = GetCacheLineSize(); - unsigned int i; - word32 u = 0; -#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - for (i=0; i<2048; i+=cacheLineSize) -#else - for (i=0; i<1024; i+=cacheLineSize) -#endif - u &= *(const word32 *)(((const byte *)Te)+i); - u &= Te[255]; - s0 |= u; s1 |= u; s2 |= u; s3 |= u; - - QUARTER_ROUND_FE(s3, t0, t1, t2, t3) - QUARTER_ROUND_FE(s2, t3, t0, t1, t2) - QUARTER_ROUND_FE(s1, t2, t3, t0, t1) - QUARTER_ROUND_FE(s0, t1, t2, t3, t0) - - // Nr - 2 full rounds: - unsigned int r = m_rounds/2 - 1; - do - { - s0 = rk[0]; s1 = rk[1]; s2 = rk[2]; s3 = rk[3]; - - QUARTER_ROUND_E(t3, s0, s1, s2, s3) - QUARTER_ROUND_E(t2, s3, s0, s1, s2) - QUARTER_ROUND_E(t1, s2, s3, s0, s1) - QUARTER_ROUND_E(t0, s1, s2, s3, s0) - - t0 = rk[4]; t1 = rk[5]; t2 = rk[6]; t3 = rk[7]; - - QUARTER_ROUND_E(s3, t0, t1, t2, t3) - QUARTER_ROUND_E(s2, t3, t0, t1, t2) - QUARTER_ROUND_E(s1, t2, t3, t0, t1) - QUARTER_ROUND_E(s0, t1, t2, t3, t0) - - rk += 8; - } while (--r); - - word32 tbw[4]; - byte *const tempBlock = (byte *)tbw; - - QUARTER_ROUND_LE(t2, 15, 2, 5, 8) - QUARTER_ROUND_LE(t1, 11, 14, 1, 4) - QUARTER_ROUND_LE(t0, 7, 10, 13, 0) - QUARTER_ROUND_LE(t3, 3, 6, 9, 12) - - Block::Put(xorBlock, outBlock)(tbw[0]^rk[0])(tbw[1]^rk[1])(tbw[2]^rk[2])(tbw[3]^rk[3]); -} - -void Rijndael::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - if (HasAESNI()) - { - Rijndael::Dec::AdvancedProcessBlocks(inBlock, xorBlock, outBlock, 16, 0); - return; - } -#endif - - typedef BlockGetAndPut Block; - - word32 s0, s1, s2, s3, t0, t1, t2, t3; - Block::Get(inBlock)(s0)(s1)(s2)(s3); - - const word32 *rk = m_key; - s0 ^= rk[0]; - s1 ^= rk[1]; - s2 ^= rk[2]; - s3 ^= rk[3]; - t0 = rk[4]; - t1 = rk[5]; - t2 = rk[6]; - t3 = rk[7]; - rk += 8; - - // timing attack countermeasure. see comments at top for more details - const int cacheLineSize = GetCacheLineSize(); - unsigned int i; - word32 u = 0; -#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - for (i=0; i<2048; i+=cacheLineSize) -#else - for (i=0; i<1024; i+=cacheLineSize) -#endif - u &= *(const word32 *)(((const byte *)Td)+i); - u &= Td[255]; - s0 |= u; s1 |= u; s2 |= u; s3 |= u; - - QUARTER_ROUND_FD(s3, t2, t1, t0, t3) - QUARTER_ROUND_FD(s2, t1, t0, t3, t2) - QUARTER_ROUND_FD(s1, t0, t3, t2, t1) - QUARTER_ROUND_FD(s0, t3, t2, t1, t0) - - // Nr - 2 full rounds: - unsigned int r = m_rounds/2 - 1; - do - { - s0 = rk[0]; s1 = rk[1]; s2 = rk[2]; s3 = rk[3]; - - QUARTER_ROUND_D(t3, s2, s1, s0, s3) - QUARTER_ROUND_D(t2, s1, s0, s3, s2) - QUARTER_ROUND_D(t1, s0, s3, s2, s1) - QUARTER_ROUND_D(t0, s3, s2, s1, s0) - - t0 = rk[4]; t1 = rk[5]; t2 = rk[6]; t3 = rk[7]; - - QUARTER_ROUND_D(s3, t2, t1, t0, t3) - QUARTER_ROUND_D(s2, t1, t0, t3, t2) - QUARTER_ROUND_D(s1, t0, t3, t2, t1) - QUARTER_ROUND_D(s0, t3, t2, t1, t0) - - rk += 8; - } while (--r); - -#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - // timing attack countermeasure. see comments at top for more details - // If CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS is defined, - // QUARTER_ROUND_LD will use Td, which is already preloaded. - u = 0; - for (i=0; i<256; i+=cacheLineSize) - u &= *(const word32 *)(Sd+i); - u &= *(const word32 *)(Sd+252); - t0 |= u; t1 |= u; t2 |= u; t3 |= u; -#endif - - word32 tbw[4]; - byte *const tempBlock = (byte *)tbw; - - QUARTER_ROUND_LD(t2, 7, 2, 13, 8) - QUARTER_ROUND_LD(t1, 3, 14, 9, 4) - QUARTER_ROUND_LD(t0, 15, 10, 5, 0) - QUARTER_ROUND_LD(t3, 11, 6, 1, 12) - - Block::Put(xorBlock, outBlock)(tbw[0]^rk[0])(tbw[1]^rk[1])(tbw[2]^rk[2])(tbw[3]^rk[3]); -} - -// ************************* Assembly Code ************************************ - -#pragma warning(disable: 4731) // frame pointer register 'ebp' modified by inline assembly code - -#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - -CRYPTOPP_NAKED void CRYPTOPP_FASTCALL Rijndael_Enc_AdvancedProcessBlocks(void *locals, const word32 *k) -{ -#if CRYPTOPP_BOOL_X86 - -#define L_REG esp -#define L_INDEX(i) (L_REG+768+i) -#define L_INXORBLOCKS L_INBLOCKS+4 -#define L_OUTXORBLOCKS L_INBLOCKS+8 -#define L_OUTBLOCKS L_INBLOCKS+12 -#define L_INCREMENTS L_INDEX(16*15) -#define L_SP L_INDEX(16*16) -#define L_LENGTH L_INDEX(16*16+4) -#define L_KEYS_BEGIN L_INDEX(16*16+8) - -#define MOVD movd -#define MM(i) mm##i - -#define MXOR(a,b,c) \ - AS2( movzx esi, b)\ - AS2( movd mm7, DWORD PTR [AS_REG_7+8*WORD_REG(si)+MAP0TO4(c)])\ - AS2( pxor MM(a), mm7)\ - -#define MMOV(a,b,c) \ - AS2( movzx esi, b)\ - AS2( movd MM(a), DWORD PTR [AS_REG_7+8*WORD_REG(si)+MAP0TO4(c)])\ - -#else - -#define L_REG r8 -#define L_INDEX(i) (L_REG+i) -#define L_INXORBLOCKS L_INBLOCKS+8 -#define L_OUTXORBLOCKS L_INBLOCKS+16 -#define L_OUTBLOCKS L_INBLOCKS+24 -#define L_INCREMENTS L_INDEX(16*16) -#define L_LENGTH L_INDEX(16*18+8) -#define L_KEYS_BEGIN L_INDEX(16*19) - -#define MOVD mov -#define MM_0 r9d -#define MM_1 r12d -#ifdef __GNUC__ -#define MM_2 r11d -#else -#define MM_2 r10d -#endif -#define MM(i) MM_##i - -#define MXOR(a,b,c) \ - AS2( movzx esi, b)\ - AS2( xor MM(a), DWORD PTR [AS_REG_7+8*WORD_REG(si)+MAP0TO4(c)])\ - -#define MMOV(a,b,c) \ - AS2( movzx esi, b)\ - AS2( mov MM(a), DWORD PTR [AS_REG_7+8*WORD_REG(si)+MAP0TO4(c)])\ - -#endif - -#define L_SUBKEYS L_INDEX(0) -#define L_SAVED_X L_SUBKEYS -#define L_KEY12 L_INDEX(16*12) -#define L_LASTROUND L_INDEX(16*13) -#define L_INBLOCKS L_INDEX(16*14) -#define MAP0TO4(i) (ASM_MOD(i+3,4)+1) - -#define XOR(a,b,c) \ - AS2( movzx esi, b)\ - AS2( xor a, DWORD PTR [AS_REG_7+8*WORD_REG(si)+MAP0TO4(c)])\ - -#define MOV(a,b,c) \ - AS2( movzx esi, b)\ - AS2( mov a, DWORD PTR [AS_REG_7+8*WORD_REG(si)+MAP0TO4(c)])\ - -#ifdef CRYPTOPP_GENERATE_X64_MASM - ALIGN 8 - Rijndael_Enc_AdvancedProcessBlocks PROC FRAME - rex_push_reg rsi - push_reg rdi - push_reg rbx - push_reg r12 - .endprolog - mov L_REG, rcx - mov AS_REG_7, ?Te@rdtable@CryptoPP@@3PA_KA - mov edi, DWORD PTR [?g_cacheLineSize@CryptoPP@@3IA] -#elif defined(__GNUC__) - __asm__ __volatile__ - ( - ".intel_syntax noprefix;" - #if CRYPTOPP_BOOL_X64 - AS2( mov L_REG, rcx) - #endif - AS_PUSH_IF86(bx) - AS_PUSH_IF86(bp) - AS2( mov AS_REG_7, WORD_REG(si)) -#else - AS_PUSH_IF86(si) - AS_PUSH_IF86(di) - AS_PUSH_IF86(bx) - AS_PUSH_IF86(bp) - AS2( lea AS_REG_7, [Te]) - AS2( mov edi, [g_cacheLineSize]) -#endif - -#if CRYPTOPP_BOOL_X86 - AS2( mov [ecx+16*12+16*4], esp) // save esp to L_SP - AS2( lea esp, [ecx-768]) -#endif - - // copy subkeys to stack - AS2( mov WORD_REG(si), [L_KEYS_BEGIN]) - AS2( mov WORD_REG(ax), 16) - AS2( and WORD_REG(ax), WORD_REG(si)) - AS2( movdqa xmm3, XMMWORD_PTR [WORD_REG(dx)+16+WORD_REG(ax)]) // subkey 1 (non-counter) or 2 (counter) - AS2( movdqa [L_KEY12], xmm3) - AS2( lea WORD_REG(ax), [WORD_REG(dx)+WORD_REG(ax)+2*16]) - AS2( sub WORD_REG(ax), WORD_REG(si)) - ASL(0) - AS2( movdqa xmm0, [WORD_REG(ax)+WORD_REG(si)]) - AS2( movdqa XMMWORD_PTR [L_SUBKEYS+WORD_REG(si)], xmm0) - AS2( add WORD_REG(si), 16) - AS2( cmp WORD_REG(si), 16*12) - ASJ( jl, 0, b) - - // read subkeys 0, 1 and last - AS2( movdqa xmm4, [WORD_REG(ax)+WORD_REG(si)]) // last subkey - AS2( movdqa xmm1, [WORD_REG(dx)]) // subkey 0 - AS2( MOVD MM(1), [WORD_REG(dx)+4*4]) // 0,1,2,3 - AS2( mov ebx, [WORD_REG(dx)+5*4]) // 4,5,6,7 - AS2( mov ecx, [WORD_REG(dx)+6*4]) // 8,9,10,11 - AS2( mov edx, [WORD_REG(dx)+7*4]) // 12,13,14,15 - - // load table into cache - AS2( xor WORD_REG(ax), WORD_REG(ax)) - ASL(9) - AS2( mov esi, [AS_REG_7+WORD_REG(ax)]) - AS2( add WORD_REG(ax), WORD_REG(di)) - AS2( mov esi, [AS_REG_7+WORD_REG(ax)]) - AS2( add WORD_REG(ax), WORD_REG(di)) - AS2( mov esi, [AS_REG_7+WORD_REG(ax)]) - AS2( add WORD_REG(ax), WORD_REG(di)) - AS2( mov esi, [AS_REG_7+WORD_REG(ax)]) - AS2( add WORD_REG(ax), WORD_REG(di)) - AS2( cmp WORD_REG(ax), 2048) - ASJ( jl, 9, b) - AS1( lfence) - - AS2( test DWORD PTR [L_LENGTH], 1) - ASJ( jz, 8, f) - - // counter mode one-time setup - AS2( mov WORD_REG(si), [L_INBLOCKS]) - AS2( movdqu xmm2, [WORD_REG(si)]) // counter - AS2( pxor xmm2, xmm1) - AS2( psrldq xmm1, 14) - AS2( movd eax, xmm1) - AS2( mov al, BYTE PTR [WORD_REG(si)+15]) - AS2( MOVD MM(2), eax) -#if CRYPTOPP_BOOL_X86 - AS2( mov eax, 1) - AS2( movd mm3, eax) -#endif - - // partial first round, in: xmm2(15,14,13,12;11,10,9,8;7,6,5,4;3,2,1,0), out: mm1, ebx, ecx, edx - AS2( movd eax, xmm2) - AS2( psrldq xmm2, 4) - AS2( movd edi, xmm2) - AS2( psrldq xmm2, 4) - MXOR( 1, al, 0) // 0 - XOR( edx, ah, 1) // 1 - AS2( shr eax, 16) - XOR( ecx, al, 2) // 2 - XOR( ebx, ah, 3) // 3 - AS2( mov eax, edi) - AS2( movd edi, xmm2) - AS2( psrldq xmm2, 4) - XOR( ebx, al, 0) // 4 - MXOR( 1, ah, 1) // 5 - AS2( shr eax, 16) - XOR( edx, al, 2) // 6 - XOR( ecx, ah, 3) // 7 - AS2( mov eax, edi) - AS2( movd edi, xmm2) - XOR( ecx, al, 0) // 8 - XOR( ebx, ah, 1) // 9 - AS2( shr eax, 16) - MXOR( 1, al, 2) // 10 - XOR( edx, ah, 3) // 11 - AS2( mov eax, edi) - XOR( edx, al, 0) // 12 - XOR( ecx, ah, 1) // 13 - AS2( shr eax, 16) - XOR( ebx, al, 2) // 14 - AS2( psrldq xmm2, 3) - - // partial second round, in: ebx(4,5,6,7), ecx(8,9,10,11), edx(12,13,14,15), out: eax, ebx, edi, mm0 - AS2( mov eax, [L_KEY12+0*4]) - AS2( mov edi, [L_KEY12+2*4]) - AS2( MOVD MM(0), [L_KEY12+3*4]) - MXOR( 0, cl, 3) /* 11 */ - XOR( edi, bl, 3) /* 7 */ - MXOR( 0, bh, 2) /* 6 */ - AS2( shr ebx, 16) /* 4,5 */ - XOR( eax, bl, 1) /* 5 */ - MOV( ebx, bh, 0) /* 4 */ - AS2( xor ebx, [L_KEY12+1*4]) - XOR( eax, ch, 2) /* 10 */ - AS2( shr ecx, 16) /* 8,9 */ - XOR( eax, dl, 3) /* 15 */ - XOR( ebx, dh, 2) /* 14 */ - AS2( shr edx, 16) /* 12,13 */ - XOR( edi, ch, 0) /* 8 */ - XOR( ebx, cl, 1) /* 9 */ - XOR( edi, dl, 1) /* 13 */ - MXOR( 0, dh, 0) /* 12 */ - - AS2( movd ecx, xmm2) - AS2( MOVD edx, MM(1)) - AS2( MOVD [L_SAVED_X+3*4], MM(0)) - AS2( mov [L_SAVED_X+0*4], eax) - AS2( mov [L_SAVED_X+1*4], ebx) - AS2( mov [L_SAVED_X+2*4], edi) - ASJ( jmp, 5, f) - - ASL(3) - // non-counter mode per-block setup - AS2( MOVD MM(1), [L_KEY12+0*4]) // 0,1,2,3 - AS2( mov ebx, [L_KEY12+1*4]) // 4,5,6,7 - AS2( mov ecx, [L_KEY12+2*4]) // 8,9,10,11 - AS2( mov edx, [L_KEY12+3*4]) // 12,13,14,15 - ASL(8) - AS2( mov WORD_REG(ax), [L_INBLOCKS]) - AS2( movdqu xmm2, [WORD_REG(ax)]) - AS2( mov WORD_REG(si), [L_INXORBLOCKS]) - AS2( movdqu xmm5, [WORD_REG(si)]) - AS2( pxor xmm2, xmm1) - AS2( pxor xmm2, xmm5) - - // first round, in: xmm2(15,14,13,12;11,10,9,8;7,6,5,4;3,2,1,0), out: eax, ebx, ecx, edx - AS2( movd eax, xmm2) - AS2( psrldq xmm2, 4) - AS2( movd edi, xmm2) - AS2( psrldq xmm2, 4) - MXOR( 1, al, 0) // 0 - XOR( edx, ah, 1) // 1 - AS2( shr eax, 16) - XOR( ecx, al, 2) // 2 - XOR( ebx, ah, 3) // 3 - AS2( mov eax, edi) - AS2( movd edi, xmm2) - AS2( psrldq xmm2, 4) - XOR( ebx, al, 0) // 4 - MXOR( 1, ah, 1) // 5 - AS2( shr eax, 16) - XOR( edx, al, 2) // 6 - XOR( ecx, ah, 3) // 7 - AS2( mov eax, edi) - AS2( movd edi, xmm2) - XOR( ecx, al, 0) // 8 - XOR( ebx, ah, 1) // 9 - AS2( shr eax, 16) - MXOR( 1, al, 2) // 10 - XOR( edx, ah, 3) // 11 - AS2( mov eax, edi) - XOR( edx, al, 0) // 12 - XOR( ecx, ah, 1) // 13 - AS2( shr eax, 16) - XOR( ebx, al, 2) // 14 - MXOR( 1, ah, 3) // 15 - AS2( MOVD eax, MM(1)) - - AS2( add L_REG, [L_KEYS_BEGIN]) - AS2( add L_REG, 4*16) - ASJ( jmp, 2, f) - - ASL(1) - // counter-mode per-block setup - AS2( MOVD ecx, MM(2)) - AS2( MOVD edx, MM(1)) - AS2( mov eax, [L_SAVED_X+0*4]) - AS2( mov ebx, [L_SAVED_X+1*4]) - AS2( xor cl, ch) - AS2( and WORD_REG(cx), 255) - ASL(5) -#if CRYPTOPP_BOOL_X86 - AS2( paddb MM(2), mm3) -#else - AS2( add MM(2), 1) -#endif - // remaining part of second round, in: edx(previous round),esi(keyed counter byte) eax,ebx,[L_SAVED_X+2*4],[L_SAVED_X+3*4], out: eax,ebx,ecx,edx - AS2( xor edx, DWORD PTR [AS_REG_7+WORD_REG(cx)*8+3]) - XOR( ebx, dl, 3) - MOV( ecx, dh, 2) - AS2( shr edx, 16) - AS2( xor ecx, [L_SAVED_X+2*4]) - XOR( eax, dh, 0) - MOV( edx, dl, 1) - AS2( xor edx, [L_SAVED_X+3*4]) - - AS2( add L_REG, [L_KEYS_BEGIN]) - AS2( add L_REG, 3*16) - ASJ( jmp, 4, f) - -// in: eax(0,1,2,3), ebx(4,5,6,7), ecx(8,9,10,11), edx(12,13,14,15) -// out: eax, ebx, edi, mm0 -#define ROUND() \ - MXOR( 0, cl, 3) /* 11 */\ - AS2( mov cl, al) /* 8,9,10,3 */\ - XOR( edi, ah, 2) /* 2 */\ - AS2( shr eax, 16) /* 0,1 */\ - XOR( edi, bl, 3) /* 7 */\ - MXOR( 0, bh, 2) /* 6 */\ - AS2( shr ebx, 16) /* 4,5 */\ - MXOR( 0, al, 1) /* 1 */\ - MOV( eax, ah, 0) /* 0 */\ - XOR( eax, bl, 1) /* 5 */\ - MOV( ebx, bh, 0) /* 4 */\ - XOR( eax, ch, 2) /* 10 */\ - XOR( ebx, cl, 3) /* 3 */\ - AS2( shr ecx, 16) /* 8,9 */\ - XOR( eax, dl, 3) /* 15 */\ - XOR( ebx, dh, 2) /* 14 */\ - AS2( shr edx, 16) /* 12,13 */\ - XOR( edi, ch, 0) /* 8 */\ - XOR( ebx, cl, 1) /* 9 */\ - XOR( edi, dl, 1) /* 13 */\ - MXOR( 0, dh, 0) /* 12 */\ - - ASL(2) // 2-round loop - AS2( MOVD MM(0), [L_SUBKEYS-4*16+3*4]) - AS2( mov edi, [L_SUBKEYS-4*16+2*4]) - ROUND() - AS2( mov ecx, edi) - AS2( xor eax, [L_SUBKEYS-4*16+0*4]) - AS2( xor ebx, [L_SUBKEYS-4*16+1*4]) - AS2( MOVD edx, MM(0)) - - ASL(4) - AS2( MOVD MM(0), [L_SUBKEYS-4*16+7*4]) - AS2( mov edi, [L_SUBKEYS-4*16+6*4]) - ROUND() - AS2( mov ecx, edi) - AS2( xor eax, [L_SUBKEYS-4*16+4*4]) - AS2( xor ebx, [L_SUBKEYS-4*16+5*4]) - AS2( MOVD edx, MM(0)) - - AS2( add L_REG, 32) - AS2( test L_REG, 255) - ASJ( jnz, 2, b) - AS2( sub L_REG, 16*16) - -#define LAST(a, b, c) \ - AS2( movzx esi, a )\ - AS2( movzx edi, BYTE PTR [AS_REG_7+WORD_REG(si)*8+1] )\ - AS2( movzx esi, b )\ - AS2( xor edi, DWORD PTR [AS_REG_7+WORD_REG(si)*8+0] )\ - AS2( mov WORD PTR [L_LASTROUND+c], di )\ - - // last round - LAST(ch, dl, 2) - LAST(dh, al, 6) - AS2( shr edx, 16) - LAST(ah, bl, 10) - AS2( shr eax, 16) - LAST(bh, cl, 14) - AS2( shr ebx, 16) - LAST(dh, al, 12) - AS2( shr ecx, 16) - LAST(ah, bl, 0) - LAST(bh, cl, 4) - LAST(ch, dl, 8) - - AS2( mov WORD_REG(ax), [L_OUTXORBLOCKS]) - AS2( mov WORD_REG(bx), [L_OUTBLOCKS]) - - AS2( mov WORD_REG(cx), [L_LENGTH]) - AS2( sub WORD_REG(cx), 16) - - AS2( movdqu xmm2, [WORD_REG(ax)]) - AS2( pxor xmm2, xmm4) - -#if CRYPTOPP_BOOL_X86 - AS2( movdqa xmm0, [L_INCREMENTS]) - AS2( paddd xmm0, [L_INBLOCKS]) - AS2( movdqa [L_INBLOCKS], xmm0) -#else - AS2( movdqa xmm0, [L_INCREMENTS+16]) - AS2( paddq xmm0, [L_INBLOCKS+16]) - AS2( movdqa [L_INBLOCKS+16], xmm0) -#endif - - AS2( pxor xmm2, [L_LASTROUND]) - AS2( movdqu [WORD_REG(bx)], xmm2) - - ASJ( jle, 7, f) - AS2( mov [L_LENGTH], WORD_REG(cx)) - AS2( test WORD_REG(cx), 1) - ASJ( jnz, 1, b) -#if CRYPTOPP_BOOL_X64 - AS2( movdqa xmm0, [L_INCREMENTS]) - AS2( paddq xmm0, [L_INBLOCKS]) - AS2( movdqa [L_INBLOCKS], xmm0) -#endif - ASJ( jmp, 3, b) - - ASL(7) - // erase keys on stack - AS2( xorps xmm0, xmm0) - AS2( lea WORD_REG(ax), [L_SUBKEYS+7*16]) - AS2( movaps [WORD_REG(ax)-7*16], xmm0) - AS2( movaps [WORD_REG(ax)-6*16], xmm0) - AS2( movaps [WORD_REG(ax)-5*16], xmm0) - AS2( movaps [WORD_REG(ax)-4*16], xmm0) - AS2( movaps [WORD_REG(ax)-3*16], xmm0) - AS2( movaps [WORD_REG(ax)-2*16], xmm0) - AS2( movaps [WORD_REG(ax)-1*16], xmm0) - AS2( movaps [WORD_REG(ax)+0*16], xmm0) - AS2( movaps [WORD_REG(ax)+1*16], xmm0) - AS2( movaps [WORD_REG(ax)+2*16], xmm0) - AS2( movaps [WORD_REG(ax)+3*16], xmm0) - AS2( movaps [WORD_REG(ax)+4*16], xmm0) - AS2( movaps [WORD_REG(ax)+5*16], xmm0) - AS2( movaps [WORD_REG(ax)+6*16], xmm0) -#if CRYPTOPP_BOOL_X86 - AS2( mov esp, [L_SP]) - AS1( emms) -#endif - AS_POP_IF86(bp) - AS_POP_IF86(bx) -#if defined(_MSC_VER) && CRYPTOPP_BOOL_X86 - AS_POP_IF86(di) - AS_POP_IF86(si) - AS1(ret) -#endif -#ifdef CRYPTOPP_GENERATE_X64_MASM - pop r12 - pop rbx - pop rdi - pop rsi - ret - Rijndael_Enc_AdvancedProcessBlocks ENDP -#endif -#ifdef __GNUC__ - ".att_syntax prefix;" - : - : "c" (locals), "d" (k), "S" (Te), "D" (g_cacheLineSize) - : "memory", "cc", "%eax" - #if CRYPTOPP_BOOL_X64 - , "%rbx", "%r8", "%r9", "%r10", "%r11", "%r12" - #endif - ); -#endif -} - -#endif - -#ifndef CRYPTOPP_GENERATE_X64_MASM - -#ifdef CRYPTOPP_X64_MASM_AVAILABLE -extern "C" { -void Rijndael_Enc_AdvancedProcessBlocks(void *locals, const word32 *k); -} -#endif - -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86 - -static inline bool AliasedWithTable(const byte *begin, const byte *end) -{ - size_t s0 = size_t(begin)%4096, s1 = size_t(end)%4096; - size_t t0 = size_t(Te)%4096, t1 = (size_t(Te)+sizeof(Te))%4096; - if (t1 > t0) - return (s0 >= t0 && s0 < t1) || (s1 > t0 && s1 <= t1); - else - return (s0 < t1 || s1 <= t1) || (s0 >= t0 || s1 > t0); -} - -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - -inline void AESNI_Enc_Block(__m128i &block, const __m128i *subkeys, unsigned int rounds) -{ - block = _mm_xor_si128(block, subkeys[0]); - for (unsigned int i=1; i -inline size_t AESNI_AdvancedProcessBlocks(F1 func1, F4 func4, const __m128i *subkeys, unsigned int rounds, const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) -{ - size_t blockSize = 16; - size_t inIncrement = (flags & (BlockTransformation::BT_InBlockIsCounter|BlockTransformation::BT_DontIncrementInOutPointers)) ? 0 : blockSize; - size_t xorIncrement = xorBlocks ? blockSize : 0; - size_t outIncrement = (flags & BlockTransformation::BT_DontIncrementInOutPointers) ? 0 : blockSize; - - if (flags & BlockTransformation::BT_ReverseDirection) - { - assert(length % blockSize == 0); - inBlocks += length - blockSize; - xorBlocks += length - blockSize; - outBlocks += length - blockSize; - inIncrement = 0-inIncrement; - xorIncrement = 0-xorIncrement; - outIncrement = 0-outIncrement; - } - - if (flags & BlockTransformation::BT_AllowParallel) - { - while (length >= 4*blockSize) - { - __m128i block0 = _mm_loadu_si128((const __m128i *)inBlocks), block1, block2, block3; - if (flags & BlockTransformation::BT_InBlockIsCounter) - { - const __m128i be1 = *(const __m128i *)s_one; - block1 = _mm_add_epi32(block0, be1); - block2 = _mm_add_epi32(block1, be1); - block3 = _mm_add_epi32(block2, be1); - _mm_storeu_si128((__m128i *)inBlocks, _mm_add_epi32(block3, be1)); - } - else - { - inBlocks += inIncrement; - block1 = _mm_loadu_si128((const __m128i *)inBlocks); - inBlocks += inIncrement; - block2 = _mm_loadu_si128((const __m128i *)inBlocks); - inBlocks += inIncrement; - block3 = _mm_loadu_si128((const __m128i *)inBlocks); - inBlocks += inIncrement; - } - - if (flags & BlockTransformation::BT_XorInput) - { - block0 = _mm_xor_si128(block0, _mm_loadu_si128((const __m128i *)xorBlocks)); - xorBlocks += xorIncrement; - block1 = _mm_xor_si128(block1, _mm_loadu_si128((const __m128i *)xorBlocks)); - xorBlocks += xorIncrement; - block2 = _mm_xor_si128(block2, _mm_loadu_si128((const __m128i *)xorBlocks)); - xorBlocks += xorIncrement; - block3 = _mm_xor_si128(block3, _mm_loadu_si128((const __m128i *)xorBlocks)); - xorBlocks += xorIncrement; - } - - func4(block0, block1, block2, block3, subkeys, rounds); - - if (xorBlocks && !(flags & BlockTransformation::BT_XorInput)) - { - block0 = _mm_xor_si128(block0, _mm_loadu_si128((const __m128i *)xorBlocks)); - xorBlocks += xorIncrement; - block1 = _mm_xor_si128(block1, _mm_loadu_si128((const __m128i *)xorBlocks)); - xorBlocks += xorIncrement; - block2 = _mm_xor_si128(block2, _mm_loadu_si128((const __m128i *)xorBlocks)); - xorBlocks += xorIncrement; - block3 = _mm_xor_si128(block3, _mm_loadu_si128((const __m128i *)xorBlocks)); - xorBlocks += xorIncrement; - } - - _mm_storeu_si128((__m128i *)outBlocks, block0); - outBlocks += outIncrement; - _mm_storeu_si128((__m128i *)outBlocks, block1); - outBlocks += outIncrement; - _mm_storeu_si128((__m128i *)outBlocks, block2); - outBlocks += outIncrement; - _mm_storeu_si128((__m128i *)outBlocks, block3); - outBlocks += outIncrement; - - length -= 4*blockSize; - } - } - - while (length >= blockSize) - { - __m128i block = _mm_loadu_si128((const __m128i *)inBlocks); - - if (flags & BlockTransformation::BT_XorInput) - block = _mm_xor_si128(block, _mm_loadu_si128((const __m128i *)xorBlocks)); - - if (flags & BlockTransformation::BT_InBlockIsCounter) - const_cast(inBlocks)[15]++; - - func1(block, subkeys, rounds); - - if (xorBlocks && !(flags & BlockTransformation::BT_XorInput)) - block = _mm_xor_si128(block, _mm_loadu_si128((const __m128i *)xorBlocks)); - - _mm_storeu_si128((__m128i *)outBlocks, block); - - inBlocks += inIncrement; - outBlocks += outIncrement; - xorBlocks += xorIncrement; - length -= blockSize; - } - - return length; -} -#endif - -size_t Rijndael::Enc::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const -{ -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - if (HasAESNI()) - return AESNI_AdvancedProcessBlocks(AESNI_Enc_Block, AESNI_Enc_4_Blocks, (const __m128i *)m_key.begin(), m_rounds, inBlocks, xorBlocks, outBlocks, length, flags); -#endif - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) - if (HasSSE2()) - { - if (length < BLOCKSIZE) - return length; - - struct Locals - { - word32 subkeys[4*12], workspace[8]; - const byte *inBlocks, *inXorBlocks, *outXorBlocks; - byte *outBlocks; - size_t inIncrement, inXorIncrement, outXorIncrement, outIncrement; - size_t regSpill, lengthAndCounterFlag, keysBegin; - }; - - size_t increment = BLOCKSIZE; - const byte* zeros = (byte *)(Te+256); - byte *space; - - do { - space = (byte *)alloca(255+sizeof(Locals)); - space += (256-(size_t)space%256)%256; - } - while (AliasedWithTable(space, space+sizeof(Locals))); - - if (flags & BT_ReverseDirection) - { - assert(length % BLOCKSIZE == 0); - inBlocks += length - BLOCKSIZE; - xorBlocks += length - BLOCKSIZE; - outBlocks += length - BLOCKSIZE; - increment = 0-increment; - } - - Locals &locals = *(Locals *)space; - - locals.inBlocks = inBlocks; - locals.inXorBlocks = (flags & BT_XorInput) && xorBlocks ? xorBlocks : zeros; - locals.outXorBlocks = (flags & BT_XorInput) || !xorBlocks ? zeros : xorBlocks; - locals.outBlocks = outBlocks; - - locals.inIncrement = (flags & BT_DontIncrementInOutPointers) ? 0 : increment; - locals.inXorIncrement = (flags & BT_XorInput) && xorBlocks ? increment : 0; - locals.outXorIncrement = (flags & BT_XorInput) || !xorBlocks ? 0 : increment; - locals.outIncrement = (flags & BT_DontIncrementInOutPointers) ? 0 : increment; - - locals.lengthAndCounterFlag = length - (length%16) - bool(flags & BT_InBlockIsCounter); - int keysToCopy = m_rounds - (flags & BT_InBlockIsCounter ? 3 : 2); - locals.keysBegin = (12-keysToCopy)*16; - - Rijndael_Enc_AdvancedProcessBlocks(&locals, m_key); - return length % BLOCKSIZE; - } -#endif - - return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags); -} - -#endif - -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - -size_t Rijndael::Dec::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const -{ - if (HasAESNI()) - return AESNI_AdvancedProcessBlocks(AESNI_Dec_Block, AESNI_Dec_4_Blocks, (const __m128i *)m_key.begin(), m_rounds, inBlocks, xorBlocks, outBlocks, length, flags); - - return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags); -} - -#endif // #if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - -NAMESPACE_END - -#endif -#endif diff --git a/CryptoPP/rijndael.h b/CryptoPP/rijndael.h deleted file mode 100644 index 64c784b07..000000000 --- a/CryptoPP/rijndael.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef CRYPTOPP_RIJNDAEL_H -#define CRYPTOPP_RIJNDAEL_H - -/** \file -*/ - -#include "seckey.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -struct Rijndael_Info : public FixedBlockSize<16>, public VariableKeyLength<16, 16, 32, 8> -{ - CRYPTOPP_DLL static const char * CRYPTOPP_API StaticAlgorithmName() {return CRYPTOPP_RIJNDAEL_NAME;} -}; - -/// Rijndael -class CRYPTOPP_DLL Rijndael : public Rijndael_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl - { - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms); - - protected: - static void FillEncTable(); - static void FillDecTable(); - - // VS2005 workaround: have to put these on seperate lines, or error C2487 is triggered in DLL build - static const byte Se[256]; - static const byte Sd[256]; - - static const word32 rcon[]; - - unsigned int m_rounds; - FixedSizeAlignedSecBlock m_key; - }; - - class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Enc : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86 - size_t AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const; -#endif - }; - - class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Dec : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; -#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE - size_t AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const; -#endif - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -typedef Rijndael::Encryption RijndaelEncryption; -typedef Rijndael::Decryption RijndaelDecryption; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/rng.cpp b/CryptoPP/rng.cpp deleted file mode 100644 index 9866cd831..000000000 --- a/CryptoPP/rng.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// rng.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#include "rng.h" -#include "fips140.h" - -#include -#include - -NAMESPACE_BEGIN(CryptoPP) - -// linear congruential generator -// originally by William S. England - -// do not use for cryptographic purposes - -/* -** Original_numbers are the original published m and q in the -** ACM article above. John Burton has furnished numbers for -** a reportedly better generator. The new numbers are now -** used in this program by default. -*/ - -#ifndef LCRNG_ORIGINAL_NUMBERS -const word32 LC_RNG::m=2147483647L; -const word32 LC_RNG::q=44488L; - -const word16 LC_RNG::a=(unsigned int)48271L; -const word16 LC_RNG::r=3399; -#else -const word32 LC_RNG::m=2147483647L; -const word32 LC_RNG::q=127773L; - -const word16 LC_RNG::a=16807; -const word16 LC_RNG::r=2836; -#endif - -void LC_RNG::GenerateBlock(byte *output, size_t size) -{ - while (size--) - { - word32 hi = seed/q; - word32 lo = seed%q; - - long test = a*lo - r*hi; - - if (test > 0) - seed = test; - else - seed = test+ m; - - *output++ = (GETBYTE(seed, 0) ^ GETBYTE(seed, 1) ^ GETBYTE(seed, 2) ^ GETBYTE(seed, 3)); - } -} - -// ******************************************************** - -#ifndef CRYPTOPP_IMPORTS - -X917RNG::X917RNG(BlockTransformation *c, const byte *seed, const byte *deterministicTimeVector) - : cipher(c), - S(cipher->BlockSize()), - dtbuf(S), - randseed(seed, S), - m_lastBlock(S), - m_deterministicTimeVector(deterministicTimeVector, deterministicTimeVector ? S : 0) -{ - if (!deterministicTimeVector) - { - time_t tstamp1 = time(0); - xorbuf(dtbuf, (byte *)&tstamp1, UnsignedMin(sizeof(tstamp1), S)); - cipher->ProcessBlock(dtbuf); - clock_t tstamp2 = clock(); - xorbuf(dtbuf, (byte *)&tstamp2, UnsignedMin(sizeof(tstamp2), S)); - cipher->ProcessBlock(dtbuf); - } - - // for FIPS 140-2 - GenerateBlock(m_lastBlock, S); -} - -void X917RNG::GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size) -{ - while (size > 0) - { - // calculate new enciphered timestamp - if (m_deterministicTimeVector.size()) - { - cipher->ProcessBlock(m_deterministicTimeVector, dtbuf); - IncrementCounterByOne(m_deterministicTimeVector, S); - } - else - { - clock_t c = clock(); - xorbuf(dtbuf, (byte *)&c, UnsignedMin(sizeof(c), S)); - time_t t = time(NULL); - xorbuf(dtbuf+S-UnsignedMin(sizeof(t), S), (byte *)&t, UnsignedMin(sizeof(t), S)); - cipher->ProcessBlock(dtbuf); - } - - // combine enciphered timestamp with seed - xorbuf(randseed, dtbuf, S); - - // generate a new block of random bytes - cipher->ProcessBlock(randseed); - if (memcmp(m_lastBlock, randseed, S) == 0) - throw SelfTestFailure("X917RNG: Continuous random number generator test failed."); - - // output random bytes - size_t len = UnsignedMin(S, size); - target.ChannelPut(channel, randseed, len); - size -= len; - - // compute new seed vector - memcpy(m_lastBlock, randseed, S); - xorbuf(randseed, dtbuf, S); - cipher->ProcessBlock(randseed); - } -} - -#endif - -MaurerRandomnessTest::MaurerRandomnessTest() - : sum(0.0), n(0) -{ - for (unsigned i=0; i= Q) - sum += log(double(n - tab[inByte])); - tab[inByte] = n; - n++; - } - return 0; -} - -double MaurerRandomnessTest::GetTestValue() const -{ - if (BytesNeeded() > 0) - throw Exception(Exception::OTHER_ERROR, "MaurerRandomnessTest: " + IntToString(BytesNeeded()) + " more bytes of input needed"); - - double fTu = (sum/(n-Q))/log(2.0); // this is the test value defined by Maurer - - double value = fTu * 0.1392; // arbitrarily normalize it to - return value > 1.0 ? 1.0 : value; // a number between 0 and 1 -} - -NAMESPACE_END diff --git a/CryptoPP/rng.h b/CryptoPP/rng.h deleted file mode 100644 index 2439dee69..000000000 --- a/CryptoPP/rng.h +++ /dev/null @@ -1,77 +0,0 @@ -// rng.h - misc RNG related classes, see also osrng.h, randpool.h - -#ifndef CRYPTOPP_RNG_H -#define CRYPTOPP_RNG_H - -#include "cryptlib.h" -#include "filters.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! linear congruential generator -/*! originally by William S. England, do not use for cryptographic purposes */ -class LC_RNG : public RandomNumberGenerator -{ -public: - LC_RNG(word32 init_seed) - : seed(init_seed) {} - - void GenerateBlock(byte *output, size_t size); - - word32 GetSeed() {return seed;} - -private: - word32 seed; - - static const word32 m; - static const word32 q; - static const word16 a; - static const word16 r; -}; - -//! RNG derived from ANSI X9.17 Appendix C - -class CRYPTOPP_DLL X917RNG : public RandomNumberGenerator, public NotCopyable -{ -public: - // cipher will be deleted by destructor, deterministicTimeVector = 0 means obtain time vector from system - X917RNG(BlockTransformation *cipher, const byte *seed, const byte *deterministicTimeVector = 0); - - void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size); - -private: - member_ptr cipher; - unsigned int S; // blocksize of cipher - SecByteBlock dtbuf; // buffer for enciphered timestamp - SecByteBlock randseed, m_lastBlock, m_deterministicTimeVector; -}; - -/** This class implements Maurer's Universal Statistical Test for Random Bit Generators - it is intended for measuring the randomness of *PHYSICAL* RNGs. - For more details see his paper in Journal of Cryptology, 1992. */ - -class MaurerRandomnessTest : public Bufferless -{ -public: - MaurerRandomnessTest(); - - size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking); - - // BytesNeeded() returns how many more bytes of input is needed by the test - // GetTestValue() should not be called before BytesNeeded()==0 - unsigned int BytesNeeded() const {return n >= (Q+K) ? 0 : Q+K-n;} - - // returns a number between 0.0 and 1.0, describing the quality of the - // random numbers entered - double GetTestValue() const; - -private: - enum {L=8, V=256, Q=2000, K=2000}; - double sum; - unsigned int n; - unsigned int tab[V]; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/rsa.cpp b/CryptoPP/rsa.cpp deleted file mode 100644 index 59449c40e..000000000 --- a/CryptoPP/rsa.cpp +++ /dev/null @@ -1,304 +0,0 @@ -// rsa.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "rsa.h" -#include "asn.h" -#include "oids.h" -#include "modarith.h" -#include "nbtheory.h" -#include "sha.h" -#include "algparam.h" -#include "fips140.h" - -#if !defined(NDEBUG) && !defined(CRYPTOPP_IS_DLL) -#include "pssr.h" -NAMESPACE_BEGIN(CryptoPP) -void RSA_TestInstantiations() -{ - RSASS::Verifier x1(1, 1); - RSASS::Signer x2(NullRNG(), 1); - RSASS::Verifier x3(x2); - RSASS::Verifier x4(x2.GetKey()); - RSASS::Verifier x5(x3); -#ifndef __MWERKS__ - RSASS::Signer x6 = x2; - x3 = x2; - x6 = x2; -#endif - RSAES::Encryptor x7(x2); -#ifndef __GNUC__ - RSAES::Encryptor x8(x3); -#endif - RSAES >::Encryptor x9(x2); - - x4 = x2.GetKey(); -} -NAMESPACE_END -#endif - -#ifndef CRYPTOPP_IMPORTS - -NAMESPACE_BEGIN(CryptoPP) - -OID RSAFunction::GetAlgorithmID() const -{ - return ASN1::rsaEncryption(); -} - -void RSAFunction::BERDecodePublicKey(BufferedTransformation &bt, bool, size_t) -{ - BERSequenceDecoder seq(bt); - m_n.BERDecode(seq); - m_e.BERDecode(seq); - seq.MessageEnd(); -} - -void RSAFunction::DEREncodePublicKey(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - m_n.DEREncode(seq); - m_e.DEREncode(seq); - seq.MessageEnd(); -} - -Integer RSAFunction::ApplyFunction(const Integer &x) const -{ - DoQuickSanityCheck(); - return a_exp_b_mod_c(x, m_e, m_n); -} - -bool RSAFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = true; - pass = pass && m_n > Integer::One() && m_n.IsOdd(); - pass = pass && m_e > Integer::One() && m_e.IsOdd() && m_e < m_n; - return pass; -} - -bool RSAFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Modulus) - CRYPTOPP_GET_FUNCTION_ENTRY(PublicExponent) - ; -} - -void RSAFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Modulus) - CRYPTOPP_SET_FUNCTION_ENTRY(PublicExponent) - ; -} - -// ***************************************************************************** - -class RSAPrimeSelector : public PrimeSelector -{ -public: - RSAPrimeSelector(const Integer &e) : m_e(e) {} - bool IsAcceptable(const Integer &candidate) const {return RelativelyPrime(m_e, candidate-Integer::One());} - Integer m_e; -}; - -void InvertibleRSAFunction::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg) -{ - int modulusSize = 2048; - alg.GetIntValue(Name::ModulusSize(), modulusSize) || alg.GetIntValue(Name::KeySize(), modulusSize); - - if (modulusSize < 16) - throw InvalidArgument("InvertibleRSAFunction: specified modulus size is too small"); - - m_e = alg.GetValueWithDefault(Name::PublicExponent(), Integer(17)); - - if (m_e < 3 || m_e.IsEven()) - throw InvalidArgument("InvertibleRSAFunction: invalid public exponent"); - - RSAPrimeSelector selector(m_e); - AlgorithmParameters primeParam = MakeParametersForTwoPrimesOfEqualSize(modulusSize) - (Name::PointerToPrimeSelector(), selector.GetSelectorPointer()); - m_p.GenerateRandom(rng, primeParam); - m_q.GenerateRandom(rng, primeParam); - - m_d = m_e.InverseMod(LCM(m_p-1, m_q-1)); - assert(m_d.IsPositive()); - - m_dp = m_d % (m_p-1); - m_dq = m_d % (m_q-1); - m_n = m_p * m_q; - m_u = m_q.InverseMod(m_p); - - if (FIPS_140_2_ComplianceEnabled()) - { - RSASS::Signer signer(*this); - RSASS::Verifier verifier(signer); - SignaturePairwiseConsistencyTest_FIPS_140_Only(signer, verifier); - - RSAES >::Decryptor decryptor(*this); - RSAES >::Encryptor encryptor(decryptor); - EncryptionPairwiseConsistencyTest_FIPS_140_Only(encryptor, decryptor); - } -} - -void InvertibleRSAFunction::Initialize(RandomNumberGenerator &rng, unsigned int keybits, const Integer &e) -{ - GenerateRandom(rng, MakeParameters(Name::ModulusSize(), (int)keybits)(Name::PublicExponent(), e+e.IsEven())); -} - -void InvertibleRSAFunction::Initialize(const Integer &n, const Integer &e, const Integer &d) -{ - if (n.IsEven() || e.IsEven() | d.IsEven()) - throw InvalidArgument("InvertibleRSAFunction: input is not a valid RSA private key"); - - m_n = n; - m_e = e; - m_d = d; - - Integer r = --(d*e); - unsigned int s = 0; - while (r.IsEven()) - { - r >>= 1; - s++; - } - - ModularArithmetic modn(n); - for (Integer i = 2; ; ++i) - { - Integer a = modn.Exponentiate(i, r); - if (a == 1) - continue; - Integer b; - unsigned int j = 0; - while (a != n-1) - { - b = modn.Square(a); - if (b == 1) - { - m_p = GCD(a-1, n); - m_q = n/m_p; - m_dp = m_d % (m_p-1); - m_dq = m_d % (m_q-1); - m_u = m_q.InverseMod(m_p); - return; - } - if (++j == s) - throw InvalidArgument("InvertibleRSAFunction: input is not a valid RSA private key"); - a = b; - } - } -} - -void InvertibleRSAFunction::BERDecodePrivateKey(BufferedTransformation &bt, bool, size_t) -{ - BERSequenceDecoder privateKey(bt); - word32 version; - BERDecodeUnsigned(privateKey, version, INTEGER, 0, 0); // check version - m_n.BERDecode(privateKey); - m_e.BERDecode(privateKey); - m_d.BERDecode(privateKey); - m_p.BERDecode(privateKey); - m_q.BERDecode(privateKey); - m_dp.BERDecode(privateKey); - m_dq.BERDecode(privateKey); - m_u.BERDecode(privateKey); - privateKey.MessageEnd(); -} - -void InvertibleRSAFunction::DEREncodePrivateKey(BufferedTransformation &bt) const -{ - DERSequenceEncoder privateKey(bt); - DEREncodeUnsigned(privateKey, 0); // version - m_n.DEREncode(privateKey); - m_e.DEREncode(privateKey); - m_d.DEREncode(privateKey); - m_p.DEREncode(privateKey); - m_q.DEREncode(privateKey); - m_dp.DEREncode(privateKey); - m_dq.DEREncode(privateKey); - m_u.DEREncode(privateKey); - privateKey.MessageEnd(); -} - -Integer InvertibleRSAFunction::CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const -{ - DoQuickSanityCheck(); - ModularArithmetic modn(m_n); - Integer r, rInv; - do { // do this in a loop for people using small numbers for testing - r.Randomize(rng, Integer::One(), m_n - Integer::One()); - rInv = modn.MultiplicativeInverse(r); - } while (rInv.IsZero()); - Integer re = modn.Exponentiate(r, m_e); - re = modn.Multiply(re, x); // blind - // here we follow the notation of PKCS #1 and let u=q inverse mod p - // but in ModRoot, u=p inverse mod q, so we reverse the order of p and q - Integer y = ModularRoot(re, m_dq, m_dp, m_q, m_p, m_u); - y = modn.Multiply(y, rInv); // unblind - if (modn.Exponentiate(y, m_e) != x) // check - throw Exception(Exception::OTHER_ERROR, "InvertibleRSAFunction: computational error during private key operation"); - return y; -} - -bool InvertibleRSAFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = RSAFunction::Validate(rng, level); - pass = pass && m_p > Integer::One() && m_p.IsOdd() && m_p < m_n; - pass = pass && m_q > Integer::One() && m_q.IsOdd() && m_q < m_n; - pass = pass && m_d > Integer::One() && m_d.IsOdd() && m_d < m_n; - pass = pass && m_dp > Integer::One() && m_dp.IsOdd() && m_dp < m_p; - pass = pass && m_dq > Integer::One() && m_dq.IsOdd() && m_dq < m_q; - pass = pass && m_u.IsPositive() && m_u < m_p; - if (level >= 1) - { - pass = pass && m_p * m_q == m_n; - pass = pass && m_e*m_d % LCM(m_p-1, m_q-1) == 1; - pass = pass && m_dp == m_d%(m_p-1) && m_dq == m_d%(m_q-1); - pass = pass && m_u * m_q % m_p == 1; - } - if (level >= 2) - pass = pass && VerifyPrime(rng, m_p, level-2) && VerifyPrime(rng, m_q, level-2); - return pass; -} - -bool InvertibleRSAFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_GET_FUNCTION_ENTRY(Prime2) - CRYPTOPP_GET_FUNCTION_ENTRY(PrivateExponent) - CRYPTOPP_GET_FUNCTION_ENTRY(ModPrime1PrivateExponent) - CRYPTOPP_GET_FUNCTION_ENTRY(ModPrime2PrivateExponent) - CRYPTOPP_GET_FUNCTION_ENTRY(MultiplicativeInverseOfPrime2ModPrime1) - ; -} - -void InvertibleRSAFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime2) - CRYPTOPP_SET_FUNCTION_ENTRY(PrivateExponent) - CRYPTOPP_SET_FUNCTION_ENTRY(ModPrime1PrivateExponent) - CRYPTOPP_SET_FUNCTION_ENTRY(ModPrime2PrivateExponent) - CRYPTOPP_SET_FUNCTION_ENTRY(MultiplicativeInverseOfPrime2ModPrime1) - ; -} - -// ***************************************************************************** - -Integer RSAFunction_ISO::ApplyFunction(const Integer &x) const -{ - Integer t = RSAFunction::ApplyFunction(x); - return t % 16 == 12 ? t : m_n - t; -} - -Integer InvertibleRSAFunction_ISO::CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const -{ - Integer t = InvertibleRSAFunction::CalculateInverse(rng, x); - return STDMIN(t, m_n-t); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/rsa.h b/CryptoPP/rsa.h deleted file mode 100644 index 6a8b18525..000000000 --- a/CryptoPP/rsa.h +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef CRYPTOPP_RSA_H -#define CRYPTOPP_RSA_H - -/** \file - This file contains classes that implement the RSA - ciphers and signature schemes as defined in PKCS #1 v2.0. -*/ - -#include "pubkey.h" -#include "asn.h" -#include "pkcspad.h" -#include "oaep.h" -#include "emsa2.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -class CRYPTOPP_DLL RSAFunction : public TrapdoorFunction, public X509PublicKey -{ - typedef RSAFunction ThisClass; - -public: - void Initialize(const Integer &n, const Integer &e) - {m_n = n; m_e = e;} - - // X509PublicKey - OID GetAlgorithmID() const; - void BERDecodePublicKey(BufferedTransformation &bt, bool parametersPresent, size_t size); - void DEREncodePublicKey(BufferedTransformation &bt) const; - - // CryptoMaterial - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - - // TrapdoorFunction - Integer ApplyFunction(const Integer &x) const; - Integer PreimageBound() const {return m_n;} - Integer ImageBound() const {return m_n;} - - // non-derived - const Integer & GetModulus() const {return m_n;} - const Integer & GetPublicExponent() const {return m_e;} - - void SetModulus(const Integer &n) {m_n = n;} - void SetPublicExponent(const Integer &e) {m_e = e;} - -protected: - Integer m_n, m_e; -}; - -//! _ -class CRYPTOPP_DLL InvertibleRSAFunction : public RSAFunction, public TrapdoorFunctionInverse, public PKCS8PrivateKey -{ - typedef InvertibleRSAFunction ThisClass; - -public: - void Initialize(RandomNumberGenerator &rng, unsigned int modulusBits, const Integer &e = 17); - void Initialize(const Integer &n, const Integer &e, const Integer &d, const Integer &p, const Integer &q, const Integer &dp, const Integer &dq, const Integer &u) - {m_n = n; m_e = e; m_d = d; m_p = p; m_q = q; m_dp = dp; m_dq = dq; m_u = u;} - //! factor n given private exponent - void Initialize(const Integer &n, const Integer &e, const Integer &d); - - // PKCS8PrivateKey - void BERDecode(BufferedTransformation &bt) - {PKCS8PrivateKey::BERDecode(bt);} - void DEREncode(BufferedTransformation &bt) const - {PKCS8PrivateKey::DEREncode(bt);} - void Load(BufferedTransformation &bt) - {PKCS8PrivateKey::BERDecode(bt);} - void Save(BufferedTransformation &bt) const - {PKCS8PrivateKey::DEREncode(bt);} - OID GetAlgorithmID() const {return RSAFunction::GetAlgorithmID();} - void BERDecodePrivateKey(BufferedTransformation &bt, bool parametersPresent, size_t size); - void DEREncodePrivateKey(BufferedTransformation &bt) const; - - // TrapdoorFunctionInverse - Integer CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const; - - // GeneratableCryptoMaterial - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - /*! parameters: (ModulusSize, PublicExponent (default 17)) */ - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg); - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - - // non-derived interface - const Integer& GetPrime1() const {return m_p;} - const Integer& GetPrime2() const {return m_q;} - const Integer& GetPrivateExponent() const {return m_d;} - const Integer& GetModPrime1PrivateExponent() const {return m_dp;} - const Integer& GetModPrime2PrivateExponent() const {return m_dq;} - const Integer& GetMultiplicativeInverseOfPrime2ModPrime1() const {return m_u;} - - void SetPrime1(const Integer &p) {m_p = p;} - void SetPrime2(const Integer &q) {m_q = q;} - void SetPrivateExponent(const Integer &d) {m_d = d;} - void SetModPrime1PrivateExponent(const Integer &dp) {m_dp = dp;} - void SetModPrime2PrivateExponent(const Integer &dq) {m_dq = dq;} - void SetMultiplicativeInverseOfPrime2ModPrime1(const Integer &u) {m_u = u;} - -protected: - Integer m_d, m_p, m_q, m_dp, m_dq, m_u; -}; - -class CRYPTOPP_DLL RSAFunction_ISO : public RSAFunction -{ -public: - Integer ApplyFunction(const Integer &x) const; - Integer PreimageBound() const {return ++(m_n>>1);} -}; - -class CRYPTOPP_DLL InvertibleRSAFunction_ISO : public InvertibleRSAFunction -{ -public: - Integer CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const; - Integer PreimageBound() const {return ++(m_n>>1);} -}; - -//! RSA -struct CRYPTOPP_DLL RSA -{ - static const char * CRYPTOPP_API StaticAlgorithmName() {return "RSA";} - typedef RSAFunction PublicKey; - typedef InvertibleRSAFunction PrivateKey; -}; - -//! RSA cryptosystem -template -struct RSAES : public TF_ES -{ -}; - -//! RSA signature scheme with appendix -/*! See documentation of PKCS1v15 for a list of hash functions that can be used with it. */ -template -struct RSASS : public TF_SS -{ -}; - -struct CRYPTOPP_DLL RSA_ISO -{ - static const char * CRYPTOPP_API StaticAlgorithmName() {return "RSA-ISO";} - typedef RSAFunction_ISO PublicKey; - typedef InvertibleRSAFunction_ISO PrivateKey; -}; - -template -struct RSASS_ISO : public TF_SS -{ -}; - -// The two RSA encryption schemes defined in PKCS #1 v2.0 -typedef RSAES::Decryptor RSAES_PKCS1v15_Decryptor; -typedef RSAES::Encryptor RSAES_PKCS1v15_Encryptor; - -typedef RSAES >::Decryptor RSAES_OAEP_SHA_Decryptor; -typedef RSAES >::Encryptor RSAES_OAEP_SHA_Encryptor; - -// The three RSA signature schemes defined in PKCS #1 v2.0 -typedef RSASS::Signer RSASSA_PKCS1v15_SHA_Signer; -typedef RSASS::Verifier RSASSA_PKCS1v15_SHA_Verifier; - -namespace Weak { -typedef RSASS::Signer RSASSA_PKCS1v15_MD2_Signer; -typedef RSASS::Verifier RSASSA_PKCS1v15_MD2_Verifier; - -typedef RSASS::Signer RSASSA_PKCS1v15_MD5_Signer; -typedef RSASS::Verifier RSASSA_PKCS1v15_MD5_Verifier; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/rw.cpp b/CryptoPP/rw.cpp deleted file mode 100644 index cdd9f2d22..000000000 --- a/CryptoPP/rw.cpp +++ /dev/null @@ -1,196 +0,0 @@ -// rw.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "rw.h" -#include "nbtheory.h" -#include "asn.h" - -#ifndef CRYPTOPP_IMPORTS - -NAMESPACE_BEGIN(CryptoPP) - -void RWFunction::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder seq(bt); - m_n.BERDecode(seq); - seq.MessageEnd(); -} - -void RWFunction::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - m_n.DEREncode(seq); - seq.MessageEnd(); -} - -Integer RWFunction::ApplyFunction(const Integer &in) const -{ - DoQuickSanityCheck(); - - Integer out = in.Squared()%m_n; - const word r = 12; - // this code was written to handle both r = 6 and r = 12, - // but now only r = 12 is used in P1363 - const word r2 = r/2; - const word r3a = (16 + 5 - r) % 16; // n%16 could be 5 or 13 - const word r3b = (16 + 13 - r) % 16; - const word r4 = (8 + 5 - r/2) % 8; // n%8 == 5 - switch (out % 16) - { - case r: - break; - case r2: - case r2+8: - out <<= 1; - break; - case r3a: - case r3b: - out.Negate(); - out += m_n; - break; - case r4: - case r4+8: - out.Negate(); - out += m_n; - out <<= 1; - break; - default: - out = Integer::Zero(); - } - return out; -} - -bool RWFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = true; - pass = pass && m_n > Integer::One() && m_n%8 == 5; - return pass; -} - -bool RWFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Modulus) - ; -} - -void RWFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Modulus) - ; -} - -// ***************************************************************************** -// private key operations: - -// generate a random private key -void InvertibleRWFunction::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg) -{ - int modulusSize = 2048; - alg.GetIntValue("ModulusSize", modulusSize) || alg.GetIntValue("KeySize", modulusSize); - - if (modulusSize < 16) - throw InvalidArgument("InvertibleRWFunction: specified modulus length is too small"); - - AlgorithmParameters primeParam = MakeParametersForTwoPrimesOfEqualSize(modulusSize); - m_p.GenerateRandom(rng, CombinedNameValuePairs(primeParam, MakeParameters("EquivalentTo", 3)("Mod", 8))); - m_q.GenerateRandom(rng, CombinedNameValuePairs(primeParam, MakeParameters("EquivalentTo", 7)("Mod", 8))); - - m_n = m_p * m_q; - m_u = m_q.InverseMod(m_p); -} - -void InvertibleRWFunction::BERDecode(BufferedTransformation &bt) -{ - BERSequenceDecoder seq(bt); - m_n.BERDecode(seq); - m_p.BERDecode(seq); - m_q.BERDecode(seq); - m_u.BERDecode(seq); - seq.MessageEnd(); -} - -void InvertibleRWFunction::DEREncode(BufferedTransformation &bt) const -{ - DERSequenceEncoder seq(bt); - m_n.DEREncode(seq); - m_p.DEREncode(seq); - m_q.DEREncode(seq); - m_u.DEREncode(seq); - seq.MessageEnd(); -} - -Integer InvertibleRWFunction::CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const -{ - DoQuickSanityCheck(); - ModularArithmetic modn(m_n); - Integer r, rInv; - do { // do this in a loop for people using small numbers for testing - r.Randomize(rng, Integer::One(), m_n - Integer::One()); - rInv = modn.MultiplicativeInverse(r); - } while (rInv.IsZero()); - Integer re = modn.Square(r); - re = modn.Multiply(re, x); // blind - - Integer cp=re%m_p, cq=re%m_q; - if (Jacobi(cp, m_p) * Jacobi(cq, m_q) != 1) - { - cp = cp.IsOdd() ? (cp+m_p) >> 1 : cp >> 1; - cq = cq.IsOdd() ? (cq+m_q) >> 1 : cq >> 1; - } - - #pragma omp parallel - #pragma omp sections - { - #pragma omp section - cp = ModularSquareRoot(cp, m_p); - #pragma omp section - cq = ModularSquareRoot(cq, m_q); - } - - Integer y = CRT(cq, m_q, cp, m_p, m_u); - y = modn.Multiply(y, rInv); // unblind - y = STDMIN(y, m_n-y); - if (ApplyFunction(y) != x) // check - throw Exception(Exception::OTHER_ERROR, "InvertibleRWFunction: computational error during private key operation"); - return y; -} - -bool InvertibleRWFunction::Validate(RandomNumberGenerator &rng, unsigned int level) const -{ - bool pass = RWFunction::Validate(rng, level); - pass = pass && m_p > Integer::One() && m_p%8 == 3 && m_p < m_n; - pass = pass && m_q > Integer::One() && m_q%8 == 7 && m_q < m_n; - pass = pass && m_u.IsPositive() && m_u < m_p; - if (level >= 1) - { - pass = pass && m_p * m_q == m_n; - pass = pass && m_u * m_q % m_p == 1; - } - if (level >= 2) - pass = pass && VerifyPrime(rng, m_p, level-2) && VerifyPrime(rng, m_q, level-2); - return pass; -} - -bool InvertibleRWFunction::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const -{ - return GetValueHelper(this, name, valueType, pValue).Assignable() - CRYPTOPP_GET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_GET_FUNCTION_ENTRY(Prime2) - CRYPTOPP_GET_FUNCTION_ENTRY(MultiplicativeInverseOfPrime2ModPrime1) - ; -} - -void InvertibleRWFunction::AssignFrom(const NameValuePairs &source) -{ - AssignFromHelper(this, source) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime1) - CRYPTOPP_SET_FUNCTION_ENTRY(Prime2) - CRYPTOPP_SET_FUNCTION_ENTRY(MultiplicativeInverseOfPrime2ModPrime1) - ; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/rw.h b/CryptoPP/rw.h deleted file mode 100644 index 6820251e8..000000000 --- a/CryptoPP/rw.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef CRYPTOPP_RW_H -#define CRYPTOPP_RW_H - -/** \file - This file contains classes that implement the - Rabin-Williams signature schemes as defined in IEEE P1363. -*/ - -#include "pubkey.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -class CRYPTOPP_DLL RWFunction : public TrapdoorFunction, public PublicKey -{ - typedef RWFunction ThisClass; - -public: - void Initialize(const Integer &n) - {m_n = n;} - - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - void Save(BufferedTransformation &bt) const - {DEREncode(bt);} - void Load(BufferedTransformation &bt) - {BERDecode(bt);} - - Integer ApplyFunction(const Integer &x) const; - Integer PreimageBound() const {return ++(m_n>>1);} - Integer ImageBound() const {return m_n;} - - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - - const Integer& GetModulus() const {return m_n;} - void SetModulus(const Integer &n) {m_n = n;} - -protected: - Integer m_n; -}; - -//! _ -class CRYPTOPP_DLL InvertibleRWFunction : public RWFunction, public TrapdoorFunctionInverse, public PrivateKey -{ - typedef InvertibleRWFunction ThisClass; - -public: - void Initialize(const Integer &n, const Integer &p, const Integer &q, const Integer &u) - {m_n = n; m_p = p; m_q = q; m_u = u;} - // generate a random private key - void Initialize(RandomNumberGenerator &rng, unsigned int modulusBits) - {GenerateRandomWithKeySize(rng, modulusBits);} - - void BERDecode(BufferedTransformation &bt); - void DEREncode(BufferedTransformation &bt) const; - - void Save(BufferedTransformation &bt) const - {DEREncode(bt);} - void Load(BufferedTransformation &bt) - {BERDecode(bt);} - - Integer CalculateInverse(RandomNumberGenerator &rng, const Integer &x) const; - - // GeneratibleCryptoMaterial - bool Validate(RandomNumberGenerator &rng, unsigned int level) const; - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const; - void AssignFrom(const NameValuePairs &source); - /*! parameters: (ModulusSize) */ - void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs &alg); - - const Integer& GetPrime1() const {return m_p;} - const Integer& GetPrime2() const {return m_q;} - const Integer& GetMultiplicativeInverseOfPrime2ModPrime1() const {return m_u;} - - void SetPrime1(const Integer &p) {m_p = p;} - void SetPrime2(const Integer &q) {m_q = q;} - void SetMultiplicativeInverseOfPrime2ModPrime1(const Integer &u) {m_u = u;} - -protected: - Integer m_p, m_q, m_u; -}; - -//! RW -struct RW -{ - static std::string StaticAlgorithmName() {return "RW";} - typedef RWFunction PublicKey; - typedef InvertibleRWFunction PrivateKey; -}; - -//! RWSS -template -struct RWSS : public TF_SS -{ -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/safer.cpp b/CryptoPP/safer.cpp deleted file mode 100644 index d46ca6417..000000000 --- a/CryptoPP/safer.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// safer.cpp - modified by by Wei Dai from Richard De Moliner's safer.c - -#include "pch.h" -#include "safer.h" -#include "misc.h" -#include "argnames.h" - -NAMESPACE_BEGIN(CryptoPP) - -const byte SAFER::Base::exp_tab[256] = - {1, 45, 226, 147, 190, 69, 21, 174, 120, 3, 135, 164, 184, 56, 207, 63, - 8, 103, 9, 148, 235, 38, 168, 107, 189, 24, 52, 27, 187, 191, 114, 247, - 64, 53, 72, 156, 81, 47, 59, 85, 227, 192, 159, 216, 211, 243, 141, 177, - 255, 167, 62, 220, 134, 119, 215, 166, 17, 251, 244, 186, 146, 145, 100, 131, - 241, 51, 239, 218, 44, 181, 178, 43, 136, 209, 153, 203, 140, 132, 29, 20, - 129, 151, 113, 202, 95, 163, 139, 87, 60, 130, 196, 82, 92, 28, 232, 160, - 4, 180, 133, 74, 246, 19, 84, 182, 223, 12, 26, 142, 222, 224, 57, 252, - 32, 155, 36, 78, 169, 152, 158, 171, 242, 96, 208, 108, 234, 250, 199, 217, - 0, 212, 31, 110, 67, 188, 236, 83, 137, 254, 122, 93, 73, 201, 50, 194, - 249, 154, 248, 109, 22, 219, 89, 150, 68, 233, 205, 230, 70, 66, 143, 10, - 193, 204, 185, 101, 176, 210, 198, 172, 30, 65, 98, 41, 46, 14, 116, 80, - 2, 90, 195, 37, 123, 138, 42, 91, 240, 6, 13, 71, 111, 112, 157, 126, - 16, 206, 18, 39, 213, 76, 79, 214, 121, 48, 104, 54, 117, 125, 228, 237, - 128, 106, 144, 55, 162, 94, 118, 170, 197, 127, 61, 175, 165, 229, 25, 97, - 253, 77, 124, 183, 11, 238, 173, 75, 34, 245, 231, 115, 35, 33, 200, 5, - 225, 102, 221, 179, 88, 105, 99, 86, 15, 161, 49, 149, 23, 7, 58, 40}; - -const byte SAFER::Base::log_tab[256] = - {128, 0, 176, 9, 96, 239, 185, 253, 16, 18, 159, 228, 105, 186, 173, 248, - 192, 56, 194, 101, 79, 6, 148, 252, 25, 222, 106, 27, 93, 78, 168, 130, - 112, 237, 232, 236, 114, 179, 21, 195, 255, 171, 182, 71, 68, 1, 172, 37, - 201, 250, 142, 65, 26, 33, 203, 211, 13, 110, 254, 38, 88, 218, 50, 15, - 32, 169, 157, 132, 152, 5, 156, 187, 34, 140, 99, 231, 197, 225, 115, 198, - 175, 36, 91, 135, 102, 39, 247, 87, 244, 150, 177, 183, 92, 139, 213, 84, - 121, 223, 170, 246, 62, 163, 241, 17, 202, 245, 209, 23, 123, 147, 131, 188, - 189, 82, 30, 235, 174, 204, 214, 53, 8, 200, 138, 180, 226, 205, 191, 217, - 208, 80, 89, 63, 77, 98, 52, 10, 72, 136, 181, 86, 76, 46, 107, 158, - 210, 61, 60, 3, 19, 251, 151, 81, 117, 74, 145, 113, 35, 190, 118, 42, - 95, 249, 212, 85, 11, 220, 55, 49, 22, 116, 215, 119, 167, 230, 7, 219, - 164, 47, 70, 243, 97, 69, 103, 227, 12, 162, 59, 28, 133, 24, 4, 29, - 41, 160, 143, 178, 90, 216, 166, 126, 238, 141, 83, 75, 161, 154, 193, 14, - 122, 73, 165, 44, 129, 196, 199, 54, 43, 127, 67, 149, 51, 242, 108, 104, - 109, 240, 2, 40, 206, 221, 155, 234, 94, 153, 124, 20, 134, 207, 229, 66, - 184, 64, 120, 45, 58, 233, 100, 31, 146, 144, 125, 57, 111, 224, 137, 48}; - -#define EXP(x) exp_tab[(x)] -#define LOG(x) log_tab[(x)] -#define PHT(x, y) { y += x; x += y; } -#define IPHT(x, y) { x -= y; y -= x; } - -static const unsigned int BLOCKSIZE = 8; -static const unsigned int MAX_ROUNDS = 13; - -void SAFER::Base::UncheckedSetKey(const byte *userkey_1, unsigned int length, const NameValuePairs ¶ms) -{ - bool strengthened = Strengthened(); - unsigned int nof_rounds = params.GetIntValueWithDefault(Name::Rounds(), length == 8 ? (strengthened ? 8 : 6) : 10); - - const byte *userkey_2 = length == 8 ? userkey_1 : userkey_1 + 8; - keySchedule.New(1 + BLOCKSIZE * (1 + 2 * nof_rounds)); - - unsigned int i, j; - byte *key = keySchedule; - SecByteBlock ka(BLOCKSIZE + 1), kb(BLOCKSIZE + 1); - - if (MAX_ROUNDS < nof_rounds) - nof_rounds = MAX_ROUNDS; - *key++ = (unsigned char)nof_rounds; - ka[BLOCKSIZE] = 0; - kb[BLOCKSIZE] = 0; - for (j = 0; j < BLOCKSIZE; j++) - { - ka[BLOCKSIZE] ^= ka[j] = rotlFixed(userkey_1[j], 5U); - kb[BLOCKSIZE] ^= kb[j] = *key++ = userkey_2[j]; - } - - for (i = 1; i <= nof_rounds; i++) - { - for (j = 0; j < BLOCKSIZE + 1; j++) - { - ka[j] = rotlFixed(ka[j], 6U); - kb[j] = rotlFixed(kb[j], 6U); - } - for (j = 0; j < BLOCKSIZE; j++) - if (strengthened) - *key++ = (ka[(j + 2 * i - 1) % (BLOCKSIZE + 1)] - + exp_tab[exp_tab[18 * i + j + 1]]) & 0xFF; - else - *key++ = (ka[j] + exp_tab[exp_tab[18 * i + j + 1]]) & 0xFF; - for (j = 0; j < BLOCKSIZE; j++) - if (strengthened) - *key++ = (kb[(j + 2 * i) % (BLOCKSIZE + 1)] - + exp_tab[exp_tab[18 * i + j + 10]]) & 0xFF; - else - *key++ = (kb[j] + exp_tab[exp_tab[18 * i + j + 10]]) & 0xFF; - } -} - -typedef BlockGetAndPut Block; - -void SAFER::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - byte a, b, c, d, e, f, g, h, t; - const byte *key = keySchedule+1; - unsigned int round = keySchedule[0]; - - Block::Get(inBlock)(a)(b)(c)(d)(e)(f)(g)(h); - while(round--) - { - a ^= key[0]; b += key[1]; c += key[2]; d ^= key[3]; - e ^= key[4]; f += key[5]; g += key[6]; h ^= key[7]; - a = EXP(a) + key[ 8]; b = LOG(b) ^ key[ 9]; - c = LOG(c) ^ key[10]; d = EXP(d) + key[11]; - e = EXP(e) + key[12]; f = LOG(f) ^ key[13]; - g = LOG(g) ^ key[14]; h = EXP(h) + key[15]; - key += 16; - PHT(a, b); PHT(c, d); PHT(e, f); PHT(g, h); - PHT(a, c); PHT(e, g); PHT(b, d); PHT(f, h); - PHT(a, e); PHT(b, f); PHT(c, g); PHT(d, h); - t = b; b = e; e = c; c = t; t = d; d = f; f = g; g = t; - } - a ^= key[0]; b += key[1]; c += key[2]; d ^= key[3]; - e ^= key[4]; f += key[5]; g += key[6]; h ^= key[7]; - Block::Put(xorBlock, outBlock)(a)(b)(c)(d)(e)(f)(g)(h); -} - -void SAFER::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - byte a, b, c, d, e, f, g, h, t; - unsigned int round = keySchedule[0]; - const byte *key = keySchedule + BLOCKSIZE * (1 + 2 * round) - 7; - - Block::Get(inBlock)(a)(b)(c)(d)(e)(f)(g)(h); - h ^= key[7]; g -= key[6]; f -= key[5]; e ^= key[4]; - d ^= key[3]; c -= key[2]; b -= key[1]; a ^= key[0]; - while (round--) - { - key -= 16; - t = e; e = b; b = c; c = t; t = f; f = d; d = g; g = t; - IPHT(a, e); IPHT(b, f); IPHT(c, g); IPHT(d, h); - IPHT(a, c); IPHT(e, g); IPHT(b, d); IPHT(f, h); - IPHT(a, b); IPHT(c, d); IPHT(e, f); IPHT(g, h); - h -= key[15]; g ^= key[14]; f ^= key[13]; e -= key[12]; - d -= key[11]; c ^= key[10]; b ^= key[9]; a -= key[8]; - h = LOG(h) ^ key[7]; g = EXP(g) - key[6]; - f = EXP(f) - key[5]; e = LOG(e) ^ key[4]; - d = LOG(d) ^ key[3]; c = EXP(c) - key[2]; - b = EXP(b) - key[1]; a = LOG(a) ^ key[0]; - } - Block::Put(xorBlock, outBlock)(a)(b)(c)(d)(e)(f)(g)(h); -} - -NAMESPACE_END diff --git a/CryptoPP/safer.h b/CryptoPP/safer.h deleted file mode 100644 index f9a3c9e1f..000000000 --- a/CryptoPP/safer.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef CRYPTOPP_SAFER_H -#define CRYPTOPP_SAFER_H - -/** \file -*/ - -#include "seckey.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -/// base class, do not use directly -class SAFER -{ -public: - class CRYPTOPP_NO_VTABLE Base : public BlockCipher - { - public: - unsigned int OptimalDataAlignment() const {return 1;} - void UncheckedSetKey(const byte *userkey, unsigned int length, const NameValuePairs ¶ms); - - protected: - virtual bool Strengthened() const =0; - - SecByteBlock keySchedule; - static const byte exp_tab[256]; - static const byte log_tab[256]; - }; - - class CRYPTOPP_NO_VTABLE Enc : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - - class CRYPTOPP_NO_VTABLE Dec : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; -}; - -template -class CRYPTOPP_NO_VTABLE SAFER_Impl : public BlockCipherImpl -{ -protected: - bool Strengthened() const {return STR;} -}; - -//! _ -struct SAFER_K_Info : public FixedBlockSize<8>, public VariableKeyLength<16, 8, 16, 8>, public VariableRounds<10, 1, 13> -{ - static const char *StaticAlgorithmName() {return "SAFER-K";} -}; - -/// SAFER-K -class SAFER_K : public SAFER_K_Info, public SAFER, public BlockCipherDocumentation -{ -public: - typedef BlockCipherFinal > Encryption; - typedef BlockCipherFinal > Decryption; -}; - -//! _ -struct SAFER_SK_Info : public FixedBlockSize<8>, public VariableKeyLength<16, 8, 16, 8>, public VariableRounds<10, 1, 13> -{ - static const char *StaticAlgorithmName() {return "SAFER-SK";} -}; - -/// SAFER-SK -class SAFER_SK : public SAFER_SK_Info, public SAFER, public BlockCipherDocumentation -{ -public: - typedef BlockCipherFinal > Encryption; - typedef BlockCipherFinal > Decryption; -}; - -typedef SAFER_K::Encryption SAFER_K_Encryption; -typedef SAFER_K::Decryption SAFER_K_Decryption; - -typedef SAFER_SK::Encryption SAFER_SK_Encryption; -typedef SAFER_SK::Decryption SAFER_SK_Decryption; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/seal.cpp b/CryptoPP/seal.cpp deleted file mode 100644 index f49b52203..000000000 --- a/CryptoPP/seal.cpp +++ /dev/null @@ -1,213 +0,0 @@ -// seal.cpp - written and placed in the public domain by Wei Dai -// updated to SEAL 3.0 by Leonard Janke - -#include "pch.h" - -#include "seal.h" -#include "sha.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -void SEAL_TestInstantiations() -{ - SEAL<>::Encryption x; -} - -struct SEAL_Gamma -{ - SEAL_Gamma(const byte *key) - : H(5), Z(5), D(16), lastIndex(0xffffffff) - { - GetUserKey(BIG_ENDIAN_ORDER, H.begin(), 5, key, 20); - memset(D, 0, 64); - } - - word32 Apply(word32 i); - - SecBlock H, Z, D; - word32 lastIndex; -}; - -word32 SEAL_Gamma::Apply(word32 i) -{ - word32 shaIndex = i/5; - if (shaIndex != lastIndex) - { - memcpy(Z, H, 20); - D[0] = shaIndex; - SHA::Transform(Z, D); - lastIndex = shaIndex; - } - return Z[i%5]; -} - -template -void SEAL_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length) -{ - m_insideCounter = m_outsideCounter = m_startCount = 0; - - unsigned int L = params.GetIntValueWithDefault("NumberOfOutputBitsPerPositionIndex", 32*1024); - m_iterationsPerCount = L / 8192; - - SEAL_Gamma gamma(key); - unsigned int i; - - for (i=0; i<512; i++) - m_T[i] = gamma.Apply(i); - - for (i=0; i<256; i++) - m_S[i] = gamma.Apply(0x1000+i); - - m_R.New(4*(L/8192)); - - for (i=0; i -void SEAL_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length) -{ - assert(length==4); - m_outsideCounter = IV ? GetWord(false, BIG_ENDIAN_ORDER, IV) : 0; - m_startCount = m_outsideCounter; - m_insideCounter = 0; -} - -template -void SEAL_Policy::SeekToIteration(lword iterationCount) -{ - m_outsideCounter = m_startCount + (unsigned int)(iterationCount / m_iterationsPerCount); - m_insideCounter = (unsigned int)(iterationCount % m_iterationsPerCount); -} - -template -void SEAL_Policy::OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount) -{ - word32 a, b, c, d, n1, n2, n3, n4; - unsigned int p, q; - - for (size_t iteration = 0; iteration < iterationCount; ++iteration) - { -#define Ttab(x) *(word32 *)((byte *)m_T.begin()+x) - - a = m_outsideCounter ^ m_R[4*m_insideCounter]; - b = rotrFixed(m_outsideCounter, 8U) ^ m_R[4*m_insideCounter+1]; - c = rotrFixed(m_outsideCounter, 16U) ^ m_R[4*m_insideCounter+2]; - d = rotrFixed(m_outsideCounter, 24U) ^ m_R[4*m_insideCounter+3]; - - for (unsigned int j=0; j<2; j++) - { - p = a & 0x7fc; - b += Ttab(p); - a = rotrFixed(a, 9U); - - p = b & 0x7fc; - c += Ttab(p); - b = rotrFixed(b, 9U); - - p = c & 0x7fc; - d += Ttab(p); - c = rotrFixed(c, 9U); - - p = d & 0x7fc; - a += Ttab(p); - d = rotrFixed(d, 9U); - } - - n1 = d, n2 = b, n3 = a, n4 = c; - - p = a & 0x7fc; - b += Ttab(p); - a = rotrFixed(a, 9U); - - p = b & 0x7fc; - c += Ttab(p); - b = rotrFixed(b, 9U); - - p = c & 0x7fc; - d += Ttab(p); - c = rotrFixed(c, 9U); - - p = d & 0x7fc; - a += Ttab(p); - d = rotrFixed(d, 9U); - - // generate 8192 bits - for (unsigned int i=0; i<64; i++) - { - p = a & 0x7fc; - a = rotrFixed(a, 9U); - b += Ttab(p); - b ^= a; - - q = b & 0x7fc; - b = rotrFixed(b, 9U); - c ^= Ttab(q); - c += b; - - p = (p+c) & 0x7fc; - c = rotrFixed(c, 9U); - d += Ttab(p); - d ^= c; - - q = (q+d) & 0x7fc; - d = rotrFixed(d, 9U); - a ^= Ttab(q); - a += d; - - p = (p+a) & 0x7fc; - b ^= Ttab(p); - a = rotrFixed(a, 9U); - - q = (q+b) & 0x7fc; - c += Ttab(q); - b = rotrFixed(b, 9U); - - p = (p+c) & 0x7fc; - d ^= Ttab(p); - c = rotrFixed(c, 9U); - - q = (q+d) & 0x7fc; - d = rotrFixed(d, 9U); - a += Ttab(q); - -#define SEAL_OUTPUT(x) \ - CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, B::ToEnum(), 0, b + m_S[4*i+0]);\ - CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, B::ToEnum(), 1, c ^ m_S[4*i+1]);\ - CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, B::ToEnum(), 2, d + m_S[4*i+2]);\ - CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, B::ToEnum(), 3, a ^ m_S[4*i+3]); - - CRYPTOPP_KEYSTREAM_OUTPUT_SWITCH(SEAL_OUTPUT, 4*4); - - if (i & 1) - { - a += n3; - b += n4; - c ^= n3; - d ^= n4; - } - else - { - a += n1; - b += n2; - c ^= n1; - d ^= n2; - } - } - - if (++m_insideCounter == m_iterationsPerCount) - { - ++m_outsideCounter; - m_insideCounter = 0; - } - } - - a = b = c = d = n1 = n2 = n3 = n4 = 0; - p = q = 0; -} - -template class SEAL_Policy; -template class SEAL_Policy; - -NAMESPACE_END diff --git a/CryptoPP/seal.h b/CryptoPP/seal.h deleted file mode 100644 index e14ae1caf..000000000 --- a/CryptoPP/seal.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef CRYPTOPP_SEAL_H -#define CRYPTOPP_SEAL_H - -#include "strciphr.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -template -struct SEAL_Info : public FixedKeyLength<20, SimpleKeyingInterface::INTERNALLY_GENERATED_IV, 4> -{ - static const char *StaticAlgorithmName() {return B::ToEnum() == LITTLE_ENDIAN_ORDER ? "SEAL-3.0-LE" : "SEAL-3.0-BE";} -}; - -template -class CRYPTOPP_NO_VTABLE SEAL_Policy : public AdditiveCipherConcretePolicy, public SEAL_Info -{ -protected: - void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length); - void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount); - void CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length); - bool CipherIsRandomAccess() const {return true;} - void SeekToIteration(lword iterationCount); - -private: - FixedSizeSecBlock m_T; - FixedSizeSecBlock m_S; - SecBlock m_R; - - word32 m_startCount, m_iterationsPerCount; - word32 m_outsideCounter, m_insideCounter; -}; - -//! SEAL -template -struct SEAL : public SEAL_Info, public SymmetricCipherDocumentation -{ - typedef SymmetricCipherFinal, AdditiveCipherTemplate<> >, SEAL_Info > Encryption; - typedef Encryption Decryption; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/secblock.h b/CryptoPP/secblock.h deleted file mode 100644 index 40cce3341..000000000 --- a/CryptoPP/secblock.h +++ /dev/null @@ -1,467 +0,0 @@ -// secblock.h - written and placed in the public domain by Wei Dai - -#ifndef CRYPTOPP_SECBLOCK_H -#define CRYPTOPP_SECBLOCK_H - -#include "config.h" -#include "misc.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -// ************** secure memory allocation *************** - -template -class AllocatorBase -{ -public: - typedef T value_type; - typedef size_t size_type; -#ifdef CRYPTOPP_MSVCRT6 - typedef ptrdiff_t difference_type; -#else - typedef std::ptrdiff_t difference_type; -#endif - typedef T * pointer; - typedef const T * const_pointer; - typedef T & reference; - typedef const T & const_reference; - - pointer address(reference r) const {return (&r);} - const_pointer address(const_reference r) const {return (&r); } - void construct(pointer p, const T& val) {new (p) T(val);} - void destroy(pointer p) {p->~T();} - size_type max_size() const {return ~size_type(0)/sizeof(T);} // switch to std::numeric_limits::max later - -protected: - static void CheckSize(size_t n) - { - if (n > ~size_t(0) / sizeof(T)) - throw InvalidArgument("AllocatorBase: requested size would cause integer overflow"); - } -}; - -#define CRYPTOPP_INHERIT_ALLOCATOR_TYPES \ -typedef typename AllocatorBase::value_type value_type;\ -typedef typename AllocatorBase::size_type size_type;\ -typedef typename AllocatorBase::difference_type difference_type;\ -typedef typename AllocatorBase::pointer pointer;\ -typedef typename AllocatorBase::const_pointer const_pointer;\ -typedef typename AllocatorBase::reference reference;\ -typedef typename AllocatorBase::const_reference const_reference; - -#if defined(_MSC_VER) && (_MSC_VER < 1300) -// this pragma causes an internal compiler error if placed immediately before std::swap(a, b) -#pragma warning(push) -#pragma warning(disable: 4700) // VC60 workaround: don't know how to get rid of this warning -#endif - -template -typename A::pointer StandardReallocate(A& a, T *p, typename A::size_type oldSize, typename A::size_type newSize, bool preserve) -{ - if (oldSize == newSize) - return p; - - if (preserve) - { - typename A::pointer newPointer = a.allocate(newSize, NULL); - memcpy_s(newPointer, sizeof(T)*newSize, p, sizeof(T)*STDMIN(oldSize, newSize)); - a.deallocate(p, oldSize); - return newPointer; - } - else - { - a.deallocate(p, oldSize); - return a.allocate(newSize, NULL); - } -} - -#if defined(_MSC_VER) && (_MSC_VER < 1300) -#pragma warning(pop) -#endif - -template -class AllocatorWithCleanup : public AllocatorBase -{ -public: - CRYPTOPP_INHERIT_ALLOCATOR_TYPES - - pointer allocate(size_type n, const void * = NULL) - { - this->CheckSize(n); - if (n == 0) - return NULL; - -#if CRYPTOPP_BOOL_ALIGN16_ENABLED - if (T_Align16 && n*sizeof(T) >= 16) - return (pointer)AlignedAllocate(n*sizeof(T)); -#endif - - return (pointer)UnalignedAllocate(n*sizeof(T)); - } - - void deallocate(void *p, size_type n) - { - SecureWipeArray((pointer)p, n); - -#if CRYPTOPP_BOOL_ALIGN16_ENABLED - if (T_Align16 && n*sizeof(T) >= 16) - return AlignedDeallocate(p); -#endif - - UnalignedDeallocate(p); - } - - pointer reallocate(T *p, size_type oldSize, size_type newSize, bool preserve) - { - return StandardReallocate(*this, p, oldSize, newSize, preserve); - } - - // VS.NET STL enforces the policy of "All STL-compliant allocators have to provide a - // template class member called rebind". - template struct rebind { typedef AllocatorWithCleanup other; }; -#if _MSC_VER >= 1500 - AllocatorWithCleanup() {} - template AllocatorWithCleanup(const AllocatorWithCleanup &) {} -#endif -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; -#if CRYPTOPP_BOOL_X86 -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; // for Integer -#endif - -template -class NullAllocator : public AllocatorBase -{ -public: - CRYPTOPP_INHERIT_ALLOCATOR_TYPES - - pointer allocate(size_type n, const void * = NULL) - { - assert(false); - return NULL; - } - - void deallocate(void *p, size_type n) - { - assert(false); - } - - size_type max_size() const {return 0;} -}; - -// This allocator can't be used with standard collections because -// they require that all objects of the same allocator type are equivalent. -// So this is for use with SecBlock only. -template , bool T_Align16 = false> -class FixedSizeAllocatorWithCleanup : public AllocatorBase -{ -public: - CRYPTOPP_INHERIT_ALLOCATOR_TYPES - - FixedSizeAllocatorWithCleanup() : m_allocated(false) {} - - pointer allocate(size_type n) - { - assert(IsAlignedOn(m_array, 8)); - - if (n <= S && !m_allocated) - { - m_allocated = true; - return GetAlignedArray(); - } - else - return m_fallbackAllocator.allocate(n); - } - - pointer allocate(size_type n, const void *hint) - { - if (n <= S && !m_allocated) - { - m_allocated = true; - return GetAlignedArray(); - } - else - return m_fallbackAllocator.allocate(n, hint); - } - - void deallocate(void *p, size_type n) - { - if (p == GetAlignedArray()) - { - assert(n <= S); - assert(m_allocated); - m_allocated = false; - SecureWipeArray((pointer)p, n); - } - else - m_fallbackAllocator.deallocate(p, n); - } - - pointer reallocate(pointer p, size_type oldSize, size_type newSize, bool preserve) - { - if (p == GetAlignedArray() && newSize <= S) - { - assert(oldSize <= S); - if (oldSize > newSize) - SecureWipeArray(p+newSize, oldSize-newSize); - return p; - } - - pointer newPointer = allocate(newSize, NULL); - if (preserve) - memcpy(newPointer, p, sizeof(T)*STDMIN(oldSize, newSize)); - deallocate(p, oldSize); - return newPointer; - } - - size_type max_size() const {return STDMAX(m_fallbackAllocator.max_size(), S);} - -private: -#ifdef __BORLANDC__ - T* GetAlignedArray() {return m_array;} - T m_array[S]; -#else - T* GetAlignedArray() {return (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16) ? (T*)(((byte *)m_array) + (0-(size_t)m_array)%16) : m_array;} - CRYPTOPP_ALIGN_DATA(8) T m_array[(CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16) ? S+8/sizeof(T) : S]; -#endif - A m_fallbackAllocator; - bool m_allocated; -}; - -//! a block of memory allocated using A -template > -class SecBlock -{ -public: - typedef typename A::value_type value_type; - typedef typename A::pointer iterator; - typedef typename A::const_pointer const_iterator; - typedef typename A::size_type size_type; - - explicit SecBlock(size_type size=0) - : m_size(size) {m_ptr = m_alloc.allocate(size, NULL);} - SecBlock(const SecBlock &t) - : m_size(t.m_size) {m_ptr = m_alloc.allocate(m_size, NULL); memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T));} - SecBlock(const T *t, size_type len) - : m_size(len) - { - m_ptr = m_alloc.allocate(len, NULL); - if (t == NULL) - memset_z(m_ptr, 0, len*sizeof(T)); - else - memcpy(m_ptr, t, len*sizeof(T)); - } - - ~SecBlock() - {m_alloc.deallocate(m_ptr, m_size);} - -#ifdef __BORLANDC__ - operator T *() const - {return (T*)m_ptr;} -#else - operator const void *() const - {return m_ptr;} - operator void *() - {return m_ptr;} - - operator const T *() const - {return m_ptr;} - operator T *() - {return m_ptr;} -#endif - -// T *operator +(size_type offset) -// {return m_ptr+offset;} - -// const T *operator +(size_type offset) const -// {return m_ptr+offset;} - -// T& operator[](size_type index) -// {assert(index >= 0 && index < m_size); return m_ptr[index];} - -// const T& operator[](size_type index) const -// {assert(index >= 0 && index < m_size); return m_ptr[index];} - - iterator begin() - {return m_ptr;} - const_iterator begin() const - {return m_ptr;} - iterator end() - {return m_ptr+m_size;} - const_iterator end() const - {return m_ptr+m_size;} - - typename A::pointer data() {return m_ptr;} - typename A::const_pointer data() const {return m_ptr;} - - size_type size() const {return m_size;} - bool empty() const {return m_size == 0;} - - byte * BytePtr() {return (byte *)m_ptr;} - const byte * BytePtr() const {return (const byte *)m_ptr;} - size_type SizeInBytes() const {return m_size*sizeof(T);} - - //! set contents and size - void Assign(const T *t, size_type len) - { - New(len); - memcpy_s(m_ptr, m_size*sizeof(T), t, len*sizeof(T)); - } - - //! copy contents and size from another SecBlock - void Assign(const SecBlock &t) - { - if (this != &t) - { - New(t.m_size); - memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T)); - } - } - - SecBlock& operator=(const SecBlock &t) - { - Assign(t); - return *this; - } - - // append to this object - SecBlock& operator+=(const SecBlock &t) - { - size_type oldSize = m_size; - Grow(m_size+t.m_size); - memcpy_s(m_ptr+oldSize, m_size*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); - return *this; - } - - // append operator - SecBlock operator+(const SecBlock &t) - { - SecBlock result(m_size+t.m_size); - memcpy_s(result.m_ptr, result.m_size*sizeof(T), m_ptr, m_size*sizeof(T)); - memcpy_s(result.m_ptr+m_size, t.m_size*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); - return result; - } - - bool operator==(const SecBlock &t) const - { - return m_size == t.m_size && VerifyBufsEqual(m_ptr, t.m_ptr, m_size*sizeof(T)); - } - - bool operator!=(const SecBlock &t) const - { - return !operator==(t); - } - - //! change size, without preserving contents - void New(size_type newSize) - { - m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, false); - m_size = newSize; - } - - //! change size and set contents to 0 - void CleanNew(size_type newSize) - { - New(newSize); - memset_z(m_ptr, 0, m_size*sizeof(T)); - } - - //! change size only if newSize > current size. contents are preserved - void Grow(size_type newSize) - { - if (newSize > m_size) - { - m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); - m_size = newSize; - } - } - - //! change size only if newSize > current size. contents are preserved and additional area is set to 0 - void CleanGrow(size_type newSize) - { - if (newSize > m_size) - { - m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); - memset(m_ptr+m_size, 0, (newSize-m_size)*sizeof(T)); - m_size = newSize; - } - } - - //! change size and preserve contents - void resize(size_type newSize) - { - m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); - m_size = newSize; - } - - //! swap contents and size with another SecBlock - void swap(SecBlock &b) - { - std::swap(m_alloc, b.m_alloc); - std::swap(m_size, b.m_size); - std::swap(m_ptr, b.m_ptr); - } - -//private: - A m_alloc; - size_type m_size; - T *m_ptr; -}; - -typedef SecBlock SecByteBlock; -typedef SecBlock > AlignedSecByteBlock; -typedef SecBlock SecWordBlock; - -//! a SecBlock with fixed size, allocated statically -template > -class FixedSizeSecBlock : public SecBlock -{ -public: - explicit FixedSizeSecBlock() : SecBlock(S) {} -}; - -template -class FixedSizeAlignedSecBlock : public FixedSizeSecBlock, T_Align16> > -{ -}; - -//! a SecBlock that preallocates size S statically, and uses the heap when this size is exceeded -template > > -class SecBlockWithHint : public SecBlock -{ -public: - explicit SecBlockWithHint(size_t size) : SecBlock(size) {} -}; - -template -inline bool operator==(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (true);} -template -inline bool operator!=(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (false);} - -NAMESPACE_END - -NAMESPACE_BEGIN(std) -template -inline void swap(CryptoPP::SecBlock &a, CryptoPP::SecBlock &b) -{ - a.swap(b); -} - -#if defined(_STLP_DONT_SUPPORT_REBIND_MEMBER_TEMPLATE) || (defined(_STLPORT_VERSION) && !defined(_STLP_MEMBER_TEMPLATE_CLASSES)) -// working for STLport 5.1.3 and MSVC 6 SP5 -template -inline CryptoPP::AllocatorWithCleanup<_Tp2>& -__stl_alloc_rebind(CryptoPP::AllocatorWithCleanup<_Tp1>& __a, const _Tp2*) -{ - return (CryptoPP::AllocatorWithCleanup<_Tp2>&)(__a); -} -#endif - -NAMESPACE_END - -#endif diff --git a/CryptoPP/seckey.h b/CryptoPP/seckey.h deleted file mode 100644 index 35046a61b..000000000 --- a/CryptoPP/seckey.h +++ /dev/null @@ -1,221 +0,0 @@ -// seckey.h - written and placed in the public domain by Wei Dai - -// This file contains helper classes/functions for implementing secret key algorithms. - -#ifndef CRYPTOPP_SECKEY_H -#define CRYPTOPP_SECKEY_H - -#include "cryptlib.h" -#include "misc.h" -#include "simple.h" - -NAMESPACE_BEGIN(CryptoPP) - -inline CipherDir ReverseCipherDir(CipherDir dir) -{ - return (dir == ENCRYPTION) ? DECRYPTION : ENCRYPTION; -} - -//! to be inherited by block ciphers with fixed block size -template -class FixedBlockSize -{ -public: - CRYPTOPP_CONSTANT(BLOCKSIZE = N) -}; - -// ************** rounds *************** - -//! to be inherited by ciphers with fixed number of rounds -template -class FixedRounds -{ -public: - CRYPTOPP_CONSTANT(ROUNDS = R) -}; - -//! to be inherited by ciphers with variable number of rounds -template // use INT_MAX here because enums are treated as signed ints -class VariableRounds -{ -public: - CRYPTOPP_CONSTANT(DEFAULT_ROUNDS = D) - CRYPTOPP_CONSTANT(MIN_ROUNDS = N) - CRYPTOPP_CONSTANT(MAX_ROUNDS = M) - static unsigned int StaticGetDefaultRounds(size_t keylength) {return DEFAULT_ROUNDS;} - -protected: - inline void ThrowIfInvalidRounds(int rounds, const Algorithm *alg) - { - if (rounds < MIN_ROUNDS || rounds > MAX_ROUNDS) - throw InvalidRounds(alg->AlgorithmName(), rounds); - } - - inline unsigned int GetRoundsAndThrowIfInvalid(const NameValuePairs ¶m, const Algorithm *alg) - { - int rounds = param.GetIntValueWithDefault("Rounds", DEFAULT_ROUNDS); - ThrowIfInvalidRounds(rounds, alg); - return (unsigned int)rounds; - } -}; - -// ************** key length *************** - -//! to be inherited by keyed algorithms with fixed key length -template -class FixedKeyLength -{ -public: - CRYPTOPP_CONSTANT(KEYLENGTH=N) - CRYPTOPP_CONSTANT(MIN_KEYLENGTH=N) - CRYPTOPP_CONSTANT(MAX_KEYLENGTH=N) - CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH=N) - CRYPTOPP_CONSTANT(IV_REQUIREMENT = IV_REQ) - CRYPTOPP_CONSTANT(IV_LENGTH = IV_L) - static size_t CRYPTOPP_API StaticGetValidKeyLength(size_t) {return KEYLENGTH;} -}; - -/// support query of variable key length, template parameters are default, min, max, multiple (default multiple 1) -template -class VariableKeyLength -{ - // make these private to avoid Doxygen documenting them in all derived classes - CRYPTOPP_COMPILE_ASSERT(Q > 0); - CRYPTOPP_COMPILE_ASSERT(N % Q == 0); - CRYPTOPP_COMPILE_ASSERT(M % Q == 0); - CRYPTOPP_COMPILE_ASSERT(N < M); - CRYPTOPP_COMPILE_ASSERT(D >= N); - CRYPTOPP_COMPILE_ASSERT(M >= D); - -public: - CRYPTOPP_CONSTANT(MIN_KEYLENGTH=N) - CRYPTOPP_CONSTANT(MAX_KEYLENGTH=M) - CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH=D) - CRYPTOPP_CONSTANT(KEYLENGTH_MULTIPLE=Q) - CRYPTOPP_CONSTANT(IV_REQUIREMENT=IV_REQ) - CRYPTOPP_CONSTANT(IV_LENGTH=IV_L) - - static size_t CRYPTOPP_API StaticGetValidKeyLength(size_t n) - { - if (n < (size_t)MIN_KEYLENGTH) - return MIN_KEYLENGTH; - else if (n > (size_t)MAX_KEYLENGTH) - return (size_t)MAX_KEYLENGTH; - else - { - n += KEYLENGTH_MULTIPLE-1; - return n - n%KEYLENGTH_MULTIPLE; - } - } -}; - -/// support query of key length that's the same as another class -template -class SameKeyLengthAs -{ -public: - CRYPTOPP_CONSTANT(MIN_KEYLENGTH=T::MIN_KEYLENGTH) - CRYPTOPP_CONSTANT(MAX_KEYLENGTH=T::MAX_KEYLENGTH) - CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH=T::DEFAULT_KEYLENGTH) - CRYPTOPP_CONSTANT(IV_REQUIREMENT=IV_REQ) - CRYPTOPP_CONSTANT(IV_LENGTH=IV_L) - - static size_t CRYPTOPP_API StaticGetValidKeyLength(size_t keylength) - {return T::StaticGetValidKeyLength(keylength);} -}; - -// ************** implementation helper for SimpleKeyed *************** - -//! _ -template -class CRYPTOPP_NO_VTABLE SimpleKeyingInterfaceImpl : public BASE -{ -public: - size_t MinKeyLength() const {return INFO::MIN_KEYLENGTH;} - size_t MaxKeyLength() const {return (size_t)INFO::MAX_KEYLENGTH;} - size_t DefaultKeyLength() const {return INFO::DEFAULT_KEYLENGTH;} - size_t GetValidKeyLength(size_t n) const {return INFO::StaticGetValidKeyLength(n);} - SimpleKeyingInterface::IV_Requirement IVRequirement() const {return (SimpleKeyingInterface::IV_Requirement)INFO::IV_REQUIREMENT;} - unsigned int IVSize() const {return INFO::IV_LENGTH;} -}; - -template -class CRYPTOPP_NO_VTABLE BlockCipherImpl : public AlgorithmImpl > > -{ -public: - unsigned int BlockSize() const {return this->BLOCKSIZE;} -}; - -//! _ -template -class BlockCipherFinal : public ClonableImpl, BASE> -{ -public: - BlockCipherFinal() {} - BlockCipherFinal(const byte *key) - {this->SetKey(key, this->DEFAULT_KEYLENGTH);} - BlockCipherFinal(const byte *key, size_t length) - {this->SetKey(key, length);} - BlockCipherFinal(const byte *key, size_t length, unsigned int rounds) - {this->SetKeyWithRounds(key, length, rounds);} - - bool IsForwardTransformation() const {return DIR == ENCRYPTION;} -}; - -//! _ -template -class MessageAuthenticationCodeImpl : public AlgorithmImpl, INFO> -{ -}; - -//! _ -template -class MessageAuthenticationCodeFinal : public ClonableImpl, MessageAuthenticationCodeImpl > -{ -public: - MessageAuthenticationCodeFinal() {} - MessageAuthenticationCodeFinal(const byte *key) - {this->SetKey(key, this->DEFAULT_KEYLENGTH);} - MessageAuthenticationCodeFinal(const byte *key, size_t length) - {this->SetKey(key, length);} -}; - -// ************** documentation *************** - -//! These objects usually should not be used directly. See CipherModeDocumentation instead. -/*! Each class derived from this one defines two types, Encryption and Decryption, - both of which implement the BlockCipher interface. */ -struct BlockCipherDocumentation -{ - //! implements the BlockCipher interface - typedef BlockCipher Encryption; - //! implements the BlockCipher interface - typedef BlockCipher Decryption; -}; - -/*! \brief Each class derived from this one defines two types, Encryption and Decryption, - both of which implement the SymmetricCipher interface. Two types of classes derive - from this class: stream ciphers and block cipher modes. Stream ciphers can be used - alone, cipher mode classes need to be used with a block cipher. See CipherModeDocumentation - for more for information about using cipher modes and block ciphers. */ -struct SymmetricCipherDocumentation -{ - //! implements the SymmetricCipher interface - typedef SymmetricCipher Encryption; - //! implements the SymmetricCipher interface - typedef SymmetricCipher Decryption; -}; - -/*! \brief Each class derived from this one defines two types, Encryption and Decryption, - both of which implement the AuthenticatedSymmetricCipher interface. */ -struct AuthenticatedSymmetricCipherDocumentation -{ - //! implements the AuthenticatedSymmetricCipher interface - typedef AuthenticatedSymmetricCipher Encryption; - //! implements the AuthenticatedSymmetricCipher interface - typedef AuthenticatedSymmetricCipher Decryption; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/seed.cpp b/CryptoPP/seed.cpp deleted file mode 100644 index 101902dce..000000000 --- a/CryptoPP/seed.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// seed.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "seed.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -static const word32 s_kc[16] = { - 0x9e3779b9, 0x3c6ef373, 0x78dde6e6, 0xf1bbcdcc, 0xe3779b99, 0xc6ef3733, 0x8dde6e67, 0x1bbcdccf, - 0x3779b99e, 0x6ef3733c, 0xdde6e678, 0xbbcdccf1, 0x779b99e3, 0xef3733c6, 0xde6e678d, 0xbcdccf1b}; - -static const byte s_s0[256] = { - 0xA9, 0x85, 0xD6, 0xD3, 0x54, 0x1D, 0xAC, 0x25, 0x5D, 0x43, 0x18, 0x1E, 0x51, 0xFC, 0xCA, 0x63, 0x28, - 0x44, 0x20, 0x9D, 0xE0, 0xE2, 0xC8, 0x17, 0xA5, 0x8F, 0x03, 0x7B, 0xBB, 0x13, 0xD2, 0xEE, 0x70, 0x8C, - 0x3F, 0xA8, 0x32, 0xDD, 0xF6, 0x74, 0xEC, 0x95, 0x0B, 0x57, 0x5C, 0x5B, 0xBD, 0x01, 0x24, 0x1C, 0x73, - 0x98, 0x10, 0xCC, 0xF2, 0xD9, 0x2C, 0xE7, 0x72, 0x83, 0x9B, 0xD1, 0x86, 0xC9, 0x60, 0x50, 0xA3, 0xEB, - 0x0D, 0xB6, 0x9E, 0x4F, 0xB7, 0x5A, 0xC6, 0x78, 0xA6, 0x12, 0xAF, 0xD5, 0x61, 0xC3, 0xB4, 0x41, 0x52, - 0x7D, 0x8D, 0x08, 0x1F, 0x99, 0x00, 0x19, 0x04, 0x53, 0xF7, 0xE1, 0xFD, 0x76, 0x2F, 0x27, 0xB0, 0x8B, - 0x0E, 0xAB, 0xA2, 0x6E, 0x93, 0x4D, 0x69, 0x7C, 0x09, 0x0A, 0xBF, 0xEF, 0xF3, 0xC5, 0x87, 0x14, 0xFE, - 0x64, 0xDE, 0x2E, 0x4B, 0x1A, 0x06, 0x21, 0x6B, 0x66, 0x02, 0xF5, 0x92, 0x8A, 0x0C, 0xB3, 0x7E, 0xD0, - 0x7A, 0x47, 0x96, 0xE5, 0x26, 0x80, 0xAD, 0xDF, 0xA1, 0x30, 0x37, 0xAE, 0x36, 0x15, 0x22, 0x38, 0xF4, - 0xA7, 0x45, 0x4C, 0x81, 0xE9, 0x84, 0x97, 0x35, 0xCB, 0xCE, 0x3C, 0x71, 0x11, 0xC7, 0x89, 0x75, 0xFB, - 0xDA, 0xF8, 0x94, 0x59, 0x82, 0xC4, 0xFF, 0x49, 0x39, 0x67, 0xC0, 0xCF, 0xD7, 0xB8, 0x0F, 0x8E, 0x42, - 0x23, 0x91, 0x6C, 0xDB, 0xA4, 0x34, 0xF1, 0x48, 0xC2, 0x6F, 0x3D, 0x2D, 0x40, 0xBE, 0x3E, 0xBC, 0xC1, - 0xAA, 0xBA, 0x4E, 0x55, 0x3B, 0xDC, 0x68, 0x7F, 0x9C, 0xD8, 0x4A, 0x56, 0x77, 0xA0, 0xED, 0x46, 0xB5, - 0x2B, 0x65, 0xFA, 0xE3, 0xB9, 0xB1, 0x9F, 0x5E, 0xF9, 0xE6, 0xB2, 0x31, 0xEA, 0x6D, 0x5F, 0xE4, 0xF0, - 0xCD, 0x88, 0x16, 0x3A, 0x58, 0xD4, 0x62, 0x29, 0x07, 0x33, 0xE8, 0x1B, 0x05, 0x79, 0x90, 0x6A, 0x2A, - 0x9A}; - -static const byte s_s1[256] = { - 0x38, 0xE8, 0x2D, 0xA6, 0xCF, 0xDE, 0xB3, 0xB8, 0xAF, 0x60, 0x55, 0xC7, 0x44, 0x6F, 0x6B, 0x5B, 0xC3, - 0x62, 0x33, 0xB5, 0x29, 0xA0, 0xE2, 0xA7, 0xD3, 0x91, 0x11, 0x06, 0x1C, 0xBC, 0x36, 0x4B, 0xEF, 0x88, - 0x6C, 0xA8, 0x17, 0xC4, 0x16, 0xF4, 0xC2, 0x45, 0xE1, 0xD6, 0x3F, 0x3D, 0x8E, 0x98, 0x28, 0x4E, 0xF6, - 0x3E, 0xA5, 0xF9, 0x0D, 0xDF, 0xD8, 0x2B, 0x66, 0x7A, 0x27, 0x2F, 0xF1, 0x72, 0x42, 0xD4, 0x41, 0xC0, - 0x73, 0x67, 0xAC, 0x8B, 0xF7, 0xAD, 0x80, 0x1F, 0xCA, 0x2C, 0xAA, 0x34, 0xD2, 0x0B, 0xEE, 0xE9, 0x5D, - 0x94, 0x18, 0xF8, 0x57, 0xAE, 0x08, 0xC5, 0x13, 0xCD, 0x86, 0xB9, 0xFF, 0x7D, 0xC1, 0x31, 0xF5, 0x8A, - 0x6A, 0xB1, 0xD1, 0x20, 0xD7, 0x02, 0x22, 0x04, 0x68, 0x71, 0x07, 0xDB, 0x9D, 0x99, 0x61, 0xBE, 0xE6, - 0x59, 0xDD, 0x51, 0x90, 0xDC, 0x9A, 0xA3, 0xAB, 0xD0, 0x81, 0x0F, 0x47, 0x1A, 0xE3, 0xEC, 0x8D, 0xBF, - 0x96, 0x7B, 0x5C, 0xA2, 0xA1, 0x63, 0x23, 0x4D, 0xC8, 0x9E, 0x9C, 0x3A, 0x0C, 0x2E, 0xBA, 0x6E, 0x9F, - 0x5A, 0xF2, 0x92, 0xF3, 0x49, 0x78, 0xCC, 0x15, 0xFB, 0x70, 0x75, 0x7F, 0x35, 0x10, 0x03, 0x64, 0x6D, - 0xC6, 0x74, 0xD5, 0xB4, 0xEA, 0x09, 0x76, 0x19, 0xFE, 0x40, 0x12, 0xE0, 0xBD, 0x05, 0xFA, 0x01, 0xF0, - 0x2A, 0x5E, 0xA9, 0x56, 0x43, 0x85, 0x14, 0x89, 0x9B, 0xB0, 0xE5, 0x48, 0x79, 0x97, 0xFC, 0x1E, 0x82, - 0x21, 0x8C, 0x1B, 0x5F, 0x77, 0x54, 0xB2, 0x1D, 0x25, 0x4F, 0x00, 0x46, 0xED, 0x58, 0x52, 0xEB, 0x7E, - 0xDA, 0xC9, 0xFD, 0x30, 0x95, 0x65, 0x3C, 0xB6, 0xE4, 0xBB, 0x7C, 0x0E, 0x50, 0x39, 0x26, 0x32, 0x84, - 0x69, 0x93, 0x37, 0xE7, 0x24, 0xA4, 0xCB, 0x53, 0x0A, 0x87, 0xD9, 0x4C, 0x83, 0x8F, 0xCE, 0x3B, 0x4A, - 0xB7}; - -#define SS0(x) ((s_s0[x]*0x01010101UL) & 0x3FCFF3FC) -#define SS1(x) ((s_s1[x]*0x01010101UL) & 0xFC3FCFF3) -#define SS2(x) ((s_s0[x]*0x01010101UL) & 0xF3FC3FCF) -#define SS3(x) ((s_s1[x]*0x01010101UL) & 0xCFF3FC3F) -#define G(x) (SS0(GETBYTE(x, 0)) ^ SS1(GETBYTE(x, 1)) ^ SS2(GETBYTE(x, 2)) ^ SS3(GETBYTE(x, 3))) - -void SEED::Base::UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms) -{ - AssertValidKeyLength(length); - - word64 key01, key23; - GetBlock get(userKey); - get(key01)(key23); - word32 *k = m_k; - size_t kInc = 2; - if (!IsForwardTransformation()) - { - k = k+30; - kInc = 0-kInc; - } - - for (int i=0; i>32) + word32(key23>>32) - s_kc[i]; - word32 t1 = word32(key01) - word32(key23) + s_kc[i]; - k[0] = G(t0); - k[1] = G(t1); - k+=kInc; - if (i&1) - key23 = rotlFixed(key23, 8); - else - key01 = rotrFixed(key01, 8); - } -} - -void SEED::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - typedef BlockGetAndPut Block; - word32 a0, a1, b0, b1, t0, t1; - Block::Get(inBlock)(a0)(a1)(b0)(b1); - - for (int i=0; i, public FixedKeyLength<16>, public FixedRounds<16> -{ - static const char *StaticAlgorithmName() {return "SEED";} -}; - -/// SEED -class SEED : public SEED_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl - { - public: - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms); - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - - protected: - FixedSizeSecBlock m_k; - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/sha.cpp b/CryptoPP/sha.cpp deleted file mode 100644 index df947ad16..000000000 --- a/CryptoPP/sha.cpp +++ /dev/null @@ -1,900 +0,0 @@ -// sha.cpp - modified by Wei Dai from Steve Reid's public domain sha1.c - -// Steve Reid implemented SHA-1. Wei Dai implemented SHA-2. -// Both are in the public domain. - -// use "cl /EP /P /DCRYPTOPP_GENERATE_X64_MASM sha.cpp" to generate MASM code - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS -#ifndef CRYPTOPP_GENERATE_X64_MASM - -#include "sha.h" -#include "misc.h" -#include "cpu.h" - -NAMESPACE_BEGIN(CryptoPP) - -// start of Steve Reid's code - -#define blk0(i) (W[i] = data[i]) -#define blk1(i) (W[i&15] = rotlFixed(W[(i+13)&15]^W[(i+8)&15]^W[(i+2)&15]^W[i&15],1)) - -void SHA1::InitState(HashWordType *state) -{ - state[0] = 0x67452301L; - state[1] = 0xEFCDAB89L; - state[2] = 0x98BADCFEL; - state[3] = 0x10325476L; - state[4] = 0xC3D2E1F0L; -} - -#define f1(x,y,z) (z^(x&(y^z))) -#define f2(x,y,z) (x^y^z) -#define f3(x,y,z) ((x&y)|(z&(x|y))) -#define f4(x,y,z) (x^y^z) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); -#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); -#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1+rotlFixed(v,5);w=rotlFixed(w,30); -#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDC+rotlFixed(v,5);w=rotlFixed(w,30); -#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6+rotlFixed(v,5);w=rotlFixed(w,30); - -void SHA1::Transform(word32 *state, const word32 *data) -{ - word32 W[16]; - /* Copy context->state[] to working vars */ - word32 a = state[0]; - word32 b = state[1]; - word32 c = state[2]; - word32 d = state[3]; - word32 e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; -} - -// end of Steve Reid's code - -// ************************************************************* - -void SHA224::InitState(HashWordType *state) -{ - static const word32 s[8] = {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4}; - memcpy(state, s, sizeof(s)); -} - -void SHA256::InitState(HashWordType *state) -{ - static const word32 s[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - memcpy(state, s, sizeof(s)); -} - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE -CRYPTOPP_ALIGN_DATA(16) extern const word32 SHA256_K[64] CRYPTOPP_SECTION_ALIGN16 = { -#else -extern const word32 SHA256_K[64] = { -#endif - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM - -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_GENERATE_X64_MASM) - -#pragma warning(disable: 4731) // frame pointer register 'ebp' modified by inline assembly code - -static void CRYPTOPP_FASTCALL X86_SHA256_HashBlocks(word32 *state, const word32 *data, size_t len -#if defined(_MSC_VER) && (_MSC_VER == 1200) - , ... // VC60 workaround: prevent VC 6 from inlining this function -#endif - ) -{ -#if defined(_MSC_VER) && (_MSC_VER == 1200) - AS2(mov ecx, [state]) - AS2(mov edx, [data]) -#endif - - #define LOCALS_SIZE 8*4 + 16*4 + 4*WORD_SZ - #define H(i) [BASE+ASM_MOD(1024+7-(i),8)*4] - #define G(i) H(i+1) - #define F(i) H(i+2) - #define E(i) H(i+3) - #define D(i) H(i+4) - #define C(i) H(i+5) - #define B(i) H(i+6) - #define A(i) H(i+7) - #define Wt(i) BASE+8*4+ASM_MOD(1024+15-(i),16)*4 - #define Wt_2(i) Wt((i)-2) - #define Wt_15(i) Wt((i)-15) - #define Wt_7(i) Wt((i)-7) - #define K_END [BASE+8*4+16*4+0*WORD_SZ] - #define STATE_SAVE [BASE+8*4+16*4+1*WORD_SZ] - #define DATA_SAVE [BASE+8*4+16*4+2*WORD_SZ] - #define DATA_END [BASE+8*4+16*4+3*WORD_SZ] - #define Kt(i) WORD_REG(si)+(i)*4 -#if CRYPTOPP_BOOL_X86 - #define BASE esp+4 -#elif defined(__GNUC__) - #define BASE r8 -#else - #define BASE rsp -#endif - -#define RA0(i, edx, edi) \ - AS2( add edx, [Kt(i)] )\ - AS2( add edx, [Wt(i)] )\ - AS2( add edx, H(i) )\ - -#define RA1(i, edx, edi) - -#define RB0(i, edx, edi) - -#define RB1(i, edx, edi) \ - AS2( mov AS_REG_7d, [Wt_2(i)] )\ - AS2( mov edi, [Wt_15(i)])\ - AS2( mov ebx, AS_REG_7d )\ - AS2( shr AS_REG_7d, 10 )\ - AS2( ror ebx, 17 )\ - AS2( xor AS_REG_7d, ebx )\ - AS2( ror ebx, 2 )\ - AS2( xor ebx, AS_REG_7d )/* s1(W_t-2) */\ - AS2( add ebx, [Wt_7(i)])\ - AS2( mov AS_REG_7d, edi )\ - AS2( shr AS_REG_7d, 3 )\ - AS2( ror edi, 7 )\ - AS2( add ebx, [Wt(i)])/* s1(W_t-2) + W_t-7 + W_t-16 */\ - AS2( xor AS_REG_7d, edi )\ - AS2( add edx, [Kt(i)])\ - AS2( ror edi, 11 )\ - AS2( add edx, H(i) )\ - AS2( xor AS_REG_7d, edi )/* s0(W_t-15) */\ - AS2( add AS_REG_7d, ebx )/* W_t = s1(W_t-2) + W_t-7 + s0(W_t-15) W_t-16*/\ - AS2( mov [Wt(i)], AS_REG_7d)\ - AS2( add edx, AS_REG_7d )\ - -#define ROUND(i, r, eax, ecx, edi, edx)\ - /* in: edi = E */\ - /* unused: eax, ecx, temp: ebx, AS_REG_7d, out: edx = T1 */\ - AS2( mov edx, F(i) )\ - AS2( xor edx, G(i) )\ - AS2( and edx, edi )\ - AS2( xor edx, G(i) )/* Ch(E,F,G) = (G^(E&(F^G))) */\ - AS2( mov AS_REG_7d, edi )\ - AS2( ror edi, 6 )\ - AS2( ror AS_REG_7d, 25 )\ - RA##r(i, edx, edi )/* H + Wt + Kt + Ch(E,F,G) */\ - AS2( xor AS_REG_7d, edi )\ - AS2( ror edi, 5 )\ - AS2( xor AS_REG_7d, edi )/* S1(E) */\ - AS2( add edx, AS_REG_7d )/* T1 = S1(E) + Ch(E,F,G) + H + Wt + Kt */\ - RB##r(i, edx, edi )/* H + Wt + Kt + Ch(E,F,G) */\ - /* in: ecx = A, eax = B^C, edx = T1 */\ - /* unused: edx, temp: ebx, AS_REG_7d, out: eax = A, ecx = B^C, edx = E */\ - AS2( mov ebx, ecx )\ - AS2( xor ecx, B(i) )/* A^B */\ - AS2( and eax, ecx )\ - AS2( xor eax, B(i) )/* Maj(A,B,C) = B^((A^B)&(B^C) */\ - AS2( mov AS_REG_7d, ebx )\ - AS2( ror ebx, 2 )\ - AS2( add eax, edx )/* T1 + Maj(A,B,C) */\ - AS2( add edx, D(i) )\ - AS2( mov D(i), edx )\ - AS2( ror AS_REG_7d, 22 )\ - AS2( xor AS_REG_7d, ebx )\ - AS2( ror ebx, 11 )\ - AS2( xor AS_REG_7d, ebx )\ - AS2( add eax, AS_REG_7d )/* T1 + S0(A) + Maj(A,B,C) */\ - AS2( mov H(i), eax )\ - -#define SWAP_COPY(i) \ - AS2( mov WORD_REG(bx), [WORD_REG(dx)+i*WORD_SZ])\ - AS1( bswap WORD_REG(bx))\ - AS2( mov [Wt(i*(1+CRYPTOPP_BOOL_X64)+CRYPTOPP_BOOL_X64)], WORD_REG(bx)) - -#if defined(__GNUC__) - #if CRYPTOPP_BOOL_X64 - FixedSizeAlignedSecBlock workspace; - #endif - __asm__ __volatile__ - ( - #if CRYPTOPP_BOOL_X64 - "lea %4, %%r8;" - #endif - ".intel_syntax noprefix;" -#elif defined(CRYPTOPP_GENERATE_X64_MASM) - ALIGN 8 - X86_SHA256_HashBlocks PROC FRAME - rex_push_reg rsi - push_reg rdi - push_reg rbx - push_reg rbp - alloc_stack(LOCALS_SIZE+8) - .endprolog - mov rdi, r8 - lea rsi, [?SHA256_K@CryptoPP@@3QBIB + 48*4] -#endif - -#if CRYPTOPP_BOOL_X86 - #ifndef __GNUC__ - AS2( mov edi, [len]) - AS2( lea WORD_REG(si), [SHA256_K+48*4]) - #endif - #if !defined(_MSC_VER) || (_MSC_VER < 1400) - AS_PUSH_IF86(bx) - #endif - - AS_PUSH_IF86(bp) - AS2( mov ebx, esp) - AS2( and esp, -16) - AS2( sub WORD_REG(sp), LOCALS_SIZE) - AS_PUSH_IF86(bx) -#endif - AS2( mov STATE_SAVE, WORD_REG(cx)) - AS2( mov DATA_SAVE, WORD_REG(dx)) - AS2( lea WORD_REG(ax), [WORD_REG(di) + WORD_REG(dx)]) - AS2( mov DATA_END, WORD_REG(ax)) - AS2( mov K_END, WORD_REG(si)) - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE -#if CRYPTOPP_BOOL_X86 - AS2( test edi, 1) - ASJ( jnz, 2, f) - AS1( dec DWORD PTR K_END) -#endif - AS2( movdqa xmm0, XMMWORD_PTR [WORD_REG(cx)+0*16]) - AS2( movdqa xmm1, XMMWORD_PTR [WORD_REG(cx)+1*16]) -#endif - -#if CRYPTOPP_BOOL_X86 -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - ASJ( jmp, 0, f) -#endif - ASL(2) // non-SSE2 - AS2( mov esi, ecx) - AS2( lea edi, A(0)) - AS2( mov ecx, 8) - AS1( rep movsd) - AS2( mov esi, K_END) - ASJ( jmp, 3, f) -#endif - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - ASL(0) - AS2( movdqa E(0), xmm1) - AS2( movdqa A(0), xmm0) -#endif -#if CRYPTOPP_BOOL_X86 - ASL(3) -#endif - AS2( sub WORD_REG(si), 48*4) - SWAP_COPY(0) SWAP_COPY(1) SWAP_COPY(2) SWAP_COPY(3) - SWAP_COPY(4) SWAP_COPY(5) SWAP_COPY(6) SWAP_COPY(7) -#if CRYPTOPP_BOOL_X86 - SWAP_COPY(8) SWAP_COPY(9) SWAP_COPY(10) SWAP_COPY(11) - SWAP_COPY(12) SWAP_COPY(13) SWAP_COPY(14) SWAP_COPY(15) -#endif - AS2( mov edi, E(0)) // E - AS2( mov eax, B(0)) // B - AS2( xor eax, C(0)) // B^C - AS2( mov ecx, A(0)) // A - - ROUND(0, 0, eax, ecx, edi, edx) - ROUND(1, 0, ecx, eax, edx, edi) - ROUND(2, 0, eax, ecx, edi, edx) - ROUND(3, 0, ecx, eax, edx, edi) - ROUND(4, 0, eax, ecx, edi, edx) - ROUND(5, 0, ecx, eax, edx, edi) - ROUND(6, 0, eax, ecx, edi, edx) - ROUND(7, 0, ecx, eax, edx, edi) - ROUND(8, 0, eax, ecx, edi, edx) - ROUND(9, 0, ecx, eax, edx, edi) - ROUND(10, 0, eax, ecx, edi, edx) - ROUND(11, 0, ecx, eax, edx, edi) - ROUND(12, 0, eax, ecx, edi, edx) - ROUND(13, 0, ecx, eax, edx, edi) - ROUND(14, 0, eax, ecx, edi, edx) - ROUND(15, 0, ecx, eax, edx, edi) - - ASL(1) - AS2(add WORD_REG(si), 4*16) - ROUND(0, 1, eax, ecx, edi, edx) - ROUND(1, 1, ecx, eax, edx, edi) - ROUND(2, 1, eax, ecx, edi, edx) - ROUND(3, 1, ecx, eax, edx, edi) - ROUND(4, 1, eax, ecx, edi, edx) - ROUND(5, 1, ecx, eax, edx, edi) - ROUND(6, 1, eax, ecx, edi, edx) - ROUND(7, 1, ecx, eax, edx, edi) - ROUND(8, 1, eax, ecx, edi, edx) - ROUND(9, 1, ecx, eax, edx, edi) - ROUND(10, 1, eax, ecx, edi, edx) - ROUND(11, 1, ecx, eax, edx, edi) - ROUND(12, 1, eax, ecx, edi, edx) - ROUND(13, 1, ecx, eax, edx, edi) - ROUND(14, 1, eax, ecx, edi, edx) - ROUND(15, 1, ecx, eax, edx, edi) - AS2( cmp WORD_REG(si), K_END) - ASJ( jb, 1, b) - - AS2( mov WORD_REG(dx), DATA_SAVE) - AS2( add WORD_REG(dx), 64) - AS2( mov AS_REG_7, STATE_SAVE) - AS2( mov DATA_SAVE, WORD_REG(dx)) - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE -#if CRYPTOPP_BOOL_X86 - AS2( test DWORD PTR K_END, 1) - ASJ( jz, 4, f) -#endif - AS2( movdqa xmm1, XMMWORD_PTR [AS_REG_7+1*16]) - AS2( movdqa xmm0, XMMWORD_PTR [AS_REG_7+0*16]) - AS2( paddd xmm1, E(0)) - AS2( paddd xmm0, A(0)) - AS2( movdqa [AS_REG_7+1*16], xmm1) - AS2( movdqa [AS_REG_7+0*16], xmm0) - AS2( cmp WORD_REG(dx), DATA_END) - ASJ( jb, 0, b) -#endif - -#if CRYPTOPP_BOOL_X86 -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - ASJ( jmp, 5, f) - ASL(4) // non-SSE2 -#endif - AS2( add [AS_REG_7+0*4], ecx) // A - AS2( add [AS_REG_7+4*4], edi) // E - AS2( mov eax, B(0)) - AS2( mov ebx, C(0)) - AS2( mov ecx, D(0)) - AS2( add [AS_REG_7+1*4], eax) - AS2( add [AS_REG_7+2*4], ebx) - AS2( add [AS_REG_7+3*4], ecx) - AS2( mov eax, F(0)) - AS2( mov ebx, G(0)) - AS2( mov ecx, H(0)) - AS2( add [AS_REG_7+5*4], eax) - AS2( add [AS_REG_7+6*4], ebx) - AS2( add [AS_REG_7+7*4], ecx) - AS2( mov ecx, AS_REG_7d) - AS2( cmp WORD_REG(dx), DATA_END) - ASJ( jb, 2, b) -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - ASL(5) -#endif -#endif - - AS_POP_IF86(sp) - AS_POP_IF86(bp) - #if !defined(_MSC_VER) || (_MSC_VER < 1400) - AS_POP_IF86(bx) - #endif - -#ifdef CRYPTOPP_GENERATE_X64_MASM - add rsp, LOCALS_SIZE+8 - pop rbp - pop rbx - pop rdi - pop rsi - ret - X86_SHA256_HashBlocks ENDP -#endif - -#ifdef __GNUC__ - ".att_syntax prefix;" - : - : "c" (state), "d" (data), "S" (SHA256_K+48), "D" (len) - #if CRYPTOPP_BOOL_X64 - , "m" (workspace[0]) - #endif - : "memory", "cc", "%eax" - #if CRYPTOPP_BOOL_X64 - , "%rbx", "%r8", "%r10" - #endif - ); -#endif -} - -#endif // #if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_GENERATE_X64_MASM) - -#ifndef CRYPTOPP_GENERATE_X64_MASM - -#ifdef CRYPTOPP_X64_MASM_AVAILABLE -extern "C" { -void CRYPTOPP_FASTCALL X86_SHA256_HashBlocks(word32 *state, const word32 *data, size_t len); -} -#endif - -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) - -size_t SHA256::HashMultipleBlocks(const word32 *input, size_t length) -{ - X86_SHA256_HashBlocks(m_state, input, (length&(size_t(0)-BLOCKSIZE)) - !HasSSE2()); - return length % BLOCKSIZE; -} - -size_t SHA224::HashMultipleBlocks(const word32 *input, size_t length) -{ - X86_SHA256_HashBlocks(m_state, input, (length&(size_t(0)-BLOCKSIZE)) - !HasSSE2()); - return length % BLOCKSIZE; -} - -#endif - -#define blk2(i) (W[i&15]+=s1(W[(i-2)&15])+W[(i-7)&15]+s0(W[(i-15)&15])) - -#define Ch(x,y,z) (z^(x&(y^z))) -#define Maj(x,y,z) (y^((x^y)&(y^z))) - -#define a(i) T[(0-i)&7] -#define b(i) T[(1-i)&7] -#define c(i) T[(2-i)&7] -#define d(i) T[(3-i)&7] -#define e(i) T[(4-i)&7] -#define f(i) T[(5-i)&7] -#define g(i) T[(6-i)&7] -#define h(i) T[(7-i)&7] - -#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA256_K[i+j]+(j?blk2(i):blk0(i));\ - d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) - -// for SHA256 -#define S0(x) (rotrFixed(x,2)^rotrFixed(x,13)^rotrFixed(x,22)) -#define S1(x) (rotrFixed(x,6)^rotrFixed(x,11)^rotrFixed(x,25)) -#define s0(x) (rotrFixed(x,7)^rotrFixed(x,18)^(x>>3)) -#define s1(x) (rotrFixed(x,17)^rotrFixed(x,19)^(x>>10)) - -void SHA256::Transform(word32 *state, const word32 *data) -{ - word32 W[16]; -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) - // this byte reverse is a waste of time, but this function is only called by MDC - ByteReverse(W, data, BLOCKSIZE); - X86_SHA256_HashBlocks(state, W, BLOCKSIZE - !HasSSE2()); -#else - word32 T[8]; - /* Copy context->state[] to working vars */ - memcpy(T, state, sizeof(T)); - /* 64 operations, partially loop unrolled */ - for (unsigned int j=0; j<64; j+=16) - { - R( 0); R( 1); R( 2); R( 3); - R( 4); R( 5); R( 6); R( 7); - R( 8); R( 9); R(10); R(11); - R(12); R(13); R(14); R(15); - } - /* Add the working vars back into context.state[] */ - state[0] += a(0); - state[1] += b(0); - state[2] += c(0); - state[3] += d(0); - state[4] += e(0); - state[5] += f(0); - state[6] += g(0); - state[7] += h(0); -#endif -} - -/* -// smaller but slower -void SHA256::Transform(word32 *state, const word32 *data) -{ - word32 T[20]; - word32 W[32]; - unsigned int i = 0, j = 0; - word32 *t = T+8; - - memcpy(t, state, 8*4); - word32 e = t[4], a = t[0]; - - do - { - word32 w = data[j]; - W[j] = w; - w += SHA256_K[j]; - w += t[7]; - w += S1(e); - w += Ch(e, t[5], t[6]); - e = t[3] + w; - t[3] = t[3+8] = e; - w += S0(t[0]); - a = w + Maj(a, t[1], t[2]); - t[-1] = t[7] = a; - --t; - ++j; - if (j%8 == 0) - t += 8; - } while (j<16); - - do - { - i = j&0xf; - word32 w = s1(W[i+16-2]) + s0(W[i+16-15]) + W[i] + W[i+16-7]; - W[i+16] = W[i] = w; - w += SHA256_K[j]; - w += t[7]; - w += S1(e); - w += Ch(e, t[5], t[6]); - e = t[3] + w; - t[3] = t[3+8] = e; - w += S0(t[0]); - a = w + Maj(a, t[1], t[2]); - t[-1] = t[7] = a; - - w = s1(W[(i+1)+16-2]) + s0(W[(i+1)+16-15]) + W[(i+1)] + W[(i+1)+16-7]; - W[(i+1)+16] = W[(i+1)] = w; - w += SHA256_K[j+1]; - w += (t-1)[7]; - w += S1(e); - w += Ch(e, (t-1)[5], (t-1)[6]); - e = (t-1)[3] + w; - (t-1)[3] = (t-1)[3+8] = e; - w += S0((t-1)[0]); - a = w + Maj(a, (t-1)[1], (t-1)[2]); - (t-1)[-1] = (t-1)[7] = a; - - t-=2; - j+=2; - if (j%8 == 0) - t += 8; - } while (j<64); - - state[0] += a; - state[1] += t[1]; - state[2] += t[2]; - state[3] += t[3]; - state[4] += e; - state[5] += t[5]; - state[6] += t[6]; - state[7] += t[7]; -} -*/ - -#undef S0 -#undef S1 -#undef s0 -#undef s1 -#undef R - -// ************************************************************* - -void SHA384::InitState(HashWordType *state) -{ - static const word64 s[8] = { - W64LIT(0xcbbb9d5dc1059ed8), W64LIT(0x629a292a367cd507), - W64LIT(0x9159015a3070dd17), W64LIT(0x152fecd8f70e5939), - W64LIT(0x67332667ffc00b31), W64LIT(0x8eb44a8768581511), - W64LIT(0xdb0c2e0d64f98fa7), W64LIT(0x47b5481dbefa4fa4)}; - memcpy(state, s, sizeof(s)); -} - -void SHA512::InitState(HashWordType *state) -{ - static const word64 s[8] = { - W64LIT(0x6a09e667f3bcc908), W64LIT(0xbb67ae8584caa73b), - W64LIT(0x3c6ef372fe94f82b), W64LIT(0xa54ff53a5f1d36f1), - W64LIT(0x510e527fade682d1), W64LIT(0x9b05688c2b3e6c1f), - W64LIT(0x1f83d9abfb41bd6b), W64LIT(0x5be0cd19137e2179)}; - memcpy(state, s, sizeof(s)); -} - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 -CRYPTOPP_ALIGN_DATA(16) static const word64 SHA512_K[80] CRYPTOPP_SECTION_ALIGN16 = { -#else -static const word64 SHA512_K[80] = { -#endif - W64LIT(0x428a2f98d728ae22), W64LIT(0x7137449123ef65cd), - W64LIT(0xb5c0fbcfec4d3b2f), W64LIT(0xe9b5dba58189dbbc), - W64LIT(0x3956c25bf348b538), W64LIT(0x59f111f1b605d019), - W64LIT(0x923f82a4af194f9b), W64LIT(0xab1c5ed5da6d8118), - W64LIT(0xd807aa98a3030242), W64LIT(0x12835b0145706fbe), - W64LIT(0x243185be4ee4b28c), W64LIT(0x550c7dc3d5ffb4e2), - W64LIT(0x72be5d74f27b896f), W64LIT(0x80deb1fe3b1696b1), - W64LIT(0x9bdc06a725c71235), W64LIT(0xc19bf174cf692694), - W64LIT(0xe49b69c19ef14ad2), W64LIT(0xefbe4786384f25e3), - W64LIT(0x0fc19dc68b8cd5b5), W64LIT(0x240ca1cc77ac9c65), - W64LIT(0x2de92c6f592b0275), W64LIT(0x4a7484aa6ea6e483), - W64LIT(0x5cb0a9dcbd41fbd4), W64LIT(0x76f988da831153b5), - W64LIT(0x983e5152ee66dfab), W64LIT(0xa831c66d2db43210), - W64LIT(0xb00327c898fb213f), W64LIT(0xbf597fc7beef0ee4), - W64LIT(0xc6e00bf33da88fc2), W64LIT(0xd5a79147930aa725), - W64LIT(0x06ca6351e003826f), W64LIT(0x142929670a0e6e70), - W64LIT(0x27b70a8546d22ffc), W64LIT(0x2e1b21385c26c926), - W64LIT(0x4d2c6dfc5ac42aed), W64LIT(0x53380d139d95b3df), - W64LIT(0x650a73548baf63de), W64LIT(0x766a0abb3c77b2a8), - W64LIT(0x81c2c92e47edaee6), W64LIT(0x92722c851482353b), - W64LIT(0xa2bfe8a14cf10364), W64LIT(0xa81a664bbc423001), - W64LIT(0xc24b8b70d0f89791), W64LIT(0xc76c51a30654be30), - W64LIT(0xd192e819d6ef5218), W64LIT(0xd69906245565a910), - W64LIT(0xf40e35855771202a), W64LIT(0x106aa07032bbd1b8), - W64LIT(0x19a4c116b8d2d0c8), W64LIT(0x1e376c085141ab53), - W64LIT(0x2748774cdf8eeb99), W64LIT(0x34b0bcb5e19b48a8), - W64LIT(0x391c0cb3c5c95a63), W64LIT(0x4ed8aa4ae3418acb), - W64LIT(0x5b9cca4f7763e373), W64LIT(0x682e6ff3d6b2b8a3), - W64LIT(0x748f82ee5defb2fc), W64LIT(0x78a5636f43172f60), - W64LIT(0x84c87814a1f0ab72), W64LIT(0x8cc702081a6439ec), - W64LIT(0x90befffa23631e28), W64LIT(0xa4506cebde82bde9), - W64LIT(0xbef9a3f7b2c67915), W64LIT(0xc67178f2e372532b), - W64LIT(0xca273eceea26619c), W64LIT(0xd186b8c721c0c207), - W64LIT(0xeada7dd6cde0eb1e), W64LIT(0xf57d4f7fee6ed178), - W64LIT(0x06f067aa72176fba), W64LIT(0x0a637dc5a2c898a6), - W64LIT(0x113f9804bef90dae), W64LIT(0x1b710b35131c471b), - W64LIT(0x28db77f523047d84), W64LIT(0x32caab7b40c72493), - W64LIT(0x3c9ebe0a15c9bebc), W64LIT(0x431d67c49c100d4c), - W64LIT(0x4cc5d4becb3e42b6), W64LIT(0x597f299cfc657e2a), - W64LIT(0x5fcb6fab3ad6faec), W64LIT(0x6c44198c4a475817) -}; - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 -// put assembly version in separate function, otherwise MSVC 2005 SP1 doesn't generate correct code for the non-assembly version -CRYPTOPP_NAKED static void CRYPTOPP_FASTCALL SHA512_SSE2_Transform(word64 *state, const word64 *data) -{ -#ifdef __GNUC__ - __asm__ __volatile__ - ( - ".intel_syntax noprefix;" - AS1( push ebx) - AS2( mov ebx, eax) -#else - AS1( push ebx) - AS1( push esi) - AS1( push edi) - AS2( lea ebx, SHA512_K) -#endif - - AS2( mov eax, esp) - AS2( and esp, 0xfffffff0) - AS2( sub esp, 27*16) // 17*16 for expanded data, 20*8 for state - AS1( push eax) - AS2( xor eax, eax) - AS2( lea edi, [esp+4+8*8]) // start at middle of state buffer. will decrement pointer each round to avoid copying - AS2( lea esi, [esp+4+20*8+8]) // 16-byte alignment, then add 8 - - AS2( movdqa xmm0, [ecx+0*16]) - AS2( movdq2q mm4, xmm0) - AS2( movdqa [edi+0*16], xmm0) - AS2( movdqa xmm0, [ecx+1*16]) - AS2( movdqa [edi+1*16], xmm0) - AS2( movdqa xmm0, [ecx+2*16]) - AS2( movdq2q mm5, xmm0) - AS2( movdqa [edi+2*16], xmm0) - AS2( movdqa xmm0, [ecx+3*16]) - AS2( movdqa [edi+3*16], xmm0) - ASJ( jmp, 0, f) - -#define SSE2_S0_S1(r, a, b, c) \ - AS2( movq mm6, r)\ - AS2( psrlq r, a)\ - AS2( movq mm7, r)\ - AS2( psllq mm6, 64-c)\ - AS2( pxor mm7, mm6)\ - AS2( psrlq r, b-a)\ - AS2( pxor mm7, r)\ - AS2( psllq mm6, c-b)\ - AS2( pxor mm7, mm6)\ - AS2( psrlq r, c-b)\ - AS2( pxor r, mm7)\ - AS2( psllq mm6, b-a)\ - AS2( pxor r, mm6) - -#define SSE2_s0(r, a, b, c) \ - AS2( movdqa xmm6, r)\ - AS2( psrlq r, a)\ - AS2( movdqa xmm7, r)\ - AS2( psllq xmm6, 64-c)\ - AS2( pxor xmm7, xmm6)\ - AS2( psrlq r, b-a)\ - AS2( pxor xmm7, r)\ - AS2( psrlq r, c-b)\ - AS2( pxor r, xmm7)\ - AS2( psllq xmm6, c-a)\ - AS2( pxor r, xmm6) - -#define SSE2_s1(r, a, b, c) \ - AS2( movdqa xmm6, r)\ - AS2( psrlq r, a)\ - AS2( movdqa xmm7, r)\ - AS2( psllq xmm6, 64-c)\ - AS2( pxor xmm7, xmm6)\ - AS2( psrlq r, b-a)\ - AS2( pxor xmm7, r)\ - AS2( psllq xmm6, c-b)\ - AS2( pxor xmm7, xmm6)\ - AS2( psrlq r, c-b)\ - AS2( pxor r, xmm7) - - ASL(SHA512_Round) - // k + w is in mm0, a is in mm4, e is in mm5 - AS2( paddq mm0, [edi+7*8]) // h - AS2( movq mm2, [edi+5*8]) // f - AS2( movq mm3, [edi+6*8]) // g - AS2( pxor mm2, mm3) - AS2( pand mm2, mm5) - SSE2_S0_S1(mm5,14,18,41) - AS2( pxor mm2, mm3) - AS2( paddq mm0, mm2) // h += Ch(e,f,g) - AS2( paddq mm5, mm0) // h += S1(e) - AS2( movq mm2, [edi+1*8]) // b - AS2( movq mm1, mm2) - AS2( por mm2, mm4) - AS2( pand mm2, [edi+2*8]) // c - AS2( pand mm1, mm4) - AS2( por mm1, mm2) - AS2( paddq mm1, mm5) // temp = h + Maj(a,b,c) - AS2( paddq mm5, [edi+3*8]) // e = d + h - AS2( movq [edi+3*8], mm5) - AS2( movq [edi+11*8], mm5) - SSE2_S0_S1(mm4,28,34,39) // S0(a) - AS2( paddq mm4, mm1) // a = temp + S0(a) - AS2( movq [edi-8], mm4) - AS2( movq [edi+7*8], mm4) - AS1( ret) - - // first 16 rounds - ASL(0) - AS2( movq mm0, [edx+eax*8]) - AS2( movq [esi+eax*8], mm0) - AS2( movq [esi+eax*8+16*8], mm0) - AS2( paddq mm0, [ebx+eax*8]) - ASC( call, SHA512_Round) - AS1( inc eax) - AS2( sub edi, 8) - AS2( test eax, 7) - ASJ( jnz, 0, b) - AS2( add edi, 8*8) - AS2( cmp eax, 16) - ASJ( jne, 0, b) - - // rest of the rounds - AS2( movdqu xmm0, [esi+(16-2)*8]) - ASL(1) - // data expansion, W[i-2] already in xmm0 - AS2( movdqu xmm3, [esi]) - AS2( paddq xmm3, [esi+(16-7)*8]) - AS2( movdqa xmm2, [esi+(16-15)*8]) - SSE2_s1(xmm0, 6, 19, 61) - AS2( paddq xmm0, xmm3) - SSE2_s0(xmm2, 1, 7, 8) - AS2( paddq xmm0, xmm2) - AS2( movdq2q mm0, xmm0) - AS2( movhlps xmm1, xmm0) - AS2( paddq mm0, [ebx+eax*8]) - AS2( movlps [esi], xmm0) - AS2( movlps [esi+8], xmm1) - AS2( movlps [esi+8*16], xmm0) - AS2( movlps [esi+8*17], xmm1) - // 2 rounds - ASC( call, SHA512_Round) - AS2( sub edi, 8) - AS2( movdq2q mm0, xmm1) - AS2( paddq mm0, [ebx+eax*8+8]) - ASC( call, SHA512_Round) - // update indices and loop - AS2( add esi, 16) - AS2( add eax, 2) - AS2( sub edi, 8) - AS2( test eax, 7) - ASJ( jnz, 1, b) - // do housekeeping every 8 rounds - AS2( mov esi, 0xf) - AS2( and esi, eax) - AS2( lea esi, [esp+4+20*8+8+esi*8]) - AS2( add edi, 8*8) - AS2( cmp eax, 80) - ASJ( jne, 1, b) - -#define SSE2_CombineState(i) \ - AS2( movdqa xmm0, [edi+i*16])\ - AS2( paddq xmm0, [ecx+i*16])\ - AS2( movdqa [ecx+i*16], xmm0) - - SSE2_CombineState(0) - SSE2_CombineState(1) - SSE2_CombineState(2) - SSE2_CombineState(3) - - AS1( pop esp) - AS1( emms) - -#if defined(__GNUC__) - AS1( pop ebx) - ".att_syntax prefix;" - : - : "a" (SHA512_K), "c" (state), "d" (data) - : "%esi", "%edi", "memory", "cc" - ); -#else - AS1( pop edi) - AS1( pop esi) - AS1( pop ebx) - AS1( ret) -#endif -} -#endif // #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - -void SHA512::Transform(word64 *state, const word64 *data) -{ -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 - if (HasSSE2()) - { - SHA512_SSE2_Transform(state, data); - return; - } -#endif - -#define S0(x) (rotrFixed(x,28)^rotrFixed(x,34)^rotrFixed(x,39)) -#define S1(x) (rotrFixed(x,14)^rotrFixed(x,18)^rotrFixed(x,41)) -#define s0(x) (rotrFixed(x,1)^rotrFixed(x,8)^(x>>7)) -#define s1(x) (rotrFixed(x,19)^rotrFixed(x,61)^(x>>6)) - -#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA512_K[i+j]+(j?blk2(i):blk0(i));\ - d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) - - word64 W[16]; - word64 T[8]; - /* Copy context->state[] to working vars */ - memcpy(T, state, sizeof(T)); - /* 80 operations, partially loop unrolled */ - for (unsigned int j=0; j<80; j+=16) - { - R( 0); R( 1); R( 2); R( 3); - R( 4); R( 5); R( 6); R( 7); - R( 8); R( 9); R(10); R(11); - R(12); R(13); R(14); R(15); - } - /* Add the working vars back into context.state[] */ - state[0] += a(0); - state[1] += b(0); - state[2] += c(0); - state[3] += d(0); - state[4] += e(0); - state[5] += f(0); - state[6] += g(0); - state[7] += h(0); -} - -NAMESPACE_END - -#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM -#endif // #ifndef CRYPTOPP_IMPORTS diff --git a/CryptoPP/sha.h b/CryptoPP/sha.h deleted file mode 100644 index 679081e8f..000000000 --- a/CryptoPP/sha.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef CRYPTOPP_SHA_H -#define CRYPTOPP_SHA_H - -#include "iterhash.h" - -NAMESPACE_BEGIN(CryptoPP) - -/// SHA-1 -class CRYPTOPP_DLL SHA1 : public IteratedHashWithStaticTransform -{ -public: - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word32 *digest, const word32 *data); - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-1";} -}; - -typedef SHA1 SHA; // for backwards compatibility - -//! implements the SHA-256 standard -class CRYPTOPP_DLL SHA256 : public IteratedHashWithStaticTransform -{ -public: -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) - size_t HashMultipleBlocks(const word32 *input, size_t length); -#endif - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word32 *digest, const word32 *data); - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-256";} -}; - -//! implements the SHA-224 standard -class CRYPTOPP_DLL SHA224 : public IteratedHashWithStaticTransform -{ -public: -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) - size_t HashMultipleBlocks(const word32 *input, size_t length); -#endif - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word32 *digest, const word32 *data) {SHA256::Transform(digest, data);} - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-224";} -}; - -//! implements the SHA-512 standard -class CRYPTOPP_DLL SHA512 : public IteratedHashWithStaticTransform -{ -public: - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word64 *digest, const word64 *data); - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-512";} -}; - -//! implements the SHA-384 standard -class CRYPTOPP_DLL SHA384 : public IteratedHashWithStaticTransform -{ -public: - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word64 *digest, const word64 *data) {SHA512::Transform(digest, data);} - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-384";} -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/shacal2.cpp b/CryptoPP/shacal2.cpp deleted file mode 100644 index b0360e404..000000000 --- a/CryptoPP/shacal2.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// shacal2.cpp - by Kevin Springle, 2003 -// -// Portions of this code were derived from -// Wei Dai's implementation of SHA-2 -// -// The original code and all modifications are in the public domain. - -#include "pch.h" -#include "shacal2.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -// SHACAL-2 function and round definitions - -#define S0(x) (rotrFixed(x,2)^rotrFixed(x,13)^rotrFixed(x,22)) -#define S1(x) (rotrFixed(x,6)^rotrFixed(x,11)^rotrFixed(x,25)) -#define s0(x) (rotrFixed(x,7)^rotrFixed(x,18)^(x>>3)) -#define s1(x) (rotrFixed(x,17)^rotrFixed(x,19)^(x>>10)) - -#define Ch(x,y,z) (z^(x&(y^z))) -#define Maj(x,y,z) ((x&y)|(z&(x|y))) - -/* R is the SHA-256 round function. */ -/* This macro increments the k argument as a side effect. */ -#define R(a,b,c,d,e,f,g,h,k) \ - h+=S1(e)+Ch(e,f,g)+*k++;d+=h;h+=S0(a)+Maj(a,b,c); - -/* P is the inverse of the SHA-256 round function. */ -/* This macro decrements the k argument as a side effect. */ -#define P(a,b,c,d,e,f,g,h,k) \ - h-=S0(a)+Maj(a,b,c);d-=h;h-=S1(e)+Ch(e,f,g)+*--k; - -void SHACAL2::Base::UncheckedSetKey(const byte *userKey, unsigned int keylen, const NameValuePairs &) -{ - AssertValidKeyLength(keylen); - - word32 *rk = m_key; - unsigned int i; - - GetUserKey(BIG_ENDIAN_ORDER, rk, m_key.size(), userKey, keylen); - for (i = 0; i < 48; i++, rk++) - { - rk[16] = rk[0] + s0(rk[1]) + rk[9] + s1(rk[14]); - rk[0] += K[i]; - } - for (i = 48; i < 64; i++, rk++) - { - rk[0] += K[i]; - } -} - -typedef BlockGetAndPut Block; - -void SHACAL2::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 a, b, c, d, e, f, g, h; - const word32 *rk = m_key; - - /* - * map byte array block to cipher state: - */ - Block::Get(inBlock)(a)(b)(c)(d)(e)(f)(g)(h); - - // Perform SHA-256 transformation. - - /* 64 operations, partially loop unrolled */ - for (unsigned int j=0; j<64; j+=8) - { - R(a,b,c,d,e,f,g,h,rk); - R(h,a,b,c,d,e,f,g,rk); - R(g,h,a,b,c,d,e,f,rk); - R(f,g,h,a,b,c,d,e,rk); - R(e,f,g,h,a,b,c,d,rk); - R(d,e,f,g,h,a,b,c,rk); - R(c,d,e,f,g,h,a,b,rk); - R(b,c,d,e,f,g,h,a,rk); - } - - /* - * map cipher state to byte array block: - */ - - Block::Put(xorBlock, outBlock)(a)(b)(c)(d)(e)(f)(g)(h); -} - -void SHACAL2::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 a, b, c, d, e, f, g, h; - const word32 *rk = m_key + 64; - - /* - * map byte array block to cipher state: - */ - Block::Get(inBlock)(a)(b)(c)(d)(e)(f)(g)(h); - - // Perform inverse SHA-256 transformation. - - /* 64 operations, partially loop unrolled */ - for (unsigned int j=0; j<64; j+=8) - { - P(b,c,d,e,f,g,h,a,rk); - P(c,d,e,f,g,h,a,b,rk); - P(d,e,f,g,h,a,b,c,rk); - P(e,f,g,h,a,b,c,d,rk); - P(f,g,h,a,b,c,d,e,rk); - P(g,h,a,b,c,d,e,f,rk); - P(h,a,b,c,d,e,f,g,rk); - P(a,b,c,d,e,f,g,h,rk); - } - - /* - * map cipher state to byte array block: - */ - - Block::Put(xorBlock, outBlock)(a)(b)(c)(d)(e)(f)(g)(h); -} - -// The SHACAL-2 round constants are identical to the SHA-256 round constants. -const word32 SHACAL2::Base::K[64] = -{ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -NAMESPACE_END diff --git a/CryptoPP/shacal2.h b/CryptoPP/shacal2.h deleted file mode 100644 index 66c987fd7..000000000 --- a/CryptoPP/shacal2.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef CRYPTOPP_SHACAL2_H -#define CRYPTOPP_SHACAL2_H - -/** \file -*/ - -#include "seckey.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -struct SHACAL2_Info : public FixedBlockSize<32>, public VariableKeyLength<16, 16, 64> -{ - static const char *StaticAlgorithmName() {return "SHACAL-2";} -}; - -/// SHACAL-2 -class SHACAL2 : public SHACAL2_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl - { - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms); - - protected: - FixedSizeSecBlock m_key; - - static const word32 K[64]; - }; - - class CRYPTOPP_NO_VTABLE Enc : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - - class CRYPTOPP_NO_VTABLE Dec : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -typedef SHACAL2::Encryption SHACAL2Encryption; -typedef SHACAL2::Decryption SHACAL2Decryption; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/simple.cpp b/CryptoPP/simple.cpp deleted file mode 100644 index 96f256b40..000000000 --- a/CryptoPP/simple.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// simple.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "simple.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -NAMESPACE_END - -#endif diff --git a/CryptoPP/simple.h b/CryptoPP/simple.h deleted file mode 100644 index 35fd65ae4..000000000 --- a/CryptoPP/simple.h +++ /dev/null @@ -1,209 +0,0 @@ -// simple.h - written and placed in the public domain by Wei Dai -/*! \file - Simple non-interface classes derived from classes in cryptlib.h. -*/ - -#ifndef CRYPTOPP_SIMPLE_H -#define CRYPTOPP_SIMPLE_H - -#include "cryptlib.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -template -class CRYPTOPP_NO_VTABLE ClonableImpl : public BASE -{ -public: - Clonable * Clone() const {return new DERIVED(*static_cast(this));} -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE AlgorithmImpl : public BASE -{ -public: - static std::string CRYPTOPP_API StaticAlgorithmName() {return ALGORITHM_INFO::StaticAlgorithmName();} - std::string AlgorithmName() const {return ALGORITHM_INFO::StaticAlgorithmName();} -}; - -//! _ -class CRYPTOPP_DLL InvalidKeyLength : public InvalidArgument -{ -public: - explicit InvalidKeyLength(const std::string &algorithm, size_t length) : InvalidArgument(algorithm + ": " + IntToString(length) + " is not a valid key length") {} -}; - -//! _ -class CRYPTOPP_DLL InvalidRounds : public InvalidArgument -{ -public: - explicit InvalidRounds(const std::string &algorithm, unsigned int rounds) : InvalidArgument(algorithm + ": " + IntToString(rounds) + " is not a valid number of rounds") {} -}; - -// ***************************** - -//! _ -template -class CRYPTOPP_NO_VTABLE Bufferless : public T -{ -public: - bool IsolatedFlush(bool hardFlush, bool blocking) {return false;} -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE Unflushable : public T -{ -public: - bool Flush(bool completeFlush, int propagation=-1, bool blocking=true) - {return ChannelFlush(DEFAULT_CHANNEL, completeFlush, propagation, blocking);} - bool IsolatedFlush(bool hardFlush, bool blocking) - {assert(false); return false;} - bool ChannelFlush(const std::string &channel, bool hardFlush, int propagation=-1, bool blocking=true) - { - if (hardFlush && !InputBufferIsEmpty()) - throw CannotFlush("Unflushable: this object has buffered input that cannot be flushed"); - else - { - BufferedTransformation *attached = this->AttachedTransformation(); - return attached && propagation ? attached->ChannelFlush(channel, hardFlush, propagation-1, blocking) : false; - } - } - -protected: - virtual bool InputBufferIsEmpty() const {return false;} -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE InputRejecting : public T -{ -public: - struct InputRejected : public NotImplemented - {InputRejected() : NotImplemented("BufferedTransformation: this object doesn't allow input") {}}; - - // shouldn't be calling these functions on this class - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking) - {throw InputRejected();} - bool IsolatedFlush(bool, bool) {return false;} - bool IsolatedMessageSeriesEnd(bool) {throw InputRejected();} - - size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking) - {throw InputRejected();} - bool ChannelMessageSeriesEnd(const std::string &, int, bool) {throw InputRejected();} -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE CustomFlushPropagation : public T -{ -public: - virtual bool Flush(bool hardFlush, int propagation=-1, bool blocking=true) =0; - -private: - bool IsolatedFlush(bool hardFlush, bool blocking) {assert(false); return false;} -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE CustomSignalPropagation : public CustomFlushPropagation -{ -public: - virtual void Initialize(const NameValuePairs ¶meters=g_nullNameValuePairs, int propagation=-1) =0; - -private: - void IsolatedInitialize(const NameValuePairs ¶meters) {assert(false);} -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE Multichannel : public CustomFlushPropagation -{ -public: - bool Flush(bool hardFlush, int propagation=-1, bool blocking=true) - {return this->ChannelFlush(DEFAULT_CHANNEL, hardFlush, propagation, blocking);} - bool MessageSeriesEnd(int propagation=-1, bool blocking=true) - {return this->ChannelMessageSeriesEnd(DEFAULT_CHANNEL, propagation, blocking);} - byte * CreatePutSpace(size_t &size) - {return this->ChannelCreatePutSpace(DEFAULT_CHANNEL, size);} - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking) - {return this->ChannelPut2(DEFAULT_CHANNEL, begin, length, messageEnd, blocking);} - size_t PutModifiable2(byte *inString, size_t length, int messageEnd, bool blocking) - {return this->ChannelPutModifiable2(DEFAULT_CHANNEL, inString, length, messageEnd, blocking);} - -// void ChannelMessageSeriesEnd(const std::string &channel, int propagation=-1) -// {PropagateMessageSeriesEnd(propagation, channel);} - byte * ChannelCreatePutSpace(const std::string &channel, size_t &size) - {size = 0; return NULL;} - bool ChannelPutModifiable(const std::string &channel, byte *inString, size_t length) - {this->ChannelPut(channel, inString, length); return false;} - - virtual size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking) =0; - size_t ChannelPutModifiable2(const std::string &channel, byte *begin, size_t length, int messageEnd, bool blocking) - {return ChannelPut2(channel, begin, length, messageEnd, blocking);} - - virtual bool ChannelFlush(const std::string &channel, bool hardFlush, int propagation=-1, bool blocking=true) =0; -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE AutoSignaling : public T -{ -public: - AutoSignaling(int propagation=-1) : m_autoSignalPropagation(propagation) {} - - void SetAutoSignalPropagation(int propagation) - {m_autoSignalPropagation = propagation;} - int GetAutoSignalPropagation() const - {return m_autoSignalPropagation;} - -private: - int m_autoSignalPropagation; -}; - -//! A BufferedTransformation that only contains pre-existing data as "output" -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Store : public AutoSignaling > -{ -public: - Store() : m_messageEnd(false) {} - - void IsolatedInitialize(const NameValuePairs ¶meters) - { - m_messageEnd = false; - StoreInitialize(parameters); - } - - unsigned int NumberOfMessages() const {return m_messageEnd ? 0 : 1;} - bool GetNextMessage(); - unsigned int CopyMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) const; - -protected: - virtual void StoreInitialize(const NameValuePairs ¶meters) =0; - - bool m_messageEnd; -}; - -//! A BufferedTransformation that doesn't produce any retrievable output -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Sink : public BufferedTransformation -{ -public: - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) - {transferBytes = 0; return 0;} - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const - {return 0;} -}; - -class CRYPTOPP_DLL BitBucket : public Bufferless -{ -public: - std::string AlgorithmName() const {return "BitBucket";} - void IsolatedInitialize(const NameValuePairs ¶meters) {} - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking) - {return 0;} -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/smartptr.h b/CryptoPP/smartptr.h deleted file mode 100644 index a0a727edc..000000000 --- a/CryptoPP/smartptr.h +++ /dev/null @@ -1,223 +0,0 @@ -#ifndef CRYPTOPP_SMARTPTR_H -#define CRYPTOPP_SMARTPTR_H - -#include "config.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -template class simple_ptr -{ -public: - simple_ptr(T *p = NULL) : m_p(p) {} - ~simple_ptr() {delete m_p; m_p = NULL;} // set m_p to NULL so double destruction (which might occur in Singleton) will be harmless - T *m_p; -}; - -template class member_ptr -{ -public: - explicit member_ptr(T *p = NULL) : m_p(p) {} - - ~member_ptr(); - - const T& operator*() const { return *m_p; } - T& operator*() { return *m_p; } - - const T* operator->() const { return m_p; } - T* operator->() { return m_p; } - - const T* get() const { return m_p; } - T* get() { return m_p; } - - T* release() - { - T *old_p = m_p; - m_p = 0; - return old_p; - } - - void reset(T *p = 0); - -protected: - member_ptr(const member_ptr& rhs); // copy not allowed - void operator=(const member_ptr& rhs); // assignment not allowed - - T *m_p; -}; - -template member_ptr::~member_ptr() {delete m_p;} -template void member_ptr::reset(T *p) {delete m_p; m_p = p;} - -// ******************************************************** - -template class value_ptr : public member_ptr -{ -public: - value_ptr(const T &obj) : member_ptr(new T(obj)) {} - value_ptr(T *p = NULL) : member_ptr(p) {} - value_ptr(const value_ptr& rhs) - : member_ptr(rhs.m_p ? new T(*rhs.m_p) : NULL) {} - - value_ptr& operator=(const value_ptr& rhs); - bool operator==(const value_ptr& rhs) - { - return (!this->m_p && !rhs.m_p) || (this->m_p && rhs.m_p && *this->m_p == *rhs.m_p); - } -}; - -template value_ptr& value_ptr::operator=(const value_ptr& rhs) -{ - T *old_p = this->m_p; - this->m_p = rhs.m_p ? new T(*rhs.m_p) : NULL; - delete old_p; - return *this; -} - -// ******************************************************** - -template class clonable_ptr : public member_ptr -{ -public: - clonable_ptr(const T &obj) : member_ptr(obj.Clone()) {} - clonable_ptr(T *p = NULL) : member_ptr(p) {} - clonable_ptr(const clonable_ptr& rhs) - : member_ptr(rhs.m_p ? rhs.m_p->Clone() : NULL) {} - - clonable_ptr& operator=(const clonable_ptr& rhs); -}; - -template clonable_ptr& clonable_ptr::operator=(const clonable_ptr& rhs) -{ - T *old_p = this->m_p; - this->m_p = rhs.m_p ? rhs.m_p->Clone() : NULL; - delete old_p; - return *this; -} - -// ******************************************************** - -template class counted_ptr -{ -public: - explicit counted_ptr(T *p = 0); - counted_ptr(const T &r) : m_p(0) {attach(r);} - counted_ptr(const counted_ptr& rhs); - - ~counted_ptr(); - - const T& operator*() const { return *m_p; } - T& operator*() { return *m_p; } - - const T* operator->() const { return m_p; } - T* operator->() { return get(); } - - const T* get() const { return m_p; } - T* get(); - - void attach(const T &p); - - counted_ptr & operator=(const counted_ptr& rhs); - -private: - T *m_p; -}; - -template counted_ptr::counted_ptr(T *p) - : m_p(p) -{ - if (m_p) - m_p->m_referenceCount = 1; -} - -template counted_ptr::counted_ptr(const counted_ptr& rhs) - : m_p(rhs.m_p) -{ - if (m_p) - m_p->m_referenceCount++; -} - -template counted_ptr::~counted_ptr() -{ - if (m_p && --m_p->m_referenceCount == 0) - delete m_p; -} - -template void counted_ptr::attach(const T &r) -{ - if (m_p && --m_p->m_referenceCount == 0) - delete m_p; - if (r.m_referenceCount == 0) - { - m_p = r.clone(); - m_p->m_referenceCount = 1; - } - else - { - m_p = const_cast(&r); - m_p->m_referenceCount++; - } -} - -template T* counted_ptr::get() -{ - if (m_p && m_p->m_referenceCount > 1) - { - T *temp = m_p->clone(); - m_p->m_referenceCount--; - m_p = temp; - m_p->m_referenceCount = 1; - } - return m_p; -} - -template counted_ptr & counted_ptr::operator=(const counted_ptr& rhs) -{ - if (m_p != rhs.m_p) - { - if (m_p && --m_p->m_referenceCount == 0) - delete m_p; - m_p = rhs.m_p; - if (m_p) - m_p->m_referenceCount++; - } - return *this; -} - -// ******************************************************** - -template class vector_member_ptrs -{ -public: - vector_member_ptrs(size_t size=0) - : m_size(size), m_ptr(new member_ptr[size]) {} - ~vector_member_ptrs() - {delete [] this->m_ptr;} - - member_ptr& operator[](size_t index) - {assert(indexm_size); return this->m_ptr[index];} - const member_ptr& operator[](size_t index) const - {assert(indexm_size); return this->m_ptr[index];} - - size_t size() const {return this->m_size;} - void resize(size_t newSize) - { - member_ptr *newPtr = new member_ptr[newSize]; - for (size_t i=0; im_size && im_ptr[i].release()); - delete [] this->m_ptr; - this->m_size = newSize; - this->m_ptr = newPtr; - } - -private: - vector_member_ptrs(const vector_member_ptrs &c); // copy not allowed - void operator=(const vector_member_ptrs &x); // assignment not allowed - - size_t m_size; - member_ptr *m_ptr; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/socketft.cpp b/CryptoPP/socketft.cpp deleted file mode 100644 index 6c5a8ff9d..000000000 --- a/CryptoPP/socketft.cpp +++ /dev/null @@ -1,531 +0,0 @@ -// socketft.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "socketft.h" - -#ifdef SOCKETS_AVAILABLE - -#include "wait.h" - -#ifdef USE_BERKELEY_STYLE_SOCKETS -#include -#include -#include -#include -#include -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -#ifdef USE_WINDOWS_STYLE_SOCKETS -const int SOCKET_EINVAL = WSAEINVAL; -const int SOCKET_EWOULDBLOCK = WSAEWOULDBLOCK; -typedef int socklen_t; -#else -const int SOCKET_EINVAL = EINVAL; -const int SOCKET_EWOULDBLOCK = EWOULDBLOCK; -#endif - -Socket::Err::Err(socket_t s, const std::string& operation, int error) - : OS_Error(IO_ERROR, "Socket: " + operation + " operation failed with error " + IntToString(error), operation, error) - , m_s(s) -{ -} - -Socket::~Socket() -{ - if (m_own) - { - try - { - CloseSocket(); - } - catch (...) - { - } - } -} - -void Socket::AttachSocket(socket_t s, bool own) -{ - if (m_own) - CloseSocket(); - - m_s = s; - m_own = own; - SocketChanged(); -} - -socket_t Socket::DetachSocket() -{ - socket_t s = m_s; - m_s = INVALID_SOCKET; - SocketChanged(); - return s; -} - -void Socket::Create(int nType) -{ - assert(m_s == INVALID_SOCKET); - m_s = socket(AF_INET, nType, 0); - CheckAndHandleError("socket", m_s); - m_own = true; - SocketChanged(); -} - -void Socket::CloseSocket() -{ - if (m_s != INVALID_SOCKET) - { -#ifdef USE_WINDOWS_STYLE_SOCKETS - CancelIo((HANDLE) m_s); - CheckAndHandleError_int("closesocket", closesocket(m_s)); -#else - CheckAndHandleError_int("close", close(m_s)); -#endif - m_s = INVALID_SOCKET; - SocketChanged(); - } -} - -void Socket::Bind(unsigned int port, const char *addr) -{ - sockaddr_in sa; - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - - if (addr == NULL) - sa.sin_addr.s_addr = htonl(INADDR_ANY); - else - { - unsigned long result = inet_addr(addr); - if (result == -1) // Solaris doesn't have INADDR_NONE - { - SetLastError(SOCKET_EINVAL); - CheckAndHandleError_int("inet_addr", SOCKET_ERROR); - } - sa.sin_addr.s_addr = result; - } - - sa.sin_port = htons((u_short)port); - - Bind((sockaddr *)&sa, sizeof(sa)); -} - -void Socket::Bind(const sockaddr *psa, socklen_t saLen) -{ - assert(m_s != INVALID_SOCKET); - // cygwin workaround: needs const_cast - CheckAndHandleError_int("bind", bind(m_s, const_cast(psa), saLen)); -} - -void Socket::Listen(int backlog) -{ - assert(m_s != INVALID_SOCKET); - CheckAndHandleError_int("listen", listen(m_s, backlog)); -} - -bool Socket::Connect(const char *addr, unsigned int port) -{ - assert(addr != NULL); - - sockaddr_in sa; - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_addr.s_addr = inet_addr(addr); - - if (sa.sin_addr.s_addr == -1) // Solaris doesn't have INADDR_NONE - { - hostent *lphost = gethostbyname(addr); - if (lphost == NULL) - { - SetLastError(SOCKET_EINVAL); - CheckAndHandleError_int("gethostbyname", SOCKET_ERROR); - } - - sa.sin_addr.s_addr = ((in_addr *)lphost->h_addr)->s_addr; - } - - sa.sin_port = htons((u_short)port); - - return Connect((const sockaddr *)&sa, sizeof(sa)); -} - -bool Socket::Connect(const sockaddr* psa, socklen_t saLen) -{ - assert(m_s != INVALID_SOCKET); - int result = connect(m_s, const_cast(psa), saLen); - if (result == SOCKET_ERROR && GetLastError() == SOCKET_EWOULDBLOCK) - return false; - CheckAndHandleError_int("connect", result); - return true; -} - -bool Socket::Accept(Socket& target, sockaddr *psa, socklen_t *psaLen) -{ - assert(m_s != INVALID_SOCKET); - socket_t s = accept(m_s, psa, psaLen); - if (s == INVALID_SOCKET && GetLastError() == SOCKET_EWOULDBLOCK) - return false; - CheckAndHandleError("accept", s); - target.AttachSocket(s, true); - return true; -} - -void Socket::GetSockName(sockaddr *psa, socklen_t *psaLen) -{ - assert(m_s != INVALID_SOCKET); - CheckAndHandleError_int("getsockname", getsockname(m_s, psa, psaLen)); -} - -void Socket::GetPeerName(sockaddr *psa, socklen_t *psaLen) -{ - assert(m_s != INVALID_SOCKET); - CheckAndHandleError_int("getpeername", getpeername(m_s, psa, psaLen)); -} - -unsigned int Socket::Send(const byte* buf, size_t bufLen, int flags) -{ - assert(m_s != INVALID_SOCKET); - int result = send(m_s, (const char *)buf, UnsignedMin(INT_MAX, bufLen), flags); - CheckAndHandleError_int("send", result); - return result; -} - -unsigned int Socket::Receive(byte* buf, size_t bufLen, int flags) -{ - assert(m_s != INVALID_SOCKET); - int result = recv(m_s, (char *)buf, UnsignedMin(INT_MAX, bufLen), flags); - CheckAndHandleError_int("recv", result); - return result; -} - -void Socket::ShutDown(int how) -{ - assert(m_s != INVALID_SOCKET); - int result = shutdown(m_s, how); - CheckAndHandleError_int("shutdown", result); -} - -void Socket::IOCtl(long cmd, unsigned long *argp) -{ - assert(m_s != INVALID_SOCKET); -#ifdef USE_WINDOWS_STYLE_SOCKETS - CheckAndHandleError_int("ioctlsocket", ioctlsocket(m_s, cmd, argp)); -#else - CheckAndHandleError_int("ioctl", ioctl(m_s, cmd, argp)); -#endif -} - -bool Socket::SendReady(const timeval *timeout) -{ - fd_set fds; - FD_ZERO(&fds); - FD_SET(m_s, &fds); - int ready; - if (timeout == NULL) - ready = select((int)m_s+1, NULL, &fds, NULL, NULL); - else - { - timeval timeoutCopy = *timeout; // select() modified timeout on Linux - ready = select((int)m_s+1, NULL, &fds, NULL, &timeoutCopy); - } - CheckAndHandleError_int("select", ready); - return ready > 0; -} - -bool Socket::ReceiveReady(const timeval *timeout) -{ - fd_set fds; - FD_ZERO(&fds); - FD_SET(m_s, &fds); - int ready; - if (timeout == NULL) - ready = select((int)m_s+1, &fds, NULL, NULL, NULL); - else - { - timeval timeoutCopy = *timeout; // select() modified timeout on Linux - ready = select((int)m_s+1, &fds, NULL, NULL, &timeoutCopy); - } - CheckAndHandleError_int("select", ready); - return ready > 0; -} - -unsigned int Socket::PortNameToNumber(const char *name, const char *protocol) -{ - int port = atoi(name); - if (IntToString(port) == name) - return port; - - servent *se = getservbyname(name, protocol); - if (!se) - throw Err(INVALID_SOCKET, "getservbyname", SOCKET_EINVAL); - return ntohs(se->s_port); -} - -void Socket::StartSockets() -{ -#ifdef USE_WINDOWS_STYLE_SOCKETS - WSADATA wsd; - int result = WSAStartup(0x0202, &wsd); - if (result != 0) - throw Err(INVALID_SOCKET, "WSAStartup", result); -#endif -} - -void Socket::ShutdownSockets() -{ -#ifdef USE_WINDOWS_STYLE_SOCKETS - int result = WSACleanup(); - if (result != 0) - throw Err(INVALID_SOCKET, "WSACleanup", result); -#endif -} - -int Socket::GetLastError() -{ -#ifdef USE_WINDOWS_STYLE_SOCKETS - return WSAGetLastError(); -#else - return errno; -#endif -} - -void Socket::SetLastError(int errorCode) -{ -#ifdef USE_WINDOWS_STYLE_SOCKETS - WSASetLastError(errorCode); -#else - errno = errorCode; -#endif -} - -void Socket::HandleError(const char *operation) const -{ - int err = GetLastError(); - throw Err(m_s, operation, err); -} - -#ifdef USE_WINDOWS_STYLE_SOCKETS - -SocketReceiver::SocketReceiver(Socket &s) - : m_s(s), m_resultPending(false), m_eofReceived(false) -{ - m_event.AttachHandle(CreateEvent(NULL, true, false, NULL), true); - m_s.CheckAndHandleError("CreateEvent", m_event.HandleValid()); - memset(&m_overlapped, 0, sizeof(m_overlapped)); - m_overlapped.hEvent = m_event; -} - -SocketReceiver::~SocketReceiver() -{ -#ifdef USE_WINDOWS_STYLE_SOCKETS - CancelIo((HANDLE) m_s.GetSocket()); -#endif -} - -bool SocketReceiver::Receive(byte* buf, size_t bufLen) -{ - assert(!m_resultPending && !m_eofReceived); - - DWORD flags = 0; - // don't queue too much at once, or we might use up non-paged memory - WSABUF wsabuf = {UnsignedMin((u_long)128*1024, bufLen), (char *)buf}; - if (WSARecv(m_s, &wsabuf, 1, &m_lastResult, &flags, &m_overlapped, NULL) == 0) - { - if (m_lastResult == 0) - m_eofReceived = true; - } - else - { - switch (WSAGetLastError()) - { - default: - m_s.CheckAndHandleError_int("WSARecv", SOCKET_ERROR); - case WSAEDISCON: - m_lastResult = 0; - m_eofReceived = true; - break; - case WSA_IO_PENDING: - m_resultPending = true; - } - } - return !m_resultPending; -} - -void SocketReceiver::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) -{ - if (m_resultPending) - container.AddHandle(m_event, CallStack("SocketReceiver::GetWaitObjects() - result pending", &callStack)); - else if (!m_eofReceived) - container.SetNoWait(CallStack("SocketReceiver::GetWaitObjects() - result ready", &callStack)); -} - -unsigned int SocketReceiver::GetReceiveResult() -{ - if (m_resultPending) - { - DWORD flags = 0; - if (WSAGetOverlappedResult(m_s, &m_overlapped, &m_lastResult, false, &flags)) - { - if (m_lastResult == 0) - m_eofReceived = true; - } - else - { - switch (WSAGetLastError()) - { - default: - m_s.CheckAndHandleError("WSAGetOverlappedResult", FALSE); - case WSAEDISCON: - m_lastResult = 0; - m_eofReceived = true; - } - } - m_resultPending = false; - } - return m_lastResult; -} - -// ************************************************************* - -SocketSender::SocketSender(Socket &s) - : m_s(s), m_resultPending(false), m_lastResult(0) -{ - m_event.AttachHandle(CreateEvent(NULL, true, false, NULL), true); - m_s.CheckAndHandleError("CreateEvent", m_event.HandleValid()); - memset(&m_overlapped, 0, sizeof(m_overlapped)); - m_overlapped.hEvent = m_event; -} - - -SocketSender::~SocketSender() -{ -#ifdef USE_WINDOWS_STYLE_SOCKETS - CancelIo((HANDLE) m_s.GetSocket()); -#endif -} - -void SocketSender::Send(const byte* buf, size_t bufLen) -{ - assert(!m_resultPending); - DWORD written = 0; - // don't queue too much at once, or we might use up non-paged memory - WSABUF wsabuf = {UnsignedMin((u_long)128*1024, bufLen), (char *)buf}; - if (WSASend(m_s, &wsabuf, 1, &written, 0, &m_overlapped, NULL) == 0) - { - m_resultPending = false; - m_lastResult = written; - } - else - { - if (WSAGetLastError() != WSA_IO_PENDING) - m_s.CheckAndHandleError_int("WSASend", SOCKET_ERROR); - - m_resultPending = true; - } -} - -void SocketSender::SendEof() -{ - assert(!m_resultPending); - m_s.ShutDown(SD_SEND); - m_s.CheckAndHandleError("ResetEvent", ResetEvent(m_event)); - m_s.CheckAndHandleError_int("WSAEventSelect", WSAEventSelect(m_s, m_event, FD_CLOSE)); - m_resultPending = true; -} - -bool SocketSender::EofSent() -{ - if (m_resultPending) - { - WSANETWORKEVENTS events; - m_s.CheckAndHandleError_int("WSAEnumNetworkEvents", WSAEnumNetworkEvents(m_s, m_event, &events)); - if ((events.lNetworkEvents & FD_CLOSE) != FD_CLOSE) - throw Socket::Err(m_s, "WSAEnumNetworkEvents (FD_CLOSE not present)", E_FAIL); - if (events.iErrorCode[FD_CLOSE_BIT] != 0) - throw Socket::Err(m_s, "FD_CLOSE (via WSAEnumNetworkEvents)", events.iErrorCode[FD_CLOSE_BIT]); - m_resultPending = false; - } - return m_lastResult != 0; -} - -void SocketSender::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) -{ - if (m_resultPending) - container.AddHandle(m_event, CallStack("SocketSender::GetWaitObjects() - result pending", &callStack)); - else - container.SetNoWait(CallStack("SocketSender::GetWaitObjects() - result ready", &callStack)); -} - -unsigned int SocketSender::GetSendResult() -{ - if (m_resultPending) - { - DWORD flags = 0; - BOOL result = WSAGetOverlappedResult(m_s, &m_overlapped, &m_lastResult, false, &flags); - m_s.CheckAndHandleError("WSAGetOverlappedResult", result); - m_resultPending = false; - } - return m_lastResult; -} - -#endif - -#ifdef USE_BERKELEY_STYLE_SOCKETS - -SocketReceiver::SocketReceiver(Socket &s) - : m_s(s), m_lastResult(0), m_eofReceived(false) -{ -} - -void SocketReceiver::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) -{ - if (!m_eofReceived) - container.AddReadFd(m_s, CallStack("SocketReceiver::GetWaitObjects()", &callStack)); -} - -bool SocketReceiver::Receive(byte* buf, size_t bufLen) -{ - m_lastResult = m_s.Receive(buf, bufLen); - if (bufLen > 0 && m_lastResult == 0) - m_eofReceived = true; - return true; -} - -unsigned int SocketReceiver::GetReceiveResult() -{ - return m_lastResult; -} - -SocketSender::SocketSender(Socket &s) - : m_s(s), m_lastResult(0) -{ -} - -void SocketSender::Send(const byte* buf, size_t bufLen) -{ - m_lastResult = m_s.Send(buf, bufLen); -} - -void SocketSender::SendEof() -{ - m_s.ShutDown(SD_SEND); -} - -unsigned int SocketSender::GetSendResult() -{ - return m_lastResult; -} - -void SocketSender::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) -{ - container.AddWriteFd(m_s, CallStack("SocketSender::GetWaitObjects()", &callStack)); -} - -#endif - -NAMESPACE_END - -#endif // #ifdef SOCKETS_AVAILABLE diff --git a/CryptoPP/socketft.h b/CryptoPP/socketft.h deleted file mode 100644 index e414aa68f..000000000 --- a/CryptoPP/socketft.h +++ /dev/null @@ -1,224 +0,0 @@ -#ifndef CRYPTOPP_SOCKETFT_H -#define CRYPTOPP_SOCKETFT_H - -#include "config.h" - -#ifdef SOCKETS_AVAILABLE - -#include "network.h" -#include "queue.h" - -#ifdef USE_WINDOWS_STYLE_SOCKETS -# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) -# error Winsock 1 is not supported by this library. Please include this file or winsock2.h before windows.h. -# endif -#include -#include "winpipes.h" -#else -#include -#include -#include -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -#ifdef USE_WINDOWS_STYLE_SOCKETS -typedef ::SOCKET socket_t; -#else -typedef int socket_t; -const socket_t INVALID_SOCKET = -1; -// cygwin 1.1.4 doesn't have SHUT_RD -const int SD_RECEIVE = 0; -const int SD_SEND = 1; -const int SD_BOTH = 2; -const int SOCKET_ERROR = -1; -#endif - -#ifndef socklen_t -typedef TYPE_OF_SOCKLEN_T socklen_t; // see config.h -#endif - -//! wrapper for Windows or Berkeley Sockets -class Socket -{ -public: - //! exception thrown by Socket class - class Err : public OS_Error - { - public: - Err(socket_t s, const std::string& operation, int error); - socket_t GetSocket() const {return m_s;} - - private: - socket_t m_s; - }; - - Socket(socket_t s = INVALID_SOCKET, bool own=false) : m_s(s), m_own(own) {} - Socket(const Socket &s) : m_s(s.m_s), m_own(false) {} - virtual ~Socket(); - - bool GetOwnership() const {return m_own;} - void SetOwnership(bool own) {m_own = own;} - - operator socket_t() {return m_s;} - socket_t GetSocket() const {return m_s;} - void AttachSocket(socket_t s, bool own=false); - socket_t DetachSocket(); - void CloseSocket(); - - void Create(int nType = SOCK_STREAM); - void Bind(unsigned int port, const char *addr=NULL); - void Bind(const sockaddr* psa, socklen_t saLen); - void Listen(int backlog=5); - // the next three functions return false if the socket is in nonblocking mode - // and the operation cannot be completed immediately - bool Connect(const char *addr, unsigned int port); - bool Connect(const sockaddr* psa, socklen_t saLen); - bool Accept(Socket& s, sockaddr *psa=NULL, socklen_t *psaLen=NULL); - void GetSockName(sockaddr *psa, socklen_t *psaLen); - void GetPeerName(sockaddr *psa, socklen_t *psaLen); - unsigned int Send(const byte* buf, size_t bufLen, int flags=0); - unsigned int Receive(byte* buf, size_t bufLen, int flags=0); - void ShutDown(int how = SD_SEND); - - void IOCtl(long cmd, unsigned long *argp); - bool SendReady(const timeval *timeout); - bool ReceiveReady(const timeval *timeout); - - virtual void HandleError(const char *operation) const; - void CheckAndHandleError_int(const char *operation, int result) const - {if (result == SOCKET_ERROR) HandleError(operation);} - void CheckAndHandleError(const char *operation, socket_t result) const - {if (result == SOCKET_ERROR) HandleError(operation);} -#ifdef USE_WINDOWS_STYLE_SOCKETS - void CheckAndHandleError(const char *operation, BOOL result) const - {assert(result==TRUE || result==FALSE); if (!result) HandleError(operation);} - void CheckAndHandleError(const char *operation, bool result) const - {if (!result) HandleError(operation);} -#endif - - //! look up the port number given its name, returns 0 if not found - static unsigned int PortNameToNumber(const char *name, const char *protocol="tcp"); - //! start Windows Sockets 2 - static void StartSockets(); - //! calls WSACleanup for Windows Sockets - static void ShutdownSockets(); - //! returns errno or WSAGetLastError - static int GetLastError(); - //! sets errno or calls WSASetLastError - static void SetLastError(int errorCode); - -protected: - virtual void SocketChanged() {} - - socket_t m_s; - bool m_own; -}; - -class SocketsInitializer -{ -public: - SocketsInitializer() {Socket::StartSockets();} - ~SocketsInitializer() {try {Socket::ShutdownSockets();} catch (...) {}} -}; - -class SocketReceiver : public NetworkReceiver -{ -public: - SocketReceiver(Socket &s); - -#ifdef USE_BERKELEY_STYLE_SOCKETS - bool MustWaitToReceive() {return true;} -#else - ~SocketReceiver(); - bool MustWaitForResult() {return true;} -#endif - bool Receive(byte* buf, size_t bufLen); - unsigned int GetReceiveResult(); - bool EofReceived() const {return m_eofReceived;} - - unsigned int GetMaxWaitObjectCount() const {return 1;} - void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); - -private: - Socket &m_s; - bool m_eofReceived; - -#ifdef USE_WINDOWS_STYLE_SOCKETS - WindowsHandle m_event; - OVERLAPPED m_overlapped; - bool m_resultPending; - DWORD m_lastResult; -#else - unsigned int m_lastResult; -#endif -}; - -class SocketSender : public NetworkSender -{ -public: - SocketSender(Socket &s); - -#ifdef USE_BERKELEY_STYLE_SOCKETS - bool MustWaitToSend() {return true;} -#else - ~SocketSender(); - bool MustWaitForResult() {return true;} - bool MustWaitForEof() { return true; } - bool EofSent(); -#endif - void Send(const byte* buf, size_t bufLen); - unsigned int GetSendResult(); - void SendEof(); - - unsigned int GetMaxWaitObjectCount() const {return 1;} - void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); - -private: - Socket &m_s; -#ifdef USE_WINDOWS_STYLE_SOCKETS - WindowsHandle m_event; - OVERLAPPED m_overlapped; - bool m_resultPending; - DWORD m_lastResult; -#else - unsigned int m_lastResult; -#endif -}; - -//! socket-based implementation of NetworkSource -class SocketSource : public NetworkSource, public Socket -{ -public: - SocketSource(socket_t s = INVALID_SOCKET, bool pumpAll = false, BufferedTransformation *attachment = NULL) - : NetworkSource(attachment), Socket(s), m_receiver(*this) - { - if (pumpAll) - PumpAll(); - } - -private: - NetworkReceiver & AccessReceiver() {return m_receiver;} - SocketReceiver m_receiver; -}; - -//! socket-based implementation of NetworkSink -class SocketSink : public NetworkSink, public Socket -{ -public: - SocketSink(socket_t s=INVALID_SOCKET, unsigned int maxBufferSize=0, unsigned int autoFlushBound=16*1024) - : NetworkSink(maxBufferSize, autoFlushBound), Socket(s), m_sender(*this) {} - - void SendEof() {ShutDown(SD_SEND);} - -private: - NetworkSender & AccessSender() {return m_sender;} - SocketSender m_sender; -}; - -NAMESPACE_END - -#endif // #ifdef SOCKETS_AVAILABLE - -#endif diff --git a/CryptoPP/square.cpp b/CryptoPP/square.cpp deleted file mode 100644 index 00e6bddbe..000000000 --- a/CryptoPP/square.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// square.cpp - written and placed in the public domain by Wei Dai -// Based on Paulo S.L.M. Barreto's public domain implementation - -#include "pch.h" -#include "square.h" -#include "misc.h" -#include "gf256.h" - -NAMESPACE_BEGIN(CryptoPP) - -// apply theta to a roundkey -static void SquareTransform (word32 in[4], word32 out[4]) -{ - static const byte G[4][4] = - { - 0x02U, 0x01U, 0x01U, 0x03U, - 0x03U, 0x02U, 0x01U, 0x01U, - 0x01U, 0x03U, 0x02U, 0x01U, - 0x01U, 0x01U, 0x03U, 0x02U - }; - - GF256 gf256(0xf5); - - for (int i = 0; i < 4; i++) - { - word32 temp = 0; - for (int j = 0; j < 4; j++) - for (int k = 0; k < 4; k++) - temp ^= (word32)gf256.Multiply(GETBYTE(in[i], 3-k), G[k][j]) << ((3-j)*8); - out[i] = temp; - } -} - -#define roundkeys(i, j) m_roundkeys[(i)*4+(j)] -#define roundkeys4(i) (m_roundkeys+(i)*4) - -void Square::Base::UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs &) -{ - AssertValidKeyLength(length); - - static const word32 offset[ROUNDS] = { - 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, - 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL, - }; - - GetUserKey(BIG_ENDIAN_ORDER, m_roundkeys.data(), KEYLENGTH/4, userKey, KEYLENGTH); - - /* apply the key evolution function */ - for (int i = 1; i < ROUNDS+1; i++) - { - roundkeys(i, 0) = roundkeys(i-1, 0) ^ rotlFixed(roundkeys(i-1, 3), 8U) ^ offset[i-1]; - roundkeys(i, 1) = roundkeys(i-1, 1) ^ roundkeys(i, 0); - roundkeys(i, 2) = roundkeys(i-1, 2) ^ roundkeys(i, 1); - roundkeys(i, 3) = roundkeys(i-1, 3) ^ roundkeys(i, 2); - } - - /* produce the round keys */ - if (IsForwardTransformation()) - { - for (int i = 0; i < ROUNDS; i++) - SquareTransform (roundkeys4(i), roundkeys4(i)); - } - else - { - for (int i = 0; i < ROUNDS/2; i++) - for (int j = 0; j < 4; j++) - std::swap(roundkeys(i, j), roundkeys(ROUNDS-i, j)); - SquareTransform (roundkeys4(ROUNDS), roundkeys4(ROUNDS)); - } -} - -#define MSB(x) (((x) >> 24) & 0xffU) /* most significant byte */ -#define SSB(x) (((x) >> 16) & 0xffU) /* second in significance */ -#define TSB(x) (((x) >> 8) & 0xffU) /* third in significance */ -#define LSB(x) (((x) ) & 0xffU) /* least significant byte */ - -#define squareRound(text, temp, T0, T1, T2, T3, roundkey) \ -{ \ - temp[0] = T0[MSB (text[0])] \ - ^ T1[MSB (text[1])] \ - ^ T2[MSB (text[2])] \ - ^ T3[MSB (text[3])] \ - ^ roundkey[0]; \ - temp[1] = T0[SSB (text[0])] \ - ^ T1[SSB (text[1])] \ - ^ T2[SSB (text[2])] \ - ^ T3[SSB (text[3])] \ - ^ roundkey[1]; \ - temp[2] = T0[TSB (text[0])] \ - ^ T1[TSB (text[1])] \ - ^ T2[TSB (text[2])] \ - ^ T3[TSB (text[3])] \ - ^ roundkey[2]; \ - temp[3] = T0[LSB (text[0])] \ - ^ T1[LSB (text[1])] \ - ^ T2[LSB (text[2])] \ - ^ T3[LSB (text[3])] \ - ^ roundkey[3]; \ -} /* squareRound */ - -#define squareFinal(text, temp, S, roundkey) \ -{ \ - text[0] = ((word32) (S[MSB (temp[0])]) << 24) \ - ^ ((word32) (S[MSB (temp[1])]) << 16) \ - ^ ((word32) (S[MSB (temp[2])]) << 8) \ - ^ (word32) (S[MSB (temp[3])]) \ - ^ roundkey[0]; \ - text[1] = ((word32) (S[SSB (temp[0])]) << 24) \ - ^ ((word32) (S[SSB (temp[1])]) << 16) \ - ^ ((word32) (S[SSB (temp[2])]) << 8) \ - ^ (word32) (S[SSB (temp[3])]) \ - ^ roundkey[1]; \ - text[2] = ((word32) (S[TSB (temp[0])]) << 24) \ - ^ ((word32) (S[TSB (temp[1])]) << 16) \ - ^ ((word32) (S[TSB (temp[2])]) << 8) \ - ^ (word32) (S[TSB (temp[3])]) \ - ^ roundkey[2]; \ - text[3] = ((word32) (S[LSB (temp[0])]) << 24) \ - ^ ((word32) (S[LSB (temp[1])]) << 16) \ - ^ ((word32) (S[LSB (temp[2])]) << 8) \ - ^ (word32) (S[LSB (temp[3])]) \ - ^ roundkey[3]; \ -} /* squareFinal */ - -typedef BlockGetAndPut Block; - -void Square::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 text[4], temp[4]; - Block::Get(inBlock)(text[0])(text[1])(text[2])(text[3]); - - /* initial key addition */ - text[0] ^= roundkeys(0, 0); - text[1] ^= roundkeys(0, 1); - text[2] ^= roundkeys(0, 2); - text[3] ^= roundkeys(0, 3); - - /* ROUNDS - 1 full rounds */ - for (int i=1; i+1, public FixedKeyLength<16>, FixedRounds<8> -{ - static const char *StaticAlgorithmName() {return "Square";} -}; - -/// Square -class Square : public Square_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl - { - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms); - - protected: - FixedSizeSecBlock m_roundkeys; - }; - - class CRYPTOPP_NO_VTABLE Enc : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - private: - static const byte Se[256]; - static const word32 Te[4][256]; - }; - - class CRYPTOPP_NO_VTABLE Dec : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - private: - static const byte Sd[256]; - static const word32 Td[4][256]; - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -typedef Square::Encryption SquareEncryption; -typedef Square::Decryption SquareDecryption; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/squaretb.cpp b/CryptoPP/squaretb.cpp deleted file mode 100644 index bc3bee7df..000000000 --- a/CryptoPP/squaretb.cpp +++ /dev/null @@ -1,582 +0,0 @@ -#include "pch.h" -#include "square.h" - -NAMESPACE_BEGIN(CryptoPP) - -const byte Square::Enc::Se[256] = { -177, 206, 195, 149, 90, 173, 231, 2, 77, 68, 251, 145, 12, 135, 161, 80, -203, 103, 84, 221, 70, 143, 225, 78, 240, 253, 252, 235, 249, 196, 26, 110, - 94, 245, 204, 141, 28, 86, 67, 254, 7, 97, 248, 117, 89, 255, 3, 34, -138, 209, 19, 238, 136, 0, 14, 52, 21, 128, 148, 227, 237, 181, 83, 35, - 75, 71, 23, 167, 144, 53, 171, 216, 184, 223, 79, 87, 154, 146, 219, 27, - 60, 200, 153, 4, 142, 224, 215, 125, 133, 187, 64, 44, 58, 69, 241, 66, -101, 32, 65, 24, 114, 37, 147, 112, 54, 5, 242, 11, 163, 121, 236, 8, - 39, 49, 50, 182, 124, 176, 10, 115, 91, 123, 183, 129, 210, 13, 106, 38, -158, 88, 156, 131, 116, 179, 172, 48, 122, 105, 119, 15, 174, 33, 222, 208, - 46, 151, 16, 164, 152, 168, 212, 104, 45, 98, 41, 109, 22, 73, 118, 199, -232, 193, 150, 55, 229, 202, 244, 233, 99, 18, 194, 166, 20, 188, 211, 40, -175, 47, 230, 36, 82, 198, 160, 9, 189, 140, 207, 93, 17, 95, 1, 197, -159, 61, 162, 155, 201, 59, 190, 81, 25, 31, 63, 92, 178, 239, 74, 205, -191, 186, 111, 100, 217, 243, 62, 180, 170, 220, 213, 6, 192, 126, 246, 102, -108, 132, 113, 56, 185, 29, 127, 157, 72, 139, 42, 218, 165, 51, 130, 57, -214, 120, 134, 250, 228, 43, 169, 30, 137, 96, 107, 234, 85, 76, 247, 226, -}; - -const byte Square::Dec::Sd[256] = { - 53, 190, 7, 46, 83, 105, 219, 40, 111, 183, 118, 107, 12, 125, 54, 139, -146, 188, 169, 50, 172, 56, 156, 66, 99, 200, 30, 79, 36, 229, 247, 201, - 97, 141, 47, 63, 179, 101, 127, 112, 175, 154, 234, 245, 91, 152, 144, 177, -135, 113, 114, 237, 55, 69, 104, 163, 227, 239, 92, 197, 80, 193, 214, 202, - 90, 98, 95, 38, 9, 93, 20, 65, 232, 157, 206, 64, 253, 8, 23, 74, - 15, 199, 180, 62, 18, 252, 37, 75, 129, 44, 4, 120, 203, 187, 32, 189, -249, 41, 153, 168, 211, 96, 223, 17, 151, 137, 126, 250, 224, 155, 31, 210, -103, 226, 100, 119, 132, 43, 158, 138, 241, 109, 136, 121, 116, 87, 221, 230, - 57, 123, 238, 131, 225, 88, 242, 13, 52, 248, 48, 233, 185, 35, 84, 21, - 68, 11, 77, 102, 58, 3, 162, 145, 148, 82, 76, 195, 130, 231, 128, 192, -182, 14, 194, 108, 147, 236, 171, 67, 149, 246, 216, 70, 134, 5, 140, 176, -117, 0, 204, 133, 215, 61, 115, 122, 72, 228, 209, 89, 173, 184, 198, 208, -220, 161, 170, 2, 29, 191, 181, 159, 81, 196, 165, 16, 34, 207, 1, 186, -143, 49, 124, 174, 150, 218, 240, 86, 71, 212, 235, 78, 217, 19, 142, 73, - 85, 22, 255, 59, 244, 164, 178, 6, 160, 167, 251, 27, 110, 60, 51, 205, - 24, 94, 106, 213, 166, 33, 222, 254, 42, 28, 243, 10, 26, 25, 39, 45, -}; - -const word32 Square::Enc::Te[4][256] = { -{ -0x97b1b126UL, 0x69cecea7UL, 0x73c3c3b0UL, 0xdf95954aUL, -0xb45a5aeeUL, 0xafadad02UL, 0x3be7e7dcUL, 0x04020206UL, -0x9a4d4dd7UL, 0x884444ccUL, 0x03fbfbf8UL, 0xd7919146UL, -0x180c0c14UL, 0xfb87877cUL, 0xb7a1a116UL, 0xa05050f0UL, -0x63cbcba8UL, 0xce6767a9UL, 0xa85454fcUL, 0x4fdddd92UL, -0x8c4646caUL, 0xeb8f8f64UL, 0x37e1e1d6UL, 0x9c4e4ed2UL, -0x15f0f0e5UL, 0x0ffdfdf2UL, 0x0dfcfcf1UL, 0x23ebebc8UL, -0x07f9f9feUL, 0x7dc4c4b9UL, 0x341a1a2eUL, 0xdc6e6eb2UL, -0xbc5e5ee2UL, 0x1ff5f5eaUL, 0x6dcccca1UL, 0xef8d8d62UL, -0x381c1c24UL, 0xac5656faUL, 0x864343c5UL, 0x09fefef7UL, -0x0e070709UL, 0xc26161a3UL, 0x05f8f8fdUL, 0xea75759fUL, -0xb25959ebUL, 0x0bfffff4UL, 0x06030305UL, 0x44222266UL, -0xe18a8a6bUL, 0x57d1d186UL, 0x26131335UL, 0x29eeeec7UL, -0xe588886dUL, 0x00000000UL, 0x1c0e0e12UL, 0x6834345cUL, -0x2a15153fUL, 0xf5808075UL, 0xdd949449UL, 0x33e3e3d0UL, -0x2fededc2UL, 0x9fb5b52aUL, 0xa65353f5UL, 0x46232365UL, -0x964b4bddUL, 0x8e4747c9UL, 0x2e171739UL, 0xbba7a71cUL, -0xd5909045UL, 0x6a35355fUL, 0xa3abab08UL, 0x45d8d89dUL, -0x85b8b83dUL, 0x4bdfdf94UL, 0x9e4f4fd1UL, 0xae5757f9UL, -0xc19a9a5bUL, 0xd1929243UL, 0x43dbdb98UL, 0x361b1b2dUL, -0x783c3c44UL, 0x65c8c8adUL, 0xc799995eUL, 0x0804040cUL, -0xe98e8e67UL, 0x35e0e0d5UL, 0x5bd7d78cUL, 0xfa7d7d87UL, -0xff85857aUL, 0x83bbbb38UL, 0x804040c0UL, 0x582c2c74UL, -0x743a3a4eUL, 0x8a4545cfUL, 0x17f1f1e6UL, 0x844242c6UL, -0xca6565afUL, 0x40202060UL, 0x824141c3UL, 0x30181828UL, -0xe4727296UL, 0x4a25256fUL, 0xd3939340UL, 0xe0707090UL, -0x6c36365aUL, 0x0a05050fUL, 0x11f2f2e3UL, 0x160b0b1dUL, -0xb3a3a310UL, 0xf279798bUL, 0x2dececc1UL, 0x10080818UL, -0x4e272769UL, 0x62313153UL, 0x64323256UL, 0x99b6b62fUL, -0xf87c7c84UL, 0x95b0b025UL, 0x140a0a1eUL, 0xe6737395UL, -0xb65b5bedUL, 0xf67b7b8dUL, 0x9bb7b72cUL, 0xf7818176UL, -0x51d2d283UL, 0x1a0d0d17UL, 0xd46a6abeUL, 0x4c26266aUL, -0xc99e9e57UL, 0xb05858e8UL, 0xcd9c9c51UL, 0xf3838370UL, -0xe874749cUL, 0x93b3b320UL, 0xadacac01UL, 0x60303050UL, -0xf47a7a8eUL, 0xd26969bbUL, 0xee777799UL, 0x1e0f0f11UL, -0xa9aeae07UL, 0x42212163UL, 0x49dede97UL, 0x55d0d085UL, -0x5c2e2e72UL, 0xdb97974cUL, 0x20101030UL, 0xbda4a419UL, -0xc598985dUL, 0xa5a8a80dUL, 0x5dd4d489UL, 0xd06868b8UL, -0x5a2d2d77UL, 0xc46262a6UL, 0x5229297bUL, 0xda6d6db7UL, -0x2c16163aUL, 0x924949dbUL, 0xec76769aUL, 0x7bc7c7bcUL, -0x25e8e8cdUL, 0x77c1c1b6UL, 0xd996964fUL, 0x6e373759UL, -0x3fe5e5daUL, 0x61cacaabUL, 0x1df4f4e9UL, 0x27e9e9ceUL, -0xc66363a5UL, 0x24121236UL, 0x71c2c2b3UL, 0xb9a6a61fUL, -0x2814143cUL, 0x8dbcbc31UL, 0x53d3d380UL, 0x50282878UL, -0xabafaf04UL, 0x5e2f2f71UL, 0x39e6e6dfUL, 0x4824246cUL, -0xa45252f6UL, 0x79c6c6bfUL, 0xb5a0a015UL, 0x1209091bUL, -0x8fbdbd32UL, 0xed8c8c61UL, 0x6bcfcfa4UL, 0xba5d5de7UL, -0x22111133UL, 0xbe5f5fe1UL, 0x02010103UL, 0x7fc5c5baUL, -0xcb9f9f54UL, 0x7a3d3d47UL, 0xb1a2a213UL, 0xc39b9b58UL, -0x67c9c9aeUL, 0x763b3b4dUL, 0x89bebe37UL, 0xa25151f3UL, -0x3219192bUL, 0x3e1f1f21UL, 0x7e3f3f41UL, 0xb85c5ce4UL, -0x91b2b223UL, 0x2befefc4UL, 0x944a4adeUL, 0x6fcdcda2UL, -0x8bbfbf34UL, 0x81baba3bUL, 0xde6f6fb1UL, 0xc86464acUL, -0x47d9d99eUL, 0x13f3f3e0UL, 0x7c3e3e42UL, 0x9db4b429UL, -0xa1aaaa0bUL, 0x4ddcdc91UL, 0x5fd5d58aUL, 0x0c06060aUL, -0x75c0c0b5UL, 0xfc7e7e82UL, 0x19f6f6efUL, 0xcc6666aaUL, -0xd86c6cb4UL, 0xfd848479UL, 0xe2717193UL, 0x70383848UL, -0x87b9b93eUL, 0x3a1d1d27UL, 0xfe7f7f81UL, 0xcf9d9d52UL, -0x904848d8UL, 0xe38b8b68UL, 0x542a2a7eUL, 0x41dada9bUL, -0xbfa5a51aUL, 0x66333355UL, 0xf1828273UL, 0x7239394bUL, -0x59d6d68fUL, 0xf0787888UL, 0xf986867fUL, 0x01fafafbUL, -0x3de4e4d9UL, 0x562b2b7dUL, 0xa7a9a90eUL, 0x3c1e1e22UL, -0xe789896eUL, 0xc06060a0UL, 0xd66b6bbdUL, 0x21eaeacbUL, -0xaa5555ffUL, 0x984c4cd4UL, 0x1bf7f7ecUL, 0x31e2e2d3UL, -}, - -{ -0x2697b1b1UL, 0xa769ceceUL, 0xb073c3c3UL, 0x4adf9595UL, -0xeeb45a5aUL, 0x02afadadUL, 0xdc3be7e7UL, 0x06040202UL, -0xd79a4d4dUL, 0xcc884444UL, 0xf803fbfbUL, 0x46d79191UL, -0x14180c0cUL, 0x7cfb8787UL, 0x16b7a1a1UL, 0xf0a05050UL, -0xa863cbcbUL, 0xa9ce6767UL, 0xfca85454UL, 0x924fddddUL, -0xca8c4646UL, 0x64eb8f8fUL, 0xd637e1e1UL, 0xd29c4e4eUL, -0xe515f0f0UL, 0xf20ffdfdUL, 0xf10dfcfcUL, 0xc823ebebUL, -0xfe07f9f9UL, 0xb97dc4c4UL, 0x2e341a1aUL, 0xb2dc6e6eUL, -0xe2bc5e5eUL, 0xea1ff5f5UL, 0xa16dccccUL, 0x62ef8d8dUL, -0x24381c1cUL, 0xfaac5656UL, 0xc5864343UL, 0xf709fefeUL, -0x090e0707UL, 0xa3c26161UL, 0xfd05f8f8UL, 0x9fea7575UL, -0xebb25959UL, 0xf40bffffUL, 0x05060303UL, 0x66442222UL, -0x6be18a8aUL, 0x8657d1d1UL, 0x35261313UL, 0xc729eeeeUL, -0x6de58888UL, 0x00000000UL, 0x121c0e0eUL, 0x5c683434UL, -0x3f2a1515UL, 0x75f58080UL, 0x49dd9494UL, 0xd033e3e3UL, -0xc22fededUL, 0x2a9fb5b5UL, 0xf5a65353UL, 0x65462323UL, -0xdd964b4bUL, 0xc98e4747UL, 0x392e1717UL, 0x1cbba7a7UL, -0x45d59090UL, 0x5f6a3535UL, 0x08a3ababUL, 0x9d45d8d8UL, -0x3d85b8b8UL, 0x944bdfdfUL, 0xd19e4f4fUL, 0xf9ae5757UL, -0x5bc19a9aUL, 0x43d19292UL, 0x9843dbdbUL, 0x2d361b1bUL, -0x44783c3cUL, 0xad65c8c8UL, 0x5ec79999UL, 0x0c080404UL, -0x67e98e8eUL, 0xd535e0e0UL, 0x8c5bd7d7UL, 0x87fa7d7dUL, -0x7aff8585UL, 0x3883bbbbUL, 0xc0804040UL, 0x74582c2cUL, -0x4e743a3aUL, 0xcf8a4545UL, 0xe617f1f1UL, 0xc6844242UL, -0xafca6565UL, 0x60402020UL, 0xc3824141UL, 0x28301818UL, -0x96e47272UL, 0x6f4a2525UL, 0x40d39393UL, 0x90e07070UL, -0x5a6c3636UL, 0x0f0a0505UL, 0xe311f2f2UL, 0x1d160b0bUL, -0x10b3a3a3UL, 0x8bf27979UL, 0xc12dececUL, 0x18100808UL, -0x694e2727UL, 0x53623131UL, 0x56643232UL, 0x2f99b6b6UL, -0x84f87c7cUL, 0x2595b0b0UL, 0x1e140a0aUL, 0x95e67373UL, -0xedb65b5bUL, 0x8df67b7bUL, 0x2c9bb7b7UL, 0x76f78181UL, -0x8351d2d2UL, 0x171a0d0dUL, 0xbed46a6aUL, 0x6a4c2626UL, -0x57c99e9eUL, 0xe8b05858UL, 0x51cd9c9cUL, 0x70f38383UL, -0x9ce87474UL, 0x2093b3b3UL, 0x01adacacUL, 0x50603030UL, -0x8ef47a7aUL, 0xbbd26969UL, 0x99ee7777UL, 0x111e0f0fUL, -0x07a9aeaeUL, 0x63422121UL, 0x9749dedeUL, 0x8555d0d0UL, -0x725c2e2eUL, 0x4cdb9797UL, 0x30201010UL, 0x19bda4a4UL, -0x5dc59898UL, 0x0da5a8a8UL, 0x895dd4d4UL, 0xb8d06868UL, -0x775a2d2dUL, 0xa6c46262UL, 0x7b522929UL, 0xb7da6d6dUL, -0x3a2c1616UL, 0xdb924949UL, 0x9aec7676UL, 0xbc7bc7c7UL, -0xcd25e8e8UL, 0xb677c1c1UL, 0x4fd99696UL, 0x596e3737UL, -0xda3fe5e5UL, 0xab61cacaUL, 0xe91df4f4UL, 0xce27e9e9UL, -0xa5c66363UL, 0x36241212UL, 0xb371c2c2UL, 0x1fb9a6a6UL, -0x3c281414UL, 0x318dbcbcUL, 0x8053d3d3UL, 0x78502828UL, -0x04abafafUL, 0x715e2f2fUL, 0xdf39e6e6UL, 0x6c482424UL, -0xf6a45252UL, 0xbf79c6c6UL, 0x15b5a0a0UL, 0x1b120909UL, -0x328fbdbdUL, 0x61ed8c8cUL, 0xa46bcfcfUL, 0xe7ba5d5dUL, -0x33221111UL, 0xe1be5f5fUL, 0x03020101UL, 0xba7fc5c5UL, -0x54cb9f9fUL, 0x477a3d3dUL, 0x13b1a2a2UL, 0x58c39b9bUL, -0xae67c9c9UL, 0x4d763b3bUL, 0x3789bebeUL, 0xf3a25151UL, -0x2b321919UL, 0x213e1f1fUL, 0x417e3f3fUL, 0xe4b85c5cUL, -0x2391b2b2UL, 0xc42befefUL, 0xde944a4aUL, 0xa26fcdcdUL, -0x348bbfbfUL, 0x3b81babaUL, 0xb1de6f6fUL, 0xacc86464UL, -0x9e47d9d9UL, 0xe013f3f3UL, 0x427c3e3eUL, 0x299db4b4UL, -0x0ba1aaaaUL, 0x914ddcdcUL, 0x8a5fd5d5UL, 0x0a0c0606UL, -0xb575c0c0UL, 0x82fc7e7eUL, 0xef19f6f6UL, 0xaacc6666UL, -0xb4d86c6cUL, 0x79fd8484UL, 0x93e27171UL, 0x48703838UL, -0x3e87b9b9UL, 0x273a1d1dUL, 0x81fe7f7fUL, 0x52cf9d9dUL, -0xd8904848UL, 0x68e38b8bUL, 0x7e542a2aUL, 0x9b41dadaUL, -0x1abfa5a5UL, 0x55663333UL, 0x73f18282UL, 0x4b723939UL, -0x8f59d6d6UL, 0x88f07878UL, 0x7ff98686UL, 0xfb01fafaUL, -0xd93de4e4UL, 0x7d562b2bUL, 0x0ea7a9a9UL, 0x223c1e1eUL, -0x6ee78989UL, 0xa0c06060UL, 0xbdd66b6bUL, 0xcb21eaeaUL, -0xffaa5555UL, 0xd4984c4cUL, 0xec1bf7f7UL, 0xd331e2e2UL, -}, - -{ -0xb12697b1UL, 0xcea769ceUL, 0xc3b073c3UL, 0x954adf95UL, -0x5aeeb45aUL, 0xad02afadUL, 0xe7dc3be7UL, 0x02060402UL, -0x4dd79a4dUL, 0x44cc8844UL, 0xfbf803fbUL, 0x9146d791UL, -0x0c14180cUL, 0x877cfb87UL, 0xa116b7a1UL, 0x50f0a050UL, -0xcba863cbUL, 0x67a9ce67UL, 0x54fca854UL, 0xdd924fddUL, -0x46ca8c46UL, 0x8f64eb8fUL, 0xe1d637e1UL, 0x4ed29c4eUL, -0xf0e515f0UL, 0xfdf20ffdUL, 0xfcf10dfcUL, 0xebc823ebUL, -0xf9fe07f9UL, 0xc4b97dc4UL, 0x1a2e341aUL, 0x6eb2dc6eUL, -0x5ee2bc5eUL, 0xf5ea1ff5UL, 0xcca16dccUL, 0x8d62ef8dUL, -0x1c24381cUL, 0x56faac56UL, 0x43c58643UL, 0xfef709feUL, -0x07090e07UL, 0x61a3c261UL, 0xf8fd05f8UL, 0x759fea75UL, -0x59ebb259UL, 0xfff40bffUL, 0x03050603UL, 0x22664422UL, -0x8a6be18aUL, 0xd18657d1UL, 0x13352613UL, 0xeec729eeUL, -0x886de588UL, 0x00000000UL, 0x0e121c0eUL, 0x345c6834UL, -0x153f2a15UL, 0x8075f580UL, 0x9449dd94UL, 0xe3d033e3UL, -0xedc22fedUL, 0xb52a9fb5UL, 0x53f5a653UL, 0x23654623UL, -0x4bdd964bUL, 0x47c98e47UL, 0x17392e17UL, 0xa71cbba7UL, -0x9045d590UL, 0x355f6a35UL, 0xab08a3abUL, 0xd89d45d8UL, -0xb83d85b8UL, 0xdf944bdfUL, 0x4fd19e4fUL, 0x57f9ae57UL, -0x9a5bc19aUL, 0x9243d192UL, 0xdb9843dbUL, 0x1b2d361bUL, -0x3c44783cUL, 0xc8ad65c8UL, 0x995ec799UL, 0x040c0804UL, -0x8e67e98eUL, 0xe0d535e0UL, 0xd78c5bd7UL, 0x7d87fa7dUL, -0x857aff85UL, 0xbb3883bbUL, 0x40c08040UL, 0x2c74582cUL, -0x3a4e743aUL, 0x45cf8a45UL, 0xf1e617f1UL, 0x42c68442UL, -0x65afca65UL, 0x20604020UL, 0x41c38241UL, 0x18283018UL, -0x7296e472UL, 0x256f4a25UL, 0x9340d393UL, 0x7090e070UL, -0x365a6c36UL, 0x050f0a05UL, 0xf2e311f2UL, 0x0b1d160bUL, -0xa310b3a3UL, 0x798bf279UL, 0xecc12decUL, 0x08181008UL, -0x27694e27UL, 0x31536231UL, 0x32566432UL, 0xb62f99b6UL, -0x7c84f87cUL, 0xb02595b0UL, 0x0a1e140aUL, 0x7395e673UL, -0x5bedb65bUL, 0x7b8df67bUL, 0xb72c9bb7UL, 0x8176f781UL, -0xd28351d2UL, 0x0d171a0dUL, 0x6abed46aUL, 0x266a4c26UL, -0x9e57c99eUL, 0x58e8b058UL, 0x9c51cd9cUL, 0x8370f383UL, -0x749ce874UL, 0xb32093b3UL, 0xac01adacUL, 0x30506030UL, -0x7a8ef47aUL, 0x69bbd269UL, 0x7799ee77UL, 0x0f111e0fUL, -0xae07a9aeUL, 0x21634221UL, 0xde9749deUL, 0xd08555d0UL, -0x2e725c2eUL, 0x974cdb97UL, 0x10302010UL, 0xa419bda4UL, -0x985dc598UL, 0xa80da5a8UL, 0xd4895dd4UL, 0x68b8d068UL, -0x2d775a2dUL, 0x62a6c462UL, 0x297b5229UL, 0x6db7da6dUL, -0x163a2c16UL, 0x49db9249UL, 0x769aec76UL, 0xc7bc7bc7UL, -0xe8cd25e8UL, 0xc1b677c1UL, 0x964fd996UL, 0x37596e37UL, -0xe5da3fe5UL, 0xcaab61caUL, 0xf4e91df4UL, 0xe9ce27e9UL, -0x63a5c663UL, 0x12362412UL, 0xc2b371c2UL, 0xa61fb9a6UL, -0x143c2814UL, 0xbc318dbcUL, 0xd38053d3UL, 0x28785028UL, -0xaf04abafUL, 0x2f715e2fUL, 0xe6df39e6UL, 0x246c4824UL, -0x52f6a452UL, 0xc6bf79c6UL, 0xa015b5a0UL, 0x091b1209UL, -0xbd328fbdUL, 0x8c61ed8cUL, 0xcfa46bcfUL, 0x5de7ba5dUL, -0x11332211UL, 0x5fe1be5fUL, 0x01030201UL, 0xc5ba7fc5UL, -0x9f54cb9fUL, 0x3d477a3dUL, 0xa213b1a2UL, 0x9b58c39bUL, -0xc9ae67c9UL, 0x3b4d763bUL, 0xbe3789beUL, 0x51f3a251UL, -0x192b3219UL, 0x1f213e1fUL, 0x3f417e3fUL, 0x5ce4b85cUL, -0xb22391b2UL, 0xefc42befUL, 0x4ade944aUL, 0xcda26fcdUL, -0xbf348bbfUL, 0xba3b81baUL, 0x6fb1de6fUL, 0x64acc864UL, -0xd99e47d9UL, 0xf3e013f3UL, 0x3e427c3eUL, 0xb4299db4UL, -0xaa0ba1aaUL, 0xdc914ddcUL, 0xd58a5fd5UL, 0x060a0c06UL, -0xc0b575c0UL, 0x7e82fc7eUL, 0xf6ef19f6UL, 0x66aacc66UL, -0x6cb4d86cUL, 0x8479fd84UL, 0x7193e271UL, 0x38487038UL, -0xb93e87b9UL, 0x1d273a1dUL, 0x7f81fe7fUL, 0x9d52cf9dUL, -0x48d89048UL, 0x8b68e38bUL, 0x2a7e542aUL, 0xda9b41daUL, -0xa51abfa5UL, 0x33556633UL, 0x8273f182UL, 0x394b7239UL, -0xd68f59d6UL, 0x7888f078UL, 0x867ff986UL, 0xfafb01faUL, -0xe4d93de4UL, 0x2b7d562bUL, 0xa90ea7a9UL, 0x1e223c1eUL, -0x896ee789UL, 0x60a0c060UL, 0x6bbdd66bUL, 0xeacb21eaUL, -0x55ffaa55UL, 0x4cd4984cUL, 0xf7ec1bf7UL, 0xe2d331e2UL, -}, - -{ -0xb1b12697UL, 0xcecea769UL, 0xc3c3b073UL, 0x95954adfUL, -0x5a5aeeb4UL, 0xadad02afUL, 0xe7e7dc3bUL, 0x02020604UL, -0x4d4dd79aUL, 0x4444cc88UL, 0xfbfbf803UL, 0x919146d7UL, -0x0c0c1418UL, 0x87877cfbUL, 0xa1a116b7UL, 0x5050f0a0UL, -0xcbcba863UL, 0x6767a9ceUL, 0x5454fca8UL, 0xdddd924fUL, -0x4646ca8cUL, 0x8f8f64ebUL, 0xe1e1d637UL, 0x4e4ed29cUL, -0xf0f0e515UL, 0xfdfdf20fUL, 0xfcfcf10dUL, 0xebebc823UL, -0xf9f9fe07UL, 0xc4c4b97dUL, 0x1a1a2e34UL, 0x6e6eb2dcUL, -0x5e5ee2bcUL, 0xf5f5ea1fUL, 0xcccca16dUL, 0x8d8d62efUL, -0x1c1c2438UL, 0x5656faacUL, 0x4343c586UL, 0xfefef709UL, -0x0707090eUL, 0x6161a3c2UL, 0xf8f8fd05UL, 0x75759feaUL, -0x5959ebb2UL, 0xfffff40bUL, 0x03030506UL, 0x22226644UL, -0x8a8a6be1UL, 0xd1d18657UL, 0x13133526UL, 0xeeeec729UL, -0x88886de5UL, 0x00000000UL, 0x0e0e121cUL, 0x34345c68UL, -0x15153f2aUL, 0x808075f5UL, 0x949449ddUL, 0xe3e3d033UL, -0xededc22fUL, 0xb5b52a9fUL, 0x5353f5a6UL, 0x23236546UL, -0x4b4bdd96UL, 0x4747c98eUL, 0x1717392eUL, 0xa7a71cbbUL, -0x909045d5UL, 0x35355f6aUL, 0xabab08a3UL, 0xd8d89d45UL, -0xb8b83d85UL, 0xdfdf944bUL, 0x4f4fd19eUL, 0x5757f9aeUL, -0x9a9a5bc1UL, 0x929243d1UL, 0xdbdb9843UL, 0x1b1b2d36UL, -0x3c3c4478UL, 0xc8c8ad65UL, 0x99995ec7UL, 0x04040c08UL, -0x8e8e67e9UL, 0xe0e0d535UL, 0xd7d78c5bUL, 0x7d7d87faUL, -0x85857affUL, 0xbbbb3883UL, 0x4040c080UL, 0x2c2c7458UL, -0x3a3a4e74UL, 0x4545cf8aUL, 0xf1f1e617UL, 0x4242c684UL, -0x6565afcaUL, 0x20206040UL, 0x4141c382UL, 0x18182830UL, -0x727296e4UL, 0x25256f4aUL, 0x939340d3UL, 0x707090e0UL, -0x36365a6cUL, 0x05050f0aUL, 0xf2f2e311UL, 0x0b0b1d16UL, -0xa3a310b3UL, 0x79798bf2UL, 0xececc12dUL, 0x08081810UL, -0x2727694eUL, 0x31315362UL, 0x32325664UL, 0xb6b62f99UL, -0x7c7c84f8UL, 0xb0b02595UL, 0x0a0a1e14UL, 0x737395e6UL, -0x5b5bedb6UL, 0x7b7b8df6UL, 0xb7b72c9bUL, 0x818176f7UL, -0xd2d28351UL, 0x0d0d171aUL, 0x6a6abed4UL, 0x26266a4cUL, -0x9e9e57c9UL, 0x5858e8b0UL, 0x9c9c51cdUL, 0x838370f3UL, -0x74749ce8UL, 0xb3b32093UL, 0xacac01adUL, 0x30305060UL, -0x7a7a8ef4UL, 0x6969bbd2UL, 0x777799eeUL, 0x0f0f111eUL, -0xaeae07a9UL, 0x21216342UL, 0xdede9749UL, 0xd0d08555UL, -0x2e2e725cUL, 0x97974cdbUL, 0x10103020UL, 0xa4a419bdUL, -0x98985dc5UL, 0xa8a80da5UL, 0xd4d4895dUL, 0x6868b8d0UL, -0x2d2d775aUL, 0x6262a6c4UL, 0x29297b52UL, 0x6d6db7daUL, -0x16163a2cUL, 0x4949db92UL, 0x76769aecUL, 0xc7c7bc7bUL, -0xe8e8cd25UL, 0xc1c1b677UL, 0x96964fd9UL, 0x3737596eUL, -0xe5e5da3fUL, 0xcacaab61UL, 0xf4f4e91dUL, 0xe9e9ce27UL, -0x6363a5c6UL, 0x12123624UL, 0xc2c2b371UL, 0xa6a61fb9UL, -0x14143c28UL, 0xbcbc318dUL, 0xd3d38053UL, 0x28287850UL, -0xafaf04abUL, 0x2f2f715eUL, 0xe6e6df39UL, 0x24246c48UL, -0x5252f6a4UL, 0xc6c6bf79UL, 0xa0a015b5UL, 0x09091b12UL, -0xbdbd328fUL, 0x8c8c61edUL, 0xcfcfa46bUL, 0x5d5de7baUL, -0x11113322UL, 0x5f5fe1beUL, 0x01010302UL, 0xc5c5ba7fUL, -0x9f9f54cbUL, 0x3d3d477aUL, 0xa2a213b1UL, 0x9b9b58c3UL, -0xc9c9ae67UL, 0x3b3b4d76UL, 0xbebe3789UL, 0x5151f3a2UL, -0x19192b32UL, 0x1f1f213eUL, 0x3f3f417eUL, 0x5c5ce4b8UL, -0xb2b22391UL, 0xefefc42bUL, 0x4a4ade94UL, 0xcdcda26fUL, -0xbfbf348bUL, 0xbaba3b81UL, 0x6f6fb1deUL, 0x6464acc8UL, -0xd9d99e47UL, 0xf3f3e013UL, 0x3e3e427cUL, 0xb4b4299dUL, -0xaaaa0ba1UL, 0xdcdc914dUL, 0xd5d58a5fUL, 0x06060a0cUL, -0xc0c0b575UL, 0x7e7e82fcUL, 0xf6f6ef19UL, 0x6666aaccUL, -0x6c6cb4d8UL, 0x848479fdUL, 0x717193e2UL, 0x38384870UL, -0xb9b93e87UL, 0x1d1d273aUL, 0x7f7f81feUL, 0x9d9d52cfUL, -0x4848d890UL, 0x8b8b68e3UL, 0x2a2a7e54UL, 0xdada9b41UL, -0xa5a51abfUL, 0x33335566UL, 0x828273f1UL, 0x39394b72UL, -0xd6d68f59UL, 0x787888f0UL, 0x86867ff9UL, 0xfafafb01UL, -0xe4e4d93dUL, 0x2b2b7d56UL, 0xa9a90ea7UL, 0x1e1e223cUL, -0x89896ee7UL, 0x6060a0c0UL, 0x6b6bbdd6UL, 0xeaeacb21UL, -0x5555ffaaUL, 0x4c4cd498UL, 0xf7f7ec1bUL, 0xe2e2d331UL, -}}; - -const word32 Square::Dec::Td[4][256] = { -{ -0xe368bc02UL, 0x5585620cUL, 0x2a3f2331UL, 0x61ab13f7UL, -0x98d46d72UL, 0x21cb9a19UL, 0x3c22a461UL, 0x459d3dcdUL, -0x05fdb423UL, 0x2bc4075fUL, 0x9b2c01c0UL, 0x3dd9800fUL, -0x486c5c74UL, 0xf97f7e85UL, 0xf173ab1fUL, 0xb6edde0eUL, -0x283c6bedUL, 0x4997781aUL, 0x9f2a918dUL, 0xc9579f33UL, -0xa907a8aaUL, 0xa50ded7dUL, 0x7c422d8fUL, 0x764db0c9UL, -0x4d91e857UL, 0xcea963ccUL, 0xb4ee96d2UL, 0x3028e1b6UL, -0x0df161b9UL, 0xbd196726UL, 0x419bad80UL, 0xc0a06ec7UL, -0x5183f241UL, 0x92dbf034UL, 0x6fa21efcUL, 0x8f32ce4cUL, -0x13e03373UL, 0x69a7c66dUL, 0xe56d6493UL, 0xbf1a2ffaUL, -0xbb1cbfb7UL, 0x587403b5UL, 0xe76e2c4fUL, 0x5d89b796UL, -0xe89c052aUL, 0x446619a3UL, 0x342e71fbUL, 0x0ff22965UL, -0xfe81827aUL, 0xb11322f1UL, 0xa30835ecUL, 0xcd510f7eUL, -0xff7aa614UL, 0x5c7293f8UL, 0x2fc29712UL, 0xf370e3c3UL, -0x992f491cUL, 0xd1431568UL, 0xc2a3261bUL, 0x88cc32b3UL, -0x8acf7a6fUL, 0xb0e8069fUL, 0x7a47f51eUL, 0xd2bb79daUL, -0xe6950821UL, 0x4398e55cUL, 0xd0b83106UL, 0x11e37bafUL, -0x7e416553UL, 0xccaa2b10UL, 0xd8b4e49cUL, 0x6456a7d4UL, -0xfb7c3659UL, 0x724b2084UL, 0xea9f4df6UL, 0x6a5faadfUL, -0x2dc1dfceUL, 0x70486858UL, 0xcaaff381UL, 0x0605d891UL, -0x5a774b69UL, 0x94de28a5UL, 0x39df1042UL, 0x813bc347UL, -0xfc82caa6UL, 0x23c8d2c5UL, 0x03f86cb2UL, 0x080cd59aUL, -0xdab7ac40UL, 0x7db909e1UL, 0x3824342cUL, 0xcf5247a2UL, -0xdcb274d1UL, 0x63a85b2bUL, 0x35d55595UL, 0x479e7511UL, -0x15e5ebe2UL, 0x4b9430c6UL, 0x4a6f14a8UL, 0x91239c86UL, -0x4c6acc39UL, 0x5f8aff4aUL, 0x0406904dUL, 0xee99ddbbUL, -0x1e1152caUL, 0xaaffc418UL, 0xeb646998UL, 0x07fefcffUL, -0x8b345e01UL, 0x567d0ebeUL, 0xbae79bd9UL, 0x4263c132UL, -0x75b5dc7bUL, 0x97264417UL, 0x67aecb66UL, 0x95250ccbUL, -0xec9a9567UL, 0x57862ad0UL, 0x60503799UL, 0xb8e4d305UL, -0x65ad83baUL, 0x19efae35UL, 0xa4f6c913UL, 0xc15b4aa9UL, -0x873e1bd6UL, 0xa0f0595eUL, 0x18148a5bUL, 0xaf02703bUL, -0xab04e076UL, 0xdd4950bfUL, 0xdf4a1863UL, 0xc6a5b656UL, -0x853d530aUL, 0xfa871237UL, 0x77b694a7UL, 0x4665517fUL, -0xed61b109UL, 0x1bece6e9UL, 0xd5458525UL, 0xf5753b52UL, -0x7fba413dUL, 0x27ce4288UL, 0xb2eb4e43UL, 0xd6bde997UL, -0x527b9ef3UL, 0x62537f45UL, 0x2c3afba0UL, 0x7bbcd170UL, -0xb91ff76bUL, 0x121b171dUL, 0xfd79eec8UL, 0x3a277cf0UL, -0x0c0a45d7UL, 0x96dd6079UL, 0x2233f6abUL, 0xacfa1c89UL, -0xc8acbb5dUL, 0xa10b7d30UL, 0xd4bea14bUL, 0xbee10b94UL, -0x25cd0a54UL, 0x547e4662UL, 0xa2f31182UL, 0x17e6a33eUL, -0x263566e6UL, 0xc3580275UL, 0x83388b9bUL, 0x7844bdc2UL, -0x020348dcUL, 0x4f92a08bUL, 0x2e39b37cUL, 0x4e6984e5UL, -0xf0888f71UL, 0x362d3927UL, 0x9cd2fd3fUL, 0x01fb246eUL, -0x893716ddUL, 0x00000000UL, 0xf68d57e0UL, 0xe293986cUL, -0x744ef815UL, 0x9320d45aUL, 0xad0138e7UL, 0xd3405db4UL, -0x1a17c287UL, 0xb3106a2dUL, 0x5078d62fUL, 0xf48e1f3cUL, -0xa70ea5a1UL, 0x71b34c36UL, 0x9ad725aeUL, 0x5e71db24UL, -0x161d8750UL, 0xef62f9d5UL, 0x8d318690UL, 0x1c121a16UL, -0xa6f581cfUL, 0x5b8c6f07UL, 0x37d61d49UL, 0x6e593a92UL, -0x84c67764UL, 0x86c53fb8UL, 0xd746cdf9UL, 0xe090d0b0UL, -0x29c74f83UL, 0xe49640fdUL, 0x0e090d0bUL, 0x6da15620UL, -0x8ec9ea22UL, 0xdb4c882eUL, 0xf776738eUL, 0xb515b2bcUL, -0x10185fc1UL, 0x322ba96aUL, 0x6ba48eb1UL, 0xaef95455UL, -0x406089eeUL, 0x6655ef08UL, 0xe9672144UL, 0x3e21ecbdUL, -0x2030be77UL, 0xf28bc7adUL, 0x80c0e729UL, 0x141ecf8cUL, -0xbce24348UL, 0xc4a6fe8aUL, 0x31d3c5d8UL, 0xb716fa60UL, -0x5380ba9dUL, 0xd94fc0f2UL, 0x1de93e78UL, 0x24362e3aUL, -0xe16bf4deUL, 0xcb54d7efUL, 0x09f7f1f4UL, 0x82c3aff5UL, -0x0bf4b928UL, 0x9d29d951UL, 0xc75e9238UL, 0xf8845aebUL, -0x90d8b8e8UL, 0xdeb13c0dUL, 0x33d08d04UL, 0x685ce203UL, -0xc55ddae4UL, 0x3bdc589eUL, 0x0a0f9d46UL, 0x3fdac8d3UL, -0x598f27dbUL, 0xa8fc8cc4UL, 0x79bf99acUL, 0x6c5a724eUL, -0x8ccaa2feUL, 0x9ed1b5e3UL, 0x1fea76a4UL, 0x73b004eaUL, -}, - -{ -0x02e368bcUL, 0x0c558562UL, 0x312a3f23UL, 0xf761ab13UL, -0x7298d46dUL, 0x1921cb9aUL, 0x613c22a4UL, 0xcd459d3dUL, -0x2305fdb4UL, 0x5f2bc407UL, 0xc09b2c01UL, 0x0f3dd980UL, -0x74486c5cUL, 0x85f97f7eUL, 0x1ff173abUL, 0x0eb6eddeUL, -0xed283c6bUL, 0x1a499778UL, 0x8d9f2a91UL, 0x33c9579fUL, -0xaaa907a8UL, 0x7da50dedUL, 0x8f7c422dUL, 0xc9764db0UL, -0x574d91e8UL, 0xcccea963UL, 0xd2b4ee96UL, 0xb63028e1UL, -0xb90df161UL, 0x26bd1967UL, 0x80419badUL, 0xc7c0a06eUL, -0x415183f2UL, 0x3492dbf0UL, 0xfc6fa21eUL, 0x4c8f32ceUL, -0x7313e033UL, 0x6d69a7c6UL, 0x93e56d64UL, 0xfabf1a2fUL, -0xb7bb1cbfUL, 0xb5587403UL, 0x4fe76e2cUL, 0x965d89b7UL, -0x2ae89c05UL, 0xa3446619UL, 0xfb342e71UL, 0x650ff229UL, -0x7afe8182UL, 0xf1b11322UL, 0xeca30835UL, 0x7ecd510fUL, -0x14ff7aa6UL, 0xf85c7293UL, 0x122fc297UL, 0xc3f370e3UL, -0x1c992f49UL, 0x68d14315UL, 0x1bc2a326UL, 0xb388cc32UL, -0x6f8acf7aUL, 0x9fb0e806UL, 0x1e7a47f5UL, 0xdad2bb79UL, -0x21e69508UL, 0x5c4398e5UL, 0x06d0b831UL, 0xaf11e37bUL, -0x537e4165UL, 0x10ccaa2bUL, 0x9cd8b4e4UL, 0xd46456a7UL, -0x59fb7c36UL, 0x84724b20UL, 0xf6ea9f4dUL, 0xdf6a5faaUL, -0xce2dc1dfUL, 0x58704868UL, 0x81caaff3UL, 0x910605d8UL, -0x695a774bUL, 0xa594de28UL, 0x4239df10UL, 0x47813bc3UL, -0xa6fc82caUL, 0xc523c8d2UL, 0xb203f86cUL, 0x9a080cd5UL, -0x40dab7acUL, 0xe17db909UL, 0x2c382434UL, 0xa2cf5247UL, -0xd1dcb274UL, 0x2b63a85bUL, 0x9535d555UL, 0x11479e75UL, -0xe215e5ebUL, 0xc64b9430UL, 0xa84a6f14UL, 0x8691239cUL, -0x394c6accUL, 0x4a5f8affUL, 0x4d040690UL, 0xbbee99ddUL, -0xca1e1152UL, 0x18aaffc4UL, 0x98eb6469UL, 0xff07fefcUL, -0x018b345eUL, 0xbe567d0eUL, 0xd9bae79bUL, 0x324263c1UL, -0x7b75b5dcUL, 0x17972644UL, 0x6667aecbUL, 0xcb95250cUL, -0x67ec9a95UL, 0xd057862aUL, 0x99605037UL, 0x05b8e4d3UL, -0xba65ad83UL, 0x3519efaeUL, 0x13a4f6c9UL, 0xa9c15b4aUL, -0xd6873e1bUL, 0x5ea0f059UL, 0x5b18148aUL, 0x3baf0270UL, -0x76ab04e0UL, 0xbfdd4950UL, 0x63df4a18UL, 0x56c6a5b6UL, -0x0a853d53UL, 0x37fa8712UL, 0xa777b694UL, 0x7f466551UL, -0x09ed61b1UL, 0xe91bece6UL, 0x25d54585UL, 0x52f5753bUL, -0x3d7fba41UL, 0x8827ce42UL, 0x43b2eb4eUL, 0x97d6bde9UL, -0xf3527b9eUL, 0x4562537fUL, 0xa02c3afbUL, 0x707bbcd1UL, -0x6bb91ff7UL, 0x1d121b17UL, 0xc8fd79eeUL, 0xf03a277cUL, -0xd70c0a45UL, 0x7996dd60UL, 0xab2233f6UL, 0x89acfa1cUL, -0x5dc8acbbUL, 0x30a10b7dUL, 0x4bd4bea1UL, 0x94bee10bUL, -0x5425cd0aUL, 0x62547e46UL, 0x82a2f311UL, 0x3e17e6a3UL, -0xe6263566UL, 0x75c35802UL, 0x9b83388bUL, 0xc27844bdUL, -0xdc020348UL, 0x8b4f92a0UL, 0x7c2e39b3UL, 0xe54e6984UL, -0x71f0888fUL, 0x27362d39UL, 0x3f9cd2fdUL, 0x6e01fb24UL, -0xdd893716UL, 0x00000000UL, 0xe0f68d57UL, 0x6ce29398UL, -0x15744ef8UL, 0x5a9320d4UL, 0xe7ad0138UL, 0xb4d3405dUL, -0x871a17c2UL, 0x2db3106aUL, 0x2f5078d6UL, 0x3cf48e1fUL, -0xa1a70ea5UL, 0x3671b34cUL, 0xae9ad725UL, 0x245e71dbUL, -0x50161d87UL, 0xd5ef62f9UL, 0x908d3186UL, 0x161c121aUL, -0xcfa6f581UL, 0x075b8c6fUL, 0x4937d61dUL, 0x926e593aUL, -0x6484c677UL, 0xb886c53fUL, 0xf9d746cdUL, 0xb0e090d0UL, -0x8329c74fUL, 0xfde49640UL, 0x0b0e090dUL, 0x206da156UL, -0x228ec9eaUL, 0x2edb4c88UL, 0x8ef77673UL, 0xbcb515b2UL, -0xc110185fUL, 0x6a322ba9UL, 0xb16ba48eUL, 0x55aef954UL, -0xee406089UL, 0x086655efUL, 0x44e96721UL, 0xbd3e21ecUL, -0x772030beUL, 0xadf28bc7UL, 0x2980c0e7UL, 0x8c141ecfUL, -0x48bce243UL, 0x8ac4a6feUL, 0xd831d3c5UL, 0x60b716faUL, -0x9d5380baUL, 0xf2d94fc0UL, 0x781de93eUL, 0x3a24362eUL, -0xdee16bf4UL, 0xefcb54d7UL, 0xf409f7f1UL, 0xf582c3afUL, -0x280bf4b9UL, 0x519d29d9UL, 0x38c75e92UL, 0xebf8845aUL, -0xe890d8b8UL, 0x0ddeb13cUL, 0x0433d08dUL, 0x03685ce2UL, -0xe4c55ddaUL, 0x9e3bdc58UL, 0x460a0f9dUL, 0xd33fdac8UL, -0xdb598f27UL, 0xc4a8fc8cUL, 0xac79bf99UL, 0x4e6c5a72UL, -0xfe8ccaa2UL, 0xe39ed1b5UL, 0xa41fea76UL, 0xea73b004UL, -}, - -{ -0xbc02e368UL, 0x620c5585UL, 0x23312a3fUL, 0x13f761abUL, -0x6d7298d4UL, 0x9a1921cbUL, 0xa4613c22UL, 0x3dcd459dUL, -0xb42305fdUL, 0x075f2bc4UL, 0x01c09b2cUL, 0x800f3dd9UL, -0x5c74486cUL, 0x7e85f97fUL, 0xab1ff173UL, 0xde0eb6edUL, -0x6bed283cUL, 0x781a4997UL, 0x918d9f2aUL, 0x9f33c957UL, -0xa8aaa907UL, 0xed7da50dUL, 0x2d8f7c42UL, 0xb0c9764dUL, -0xe8574d91UL, 0x63cccea9UL, 0x96d2b4eeUL, 0xe1b63028UL, -0x61b90df1UL, 0x6726bd19UL, 0xad80419bUL, 0x6ec7c0a0UL, -0xf2415183UL, 0xf03492dbUL, 0x1efc6fa2UL, 0xce4c8f32UL, -0x337313e0UL, 0xc66d69a7UL, 0x6493e56dUL, 0x2ffabf1aUL, -0xbfb7bb1cUL, 0x03b55874UL, 0x2c4fe76eUL, 0xb7965d89UL, -0x052ae89cUL, 0x19a34466UL, 0x71fb342eUL, 0x29650ff2UL, -0x827afe81UL, 0x22f1b113UL, 0x35eca308UL, 0x0f7ecd51UL, -0xa614ff7aUL, 0x93f85c72UL, 0x97122fc2UL, 0xe3c3f370UL, -0x491c992fUL, 0x1568d143UL, 0x261bc2a3UL, 0x32b388ccUL, -0x7a6f8acfUL, 0x069fb0e8UL, 0xf51e7a47UL, 0x79dad2bbUL, -0x0821e695UL, 0xe55c4398UL, 0x3106d0b8UL, 0x7baf11e3UL, -0x65537e41UL, 0x2b10ccaaUL, 0xe49cd8b4UL, 0xa7d46456UL, -0x3659fb7cUL, 0x2084724bUL, 0x4df6ea9fUL, 0xaadf6a5fUL, -0xdfce2dc1UL, 0x68587048UL, 0xf381caafUL, 0xd8910605UL, -0x4b695a77UL, 0x28a594deUL, 0x104239dfUL, 0xc347813bUL, -0xcaa6fc82UL, 0xd2c523c8UL, 0x6cb203f8UL, 0xd59a080cUL, -0xac40dab7UL, 0x09e17db9UL, 0x342c3824UL, 0x47a2cf52UL, -0x74d1dcb2UL, 0x5b2b63a8UL, 0x559535d5UL, 0x7511479eUL, -0xebe215e5UL, 0x30c64b94UL, 0x14a84a6fUL, 0x9c869123UL, -0xcc394c6aUL, 0xff4a5f8aUL, 0x904d0406UL, 0xddbbee99UL, -0x52ca1e11UL, 0xc418aaffUL, 0x6998eb64UL, 0xfcff07feUL, -0x5e018b34UL, 0x0ebe567dUL, 0x9bd9bae7UL, 0xc1324263UL, -0xdc7b75b5UL, 0x44179726UL, 0xcb6667aeUL, 0x0ccb9525UL, -0x9567ec9aUL, 0x2ad05786UL, 0x37996050UL, 0xd305b8e4UL, -0x83ba65adUL, 0xae3519efUL, 0xc913a4f6UL, 0x4aa9c15bUL, -0x1bd6873eUL, 0x595ea0f0UL, 0x8a5b1814UL, 0x703baf02UL, -0xe076ab04UL, 0x50bfdd49UL, 0x1863df4aUL, 0xb656c6a5UL, -0x530a853dUL, 0x1237fa87UL, 0x94a777b6UL, 0x517f4665UL, -0xb109ed61UL, 0xe6e91becUL, 0x8525d545UL, 0x3b52f575UL, -0x413d7fbaUL, 0x428827ceUL, 0x4e43b2ebUL, 0xe997d6bdUL, -0x9ef3527bUL, 0x7f456253UL, 0xfba02c3aUL, 0xd1707bbcUL, -0xf76bb91fUL, 0x171d121bUL, 0xeec8fd79UL, 0x7cf03a27UL, -0x45d70c0aUL, 0x607996ddUL, 0xf6ab2233UL, 0x1c89acfaUL, -0xbb5dc8acUL, 0x7d30a10bUL, 0xa14bd4beUL, 0x0b94bee1UL, -0x0a5425cdUL, 0x4662547eUL, 0x1182a2f3UL, 0xa33e17e6UL, -0x66e62635UL, 0x0275c358UL, 0x8b9b8338UL, 0xbdc27844UL, -0x48dc0203UL, 0xa08b4f92UL, 0xb37c2e39UL, 0x84e54e69UL, -0x8f71f088UL, 0x3927362dUL, 0xfd3f9cd2UL, 0x246e01fbUL, -0x16dd8937UL, 0x00000000UL, 0x57e0f68dUL, 0x986ce293UL, -0xf815744eUL, 0xd45a9320UL, 0x38e7ad01UL, 0x5db4d340UL, -0xc2871a17UL, 0x6a2db310UL, 0xd62f5078UL, 0x1f3cf48eUL, -0xa5a1a70eUL, 0x4c3671b3UL, 0x25ae9ad7UL, 0xdb245e71UL, -0x8750161dUL, 0xf9d5ef62UL, 0x86908d31UL, 0x1a161c12UL, -0x81cfa6f5UL, 0x6f075b8cUL, 0x1d4937d6UL, 0x3a926e59UL, -0x776484c6UL, 0x3fb886c5UL, 0xcdf9d746UL, 0xd0b0e090UL, -0x4f8329c7UL, 0x40fde496UL, 0x0d0b0e09UL, 0x56206da1UL, -0xea228ec9UL, 0x882edb4cUL, 0x738ef776UL, 0xb2bcb515UL, -0x5fc11018UL, 0xa96a322bUL, 0x8eb16ba4UL, 0x5455aef9UL, -0x89ee4060UL, 0xef086655UL, 0x2144e967UL, 0xecbd3e21UL, -0xbe772030UL, 0xc7adf28bUL, 0xe72980c0UL, 0xcf8c141eUL, -0x4348bce2UL, 0xfe8ac4a6UL, 0xc5d831d3UL, 0xfa60b716UL, -0xba9d5380UL, 0xc0f2d94fUL, 0x3e781de9UL, 0x2e3a2436UL, -0xf4dee16bUL, 0xd7efcb54UL, 0xf1f409f7UL, 0xaff582c3UL, -0xb9280bf4UL, 0xd9519d29UL, 0x9238c75eUL, 0x5aebf884UL, -0xb8e890d8UL, 0x3c0ddeb1UL, 0x8d0433d0UL, 0xe203685cUL, -0xdae4c55dUL, 0x589e3bdcUL, 0x9d460a0fUL, 0xc8d33fdaUL, -0x27db598fUL, 0x8cc4a8fcUL, 0x99ac79bfUL, 0x724e6c5aUL, -0xa2fe8ccaUL, 0xb5e39ed1UL, 0x76a41feaUL, 0x04ea73b0UL, -}, - -{ -0x68bc02e3UL, 0x85620c55UL, 0x3f23312aUL, 0xab13f761UL, -0xd46d7298UL, 0xcb9a1921UL, 0x22a4613cUL, 0x9d3dcd45UL, -0xfdb42305UL, 0xc4075f2bUL, 0x2c01c09bUL, 0xd9800f3dUL, -0x6c5c7448UL, 0x7f7e85f9UL, 0x73ab1ff1UL, 0xedde0eb6UL, -0x3c6bed28UL, 0x97781a49UL, 0x2a918d9fUL, 0x579f33c9UL, -0x07a8aaa9UL, 0x0ded7da5UL, 0x422d8f7cUL, 0x4db0c976UL, -0x91e8574dUL, 0xa963ccceUL, 0xee96d2b4UL, 0x28e1b630UL, -0xf161b90dUL, 0x196726bdUL, 0x9bad8041UL, 0xa06ec7c0UL, -0x83f24151UL, 0xdbf03492UL, 0xa21efc6fUL, 0x32ce4c8fUL, -0xe0337313UL, 0xa7c66d69UL, 0x6d6493e5UL, 0x1a2ffabfUL, -0x1cbfb7bbUL, 0x7403b558UL, 0x6e2c4fe7UL, 0x89b7965dUL, -0x9c052ae8UL, 0x6619a344UL, 0x2e71fb34UL, 0xf229650fUL, -0x81827afeUL, 0x1322f1b1UL, 0x0835eca3UL, 0x510f7ecdUL, -0x7aa614ffUL, 0x7293f85cUL, 0xc297122fUL, 0x70e3c3f3UL, -0x2f491c99UL, 0x431568d1UL, 0xa3261bc2UL, 0xcc32b388UL, -0xcf7a6f8aUL, 0xe8069fb0UL, 0x47f51e7aUL, 0xbb79dad2UL, -0x950821e6UL, 0x98e55c43UL, 0xb83106d0UL, 0xe37baf11UL, -0x4165537eUL, 0xaa2b10ccUL, 0xb4e49cd8UL, 0x56a7d464UL, -0x7c3659fbUL, 0x4b208472UL, 0x9f4df6eaUL, 0x5faadf6aUL, -0xc1dfce2dUL, 0x48685870UL, 0xaff381caUL, 0x05d89106UL, -0x774b695aUL, 0xde28a594UL, 0xdf104239UL, 0x3bc34781UL, -0x82caa6fcUL, 0xc8d2c523UL, 0xf86cb203UL, 0x0cd59a08UL, -0xb7ac40daUL, 0xb909e17dUL, 0x24342c38UL, 0x5247a2cfUL, -0xb274d1dcUL, 0xa85b2b63UL, 0xd5559535UL, 0x9e751147UL, -0xe5ebe215UL, 0x9430c64bUL, 0x6f14a84aUL, 0x239c8691UL, -0x6acc394cUL, 0x8aff4a5fUL, 0x06904d04UL, 0x99ddbbeeUL, -0x1152ca1eUL, 0xffc418aaUL, 0x646998ebUL, 0xfefcff07UL, -0x345e018bUL, 0x7d0ebe56UL, 0xe79bd9baUL, 0x63c13242UL, -0xb5dc7b75UL, 0x26441797UL, 0xaecb6667UL, 0x250ccb95UL, -0x9a9567ecUL, 0x862ad057UL, 0x50379960UL, 0xe4d305b8UL, -0xad83ba65UL, 0xefae3519UL, 0xf6c913a4UL, 0x5b4aa9c1UL, -0x3e1bd687UL, 0xf0595ea0UL, 0x148a5b18UL, 0x02703bafUL, -0x04e076abUL, 0x4950bfddUL, 0x4a1863dfUL, 0xa5b656c6UL, -0x3d530a85UL, 0x871237faUL, 0xb694a777UL, 0x65517f46UL, -0x61b109edUL, 0xece6e91bUL, 0x458525d5UL, 0x753b52f5UL, -0xba413d7fUL, 0xce428827UL, 0xeb4e43b2UL, 0xbde997d6UL, -0x7b9ef352UL, 0x537f4562UL, 0x3afba02cUL, 0xbcd1707bUL, -0x1ff76bb9UL, 0x1b171d12UL, 0x79eec8fdUL, 0x277cf03aUL, -0x0a45d70cUL, 0xdd607996UL, 0x33f6ab22UL, 0xfa1c89acUL, -0xacbb5dc8UL, 0x0b7d30a1UL, 0xbea14bd4UL, 0xe10b94beUL, -0xcd0a5425UL, 0x7e466254UL, 0xf31182a2UL, 0xe6a33e17UL, -0x3566e626UL, 0x580275c3UL, 0x388b9b83UL, 0x44bdc278UL, -0x0348dc02UL, 0x92a08b4fUL, 0x39b37c2eUL, 0x6984e54eUL, -0x888f71f0UL, 0x2d392736UL, 0xd2fd3f9cUL, 0xfb246e01UL, -0x3716dd89UL, 0x00000000UL, 0x8d57e0f6UL, 0x93986ce2UL, -0x4ef81574UL, 0x20d45a93UL, 0x0138e7adUL, 0x405db4d3UL, -0x17c2871aUL, 0x106a2db3UL, 0x78d62f50UL, 0x8e1f3cf4UL, -0x0ea5a1a7UL, 0xb34c3671UL, 0xd725ae9aUL, 0x71db245eUL, -0x1d875016UL, 0x62f9d5efUL, 0x3186908dUL, 0x121a161cUL, -0xf581cfa6UL, 0x8c6f075bUL, 0xd61d4937UL, 0x593a926eUL, -0xc6776484UL, 0xc53fb886UL, 0x46cdf9d7UL, 0x90d0b0e0UL, -0xc74f8329UL, 0x9640fde4UL, 0x090d0b0eUL, 0xa156206dUL, -0xc9ea228eUL, 0x4c882edbUL, 0x76738ef7UL, 0x15b2bcb5UL, -0x185fc110UL, 0x2ba96a32UL, 0xa48eb16bUL, 0xf95455aeUL, -0x6089ee40UL, 0x55ef0866UL, 0x672144e9UL, 0x21ecbd3eUL, -0x30be7720UL, 0x8bc7adf2UL, 0xc0e72980UL, 0x1ecf8c14UL, -0xe24348bcUL, 0xa6fe8ac4UL, 0xd3c5d831UL, 0x16fa60b7UL, -0x80ba9d53UL, 0x4fc0f2d9UL, 0xe93e781dUL, 0x362e3a24UL, -0x6bf4dee1UL, 0x54d7efcbUL, 0xf7f1f409UL, 0xc3aff582UL, -0xf4b9280bUL, 0x29d9519dUL, 0x5e9238c7UL, 0x845aebf8UL, -0xd8b8e890UL, 0xb13c0ddeUL, 0xd08d0433UL, 0x5ce20368UL, -0x5ddae4c5UL, 0xdc589e3bUL, 0x0f9d460aUL, 0xdac8d33fUL, -0x8f27db59UL, 0xfc8cc4a8UL, 0xbf99ac79UL, 0x5a724e6cUL, -0xcaa2fe8cUL, 0xd1b5e39eUL, 0xea76a41fUL, 0xb004ea73UL, -}}; - -NAMESPACE_END diff --git a/CryptoPP/stdcpp.h b/CryptoPP/stdcpp.h deleted file mode 100644 index 6511c4fa2..000000000 --- a/CryptoPP/stdcpp.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CRYPTOPP_STDCPP_H -#define CRYPTOPP_STDCPP_H - -#if _MSC_VER >= 1500 -#define _DO_NOT_DECLARE_INTERLOCKED_INTRINSICS_IN_MEMORY -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CRYPTOPP_INCLUDE_VECTOR_CC -// workaround needed on Sun Studio 12u1 Sun C++ 5.10 SunOS_i386 128229-02 2009/09/21 -#include -#endif - -// for alloca -#ifdef __sun -#include -#elif defined(__MINGW32__) || defined(__BORLANDC__) -#include -#endif - -#ifdef _MSC_VER -#pragma warning(disable: 4231) // re-disable this -#ifdef _CRTAPI1 -#define CRYPTOPP_MSVCRT6 -#endif -#endif - -#endif diff --git a/CryptoPP/strciphr.cpp b/CryptoPP/strciphr.cpp deleted file mode 100644 index 53e007376..000000000 --- a/CryptoPP/strciphr.cpp +++ /dev/null @@ -1,252 +0,0 @@ -// strciphr.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "strciphr.h" - -NAMESPACE_BEGIN(CryptoPP) - -template -void AdditiveCipherTemplate::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) -{ - PolicyInterface &policy = this->AccessPolicy(); - policy.CipherSetKey(params, key, length); - m_leftOver = 0; - unsigned int bufferByteSize = policy.CanOperateKeystream() ? GetBufferByteSize(policy) : RoundUpToMultipleOf(1024U, GetBufferByteSize(policy)); - m_buffer.New(bufferByteSize); - - if (this->IsResynchronizable()) - { - size_t ivLength; - const byte *iv = this->GetIVAndThrowIfInvalid(params, ivLength); - policy.CipherResynchronize(m_buffer, iv, ivLength); - } -} - -template -void AdditiveCipherTemplate::GenerateBlock(byte *outString, size_t length) -{ - if (m_leftOver > 0) - { - size_t len = STDMIN(m_leftOver, length); - memcpy(outString, KeystreamBufferEnd()-m_leftOver, len); - length -= len; - m_leftOver -= len; - outString += len; - - if (!length) - return; - } - assert(m_leftOver == 0); - - PolicyInterface &policy = this->AccessPolicy(); - unsigned int bytesPerIteration = policy.GetBytesPerIteration(); - - if (length >= bytesPerIteration) - { - size_t iterations = length / bytesPerIteration; - policy.WriteKeystream(outString, iterations); - outString += iterations * bytesPerIteration; - length -= iterations * bytesPerIteration; - } - - if (length > 0) - { - size_t bufferByteSize = RoundUpToMultipleOf(length, bytesPerIteration); - size_t bufferIterations = bufferByteSize / bytesPerIteration; - - policy.WriteKeystream(KeystreamBufferEnd()-bufferByteSize, bufferIterations); - memcpy(outString, KeystreamBufferEnd()-bufferByteSize, length); - m_leftOver = bufferByteSize - length; - } -} - -template -void AdditiveCipherTemplate::ProcessData(byte *outString, const byte *inString, size_t length) -{ - if (m_leftOver > 0) - { - size_t len = STDMIN(m_leftOver, length); - xorbuf(outString, inString, KeystreamBufferEnd()-m_leftOver, len); - length -= len; - m_leftOver -= len; - inString += len; - outString += len; - - if (!length) - return; - } - assert(m_leftOver == 0); - - PolicyInterface &policy = this->AccessPolicy(); - unsigned int bytesPerIteration = policy.GetBytesPerIteration(); - - if (policy.CanOperateKeystream() && length >= bytesPerIteration) - { - size_t iterations = length / bytesPerIteration; - unsigned int alignment = policy.GetAlignment(); - KeystreamOperation operation = KeystreamOperation((IsAlignedOn(inString, alignment) * 2) | (int)IsAlignedOn(outString, alignment)); - - policy.OperateKeystream(operation, outString, inString, iterations); - - inString += iterations * bytesPerIteration; - outString += iterations * bytesPerIteration; - length -= iterations * bytesPerIteration; - - if (!length) - return; - } - - size_t bufferByteSize = m_buffer.size(); - size_t bufferIterations = bufferByteSize / bytesPerIteration; - - while (length >= bufferByteSize) - { - policy.WriteKeystream(m_buffer, bufferIterations); - xorbuf(outString, inString, KeystreamBufferBegin(), bufferByteSize); - length -= bufferByteSize; - inString += bufferByteSize; - outString += bufferByteSize; - } - - if (length > 0) - { - bufferByteSize = RoundUpToMultipleOf(length, bytesPerIteration); - bufferIterations = bufferByteSize / bytesPerIteration; - - policy.WriteKeystream(KeystreamBufferEnd()-bufferByteSize, bufferIterations); - xorbuf(outString, inString, KeystreamBufferEnd()-bufferByteSize, length); - m_leftOver = bufferByteSize - length; - } -} - -template -void AdditiveCipherTemplate::Resynchronize(const byte *iv, int length) -{ - PolicyInterface &policy = this->AccessPolicy(); - m_leftOver = 0; - m_buffer.New(GetBufferByteSize(policy)); - policy.CipherResynchronize(m_buffer, iv, this->ThrowIfInvalidIVLength(length)); -} - -template -void AdditiveCipherTemplate::Seek(lword position) -{ - PolicyInterface &policy = this->AccessPolicy(); - unsigned int bytesPerIteration = policy.GetBytesPerIteration(); - - policy.SeekToIteration(position / bytesPerIteration); - position %= bytesPerIteration; - - if (position > 0) - { - policy.WriteKeystream(KeystreamBufferEnd()-bytesPerIteration, 1); - m_leftOver = bytesPerIteration - (unsigned int)position; - } - else - m_leftOver = 0; -} - -template -void CFB_CipherTemplate::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) -{ - PolicyInterface &policy = this->AccessPolicy(); - policy.CipherSetKey(params, key, length); - - if (this->IsResynchronizable()) - { - size_t ivLength; - const byte *iv = this->GetIVAndThrowIfInvalid(params, ivLength); - policy.CipherResynchronize(iv, ivLength); - } - - m_leftOver = policy.GetBytesPerIteration(); -} - -template -void CFB_CipherTemplate::Resynchronize(const byte *iv, int length) -{ - PolicyInterface &policy = this->AccessPolicy(); - policy.CipherResynchronize(iv, this->ThrowIfInvalidIVLength(length)); - m_leftOver = policy.GetBytesPerIteration(); -} - -template -void CFB_CipherTemplate::ProcessData(byte *outString, const byte *inString, size_t length) -{ - assert(length % this->MandatoryBlockSize() == 0); - - PolicyInterface &policy = this->AccessPolicy(); - unsigned int bytesPerIteration = policy.GetBytesPerIteration(); - unsigned int alignment = policy.GetAlignment(); - byte *reg = policy.GetRegisterBegin(); - - if (m_leftOver) - { - size_t len = STDMIN(m_leftOver, length); - CombineMessageAndShiftRegister(outString, reg + bytesPerIteration - m_leftOver, inString, len); - m_leftOver -= len; - length -= len; - inString += len; - outString += len; - } - - if (!length) - return; - - assert(m_leftOver == 0); - - if (policy.CanIterate() && length >= bytesPerIteration && IsAlignedOn(outString, alignment)) - { - if (IsAlignedOn(inString, alignment)) - policy.Iterate(outString, inString, GetCipherDir(*this), length / bytesPerIteration); - else - { - memcpy(outString, inString, length); - policy.Iterate(outString, outString, GetCipherDir(*this), length / bytesPerIteration); - } - inString += length - length % bytesPerIteration; - outString += length - length % bytesPerIteration; - length %= bytesPerIteration; - } - - while (length >= bytesPerIteration) - { - policy.TransformRegister(); - CombineMessageAndShiftRegister(outString, reg, inString, bytesPerIteration); - length -= bytesPerIteration; - inString += bytesPerIteration; - outString += bytesPerIteration; - } - - if (length > 0) - { - policy.TransformRegister(); - CombineMessageAndShiftRegister(outString, reg, inString, length); - m_leftOver = bytesPerIteration - length; - } -} - -template -void CFB_EncryptionTemplate::CombineMessageAndShiftRegister(byte *output, byte *reg, const byte *message, size_t length) -{ - xorbuf(reg, message, length); - memcpy(output, reg, length); -} - -template -void CFB_DecryptionTemplate::CombineMessageAndShiftRegister(byte *output, byte *reg, const byte *message, size_t length) -{ - for (unsigned int i=0; i, AdditiveCipherTemplate\<\> \> \> Encryption; - - AdditiveCipherTemplate and CFB_CipherTemplate are designed so that they don't need - to take a policy class as a template parameter (although this is allowed), so that - their code is not duplicated for each new cipher. Instead they each - get a reference to an abstract policy interface by calling AccessPolicy() on itself, so - AccessPolicy() must be overriden to return the actual policy reference. This is done - by the ConceretePolicyHolder class. Finally, SymmetricCipherFinal implements the constructors and - other functions that must be implemented by the most derived class. -*/ - -#ifndef CRYPTOPP_STRCIPHR_H -#define CRYPTOPP_STRCIPHR_H - -#include "seckey.h" -#include "secblock.h" -#include "argnames.h" - -NAMESPACE_BEGIN(CryptoPP) - -template -class CRYPTOPP_NO_VTABLE AbstractPolicyHolder : public BASE -{ -public: - typedef POLICY_INTERFACE PolicyInterface; - virtual ~AbstractPolicyHolder() {} - -protected: - virtual const POLICY_INTERFACE & GetPolicy() const =0; - virtual POLICY_INTERFACE & AccessPolicy() =0; -}; - -template -class ConcretePolicyHolder : public BASE, protected POLICY -{ -protected: - const POLICY_INTERFACE & GetPolicy() const {return *this;} - POLICY_INTERFACE & AccessPolicy() {return *this;} -}; - -enum KeystreamOperationFlags {OUTPUT_ALIGNED=1, INPUT_ALIGNED=2, INPUT_NULL = 4}; -enum KeystreamOperation { - WRITE_KEYSTREAM = INPUT_NULL, - WRITE_KEYSTREAM_ALIGNED = INPUT_NULL | OUTPUT_ALIGNED, - XOR_KEYSTREAM = 0, - XOR_KEYSTREAM_INPUT_ALIGNED = INPUT_ALIGNED, - XOR_KEYSTREAM_OUTPUT_ALIGNED= OUTPUT_ALIGNED, - XOR_KEYSTREAM_BOTH_ALIGNED = OUTPUT_ALIGNED | INPUT_ALIGNED}; - -struct CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AdditiveCipherAbstractPolicy -{ - virtual ~AdditiveCipherAbstractPolicy() {} - virtual unsigned int GetAlignment() const {return 1;} - virtual unsigned int GetBytesPerIteration() const =0; - virtual unsigned int GetOptimalBlockSize() const {return GetBytesPerIteration();} - virtual unsigned int GetIterationsToBuffer() const =0; - virtual void WriteKeystream(byte *keystream, size_t iterationCount) - {OperateKeystream(KeystreamOperation(INPUT_NULL | (KeystreamOperationFlags)IsAlignedOn(keystream, GetAlignment())), keystream, NULL, iterationCount);} - virtual bool CanOperateKeystream() const {return false;} - virtual void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount) {assert(false);} - virtual void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length) =0; - virtual void CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length) {throw NotImplemented("SimpleKeyingInterface: this object doesn't support resynchronization");} - virtual bool CipherIsRandomAccess() const =0; - virtual void SeekToIteration(lword iterationCount) {assert(!CipherIsRandomAccess()); throw NotImplemented("StreamTransformation: this object doesn't support random access");} -}; - -template -struct CRYPTOPP_NO_VTABLE AdditiveCipherConcretePolicy : public BASE -{ - typedef WT WordType; - CRYPTOPP_CONSTANT(BYTES_PER_ITERATION = sizeof(WordType) * W) - -#if !(CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X64) - unsigned int GetAlignment() const {return GetAlignmentOf();} -#endif - unsigned int GetBytesPerIteration() const {return BYTES_PER_ITERATION;} - unsigned int GetIterationsToBuffer() const {return X;} - bool CanOperateKeystream() const {return true;} - virtual void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount) =0; -}; - -// use these to implement OperateKeystream -#define CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, b, i, a) \ - PutWord(bool(x & OUTPUT_ALIGNED), b, output+i*sizeof(WordType), (x & INPUT_NULL) ? a : a ^ GetWord(bool(x & INPUT_ALIGNED), b, input+i*sizeof(WordType))); -#define CRYPTOPP_KEYSTREAM_OUTPUT_XMM(x, i, a) {\ - __m128i t = (x & INPUT_NULL) ? a : _mm_xor_si128(a, (x & INPUT_ALIGNED) ? _mm_load_si128((__m128i *)input+i) : _mm_loadu_si128((__m128i *)input+i));\ - if (x & OUTPUT_ALIGNED) _mm_store_si128((__m128i *)output+i, t);\ - else _mm_storeu_si128((__m128i *)output+i, t);} -#define CRYPTOPP_KEYSTREAM_OUTPUT_SWITCH(x, y) \ - switch (operation) \ - { \ - case WRITE_KEYSTREAM: \ - x(WRITE_KEYSTREAM) \ - break; \ - case XOR_KEYSTREAM: \ - x(XOR_KEYSTREAM) \ - input += y; \ - break; \ - case XOR_KEYSTREAM_INPUT_ALIGNED: \ - x(XOR_KEYSTREAM_INPUT_ALIGNED) \ - input += y; \ - break; \ - case XOR_KEYSTREAM_OUTPUT_ALIGNED: \ - x(XOR_KEYSTREAM_OUTPUT_ALIGNED) \ - input += y; \ - break; \ - case WRITE_KEYSTREAM_ALIGNED: \ - x(WRITE_KEYSTREAM_ALIGNED) \ - break; \ - case XOR_KEYSTREAM_BOTH_ALIGNED: \ - x(XOR_KEYSTREAM_BOTH_ALIGNED) \ - input += y; \ - break; \ - } \ - output += y; - -template > -class CRYPTOPP_NO_VTABLE AdditiveCipherTemplate : public BASE, public RandomNumberGenerator -{ -public: - void GenerateBlock(byte *output, size_t size); - void ProcessData(byte *outString, const byte *inString, size_t length); - void Resynchronize(const byte *iv, int length=-1); - unsigned int OptimalBlockSize() const {return this->GetPolicy().GetOptimalBlockSize();} - unsigned int GetOptimalNextBlockSize() const {return (unsigned int)this->m_leftOver;} - unsigned int OptimalDataAlignment() const {return this->GetPolicy().GetAlignment();} - bool IsSelfInverting() const {return true;} - bool IsForwardTransformation() const {return true;} - bool IsRandomAccess() const {return this->GetPolicy().CipherIsRandomAccess();} - void Seek(lword position); - - typedef typename BASE::PolicyInterface PolicyInterface; - -protected: - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms); - - unsigned int GetBufferByteSize(const PolicyInterface &policy) const {return policy.GetBytesPerIteration() * policy.GetIterationsToBuffer();} - - inline byte * KeystreamBufferBegin() {return this->m_buffer.data();} - inline byte * KeystreamBufferEnd() {return (this->m_buffer.data() + this->m_buffer.size());} - - SecByteBlock m_buffer; - size_t m_leftOver; -}; - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CFB_CipherAbstractPolicy -{ -public: - virtual ~CFB_CipherAbstractPolicy() {} - virtual unsigned int GetAlignment() const =0; - virtual unsigned int GetBytesPerIteration() const =0; - virtual byte * GetRegisterBegin() =0; - virtual void TransformRegister() =0; - virtual bool CanIterate() const {return false;} - virtual void Iterate(byte *output, const byte *input, CipherDir dir, size_t iterationCount) {assert(false); throw 0;} - virtual void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length) =0; - virtual void CipherResynchronize(const byte *iv, size_t length) {throw NotImplemented("SimpleKeyingInterface: this object doesn't support resynchronization");} -}; - -template -struct CRYPTOPP_NO_VTABLE CFB_CipherConcretePolicy : public BASE -{ - typedef WT WordType; - - unsigned int GetAlignment() const {return sizeof(WordType);} - unsigned int GetBytesPerIteration() const {return sizeof(WordType) * W;} - bool CanIterate() const {return true;} - void TransformRegister() {this->Iterate(NULL, NULL, ENCRYPTION, 1);} - - template - struct RegisterOutput - { - RegisterOutput(byte *output, const byte *input, CipherDir dir) - : m_output(output), m_input(input), m_dir(dir) {} - - inline RegisterOutput& operator()(WordType ®isterWord) - { - assert(IsAligned(m_output)); - assert(IsAligned(m_input)); - - if (!NativeByteOrderIs(B::ToEnum())) - registerWord = ByteReverse(registerWord); - - if (m_dir == ENCRYPTION) - { - if (m_input == NULL) - assert(m_output == NULL); - else - { - WordType ct = *(const WordType *)m_input ^ registerWord; - registerWord = ct; - *(WordType*)m_output = ct; - m_input += sizeof(WordType); - m_output += sizeof(WordType); - } - } - else - { - WordType ct = *(const WordType *)m_input; - *(WordType*)m_output = registerWord ^ ct; - registerWord = ct; - m_input += sizeof(WordType); - m_output += sizeof(WordType); - } - - // registerWord is left unreversed so it can be xor-ed with further input - - return *this; - } - - byte *m_output; - const byte *m_input; - CipherDir m_dir; - }; -}; - -template -class CRYPTOPP_NO_VTABLE CFB_CipherTemplate : public BASE -{ -public: - void ProcessData(byte *outString, const byte *inString, size_t length); - void Resynchronize(const byte *iv, int length=-1); - unsigned int OptimalBlockSize() const {return this->GetPolicy().GetBytesPerIteration();} - unsigned int GetOptimalNextBlockSize() const {return (unsigned int)m_leftOver;} - unsigned int OptimalDataAlignment() const {return this->GetPolicy().GetAlignment();} - bool IsRandomAccess() const {return false;} - bool IsSelfInverting() const {return false;} - - typedef typename BASE::PolicyInterface PolicyInterface; - -protected: - virtual void CombineMessageAndShiftRegister(byte *output, byte *reg, const byte *message, size_t length) =0; - - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms); - - size_t m_leftOver; -}; - -template > -class CRYPTOPP_NO_VTABLE CFB_EncryptionTemplate : public CFB_CipherTemplate -{ - bool IsForwardTransformation() const {return true;} - void CombineMessageAndShiftRegister(byte *output, byte *reg, const byte *message, size_t length); -}; - -template > -class CRYPTOPP_NO_VTABLE CFB_DecryptionTemplate : public CFB_CipherTemplate -{ - bool IsForwardTransformation() const {return false;} - void CombineMessageAndShiftRegister(byte *output, byte *reg, const byte *message, size_t length); -}; - -template -class CFB_RequireFullDataBlocks : public BASE -{ -public: - unsigned int MandatoryBlockSize() const {return this->OptimalBlockSize();} -}; - -//! _ -template -class SymmetricCipherFinal : public AlgorithmImpl, INFO> -{ -public: - SymmetricCipherFinal() {} - SymmetricCipherFinal(const byte *key) - {this->SetKey(key, this->DEFAULT_KEYLENGTH);} - SymmetricCipherFinal(const byte *key, size_t length) - {this->SetKey(key, length);} - SymmetricCipherFinal(const byte *key, size_t length, const byte *iv) - {this->SetKeyWithIV(key, length, iv);} - - Clonable * Clone() const {return static_cast(new SymmetricCipherFinal(*this));} -}; - -NAMESPACE_END - -#ifdef CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES -#include "strciphr.cpp" -#endif - -NAMESPACE_BEGIN(CryptoPP) -CRYPTOPP_DLL_TEMPLATE_CLASS AbstractPolicyHolder; -CRYPTOPP_DLL_TEMPLATE_CLASS AdditiveCipherTemplate >; -CRYPTOPP_DLL_TEMPLATE_CLASS CFB_CipherTemplate >; -CRYPTOPP_DLL_TEMPLATE_CLASS CFB_EncryptionTemplate >; -CRYPTOPP_DLL_TEMPLATE_CLASS CFB_DecryptionTemplate >; -NAMESPACE_END - -#endif diff --git a/CryptoPP/tea.cpp b/CryptoPP/tea.cpp deleted file mode 100644 index b1fb6f140..000000000 --- a/CryptoPP/tea.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// tea.cpp - modified by Wei Dai from code in the original paper - -#include "pch.h" -#include "tea.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -static const word32 DELTA = 0x9e3779b9; -typedef BlockGetAndPut Block; - -void TEA::Base::UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms) -{ - AssertValidKeyLength(length); - - GetUserKey(BIG_ENDIAN_ORDER, m_k.begin(), 4, userKey, KEYLENGTH); - m_limit = GetRoundsAndThrowIfInvalid(params, this) * DELTA; -} - -void TEA::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 y, z; - Block::Get(inBlock)(y)(z); - - word32 sum = 0; - while (sum != m_limit) - { - sum += DELTA; - y += (z << 4) + m_k[0] ^ z + sum ^ (z >> 5) + m_k[1]; - z += (y << 4) + m_k[2] ^ y + sum ^ (y >> 5) + m_k[3]; - } - - Block::Put(xorBlock, outBlock)(y)(z); -} - -void TEA::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 y, z; - Block::Get(inBlock)(y)(z); - - word32 sum = m_limit; - while (sum != 0) - { - z -= (y << 4) + m_k[2] ^ y + sum ^ (y >> 5) + m_k[3]; - y -= (z << 4) + m_k[0] ^ z + sum ^ (z >> 5) + m_k[1]; - sum -= DELTA; - } - - Block::Put(xorBlock, outBlock)(y)(z); -} - -void XTEA::Base::UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms) -{ - AssertValidKeyLength(length); - - GetUserKey(BIG_ENDIAN_ORDER, m_k.begin(), 4, userKey, KEYLENGTH); - m_limit = GetRoundsAndThrowIfInvalid(params, this) * DELTA; -} - -void XTEA::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 y, z; - Block::Get(inBlock)(y)(z); - -#ifdef __SUNPRO_CC - // workaround needed on Sun Studio 12u1 Sun C++ 5.10 SunOS_i386 128229-02 2009/09/21 - size_t sum = 0; - while ((sum&0xffffffff) != m_limit) -#else - word32 sum = 0; - while (sum != m_limit) -#endif - { - y += (z<<4 ^ z>>5) + z ^ sum + m_k[sum&3]; - sum += DELTA; - z += (y<<4 ^ y>>5) + y ^ sum + m_k[sum>>11 & 3]; - } - - Block::Put(xorBlock, outBlock)(y)(z); -} - -void XTEA::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - word32 y, z; - Block::Get(inBlock)(y)(z); - -#ifdef __SUNPRO_CC - // workaround needed on Sun Studio 12u1 Sun C++ 5.10 SunOS_i386 128229-02 2009/09/21 - size_t sum = m_limit; - while ((sum&0xffffffff) != 0) -#else - word32 sum = m_limit; - while (sum != 0) -#endif - { - z -= (y<<4 ^ y>>5) + y ^ sum + m_k[sum>>11 & 3]; - sum -= DELTA; - y -= (z<<4 ^ z>>5) + z ^ sum + m_k[sum&3]; - } - - Block::Put(xorBlock, outBlock)(y)(z); -} - -#define MX (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(m_k[p&3^e]^z) - -void BTEA::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - unsigned int n = m_blockSize / 4; - word32 *v = (word32*)outBlock; - ConditionalByteReverse(BIG_ENDIAN_ORDER, v, (const word32*)inBlock, m_blockSize); - - word32 y = v[0], z = v[n-1], e; - word32 p, q = 6+52/n; - word32 sum = 0; - - while (q-- > 0) - { - sum += DELTA; - e = sum>>2 & 3; - for (p = 0; p < n-1; p++) - { - y = v[p+1]; - z = v[p] += MX; - } - y = v[0]; - z = v[n-1] += MX; - } - - ConditionalByteReverse(BIG_ENDIAN_ORDER, v, v, m_blockSize); -} - -void BTEA::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const -{ - unsigned int n = m_blockSize / 4; - word32 *v = (word32*)outBlock; - ConditionalByteReverse(BIG_ENDIAN_ORDER, v, (const word32*)inBlock, m_blockSize); - - word32 y = v[0], z = v[n-1], e; - word32 p, q = 6+52/n; - word32 sum = q * DELTA; - - while (sum != 0) - { - e = sum>>2 & 3; - for (p = n-1; p > 0; p--) - { - z = v[p-1]; - y = v[p] -= MX; - } - - z = v[n-1]; - y = v[0] -= MX; - sum -= DELTA; - } - - ConditionalByteReverse(BIG_ENDIAN_ORDER, v, v, m_blockSize); -} - -NAMESPACE_END diff --git a/CryptoPP/tea.h b/CryptoPP/tea.h deleted file mode 100644 index d8ddded86..000000000 --- a/CryptoPP/tea.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef CRYPTOPP_TEA_H -#define CRYPTOPP_TEA_H - -/** \file -*/ - -#include "seckey.h" -#include "secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -struct TEA_Info : public FixedBlockSize<8>, public FixedKeyLength<16>, public VariableRounds<32> -{ - static const char *StaticAlgorithmName() {return "TEA";} -}; - -/// TEA -class TEA : public TEA_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl - { - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms); - - protected: - FixedSizeSecBlock m_k; - word32 m_limit; - }; - - class CRYPTOPP_NO_VTABLE Enc : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - - class CRYPTOPP_NO_VTABLE Dec : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -typedef TEA::Encryption TEAEncryption; -typedef TEA::Decryption TEADecryption; - -//! _ -struct XTEA_Info : public FixedBlockSize<8>, public FixedKeyLength<16>, public VariableRounds<32> -{ - static const char *StaticAlgorithmName() {return "XTEA";} -}; - -/// XTEA -class XTEA : public XTEA_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl - { - public: - void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms); - - protected: - FixedSizeSecBlock m_k; - word32 m_limit; - }; - - class CRYPTOPP_NO_VTABLE Enc : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - - class CRYPTOPP_NO_VTABLE Dec : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -//! _ -struct BTEA_Info : public FixedKeyLength<16> -{ - static const char *StaticAlgorithmName() {return "BTEA";} -}; - -//! corrected Block TEA (as described in "xxtea"). -/*! This class hasn't been tested yet. */ -class BTEA : public BTEA_Info, public BlockCipherDocumentation -{ - class CRYPTOPP_NO_VTABLE Base : public AlgorithmImpl, BTEA_Info>, public BTEA_Info - { - public: - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) - { - m_blockSize = params.GetIntValueWithDefault("BlockSize", 60*4); - GetUserKey(BIG_ENDIAN_ORDER, m_k.begin(), 4, key, KEYLENGTH); - } - - unsigned int BlockSize() const {return m_blockSize;} - - protected: - FixedSizeSecBlock m_k; - unsigned int m_blockSize; - }; - - class CRYPTOPP_NO_VTABLE Enc : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - - class CRYPTOPP_NO_VTABLE Dec : public Base - { - public: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - -public: - typedef BlockCipherFinal Encryption; - typedef BlockCipherFinal Decryption; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/tiger.cpp b/CryptoPP/tiger.cpp deleted file mode 100644 index c6c05caed..000000000 --- a/CryptoPP/tiger.cpp +++ /dev/null @@ -1,265 +0,0 @@ -// tiger.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "tiger.h" -#include "misc.h" -#include "cpu.h" - -NAMESPACE_BEGIN(CryptoPP) - -void Tiger::InitState(HashWordType *state) -{ - state[0] = W64LIT(0x0123456789ABCDEF); - state[1] = W64LIT(0xFEDCBA9876543210); - state[2] = W64LIT(0xF096A5B4C3B2E187); -} - -void Tiger::TruncatedFinal(byte *hash, size_t size) -{ - ThrowIfInvalidTruncatedSize(size); - - PadLastBlock(56, 0x01); - CorrectEndianess(m_data, m_data, 56); - - m_data[7] = GetBitCountLo(); - - Transform(m_state, m_data); - CorrectEndianess(m_state, m_state, DigestSize()); - memcpy(hash, m_state, size); - - Restart(); // reinit for next use -} - -void Tiger::Transform (word64 *digest, const word64 *X) -{ -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 - if (HasSSE2()) - { -#ifdef __GNUC__ - __asm__ __volatile__ - ( - ".intel_syntax noprefix;" - AS1( push ebx) -#else - #if _MSC_VER < 1300 - const word64 *t = table; - AS2( mov edx, t) - #else - AS2( lea edx, [table]) - #endif - AS2( mov eax, digest) - AS2( mov esi, X) -#endif - AS2( movq mm0, [eax]) - AS2( movq mm1, [eax+1*8]) - AS2( movq mm5, mm1) - AS2( movq mm2, [eax+2*8]) - AS2( movq mm7, [edx+4*2048+0*8]) - AS2( movq mm6, [edx+4*2048+1*8]) - AS2( mov ecx, esp) - AS2( and esp, 0xfffffff0) - AS2( sub esp, 8*8) - AS1( push ecx) - -#define SSE2_round(a,b,c,x,mul) \ - AS2( pxor c, [x])\ - AS2( movd ecx, c)\ - AS2( movzx edi, cl)\ - AS2( movq mm3, [edx+0*2048+edi*8])\ - AS2( movzx edi, ch)\ - AS2( movq mm4, [edx+3*2048+edi*8])\ - AS2( shr ecx, 16)\ - AS2( movzx edi, cl)\ - AS2( pxor mm3, [edx+1*2048+edi*8])\ - AS2( movzx edi, ch)\ - AS2( pxor mm4, [edx+2*2048+edi*8])\ - AS3( pextrw ecx, c, 2)\ - AS2( movzx edi, cl)\ - AS2( pxor mm3, [edx+2*2048+edi*8])\ - AS2( movzx edi, ch)\ - AS2( pxor mm4, [edx+1*2048+edi*8])\ - AS3( pextrw ecx, c, 3)\ - AS2( movzx edi, cl)\ - AS2( pxor mm3, [edx+3*2048+edi*8])\ - AS2( psubq a, mm3)\ - AS2( movzx edi, ch)\ - AS2( pxor mm4, [edx+0*2048+edi*8])\ - AS2( paddq b, mm4)\ - SSE2_mul_##mul(b) - -#define SSE2_mul_5(b) \ - AS2( movq mm3, b)\ - AS2( psllq b, 2)\ - AS2( paddq b, mm3) - -#define SSE2_mul_7(b) \ - AS2( movq mm3, b)\ - AS2( psllq b, 3)\ - AS2( psubq b, mm3) - -#define SSE2_mul_9(b) \ - AS2( movq mm3, b)\ - AS2( psllq b, 3)\ - AS2( paddq b, mm3) - -#define label2_5 1 -#define label2_7 2 -#define label2_9 3 - -#define SSE2_pass(A,B,C,mul,X) \ - AS2( xor ebx, ebx)\ - ASL(mul)\ - SSE2_round(A,B,C,X+0*8+ebx,mul)\ - SSE2_round(B,C,A,X+1*8+ebx,mul)\ - AS2( cmp ebx, 6*8)\ - ASJ( je, label2_##mul, f)\ - SSE2_round(C,A,B,X+2*8+ebx,mul)\ - AS2( add ebx, 3*8)\ - ASJ( jmp, mul, b)\ - ASL(label2_##mul) - -#define SSE2_key_schedule(Y,X) \ - AS2( movq mm3, [X+7*8])\ - AS2( pxor mm3, mm6)\ - AS2( movq mm4, [X+0*8])\ - AS2( psubq mm4, mm3)\ - AS2( movq [Y+0*8], mm4)\ - AS2( pxor mm4, [X+1*8])\ - AS2( movq mm3, mm4)\ - AS2( movq [Y+1*8], mm4)\ - AS2( paddq mm4, [X+2*8])\ - AS2( pxor mm3, mm7)\ - AS2( psllq mm3, 19)\ - AS2( movq [Y+2*8], mm4)\ - AS2( pxor mm3, mm4)\ - AS2( movq mm4, [X+3*8])\ - AS2( psubq mm4, mm3)\ - AS2( movq [Y+3*8], mm4)\ - AS2( pxor mm4, [X+4*8])\ - AS2( movq mm3, mm4)\ - AS2( movq [Y+4*8], mm4)\ - AS2( paddq mm4, [X+5*8])\ - AS2( pxor mm3, mm7)\ - AS2( psrlq mm3, 23)\ - AS2( movq [Y+5*8], mm4)\ - AS2( pxor mm3, mm4)\ - AS2( movq mm4, [X+6*8])\ - AS2( psubq mm4, mm3)\ - AS2( movq [Y+6*8], mm4)\ - AS2( pxor mm4, [X+7*8])\ - AS2( movq mm3, mm4)\ - AS2( movq [Y+7*8], mm4)\ - AS2( paddq mm4, [Y+0*8])\ - AS2( pxor mm3, mm7)\ - AS2( psllq mm3, 19)\ - AS2( movq [Y+0*8], mm4)\ - AS2( pxor mm3, mm4)\ - AS2( movq mm4, [Y+1*8])\ - AS2( psubq mm4, mm3)\ - AS2( movq [Y+1*8], mm4)\ - AS2( pxor mm4, [Y+2*8])\ - AS2( movq mm3, mm4)\ - AS2( movq [Y+2*8], mm4)\ - AS2( paddq mm4, [Y+3*8])\ - AS2( pxor mm3, mm7)\ - AS2( psrlq mm3, 23)\ - AS2( movq [Y+3*8], mm4)\ - AS2( pxor mm3, mm4)\ - AS2( movq mm4, [Y+4*8])\ - AS2( psubq mm4, mm3)\ - AS2( movq [Y+4*8], mm4)\ - AS2( pxor mm4, [Y+5*8])\ - AS2( movq [Y+5*8], mm4)\ - AS2( paddq mm4, [Y+6*8])\ - AS2( movq [Y+6*8], mm4)\ - AS2( pxor mm4, [edx+4*2048+2*8])\ - AS2( movq mm3, [Y+7*8])\ - AS2( psubq mm3, mm4)\ - AS2( movq [Y+7*8], mm3) - - SSE2_pass(mm0, mm1, mm2, 5, esi) - SSE2_key_schedule(esp+4, esi) - SSE2_pass(mm2, mm0, mm1, 7, esp+4) - SSE2_key_schedule(esp+4, esp+4) - SSE2_pass(mm1, mm2, mm0, 9, esp+4) - - AS2( pxor mm0, [eax+0*8]) - AS2( movq [eax+0*8], mm0) - AS2( psubq mm1, mm5) - AS2( movq [eax+1*8], mm1) - AS2( paddq mm2, [eax+2*8]) - AS2( movq [eax+2*8], mm2) - - AS1( pop esp) - AS1( emms) -#ifdef __GNUC__ - AS1( pop ebx) - ".att_syntax prefix;" - : - : "a" (digest), "S" (X), "d" (table) - : "%ecx", "%edi", "memory", "cc" - ); -#endif - } - else -#endif - { - word64 a = digest[0]; - word64 b = digest[1]; - word64 c = digest[2]; - word64 Y[8]; - -#define t1 (table) -#define t2 (table+256) -#define t3 (table+256*2) -#define t4 (table+256*3) - -#define round(a,b,c,x,mul) \ - c ^= x; \ - a -= t1[GETBYTE(c,0)] ^ t2[GETBYTE(c,2)] ^ t3[GETBYTE(c,4)] ^ t4[GETBYTE(c,6)]; \ - b += t4[GETBYTE(c,1)] ^ t3[GETBYTE(c,3)] ^ t2[GETBYTE(c,5)] ^ t1[GETBYTE(c,7)]; \ - b *= mul - -#define pass(a,b,c,mul,X) {\ - int i=0;\ - while (true)\ - {\ - round(a,b,c,X[i+0],mul); \ - round(b,c,a,X[i+1],mul); \ - if (i==6)\ - break;\ - round(c,a,b,X[i+2],mul); \ - i+=3;\ - }} - -#define key_schedule(Y,X) \ - Y[0] = X[0] - (X[7]^W64LIT(0xA5A5A5A5A5A5A5A5)); \ - Y[1] = X[1] ^ Y[0]; \ - Y[2] = X[2] + Y[1]; \ - Y[3] = X[3] - (Y[2] ^ ((~Y[1])<<19)); \ - Y[4] = X[4] ^ Y[3]; \ - Y[5] = X[5] + Y[4]; \ - Y[6] = X[6] - (Y[5] ^ ((~Y[4])>>23)); \ - Y[7] = X[7] ^ Y[6]; \ - Y[0] += Y[7]; \ - Y[1] -= Y[0] ^ ((~Y[7])<<19); \ - Y[2] ^= Y[1]; \ - Y[3] += Y[2]; \ - Y[4] -= Y[3] ^ ((~Y[2])>>23); \ - Y[5] ^= Y[4]; \ - Y[6] += Y[5]; \ - Y[7] -= Y[6] ^ W64LIT(0x0123456789ABCDEF) - - pass(a,b,c,5,X); - key_schedule(Y,X); - pass(c,a,b,7,Y); - key_schedule(Y,Y); - pass(b,c,a,9,Y); - - digest[0] = a ^ digest[0]; - digest[1] = b - digest[1]; - digest[2] = c + digest[2]; - } -} - -NAMESPACE_END diff --git a/CryptoPP/tiger.h b/CryptoPP/tiger.h deleted file mode 100644 index 5f6e941ac..000000000 --- a/CryptoPP/tiger.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef CRYPTOPP_TIGER_H -#define CRYPTOPP_TIGER_H - -#include "config.h" -#include "iterhash.h" - -NAMESPACE_BEGIN(CryptoPP) - -/// Tiger -class Tiger : public IteratedHashWithStaticTransform -{ -public: - static void InitState(HashWordType *state); - static void Transform(word64 *digest, const word64 *data); - void TruncatedFinal(byte *hash, size_t size); - static const char * StaticAlgorithmName() {return "Tiger";} - -protected: - static const word64 table[4*256+3]; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/tigertab.cpp b/CryptoPP/tigertab.cpp deleted file mode 100644 index 5c1595b5b..000000000 --- a/CryptoPP/tigertab.cpp +++ /dev/null @@ -1,525 +0,0 @@ -#include "pch.h" -#include "tiger.h" - -NAMESPACE_BEGIN(CryptoPP) - -const word64 Tiger::table[4*256+3] = -{ - W64LIT(0x02AAB17CF7E90C5E) /* 0 */, W64LIT(0xAC424B03E243A8EC) /* 1 */, - W64LIT(0x72CD5BE30DD5FCD3) /* 2 */, W64LIT(0x6D019B93F6F97F3A) /* 3 */, - W64LIT(0xCD9978FFD21F9193) /* 4 */, W64LIT(0x7573A1C9708029E2) /* 5 */, - W64LIT(0xB164326B922A83C3) /* 6 */, W64LIT(0x46883EEE04915870) /* 7 */, - W64LIT(0xEAACE3057103ECE6) /* 8 */, W64LIT(0xC54169B808A3535C) /* 9 */, - W64LIT(0x4CE754918DDEC47C) /* 10 */, W64LIT(0x0AA2F4DFDC0DF40C) /* 11 */, - W64LIT(0x10B76F18A74DBEFA) /* 12 */, W64LIT(0xC6CCB6235AD1AB6A) /* 13 */, - W64LIT(0x13726121572FE2FF) /* 14 */, W64LIT(0x1A488C6F199D921E) /* 15 */, - W64LIT(0x4BC9F9F4DA0007CA) /* 16 */, W64LIT(0x26F5E6F6E85241C7) /* 17 */, - W64LIT(0x859079DBEA5947B6) /* 18 */, W64LIT(0x4F1885C5C99E8C92) /* 19 */, - W64LIT(0xD78E761EA96F864B) /* 20 */, W64LIT(0x8E36428C52B5C17D) /* 21 */, - W64LIT(0x69CF6827373063C1) /* 22 */, W64LIT(0xB607C93D9BB4C56E) /* 23 */, - W64LIT(0x7D820E760E76B5EA) /* 24 */, W64LIT(0x645C9CC6F07FDC42) /* 25 */, - W64LIT(0xBF38A078243342E0) /* 26 */, W64LIT(0x5F6B343C9D2E7D04) /* 27 */, - W64LIT(0xF2C28AEB600B0EC6) /* 28 */, W64LIT(0x6C0ED85F7254BCAC) /* 29 */, - W64LIT(0x71592281A4DB4FE5) /* 30 */, W64LIT(0x1967FA69CE0FED9F) /* 31 */, - W64LIT(0xFD5293F8B96545DB) /* 32 */, W64LIT(0xC879E9D7F2A7600B) /* 33 */, - W64LIT(0x860248920193194E) /* 34 */, W64LIT(0xA4F9533B2D9CC0B3) /* 35 */, - W64LIT(0x9053836C15957613) /* 36 */, W64LIT(0xDB6DCF8AFC357BF1) /* 37 */, - W64LIT(0x18BEEA7A7A370F57) /* 38 */, W64LIT(0x037117CA50B99066) /* 39 */, - W64LIT(0x6AB30A9774424A35) /* 40 */, W64LIT(0xF4E92F02E325249B) /* 41 */, - W64LIT(0x7739DB07061CCAE1) /* 42 */, W64LIT(0xD8F3B49CECA42A05) /* 43 */, - W64LIT(0xBD56BE3F51382F73) /* 44 */, W64LIT(0x45FAED5843B0BB28) /* 45 */, - W64LIT(0x1C813D5C11BF1F83) /* 46 */, W64LIT(0x8AF0E4B6D75FA169) /* 47 */, - W64LIT(0x33EE18A487AD9999) /* 48 */, W64LIT(0x3C26E8EAB1C94410) /* 49 */, - W64LIT(0xB510102BC0A822F9) /* 50 */, W64LIT(0x141EEF310CE6123B) /* 51 */, - W64LIT(0xFC65B90059DDB154) /* 52 */, W64LIT(0xE0158640C5E0E607) /* 53 */, - W64LIT(0x884E079826C3A3CF) /* 54 */, W64LIT(0x930D0D9523C535FD) /* 55 */, - W64LIT(0x35638D754E9A2B00) /* 56 */, W64LIT(0x4085FCCF40469DD5) /* 57 */, - W64LIT(0xC4B17AD28BE23A4C) /* 58 */, W64LIT(0xCAB2F0FC6A3E6A2E) /* 59 */, - W64LIT(0x2860971A6B943FCD) /* 60 */, W64LIT(0x3DDE6EE212E30446) /* 61 */, - W64LIT(0x6222F32AE01765AE) /* 62 */, W64LIT(0x5D550BB5478308FE) /* 63 */, - W64LIT(0xA9EFA98DA0EDA22A) /* 64 */, W64LIT(0xC351A71686C40DA7) /* 65 */, - W64LIT(0x1105586D9C867C84) /* 66 */, W64LIT(0xDCFFEE85FDA22853) /* 67 */, - W64LIT(0xCCFBD0262C5EEF76) /* 68 */, W64LIT(0xBAF294CB8990D201) /* 69 */, - W64LIT(0xE69464F52AFAD975) /* 70 */, W64LIT(0x94B013AFDF133E14) /* 71 */, - W64LIT(0x06A7D1A32823C958) /* 72 */, W64LIT(0x6F95FE5130F61119) /* 73 */, - W64LIT(0xD92AB34E462C06C0) /* 74 */, W64LIT(0xED7BDE33887C71D2) /* 75 */, - W64LIT(0x79746D6E6518393E) /* 76 */, W64LIT(0x5BA419385D713329) /* 77 */, - W64LIT(0x7C1BA6B948A97564) /* 78 */, W64LIT(0x31987C197BFDAC67) /* 79 */, - W64LIT(0xDE6C23C44B053D02) /* 80 */, W64LIT(0x581C49FED002D64D) /* 81 */, - W64LIT(0xDD474D6338261571) /* 82 */, W64LIT(0xAA4546C3E473D062) /* 83 */, - W64LIT(0x928FCE349455F860) /* 84 */, W64LIT(0x48161BBACAAB94D9) /* 85 */, - W64LIT(0x63912430770E6F68) /* 86 */, W64LIT(0x6EC8A5E602C6641C) /* 87 */, - W64LIT(0x87282515337DDD2B) /* 88 */, W64LIT(0x2CDA6B42034B701B) /* 89 */, - W64LIT(0xB03D37C181CB096D) /* 90 */, W64LIT(0xE108438266C71C6F) /* 91 */, - W64LIT(0x2B3180C7EB51B255) /* 92 */, W64LIT(0xDF92B82F96C08BBC) /* 93 */, - W64LIT(0x5C68C8C0A632F3BA) /* 94 */, W64LIT(0x5504CC861C3D0556) /* 95 */, - W64LIT(0xABBFA4E55FB26B8F) /* 96 */, W64LIT(0x41848B0AB3BACEB4) /* 97 */, - W64LIT(0xB334A273AA445D32) /* 98 */, W64LIT(0xBCA696F0A85AD881) /* 99 */, - W64LIT(0x24F6EC65B528D56C) /* 100 */, W64LIT(0x0CE1512E90F4524A) /* 101 */, - W64LIT(0x4E9DD79D5506D35A) /* 102 */, W64LIT(0x258905FAC6CE9779) /* 103 */, - W64LIT(0x2019295B3E109B33) /* 104 */, W64LIT(0xF8A9478B73A054CC) /* 105 */, - W64LIT(0x2924F2F934417EB0) /* 106 */, W64LIT(0x3993357D536D1BC4) /* 107 */, - W64LIT(0x38A81AC21DB6FF8B) /* 108 */, W64LIT(0x47C4FBF17D6016BF) /* 109 */, - W64LIT(0x1E0FAADD7667E3F5) /* 110 */, W64LIT(0x7ABCFF62938BEB96) /* 111 */, - W64LIT(0xA78DAD948FC179C9) /* 112 */, W64LIT(0x8F1F98B72911E50D) /* 113 */, - W64LIT(0x61E48EAE27121A91) /* 114 */, W64LIT(0x4D62F7AD31859808) /* 115 */, - W64LIT(0xECEBA345EF5CEAEB) /* 116 */, W64LIT(0xF5CEB25EBC9684CE) /* 117 */, - W64LIT(0xF633E20CB7F76221) /* 118 */, W64LIT(0xA32CDF06AB8293E4) /* 119 */, - W64LIT(0x985A202CA5EE2CA4) /* 120 */, W64LIT(0xCF0B8447CC8A8FB1) /* 121 */, - W64LIT(0x9F765244979859A3) /* 122 */, W64LIT(0xA8D516B1A1240017) /* 123 */, - W64LIT(0x0BD7BA3EBB5DC726) /* 124 */, W64LIT(0xE54BCA55B86ADB39) /* 125 */, - W64LIT(0x1D7A3AFD6C478063) /* 126 */, W64LIT(0x519EC608E7669EDD) /* 127 */, - W64LIT(0x0E5715A2D149AA23) /* 128 */, W64LIT(0x177D4571848FF194) /* 129 */, - W64LIT(0xEEB55F3241014C22) /* 130 */, W64LIT(0x0F5E5CA13A6E2EC2) /* 131 */, - W64LIT(0x8029927B75F5C361) /* 132 */, W64LIT(0xAD139FABC3D6E436) /* 133 */, - W64LIT(0x0D5DF1A94CCF402F) /* 134 */, W64LIT(0x3E8BD948BEA5DFC8) /* 135 */, - W64LIT(0xA5A0D357BD3FF77E) /* 136 */, W64LIT(0xA2D12E251F74F645) /* 137 */, - W64LIT(0x66FD9E525E81A082) /* 138 */, W64LIT(0x2E0C90CE7F687A49) /* 139 */, - W64LIT(0xC2E8BCBEBA973BC5) /* 140 */, W64LIT(0x000001BCE509745F) /* 141 */, - W64LIT(0x423777BBE6DAB3D6) /* 142 */, W64LIT(0xD1661C7EAEF06EB5) /* 143 */, - W64LIT(0xA1781F354DAACFD8) /* 144 */, W64LIT(0x2D11284A2B16AFFC) /* 145 */, - W64LIT(0xF1FC4F67FA891D1F) /* 146 */, W64LIT(0x73ECC25DCB920ADA) /* 147 */, - W64LIT(0xAE610C22C2A12651) /* 148 */, W64LIT(0x96E0A810D356B78A) /* 149 */, - W64LIT(0x5A9A381F2FE7870F) /* 150 */, W64LIT(0xD5AD62EDE94E5530) /* 151 */, - W64LIT(0xD225E5E8368D1427) /* 152 */, W64LIT(0x65977B70C7AF4631) /* 153 */, - W64LIT(0x99F889B2DE39D74F) /* 154 */, W64LIT(0x233F30BF54E1D143) /* 155 */, - W64LIT(0x9A9675D3D9A63C97) /* 156 */, W64LIT(0x5470554FF334F9A8) /* 157 */, - W64LIT(0x166ACB744A4F5688) /* 158 */, W64LIT(0x70C74CAAB2E4AEAD) /* 159 */, - W64LIT(0xF0D091646F294D12) /* 160 */, W64LIT(0x57B82A89684031D1) /* 161 */, - W64LIT(0xEFD95A5A61BE0B6B) /* 162 */, W64LIT(0x2FBD12E969F2F29A) /* 163 */, - W64LIT(0x9BD37013FEFF9FE8) /* 164 */, W64LIT(0x3F9B0404D6085A06) /* 165 */, - W64LIT(0x4940C1F3166CFE15) /* 166 */, W64LIT(0x09542C4DCDF3DEFB) /* 167 */, - W64LIT(0xB4C5218385CD5CE3) /* 168 */, W64LIT(0xC935B7DC4462A641) /* 169 */, - W64LIT(0x3417F8A68ED3B63F) /* 170 */, W64LIT(0xB80959295B215B40) /* 171 */, - W64LIT(0xF99CDAEF3B8C8572) /* 172 */, W64LIT(0x018C0614F8FCB95D) /* 173 */, - W64LIT(0x1B14ACCD1A3ACDF3) /* 174 */, W64LIT(0x84D471F200BB732D) /* 175 */, - W64LIT(0xC1A3110E95E8DA16) /* 176 */, W64LIT(0x430A7220BF1A82B8) /* 177 */, - W64LIT(0xB77E090D39DF210E) /* 178 */, W64LIT(0x5EF4BD9F3CD05E9D) /* 179 */, - W64LIT(0x9D4FF6DA7E57A444) /* 180 */, W64LIT(0xDA1D60E183D4A5F8) /* 181 */, - W64LIT(0xB287C38417998E47) /* 182 */, W64LIT(0xFE3EDC121BB31886) /* 183 */, - W64LIT(0xC7FE3CCC980CCBEF) /* 184 */, W64LIT(0xE46FB590189BFD03) /* 185 */, - W64LIT(0x3732FD469A4C57DC) /* 186 */, W64LIT(0x7EF700A07CF1AD65) /* 187 */, - W64LIT(0x59C64468A31D8859) /* 188 */, W64LIT(0x762FB0B4D45B61F6) /* 189 */, - W64LIT(0x155BAED099047718) /* 190 */, W64LIT(0x68755E4C3D50BAA6) /* 191 */, - W64LIT(0xE9214E7F22D8B4DF) /* 192 */, W64LIT(0x2ADDBF532EAC95F4) /* 193 */, - W64LIT(0x32AE3909B4BD0109) /* 194 */, W64LIT(0x834DF537B08E3450) /* 195 */, - W64LIT(0xFA209DA84220728D) /* 196 */, W64LIT(0x9E691D9B9EFE23F7) /* 197 */, - W64LIT(0x0446D288C4AE8D7F) /* 198 */, W64LIT(0x7B4CC524E169785B) /* 199 */, - W64LIT(0x21D87F0135CA1385) /* 200 */, W64LIT(0xCEBB400F137B8AA5) /* 201 */, - W64LIT(0x272E2B66580796BE) /* 202 */, W64LIT(0x3612264125C2B0DE) /* 203 */, - W64LIT(0x057702BDAD1EFBB2) /* 204 */, W64LIT(0xD4BABB8EACF84BE9) /* 205 */, - W64LIT(0x91583139641BC67B) /* 206 */, W64LIT(0x8BDC2DE08036E024) /* 207 */, - W64LIT(0x603C8156F49F68ED) /* 208 */, W64LIT(0xF7D236F7DBEF5111) /* 209 */, - W64LIT(0x9727C4598AD21E80) /* 210 */, W64LIT(0xA08A0896670A5FD7) /* 211 */, - W64LIT(0xCB4A8F4309EBA9CB) /* 212 */, W64LIT(0x81AF564B0F7036A1) /* 213 */, - W64LIT(0xC0B99AA778199ABD) /* 214 */, W64LIT(0x959F1EC83FC8E952) /* 215 */, - W64LIT(0x8C505077794A81B9) /* 216 */, W64LIT(0x3ACAAF8F056338F0) /* 217 */, - W64LIT(0x07B43F50627A6778) /* 218 */, W64LIT(0x4A44AB49F5ECCC77) /* 219 */, - W64LIT(0x3BC3D6E4B679EE98) /* 220 */, W64LIT(0x9CC0D4D1CF14108C) /* 221 */, - W64LIT(0x4406C00B206BC8A0) /* 222 */, W64LIT(0x82A18854C8D72D89) /* 223 */, - W64LIT(0x67E366B35C3C432C) /* 224 */, W64LIT(0xB923DD61102B37F2) /* 225 */, - W64LIT(0x56AB2779D884271D) /* 226 */, W64LIT(0xBE83E1B0FF1525AF) /* 227 */, - W64LIT(0xFB7C65D4217E49A9) /* 228 */, W64LIT(0x6BDBE0E76D48E7D4) /* 229 */, - W64LIT(0x08DF828745D9179E) /* 230 */, W64LIT(0x22EA6A9ADD53BD34) /* 231 */, - W64LIT(0xE36E141C5622200A) /* 232 */, W64LIT(0x7F805D1B8CB750EE) /* 233 */, - W64LIT(0xAFE5C7A59F58E837) /* 234 */, W64LIT(0xE27F996A4FB1C23C) /* 235 */, - W64LIT(0xD3867DFB0775F0D0) /* 236 */, W64LIT(0xD0E673DE6E88891A) /* 237 */, - W64LIT(0x123AEB9EAFB86C25) /* 238 */, W64LIT(0x30F1D5D5C145B895) /* 239 */, - W64LIT(0xBB434A2DEE7269E7) /* 240 */, W64LIT(0x78CB67ECF931FA38) /* 241 */, - W64LIT(0xF33B0372323BBF9C) /* 242 */, W64LIT(0x52D66336FB279C74) /* 243 */, - W64LIT(0x505F33AC0AFB4EAA) /* 244 */, W64LIT(0xE8A5CD99A2CCE187) /* 245 */, - W64LIT(0x534974801E2D30BB) /* 246 */, W64LIT(0x8D2D5711D5876D90) /* 247 */, - W64LIT(0x1F1A412891BC038E) /* 248 */, W64LIT(0xD6E2E71D82E56648) /* 249 */, - W64LIT(0x74036C3A497732B7) /* 250 */, W64LIT(0x89B67ED96361F5AB) /* 251 */, - W64LIT(0xFFED95D8F1EA02A2) /* 252 */, W64LIT(0xE72B3BD61464D43D) /* 253 */, - W64LIT(0xA6300F170BDC4820) /* 254 */, W64LIT(0xEBC18760ED78A77A) /* 255 */, - W64LIT(0xE6A6BE5A05A12138) /* 256 */, W64LIT(0xB5A122A5B4F87C98) /* 257 */, - W64LIT(0x563C6089140B6990) /* 258 */, W64LIT(0x4C46CB2E391F5DD5) /* 259 */, - W64LIT(0xD932ADDBC9B79434) /* 260 */, W64LIT(0x08EA70E42015AFF5) /* 261 */, - W64LIT(0xD765A6673E478CF1) /* 262 */, W64LIT(0xC4FB757EAB278D99) /* 263 */, - W64LIT(0xDF11C6862D6E0692) /* 264 */, W64LIT(0xDDEB84F10D7F3B16) /* 265 */, - W64LIT(0x6F2EF604A665EA04) /* 266 */, W64LIT(0x4A8E0F0FF0E0DFB3) /* 267 */, - W64LIT(0xA5EDEEF83DBCBA51) /* 268 */, W64LIT(0xFC4F0A2A0EA4371E) /* 269 */, - W64LIT(0xE83E1DA85CB38429) /* 270 */, W64LIT(0xDC8FF882BA1B1CE2) /* 271 */, - W64LIT(0xCD45505E8353E80D) /* 272 */, W64LIT(0x18D19A00D4DB0717) /* 273 */, - W64LIT(0x34A0CFEDA5F38101) /* 274 */, W64LIT(0x0BE77E518887CAF2) /* 275 */, - W64LIT(0x1E341438B3C45136) /* 276 */, W64LIT(0xE05797F49089CCF9) /* 277 */, - W64LIT(0xFFD23F9DF2591D14) /* 278 */, W64LIT(0x543DDA228595C5CD) /* 279 */, - W64LIT(0x661F81FD99052A33) /* 280 */, W64LIT(0x8736E641DB0F7B76) /* 281 */, - W64LIT(0x15227725418E5307) /* 282 */, W64LIT(0xE25F7F46162EB2FA) /* 283 */, - W64LIT(0x48A8B2126C13D9FE) /* 284 */, W64LIT(0xAFDC541792E76EEA) /* 285 */, - W64LIT(0x03D912BFC6D1898F) /* 286 */, W64LIT(0x31B1AAFA1B83F51B) /* 287 */, - W64LIT(0xF1AC2796E42AB7D9) /* 288 */, W64LIT(0x40A3A7D7FCD2EBAC) /* 289 */, - W64LIT(0x1056136D0AFBBCC5) /* 290 */, W64LIT(0x7889E1DD9A6D0C85) /* 291 */, - W64LIT(0xD33525782A7974AA) /* 292 */, W64LIT(0xA7E25D09078AC09B) /* 293 */, - W64LIT(0xBD4138B3EAC6EDD0) /* 294 */, W64LIT(0x920ABFBE71EB9E70) /* 295 */, - W64LIT(0xA2A5D0F54FC2625C) /* 296 */, W64LIT(0xC054E36B0B1290A3) /* 297 */, - W64LIT(0xF6DD59FF62FE932B) /* 298 */, W64LIT(0x3537354511A8AC7D) /* 299 */, - W64LIT(0xCA845E9172FADCD4) /* 300 */, W64LIT(0x84F82B60329D20DC) /* 301 */, - W64LIT(0x79C62CE1CD672F18) /* 302 */, W64LIT(0x8B09A2ADD124642C) /* 303 */, - W64LIT(0xD0C1E96A19D9E726) /* 304 */, W64LIT(0x5A786A9B4BA9500C) /* 305 */, - W64LIT(0x0E020336634C43F3) /* 306 */, W64LIT(0xC17B474AEB66D822) /* 307 */, - W64LIT(0x6A731AE3EC9BAAC2) /* 308 */, W64LIT(0x8226667AE0840258) /* 309 */, - W64LIT(0x67D4567691CAECA5) /* 310 */, W64LIT(0x1D94155C4875ADB5) /* 311 */, - W64LIT(0x6D00FD985B813FDF) /* 312 */, W64LIT(0x51286EFCB774CD06) /* 313 */, - W64LIT(0x5E8834471FA744AF) /* 314 */, W64LIT(0xF72CA0AEE761AE2E) /* 315 */, - W64LIT(0xBE40E4CDAEE8E09A) /* 316 */, W64LIT(0xE9970BBB5118F665) /* 317 */, - W64LIT(0x726E4BEB33DF1964) /* 318 */, W64LIT(0x703B000729199762) /* 319 */, - W64LIT(0x4631D816F5EF30A7) /* 320 */, W64LIT(0xB880B5B51504A6BE) /* 321 */, - W64LIT(0x641793C37ED84B6C) /* 322 */, W64LIT(0x7B21ED77F6E97D96) /* 323 */, - W64LIT(0x776306312EF96B73) /* 324 */, W64LIT(0xAE528948E86FF3F4) /* 325 */, - W64LIT(0x53DBD7F286A3F8F8) /* 326 */, W64LIT(0x16CADCE74CFC1063) /* 327 */, - W64LIT(0x005C19BDFA52C6DD) /* 328 */, W64LIT(0x68868F5D64D46AD3) /* 329 */, - W64LIT(0x3A9D512CCF1E186A) /* 330 */, W64LIT(0x367E62C2385660AE) /* 331 */, - W64LIT(0xE359E7EA77DCB1D7) /* 332 */, W64LIT(0x526C0773749ABE6E) /* 333 */, - W64LIT(0x735AE5F9D09F734B) /* 334 */, W64LIT(0x493FC7CC8A558BA8) /* 335 */, - W64LIT(0xB0B9C1533041AB45) /* 336 */, W64LIT(0x321958BA470A59BD) /* 337 */, - W64LIT(0x852DB00B5F46C393) /* 338 */, W64LIT(0x91209B2BD336B0E5) /* 339 */, - W64LIT(0x6E604F7D659EF19F) /* 340 */, W64LIT(0xB99A8AE2782CCB24) /* 341 */, - W64LIT(0xCCF52AB6C814C4C7) /* 342 */, W64LIT(0x4727D9AFBE11727B) /* 343 */, - W64LIT(0x7E950D0C0121B34D) /* 344 */, W64LIT(0x756F435670AD471F) /* 345 */, - W64LIT(0xF5ADD442615A6849) /* 346 */, W64LIT(0x4E87E09980B9957A) /* 347 */, - W64LIT(0x2ACFA1DF50AEE355) /* 348 */, W64LIT(0xD898263AFD2FD556) /* 349 */, - W64LIT(0xC8F4924DD80C8FD6) /* 350 */, W64LIT(0xCF99CA3D754A173A) /* 351 */, - W64LIT(0xFE477BACAF91BF3C) /* 352 */, W64LIT(0xED5371F6D690C12D) /* 353 */, - W64LIT(0x831A5C285E687094) /* 354 */, W64LIT(0xC5D3C90A3708A0A4) /* 355 */, - W64LIT(0x0F7F903717D06580) /* 356 */, W64LIT(0x19F9BB13B8FDF27F) /* 357 */, - W64LIT(0xB1BD6F1B4D502843) /* 358 */, W64LIT(0x1C761BA38FFF4012) /* 359 */, - W64LIT(0x0D1530C4E2E21F3B) /* 360 */, W64LIT(0x8943CE69A7372C8A) /* 361 */, - W64LIT(0xE5184E11FEB5CE66) /* 362 */, W64LIT(0x618BDB80BD736621) /* 363 */, - W64LIT(0x7D29BAD68B574D0B) /* 364 */, W64LIT(0x81BB613E25E6FE5B) /* 365 */, - W64LIT(0x071C9C10BC07913F) /* 366 */, W64LIT(0xC7BEEB7909AC2D97) /* 367 */, - W64LIT(0xC3E58D353BC5D757) /* 368 */, W64LIT(0xEB017892F38F61E8) /* 369 */, - W64LIT(0xD4EFFB9C9B1CC21A) /* 370 */, W64LIT(0x99727D26F494F7AB) /* 371 */, - W64LIT(0xA3E063A2956B3E03) /* 372 */, W64LIT(0x9D4A8B9A4AA09C30) /* 373 */, - W64LIT(0x3F6AB7D500090FB4) /* 374 */, W64LIT(0x9CC0F2A057268AC0) /* 375 */, - W64LIT(0x3DEE9D2DEDBF42D1) /* 376 */, W64LIT(0x330F49C87960A972) /* 377 */, - W64LIT(0xC6B2720287421B41) /* 378 */, W64LIT(0x0AC59EC07C00369C) /* 379 */, - W64LIT(0xEF4EAC49CB353425) /* 380 */, W64LIT(0xF450244EEF0129D8) /* 381 */, - W64LIT(0x8ACC46E5CAF4DEB6) /* 382 */, W64LIT(0x2FFEAB63989263F7) /* 383 */, - W64LIT(0x8F7CB9FE5D7A4578) /* 384 */, W64LIT(0x5BD8F7644E634635) /* 385 */, - W64LIT(0x427A7315BF2DC900) /* 386 */, W64LIT(0x17D0C4AA2125261C) /* 387 */, - W64LIT(0x3992486C93518E50) /* 388 */, W64LIT(0xB4CBFEE0A2D7D4C3) /* 389 */, - W64LIT(0x7C75D6202C5DDD8D) /* 390 */, W64LIT(0xDBC295D8E35B6C61) /* 391 */, - W64LIT(0x60B369D302032B19) /* 392 */, W64LIT(0xCE42685FDCE44132) /* 393 */, - W64LIT(0x06F3DDB9DDF65610) /* 394 */, W64LIT(0x8EA4D21DB5E148F0) /* 395 */, - W64LIT(0x20B0FCE62FCD496F) /* 396 */, W64LIT(0x2C1B912358B0EE31) /* 397 */, - W64LIT(0xB28317B818F5A308) /* 398 */, W64LIT(0xA89C1E189CA6D2CF) /* 399 */, - W64LIT(0x0C6B18576AAADBC8) /* 400 */, W64LIT(0xB65DEAA91299FAE3) /* 401 */, - W64LIT(0xFB2B794B7F1027E7) /* 402 */, W64LIT(0x04E4317F443B5BEB) /* 403 */, - W64LIT(0x4B852D325939D0A6) /* 404 */, W64LIT(0xD5AE6BEEFB207FFC) /* 405 */, - W64LIT(0x309682B281C7D374) /* 406 */, W64LIT(0xBAE309A194C3B475) /* 407 */, - W64LIT(0x8CC3F97B13B49F05) /* 408 */, W64LIT(0x98A9422FF8293967) /* 409 */, - W64LIT(0x244B16B01076FF7C) /* 410 */, W64LIT(0xF8BF571C663D67EE) /* 411 */, - W64LIT(0x1F0D6758EEE30DA1) /* 412 */, W64LIT(0xC9B611D97ADEB9B7) /* 413 */, - W64LIT(0xB7AFD5887B6C57A2) /* 414 */, W64LIT(0x6290AE846B984FE1) /* 415 */, - W64LIT(0x94DF4CDEACC1A5FD) /* 416 */, W64LIT(0x058A5BD1C5483AFF) /* 417 */, - W64LIT(0x63166CC142BA3C37) /* 418 */, W64LIT(0x8DB8526EB2F76F40) /* 419 */, - W64LIT(0xE10880036F0D6D4E) /* 420 */, W64LIT(0x9E0523C9971D311D) /* 421 */, - W64LIT(0x45EC2824CC7CD691) /* 422 */, W64LIT(0x575B8359E62382C9) /* 423 */, - W64LIT(0xFA9E400DC4889995) /* 424 */, W64LIT(0xD1823ECB45721568) /* 425 */, - W64LIT(0xDAFD983B8206082F) /* 426 */, W64LIT(0xAA7D29082386A8CB) /* 427 */, - W64LIT(0x269FCD4403B87588) /* 428 */, W64LIT(0x1B91F5F728BDD1E0) /* 429 */, - W64LIT(0xE4669F39040201F6) /* 430 */, W64LIT(0x7A1D7C218CF04ADE) /* 431 */, - W64LIT(0x65623C29D79CE5CE) /* 432 */, W64LIT(0x2368449096C00BB1) /* 433 */, - W64LIT(0xAB9BF1879DA503BA) /* 434 */, W64LIT(0xBC23ECB1A458058E) /* 435 */, - W64LIT(0x9A58DF01BB401ECC) /* 436 */, W64LIT(0xA070E868A85F143D) /* 437 */, - W64LIT(0x4FF188307DF2239E) /* 438 */, W64LIT(0x14D565B41A641183) /* 439 */, - W64LIT(0xEE13337452701602) /* 440 */, W64LIT(0x950E3DCF3F285E09) /* 441 */, - W64LIT(0x59930254B9C80953) /* 442 */, W64LIT(0x3BF299408930DA6D) /* 443 */, - W64LIT(0xA955943F53691387) /* 444 */, W64LIT(0xA15EDECAA9CB8784) /* 445 */, - W64LIT(0x29142127352BE9A0) /* 446 */, W64LIT(0x76F0371FFF4E7AFB) /* 447 */, - W64LIT(0x0239F450274F2228) /* 448 */, W64LIT(0xBB073AF01D5E868B) /* 449 */, - W64LIT(0xBFC80571C10E96C1) /* 450 */, W64LIT(0xD267088568222E23) /* 451 */, - W64LIT(0x9671A3D48E80B5B0) /* 452 */, W64LIT(0x55B5D38AE193BB81) /* 453 */, - W64LIT(0x693AE2D0A18B04B8) /* 454 */, W64LIT(0x5C48B4ECADD5335F) /* 455 */, - W64LIT(0xFD743B194916A1CA) /* 456 */, W64LIT(0x2577018134BE98C4) /* 457 */, - W64LIT(0xE77987E83C54A4AD) /* 458 */, W64LIT(0x28E11014DA33E1B9) /* 459 */, - W64LIT(0x270CC59E226AA213) /* 460 */, W64LIT(0x71495F756D1A5F60) /* 461 */, - W64LIT(0x9BE853FB60AFEF77) /* 462 */, W64LIT(0xADC786A7F7443DBF) /* 463 */, - W64LIT(0x0904456173B29A82) /* 464 */, W64LIT(0x58BC7A66C232BD5E) /* 465 */, - W64LIT(0xF306558C673AC8B2) /* 466 */, W64LIT(0x41F639C6B6C9772A) /* 467 */, - W64LIT(0x216DEFE99FDA35DA) /* 468 */, W64LIT(0x11640CC71C7BE615) /* 469 */, - W64LIT(0x93C43694565C5527) /* 470 */, W64LIT(0xEA038E6246777839) /* 471 */, - W64LIT(0xF9ABF3CE5A3E2469) /* 472 */, W64LIT(0x741E768D0FD312D2) /* 473 */, - W64LIT(0x0144B883CED652C6) /* 474 */, W64LIT(0xC20B5A5BA33F8552) /* 475 */, - W64LIT(0x1AE69633C3435A9D) /* 476 */, W64LIT(0x97A28CA4088CFDEC) /* 477 */, - W64LIT(0x8824A43C1E96F420) /* 478 */, W64LIT(0x37612FA66EEEA746) /* 479 */, - W64LIT(0x6B4CB165F9CF0E5A) /* 480 */, W64LIT(0x43AA1C06A0ABFB4A) /* 481 */, - W64LIT(0x7F4DC26FF162796B) /* 482 */, W64LIT(0x6CBACC8E54ED9B0F) /* 483 */, - W64LIT(0xA6B7FFEFD2BB253E) /* 484 */, W64LIT(0x2E25BC95B0A29D4F) /* 485 */, - W64LIT(0x86D6A58BDEF1388C) /* 486 */, W64LIT(0xDED74AC576B6F054) /* 487 */, - W64LIT(0x8030BDBC2B45805D) /* 488 */, W64LIT(0x3C81AF70E94D9289) /* 489 */, - W64LIT(0x3EFF6DDA9E3100DB) /* 490 */, W64LIT(0xB38DC39FDFCC8847) /* 491 */, - W64LIT(0x123885528D17B87E) /* 492 */, W64LIT(0xF2DA0ED240B1B642) /* 493 */, - W64LIT(0x44CEFADCD54BF9A9) /* 494 */, W64LIT(0x1312200E433C7EE6) /* 495 */, - W64LIT(0x9FFCC84F3A78C748) /* 496 */, W64LIT(0xF0CD1F72248576BB) /* 497 */, - W64LIT(0xEC6974053638CFE4) /* 498 */, W64LIT(0x2BA7B67C0CEC4E4C) /* 499 */, - W64LIT(0xAC2F4DF3E5CE32ED) /* 500 */, W64LIT(0xCB33D14326EA4C11) /* 501 */, - W64LIT(0xA4E9044CC77E58BC) /* 502 */, W64LIT(0x5F513293D934FCEF) /* 503 */, - W64LIT(0x5DC9645506E55444) /* 504 */, W64LIT(0x50DE418F317DE40A) /* 505 */, - W64LIT(0x388CB31A69DDE259) /* 506 */, W64LIT(0x2DB4A83455820A86) /* 507 */, - W64LIT(0x9010A91E84711AE9) /* 508 */, W64LIT(0x4DF7F0B7B1498371) /* 509 */, - W64LIT(0xD62A2EABC0977179) /* 510 */, W64LIT(0x22FAC097AA8D5C0E) /* 511 */, - W64LIT(0xF49FCC2FF1DAF39B) /* 512 */, W64LIT(0x487FD5C66FF29281) /* 513 */, - W64LIT(0xE8A30667FCDCA83F) /* 514 */, W64LIT(0x2C9B4BE3D2FCCE63) /* 515 */, - W64LIT(0xDA3FF74B93FBBBC2) /* 516 */, W64LIT(0x2FA165D2FE70BA66) /* 517 */, - W64LIT(0xA103E279970E93D4) /* 518 */, W64LIT(0xBECDEC77B0E45E71) /* 519 */, - W64LIT(0xCFB41E723985E497) /* 520 */, W64LIT(0xB70AAA025EF75017) /* 521 */, - W64LIT(0xD42309F03840B8E0) /* 522 */, W64LIT(0x8EFC1AD035898579) /* 523 */, - W64LIT(0x96C6920BE2B2ABC5) /* 524 */, W64LIT(0x66AF4163375A9172) /* 525 */, - W64LIT(0x2174ABDCCA7127FB) /* 526 */, W64LIT(0xB33CCEA64A72FF41) /* 527 */, - W64LIT(0xF04A4933083066A5) /* 528 */, W64LIT(0x8D970ACDD7289AF5) /* 529 */, - W64LIT(0x8F96E8E031C8C25E) /* 530 */, W64LIT(0xF3FEC02276875D47) /* 531 */, - W64LIT(0xEC7BF310056190DD) /* 532 */, W64LIT(0xF5ADB0AEBB0F1491) /* 533 */, - W64LIT(0x9B50F8850FD58892) /* 534 */, W64LIT(0x4975488358B74DE8) /* 535 */, - W64LIT(0xA3354FF691531C61) /* 536 */, W64LIT(0x0702BBE481D2C6EE) /* 537 */, - W64LIT(0x89FB24057DEDED98) /* 538 */, W64LIT(0xAC3075138596E902) /* 539 */, - W64LIT(0x1D2D3580172772ED) /* 540 */, W64LIT(0xEB738FC28E6BC30D) /* 541 */, - W64LIT(0x5854EF8F63044326) /* 542 */, W64LIT(0x9E5C52325ADD3BBE) /* 543 */, - W64LIT(0x90AA53CF325C4623) /* 544 */, W64LIT(0xC1D24D51349DD067) /* 545 */, - W64LIT(0x2051CFEEA69EA624) /* 546 */, W64LIT(0x13220F0A862E7E4F) /* 547 */, - W64LIT(0xCE39399404E04864) /* 548 */, W64LIT(0xD9C42CA47086FCB7) /* 549 */, - W64LIT(0x685AD2238A03E7CC) /* 550 */, W64LIT(0x066484B2AB2FF1DB) /* 551 */, - W64LIT(0xFE9D5D70EFBF79EC) /* 552 */, W64LIT(0x5B13B9DD9C481854) /* 553 */, - W64LIT(0x15F0D475ED1509AD) /* 554 */, W64LIT(0x0BEBCD060EC79851) /* 555 */, - W64LIT(0xD58C6791183AB7F8) /* 556 */, W64LIT(0xD1187C5052F3EEE4) /* 557 */, - W64LIT(0xC95D1192E54E82FF) /* 558 */, W64LIT(0x86EEA14CB9AC6CA2) /* 559 */, - W64LIT(0x3485BEB153677D5D) /* 560 */, W64LIT(0xDD191D781F8C492A) /* 561 */, - W64LIT(0xF60866BAA784EBF9) /* 562 */, W64LIT(0x518F643BA2D08C74) /* 563 */, - W64LIT(0x8852E956E1087C22) /* 564 */, W64LIT(0xA768CB8DC410AE8D) /* 565 */, - W64LIT(0x38047726BFEC8E1A) /* 566 */, W64LIT(0xA67738B4CD3B45AA) /* 567 */, - W64LIT(0xAD16691CEC0DDE19) /* 568 */, W64LIT(0xC6D4319380462E07) /* 569 */, - W64LIT(0xC5A5876D0BA61938) /* 570 */, W64LIT(0x16B9FA1FA58FD840) /* 571 */, - W64LIT(0x188AB1173CA74F18) /* 572 */, W64LIT(0xABDA2F98C99C021F) /* 573 */, - W64LIT(0x3E0580AB134AE816) /* 574 */, W64LIT(0x5F3B05B773645ABB) /* 575 */, - W64LIT(0x2501A2BE5575F2F6) /* 576 */, W64LIT(0x1B2F74004E7E8BA9) /* 577 */, - W64LIT(0x1CD7580371E8D953) /* 578 */, W64LIT(0x7F6ED89562764E30) /* 579 */, - W64LIT(0xB15926FF596F003D) /* 580 */, W64LIT(0x9F65293DA8C5D6B9) /* 581 */, - W64LIT(0x6ECEF04DD690F84C) /* 582 */, W64LIT(0x4782275FFF33AF88) /* 583 */, - W64LIT(0xE41433083F820801) /* 584 */, W64LIT(0xFD0DFE409A1AF9B5) /* 585 */, - W64LIT(0x4325A3342CDB396B) /* 586 */, W64LIT(0x8AE77E62B301B252) /* 587 */, - W64LIT(0xC36F9E9F6655615A) /* 588 */, W64LIT(0x85455A2D92D32C09) /* 589 */, - W64LIT(0xF2C7DEA949477485) /* 590 */, W64LIT(0x63CFB4C133A39EBA) /* 591 */, - W64LIT(0x83B040CC6EBC5462) /* 592 */, W64LIT(0x3B9454C8FDB326B0) /* 593 */, - W64LIT(0x56F56A9E87FFD78C) /* 594 */, W64LIT(0x2DC2940D99F42BC6) /* 595 */, - W64LIT(0x98F7DF096B096E2D) /* 596 */, W64LIT(0x19A6E01E3AD852BF) /* 597 */, - W64LIT(0x42A99CCBDBD4B40B) /* 598 */, W64LIT(0xA59998AF45E9C559) /* 599 */, - W64LIT(0x366295E807D93186) /* 600 */, W64LIT(0x6B48181BFAA1F773) /* 601 */, - W64LIT(0x1FEC57E2157A0A1D) /* 602 */, W64LIT(0x4667446AF6201AD5) /* 603 */, - W64LIT(0xE615EBCACFB0F075) /* 604 */, W64LIT(0xB8F31F4F68290778) /* 605 */, - W64LIT(0x22713ED6CE22D11E) /* 606 */, W64LIT(0x3057C1A72EC3C93B) /* 607 */, - W64LIT(0xCB46ACC37C3F1F2F) /* 608 */, W64LIT(0xDBB893FD02AAF50E) /* 609 */, - W64LIT(0x331FD92E600B9FCF) /* 610 */, W64LIT(0xA498F96148EA3AD6) /* 611 */, - W64LIT(0xA8D8426E8B6A83EA) /* 612 */, W64LIT(0xA089B274B7735CDC) /* 613 */, - W64LIT(0x87F6B3731E524A11) /* 614 */, W64LIT(0x118808E5CBC96749) /* 615 */, - W64LIT(0x9906E4C7B19BD394) /* 616 */, W64LIT(0xAFED7F7E9B24A20C) /* 617 */, - W64LIT(0x6509EADEEB3644A7) /* 618 */, W64LIT(0x6C1EF1D3E8EF0EDE) /* 619 */, - W64LIT(0xB9C97D43E9798FB4) /* 620 */, W64LIT(0xA2F2D784740C28A3) /* 621 */, - W64LIT(0x7B8496476197566F) /* 622 */, W64LIT(0x7A5BE3E6B65F069D) /* 623 */, - W64LIT(0xF96330ED78BE6F10) /* 624 */, W64LIT(0xEEE60DE77A076A15) /* 625 */, - W64LIT(0x2B4BEE4AA08B9BD0) /* 626 */, W64LIT(0x6A56A63EC7B8894E) /* 627 */, - W64LIT(0x02121359BA34FEF4) /* 628 */, W64LIT(0x4CBF99F8283703FC) /* 629 */, - W64LIT(0x398071350CAF30C8) /* 630 */, W64LIT(0xD0A77A89F017687A) /* 631 */, - W64LIT(0xF1C1A9EB9E423569) /* 632 */, W64LIT(0x8C7976282DEE8199) /* 633 */, - W64LIT(0x5D1737A5DD1F7ABD) /* 634 */, W64LIT(0x4F53433C09A9FA80) /* 635 */, - W64LIT(0xFA8B0C53DF7CA1D9) /* 636 */, W64LIT(0x3FD9DCBC886CCB77) /* 637 */, - W64LIT(0xC040917CA91B4720) /* 638 */, W64LIT(0x7DD00142F9D1DCDF) /* 639 */, - W64LIT(0x8476FC1D4F387B58) /* 640 */, W64LIT(0x23F8E7C5F3316503) /* 641 */, - W64LIT(0x032A2244E7E37339) /* 642 */, W64LIT(0x5C87A5D750F5A74B) /* 643 */, - W64LIT(0x082B4CC43698992E) /* 644 */, W64LIT(0xDF917BECB858F63C) /* 645 */, - W64LIT(0x3270B8FC5BF86DDA) /* 646 */, W64LIT(0x10AE72BB29B5DD76) /* 647 */, - W64LIT(0x576AC94E7700362B) /* 648 */, W64LIT(0x1AD112DAC61EFB8F) /* 649 */, - W64LIT(0x691BC30EC5FAA427) /* 650 */, W64LIT(0xFF246311CC327143) /* 651 */, - W64LIT(0x3142368E30E53206) /* 652 */, W64LIT(0x71380E31E02CA396) /* 653 */, - W64LIT(0x958D5C960AAD76F1) /* 654 */, W64LIT(0xF8D6F430C16DA536) /* 655 */, - W64LIT(0xC8FFD13F1BE7E1D2) /* 656 */, W64LIT(0x7578AE66004DDBE1) /* 657 */, - W64LIT(0x05833F01067BE646) /* 658 */, W64LIT(0xBB34B5AD3BFE586D) /* 659 */, - W64LIT(0x095F34C9A12B97F0) /* 660 */, W64LIT(0x247AB64525D60CA8) /* 661 */, - W64LIT(0xDCDBC6F3017477D1) /* 662 */, W64LIT(0x4A2E14D4DECAD24D) /* 663 */, - W64LIT(0xBDB5E6D9BE0A1EEB) /* 664 */, W64LIT(0x2A7E70F7794301AB) /* 665 */, - W64LIT(0xDEF42D8A270540FD) /* 666 */, W64LIT(0x01078EC0A34C22C1) /* 667 */, - W64LIT(0xE5DE511AF4C16387) /* 668 */, W64LIT(0x7EBB3A52BD9A330A) /* 669 */, - W64LIT(0x77697857AA7D6435) /* 670 */, W64LIT(0x004E831603AE4C32) /* 671 */, - W64LIT(0xE7A21020AD78E312) /* 672 */, W64LIT(0x9D41A70C6AB420F2) /* 673 */, - W64LIT(0x28E06C18EA1141E6) /* 674 */, W64LIT(0xD2B28CBD984F6B28) /* 675 */, - W64LIT(0x26B75F6C446E9D83) /* 676 */, W64LIT(0xBA47568C4D418D7F) /* 677 */, - W64LIT(0xD80BADBFE6183D8E) /* 678 */, W64LIT(0x0E206D7F5F166044) /* 679 */, - W64LIT(0xE258A43911CBCA3E) /* 680 */, W64LIT(0x723A1746B21DC0BC) /* 681 */, - W64LIT(0xC7CAA854F5D7CDD3) /* 682 */, W64LIT(0x7CAC32883D261D9C) /* 683 */, - W64LIT(0x7690C26423BA942C) /* 684 */, W64LIT(0x17E55524478042B8) /* 685 */, - W64LIT(0xE0BE477656A2389F) /* 686 */, W64LIT(0x4D289B5E67AB2DA0) /* 687 */, - W64LIT(0x44862B9C8FBBFD31) /* 688 */, W64LIT(0xB47CC8049D141365) /* 689 */, - W64LIT(0x822C1B362B91C793) /* 690 */, W64LIT(0x4EB14655FB13DFD8) /* 691 */, - W64LIT(0x1ECBBA0714E2A97B) /* 692 */, W64LIT(0x6143459D5CDE5F14) /* 693 */, - W64LIT(0x53A8FBF1D5F0AC89) /* 694 */, W64LIT(0x97EA04D81C5E5B00) /* 695 */, - W64LIT(0x622181A8D4FDB3F3) /* 696 */, W64LIT(0xE9BCD341572A1208) /* 697 */, - W64LIT(0x1411258643CCE58A) /* 698 */, W64LIT(0x9144C5FEA4C6E0A4) /* 699 */, - W64LIT(0x0D33D06565CF620F) /* 700 */, W64LIT(0x54A48D489F219CA1) /* 701 */, - W64LIT(0xC43E5EAC6D63C821) /* 702 */, W64LIT(0xA9728B3A72770DAF) /* 703 */, - W64LIT(0xD7934E7B20DF87EF) /* 704 */, W64LIT(0xE35503B61A3E86E5) /* 705 */, - W64LIT(0xCAE321FBC819D504) /* 706 */, W64LIT(0x129A50B3AC60BFA6) /* 707 */, - W64LIT(0xCD5E68EA7E9FB6C3) /* 708 */, W64LIT(0xB01C90199483B1C7) /* 709 */, - W64LIT(0x3DE93CD5C295376C) /* 710 */, W64LIT(0xAED52EDF2AB9AD13) /* 711 */, - W64LIT(0x2E60F512C0A07884) /* 712 */, W64LIT(0xBC3D86A3E36210C9) /* 713 */, - W64LIT(0x35269D9B163951CE) /* 714 */, W64LIT(0x0C7D6E2AD0CDB5FA) /* 715 */, - W64LIT(0x59E86297D87F5733) /* 716 */, W64LIT(0x298EF221898DB0E7) /* 717 */, - W64LIT(0x55000029D1A5AA7E) /* 718 */, W64LIT(0x8BC08AE1B5061B45) /* 719 */, - W64LIT(0xC2C31C2B6C92703A) /* 720 */, W64LIT(0x94CC596BAF25EF42) /* 721 */, - W64LIT(0x0A1D73DB22540456) /* 722 */, W64LIT(0x04B6A0F9D9C4179A) /* 723 */, - W64LIT(0xEFFDAFA2AE3D3C60) /* 724 */, W64LIT(0xF7C8075BB49496C4) /* 725 */, - W64LIT(0x9CC5C7141D1CD4E3) /* 726 */, W64LIT(0x78BD1638218E5534) /* 727 */, - W64LIT(0xB2F11568F850246A) /* 728 */, W64LIT(0xEDFABCFA9502BC29) /* 729 */, - W64LIT(0x796CE5F2DA23051B) /* 730 */, W64LIT(0xAAE128B0DC93537C) /* 731 */, - W64LIT(0x3A493DA0EE4B29AE) /* 732 */, W64LIT(0xB5DF6B2C416895D7) /* 733 */, - W64LIT(0xFCABBD25122D7F37) /* 734 */, W64LIT(0x70810B58105DC4B1) /* 735 */, - W64LIT(0xE10FDD37F7882A90) /* 736 */, W64LIT(0x524DCAB5518A3F5C) /* 737 */, - W64LIT(0x3C9E85878451255B) /* 738 */, W64LIT(0x4029828119BD34E2) /* 739 */, - W64LIT(0x74A05B6F5D3CECCB) /* 740 */, W64LIT(0xB610021542E13ECA) /* 741 */, - W64LIT(0x0FF979D12F59E2AC) /* 742 */, W64LIT(0x6037DA27E4F9CC50) /* 743 */, - W64LIT(0x5E92975A0DF1847D) /* 744 */, W64LIT(0xD66DE190D3E623FE) /* 745 */, - W64LIT(0x5032D6B87B568048) /* 746 */, W64LIT(0x9A36B7CE8235216E) /* 747 */, - W64LIT(0x80272A7A24F64B4A) /* 748 */, W64LIT(0x93EFED8B8C6916F7) /* 749 */, - W64LIT(0x37DDBFF44CCE1555) /* 750 */, W64LIT(0x4B95DB5D4B99BD25) /* 751 */, - W64LIT(0x92D3FDA169812FC0) /* 752 */, W64LIT(0xFB1A4A9A90660BB6) /* 753 */, - W64LIT(0x730C196946A4B9B2) /* 754 */, W64LIT(0x81E289AA7F49DA68) /* 755 */, - W64LIT(0x64669A0F83B1A05F) /* 756 */, W64LIT(0x27B3FF7D9644F48B) /* 757 */, - W64LIT(0xCC6B615C8DB675B3) /* 758 */, W64LIT(0x674F20B9BCEBBE95) /* 759 */, - W64LIT(0x6F31238275655982) /* 760 */, W64LIT(0x5AE488713E45CF05) /* 761 */, - W64LIT(0xBF619F9954C21157) /* 762 */, W64LIT(0xEABAC46040A8EAE9) /* 763 */, - W64LIT(0x454C6FE9F2C0C1CD) /* 764 */, W64LIT(0x419CF6496412691C) /* 765 */, - W64LIT(0xD3DC3BEF265B0F70) /* 766 */, W64LIT(0x6D0E60F5C3578A9E) /* 767 */, - W64LIT(0x5B0E608526323C55) /* 768 */, W64LIT(0x1A46C1A9FA1B59F5) /* 769 */, - W64LIT(0xA9E245A17C4C8FFA) /* 770 */, W64LIT(0x65CA5159DB2955D7) /* 771 */, - W64LIT(0x05DB0A76CE35AFC2) /* 772 */, W64LIT(0x81EAC77EA9113D45) /* 773 */, - W64LIT(0x528EF88AB6AC0A0D) /* 774 */, W64LIT(0xA09EA253597BE3FF) /* 775 */, - W64LIT(0x430DDFB3AC48CD56) /* 776 */, W64LIT(0xC4B3A67AF45CE46F) /* 777 */, - W64LIT(0x4ECECFD8FBE2D05E) /* 778 */, W64LIT(0x3EF56F10B39935F0) /* 779 */, - W64LIT(0x0B22D6829CD619C6) /* 780 */, W64LIT(0x17FD460A74DF2069) /* 781 */, - W64LIT(0x6CF8CC8E8510ED40) /* 782 */, W64LIT(0xD6C824BF3A6ECAA7) /* 783 */, - W64LIT(0x61243D581A817049) /* 784 */, W64LIT(0x048BACB6BBC163A2) /* 785 */, - W64LIT(0xD9A38AC27D44CC32) /* 786 */, W64LIT(0x7FDDFF5BAAF410AB) /* 787 */, - W64LIT(0xAD6D495AA804824B) /* 788 */, W64LIT(0xE1A6A74F2D8C9F94) /* 789 */, - W64LIT(0xD4F7851235DEE8E3) /* 790 */, W64LIT(0xFD4B7F886540D893) /* 791 */, - W64LIT(0x247C20042AA4BFDA) /* 792 */, W64LIT(0x096EA1C517D1327C) /* 793 */, - W64LIT(0xD56966B4361A6685) /* 794 */, W64LIT(0x277DA5C31221057D) /* 795 */, - W64LIT(0x94D59893A43ACFF7) /* 796 */, W64LIT(0x64F0C51CCDC02281) /* 797 */, - W64LIT(0x3D33BCC4FF6189DB) /* 798 */, W64LIT(0xE005CB184CE66AF1) /* 799 */, - W64LIT(0xFF5CCD1D1DB99BEA) /* 800 */, W64LIT(0xB0B854A7FE42980F) /* 801 */, - W64LIT(0x7BD46A6A718D4B9F) /* 802 */, W64LIT(0xD10FA8CC22A5FD8C) /* 803 */, - W64LIT(0xD31484952BE4BD31) /* 804 */, W64LIT(0xC7FA975FCB243847) /* 805 */, - W64LIT(0x4886ED1E5846C407) /* 806 */, W64LIT(0x28CDDB791EB70B04) /* 807 */, - W64LIT(0xC2B00BE2F573417F) /* 808 */, W64LIT(0x5C9590452180F877) /* 809 */, - W64LIT(0x7A6BDDFFF370EB00) /* 810 */, W64LIT(0xCE509E38D6D9D6A4) /* 811 */, - W64LIT(0xEBEB0F00647FA702) /* 812 */, W64LIT(0x1DCC06CF76606F06) /* 813 */, - W64LIT(0xE4D9F28BA286FF0A) /* 814 */, W64LIT(0xD85A305DC918C262) /* 815 */, - W64LIT(0x475B1D8732225F54) /* 816 */, W64LIT(0x2D4FB51668CCB5FE) /* 817 */, - W64LIT(0xA679B9D9D72BBA20) /* 818 */, W64LIT(0x53841C0D912D43A5) /* 819 */, - W64LIT(0x3B7EAA48BF12A4E8) /* 820 */, W64LIT(0x781E0E47F22F1DDF) /* 821 */, - W64LIT(0xEFF20CE60AB50973) /* 822 */, W64LIT(0x20D261D19DFFB742) /* 823 */, - W64LIT(0x16A12B03062A2E39) /* 824 */, W64LIT(0x1960EB2239650495) /* 825 */, - W64LIT(0x251C16FED50EB8B8) /* 826 */, W64LIT(0x9AC0C330F826016E) /* 827 */, - W64LIT(0xED152665953E7671) /* 828 */, W64LIT(0x02D63194A6369570) /* 829 */, - W64LIT(0x5074F08394B1C987) /* 830 */, W64LIT(0x70BA598C90B25CE1) /* 831 */, - W64LIT(0x794A15810B9742F6) /* 832 */, W64LIT(0x0D5925E9FCAF8C6C) /* 833 */, - W64LIT(0x3067716CD868744E) /* 834 */, W64LIT(0x910AB077E8D7731B) /* 835 */, - W64LIT(0x6A61BBDB5AC42F61) /* 836 */, W64LIT(0x93513EFBF0851567) /* 837 */, - W64LIT(0xF494724B9E83E9D5) /* 838 */, W64LIT(0xE887E1985C09648D) /* 839 */, - W64LIT(0x34B1D3C675370CFD) /* 840 */, W64LIT(0xDC35E433BC0D255D) /* 841 */, - W64LIT(0xD0AAB84234131BE0) /* 842 */, W64LIT(0x08042A50B48B7EAF) /* 843 */, - W64LIT(0x9997C4EE44A3AB35) /* 844 */, W64LIT(0x829A7B49201799D0) /* 845 */, - W64LIT(0x263B8307B7C54441) /* 846 */, W64LIT(0x752F95F4FD6A6CA6) /* 847 */, - W64LIT(0x927217402C08C6E5) /* 848 */, W64LIT(0x2A8AB754A795D9EE) /* 849 */, - W64LIT(0xA442F7552F72943D) /* 850 */, W64LIT(0x2C31334E19781208) /* 851 */, - W64LIT(0x4FA98D7CEAEE6291) /* 852 */, W64LIT(0x55C3862F665DB309) /* 853 */, - W64LIT(0xBD0610175D53B1F3) /* 854 */, W64LIT(0x46FE6CB840413F27) /* 855 */, - W64LIT(0x3FE03792DF0CFA59) /* 856 */, W64LIT(0xCFE700372EB85E8F) /* 857 */, - W64LIT(0xA7BE29E7ADBCE118) /* 858 */, W64LIT(0xE544EE5CDE8431DD) /* 859 */, - W64LIT(0x8A781B1B41F1873E) /* 860 */, W64LIT(0xA5C94C78A0D2F0E7) /* 861 */, - W64LIT(0x39412E2877B60728) /* 862 */, W64LIT(0xA1265EF3AFC9A62C) /* 863 */, - W64LIT(0xBCC2770C6A2506C5) /* 864 */, W64LIT(0x3AB66DD5DCE1CE12) /* 865 */, - W64LIT(0xE65499D04A675B37) /* 866 */, W64LIT(0x7D8F523481BFD216) /* 867 */, - W64LIT(0x0F6F64FCEC15F389) /* 868 */, W64LIT(0x74EFBE618B5B13C8) /* 869 */, - W64LIT(0xACDC82B714273E1D) /* 870 */, W64LIT(0xDD40BFE003199D17) /* 871 */, - W64LIT(0x37E99257E7E061F8) /* 872 */, W64LIT(0xFA52626904775AAA) /* 873 */, - W64LIT(0x8BBBF63A463D56F9) /* 874 */, W64LIT(0xF0013F1543A26E64) /* 875 */, - W64LIT(0xA8307E9F879EC898) /* 876 */, W64LIT(0xCC4C27A4150177CC) /* 877 */, - W64LIT(0x1B432F2CCA1D3348) /* 878 */, W64LIT(0xDE1D1F8F9F6FA013) /* 879 */, - W64LIT(0x606602A047A7DDD6) /* 880 */, W64LIT(0xD237AB64CC1CB2C7) /* 881 */, - W64LIT(0x9B938E7225FCD1D3) /* 882 */, W64LIT(0xEC4E03708E0FF476) /* 883 */, - W64LIT(0xFEB2FBDA3D03C12D) /* 884 */, W64LIT(0xAE0BCED2EE43889A) /* 885 */, - W64LIT(0x22CB8923EBFB4F43) /* 886 */, W64LIT(0x69360D013CF7396D) /* 887 */, - W64LIT(0x855E3602D2D4E022) /* 888 */, W64LIT(0x073805BAD01F784C) /* 889 */, - W64LIT(0x33E17A133852F546) /* 890 */, W64LIT(0xDF4874058AC7B638) /* 891 */, - W64LIT(0xBA92B29C678AA14A) /* 892 */, W64LIT(0x0CE89FC76CFAADCD) /* 893 */, - W64LIT(0x5F9D4E0908339E34) /* 894 */, W64LIT(0xF1AFE9291F5923B9) /* 895 */, - W64LIT(0x6E3480F60F4A265F) /* 896 */, W64LIT(0xEEBF3A2AB29B841C) /* 897 */, - W64LIT(0xE21938A88F91B4AD) /* 898 */, W64LIT(0x57DFEFF845C6D3C3) /* 899 */, - W64LIT(0x2F006B0BF62CAAF2) /* 900 */, W64LIT(0x62F479EF6F75EE78) /* 901 */, - W64LIT(0x11A55AD41C8916A9) /* 902 */, W64LIT(0xF229D29084FED453) /* 903 */, - W64LIT(0x42F1C27B16B000E6) /* 904 */, W64LIT(0x2B1F76749823C074) /* 905 */, - W64LIT(0x4B76ECA3C2745360) /* 906 */, W64LIT(0x8C98F463B91691BD) /* 907 */, - W64LIT(0x14BCC93CF1ADE66A) /* 908 */, W64LIT(0x8885213E6D458397) /* 909 */, - W64LIT(0x8E177DF0274D4711) /* 910 */, W64LIT(0xB49B73B5503F2951) /* 911 */, - W64LIT(0x10168168C3F96B6B) /* 912 */, W64LIT(0x0E3D963B63CAB0AE) /* 913 */, - W64LIT(0x8DFC4B5655A1DB14) /* 914 */, W64LIT(0xF789F1356E14DE5C) /* 915 */, - W64LIT(0x683E68AF4E51DAC1) /* 916 */, W64LIT(0xC9A84F9D8D4B0FD9) /* 917 */, - W64LIT(0x3691E03F52A0F9D1) /* 918 */, W64LIT(0x5ED86E46E1878E80) /* 919 */, - W64LIT(0x3C711A0E99D07150) /* 920 */, W64LIT(0x5A0865B20C4E9310) /* 921 */, - W64LIT(0x56FBFC1FE4F0682E) /* 922 */, W64LIT(0xEA8D5DE3105EDF9B) /* 923 */, - W64LIT(0x71ABFDB12379187A) /* 924 */, W64LIT(0x2EB99DE1BEE77B9C) /* 925 */, - W64LIT(0x21ECC0EA33CF4523) /* 926 */, W64LIT(0x59A4D7521805C7A1) /* 927 */, - W64LIT(0x3896F5EB56AE7C72) /* 928 */, W64LIT(0xAA638F3DB18F75DC) /* 929 */, - W64LIT(0x9F39358DABE9808E) /* 930 */, W64LIT(0xB7DEFA91C00B72AC) /* 931 */, - W64LIT(0x6B5541FD62492D92) /* 932 */, W64LIT(0x6DC6DEE8F92E4D5B) /* 933 */, - W64LIT(0x353F57ABC4BEEA7E) /* 934 */, W64LIT(0x735769D6DA5690CE) /* 935 */, - W64LIT(0x0A234AA642391484) /* 936 */, W64LIT(0xF6F9508028F80D9D) /* 937 */, - W64LIT(0xB8E319A27AB3F215) /* 938 */, W64LIT(0x31AD9C1151341A4D) /* 939 */, - W64LIT(0x773C22A57BEF5805) /* 940 */, W64LIT(0x45C7561A07968633) /* 941 */, - W64LIT(0xF913DA9E249DBE36) /* 942 */, W64LIT(0xDA652D9B78A64C68) /* 943 */, - W64LIT(0x4C27A97F3BC334EF) /* 944 */, W64LIT(0x76621220E66B17F4) /* 945 */, - W64LIT(0x967743899ACD7D0B) /* 946 */, W64LIT(0xF3EE5BCAE0ED6782) /* 947 */, - W64LIT(0x409F753600C879FC) /* 948 */, W64LIT(0x06D09A39B5926DB6) /* 949 */, - W64LIT(0x6F83AEB0317AC588) /* 950 */, W64LIT(0x01E6CA4A86381F21) /* 951 */, - W64LIT(0x66FF3462D19F3025) /* 952 */, W64LIT(0x72207C24DDFD3BFB) /* 953 */, - W64LIT(0x4AF6B6D3E2ECE2EB) /* 954 */, W64LIT(0x9C994DBEC7EA08DE) /* 955 */, - W64LIT(0x49ACE597B09A8BC4) /* 956 */, W64LIT(0xB38C4766CF0797BA) /* 957 */, - W64LIT(0x131B9373C57C2A75) /* 958 */, W64LIT(0xB1822CCE61931E58) /* 959 */, - W64LIT(0x9D7555B909BA1C0C) /* 960 */, W64LIT(0x127FAFDD937D11D2) /* 961 */, - W64LIT(0x29DA3BADC66D92E4) /* 962 */, W64LIT(0xA2C1D57154C2ECBC) /* 963 */, - W64LIT(0x58C5134D82F6FE24) /* 964 */, W64LIT(0x1C3AE3515B62274F) /* 965 */, - W64LIT(0xE907C82E01CB8126) /* 966 */, W64LIT(0xF8ED091913E37FCB) /* 967 */, - W64LIT(0x3249D8F9C80046C9) /* 968 */, W64LIT(0x80CF9BEDE388FB63) /* 969 */, - W64LIT(0x1881539A116CF19E) /* 970 */, W64LIT(0x5103F3F76BD52457) /* 971 */, - W64LIT(0x15B7E6F5AE47F7A8) /* 972 */, W64LIT(0xDBD7C6DED47E9CCF) /* 973 */, - W64LIT(0x44E55C410228BB1A) /* 974 */, W64LIT(0xB647D4255EDB4E99) /* 975 */, - W64LIT(0x5D11882BB8AAFC30) /* 976 */, W64LIT(0xF5098BBB29D3212A) /* 977 */, - W64LIT(0x8FB5EA14E90296B3) /* 978 */, W64LIT(0x677B942157DD025A) /* 979 */, - W64LIT(0xFB58E7C0A390ACB5) /* 980 */, W64LIT(0x89D3674C83BD4A01) /* 981 */, - W64LIT(0x9E2DA4DF4BF3B93B) /* 982 */, W64LIT(0xFCC41E328CAB4829) /* 983 */, - W64LIT(0x03F38C96BA582C52) /* 984 */, W64LIT(0xCAD1BDBD7FD85DB2) /* 985 */, - W64LIT(0xBBB442C16082AE83) /* 986 */, W64LIT(0xB95FE86BA5DA9AB0) /* 987 */, - W64LIT(0xB22E04673771A93F) /* 988 */, W64LIT(0x845358C9493152D8) /* 989 */, - W64LIT(0xBE2A488697B4541E) /* 990 */, W64LIT(0x95A2DC2DD38E6966) /* 991 */, - W64LIT(0xC02C11AC923C852B) /* 992 */, W64LIT(0x2388B1990DF2A87B) /* 993 */, - W64LIT(0x7C8008FA1B4F37BE) /* 994 */, W64LIT(0x1F70D0C84D54E503) /* 995 */, - W64LIT(0x5490ADEC7ECE57D4) /* 996 */, W64LIT(0x002B3C27D9063A3A) /* 997 */, - W64LIT(0x7EAEA3848030A2BF) /* 998 */, W64LIT(0xC602326DED2003C0) /* 999 */, - W64LIT(0x83A7287D69A94086) /* 1000 */, W64LIT(0xC57A5FCB30F57A8A) /* 1001 */, - W64LIT(0xB56844E479EBE779) /* 1002 */, W64LIT(0xA373B40F05DCBCE9) /* 1003 */, - W64LIT(0xD71A786E88570EE2) /* 1004 */, W64LIT(0x879CBACDBDE8F6A0) /* 1005 */, - W64LIT(0x976AD1BCC164A32F) /* 1006 */, W64LIT(0xAB21E25E9666D78B) /* 1007 */, - W64LIT(0x901063AAE5E5C33C) /* 1008 */, W64LIT(0x9818B34448698D90) /* 1009 */, - W64LIT(0xE36487AE3E1E8ABB) /* 1010 */, W64LIT(0xAFBDF931893BDCB4) /* 1011 */, - W64LIT(0x6345A0DC5FBBD519) /* 1012 */, W64LIT(0x8628FE269B9465CA) /* 1013 */, - W64LIT(0x1E5D01603F9C51EC) /* 1014 */, W64LIT(0x4DE44006A15049B7) /* 1015 */, - W64LIT(0xBF6C70E5F776CBB1) /* 1016 */, W64LIT(0x411218F2EF552BED) /* 1017 */, - W64LIT(0xCB0C0708705A36A3) /* 1018 */, W64LIT(0xE74D14754F986044) /* 1019 */, - W64LIT(0xCD56D9430EA8280E) /* 1020 */, W64LIT(0xC12591D7535F5065) /* 1021 */, - W64LIT(0xC83223F1720AEF96) /* 1022 */, W64LIT(0xC3A0396F7363A51F) /* 1023 */, - W64LIT(0xffffffffffffffff), - W64LIT(0xA5A5A5A5A5A5A5A5), - W64LIT(0x0123456789ABCDEF), -}; - -NAMESPACE_END diff --git a/CryptoPP/trdlocal.cpp b/CryptoPP/trdlocal.cpp deleted file mode 100644 index 6d6b822c0..000000000 --- a/CryptoPP/trdlocal.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// trdlocal.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" - -#ifndef CRYPTOPP_IMPORTS -#ifdef THREADS_AVAILABLE - -#include "trdlocal.h" - -#ifdef HAS_WINTHREADS -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -ThreadLocalStorage::Err::Err(const std::string& operation, int error) - : OS_Error(OTHER_ERROR, "ThreadLocalStorage: " + operation + " operation failed with error 0x" + IntToString(error, 16), operation, error) -{ -} - -ThreadLocalStorage::ThreadLocalStorage() -{ -#ifdef HAS_WINTHREADS - m_index = TlsAlloc(); - if (m_index == TLS_OUT_OF_INDEXES) - throw Err("TlsAlloc", GetLastError()); -#else - int error = pthread_key_create(&m_index, NULL); - if (error) - throw Err("pthread_key_create", error); -#endif -} - -ThreadLocalStorage::~ThreadLocalStorage() -{ -#ifdef HAS_WINTHREADS - if (!TlsFree(m_index)) - throw Err("TlsFree", GetLastError()); -#else - int error = pthread_key_delete(m_index); - if (error) - throw Err("pthread_key_delete", error); -#endif -} - -void ThreadLocalStorage::SetValue(void *value) -{ -#ifdef HAS_WINTHREADS - if (!TlsSetValue(m_index, value)) - throw Err("TlsSetValue", GetLastError()); -#else - int error = pthread_setspecific(m_index, value); - if (error) - throw Err("pthread_key_getspecific", error); -#endif -} - -void *ThreadLocalStorage::GetValue() const -{ -#ifdef HAS_WINTHREADS - void *result = TlsGetValue(m_index); - if (!result && GetLastError() != NO_ERROR) - throw Err("TlsGetValue", GetLastError()); -#else - void *result = pthread_getspecific(m_index); -#endif - return result; -} - -NAMESPACE_END - -#endif // #ifdef THREADS_AVAILABLE -#endif diff --git a/CryptoPP/trdlocal.h b/CryptoPP/trdlocal.h deleted file mode 100644 index 92d244a0a..000000000 --- a/CryptoPP/trdlocal.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef CRYPTOPP_TRDLOCAL_H -#define CRYPTOPP_TRDLOCAL_H - -#include "config.h" - -#ifdef THREADS_AVAILABLE - -#include "misc.h" - -#ifdef HAS_WINTHREADS -typedef unsigned long ThreadLocalIndexType; -#else -#include -typedef pthread_key_t ThreadLocalIndexType; -#endif - -NAMESPACE_BEGIN(CryptoPP) - -//! thread local storage -class CRYPTOPP_DLL ThreadLocalStorage : public NotCopyable -{ -public: - //! exception thrown by ThreadLocalStorage class - class Err : public OS_Error - { - public: - Err(const std::string& operation, int error); - }; - - ThreadLocalStorage(); - ~ThreadLocalStorage(); - - void SetValue(void *value); - void *GetValue() const; - -private: - ThreadLocalIndexType m_index; -}; - -NAMESPACE_END - -#endif // #ifdef THREADS_AVAILABLE - -#endif diff --git a/CryptoPP/trunhash.h b/CryptoPP/trunhash.h deleted file mode 100644 index c1c4e9b64..000000000 --- a/CryptoPP/trunhash.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef CRYPTOPP_TRUNHASH_H -#define CRYPTOPP_TRUNHASH_H - -#include "cryptlib.h" - -NAMESPACE_BEGIN(CryptoPP) - -class NullHash : public HashTransformation -{ -public: - void Update(const byte *input, size_t length) {} - unsigned int DigestSize() const {return 0;} - void TruncatedFinal(byte *digest, size_t digestSize) {} - bool TruncatedVerify(const byte *digest, size_t digestLength) {return true;} -}; - -//! construct new HashModule with smaller DigestSize() from existing one -template -class TruncatedHashTemplate : public HashTransformation -{ -public: - TruncatedHashTemplate(T hm, unsigned int digestSize) - : m_hm(hm), m_digestSize(digestSize) {} - TruncatedHashTemplate(const byte *key, size_t keyLength, unsigned int digestSize) - : m_hm(key, keyLength), m_digestSize(digestSize) {} - TruncatedHashTemplate(size_t digestSize) - : m_digestSize(digestSize) {} - - void Restart() - {m_hm.Restart();} - void Update(const byte *input, size_t length) - {m_hm.Update(input, length);} - unsigned int DigestSize() const {return m_digestSize;} - void TruncatedFinal(byte *digest, size_t digestSize) - {m_hm.TruncatedFinal(digest, digestSize);} - bool TruncatedVerify(const byte *digest, size_t digestLength) - {return m_hm.TruncatedVerify(digest, digestLength);} - -private: - T m_hm; - unsigned int m_digestSize; -}; - -typedef TruncatedHashTemplate TruncatedHashModule; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/ttmac.cpp b/CryptoPP/ttmac.cpp deleted file mode 100644 index d4ff38104..000000000 --- a/CryptoPP/ttmac.cpp +++ /dev/null @@ -1,338 +0,0 @@ -// ttmac.cpp - written and placed in the public domain by Kevin Springle - -#include "pch.h" -#include "ttmac.h" -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -void TTMAC_Base::UncheckedSetKey(const byte *userKey, unsigned int keylength, const NameValuePairs &) -{ - AssertValidKeyLength(keylength); - - memcpy(m_key, userKey, KEYLENGTH); - CorrectEndianess(m_key, m_key, KEYLENGTH); - - Init(); -} - -void TTMAC_Base::Init() -{ - m_digest[0] = m_digest[5] = m_key[0]; - m_digest[1] = m_digest[6] = m_key[1]; - m_digest[2] = m_digest[7] = m_key[2]; - m_digest[3] = m_digest[8] = m_key[3]; - m_digest[4] = m_digest[9] = m_key[4]; -} - -void TTMAC_Base::TruncatedFinal(byte *hash, size_t size) -{ - PadLastBlock(BlockSize() - 2*sizeof(HashWordType)); - CorrectEndianess(m_data, m_data, BlockSize() - 2*sizeof(HashWordType)); - - m_data[m_data.size()-2] = GetBitCountLo(); - m_data[m_data.size()-1] = GetBitCountHi(); - - Transform(m_digest, m_data, true); - - word32 t2 = m_digest[2]; - word32 t3 = m_digest[3]; - if (size != DIGESTSIZE) - { - switch (size) - { - case 16: - m_digest[3] += m_digest[1] + m_digest[4]; - - case 12: - m_digest[2] += m_digest[0] + t3; - - case 8: - m_digest[0] += m_digest[1] + t3; - m_digest[1] += m_digest[4] + t2; - break; - - case 4: - m_digest[0] += - m_digest[1] + - m_digest[2] + - m_digest[3] + - m_digest[4]; - break; - - case 0: - // Used by HashTransformation::Restart() - break; - - default: - throw InvalidArgument("TTMAC_Base: can't truncate a Two-Track-MAC 20 byte digest to " + IntToString(size) + " bytes"); - break; - } - } - - CorrectEndianess(m_digest, m_digest, size); - memcpy(hash, m_digest, size); - - Restart(); // reinit for next use -} - -// RIPEMD-160 definitions used by Two-Track-MAC - -#define F(x, y, z) (x ^ y ^ z) -#define G(x, y, z) (z ^ (x & (y^z))) -#define H(x, y, z) (z ^ (x | ~y)) -#define I(x, y, z) (y ^ (z & (x^y))) -#define J(x, y, z) (x ^ (y | ~z)) - -#define k0 0 -#define k1 0x5a827999UL -#define k2 0x6ed9eba1UL -#define k3 0x8f1bbcdcUL -#define k4 0xa953fd4eUL -#define k5 0x50a28be6UL -#define k6 0x5c4dd124UL -#define k7 0x6d703ef3UL -#define k8 0x7a6d76e9UL -#define k9 0 - -void TTMAC_Base::Transform(word32 *digest, const word32 *X, bool last) -{ -#define Subround(f, a, b, c, d, e, x, s, k) \ - a += f(b, c, d) + x + k;\ - a = rotlFixed((word32)a, s) + e;\ - c = rotlFixed((word32)c, 10U) - - word32 a1, b1, c1, d1, e1, a2, b2, c2, d2, e2; - word32 *trackA, *trackB; - - if (!last) - { - trackA = digest; - trackB = digest+5; - } - else - { - trackB = digest; - trackA = digest+5; - } - a1 = trackA[0]; - b1 = trackA[1]; - c1 = trackA[2]; - d1 = trackA[3]; - e1 = trackA[4]; - a2 = trackB[0]; - b2 = trackB[1]; - c2 = trackB[2]; - d2 = trackB[3]; - e2 = trackB[4]; - - Subround(F, a1, b1, c1, d1, e1, X[ 0], 11, k0); - Subround(F, e1, a1, b1, c1, d1, X[ 1], 14, k0); - Subround(F, d1, e1, a1, b1, c1, X[ 2], 15, k0); - Subround(F, c1, d1, e1, a1, b1, X[ 3], 12, k0); - Subround(F, b1, c1, d1, e1, a1, X[ 4], 5, k0); - Subround(F, a1, b1, c1, d1, e1, X[ 5], 8, k0); - Subround(F, e1, a1, b1, c1, d1, X[ 6], 7, k0); - Subround(F, d1, e1, a1, b1, c1, X[ 7], 9, k0); - Subround(F, c1, d1, e1, a1, b1, X[ 8], 11, k0); - Subround(F, b1, c1, d1, e1, a1, X[ 9], 13, k0); - Subround(F, a1, b1, c1, d1, e1, X[10], 14, k0); - Subround(F, e1, a1, b1, c1, d1, X[11], 15, k0); - Subround(F, d1, e1, a1, b1, c1, X[12], 6, k0); - Subround(F, c1, d1, e1, a1, b1, X[13], 7, k0); - Subround(F, b1, c1, d1, e1, a1, X[14], 9, k0); - Subround(F, a1, b1, c1, d1, e1, X[15], 8, k0); - - Subround(G, e1, a1, b1, c1, d1, X[ 7], 7, k1); - Subround(G, d1, e1, a1, b1, c1, X[ 4], 6, k1); - Subround(G, c1, d1, e1, a1, b1, X[13], 8, k1); - Subround(G, b1, c1, d1, e1, a1, X[ 1], 13, k1); - Subround(G, a1, b1, c1, d1, e1, X[10], 11, k1); - Subround(G, e1, a1, b1, c1, d1, X[ 6], 9, k1); - Subround(G, d1, e1, a1, b1, c1, X[15], 7, k1); - Subround(G, c1, d1, e1, a1, b1, X[ 3], 15, k1); - Subround(G, b1, c1, d1, e1, a1, X[12], 7, k1); - Subround(G, a1, b1, c1, d1, e1, X[ 0], 12, k1); - Subround(G, e1, a1, b1, c1, d1, X[ 9], 15, k1); - Subround(G, d1, e1, a1, b1, c1, X[ 5], 9, k1); - Subround(G, c1, d1, e1, a1, b1, X[ 2], 11, k1); - Subround(G, b1, c1, d1, e1, a1, X[14], 7, k1); - Subround(G, a1, b1, c1, d1, e1, X[11], 13, k1); - Subround(G, e1, a1, b1, c1, d1, X[ 8], 12, k1); - - Subround(H, d1, e1, a1, b1, c1, X[ 3], 11, k2); - Subround(H, c1, d1, e1, a1, b1, X[10], 13, k2); - Subround(H, b1, c1, d1, e1, a1, X[14], 6, k2); - Subround(H, a1, b1, c1, d1, e1, X[ 4], 7, k2); - Subround(H, e1, a1, b1, c1, d1, X[ 9], 14, k2); - Subround(H, d1, e1, a1, b1, c1, X[15], 9, k2); - Subround(H, c1, d1, e1, a1, b1, X[ 8], 13, k2); - Subround(H, b1, c1, d1, e1, a1, X[ 1], 15, k2); - Subround(H, a1, b1, c1, d1, e1, X[ 2], 14, k2); - Subround(H, e1, a1, b1, c1, d1, X[ 7], 8, k2); - Subround(H, d1, e1, a1, b1, c1, X[ 0], 13, k2); - Subround(H, c1, d1, e1, a1, b1, X[ 6], 6, k2); - Subround(H, b1, c1, d1, e1, a1, X[13], 5, k2); - Subround(H, a1, b1, c1, d1, e1, X[11], 12, k2); - Subround(H, e1, a1, b1, c1, d1, X[ 5], 7, k2); - Subround(H, d1, e1, a1, b1, c1, X[12], 5, k2); - - Subround(I, c1, d1, e1, a1, b1, X[ 1], 11, k3); - Subround(I, b1, c1, d1, e1, a1, X[ 9], 12, k3); - Subround(I, a1, b1, c1, d1, e1, X[11], 14, k3); - Subround(I, e1, a1, b1, c1, d1, X[10], 15, k3); - Subround(I, d1, e1, a1, b1, c1, X[ 0], 14, k3); - Subround(I, c1, d1, e1, a1, b1, X[ 8], 15, k3); - Subround(I, b1, c1, d1, e1, a1, X[12], 9, k3); - Subround(I, a1, b1, c1, d1, e1, X[ 4], 8, k3); - Subround(I, e1, a1, b1, c1, d1, X[13], 9, k3); - Subround(I, d1, e1, a1, b1, c1, X[ 3], 14, k3); - Subround(I, c1, d1, e1, a1, b1, X[ 7], 5, k3); - Subround(I, b1, c1, d1, e1, a1, X[15], 6, k3); - Subround(I, a1, b1, c1, d1, e1, X[14], 8, k3); - Subround(I, e1, a1, b1, c1, d1, X[ 5], 6, k3); - Subround(I, d1, e1, a1, b1, c1, X[ 6], 5, k3); - Subround(I, c1, d1, e1, a1, b1, X[ 2], 12, k3); - - Subround(J, b1, c1, d1, e1, a1, X[ 4], 9, k4); - Subround(J, a1, b1, c1, d1, e1, X[ 0], 15, k4); - Subround(J, e1, a1, b1, c1, d1, X[ 5], 5, k4); - Subround(J, d1, e1, a1, b1, c1, X[ 9], 11, k4); - Subround(J, c1, d1, e1, a1, b1, X[ 7], 6, k4); - Subround(J, b1, c1, d1, e1, a1, X[12], 8, k4); - Subround(J, a1, b1, c1, d1, e1, X[ 2], 13, k4); - Subround(J, e1, a1, b1, c1, d1, X[10], 12, k4); - Subround(J, d1, e1, a1, b1, c1, X[14], 5, k4); - Subround(J, c1, d1, e1, a1, b1, X[ 1], 12, k4); - Subround(J, b1, c1, d1, e1, a1, X[ 3], 13, k4); - Subround(J, a1, b1, c1, d1, e1, X[ 8], 14, k4); - Subround(J, e1, a1, b1, c1, d1, X[11], 11, k4); - Subround(J, d1, e1, a1, b1, c1, X[ 6], 8, k4); - Subround(J, c1, d1, e1, a1, b1, X[15], 5, k4); - Subround(J, b1, c1, d1, e1, a1, X[13], 6, k4); - - Subround(J, a2, b2, c2, d2, e2, X[ 5], 8, k5); - Subround(J, e2, a2, b2, c2, d2, X[14], 9, k5); - Subround(J, d2, e2, a2, b2, c2, X[ 7], 9, k5); - Subround(J, c2, d2, e2, a2, b2, X[ 0], 11, k5); - Subround(J, b2, c2, d2, e2, a2, X[ 9], 13, k5); - Subround(J, a2, b2, c2, d2, e2, X[ 2], 15, k5); - Subround(J, e2, a2, b2, c2, d2, X[11], 15, k5); - Subround(J, d2, e2, a2, b2, c2, X[ 4], 5, k5); - Subround(J, c2, d2, e2, a2, b2, X[13], 7, k5); - Subround(J, b2, c2, d2, e2, a2, X[ 6], 7, k5); - Subround(J, a2, b2, c2, d2, e2, X[15], 8, k5); - Subround(J, e2, a2, b2, c2, d2, X[ 8], 11, k5); - Subround(J, d2, e2, a2, b2, c2, X[ 1], 14, k5); - Subround(J, c2, d2, e2, a2, b2, X[10], 14, k5); - Subround(J, b2, c2, d2, e2, a2, X[ 3], 12, k5); - Subround(J, a2, b2, c2, d2, e2, X[12], 6, k5); - - Subround(I, e2, a2, b2, c2, d2, X[ 6], 9, k6); - Subround(I, d2, e2, a2, b2, c2, X[11], 13, k6); - Subround(I, c2, d2, e2, a2, b2, X[ 3], 15, k6); - Subround(I, b2, c2, d2, e2, a2, X[ 7], 7, k6); - Subround(I, a2, b2, c2, d2, e2, X[ 0], 12, k6); - Subround(I, e2, a2, b2, c2, d2, X[13], 8, k6); - Subround(I, d2, e2, a2, b2, c2, X[ 5], 9, k6); - Subround(I, c2, d2, e2, a2, b2, X[10], 11, k6); - Subround(I, b2, c2, d2, e2, a2, X[14], 7, k6); - Subround(I, a2, b2, c2, d2, e2, X[15], 7, k6); - Subround(I, e2, a2, b2, c2, d2, X[ 8], 12, k6); - Subround(I, d2, e2, a2, b2, c2, X[12], 7, k6); - Subround(I, c2, d2, e2, a2, b2, X[ 4], 6, k6); - Subround(I, b2, c2, d2, e2, a2, X[ 9], 15, k6); - Subround(I, a2, b2, c2, d2, e2, X[ 1], 13, k6); - Subround(I, e2, a2, b2, c2, d2, X[ 2], 11, k6); - - Subround(H, d2, e2, a2, b2, c2, X[15], 9, k7); - Subround(H, c2, d2, e2, a2, b2, X[ 5], 7, k7); - Subround(H, b2, c2, d2, e2, a2, X[ 1], 15, k7); - Subround(H, a2, b2, c2, d2, e2, X[ 3], 11, k7); - Subround(H, e2, a2, b2, c2, d2, X[ 7], 8, k7); - Subround(H, d2, e2, a2, b2, c2, X[14], 6, k7); - Subround(H, c2, d2, e2, a2, b2, X[ 6], 6, k7); - Subround(H, b2, c2, d2, e2, a2, X[ 9], 14, k7); - Subround(H, a2, b2, c2, d2, e2, X[11], 12, k7); - Subround(H, e2, a2, b2, c2, d2, X[ 8], 13, k7); - Subround(H, d2, e2, a2, b2, c2, X[12], 5, k7); - Subround(H, c2, d2, e2, a2, b2, X[ 2], 14, k7); - Subround(H, b2, c2, d2, e2, a2, X[10], 13, k7); - Subround(H, a2, b2, c2, d2, e2, X[ 0], 13, k7); - Subround(H, e2, a2, b2, c2, d2, X[ 4], 7, k7); - Subround(H, d2, e2, a2, b2, c2, X[13], 5, k7); - - Subround(G, c2, d2, e2, a2, b2, X[ 8], 15, k8); - Subround(G, b2, c2, d2, e2, a2, X[ 6], 5, k8); - Subround(G, a2, b2, c2, d2, e2, X[ 4], 8, k8); - Subround(G, e2, a2, b2, c2, d2, X[ 1], 11, k8); - Subround(G, d2, e2, a2, b2, c2, X[ 3], 14, k8); - Subround(G, c2, d2, e2, a2, b2, X[11], 14, k8); - Subround(G, b2, c2, d2, e2, a2, X[15], 6, k8); - Subround(G, a2, b2, c2, d2, e2, X[ 0], 14, k8); - Subround(G, e2, a2, b2, c2, d2, X[ 5], 6, k8); - Subround(G, d2, e2, a2, b2, c2, X[12], 9, k8); - Subround(G, c2, d2, e2, a2, b2, X[ 2], 12, k8); - Subround(G, b2, c2, d2, e2, a2, X[13], 9, k8); - Subround(G, a2, b2, c2, d2, e2, X[ 9], 12, k8); - Subround(G, e2, a2, b2, c2, d2, X[ 7], 5, k8); - Subround(G, d2, e2, a2, b2, c2, X[10], 15, k8); - Subround(G, c2, d2, e2, a2, b2, X[14], 8, k8); - - Subround(F, b2, c2, d2, e2, a2, X[12], 8, k9); - Subround(F, a2, b2, c2, d2, e2, X[15], 5, k9); - Subround(F, e2, a2, b2, c2, d2, X[10], 12, k9); - Subround(F, d2, e2, a2, b2, c2, X[ 4], 9, k9); - Subround(F, c2, d2, e2, a2, b2, X[ 1], 12, k9); - Subround(F, b2, c2, d2, e2, a2, X[ 5], 5, k9); - Subround(F, a2, b2, c2, d2, e2, X[ 8], 14, k9); - Subround(F, e2, a2, b2, c2, d2, X[ 7], 6, k9); - Subround(F, d2, e2, a2, b2, c2, X[ 6], 8, k9); - Subround(F, c2, d2, e2, a2, b2, X[ 2], 13, k9); - Subround(F, b2, c2, d2, e2, a2, X[13], 6, k9); - Subround(F, a2, b2, c2, d2, e2, X[14], 5, k9); - Subround(F, e2, a2, b2, c2, d2, X[ 0], 15, k9); - Subround(F, d2, e2, a2, b2, c2, X[ 3], 13, k9); - Subround(F, c2, d2, e2, a2, b2, X[ 9], 11, k9); - Subround(F, b2, c2, d2, e2, a2, X[11], 11, k9); - - a1 -= trackA[0]; - b1 -= trackA[1]; - c1 -= trackA[2]; - d1 -= trackA[3]; - e1 -= trackA[4]; - a2 -= trackB[0]; - b2 -= trackB[1]; - c2 -= trackB[2]; - d2 -= trackB[3]; - e2 -= trackB[4]; - - if (!last) - { - trackA[0] = (b1 + e1) - d2; - trackA[1] = c1 - e2; - trackA[2] = d1 - a2; - trackA[3] = e1 - b2; - trackA[4] = a1 - c2; - trackB[0] = d1 - e2; - trackB[1] = (e1 + c1) - a2; - trackB[2] = a1 - b2; - trackB[3] = b1 - c2; - trackB[4] = c1 - d2; - } - else - { - trackB[0] = a2 - a1; - trackB[1] = b2 - b1; - trackB[2] = c2 - c1; - trackB[3] = d2 - d1; - trackB[4] = e2 - e1; - trackA[0] = 0; - trackA[1] = 0; - trackA[2] = 0; - trackA[3] = 0; - trackA[4] = 0; - } -} - -NAMESPACE_END diff --git a/CryptoPP/ttmac.h b/CryptoPP/ttmac.h deleted file mode 100644 index b4bf86e26..000000000 --- a/CryptoPP/ttmac.h +++ /dev/null @@ -1,38 +0,0 @@ -// ttmac.h - written and placed in the public domain by Kevin Springle - -#ifndef CRYPTOPP_TTMAC_H -#define CRYPTOPP_TTMAC_H - -#include "seckey.h" -#include "iterhash.h" - -NAMESPACE_BEGIN(CryptoPP) - -//! _ -class CRYPTOPP_NO_VTABLE TTMAC_Base : public FixedKeyLength<20>, public IteratedHash -{ -public: - static std::string StaticAlgorithmName() {return std::string("Two-Track-MAC");} - CRYPTOPP_CONSTANT(DIGESTSIZE=20) - - unsigned int DigestSize() const {return DIGESTSIZE;}; - void UncheckedSetKey(const byte *userKey, unsigned int keylength, const NameValuePairs ¶ms); - void TruncatedFinal(byte *mac, size_t size); - -protected: - static void Transform (word32 *digest, const word32 *X, bool last); - void HashEndianCorrectedBlock(const word32 *data) {Transform(m_digest, data, false);} - void Init(); - word32* StateBuf() {return m_digest;} - - FixedSizeSecBlock m_digest; - FixedSizeSecBlock m_key; -}; - -//! Two-Track-MAC -/*! 160 Bit MAC with 160 Bit Key */ -DOCUMENTED_TYPEDEF(MessageAuthenticationCodeFinal, TTMAC) - -NAMESPACE_END - -#endif diff --git a/CryptoPP/validate.h b/CryptoPP/validate.h deleted file mode 100644 index 0ab23cba3..000000000 --- a/CryptoPP/validate.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef CRYPTOPP_VALIDATE_H -#define CRYPTOPP_VALIDATE_H - -#include "cryptlib.h" - -bool ValidateAll(bool thorough); -bool TestSettings(); -bool TestOS_RNG(); -bool ValidateBaseCode(); - -bool ValidateCRC32(); -bool ValidateAdler32(); -bool ValidateMD2(); -bool ValidateMD4(); -bool ValidateMD5(); -bool ValidateSHA(); -bool ValidateSHA2(); -bool ValidateTiger(); -bool ValidateRIPEMD(); -bool ValidatePanama(); -bool ValidateWhirlpool(); - -bool ValidateHMAC(); -bool ValidateTTMAC(); - -bool ValidateCipherModes(); -bool ValidatePBKDF(); - -bool ValidateDES(); -bool ValidateIDEA(); -bool ValidateSAFER(); -bool ValidateRC2(); -bool ValidateARC4(); - -bool ValidateRC5(); -bool ValidateBlowfish(); -bool ValidateThreeWay(); -bool ValidateGOST(); -bool ValidateSHARK(); -bool ValidateSEAL(); -bool ValidateCAST(); -bool ValidateSquare(); -bool ValidateSKIPJACK(); -bool ValidateRC6(); -bool ValidateMARS(); -bool ValidateRijndael(); -bool ValidateTwofish(); -bool ValidateSerpent(); -bool ValidateSHACAL2(); -bool ValidateCamellia(); -bool ValidateSalsa(); -bool ValidateSosemanuk(); -bool ValidateVMAC(); -bool ValidateCCM(); -bool ValidateGCM(); -bool ValidateCMAC(); - -bool ValidateBBS(); -bool ValidateDH(); -bool ValidateMQV(); -bool ValidateRSA(); -bool ValidateElGamal(); -bool ValidateDLIES(); -bool ValidateNR(); -bool ValidateDSA(bool thorough); -bool ValidateLUC(); -bool ValidateLUC_DL(); -bool ValidateLUC_DH(); -bool ValidateXTR_DH(); -bool ValidateRabin(); -bool ValidateRW(); -//bool ValidateBlumGoldwasser(); -bool ValidateECP(); -bool ValidateEC2N(); -bool ValidateECDSA(); -bool ValidateESIGN(); - -CryptoPP::RandomNumberGenerator & GlobalRNG(); -bool RunTestDataFile(const char *filename, const CryptoPP::NameValuePairs &overrideParameters=CryptoPP::g_nullNameValuePairs, bool thorough=true); - -#endif diff --git a/CryptoPP/vmac.cpp b/CryptoPP/vmac.cpp deleted file mode 100644 index 6b490f904..000000000 --- a/CryptoPP/vmac.cpp +++ /dev/null @@ -1,832 +0,0 @@ -// vmac.cpp - written and placed in the public domain by Wei Dai -// based on Ted Krovetz's public domain vmac.c and draft-krovetz-vmac-01.txt - -#include "pch.h" -#include "vmac.h" -#include "argnames.h" -#include "cpu.h" - -NAMESPACE_BEGIN(CryptoPP) - -#if defined(_MSC_VER) && !CRYPTOPP_BOOL_SLOW_WORD64 -#include -#endif - -#define VMAC_BOOL_WORD128 (defined(CRYPTOPP_WORD128_AVAILABLE) && !defined(CRYPTOPP_X64_ASM_AVAILABLE)) -#ifdef __BORLANDC__ -#define const // Turbo C++ 2006 workaround -#endif -static const word64 p64 = W64LIT(0xfffffffffffffeff); /* 2^64 - 257 prime */ -static const word64 m62 = W64LIT(0x3fffffffffffffff); /* 62-bit mask */ -static const word64 m63 = W64LIT(0x7fffffffffffffff); /* 63-bit mask */ -static const word64 m64 = W64LIT(0xffffffffffffffff); /* 64-bit mask */ -static const word64 mpoly = W64LIT(0x1fffffff1fffffff); /* Poly key mask */ -#ifdef __BORLANDC__ -#undef const -#endif -#if VMAC_BOOL_WORD128 -#ifdef __powerpc__ -// workaround GCC Bug 31690: ICE with const __uint128_t and C++ front-end -#define m126 ((word128(m62)<<64)|m64) -#else -static const word128 m126 = (word128(m62)<<64)|m64; /* 126-bit mask */ -#endif -#endif - -void VMAC_Base::UncheckedSetKey(const byte *userKey, unsigned int keylength, const NameValuePairs ¶ms) -{ - int digestLength = params.GetIntValueWithDefault(Name::DigestSize(), DefaultDigestSize()); - if (digestLength != 8 && digestLength != 16) - throw InvalidArgument("VMAC: DigestSize must be 8 or 16"); - m_is128 = digestLength == 16; - - m_L1KeyLength = params.GetIntValueWithDefault(Name::L1KeyLength(), 128); - if (m_L1KeyLength <= 0 || m_L1KeyLength % 128 != 0) - throw InvalidArgument("VMAC: L1KeyLength must be a positive multiple of 128"); - - AllocateBlocks(); - - BlockCipher &cipher = AccessCipher(); - cipher.SetKey(userKey, keylength, params); - unsigned int blockSize = cipher.BlockSize(); - unsigned int blockSizeInWords = blockSize / sizeof(word64); - SecBlock out(blockSizeInWords); - SecByteBlock in; - in.CleanNew(blockSize); - size_t i; - - /* Fill nh key */ - in[0] = 0x80; - cipher.AdvancedProcessBlocks(in, NULL, (byte *)m_nhKey(), m_nhKeySize()*sizeof(word64), cipher.BT_InBlockIsCounter); - ConditionalByteReverse(BIG_ENDIAN_ORDER, m_nhKey(), m_nhKey(), m_nhKeySize()*sizeof(word64)); - - /* Fill poly key */ - in[0] = 0xC0; - in[15] = 0; - for (i = 0; i <= (size_t)m_is128; i++) - { - cipher.ProcessBlock(in, out.BytePtr()); - m_polyState()[i*4+2] = GetWord(true, BIG_ENDIAN_ORDER, out.BytePtr()) & mpoly; - m_polyState()[i*4+3] = GetWord(true, BIG_ENDIAN_ORDER, out.BytePtr()+8) & mpoly; - in[15]++; - } - - /* Fill ip key */ - in[0] = 0xE0; - in[15] = 0; - word64 *l3Key = m_l3Key(); - for (i = 0; i <= (size_t)m_is128; i++) - do - { - cipher.ProcessBlock(in, out.BytePtr()); - l3Key[i*2+0] = GetWord(true, BIG_ENDIAN_ORDER, out.BytePtr()); - l3Key[i*2+1] = GetWord(true, BIG_ENDIAN_ORDER, out.BytePtr()+8); - in[15]++; - } while ((l3Key[i*2+0] >= p64) || (l3Key[i*2+1] >= p64)); - - m_padCached = false; - size_t nonceLength; - const byte *nonce = GetIVAndThrowIfInvalid(params, nonceLength); - Resynchronize(nonce, (int)nonceLength); -} - -void VMAC_Base::GetNextIV(RandomNumberGenerator &rng, byte *IV) -{ - SimpleKeyingInterface::GetNextIV(rng, IV); - IV[0] &= 0x7f; -} - -void VMAC_Base::Resynchronize(const byte *nonce, int len) -{ - size_t length = ThrowIfInvalidIVLength(len); - size_t s = IVSize(); - byte *storedNonce = m_nonce(); - - if (m_is128) - { - memset(storedNonce, 0, s-length); - memcpy(storedNonce+s-length, nonce, length); - AccessCipher().ProcessBlock(storedNonce, m_pad()); - } - else - { - if (m_padCached && (storedNonce[s-1] | 1) == (nonce[length-1] | 1)) - { - m_padCached = VerifyBufsEqual(storedNonce+s-length, nonce, length-1); - for (size_t i=0; m_padCached && i>64); rl = word64(p);} - #define AccumulateNH(a, b, c) a += word128(b)*(c) - #define Multiply128(r, i1, i2) r = word128(word64(i1)) * word64(i2) -#else - #if _MSC_VER >= 1400 && !defined(__INTEL_COMPILER) - #define MUL32(a, b) __emulu(word32(a), word32(b)) - #else - #define MUL32(a, b) ((word64)((word32)(a)) * (word32)(b)) - #endif - #if defined(CRYPTOPP_X64_ASM_AVAILABLE) - #define DeclareNH(a) word64 a##0=0, a##1=0 - #define MUL64(rh,rl,i1,i2) asm ("mulq %3" : "=a"(rl), "=d"(rh) : "a"(i1), "g"(i2) : "cc"); - #define AccumulateNH(a, b, c) asm ("mulq %3; addq %%rax, %0; adcq %%rdx, %1" : "+r"(a##0), "+r"(a##1) : "a"(b), "g"(c) : "%rdx", "cc"); - #define ADD128(rh,rl,ih,il) asm ("addq %3, %1; adcq %2, %0" : "+r"(rh),"+r"(rl) : "r"(ih),"r"(il) : "cc"); - #elif defined(_MSC_VER) && !CRYPTOPP_BOOL_SLOW_WORD64 - #define DeclareNH(a) word64 a##0=0, a##1=0 - #define MUL64(rh,rl,i1,i2) (rl) = _umul128(i1,i2,&(rh)); - #define AccumulateNH(a, b, c) {\ - word64 ph, pl;\ - pl = _umul128(b,c,&ph);\ - a##0 += pl;\ - a##1 += ph + (a##0 < pl);} - #else - #define VMAC_BOOL_32BIT 1 - #define DeclareNH(a) word64 a##0=0, a##1=0, a##2=0 - #define MUL64(rh,rl,i1,i2) \ - { word64 _i1 = (i1), _i2 = (i2); \ - word64 m1= MUL32(_i1,_i2>>32); \ - word64 m2= MUL32(_i1>>32,_i2); \ - rh = MUL32(_i1>>32,_i2>>32); \ - rl = MUL32(_i1,_i2); \ - ADD128(rh,rl,(m1 >> 32),(m1 << 32)); \ - ADD128(rh,rl,(m2 >> 32),(m2 << 32)); \ - } - #define AccumulateNH(a, b, c) {\ - word64 p = MUL32(b, c);\ - a##1 += word32((p)>>32);\ - a##0 += word32(p);\ - p = MUL32((b)>>32, c);\ - a##2 += word32((p)>>32);\ - a##1 += word32(p);\ - p = MUL32((b)>>32, (c)>>32);\ - a##2 += p;\ - p = MUL32(b, (c)>>32);\ - a##1 += word32(p);\ - a##2 += word32(p>>32);} - #endif -#endif -#ifndef VMAC_BOOL_32BIT - #define VMAC_BOOL_32BIT 0 -#endif -#ifndef ADD128 - #define ADD128(rh,rl,ih,il) \ - { word64 _il = (il); \ - (rl) += (_il); \ - (rh) += (ih) + ((rl) < (_il)); \ - } -#endif - -#if !(defined(_MSC_VER) && _MSC_VER < 1300) -template -#endif -void VMAC_Base::VHASH_Update_Template(const word64 *data, size_t blocksRemainingInWord64) -{ - #define INNER_LOOP_ITERATION(j) {\ - word64 d0 = ConditionalByteReverse(LITTLE_ENDIAN_ORDER, data[i+2*j+0]);\ - word64 d1 = ConditionalByteReverse(LITTLE_ENDIAN_ORDER, data[i+2*j+1]);\ - AccumulateNH(nhA, d0+nhK[i+2*j+0], d1+nhK[i+2*j+1]);\ - if (T_128BitTag)\ - AccumulateNH(nhB, d0+nhK[i+2*j+2], d1+nhK[i+2*j+3]);\ - } - -#if (defined(_MSC_VER) && _MSC_VER < 1300) - bool T_128BitTag = m_is128; -#endif - size_t L1KeyLengthInWord64 = m_L1KeyLength / 8; - size_t innerLoopEnd = L1KeyLengthInWord64; - const word64 *nhK = m_nhKey(); - word64 *polyS = m_polyState(); - bool isFirstBlock = true; - size_t i; - - #if !VMAC_BOOL_32BIT - #if VMAC_BOOL_WORD128 - word128 a1, a2; - #else - word64 ah1, al1, ah2, al2; - #endif - word64 kh1, kl1, kh2, kl2; - kh1=(polyS+0*4+2)[0]; kl1=(polyS+0*4+2)[1]; - if (T_128BitTag) - { - kh2=(polyS+1*4+2)[0]; kl2=(polyS+1*4+2)[1]; - } - #endif - - do - { - DeclareNH(nhA); - DeclareNH(nhB); - - i = 0; - if (blocksRemainingInWord64 < L1KeyLengthInWord64) - { - if (blocksRemainingInWord64 % 8) - { - innerLoopEnd = blocksRemainingInWord64 % 8; - for (; i> 32); - nh1[0] = word32(nhA1); - nh2[0] = (nhA2 + (nhA1 >> 32)) & m62; - - if (T_128BitTag) - { - nh0[1] = word32(nhB0); - nhB1 += (nhB0 >> 32); - nh1[1] = word32(nhB1); - nh2[1] = (nhB2 + (nhB1 >> 32)) & m62; - } - - #define a0 (((word32 *)(polyS+i*4))[2+NativeByteOrder::ToEnum()]) - #define a1 (*(((word32 *)(polyS+i*4))+3-NativeByteOrder::ToEnum())) // workaround for GCC 3.2 - #define a2 (((word32 *)(polyS+i*4))[0+NativeByteOrder::ToEnum()]) - #define a3 (*(((word32 *)(polyS+i*4))+1-NativeByteOrder::ToEnum())) - #define aHi ((polyS+i*4)[0]) - #define k0 (((word32 *)(polyS+i*4+2))[2+NativeByteOrder::ToEnum()]) - #define k1 (*(((word32 *)(polyS+i*4+2))+3-NativeByteOrder::ToEnum())) - #define k2 (((word32 *)(polyS+i*4+2))[0+NativeByteOrder::ToEnum()]) - #define k3 (*(((word32 *)(polyS+i*4+2))+1-NativeByteOrder::ToEnum())) - #define kHi ((polyS+i*4+2)[0]) - - if (isFirstBlock) - { - isFirstBlock = false; - if (m_isFirstBlock) - { - m_isFirstBlock = false; - for (i=0; i<=(size_t)T_128BitTag; i++) - { - word64 t = (word64)nh0[i] + k0; - a0 = (word32)t; - t = (t >> 32) + nh1[i] + k1; - a1 = (word32)t; - aHi = (t >> 32) + nh2[i] + kHi; - } - continue; - } - } - for (i=0; i<=(size_t)T_128BitTag; i++) - { - word64 p, t; - word32 t2; - - p = MUL32(a3, 2*k3); - p += nh2[i]; - p += MUL32(a0, k2); - p += MUL32(a1, k1); - p += MUL32(a2, k0); - t2 = (word32)p; - p >>= 32; - p += MUL32(a0, k3); - p += MUL32(a1, k2); - p += MUL32(a2, k1); - p += MUL32(a3, k0); - t = (word64(word32(p) & 0x7fffffff) << 32) | t2; - p >>= 31; - p += nh0[i]; - p += MUL32(a0, k0); - p += MUL32(a1, 2*k3); - p += MUL32(a2, 2*k2); - p += MUL32(a3, 2*k1); - t2 = (word32)p; - p >>= 32; - p += nh1[i]; - p += MUL32(a0, k1); - p += MUL32(a1, k0); - p += MUL32(a2, 2*k3); - p += MUL32(a3, 2*k2); - a0 = t2; - a1 = (word32)p; - aHi = (p >> 32) + t; - } - - #undef a0 - #undef a1 - #undef a2 - #undef a3 - #undef aHi - #undef k0 - #undef k1 - #undef k2 - #undef k3 - #undef kHi - #else // #if VMAC_BOOL_32BIT - if (isFirstBlock) - { - isFirstBlock = false; - if (m_isFirstBlock) - { - m_isFirstBlock = false; - #if VMAC_BOOL_WORD128 - #define first_poly_step(a, kh, kl, m) a = (m & m126) + ((word128(kh) << 64) | kl) - - first_poly_step(a1, kh1, kl1, nhA); - if (T_128BitTag) - first_poly_step(a2, kh2, kl2, nhB); - #else - #define first_poly_step(ah, al, kh, kl, mh, ml) {\ - mh &= m62;\ - ADD128(mh, ml, kh, kl); \ - ah = mh; al = ml;} - - first_poly_step(ah1, al1, kh1, kl1, nhA1, nhA0); - if (T_128BitTag) - first_poly_step(ah2, al2, kh2, kl2, nhB1, nhB0); - #endif - continue; - } - else - { - #if VMAC_BOOL_WORD128 - a1 = (word128((polyS+0*4)[0]) << 64) | (polyS+0*4)[1]; - #else - ah1=(polyS+0*4)[0]; al1=(polyS+0*4)[1]; - #endif - if (T_128BitTag) - { - #if VMAC_BOOL_WORD128 - a2 = (word128((polyS+1*4)[0]) << 64) | (polyS+1*4)[1]; - #else - ah2=(polyS+1*4)[0]; al2=(polyS+1*4)[1]; - #endif - } - } - } - - #if VMAC_BOOL_WORD128 - #define poly_step(a, kh, kl, m) \ - { word128 t1, t2, t3, t4;\ - Multiply128(t2, a>>64, kl);\ - Multiply128(t3, a, kh);\ - Multiply128(t1, a, kl);\ - Multiply128(t4, a>>64, 2*kh);\ - t2 += t3;\ - t4 += t1;\ - t2 += t4>>64;\ - a = (word128(word64(t2)&m63) << 64) | word64(t4);\ - t2 *= 2;\ - a += m & m126;\ - a += t2>>64;} - - poly_step(a1, kh1, kl1, nhA); - if (T_128BitTag) - poly_step(a2, kh2, kl2, nhB); - #else - #define poly_step(ah, al, kh, kl, mh, ml) \ - { word64 t1h, t1l, t2h, t2l, t3h, t3l, z=0; \ - /* compute ab*cd, put bd into result registers */ \ - MUL64(t2h,t2l,ah,kl); \ - MUL64(t3h,t3l,al,kh); \ - MUL64(t1h,t1l,ah,2*kh); \ - MUL64(ah,al,al,kl); \ - /* add together ad + bc */ \ - ADD128(t2h,t2l,t3h,t3l); \ - /* add 2 * ac to result */ \ - ADD128(ah,al,t1h,t1l); \ - /* now (ah,al), (t2l,2*t2h) need summing */ \ - /* first add the high registers, carrying into t2h */ \ - ADD128(t2h,ah,z,t2l); \ - /* double t2h and add top bit of ah */ \ - t2h += t2h + (ah >> 63); \ - ah &= m63; \ - /* now add the low registers */ \ - mh &= m62; \ - ADD128(ah,al,mh,ml); \ - ADD128(ah,al,z,t2h); \ - } - - poly_step(ah1, al1, kh1, kl1, nhA1, nhA0); - if (T_128BitTag) - poly_step(ah2, al2, kh2, kl2, nhB1, nhB0); - #endif - #endif // #if VMAC_BOOL_32BIT - } while (blocksRemainingInWord64); - - #if VMAC_BOOL_WORD128 - (polyS+0*4)[0]=word64(a1>>64); (polyS+0*4)[1]=word64(a1); - if (T_128BitTag) - { - (polyS+1*4)[0]=word64(a2>>64); (polyS+1*4)[1]=word64(a2); - } - #elif !VMAC_BOOL_32BIT - (polyS+0*4)[0]=ah1; (polyS+0*4)[1]=al1; - if (T_128BitTag) - { - (polyS+1*4)[0]=ah2; (polyS+1*4)[1]=al2; - } - #endif -} - -inline void VMAC_Base::VHASH_Update(const word64 *data, size_t blocksRemainingInWord64) -{ -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 - if (HasSSE2()) - { - VHASH_Update_SSE2(data, blocksRemainingInWord64, 0); - if (m_is128) - VHASH_Update_SSE2(data, blocksRemainingInWord64, 1); - m_isFirstBlock = false; - } - else -#endif - { -#if defined(_MSC_VER) && _MSC_VER < 1300 - VHASH_Update_Template(data, blocksRemainingInWord64); -#else - if (m_is128) - VHASH_Update_Template(data, blocksRemainingInWord64); - else - VHASH_Update_Template(data, blocksRemainingInWord64); -#endif - } -} - -size_t VMAC_Base::HashMultipleBlocks(const word64 *data, size_t length) -{ - size_t remaining = ModPowerOf2(length, m_L1KeyLength); - VHASH_Update(data, (length-remaining)/8); - return remaining; -} - -static word64 L3Hash(const word64 *input, const word64 *l3Key, size_t len) -{ - word64 rh, rl, t, z=0; - word64 p1 = input[0], p2 = input[1]; - word64 k1 = l3Key[0], k2 = l3Key[1]; - - /* fully reduce (p1,p2)+(len,0) mod p127 */ - t = p1 >> 63; - p1 &= m63; - ADD128(p1, p2, len, t); - /* At this point, (p1,p2) is at most 2^127+(len<<64) */ - t = (p1 > m63) + ((p1 == m63) & (p2 == m64)); - ADD128(p1, p2, z, t); - p1 &= m63; - - /* compute (p1,p2)/(2^64-2^32) and (p1,p2)%(2^64-2^32) */ - t = p1 + (p2 >> 32); - t += (t >> 32); - t += (word32)t > 0xfffffffeU; - p1 += (t >> 32); - p2 += (p1 << 32); - - /* compute (p1+k1)%p64 and (p2+k2)%p64 */ - p1 += k1; - p1 += (0 - (p1 < k1)) & 257; - p2 += k2; - p2 += (0 - (p2 < k2)) & 257; - - /* compute (p1+k1)*(p2+k2)%p64 */ - MUL64(rh, rl, p1, p2); - t = rh >> 56; - ADD128(t, rl, z, rh); - rh <<= 8; - ADD128(t, rl, z, rh); - t += t << 8; - rl += t; - rl += (0 - (rl < t)) & 257; - rl += (0 - (rl > p64-1)) & 257; - return rl; -} - -void VMAC_Base::TruncatedFinal(byte *mac, size_t size) -{ - size_t len = ModPowerOf2(GetBitCountLo()/8, m_L1KeyLength); - - if (len) - { - memset(m_data()+len, 0, (0-len)%16); - VHASH_Update(DataBuf(), ((len+15)/16)*2); - len *= 8; // convert to bits - } - else if (m_isFirstBlock) - { - // special case for empty string - m_polyState()[0] = m_polyState()[2]; - m_polyState()[1] = m_polyState()[3]; - if (m_is128) - { - m_polyState()[4] = m_polyState()[6]; - m_polyState()[5] = m_polyState()[7]; - } - } - - if (m_is128) - { - word64 t[2]; - t[0] = L3Hash(m_polyState(), m_l3Key(), len) + GetWord(true, BIG_ENDIAN_ORDER, m_pad()); - t[1] = L3Hash(m_polyState()+4, m_l3Key()+2, len) + GetWord(true, BIG_ENDIAN_ORDER, m_pad()+8); - if (size == 16) - { - PutWord(false, BIG_ENDIAN_ORDER, mac, t[0]); - PutWord(false, BIG_ENDIAN_ORDER, mac+8, t[1]); - } - else - { - t[0] = ConditionalByteReverse(BIG_ENDIAN_ORDER, t[0]); - t[1] = ConditionalByteReverse(BIG_ENDIAN_ORDER, t[1]); - memcpy(mac, t, size); - } - } - else - { - word64 t = L3Hash(m_polyState(), m_l3Key(), len); - t += GetWord(true, BIG_ENDIAN_ORDER, m_pad() + (m_nonce()[IVSize()-1]&1) * 8); - if (size == 8) - PutWord(false, BIG_ENDIAN_ORDER, mac, t); - else - { - t = ConditionalByteReverse(BIG_ENDIAN_ORDER, t); - memcpy(mac, &t, size); - } - } -} - -NAMESPACE_END diff --git a/CryptoPP/vmac.h b/CryptoPP/vmac.h deleted file mode 100644 index 07240173c..000000000 --- a/CryptoPP/vmac.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef CRYPTOPP_VMAC_H -#define CRYPTOPP_VMAC_H - -#include "iterhash.h" -#include "seckey.h" - -NAMESPACE_BEGIN(CryptoPP) - -/// . -class VMAC_Base : public IteratedHashBase -{ -public: - std::string AlgorithmName() const {return std::string("VMAC(") + GetCipher().AlgorithmName() + ")-" + IntToString(DigestSize()*8);} - unsigned int IVSize() const {return GetCipher().BlockSize();} - unsigned int MinIVLength() const {return 1;} - void Resynchronize(const byte *nonce, int length=-1); - void GetNextIV(RandomNumberGenerator &rng, byte *IV); - unsigned int DigestSize() const {return m_is128 ? 16 : 8;}; - void UncheckedSetKey(const byte *userKey, unsigned int keylength, const NameValuePairs ¶ms); - void TruncatedFinal(byte *mac, size_t size); - unsigned int BlockSize() const {return m_L1KeyLength;} - ByteOrder GetByteOrder() const {return LITTLE_ENDIAN_ORDER;} - -protected: - virtual BlockCipher & AccessCipher() =0; - virtual int DefaultDigestSize() const =0; - const BlockCipher & GetCipher() const {return const_cast(this)->AccessCipher();} - void HashEndianCorrectedBlock(const word64 *data); - size_t HashMultipleBlocks(const word64 *input, size_t length); - void Init() {} - word64* StateBuf() {return NULL;} - word64* DataBuf() {return (word64 *)m_data();} - - void VHASH_Update_SSE2(const word64 *data, size_t blocksRemainingInWord64, int tagPart); -#if !(defined(_MSC_VER) && _MSC_VER < 1300) // can't use function template here with VC6 - template -#endif - void VHASH_Update_Template(const word64 *data, size_t blockRemainingInWord128); - void VHASH_Update(const word64 *data, size_t blocksRemainingInWord128); - - CRYPTOPP_BLOCK_1(polyState, word64, 4*(m_is128+1)) - CRYPTOPP_BLOCK_2(nhKey, word64, m_L1KeyLength/sizeof(word64) + 2*m_is128) - CRYPTOPP_BLOCK_3(data, byte, m_L1KeyLength) - CRYPTOPP_BLOCK_4(l3Key, word64, 2*(m_is128+1)) - CRYPTOPP_BLOCK_5(nonce, byte, IVSize()) - CRYPTOPP_BLOCK_6(pad, byte, IVSize()) - CRYPTOPP_BLOCKS_END(6) - - bool m_is128, m_padCached, m_isFirstBlock; - int m_L1KeyLength; -}; - -/// VMAC -template -class VMAC : public SimpleKeyingInterfaceImpl > -{ -public: - static std::string StaticAlgorithmName() {return std::string("VMAC(") + T_BlockCipher::StaticAlgorithmName() + ")-" + IntToString(T_DigestBitSize);} - -private: - BlockCipher & AccessCipher() {return m_cipher;} - int DefaultDigestSize() const {return T_DigestBitSize/8;} - typename T_BlockCipher::Encryption m_cipher; -}; - -NAMESPACE_END - -#endif diff --git a/CryptoPP/wait.cpp b/CryptoPP/wait.cpp deleted file mode 100644 index 198785838..000000000 --- a/CryptoPP/wait.cpp +++ /dev/null @@ -1,397 +0,0 @@ -// wait.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "wait.h" -#include "misc.h" - -#ifdef SOCKETS_AVAILABLE - -#ifdef USE_BERKELEY_STYLE_SOCKETS -#include -#include -#include -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -unsigned int WaitObjectContainer::MaxWaitObjects() -{ -#ifdef USE_WINDOWS_STYLE_SOCKETS - return MAXIMUM_WAIT_OBJECTS * (MAXIMUM_WAIT_OBJECTS-1); -#else - return FD_SETSIZE; -#endif -} - -WaitObjectContainer::WaitObjectContainer(WaitObjectsTracer* tracer) - : m_tracer(tracer), m_eventTimer(Timer::MILLISECONDS) - , m_sameResultCount(0), m_noWaitTimer(Timer::MILLISECONDS) -{ - Clear(); - m_eventTimer.StartTimer(); -} - -void WaitObjectContainer::Clear() -{ -#ifdef USE_WINDOWS_STYLE_SOCKETS - m_handles.clear(); -#else - m_maxFd = 0; - FD_ZERO(&m_readfds); - FD_ZERO(&m_writefds); -#endif - m_noWait = false; - m_firstEventTime = 0; -} - -inline void WaitObjectContainer::SetLastResult(LastResultType result) -{ - if (result == m_lastResult) - m_sameResultCount++; - else - { - m_lastResult = result; - m_sameResultCount = 0; - } -} - -void WaitObjectContainer::DetectNoWait(LastResultType result, CallStack const& callStack) -{ - if (result == m_lastResult && m_noWaitTimer.ElapsedTime() > 1000) - { - if (m_sameResultCount > m_noWaitTimer.ElapsedTime()) - { - if (m_tracer) - { - std::string desc = "No wait loop detected - m_lastResult: "; - desc.append(IntToString(m_lastResult)).append(", call stack:"); - for (CallStack const* cs = &callStack; cs; cs = cs->Prev()) - desc.append("\n- ").append(cs->Format()); - m_tracer->TraceNoWaitLoop(desc); - } - try { throw 0; } catch (...) {} // help debugger break - } - - m_noWaitTimer.StartTimer(); - m_sameResultCount = 0; - } -} - -void WaitObjectContainer::SetNoWait(CallStack const& callStack) -{ - DetectNoWait(LASTRESULT_NOWAIT, CallStack("WaitObjectContainer::SetNoWait()", &callStack)); - m_noWait = true; -} - -void WaitObjectContainer::ScheduleEvent(double milliseconds, CallStack const& callStack) -{ - if (milliseconds <= 3) - DetectNoWait(LASTRESULT_SCHEDULED, CallStack("WaitObjectContainer::ScheduleEvent()", &callStack)); - double thisEventTime = m_eventTimer.ElapsedTimeAsDouble() + milliseconds; - if (!m_firstEventTime || thisEventTime < m_firstEventTime) - m_firstEventTime = thisEventTime; -} - -#ifdef USE_WINDOWS_STYLE_SOCKETS - -struct WaitingThreadData -{ - bool waitingToWait, terminate; - HANDLE startWaiting, stopWaiting; - const HANDLE *waitHandles; - unsigned int count; - HANDLE threadHandle; - DWORD threadId; - DWORD* error; -}; - -WaitObjectContainer::~WaitObjectContainer() -{ - try // don't let exceptions escape destructor - { - if (!m_threads.empty()) - { - HANDLE threadHandles[MAXIMUM_WAIT_OBJECTS]; - unsigned int i; - for (i=0; i pThread((WaitingThreadData *)lParam); - WaitingThreadData &thread = *pThread; - std::vector handles; - - while (true) - { - thread.waitingToWait = true; - ::WaitForSingleObject(thread.startWaiting, INFINITE); - thread.waitingToWait = false; - - if (thread.terminate) - break; - if (!thread.count) - continue; - - handles.resize(thread.count + 1); - handles[0] = thread.stopWaiting; - std::copy(thread.waitHandles, thread.waitHandles+thread.count, handles.begin()+1); - - DWORD result = ::WaitForMultipleObjects((DWORD)handles.size(), &handles[0], FALSE, INFINITE); - - if (result == WAIT_OBJECT_0) - continue; // another thread finished waiting first, so do nothing - SetEvent(thread.stopWaiting); - if (!(result > WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + handles.size())) - { - assert(!"error in WaitingThread"); // break here so we can see which thread has an error - *thread.error = ::GetLastError(); - } - } - - return S_OK; // return a value here to avoid compiler warning -} - -void WaitObjectContainer::CreateThreads(unsigned int count) -{ - size_t currentCount = m_threads.size(); - if (currentCount == 0) - { - m_startWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL); - m_stopWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL); - } - - if (currentCount < count) - { - m_threads.resize(count); - for (size_t i=currentCount; i MAXIMUM_WAIT_OBJECTS) - { - // too many wait objects for a single WaitForMultipleObjects call, so use multiple threads - static const unsigned int WAIT_OBJECTS_PER_THREAD = MAXIMUM_WAIT_OBJECTS-1; - unsigned int nThreads = (unsigned int)((m_handles.size() + WAIT_OBJECTS_PER_THREAD - 1) / WAIT_OBJECTS_PER_THREAD); - if (nThreads > MAXIMUM_WAIT_OBJECTS) // still too many wait objects, maybe implement recursive threading later? - throw Err("WaitObjectContainer: number of wait objects exceeds limit"); - CreateThreads(nThreads); - DWORD error = S_OK; - - for (unsigned int i=0; i 0) - { - unsigned long timeAfterWait = t.ElapsedTime(); - OutputDebugString(("Handles " + IntToString(m_handles.size()) + ", Woke up by " + IntToString(result-WAIT_OBJECT_0) + ", Busied for " + IntToString(timeBeforeWait-lastTime) + " us, Waited for " + IntToString(timeAfterWait-timeBeforeWait) + " us, max " + IntToString(milliseconds) + "ms\n").c_str()); - lastTime = timeAfterWait; - } -#endif - if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + m_handles.size()) - { - if (result == m_lastResult) - m_sameResultCount++; - else - { - m_lastResult = result; - m_sameResultCount = 0; - } - return true; - } - else if (result == WAIT_TIMEOUT) - { - SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT); - return timeoutIsScheduledEvent; - } - else - throw Err("WaitObjectContainer: WaitForMultipleObjects failed with error " + IntToString(::GetLastError())); - } -} - -#else // #ifdef USE_WINDOWS_STYLE_SOCKETS - -void WaitObjectContainer::AddReadFd(int fd, CallStack const& callStack) // TODO: do something with callStack -{ - FD_SET(fd, &m_readfds); - m_maxFd = STDMAX(m_maxFd, fd); -} - -void WaitObjectContainer::AddWriteFd(int fd, CallStack const& callStack) // TODO: do something with callStack -{ - FD_SET(fd, &m_writefds); - m_maxFd = STDMAX(m_maxFd, fd); -} - -bool WaitObjectContainer::Wait(unsigned long milliseconds) -{ - if (m_noWait || (!m_maxFd && !m_firstEventTime)) - return true; - - bool timeoutIsScheduledEvent = false; - - if (m_firstEventTime) - { - double timeToFirstEvent = SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble()); - if (timeToFirstEvent <= milliseconds) - { - milliseconds = (unsigned long)timeToFirstEvent; - timeoutIsScheduledEvent = true; - } - } - - timeval tv, *timeout; - - if (milliseconds == INFINITE_TIME) - timeout = NULL; - else - { - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = (milliseconds % 1000) * 1000; - timeout = &tv; - } - - int result = select(m_maxFd+1, &m_readfds, &m_writefds, NULL, timeout); - - if (result > 0) - return true; - else if (result == 0) - return timeoutIsScheduledEvent; - else - throw Err("WaitObjectContainer: select failed with error " + errno); -} - -#endif - -// ******************************************************** - -std::string CallStack::Format() const -{ - return m_info; -} - -std::string CallStackWithNr::Format() const -{ - return std::string(m_info) + " / nr: " + IntToString(m_nr); -} - -std::string CallStackWithStr::Format() const -{ - return std::string(m_info) + " / " + std::string(m_z); -} - -bool Waitable::Wait(unsigned long milliseconds, CallStack const& callStack) -{ - WaitObjectContainer container; - GetWaitObjects(container, callStack); // reduce clutter by not adding this func to stack - return container.Wait(milliseconds); -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/wait.h b/CryptoPP/wait.h deleted file mode 100644 index 045afbc18..000000000 --- a/CryptoPP/wait.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef CRYPTOPP_WAIT_H -#define CRYPTOPP_WAIT_H - -#include "config.h" - -#ifdef SOCKETS_AVAILABLE - -#include "misc.h" -#include "cryptlib.h" -#include - -#ifdef USE_WINDOWS_STYLE_SOCKETS -#include -#else -#include -#endif - -#include "hrtimer.h" - -NAMESPACE_BEGIN(CryptoPP) - -class Tracer -{ -public: - Tracer(unsigned int level) : m_level(level) {} - virtual ~Tracer() {} - -protected: - //! Override this in your most-derived tracer to do the actual tracing. - virtual void Trace(unsigned int n, std::string const& s) = 0; - - /*! By default, tracers will decide which trace messages to trace according to a trace level - mechanism. If your most-derived tracer uses a different mechanism, override this to - return false. If this method returns false, the default TraceXxxx(void) methods will all - return 0 and must be overridden explicitly by your tracer for trace messages you want. */ - virtual bool UsingDefaults() const { return true; } - -protected: - unsigned int m_level; - - void TraceIf(unsigned int n, std::string const&s) - { if (n) Trace(n, s); } - - /*! Returns nr if, according to the default log settings mechanism (using log levels), - the message should be traced. Returns 0 if the default trace level mechanism is not - in use, or if it is in use but the event should not be traced. Provided as a utility - method for easier and shorter coding of default TraceXxxx(void) implementations. */ - unsigned int Tracing(unsigned int nr, unsigned int minLevel) const - { return (UsingDefaults() && m_level >= minLevel) ? nr : 0; } -}; - -// Your Tracer-derived class should inherit as virtual public from Tracer or another -// Tracer-derived class, and should pass the log level in its constructor. You can use the -// following methods to begin and end your Tracer definition. - -// This constructor macro initializes Tracer directly even if not derived directly from it; -// this is intended, virtual base classes are always initialized by the most derived class. -#define CRYPTOPP_TRACER_CONSTRUCTOR(DERIVED) \ - public: DERIVED(unsigned int level = 0) : Tracer(level) {} - -#define CRYPTOPP_BEGIN_TRACER_CLASS_1(DERIVED, BASE1) \ - class DERIVED : virtual public BASE1 { CRYPTOPP_TRACER_CONSTRUCTOR(DERIVED) - -#define CRYPTOPP_BEGIN_TRACER_CLASS_2(DERIVED, BASE1, BASE2) \ - class DERIVED : virtual public BASE1, virtual public BASE2 { CRYPTOPP_TRACER_CONSTRUCTOR(DERIVED) - -#define CRYPTOPP_END_TRACER_CLASS }; - -// In your Tracer-derived class, you should define a globally unique event number for each -// new event defined. This can be done using the following macros. - -#define CRYPTOPP_BEGIN_TRACER_EVENTS(UNIQUENR) enum { EVENTBASE = UNIQUENR, -#define CRYPTOPP_TRACER_EVENT(EVENTNAME) EventNr_##EVENTNAME, -#define CRYPTOPP_END_TRACER_EVENTS }; - -// In your own Tracer-derived class, you must define two methods per new trace event type: -// - unsigned int TraceXxxx() const -// Your default implementation of this method should return the event number if according -// to the default trace level system the event should be traced, or 0 if it should not. -// - void TraceXxxx(string const& s) -// This method should call TraceIf(TraceXxxx(), s); to do the tracing. -// For your convenience, a macro to define these two types of methods are defined below. -// If you use this macro, you should also use the TRACER_EVENTS macros above to associate -// event names with numbers. - -#define CRYPTOPP_TRACER_EVENT_METHODS(EVENTNAME, LOGLEVEL) \ - virtual unsigned int Trace##EVENTNAME() const { return Tracing(EventNr_##EVENTNAME, LOGLEVEL); } \ - virtual void Trace##EVENTNAME(std::string const& s) { TraceIf(Trace##EVENTNAME(), s); } - - -/*! A simple unidirectional linked list with m_prev == 0 to indicate the final entry. - The aim of this implementation is to provide a very lightweight and practical - tracing mechanism with a low performance impact. Functions and methods supporting - this call-stack mechanism would take a parameter of the form "CallStack const& callStack", - and would pass this parameter to subsequent functions they call using the construct: - - SubFunc(arg1, arg2, CallStack("my func at place such and such", &callStack)); - - The advantage of this approach is that it is easy to use and should be very efficient, - involving no allocation from the heap, just a linked list of stack objects containing - pointers to static ASCIIZ strings (or possibly additional but simple data if derived). */ -class CallStack -{ -public: - CallStack(char const* i, CallStack const* p) : m_info(i), m_prev(p) {} - CallStack const* Prev() const { return m_prev; } - virtual std::string Format() const; - -protected: - char const* m_info; - CallStack const* m_prev; -}; - -/*! An extended CallStack entry type with an additional numeric parameter. */ -class CallStackWithNr : public CallStack -{ -public: - CallStackWithNr(char const* i, word32 n, CallStack const* p) : CallStack(i, p), m_nr(n) {} - std::string Format() const; - -protected: - word32 m_nr; -}; - -/*! An extended CallStack entry type with an additional string parameter. */ -class CallStackWithStr : public CallStack -{ -public: - CallStackWithStr(char const* i, char const* z, CallStack const* p) : CallStack(i, p), m_z(z) {} - std::string Format() const; - -protected: - char const* m_z; -}; - -CRYPTOPP_BEGIN_TRACER_CLASS_1(WaitObjectsTracer, Tracer) - CRYPTOPP_BEGIN_TRACER_EVENTS(0x48752841) - CRYPTOPP_TRACER_EVENT(NoWaitLoop) - CRYPTOPP_END_TRACER_EVENTS - CRYPTOPP_TRACER_EVENT_METHODS(NoWaitLoop, 1) -CRYPTOPP_END_TRACER_CLASS - -struct WaitingThreadData; - -//! container of wait objects -class WaitObjectContainer : public NotCopyable -{ -public: - //! exception thrown by WaitObjectContainer - class Err : public Exception - { - public: - Err(const std::string& s) : Exception(IO_ERROR, s) {} - }; - - static unsigned int MaxWaitObjects(); - - WaitObjectContainer(WaitObjectsTracer* tracer = 0); - - void Clear(); - void SetNoWait(CallStack const& callStack); - void ScheduleEvent(double milliseconds, CallStack const& callStack); - // returns false if timed out - bool Wait(unsigned long milliseconds); - -#ifdef USE_WINDOWS_STYLE_SOCKETS - ~WaitObjectContainer(); - void AddHandle(HANDLE handle, CallStack const& callStack); -#else - void AddReadFd(int fd, CallStack const& callStack); - void AddWriteFd(int fd, CallStack const& callStack); -#endif - -private: - WaitObjectsTracer* m_tracer; - -#ifdef USE_WINDOWS_STYLE_SOCKETS - void CreateThreads(unsigned int count); - std::vector m_handles; - std::vector m_threads; - HANDLE m_startWaiting; - HANDLE m_stopWaiting; -#else - fd_set m_readfds, m_writefds; - int m_maxFd; -#endif - bool m_noWait; - double m_firstEventTime; - Timer m_eventTimer; - -#ifdef USE_WINDOWS_STYLE_SOCKETS - typedef size_t LastResultType; -#else - typedef int LastResultType; -#endif - enum { LASTRESULT_NOWAIT = -1, LASTRESULT_SCHEDULED = -2, LASTRESULT_TIMEOUT = -3 }; - LastResultType m_lastResult; - unsigned int m_sameResultCount; - Timer m_noWaitTimer; - void SetLastResult(LastResultType result); - void DetectNoWait(LastResultType result, CallStack const& callStack); -}; - -NAMESPACE_END - -#endif - -#endif diff --git a/CryptoPP/winpipes.cpp b/CryptoPP/winpipes.cpp deleted file mode 100644 index 1c2e047b0..000000000 --- a/CryptoPP/winpipes.cpp +++ /dev/null @@ -1,205 +0,0 @@ -// winpipes.cpp - written and placed in the public domain by Wei Dai - -#include "pch.h" -#include "winpipes.h" - -#ifdef WINDOWS_PIPES_AVAILABLE - -#include "wait.h" - -NAMESPACE_BEGIN(CryptoPP) - -WindowsHandle::WindowsHandle(HANDLE h, bool own) - : m_h(h), m_own(own) -{ -} - -WindowsHandle::~WindowsHandle() -{ - if (m_own) - { - try - { - CloseHandle(); - } - catch (...) - { - } - } -} - -bool WindowsHandle::HandleValid() const -{ - return m_h && m_h != INVALID_HANDLE_VALUE; -} - -void WindowsHandle::AttachHandle(HANDLE h, bool own) -{ - if (m_own) - CloseHandle(); - - m_h = h; - m_own = own; - HandleChanged(); -} - -HANDLE WindowsHandle::DetachHandle() -{ - HANDLE h = m_h; - m_h = INVALID_HANDLE_VALUE; - HandleChanged(); - return h; -} - -void WindowsHandle::CloseHandle() -{ - if (m_h != INVALID_HANDLE_VALUE) - { - ::CloseHandle(m_h); - m_h = INVALID_HANDLE_VALUE; - HandleChanged(); - } -} - -// ******************************************************** - -void WindowsPipe::HandleError(const char *operation) const -{ - DWORD err = GetLastError(); - throw Err(GetHandle(), operation, err); -} - -WindowsPipe::Err::Err(HANDLE s, const std::string& operation, int error) - : OS_Error(IO_ERROR, "WindowsPipe: " + operation + " operation failed with error 0x" + IntToString(error, 16), operation, error) - , m_h(s) -{ -} - -// ************************************************************* - -WindowsPipeReceiver::WindowsPipeReceiver() - : m_resultPending(false), m_eofReceived(false) -{ - m_event.AttachHandle(CreateEvent(NULL, true, false, NULL), true); - CheckAndHandleError("CreateEvent", m_event.HandleValid()); - memset(&m_overlapped, 0, sizeof(m_overlapped)); - m_overlapped.hEvent = m_event; -} - -bool WindowsPipeReceiver::Receive(byte* buf, size_t bufLen) -{ - assert(!m_resultPending && !m_eofReceived); - - HANDLE h = GetHandle(); - // don't queue too much at once, or we might use up non-paged memory - if (ReadFile(h, buf, UnsignedMin((DWORD)128*1024, bufLen), &m_lastResult, &m_overlapped)) - { - if (m_lastResult == 0) - m_eofReceived = true; - } - else - { - switch (GetLastError()) - { - default: - CheckAndHandleError("ReadFile", false); - case ERROR_BROKEN_PIPE: - case ERROR_HANDLE_EOF: - m_lastResult = 0; - m_eofReceived = true; - break; - case ERROR_IO_PENDING: - m_resultPending = true; - } - } - return !m_resultPending; -} - -void WindowsPipeReceiver::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) -{ - if (m_resultPending) - container.AddHandle(m_event, CallStack("WindowsPipeReceiver::GetWaitObjects() - result pending", &callStack)); - else if (!m_eofReceived) - container.SetNoWait(CallStack("WindowsPipeReceiver::GetWaitObjects() - result ready", &callStack)); -} - -unsigned int WindowsPipeReceiver::GetReceiveResult() -{ - if (m_resultPending) - { - HANDLE h = GetHandle(); - if (GetOverlappedResult(h, &m_overlapped, &m_lastResult, false)) - { - if (m_lastResult == 0) - m_eofReceived = true; - } - else - { - switch (GetLastError()) - { - default: - CheckAndHandleError("GetOverlappedResult", false); - case ERROR_BROKEN_PIPE: - case ERROR_HANDLE_EOF: - m_lastResult = 0; - m_eofReceived = true; - } - } - m_resultPending = false; - } - return m_lastResult; -} - -// ************************************************************* - -WindowsPipeSender::WindowsPipeSender() - : m_resultPending(false), m_lastResult(0) -{ - m_event.AttachHandle(CreateEvent(NULL, true, false, NULL), true); - CheckAndHandleError("CreateEvent", m_event.HandleValid()); - memset(&m_overlapped, 0, sizeof(m_overlapped)); - m_overlapped.hEvent = m_event; -} - -void WindowsPipeSender::Send(const byte* buf, size_t bufLen) -{ - DWORD written = 0; - HANDLE h = GetHandle(); - // don't queue too much at once, or we might use up non-paged memory - if (WriteFile(h, buf, UnsignedMin((DWORD)128*1024, bufLen), &written, &m_overlapped)) - { - m_resultPending = false; - m_lastResult = written; - } - else - { - if (GetLastError() != ERROR_IO_PENDING) - CheckAndHandleError("WriteFile", false); - - m_resultPending = true; - } -} - -void WindowsPipeSender::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) -{ - if (m_resultPending) - container.AddHandle(m_event, CallStack("WindowsPipeSender::GetWaitObjects() - result pending", &callStack)); - else - container.SetNoWait(CallStack("WindowsPipeSender::GetWaitObjects() - result ready", &callStack)); -} - -unsigned int WindowsPipeSender::GetSendResult() -{ - if (m_resultPending) - { - HANDLE h = GetHandle(); - BOOL result = GetOverlappedResult(h, &m_overlapped, &m_lastResult, false); - CheckAndHandleError("GetOverlappedResult", result); - m_resultPending = false; - } - return m_lastResult; -} - -NAMESPACE_END - -#endif diff --git a/CryptoPP/winpipes.h b/CryptoPP/winpipes.h deleted file mode 100644 index 07225f9f1..000000000 --- a/CryptoPP/winpipes.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef CRYPTOPP_WINPIPES_H -#define CRYPTOPP_WINPIPES_H - -#include "config.h" - -#ifdef WINDOWS_PIPES_AVAILABLE - -#include "network.h" -#include "queue.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -//! Windows Handle -class WindowsHandle -{ -public: - WindowsHandle(HANDLE h = INVALID_HANDLE_VALUE, bool own=false); - WindowsHandle(const WindowsHandle &h) : m_h(h.m_h), m_own(false) {} - virtual ~WindowsHandle(); - - bool GetOwnership() const {return m_own;} - void SetOwnership(bool own) {m_own = own;} - - operator HANDLE() {return m_h;} - HANDLE GetHandle() const {return m_h;} - bool HandleValid() const; - void AttachHandle(HANDLE h, bool own=false); - HANDLE DetachHandle(); - void CloseHandle(); - -protected: - virtual void HandleChanged() {} - - HANDLE m_h; - bool m_own; -}; - -//! Windows Pipe -class WindowsPipe -{ -public: - class Err : public OS_Error - { - public: - Err(HANDLE h, const std::string& operation, int error); - HANDLE GetHandle() const {return m_h;} - - private: - HANDLE m_h; - }; - -protected: - virtual HANDLE GetHandle() const =0; - virtual void HandleError(const char *operation) const; - void CheckAndHandleError(const char *operation, BOOL result) const - {assert(result==TRUE || result==FALSE); if (!result) HandleError(operation);} -}; - -//! pipe-based implementation of NetworkReceiver -class WindowsPipeReceiver : public WindowsPipe, public NetworkReceiver -{ -public: - WindowsPipeReceiver(); - - bool MustWaitForResult() {return true;} - bool Receive(byte* buf, size_t bufLen); - unsigned int GetReceiveResult(); - bool EofReceived() const {return m_eofReceived;} - - unsigned int GetMaxWaitObjectCount() const {return 1;} - void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); - -private: - WindowsHandle m_event; - OVERLAPPED m_overlapped; - bool m_resultPending; - DWORD m_lastResult; - bool m_eofReceived; -}; - -//! pipe-based implementation of NetworkSender -class WindowsPipeSender : public WindowsPipe, public NetworkSender -{ -public: - WindowsPipeSender(); - - bool MustWaitForResult() {return true;} - void Send(const byte* buf, size_t bufLen); - unsigned int GetSendResult(); - bool MustWaitForEof() { return false; } - void SendEof() {} - - unsigned int GetMaxWaitObjectCount() const {return 1;} - void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); - -private: - WindowsHandle m_event; - OVERLAPPED m_overlapped; - bool m_resultPending; - DWORD m_lastResult; -}; - -//! Windows Pipe Source -class WindowsPipeSource : public WindowsHandle, public NetworkSource, public WindowsPipeReceiver -{ -public: - WindowsPipeSource(HANDLE h=INVALID_HANDLE_VALUE, bool pumpAll=false, BufferedTransformation *attachment=NULL) - : WindowsHandle(h), NetworkSource(attachment) - { - if (pumpAll) - PumpAll(); - } - - NetworkSource::GetMaxWaitObjectCount; - NetworkSource::GetWaitObjects; - -private: - HANDLE GetHandle() const {return WindowsHandle::GetHandle();} - NetworkReceiver & AccessReceiver() {return *this;} -}; - -//! Windows Pipe Sink -class WindowsPipeSink : public WindowsHandle, public NetworkSink, public WindowsPipeSender -{ -public: - WindowsPipeSink(HANDLE h=INVALID_HANDLE_VALUE, unsigned int maxBufferSize=0, unsigned int autoFlushBound=16*1024) - : WindowsHandle(h), NetworkSink(maxBufferSize, autoFlushBound) {} - - NetworkSink::GetMaxWaitObjectCount; - NetworkSink::GetWaitObjects; - -private: - HANDLE GetHandle() const {return WindowsHandle::GetHandle();} - NetworkSender & AccessSender() {return *this;} -}; - -NAMESPACE_END - -#endif - -#endif diff --git a/CryptoPP/words.h b/CryptoPP/words.h deleted file mode 100644 index d5fda71da..000000000 --- a/CryptoPP/words.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef CRYPTOPP_WORDS_H -#define CRYPTOPP_WORDS_H - -#include "misc.h" - -NAMESPACE_BEGIN(CryptoPP) - -inline size_t CountWords(const word *X, size_t N) -{ - while (N && X[N-1]==0) - N--; - return N; -} - -inline void SetWords(word *r, word a, size_t n) -{ - for (size_t i=0; i> (WORD_BITS-shiftBits); - } - return carry; -} - -inline word ShiftWordsRightByBits(word *r, size_t n, unsigned int shiftBits) -{ - assert (shiftBits0; i--) - { - u = r[i-1]; - r[i-1] = (u >> shiftBits) | carry; - carry = u << (WORD_BITS-shiftBits); - } - return carry; -} - -inline void ShiftWordsLeftByWords(word *r, size_t n, size_t shiftWords) -{ - shiftWords = STDMIN(shiftWords, n); - if (shiftWords) - { - for (size_t i=n-1; i>=shiftWords; i--) - r[i] = r[i-shiftWords]; - SetWords(r, 0, shiftWords); - } -} - -inline void ShiftWordsRightByWords(word *r, size_t n, size_t shiftWords) -{ - shiftWords = STDMIN(shiftWords, n); - if (shiftWords) - { - for (size_t i=0; i+shiftWords
SQLITE_CONFIG_SQLLOG +**
This option is only available if sqlite is compiled with the +** SQLITE_ENABLE_SQLLOG pre-processor macro defined. The first argument should +** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int). +** The second should be of type (void*). The callback is invoked by the library +** in three separate circumstances, identified by the value passed as the +** fourth parameter. If the fourth parameter is 0, then the database connection +** passed as the second argument has just been opened. The third argument +** points to a buffer containing the name of the main database file. If the +** fourth parameter is 1, then the SQL statement that the third parameter +** points to has just been executed. Or, if the fourth parameter is 2, then +** the connection being passed as the second parameter is being closed. The +** third parameter is passed NULL In this case. +** +*/ +#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ +#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ +#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ +#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */ +#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ +#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ +#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ +#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ +#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ +/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ +#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ +#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +#define SQLITE_CONFIG_URI 17 /* int */ +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ +#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ + +/* +** CAPI3REF: Database Connection Configuration Options +** +** These constants are the available integer configuration options that +** can be passed as the second argument to the [sqlite3_db_config()] interface. +** +** New configuration options may be added in future releases of SQLite. +** Existing configuration options might be discontinued. Applications +** should check the return code from [sqlite3_db_config()] to make sure that +** the call worked. ^The [sqlite3_db_config()] interface will return a +** non-zero [error code] if a discontinued or unsupported configuration option +** is invoked. +** +**
+**
SQLITE_DBCONFIG_LOOKASIDE
+**
^This option takes three additional arguments that determine the +** [lookaside memory allocator] configuration for the [database connection]. +** ^The first argument (the third parameter to [sqlite3_db_config()] is a +** pointer to a memory buffer to use for lookaside memory. +** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb +** may be NULL in which case SQLite will allocate the +** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the +** size of each lookaside buffer slot. ^The third argument is the number of +** slots. The size of the buffer in the first argument must be greater than +** or equal to the product of the second and third arguments. The buffer +** must be aligned to an 8-byte boundary. ^If the second argument to +** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally +** rounded down to the next smaller multiple of 8. ^(The lookaside memory +** configuration for a database connection can only be changed when that +** connection is not currently using lookaside memory, or in other words +** when the "current value" returned by +** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. +** Any attempt to change the lookaside memory configuration when lookaside +** memory is in use leaves the configuration unchanged and returns +** [SQLITE_BUSY].)^
+** +**
SQLITE_DBCONFIG_ENABLE_FKEY
+**
^This option is used to enable or disable the enforcement of +** [foreign key constraints]. There should be two additional arguments. +** The first argument is an integer which is 0 to disable FK enforcement, +** positive to enable FK enforcement or negative to leave FK enforcement +** unchanged. The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether FK enforcement is off or on +** following this call. The second parameter may be a NULL pointer, in +** which case the FK enforcement setting is not reported back.
+** +**
SQLITE_DBCONFIG_ENABLE_TRIGGER
+**
^This option is used to enable or disable [CREATE TRIGGER | triggers]. +** There should be two additional arguments. +** The first argument is an integer which is 0 to disable triggers, +** positive to enable triggers or negative to leave the setting unchanged. +** The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether triggers are disabled or enabled +** following this call. The second parameter may be a NULL pointer, in +** which case the trigger setting is not reported back.
+** +**
+*/ +#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ + + +/* +** CAPI3REF: Enable Or Disable Extended Result Codes +** +** ^The sqlite3_extended_result_codes() routine enables or disables the +** [extended result codes] feature of SQLite. ^The extended result +** codes are disabled by default for historical compatibility. +*/ +SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); + +/* +** CAPI3REF: Last Insert Rowid +** +** ^Each entry in an SQLite table has a unique 64-bit signed +** integer key called the [ROWID | "rowid"]. ^The rowid is always available +** as an undeclared column named ROWID, OID, or _ROWID_ as long as those +** names are not also used by explicitly declared columns. ^If +** the table has a column of type [INTEGER PRIMARY KEY] then that column +** is another alias for the rowid. +** +** ^This routine returns the [rowid] of the most recent +** successful [INSERT] into the database from the [database connection] +** in the first argument. ^As of SQLite version 3.7.7, this routines +** records the last insert rowid of both ordinary tables and [virtual tables]. +** ^If no successful [INSERT]s +** have ever occurred on that database connection, zero is returned. +** +** ^(If an [INSERT] occurs within a trigger or within a [virtual table] +** method, then this routine will return the [rowid] of the inserted +** row as long as the trigger or virtual table method is running. +** But once the trigger or virtual table method ends, the value returned +** by this routine reverts to what it was before the trigger or virtual +** table method began.)^ +** +** ^An [INSERT] that fails due to a constraint violation is not a +** successful [INSERT] and does not change the value returned by this +** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, +** and INSERT OR ABORT make no changes to the return value of this +** routine when their insertion fails. ^(When INSERT OR REPLACE +** encounters a constraint violation, it does not fail. The +** INSERT continues to completion after deleting rows that caused +** the constraint problem so INSERT OR REPLACE will always change +** the return value of this interface.)^ +** +** ^For the purposes of this routine, an [INSERT] is considered to +** be successful even if it is subsequently rolled back. +** +** This function is accessible to SQL statements via the +** [last_insert_rowid() SQL function]. +** +** If a separate thread performs a new [INSERT] on the same +** database connection while the [sqlite3_last_insert_rowid()] +** function is running and thus changes the last insert [rowid], +** then the value returned by [sqlite3_last_insert_rowid()] is +** unpredictable and might not equal either the old or the new +** last insert [rowid]. +*/ +SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); + +/* +** CAPI3REF: Count The Number Of Rows Modified +** +** ^This function returns the number of database rows that were changed +** or inserted or deleted by the most recently completed SQL statement +** on the [database connection] specified by the first parameter. +** ^(Only changes that are directly specified by the [INSERT], [UPDATE], +** or [DELETE] statement are counted. Auxiliary changes caused by +** triggers or [foreign key actions] are not counted.)^ Use the +** [sqlite3_total_changes()] function to find the total number of changes +** including changes caused by triggers and foreign key actions. +** +** ^Changes to a view that are simulated by an [INSTEAD OF trigger] +** are not counted. Only real table changes are counted. +** +** ^(A "row change" is a change to a single row of a single table +** caused by an INSERT, DELETE, or UPDATE statement. Rows that +** are changed as side effects of [REPLACE] constraint resolution, +** rollback, ABORT processing, [DROP TABLE], or by any other +** mechanisms do not count as direct row changes.)^ +** +** A "trigger context" is a scope of execution that begins and +** ends with the script of a [CREATE TRIGGER | trigger]. +** Most SQL statements are +** evaluated outside of any trigger. This is the "top level" +** trigger context. If a trigger fires from the top level, a +** new trigger context is entered for the duration of that one +** trigger. Subtriggers create subcontexts for their duration. +** +** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does +** not create a new trigger context. +** +** ^This function returns the number of direct row changes in the +** most recent INSERT, UPDATE, or DELETE statement within the same +** trigger context. +** +** ^Thus, when called from the top level, this function returns the +** number of changes in the most recent INSERT, UPDATE, or DELETE +** that also occurred at the top level. ^(Within the body of a trigger, +** the sqlite3_changes() interface can be called to find the number of +** changes in the most recently completed INSERT, UPDATE, or DELETE +** statement within the body of the same trigger. +** However, the number returned does not include changes +** caused by subtriggers since those have their own context.)^ +** +** See also the [sqlite3_total_changes()] interface, the +** [count_changes pragma], and the [changes() SQL function]. +** +** If a separate thread makes changes on the same database connection +** while [sqlite3_changes()] is running then the value returned +** is unpredictable and not meaningful. +*/ +SQLITE_API int sqlite3_changes(sqlite3*); + +/* +** CAPI3REF: Total Number Of Rows Modified +** +** ^This function returns the number of row changes caused by [INSERT], +** [UPDATE] or [DELETE] statements since the [database connection] was opened. +** ^(The count returned by sqlite3_total_changes() includes all changes +** from all [CREATE TRIGGER | trigger] contexts and changes made by +** [foreign key actions]. However, +** the count does not include changes used to implement [REPLACE] constraints, +** do rollbacks or ABORT processing, or [DROP TABLE] processing. The +** count does not include rows of views that fire an [INSTEAD OF trigger], +** though if the INSTEAD OF trigger makes changes of its own, those changes +** are counted.)^ +** ^The sqlite3_total_changes() function counts the changes as soon as +** the statement that makes them is completed (when the statement handle +** is passed to [sqlite3_reset()] or [sqlite3_finalize()]). +** +** See also the [sqlite3_changes()] interface, the +** [count_changes pragma], and the [total_changes() SQL function]. +** +** If a separate thread makes changes on the same database connection +** while [sqlite3_total_changes()] is running then the value +** returned is unpredictable and not meaningful. +*/ +SQLITE_API int sqlite3_total_changes(sqlite3*); + +/* +** CAPI3REF: Interrupt A Long-Running Query +** +** ^This function causes any pending database operation to abort and +** return at its earliest opportunity. This routine is typically +** called in response to a user action such as pressing "Cancel" +** or Ctrl-C where the user wants a long query operation to halt +** immediately. +** +** ^It is safe to call this routine from a thread different from the +** thread that is currently running the database operation. But it +** is not safe to call this routine with a [database connection] that +** is closed or might close before sqlite3_interrupt() returns. +** +** ^If an SQL operation is very nearly finished at the time when +** sqlite3_interrupt() is called, then it might not have an opportunity +** to be interrupted and might continue to completion. +** +** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. +** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE +** that is inside an explicit transaction, then the entire transaction +** will be rolled back automatically. +** +** ^The sqlite3_interrupt(D) call is in effect until all currently running +** SQL statements on [database connection] D complete. ^Any new SQL statements +** that are started after the sqlite3_interrupt() call and before the +** running statements reaches zero are interrupted as if they had been +** running prior to the sqlite3_interrupt() call. ^New SQL statements +** that are started after the running statement count reaches zero are +** not effected by the sqlite3_interrupt(). +** ^A call to sqlite3_interrupt(D) that occurs when there are no running +** SQL statements is a no-op and has no effect on SQL statements +** that are started after the sqlite3_interrupt() call returns. +** +** If the database connection closes while [sqlite3_interrupt()] +** is running then bad things will likely happen. +*/ +SQLITE_API void sqlite3_interrupt(sqlite3*); + +/* +** CAPI3REF: Determine If An SQL Statement Is Complete +** +** These routines are useful during command-line input to determine if the +** currently entered text seems to form a complete SQL statement or +** if additional input is needed before sending the text into +** SQLite for parsing. ^These routines return 1 if the input string +** appears to be a complete SQL statement. ^A statement is judged to be +** complete if it ends with a semicolon token and is not a prefix of a +** well-formed CREATE TRIGGER statement. ^Semicolons that are embedded within +** string literals or quoted identifier names or comments are not +** independent tokens (they are part of the token in which they are +** embedded) and thus do not count as a statement terminator. ^Whitespace +** and comments that follow the final semicolon are ignored. +** +** ^These routines return 0 if the statement is incomplete. ^If a +** memory allocation fails, then SQLITE_NOMEM is returned. +** +** ^These routines do not parse the SQL statements thus +** will not detect syntactically incorrect SQL. +** +** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior +** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked +** automatically by sqlite3_complete16(). If that initialization fails, +** then the return value from sqlite3_complete16() will be non-zero +** regardless of whether or not the input SQL is complete.)^ +** +** The input to [sqlite3_complete()] must be a zero-terminated +** UTF-8 string. +** +** The input to [sqlite3_complete16()] must be a zero-terminated +** UTF-16 string in native byte order. +*/ +SQLITE_API int sqlite3_complete(const char *sql); +SQLITE_API int sqlite3_complete16(const void *sql); + +/* +** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors +** +** ^This routine sets a callback function that might be invoked whenever +** an attempt is made to open a database table that another thread +** or process has locked. +** +** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] +** is returned immediately upon encountering the lock. ^If the busy callback +** is not NULL, then the callback might be invoked with two arguments. +** +** ^The first argument to the busy handler is a copy of the void* pointer which +** is the third argument to sqlite3_busy_handler(). ^The second argument to +** the busy handler callback is the number of times that the busy handler has +** been invoked for this locking event. ^If the +** busy callback returns 0, then no additional attempts are made to +** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned. +** ^If the callback returns non-zero, then another attempt +** is made to open the database for reading and the cycle repeats. +** +** The presence of a busy handler does not guarantee that it will be invoked +** when there is lock contention. ^If SQLite determines that invoking the busy +** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] +** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler. +** Consider a scenario where one process is holding a read lock that +** it is trying to promote to a reserved lock and +** a second process is holding a reserved lock that it is trying +** to promote to an exclusive lock. The first process cannot proceed +** because it is blocked by the second and the second process cannot +** proceed because it is blocked by the first. If both processes +** invoke the busy handlers, neither will make any progress. Therefore, +** SQLite returns [SQLITE_BUSY] for the first process, hoping that this +** will induce the first process to release its read lock and allow +** the second process to proceed. +** +** ^The default busy callback is NULL. +** +** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] +** when SQLite is in the middle of a large transaction where all the +** changes will not fit into the in-memory cache. SQLite will +** already hold a RESERVED lock on the database file, but it needs +** to promote this lock to EXCLUSIVE so that it can spill cache +** pages into the database file without harm to concurrent +** readers. ^If it is unable to promote the lock, then the in-memory +** cache will be left in an inconsistent state and so the error +** code is promoted from the relatively benign [SQLITE_BUSY] to +** the more severe [SQLITE_IOERR_BLOCKED]. ^This error code promotion +** forces an automatic rollback of the changes. See the +** +** CorruptionFollowingBusyError wiki page for a discussion of why +** this is important. +** +** ^(There can only be a single busy handler defined for each +** [database connection]. Setting a new busy handler clears any +** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] +** will also set or clear the busy handler. +** +** The busy callback should not take any actions which modify the +** database connection that invoked the busy handler. Any such actions +** result in undefined behavior. +** +** A busy handler must not close the database connection +** or [prepared statement] that invoked the busy handler. +*/ +SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); + +/* +** CAPI3REF: Set A Busy Timeout +** +** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps +** for a specified amount of time when a table is locked. ^The handler +** will sleep multiple times until at least "ms" milliseconds of sleeping +** have accumulated. ^After at least "ms" milliseconds of sleeping, +** the handler returns 0 which causes [sqlite3_step()] to return +** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]. +** +** ^Calling this routine with an argument less than or equal to zero +** turns off all busy handlers. +** +** ^(There can only be a single busy handler for a particular +** [database connection] any any given moment. If another busy handler +** was defined (using [sqlite3_busy_handler()]) prior to calling +** this routine, that other busy handler is cleared.)^ +*/ +SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); + +/* +** CAPI3REF: Convenience Routines For Running Queries +** +** This is a legacy interface that is preserved for backwards compatibility. +** Use of this interface is not recommended. +** +** Definition: A result table is memory data structure created by the +** [sqlite3_get_table()] interface. A result table records the +** complete query results from one or more queries. +** +** The table conceptually has a number of rows and columns. But +** these numbers are not part of the result table itself. These +** numbers are obtained separately. Let N be the number of rows +** and M be the number of columns. +** +** A result table is an array of pointers to zero-terminated UTF-8 strings. +** There are (N+1)*M elements in the array. The first M pointers point +** to zero-terminated strings that contain the names of the columns. +** The remaining entries all point to query results. NULL values result +** in NULL pointers. All other values are in their UTF-8 zero-terminated +** string representation as returned by [sqlite3_column_text()]. +** +** A result table might consist of one or more memory allocations. +** It is not safe to pass a result table directly to [sqlite3_free()]. +** A result table should be deallocated using [sqlite3_free_table()]. +** +** ^(As an example of the result table format, suppose a query result +** is as follows: +** +**
+**        Name        | Age
+**        -----------------------
+**        Alice       | 43
+**        Bob         | 28
+**        Cindy       | 21
+** 
+** +** There are two column (M==2) and three rows (N==3). Thus the +** result table has 8 entries. Suppose the result table is stored +** in an array names azResult. Then azResult holds this content: +** +**
+**        azResult[0] = "Name";
+**        azResult[1] = "Age";
+**        azResult[2] = "Alice";
+**        azResult[3] = "43";
+**        azResult[4] = "Bob";
+**        azResult[5] = "28";
+**        azResult[6] = "Cindy";
+**        azResult[7] = "21";
+** 
)^ +** +** ^The sqlite3_get_table() function evaluates one or more +** semicolon-separated SQL statements in the zero-terminated UTF-8 +** string of its 2nd parameter and returns a result table to the +** pointer given in its 3rd parameter. +** +** After the application has finished with the result from sqlite3_get_table(), +** it must pass the result table pointer to sqlite3_free_table() in order to +** release the memory that was malloced. Because of the way the +** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling +** function must not try to call [sqlite3_free()] directly. Only +** [sqlite3_free_table()] is able to release the memory properly and safely. +** +** The sqlite3_get_table() interface is implemented as a wrapper around +** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access +** to any internal data structures of SQLite. It uses only the public +** interface defined here. As a consequence, errors that occur in the +** wrapper layer outside of the internal [sqlite3_exec()] call are not +** reflected in subsequent calls to [sqlite3_errcode()] or +** [sqlite3_errmsg()]. +*/ +SQLITE_API int sqlite3_get_table( + sqlite3 *db, /* An open database */ + const char *zSql, /* SQL to be evaluated */ + char ***pazResult, /* Results of the query */ + int *pnRow, /* Number of result rows written here */ + int *pnColumn, /* Number of result columns written here */ + char **pzErrmsg /* Error msg written here */ +); +SQLITE_API void sqlite3_free_table(char **result); + +/* +** CAPI3REF: Formatted String Printing Functions +** +** These routines are work-alikes of the "printf()" family of functions +** from the standard C library. +** +** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their +** results into memory obtained from [sqlite3_malloc()]. +** The strings returned by these two routines should be +** released by [sqlite3_free()]. ^Both routines return a +** NULL pointer if [sqlite3_malloc()] is unable to allocate enough +** memory to hold the resulting string. +** +** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from +** the standard C library. The result is written into the +** buffer supplied as the second parameter whose size is given by +** the first parameter. Note that the order of the +** first two parameters is reversed from snprintf().)^ This is an +** historical accident that cannot be fixed without breaking +** backwards compatibility. ^(Note also that sqlite3_snprintf() +** returns a pointer to its buffer instead of the number of +** characters actually written into the buffer.)^ We admit that +** the number of characters written would be a more useful return +** value but we cannot change the implementation of sqlite3_snprintf() +** now without breaking compatibility. +** +** ^As long as the buffer size is greater than zero, sqlite3_snprintf() +** guarantees that the buffer is always zero-terminated. ^The first +** parameter "n" is the total size of the buffer, including space for +** the zero terminator. So the longest string that can be completely +** written will be n-1 characters. +** +** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). +** +** These routines all implement some additional formatting +** options that are useful for constructing SQL statements. +** All of the usual printf() formatting options apply. In addition, there +** is are "%q", "%Q", and "%z" options. +** +** ^(The %q option works like %s in that it substitutes a nul-terminated +** string from the argument list. But %q also doubles every '\'' character. +** %q is designed for use inside a string literal.)^ By doubling each '\'' +** character it escapes that character and allows it to be inserted into +** the string. +** +** For example, assume the string variable zText contains text as follows: +** +**
+**  char *zText = "It's a happy day!";
+** 
+** +** One can use this text in an SQL statement as follows: +** +**
+**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
+**  sqlite3_exec(db, zSQL, 0, 0, 0);
+**  sqlite3_free(zSQL);
+** 
+** +** Because the %q format string is used, the '\'' character in zText +** is escaped and the SQL generated is as follows: +** +**
+**  INSERT INTO table1 VALUES('It''s a happy day!')
+** 
+** +** This is correct. Had we used %s instead of %q, the generated SQL +** would have looked like this: +** +**
+**  INSERT INTO table1 VALUES('It's a happy day!');
+** 
+** +** This second example is an SQL syntax error. As a general rule you should +** always use %q instead of %s when inserting text into a string literal. +** +** ^(The %Q option works like %q except it also adds single quotes around +** the outside of the total string. Additionally, if the parameter in the +** argument list is a NULL pointer, %Q substitutes the text "NULL" (without +** single quotes).)^ So, for example, one could say: +** +**
+**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
+**  sqlite3_exec(db, zSQL, 0, 0, 0);
+**  sqlite3_free(zSQL);
+** 
+** +** The code above will render a correct SQL statement in the zSQL +** variable even if the zText variable is a NULL pointer. +** +** ^(The "%z" formatting option works like "%s" but with the +** addition that after the string has been read and copied into +** the result, [sqlite3_free()] is called on the input string.)^ +*/ +SQLITE_API char *sqlite3_mprintf(const char*,...); +SQLITE_API char *sqlite3_vmprintf(const char*, va_list); +SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); + +/* +** CAPI3REF: Memory Allocation Subsystem +** +** The SQLite core uses these three routines for all of its own +** internal memory allocation needs. "Core" in the previous sentence +** does not include operating-system specific VFS implementation. The +** Windows VFS uses native malloc() and free() for some operations. +** +** ^The sqlite3_malloc() routine returns a pointer to a block +** of memory at least N bytes in length, where N is the parameter. +** ^If sqlite3_malloc() is unable to obtain sufficient free +** memory, it returns a NULL pointer. ^If the parameter N to +** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns +** a NULL pointer. +** +** ^Calling sqlite3_free() with a pointer previously returned +** by sqlite3_malloc() or sqlite3_realloc() releases that memory so +** that it might be reused. ^The sqlite3_free() routine is +** a no-op if is called with a NULL pointer. Passing a NULL pointer +** to sqlite3_free() is harmless. After being freed, memory +** should neither be read nor written. Even reading previously freed +** memory might result in a segmentation fault or other severe error. +** Memory corruption, a segmentation fault, or other severe error +** might result if sqlite3_free() is called with a non-NULL pointer that +** was not obtained from sqlite3_malloc() or sqlite3_realloc(). +** +** ^(The sqlite3_realloc() interface attempts to resize a +** prior memory allocation to be at least N bytes, where N is the +** second parameter. The memory allocation to be resized is the first +** parameter.)^ ^ If the first parameter to sqlite3_realloc() +** is a NULL pointer then its behavior is identical to calling +** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc(). +** ^If the second parameter to sqlite3_realloc() is zero or +** negative then the behavior is exactly the same as calling +** sqlite3_free(P) where P is the first parameter to sqlite3_realloc(). +** ^sqlite3_realloc() returns a pointer to a memory allocation +** of at least N bytes in size or NULL if sufficient memory is unavailable. +** ^If M is the size of the prior allocation, then min(N,M) bytes +** of the prior allocation are copied into the beginning of buffer returned +** by sqlite3_realloc() and the prior allocation is freed. +** ^If sqlite3_realloc() returns NULL, then the prior allocation +** is not freed. +** +** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() +** is always aligned to at least an 8 byte boundary, or to a +** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time +** option is used. +** +** In SQLite version 3.5.0 and 3.5.1, it was possible to define +** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in +** implementation of these routines to be omitted. That capability +** is no longer provided. Only built-in memory allocators can be used. +** +** Prior to SQLite version 3.7.10, the Windows OS interface layer called +** the system malloc() and free() directly when converting +** filenames between the UTF-8 encoding used by SQLite +** and whatever filename encoding is used by the particular Windows +** installation. Memory allocation errors were detected, but +** they were reported back as [SQLITE_CANTOPEN] or +** [SQLITE_IOERR] rather than [SQLITE_NOMEM]. +** +** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] +** must be either NULL or else pointers obtained from a prior +** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have +** not yet been released. +** +** The application must not read or write any part of +** a block of memory after it has been released using +** [sqlite3_free()] or [sqlite3_realloc()]. +*/ +SQLITE_API void *sqlite3_malloc(int); +SQLITE_API void *sqlite3_realloc(void*, int); +SQLITE_API void sqlite3_free(void*); + +/* +** CAPI3REF: Memory Allocator Statistics +** +** SQLite provides these two interfaces for reporting on the status +** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] +** routines, which form the built-in memory allocation subsystem. +** +** ^The [sqlite3_memory_used()] routine returns the number of bytes +** of memory currently outstanding (malloced but not freed). +** ^The [sqlite3_memory_highwater()] routine returns the maximum +** value of [sqlite3_memory_used()] since the high-water mark +** was last reset. ^The values returned by [sqlite3_memory_used()] and +** [sqlite3_memory_highwater()] include any overhead +** added by SQLite in its implementation of [sqlite3_malloc()], +** but not overhead added by the any underlying system library +** routines that [sqlite3_malloc()] may call. +** +** ^The memory high-water mark is reset to the current value of +** [sqlite3_memory_used()] if and only if the parameter to +** [sqlite3_memory_highwater()] is true. ^The value returned +** by [sqlite3_memory_highwater(1)] is the high-water mark +** prior to the reset. +*/ +SQLITE_API sqlite3_int64 sqlite3_memory_used(void); +SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); + +/* +** CAPI3REF: Pseudo-Random Number Generator +** +** SQLite contains a high-quality pseudo-random number generator (PRNG) used to +** select random [ROWID | ROWIDs] when inserting new records into a table that +** already uses the largest possible [ROWID]. The PRNG is also used for +** the build-in random() and randomblob() SQL functions. This interface allows +** applications to access the same PRNG for other purposes. +** +** ^A call to this routine stores N bytes of randomness into buffer P. +** +** ^The first time this routine is invoked (either internally or by +** the application) the PRNG is seeded using randomness obtained +** from the xRandomness method of the default [sqlite3_vfs] object. +** ^On all subsequent invocations, the pseudo-randomness is generated +** internally and without recourse to the [sqlite3_vfs] xRandomness +** method. +*/ +SQLITE_API void sqlite3_randomness(int N, void *P); + +/* +** CAPI3REF: Compile-Time Authorization Callbacks +** +** ^This routine registers an authorizer callback with a particular +** [database connection], supplied in the first argument. +** ^The authorizer callback is invoked as SQL statements are being compiled +** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], +** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. ^At various +** points during the compilation process, as logic is being created +** to perform various actions, the authorizer callback is invoked to +** see if those actions are allowed. ^The authorizer callback should +** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the +** specific action but allow the SQL statement to continue to be +** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be +** rejected with an error. ^If the authorizer callback returns +** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY] +** then the [sqlite3_prepare_v2()] or equivalent call that triggered +** the authorizer will fail with an error message. +** +** When the callback returns [SQLITE_OK], that means the operation +** requested is ok. ^When the callback returns [SQLITE_DENY], the +** [sqlite3_prepare_v2()] or equivalent call that triggered the +** authorizer will fail with an error message explaining that +** access is denied. +** +** ^The first parameter to the authorizer callback is a copy of the third +** parameter to the sqlite3_set_authorizer() interface. ^The second parameter +** to the callback is an integer [SQLITE_COPY | action code] that specifies +** the particular action to be authorized. ^The third through sixth parameters +** to the callback are zero-terminated strings that contain additional +** details about the action to be authorized. +** +** ^If the action code is [SQLITE_READ] +** and the callback returns [SQLITE_IGNORE] then the +** [prepared statement] statement is constructed to substitute +** a NULL value in place of the table column that would have +** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE] +** return can be used to deny an untrusted user access to individual +** columns of a table. +** ^If the action code is [SQLITE_DELETE] and the callback returns +** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the +** [truncate optimization] is disabled and all rows are deleted individually. +** +** An authorizer is used when [sqlite3_prepare | preparing] +** SQL statements from an untrusted source, to ensure that the SQL statements +** do not try to access data they are not allowed to see, or that they do not +** try to execute malicious statements that damage the database. For +** example, an application may allow a user to enter arbitrary +** SQL queries for evaluation by a database. But the application does +** not want the user to be able to make arbitrary changes to the +** database. An authorizer could then be put in place while the +** user-entered SQL is being [sqlite3_prepare | prepared] that +** disallows everything except [SELECT] statements. +** +** Applications that need to process SQL from untrusted sources +** might also consider lowering resource limits using [sqlite3_limit()] +** and limiting database size using the [max_page_count] [PRAGMA] +** in addition to using an authorizer. +** +** ^(Only a single authorizer can be in place on a database connection +** at a time. Each call to sqlite3_set_authorizer overrides the +** previous call.)^ ^Disable the authorizer by installing a NULL callback. +** The authorizer is disabled by default. +** +** The authorizer callback must not do anything that will modify +** the database connection that invoked the authorizer callback. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the +** statement might be re-prepared during [sqlite3_step()] due to a +** schema change. Hence, the application should ensure that the +** correct authorizer callback remains in place during the [sqlite3_step()]. +** +** ^Note that the authorizer callback is invoked only during +** [sqlite3_prepare()] or its variants. Authorization is not +** performed during statement evaluation in [sqlite3_step()], unless +** as stated in the previous paragraph, sqlite3_step() invokes +** sqlite3_prepare_v2() to reprepare a statement after a schema change. +*/ +SQLITE_API int sqlite3_set_authorizer( + sqlite3*, + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), + void *pUserData +); + +/* +** CAPI3REF: Authorizer Return Codes +** +** The [sqlite3_set_authorizer | authorizer callback function] must +** return either [SQLITE_OK] or one of these two constants in order +** to signal SQLite whether or not the action is permitted. See the +** [sqlite3_set_authorizer | authorizer documentation] for additional +** information. +** +** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code] +** from the [sqlite3_vtab_on_conflict()] interface. +*/ +#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ +#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ + +/* +** CAPI3REF: Authorizer Action Codes +** +** The [sqlite3_set_authorizer()] interface registers a callback function +** that is invoked to authorize certain SQL statement actions. The +** second parameter to the callback is an integer code that specifies +** what action is being authorized. These are the integer action codes that +** the authorizer callback may be passed. +** +** These action code values signify what kind of operation is to be +** authorized. The 3rd and 4th parameters to the authorization +** callback function will be parameters or NULL depending on which of these +** codes is used as the second parameter. ^(The 5th parameter to the +** authorizer callback is the name of the database ("main", "temp", +** etc.) if applicable.)^ ^The 6th parameter to the authorizer callback +** is the name of the inner-most trigger or view that is responsible for +** the access attempt or NULL if this access attempt is directly from +** top-level SQL code. +*/ +/******************************************* 3rd ************ 4th ***********/ +#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ +#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ +#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ +#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ +#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ +#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ +#define SQLITE_DELETE 9 /* Table Name NULL */ +#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ +#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ +#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ +#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ +#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ +#define SQLITE_DROP_VIEW 17 /* View Name NULL */ +#define SQLITE_INSERT 18 /* Table Name NULL */ +#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ +#define SQLITE_READ 20 /* Table Name Column Name */ +#define SQLITE_SELECT 21 /* NULL NULL */ +#define SQLITE_TRANSACTION 22 /* Operation NULL */ +#define SQLITE_UPDATE 23 /* Table Name Column Name */ +#define SQLITE_ATTACH 24 /* Filename NULL */ +#define SQLITE_DETACH 25 /* Database Name NULL */ +#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */ +#define SQLITE_REINDEX 27 /* Index Name NULL */ +#define SQLITE_ANALYZE 28 /* Table Name NULL */ +#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ +#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ +#define SQLITE_FUNCTION 31 /* NULL Function Name */ +#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ +#define SQLITE_COPY 0 /* No longer used */ + +/* +** CAPI3REF: Tracing And Profiling Functions +** +** These routines register callback functions that can be used for +** tracing and profiling the execution of SQL statements. +** +** ^The callback function registered by sqlite3_trace() is invoked at +** various times when an SQL statement is being run by [sqlite3_step()]. +** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the +** SQL statement text as the statement first begins executing. +** ^(Additional sqlite3_trace() callbacks might occur +** as each triggered subprogram is entered. The callbacks for triggers +** contain a UTF-8 SQL comment that identifies the trigger.)^ +** +** ^The callback function registered by sqlite3_profile() is invoked +** as each SQL statement finishes. ^The profile callback contains +** the original statement text and an estimate of wall-clock time +** of how long that statement took to run. ^The profile callback +** time is in units of nanoseconds, however the current implementation +** is only capable of millisecond resolution so the six least significant +** digits in the time are meaningless. Future versions of SQLite +** might provide greater resolution on the profiler callback. The +** sqlite3_profile() function is considered experimental and is +** subject to change in future versions of SQLite. +*/ +SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); +SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, + void(*xProfile)(void*,const char*,sqlite3_uint64), void*); + +/* +** CAPI3REF: Query Progress Callbacks +** +** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback +** function X to be invoked periodically during long running calls to +** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for +** database connection D. An example use for this +** interface is to keep a GUI updated during a large query. +** +** ^The parameter P is passed through as the only parameter to the +** callback function X. ^The parameter N is the number of +** [virtual machine instructions] that are evaluated between successive +** invocations of the callback X. +** +** ^Only a single progress handler may be defined at one time per +** [database connection]; setting a new progress handler cancels the +** old one. ^Setting parameter X to NULL disables the progress handler. +** ^The progress handler is also disabled by setting N to a value less +** than 1. +** +** ^If the progress callback returns non-zero, the operation is +** interrupted. This feature can be used to implement a +** "Cancel" button on a GUI progress dialog box. +** +** The progress handler callback must not do anything that will modify +** the database connection that invoked the progress handler. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +*/ +SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + +/* +** CAPI3REF: Opening A New Database Connection +** +** ^These routines open an SQLite database file as specified by the +** filename argument. ^The filename argument is interpreted as UTF-8 for +** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte +** order for sqlite3_open16(). ^(A [database connection] handle is usually +** returned in *ppDb, even if an error occurs. The only exception is that +** if SQLite is unable to allocate memory to hold the [sqlite3] object, +** a NULL will be written into *ppDb instead of a pointer to the [sqlite3] +** object.)^ ^(If the database is opened (and/or created) successfully, then +** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The +** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain +** an English language description of the error following a failure of any +** of the sqlite3_open() routines. +** +** ^The default encoding for the database will be UTF-8 if +** sqlite3_open() or sqlite3_open_v2() is called and +** UTF-16 in the native byte order if sqlite3_open16() is used. +** +** Whether or not an error occurs when it is opened, resources +** associated with the [database connection] handle should be released by +** passing it to [sqlite3_close()] when it is no longer required. +** +** The sqlite3_open_v2() interface works like sqlite3_open() +** except that it accepts two additional parameters for additional control +** over the new database connection. ^(The flags parameter to +** sqlite3_open_v2() can take one of +** the following three values, optionally combined with the +** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE], +** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^ +** +**
+** ^(
[SQLITE_OPEN_READONLY]
+**
The database is opened in read-only mode. If the database does not +** already exist, an error is returned.
)^ +** +** ^(
[SQLITE_OPEN_READWRITE]
+**
The database is opened for reading and writing if possible, or reading +** only if the file is write protected by the operating system. In either +** case the database must already exist, otherwise an error is returned.
)^ +** +** ^(
[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
+**
The database is opened for reading and writing, and is created if +** it does not already exist. This is the behavior that is always used for +** sqlite3_open() and sqlite3_open16().
)^ +**
+** +** If the 3rd parameter to sqlite3_open_v2() is not one of the +** combinations shown above optionally combined with other +** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] +** then the behavior is undefined. +** +** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection +** opens in the multi-thread [threading mode] as long as the single-thread +** mode has not been set at compile-time or start-time. ^If the +** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens +** in the serialized [threading mode] unless single-thread was +** previously selected at compile-time or start-time. +** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be +** eligible to use [shared cache mode], regardless of whether or not shared +** cache is enabled using [sqlite3_enable_shared_cache()]. ^The +** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not +** participate in [shared cache mode] even if it is enabled. +** +** ^The fourth parameter to sqlite3_open_v2() is the name of the +** [sqlite3_vfs] object that defines the operating system interface that +** the new database connection should use. ^If the fourth parameter is +** a NULL pointer then the default [sqlite3_vfs] object is used. +** +** ^If the filename is ":memory:", then a private, temporary in-memory database +** is created for the connection. ^This in-memory database will vanish when +** the database connection is closed. Future versions of SQLite might +** make use of additional special filenames that begin with the ":" character. +** It is recommended that when a database filename actually does begin with +** a ":" character you should prefix the filename with a pathname such as +** "./" to avoid ambiguity. +** +** ^If the filename is an empty string, then a private, temporary +** on-disk database will be created. ^This private database will be +** automatically deleted as soon as the database connection is closed. +** +** [[URI filenames in sqlite3_open()]]

URI Filenames

+** +** ^If [URI filename] interpretation is enabled, and the filename argument +** begins with "file:", then the filename is interpreted as a URI. ^URI +** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is +** set in the fourth argument to sqlite3_open_v2(), or if it has +** been enabled globally using the [SQLITE_CONFIG_URI] option with the +** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. +** As of SQLite version 3.7.7, URI filename interpretation is turned off +** by default, but future releases of SQLite might enable URI filename +** interpretation by default. See "[URI filenames]" for additional +** information. +** +** URI filenames are parsed according to RFC 3986. ^If the URI contains an +** authority, then it must be either an empty string or the string +** "localhost". ^If the authority is not an empty string or "localhost", an +** error is returned to the caller. ^The fragment component of a URI, if +** present, is ignored. +** +** ^SQLite uses the path component of the URI as the name of the disk file +** which contains the database. ^If the path begins with a '/' character, +** then it is interpreted as an absolute path. ^If the path does not begin +** with a '/' (meaning that the authority section is omitted from the URI) +** then the path is interpreted as a relative path. +** ^On windows, the first component of an absolute path +** is a drive specification (e.g. "C:"). +** +** [[core URI query parameters]] +** The query component of a URI may contain parameters that are interpreted +** either by SQLite itself, or by a [VFS | custom VFS implementation]. +** SQLite interprets the following three query parameters: +** +**
    +**
  • vfs: ^The "vfs" parameter may be used to specify the name of +** a VFS object that provides the operating system interface that should +** be used to access the database file on disk. ^If this option is set to +** an empty string the default VFS object is used. ^Specifying an unknown +** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is +** present, then the VFS specified by the option takes precedence over +** the value passed as the fourth parameter to sqlite3_open_v2(). +** +**
  • mode: ^(The mode parameter may be set to either "ro", "rw", +** "rwc", or "memory". Attempting to set it to any other value is +** an error)^. +** ^If "ro" is specified, then the database is opened for read-only +** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the +** third argument to sqlite3_open_v2(). ^If the mode option is set to +** "rw", then the database is opened for read-write (but not create) +** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had +** been set. ^Value "rwc" is equivalent to setting both +** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is +** set to "memory" then a pure [in-memory database] that never reads +** or writes from disk is used. ^It is an error to specify a value for +** the mode parameter that is less restrictive than that specified by +** the flags passed in the third parameter to sqlite3_open_v2(). +** +**
  • cache: ^The cache parameter may be set to either "shared" or +** "private". ^Setting it to "shared" is equivalent to setting the +** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to +** sqlite3_open_v2(). ^Setting the cache parameter to "private" is +** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. +** ^If sqlite3_open_v2() is used and the "cache" parameter is present in +** a URI filename, its value overrides any behavior requested by setting +** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. +**
+** +** ^Specifying an unknown parameter in the query component of a URI is not an +** error. Future versions of SQLite might understand additional query +** parameters. See "[query parameters with special meaning to SQLite]" for +** additional information. +** +** [[URI filename examples]]

URI filename examples

+** +** +**
URI filenames Results +**
file:data.db +** Open the file "data.db" in the current directory. +**
file:/home/fred/data.db
+** file:///home/fred/data.db
+** file://localhost/home/fred/data.db
+** Open the database file "/home/fred/data.db". +**
file://darkstar/home/fred/data.db +** An error. "darkstar" is not a recognized authority. +**
+** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db +** Windows only: Open the file "data.db" on fred's desktop on drive +** C:. Note that the %20 escaping in this example is not strictly +** necessary - space characters can be used literally +** in URI filenames. +**
file:data.db?mode=ro&cache=private +** Open file "data.db" in the current directory for read-only access. +** Regardless of whether or not shared-cache mode is enabled by +** default, use a private cache. +**
file:/home/fred/data.db?vfs=unix-nolock +** Open file "/home/fred/data.db". Use the special VFS "unix-nolock". +**
file:data.db?mode=readonly +** An error. "readonly" is not a valid option for the "mode" parameter. +**
+** +** ^URI hexadecimal escape sequences (%HH) are supported within the path and +** query components of a URI. A hexadecimal escape sequence consists of a +** percent sign - "%" - followed by exactly two hexadecimal digits +** specifying an octet value. ^Before the path or query components of a +** URI filename are interpreted, they are encoded using UTF-8 and all +** hexadecimal escape sequences replaced by a single byte containing the +** corresponding octet. If this process generates an invalid UTF-8 encoding, +** the results are undefined. +** +** Note to Windows users: The encoding used for the filename argument +** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever +** codepage is currently defined. Filenames containing international +** characters must be converted to UTF-8 prior to passing them into +** sqlite3_open() or sqlite3_open_v2(). +** +** Note to Windows Runtime users: The temporary directory must be set +** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various +** features that require the use of temporary files may fail. +** +** See also: [sqlite3_temp_directory] +*/ +SQLITE_API int sqlite3_open( + const char *filename, /* Database filename (UTF-8) */ + sqlite3 **ppDb /* OUT: SQLite db handle */ +); +SQLITE_API int sqlite3_open16( + const void *filename, /* Database filename (UTF-16) */ + sqlite3 **ppDb /* OUT: SQLite db handle */ +); +SQLITE_API int sqlite3_open_v2( + const char *filename, /* Database filename (UTF-8) */ + sqlite3 **ppDb, /* OUT: SQLite db handle */ + int flags, /* Flags */ + const char *zVfs /* Name of VFS module to use */ +); + +/* +** CAPI3REF: Obtain Values For URI Parameters +** +** These are utility routines, useful to VFS implementations, that check +** to see if a database file was a URI that contained a specific query +** parameter, and if so obtains the value of that query parameter. +** +** If F is the database filename pointer passed into the xOpen() method of +** a VFS implementation when the flags parameter to xOpen() has one or +** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and +** P is the name of the query parameter, then +** sqlite3_uri_parameter(F,P) returns the value of the P +** parameter if it exists or a NULL pointer if P does not appear as a +** query parameter on F. If P is a query parameter of F +** has no explicit value, then sqlite3_uri_parameter(F,P) returns +** a pointer to an empty string. +** +** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean +** parameter and returns true (1) or false (0) according to the value +** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the +** value of query parameter P is one of "yes", "true", or "on" in any +** case or if the value begins with a non-zero number. The +** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of +** query parameter P is one of "no", "false", or "off" in any case or +** if the value begins with a numeric zero. If P is not a query +** parameter on F or if the value of P is does not match any of the +** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0). +** +** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a +** 64-bit signed integer and returns that integer, or D if P does not +** exist. If the value of P is something other than an integer, then +** zero is returned. +** +** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and +** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and +** is not a database file pathname pointer that SQLite passed into the xOpen +** VFS method, then the behavior of this routine is undefined and probably +** undesirable. +*/ +SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); +SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); + + +/* +** CAPI3REF: Error Codes And Messages +** +** ^The sqlite3_errcode() interface returns the numeric [result code] or +** [extended result code] for the most recent failed sqlite3_* API call +** associated with a [database connection]. If a prior API call failed +** but the most recent API call succeeded, the return value from +** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() +** interface is the same except that it always returns the +** [extended result code] even when extended result codes are +** disabled. +** +** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language +** text that describes the error, as either UTF-8 or UTF-16 respectively. +** ^(Memory to hold the error message string is managed internally. +** The application does not need to worry about freeing the result. +** However, the error string might be overwritten or deallocated by +** subsequent calls to other SQLite interface functions.)^ +** +** ^The sqlite3_errstr() interface returns the English-language text +** that describes the [result code], as UTF-8. +** ^(Memory to hold the error message string is managed internally +** and must not be freed by the application)^. +** +** When the serialized [threading mode] is in use, it might be the +** case that a second error occurs on a separate thread in between +** the time of the first error and the call to these interfaces. +** When that happens, the second error will be reported since these +** interfaces always report the most recent result. To avoid +** this, each thread can obtain exclusive use of the [database connection] D +** by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning +** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after +** all calls to the interfaces listed here are completed. +** +** If an interface fails with SQLITE_MISUSE, that means the interface +** was invoked incorrectly by the application. In that case, the +** error code and message may or may not be set. +*/ +SQLITE_API int sqlite3_errcode(sqlite3 *db); +SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); +SQLITE_API const char *sqlite3_errmsg(sqlite3*); +SQLITE_API const void *sqlite3_errmsg16(sqlite3*); +SQLITE_API const char *sqlite3_errstr(int); + +/* +** CAPI3REF: SQL Statement Object +** KEYWORDS: {prepared statement} {prepared statements} +** +** An instance of this object represents a single SQL statement. +** This object is variously known as a "prepared statement" or a +** "compiled SQL statement" or simply as a "statement". +** +** The life of a statement object goes something like this: +** +**
    +**
  1. Create the object using [sqlite3_prepare_v2()] or a related +** function. +**
  2. Bind values to [host parameters] using the sqlite3_bind_*() +** interfaces. +**
  3. Run the SQL by calling [sqlite3_step()] one or more times. +**
  4. Reset the statement using [sqlite3_reset()] then go back +** to step 2. Do this zero or more times. +**
  5. Destroy the object using [sqlite3_finalize()]. +**
+** +** Refer to documentation on individual methods above for additional +** information. +*/ +typedef struct sqlite3_stmt sqlite3_stmt; + +/* +** CAPI3REF: Run-time Limits +** +** ^(This interface allows the size of various constructs to be limited +** on a connection by connection basis. The first parameter is the +** [database connection] whose limit is to be set or queried. The +** second parameter is one of the [limit categories] that define a +** class of constructs to be size limited. The third parameter is the +** new limit for that construct.)^ +** +** ^If the new limit is a negative number, the limit is unchanged. +** ^(For each limit category SQLITE_LIMIT_NAME there is a +** [limits | hard upper bound] +** set at compile-time by a C preprocessor macro called +** [limits | SQLITE_MAX_NAME]. +** (The "_LIMIT_" in the name is changed to "_MAX_".))^ +** ^Attempts to increase a limit above its hard upper bound are +** silently truncated to the hard upper bound. +** +** ^Regardless of whether or not the limit was changed, the +** [sqlite3_limit()] interface returns the prior value of the limit. +** ^Hence, to find the current value of a limit without changing it, +** simply invoke this interface with the third parameter set to -1. +** +** Run-time limits are intended for use in applications that manage +** both their own internal database and also databases that are controlled +** by untrusted external sources. An example application might be a +** web browser that has its own databases for storing history and +** separate databases controlled by JavaScript applications downloaded +** off the Internet. The internal databases can be given the +** large, default limits. Databases managed by external sources can +** be given much smaller limits designed to prevent a denial of service +** attack. Developers might also want to use the [sqlite3_set_authorizer()] +** interface to further control untrusted SQL. The size of the database +** created by an untrusted script can be contained using the +** [max_page_count] [PRAGMA]. +** +** New run-time limit categories may be added in future releases. +*/ +SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); + +/* +** CAPI3REF: Run-Time Limit Categories +** KEYWORDS: {limit category} {*limit categories} +** +** These constants define various performance limits +** that can be lowered at run-time using [sqlite3_limit()]. +** The synopsis of the meanings of the various limits is shown below. +** Additional information is available at [limits | Limits in SQLite]. +** +**
+** [[SQLITE_LIMIT_LENGTH]] ^(
SQLITE_LIMIT_LENGTH
+**
The maximum size of any string or BLOB or table row, in bytes.
)^ +** +** [[SQLITE_LIMIT_SQL_LENGTH]] ^(
SQLITE_LIMIT_SQL_LENGTH
+**
The maximum length of an SQL statement, in bytes.
)^ +** +** [[SQLITE_LIMIT_COLUMN]] ^(
SQLITE_LIMIT_COLUMN
+**
The maximum number of columns in a table definition or in the +** result set of a [SELECT] or the maximum number of columns in an index +** or in an ORDER BY or GROUP BY clause.
)^ +** +** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(
SQLITE_LIMIT_EXPR_DEPTH
+**
The maximum depth of the parse tree on any expression.
)^ +** +** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(
SQLITE_LIMIT_COMPOUND_SELECT
+**
The maximum number of terms in a compound SELECT statement.
)^ +** +** [[SQLITE_LIMIT_VDBE_OP]] ^(
SQLITE_LIMIT_VDBE_OP
+**
The maximum number of instructions in a virtual machine program +** used to implement an SQL statement. This limit is not currently +** enforced, though that might be added in some future release of +** SQLite.
)^ +** +** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(
SQLITE_LIMIT_FUNCTION_ARG
+**
The maximum number of arguments on a function.
)^ +** +** [[SQLITE_LIMIT_ATTACHED]] ^(
SQLITE_LIMIT_ATTACHED
+**
The maximum number of [ATTACH | attached databases].)^
+** +** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]] +** ^(
SQLITE_LIMIT_LIKE_PATTERN_LENGTH
+**
The maximum length of the pattern argument to the [LIKE] or +** [GLOB] operators.
)^ +** +** [[SQLITE_LIMIT_VARIABLE_NUMBER]] +** ^(
SQLITE_LIMIT_VARIABLE_NUMBER
+**
The maximum index number of any [parameter] in an SQL statement.)^ +** +** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(
SQLITE_LIMIT_TRIGGER_DEPTH
+**
The maximum depth of recursion for triggers.
)^ +**
+*/ +#define SQLITE_LIMIT_LENGTH 0 +#define SQLITE_LIMIT_SQL_LENGTH 1 +#define SQLITE_LIMIT_COLUMN 2 +#define SQLITE_LIMIT_EXPR_DEPTH 3 +#define SQLITE_LIMIT_COMPOUND_SELECT 4 +#define SQLITE_LIMIT_VDBE_OP 5 +#define SQLITE_LIMIT_FUNCTION_ARG 6 +#define SQLITE_LIMIT_ATTACHED 7 +#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 +#define SQLITE_LIMIT_VARIABLE_NUMBER 9 +#define SQLITE_LIMIT_TRIGGER_DEPTH 10 + +/* +** CAPI3REF: Compiling An SQL Statement +** KEYWORDS: {SQL statement compiler} +** +** To execute an SQL query, it must first be compiled into a byte-code +** program using one of these routines. +** +** The first argument, "db", is a [database connection] obtained from a +** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or +** [sqlite3_open16()]. The database connection must not have been closed. +** +** The second argument, "zSql", is the statement to be compiled, encoded +** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2() +** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() +** use UTF-16. +** +** ^If the nByte argument is less than zero, then zSql is read up to the +** first zero terminator. ^If nByte is non-negative, then it is the maximum +** number of bytes read from zSql. ^When nByte is non-negative, the +** zSql string ends at either the first '\000' or '\u0000' character or +** the nByte-th byte, whichever comes first. If the caller knows +** that the supplied string is nul-terminated, then there is a small +** performance advantage to be gained by passing an nByte parameter that +** is equal to the number of bytes in the input string including +** the nul-terminator bytes as this saves SQLite from having to +** make a copy of the input string. +** +** ^If pzTail is not NULL then *pzTail is made to point to the first byte +** past the end of the first SQL statement in zSql. These routines only +** compile the first statement in zSql, so *pzTail is left pointing to +** what remains uncompiled. +** +** ^*ppStmt is left pointing to a compiled [prepared statement] that can be +** executed using [sqlite3_step()]. ^If there is an error, *ppStmt is set +** to NULL. ^If the input text contains no SQL (if the input is an empty +** string or a comment) then *ppStmt is set to NULL. +** The calling procedure is responsible for deleting the compiled +** SQL statement using [sqlite3_finalize()] after it has finished with it. +** ppStmt may not be NULL. +** +** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; +** otherwise an [error code] is returned. +** +** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are +** recommended for all new programs. The two older interfaces are retained +** for backwards compatibility, but their use is discouraged. +** ^In the "v2" interfaces, the prepared statement +** that is returned (the [sqlite3_stmt] object) contains a copy of the +** original SQL text. This causes the [sqlite3_step()] interface to +** behave differently in three ways: +** +**
    +**
  1. +** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it +** always used to do, [sqlite3_step()] will automatically recompile the SQL +** statement and try to run it again. +**
  2. +** +**
  3. +** ^When an error occurs, [sqlite3_step()] will return one of the detailed +** [error codes] or [extended error codes]. ^The legacy behavior was that +** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code +** and the application would have to make a second call to [sqlite3_reset()] +** in order to find the underlying cause of the problem. With the "v2" prepare +** interfaces, the underlying reason for the error is returned immediately. +**
  4. +** +**
  5. +** ^If the specific value bound to [parameter | host parameter] in the +** WHERE clause might influence the choice of query plan for a statement, +** then the statement will be automatically recompiled, as if there had been +** a schema change, on the first [sqlite3_step()] call following any change +** to the [sqlite3_bind_text | bindings] of that [parameter]. +** ^The specific value of WHERE-clause [parameter] might influence the +** choice of query plan if the parameter is the left-hand side of a [LIKE] +** or [GLOB] operator or if the parameter is compared to an indexed column +** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. +** the +**
  6. +**
+*/ +SQLITE_API int sqlite3_prepare( + sqlite3 *db, /* Database handle */ + const char *zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const char **pzTail /* OUT: Pointer to unused portion of zSql */ +); +SQLITE_API int sqlite3_prepare_v2( + sqlite3 *db, /* Database handle */ + const char *zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const char **pzTail /* OUT: Pointer to unused portion of zSql */ +); +SQLITE_API int sqlite3_prepare16( + sqlite3 *db, /* Database handle */ + const void *zSql, /* SQL statement, UTF-16 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const void **pzTail /* OUT: Pointer to unused portion of zSql */ +); +SQLITE_API int sqlite3_prepare16_v2( + sqlite3 *db, /* Database handle */ + const void *zSql, /* SQL statement, UTF-16 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const void **pzTail /* OUT: Pointer to unused portion of zSql */ +); + +/* +** CAPI3REF: Retrieving Statement SQL +** +** ^This interface can be used to retrieve a saved copy of the original +** SQL text used to create a [prepared statement] if that statement was +** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. +*/ +SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Determine If An SQL Statement Writes The Database +** +** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if +** and only if the [prepared statement] X makes no direct changes to +** the content of the database file. +** +** Note that [application-defined SQL functions] or +** [virtual tables] might change the database indirectly as a side effect. +** ^(For example, if an application defines a function "eval()" that +** calls [sqlite3_exec()], then the following SQL statement would +** change the database file through side-effects: +** +**
+**    SELECT eval('DELETE FROM t1') FROM t2;
+** 
+** +** But because the [SELECT] statement does not change the database file +** directly, sqlite3_stmt_readonly() would still return true.)^ +** +** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK], +** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, +** since the statements themselves do not actually modify the database but +** rather they control the timing of when other statements modify the +** database. ^The [ATTACH] and [DETACH] statements also cause +** sqlite3_stmt_readonly() to return true since, while those statements +** change the configuration of a database connection, they do not make +** changes to the content of the database files on disk. +*/ +SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Determine If A Prepared Statement Has Been Reset +** +** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the +** [prepared statement] S has been stepped at least once using +** [sqlite3_step(S)] but has not run to completion and/or has not +** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) +** interface returns false if S is a NULL pointer. If S is not a +** NULL pointer and is not a pointer to a valid [prepared statement] +** object, then the behavior is undefined and probably undesirable. +** +** This interface can be used in combination [sqlite3_next_stmt()] +** to locate all prepared statements associated with a database +** connection that are in need of being reset. This can be used, +** for example, in diagnostic routines to search for prepared +** statements that are holding a transaction open. +*/ +SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); + +/* +** CAPI3REF: Dynamically Typed Value Object +** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} +** +** SQLite uses the sqlite3_value object to represent all values +** that can be stored in a database table. SQLite uses dynamic typing +** for the values it stores. ^Values stored in sqlite3_value objects +** can be integers, floating point values, strings, BLOBs, or NULL. +** +** An sqlite3_value object may be either "protected" or "unprotected". +** Some interfaces require a protected sqlite3_value. Other interfaces +** will accept either a protected or an unprotected sqlite3_value. +** Every interface that accepts sqlite3_value arguments specifies +** whether or not it requires a protected sqlite3_value. +** +** The terms "protected" and "unprotected" refer to whether or not +** a mutex is held. An internal mutex is held for a protected +** sqlite3_value object but no mutex is held for an unprotected +** sqlite3_value object. If SQLite is compiled to be single-threaded +** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) +** or if SQLite is run in one of reduced mutex modes +** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] +** then there is no distinction between protected and unprotected +** sqlite3_value objects and they can be used interchangeably. However, +** for maximum code portability it is recommended that applications +** still make the distinction between protected and unprotected +** sqlite3_value objects even when not strictly required. +** +** ^The sqlite3_value objects that are passed as parameters into the +** implementation of [application-defined SQL functions] are protected. +** ^The sqlite3_value object returned by +** [sqlite3_column_value()] is unprotected. +** Unprotected sqlite3_value objects may only be used with +** [sqlite3_result_value()] and [sqlite3_bind_value()]. +** The [sqlite3_value_blob | sqlite3_value_type()] family of +** interfaces require protected sqlite3_value objects. +*/ +typedef struct Mem sqlite3_value; + +/* +** CAPI3REF: SQL Function Context Object +** +** The context in which an SQL function executes is stored in an +** sqlite3_context object. ^A pointer to an sqlite3_context object +** is always first parameter to [application-defined SQL functions]. +** The application-defined SQL function implementation will pass this +** pointer through into calls to [sqlite3_result_int | sqlite3_result()], +** [sqlite3_aggregate_context()], [sqlite3_user_data()], +** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()], +** and/or [sqlite3_set_auxdata()]. +*/ +typedef struct sqlite3_context sqlite3_context; + +/* +** CAPI3REF: Binding Values To Prepared Statements +** KEYWORDS: {host parameter} {host parameters} {host parameter name} +** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} +** +** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, +** literals may be replaced by a [parameter] that matches one of following +** templates: +** +**
    +**
  • ? +**
  • ?NNN +**
  • :VVV +**
  • @VVV +**
  • $VVV +**
+** +** In the templates above, NNN represents an integer literal, +** and VVV represents an alphanumeric identifier.)^ ^The values of these +** parameters (also called "host parameter names" or "SQL parameters") +** can be set using the sqlite3_bind_*() routines defined here. +** +** ^The first argument to the sqlite3_bind_*() routines is always +** a pointer to the [sqlite3_stmt] object returned from +** [sqlite3_prepare_v2()] or its variants. +** +** ^The second argument is the index of the SQL parameter to be set. +** ^The leftmost SQL parameter has an index of 1. ^When the same named +** SQL parameter is used more than once, second and subsequent +** occurrences have the same index as the first occurrence. +** ^The index for named parameters can be looked up using the +** [sqlite3_bind_parameter_index()] API if desired. ^The index +** for "?NNN" parameters is the value of NNN. +** ^The NNN value must be between 1 and the [sqlite3_limit()] +** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). +** +** ^The third argument is the value to bind to the parameter. +** +** ^(In those routines that have a fourth argument, its value is the +** number of bytes in the parameter. To be clear: the value is the +** number of bytes in the value, not the number of characters.)^ +** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() +** is negative, then the length of the string is +** the number of bytes up to the first zero terminator. +** If the fourth parameter to sqlite3_bind_blob() is negative, then +** the behavior is undefined. +** If a non-negative fourth parameter is provided to sqlite3_bind_text() +** or sqlite3_bind_text16() then that parameter must be the byte offset +** where the NUL terminator would occur assuming the string were NUL +** terminated. If any NUL characters occur at byte offsets less than +** the value of the fourth parameter then the resulting string value will +** contain embedded NULs. The result of expressions involving strings +** with embedded NULs is undefined. +** +** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and +** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or +** string after SQLite has finished with it. ^The destructor is called +** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), +** sqlite3_bind_text(), or sqlite3_bind_text16() fails. +** ^If the fifth argument is +** the special value [SQLITE_STATIC], then SQLite assumes that the +** information is in static, unmanaged space and does not need to be freed. +** ^If the fifth argument has the value [SQLITE_TRANSIENT], then +** SQLite makes its own private copy of the data immediately, before +** the sqlite3_bind_*() routine returns. +** +** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that +** is filled with zeroes. ^A zeroblob uses a fixed amount of memory +** (just an integer to hold its size) while it is being processed. +** Zeroblobs are intended to serve as placeholders for BLOBs whose +** content is later written using +** [sqlite3_blob_open | incremental BLOB I/O] routines. +** ^A negative value for the zeroblob results in a zero-length BLOB. +** +** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer +** for the [prepared statement] or with a prepared statement for which +** [sqlite3_step()] has been called more recently than [sqlite3_reset()], +** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() +** routine is passed a [prepared statement] that has been finalized, the +** result is undefined and probably harmful. +** +** ^Bindings are not cleared by the [sqlite3_reset()] routine. +** ^Unbound parameters are interpreted as NULL. +** +** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an +** [error code] if anything goes wrong. +** ^[SQLITE_RANGE] is returned if the parameter +** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. +** +** See also: [sqlite3_bind_parameter_count()], +** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. +*/ +SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); +SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); +SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); +SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); +SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); +SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); +SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); + +/* +** CAPI3REF: Number Of SQL Parameters +** +** ^This routine can be used to find the number of [SQL parameters] +** in a [prepared statement]. SQL parameters are tokens of the +** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as +** placeholders for values that are [sqlite3_bind_blob | bound] +** to the parameters at a later time. +** +** ^(This routine actually returns the index of the largest (rightmost) +** parameter. For all forms except ?NNN, this will correspond to the +** number of unique parameters. If parameters of the ?NNN form are used, +** there may be gaps in the list.)^ +** +** See also: [sqlite3_bind_blob|sqlite3_bind()], +** [sqlite3_bind_parameter_name()], and +** [sqlite3_bind_parameter_index()]. +*/ +SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); + +/* +** CAPI3REF: Name Of A Host Parameter +** +** ^The sqlite3_bind_parameter_name(P,N) interface returns +** the name of the N-th [SQL parameter] in the [prepared statement] P. +** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" +** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" +** respectively. +** In other words, the initial ":" or "$" or "@" or "?" +** is included as part of the name.)^ +** ^Parameters of the form "?" without a following integer have no name +** and are referred to as "nameless" or "anonymous parameters". +** +** ^The first host parameter has an index of 1, not 0. +** +** ^If the value N is out of range or if the N-th parameter is +** nameless, then NULL is returned. ^The returned string is +** always in UTF-8 encoding even if the named parameter was +** originally specified as UTF-16 in [sqlite3_prepare16()] or +** [sqlite3_prepare16_v2()]. +** +** See also: [sqlite3_bind_blob|sqlite3_bind()], +** [sqlite3_bind_parameter_count()], and +** [sqlite3_bind_parameter_index()]. +*/ +SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); + +/* +** CAPI3REF: Index Of A Parameter With A Given Name +** +** ^Return the index of an SQL parameter given its name. ^The +** index value returned is suitable for use as the second +** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero +** is returned if no matching parameter is found. ^The parameter +** name must be given in UTF-8 even if the original statement +** was prepared from UTF-16 text using [sqlite3_prepare16_v2()]. +** +** See also: [sqlite3_bind_blob|sqlite3_bind()], +** [sqlite3_bind_parameter_count()], and +** [sqlite3_bind_parameter_index()]. +*/ +SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); + +/* +** CAPI3REF: Reset All Bindings On A Prepared Statement +** +** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset +** the [sqlite3_bind_blob | bindings] on a [prepared statement]. +** ^Use this routine to reset all host parameters to NULL. +*/ +SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); + +/* +** CAPI3REF: Number Of Columns In A Result Set +** +** ^Return the number of columns in the result set returned by the +** [prepared statement]. ^This routine returns 0 if pStmt is an SQL +** statement that does not return data (for example an [UPDATE]). +** +** See also: [sqlite3_data_count()] +*/ +SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Column Names In A Result Set +** +** ^These routines return the name assigned to a particular column +** in the result set of a [SELECT] statement. ^The sqlite3_column_name() +** interface returns a pointer to a zero-terminated UTF-8 string +** and sqlite3_column_name16() returns a pointer to a zero-terminated +** UTF-16 string. ^The first parameter is the [prepared statement] +** that implements the [SELECT] statement. ^The second parameter is the +** column number. ^The leftmost column is number 0. +** +** ^The returned string pointer is valid until either the [prepared statement] +** is destroyed by [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the next call to +** sqlite3_column_name() or sqlite3_column_name16() on the same column. +** +** ^If sqlite3_malloc() fails during the processing of either routine +** (for example during a conversion from UTF-8 to UTF-16) then a +** NULL pointer is returned. +** +** ^The name of a result column is the value of the "AS" clause for +** that column, if there is an AS clause. If there is no AS clause +** then the name of the column is unspecified and may change from +** one release of SQLite to the next. +*/ +SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); +SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); + +/* +** CAPI3REF: Source Of Data In A Query Result +** +** ^These routines provide a means to determine the database, table, and +** table column that is the origin of a particular result column in +** [SELECT] statement. +** ^The name of the database or table or column can be returned as +** either a UTF-8 or UTF-16 string. ^The _database_ routines return +** the database name, the _table_ routines return the table name, and +** the origin_ routines return the column name. +** ^The returned string is valid until the [prepared statement] is destroyed +** using [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the same information is requested +** again in a different encoding. +** +** ^The names returned are the original un-aliased names of the +** database, table, and column. +** +** ^The first argument to these interfaces is a [prepared statement]. +** ^These functions return information about the Nth result column returned by +** the statement, where N is the second function argument. +** ^The left-most column is column 0 for these routines. +** +** ^If the Nth column returned by the statement is an expression or +** subquery and is not a column value, then all of these functions return +** NULL. ^These routine might also return NULL if a memory allocation error +** occurs. ^Otherwise, they return the name of the attached database, table, +** or column that query result column was extracted from. +** +** ^As with all other SQLite APIs, those whose names end with "16" return +** UTF-16 encoded strings and the other functions return UTF-8. +** +** ^These APIs are only available if the library was compiled with the +** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol. +** +** If two or more threads call one or more of these routines against the same +** prepared statement and column at the same time then the results are +** undefined. +** +** If two or more threads call one or more +** [sqlite3_column_database_name | column metadata interfaces] +** for the same [prepared statement] and result column +** at the same time then the results are undefined. +*/ +SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); + +/* +** CAPI3REF: Declared Datatype Of A Query Result +** +** ^(The first parameter is a [prepared statement]. +** If this statement is a [SELECT] statement and the Nth column of the +** returned result set of that [SELECT] is a table column (not an +** expression or subquery) then the declared type of the table +** column is returned.)^ ^If the Nth column of the result set is an +** expression or subquery, then a NULL pointer is returned. +** ^The returned string is always UTF-8 encoded. +** +** ^(For example, given the database schema: +** +** CREATE TABLE t1(c1 VARIANT); +** +** and the following statement to be compiled: +** +** SELECT c1 + 1, c1 FROM t1; +** +** this routine would return the string "VARIANT" for the second result +** column (i==1), and a NULL pointer for the first result column (i==0).)^ +** +** ^SQLite uses dynamic run-time typing. ^So just because a column +** is declared to contain a particular type does not mean that the +** data stored in that column is of the declared type. SQLite is +** strongly typed, but the typing is dynamic not static. ^Type +** is associated with individual values, not with the containers +** used to hold those values. +*/ +SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); + +/* +** CAPI3REF: Evaluate An SQL Statement +** +** After a [prepared statement] has been prepared using either +** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy +** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function +** must be called one or more times to evaluate the statement. +** +** The details of the behavior of the sqlite3_step() interface depend +** on whether the statement was prepared using the newer "v2" interface +** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy +** interface [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the +** new "v2" interface is recommended for new applications but the legacy +** interface will continue to be supported. +** +** ^In the legacy interface, the return value will be either [SQLITE_BUSY], +** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. +** ^With the "v2" interface, any of the other [result codes] or +** [extended result codes] might be returned as well. +** +** ^[SQLITE_BUSY] means that the database engine was unable to acquire the +** database locks it needs to do its job. ^If the statement is a [COMMIT] +** or occurs outside of an explicit transaction, then you can retry the +** statement. If the statement is not a [COMMIT] and occurs within an +** explicit transaction then you should rollback the transaction before +** continuing. +** +** ^[SQLITE_DONE] means that the statement has finished executing +** successfully. sqlite3_step() should not be called again on this virtual +** machine without first calling [sqlite3_reset()] to reset the virtual +** machine back to its initial state. +** +** ^If the SQL statement being executed returns any data, then [SQLITE_ROW] +** is returned each time a new row of data is ready for processing by the +** caller. The values may be accessed using the [column access functions]. +** sqlite3_step() is called again to retrieve the next row of data. +** +** ^[SQLITE_ERROR] means that a run-time error (such as a constraint +** violation) has occurred. sqlite3_step() should not be called again on +** the VM. More information may be found by calling [sqlite3_errmsg()]. +** ^With the legacy interface, a more specific error code (for example, +** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) +** can be obtained by calling [sqlite3_reset()] on the +** [prepared statement]. ^In the "v2" interface, +** the more specific error code is returned directly by sqlite3_step(). +** +** [SQLITE_MISUSE] means that the this routine was called inappropriately. +** Perhaps it was called on a [prepared statement] that has +** already been [sqlite3_finalize | finalized] or on one that had +** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could +** be the case that the same database connection is being used by two or +** more threads at the same moment in time. +** +** For all versions of SQLite up to and including 3.6.23.1, a call to +** [sqlite3_reset()] was required after sqlite3_step() returned anything +** other than [SQLITE_ROW] before any subsequent invocation of +** sqlite3_step(). Failure to reset the prepared statement using +** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from +** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began +** calling [sqlite3_reset()] automatically in this circumstance rather +** than returning [SQLITE_MISUSE]. This is not considered a compatibility +** break because any application that ever receives an SQLITE_MISUSE error +** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option +** can be used to restore the legacy behavior. +** +** Goofy Interface Alert: In the legacy interface, the sqlite3_step() +** API always returns a generic error code, [SQLITE_ERROR], following any +** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call +** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the +** specific [error codes] that better describes the error. +** We admit that this is a goofy design. The problem has been fixed +** with the "v2" interface. If you prepare all of your SQL statements +** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead +** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, +** then the more specific [error codes] are returned directly +** by sqlite3_step(). The use of the "v2" interface is recommended. +*/ +SQLITE_API int sqlite3_step(sqlite3_stmt*); + +/* +** CAPI3REF: Number of columns in a result set +** +** ^The sqlite3_data_count(P) interface returns the number of columns in the +** current row of the result set of [prepared statement] P. +** ^If prepared statement P does not have results ready to return +** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of +** interfaces) then sqlite3_data_count(P) returns 0. +** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. +** ^The sqlite3_data_count(P) routine returns 0 if the previous call to +** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) +** will return non-zero if previous call to [sqlite3_step](P) returned +** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] +** where it always returns zero since each step of that multi-step +** pragma returns 0 columns of data. +** +** See also: [sqlite3_column_count()] +*/ +SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Fundamental Datatypes +** KEYWORDS: SQLITE_TEXT +** +** ^(Every value in SQLite has one of five fundamental datatypes: +** +**
    +**
  • 64-bit signed integer +**
  • 64-bit IEEE floating point number +**
  • string +**
  • BLOB +**
  • NULL +**
)^ +** +** These constants are codes for each of those types. +** +** Note that the SQLITE_TEXT constant was also used in SQLite version 2 +** for a completely different meaning. Software that links against both +** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not +** SQLITE_TEXT. +*/ +#define SQLITE_INTEGER 1 +#define SQLITE_FLOAT 2 +#define SQLITE_BLOB 4 +#define SQLITE_NULL 5 +#ifdef SQLITE_TEXT +# undef SQLITE_TEXT +#else +# define SQLITE_TEXT 3 +#endif +#define SQLITE3_TEXT 3 + +/* +** CAPI3REF: Result Values From A Query +** KEYWORDS: {column access functions} +** +** These routines form the "result set" interface. +** +** ^These routines return information about a single column of the current +** result row of a query. ^In every case the first argument is a pointer +** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] +** that was returned from [sqlite3_prepare_v2()] or one of its variants) +** and the second argument is the index of the column for which information +** should be returned. ^The leftmost column of the result set has the index 0. +** ^The number of columns in the result can be determined using +** [sqlite3_column_count()]. +** +** If the SQL statement does not currently point to a valid row, or if the +** column index is out of range, the result is undefined. +** These routines may only be called when the most recent call to +** [sqlite3_step()] has returned [SQLITE_ROW] and neither +** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently. +** If any of these routines are called after [sqlite3_reset()] or +** [sqlite3_finalize()] or after [sqlite3_step()] has returned +** something other than [SQLITE_ROW], the results are undefined. +** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] +** are called from a different thread while any of these routines +** are pending, then the results are undefined. +** +** ^The sqlite3_column_type() routine returns the +** [SQLITE_INTEGER | datatype code] for the initial data type +** of the result column. ^The returned value is one of [SQLITE_INTEGER], +** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value +** returned by sqlite3_column_type() is only meaningful if no type +** conversions have occurred as described below. After a type conversion, +** the value returned by sqlite3_column_type() is undefined. Future +** versions of SQLite may change the behavior of sqlite3_column_type() +** following a type conversion. +** +** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() +** routine returns the number of bytes in that BLOB or string. +** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts +** the string to UTF-8 and then returns the number of bytes. +** ^If the result is a numeric value then sqlite3_column_bytes() uses +** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns +** the number of bytes in that string. +** ^If the result is NULL, then sqlite3_column_bytes() returns zero. +** +** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() +** routine returns the number of bytes in that BLOB or string. +** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts +** the string to UTF-16 and then returns the number of bytes. +** ^If the result is a numeric value then sqlite3_column_bytes16() uses +** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns +** the number of bytes in that string. +** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. +** +** ^The values returned by [sqlite3_column_bytes()] and +** [sqlite3_column_bytes16()] do not include the zero terminators at the end +** of the string. ^For clarity: the values returned by +** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of +** bytes in the string, not the number of characters. +** +** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), +** even empty strings, are always zero-terminated. ^The return +** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. +** +** ^The object returned by [sqlite3_column_value()] is an +** [unprotected sqlite3_value] object. An unprotected sqlite3_value object +** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. +** If the [unprotected sqlite3_value] object returned by +** [sqlite3_column_value()] is used in any other way, including calls +** to routines like [sqlite3_value_int()], [sqlite3_value_text()], +** or [sqlite3_value_bytes()], then the behavior is undefined. +** +** These routines attempt to convert the value where appropriate. ^For +** example, if the internal representation is FLOAT and a text result +** is requested, [sqlite3_snprintf()] is used internally to perform the +** conversion automatically. ^(The following table details the conversions +** that are applied: +** +**
+** +**
Internal
Type
Requested
Type
Conversion +** +**
NULL INTEGER Result is 0 +**
NULL FLOAT Result is 0.0 +**
NULL TEXT Result is NULL pointer +**
NULL BLOB Result is NULL pointer +**
INTEGER FLOAT Convert from integer to float +**
INTEGER TEXT ASCII rendering of the integer +**
INTEGER BLOB Same as INTEGER->TEXT +**
FLOAT INTEGER Convert from float to integer +**
FLOAT TEXT ASCII rendering of the float +**
FLOAT BLOB Same as FLOAT->TEXT +**
TEXT INTEGER Use atoi() +**
TEXT FLOAT Use atof() +**
TEXT BLOB No change +**
BLOB INTEGER Convert to TEXT then use atoi() +**
BLOB FLOAT Convert to TEXT then use atof() +**
BLOB TEXT Add a zero terminator if needed +**
+**
)^ +** +** The table above makes reference to standard C library functions atoi() +** and atof(). SQLite does not really use these functions. It has its +** own equivalent internal routines. The atoi() and atof() names are +** used in the table for brevity and because they are familiar to most +** C programmers. +** +** Note that when type conversions occur, pointers returned by prior +** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or +** sqlite3_column_text16() may be invalidated. +** Type conversions and pointer invalidations might occur +** in the following cases: +** +**
    +**
  • The initial content is a BLOB and sqlite3_column_text() or +** sqlite3_column_text16() is called. A zero-terminator might +** need to be added to the string.
  • +**
  • The initial content is UTF-8 text and sqlite3_column_bytes16() or +** sqlite3_column_text16() is called. The content must be converted +** to UTF-16.
  • +**
  • The initial content is UTF-16 text and sqlite3_column_bytes() or +** sqlite3_column_text() is called. The content must be converted +** to UTF-8.
  • +**
+** +** ^Conversions between UTF-16be and UTF-16le are always done in place and do +** not invalidate a prior pointer, though of course the content of the buffer +** that the prior pointer references will have been modified. Other kinds +** of conversion are done in place when it is possible, but sometimes they +** are not possible and in those cases prior pointers are invalidated. +** +** The safest and easiest to remember policy is to invoke these routines +** in one of the following ways: +** +**
    +**
  • sqlite3_column_text() followed by sqlite3_column_bytes()
  • +**
  • sqlite3_column_blob() followed by sqlite3_column_bytes()
  • +**
  • sqlite3_column_text16() followed by sqlite3_column_bytes16()
  • +**
+** +** In other words, you should call sqlite3_column_text(), +** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result +** into the desired format, then invoke sqlite3_column_bytes() or +** sqlite3_column_bytes16() to find the size of the result. Do not mix calls +** to sqlite3_column_text() or sqlite3_column_blob() with calls to +** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() +** with calls to sqlite3_column_bytes(). +** +** ^The pointers returned are valid until a type conversion occurs as +** described above, or until [sqlite3_step()] or [sqlite3_reset()] or +** [sqlite3_finalize()] is called. ^The memory space used to hold strings +** and BLOBs is freed automatically. Do not pass the pointers returned +** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into +** [sqlite3_free()]. +** +** ^(If a memory allocation error occurs during the evaluation of any +** of these routines, a default value is returned. The default value +** is either the integer 0, the floating point number 0.0, or a NULL +** pointer. Subsequent calls to [sqlite3_errcode()] will return +** [SQLITE_NOMEM].)^ +*/ +SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); +SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); +SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); +SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); + +/* +** CAPI3REF: Destroy A Prepared Statement Object +** +** ^The sqlite3_finalize() function is called to delete a [prepared statement]. +** ^If the most recent evaluation of the statement encountered no errors +** or if the statement is never been evaluated, then sqlite3_finalize() returns +** SQLITE_OK. ^If the most recent evaluation of statement S failed, then +** sqlite3_finalize(S) returns the appropriate [error code] or +** [extended error code]. +** +** ^The sqlite3_finalize(S) routine can be called at any point during +** the life cycle of [prepared statement] S: +** before statement S is ever evaluated, after +** one or more calls to [sqlite3_reset()], or after any call +** to [sqlite3_step()] regardless of whether or not the statement has +** completed execution. +** +** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. +** +** The application must finalize every [prepared statement] in order to avoid +** resource leaks. It is a grievous error for the application to try to use +** a prepared statement after it has been finalized. Any use of a prepared +** statement after it has been finalized can result in undefined and +** undesirable behavior such as segfaults and heap corruption. +*/ +SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Reset A Prepared Statement Object +** +** The sqlite3_reset() function is called to reset a [prepared statement] +** object back to its initial state, ready to be re-executed. +** ^Any SQL statement variables that had values bound to them using +** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. +** Use [sqlite3_clear_bindings()] to reset the bindings. +** +** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S +** back to the beginning of its program. +** +** ^If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], +** or if [sqlite3_step(S)] has never before been called on S, +** then [sqlite3_reset(S)] returns [SQLITE_OK]. +** +** ^If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S indicated an error, then +** [sqlite3_reset(S)] returns an appropriate [error code]. +** +** ^The [sqlite3_reset(S)] interface does not change the values +** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. +*/ +SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Create Or Redefine SQL Functions +** KEYWORDS: {function creation routines} +** KEYWORDS: {application-defined SQL function} +** KEYWORDS: {application-defined SQL functions} +** +** ^These functions (collectively known as "function creation routines") +** are used to add SQL functions or aggregates or to redefine the behavior +** of existing SQL functions or aggregates. The only differences between +** these routines are the text encoding expected for +** the second parameter (the name of the function being created) +** and the presence or absence of a destructor callback for +** the application data pointer. +** +** ^The first parameter is the [database connection] to which the SQL +** function is to be added. ^If an application uses more than one database +** connection then application-defined SQL functions must be added +** to each database connection separately. +** +** ^The second parameter is the name of the SQL function to be created or +** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 +** representation, exclusive of the zero-terminator. ^Note that the name +** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. +** ^Any attempt to create a function with a longer name +** will result in [SQLITE_MISUSE] being returned. +** +** ^The third parameter (nArg) +** is the number of arguments that the SQL function or +** aggregate takes. ^If this parameter is -1, then the SQL function or +** aggregate may take any number of arguments between 0 and the limit +** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third +** parameter is less than -1 or greater than 127 then the behavior is +** undefined. +** +** ^The fourth parameter, eTextRep, specifies what +** [SQLITE_UTF8 | text encoding] this SQL function prefers for +** its parameters. Every SQL function implementation must be able to work +** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be +** more efficient with one encoding than another. ^An application may +** invoke sqlite3_create_function() or sqlite3_create_function16() multiple +** times with the same function but with different values of eTextRep. +** ^When multiple implementations of the same function are available, SQLite +** will pick the one that involves the least amount of data conversion. +** If there is only a single implementation which does not care what text +** encoding is used, then the fourth argument should be [SQLITE_ANY]. +** +** ^(The fifth parameter is an arbitrary pointer. The implementation of the +** function can gain access to this pointer using [sqlite3_user_data()].)^ +** +** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are +** pointers to C-language functions that implement the SQL function or +** aggregate. ^A scalar SQL function requires an implementation of the xFunc +** callback only; NULL pointers must be passed as the xStep and xFinal +** parameters. ^An aggregate SQL function requires an implementation of xStep +** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing +** SQL function or aggregate, pass NULL pointers for all three function +** callbacks. +** +** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL, +** then it is destructor for the application data pointer. +** The destructor is invoked when the function is deleted, either by being +** overloaded or when the database connection closes.)^ +** ^The destructor is also invoked if the call to +** sqlite3_create_function_v2() fails. +** ^When the destructor callback of the tenth parameter is invoked, it +** is passed a single argument which is a copy of the application data +** pointer which was the fifth parameter to sqlite3_create_function_v2(). +** +** ^It is permitted to register multiple implementations of the same +** functions with the same name but with either differing numbers of +** arguments or differing preferred text encodings. ^SQLite will use +** the implementation that most closely matches the way in which the +** SQL function is used. ^A function implementation with a non-negative +** nArg parameter is a better match than a function implementation with +** a negative nArg. ^A function where the preferred text encoding +** matches the database encoding is a better +** match than a function where the encoding is different. +** ^A function where the encoding difference is between UTF16le and UTF16be +** is a closer match than a function where the encoding difference is +** between UTF8 and UTF16. +** +** ^Built-in functions may be overloaded by new application-defined functions. +** +** ^An application-defined function is permitted to call other +** SQLite interfaces. However, such calls must not +** close the database connection nor finalize or reset the prepared +** statement in which the function is running. +*/ +SQLITE_API int sqlite3_create_function( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*) +); +SQLITE_API int sqlite3_create_function16( + sqlite3 *db, + const void *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*) +); +SQLITE_API int sqlite3_create_function_v2( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void(*xDestroy)(void*) +); + +/* +** CAPI3REF: Text Encodings +** +** These constant define integer codes that represent the various +** text encodings supported by SQLite. +*/ +#define SQLITE_UTF8 1 +#define SQLITE_UTF16LE 2 +#define SQLITE_UTF16BE 3 +#define SQLITE_UTF16 4 /* Use native byte order */ +#define SQLITE_ANY 5 /* sqlite3_create_function only */ +#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ + +/* +** CAPI3REF: Deprecated Functions +** DEPRECATED +** +** These functions are [deprecated]. In order to maintain +** backwards compatibility with older code, these functions continue +** to be supported. However, new applications should avoid +** the use of these functions. To help encourage people to avoid +** using these functions, we are not going to tell you what they do. +*/ +#ifndef SQLITE_OMIT_DEPRECATED +SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); +SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); +SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), + void*,sqlite3_int64); +#endif + +/* +** CAPI3REF: Obtaining SQL Function Parameter Values +** +** The C-language implementation of SQL functions and aggregates uses +** this set of interface routines to access the parameter values on +** the function or aggregate. +** +** The xFunc (for scalar functions) or xStep (for aggregates) parameters +** to [sqlite3_create_function()] and [sqlite3_create_function16()] +** define callbacks that implement the SQL functions and aggregates. +** The 3rd parameter to these callbacks is an array of pointers to +** [protected sqlite3_value] objects. There is one [sqlite3_value] object for +** each parameter to the SQL function. These routines are used to +** extract values from the [sqlite3_value] objects. +** +** These routines work only with [protected sqlite3_value] objects. +** Any attempt to use these routines on an [unprotected sqlite3_value] +** object results in undefined behavior. +** +** ^These routines work just like the corresponding [column access functions] +** except that these routines take a single [protected sqlite3_value] object +** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. +** +** ^The sqlite3_value_text16() interface extracts a UTF-16 string +** in the native byte-order of the host machine. ^The +** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces +** extract UTF-16 strings as big-endian and little-endian respectively. +** +** ^(The sqlite3_value_numeric_type() interface attempts to apply +** numeric affinity to the value. This means that an attempt is +** made to convert the value to an integer or floating point. If +** such a conversion is possible without loss of information (in other +** words, if the value is a string that looks like a number) +** then the conversion is performed. Otherwise no conversion occurs. +** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ +** +** Please pay particular attention to the fact that the pointer returned +** from [sqlite3_value_blob()], [sqlite3_value_text()], or +** [sqlite3_value_text16()] can be invalidated by a subsequent call to +** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], +** or [sqlite3_value_text16()]. +** +** These routines must be called from the same thread as +** the SQL function that supplied the [sqlite3_value*] parameters. +*/ +SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API double sqlite3_value_double(sqlite3_value*); +SQLITE_API int sqlite3_value_int(sqlite3_value*); +SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); +SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); +SQLITE_API int sqlite3_value_type(sqlite3_value*); +SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); + +/* +** CAPI3REF: Obtain Aggregate Function Context +** +** Implementations of aggregate SQL functions use this +** routine to allocate memory for storing their state. +** +** ^The first time the sqlite3_aggregate_context(C,N) routine is called +** for a particular aggregate function, SQLite +** allocates N of memory, zeroes out that memory, and returns a pointer +** to the new memory. ^On second and subsequent calls to +** sqlite3_aggregate_context() for the same aggregate function instance, +** the same buffer is returned. Sqlite3_aggregate_context() is normally +** called once for each invocation of the xStep callback and then one +** last time when the xFinal callback is invoked. ^(When no rows match +** an aggregate query, the xStep() callback of the aggregate function +** implementation is never called and xFinal() is called exactly once. +** In those cases, sqlite3_aggregate_context() might be called for the +** first time from within xFinal().)^ +** +** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer +** when first called if N is less than or equal to zero or if a memory +** allocate error occurs. +** +** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is +** determined by the N parameter on first successful call. Changing the +** value of N in subsequent call to sqlite3_aggregate_context() within +** the same aggregate function instance will not resize the memory +** allocation.)^ Within the xFinal callback, it is customary to set +** N=0 in calls to sqlite3_aggregate_context(C,N) so that no +** pointless memory allocations occur. +** +** ^SQLite automatically frees the memory allocated by +** sqlite3_aggregate_context() when the aggregate query concludes. +** +** The first parameter must be a copy of the +** [sqlite3_context | SQL function context] that is the first parameter +** to the xStep or xFinal callback routine that implements the aggregate +** function. +** +** This routine must be called from the same thread in which +** the aggregate SQL function is running. +*/ +SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); + +/* +** CAPI3REF: User Data For Functions +** +** ^The sqlite3_user_data() interface returns a copy of +** the pointer that was the pUserData parameter (the 5th parameter) +** of the [sqlite3_create_function()] +** and [sqlite3_create_function16()] routines that originally +** registered the application defined function. +** +** This routine must be called from the same thread in which +** the application-defined function is running. +*/ +SQLITE_API void *sqlite3_user_data(sqlite3_context*); + +/* +** CAPI3REF: Database Connection For Functions +** +** ^The sqlite3_context_db_handle() interface returns a copy of +** the pointer to the [database connection] (the 1st parameter) +** of the [sqlite3_create_function()] +** and [sqlite3_create_function16()] routines that originally +** registered the application defined function. +*/ +SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); + +/* +** CAPI3REF: Function Auxiliary Data +** +** The following two functions may be used by scalar SQL functions to +** associate metadata with argument values. If the same value is passed to +** multiple invocations of the same SQL function during query execution, under +** some circumstances the associated metadata may be preserved. This may +** be used, for example, to add a regular-expression matching scalar +** function. The compiled version of the regular expression is stored as +** metadata associated with the SQL value passed as the regular expression +** pattern. The compiled regular expression can be reused on multiple +** invocations of the same function so that the original pattern string +** does not need to be recompiled on each invocation. +** +** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata +** associated by the sqlite3_set_auxdata() function with the Nth argument +** value to the application-defined function. ^If no metadata has been ever +** been set for the Nth argument of the function, or if the corresponding +** function parameter has changed since the meta-data was set, +** then sqlite3_get_auxdata() returns a NULL pointer. +** +** ^The sqlite3_set_auxdata() interface saves the metadata +** pointed to by its 3rd parameter as the metadata for the N-th +** argument of the application-defined function. Subsequent +** calls to sqlite3_get_auxdata() might return this data, if it has +** not been destroyed. +** ^If it is not NULL, SQLite will invoke the destructor +** function given by the 4th parameter to sqlite3_set_auxdata() on +** the metadata when the corresponding function parameter changes +** or when the SQL statement completes, whichever comes first. +** +** SQLite is free to call the destructor and drop metadata on any +** parameter of any function at any time. ^The only guarantee is that +** the destructor will be called before the metadata is dropped. +** +** ^(In practice, metadata is preserved between function calls for +** expressions that are constant at compile time. This includes literal +** values and [parameters].)^ +** +** These routines must be called from the same thread in which +** the SQL function is running. +*/ +SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); + + +/* +** CAPI3REF: Constants Defining Special Destructor Behavior +** +** These are special values for the destructor that is passed in as the +** final argument to routines like [sqlite3_result_blob()]. ^If the destructor +** argument is SQLITE_STATIC, it means that the content pointer is constant +** and will never change. It does not need to be destroyed. ^The +** SQLITE_TRANSIENT value means that the content will likely change in +** the near future and that SQLite should make its own private copy of +** the content before returning. +** +** The typedef is necessary to work around problems in certain +** C++ compilers. See ticket #2191. +*/ +typedef void (*sqlite3_destructor_type)(void*); +#define SQLITE_STATIC ((sqlite3_destructor_type)0) +#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) + +/* +** CAPI3REF: Setting The Result Of An SQL Function +** +** These routines are used by the xFunc or xFinal callbacks that +** implement SQL functions and aggregates. See +** [sqlite3_create_function()] and [sqlite3_create_function16()] +** for additional information. +** +** These functions work very much like the [parameter binding] family of +** functions used to bind values to host parameters in prepared statements. +** Refer to the [SQL parameter] documentation for additional information. +** +** ^The sqlite3_result_blob() interface sets the result from +** an application-defined function to be the BLOB whose content is pointed +** to by the second parameter and which is N bytes long where N is the +** third parameter. +** +** ^The sqlite3_result_zeroblob() interfaces set the result of +** the application-defined function to be a BLOB containing all zero +** bytes and N bytes in size, where N is the value of the 2nd parameter. +** +** ^The sqlite3_result_double() interface sets the result from +** an application-defined function to be a floating point value specified +** by its 2nd argument. +** +** ^The sqlite3_result_error() and sqlite3_result_error16() functions +** cause the implemented SQL function to throw an exception. +** ^SQLite uses the string pointed to by the +** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() +** as the text of an error message. ^SQLite interprets the error +** message string from sqlite3_result_error() as UTF-8. ^SQLite +** interprets the string from sqlite3_result_error16() as UTF-16 in native +** byte order. ^If the third parameter to sqlite3_result_error() +** or sqlite3_result_error16() is negative then SQLite takes as the error +** message all text up through the first zero character. +** ^If the third parameter to sqlite3_result_error() or +** sqlite3_result_error16() is non-negative then SQLite takes that many +** bytes (not characters) from the 2nd parameter as the error message. +** ^The sqlite3_result_error() and sqlite3_result_error16() +** routines make a private copy of the error message text before +** they return. Hence, the calling function can deallocate or +** modify the text after they return without harm. +** ^The sqlite3_result_error_code() function changes the error code +** returned by SQLite as a result of an error in a function. ^By default, +** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() +** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. +** +** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an +** error indicating that a string or BLOB is too long to represent. +** +** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an +** error indicating that a memory allocation failed. +** +** ^The sqlite3_result_int() interface sets the return value +** of the application-defined function to be the 32-bit signed integer +** value given in the 2nd argument. +** ^The sqlite3_result_int64() interface sets the return value +** of the application-defined function to be the 64-bit signed integer +** value given in the 2nd argument. +** +** ^The sqlite3_result_null() interface sets the return value +** of the application-defined function to be NULL. +** +** ^The sqlite3_result_text(), sqlite3_result_text16(), +** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces +** set the return value of the application-defined function to be +** a text string which is represented as UTF-8, UTF-16 native byte order, +** UTF-16 little endian, or UTF-16 big endian, respectively. +** ^SQLite takes the text result from the application from +** the 2nd parameter of the sqlite3_result_text* interfaces. +** ^If the 3rd parameter to the sqlite3_result_text* interfaces +** is negative, then SQLite takes result text from the 2nd parameter +** through the first zero character. +** ^If the 3rd parameter to the sqlite3_result_text* interfaces +** is non-negative, then as many bytes (not characters) of the text +** pointed to by the 2nd parameter are taken as the application-defined +** function result. If the 3rd parameter is non-negative, then it +** must be the byte offset into the string where the NUL terminator would +** appear if the string where NUL terminated. If any NUL characters occur +** in the string at a byte offset that is less than the value of the 3rd +** parameter, then the resulting string will contain embedded NULs and the +** result of expressions operating on strings with embedded NULs is undefined. +** ^If the 4th parameter to the sqlite3_result_text* interfaces +** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that +** function as the destructor on the text or BLOB result when it has +** finished using that result. +** ^If the 4th parameter to the sqlite3_result_text* interfaces or to +** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite +** assumes that the text or BLOB result is in constant space and does not +** copy the content of the parameter nor call a destructor on the content +** when it has finished using that result. +** ^If the 4th parameter to the sqlite3_result_text* interfaces +** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT +** then SQLite makes a copy of the result into space obtained from +** from [sqlite3_malloc()] before it returns. +** +** ^The sqlite3_result_value() interface sets the result of +** the application-defined function to be a copy the +** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The +** sqlite3_result_value() interface makes a copy of the [sqlite3_value] +** so that the [sqlite3_value] specified in the parameter may change or +** be deallocated after sqlite3_result_value() returns without harm. +** ^A [protected sqlite3_value] object may always be used where an +** [unprotected sqlite3_value] object is required, so either +** kind of [sqlite3_value] object can be used with this interface. +** +** If these routines are called from within the different thread +** than the one containing the application-defined function that received +** the [sqlite3_context] pointer, the results are undefined. +*/ +SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_double(sqlite3_context*, double); +SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); +SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); +SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); +SQLITE_API void sqlite3_result_null(sqlite3_context*); +SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); +SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); + +/* +** CAPI3REF: Define New Collating Sequences +** +** ^These functions add, remove, or modify a [collation] associated +** with the [database connection] specified as the first argument. +** +** ^The name of the collation is a UTF-8 string +** for sqlite3_create_collation() and sqlite3_create_collation_v2() +** and a UTF-16 string in native byte order for sqlite3_create_collation16(). +** ^Collation names that compare equal according to [sqlite3_strnicmp()] are +** considered to be the same name. +** +** ^(The third argument (eTextRep) must be one of the constants: +**
    +**
  • [SQLITE_UTF8], +**
  • [SQLITE_UTF16LE], +**
  • [SQLITE_UTF16BE], +**
  • [SQLITE_UTF16], or +**
  • [SQLITE_UTF16_ALIGNED]. +**
)^ +** ^The eTextRep argument determines the encoding of strings passed +** to the collating function callback, xCallback. +** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep +** force strings to be UTF16 with native byte order. +** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin +** on an even byte address. +** +** ^The fourth argument, pArg, is an application data pointer that is passed +** through as the first argument to the collating function callback. +** +** ^The fifth argument, xCallback, is a pointer to the collating function. +** ^Multiple collating functions can be registered using the same name but +** with different eTextRep parameters and SQLite will use whichever +** function requires the least amount of data transformation. +** ^If the xCallback argument is NULL then the collating function is +** deleted. ^When all collating functions having the same name are deleted, +** that collation is no longer usable. +** +** ^The collating function callback is invoked with a copy of the pArg +** application data pointer and with two strings in the encoding specified +** by the eTextRep argument. The collating function must return an +** integer that is negative, zero, or positive +** if the first string is less than, equal to, or greater than the second, +** respectively. A collating function must always return the same answer +** given the same inputs. If two or more collating functions are registered +** to the same collation name (using different eTextRep values) then all +** must give an equivalent answer when invoked with equivalent strings. +** The collating function must obey the following properties for all +** strings A, B, and C: +** +**
    +**
  1. If A==B then B==A. +**
  2. If A==B and B==C then A==C. +**
  3. If A<B THEN B>A. +**
  4. If A<B and B<C then A<C. +**
+** +** If a collating function fails any of the above constraints and that +** collating function is registered and used, then the behavior of SQLite +** is undefined. +** +** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() +** with the addition that the xDestroy callback is invoked on pArg when +** the collating function is deleted. +** ^Collating functions are deleted when they are overridden by later +** calls to the collation creation functions or when the +** [database connection] is closed using [sqlite3_close()]. +** +** ^The xDestroy callback is not called if the +** sqlite3_create_collation_v2() function fails. Applications that invoke +** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should +** check the return code and dispose of the application data pointer +** themselves rather than expecting SQLite to deal with it for them. +** This is different from every other SQLite interface. The inconsistency +** is unfortunate but cannot be changed without breaking backwards +** compatibility. +** +** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. +*/ +SQLITE_API int sqlite3_create_collation( + sqlite3*, + const char *zName, + int eTextRep, + void *pArg, + int(*xCompare)(void*,int,const void*,int,const void*) +); +SQLITE_API int sqlite3_create_collation_v2( + sqlite3*, + const char *zName, + int eTextRep, + void *pArg, + int(*xCompare)(void*,int,const void*,int,const void*), + void(*xDestroy)(void*) +); +SQLITE_API int sqlite3_create_collation16( + sqlite3*, + const void *zName, + int eTextRep, + void *pArg, + int(*xCompare)(void*,int,const void*,int,const void*) +); + +/* +** CAPI3REF: Collation Needed Callbacks +** +** ^To avoid having to register all collation sequences before a database +** can be used, a single callback function may be registered with the +** [database connection] to be invoked whenever an undefined collation +** sequence is required. +** +** ^If the function is registered using the sqlite3_collation_needed() API, +** then it is passed the names of undefined collation sequences as strings +** encoded in UTF-8. ^If sqlite3_collation_needed16() is used, +** the names are passed as UTF-16 in machine native byte order. +** ^A call to either function replaces the existing collation-needed callback. +** +** ^(When the callback is invoked, the first argument passed is a copy +** of the second argument to sqlite3_collation_needed() or +** sqlite3_collation_needed16(). The second argument is the database +** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], +** or [SQLITE_UTF16LE], indicating the most desirable form of the collation +** sequence function required. The fourth parameter is the name of the +** required collation sequence.)^ +** +** The callback function should register the desired collation using +** [sqlite3_create_collation()], [sqlite3_create_collation16()], or +** [sqlite3_create_collation_v2()]. +*/ +SQLITE_API int sqlite3_collation_needed( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const char*) +); +SQLITE_API int sqlite3_collation_needed16( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const void*) +); + +#ifdef SQLITE_HAS_CODEC +/* +** Specify the key for an encrypted database. This routine should be +** called right after sqlite3_open(). +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +SQLITE_API int sqlite3_key( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The key */ +); + +/* +** Change the key on an open database. If the current database is not +** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the +** database is decrypted. +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +SQLITE_API int sqlite3_rekey( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The new key */ +); + +/* +** Specify the activation key for a SEE database. Unless +** activated, none of the SEE routines will work. +*/ +SQLITE_API void sqlite3_activate_see( + const char *zPassPhrase /* Activation phrase */ +); +#endif + +#ifdef SQLITE_ENABLE_CEROD +/* +** Specify the activation key for a CEROD database. Unless +** activated, none of the CEROD routines will work. +*/ +SQLITE_API void sqlite3_activate_cerod( + const char *zPassPhrase /* Activation phrase */ +); +#endif + +/* +** CAPI3REF: Suspend Execution For A Short Time +** +** The sqlite3_sleep() function causes the current thread to suspend execution +** for at least a number of milliseconds specified in its parameter. +** +** If the operating system does not support sleep requests with +** millisecond time resolution, then the time will be rounded up to +** the nearest second. The number of milliseconds of sleep actually +** requested from the operating system is returned. +** +** ^SQLite implements this interface by calling the xSleep() +** method of the default [sqlite3_vfs] object. If the xSleep() method +** of the default VFS is not implemented correctly, or not implemented at +** all, then the behavior of sqlite3_sleep() may deviate from the description +** in the previous paragraphs. +*/ +SQLITE_API int sqlite3_sleep(int); + +/* +** CAPI3REF: Name Of The Folder Holding Temporary Files +** +** ^(If this global variable is made to point to a string which is +** the name of a folder (a.k.a. directory), then all temporary files +** created by SQLite when using a built-in [sqlite3_vfs | VFS] +** will be placed in that directory.)^ ^If this variable +** is a NULL pointer, then SQLite performs a search for an appropriate +** temporary file directory. +** +** It is not safe to read or modify this variable in more than one +** thread at a time. It is not safe to read or modify this variable +** if a [database connection] is being used at the same time in a separate +** thread. +** It is intended that this variable be set once +** as part of process initialization and before any SQLite interface +** routines have been called and that this variable remain unchanged +** thereafter. +** +** ^The [temp_store_directory pragma] may modify this variable and cause +** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, +** the [temp_store_directory pragma] always assumes that any string +** that this variable points to is held in memory obtained from +** [sqlite3_malloc] and the pragma may attempt to free that memory +** using [sqlite3_free]. +** Hence, if this variable is modified directly, either it should be +** made NULL or made to point to memory obtained from [sqlite3_malloc] +** or else the use of the [temp_store_directory pragma] should be avoided. +** +** Note to Windows Runtime users: The temporary directory must be set +** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various +** features that require the use of temporary files may fail. Here is an +** example of how to do this using C++ with the Windows Runtime: +** +**
+** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
+**       TemporaryFolder->Path->Data();
+** char zPathBuf[MAX_PATH + 1];
+** memset(zPathBuf, 0, sizeof(zPathBuf));
+** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
+**       NULL, NULL);
+** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
+** 
+*/ +SQLITE_API char *sqlite3_temp_directory; + +/* +** CAPI3REF: Name Of The Folder Holding Database Files +** +** ^(If this global variable is made to point to a string which is +** the name of a folder (a.k.a. directory), then all database files +** specified with a relative pathname and created or accessed by +** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed +** to be relative to that directory.)^ ^If this variable is a NULL +** pointer, then SQLite assumes that all database files specified +** with a relative pathname are relative to the current directory +** for the process. Only the windows VFS makes use of this global +** variable; it is ignored by the unix VFS. +** +** Changing the value of this variable while a database connection is +** open can result in a corrupt database. +** +** It is not safe to read or modify this variable in more than one +** thread at a time. It is not safe to read or modify this variable +** if a [database connection] is being used at the same time in a separate +** thread. +** It is intended that this variable be set once +** as part of process initialization and before any SQLite interface +** routines have been called and that this variable remain unchanged +** thereafter. +** +** ^The [data_store_directory pragma] may modify this variable and cause +** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, +** the [data_store_directory pragma] always assumes that any string +** that this variable points to is held in memory obtained from +** [sqlite3_malloc] and the pragma may attempt to free that memory +** using [sqlite3_free]. +** Hence, if this variable is modified directly, either it should be +** made NULL or made to point to memory obtained from [sqlite3_malloc] +** or else the use of the [data_store_directory pragma] should be avoided. +*/ +SQLITE_API char *sqlite3_data_directory; + +/* +** CAPI3REF: Test For Auto-Commit Mode +** KEYWORDS: {autocommit mode} +** +** ^The sqlite3_get_autocommit() interface returns non-zero or +** zero if the given database connection is or is not in autocommit mode, +** respectively. ^Autocommit mode is on by default. +** ^Autocommit mode is disabled by a [BEGIN] statement. +** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. +** +** If certain kinds of errors occur on a statement within a multi-statement +** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], +** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the +** transaction might be rolled back automatically. The only way to +** find out whether SQLite automatically rolled back the transaction after +** an error is to use this function. +** +** If another thread changes the autocommit status of the database +** connection while this routine is running, then the return value +** is undefined. +*/ +SQLITE_API int sqlite3_get_autocommit(sqlite3*); + +/* +** CAPI3REF: Find The Database Handle Of A Prepared Statement +** +** ^The sqlite3_db_handle interface returns the [database connection] handle +** to which a [prepared statement] belongs. ^The [database connection] +** returned by sqlite3_db_handle is the same [database connection] +** that was the first argument +** to the [sqlite3_prepare_v2()] call (or its variants) that was used to +** create the statement in the first place. +*/ +SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); + +/* +** CAPI3REF: Return The Filename For A Database Connection +** +** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename +** associated with database N of connection D. ^The main database file +** has the name "main". If there is no attached database N on the database +** connection D, or if database N is a temporary or in-memory database, then +** a NULL pointer is returned. +** +** ^The filename returned by this function is the output of the +** xFullPathname method of the [VFS]. ^In other words, the filename +** will be an absolute pathname, even if the filename used +** to open the database originally was a URI or relative pathname. +*/ +SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); + +/* +** CAPI3REF: Determine if a database is read-only +** +** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N +** of connection D is read-only, 0 if it is read/write, or -1 if N is not +** the name of a database on connection D. +*/ +SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); + +/* +** CAPI3REF: Find the next prepared statement +** +** ^This interface returns a pointer to the next [prepared statement] after +** pStmt associated with the [database connection] pDb. ^If pStmt is NULL +** then this interface returns a pointer to the first prepared statement +** associated with the database connection pDb. ^If no prepared statement +** satisfies the conditions of this routine, it returns NULL. +** +** The [database connection] pointer D in a call to +** [sqlite3_next_stmt(D,S)] must refer to an open database +** connection and in particular must not be a NULL pointer. +*/ +SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Commit And Rollback Notification Callbacks +** +** ^The sqlite3_commit_hook() interface registers a callback +** function to be invoked whenever a transaction is [COMMIT | committed]. +** ^Any callback set by a previous call to sqlite3_commit_hook() +** for the same database connection is overridden. +** ^The sqlite3_rollback_hook() interface registers a callback +** function to be invoked whenever a transaction is [ROLLBACK | rolled back]. +** ^Any callback set by a previous call to sqlite3_rollback_hook() +** for the same database connection is overridden. +** ^The pArg argument is passed through to the callback. +** ^If the callback on a commit hook function returns non-zero, +** then the commit is converted into a rollback. +** +** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions +** return the P argument from the previous call of the same function +** on the same [database connection] D, or NULL for +** the first call for each function on D. +** +** The commit and rollback hook callbacks are not reentrant. +** The callback implementation must not do anything that will modify +** the database connection that invoked the callback. Any actions +** to modify the database connection must be deferred until after the +** completion of the [sqlite3_step()] call that triggered the commit +** or rollback hook in the first place. +** Note that running any other SQL statements, including SELECT statements, +** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify +** the database connections for the meaning of "modify" in this paragraph. +** +** ^Registering a NULL function disables the callback. +** +** ^When the commit hook callback routine returns zero, the [COMMIT] +** operation is allowed to continue normally. ^If the commit hook +** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK]. +** ^The rollback hook is invoked on a rollback that results from a commit +** hook returning non-zero, just as it would be with any other rollback. +** +** ^For the purposes of this API, a transaction is said to have been +** rolled back if an explicit "ROLLBACK" statement is executed, or +** an error or constraint causes an implicit rollback to occur. +** ^The rollback callback is not invoked if a transaction is +** automatically rolled back because the database connection is closed. +** +** See also the [sqlite3_update_hook()] interface. +*/ +SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); +SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + +/* +** CAPI3REF: Data Change Notification Callbacks +** +** ^The sqlite3_update_hook() interface registers a callback function +** with the [database connection] identified by the first argument +** to be invoked whenever a row is updated, inserted or deleted. +** ^Any callback set by a previous call to this function +** for the same database connection is overridden. +** +** ^The second argument is a pointer to the function to invoke when a +** row is updated, inserted or deleted. +** ^The first argument to the callback is a copy of the third argument +** to sqlite3_update_hook(). +** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], +** or [SQLITE_UPDATE], depending on the operation that caused the callback +** to be invoked. +** ^The third and fourth arguments to the callback contain pointers to the +** database and table name containing the affected row. +** ^The final callback parameter is the [rowid] of the row. +** ^In the case of an update, this is the [rowid] after the update takes place. +** +** ^(The update hook is not invoked when internal system tables are +** modified (i.e. sqlite_master and sqlite_sequence).)^ +** +** ^In the current implementation, the update hook +** is not invoked when duplication rows are deleted because of an +** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook +** invoked when rows are deleted using the [truncate optimization]. +** The exceptions defined in this paragraph might change in a future +** release of SQLite. +** +** The update hook implementation must not do anything that will modify +** the database connection that invoked the update hook. Any actions +** to modify the database connection must be deferred until after the +** completion of the [sqlite3_step()] call that triggered the update hook. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +** ^The sqlite3_update_hook(D,C,P) function +** returns the P argument from the previous call +** on the same [database connection] D, or NULL for +** the first call on D. +** +** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] +** interfaces. +*/ +SQLITE_API void *sqlite3_update_hook( + sqlite3*, + void(*)(void *,int ,char const *,char const *,sqlite3_int64), + void* +); + +/* +** CAPI3REF: Enable Or Disable Shared Pager Cache +** +** ^(This routine enables or disables the sharing of the database cache +** and schema data structures between [database connection | connections] +** to the same database. Sharing is enabled if the argument is true +** and disabled if the argument is false.)^ +** +** ^Cache sharing is enabled and disabled for an entire process. +** This is a change as of SQLite version 3.5.0. In prior versions of SQLite, +** sharing was enabled or disabled for each thread separately. +** +** ^(The cache sharing mode set by this interface effects all subsequent +** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. +** Existing database connections continue use the sharing mode +** that was in effect at the time they were opened.)^ +** +** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled +** successfully. An [error code] is returned otherwise.)^ +** +** ^Shared cache is disabled by default. But this might change in +** future releases of SQLite. Applications that care about shared +** cache setting should set it explicitly. +** +** This interface is threadsafe on processors where writing a +** 32-bit integer is atomic. +** +** See Also: [SQLite Shared-Cache Mode] +*/ +SQLITE_API int sqlite3_enable_shared_cache(int); + +/* +** CAPI3REF: Attempt To Free Heap Memory +** +** ^The sqlite3_release_memory() interface attempts to free N bytes +** of heap memory by deallocating non-essential memory allocations +** held by the database library. Memory used to cache database +** pages to improve performance is an example of non-essential memory. +** ^sqlite3_release_memory() returns the number of bytes actually freed, +** which might be more or less than the amount requested. +** ^The sqlite3_release_memory() routine is a no-op returning zero +** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. +** +** See also: [sqlite3_db_release_memory()] +*/ +SQLITE_API int sqlite3_release_memory(int); + +/* +** CAPI3REF: Free Memory Used By A Database Connection +** +** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap +** memory as possible from database connection D. Unlike the +** [sqlite3_release_memory()] interface, this interface is effect even +** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is +** omitted. +** +** See also: [sqlite3_release_memory()] +*/ +SQLITE_API int sqlite3_db_release_memory(sqlite3*); + +/* +** CAPI3REF: Impose A Limit On Heap Size +** +** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the +** soft limit on the amount of heap memory that may be allocated by SQLite. +** ^SQLite strives to keep heap memory utilization below the soft heap +** limit by reducing the number of pages held in the page cache +** as heap memory usages approaches the limit. +** ^The soft heap limit is "soft" because even though SQLite strives to stay +** below the limit, it will exceed the limit rather than generate +** an [SQLITE_NOMEM] error. In other words, the soft heap limit +** is advisory only. +** +** ^The return value from sqlite3_soft_heap_limit64() is the size of +** the soft heap limit prior to the call, or negative in the case of an +** error. ^If the argument N is negative +** then no change is made to the soft heap limit. Hence, the current +** size of the soft heap limit can be determined by invoking +** sqlite3_soft_heap_limit64() with a negative argument. +** +** ^If the argument N is zero then the soft heap limit is disabled. +** +** ^(The soft heap limit is not enforced in the current implementation +** if one or more of following conditions are true: +** +**
    +**
  • The soft heap limit is set to zero. +**
  • Memory accounting is disabled using a combination of the +** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and +** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. +**
  • An alternative page cache implementation is specified using +** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). +**
  • The page cache allocates from its own memory pool supplied +** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than +** from the heap. +**
)^ +** +** Beginning with SQLite version 3.7.3, the soft heap limit is enforced +** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT] +** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT], +** the soft heap limit is enforced on every memory allocation. Without +** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced +** when memory is allocated by the page cache. Testing suggests that because +** the page cache is the predominate memory user in SQLite, most +** applications will achieve adequate soft heap limit enforcement without +** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. +** +** The circumstances under which SQLite will enforce the soft heap limit may +** changes in future releases of SQLite. +*/ +SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); + +/* +** CAPI3REF: Deprecated Soft Heap Limit Interface +** DEPRECATED +** +** This is a deprecated version of the [sqlite3_soft_heap_limit64()] +** interface. This routine is provided for historical compatibility +** only. All new applications should use the +** [sqlite3_soft_heap_limit64()] interface rather than this one. +*/ +SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); + + +/* +** CAPI3REF: Extract Metadata About A Column Of A Table +** +** ^This routine returns metadata about a specific column of a specific +** database table accessible using the [database connection] handle +** passed as the first function argument. +** +** ^The column is identified by the second, third and fourth parameters to +** this function. ^The second parameter is either the name of the database +** (i.e. "main", "temp", or an attached database) containing the specified +** table or NULL. ^If it is NULL, then all attached databases are searched +** for the table using the same algorithm used by the database engine to +** resolve unqualified table references. +** +** ^The third and fourth parameters to this function are the table and column +** name of the desired column, respectively. Neither of these parameters +** may be NULL. +** +** ^Metadata is returned by writing to the memory locations passed as the 5th +** and subsequent parameters to this function. ^Any of these arguments may be +** NULL, in which case the corresponding element of metadata is omitted. +** +** ^(
+** +**
Parameter Output
Type
Description +** +**
5th const char* Data type +**
6th const char* Name of default collation sequence +**
7th int True if column has a NOT NULL constraint +**
8th int True if column is part of the PRIMARY KEY +**
9th int True if column is [AUTOINCREMENT] +**
+**
)^ +** +** ^The memory pointed to by the character pointers returned for the +** declaration type and collation sequence is valid only until the next +** call to any SQLite API function. +** +** ^If the specified table is actually a view, an [error code] is returned. +** +** ^If the specified column is "rowid", "oid" or "_rowid_" and an +** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output +** parameters are set for the explicitly declared column. ^(If there is no +** explicitly declared [INTEGER PRIMARY KEY] column, then the output +** parameters are set as follows: +** +**
+**     data type: "INTEGER"
+**     collation sequence: "BINARY"
+**     not null: 0
+**     primary key: 1
+**     auto increment: 0
+** 
)^ +** +** ^(This function may load one or more schemas from database files. If an +** error occurs during this process, or if the requested table or column +** cannot be found, an [error code] is returned and an error message left +** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^ +** +** ^This API is only available if the library was compiled with the +** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. +*/ +SQLITE_API int sqlite3_table_column_metadata( + sqlite3 *db, /* Connection handle */ + const char *zDbName, /* Database name or NULL */ + const char *zTableName, /* Table name */ + const char *zColumnName, /* Column name */ + char const **pzDataType, /* OUTPUT: Declared data type */ + char const **pzCollSeq, /* OUTPUT: Collation sequence name */ + int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ + int *pPrimaryKey, /* OUTPUT: True if column part of PK */ + int *pAutoinc /* OUTPUT: True if column is auto-increment */ +); + +/* +** CAPI3REF: Load An Extension +** +** ^This interface loads an SQLite extension library from the named file. +** +** ^The sqlite3_load_extension() interface attempts to load an +** SQLite extension library contained in the file zFile. +** +** ^The entry point is zProc. +** ^zProc may be 0, in which case the name of the entry point +** defaults to "sqlite3_extension_init". +** ^The sqlite3_load_extension() interface returns +** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. +** ^If an error occurs and pzErrMsg is not 0, then the +** [sqlite3_load_extension()] interface shall attempt to +** fill *pzErrMsg with error message text stored in memory +** obtained from [sqlite3_malloc()]. The calling function +** should free this memory by calling [sqlite3_free()]. +** +** ^Extension loading must be enabled using +** [sqlite3_enable_load_extension()] prior to calling this API, +** otherwise an error will be returned. +** +** See also the [load_extension() SQL function]. +*/ +SQLITE_API int sqlite3_load_extension( + sqlite3 *db, /* Load the extension into this database connection */ + const char *zFile, /* Name of the shared library containing extension */ + const char *zProc, /* Entry point. Derived from zFile if 0 */ + char **pzErrMsg /* Put error message here if not 0 */ +); + +/* +** CAPI3REF: Enable Or Disable Extension Loading +** +** ^So as not to open security holes in older applications that are +** unprepared to deal with extension loading, and as a means of disabling +** extension loading while evaluating user-entered SQL, the following API +** is provided to turn the [sqlite3_load_extension()] mechanism on and off. +** +** ^Extension loading is off by default. See ticket #1863. +** ^Call the sqlite3_enable_load_extension() routine with onoff==1 +** to turn extension loading on and call it with onoff==0 to turn +** it back off again. +*/ +SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); + +/* +** CAPI3REF: Automatically Load Statically Linked Extensions +** +** ^This interface causes the xEntryPoint() function to be invoked for +** each new [database connection] that is created. The idea here is that +** xEntryPoint() is the entry point for a statically linked SQLite extension +** that is to be automatically loaded into all new database connections. +** +** ^(Even though the function prototype shows that xEntryPoint() takes +** no arguments and returns void, SQLite invokes xEntryPoint() with three +** arguments and expects and integer result as if the signature of the +** entry point where as follows: +** +**
+**    int xEntryPoint(
+**      sqlite3 *db,
+**      const char **pzErrMsg,
+**      const struct sqlite3_api_routines *pThunk
+**    );
+** 
)^ +** +** If the xEntryPoint routine encounters an error, it should make *pzErrMsg +** point to an appropriate error message (obtained from [sqlite3_mprintf()]) +** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg +** is NULL before calling the xEntryPoint(). ^SQLite will invoke +** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any +** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], +** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. +** +** ^Calling sqlite3_auto_extension(X) with an entry point X that is already +** on the list of automatic extensions is a harmless no-op. ^No entry point +** will be called more than once for each database connection that is opened. +** +** See also: [sqlite3_reset_auto_extension()]. +*/ +SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); + +/* +** CAPI3REF: Reset Automatic Extension Loading +** +** ^This interface disables all automatic extensions previously +** registered using [sqlite3_auto_extension()]. +*/ +SQLITE_API void sqlite3_reset_auto_extension(void); + +/* +** The interface to the virtual-table mechanism is currently considered +** to be experimental. The interface might change in incompatible ways. +** If this is a problem for you, do not use the interface at this time. +** +** When the virtual-table mechanism stabilizes, we will declare the +** interface fixed, support it indefinitely, and remove this comment. +*/ + +/* +** Structures used by the virtual table interface +*/ +typedef struct sqlite3_vtab sqlite3_vtab; +typedef struct sqlite3_index_info sqlite3_index_info; +typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; +typedef struct sqlite3_module sqlite3_module; + +/* +** CAPI3REF: Virtual Table Object +** KEYWORDS: sqlite3_module {virtual table module} +** +** This structure, sometimes called a "virtual table module", +** defines the implementation of a [virtual tables]. +** This structure consists mostly of methods for the module. +** +** ^A virtual table module is created by filling in a persistent +** instance of this structure and passing a pointer to that instance +** to [sqlite3_create_module()] or [sqlite3_create_module_v2()]. +** ^The registration remains valid until it is replaced by a different +** module or until the [database connection] closes. The content +** of this structure must not change while it is registered with +** any database connection. +*/ +struct sqlite3_module { + int iVersion; + int (*xCreate)(sqlite3*, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**); + int (*xConnect)(sqlite3*, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**); + int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*); + int (*xDisconnect)(sqlite3_vtab *pVTab); + int (*xDestroy)(sqlite3_vtab *pVTab); + int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor); + int (*xClose)(sqlite3_vtab_cursor*); + int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, + int argc, sqlite3_value **argv); + int (*xNext)(sqlite3_vtab_cursor*); + int (*xEof)(sqlite3_vtab_cursor*); + int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int); + int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid); + int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *); + int (*xBegin)(sqlite3_vtab *pVTab); + int (*xSync)(sqlite3_vtab *pVTab); + int (*xCommit)(sqlite3_vtab *pVTab); + int (*xRollback)(sqlite3_vtab *pVTab); + int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName, + void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), + void **ppArg); + int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); + /* The methods above are in version 1 of the sqlite_module object. Those + ** below are for version 2 and greater. */ + int (*xSavepoint)(sqlite3_vtab *pVTab, int); + int (*xRelease)(sqlite3_vtab *pVTab, int); + int (*xRollbackTo)(sqlite3_vtab *pVTab, int); +}; + +/* +** CAPI3REF: Virtual Table Indexing Information +** KEYWORDS: sqlite3_index_info +** +** The sqlite3_index_info structure and its substructures is used as part +** of the [virtual table] interface to +** pass information into and receive the reply from the [xBestIndex] +** method of a [virtual table module]. The fields under **Inputs** are the +** inputs to xBestIndex and are read-only. xBestIndex inserts its +** results into the **Outputs** fields. +** +** ^(The aConstraint[] array records WHERE clause constraints of the form: +** +**
column OP expr
+** +** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is +** stored in aConstraint[].op using one of the +** [SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_ values].)^ +** ^(The index of the column is stored in +** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the +** expr on the right-hand side can be evaluated (and thus the constraint +** is usable) and false if it cannot.)^ +** +** ^The optimizer automatically inverts terms of the form "expr OP column" +** and makes other simplifications to the WHERE clause in an attempt to +** get as many WHERE clause terms into the form shown above as possible. +** ^The aConstraint[] array only reports WHERE clause terms that are +** relevant to the particular virtual table being queried. +** +** ^Information about the ORDER BY clause is stored in aOrderBy[]. +** ^Each term of aOrderBy records a column of the ORDER BY clause. +** +** The [xBestIndex] method must fill aConstraintUsage[] with information +** about what parameters to pass to xFilter. ^If argvIndex>0 then +** the right-hand side of the corresponding aConstraint[] is evaluated +** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit +** is true, then the constraint is assumed to be fully handled by the +** virtual table and is not checked again by SQLite.)^ +** +** ^The idxNum and idxPtr values are recorded and passed into the +** [xFilter] method. +** ^[sqlite3_free()] is used to free idxPtr if and only if +** needToFreeIdxPtr is true. +** +** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in +** the correct order to satisfy the ORDER BY clause so that no separate +** sorting step is required. +** +** ^The estimatedCost value is an estimate of the cost of doing the +** particular lookup. A full scan of a table with N entries should have +** a cost of N. A binary search of a table of N entries should have a +** cost of approximately log(N). +*/ +struct sqlite3_index_info { + /* Inputs */ + int nConstraint; /* Number of entries in aConstraint */ + struct sqlite3_index_constraint { + int iColumn; /* Column on left-hand side of constraint */ + unsigned char op; /* Constraint operator */ + unsigned char usable; /* True if this constraint is usable */ + int iTermOffset; /* Used internally - xBestIndex should ignore */ + } *aConstraint; /* Table of WHERE clause constraints */ + int nOrderBy; /* Number of terms in the ORDER BY clause */ + struct sqlite3_index_orderby { + int iColumn; /* Column number */ + unsigned char desc; /* True for DESC. False for ASC. */ + } *aOrderBy; /* The ORDER BY clause */ + /* Outputs */ + struct sqlite3_index_constraint_usage { + int argvIndex; /* if >0, constraint is part of argv to xFilter */ + unsigned char omit; /* Do not code a test for this constraint */ + } *aConstraintUsage; + int idxNum; /* Number used to identify the index */ + char *idxStr; /* String, possibly obtained from sqlite3_malloc */ + int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ + int orderByConsumed; /* True if output is already ordered */ + double estimatedCost; /* Estimated cost of using this index */ +}; + +/* +** CAPI3REF: Virtual Table Constraint Operator Codes +** +** These macros defined the allowed values for the +** [sqlite3_index_info].aConstraint[].op field. Each value represents +** an operator that is part of a constraint term in the wHERE clause of +** a query that uses a [virtual table]. +*/ +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 + +/* +** CAPI3REF: Register A Virtual Table Implementation +** +** ^These routines are used to register a new [virtual table module] name. +** ^Module names must be registered before +** creating a new [virtual table] using the module and before using a +** preexisting [virtual table] for the module. +** +** ^The module name is registered on the [database connection] specified +** by the first parameter. ^The name of the module is given by the +** second parameter. ^The third parameter is a pointer to +** the implementation of the [virtual table module]. ^The fourth +** parameter is an arbitrary client data pointer that is passed through +** into the [xCreate] and [xConnect] methods of the virtual table module +** when a new virtual table is be being created or reinitialized. +** +** ^The sqlite3_create_module_v2() interface has a fifth parameter which +** is a pointer to a destructor for the pClientData. ^SQLite will +** invoke the destructor function (if it is not NULL) when SQLite +** no longer needs the pClientData pointer. ^The destructor will also +** be invoked if the call to sqlite3_create_module_v2() fails. +** ^The sqlite3_create_module() +** interface is equivalent to sqlite3_create_module_v2() with a NULL +** destructor. +*/ +SQLITE_API int sqlite3_create_module( + sqlite3 *db, /* SQLite connection to register module with */ + const char *zName, /* Name of the module */ + const sqlite3_module *p, /* Methods for the module */ + void *pClientData /* Client data for xCreate/xConnect */ +); +SQLITE_API int sqlite3_create_module_v2( + sqlite3 *db, /* SQLite connection to register module with */ + const char *zName, /* Name of the module */ + const sqlite3_module *p, /* Methods for the module */ + void *pClientData, /* Client data for xCreate/xConnect */ + void(*xDestroy)(void*) /* Module destructor function */ +); + +/* +** CAPI3REF: Virtual Table Instance Object +** KEYWORDS: sqlite3_vtab +** +** Every [virtual table module] implementation uses a subclass +** of this object to describe a particular instance +** of the [virtual table]. Each subclass will +** be tailored to the specific needs of the module implementation. +** The purpose of this superclass is to define certain fields that are +** common to all module implementations. +** +** ^Virtual tables methods can set an error message by assigning a +** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should +** take care that any prior string is freed by a call to [sqlite3_free()] +** prior to assigning a new string to zErrMsg. ^After the error message +** is delivered up to the client application, the string will be automatically +** freed by sqlite3_free() and the zErrMsg field will be zeroed. +*/ +struct sqlite3_vtab { + const sqlite3_module *pModule; /* The module for this virtual table */ + int nRef; /* NO LONGER USED */ + char *zErrMsg; /* Error message from sqlite3_mprintf() */ + /* Virtual table implementations will typically add additional fields */ +}; + +/* +** CAPI3REF: Virtual Table Cursor Object +** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} +** +** Every [virtual table module] implementation uses a subclass of the +** following structure to describe cursors that point into the +** [virtual table] and are used +** to loop through the virtual table. Cursors are created using the +** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed +** by the [sqlite3_module.xClose | xClose] method. Cursors are used +** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods +** of the module. Each module implementation will define +** the content of a cursor structure to suit its own needs. +** +** This superclass exists in order to define fields of the cursor that +** are common to all implementations. +*/ +struct sqlite3_vtab_cursor { + sqlite3_vtab *pVtab; /* Virtual table of this cursor */ + /* Virtual table implementations will typically add additional fields */ +}; + +/* +** CAPI3REF: Declare The Schema Of A Virtual Table +** +** ^The [xCreate] and [xConnect] methods of a +** [virtual table module] call this interface +** to declare the format (the names and datatypes of the columns) of +** the virtual tables they implement. +*/ +SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); + +/* +** CAPI3REF: Overload A Function For A Virtual Table +** +** ^(Virtual tables can provide alternative implementations of functions +** using the [xFindFunction] method of the [virtual table module]. +** But global versions of those functions +** must exist in order to be overloaded.)^ +** +** ^(This API makes sure a global version of a function with a particular +** name and number of parameters exists. If no such function exists +** before this API is called, a new function is created.)^ ^The implementation +** of the new function always causes an exception to be thrown. So +** the new function is not good for anything by itself. Its only +** purpose is to be a placeholder function that can be overloaded +** by a [virtual table]. +*/ +SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); + +/* +** The interface to the virtual-table mechanism defined above (back up +** to a comment remarkably similar to this one) is currently considered +** to be experimental. The interface might change in incompatible ways. +** If this is a problem for you, do not use the interface at this time. +** +** When the virtual-table mechanism stabilizes, we will declare the +** interface fixed, support it indefinitely, and remove this comment. +*/ + +/* +** CAPI3REF: A Handle To An Open BLOB +** KEYWORDS: {BLOB handle} {BLOB handles} +** +** An instance of this object represents an open BLOB on which +** [sqlite3_blob_open | incremental BLOB I/O] can be performed. +** ^Objects of this type are created by [sqlite3_blob_open()] +** and destroyed by [sqlite3_blob_close()]. +** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces +** can be used to read or write small subsections of the BLOB. +** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. +*/ +typedef struct sqlite3_blob sqlite3_blob; + +/* +** CAPI3REF: Open A BLOB For Incremental I/O +** +** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located +** in row iRow, column zColumn, table zTable in database zDb; +** in other words, the same BLOB that would be selected by: +** +**
+**     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
+** 
)^ +** +** ^If the flags parameter is non-zero, then the BLOB is opened for read +** and write access. ^If it is zero, the BLOB is opened for read access. +** ^It is not possible to open a column that is part of an index or primary +** key for writing. ^If [foreign key constraints] are enabled, it is +** not possible to open a column that is part of a [child key] for writing. +** +** ^Note that the database name is not the filename that contains +** the database but rather the symbolic name of the database that +** appears after the AS keyword when the database is connected using [ATTACH]. +** ^For the main database file, the database name is "main". +** ^For TEMP tables, the database name is "temp". +** +** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written +** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set +** to be a null pointer.)^ +** ^This function sets the [database connection] error code and message +** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related +** functions. ^Note that the *ppBlob variable is always initialized in a +** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob +** regardless of the success or failure of this routine. +** +** ^(If the row that a BLOB handle points to is modified by an +** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects +** then the BLOB handle is marked as "expired". +** This is true if any column of the row is changed, even a column +** other than the one the BLOB handle is open on.)^ +** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for +** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. +** ^(Changes written into a BLOB prior to the BLOB expiring are not +** rolled back by the expiration of the BLOB. Such changes will eventually +** commit if the transaction continues to completion.)^ +** +** ^Use the [sqlite3_blob_bytes()] interface to determine the size of +** the opened blob. ^The size of a blob may not be changed by this +** interface. Use the [UPDATE] SQL command to change the size of a +** blob. +** +** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces +** and the built-in [zeroblob] SQL function can be used, if desired, +** to create an empty, zero-filled blob in which to read or write using +** this interface. +** +** To avoid a resource leak, every open [BLOB handle] should eventually +** be released by a call to [sqlite3_blob_close()]. +*/ +SQLITE_API int sqlite3_blob_open( + sqlite3*, + const char *zDb, + const char *zTable, + const char *zColumn, + sqlite3_int64 iRow, + int flags, + sqlite3_blob **ppBlob +); + +/* +** CAPI3REF: Move a BLOB Handle to a New Row +** +** ^This function is used to move an existing blob handle so that it points +** to a different row of the same database table. ^The new row is identified +** by the rowid value passed as the second argument. Only the row can be +** changed. ^The database, table and column on which the blob handle is open +** remain the same. Moving an existing blob handle to a new row can be +** faster than closing the existing handle and opening a new one. +** +** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] - +** it must exist and there must be either a blob or text value stored in +** the nominated column.)^ ^If the new row is not present in the table, or if +** it does not contain a blob or text value, or if another error occurs, an +** SQLite error code is returned and the blob handle is considered aborted. +** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or +** [sqlite3_blob_reopen()] on an aborted blob handle immediately return +** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle +** always returns zero. +** +** ^This function sets the database handle error code and message. +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); + +/* +** CAPI3REF: Close A BLOB Handle +** +** ^Closes an open [BLOB handle]. +** +** ^Closing a BLOB shall cause the current transaction to commit +** if there are no other BLOBs, no pending prepared statements, and the +** database connection is in [autocommit mode]. +** ^If any writes were made to the BLOB, they might be held in cache +** until the close operation if they will fit. +** +** ^(Closing the BLOB often forces the changes +** out to disk and so if any I/O errors occur, they will likely occur +** at the time when the BLOB is closed. Any errors that occur during +** closing are reported as a non-zero return value.)^ +** +** ^(The BLOB is closed unconditionally. Even if this routine returns +** an error code, the BLOB is still closed.)^ +** +** ^Calling this routine with a null pointer (such as would be returned +** by a failed call to [sqlite3_blob_open()]) is a harmless no-op. +*/ +SQLITE_API int sqlite3_blob_close(sqlite3_blob *); + +/* +** CAPI3REF: Return The Size Of An Open BLOB +** +** ^Returns the size in bytes of the BLOB accessible via the +** successfully opened [BLOB handle] in its only argument. ^The +** incremental blob I/O routines can only read or overwriting existing +** blob content; they cannot change the size of a blob. +** +** This routine only works on a [BLOB handle] which has been created +** by a prior successful call to [sqlite3_blob_open()] and which has not +** been closed by [sqlite3_blob_close()]. Passing any other pointer in +** to this routine results in undefined and probably undesirable behavior. +*/ +SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); + +/* +** CAPI3REF: Read Data From A BLOB Incrementally +** +** ^(This function is used to read data from an open [BLOB handle] into a +** caller-supplied buffer. N bytes of data are copied into buffer Z +** from the open BLOB, starting at offset iOffset.)^ +** +** ^If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is +** less than zero, [SQLITE_ERROR] is returned and no data is read. +** ^The size of the blob (and hence the maximum value of N+iOffset) +** can be determined using the [sqlite3_blob_bytes()] interface. +** +** ^An attempt to read from an expired [BLOB handle] fails with an +** error code of [SQLITE_ABORT]. +** +** ^(On success, sqlite3_blob_read() returns SQLITE_OK. +** Otherwise, an [error code] or an [extended error code] is returned.)^ +** +** This routine only works on a [BLOB handle] which has been created +** by a prior successful call to [sqlite3_blob_open()] and which has not +** been closed by [sqlite3_blob_close()]. Passing any other pointer in +** to this routine results in undefined and probably undesirable behavior. +** +** See also: [sqlite3_blob_write()]. +*/ +SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); + +/* +** CAPI3REF: Write Data Into A BLOB Incrementally +** +** ^This function is used to write data into an open [BLOB handle] from a +** caller-supplied buffer. ^N bytes of data are copied from the buffer Z +** into the open BLOB, starting at offset iOffset. +** +** ^If the [BLOB handle] passed as the first argument was not opened for +** writing (the flags parameter to [sqlite3_blob_open()] was zero), +** this function returns [SQLITE_READONLY]. +** +** ^This function may only modify the contents of the BLOB; it is +** not possible to increase the size of a BLOB using this API. +** ^If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is written. ^If N is +** less than zero [SQLITE_ERROR] is returned and no data is written. +** The size of the BLOB (and hence the maximum value of N+iOffset) +** can be determined using the [sqlite3_blob_bytes()] interface. +** +** ^An attempt to write to an expired [BLOB handle] fails with an +** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred +** before the [BLOB handle] expired are not rolled back by the +** expiration of the handle, though of course those changes might +** have been overwritten by the statement that expired the BLOB handle +** or by other independent statements. +** +** ^(On success, sqlite3_blob_write() returns SQLITE_OK. +** Otherwise, an [error code] or an [extended error code] is returned.)^ +** +** This routine only works on a [BLOB handle] which has been created +** by a prior successful call to [sqlite3_blob_open()] and which has not +** been closed by [sqlite3_blob_close()]. Passing any other pointer in +** to this routine results in undefined and probably undesirable behavior. +** +** See also: [sqlite3_blob_read()]. +*/ +SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); + +/* +** CAPI3REF: Virtual File System Objects +** +** A virtual filesystem (VFS) is an [sqlite3_vfs] object +** that SQLite uses to interact +** with the underlying operating system. Most SQLite builds come with a +** single default VFS that is appropriate for the host computer. +** New VFSes can be registered and existing VFSes can be unregistered. +** The following interfaces are provided. +** +** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. +** ^Names are case sensitive. +** ^Names are zero-terminated UTF-8 strings. +** ^If there is no match, a NULL pointer is returned. +** ^If zVfsName is NULL then the default VFS is returned. +** +** ^New VFSes are registered with sqlite3_vfs_register(). +** ^Each new VFS becomes the default VFS if the makeDflt flag is set. +** ^The same VFS can be registered multiple times without injury. +** ^To make an existing VFS into the default VFS, register it again +** with the makeDflt flag set. If two different VFSes with the +** same name are registered, the behavior is undefined. If a +** VFS is registered with a name that is NULL or an empty string, +** then the behavior is undefined. +** +** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. +** ^(If the default VFS is unregistered, another VFS is chosen as +** the default. The choice for the new VFS is arbitrary.)^ +*/ +SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); +SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); +SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); + +/* +** CAPI3REF: Mutexes +** +** The SQLite core uses these routines for thread +** synchronization. Though they are intended for internal +** use by SQLite, code that links against SQLite is +** permitted to use any of these routines. +** +** The SQLite source code contains multiple implementations +** of these mutex routines. An appropriate implementation +** is selected automatically at compile-time. ^(The following +** implementations are available in the SQLite core: +** +**
    +**
  • SQLITE_MUTEX_PTHREADS +**
  • SQLITE_MUTEX_W32 +**
  • SQLITE_MUTEX_NOOP +**
)^ +** +** ^The SQLITE_MUTEX_NOOP implementation is a set of routines +** that does no real locking and is appropriate for use in +** a single-threaded application. ^The SQLITE_MUTEX_PTHREADS and +** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix +** and Windows. +** +** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor +** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex +** implementation is included with the library. In this case the +** application must supply a custom mutex implementation using the +** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function +** before calling sqlite3_initialize() or any other public sqlite3_ +** function that calls sqlite3_initialize().)^ +** +** ^The sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. ^If it returns NULL +** that means that a mutex could not be allocated. ^SQLite +** will unwind its stack and return an error. ^(The argument +** to sqlite3_mutex_alloc() is one of these integer constants: +** +**
    +**
  • SQLITE_MUTEX_FAST +**
  • SQLITE_MUTEX_RECURSIVE +**
  • SQLITE_MUTEX_STATIC_MASTER +**
  • SQLITE_MUTEX_STATIC_MEM +**
  • SQLITE_MUTEX_STATIC_MEM2 +**
  • SQLITE_MUTEX_STATIC_PRNG +**
  • SQLITE_MUTEX_STATIC_LRU +**
  • SQLITE_MUTEX_STATIC_LRU2 +**
)^ +** +** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) +** cause sqlite3_mutex_alloc() to create +** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE +** is used but not necessarily so when SQLITE_MUTEX_FAST is used. +** The mutex implementation does not need to make a distinction +** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does +** not want to. ^SQLite will only request a recursive mutex in +** cases where it really needs one. ^If a faster non-recursive mutex +** implementation is available on the host platform, the mutex subsystem +** might return such a mutex in response to SQLITE_MUTEX_FAST. +** +** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other +** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return +** a pointer to a static preexisting mutex. ^Six static mutexes are +** used by the current version of SQLite. Future versions of SQLite +** may add additional static mutexes. Static mutexes are for internal +** use by SQLite only. Applications that use SQLite mutexes should +** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or +** SQLITE_MUTEX_RECURSIVE. +** +** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST +** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() +** returns a different mutex on every call. ^But for the static +** mutex types, the same mutex is returned on every call that has +** the same type number. +** +** ^The sqlite3_mutex_free() routine deallocates a previously +** allocated dynamic mutex. ^SQLite is careful to deallocate every +** dynamic mutex that it allocates. The dynamic mutexes must not be in +** use when they are deallocated. Attempting to deallocate a static +** mutex results in undefined behavior. ^SQLite never deallocates +** a static mutex. +** +** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt +** to enter a mutex. ^If another thread is already within the mutex, +** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return +** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] +** upon successful entry. ^(Mutexes created using +** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. +** In such cases the, +** mutex must be exited an equal number of times before another thread +** can enter.)^ ^(If the same thread tries to enter any other +** kind of mutex more than once, the behavior is undefined. +** SQLite will never exhibit +** such behavior in its own use of mutexes.)^ +** +** ^(Some systems (for example, Windows 95) do not support the operation +** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() +** will always return SQLITE_BUSY. The SQLite core only ever uses +** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^ +** +** ^The sqlite3_mutex_leave() routine exits a mutex that was +** previously entered by the same thread. ^(The behavior +** is undefined if the mutex is not currently entered by the +** calling thread or is not currently allocated. SQLite will +** never do either.)^ +** +** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or +** sqlite3_mutex_leave() is a NULL pointer, then all three routines +** behave as no-ops. +** +** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. +*/ +SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); +SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); + +/* +** CAPI3REF: Mutex Methods Object +** +** An instance of this structure defines the low-level routines +** used to allocate and use mutexes. +** +** Usually, the default mutex implementations provided by SQLite are +** sufficient, however the user has the option of substituting a custom +** implementation for specialized deployments or systems for which SQLite +** does not provide a suitable implementation. In this case, the user +** creates and populates an instance of this structure to pass +** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. +** Additionally, an instance of this structure can be used as an +** output variable when querying the system for the current mutex +** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. +** +** ^The xMutexInit method defined by this structure is invoked as +** part of system initialization by the sqlite3_initialize() function. +** ^The xMutexInit routine is called by SQLite exactly once for each +** effective call to [sqlite3_initialize()]. +** +** ^The xMutexEnd method defined by this structure is invoked as +** part of system shutdown by the sqlite3_shutdown() function. The +** implementation of this method is expected to release all outstanding +** resources obtained by the mutex methods implementation, especially +** those obtained by the xMutexInit method. ^The xMutexEnd() +** interface is invoked exactly once for each call to [sqlite3_shutdown()]. +** +** ^(The remaining seven methods defined by this structure (xMutexAlloc, +** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and +** xMutexNotheld) implement the following interfaces (respectively): +** +**
    +**
  • [sqlite3_mutex_alloc()]
  • +**
  • [sqlite3_mutex_free()]
  • +**
  • [sqlite3_mutex_enter()]
  • +**
  • [sqlite3_mutex_try()]
  • +**
  • [sqlite3_mutex_leave()]
  • +**
  • [sqlite3_mutex_held()]
  • +**
  • [sqlite3_mutex_notheld()]
  • +**
)^ +** +** The only difference is that the public sqlite3_XXX functions enumerated +** above silently ignore any invocations that pass a NULL pointer instead +** of a valid mutex handle. The implementations of the methods defined +** by this structure are not required to handle this case, the results +** of passing a NULL pointer instead of a valid mutex handle are undefined +** (i.e. it is acceptable to provide an implementation that segfaults if +** it is passed a NULL pointer). +** +** The xMutexInit() method must be threadsafe. ^It must be harmless to +** invoke xMutexInit() multiple times within the same process and without +** intervening calls to xMutexEnd(). Second and subsequent calls to +** xMutexInit() must be no-ops. +** +** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] +** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory +** allocation for a static mutex. ^However xMutexAlloc() may use SQLite +** memory allocation for a fast or recursive mutex. +** +** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is +** called, but only if the prior call to xMutexInit returned SQLITE_OK. +** If xMutexInit fails in any way, it is expected to clean up after itself +** prior to returning. +*/ +typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; +struct sqlite3_mutex_methods { + int (*xMutexInit)(void); + int (*xMutexEnd)(void); + sqlite3_mutex *(*xMutexAlloc)(int); + void (*xMutexFree)(sqlite3_mutex *); + void (*xMutexEnter)(sqlite3_mutex *); + int (*xMutexTry)(sqlite3_mutex *); + void (*xMutexLeave)(sqlite3_mutex *); + int (*xMutexHeld)(sqlite3_mutex *); + int (*xMutexNotheld)(sqlite3_mutex *); +}; + +/* +** CAPI3REF: Mutex Verification Routines +** +** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines +** are intended for use inside assert() statements. ^The SQLite core +** never uses these routines except inside an assert() and applications +** are advised to follow the lead of the core. ^The SQLite core only +** provides implementations for these routines when it is compiled +** with the SQLITE_DEBUG flag. ^External mutex implementations +** are only required to provide these routines if SQLITE_DEBUG is +** defined and if NDEBUG is not defined. +** +** ^These routines should return true if the mutex in their argument +** is held or not held, respectively, by the calling thread. +** +** ^The implementation is not required to provide versions of these +** routines that actually work. If the implementation does not provide working +** versions of these routines, it should at least provide stubs that always +** return true so that one does not get spurious assertion failures. +** +** ^If the argument to sqlite3_mutex_held() is a NULL pointer then +** the routine should return 1. This seems counter-intuitive since +** clearly the mutex cannot be held if it does not exist. But +** the reason the mutex does not exist is because the build is not +** using mutexes. And we do not want the assert() containing the +** call to sqlite3_mutex_held() to fail, so a non-zero return is +** the appropriate thing to do. ^The sqlite3_mutex_notheld() +** interface should also return 1 when given a NULL pointer. +*/ +#ifndef NDEBUG +SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); +#endif + +/* +** CAPI3REF: Mutex Types +** +** The [sqlite3_mutex_alloc()] interface takes a single argument +** which is one of these integer constants. +** +** The set of static mutexes may change from one SQLite release to the +** next. Applications that override the built-in mutex logic must be +** prepared to accommodate additional static mutexes. +*/ +#define SQLITE_MUTEX_FAST 0 +#define SQLITE_MUTEX_RECURSIVE 1 +#define SQLITE_MUTEX_STATIC_MASTER 2 +#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ +#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ +#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ +#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ +#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ + +/* +** CAPI3REF: Retrieve the mutex for a database connection +** +** ^This interface returns a pointer the [sqlite3_mutex] object that +** serializes access to the [database connection] given in the argument +** when the [threading mode] is Serialized. +** ^If the [threading mode] is Single-thread or Multi-thread then this +** routine returns a NULL pointer. +*/ +SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); + +/* +** CAPI3REF: Low-Level Control Of Database Files +** +** ^The [sqlite3_file_control()] interface makes a direct call to the +** xFileControl method for the [sqlite3_io_methods] object associated +** with a particular database identified by the second argument. ^The +** name of the database is "main" for the main database or "temp" for the +** TEMP database, or the name that appears after the AS keyword for +** databases that are added using the [ATTACH] SQL command. +** ^A NULL pointer can be used in place of "main" to refer to the +** main database file. +** ^The third and fourth parameters to this routine +** are passed directly through to the second and third parameters of +** the xFileControl method. ^The return value of the xFileControl +** method becomes the return value of this routine. +** +** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes +** a pointer to the underlying [sqlite3_file] object to be written into +** the space pointed to by the 4th parameter. ^The SQLITE_FCNTL_FILE_POINTER +** case is a short-circuit path which does not actually invoke the +** underlying sqlite3_io_methods.xFileControl method. +** +** ^If the second parameter (zDbName) does not match the name of any +** open database file, then SQLITE_ERROR is returned. ^This error +** code is not remembered and will not be recalled by [sqlite3_errcode()] +** or [sqlite3_errmsg()]. The underlying xFileControl method might +** also return SQLITE_ERROR. There is no way to distinguish between +** an incorrect zDbName and an SQLITE_ERROR return from the underlying +** xFileControl method. +** +** See also: [SQLITE_FCNTL_LOCKSTATE] +*/ +SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); + +/* +** CAPI3REF: Testing Interface +** +** ^The sqlite3_test_control() interface is used to read out internal +** state of SQLite and to inject faults into SQLite for testing +** purposes. ^The first parameter is an operation code that determines +** the number, meaning, and operation of all subsequent parameters. +** +** This interface is not for use by applications. It exists solely +** for verifying the correct operation of the SQLite library. Depending +** on how the SQLite library is compiled, this interface might not exist. +** +** The details of the operation codes, their meanings, the parameters +** they take, and what they do are all subject to change without notice. +** Unlike most of the SQLite API, this function is not guaranteed to +** operate consistently from one release to the next. +*/ +SQLITE_API int sqlite3_test_control(int op, ...); + +/* +** CAPI3REF: Testing Interface Operation Codes +** +** These constants are the valid operation code parameters used +** as the first argument to [sqlite3_test_control()]. +** +** These parameters and their meanings are subject to change +** without notice. These values are for testing purposes only. +** Applications should not use any of these parameters or the +** [sqlite3_test_control()] interface. +*/ +#define SQLITE_TESTCTRL_FIRST 5 +#define SQLITE_TESTCTRL_PRNG_SAVE 5 +#define SQLITE_TESTCTRL_PRNG_RESTORE 6 +#define SQLITE_TESTCTRL_PRNG_RESET 7 +#define SQLITE_TESTCTRL_BITVEC_TEST 8 +#define SQLITE_TESTCTRL_FAULT_INSTALL 9 +#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 +#define SQLITE_TESTCTRL_PENDING_BYTE 11 +#define SQLITE_TESTCTRL_ASSERT 12 +#define SQLITE_TESTCTRL_ALWAYS 13 +#define SQLITE_TESTCTRL_RESERVE 14 +#define SQLITE_TESTCTRL_OPTIMIZATIONS 15 +#define SQLITE_TESTCTRL_ISKEYWORD 16 +#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 +#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 +#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 +#define SQLITE_TESTCTRL_LAST 19 + +/* +** CAPI3REF: SQLite Runtime Status +** +** ^This interface is used to retrieve runtime status information +** about the performance of SQLite, and optionally to reset various +** highwater marks. ^The first argument is an integer code for +** the specific parameter to measure. ^(Recognized integer codes +** are of the form [status parameters | SQLITE_STATUS_...].)^ +** ^The current value of the parameter is returned into *pCurrent. +** ^The highest recorded value is returned in *pHighwater. ^If the +** resetFlag is true, then the highest record value is reset after +** *pHighwater is written. ^(Some parameters do not record the highest +** value. For those parameters +** nothing is written into *pHighwater and the resetFlag is ignored.)^ +** ^(Other parameters record only the highwater mark and not the current +** value. For these latter parameters nothing is written into *pCurrent.)^ +** +** ^The sqlite3_status() routine returns SQLITE_OK on success and a +** non-zero [error code] on failure. +** +** This routine is threadsafe but is not atomic. This routine can be +** called while other threads are running the same or different SQLite +** interfaces. However the values returned in *pCurrent and +** *pHighwater reflect the status of SQLite at different points in time +** and it is possible that another thread might change the parameter +** in between the times when *pCurrent and *pHighwater are written. +** +** See also: [sqlite3_db_status()] +*/ +SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); + + +/* +** CAPI3REF: Status Parameters +** KEYWORDS: {status parameters} +** +** These integer constants designate various run-time status parameters +** that can be returned by [sqlite3_status()]. +** +**
+** [[SQLITE_STATUS_MEMORY_USED]] ^(
SQLITE_STATUS_MEMORY_USED
+**
This parameter is the current amount of memory checked out +** using [sqlite3_malloc()], either directly or indirectly. The +** figure includes calls made to [sqlite3_malloc()] by the application +** and internal memory usage by the SQLite library. Scratch memory +** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache +** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in +** this parameter. The amount returned is the sum of the allocation +** sizes as reported by the xSize method in [sqlite3_mem_methods].
)^ +** +** [[SQLITE_STATUS_MALLOC_SIZE]] ^(
SQLITE_STATUS_MALLOC_SIZE
+**
This parameter records the largest memory allocation request +** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their +** internal equivalents). Only the value returned in the +** *pHighwater parameter to [sqlite3_status()] is of interest. +** The value written into the *pCurrent parameter is undefined.
)^ +** +** [[SQLITE_STATUS_MALLOC_COUNT]] ^(
SQLITE_STATUS_MALLOC_COUNT
+**
This parameter records the number of separate memory allocations +** currently checked out.
)^ +** +** [[SQLITE_STATUS_PAGECACHE_USED]] ^(
SQLITE_STATUS_PAGECACHE_USED
+**
This parameter returns the number of pages used out of the +** [pagecache memory allocator] that was configured using +** [SQLITE_CONFIG_PAGECACHE]. The +** value returned is in pages, not in bytes.
)^ +** +** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] +** ^(
SQLITE_STATUS_PAGECACHE_OVERFLOW
+**
This parameter returns the number of bytes of page cache +** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] +** buffer and where forced to overflow to [sqlite3_malloc()]. The +** returned value includes allocations that overflowed because they +** where too large (they were larger than the "sz" parameter to +** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because +** no space was left in the page cache.
)^ +** +** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(
SQLITE_STATUS_PAGECACHE_SIZE
+**
This parameter records the largest memory allocation request +** handed to [pagecache memory allocator]. Only the value returned in the +** *pHighwater parameter to [sqlite3_status()] is of interest. +** The value written into the *pCurrent parameter is undefined.
)^ +** +** [[SQLITE_STATUS_SCRATCH_USED]] ^(
SQLITE_STATUS_SCRATCH_USED
+**
This parameter returns the number of allocations used out of the +** [scratch memory allocator] configured using +** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not +** in bytes. Since a single thread may only have one scratch allocation +** outstanding at time, this parameter also reports the number of threads +** using scratch memory at the same time.
)^ +** +** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(
SQLITE_STATUS_SCRATCH_OVERFLOW
+**
This parameter returns the number of bytes of scratch memory +** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] +** buffer and where forced to overflow to [sqlite3_malloc()]. The values +** returned include overflows because the requested allocation was too +** larger (that is, because the requested allocation was larger than the +** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer +** slots were available. +**
)^ +** +** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(
SQLITE_STATUS_SCRATCH_SIZE
+**
This parameter records the largest memory allocation request +** handed to [scratch memory allocator]. Only the value returned in the +** *pHighwater parameter to [sqlite3_status()] is of interest. +** The value written into the *pCurrent parameter is undefined.
)^ +** +** [[SQLITE_STATUS_PARSER_STACK]] ^(
SQLITE_STATUS_PARSER_STACK
+**
This parameter records the deepest parser stack. It is only +** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].
)^ +**
+** +** New status parameters may be added from time to time. +*/ +#define SQLITE_STATUS_MEMORY_USED 0 +#define SQLITE_STATUS_PAGECACHE_USED 1 +#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2 +#define SQLITE_STATUS_SCRATCH_USED 3 +#define SQLITE_STATUS_SCRATCH_OVERFLOW 4 +#define SQLITE_STATUS_MALLOC_SIZE 5 +#define SQLITE_STATUS_PARSER_STACK 6 +#define SQLITE_STATUS_PAGECACHE_SIZE 7 +#define SQLITE_STATUS_SCRATCH_SIZE 8 +#define SQLITE_STATUS_MALLOC_COUNT 9 + +/* +** CAPI3REF: Database Connection Status +** +** ^This interface is used to retrieve runtime status information +** about a single [database connection]. ^The first argument is the +** database connection object to be interrogated. ^The second argument +** is an integer constant, taken from the set of +** [SQLITE_DBSTATUS options], that +** determines the parameter to interrogate. The set of +** [SQLITE_DBSTATUS options] is likely +** to grow in future releases of SQLite. +** +** ^The current value of the requested parameter is written into *pCur +** and the highest instantaneous value is written into *pHiwtr. ^If +** the resetFlg is true, then the highest instantaneous value is +** reset back down to the current value. +** +** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a +** non-zero [error code] on failure. +** +** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. +*/ +SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); + +/* +** CAPI3REF: Status Parameters for database connections +** KEYWORDS: {SQLITE_DBSTATUS options} +** +** These constants are the available integer "verbs" that can be passed as +** the second argument to the [sqlite3_db_status()] interface. +** +** New verbs may be added in future releases of SQLite. Existing verbs +** might be discontinued. Applications should check the return code from +** [sqlite3_db_status()] to make sure that the call worked. +** The [sqlite3_db_status()] interface will return a non-zero error code +** if a discontinued or unsupported verb is invoked. +** +**
+** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(
SQLITE_DBSTATUS_LOOKASIDE_USED
+**
This parameter returns the number of lookaside memory slots currently +** checked out.
)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(
SQLITE_DBSTATUS_LOOKASIDE_HIT
+**
This parameter returns the number malloc attempts that were +** satisfied using lookaside memory. Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] +** ^(
SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
+**
This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to the amount of +** memory requested being larger than the lookaside slot size. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] +** ^(
SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
+**
This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to all lookaside +** memory already being in use. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_CACHE_USED]] ^(
SQLITE_DBSTATUS_CACHE_USED
+**
This parameter returns the approximate number of of bytes of heap +** memory used by all pager caches associated with the database connection.)^ +** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. +** +** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
SQLITE_DBSTATUS_SCHEMA_USED
+**
This parameter returns the approximate number of of bytes of heap +** memory used to store the schema for all databases associated +** with the connection - main, temp, and any [ATTACH]-ed databases.)^ +** ^The full amount of memory used by the schemas is reported, even if the +** schema memory is shared with other database connections due to +** [shared cache mode] being enabled. +** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. +** +** [[SQLITE_DBSTATUS_STMT_USED]] ^(
SQLITE_DBSTATUS_STMT_USED
+**
This parameter returns the approximate number of of bytes of heap +** and lookaside memory used by all prepared statements associated with +** the database connection.)^ +** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0. +**
+** +** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(
SQLITE_DBSTATUS_CACHE_HIT
+**
This parameter returns the number of pager cache hits that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT +** is always 0. +**
+** +** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(
SQLITE_DBSTATUS_CACHE_MISS
+**
This parameter returns the number of pager cache misses that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS +** is always 0. +**
+** +** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(
SQLITE_DBSTATUS_CACHE_WRITE
+**
This parameter returns the number of dirty cache entries that have +** been written to disk. Specifically, the number of pages written to the +** wal file in wal mode databases, or the number of pages written to the +** database file in rollback mode databases. Any pages written as part of +** transaction rollback or database recovery operations are not included. +** If an IO or other error occurs while writing a page to disk, the effect +** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The +** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. +**
+**
+*/ +#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 +#define SQLITE_DBSTATUS_CACHE_USED 1 +#define SQLITE_DBSTATUS_SCHEMA_USED 2 +#define SQLITE_DBSTATUS_STMT_USED 3 +#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 +#define SQLITE_DBSTATUS_CACHE_HIT 7 +#define SQLITE_DBSTATUS_CACHE_MISS 8 +#define SQLITE_DBSTATUS_CACHE_WRITE 9 +#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */ + + +/* +** CAPI3REF: Prepared Statement Status +** +** ^(Each prepared statement maintains various +** [SQLITE_STMTSTATUS counters] that measure the number +** of times it has performed specific operations.)^ These counters can +** be used to monitor the performance characteristics of the prepared +** statements. For example, if the number of table steps greatly exceeds +** the number of table searches or result rows, that would tend to indicate +** that the prepared statement is using a full table scan rather than +** an index. +** +** ^(This interface is used to retrieve and reset counter values from +** a [prepared statement]. The first argument is the prepared statement +** object to be interrogated. The second argument +** is an integer code for a specific [SQLITE_STMTSTATUS counter] +** to be interrogated.)^ +** ^The current value of the requested counter is returned. +** ^If the resetFlg is true, then the counter is reset to zero after this +** interface call returns. +** +** See also: [sqlite3_status()] and [sqlite3_db_status()]. +*/ +SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); + +/* +** CAPI3REF: Status Parameters for prepared statements +** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters} +** +** These preprocessor macros define integer codes that name counter +** values associated with the [sqlite3_stmt_status()] interface. +** The meanings of the various counters are as follows: +** +**
+** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]]
SQLITE_STMTSTATUS_FULLSCAN_STEP
+**
^This is the number of times that SQLite has stepped forward in +** a table as part of a full table scan. Large numbers for this counter +** may indicate opportunities for performance improvement through +** careful use of indices.
+** +** [[SQLITE_STMTSTATUS_SORT]]
SQLITE_STMTSTATUS_SORT
+**
^This is the number of sort operations that have occurred. +** A non-zero value in this counter may indicate an opportunity to +** improvement performance through careful use of indices.
+** +** [[SQLITE_STMTSTATUS_AUTOINDEX]]
SQLITE_STMTSTATUS_AUTOINDEX
+**
^This is the number of rows inserted into transient indices that +** were created automatically in order to help joins run faster. +** A non-zero value in this counter may indicate an opportunity to +** improvement performance by adding permanent indices that do not +** need to be reinitialized each time the statement is run.
+**
+*/ +#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 +#define SQLITE_STMTSTATUS_SORT 2 +#define SQLITE_STMTSTATUS_AUTOINDEX 3 + +/* +** CAPI3REF: Custom Page Cache Object +** +** The sqlite3_pcache type is opaque. It is implemented by +** the pluggable module. The SQLite core has no knowledge of +** its size or internal structure and never deals with the +** sqlite3_pcache object except by holding and passing pointers +** to the object. +** +** See [sqlite3_pcache_methods2] for additional information. +*/ +typedef struct sqlite3_pcache sqlite3_pcache; + +/* +** CAPI3REF: Custom Page Cache Object +** +** The sqlite3_pcache_page object represents a single page in the +** page cache. The page cache will allocate instances of this +** object. Various methods of the page cache use pointers to instances +** of this object as parameters or as their return value. +** +** See [sqlite3_pcache_methods2] for additional information. +*/ +typedef struct sqlite3_pcache_page sqlite3_pcache_page; +struct sqlite3_pcache_page { + void *pBuf; /* The content of the page */ + void *pExtra; /* Extra information associated with the page */ +}; + +/* +** CAPI3REF: Application Defined Page Cache. +** KEYWORDS: {page cache} +** +** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can +** register an alternative page cache implementation by passing in an +** instance of the sqlite3_pcache_methods2 structure.)^ +** In many applications, most of the heap memory allocated by +** SQLite is used for the page cache. +** By implementing a +** custom page cache using this API, an application can better control +** the amount of memory consumed by SQLite, the way in which +** that memory is allocated and released, and the policies used to +** determine exactly which parts of a database file are cached and for +** how long. +** +** The alternative page cache mechanism is an +** extreme measure that is only needed by the most demanding applications. +** The built-in page cache is recommended for most uses. +** +** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an +** internal buffer by SQLite within the call to [sqlite3_config]. Hence +** the application may discard the parameter after the call to +** [sqlite3_config()] returns.)^ +** +** [[the xInit() page cache method]] +** ^(The xInit() method is called once for each effective +** call to [sqlite3_initialize()])^ +** (usually only once during the lifetime of the process). ^(The xInit() +** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^ +** The intent of the xInit() method is to set up global data structures +** required by the custom page cache implementation. +** ^(If the xInit() method is NULL, then the +** built-in default page cache is used instead of the application defined +** page cache.)^ +** +** [[the xShutdown() page cache method]] +** ^The xShutdown() method is called by [sqlite3_shutdown()]. +** It can be used to clean up +** any outstanding resources before process shutdown, if required. +** ^The xShutdown() method may be NULL. +** +** ^SQLite automatically serializes calls to the xInit method, +** so the xInit method need not be threadsafe. ^The +** xShutdown method is only called from [sqlite3_shutdown()] so it does +** not need to be threadsafe either. All other methods must be threadsafe +** in multithreaded applications. +** +** ^SQLite will never invoke xInit() more than once without an intervening +** call to xShutdown(). +** +** [[the xCreate() page cache methods]] +** ^SQLite invokes the xCreate() method to construct a new cache instance. +** SQLite will typically create one cache instance for each open database file, +** though this is not guaranteed. ^The +** first parameter, szPage, is the size in bytes of the pages that must +** be allocated by the cache. ^szPage will always a power of two. ^The +** second parameter szExtra is a number of bytes of extra storage +** associated with each page cache entry. ^The szExtra parameter will +** a number less than 250. SQLite will use the +** extra szExtra bytes on each page to store metadata about the underlying +** database page on disk. The value passed into szExtra depends +** on the SQLite version, the target platform, and how SQLite was compiled. +** ^The third argument to xCreate(), bPurgeable, is true if the cache being +** created will be used to cache database pages of a file stored on disk, or +** false if it is used for an in-memory database. The cache implementation +** does not have to do anything special based with the value of bPurgeable; +** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will +** never invoke xUnpin() except to deliberately delete a page. +** ^In other words, calls to xUnpin() on a cache with bPurgeable set to +** false will always have the "discard" flag set to true. +** ^Hence, a cache created with bPurgeable false will +** never contain any unpinned pages. +** +** [[the xCachesize() page cache method]] +** ^(The xCachesize() method may be called at any time by SQLite to set the +** suggested maximum cache-size (number of pages stored by) the cache +** instance passed as the first argument. This is the value configured using +** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable +** parameter, the implementation is not required to do anything with this +** value; it is advisory only. +** +** [[the xPagecount() page cache methods]] +** The xPagecount() method must return the number of pages currently +** stored in the cache, both pinned and unpinned. +** +** [[the xFetch() page cache methods]] +** The xFetch() method locates a page in the cache and returns a pointer to +** an sqlite3_pcache_page object associated with that page, or a NULL pointer. +** The pBuf element of the returned sqlite3_pcache_page object will be a +** pointer to a buffer of szPage bytes used to store the content of a +** single database page. The pExtra element of sqlite3_pcache_page will be +** a pointer to the szExtra bytes of extra storage that SQLite has requested +** for each entry in the page cache. +** +** The page to be fetched is determined by the key. ^The minimum key value +** is 1. After it has been retrieved using xFetch, the page is considered +** to be "pinned". +** +** If the requested page is already in the page cache, then the page cache +** implementation must return a pointer to the page buffer with its content +** intact. If the requested page is not already in the cache, then the +** cache implementation should use the value of the createFlag +** parameter to help it determined what action to take: +** +** +**
createFlag Behavior when page is not already in cache +**
0 Do not allocate a new page. Return NULL. +**
1 Allocate a new page if it easy and convenient to do so. +** Otherwise return NULL. +**
2 Make every effort to allocate a new page. Only return +** NULL if allocating a new page is effectively impossible. +**
+** +** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite +** will only use a createFlag of 2 after a prior call with a createFlag of 1 +** failed.)^ In between the to xFetch() calls, SQLite may +** attempt to unpin one or more cache pages by spilling the content of +** pinned pages to disk and synching the operating system disk cache. +** +** [[the xUnpin() page cache method]] +** ^xUnpin() is called by SQLite with a pointer to a currently pinned page +** as its second argument. If the third parameter, discard, is non-zero, +** then the page must be evicted from the cache. +** ^If the discard parameter is +** zero, then the page may be discarded or retained at the discretion of +** page cache implementation. ^The page cache implementation +** may choose to evict unpinned pages at any time. +** +** The cache must not perform any reference counting. A single +** call to xUnpin() unpins the page regardless of the number of prior calls +** to xFetch(). +** +** [[the xRekey() page cache methods]] +** The xRekey() method is used to change the key value associated with the +** page passed as the second argument. If the cache +** previously contains an entry associated with newKey, it must be +** discarded. ^Any prior cache entry associated with newKey is guaranteed not +** to be pinned. +** +** When SQLite calls the xTruncate() method, the cache must discard all +** existing cache entries with page numbers (keys) greater than or equal +** to the value of the iLimit parameter passed to xTruncate(). If any +** of these pages are pinned, they are implicitly unpinned, meaning that +** they can be safely discarded. +** +** [[the xDestroy() page cache method]] +** ^The xDestroy() method is used to delete a cache allocated by xCreate(). +** All resources associated with the specified cache should be freed. ^After +** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] +** handle invalid, and will not use it with any other sqlite3_pcache_methods2 +** functions. +** +** [[the xShrink() page cache method]] +** ^SQLite invokes the xShrink() method when it wants the page cache to +** free up as much of heap memory as possible. The page cache implementation +** is not obligated to free any memory, but well-behaved implementations should +** do their best. +*/ +typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; +struct sqlite3_pcache_methods2 { + int iVersion; + void *pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); + void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, + unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); + void (*xShrink)(sqlite3_pcache*); +}; + +/* +** This is the obsolete pcache_methods object that has now been replaced +** by sqlite3_pcache_methods2. This object is not used by SQLite. It is +** retained in the header file for backwards compatibility only. +*/ +typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; +struct sqlite3_pcache_methods { + void *pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, void*, int discard); + void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); +}; + + +/* +** CAPI3REF: Online Backup Object +** +** The sqlite3_backup object records state information about an ongoing +** online backup operation. ^The sqlite3_backup object is created by +** a call to [sqlite3_backup_init()] and is destroyed by a call to +** [sqlite3_backup_finish()]. +** +** See Also: [Using the SQLite Online Backup API] +*/ +typedef struct sqlite3_backup sqlite3_backup; + +/* +** CAPI3REF: Online Backup API. +** +** The backup API copies the content of one database into another. +** It is useful either for creating backups of databases or +** for copying in-memory databases to or from persistent files. +** +** See Also: [Using the SQLite Online Backup API] +** +** ^SQLite holds a write transaction open on the destination database file +** for the duration of the backup operation. +** ^The source database is read-locked only while it is being read; +** it is not locked continuously for the entire backup operation. +** ^Thus, the backup may be performed on a live source database without +** preventing other database connections from +** reading or writing to the source database while the backup is underway. +** +** ^(To perform a backup operation: +**
    +**
  1. sqlite3_backup_init() is called once to initialize the +** backup, +**
  2. sqlite3_backup_step() is called one or more times to transfer +** the data between the two databases, and finally +**
  3. sqlite3_backup_finish() is called to release all resources +** associated with the backup operation. +**
)^ +** There should be exactly one call to sqlite3_backup_finish() for each +** successful call to sqlite3_backup_init(). +** +** [[sqlite3_backup_init()]] sqlite3_backup_init() +** +** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the +** [database connection] associated with the destination database +** and the database name, respectively. +** ^The database name is "main" for the main database, "temp" for the +** temporary database, or the name specified after the AS keyword in +** an [ATTACH] statement for an attached database. +** ^The S and M arguments passed to +** sqlite3_backup_init(D,N,S,M) identify the [database connection] +** and database name of the source database, respectively. +** ^The source and destination [database connections] (parameters S and D) +** must be different or else sqlite3_backup_init(D,N,S,M) will fail with +** an error. +** +** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is +** returned and an error code and error message are stored in the +** destination [database connection] D. +** ^The error code and message for the failed call to sqlite3_backup_init() +** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or +** [sqlite3_errmsg16()] functions. +** ^A successful call to sqlite3_backup_init() returns a pointer to an +** [sqlite3_backup] object. +** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and +** sqlite3_backup_finish() functions to perform the specified backup +** operation. +** +** [[sqlite3_backup_step()]] sqlite3_backup_step() +** +** ^Function sqlite3_backup_step(B,N) will copy up to N pages between +** the source and destination databases specified by [sqlite3_backup] object B. +** ^If N is negative, all remaining source pages are copied. +** ^If sqlite3_backup_step(B,N) successfully copies N pages and there +** are still more pages to be copied, then the function returns [SQLITE_OK]. +** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages +** from source to destination, then it returns [SQLITE_DONE]. +** ^If an error occurs while running sqlite3_backup_step(B,N), +** then an [error code] is returned. ^As well as [SQLITE_OK] and +** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], +** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an +** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. +** +** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if +**
    +**
  1. the destination database was opened read-only, or +**
  2. the destination database is using write-ahead-log journaling +** and the destination and source page sizes differ, or +**
  3. the destination database is an in-memory database and the +** destination and source page sizes differ. +**
)^ +** +** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then +** the [sqlite3_busy_handler | busy-handler function] +** is invoked (if one is specified). ^If the +** busy-handler returns non-zero before the lock is available, then +** [SQLITE_BUSY] is returned to the caller. ^In this case the call to +** sqlite3_backup_step() can be retried later. ^If the source +** [database connection] +** is being used to write to the source database when sqlite3_backup_step() +** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this +** case the call to sqlite3_backup_step() can be retried later on. ^(If +** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or +** [SQLITE_READONLY] is returned, then +** there is no point in retrying the call to sqlite3_backup_step(). These +** errors are considered fatal.)^ The application must accept +** that the backup operation has failed and pass the backup operation handle +** to the sqlite3_backup_finish() to release associated resources. +** +** ^The first call to sqlite3_backup_step() obtains an exclusive lock +** on the destination file. ^The exclusive lock is not released until either +** sqlite3_backup_finish() is called or the backup operation is complete +** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to +** sqlite3_backup_step() obtains a [shared lock] on the source database that +** lasts for the duration of the sqlite3_backup_step() call. +** ^Because the source database is not locked between calls to +** sqlite3_backup_step(), the source database may be modified mid-way +** through the backup process. ^If the source database is modified by an +** external process or via a database connection other than the one being +** used by the backup operation, then the backup will be automatically +** restarted by the next call to sqlite3_backup_step(). ^If the source +** database is modified by the using the same database connection as is used +** by the backup operation, then the backup database is automatically +** updated at the same time. +** +** [[sqlite3_backup_finish()]] sqlite3_backup_finish() +** +** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the +** application wishes to abandon the backup operation, the application +** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). +** ^The sqlite3_backup_finish() interfaces releases all +** resources associated with the [sqlite3_backup] object. +** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any +** active write-transaction on the destination database is rolled back. +** The [sqlite3_backup] object is invalid +** and may not be used following a call to sqlite3_backup_finish(). +** +** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no +** sqlite3_backup_step() errors occurred, regardless or whether or not +** sqlite3_backup_step() completed. +** ^If an out-of-memory condition or IO error occurred during any prior +** sqlite3_backup_step() call on the same [sqlite3_backup] object, then +** sqlite3_backup_finish() returns the corresponding [error code]. +** +** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() +** is not a permanent error and does not affect the return value of +** sqlite3_backup_finish(). +** +** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] +** sqlite3_backup_remaining() and sqlite3_backup_pagecount() +** +** ^Each call to sqlite3_backup_step() sets two values inside +** the [sqlite3_backup] object: the number of pages still to be backed +** up and the total number of pages in the source database file. +** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces +** retrieve these two values, respectively. +** +** ^The values returned by these functions are only updated by +** sqlite3_backup_step(). ^If the source database is modified during a backup +** operation, then the values are not updated to account for any extra +** pages that need to be updated or the size of the source database file +** changing. +** +** Concurrent Usage of Database Handles +** +** ^The source [database connection] may be used by the application for other +** purposes while a backup operation is underway or being initialized. +** ^If SQLite is compiled and configured to support threadsafe database +** connections, then the source database connection may be used concurrently +** from within other threads. +** +** However, the application must guarantee that the destination +** [database connection] is not passed to any other API (by any thread) after +** sqlite3_backup_init() is called and before the corresponding call to +** sqlite3_backup_finish(). SQLite does not currently check to see +** if the application incorrectly accesses the destination [database connection] +** and so no error code is reported, but the operations may malfunction +** nevertheless. Use of the destination database connection while a +** backup is in progress might also also cause a mutex deadlock. +** +** If running in [shared cache mode], the application must +** guarantee that the shared cache used by the destination database +** is not accessed while the backup is running. In practice this means +** that the application must guarantee that the disk file being +** backed up to is not accessed by any connection within the process, +** not just the specific connection that was passed to sqlite3_backup_init(). +** +** The [sqlite3_backup] object itself is partially threadsafe. Multiple +** threads may safely make multiple concurrent calls to sqlite3_backup_step(). +** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() +** APIs are not strictly speaking threadsafe. If they are invoked at the +** same time as another thread is invoking sqlite3_backup_step() it is +** possible that they return invalid values. +*/ +SQLITE_API sqlite3_backup *sqlite3_backup_init( + sqlite3 *pDest, /* Destination database handle */ + const char *zDestName, /* Destination database name */ + sqlite3 *pSource, /* Source database handle */ + const char *zSourceName /* Source database name */ +); +SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); +SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); +SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); +SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); + +/* +** CAPI3REF: Unlock Notification +** +** ^When running in shared-cache mode, a database operation may fail with +** an [SQLITE_LOCKED] error if the required locks on the shared-cache or +** individual tables within the shared-cache cannot be obtained. See +** [SQLite Shared-Cache Mode] for a description of shared-cache locking. +** ^This API may be used to register a callback that SQLite will invoke +** when the connection currently holding the required lock relinquishes it. +** ^This API is only available if the library was compiled with the +** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. +** +** See Also: [Using the SQLite Unlock Notification Feature]. +** +** ^Shared-cache locks are released when a database connection concludes +** its current transaction, either by committing it or rolling it back. +** +** ^When a connection (known as the blocked connection) fails to obtain a +** shared-cache lock and SQLITE_LOCKED is returned to the caller, the +** identity of the database connection (the blocking connection) that +** has locked the required resource is stored internally. ^After an +** application receives an SQLITE_LOCKED error, it may call the +** sqlite3_unlock_notify() method with the blocked connection handle as +** the first argument to register for a callback that will be invoked +** when the blocking connections current transaction is concluded. ^The +** callback is invoked from within the [sqlite3_step] or [sqlite3_close] +** call that concludes the blocking connections transaction. +** +** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, +** there is a chance that the blocking connection will have already +** concluded its transaction by the time sqlite3_unlock_notify() is invoked. +** If this happens, then the specified callback is invoked immediately, +** from within the call to sqlite3_unlock_notify().)^ +** +** ^If the blocked connection is attempting to obtain a write-lock on a +** shared-cache table, and more than one other connection currently holds +** a read-lock on the same table, then SQLite arbitrarily selects one of +** the other connections to use as the blocking connection. +** +** ^(There may be at most one unlock-notify callback registered by a +** blocked connection. If sqlite3_unlock_notify() is called when the +** blocked connection already has a registered unlock-notify callback, +** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is +** called with a NULL pointer as its second argument, then any existing +** unlock-notify callback is canceled. ^The blocked connections +** unlock-notify callback may also be canceled by closing the blocked +** connection using [sqlite3_close()]. +** +** The unlock-notify callback is not reentrant. If an application invokes +** any sqlite3_xxx API functions from within an unlock-notify callback, a +** crash or deadlock may be the result. +** +** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always +** returns SQLITE_OK. +** +** Callback Invocation Details +** +** When an unlock-notify callback is registered, the application provides a +** single void* pointer that is passed to the callback when it is invoked. +** However, the signature of the callback function allows SQLite to pass +** it an array of void* context pointers. The first argument passed to +** an unlock-notify callback is a pointer to an array of void* pointers, +** and the second is the number of entries in the array. +** +** When a blocking connections transaction is concluded, there may be +** more than one blocked connection that has registered for an unlock-notify +** callback. ^If two or more such blocked connections have specified the +** same callback function, then instead of invoking the callback function +** multiple times, it is invoked once with the set of void* context pointers +** specified by the blocked connections bundled together into an array. +** This gives the application an opportunity to prioritize any actions +** related to the set of unblocked database connections. +** +** Deadlock Detection +** +** Assuming that after registering for an unlock-notify callback a +** database waits for the callback to be issued before taking any further +** action (a reasonable assumption), then using this API may cause the +** application to deadlock. For example, if connection X is waiting for +** connection Y's transaction to be concluded, and similarly connection +** Y is waiting on connection X's transaction, then neither connection +** will proceed and the system may remain deadlocked indefinitely. +** +** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock +** detection. ^If a given call to sqlite3_unlock_notify() would put the +** system in a deadlocked state, then SQLITE_LOCKED is returned and no +** unlock-notify callback is registered. The system is said to be in +** a deadlocked state if connection A has registered for an unlock-notify +** callback on the conclusion of connection B's transaction, and connection +** B has itself registered for an unlock-notify callback when connection +** A's transaction is concluded. ^Indirect deadlock is also detected, so +** the system is also considered to be deadlocked if connection B has +** registered for an unlock-notify callback on the conclusion of connection +** C's transaction, where connection C is waiting on connection A. ^Any +** number of levels of indirection are allowed. +** +** The "DROP TABLE" Exception +** +** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost +** always appropriate to call sqlite3_unlock_notify(). There is however, +** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement, +** SQLite checks if there are any currently executing SELECT statements +** that belong to the same connection. If there are, SQLITE_LOCKED is +** returned. In this case there is no "blocking connection", so invoking +** sqlite3_unlock_notify() results in the unlock-notify callback being +** invoked immediately. If the application then re-attempts the "DROP TABLE" +** or "DROP INDEX" query, an infinite loop might be the result. +** +** One way around this problem is to check the extended error code returned +** by an sqlite3_step() call. ^(If there is a blocking connection, then the +** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in +** the special "DROP TABLE/INDEX" case, the extended error code is just +** SQLITE_LOCKED.)^ +*/ +SQLITE_API int sqlite3_unlock_notify( + sqlite3 *pBlocked, /* Waiting connection */ + void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ + void *pNotifyArg /* Argument to pass to xNotify */ +); + + +/* +** CAPI3REF: String Comparison +** +** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications +** and extensions to compare the contents of two buffers containing UTF-8 +** strings in a case-independent fashion, using the same definition of "case +** independence" that SQLite uses internally when comparing identifiers. +*/ +SQLITE_API int sqlite3_stricmp(const char *, const char *); +SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); + +/* +** CAPI3REF: Error Logging Interface +** +** ^The [sqlite3_log()] interface writes a message into the error log +** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. +** ^If logging is enabled, the zFormat string and subsequent arguments are +** used with [sqlite3_snprintf()] to generate the final output string. +** +** The sqlite3_log() interface is intended for use by extensions such as +** virtual tables, collating functions, and SQL functions. While there is +** nothing to prevent an application from calling sqlite3_log(), doing so +** is considered bad form. +** +** The zFormat string must not be NULL. +** +** To avoid deadlocks and other threading problems, the sqlite3_log() routine +** will not use dynamically allocated memory. The log message is stored in +** a fixed-length buffer on the stack. If the log message is longer than +** a few hundred characters, it will be truncated to the length of the +** buffer. +*/ +SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); + +/* +** CAPI3REF: Write-Ahead Log Commit Hook +** +** ^The [sqlite3_wal_hook()] function is used to register a callback that +** will be invoked each time a database connection commits data to a +** [write-ahead log] (i.e. whenever a transaction is committed in +** [journal_mode | journal_mode=WAL mode]). +** +** ^The callback is invoked by SQLite after the commit has taken place and +** the associated write-lock on the database released, so the implementation +** may read, write or [checkpoint] the database as required. +** +** ^The first parameter passed to the callback function when it is invoked +** is a copy of the third parameter passed to sqlite3_wal_hook() when +** registering the callback. ^The second is a copy of the database handle. +** ^The third parameter is the name of the database that was written to - +** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter +** is the number of pages currently in the write-ahead log file, +** including those that were just committed. +** +** The callback function should normally return [SQLITE_OK]. ^If an error +** code is returned, that error will propagate back up through the +** SQLite code base to cause the statement that provoked the callback +** to report an error, though the commit will have still occurred. If the +** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value +** that does not correspond to any valid SQLite error code, the results +** are undefined. +** +** A single database handle may have at most a single write-ahead log callback +** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any +** previously registered write-ahead log callback. ^Note that the +** [sqlite3_wal_autocheckpoint()] interface and the +** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will +** those overwrite any prior [sqlite3_wal_hook()] settings. +*/ +SQLITE_API void *sqlite3_wal_hook( + sqlite3*, + int(*)(void *,sqlite3*,const char*,int), + void* +); + +/* +** CAPI3REF: Configure an auto-checkpoint +** +** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around +** [sqlite3_wal_hook()] that causes any database on [database connection] D +** to automatically [checkpoint] +** after committing a transaction if there are N or +** more frames in the [write-ahead log] file. ^Passing zero or +** a negative value as the nFrame parameter disables automatic +** checkpoints entirely. +** +** ^The callback registered by this function replaces any existing callback +** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback +** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism +** configured by this function. +** +** ^The [wal_autocheckpoint pragma] can be used to invoke this interface +** from SQL. +** +** ^Every new [database connection] defaults to having the auto-checkpoint +** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] +** pages. The use of this interface +** is only necessary if the default setting is found to be suboptimal +** for a particular application. +*/ +SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); + +/* +** CAPI3REF: Checkpoint a database +** +** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X +** on [database connection] D to be [checkpointed]. ^If X is NULL or an +** empty string, then a checkpoint is run on all databases of +** connection D. ^If the database connection D is not in +** [WAL | write-ahead log mode] then this interface is a harmless no-op. +** +** ^The [wal_checkpoint pragma] can be used to invoke this interface +** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the +** [wal_autocheckpoint pragma] can be used to cause this interface to be +** run whenever the WAL reaches a certain size threshold. +** +** See also: [sqlite3_wal_checkpoint_v2()] +*/ +SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); + +/* +** CAPI3REF: Checkpoint a database +** +** Run a checkpoint operation on WAL database zDb attached to database +** handle db. The specific operation is determined by the value of the +** eMode parameter: +** +**
+**
SQLITE_CHECKPOINT_PASSIVE
+** Checkpoint as many frames as possible without waiting for any database +** readers or writers to finish. Sync the db file if all frames in the log +** are checkpointed. This mode is the same as calling +** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked. +** +**
SQLITE_CHECKPOINT_FULL
+** This mode blocks (calls the busy-handler callback) until there is no +** database writer and all readers are reading from the most recent database +** snapshot. It then checkpoints all frames in the log file and syncs the +** database file. This call blocks database writers while it is running, +** but not database readers. +** +**
SQLITE_CHECKPOINT_RESTART
+** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after +** checkpointing the log file it blocks (calls the busy-handler callback) +** until all readers are reading from the database file only. This ensures +** that the next client to write to the database file restarts the log file +** from the beginning. This call blocks database writers while it is running, +** but not database readers. +**
+** +** If pnLog is not NULL, then *pnLog is set to the total number of frames in +** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to +** the total number of checkpointed frames (including any that were already +** checkpointed when this function is called). *pnLog and *pnCkpt may be +** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK. +** If no values are available because of an error, they are both set to -1 +** before returning to communicate this to the caller. +** +** All calls obtain an exclusive "checkpoint" lock on the database file. If +** any other process is running a checkpoint operation at the same time, the +** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a +** busy-handler configured, it will not be invoked in this case. +** +** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive +** "writer" lock on the database file. If the writer lock cannot be obtained +** immediately, and a busy-handler is configured, it is invoked and the writer +** lock retried until either the busy-handler returns 0 or the lock is +** successfully obtained. The busy-handler is also invoked while waiting for +** database readers as described above. If the busy-handler returns 0 before +** the writer lock is obtained or while waiting for database readers, the +** checkpoint operation proceeds from that point in the same way as +** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible +** without blocking any further. SQLITE_BUSY is returned in this case. +** +** If parameter zDb is NULL or points to a zero length string, then the +** specified operation is attempted on all WAL databases. In this case the +** values written to output parameters *pnLog and *pnCkpt are undefined. If +** an SQLITE_BUSY error is encountered when processing one or more of the +** attached WAL databases, the operation is still attempted on any remaining +** attached databases and SQLITE_BUSY is returned to the caller. If any other +** error occurs while processing an attached database, processing is abandoned +** and the error code returned to the caller immediately. If no error +** (SQLITE_BUSY or otherwise) is encountered while processing the attached +** databases, SQLITE_OK is returned. +** +** If database zDb is the name of an attached database that is not in WAL +** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If +** zDb is not NULL (or a zero length string) and is not the name of any +** attached database, SQLITE_ERROR is returned to the caller. +*/ +SQLITE_API int sqlite3_wal_checkpoint_v2( + sqlite3 *db, /* Database handle */ + const char *zDb, /* Name of attached database (or NULL) */ + int eMode, /* SQLITE_CHECKPOINT_* value */ + int *pnLog, /* OUT: Size of WAL log in frames */ + int *pnCkpt /* OUT: Total number of frames checkpointed */ +); + +/* +** CAPI3REF: Checkpoint operation parameters +** +** These constants can be used as the 3rd parameter to +** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] +** documentation for additional information about the meaning and use of +** each of these values. +*/ +#define SQLITE_CHECKPOINT_PASSIVE 0 +#define SQLITE_CHECKPOINT_FULL 1 +#define SQLITE_CHECKPOINT_RESTART 2 + +/* +** CAPI3REF: Virtual Table Interface Configuration +** +** This function may be called by either the [xConnect] or [xCreate] method +** of a [virtual table] implementation to configure +** various facets of the virtual table interface. +** +** If this interface is invoked outside the context of an xConnect or +** xCreate virtual table method then the behavior is undefined. +** +** At present, there is only one option that may be configured using +** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options +** may be added in the future. +*/ +SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + +/* +** CAPI3REF: Virtual Table Configuration Options +** +** These macros define the various options to the +** [sqlite3_vtab_config()] interface that [virtual table] implementations +** can use to customize and optimize their behavior. +** +**
+**
SQLITE_VTAB_CONSTRAINT_SUPPORT +**
Calls of the form +** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, +** where X is an integer. If X is zero, then the [virtual table] whose +** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not +** support constraints. In this configuration (which is the default) if +** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire +** statement is rolled back as if [ON CONFLICT | OR ABORT] had been +** specified as part of the users SQL statement, regardless of the actual +** ON CONFLICT mode specified. +** +** If X is non-zero, then the virtual table implementation guarantees +** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before +** any modifications to internal or persistent data structures have been made. +** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite +** is able to roll back a statement or database transaction, and abandon +** or continue processing the current SQL statement as appropriate. +** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns +** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode +** had been ABORT. +** +** Virtual table implementations that are required to handle OR REPLACE +** must do so within the [xUpdate] method. If a call to the +** [sqlite3_vtab_on_conflict()] function indicates that the current ON +** CONFLICT policy is REPLACE, the virtual table implementation should +** silently replace the appropriate rows within the xUpdate callback and +** return SQLITE_OK. Or, if this is not possible, it may return +** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT +** constraint handling. +**
+*/ +#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 + +/* +** CAPI3REF: Determine The Virtual Table Conflict Policy +** +** This function may only be called from within a call to the [xUpdate] method +** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The +** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], +** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode +** of the SQL statement that triggered the call to the [xUpdate] method of the +** [virtual table]. +*/ +SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); + +/* +** CAPI3REF: Conflict resolution modes +** +** These constants are returned by [sqlite3_vtab_on_conflict()] to +** inform a [virtual table] implementation what the [ON CONFLICT] mode +** is for the SQL statement being evaluated. +** +** Note that the [SQLITE_IGNORE] constant is also used as a potential +** return value from the [sqlite3_set_authorizer()] callback and that +** [SQLITE_ABORT] is also a [result code]. +*/ +#define SQLITE_ROLLBACK 1 +/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ +#define SQLITE_FAIL 3 +/* #define SQLITE_ABORT 4 // Also an error code */ +#define SQLITE_REPLACE 5 + + + +/* +** Undo the hack that converts floating point types to integer for +** builds on processors without floating point support. +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# undef double +#endif + +#if 0 +} /* End of the 'extern "C"' block */ +#endif +#endif + +/* +** 2010 August 30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +*/ + +#ifndef _SQLITE3RTREE_H_ +#define _SQLITE3RTREE_H_ + + +#if 0 +extern "C" { +#endif + +typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; + +/* +** Register a geometry callback named zGeom that can be used as part of an +** R-Tree geometry query as follows: +** +** SELECT ... FROM WHERE MATCH $zGeom(... params ...) +*/ +SQLITE_API int sqlite3_rtree_geometry_callback( + sqlite3 *db, + const char *zGeom, +#ifdef SQLITE_RTREE_INT_ONLY + int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes), +#else + int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes), +#endif + void *pContext +); + + +/* +** A pointer to a structure of the following type is passed as the first +** argument to callbacks registered using rtree_geometry_callback(). +*/ +struct sqlite3_rtree_geometry { + void *pContext; /* Copy of pContext passed to s_r_g_c() */ + int nParam; /* Size of array aParam[] */ + double *aParam; /* Parameters passed to SQL geom function */ + void *pUser; /* Callback implementation user data */ + void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ +}; + + +#if 0 +} /* end of the 'extern "C"' block */ +#endif + +#endif /* ifndef _SQLITE3RTREE_H_ */ + + +/************** End of sqlite3.h *********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include hash.h in the middle of sqliteInt.h ******************/ +/************** Begin file hash.h ********************************************/ +/* +** 2001 September 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This is the header file for the generic hash-table implementation +** used in SQLite. +*/ +#ifndef _SQLITE_HASH_H_ +#define _SQLITE_HASH_H_ + +/* Forward declarations of structures. */ +typedef struct Hash Hash; +typedef struct HashElem HashElem; + +/* A complete hash table is an instance of the following structure. +** The internals of this structure are intended to be opaque -- client +** code should not attempt to access or modify the fields of this structure +** directly. Change this structure only by using the routines below. +** However, some of the "procedures" and "functions" for modifying and +** accessing this structure are really macros, so we can't really make +** this structure opaque. +** +** All elements of the hash table are on a single doubly-linked list. +** Hash.first points to the head of this list. +** +** There are Hash.htsize buckets. Each bucket points to a spot in +** the global doubly-linked list. The contents of the bucket are the +** element pointed to plus the next _ht.count-1 elements in the list. +** +** Hash.htsize and Hash.ht may be zero. In that case lookup is done +** by a linear search of the global list. For small tables, the +** Hash.ht table is never allocated because if there are few elements +** in the table, it is faster to do a linear search than to manage +** the hash table. +*/ +struct Hash { + unsigned int htsize; /* Number of buckets in the hash table */ + unsigned int count; /* Number of entries in this table */ + HashElem *first; /* The first element of the array */ + struct _ht { /* the hash table */ + int count; /* Number of entries with this hash */ + HashElem *chain; /* Pointer to first entry with this hash */ + } *ht; +}; + +/* Each element in the hash table is an instance of the following +** structure. All elements are stored on a single doubly-linked list. +** +** Again, this structure is intended to be opaque, but it can't really +** be opaque because it is used by macros. +*/ +struct HashElem { + HashElem *next, *prev; /* Next and previous elements in the table */ + void *data; /* Data associated with this element */ + const char *pKey; int nKey; /* Key associated with this element */ +}; + +/* +** Access routines. To delete, insert a NULL pointer. +*/ +SQLITE_PRIVATE void sqlite3HashInit(Hash*); +SQLITE_PRIVATE void *sqlite3HashInsert(Hash*, const char *pKey, int nKey, void *pData); +SQLITE_PRIVATE void *sqlite3HashFind(const Hash*, const char *pKey, int nKey); +SQLITE_PRIVATE void sqlite3HashClear(Hash*); + +/* +** Macros for looping over all elements of a hash table. The idiom is +** like this: +** +** Hash h; +** HashElem *p; +** ... +** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){ +** SomeStructure *pData = sqliteHashData(p); +** // do something with pData +** } +*/ +#define sqliteHashFirst(H) ((H)->first) +#define sqliteHashNext(E) ((E)->next) +#define sqliteHashData(E) ((E)->data) +/* #define sqliteHashKey(E) ((E)->pKey) // NOT USED */ +/* #define sqliteHashKeysize(E) ((E)->nKey) // NOT USED */ + +/* +** Number of entries in a hash table +*/ +/* #define sqliteHashCount(H) ((H)->count) // NOT USED */ + +#endif /* _SQLITE_HASH_H_ */ + +/************** End of hash.h ************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include parse.h in the middle of sqliteInt.h *****************/ +/************** Begin file parse.h *******************************************/ +#define TK_SEMI 1 +#define TK_EXPLAIN 2 +#define TK_QUERY 3 +#define TK_PLAN 4 +#define TK_BEGIN 5 +#define TK_TRANSACTION 6 +#define TK_DEFERRED 7 +#define TK_IMMEDIATE 8 +#define TK_EXCLUSIVE 9 +#define TK_COMMIT 10 +#define TK_END 11 +#define TK_ROLLBACK 12 +#define TK_SAVEPOINT 13 +#define TK_RELEASE 14 +#define TK_TO 15 +#define TK_TABLE 16 +#define TK_CREATE 17 +#define TK_IF 18 +#define TK_NOT 19 +#define TK_EXISTS 20 +#define TK_TEMP 21 +#define TK_LP 22 +#define TK_RP 23 +#define TK_AS 24 +#define TK_COMMA 25 +#define TK_ID 26 +#define TK_INDEXED 27 +#define TK_ABORT 28 +#define TK_ACTION 29 +#define TK_AFTER 30 +#define TK_ANALYZE 31 +#define TK_ASC 32 +#define TK_ATTACH 33 +#define TK_BEFORE 34 +#define TK_BY 35 +#define TK_CASCADE 36 +#define TK_CAST 37 +#define TK_COLUMNKW 38 +#define TK_CONFLICT 39 +#define TK_DATABASE 40 +#define TK_DESC 41 +#define TK_DETACH 42 +#define TK_EACH 43 +#define TK_FAIL 44 +#define TK_FOR 45 +#define TK_IGNORE 46 +#define TK_INITIALLY 47 +#define TK_INSTEAD 48 +#define TK_LIKE_KW 49 +#define TK_MATCH 50 +#define TK_NO 51 +#define TK_KEY 52 +#define TK_OF 53 +#define TK_OFFSET 54 +#define TK_PRAGMA 55 +#define TK_RAISE 56 +#define TK_REPLACE 57 +#define TK_RESTRICT 58 +#define TK_ROW 59 +#define TK_TRIGGER 60 +#define TK_VACUUM 61 +#define TK_VIEW 62 +#define TK_VIRTUAL 63 +#define TK_REINDEX 64 +#define TK_RENAME 65 +#define TK_CTIME_KW 66 +#define TK_ANY 67 +#define TK_OR 68 +#define TK_AND 69 +#define TK_IS 70 +#define TK_BETWEEN 71 +#define TK_IN 72 +#define TK_ISNULL 73 +#define TK_NOTNULL 74 +#define TK_NE 75 +#define TK_EQ 76 +#define TK_GT 77 +#define TK_LE 78 +#define TK_LT 79 +#define TK_GE 80 +#define TK_ESCAPE 81 +#define TK_BITAND 82 +#define TK_BITOR 83 +#define TK_LSHIFT 84 +#define TK_RSHIFT 85 +#define TK_PLUS 86 +#define TK_MINUS 87 +#define TK_STAR 88 +#define TK_SLASH 89 +#define TK_REM 90 +#define TK_CONCAT 91 +#define TK_COLLATE 92 +#define TK_BITNOT 93 +#define TK_STRING 94 +#define TK_JOIN_KW 95 +#define TK_CONSTRAINT 96 +#define TK_DEFAULT 97 +#define TK_NULL 98 +#define TK_PRIMARY 99 +#define TK_UNIQUE 100 +#define TK_CHECK 101 +#define TK_REFERENCES 102 +#define TK_AUTOINCR 103 +#define TK_ON 104 +#define TK_INSERT 105 +#define TK_DELETE 106 +#define TK_UPDATE 107 +#define TK_SET 108 +#define TK_DEFERRABLE 109 +#define TK_FOREIGN 110 +#define TK_DROP 111 +#define TK_UNION 112 +#define TK_ALL 113 +#define TK_EXCEPT 114 +#define TK_INTERSECT 115 +#define TK_SELECT 116 +#define TK_DISTINCT 117 +#define TK_DOT 118 +#define TK_FROM 119 +#define TK_JOIN 120 +#define TK_USING 121 +#define TK_ORDER 122 +#define TK_GROUP 123 +#define TK_HAVING 124 +#define TK_LIMIT 125 +#define TK_WHERE 126 +#define TK_INTO 127 +#define TK_VALUES 128 +#define TK_INTEGER 129 +#define TK_FLOAT 130 +#define TK_BLOB 131 +#define TK_REGISTER 132 +#define TK_VARIABLE 133 +#define TK_CASE 134 +#define TK_WHEN 135 +#define TK_THEN 136 +#define TK_ELSE 137 +#define TK_INDEX 138 +#define TK_ALTER 139 +#define TK_ADD 140 +#define TK_TO_TEXT 141 +#define TK_TO_BLOB 142 +#define TK_TO_NUMERIC 143 +#define TK_TO_INT 144 +#define TK_TO_REAL 145 +#define TK_ISNOT 146 +#define TK_END_OF_FILE 147 +#define TK_ILLEGAL 148 +#define TK_SPACE 149 +#define TK_UNCLOSED_STRING 150 +#define TK_FUNCTION 151 +#define TK_COLUMN 152 +#define TK_AGG_FUNCTION 153 +#define TK_AGG_COLUMN 154 +#define TK_CONST_FUNC 155 +#define TK_UMINUS 156 +#define TK_UPLUS 157 + +/************** End of parse.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +#include +#include +#include +#include +#include + +/* +** If compiling for a processor that lacks floating point support, +** substitute integer for floating-point +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# define double sqlite_int64 +# define float sqlite_int64 +# define LONGDOUBLE_TYPE sqlite_int64 +# ifndef SQLITE_BIG_DBL +# define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) +# endif +# define SQLITE_OMIT_DATETIME_FUNCS 1 +# define SQLITE_OMIT_TRACE 1 +# undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +# undef SQLITE_HAVE_ISNAN +#endif +#ifndef SQLITE_BIG_DBL +# define SQLITE_BIG_DBL (1e99) +#endif + +/* +** OMIT_TEMPDB is set to 1 if SQLITE_OMIT_TEMPDB is defined, or 0 +** afterward. Having this macro allows us to cause the C compiler +** to omit code used by TEMP tables without messy #ifndef statements. +*/ +#ifdef SQLITE_OMIT_TEMPDB +#define OMIT_TEMPDB 1 +#else +#define OMIT_TEMPDB 0 +#endif + +/* +** The "file format" number is an integer that is incremented whenever +** the VDBE-level file format changes. The following macros define the +** the default file format for new databases and the maximum file format +** that the library can read. +*/ +#define SQLITE_MAX_FILE_FORMAT 4 +#ifndef SQLITE_DEFAULT_FILE_FORMAT +# define SQLITE_DEFAULT_FILE_FORMAT 4 +#endif + +/* +** Determine whether triggers are recursive by default. This can be +** changed at run-time using a pragma. +*/ +#ifndef SQLITE_DEFAULT_RECURSIVE_TRIGGERS +# define SQLITE_DEFAULT_RECURSIVE_TRIGGERS 0 +#endif + +/* +** Provide a default value for SQLITE_TEMP_STORE in case it is not specified +** on the command-line +*/ +#ifndef SQLITE_TEMP_STORE +# define SQLITE_TEMP_STORE 1 +#endif + +/* +** GCC does not define the offsetof() macro so we'll have to do it +** ourselves. +*/ +#ifndef offsetof +#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) +#endif + +/* +** Check to see if this machine uses EBCDIC. (Yes, believe it or +** not, there are still machines out there that use EBCDIC.) +*/ +#if 'A' == '\301' +# define SQLITE_EBCDIC 1 +#else +# define SQLITE_ASCII 1 +#endif + +/* +** Integers of known sizes. These typedefs might change for architectures +** where the sizes very. Preprocessor macros are available so that the +** types can be conveniently redefined at compile-type. Like this: +** +** cc '-DUINTPTR_TYPE=long long int' ... +*/ +#ifndef UINT32_TYPE +# ifdef HAVE_UINT32_T +# define UINT32_TYPE uint32_t +# else +# define UINT32_TYPE unsigned int +# endif +#endif +#ifndef UINT16_TYPE +# ifdef HAVE_UINT16_T +# define UINT16_TYPE uint16_t +# else +# define UINT16_TYPE unsigned short int +# endif +#endif +#ifndef INT16_TYPE +# ifdef HAVE_INT16_T +# define INT16_TYPE int16_t +# else +# define INT16_TYPE short int +# endif +#endif +#ifndef UINT8_TYPE +# ifdef HAVE_UINT8_T +# define UINT8_TYPE uint8_t +# else +# define UINT8_TYPE unsigned char +# endif +#endif +#ifndef INT8_TYPE +# ifdef HAVE_INT8_T +# define INT8_TYPE int8_t +# else +# define INT8_TYPE signed char +# endif +#endif +#ifndef LONGDOUBLE_TYPE +# define LONGDOUBLE_TYPE long double +#endif +typedef sqlite_int64 i64; /* 8-byte signed integer */ +typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ +typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ +typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ +typedef INT16_TYPE i16; /* 2-byte signed integer */ +typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ +typedef INT8_TYPE i8; /* 1-byte signed integer */ + +/* +** SQLITE_MAX_U32 is a u64 constant that is the maximum u64 value +** that can be stored in a u32 without loss of data. The value +** is 0x00000000ffffffff. But because of quirks of some compilers, we +** have to specify the value in the less intuitive manner shown: +*/ +#define SQLITE_MAX_U32 ((((u64)1)<<32)-1) + +/* +** The datatype used to store estimates of the number of rows in a +** table or index. This is an unsigned integer type. For 99.9% of +** the world, a 32-bit integer is sufficient. But a 64-bit integer +** can be used at compile-time if desired. +*/ +#ifdef SQLITE_64BIT_STATS + typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */ +#else + typedef u32 tRowcnt; /* 32-bit is the default */ +#endif + +/* +** Macros to determine whether the machine is big or little endian, +** evaluated at runtime. +*/ +#ifdef SQLITE_AMALGAMATION +SQLITE_PRIVATE const int sqlite3one = 1; +#else +SQLITE_PRIVATE const int sqlite3one; +#endif +#if defined(i386) || defined(__i386__) || defined(_M_IX86)\ + || defined(__x86_64) || defined(__x86_64__) +# define SQLITE_BIGENDIAN 0 +# define SQLITE_LITTLEENDIAN 1 +# define SQLITE_UTF16NATIVE SQLITE_UTF16LE +#else +# define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0) +# define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1) +# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) +#endif + +/* +** Constants for the largest and smallest possible 64-bit signed integers. +** These macros are designed to work correctly on both 32-bit and 64-bit +** compilers. +*/ +#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) +#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) + +/* +** Round up a number to the next larger multiple of 8. This is used +** to force 8-byte alignment on 64-bit architectures. +*/ +#define ROUND8(x) (((x)+7)&~7) + +/* +** Round down to the nearest multiple of 8 +*/ +#define ROUNDDOWN8(x) ((x)&~7) + +/* +** Assert that the pointer X is aligned to an 8-byte boundary. This +** macro is used only within assert() to verify that the code gets +** all alignment restrictions correct. +** +** Except, if SQLITE_4_BYTE_ALIGNED_MALLOC is defined, then the +** underlying malloc() implemention might return us 4-byte aligned +** pointers. In that case, only verify 4-byte alignment. +*/ +#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC +# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0) +#else +# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) +#endif + + +/* +** An instance of the following structure is used to store the busy-handler +** callback for a given sqlite handle. +** +** The sqlite.busyHandler member of the sqlite struct contains the busy +** callback for the database handle. Each pager opened via the sqlite +** handle is passed a pointer to sqlite.busyHandler. The busy-handler +** callback is currently invoked only from within pager.c. +*/ +typedef struct BusyHandler BusyHandler; +struct BusyHandler { + int (*xFunc)(void *,int); /* The busy callback */ + void *pArg; /* First arg to busy callback */ + int nBusy; /* Incremented with each busy call */ +}; + +/* +** Name of the master database table. The master database table +** is a special table that holds the names and attributes of all +** user tables and indices. +*/ +#define MASTER_NAME "sqlite_master" +#define TEMP_MASTER_NAME "sqlite_temp_master" + +/* +** The root-page of the master database table. +*/ +#define MASTER_ROOT 1 + +/* +** The name of the schema table. +*/ +#define SCHEMA_TABLE(x) ((!OMIT_TEMPDB)&&(x==1)?TEMP_MASTER_NAME:MASTER_NAME) + +/* +** A convenience macro that returns the number of elements in +** an array. +*/ +#define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) + +/* +** Determine if the argument is a power of two +*/ +#define IsPowerOfTwo(X) (((X)&((X)-1))==0) + +/* +** The following value as a destructor means to use sqlite3DbFree(). +** The sqlite3DbFree() routine requires two parameters instead of the +** one parameter that destructors normally want. So we have to introduce +** this magic value that the code knows to handle differently. Any +** pointer will work here as long as it is distinct from SQLITE_STATIC +** and SQLITE_TRANSIENT. +*/ +#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize) + +/* +** When SQLITE_OMIT_WSD is defined, it means that the target platform does +** not support Writable Static Data (WSD) such as global and static variables. +** All variables must either be on the stack or dynamically allocated from +** the heap. When WSD is unsupported, the variable declarations scattered +** throughout the SQLite code must become constants instead. The SQLITE_WSD +** macro is used for this purpose. And instead of referencing the variable +** directly, we use its constant as a key to lookup the run-time allocated +** buffer that holds real variable. The constant is also the initializer +** for the run-time allocated buffer. +** +** In the usual case where WSD is supported, the SQLITE_WSD and GLOBAL +** macros become no-ops and have zero performance impact. +*/ +#ifdef SQLITE_OMIT_WSD + #define SQLITE_WSD const + #define GLOBAL(t,v) (*(t*)sqlite3_wsd_find((void*)&(v), sizeof(v))) + #define sqlite3GlobalConfig GLOBAL(struct Sqlite3Config, sqlite3Config) +SQLITE_API int sqlite3_wsd_init(int N, int J); +SQLITE_API void *sqlite3_wsd_find(void *K, int L); +#else + #define SQLITE_WSD + #define GLOBAL(t,v) v + #define sqlite3GlobalConfig sqlite3Config +#endif + +/* +** The following macros are used to suppress compiler warnings and to +** make it clear to human readers when a function parameter is deliberately +** left unused within the body of a function. This usually happens when +** a function is called via a function pointer. For example the +** implementation of an SQL aggregate step callback may not use the +** parameter indicating the number of arguments passed to the aggregate, +** if it knows that this is enforced elsewhere. +** +** When a function parameter is not used at all within the body of a function, +** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. +** However, these macros may also be used to suppress warnings related to +** parameters that may or may not be used depending on compilation options. +** For example those parameters only used in assert() statements. In these +** cases the parameters are named as per the usual conventions. +*/ +#define UNUSED_PARAMETER(x) (void)(x) +#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) + +/* +** Forward references to structures +*/ +typedef struct AggInfo AggInfo; +typedef struct AuthContext AuthContext; +typedef struct AutoincInfo AutoincInfo; +typedef struct Bitvec Bitvec; +typedef struct CollSeq CollSeq; +typedef struct Column Column; +typedef struct Db Db; +typedef struct Schema Schema; +typedef struct Expr Expr; +typedef struct ExprList ExprList; +typedef struct ExprSpan ExprSpan; +typedef struct FKey FKey; +typedef struct FuncDestructor FuncDestructor; +typedef struct FuncDef FuncDef; +typedef struct FuncDefHash FuncDefHash; +typedef struct IdList IdList; +typedef struct Index Index; +typedef struct IndexSample IndexSample; +typedef struct KeyClass KeyClass; +typedef struct KeyInfo KeyInfo; +typedef struct Lookaside Lookaside; +typedef struct LookasideSlot LookasideSlot; +typedef struct Module Module; +typedef struct NameContext NameContext; +typedef struct Parse Parse; +typedef struct RowSet RowSet; +typedef struct Savepoint Savepoint; +typedef struct Select Select; +typedef struct SelectDest SelectDest; +typedef struct SrcList SrcList; +typedef struct StrAccum StrAccum; +typedef struct Table Table; +typedef struct TableLock TableLock; +typedef struct Token Token; +typedef struct Trigger Trigger; +typedef struct TriggerPrg TriggerPrg; +typedef struct TriggerStep TriggerStep; +typedef struct UnpackedRecord UnpackedRecord; +typedef struct VTable VTable; +typedef struct VtabCtx VtabCtx; +typedef struct Walker Walker; +typedef struct WherePlan WherePlan; +typedef struct WhereInfo WhereInfo; +typedef struct WhereLevel WhereLevel; + +/* +** Defer sourcing vdbe.h and btree.h until after the "u8" and +** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque +** pointer types (i.e. FuncDef) defined above. +*/ +/************** Include btree.h in the middle of sqliteInt.h *****************/ +/************** Begin file btree.h *******************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite B-Tree file +** subsystem. See comments in the source code for a detailed description +** of what each interface routine does. +*/ +#ifndef _BTREE_H_ +#define _BTREE_H_ + +/* TODO: This definition is just included so other modules compile. It +** needs to be revisited. +*/ +#define SQLITE_N_BTREE_META 10 + +/* +** If defined as non-zero, auto-vacuum is enabled by default. Otherwise +** it must be turned on for each database using "PRAGMA auto_vacuum = 1". +*/ +#ifndef SQLITE_DEFAULT_AUTOVACUUM + #define SQLITE_DEFAULT_AUTOVACUUM 0 +#endif + +#define BTREE_AUTOVACUUM_NONE 0 /* Do not do auto-vacuum */ +#define BTREE_AUTOVACUUM_FULL 1 /* Do full auto-vacuum */ +#define BTREE_AUTOVACUUM_INCR 2 /* Incremental vacuum */ + +/* +** Forward declarations of structure +*/ +typedef struct Btree Btree; +typedef struct BtCursor BtCursor; +typedef struct BtShared BtShared; + + +SQLITE_PRIVATE int sqlite3BtreeOpen( + sqlite3_vfs *pVfs, /* VFS to use with this b-tree */ + const char *zFilename, /* Name of database file to open */ + sqlite3 *db, /* Associated database connection */ + Btree **ppBtree, /* Return open Btree* here */ + int flags, /* Flags */ + int vfsFlags /* Flags passed through to VFS open */ +); + +/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the +** following values. +** +** NOTE: These values must match the corresponding PAGER_ values in +** pager.h. +*/ +#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */ +#define BTREE_MEMORY 2 /* This is an in-memory DB */ +#define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */ +#define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */ + +SQLITE_PRIVATE int sqlite3BtreeClose(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int); +SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); +SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); +SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); +SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); +#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); +#endif +SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); +SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); +SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); +SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); +SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags); +SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); +SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); +SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); +SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); +SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); +SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); +SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int); + +SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); +SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); +SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); + +SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); + +/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR +** of the flags shown below. +** +** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set. +** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data +** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With +** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored +** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL +** indices.) +*/ +#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ +#define BTREE_BLOBKEY 2 /* Table has keys only - no data */ + +SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); +SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); +SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int); + +SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); +SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); + +SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p); + +/* +** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta +** should be one of the following values. The integer values are assigned +** to constants so that the offset of the corresponding field in an +** SQLite database header may be found using the following formula: +** +** offset = 36 + (idx * 4) +** +** For example, the free-page-count field is located at byte offset 36 of +** the database file header. The incr-vacuum-flag field is located at +** byte offset 64 (== 36+4*7). +*/ +#define BTREE_FREE_PAGE_COUNT 0 +#define BTREE_SCHEMA_VERSION 1 +#define BTREE_FILE_FORMAT 2 +#define BTREE_DEFAULT_CACHE_SIZE 3 +#define BTREE_LARGEST_ROOT_PAGE 4 +#define BTREE_TEXT_ENCODING 5 +#define BTREE_USER_VERSION 6 +#define BTREE_INCR_VACUUM 7 + +/* +** Values that may be OR'd together to form the second argument of an +** sqlite3BtreeCursorHints() call. +*/ +#define BTREE_BULKLOAD 0x00000001 + +SQLITE_PRIVATE int sqlite3BtreeCursor( + Btree*, /* BTree containing table to open */ + int iTable, /* Index of root page */ + int wrFlag, /* 1 for writing. 0 for read-only */ + struct KeyInfo*, /* First argument to compare function */ + BtCursor *pCursor /* Space to write cursor structure */ +); +SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); +SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); + +SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( + BtCursor*, + UnpackedRecord *pUnKey, + i64 intKey, + int bias, + int *pRes +); +SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*, int*); +SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, + const void *pData, int nData, + int nZero, int bias, int seekResult); +SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); +SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); +SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); +SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); +SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64); +SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*); + +SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); +SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); + +SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *); +SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *); +SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); +SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask); + +#ifndef NDEBUG +SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); +#endif + +#ifndef SQLITE_OMIT_BTREECOUNT +SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *, i64 *); +#endif + +#ifdef SQLITE_TEST +SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); +SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); +#endif + +#ifndef SQLITE_OMIT_WAL +SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); +#endif + +/* +** If we are not using shared cache, then there is no need to +** use mutexes to access the BtShared structures. So make the +** Enter and Leave procedures no-ops. +*/ +#ifndef SQLITE_OMIT_SHARED_CACHE +SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*); +SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); +#else +# define sqlite3BtreeEnter(X) +# define sqlite3BtreeEnterAll(X) +#endif + +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE +SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*); +SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); +SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); +SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); +SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); +#ifndef NDEBUG + /* These routines are used inside assert() statements only. */ +SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); +SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); +SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); +#endif +#else + +# define sqlite3BtreeSharable(X) 0 +# define sqlite3BtreeLeave(X) +# define sqlite3BtreeEnterCursor(X) +# define sqlite3BtreeLeaveCursor(X) +# define sqlite3BtreeLeaveAll(X) + +# define sqlite3BtreeHoldsMutex(X) 1 +# define sqlite3BtreeHoldsAllMutexes(X) 1 +# define sqlite3SchemaMutexHeld(X,Y,Z) 1 +#endif + + +#endif /* _BTREE_H_ */ + +/************** End of btree.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include vdbe.h in the middle of sqliteInt.h ******************/ +/************** Begin file vdbe.h ********************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Header file for the Virtual DataBase Engine (VDBE) +** +** This header defines the interface to the virtual database engine +** or VDBE. The VDBE implements an abstract machine that runs a +** simple program to access and modify the underlying database. +*/ +#ifndef _SQLITE_VDBE_H_ +#define _SQLITE_VDBE_H_ +/* #include */ + +/* +** A single VDBE is an opaque structure named "Vdbe". Only routines +** in the source file sqliteVdbe.c are allowed to see the insides +** of this structure. +*/ +typedef struct Vdbe Vdbe; + +/* +** The names of the following types declared in vdbeInt.h are required +** for the VdbeOp definition. +*/ +typedef struct VdbeFunc VdbeFunc; +typedef struct Mem Mem; +typedef struct SubProgram SubProgram; + +/* +** A single instruction of the virtual machine has an opcode +** and as many as three operands. The instruction is recorded +** as an instance of the following structure: +*/ +struct VdbeOp { + u8 opcode; /* What operation to perform */ + signed char p4type; /* One of the P4_xxx constants for p4 */ + u8 opflags; /* Mask of the OPFLG_* flags in opcodes.h */ + u8 p5; /* Fifth parameter is an unsigned character */ + int p1; /* First operand */ + int p2; /* Second parameter (often the jump destination) */ + int p3; /* The third parameter */ + union { /* fourth parameter */ + int i; /* Integer value if p4type==P4_INT32 */ + void *p; /* Generic pointer */ + char *z; /* Pointer to data for string (char array) types */ + i64 *pI64; /* Used when p4type is P4_INT64 */ + double *pReal; /* Used when p4type is P4_REAL */ + FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ + VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */ + CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ + Mem *pMem; /* Used when p4type is P4_MEM */ + VTable *pVtab; /* Used when p4type is P4_VTAB */ + KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ + int *ai; /* Used when p4type is P4_INTARRAY */ + SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ + int (*xAdvance)(BtCursor *, int *); + } p4; +#ifdef SQLITE_DEBUG + char *zComment; /* Comment to improve readability */ +#endif +#ifdef VDBE_PROFILE + int cnt; /* Number of times this instruction was executed */ + u64 cycles; /* Total time spent executing this instruction */ +#endif +}; +typedef struct VdbeOp VdbeOp; + + +/* +** A sub-routine used to implement a trigger program. +*/ +struct SubProgram { + VdbeOp *aOp; /* Array of opcodes for sub-program */ + int nOp; /* Elements in aOp[] */ + int nMem; /* Number of memory cells required */ + int nCsr; /* Number of cursors required */ + int nOnce; /* Number of OP_Once instructions */ + void *token; /* id that may be used to recursive triggers */ + SubProgram *pNext; /* Next sub-program already visited */ +}; + +/* +** A smaller version of VdbeOp used for the VdbeAddOpList() function because +** it takes up less space. +*/ +struct VdbeOpList { + u8 opcode; /* What operation to perform */ + signed char p1; /* First operand */ + signed char p2; /* Second parameter (often the jump destination) */ + signed char p3; /* Third parameter */ +}; +typedef struct VdbeOpList VdbeOpList; + +/* +** Allowed values of VdbeOp.p4type +*/ +#define P4_NOTUSED 0 /* The P4 parameter is not used */ +#define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ +#define P4_STATIC (-2) /* Pointer to a static string */ +#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */ +#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */ +#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */ +#define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */ +#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */ +#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */ +#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */ +#define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ +#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ +#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ +#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ +#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ +#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ +#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */ + +/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure +** is made. That copy is freed when the Vdbe is finalized. But if the +** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still +** gets freed when the Vdbe is finalized so it still should be obtained +** from a single sqliteMalloc(). But no copy is made and the calling +** function should *not* try to free the KeyInfo. +*/ +#define P4_KEYINFO_HANDOFF (-16) +#define P4_KEYINFO_STATIC (-17) + +/* +** The Vdbe.aColName array contains 5n Mem structures, where n is the +** number of columns of data returned by the statement. +*/ +#define COLNAME_NAME 0 +#define COLNAME_DECLTYPE 1 +#define COLNAME_DATABASE 2 +#define COLNAME_TABLE 3 +#define COLNAME_COLUMN 4 +#ifdef SQLITE_ENABLE_COLUMN_METADATA +# define COLNAME_N 5 /* Number of COLNAME_xxx symbols */ +#else +# ifdef SQLITE_OMIT_DECLTYPE +# define COLNAME_N 1 /* Store only the name */ +# else +# define COLNAME_N 2 /* Store the name and decltype */ +# endif +#endif + +/* +** The following macro converts a relative address in the p2 field +** of a VdbeOp structure into a negative number so that +** sqlite3VdbeAddOpList() knows that the address is relative. Calling +** the macro again restores the address. +*/ +#define ADDR(X) (-1-(X)) + +/* +** The makefile scans the vdbe.c source file and creates the "opcodes.h" +** header file that defines a number for each opcode used by the VDBE. +*/ +/************** Include opcodes.h in the middle of vdbe.h ********************/ +/************** Begin file opcodes.h *****************************************/ +/* Automatically generated. Do not edit */ +/* See the mkopcodeh.awk script for details */ +#define OP_Goto 1 +#define OP_Gosub 2 +#define OP_Return 3 +#define OP_Yield 4 +#define OP_HaltIfNull 5 +#define OP_Halt 6 +#define OP_Integer 7 +#define OP_Int64 8 +#define OP_Real 130 /* same as TK_FLOAT */ +#define OP_String8 94 /* same as TK_STRING */ +#define OP_String 9 +#define OP_Null 10 +#define OP_Blob 11 +#define OP_Variable 12 +#define OP_Move 13 +#define OP_Copy 14 +#define OP_SCopy 15 +#define OP_ResultRow 16 +#define OP_Concat 91 /* same as TK_CONCAT */ +#define OP_Add 86 /* same as TK_PLUS */ +#define OP_Subtract 87 /* same as TK_MINUS */ +#define OP_Multiply 88 /* same as TK_STAR */ +#define OP_Divide 89 /* same as TK_SLASH */ +#define OP_Remainder 90 /* same as TK_REM */ +#define OP_CollSeq 17 +#define OP_Function 18 +#define OP_BitAnd 82 /* same as TK_BITAND */ +#define OP_BitOr 83 /* same as TK_BITOR */ +#define OP_ShiftLeft 84 /* same as TK_LSHIFT */ +#define OP_ShiftRight 85 /* same as TK_RSHIFT */ +#define OP_AddImm 20 +#define OP_MustBeInt 21 +#define OP_RealAffinity 22 +#define OP_ToText 141 /* same as TK_TO_TEXT */ +#define OP_ToBlob 142 /* same as TK_TO_BLOB */ +#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/ +#define OP_ToInt 144 /* same as TK_TO_INT */ +#define OP_ToReal 145 /* same as TK_TO_REAL */ +#define OP_Eq 76 /* same as TK_EQ */ +#define OP_Ne 75 /* same as TK_NE */ +#define OP_Lt 79 /* same as TK_LT */ +#define OP_Le 78 /* same as TK_LE */ +#define OP_Gt 77 /* same as TK_GT */ +#define OP_Ge 80 /* same as TK_GE */ +#define OP_Permutation 23 +#define OP_Compare 24 +#define OP_Jump 25 +#define OP_And 69 /* same as TK_AND */ +#define OP_Or 68 /* same as TK_OR */ +#define OP_Not 19 /* same as TK_NOT */ +#define OP_BitNot 93 /* same as TK_BITNOT */ +#define OP_Once 26 +#define OP_If 27 +#define OP_IfNot 28 +#define OP_IsNull 73 /* same as TK_ISNULL */ +#define OP_NotNull 74 /* same as TK_NOTNULL */ +#define OP_Column 29 +#define OP_Affinity 30 +#define OP_MakeRecord 31 +#define OP_Count 32 +#define OP_Savepoint 33 +#define OP_AutoCommit 34 +#define OP_Transaction 35 +#define OP_ReadCookie 36 +#define OP_SetCookie 37 +#define OP_VerifyCookie 38 +#define OP_OpenRead 39 +#define OP_OpenWrite 40 +#define OP_OpenAutoindex 41 +#define OP_OpenEphemeral 42 +#define OP_SorterOpen 43 +#define OP_OpenPseudo 44 +#define OP_Close 45 +#define OP_SeekLt 46 +#define OP_SeekLe 47 +#define OP_SeekGe 48 +#define OP_SeekGt 49 +#define OP_Seek 50 +#define OP_NotFound 51 +#define OP_Found 52 +#define OP_IsUnique 53 +#define OP_NotExists 54 +#define OP_Sequence 55 +#define OP_NewRowid 56 +#define OP_Insert 57 +#define OP_InsertInt 58 +#define OP_Delete 59 +#define OP_ResetCount 60 +#define OP_SorterCompare 61 +#define OP_SorterData 62 +#define OP_RowKey 63 +#define OP_RowData 64 +#define OP_Rowid 65 +#define OP_NullRow 66 +#define OP_Last 67 +#define OP_SorterSort 70 +#define OP_Sort 71 +#define OP_Rewind 72 +#define OP_SorterNext 81 +#define OP_Prev 92 +#define OP_Next 95 +#define OP_SorterInsert 96 +#define OP_IdxInsert 97 +#define OP_IdxDelete 98 +#define OP_IdxRowid 99 +#define OP_IdxLT 100 +#define OP_IdxGE 101 +#define OP_Destroy 102 +#define OP_Clear 103 +#define OP_CreateIndex 104 +#define OP_CreateTable 105 +#define OP_ParseSchema 106 +#define OP_LoadAnalysis 107 +#define OP_DropTable 108 +#define OP_DropIndex 109 +#define OP_DropTrigger 110 +#define OP_IntegrityCk 111 +#define OP_RowSetAdd 112 +#define OP_RowSetRead 113 +#define OP_RowSetTest 114 +#define OP_Program 115 +#define OP_Param 116 +#define OP_FkCounter 117 +#define OP_FkIfZero 118 +#define OP_MemMax 119 +#define OP_IfPos 120 +#define OP_IfNeg 121 +#define OP_IfZero 122 +#define OP_AggStep 123 +#define OP_AggFinal 124 +#define OP_Checkpoint 125 +#define OP_JournalMode 126 +#define OP_Vacuum 127 +#define OP_IncrVacuum 128 +#define OP_Expire 129 +#define OP_TableLock 131 +#define OP_VBegin 132 +#define OP_VCreate 133 +#define OP_VDestroy 134 +#define OP_VOpen 135 +#define OP_VFilter 136 +#define OP_VColumn 137 +#define OP_VNext 138 +#define OP_VRename 139 +#define OP_VUpdate 140 +#define OP_Pagecount 146 +#define OP_MaxPgcnt 147 +#define OP_Trace 148 +#define OP_Noop 149 +#define OP_Explain 150 + + +/* Properties such as "out2" or "jump" that are specified in +** comments following the "case" for each opcode in the vdbe.c +** are encoded into bitvectors as follows: +*/ +#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */ +#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */ +#define OPFLG_IN1 0x0004 /* in1: P1 is an input */ +#define OPFLG_IN2 0x0008 /* in2: P2 is an input */ +#define OPFLG_IN3 0x0010 /* in3: P3 is an input */ +#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */ +#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ +#define OPFLG_INITIALIZER {\ +/* 0 */ 0x00, 0x01, 0x01, 0x04, 0x04, 0x10, 0x00, 0x02,\ +/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x24,\ +/* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\ +/* 24 */ 0x00, 0x01, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00,\ +/* 32 */ 0x02, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00,\ +/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\ +/* 48 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x02,\ +/* 56 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 64 */ 0x00, 0x02, 0x00, 0x01, 0x4c, 0x4c, 0x01, 0x01,\ +/* 72 */ 0x01, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\ +/* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\ +/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x01, 0x24, 0x02, 0x01,\ +/* 96 */ 0x08, 0x08, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\ +/* 104 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 112 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\ +/* 120 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00,\ +/* 128 */ 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 136 */ 0x01, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x04,\ +/* 144 */ 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,} + +/************** End of opcodes.h *********************************************/ +/************** Continuing where we left off in vdbe.h ***********************/ + +/* +** Prototypes for the VDBE interface. See comments on the implementation +** for a description of what each of these routines does. +*/ +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); +SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); +SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); +SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1); +SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2); +SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); +SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); +SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); +SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr); +SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); +SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); +SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); +SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3*,Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*); +SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); +SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *, int); +SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); +#endif +SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe*); +SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe*,int); +SQLITE_PRIVATE int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); +SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe*); +SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); +SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); +SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); +SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8); +SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int); +#ifndef SQLITE_OMIT_TRACE +SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*); +#endif + +SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); +SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); +SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **); + +#ifndef SQLITE_OMIT_TRIGGER +SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); +#endif + + +#ifndef NDEBUG +SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...); +# define VdbeComment(X) sqlite3VdbeComment X +SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); +# define VdbeNoopComment(X) sqlite3VdbeNoopComment X +#else +# define VdbeComment(X) +# define VdbeNoopComment(X) +#endif + +#endif + +/************** End of vdbe.h ************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include pager.h in the middle of sqliteInt.h *****************/ +/************** Begin file pager.h *******************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite page cache +** subsystem. The page cache subsystem reads and writes a file a page +** at a time and provides a journal for rollback. +*/ + +#ifndef _PAGER_H_ +#define _PAGER_H_ + +/* +** Default maximum size for persistent journal files. A negative +** value means no limit. This value may be overridden using the +** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". +*/ +#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT + #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 +#endif + +/* +** The type used to represent a page number. The first page in a file +** is called page 1. 0 is used to represent "not a page". +*/ +typedef u32 Pgno; + +/* +** Each open file is managed by a separate instance of the "Pager" structure. +*/ +typedef struct Pager Pager; + +/* +** Handle type for pages. +*/ +typedef struct PgHdr DbPage; + +/* +** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is +** reserved for working around a windows/posix incompatibility). It is +** used in the journal to signify that the remainder of the journal file +** is devoted to storing a master journal name - there are no more pages to +** roll back. See comments for function writeMasterJournal() in pager.c +** for details. +*/ +#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) + +/* +** Allowed values for the flags parameter to sqlite3PagerOpen(). +** +** NOTE: These values must match the corresponding BTREE_ values in btree.h. +*/ +#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ +#define PAGER_MEMORY 0x0002 /* In-memory database */ + +/* +** Valid values for the second argument to sqlite3PagerLockingMode(). +*/ +#define PAGER_LOCKINGMODE_QUERY -1 +#define PAGER_LOCKINGMODE_NORMAL 0 +#define PAGER_LOCKINGMODE_EXCLUSIVE 1 + +/* +** Numeric constants that encode the journalmode. +*/ +#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ +#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ +#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ +#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ +#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ +#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ +#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ + +/* +** The remainder of this file contains the declarations of the functions +** that make up the Pager sub-system API. See source code comments for +** a detailed description of each routine. +*/ + +/* Open and close a Pager connection. */ +SQLITE_PRIVATE int sqlite3PagerOpen( + sqlite3_vfs*, + Pager **ppPager, + const char*, + int, + int, + int, + void(*)(DbPage*) +); +SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); + +/* Functions used to configure a Pager object. */ +SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); +SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); +SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); +SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); +SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); +SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int,int); +SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); +SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); +SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); +SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); +SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); +SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); + +/* Functions used to obtain and release page references. */ +SQLITE_PRIVATE int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); +#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0) +SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); +SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); +SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); + +/* Operations on page references. */ +SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); +SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); +SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); +SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); +SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); +SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); + +/* Functions used to manage pager transactions and savepoints. */ +SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); +SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); +SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); +SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); +SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); +SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); +SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); +SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); +SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); + +#ifndef SQLITE_OMIT_WAL +SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*); +SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); +SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager); +#endif + +#ifdef SQLITE_ENABLE_ZIPVFS +SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); +#endif + +/* Functions used to query pager state and configuration. */ +SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); +SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); +SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); +SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); +SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); +SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); +SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); +SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); +SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); +SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); +SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); +SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); +SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); + +/* Functions used to truncate the database file. */ +SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); + +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) +SQLITE_PRIVATE void *sqlite3PagerCodec(DbPage *); +#endif + +/* Functions to support testing and debugging. */ +#if !defined(NDEBUG) || defined(SQLITE_TEST) +SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); +SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); +#endif +#ifdef SQLITE_TEST +SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); +SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); + void disable_simulated_io_errors(void); + void enable_simulated_io_errors(void); +#else +# define disable_simulated_io_errors() +# define enable_simulated_io_errors() +#endif + +#endif /* _PAGER_H_ */ + +/************** End of pager.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include pcache.h in the middle of sqliteInt.h ****************/ +/************** Begin file pcache.h ******************************************/ +/* +** 2008 August 05 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite page cache +** subsystem. +*/ + +#ifndef _PCACHE_H_ + +typedef struct PgHdr PgHdr; +typedef struct PCache PCache; + +/* +** Every page in the cache is controlled by an instance of the following +** structure. +*/ +struct PgHdr { + sqlite3_pcache_page *pPage; /* Pcache object page handle */ + void *pData; /* Page data */ + void *pExtra; /* Extra content */ + PgHdr *pDirty; /* Transient list of dirty pages */ + Pager *pPager; /* The pager this page is part of */ + Pgno pgno; /* Page number for this page */ +#ifdef SQLITE_CHECK_PAGES + u32 pageHash; /* Hash of page content */ +#endif + u16 flags; /* PGHDR flags defined below */ + + /********************************************************************** + ** Elements above are public. All that follows is private to pcache.c + ** and should not be accessed by other modules. + */ + i16 nRef; /* Number of users of this page */ + PCache *pCache; /* Cache that owns this page */ + + PgHdr *pDirtyNext; /* Next element in list of dirty pages */ + PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ +}; + +/* Bit values for PgHdr.flags */ +#define PGHDR_DIRTY 0x002 /* Page has changed */ +#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before + ** writing this page to the database */ +#define PGHDR_NEED_READ 0x008 /* Content is unread */ +#define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */ +#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ + +/* Initialize and shutdown the page cache subsystem */ +SQLITE_PRIVATE int sqlite3PcacheInitialize(void); +SQLITE_PRIVATE void sqlite3PcacheShutdown(void); + +/* Page cache buffer management: +** These routines implement SQLITE_CONFIG_PAGECACHE. +*/ +SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *, int sz, int n); + +/* Create a new pager cache. +** Under memory stress, invoke xStress to try to make pages clean. +** Only clean and unpinned pages can be reclaimed. +*/ +SQLITE_PRIVATE void sqlite3PcacheOpen( + int szPage, /* Size of every page */ + int szExtra, /* Extra space associated with each page */ + int bPurgeable, /* True if pages are on backing store */ + int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */ + void *pStress, /* Argument to xStress */ + PCache *pToInit /* Preallocated space for the PCache */ +); + +/* Modify the page-size after the cache has been created. */ +SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *, int); + +/* Return the size in bytes of a PCache object. Used to preallocate +** storage space. +*/ +SQLITE_PRIVATE int sqlite3PcacheSize(void); + +/* One release per successful fetch. Page is pinned until released. +** Reference counted. +*/ +SQLITE_PRIVATE int sqlite3PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**); +SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr*); + +SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */ +SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */ +SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */ +SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */ + +/* Change a page number. Used by incr-vacuum. */ +SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr*, Pgno); + +/* Remove all pages with pgno>x. Reset the cache if x==0 */ +SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache*, Pgno x); + +/* Get a list of all dirty pages in the cache, sorted by page number */ +SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache*); + +/* Reset and close the cache object */ +SQLITE_PRIVATE void sqlite3PcacheClose(PCache*); + +/* Clear flags from pages of the page cache */ +SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); + +/* Discard the contents of the cache */ +SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); + +/* Return the total number of outstanding page references */ +SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*); + +/* Increment the reference count of an existing page */ +SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); + +SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*); + +/* Return the total number of pages stored in the cache */ +SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); + +#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) +/* Iterate through all dirty pages currently stored in the cache. This +** interface is only available if SQLITE_CHECK_PAGES is defined when the +** library is built. +*/ +SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)); +#endif + +/* Set and get the suggested cache-size for the specified pager-cache. +** +** If no global maximum is configured, then the system attempts to limit +** the total number of pages cached by purgeable pager-caches to the sum +** of the suggested cache-sizes. +*/ +SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int); +#ifdef SQLITE_TEST +SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *); +#endif + +/* Free up as much memory as possible from the page cache */ +SQLITE_PRIVATE void sqlite3PcacheShrink(PCache*); + +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +/* Try to return memory used by the pcache module to the main memory heap */ +SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int); +#endif + +#ifdef SQLITE_TEST +SQLITE_PRIVATE void sqlite3PcacheStats(int*,int*,int*,int*); +#endif + +SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); + +#endif /* _PCACHE_H_ */ + +/************** End of pcache.h **********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + +/************** Include os.h in the middle of sqliteInt.h ********************/ +/************** Begin file os.h **********************************************/ +/* +** 2001 September 16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This header file (together with is companion C source-code file +** "os.c") attempt to abstract the underlying operating system so that +** the SQLite library will work on both POSIX and windows systems. +** +** This header file is #include-ed by sqliteInt.h and thus ends up +** being included by every source file. +*/ +#ifndef _SQLITE_OS_H_ +#define _SQLITE_OS_H_ + +/* +** Figure out if we are dealing with Unix, Windows, or some other +** operating system. After the following block of preprocess macros, +** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, and SQLITE_OS_OTHER +** will defined to either 1 or 0. One of the four will be 1. The other +** three will be 0. +*/ +#if defined(SQLITE_OS_OTHER) +# if SQLITE_OS_OTHER==1 +# undef SQLITE_OS_UNIX +# define SQLITE_OS_UNIX 0 +# undef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +# else +# undef SQLITE_OS_OTHER +# endif +#endif +#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER) +# define SQLITE_OS_OTHER 0 +# ifndef SQLITE_OS_WIN +# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) +# define SQLITE_OS_WIN 1 +# define SQLITE_OS_UNIX 0 +# else +# define SQLITE_OS_WIN 0 +# define SQLITE_OS_UNIX 1 +# endif +# else +# define SQLITE_OS_UNIX 0 +# endif +#else +# ifndef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +# endif +#endif + +#if SQLITE_OS_WIN +# include +#endif + +/* +** Determine if we are dealing with Windows NT. +** +** We ought to be able to determine if we are compiling for win98 or winNT +** using the _WIN32_WINNT macro as follows: +** +** #if defined(_WIN32_WINNT) +** # define SQLITE_OS_WINNT 1 +** #else +** # define SQLITE_OS_WINNT 0 +** #endif +** +** However, vs2005 does not set _WIN32_WINNT by default, as it ought to, +** so the above test does not work. We'll just assume that everything is +** winNT unless the programmer explicitly says otherwise by setting +** SQLITE_OS_WINNT to 0. +*/ +#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT) +# define SQLITE_OS_WINNT 1 +#endif + +/* +** Determine if we are dealing with WindowsCE - which has a much +** reduced API. +*/ +#if defined(_WIN32_WCE) +# define SQLITE_OS_WINCE 1 +#else +# define SQLITE_OS_WINCE 0 +#endif + +/* +** Determine if we are dealing with WinRT, which provides only a subset of +** the full Win32 API. +*/ +#if !defined(SQLITE_OS_WINRT) +# define SQLITE_OS_WINRT 0 +#endif + +/* +** When compiled for WinCE or WinRT, there is no concept of the current +** directory. + */ +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +# define SQLITE_CURDIR 1 +#endif + +/* If the SET_FULLSYNC macro is not defined above, then make it +** a no-op +*/ +#ifndef SET_FULLSYNC +# define SET_FULLSYNC(x,y) +#endif + +/* +** The default size of a disk sector +*/ +#ifndef SQLITE_DEFAULT_SECTOR_SIZE +# define SQLITE_DEFAULT_SECTOR_SIZE 4096 +#endif + +/* +** Temporary files are named starting with this prefix followed by 16 random +** alphanumeric characters, and no file extension. They are stored in the +** OS's standard temporary file directory, and are deleted prior to exit. +** If sqlite is being embedded in another program, you may wish to change the +** prefix to reflect your program's name, so that if your program exits +** prematurely, old temporary files can be easily identified. This can be done +** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. +** +** 2006-10-31: The default prefix used to be "sqlite_". But then +** Mcafee started using SQLite in their anti-virus product and it +** started putting files with the "sqlite" name in the c:/temp folder. +** This annoyed many windows users. Those users would then do a +** Google search for "sqlite", find the telephone numbers of the +** developers and call to wake them up at night and complain. +** For this reason, the default name prefix is changed to be "sqlite" +** spelled backwards. So the temp files are still identified, but +** anybody smart enough to figure out the code is also likely smart +** enough to know that calling the developer will not help get rid +** of the file. +*/ +#ifndef SQLITE_TEMP_FILE_PREFIX +# define SQLITE_TEMP_FILE_PREFIX "etilqs_" +#endif + +/* +** The following values may be passed as the second argument to +** sqlite3OsLock(). The various locks exhibit the following semantics: +** +** SHARED: Any number of processes may hold a SHARED lock simultaneously. +** RESERVED: A single process may hold a RESERVED lock on a file at +** any time. Other processes may hold and obtain new SHARED locks. +** PENDING: A single process may hold a PENDING lock on a file at +** any one time. Existing SHARED locks may persist, but no new +** SHARED locks may be obtained by other processes. +** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. +** +** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a +** process that requests an EXCLUSIVE lock may actually obtain a PENDING +** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to +** sqlite3OsLock(). +*/ +#define NO_LOCK 0 +#define SHARED_LOCK 1 +#define RESERVED_LOCK 2 +#define PENDING_LOCK 3 +#define EXCLUSIVE_LOCK 4 + +/* +** File Locking Notes: (Mostly about windows but also some info for Unix) +** +** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because +** those functions are not available. So we use only LockFile() and +** UnlockFile(). +** +** LockFile() prevents not just writing but also reading by other processes. +** A SHARED_LOCK is obtained by locking a single randomly-chosen +** byte out of a specific range of bytes. The lock byte is obtained at +** random so two separate readers can probably access the file at the +** same time, unless they are unlucky and choose the same lock byte. +** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. +** There can only be one writer. A RESERVED_LOCK is obtained by locking +** a single byte of the file that is designated as the reserved lock byte. +** A PENDING_LOCK is obtained by locking a designated byte different from +** the RESERVED_LOCK byte. +** +** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, +** which means we can use reader/writer locks. When reader/writer locks +** are used, the lock is placed on the same range of bytes that is used +** for probabilistic locking in Win95/98/ME. Hence, the locking scheme +** will support two or more Win95 readers or two or more WinNT readers. +** But a single Win95 reader will lock out all WinNT readers and a single +** WinNT reader will lock out all other Win95 readers. +** +** The following #defines specify the range of bytes used for locking. +** SHARED_SIZE is the number of bytes available in the pool from which +** a random byte is selected for a shared lock. The pool of bytes for +** shared locks begins at SHARED_FIRST. +** +** The same locking strategy and +** byte ranges are used for Unix. This leaves open the possiblity of having +** clients on win95, winNT, and unix all talking to the same shared file +** and all locking correctly. To do so would require that samba (or whatever +** tool is being used for file sharing) implements locks correctly between +** windows and unix. I'm guessing that isn't likely to happen, but by +** using the same locking range we are at least open to the possibility. +** +** Locking in windows is manditory. For this reason, we cannot store +** actual data in the bytes used for locking. The pager never allocates +** the pages involved in locking therefore. SHARED_SIZE is selected so +** that all locks will fit on a single page even at the minimum page size. +** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE +** is set high so that we don't have to allocate an unused page except +** for very large databases. But one should test the page skipping logic +** by setting PENDING_BYTE low and running the entire regression suite. +** +** Changing the value of PENDING_BYTE results in a subtly incompatible +** file format. Depending on how it is changed, you might not notice +** the incompatibility right away, even running a full regression test. +** The default location of PENDING_BYTE is the first byte past the +** 1GB boundary. +** +*/ +#ifdef SQLITE_OMIT_WSD +# define PENDING_BYTE (0x40000000) +#else +# define PENDING_BYTE sqlite3PendingByte +#endif +#define RESERVED_BYTE (PENDING_BYTE+1) +#define SHARED_FIRST (PENDING_BYTE+2) +#define SHARED_SIZE 510 + +/* +** Wrapper around OS specific sqlite3_os_init() function. +*/ +SQLITE_PRIVATE int sqlite3OsInit(void); + +/* +** Functions for accessing sqlite3_file methods +*/ +SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file*); +SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); +SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); +SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); +SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); +SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); +SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); +SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*); +#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 +SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); +SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int); +SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int); + + +/* +** Functions for accessing sqlite3_vfs methods +*/ +SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); +SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); +SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); +SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); +#ifndef SQLITE_OMIT_LOAD_EXTENSION +SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); +SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); +SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); +SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); +#endif /* SQLITE_OMIT_LOAD_EXTENSION */ +SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); +SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); +SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*); + +/* +** Convenience functions for opening and closing files using +** sqlite3_malloc() to obtain space for the file-handle structure. +*/ +SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); +SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *); + +#endif /* _SQLITE_OS_H_ */ + +/************** End of os.h **************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include mutex.h in the middle of sqliteInt.h *****************/ +/************** Begin file mutex.h *******************************************/ +/* +** 2007 August 28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains the common header for all mutex implementations. +** The sqliteInt.h header #includes this file so that it is available +** to all source files. We break it out in an effort to keep the code +** better organized. +** +** NOTE: source files should *not* #include this header file directly. +** Source files should #include the sqliteInt.h file and let that file +** include this one indirectly. +*/ + + +/* +** Figure out what version of the code to use. The choices are +** +** SQLITE_MUTEX_OMIT No mutex logic. Not even stubs. The +** mutexes implemention cannot be overridden +** at start-time. +** +** SQLITE_MUTEX_NOOP For single-threaded applications. No +** mutual exclusion is provided. But this +** implementation can be overridden at +** start-time. +** +** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix. +** +** SQLITE_MUTEX_W32 For multi-threaded applications on Win32. +*/ +#if !SQLITE_THREADSAFE +# define SQLITE_MUTEX_OMIT +#endif +#if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP) +# if SQLITE_OS_UNIX +# define SQLITE_MUTEX_PTHREADS +# elif SQLITE_OS_WIN +# define SQLITE_MUTEX_W32 +# else +# define SQLITE_MUTEX_NOOP +# endif +#endif + +#ifdef SQLITE_MUTEX_OMIT +/* +** If this is a no-op implementation, implement everything as macros. +*/ +#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) +#define sqlite3_mutex_free(X) +#define sqlite3_mutex_enter(X) +#define sqlite3_mutex_try(X) SQLITE_OK +#define sqlite3_mutex_leave(X) +#define sqlite3_mutex_held(X) ((void)(X),1) +#define sqlite3_mutex_notheld(X) ((void)(X),1) +#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) +#define sqlite3MutexInit() SQLITE_OK +#define sqlite3MutexEnd() +#define MUTEX_LOGIC(X) +#else +#define MUTEX_LOGIC(X) X +#endif /* defined(SQLITE_MUTEX_OMIT) */ + +/************** End of mutex.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + + +/* +** Each database file to be accessed by the system is an instance +** of the following structure. There are normally two of these structures +** in the sqlite.aDb[] array. aDb[0] is the main database file and +** aDb[1] is the database file used to hold temporary tables. Additional +** databases may be attached. +*/ +struct Db { + char *zName; /* Name of this database */ + Btree *pBt; /* The B*Tree structure for this database file */ + u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ + u8 safety_level; /* How aggressive at syncing data to disk */ + Schema *pSchema; /* Pointer to database schema (possibly shared) */ +}; + +/* +** An instance of the following structure stores a database schema. +** +** Most Schema objects are associated with a Btree. The exception is +** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. +** In shared cache mode, a single Schema object can be shared by multiple +** Btrees that refer to the same underlying BtShared object. +** +** Schema objects are automatically deallocated when the last Btree that +** references them is destroyed. The TEMP Schema is manually freed by +** sqlite3_close(). +* +** A thread must be holding a mutex on the corresponding Btree in order +** to access Schema content. This implies that the thread must also be +** holding a mutex on the sqlite3 connection pointer that owns the Btree. +** For a TEMP Schema, only the connection mutex is required. +*/ +struct Schema { + int schema_cookie; /* Database schema version number for this file */ + int iGeneration; /* Generation counter. Incremented with each change */ + Hash tblHash; /* All tables indexed by name */ + Hash idxHash; /* All (named) indices indexed by name */ + Hash trigHash; /* All triggers indexed by name */ + Hash fkeyHash; /* All foreign keys by referenced table name */ + Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ + u8 file_format; /* Schema format version for this file */ + u8 enc; /* Text encoding used by this database */ + u16 flags; /* Flags associated with this schema */ + int cache_size; /* Number of pages to use in the cache */ +}; + +/* +** These macros can be used to test, set, or clear bits in the +** Db.pSchema->flags field. +*/ +#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P)) +#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0) +#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P) +#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P) + +/* +** Allowed values for the DB.pSchema->flags field. +** +** The DB_SchemaLoaded flag is set after the database schema has been +** read into internal hash tables. +** +** DB_UnresetViews means that one or more views have column names that +** have been filled out. If the schema changes, these column names might +** changes and so the view will need to be reset. +*/ +#define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ +#define DB_UnresetViews 0x0002 /* Some views have defined column names */ +#define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */ + +/* +** The number of different kinds of things that can be limited +** using the sqlite3_limit() interface. +*/ +#define SQLITE_N_LIMIT (SQLITE_LIMIT_TRIGGER_DEPTH+1) + +/* +** Lookaside malloc is a set of fixed-size buffers that can be used +** to satisfy small transient memory allocation requests for objects +** associated with a particular database connection. The use of +** lookaside malloc provides a significant performance enhancement +** (approx 10%) by avoiding numerous malloc/free requests while parsing +** SQL statements. +** +** The Lookaside structure holds configuration information about the +** lookaside malloc subsystem. Each available memory allocation in +** the lookaside subsystem is stored on a linked list of LookasideSlot +** objects. +** +** Lookaside allocations are only allowed for objects that are associated +** with a particular database connection. Hence, schema information cannot +** be stored in lookaside because in shared cache mode the schema information +** is shared by multiple database connections. Therefore, while parsing +** schema information, the Lookaside.bEnabled flag is cleared so that +** lookaside allocations are not used to construct the schema objects. +*/ +struct Lookaside { + u16 sz; /* Size of each buffer in bytes */ + u8 bEnabled; /* False to disable new lookaside allocations */ + u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ + int nOut; /* Number of buffers currently checked out */ + int mxOut; /* Highwater mark for nOut */ + int anStat[3]; /* 0: hits. 1: size misses. 2: full misses */ + LookasideSlot *pFree; /* List of available buffers */ + void *pStart; /* First byte of available memory space */ + void *pEnd; /* First byte past end of available space */ +}; +struct LookasideSlot { + LookasideSlot *pNext; /* Next buffer in the list of free buffers */ +}; + +/* +** A hash table for function definitions. +** +** Hash each FuncDef structure into one of the FuncDefHash.a[] slots. +** Collisions are on the FuncDef.pHash chain. +*/ +struct FuncDefHash { + FuncDef *a[23]; /* Hash table for functions */ +}; + +/* +** Each database connection is an instance of the following structure. +*/ +struct sqlite3 { + sqlite3_vfs *pVfs; /* OS Interface */ + struct Vdbe *pVdbe; /* List of active virtual machines */ + CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ + sqlite3_mutex *mutex; /* Connection mutex */ + Db *aDb; /* All backends */ + int nDb; /* Number of backends currently in use */ + int flags; /* Miscellaneous flags. See below */ + i64 lastRowid; /* ROWID of most recent insert (see above) */ + unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ + int errCode; /* Most recent error code (SQLITE_*) */ + int errMask; /* & result codes with this before returning */ + u16 dbOptFlags; /* Flags to enable/disable optimizations */ + u8 autoCommit; /* The auto-commit flag. */ + u8 temp_store; /* 1: file 2: memory 0: default */ + u8 mallocFailed; /* True if we have seen a malloc failure */ + u8 dfltLockMode; /* Default locking-mode for attached dbs */ + signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ + u8 suppressErr; /* Do not issue error messages if true */ + u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ + u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ + int nextPagesize; /* Pagesize after VACUUM if >0 */ + u32 magic; /* Magic number for detect library misuse */ + int nChange; /* Value returned by sqlite3_changes() */ + int nTotalChange; /* Value returned by sqlite3_total_changes() */ + int aLimit[SQLITE_N_LIMIT]; /* Limits */ + struct sqlite3InitInfo { /* Information used during initialization */ + int newTnum; /* Rootpage of table being initialized */ + u8 iDb; /* Which db file is being initialized */ + u8 busy; /* TRUE if currently initializing */ + u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ + } init; + int activeVdbeCnt; /* Number of VDBEs currently executing */ + int writeVdbeCnt; /* Number of active VDBEs that are writing */ + int vdbeExecCnt; /* Number of nested calls to VdbeExec() */ + int nExtension; /* Number of loaded extensions */ + void **aExtension; /* Array of shared library handles */ + void (*xTrace)(void*,const char*); /* Trace function */ + void *pTraceArg; /* Argument to the trace function */ + void (*xProfile)(void*,const char*,u64); /* Profiling function */ + void *pProfileArg; /* Argument to profile function */ + void *pCommitArg; /* Argument to xCommitCallback() */ + int (*xCommitCallback)(void*); /* Invoked at every commit. */ + void *pRollbackArg; /* Argument to xRollbackCallback() */ + void (*xRollbackCallback)(void*); /* Invoked at every commit. */ + void *pUpdateArg; + void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); +#ifndef SQLITE_OMIT_WAL + int (*xWalCallback)(void *, sqlite3 *, const char *, int); + void *pWalArg; +#endif + void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); + void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); + void *pCollNeededArg; + sqlite3_value *pErr; /* Most recent error message */ + char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ + char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ + union { + volatile int isInterrupted; /* True if sqlite3_interrupt has been called */ + double notUsed1; /* Spacer */ + } u1; + Lookaside lookaside; /* Lookaside malloc configuration */ +#ifndef SQLITE_OMIT_AUTHORIZATION + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); + /* Access authorization function */ + void *pAuthArg; /* 1st argument to the access auth function */ +#endif +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + int (*xProgress)(void *); /* The progress callback */ + void *pProgressArg; /* Argument to the progress callback */ + int nProgressOps; /* Number of opcodes for progress callback */ +#endif +#ifndef SQLITE_OMIT_VIRTUALTABLE + int nVTrans; /* Allocated size of aVTrans */ + Hash aModule; /* populated by sqlite3_create_module() */ + VtabCtx *pVtabCtx; /* Context for active vtab connect/create */ + VTable **aVTrans; /* Virtual tables with open transactions */ + VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ +#endif + FuncDefHash aFunc; /* Hash table of connection functions */ + Hash aCollSeq; /* All collating sequences */ + BusyHandler busyHandler; /* Busy callback */ + Db aDbStatic[2]; /* Static space for the 2 default backends */ + Savepoint *pSavepoint; /* List of active savepoints */ + int busyTimeout; /* Busy handler timeout, in msec */ + int nSavepoint; /* Number of non-transaction savepoints */ + int nStatement; /* Number of nested statement-transactions */ + i64 nDeferredCons; /* Net deferred constraints this transaction. */ + int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ + +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + /* The following variables are all protected by the STATIC_MASTER + ** mutex, not by sqlite3.mutex. They are used by code in notify.c. + ** + ** When X.pUnlockConnection==Y, that means that X is waiting for Y to + ** unlock so that it can proceed. + ** + ** When X.pBlockingConnection==Y, that means that something that X tried + ** tried to do recently failed with an SQLITE_LOCKED error due to locks + ** held by Y. + */ + sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ + sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ + void *pUnlockArg; /* Argument to xUnlockNotify */ + void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ + sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ +#endif +}; + +/* +** A macro to discover the encoding of a database. +*/ +#define ENC(db) ((db)->aDb[0].pSchema->enc) + +/* +** Possible values for the sqlite3.flags. +*/ +#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ +#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */ +#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */ +#define SQLITE_ShortColNames 0x00000008 /* Show short columns names */ +#define SQLITE_CountRows 0x00000010 /* Count rows changed by INSERT, */ + /* DELETE, or UPDATE and return */ + /* the count using a callback. */ +#define SQLITE_NullCallback 0x00000020 /* Invoke the callback once if the */ + /* result set is empty */ +#define SQLITE_SqlTrace 0x00000040 /* Debug print SQL as it executes */ +#define SQLITE_VdbeListing 0x00000080 /* Debug listings of VDBE programs */ +#define SQLITE_WriteSchema 0x00000100 /* OK to update SQLITE_MASTER */ +#define SQLITE_VdbeAddopTrace 0x00000200 /* Trace sqlite3VdbeAddOp() calls */ +#define SQLITE_IgnoreChecks 0x00000400 /* Do not enforce check constraints */ +#define SQLITE_ReadUncommitted 0x0000800 /* For shared-cache mode */ +#define SQLITE_LegacyFileFmt 0x00001000 /* Create new databases in format 1 */ +#define SQLITE_FullFSync 0x00002000 /* Use full fsync on the backend */ +#define SQLITE_CkptFullFSync 0x00004000 /* Use full fsync for checkpoint */ +#define SQLITE_RecoveryMode 0x00008000 /* Ignore schema errors */ +#define SQLITE_ReverseOrder 0x00010000 /* Reverse unordered SELECTs */ +#define SQLITE_RecTriggers 0x00020000 /* Enable recursive triggers */ +#define SQLITE_ForeignKeys 0x00040000 /* Enforce foreign key constraints */ +#define SQLITE_AutoIndex 0x00080000 /* Enable automatic indexes */ +#define SQLITE_PreferBuiltin 0x00100000 /* Preference to built-in funcs */ +#define SQLITE_LoadExtension 0x00200000 /* Enable load_extension */ +#define SQLITE_EnableTrigger 0x00400000 /* True to enable triggers */ + +/* +** Bits of the sqlite3.dbOptFlags field that are used by the +** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to +** selectively disable various optimizations. +*/ +#define SQLITE_QueryFlattener 0x0001 /* Query flattening */ +#define SQLITE_ColumnCache 0x0002 /* Column cache */ +#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ +#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ +#define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ +#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ +#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ +#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ +#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ +#define SQLITE_Transitive 0x0200 /* Transitive constraints */ +#define SQLITE_AllOpts 0xffff /* All optimizations */ + +/* +** Macros for testing whether or not optimizations are enabled or disabled. +*/ +#ifndef SQLITE_OMIT_BUILTIN_TEST +#define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) +#define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0) +#else +#define OptimizationDisabled(db, mask) 0 +#define OptimizationEnabled(db, mask) 1 +#endif + +/* +** Possible values for the sqlite.magic field. +** The numbers are obtained at random and have no special meaning, other +** than being distinct from one another. +*/ +#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ +#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ +#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ +#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ +#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ +#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */ + +/* +** Each SQL function is defined by an instance of the following +** structure. A pointer to this structure is stored in the sqlite.aFunc +** hash table. When multiple functions have the same name, the hash table +** points to a linked list of these structures. +*/ +struct FuncDef { + i16 nArg; /* Number of arguments. -1 means unlimited */ + u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */ + u8 flags; /* Some combination of SQLITE_FUNC_* */ + void *pUserData; /* User data parameter */ + FuncDef *pNext; /* Next function with same name */ + void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ + void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ + void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ + char *zName; /* SQL name of the function. */ + FuncDef *pHash; /* Next with a different name but the same hash */ + FuncDestructor *pDestructor; /* Reference counted destructor function */ +}; + +/* +** This structure encapsulates a user-function destructor callback (as +** configured using create_function_v2()) and a reference counter. When +** create_function_v2() is called to create a function with a destructor, +** a single object of this type is allocated. FuncDestructor.nRef is set to +** the number of FuncDef objects created (either 1 or 3, depending on whether +** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor +** member of each of the new FuncDef objects is set to point to the allocated +** FuncDestructor. +** +** Thereafter, when one of the FuncDef objects is deleted, the reference +** count on this object is decremented. When it reaches 0, the destructor +** is invoked and the FuncDestructor structure freed. +*/ +struct FuncDestructor { + int nRef; + void (*xDestroy)(void *); + void *pUserData; +}; + +/* +** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF +** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There +** are assert() statements in the code to verify this. +*/ +#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ +#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ +#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ +#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ +#define SQLITE_FUNC_COUNT 0x10 /* Built-in count(*) aggregate */ +#define SQLITE_FUNC_COALESCE 0x20 /* Built-in coalesce() or ifnull() function */ +#define SQLITE_FUNC_LENGTH 0x40 /* Built-in length() function */ +#define SQLITE_FUNC_TYPEOF 0x80 /* Built-in typeof() function */ + +/* +** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are +** used to create the initializers for the FuncDef structures. +** +** FUNCTION(zName, nArg, iArg, bNC, xFunc) +** Used to create a scalar function definition of a function zName +** implemented by C function xFunc that accepts nArg arguments. The +** value passed as iArg is cast to a (void*) and made available +** as the user-data (sqlite3_user_data()) for the function. If +** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set. +** +** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal) +** Used to create an aggregate function definition implemented by +** the C functions xStep and xFinal. The first four parameters +** are interpreted in the same way as the first 4 parameters to +** FUNCTION(). +** +** LIKEFUNC(zName, nArg, pArg, flags) +** Used to create a scalar function definition of a function zName +** that accepts nArg arguments and is implemented by a call to C +** function likeFunc. Argument pArg is cast to a (void *) and made +** available as the function user-data (sqlite3_user_data()). The +** FuncDef.flags variable is set to the value passed as the flags +** parameter. +*/ +#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ + {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL), \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} +#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ + {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} +#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ + {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ + pArg, 0, xFunc, 0, 0, #zName, 0, 0} +#define LIKEFUNC(zName, nArg, arg, flags) \ + {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} +#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ + {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \ + SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0} + +/* +** All current savepoints are stored in a linked list starting at +** sqlite3.pSavepoint. The first element in the list is the most recently +** opened savepoint. Savepoints are added to the list by the vdbe +** OP_Savepoint instruction. +*/ +struct Savepoint { + char *zName; /* Savepoint name (nul-terminated) */ + i64 nDeferredCons; /* Number of deferred fk violations */ + Savepoint *pNext; /* Parent savepoint (if any) */ +}; + +/* +** The following are used as the second parameter to sqlite3Savepoint(), +** and as the P1 argument to the OP_Savepoint instruction. +*/ +#define SAVEPOINT_BEGIN 0 +#define SAVEPOINT_RELEASE 1 +#define SAVEPOINT_ROLLBACK 2 + + +/* +** Each SQLite module (virtual table definition) is defined by an +** instance of the following structure, stored in the sqlite3.aModule +** hash table. +*/ +struct Module { + const sqlite3_module *pModule; /* Callback pointers */ + const char *zName; /* Name passed to create_module() */ + void *pAux; /* pAux passed to create_module() */ + void (*xDestroy)(void *); /* Module destructor function */ +}; + +/* +** information about each column of an SQL table is held in an instance +** of this structure. +*/ +struct Column { + char *zName; /* Name of this column */ + Expr *pDflt; /* Default value of this column */ + char *zDflt; /* Original text of the default value */ + char *zType; /* Data type for this column */ + char *zColl; /* Collating sequence. If NULL, use the default */ + u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ + char affinity; /* One of the SQLITE_AFF_... values */ + u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ +}; + +/* Allowed values for Column.colFlags: +*/ +#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */ +#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */ + +/* +** A "Collating Sequence" is defined by an instance of the following +** structure. Conceptually, a collating sequence consists of a name and +** a comparison routine that defines the order of that sequence. +** +** If CollSeq.xCmp is NULL, it means that the +** collating sequence is undefined. Indices built on an undefined +** collating sequence may not be read or written. +*/ +struct CollSeq { + char *zName; /* Name of the collating sequence, UTF-8 encoded */ + u8 enc; /* Text encoding handled by xCmp() */ + void *pUser; /* First argument to xCmp() */ + int (*xCmp)(void*,int, const void*, int, const void*); + void (*xDel)(void*); /* Destructor for pUser */ +}; + +/* +** A sort order can be either ASC or DESC. +*/ +#define SQLITE_SO_ASC 0 /* Sort in ascending order */ +#define SQLITE_SO_DESC 1 /* Sort in ascending order */ + +/* +** Column affinity types. +** +** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and +** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve +** the speed a little by numbering the values consecutively. +** +** But rather than start with 0 or 1, we begin with 'a'. That way, +** when multiple affinity types are concatenated into a string and +** used as the P4 operand, they will be more readable. +** +** Note also that the numeric types are grouped together so that testing +** for a numeric type is a single comparison. +*/ +#define SQLITE_AFF_TEXT 'a' +#define SQLITE_AFF_NONE 'b' +#define SQLITE_AFF_NUMERIC 'c' +#define SQLITE_AFF_INTEGER 'd' +#define SQLITE_AFF_REAL 'e' + +#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) + +/* +** The SQLITE_AFF_MASK values masks off the significant bits of an +** affinity value. +*/ +#define SQLITE_AFF_MASK 0x67 + +/* +** Additional bit values that can be ORed with an affinity without +** changing the affinity. +*/ +#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ +#define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ +#define SQLITE_NULLEQ 0x80 /* NULL=NULL */ + +/* +** An object of this type is created for each virtual table present in +** the database schema. +** +** If the database schema is shared, then there is one instance of this +** structure for each database connection (sqlite3*) that uses the shared +** schema. This is because each database connection requires its own unique +** instance of the sqlite3_vtab* handle used to access the virtual table +** implementation. sqlite3_vtab* handles can not be shared between +** database connections, even when the rest of the in-memory database +** schema is shared, as the implementation often stores the database +** connection handle passed to it via the xConnect() or xCreate() method +** during initialization internally. This database connection handle may +** then be used by the virtual table implementation to access real tables +** within the database. So that they appear as part of the callers +** transaction, these accesses need to be made via the same database +** connection as that used to execute SQL operations on the virtual table. +** +** All VTable objects that correspond to a single table in a shared +** database schema are initially stored in a linked-list pointed to by +** the Table.pVTable member variable of the corresponding Table object. +** When an sqlite3_prepare() operation is required to access the virtual +** table, it searches the list for the VTable that corresponds to the +** database connection doing the preparing so as to use the correct +** sqlite3_vtab* handle in the compiled query. +** +** When an in-memory Table object is deleted (for example when the +** schema is being reloaded for some reason), the VTable objects are not +** deleted and the sqlite3_vtab* handles are not xDisconnect()ed +** immediately. Instead, they are moved from the Table.pVTable list to +** another linked list headed by the sqlite3.pDisconnect member of the +** corresponding sqlite3 structure. They are then deleted/xDisconnected +** next time a statement is prepared using said sqlite3*. This is done +** to avoid deadlock issues involving multiple sqlite3.mutex mutexes. +** Refer to comments above function sqlite3VtabUnlockList() for an +** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect +** list without holding the corresponding sqlite3.mutex mutex. +** +** The memory for objects of this type is always allocated by +** sqlite3DbMalloc(), using the connection handle stored in VTable.db as +** the first argument. +*/ +struct VTable { + sqlite3 *db; /* Database connection associated with this table */ + Module *pMod; /* Pointer to module implementation */ + sqlite3_vtab *pVtab; /* Pointer to vtab instance */ + int nRef; /* Number of pointers to this structure */ + u8 bConstraint; /* True if constraints are supported */ + int iSavepoint; /* Depth of the SAVEPOINT stack */ + VTable *pNext; /* Next in linked list (see above) */ +}; + +/* +** Each SQL table is represented in memory by an instance of the +** following structure. +** +** Table.zName is the name of the table. The case of the original +** CREATE TABLE statement is stored, but case is not significant for +** comparisons. +** +** Table.nCol is the number of columns in this table. Table.aCol is a +** pointer to an array of Column structures, one for each column. +** +** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of +** the column that is that key. Otherwise Table.iPKey is negative. Note +** that the datatype of the PRIMARY KEY must be INTEGER for this field to +** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of +** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid +** is generated for each row of the table. TF_HasPrimaryKey is set if +** the table has any PRIMARY KEY, INTEGER or otherwise. +** +** Table.tnum is the page number for the root BTree page of the table in the +** database file. If Table.iDb is the index of the database table backend +** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that +** holds temporary tables and indices. If TF_Ephemeral is set +** then the table is stored in a file that is automatically deleted +** when the VDBE cursor to the table is closed. In this case Table.tnum +** refers VDBE cursor number that holds the table open, not to the root +** page number. Transient tables are used to hold the results of a +** sub-query that appears instead of a real table name in the FROM clause +** of a SELECT statement. +*/ +struct Table { + char *zName; /* Name of the table or view */ + Column *aCol; /* Information about each column */ + Index *pIndex; /* List of SQL indexes on this table. */ + Select *pSelect; /* NULL for tables. Points to definition if a view. */ + FKey *pFKey; /* Linked list of all foreign keys in this table */ + char *zColAff; /* String defining the affinity of each column */ +#ifndef SQLITE_OMIT_CHECK + ExprList *pCheck; /* All CHECK constraints */ +#endif + tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ + int tnum; /* Root BTree node for this table (see note above) */ + i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */ + i16 nCol; /* Number of columns in this table */ + u16 nRef; /* Number of pointers to this Table */ + u8 tabFlags; /* Mask of TF_* values */ + u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ +#ifndef SQLITE_OMIT_ALTERTABLE + int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ +#endif +#ifndef SQLITE_OMIT_VIRTUALTABLE + int nModuleArg; /* Number of arguments to the module */ + char **azModuleArg; /* Text of all module args. [0] is module name */ + VTable *pVTable; /* List of VTable objects. */ +#endif + Trigger *pTrigger; /* List of triggers stored in pSchema */ + Schema *pSchema; /* Schema that contains this table */ + Table *pNextZombie; /* Next on the Parse.pZombieTab list */ +}; + +/* +** Allowed values for Tabe.tabFlags. +*/ +#define TF_Readonly 0x01 /* Read-only system table */ +#define TF_Ephemeral 0x02 /* An ephemeral table */ +#define TF_HasPrimaryKey 0x04 /* Table has a primary key */ +#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ +#define TF_Virtual 0x10 /* Is a virtual table */ + + +/* +** Test to see whether or not a table is a virtual table. This is +** done as a macro so that it will be optimized out when virtual +** table support is omitted from the build. +*/ +#ifndef SQLITE_OMIT_VIRTUALTABLE +# define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0) +# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0) +#else +# define IsVirtual(X) 0 +# define IsHiddenColumn(X) 0 +#endif + +/* +** Each foreign key constraint is an instance of the following structure. +** +** A foreign key is associated with two tables. The "from" table is +** the table that contains the REFERENCES clause that creates the foreign +** key. The "to" table is the table that is named in the REFERENCES clause. +** Consider this example: +** +** CREATE TABLE ex1( +** a INTEGER PRIMARY KEY, +** b INTEGER CONSTRAINT fk1 REFERENCES ex2(x) +** ); +** +** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2". +** +** Each REFERENCES clause generates an instance of the following structure +** which is attached to the from-table. The to-table need not exist when +** the from-table is created. The existence of the to-table is not checked. +*/ +struct FKey { + Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */ + FKey *pNextFrom; /* Next foreign key in pFrom */ + char *zTo; /* Name of table that the key points to (aka: Parent) */ + FKey *pNextTo; /* Next foreign key on table named zTo */ + FKey *pPrevTo; /* Previous foreign key on table named zTo */ + int nCol; /* Number of columns in this key */ + /* EV: R-30323-21917 */ + u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ + u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ + Trigger *apTrigger[2]; /* Triggers for aAction[] actions */ + struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ + int iFrom; /* Index of column in pFrom */ + char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ + } aCol[1]; /* One entry for each of nCol column s */ +}; + +/* +** SQLite supports many different ways to resolve a constraint +** error. ROLLBACK processing means that a constraint violation +** causes the operation in process to fail and for the current transaction +** to be rolled back. ABORT processing means the operation in process +** fails and any prior changes from that one operation are backed out, +** but the transaction is not rolled back. FAIL processing means that +** the operation in progress stops and returns an error code. But prior +** changes due to the same operation are not backed out and no rollback +** occurs. IGNORE means that the particular row that caused the constraint +** error is not inserted or updated. Processing continues and no error +** is returned. REPLACE means that preexisting database rows that caused +** a UNIQUE constraint violation are removed so that the new insert or +** update can proceed. Processing continues and no error is reported. +** +** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. +** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the +** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign +** key is set to NULL. CASCADE means that a DELETE or UPDATE of the +** referenced table row is propagated into the row that holds the +** foreign key. +** +** The following symbolic values are used to record which type +** of action to take. +*/ +#define OE_None 0 /* There is no constraint to check */ +#define OE_Rollback 1 /* Fail the operation and rollback the transaction */ +#define OE_Abort 2 /* Back out changes but do no rollback transaction */ +#define OE_Fail 3 /* Stop the operation but leave all prior changes */ +#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ +#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ + +#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */ +#define OE_SetNull 7 /* Set the foreign key value to NULL */ +#define OE_SetDflt 8 /* Set the foreign key value to its default */ +#define OE_Cascade 9 /* Cascade the changes */ + +#define OE_Default 99 /* Do whatever the default action is */ + + +/* +** An instance of the following structure is passed as the first +** argument to sqlite3VdbeKeyCompare and is used to control the +** comparison of the two index keys. +*/ +struct KeyInfo { + sqlite3 *db; /* The database connection */ + u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ + u16 nField; /* Number of entries in aColl[] */ + u8 *aSortOrder; /* Sort order for each column. May be NULL */ + CollSeq *aColl[1]; /* Collating sequence for each term of the key */ +}; + +/* +** An instance of the following structure holds information about a +** single index record that has already been parsed out into individual +** values. +** +** A record is an object that contains one or more fields of data. +** Records are used to store the content of a table row and to store +** the key of an index. A blob encoding of a record is created by +** the OP_MakeRecord opcode of the VDBE and is disassembled by the +** OP_Column opcode. +** +** This structure holds a record that has already been disassembled +** into its constituent fields. +*/ +struct UnpackedRecord { + KeyInfo *pKeyInfo; /* Collation and sort-order information */ + u16 nField; /* Number of entries in apMem[] */ + u8 flags; /* Boolean settings. UNPACKED_... below */ + i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */ + Mem *aMem; /* Values */ +}; + +/* +** Allowed values of UnpackedRecord.flags +*/ +#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */ +#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */ +#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */ + +/* +** Each SQL index is represented in memory by an +** instance of the following structure. +** +** The columns of the table that are to be indexed are described +** by the aiColumn[] field of this structure. For example, suppose +** we have the following table and index: +** +** CREATE TABLE Ex1(c1 int, c2 int, c3 text); +** CREATE INDEX Ex2 ON Ex1(c3,c1); +** +** In the Table structure describing Ex1, nCol==3 because there are +** three columns in the table. In the Index structure describing +** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed. +** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the +** first column to be indexed (c3) has an index of 2 in Ex1.aCol[]. +** The second column to be indexed (c1) has an index of 0 in +** Ex1.aCol[], hence Ex2.aiColumn[1]==0. +** +** The Index.onError field determines whether or not the indexed columns +** must be unique and what to do if they are not. When Index.onError=OE_None, +** it means this is not a unique index. Otherwise it is a unique index +** and the value of Index.onError indicate the which conflict resolution +** algorithm to employ whenever an attempt is made to insert a non-unique +** element. +*/ +struct Index { + char *zName; /* Name of this index */ + int *aiColumn; /* Which columns are used by this index. 1st is 0 */ + tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ + Table *pTable; /* The SQL table being indexed */ + char *zColAff; /* String defining the affinity of each column */ + Index *pNext; /* The next index associated with the same table */ + Schema *pSchema; /* Schema containing this index */ + u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ + char **azColl; /* Array of collation sequence names for index */ + int tnum; /* DB Page containing root of this index */ + u16 nColumn; /* Number of columns in table used by this index */ + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ + unsigned bUnordered:1; /* Use this index for == or IN queries only */ +#ifdef SQLITE_ENABLE_STAT3 + int nSample; /* Number of elements in aSample[] */ + tRowcnt avgEq; /* Average nEq value for key values not in aSample */ + IndexSample *aSample; /* Samples of the left-most key */ +#endif +}; + +/* +** Each sample stored in the sqlite_stat3 table is represented in memory +** using a structure of this type. See documentation at the top of the +** analyze.c source file for additional information. +*/ +struct IndexSample { + union { + char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ + double r; /* Value if eType is SQLITE_FLOAT */ + i64 i; /* Value if eType is SQLITE_INTEGER */ + } u; + u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */ + int nByte; /* Size in byte of text or blob. */ + tRowcnt nEq; /* Est. number of rows where the key equals this sample */ + tRowcnt nLt; /* Est. number of rows where key is less than this sample */ + tRowcnt nDLt; /* Est. number of distinct keys less than this sample */ +}; + +/* +** Each token coming out of the lexer is an instance of +** this structure. Tokens are also used as part of an expression. +** +** Note if Token.z==0 then Token.dyn and Token.n are undefined and +** may contain random values. Do not make any assumptions about Token.dyn +** and Token.n when Token.z==0. +*/ +struct Token { + const char *z; /* Text of the token. Not NULL-terminated! */ + unsigned int n; /* Number of characters in this token */ +}; + +/* +** An instance of this structure contains information needed to generate +** code for a SELECT that contains aggregate functions. +** +** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a +** pointer to this structure. The Expr.iColumn field is the index in +** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate +** code for that node. +** +** AggInfo.pGroupBy and AggInfo.aFunc.pExpr point to fields within the +** original Select structure that describes the SELECT statement. These +** fields do not need to be freed when deallocating the AggInfo structure. +*/ +struct AggInfo { + u8 directMode; /* Direct rendering mode means take data directly + ** from source tables rather than from accumulators */ + u8 useSortingIdx; /* In direct mode, reference the sorting index rather + ** than the source table */ + int sortingIdx; /* Cursor number of the sorting index */ + int sortingIdxPTab; /* Cursor number of pseudo-table */ + int nSortingColumn; /* Number of columns in the sorting index */ + ExprList *pGroupBy; /* The group by clause */ + struct AggInfo_col { /* For each column used in source tables */ + Table *pTab; /* Source table */ + int iTable; /* Cursor number of the source table */ + int iColumn; /* Column number within the source table */ + int iSorterColumn; /* Column number in the sorting index */ + int iMem; /* Memory location that acts as accumulator */ + Expr *pExpr; /* The original expression */ + } *aCol; + int nColumn; /* Number of used entries in aCol[] */ + int nAccumulator; /* Number of columns that show through to the output. + ** Additional columns are used only as parameters to + ** aggregate functions */ + struct AggInfo_func { /* For each aggregate function */ + Expr *pExpr; /* Expression encoding the function */ + FuncDef *pFunc; /* The aggregate function implementation */ + int iMem; /* Memory location that acts as accumulator */ + int iDistinct; /* Ephemeral table used to enforce DISTINCT */ + } *aFunc; + int nFunc; /* Number of entries in aFunc[] */ +}; + +/* +** The datatype ynVar is a signed integer, either 16-bit or 32-bit. +** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater +** than 32767 we have to make it 32-bit. 16-bit is preferred because +** it uses less memory in the Expr object, which is a big memory user +** in systems with lots of prepared statements. And few applications +** need more than about 10 or 20 variables. But some extreme users want +** to have prepared statements with over 32767 variables, and for them +** the option is available (at compile-time). +*/ +#if SQLITE_MAX_VARIABLE_NUMBER<=32767 +typedef i16 ynVar; +#else +typedef int ynVar; +#endif + +/* +** Each node of an expression in the parse tree is an instance +** of this structure. +** +** Expr.op is the opcode. The integer parser token codes are reused +** as opcodes here. For example, the parser defines TK_GE to be an integer +** code representing the ">=" operator. This same integer code is reused +** to represent the greater-than-or-equal-to operator in the expression +** tree. +** +** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, +** or TK_STRING), then Expr.token contains the text of the SQL literal. If +** the expression is a variable (TK_VARIABLE), then Expr.token contains the +** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), +** then Expr.token contains the name of the function. +** +** Expr.pRight and Expr.pLeft are the left and right subexpressions of a +** binary operator. Either or both may be NULL. +** +** Expr.x.pList is a list of arguments if the expression is an SQL function, +** a CASE expression or an IN expression of the form " IN (, ...)". +** Expr.x.pSelect is used if the expression is a sub-select or an expression of +** the form " IN (SELECT ...)". If the EP_xIsSelect bit is set in the +** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is +** valid. +** +** An expression of the form ID or ID.ID refers to a column in a table. +** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is +** the integer cursor number of a VDBE cursor pointing to that table and +** Expr.iColumn is the column number for the specific column. If the +** expression is used as a result in an aggregate SELECT, then the +** value is also stored in the Expr.iAgg column in the aggregate so that +** it can be accessed after all aggregates are computed. +** +** If the expression is an unbound variable marker (a question mark +** character '?' in the original SQL) then the Expr.iTable holds the index +** number for that variable. +** +** If the expression is a subquery then Expr.iColumn holds an integer +** register number containing the result of the subquery. If the +** subquery gives a constant result, then iTable is -1. If the subquery +** gives a different answer at different times during statement processing +** then iTable is the address of a subroutine that computes the subquery. +** +** If the Expr is of type OP_Column, and the table it is selecting from +** is a disk table or the "old.*" pseudo-table, then pTab points to the +** corresponding table definition. +** +** ALLOCATION NOTES: +** +** Expr objects can use a lot of memory space in database schema. To +** help reduce memory requirements, sometimes an Expr object will be +** truncated. And to reduce the number of memory allocations, sometimes +** two or more Expr objects will be stored in a single memory allocation, +** together with Expr.zToken strings. +** +** If the EP_Reduced and EP_TokenOnly flags are set when +** an Expr object is truncated. When EP_Reduced is set, then all +** the child Expr objects in the Expr.pLeft and Expr.pRight subtrees +** are contained within the same memory allocation. Note, however, that +** the subtrees in Expr.x.pList or Expr.x.pSelect are always separately +** allocated, regardless of whether or not EP_Reduced is set. +*/ +struct Expr { + u8 op; /* Operation performed by this node */ + char affinity; /* The affinity of the column or 0 if not a column */ + u16 flags; /* Various flags. EP_* See below */ + union { + char *zToken; /* Token value. Zero terminated and dequoted */ + int iValue; /* Non-negative integer value if EP_IntValue */ + } u; + + /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no + ** space is allocated for the fields below this point. An attempt to + ** access them will result in a segfault or malfunction. + *********************************************************************/ + + Expr *pLeft; /* Left subnode */ + Expr *pRight; /* Right subnode */ + union { + ExprList *pList; /* Function arguments or in " IN ( IN ()" */ - } x; - - /* If the EP_Reduced flag is set in the Expr.flags mask, then no - ** space is allocated for the fields below this point. An attempt to - ** access them will result in a segfault or malfunction. - *********************************************************************/ - -#if SQLITE_MAX_EXPR_DEPTH>0 - int nHeight; /* Height of the tree headed by this node */ -#endif - int iTable; /* TK_COLUMN: cursor number of table holding column - ** TK_REGISTER: register number - ** TK_TRIGGER: 1 -> new, 0 -> old */ - ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid. - ** TK_VARIABLE: variable number (always >= 1). */ - i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ - i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ - u8 flags2; /* Second set of flags. EP2_... */ - u8 op2; /* TK_REGISTER: original value of Expr.op - ** TK_COLUMN: the value of p5 for OP_Column - ** TK_AGG_FUNCTION: nesting depth */ - AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ - Table *pTab; /* Table for TK_COLUMN expressions. */ -}; - -/* -** The following are the meanings of bits in the Expr.flags field. -*/ -#define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */ -#define EP_Agg 0x0002 /* Contains one or more aggregate functions */ -#define EP_Resolved 0x0004 /* IDs have been resolved to COLUMNs */ -#define EP_Error 0x0008 /* Expression contains one or more errors */ -#define EP_Distinct 0x0010 /* Aggregate function with DISTINCT keyword */ -#define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */ -#define EP_DblQuoted 0x0040 /* token.z was originally in "..." */ -#define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */ -#define EP_Collate 0x0100 /* Tree contains a TK_COLLATE opeartor */ -#define EP_FixedDest 0x0200 /* Result needed in a specific register */ -#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */ -#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */ -#define EP_Hint 0x1000 /* Not used */ -#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */ -#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */ -#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */ - -/* -** The following are the meanings of bits in the Expr.flags2 field. -*/ -#define EP2_MallocedToken 0x0001 /* Need to sqlite3DbFree() Expr.zToken */ -#define EP2_Irreducible 0x0002 /* Cannot EXPRDUP_REDUCE this Expr */ - -/* -** The pseudo-routine sqlite3ExprSetIrreducible sets the EP2_Irreducible -** flag on an expression structure. This flag is used for VV&A only. The -** routine is implemented as a macro that only works when in debugging mode, -** so as not to burden production code. -*/ -#ifdef SQLITE_DEBUG -# define ExprSetIrreducible(X) (X)->flags2 |= EP2_Irreducible -#else -# define ExprSetIrreducible(X) -#endif - -/* -** These macros can be used to test, set, or clear bits in the -** Expr.flags field. -*/ -#define ExprHasProperty(E,P) (((E)->flags&(P))==(P)) -#define ExprHasAnyProperty(E,P) (((E)->flags&(P))!=0) -#define ExprSetProperty(E,P) (E)->flags|=(P) -#define ExprClearProperty(E,P) (E)->flags&=~(P) - -/* -** Macros to determine the number of bytes required by a normal Expr -** struct, an Expr struct with the EP_Reduced flag set in Expr.flags -** and an Expr struct with the EP_TokenOnly flag set. -*/ -#define EXPR_FULLSIZE sizeof(Expr) /* Full size */ -#define EXPR_REDUCEDSIZE offsetof(Expr,iTable) /* Common features */ -#define EXPR_TOKENONLYSIZE offsetof(Expr,pLeft) /* Fewer features */ - -/* -** Flags passed to the sqlite3ExprDup() function. See the header comment -** above sqlite3ExprDup() for details. -*/ -#define EXPRDUP_REDUCE 0x0001 /* Used reduced-size Expr nodes */ - -/* -** A list of expressions. Each expression may optionally have a -** name. An expr/name combination can be used in several ways, such -** as the list of "expr AS ID" fields following a "SELECT" or in the -** list of "ID = expr" items in an UPDATE. A list of expressions can -** also be used as the argument to a function, in which case the a.zName -** field is not used. -** -** By default the Expr.zSpan field holds a human-readable description of -** the expression that is used in the generation of error messages and -** column labels. In this case, Expr.zSpan is typically the text of a -** column expression as it exists in a SELECT statement. However, if -** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name -** of the result column in the form: DATABASE.TABLE.COLUMN. This later -** form is used for name resolution with nested FROM clauses. -*/ -struct ExprList { - int nExpr; /* Number of expressions on the list */ - int iECursor; /* VDBE Cursor associated with this ExprList */ - struct ExprList_item { /* For each expression in the list */ - Expr *pExpr; /* The list of expressions */ - char *zName; /* Token associated with this expression */ - char *zSpan; /* Original text of the expression */ - u8 sortOrder; /* 1 for DESC or 0 for ASC */ - unsigned done :1; /* A flag to indicate when processing is finished */ - unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ - u16 iOrderByCol; /* For ORDER BY, column number in result set */ - u16 iAlias; /* Index into Parse.aAlias[] for zName */ - } *a; /* Alloc a power of two greater or equal to nExpr */ -}; - -/* -** An instance of this structure is used by the parser to record both -** the parse tree for an expression and the span of input text for an -** expression. -*/ -struct ExprSpan { - Expr *pExpr; /* The expression parse tree */ - const char *zStart; /* First character of input text */ - const char *zEnd; /* One character past the end of input text */ -}; - -/* -** An instance of this structure can hold a simple list of identifiers, -** such as the list "a,b,c" in the following statements: -** -** INSERT INTO t(a,b,c) VALUES ...; -** CREATE INDEX idx ON t(a,b,c); -** CREATE TRIGGER trig BEFORE UPDATE ON t(a,b,c) ...; -** -** The IdList.a.idx field is used when the IdList represents the list of -** column names after a table name in an INSERT statement. In the statement -** -** INSERT INTO t(a,b,c) ... -** -** If "a" is the k-th column of table "t", then IdList.a[0].idx==k. -*/ -struct IdList { - struct IdList_item { - char *zName; /* Name of the identifier */ - int idx; /* Index in some Table.aCol[] of a column named zName */ - } *a; - int nId; /* Number of identifiers on the list */ -}; - -/* -** The bitmask datatype defined below is used for various optimizations. -** -** Changing this from a 64-bit to a 32-bit type limits the number of -** tables in a join to 32 instead of 64. But it also reduces the size -** of the library by 738 bytes on ix86. -*/ -typedef u64 Bitmask; - -/* -** The number of bits in a Bitmask. "BMS" means "BitMask Size". -*/ -#define BMS ((int)(sizeof(Bitmask)*8)) - -/* -** The following structure describes the FROM clause of a SELECT statement. -** Each table or subquery in the FROM clause is a separate element of -** the SrcList.a[] array. -** -** With the addition of multiple database support, the following structure -** can also be used to describe a particular table such as the table that -** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL, -** such a table must be a simple name: ID. But in SQLite, the table can -** now be identified by a database name, a dot, then the table name: ID.ID. -** -** The jointype starts out showing the join type between the current table -** and the next table on the list. The parser builds the list this way. -** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each -** jointype expresses the join between the table and the previous table. -** -** In the colUsed field, the high-order bit (bit 63) is set if the table -** contains more than 63 columns and the 64-th or later column is used. -*/ -struct SrcList { - i16 nSrc; /* Number of tables or subqueries in the FROM clause */ - i16 nAlloc; /* Number of entries allocated in a[] below */ - struct SrcList_item { - Schema *pSchema; /* Schema to which this item is fixed */ - char *zDatabase; /* Name of database holding this table */ - char *zName; /* Name of the table */ - char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ - Table *pTab; /* An SQL table corresponding to zName */ - Select *pSelect; /* A SELECT statement used in place of a table name */ - int addrFillSub; /* Address of subroutine to manifest a subquery */ - int regReturn; /* Register holding return address of addrFillSub */ - u8 jointype; /* Type of join between this able and the previous */ - unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ - unsigned isCorrelated :1; /* True if sub-query is correlated */ - unsigned viaCoroutine :1; /* Implemented as a co-routine */ -#ifndef SQLITE_OMIT_EXPLAIN - u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */ -#endif - int iCursor; /* The VDBE cursor number used to access this table */ - Expr *pOn; /* The ON clause of a join */ - IdList *pUsing; /* The USING clause of a join */ - Bitmask colUsed; /* Bit N (1<" clause */ - Index *pIndex; /* Index structure corresponding to zIndex, if any */ - } a[1]; /* One entry for each identifier on the list */ -}; - -/* -** Permitted values of the SrcList.a.jointype field -*/ -#define JT_INNER 0x0001 /* Any kind of inner or cross join */ -#define JT_CROSS 0x0002 /* Explicit use of the CROSS keyword */ -#define JT_NATURAL 0x0004 /* True for a "natural" join */ -#define JT_LEFT 0x0008 /* Left outer join */ -#define JT_RIGHT 0x0010 /* Right outer join */ -#define JT_OUTER 0x0020 /* The "OUTER" keyword is present */ -#define JT_ERROR 0x0040 /* unknown or unsupported join type */ - - -/* -** A WherePlan object holds information that describes a lookup -** strategy. -** -** This object is intended to be opaque outside of the where.c module. -** It is included here only so that that compiler will know how big it -** is. None of the fields in this object should be used outside of -** the where.c module. -** -** Within the union, pIdx is only used when wsFlags&WHERE_INDEXED is true. -** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx -** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the -** case that more than one of these conditions is true. -*/ -struct WherePlan { - u32 wsFlags; /* WHERE_* flags that describe the strategy */ - u16 nEq; /* Number of == constraints */ - u16 nOBSat; /* Number of ORDER BY terms satisfied */ - double nRow; /* Estimated number of rows (for EQP) */ - union { - Index *pIdx; /* Index when WHERE_INDEXED is true */ - struct WhereTerm *pTerm; /* WHERE clause term for OR-search */ - sqlite3_index_info *pVtabIdx; /* Virtual table index to use */ - } u; -}; - -/* -** For each nested loop in a WHERE clause implementation, the WhereInfo -** structure contains a single instance of this structure. This structure -** is intended to be private to the where.c module and should not be -** access or modified by other modules. -** -** The pIdxInfo field is used to help pick the best index on a -** virtual table. The pIdxInfo pointer contains indexing -** information for the i-th table in the FROM clause before reordering. -** All the pIdxInfo pointers are freed by whereInfoFree() in where.c. -** All other information in the i-th WhereLevel object for the i-th table -** after FROM clause ordering. -*/ -struct WhereLevel { - WherePlan plan; /* query plan for this element of the FROM clause */ - int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */ - int iTabCur; /* The VDBE cursor used to access the table */ - int iIdxCur; /* The VDBE cursor used to access pIdx */ - int addrBrk; /* Jump here to break out of the loop */ - int addrNxt; /* Jump here to start the next IN combination */ - int addrCont; /* Jump here to continue with the next loop cycle */ - int addrFirst; /* First instruction of interior of the loop */ - u8 iFrom; /* Which entry in the FROM clause */ - u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */ - int p1, p2; /* Operands of the opcode used to ends the loop */ - union { /* Information that depends on plan.wsFlags */ - struct { - int nIn; /* Number of entries in aInLoop[] */ - struct InLoop { - int iCur; /* The VDBE cursor used by this IN operator */ - int addrInTop; /* Top of the IN loop */ - u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */ - } *aInLoop; /* Information about each nested IN operator */ - } in; /* Used when plan.wsFlags&WHERE_IN_ABLE */ - Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ - } u; - double rOptCost; /* "Optimal" cost for this level */ - - /* The following field is really not part of the current level. But - ** we need a place to cache virtual table index information for each - ** virtual table in the FROM clause and the WhereLevel structure is - ** a convenient place since there is one WhereLevel for each FROM clause - ** element. - */ - sqlite3_index_info *pIdxInfo; /* Index info for n-th source table */ -}; - -/* -** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin() -** and the WhereInfo.wctrlFlags member. -*/ -#define WHERE_ORDERBY_NORMAL 0x0000 /* No-op */ -#define WHERE_ORDERBY_MIN 0x0001 /* ORDER BY processing for min() func */ -#define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */ -#define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */ -#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */ -#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */ -#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */ -#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */ -#define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */ - -/* -** The WHERE clause processing routine has two halves. The -** first part does the start of the WHERE loop and the second -** half does the tail of the WHERE loop. An instance of -** this structure is returned by the first half and passed -** into the second half to give some continuity. -*/ -struct WhereInfo { - Parse *pParse; /* Parsing and code generating context */ - SrcList *pTabList; /* List of tables in the join */ - u16 nOBSat; /* Number of ORDER BY terms satisfied by indices */ - u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ - u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */ - u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ - u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */ - int iTop; /* The very beginning of the WHERE loop */ - int iContinue; /* Jump here to continue with next record */ - int iBreak; /* Jump here to break out of the loop */ - int nLevel; /* Number of nested loop */ - struct WhereClause *pWC; /* Decomposition of the WHERE clause */ - double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ - double nRowOut; /* Estimated number of output rows */ - WhereLevel a[1]; /* Information about each nest loop in WHERE */ -}; - -/* Allowed values for WhereInfo.eDistinct and DistinctCtx.eTnctType */ -#define WHERE_DISTINCT_NOOP 0 /* DISTINCT keyword not used */ -#define WHERE_DISTINCT_UNIQUE 1 /* No duplicates */ -#define WHERE_DISTINCT_ORDERED 2 /* All duplicates are adjacent */ -#define WHERE_DISTINCT_UNORDERED 3 /* Duplicates are scattered */ - -/* -** A NameContext defines a context in which to resolve table and column -** names. The context consists of a list of tables (the pSrcList) field and -** a list of named expression (pEList). The named expression list may -** be NULL. The pSrc corresponds to the FROM clause of a SELECT or -** to the table being operated on by INSERT, UPDATE, or DELETE. The -** pEList corresponds to the result set of a SELECT and is NULL for -** other statements. -** -** NameContexts can be nested. When resolving names, the inner-most -** context is searched first. If no match is found, the next outer -** context is checked. If there is still no match, the next context -** is checked. This process continues until either a match is found -** or all contexts are check. When a match is found, the nRef member of -** the context containing the match is incremented. -** -** Each subquery gets a new NameContext. The pNext field points to the -** NameContext in the parent query. Thus the process of scanning the -** NameContext list corresponds to searching through successively outer -** subqueries looking for a match. -*/ -struct NameContext { - Parse *pParse; /* The parser */ - SrcList *pSrcList; /* One or more tables used to resolve names */ - ExprList *pEList; /* Optional list of named expressions */ - AggInfo *pAggInfo; /* Information about aggregates at this level */ - NameContext *pNext; /* Next outer name context. NULL for outermost */ - int nRef; /* Number of names resolved by this context */ - int nErr; /* Number of errors encountered while resolving names */ - u8 ncFlags; /* Zero or more NC_* flags defined below */ -}; - -/* -** Allowed values for the NameContext, ncFlags field. -*/ -#define NC_AllowAgg 0x01 /* Aggregate functions are allowed here */ -#define NC_HasAgg 0x02 /* One or more aggregate functions seen */ -#define NC_IsCheck 0x04 /* True if resolving names in a CHECK constraint */ -#define NC_InAggFunc 0x08 /* True if analyzing arguments to an agg func */ - -/* -** An instance of the following structure contains all information -** needed to generate code for a single SELECT statement. -** -** nLimit is set to -1 if there is no LIMIT clause. nOffset is set to 0. -** If there is a LIMIT clause, the parser sets nLimit to the value of the -** limit and nOffset to the value of the offset (or 0 if there is not -** offset). But later on, nLimit and nOffset become the memory locations -** in the VDBE that record the limit and offset counters. -** -** addrOpenEphm[] entries contain the address of OP_OpenEphemeral opcodes. -** These addresses must be stored so that we can go back and fill in -** the P4_KEYINFO and P2 parameters later. Neither the KeyInfo nor -** the number of columns in P2 can be computed at the same time -** as the OP_OpenEphm instruction is coded because not -** enough information about the compound query is known at that point. -** The KeyInfo for addrOpenTran[0] and [1] contains collating sequences -** for the result set. The KeyInfo for addrOpenEphm[2] contains collating -** sequences for the ORDER BY clause. -*/ -struct Select { - ExprList *pEList; /* The fields of the result */ - u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */ - u16 selFlags; /* Various SF_* values */ - int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ - int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */ - double nSelectRow; /* Estimated number of result rows */ - SrcList *pSrc; /* The FROM clause */ - Expr *pWhere; /* The WHERE clause */ - ExprList *pGroupBy; /* The GROUP BY clause */ - Expr *pHaving; /* The HAVING clause */ - ExprList *pOrderBy; /* The ORDER BY clause */ - Select *pPrior; /* Prior select in a compound select statement */ - Select *pNext; /* Next select to the left in a compound */ - Select *pRightmost; /* Right-most select in a compound select statement */ - Expr *pLimit; /* LIMIT expression. NULL means not used. */ - Expr *pOffset; /* OFFSET expression. NULL means not used. */ -}; - -/* -** Allowed values for Select.selFlags. The "SF" prefix stands for -** "Select Flag". -*/ -#define SF_Distinct 0x0001 /* Output should be DISTINCT */ -#define SF_Resolved 0x0002 /* Identifiers have been resolved */ -#define SF_Aggregate 0x0004 /* Contains aggregate functions */ -#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */ -#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ -#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ -#define SF_UseSorter 0x0040 /* Sort using a sorter */ -#define SF_Values 0x0080 /* Synthesized from VALUES clause */ -#define SF_Materialize 0x0100 /* Force materialization of views */ -#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ - - -/* -** The results of a select can be distributed in several ways. The -** "SRT" prefix means "SELECT Result Type". -*/ -#define SRT_Union 1 /* Store result as keys in an index */ -#define SRT_Except 2 /* Remove result from a UNION index */ -#define SRT_Exists 3 /* Store 1 if the result is not empty */ -#define SRT_Discard 4 /* Do not save the results anywhere */ - -/* The ORDER BY clause is ignored for all of the above */ -#define IgnorableOrderby(X) ((X->eDest)<=SRT_Discard) - -#define SRT_Output 5 /* Output each row of result */ -#define SRT_Mem 6 /* Store result in a memory cell */ -#define SRT_Set 7 /* Store results as keys in an index */ -#define SRT_Table 8 /* Store result as data with an automatic rowid */ -#define SRT_EphemTab 9 /* Create transient tab and store like SRT_Table */ -#define SRT_Coroutine 10 /* Generate a single row of result */ - -/* -** An instance of this object describes where to put of the results of -** a SELECT statement. -*/ -struct SelectDest { - u8 eDest; /* How to dispose of the results. On of SRT_* above. */ - char affSdst; /* Affinity used when eDest==SRT_Set */ - int iSDParm; /* A parameter used by the eDest disposal method */ - int iSdst; /* Base register where results are written */ - int nSdst; /* Number of registers allocated */ -}; - -/* -** During code generation of statements that do inserts into AUTOINCREMENT -** tables, the following information is attached to the Table.u.autoInc.p -** pointer of each autoincrement table to record some side information that -** the code generator needs. We have to keep per-table autoincrement -** information in case inserts are down within triggers. Triggers do not -** normally coordinate their activities, but we do need to coordinate the -** loading and saving of autoincrement information. -*/ -struct AutoincInfo { - AutoincInfo *pNext; /* Next info block in a list of them all */ - Table *pTab; /* Table this info block refers to */ - int iDb; /* Index in sqlite3.aDb[] of database holding pTab */ - int regCtr; /* Memory register holding the rowid counter */ -}; - -/* -** Size of the column cache -*/ -#ifndef SQLITE_N_COLCACHE -# define SQLITE_N_COLCACHE 10 -#endif - -/* -** At least one instance of the following structure is created for each -** trigger that may be fired while parsing an INSERT, UPDATE or DELETE -** statement. All such objects are stored in the linked list headed at -** Parse.pTriggerPrg and deleted once statement compilation has been -** completed. -** -** A Vdbe sub-program that implements the body and WHEN clause of trigger -** TriggerPrg.pTrigger, assuming a default ON CONFLICT clause of -** TriggerPrg.orconf, is stored in the TriggerPrg.pProgram variable. -** The Parse.pTriggerPrg list never contains two entries with the same -** values for both pTrigger and orconf. -** -** The TriggerPrg.aColmask[0] variable is set to a mask of old.* columns -** accessed (or set to 0 for triggers fired as a result of INSERT -** statements). Similarly, the TriggerPrg.aColmask[1] variable is set to -** a mask of new.* columns used by the program. -*/ -struct TriggerPrg { - Trigger *pTrigger; /* Trigger this program was coded from */ - TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */ - SubProgram *pProgram; /* Program implementing pTrigger/orconf */ - int orconf; /* Default ON CONFLICT policy */ - u32 aColmask[2]; /* Masks of old.*, new.* columns accessed */ -}; - -/* -** The yDbMask datatype for the bitmask of all attached databases. -*/ -#if SQLITE_MAX_ATTACHED>30 - typedef sqlite3_uint64 yDbMask; -#else - typedef unsigned int yDbMask; -#endif - -/* -** An SQL parser context. A copy of this structure is passed through -** the parser and down into all the parser action routine in order to -** carry around information that is global to the entire parse. -** -** The structure is divided into two parts. When the parser and code -** generate call themselves recursively, the first part of the structure -** is constant but the second part is reset at the beginning and end of -** each recursion. -** -** The nTableLock and aTableLock variables are only used if the shared-cache -** feature is enabled (if sqlite3Tsd()->useSharedData is true). They are -** used to store the set of table-locks required by the statement being -** compiled. Function sqlite3TableLock() is used to add entries to the -** list. -*/ -struct Parse { - sqlite3 *db; /* The main database structure */ - char *zErrMsg; /* An error message */ - Vdbe *pVdbe; /* An engine for executing database bytecode */ - int rc; /* Return code from execution */ - u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */ - u8 checkSchema; /* Causes schema cookie check after an error */ - u8 nested; /* Number of nested calls to the parser/code generator */ - u8 nTempReg; /* Number of temporary registers in aTempReg[] */ - u8 nTempInUse; /* Number of aTempReg[] currently checked out */ - u8 nColCache; /* Number of entries in aColCache[] */ - u8 iColCache; /* Next entry in aColCache[] to replace */ - u8 isMultiWrite; /* True if statement may modify/insert multiple rows */ - u8 mayAbort; /* True if statement may throw an ABORT exception */ - int aTempReg[8]; /* Holding area for temporary registers */ - int nRangeReg; /* Size of the temporary register block */ - int iRangeReg; /* First register in temporary register block */ - int nErr; /* Number of errors seen */ - int nTab; /* Number of previously allocated VDBE cursors */ - int nMem; /* Number of memory cells used so far */ - int nSet; /* Number of sets used so far */ - int nOnce; /* Number of OP_Once instructions so far */ - int ckBase; /* Base register of data during check constraints */ - int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ - int iCacheCnt; /* Counter used to generate aColCache[].lru values */ - struct yColCache { - int iTable; /* Table cursor number */ - int iColumn; /* Table column number */ - u8 tempReg; /* iReg is a temp register that needs to be freed */ - int iLevel; /* Nesting level */ - int iReg; /* Reg with value of this column. 0 means none. */ - int lru; /* Least recently used entry has the smallest value */ - } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ - yDbMask writeMask; /* Start a write transaction on these databases */ - yDbMask cookieMask; /* Bitmask of schema verified databases */ - int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ - int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */ - int regRowid; /* Register holding rowid of CREATE TABLE entry */ - int regRoot; /* Register holding root page number for new objects */ - int nMaxArg; /* Max args passed to user function by sub-program */ - Token constraintName;/* Name of the constraint currently being parsed */ -#ifndef SQLITE_OMIT_SHARED_CACHE - int nTableLock; /* Number of locks in aTableLock */ - TableLock *aTableLock; /* Required table locks for shared-cache mode */ -#endif - AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ - - /* Information used while coding trigger programs. */ - Parse *pToplevel; /* Parse structure for main program (or NULL) */ - Table *pTriggerTab; /* Table triggers are being coded for */ - double nQueryLoop; /* Estimated number of iterations of a query */ - u32 oldmask; /* Mask of old.* columns referenced */ - u32 newmask; /* Mask of new.* columns referenced */ - u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ - u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ - u8 disableTriggers; /* True to disable triggers */ - - /* Above is constant between recursions. Below is reset before and after - ** each recursion */ - - int nVar; /* Number of '?' variables seen in the SQL so far */ - int nzVar; /* Number of available slots in azVar[] */ - u8 explain; /* True if the EXPLAIN flag is found on the query */ -#ifndef SQLITE_OMIT_VIRTUALTABLE - u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ - int nVtabLock; /* Number of virtual tables to lock */ -#endif - int nAlias; /* Number of aliased result set columns */ - int nHeight; /* Expression tree height of current sub-select */ -#ifndef SQLITE_OMIT_EXPLAIN - int iSelectId; /* ID of current select for EXPLAIN output */ - int iNextSelectId; /* Next available select ID for EXPLAIN output */ -#endif - char **azVar; /* Pointers to names of parameters */ - Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ - int *aAlias; /* Register used to hold aliased result */ - const char *zTail; /* All SQL text past the last semicolon parsed */ - Table *pNewTable; /* A table being constructed by CREATE TABLE */ - Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ - const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ - Token sNameToken; /* Token with unqualified schema object name */ - Token sLastToken; /* The last token parsed */ -#ifndef SQLITE_OMIT_VIRTUALTABLE - Token sArg; /* Complete text of a module argument */ - Table **apVtabLock; /* Pointer to virtual tables needing locking */ -#endif - Table *pZombieTab; /* List of Table objects to delete after code gen */ - TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ -}; - -/* -** Return true if currently inside an sqlite3_declare_vtab() call. -*/ -#ifdef SQLITE_OMIT_VIRTUALTABLE - #define IN_DECLARE_VTAB 0 -#else - #define IN_DECLARE_VTAB (pParse->declareVtab) -#endif - -/* -** An instance of the following structure can be declared on a stack and used -** to save the Parse.zAuthContext value so that it can be restored later. -*/ -struct AuthContext { - const char *zAuthContext; /* Put saved Parse.zAuthContext here */ - Parse *pParse; /* The Parse structure */ -}; - -/* -** Bitfield flags for P5 value in various opcodes. -*/ -#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */ -#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */ -#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ -#define OPFLAG_APPEND 0x08 /* This is likely to be an append */ -#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ -#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */ -#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ -#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ -#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ -#define OPFLAG_P2ISREG 0x02 /* P2 to OP_Open** is a register number */ -#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */ - -/* - * Each trigger present in the database schema is stored as an instance of - * struct Trigger. - * - * Pointers to instances of struct Trigger are stored in two ways. - * 1. In the "trigHash" hash table (part of the sqlite3* that represents the - * database). This allows Trigger structures to be retrieved by name. - * 2. All triggers associated with a single table form a linked list, using the - * pNext member of struct Trigger. A pointer to the first element of the - * linked list is stored as the "pTrigger" member of the associated - * struct Table. - * - * The "step_list" member points to the first element of a linked list - * containing the SQL statements specified as the trigger program. - */ -struct Trigger { - char *zName; /* The name of the trigger */ - char *table; /* The table or view to which the trigger applies */ - u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ - u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ - Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */ - IdList *pColumns; /* If this is an UPDATE OF trigger, - the is stored here */ - Schema *pSchema; /* Schema containing the trigger */ - Schema *pTabSchema; /* Schema containing the table */ - TriggerStep *step_list; /* Link list of trigger program steps */ - Trigger *pNext; /* Next trigger associated with the table */ -}; - -/* -** A trigger is either a BEFORE or an AFTER trigger. The following constants -** determine which. -** -** If there are multiple triggers, you might of some BEFORE and some AFTER. -** In that cases, the constants below can be ORed together. -*/ -#define TRIGGER_BEFORE 1 -#define TRIGGER_AFTER 2 - -/* - * An instance of struct TriggerStep is used to store a single SQL statement - * that is a part of a trigger-program. - * - * Instances of struct TriggerStep are stored in a singly linked list (linked - * using the "pNext" member) referenced by the "step_list" member of the - * associated struct Trigger instance. The first element of the linked list is - * the first step of the trigger-program. - * - * The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or - * "SELECT" statement. The meanings of the other members is determined by the - * value of "op" as follows: - * - * (op == TK_INSERT) - * orconf -> stores the ON CONFLICT algorithm - * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then - * this stores a pointer to the SELECT statement. Otherwise NULL. - * target -> A token holding the quoted name of the table to insert into. - * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then - * this stores values to be inserted. Otherwise NULL. - * pIdList -> If this is an INSERT INTO ... () VALUES ... - * statement, then this stores the column-names to be - * inserted into. - * - * (op == TK_DELETE) - * target -> A token holding the quoted name of the table to delete from. - * pWhere -> The WHERE clause of the DELETE statement if one is specified. - * Otherwise NULL. - * - * (op == TK_UPDATE) - * target -> A token holding the quoted name of the table to update rows of. - * pWhere -> The WHERE clause of the UPDATE statement if one is specified. - * Otherwise NULL. - * pExprList -> A list of the columns to update and the expressions to update - * them to. See sqlite3Update() documentation of "pChanges" - * argument. - * - */ -struct TriggerStep { - u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ - u8 orconf; /* OE_Rollback etc. */ - Trigger *pTrig; /* The trigger that this step is a part of */ - Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */ - Token target; /* Target table for DELETE, UPDATE, INSERT */ - Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ - ExprList *pExprList; /* SET clause for UPDATE. VALUES clause for INSERT */ - IdList *pIdList; /* Column names for INSERT */ - TriggerStep *pNext; /* Next in the link-list */ - TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */ -}; - -/* -** The following structure contains information used by the sqliteFix... -** routines as they walk the parse tree to make database references -** explicit. -*/ -typedef struct DbFixer DbFixer; -struct DbFixer { - Parse *pParse; /* The parsing context. Error messages written here */ - Schema *pSchema; /* Fix items to this schema */ - const char *zDb; /* Make sure all objects are contained in this database */ - const char *zType; /* Type of the container - used for error messages */ - const Token *pName; /* Name of the container - used for error messages */ -}; - -/* -** An objected used to accumulate the text of a string where we -** do not necessarily know how big the string will be in the end. -*/ -struct StrAccum { - sqlite3 *db; /* Optional database for lookaside. Can be NULL */ - char *zBase; /* A base allocation. Not from malloc. */ - char *zText; /* The string collected so far */ - int nChar; /* Length of the string so far */ - int nAlloc; /* Amount of space allocated in zText */ - int mxAlloc; /* Maximum allowed string length */ - u8 mallocFailed; /* Becomes true if any memory allocation fails */ - u8 useMalloc; /* 0: none, 1: sqlite3DbMalloc, 2: sqlite3_malloc */ - u8 tooBig; /* Becomes true if string size exceeds limits */ -}; - -/* -** A pointer to this structure is used to communicate information -** from sqlite3Init and OP_ParseSchema into the sqlite3InitCallback. -*/ -typedef struct { - sqlite3 *db; /* The database being initialized */ - char **pzErrMsg; /* Error message stored here */ - int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ - int rc; /* Result code stored here */ -} InitData; - -/* -** Structure containing global configuration data for the SQLite library. -** -** This structure also contains some state information. -*/ -struct Sqlite3Config { - int bMemstat; /* True to enable memory status */ - int bCoreMutex; /* True to enable core mutexing */ - int bFullMutex; /* True to enable full mutexing */ - int bOpenUri; /* True to interpret filenames as URIs */ - int bUseCis; /* Use covering indices for full-scans */ - int mxStrlen; /* Maximum string length */ - int szLookaside; /* Default lookaside buffer size */ - int nLookaside; /* Default lookaside buffer count */ - sqlite3_mem_methods m; /* Low-level memory allocation interface */ - sqlite3_mutex_methods mutex; /* Low-level mutex interface */ - sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */ - void *pHeap; /* Heap storage space */ - int nHeap; /* Size of pHeap[] */ - int mnReq, mxReq; /* Min and max heap requests sizes */ - void *pScratch; /* Scratch memory */ - int szScratch; /* Size of each scratch buffer */ - int nScratch; /* Number of scratch buffers */ - void *pPage; /* Page cache memory */ - int szPage; /* Size of each page in pPage[] */ - int nPage; /* Number of pages in pPage[] */ - int mxParserStack; /* maximum depth of the parser stack */ - int sharedCacheEnabled; /* true if shared-cache mode enabled */ - /* The above might be initialized to non-zero. The following need to always - ** initially be zero, however. */ - int isInit; /* True after initialization has finished */ - int inProgress; /* True while initialization in progress */ - int isMutexInit; /* True after mutexes are initialized */ - int isMallocInit; /* True after malloc is initialized */ - int isPCacheInit; /* True after malloc is initialized */ - sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */ - int nRefInitMutex; /* Number of users of pInitMutex */ - void (*xLog)(void*,int,const char*); /* Function for logging */ - void *pLogArg; /* First argument to xLog() */ - int bLocaltimeFault; /* True to fail localtime() calls */ -#ifdef SQLITE_ENABLE_SQLLOG - void(*xSqllog)(void*,sqlite3*,const char*, int); - void *pSqllogArg; -#endif -}; - -/* -** Context pointer passed down through the tree-walk. -*/ -struct Walker { - int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */ - int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ - Parse *pParse; /* Parser context. */ - int walkerDepth; /* Number of subqueries */ - union { /* Extra data for callback */ - NameContext *pNC; /* Naming context */ - int i; /* Integer value */ - SrcList *pSrcList; /* FROM clause */ - struct SrcCount *pSrcCount; /* Counting column references */ - } u; -}; - -/* Forward declarations */ -SQLITE_PRIVATE int sqlite3WalkExpr(Walker*, Expr*); -SQLITE_PRIVATE int sqlite3WalkExprList(Walker*, ExprList*); -SQLITE_PRIVATE int sqlite3WalkSelect(Walker*, Select*); -SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker*, Select*); -SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker*, Select*); - -/* -** Return code from the parse-tree walking primitives and their -** callbacks. -*/ -#define WRC_Continue 0 /* Continue down into children */ -#define WRC_Prune 1 /* Omit children but continue walking siblings */ -#define WRC_Abort 2 /* Abandon the tree walk */ - -/* -** Assuming zIn points to the first byte of a UTF-8 character, -** advance zIn to point to the first byte of the next UTF-8 character. -*/ -#define SQLITE_SKIP_UTF8(zIn) { \ - if( (*(zIn++))>=0xc0 ){ \ - while( (*zIn & 0xc0)==0x80 ){ zIn++; } \ - } \ -} - -/* -** The SQLITE_*_BKPT macros are substitutes for the error codes with -** the same name but without the _BKPT suffix. These macros invoke -** routines that report the line-number on which the error originated -** using sqlite3_log(). The routines also provide a convenient place -** to set a debugger breakpoint. -*/ -SQLITE_PRIVATE int sqlite3CorruptError(int); -SQLITE_PRIVATE int sqlite3MisuseError(int); -SQLITE_PRIVATE int sqlite3CantopenError(int); -#define SQLITE_CORRUPT_BKPT sqlite3CorruptError(__LINE__) -#define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__) -#define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__) - - -/* -** FTS4 is really an extension for FTS3. It is enabled using the -** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all -** the SQLITE_ENABLE_FTS4 macro to serve as an alisse for SQLITE_ENABLE_FTS3. -*/ -#if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) -# define SQLITE_ENABLE_FTS3 -#endif - -/* -** The ctype.h header is needed for non-ASCII systems. It is also -** needed by FTS3 when FTS3 is included in the amalgamation. -*/ -#if !defined(SQLITE_ASCII) || \ - (defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_AMALGAMATION)) -# include -#endif - -/* -** The following macros mimic the standard library functions toupper(), -** isspace(), isalnum(), isdigit() and isxdigit(), respectively. The -** sqlite versions only work for ASCII characters, regardless of locale. -*/ -#ifdef SQLITE_ASCII -# define sqlite3Toupper(x) ((x)&~(sqlite3CtypeMap[(unsigned char)(x)]&0x20)) -# define sqlite3Isspace(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x01) -# define sqlite3Isalnum(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x06) -# define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02) -# define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04) -# define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) -# define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) -#else -# define sqlite3Toupper(x) toupper((unsigned char)(x)) -# define sqlite3Isspace(x) isspace((unsigned char)(x)) -# define sqlite3Isalnum(x) isalnum((unsigned char)(x)) -# define sqlite3Isalpha(x) isalpha((unsigned char)(x)) -# define sqlite3Isdigit(x) isdigit((unsigned char)(x)) -# define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) -# define sqlite3Tolower(x) tolower((unsigned char)(x)) -#endif - -/* -** Internal function prototypes -*/ -#define sqlite3StrICmp sqlite3_stricmp -SQLITE_PRIVATE int sqlite3Strlen30(const char*); -#define sqlite3StrNICmp sqlite3_strnicmp - -SQLITE_PRIVATE int sqlite3MallocInit(void); -SQLITE_PRIVATE void sqlite3MallocEnd(void); -SQLITE_PRIVATE void *sqlite3Malloc(int); -SQLITE_PRIVATE void *sqlite3MallocZero(int); -SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3*, int); -SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3*, int); -SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3*,const char*); -SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, int); -SQLITE_PRIVATE void *sqlite3Realloc(void*, int); -SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, int); -SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, int); -SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*); -SQLITE_PRIVATE int sqlite3MallocSize(void*); -SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*); -SQLITE_PRIVATE void *sqlite3ScratchMalloc(int); -SQLITE_PRIVATE void sqlite3ScratchFree(void*); -SQLITE_PRIVATE void *sqlite3PageMalloc(int); -SQLITE_PRIVATE void sqlite3PageFree(void*); -SQLITE_PRIVATE void sqlite3MemSetDefault(void); -SQLITE_PRIVATE void sqlite3BenignMallocHooks(void (*)(void), void (*)(void)); -SQLITE_PRIVATE int sqlite3HeapNearlyFull(void); - -/* -** On systems with ample stack space and that support alloca(), make -** use of alloca() to obtain space for large automatic objects. By default, -** obtain space from malloc(). -** -** The alloca() routine never returns NULL. This will cause code paths -** that deal with sqlite3StackAlloc() failures to be unreachable. -*/ -#ifdef SQLITE_USE_ALLOCA -# define sqlite3StackAllocRaw(D,N) alloca(N) -# define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N) -# define sqlite3StackFree(D,P) -#else -# define sqlite3StackAllocRaw(D,N) sqlite3DbMallocRaw(D,N) -# define sqlite3StackAllocZero(D,N) sqlite3DbMallocZero(D,N) -# define sqlite3StackFree(D,P) sqlite3DbFree(D,P) -#endif - -#ifdef SQLITE_ENABLE_MEMSYS3 -SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void); -#endif -#ifdef SQLITE_ENABLE_MEMSYS5 -SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void); -#endif - - -#ifndef SQLITE_MUTEX_OMIT -SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void); -SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3NoopMutex(void); -SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int); -SQLITE_PRIVATE int sqlite3MutexInit(void); -SQLITE_PRIVATE int sqlite3MutexEnd(void); -#endif - -SQLITE_PRIVATE int sqlite3StatusValue(int); -SQLITE_PRIVATE void sqlite3StatusAdd(int, int); -SQLITE_PRIVATE void sqlite3StatusSet(int, int); - -#ifndef SQLITE_OMIT_FLOATING_POINT -SQLITE_PRIVATE int sqlite3IsNaN(double); -#else -# define sqlite3IsNaN(X) 0 -#endif - -SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, int, const char*, va_list); -#ifndef SQLITE_OMIT_TRACE -SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, const char*, ...); -#endif -SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...); -SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list); -SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3*,char*,const char*,...); -#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) -SQLITE_PRIVATE void sqlite3DebugPrintf(const char*, ...); -#endif -#if defined(SQLITE_TEST) -SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*); -#endif - -/* Output formatting for SQLITE_TESTCTRL_EXPLAIN */ -#if defined(SQLITE_ENABLE_TREE_EXPLAIN) -SQLITE_PRIVATE void sqlite3ExplainBegin(Vdbe*); -SQLITE_PRIVATE void sqlite3ExplainPrintf(Vdbe*, const char*, ...); -SQLITE_PRIVATE void sqlite3ExplainNL(Vdbe*); -SQLITE_PRIVATE void sqlite3ExplainPush(Vdbe*); -SQLITE_PRIVATE void sqlite3ExplainPop(Vdbe*); -SQLITE_PRIVATE void sqlite3ExplainFinish(Vdbe*); -SQLITE_PRIVATE void sqlite3ExplainSelect(Vdbe*, Select*); -SQLITE_PRIVATE void sqlite3ExplainExpr(Vdbe*, Expr*); -SQLITE_PRIVATE void sqlite3ExplainExprList(Vdbe*, ExprList*); -SQLITE_PRIVATE const char *sqlite3VdbeExplanation(Vdbe*); -#else -# define sqlite3ExplainBegin(X) -# define sqlite3ExplainSelect(A,B) -# define sqlite3ExplainExpr(A,B) -# define sqlite3ExplainExprList(A,B) -# define sqlite3ExplainFinish(X) -# define sqlite3VdbeExplanation(X) 0 -#endif - - -SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*, ...); -SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...); -SQLITE_PRIVATE int sqlite3Dequote(char*); -SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int); -SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*, char **); -SQLITE_PRIVATE void sqlite3FinishCoding(Parse*); -SQLITE_PRIVATE int sqlite3GetTempReg(Parse*); -SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int); -SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int); -SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int); -SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*); -SQLITE_PRIVATE Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); -SQLITE_PRIVATE Expr *sqlite3Expr(sqlite3*,int,const char*); -SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); -SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*, const Token*); -SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*); -SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*); -SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*); -SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); -SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); -SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); -SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*); -SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); -SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); -SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); -SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); -SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*); -SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int); -SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*); -SQLITE_PRIVATE void sqlite3BeginParse(Parse*,int); -SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*); -SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*); -SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int); -SQLITE_PRIVATE void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int); -SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*); -SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int); -SQLITE_PRIVATE void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int); -SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*); -SQLITE_PRIVATE void sqlite3AddColumnType(Parse*,Token*); -SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,ExprSpan*); -SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*); -SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,Select*); -SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*, - sqlite3_vfs**,char**,char **); -SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*); -SQLITE_PRIVATE int sqlite3CodeOnce(Parse *); - -SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32); -SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec*, u32); -SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec*, u32); -SQLITE_PRIVATE void sqlite3BitvecClear(Bitvec*, u32, void*); -SQLITE_PRIVATE void sqlite3BitvecDestroy(Bitvec*); -SQLITE_PRIVATE u32 sqlite3BitvecSize(Bitvec*); -SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int,int*); - -SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int); -SQLITE_PRIVATE void sqlite3RowSetClear(RowSet*); -SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet*, i64); -SQLITE_PRIVATE int sqlite3RowSetTest(RowSet*, u8 iBatch, i64); -SQLITE_PRIVATE int sqlite3RowSetNext(RowSet*, i64*); - -SQLITE_PRIVATE void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int); - -#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) -SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse*,Table*); -#else -# define sqlite3ViewGetColumnNames(A,B) 0 -#endif - -SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int); -SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int); -SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*); -#ifndef SQLITE_OMIT_AUTOINCREMENT -SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse); -SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse); -#else -# define sqlite3AutoincrementBegin(X) -# define sqlite3AutoincrementEnd(X) -#endif -SQLITE_PRIVATE int sqlite3CodeCoroutine(Parse*, Select*, SelectDest*); -SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int); -SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); -SQLITE_PRIVATE IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); -SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*); -SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int); -SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*); -SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, - Token*, Select*, Expr*, IdList*); -SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); -SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, struct SrcList_item *); -SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList*); -SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); -SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*); -SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*); -SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, - Token*, int, int); -SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int); -SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*); -SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, - Expr*,ExprList*,u16,Expr*,Expr*); -SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*); -SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*); -SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int); -SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); -#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) -SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,Expr*,char*); -#endif -SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); -SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); -SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int); -SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*); -SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); -SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); -SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int); -SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse*, int, int, int); -SQLITE_PRIVATE void sqlite3ExprCachePush(Parse*); -SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*, int); -SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int); -SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*); -SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int); -SQLITE_PRIVATE int sqlite3ExprCode(Parse*, Expr*, int); -SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); -SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); -SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse*, Expr*, int); -SQLITE_PRIVATE void sqlite3ExprCodeConstants(Parse*, Expr*); -SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int); -SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse*, Expr*, int, int); -SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse*, Expr*, int, int); -SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3*,const char*, const char*); -SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,int isView,const char*, const char*); -SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,int isView,struct SrcList_item *); -SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*); -SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); -SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); -SQLITE_PRIVATE void sqlite3Vacuum(Parse*); -SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*); -SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*); -SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*); -SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*); -SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); -SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); -SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*); -SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*); -SQLITE_PRIVATE void sqlite3PrngSaveState(void); -SQLITE_PRIVATE void sqlite3PrngRestoreState(void); -SQLITE_PRIVATE void sqlite3PrngResetState(void); -SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*,int); -SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse*, int); -SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb); -SQLITE_PRIVATE void sqlite3BeginTransaction(Parse*, int); -SQLITE_PRIVATE void sqlite3CommitTransaction(Parse*); -SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse*); -SQLITE_PRIVATE void sqlite3Savepoint(Parse*, int, Token*); -SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *); -SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*); -SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); -SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); -SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*); -SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*); -SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); -SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump(Vdbe*, const Expr*, int, int); -SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); -SQLITE_PRIVATE int sqlite3IsRowid(const char*); -SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int); -SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*); -SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int); -SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int, - int*,int,int,int,int,int*); -SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int); -SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, int); -SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse*, int, int); -SQLITE_PRIVATE void sqlite3MultiWrite(Parse*); -SQLITE_PRIVATE void sqlite3MayAbort(Parse*); -SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, int); -SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,Expr*,int); -SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); -SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); -SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*); -SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int); -SQLITE_PRIVATE void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*); -SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,u8); -SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3*); -SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); -SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void); -SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*); -SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*); -SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int); - -#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) -SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, int); -#endif - -#ifndef SQLITE_OMIT_TRIGGER -SQLITE_PRIVATE void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*, - Expr*,int, int); -SQLITE_PRIVATE void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*); -SQLITE_PRIVATE void sqlite3DropTrigger(Parse*, SrcList*, int); -SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse*, Trigger*); -SQLITE_PRIVATE Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask); -SQLITE_PRIVATE Trigger *sqlite3TriggerList(Parse *, Table *); -SQLITE_PRIVATE void sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *, - int, int, int); -SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, int, int, int); - void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); -SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); -SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*); -SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*, - ExprList*,Select*,u8); -SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8); -SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*); -SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3*, Trigger*); -SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); -SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int); -# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p)) -#else -# define sqlite3TriggersExist(B,C,D,E,F) 0 -# define sqlite3DeleteTrigger(A,B) -# define sqlite3DropTriggerPtr(A,B) -# define sqlite3UnlinkAndDeleteTrigger(A,B,C) -# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I) -# define sqlite3CodeRowTriggerDirect(A,B,C,D,E,F) -# define sqlite3TriggerList(X, Y) 0 -# define sqlite3ParseToplevel(p) p -# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0 -#endif - -SQLITE_PRIVATE int sqlite3JoinType(Parse*, Token*, Token*, Token*); -SQLITE_PRIVATE void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int); -SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse*, int); -#ifndef SQLITE_OMIT_AUTHORIZATION -SQLITE_PRIVATE void sqlite3AuthRead(Parse*,Expr*,Schema*,SrcList*); -SQLITE_PRIVATE int sqlite3AuthCheck(Parse*,int, const char*, const char*, const char*); -SQLITE_PRIVATE void sqlite3AuthContextPush(Parse*, AuthContext*, const char*); -SQLITE_PRIVATE void sqlite3AuthContextPop(AuthContext*); -SQLITE_PRIVATE int sqlite3AuthReadCol(Parse*, const char *, const char *, int); -#else -# define sqlite3AuthRead(a,b,c,d) -# define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK -# define sqlite3AuthContextPush(a,b,c) -# define sqlite3AuthContextPop(a) ((void)(a)) -#endif -SQLITE_PRIVATE void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); -SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*); -SQLITE_PRIVATE int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); -SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); -SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); -SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); -SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); -SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); -SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); -SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); -SQLITE_PRIVATE int sqlite3Atoi(const char*); -SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); -SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); -SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); - -/* -** Routines to read and write variable-length integers. These used to -** be defined locally, but now we use the varint routines in the util.c -** file. Code should use the MACRO forms below, as the Varint32 versions -** are coded to assume the single byte case is already handled (which -** the MACRO form does). -*/ -SQLITE_PRIVATE int sqlite3PutVarint(unsigned char*, u64); -SQLITE_PRIVATE int sqlite3PutVarint32(unsigned char*, u32); -SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *, u64 *); -SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *, u32 *); -SQLITE_PRIVATE int sqlite3VarintLen(u64 v); - -/* -** The header of a record consists of a sequence variable-length integers. -** These integers are almost always small and are encoded as a single byte. -** The following macros take advantage this fact to provide a fast encode -** and decode of the integers in a record header. It is faster for the common -** case where the integer is a single byte. It is a little slower when the -** integer is two or more bytes. But overall it is faster. -** -** The following expressions are equivalent: -** -** x = sqlite3GetVarint32( A, &B ); -** x = sqlite3PutVarint32( A, B ); -** -** x = getVarint32( A, B ); -** x = putVarint32( A, B ); -** -*/ -#define getVarint32(A,B) \ - (u8)((*(A)<(u8)0x80)?((B)=(u32)*(A)),1:sqlite3GetVarint32((A),(u32 *)&(B))) -#define putVarint32(A,B) \ - (u8)(((u32)(B)<(u32)0x80)?(*(A)=(unsigned char)(B)),1:\ - sqlite3PutVarint32((A),(B))) -#define getVarint sqlite3GetVarint -#define putVarint sqlite3PutVarint - - -SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); -SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); -SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); -SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); -SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); -SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); -SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); -SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); -SQLITE_PRIVATE u8 sqlite3HexToInt(int h); -SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); -SQLITE_PRIVATE const char *sqlite3ErrStr(int); -SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); -SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); -SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); -SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr); -SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, Token*); -SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*); -SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*); -SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *); -SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *); -SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int); -SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64); -SQLITE_PRIVATE int sqlite3SubInt64(i64*,i64); -SQLITE_PRIVATE int sqlite3MulInt64(i64*,i64); -SQLITE_PRIVATE int sqlite3AbsInt32(int); -#ifdef SQLITE_ENABLE_8_3_NAMES -SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*); -#else -# define sqlite3FileSuffix3(X,Y) -#endif -SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,int); - -SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8); -SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8); -SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, - void(*)(void*)); -SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*); -SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *); -SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); -#ifdef SQLITE_ENABLE_STAT3 -SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *); -#endif -SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); -SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); -#ifndef SQLITE_AMALGAMATION -SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[]; -SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; -SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; -SQLITE_PRIVATE const Token sqlite3IntTokens[]; -SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; -SQLITE_PRIVATE SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; -#ifndef SQLITE_OMIT_WSD -SQLITE_PRIVATE int sqlite3PendingByte; -#endif -#endif -SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, int, int); -SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*); -SQLITE_PRIVATE void sqlite3AlterFunctions(void); -SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); -SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); -SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); -SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*); -SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int); -SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); -SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); -SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); -SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); -SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); -SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); -SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); -SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *); -SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); -SQLITE_PRIVATE char sqlite3AffinityType(const char*); -SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*); -SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*); -SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*); -SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *); -SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB); -SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*); -SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*); -SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3*, int); -SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); -SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse*, int, int); -SQLITE_PRIVATE void sqlite3SchemaClear(void *); -SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *, Btree *); -SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *); -SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); -SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, - void (*)(sqlite3_context*,int,sqlite3_value **), - void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*), - FuncDestructor *pDestructor -); -SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); -SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); - -SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, char*, int, int); -SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int); -SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum*,int); -SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); -SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum*); -SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int); -SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); - -SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *); -SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); - -/* -** The interface to the LEMON-generated parser -*/ -SQLITE_PRIVATE void *sqlite3ParserAlloc(void*(*)(size_t)); -SQLITE_PRIVATE void sqlite3ParserFree(void*, void(*)(void*)); -SQLITE_PRIVATE void sqlite3Parser(void*, int, Token, Parse*); -#ifdef YYTRACKMAXSTACKDEPTH -SQLITE_PRIVATE int sqlite3ParserStackPeak(void*); -#endif - -SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3*); -#ifndef SQLITE_OMIT_LOAD_EXTENSION -SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3*); -#else -# define sqlite3CloseExtensions(X) -#endif - -#ifndef SQLITE_OMIT_SHARED_CACHE -SQLITE_PRIVATE void sqlite3TableLock(Parse *, int, int, u8, const char *); -#else - #define sqlite3TableLock(v,w,x,y,z) -#endif - -#ifdef SQLITE_TEST -SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*); -#endif - -#ifdef SQLITE_OMIT_VIRTUALTABLE -# define sqlite3VtabClear(Y) -# define sqlite3VtabSync(X,Y) SQLITE_OK -# define sqlite3VtabRollback(X) -# define sqlite3VtabCommit(X) -# define sqlite3VtabInSync(db) 0 -# define sqlite3VtabLock(X) -# define sqlite3VtabUnlock(X) -# define sqlite3VtabUnlockList(X) -# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK -# define sqlite3GetVTable(X,Y) ((VTable*)0) -#else -SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table*); -SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p); -SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **); -SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db); -SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db); -SQLITE_PRIVATE void sqlite3VtabLock(VTable *); -SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *); -SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*); -SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int); -SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*); -# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) -#endif -SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*); -SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int); -SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse*, Token*); -SQLITE_PRIVATE void sqlite3VtabArgInit(Parse*); -SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse*, Token*); -SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); -SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse*, Table*); -SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *); -SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *); -SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); -SQLITE_PRIVATE void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**); -SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); -SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); -SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*); -SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); -SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); -SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3*); -SQLITE_PRIVATE const char *sqlite3JournalModename(int); -#ifndef SQLITE_OMIT_WAL -SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*); -SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); -#endif - -/* Declarations for functions in fkey.c. All of these are replaced by -** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign -** key functionality is available. If OMIT_TRIGGER is defined but -** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In -** this case foreign keys are parsed, but no other functionality is -** provided (enforcement of FK constraints requires the triggers sub-system). -*/ -#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) -SQLITE_PRIVATE void sqlite3FkCheck(Parse*, Table*, int, int); -SQLITE_PRIVATE void sqlite3FkDropTable(Parse*, SrcList *, Table*); -SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int); -SQLITE_PRIVATE int sqlite3FkRequired(Parse*, Table*, int*, int); -SQLITE_PRIVATE u32 sqlite3FkOldmask(Parse*, Table*); -SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *); -#else - #define sqlite3FkActions(a,b,c,d) - #define sqlite3FkCheck(a,b,c,d) - #define sqlite3FkDropTable(a,b,c) - #define sqlite3FkOldmask(a,b) 0 - #define sqlite3FkRequired(a,b,c,d) 0 -#endif -#ifndef SQLITE_OMIT_FOREIGN_KEY -SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*); -SQLITE_PRIVATE int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**); -#else - #define sqlite3FkDelete(a,b) - #define sqlite3FkLocateIndex(a,b,c,d,e) -#endif - - -/* -** Available fault injectors. Should be numbered beginning with 0. -*/ -#define SQLITE_FAULTINJECTOR_MALLOC 0 -#define SQLITE_FAULTINJECTOR_COUNT 1 - -/* -** The interface to the code in fault.c used for identifying "benign" -** malloc failures. This is only present if SQLITE_OMIT_BUILTIN_TEST -** is not defined. -*/ -#ifndef SQLITE_OMIT_BUILTIN_TEST -SQLITE_PRIVATE void sqlite3BeginBenignMalloc(void); -SQLITE_PRIVATE void sqlite3EndBenignMalloc(void); -#else - #define sqlite3BeginBenignMalloc() - #define sqlite3EndBenignMalloc() -#endif - -#define IN_INDEX_ROWID 1 -#define IN_INDEX_EPH 2 -#define IN_INDEX_INDEX_ASC 3 -#define IN_INDEX_INDEX_DESC 4 -SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, int*); - -#ifdef SQLITE_ENABLE_ATOMIC_WRITE -SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int); -SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *); -SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *); -SQLITE_PRIVATE int sqlite3JournalExists(sqlite3_file *p); -#else - #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile) - #define sqlite3JournalExists(p) 1 -#endif - -SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *); -SQLITE_PRIVATE int sqlite3MemJournalSize(void); -SQLITE_PRIVATE int sqlite3IsMemJournal(sqlite3_file *); - -#if SQLITE_MAX_EXPR_DEPTH>0 -SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p); -SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *); -SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int); -#else - #define sqlite3ExprSetHeight(x,y) - #define sqlite3SelectExprHeight(x) 0 - #define sqlite3ExprCheckHeight(x,y) -#endif - -SQLITE_PRIVATE u32 sqlite3Get4byte(const u8*); -SQLITE_PRIVATE void sqlite3Put4byte(u8*, u32); - -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY -SQLITE_PRIVATE void sqlite3ConnectionBlocked(sqlite3 *, sqlite3 *); -SQLITE_PRIVATE void sqlite3ConnectionUnlocked(sqlite3 *db); -SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db); -#else - #define sqlite3ConnectionBlocked(x,y) - #define sqlite3ConnectionUnlocked(x) - #define sqlite3ConnectionClosed(x) -#endif - -#ifdef SQLITE_DEBUG -SQLITE_PRIVATE void sqlite3ParserTrace(FILE*, char *); -#endif - -/* -** If the SQLITE_ENABLE IOTRACE exists then the global variable -** sqlite3IoTrace is a pointer to a printf-like routine used to -** print I/O tracing messages. -*/ -#ifdef SQLITE_ENABLE_IOTRACE -# define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; } -SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe*); -SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*,...); -#else -# define IOTRACE(A) -# define sqlite3VdbeIOTraceSql(X) -#endif - -/* -** These routines are available for the mem2.c debugging memory allocator -** only. They are used to verify that different "types" of memory -** allocations are properly tracked by the system. -** -** sqlite3MemdebugSetType() sets the "type" of an allocation to one of -** the MEMTYPE_* macros defined below. The type must be a bitmask with -** a single bit set. -** -** sqlite3MemdebugHasType() returns true if any of the bits in its second -** argument match the type set by the previous sqlite3MemdebugSetType(). -** sqlite3MemdebugHasType() is intended for use inside assert() statements. -** -** sqlite3MemdebugNoType() returns true if none of the bits in its second -** argument match the type set by the previous sqlite3MemdebugSetType(). -** -** Perhaps the most important point is the difference between MEMTYPE_HEAP -** and MEMTYPE_LOOKASIDE. If an allocation is MEMTYPE_LOOKASIDE, that means -** it might have been allocated by lookaside, except the allocation was -** too large or lookaside was already full. It is important to verify -** that allocations that might have been satisfied by lookaside are not -** passed back to non-lookaside free() routines. Asserts such as the -** example above are placed on the non-lookaside free() routines to verify -** this constraint. -** -** All of this is no-op for a production build. It only comes into -** play when the SQLITE_MEMDEBUG compile-time option is used. -*/ -#ifdef SQLITE_MEMDEBUG -SQLITE_PRIVATE void sqlite3MemdebugSetType(void*,u8); -SQLITE_PRIVATE int sqlite3MemdebugHasType(void*,u8); -SQLITE_PRIVATE int sqlite3MemdebugNoType(void*,u8); -#else -# define sqlite3MemdebugSetType(X,Y) /* no-op */ -# define sqlite3MemdebugHasType(X,Y) 1 -# define sqlite3MemdebugNoType(X,Y) 1 -#endif -#define MEMTYPE_HEAP 0x01 /* General heap allocations */ -#define MEMTYPE_LOOKASIDE 0x02 /* Might have been lookaside memory */ -#define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */ -#define MEMTYPE_PCACHE 0x08 /* Page cache allocations */ -#define MEMTYPE_DB 0x10 /* Uses sqlite3DbMalloc, not sqlite_malloc */ - -#endif /* _SQLITEINT_H_ */ - -/************** End of sqliteInt.h *******************************************/ -/************** Begin file global.c ******************************************/ -/* -** 2008 June 13 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains definitions of global variables and contants. -*/ - -/* An array to map all upper-case characters into their corresponding -** lower-case character. -** -** SQLite only considers US-ASCII (or EBCDIC) characters. We do not -** handle case conversions for the UTF character set since the tables -** involved are nearly as big or bigger than SQLite itself. -*/ -SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = { -#ifdef SQLITE_ASCII - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, - 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, - 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, - 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, - 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, - 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, - 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, - 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, - 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, - 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, - 252,253,254,255 -#endif -#ifdef SQLITE_EBCDIC - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */ - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */ - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */ - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ - 96, 97, 66, 67, 68, 69, 70, 71, 72, 73,106,107,108,109,110,111, /* 6x */ - 112, 81, 82, 83, 84, 85, 86, 87, 88, 89,122,123,124,125,126,127, /* 7x */ - 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ - 144,145,146,147,148,149,150,151,152,153,154,155,156,157,156,159, /* 9x */ - 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ - 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ - 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ - 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ - 224,225,162,163,164,165,166,167,168,169,232,203,204,205,206,207, /* Ex */ - 239,240,241,242,243,244,245,246,247,248,249,219,220,221,222,255, /* Fx */ -#endif -}; - -/* -** The following 256 byte lookup table is used to support SQLites built-in -** equivalents to the following standard library functions: -** -** isspace() 0x01 -** isalpha() 0x02 -** isdigit() 0x04 -** isalnum() 0x06 -** isxdigit() 0x08 -** toupper() 0x20 -** SQLite identifier character 0x40 -** -** Bit 0x20 is set if the mapped character requires translation to upper -** case. i.e. if the character is a lower-case ASCII character. -** If x is a lower-case ASCII character, then its upper-case equivalent -** is (x - 0x20). Therefore toupper() can be implemented as: -** -** (x & ~(map[x]&0x20)) -** -** Standard function tolower() is implemented using the sqlite3UpperToLower[] -** array. tolower() is used more often than toupper() by SQLite. -** -** Bit 0x40 is set if the character non-alphanumeric and can be used in an -** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any -** non-ASCII UTF character. Hence the test for whether or not a character is -** part of an identifier is 0x46. -** -** SQLite's versions are identical to the standard versions assuming a -** locale of "C". They are implemented as macros in sqliteInt.h. -*/ -#ifdef SQLITE_ASCII -SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */ - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */ - 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, /* 20..27 !"#$%&' */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */ - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */ - 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */ - - 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */ - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */ - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */ - 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */ - 0x00, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */ - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */ - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */ - 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */ - - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */ - - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ -}; -#endif - -#ifndef SQLITE_USE_URI -# define SQLITE_USE_URI 0 -#endif - -#ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN -# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 -#endif - -/* -** The following singleton contains the global configuration for -** the SQLite library. -*/ -SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { - SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ - 1, /* bCoreMutex */ - SQLITE_THREADSAFE==1, /* bFullMutex */ - SQLITE_USE_URI, /* bOpenUri */ - SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ - 0x7ffffffe, /* mxStrlen */ - 128, /* szLookaside */ - 500, /* nLookaside */ - {0,0,0,0,0,0,0,0}, /* m */ - {0,0,0,0,0,0,0,0,0}, /* mutex */ - {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ - (void*)0, /* pHeap */ - 0, /* nHeap */ - 0, 0, /* mnHeap, mxHeap */ - (void*)0, /* pScratch */ - 0, /* szScratch */ - 0, /* nScratch */ - (void*)0, /* pPage */ - 0, /* szPage */ - 0, /* nPage */ - 0, /* mxParserStack */ - 0, /* sharedCacheEnabled */ - /* All the rest should always be initialized to zero */ - 0, /* isInit */ - 0, /* inProgress */ - 0, /* isMutexInit */ - 0, /* isMallocInit */ - 0, /* isPCacheInit */ - 0, /* pInitMutex */ - 0, /* nRefInitMutex */ - 0, /* xLog */ - 0, /* pLogArg */ - 0, /* bLocaltimeFault */ -#ifdef SQLITE_ENABLE_SQLLOG - 0, /* xSqllog */ - 0 /* pSqllogArg */ -#endif -}; - - -/* -** Hash table for global functions - functions common to all -** database connections. After initialization, this table is -** read-only. -*/ -SQLITE_PRIVATE SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; - -/* -** Constant tokens for values 0 and 1. -*/ -SQLITE_PRIVATE const Token sqlite3IntTokens[] = { - { "0", 1 }, - { "1", 1 } -}; - - -/* -** The value of the "pending" byte must be 0x40000000 (1 byte past the -** 1-gibabyte boundary) in a compatible database. SQLite never uses -** the database page that contains the pending byte. It never attempts -** to read or write that page. The pending byte page is set assign -** for use by the VFS layers as space for managing file locks. -** -** During testing, it is often desirable to move the pending byte to -** a different position in the file. This allows code that has to -** deal with the pending byte to run on files that are much smaller -** than 1 GiB. The sqlite3_test_control() interface can be used to -** move the pending byte. -** -** IMPORTANT: Changing the pending byte to any value other than -** 0x40000000 results in an incompatible database file format! -** Changing the pending byte during operating results in undefined -** and dileterious behavior. -*/ -#ifndef SQLITE_OMIT_WSD -SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000; -#endif - -/* -** Properties of opcodes. The OPFLG_INITIALIZER macro is -** created by mkopcodeh.awk during compilation. Data is obtained -** from the comments following the "case OP_xxxx:" statements in -** the vdbe.c file. -*/ -SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; - -/************** End of global.c **********************************************/ -/************** Begin file ctime.c *******************************************/ -/* -** 2010 February 23 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file implements routines used to report what compile-time options -** SQLite was built with. -*/ - -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS - - -/* -** An array of names of all compile-time options. This array should -** be sorted A-Z. -** -** This array looks large, but in a typical installation actually uses -** only a handful of compile-time options, so most times this array is usually -** rather short and uses little memory space. -*/ -static const char * const azCompileOpt[] = { - -/* These macros are provided to "stringify" the value of the define -** for those options in which the value is meaningful. */ -#define CTIMEOPT_VAL_(opt) #opt -#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) - -#ifdef SQLITE_32BIT_ROWID - "32BIT_ROWID", -#endif -#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC - "4_BYTE_ALIGNED_MALLOC", -#endif -#ifdef SQLITE_CASE_SENSITIVE_LIKE - "CASE_SENSITIVE_LIKE", -#endif -#ifdef SQLITE_CHECK_PAGES - "CHECK_PAGES", -#endif -#ifdef SQLITE_COVERAGE_TEST - "COVERAGE_TEST", -#endif -#ifdef SQLITE_CURDIR - "CURDIR", -#endif -#ifdef SQLITE_DEBUG - "DEBUG", -#endif -#ifdef SQLITE_DEFAULT_LOCKING_MODE - "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), -#endif -#ifdef SQLITE_DISABLE_DIRSYNC - "DISABLE_DIRSYNC", -#endif -#ifdef SQLITE_DISABLE_LFS - "DISABLE_LFS", -#endif -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - "ENABLE_ATOMIC_WRITE", -#endif -#ifdef SQLITE_ENABLE_CEROD - "ENABLE_CEROD", -#endif -#ifdef SQLITE_ENABLE_COLUMN_METADATA - "ENABLE_COLUMN_METADATA", -#endif -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT - "ENABLE_EXPENSIVE_ASSERT", -#endif -#ifdef SQLITE_ENABLE_FTS1 - "ENABLE_FTS1", -#endif -#ifdef SQLITE_ENABLE_FTS2 - "ENABLE_FTS2", -#endif -#ifdef SQLITE_ENABLE_FTS3 - "ENABLE_FTS3", -#endif -#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS - "ENABLE_FTS3_PARENTHESIS", -#endif -#ifdef SQLITE_ENABLE_FTS4 - "ENABLE_FTS4", -#endif -#ifdef SQLITE_ENABLE_ICU - "ENABLE_ICU", -#endif -#ifdef SQLITE_ENABLE_IOTRACE - "ENABLE_IOTRACE", -#endif -#ifdef SQLITE_ENABLE_LOAD_EXTENSION - "ENABLE_LOAD_EXTENSION", -#endif -#ifdef SQLITE_ENABLE_LOCKING_STYLE - "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), -#endif -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT - "ENABLE_MEMORY_MANAGEMENT", -#endif -#ifdef SQLITE_ENABLE_MEMSYS3 - "ENABLE_MEMSYS3", -#endif -#ifdef SQLITE_ENABLE_MEMSYS5 - "ENABLE_MEMSYS5", -#endif -#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK - "ENABLE_OVERSIZE_CELL_CHECK", -#endif -#ifdef SQLITE_ENABLE_RTREE - "ENABLE_RTREE", -#endif -#ifdef SQLITE_ENABLE_STAT3 - "ENABLE_STAT3", -#endif -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY - "ENABLE_UNLOCK_NOTIFY", -#endif -#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT - "ENABLE_UPDATE_DELETE_LIMIT", -#endif -#ifdef SQLITE_HAS_CODEC - "HAS_CODEC", -#endif -#ifdef SQLITE_HAVE_ISNAN - "HAVE_ISNAN", -#endif -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX - "HOMEGROWN_RECURSIVE_MUTEX", -#endif -#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS - "IGNORE_AFP_LOCK_ERRORS", -#endif -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - "IGNORE_FLOCK_LOCK_ERRORS", -#endif -#ifdef SQLITE_INT64_TYPE - "INT64_TYPE", -#endif -#ifdef SQLITE_LOCK_TRACE - "LOCK_TRACE", -#endif -#ifdef SQLITE_MAX_SCHEMA_RETRY - "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), -#endif -#ifdef SQLITE_MEMDEBUG - "MEMDEBUG", -#endif -#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT - "MIXED_ENDIAN_64BIT_FLOAT", -#endif -#ifdef SQLITE_NO_SYNC - "NO_SYNC", -#endif -#ifdef SQLITE_OMIT_ALTERTABLE - "OMIT_ALTERTABLE", -#endif -#ifdef SQLITE_OMIT_ANALYZE - "OMIT_ANALYZE", -#endif -#ifdef SQLITE_OMIT_ATTACH - "OMIT_ATTACH", -#endif -#ifdef SQLITE_OMIT_AUTHORIZATION - "OMIT_AUTHORIZATION", -#endif -#ifdef SQLITE_OMIT_AUTOINCREMENT - "OMIT_AUTOINCREMENT", -#endif -#ifdef SQLITE_OMIT_AUTOINIT - "OMIT_AUTOINIT", -#endif -#ifdef SQLITE_OMIT_AUTOMATIC_INDEX - "OMIT_AUTOMATIC_INDEX", -#endif -#ifdef SQLITE_OMIT_AUTORESET - "OMIT_AUTORESET", -#endif -#ifdef SQLITE_OMIT_AUTOVACUUM - "OMIT_AUTOVACUUM", -#endif -#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION - "OMIT_BETWEEN_OPTIMIZATION", -#endif -#ifdef SQLITE_OMIT_BLOB_LITERAL - "OMIT_BLOB_LITERAL", -#endif -#ifdef SQLITE_OMIT_BTREECOUNT - "OMIT_BTREECOUNT", -#endif -#ifdef SQLITE_OMIT_BUILTIN_TEST - "OMIT_BUILTIN_TEST", -#endif -#ifdef SQLITE_OMIT_CAST - "OMIT_CAST", -#endif -#ifdef SQLITE_OMIT_CHECK - "OMIT_CHECK", -#endif -/* // redundant -** #ifdef SQLITE_OMIT_COMPILEOPTION_DIAGS -** "OMIT_COMPILEOPTION_DIAGS", -** #endif -*/ -#ifdef SQLITE_OMIT_COMPLETE - "OMIT_COMPLETE", -#endif -#ifdef SQLITE_OMIT_COMPOUND_SELECT - "OMIT_COMPOUND_SELECT", -#endif -#ifdef SQLITE_OMIT_DATETIME_FUNCS - "OMIT_DATETIME_FUNCS", -#endif -#ifdef SQLITE_OMIT_DECLTYPE - "OMIT_DECLTYPE", -#endif -#ifdef SQLITE_OMIT_DEPRECATED - "OMIT_DEPRECATED", -#endif -#ifdef SQLITE_OMIT_DISKIO - "OMIT_DISKIO", -#endif -#ifdef SQLITE_OMIT_EXPLAIN - "OMIT_EXPLAIN", -#endif -#ifdef SQLITE_OMIT_FLAG_PRAGMAS - "OMIT_FLAG_PRAGMAS", -#endif -#ifdef SQLITE_OMIT_FLOATING_POINT - "OMIT_FLOATING_POINT", -#endif -#ifdef SQLITE_OMIT_FOREIGN_KEY - "OMIT_FOREIGN_KEY", -#endif -#ifdef SQLITE_OMIT_GET_TABLE - "OMIT_GET_TABLE", -#endif -#ifdef SQLITE_OMIT_INCRBLOB - "OMIT_INCRBLOB", -#endif -#ifdef SQLITE_OMIT_INTEGRITY_CHECK - "OMIT_INTEGRITY_CHECK", -#endif -#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION - "OMIT_LIKE_OPTIMIZATION", -#endif -#ifdef SQLITE_OMIT_LOAD_EXTENSION - "OMIT_LOAD_EXTENSION", -#endif -#ifdef SQLITE_OMIT_LOCALTIME - "OMIT_LOCALTIME", -#endif -#ifdef SQLITE_OMIT_LOOKASIDE - "OMIT_LOOKASIDE", -#endif -#ifdef SQLITE_OMIT_MEMORYDB - "OMIT_MEMORYDB", -#endif -#ifdef SQLITE_OMIT_OR_OPTIMIZATION - "OMIT_OR_OPTIMIZATION", -#endif -#ifdef SQLITE_OMIT_PAGER_PRAGMAS - "OMIT_PAGER_PRAGMAS", -#endif -#ifdef SQLITE_OMIT_PRAGMA - "OMIT_PRAGMA", -#endif -#ifdef SQLITE_OMIT_PROGRESS_CALLBACK - "OMIT_PROGRESS_CALLBACK", -#endif -#ifdef SQLITE_OMIT_QUICKBALANCE - "OMIT_QUICKBALANCE", -#endif -#ifdef SQLITE_OMIT_REINDEX - "OMIT_REINDEX", -#endif -#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS - "OMIT_SCHEMA_PRAGMAS", -#endif -#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS - "OMIT_SCHEMA_VERSION_PRAGMAS", -#endif -#ifdef SQLITE_OMIT_SHARED_CACHE - "OMIT_SHARED_CACHE", -#endif -#ifdef SQLITE_OMIT_SUBQUERY - "OMIT_SUBQUERY", -#endif -#ifdef SQLITE_OMIT_TCL_VARIABLE - "OMIT_TCL_VARIABLE", -#endif -#ifdef SQLITE_OMIT_TEMPDB - "OMIT_TEMPDB", -#endif -#ifdef SQLITE_OMIT_TRACE - "OMIT_TRACE", -#endif -#ifdef SQLITE_OMIT_TRIGGER - "OMIT_TRIGGER", -#endif -#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION - "OMIT_TRUNCATE_OPTIMIZATION", -#endif -#ifdef SQLITE_OMIT_UTF16 - "OMIT_UTF16", -#endif -#ifdef SQLITE_OMIT_VACUUM - "OMIT_VACUUM", -#endif -#ifdef SQLITE_OMIT_VIEW - "OMIT_VIEW", -#endif -#ifdef SQLITE_OMIT_VIRTUALTABLE - "OMIT_VIRTUALTABLE", -#endif -#ifdef SQLITE_OMIT_WAL - "OMIT_WAL", -#endif -#ifdef SQLITE_OMIT_WSD - "OMIT_WSD", -#endif -#ifdef SQLITE_OMIT_XFER_OPT - "OMIT_XFER_OPT", -#endif -#ifdef SQLITE_PERFORMANCE_TRACE - "PERFORMANCE_TRACE", -#endif -#ifdef SQLITE_PROXY_DEBUG - "PROXY_DEBUG", -#endif -#ifdef SQLITE_RTREE_INT_ONLY - "RTREE_INT_ONLY", -#endif -#ifdef SQLITE_SECURE_DELETE - "SECURE_DELETE", -#endif -#ifdef SQLITE_SMALL_STACK - "SMALL_STACK", -#endif -#ifdef SQLITE_SOUNDEX - "SOUNDEX", -#endif -#ifdef SQLITE_TCL - "TCL", -#endif -#ifdef SQLITE_TEMP_STORE - "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), -#endif -#ifdef SQLITE_TEST - "TEST", -#endif -#ifdef SQLITE_THREADSAFE - "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), -#endif -#ifdef SQLITE_USE_ALLOCA - "USE_ALLOCA", -#endif -#ifdef SQLITE_ZERO_MALLOC - "ZERO_MALLOC" -#endif -}; - -/* -** Given the name of a compile-time option, return true if that option -** was used and false if not. -** -** The name can optionally begin with "SQLITE_" but the "SQLITE_" prefix -** is not required for a match. -*/ -SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ - int i, n; - if( sqlite3StrNICmp(zOptName, "SQLITE_", 7)==0 ) zOptName += 7; - n = sqlite3Strlen30(zOptName); - - /* Since ArraySize(azCompileOpt) is normally in single digits, a - ** linear search is adequate. No need for a binary search. */ - for(i=0; i=0 && NaDb[] (or -1) */ - int pseudoTableReg; /* Register holding pseudotable content. */ - int nField; /* Number of fields in the header */ - Bool zeroed; /* True if zeroed out and ready for reuse */ - Bool rowidIsValid; /* True if lastRowid is valid */ - Bool atFirst; /* True if pointing to first entry */ - Bool useRandomRowid; /* Generate new record numbers semi-randomly */ - Bool nullRow; /* True if pointing to a row with no data */ - Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ - Bool isTable; /* True if a table requiring integer keys */ - Bool isIndex; /* True if an index containing keys only - no data */ - Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ - Bool isSorter; /* True if a new-style sorter */ - Bool multiPseudo; /* Multi-register pseudo-cursor */ - sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ - const sqlite3_module *pModule; /* Module for cursor pVtabCursor */ - i64 seqCount; /* Sequence counter */ - i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ - i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ - VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ - - /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or - ** OP_IsUnique opcode on this cursor. */ - int seekResult; - - /* Cached information about the header for the data record that the - ** cursor is currently pointing to. Only valid if cacheStatus matches - ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of - ** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that - ** the cache is out of date. - ** - ** aRow might point to (ephemeral) data for the current row, or it might - ** be NULL. - */ - u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ - int payloadSize; /* Total number of bytes in the record */ - u32 *aType; /* Type values for all entries in the record */ - u32 *aOffset; /* Cached offsets to the start of each columns data */ - u8 *aRow; /* Data for the current row, if all on one page */ -}; -typedef struct VdbeCursor VdbeCursor; - -/* -** When a sub-program is executed (OP_Program), a structure of this type -** is allocated to store the current value of the program counter, as -** well as the current memory cell array and various other frame specific -** values stored in the Vdbe struct. When the sub-program is finished, -** these values are copied back to the Vdbe from the VdbeFrame structure, -** restoring the state of the VM to as it was before the sub-program -** began executing. -** -** The memory for a VdbeFrame object is allocated and managed by a memory -** cell in the parent (calling) frame. When the memory cell is deleted or -** overwritten, the VdbeFrame object is not freed immediately. Instead, it -** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame -** list is deleted when the VM is reset in VdbeHalt(). The reason for doing -** this instead of deleting the VdbeFrame immediately is to avoid recursive -** calls to sqlite3VdbeMemRelease() when the memory cells belonging to the -** child frame are released. -** -** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is -** set to NULL if the currently executing frame is the main program. -*/ -typedef struct VdbeFrame VdbeFrame; -struct VdbeFrame { - Vdbe *v; /* VM this frame belongs to */ - VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ - Op *aOp; /* Program instructions for parent frame */ - Mem *aMem; /* Array of memory cells for parent frame */ - u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */ - VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ - void *token; /* Copy of SubProgram.token */ - i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ - int nCursor; /* Number of entries in apCsr */ - int pc; /* Program Counter in parent (calling) frame */ - int nOp; /* Size of aOp array */ - int nMem; /* Number of entries in aMem */ - int nOnceFlag; /* Number of entries in aOnceFlag */ - int nChildMem; /* Number of memory cells for child frame */ - int nChildCsr; /* Number of cursors for child frame */ - int nChange; /* Statement changes (Vdbe.nChanges) */ -}; - -#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) - -/* -** A value for VdbeCursor.cacheValid that means the cache is always invalid. -*/ -#define CACHE_STALE 0 - -/* -** Internally, the vdbe manipulates nearly all SQL values as Mem -** structures. Each Mem struct may cache multiple representations (string, -** integer etc.) of the same value. -*/ -struct Mem { - sqlite3 *db; /* The associated database connection */ - char *z; /* String or BLOB value */ - double r; /* Real value */ - union { - i64 i; /* Integer value used when MEM_Int is set in flags */ - int nZero; /* Used when bit MEM_Zero is set in flags */ - FuncDef *pDef; /* Used only when flags==MEM_Agg */ - RowSet *pRowSet; /* Used only when flags==MEM_RowSet */ - VdbeFrame *pFrame; /* Used when flags==MEM_Frame */ - } u; - int n; /* Number of characters in string value, excluding '\0' */ - u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ - u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */ - u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ -#ifdef SQLITE_DEBUG - Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ - void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */ -#endif - void (*xDel)(void *); /* If not null, call this function to delete Mem.z */ - char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */ -}; - -/* One or more of the following flags are set to indicate the validOK -** representations of the value stored in the Mem struct. -** -** If the MEM_Null flag is set, then the value is an SQL NULL value. -** No other flags may be set in this case. -** -** If the MEM_Str flag is set then Mem.z points at a string representation. -** Usually this is encoded in the same unicode encoding as the main -** database (see below for exceptions). If the MEM_Term flag is also -** set, then the string is nul terminated. The MEM_Int and MEM_Real -** flags may coexist with the MEM_Str flag. -*/ -#define MEM_Null 0x0001 /* Value is NULL */ -#define MEM_Str 0x0002 /* Value is a string */ -#define MEM_Int 0x0004 /* Value is an integer */ -#define MEM_Real 0x0008 /* Value is a real number */ -#define MEM_Blob 0x0010 /* Value is a BLOB */ -#define MEM_RowSet 0x0020 /* Value is a RowSet object */ -#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ -#define MEM_Invalid 0x0080 /* Value is undefined */ -#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ -#define MEM_TypeMask 0x01ff /* Mask of type bits */ - - -/* Whenever Mem contains a valid string or blob representation, one of -** the following flags must be set to determine the memory management -** policy for Mem.z. The MEM_Term flag tells us whether or not the -** string is \000 or \u0000 terminated -*/ -#define MEM_Term 0x0200 /* String rep is nul terminated */ -#define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ -#define MEM_Static 0x0800 /* Mem.z points to a static string */ -#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ -#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ -#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ -#ifdef SQLITE_OMIT_INCRBLOB - #undef MEM_Zero - #define MEM_Zero 0x0000 -#endif - -/* -** Clear any existing type flags from a Mem and replace them with f -*/ -#define MemSetTypeFlag(p, f) \ - ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) - -/* -** Return true if a memory cell is not marked as invalid. This macro -** is for use inside assert() statements only. -*/ -#ifdef SQLITE_DEBUG -#define memIsValid(M) ((M)->flags & MEM_Invalid)==0 -#endif - - -/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains -** additional information about auxiliary information bound to arguments -** of the function. This is used to implement the sqlite3_get_auxdata() -** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data -** that can be associated with a constant argument to a function. This -** allows functions such as "regexp" to compile their constant regular -** expression argument once and reused the compiled code for multiple -** invocations. -*/ -struct VdbeFunc { - FuncDef *pFunc; /* The definition of the function */ - int nAux; /* Number of entries allocated for apAux[] */ - struct AuxData { - void *pAux; /* Aux data for the i-th argument */ - void (*xDelete)(void *); /* Destructor for the aux data */ - } apAux[1]; /* One slot for each function argument */ -}; - -/* -** The "context" argument for a installable function. A pointer to an -** instance of this structure is the first argument to the routines used -** implement the SQL functions. -** -** There is a typedef for this structure in sqlite.h. So all routines, -** even the public interface to SQLite, can use a pointer to this structure. -** But this file is the only place where the internal details of this -** structure are known. -** -** This structure is defined inside of vdbeInt.h because it uses substructures -** (Mem) which are only defined there. -*/ -struct sqlite3_context { - FuncDef *pFunc; /* Pointer to function information. MUST BE FIRST */ - VdbeFunc *pVdbeFunc; /* Auxilary data, if created. */ - Mem s; /* The return value is stored here */ - Mem *pMem; /* Memory cell used to store aggregate context */ - CollSeq *pColl; /* Collating sequence */ - int isError; /* Error code returned by the function. */ - int skipFlag; /* Skip skip accumulator loading if true */ -}; - -/* -** An Explain object accumulates indented output which is helpful -** in describing recursive data structures. -*/ -struct Explain { - Vdbe *pVdbe; /* Attach the explanation to this Vdbe */ - StrAccum str; /* The string being accumulated */ - int nIndent; /* Number of elements in aIndent */ - u16 aIndent[100]; /* Levels of indentation */ - char zBase[100]; /* Initial space */ -}; - -/* A bitfield type for use inside of structures. Always follow with :N where -** N is the number of bits. -*/ -typedef unsigned bft; /* Bit Field Type */ - -/* -** An instance of the virtual machine. This structure contains the complete -** state of the virtual machine. -** -** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare() -** is really a pointer to an instance of this structure. -** -** The Vdbe.inVtabMethod variable is set to non-zero for the duration of -** any virtual table method invocations made by the vdbe program. It is -** set to 2 for xDestroy method calls and 1 for all other methods. This -** variable is used for two purposes: to allow xDestroy methods to execute -** "DROP TABLE" statements and to prevent some nasty side effects of -** malloc failure when SQLite is invoked recursively by a virtual table -** method function. -*/ -struct Vdbe { - sqlite3 *db; /* The database connection that owns this statement */ - Op *aOp; /* Space to hold the virtual machine's program */ - Mem *aMem; /* The memory locations */ - Mem **apArg; /* Arguments to currently executing user function */ - Mem *aColName; /* Column names to return */ - Mem *pResultSet; /* Pointer to an array of results */ - int nMem; /* Number of memory locations currently allocated */ - int nOp; /* Number of instructions in the program */ - int nOpAlloc; /* Number of slots allocated for aOp[] */ - int nLabel; /* Number of labels used */ - int *aLabel; /* Space to hold the labels */ - u16 nResColumn; /* Number of columns in one row of the result set */ - int nCursor; /* Number of slots in apCsr[] */ - u32 magic; /* Magic number for sanity checking */ - char *zErrMsg; /* Error message written here */ - Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ - VdbeCursor **apCsr; /* One element of this array for each open cursor */ - Mem *aVar; /* Values for the OP_Variable opcode. */ - char **azVar; /* Name of variables */ - ynVar nVar; /* Number of entries in aVar[] */ - ynVar nzVar; /* Number of entries in azVar[] */ - u32 cacheCtr; /* VdbeCursor row cache generation counter */ - int pc; /* The program counter */ - int rc; /* Value to return */ - u8 errorAction; /* Recovery action to do in case of an error */ - u8 minWriteFileFormat; /* Minimum file format for writable database files */ - bft explain:2; /* True if EXPLAIN present on SQL command */ - bft inVtabMethod:2; /* See comments above */ - bft changeCntOn:1; /* True to update the change-counter */ - bft expired:1; /* True if the VM needs to be recompiled */ - bft runOnlyOnce:1; /* Automatically expire on reset */ - bft usesStmtJournal:1; /* True if uses a statement journal */ - bft readOnly:1; /* True for read-only statements */ - bft isPrepareV2:1; /* True if prepared with prepare_v2() */ - bft doingRerun:1; /* True if rerunning after an auto-reprepare */ - int nChange; /* Number of db changes made since last reset */ - yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ - yDbMask lockMask; /* Subset of btreeMask that requires a lock */ - int iStatement; /* Statement number (or 0 if has not opened stmt) */ - int aCounter[3]; /* Counters used by sqlite3_stmt_status() */ -#ifndef SQLITE_OMIT_TRACE - i64 startTime; /* Time when query started - used for profiling */ -#endif - i64 nFkConstraint; /* Number of imm. FK constraints this VM */ - i64 nStmtDefCons; /* Number of def. constraints when stmt started */ - char *zSql; /* Text of the SQL statement that generated this */ - void *pFree; /* Free this when deleting the vdbe */ -#ifdef SQLITE_DEBUG - FILE *trace; /* Write an execution trace here, if not NULL */ -#endif -#ifdef SQLITE_ENABLE_TREE_EXPLAIN - Explain *pExplain; /* The explainer */ - char *zExplain; /* Explanation of data structures */ -#endif - VdbeFrame *pFrame; /* Parent frame */ - VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */ - int nFrame; /* Number of frames in pFrame list */ - u32 expmask; /* Binding to these vars invalidates VM */ - SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ - int nOnceFlag; /* Size of array aOnceFlag[] */ - u8 *aOnceFlag; /* Flags for OP_Once */ -}; - -/* -** The following are allowed values for Vdbe.magic -*/ -#define VDBE_MAGIC_INIT 0x26bceaa5 /* Building a VDBE program */ -#define VDBE_MAGIC_RUN 0xbdf20da3 /* VDBE is ready to execute */ -#define VDBE_MAGIC_HALT 0x519c2973 /* VDBE has completed execution */ -#define VDBE_MAGIC_DEAD 0xb606c3c8 /* The VDBE has been deallocated */ - -/* -** Function prototypes -*/ -SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); -void sqliteVdbePopStack(Vdbe*,int); -SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor*); -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) -SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, Op*); -#endif -SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32); -SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int); -SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, int, Mem*, int); -SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); -SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc*, int); - -int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); -SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int*); -SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor *, i64 *); -SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*); -SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*); -SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*); -SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*); -SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int); -SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*); -SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); -SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); -SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); -#ifdef SQLITE_OMIT_FLOATING_POINT -# define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 -#else -SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double); -#endif -SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem*); -SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,int); -SQLITE_PRIVATE void sqlite3VdbeMemSetRowSet(Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, int); -SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem*); -SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*); -SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*); -SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p); -SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p); -#define VdbeMemRelease(X) \ - if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \ - sqlite3VdbeMemReleaseExternal(X); -SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); -SQLITE_PRIVATE const char *sqlite3OpcodeName(int); -SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); -SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); -SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); -SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); -SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); -SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p); - -SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *); -SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *); -SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *); -SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *); -SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *, const VdbeCursor *, int *); -SQLITE_PRIVATE int sqlite3VdbeSorterWrite(sqlite3 *, const VdbeCursor *, Mem *); -SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int *); - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 -SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*); -#else -# define sqlite3VdbeEnter(X) -# define sqlite3VdbeLeave(X) -#endif - -#ifdef SQLITE_DEBUG -SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); -#endif - -#ifndef SQLITE_OMIT_FOREIGN_KEY -SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int); -#else -# define sqlite3VdbeCheckFk(p,i) 0 -#endif - -SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem*, u8); -#ifdef SQLITE_DEBUG -SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf); -#endif -SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem); - -#ifndef SQLITE_OMIT_INCRBLOB -SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *); - #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0) -#else - #define sqlite3VdbeMemExpandBlob(x) SQLITE_OK - #define ExpandBlob(P) SQLITE_OK -#endif - -#endif /* !defined(_VDBEINT_H_) */ - -/************** End of vdbeInt.h *********************************************/ -/************** Continuing where we left off in status.c *********************/ - -/* -** Variables in which to record status information. -*/ -typedef struct sqlite3StatType sqlite3StatType; -static SQLITE_WSD struct sqlite3StatType { - int nowValue[10]; /* Current value */ - int mxValue[10]; /* Maximum value */ -} sqlite3Stat = { {0,}, {0,} }; - - -/* The "wsdStat" macro will resolve to the status information -** state vector. If writable static data is unsupported on the target, -** we have to locate the state vector at run-time. In the more common -** case where writable static data is supported, wsdStat can refer directly -** to the "sqlite3Stat" state vector declared above. -*/ -#ifdef SQLITE_OMIT_WSD -# define wsdStatInit sqlite3StatType *x = &GLOBAL(sqlite3StatType,sqlite3Stat) -# define wsdStat x[0] -#else -# define wsdStatInit -# define wsdStat sqlite3Stat -#endif - -/* -** Return the current value of a status parameter. -*/ -SQLITE_PRIVATE int sqlite3StatusValue(int op){ - wsdStatInit; - assert( op>=0 && op=0 && opwsdStat.mxValue[op] ){ - wsdStat.mxValue[op] = wsdStat.nowValue[op]; - } -} - -/* -** Set the value of a status to X. -*/ -SQLITE_PRIVATE void sqlite3StatusSet(int op, int X){ - wsdStatInit; - assert( op>=0 && opwsdStat.mxValue[op] ){ - wsdStat.mxValue[op] = wsdStat.nowValue[op]; - } -} - -/* -** Query status information. -** -** This implementation assumes that reading or writing an aligned -** 32-bit integer is an atomic operation. If that assumption is not true, -** then this routine is not threadsafe. -*/ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ - wsdStatInit; - if( op<0 || op>=ArraySize(wsdStat.nowValue) ){ - return SQLITE_MISUSE_BKPT; - } - *pCurrent = wsdStat.nowValue[op]; - *pHighwater = wsdStat.mxValue[op]; - if( resetFlag ){ - wsdStat.mxValue[op] = wsdStat.nowValue[op]; - } - return SQLITE_OK; -} - -/* -** Query status information for a single database connection -*/ -SQLITE_API int sqlite3_db_status( - sqlite3 *db, /* The database connection whose status is desired */ - int op, /* Status verb */ - int *pCurrent, /* Write current value here */ - int *pHighwater, /* Write high-water mark here */ - int resetFlag /* Reset high-water mark if true */ -){ - int rc = SQLITE_OK; /* Return code */ - sqlite3_mutex_enter(db->mutex); - switch( op ){ - case SQLITE_DBSTATUS_LOOKASIDE_USED: { - *pCurrent = db->lookaside.nOut; - *pHighwater = db->lookaside.mxOut; - if( resetFlag ){ - db->lookaside.mxOut = db->lookaside.nOut; - } - break; - } - - case SQLITE_DBSTATUS_LOOKASIDE_HIT: - case SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE: - case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: { - testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT ); - testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE ); - testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL ); - assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); - assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); - *pCurrent = 0; - *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT]; - if( resetFlag ){ - db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; - } - break; - } - - /* - ** Return an approximation for the amount of memory currently used - ** by all pagers associated with the given database connection. The - ** highwater mark is meaningless and is returned as zero. - */ - case SQLITE_DBSTATUS_CACHE_USED: { - int totalUsed = 0; - int i; - sqlite3BtreeEnterAll(db); - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - Pager *pPager = sqlite3BtreePager(pBt); - totalUsed += sqlite3PagerMemUsed(pPager); - } - } - sqlite3BtreeLeaveAll(db); - *pCurrent = totalUsed; - *pHighwater = 0; - break; - } - - /* - ** *pCurrent gets an accurate estimate of the amount of memory used - ** to store the schema for all databases (main, temp, and any ATTACHed - ** databases. *pHighwater is set to zero. - */ - case SQLITE_DBSTATUS_SCHEMA_USED: { - int i; /* Used to iterate through schemas */ - int nByte = 0; /* Used to accumulate return value */ - - sqlite3BtreeEnterAll(db); - db->pnBytesFreed = &nByte; - for(i=0; inDb; i++){ - Schema *pSchema = db->aDb[i].pSchema; - if( ALWAYS(pSchema!=0) ){ - HashElem *p; - - nByte += sqlite3GlobalConfig.m.xRoundup(sizeof(HashElem)) * ( - pSchema->tblHash.count - + pSchema->trigHash.count - + pSchema->idxHash.count - + pSchema->fkeyHash.count - ); - nByte += sqlite3MallocSize(pSchema->tblHash.ht); - nByte += sqlite3MallocSize(pSchema->trigHash.ht); - nByte += sqlite3MallocSize(pSchema->idxHash.ht); - nByte += sqlite3MallocSize(pSchema->fkeyHash.ht); - - for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){ - sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p)); - } - for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){ - sqlite3DeleteTable(db, (Table *)sqliteHashData(p)); - } - } - } - db->pnBytesFreed = 0; - sqlite3BtreeLeaveAll(db); - - *pHighwater = 0; - *pCurrent = nByte; - break; - } - - /* - ** *pCurrent gets an accurate estimate of the amount of memory used - ** to store all prepared statements. - ** *pHighwater is set to zero. - */ - case SQLITE_DBSTATUS_STMT_USED: { - struct Vdbe *pVdbe; /* Used to iterate through VMs */ - int nByte = 0; /* Used to accumulate return value */ - - db->pnBytesFreed = &nByte; - for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){ - sqlite3VdbeClearObject(db, pVdbe); - sqlite3DbFree(db, pVdbe); - } - db->pnBytesFreed = 0; - - *pHighwater = 0; - *pCurrent = nByte; - - break; - } - - /* - ** Set *pCurrent to the total cache hits or misses encountered by all - ** pagers the database handle is connected to. *pHighwater is always set - ** to zero. - */ - case SQLITE_DBSTATUS_CACHE_HIT: - case SQLITE_DBSTATUS_CACHE_MISS: - case SQLITE_DBSTATUS_CACHE_WRITE:{ - int i; - int nRet = 0; - assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); - assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 ); - - for(i=0; inDb; i++){ - if( db->aDb[i].pBt ){ - Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt); - sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet); - } - } - *pHighwater = 0; - *pCurrent = nRet; - break; - } - - default: { - rc = SQLITE_ERROR; - } - } - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/************** End of status.c **********************************************/ -/************** Begin file date.c ********************************************/ -/* -** 2003 October 31 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the C functions that implement date and time -** functions for SQLite. -** -** There is only one exported symbol in this file - the function -** sqlite3RegisterDateTimeFunctions() found at the bottom of the file. -** All other code has file scope. -** -** SQLite processes all times and dates as Julian Day numbers. The -** dates and times are stored as the number of days since noon -** in Greenwich on November 24, 4714 B.C. according to the Gregorian -** calendar system. -** -** 1970-01-01 00:00:00 is JD 2440587.5 -** 2000-01-01 00:00:00 is JD 2451544.5 -** -** This implemention requires years to be expressed as a 4-digit number -** which means that only dates between 0000-01-01 and 9999-12-31 can -** be represented, even though julian day numbers allow a much wider -** range of dates. -** -** The Gregorian calendar system is used for all dates and times, -** even those that predate the Gregorian calendar. Historians usually -** use the Julian calendar for dates prior to 1582-10-15 and for some -** dates afterwards, depending on locale. Beware of this difference. -** -** The conversion algorithms are implemented based on descriptions -** in the following text: -** -** Jean Meeus -** Astronomical Algorithms, 2nd Edition, 1998 -** ISBM 0-943396-61-1 -** Willmann-Bell, Inc -** Richmond, Virginia (USA) -*/ -/* #include */ -/* #include */ -#include - -#ifndef SQLITE_OMIT_DATETIME_FUNCS - - -/* -** A structure for holding a single date and time. -*/ -typedef struct DateTime DateTime; -struct DateTime { - sqlite3_int64 iJD; /* The julian day number times 86400000 */ - int Y, M, D; /* Year, month, and day */ - int h, m; /* Hour and minutes */ - int tz; /* Timezone offset in minutes */ - double s; /* Seconds */ - char validYMD; /* True (1) if Y,M,D are valid */ - char validHMS; /* True (1) if h,m,s are valid */ - char validJD; /* True (1) if iJD is valid */ - char validTZ; /* True (1) if tz is valid */ -}; - - -/* -** Convert zDate into one or more integers. Additional arguments -** come in groups of 5 as follows: -** -** N number of digits in the integer -** min minimum allowed value of the integer -** max maximum allowed value of the integer -** nextC first character after the integer -** pVal where to write the integers value. -** -** Conversions continue until one with nextC==0 is encountered. -** The function returns the number of successful conversions. -*/ -static int getDigits(const char *zDate, ...){ - va_list ap; - int val; - int N; - int min; - int max; - int nextC; - int *pVal; - int cnt = 0; - va_start(ap, zDate); - do{ - N = va_arg(ap, int); - min = va_arg(ap, int); - max = va_arg(ap, int); - nextC = va_arg(ap, int); - pVal = va_arg(ap, int*); - val = 0; - while( N-- ){ - if( !sqlite3Isdigit(*zDate) ){ - goto end_getDigits; - } - val = val*10 + *zDate - '0'; - zDate++; - } - if( valmax || (nextC!=0 && nextC!=*zDate) ){ - goto end_getDigits; - } - *pVal = val; - zDate++; - cnt++; - }while( nextC ); -end_getDigits: - va_end(ap); - return cnt; -} - -/* -** Parse a timezone extension on the end of a date-time. -** The extension is of the form: -** -** (+/-)HH:MM -** -** Or the "zulu" notation: -** -** Z -** -** If the parse is successful, write the number of minutes -** of change in p->tz and return 0. If a parser error occurs, -** return non-zero. -** -** A missing specifier is not considered an error. -*/ -static int parseTimezone(const char *zDate, DateTime *p){ - int sgn = 0; - int nHr, nMn; - int c; - while( sqlite3Isspace(*zDate) ){ zDate++; } - p->tz = 0; - c = *zDate; - if( c=='-' ){ - sgn = -1; - }else if( c=='+' ){ - sgn = +1; - }else if( c=='Z' || c=='z' ){ - zDate++; - goto zulu_time; - }else{ - return c!=0; - } - zDate++; - if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){ - return 1; - } - zDate += 5; - p->tz = sgn*(nMn + nHr*60); -zulu_time: - while( sqlite3Isspace(*zDate) ){ zDate++; } - return *zDate!=0; -} - -/* -** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF. -** The HH, MM, and SS must each be exactly 2 digits. The -** fractional seconds FFFF can be one or more digits. -** -** Return 1 if there is a parsing error and 0 on success. -*/ -static int parseHhMmSs(const char *zDate, DateTime *p){ - int h, m, s; - double ms = 0.0; - if( getDigits(zDate, 2, 0, 24, ':', &h, 2, 0, 59, 0, &m)!=2 ){ - return 1; - } - zDate += 5; - if( *zDate==':' ){ - zDate++; - if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){ - return 1; - } - zDate += 2; - if( *zDate=='.' && sqlite3Isdigit(zDate[1]) ){ - double rScale = 1.0; - zDate++; - while( sqlite3Isdigit(*zDate) ){ - ms = ms*10.0 + *zDate - '0'; - rScale *= 10.0; - zDate++; - } - ms /= rScale; - } - }else{ - s = 0; - } - p->validJD = 0; - p->validHMS = 1; - p->h = h; - p->m = m; - p->s = s + ms; - if( parseTimezone(zDate, p) ) return 1; - p->validTZ = (p->tz!=0)?1:0; - return 0; -} - -/* -** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume -** that the YYYY-MM-DD is according to the Gregorian calendar. -** -** Reference: Meeus page 61 -*/ -static void computeJD(DateTime *p){ - int Y, M, D, A, B, X1, X2; - - if( p->validJD ) return; - if( p->validYMD ){ - Y = p->Y; - M = p->M; - D = p->D; - }else{ - Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */ - M = 1; - D = 1; - } - if( M<=2 ){ - Y--; - M += 12; - } - A = Y/100; - B = 2 - A + (A/4); - X1 = 36525*(Y+4716)/100; - X2 = 306001*(M+1)/10000; - p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); - p->validJD = 1; - if( p->validHMS ){ - p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000); - if( p->validTZ ){ - p->iJD -= p->tz*60000; - p->validYMD = 0; - p->validHMS = 0; - p->validTZ = 0; - } - } -} - -/* -** Parse dates of the form -** -** YYYY-MM-DD HH:MM:SS.FFF -** YYYY-MM-DD HH:MM:SS -** YYYY-MM-DD HH:MM -** YYYY-MM-DD -** -** Write the result into the DateTime structure and return 0 -** on success and 1 if the input string is not a well-formed -** date. -*/ -static int parseYyyyMmDd(const char *zDate, DateTime *p){ - int Y, M, D, neg; - - if( zDate[0]=='-' ){ - zDate++; - neg = 1; - }else{ - neg = 0; - } - if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){ - return 1; - } - zDate += 10; - while( sqlite3Isspace(*zDate) || 'T'==*(u8*)zDate ){ zDate++; } - if( parseHhMmSs(zDate, p)==0 ){ - /* We got the time */ - }else if( *zDate==0 ){ - p->validHMS = 0; - }else{ - return 1; - } - p->validJD = 0; - p->validYMD = 1; - p->Y = neg ? -Y : Y; - p->M = M; - p->D = D; - if( p->validTZ ){ - computeJD(p); - } - return 0; -} - -/* -** Set the time to the current time reported by the VFS. -** -** Return the number of errors. -*/ -static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ - sqlite3 *db = sqlite3_context_db_handle(context); - if( sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD)==SQLITE_OK ){ - p->validJD = 1; - return 0; - }else{ - return 1; - } -} - -/* -** Attempt to parse the given string into a Julian Day Number. Return -** the number of errors. -** -** The following are acceptable forms for the input string: -** -** YYYY-MM-DD HH:MM:SS.FFF +/-HH:MM -** DDDD.DD -** now -** -** In the first form, the +/-HH:MM is always optional. The fractional -** seconds extension (the ".FFF") is optional. The seconds portion -** (":SS.FFF") is option. The year and date can be omitted as long -** as there is a time string. The time string can be omitted as long -** as there is a year and date. -*/ -static int parseDateOrTime( - sqlite3_context *context, - const char *zDate, - DateTime *p -){ - double r; - if( parseYyyyMmDd(zDate,p)==0 ){ - return 0; - }else if( parseHhMmSs(zDate, p)==0 ){ - return 0; - }else if( sqlite3StrICmp(zDate,"now")==0){ - return setDateTimeToCurrent(context, p); - }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){ - p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5); - p->validJD = 1; - return 0; - } - return 1; -} - -/* -** Compute the Year, Month, and Day from the julian day number. -*/ -static void computeYMD(DateTime *p){ - int Z, A, B, C, D, E, X1; - if( p->validYMD ) return; - if( !p->validJD ){ - p->Y = 2000; - p->M = 1; - p->D = 1; - }else{ - Z = (int)((p->iJD + 43200000)/86400000); - A = (int)((Z - 1867216.25)/36524.25); - A = Z + 1 + A - (A/4); - B = A + 1524; - C = (int)((B - 122.1)/365.25); - D = (36525*C)/100; - E = (int)((B-D)/30.6001); - X1 = (int)(30.6001*E); - p->D = B - D - X1; - p->M = E<14 ? E-1 : E-13; - p->Y = p->M>2 ? C - 4716 : C - 4715; - } - p->validYMD = 1; -} - -/* -** Compute the Hour, Minute, and Seconds from the julian day number. -*/ -static void computeHMS(DateTime *p){ - int s; - if( p->validHMS ) return; - computeJD(p); - s = (int)((p->iJD + 43200000) % 86400000); - p->s = s/1000.0; - s = (int)p->s; - p->s -= s; - p->h = s/3600; - s -= p->h*3600; - p->m = s/60; - p->s += s - p->m*60; - p->validHMS = 1; -} - -/* -** Compute both YMD and HMS -*/ -static void computeYMD_HMS(DateTime *p){ - computeYMD(p); - computeHMS(p); -} - -/* -** Clear the YMD and HMS and the TZ -*/ -static void clearYMD_HMS_TZ(DateTime *p){ - p->validYMD = 0; - p->validHMS = 0; - p->validTZ = 0; -} - -/* -** On recent Windows platforms, the localtime_s() function is available -** as part of the "Secure CRT". It is essentially equivalent to -** localtime_r() available under most POSIX platforms, except that the -** order of the parameters is reversed. -** -** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx. -** -** If the user has not indicated to use localtime_r() or localtime_s() -** already, check for an MSVC build environment that provides -** localtime_s(). -*/ -#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \ - defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) -#define HAVE_LOCALTIME_S 1 -#endif - -#ifndef SQLITE_OMIT_LOCALTIME -/* -** The following routine implements the rough equivalent of localtime_r() -** using whatever operating-system specific localtime facility that -** is available. This routine returns 0 on success and -** non-zero on any kind of error. -** -** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this -** routine will always fail. -*/ -static int osLocaltime(time_t *t, struct tm *pTm){ - int rc; -#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \ - && (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S) - struct tm *pX; -#if SQLITE_THREADSAFE>0 - sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); -#endif - sqlite3_mutex_enter(mutex); - pX = localtime(t); -#ifndef SQLITE_OMIT_BUILTIN_TEST - if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0; -#endif - if( pX ) *pTm = *pX; - sqlite3_mutex_leave(mutex); - rc = pX==0; -#else -#ifndef SQLITE_OMIT_BUILTIN_TEST - if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; -#endif -#if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R - rc = localtime_r(t, pTm)==0; -#else - rc = localtime_s(pTm, t); -#endif /* HAVE_LOCALTIME_R */ -#endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */ - return rc; -} -#endif /* SQLITE_OMIT_LOCALTIME */ - - -#ifndef SQLITE_OMIT_LOCALTIME -/* -** Compute the difference (in milliseconds) between localtime and UTC -** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs, -** return this value and set *pRc to SQLITE_OK. -** -** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value -** is undefined in this case. -*/ -static sqlite3_int64 localtimeOffset( - DateTime *p, /* Date at which to calculate offset */ - sqlite3_context *pCtx, /* Write error here if one occurs */ - int *pRc /* OUT: Error code. SQLITE_OK or ERROR */ -){ - DateTime x, y; - time_t t; - struct tm sLocal; - - /* Initialize the contents of sLocal to avoid a compiler warning. */ - memset(&sLocal, 0, sizeof(sLocal)); - - x = *p; - computeYMD_HMS(&x); - if( x.Y<1971 || x.Y>=2038 ){ - x.Y = 2000; - x.M = 1; - x.D = 1; - x.h = 0; - x.m = 0; - x.s = 0.0; - } else { - int s = (int)(x.s + 0.5); - x.s = s; - } - x.tz = 0; - x.validJD = 0; - computeJD(&x); - t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); - if( osLocaltime(&t, &sLocal) ){ - sqlite3_result_error(pCtx, "local time unavailable", -1); - *pRc = SQLITE_ERROR; - return 0; - } - y.Y = sLocal.tm_year + 1900; - y.M = sLocal.tm_mon + 1; - y.D = sLocal.tm_mday; - y.h = sLocal.tm_hour; - y.m = sLocal.tm_min; - y.s = sLocal.tm_sec; - y.validYMD = 1; - y.validHMS = 1; - y.validJD = 0; - y.validTZ = 0; - computeJD(&y); - *pRc = SQLITE_OK; - return y.iJD - x.iJD; -} -#endif /* SQLITE_OMIT_LOCALTIME */ - -/* -** Process a modifier to a date-time stamp. The modifiers are -** as follows: -** -** NNN days -** NNN hours -** NNN minutes -** NNN.NNNN seconds -** NNN months -** NNN years -** start of month -** start of year -** start of week -** start of day -** weekday N -** unixepoch -** localtime -** utc -** -** Return 0 on success and 1 if there is any kind of error. If the error -** is in a system call (i.e. localtime()), then an error message is written -** to context pCtx. If the error is an unrecognized modifier, no error is -** written to pCtx. -*/ -static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){ - int rc = 1; - int n; - double r; - char *z, zBuf[30]; - z = zBuf; - for(n=0; niJD += localtimeOffset(p, pCtx, &rc); - clearYMD_HMS_TZ(p); - } - break; - } -#endif - case 'u': { - /* - ** unixepoch - ** - ** Treat the current value of p->iJD as the number of - ** seconds since 1970. Convert to a real julian day number. - */ - if( strcmp(z, "unixepoch")==0 && p->validJD ){ - p->iJD = (p->iJD + 43200)/86400 + 21086676*(i64)10000000; - clearYMD_HMS_TZ(p); - rc = 0; - } -#ifndef SQLITE_OMIT_LOCALTIME - else if( strcmp(z, "utc")==0 ){ - sqlite3_int64 c1; - computeJD(p); - c1 = localtimeOffset(p, pCtx, &rc); - if( rc==SQLITE_OK ){ - p->iJD -= c1; - clearYMD_HMS_TZ(p); - p->iJD += c1 - localtimeOffset(p, pCtx, &rc); - } - } -#endif - break; - } - case 'w': { - /* - ** weekday N - ** - ** Move the date to the same time on the next occurrence of - ** weekday N where 0==Sunday, 1==Monday, and so forth. If the - ** date is already on the appropriate weekday, this is a no-op. - */ - if( strncmp(z, "weekday ", 8)==0 - && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8) - && (n=(int)r)==r && n>=0 && r<7 ){ - sqlite3_int64 Z; - computeYMD_HMS(p); - p->validTZ = 0; - p->validJD = 0; - computeJD(p); - Z = ((p->iJD + 129600000)/86400000) % 7; - if( Z>n ) Z -= 7; - p->iJD += (n - Z)*86400000; - clearYMD_HMS_TZ(p); - rc = 0; - } - break; - } - case 's': { - /* - ** start of TTTTT - ** - ** Move the date backwards to the beginning of the current day, - ** or month or year. - */ - if( strncmp(z, "start of ", 9)!=0 ) break; - z += 9; - computeYMD(p); - p->validHMS = 1; - p->h = p->m = 0; - p->s = 0.0; - p->validTZ = 0; - p->validJD = 0; - if( strcmp(z,"month")==0 ){ - p->D = 1; - rc = 0; - }else if( strcmp(z,"year")==0 ){ - computeYMD(p); - p->M = 1; - p->D = 1; - rc = 0; - }else if( strcmp(z,"day")==0 ){ - rc = 0; - } - break; - } - case '+': - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - double rRounder; - for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} - if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){ - rc = 1; - break; - } - if( z[n]==':' ){ - /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the - ** specified number of hours, minutes, seconds, and fractional seconds - ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be - ** omitted. - */ - const char *z2 = z; - DateTime tx; - sqlite3_int64 day; - if( !sqlite3Isdigit(*z2) ) z2++; - memset(&tx, 0, sizeof(tx)); - if( parseHhMmSs(z2, &tx) ) break; - computeJD(&tx); - tx.iJD -= 43200000; - day = tx.iJD/86400000; - tx.iJD -= day*86400000; - if( z[0]=='-' ) tx.iJD = -tx.iJD; - computeJD(p); - clearYMD_HMS_TZ(p); - p->iJD += tx.iJD; - rc = 0; - break; - } - z += n; - while( sqlite3Isspace(*z) ) z++; - n = sqlite3Strlen30(z); - if( n>10 || n<3 ) break; - if( z[n-1]=='s' ){ z[n-1] = 0; n--; } - computeJD(p); - rc = 0; - rRounder = r<0 ? -0.5 : +0.5; - if( n==3 && strcmp(z,"day")==0 ){ - p->iJD += (sqlite3_int64)(r*86400000.0 + rRounder); - }else if( n==4 && strcmp(z,"hour")==0 ){ - p->iJD += (sqlite3_int64)(r*(86400000.0/24.0) + rRounder); - }else if( n==6 && strcmp(z,"minute")==0 ){ - p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0)) + rRounder); - }else if( n==6 && strcmp(z,"second")==0 ){ - p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0*60.0)) + rRounder); - }else if( n==5 && strcmp(z,"month")==0 ){ - int x, y; - computeYMD_HMS(p); - p->M += (int)r; - x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; - p->Y += x; - p->M -= x*12; - p->validJD = 0; - computeJD(p); - y = (int)r; - if( y!=r ){ - p->iJD += (sqlite3_int64)((r - y)*30.0*86400000.0 + rRounder); - } - }else if( n==4 && strcmp(z,"year")==0 ){ - int y = (int)r; - computeYMD_HMS(p); - p->Y += y; - p->validJD = 0; - computeJD(p); - if( y!=r ){ - p->iJD += (sqlite3_int64)((r - y)*365.0*86400000.0 + rRounder); - } - }else{ - rc = 1; - } - clearYMD_HMS_TZ(p); - break; - } - default: { - break; - } - } - return rc; -} - -/* -** Process time function arguments. argv[0] is a date-time stamp. -** argv[1] and following are modifiers. Parse them all and write -** the resulting time into the DateTime structure p. Return 0 -** on success and 1 if there are any errors. -** -** If there are zero parameters (if even argv[0] is undefined) -** then assume a default value of "now" for argv[0]. -*/ -static int isDate( - sqlite3_context *context, - int argc, - sqlite3_value **argv, - DateTime *p -){ - int i; - const unsigned char *z; - int eType; - memset(p, 0, sizeof(*p)); - if( argc==0 ){ - return setDateTimeToCurrent(context, p); - } - if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT - || eType==SQLITE_INTEGER ){ - p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*86400000.0 + 0.5); - p->validJD = 1; - }else{ - z = sqlite3_value_text(argv[0]); - if( !z || parseDateOrTime(context, (char*)z, p) ){ - return 1; - } - } - for(i=1; iaLimit[SQLITE_LIMIT_LENGTH]+1 ); - testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ); - if( n(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - sqlite3_result_error_toobig(context); - return; - }else{ - z = sqlite3DbMallocRaw(db, (int)n); - if( z==0 ){ - sqlite3_result_error_nomem(context); - return; - } - } - computeJD(&x); - computeYMD_HMS(&x); - for(i=j=0; zFmt[i]; i++){ - if( zFmt[i]!='%' ){ - z[j++] = zFmt[i]; - }else{ - i++; - switch( zFmt[i] ){ - case 'd': sqlite3_snprintf(3, &z[j],"%02d",x.D); j+=2; break; - case 'f': { - double s = x.s; - if( s>59.999 ) s = 59.999; - sqlite3_snprintf(7, &z[j],"%06.3f", s); - j += sqlite3Strlen30(&z[j]); - break; - } - case 'H': sqlite3_snprintf(3, &z[j],"%02d",x.h); j+=2; break; - case 'W': /* Fall thru */ - case 'j': { - int nDay; /* Number of days since 1st day of year */ - DateTime y = x; - y.validJD = 0; - y.M = 1; - y.D = 1; - computeJD(&y); - nDay = (int)((x.iJD-y.iJD+43200000)/86400000); - if( zFmt[i]=='W' ){ - int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ - wd = (int)(((x.iJD+43200000)/86400000)%7); - sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7); - j += 2; - }else{ - sqlite3_snprintf(4, &z[j],"%03d",nDay+1); - j += 3; - } - break; - } - case 'J': { - sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0); - j+=sqlite3Strlen30(&z[j]); - break; - } - case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; - case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; - case 's': { - sqlite3_snprintf(30,&z[j],"%lld", - (i64)(x.iJD/1000 - 21086676*(i64)10000)); - j += sqlite3Strlen30(&z[j]); - break; - } - case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; - case 'w': { - z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; - break; - } - case 'Y': { - sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=sqlite3Strlen30(&z[j]); - break; - } - default: z[j++] = '%'; break; - } - } - } - z[j] = 0; - sqlite3_result_text(context, z, -1, - z==zBuf ? SQLITE_TRANSIENT : SQLITE_DYNAMIC); -} - -/* -** current_time() -** -** This function returns the same value as time('now'). -*/ -static void ctimeFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **NotUsed2 -){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - timeFunc(context, 0, 0); -} - -/* -** current_date() -** -** This function returns the same value as date('now'). -*/ -static void cdateFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **NotUsed2 -){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - dateFunc(context, 0, 0); -} - -/* -** current_timestamp() -** -** This function returns the same value as datetime('now'). -*/ -static void ctimestampFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **NotUsed2 -){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - datetimeFunc(context, 0, 0); -} -#endif /* !defined(SQLITE_OMIT_DATETIME_FUNCS) */ - -#ifdef SQLITE_OMIT_DATETIME_FUNCS -/* -** If the library is compiled to omit the full-scale date and time -** handling (to get a smaller binary), the following minimal version -** of the functions current_time(), current_date() and current_timestamp() -** are included instead. This is to support column declarations that -** include "DEFAULT CURRENT_TIME" etc. -** -** This function uses the C-library functions time(), gmtime() -** and strftime(). The format string to pass to strftime() is supplied -** as the user-data for the function. -*/ -static void currentTimeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - time_t t; - char *zFormat = (char *)sqlite3_user_data(context); - sqlite3 *db; - sqlite3_int64 iT; - struct tm *pTm; - struct tm sNow; - char zBuf[20]; - - UNUSED_PARAMETER(argc); - UNUSED_PARAMETER(argv); - - db = sqlite3_context_db_handle(context); - if( sqlite3OsCurrentTimeInt64(db->pVfs, &iT) ) return; - t = iT/1000 - 10000*(sqlite3_int64)21086676; -#ifdef HAVE_GMTIME_R - pTm = gmtime_r(&t, &sNow); -#else - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - pTm = gmtime(&t); - if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); -#endif - if( pTm ){ - strftime(zBuf, 20, zFormat, &sNow); - sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); - } -} -#endif - -/* -** This function registered all of the above C functions as SQL -** functions. This should be the only routine in this file with -** external linkage. -*/ -SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){ - static SQLITE_WSD FuncDef aDateTimeFuncs[] = { -#ifndef SQLITE_OMIT_DATETIME_FUNCS - FUNCTION(julianday, -1, 0, 0, juliandayFunc ), - FUNCTION(date, -1, 0, 0, dateFunc ), - FUNCTION(time, -1, 0, 0, timeFunc ), - FUNCTION(datetime, -1, 0, 0, datetimeFunc ), - FUNCTION(strftime, -1, 0, 0, strftimeFunc ), - FUNCTION(current_time, 0, 0, 0, ctimeFunc ), - FUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), - FUNCTION(current_date, 0, 0, 0, cdateFunc ), -#else - STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc), - STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc), - STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc), -#endif - }; - int i; - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); - FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aDateTimeFuncs); - - for(i=0; ipMethods ){ - rc = pId->pMethods->xClose(pId); - pId->pMethods = 0; - } - return rc; -} -SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file *id, void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xRead(id, pBuf, amt, offset); -} -SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file *id, const void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xWrite(id, pBuf, amt, offset); -} -SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file *id, i64 size){ - return id->pMethods->xTruncate(id, size); -} -SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file *id, int flags){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xSync(id, flags); -} -SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xFileSize(id, pSize); -} -SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file *id, int lockType){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xLock(id, lockType); -} -SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file *id, int lockType){ - return id->pMethods->xUnlock(id, lockType); -} -SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xCheckReservedLock(id, pResOut); -} - -/* -** Use sqlite3OsFileControl() when we are doing something that might fail -** and we need to know about the failures. Use sqlite3OsFileControlHint() -** when simply tossing information over the wall to the VFS and we do not -** really care if the VFS receives and understands the information since it -** is only a hint and can be safely ignored. The sqlite3OsFileControlHint() -** routine has no return value since the return value would be meaningless. -*/ -SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xFileControl(id, op, pArg); -} -SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ - (void)id->pMethods->xFileControl(id, op, pArg); -} - -SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){ - int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize; - return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); -} -SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ - return id->pMethods->xDeviceCharacteristics(id); -} -SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int offset, int n, int flags){ - return id->pMethods->xShmLock(id, offset, n, flags); -} -SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id){ - id->pMethods->xShmBarrier(id); -} -SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int deleteFlag){ - return id->pMethods->xShmUnmap(id, deleteFlag); -} -SQLITE_PRIVATE int sqlite3OsShmMap( - sqlite3_file *id, /* Database file handle */ - int iPage, - int pgsz, - int bExtend, /* True to extend file if necessary */ - void volatile **pp /* OUT: Pointer to mapping */ -){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp); -} - -/* -** The next group of routines are convenience wrappers around the -** VFS methods. -*/ -SQLITE_PRIVATE int sqlite3OsOpen( - sqlite3_vfs *pVfs, - const char *zPath, - sqlite3_file *pFile, - int flags, - int *pFlagsOut -){ - int rc; - DO_OS_MALLOC_TEST(0); - /* 0x87f7f is a mask of SQLITE_OPEN_ flags that are valid to be passed - ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, - ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before - ** reaching the VFS. */ - rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f7f, pFlagsOut); - assert( rc==SQLITE_OK || pFile->pMethods==0 ); - return rc; -} -SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ - DO_OS_MALLOC_TEST(0); - assert( dirSync==0 || dirSync==1 ); - return pVfs->xDelete(pVfs, zPath, dirSync); -} -SQLITE_PRIVATE int sqlite3OsAccess( - sqlite3_vfs *pVfs, - const char *zPath, - int flags, - int *pResOut -){ - DO_OS_MALLOC_TEST(0); - return pVfs->xAccess(pVfs, zPath, flags, pResOut); -} -SQLITE_PRIVATE int sqlite3OsFullPathname( - sqlite3_vfs *pVfs, - const char *zPath, - int nPathOut, - char *zPathOut -){ - DO_OS_MALLOC_TEST(0); - zPathOut[0] = 0; - return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); -} -#ifndef SQLITE_OMIT_LOAD_EXTENSION -SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ - return pVfs->xDlOpen(pVfs, zPath); -} -SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ - pVfs->xDlError(pVfs, nByte, zBufOut); -} -SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *pVfs, void *pHdle, const char *zSym))(void){ - return pVfs->xDlSym(pVfs, pHdle, zSym); -} -SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *pVfs, void *pHandle){ - pVfs->xDlClose(pVfs, pHandle); -} -#endif /* SQLITE_OMIT_LOAD_EXTENSION */ -SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ - return pVfs->xRandomness(pVfs, nByte, zBufOut); -} -SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){ - return pVfs->xSleep(pVfs, nMicro); -} -SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ - int rc; - /* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64() - ** method to get the current date and time if that method is available - ** (if iVersion is 2 or greater and the function pointer is not NULL) and - ** will fall back to xCurrentTime() if xCurrentTimeInt64() is - ** unavailable. - */ - if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ - rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut); - }else{ - double r; - rc = pVfs->xCurrentTime(pVfs, &r); - *pTimeOut = (sqlite3_int64)(r*86400000.0); - } - return rc; -} - -SQLITE_PRIVATE int sqlite3OsOpenMalloc( - sqlite3_vfs *pVfs, - const char *zFile, - sqlite3_file **ppFile, - int flags, - int *pOutFlags -){ - int rc = SQLITE_NOMEM; - sqlite3_file *pFile; - pFile = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile); - if( pFile ){ - rc = sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags); - if( rc!=SQLITE_OK ){ - sqlite3_free(pFile); - }else{ - *ppFile = pFile; - } - } - return rc; -} -SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *pFile){ - int rc = SQLITE_OK; - assert( pFile ); - rc = sqlite3OsClose(pFile); - sqlite3_free(pFile); - return rc; -} - -/* -** This function is a wrapper around the OS specific implementation of -** sqlite3_os_init(). The purpose of the wrapper is to provide the -** ability to simulate a malloc failure, so that the handling of an -** error in sqlite3_os_init() by the upper layers can be tested. -*/ -SQLITE_PRIVATE int sqlite3OsInit(void){ - void *p = sqlite3_malloc(10); - if( p==0 ) return SQLITE_NOMEM; - sqlite3_free(p); - return sqlite3_os_init(); -} - -/* -** The list of all registered VFS implementations. -*/ -static sqlite3_vfs * SQLITE_WSD vfsList = 0; -#define vfsList GLOBAL(sqlite3_vfs *, vfsList) - -/* -** Locate a VFS by name. If no name is given, simply return the -** first VFS on the list. -*/ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){ - sqlite3_vfs *pVfs = 0; -#if SQLITE_THREADSAFE - sqlite3_mutex *mutex; -#endif -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlite3_initialize(); - if( rc ) return 0; -#endif -#if SQLITE_THREADSAFE - mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); -#endif - sqlite3_mutex_enter(mutex); - for(pVfs = vfsList; pVfs; pVfs=pVfs->pNext){ - if( zVfs==0 ) break; - if( strcmp(zVfs, pVfs->zName)==0 ) break; - } - sqlite3_mutex_leave(mutex); - return pVfs; -} - -/* -** Unlink a VFS from the linked list -*/ -static void vfsUnlink(sqlite3_vfs *pVfs){ - assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) ); - if( pVfs==0 ){ - /* No-op */ - }else if( vfsList==pVfs ){ - vfsList = pVfs->pNext; - }else if( vfsList ){ - sqlite3_vfs *p = vfsList; - while( p->pNext && p->pNext!=pVfs ){ - p = p->pNext; - } - if( p->pNext==pVfs ){ - p->pNext = pVfs->pNext; - } - } -} - -/* -** Register a VFS with the system. It is harmless to register the same -** VFS multiple times. The new VFS becomes the default if makeDflt is -** true. -*/ -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){ - MUTEX_LOGIC(sqlite3_mutex *mutex;) -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlite3_initialize(); - if( rc ) return rc; -#endif - MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) - sqlite3_mutex_enter(mutex); - vfsUnlink(pVfs); - if( makeDflt || vfsList==0 ){ - pVfs->pNext = vfsList; - vfsList = pVfs; - }else{ - pVfs->pNext = vfsList->pNext; - vfsList->pNext = pVfs; - } - assert(vfsList); - sqlite3_mutex_leave(mutex); - return SQLITE_OK; -} - -/* -** Unregister a VFS so that it is no longer accessible. -*/ -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){ -#if SQLITE_THREADSAFE - sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); -#endif - sqlite3_mutex_enter(mutex); - vfsUnlink(pVfs); - sqlite3_mutex_leave(mutex); - return SQLITE_OK; -} - -/************** End of os.c **************************************************/ -/************** Begin file fault.c *******************************************/ -/* -** 2008 Jan 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code to support the concept of "benign" -** malloc failures (when the xMalloc() or xRealloc() method of the -** sqlite3_mem_methods structure fails to allocate a block of memory -** and returns 0). -** -** Most malloc failures are non-benign. After they occur, SQLite -** abandons the current operation and returns an error code (usually -** SQLITE_NOMEM) to the user. However, sometimes a fault is not necessarily -** fatal. For example, if a malloc fails while resizing a hash table, this -** is completely recoverable simply by not carrying out the resize. The -** hash table will continue to function normally. So a malloc failure -** during a hash table resize is a benign fault. -*/ - - -#ifndef SQLITE_OMIT_BUILTIN_TEST - -/* -** Global variables. -*/ -typedef struct BenignMallocHooks BenignMallocHooks; -static SQLITE_WSD struct BenignMallocHooks { - void (*xBenignBegin)(void); - void (*xBenignEnd)(void); -} sqlite3Hooks = { 0, 0 }; - -/* The "wsdHooks" macro will resolve to the appropriate BenignMallocHooks -** structure. If writable static data is unsupported on the target, -** we have to locate the state vector at run-time. In the more common -** case where writable static data is supported, wsdHooks can refer directly -** to the "sqlite3Hooks" state vector declared above. -*/ -#ifdef SQLITE_OMIT_WSD -# define wsdHooksInit \ - BenignMallocHooks *x = &GLOBAL(BenignMallocHooks,sqlite3Hooks) -# define wsdHooks x[0] -#else -# define wsdHooksInit -# define wsdHooks sqlite3Hooks -#endif - - -/* -** Register hooks to call when sqlite3BeginBenignMalloc() and -** sqlite3EndBenignMalloc() are called, respectively. -*/ -SQLITE_PRIVATE void sqlite3BenignMallocHooks( - void (*xBenignBegin)(void), - void (*xBenignEnd)(void) -){ - wsdHooksInit; - wsdHooks.xBenignBegin = xBenignBegin; - wsdHooks.xBenignEnd = xBenignEnd; -} - -/* -** This (sqlite3EndBenignMalloc()) is called by SQLite code to indicate that -** subsequent malloc failures are benign. A call to sqlite3EndBenignMalloc() -** indicates that subsequent malloc failures are non-benign. -*/ -SQLITE_PRIVATE void sqlite3BeginBenignMalloc(void){ - wsdHooksInit; - if( wsdHooks.xBenignBegin ){ - wsdHooks.xBenignBegin(); - } -} -SQLITE_PRIVATE void sqlite3EndBenignMalloc(void){ - wsdHooksInit; - if( wsdHooks.xBenignEnd ){ - wsdHooks.xBenignEnd(); - } -} - -#endif /* #ifndef SQLITE_OMIT_BUILTIN_TEST */ - -/************** End of fault.c ***********************************************/ -/************** Begin file mem0.c ********************************************/ -/* -** 2008 October 28 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains a no-op memory allocation drivers for use when -** SQLITE_ZERO_MALLOC is defined. The allocation drivers implemented -** here always fail. SQLite will not operate with these drivers. These -** are merely placeholders. Real drivers must be substituted using -** sqlite3_config() before SQLite will operate. -*/ - -/* -** This version of the memory allocator is the default. It is -** used when no other memory allocator is specified using compile-time -** macros. -*/ -#ifdef SQLITE_ZERO_MALLOC - -/* -** No-op versions of all memory allocation routines -*/ -static void *sqlite3MemMalloc(int nByte){ return 0; } -static void sqlite3MemFree(void *pPrior){ return; } -static void *sqlite3MemRealloc(void *pPrior, int nByte){ return 0; } -static int sqlite3MemSize(void *pPrior){ return 0; } -static int sqlite3MemRoundup(int n){ return n; } -static int sqlite3MemInit(void *NotUsed){ return SQLITE_OK; } -static void sqlite3MemShutdown(void *NotUsed){ return; } - -/* -** This routine is the only routine in this file with external linkage. -** -** Populate the low-level memory allocation function pointers in -** sqlite3GlobalConfig.m with pointers to the routines in this file. -*/ -SQLITE_PRIVATE void sqlite3MemSetDefault(void){ - static const sqlite3_mem_methods defaultMethods = { - sqlite3MemMalloc, - sqlite3MemFree, - sqlite3MemRealloc, - sqlite3MemSize, - sqlite3MemRoundup, - sqlite3MemInit, - sqlite3MemShutdown, - 0 - }; - sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); -} - -#endif /* SQLITE_ZERO_MALLOC */ - -/************** End of mem0.c ************************************************/ -/************** Begin file mem1.c ********************************************/ -/* -** 2007 August 14 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains low-level memory allocation drivers for when -** SQLite will use the standard C-library malloc/realloc/free interface -** to obtain the memory it needs. -** -** This file contains implementations of the low-level memory allocation -** routines specified in the sqlite3_mem_methods object. The content of -** this file is only used if SQLITE_SYSTEM_MALLOC is defined. The -** SQLITE_SYSTEM_MALLOC macro is defined automatically if neither the -** SQLITE_MEMDEBUG nor the SQLITE_WIN32_MALLOC macros are defined. The -** default configuration is to use memory allocation routines in this -** file. -** -** C-preprocessor macro summary: -** -** HAVE_MALLOC_USABLE_SIZE The configure script sets this symbol if -** the malloc_usable_size() interface exists -** on the target platform. Or, this symbol -** can be set manually, if desired. -** If an equivalent interface exists by -** a different name, using a separate -D -** option to rename it. -** -** SQLITE_WITHOUT_ZONEMALLOC Some older macs lack support for the zone -** memory allocator. Set this symbol to enable -** building on older macs. -** -** SQLITE_WITHOUT_MSIZE Set this symbol to disable the use of -** _msize() on windows systems. This might -** be necessary when compiling for Delphi, -** for example. -*/ - -/* -** This version of the memory allocator is the default. It is -** used when no other memory allocator is specified using compile-time -** macros. -*/ -#ifdef SQLITE_SYSTEM_MALLOC - -/* -** The MSVCRT has malloc_usable_size() but it is called _msize(). -** The use of _msize() is automatic, but can be disabled by compiling -** with -DSQLITE_WITHOUT_MSIZE -*/ -#if defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE) -# define SQLITE_MALLOCSIZE _msize -#endif - -#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) - -/* -** Use the zone allocator available on apple products unless the -** SQLITE_WITHOUT_ZONEMALLOC symbol is defined. -*/ -#include -#include -#include -static malloc_zone_t* _sqliteZone_; -#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x)) -#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x)); -#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y)) -#define SQLITE_MALLOCSIZE(x) \ - (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x)) - -#else /* if not __APPLE__ */ - -/* -** Use standard C library malloc and free on non-Apple systems. -** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined. -*/ -#define SQLITE_MALLOC(x) malloc(x) -#define SQLITE_FREE(x) free(x) -#define SQLITE_REALLOC(x,y) realloc((x),(y)) - -#if (defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)) \ - || (defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE)) -# include /* Needed for malloc_usable_size on linux */ -#endif -#ifdef HAVE_MALLOC_USABLE_SIZE -# ifndef SQLITE_MALLOCSIZE -# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x) -# endif -#else -# undef SQLITE_MALLOCSIZE -#endif - -#endif /* __APPLE__ or not __APPLE__ */ - -/* -** Like malloc(), but remember the size of the allocation -** so that we can find it later using sqlite3MemSize(). -** -** For this low-level routine, we are guaranteed that nByte>0 because -** cases of nByte<=0 will be intercepted and dealt with by higher level -** routines. -*/ -static void *sqlite3MemMalloc(int nByte){ -#ifdef SQLITE_MALLOCSIZE - void *p = SQLITE_MALLOC( nByte ); - if( p==0 ){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); - } - return p; -#else - sqlite3_int64 *p; - assert( nByte>0 ); - nByte = ROUND8(nByte); - p = SQLITE_MALLOC( nByte+8 ); - if( p ){ - p[0] = nByte; - p++; - }else{ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); - } - return (void *)p; -#endif -} - -/* -** Like free() but works for allocations obtained from sqlite3MemMalloc() -** or sqlite3MemRealloc(). -** -** For this low-level routine, we already know that pPrior!=0 since -** cases where pPrior==0 will have been intecepted and dealt with -** by higher-level routines. -*/ -static void sqlite3MemFree(void *pPrior){ -#ifdef SQLITE_MALLOCSIZE - SQLITE_FREE(pPrior); -#else - sqlite3_int64 *p = (sqlite3_int64*)pPrior; - assert( pPrior!=0 ); - p--; - SQLITE_FREE(p); -#endif -} - -/* -** Report the allocated size of a prior return from xMalloc() -** or xRealloc(). -*/ -static int sqlite3MemSize(void *pPrior){ -#ifdef SQLITE_MALLOCSIZE - return pPrior ? (int)SQLITE_MALLOCSIZE(pPrior) : 0; -#else - sqlite3_int64 *p; - if( pPrior==0 ) return 0; - p = (sqlite3_int64*)pPrior; - p--; - return (int)p[0]; -#endif -} - -/* -** Like realloc(). Resize an allocation previously obtained from -** sqlite3MemMalloc(). -** -** For this low-level interface, we know that pPrior!=0. Cases where -** pPrior==0 while have been intercepted by higher-level routine and -** redirected to xMalloc. Similarly, we know that nByte>0 becauses -** cases where nByte<=0 will have been intercepted by higher-level -** routines and redirected to xFree. -*/ -static void *sqlite3MemRealloc(void *pPrior, int nByte){ -#ifdef SQLITE_MALLOCSIZE - void *p = SQLITE_REALLOC(pPrior, nByte); - if( p==0 ){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_NOMEM, - "failed memory resize %u to %u bytes", - SQLITE_MALLOCSIZE(pPrior), nByte); - } - return p; -#else - sqlite3_int64 *p = (sqlite3_int64*)pPrior; - assert( pPrior!=0 && nByte>0 ); - assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */ - p--; - p = SQLITE_REALLOC(p, nByte+8 ); - if( p ){ - p[0] = nByte; - p++; - }else{ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_NOMEM, - "failed memory resize %u to %u bytes", - sqlite3MemSize(pPrior), nByte); - } - return (void*)p; -#endif -} - -/* -** Round up a request size to the next valid allocation size. -*/ -static int sqlite3MemRoundup(int n){ - return ROUND8(n); -} - -/* -** Initialize this module. -*/ -static int sqlite3MemInit(void *NotUsed){ -#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) - int cpuCount; - size_t len; - if( _sqliteZone_ ){ - return SQLITE_OK; - } - len = sizeof(cpuCount); - /* One usually wants to use hw.acctivecpu for MT decisions, but not here */ - sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); - if( cpuCount>1 ){ - /* defer MT decisions to system malloc */ - _sqliteZone_ = malloc_default_zone(); - }else{ - /* only 1 core, use our own zone to contention over global locks, - ** e.g. we have our own dedicated locks */ - bool success; - malloc_zone_t* newzone = malloc_create_zone(4096, 0); - malloc_set_zone_name(newzone, "Sqlite_Heap"); - do{ - success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone, - (void * volatile *)&_sqliteZone_); - }while(!_sqliteZone_); - if( !success ){ - /* somebody registered a zone first */ - malloc_destroy_zone(newzone); - } - } -#endif - UNUSED_PARAMETER(NotUsed); - return SQLITE_OK; -} - -/* -** Deinitialize this module. -*/ -static void sqlite3MemShutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - return; -} - -/* -** This routine is the only routine in this file with external linkage. -** -** Populate the low-level memory allocation function pointers in -** sqlite3GlobalConfig.m with pointers to the routines in this file. -*/ -SQLITE_PRIVATE void sqlite3MemSetDefault(void){ - static const sqlite3_mem_methods defaultMethods = { - sqlite3MemMalloc, - sqlite3MemFree, - sqlite3MemRealloc, - sqlite3MemSize, - sqlite3MemRoundup, - sqlite3MemInit, - sqlite3MemShutdown, - 0 - }; - sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); -} - -#endif /* SQLITE_SYSTEM_MALLOC */ - -/************** End of mem1.c ************************************************/ -/************** Begin file mem2.c ********************************************/ -/* -** 2007 August 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains low-level memory allocation drivers for when -** SQLite will use the standard C-library malloc/realloc/free interface -** to obtain the memory it needs while adding lots of additional debugging -** information to each allocation in order to help detect and fix memory -** leaks and memory usage errors. -** -** This file contains implementations of the low-level memory allocation -** routines specified in the sqlite3_mem_methods object. -*/ - -/* -** This version of the memory allocator is used only if the -** SQLITE_MEMDEBUG macro is defined -*/ -#ifdef SQLITE_MEMDEBUG - -/* -** The backtrace functionality is only available with GLIBC -*/ -#ifdef __GLIBC__ - extern int backtrace(void**,int); - extern void backtrace_symbols_fd(void*const*,int,int); -#else -# define backtrace(A,B) 1 -# define backtrace_symbols_fd(A,B,C) -#endif -/* #include */ - -/* -** Each memory allocation looks like this: -** -** ------------------------------------------------------------------------ -** | Title | backtrace pointers | MemBlockHdr | allocation | EndGuard | -** ------------------------------------------------------------------------ -** -** The application code sees only a pointer to the allocation. We have -** to back up from the allocation pointer to find the MemBlockHdr. The -** MemBlockHdr tells us the size of the allocation and the number of -** backtrace pointers. There is also a guard word at the end of the -** MemBlockHdr. -*/ -struct MemBlockHdr { - i64 iSize; /* Size of this allocation */ - struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */ - char nBacktrace; /* Number of backtraces on this alloc */ - char nBacktraceSlots; /* Available backtrace slots */ - u8 nTitle; /* Bytes of title; includes '\0' */ - u8 eType; /* Allocation type code */ - int iForeGuard; /* Guard word for sanity */ -}; - -/* -** Guard words -*/ -#define FOREGUARD 0x80F5E153 -#define REARGUARD 0xE4676B53 - -/* -** Number of malloc size increments to track. -*/ -#define NCSIZE 1000 - -/* -** All of the static variables used by this module are collected -** into a single structure named "mem". This is to keep the -** static variables organized and to reduce namespace pollution -** when this module is combined with other in the amalgamation. -*/ -static struct { - - /* - ** Mutex to control access to the memory allocation subsystem. - */ - sqlite3_mutex *mutex; - - /* - ** Head and tail of a linked list of all outstanding allocations - */ - struct MemBlockHdr *pFirst; - struct MemBlockHdr *pLast; - - /* - ** The number of levels of backtrace to save in new allocations. - */ - int nBacktrace; - void (*xBacktrace)(int, int, void **); - - /* - ** Title text to insert in front of each block - */ - int nTitle; /* Bytes of zTitle to save. Includes '\0' and padding */ - char zTitle[100]; /* The title text */ - - /* - ** sqlite3MallocDisallow() increments the following counter. - ** sqlite3MallocAllow() decrements it. - */ - int disallow; /* Do not allow memory allocation */ - - /* - ** Gather statistics on the sizes of memory allocations. - ** nAlloc[i] is the number of allocation attempts of i*8 - ** bytes. i==NCSIZE is the number of allocation attempts for - ** sizes more than NCSIZE*8 bytes. - */ - int nAlloc[NCSIZE]; /* Total number of allocations */ - int nCurrent[NCSIZE]; /* Current number of allocations */ - int mxCurrent[NCSIZE]; /* Highwater mark for nCurrent */ - -} mem; - - -/* -** Adjust memory usage statistics -*/ -static void adjustStats(int iSize, int increment){ - int i = ROUND8(iSize)/8; - if( i>NCSIZE-1 ){ - i = NCSIZE - 1; - } - if( increment>0 ){ - mem.nAlloc[i]++; - mem.nCurrent[i]++; - if( mem.nCurrent[i]>mem.mxCurrent[i] ){ - mem.mxCurrent[i] = mem.nCurrent[i]; - } - }else{ - mem.nCurrent[i]--; - assert( mem.nCurrent[i]>=0 ); - } -} - -/* -** Given an allocation, find the MemBlockHdr for that allocation. -** -** This routine checks the guards at either end of the allocation and -** if they are incorrect it asserts. -*/ -static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){ - struct MemBlockHdr *p; - int *pInt; - u8 *pU8; - int nReserve; - - p = (struct MemBlockHdr*)pAllocation; - p--; - assert( p->iForeGuard==(int)FOREGUARD ); - nReserve = ROUND8(p->iSize); - pInt = (int*)pAllocation; - pU8 = (u8*)pAllocation; - assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD ); - /* This checks any of the "extra" bytes allocated due - ** to rounding up to an 8 byte boundary to ensure - ** they haven't been overwritten. - */ - while( nReserve-- > p->iSize ) assert( pU8[nReserve]==0x65 ); - return p; -} - -/* -** Return the number of bytes currently allocated at address p. -*/ -static int sqlite3MemSize(void *p){ - struct MemBlockHdr *pHdr; - if( !p ){ - return 0; - } - pHdr = sqlite3MemsysGetHeader(p); - return pHdr->iSize; -} - -/* -** Initialize the memory allocation subsystem. -*/ -static int sqlite3MemInit(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - assert( (sizeof(struct MemBlockHdr)&7) == 0 ); - if( !sqlite3GlobalConfig.bMemstat ){ - /* If memory status is enabled, then the malloc.c wrapper will already - ** hold the STATIC_MEM mutex when the routines here are invoked. */ - mem.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); - } - return SQLITE_OK; -} - -/* -** Deinitialize the memory allocation subsystem. -*/ -static void sqlite3MemShutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - mem.mutex = 0; -} - -/* -** Round up a request size to the next valid allocation size. -*/ -static int sqlite3MemRoundup(int n){ - return ROUND8(n); -} - -/* -** Fill a buffer with pseudo-random bytes. This is used to preset -** the content of a new memory allocation to unpredictable values and -** to clear the content of a freed allocation to unpredictable values. -*/ -static void randomFill(char *pBuf, int nByte){ - unsigned int x, y, r; - x = SQLITE_PTR_TO_INT(pBuf); - y = nByte | 1; - while( nByte >= 4 ){ - x = (x>>1) ^ (-(x&1) & 0xd0000001); - y = y*1103515245 + 12345; - r = x ^ y; - *(int*)pBuf = r; - pBuf += 4; - nByte -= 4; - } - while( nByte-- > 0 ){ - x = (x>>1) ^ (-(x&1) & 0xd0000001); - y = y*1103515245 + 12345; - r = x ^ y; - *(pBuf++) = r & 0xff; - } -} - -/* -** Allocate nByte bytes of memory. -*/ -static void *sqlite3MemMalloc(int nByte){ - struct MemBlockHdr *pHdr; - void **pBt; - char *z; - int *pInt; - void *p = 0; - int totalSize; - int nReserve; - sqlite3_mutex_enter(mem.mutex); - assert( mem.disallow==0 ); - nReserve = ROUND8(nByte); - totalSize = nReserve + sizeof(*pHdr) + sizeof(int) + - mem.nBacktrace*sizeof(void*) + mem.nTitle; - p = malloc(totalSize); - if( p ){ - z = p; - pBt = (void**)&z[mem.nTitle]; - pHdr = (struct MemBlockHdr*)&pBt[mem.nBacktrace]; - pHdr->pNext = 0; - pHdr->pPrev = mem.pLast; - if( mem.pLast ){ - mem.pLast->pNext = pHdr; - }else{ - mem.pFirst = pHdr; - } - mem.pLast = pHdr; - pHdr->iForeGuard = FOREGUARD; - pHdr->eType = MEMTYPE_HEAP; - pHdr->nBacktraceSlots = mem.nBacktrace; - pHdr->nTitle = mem.nTitle; - if( mem.nBacktrace ){ - void *aAddr[40]; - pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; - memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*)); - assert(pBt[0]); - if( mem.xBacktrace ){ - mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]); - } - }else{ - pHdr->nBacktrace = 0; - } - if( mem.nTitle ){ - memcpy(z, mem.zTitle, mem.nTitle); - } - pHdr->iSize = nByte; - adjustStats(nByte, +1); - pInt = (int*)&pHdr[1]; - pInt[nReserve/sizeof(int)] = REARGUARD; - randomFill((char*)pInt, nByte); - memset(((char*)pInt)+nByte, 0x65, nReserve-nByte); - p = (void*)pInt; - } - sqlite3_mutex_leave(mem.mutex); - return p; -} - -/* -** Free memory. -*/ -static void sqlite3MemFree(void *pPrior){ - struct MemBlockHdr *pHdr; - void **pBt; - char *z; - assert( sqlite3GlobalConfig.bMemstat || sqlite3GlobalConfig.bCoreMutex==0 - || mem.mutex!=0 ); - pHdr = sqlite3MemsysGetHeader(pPrior); - pBt = (void**)pHdr; - pBt -= pHdr->nBacktraceSlots; - sqlite3_mutex_enter(mem.mutex); - if( pHdr->pPrev ){ - assert( pHdr->pPrev->pNext==pHdr ); - pHdr->pPrev->pNext = pHdr->pNext; - }else{ - assert( mem.pFirst==pHdr ); - mem.pFirst = pHdr->pNext; - } - if( pHdr->pNext ){ - assert( pHdr->pNext->pPrev==pHdr ); - pHdr->pNext->pPrev = pHdr->pPrev; - }else{ - assert( mem.pLast==pHdr ); - mem.pLast = pHdr->pPrev; - } - z = (char*)pBt; - z -= pHdr->nTitle; - adjustStats(pHdr->iSize, -1); - randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) + - pHdr->iSize + sizeof(int) + pHdr->nTitle); - free(z); - sqlite3_mutex_leave(mem.mutex); -} - -/* -** Change the size of an existing memory allocation. -** -** For this debugging implementation, we *always* make a copy of the -** allocation into a new place in memory. In this way, if the -** higher level code is using pointer to the old allocation, it is -** much more likely to break and we are much more liking to find -** the error. -*/ -static void *sqlite3MemRealloc(void *pPrior, int nByte){ - struct MemBlockHdr *pOldHdr; - void *pNew; - assert( mem.disallow==0 ); - assert( (nByte & 7)==0 ); /* EV: R-46199-30249 */ - pOldHdr = sqlite3MemsysGetHeader(pPrior); - pNew = sqlite3MemMalloc(nByte); - if( pNew ){ - memcpy(pNew, pPrior, nByteiSize ? nByte : pOldHdr->iSize); - if( nByte>pOldHdr->iSize ){ - randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize); - } - sqlite3MemFree(pPrior); - } - return pNew; -} - -/* -** Populate the low-level memory allocation function pointers in -** sqlite3GlobalConfig.m with pointers to the routines in this file. -*/ -SQLITE_PRIVATE void sqlite3MemSetDefault(void){ - static const sqlite3_mem_methods defaultMethods = { - sqlite3MemMalloc, - sqlite3MemFree, - sqlite3MemRealloc, - sqlite3MemSize, - sqlite3MemRoundup, - sqlite3MemInit, - sqlite3MemShutdown, - 0 - }; - sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); -} - -/* -** Set the "type" of an allocation. -*/ -SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){ - if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ - struct MemBlockHdr *pHdr; - pHdr = sqlite3MemsysGetHeader(p); - assert( pHdr->iForeGuard==FOREGUARD ); - pHdr->eType = eType; - } -} - -/* -** Return TRUE if the mask of type in eType matches the type of the -** allocation p. Also return true if p==NULL. -** -** This routine is designed for use within an assert() statement, to -** verify the type of an allocation. For example: -** -** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); -*/ -SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){ - int rc = 1; - if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ - struct MemBlockHdr *pHdr; - pHdr = sqlite3MemsysGetHeader(p); - assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ - if( (pHdr->eType&eType)==0 ){ - rc = 0; - } - } - return rc; -} - -/* -** Return TRUE if the mask of type in eType matches no bits of the type of the -** allocation p. Also return true if p==NULL. -** -** This routine is designed for use within an assert() statement, to -** verify the type of an allocation. For example: -** -** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); -*/ -SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){ - int rc = 1; - if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ - struct MemBlockHdr *pHdr; - pHdr = sqlite3MemsysGetHeader(p); - assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ - if( (pHdr->eType&eType)!=0 ){ - rc = 0; - } - } - return rc; -} - -/* -** Set the number of backtrace levels kept for each allocation. -** A value of zero turns off backtracing. The number is always rounded -** up to a multiple of 2. -*/ -SQLITE_PRIVATE void sqlite3MemdebugBacktrace(int depth){ - if( depth<0 ){ depth = 0; } - if( depth>20 ){ depth = 20; } - depth = (depth+1)&0xfe; - mem.nBacktrace = depth; -} - -SQLITE_PRIVATE void sqlite3MemdebugBacktraceCallback(void (*xBacktrace)(int, int, void **)){ - mem.xBacktrace = xBacktrace; -} - -/* -** Set the title string for subsequent allocations. -*/ -SQLITE_PRIVATE void sqlite3MemdebugSettitle(const char *zTitle){ - unsigned int n = sqlite3Strlen30(zTitle) + 1; - sqlite3_mutex_enter(mem.mutex); - if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1; - memcpy(mem.zTitle, zTitle, n); - mem.zTitle[n] = 0; - mem.nTitle = ROUND8(n); - sqlite3_mutex_leave(mem.mutex); -} - -SQLITE_PRIVATE void sqlite3MemdebugSync(){ - struct MemBlockHdr *pHdr; - for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ - void **pBt = (void**)pHdr; - pBt -= pHdr->nBacktraceSlots; - mem.xBacktrace(pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]); - } -} - -/* -** Open the file indicated and write a log of all unfreed memory -** allocations into that log. -*/ -SQLITE_PRIVATE void sqlite3MemdebugDump(const char *zFilename){ - FILE *out; - struct MemBlockHdr *pHdr; - void **pBt; - int i; - out = fopen(zFilename, "w"); - if( out==0 ){ - fprintf(stderr, "** Unable to output memory debug output log: %s **\n", - zFilename); - return; - } - for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ - char *z = (char*)pHdr; - z -= pHdr->nBacktraceSlots*sizeof(void*) + pHdr->nTitle; - fprintf(out, "**** %lld bytes at %p from %s ****\n", - pHdr->iSize, &pHdr[1], pHdr->nTitle ? z : "???"); - if( pHdr->nBacktrace ){ - fflush(out); - pBt = (void**)pHdr; - pBt -= pHdr->nBacktraceSlots; - backtrace_symbols_fd(pBt, pHdr->nBacktrace, fileno(out)); - fprintf(out, "\n"); - } - } - fprintf(out, "COUNTS:\n"); - for(i=0; i=1 ); - size = mem3.aPool[i-1].u.hdr.size4x/4; - assert( size==mem3.aPool[i+size-1].u.hdr.prevSize ); - assert( size>=2 ); - if( size <= MX_SMALL ){ - memsys3UnlinkFromList(i, &mem3.aiSmall[size-2]); - }else{ - hash = size % N_HASH; - memsys3UnlinkFromList(i, &mem3.aiHash[hash]); - } -} - -/* -** Link the chunk at mem3.aPool[i] so that is on the list rooted -** at *pRoot. -*/ -static void memsys3LinkIntoList(u32 i, u32 *pRoot){ - assert( sqlite3_mutex_held(mem3.mutex) ); - mem3.aPool[i].u.list.next = *pRoot; - mem3.aPool[i].u.list.prev = 0; - if( *pRoot ){ - mem3.aPool[*pRoot].u.list.prev = i; - } - *pRoot = i; -} - -/* -** Link the chunk at index i into either the appropriate -** small chunk list, or into the large chunk hash table. -*/ -static void memsys3Link(u32 i){ - u32 size, hash; - assert( sqlite3_mutex_held(mem3.mutex) ); - assert( i>=1 ); - assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 ); - size = mem3.aPool[i-1].u.hdr.size4x/4; - assert( size==mem3.aPool[i+size-1].u.hdr.prevSize ); - assert( size>=2 ); - if( size <= MX_SMALL ){ - memsys3LinkIntoList(i, &mem3.aiSmall[size-2]); - }else{ - hash = size % N_HASH; - memsys3LinkIntoList(i, &mem3.aiHash[hash]); - } -} - -/* -** If the STATIC_MEM mutex is not already held, obtain it now. The mutex -** will already be held (obtained by code in malloc.c) if -** sqlite3GlobalConfig.bMemStat is true. -*/ -static void memsys3Enter(void){ - if( sqlite3GlobalConfig.bMemstat==0 && mem3.mutex==0 ){ - mem3.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); - } - sqlite3_mutex_enter(mem3.mutex); -} -static void memsys3Leave(void){ - sqlite3_mutex_leave(mem3.mutex); -} - -/* -** Called when we are unable to satisfy an allocation of nBytes. -*/ -static void memsys3OutOfMemory(int nByte){ - if( !mem3.alarmBusy ){ - mem3.alarmBusy = 1; - assert( sqlite3_mutex_held(mem3.mutex) ); - sqlite3_mutex_leave(mem3.mutex); - sqlite3_release_memory(nByte); - sqlite3_mutex_enter(mem3.mutex); - mem3.alarmBusy = 0; - } -} - - -/* -** Chunk i is a free chunk that has been unlinked. Adjust its -** size parameters for check-out and return a pointer to the -** user portion of the chunk. -*/ -static void *memsys3Checkout(u32 i, u32 nBlock){ - u32 x; - assert( sqlite3_mutex_held(mem3.mutex) ); - assert( i>=1 ); - assert( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ); - assert( mem3.aPool[i+nBlock-1].u.hdr.prevSize==nBlock ); - x = mem3.aPool[i-1].u.hdr.size4x; - mem3.aPool[i-1].u.hdr.size4x = nBlock*4 | 1 | (x&2); - mem3.aPool[i+nBlock-1].u.hdr.prevSize = nBlock; - mem3.aPool[i+nBlock-1].u.hdr.size4x |= 2; - return &mem3.aPool[i]; -} - -/* -** Carve a piece off of the end of the mem3.iMaster free chunk. -** Return a pointer to the new allocation. Or, if the master chunk -** is not large enough, return 0. -*/ -static void *memsys3FromMaster(u32 nBlock){ - assert( sqlite3_mutex_held(mem3.mutex) ); - assert( mem3.szMaster>=nBlock ); - if( nBlock>=mem3.szMaster-1 ){ - /* Use the entire master */ - void *p = memsys3Checkout(mem3.iMaster, mem3.szMaster); - mem3.iMaster = 0; - mem3.szMaster = 0; - mem3.mnMaster = 0; - return p; - }else{ - /* Split the master block. Return the tail. */ - u32 newi, x; - newi = mem3.iMaster + mem3.szMaster - nBlock; - assert( newi > mem3.iMaster+1 ); - mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = nBlock; - mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x |= 2; - mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1; - mem3.szMaster -= nBlock; - mem3.aPool[newi-1].u.hdr.prevSize = mem3.szMaster; - x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2; - mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x; - if( mem3.szMaster < mem3.mnMaster ){ - mem3.mnMaster = mem3.szMaster; - } - return (void*)&mem3.aPool[newi]; - } -} - -/* -** *pRoot is the head of a list of free chunks of the same size -** or same size hash. In other words, *pRoot is an entry in either -** mem3.aiSmall[] or mem3.aiHash[]. -** -** This routine examines all entries on the given list and tries -** to coalesce each entries with adjacent free chunks. -** -** If it sees a chunk that is larger than mem3.iMaster, it replaces -** the current mem3.iMaster with the new larger chunk. In order for -** this mem3.iMaster replacement to work, the master chunk must be -** linked into the hash tables. That is not the normal state of -** affairs, of course. The calling routine must link the master -** chunk before invoking this routine, then must unlink the (possibly -** changed) master chunk once this routine has finished. -*/ -static void memsys3Merge(u32 *pRoot){ - u32 iNext, prev, size, i, x; - - assert( sqlite3_mutex_held(mem3.mutex) ); - for(i=*pRoot; i>0; i=iNext){ - iNext = mem3.aPool[i].u.list.next; - size = mem3.aPool[i-1].u.hdr.size4x; - assert( (size&1)==0 ); - if( (size&2)==0 ){ - memsys3UnlinkFromList(i, pRoot); - assert( i > mem3.aPool[i-1].u.hdr.prevSize ); - prev = i - mem3.aPool[i-1].u.hdr.prevSize; - if( prev==iNext ){ - iNext = mem3.aPool[prev].u.list.next; - } - memsys3Unlink(prev); - size = i + size/4 - prev; - x = mem3.aPool[prev-1].u.hdr.size4x & 2; - mem3.aPool[prev-1].u.hdr.size4x = size*4 | x; - mem3.aPool[prev+size-1].u.hdr.prevSize = size; - memsys3Link(prev); - i = prev; - }else{ - size /= 4; - } - if( size>mem3.szMaster ){ - mem3.iMaster = i; - mem3.szMaster = size; - } - } -} - -/* -** Return a block of memory of at least nBytes in size. -** Return NULL if unable. -** -** This function assumes that the necessary mutexes, if any, are -** already held by the caller. Hence "Unsafe". -*/ -static void *memsys3MallocUnsafe(int nByte){ - u32 i; - u32 nBlock; - u32 toFree; - - assert( sqlite3_mutex_held(mem3.mutex) ); - assert( sizeof(Mem3Block)==8 ); - if( nByte<=12 ){ - nBlock = 2; - }else{ - nBlock = (nByte + 11)/8; - } - assert( nBlock>=2 ); - - /* STEP 1: - ** Look for an entry of the correct size in either the small - ** chunk table or in the large chunk hash table. This is - ** successful most of the time (about 9 times out of 10). - */ - if( nBlock <= MX_SMALL ){ - i = mem3.aiSmall[nBlock-2]; - if( i>0 ){ - memsys3UnlinkFromList(i, &mem3.aiSmall[nBlock-2]); - return memsys3Checkout(i, nBlock); - } - }else{ - int hash = nBlock % N_HASH; - for(i=mem3.aiHash[hash]; i>0; i=mem3.aPool[i].u.list.next){ - if( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ){ - memsys3UnlinkFromList(i, &mem3.aiHash[hash]); - return memsys3Checkout(i, nBlock); - } - } - } - - /* STEP 2: - ** Try to satisfy the allocation by carving a piece off of the end - ** of the master chunk. This step usually works if step 1 fails. - */ - if( mem3.szMaster>=nBlock ){ - return memsys3FromMaster(nBlock); - } - - - /* STEP 3: - ** Loop through the entire memory pool. Coalesce adjacent free - ** chunks. Recompute the master chunk as the largest free chunk. - ** Then try again to satisfy the allocation by carving a piece off - ** of the end of the master chunk. This step happens very - ** rarely (we hope!) - */ - for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){ - memsys3OutOfMemory(toFree); - if( mem3.iMaster ){ - memsys3Link(mem3.iMaster); - mem3.iMaster = 0; - mem3.szMaster = 0; - } - for(i=0; i=nBlock ){ - return memsys3FromMaster(nBlock); - } - } - } - - /* If none of the above worked, then we fail. */ - return 0; -} - -/* -** Free an outstanding memory allocation. -** -** This function assumes that the necessary mutexes, if any, are -** already held by the caller. Hence "Unsafe". -*/ -static void memsys3FreeUnsafe(void *pOld){ - Mem3Block *p = (Mem3Block*)pOld; - int i; - u32 size, x; - assert( sqlite3_mutex_held(mem3.mutex) ); - assert( p>mem3.aPool && p<&mem3.aPool[mem3.nPool] ); - i = p - mem3.aPool; - assert( (mem3.aPool[i-1].u.hdr.size4x&1)==1 ); - size = mem3.aPool[i-1].u.hdr.size4x/4; - assert( i+size<=mem3.nPool+1 ); - mem3.aPool[i-1].u.hdr.size4x &= ~1; - mem3.aPool[i+size-1].u.hdr.prevSize = size; - mem3.aPool[i+size-1].u.hdr.size4x &= ~2; - memsys3Link(i); - - /* Try to expand the master using the newly freed chunk */ - if( mem3.iMaster ){ - while( (mem3.aPool[mem3.iMaster-1].u.hdr.size4x&2)==0 ){ - size = mem3.aPool[mem3.iMaster-1].u.hdr.prevSize; - mem3.iMaster -= size; - mem3.szMaster += size; - memsys3Unlink(mem3.iMaster); - x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2; - mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x; - mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster; - } - x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2; - while( (mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x&1)==0 ){ - memsys3Unlink(mem3.iMaster+mem3.szMaster); - mem3.szMaster += mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x/4; - mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x; - mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster; - } - } -} - -/* -** Return the size of an outstanding allocation, in bytes. The -** size returned omits the 8-byte header overhead. This only -** works for chunks that are currently checked out. -*/ -static int memsys3Size(void *p){ - Mem3Block *pBlock; - if( p==0 ) return 0; - pBlock = (Mem3Block*)p; - assert( (pBlock[-1].u.hdr.size4x&1)!=0 ); - return (pBlock[-1].u.hdr.size4x&~3)*2 - 4; -} - -/* -** Round up a request size to the next valid allocation size. -*/ -static int memsys3Roundup(int n){ - if( n<=12 ){ - return 12; - }else{ - return ((n+11)&~7) - 4; - } -} - -/* -** Allocate nBytes of memory. -*/ -static void *memsys3Malloc(int nBytes){ - sqlite3_int64 *p; - assert( nBytes>0 ); /* malloc.c filters out 0 byte requests */ - memsys3Enter(); - p = memsys3MallocUnsafe(nBytes); - memsys3Leave(); - return (void*)p; -} - -/* -** Free memory. -*/ -static void memsys3Free(void *pPrior){ - assert( pPrior ); - memsys3Enter(); - memsys3FreeUnsafe(pPrior); - memsys3Leave(); -} - -/* -** Change the size of an existing memory allocation -*/ -static void *memsys3Realloc(void *pPrior, int nBytes){ - int nOld; - void *p; - if( pPrior==0 ){ - return sqlite3_malloc(nBytes); - } - if( nBytes<=0 ){ - sqlite3_free(pPrior); - return 0; - } - nOld = memsys3Size(pPrior); - if( nBytes<=nOld && nBytes>=nOld-128 ){ - return pPrior; - } - memsys3Enter(); - p = memsys3MallocUnsafe(nBytes); - if( p ){ - if( nOld>1)!=(size&1) ){ - fprintf(out, "%p tail checkout bit is incorrect\n", &mem3.aPool[i]); - assert( 0 ); - break; - } - if( size&1 ){ - fprintf(out, "%p %6d bytes checked out\n", &mem3.aPool[i], (size/4)*8-8); - }else{ - fprintf(out, "%p %6d bytes free%s\n", &mem3.aPool[i], (size/4)*8-8, - i==mem3.iMaster ? " **master**" : ""); - } - } - for(i=0; i0; j=mem3.aPool[j].u.list.next){ - fprintf(out, " %p(%d)", &mem3.aPool[j], - (mem3.aPool[j-1].u.hdr.size4x/4)*8-8); - } - fprintf(out, "\n"); - } - for(i=0; i0; j=mem3.aPool[j].u.list.next){ - fprintf(out, " %p(%d)", &mem3.aPool[j], - (mem3.aPool[j-1].u.hdr.size4x/4)*8-8); - } - fprintf(out, "\n"); - } - fprintf(out, "master=%d\n", mem3.iMaster); - fprintf(out, "nowUsed=%d\n", mem3.nPool*8 - mem3.szMaster*8); - fprintf(out, "mxUsed=%d\n", mem3.nPool*8 - mem3.mnMaster*8); - sqlite3_mutex_leave(mem3.mutex); - if( out==stdout ){ - fflush(stdout); - }else{ - fclose(out); - } -#else - UNUSED_PARAMETER(zFilename); -#endif -} - -/* -** This routine is the only routine in this file with external -** linkage. -** -** Populate the low-level memory allocation function pointers in -** sqlite3GlobalConfig.m with pointers to the routines in this file. The -** arguments specify the block of memory to manage. -** -** This routine is only called by sqlite3_config(), and therefore -** is not required to be threadsafe (it is not). -*/ -SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){ - static const sqlite3_mem_methods mempoolMethods = { - memsys3Malloc, - memsys3Free, - memsys3Realloc, - memsys3Size, - memsys3Roundup, - memsys3Init, - memsys3Shutdown, - 0 - }; - return &mempoolMethods; -} - -#endif /* SQLITE_ENABLE_MEMSYS3 */ - -/************** End of mem3.c ************************************************/ -/************** Begin file mem5.c ********************************************/ -/* -** 2007 October 14 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the C functions that implement a memory -** allocation subsystem for use by SQLite. -** -** This version of the memory allocation subsystem omits all -** use of malloc(). The application gives SQLite a block of memory -** before calling sqlite3_initialize() from which allocations -** are made and returned by the xMalloc() and xRealloc() -** implementations. Once sqlite3_initialize() has been called, -** the amount of memory available to SQLite is fixed and cannot -** be changed. -** -** This version of the memory allocation subsystem is included -** in the build only if SQLITE_ENABLE_MEMSYS5 is defined. -** -** This memory allocator uses the following algorithm: -** -** 1. All memory allocations sizes are rounded up to a power of 2. -** -** 2. If two adjacent free blocks are the halves of a larger block, -** then the two blocks are coalesed into the single larger block. -** -** 3. New memory is allocated from the first available free block. -** -** This algorithm is described in: J. M. Robson. "Bounds for Some Functions -** Concerning Dynamic Storage Allocation". Journal of the Association for -** Computing Machinery, Volume 21, Number 8, July 1974, pages 491-499. -** -** Let n be the size of the largest allocation divided by the minimum -** allocation size (after rounding all sizes up to a power of 2.) Let M -** be the maximum amount of memory ever outstanding at one time. Let -** N be the total amount of memory available for allocation. Robson -** proved that this memory allocator will never breakdown due to -** fragmentation as long as the following constraint holds: -** -** N >= M*(1 + log2(n)/2) - n + 1 -** -** The sqlite3_status() logic tracks the maximum values of n and M so -** that an application can, at any time, verify this constraint. -*/ - -/* -** This version of the memory allocator is used only when -** SQLITE_ENABLE_MEMSYS5 is defined. -*/ -#ifdef SQLITE_ENABLE_MEMSYS5 - -/* -** A minimum allocation is an instance of the following structure. -** Larger allocations are an array of these structures where the -** size of the array is a power of 2. -** -** The size of this object must be a power of two. That fact is -** verified in memsys5Init(). -*/ -typedef struct Mem5Link Mem5Link; -struct Mem5Link { - int next; /* Index of next free chunk */ - int prev; /* Index of previous free chunk */ -}; - -/* -** Maximum size of any allocation is ((1<=0 && i=0 && iLogsize<=LOGMAX ); - assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize ); - - next = MEM5LINK(i)->next; - prev = MEM5LINK(i)->prev; - if( prev<0 ){ - mem5.aiFreelist[iLogsize] = next; - }else{ - MEM5LINK(prev)->next = next; - } - if( next>=0 ){ - MEM5LINK(next)->prev = prev; - } -} - -/* -** Link the chunk at mem5.aPool[i] so that is on the iLogsize -** free list. -*/ -static void memsys5Link(int i, int iLogsize){ - int x; - assert( sqlite3_mutex_held(mem5.mutex) ); - assert( i>=0 && i=0 && iLogsize<=LOGMAX ); - assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize ); - - x = MEM5LINK(i)->next = mem5.aiFreelist[iLogsize]; - MEM5LINK(i)->prev = -1; - if( x>=0 ){ - assert( xprev = i; - } - mem5.aiFreelist[iLogsize] = i; -} - -/* -** If the STATIC_MEM mutex is not already held, obtain it now. The mutex -** will already be held (obtained by code in malloc.c) if -** sqlite3GlobalConfig.bMemStat is true. -*/ -static void memsys5Enter(void){ - sqlite3_mutex_enter(mem5.mutex); -} -static void memsys5Leave(void){ - sqlite3_mutex_leave(mem5.mutex); -} - -/* -** Return the size of an outstanding allocation, in bytes. The -** size returned omits the 8-byte header overhead. This only -** works for chunks that are currently checked out. -*/ -static int memsys5Size(void *p){ - int iSize = 0; - if( p ){ - int i = ((u8 *)p-mem5.zPool)/mem5.szAtom; - assert( i>=0 && i=0 && iLogsize<=LOGMAX ); - i = iFirst = mem5.aiFreelist[iLogsize]; - assert( iFirst>=0 ); - while( i>0 ){ - if( inext; - } - memsys5Unlink(iFirst, iLogsize); - return iFirst; -} - -/* -** Return a block of memory of at least nBytes in size. -** Return NULL if unable. Return NULL if nBytes==0. -** -** The caller guarantees that nByte positive. -** -** The caller has obtained a mutex prior to invoking this -** routine so there is never any chance that two or more -** threads can be in this routine at the same time. -*/ -static void *memsys5MallocUnsafe(int nByte){ - int i; /* Index of a mem5.aPool[] slot */ - int iBin; /* Index into mem5.aiFreelist[] */ - int iFullSz; /* Size of allocation rounded up to power of 2 */ - int iLogsize; /* Log2 of iFullSz/POW2_MIN */ - - /* nByte must be a positive */ - assert( nByte>0 ); - - /* Keep track of the maximum allocation request. Even unfulfilled - ** requests are counted */ - if( (u32)nByte>mem5.maxRequest ){ - mem5.maxRequest = nByte; - } - - /* Abort if the requested allocation size is larger than the largest - ** power of two that we can represent using 32-bit signed integers. - */ - if( nByte > 0x40000000 ){ - return 0; - } - - /* Round nByte up to the next valid power of two */ - for(iFullSz=mem5.szAtom, iLogsize=0; iFullSzLOGMAX ){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte); - return 0; - } - i = memsys5UnlinkFirst(iBin); - while( iBin>iLogsize ){ - int newSize; - - iBin--; - newSize = 1 << iBin; - mem5.aCtrl[i+newSize] = CTRL_FREE | iBin; - memsys5Link(i+newSize, iBin); - } - mem5.aCtrl[i] = iLogsize; - - /* Update allocator performance statistics. */ - mem5.nAlloc++; - mem5.totalAlloc += iFullSz; - mem5.totalExcess += iFullSz - nByte; - mem5.currentCount++; - mem5.currentOut += iFullSz; - if( mem5.maxCount=0 && iBlock0 ); - assert( mem5.currentOut>=(size*mem5.szAtom) ); - mem5.currentCount--; - mem5.currentOut -= size*mem5.szAtom; - assert( mem5.currentOut>0 || mem5.currentCount==0 ); - assert( mem5.currentCount>0 || mem5.currentOut==0 ); - - mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; - while( ALWAYS(iLogsize>iLogsize) & 1 ){ - iBuddy = iBlock - size; - }else{ - iBuddy = iBlock + size; - } - assert( iBuddy>=0 ); - if( (iBuddy+(1<mem5.nBlock ) break; - if( mem5.aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break; - memsys5Unlink(iBuddy, iLogsize); - iLogsize++; - if( iBuddy0 ){ - memsys5Enter(); - p = memsys5MallocUnsafe(nBytes); - memsys5Leave(); - } - return (void*)p; -} - -/* -** Free memory. -** -** The outer layer memory allocator prevents this routine from -** being called with pPrior==0. -*/ -static void memsys5Free(void *pPrior){ - assert( pPrior!=0 ); - memsys5Enter(); - memsys5FreeUnsafe(pPrior); - memsys5Leave(); -} - -/* -** Change the size of an existing memory allocation. -** -** The outer layer memory allocator prevents this routine from -** being called with pPrior==0. -** -** nBytes is always a value obtained from a prior call to -** memsys5Round(). Hence nBytes is always a non-negative power -** of two. If nBytes==0 that means that an oversize allocation -** (an allocation larger than 0x40000000) was requested and this -** routine should return 0 without freeing pPrior. -*/ -static void *memsys5Realloc(void *pPrior, int nBytes){ - int nOld; - void *p; - assert( pPrior!=0 ); - assert( (nBytes&(nBytes-1))==0 ); /* EV: R-46199-30249 */ - assert( nBytes>=0 ); - if( nBytes==0 ){ - return 0; - } - nOld = memsys5Size(pPrior); - if( nBytes<=nOld ){ - return pPrior; - } - memsys5Enter(); - p = memsys5MallocUnsafe(nBytes); - if( p ){ - memcpy(p, pPrior, nOld); - memsys5FreeUnsafe(pPrior); - } - memsys5Leave(); - return p; -} - -/* -** Round up a request size to the next valid allocation size. If -** the allocation is too large to be handled by this allocation system, -** return 0. -** -** All allocations must be a power of two and must be expressed by a -** 32-bit signed integer. Hence the largest allocation is 0x40000000 -** or 1073741824 bytes. -*/ -static int memsys5Roundup(int n){ - int iFullSz; - if( n > 0x40000000 ) return 0; - for(iFullSz=mem5.szAtom; iFullSz 0 -** memsys5Log(2) -> 1 -** memsys5Log(4) -> 2 -** memsys5Log(5) -> 3 -** memsys5Log(8) -> 3 -** memsys5Log(9) -> 4 -*/ -static int memsys5Log(int iValue){ - int iLog; - for(iLog=0; (iLog<(int)((sizeof(int)*8)-1)) && (1<mem5.szAtom ){ - mem5.szAtom = mem5.szAtom << 1; - } - - mem5.nBlock = (nByte / (mem5.szAtom+sizeof(u8))); - mem5.zPool = zByte; - mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.szAtom]; - - for(ii=0; ii<=LOGMAX; ii++){ - mem5.aiFreelist[ii] = -1; - } - - iOffset = 0; - for(ii=LOGMAX; ii>=0; ii--){ - int nAlloc = (1<mem5.nBlock); - } - - /* If a mutex is required for normal operation, allocate one */ - if( sqlite3GlobalConfig.bMemstat==0 ){ - mem5.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); - } - - return SQLITE_OK; -} - -/* -** Deinitialize this module. -*/ -static void memsys5Shutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - mem5.mutex = 0; - return; -} - -#ifdef SQLITE_TEST -/* -** Open the file indicated and write a log of all unfreed memory -** allocations into that log. -*/ -SQLITE_PRIVATE void sqlite3Memsys5Dump(const char *zFilename){ - FILE *out; - int i, j, n; - int nMinLog; - - if( zFilename==0 || zFilename[0]==0 ){ - out = stdout; - }else{ - out = fopen(zFilename, "w"); - if( out==0 ){ - fprintf(stderr, "** Unable to output memory debug output log: %s **\n", - zFilename); - return; - } - } - memsys5Enter(); - nMinLog = memsys5Log(mem5.szAtom); - for(i=0; i<=LOGMAX && i+nMinLog<32; i++){ - for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){} - fprintf(out, "freelist items of size %d: %d\n", mem5.szAtom << i, n); - } - fprintf(out, "mem5.nAlloc = %llu\n", mem5.nAlloc); - fprintf(out, "mem5.totalAlloc = %llu\n", mem5.totalAlloc); - fprintf(out, "mem5.totalExcess = %llu\n", mem5.totalExcess); - fprintf(out, "mem5.currentOut = %u\n", mem5.currentOut); - fprintf(out, "mem5.currentCount = %u\n", mem5.currentCount); - fprintf(out, "mem5.maxOut = %u\n", mem5.maxOut); - fprintf(out, "mem5.maxCount = %u\n", mem5.maxCount); - fprintf(out, "mem5.maxRequest = %u\n", mem5.maxRequest); - memsys5Leave(); - if( out==stdout ){ - fflush(stdout); - }else{ - fclose(out); - } -} -#endif - -/* -** This routine is the only routine in this file with external -** linkage. It returns a pointer to a static sqlite3_mem_methods -** struct populated with the memsys5 methods. -*/ -SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){ - static const sqlite3_mem_methods memsys5Methods = { - memsys5Malloc, - memsys5Free, - memsys5Realloc, - memsys5Size, - memsys5Roundup, - memsys5Init, - memsys5Shutdown, - 0 - }; - return &memsys5Methods; -} - -#endif /* SQLITE_ENABLE_MEMSYS5 */ - -/************** End of mem5.c ************************************************/ -/************** Begin file mutex.c *******************************************/ -/* -** 2007 August 14 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the C functions that implement mutexes. -** -** This file contains code that is common across all mutex implementations. -*/ - -#if defined(SQLITE_DEBUG) && !defined(SQLITE_MUTEX_OMIT) -/* -** For debugging purposes, record when the mutex subsystem is initialized -** and uninitialized so that we can assert() if there is an attempt to -** allocate a mutex while the system is uninitialized. -*/ -static SQLITE_WSD int mutexIsInit = 0; -#endif /* SQLITE_DEBUG */ - - -#ifndef SQLITE_MUTEX_OMIT -/* -** Initialize the mutex system. -*/ -SQLITE_PRIVATE int sqlite3MutexInit(void){ - int rc = SQLITE_OK; - if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){ - /* If the xMutexAlloc method has not been set, then the user did not - ** install a mutex implementation via sqlite3_config() prior to - ** sqlite3_initialize() being called. This block copies pointers to - ** the default implementation into the sqlite3GlobalConfig structure. - */ - sqlite3_mutex_methods const *pFrom; - sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex; - - if( sqlite3GlobalConfig.bCoreMutex ){ - pFrom = sqlite3DefaultMutex(); - }else{ - pFrom = sqlite3NoopMutex(); - } - memcpy(pTo, pFrom, offsetof(sqlite3_mutex_methods, xMutexAlloc)); - memcpy(&pTo->xMutexFree, &pFrom->xMutexFree, - sizeof(*pTo) - offsetof(sqlite3_mutex_methods, xMutexFree)); - pTo->xMutexAlloc = pFrom->xMutexAlloc; - } - rc = sqlite3GlobalConfig.mutex.xMutexInit(); - -#ifdef SQLITE_DEBUG - GLOBAL(int, mutexIsInit) = 1; -#endif - - return rc; -} - -/* -** Shutdown the mutex system. This call frees resources allocated by -** sqlite3MutexInit(). -*/ -SQLITE_PRIVATE int sqlite3MutexEnd(void){ - int rc = SQLITE_OK; - if( sqlite3GlobalConfig.mutex.xMutexEnd ){ - rc = sqlite3GlobalConfig.mutex.xMutexEnd(); - } - -#ifdef SQLITE_DEBUG - GLOBAL(int, mutexIsInit) = 0; -#endif - - return rc; -} - -/* -** Retrieve a pointer to a static mutex or allocate a new dynamic one. -*/ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int id){ -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlite3_initialize() ) return 0; -#endif - return sqlite3GlobalConfig.mutex.xMutexAlloc(id); -} - -SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int id){ - if( !sqlite3GlobalConfig.bCoreMutex ){ - return 0; - } - assert( GLOBAL(int, mutexIsInit) ); - return sqlite3GlobalConfig.mutex.xMutexAlloc(id); -} - -/* -** Free a dynamic mutex. -*/ -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex *p){ - if( p ){ - sqlite3GlobalConfig.mutex.xMutexFree(p); - } -} - -/* -** Obtain the mutex p. If some other thread already has the mutex, block -** until it can be obtained. -*/ -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *p){ - if( p ){ - sqlite3GlobalConfig.mutex.xMutexEnter(p); - } -} - -/* -** Obtain the mutex p. If successful, return SQLITE_OK. Otherwise, if another -** thread holds the mutex and it cannot be obtained, return SQLITE_BUSY. -*/ -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *p){ - int rc = SQLITE_OK; - if( p ){ - return sqlite3GlobalConfig.mutex.xMutexTry(p); - } - return rc; -} - -/* -** The sqlite3_mutex_leave() routine exits a mutex that was previously -** entered by the same thread. The behavior is undefined if the mutex -** is not currently entered. If a NULL pointer is passed as an argument -** this function is a no-op. -*/ -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){ - if( p ){ - sqlite3GlobalConfig.mutex.xMutexLeave(p); - } -} - -#ifndef NDEBUG -/* -** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are -** intended for use inside assert() statements. -*/ -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){ - return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p); -} -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ - return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); -} -#endif - -#endif /* !defined(SQLITE_MUTEX_OMIT) */ - -/************** End of mutex.c ***********************************************/ -/************** Begin file mutex_noop.c **************************************/ -/* -** 2008 October 07 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the C functions that implement mutexes. -** -** This implementation in this file does not provide any mutual -** exclusion and is thus suitable for use only in applications -** that use SQLite in a single thread. The routines defined -** here are place-holders. Applications can substitute working -** mutex routines at start-time using the -** -** sqlite3_config(SQLITE_CONFIG_MUTEX,...) -** -** interface. -** -** If compiled with SQLITE_DEBUG, then additional logic is inserted -** that does error checking on mutexes to make sure they are being -** called correctly. -*/ - -#ifndef SQLITE_MUTEX_OMIT - -#ifndef SQLITE_DEBUG -/* -** Stub routines for all mutex methods. -** -** This routines provide no mutual exclusion or error checking. -*/ -static int noopMutexInit(void){ return SQLITE_OK; } -static int noopMutexEnd(void){ return SQLITE_OK; } -static sqlite3_mutex *noopMutexAlloc(int id){ - UNUSED_PARAMETER(id); - return (sqlite3_mutex*)8; -} -static void noopMutexFree(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } -static void noopMutexEnter(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } -static int noopMutexTry(sqlite3_mutex *p){ - UNUSED_PARAMETER(p); - return SQLITE_OK; -} -static void noopMutexLeave(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } - -SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3NoopMutex(void){ - static const sqlite3_mutex_methods sMutex = { - noopMutexInit, - noopMutexEnd, - noopMutexAlloc, - noopMutexFree, - noopMutexEnter, - noopMutexTry, - noopMutexLeave, - - 0, - 0, - }; - - return &sMutex; -} -#endif /* !SQLITE_DEBUG */ - -#ifdef SQLITE_DEBUG -/* -** In this implementation, error checking is provided for testing -** and debugging purposes. The mutexes still do not provide any -** mutual exclusion. -*/ - -/* -** The mutex object -*/ -typedef struct sqlite3_debug_mutex { - int id; /* The mutex type */ - int cnt; /* Number of entries without a matching leave */ -} sqlite3_debug_mutex; - -/* -** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are -** intended for use inside assert() statements. -*/ -static int debugMutexHeld(sqlite3_mutex *pX){ - sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; - return p==0 || p->cnt>0; -} -static int debugMutexNotheld(sqlite3_mutex *pX){ - sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; - return p==0 || p->cnt==0; -} - -/* -** Initialize and deinitialize the mutex subsystem. -*/ -static int debugMutexInit(void){ return SQLITE_OK; } -static int debugMutexEnd(void){ return SQLITE_OK; } - -/* -** The sqlite3_mutex_alloc() routine allocates a new -** mutex and returns a pointer to it. If it returns NULL -** that means that a mutex could not be allocated. -*/ -static sqlite3_mutex *debugMutexAlloc(int id){ - static sqlite3_debug_mutex aStatic[6]; - sqlite3_debug_mutex *pNew = 0; - switch( id ){ - case SQLITE_MUTEX_FAST: - case SQLITE_MUTEX_RECURSIVE: { - pNew = sqlite3Malloc(sizeof(*pNew)); - if( pNew ){ - pNew->id = id; - pNew->cnt = 0; - } - break; - } - default: { - assert( id-2 >= 0 ); - assert( id-2 < (int)(sizeof(aStatic)/sizeof(aStatic[0])) ); - pNew = &aStatic[id-2]; - pNew->id = id; - break; - } - } - return (sqlite3_mutex*)pNew; -} - -/* -** This routine deallocates a previously allocated mutex. -*/ -static void debugMutexFree(sqlite3_mutex *pX){ - sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; - assert( p->cnt==0 ); - assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); - sqlite3_free(p); -} - -/* -** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt -** to enter a mutex. If another thread is already within the mutex, -** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return -** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK -** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can -** be entered multiple times by the same thread. In such cases the, -** mutex must be exited an equal number of times before another thread -** can enter. If the same thread tries to enter any other kind of mutex -** more than once, the behavior is undefined. -*/ -static void debugMutexEnter(sqlite3_mutex *pX){ - sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; - assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); - p->cnt++; -} -static int debugMutexTry(sqlite3_mutex *pX){ - sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; - assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); - p->cnt++; - return SQLITE_OK; -} - -/* -** The sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. The behavior -** is undefined if the mutex is not currently entered or -** is not currently allocated. SQLite will never do either. -*/ -static void debugMutexLeave(sqlite3_mutex *pX){ - sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; - assert( debugMutexHeld(pX) ); - p->cnt--; - assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); -} - -SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3NoopMutex(void){ - static const sqlite3_mutex_methods sMutex = { - debugMutexInit, - debugMutexEnd, - debugMutexAlloc, - debugMutexFree, - debugMutexEnter, - debugMutexTry, - debugMutexLeave, - - debugMutexHeld, - debugMutexNotheld - }; - - return &sMutex; -} -#endif /* SQLITE_DEBUG */ - -/* -** If compiled with SQLITE_MUTEX_NOOP, then the no-op mutex implementation -** is used regardless of the run-time threadsafety setting. -*/ -#ifdef SQLITE_MUTEX_NOOP -SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ - return sqlite3NoopMutex(); -} -#endif /* defined(SQLITE_MUTEX_NOOP) */ -#endif /* !defined(SQLITE_MUTEX_OMIT) */ - -/************** End of mutex_noop.c ******************************************/ -/************** Begin file mutex_unix.c **************************************/ -/* -** 2007 August 28 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the C functions that implement mutexes for pthreads -*/ - -/* -** The code in this file is only used if we are compiling threadsafe -** under unix with pthreads. -** -** Note that this implementation requires a version of pthreads that -** supports recursive mutexes. -*/ -#ifdef SQLITE_MUTEX_PTHREADS - -#include - -/* -** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields -** are necessary under two condidtions: (1) Debug builds and (2) using -** home-grown mutexes. Encapsulate these conditions into a single #define. -*/ -#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX) -# define SQLITE_MUTEX_NREF 1 -#else -# define SQLITE_MUTEX_NREF 0 -#endif - -/* -** Each recursive mutex is an instance of the following structure. -*/ -struct sqlite3_mutex { - pthread_mutex_t mutex; /* Mutex controlling the lock */ -#if SQLITE_MUTEX_NREF - int id; /* Mutex type */ - volatile int nRef; /* Number of entrances */ - volatile pthread_t owner; /* Thread that is within this mutex */ - int trace; /* True to trace changes */ -#endif -}; -#if SQLITE_MUTEX_NREF -#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0, (pthread_t)0, 0 } -#else -#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } -#endif - -/* -** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are -** intended for use only inside assert() statements. On some platforms, -** there might be race conditions that can cause these routines to -** deliver incorrect results. In particular, if pthread_equal() is -** not an atomic operation, then these routines might delivery -** incorrect results. On most platforms, pthread_equal() is a -** comparison of two integers and is therefore atomic. But we are -** told that HPUX is not such a platform. If so, then these routines -** will not always work correctly on HPUX. -** -** On those platforms where pthread_equal() is not atomic, SQLite -** should be compiled without -DSQLITE_DEBUG and with -DNDEBUG to -** make sure no assert() statements are evaluated and hence these -** routines are never called. -*/ -#if !defined(NDEBUG) || defined(SQLITE_DEBUG) -static int pthreadMutexHeld(sqlite3_mutex *p){ - return (p->nRef!=0 && pthread_equal(p->owner, pthread_self())); -} -static int pthreadMutexNotheld(sqlite3_mutex *p){ - return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0; -} -#endif - -/* -** Initialize and deinitialize the mutex subsystem. -*/ -static int pthreadMutexInit(void){ return SQLITE_OK; } -static int pthreadMutexEnd(void){ return SQLITE_OK; } - -/* -** The sqlite3_mutex_alloc() routine allocates a new -** mutex and returns a pointer to it. If it returns NULL -** that means that a mutex could not be allocated. SQLite -** will unwind its stack and return an error. The argument -** to sqlite3_mutex_alloc() is one of these integer constants: -** -**
    -**
  • SQLITE_MUTEX_FAST -**
  • SQLITE_MUTEX_RECURSIVE -**
  • SQLITE_MUTEX_STATIC_MASTER -**
  • SQLITE_MUTEX_STATIC_MEM -**
  • SQLITE_MUTEX_STATIC_MEM2 -**
  • SQLITE_MUTEX_STATIC_PRNG -**
  • SQLITE_MUTEX_STATIC_LRU -**
  • SQLITE_MUTEX_STATIC_PMEM -**
-** -** The first two constants cause sqlite3_mutex_alloc() to create -** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE -** is used but not necessarily so when SQLITE_MUTEX_FAST is used. -** The mutex implementation does not need to make a distinction -** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does -** not want to. But SQLite will only request a recursive mutex in -** cases where it really needs one. If a faster non-recursive mutex -** implementation is available on the host platform, the mutex subsystem -** might return such a mutex in response to SQLITE_MUTEX_FAST. -** -** The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Six static mutexes are -** used by the current version of SQLite. Future versions of SQLite -** may add additional static mutexes. Static mutexes are for internal -** use by SQLite only. Applications that use SQLite mutexes should -** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or -** SQLITE_MUTEX_RECURSIVE. -** -** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST -** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() -** returns a different mutex on every call. But for the static -** mutex types, the same mutex is returned on every call that has -** the same type number. -*/ -static sqlite3_mutex *pthreadMutexAlloc(int iType){ - static sqlite3_mutex staticMutexes[] = { - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER - }; - sqlite3_mutex *p; - switch( iType ){ - case SQLITE_MUTEX_RECURSIVE: { - p = sqlite3MallocZero( sizeof(*p) ); - if( p ){ -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX - /* If recursive mutexes are not available, we will have to - ** build our own. See below. */ - pthread_mutex_init(&p->mutex, 0); -#else - /* Use a recursive mutex if it is available */ - pthread_mutexattr_t recursiveAttr; - pthread_mutexattr_init(&recursiveAttr); - pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&p->mutex, &recursiveAttr); - pthread_mutexattr_destroy(&recursiveAttr); -#endif -#if SQLITE_MUTEX_NREF - p->id = iType; -#endif - } - break; - } - case SQLITE_MUTEX_FAST: { - p = sqlite3MallocZero( sizeof(*p) ); - if( p ){ -#if SQLITE_MUTEX_NREF - p->id = iType; -#endif - pthread_mutex_init(&p->mutex, 0); - } - break; - } - default: { - assert( iType-2 >= 0 ); - assert( iType-2 < ArraySize(staticMutexes) ); - p = &staticMutexes[iType-2]; -#if SQLITE_MUTEX_NREF - p->id = iType; -#endif - break; - } - } - return p; -} - - -/* -** This routine deallocates a previously -** allocated mutex. SQLite is careful to deallocate every -** mutex that it allocates. -*/ -static void pthreadMutexFree(sqlite3_mutex *p){ - assert( p->nRef==0 ); - assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); - pthread_mutex_destroy(&p->mutex); - sqlite3_free(p); -} - -/* -** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt -** to enter a mutex. If another thread is already within the mutex, -** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return -** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK -** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can -** be entered multiple times by the same thread. In such cases the, -** mutex must be exited an equal number of times before another thread -** can enter. If the same thread tries to enter any other kind of mutex -** more than once, the behavior is undefined. -*/ -static void pthreadMutexEnter(sqlite3_mutex *p){ - assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) ); - -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX - /* If recursive mutexes are not available, then we have to grow - ** our own. This implementation assumes that pthread_equal() - ** is atomic - that it cannot be deceived into thinking self - ** and p->owner are equal if p->owner changes between two values - ** that are not equal to self while the comparison is taking place. - ** This implementation also assumes a coherent cache - that - ** separate processes cannot read different values from the same - ** address at the same time. If either of these two conditions - ** are not met, then the mutexes will fail and problems will result. - */ - { - pthread_t self = pthread_self(); - if( p->nRef>0 && pthread_equal(p->owner, self) ){ - p->nRef++; - }else{ - pthread_mutex_lock(&p->mutex); - assert( p->nRef==0 ); - p->owner = self; - p->nRef = 1; - } - } -#else - /* Use the built-in recursive mutexes if they are available. - */ - pthread_mutex_lock(&p->mutex); -#if SQLITE_MUTEX_NREF - assert( p->nRef>0 || p->owner==0 ); - p->owner = pthread_self(); - p->nRef++; -#endif -#endif - -#ifdef SQLITE_DEBUG - if( p->trace ){ - printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); - } -#endif -} -static int pthreadMutexTry(sqlite3_mutex *p){ - int rc; - assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) ); - -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX - /* If recursive mutexes are not available, then we have to grow - ** our own. This implementation assumes that pthread_equal() - ** is atomic - that it cannot be deceived into thinking self - ** and p->owner are equal if p->owner changes between two values - ** that are not equal to self while the comparison is taking place. - ** This implementation also assumes a coherent cache - that - ** separate processes cannot read different values from the same - ** address at the same time. If either of these two conditions - ** are not met, then the mutexes will fail and problems will result. - */ - { - pthread_t self = pthread_self(); - if( p->nRef>0 && pthread_equal(p->owner, self) ){ - p->nRef++; - rc = SQLITE_OK; - }else if( pthread_mutex_trylock(&p->mutex)==0 ){ - assert( p->nRef==0 ); - p->owner = self; - p->nRef = 1; - rc = SQLITE_OK; - }else{ - rc = SQLITE_BUSY; - } - } -#else - /* Use the built-in recursive mutexes if they are available. - */ - if( pthread_mutex_trylock(&p->mutex)==0 ){ -#if SQLITE_MUTEX_NREF - p->owner = pthread_self(); - p->nRef++; -#endif - rc = SQLITE_OK; - }else{ - rc = SQLITE_BUSY; - } -#endif - -#ifdef SQLITE_DEBUG - if( rc==SQLITE_OK && p->trace ){ - printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); - } -#endif - return rc; -} - -/* -** The sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. The behavior -** is undefined if the mutex is not currently entered or -** is not currently allocated. SQLite will never do either. -*/ -static void pthreadMutexLeave(sqlite3_mutex *p){ - assert( pthreadMutexHeld(p) ); -#if SQLITE_MUTEX_NREF - p->nRef--; - if( p->nRef==0 ) p->owner = 0; -#endif - assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); - -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX - if( p->nRef==0 ){ - pthread_mutex_unlock(&p->mutex); - } -#else - pthread_mutex_unlock(&p->mutex); -#endif - -#ifdef SQLITE_DEBUG - if( p->trace ){ - printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); - } -#endif -} - -SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ - static const sqlite3_mutex_methods sMutex = { - pthreadMutexInit, - pthreadMutexEnd, - pthreadMutexAlloc, - pthreadMutexFree, - pthreadMutexEnter, - pthreadMutexTry, - pthreadMutexLeave, -#ifdef SQLITE_DEBUG - pthreadMutexHeld, - pthreadMutexNotheld -#else - 0, - 0 -#endif - }; - - return &sMutex; -} - -#endif /* SQLITE_MUTEX_PTHREADS */ - -/************** End of mutex_unix.c ******************************************/ -/************** Begin file mutex_w32.c ***************************************/ -/* -** 2007 August 14 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the C functions that implement mutexes for win32 -*/ - -/* -** The code in this file is only used if we are compiling multithreaded -** on a win32 system. -*/ -#ifdef SQLITE_MUTEX_W32 - -/* -** Each recursive mutex is an instance of the following structure. -*/ -struct sqlite3_mutex { - CRITICAL_SECTION mutex; /* Mutex controlling the lock */ - int id; /* Mutex type */ -#ifdef SQLITE_DEBUG - volatile int nRef; /* Number of enterances */ - volatile DWORD owner; /* Thread holding this mutex */ - int trace; /* True to trace changes */ -#endif -}; -#define SQLITE_W32_MUTEX_INITIALIZER { 0 } -#ifdef SQLITE_DEBUG -#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 } -#else -#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 } -#endif - -/* -** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, -** or WinCE. Return false (zero) for Win95, Win98, or WinME. -** -** Here is an interesting observation: Win95, Win98, and WinME lack -** the LockFileEx() API. But we can still statically link against that -** API as long as we don't call it win running Win95/98/ME. A call to -** this routine is used to determine if the host is Win95/98/ME or -** WinNT/2K/XP so that we will know whether or not we can safely call -** the LockFileEx() API. -** -** mutexIsNT() is only used for the TryEnterCriticalSection() API call, -** which is only available if your application was compiled with -** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only -** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef -** this out as well. -*/ -#if 0 -#if SQLITE_OS_WINCE || SQLITE_OS_WINRT -# define mutexIsNT() (1) -#else - static int mutexIsNT(void){ - static int osType = 0; - if( osType==0 ){ - OSVERSIONINFO sInfo; - sInfo.dwOSVersionInfoSize = sizeof(sInfo); - GetVersionEx(&sInfo); - osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; - } - return osType==2; - } -#endif /* SQLITE_OS_WINCE */ -#endif - -#ifdef SQLITE_DEBUG -/* -** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are -** intended for use only inside assert() statements. -*/ -static int winMutexHeld(sqlite3_mutex *p){ - return p->nRef!=0 && p->owner==GetCurrentThreadId(); -} -static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){ - return p->nRef==0 || p->owner!=tid; -} -static int winMutexNotheld(sqlite3_mutex *p){ - DWORD tid = GetCurrentThreadId(); - return winMutexNotheld2(p, tid); -} -#endif - - -/* -** Initialize and deinitialize the mutex subsystem. -*/ -static sqlite3_mutex winMutex_staticMutexes[6] = { - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER -}; -static int winMutex_isInit = 0; -/* As winMutexInit() and winMutexEnd() are called as part -** of the sqlite3_initialize and sqlite3_shutdown() -** processing, the "interlocked" magic is probably not -** strictly necessary. -*/ -static long winMutex_lock = 0; - -SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ - -static int winMutexInit(void){ - /* The first to increment to 1 does actual initialization */ - if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){ - int i; - for(i=0; i -**
  • SQLITE_MUTEX_FAST -**
  • SQLITE_MUTEX_RECURSIVE -**
  • SQLITE_MUTEX_STATIC_MASTER -**
  • SQLITE_MUTEX_STATIC_MEM -**
  • SQLITE_MUTEX_STATIC_MEM2 -**
  • SQLITE_MUTEX_STATIC_PRNG -**
  • SQLITE_MUTEX_STATIC_LRU -**
  • SQLITE_MUTEX_STATIC_PMEM -** -** -** The first two constants cause sqlite3_mutex_alloc() to create -** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE -** is used but not necessarily so when SQLITE_MUTEX_FAST is used. -** The mutex implementation does not need to make a distinction -** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does -** not want to. But SQLite will only request a recursive mutex in -** cases where it really needs one. If a faster non-recursive mutex -** implementation is available on the host platform, the mutex subsystem -** might return such a mutex in response to SQLITE_MUTEX_FAST. -** -** The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Six static mutexes are -** used by the current version of SQLite. Future versions of SQLite -** may add additional static mutexes. Static mutexes are for internal -** use by SQLite only. Applications that use SQLite mutexes should -** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or -** SQLITE_MUTEX_RECURSIVE. -** -** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST -** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() -** returns a different mutex on every call. But for the static -** mutex types, the same mutex is returned on every call that has -** the same type number. -*/ -static sqlite3_mutex *winMutexAlloc(int iType){ - sqlite3_mutex *p; - - switch( iType ){ - case SQLITE_MUTEX_FAST: - case SQLITE_MUTEX_RECURSIVE: { - p = sqlite3MallocZero( sizeof(*p) ); - if( p ){ -#ifdef SQLITE_DEBUG - p->id = iType; -#endif -#if SQLITE_OS_WINRT - InitializeCriticalSectionEx(&p->mutex, 0, 0); -#else - InitializeCriticalSection(&p->mutex); -#endif - } - break; - } - default: { - assert( winMutex_isInit==1 ); - assert( iType-2 >= 0 ); - assert( iType-2 < ArraySize(winMutex_staticMutexes) ); - p = &winMutex_staticMutexes[iType-2]; -#ifdef SQLITE_DEBUG - p->id = iType; -#endif - break; - } - } - return p; -} - - -/* -** This routine deallocates a previously -** allocated mutex. SQLite is careful to deallocate every -** mutex that it allocates. -*/ -static void winMutexFree(sqlite3_mutex *p){ - assert( p ); - assert( p->nRef==0 && p->owner==0 ); - assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); - DeleteCriticalSection(&p->mutex); - sqlite3_free(p); -} - -/* -** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt -** to enter a mutex. If another thread is already within the mutex, -** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return -** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK -** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can -** be entered multiple times by the same thread. In such cases the, -** mutex must be exited an equal number of times before another thread -** can enter. If the same thread tries to enter any other kind of mutex -** more than once, the behavior is undefined. -*/ -static void winMutexEnter(sqlite3_mutex *p){ -#ifdef SQLITE_DEBUG - DWORD tid = GetCurrentThreadId(); - assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); -#endif - EnterCriticalSection(&p->mutex); -#ifdef SQLITE_DEBUG - assert( p->nRef>0 || p->owner==0 ); - p->owner = tid; - p->nRef++; - if( p->trace ){ - printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); - } -#endif -} -static int winMutexTry(sqlite3_mutex *p){ -#ifndef NDEBUG - DWORD tid = GetCurrentThreadId(); -#endif - int rc = SQLITE_BUSY; - assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); - /* - ** The sqlite3_mutex_try() routine is very rarely used, and when it - ** is used it is merely an optimization. So it is OK for it to always - ** fail. - ** - ** The TryEnterCriticalSection() interface is only available on WinNT. - ** And some windows compilers complain if you try to use it without - ** first doing some #defines that prevent SQLite from building on Win98. - ** For that reason, we will omit this optimization for now. See - ** ticket #2685. - */ -#if 0 - if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){ - p->owner = tid; - p->nRef++; - rc = SQLITE_OK; - } -#else - UNUSED_PARAMETER(p); -#endif -#ifdef SQLITE_DEBUG - if( rc==SQLITE_OK && p->trace ){ - printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); - } -#endif - return rc; -} - -/* -** The sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. The behavior -** is undefined if the mutex is not currently entered or -** is not currently allocated. SQLite will never do either. -*/ -static void winMutexLeave(sqlite3_mutex *p){ -#ifndef NDEBUG - DWORD tid = GetCurrentThreadId(); - assert( p->nRef>0 ); - assert( p->owner==tid ); - p->nRef--; - if( p->nRef==0 ) p->owner = 0; - assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); -#endif - LeaveCriticalSection(&p->mutex); -#ifdef SQLITE_DEBUG - if( p->trace ){ - printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); - } -#endif -} - -SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ - static const sqlite3_mutex_methods sMutex = { - winMutexInit, - winMutexEnd, - winMutexAlloc, - winMutexFree, - winMutexEnter, - winMutexTry, - winMutexLeave, -#ifdef SQLITE_DEBUG - winMutexHeld, - winMutexNotheld -#else - 0, - 0 -#endif - }; - - return &sMutex; -} -#endif /* SQLITE_MUTEX_W32 */ - -/************** End of mutex_w32.c *******************************************/ -/************** Begin file malloc.c ******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** Memory allocation functions used throughout sqlite. -*/ -/* #include */ - -/* -** Attempt to release up to n bytes of non-essential memory currently -** held by SQLite. An example of non-essential memory is memory used to -** cache database pages that are not currently in use. -*/ -SQLITE_API int sqlite3_release_memory(int n){ -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT - return sqlite3PcacheReleaseMemory(n); -#else - /* IMPLEMENTATION-OF: R-34391-24921 The sqlite3_release_memory() routine - ** is a no-op returning zero if SQLite is not compiled with - ** SQLITE_ENABLE_MEMORY_MANAGEMENT. */ - UNUSED_PARAMETER(n); - return 0; -#endif -} - -/* -** An instance of the following object records the location of -** each unused scratch buffer. -*/ -typedef struct ScratchFreeslot { - struct ScratchFreeslot *pNext; /* Next unused scratch buffer */ -} ScratchFreeslot; - -/* -** State information local to the memory allocation subsystem. -*/ -static SQLITE_WSD struct Mem0Global { - sqlite3_mutex *mutex; /* Mutex to serialize access */ - - /* - ** The alarm callback and its arguments. The mem0.mutex lock will - ** be held while the callback is running. Recursive calls into - ** the memory subsystem are allowed, but no new callbacks will be - ** issued. - */ - sqlite3_int64 alarmThreshold; - void (*alarmCallback)(void*, sqlite3_int64,int); - void *alarmArg; - - /* - ** Pointers to the end of sqlite3GlobalConfig.pScratch memory - ** (so that a range test can be used to determine if an allocation - ** being freed came from pScratch) and a pointer to the list of - ** unused scratch allocations. - */ - void *pScratchEnd; - ScratchFreeslot *pScratchFree; - u32 nScratchFree; - - /* - ** True if heap is nearly "full" where "full" is defined by the - ** sqlite3_soft_heap_limit() setting. - */ - int nearlyFull; -} mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 }; - -#define mem0 GLOBAL(struct Mem0Global, mem0) - -/* -** This routine runs when the memory allocator sees that the -** total memory allocation is about to exceed the soft heap -** limit. -*/ -static void softHeapLimitEnforcer( - void *NotUsed, - sqlite3_int64 NotUsed2, - int allocSize -){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - sqlite3_release_memory(allocSize); -} - -/* -** Change the alarm callback -*/ -static int sqlite3MemoryAlarm( - void(*xCallback)(void *pArg, sqlite3_int64 used,int N), - void *pArg, - sqlite3_int64 iThreshold -){ - int nUsed; - sqlite3_mutex_enter(mem0.mutex); - mem0.alarmCallback = xCallback; - mem0.alarmArg = pArg; - mem0.alarmThreshold = iThreshold; - nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - mem0.nearlyFull = (iThreshold>0 && iThreshold<=nUsed); - sqlite3_mutex_leave(mem0.mutex); - return SQLITE_OK; -} - -#ifndef SQLITE_OMIT_DEPRECATED -/* -** Deprecated external interface. Internal/core SQLite code -** should call sqlite3MemoryAlarm. -*/ -SQLITE_API int sqlite3_memory_alarm( - void(*xCallback)(void *pArg, sqlite3_int64 used,int N), - void *pArg, - sqlite3_int64 iThreshold -){ - return sqlite3MemoryAlarm(xCallback, pArg, iThreshold); -} -#endif - -/* -** Set the soft heap-size limit for the library. Passing a zero or -** negative value indicates no limit. -*/ -SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){ - sqlite3_int64 priorLimit; - sqlite3_int64 excess; -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlite3_initialize(); - if( rc ) return -1; -#endif - sqlite3_mutex_enter(mem0.mutex); - priorLimit = mem0.alarmThreshold; - sqlite3_mutex_leave(mem0.mutex); - if( n<0 ) return priorLimit; - if( n>0 ){ - sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n); - }else{ - sqlite3MemoryAlarm(0, 0, 0); - } - excess = sqlite3_memory_used() - n; - if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); - return priorLimit; -} -SQLITE_API void sqlite3_soft_heap_limit(int n){ - if( n<0 ) n = 0; - sqlite3_soft_heap_limit64(n); -} - -/* -** Initialize the memory allocation subsystem. -*/ -SQLITE_PRIVATE int sqlite3MallocInit(void){ - if( sqlite3GlobalConfig.m.xMalloc==0 ){ - sqlite3MemSetDefault(); - } - memset(&mem0, 0, sizeof(mem0)); - if( sqlite3GlobalConfig.bCoreMutex ){ - mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); - } - if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100 - && sqlite3GlobalConfig.nScratch>0 ){ - int i, n, sz; - ScratchFreeslot *pSlot; - sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch); - sqlite3GlobalConfig.szScratch = sz; - pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch; - n = sqlite3GlobalConfig.nScratch; - mem0.pScratchFree = pSlot; - mem0.nScratchFree = n; - for(i=0; ipNext = (ScratchFreeslot*)(sz+(char*)pSlot); - pSlot = pSlot->pNext; - } - pSlot->pNext = 0; - mem0.pScratchEnd = (void*)&pSlot[1]; - }else{ - mem0.pScratchEnd = 0; - sqlite3GlobalConfig.pScratch = 0; - sqlite3GlobalConfig.szScratch = 0; - sqlite3GlobalConfig.nScratch = 0; - } - if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 - || sqlite3GlobalConfig.nPage<1 ){ - sqlite3GlobalConfig.pPage = 0; - sqlite3GlobalConfig.szPage = 0; - sqlite3GlobalConfig.nPage = 0; - } - return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); -} - -/* -** Return true if the heap is currently under memory pressure - in other -** words if the amount of heap used is close to the limit set by -** sqlite3_soft_heap_limit(). -*/ -SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){ - return mem0.nearlyFull; -} - -/* -** Deinitialize the memory allocation subsystem. -*/ -SQLITE_PRIVATE void sqlite3MallocEnd(void){ - if( sqlite3GlobalConfig.m.xShutdown ){ - sqlite3GlobalConfig.m.xShutdown(sqlite3GlobalConfig.m.pAppData); - } - memset(&mem0, 0, sizeof(mem0)); -} - -/* -** Return the amount of memory currently checked out. -*/ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void){ - int n, mx; - sqlite3_int64 res; - sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, 0); - res = (sqlite3_int64)n; /* Work around bug in Borland C. Ticket #3216 */ - return res; -} - -/* -** Return the maximum amount of memory that has ever been -** checked out since either the beginning of this process -** or since the most recent reset. -*/ -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag){ - int n, mx; - sqlite3_int64 res; - sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag); - res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */ - return res; -} - -/* -** Trigger the alarm -*/ -static void sqlite3MallocAlarm(int nByte){ - void (*xCallback)(void*,sqlite3_int64,int); - sqlite3_int64 nowUsed; - void *pArg; - if( mem0.alarmCallback==0 ) return; - xCallback = mem0.alarmCallback; - nowUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - pArg = mem0.alarmArg; - mem0.alarmCallback = 0; - sqlite3_mutex_leave(mem0.mutex); - xCallback(pArg, nowUsed, nByte); - sqlite3_mutex_enter(mem0.mutex); - mem0.alarmCallback = xCallback; - mem0.alarmArg = pArg; -} - -/* -** Do a memory allocation with statistics and alarms. Assume the -** lock is already held. -*/ -static int mallocWithAlarm(int n, void **pp){ - int nFull; - void *p; - assert( sqlite3_mutex_held(mem0.mutex) ); - nFull = sqlite3GlobalConfig.m.xRoundup(n); - sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n); - if( mem0.alarmCallback!=0 ){ - int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - if( nUsed >= mem0.alarmThreshold - nFull ){ - mem0.nearlyFull = 1; - sqlite3MallocAlarm(nFull); - }else{ - mem0.nearlyFull = 0; - } - } - p = sqlite3GlobalConfig.m.xMalloc(nFull); -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT - if( p==0 && mem0.alarmCallback ){ - sqlite3MallocAlarm(nFull); - p = sqlite3GlobalConfig.m.xMalloc(nFull); - } -#endif - if( p ){ - nFull = sqlite3MallocSize(p); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull); - sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1); - } - *pp = p; - return nFull; -} - -/* -** Allocate memory. This routine is like sqlite3_malloc() except that it -** assumes the memory subsystem has already been initialized. -*/ -SQLITE_PRIVATE void *sqlite3Malloc(int n){ - void *p; - if( n<=0 /* IMP: R-65312-04917 */ - || n>=0x7fffff00 - ){ - /* A memory allocation of a number of bytes which is near the maximum - ** signed integer value might cause an integer overflow inside of the - ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving - ** 255 bytes of overhead. SQLite itself will never use anything near - ** this amount. The only way to reach the limit is with sqlite3_malloc() */ - p = 0; - }else if( sqlite3GlobalConfig.bMemstat ){ - sqlite3_mutex_enter(mem0.mutex); - mallocWithAlarm(n, &p); - sqlite3_mutex_leave(mem0.mutex); - }else{ - p = sqlite3GlobalConfig.m.xMalloc(n); - } - assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */ - return p; -} - -/* -** This version of the memory allocation is for use by the application. -** First make sure the memory subsystem is initialized, then do the -** allocation. -*/ -SQLITE_API void *sqlite3_malloc(int n){ -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlite3_initialize() ) return 0; -#endif - return sqlite3Malloc(n); -} - -/* -** Each thread may only have a single outstanding allocation from -** xScratchMalloc(). We verify this constraint in the single-threaded -** case by setting scratchAllocOut to 1 when an allocation -** is outstanding clearing it when the allocation is freed. -*/ -#if SQLITE_THREADSAFE==0 && !defined(NDEBUG) -static int scratchAllocOut = 0; -#endif - - -/* -** Allocate memory that is to be used and released right away. -** This routine is similar to alloca() in that it is not intended -** for situations where the memory might be held long-term. This -** routine is intended to get memory to old large transient data -** structures that would not normally fit on the stack of an -** embedded processor. -*/ -SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){ - void *p; - assert( n>0 ); - - sqlite3_mutex_enter(mem0.mutex); - if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){ - p = mem0.pScratchFree; - mem0.pScratchFree = mem0.pScratchFree->pNext; - mem0.nScratchFree--; - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1); - sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n); - sqlite3_mutex_leave(mem0.mutex); - }else{ - if( sqlite3GlobalConfig.bMemstat ){ - sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n); - n = mallocWithAlarm(n, &p); - if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n); - sqlite3_mutex_leave(mem0.mutex); - }else{ - sqlite3_mutex_leave(mem0.mutex); - p = sqlite3GlobalConfig.m.xMalloc(n); - } - sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH); - } - assert( sqlite3_mutex_notheld(mem0.mutex) ); - - -#if SQLITE_THREADSAFE==0 && !defined(NDEBUG) - /* Verify that no more than two scratch allocations per thread - ** are outstanding at one time. (This is only checked in the - ** single-threaded case since checking in the multi-threaded case - ** would be much more complicated.) */ - assert( scratchAllocOut<=1 ); - if( p ) scratchAllocOut++; -#endif - - return p; -} -SQLITE_PRIVATE void sqlite3ScratchFree(void *p){ - if( p ){ - -#if SQLITE_THREADSAFE==0 && !defined(NDEBUG) - /* Verify that no more than two scratch allocation per thread - ** is outstanding at one time. (This is only checked in the - ** single-threaded case since checking in the multi-threaded case - ** would be much more complicated.) */ - assert( scratchAllocOut>=1 && scratchAllocOut<=2 ); - scratchAllocOut--; -#endif - - if( p>=sqlite3GlobalConfig.pScratch && ppNext = mem0.pScratchFree; - mem0.pScratchFree = pSlot; - mem0.nScratchFree++; - assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch ); - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1); - sqlite3_mutex_leave(mem0.mutex); - }else{ - /* Release memory back to the heap */ - assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) ); - assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) ); - sqlite3MemdebugSetType(p, MEMTYPE_HEAP); - if( sqlite3GlobalConfig.bMemstat ){ - int iSize = sqlite3MallocSize(p); - sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize); - sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); - sqlite3GlobalConfig.m.xFree(p); - sqlite3_mutex_leave(mem0.mutex); - }else{ - sqlite3GlobalConfig.m.xFree(p); - } - } - } -} - -/* -** TRUE if p is a lookaside memory allocation from db -*/ -#ifndef SQLITE_OMIT_LOOKASIDE -static int isLookaside(sqlite3 *db, void *p){ - return p && p>=db->lookaside.pStart && plookaside.pEnd; -} -#else -#define isLookaside(A,B) 0 -#endif - -/* -** Return the size of a memory allocation previously obtained from -** sqlite3Malloc() or sqlite3_malloc(). -*/ -SQLITE_PRIVATE int sqlite3MallocSize(void *p){ - assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); - return sqlite3GlobalConfig.m.xSize(p); -} -SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ - assert( db==0 || sqlite3_mutex_held(db->mutex) ); - if( db && isLookaside(db, p) ){ - return db->lookaside.sz; - }else{ - assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); - assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) ); - assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); - return sqlite3GlobalConfig.m.xSize(p); - } -} - -/* -** Free memory previously obtained from sqlite3Malloc(). -*/ -SQLITE_API void sqlite3_free(void *p){ - if( p==0 ) return; /* IMP: R-49053-54554 */ - assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); - assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - if( sqlite3GlobalConfig.bMemstat ){ - sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p)); - sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); - sqlite3GlobalConfig.m.xFree(p); - sqlite3_mutex_leave(mem0.mutex); - }else{ - sqlite3GlobalConfig.m.xFree(p); - } -} - -/* -** Free memory that might be associated with a particular database -** connection. -*/ -SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){ - assert( db==0 || sqlite3_mutex_held(db->mutex) ); - if( db ){ - if( db->pnBytesFreed ){ - *db->pnBytesFreed += sqlite3DbMallocSize(db, p); - return; - } - if( isLookaside(db, p) ){ - LookasideSlot *pBuf = (LookasideSlot*)p; -#if SQLITE_DEBUG - /* Trash all content in the buffer being freed */ - memset(p, 0xaa, db->lookaside.sz); -#endif - pBuf->pNext = db->lookaside.pFree; - db->lookaside.pFree = pBuf; - db->lookaside.nOut--; - return; - } - } - assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); - assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) ); - assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); - sqlite3MemdebugSetType(p, MEMTYPE_HEAP); - sqlite3_free(p); -} - -/* -** Change the size of an existing memory allocation -*/ -SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){ - int nOld, nNew, nDiff; - void *pNew; - if( pOld==0 ){ - return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */ - } - if( nBytes<=0 ){ - sqlite3_free(pOld); /* IMP: R-31593-10574 */ - return 0; - } - if( nBytes>=0x7fffff00 ){ - /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */ - return 0; - } - nOld = sqlite3MallocSize(pOld); - /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second - ** argument to xRealloc is always a value returned by a prior call to - ** xRoundup. */ - nNew = sqlite3GlobalConfig.m.xRoundup(nBytes); - if( nOld==nNew ){ - pNew = pOld; - }else if( sqlite3GlobalConfig.bMemstat ){ - sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); - nDiff = nNew - nOld; - if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= - mem0.alarmThreshold-nDiff ){ - sqlite3MallocAlarm(nDiff); - } - assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); - assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) ); - pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); - if( pNew==0 && mem0.alarmCallback ){ - sqlite3MallocAlarm(nBytes); - pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); - } - if( pNew ){ - nNew = sqlite3MallocSize(pNew); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); - } - sqlite3_mutex_leave(mem0.mutex); - }else{ - pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); - } - assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-04675-44850 */ - return pNew; -} - -/* -** The public interface to sqlite3Realloc. Make sure that the memory -** subsystem is initialized prior to invoking sqliteRealloc. -*/ -SQLITE_API void *sqlite3_realloc(void *pOld, int n){ -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlite3_initialize() ) return 0; -#endif - return sqlite3Realloc(pOld, n); -} - - -/* -** Allocate and zero memory. -*/ -SQLITE_PRIVATE void *sqlite3MallocZero(int n){ - void *p = sqlite3Malloc(n); - if( p ){ - memset(p, 0, n); - } - return p; -} - -/* -** Allocate and zero memory. If the allocation fails, make -** the mallocFailed flag in the connection pointer. -*/ -SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, int n){ - void *p = sqlite3DbMallocRaw(db, n); - if( p ){ - memset(p, 0, n); - } - return p; -} - -/* -** Allocate and zero memory. If the allocation fails, make -** the mallocFailed flag in the connection pointer. -** -** If db!=0 and db->mallocFailed is true (indicating a prior malloc -** failure on the same database connection) then always return 0. -** Hence for a particular database connection, once malloc starts -** failing, it fails consistently until mallocFailed is reset. -** This is an important assumption. There are many places in the -** code that do things like this: -** -** int *a = (int*)sqlite3DbMallocRaw(db, 100); -** int *b = (int*)sqlite3DbMallocRaw(db, 200); -** if( b ) a[10] = 9; -** -** In other words, if a subsequent malloc (ex: "b") worked, it is assumed -** that all prior mallocs (ex: "a") worked too. -*/ -SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, int n){ - void *p; - assert( db==0 || sqlite3_mutex_held(db->mutex) ); - assert( db==0 || db->pnBytesFreed==0 ); -#ifndef SQLITE_OMIT_LOOKASIDE - if( db ){ - LookasideSlot *pBuf; - if( db->mallocFailed ){ - return 0; - } - if( db->lookaside.bEnabled ){ - if( n>db->lookaside.sz ){ - db->lookaside.anStat[1]++; - }else if( (pBuf = db->lookaside.pFree)==0 ){ - db->lookaside.anStat[2]++; - }else{ - db->lookaside.pFree = pBuf->pNext; - db->lookaside.nOut++; - db->lookaside.anStat[0]++; - if( db->lookaside.nOut>db->lookaside.mxOut ){ - db->lookaside.mxOut = db->lookaside.nOut; - } - return (void*)pBuf; - } - } - } -#else - if( db && db->mallocFailed ){ - return 0; - } -#endif - p = sqlite3Malloc(n); - if( !p && db ){ - db->mallocFailed = 1; - } - sqlite3MemdebugSetType(p, MEMTYPE_DB | - ((db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); - return p; -} - -/* -** Resize the block of memory pointed to by p to n bytes. If the -** resize fails, set the mallocFailed flag in the connection object. -*/ -SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){ - void *pNew = 0; - assert( db!=0 ); - assert( sqlite3_mutex_held(db->mutex) ); - if( db->mallocFailed==0 ){ - if( p==0 ){ - return sqlite3DbMallocRaw(db, n); - } - if( isLookaside(db, p) ){ - if( n<=db->lookaside.sz ){ - return p; - } - pNew = sqlite3DbMallocRaw(db, n); - if( pNew ){ - memcpy(pNew, p, db->lookaside.sz); - sqlite3DbFree(db, p); - } - }else{ - assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); - assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) ); - sqlite3MemdebugSetType(p, MEMTYPE_HEAP); - pNew = sqlite3_realloc(p, n); - if( !pNew ){ - sqlite3MemdebugSetType(p, MEMTYPE_DB|MEMTYPE_HEAP); - db->mallocFailed = 1; - } - sqlite3MemdebugSetType(pNew, MEMTYPE_DB | - (db->lookaside.bEnabled ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); - } - } - return pNew; -} - -/* -** Attempt to reallocate p. If the reallocation fails, then free p -** and set the mallocFailed flag in the database connection. -*/ -SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, int n){ - void *pNew; - pNew = sqlite3DbRealloc(db, p, n); - if( !pNew ){ - sqlite3DbFree(db, p); - } - return pNew; -} - -/* -** Make a copy of a string in memory obtained from sqliteMalloc(). These -** functions call sqlite3MallocRaw() directly instead of sqliteMalloc(). This -** is because when memory debugging is turned on, these two functions are -** called via macros that record the current file and line number in the -** ThreadData structure. -*/ -SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3 *db, const char *z){ - char *zNew; - size_t n; - if( z==0 ){ - return 0; - } - n = sqlite3Strlen30(z) + 1; - assert( (n&0x7fffffff)==n ); - zNew = sqlite3DbMallocRaw(db, (int)n); - if( zNew ){ - memcpy(zNew, z, n); - } - return zNew; -} -SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, int n){ - char *zNew; - if( z==0 ){ - return 0; - } - assert( (n&0x7fffffff)==n ); - zNew = sqlite3DbMallocRaw(db, n+1); - if( zNew ){ - memcpy(zNew, z, n); - zNew[n] = 0; - } - return zNew; -} - -/* -** Create a string from the zFromat argument and the va_list that follows. -** Store the string in memory obtained from sqliteMalloc() and make *pz -** point to that string. -*/ -SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){ - va_list ap; - char *z; - - va_start(ap, zFormat); - z = sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - sqlite3DbFree(db, *pz); - *pz = z; -} - - -/* -** This function must be called before exiting any API function (i.e. -** returning control to the user) that has called sqlite3_malloc or -** sqlite3_realloc. -** -** The returned value is normally a copy of the second argument to this -** function. However, if a malloc() failure has occurred since the previous -** invocation SQLITE_NOMEM is returned instead. -** -** If the first argument, db, is not NULL and a malloc() error has occurred, -** then the connection error-code (the value returned by sqlite3_errcode()) -** is set to SQLITE_NOMEM. -*/ -SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){ - /* If the db handle is not NULL, then we must hold the connection handle - ** mutex here. Otherwise the read (and possible write) of db->mallocFailed - ** is unsafe, as is the call to sqlite3Error(). - */ - assert( !db || sqlite3_mutex_held(db->mutex) ); - if( db && (db->mallocFailed || rc==SQLITE_IOERR_NOMEM) ){ - sqlite3Error(db, SQLITE_NOMEM, 0); - db->mallocFailed = 0; - rc = SQLITE_NOMEM; - } - return rc & (db ? db->errMask : 0xff); -} - -/************** End of malloc.c **********************************************/ -/************** Begin file printf.c ******************************************/ -/* -** The "printf" code that follows dates from the 1980's. It is in -** the public domain. The original comments are included here for -** completeness. They are very out-of-date but might be useful as -** an historical reference. Most of the "enhancements" have been backed -** out so that the functionality is now the same as standard printf(). -** -************************************************************************** -** -** This file contains code for a set of "printf"-like routines. These -** routines format strings much like the printf() from the standard C -** library, though the implementation here has enhancements to support -** SQLlite. -*/ - -/* -** Conversion types fall into various categories as defined by the -** following enumeration. -*/ -#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */ -#define etFLOAT 2 /* Floating point. %f */ -#define etEXP 3 /* Exponentional notation. %e and %E */ -#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */ -#define etSIZE 5 /* Return number of characters processed so far. %n */ -#define etSTRING 6 /* Strings. %s */ -#define etDYNSTRING 7 /* Dynamically allocated strings. %z */ -#define etPERCENT 8 /* Percent symbol. %% */ -#define etCHARX 9 /* Characters. %c */ -/* The rest are extensions, not normally found in printf() */ -#define etSQLESCAPE 10 /* Strings with '\'' doubled. %q */ -#define etSQLESCAPE2 11 /* Strings with '\'' doubled and enclosed in '', - NULL pointers replaced by SQL NULL. %Q */ -#define etTOKEN 12 /* a pointer to a Token structure */ -#define etSRCLIST 13 /* a pointer to a SrcList */ -#define etPOINTER 14 /* The %p conversion */ -#define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */ -#define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ - -#define etINVALID 0 /* Any unrecognized conversion type */ - - -/* -** An "etByte" is an 8-bit unsigned value. -*/ -typedef unsigned char etByte; - -/* -** Each builtin conversion character (ex: the 'd' in "%d") is described -** by an instance of the following structure -*/ -typedef struct et_info { /* Information about each format field */ - char fmttype; /* The format field code letter */ - etByte base; /* The base for radix conversion */ - etByte flags; /* One or more of FLAG_ constants below */ - etByte type; /* Conversion paradigm */ - etByte charset; /* Offset into aDigits[] of the digits string */ - etByte prefix; /* Offset into aPrefix[] of the prefix string */ -} et_info; - -/* -** Allowed values for et_info.flags -*/ -#define FLAG_SIGNED 1 /* True if the value to convert is signed */ -#define FLAG_INTERN 2 /* True if for internal use only */ -#define FLAG_STRING 4 /* Allow infinity precision */ - - -/* -** The following table is searched linearly, so it is good to put the -** most frequently used conversion types first. -*/ -static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; -static const char aPrefix[] = "-x0\000X0"; -static const et_info fmtinfo[] = { - { 'd', 10, 1, etRADIX, 0, 0 }, - { 's', 0, 4, etSTRING, 0, 0 }, - { 'g', 0, 1, etGENERIC, 30, 0 }, - { 'z', 0, 4, etDYNSTRING, 0, 0 }, - { 'q', 0, 4, etSQLESCAPE, 0, 0 }, - { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, - { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, - { 'c', 0, 0, etCHARX, 0, 0 }, - { 'o', 8, 0, etRADIX, 0, 2 }, - { 'u', 10, 0, etRADIX, 0, 0 }, - { 'x', 16, 0, etRADIX, 16, 1 }, - { 'X', 16, 0, etRADIX, 0, 4 }, -#ifndef SQLITE_OMIT_FLOATING_POINT - { 'f', 0, 1, etFLOAT, 0, 0 }, - { 'e', 0, 1, etEXP, 30, 0 }, - { 'E', 0, 1, etEXP, 14, 0 }, - { 'G', 0, 1, etGENERIC, 14, 0 }, -#endif - { 'i', 10, 1, etRADIX, 0, 0 }, - { 'n', 0, 0, etSIZE, 0, 0 }, - { '%', 0, 0, etPERCENT, 0, 0 }, - { 'p', 16, 0, etPOINTER, 0, 1 }, - -/* All the rest have the FLAG_INTERN bit set and are thus for internal -** use only */ - { 'T', 0, 2, etTOKEN, 0, 0 }, - { 'S', 0, 2, etSRCLIST, 0, 0 }, - { 'r', 10, 3, etORDINAL, 0, 0 }, -}; - -/* -** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point -** conversions will work. -*/ -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** "*val" is a double such that 0.1 <= *val < 10.0 -** Return the ascii code for the leading digit of *val, then -** multiply "*val" by 10.0 to renormalize. -** -** Example: -** input: *val = 3.14159 -** output: *val = 1.4159 function return = '3' -** -** The counter *cnt is incremented each time. After counter exceeds -** 16 (the number of significant digits in a 64-bit float) '0' is -** always returned. -*/ -static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ - int digit; - LONGDOUBLE_TYPE d; - if( (*cnt)<=0 ) return '0'; - (*cnt)--; - digit = (int)*val; - d = digit; - digit += '0'; - *val = (*val - d)*10.0; - return (char)digit; -} -#endif /* SQLITE_OMIT_FLOATING_POINT */ - -/* -** Append N space characters to the given string buffer. -*/ -SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum *pAccum, int N){ - static const char zSpaces[] = " "; - while( N>=(int)sizeof(zSpaces)-1 ){ - sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1); - N -= sizeof(zSpaces)-1; - } - if( N>0 ){ - sqlite3StrAccumAppend(pAccum, zSpaces, N); - } -} - -/* -** On machines with a small stack size, you can redefine the -** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired. -*/ -#ifndef SQLITE_PRINT_BUF_SIZE -# define SQLITE_PRINT_BUF_SIZE 70 -#endif -#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */ - -/* -** Render a string given by "fmt" into the StrAccum object. -*/ -SQLITE_PRIVATE void sqlite3VXPrintf( - StrAccum *pAccum, /* Accumulate results here */ - int useExtended, /* Allow extended %-conversions */ - const char *fmt, /* Format string */ - va_list ap /* arguments */ -){ - int c; /* Next character in the format string */ - char *bufpt; /* Pointer to the conversion buffer */ - int precision; /* Precision of the current field */ - int length; /* Length of the field */ - int idx; /* A general purpose loop counter */ - int width; /* Width of the current field */ - etByte flag_leftjustify; /* True if "-" flag is present */ - etByte flag_plussign; /* True if "+" flag is present */ - etByte flag_blanksign; /* True if " " flag is present */ - etByte flag_alternateform; /* True if "#" flag is present */ - etByte flag_altform2; /* True if "!" flag is present */ - etByte flag_zeropad; /* True if field width constant starts with zero */ - etByte flag_long; /* True if "l" flag is present */ - etByte flag_longlong; /* True if the "ll" flag is present */ - etByte done; /* Loop termination flag */ - etByte xtype = 0; /* Conversion paradigm */ - char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ - sqlite_uint64 longvalue; /* Value for integer types */ - LONGDOUBLE_TYPE realvalue; /* Value for real types */ - const et_info *infop; /* Pointer to the appropriate info structure */ - char *zOut; /* Rendering buffer */ - int nOut; /* Size of the rendering buffer */ - char *zExtra; /* Malloced memory used by some conversion */ -#ifndef SQLITE_OMIT_FLOATING_POINT - int exp, e2; /* exponent of real numbers */ - int nsd; /* Number of significant digits returned */ - double rounder; /* Used for rounding floating point values */ - etByte flag_dp; /* True if decimal point should be shown */ - etByte flag_rtz; /* True if trailing zeros should be removed */ -#endif - char buf[etBUFSIZE]; /* Conversion buffer */ - - bufpt = 0; - for(; (c=(*fmt))!=0; ++fmt){ - if( c!='%' ){ - int amt; - bufpt = (char *)fmt; - amt = 1; - while( (c=(*++fmt))!='%' && c!=0 ) amt++; - sqlite3StrAccumAppend(pAccum, bufpt, amt); - if( c==0 ) break; - } - if( (c=(*++fmt))==0 ){ - sqlite3StrAccumAppend(pAccum, "%", 1); - break; - } - /* Find out what flags are present */ - flag_leftjustify = flag_plussign = flag_blanksign = - flag_alternateform = flag_altform2 = flag_zeropad = 0; - done = 0; - do{ - switch( c ){ - case '-': flag_leftjustify = 1; break; - case '+': flag_plussign = 1; break; - case ' ': flag_blanksign = 1; break; - case '#': flag_alternateform = 1; break; - case '!': flag_altform2 = 1; break; - case '0': flag_zeropad = 1; break; - default: done = 1; break; - } - }while( !done && (c=(*++fmt))!=0 ); - /* Get the field width */ - width = 0; - if( c=='*' ){ - width = va_arg(ap,int); - if( width<0 ){ - flag_leftjustify = 1; - width = -width; - } - c = *++fmt; - }else{ - while( c>='0' && c<='9' ){ - width = width*10 + c - '0'; - c = *++fmt; - } - } - /* Get the precision */ - if( c=='.' ){ - precision = 0; - c = *++fmt; - if( c=='*' ){ - precision = va_arg(ap,int); - if( precision<0 ) precision = -precision; - c = *++fmt; - }else{ - while( c>='0' && c<='9' ){ - precision = precision*10 + c - '0'; - c = *++fmt; - } - } - }else{ - precision = -1; - } - /* Get the conversion type modifier */ - if( c=='l' ){ - flag_long = 1; - c = *++fmt; - if( c=='l' ){ - flag_longlong = 1; - c = *++fmt; - }else{ - flag_longlong = 0; - } - }else{ - flag_long = flag_longlong = 0; - } - /* Fetch the info entry for the field */ - infop = &fmtinfo[0]; - xtype = etINVALID; - for(idx=0; idxflags & FLAG_INTERN)==0 ){ - xtype = infop->type; - }else{ - return; - } - break; - } - } - zExtra = 0; - - /* - ** At this point, variables are initialized as follows: - ** - ** flag_alternateform TRUE if a '#' is present. - ** flag_altform2 TRUE if a '!' is present. - ** flag_plussign TRUE if a '+' is present. - ** flag_leftjustify TRUE if a '-' is present or if the - ** field width was negative. - ** flag_zeropad TRUE if the width began with 0. - ** flag_long TRUE if the letter 'l' (ell) prefixed - ** the conversion character. - ** flag_longlong TRUE if the letter 'll' (ell ell) prefixed - ** the conversion character. - ** flag_blanksign TRUE if a ' ' is present. - ** width The specified field width. This is - ** always non-negative. Zero is the default. - ** precision The specified precision. The default - ** is -1. - ** xtype The class of the conversion. - ** infop Pointer to the appropriate info struct. - */ - switch( xtype ){ - case etPOINTER: - flag_longlong = sizeof(char*)==sizeof(i64); - flag_long = sizeof(char*)==sizeof(long int); - /* Fall through into the next case */ - case etORDINAL: - case etRADIX: - if( infop->flags & FLAG_SIGNED ){ - i64 v; - if( flag_longlong ){ - v = va_arg(ap,i64); - }else if( flag_long ){ - v = va_arg(ap,long int); - }else{ - v = va_arg(ap,int); - } - if( v<0 ){ - if( v==SMALLEST_INT64 ){ - longvalue = ((u64)1)<<63; - }else{ - longvalue = -v; - } - prefix = '-'; - }else{ - longvalue = v; - if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - } - }else{ - if( flag_longlong ){ - longvalue = va_arg(ap,u64); - }else if( flag_long ){ - longvalue = va_arg(ap,unsigned long int); - }else{ - longvalue = va_arg(ap,unsigned int); - } - prefix = 0; - } - if( longvalue==0 ) flag_alternateform = 0; - if( flag_zeropad && precisionmallocFailed = 1; - return; - } - } - bufpt = &zOut[nOut-1]; - if( xtype==etORDINAL ){ - static const char zOrd[] = "thstndrd"; - int x = (int)(longvalue % 10); - if( x>=4 || (longvalue/10)%10==1 ){ - x = 0; - } - *(--bufpt) = zOrd[x*2+1]; - *(--bufpt) = zOrd[x*2]; - } - { - register const char *cset; /* Use registers for speed */ - register int base; - cset = &aDigits[infop->charset]; - base = infop->base; - do{ /* Convert to ascii */ - *(--bufpt) = cset[longvalue%base]; - longvalue = longvalue/base; - }while( longvalue>0 ); - } - length = (int)(&zOut[nOut-1]-bufpt); - for(idx=precision-length; idx>0; idx--){ - *(--bufpt) = '0'; /* Zero pad */ - } - if( prefix ) *(--bufpt) = prefix; /* Add sign */ - if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ - const char *pre; - char x; - pre = &aPrefix[infop->prefix]; - for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; - } - length = (int)(&zOut[nOut-1]-bufpt); - break; - case etFLOAT: - case etEXP: - case etGENERIC: - realvalue = va_arg(ap,double); -#ifdef SQLITE_OMIT_FLOATING_POINT - length = 0; -#else - if( precision<0 ) precision = 6; /* Set default precision */ - if( realvalue<0.0 ){ - realvalue = -realvalue; - prefix = '-'; - }else{ - if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - } - if( xtype==etGENERIC && precision>0 ) precision--; -#if 0 - /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */ - for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); -#else - /* It makes more sense to use 0.5 */ - for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){} -#endif - if( xtype==etFLOAT ) realvalue += rounder; - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - exp = 0; - if( sqlite3IsNaN((double)realvalue) ){ - bufpt = "NaN"; - length = 3; - break; - } - if( realvalue>0.0 ){ - LONGDOUBLE_TYPE scale = 1.0; - while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;} - while( realvalue>=1e64*scale && exp<=350 ){ scale *= 1e64; exp+=64; } - while( realvalue>=1e8*scale && exp<=350 ){ scale *= 1e8; exp+=8; } - while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } - realvalue /= scale; - while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } - while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } - if( exp>350 ){ - if( prefix=='-' ){ - bufpt = "-Inf"; - }else if( prefix=='+' ){ - bufpt = "+Inf"; - }else{ - bufpt = "Inf"; - } - length = sqlite3Strlen30(bufpt); - break; - } - } - bufpt = buf; - /* - ** If the field type is etGENERIC, then convert to either etEXP - ** or etFLOAT, as appropriate. - */ - if( xtype!=etFLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } - if( xtype==etGENERIC ){ - flag_rtz = !flag_alternateform; - if( exp<-4 || exp>precision ){ - xtype = etEXP; - }else{ - precision = precision - exp; - xtype = etFLOAT; - } - }else{ - flag_rtz = flag_altform2; - } - if( xtype==etEXP ){ - e2 = 0; - }else{ - e2 = exp; - } - if( e2+precision+width > etBUFSIZE - 15 ){ - bufpt = zExtra = sqlite3Malloc( e2+precision+width+15 ); - if( bufpt==0 ){ - pAccum->mallocFailed = 1; - return; - } - } - zOut = bufpt; - nsd = 16 + flag_altform2*10; - flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; - /* The sign in front of the number */ - if( prefix ){ - *(bufpt++) = prefix; - } - /* Digits prior to the decimal point */ - if( e2<0 ){ - *(bufpt++) = '0'; - }else{ - for(; e2>=0; e2--){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); - } - } - /* The decimal point */ - if( flag_dp ){ - *(bufpt++) = '.'; - } - /* "0" digits after the decimal point but before the first - ** significant digit of the number */ - for(e2++; e2<0; precision--, e2++){ - assert( precision>0 ); - *(bufpt++) = '0'; - } - /* Significant digits after the decimal point */ - while( (precision--)>0 ){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); - } - /* Remove trailing zeros and the "." if no digits follow the "." */ - if( flag_rtz && flag_dp ){ - while( bufpt[-1]=='0' ) *(--bufpt) = 0; - assert( bufpt>zOut ); - if( bufpt[-1]=='.' ){ - if( flag_altform2 ){ - *(bufpt++) = '0'; - }else{ - *(--bufpt) = 0; - } - } - } - /* Add the "eNNN" suffix */ - if( xtype==etEXP ){ - *(bufpt++) = aDigits[infop->charset]; - if( exp<0 ){ - *(bufpt++) = '-'; exp = -exp; - }else{ - *(bufpt++) = '+'; - } - if( exp>=100 ){ - *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */ - exp %= 100; - } - *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */ - *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */ - } - *bufpt = 0; - - /* The converted number is in buf[] and zero terminated. Output it. - ** Note that the number is in the usual order, not reversed as with - ** integer conversions. */ - length = (int)(bufpt-zOut); - bufpt = zOut; - - /* Special case: Add leading zeros if the flag_zeropad flag is - ** set and we are not left justified */ - if( flag_zeropad && !flag_leftjustify && length < width){ - int i; - int nPad = width - length; - for(i=width; i>=nPad; i--){ - bufpt[i] = bufpt[i-nPad]; - } - i = prefix!=0; - while( nPad-- ) bufpt[i++] = '0'; - length = width; - } -#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ - break; - case etSIZE: - *(va_arg(ap,int*)) = pAccum->nChar; - length = width = 0; - break; - case etPERCENT: - buf[0] = '%'; - bufpt = buf; - length = 1; - break; - case etCHARX: - c = va_arg(ap,int); - buf[0] = (char)c; - if( precision>=0 ){ - for(idx=1; idx=0 ){ - for(length=0; lengthetBUFSIZE ){ - bufpt = zExtra = sqlite3Malloc( n ); - if( bufpt==0 ){ - pAccum->mallocFailed = 1; - return; - } - }else{ - bufpt = buf; - } - j = 0; - if( needQuote ) bufpt[j++] = q; - k = i; - for(i=0; i=0 && precisionz, pToken->n); - } - length = width = 0; - break; - } - case etSRCLIST: { - SrcList *pSrc = va_arg(ap, SrcList*); - int k = va_arg(ap, int); - struct SrcList_item *pItem = &pSrc->a[k]; - assert( k>=0 && knSrc ); - if( pItem->zDatabase ){ - sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1); - sqlite3StrAccumAppend(pAccum, ".", 1); - } - sqlite3StrAccumAppend(pAccum, pItem->zName, -1); - length = width = 0; - break; - } - default: { - assert( xtype==etINVALID ); - return; - } - }/* End switch over the format type */ - /* - ** The text of the conversion is pointed to by "bufpt" and is - ** "length" characters long. The field width is "width". Do - ** the output. - */ - if( !flag_leftjustify ){ - register int nspace; - nspace = width-length; - if( nspace>0 ){ - sqlite3AppendSpace(pAccum, nspace); - } - } - if( length>0 ){ - sqlite3StrAccumAppend(pAccum, bufpt, length); - } - if( flag_leftjustify ){ - register int nspace; - nspace = width-length; - if( nspace>0 ){ - sqlite3AppendSpace(pAccum, nspace); - } - } - sqlite3_free(zExtra); - }/* End for loop over the format string */ -} /* End of function */ - -/* -** Append N bytes of text from z to the StrAccum object. -*/ -SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ - assert( z!=0 || N==0 ); - if( p->tooBig | p->mallocFailed ){ - testcase(p->tooBig); - testcase(p->mallocFailed); - return; - } - assert( p->zText!=0 || p->nChar==0 ); - if( N<0 ){ - N = sqlite3Strlen30(z); - } - if( N==0 || NEVER(z==0) ){ - return; - } - if( p->nChar+N >= p->nAlloc ){ - char *zNew; - if( !p->useMalloc ){ - p->tooBig = 1; - N = p->nAlloc - p->nChar - 1; - if( N<=0 ){ - return; - } - }else{ - char *zOld = (p->zText==p->zBase ? 0 : p->zText); - i64 szNew = p->nChar; - szNew += N + 1; - if( szNew > p->mxAlloc ){ - sqlite3StrAccumReset(p); - p->tooBig = 1; - return; - }else{ - p->nAlloc = (int)szNew; - } - if( p->useMalloc==1 ){ - zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); - }else{ - zNew = sqlite3_realloc(zOld, p->nAlloc); - } - if( zNew ){ - if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar); - p->zText = zNew; - }else{ - p->mallocFailed = 1; - sqlite3StrAccumReset(p); - return; - } - } - } - assert( p->zText ); - memcpy(&p->zText[p->nChar], z, N); - p->nChar += N; -} - -/* -** Finish off a string by making sure it is zero-terminated. -** Return a pointer to the resulting string. Return a NULL -** pointer if any kind of error was encountered. -*/ -SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){ - if( p->zText ){ - p->zText[p->nChar] = 0; - if( p->useMalloc && p->zText==p->zBase ){ - if( p->useMalloc==1 ){ - p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); - }else{ - p->zText = sqlite3_malloc(p->nChar+1); - } - if( p->zText ){ - memcpy(p->zText, p->zBase, p->nChar+1); - }else{ - p->mallocFailed = 1; - } - } - } - return p->zText; -} - -/* -** Reset an StrAccum string. Reclaim all malloced memory. -*/ -SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){ - if( p->zText!=p->zBase ){ - if( p->useMalloc==1 ){ - sqlite3DbFree(p->db, p->zText); - }else{ - sqlite3_free(p->zText); - } - } - p->zText = 0; -} - -/* -** Initialize a string accumulator -*/ -SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx){ - p->zText = p->zBase = zBase; - p->db = 0; - p->nChar = 0; - p->nAlloc = n; - p->mxAlloc = mx; - p->useMalloc = 1; - p->tooBig = 0; - p->mallocFailed = 0; -} - -/* -** Print into memory obtained from sqliteMalloc(). Use the internal -** %-conversion extensions. -*/ -SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){ - char *z; - char zBase[SQLITE_PRINT_BUF_SIZE]; - StrAccum acc; - assert( db!=0 ); - sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), - db->aLimit[SQLITE_LIMIT_LENGTH]); - acc.db = db; - sqlite3VXPrintf(&acc, 1, zFormat, ap); - z = sqlite3StrAccumFinish(&acc); - if( acc.mallocFailed ){ - db->mallocFailed = 1; - } - return z; -} - -/* -** Print into memory obtained from sqliteMalloc(). Use the internal -** %-conversion extensions. -*/ -SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){ - va_list ap; - char *z; - va_start(ap, zFormat); - z = sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - return z; -} - -/* -** Like sqlite3MPrintf(), but call sqlite3DbFree() on zStr after formatting -** the string and before returnning. This routine is intended to be used -** to modify an existing string. For example: -** -** x = sqlite3MPrintf(db, x, "prefix %s suffix", x); -** -*/ -SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3 *db, char *zStr, const char *zFormat, ...){ - va_list ap; - char *z; - va_start(ap, zFormat); - z = sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - sqlite3DbFree(db, zStr); - return z; -} - -/* -** Print into memory obtained from sqlite3_malloc(). Omit the internal -** %-conversion extensions. -*/ -SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){ - char *z; - char zBase[SQLITE_PRINT_BUF_SIZE]; - StrAccum acc; -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlite3_initialize() ) return 0; -#endif - sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), SQLITE_MAX_LENGTH); - acc.useMalloc = 2; - sqlite3VXPrintf(&acc, 0, zFormat, ap); - z = sqlite3StrAccumFinish(&acc); - return z; -} - -/* -** Print into memory obtained from sqlite3_malloc()(). Omit the internal -** %-conversion extensions. -*/ -SQLITE_API char *sqlite3_mprintf(const char *zFormat, ...){ - va_list ap; - char *z; -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlite3_initialize() ) return 0; -#endif - va_start(ap, zFormat); - z = sqlite3_vmprintf(zFormat, ap); - va_end(ap); - return z; -} - -/* -** sqlite3_snprintf() works like snprintf() except that it ignores the -** current locale settings. This is important for SQLite because we -** are not able to use a "," as the decimal point in place of "." as -** specified by some locales. -** -** Oops: The first two arguments of sqlite3_snprintf() are backwards -** from the snprintf() standard. Unfortunately, it is too late to change -** this without breaking compatibility, so we just have to live with the -** mistake. -** -** sqlite3_vsnprintf() is the varargs version. -*/ -SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){ - StrAccum acc; - if( n<=0 ) return zBuf; - sqlite3StrAccumInit(&acc, zBuf, n, 0); - acc.useMalloc = 0; - sqlite3VXPrintf(&acc, 0, zFormat, ap); - return sqlite3StrAccumFinish(&acc); -} -SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ - char *z; - va_list ap; - va_start(ap,zFormat); - z = sqlite3_vsnprintf(n, zBuf, zFormat, ap); - va_end(ap); - return z; -} - -/* -** This is the routine that actually formats the sqlite3_log() message. -** We house it in a separate routine from sqlite3_log() to avoid using -** stack space on small-stack systems when logging is disabled. -** -** sqlite3_log() must render into a static buffer. It cannot dynamically -** allocate memory because it might be called while the memory allocator -** mutex is held. -*/ -static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ - StrAccum acc; /* String accumulator */ - char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ - - sqlite3StrAccumInit(&acc, zMsg, sizeof(zMsg), 0); - acc.useMalloc = 0; - sqlite3VXPrintf(&acc, 0, zFormat, ap); - sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode, - sqlite3StrAccumFinish(&acc)); -} - -/* -** Format and write a message to the log if logging is enabled. -*/ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...){ - va_list ap; /* Vararg list */ - if( sqlite3GlobalConfig.xLog ){ - va_start(ap, zFormat); - renderLogMsg(iErrCode, zFormat, ap); - va_end(ap); - } -} - -#if defined(SQLITE_DEBUG) -/* -** A version of printf() that understands %lld. Used for debugging. -** The printf() built into some versions of windows does not understand %lld -** and segfaults if you give it a long long int. -*/ -SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ - va_list ap; - StrAccum acc; - char zBuf[500]; - sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf), 0); - acc.useMalloc = 0; - va_start(ap,zFormat); - sqlite3VXPrintf(&acc, 0, zFormat, ap); - va_end(ap); - sqlite3StrAccumFinish(&acc); - fprintf(stdout,"%s", zBuf); - fflush(stdout); -} -#endif - -#ifndef SQLITE_OMIT_TRACE -/* -** variable-argument wrapper around sqlite3VXPrintf(). -*/ -SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){ - va_list ap; - va_start(ap,zFormat); - sqlite3VXPrintf(p, 1, zFormat, ap); - va_end(ap); -} -#endif - -/************** End of printf.c **********************************************/ -/************** Begin file random.c ******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code to implement a pseudo-random number -** generator (PRNG) for SQLite. -** -** Random numbers are used by some of the database backends in order -** to generate random integer keys for tables or random filenames. -*/ - - -/* All threads share a single random number generator. -** This structure is the current state of the generator. -*/ -static SQLITE_WSD struct sqlite3PrngType { - unsigned char isInit; /* True if initialized */ - unsigned char i, j; /* State variables */ - unsigned char s[256]; /* State variables */ -} sqlite3Prng; - -/* -** Get a single 8-bit random value from the RC4 PRNG. The Mutex -** must be held while executing this routine. -** -** Why not just use a library random generator like lrand48() for this? -** Because the OP_NewRowid opcode in the VDBE depends on having a very -** good source of random numbers. The lrand48() library function may -** well be good enough. But maybe not. Or maybe lrand48() has some -** subtle problems on some systems that could cause problems. It is hard -** to know. To minimize the risk of problems due to bad lrand48() -** implementations, SQLite uses this random number generator based -** on RC4, which we know works very well. -** -** (Later): Actually, OP_NewRowid does not depend on a good source of -** randomness any more. But we will leave this code in all the same. -*/ -static u8 randomByte(void){ - unsigned char t; - - - /* The "wsdPrng" macro will resolve to the pseudo-random number generator - ** state vector. If writable static data is unsupported on the target, - ** we have to locate the state vector at run-time. In the more common - ** case where writable static data is supported, wsdPrng can refer directly - ** to the "sqlite3Prng" state vector declared above. - */ -#ifdef SQLITE_OMIT_WSD - struct sqlite3PrngType *p = &GLOBAL(struct sqlite3PrngType, sqlite3Prng); -# define wsdPrng p[0] -#else -# define wsdPrng sqlite3Prng -#endif - - - /* Initialize the state of the random number generator once, - ** the first time this routine is called. The seed value does - ** not need to contain a lot of randomness since we are not - ** trying to do secure encryption or anything like that... - ** - ** Nothing in this file or anywhere else in SQLite does any kind of - ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random - ** number generator) not as an encryption device. - */ - if( !wsdPrng.isInit ){ - int i; - char k[256]; - wsdPrng.j = 0; - wsdPrng.i = 0; - sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k); - for(i=0; i<256; i++){ - wsdPrng.s[i] = (u8)i; - } - for(i=0; i<256; i++){ - wsdPrng.j += wsdPrng.s[i] + k[i]; - t = wsdPrng.s[wsdPrng.j]; - wsdPrng.s[wsdPrng.j] = wsdPrng.s[i]; - wsdPrng.s[i] = t; - } - wsdPrng.isInit = 1; - } - - /* Generate and return single random byte - */ - wsdPrng.i++; - t = wsdPrng.s[wsdPrng.i]; - wsdPrng.j += t; - wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j]; - wsdPrng.s[wsdPrng.j] = t; - t += wsdPrng.s[wsdPrng.i]; - return wsdPrng.s[t]; -} - -/* -** Return N random bytes. -*/ -SQLITE_API void sqlite3_randomness(int N, void *pBuf){ - unsigned char *zBuf = pBuf; -#if SQLITE_THREADSAFE - sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG); -#endif - sqlite3_mutex_enter(mutex); - while( N-- ){ - *(zBuf++) = randomByte(); - } - sqlite3_mutex_leave(mutex); -} - -#ifndef SQLITE_OMIT_BUILTIN_TEST -/* -** For testing purposes, we sometimes want to preserve the state of -** PRNG and restore the PRNG to its saved state at a later time, or -** to reset the PRNG to its initial state. These routines accomplish -** those tasks. -** -** The sqlite3_test_control() interface calls these routines to -** control the PRNG. -*/ -static SQLITE_WSD struct sqlite3PrngType sqlite3SavedPrng; -SQLITE_PRIVATE void sqlite3PrngSaveState(void){ - memcpy( - &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng), - &GLOBAL(struct sqlite3PrngType, sqlite3Prng), - sizeof(sqlite3Prng) - ); -} -SQLITE_PRIVATE void sqlite3PrngRestoreState(void){ - memcpy( - &GLOBAL(struct sqlite3PrngType, sqlite3Prng), - &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng), - sizeof(sqlite3Prng) - ); -} -SQLITE_PRIVATE void sqlite3PrngResetState(void){ - GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0; -} -#endif /* SQLITE_OMIT_BUILTIN_TEST */ - -/************** End of random.c **********************************************/ -/************** Begin file utf.c *********************************************/ -/* -** 2004 April 13 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains routines used to translate between UTF-8, -** UTF-16, UTF-16BE, and UTF-16LE. -** -** Notes on UTF-8: -** -** Byte-0 Byte-1 Byte-2 Byte-3 Value -** 0xxxxxxx 00000000 00000000 0xxxxxxx -** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx -** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx -** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx -** -** -** Notes on UTF-16: (with wwww+1==uuuuu) -** -** Word-0 Word-1 Value -** 110110ww wwzzzzyy 110111yy yyxxxxxx 000uuuuu zzzzyyyy yyxxxxxx -** zzzzyyyy yyxxxxxx 00000000 zzzzyyyy yyxxxxxx -** -** -** BOM or Byte Order Mark: -** 0xff 0xfe little-endian utf-16 follows -** 0xfe 0xff big-endian utf-16 follows -** -*/ -/* #include */ - -#ifndef SQLITE_AMALGAMATION -/* -** The following constant value is used by the SQLITE_BIGENDIAN and -** SQLITE_LITTLEENDIAN macros. -*/ -SQLITE_PRIVATE const int sqlite3one = 1; -#endif /* SQLITE_AMALGAMATION */ - -/* -** This lookup table is used to help decode the first byte of -** a multi-byte UTF8 character. -*/ -static const unsigned char sqlite3Utf8Trans1[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, -}; - - -#define WRITE_UTF8(zOut, c) { \ - if( c<0x00080 ){ \ - *zOut++ = (u8)(c&0xFF); \ - } \ - else if( c<0x00800 ){ \ - *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \ - *zOut++ = 0x80 + (u8)(c & 0x3F); \ - } \ - else if( c<0x10000 ){ \ - *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \ - *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (u8)(c & 0x3F); \ - }else{ \ - *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \ - *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \ - *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (u8)(c & 0x3F); \ - } \ -} - -#define WRITE_UTF16LE(zOut, c) { \ - if( c<=0xFFFF ){ \ - *zOut++ = (u8)(c&0x00FF); \ - *zOut++ = (u8)((c>>8)&0x00FF); \ - }else{ \ - *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ - *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \ - *zOut++ = (u8)(c&0x00FF); \ - *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ - } \ -} - -#define WRITE_UTF16BE(zOut, c) { \ - if( c<=0xFFFF ){ \ - *zOut++ = (u8)((c>>8)&0x00FF); \ - *zOut++ = (u8)(c&0x00FF); \ - }else{ \ - *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \ - *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ - *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ - *zOut++ = (u8)(c&0x00FF); \ - } \ -} - -#define READ_UTF16LE(zIn, TERM, c){ \ - c = (*zIn++); \ - c += ((*zIn++)<<8); \ - if( c>=0xD800 && c<0xE000 && TERM ){ \ - int c2 = (*zIn++); \ - c2 += ((*zIn++)<<8); \ - c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ - } \ -} - -#define READ_UTF16BE(zIn, TERM, c){ \ - c = ((*zIn++)<<8); \ - c += (*zIn++); \ - if( c>=0xD800 && c<0xE000 && TERM ){ \ - int c2 = ((*zIn++)<<8); \ - c2 += (*zIn++); \ - c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ - } \ -} - -/* -** Translate a single UTF-8 character. Return the unicode value. -** -** During translation, assume that the byte that zTerm points -** is a 0x00. -** -** Write a pointer to the next unread byte back into *pzNext. -** -** Notes On Invalid UTF-8: -** -** * This routine never allows a 7-bit character (0x00 through 0x7f) to -** be encoded as a multi-byte character. Any multi-byte character that -** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. -** -** * This routine never allows a UTF16 surrogate value to be encoded. -** If a multi-byte character attempts to encode a value between -** 0xd800 and 0xe000 then it is rendered as 0xfffd. -** -** * Bytes in the range of 0x80 through 0xbf which occur as the first -** byte of a character are interpreted as single-byte characters -** and rendered as themselves even though they are technically -** invalid characters. -** -** * This routine accepts an infinite number of different UTF8 encodings -** for unicode values 0x80 and greater. It do not change over-length -** encodings to 0xfffd as some systems recommend. -*/ -#define READ_UTF8(zIn, zTerm, c) \ - c = *(zIn++); \ - if( c>=0xc0 ){ \ - c = sqlite3Utf8Trans1[c-0xc0]; \ - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ - c = (c<<6) + (0x3f & *(zIn++)); \ - } \ - if( c<0x80 \ - || (c&0xFFFFF800)==0xD800 \ - || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ - } -SQLITE_PRIVATE u32 sqlite3Utf8Read( - const unsigned char **pz /* Pointer to string from which to read char */ -){ - unsigned int c; - - /* Same as READ_UTF8() above but without the zTerm parameter. - ** For this routine, we assume the UTF8 string is always zero-terminated. - */ - c = *((*pz)++); - if( c>=0xc0 ){ - c = sqlite3Utf8Trans1[c-0xc0]; - while( (*(*pz) & 0xc0)==0x80 ){ - c = (c<<6) + (0x3f & *((*pz)++)); - } - if( c<0x80 - || (c&0xFFFFF800)==0xD800 - || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } - } - return c; -} - - - - -/* -** If the TRANSLATE_TRACE macro is defined, the value of each Mem is -** printed on stderr on the way into and out of sqlite3VdbeMemTranslate(). -*/ -/* #define TRANSLATE_TRACE 1 */ - -#ifndef SQLITE_OMIT_UTF16 -/* -** This routine transforms the internal text encoding used by pMem to -** desiredEnc. It is an error if the string is already of the desired -** encoding, or if *pMem does not contain a string value. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ - int len; /* Maximum length of output string in bytes */ - unsigned char *zOut; /* Output buffer */ - unsigned char *zIn; /* Input iterator */ - unsigned char *zTerm; /* End of input */ - unsigned char *z; /* Output iterator */ - unsigned int c; - - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( pMem->flags&MEM_Str ); - assert( pMem->enc!=desiredEnc ); - assert( pMem->enc!=0 ); - assert( pMem->n>=0 ); - -#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG) - { - char zBuf[100]; - sqlite3VdbeMemPrettyPrint(pMem, zBuf); - fprintf(stderr, "INPUT: %s\n", zBuf); - } -#endif - - /* If the translation is between UTF-16 little and big endian, then - ** all that is required is to swap the byte order. This case is handled - ** differently from the others. - */ - if( pMem->enc!=SQLITE_UTF8 && desiredEnc!=SQLITE_UTF8 ){ - u8 temp; - int rc; - rc = sqlite3VdbeMemMakeWriteable(pMem); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_NOMEM ); - return SQLITE_NOMEM; - } - zIn = (u8*)pMem->z; - zTerm = &zIn[pMem->n&~1]; - while( zInenc = desiredEnc; - goto translate_out; - } - - /* Set len to the maximum number of bytes required in the output buffer. */ - if( desiredEnc==SQLITE_UTF8 ){ - /* When converting from UTF-16, the maximum growth results from - ** translating a 2-byte character to a 4-byte UTF-8 character. - ** A single byte is required for the output string - ** nul-terminator. - */ - pMem->n &= ~1; - len = pMem->n * 2 + 1; - }else{ - /* When converting from UTF-8 to UTF-16 the maximum growth is caused - ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16 - ** character. Two bytes are required in the output buffer for the - ** nul-terminator. - */ - len = pMem->n * 2 + 2; - } - - /* Set zIn to point at the start of the input buffer and zTerm to point 1 - ** byte past the end. - ** - ** Variable zOut is set to point at the output buffer, space obtained - ** from sqlite3_malloc(). - */ - zIn = (u8*)pMem->z; - zTerm = &zIn[pMem->n]; - zOut = sqlite3DbMallocRaw(pMem->db, len); - if( !zOut ){ - return SQLITE_NOMEM; - } - z = zOut; - - if( pMem->enc==SQLITE_UTF8 ){ - if( desiredEnc==SQLITE_UTF16LE ){ - /* UTF-8 -> UTF-16 Little-endian */ - while( zIn UTF-16 Big-endian */ - while( zInn = (int)(z - zOut); - *z++ = 0; - }else{ - assert( desiredEnc==SQLITE_UTF8 ); - if( pMem->enc==SQLITE_UTF16LE ){ - /* UTF-16 Little-endian -> UTF-8 */ - while( zIn UTF-8 */ - while( zInn = (int)(z - zOut); - } - *z = 0; - assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len ); - - sqlite3VdbeMemRelease(pMem); - pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem); - pMem->enc = desiredEnc; - pMem->flags |= (MEM_Term|MEM_Dyn); - pMem->z = (char*)zOut; - pMem->zMalloc = pMem->z; - -translate_out: -#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG) - { - char zBuf[100]; - sqlite3VdbeMemPrettyPrint(pMem, zBuf); - fprintf(stderr, "OUTPUT: %s\n", zBuf); - } -#endif - return SQLITE_OK; -} - -/* -** This routine checks for a byte-order mark at the beginning of the -** UTF-16 string stored in *pMem. If one is present, it is removed and -** the encoding of the Mem adjusted. This routine does not do any -** byte-swapping, it just sets Mem.enc appropriately. -** -** The allocation (static, dynamic etc.) and encoding of the Mem may be -** changed by this function. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem){ - int rc = SQLITE_OK; - u8 bom = 0; - - assert( pMem->n>=0 ); - if( pMem->n>1 ){ - u8 b1 = *(u8 *)pMem->z; - u8 b2 = *(((u8 *)pMem->z) + 1); - if( b1==0xFE && b2==0xFF ){ - bom = SQLITE_UTF16BE; - } - if( b1==0xFF && b2==0xFE ){ - bom = SQLITE_UTF16LE; - } - } - - if( bom ){ - rc = sqlite3VdbeMemMakeWriteable(pMem); - if( rc==SQLITE_OK ){ - pMem->n -= 2; - memmove(pMem->z, &pMem->z[2], pMem->n); - pMem->z[pMem->n] = '\0'; - pMem->z[pMem->n+1] = '\0'; - pMem->flags |= MEM_Term; - pMem->enc = bom; - } - } - return rc; -} -#endif /* SQLITE_OMIT_UTF16 */ - -/* -** pZ is a UTF-8 encoded unicode string. If nByte is less than zero, -** return the number of unicode characters in pZ up to (but not including) -** the first 0x00 byte. If nByte is not less than zero, return the -** number of unicode characters in the first nByte of pZ (or up to -** the first 0x00, whichever comes first). -*/ -SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *zIn, int nByte){ - int r = 0; - const u8 *z = (const u8*)zIn; - const u8 *zTerm; - if( nByte>=0 ){ - zTerm = &z[nByte]; - }else{ - zTerm = (const u8*)(-1); - } - assert( z<=zTerm ); - while( *z!=0 && zmallocFailed ){ - sqlite3VdbeMemRelease(&m); - m.z = 0; - } - assert( (m.flags & MEM_Term)!=0 || db->mallocFailed ); - assert( (m.flags & MEM_Str)!=0 || db->mallocFailed ); - assert( (m.flags & MEM_Dyn)!=0 || db->mallocFailed ); - assert( m.z || db->mallocFailed ); - return m.z; -} - -/* -** Convert a UTF-8 string to the UTF-16 encoding specified by parameter -** enc. A pointer to the new string is returned, and the value of *pnOut -** is set to the length of the returned string in bytes. The call should -** arrange to call sqlite3DbFree() on the returned pointer when it is -** no longer required. -** -** If a malloc failure occurs, NULL is returned and the db.mallocFailed -** flag set. -*/ -#ifdef SQLITE_ENABLE_STAT3 -SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){ - Mem m; - memset(&m, 0, sizeof(m)); - m.db = db; - sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC); - if( sqlite3VdbeMemTranslate(&m, enc) ){ - assert( db->mallocFailed ); - return 0; - } - assert( m.z==m.zMalloc ); - *pnOut = m.n; - return m.z; -} -#endif - -/* -** zIn is a UTF-16 encoded unicode string at least nChar characters long. -** Return the number of bytes in the first nChar unicode characters -** in pZ. nChar must be non-negative. -*/ -SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nChar){ - int c; - unsigned char const *z = zIn; - int n = 0; - - if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){ - while( n0 && n<=4 ); - z[0] = 0; - z = zBuf; - c = sqlite3Utf8Read((const u8**)&z); - t = i; - if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD; - if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD; - assert( c==t ); - assert( (z-zBuf)==n ); - } - for(i=0; i<0x00110000; i++){ - if( i>=0xD800 && i<0xE000 ) continue; - z = zBuf; - WRITE_UTF16LE(z, i); - n = (int)(z-zBuf); - assert( n>0 && n<=4 ); - z[0] = 0; - z = zBuf; - READ_UTF16LE(z, 1, c); - assert( c==i ); - assert( (z-zBuf)==n ); - } - for(i=0; i<0x00110000; i++){ - if( i>=0xD800 && i<0xE000 ) continue; - z = zBuf; - WRITE_UTF16BE(z, i); - n = (int)(z-zBuf); - assert( n>0 && n<=4 ); - z[0] = 0; - z = zBuf; - READ_UTF16BE(z, 1, c); - assert( c==i ); - assert( (z-zBuf)==n ); - } -} -#endif /* SQLITE_TEST */ -#endif /* SQLITE_OMIT_UTF16 */ - -/************** End of utf.c *************************************************/ -/************** Begin file util.c ********************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Utility functions used throughout sqlite. -** -** This file contains functions for allocating memory, comparing -** strings, and stuff like that. -** -*/ -/* #include */ -#ifdef SQLITE_HAVE_ISNAN -# include -#endif - -/* -** Routine needed to support the testcase() macro. -*/ -#ifdef SQLITE_COVERAGE_TEST -SQLITE_PRIVATE void sqlite3Coverage(int x){ - static unsigned dummy = 0; - dummy += (unsigned)x; -} -#endif - -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** Return true if the floating point value is Not a Number (NaN). -** -** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. -** Otherwise, we have our own implementation that works on most systems. -*/ -SQLITE_PRIVATE int sqlite3IsNaN(double x){ - int rc; /* The value return */ -#if !defined(SQLITE_HAVE_ISNAN) - /* - ** Systems that support the isnan() library function should probably - ** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have - ** found that many systems do not have a working isnan() function so - ** this implementation is provided as an alternative. - ** - ** This NaN test sometimes fails if compiled on GCC with -ffast-math. - ** On the other hand, the use of -ffast-math comes with the following - ** warning: - ** - ** This option [-ffast-math] should never be turned on by any - ** -O option since it can result in incorrect output for programs - ** which depend on an exact implementation of IEEE or ISO - ** rules/specifications for math functions. - ** - ** Under MSVC, this NaN test may fail if compiled with a floating- - ** point precision mode other than /fp:precise. From the MSDN - ** documentation: - ** - ** The compiler [with /fp:precise] will properly handle comparisons - ** involving NaN. For example, x != x evaluates to true if x is NaN - ** ... - */ -#ifdef __FAST_MATH__ -# error SQLite will not work correctly with the -ffast-math option of GCC. -#endif - volatile double y = x; - volatile double z = y; - rc = (y!=z); -#else /* if defined(SQLITE_HAVE_ISNAN) */ - rc = isnan(x); -#endif /* SQLITE_HAVE_ISNAN */ - testcase( rc ); - return rc; -} -#endif /* SQLITE_OMIT_FLOATING_POINT */ - -/* -** Compute a string length that is limited to what can be stored in -** lower 30 bits of a 32-bit signed integer. -** -** The value returned will never be negative. Nor will it ever be greater -** than the actual length of the string. For very long strings (greater -** than 1GiB) the value returned might be less than the true string length. -*/ -SQLITE_PRIVATE int sqlite3Strlen30(const char *z){ - const char *z2 = z; - if( z==0 ) return 0; - while( *z2 ){ z2++; } - return 0x3fffffff & (int)(z2 - z); -} - -/* -** Set the most recent error code and error string for the sqlite -** handle "db". The error code is set to "err_code". -** -** If it is not NULL, string zFormat specifies the format of the -** error string in the style of the printf functions: The following -** format characters are allowed: -** -** %s Insert a string -** %z A string that should be freed after use -** %d Insert an integer -** %T Insert a token -** %S Insert the first element of a SrcList -** -** zFormat and any string tokens that follow it are assumed to be -** encoded in UTF-8. -** -** To clear the most recent error for sqlite handle "db", sqlite3Error -** should be called with err_code set to SQLITE_OK and zFormat set -** to NULL. -*/ -SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){ - if( db && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){ - db->errCode = err_code; - if( zFormat ){ - char *z; - va_list ap; - va_start(ap, zFormat); - z = sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC); - }else{ - sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC); - } - } -} - -/* -** Add an error message to pParse->zErrMsg and increment pParse->nErr. -** The following formatting characters are allowed: -** -** %s Insert a string -** %z A string that should be freed after use -** %d Insert an integer -** %T Insert a token -** %S Insert the first element of a SrcList -** -** This function should be used to report any error that occurs whilst -** compiling an SQL statement (i.e. within sqlite3_prepare()). The -** last thing the sqlite3_prepare() function does is copy the error -** stored by this function into the database handle using sqlite3Error(). -** Function sqlite3Error() should be used during statement execution -** (sqlite3_step() etc.). -*/ -SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ - char *zMsg; - va_list ap; - sqlite3 *db = pParse->db; - va_start(ap, zFormat); - zMsg = sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - if( db->suppressErr ){ - sqlite3DbFree(db, zMsg); - }else{ - pParse->nErr++; - sqlite3DbFree(db, pParse->zErrMsg); - pParse->zErrMsg = zMsg; - pParse->rc = SQLITE_ERROR; - } -} - -/* -** Convert an SQL-style quoted string into a normal string by removing -** the quote characters. The conversion is done in-place. If the -** input does not begin with a quote character, then this routine -** is a no-op. -** -** The input string must be zero-terminated. A new zero-terminator -** is added to the dequoted string. -** -** The return value is -1 if no dequoting occurs or the length of the -** dequoted string, exclusive of the zero terminator, if dequoting does -** occur. -** -** 2002-Feb-14: This routine is extended to remove MS-Access style -** brackets from around identifers. For example: "[a-b-c]" becomes -** "a-b-c". -*/ -SQLITE_PRIVATE int sqlite3Dequote(char *z){ - char quote; - int i, j; - if( z==0 ) return -1; - quote = z[0]; - switch( quote ){ - case '\'': break; - case '"': break; - case '`': break; /* For MySQL compatibility */ - case '[': quote = ']'; break; /* For MS SqlServer compatibility */ - default: return -1; - } - for(i=1, j=0; ALWAYS(z[i]); i++){ - if( z[i]==quote ){ - if( z[i+1]==quote ){ - z[j++] = quote; - i++; - }else{ - break; - } - }else{ - z[j++] = z[i]; - } - } - z[j] = 0; - return j; -} - -/* Convenient short-hand */ -#define UpperToLower sqlite3UpperToLower - -/* -** Some systems have stricmp(). Others have strcasecmp(). Because -** there is no consistency, we will define our own. -** -** IMPLEMENTATION-OF: R-30243-02494 The sqlite3_stricmp() and -** sqlite3_strnicmp() APIs allow applications and extensions to compare -** the contents of two buffers containing UTF-8 strings in a -** case-independent fashion, using the same definition of "case -** independence" that SQLite uses internally when comparing identifiers. -*/ -SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){ - register unsigned char *a, *b; - a = (unsigned char *)zLeft; - b = (unsigned char *)zRight; - while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } - return UpperToLower[*a] - UpperToLower[*b]; -} -SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ - register unsigned char *a, *b; - a = (unsigned char *)zLeft; - b = (unsigned char *)zRight; - while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } - return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; -} - -/* -** The string z[] is an text representation of a real number. -** Convert this string to a double and write it into *pResult. -** -** The string z[] is length bytes in length (bytes, not characters) and -** uses the encoding enc. The string is not necessarily zero-terminated. -** -** Return TRUE if the result is a valid real number (or integer) and FALSE -** if the string is empty or contains extraneous text. Valid numbers -** are in one of these formats: -** -** [+-]digits[E[+-]digits] -** [+-]digits.[digits][E[+-]digits] -** [+-].digits[E[+-]digits] -** -** Leading and trailing whitespace is ignored for the purpose of determining -** validity. -** -** If some prefix of the input string is a valid number, this routine -** returns FALSE but it still converts the prefix and writes the result -** into *pResult. -*/ -SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ -#ifndef SQLITE_OMIT_FLOATING_POINT - int incr; - const char *zEnd = z + length; - /* sign * significand * (10 ^ (esign * exponent)) */ - int sign = 1; /* sign of significand */ - i64 s = 0; /* significand */ - int d = 0; /* adjust exponent for shifting decimal point */ - int esign = 1; /* sign of exponent */ - int e = 0; /* exponent */ - int eValid = 1; /* True exponent is either not used or is well-formed */ - double result; - int nDigits = 0; - int nonNum = 0; - - assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); - *pResult = 0.0; /* Default return value, in case of an error */ - - if( enc==SQLITE_UTF8 ){ - incr = 1; - }else{ - int i; - incr = 2; - assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); - for(i=3-enc; i=zEnd ) return 0; - - /* get sign of significand */ - if( *z=='-' ){ - sign = -1; - z+=incr; - }else if( *z=='+' ){ - z+=incr; - } - - /* skip leading zeroes */ - while( z=zEnd ) goto do_atof_calc; - - /* if decimal point is present */ - if( *z=='.' ){ - z+=incr; - /* copy digits from after decimal to significand - ** (decrease exponent by d to shift decimal right) */ - while( z=zEnd ) goto do_atof_calc; - - /* if exponent is present */ - if( *z=='e' || *z=='E' ){ - z+=incr; - eValid = 0; - if( z>=zEnd ) goto do_atof_calc; - /* get sign of exponent */ - if( *z=='-' ){ - esign = -1; - z+=incr; - }else if( *z=='+' ){ - z+=incr; - } - /* copy digits to exponent */ - while( z0 ){ - while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10; - }else{ - while( !(s%10) && e>0 ) e--,s/=10; - } - - /* adjust the sign of significand */ - s = sign<0 ? -s : s; - - /* if exponent, scale significand as appropriate - ** and store in result. */ - if( e ){ - LONGDOUBLE_TYPE scale = 1.0; - /* attempt to handle extremely small/large numbers better */ - if( e>307 && e<342 ){ - while( e%308 ) { scale *= 1.0e+1; e -= 1; } - if( esign<0 ){ - result = s / scale; - result /= 1.0e+308; - }else{ - result = s * scale; - result *= 1.0e+308; - } - }else if( e>=342 ){ - if( esign<0 ){ - result = 0.0*s; - }else{ - result = 1e308*1e308*s; /* Infinity */ - } - }else{ - /* 1.0e+22 is the largest power of 10 than can be - ** represented exactly. */ - while( e%22 ) { scale *= 1.0e+1; e -= 1; } - while( e>0 ) { scale *= 1.0e+22; e -= 22; } - if( esign<0 ){ - result = s / scale; - }else{ - result = s * scale; - } - } - } else { - result = (double)s; - } - } - - /* store the result */ - *pResult = result; - - /* return true if number and no extra non-whitespace chracters after */ - return z>=zEnd && nDigits>0 && eValid && nonNum==0; -#else - return !sqlite3Atoi64(z, pResult, length, enc); -#endif /* SQLITE_OMIT_FLOATING_POINT */ -} - -/* -** Compare the 19-character string zNum against the text representation -** value 2^63: 9223372036854775808. Return negative, zero, or positive -** if zNum is less than, equal to, or greater than the string. -** Note that zNum must contain exactly 19 characters. -** -** Unlike memcmp() this routine is guaranteed to return the difference -** in the values of the last digit if the only difference is in the -** last digit. So, for example, -** -** compare2pow63("9223372036854775800", 1) -** -** will return -8. -*/ -static int compare2pow63(const char *zNum, int incr){ - int c = 0; - int i; - /* 012345678901234567 */ - const char *pow63 = "922337203685477580"; - for(i=0; c==0 && i<18; i++){ - c = (zNum[i*incr]-pow63[i])*10; - } - if( c==0 ){ - c = zNum[18*incr] - '8'; - testcase( c==(-1) ); - testcase( c==0 ); - testcase( c==(+1) ); - } - return c; -} - - -/* -** Convert zNum to a 64-bit signed integer. -** -** If the zNum value is representable as a 64-bit twos-complement -** integer, then write that value into *pNum and return 0. -** -** If zNum is exactly 9223372036854665808, return 2. This special -** case is broken out because while 9223372036854665808 cannot be a -** signed 64-bit integer, its negative -9223372036854665808 can be. -** -** If zNum is too big for a 64-bit integer and is not -** 9223372036854665808 or if zNum contains any non-numeric text, -** then return 1. -** -** length is the number of bytes in the string (bytes, not characters). -** The string is not necessarily zero-terminated. The encoding is -** given by enc. -*/ -SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ - int incr; - u64 u = 0; - int neg = 0; /* assume positive */ - int i; - int c = 0; - int nonNum = 0; - const char *zStart; - const char *zEnd = zNum + length; - assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); - if( enc==SQLITE_UTF8 ){ - incr = 1; - }else{ - incr = 2; - assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); - for(i=3-enc; i='0' && c<='9'; i+=incr){ - u = u*10 + c - '0'; - } - if( u>LARGEST_INT64 ){ - *pNum = SMALLEST_INT64; - }else if( neg ){ - *pNum = -(i64)u; - }else{ - *pNum = (i64)u; - } - testcase( i==18 ); - testcase( i==19 ); - testcase( i==20 ); - if( (c!=0 && &zNum[i]19*incr || nonNum ){ - /* zNum is empty or contains non-numeric text or is longer - ** than 19 digits (thus guaranteeing that it is too large) */ - return 1; - }else if( i<19*incr ){ - /* Less than 19 digits, so we know that it fits in 64 bits */ - assert( u<=LARGEST_INT64 ); - return 0; - }else{ - /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */ - c = compare2pow63(zNum, incr); - if( c<0 ){ - /* zNum is less than 9223372036854775808 so it fits */ - assert( u<=LARGEST_INT64 ); - return 0; - }else if( c>0 ){ - /* zNum is greater than 9223372036854775808 so it overflows */ - return 1; - }else{ - /* zNum is exactly 9223372036854775808. Fits if negative. The - ** special case 2 overflow if positive */ - assert( u-1==LARGEST_INT64 ); - assert( (*pNum)==SMALLEST_INT64 ); - return neg ? 0 : 2; - } - } -} - -/* -** If zNum represents an integer that will fit in 32-bits, then set -** *pValue to that integer and return true. Otherwise return false. -** -** Any non-numeric characters that following zNum are ignored. -** This is different from sqlite3Atoi64() which requires the -** input number to be zero-terminated. -*/ -SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){ - sqlite_int64 v = 0; - int i, c; - int neg = 0; - if( zNum[0]=='-' ){ - neg = 1; - zNum++; - }else if( zNum[0]=='+' ){ - zNum++; - } - while( zNum[0]=='0' ) zNum++; - for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){ - v = v*10 + c; - } - - /* The longest decimal representation of a 32 bit integer is 10 digits: - ** - ** 1234567890 - ** 2^31 -> 2147483648 - */ - testcase( i==10 ); - if( i>10 ){ - return 0; - } - testcase( v-neg==2147483647 ); - if( v-neg>2147483647 ){ - return 0; - } - if( neg ){ - v = -v; - } - *pValue = (int)v; - return 1; -} - -/* -** Return a 32-bit integer value extracted from a string. If the -** string is not an integer, just return 0. -*/ -SQLITE_PRIVATE int sqlite3Atoi(const char *z){ - int x = 0; - if( z ) sqlite3GetInt32(z, &x); - return x; -} - -/* -** The variable-length integer encoding is as follows: -** -** KEY: -** A = 0xxxxxxx 7 bits of data and one flag bit -** B = 1xxxxxxx 7 bits of data and one flag bit -** C = xxxxxxxx 8 bits of data -** -** 7 bits - A -** 14 bits - BA -** 21 bits - BBA -** 28 bits - BBBA -** 35 bits - BBBBA -** 42 bits - BBBBBA -** 49 bits - BBBBBBA -** 56 bits - BBBBBBBA -** 64 bits - BBBBBBBBC -*/ - -/* -** Write a 64-bit variable-length integer to memory starting at p[0]. -** The length of data write will be between 1 and 9 bytes. The number -** of bytes written is returned. -** -** A variable-length integer consists of the lower 7 bits of each byte -** for all bytes that have the 8th bit set and one byte with the 8th -** bit clear. Except, if we get to the 9th byte, it stores the full -** 8 bits and is the last byte. -*/ -SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){ - int i, j, n; - u8 buf[10]; - if( v & (((u64)0xff000000)<<32) ){ - p[8] = (u8)v; - v >>= 8; - for(i=7; i>=0; i--){ - p[i] = (u8)((v & 0x7f) | 0x80); - v >>= 7; - } - return 9; - } - n = 0; - do{ - buf[n++] = (u8)((v & 0x7f) | 0x80); - v >>= 7; - }while( v!=0 ); - buf[0] &= 0x7f; - assert( n<=9 ); - for(i=0, j=n-1; j>=0; j--, i++){ - p[i] = buf[j]; - } - return n; -} - -/* -** This routine is a faster version of sqlite3PutVarint() that only -** works for 32-bit positive integers and which is optimized for -** the common case of small integers. A MACRO version, putVarint32, -** is provided which inlines the single-byte case. All code should use -** the MACRO version as this function assumes the single-byte case has -** already been handled. -*/ -SQLITE_PRIVATE int sqlite3PutVarint32(unsigned char *p, u32 v){ -#ifndef putVarint32 - if( (v & ~0x7f)==0 ){ - p[0] = v; - return 1; - } -#endif - if( (v & ~0x3fff)==0 ){ - p[0] = (u8)((v>>7) | 0x80); - p[1] = (u8)(v & 0x7f); - return 2; - } - return sqlite3PutVarint(p, v); -} - -/* -** Bitmasks used by sqlite3GetVarint(). These precomputed constants -** are defined here rather than simply putting the constant expressions -** inline in order to work around bugs in the RVT compiler. -** -** SLOT_2_0 A mask for (0x7f<<14) | 0x7f -** -** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0 -*/ -#define SLOT_2_0 0x001fc07f -#define SLOT_4_2_0 0xf01fc07f - - -/* -** Read a 64-bit variable-length integer from memory starting at p[0]. -** Return the number of bytes read. The value is stored in *v. -*/ -SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ - u32 a,b,s; - - a = *p; - /* a: p0 (unmasked) */ - if (!(a&0x80)) - { - *v = a; - return 1; - } - - p++; - b = *p; - /* b: p1 (unmasked) */ - if (!(b&0x80)) - { - a &= 0x7f; - a = a<<7; - a |= b; - *v = a; - return 2; - } - - /* Verify that constants are precomputed correctly */ - assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) ); - assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) ); - - p++; - a = a<<14; - a |= *p; - /* a: p0<<14 | p2 (unmasked) */ - if (!(a&0x80)) - { - a &= SLOT_2_0; - b &= 0x7f; - b = b<<7; - a |= b; - *v = a; - return 3; - } - - /* CSE1 from below */ - a &= SLOT_2_0; - p++; - b = b<<14; - b |= *p; - /* b: p1<<14 | p3 (unmasked) */ - if (!(b&0x80)) - { - b &= SLOT_2_0; - /* moved CSE1 up */ - /* a &= (0x7f<<14)|(0x7f); */ - a = a<<7; - a |= b; - *v = a; - return 4; - } - - /* a: p0<<14 | p2 (masked) */ - /* b: p1<<14 | p3 (unmasked) */ - /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ - /* moved CSE1 up */ - /* a &= (0x7f<<14)|(0x7f); */ - b &= SLOT_2_0; - s = a; - /* s: p0<<14 | p2 (masked) */ - - p++; - a = a<<14; - a |= *p; - /* a: p0<<28 | p2<<14 | p4 (unmasked) */ - if (!(a&0x80)) - { - /* we can skip these cause they were (effectively) done above in calc'ing s */ - /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ - /* b &= (0x7f<<14)|(0x7f); */ - b = b<<7; - a |= b; - s = s>>18; - *v = ((u64)s)<<32 | a; - return 5; - } - - /* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ - s = s<<7; - s |= b; - /* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ - - p++; - b = b<<14; - b |= *p; - /* b: p1<<28 | p3<<14 | p5 (unmasked) */ - if (!(b&0x80)) - { - /* we can skip this cause it was (effectively) done above in calc'ing s */ - /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ - a &= SLOT_2_0; - a = a<<7; - a |= b; - s = s>>18; - *v = ((u64)s)<<32 | a; - return 6; - } - - p++; - a = a<<14; - a |= *p; - /* a: p2<<28 | p4<<14 | p6 (unmasked) */ - if (!(a&0x80)) - { - a &= SLOT_4_2_0; - b &= SLOT_2_0; - b = b<<7; - a |= b; - s = s>>11; - *v = ((u64)s)<<32 | a; - return 7; - } - - /* CSE2 from below */ - a &= SLOT_2_0; - p++; - b = b<<14; - b |= *p; - /* b: p3<<28 | p5<<14 | p7 (unmasked) */ - if (!(b&0x80)) - { - b &= SLOT_4_2_0; - /* moved CSE2 up */ - /* a &= (0x7f<<14)|(0x7f); */ - a = a<<7; - a |= b; - s = s>>4; - *v = ((u64)s)<<32 | a; - return 8; - } - - p++; - a = a<<15; - a |= *p; - /* a: p4<<29 | p6<<15 | p8 (unmasked) */ - - /* moved CSE2 up */ - /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */ - b &= SLOT_2_0; - b = b<<8; - a |= b; - - s = s<<4; - b = p[-4]; - b &= 0x7f; - b = b>>3; - s |= b; - - *v = ((u64)s)<<32 | a; - - return 9; -} - -/* -** Read a 32-bit variable-length integer from memory starting at p[0]. -** Return the number of bytes read. The value is stored in *v. -** -** If the varint stored in p[0] is larger than can fit in a 32-bit unsigned -** integer, then set *v to 0xffffffff. -** -** A MACRO version, getVarint32, is provided which inlines the -** single-byte case. All code should use the MACRO version as -** this function assumes the single-byte case has already been handled. -*/ -SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ - u32 a,b; - - /* The 1-byte case. Overwhelmingly the most common. Handled inline - ** by the getVarin32() macro */ - a = *p; - /* a: p0 (unmasked) */ -#ifndef getVarint32 - if (!(a&0x80)) - { - /* Values between 0 and 127 */ - *v = a; - return 1; - } -#endif - - /* The 2-byte case */ - p++; - b = *p; - /* b: p1 (unmasked) */ - if (!(b&0x80)) - { - /* Values between 128 and 16383 */ - a &= 0x7f; - a = a<<7; - *v = a | b; - return 2; - } - - /* The 3-byte case */ - p++; - a = a<<14; - a |= *p; - /* a: p0<<14 | p2 (unmasked) */ - if (!(a&0x80)) - { - /* Values between 16384 and 2097151 */ - a &= (0x7f<<14)|(0x7f); - b &= 0x7f; - b = b<<7; - *v = a | b; - return 3; - } - - /* A 32-bit varint is used to store size information in btrees. - ** Objects are rarely larger than 2MiB limit of a 3-byte varint. - ** A 3-byte varint is sufficient, for example, to record the size - ** of a 1048569-byte BLOB or string. - ** - ** We only unroll the first 1-, 2-, and 3- byte cases. The very - ** rare larger cases can be handled by the slower 64-bit varint - ** routine. - */ -#if 1 - { - u64 v64; - u8 n; - - p -= 2; - n = sqlite3GetVarint(p, &v64); - assert( n>3 && n<=9 ); - if( (v64 & SQLITE_MAX_U32)!=v64 ){ - *v = 0xffffffff; - }else{ - *v = (u32)v64; - } - return n; - } - -#else - /* For following code (kept for historical record only) shows an - ** unrolling for the 3- and 4-byte varint cases. This code is - ** slightly faster, but it is also larger and much harder to test. - */ - p++; - b = b<<14; - b |= *p; - /* b: p1<<14 | p3 (unmasked) */ - if (!(b&0x80)) - { - /* Values between 2097152 and 268435455 */ - b &= (0x7f<<14)|(0x7f); - a &= (0x7f<<14)|(0x7f); - a = a<<7; - *v = a | b; - return 4; - } - - p++; - a = a<<14; - a |= *p; - /* a: p0<<28 | p2<<14 | p4 (unmasked) */ - if (!(a&0x80)) - { - /* Values between 268435456 and 34359738367 */ - a &= SLOT_4_2_0; - b &= SLOT_4_2_0; - b = b<<7; - *v = a | b; - return 5; - } - - /* We can only reach this point when reading a corrupt database - ** file. In that case we are not in any hurry. Use the (relatively - ** slow) general-purpose sqlite3GetVarint() routine to extract the - ** value. */ - { - u64 v64; - u8 n; - - p -= 4; - n = sqlite3GetVarint(p, &v64); - assert( n>5 && n<=9 ); - *v = (u32)v64; - return n; - } -#endif -} - -/* -** Return the number of bytes that will be needed to store the given -** 64-bit integer. -*/ -SQLITE_PRIVATE int sqlite3VarintLen(u64 v){ - int i = 0; - do{ - i++; - v >>= 7; - }while( v!=0 && ALWAYS(i<9) ); - return i; -} - - -/* -** Read or write a four-byte big-endian integer value. -*/ -SQLITE_PRIVATE u32 sqlite3Get4byte(const u8 *p){ - return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; -} -SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){ - p[0] = (u8)(v>>24); - p[1] = (u8)(v>>16); - p[2] = (u8)(v>>8); - p[3] = (u8)v; -} - - - -/* -** Translate a single byte of Hex into an integer. -** This routine only works if h really is a valid hexadecimal -** character: 0..9a..fA..F -*/ -SQLITE_PRIVATE u8 sqlite3HexToInt(int h){ - assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); -#ifdef SQLITE_ASCII - h += 9*(1&(h>>6)); -#endif -#ifdef SQLITE_EBCDIC - h += 9*(1&~(h>>4)); -#endif - return (u8)(h & 0xf); -} - -#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) -/* -** Convert a BLOB literal of the form "x'hhhhhh'" into its binary -** value. Return a pointer to its binary value. Space to hold the -** binary value has been obtained from malloc and must be freed by -** the calling routine. -*/ -SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){ - char *zBlob; - int i; - - zBlob = (char *)sqlite3DbMallocRaw(db, n/2 + 1); - n--; - if( zBlob ){ - for(i=0; imagic; - if( magic!=SQLITE_MAGIC_OPEN ){ - if( sqlite3SafetyCheckSickOrOk(db) ){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - logBadConnection("unopened"); - } - return 0; - }else{ - return 1; - } -} -SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ - u32 magic; - magic = db->magic; - if( magic!=SQLITE_MAGIC_SICK && - magic!=SQLITE_MAGIC_OPEN && - magic!=SQLITE_MAGIC_BUSY ){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - logBadConnection("invalid"); - return 0; - }else{ - return 1; - } -} - -/* -** Attempt to add, substract, or multiply the 64-bit signed value iB against -** the other 64-bit signed integer at *pA and store the result in *pA. -** Return 0 on success. Or if the operation would have resulted in an -** overflow, leave *pA unchanged and return 1. -*/ -SQLITE_PRIVATE int sqlite3AddInt64(i64 *pA, i64 iB){ - i64 iA = *pA; - testcase( iA==0 ); testcase( iA==1 ); - testcase( iB==-1 ); testcase( iB==0 ); - if( iB>=0 ){ - testcase( iA>0 && LARGEST_INT64 - iA == iB ); - testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); - if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; - *pA += iB; - }else{ - testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); - testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); - if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; - *pA += iB; - } - return 0; -} -SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){ - testcase( iB==SMALLEST_INT64+1 ); - if( iB==SMALLEST_INT64 ){ - testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); - if( (*pA)>=0 ) return 1; - *pA -= iB; - return 0; - }else{ - return sqlite3AddInt64(pA, -iB); - } -} -#define TWOPOWER32 (((i64)1)<<32) -#define TWOPOWER31 (((i64)1)<<31) -SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){ - i64 iA = *pA; - i64 iA1, iA0, iB1, iB0, r; - - iA1 = iA/TWOPOWER32; - iA0 = iA % TWOPOWER32; - iB1 = iB/TWOPOWER32; - iB0 = iB % TWOPOWER32; - if( iA1*iB1 != 0 ) return 1; - assert( iA1*iB0==0 || iA0*iB1==0 ); - r = iA1*iB0 + iA0*iB1; - testcase( r==(-TWOPOWER31)-1 ); - testcase( r==(-TWOPOWER31) ); - testcase( r==TWOPOWER31 ); - testcase( r==TWOPOWER31-1 ); - if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1; - r *= TWOPOWER32; - if( sqlite3AddInt64(&r, iA0*iB0) ) return 1; - *pA = r; - return 0; -} - -/* -** Compute the absolute value of a 32-bit signed integer, of possible. Or -** if the integer has a value of -2147483648, return +2147483647 -*/ -SQLITE_PRIVATE int sqlite3AbsInt32(int x){ - if( x>=0 ) return x; - if( x==(int)0x80000000 ) return 0x7fffffff; - return -x; -} - -#ifdef SQLITE_ENABLE_8_3_NAMES -/* -** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database -** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and -** if filename in z[] has a suffix (a.k.a. "extension") that is longer than -** three characters, then shorten the suffix on z[] to be the last three -** characters of the original suffix. -** -** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always -** do the suffix shortening regardless of URI parameter. -** -** Examples: -** -** test.db-journal => test.nal -** test.db-wal => test.wal -** test.db-shm => test.shm -** test.db-mj7f3319fa => test.9fa -*/ -SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){ -#if SQLITE_ENABLE_8_3_NAMES<2 - if( sqlite3_uri_boolean(zBaseFilename, "8_3_names", 0) ) -#endif - { - int i, sz; - sz = sqlite3Strlen30(z); - for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} - if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4); - } -} -#endif - -/************** End of util.c ************************************************/ -/************** Begin file hash.c ********************************************/ -/* -** 2001 September 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This is the implementation of generic hash-tables -** used in SQLite. -*/ -/* #include */ - -/* Turn bulk memory into a hash table object by initializing the -** fields of the Hash structure. -** -** "pNew" is a pointer to the hash table that is to be initialized. -*/ -SQLITE_PRIVATE void sqlite3HashInit(Hash *pNew){ - assert( pNew!=0 ); - pNew->first = 0; - pNew->count = 0; - pNew->htsize = 0; - pNew->ht = 0; -} - -/* Remove all entries from a hash table. Reclaim all memory. -** Call this routine to delete a hash table or to reset a hash table -** to the empty state. -*/ -SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){ - HashElem *elem; /* For looping over all elements of the table */ - - assert( pH!=0 ); - elem = pH->first; - pH->first = 0; - sqlite3_free(pH->ht); - pH->ht = 0; - pH->htsize = 0; - while( elem ){ - HashElem *next_elem = elem->next; - sqlite3_free(elem); - elem = next_elem; - } - pH->count = 0; -} - -/* -** The hashing function. -*/ -static unsigned int strHash(const char *z, int nKey){ - int h = 0; - assert( nKey>=0 ); - while( nKey > 0 ){ - h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++]; - nKey--; - } - return h; -} - - -/* Link pNew element into the hash table pH. If pEntry!=0 then also -** insert pNew into the pEntry hash bucket. -*/ -static void insertElement( - Hash *pH, /* The complete hash table */ - struct _ht *pEntry, /* The entry into which pNew is inserted */ - HashElem *pNew /* The element to be inserted */ -){ - HashElem *pHead; /* First element already in pEntry */ - if( pEntry ){ - pHead = pEntry->count ? pEntry->chain : 0; - pEntry->count++; - pEntry->chain = pNew; - }else{ - pHead = 0; - } - if( pHead ){ - pNew->next = pHead; - pNew->prev = pHead->prev; - if( pHead->prev ){ pHead->prev->next = pNew; } - else { pH->first = pNew; } - pHead->prev = pNew; - }else{ - pNew->next = pH->first; - if( pH->first ){ pH->first->prev = pNew; } - pNew->prev = 0; - pH->first = pNew; - } -} - - -/* Resize the hash table so that it cantains "new_size" buckets. -** -** The hash table might fail to resize if sqlite3_malloc() fails or -** if the new size is the same as the prior size. -** Return TRUE if the resize occurs and false if not. -*/ -static int rehash(Hash *pH, unsigned int new_size){ - struct _ht *new_ht; /* The new hash table */ - HashElem *elem, *next_elem; /* For looping over existing elements */ - -#if SQLITE_MALLOC_SOFT_LIMIT>0 - if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){ - new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht); - } - if( new_size==pH->htsize ) return 0; -#endif - - /* The inability to allocates space for a larger hash table is - ** a performance hit but it is not a fatal error. So mark the - ** allocation as a benign. Use sqlite3Malloc()/memset(0) instead of - ** sqlite3MallocZero() to make the allocation, as sqlite3MallocZero() - ** only zeroes the requested number of bytes whereas this module will - ** use the actual amount of space allocated for the hash table (which - ** may be larger than the requested amount). - */ - sqlite3BeginBenignMalloc(); - new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) ); - sqlite3EndBenignMalloc(); - - if( new_ht==0 ) return 0; - sqlite3_free(pH->ht); - pH->ht = new_ht; - pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht); - memset(new_ht, 0, new_size*sizeof(struct _ht)); - for(elem=pH->first, pH->first=0; elem; elem = next_elem){ - unsigned int h = strHash(elem->pKey, elem->nKey) % new_size; - next_elem = elem->next; - insertElement(pH, &new_ht[h], elem); - } - return 1; -} - -/* This function (for internal use only) locates an element in an -** hash table that matches the given key. The hash for this key has -** already been computed and is passed as the 4th parameter. -*/ -static HashElem *findElementGivenHash( - const Hash *pH, /* The pH to be searched */ - const char *pKey, /* The key we are searching for */ - int nKey, /* Bytes in key (not counting zero terminator) */ - unsigned int h /* The hash for this key. */ -){ - HashElem *elem; /* Used to loop thru the element list */ - int count; /* Number of elements left to test */ - - if( pH->ht ){ - struct _ht *pEntry = &pH->ht[h]; - elem = pEntry->chain; - count = pEntry->count; - }else{ - elem = pH->first; - count = pH->count; - } - while( count-- && ALWAYS(elem) ){ - if( elem->nKey==nKey && sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){ - return elem; - } - elem = elem->next; - } - return 0; -} - -/* Remove a single entry from the hash table given a pointer to that -** element and a hash on the element's key. -*/ -static void removeElementGivenHash( - Hash *pH, /* The pH containing "elem" */ - HashElem* elem, /* The element to be removed from the pH */ - unsigned int h /* Hash value for the element */ -){ - struct _ht *pEntry; - if( elem->prev ){ - elem->prev->next = elem->next; - }else{ - pH->first = elem->next; - } - if( elem->next ){ - elem->next->prev = elem->prev; - } - if( pH->ht ){ - pEntry = &pH->ht[h]; - if( pEntry->chain==elem ){ - pEntry->chain = elem->next; - } - pEntry->count--; - assert( pEntry->count>=0 ); - } - sqlite3_free( elem ); - pH->count--; - if( pH->count==0 ){ - assert( pH->first==0 ); - assert( pH->count==0 ); - sqlite3HashClear(pH); - } -} - -/* Attempt to locate an element of the hash table pH with a key -** that matches pKey,nKey. Return the data for this element if it is -** found, or NULL if there is no match. -*/ -SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey, int nKey){ - HashElem *elem; /* The element that matches key */ - unsigned int h; /* A hash on key */ - - assert( pH!=0 ); - assert( pKey!=0 ); - assert( nKey>=0 ); - if( pH->ht ){ - h = strHash(pKey, nKey) % pH->htsize; - }else{ - h = 0; - } - elem = findElementGivenHash(pH, pKey, nKey, h); - return elem ? elem->data : 0; -} - -/* Insert an element into the hash table pH. The key is pKey,nKey -** and the data is "data". -** -** If no element exists with a matching key, then a new -** element is created and NULL is returned. -** -** If another element already exists with the same key, then the -** new data replaces the old data and the old data is returned. -** The key is not copied in this instance. If a malloc fails, then -** the new data is returned and the hash table is unchanged. -** -** If the "data" parameter to this function is NULL, then the -** element corresponding to "key" is removed from the hash table. -*/ -SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, void *data){ - unsigned int h; /* the hash of the key modulo hash table size */ - HashElem *elem; /* Used to loop thru the element list */ - HashElem *new_elem; /* New element added to the pH */ - - assert( pH!=0 ); - assert( pKey!=0 ); - assert( nKey>=0 ); - if( pH->htsize ){ - h = strHash(pKey, nKey) % pH->htsize; - }else{ - h = 0; - } - elem = findElementGivenHash(pH,pKey,nKey,h); - if( elem ){ - void *old_data = elem->data; - if( data==0 ){ - removeElementGivenHash(pH,elem,h); - }else{ - elem->data = data; - elem->pKey = pKey; - assert(nKey==elem->nKey); - } - return old_data; - } - if( data==0 ) return 0; - new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) ); - if( new_elem==0 ) return data; - new_elem->pKey = pKey; - new_elem->nKey = nKey; - new_elem->data = data; - pH->count++; - if( pH->count>=10 && pH->count > 2*pH->htsize ){ - if( rehash(pH, pH->count*2) ){ - assert( pH->htsize>0 ); - h = strHash(pKey, nKey) % pH->htsize; - } - } - if( pH->ht ){ - insertElement(pH, &pH->ht[h], new_elem); - }else{ - insertElement(pH, 0, new_elem); - } - return 0; -} - -/************** End of hash.c ************************************************/ -/************** Begin file opcodes.c *****************************************/ -/* Automatically generated. Do not edit */ -/* See the mkopcodec.awk script for details. */ -#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) -SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ - static const char *const azName[] = { "?", - /* 1 */ "Goto", - /* 2 */ "Gosub", - /* 3 */ "Return", - /* 4 */ "Yield", - /* 5 */ "HaltIfNull", - /* 6 */ "Halt", - /* 7 */ "Integer", - /* 8 */ "Int64", - /* 9 */ "String", - /* 10 */ "Null", - /* 11 */ "Blob", - /* 12 */ "Variable", - /* 13 */ "Move", - /* 14 */ "Copy", - /* 15 */ "SCopy", - /* 16 */ "ResultRow", - /* 17 */ "CollSeq", - /* 18 */ "Function", - /* 19 */ "Not", - /* 20 */ "AddImm", - /* 21 */ "MustBeInt", - /* 22 */ "RealAffinity", - /* 23 */ "Permutation", - /* 24 */ "Compare", - /* 25 */ "Jump", - /* 26 */ "Once", - /* 27 */ "If", - /* 28 */ "IfNot", - /* 29 */ "Column", - /* 30 */ "Affinity", - /* 31 */ "MakeRecord", - /* 32 */ "Count", - /* 33 */ "Savepoint", - /* 34 */ "AutoCommit", - /* 35 */ "Transaction", - /* 36 */ "ReadCookie", - /* 37 */ "SetCookie", - /* 38 */ "VerifyCookie", - /* 39 */ "OpenRead", - /* 40 */ "OpenWrite", - /* 41 */ "OpenAutoindex", - /* 42 */ "OpenEphemeral", - /* 43 */ "SorterOpen", - /* 44 */ "OpenPseudo", - /* 45 */ "Close", - /* 46 */ "SeekLt", - /* 47 */ "SeekLe", - /* 48 */ "SeekGe", - /* 49 */ "SeekGt", - /* 50 */ "Seek", - /* 51 */ "NotFound", - /* 52 */ "Found", - /* 53 */ "IsUnique", - /* 54 */ "NotExists", - /* 55 */ "Sequence", - /* 56 */ "NewRowid", - /* 57 */ "Insert", - /* 58 */ "InsertInt", - /* 59 */ "Delete", - /* 60 */ "ResetCount", - /* 61 */ "SorterCompare", - /* 62 */ "SorterData", - /* 63 */ "RowKey", - /* 64 */ "RowData", - /* 65 */ "Rowid", - /* 66 */ "NullRow", - /* 67 */ "Last", - /* 68 */ "Or", - /* 69 */ "And", - /* 70 */ "SorterSort", - /* 71 */ "Sort", - /* 72 */ "Rewind", - /* 73 */ "IsNull", - /* 74 */ "NotNull", - /* 75 */ "Ne", - /* 76 */ "Eq", - /* 77 */ "Gt", - /* 78 */ "Le", - /* 79 */ "Lt", - /* 80 */ "Ge", - /* 81 */ "SorterNext", - /* 82 */ "BitAnd", - /* 83 */ "BitOr", - /* 84 */ "ShiftLeft", - /* 85 */ "ShiftRight", - /* 86 */ "Add", - /* 87 */ "Subtract", - /* 88 */ "Multiply", - /* 89 */ "Divide", - /* 90 */ "Remainder", - /* 91 */ "Concat", - /* 92 */ "Prev", - /* 93 */ "BitNot", - /* 94 */ "String8", - /* 95 */ "Next", - /* 96 */ "SorterInsert", - /* 97 */ "IdxInsert", - /* 98 */ "IdxDelete", - /* 99 */ "IdxRowid", - /* 100 */ "IdxLT", - /* 101 */ "IdxGE", - /* 102 */ "Destroy", - /* 103 */ "Clear", - /* 104 */ "CreateIndex", - /* 105 */ "CreateTable", - /* 106 */ "ParseSchema", - /* 107 */ "LoadAnalysis", - /* 108 */ "DropTable", - /* 109 */ "DropIndex", - /* 110 */ "DropTrigger", - /* 111 */ "IntegrityCk", - /* 112 */ "RowSetAdd", - /* 113 */ "RowSetRead", - /* 114 */ "RowSetTest", - /* 115 */ "Program", - /* 116 */ "Param", - /* 117 */ "FkCounter", - /* 118 */ "FkIfZero", - /* 119 */ "MemMax", - /* 120 */ "IfPos", - /* 121 */ "IfNeg", - /* 122 */ "IfZero", - /* 123 */ "AggStep", - /* 124 */ "AggFinal", - /* 125 */ "Checkpoint", - /* 126 */ "JournalMode", - /* 127 */ "Vacuum", - /* 128 */ "IncrVacuum", - /* 129 */ "Expire", - /* 130 */ "Real", - /* 131 */ "TableLock", - /* 132 */ "VBegin", - /* 133 */ "VCreate", - /* 134 */ "VDestroy", - /* 135 */ "VOpen", - /* 136 */ "VFilter", - /* 137 */ "VColumn", - /* 138 */ "VNext", - /* 139 */ "VRename", - /* 140 */ "VUpdate", - /* 141 */ "ToText", - /* 142 */ "ToBlob", - /* 143 */ "ToNumeric", - /* 144 */ "ToInt", - /* 145 */ "ToReal", - /* 146 */ "Pagecount", - /* 147 */ "MaxPgcnt", - /* 148 */ "Trace", - /* 149 */ "Noop", - /* 150 */ "Explain", - }; - return azName[i]; -} -#endif - -/************** End of opcodes.c *********************************************/ -/************** Begin file os_unix.c *****************************************/ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains the VFS implementation for unix-like operating systems -** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others. -** -** There are actually several different VFS implementations in this file. -** The differences are in the way that file locking is done. The default -** implementation uses Posix Advisory Locks. Alternative implementations -** use flock(), dot-files, various proprietary locking schemas, or simply -** skip locking all together. -** -** This source file is organized into divisions where the logic for various -** subfunctions is contained within the appropriate division. PLEASE -** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed -** in the correct division and should be clearly labeled. -** -** The layout of divisions is as follows: -** -** * General-purpose declarations and utility functions. -** * Unique file ID logic used by VxWorks. -** * Various locking primitive implementations (all except proxy locking): -** + for Posix Advisory Locks -** + for no-op locks -** + for dot-file locks -** + for flock() locking -** + for named semaphore locks (VxWorks only) -** + for AFP filesystem locks (MacOSX only) -** * sqlite3_file methods not associated with locking. -** * Definitions of sqlite3_io_methods objects for all locking -** methods plus "finder" functions for each locking method. -** * sqlite3_vfs method implementations. -** * Locking primitives for the proxy uber-locking-method. (MacOSX only) -** * Definitions of sqlite3_vfs objects for all locking methods -** plus implementations of sqlite3_os_init() and sqlite3_os_end(). -*/ -#if SQLITE_OS_UNIX /* This file is used on unix only */ - -/* Use posix_fallocate() if it is available -*/ -#if !defined(HAVE_POSIX_FALLOCATE) \ - && (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L) -# define HAVE_POSIX_FALLOCATE 1 -#endif - -/* -** There are various methods for file locking used for concurrency -** control: -** -** 1. POSIX locking (the default), -** 2. No locking, -** 3. Dot-file locking, -** 4. flock() locking, -** 5. AFP locking (OSX only), -** 6. Named POSIX semaphores (VXWorks only), -** 7. proxy locking. (OSX only) -** -** Styles 4, 5, and 7 are only available of SQLITE_ENABLE_LOCKING_STYLE -** is defined to 1. The SQLITE_ENABLE_LOCKING_STYLE also enables automatic -** selection of the appropriate locking style based on the filesystem -** where the database is located. -*/ -#if !defined(SQLITE_ENABLE_LOCKING_STYLE) -# if defined(__APPLE__) -# define SQLITE_ENABLE_LOCKING_STYLE 1 -# else -# define SQLITE_ENABLE_LOCKING_STYLE 0 -# endif -#endif - -/* -** Define the OS_VXWORKS pre-processor macro to 1 if building on -** vxworks, or 0 otherwise. -*/ -#ifndef OS_VXWORKS -# if defined(__RTP__) || defined(_WRS_KERNEL) -# define OS_VXWORKS 1 -# else -# define OS_VXWORKS 0 -# endif -#endif - -/* -** These #defines should enable >2GB file support on Posix if the -** underlying operating system supports it. If the OS lacks -** large file support, these should be no-ops. -** -** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch -** on the compiler command line. This is necessary if you are compiling -** on a recent machine (ex: RedHat 7.2) but you want your code to work -** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2 -** without this option, LFS is enable. But LFS does not exist in the kernel -** in RedHat 6.0, so the code won't work. Hence, for maximum binary -** portability you should omit LFS. -** -** The previous paragraph was written in 2005. (This paragraph is written -** on 2008-11-28.) These days, all Linux kernels support large files, so -** you should probably leave LFS enabled. But some embedded platforms might -** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful. -*/ -#ifndef SQLITE_DISABLE_LFS -# define _LARGE_FILE 1 -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# define _LARGEFILE_SOURCE 1 -#endif - -/* -** standard include files. -*/ -#include -#include -#include -#include -/* #include */ -#include -#include -#ifndef SQLITE_OMIT_WAL -#include -#endif - - -#if SQLITE_ENABLE_LOCKING_STYLE -# include -# if OS_VXWORKS -# include -# include -# else -# include -# include -# endif -#endif /* SQLITE_ENABLE_LOCKING_STYLE */ - -#if defined(__APPLE__) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS) -# include -#endif - -#ifdef HAVE_UTIME -# include -#endif - -/* -** Allowed values of unixFile.fsFlags -*/ -#define SQLITE_FSFLAGS_IS_MSDOS 0x1 - -/* -** If we are to be thread-safe, include the pthreads header and define -** the SQLITE_UNIX_THREADS macro. -*/ -#if SQLITE_THREADSAFE -/* # include */ -# define SQLITE_UNIX_THREADS 1 -#endif - -/* -** Default permissions when creating a new file -*/ -#ifndef SQLITE_DEFAULT_FILE_PERMISSIONS -# define SQLITE_DEFAULT_FILE_PERMISSIONS 0644 -#endif - -/* -** Default permissions when creating auto proxy dir -*/ -#ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS -# define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755 -#endif - -/* -** Maximum supported path-length. -*/ -#define MAX_PATHNAME 512 - -/* -** Only set the lastErrno if the error code is a real error and not -** a normal expected return code of SQLITE_BUSY or SQLITE_OK -*/ -#define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) - -/* Forward references */ -typedef struct unixShm unixShm; /* Connection shared memory */ -typedef struct unixShmNode unixShmNode; /* Shared memory instance */ -typedef struct unixInodeInfo unixInodeInfo; /* An i-node */ -typedef struct UnixUnusedFd UnixUnusedFd; /* An unused file descriptor */ - -/* -** Sometimes, after a file handle is closed by SQLite, the file descriptor -** cannot be closed immediately. In these cases, instances of the following -** structure are used to store the file descriptor while waiting for an -** opportunity to either close or reuse it. -*/ -struct UnixUnusedFd { - int fd; /* File descriptor to close */ - int flags; /* Flags this file descriptor was opened with */ - UnixUnusedFd *pNext; /* Next unused file descriptor on same file */ -}; - -/* -** The unixFile structure is subclass of sqlite3_file specific to the unix -** VFS implementations. -*/ -typedef struct unixFile unixFile; -struct unixFile { - sqlite3_io_methods const *pMethod; /* Always the first entry */ - sqlite3_vfs *pVfs; /* The VFS that created this unixFile */ - unixInodeInfo *pInode; /* Info about locks on this inode */ - int h; /* The file descriptor */ - unsigned char eFileLock; /* The type of lock held on this fd */ - unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ - int lastErrno; /* The unix errno from last I/O error */ - void *lockingContext; /* Locking style specific state */ - UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ - const char *zPath; /* Name of the file */ - unixShm *pShm; /* Shared memory segment information */ - int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ -#ifdef __QNXNTO__ - int sectorSize; /* Device sector size */ - int deviceCharacteristics; /* Precomputed device characteristics */ -#endif -#if SQLITE_ENABLE_LOCKING_STYLE - int openFlags; /* The flags specified at open() */ -#endif -#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) - unsigned fsFlags; /* cached details from statfs() */ -#endif -#if OS_VXWORKS - struct vxworksFileId *pId; /* Unique file ID */ -#endif -#ifdef SQLITE_DEBUG - /* The next group of variables are used to track whether or not the - ** transaction counter in bytes 24-27 of database files are updated - ** whenever any part of the database changes. An assertion fault will - ** occur if a file is updated without also updating the transaction - ** counter. This test is made to avoid new problems similar to the - ** one described by ticket #3584. - */ - unsigned char transCntrChng; /* True if the transaction counter changed */ - unsigned char dbUpdate; /* True if any part of database file changed */ - unsigned char inNormalWrite; /* True if in a normal write operation */ -#endif -#ifdef SQLITE_TEST - /* In test mode, increase the size of this structure a bit so that - ** it is larger than the struct CrashFile defined in test6.c. - */ - char aPadding[32]; -#endif -}; - -/* -** Allowed values for the unixFile.ctrlFlags bitmask: -*/ -#define UNIXFILE_EXCL 0x01 /* Connections from one process only */ -#define UNIXFILE_RDONLY 0x02 /* Connection is read only */ -#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ -#ifndef SQLITE_DISABLE_DIRSYNC -# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ -#else -# define UNIXFILE_DIRSYNC 0x00 -#endif -#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ -#define UNIXFILE_DELETE 0x20 /* Delete on close */ -#define UNIXFILE_URI 0x40 /* Filename might have query parameters */ -#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ - -/* -** Include code that is common to all os_*.c files -*/ -/************** Include os_common.h in the middle of os_unix.c ***************/ -/************** Begin file os_common.h ***************************************/ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains macros and a little bit of code that is common to -** all of the platform-specific files (os_*.c) and is #included into those -** files. -** -** This file should be #included by the os_*.c files only. It is not a -** general purpose header file. -*/ -#ifndef _OS_COMMON_H_ -#define _OS_COMMON_H_ - -/* -** At least two bugs have slipped in because we changed the MEMORY_DEBUG -** macro to SQLITE_DEBUG and some older makefiles have not yet made the -** switch. The following code should catch this problem at compile-time. -*/ -#ifdef MEMORY_DEBUG -# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." -#endif - -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -# ifndef SQLITE_DEBUG_OS_TRACE -# define SQLITE_DEBUG_OS_TRACE 0 -# endif - int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; -# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X -#else -# define OSTRACE(X) -#endif - -/* -** Macros for performance tracing. Normally turned off. Only works -** on i486 hardware. -*/ -#ifdef SQLITE_PERFORMANCE_TRACE - -/* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of os_common.h ****************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains inline asm code for retrieving "high-performance" -** counters for x86 class CPUs. -*/ -#ifndef _HWTIME_H_ -#define _HWTIME_H_ - -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - -#elif (defined(__GNUC__) && defined(__x86_64__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } - -#elif (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } - -#else - - #error Need implementation of sqlite3Hwtime() for your platform. - - /* - ** To compile without implementing sqlite3Hwtime() for your platform, - ** you can remove the above #error and use the following - ** stub function. You will lose timing support for many - ** of the debugging and testing utilities, but it should at - ** least compile and run. - */ -SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(_HWTIME_H_) */ - -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in os_common.h ******************/ - -static sqlite_uint64 g_start; -static sqlite_uint64 g_elapsed; -#define TIMER_START g_start=sqlite3Hwtime() -#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start -#define TIMER_ELAPSED g_elapsed -#else -#define TIMER_START -#define TIMER_END -#define TIMER_ELAPSED ((sqlite_uint64)0) -#endif - -/* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */ -SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */ -SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */ -SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */ -SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */ -SQLITE_API int sqlite3_diskfull_pending = 0; -SQLITE_API int sqlite3_diskfull = 0; -#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) -#define SimulateIOError(CODE) \ - if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ - || sqlite3_io_error_pending-- == 1 ) \ - { local_ioerr(); CODE; } -static void local_ioerr(){ - IOTRACE(("IOERR\n")); - sqlite3_io_error_hit++; - if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; -} -#define SimulateDiskfullError(CODE) \ - if( sqlite3_diskfull_pending ){ \ - if( sqlite3_diskfull_pending == 1 ){ \ - local_ioerr(); \ - sqlite3_diskfull = 1; \ - sqlite3_io_error_hit = 1; \ - CODE; \ - }else{ \ - sqlite3_diskfull_pending--; \ - } \ - } -#else -#define SimulateIOErrorBenign(X) -#define SimulateIOError(A) -#define SimulateDiskfullError(A) -#endif - -/* -** When testing, keep a count of the number of open files. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_open_file_count = 0; -#define OpenCounter(X) sqlite3_open_file_count+=(X) -#else -#define OpenCounter(X) -#endif - -#endif /* !defined(_OS_COMMON_H_) */ - -/************** End of os_common.h *******************************************/ -/************** Continuing where we left off in os_unix.c ********************/ - -/* -** Define various macros that are missing from some systems. -*/ -#ifndef O_LARGEFILE -# define O_LARGEFILE 0 -#endif -#ifdef SQLITE_DISABLE_LFS -# undef O_LARGEFILE -# define O_LARGEFILE 0 -#endif -#ifndef O_NOFOLLOW -# define O_NOFOLLOW 0 -#endif -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -/* -** The threadid macro resolves to the thread-id or to 0. Used for -** testing and debugging only. -*/ -#if SQLITE_THREADSAFE -#define threadid pthread_self() -#else -#define threadid 0 -#endif - -/* -** Different Unix systems declare open() in different ways. Same use -** open(const char*,int,mode_t). Others use open(const char*,int,...). -** The difference is important when using a pointer to the function. -** -** The safest way to deal with the problem is to always use this wrapper -** which always has the same well-defined interface. -*/ -static int posixOpen(const char *zFile, int flags, int mode){ - return open(zFile, flags, mode); -} - -/* -** On some systems, calls to fchown() will trigger a message in a security -** log if they come from non-root processes. So avoid calling fchown() if -** we are not running as root. -*/ -static int posixFchown(int fd, uid_t uid, gid_t gid){ - return geteuid() ? 0 : fchown(fd,uid,gid); -} - -/* Forward reference */ -static int openDirectory(const char*, int*); - -/* -** Many system calls are accessed through pointer-to-functions so that -** they may be overridden at runtime to facilitate fault injection during -** testing and sandboxing. The following array holds the names and pointers -** to all overrideable system calls. -*/ -static struct unix_syscall { - const char *zName; /* Name of the system call */ - sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ - sqlite3_syscall_ptr pDefault; /* Default value */ -} aSyscall[] = { - { "open", (sqlite3_syscall_ptr)posixOpen, 0 }, -#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) - - { "close", (sqlite3_syscall_ptr)close, 0 }, -#define osClose ((int(*)(int))aSyscall[1].pCurrent) - - { "access", (sqlite3_syscall_ptr)access, 0 }, -#define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent) - - { "getcwd", (sqlite3_syscall_ptr)getcwd, 0 }, -#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent) - - { "stat", (sqlite3_syscall_ptr)stat, 0 }, -#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent) - -/* -** The DJGPP compiler environment looks mostly like Unix, but it -** lacks the fcntl() system call. So redefine fcntl() to be something -** that always succeeds. This means that locking does not occur under -** DJGPP. But it is DOS - what did you expect? -*/ -#ifdef __DJGPP__ - { "fstat", 0, 0 }, -#define osFstat(a,b,c) 0 -#else - { "fstat", (sqlite3_syscall_ptr)fstat, 0 }, -#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent) -#endif - - { "ftruncate", (sqlite3_syscall_ptr)ftruncate, 0 }, -#define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent) - - { "fcntl", (sqlite3_syscall_ptr)fcntl, 0 }, -#define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent) - - { "read", (sqlite3_syscall_ptr)read, 0 }, -#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) - -#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE - { "pread", (sqlite3_syscall_ptr)pread, 0 }, -#else - { "pread", (sqlite3_syscall_ptr)0, 0 }, -#endif -#define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent) - -#if defined(USE_PREAD64) - { "pread64", (sqlite3_syscall_ptr)pread64, 0 }, -#else - { "pread64", (sqlite3_syscall_ptr)0, 0 }, -#endif -#define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent) - - { "write", (sqlite3_syscall_ptr)write, 0 }, -#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) - -#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE - { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 }, -#else - { "pwrite", (sqlite3_syscall_ptr)0, 0 }, -#endif -#define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ - aSyscall[12].pCurrent) - -#if defined(USE_PREAD64) - { "pwrite64", (sqlite3_syscall_ptr)pwrite64, 0 }, -#else - { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, -#endif -#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ - aSyscall[13].pCurrent) - - { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, -#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) - -#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE - { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, -#else - { "fallocate", (sqlite3_syscall_ptr)0, 0 }, -#endif -#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent) - - { "unlink", (sqlite3_syscall_ptr)unlink, 0 }, -#define osUnlink ((int(*)(const char*))aSyscall[16].pCurrent) - - { "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 }, -#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent) - - { "mkdir", (sqlite3_syscall_ptr)mkdir, 0 }, -#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent) - - { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 }, -#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) - - { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, -#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) - -}; /* End of the overrideable system calls */ - -/* -** This is the xSetSystemCall() method of sqlite3_vfs for all of the -** "unix" VFSes. Return SQLITE_OK opon successfully updating the -** system call pointer, or SQLITE_NOTFOUND if there is no configurable -** system call named zName. -*/ -static int unixSetSystemCall( - sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ - const char *zName, /* Name of system call to override */ - sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ -){ - unsigned int i; - int rc = SQLITE_NOTFOUND; - - UNUSED_PARAMETER(pNotUsed); - if( zName==0 ){ - /* If no zName is given, restore all system calls to their default - ** settings and return NULL - */ - rc = SQLITE_OK; - for(i=0; i=0 ){ - if( m!=0 ){ - struct stat statbuf; - if( osFstat(fd, &statbuf)==0 - && statbuf.st_size==0 - && (statbuf.st_mode&0777)!=m - ){ - osFchmod(fd, m); - } - } -#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) - osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); -#endif - } - return fd; -} - -/* -** Helper functions to obtain and relinquish the global mutex. The -** global mutex is used to protect the unixInodeInfo and -** vxworksFileId objects used by this file, all of which may be -** shared by multiple threads. -** -** Function unixMutexHeld() is used to assert() that the global mutex -** is held when required. This function is only used as part of assert() -** statements. e.g. -** -** unixEnterMutex() -** assert( unixMutexHeld() ); -** unixEnterLeave() -*/ -static void unixEnterMutex(void){ - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); -} -static void unixLeaveMutex(void){ - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); -} -#ifdef SQLITE_DEBUG -static int unixMutexHeld(void) { - return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); -} -#endif - - -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -/* -** Helper function for printing out trace information from debugging -** binaries. This returns the string represetation of the supplied -** integer lock-type. -*/ -static const char *azFileLock(int eFileLock){ - switch( eFileLock ){ - case NO_LOCK: return "NONE"; - case SHARED_LOCK: return "SHARED"; - case RESERVED_LOCK: return "RESERVED"; - case PENDING_LOCK: return "PENDING"; - case EXCLUSIVE_LOCK: return "EXCLUSIVE"; - } - return "ERROR"; -} -#endif - -#ifdef SQLITE_LOCK_TRACE -/* -** Print out information about all locking operations. -** -** This routine is used for troubleshooting locks on multithreaded -** platforms. Enable by compiling with the -DSQLITE_LOCK_TRACE -** command-line option on the compiler. This code is normally -** turned off. -*/ -static int lockTrace(int fd, int op, struct flock *p){ - char *zOpName, *zType; - int s; - int savedErrno; - if( op==F_GETLK ){ - zOpName = "GETLK"; - }else if( op==F_SETLK ){ - zOpName = "SETLK"; - }else{ - s = osFcntl(fd, op, p); - sqlite3DebugPrintf("fcntl unknown %d %d %d\n", fd, op, s); - return s; - } - if( p->l_type==F_RDLCK ){ - zType = "RDLCK"; - }else if( p->l_type==F_WRLCK ){ - zType = "WRLCK"; - }else if( p->l_type==F_UNLCK ){ - zType = "UNLCK"; - }else{ - assert( 0 ); - } - assert( p->l_whence==SEEK_SET ); - s = osFcntl(fd, op, p); - savedErrno = errno; - sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n", - threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len, - (int)p->l_pid, s); - if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){ - struct flock l2; - l2 = *p; - osFcntl(fd, F_GETLK, &l2); - if( l2.l_type==F_RDLCK ){ - zType = "RDLCK"; - }else if( l2.l_type==F_WRLCK ){ - zType = "WRLCK"; - }else if( l2.l_type==F_UNLCK ){ - zType = "UNLCK"; - }else{ - assert( 0 ); - } - sqlite3DebugPrintf("fcntl-failure-reason: %s %d %d %d\n", - zType, (int)l2.l_start, (int)l2.l_len, (int)l2.l_pid); - } - errno = savedErrno; - return s; -} -#undef osFcntl -#define osFcntl lockTrace -#endif /* SQLITE_LOCK_TRACE */ - -/* -** Retry ftruncate() calls that fail due to EINTR -*/ -static int robust_ftruncate(int h, sqlite3_int64 sz){ - int rc; - do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR ); - return rc; -} - -/* -** This routine translates a standard POSIX errno code into something -** useful to the clients of the sqlite3 functions. Specifically, it is -** intended to translate a variety of "try again" errors into SQLITE_BUSY -** and a variety of "please close the file descriptor NOW" errors into -** SQLITE_IOERR -** -** Errors during initialization of locks, or file system support for locks, -** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately. -*/ -static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { - switch (posixError) { -#if 0 - /* At one point this code was not commented out. In theory, this branch - ** should never be hit, as this function should only be called after - ** a locking-related function (i.e. fcntl()) has returned non-zero with - ** the value of errno as the first argument. Since a system call has failed, - ** errno should be non-zero. - ** - ** Despite this, if errno really is zero, we still don't want to return - ** SQLITE_OK. The system call failed, and *some* SQLite error should be - ** propagated back to the caller. Commenting this branch out means errno==0 - ** will be handled by the "default:" case below. - */ - case 0: - return SQLITE_OK; -#endif - - case EAGAIN: - case ETIMEDOUT: - case EBUSY: - case EINTR: - case ENOLCK: - /* random NFS retry error, unless during file system support - * introspection, in which it actually means what it says */ - return SQLITE_BUSY; - - case EACCES: - /* EACCES is like EAGAIN during locking operations, but not any other time*/ - if( (sqliteIOErr == SQLITE_IOERR_LOCK) || - (sqliteIOErr == SQLITE_IOERR_UNLOCK) || - (sqliteIOErr == SQLITE_IOERR_RDLOCK) || - (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){ - return SQLITE_BUSY; - } - /* else fall through */ - case EPERM: - return SQLITE_PERM; - - /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And - ** this module never makes such a call. And the code in SQLite itself - ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons - ** this case is also commented out. If the system does set errno to EDEADLK, - ** the default SQLITE_IOERR_XXX code will be returned. */ -#if 0 - case EDEADLK: - return SQLITE_IOERR_BLOCKED; -#endif - -#if EOPNOTSUPP!=ENOTSUP - case EOPNOTSUPP: - /* something went terribly awry, unless during file system support - * introspection, in which it actually means what it says */ -#endif -#ifdef ENOTSUP - case ENOTSUP: - /* invalid fd, unless during file system support introspection, in which - * it actually means what it says */ -#endif - case EIO: - case EBADF: - case EINVAL: - case ENOTCONN: - case ENODEV: - case ENXIO: - case ENOENT: -#ifdef ESTALE /* ESTALE is not defined on Interix systems */ - case ESTALE: -#endif - case ENOSYS: - /* these should force the client to close the file and reconnect */ - - default: - return sqliteIOErr; - } -} - - - -/****************************************************************************** -****************** Begin Unique File ID Utility Used By VxWorks *************** -** -** On most versions of unix, we can get a unique ID for a file by concatenating -** the device number and the inode number. But this does not work on VxWorks. -** On VxWorks, a unique file id must be based on the canonical filename. -** -** A pointer to an instance of the following structure can be used as a -** unique file ID in VxWorks. Each instance of this structure contains -** a copy of the canonical filename. There is also a reference count. -** The structure is reclaimed when the number of pointers to it drops to -** zero. -** -** There are never very many files open at one time and lookups are not -** a performance-critical path, so it is sufficient to put these -** structures on a linked list. -*/ -struct vxworksFileId { - struct vxworksFileId *pNext; /* Next in a list of them all */ - int nRef; /* Number of references to this one */ - int nName; /* Length of the zCanonicalName[] string */ - char *zCanonicalName; /* Canonical filename */ -}; - -#if OS_VXWORKS -/* -** All unique filenames are held on a linked list headed by this -** variable: -*/ -static struct vxworksFileId *vxworksFileList = 0; - -/* -** Simplify a filename into its canonical form -** by making the following changes: -** -** * removing any trailing and duplicate / -** * convert /./ into just / -** * convert /A/../ where A is any simple name into just / -** -** Changes are made in-place. Return the new name length. -** -** The original filename is in z[0..n-1]. Return the number of -** characters in the simplified name. -*/ -static int vxworksSimplifyName(char *z, int n){ - int i, j; - while( n>1 && z[n-1]=='/' ){ n--; } - for(i=j=0; i0 && z[j-1]!='/' ){ j--; } - if( j>0 ){ j--; } - i += 2; - continue; - } - } - z[j++] = z[i]; - } - z[j] = 0; - return j; -} - -/* -** Find a unique file ID for the given absolute pathname. Return -** a pointer to the vxworksFileId object. This pointer is the unique -** file ID. -** -** The nRef field of the vxworksFileId object is incremented before -** the object is returned. A new vxworksFileId object is created -** and added to the global list if necessary. -** -** If a memory allocation error occurs, return NULL. -*/ -static struct vxworksFileId *vxworksFindFileId(const char *zAbsoluteName){ - struct vxworksFileId *pNew; /* search key and new file ID */ - struct vxworksFileId *pCandidate; /* For looping over existing file IDs */ - int n; /* Length of zAbsoluteName string */ - - assert( zAbsoluteName[0]=='/' ); - n = (int)strlen(zAbsoluteName); - pNew = sqlite3_malloc( sizeof(*pNew) + (n+1) ); - if( pNew==0 ) return 0; - pNew->zCanonicalName = (char*)&pNew[1]; - memcpy(pNew->zCanonicalName, zAbsoluteName, n+1); - n = vxworksSimplifyName(pNew->zCanonicalName, n); - - /* Search for an existing entry that matching the canonical name. - ** If found, increment the reference count and return a pointer to - ** the existing file ID. - */ - unixEnterMutex(); - for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){ - if( pCandidate->nName==n - && memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0 - ){ - sqlite3_free(pNew); - pCandidate->nRef++; - unixLeaveMutex(); - return pCandidate; - } - } - - /* No match was found. We will make a new file ID */ - pNew->nRef = 1; - pNew->nName = n; - pNew->pNext = vxworksFileList; - vxworksFileList = pNew; - unixLeaveMutex(); - return pNew; -} - -/* -** Decrement the reference count on a vxworksFileId object. Free -** the object when the reference count reaches zero. -*/ -static void vxworksReleaseFileId(struct vxworksFileId *pId){ - unixEnterMutex(); - assert( pId->nRef>0 ); - pId->nRef--; - if( pId->nRef==0 ){ - struct vxworksFileId **pp; - for(pp=&vxworksFileList; *pp && *pp!=pId; pp = &((*pp)->pNext)){} - assert( *pp==pId ); - *pp = pId->pNext; - sqlite3_free(pId); - } - unixLeaveMutex(); -} -#endif /* OS_VXWORKS */ -/*************** End of Unique File ID Utility Used By VxWorks **************** -******************************************************************************/ - - -/****************************************************************************** -*************************** Posix Advisory Locking **************************** -** -** POSIX advisory locks are broken by design. ANSI STD 1003.1 (1996) -** section 6.5.2.2 lines 483 through 490 specify that when a process -** sets or clears a lock, that operation overrides any prior locks set -** by the same process. It does not explicitly say so, but this implies -** that it overrides locks set by the same process using a different -** file descriptor. Consider this test case: -** -** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); -** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); -** -** Suppose ./file1 and ./file2 are really the same file (because -** one is a hard or symbolic link to the other) then if you set -** an exclusive lock on fd1, then try to get an exclusive lock -** on fd2, it works. I would have expected the second lock to -** fail since there was already a lock on the file due to fd1. -** But not so. Since both locks came from the same process, the -** second overrides the first, even though they were on different -** file descriptors opened on different file names. -** -** This means that we cannot use POSIX locks to synchronize file access -** among competing threads of the same process. POSIX locks will work fine -** to synchronize access for threads in separate processes, but not -** threads within the same process. -** -** To work around the problem, SQLite has to manage file locks internally -** on its own. Whenever a new database is opened, we have to find the -** specific inode of the database file (the inode is determined by the -** st_dev and st_ino fields of the stat structure that fstat() fills in) -** and check for locks already existing on that inode. When locks are -** created or removed, we have to look at our own internal record of the -** locks to see if another thread has previously set a lock on that same -** inode. -** -** (Aside: The use of inode numbers as unique IDs does not work on VxWorks. -** For VxWorks, we have to use the alternative unique ID system based on -** canonical filename and implemented in the previous division.) -** -** The sqlite3_file structure for POSIX is no longer just an integer file -** descriptor. It is now a structure that holds the integer file -** descriptor and a pointer to a structure that describes the internal -** locks on the corresponding inode. There is one locking structure -** per inode, so if the same inode is opened twice, both unixFile structures -** point to the same locking structure. The locking structure keeps -** a reference count (so we will know when to delete it) and a "cnt" -** field that tells us its internal lock status. cnt==0 means the -** file is unlocked. cnt==-1 means the file has an exclusive lock. -** cnt>0 means there are cnt shared locks on the file. -** -** Any attempt to lock or unlock a file first checks the locking -** structure. The fcntl() system call is only invoked to set a -** POSIX lock if the internal lock structure transitions between -** a locked and an unlocked state. -** -** But wait: there are yet more problems with POSIX advisory locks. -** -** If you close a file descriptor that points to a file that has locks, -** all locks on that file that are owned by the current process are -** released. To work around this problem, each unixInodeInfo object -** maintains a count of the number of pending locks on tha inode. -** When an attempt is made to close an unixFile, if there are -** other unixFile open on the same inode that are holding locks, the call -** to close() the file descriptor is deferred until all of the locks clear. -** The unixInodeInfo structure keeps a list of file descriptors that need to -** be closed and that list is walked (and cleared) when the last lock -** clears. -** -** Yet another problem: LinuxThreads do not play well with posix locks. -** -** Many older versions of linux use the LinuxThreads library which is -** not posix compliant. Under LinuxThreads, a lock created by thread -** A cannot be modified or overridden by a different thread B. -** Only thread A can modify the lock. Locking behavior is correct -** if the appliation uses the newer Native Posix Thread Library (NPTL) -** on linux - with NPTL a lock created by thread A can override locks -** in thread B. But there is no way to know at compile-time which -** threading library is being used. So there is no way to know at -** compile-time whether or not thread A can override locks on thread B. -** One has to do a run-time check to discover the behavior of the -** current process. -** -** SQLite used to support LinuxThreads. But support for LinuxThreads -** was dropped beginning with version 3.7.0. SQLite will still work with -** LinuxThreads provided that (1) there is no more than one connection -** per database file in the same process and (2) database connections -** do not move across threads. -*/ - -/* -** An instance of the following structure serves as the key used -** to locate a particular unixInodeInfo object. -*/ -struct unixFileId { - dev_t dev; /* Device number */ -#if OS_VXWORKS - struct vxworksFileId *pId; /* Unique file ID for vxworks. */ -#else - ino_t ino; /* Inode number */ -#endif -}; - -/* -** An instance of the following structure is allocated for each open -** inode. Or, on LinuxThreads, there is one of these structures for -** each inode opened by each thread. -** -** A single inode can have multiple file descriptors, so each unixFile -** structure contains a pointer to an instance of this object and this -** object keeps a count of the number of unixFile pointing to it. -*/ -struct unixInodeInfo { - struct unixFileId fileId; /* The lookup key */ - int nShared; /* Number of SHARED locks held */ - unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ - unsigned char bProcessLock; /* An exclusive process lock is held */ - int nRef; /* Number of pointers to this structure */ - unixShmNode *pShmNode; /* Shared memory associated with this inode */ - int nLock; /* Number of outstanding file locks */ - UnixUnusedFd *pUnused; /* Unused file descriptors to close */ - unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ - unixInodeInfo *pPrev; /* .... doubly linked */ -#if SQLITE_ENABLE_LOCKING_STYLE - unsigned long long sharedByte; /* for AFP simulated shared lock */ -#endif -#if OS_VXWORKS - sem_t *pSem; /* Named POSIX semaphore */ - char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ -#endif -}; - -/* -** A lists of all unixInodeInfo objects. -*/ -static unixInodeInfo *inodeList = 0; - -/* -** -** This function - unixLogError_x(), is only ever called via the macro -** unixLogError(). -** -** It is invoked after an error occurs in an OS function and errno has been -** set. It logs a message using sqlite3_log() containing the current value of -** errno and, if possible, the human-readable equivalent from strerror() or -** strerror_r(). -** -** The first argument passed to the macro should be the error code that -** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). -** The two subsequent arguments should be the name of the OS function that -** failed (e.g. "unlink", "open") and the associated file-system path, -** if any. -*/ -#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__) -static int unixLogErrorAtLine( - int errcode, /* SQLite error code */ - const char *zFunc, /* Name of OS function that failed */ - const char *zPath, /* File path associated with error */ - int iLine /* Source line number where error occurred */ -){ - char *zErr; /* Message from strerror() or equivalent */ - int iErrno = errno; /* Saved syscall error number */ - - /* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use - ** the strerror() function to obtain the human-readable error message - ** equivalent to errno. Otherwise, use strerror_r(). - */ -#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R) - char aErr[80]; - memset(aErr, 0, sizeof(aErr)); - zErr = aErr; - - /* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined, - ** assume that the system provides the GNU version of strerror_r() that - ** returns a pointer to a buffer containing the error message. That pointer - ** may point to aErr[], or it may point to some static storage somewhere. - ** Otherwise, assume that the system provides the POSIX version of - ** strerror_r(), which always writes an error message into aErr[]. - ** - ** If the code incorrectly assumes that it is the POSIX version that is - ** available, the error message will often be an empty string. Not a - ** huge problem. Incorrectly concluding that the GNU version is available - ** could lead to a segfault though. - */ -#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) - zErr = -# endif - strerror_r(iErrno, aErr, sizeof(aErr)-1); - -#elif SQLITE_THREADSAFE - /* This is a threadsafe build, but strerror_r() is not available. */ - zErr = ""; -#else - /* Non-threadsafe build, use strerror(). */ - zErr = strerror(iErrno); -#endif - - assert( errcode!=SQLITE_OK ); - if( zPath==0 ) zPath = ""; - sqlite3_log(errcode, - "os_unix.c:%d: (%d) %s(%s) - %s", - iLine, iErrno, zFunc, zPath, zErr - ); - - return errcode; -} - -/* -** Close a file descriptor. -** -** We assume that close() almost always works, since it is only in a -** very sick application or on a very sick platform that it might fail. -** If it does fail, simply leak the file descriptor, but do log the -** error. -** -** Note that it is not safe to retry close() after EINTR since the -** file descriptor might have already been reused by another thread. -** So we don't even try to recover from an EINTR. Just log the error -** and move on. -*/ -static void robust_close(unixFile *pFile, int h, int lineno){ - if( osClose(h) ){ - unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close", - pFile ? pFile->zPath : 0, lineno); - } -} - -/* -** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. -*/ -static void closePendingFds(unixFile *pFile){ - unixInodeInfo *pInode = pFile->pInode; - UnixUnusedFd *p; - UnixUnusedFd *pNext; - for(p=pInode->pUnused; p; p=pNext){ - pNext = p->pNext; - robust_close(pFile, p->fd, __LINE__); - sqlite3_free(p); - } - pInode->pUnused = 0; -} - -/* -** Release a unixInodeInfo structure previously allocated by findInodeInfo(). -** -** The mutex entered using the unixEnterMutex() function must be held -** when this function is called. -*/ -static void releaseInodeInfo(unixFile *pFile){ - unixInodeInfo *pInode = pFile->pInode; - assert( unixMutexHeld() ); - if( ALWAYS(pInode) ){ - pInode->nRef--; - if( pInode->nRef==0 ){ - assert( pInode->pShmNode==0 ); - closePendingFds(pFile); - if( pInode->pPrev ){ - assert( pInode->pPrev->pNext==pInode ); - pInode->pPrev->pNext = pInode->pNext; - }else{ - assert( inodeList==pInode ); - inodeList = pInode->pNext; - } - if( pInode->pNext ){ - assert( pInode->pNext->pPrev==pInode ); - pInode->pNext->pPrev = pInode->pPrev; - } - sqlite3_free(pInode); - } - } -} - -/* -** Given a file descriptor, locate the unixInodeInfo object that -** describes that file descriptor. Create a new one if necessary. The -** return value might be uninitialized if an error occurs. -** -** The mutex entered using the unixEnterMutex() function must be held -** when this function is called. -** -** Return an appropriate error code. -*/ -static int findInodeInfo( - unixFile *pFile, /* Unix file with file desc used in the key */ - unixInodeInfo **ppInode /* Return the unixInodeInfo object here */ -){ - int rc; /* System call return code */ - int fd; /* The file descriptor for pFile */ - struct unixFileId fileId; /* Lookup key for the unixInodeInfo */ - struct stat statbuf; /* Low-level file information */ - unixInodeInfo *pInode = 0; /* Candidate unixInodeInfo object */ - - assert( unixMutexHeld() ); - - /* Get low-level information about the file that we can used to - ** create a unique name for the file. - */ - fd = pFile->h; - rc = osFstat(fd, &statbuf); - if( rc!=0 ){ - pFile->lastErrno = errno; -#ifdef EOVERFLOW - if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; -#endif - return SQLITE_IOERR; - } - -#ifdef __APPLE__ - /* On OS X on an msdos filesystem, the inode number is reported - ** incorrectly for zero-size files. See ticket #3260. To work - ** around this problem (we consider it a bug in OS X, not SQLite) - ** we always increase the file size to 1 by writing a single byte - ** prior to accessing the inode number. The one byte written is - ** an ASCII 'S' character which also happens to be the first byte - ** in the header of every SQLite database. In this way, if there - ** is a race condition such that another thread has already populated - ** the first page of the database, no damage is done. - */ - if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ - do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); - if( rc!=1 ){ - pFile->lastErrno = errno; - return SQLITE_IOERR; - } - rc = osFstat(fd, &statbuf); - if( rc!=0 ){ - pFile->lastErrno = errno; - return SQLITE_IOERR; - } - } -#endif - - memset(&fileId, 0, sizeof(fileId)); - fileId.dev = statbuf.st_dev; -#if OS_VXWORKS - fileId.pId = pFile->pId; -#else - fileId.ino = statbuf.st_ino; -#endif - pInode = inodeList; - while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){ - pInode = pInode->pNext; - } - if( pInode==0 ){ - pInode = sqlite3_malloc( sizeof(*pInode) ); - if( pInode==0 ){ - return SQLITE_NOMEM; - } - memset(pInode, 0, sizeof(*pInode)); - memcpy(&pInode->fileId, &fileId, sizeof(fileId)); - pInode->nRef = 1; - pInode->pNext = inodeList; - pInode->pPrev = 0; - if( inodeList ) inodeList->pPrev = pInode; - inodeList = pInode; - }else{ - pInode->nRef++; - } - *ppInode = pInode; - return SQLITE_OK; -} - - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -*/ -static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ - int rc = SQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - - assert( pFile ); - unixEnterMutex(); /* Because pFile->pInode is shared across threads */ - - /* Check if a thread in this process holds such a lock */ - if( pFile->pInode->eFileLock>SHARED_LOCK ){ - reserved = 1; - } - - /* Otherwise see if some other process holds it. - */ -#ifndef __DJGPP__ - if( !reserved && !pFile->pInode->bProcessLock ){ - struct flock lock; - lock.l_whence = SEEK_SET; - lock.l_start = RESERVED_BYTE; - lock.l_len = 1; - lock.l_type = F_WRLCK; - if( osFcntl(pFile->h, F_GETLK, &lock) ){ - rc = SQLITE_IOERR_CHECKRESERVEDLOCK; - pFile->lastErrno = errno; - } else if( lock.l_type!=F_UNLCK ){ - reserved = 1; - } - } -#endif - - unixLeaveMutex(); - OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved)); - - *pResOut = reserved; - return rc; -} - -/* -** Attempt to set a system-lock on the file pFile. The lock is -** described by pLock. -** -** If the pFile was opened read/write from unix-excl, then the only lock -** ever obtained is an exclusive lock, and it is obtained exactly once -** the first time any lock is attempted. All subsequent system locking -** operations become no-ops. Locking operations still happen internally, -** in order to coordinate access between separate database connections -** within this process, but all of that is handled in memory and the -** operating system does not participate. -** -** This function is a pass-through to fcntl(F_SETLK) if pFile is using -** any VFS other than "unix-excl" or if pFile is opened on "unix-excl" -** and is read-only. -** -** Zero is returned if the call completes successfully, or -1 if a call -** to fcntl() fails. In this case, errno is set appropriately (by fcntl()). -*/ -static int unixFileLock(unixFile *pFile, struct flock *pLock){ - int rc; - unixInodeInfo *pInode = pFile->pInode; - assert( unixMutexHeld() ); - assert( pInode!=0 ); - if( ((pFile->ctrlFlags & UNIXFILE_EXCL)!=0 || pInode->bProcessLock) - && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0) - ){ - if( pInode->bProcessLock==0 ){ - struct flock lock; - assert( pInode->nLock==0 ); - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - lock.l_type = F_WRLCK; - rc = osFcntl(pFile->h, F_SETLK, &lock); - if( rc<0 ) return rc; - pInode->bProcessLock = 1; - pInode->nLock++; - }else{ - rc = 0; - } - }else{ - rc = osFcntl(pFile->h, F_SETLK, pLock); - } - return rc; -} - -/* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. Use the sqlite3OsUnlock() -** routine to lower a locking level. -*/ -static int unixLock(sqlite3_file *id, int eFileLock){ - /* The following describes the implementation of the various locks and - ** lock transitions in terms of the POSIX advisory shared and exclusive - ** lock primitives (called read-locks and write-locks below, to avoid - ** confusion with SQLite lock names). The algorithms are complicated - ** slightly in order to be compatible with windows systems simultaneously - ** accessing the same database file, in case that is ever required. - ** - ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved - ** byte', each single bytes at well known offsets, and the 'shared byte - ** range', a range of 510 bytes at a well known offset. - ** - ** To obtain a SHARED lock, a read-lock is obtained on the 'pending - ** byte'. If this is successful, a random byte from the 'shared byte - ** range' is read-locked and the lock on the 'pending byte' released. - ** - ** A process may only obtain a RESERVED lock after it has a SHARED lock. - ** A RESERVED lock is implemented by grabbing a write-lock on the - ** 'reserved byte'. - ** - ** A process may only obtain a PENDING lock after it has obtained a - ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock - ** on the 'pending byte'. This ensures that no new SHARED locks can be - ** obtained, but existing SHARED locks are allowed to persist. A process - ** does not have to obtain a RESERVED lock on the way to a PENDING lock. - ** This property is used by the algorithm for rolling back a journal file - ** after a crash. - ** - ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is - ** implemented by obtaining a write-lock on the entire 'shared byte - ** range'. Since all other locks require a read-lock on one of the bytes - ** within this range, this ensures that no other locks are held on the - ** database. - ** - ** The reason a single byte cannot be used instead of the 'shared byte - ** range' is that some versions of windows do not support read-locks. By - ** locking a random byte from a range, concurrent SHARED locks may exist - ** even if the locking primitive used is always a write-lock. - */ - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode; - struct flock lock; - int tErrno = 0; - - assert( pFile ); - OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, - azFileLock(eFileLock), azFileLock(pFile->eFileLock), - azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared , getpid())); - - /* If there is already a lock of this type or more restrictive on the - ** unixFile, do nothing. Don't use the end_lock: exit path, as - ** unixEnterMutex() hasn't been called yet. - */ - if( pFile->eFileLock>=eFileLock ){ - OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h, - azFileLock(eFileLock))); - return SQLITE_OK; - } - - /* Make sure the locking sequence is correct. - ** (1) We never move from unlocked to anything higher than shared lock. - ** (2) SQLite never explicitly requests a pendig lock. - ** (3) A shared lock is always held when a reserve lock is requested. - */ - assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); - assert( eFileLock!=PENDING_LOCK ); - assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); - - /* This mutex is needed because pFile->pInode is shared across threads - */ - unixEnterMutex(); - pInode = pFile->pInode; - - /* If some thread using this PID has a lock via a different unixFile* - ** handle that precludes the requested lock, return BUSY. - */ - if( (pFile->eFileLock!=pInode->eFileLock && - (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) - ){ - rc = SQLITE_BUSY; - goto end_lock; - } - - /* If a SHARED lock is requested, and some thread using this PID already - ** has a SHARED or RESERVED lock, then increment reference counts and - ** return SQLITE_OK. - */ - if( eFileLock==SHARED_LOCK && - (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ - assert( eFileLock==SHARED_LOCK ); - assert( pFile->eFileLock==0 ); - assert( pInode->nShared>0 ); - pFile->eFileLock = SHARED_LOCK; - pInode->nShared++; - pInode->nLock++; - goto end_lock; - } - - - /* A PENDING lock is needed before acquiring a SHARED lock and before - ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will - ** be released. - */ - lock.l_len = 1L; - lock.l_whence = SEEK_SET; - if( eFileLock==SHARED_LOCK - || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLocklastErrno = tErrno; - } - goto end_lock; - } - } - - - /* If control gets to this point, then actually go ahead and make - ** operating system calls for the specified lock. - */ - if( eFileLock==SHARED_LOCK ){ - assert( pInode->nShared==0 ); - assert( pInode->eFileLock==0 ); - assert( rc==SQLITE_OK ); - - /* Now get the read-lock */ - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( unixFileLock(pFile, &lock) ){ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - } - - /* Drop the temporary PENDING lock */ - lock.l_start = PENDING_BYTE; - lock.l_len = 1L; - lock.l_type = F_UNLCK; - if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){ - /* This could happen with a network mount */ - tErrno = errno; - rc = SQLITE_IOERR_UNLOCK; - } - - if( rc ){ - if( rc!=SQLITE_BUSY ){ - pFile->lastErrno = tErrno; - } - goto end_lock; - }else{ - pFile->eFileLock = SHARED_LOCK; - pInode->nLock++; - pInode->nShared = 1; - } - }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ - /* We are trying for an exclusive lock but another thread in this - ** same process is still holding a shared lock. */ - rc = SQLITE_BUSY; - }else{ - /* The request was for a RESERVED or EXCLUSIVE lock. It is - ** assumed that there is a SHARED or greater lock on the file - ** already. - */ - assert( 0!=pFile->eFileLock ); - lock.l_type = F_WRLCK; - - assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK ); - if( eFileLock==RESERVED_LOCK ){ - lock.l_start = RESERVED_BYTE; - lock.l_len = 1L; - }else{ - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - } - - if( unixFileLock(pFile, &lock) ){ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( rc!=SQLITE_BUSY ){ - pFile->lastErrno = tErrno; - } - } - } - - -#ifdef SQLITE_DEBUG - /* Set up the transaction-counter change checking flags when - ** transitioning from a SHARED to a RESERVED lock. The change - ** from SHARED to RESERVED marks the beginning of a normal - ** write operation (not a hot journal rollback). - */ - if( rc==SQLITE_OK - && pFile->eFileLock<=SHARED_LOCK - && eFileLock==RESERVED_LOCK - ){ - pFile->transCntrChng = 0; - pFile->dbUpdate = 0; - pFile->inNormalWrite = 1; - } -#endif - - - if( rc==SQLITE_OK ){ - pFile->eFileLock = eFileLock; - pInode->eFileLock = eFileLock; - }else if( eFileLock==EXCLUSIVE_LOCK ){ - pFile->eFileLock = PENDING_LOCK; - pInode->eFileLock = PENDING_LOCK; - } - -end_lock: - unixLeaveMutex(); - OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock), - rc==SQLITE_OK ? "ok" : "failed")); - return rc; -} - -/* -** Add the file descriptor used by file handle pFile to the corresponding -** pUnused list. -*/ -static void setPendingFd(unixFile *pFile){ - unixInodeInfo *pInode = pFile->pInode; - UnixUnusedFd *p = pFile->pUnused; - p->pNext = pInode->pUnused; - pInode->pUnused = p; - pFile->h = -1; - pFile->pUnused = 0; -} - -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -** -** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED -** the byte range is divided into 2 parts and the first part is unlocked then -** set to a read lock, then the other part is simply unlocked. This works -** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to -** remove the write lock on a region when a read lock is set. -*/ -static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode; - struct flock lock; - int rc = SQLITE_OK; - - assert( pFile ); - OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, - pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, - getpid())); - - assert( eFileLock<=SHARED_LOCK ); - if( pFile->eFileLock<=eFileLock ){ - return SQLITE_OK; - } - unixEnterMutex(); - pInode = pFile->pInode; - assert( pInode->nShared!=0 ); - if( pFile->eFileLock>SHARED_LOCK ){ - assert( pInode->eFileLock==pFile->eFileLock ); - -#ifdef SQLITE_DEBUG - /* When reducing a lock such that other processes can start - ** reading the database file again, make sure that the - ** transaction counter was updated if any part of the database - ** file changed. If the transaction counter is not updated, - ** other connections to the same file might not realize that - ** the file has changed and hence might not know to flush their - ** cache. The use of a stale cache can lead to database corruption. - */ - pFile->inNormalWrite = 0; -#endif - - /* downgrading to a shared lock on NFS involves clearing the write lock - ** before establishing the readlock - to avoid a race condition we downgrade - ** the lock in 2 blocks, so that part of the range will be covered by a - ** write lock until the rest is covered by a read lock: - ** 1: [WWWWW] - ** 2: [....W] - ** 3: [RRRRW] - ** 4: [RRRR.] - */ - if( eFileLock==SHARED_LOCK ){ - -#if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE - (void)handleNFSUnlock; - assert( handleNFSUnlock==0 ); -#endif -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE - if( handleNFSUnlock ){ - int tErrno; /* Error code from system call errors */ - off_t divSize = SHARED_SIZE - 1; - - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = divSize; - if( unixFileLock(pFile, &lock)==(-1) ){ - tErrno = errno; - rc = SQLITE_IOERR_UNLOCK; - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_unlock; - } - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = divSize; - if( unixFileLock(pFile, &lock)==(-1) ){ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_unlock; - } - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST+divSize; - lock.l_len = SHARED_SIZE-divSize; - if( unixFileLock(pFile, &lock)==(-1) ){ - tErrno = errno; - rc = SQLITE_IOERR_UNLOCK; - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_unlock; - } - }else -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ - { - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( unixFileLock(pFile, &lock) ){ - /* In theory, the call to unixFileLock() cannot fail because another - ** process is holding an incompatible lock. If it does, this - ** indicates that the other process is not following the locking - ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning - ** SQLITE_BUSY would confuse the upper layer (in practice it causes - ** an assert to fail). */ - rc = SQLITE_IOERR_RDLOCK; - pFile->lastErrno = errno; - goto end_unlock; - } - } - } - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = PENDING_BYTE; - lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); - if( unixFileLock(pFile, &lock)==0 ){ - pInode->eFileLock = SHARED_LOCK; - }else{ - rc = SQLITE_IOERR_UNLOCK; - pFile->lastErrno = errno; - goto end_unlock; - } - } - if( eFileLock==NO_LOCK ){ - /* Decrement the shared lock counter. Release the lock using an - ** OS call only when all threads in this same process have released - ** the lock. - */ - pInode->nShared--; - if( pInode->nShared==0 ){ - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = lock.l_len = 0L; - if( unixFileLock(pFile, &lock)==0 ){ - pInode->eFileLock = NO_LOCK; - }else{ - rc = SQLITE_IOERR_UNLOCK; - pFile->lastErrno = errno; - pInode->eFileLock = NO_LOCK; - pFile->eFileLock = NO_LOCK; - } - } - - /* Decrement the count of locks against this same file. When the - ** count reaches zero, close any other file descriptors whose close - ** was deferred because of outstanding locks. - */ - pInode->nLock--; - assert( pInode->nLock>=0 ); - if( pInode->nLock==0 ){ - closePendingFds(pFile); - } - } - -end_unlock: - unixLeaveMutex(); - if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; - return rc; -} - -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -*/ -static int unixUnlock(sqlite3_file *id, int eFileLock){ - return posixUnlock(id, eFileLock, 0); -} - -/* -** This function performs the parts of the "close file" operation -** common to all locking schemes. It closes the directory and file -** handles, if they are valid, and sets all fields of the unixFile -** structure to 0. -** -** It is *not* necessary to hold the mutex when this routine is called, -** even on VxWorks. A mutex will be acquired on VxWorks by the -** vxworksReleaseFileId() routine. -*/ -static int closeUnixFile(sqlite3_file *id){ - unixFile *pFile = (unixFile*)id; - if( pFile->h>=0 ){ - robust_close(pFile, pFile->h, __LINE__); - pFile->h = -1; - } -#if OS_VXWORKS - if( pFile->pId ){ - if( pFile->ctrlFlags & UNIXFILE_DELETE ){ - osUnlink(pFile->pId->zCanonicalName); - } - vxworksReleaseFileId(pFile->pId); - pFile->pId = 0; - } -#endif - OSTRACE(("CLOSE %-3d\n", pFile->h)); - OpenCounter(-1); - sqlite3_free(pFile->pUnused); - memset(pFile, 0, sizeof(unixFile)); - return SQLITE_OK; -} - -/* -** Close a file. -*/ -static int unixClose(sqlite3_file *id){ - int rc = SQLITE_OK; - unixFile *pFile = (unixFile *)id; - unixUnlock(id, NO_LOCK); - unixEnterMutex(); - - /* unixFile.pInode is always valid here. Otherwise, a different close - ** routine (e.g. nolockClose()) would be called instead. - */ - assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); - if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->pUnused list. It will be automatically closed - ** when the last lock is cleared. - */ - setPendingFd(pFile); - } - releaseInodeInfo(pFile); - rc = closeUnixFile(id); - unixLeaveMutex(); - return rc; -} - -/************** End of the posix advisory lock implementation ***************** -******************************************************************************/ - -/****************************************************************************** -****************************** No-op Locking ********************************** -** -** Of the various locking implementations available, this is by far the -** simplest: locking is ignored. No attempt is made to lock the database -** file for reading or writing. -** -** This locking mode is appropriate for use on read-only databases -** (ex: databases that are burned into CD-ROM, for example.) It can -** also be used if the application employs some external mechanism to -** prevent simultaneous access of the same database by two or more -** database connections. But there is a serious risk of database -** corruption if this locking mode is used in situations where multiple -** database connections are accessing the same database file at the same -** time and one or more of those connections are writing. -*/ - -static int nolockCheckReservedLock(sqlite3_file *NotUsed, int *pResOut){ - UNUSED_PARAMETER(NotUsed); - *pResOut = 0; - return SQLITE_OK; -} -static int nolockLock(sqlite3_file *NotUsed, int NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return SQLITE_OK; -} -static int nolockUnlock(sqlite3_file *NotUsed, int NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return SQLITE_OK; -} - -/* -** Close the file. -*/ -static int nolockClose(sqlite3_file *id) { - return closeUnixFile(id); -} - -/******************* End of the no-op lock implementation ********************* -******************************************************************************/ - -/****************************************************************************** -************************* Begin dot-file Locking ****************************** -** -** The dotfile locking implementation uses the existence of separate lock -** files (really a directory) to control access to the database. This works -** on just about every filesystem imaginable. But there are serious downsides: -** -** (1) There is zero concurrency. A single reader blocks all other -** connections from reading or writing the database. -** -** (2) An application crash or power loss can leave stale lock files -** sitting around that need to be cleared manually. -** -** Nevertheless, a dotlock is an appropriate locking mode for use if no -** other locking strategy is available. -** -** Dotfile locking works by creating a subdirectory in the same directory as -** the database and with the same name but with a ".lock" extension added. -** The existence of a lock directory implies an EXCLUSIVE lock. All other -** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. -*/ - -/* -** The file suffix added to the data base filename in order to create the -** lock directory. -*/ -#define DOTLOCK_SUFFIX ".lock" - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -** -** In dotfile locking, either a lock exists or it does not. So in this -** variation of CheckReservedLock(), *pResOut is set to true if any lock -** is held on the file and false if the file is unlocked. -*/ -static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { - int rc = SQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - - assert( pFile ); - - /* Check if a thread in this process holds such a lock */ - if( pFile->eFileLock>SHARED_LOCK ){ - /* Either this connection or some other connection in the same process - ** holds a lock on the file. No need to check further. */ - reserved = 1; - }else{ - /* The lock is held if and only if the lockfile exists */ - const char *zLockFile = (const char*)pFile->lockingContext; - reserved = osAccess(zLockFile, 0)==0; - } - OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); - *pResOut = reserved; - return rc; -} - -/* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. Use the sqlite3OsUnlock() -** routine to lower a locking level. -** -** With dotfile locking, we really only support state (4): EXCLUSIVE. -** But we track the other locking levels internally. -*/ -static int dotlockLock(sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - char *zLockFile = (char *)pFile->lockingContext; - int rc = SQLITE_OK; - - - /* If we have any lock, then the lock file already exists. All we have - ** to do is adjust our internal record of the lock level. - */ - if( pFile->eFileLock > NO_LOCK ){ - pFile->eFileLock = eFileLock; - /* Always update the timestamp on the old file */ -#ifdef HAVE_UTIME - utime(zLockFile, NULL); -#else - utimes(zLockFile, NULL); -#endif - return SQLITE_OK; - } - - /* grab an exclusive lock */ - rc = osMkdir(zLockFile, 0777); - if( rc<0 ){ - /* failed to open/create the lock directory */ - int tErrno = errno; - if( EEXIST == tErrno ){ - rc = SQLITE_BUSY; - } else { - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - } - return rc; - } - - /* got it, set the type and return ok */ - pFile->eFileLock = eFileLock; - return rc; -} - -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -** -** When the locking level reaches NO_LOCK, delete the lock file. -*/ -static int dotlockUnlock(sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - char *zLockFile = (char *)pFile->lockingContext; - int rc; - - assert( pFile ); - OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock, - pFile->eFileLock, getpid())); - assert( eFileLock<=SHARED_LOCK ); - - /* no-op if possible */ - if( pFile->eFileLock==eFileLock ){ - return SQLITE_OK; - } - - /* To downgrade to shared, simply update our internal notion of the - ** lock state. No need to mess with the file on disk. - */ - if( eFileLock==SHARED_LOCK ){ - pFile->eFileLock = SHARED_LOCK; - return SQLITE_OK; - } - - /* To fully unlock the database, delete the lock file */ - assert( eFileLock==NO_LOCK ); - rc = osRmdir(zLockFile); - if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile); - if( rc<0 ){ - int tErrno = errno; - rc = 0; - if( ENOENT != tErrno ){ - rc = SQLITE_IOERR_UNLOCK; - } - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - return rc; - } - pFile->eFileLock = NO_LOCK; - return SQLITE_OK; -} - -/* -** Close a file. Make sure the lock has been released before closing. -*/ -static int dotlockClose(sqlite3_file *id) { - int rc = SQLITE_OK; - if( id ){ - unixFile *pFile = (unixFile*)id; - dotlockUnlock(id, NO_LOCK); - sqlite3_free(pFile->lockingContext); - rc = closeUnixFile(id); - } - return rc; -} -/****************** End of the dot-file lock implementation ******************* -******************************************************************************/ - -/****************************************************************************** -************************** Begin flock Locking ******************************** -** -** Use the flock() system call to do file locking. -** -** flock() locking is like dot-file locking in that the various -** fine-grain locking levels supported by SQLite are collapsed into -** a single exclusive lock. In other words, SHARED, RESERVED, and -** PENDING locks are the same thing as an EXCLUSIVE lock. SQLite -** still works when you do this, but concurrency is reduced since -** only a single process can be reading the database at a time. -** -** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off or if -** compiling for VXWORKS. -*/ -#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS - -/* -** Retry flock() calls that fail with EINTR -*/ -#ifdef EINTR -static int robust_flock(int fd, int op){ - int rc; - do{ rc = flock(fd,op); }while( rc<0 && errno==EINTR ); - return rc; -} -#else -# define robust_flock(a,b) flock(a,b) -#endif - - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -*/ -static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ - int rc = SQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - - assert( pFile ); - - /* Check if a thread in this process holds such a lock */ - if( pFile->eFileLock>SHARED_LOCK ){ - reserved = 1; - } - - /* Otherwise see if some other process holds it. */ - if( !reserved ){ - /* attempt to get the lock */ - int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); - if( !lrc ){ - /* got the lock, unlock it */ - lrc = robust_flock(pFile->h, LOCK_UN); - if ( lrc ) { - int tErrno = errno; - /* unlock failed with an error */ - lrc = SQLITE_IOERR_UNLOCK; - if( IS_LOCK_ERROR(lrc) ){ - pFile->lastErrno = tErrno; - rc = lrc; - } - } - } else { - int tErrno = errno; - reserved = 1; - /* someone else might have it reserved */ - lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(lrc) ){ - pFile->lastErrno = tErrno; - rc = lrc; - } - } - } - OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); - -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ - rc = SQLITE_OK; - reserved=1; - } -#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ - *pResOut = reserved; - return rc; -} - -/* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** flock() only really support EXCLUSIVE locks. We track intermediate -** lock states in the sqlite3_file structure, but all locks SHARED or -** above are really EXCLUSIVE locks and exclude all other processes from -** access the file. -** -** This routine will only increase a lock. Use the sqlite3OsUnlock() -** routine to lower a locking level. -*/ -static int flockLock(sqlite3_file *id, int eFileLock) { - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - - assert( pFile ); - - /* if we already have a lock, it is exclusive. - ** Just adjust level and punt on outta here. */ - if (pFile->eFileLock > NO_LOCK) { - pFile->eFileLock = eFileLock; - return SQLITE_OK; - } - - /* grab an exclusive lock */ - - if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) { - int tErrno = errno; - /* didn't get, must be busy */ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - } else { - /* got it, set the type and return ok */ - pFile->eFileLock = eFileLock; - } - OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock), - rc==SQLITE_OK ? "ok" : "failed")); -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ - rc = SQLITE_BUSY; - } -#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ - return rc; -} - - -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -*/ -static int flockUnlock(sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - - assert( pFile ); - OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock, - pFile->eFileLock, getpid())); - assert( eFileLock<=SHARED_LOCK ); - - /* no-op if possible */ - if( pFile->eFileLock==eFileLock ){ - return SQLITE_OK; - } - - /* shared can just be set because we always have an exclusive */ - if (eFileLock==SHARED_LOCK) { - pFile->eFileLock = eFileLock; - return SQLITE_OK; - } - - /* no, really, unlock. */ - if( robust_flock(pFile->h, LOCK_UN) ){ -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - return SQLITE_OK; -#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ - return SQLITE_IOERR_UNLOCK; - }else{ - pFile->eFileLock = NO_LOCK; - return SQLITE_OK; - } -} - -/* -** Close a file. -*/ -static int flockClose(sqlite3_file *id) { - int rc = SQLITE_OK; - if( id ){ - flockUnlock(id, NO_LOCK); - rc = closeUnixFile(id); - } - return rc; -} - -#endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */ - -/******************* End of the flock lock implementation ********************* -******************************************************************************/ - -/****************************************************************************** -************************ Begin Named Semaphore Locking ************************ -** -** Named semaphore locking is only supported on VxWorks. -** -** Semaphore locking is like dot-lock and flock in that it really only -** supports EXCLUSIVE locking. Only a single process can read or write -** the database file at a time. This reduces potential concurrency, but -** makes the lock implementation much easier. -*/ -#if OS_VXWORKS - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -*/ -static int semCheckReservedLock(sqlite3_file *id, int *pResOut) { - int rc = SQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - - assert( pFile ); - - /* Check if a thread in this process holds such a lock */ - if( pFile->eFileLock>SHARED_LOCK ){ - reserved = 1; - } - - /* Otherwise see if some other process holds it. */ - if( !reserved ){ - sem_t *pSem = pFile->pInode->pSem; - struct stat statBuf; - - if( sem_trywait(pSem)==-1 ){ - int tErrno = errno; - if( EAGAIN != tErrno ){ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); - pFile->lastErrno = tErrno; - } else { - /* someone else has the lock when we are in NO_LOCK */ - reserved = (pFile->eFileLock < SHARED_LOCK); - } - }else{ - /* we could have it if we want it */ - sem_post(pSem); - } - } - OSTRACE(("TEST WR-LOCK %d %d %d (sem)\n", pFile->h, rc, reserved)); - - *pResOut = reserved; - return rc; -} - -/* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** Semaphore locks only really support EXCLUSIVE locks. We track intermediate -** lock states in the sqlite3_file structure, but all locks SHARED or -** above are really EXCLUSIVE locks and exclude all other processes from -** access the file. -** -** This routine will only increase a lock. Use the sqlite3OsUnlock() -** routine to lower a locking level. -*/ -static int semLock(sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - int fd; - sem_t *pSem = pFile->pInode->pSem; - int rc = SQLITE_OK; - - /* if we already have a lock, it is exclusive. - ** Just adjust level and punt on outta here. */ - if (pFile->eFileLock > NO_LOCK) { - pFile->eFileLock = eFileLock; - rc = SQLITE_OK; - goto sem_end_lock; - } - - /* lock semaphore now but bail out when already locked. */ - if( sem_trywait(pSem)==-1 ){ - rc = SQLITE_BUSY; - goto sem_end_lock; - } - - /* got it, set the type and return ok */ - pFile->eFileLock = eFileLock; - - sem_end_lock: - return rc; -} - -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -*/ -static int semUnlock(sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - sem_t *pSem = pFile->pInode->pSem; - - assert( pFile ); - assert( pSem ); - OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock, - pFile->eFileLock, getpid())); - assert( eFileLock<=SHARED_LOCK ); - - /* no-op if possible */ - if( pFile->eFileLock==eFileLock ){ - return SQLITE_OK; - } - - /* shared can just be set because we always have an exclusive */ - if (eFileLock==SHARED_LOCK) { - pFile->eFileLock = eFileLock; - return SQLITE_OK; - } - - /* no, really unlock. */ - if ( sem_post(pSem)==-1 ) { - int rc, tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - return rc; - } - pFile->eFileLock = NO_LOCK; - return SQLITE_OK; -} - -/* - ** Close a file. - */ -static int semClose(sqlite3_file *id) { - if( id ){ - unixFile *pFile = (unixFile*)id; - semUnlock(id, NO_LOCK); - assert( pFile ); - unixEnterMutex(); - releaseInodeInfo(pFile); - unixLeaveMutex(); - closeUnixFile(id); - } - return SQLITE_OK; -} - -#endif /* OS_VXWORKS */ -/* -** Named semaphore locking is only available on VxWorks. -** -*************** End of the named semaphore lock implementation **************** -******************************************************************************/ - - -/****************************************************************************** -*************************** Begin AFP Locking ********************************* -** -** AFP is the Apple Filing Protocol. AFP is a network filesystem found -** on Apple Macintosh computers - both OS9 and OSX. -** -** Third-party implementations of AFP are available. But this code here -** only works on OSX. -*/ - -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -/* -** The afpLockingContext structure contains all afp lock specific state -*/ -typedef struct afpLockingContext afpLockingContext; -struct afpLockingContext { - int reserved; - const char *dbPath; /* Name of the open file */ -}; - -struct ByteRangeLockPB2 -{ - unsigned long long offset; /* offset to first byte to lock */ - unsigned long long length; /* nbr of bytes to lock */ - unsigned long long retRangeStart; /* nbr of 1st byte locked if successful */ - unsigned char unLockFlag; /* 1 = unlock, 0 = lock */ - unsigned char startEndFlag; /* 1=rel to end of fork, 0=rel to start */ - int fd; /* file desc to assoc this lock with */ -}; - -#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2) - -/* -** This is a utility for setting or clearing a bit-range lock on an -** AFP filesystem. -** -** Return SQLITE_OK on success, SQLITE_BUSY on failure. -*/ -static int afpSetLock( - const char *path, /* Name of the file to be locked or unlocked */ - unixFile *pFile, /* Open file descriptor on path */ - unsigned long long offset, /* First byte to be locked */ - unsigned long long length, /* Number of bytes to lock */ - int setLockFlag /* True to set lock. False to clear lock */ -){ - struct ByteRangeLockPB2 pb; - int err; - - pb.unLockFlag = setLockFlag ? 0 : 1; - pb.startEndFlag = 0; - pb.offset = offset; - pb.length = length; - pb.fd = pFile->h; - - OSTRACE(("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n", - (setLockFlag?"ON":"OFF"), pFile->h, (pb.fd==-1?"[testval-1]":""), - offset, length)); - err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0); - if ( err==-1 ) { - int rc; - int tErrno = errno; - OSTRACE(("AFPSETLOCK failed to fsctl() '%s' %d %s\n", - path, tErrno, strerror(tErrno))); -#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS - rc = SQLITE_BUSY; -#else - rc = sqliteErrorFromPosixError(tErrno, - setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); -#endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */ - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - return rc; - } else { - return SQLITE_OK; - } -} - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -*/ -static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){ - int rc = SQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; - afpLockingContext *context; - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - - assert( pFile ); - context = (afpLockingContext *) pFile->lockingContext; - if( context->reserved ){ - *pResOut = 1; - return SQLITE_OK; - } - unixEnterMutex(); /* Because pFile->pInode is shared across threads */ - - /* Check if a thread in this process holds such a lock */ - if( pFile->pInode->eFileLock>SHARED_LOCK ){ - reserved = 1; - } - - /* Otherwise see if some other process holds it. - */ - if( !reserved ){ - /* lock the RESERVED byte */ - int lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); - if( SQLITE_OK==lrc ){ - /* if we succeeded in taking the reserved lock, unlock it to restore - ** the original state */ - lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); - } else { - /* if we failed to get the lock then someone else must have it */ - reserved = 1; - } - if( IS_LOCK_ERROR(lrc) ){ - rc=lrc; - } - } - - unixLeaveMutex(); - OSTRACE(("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved)); - - *pResOut = reserved; - return rc; -} - -/* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. Use the sqlite3OsUnlock() -** routine to lower a locking level. -*/ -static int afpLock(sqlite3_file *id, int eFileLock){ - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode = pFile->pInode; - afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; - - assert( pFile ); - OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h, - azFileLock(eFileLock), azFileLock(pFile->eFileLock), - azFileLock(pInode->eFileLock), pInode->nShared , getpid())); - - /* If there is already a lock of this type or more restrictive on the - ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as - ** unixEnterMutex() hasn't been called yet. - */ - if( pFile->eFileLock>=eFileLock ){ - OSTRACE(("LOCK %d %s ok (already held) (afp)\n", pFile->h, - azFileLock(eFileLock))); - return SQLITE_OK; - } - - /* Make sure the locking sequence is correct - ** (1) We never move from unlocked to anything higher than shared lock. - ** (2) SQLite never explicitly requests a pendig lock. - ** (3) A shared lock is always held when a reserve lock is requested. - */ - assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); - assert( eFileLock!=PENDING_LOCK ); - assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); - - /* This mutex is needed because pFile->pInode is shared across threads - */ - unixEnterMutex(); - pInode = pFile->pInode; - - /* If some thread using this PID has a lock via a different unixFile* - ** handle that precludes the requested lock, return BUSY. - */ - if( (pFile->eFileLock!=pInode->eFileLock && - (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) - ){ - rc = SQLITE_BUSY; - goto afp_end_lock; - } - - /* If a SHARED lock is requested, and some thread using this PID already - ** has a SHARED or RESERVED lock, then increment reference counts and - ** return SQLITE_OK. - */ - if( eFileLock==SHARED_LOCK && - (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ - assert( eFileLock==SHARED_LOCK ); - assert( pFile->eFileLock==0 ); - assert( pInode->nShared>0 ); - pFile->eFileLock = SHARED_LOCK; - pInode->nShared++; - pInode->nLock++; - goto afp_end_lock; - } - - /* A PENDING lock is needed before acquiring a SHARED lock and before - ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will - ** be released. - */ - if( eFileLock==SHARED_LOCK - || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLockdbPath, pFile, PENDING_BYTE, 1, 1); - if (failed) { - rc = failed; - goto afp_end_lock; - } - } - - /* If control gets to this point, then actually go ahead and make - ** operating system calls for the specified lock. - */ - if( eFileLock==SHARED_LOCK ){ - int lrc1, lrc2, lrc1Errno = 0; - long lk, mask; - - assert( pInode->nShared==0 ); - assert( pInode->eFileLock==0 ); - - mask = (sizeof(long)==8) ? LARGEST_INT64 : 0x7fffffff; - /* Now get the read-lock SHARED_LOCK */ - /* note that the quality of the randomness doesn't matter that much */ - lk = random(); - pInode->sharedByte = (lk & mask)%(SHARED_SIZE - 1); - lrc1 = afpSetLock(context->dbPath, pFile, - SHARED_FIRST+pInode->sharedByte, 1, 1); - if( IS_LOCK_ERROR(lrc1) ){ - lrc1Errno = pFile->lastErrno; - } - /* Drop the temporary PENDING lock */ - lrc2 = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); - - if( IS_LOCK_ERROR(lrc1) ) { - pFile->lastErrno = lrc1Errno; - rc = lrc1; - goto afp_end_lock; - } else if( IS_LOCK_ERROR(lrc2) ){ - rc = lrc2; - goto afp_end_lock; - } else if( lrc1 != SQLITE_OK ) { - rc = lrc1; - } else { - pFile->eFileLock = SHARED_LOCK; - pInode->nLock++; - pInode->nShared = 1; - } - }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ - /* We are trying for an exclusive lock but another thread in this - ** same process is still holding a shared lock. */ - rc = SQLITE_BUSY; - }else{ - /* The request was for a RESERVED or EXCLUSIVE lock. It is - ** assumed that there is a SHARED or greater lock on the file - ** already. - */ - int failed = 0; - assert( 0!=pFile->eFileLock ); - if (eFileLock >= RESERVED_LOCK && pFile->eFileLock < RESERVED_LOCK) { - /* Acquire a RESERVED lock */ - failed = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); - if( !failed ){ - context->reserved = 1; - } - } - if (!failed && eFileLock == EXCLUSIVE_LOCK) { - /* Acquire an EXCLUSIVE lock */ - - /* Remove the shared lock before trying the range. we'll need to - ** reestablish the shared lock if we can't get the afpUnlock - */ - if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST + - pInode->sharedByte, 1, 0)) ){ - int failed2 = SQLITE_OK; - /* now attemmpt to get the exclusive lock range */ - failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, - SHARED_SIZE, 1); - if( failed && (failed2 = afpSetLock(context->dbPath, pFile, - SHARED_FIRST + pInode->sharedByte, 1, 1)) ){ - /* Can't reestablish the shared lock. Sqlite can't deal, this is - ** a critical I/O error - */ - rc = ((failed & SQLITE_IOERR) == SQLITE_IOERR) ? failed2 : - SQLITE_IOERR_LOCK; - goto afp_end_lock; - } - }else{ - rc = failed; - } - } - if( failed ){ - rc = failed; - } - } - - if( rc==SQLITE_OK ){ - pFile->eFileLock = eFileLock; - pInode->eFileLock = eFileLock; - }else if( eFileLock==EXCLUSIVE_LOCK ){ - pFile->eFileLock = PENDING_LOCK; - pInode->eFileLock = PENDING_LOCK; - } - -afp_end_lock: - unixLeaveMutex(); - OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock), - rc==SQLITE_OK ? "ok" : "failed")); - return rc; -} - -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -*/ -static int afpUnlock(sqlite3_file *id, int eFileLock) { - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode; - afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; - int skipShared = 0; -#ifdef SQLITE_TEST - int h = pFile->h; -#endif - - assert( pFile ); - OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, - pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, - getpid())); - - assert( eFileLock<=SHARED_LOCK ); - if( pFile->eFileLock<=eFileLock ){ - return SQLITE_OK; - } - unixEnterMutex(); - pInode = pFile->pInode; - assert( pInode->nShared!=0 ); - if( pFile->eFileLock>SHARED_LOCK ){ - assert( pInode->eFileLock==pFile->eFileLock ); - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); - -#ifdef SQLITE_DEBUG - /* When reducing a lock such that other processes can start - ** reading the database file again, make sure that the - ** transaction counter was updated if any part of the database - ** file changed. If the transaction counter is not updated, - ** other connections to the same file might not realize that - ** the file has changed and hence might not know to flush their - ** cache. The use of a stale cache can lead to database corruption. - */ - assert( pFile->inNormalWrite==0 - || pFile->dbUpdate==0 - || pFile->transCntrChng==1 ); - pFile->inNormalWrite = 0; -#endif - - if( pFile->eFileLock==EXCLUSIVE_LOCK ){ - rc = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0); - if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1) ){ - /* only re-establish the shared lock if necessary */ - int sharedLockByte = SHARED_FIRST+pInode->sharedByte; - rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 1); - } else { - skipShared = 1; - } - } - if( rc==SQLITE_OK && pFile->eFileLock>=PENDING_LOCK ){ - rc = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); - } - if( rc==SQLITE_OK && pFile->eFileLock>=RESERVED_LOCK && context->reserved ){ - rc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); - if( !rc ){ - context->reserved = 0; - } - } - if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1)){ - pInode->eFileLock = SHARED_LOCK; - } - } - if( rc==SQLITE_OK && eFileLock==NO_LOCK ){ - - /* Decrement the shared lock counter. Release the lock using an - ** OS call only when all threads in this same process have released - ** the lock. - */ - unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte; - pInode->nShared--; - if( pInode->nShared==0 ){ - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); - if( !skipShared ){ - rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0); - } - if( !rc ){ - pInode->eFileLock = NO_LOCK; - pFile->eFileLock = NO_LOCK; - } - } - if( rc==SQLITE_OK ){ - pInode->nLock--; - assert( pInode->nLock>=0 ); - if( pInode->nLock==0 ){ - closePendingFds(pFile); - } - } - } - - unixLeaveMutex(); - if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; - return rc; -} - -/* -** Close a file & cleanup AFP specific locking context -*/ -static int afpClose(sqlite3_file *id) { - int rc = SQLITE_OK; - if( id ){ - unixFile *pFile = (unixFile*)id; - afpUnlock(id, NO_LOCK); - unixEnterMutex(); - if( pFile->pInode && pFile->pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->aPending. It will be automatically closed when - ** the last lock is cleared. - */ - setPendingFd(pFile); - } - releaseInodeInfo(pFile); - sqlite3_free(pFile->lockingContext); - rc = closeUnixFile(id); - unixLeaveMutex(); - } - return rc; -} - -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ -/* -** The code above is the AFP lock implementation. The code is specific -** to MacOSX and does not work on other unix platforms. No alternative -** is available. If you don't compile for a mac, then the "unix-afp" -** VFS is not available. -** -********************* End of the AFP lock implementation ********************** -******************************************************************************/ - -/****************************************************************************** -*************************** Begin NFS Locking ********************************/ - -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -/* - ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock - ** must be either NO_LOCK or SHARED_LOCK. - ** - ** If the locking level of the file descriptor is already at or below - ** the requested locking level, this routine is a no-op. - */ -static int nfsUnlock(sqlite3_file *id, int eFileLock){ - return posixUnlock(id, eFileLock, 1); -} - -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ -/* -** The code above is the NFS lock implementation. The code is specific -** to MacOSX and does not work on other unix platforms. No alternative -** is available. -** -********************* End of the NFS lock implementation ********************** -******************************************************************************/ - -/****************************************************************************** -**************** Non-locking sqlite3_file methods ***************************** -** -** The next division contains implementations for all methods of the -** sqlite3_file object other than the locking methods. The locking -** methods were defined in divisions above (one locking method per -** division). Those methods that are common to all locking modes -** are gather together into this division. -*/ - -/* -** Seek to the offset passed as the second argument, then read cnt -** bytes into pBuf. Return the number of bytes actually read. -** -** NB: If you define USE_PREAD or USE_PREAD64, then it might also -** be necessary to define _XOPEN_SOURCE to be 500. This varies from -** one system to another. Since SQLite does not define USE_PREAD -** any any form by default, we will not attempt to define _XOPEN_SOURCE. -** See tickets #2741 and #2681. -** -** To avoid stomping the errno value on a failed read the lastErrno value -** is set before returning. -*/ -static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ - int got; - int prior = 0; -#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) - i64 newOffset; -#endif - TIMER_START; - assert( cnt==(cnt&0x1ffff) ); - cnt &= 0x1ffff; - do{ -#if defined(USE_PREAD) - got = osPread(id->h, pBuf, cnt, offset); - SimulateIOError( got = -1 ); -#elif defined(USE_PREAD64) - got = osPread64(id->h, pBuf, cnt, offset); - SimulateIOError( got = -1 ); -#else - newOffset = lseek(id->h, offset, SEEK_SET); - SimulateIOError( newOffset-- ); - if( newOffset!=offset ){ - if( newOffset == -1 ){ - ((unixFile*)id)->lastErrno = errno; - }else{ - ((unixFile*)id)->lastErrno = 0; - } - return -1; - } - got = osRead(id->h, pBuf, cnt); -#endif - if( got==cnt ) break; - if( got<0 ){ - if( errno==EINTR ){ got = 1; continue; } - prior = 0; - ((unixFile*)id)->lastErrno = errno; - break; - }else if( got>0 ){ - cnt -= got; - offset += got; - prior += got; - pBuf = (void*)(got + (char*)pBuf); - } - }while( got>0 ); - TIMER_END; - OSTRACE(("READ %-3d %5d %7lld %llu\n", - id->h, got+prior, offset-prior, TIMER_ELAPSED)); - return got+prior; -} - -/* -** Read data from a file into a buffer. Return SQLITE_OK if all -** bytes were read successfully and SQLITE_IOERR if anything goes -** wrong. -*/ -static int unixRead( - sqlite3_file *id, - void *pBuf, - int amt, - sqlite3_int64 offset -){ - unixFile *pFile = (unixFile *)id; - int got; - assert( id ); - - /* If this is a database file (not a journal, master-journal or temp - ** file), the bytes in the locking range should never be read or written. */ -#if 0 - assert( pFile->pUnused==0 - || offset>=PENDING_BYTE+512 - || offset+amt<=PENDING_BYTE - ); -#endif - - got = seekAndRead(pFile, offset, pBuf, amt); - if( got==amt ){ - return SQLITE_OK; - }else if( got<0 ){ - /* lastErrno set by seekAndRead */ - return SQLITE_IOERR_READ; - }else{ - pFile->lastErrno = 0; /* not a system error */ - /* Unread parts of the buffer must be zero-filled */ - memset(&((char*)pBuf)[got], 0, amt-got); - return SQLITE_IOERR_SHORT_READ; - } -} - -/* -** Seek to the offset in id->offset then read cnt bytes into pBuf. -** Return the number of bytes actually read. Update the offset. -** -** To avoid stomping the errno value on a failed write the lastErrno value -** is set before returning. -*/ -static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ - int got; -#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) - i64 newOffset; -#endif - assert( cnt==(cnt&0x1ffff) ); - cnt &= 0x1ffff; - TIMER_START; -#if defined(USE_PREAD) - do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); -#elif defined(USE_PREAD64) - do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR); -#else - do{ - newOffset = lseek(id->h, offset, SEEK_SET); - SimulateIOError( newOffset-- ); - if( newOffset!=offset ){ - if( newOffset == -1 ){ - ((unixFile*)id)->lastErrno = errno; - }else{ - ((unixFile*)id)->lastErrno = 0; - } - return -1; - } - got = osWrite(id->h, pBuf, cnt); - }while( got<0 && errno==EINTR ); -#endif - TIMER_END; - if( got<0 ){ - ((unixFile*)id)->lastErrno = errno; - } - - OSTRACE(("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED)); - return got; -} - - -/* -** Write data from a buffer into a file. Return SQLITE_OK on success -** or some other error code on failure. -*/ -static int unixWrite( - sqlite3_file *id, - const void *pBuf, - int amt, - sqlite3_int64 offset -){ - unixFile *pFile = (unixFile*)id; - int wrote = 0; - assert( id ); - assert( amt>0 ); - - /* If this is a database file (not a journal, master-journal or temp - ** file), the bytes in the locking range should never be read or written. */ -#if 0 - assert( pFile->pUnused==0 - || offset>=PENDING_BYTE+512 - || offset+amt<=PENDING_BYTE - ); -#endif - -#ifdef SQLITE_DEBUG - /* If we are doing a normal write to a database file (as opposed to - ** doing a hot-journal rollback or a write to some file other than a - ** normal database file) then record the fact that the database - ** has changed. If the transaction counter is modified, record that - ** fact too. - */ - if( pFile->inNormalWrite ){ - pFile->dbUpdate = 1; /* The database has been modified */ - if( offset<=24 && offset+amt>=27 ){ - int rc; - char oldCntr[4]; - SimulateIOErrorBenign(1); - rc = seekAndRead(pFile, 24, oldCntr, 4); - SimulateIOErrorBenign(0); - if( rc!=4 || memcmp(oldCntr, &((char*)pBuf)[24-offset], 4)!=0 ){ - pFile->transCntrChng = 1; /* The transaction counter has changed */ - } - } - } -#endif - - while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ - amt -= wrote; - offset += wrote; - pBuf = &((char*)pBuf)[wrote]; - } - SimulateIOError(( wrote=(-1), amt=1 )); - SimulateDiskfullError(( wrote=0, amt=1 )); - - if( amt>0 ){ - if( wrote<0 && pFile->lastErrno!=ENOSPC ){ - /* lastErrno set by seekAndWrite */ - return SQLITE_IOERR_WRITE; - }else{ - pFile->lastErrno = 0; /* not a system error */ - return SQLITE_FULL; - } - } - - return SQLITE_OK; -} - -#ifdef SQLITE_TEST -/* -** Count the number of fullsyncs and normal syncs. This is used to test -** that syncs and fullsyncs are occurring at the right times. -*/ -SQLITE_API int sqlite3_sync_count = 0; -SQLITE_API int sqlite3_fullsync_count = 0; -#endif - -/* -** We do not trust systems to provide a working fdatasync(). Some do. -** Others do no. To be safe, we will stick with the (slightly slower) -** fsync(). If you know that your system does support fdatasync() correctly, -** then simply compile with -Dfdatasync=fdatasync -*/ -#if !defined(fdatasync) -# define fdatasync fsync -#endif - -/* -** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not -** the F_FULLFSYNC macro is defined. F_FULLFSYNC is currently -** only available on Mac OS X. But that could change. -*/ -#ifdef F_FULLFSYNC -# define HAVE_FULLFSYNC 1 -#else -# define HAVE_FULLFSYNC 0 -#endif - - -/* -** The fsync() system call does not work as advertised on many -** unix systems. The following procedure is an attempt to make -** it work better. -** -** The SQLITE_NO_SYNC macro disables all fsync()s. This is useful -** for testing when we want to run through the test suite quickly. -** You are strongly advised *not* to deploy with SQLITE_NO_SYNC -** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash -** or power failure will likely corrupt the database file. -** -** SQLite sets the dataOnly flag if the size of the file is unchanged. -** The idea behind dataOnly is that it should only write the file content -** to disk, not the inode. We only set dataOnly if the file size is -** unchanged since the file size is part of the inode. However, -** Ted Ts'o tells us that fdatasync() will also write the inode if the -** file size has changed. The only real difference between fdatasync() -** and fsync(), Ted tells us, is that fdatasync() will not flush the -** inode if the mtime or owner or other inode attributes have changed. -** We only care about the file size, not the other file attributes, so -** as far as SQLite is concerned, an fdatasync() is always adequate. -** So, we always use fdatasync() if it is available, regardless of -** the value of the dataOnly flag. -*/ -static int full_fsync(int fd, int fullSync, int dataOnly){ - int rc; - - /* The following "ifdef/elif/else/" block has the same structure as - ** the one below. It is replicated here solely to avoid cluttering - ** up the real code with the UNUSED_PARAMETER() macros. - */ -#ifdef SQLITE_NO_SYNC - UNUSED_PARAMETER(fd); - UNUSED_PARAMETER(fullSync); - UNUSED_PARAMETER(dataOnly); -#elif HAVE_FULLFSYNC - UNUSED_PARAMETER(dataOnly); -#else - UNUSED_PARAMETER(fullSync); - UNUSED_PARAMETER(dataOnly); -#endif - - /* Record the number of times that we do a normal fsync() and - ** FULLSYNC. This is used during testing to verify that this procedure - ** gets called with the correct arguments. - */ -#ifdef SQLITE_TEST - if( fullSync ) sqlite3_fullsync_count++; - sqlite3_sync_count++; -#endif - - /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a - ** no-op - */ -#ifdef SQLITE_NO_SYNC - rc = SQLITE_OK; -#elif HAVE_FULLFSYNC - if( fullSync ){ - rc = osFcntl(fd, F_FULLFSYNC, 0); - }else{ - rc = 1; - } - /* If the FULLFSYNC failed, fall back to attempting an fsync(). - ** It shouldn't be possible for fullfsync to fail on the local - ** file system (on OSX), so failure indicates that FULLFSYNC - ** isn't supported for this file system. So, attempt an fsync - ** and (for now) ignore the overhead of a superfluous fcntl call. - ** It'd be better to detect fullfsync support once and avoid - ** the fcntl call every time sync is called. - */ - if( rc ) rc = fsync(fd); - -#elif defined(__APPLE__) - /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly - ** so currently we default to the macro that redefines fdatasync to fsync - */ - rc = fsync(fd); -#else - rc = fdatasync(fd); -#if OS_VXWORKS - if( rc==-1 && errno==ENOTSUP ){ - rc = fsync(fd); - } -#endif /* OS_VXWORKS */ -#endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */ - - if( OS_VXWORKS && rc!= -1 ){ - rc = 0; - } - return rc; -} - -/* -** Open a file descriptor to the directory containing file zFilename. -** If successful, *pFd is set to the opened file descriptor and -** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM -** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined -** value. -** -** The directory file descriptor is used for only one thing - to -** fsync() a directory to make sure file creation and deletion events -** are flushed to disk. Such fsyncs are not needed on newer -** journaling filesystems, but are required on older filesystems. -** -** This routine can be overridden using the xSetSysCall interface. -** The ability to override this routine was added in support of the -** chromium sandbox. Opening a directory is a security risk (we are -** told) so making it overrideable allows the chromium sandbox to -** replace this routine with a harmless no-op. To make this routine -** a no-op, replace it with a stub that returns SQLITE_OK but leaves -** *pFd set to a negative number. -** -** If SQLITE_OK is returned, the caller is responsible for closing -** the file descriptor *pFd using close(). -*/ -static int openDirectory(const char *zFilename, int *pFd){ - int ii; - int fd = -1; - char zDirname[MAX_PATHNAME+1]; - - sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename); - for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--); - if( ii>0 ){ - zDirname[ii] = '\0'; - fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); - if( fd>=0 ){ - OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); - } - } - *pFd = fd; - return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname)); -} - -/* -** Make sure all writes to a particular file are committed to disk. -** -** If dataOnly==0 then both the file itself and its metadata (file -** size, access time, etc) are synced. If dataOnly!=0 then only the -** file data is synced. -** -** Under Unix, also make sure that the directory entry for the file -** has been created by fsync-ing the directory that contains the file. -** If we do not do this and we encounter a power failure, the directory -** entry for the journal might not exist after we reboot. The next -** SQLite to access the file will not know that the journal exists (because -** the directory entry for the journal was never created) and the transaction -** will not roll back - possibly leading to database corruption. -*/ -static int unixSync(sqlite3_file *id, int flags){ - int rc; - unixFile *pFile = (unixFile*)id; - - int isDataOnly = (flags&SQLITE_SYNC_DATAONLY); - int isFullsync = (flags&0x0F)==SQLITE_SYNC_FULL; - - /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ - assert((flags&0x0F)==SQLITE_SYNC_NORMAL - || (flags&0x0F)==SQLITE_SYNC_FULL - ); - - /* Unix cannot, but some systems may return SQLITE_FULL from here. This - ** line is to test that doing so does not cause any problems. - */ - SimulateDiskfullError( return SQLITE_FULL ); - - assert( pFile ); - OSTRACE(("SYNC %-3d\n", pFile->h)); - rc = full_fsync(pFile->h, isFullsync, isDataOnly); - SimulateIOError( rc=1 ); - if( rc ){ - pFile->lastErrno = errno; - return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); - } - - /* Also fsync the directory containing the file if the DIRSYNC flag - ** is set. This is a one-time occurrence. Many systems (examples: AIX) - ** are unable to fsync a directory, so ignore errors on the fsync. - */ - if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){ - int dirfd; - OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath, - HAVE_FULLFSYNC, isFullsync)); - rc = osOpenDirectory(pFile->zPath, &dirfd); - if( rc==SQLITE_OK && dirfd>=0 ){ - full_fsync(dirfd, 0, 0); - robust_close(pFile, dirfd, __LINE__); - }else if( rc==SQLITE_CANTOPEN ){ - rc = SQLITE_OK; - } - pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC; - } - return rc; -} - -/* -** Truncate an open file to a specified size -*/ -static int unixTruncate(sqlite3_file *id, i64 nByte){ - unixFile *pFile = (unixFile *)id; - int rc; - assert( pFile ); - SimulateIOError( return SQLITE_IOERR_TRUNCATE ); - - /* If the user has configured a chunk-size for this file, truncate the - ** file so that it consists of an integer number of chunks (i.e. the - ** actual file size after the operation may be larger than the requested - ** size). - */ - if( pFile->szChunk>0 ){ - nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; - } - - rc = robust_ftruncate(pFile->h, (off_t)nByte); - if( rc ){ - pFile->lastErrno = errno; - return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); - }else{ -#ifdef SQLITE_DEBUG - /* If we are doing a normal write to a database file (as opposed to - ** doing a hot-journal rollback or a write to some file other than a - ** normal database file) and we truncate the file to zero length, - ** that effectively updates the change counter. This might happen - ** when restoring a database using the backup API from a zero-length - ** source. - */ - if( pFile->inNormalWrite && nByte==0 ){ - pFile->transCntrChng = 1; - } -#endif - - return SQLITE_OK; - } -} - -/* -** Determine the current size of a file in bytes -*/ -static int unixFileSize(sqlite3_file *id, i64 *pSize){ - int rc; - struct stat buf; - assert( id ); - rc = osFstat(((unixFile*)id)->h, &buf); - SimulateIOError( rc=1 ); - if( rc!=0 ){ - ((unixFile*)id)->lastErrno = errno; - return SQLITE_IOERR_FSTAT; - } - *pSize = buf.st_size; - - /* When opening a zero-size database, the findInodeInfo() procedure - ** writes a single byte into that file in order to work around a bug - ** in the OS-X msdos filesystem. In order to avoid problems with upper - ** layers, we need to report this file size as zero even though it is - ** really 1. Ticket #3260. - */ - if( *pSize==1 ) *pSize = 0; - - - return SQLITE_OK; -} - -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) -/* -** Handler for proxy-locking file-control verbs. Defined below in the -** proxying locking division. -*/ -static int proxyFileControl(sqlite3_file*,int,void*); -#endif - -/* -** This function is called to handle the SQLITE_FCNTL_SIZE_HINT -** file-control operation. Enlarge the database to nBytes in size -** (rounded up to the next chunk-size). If the database is already -** nBytes or larger, this routine is a no-op. -*/ -static int fcntlSizeHint(unixFile *pFile, i64 nByte){ - if( pFile->szChunk>0 ){ - i64 nSize; /* Required file size */ - struct stat buf; /* Used to hold return values of fstat() */ - - if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; - - nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; - if( nSize>(i64)buf.st_size ){ - -#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE - /* The code below is handling the return value of osFallocate() - ** correctly. posix_fallocate() is defined to "returns zero on success, - ** or an error number on failure". See the manpage for details. */ - int err; - do{ - err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); - }while( err==EINTR ); - if( err ) return SQLITE_IOERR_WRITE; -#else - /* If the OS does not have posix_fallocate(), fake it. First use - ** ftruncate() to set the file size, then write a single byte to - ** the last byte in each block within the extended region. This - ** is the same technique used by glibc to implement posix_fallocate() - ** on systems that do not have a real fallocate() system call. - */ - int nBlk = buf.st_blksize; /* File-system block size */ - i64 iWrite; /* Next offset to write to */ - - if( robust_ftruncate(pFile->h, nSize) ){ - pFile->lastErrno = errno; - return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); - } - iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; - while( iWritectrlFlags is set. -** -** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. -*/ -static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ - if( *pArg<0 ){ - *pArg = (pFile->ctrlFlags & mask)!=0; - }else if( (*pArg)==0 ){ - pFile->ctrlFlags &= ~mask; - }else{ - pFile->ctrlFlags |= mask; - } -} - -/* Forward declaration */ -static int unixGetTempname(int nBuf, char *zBuf); - -/* -** Information and control of an open file handle. -*/ -static int unixFileControl(sqlite3_file *id, int op, void *pArg){ - unixFile *pFile = (unixFile*)id; - switch( op ){ - case SQLITE_FCNTL_LOCKSTATE: { - *(int*)pArg = pFile->eFileLock; - return SQLITE_OK; - } - case SQLITE_LAST_ERRNO: { - *(int*)pArg = pFile->lastErrno; - return SQLITE_OK; - } - case SQLITE_FCNTL_CHUNK_SIZE: { - pFile->szChunk = *(int *)pArg; - return SQLITE_OK; - } - case SQLITE_FCNTL_SIZE_HINT: { - int rc; - SimulateIOErrorBenign(1); - rc = fcntlSizeHint(pFile, *(i64 *)pArg); - SimulateIOErrorBenign(0); - return rc; - } - case SQLITE_FCNTL_PERSIST_WAL: { - unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg); - return SQLITE_OK; - } - case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { - unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg); - return SQLITE_OK; - } - case SQLITE_FCNTL_VFSNAME: { - *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName); - return SQLITE_OK; - } - case SQLITE_FCNTL_TEMPFILENAME: { - char *zTFile = sqlite3_malloc( pFile->pVfs->mxPathname ); - if( zTFile ){ - unixGetTempname(pFile->pVfs->mxPathname, zTFile); - *(char**)pArg = zTFile; - } - return SQLITE_OK; - } -#ifdef SQLITE_DEBUG - /* The pager calls this method to signal that it has done - ** a rollback and that the database is therefore unchanged and - ** it hence it is OK for the transaction change counter to be - ** unchanged. - */ - case SQLITE_FCNTL_DB_UNCHANGED: { - ((unixFile*)id)->dbUpdate = 0; - return SQLITE_OK; - } -#endif -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - case SQLITE_SET_LOCKPROXYFILE: - case SQLITE_GET_LOCKPROXYFILE: { - return proxyFileControl(id,op,pArg); - } -#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ - } - return SQLITE_NOTFOUND; -} - -/* -** Return the sector size in bytes of the underlying block device for -** the specified file. This is almost always 512 bytes, but may be -** larger for some devices. -** -** SQLite code assumes this function cannot fail. It also assumes that -** if two files are created in the same file-system directory (i.e. -** a database and its journal file) that the sector size will be the -** same for both. -*/ -#ifndef __QNXNTO__ -static int unixSectorSize(sqlite3_file *NotUsed){ - UNUSED_PARAMETER(NotUsed); - return SQLITE_DEFAULT_SECTOR_SIZE; -} -#endif - -/* -** The following version of unixSectorSize() is optimized for QNX. -*/ -#ifdef __QNXNTO__ -#include -#include -static int unixSectorSize(sqlite3_file *id){ - unixFile *pFile = (unixFile*)id; - if( pFile->sectorSize == 0 ){ - struct statvfs fsInfo; - - /* Set defaults for non-supported filesystems */ - pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; - pFile->deviceCharacteristics = 0; - if( fstatvfs(pFile->h, &fsInfo) == -1 ) { - return pFile->sectorSize; - } - - if( !strcmp(fsInfo.f_basetype, "tmp") ) { - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - SQLITE_IOCAP_ATOMIC4K | /* All ram filesystem writes are atomic */ - SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until - ** the write succeeds */ - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else if( strstr(fsInfo.f_basetype, "etfs") ){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - /* etfs cluster size writes are atomic */ - (pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) | - SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until - ** the write succeeds */ - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else if( !strcmp(fsInfo.f_basetype, "qnx6") ){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - SQLITE_IOCAP_ATOMIC | /* All filesystem writes are atomic */ - SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until - ** the write succeeds */ - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - /* full bitset of atomics from max sector size and smaller */ - ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else if( strstr(fsInfo.f_basetype, "dos") ){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - /* full bitset of atomics from max sector size and smaller */ - ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else{ - pFile->deviceCharacteristics = - SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */ - SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until - ** the write succeeds */ - 0; - } - } - /* Last chance verification. If the sector size isn't a multiple of 512 - ** then it isn't valid.*/ - if( pFile->sectorSize % 512 != 0 ){ - pFile->deviceCharacteristics = 0; - pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; - } - return pFile->sectorSize; -} -#endif /* __QNXNTO__ */ - -/* -** Return the device characteristics for the file. -** -** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default. -** However, that choice is contraversial since technically the underlying -** file system does not always provide powersafe overwrites. (In other -** words, after a power-loss event, parts of the file that were never -** written might end up being altered.) However, non-PSOW behavior is very, -** very rare. And asserting PSOW makes a large reduction in the amount -** of required I/O for journaling, since a lot of padding is eliminated. -** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control -** available to turn it off and URI query parameter available to turn it off. -*/ -static int unixDeviceCharacteristics(sqlite3_file *id){ - unixFile *p = (unixFile*)id; - int rc = 0; -#ifdef __QNXNTO__ - if( p->sectorSize==0 ) unixSectorSize(id); - rc = p->deviceCharacteristics; -#endif - if( p->ctrlFlags & UNIXFILE_PSOW ){ - rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; - } - return rc; -} - -#ifndef SQLITE_OMIT_WAL - - -/* -** Object used to represent an shared memory buffer. -** -** When multiple threads all reference the same wal-index, each thread -** has its own unixShm object, but they all point to a single instance -** of this unixShmNode object. In other words, each wal-index is opened -** only once per process. -** -** Each unixShmNode object is connected to a single unixInodeInfo object. -** We could coalesce this object into unixInodeInfo, but that would mean -** every open file that does not use shared memory (in other words, most -** open files) would have to carry around this extra information. So -** the unixInodeInfo object contains a pointer to this unixShmNode object -** and the unixShmNode object is created only when needed. -** -** unixMutexHeld() must be true when creating or destroying -** this object or while reading or writing the following fields: -** -** nRef -** -** The following fields are read-only after the object is created: -** -** fid -** zFilename -** -** Either unixShmNode.mutex must be held or unixShmNode.nRef==0 and -** unixMutexHeld() is true when reading or writing any other field -** in this structure. -*/ -struct unixShmNode { - unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ - sqlite3_mutex *mutex; /* Mutex to access this object */ - char *zFilename; /* Name of the mmapped file */ - int h; /* Open file descriptor */ - int szRegion; /* Size of shared-memory regions */ - u16 nRegion; /* Size of array apRegion */ - u8 isReadonly; /* True if read-only */ - char **apRegion; /* Array of mapped shared-memory regions */ - int nRef; /* Number of unixShm objects pointing to this */ - unixShm *pFirst; /* All unixShm objects pointing to this */ -#ifdef SQLITE_DEBUG - u8 exclMask; /* Mask of exclusive locks held */ - u8 sharedMask; /* Mask of shared locks held */ - u8 nextShmId; /* Next available unixShm.id value */ -#endif -}; - -/* -** Structure used internally by this VFS to record the state of an -** open shared memory connection. -** -** The following fields are initialized when this object is created and -** are read-only thereafter: -** -** unixShm.pFile -** unixShm.id -** -** All other fields are read/write. The unixShm.pFile->mutex must be held -** while accessing any read/write fields. -*/ -struct unixShm { - unixShmNode *pShmNode; /* The underlying unixShmNode object */ - unixShm *pNext; /* Next unixShm with the same unixShmNode */ - u8 hasMutex; /* True if holding the unixShmNode mutex */ - u8 id; /* Id of this connection within its unixShmNode */ - u16 sharedMask; /* Mask of shared locks held */ - u16 exclMask; /* Mask of exclusive locks held */ -}; - -/* -** Constants used for locking -*/ -#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ -#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ - -/* -** Apply posix advisory locks for all bytes from ofst through ofst+n-1. -** -** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking -** otherwise. -*/ -static int unixShmSystemLock( - unixShmNode *pShmNode, /* Apply locks to this open shared-memory segment */ - int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ - int ofst, /* First byte of the locking range */ - int n /* Number of bytes to lock */ -){ - struct flock f; /* The posix advisory locking structure */ - int rc = SQLITE_OK; /* Result code form fcntl() */ - - /* Access to the unixShmNode object is serialized by the caller */ - assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 ); - - /* Shared locks never span more than one byte */ - assert( n==1 || lockType!=F_RDLCK ); - - /* Locks are within range */ - assert( n>=1 && nh>=0 ){ - /* Initialize the locking parameters */ - memset(&f, 0, sizeof(f)); - f.l_type = lockType; - f.l_whence = SEEK_SET; - f.l_start = ofst; - f.l_len = n; - - rc = osFcntl(pShmNode->h, F_SETLK, &f); - rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; - } - - /* Update the global lock state and do debug tracing */ -#ifdef SQLITE_DEBUG - { u16 mask; - OSTRACE(("SHM-LOCK ")); - mask = (1<<(ofst+n)) - (1<exclMask &= ~mask; - pShmNode->sharedMask &= ~mask; - }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock %d ok", ofst)); - pShmNode->exclMask &= ~mask; - pShmNode->sharedMask |= mask; - }else{ - assert( lockType==F_WRLCK ); - OSTRACE(("write-lock %d ok", ofst)); - pShmNode->exclMask |= mask; - pShmNode->sharedMask &= ~mask; - } - }else{ - if( lockType==F_UNLCK ){ - OSTRACE(("unlock %d failed", ofst)); - }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock failed")); - }else{ - assert( lockType==F_WRLCK ); - OSTRACE(("write-lock %d failed", ofst)); - } - } - OSTRACE((" - afterwards %03x,%03x\n", - pShmNode->sharedMask, pShmNode->exclMask)); - } -#endif - - return rc; -} - - -/* -** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0. -** -** This is not a VFS shared-memory method; it is a utility function called -** by VFS shared-memory methods. -*/ -static void unixShmPurge(unixFile *pFd){ - unixShmNode *p = pFd->pInode->pShmNode; - assert( unixMutexHeld() ); - if( p && p->nRef==0 ){ - int i; - assert( p->pInode==pFd->pInode ); - sqlite3_mutex_free(p->mutex); - for(i=0; inRegion; i++){ - if( p->h>=0 ){ - munmap(p->apRegion[i], p->szRegion); - }else{ - sqlite3_free(p->apRegion[i]); - } - } - sqlite3_free(p->apRegion); - if( p->h>=0 ){ - robust_close(pFd, p->h, __LINE__); - p->h = -1; - } - p->pInode->pShmNode = 0; - sqlite3_free(p); - } -} - -/* -** Open a shared-memory area associated with open database file pDbFd. -** This particular implementation uses mmapped files. -** -** The file used to implement shared-memory is in the same directory -** as the open database file and has the same name as the open database -** file with the "-shm" suffix added. For example, if the database file -** is "/home/user1/config.db" then the file that is created and mmapped -** for shared memory will be called "/home/user1/config.db-shm". -** -** Another approach to is to use files in /dev/shm or /dev/tmp or an -** some other tmpfs mount. But if a file in a different directory -** from the database file is used, then differing access permissions -** or a chroot() might cause two different processes on the same -** database to end up using different files for shared memory - -** meaning that their memory would not really be shared - resulting -** in database corruption. Nevertheless, this tmpfs file usage -** can be enabled at compile-time using -DSQLITE_SHM_DIRECTORY="/dev/shm" -** or the equivalent. The use of the SQLITE_SHM_DIRECTORY compile-time -** option results in an incompatible build of SQLite; builds of SQLite -** that with differing SQLITE_SHM_DIRECTORY settings attempt to use the -** same database file at the same time, database corruption will likely -** result. The SQLITE_SHM_DIRECTORY compile-time option is considered -** "unsupported" and may go away in a future SQLite release. -** -** When opening a new shared-memory file, if no other instances of that -** file are currently open, in this process or in other processes, then -** the file must be truncated to zero length or have its header cleared. -** -** If the original database file (pDbFd) is using the "unix-excl" VFS -** that means that an exclusive lock is held on the database file and -** that no other processes are able to read or write the database. In -** that case, we do not really need shared memory. No shared memory -** file is created. The shared memory will be simulated with heap memory. -*/ -static int unixOpenSharedMemory(unixFile *pDbFd){ - struct unixShm *p = 0; /* The connection to be opened */ - struct unixShmNode *pShmNode; /* The underlying mmapped file */ - int rc; /* Result code */ - unixInodeInfo *pInode; /* The inode of fd */ - char *zShmFilename; /* Name of the file used for SHM */ - int nShmFilename; /* Size of the SHM filename in bytes */ - - /* Allocate space for the new unixShm object. */ - p = sqlite3_malloc( sizeof(*p) ); - if( p==0 ) return SQLITE_NOMEM; - memset(p, 0, sizeof(*p)); - assert( pDbFd->pShm==0 ); - - /* Check to see if a unixShmNode object already exists. Reuse an existing - ** one if present. Create a new one if necessary. - */ - unixEnterMutex(); - pInode = pDbFd->pInode; - pShmNode = pInode->pShmNode; - if( pShmNode==0 ){ - struct stat sStat; /* fstat() info for database file */ - - /* Call fstat() to figure out the permissions on the database file. If - ** a new *-shm file is created, an attempt will be made to create it - ** with the same permissions. - */ - if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){ - rc = SQLITE_IOERR_FSTAT; - goto shm_open_err; - } - -#ifdef SQLITE_SHM_DIRECTORY - nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31; -#else - nShmFilename = 6 + (int)strlen(pDbFd->zPath); -#endif - pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename ); - if( pShmNode==0 ){ - rc = SQLITE_NOMEM; - goto shm_open_err; - } - memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename); - zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1]; -#ifdef SQLITE_SHM_DIRECTORY - sqlite3_snprintf(nShmFilename, zShmFilename, - SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x", - (u32)sStat.st_ino, (u32)sStat.st_dev); -#else - sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath); - sqlite3FileSuffix3(pDbFd->zPath, zShmFilename); -#endif - pShmNode->h = -1; - pDbFd->pInode->pShmNode = pShmNode; - pShmNode->pInode = pDbFd->pInode; - pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pShmNode->mutex==0 ){ - rc = SQLITE_NOMEM; - goto shm_open_err; - } - - if( pInode->bProcessLock==0 ){ - int openFlags = O_RDWR | O_CREAT; - if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ - openFlags = O_RDONLY; - pShmNode->isReadonly = 1; - } - pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777)); - if( pShmNode->h<0 ){ - rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); - goto shm_open_err; - } - - /* If this process is running as root, make sure that the SHM file - ** is owned by the same user that owns the original database. Otherwise, - ** the original owner will not be able to connect. - */ - osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid); - - /* Check to see if another process is holding the dead-man switch. - ** If not, truncate the file to zero length. - */ - rc = SQLITE_OK; - if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ - if( robust_ftruncate(pShmNode->h, 0) ){ - rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); - } - } - if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); - } - if( rc ) goto shm_open_err; - } - } - - /* Make the new connection a child of the unixShmNode */ - p->pShmNode = pShmNode; -#ifdef SQLITE_DEBUG - p->id = pShmNode->nextShmId++; -#endif - pShmNode->nRef++; - pDbFd->pShm = p; - unixLeaveMutex(); - - /* The reference count on pShmNode has already been incremented under - ** the cover of the unixEnterMutex() mutex and the pointer from the - ** new (struct unixShm) object to the pShmNode has been set. All that is - ** left to do is to link the new object into the linked list starting - ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex - ** mutex. - */ - sqlite3_mutex_enter(pShmNode->mutex); - p->pNext = pShmNode->pFirst; - pShmNode->pFirst = p; - sqlite3_mutex_leave(pShmNode->mutex); - return SQLITE_OK; - - /* Jump here on any error */ -shm_open_err: - unixShmPurge(pDbFd); /* This call frees pShmNode if required */ - sqlite3_free(p); - unixLeaveMutex(); - return rc; -} - -/* -** This function is called to obtain a pointer to region iRegion of the -** shared-memory associated with the database file fd. Shared-memory regions -** are numbered starting from zero. Each shared-memory region is szRegion -** bytes in size. -** -** If an error occurs, an error code is returned and *pp is set to NULL. -** -** Otherwise, if the bExtend parameter is 0 and the requested shared-memory -** region has not been allocated (by any client, including one running in a -** separate process), then *pp is set to NULL and SQLITE_OK returned. If -** bExtend is non-zero and the requested shared-memory region has not yet -** been allocated, it is allocated by this function. -** -** If the shared-memory region has already been allocated or is allocated by -** this call as described above, then it is mapped into this processes -** address space (if it is not already), *pp is set to point to the mapped -** memory and SQLITE_OK returned. -*/ -static int unixShmMap( - sqlite3_file *fd, /* Handle open on database file */ - int iRegion, /* Region to retrieve */ - int szRegion, /* Size of regions */ - int bExtend, /* True to extend file if necessary */ - void volatile **pp /* OUT: Mapped memory */ -){ - unixFile *pDbFd = (unixFile*)fd; - unixShm *p; - unixShmNode *pShmNode; - int rc = SQLITE_OK; - - /* If the shared-memory file has not yet been opened, open it now. */ - if( pDbFd->pShm==0 ){ - rc = unixOpenSharedMemory(pDbFd); - if( rc!=SQLITE_OK ) return rc; - } - - p = pDbFd->pShm; - pShmNode = p->pShmNode; - sqlite3_mutex_enter(pShmNode->mutex); - assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); - assert( pShmNode->pInode==pDbFd->pInode ); - assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); - assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); - - if( pShmNode->nRegion<=iRegion ){ - char **apNew; /* New apRegion[] array */ - int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ - struct stat sStat; /* Used by fstat() */ - - pShmNode->szRegion = szRegion; - - if( pShmNode->h>=0 ){ - /* The requested region is not mapped into this processes address space. - ** Check to see if it has been allocated (i.e. if the wal-index file is - ** large enough to contain the requested region). - */ - if( osFstat(pShmNode->h, &sStat) ){ - rc = SQLITE_IOERR_SHMSIZE; - goto shmpage_out; - } - - if( sStat.st_sizeh, sStat.st_size, nByte)!=0 ){ - rc = unixLogError(SQLITE_IOERR_SHMSIZE, "fallocate", - pShmNode->zFilename); - goto shmpage_out; - } -#else - if( robust_ftruncate(pShmNode->h, nByte) ){ - rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate", - pShmNode->zFilename); - goto shmpage_out; - } -#endif - } - } - - /* Map the requested memory region into this processes address space. */ - apNew = (char **)sqlite3_realloc( - pShmNode->apRegion, (iRegion+1)*sizeof(char *) - ); - if( !apNew ){ - rc = SQLITE_IOERR_NOMEM; - goto shmpage_out; - } - pShmNode->apRegion = apNew; - while(pShmNode->nRegion<=iRegion){ - void *pMem; - if( pShmNode->h>=0 ){ - pMem = mmap(0, szRegion, - pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, - MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion - ); - if( pMem==MAP_FAILED ){ - rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename); - goto shmpage_out; - } - }else{ - pMem = sqlite3_malloc(szRegion); - if( pMem==0 ){ - rc = SQLITE_NOMEM; - goto shmpage_out; - } - memset(pMem, 0, szRegion); - } - pShmNode->apRegion[pShmNode->nRegion] = pMem; - pShmNode->nRegion++; - } - } - -shmpage_out: - if( pShmNode->nRegion>iRegion ){ - *pp = pShmNode->apRegion[iRegion]; - }else{ - *pp = 0; - } - if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; - sqlite3_mutex_leave(pShmNode->mutex); - return rc; -} - -/* -** Change the lock state for a shared-memory segment. -** -** Note that the relationship between SHAREd and EXCLUSIVE locks is a little -** different here than in posix. In xShmLock(), one can go from unlocked -** to shared and back or from unlocked to exclusive and back. But one may -** not go from shared to exclusive or from exclusive to shared. -*/ -static int unixShmLock( - sqlite3_file *fd, /* Database file holding the shared memory */ - int ofst, /* First lock to acquire or release */ - int n, /* Number of locks to acquire or release */ - int flags /* What to do with the lock */ -){ - unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */ - unixShm *p = pDbFd->pShm; /* The shared memory being locked */ - unixShm *pX; /* For looping over all siblings */ - unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */ - int rc = SQLITE_OK; /* Result code */ - u16 mask; /* Mask of locks to take or release */ - - assert( pShmNode==pDbFd->pInode->pShmNode ); - assert( pShmNode->pInode==pDbFd->pInode ); - assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); - assert( n>=1 ); - assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) - || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) - || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) - || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); - assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); - assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); - assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); - - mask = (1<<(ofst+n)) - (1<1 || mask==(1<mutex); - if( flags & SQLITE_SHM_UNLOCK ){ - u16 allMask = 0; /* Mask of locks held by siblings */ - - /* See if any siblings hold this same lock */ - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); - allMask |= pX->sharedMask; - } - - /* Unlock the system-level locks */ - if( (mask & allMask)==0 ){ - rc = unixShmSystemLock(pShmNode, F_UNLCK, ofst+UNIX_SHM_BASE, n); - }else{ - rc = SQLITE_OK; - } - - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~mask; - p->sharedMask &= ~mask; - } - }else if( flags & SQLITE_SHM_SHARED ){ - u16 allShared = 0; /* Union of locks held by connections other than "p" */ - - /* Find out which shared locks are already held by sibling connections. - ** If any sibling already holds an exclusive lock, go ahead and return - ** SQLITE_BUSY. - */ - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - if( (pX->exclMask & mask)!=0 ){ - rc = SQLITE_BUSY; - break; - } - allShared |= pX->sharedMask; - } - - /* Get shared locks at the system level, if necessary */ - if( rc==SQLITE_OK ){ - if( (allShared & mask)==0 ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, ofst+UNIX_SHM_BASE, n); - }else{ - rc = SQLITE_OK; - } - } - - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= mask; - } - }else{ - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. - */ - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ - rc = SQLITE_BUSY; - break; - } - } - - /* Get the exclusive locks at the system level. Then if successful - ** also mark the local connection as being locked. - */ - if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_WRLCK, ofst+UNIX_SHM_BASE, n); - if( rc==SQLITE_OK ){ - assert( (p->sharedMask & mask)==0 ); - p->exclMask |= mask; - } - } - } - sqlite3_mutex_leave(pShmNode->mutex); - OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", - p->id, getpid(), p->sharedMask, p->exclMask)); - return rc; -} - -/* -** Implement a memory barrier or memory fence on shared memory. -** -** All loads and stores begun before the barrier must complete before -** any load or store begun after the barrier. -*/ -static void unixShmBarrier( - sqlite3_file *fd /* Database file holding the shared memory */ -){ - UNUSED_PARAMETER(fd); - unixEnterMutex(); - unixLeaveMutex(); -} - -/* -** Close a connection to shared-memory. Delete the underlying -** storage if deleteFlag is true. -** -** If there is no shared memory associated with the connection then this -** routine is a harmless no-op. -*/ -static int unixShmUnmap( - sqlite3_file *fd, /* The underlying database file */ - int deleteFlag /* Delete shared-memory if true */ -){ - unixShm *p; /* The connection to be closed */ - unixShmNode *pShmNode; /* The underlying shared-memory file */ - unixShm **pp; /* For looping over sibling connections */ - unixFile *pDbFd; /* The underlying database file */ - - pDbFd = (unixFile*)fd; - p = pDbFd->pShm; - if( p==0 ) return SQLITE_OK; - pShmNode = p->pShmNode; - - assert( pShmNode==pDbFd->pInode->pShmNode ); - assert( pShmNode->pInode==pDbFd->pInode ); - - /* Remove connection p from the set of connections associated - ** with pShmNode */ - sqlite3_mutex_enter(pShmNode->mutex); - for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} - *pp = p->pNext; - - /* Free the connection p */ - sqlite3_free(p); - pDbFd->pShm = 0; - sqlite3_mutex_leave(pShmNode->mutex); - - /* If pShmNode->nRef has reached 0, then close the underlying - ** shared-memory file, too */ - unixEnterMutex(); - assert( pShmNode->nRef>0 ); - pShmNode->nRef--; - if( pShmNode->nRef==0 ){ - if( deleteFlag && pShmNode->h>=0 ) osUnlink(pShmNode->zFilename); - unixShmPurge(pDbFd); - } - unixLeaveMutex(); - - return SQLITE_OK; -} - - -#else -# define unixShmMap 0 -# define unixShmLock 0 -# define unixShmBarrier 0 -# define unixShmUnmap 0 -#endif /* #ifndef SQLITE_OMIT_WAL */ - -/* -** Here ends the implementation of all sqlite3_file methods. -** -********************** End sqlite3_file Methods ******************************* -******************************************************************************/ - -/* -** This division contains definitions of sqlite3_io_methods objects that -** implement various file locking strategies. It also contains definitions -** of "finder" functions. A finder-function is used to locate the appropriate -** sqlite3_io_methods object for a particular database file. The pAppData -** field of the sqlite3_vfs VFS objects are initialized to be pointers to -** the correct finder-function for that VFS. -** -** Most finder functions return a pointer to a fixed sqlite3_io_methods -** object. The only interesting finder-function is autolockIoFinder, which -** looks at the filesystem type and tries to guess the best locking -** strategy from that. -** -** For finder-funtion F, two objects are created: -** -** (1) The real finder-function named "FImpt()". -** -** (2) A constant pointer to this function named just "F". -** -** -** A pointer to the F pointer is used as the pAppData value for VFS -** objects. We have to do this instead of letting pAppData point -** directly at the finder-function since C90 rules prevent a void* -** from be cast into a function pointer. -** -** -** Each instance of this macro generates two objects: -** -** * A constant sqlite3_io_methods object call METHOD that has locking -** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. -** -** * An I/O method finder function called FINDER that returns a pointer -** to the METHOD object in the previous bullet. -*/ -#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \ -static const sqlite3_io_methods METHOD = { \ - VERSION, /* iVersion */ \ - CLOSE, /* xClose */ \ - unixRead, /* xRead */ \ - unixWrite, /* xWrite */ \ - unixTruncate, /* xTruncate */ \ - unixSync, /* xSync */ \ - unixFileSize, /* xFileSize */ \ - LOCK, /* xLock */ \ - UNLOCK, /* xUnlock */ \ - CKLOCK, /* xCheckReservedLock */ \ - unixFileControl, /* xFileControl */ \ - unixSectorSize, /* xSectorSize */ \ - unixDeviceCharacteristics, /* xDeviceCapabilities */ \ - unixShmMap, /* xShmMap */ \ - unixShmLock, /* xShmLock */ \ - unixShmBarrier, /* xShmBarrier */ \ - unixShmUnmap /* xShmUnmap */ \ -}; \ -static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ - UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ - return &METHOD; \ -} \ -static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ - = FINDER##Impl; - -/* -** Here are all of the sqlite3_io_methods objects for each of the -** locking strategies. Functions that return pointers to these methods -** are also created. -*/ -IOMETHODS( - posixIoFinder, /* Finder function name */ - posixIoMethods, /* sqlite3_io_methods object name */ - 2, /* shared memory is enabled */ - unixClose, /* xClose method */ - unixLock, /* xLock method */ - unixUnlock, /* xUnlock method */ - unixCheckReservedLock /* xCheckReservedLock method */ -) -IOMETHODS( - nolockIoFinder, /* Finder function name */ - nolockIoMethods, /* sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - nolockClose, /* xClose method */ - nolockLock, /* xLock method */ - nolockUnlock, /* xUnlock method */ - nolockCheckReservedLock /* xCheckReservedLock method */ -) -IOMETHODS( - dotlockIoFinder, /* Finder function name */ - dotlockIoMethods, /* sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - dotlockClose, /* xClose method */ - dotlockLock, /* xLock method */ - dotlockUnlock, /* xUnlock method */ - dotlockCheckReservedLock /* xCheckReservedLock method */ -) - -#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS -IOMETHODS( - flockIoFinder, /* Finder function name */ - flockIoMethods, /* sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - flockClose, /* xClose method */ - flockLock, /* xLock method */ - flockUnlock, /* xUnlock method */ - flockCheckReservedLock /* xCheckReservedLock method */ -) -#endif - -#if OS_VXWORKS -IOMETHODS( - semIoFinder, /* Finder function name */ - semIoMethods, /* sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - semClose, /* xClose method */ - semLock, /* xLock method */ - semUnlock, /* xUnlock method */ - semCheckReservedLock /* xCheckReservedLock method */ -) -#endif - -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -IOMETHODS( - afpIoFinder, /* Finder function name */ - afpIoMethods, /* sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - afpClose, /* xClose method */ - afpLock, /* xLock method */ - afpUnlock, /* xUnlock method */ - afpCheckReservedLock /* xCheckReservedLock method */ -) -#endif - -/* -** The proxy locking method is a "super-method" in the sense that it -** opens secondary file descriptors for the conch and lock files and -** it uses proxy, dot-file, AFP, and flock() locking methods on those -** secondary files. For this reason, the division that implements -** proxy locking is located much further down in the file. But we need -** to go ahead and define the sqlite3_io_methods and finder function -** for proxy locking here. So we forward declare the I/O methods. -*/ -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -static int proxyClose(sqlite3_file*); -static int proxyLock(sqlite3_file*, int); -static int proxyUnlock(sqlite3_file*, int); -static int proxyCheckReservedLock(sqlite3_file*, int*); -IOMETHODS( - proxyIoFinder, /* Finder function name */ - proxyIoMethods, /* sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - proxyClose, /* xClose method */ - proxyLock, /* xLock method */ - proxyUnlock, /* xUnlock method */ - proxyCheckReservedLock /* xCheckReservedLock method */ -) -#endif - -/* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */ -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -IOMETHODS( - nfsIoFinder, /* Finder function name */ - nfsIoMethods, /* sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - unixClose, /* xClose method */ - unixLock, /* xLock method */ - nfsUnlock, /* xUnlock method */ - unixCheckReservedLock /* xCheckReservedLock method */ -) -#endif - -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -/* -** This "finder" function attempts to determine the best locking strategy -** for the database file "filePath". It then returns the sqlite3_io_methods -** object that implements that strategy. -** -** This is for MacOSX only. -*/ -static const sqlite3_io_methods *autolockIoFinderImpl( - const char *filePath, /* name of the database file */ - unixFile *pNew /* open file object for the database file */ -){ - static const struct Mapping { - const char *zFilesystem; /* Filesystem type name */ - const sqlite3_io_methods *pMethods; /* Appropriate locking method */ - } aMap[] = { - { "hfs", &posixIoMethods }, - { "ufs", &posixIoMethods }, - { "afpfs", &afpIoMethods }, - { "smbfs", &afpIoMethods }, - { "webdav", &nolockIoMethods }, - { 0, 0 } - }; - int i; - struct statfs fsInfo; - struct flock lockInfo; - - if( !filePath ){ - /* If filePath==NULL that means we are dealing with a transient file - ** that does not need to be locked. */ - return &nolockIoMethods; - } - if( statfs(filePath, &fsInfo) != -1 ){ - if( fsInfo.f_flags & MNT_RDONLY ){ - return &nolockIoMethods; - } - for(i=0; aMap[i].zFilesystem; i++){ - if( strcmp(fsInfo.f_fstypename, aMap[i].zFilesystem)==0 ){ - return aMap[i].pMethods; - } - } - } - - /* Default case. Handles, amongst others, "nfs". - ** Test byte-range lock using fcntl(). If the call succeeds, - ** assume that the file-system supports POSIX style locks. - */ - lockInfo.l_len = 1; - lockInfo.l_start = 0; - lockInfo.l_whence = SEEK_SET; - lockInfo.l_type = F_RDLCK; - if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { - if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ - return &nfsIoMethods; - } else { - return &posixIoMethods; - } - }else{ - return &dotlockIoMethods; - } -} -static const sqlite3_io_methods - *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; - -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ - -#if OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE -/* -** This "finder" function attempts to determine the best locking strategy -** for the database file "filePath". It then returns the sqlite3_io_methods -** object that implements that strategy. -** -** This is for VXWorks only. -*/ -static const sqlite3_io_methods *autolockIoFinderImpl( - const char *filePath, /* name of the database file */ - unixFile *pNew /* the open file object */ -){ - struct flock lockInfo; - - if( !filePath ){ - /* If filePath==NULL that means we are dealing with a transient file - ** that does not need to be locked. */ - return &nolockIoMethods; - } - - /* Test if fcntl() is supported and use POSIX style locks. - ** Otherwise fall back to the named semaphore method. - */ - lockInfo.l_len = 1; - lockInfo.l_start = 0; - lockInfo.l_whence = SEEK_SET; - lockInfo.l_type = F_RDLCK; - if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { - return &posixIoMethods; - }else{ - return &semIoMethods; - } -} -static const sqlite3_io_methods - *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; - -#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */ - -/* -** An abstract type for a pointer to a IO method finder function: -*/ -typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*); - - -/**************************************************************************** -**************************** sqlite3_vfs methods **************************** -** -** This division contains the implementation of methods on the -** sqlite3_vfs object. -*/ - -/* -** Initialize the contents of the unixFile structure pointed to by pId. -*/ -static int fillInUnixFile( - sqlite3_vfs *pVfs, /* Pointer to vfs object */ - int h, /* Open file descriptor of file being opened */ - sqlite3_file *pId, /* Write to the unixFile structure here */ - const char *zFilename, /* Name of the file being opened */ - int ctrlFlags /* Zero or more UNIXFILE_* values */ -){ - const sqlite3_io_methods *pLockingStyle; - unixFile *pNew = (unixFile *)pId; - int rc = SQLITE_OK; - - assert( pNew->pInode==NULL ); - - /* Usually the path zFilename should not be a relative pathname. The - ** exception is when opening the proxy "conch" file in builds that - ** include the special Apple locking styles. - */ -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE - assert( zFilename==0 || zFilename[0]=='/' - || pVfs->pAppData==(void*)&autolockIoFinder ); -#else - assert( zFilename==0 || zFilename[0]=='/' ); -#endif - - /* No locking occurs in temporary files */ - assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 ); - - OSTRACE(("OPEN %-3d %s\n", h, zFilename)); - pNew->h = h; - pNew->pVfs = pVfs; - pNew->zPath = zFilename; - pNew->ctrlFlags = (u8)ctrlFlags; - if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), - "psow", SQLITE_POWERSAFE_OVERWRITE) ){ - pNew->ctrlFlags |= UNIXFILE_PSOW; - } - if( strcmp(pVfs->zName,"unix-excl")==0 ){ - pNew->ctrlFlags |= UNIXFILE_EXCL; - } - -#if OS_VXWORKS - pNew->pId = vxworksFindFileId(zFilename); - if( pNew->pId==0 ){ - ctrlFlags |= UNIXFILE_NOLOCK; - rc = SQLITE_NOMEM; - } -#endif - - if( ctrlFlags & UNIXFILE_NOLOCK ){ - pLockingStyle = &nolockIoMethods; - }else{ - pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); -#if SQLITE_ENABLE_LOCKING_STYLE - /* Cache zFilename in the locking context (AFP and dotlock override) for - ** proxyLock activation is possible (remote proxy is based on db name) - ** zFilename remains valid until file is closed, to support */ - pNew->lockingContext = (void*)zFilename; -#endif - } - - if( pLockingStyle == &posixIoMethods -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE - || pLockingStyle == &nfsIoMethods -#endif - ){ - unixEnterMutex(); - rc = findInodeInfo(pNew, &pNew->pInode); - if( rc!=SQLITE_OK ){ - /* If an error occurred in findInodeInfo(), close the file descriptor - ** immediately, before releasing the mutex. findInodeInfo() may fail - ** in two scenarios: - ** - ** (a) A call to fstat() failed. - ** (b) A malloc failed. - ** - ** Scenario (b) may only occur if the process is holding no other - ** file descriptors open on the same file. If there were other file - ** descriptors on this file, then no malloc would be required by - ** findInodeInfo(). If this is the case, it is quite safe to close - ** handle h - as it is guaranteed that no posix locks will be released - ** by doing so. - ** - ** If scenario (a) caused the error then things are not so safe. The - ** implicit assumption here is that if fstat() fails, things are in - ** such bad shape that dropping a lock or two doesn't matter much. - */ - robust_close(pNew, h, __LINE__); - h = -1; - } - unixLeaveMutex(); - } - -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - else if( pLockingStyle == &afpIoMethods ){ - /* AFP locking uses the file path so it needs to be included in - ** the afpLockingContext. - */ - afpLockingContext *pCtx; - pNew->lockingContext = pCtx = sqlite3_malloc( sizeof(*pCtx) ); - if( pCtx==0 ){ - rc = SQLITE_NOMEM; - }else{ - /* NB: zFilename exists and remains valid until the file is closed - ** according to requirement F11141. So we do not need to make a - ** copy of the filename. */ - pCtx->dbPath = zFilename; - pCtx->reserved = 0; - srandomdev(); - unixEnterMutex(); - rc = findInodeInfo(pNew, &pNew->pInode); - if( rc!=SQLITE_OK ){ - sqlite3_free(pNew->lockingContext); - robust_close(pNew, h, __LINE__); - h = -1; - } - unixLeaveMutex(); - } - } -#endif - - else if( pLockingStyle == &dotlockIoMethods ){ - /* Dotfile locking uses the file path so it needs to be included in - ** the dotlockLockingContext - */ - char *zLockFile; - int nFilename; - assert( zFilename!=0 ); - nFilename = (int)strlen(zFilename) + 6; - zLockFile = (char *)sqlite3_malloc(nFilename); - if( zLockFile==0 ){ - rc = SQLITE_NOMEM; - }else{ - sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); - } - pNew->lockingContext = zLockFile; - } - -#if OS_VXWORKS - else if( pLockingStyle == &semIoMethods ){ - /* Named semaphore locking uses the file path so it needs to be - ** included in the semLockingContext - */ - unixEnterMutex(); - rc = findInodeInfo(pNew, &pNew->pInode); - if( (rc==SQLITE_OK) && (pNew->pInode->pSem==NULL) ){ - char *zSemName = pNew->pInode->aSemName; - int n; - sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem", - pNew->pId->zCanonicalName); - for( n=1; zSemName[n]; n++ ) - if( zSemName[n]=='/' ) zSemName[n] = '_'; - pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1); - if( pNew->pInode->pSem == SEM_FAILED ){ - rc = SQLITE_NOMEM; - pNew->pInode->aSemName[0] = '\0'; - } - } - unixLeaveMutex(); - } -#endif - - pNew->lastErrno = 0; -#if OS_VXWORKS - if( rc!=SQLITE_OK ){ - if( h>=0 ) robust_close(pNew, h, __LINE__); - h = -1; - osUnlink(zFilename); - isDelete = 0; - } - if( isDelete ) pNew->ctrlFlags |= UNIXFILE_DELETE; -#endif - if( rc!=SQLITE_OK ){ - if( h>=0 ) robust_close(pNew, h, __LINE__); - }else{ - pNew->pMethod = pLockingStyle; - OpenCounter(+1); - } - return rc; -} - -/* -** Return the name of a directory in which to put temporary files. -** If no suitable temporary file directory can be found, return NULL. -*/ -static const char *unixTempFileDir(void){ - static const char *azDirs[] = { - 0, - 0, - "/var/tmp", - "/usr/tmp", - "/tmp", - 0 /* List terminator */ - }; - unsigned int i; - struct stat buf; - const char *zDir = 0; - - azDirs[0] = sqlite3_temp_directory; - if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); - for(i=0; imxPathname bytes. -*/ -static int unixGetTempname(int nBuf, char *zBuf){ - static const unsigned char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789"; - unsigned int i, j; - const char *zDir; - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. - */ - SimulateIOError( return SQLITE_IOERR ); - - zDir = unixTempFileDir(); - if( zDir==0 ) zDir = "."; - - /* Check that the output buffer is large enough for the temporary file - ** name. If it is not, return SQLITE_ERROR. - */ - if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf ){ - return SQLITE_ERROR; - } - - do{ - sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); - j = (int)strlen(zBuf); - sqlite3_randomness(15, &zBuf[j]); - for(i=0; i<15; i++, j++){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; - } - zBuf[j] = 0; - zBuf[j+1] = 0; - }while( osAccess(zBuf,0)==0 ); - return SQLITE_OK; -} - -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) -/* -** Routine to transform a unixFile into a proxy-locking unixFile. -** Implementation in the proxy-lock division, but used by unixOpen() -** if SQLITE_PREFER_PROXY_LOCKING is defined. -*/ -static int proxyTransformUnixFile(unixFile*, const char*); -#endif - -/* -** Search for an unused file descriptor that was opened on the database -** file (not a journal or master-journal file) identified by pathname -** zPath with SQLITE_OPEN_XXX flags matching those passed as the second -** argument to this function. -** -** Such a file descriptor may exist if a database connection was closed -** but the associated file descriptor could not be closed because some -** other file descriptor open on the same file is holding a file-lock. -** Refer to comments in the unixClose() function and the lengthy comment -** describing "Posix Advisory Locking" at the start of this file for -** further details. Also, ticket #4018. -** -** If a suitable file descriptor is found, then it is returned. If no -** such file descriptor is located, -1 is returned. -*/ -static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ - UnixUnusedFd *pUnused = 0; - - /* Do not search for an unused file descriptor on vxworks. Not because - ** vxworks would not benefit from the change (it might, we're not sure), - ** but because no way to test it is currently available. It is better - ** not to risk breaking vxworks support for the sake of such an obscure - ** feature. */ -#if !OS_VXWORKS - struct stat sStat; /* Results of stat() call */ - - /* A stat() call may fail for various reasons. If this happens, it is - ** almost certain that an open() call on the same path will also fail. - ** For this reason, if an error occurs in the stat() call here, it is - ** ignored and -1 is returned. The caller will try to open a new file - ** descriptor on the same path, fail, and return an error to SQLite. - ** - ** Even if a subsequent open() call does succeed, the consequences of - ** not searching for a resusable file descriptor are not dire. */ - if( 0==osStat(zPath, &sStat) ){ - unixInodeInfo *pInode; - - unixEnterMutex(); - pInode = inodeList; - while( pInode && (pInode->fileId.dev!=sStat.st_dev - || pInode->fileId.ino!=sStat.st_ino) ){ - pInode = pInode->pNext; - } - if( pInode ){ - UnixUnusedFd **pp; - for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); - pUnused = *pp; - if( pUnused ){ - *pp = pUnused->pNext; - } - } - unixLeaveMutex(); - } -#endif /* if !OS_VXWORKS */ - return pUnused; -} - -/* -** This function is called by unixOpen() to determine the unix permissions -** to create new files with. If no error occurs, then SQLITE_OK is returned -** and a value suitable for passing as the third argument to open(2) is -** written to *pMode. If an IO error occurs, an SQLite error code is -** returned and the value of *pMode is not modified. -** -** In most cases cases, this routine sets *pMode to 0, which will become -** an indication to robust_open() to create the file using -** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask. -** But if the file being opened is a WAL or regular journal file, then -** this function queries the file-system for the permissions on the -** corresponding database file and sets *pMode to this value. Whenever -** possible, WAL and journal files are created using the same permissions -** as the associated database file. -** -** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the -** original filename is unavailable. But 8_3_NAMES is only used for -** FAT filesystems and permissions do not matter there, so just use -** the default permissions. -*/ -static int findCreateFileMode( - const char *zPath, /* Path of file (possibly) being created */ - int flags, /* Flags passed as 4th argument to xOpen() */ - mode_t *pMode, /* OUT: Permissions to open file with */ - uid_t *pUid, /* OUT: uid to set on the file */ - gid_t *pGid /* OUT: gid to set on the file */ -){ - int rc = SQLITE_OK; /* Return Code */ - *pMode = 0; - *pUid = 0; - *pGid = 0; - if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ - char zDb[MAX_PATHNAME+1]; /* Database file path */ - int nDb; /* Number of valid bytes in zDb */ - struct stat sStat; /* Output of stat() on database file */ - - /* zPath is a path to a WAL or journal file. The following block derives - ** the path to the associated database file from zPath. This block handles - ** the following naming conventions: - ** - ** "-journal" - ** "-wal" - ** "-journalNN" - ** "-walNN" - ** - ** where NN is a decimal number. The NN naming schemes are - ** used by the test_multiplex.c module. - */ - nDb = sqlite3Strlen30(zPath) - 1; -#ifdef SQLITE_ENABLE_8_3_NAMES - while( nDb>0 && sqlite3Isalnum(zPath[nDb]) ) nDb--; - if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK; -#else - while( zPath[nDb]!='-' ){ - assert( nDb>0 ); - assert( zPath[nDb]!='\n' ); - nDb--; - } -#endif - memcpy(zDb, zPath, nDb); - zDb[nDb] = '\0'; - - if( 0==osStat(zDb, &sStat) ){ - *pMode = sStat.st_mode & 0777; - *pUid = sStat.st_uid; - *pGid = sStat.st_gid; - }else{ - rc = SQLITE_IOERR_FSTAT; - } - }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ - *pMode = 0600; - } - return rc; -} - -/* -** Open the file zPath. -** -** Previously, the SQLite OS layer used three functions in place of this -** one: -** -** sqlite3OsOpenReadWrite(); -** sqlite3OsOpenReadOnly(); -** sqlite3OsOpenExclusive(); -** -** These calls correspond to the following combinations of flags: -** -** ReadWrite() -> (READWRITE | CREATE) -** ReadOnly() -> (READONLY) -** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE) -** -** The old OpenExclusive() accepted a boolean argument - "delFlag". If -** true, the file was configured to be automatically deleted when the -** file handle closed. To achieve the same effect using this new -** interface, add the DELETEONCLOSE flag to those specified above for -** OpenExclusive(). -*/ -static int unixOpen( - sqlite3_vfs *pVfs, /* The VFS for which this is the xOpen method */ - const char *zPath, /* Pathname of file to be opened */ - sqlite3_file *pFile, /* The file descriptor to be filled in */ - int flags, /* Input flags to control the opening */ - int *pOutFlags /* Output flags returned to SQLite core */ -){ - unixFile *p = (unixFile *)pFile; - int fd = -1; /* File descriptor returned by open() */ - int openFlags = 0; /* Flags to pass to open() */ - int eType = flags&0xFFFFFF00; /* Type of file to open */ - int noLock; /* True to omit locking primitives */ - int rc = SQLITE_OK; /* Function Return Code */ - int ctrlFlags = 0; /* UNIXFILE_* flags */ - - int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); - int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); - int isCreate = (flags & SQLITE_OPEN_CREATE); - int isReadonly = (flags & SQLITE_OPEN_READONLY); - int isReadWrite = (flags & SQLITE_OPEN_READWRITE); -#if SQLITE_ENABLE_LOCKING_STYLE - int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY); -#endif -#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE - struct statfs fsInfo; -#endif - - /* If creating a master or main-file journal, this function will open - ** a file-descriptor on the directory too. The first time unixSync() - ** is called the directory file descriptor will be fsync()ed and close()d. - */ - int syncDir = (isCreate && ( - eType==SQLITE_OPEN_MASTER_JOURNAL - || eType==SQLITE_OPEN_MAIN_JOURNAL - || eType==SQLITE_OPEN_WAL - )); - - /* If argument zPath is a NULL pointer, this function is required to open - ** a temporary file. Use this buffer to store the file name in. - */ - char zTmpname[MAX_PATHNAME+2]; - const char *zName = zPath; - - /* Check the following statements are true: - ** - ** (a) Exactly one of the READWRITE and READONLY flags must be set, and - ** (b) if CREATE is set, then READWRITE must also be set, and - ** (c) if EXCLUSIVE is set, then CREATE must also be set. - ** (d) if DELETEONCLOSE is set, then CREATE must also be set. - */ - assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); - assert(isCreate==0 || isReadWrite); - assert(isExclusive==0 || isCreate); - assert(isDelete==0 || isCreate); - - /* The main DB, main journal, WAL file and master journal are never - ** automatically deleted. Nor are they ever temporary files. */ - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); - - /* Assert that the upper layer has set one of the "file-type" flags. */ - assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB - || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL - || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL - || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL - ); - - memset(p, 0, sizeof(unixFile)); - - if( eType==SQLITE_OPEN_MAIN_DB ){ - UnixUnusedFd *pUnused; - pUnused = findReusableFd(zName, flags); - if( pUnused ){ - fd = pUnused->fd; - }else{ - pUnused = sqlite3_malloc(sizeof(*pUnused)); - if( !pUnused ){ - return SQLITE_NOMEM; - } - } - p->pUnused = pUnused; - - /* Database filenames are double-zero terminated if they are not - ** URIs with parameters. Hence, they can always be passed into - ** sqlite3_uri_parameter(). */ - assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 ); - - }else if( !zName ){ - /* If zName is NULL, the upper layer is requesting a temp file. */ - assert(isDelete && !syncDir); - rc = unixGetTempname(MAX_PATHNAME+2, zTmpname); - if( rc!=SQLITE_OK ){ - return rc; - } - zName = zTmpname; - - /* Generated temporary filenames are always double-zero terminated - ** for use by sqlite3_uri_parameter(). */ - assert( zName[strlen(zName)+1]==0 ); - } - - /* Determine the value of the flags parameter passed to POSIX function - ** open(). These must be calculated even if open() is not called, as - ** they may be stored as part of the file handle and used by the - ** 'conch file' locking functions later on. */ - if( isReadonly ) openFlags |= O_RDONLY; - if( isReadWrite ) openFlags |= O_RDWR; - if( isCreate ) openFlags |= O_CREAT; - if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); - openFlags |= (O_LARGEFILE|O_BINARY); - - if( fd<0 ){ - mode_t openMode; /* Permissions to create file with */ - uid_t uid; /* Userid for the file */ - gid_t gid; /* Groupid for the file */ - rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid); - if( rc!=SQLITE_OK ){ - assert( !p->pUnused ); - assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); - return rc; - } - fd = robust_open(zName, openFlags, openMode); - OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags)); - if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ - /* Failed to open the file for read/write access. Try read-only. */ - flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); - openFlags &= ~(O_RDWR|O_CREAT); - flags |= SQLITE_OPEN_READONLY; - openFlags |= O_RDONLY; - isReadonly = 1; - fd = robust_open(zName, openFlags, openMode); - } - if( fd<0 ){ - rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); - goto open_finished; - } - - /* If this process is running as root and if creating a new rollback - ** journal or WAL file, set the ownership of the journal or WAL to be - ** the same as the original database. - */ - if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ - osFchown(fd, uid, gid); - } - } - assert( fd>=0 ); - if( pOutFlags ){ - *pOutFlags = flags; - } - - if( p->pUnused ){ - p->pUnused->fd = fd; - p->pUnused->flags = flags; - } - - if( isDelete ){ -#if OS_VXWORKS - zPath = zName; -#else - osUnlink(zName); -#endif - } -#if SQLITE_ENABLE_LOCKING_STYLE - else{ - p->openFlags = openFlags; - } -#endif - - noLock = eType!=SQLITE_OPEN_MAIN_DB; - - -#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE - if( fstatfs(fd, &fsInfo) == -1 ){ - ((unixFile*)pFile)->lastErrno = errno; - robust_close(p, fd, __LINE__); - return SQLITE_IOERR_ACCESS; - } - if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { - ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; - } -#endif - - /* Set up appropriate ctrlFlags */ - if( isDelete ) ctrlFlags |= UNIXFILE_DELETE; - if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY; - if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK; - if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC; - if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI; - -#if SQLITE_ENABLE_LOCKING_STYLE -#if SQLITE_PREFER_PROXY_LOCKING - isAutoProxy = 1; -#endif - if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){ - char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING"); - int useProxy = 0; - - /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means - ** never use proxy, NULL means use proxy for non-local files only. */ - if( envforce!=NULL ){ - useProxy = atoi(envforce)>0; - }else{ - if( statfs(zPath, &fsInfo) == -1 ){ - /* In theory, the close(fd) call is sub-optimal. If the file opened - ** with fd is a database file, and there are other connections open - ** on that file that are currently holding advisory locks on it, - ** then the call to close() will cancel those locks. In practice, - ** we're assuming that statfs() doesn't fail very often. At least - ** not while other file descriptors opened by the same process on - ** the same file are working. */ - p->lastErrno = errno; - robust_close(p, fd, __LINE__); - rc = SQLITE_IOERR_ACCESS; - goto open_finished; - } - useProxy = !(fsInfo.f_flags&MNT_LOCAL); - } - if( useProxy ){ - rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); - if( rc==SQLITE_OK ){ - rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); - if( rc!=SQLITE_OK ){ - /* Use unixClose to clean up the resources added in fillInUnixFile - ** and clear all the structure's references. Specifically, - ** pFile->pMethods will be NULL so sqlite3OsClose will be a no-op - */ - unixClose(pFile); - return rc; - } - } - goto open_finished; - } - } -#endif - - rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); - -open_finished: - if( rc!=SQLITE_OK ){ - sqlite3_free(p->pUnused); - } - return rc; -} - - -/* -** Delete the file at zPath. If the dirSync argument is true, fsync() -** the directory after deleting the file. -*/ -static int unixDelete( - sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */ - const char *zPath, /* Name of file to be deleted */ - int dirSync /* If true, fsync() directory after deleting file */ -){ - int rc = SQLITE_OK; - UNUSED_PARAMETER(NotUsed); - SimulateIOError(return SQLITE_IOERR_DELETE); - if( osUnlink(zPath)==(-1) ){ - if( errno==ENOENT ){ - rc = SQLITE_IOERR_DELETE_NOENT; - }else{ - rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); - } - return rc; - } -#ifndef SQLITE_DISABLE_DIRSYNC - if( (dirSync & 1)!=0 ){ - int fd; - rc = osOpenDirectory(zPath, &fd); - if( rc==SQLITE_OK ){ -#if OS_VXWORKS - if( fsync(fd)==-1 ) -#else - if( fsync(fd) ) -#endif - { - rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); - } - robust_close(0, fd, __LINE__); - }else if( rc==SQLITE_CANTOPEN ){ - rc = SQLITE_OK; - } - } -#endif - return rc; -} - -/* -** Test the existence of or access permissions of file zPath. The -** test performed depends on the value of flags: -** -** SQLITE_ACCESS_EXISTS: Return 1 if the file exists -** SQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable. -** SQLITE_ACCESS_READONLY: Return 1 if the file is readable. -** -** Otherwise return 0. -*/ -static int unixAccess( - sqlite3_vfs *NotUsed, /* The VFS containing this xAccess method */ - const char *zPath, /* Path of the file to examine */ - int flags, /* What do we want to learn about the zPath file? */ - int *pResOut /* Write result boolean here */ -){ - int amode = 0; - UNUSED_PARAMETER(NotUsed); - SimulateIOError( return SQLITE_IOERR_ACCESS; ); - switch( flags ){ - case SQLITE_ACCESS_EXISTS: - amode = F_OK; - break; - case SQLITE_ACCESS_READWRITE: - amode = W_OK|R_OK; - break; - case SQLITE_ACCESS_READ: - amode = R_OK; - break; - - default: - assert(!"Invalid flags argument"); - } - *pResOut = (osAccess(zPath, amode)==0); - if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){ - struct stat buf; - if( 0==osStat(zPath, &buf) && buf.st_size==0 ){ - *pResOut = 0; - } - } - return SQLITE_OK; -} - - -/* -** Turn a relative pathname into a full pathname. The relative path -** is stored as a nul-terminated string in the buffer pointed to by -** zPath. -** -** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes -** (in this case, MAX_PATHNAME bytes). The full-path is written to -** this buffer before returning. -*/ -static int unixFullPathname( - sqlite3_vfs *pVfs, /* Pointer to vfs object */ - const char *zPath, /* Possibly relative input path */ - int nOut, /* Size of output buffer in bytes */ - char *zOut /* Output buffer */ -){ - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. This function could fail if, for example, the - ** current working directory has been unlinked. - */ - SimulateIOError( return SQLITE_ERROR ); - - assert( pVfs->mxPathname==MAX_PATHNAME ); - UNUSED_PARAMETER(pVfs); - - zOut[nOut-1] = '\0'; - if( zPath[0]=='/' ){ - sqlite3_snprintf(nOut, zOut, "%s", zPath); - }else{ - int nCwd; - if( osGetcwd(zOut, nOut-1)==0 ){ - return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); - } - nCwd = (int)strlen(zOut); - sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath); - } - return SQLITE_OK; -} - - -#ifndef SQLITE_OMIT_LOAD_EXTENSION -/* -** Interfaces for opening a shared library, finding entry points -** within the shared library, and closing the shared library. -*/ -#include -static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){ - UNUSED_PARAMETER(NotUsed); - return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL); -} - -/* -** SQLite calls this function immediately after a call to unixDlSym() or -** unixDlOpen() fails (returns a null pointer). If a more detailed error -** message is available, it is written to zBufOut. If no error message -** is available, zBufOut is left unmodified and SQLite uses a default -** error message. -*/ -static void unixDlError(sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){ - const char *zErr; - UNUSED_PARAMETER(NotUsed); - unixEnterMutex(); - zErr = dlerror(); - if( zErr ){ - sqlite3_snprintf(nBuf, zBufOut, "%s", zErr); - } - unixLeaveMutex(); -} -static void (*unixDlSym(sqlite3_vfs *NotUsed, void *p, const char*zSym))(void){ - /* - ** GCC with -pedantic-errors says that C90 does not allow a void* to be - ** cast into a pointer to a function. And yet the library dlsym() routine - ** returns a void* which is really a pointer to a function. So how do we - ** use dlsym() with -pedantic-errors? - ** - ** Variable x below is defined to be a pointer to a function taking - ** parameters void* and const char* and returning a pointer to a function. - ** We initialize x by assigning it a pointer to the dlsym() function. - ** (That assignment requires a cast.) Then we call the function that - ** x points to. - ** - ** This work-around is unlikely to work correctly on any system where - ** you really cannot cast a function pointer into void*. But then, on the - ** other hand, dlsym() will not work on such a system either, so we have - ** not really lost anything. - */ - void (*(*x)(void*,const char*))(void); - UNUSED_PARAMETER(NotUsed); - x = (void(*(*)(void*,const char*))(void))dlsym; - return (*x)(p, zSym); -} -static void unixDlClose(sqlite3_vfs *NotUsed, void *pHandle){ - UNUSED_PARAMETER(NotUsed); - dlclose(pHandle); -} -#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ - #define unixDlOpen 0 - #define unixDlError 0 - #define unixDlSym 0 - #define unixDlClose 0 -#endif - -/* -** Write nBuf bytes of random data to the supplied buffer zBuf. -*/ -static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ - UNUSED_PARAMETER(NotUsed); - assert((size_t)nBuf>=(sizeof(time_t)+sizeof(int))); - - /* We have to initialize zBuf to prevent valgrind from reporting - ** errors. The reports issued by valgrind are incorrect - we would - ** prefer that the randomness be increased by making use of the - ** uninitialized space in zBuf - but valgrind errors tend to worry - ** some users. Rather than argue, it seems easier just to initialize - ** the whole array and silence valgrind, even if that means less randomness - ** in the random seed. - ** - ** When testing, initializing zBuf[] to zero is all we do. That means - ** that we always use the same random number sequence. This makes the - ** tests repeatable. - */ - memset(zBuf, 0, nBuf); -#if !defined(SQLITE_TEST) - { - int pid, fd, got; - fd = robust_open("/dev/urandom", O_RDONLY, 0); - if( fd<0 ){ - time_t t; - time(&t); - memcpy(zBuf, &t, sizeof(t)); - pid = getpid(); - memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); - assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); - nBuf = sizeof(t) + sizeof(pid); - }else{ - do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR ); - robust_close(0, fd, __LINE__); - } - } -#endif - return nBuf; -} - - -/* -** Sleep for a little while. Return the amount of time slept. -** The argument is the number of microseconds we want to sleep. -** The return value is the number of microseconds of sleep actually -** requested from the underlying operating system, a number which -** might be greater than or equal to the argument, but not less -** than the argument. -*/ -static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){ -#if OS_VXWORKS - struct timespec sp; - - sp.tv_sec = microseconds / 1000000; - sp.tv_nsec = (microseconds % 1000000) * 1000; - nanosleep(&sp, NULL); - UNUSED_PARAMETER(NotUsed); - return microseconds; -#elif defined(HAVE_USLEEP) && HAVE_USLEEP - usleep(microseconds); - UNUSED_PARAMETER(NotUsed); - return microseconds; -#else - int seconds = (microseconds+999999)/1000000; - sleep(seconds); - UNUSED_PARAMETER(NotUsed); - return seconds*1000000; -#endif -} - -/* -** The following variable, if set to a non-zero value, is interpreted as -** the number of seconds since 1970 and is used to set the result of -** sqlite3OsCurrentTime() during testing. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ -#endif - -/* -** Find the current time (in Universal Coordinated Time). Write into *piNow -** the current time and date as a Julian Day number times 86_400_000. In -** other words, write into *piNow the number of milliseconds since the Julian -** epoch of noon in Greenwich on November 24, 4714 B.C according to the -** proleptic Gregorian calendar. -** -** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date -** cannot be found. -*/ -static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ - static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; - int rc = SQLITE_OK; -#if defined(NO_GETTOD) - time_t t; - time(&t); - *piNow = ((sqlite3_int64)t)*1000 + unixEpoch; -#elif OS_VXWORKS - struct timespec sNow; - clock_gettime(CLOCK_REALTIME, &sNow); - *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000; -#else - struct timeval sNow; - if( gettimeofday(&sNow, 0)==0 ){ - *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; - }else{ - rc = SQLITE_ERROR; - } -#endif - -#ifdef SQLITE_TEST - if( sqlite3_current_time ){ - *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; - } -#endif - UNUSED_PARAMETER(NotUsed); - return rc; -} - -/* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. -*/ -static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){ - sqlite3_int64 i = 0; - int rc; - UNUSED_PARAMETER(NotUsed); - rc = unixCurrentTimeInt64(0, &i); - *prNow = i/86400000.0; - return rc; -} - -/* -** We added the xGetLastError() method with the intention of providing -** better low-level error messages when operating-system problems come up -** during SQLite operation. But so far, none of that has been implemented -** in the core. So this routine is never called. For now, it is merely -** a place-holder. -*/ -static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ - UNUSED_PARAMETER(NotUsed); - UNUSED_PARAMETER(NotUsed2); - UNUSED_PARAMETER(NotUsed3); - return 0; -} - - -/* -************************ End of sqlite3_vfs methods *************************** -******************************************************************************/ - -/****************************************************************************** -************************** Begin Proxy Locking ******************************** -** -** Proxy locking is a "uber-locking-method" in this sense: It uses the -** other locking methods on secondary lock files. Proxy locking is a -** meta-layer over top of the primitive locking implemented above. For -** this reason, the division that implements of proxy locking is deferred -** until late in the file (here) after all of the other I/O methods have -** been defined - so that the primitive locking methods are available -** as services to help with the implementation of proxy locking. -** -**** -** -** The default locking schemes in SQLite use byte-range locks on the -** database file to coordinate safe, concurrent access by multiple readers -** and writers [http://sqlite.org/lockingv3.html]. The five file locking -** states (UNLOCKED, PENDING, SHARED, RESERVED, EXCLUSIVE) are implemented -** as POSIX read & write locks over fixed set of locations (via fsctl), -** on AFP and SMB only exclusive byte-range locks are available via fsctl -** with _IOWR('z', 23, struct ByteRangeLockPB2) to track the same 5 states. -** To simulate a F_RDLCK on the shared range, on AFP a randomly selected -** address in the shared range is taken for a SHARED lock, the entire -** shared range is taken for an EXCLUSIVE lock): -** -** PENDING_BYTE 0x40000000 -** RESERVED_BYTE 0x40000001 -** SHARED_RANGE 0x40000002 -> 0x40000200 -** -** This works well on the local file system, but shows a nearly 100x -** slowdown in read performance on AFP because the AFP client disables -** the read cache when byte-range locks are present. Enabling the read -** cache exposes a cache coherency problem that is present on all OS X -** supported network file systems. NFS and AFP both observe the -** close-to-open semantics for ensuring cache coherency -** [http://nfs.sourceforge.net/#faq_a8], which does not effectively -** address the requirements for concurrent database access by multiple -** readers and writers -** [http://www.nabble.com/SQLite-on-NFS-cache-coherency-td15655701.html]. -** -** To address the performance and cache coherency issues, proxy file locking -** changes the way database access is controlled by limiting access to a -** single host at a time and moving file locks off of the database file -** and onto a proxy file on the local file system. -** -** -** Using proxy locks -** ----------------- -** -** C APIs -** -** sqlite3_file_control(db, dbname, SQLITE_SET_LOCKPROXYFILE, -** | ":auto:"); -** sqlite3_file_control(db, dbname, SQLITE_GET_LOCKPROXYFILE, &); -** -** -** SQL pragmas -** -** PRAGMA [database.]lock_proxy_file= | :auto: -** PRAGMA [database.]lock_proxy_file -** -** Specifying ":auto:" means that if there is a conch file with a matching -** host ID in it, the proxy path in the conch file will be used, otherwise -** a proxy path based on the user's temp dir -** (via confstr(_CS_DARWIN_USER_TEMP_DIR,...)) will be used and the -** actual proxy file name is generated from the name and path of the -** database file. For example: -** -** For database path "/Users/me/foo.db" -** The lock path will be "/sqliteplocks/_Users_me_foo.db:auto:") -** -** Once a lock proxy is configured for a database connection, it can not -** be removed, however it may be switched to a different proxy path via -** the above APIs (assuming the conch file is not being held by another -** connection or process). -** -** -** How proxy locking works -** ----------------------- -** -** Proxy file locking relies primarily on two new supporting files: -** -** * conch file to limit access to the database file to a single host -** at a time -** -** * proxy file to act as a proxy for the advisory locks normally -** taken on the database -** -** The conch file - to use a proxy file, sqlite must first "hold the conch" -** by taking an sqlite-style shared lock on the conch file, reading the -** contents and comparing the host's unique host ID (see below) and lock -** proxy path against the values stored in the conch. The conch file is -** stored in the same directory as the database file and the file name -** is patterned after the database file name as ".-conch". -** If the conch file does not exist, or it's contents do not match the -** host ID and/or proxy path, then the lock is escalated to an exclusive -** lock and the conch file contents is updated with the host ID and proxy -** path and the lock is downgraded to a shared lock again. If the conch -** is held by another process (with a shared lock), the exclusive lock -** will fail and SQLITE_BUSY is returned. -** -** The proxy file - a single-byte file used for all advisory file locks -** normally taken on the database file. This allows for safe sharing -** of the database file for multiple readers and writers on the same -** host (the conch ensures that they all use the same local lock file). -** -** Requesting the lock proxy does not immediately take the conch, it is -** only taken when the first request to lock database file is made. -** This matches the semantics of the traditional locking behavior, where -** opening a connection to a database file does not take a lock on it. -** The shared lock and an open file descriptor are maintained until -** the connection to the database is closed. -** -** The proxy file and the lock file are never deleted so they only need -** to be created the first time they are used. -** -** Configuration options -** --------------------- -** -** SQLITE_PREFER_PROXY_LOCKING -** -** Database files accessed on non-local file systems are -** automatically configured for proxy locking, lock files are -** named automatically using the same logic as -** PRAGMA lock_proxy_file=":auto:" -** -** SQLITE_PROXY_DEBUG -** -** Enables the logging of error messages during host id file -** retrieval and creation -** -** LOCKPROXYDIR -** -** Overrides the default directory used for lock proxy files that -** are named automatically via the ":auto:" setting -** -** SQLITE_DEFAULT_PROXYDIR_PERMISSIONS -** -** Permissions to use when creating a directory for storing the -** lock proxy files, only used when LOCKPROXYDIR is not set. -** -** -** As mentioned above, when compiled with SQLITE_PREFER_PROXY_LOCKING, -** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will -** force proxy locking to be used for every database file opened, and 0 -** will force automatic proxy locking to be disabled for all database -** files (explicity calling the SQLITE_SET_LOCKPROXYFILE pragma or -** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING). -*/ - -/* -** Proxy locking is only available on MacOSX -*/ -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE - -/* -** The proxyLockingContext has the path and file structures for the remote -** and local proxy files in it -*/ -typedef struct proxyLockingContext proxyLockingContext; -struct proxyLockingContext { - unixFile *conchFile; /* Open conch file */ - char *conchFilePath; /* Name of the conch file */ - unixFile *lockProxy; /* Open proxy lock file */ - char *lockProxyPath; /* Name of the proxy lock file */ - char *dbPath; /* Name of the open file */ - int conchHeld; /* 1 if the conch is held, -1 if lockless */ - void *oldLockingContext; /* Original lockingcontext to restore on close */ - sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */ -}; - -/* -** The proxy lock file path for the database at dbPath is written into lPath, -** which must point to valid, writable memory large enough for a maxLen length -** file path. -*/ -static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ - int len; - int dbLen; - int i; - -#ifdef LOCKPROXYDIR - len = strlcpy(lPath, LOCKPROXYDIR, maxLen); -#else -# ifdef _CS_DARWIN_USER_TEMP_DIR - { - if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ - OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n", - lPath, errno, getpid())); - return SQLITE_IOERR_LOCK; - } - len = strlcat(lPath, "sqliteplocks", maxLen); - } -# else - len = strlcpy(lPath, "/tmp/", maxLen); -# endif -#endif - - if( lPath[len-1]!='/' ){ - len = strlcat(lPath, "/", maxLen); - } - - /* transform the db path to a unique cache name */ - dbLen = (int)strlen(dbPath); - for( i=0; i 0) ){ - /* only mkdir if leaf dir != "." or "/" or ".." */ - if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/') - || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){ - buf[i]='\0'; - if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ - int err=errno; - if( err!=EEXIST ) { - OSTRACE(("CREATELOCKPATH FAILED creating %s, " - "'%s' proxy lock path=%s pid=%d\n", - buf, strerror(err), lockPath, getpid())); - return err; - } - } - } - start=i+1; - } - buf[i] = lockPath[i]; - } - OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, getpid())); - return 0; -} - -/* -** Create a new VFS file descriptor (stored in memory obtained from -** sqlite3_malloc) and open the file named "path" in the file descriptor. -** -** The caller is responsible not only for closing the file descriptor -** but also for freeing the memory associated with the file descriptor. -*/ -static int proxyCreateUnixFile( - const char *path, /* path for the new unixFile */ - unixFile **ppFile, /* unixFile created and returned by ref */ - int islockfile /* if non zero missing dirs will be created */ -) { - int fd = -1; - unixFile *pNew; - int rc = SQLITE_OK; - int openFlags = O_RDWR | O_CREAT; - sqlite3_vfs dummyVfs; - int terrno = 0; - UnixUnusedFd *pUnused = NULL; - - /* 1. first try to open/create the file - ** 2. if that fails, and this is a lock file (not-conch), try creating - ** the parent directories and then try again. - ** 3. if that fails, try to open the file read-only - ** otherwise return BUSY (if lock file) or CANTOPEN for the conch file - */ - pUnused = findReusableFd(path, openFlags); - if( pUnused ){ - fd = pUnused->fd; - }else{ - pUnused = sqlite3_malloc(sizeof(*pUnused)); - if( !pUnused ){ - return SQLITE_NOMEM; - } - } - if( fd<0 ){ - fd = robust_open(path, openFlags, 0); - terrno = errno; - if( fd<0 && errno==ENOENT && islockfile ){ - if( proxyCreateLockPath(path) == SQLITE_OK ){ - fd = robust_open(path, openFlags, 0); - } - } - } - if( fd<0 ){ - openFlags = O_RDONLY; - fd = robust_open(path, openFlags, 0); - terrno = errno; - } - if( fd<0 ){ - if( islockfile ){ - return SQLITE_BUSY; - } - switch (terrno) { - case EACCES: - return SQLITE_PERM; - case EIO: - return SQLITE_IOERR_LOCK; /* even though it is the conch */ - default: - return SQLITE_CANTOPEN_BKPT; - } - } - - pNew = (unixFile *)sqlite3_malloc(sizeof(*pNew)); - if( pNew==NULL ){ - rc = SQLITE_NOMEM; - goto end_create_proxy; - } - memset(pNew, 0, sizeof(unixFile)); - pNew->openFlags = openFlags; - memset(&dummyVfs, 0, sizeof(dummyVfs)); - dummyVfs.pAppData = (void*)&autolockIoFinder; - dummyVfs.zName = "dummy"; - pUnused->fd = fd; - pUnused->flags = openFlags; - pNew->pUnused = pUnused; - - rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0); - if( rc==SQLITE_OK ){ - *ppFile = pNew; - return SQLITE_OK; - } -end_create_proxy: - robust_close(pNew, fd, __LINE__); - sqlite3_free(pNew); - sqlite3_free(pUnused); - return rc; -} - -#ifdef SQLITE_TEST -/* simulate multiple hosts by creating unique hostid file paths */ -SQLITE_API int sqlite3_hostid_num = 0; -#endif - -#define PROXY_HOSTIDLEN 16 /* conch file host id length */ - -/* Not always defined in the headers as it ought to be */ -extern int gethostuuid(uuid_t id, const struct timespec *wait); - -/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN -** bytes of writable memory. -*/ -static int proxyGetHostID(unsigned char *pHostID, int *pError){ - assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); - memset(pHostID, 0, PROXY_HOSTIDLEN); -#if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\ - && __MAC_OS_X_VERSION_MIN_REQUIRED<1050 - { - static const struct timespec timeout = {1, 0}; /* 1 sec timeout */ - if( gethostuuid(pHostID, &timeout) ){ - int err = errno; - if( pError ){ - *pError = err; - } - return SQLITE_IOERR; - } - } -#else - UNUSED_PARAMETER(pError); -#endif -#ifdef SQLITE_TEST - /* simulate multiple hosts by creating unique hostid file paths */ - if( sqlite3_hostid_num != 0){ - pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF)); - } -#endif - - return SQLITE_OK; -} - -/* The conch file contains the header, host id and lock file path - */ -#define PROXY_CONCHVERSION 2 /* 1-byte header, 16-byte host id, path */ -#define PROXY_HEADERLEN 1 /* conch file header length */ -#define PROXY_PATHINDEX (PROXY_HEADERLEN+PROXY_HOSTIDLEN) -#define PROXY_MAXCONCHLEN (PROXY_HEADERLEN+PROXY_HOSTIDLEN+MAXPATHLEN) - -/* -** Takes an open conch file, copies the contents to a new path and then moves -** it back. The newly created file's file descriptor is assigned to the -** conch file structure and finally the original conch file descriptor is -** closed. Returns zero if successful. -*/ -static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - unixFile *conchFile = pCtx->conchFile; - char tPath[MAXPATHLEN]; - char buf[PROXY_MAXCONCHLEN]; - char *cPath = pCtx->conchFilePath; - size_t readLen = 0; - size_t pathLen = 0; - char errmsg[64] = ""; - int fd = -1; - int rc = -1; - UNUSED_PARAMETER(myHostID); - - /* create a new path by replace the trailing '-conch' with '-break' */ - pathLen = strlcpy(tPath, cPath, MAXPATHLEN); - if( pathLen>MAXPATHLEN || pathLen<6 || - (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){ - sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen); - goto end_breaklock; - } - /* read the conch content */ - readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); - if( readLenh, __LINE__); - conchFile->h = fd; - conchFile->openFlags = O_RDWR | O_CREAT; - -end_breaklock: - if( rc ){ - if( fd>=0 ){ - osUnlink(tPath); - robust_close(pFile, fd, __LINE__); - } - fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg); - } - return rc; -} - -/* Take the requested lock on the conch file and break a stale lock if the -** host id matches. -*/ -static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - unixFile *conchFile = pCtx->conchFile; - int rc = SQLITE_OK; - int nTries = 0; - struct timespec conchModTime; - - memset(&conchModTime, 0, sizeof(conchModTime)); - do { - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType); - nTries ++; - if( rc==SQLITE_BUSY ){ - /* If the lock failed (busy): - * 1st try: get the mod time of the conch, wait 0.5s and try again. - * 2nd try: fail if the mod time changed or host id is different, wait - * 10 sec and try again - * 3rd try: break the lock unless the mod time has changed. - */ - struct stat buf; - if( osFstat(conchFile->h, &buf) ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_LOCK; - } - - if( nTries==1 ){ - conchModTime = buf.st_mtimespec; - usleep(500000); /* wait 0.5 sec and try the lock again*/ - continue; - } - - assert( nTries>1 ); - if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec || - conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){ - return SQLITE_BUSY; - } - - if( nTries==2 ){ - char tBuf[PROXY_MAXCONCHLEN]; - int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); - if( len<0 ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_LOCK; - } - if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ - /* don't break the lock if the host id doesn't match */ - if( 0!=memcmp(&tBuf[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN) ){ - return SQLITE_BUSY; - } - }else{ - /* don't break the lock on short read or a version mismatch */ - return SQLITE_BUSY; - } - usleep(10000000); /* wait 10 sec and try the lock again */ - continue; - } - - assert( nTries==3 ); - if( 0==proxyBreakConchLock(pFile, myHostID) ){ - rc = SQLITE_OK; - if( lockType==EXCLUSIVE_LOCK ){ - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK); - } - if( !rc ){ - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType); - } - } - } - } while( rc==SQLITE_BUSY && nTries<3 ); - - return rc; -} - -/* Takes the conch by taking a shared lock and read the contents conch, if -** lockPath is non-NULL, the host ID and lock file path must match. A NULL -** lockPath means that the lockPath in the conch file will be used if the -** host IDs match, or a new lock path will be generated automatically -** and written to the conch file. -*/ -static int proxyTakeConch(unixFile *pFile){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - - if( pCtx->conchHeld!=0 ){ - return SQLITE_OK; - }else{ - unixFile *conchFile = pCtx->conchFile; - uuid_t myHostID; - int pError = 0; - char readBuf[PROXY_MAXCONCHLEN]; - char lockPath[MAXPATHLEN]; - char *tempLockPath = NULL; - int rc = SQLITE_OK; - int createConch = 0; - int hostIdMatch = 0; - int readLen = 0; - int tryOldLockPath = 0; - int forceNewLockPath = 0; - - OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h, - (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), getpid())); - - rc = proxyGetHostID(myHostID, &pError); - if( (rc&0xff)==SQLITE_IOERR ){ - pFile->lastErrno = pError; - goto end_takeconch; - } - rc = proxyConchLock(pFile, myHostID, SHARED_LOCK); - if( rc!=SQLITE_OK ){ - goto end_takeconch; - } - /* read the existing conch file */ - readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN); - if( readLen<0 ){ - /* I/O error: lastErrno set by seekAndRead */ - pFile->lastErrno = conchFile->lastErrno; - rc = SQLITE_IOERR_READ; - goto end_takeconch; - }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) || - readBuf[0]!=(char)PROXY_CONCHVERSION ){ - /* a short read or version format mismatch means we need to create a new - ** conch file. - */ - createConch = 1; - } - /* if the host id matches and the lock path already exists in the conch - ** we'll try to use the path there, if we can't open that path, we'll - ** retry with a new auto-generated path - */ - do { /* in case we need to try again for an :auto: named lock file */ - - if( !createConch && !forceNewLockPath ){ - hostIdMatch = !memcmp(&readBuf[PROXY_HEADERLEN], myHostID, - PROXY_HOSTIDLEN); - /* if the conch has data compare the contents */ - if( !pCtx->lockProxyPath ){ - /* for auto-named local lock file, just check the host ID and we'll - ** use the local lock file path that's already in there - */ - if( hostIdMatch ){ - size_t pathLen = (readLen - PROXY_PATHINDEX); - - if( pathLen>=MAXPATHLEN ){ - pathLen=MAXPATHLEN-1; - } - memcpy(lockPath, &readBuf[PROXY_PATHINDEX], pathLen); - lockPath[pathLen] = 0; - tempLockPath = lockPath; - tryOldLockPath = 1; - /* create a copy of the lock path if the conch is taken */ - goto end_takeconch; - } - }else if( hostIdMatch - && !strncmp(pCtx->lockProxyPath, &readBuf[PROXY_PATHINDEX], - readLen-PROXY_PATHINDEX) - ){ - /* conch host and lock path match */ - goto end_takeconch; - } - } - - /* if the conch isn't writable and doesn't match, we can't take it */ - if( (conchFile->openFlags&O_RDWR) == 0 ){ - rc = SQLITE_BUSY; - goto end_takeconch; - } - - /* either the conch didn't match or we need to create a new one */ - if( !pCtx->lockProxyPath ){ - proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN); - tempLockPath = lockPath; - /* create a copy of the lock path _only_ if the conch is taken */ - } - - /* update conch with host and path (this will fail if other process - ** has a shared lock already), if the host id matches, use the big - ** stick. - */ - futimes(conchFile->h, NULL); - if( hostIdMatch && !createConch ){ - if( conchFile->pInode && conchFile->pInode->nShared>1 ){ - /* We are trying for an exclusive lock but another thread in this - ** same process is still holding a shared lock. */ - rc = SQLITE_BUSY; - } else { - rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); - } - }else{ - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, EXCLUSIVE_LOCK); - } - if( rc==SQLITE_OK ){ - char writeBuffer[PROXY_MAXCONCHLEN]; - int writeSize = 0; - - writeBuffer[0] = (char)PROXY_CONCHVERSION; - memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN); - if( pCtx->lockProxyPath!=NULL ){ - strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, MAXPATHLEN); - }else{ - strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); - } - writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]); - robust_ftruncate(conchFile->h, writeSize); - rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0); - fsync(conchFile->h); - /* If we created a new conch file (not just updated the contents of a - ** valid conch file), try to match the permissions of the database - */ - if( rc==SQLITE_OK && createConch ){ - struct stat buf; - int err = osFstat(pFile->h, &buf); - if( err==0 ){ - mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | - S_IROTH|S_IWOTH); - /* try to match the database file R/W permissions, ignore failure */ -#ifndef SQLITE_PROXY_DEBUG - osFchmod(conchFile->h, cmode); -#else - do{ - rc = osFchmod(conchFile->h, cmode); - }while( rc==(-1) && errno==EINTR ); - if( rc!=0 ){ - int code = errno; - fprintf(stderr, "fchmod %o FAILED with %d %s\n", - cmode, code, strerror(code)); - } else { - fprintf(stderr, "fchmod %o SUCCEDED\n",cmode); - } - }else{ - int code = errno; - fprintf(stderr, "STAT FAILED[%d] with %d %s\n", - err, code, strerror(code)); -#endif - } - } - } - conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK); - - end_takeconch: - OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); - if( rc==SQLITE_OK && pFile->openFlags ){ - int fd; - if( pFile->h>=0 ){ - robust_close(pFile, pFile->h, __LINE__); - } - pFile->h = -1; - fd = robust_open(pCtx->dbPath, pFile->openFlags, 0); - OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); - if( fd>=0 ){ - pFile->h = fd; - }else{ - rc=SQLITE_CANTOPEN_BKPT; /* SQLITE_BUSY? proxyTakeConch called - during locking */ - } - } - if( rc==SQLITE_OK && !pCtx->lockProxy ){ - char *path = tempLockPath ? tempLockPath : pCtx->lockProxyPath; - rc = proxyCreateUnixFile(path, &pCtx->lockProxy, 1); - if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && tryOldLockPath ){ - /* we couldn't create the proxy lock file with the old lock file path - ** so try again via auto-naming - */ - forceNewLockPath = 1; - tryOldLockPath = 0; - continue; /* go back to the do {} while start point, try again */ - } - } - if( rc==SQLITE_OK ){ - /* Need to make a copy of path if we extracted the value - ** from the conch file or the path was allocated on the stack - */ - if( tempLockPath ){ - pCtx->lockProxyPath = sqlite3DbStrDup(0, tempLockPath); - if( !pCtx->lockProxyPath ){ - rc = SQLITE_NOMEM; - } - } - } - if( rc==SQLITE_OK ){ - pCtx->conchHeld = 1; - - if( pCtx->lockProxy->pMethod == &afpIoMethods ){ - afpLockingContext *afpCtx; - afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext; - afpCtx->dbPath = pCtx->lockProxyPath; - } - } else { - conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); - } - OSTRACE(("TAKECONCH %d %s\n", conchFile->h, - rc==SQLITE_OK?"ok":"failed")); - return rc; - } while (1); /* in case we need to retry the :auto: lock file - - ** we should never get here except via the 'continue' call. */ - } -} - -/* -** If pFile holds a lock on a conch file, then release that lock. -*/ -static int proxyReleaseConch(unixFile *pFile){ - int rc = SQLITE_OK; /* Subroutine return code */ - proxyLockingContext *pCtx; /* The locking context for the proxy lock */ - unixFile *conchFile; /* Name of the conch file */ - - pCtx = (proxyLockingContext *)pFile->lockingContext; - conchFile = pCtx->conchFile; - OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h, - (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), - getpid())); - if( pCtx->conchHeld>0 ){ - rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); - } - pCtx->conchHeld = 0; - OSTRACE(("RELEASECONCH %d %s\n", conchFile->h, - (rc==SQLITE_OK ? "ok" : "failed"))); - return rc; -} - -/* -** Given the name of a database file, compute the name of its conch file. -** Store the conch filename in memory obtained from sqlite3_malloc(). -** Make *pConchPath point to the new name. Return SQLITE_OK on success -** or SQLITE_NOMEM if unable to obtain memory. -** -** The caller is responsible for ensuring that the allocated memory -** space is eventually freed. -** -** *pConchPath is set to NULL if a memory allocation error occurs. -*/ -static int proxyCreateConchPathname(char *dbPath, char **pConchPath){ - int i; /* Loop counter */ - int len = (int)strlen(dbPath); /* Length of database filename - dbPath */ - char *conchPath; /* buffer in which to construct conch name */ - - /* Allocate space for the conch filename and initialize the name to - ** the name of the original database file. */ - *pConchPath = conchPath = (char *)sqlite3_malloc(len + 8); - if( conchPath==0 ){ - return SQLITE_NOMEM; - } - memcpy(conchPath, dbPath, len+1); - - /* now insert a "." before the last / character */ - for( i=(len-1); i>=0; i-- ){ - if( conchPath[i]=='/' ){ - i++; - break; - } - } - conchPath[i]='.'; - while ( ilockingContext; - char *oldPath = pCtx->lockProxyPath; - int rc = SQLITE_OK; - - if( pFile->eFileLock!=NO_LOCK ){ - return SQLITE_BUSY; - } - - /* nothing to do if the path is NULL, :auto: or matches the existing path */ - if( !path || path[0]=='\0' || !strcmp(path, ":auto:") || - (oldPath && !strncmp(oldPath, path, MAXPATHLEN)) ){ - return SQLITE_OK; - }else{ - unixFile *lockProxy = pCtx->lockProxy; - pCtx->lockProxy=NULL; - pCtx->conchHeld = 0; - if( lockProxy!=NULL ){ - rc=lockProxy->pMethod->xClose((sqlite3_file *)lockProxy); - if( rc ) return rc; - sqlite3_free(lockProxy); - } - sqlite3_free(oldPath); - pCtx->lockProxyPath = sqlite3DbStrDup(0, path); - } - - return rc; -} - -/* -** pFile is a file that has been opened by a prior xOpen call. dbPath -** is a string buffer at least MAXPATHLEN+1 characters in size. -** -** This routine find the filename associated with pFile and writes it -** int dbPath. -*/ -static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){ -#if defined(__APPLE__) - if( pFile->pMethod == &afpIoMethods ){ - /* afp style keeps a reference to the db path in the filePath field - ** of the struct */ - assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); - strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, MAXPATHLEN); - } else -#endif - if( pFile->pMethod == &dotlockIoMethods ){ - /* dot lock style uses the locking context to store the dot lock - ** file path */ - int len = strlen((char *)pFile->lockingContext) - strlen(DOTLOCK_SUFFIX); - memcpy(dbPath, (char *)pFile->lockingContext, len + 1); - }else{ - /* all other styles use the locking context to store the db file path */ - assert( strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); - strlcpy(dbPath, (char *)pFile->lockingContext, MAXPATHLEN); - } - return SQLITE_OK; -} - -/* -** Takes an already filled in unix file and alters it so all file locking -** will be performed on the local proxy lock file. The following fields -** are preserved in the locking context so that they can be restored and -** the unix structure properly cleaned up at close time: -** ->lockingContext -** ->pMethod -*/ -static int proxyTransformUnixFile(unixFile *pFile, const char *path) { - proxyLockingContext *pCtx; - char dbPath[MAXPATHLEN+1]; /* Name of the database file */ - char *lockPath=NULL; - int rc = SQLITE_OK; - - if( pFile->eFileLock!=NO_LOCK ){ - return SQLITE_BUSY; - } - proxyGetDbPathForUnixFile(pFile, dbPath); - if( !path || path[0]=='\0' || !strcmp(path, ":auto:") ){ - lockPath=NULL; - }else{ - lockPath=(char *)path; - } - - OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h, - (lockPath ? lockPath : ":auto:"), getpid())); - - pCtx = sqlite3_malloc( sizeof(*pCtx) ); - if( pCtx==0 ){ - return SQLITE_NOMEM; - } - memset(pCtx, 0, sizeof(*pCtx)); - - rc = proxyCreateConchPathname(dbPath, &pCtx->conchFilePath); - if( rc==SQLITE_OK ){ - rc = proxyCreateUnixFile(pCtx->conchFilePath, &pCtx->conchFile, 0); - if( rc==SQLITE_CANTOPEN && ((pFile->openFlags&O_RDWR) == 0) ){ - /* if (a) the open flags are not O_RDWR, (b) the conch isn't there, and - ** (c) the file system is read-only, then enable no-locking access. - ** Ugh, since O_RDONLY==0x0000 we test for !O_RDWR since unixOpen asserts - ** that openFlags will have only one of O_RDONLY or O_RDWR. - */ - struct statfs fsInfo; - struct stat conchInfo; - int goLockless = 0; - - if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) { - int err = errno; - if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){ - goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY; - } - } - if( goLockless ){ - pCtx->conchHeld = -1; /* read only FS/ lockless */ - rc = SQLITE_OK; - } - } - } - if( rc==SQLITE_OK && lockPath ){ - pCtx->lockProxyPath = sqlite3DbStrDup(0, lockPath); - } - - if( rc==SQLITE_OK ){ - pCtx->dbPath = sqlite3DbStrDup(0, dbPath); - if( pCtx->dbPath==NULL ){ - rc = SQLITE_NOMEM; - } - } - if( rc==SQLITE_OK ){ - /* all memory is allocated, proxys are created and assigned, - ** switch the locking context and pMethod then return. - */ - pCtx->oldLockingContext = pFile->lockingContext; - pFile->lockingContext = pCtx; - pCtx->pOldMethod = pFile->pMethod; - pFile->pMethod = &proxyIoMethods; - }else{ - if( pCtx->conchFile ){ - pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile); - sqlite3_free(pCtx->conchFile); - } - sqlite3DbFree(0, pCtx->lockProxyPath); - sqlite3_free(pCtx->conchFilePath); - sqlite3_free(pCtx); - } - OSTRACE(("TRANSPROXY %d %s\n", pFile->h, - (rc==SQLITE_OK ? "ok" : "failed"))); - return rc; -} - - -/* -** This routine handles sqlite3_file_control() calls that are specific -** to proxy locking. -*/ -static int proxyFileControl(sqlite3_file *id, int op, void *pArg){ - switch( op ){ - case SQLITE_GET_LOCKPROXYFILE: { - unixFile *pFile = (unixFile*)id; - if( pFile->pMethod == &proxyIoMethods ){ - proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; - proxyTakeConch(pFile); - if( pCtx->lockProxyPath ){ - *(const char **)pArg = pCtx->lockProxyPath; - }else{ - *(const char **)pArg = ":auto: (not held)"; - } - } else { - *(const char **)pArg = NULL; - } - return SQLITE_OK; - } - case SQLITE_SET_LOCKPROXYFILE: { - unixFile *pFile = (unixFile*)id; - int rc = SQLITE_OK; - int isProxyStyle = (pFile->pMethod == &proxyIoMethods); - if( pArg==NULL || (const char *)pArg==0 ){ - if( isProxyStyle ){ - /* turn off proxy locking - not supported */ - rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/; - }else{ - /* turn off proxy locking - already off - NOOP */ - rc = SQLITE_OK; - } - }else{ - const char *proxyPath = (const char *)pArg; - if( isProxyStyle ){ - proxyLockingContext *pCtx = - (proxyLockingContext*)pFile->lockingContext; - if( !strcmp(pArg, ":auto:") - || (pCtx->lockProxyPath && - !strncmp(pCtx->lockProxyPath, proxyPath, MAXPATHLEN)) - ){ - rc = SQLITE_OK; - }else{ - rc = switchLockProxyPath(pFile, proxyPath); - } - }else{ - /* turn on proxy file locking */ - rc = proxyTransformUnixFile(pFile, proxyPath); - } - } - return rc; - } - default: { - assert( 0 ); /* The call assures that only valid opcodes are sent */ - } - } - /*NOTREACHED*/ - return SQLITE_ERROR; -} - -/* -** Within this division (the proxying locking implementation) the procedures -** above this point are all utilities. The lock-related methods of the -** proxy-locking sqlite3_io_method object follow. -*/ - - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -*/ -static int proxyCheckReservedLock(sqlite3_file *id, int *pResOut) { - unixFile *pFile = (unixFile*)id; - int rc = proxyTakeConch(pFile); - if( rc==SQLITE_OK ){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - if( pCtx->conchHeld>0 ){ - unixFile *proxy = pCtx->lockProxy; - return proxy->pMethod->xCheckReservedLock((sqlite3_file*)proxy, pResOut); - }else{ /* conchHeld < 0 is lockless */ - pResOut=0; - } - } - return rc; -} - -/* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. Use the sqlite3OsUnlock() -** routine to lower a locking level. -*/ -static int proxyLock(sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - int rc = proxyTakeConch(pFile); - if( rc==SQLITE_OK ){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - if( pCtx->conchHeld>0 ){ - unixFile *proxy = pCtx->lockProxy; - rc = proxy->pMethod->xLock((sqlite3_file*)proxy, eFileLock); - pFile->eFileLock = proxy->eFileLock; - }else{ - /* conchHeld < 0 is lockless */ - } - } - return rc; -} - - -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -*/ -static int proxyUnlock(sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - int rc = proxyTakeConch(pFile); - if( rc==SQLITE_OK ){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - if( pCtx->conchHeld>0 ){ - unixFile *proxy = pCtx->lockProxy; - rc = proxy->pMethod->xUnlock((sqlite3_file*)proxy, eFileLock); - pFile->eFileLock = proxy->eFileLock; - }else{ - /* conchHeld < 0 is lockless */ - } - } - return rc; -} - -/* -** Close a file that uses proxy locks. -*/ -static int proxyClose(sqlite3_file *id) { - if( id ){ - unixFile *pFile = (unixFile*)id; - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - unixFile *lockProxy = pCtx->lockProxy; - unixFile *conchFile = pCtx->conchFile; - int rc = SQLITE_OK; - - if( lockProxy ){ - rc = lockProxy->pMethod->xUnlock((sqlite3_file*)lockProxy, NO_LOCK); - if( rc ) return rc; - rc = lockProxy->pMethod->xClose((sqlite3_file*)lockProxy); - if( rc ) return rc; - sqlite3_free(lockProxy); - pCtx->lockProxy = 0; - } - if( conchFile ){ - if( pCtx->conchHeld ){ - rc = proxyReleaseConch(pFile); - if( rc ) return rc; - } - rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile); - if( rc ) return rc; - sqlite3_free(conchFile); - } - sqlite3DbFree(0, pCtx->lockProxyPath); - sqlite3_free(pCtx->conchFilePath); - sqlite3DbFree(0, pCtx->dbPath); - /* restore the original locking context and pMethod then close it */ - pFile->lockingContext = pCtx->oldLockingContext; - pFile->pMethod = pCtx->pOldMethod; - sqlite3_free(pCtx); - return pFile->pMethod->xClose(id); - } - return SQLITE_OK; -} - - - -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ -/* -** The proxy locking style is intended for use with AFP filesystems. -** And since AFP is only supported on MacOSX, the proxy locking is also -** restricted to MacOSX. -** -** -******************* End of the proxy lock implementation ********************** -******************************************************************************/ - -/* -** Initialize the operating system interface. -** -** This routine registers all VFS implementations for unix-like operating -** systems. This routine, and the sqlite3_os_end() routine that follows, -** should be the only routines in this file that are visible from other -** files. -** -** This routine is called once during SQLite initialization and by a -** single thread. The memory allocation and mutex subsystems have not -** necessarily been initialized when this routine is called, and so they -** should not be used. -*/ -SQLITE_API int sqlite3_os_init(void){ - /* - ** The following macro defines an initializer for an sqlite3_vfs object. - ** The name of the VFS is NAME. The pAppData is a pointer to a pointer - ** to the "finder" function. (pAppData is a pointer to a pointer because - ** silly C90 rules prohibit a void* from being cast to a function pointer - ** and so we have to go through the intermediate pointer to avoid problems - ** when compiling with -pedantic-errors on GCC.) - ** - ** The FINDER parameter to this macro is the name of the pointer to the - ** finder-function. The finder-function returns a pointer to the - ** sqlite_io_methods object that implements the desired locking - ** behaviors. See the division above that contains the IOMETHODS - ** macro for addition information on finder-functions. - ** - ** Most finders simply return a pointer to a fixed sqlite3_io_methods - ** object. But the "autolockIoFinder" available on MacOSX does a little - ** more than that; it looks at the filesystem type that hosts the - ** database file and tries to choose an locking method appropriate for - ** that filesystem time. - */ - #define UNIXVFS(VFSNAME, FINDER) { \ - 3, /* iVersion */ \ - sizeof(unixFile), /* szOsFile */ \ - MAX_PATHNAME, /* mxPathname */ \ - 0, /* pNext */ \ - VFSNAME, /* zName */ \ - (void*)&FINDER, /* pAppData */ \ - unixOpen, /* xOpen */ \ - unixDelete, /* xDelete */ \ - unixAccess, /* xAccess */ \ - unixFullPathname, /* xFullPathname */ \ - unixDlOpen, /* xDlOpen */ \ - unixDlError, /* xDlError */ \ - unixDlSym, /* xDlSym */ \ - unixDlClose, /* xDlClose */ \ - unixRandomness, /* xRandomness */ \ - unixSleep, /* xSleep */ \ - unixCurrentTime, /* xCurrentTime */ \ - unixGetLastError, /* xGetLastError */ \ - unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ - unixSetSystemCall, /* xSetSystemCall */ \ - unixGetSystemCall, /* xGetSystemCall */ \ - unixNextSystemCall, /* xNextSystemCall */ \ - } - - /* - ** All default VFSes for unix are contained in the following array. - ** - ** Note that the sqlite3_vfs.pNext field of the VFS object is modified - ** by the SQLite core when the VFS is registered. So the following - ** array cannot be const. - */ - static sqlite3_vfs aVfs[] = { -#if SQLITE_ENABLE_LOCKING_STYLE && (OS_VXWORKS || defined(__APPLE__)) - UNIXVFS("unix", autolockIoFinder ), -#else - UNIXVFS("unix", posixIoFinder ), -#endif - UNIXVFS("unix-none", nolockIoFinder ), - UNIXVFS("unix-dotfile", dotlockIoFinder ), - UNIXVFS("unix-excl", posixIoFinder ), -#if OS_VXWORKS - UNIXVFS("unix-namedsem", semIoFinder ), -#endif -#if SQLITE_ENABLE_LOCKING_STYLE - UNIXVFS("unix-posix", posixIoFinder ), -#if !OS_VXWORKS - UNIXVFS("unix-flock", flockIoFinder ), -#endif -#endif -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - UNIXVFS("unix-afp", afpIoFinder ), - UNIXVFS("unix-nfs", nfsIoFinder ), - UNIXVFS("unix-proxy", proxyIoFinder ), -#endif - }; - unsigned int i; /* Loop counter */ - - /* Double-check that the aSyscall[] array has been constructed - ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==21 ); - - /* Register all VFSes defined in the aVfs[] array */ - for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ - sqlite3_vfs_register(&aVfs[i], i==0); - } - return SQLITE_OK; -} - -/* -** Shutdown the operating system interface. -** -** Some operating systems might need to do some cleanup in this routine, -** to release dynamically allocated objects. But not on unix. -** This routine is a no-op for unix. -*/ -SQLITE_API int sqlite3_os_end(void){ - return SQLITE_OK; -} - -#endif /* SQLITE_OS_UNIX */ - -/************** End of os_unix.c *********************************************/ -/************** Begin file os_win.c ******************************************/ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains code that is specific to Windows. -*/ -#if SQLITE_OS_WIN /* This file is used for Windows only */ - -#ifdef __CYGWIN__ -# include -#endif - -/* -** Include code that is common to all os_*.c files -*/ -/************** Include os_common.h in the middle of os_win.c ****************/ -/************** Begin file os_common.h ***************************************/ -/* -** 2004 May 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains macros and a little bit of code that is common to -** all of the platform-specific files (os_*.c) and is #included into those -** files. -** -** This file should be #included by the os_*.c files only. It is not a -** general purpose header file. -*/ -#ifndef _OS_COMMON_H_ -#define _OS_COMMON_H_ - -/* -** At least two bugs have slipped in because we changed the MEMORY_DEBUG -** macro to SQLITE_DEBUG and some older makefiles have not yet made the -** switch. The following code should catch this problem at compile-time. -*/ -#ifdef MEMORY_DEBUG -# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." -#endif - -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -# ifndef SQLITE_DEBUG_OS_TRACE -# define SQLITE_DEBUG_OS_TRACE 0 -# endif - int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; -# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X -#else -# define OSTRACE(X) -#endif - -/* -** Macros for performance tracing. Normally turned off. Only works -** on i486 hardware. -*/ -#ifdef SQLITE_PERFORMANCE_TRACE - -/* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of os_common.h ****************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains inline asm code for retrieving "high-performance" -** counters for x86 class CPUs. -*/ -#ifndef _HWTIME_H_ -#define _HWTIME_H_ - -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - -#elif (defined(__GNUC__) && defined(__x86_64__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } - -#elif (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } - -#else - - #error Need implementation of sqlite3Hwtime() for your platform. - - /* - ** To compile without implementing sqlite3Hwtime() for your platform, - ** you can remove the above #error and use the following - ** stub function. You will lose timing support for many - ** of the debugging and testing utilities, but it should at - ** least compile and run. - */ -SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(_HWTIME_H_) */ - -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in os_common.h ******************/ - -static sqlite_uint64 g_start; -static sqlite_uint64 g_elapsed; -#define TIMER_START g_start=sqlite3Hwtime() -#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start -#define TIMER_ELAPSED g_elapsed -#else -#define TIMER_START -#define TIMER_END -#define TIMER_ELAPSED ((sqlite_uint64)0) -#endif - -/* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */ -SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */ -SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */ -SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */ -SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */ -SQLITE_API int sqlite3_diskfull_pending = 0; -SQLITE_API int sqlite3_diskfull = 0; -#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) -#define SimulateIOError(CODE) \ - if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ - || sqlite3_io_error_pending-- == 1 ) \ - { local_ioerr(); CODE; } -static void local_ioerr(){ - IOTRACE(("IOERR\n")); - sqlite3_io_error_hit++; - if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; -} -#define SimulateDiskfullError(CODE) \ - if( sqlite3_diskfull_pending ){ \ - if( sqlite3_diskfull_pending == 1 ){ \ - local_ioerr(); \ - sqlite3_diskfull = 1; \ - sqlite3_io_error_hit = 1; \ - CODE; \ - }else{ \ - sqlite3_diskfull_pending--; \ - } \ - } -#else -#define SimulateIOErrorBenign(X) -#define SimulateIOError(A) -#define SimulateDiskfullError(A) -#endif - -/* -** When testing, keep a count of the number of open files. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_open_file_count = 0; -#define OpenCounter(X) sqlite3_open_file_count+=(X) -#else -#define OpenCounter(X) -#endif - -#endif /* !defined(_OS_COMMON_H_) */ - -/************** End of os_common.h *******************************************/ -/************** Continuing where we left off in os_win.c *********************/ - -/* -** Compiling and using WAL mode requires several APIs that are only -** available in Windows platforms based on the NT kernel. -*/ -#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL) -# error "WAL mode requires support from the Windows NT kernel, compile\ - with SQLITE_OMIT_WAL." -#endif - -/* -** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions -** based on the sub-platform)? -*/ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT -# define SQLITE_WIN32_HAS_ANSI -#endif - -/* -** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions -** based on the sub-platform)? -*/ -#if SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT -# define SQLITE_WIN32_HAS_WIDE -#endif - -/* -** Do we need to manually define the Win32 file mapping APIs for use with WAL -** mode (e.g. these APIs are available in the Windows CE SDK; however, they -** are not present in the header file)? -*/ -#if SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) -/* -** Two of the file mapping APIs are different under WinRT. Figure out which -** set we need. -*/ -#if SQLITE_OS_WINRT -WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \ - LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR); - -WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T); -#else -#if defined(SQLITE_WIN32_HAS_ANSI) -WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \ - DWORD, DWORD, DWORD, LPCSTR); -#endif /* defined(SQLITE_WIN32_HAS_ANSI) */ - -#if defined(SQLITE_WIN32_HAS_WIDE) -WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \ - DWORD, DWORD, DWORD, LPCWSTR); -#endif /* defined(SQLITE_WIN32_HAS_WIDE) */ - -WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); -#endif /* SQLITE_OS_WINRT */ - -/* -** This file mapping API is common to both Win32 and WinRT. -*/ -WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); -#endif /* SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) */ - -/* -** Macro to find the minimum of two numeric values. -*/ -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -/* -** Some Microsoft compilers lack this definition. -*/ -#ifndef INVALID_FILE_ATTRIBUTES -# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) -#endif - -#ifndef FILE_FLAG_MASK -# define FILE_FLAG_MASK (0xFF3C0000) -#endif - -#ifndef FILE_ATTRIBUTE_MASK -# define FILE_ATTRIBUTE_MASK (0x0003FFF7) -#endif - -#ifndef SQLITE_OMIT_WAL -/* Forward references */ -typedef struct winShm winShm; /* A connection to shared-memory */ -typedef struct winShmNode winShmNode; /* A region of shared-memory */ -#endif - -/* -** WinCE lacks native support for file locking so we have to fake it -** with some code of our own. -*/ -#if SQLITE_OS_WINCE -typedef struct winceLock { - int nReaders; /* Number of reader locks obtained */ - BOOL bPending; /* Indicates a pending lock has been obtained */ - BOOL bReserved; /* Indicates a reserved lock has been obtained */ - BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ -} winceLock; -#endif - -/* -** The winFile structure is a subclass of sqlite3_file* specific to the win32 -** portability layer. -*/ -typedef struct winFile winFile; -struct winFile { - const sqlite3_io_methods *pMethod; /*** Must be first ***/ - sqlite3_vfs *pVfs; /* The VFS used to open this file */ - HANDLE h; /* Handle for accessing the file */ - u8 locktype; /* Type of lock currently held on this file */ - short sharedLockByte; /* Randomly chosen byte used as a shared lock */ - u8 ctrlFlags; /* Flags. See WINFILE_* below */ - DWORD lastErrno; /* The Windows errno from the last I/O error */ -#ifndef SQLITE_OMIT_WAL - winShm *pShm; /* Instance of shared memory on this file */ -#endif - const char *zPath; /* Full pathname of this file */ - int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ -#if SQLITE_OS_WINCE - LPWSTR zDeleteOnClose; /* Name of file to delete when closing */ - HANDLE hMutex; /* Mutex used to control access to shared lock */ - HANDLE hShared; /* Shared memory segment used for locking */ - winceLock local; /* Locks obtained by this instance of winFile */ - winceLock *shared; /* Global shared lock memory for the file */ -#endif -}; - -/* -** Allowed values for winFile.ctrlFlags -*/ -#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ -#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ - -/* - * The size of the buffer used by sqlite3_win32_write_debug(). - */ -#ifndef SQLITE_WIN32_DBG_BUF_SIZE -# define SQLITE_WIN32_DBG_BUF_SIZE ((int)(4096-sizeof(DWORD))) -#endif - -/* - * The value used with sqlite3_win32_set_directory() to specify that - * the data directory should be changed. - */ -#ifndef SQLITE_WIN32_DATA_DIRECTORY_TYPE -# define SQLITE_WIN32_DATA_DIRECTORY_TYPE (1) -#endif - -/* - * The value used with sqlite3_win32_set_directory() to specify that - * the temporary directory should be changed. - */ -#ifndef SQLITE_WIN32_TEMP_DIRECTORY_TYPE -# define SQLITE_WIN32_TEMP_DIRECTORY_TYPE (2) -#endif - -/* - * If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the - * various Win32 API heap functions instead of our own. - */ -#ifdef SQLITE_WIN32_MALLOC - -/* - * If this is non-zero, an isolated heap will be created by the native Win32 - * allocator subsystem; otherwise, the default process heap will be used. This - * setting has no effect when compiling for WinRT. By default, this is enabled - * and an isolated heap will be created to store all allocated data. - * - ****************************************************************************** - * WARNING: It is important to note that when this setting is non-zero and the - * winMemShutdown function is called (e.g. by the sqlite3_shutdown - * function), all data that was allocated using the isolated heap will - * be freed immediately and any attempt to access any of that freed - * data will almost certainly result in an immediate access violation. - ****************************************************************************** - */ -#ifndef SQLITE_WIN32_HEAP_CREATE -# define SQLITE_WIN32_HEAP_CREATE (TRUE) -#endif - -/* - * The initial size of the Win32-specific heap. This value may be zero. - */ -#ifndef SQLITE_WIN32_HEAP_INIT_SIZE -# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_DEFAULT_CACHE_SIZE) * \ - (SQLITE_DEFAULT_PAGE_SIZE) + 4194304) -#endif - -/* - * The maximum size of the Win32-specific heap. This value may be zero. - */ -#ifndef SQLITE_WIN32_HEAP_MAX_SIZE -# define SQLITE_WIN32_HEAP_MAX_SIZE (0) -#endif - -/* - * The extra flags to use in calls to the Win32 heap APIs. This value may be - * zero for the default behavior. - */ -#ifndef SQLITE_WIN32_HEAP_FLAGS -# define SQLITE_WIN32_HEAP_FLAGS (0) -#endif - -/* -** The winMemData structure stores information required by the Win32-specific -** sqlite3_mem_methods implementation. -*/ -typedef struct winMemData winMemData; -struct winMemData { -#ifndef NDEBUG - u32 magic; /* Magic number to detect structure corruption. */ -#endif - HANDLE hHeap; /* The handle to our heap. */ - BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */ -}; - -#ifndef NDEBUG -#define WINMEM_MAGIC 0x42b2830b -#endif - -static struct winMemData win_mem_data = { -#ifndef NDEBUG - WINMEM_MAGIC, -#endif - NULL, FALSE -}; - -#ifndef NDEBUG -#define winMemAssertMagic() assert( win_mem_data.magic==WINMEM_MAGIC ) -#else -#define winMemAssertMagic() -#endif - -#define winMemGetHeap() win_mem_data.hHeap - -static void *winMemMalloc(int nBytes); -static void winMemFree(void *pPrior); -static void *winMemRealloc(void *pPrior, int nBytes); -static int winMemSize(void *p); -static int winMemRoundup(int n); -static int winMemInit(void *pAppData); -static void winMemShutdown(void *pAppData); - -SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void); -#endif /* SQLITE_WIN32_MALLOC */ - -/* -** The following variable is (normally) set once and never changes -** thereafter. It records whether the operating system is Win9x -** or WinNT. -** -** 0: Operating system unknown. -** 1: Operating system is Win9x. -** 2: Operating system is WinNT. -** -** In order to facilitate testing on a WinNT system, the test fixture -** can manually set this value to 1 to emulate Win98 behavior. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_os_type = 0; -#else -static int sqlite3_os_type = 0; -#endif - -#ifndef SYSCALL -# define SYSCALL sqlite3_syscall_ptr -#endif - -/* -** This function is not available on Windows CE or WinRT. - */ - -#if SQLITE_OS_WINCE || SQLITE_OS_WINRT -# define osAreFileApisANSI() 1 -#endif - -/* -** Many system calls are accessed through pointer-to-functions so that -** they may be overridden at runtime to facilitate fault injection during -** testing and sandboxing. The following array holds the names and pointers -** to all overrideable system calls. -*/ -static struct win_syscall { - const char *zName; /* Name of the system call */ - sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ - sqlite3_syscall_ptr pDefault; /* Default value */ -} aSyscall[] = { -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT - { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 }, -#else - { "AreFileApisANSI", (SYSCALL)0, 0 }, -#endif - -#ifndef osAreFileApisANSI -#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent) -#endif - -#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) - { "CharLowerW", (SYSCALL)CharLowerW, 0 }, -#else - { "CharLowerW", (SYSCALL)0, 0 }, -#endif - -#define osCharLowerW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[1].pCurrent) - -#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) - { "CharUpperW", (SYSCALL)CharUpperW, 0 }, -#else - { "CharUpperW", (SYSCALL)0, 0 }, -#endif - -#define osCharUpperW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[2].pCurrent) - - { "CloseHandle", (SYSCALL)CloseHandle, 0 }, - -#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[3].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) - { "CreateFileA", (SYSCALL)CreateFileA, 0 }, -#else - { "CreateFileA", (SYSCALL)0, 0 }, -#endif - -#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \ - LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent) - -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "CreateFileW", (SYSCALL)CreateFileW, 0 }, -#else - { "CreateFileW", (SYSCALL)0, 0 }, -#endif - -#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \ - LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent) - -#if (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \ - !defined(SQLITE_OMIT_WAL)) - { "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 }, -#else - { "CreateFileMappingA", (SYSCALL)0, 0 }, -#endif - -#define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ - DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent) - -#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ - !defined(SQLITE_OMIT_WAL)) - { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 }, -#else - { "CreateFileMappingW", (SYSCALL)0, 0 }, -#endif - -#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ - DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent) - -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "CreateMutexW", (SYSCALL)CreateMutexW, 0 }, -#else - { "CreateMutexW", (SYSCALL)0, 0 }, -#endif - -#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \ - LPCWSTR))aSyscall[8].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) - { "DeleteFileA", (SYSCALL)DeleteFileA, 0 }, -#else - { "DeleteFileA", (SYSCALL)0, 0 }, -#endif - -#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[9].pCurrent) - -#if defined(SQLITE_WIN32_HAS_WIDE) - { "DeleteFileW", (SYSCALL)DeleteFileW, 0 }, -#else - { "DeleteFileW", (SYSCALL)0, 0 }, -#endif - -#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[10].pCurrent) - -#if SQLITE_OS_WINCE - { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 }, -#else - { "FileTimeToLocalFileTime", (SYSCALL)0, 0 }, -#endif - -#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \ - LPFILETIME))aSyscall[11].pCurrent) - -#if SQLITE_OS_WINCE - { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 }, -#else - { "FileTimeToSystemTime", (SYSCALL)0, 0 }, -#endif - -#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \ - LPSYSTEMTIME))aSyscall[12].pCurrent) - - { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 }, - -#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) - { "FormatMessageA", (SYSCALL)FormatMessageA, 0 }, -#else - { "FormatMessageA", (SYSCALL)0, 0 }, -#endif - -#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \ - DWORD,va_list*))aSyscall[14].pCurrent) - -#if defined(SQLITE_WIN32_HAS_WIDE) - { "FormatMessageW", (SYSCALL)FormatMessageW, 0 }, -#else - { "FormatMessageW", (SYSCALL)0, 0 }, -#endif - -#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \ - DWORD,va_list*))aSyscall[15].pCurrent) - -#if !defined(SQLITE_OMIT_LOAD_EXTENSION) - { "FreeLibrary", (SYSCALL)FreeLibrary, 0 }, -#else - { "FreeLibrary", (SYSCALL)0, 0 }, -#endif - -#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent) - - { "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 }, - -#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[17].pCurrent) - -#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) - { "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 }, -#else - { "GetDiskFreeSpaceA", (SYSCALL)0, 0 }, -#endif - -#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \ - LPDWORD))aSyscall[18].pCurrent) - -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 }, -#else - { "GetDiskFreeSpaceW", (SYSCALL)0, 0 }, -#endif - -#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \ - LPDWORD))aSyscall[19].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) - { "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 }, -#else - { "GetFileAttributesA", (SYSCALL)0, 0 }, -#endif - -#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent) - -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 }, -#else - { "GetFileAttributesW", (SYSCALL)0, 0 }, -#endif - -#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[21].pCurrent) - -#if defined(SQLITE_WIN32_HAS_WIDE) - { "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 }, -#else - { "GetFileAttributesExW", (SYSCALL)0, 0 }, -#endif - -#define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \ - LPVOID))aSyscall[22].pCurrent) - -#if !SQLITE_OS_WINRT - { "GetFileSize", (SYSCALL)GetFileSize, 0 }, -#else - { "GetFileSize", (SYSCALL)0, 0 }, -#endif - -#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent) - -#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) - { "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 }, -#else - { "GetFullPathNameA", (SYSCALL)0, 0 }, -#endif - -#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \ - LPSTR*))aSyscall[24].pCurrent) - -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 }, -#else - { "GetFullPathNameW", (SYSCALL)0, 0 }, -#endif - -#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ - LPWSTR*))aSyscall[25].pCurrent) - - { "GetLastError", (SYSCALL)GetLastError, 0 }, - -#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) - -#if !defined(SQLITE_OMIT_LOAD_EXTENSION) -#if SQLITE_OS_WINCE - /* The GetProcAddressA() routine is only available on Windows CE. */ - { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 }, -#else - /* All other Windows platforms expect GetProcAddress() to take - ** an ANSI string regardless of the _UNICODE setting */ - { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 }, -#endif -#else - { "GetProcAddressA", (SYSCALL)0, 0 }, -#endif - -#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \ - LPCSTR))aSyscall[27].pCurrent) - -#if !SQLITE_OS_WINRT - { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 }, -#else - { "GetSystemInfo", (SYSCALL)0, 0 }, -#endif - -#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent) - - { "GetSystemTime", (SYSCALL)GetSystemTime, 0 }, - -#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent) - -#if !SQLITE_OS_WINCE - { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 }, -#else - { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 }, -#endif - -#define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \ - LPFILETIME))aSyscall[30].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) - { "GetTempPathA", (SYSCALL)GetTempPathA, 0 }, -#else - { "GetTempPathA", (SYSCALL)0, 0 }, -#endif - -#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent) - -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "GetTempPathW", (SYSCALL)GetTempPathW, 0 }, -#else - { "GetTempPathW", (SYSCALL)0, 0 }, -#endif - -#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent) - -#if !SQLITE_OS_WINRT - { "GetTickCount", (SYSCALL)GetTickCount, 0 }, -#else - { "GetTickCount", (SYSCALL)0, 0 }, -#endif - -#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) - { "GetVersionExA", (SYSCALL)GetVersionExA, 0 }, -#else - { "GetVersionExA", (SYSCALL)0, 0 }, -#endif - -#define osGetVersionExA ((BOOL(WINAPI*)( \ - LPOSVERSIONINFOA))aSyscall[34].pCurrent) - - { "HeapAlloc", (SYSCALL)HeapAlloc, 0 }, - -#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \ - SIZE_T))aSyscall[35].pCurrent) - -#if !SQLITE_OS_WINRT - { "HeapCreate", (SYSCALL)HeapCreate, 0 }, -#else - { "HeapCreate", (SYSCALL)0, 0 }, -#endif - -#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \ - SIZE_T))aSyscall[36].pCurrent) - -#if !SQLITE_OS_WINRT - { "HeapDestroy", (SYSCALL)HeapDestroy, 0 }, -#else - { "HeapDestroy", (SYSCALL)0, 0 }, -#endif - -#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[37].pCurrent) - - { "HeapFree", (SYSCALL)HeapFree, 0 }, - -#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[38].pCurrent) - - { "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 }, - -#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \ - SIZE_T))aSyscall[39].pCurrent) - - { "HeapSize", (SYSCALL)HeapSize, 0 }, - -#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \ - LPCVOID))aSyscall[40].pCurrent) - -#if !SQLITE_OS_WINRT - { "HeapValidate", (SYSCALL)HeapValidate, 0 }, -#else - { "HeapValidate", (SYSCALL)0, 0 }, -#endif - -#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \ - LPCVOID))aSyscall[41].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION) - { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 }, -#else - { "LoadLibraryA", (SYSCALL)0, 0 }, -#endif - -#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[42].pCurrent) - -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ - !defined(SQLITE_OMIT_LOAD_EXTENSION) - { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 }, -#else - { "LoadLibraryW", (SYSCALL)0, 0 }, -#endif - -#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[43].pCurrent) - -#if !SQLITE_OS_WINRT - { "LocalFree", (SYSCALL)LocalFree, 0 }, -#else - { "LocalFree", (SYSCALL)0, 0 }, -#endif - -#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[44].pCurrent) - -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT - { "LockFile", (SYSCALL)LockFile, 0 }, -#else - { "LockFile", (SYSCALL)0, 0 }, -#endif - -#ifndef osLockFile -#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - DWORD))aSyscall[45].pCurrent) -#endif - -#if !SQLITE_OS_WINCE - { "LockFileEx", (SYSCALL)LockFileEx, 0 }, -#else - { "LockFileEx", (SYSCALL)0, 0 }, -#endif - -#ifndef osLockFileEx -#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \ - LPOVERLAPPED))aSyscall[46].pCurrent) -#endif - -#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)) - { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 }, -#else - { "MapViewOfFile", (SYSCALL)0, 0 }, -#endif - -#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - SIZE_T))aSyscall[47].pCurrent) - - { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 }, - -#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \ - int))aSyscall[48].pCurrent) - - { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 }, - -#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \ - LARGE_INTEGER*))aSyscall[49].pCurrent) - - { "ReadFile", (SYSCALL)ReadFile, 0 }, - -#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \ - LPOVERLAPPED))aSyscall[50].pCurrent) - - { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 }, - -#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[51].pCurrent) - -#if !SQLITE_OS_WINRT - { "SetFilePointer", (SYSCALL)SetFilePointer, 0 }, -#else - { "SetFilePointer", (SYSCALL)0, 0 }, -#endif - -#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \ - DWORD))aSyscall[52].pCurrent) - -#if !SQLITE_OS_WINRT - { "Sleep", (SYSCALL)Sleep, 0 }, -#else - { "Sleep", (SYSCALL)0, 0 }, -#endif - -#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[53].pCurrent) - - { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 }, - -#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \ - LPFILETIME))aSyscall[54].pCurrent) - -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT - { "UnlockFile", (SYSCALL)UnlockFile, 0 }, -#else - { "UnlockFile", (SYSCALL)0, 0 }, -#endif - -#ifndef osUnlockFile -#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - DWORD))aSyscall[55].pCurrent) -#endif - -#if !SQLITE_OS_WINCE - { "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 }, -#else - { "UnlockFileEx", (SYSCALL)0, 0 }, -#endif - -#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - LPOVERLAPPED))aSyscall[56].pCurrent) - -#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) - { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 }, -#else - { "UnmapViewOfFile", (SYSCALL)0, 0 }, -#endif - -#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[57].pCurrent) - - { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 }, - -#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \ - LPCSTR,LPBOOL))aSyscall[58].pCurrent) - - { "WriteFile", (SYSCALL)WriteFile, 0 }, - -#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ - LPOVERLAPPED))aSyscall[59].pCurrent) - -#if SQLITE_OS_WINRT - { "CreateEventExW", (SYSCALL)CreateEventExW, 0 }, -#else - { "CreateEventExW", (SYSCALL)0, 0 }, -#endif - -#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ - DWORD,DWORD))aSyscall[60].pCurrent) - -#if !SQLITE_OS_WINRT - { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, -#else - { "WaitForSingleObject", (SYSCALL)0, 0 }, -#endif - -#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ - DWORD))aSyscall[61].pCurrent) - -#if SQLITE_OS_WINRT - { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, -#else - { "WaitForSingleObjectEx", (SYSCALL)0, 0 }, -#endif - -#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ - BOOL))aSyscall[62].pCurrent) - -#if SQLITE_OS_WINRT - { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 }, -#else - { "SetFilePointerEx", (SYSCALL)0, 0 }, -#endif - -#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \ - PLARGE_INTEGER,DWORD))aSyscall[63].pCurrent) - -#if SQLITE_OS_WINRT - { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 }, -#else - { "GetFileInformationByHandleEx", (SYSCALL)0, 0 }, -#endif - -#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \ - FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[64].pCurrent) - -#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL) - { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 }, -#else - { "MapViewOfFileFromApp", (SYSCALL)0, 0 }, -#endif - -#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \ - SIZE_T))aSyscall[65].pCurrent) - -#if SQLITE_OS_WINRT - { "CreateFile2", (SYSCALL)CreateFile2, 0 }, -#else - { "CreateFile2", (SYSCALL)0, 0 }, -#endif - -#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \ - LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[66].pCurrent) - -#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION) - { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 }, -#else - { "LoadPackagedLibrary", (SYSCALL)0, 0 }, -#endif - -#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \ - DWORD))aSyscall[67].pCurrent) - -#if SQLITE_OS_WINRT - { "GetTickCount64", (SYSCALL)GetTickCount64, 0 }, -#else - { "GetTickCount64", (SYSCALL)0, 0 }, -#endif - -#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[68].pCurrent) - -#if SQLITE_OS_WINRT - { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, -#else - { "GetNativeSystemInfo", (SYSCALL)0, 0 }, -#endif - -#define osGetNativeSystemInfo ((VOID(WINAPI*)( \ - LPSYSTEM_INFO))aSyscall[69].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) - { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, -#else - { "OutputDebugStringA", (SYSCALL)0, 0 }, -#endif - -#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[70].pCurrent) - -#if defined(SQLITE_WIN32_HAS_WIDE) - { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, -#else - { "OutputDebugStringW", (SYSCALL)0, 0 }, -#endif - -#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[71].pCurrent) - - { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, - -#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[72].pCurrent) - -#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL) - { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 }, -#else - { "CreateFileMappingFromApp", (SYSCALL)0, 0 }, -#endif - -#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \ - LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[73].pCurrent) - -}; /* End of the overrideable system calls */ - -/* -** This is the xSetSystemCall() method of sqlite3_vfs for all of the -** "win32" VFSes. Return SQLITE_OK opon successfully updating the -** system call pointer, or SQLITE_NOTFOUND if there is no configurable -** system call named zName. -*/ -static int winSetSystemCall( - sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ - const char *zName, /* Name of system call to override */ - sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ -){ - unsigned int i; - int rc = SQLITE_NOTFOUND; - - UNUSED_PARAMETER(pNotUsed); - if( zName==0 ){ - /* If no zName is given, restore all system calls to their default - ** settings and return NULL - */ - rc = SQLITE_OK; - for(i=0; i0 ){ - memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); - memcpy(zDbgBuf, zBuf, nMin); - osOutputDebugStringA(zDbgBuf); - }else{ - osOutputDebugStringA(zBuf); - } -#elif defined(SQLITE_WIN32_HAS_WIDE) - memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); - if ( osMultiByteToWideChar( - osAreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, zBuf, - nMin, (LPWSTR)zDbgBuf, SQLITE_WIN32_DBG_BUF_SIZE/sizeof(WCHAR))<=0 ){ - return; - } - osOutputDebugStringW((LPCWSTR)zDbgBuf); -#else - if( nMin>0 ){ - memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); - memcpy(zDbgBuf, zBuf, nMin); - fprintf(stderr, "%s", zDbgBuf); - }else{ - fprintf(stderr, "%s", zBuf); - } -#endif -} - -/* -** The following routine suspends the current thread for at least ms -** milliseconds. This is equivalent to the Win32 Sleep() interface. -*/ -#if SQLITE_OS_WINRT -static HANDLE sleepObj = NULL; -#endif - -SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){ -#if SQLITE_OS_WINRT - if ( sleepObj==NULL ){ - sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET, - SYNCHRONIZE); - } - assert( sleepObj!=NULL ); - osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE); -#else - osSleep(milliseconds); -#endif -} - -/* -** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, -** or WinCE. Return false (zero) for Win95, Win98, or WinME. -** -** Here is an interesting observation: Win95, Win98, and WinME lack -** the LockFileEx() API. But we can still statically link against that -** API as long as we don't call it when running Win95/98/ME. A call to -** this routine is used to determine if the host is Win95/98/ME or -** WinNT/2K/XP so that we will know whether or not we can safely call -** the LockFileEx() API. -*/ -#if SQLITE_OS_WINCE || SQLITE_OS_WINRT -# define isNT() (1) -#elif !defined(SQLITE_WIN32_HAS_WIDE) -# define isNT() (0) -#else - static int isNT(void){ - if( sqlite3_os_type==0 ){ - OSVERSIONINFOA sInfo; - sInfo.dwOSVersionInfoSize = sizeof(sInfo); - osGetVersionExA(&sInfo); - sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; - } - return sqlite3_os_type==2; - } -#endif - -#ifdef SQLITE_WIN32_MALLOC -/* -** Allocate nBytes of memory. -*/ -static void *winMemMalloc(int nBytes){ - HANDLE hHeap; - void *p; - - winMemAssertMagic(); - hHeap = winMemGetHeap(); - assert( hHeap!=0 ); - assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); -#endif - assert( nBytes>=0 ); - p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); - if( !p ){ - sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%d), heap=%p", - nBytes, osGetLastError(), (void*)hHeap); - } - return p; -} - -/* -** Free memory. -*/ -static void winMemFree(void *pPrior){ - HANDLE hHeap; - - winMemAssertMagic(); - hHeap = winMemGetHeap(); - assert( hHeap!=0 ); - assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); -#endif - if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */ - if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){ - sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%d), heap=%p", - pPrior, osGetLastError(), (void*)hHeap); - } -} - -/* -** Change the size of an existing memory allocation -*/ -static void *winMemRealloc(void *pPrior, int nBytes){ - HANDLE hHeap; - void *p; - - winMemAssertMagic(); - hHeap = winMemGetHeap(); - assert( hHeap!=0 ); - assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); -#endif - assert( nBytes>=0 ); - if( !pPrior ){ - p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); - }else{ - p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes); - } - if( !p ){ - sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%d), heap=%p", - pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(), - (void*)hHeap); - } - return p; -} - -/* -** Return the size of an outstanding allocation, in bytes. -*/ -static int winMemSize(void *p){ - HANDLE hHeap; - SIZE_T n; - - winMemAssertMagic(); - hHeap = winMemGetHeap(); - assert( hHeap!=0 ); - assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); -#endif - if( !p ) return 0; - n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p); - if( n==(SIZE_T)-1 ){ - sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%d), heap=%p", - p, osGetLastError(), (void*)hHeap); - return 0; - } - return (int)n; -} - -/* -** Round up a request size to the next valid allocation size. -*/ -static int winMemRoundup(int n){ - return n; -} - -/* -** Initialize this module. -*/ -static int winMemInit(void *pAppData){ - winMemData *pWinMemData = (winMemData *)pAppData; - - if( !pWinMemData ) return SQLITE_ERROR; - assert( pWinMemData->magic==WINMEM_MAGIC ); - -#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE - if( !pWinMemData->hHeap ){ - pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS, - SQLITE_WIN32_HEAP_INIT_SIZE, - SQLITE_WIN32_HEAP_MAX_SIZE); - if( !pWinMemData->hHeap ){ - sqlite3_log(SQLITE_NOMEM, - "failed to HeapCreate (%d), flags=%u, initSize=%u, maxSize=%u", - osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, - SQLITE_WIN32_HEAP_INIT_SIZE, SQLITE_WIN32_HEAP_MAX_SIZE); - return SQLITE_NOMEM; - } - pWinMemData->bOwned = TRUE; - assert( pWinMemData->bOwned ); - } -#else - pWinMemData->hHeap = osGetProcessHeap(); - if( !pWinMemData->hHeap ){ - sqlite3_log(SQLITE_NOMEM, - "failed to GetProcessHeap (%d)", osGetLastError()); - return SQLITE_NOMEM; - } - pWinMemData->bOwned = FALSE; - assert( !pWinMemData->bOwned ); -#endif - assert( pWinMemData->hHeap!=0 ); - assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); -#endif - return SQLITE_OK; -} - -/* -** Deinitialize this module. -*/ -static void winMemShutdown(void *pAppData){ - winMemData *pWinMemData = (winMemData *)pAppData; - - if( !pWinMemData ) return; - if( pWinMemData->hHeap ){ - assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); -#endif - if( pWinMemData->bOwned ){ - if( !osHeapDestroy(pWinMemData->hHeap) ){ - sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%d), heap=%p", - osGetLastError(), (void*)pWinMemData->hHeap); - } - pWinMemData->bOwned = FALSE; - } - pWinMemData->hHeap = NULL; - } -} - -/* -** Populate the low-level memory allocation function pointers in -** sqlite3GlobalConfig.m with pointers to the routines in this file. The -** arguments specify the block of memory to manage. -** -** This routine is only called by sqlite3_config(), and therefore -** is not required to be threadsafe (it is not). -*/ -SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void){ - static const sqlite3_mem_methods winMemMethods = { - winMemMalloc, - winMemFree, - winMemRealloc, - winMemSize, - winMemRoundup, - winMemInit, - winMemShutdown, - &win_mem_data - }; - return &winMemMethods; -} - -SQLITE_PRIVATE void sqlite3MemSetDefault(void){ - sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32()); -} -#endif /* SQLITE_WIN32_MALLOC */ - -/* -** Convert a UTF-8 string to Microsoft Unicode (UTF-16?). -** -** Space to hold the returned string is obtained from malloc. -*/ -static LPWSTR utf8ToUnicode(const char *zFilename){ - int nChar; - LPWSTR zWideFilename; - - nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); - if( nChar==0 ){ - return 0; - } - zWideFilename = sqlite3MallocZero( nChar*sizeof(zWideFilename[0]) ); - if( zWideFilename==0 ){ - return 0; - } - nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, - nChar); - if( nChar==0 ){ - sqlite3_free(zWideFilename); - zWideFilename = 0; - } - return zWideFilename; -} - -/* -** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is -** obtained from sqlite3_malloc(). -*/ -static char *unicodeToUtf8(LPCWSTR zWideFilename){ - int nByte; - char *zFilename; - - nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); - if( nByte == 0 ){ - return 0; - } - zFilename = sqlite3MallocZero( nByte ); - if( zFilename==0 ){ - return 0; - } - nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, - 0, 0); - if( nByte == 0 ){ - sqlite3_free(zFilename); - zFilename = 0; - } - return zFilename; -} - -/* -** Convert an ANSI string to Microsoft Unicode, based on the -** current codepage settings for file apis. -** -** Space to hold the returned string is obtained -** from sqlite3_malloc. -*/ -static LPWSTR mbcsToUnicode(const char *zFilename){ - int nByte; - LPWSTR zMbcsFilename; - int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; - - nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL, - 0)*sizeof(WCHAR); - if( nByte==0 ){ - return 0; - } - zMbcsFilename = sqlite3MallocZero( nByte*sizeof(zMbcsFilename[0]) ); - if( zMbcsFilename==0 ){ - return 0; - } - nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, - nByte); - if( nByte==0 ){ - sqlite3_free(zMbcsFilename); - zMbcsFilename = 0; - } - return zMbcsFilename; -} - -/* -** Convert Microsoft Unicode to multi-byte character string, based on the -** user's ANSI codepage. -** -** Space to hold the returned string is obtained from -** sqlite3_malloc(). -*/ -static char *unicodeToMbcs(LPCWSTR zWideFilename){ - int nByte; - char *zFilename; - int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; - - nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0); - if( nByte == 0 ){ - return 0; - } - zFilename = sqlite3MallocZero( nByte ); - if( zFilename==0 ){ - return 0; - } - nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, - nByte, 0, 0); - if( nByte == 0 ){ - sqlite3_free(zFilename); - zFilename = 0; - } - return zFilename; -} - -/* -** Convert multibyte character string to UTF-8. Space to hold the -** returned string is obtained from sqlite3_malloc(). -*/ -SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ - char *zFilenameUtf8; - LPWSTR zTmpWide; - - zTmpWide = mbcsToUnicode(zFilename); - if( zTmpWide==0 ){ - return 0; - } - zFilenameUtf8 = unicodeToUtf8(zTmpWide); - sqlite3_free(zTmpWide); - return zFilenameUtf8; -} - -/* -** Convert UTF-8 to multibyte character string. Space to hold the -** returned string is obtained from sqlite3_malloc(). -*/ -SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ - char *zFilenameMbcs; - LPWSTR zTmpWide; - - zTmpWide = utf8ToUnicode(zFilename); - if( zTmpWide==0 ){ - return 0; - } - zFilenameMbcs = unicodeToMbcs(zTmpWide); - sqlite3_free(zTmpWide); - return zFilenameMbcs; -} - -/* -** This function sets the data directory or the temporary directory based on -** the provided arguments. The type argument must be 1 in order to set the -** data directory or 2 in order to set the temporary directory. The zValue -** argument is the name of the directory to use. The return value will be -** SQLITE_OK if successful. -*/ -SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){ - char **ppDirectory = 0; -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlite3_initialize(); - if( rc ) return rc; -#endif - if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){ - ppDirectory = &sqlite3_data_directory; - }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){ - ppDirectory = &sqlite3_temp_directory; - } - assert( !ppDirectory || type==SQLITE_WIN32_DATA_DIRECTORY_TYPE - || type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE - ); - assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) ); - if( ppDirectory ){ - char *zValueUtf8 = 0; - if( zValue && zValue[0] ){ - zValueUtf8 = unicodeToUtf8(zValue); - if ( zValueUtf8==0 ){ - return SQLITE_NOMEM; - } - } - sqlite3_free(*ppDirectory); - *ppDirectory = zValueUtf8; - return SQLITE_OK; - } - return SQLITE_ERROR; -} - -/* -** The return value of getLastErrorMsg -** is zero if the error message fits in the buffer, or non-zero -** otherwise (if the message was truncated). -*/ -static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ - /* FormatMessage returns 0 on failure. Otherwise it - ** returns the number of TCHARs written to the output - ** buffer, excluding the terminating null char. - */ - DWORD dwLen = 0; - char *zOut = 0; - - if( isNT() ){ -#if SQLITE_OS_WINRT - WCHAR zTempWide[MAX_PATH+1]; /* NOTE: Somewhat arbitrary. */ - dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - lastErrno, - 0, - zTempWide, - MAX_PATH, - 0); -#else - LPWSTR zTempWide = NULL; - dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - lastErrno, - 0, - (LPWSTR) &zTempWide, - 0, - 0); -#endif - if( dwLen > 0 ){ - /* allocate a buffer and convert to UTF8 */ - sqlite3BeginBenignMalloc(); - zOut = unicodeToUtf8(zTempWide); - sqlite3EndBenignMalloc(); -#if !SQLITE_OS_WINRT - /* free the system buffer allocated by FormatMessage */ - osLocalFree(zTempWide); -#endif - } - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - char *zTemp = NULL; - dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - lastErrno, - 0, - (LPSTR) &zTemp, - 0, - 0); - if( dwLen > 0 ){ - /* allocate a buffer and convert to UTF8 */ - sqlite3BeginBenignMalloc(); - zOut = sqlite3_win32_mbcs_to_utf8(zTemp); - sqlite3EndBenignMalloc(); - /* free the system buffer allocated by FormatMessage */ - osLocalFree(zTemp); - } - } -#endif - if( 0 == dwLen ){ - sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", lastErrno, lastErrno); - }else{ - /* copy a maximum of nBuf chars to output buffer */ - sqlite3_snprintf(nBuf, zBuf, "%s", zOut); - /* free the UTF8 buffer */ - sqlite3_free(zOut); - } - return 0; -} - -/* -** -** This function - winLogErrorAtLine() - is only ever called via the macro -** winLogError(). -** -** This routine is invoked after an error occurs in an OS function. -** It logs a message using sqlite3_log() containing the current value of -** error code and, if possible, the human-readable equivalent from -** FormatMessage. -** -** The first argument passed to the macro should be the error code that -** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). -** The two subsequent arguments should be the name of the OS function that -** failed and the associated file-system path, if any. -*/ -#define winLogError(a,b,c,d) winLogErrorAtLine(a,b,c,d,__LINE__) -static int winLogErrorAtLine( - int errcode, /* SQLite error code */ - DWORD lastErrno, /* Win32 last error */ - const char *zFunc, /* Name of OS function that failed */ - const char *zPath, /* File path associated with error */ - int iLine /* Source line number where error occurred */ -){ - char zMsg[500]; /* Human readable error text */ - int i; /* Loop counter */ - - zMsg[0] = 0; - getLastErrorMsg(lastErrno, sizeof(zMsg), zMsg); - assert( errcode!=SQLITE_OK ); - if( zPath==0 ) zPath = ""; - for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} - zMsg[i] = 0; - sqlite3_log(errcode, - "os_win.c:%d: (%d) %s(%s) - %s", - iLine, lastErrno, zFunc, zPath, zMsg - ); - - return errcode; -} - -/* -** The number of times that a ReadFile(), WriteFile(), and DeleteFile() -** will be retried following a locking error - probably caused by -** antivirus software. Also the initial delay before the first retry. -** The delay increases linearly with each retry. -*/ -#ifndef SQLITE_WIN32_IOERR_RETRY -# define SQLITE_WIN32_IOERR_RETRY 10 -#endif -#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY -# define SQLITE_WIN32_IOERR_RETRY_DELAY 25 -#endif -static int win32IoerrRetry = SQLITE_WIN32_IOERR_RETRY; -static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY; - -/* -** If a ReadFile() or WriteFile() error occurs, invoke this routine -** to see if it should be retried. Return TRUE to retry. Return FALSE -** to give up with an error. -*/ -static int retryIoerr(int *pnRetry, DWORD *pError){ - DWORD e = osGetLastError(); - if( *pnRetry>=win32IoerrRetry ){ - if( pError ){ - *pError = e; - } - return 0; - } - if( e==ERROR_ACCESS_DENIED || - e==ERROR_LOCK_VIOLATION || - e==ERROR_SHARING_VIOLATION ){ - sqlite3_win32_sleep(win32IoerrRetryDelay*(1+*pnRetry)); - ++*pnRetry; - return 1; - } - if( pError ){ - *pError = e; - } - return 0; -} - -/* -** Log a I/O error retry episode. -*/ -static void logIoerr(int nRetry){ - if( nRetry ){ - sqlite3_log(SQLITE_IOERR, - "delayed %dms for lock/sharing conflict", - win32IoerrRetryDelay*nRetry*(nRetry+1)/2 - ); - } -} - -#if SQLITE_OS_WINCE -/************************************************************************* -** This section contains code for WinCE only. -*/ -#if !defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API -/* -** The MSVC CRT on Windows CE may not have a localtime() function. So -** create a substitute. -*/ -/* #include */ -struct tm *__cdecl localtime(const time_t *t) -{ - static struct tm y; - FILETIME uTm, lTm; - SYSTEMTIME pTm; - sqlite3_int64 t64; - t64 = *t; - t64 = (t64 + 11644473600)*10000000; - uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); - uTm.dwHighDateTime= (DWORD)(t64 >> 32); - osFileTimeToLocalFileTime(&uTm,&lTm); - osFileTimeToSystemTime(&lTm,&pTm); - y.tm_year = pTm.wYear - 1900; - y.tm_mon = pTm.wMonth - 1; - y.tm_wday = pTm.wDayOfWeek; - y.tm_mday = pTm.wDay; - y.tm_hour = pTm.wHour; - y.tm_min = pTm.wMinute; - y.tm_sec = pTm.wSecond; - return &y; -} -#endif - -#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] - -/* -** Acquire a lock on the handle h -*/ -static void winceMutexAcquire(HANDLE h){ - DWORD dwErr; - do { - dwErr = osWaitForSingleObject(h, INFINITE); - } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED); -} -/* -** Release a lock acquired by winceMutexAcquire() -*/ -#define winceMutexRelease(h) ReleaseMutex(h) - -/* -** Create the mutex and shared memory used for locking in the file -** descriptor pFile -*/ -static int winceCreateLock(const char *zFilename, winFile *pFile){ - LPWSTR zTok; - LPWSTR zName; - DWORD lastErrno; - BOOL bLogged = FALSE; - BOOL bInit = TRUE; - - zName = utf8ToUnicode(zFilename); - if( zName==0 ){ - /* out of memory */ - return SQLITE_IOERR_NOMEM; - } - - /* Initialize the local lockdata */ - memset(&pFile->local, 0, sizeof(pFile->local)); - - /* Replace the backslashes from the filename and lowercase it - ** to derive a mutex name. */ - zTok = osCharLowerW(zName); - for (;*zTok;zTok++){ - if (*zTok == '\\') *zTok = '_'; - } - - /* Create/open the named mutex */ - pFile->hMutex = osCreateMutexW(NULL, FALSE, zName); - if (!pFile->hMutex){ - pFile->lastErrno = osGetLastError(); - winLogError(SQLITE_IOERR, pFile->lastErrno, - "winceCreateLock1", zFilename); - sqlite3_free(zName); - return SQLITE_IOERR; - } - - /* Acquire the mutex before continuing */ - winceMutexAcquire(pFile->hMutex); - - /* Since the names of named mutexes, semaphores, file mappings etc are - ** case-sensitive, take advantage of that by uppercasing the mutex name - ** and using that as the shared filemapping name. - */ - osCharUpperW(zName); - pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL, - PAGE_READWRITE, 0, sizeof(winceLock), - zName); - - /* Set a flag that indicates we're the first to create the memory so it - ** must be zero-initialized */ - lastErrno = osGetLastError(); - if (lastErrno == ERROR_ALREADY_EXISTS){ - bInit = FALSE; - } - - sqlite3_free(zName); - - /* If we succeeded in making the shared memory handle, map it. */ - if( pFile->hShared ){ - pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared, - FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); - /* If mapping failed, close the shared memory handle and erase it */ - if( !pFile->shared ){ - pFile->lastErrno = osGetLastError(); - winLogError(SQLITE_IOERR, pFile->lastErrno, - "winceCreateLock2", zFilename); - bLogged = TRUE; - osCloseHandle(pFile->hShared); - pFile->hShared = NULL; - } - } - - /* If shared memory could not be created, then close the mutex and fail */ - if( pFile->hShared==NULL ){ - if( !bLogged ){ - pFile->lastErrno = lastErrno; - winLogError(SQLITE_IOERR, pFile->lastErrno, - "winceCreateLock3", zFilename); - bLogged = TRUE; - } - winceMutexRelease(pFile->hMutex); - osCloseHandle(pFile->hMutex); - pFile->hMutex = NULL; - return SQLITE_IOERR; - } - - /* Initialize the shared memory if we're supposed to */ - if( bInit ){ - memset(pFile->shared, 0, sizeof(winceLock)); - } - - winceMutexRelease(pFile->hMutex); - return SQLITE_OK; -} - -/* -** Destroy the part of winFile that deals with wince locks -*/ -static void winceDestroyLock(winFile *pFile){ - if (pFile->hMutex){ - /* Acquire the mutex */ - winceMutexAcquire(pFile->hMutex); - - /* The following blocks should probably assert in debug mode, but they - are to cleanup in case any locks remained open */ - if (pFile->local.nReaders){ - pFile->shared->nReaders --; - } - if (pFile->local.bReserved){ - pFile->shared->bReserved = FALSE; - } - if (pFile->local.bPending){ - pFile->shared->bPending = FALSE; - } - if (pFile->local.bExclusive){ - pFile->shared->bExclusive = FALSE; - } - - /* De-reference and close our copy of the shared memory handle */ - osUnmapViewOfFile(pFile->shared); - osCloseHandle(pFile->hShared); - - /* Done with the mutex */ - winceMutexRelease(pFile->hMutex); - osCloseHandle(pFile->hMutex); - pFile->hMutex = NULL; - } -} - -/* -** An implementation of the LockFile() API of Windows for CE -*/ -static BOOL winceLockFile( - LPHANDLE phFile, - DWORD dwFileOffsetLow, - DWORD dwFileOffsetHigh, - DWORD nNumberOfBytesToLockLow, - DWORD nNumberOfBytesToLockHigh -){ - winFile *pFile = HANDLE_TO_WINFILE(phFile); - BOOL bReturn = FALSE; - - UNUSED_PARAMETER(dwFileOffsetHigh); - UNUSED_PARAMETER(nNumberOfBytesToLockHigh); - - if (!pFile->hMutex) return TRUE; - winceMutexAcquire(pFile->hMutex); - - /* Wanting an exclusive lock? */ - if (dwFileOffsetLow == (DWORD)SHARED_FIRST - && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ - if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ - pFile->shared->bExclusive = TRUE; - pFile->local.bExclusive = TRUE; - bReturn = TRUE; - } - } - - /* Want a read-only lock? */ - else if (dwFileOffsetLow == (DWORD)SHARED_FIRST && - nNumberOfBytesToLockLow == 1){ - if (pFile->shared->bExclusive == 0){ - pFile->local.nReaders ++; - if (pFile->local.nReaders == 1){ - pFile->shared->nReaders ++; - } - bReturn = TRUE; - } - } - - /* Want a pending lock? */ - else if (dwFileOffsetLow == (DWORD)PENDING_BYTE - && nNumberOfBytesToLockLow == 1){ - /* If no pending lock has been acquired, then acquire it */ - if (pFile->shared->bPending == 0) { - pFile->shared->bPending = TRUE; - pFile->local.bPending = TRUE; - bReturn = TRUE; - } - } - - /* Want a reserved lock? */ - else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE - && nNumberOfBytesToLockLow == 1){ - if (pFile->shared->bReserved == 0) { - pFile->shared->bReserved = TRUE; - pFile->local.bReserved = TRUE; - bReturn = TRUE; - } - } - - winceMutexRelease(pFile->hMutex); - return bReturn; -} - -/* -** An implementation of the UnlockFile API of Windows for CE -*/ -static BOOL winceUnlockFile( - LPHANDLE phFile, - DWORD dwFileOffsetLow, - DWORD dwFileOffsetHigh, - DWORD nNumberOfBytesToUnlockLow, - DWORD nNumberOfBytesToUnlockHigh -){ - winFile *pFile = HANDLE_TO_WINFILE(phFile); - BOOL bReturn = FALSE; - - UNUSED_PARAMETER(dwFileOffsetHigh); - UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh); - - if (!pFile->hMutex) return TRUE; - winceMutexAcquire(pFile->hMutex); - - /* Releasing a reader lock or an exclusive lock */ - if (dwFileOffsetLow == (DWORD)SHARED_FIRST){ - /* Did we have an exclusive lock? */ - if (pFile->local.bExclusive){ - assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE); - pFile->local.bExclusive = FALSE; - pFile->shared->bExclusive = FALSE; - bReturn = TRUE; - } - - /* Did we just have a reader lock? */ - else if (pFile->local.nReaders){ - assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE - || nNumberOfBytesToUnlockLow == 1); - pFile->local.nReaders --; - if (pFile->local.nReaders == 0) - { - pFile->shared->nReaders --; - } - bReturn = TRUE; - } - } - - /* Releasing a pending lock */ - else if (dwFileOffsetLow == (DWORD)PENDING_BYTE - && nNumberOfBytesToUnlockLow == 1){ - if (pFile->local.bPending){ - pFile->local.bPending = FALSE; - pFile->shared->bPending = FALSE; - bReturn = TRUE; - } - } - /* Releasing a reserved lock */ - else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE - && nNumberOfBytesToUnlockLow == 1){ - if (pFile->local.bReserved) { - pFile->local.bReserved = FALSE; - pFile->shared->bReserved = FALSE; - bReturn = TRUE; - } - } - - winceMutexRelease(pFile->hMutex); - return bReturn; -} -/* -** End of the special code for wince -*****************************************************************************/ -#endif /* SQLITE_OS_WINCE */ - -/* -** Lock a file region. -*/ -static BOOL winLockFile( - LPHANDLE phFile, - DWORD flags, - DWORD offsetLow, - DWORD offsetHigh, - DWORD numBytesLow, - DWORD numBytesHigh -){ -#if SQLITE_OS_WINCE - /* - ** NOTE: Windows CE is handled differently here due its lack of the Win32 - ** API LockFile. - */ - return winceLockFile(phFile, offsetLow, offsetHigh, - numBytesLow, numBytesHigh); -#else - if( isNT() ){ - OVERLAPPED ovlp; - memset(&ovlp, 0, sizeof(OVERLAPPED)); - ovlp.Offset = offsetLow; - ovlp.OffsetHigh = offsetHigh; - return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp); - }else{ - return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, - numBytesHigh); - } -#endif -} - -/* -** Unlock a file region. - */ -static BOOL winUnlockFile( - LPHANDLE phFile, - DWORD offsetLow, - DWORD offsetHigh, - DWORD numBytesLow, - DWORD numBytesHigh -){ -#if SQLITE_OS_WINCE - /* - ** NOTE: Windows CE is handled differently here due its lack of the Win32 - ** API UnlockFile. - */ - return winceUnlockFile(phFile, offsetLow, offsetHigh, - numBytesLow, numBytesHigh); -#else - if( isNT() ){ - OVERLAPPED ovlp; - memset(&ovlp, 0, sizeof(OVERLAPPED)); - ovlp.Offset = offsetLow; - ovlp.OffsetHigh = offsetHigh; - return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp); - }else{ - return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, - numBytesHigh); - } -#endif -} - -/***************************************************************************** -** The next group of routines implement the I/O methods specified -** by the sqlite3_io_methods object. -******************************************************************************/ - -/* -** Some Microsoft compilers lack this definition. -*/ -#ifndef INVALID_SET_FILE_POINTER -# define INVALID_SET_FILE_POINTER ((DWORD)-1) -#endif - -/* -** Move the current position of the file handle passed as the first -** argument to offset iOffset within the file. If successful, return 0. -** Otherwise, set pFile->lastErrno and return non-zero. -*/ -static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ -#if !SQLITE_OS_WINRT - LONG upperBits; /* Most sig. 32 bits of new offset */ - LONG lowerBits; /* Least sig. 32 bits of new offset */ - DWORD dwRet; /* Value returned by SetFilePointer() */ - DWORD lastErrno; /* Value returned by GetLastError() */ - - upperBits = (LONG)((iOffset>>32) & 0x7fffffff); - lowerBits = (LONG)(iOffset & 0xffffffff); - - /* API oddity: If successful, SetFilePointer() returns a dword - ** containing the lower 32-bits of the new file-offset. Or, if it fails, - ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, - ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine - ** whether an error has actually occurred, it is also necessary to call - ** GetLastError(). - */ - dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); - - if( (dwRet==INVALID_SET_FILE_POINTER - && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ - pFile->lastErrno = lastErrno; - winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, - "seekWinFile", pFile->zPath); - return 1; - } - - return 0; -#else - /* - ** Same as above, except that this implementation works for WinRT. - */ - - LARGE_INTEGER x; /* The new offset */ - BOOL bRet; /* Value returned by SetFilePointerEx() */ - - x.QuadPart = iOffset; - bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN); - - if(!bRet){ - pFile->lastErrno = osGetLastError(); - winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, - "seekWinFile", pFile->zPath); - return 1; - } - - return 0; -#endif -} - -/* -** Close a file. -** -** It is reported that an attempt to close a handle might sometimes -** fail. This is a very unreasonable result, but Windows is notorious -** for being unreasonable so I do not doubt that it might happen. If -** the close fails, we pause for 100 milliseconds and try again. As -** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before -** giving up and returning an error. -*/ -#define MX_CLOSE_ATTEMPT 3 -static int winClose(sqlite3_file *id){ - int rc, cnt = 0; - winFile *pFile = (winFile*)id; - - assert( id!=0 ); -#ifndef SQLITE_OMIT_WAL - assert( pFile->pShm==0 ); -#endif - OSTRACE(("CLOSE %d\n", pFile->h)); - assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE ); - do{ - rc = osCloseHandle(pFile->h); - /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */ - }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlite3_win32_sleep(100), 1) ); -#if SQLITE_OS_WINCE -#define WINCE_DELETION_ATTEMPTS 3 - winceDestroyLock(pFile); - if( pFile->zDeleteOnClose ){ - int cnt = 0; - while( - osDeleteFileW(pFile->zDeleteOnClose)==0 - && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff - && cnt++ < WINCE_DELETION_ATTEMPTS - ){ - sqlite3_win32_sleep(100); /* Wait a little before trying again */ - } - sqlite3_free(pFile->zDeleteOnClose); - } -#endif - OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); - if( rc ){ - pFile->h = NULL; - } - OpenCounter(-1); - return rc ? SQLITE_OK - : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(), - "winClose", pFile->zPath); -} - -/* -** Read data from a file into a buffer. Return SQLITE_OK if all -** bytes were read successfully and SQLITE_IOERR if anything goes -** wrong. -*/ -static int winRead( - sqlite3_file *id, /* File to read from */ - void *pBuf, /* Write content into this buffer */ - int amt, /* Number of bytes to read */ - sqlite3_int64 offset /* Begin reading at this offset */ -){ -#if !SQLITE_OS_WINCE - OVERLAPPED overlapped; /* The offset for ReadFile. */ -#endif - winFile *pFile = (winFile*)id; /* file handle */ - DWORD nRead; /* Number of bytes actually read from file */ - int nRetry = 0; /* Number of retrys */ - - assert( id!=0 ); - SimulateIOError(return SQLITE_IOERR_READ); - OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype)); - -#if SQLITE_OS_WINCE - if( seekWinFile(pFile, offset) ){ - return SQLITE_FULL; - } - while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ -#else - memset(&overlapped, 0, sizeof(OVERLAPPED)); - overlapped.Offset = (LONG)(offset & 0xffffffff); - overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); - while( !osReadFile(pFile->h, pBuf, amt, &nRead, &overlapped) && - osGetLastError()!=ERROR_HANDLE_EOF ){ -#endif - DWORD lastErrno; - if( retryIoerr(&nRetry, &lastErrno) ) continue; - pFile->lastErrno = lastErrno; - return winLogError(SQLITE_IOERR_READ, pFile->lastErrno, - "winRead", pFile->zPath); - } - logIoerr(nRetry); - if( nRead<(DWORD)amt ){ - /* Unread parts of the buffer must be zero-filled */ - memset(&((char*)pBuf)[nRead], 0, amt-nRead); - return SQLITE_IOERR_SHORT_READ; - } - - return SQLITE_OK; -} - -/* -** Write data from a buffer into a file. Return SQLITE_OK on success -** or some other error code on failure. -*/ -static int winWrite( - sqlite3_file *id, /* File to write into */ - const void *pBuf, /* The bytes to be written */ - int amt, /* Number of bytes to write */ - sqlite3_int64 offset /* Offset into the file to begin writing at */ -){ - int rc = 0; /* True if error has occurred, else false */ - winFile *pFile = (winFile*)id; /* File handle */ - int nRetry = 0; /* Number of retries */ - - assert( amt>0 ); - assert( pFile ); - SimulateIOError(return SQLITE_IOERR_WRITE); - SimulateDiskfullError(return SQLITE_FULL); - - OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype)); - -#if SQLITE_OS_WINCE - rc = seekWinFile(pFile, offset); - if( rc==0 ){ -#else - { -#endif -#if !SQLITE_OS_WINCE - OVERLAPPED overlapped; /* The offset for WriteFile. */ -#endif - u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ - int nRem = amt; /* Number of bytes yet to be written */ - DWORD nWrite; /* Bytes written by each WriteFile() call */ - DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ - -#if !SQLITE_OS_WINCE - memset(&overlapped, 0, sizeof(OVERLAPPED)); - overlapped.Offset = (LONG)(offset & 0xffffffff); - overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); -#endif - - while( nRem>0 ){ -#if SQLITE_OS_WINCE - if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ -#else - if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ -#endif - if( retryIoerr(&nRetry, &lastErrno) ) continue; - break; - } - assert( nWrite==0 || nWrite<=(DWORD)nRem ); - if( nWrite==0 || nWrite>(DWORD)nRem ){ - lastErrno = osGetLastError(); - break; - } -#if !SQLITE_OS_WINCE - offset += nWrite; - overlapped.Offset = (LONG)(offset & 0xffffffff); - overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); -#endif - aRem += nWrite; - nRem -= nWrite; - } - if( nRem>0 ){ - pFile->lastErrno = lastErrno; - rc = 1; - } - } - - if( rc ){ - if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) - || ( pFile->lastErrno==ERROR_DISK_FULL )){ - return SQLITE_FULL; - } - return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, - "winWrite", pFile->zPath); - }else{ - logIoerr(nRetry); - } - return SQLITE_OK; -} - -/* -** Truncate an open file to a specified size -*/ -static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ - winFile *pFile = (winFile*)id; /* File handle object */ - int rc = SQLITE_OK; /* Return code for this function */ - - assert( pFile ); - - OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte)); - SimulateIOError(return SQLITE_IOERR_TRUNCATE); - - /* If the user has configured a chunk-size for this file, truncate the - ** file so that it consists of an integer number of chunks (i.e. the - ** actual file size after the operation may be larger than the requested - ** size). - */ - if( pFile->szChunk>0 ){ - nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; - } - - /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ - if( seekWinFile(pFile, nByte) ){ - rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, - "winTruncate1", pFile->zPath); - }else if( 0==osSetEndOfFile(pFile->h) ){ - pFile->lastErrno = osGetLastError(); - rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, - "winTruncate2", pFile->zPath); - } - - OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok")); - return rc; -} - -#ifdef SQLITE_TEST -/* -** Count the number of fullsyncs and normal syncs. This is used to test -** that syncs and fullsyncs are occuring at the right times. -*/ -SQLITE_API int sqlite3_sync_count = 0; -SQLITE_API int sqlite3_fullsync_count = 0; -#endif - -/* -** Make sure all writes to a particular file are committed to disk. -*/ -static int winSync(sqlite3_file *id, int flags){ -#ifndef SQLITE_NO_SYNC - /* - ** Used only when SQLITE_NO_SYNC is not defined. - */ - BOOL rc; -#endif -#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \ - (defined(SQLITE_TEST) && defined(SQLITE_DEBUG)) - /* - ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or - ** OSTRACE() macros. - */ - winFile *pFile = (winFile*)id; -#else - UNUSED_PARAMETER(id); -#endif - - assert( pFile ); - /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ - assert((flags&0x0F)==SQLITE_SYNC_NORMAL - || (flags&0x0F)==SQLITE_SYNC_FULL - ); - - OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype)); - - /* Unix cannot, but some systems may return SQLITE_FULL from here. This - ** line is to test that doing so does not cause any problems. - */ - SimulateDiskfullError( return SQLITE_FULL ); - -#ifndef SQLITE_TEST - UNUSED_PARAMETER(flags); -#else - if( (flags&0x0F)==SQLITE_SYNC_FULL ){ - sqlite3_fullsync_count++; - } - sqlite3_sync_count++; -#endif - - /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a - ** no-op - */ -#ifdef SQLITE_NO_SYNC - return SQLITE_OK; -#else - rc = osFlushFileBuffers(pFile->h); - SimulateIOError( rc=FALSE ); - if( rc ){ - return SQLITE_OK; - }else{ - pFile->lastErrno = osGetLastError(); - return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno, - "winSync", pFile->zPath); - } -#endif -} - -/* -** Determine the current size of a file in bytes -*/ -static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ - winFile *pFile = (winFile*)id; - int rc = SQLITE_OK; - - assert( id!=0 ); - SimulateIOError(return SQLITE_IOERR_FSTAT); -#if SQLITE_OS_WINRT - { - FILE_STANDARD_INFO info; - if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo, - &info, sizeof(info)) ){ - *pSize = info.EndOfFile.QuadPart; - }else{ - pFile->lastErrno = osGetLastError(); - rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, - "winFileSize", pFile->zPath); - } - } -#else - { - DWORD upperBits; - DWORD lowerBits; - DWORD lastErrno; - - lowerBits = osGetFileSize(pFile->h, &upperBits); - *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits; - if( (lowerBits == INVALID_FILE_SIZE) - && ((lastErrno = osGetLastError())!=NO_ERROR) ){ - pFile->lastErrno = lastErrno; - rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, - "winFileSize", pFile->zPath); - } - } -#endif - return rc; -} - -/* -** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. -*/ -#ifndef LOCKFILE_FAIL_IMMEDIATELY -# define LOCKFILE_FAIL_IMMEDIATELY 1 -#endif - -#ifndef LOCKFILE_EXCLUSIVE_LOCK -# define LOCKFILE_EXCLUSIVE_LOCK 2 -#endif - -/* -** Historically, SQLite has used both the LockFile and LockFileEx functions. -** When the LockFile function was used, it was always expected to fail -** immediately if the lock could not be obtained. Also, it always expected to -** obtain an exclusive lock. These flags are used with the LockFileEx function -** and reflect those expectations; therefore, they should not be changed. -*/ -#ifndef SQLITE_LOCKFILE_FLAGS -# define SQLITE_LOCKFILE_FLAGS (LOCKFILE_FAIL_IMMEDIATELY | \ - LOCKFILE_EXCLUSIVE_LOCK) -#endif - -/* -** Currently, SQLite never calls the LockFileEx function without wanting the -** call to fail immediately if the lock cannot be obtained. -*/ -#ifndef SQLITE_LOCKFILEEX_FLAGS -# define SQLITE_LOCKFILEEX_FLAGS (LOCKFILE_FAIL_IMMEDIATELY) -#endif - -/* -** Acquire a reader lock. -** Different API routines are called depending on whether or not this -** is Win9x or WinNT. -*/ -static int getReadLock(winFile *pFile){ - int res; - if( isNT() ){ -#if SQLITE_OS_WINCE - /* - ** NOTE: Windows CE is handled differently here due its lack of the Win32 - ** API LockFileEx. - */ - res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); -#else - res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0, - SHARED_SIZE, 0); -#endif - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - int lk; - sqlite3_randomness(sizeof(lk), &lk); - pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); - res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, - SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); - } -#endif - if( res == 0 ){ - pFile->lastErrno = osGetLastError(); - /* No need to log a failure to lock */ - } - return res; -} - -/* -** Undo a readlock -*/ -static int unlockReadLock(winFile *pFile){ - int res; - DWORD lastErrno; - if( isNT() ){ - res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); - } -#endif - if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){ - pFile->lastErrno = lastErrno; - winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno, - "unlockReadLock", pFile->zPath); - } - return res; -} - -/* -** Lock the file with the lock specified by parameter locktype - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. The winUnlock() routine -** erases all locks at once and returns us immediately to locking level 0. -** It is not possible to lower the locking level one step at a time. You -** must go straight to locking level 0. -*/ -static int winLock(sqlite3_file *id, int locktype){ - int rc = SQLITE_OK; /* Return code from subroutines */ - int res = 1; /* Result of a Windows lock call */ - int newLocktype; /* Set pFile->locktype to this value before exiting */ - int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ - winFile *pFile = (winFile*)id; - DWORD lastErrno = NO_ERROR; - - assert( id!=0 ); - OSTRACE(("LOCK %d %d was %d(%d)\n", - pFile->h, locktype, pFile->locktype, pFile->sharedLockByte)); - - /* If there is already a lock of this type or more restrictive on the - ** OsFile, do nothing. Don't use the end_lock: exit path, as - ** sqlite3OsEnterMutex() hasn't been called yet. - */ - if( pFile->locktype>=locktype ){ - return SQLITE_OK; - } - - /* Make sure the locking sequence is correct - */ - assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); - assert( locktype!=PENDING_LOCK ); - assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); - - /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or - ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of - ** the PENDING_LOCK byte is temporary. - */ - newLocktype = pFile->locktype; - if( (pFile->locktype==NO_LOCK) - || ( (locktype==EXCLUSIVE_LOCK) - && (pFile->locktype==RESERVED_LOCK)) - ){ - int cnt = 3; - while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, - PENDING_BYTE, 0, 1, 0))==0 ){ - /* Try 3 times to get the pending lock. This is needed to work - ** around problems caused by indexing and/or anti-virus software on - ** Windows systems. - ** If you are using this code as a model for alternative VFSes, do not - ** copy this retry logic. It is a hack intended for Windows only. - */ - OSTRACE(("could not get a PENDING lock. cnt=%d\n", cnt)); - if( cnt ) sqlite3_win32_sleep(1); - } - gotPendingLock = res; - if( !res ){ - lastErrno = osGetLastError(); - } - } - - /* Acquire a shared lock - */ - if( locktype==SHARED_LOCK && res ){ - assert( pFile->locktype==NO_LOCK ); - res = getReadLock(pFile); - if( res ){ - newLocktype = SHARED_LOCK; - }else{ - lastErrno = osGetLastError(); - } - } - - /* Acquire a RESERVED lock - */ - if( locktype==RESERVED_LOCK && res ){ - assert( pFile->locktype==SHARED_LOCK ); - res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0); - if( res ){ - newLocktype = RESERVED_LOCK; - }else{ - lastErrno = osGetLastError(); - } - } - - /* Acquire a PENDING lock - */ - if( locktype==EXCLUSIVE_LOCK && res ){ - newLocktype = PENDING_LOCK; - gotPendingLock = 0; - } - - /* Acquire an EXCLUSIVE lock - */ - if( locktype==EXCLUSIVE_LOCK && res ){ - assert( pFile->locktype>=SHARED_LOCK ); - res = unlockReadLock(pFile); - OSTRACE(("unreadlock = %d\n", res)); - res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0, - SHARED_SIZE, 0); - if( res ){ - newLocktype = EXCLUSIVE_LOCK; - }else{ - lastErrno = osGetLastError(); - OSTRACE(("error-code = %d\n", lastErrno)); - getReadLock(pFile); - } - } - - /* If we are holding a PENDING lock that ought to be released, then - ** release it now. - */ - if( gotPendingLock && locktype==SHARED_LOCK ){ - winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); - } - - /* Update the state of the lock has held in the file descriptor then - ** return the appropriate result code. - */ - if( res ){ - rc = SQLITE_OK; - }else{ - OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h, - locktype, newLocktype)); - pFile->lastErrno = lastErrno; - rc = SQLITE_BUSY; - } - pFile->locktype = (u8)newLocktype; - return rc; -} - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, return -** non-zero, otherwise zero. -*/ -static int winCheckReservedLock(sqlite3_file *id, int *pResOut){ - int rc; - winFile *pFile = (winFile*)id; - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - - assert( id!=0 ); - if( pFile->locktype>=RESERVED_LOCK ){ - rc = 1; - OSTRACE(("TEST WR-LOCK %d %d (local)\n", pFile->h, rc)); - }else{ - rc = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0); - if( rc ){ - winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); - } - rc = !rc; - OSTRACE(("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc)); - } - *pResOut = rc; - return SQLITE_OK; -} - -/* -** Lower the locking level on file descriptor id to locktype. locktype -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -** -** It is not possible for this routine to fail if the second argument -** is NO_LOCK. If the second argument is SHARED_LOCK then this routine -** might return SQLITE_IOERR; -*/ -static int winUnlock(sqlite3_file *id, int locktype){ - int type; - winFile *pFile = (winFile*)id; - int rc = SQLITE_OK; - assert( pFile!=0 ); - assert( locktype<=SHARED_LOCK ); - OSTRACE(("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype, - pFile->locktype, pFile->sharedLockByte)); - type = pFile->locktype; - if( type>=EXCLUSIVE_LOCK ){ - winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ - /* This should never happen. We should always be able to - ** reacquire the read lock */ - rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), - "winUnlock", pFile->zPath); - } - } - if( type>=RESERVED_LOCK ){ - winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); - } - if( locktype==NO_LOCK && type>=SHARED_LOCK ){ - unlockReadLock(pFile); - } - if( type>=PENDING_LOCK ){ - winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); - } - pFile->locktype = (u8)locktype; - return rc; -} - -/* -** If *pArg is inititially negative then this is a query. Set *pArg to -** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. -** -** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. -*/ -static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){ - if( *pArg<0 ){ - *pArg = (pFile->ctrlFlags & mask)!=0; - }else if( (*pArg)==0 ){ - pFile->ctrlFlags &= ~mask; - }else{ - pFile->ctrlFlags |= mask; - } -} - -/* Forward declaration */ -static int getTempname(int nBuf, char *zBuf); - -/* -** Control and query of the open file handle. -*/ -static int winFileControl(sqlite3_file *id, int op, void *pArg){ - winFile *pFile = (winFile*)id; - switch( op ){ - case SQLITE_FCNTL_LOCKSTATE: { - *(int*)pArg = pFile->locktype; - return SQLITE_OK; - } - case SQLITE_LAST_ERRNO: { - *(int*)pArg = (int)pFile->lastErrno; - return SQLITE_OK; - } - case SQLITE_FCNTL_CHUNK_SIZE: { - pFile->szChunk = *(int *)pArg; - return SQLITE_OK; - } - case SQLITE_FCNTL_SIZE_HINT: { - if( pFile->szChunk>0 ){ - sqlite3_int64 oldSz; - int rc = winFileSize(id, &oldSz); - if( rc==SQLITE_OK ){ - sqlite3_int64 newSz = *(sqlite3_int64*)pArg; - if( newSz>oldSz ){ - SimulateIOErrorBenign(1); - rc = winTruncate(id, newSz); - SimulateIOErrorBenign(0); - } - } - return rc; - } - return SQLITE_OK; - } - case SQLITE_FCNTL_PERSIST_WAL: { - winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg); - return SQLITE_OK; - } - case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { - winModeBit(pFile, WINFILE_PSOW, (int*)pArg); - return SQLITE_OK; - } - case SQLITE_FCNTL_VFSNAME: { - *(char**)pArg = sqlite3_mprintf("win32"); - return SQLITE_OK; - } - case SQLITE_FCNTL_WIN32_AV_RETRY: { - int *a = (int*)pArg; - if( a[0]>0 ){ - win32IoerrRetry = a[0]; - }else{ - a[0] = win32IoerrRetry; - } - if( a[1]>0 ){ - win32IoerrRetryDelay = a[1]; - }else{ - a[1] = win32IoerrRetryDelay; - } - return SQLITE_OK; - } - case SQLITE_FCNTL_TEMPFILENAME: { - char *zTFile = sqlite3MallocZero( pFile->pVfs->mxPathname ); - if( zTFile ){ - getTempname(pFile->pVfs->mxPathname, zTFile); - *(char**)pArg = zTFile; - } - return SQLITE_OK; - } - } - return SQLITE_NOTFOUND; -} - -/* -** Return the sector size in bytes of the underlying block device for -** the specified file. This is almost always 512 bytes, but may be -** larger for some devices. -** -** SQLite code assumes this function cannot fail. It also assumes that -** if two files are created in the same file-system directory (i.e. -** a database and its journal file) that the sector size will be the -** same for both. -*/ -static int winSectorSize(sqlite3_file *id){ - (void)id; - return SQLITE_DEFAULT_SECTOR_SIZE; -} - -/* -** Return a vector of device characteristics. -*/ -static int winDeviceCharacteristics(sqlite3_file *id){ - winFile *p = (winFile*)id; - return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | - ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); -} - -#ifndef SQLITE_OMIT_WAL - -/* -** Windows will only let you create file view mappings -** on allocation size granularity boundaries. -** During sqlite3_os_init() we do a GetSystemInfo() -** to get the granularity size. -*/ -SYSTEM_INFO winSysInfo; - -/* -** Helper functions to obtain and relinquish the global mutex. The -** global mutex is used to protect the winLockInfo objects used by -** this file, all of which may be shared by multiple threads. -** -** Function winShmMutexHeld() is used to assert() that the global mutex -** is held when required. This function is only used as part of assert() -** statements. e.g. -** -** winShmEnterMutex() -** assert( winShmMutexHeld() ); -** winShmLeaveMutex() -*/ -static void winShmEnterMutex(void){ - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); -} -static void winShmLeaveMutex(void){ - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); -} -#ifdef SQLITE_DEBUG -static int winShmMutexHeld(void) { - return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); -} -#endif - -/* -** Object used to represent a single file opened and mmapped to provide -** shared memory. When multiple threads all reference the same -** log-summary, each thread has its own winFile object, but they all -** point to a single instance of this object. In other words, each -** log-summary is opened only once per process. -** -** winShmMutexHeld() must be true when creating or destroying -** this object or while reading or writing the following fields: -** -** nRef -** pNext -** -** The following fields are read-only after the object is created: -** -** fid -** zFilename -** -** Either winShmNode.mutex must be held or winShmNode.nRef==0 and -** winShmMutexHeld() is true when reading or writing any other field -** in this structure. -** -*/ -struct winShmNode { - sqlite3_mutex *mutex; /* Mutex to access this object */ - char *zFilename; /* Name of the file */ - winFile hFile; /* File handle from winOpen */ - - int szRegion; /* Size of shared-memory regions */ - int nRegion; /* Size of array apRegion */ - struct ShmRegion { - HANDLE hMap; /* File handle from CreateFileMapping */ - void *pMap; - } *aRegion; - DWORD lastErrno; /* The Windows errno from the last I/O error */ - - int nRef; /* Number of winShm objects pointing to this */ - winShm *pFirst; /* All winShm objects pointing to this */ - winShmNode *pNext; /* Next in list of all winShmNode objects */ -#ifdef SQLITE_DEBUG - u8 nextShmId; /* Next available winShm.id value */ -#endif -}; - -/* -** A global array of all winShmNode objects. -** -** The winShmMutexHeld() must be true while reading or writing this list. -*/ -static winShmNode *winShmNodeList = 0; - -/* -** Structure used internally by this VFS to record the state of an -** open shared memory connection. -** -** The following fields are initialized when this object is created and -** are read-only thereafter: -** -** winShm.pShmNode -** winShm.id -** -** All other fields are read/write. The winShm.pShmNode->mutex must be held -** while accessing any read/write fields. -*/ -struct winShm { - winShmNode *pShmNode; /* The underlying winShmNode object */ - winShm *pNext; /* Next winShm with the same winShmNode */ - u8 hasMutex; /* True if holding the winShmNode mutex */ - u16 sharedMask; /* Mask of shared locks held */ - u16 exclMask; /* Mask of exclusive locks held */ -#ifdef SQLITE_DEBUG - u8 id; /* Id of this connection with its winShmNode */ -#endif -}; - -/* -** Constants used for locking -*/ -#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ -#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ - -/* -** Apply advisory locks for all n bytes beginning at ofst. -*/ -#define _SHM_UNLCK 1 -#define _SHM_RDLCK 2 -#define _SHM_WRLCK 3 -static int winShmSystemLock( - winShmNode *pFile, /* Apply locks to this open shared-memory segment */ - int lockType, /* _SHM_UNLCK, _SHM_RDLCK, or _SHM_WRLCK */ - int ofst, /* Offset to first byte to be locked/unlocked */ - int nByte /* Number of bytes to lock or unlock */ -){ - int rc = 0; /* Result code form Lock/UnlockFileEx() */ - - /* Access to the winShmNode object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); - - /* Release/Acquire the system-level lock */ - if( lockType==_SHM_UNLCK ){ - rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); - }else{ - /* Initialize the locking parameters */ - DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; - if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; - rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0); - } - - if( rc!= 0 ){ - rc = SQLITE_OK; - }else{ - pFile->lastErrno = osGetLastError(); - rc = SQLITE_BUSY; - } - - OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n", - pFile->hFile.h, - rc==SQLITE_OK ? "ok" : "failed", - lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx", - pFile->lastErrno)); - - return rc; -} - -/* Forward references to VFS methods */ -static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); -static int winDelete(sqlite3_vfs *,const char*,int); - -/* -** Purge the winShmNodeList list of all entries with winShmNode.nRef==0. -** -** This is not a VFS shared-memory method; it is a utility function called -** by VFS shared-memory methods. -*/ -static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ - winShmNode **pp; - winShmNode *p; - BOOL bRc; - assert( winShmMutexHeld() ); - pp = &winShmNodeList; - while( (p = *pp)!=0 ){ - if( p->nRef==0 ){ - int i; - if( p->mutex ) sqlite3_mutex_free(p->mutex); - for(i=0; inRegion; i++){ - bRc = osUnmapViewOfFile(p->aRegion[i].pMap); - OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", - (int)osGetCurrentProcessId(), i, - bRc ? "ok" : "failed")); - bRc = osCloseHandle(p->aRegion[i].hMap); - OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n", - (int)osGetCurrentProcessId(), i, - bRc ? "ok" : "failed")); - } - if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){ - SimulateIOErrorBenign(1); - winClose((sqlite3_file *)&p->hFile); - SimulateIOErrorBenign(0); - } - if( deleteFlag ){ - SimulateIOErrorBenign(1); - sqlite3BeginBenignMalloc(); - winDelete(pVfs, p->zFilename, 0); - sqlite3EndBenignMalloc(); - SimulateIOErrorBenign(0); - } - *pp = p->pNext; - sqlite3_free(p->aRegion); - sqlite3_free(p); - }else{ - pp = &p->pNext; - } - } -} - -/* -** Open the shared-memory area associated with database file pDbFd. -** -** When opening a new shared-memory file, if no other instances of that -** file are currently open, in this process or in other processes, then -** the file must be truncated to zero length or have its header cleared. -*/ -static int winOpenSharedMemory(winFile *pDbFd){ - struct winShm *p; /* The connection to be opened */ - struct winShmNode *pShmNode = 0; /* The underlying mmapped file */ - int rc; /* Result code */ - struct winShmNode *pNew; /* Newly allocated winShmNode */ - int nName; /* Size of zName in bytes */ - - assert( pDbFd->pShm==0 ); /* Not previously opened */ - - /* Allocate space for the new sqlite3_shm object. Also speculatively - ** allocate space for a new winShmNode and filename. - */ - p = sqlite3MallocZero( sizeof(*p) ); - if( p==0 ) return SQLITE_IOERR_NOMEM; - nName = sqlite3Strlen30(pDbFd->zPath); - pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 ); - if( pNew==0 ){ - sqlite3_free(p); - return SQLITE_IOERR_NOMEM; - } - pNew->zFilename = (char*)&pNew[1]; - sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); - sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); - - /* Look to see if there is an existing winShmNode that can be used. - ** If no matching winShmNode currently exists, create a new one. - */ - winShmEnterMutex(); - for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ - /* TBD need to come up with better match here. Perhaps - ** use FILE_ID_BOTH_DIR_INFO Structure. - */ - if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; - } - if( pShmNode ){ - sqlite3_free(pNew); - }else{ - pShmNode = pNew; - pNew = 0; - ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; - pShmNode->pNext = winShmNodeList; - winShmNodeList = pShmNode; - - pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pShmNode->mutex==0 ){ - rc = SQLITE_IOERR_NOMEM; - goto shm_open_err; - } - - rc = winOpen(pDbFd->pVfs, - pShmNode->zFilename, /* Name of the file (UTF-8) */ - (sqlite3_file*)&pShmNode->hFile, /* File handle here */ - SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, - 0); - if( SQLITE_OK!=rc ){ - goto shm_open_err; - } - - /* Check to see if another process is holding the dead-man switch. - ** If not, truncate the file to zero length. - */ - if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){ - rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); - if( rc!=SQLITE_OK ){ - rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), - "winOpenShm", pDbFd->zPath); - } - } - if( rc==SQLITE_OK ){ - winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); - rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS, 1); - } - if( rc ) goto shm_open_err; - } - - /* Make the new connection a child of the winShmNode */ - p->pShmNode = pShmNode; -#ifdef SQLITE_DEBUG - p->id = pShmNode->nextShmId++; -#endif - pShmNode->nRef++; - pDbFd->pShm = p; - winShmLeaveMutex(); - - /* The reference count on pShmNode has already been incremented under - ** the cover of the winShmEnterMutex() mutex and the pointer from the - ** new (struct winShm) object to the pShmNode has been set. All that is - ** left to do is to link the new object into the linked list starting - ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex - ** mutex. - */ - sqlite3_mutex_enter(pShmNode->mutex); - p->pNext = pShmNode->pFirst; - pShmNode->pFirst = p; - sqlite3_mutex_leave(pShmNode->mutex); - return SQLITE_OK; - - /* Jump here on any error */ -shm_open_err: - winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); - winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ - sqlite3_free(p); - sqlite3_free(pNew); - winShmLeaveMutex(); - return rc; -} - -/* -** Close a connection to shared-memory. Delete the underlying -** storage if deleteFlag is true. -*/ -static int winShmUnmap( - sqlite3_file *fd, /* Database holding shared memory */ - int deleteFlag /* Delete after closing if true */ -){ - winFile *pDbFd; /* Database holding shared-memory */ - winShm *p; /* The connection to be closed */ - winShmNode *pShmNode; /* The underlying shared-memory file */ - winShm **pp; /* For looping over sibling connections */ - - pDbFd = (winFile*)fd; - p = pDbFd->pShm; - if( p==0 ) return SQLITE_OK; - pShmNode = p->pShmNode; - - /* Remove connection p from the set of connections associated - ** with pShmNode */ - sqlite3_mutex_enter(pShmNode->mutex); - for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} - *pp = p->pNext; - - /* Free the connection p */ - sqlite3_free(p); - pDbFd->pShm = 0; - sqlite3_mutex_leave(pShmNode->mutex); - - /* If pShmNode->nRef has reached 0, then close the underlying - ** shared-memory file, too */ - winShmEnterMutex(); - assert( pShmNode->nRef>0 ); - pShmNode->nRef--; - if( pShmNode->nRef==0 ){ - winShmPurge(pDbFd->pVfs, deleteFlag); - } - winShmLeaveMutex(); - - return SQLITE_OK; -} - -/* -** Change the lock state for a shared-memory segment. -*/ -static int winShmLock( - sqlite3_file *fd, /* Database file holding the shared memory */ - int ofst, /* First lock to acquire or release */ - int n, /* Number of locks to acquire or release */ - int flags /* What to do with the lock */ -){ - winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ - winShm *p = pDbFd->pShm; /* The shared memory being locked */ - winShm *pX; /* For looping over all siblings */ - winShmNode *pShmNode = p->pShmNode; - int rc = SQLITE_OK; /* Result code */ - u16 mask; /* Mask of locks to take or release */ - - assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); - assert( n>=1 ); - assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) - || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) - || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) - || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); - assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); - - mask = (u16)((1U<<(ofst+n)) - (1U<1 || mask==(1<mutex); - if( flags & SQLITE_SHM_UNLOCK ){ - u16 allMask = 0; /* Mask of locks held by siblings */ - - /* See if any siblings hold this same lock */ - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); - allMask |= pX->sharedMask; - } - - /* Unlock the system-level locks */ - if( (mask & allMask)==0 ){ - rc = winShmSystemLock(pShmNode, _SHM_UNLCK, ofst+WIN_SHM_BASE, n); - }else{ - rc = SQLITE_OK; - } - - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~mask; - p->sharedMask &= ~mask; - } - }else if( flags & SQLITE_SHM_SHARED ){ - u16 allShared = 0; /* Union of locks held by connections other than "p" */ - - /* Find out which shared locks are already held by sibling connections. - ** If any sibling already holds an exclusive lock, go ahead and return - ** SQLITE_BUSY. - */ - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - if( (pX->exclMask & mask)!=0 ){ - rc = SQLITE_BUSY; - break; - } - allShared |= pX->sharedMask; - } - - /* Get shared locks at the system level, if necessary */ - if( rc==SQLITE_OK ){ - if( (allShared & mask)==0 ){ - rc = winShmSystemLock(pShmNode, _SHM_RDLCK, ofst+WIN_SHM_BASE, n); - }else{ - rc = SQLITE_OK; - } - } - - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= mask; - } - }else{ - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. - */ - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ - rc = SQLITE_BUSY; - break; - } - } - - /* Get the exclusive locks at the system level. Then if successful - ** also mark the local connection as being locked. - */ - if( rc==SQLITE_OK ){ - rc = winShmSystemLock(pShmNode, _SHM_WRLCK, ofst+WIN_SHM_BASE, n); - if( rc==SQLITE_OK ){ - assert( (p->sharedMask & mask)==0 ); - p->exclMask |= mask; - } - } - } - sqlite3_mutex_leave(pShmNode->mutex); - OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n", - p->id, (int)osGetCurrentProcessId(), p->sharedMask, p->exclMask, - rc ? "failed" : "ok")); - return rc; -} - -/* -** Implement a memory barrier or memory fence on shared memory. -** -** All loads and stores begun before the barrier must complete before -** any load or store begun after the barrier. -*/ -static void winShmBarrier( - sqlite3_file *fd /* Database holding the shared memory */ -){ - UNUSED_PARAMETER(fd); - /* MemoryBarrier(); // does not work -- do not know why not */ - winShmEnterMutex(); - winShmLeaveMutex(); -} - -/* -** This function is called to obtain a pointer to region iRegion of the -** shared-memory associated with the database file fd. Shared-memory regions -** are numbered starting from zero. Each shared-memory region is szRegion -** bytes in size. -** -** If an error occurs, an error code is returned and *pp is set to NULL. -** -** Otherwise, if the isWrite parameter is 0 and the requested shared-memory -** region has not been allocated (by any client, including one running in a -** separate process), then *pp is set to NULL and SQLITE_OK returned. If -** isWrite is non-zero and the requested shared-memory region has not yet -** been allocated, it is allocated by this function. -** -** If the shared-memory region has already been allocated or is allocated by -** this call as described above, then it is mapped into this processes -** address space (if it is not already), *pp is set to point to the mapped -** memory and SQLITE_OK returned. -*/ -static int winShmMap( - sqlite3_file *fd, /* Handle open on database file */ - int iRegion, /* Region to retrieve */ - int szRegion, /* Size of regions */ - int isWrite, /* True to extend file if necessary */ - void volatile **pp /* OUT: Mapped memory */ -){ - winFile *pDbFd = (winFile*)fd; - winShm *p = pDbFd->pShm; - winShmNode *pShmNode; - int rc = SQLITE_OK; - - if( !p ){ - rc = winOpenSharedMemory(pDbFd); - if( rc!=SQLITE_OK ) return rc; - p = pDbFd->pShm; - } - pShmNode = p->pShmNode; - - sqlite3_mutex_enter(pShmNode->mutex); - assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); - - if( pShmNode->nRegion<=iRegion ){ - struct ShmRegion *apNew; /* New aRegion[] array */ - int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ - sqlite3_int64 sz; /* Current size of wal-index file */ - - pShmNode->szRegion = szRegion; - - /* The requested region is not mapped into this processes address space. - ** Check to see if it has been allocated (i.e. if the wal-index file is - ** large enough to contain the requested region). - */ - rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); - if( rc!=SQLITE_OK ){ - rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), - "winShmMap1", pDbFd->zPath); - goto shmpage_out; - } - - if( szhFile, nByte); - if( rc!=SQLITE_OK ){ - rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), - "winShmMap2", pDbFd->zPath); - goto shmpage_out; - } - } - - /* Map the requested memory region into this processes address space. */ - apNew = (struct ShmRegion *)sqlite3_realloc( - pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) - ); - if( !apNew ){ - rc = SQLITE_IOERR_NOMEM; - goto shmpage_out; - } - pShmNode->aRegion = apNew; - - while( pShmNode->nRegion<=iRegion ){ - HANDLE hMap = NULL; /* file-mapping handle */ - void *pMap = 0; /* Mapped memory region */ - -#if SQLITE_OS_WINRT - hMap = osCreateFileMappingFromApp(pShmNode->hFile.h, - NULL, PAGE_READWRITE, nByte, NULL - ); -#elif defined(SQLITE_WIN32_HAS_WIDE) - hMap = osCreateFileMappingW(pShmNode->hFile.h, - NULL, PAGE_READWRITE, 0, nByte, NULL - ); -#elif defined(SQLITE_WIN32_HAS_ANSI) - hMap = osCreateFileMappingA(pShmNode->hFile.h, - NULL, PAGE_READWRITE, 0, nByte, NULL - ); -#endif - OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n", - (int)osGetCurrentProcessId(), pShmNode->nRegion, nByte, - hMap ? "ok" : "failed")); - if( hMap ){ - int iOffset = pShmNode->nRegion*szRegion; - int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; -#if SQLITE_OS_WINRT - pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ, - iOffset - iOffsetShift, szRegion + iOffsetShift - ); -#else - pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, - 0, iOffset - iOffsetShift, szRegion + iOffsetShift - ); -#endif - OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n", - (int)osGetCurrentProcessId(), pShmNode->nRegion, iOffset, - szRegion, pMap ? "ok" : "failed")); - } - if( !pMap ){ - pShmNode->lastErrno = osGetLastError(); - rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno, - "winShmMap3", pDbFd->zPath); - if( hMap ) osCloseHandle(hMap); - goto shmpage_out; - } - - pShmNode->aRegion[pShmNode->nRegion].pMap = pMap; - pShmNode->aRegion[pShmNode->nRegion].hMap = hMap; - pShmNode->nRegion++; - } - } - -shmpage_out: - if( pShmNode->nRegion>iRegion ){ - int iOffset = iRegion*szRegion; - int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; - char *p = (char *)pShmNode->aRegion[iRegion].pMap; - *pp = (void *)&p[iOffsetShift]; - }else{ - *pp = 0; - } - sqlite3_mutex_leave(pShmNode->mutex); - return rc; -} - -#else -# define winShmMap 0 -# define winShmLock 0 -# define winShmBarrier 0 -# define winShmUnmap 0 -#endif /* #ifndef SQLITE_OMIT_WAL */ - -/* -** Here ends the implementation of all sqlite3_file methods. -** -********************** End sqlite3_file Methods ******************************* -******************************************************************************/ - -/* -** This vector defines all the methods that can operate on an -** sqlite3_file for win32. -*/ -static const sqlite3_io_methods winIoMethod = { - 2, /* iVersion */ - winClose, /* xClose */ - winRead, /* xRead */ - winWrite, /* xWrite */ - winTruncate, /* xTruncate */ - winSync, /* xSync */ - winFileSize, /* xFileSize */ - winLock, /* xLock */ - winUnlock, /* xUnlock */ - winCheckReservedLock, /* xCheckReservedLock */ - winFileControl, /* xFileControl */ - winSectorSize, /* xSectorSize */ - winDeviceCharacteristics, /* xDeviceCharacteristics */ - winShmMap, /* xShmMap */ - winShmLock, /* xShmLock */ - winShmBarrier, /* xShmBarrier */ - winShmUnmap /* xShmUnmap */ -}; - -/**************************************************************************** -**************************** sqlite3_vfs methods **************************** -** -** This division contains the implementation of methods on the -** sqlite3_vfs object. -*/ - -/* -** Convert a UTF-8 filename into whatever form the underlying -** operating system wants filenames in. Space to hold the result -** is obtained from malloc and must be freed by the calling -** function. -*/ -static void *convertUtf8Filename(const char *zFilename){ - void *zConverted = 0; - if( isNT() ){ - zConverted = utf8ToUnicode(zFilename); - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - zConverted = sqlite3_win32_utf8_to_mbcs(zFilename); - } -#endif - /* caller will handle out of memory */ - return zConverted; -} - -/* -** Create a temporary file name in zBuf. zBuf must be big enough to -** hold at pVfs->mxPathname characters. -*/ -static int getTempname(int nBuf, char *zBuf){ - static char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789"; - size_t i, j; - int nTempPath; - char zTempPath[MAX_PATH+2]; - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. - */ - SimulateIOError( return SQLITE_IOERR ); - - memset(zTempPath, 0, MAX_PATH+2); - - if( sqlite3_temp_directory ){ - sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory); - } -#if !SQLITE_OS_WINRT - else if( isNT() ){ - char *zMulti; - WCHAR zWidePath[MAX_PATH]; - osGetTempPathW(MAX_PATH-30, zWidePath); - zMulti = unicodeToUtf8(zWidePath); - if( zMulti ){ - sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti); - sqlite3_free(zMulti); - }else{ - return SQLITE_IOERR_NOMEM; - } - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - char *zUtf8; - char zMbcsPath[MAX_PATH]; - osGetTempPathA(MAX_PATH-30, zMbcsPath); - zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath); - if( zUtf8 ){ - sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8); - sqlite3_free(zUtf8); - }else{ - return SQLITE_IOERR_NOMEM; - } - } -#endif -#endif - - /* Check that the output buffer is large enough for the temporary file - ** name. If it is not, return SQLITE_ERROR. - */ - nTempPath = sqlite3Strlen30(zTempPath); - - if( (nTempPath + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){ - return SQLITE_ERROR; - } - - for(i=nTempPath; i>0 && zTempPath[i-1]=='\\'; i--){} - zTempPath[i] = 0; - - sqlite3_snprintf(nBuf-18, zBuf, (nTempPath > 0) ? - "%s\\"SQLITE_TEMP_FILE_PREFIX : SQLITE_TEMP_FILE_PREFIX, - zTempPath); - j = sqlite3Strlen30(zBuf); - sqlite3_randomness(15, &zBuf[j]); - for(i=0; i<15; i++, j++){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; - } - zBuf[j] = 0; - zBuf[j+1] = 0; - - OSTRACE(("TEMP FILENAME: %s\n", zBuf)); - return SQLITE_OK; -} - -/* -** Return TRUE if the named file is really a directory. Return false if -** it is something other than a directory, or if there is any kind of memory -** allocation failure. -*/ -static int winIsDir(const void *zConverted){ - DWORD attr; - int rc = 0; - DWORD lastErrno; - - if( isNT() ){ - int cnt = 0; - WIN32_FILE_ATTRIBUTE_DATA sAttrData; - memset(&sAttrData, 0, sizeof(sAttrData)); - while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, - GetFileExInfoStandard, - &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){} - if( !rc ){ - return 0; /* Invalid name? */ - } - attr = sAttrData.dwFileAttributes; -#if SQLITE_OS_WINCE==0 - }else{ - attr = osGetFileAttributesA((char*)zConverted); -#endif - } - return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY); -} - -/* -** Open a file. -*/ -static int winOpen( - sqlite3_vfs *pVfs, /* Not used */ - const char *zName, /* Name of the file (UTF-8) */ - sqlite3_file *id, /* Write the SQLite file handle here */ - int flags, /* Open mode flags */ - int *pOutFlags /* Status return flags */ -){ - HANDLE h; - DWORD lastErrno; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - DWORD dwFlagsAndAttributes = 0; -#if SQLITE_OS_WINCE - int isTemp = 0; -#endif - winFile *pFile = (winFile*)id; - void *zConverted; /* Filename in OS encoding */ - const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ - int cnt = 0; - - /* If argument zPath is a NULL pointer, this function is required to open - ** a temporary file. Use this buffer to store the file name in. - */ - char zTmpname[MAX_PATH+2]; /* Buffer used to create temp filename */ - - int rc = SQLITE_OK; /* Function Return Code */ -#if !defined(NDEBUG) || SQLITE_OS_WINCE - int eType = flags&0xFFFFFF00; /* Type of file to open */ -#endif - - int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); - int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); - int isCreate = (flags & SQLITE_OPEN_CREATE); -#ifndef NDEBUG - int isReadonly = (flags & SQLITE_OPEN_READONLY); -#endif - int isReadWrite = (flags & SQLITE_OPEN_READWRITE); - -#ifndef NDEBUG - int isOpenJournal = (isCreate && ( - eType==SQLITE_OPEN_MASTER_JOURNAL - || eType==SQLITE_OPEN_MAIN_JOURNAL - || eType==SQLITE_OPEN_WAL - )); -#endif - - /* Check the following statements are true: - ** - ** (a) Exactly one of the READWRITE and READONLY flags must be set, and - ** (b) if CREATE is set, then READWRITE must also be set, and - ** (c) if EXCLUSIVE is set, then CREATE must also be set. - ** (d) if DELETEONCLOSE is set, then CREATE must also be set. - */ - assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); - assert(isCreate==0 || isReadWrite); - assert(isExclusive==0 || isCreate); - assert(isDelete==0 || isCreate); - - /* The main DB, main journal, WAL file and master journal are never - ** automatically deleted. Nor are they ever temporary files. */ - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); - - /* Assert that the upper layer has set one of the "file-type" flags. */ - assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB - || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL - || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL - || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL - ); - - assert( pFile!=0 ); - memset(pFile, 0, sizeof(winFile)); - pFile->h = INVALID_HANDLE_VALUE; - -#if SQLITE_OS_WINRT - if( !sqlite3_temp_directory ){ - sqlite3_log(SQLITE_ERROR, - "sqlite3_temp_directory variable should be set for WinRT"); - } -#endif - - /* If the second argument to this function is NULL, generate a - ** temporary file name to use - */ - if( !zUtf8Name ){ - assert(isDelete && !isOpenJournal); - memset(zTmpname, 0, MAX_PATH+2); - rc = getTempname(MAX_PATH+2, zTmpname); - if( rc!=SQLITE_OK ){ - return rc; - } - zUtf8Name = zTmpname; - } - - /* Database filenames are double-zero terminated if they are not - ** URIs with parameters. Hence, they can always be passed into - ** sqlite3_uri_parameter(). - */ - assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) || - zUtf8Name[strlen(zUtf8Name)+1]==0 ); - - /* Convert the filename to the system encoding. */ - zConverted = convertUtf8Filename(zUtf8Name); - if( zConverted==0 ){ - return SQLITE_IOERR_NOMEM; - } - - if( winIsDir(zConverted) ){ - sqlite3_free(zConverted); - return SQLITE_CANTOPEN_ISDIR; - } - - if( isReadWrite ){ - dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; - }else{ - dwDesiredAccess = GENERIC_READ; - } - - /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is - ** created. SQLite doesn't use it to indicate "exclusive access" - ** as it is usually understood. - */ - if( isExclusive ){ - /* Creates a new file, only if it does not already exist. */ - /* If the file exists, it fails. */ - dwCreationDisposition = CREATE_NEW; - }else if( isCreate ){ - /* Open existing file, or create if it doesn't exist */ - dwCreationDisposition = OPEN_ALWAYS; - }else{ - /* Opens a file, only if it exists. */ - dwCreationDisposition = OPEN_EXISTING; - } - - dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - - if( isDelete ){ -#if SQLITE_OS_WINCE - dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN; - isTemp = 1; -#else - dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY - | FILE_ATTRIBUTE_HIDDEN - | FILE_FLAG_DELETE_ON_CLOSE; -#endif - }else{ - dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; - } - /* Reports from the internet are that performance is always - ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */ -#if SQLITE_OS_WINCE - dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; -#endif - - if( isNT() ){ -#if SQLITE_OS_WINRT - CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; - extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); - extendedParameters.dwFileAttributes = - dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK; - extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK; - extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; - extendedParameters.lpSecurityAttributes = NULL; - extendedParameters.hTemplateFile = NULL; - while( (h = osCreateFile2((LPCWSTR)zConverted, - dwDesiredAccess, - dwShareMode, - dwCreationDisposition, - &extendedParameters))==INVALID_HANDLE_VALUE && - retryIoerr(&cnt, &lastErrno) ){ - /* Noop */ - } -#else - while( (h = osCreateFileW((LPCWSTR)zConverted, - dwDesiredAccess, - dwShareMode, NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL))==INVALID_HANDLE_VALUE && - retryIoerr(&cnt, &lastErrno) ){ - /* Noop */ - } -#endif - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - while( (h = osCreateFileA((LPCSTR)zConverted, - dwDesiredAccess, - dwShareMode, NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL))==INVALID_HANDLE_VALUE && - retryIoerr(&cnt, &lastErrno) ){ - /* Noop */ - } - } -#endif - logIoerr(cnt); - - OSTRACE(("OPEN %d %s 0x%lx %s\n", - h, zName, dwDesiredAccess, - h==INVALID_HANDLE_VALUE ? "failed" : "ok")); - - if( h==INVALID_HANDLE_VALUE ){ - pFile->lastErrno = lastErrno; - winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name); - sqlite3_free(zConverted); - if( isReadWrite && !isExclusive ){ - return winOpen(pVfs, zName, id, - ((flags|SQLITE_OPEN_READONLY) & - ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), - pOutFlags); - }else{ - return SQLITE_CANTOPEN_BKPT; - } - } - - if( pOutFlags ){ - if( isReadWrite ){ - *pOutFlags = SQLITE_OPEN_READWRITE; - }else{ - *pOutFlags = SQLITE_OPEN_READONLY; - } - } - -#if SQLITE_OS_WINCE - if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB - && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK - ){ - osCloseHandle(h); - sqlite3_free(zConverted); - return rc; - } - if( isTemp ){ - pFile->zDeleteOnClose = zConverted; - }else -#endif - { - sqlite3_free(zConverted); - } - - pFile->pMethod = &winIoMethod; - pFile->pVfs = pVfs; - pFile->h = h; - if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){ - pFile->ctrlFlags |= WINFILE_PSOW; - } - pFile->lastErrno = NO_ERROR; - pFile->zPath = zName; - - OpenCounter(+1); - return rc; -} - -/* -** Delete the named file. -** -** Note that Windows does not allow a file to be deleted if some other -** process has it open. Sometimes a virus scanner or indexing program -** will open a journal file shortly after it is created in order to do -** whatever it does. While this other process is holding the -** file open, we will be unable to delete it. To work around this -** problem, we delay 100 milliseconds and try to delete again. Up -** to MX_DELETION_ATTEMPTs deletion attempts are run before giving -** up and returning an error. -*/ -static int winDelete( - sqlite3_vfs *pVfs, /* Not used on win32 */ - const char *zFilename, /* Name of file to delete */ - int syncDir /* Not used on win32 */ -){ - int cnt = 0; - int rc; - DWORD attr; - DWORD lastErrno; - void *zConverted; - UNUSED_PARAMETER(pVfs); - UNUSED_PARAMETER(syncDir); - - SimulateIOError(return SQLITE_IOERR_DELETE); - zConverted = convertUtf8Filename(zFilename); - if( zConverted==0 ){ - return SQLITE_IOERR_NOMEM; - } - if( isNT() ){ - do { -#if SQLITE_OS_WINRT - WIN32_FILE_ATTRIBUTE_DATA sAttrData; - memset(&sAttrData, 0, sizeof(sAttrData)); - if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard, - &sAttrData) ){ - attr = sAttrData.dwFileAttributes; - }else{ - lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND - || lastErrno==ERROR_PATH_NOT_FOUND ){ - rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ - }else{ - rc = SQLITE_ERROR; - } - break; - } -#else - attr = osGetFileAttributesW(zConverted); -#endif - if ( attr==INVALID_FILE_ATTRIBUTES ){ - lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND - || lastErrno==ERROR_PATH_NOT_FOUND ){ - rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ - }else{ - rc = SQLITE_ERROR; - } - break; - } - if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ - rc = SQLITE_ERROR; /* Files only. */ - break; - } - if ( osDeleteFileW(zConverted) ){ - rc = SQLITE_OK; /* Deleted OK. */ - break; - } - if ( !retryIoerr(&cnt, &lastErrno) ){ - rc = SQLITE_ERROR; /* No more retries. */ - break; - } - } while(1); - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - do { - attr = osGetFileAttributesA(zConverted); - if ( attr==INVALID_FILE_ATTRIBUTES ){ - lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND - || lastErrno==ERROR_PATH_NOT_FOUND ){ - rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ - }else{ - rc = SQLITE_ERROR; - } - break; - } - if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ - rc = SQLITE_ERROR; /* Files only. */ - break; - } - if ( osDeleteFileA(zConverted) ){ - rc = SQLITE_OK; /* Deleted OK. */ - break; - } - if ( !retryIoerr(&cnt, &lastErrno) ){ - rc = SQLITE_ERROR; /* No more retries. */ - break; - } - } while(1); - } -#endif - if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){ - rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, - "winDelete", zFilename); - }else{ - logIoerr(cnt); - } - sqlite3_free(zConverted); - OSTRACE(("DELETE \"%s\" %s\n", zFilename, (rc ? "failed" : "ok" ))); - return rc; -} - -/* -** Check the existence and status of a file. -*/ -static int winAccess( - sqlite3_vfs *pVfs, /* Not used on win32 */ - const char *zFilename, /* Name of file to check */ - int flags, /* Type of test to make on this file */ - int *pResOut /* OUT: Result */ -){ - DWORD attr; - int rc = 0; - DWORD lastErrno; - void *zConverted; - UNUSED_PARAMETER(pVfs); - - SimulateIOError( return SQLITE_IOERR_ACCESS; ); - zConverted = convertUtf8Filename(zFilename); - if( zConverted==0 ){ - return SQLITE_IOERR_NOMEM; - } - if( isNT() ){ - int cnt = 0; - WIN32_FILE_ATTRIBUTE_DATA sAttrData; - memset(&sAttrData, 0, sizeof(sAttrData)); - while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, - GetFileExInfoStandard, - &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){} - if( rc ){ - /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file - ** as if it does not exist. - */ - if( flags==SQLITE_ACCESS_EXISTS - && sAttrData.nFileSizeHigh==0 - && sAttrData.nFileSizeLow==0 ){ - attr = INVALID_FILE_ATTRIBUTES; - }else{ - attr = sAttrData.dwFileAttributes; - } - }else{ - logIoerr(cnt); - if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ - winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", zFilename); - sqlite3_free(zConverted); - return SQLITE_IOERR_ACCESS; - }else{ - attr = INVALID_FILE_ATTRIBUTES; - } - } - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - attr = osGetFileAttributesA((char*)zConverted); - } -#endif - sqlite3_free(zConverted); - switch( flags ){ - case SQLITE_ACCESS_READ: - case SQLITE_ACCESS_EXISTS: - rc = attr!=INVALID_FILE_ATTRIBUTES; - break; - case SQLITE_ACCESS_READWRITE: - rc = attr!=INVALID_FILE_ATTRIBUTES && - (attr & FILE_ATTRIBUTE_READONLY)==0; - break; - default: - assert(!"Invalid flags argument"); - } - *pResOut = rc; - return SQLITE_OK; -} - - -/* -** Returns non-zero if the specified path name should be used verbatim. If -** non-zero is returned from this function, the calling function must simply -** use the provided path name verbatim -OR- resolve it into a full path name -** using the GetFullPathName Win32 API function (if available). -*/ -static BOOL winIsVerbatimPathname( - const char *zPathname -){ - /* - ** If the path name starts with a forward slash or a backslash, it is either - ** a legal UNC name, a volume relative path, or an absolute path name in the - ** "Unix" format on Windows. There is no easy way to differentiate between - ** the final two cases; therefore, we return the safer return value of TRUE - ** so that callers of this function will simply use it verbatim. - */ - if ( zPathname[0]=='/' || zPathname[0]=='\\' ){ - return TRUE; - } - - /* - ** If the path name starts with a letter and a colon it is either a volume - ** relative path or an absolute path. Callers of this function must not - ** attempt to treat it as a relative path name (i.e. they should simply use - ** it verbatim). - */ - if ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ){ - return TRUE; - } - - /* - ** If we get to this point, the path name should almost certainly be a purely - ** relative one (i.e. not a UNC name, not absolute, and not volume relative). - */ - return FALSE; -} - -/* -** Turn a relative pathname into a full pathname. Write the full -** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname -** bytes in size. -*/ -static int winFullPathname( - sqlite3_vfs *pVfs, /* Pointer to vfs object */ - const char *zRelative, /* Possibly relative input path */ - int nFull, /* Size of output buffer in bytes */ - char *zFull /* Output buffer */ -){ - -#if defined(__CYGWIN__) - SimulateIOError( return SQLITE_ERROR ); - UNUSED_PARAMETER(nFull); - assert( pVfs->mxPathname>=MAX_PATH ); - assert( nFull>=pVfs->mxPathname ); - if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ - /* - ** NOTE: We are dealing with a relative path name and the data - ** directory has been set. Therefore, use it as the basis - ** for converting the relative path name to an absolute - ** one by prepending the data directory and a slash. - */ - char zOut[MAX_PATH+1]; - memset(zOut, 0, MAX_PATH+1); - cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut, - MAX_PATH+1); - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s", - sqlite3_data_directory, zOut); - }else{ - cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull); - } - return SQLITE_OK; -#endif - -#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__) - SimulateIOError( return SQLITE_ERROR ); - /* WinCE has no concept of a relative pathname, or so I am told. */ - /* WinRT has no way to convert a relative path to an absolute one. */ - if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ - /* - ** NOTE: We are dealing with a relative path name and the data - ** directory has been set. Therefore, use it as the basis - ** for converting the relative path name to an absolute - ** one by prepending the data directory and a backslash. - */ - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s", - sqlite3_data_directory, zRelative); - }else{ - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative); - } - return SQLITE_OK; -#endif - -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) - DWORD nByte; - void *zConverted; - char *zOut; - - /* If this path name begins with "/X:", where "X" is any alphabetic - ** character, discard the initial "/" from the pathname. - */ - if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){ - zRelative++; - } - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. This function could fail if, for example, the - ** current working directory has been unlinked. - */ - SimulateIOError( return SQLITE_ERROR ); - if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ - /* - ** NOTE: We are dealing with a relative path name and the data - ** directory has been set. Therefore, use it as the basis - ** for converting the relative path name to an absolute - ** one by prepending the data directory and a backslash. - */ - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s", - sqlite3_data_directory, zRelative); - return SQLITE_OK; - } - zConverted = convertUtf8Filename(zRelative); - if( zConverted==0 ){ - return SQLITE_IOERR_NOMEM; - } - if( isNT() ){ - LPWSTR zTemp; - nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0); - if( nByte==0 ){ - winLogError(SQLITE_ERROR, osGetLastError(), - "GetFullPathNameW1", zConverted); - sqlite3_free(zConverted); - return SQLITE_CANTOPEN_FULLPATH; - } - nByte += 3; - zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); - if( zTemp==0 ){ - sqlite3_free(zConverted); - return SQLITE_IOERR_NOMEM; - } - nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0); - if( nByte==0 ){ - winLogError(SQLITE_ERROR, osGetLastError(), - "GetFullPathNameW2", zConverted); - sqlite3_free(zConverted); - sqlite3_free(zTemp); - return SQLITE_CANTOPEN_FULLPATH; - } - sqlite3_free(zConverted); - zOut = unicodeToUtf8(zTemp); - sqlite3_free(zTemp); - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - char *zTemp; - nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0); - if( nByte==0 ){ - winLogError(SQLITE_ERROR, osGetLastError(), - "GetFullPathNameA1", zConverted); - sqlite3_free(zConverted); - return SQLITE_CANTOPEN_FULLPATH; - } - nByte += 3; - zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); - if( zTemp==0 ){ - sqlite3_free(zConverted); - return SQLITE_IOERR_NOMEM; - } - nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0); - if( nByte==0 ){ - winLogError(SQLITE_ERROR, osGetLastError(), - "GetFullPathNameA2", zConverted); - sqlite3_free(zConverted); - sqlite3_free(zTemp); - return SQLITE_CANTOPEN_FULLPATH; - } - sqlite3_free(zConverted); - zOut = sqlite3_win32_mbcs_to_utf8(zTemp); - sqlite3_free(zTemp); - } -#endif - if( zOut ){ - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut); - sqlite3_free(zOut); - return SQLITE_OK; - }else{ - return SQLITE_IOERR_NOMEM; - } -#endif -} - -#ifndef SQLITE_OMIT_LOAD_EXTENSION -/* -** Interfaces for opening a shared library, finding entry points -** within the shared library, and closing the shared library. -*/ -/* -** Interfaces for opening a shared library, finding entry points -** within the shared library, and closing the shared library. -*/ -static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ - HANDLE h; - void *zConverted = convertUtf8Filename(zFilename); - UNUSED_PARAMETER(pVfs); - if( zConverted==0 ){ - return 0; - } - if( isNT() ){ -#if SQLITE_OS_WINRT - h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0); -#else - h = osLoadLibraryW((LPCWSTR)zConverted); -#endif - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - h = osLoadLibraryA((char*)zConverted); - } -#endif - sqlite3_free(zConverted); - return (void*)h; -} -static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ - UNUSED_PARAMETER(pVfs); - getLastErrorMsg(osGetLastError(), nBuf, zBufOut); -} -static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){ - UNUSED_PARAMETER(pVfs); - return (void(*)(void))osGetProcAddressA((HANDLE)pH, zSym); -} -static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ - UNUSED_PARAMETER(pVfs); - osFreeLibrary((HANDLE)pHandle); -} -#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ - #define winDlOpen 0 - #define winDlError 0 - #define winDlSym 0 - #define winDlClose 0 -#endif - - -/* -** Write up to nBuf bytes of randomness into zBuf. -*/ -static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ - int n = 0; - UNUSED_PARAMETER(pVfs); -#if defined(SQLITE_TEST) - n = nBuf; - memset(zBuf, 0, nBuf); -#else - if( sizeof(SYSTEMTIME)<=nBuf-n ){ - SYSTEMTIME x; - osGetSystemTime(&x); - memcpy(&zBuf[n], &x, sizeof(x)); - n += sizeof(x); - } - if( sizeof(DWORD)<=nBuf-n ){ - DWORD pid = osGetCurrentProcessId(); - memcpy(&zBuf[n], &pid, sizeof(pid)); - n += sizeof(pid); - } -#if SQLITE_OS_WINRT - if( sizeof(ULONGLONG)<=nBuf-n ){ - ULONGLONG cnt = osGetTickCount64(); - memcpy(&zBuf[n], &cnt, sizeof(cnt)); - n += sizeof(cnt); - } -#else - if( sizeof(DWORD)<=nBuf-n ){ - DWORD cnt = osGetTickCount(); - memcpy(&zBuf[n], &cnt, sizeof(cnt)); - n += sizeof(cnt); - } -#endif - if( sizeof(LARGE_INTEGER)<=nBuf-n ){ - LARGE_INTEGER i; - osQueryPerformanceCounter(&i); - memcpy(&zBuf[n], &i, sizeof(i)); - n += sizeof(i); - } -#endif - return n; -} - - -/* -** Sleep for a little while. Return the amount of time slept. -*/ -static int winSleep(sqlite3_vfs *pVfs, int microsec){ - sqlite3_win32_sleep((microsec+999)/1000); - UNUSED_PARAMETER(pVfs); - return ((microsec+999)/1000)*1000; -} - -/* -** The following variable, if set to a non-zero value, is interpreted as -** the number of seconds since 1970 and is used to set the result of -** sqlite3OsCurrentTime() during testing. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ -#endif - -/* -** Find the current time (in Universal Coordinated Time). Write into *piNow -** the current time and date as a Julian Day number times 86_400_000. In -** other words, write into *piNow the number of milliseconds since the Julian -** epoch of noon in Greenwich on November 24, 4714 B.C according to the -** proleptic Gregorian calendar. -** -** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date -** cannot be found. -*/ -static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ - /* FILETIME structure is a 64-bit value representing the number of - 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). - */ - FILETIME ft; - static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000; -#ifdef SQLITE_TEST - static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; -#endif - /* 2^32 - to avoid use of LL and warnings in gcc */ - static const sqlite3_int64 max32BitValue = - (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + - (sqlite3_int64)294967296; - -#if SQLITE_OS_WINCE - SYSTEMTIME time; - osGetSystemTime(&time); - /* if SystemTimeToFileTime() fails, it returns zero. */ - if (!osSystemTimeToFileTime(&time,&ft)){ - return SQLITE_ERROR; - } -#else - osGetSystemTimeAsFileTime( &ft ); -#endif - - *piNow = winFiletimeEpoch + - ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + - (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000; - -#ifdef SQLITE_TEST - if( sqlite3_current_time ){ - *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; - } -#endif - UNUSED_PARAMETER(pVfs); - return SQLITE_OK; -} - -/* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. -*/ -static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ - int rc; - sqlite3_int64 i; - rc = winCurrentTimeInt64(pVfs, &i); - if( !rc ){ - *prNow = i/86400000.0; - } - return rc; -} - -/* -** The idea is that this function works like a combination of -** GetLastError() and FormatMessage() on Windows (or errno and -** strerror_r() on Unix). After an error is returned by an OS -** function, SQLite calls this function with zBuf pointing to -** a buffer of nBuf bytes. The OS layer should populate the -** buffer with a nul-terminated UTF-8 encoded error message -** describing the last IO error to have occurred within the calling -** thread. -** -** If the error message is too large for the supplied buffer, -** it should be truncated. The return value of xGetLastError -** is zero if the error message fits in the buffer, or non-zero -** otherwise (if the message was truncated). If non-zero is returned, -** then it is not necessary to include the nul-terminator character -** in the output buffer. -** -** Not supplying an error message will have no adverse effect -** on SQLite. It is fine to have an implementation that never -** returns an error message: -** -** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ -** assert(zBuf[0]=='\0'); -** return 0; -** } -** -** However if an error message is supplied, it will be incorporated -** by sqlite into the error message available to the user using -** sqlite3_errmsg(), possibly making IO errors easier to debug. -*/ -static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ - UNUSED_PARAMETER(pVfs); - return getLastErrorMsg(osGetLastError(), nBuf, zBuf); -} - -/* -** Initialize and deinitialize the operating system interface. -*/ -SQLITE_API int sqlite3_os_init(void){ - static sqlite3_vfs winVfs = { - 3, /* iVersion */ - sizeof(winFile), /* szOsFile */ - MAX_PATH, /* mxPathname */ - 0, /* pNext */ - "win32", /* zName */ - 0, /* pAppData */ - winOpen, /* xOpen */ - winDelete, /* xDelete */ - winAccess, /* xAccess */ - winFullPathname, /* xFullPathname */ - winDlOpen, /* xDlOpen */ - winDlError, /* xDlError */ - winDlSym, /* xDlSym */ - winDlClose, /* xDlClose */ - winRandomness, /* xRandomness */ - winSleep, /* xSleep */ - winCurrentTime, /* xCurrentTime */ - winGetLastError, /* xGetLastError */ - winCurrentTimeInt64, /* xCurrentTimeInt64 */ - winSetSystemCall, /* xSetSystemCall */ - winGetSystemCall, /* xGetSystemCall */ - winNextSystemCall, /* xNextSystemCall */ - }; - - /* Double-check that the aSyscall[] array has been constructed - ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==74 ); - -#ifndef SQLITE_OMIT_WAL - /* get memory map allocation granularity */ - memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); -#if SQLITE_OS_WINRT - osGetNativeSystemInfo(&winSysInfo); -#else - osGetSystemInfo(&winSysInfo); -#endif - assert(winSysInfo.dwAllocationGranularity > 0); -#endif - - sqlite3_vfs_register(&winVfs, 1); - return SQLITE_OK; -} - -SQLITE_API int sqlite3_os_end(void){ -#if SQLITE_OS_WINRT - if( sleepObj!=NULL ){ - osCloseHandle(sleepObj); - sleepObj = NULL; - } -#endif - return SQLITE_OK; -} - -#endif /* SQLITE_OS_WIN */ - -/************** End of os_win.c **********************************************/ -/************** Begin file bitvec.c ******************************************/ -/* -** 2008 February 16 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file implements an object that represents a fixed-length -** bitmap. Bits are numbered starting with 1. -** -** A bitmap is used to record which pages of a database file have been -** journalled during a transaction, or which pages have the "dont-write" -** property. Usually only a few pages are meet either condition. -** So the bitmap is usually sparse and has low cardinality. -** But sometimes (for example when during a DROP of a large table) most -** or all of the pages in a database can get journalled. In those cases, -** the bitmap becomes dense with high cardinality. The algorithm needs -** to handle both cases well. -** -** The size of the bitmap is fixed when the object is created. -** -** All bits are clear when the bitmap is created. Individual bits -** may be set or cleared one at a time. -** -** Test operations are about 100 times more common that set operations. -** Clear operations are exceedingly rare. There are usually between -** 5 and 500 set operations per Bitvec object, though the number of sets can -** sometimes grow into tens of thousands or larger. The size of the -** Bitvec object is the number of pages in the database file at the -** start of a transaction, and is thus usually less than a few thousand, -** but can be as large as 2 billion for a really big database. -*/ - -/* Size of the Bitvec structure in bytes. */ -#define BITVEC_SZ 512 - -/* Round the union size down to the nearest pointer boundary, since that's how -** it will be aligned within the Bitvec struct. */ -#define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*)) - -/* Type of the array "element" for the bitmap representation. -** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE. -** Setting this to the "natural word" size of your CPU may improve -** performance. */ -#define BITVEC_TELEM u8 -/* Size, in bits, of the bitmap element. */ -#define BITVEC_SZELEM 8 -/* Number of elements in a bitmap array. */ -#define BITVEC_NELEM (BITVEC_USIZE/sizeof(BITVEC_TELEM)) -/* Number of bits in the bitmap array. */ -#define BITVEC_NBIT (BITVEC_NELEM*BITVEC_SZELEM) - -/* Number of u32 values in hash table. */ -#define BITVEC_NINT (BITVEC_USIZE/sizeof(u32)) -/* Maximum number of entries in hash table before -** sub-dividing and re-hashing. */ -#define BITVEC_MXHASH (BITVEC_NINT/2) -/* Hashing function for the aHash representation. -** Empirical testing showed that the *37 multiplier -** (an arbitrary prime)in the hash function provided -** no fewer collisions than the no-op *1. */ -#define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT) - -#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *)) - - -/* -** A bitmap is an instance of the following structure. -** -** This bitmap records the existence of zero or more bits -** with values between 1 and iSize, inclusive. -** -** There are three possible representations of the bitmap. -** If iSize<=BITVEC_NBIT, then Bitvec.u.aBitmap[] is a straight -** bitmap. The least significant bit is bit 1. -** -** If iSize>BITVEC_NBIT and iDivisor==0 then Bitvec.u.aHash[] is -** a hash table that will hold up to BITVEC_MXHASH distinct values. -** -** Otherwise, the value i is redirected into one of BITVEC_NPTR -** sub-bitmaps pointed to by Bitvec.u.apSub[]. Each subbitmap -** handles up to iDivisor separate values of i. apSub[0] holds -** values between 1 and iDivisor. apSub[1] holds values between -** iDivisor+1 and 2*iDivisor. apSub[N] holds values between -** N*iDivisor+1 and (N+1)*iDivisor. Each subbitmap is normalized -** to hold deal with values between 1 and iDivisor. -*/ -struct Bitvec { - u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */ - u32 nSet; /* Number of bits that are set - only valid for aHash - ** element. Max is BITVEC_NINT. For BITVEC_SZ of 512, - ** this would be 125. */ - u32 iDivisor; /* Number of bits handled by each apSub[] entry. */ - /* Should >=0 for apSub element. */ - /* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */ - /* For a BITVEC_SZ of 512, this would be 34,359,739. */ - union { - BITVEC_TELEM aBitmap[BITVEC_NELEM]; /* Bitmap representation */ - u32 aHash[BITVEC_NINT]; /* Hash table representation */ - Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */ - } u; -}; - -/* -** Create a new bitmap object able to handle bits between 0 and iSize, -** inclusive. Return a pointer to the new object. Return NULL if -** malloc fails. -*/ -SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32 iSize){ - Bitvec *p; - assert( sizeof(*p)==BITVEC_SZ ); - p = sqlite3MallocZero( sizeof(*p) ); - if( p ){ - p->iSize = iSize; - } - return p; -} - -/* -** Check to see if the i-th bit is set. Return true or false. -** If p is NULL (if the bitmap has not been created) or if -** i is out of range, then return false. -*/ -SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec *p, u32 i){ - if( p==0 ) return 0; - if( i>p->iSize || i==0 ) return 0; - i--; - while( p->iDivisor ){ - u32 bin = i/p->iDivisor; - i = i%p->iDivisor; - p = p->u.apSub[bin]; - if (!p) { - return 0; - } - } - if( p->iSize<=BITVEC_NBIT ){ - return (p->u.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))))!=0; - } else{ - u32 h = BITVEC_HASH(i++); - while( p->u.aHash[h] ){ - if( p->u.aHash[h]==i ) return 1; - h = (h+1) % BITVEC_NINT; - } - return 0; - } -} - -/* -** Set the i-th bit. Return 0 on success and an error code if -** anything goes wrong. -** -** This routine might cause sub-bitmaps to be allocated. Failing -** to get the memory needed to hold the sub-bitmap is the only -** that can go wrong with an insert, assuming p and i are valid. -** -** The calling function must ensure that p is a valid Bitvec object -** and that the value for "i" is within range of the Bitvec object. -** Otherwise the behavior is undefined. -*/ -SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){ - u32 h; - if( p==0 ) return SQLITE_OK; - assert( i>0 ); - assert( i<=p->iSize ); - i--; - while((p->iSize > BITVEC_NBIT) && p->iDivisor) { - u32 bin = i/p->iDivisor; - i = i%p->iDivisor; - if( p->u.apSub[bin]==0 ){ - p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor ); - if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM; - } - p = p->u.apSub[bin]; - } - if( p->iSize<=BITVEC_NBIT ){ - p->u.aBitmap[i/BITVEC_SZELEM] |= 1 << (i&(BITVEC_SZELEM-1)); - return SQLITE_OK; - } - h = BITVEC_HASH(i++); - /* if there wasn't a hash collision, and this doesn't */ - /* completely fill the hash, then just add it without */ - /* worring about sub-dividing and re-hashing. */ - if( !p->u.aHash[h] ){ - if (p->nSet<(BITVEC_NINT-1)) { - goto bitvec_set_end; - } else { - goto bitvec_set_rehash; - } - } - /* there was a collision, check to see if it's already */ - /* in hash, if not, try to find a spot for it */ - do { - if( p->u.aHash[h]==i ) return SQLITE_OK; - h++; - if( h>=BITVEC_NINT ) h = 0; - } while( p->u.aHash[h] ); - /* we didn't find it in the hash. h points to the first */ - /* available free spot. check to see if this is going to */ - /* make our hash too "full". */ -bitvec_set_rehash: - if( p->nSet>=BITVEC_MXHASH ){ - unsigned int j; - int rc; - u32 *aiValues = sqlite3StackAllocRaw(0, sizeof(p->u.aHash)); - if( aiValues==0 ){ - return SQLITE_NOMEM; - }else{ - memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); - memset(p->u.apSub, 0, sizeof(p->u.apSub)); - p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR; - rc = sqlite3BitvecSet(p, i); - for(j=0; jnSet++; - p->u.aHash[h] = i; - return SQLITE_OK; -} - -/* -** Clear the i-th bit. -** -** pBuf must be a pointer to at least BITVEC_SZ bytes of temporary storage -** that BitvecClear can use to rebuilt its hash table. -*/ -SQLITE_PRIVATE void sqlite3BitvecClear(Bitvec *p, u32 i, void *pBuf){ - if( p==0 ) return; - assert( i>0 ); - i--; - while( p->iDivisor ){ - u32 bin = i/p->iDivisor; - i = i%p->iDivisor; - p = p->u.apSub[bin]; - if (!p) { - return; - } - } - if( p->iSize<=BITVEC_NBIT ){ - p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1))); - }else{ - unsigned int j; - u32 *aiValues = pBuf; - memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); - memset(p->u.aHash, 0, sizeof(p->u.aHash)); - p->nSet = 0; - for(j=0; jnSet++; - while( p->u.aHash[h] ){ - h++; - if( h>=BITVEC_NINT ) h = 0; - } - p->u.aHash[h] = aiValues[j]; - } - } - } -} - -/* -** Destroy a bitmap object. Reclaim all memory used. -*/ -SQLITE_PRIVATE void sqlite3BitvecDestroy(Bitvec *p){ - if( p==0 ) return; - if( p->iDivisor ){ - unsigned int i; - for(i=0; iu.apSub[i]); - } - } - sqlite3_free(p); -} - -/* -** Return the value of the iSize parameter specified when Bitvec *p -** was created. -*/ -SQLITE_PRIVATE u32 sqlite3BitvecSize(Bitvec *p){ - return p->iSize; -} - -#ifndef SQLITE_OMIT_BUILTIN_TEST -/* -** Let V[] be an array of unsigned characters sufficient to hold -** up to N bits. Let I be an integer between 0 and N. 0<=I>3] |= (1<<(I&7)) -#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7)) -#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0 - -/* -** This routine runs an extensive test of the Bitvec code. -** -** The input is an array of integers that acts as a program -** to test the Bitvec. The integers are opcodes followed -** by 0, 1, or 3 operands, depending on the opcode. Another -** opcode follows immediately after the last operand. -** -** There are 6 opcodes numbered from 0 through 5. 0 is the -** "halt" opcode and causes the test to end. -** -** 0 Halt and return the number of errors -** 1 N S X Set N bits beginning with S and incrementing by X -** 2 N S X Clear N bits beginning with S and incrementing by X -** 3 N Set N randomly chosen bits -** 4 N Clear N randomly chosen bits -** 5 N S X Set N bits from S increment X in array only, not in bitvec -** -** The opcodes 1 through 4 perform set and clear operations are performed -** on both a Bitvec object and on a linear array of bits obtained from malloc. -** Opcode 5 works on the linear array only, not on the Bitvec. -** Opcode 5 is used to deliberately induce a fault in order to -** confirm that error detection works. -** -** At the conclusion of the test the linear array is compared -** against the Bitvec object. If there are any differences, -** an error is returned. If they are the same, zero is returned. -** -** If a memory allocation error occurs, return -1. -*/ -SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){ - Bitvec *pBitvec = 0; - unsigned char *pV = 0; - int rc = -1; - int i, nx, pc, op; - void *pTmpSpace; - - /* Allocate the Bitvec to be tested and a linear array of - ** bits to act as the reference */ - pBitvec = sqlite3BitvecCreate( sz ); - pV = sqlite3MallocZero( (sz+7)/8 + 1 ); - pTmpSpace = sqlite3_malloc(BITVEC_SZ); - if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; - - /* NULL pBitvec tests */ - sqlite3BitvecSet(0, 1); - sqlite3BitvecClear(0, 1, pTmpSpace); - - /* Run the program */ - pc = 0; - while( (op = aOp[pc])!=0 ){ - switch( op ){ - case 1: - case 2: - case 5: { - nx = 4; - i = aOp[pc+2] - 1; - aOp[pc+2] += aOp[pc+3]; - break; - } - case 3: - case 4: - default: { - nx = 2; - sqlite3_randomness(sizeof(i), &i); - break; - } - } - if( (--aOp[pc+1]) > 0 ) nx = 0; - pc += nx; - i = (i & 0x7fffffff)%sz; - if( (op & 1)!=0 ){ - SETBIT(pV, (i+1)); - if( op!=5 ){ - if( sqlite3BitvecSet(pBitvec, i+1) ) goto bitvec_end; - } - }else{ - CLEARBIT(pV, (i+1)); - sqlite3BitvecClear(pBitvec, i+1, pTmpSpace); - } - } - - /* Test to make sure the linear array exactly matches the - ** Bitvec object. Start with the assumption that they do - ** match (rc==0). Change rc to non-zero if a discrepancy - ** is found. - */ - rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1) - + sqlite3BitvecTest(pBitvec, 0) - + (sqlite3BitvecSize(pBitvec) - sz); - for(i=1; i<=sz; i++){ - if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){ - rc = i; - break; - } - } - - /* Free allocated structure */ -bitvec_end: - sqlite3_free(pTmpSpace); - sqlite3_free(pV); - sqlite3BitvecDestroy(pBitvec); - return rc; -} -#endif /* SQLITE_OMIT_BUILTIN_TEST */ - -/************** End of bitvec.c **********************************************/ -/************** Begin file pcache.c ******************************************/ -/* -** 2008 August 05 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file implements that page cache. -*/ - -/* -** A complete page cache is an instance of this structure. -*/ -struct PCache { - PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ - PgHdr *pSynced; /* Last synced page in dirty page list */ - int nRef; /* Number of referenced pages */ - int szCache; /* Configured cache size */ - int szPage; /* Size of every page in this cache */ - int szExtra; /* Size of extra space for each page */ - int bPurgeable; /* True if pages are on backing store */ - int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */ - void *pStress; /* Argument to xStress */ - sqlite3_pcache *pCache; /* Pluggable cache module */ - PgHdr *pPage1; /* Reference to page 1 */ -}; - -/* -** Some of the assert() macros in this code are too expensive to run -** even during normal debugging. Use them only rarely on long-running -** tests. Enable the expensive asserts using the -** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option. -*/ -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT -# define expensive_assert(X) assert(X) -#else -# define expensive_assert(X) -#endif - -/********************************** Linked List Management ********************/ - -#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT) -/* -** Check that the pCache->pSynced variable is set correctly. If it -** is not, either fail an assert or return zero. Otherwise, return -** non-zero. This is only used in debugging builds, as follows: -** -** expensive_assert( pcacheCheckSynced(pCache) ); -*/ -static int pcacheCheckSynced(PCache *pCache){ - PgHdr *p; - for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pDirtyPrev){ - assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) ); - } - return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0); -} -#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */ - -/* -** Remove page pPage from the list of dirty pages. -*/ -static void pcacheRemoveFromDirtyList(PgHdr *pPage){ - PCache *p = pPage->pCache; - - assert( pPage->pDirtyNext || pPage==p->pDirtyTail ); - assert( pPage->pDirtyPrev || pPage==p->pDirty ); - - /* Update the PCache1.pSynced variable if necessary. */ - if( p->pSynced==pPage ){ - PgHdr *pSynced = pPage->pDirtyPrev; - while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){ - pSynced = pSynced->pDirtyPrev; - } - p->pSynced = pSynced; - } - - if( pPage->pDirtyNext ){ - pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev; - }else{ - assert( pPage==p->pDirtyTail ); - p->pDirtyTail = pPage->pDirtyPrev; - } - if( pPage->pDirtyPrev ){ - pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; - }else{ - assert( pPage==p->pDirty ); - p->pDirty = pPage->pDirtyNext; - } - pPage->pDirtyNext = 0; - pPage->pDirtyPrev = 0; - - expensive_assert( pcacheCheckSynced(p) ); -} - -/* -** Add page pPage to the head of the dirty list (PCache1.pDirty is set to -** pPage). -*/ -static void pcacheAddToDirtyList(PgHdr *pPage){ - PCache *p = pPage->pCache; - - assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage ); - - pPage->pDirtyNext = p->pDirty; - if( pPage->pDirtyNext ){ - assert( pPage->pDirtyNext->pDirtyPrev==0 ); - pPage->pDirtyNext->pDirtyPrev = pPage; - } - p->pDirty = pPage; - if( !p->pDirtyTail ){ - p->pDirtyTail = pPage; - } - if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){ - p->pSynced = pPage; - } - expensive_assert( pcacheCheckSynced(p) ); -} - -/* -** Wrapper around the pluggable caches xUnpin method. If the cache is -** being used for an in-memory database, this function is a no-op. -*/ -static void pcacheUnpin(PgHdr *p){ - PCache *pCache = p->pCache; - if( pCache->bPurgeable ){ - if( p->pgno==1 ){ - pCache->pPage1 = 0; - } - sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 0); - } -} - -/*************************************************** General Interfaces ****** -** -** Initialize and shutdown the page cache subsystem. Neither of these -** functions are threadsafe. -*/ -SQLITE_PRIVATE int sqlite3PcacheInitialize(void){ - if( sqlite3GlobalConfig.pcache2.xInit==0 ){ - /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the - ** built-in default page cache is used instead of the application defined - ** page cache. */ - sqlite3PCacheSetDefault(); - } - return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg); -} -SQLITE_PRIVATE void sqlite3PcacheShutdown(void){ - if( sqlite3GlobalConfig.pcache2.xShutdown ){ - /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ - sqlite3GlobalConfig.pcache2.xShutdown(sqlite3GlobalConfig.pcache2.pArg); - } -} - -/* -** Return the size in bytes of a PCache object. -*/ -SQLITE_PRIVATE int sqlite3PcacheSize(void){ return sizeof(PCache); } - -/* -** Create a new PCache object. Storage space to hold the object -** has already been allocated and is passed in as the p pointer. -** The caller discovers how much space needs to be allocated by -** calling sqlite3PcacheSize(). -*/ -SQLITE_PRIVATE void sqlite3PcacheOpen( - int szPage, /* Size of every page */ - int szExtra, /* Extra space associated with each page */ - int bPurgeable, /* True if pages are on backing store */ - int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */ - void *pStress, /* Argument to xStress */ - PCache *p /* Preallocated space for the PCache */ -){ - memset(p, 0, sizeof(PCache)); - p->szPage = szPage; - p->szExtra = szExtra; - p->bPurgeable = bPurgeable; - p->xStress = xStress; - p->pStress = pStress; - p->szCache = 100; -} - -/* -** Change the page size for PCache object. The caller must ensure that there -** are no outstanding page references when this function is called. -*/ -SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ - assert( pCache->nRef==0 && pCache->pDirty==0 ); - if( pCache->pCache ){ - sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); - pCache->pCache = 0; - pCache->pPage1 = 0; - } - pCache->szPage = szPage; -} - -/* -** Compute the number of pages of cache requested. -*/ -static int numberOfCachePages(PCache *p){ - if( p->szCache>=0 ){ - return p->szCache; - }else{ - return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); - } -} - -/* -** Try to obtain a page from the cache. -*/ -SQLITE_PRIVATE int sqlite3PcacheFetch( - PCache *pCache, /* Obtain the page from this cache */ - Pgno pgno, /* Page number to obtain */ - int createFlag, /* If true, create page if it does not exist already */ - PgHdr **ppPage /* Write the page here */ -){ - sqlite3_pcache_page *pPage = 0; - PgHdr *pPgHdr = 0; - int eCreate; - - assert( pCache!=0 ); - assert( createFlag==1 || createFlag==0 ); - assert( pgno>0 ); - - /* If the pluggable cache (sqlite3_pcache*) has not been allocated, - ** allocate it now. - */ - if( !pCache->pCache && createFlag ){ - sqlite3_pcache *p; - p = sqlite3GlobalConfig.pcache2.xCreate( - pCache->szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable - ); - if( !p ){ - return SQLITE_NOMEM; - } - sqlite3GlobalConfig.pcache2.xCachesize(p, numberOfCachePages(pCache)); - pCache->pCache = p; - } - - eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty)); - if( pCache->pCache ){ - pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); - } - - if( !pPage && eCreate==1 ){ - PgHdr *pPg; - - /* Find a dirty page to write-out and recycle. First try to find a - ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC - ** cleared), but if that is not possible settle for any other - ** unreferenced dirty page. - */ - expensive_assert( pcacheCheckSynced(pCache) ); - for(pPg=pCache->pSynced; - pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); - pPg=pPg->pDirtyPrev - ); - pCache->pSynced = pPg; - if( !pPg ){ - for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev); - } - if( pPg ){ - int rc; -#ifdef SQLITE_LOG_CACHE_SPILL - sqlite3_log(SQLITE_FULL, - "spill page %d making room for %d - cache used: %d/%d", - pPg->pgno, pgno, - sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache), - numberOfCachePages(pCache)); -#endif - rc = pCache->xStress(pCache->pStress, pPg); - if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ - return rc; - } - } - - pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2); - } - - if( pPage ){ - pPgHdr = (PgHdr *)pPage->pExtra; - - if( !pPgHdr->pPage ){ - memset(pPgHdr, 0, sizeof(PgHdr)); - pPgHdr->pPage = pPage; - pPgHdr->pData = pPage->pBuf; - pPgHdr->pExtra = (void *)&pPgHdr[1]; - memset(pPgHdr->pExtra, 0, pCache->szExtra); - pPgHdr->pCache = pCache; - pPgHdr->pgno = pgno; - } - assert( pPgHdr->pCache==pCache ); - assert( pPgHdr->pgno==pgno ); - assert( pPgHdr->pData==pPage->pBuf ); - assert( pPgHdr->pExtra==(void *)&pPgHdr[1] ); - - if( 0==pPgHdr->nRef ){ - pCache->nRef++; - } - pPgHdr->nRef++; - if( pgno==1 ){ - pCache->pPage1 = pPgHdr; - } - } - *ppPage = pPgHdr; - return (pPgHdr==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK; -} - -/* -** Decrement the reference count on a page. If the page is clean and the -** reference count drops to 0, then it is made elible for recycling. -*/ -SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr *p){ - assert( p->nRef>0 ); - p->nRef--; - if( p->nRef==0 ){ - PCache *pCache = p->pCache; - pCache->nRef--; - if( (p->flags&PGHDR_DIRTY)==0 ){ - pcacheUnpin(p); - }else{ - /* Move the page to the head of the dirty list. */ - pcacheRemoveFromDirtyList(p); - pcacheAddToDirtyList(p); - } - } -} - -/* -** Increase the reference count of a supplied page by 1. -*/ -SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr *p){ - assert(p->nRef>0); - p->nRef++; -} - -/* -** Drop a page from the cache. There must be exactly one reference to the -** page. This function deletes that reference, so after it returns the -** page pointed to by p is invalid. -*/ -SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr *p){ - PCache *pCache; - assert( p->nRef==1 ); - if( p->flags&PGHDR_DIRTY ){ - pcacheRemoveFromDirtyList(p); - } - pCache = p->pCache; - pCache->nRef--; - if( p->pgno==1 ){ - pCache->pPage1 = 0; - } - sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 1); -} - -/* -** Make sure the page is marked as dirty. If it isn't dirty already, -** make it so. -*/ -SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){ - p->flags &= ~PGHDR_DONT_WRITE; - assert( p->nRef>0 ); - if( 0==(p->flags & PGHDR_DIRTY) ){ - p->flags |= PGHDR_DIRTY; - pcacheAddToDirtyList( p); - } -} - -/* -** Make sure the page is marked as clean. If it isn't clean already, -** make it so. -*/ -SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){ - if( (p->flags & PGHDR_DIRTY) ){ - pcacheRemoveFromDirtyList(p); - p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC); - if( p->nRef==0 ){ - pcacheUnpin(p); - } - } -} - -/* -** Make every page in the cache clean. -*/ -SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache *pCache){ - PgHdr *p; - while( (p = pCache->pDirty)!=0 ){ - sqlite3PcacheMakeClean(p); - } -} - -/* -** Clear the PGHDR_NEED_SYNC flag from all dirty pages. -*/ -SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){ - PgHdr *p; - for(p=pCache->pDirty; p; p=p->pDirtyNext){ - p->flags &= ~PGHDR_NEED_SYNC; - } - pCache->pSynced = pCache->pDirtyTail; -} - -/* -** Change the page number of page p to newPgno. -*/ -SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ - PCache *pCache = p->pCache; - assert( p->nRef>0 ); - assert( newPgno>0 ); - sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); - p->pgno = newPgno; - if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ - pcacheRemoveFromDirtyList(p); - pcacheAddToDirtyList(p); - } -} - -/* -** Drop every cache entry whose page number is greater than "pgno". The -** caller must ensure that there are no outstanding references to any pages -** other than page 1 with a page number greater than pgno. -** -** If there is a reference to page 1 and the pgno parameter passed to this -** function is 0, then the data area associated with page 1 is zeroed, but -** the page object is not dropped. -*/ -SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ - if( pCache->pCache ){ - PgHdr *p; - PgHdr *pNext; - for(p=pCache->pDirty; p; p=pNext){ - pNext = p->pDirtyNext; - /* This routine never gets call with a positive pgno except right - ** after sqlite3PcacheCleanAll(). So if there are dirty pages, - ** it must be that pgno==0. - */ - assert( p->pgno>0 ); - if( ALWAYS(p->pgno>pgno) ){ - assert( p->flags&PGHDR_DIRTY ); - sqlite3PcacheMakeClean(p); - } - } - if( pgno==0 && pCache->pPage1 ){ - memset(pCache->pPage1->pData, 0, pCache->szPage); - pgno = 1; - } - sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1); - } -} - -/* -** Close a cache. -*/ -SQLITE_PRIVATE void sqlite3PcacheClose(PCache *pCache){ - if( pCache->pCache ){ - sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); - } -} - -/* -** Discard the contents of the cache. -*/ -SQLITE_PRIVATE void sqlite3PcacheClear(PCache *pCache){ - sqlite3PcacheTruncate(pCache, 0); -} - -/* -** Merge two lists of pages connected by pDirty and in pgno order. -** Do not both fixing the pDirtyPrev pointers. -*/ -static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){ - PgHdr result, *pTail; - pTail = &result; - while( pA && pB ){ - if( pA->pgnopgno ){ - pTail->pDirty = pA; - pTail = pA; - pA = pA->pDirty; - }else{ - pTail->pDirty = pB; - pTail = pB; - pB = pB->pDirty; - } - } - if( pA ){ - pTail->pDirty = pA; - }else if( pB ){ - pTail->pDirty = pB; - }else{ - pTail->pDirty = 0; - } - return result.pDirty; -} - -/* -** Sort the list of pages in accending order by pgno. Pages are -** connected by pDirty pointers. The pDirtyPrev pointers are -** corrupted by this sort. -** -** Since there cannot be more than 2^31 distinct pages in a database, -** there cannot be more than 31 buckets required by the merge sorter. -** One extra bucket is added to catch overflow in case something -** ever changes to make the previous sentence incorrect. -*/ -#define N_SORT_BUCKET 32 -static PgHdr *pcacheSortDirtyList(PgHdr *pIn){ - PgHdr *a[N_SORT_BUCKET], *p; - int i; - memset(a, 0, sizeof(a)); - while( pIn ){ - p = pIn; - pIn = p->pDirty; - p->pDirty = 0; - for(i=0; ALWAYS(ipDirty; p; p=p->pDirtyNext){ - p->pDirty = p->pDirtyNext; - } - return pcacheSortDirtyList(pCache->pDirty); -} - -/* -** Return the total number of referenced pages held by the cache. -*/ -SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){ - return pCache->nRef; -} - -/* -** Return the number of references to the page supplied as an argument. -*/ -SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr *p){ - return p->nRef; -} - -/* -** Return the total number of pages in the cache. -*/ -SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){ - int nPage = 0; - if( pCache->pCache ){ - nPage = sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache); - } - return nPage; -} - -#ifdef SQLITE_TEST -/* -** Get the suggested cache-size value. -*/ -SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *pCache){ - return numberOfCachePages(pCache); -} -#endif - -/* -** Set the suggested cache-size value. -*/ -SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){ - pCache->szCache = mxPage; - if( pCache->pCache ){ - sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache, - numberOfCachePages(pCache)); - } -} - -/* -** Free up as much memory as possible from the page cache. -*/ -SQLITE_PRIVATE void sqlite3PcacheShrink(PCache *pCache){ - if( pCache->pCache ){ - sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache); - } -} - -#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) -/* -** For all dirty pages currently in the cache, invoke the specified -** callback. This is only used if the SQLITE_CHECK_PAGES macro is -** defined. -*/ -SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){ - PgHdr *pDirty; - for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){ - xIter(pDirty); - } -} -#endif - -/************** End of pcache.c **********************************************/ -/************** Begin file pcache1.c *****************************************/ -/* -** 2008 November 05 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file implements the default page cache implementation (the -** sqlite3_pcache interface). It also contains part of the implementation -** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features. -** If the default page cache implementation is overriden, then neither of -** these two features are available. -*/ - - -typedef struct PCache1 PCache1; -typedef struct PgHdr1 PgHdr1; -typedef struct PgFreeslot PgFreeslot; -typedef struct PGroup PGroup; - -/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set -** of one or more PCaches that are able to recycle each others unpinned -** pages when they are under memory pressure. A PGroup is an instance of -** the following object. -** -** This page cache implementation works in one of two modes: -** -** (1) Every PCache is the sole member of its own PGroup. There is -** one PGroup per PCache. -** -** (2) There is a single global PGroup that all PCaches are a member -** of. -** -** Mode 1 uses more memory (since PCache instances are not able to rob -** unused pages from other PCaches) but it also operates without a mutex, -** and is therefore often faster. Mode 2 requires a mutex in order to be -** threadsafe, but recycles pages more efficiently. -** -** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single -** PGroup which is the pcache1.grp global variable and its mutex is -** SQLITE_MUTEX_STATIC_LRU. -*/ -struct PGroup { - sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ - unsigned int nMaxPage; /* Sum of nMax for purgeable caches */ - unsigned int nMinPage; /* Sum of nMin for purgeable caches */ - unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */ - unsigned int nCurrentPage; /* Number of purgeable pages allocated */ - PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */ -}; - -/* Each page cache is an instance of the following object. Every -** open database file (including each in-memory database and each -** temporary or transient database) has a single page cache which -** is an instance of this object. -** -** Pointers to structures of this type are cast and returned as -** opaque sqlite3_pcache* handles. -*/ -struct PCache1 { - /* Cache configuration parameters. Page size (szPage) and the purgeable - ** flag (bPurgeable) are set when the cache is created. nMax may be - ** modified at any time by a call to the pcache1Cachesize() method. - ** The PGroup mutex must be held when accessing nMax. - */ - PGroup *pGroup; /* PGroup this cache belongs to */ - int szPage; /* Size of allocated pages in bytes */ - int szExtra; /* Size of extra space in bytes */ - int bPurgeable; /* True if cache is purgeable */ - unsigned int nMin; /* Minimum number of pages reserved */ - unsigned int nMax; /* Configured "cache_size" value */ - unsigned int n90pct; /* nMax*9/10 */ - unsigned int iMaxKey; /* Largest key seen since xTruncate() */ - - /* Hash table of all pages. The following variables may only be accessed - ** when the accessor is holding the PGroup mutex. - */ - unsigned int nRecyclable; /* Number of pages in the LRU list */ - unsigned int nPage; /* Total number of pages in apHash */ - unsigned int nHash; /* Number of slots in apHash[] */ - PgHdr1 **apHash; /* Hash table for fast lookup by key */ -}; - -/* -** Each cache entry is represented by an instance of the following -** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of -** PgHdr1.pCache->szPage bytes is allocated directly before this structure -** in memory. -*/ -struct PgHdr1 { - sqlite3_pcache_page page; - unsigned int iKey; /* Key value (page number) */ - PgHdr1 *pNext; /* Next in hash table chain */ - PCache1 *pCache; /* Cache that currently owns this page */ - PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ - PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ -}; - -/* -** Free slots in the allocator used to divide up the buffer provided using -** the SQLITE_CONFIG_PAGECACHE mechanism. -*/ -struct PgFreeslot { - PgFreeslot *pNext; /* Next free slot */ -}; - -/* -** Global data used by this cache. -*/ -static SQLITE_WSD struct PCacheGlobal { - PGroup grp; /* The global PGroup for mode (2) */ - - /* Variables related to SQLITE_CONFIG_PAGECACHE settings. The - ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all - ** fixed at sqlite3_initialize() time and do not require mutex protection. - ** The nFreeSlot and pFree values do require mutex protection. - */ - int isInit; /* True if initialized */ - int szSlot; /* Size of each free slot */ - int nSlot; /* The number of pcache slots */ - int nReserve; /* Try to keep nFreeSlot above this */ - void *pStart, *pEnd; /* Bounds of pagecache malloc range */ - /* Above requires no mutex. Use mutex below for variable that follow. */ - sqlite3_mutex *mutex; /* Mutex for accessing the following: */ - PgFreeslot *pFree; /* Free page blocks */ - int nFreeSlot; /* Number of unused pcache slots */ - /* The following value requires a mutex to change. We skip the mutex on - ** reading because (1) most platforms read a 32-bit integer atomically and - ** (2) even if an incorrect value is read, no great harm is done since this - ** is really just an optimization. */ - int bUnderPressure; /* True if low on PAGECACHE memory */ -} pcache1_g; - -/* -** All code in this file should access the global structure above via the -** alias "pcache1". This ensures that the WSD emulation is used when -** compiling for systems that do not support real WSD. -*/ -#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g)) - -/* -** Macros to enter and leave the PCache LRU mutex. -*/ -#define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex) -#define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex) - -/******************************************************************************/ -/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/ - -/* -** This function is called during initialization if a static buffer is -** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE -** verb to sqlite3_config(). Parameter pBuf points to an allocation large -** enough to contain 'n' buffers of 'sz' bytes each. -** -** This routine is called from sqlite3_initialize() and so it is guaranteed -** to be serialized already. There is no need for further mutexing. -*/ -SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ - if( pcache1.isInit ){ - PgFreeslot *p; - sz = ROUNDDOWN8(sz); - pcache1.szSlot = sz; - pcache1.nSlot = pcache1.nFreeSlot = n; - pcache1.nReserve = n>90 ? 10 : (n/10 + 1); - pcache1.pStart = pBuf; - pcache1.pFree = 0; - pcache1.bUnderPressure = 0; - while( n-- ){ - p = (PgFreeslot*)pBuf; - p->pNext = pcache1.pFree; - pcache1.pFree = p; - pBuf = (void*)&((char*)pBuf)[sz]; - } - pcache1.pEnd = pBuf; - } -} - -/* -** Malloc function used within this file to allocate space from the buffer -** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no -** such buffer exists or there is no space left in it, this function falls -** back to sqlite3Malloc(). -** -** Multiple threads can run this routine at the same time. Global variables -** in pcache1 need to be protected via mutex. -*/ -static void *pcache1Alloc(int nByte){ - void *p = 0; - assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); - sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); - if( nByte<=pcache1.szSlot ){ - sqlite3_mutex_enter(pcache1.mutex); - p = (PgHdr1 *)pcache1.pFree; - if( p ){ - pcache1.pFree = pcache1.pFree->pNext; - pcache1.nFreeSlot--; - pcache1.bUnderPressure = pcache1.nFreeSlot=0 ); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); - } - sqlite3_mutex_leave(pcache1.mutex); - } - if( p==0 ){ - /* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get - ** it from sqlite3Malloc instead. - */ - p = sqlite3Malloc(nByte); -#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS - if( p ){ - int sz = sqlite3MallocSize(p); - sqlite3_mutex_enter(pcache1.mutex); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); - sqlite3_mutex_leave(pcache1.mutex); - } -#endif - sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); - } - return p; -} - -/* -** Free an allocated buffer obtained from pcache1Alloc(). -*/ -static int pcache1Free(void *p){ - int nFreed = 0; - if( p==0 ) return 0; - if( p>=pcache1.pStart && ppNext = pcache1.pFree; - pcache1.pFree = pSlot; - pcache1.nFreeSlot++; - pcache1.bUnderPressure = pcache1.nFreeSlot=pcache1.pStart && ppGroup->mutex) ); - pcache1LeaveMutex(pCache->pGroup); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - pPg = pcache1Alloc(pCache->szPage); - p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); - if( !pPg || !p ){ - pcache1Free(pPg); - sqlite3_free(p); - pPg = 0; - } -#else - pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra); - p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; -#endif - pcache1EnterMutex(pCache->pGroup); - - if( pPg ){ - p->page.pBuf = pPg; - p->page.pExtra = &p[1]; - if( pCache->bPurgeable ){ - pCache->pGroup->nCurrentPage++; - } - return p; - } - return 0; -} - -/* -** Free a page object allocated by pcache1AllocPage(). -** -** The pointer is allowed to be NULL, which is prudent. But it turns out -** that the current implementation happens to never call this routine -** with a NULL pointer, so we mark the NULL test with ALWAYS(). -*/ -static void pcache1FreePage(PgHdr1 *p){ - if( ALWAYS(p) ){ - PCache1 *pCache = p->pCache; - assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) ); - pcache1Free(p->page.pBuf); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - sqlite3_free(p); -#endif - if( pCache->bPurgeable ){ - pCache->pGroup->nCurrentPage--; - } - } -} - -/* -** Malloc function used by SQLite to obtain space from the buffer configured -** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer -** exists, this function falls back to sqlite3Malloc(). -*/ -SQLITE_PRIVATE void *sqlite3PageMalloc(int sz){ - return pcache1Alloc(sz); -} - -/* -** Free an allocated buffer obtained from sqlite3PageMalloc(). -*/ -SQLITE_PRIVATE void sqlite3PageFree(void *p){ - pcache1Free(p); -} - - -/* -** Return true if it desirable to avoid allocating a new page cache -** entry. -** -** If memory was allocated specifically to the page cache using -** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then -** it is desirable to avoid allocating a new page cache entry because -** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient -** for all page cache needs and we should not need to spill the -** allocation onto the heap. -** -** Or, the heap is used for all page cache memory but the heap is -** under memory pressure, then again it is desirable to avoid -** allocating a new page cache entry in order to avoid stressing -** the heap even further. -*/ -static int pcache1UnderMemoryPressure(PCache1 *pCache){ - if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ - return pcache1.bUnderPressure; - }else{ - return sqlite3HeapNearlyFull(); - } -} - -/******************************************************************************/ -/******** General Implementation Functions ************************************/ - -/* -** This function is used to resize the hash table used by the cache passed -** as the first argument. -** -** The PCache mutex must be held when this function is called. -*/ -static int pcache1ResizeHash(PCache1 *p){ - PgHdr1 **apNew; - unsigned int nNew; - unsigned int i; - - assert( sqlite3_mutex_held(p->pGroup->mutex) ); - - nNew = p->nHash*2; - if( nNew<256 ){ - nNew = 256; - } - - pcache1LeaveMutex(p->pGroup); - if( p->nHash ){ sqlite3BeginBenignMalloc(); } - apNew = (PgHdr1 **)sqlite3MallocZero(sizeof(PgHdr1 *)*nNew); - if( p->nHash ){ sqlite3EndBenignMalloc(); } - pcache1EnterMutex(p->pGroup); - if( apNew ){ - for(i=0; inHash; i++){ - PgHdr1 *pPage; - PgHdr1 *pNext = p->apHash[i]; - while( (pPage = pNext)!=0 ){ - unsigned int h = pPage->iKey % nNew; - pNext = pPage->pNext; - pPage->pNext = apNew[h]; - apNew[h] = pPage; - } - } - sqlite3_free(p->apHash); - p->apHash = apNew; - p->nHash = nNew; - } - - return (p->apHash ? SQLITE_OK : SQLITE_NOMEM); -} - -/* -** This function is used internally to remove the page pPage from the -** PGroup LRU list, if is part of it. If pPage is not part of the PGroup -** LRU list, then this function is a no-op. -** -** The PGroup mutex must be held when this function is called. -** -** If pPage is NULL then this routine is a no-op. -*/ -static void pcache1PinPage(PgHdr1 *pPage){ - PCache1 *pCache; - PGroup *pGroup; - - if( pPage==0 ) return; - pCache = pPage->pCache; - pGroup = pCache->pGroup; - assert( sqlite3_mutex_held(pGroup->mutex) ); - if( pPage->pLruNext || pPage==pGroup->pLruTail ){ - if( pPage->pLruPrev ){ - pPage->pLruPrev->pLruNext = pPage->pLruNext; - } - if( pPage->pLruNext ){ - pPage->pLruNext->pLruPrev = pPage->pLruPrev; - } - if( pGroup->pLruHead==pPage ){ - pGroup->pLruHead = pPage->pLruNext; - } - if( pGroup->pLruTail==pPage ){ - pGroup->pLruTail = pPage->pLruPrev; - } - pPage->pLruNext = 0; - pPage->pLruPrev = 0; - pPage->pCache->nRecyclable--; - } -} - - -/* -** Remove the page supplied as an argument from the hash table -** (PCache1.apHash structure) that it is currently stored in. -** -** The PGroup mutex must be held when this function is called. -*/ -static void pcache1RemoveFromHash(PgHdr1 *pPage){ - unsigned int h; - PCache1 *pCache = pPage->pCache; - PgHdr1 **pp; - - assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); - h = pPage->iKey % pCache->nHash; - for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext); - *pp = (*pp)->pNext; - - pCache->nPage--; -} - -/* -** If there are currently more than nMaxPage pages allocated, try -** to recycle pages to reduce the number allocated to nMaxPage. -*/ -static void pcache1EnforceMaxPage(PGroup *pGroup){ - assert( sqlite3_mutex_held(pGroup->mutex) ); - while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){ - PgHdr1 *p = pGroup->pLruTail; - assert( p->pCache->pGroup==pGroup ); - pcache1PinPage(p); - pcache1RemoveFromHash(p); - pcache1FreePage(p); - } -} - -/* -** Discard all pages from cache pCache with a page number (key value) -** greater than or equal to iLimit. Any pinned pages that meet this -** criteria are unpinned before they are discarded. -** -** The PCache mutex must be held when this function is called. -*/ -static void pcache1TruncateUnsafe( - PCache1 *pCache, /* The cache to truncate */ - unsigned int iLimit /* Drop pages with this pgno or larger */ -){ - TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */ - unsigned int h; - assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); - for(h=0; hnHash; h++){ - PgHdr1 **pp = &pCache->apHash[h]; - PgHdr1 *pPage; - while( (pPage = *pp)!=0 ){ - if( pPage->iKey>=iLimit ){ - pCache->nPage--; - *pp = pPage->pNext; - pcache1PinPage(pPage); - pcache1FreePage(pPage); - }else{ - pp = &pPage->pNext; - TESTONLY( nPage++; ) - } - } - } - assert( pCache->nPage==nPage ); -} - -/******************************************************************************/ -/******** sqlite3_pcache Methods **********************************************/ - -/* -** Implementation of the sqlite3_pcache.xInit method. -*/ -static int pcache1Init(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - assert( pcache1.isInit==0 ); - memset(&pcache1, 0, sizeof(pcache1)); - if( sqlite3GlobalConfig.bCoreMutex ){ - pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU); - pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM); - } - pcache1.grp.mxPinned = 10; - pcache1.isInit = 1; - return SQLITE_OK; -} - -/* -** Implementation of the sqlite3_pcache.xShutdown method. -** Note that the static mutex allocated in xInit does -** not need to be freed. -*/ -static void pcache1Shutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - assert( pcache1.isInit!=0 ); - memset(&pcache1, 0, sizeof(pcache1)); -} - -/* -** Implementation of the sqlite3_pcache.xCreate method. -** -** Allocate a new cache. -*/ -static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ - PCache1 *pCache; /* The newly created page cache */ - PGroup *pGroup; /* The group the new page cache will belong to */ - int sz; /* Bytes of memory required to allocate the new cache */ - - /* - ** The seperateCache variable is true if each PCache has its own private - ** PGroup. In other words, separateCache is true for mode (1) where no - ** mutexing is required. - ** - ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT - ** - ** * Always use a unified cache in single-threaded applications - ** - ** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off) - ** use separate caches (mode-1) - */ -#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0 - const int separateCache = 0; -#else - int separateCache = sqlite3GlobalConfig.bCoreMutex>0; -#endif - - assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); - assert( szExtra < 300 ); - - sz = sizeof(PCache1) + sizeof(PGroup)*separateCache; - pCache = (PCache1 *)sqlite3MallocZero(sz); - if( pCache ){ - if( separateCache ){ - pGroup = (PGroup*)&pCache[1]; - pGroup->mxPinned = 10; - }else{ - pGroup = &pcache1.grp; - } - pCache->pGroup = pGroup; - pCache->szPage = szPage; - pCache->szExtra = szExtra; - pCache->bPurgeable = (bPurgeable ? 1 : 0); - if( bPurgeable ){ - pCache->nMin = 10; - pcache1EnterMutex(pGroup); - pGroup->nMinPage += pCache->nMin; - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pcache1LeaveMutex(pGroup); - } - } - return (sqlite3_pcache *)pCache; -} - -/* -** Implementation of the sqlite3_pcache.xCachesize method. -** -** Configure the cache_size limit for a cache. -*/ -static void pcache1Cachesize(sqlite3_pcache *p, int nMax){ - PCache1 *pCache = (PCache1 *)p; - if( pCache->bPurgeable ){ - PGroup *pGroup = pCache->pGroup; - pcache1EnterMutex(pGroup); - pGroup->nMaxPage += (nMax - pCache->nMax); - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pCache->nMax = nMax; - pCache->n90pct = pCache->nMax*9/10; - pcache1EnforceMaxPage(pGroup); - pcache1LeaveMutex(pGroup); - } -} - -/* -** Implementation of the sqlite3_pcache.xShrink method. -** -** Free up as much memory as possible. -*/ -static void pcache1Shrink(sqlite3_pcache *p){ - PCache1 *pCache = (PCache1*)p; - if( pCache->bPurgeable ){ - PGroup *pGroup = pCache->pGroup; - int savedMaxPage; - pcache1EnterMutex(pGroup); - savedMaxPage = pGroup->nMaxPage; - pGroup->nMaxPage = 0; - pcache1EnforceMaxPage(pGroup); - pGroup->nMaxPage = savedMaxPage; - pcache1LeaveMutex(pGroup); - } -} - -/* -** Implementation of the sqlite3_pcache.xPagecount method. -*/ -static int pcache1Pagecount(sqlite3_pcache *p){ - int n; - PCache1 *pCache = (PCache1*)p; - pcache1EnterMutex(pCache->pGroup); - n = pCache->nPage; - pcache1LeaveMutex(pCache->pGroup); - return n; -} - -/* -** Implementation of the sqlite3_pcache.xFetch method. -** -** Fetch a page by key value. -** -** Whether or not a new page may be allocated by this function depends on -** the value of the createFlag argument. 0 means do not allocate a new -** page. 1 means allocate a new page if space is easily available. 2 -** means to try really hard to allocate a new page. -** -** For a non-purgeable cache (a cache used as the storage for an in-memory -** database) there is really no difference between createFlag 1 and 2. So -** the calling function (pcache.c) will never have a createFlag of 1 on -** a non-purgeable cache. -** -** There are three different approaches to obtaining space for a page, -** depending on the value of parameter createFlag (which may be 0, 1 or 2). -** -** 1. Regardless of the value of createFlag, the cache is searched for a -** copy of the requested page. If one is found, it is returned. -** -** 2. If createFlag==0 and the page is not already in the cache, NULL is -** returned. -** -** 3. If createFlag is 1, and the page is not already in the cache, then -** return NULL (do not allocate a new page) if any of the following -** conditions are true: -** -** (a) the number of pages pinned by the cache is greater than -** PCache1.nMax, or -** -** (b) the number of pages pinned by the cache is greater than -** the sum of nMax for all purgeable caches, less the sum of -** nMin for all other purgeable caches, or -** -** 4. If none of the first three conditions apply and the cache is marked -** as purgeable, and if one of the following is true: -** -** (a) The number of pages allocated for the cache is already -** PCache1.nMax, or -** -** (b) The number of pages allocated for all purgeable caches is -** already equal to or greater than the sum of nMax for all -** purgeable caches, -** -** (c) The system is under memory pressure and wants to avoid -** unnecessary pages cache entry allocations -** -** then attempt to recycle a page from the LRU list. If it is the right -** size, return the recycled buffer. Otherwise, free the buffer and -** proceed to step 5. -** -** 5. Otherwise, allocate and return a new page buffer. -*/ -static sqlite3_pcache_page *pcache1Fetch( - sqlite3_pcache *p, - unsigned int iKey, - int createFlag -){ - unsigned int nPinned; - PCache1 *pCache = (PCache1 *)p; - PGroup *pGroup; - PgHdr1 *pPage = 0; - - assert( pCache->bPurgeable || createFlag!=1 ); - assert( pCache->bPurgeable || pCache->nMin==0 ); - assert( pCache->bPurgeable==0 || pCache->nMin==10 ); - assert( pCache->nMin==0 || pCache->bPurgeable ); - pcache1EnterMutex(pGroup = pCache->pGroup); - - /* Step 1: Search the hash table for an existing entry. */ - if( pCache->nHash>0 ){ - unsigned int h = iKey % pCache->nHash; - for(pPage=pCache->apHash[h]; pPage&&pPage->iKey!=iKey; pPage=pPage->pNext); - } - - /* Step 2: Abort if no existing page is found and createFlag is 0 */ - if( pPage || createFlag==0 ){ - pcache1PinPage(pPage); - goto fetch_out; - } - - /* The pGroup local variable will normally be initialized by the - ** pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined, - ** then pcache1EnterMutex() is a no-op, so we have to initialize the - ** local variable here. Delaying the initialization of pGroup is an - ** optimization: The common case is to exit the module before reaching - ** this point. - */ -#ifdef SQLITE_MUTEX_OMIT - pGroup = pCache->pGroup; -#endif - - /* Step 3: Abort if createFlag is 1 but the cache is nearly full */ - assert( pCache->nPage >= pCache->nRecyclable ); - nPinned = pCache->nPage - pCache->nRecyclable; - assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage ); - assert( pCache->n90pct == pCache->nMax*9/10 ); - if( createFlag==1 && ( - nPinned>=pGroup->mxPinned - || nPinned>=pCache->n90pct - || pcache1UnderMemoryPressure(pCache) - )){ - goto fetch_out; - } - - if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){ - goto fetch_out; - } - - /* Step 4. Try to recycle a page. */ - if( pCache->bPurgeable && pGroup->pLruTail && ( - (pCache->nPage+1>=pCache->nMax) - || pGroup->nCurrentPage>=pGroup->nMaxPage - || pcache1UnderMemoryPressure(pCache) - )){ - PCache1 *pOther; - pPage = pGroup->pLruTail; - pcache1RemoveFromHash(pPage); - pcache1PinPage(pPage); - pOther = pPage->pCache; - - /* We want to verify that szPage and szExtra are the same for pOther - ** and pCache. Assert that we can verify this by comparing sums. */ - assert( (pCache->szPage & (pCache->szPage-1))==0 && pCache->szPage>=512 ); - assert( pCache->szExtra<512 ); - assert( (pOther->szPage & (pOther->szPage-1))==0 && pOther->szPage>=512 ); - assert( pOther->szExtra<512 ); - - if( pOther->szPage+pOther->szExtra != pCache->szPage+pCache->szExtra ){ - pcache1FreePage(pPage); - pPage = 0; - }else{ - pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable); - } - } - - /* Step 5. If a usable page buffer has still not been found, - ** attempt to allocate a new one. - */ - if( !pPage ){ - if( createFlag==1 ) sqlite3BeginBenignMalloc(); - pPage = pcache1AllocPage(pCache); - if( createFlag==1 ) sqlite3EndBenignMalloc(); - } - - if( pPage ){ - unsigned int h = iKey % pCache->nHash; - pCache->nPage++; - pPage->iKey = iKey; - pPage->pNext = pCache->apHash[h]; - pPage->pCache = pCache; - pPage->pLruPrev = 0; - pPage->pLruNext = 0; - *(void **)pPage->page.pExtra = 0; - pCache->apHash[h] = pPage; - } - -fetch_out: - if( pPage && iKey>pCache->iMaxKey ){ - pCache->iMaxKey = iKey; - } - pcache1LeaveMutex(pGroup); - return &pPage->page; -} - - -/* -** Implementation of the sqlite3_pcache.xUnpin method. -** -** Mark a page as unpinned (eligible for asynchronous recycling). -*/ -static void pcache1Unpin( - sqlite3_pcache *p, - sqlite3_pcache_page *pPg, - int reuseUnlikely -){ - PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = (PgHdr1 *)pPg; - PGroup *pGroup = pCache->pGroup; - - assert( pPage->pCache==pCache ); - pcache1EnterMutex(pGroup); - - /* It is an error to call this function if the page is already - ** part of the PGroup LRU list. - */ - assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); - assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage ); - - if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){ - pcache1RemoveFromHash(pPage); - pcache1FreePage(pPage); - }else{ - /* Add the page to the PGroup LRU list. */ - if( pGroup->pLruHead ){ - pGroup->pLruHead->pLruPrev = pPage; - pPage->pLruNext = pGroup->pLruHead; - pGroup->pLruHead = pPage; - }else{ - pGroup->pLruTail = pPage; - pGroup->pLruHead = pPage; - } - pCache->nRecyclable++; - } - - pcache1LeaveMutex(pCache->pGroup); -} - -/* -** Implementation of the sqlite3_pcache.xRekey method. -*/ -static void pcache1Rekey( - sqlite3_pcache *p, - sqlite3_pcache_page *pPg, - unsigned int iOld, - unsigned int iNew -){ - PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = (PgHdr1 *)pPg; - PgHdr1 **pp; - unsigned int h; - assert( pPage->iKey==iOld ); - assert( pPage->pCache==pCache ); - - pcache1EnterMutex(pCache->pGroup); - - h = iOld%pCache->nHash; - pp = &pCache->apHash[h]; - while( (*pp)!=pPage ){ - pp = &(*pp)->pNext; - } - *pp = pPage->pNext; - - h = iNew%pCache->nHash; - pPage->iKey = iNew; - pPage->pNext = pCache->apHash[h]; - pCache->apHash[h] = pPage; - if( iNew>pCache->iMaxKey ){ - pCache->iMaxKey = iNew; - } - - pcache1LeaveMutex(pCache->pGroup); -} - -/* -** Implementation of the sqlite3_pcache.xTruncate method. -** -** Discard all unpinned pages in the cache with a page number equal to -** or greater than parameter iLimit. Any pinned pages with a page number -** equal to or greater than iLimit are implicitly unpinned. -*/ -static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){ - PCache1 *pCache = (PCache1 *)p; - pcache1EnterMutex(pCache->pGroup); - if( iLimit<=pCache->iMaxKey ){ - pcache1TruncateUnsafe(pCache, iLimit); - pCache->iMaxKey = iLimit-1; - } - pcache1LeaveMutex(pCache->pGroup); -} - -/* -** Implementation of the sqlite3_pcache.xDestroy method. -** -** Destroy a cache allocated using pcache1Create(). -*/ -static void pcache1Destroy(sqlite3_pcache *p){ - PCache1 *pCache = (PCache1 *)p; - PGroup *pGroup = pCache->pGroup; - assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); - pcache1EnterMutex(pGroup); - pcache1TruncateUnsafe(pCache, 0); - assert( pGroup->nMaxPage >= pCache->nMax ); - pGroup->nMaxPage -= pCache->nMax; - assert( pGroup->nMinPage >= pCache->nMin ); - pGroup->nMinPage -= pCache->nMin; - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pcache1EnforceMaxPage(pGroup); - pcache1LeaveMutex(pGroup); - sqlite3_free(pCache->apHash); - sqlite3_free(pCache); -} - -/* -** This function is called during initialization (sqlite3_initialize()) to -** install the default pluggable cache module, assuming the user has not -** already provided an alternative. -*/ -SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){ - static const sqlite3_pcache_methods2 defaultMethods = { - 1, /* iVersion */ - 0, /* pArg */ - pcache1Init, /* xInit */ - pcache1Shutdown, /* xShutdown */ - pcache1Create, /* xCreate */ - pcache1Cachesize, /* xCachesize */ - pcache1Pagecount, /* xPagecount */ - pcache1Fetch, /* xFetch */ - pcache1Unpin, /* xUnpin */ - pcache1Rekey, /* xRekey */ - pcache1Truncate, /* xTruncate */ - pcache1Destroy, /* xDestroy */ - pcache1Shrink /* xShrink */ - }; - sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods); -} - -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -/* -** This function is called to free superfluous dynamically allocated memory -** held by the pager system. Memory in use by any SQLite pager allocated -** by the current thread may be sqlite3_free()ed. -** -** nReq is the number of bytes of memory required. Once this much has -** been released, the function returns. The return value is the total number -** of bytes of memory released. -*/ -SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ - int nFree = 0; - assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); - assert( sqlite3_mutex_notheld(pcache1.mutex) ); - if( pcache1.pStart==0 ){ - PgHdr1 *p; - pcache1EnterMutex(&pcache1.grp); - while( (nReq<0 || nFreepage.pBuf); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - nFree += sqlite3MemSize(p); -#endif - pcache1PinPage(p); - pcache1RemoveFromHash(p); - pcache1FreePage(p); - } - pcache1LeaveMutex(&pcache1.grp); - } - return nFree; -} -#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ - -#ifdef SQLITE_TEST -/* -** This function is used by test procedures to inspect the internal state -** of the global cache. -*/ -SQLITE_PRIVATE void sqlite3PcacheStats( - int *pnCurrent, /* OUT: Total number of pages cached */ - int *pnMax, /* OUT: Global maximum cache size */ - int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */ - int *pnRecyclable /* OUT: Total number of pages available for recycling */ -){ - PgHdr1 *p; - int nRecyclable = 0; - for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){ - nRecyclable++; - } - *pnCurrent = pcache1.grp.nCurrentPage; - *pnMax = (int)pcache1.grp.nMaxPage; - *pnMin = (int)pcache1.grp.nMinPage; - *pnRecyclable = nRecyclable; -} -#endif - -/************** End of pcache1.c *********************************************/ -/************** Begin file rowset.c ******************************************/ -/* -** 2008 December 3 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This module implements an object we call a "RowSet". -** -** The RowSet object is a collection of rowids. Rowids -** are inserted into the RowSet in an arbitrary order. Inserts -** can be intermixed with tests to see if a given rowid has been -** previously inserted into the RowSet. -** -** After all inserts are finished, it is possible to extract the -** elements of the RowSet in sorted order. Once this extraction -** process has started, no new elements may be inserted. -** -** Hence, the primitive operations for a RowSet are: -** -** CREATE -** INSERT -** TEST -** SMALLEST -** DESTROY -** -** The CREATE and DESTROY primitives are the constructor and destructor, -** obviously. The INSERT primitive adds a new element to the RowSet. -** TEST checks to see if an element is already in the RowSet. SMALLEST -** extracts the least value from the RowSet. -** -** The INSERT primitive might allocate additional memory. Memory is -** allocated in chunks so most INSERTs do no allocation. There is an -** upper bound on the size of allocated memory. No memory is freed -** until DESTROY. -** -** The TEST primitive includes a "batch" number. The TEST primitive -** will only see elements that were inserted before the last change -** in the batch number. In other words, if an INSERT occurs between -** two TESTs where the TESTs have the same batch nubmer, then the -** value added by the INSERT will not be visible to the second TEST. -** The initial batch number is zero, so if the very first TEST contains -** a non-zero batch number, it will see all prior INSERTs. -** -** No INSERTs may occurs after a SMALLEST. An assertion will fail if -** that is attempted. -** -** The cost of an INSERT is roughly constant. (Sometime new memory -** has to be allocated on an INSERT.) The cost of a TEST with a new -** batch number is O(NlogN) where N is the number of elements in the RowSet. -** The cost of a TEST using the same batch number is O(logN). The cost -** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST -** primitives are constant time. The cost of DESTROY is O(N). -** -** There is an added cost of O(N) when switching between TEST and -** SMALLEST primitives. -*/ - - -/* -** Target size for allocation chunks. -*/ -#define ROWSET_ALLOCATION_SIZE 1024 - -/* -** The number of rowset entries per allocation chunk. -*/ -#define ROWSET_ENTRY_PER_CHUNK \ - ((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry)) - -/* -** Each entry in a RowSet is an instance of the following object. -** -** This same object is reused to store a linked list of trees of RowSetEntry -** objects. In that alternative use, pRight points to the next entry -** in the list, pLeft points to the tree, and v is unused. The -** RowSet.pForest value points to the head of this forest list. -*/ -struct RowSetEntry { - i64 v; /* ROWID value for this entry */ - struct RowSetEntry *pRight; /* Right subtree (larger entries) or list */ - struct RowSetEntry *pLeft; /* Left subtree (smaller entries) */ -}; - -/* -** RowSetEntry objects are allocated in large chunks (instances of the -** following structure) to reduce memory allocation overhead. The -** chunks are kept on a linked list so that they can be deallocated -** when the RowSet is destroyed. -*/ -struct RowSetChunk { - struct RowSetChunk *pNextChunk; /* Next chunk on list of them all */ - struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */ -}; - -/* -** A RowSet in an instance of the following structure. -** -** A typedef of this structure if found in sqliteInt.h. -*/ -struct RowSet { - struct RowSetChunk *pChunk; /* List of all chunk allocations */ - sqlite3 *db; /* The database connection */ - struct RowSetEntry *pEntry; /* List of entries using pRight */ - struct RowSetEntry *pLast; /* Last entry on the pEntry list */ - struct RowSetEntry *pFresh; /* Source of new entry objects */ - struct RowSetEntry *pForest; /* List of binary trees of entries */ - u16 nFresh; /* Number of objects on pFresh */ - u8 rsFlags; /* Various flags */ - u8 iBatch; /* Current insert batch */ -}; - -/* -** Allowed values for RowSet.rsFlags -*/ -#define ROWSET_SORTED 0x01 /* True if RowSet.pEntry is sorted */ -#define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */ - -/* -** Turn bulk memory into a RowSet object. N bytes of memory -** are available at pSpace. The db pointer is used as a memory context -** for any subsequent allocations that need to occur. -** Return a pointer to the new RowSet object. -** -** It must be the case that N is sufficient to make a Rowset. If not -** an assertion fault occurs. -** -** If N is larger than the minimum, use the surplus as an initial -** allocation of entries available to be filled. -*/ -SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){ - RowSet *p; - assert( N >= ROUND8(sizeof(*p)) ); - p = pSpace; - p->pChunk = 0; - p->db = db; - p->pEntry = 0; - p->pLast = 0; - p->pForest = 0; - p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p); - p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry)); - p->rsFlags = ROWSET_SORTED; - p->iBatch = 0; - return p; -} - -/* -** Deallocate all chunks from a RowSet. This frees all memory that -** the RowSet has allocated over its lifetime. This routine is -** the destructor for the RowSet. -*/ -SQLITE_PRIVATE void sqlite3RowSetClear(RowSet *p){ - struct RowSetChunk *pChunk, *pNextChunk; - for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){ - pNextChunk = pChunk->pNextChunk; - sqlite3DbFree(p->db, pChunk); - } - p->pChunk = 0; - p->nFresh = 0; - p->pEntry = 0; - p->pLast = 0; - p->pForest = 0; - p->rsFlags = ROWSET_SORTED; -} - -/* -** Allocate a new RowSetEntry object that is associated with the -** given RowSet. Return a pointer to the new and completely uninitialized -** objected. -** -** In an OOM situation, the RowSet.db->mallocFailed flag is set and this -** routine returns NULL. -*/ -static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){ - assert( p!=0 ); - if( p->nFresh==0 ){ - struct RowSetChunk *pNew; - pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew)); - if( pNew==0 ){ - return 0; - } - pNew->pNextChunk = p->pChunk; - p->pChunk = pNew; - p->pFresh = pNew->aEntry; - p->nFresh = ROWSET_ENTRY_PER_CHUNK; - } - p->nFresh--; - return p->pFresh++; -} - -/* -** Insert a new value into a RowSet. -** -** The mallocFailed flag of the database connection is set if a -** memory allocation fails. -*/ -SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){ - struct RowSetEntry *pEntry; /* The new entry */ - struct RowSetEntry *pLast; /* The last prior entry */ - - /* This routine is never called after sqlite3RowSetNext() */ - assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 ); - - pEntry = rowSetEntryAlloc(p); - if( pEntry==0 ) return; - pEntry->v = rowid; - pEntry->pRight = 0; - pLast = p->pLast; - if( pLast ){ - if( (p->rsFlags & ROWSET_SORTED)!=0 && rowid<=pLast->v ){ - p->rsFlags &= ~ROWSET_SORTED; - } - pLast->pRight = pEntry; - }else{ - p->pEntry = pEntry; - } - p->pLast = pEntry; -} - -/* -** Merge two lists of RowSetEntry objects. Remove duplicates. -** -** The input lists are connected via pRight pointers and are -** assumed to each already be in sorted order. -*/ -static struct RowSetEntry *rowSetEntryMerge( - struct RowSetEntry *pA, /* First sorted list to be merged */ - struct RowSetEntry *pB /* Second sorted list to be merged */ -){ - struct RowSetEntry head; - struct RowSetEntry *pTail; - - pTail = &head; - while( pA && pB ){ - assert( pA->pRight==0 || pA->v<=pA->pRight->v ); - assert( pB->pRight==0 || pB->v<=pB->pRight->v ); - if( pA->vv ){ - pTail->pRight = pA; - pA = pA->pRight; - pTail = pTail->pRight; - }else if( pB->vv ){ - pTail->pRight = pB; - pB = pB->pRight; - pTail = pTail->pRight; - }else{ - pA = pA->pRight; - } - } - if( pA ){ - assert( pA->pRight==0 || pA->v<=pA->pRight->v ); - pTail->pRight = pA; - }else{ - assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v ); - pTail->pRight = pB; - } - return head.pRight; -} - -/* -** Sort all elements on the list of RowSetEntry objects into order of -** increasing v. -*/ -static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){ - unsigned int i; - struct RowSetEntry *pNext, *aBucket[40]; - - memset(aBucket, 0, sizeof(aBucket)); - while( pIn ){ - pNext = pIn->pRight; - pIn->pRight = 0; - for(i=0; aBucket[i]; i++){ - pIn = rowSetEntryMerge(aBucket[i], pIn); - aBucket[i] = 0; - } - aBucket[i] = pIn; - pIn = pNext; - } - pIn = 0; - for(i=0; ipLeft ){ - struct RowSetEntry *p; - rowSetTreeToList(pIn->pLeft, ppFirst, &p); - p->pRight = pIn; - }else{ - *ppFirst = pIn; - } - if( pIn->pRight ){ - rowSetTreeToList(pIn->pRight, &pIn->pRight, ppLast); - }else{ - *ppLast = pIn; - } - assert( (*ppLast)->pRight==0 ); -} - - -/* -** Convert a sorted list of elements (connected by pRight) into a binary -** tree with depth of iDepth. A depth of 1 means the tree contains a single -** node taken from the head of *ppList. A depth of 2 means a tree with -** three nodes. And so forth. -** -** Use as many entries from the input list as required and update the -** *ppList to point to the unused elements of the list. If the input -** list contains too few elements, then construct an incomplete tree -** and leave *ppList set to NULL. -** -** Return a pointer to the root of the constructed binary tree. -*/ -static struct RowSetEntry *rowSetNDeepTree( - struct RowSetEntry **ppList, - int iDepth -){ - struct RowSetEntry *p; /* Root of the new tree */ - struct RowSetEntry *pLeft; /* Left subtree */ - if( *ppList==0 ){ - return 0; - } - if( iDepth==1 ){ - p = *ppList; - *ppList = p->pRight; - p->pLeft = p->pRight = 0; - return p; - } - pLeft = rowSetNDeepTree(ppList, iDepth-1); - p = *ppList; - if( p==0 ){ - return pLeft; - } - p->pLeft = pLeft; - *ppList = p->pRight; - p->pRight = rowSetNDeepTree(ppList, iDepth-1); - return p; -} - -/* -** Convert a sorted list of elements into a binary tree. Make the tree -** as deep as it needs to be in order to contain the entire list. -*/ -static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){ - int iDepth; /* Depth of the tree so far */ - struct RowSetEntry *p; /* Current tree root */ - struct RowSetEntry *pLeft; /* Left subtree */ - - assert( pList!=0 ); - p = pList; - pList = p->pRight; - p->pLeft = p->pRight = 0; - for(iDepth=1; pList; iDepth++){ - pLeft = p; - p = pList; - pList = p->pRight; - p->pLeft = pLeft; - p->pRight = rowSetNDeepTree(&pList, iDepth); - } - return p; -} - -/* -** Take all the entries on p->pEntry and on the trees in p->pForest and -** sort them all together into one big ordered list on p->pEntry. -** -** This routine should only be called once in the life of a RowSet. -*/ -static void rowSetToList(RowSet *p){ - - /* This routine is called only once */ - assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 ); - - if( (p->rsFlags & ROWSET_SORTED)==0 ){ - p->pEntry = rowSetEntrySort(p->pEntry); - } - - /* While this module could theoretically support it, sqlite3RowSetNext() - ** is never called after sqlite3RowSetText() for the same RowSet. So - ** there is never a forest to deal with. Should this change, simply - ** remove the assert() and the #if 0. */ - assert( p->pForest==0 ); -#if 0 - while( p->pForest ){ - struct RowSetEntry *pTree = p->pForest->pLeft; - if( pTree ){ - struct RowSetEntry *pHead, *pTail; - rowSetTreeToList(pTree, &pHead, &pTail); - p->pEntry = rowSetEntryMerge(p->pEntry, pHead); - } - p->pForest = p->pForest->pRight; - } -#endif - p->rsFlags |= ROWSET_NEXT; /* Verify this routine is never called again */ -} - -/* -** Extract the smallest element from the RowSet. -** Write the element into *pRowid. Return 1 on success. Return -** 0 if the RowSet is already empty. -** -** After this routine has been called, the sqlite3RowSetInsert() -** routine may not be called again. -*/ -SQLITE_PRIVATE int sqlite3RowSetNext(RowSet *p, i64 *pRowid){ - assert( p!=0 ); - - /* Merge the forest into a single sorted list on first call */ - if( (p->rsFlags & ROWSET_NEXT)==0 ) rowSetToList(p); - - /* Return the next entry on the list */ - if( p->pEntry ){ - *pRowid = p->pEntry->v; - p->pEntry = p->pEntry->pRight; - if( p->pEntry==0 ){ - sqlite3RowSetClear(p); - } - return 1; - }else{ - return 0; - } -} - -/* -** Check to see if element iRowid was inserted into the rowset as -** part of any insert batch prior to iBatch. Return 1 or 0. -** -** If this is the first test of a new batch and if there exist entires -** on pRowSet->pEntry, then sort those entires into the forest at -** pRowSet->pForest so that they can be tested. -*/ -SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){ - struct RowSetEntry *p, *pTree; - - /* This routine is never called after sqlite3RowSetNext() */ - assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 ); - - /* Sort entries into the forest on the first test of a new batch - */ - if( iBatch!=pRowSet->iBatch ){ - p = pRowSet->pEntry; - if( p ){ - struct RowSetEntry **ppPrevTree = &pRowSet->pForest; - if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ - p = rowSetEntrySort(p); - } - for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ - ppPrevTree = &pTree->pRight; - if( pTree->pLeft==0 ){ - pTree->pLeft = rowSetListToTree(p); - break; - }else{ - struct RowSetEntry *pAux, *pTail; - rowSetTreeToList(pTree->pLeft, &pAux, &pTail); - pTree->pLeft = 0; - p = rowSetEntryMerge(pAux, p); - } - } - if( pTree==0 ){ - *ppPrevTree = pTree = rowSetEntryAlloc(pRowSet); - if( pTree ){ - pTree->v = 0; - pTree->pRight = 0; - pTree->pLeft = rowSetListToTree(p); - } - } - pRowSet->pEntry = 0; - pRowSet->pLast = 0; - pRowSet->rsFlags |= ROWSET_SORTED; - } - pRowSet->iBatch = iBatch; - } - - /* Test to see if the iRowid value appears anywhere in the forest. - ** Return 1 if it does and 0 if not. - */ - for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ - p = pTree->pLeft; - while( p ){ - if( p->vpRight; - }else if( p->v>iRowid ){ - p = p->pLeft; - }else{ - return 1; - } - } - } - return 0; -} - -/************** End of rowset.c **********************************************/ -/************** Begin file pager.c *******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This is the implementation of the page cache subsystem or "pager". -** -** The pager is used to access a database disk file. It implements -** atomic commit and rollback through the use of a journal file that -** is separate from the database file. The pager also implements file -** locking to prevent two processes from writing the same database -** file simultaneously, or one process from reading the database while -** another is writing. -*/ -#ifndef SQLITE_OMIT_DISKIO -/************** Include wal.h in the middle of pager.c ***********************/ -/************** Begin file wal.h *********************************************/ -/* -** 2010 February 1 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface to the write-ahead logging -** system. Refer to the comments below and the header comment attached to -** the implementation of each function in log.c for further details. -*/ - -#ifndef _WAL_H_ -#define _WAL_H_ - - -/* Additional values that can be added to the sync_flags argument of -** sqlite3WalFrames(): -*/ -#define WAL_SYNC_TRANSACTIONS 0x20 /* Sync at the end of each transaction */ -#define SQLITE_SYNC_MASK 0x13 /* Mask off the SQLITE_SYNC_* values */ - -#ifdef SQLITE_OMIT_WAL -# define sqlite3WalOpen(x,y,z) 0 -# define sqlite3WalLimit(x,y) -# define sqlite3WalClose(w,x,y,z) 0 -# define sqlite3WalBeginReadTransaction(y,z) 0 -# define sqlite3WalEndReadTransaction(z) -# define sqlite3WalRead(v,w,x,y,z) 0 -# define sqlite3WalDbsize(y) 0 -# define sqlite3WalBeginWriteTransaction(y) 0 -# define sqlite3WalEndWriteTransaction(x) 0 -# define sqlite3WalUndo(x,y,z) 0 -# define sqlite3WalSavepoint(y,z) -# define sqlite3WalSavepointUndo(y,z) 0 -# define sqlite3WalFrames(u,v,w,x,y,z) 0 -# define sqlite3WalCheckpoint(r,s,t,u,v,w,x,y,z) 0 -# define sqlite3WalCallback(z) 0 -# define sqlite3WalExclusiveMode(y,z) 0 -# define sqlite3WalHeapMemory(z) 0 -# define sqlite3WalFramesize(z) 0 -#else - -#define WAL_SAVEPOINT_NDATA 4 - -/* Connection to a write-ahead log (WAL) file. -** There is one object of this type for each pager. -*/ -typedef struct Wal Wal; - -/* Open and close a connection to a write-ahead log. */ -SQLITE_PRIVATE int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *, int, i64, Wal**); -SQLITE_PRIVATE int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); - -/* Set the limiting size of a WAL file. */ -SQLITE_PRIVATE void sqlite3WalLimit(Wal*, i64); - -/* Used by readers to open (lock) and close (unlock) a snapshot. A -** snapshot is like a read-transaction. It is the state of the database -** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and -** preserves the current state even if the other threads or processes -** write to or checkpoint the WAL. sqlite3WalCloseSnapshot() closes the -** transaction and releases the lock. -*/ -SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *); -SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal); - -/* Read a page from the write-ahead log, if it is present. */ -SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut); - -/* If the WAL is not empty, return the size of the database. */ -SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal); - -/* Obtain or release the WRITER lock. */ -SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal); -SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal); - -/* Undo any frames written (but not committed) to the log */ -SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx); - -/* Return an integer that records the current (uncommitted) write -** position in the WAL */ -SQLITE_PRIVATE void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData); - -/* Move the write position of the WAL back to iFrame. Called in -** response to a ROLLBACK TO command. */ -SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData); - -/* Write a frame or frames to the log. */ -SQLITE_PRIVATE int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); - -/* Copy pages from the log to the database file */ -SQLITE_PRIVATE int sqlite3WalCheckpoint( - Wal *pWal, /* Write-ahead log connection */ - int eMode, /* One of PASSIVE, FULL and RESTART */ - int (*xBusy)(void*), /* Function to call when busy */ - void *pBusyArg, /* Context argument for xBusyHandler */ - int sync_flags, /* Flags to sync db file with (or 0) */ - int nBuf, /* Size of buffer nBuf */ - u8 *zBuf, /* Temporary buffer to use */ - int *pnLog, /* OUT: Number of frames in WAL */ - int *pnCkpt /* OUT: Number of backfilled frames in WAL */ -); - -/* Return the value to pass to a sqlite3_wal_hook callback, the -** number of frames in the WAL at the point of the last commit since -** sqlite3WalCallback() was called. If no commits have occurred since -** the last call, then return 0. -*/ -SQLITE_PRIVATE int sqlite3WalCallback(Wal *pWal); - -/* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) -** by the pager layer on the database file. -*/ -SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op); - -/* Return true if the argument is non-NULL and the WAL module is using -** heap-memory for the wal-index. Otherwise, if the argument is NULL or the -** WAL module is using shared-memory, return false. -*/ -SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal); - -#ifdef SQLITE_ENABLE_ZIPVFS -/* If the WAL file is not empty, return the number of bytes of content -** stored in each frame (i.e. the db page-size when the WAL was created). -*/ -SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal); -#endif - -#endif /* ifndef SQLITE_OMIT_WAL */ -#endif /* _WAL_H_ */ - -/************** End of wal.h *************************************************/ -/************** Continuing where we left off in pager.c **********************/ - - -/******************* NOTES ON THE DESIGN OF THE PAGER ************************ -** -** This comment block describes invariants that hold when using a rollback -** journal. These invariants do not apply for journal_mode=WAL, -** journal_mode=MEMORY, or journal_mode=OFF. -** -** Within this comment block, a page is deemed to have been synced -** automatically as soon as it is written when PRAGMA synchronous=OFF. -** Otherwise, the page is not synced until the xSync method of the VFS -** is called successfully on the file containing the page. -** -** Definition: A page of the database file is said to be "overwriteable" if -** one or more of the following are true about the page: -** -** (a) The original content of the page as it was at the beginning of -** the transaction has been written into the rollback journal and -** synced. -** -** (b) The page was a freelist leaf page at the start of the transaction. -** -** (c) The page number is greater than the largest page that existed in -** the database file at the start of the transaction. -** -** (1) A page of the database file is never overwritten unless one of the -** following are true: -** -** (a) The page and all other pages on the same sector are overwriteable. -** -** (b) The atomic page write optimization is enabled, and the entire -** transaction other than the update of the transaction sequence -** number consists of a single page change. -** -** (2) The content of a page written into the rollback journal exactly matches -** both the content in the database when the rollback journal was written -** and the content in the database at the beginning of the current -** transaction. -** -** (3) Writes to the database file are an integer multiple of the page size -** in length and are aligned on a page boundary. -** -** (4) Reads from the database file are either aligned on a page boundary and -** an integer multiple of the page size in length or are taken from the -** first 100 bytes of the database file. -** -** (5) All writes to the database file are synced prior to the rollback journal -** being deleted, truncated, or zeroed. -** -** (6) If a master journal file is used, then all writes to the database file -** are synced prior to the master journal being deleted. -** -** Definition: Two databases (or the same database at two points it time) -** are said to be "logically equivalent" if they give the same answer to -** all queries. Note in particular the content of freelist leaf -** pages can be changed arbitarily without effecting the logical equivalence -** of the database. -** -** (7) At any time, if any subset, including the empty set and the total set, -** of the unsynced changes to a rollback journal are removed and the -** journal is rolled back, the resulting database file will be logical -** equivalent to the database file at the beginning of the transaction. -** -** (8) When a transaction is rolled back, the xTruncate method of the VFS -** is called to restore the database file to the same size it was at -** the beginning of the transaction. (In some VFSes, the xTruncate -** method is a no-op, but that does not change the fact the SQLite will -** invoke it.) -** -** (9) Whenever the database file is modified, at least one bit in the range -** of bytes from 24 through 39 inclusive will be changed prior to releasing -** the EXCLUSIVE lock, thus signaling other connections on the same -** database to flush their caches. -** -** (10) The pattern of bits in bytes 24 through 39 shall not repeat in less -** than one billion transactions. -** -** (11) A database file is well-formed at the beginning and at the conclusion -** of every transaction. -** -** (12) An EXCLUSIVE lock is held on the database file when writing to -** the database file. -** -** (13) A SHARED lock is held on the database file while reading any -** content out of the database file. -** -******************************************************************************/ - -/* -** Macros for troubleshooting. Normally turned off -*/ -#if 0 -int sqlite3PagerTrace=1; /* True to enable tracing */ -#define sqlite3DebugPrintf printf -#define PAGERTRACE(X) if( sqlite3PagerTrace ){ sqlite3DebugPrintf X; } -#else -#define PAGERTRACE(X) -#endif - -/* -** The following two macros are used within the PAGERTRACE() macros above -** to print out file-descriptors. -** -** PAGERID() takes a pointer to a Pager struct as its argument. The -** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file -** struct as its argument. -*/ -#define PAGERID(p) ((int)(p->fd)) -#define FILEHANDLEID(fd) ((int)fd) - -/* -** The Pager.eState variable stores the current 'state' of a pager. A -** pager may be in any one of the seven states shown in the following -** state diagram. -** -** OPEN <------+------+ -** | | | -** V | | -** +---------> READER-------+ | -** | | | -** | V | -** |<-------WRITER_LOCKED------> ERROR -** | | ^ -** | V | -** |<------WRITER_CACHEMOD-------->| -** | | | -** | V | -** |<-------WRITER_DBMOD---------->| -** | | | -** | V | -** +<------WRITER_FINISHED-------->+ -** -** -** List of state transitions and the C [function] that performs each: -** -** OPEN -> READER [sqlite3PagerSharedLock] -** READER -> OPEN [pager_unlock] -** -** READER -> WRITER_LOCKED [sqlite3PagerBegin] -** WRITER_LOCKED -> WRITER_CACHEMOD [pager_open_journal] -** WRITER_CACHEMOD -> WRITER_DBMOD [syncJournal] -** WRITER_DBMOD -> WRITER_FINISHED [sqlite3PagerCommitPhaseOne] -** WRITER_*** -> READER [pager_end_transaction] -** -** WRITER_*** -> ERROR [pager_error] -** ERROR -> OPEN [pager_unlock] -** -** -** OPEN: -** -** The pager starts up in this state. Nothing is guaranteed in this -** state - the file may or may not be locked and the database size is -** unknown. The database may not be read or written. -** -** * No read or write transaction is active. -** * Any lock, or no lock at all, may be held on the database file. -** * The dbSize, dbOrigSize and dbFileSize variables may not be trusted. -** -** READER: -** -** In this state all the requirements for reading the database in -** rollback (non-WAL) mode are met. Unless the pager is (or recently -** was) in exclusive-locking mode, a user-level read transaction is -** open. The database size is known in this state. -** -** A connection running with locking_mode=normal enters this state when -** it opens a read-transaction on the database and returns to state -** OPEN after the read-transaction is completed. However a connection -** running in locking_mode=exclusive (including temp databases) remains in -** this state even after the read-transaction is closed. The only way -** a locking_mode=exclusive connection can transition from READER to OPEN -** is via the ERROR state (see below). -** -** * A read transaction may be active (but a write-transaction cannot). -** * A SHARED or greater lock is held on the database file. -** * The dbSize variable may be trusted (even if a user-level read -** transaction is not active). The dbOrigSize and dbFileSize variables -** may not be trusted at this point. -** * If the database is a WAL database, then the WAL connection is open. -** * Even if a read-transaction is not open, it is guaranteed that -** there is no hot-journal in the file-system. -** -** WRITER_LOCKED: -** -** The pager moves to this state from READER when a write-transaction -** is first opened on the database. In WRITER_LOCKED state, all locks -** required to start a write-transaction are held, but no actual -** modifications to the cache or database have taken place. -** -** In rollback mode, a RESERVED or (if the transaction was opened with -** BEGIN EXCLUSIVE) EXCLUSIVE lock is obtained on the database file when -** moving to this state, but the journal file is not written to or opened -** to in this state. If the transaction is committed or rolled back while -** in WRITER_LOCKED state, all that is required is to unlock the database -** file. -** -** IN WAL mode, WalBeginWriteTransaction() is called to lock the log file. -** If the connection is running with locking_mode=exclusive, an attempt -** is made to obtain an EXCLUSIVE lock on the database file. -** -** * A write transaction is active. -** * If the connection is open in rollback-mode, a RESERVED or greater -** lock is held on the database file. -** * If the connection is open in WAL-mode, a WAL write transaction -** is open (i.e. sqlite3WalBeginWriteTransaction() has been successfully -** called). -** * The dbSize, dbOrigSize and dbFileSize variables are all valid. -** * The contents of the pager cache have not been modified. -** * The journal file may or may not be open. -** * Nothing (not even the first header) has been written to the journal. -** -** WRITER_CACHEMOD: -** -** A pager moves from WRITER_LOCKED state to this state when a page is -** first modified by the upper layer. In rollback mode the journal file -** is opened (if it is not already open) and a header written to the -** start of it. The database file on disk has not been modified. -** -** * A write transaction is active. -** * A RESERVED or greater lock is held on the database file. -** * The journal file is open and the first header has been written -** to it, but the header has not been synced to disk. -** * The contents of the page cache have been modified. -** -** WRITER_DBMOD: -** -** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state -** when it modifies the contents of the database file. WAL connections -** never enter this state (since they do not modify the database file, -** just the log file). -** -** * A write transaction is active. -** * An EXCLUSIVE or greater lock is held on the database file. -** * The journal file is open and the first header has been written -** and synced to disk. -** * The contents of the page cache have been modified (and possibly -** written to disk). -** -** WRITER_FINISHED: -** -** It is not possible for a WAL connection to enter this state. -** -** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD -** state after the entire transaction has been successfully written into the -** database file. In this state the transaction may be committed simply -** by finalizing the journal file. Once in WRITER_FINISHED state, it is -** not possible to modify the database further. At this point, the upper -** layer must either commit or rollback the transaction. -** -** * A write transaction is active. -** * An EXCLUSIVE or greater lock is held on the database file. -** * All writing and syncing of journal and database data has finished. -** If no error occurred, all that remains is to finalize the journal to -** commit the transaction. If an error did occur, the caller will need -** to rollback the transaction. -** -** ERROR: -** -** The ERROR state is entered when an IO or disk-full error (including -** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it -** difficult to be sure that the in-memory pager state (cache contents, -** db size etc.) are consistent with the contents of the file-system. -** -** Temporary pager files may enter the ERROR state, but in-memory pagers -** cannot. -** -** For example, if an IO error occurs while performing a rollback, -** the contents of the page-cache may be left in an inconsistent state. -** At this point it would be dangerous to change back to READER state -** (as usually happens after a rollback). Any subsequent readers might -** report database corruption (due to the inconsistent cache), and if -** they upgrade to writers, they may inadvertently corrupt the database -** file. To avoid this hazard, the pager switches into the ERROR state -** instead of READER following such an error. -** -** Once it has entered the ERROR state, any attempt to use the pager -** to read or write data returns an error. Eventually, once all -** outstanding transactions have been abandoned, the pager is able to -** transition back to OPEN state, discarding the contents of the -** page-cache and any other in-memory state at the same time. Everything -** is reloaded from disk (and, if necessary, hot-journal rollback peformed) -** when a read-transaction is next opened on the pager (transitioning -** the pager into READER state). At that point the system has recovered -** from the error. -** -** Specifically, the pager jumps into the ERROR state if: -** -** 1. An error occurs while attempting a rollback. This happens in -** function sqlite3PagerRollback(). -** -** 2. An error occurs while attempting to finalize a journal file -** following a commit in function sqlite3PagerCommitPhaseTwo(). -** -** 3. An error occurs while attempting to write to the journal or -** database file in function pagerStress() in order to free up -** memory. -** -** In other cases, the error is returned to the b-tree layer. The b-tree -** layer then attempts a rollback operation. If the error condition -** persists, the pager enters the ERROR state via condition (1) above. -** -** Condition (3) is necessary because it can be triggered by a read-only -** statement executed within a transaction. In this case, if the error -** code were simply returned to the user, the b-tree layer would not -** automatically attempt a rollback, as it assumes that an error in a -** read-only statement cannot leave the pager in an internally inconsistent -** state. -** -** * The Pager.errCode variable is set to something other than SQLITE_OK. -** * There are one or more outstanding references to pages (after the -** last reference is dropped the pager should move back to OPEN state). -** * The pager is not an in-memory pager. -** -** -** Notes: -** -** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the -** connection is open in WAL mode. A WAL connection is always in one -** of the first four states. -** -** * Normally, a connection open in exclusive mode is never in PAGER_OPEN -** state. There are two exceptions: immediately after exclusive-mode has -** been turned on (and before any read or write transactions are -** executed), and when the pager is leaving the "error state". -** -** * See also: assert_pager_state(). -*/ -#define PAGER_OPEN 0 -#define PAGER_READER 1 -#define PAGER_WRITER_LOCKED 2 -#define PAGER_WRITER_CACHEMOD 3 -#define PAGER_WRITER_DBMOD 4 -#define PAGER_WRITER_FINISHED 5 -#define PAGER_ERROR 6 - -/* -** The Pager.eLock variable is almost always set to one of the -** following locking-states, according to the lock currently held on -** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. -** This variable is kept up to date as locks are taken and released by -** the pagerLockDb() and pagerUnlockDb() wrappers. -** -** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY -** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not -** the operation was successful. In these circumstances pagerLockDb() and -** pagerUnlockDb() take a conservative approach - eLock is always updated -** when unlocking the file, and only updated when locking the file if the -** VFS call is successful. This way, the Pager.eLock variable may be set -** to a less exclusive (lower) value than the lock that is actually held -** at the system level, but it is never set to a more exclusive value. -** -** This is usually safe. If an xUnlock fails or appears to fail, there may -** be a few redundant xLock() calls or a lock may be held for longer than -** required, but nothing really goes wrong. -** -** The exception is when the database file is unlocked as the pager moves -** from ERROR to OPEN state. At this point there may be a hot-journal file -** in the file-system that needs to be rolled back (as part of a OPEN->SHARED -** transition, by the same pager or any other). If the call to xUnlock() -** fails at this point and the pager is left holding an EXCLUSIVE lock, this -** can confuse the call to xCheckReservedLock() call made later as part -** of hot-journal detection. -** -** xCheckReservedLock() is defined as returning true "if there is a RESERVED -** lock held by this process or any others". So xCheckReservedLock may -** return true because the caller itself is holding an EXCLUSIVE lock (but -** doesn't know it because of a previous error in xUnlock). If this happens -** a hot-journal may be mistaken for a journal being created by an active -** transaction in another process, causing SQLite to read from the database -** without rolling it back. -** -** To work around this, if a call to xUnlock() fails when unlocking the -** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It -** is only changed back to a real locking state after a successful call -** to xLock(EXCLUSIVE). Also, the code to do the OPEN->SHARED state transition -** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK -** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE -** lock on the database file before attempting to roll it back. See function -** PagerSharedLock() for more detail. -** -** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in -** PAGER_OPEN state. -*/ -#define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1) - -/* -** A macro used for invoking the codec if there is one -*/ -#ifdef SQLITE_HAS_CODEC -# define CODEC1(P,D,N,X,E) \ - if( P->xCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; } -# define CODEC2(P,D,N,X,E,O) \ - if( P->xCodec==0 ){ O=(char*)D; }else \ - if( (O=(char*)(P->xCodec(P->pCodec,D,N,X)))==0 ){ E; } -#else -# define CODEC1(P,D,N,X,E) /* NO-OP */ -# define CODEC2(P,D,N,X,E,O) O=(char*)D -#endif - -/* -** The maximum allowed sector size. 64KiB. If the xSectorsize() method -** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. -** This could conceivably cause corruption following a power failure on -** such a system. This is currently an undocumented limit. -*/ -#define MAX_SECTOR_SIZE 0x10000 - -/* -** An instance of the following structure is allocated for each active -** savepoint and statement transaction in the system. All such structures -** are stored in the Pager.aSavepoint[] array, which is allocated and -** resized using sqlite3Realloc(). -** -** When a savepoint is created, the PagerSavepoint.iHdrOffset field is -** set to 0. If a journal-header is written into the main journal while -** the savepoint is active, then iHdrOffset is set to the byte offset -** immediately following the last journal record written into the main -** journal before the journal-header. This is required during savepoint -** rollback (see pagerPlaybackSavepoint()). -*/ -typedef struct PagerSavepoint PagerSavepoint; -struct PagerSavepoint { - i64 iOffset; /* Starting offset in main journal */ - i64 iHdrOffset; /* See above */ - Bitvec *pInSavepoint; /* Set of pages in this savepoint */ - Pgno nOrig; /* Original number of pages in file */ - Pgno iSubRec; /* Index of first record in sub-journal */ -#ifndef SQLITE_OMIT_WAL - u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ -#endif -}; - -/* -** A open page cache is an instance of struct Pager. A description of -** some of the more important member variables follows: -** -** eState -** -** The current 'state' of the pager object. See the comment and state -** diagram above for a description of the pager state. -** -** eLock -** -** For a real on-disk database, the current lock held on the database file - -** NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. -** -** For a temporary or in-memory database (neither of which require any -** locks), this variable is always set to EXCLUSIVE_LOCK. Since such -** databases always have Pager.exclusiveMode==1, this tricks the pager -** logic into thinking that it already has all the locks it will ever -** need (and no reason to release them). -** -** In some (obscure) circumstances, this variable may also be set to -** UNKNOWN_LOCK. See the comment above the #define of UNKNOWN_LOCK for -** details. -** -** changeCountDone -** -** This boolean variable is used to make sure that the change-counter -** (the 4-byte header field at byte offset 24 of the database file) is -** not updated more often than necessary. -** -** It is set to true when the change-counter field is updated, which -** can only happen if an exclusive lock is held on the database file. -** It is cleared (set to false) whenever an exclusive lock is -** relinquished on the database file. Each time a transaction is committed, -** The changeCountDone flag is inspected. If it is true, the work of -** updating the change-counter is omitted for the current transaction. -** -** This mechanism means that when running in exclusive mode, a connection -** need only update the change-counter once, for the first transaction -** committed. -** -** setMaster -** -** When PagerCommitPhaseOne() is called to commit a transaction, it may -** (or may not) specify a master-journal name to be written into the -** journal file before it is synced to disk. -** -** Whether or not a journal file contains a master-journal pointer affects -** the way in which the journal file is finalized after the transaction is -** committed or rolled back when running in "journal_mode=PERSIST" mode. -** If a journal file does not contain a master-journal pointer, it is -** finalized by overwriting the first journal header with zeroes. If -** it does contain a master-journal pointer the journal file is finalized -** by truncating it to zero bytes, just as if the connection were -** running in "journal_mode=truncate" mode. -** -** Journal files that contain master journal pointers cannot be finalized -** simply by overwriting the first journal-header with zeroes, as the -** master journal pointer could interfere with hot-journal rollback of any -** subsequently interrupted transaction that reuses the journal file. -** -** The flag is cleared as soon as the journal file is finalized (either -** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the -** journal file from being successfully finalized, the setMaster flag -** is cleared anyway (and the pager will move to ERROR state). -** -** doNotSpill, doNotSyncSpill -** -** These two boolean variables control the behavior of cache-spills -** (calls made by the pcache module to the pagerStress() routine to -** write cached data to the file-system in order to free up memory). -** -** When doNotSpill is non-zero, writing to the database from pagerStress() -** is disabled altogether. This is done in a very obscure case that -** comes up during savepoint rollback that requires the pcache module -** to allocate a new page to prevent the journal file from being written -** while it is being traversed by code in pager_playback(). -** -** If doNotSyncSpill is non-zero, writing to the database from pagerStress() -** is permitted, but syncing the journal file is not. This flag is set -** by sqlite3PagerWrite() when the file-system sector-size is larger than -** the database page-size in order to prevent a journal sync from happening -** in between the journalling of two pages on the same sector. -** -** subjInMemory -** -** This is a boolean variable. If true, then any required sub-journal -** is opened as an in-memory journal file. If false, then in-memory -** sub-journals are only used for in-memory pager files. -** -** This variable is updated by the upper layer each time a new -** write-transaction is opened. -** -** dbSize, dbOrigSize, dbFileSize -** -** Variable dbSize is set to the number of pages in the database file. -** It is valid in PAGER_READER and higher states (all states except for -** OPEN and ERROR). -** -** dbSize is set based on the size of the database file, which may be -** larger than the size of the database (the value stored at offset -** 28 of the database header by the btree). If the size of the file -** is not an integer multiple of the page-size, the value stored in -** dbSize is rounded down (i.e. a 5KB file with 2K page-size has dbSize==2). -** Except, any file that is greater than 0 bytes in size is considered -** to have at least one page. (i.e. a 1KB file with 2K page-size leads -** to dbSize==1). -** -** During a write-transaction, if pages with page-numbers greater than -** dbSize are modified in the cache, dbSize is updated accordingly. -** Similarly, if the database is truncated using PagerTruncateImage(), -** dbSize is updated. -** -** Variables dbOrigSize and dbFileSize are valid in states -** PAGER_WRITER_LOCKED and higher. dbOrigSize is a copy of the dbSize -** variable at the start of the transaction. It is used during rollback, -** and to determine whether or not pages need to be journalled before -** being modified. -** -** Throughout a write-transaction, dbFileSize contains the size of -** the file on disk in pages. It is set to a copy of dbSize when the -** write-transaction is first opened, and updated when VFS calls are made -** to write or truncate the database file on disk. -** -** The only reason the dbFileSize variable is required is to suppress -** unnecessary calls to xTruncate() after committing a transaction. If, -** when a transaction is committed, the dbFileSize variable indicates -** that the database file is larger than the database image (Pager.dbSize), -** pager_truncate() is called. The pager_truncate() call uses xFilesize() -** to measure the database file on disk, and then truncates it if required. -** dbFileSize is not used when rolling back a transaction. In this case -** pager_truncate() is called unconditionally (which means there may be -** a call to xFilesize() that is not strictly required). In either case, -** pager_truncate() may cause the file to become smaller or larger. -** -** dbHintSize -** -** The dbHintSize variable is used to limit the number of calls made to -** the VFS xFileControl(FCNTL_SIZE_HINT) method. -** -** dbHintSize is set to a copy of the dbSize variable when a -** write-transaction is opened (at the same time as dbFileSize and -** dbOrigSize). If the xFileControl(FCNTL_SIZE_HINT) method is called, -** dbHintSize is increased to the number of pages that correspond to the -** size-hint passed to the method call. See pager_write_pagelist() for -** details. -** -** errCode -** -** The Pager.errCode variable is only ever used in PAGER_ERROR state. It -** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode -** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX -** sub-codes. -*/ -struct Pager { - sqlite3_vfs *pVfs; /* OS functions to use for IO */ - u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ - u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ - u8 useJournal; /* Use a rollback journal on this file */ - u8 noSync; /* Do not sync the journal if true */ - u8 fullSync; /* Do extra syncs of the journal for robustness */ - u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */ - u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */ - u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */ - u8 tempFile; /* zFilename is a temporary file */ - u8 readOnly; /* True for a read-only database */ - u8 memDb; /* True to inhibit all file I/O */ - - /************************************************************************** - ** The following block contains those class members that change during - ** routine opertion. Class members not in this block are either fixed - ** when the pager is first created or else only change when there is a - ** significant mode change (such as changing the page_size, locking_mode, - ** or the journal_mode). From another view, these class members describe - ** the "state" of the pager, while other class members describe the - ** "configuration" of the pager. - */ - u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */ - u8 eLock; /* Current lock held on database file */ - u8 changeCountDone; /* Set after incrementing the change-counter */ - u8 setMaster; /* True if a m-j name has been written to jrnl */ - u8 doNotSpill; /* Do not spill the cache when non-zero */ - u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */ - u8 subjInMemory; /* True to use in-memory sub-journals */ - Pgno dbSize; /* Number of pages in the database */ - Pgno dbOrigSize; /* dbSize before the current transaction */ - Pgno dbFileSize; /* Number of pages in the database file */ - Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ - int errCode; /* One of several kinds of errors */ - int nRec; /* Pages journalled since last j-header written */ - u32 cksumInit; /* Quasi-random value added to every checksum */ - u32 nSubRec; /* Number of records written to sub-journal */ - Bitvec *pInJournal; /* One bit for each page in the database file */ - sqlite3_file *fd; /* File descriptor for database */ - sqlite3_file *jfd; /* File descriptor for main journal */ - sqlite3_file *sjfd; /* File descriptor for sub-journal */ - i64 journalOff; /* Current write offset in the journal file */ - i64 journalHdr; /* Byte offset to previous journal header */ - sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ - PagerSavepoint *aSavepoint; /* Array of active savepoints */ - int nSavepoint; /* Number of elements in aSavepoint[] */ - char dbFileVers[16]; /* Changes whenever database file changes */ - /* - ** End of the routinely-changing class members - ***************************************************************************/ - - u16 nExtra; /* Add this many bytes to each in-memory page */ - i16 nReserve; /* Number of unused bytes at end of each page */ - u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */ - u32 sectorSize; /* Assumed sector size during rollback */ - int pageSize; /* Number of bytes in a page */ - Pgno mxPgno; /* Maximum allowed size of the database */ - i64 journalSizeLimit; /* Size limit for persistent journal files */ - char *zFilename; /* Name of the database file */ - char *zJournal; /* Name of the journal file */ - int (*xBusyHandler)(void*); /* Function to call when busy */ - void *pBusyHandlerArg; /* Context argument for xBusyHandler */ - int aStat[3]; /* Total cache hits, misses and writes */ -#ifdef SQLITE_TEST - int nRead; /* Database pages read */ -#endif - void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ -#ifdef SQLITE_HAS_CODEC - void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ - void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ - void (*xCodecFree)(void*); /* Destructor for the codec */ - void *pCodec; /* First argument to xCodec... methods */ -#endif - char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ - PCache *pPCache; /* Pointer to page cache object */ -#ifndef SQLITE_OMIT_WAL - Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ - char *zWal; /* File name for write-ahead log */ -#endif -}; - -/* -** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains -** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS -** or CACHE_WRITE to sqlite3_db_status(). -*/ -#define PAGER_STAT_HIT 0 -#define PAGER_STAT_MISS 1 -#define PAGER_STAT_WRITE 2 - -/* -** The following global variables hold counters used for -** testing purposes only. These variables do not exist in -** a non-testing build. These variables are not thread-safe. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_pager_readdb_count = 0; /* Number of full pages read from DB */ -SQLITE_API int sqlite3_pager_writedb_count = 0; /* Number of full pages written to DB */ -SQLITE_API int sqlite3_pager_writej_count = 0; /* Number of pages written to journal */ -# define PAGER_INCR(v) v++ -#else -# define PAGER_INCR(v) -#endif - - - -/* -** Journal files begin with the following magic string. The data -** was obtained from /dev/random. It is used only as a sanity check. -** -** Since version 2.8.0, the journal format contains additional sanity -** checking information. If the power fails while the journal is being -** written, semi-random garbage data might appear in the journal -** file after power is restored. If an attempt is then made -** to roll the journal back, the database could be corrupted. The additional -** sanity checking data is an attempt to discover the garbage in the -** journal and ignore it. -** -** The sanity checking information for the new journal format consists -** of a 32-bit checksum on each page of data. The checksum covers both -** the page number and the pPager->pageSize bytes of data for the page. -** This cksum is initialized to a 32-bit random value that appears in the -** journal file right after the header. The random initializer is important, -** because garbage data that appears at the end of a journal is likely -** data that was once in other files that have now been deleted. If the -** garbage data came from an obsolete journal file, the checksums might -** be correct. But by initializing the checksum to random value which -** is different for every journal, we minimize that risk. -*/ -static const unsigned char aJournalMagic[] = { - 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7, -}; - -/* -** The size of the of each page record in the journal is given by -** the following macro. -*/ -#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8) - -/* -** The journal header size for this pager. This is usually the same -** size as a single disk sector. See also setSectorSize(). -*/ -#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize) - -/* -** The macro MEMDB is true if we are dealing with an in-memory database. -** We do this as a macro so that if the SQLITE_OMIT_MEMORYDB macro is set, -** the value of MEMDB will be a constant and the compiler will optimize -** out code that would never execute. -*/ -#ifdef SQLITE_OMIT_MEMORYDB -# define MEMDB 0 -#else -# define MEMDB pPager->memDb -#endif - -/* -** The maximum legal page number is (2^31 - 1). -*/ -#define PAGER_MAX_PGNO 2147483647 - -/* -** The argument to this macro is a file descriptor (type sqlite3_file*). -** Return 0 if it is not open, or non-zero (but not 1) if it is. -** -** This is so that expressions can be written as: -** -** if( isOpen(pPager->jfd) ){ ... -** -** instead of -** -** if( pPager->jfd->pMethods ){ ... -*/ -#define isOpen(pFd) ((pFd)->pMethods) - -/* -** Return true if this pager uses a write-ahead log instead of the usual -** rollback journal. Otherwise false. -*/ -#ifndef SQLITE_OMIT_WAL -static int pagerUseWal(Pager *pPager){ - return (pPager->pWal!=0); -} -#else -# define pagerUseWal(x) 0 -# define pagerRollbackWal(x) 0 -# define pagerWalFrames(v,w,x,y) 0 -# define pagerOpenWalIfPresent(z) SQLITE_OK -# define pagerBeginReadTransaction(z) SQLITE_OK -#endif - -#ifndef NDEBUG -/* -** Usage: -** -** assert( assert_pager_state(pPager) ); -** -** This function runs many asserts to try to find inconsistencies in -** the internal state of the Pager object. -*/ -static int assert_pager_state(Pager *p){ - Pager *pPager = p; - - /* State must be valid. */ - assert( p->eState==PAGER_OPEN - || p->eState==PAGER_READER - || p->eState==PAGER_WRITER_LOCKED - || p->eState==PAGER_WRITER_CACHEMOD - || p->eState==PAGER_WRITER_DBMOD - || p->eState==PAGER_WRITER_FINISHED - || p->eState==PAGER_ERROR - ); - - /* Regardless of the current state, a temp-file connection always behaves - ** as if it has an exclusive lock on the database file. It never updates - ** the change-counter field, so the changeCountDone flag is always set. - */ - assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK ); - assert( p->tempFile==0 || pPager->changeCountDone ); - - /* If the useJournal flag is clear, the journal-mode must be "OFF". - ** And if the journal-mode is "OFF", the journal file must not be open. - */ - assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal ); - assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) ); - - /* Check that MEMDB implies noSync. And an in-memory journal. Since - ** this means an in-memory pager performs no IO at all, it cannot encounter - ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing - ** a journal file. (although the in-memory journal implementation may - ** return SQLITE_IOERR_NOMEM while the journal file is being written). It - ** is therefore not possible for an in-memory pager to enter the ERROR - ** state. - */ - if( MEMDB ){ - assert( p->noSync ); - assert( p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_MEMORY - ); - assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN ); - assert( pagerUseWal(p)==0 ); - } - - /* If changeCountDone is set, a RESERVED lock or greater must be held - ** on the file. - */ - assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK ); - assert( p->eLock!=PENDING_LOCK ); - - switch( p->eState ){ - case PAGER_OPEN: - assert( !MEMDB ); - assert( pPager->errCode==SQLITE_OK ); - assert( sqlite3PcacheRefCount(pPager->pPCache)==0 || pPager->tempFile ); - break; - - case PAGER_READER: - assert( pPager->errCode==SQLITE_OK ); - assert( p->eLock!=UNKNOWN_LOCK ); - assert( p->eLock>=SHARED_LOCK ); - break; - - case PAGER_WRITER_LOCKED: - assert( p->eLock!=UNKNOWN_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - if( !pagerUseWal(pPager) ){ - assert( p->eLock>=RESERVED_LOCK ); - } - assert( pPager->dbSize==pPager->dbOrigSize ); - assert( pPager->dbOrigSize==pPager->dbFileSize ); - assert( pPager->dbOrigSize==pPager->dbHintSize ); - assert( pPager->setMaster==0 ); - break; - - case PAGER_WRITER_CACHEMOD: - assert( p->eLock!=UNKNOWN_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - if( !pagerUseWal(pPager) ){ - /* It is possible that if journal_mode=wal here that neither the - ** journal file nor the WAL file are open. This happens during - ** a rollback transaction that switches from journal_mode=off - ** to journal_mode=wal. - */ - assert( p->eLock>=RESERVED_LOCK ); - assert( isOpen(p->jfd) - || p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_WAL - ); - } - assert( pPager->dbOrigSize==pPager->dbFileSize ); - assert( pPager->dbOrigSize==pPager->dbHintSize ); - break; - - case PAGER_WRITER_DBMOD: - assert( p->eLock==EXCLUSIVE_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - assert( !pagerUseWal(pPager) ); - assert( p->eLock>=EXCLUSIVE_LOCK ); - assert( isOpen(p->jfd) - || p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_WAL - ); - assert( pPager->dbOrigSize<=pPager->dbHintSize ); - break; - - case PAGER_WRITER_FINISHED: - assert( p->eLock==EXCLUSIVE_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - assert( !pagerUseWal(pPager) ); - assert( isOpen(p->jfd) - || p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_WAL - ); - break; - - case PAGER_ERROR: - /* There must be at least one outstanding reference to the pager if - ** in ERROR state. Otherwise the pager should have already dropped - ** back to OPEN state. - */ - assert( pPager->errCode!=SQLITE_OK ); - assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); - break; - } - - return 1; -} -#endif /* ifndef NDEBUG */ - -#ifdef SQLITE_DEBUG -/* -** Return a pointer to a human readable string in a static buffer -** containing the state of the Pager object passed as an argument. This -** is intended to be used within debuggers. For example, as an alternative -** to "print *pPager" in gdb: -** -** (gdb) printf "%s", print_pager_state(pPager) -*/ -static char *print_pager_state(Pager *p){ - static char zRet[1024]; - - sqlite3_snprintf(1024, zRet, - "Filename: %s\n" - "State: %s errCode=%d\n" - "Lock: %s\n" - "Locking mode: locking_mode=%s\n" - "Journal mode: journal_mode=%s\n" - "Backing store: tempFile=%d memDb=%d useJournal=%d\n" - "Journal: journalOff=%lld journalHdr=%lld\n" - "Size: dbsize=%d dbOrigSize=%d dbFileSize=%d\n" - , p->zFilename - , p->eState==PAGER_OPEN ? "OPEN" : - p->eState==PAGER_READER ? "READER" : - p->eState==PAGER_WRITER_LOCKED ? "WRITER_LOCKED" : - p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" : - p->eState==PAGER_WRITER_DBMOD ? "WRITER_DBMOD" : - p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" : - p->eState==PAGER_ERROR ? "ERROR" : "?error?" - , (int)p->errCode - , p->eLock==NO_LOCK ? "NO_LOCK" : - p->eLock==RESERVED_LOCK ? "RESERVED" : - p->eLock==EXCLUSIVE_LOCK ? "EXCLUSIVE" : - p->eLock==SHARED_LOCK ? "SHARED" : - p->eLock==UNKNOWN_LOCK ? "UNKNOWN" : "?error?" - , p->exclusiveMode ? "exclusive" : "normal" - , p->journalMode==PAGER_JOURNALMODE_MEMORY ? "memory" : - p->journalMode==PAGER_JOURNALMODE_OFF ? "off" : - p->journalMode==PAGER_JOURNALMODE_DELETE ? "delete" : - p->journalMode==PAGER_JOURNALMODE_PERSIST ? "persist" : - p->journalMode==PAGER_JOURNALMODE_TRUNCATE ? "truncate" : - p->journalMode==PAGER_JOURNALMODE_WAL ? "wal" : "?error?" - , (int)p->tempFile, (int)p->memDb, (int)p->useJournal - , p->journalOff, p->journalHdr - , (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize - ); - - return zRet; -} -#endif - -/* -** Return true if it is necessary to write page *pPg into the sub-journal. -** A page needs to be written into the sub-journal if there exists one -** or more open savepoints for which: -** -** * The page-number is less than or equal to PagerSavepoint.nOrig, and -** * The bit corresponding to the page-number is not set in -** PagerSavepoint.pInSavepoint. -*/ -static int subjRequiresPage(PgHdr *pPg){ - Pgno pgno = pPg->pgno; - Pager *pPager = pPg->pPager; - int i; - for(i=0; inSavepoint; i++){ - PagerSavepoint *p = &pPager->aSavepoint[i]; - if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){ - return 1; - } - } - return 0; -} - -/* -** Return true if the page is already in the journal file. -*/ -static int pageInJournal(PgHdr *pPg){ - return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno); -} - -/* -** Read a 32-bit integer from the given file descriptor. Store the integer -** that is read in *pRes. Return SQLITE_OK if everything worked, or an -** error code is something goes wrong. -** -** All values are stored on disk as big-endian. -*/ -static int read32bits(sqlite3_file *fd, i64 offset, u32 *pRes){ - unsigned char ac[4]; - int rc = sqlite3OsRead(fd, ac, sizeof(ac), offset); - if( rc==SQLITE_OK ){ - *pRes = sqlite3Get4byte(ac); - } - return rc; -} - -/* -** Write a 32-bit integer into a string buffer in big-endian byte order. -*/ -#define put32bits(A,B) sqlite3Put4byte((u8*)A,B) - - -/* -** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK -** on success or an error code is something goes wrong. -*/ -static int write32bits(sqlite3_file *fd, i64 offset, u32 val){ - char ac[4]; - put32bits(ac, val); - return sqlite3OsWrite(fd, ac, 4, offset); -} - -/* -** Unlock the database file to level eLock, which must be either NO_LOCK -** or SHARED_LOCK. Regardless of whether or not the call to xUnlock() -** succeeds, set the Pager.eLock variable to match the (attempted) new lock. -** -** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is -** called, do not modify it. See the comment above the #define of -** UNKNOWN_LOCK for an explanation of this. -*/ -static int pagerUnlockDb(Pager *pPager, int eLock){ - int rc = SQLITE_OK; - - assert( !pPager->exclusiveMode || pPager->eLock==eLock ); - assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); - assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); - if( isOpen(pPager->fd) ){ - assert( pPager->eLock>=eLock ); - rc = sqlite3OsUnlock(pPager->fd, eLock); - if( pPager->eLock!=UNKNOWN_LOCK ){ - pPager->eLock = (u8)eLock; - } - IOTRACE(("UNLOCK %p %d\n", pPager, eLock)) - } - return rc; -} - -/* -** Lock the database file to level eLock, which must be either SHARED_LOCK, -** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the -** Pager.eLock variable to the new locking state. -** -** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is -** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. -** See the comment above the #define of UNKNOWN_LOCK for an explanation -** of this. -*/ -static int pagerLockDb(Pager *pPager, int eLock){ - int rc = SQLITE_OK; - - assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK ); - if( pPager->eLockeLock==UNKNOWN_LOCK ){ - rc = sqlite3OsLock(pPager->fd, eLock); - if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){ - pPager->eLock = (u8)eLock; - IOTRACE(("LOCK %p %d\n", pPager, eLock)) - } - } - return rc; -} - -/* -** This function determines whether or not the atomic-write optimization -** can be used with this pager. The optimization can be used if: -** -** (a) the value returned by OsDeviceCharacteristics() indicates that -** a database page may be written atomically, and -** (b) the value returned by OsSectorSize() is less than or equal -** to the page size. -** -** The optimization is also always enabled for temporary files. It is -** an error to call this function if pPager is opened on an in-memory -** database. -** -** If the optimization cannot be used, 0 is returned. If it can be used, -** then the value returned is the size of the journal file when it -** contains rollback data for exactly one page. -*/ -#ifdef SQLITE_ENABLE_ATOMIC_WRITE -static int jrnlBufferSize(Pager *pPager){ - assert( !MEMDB ); - if( !pPager->tempFile ){ - int dc; /* Device characteristics */ - int nSector; /* Sector size */ - int szPage; /* Page size */ - - assert( isOpen(pPager->fd) ); - dc = sqlite3OsDeviceCharacteristics(pPager->fd); - nSector = pPager->sectorSize; - szPage = pPager->pageSize; - - assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); - assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); - if( 0==(dc&(SQLITE_IOCAP_ATOMIC|(szPage>>8)) || nSector>szPage) ){ - return 0; - } - } - - return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); -} -#endif - -/* -** If SQLITE_CHECK_PAGES is defined then we do some sanity checking -** on the cache using a hash function. This is used for testing -** and debugging only. -*/ -#ifdef SQLITE_CHECK_PAGES -/* -** Return a 32-bit hash of the page data for pPage. -*/ -static u32 pager_datahash(int nByte, unsigned char *pData){ - u32 hash = 0; - int i; - for(i=0; ipPager->pageSize, (unsigned char *)pPage->pData); -} -static void pager_set_pagehash(PgHdr *pPage){ - pPage->pageHash = pager_pagehash(pPage); -} - -/* -** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES -** is defined, and NDEBUG is not defined, an assert() statement checks -** that the page is either dirty or still matches the calculated page-hash. -*/ -#define CHECK_PAGE(x) checkPage(x) -static void checkPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - assert( pPager->eState!=PAGER_ERROR ); - assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) ); -} - -#else -#define pager_datahash(X,Y) 0 -#define pager_pagehash(X) 0 -#define pager_set_pagehash(X) -#define CHECK_PAGE(x) -#endif /* SQLITE_CHECK_PAGES */ - -/* -** When this is called the journal file for pager pPager must be open. -** This function attempts to read a master journal file name from the -** end of the file and, if successful, copies it into memory supplied -** by the caller. See comments above writeMasterJournal() for the format -** used to store a master journal file name at the end of a journal file. -** -** zMaster must point to a buffer of at least nMaster bytes allocated by -** the caller. This should be sqlite3_vfs.mxPathname+1 (to ensure there is -** enough space to write the master journal name). If the master journal -** name in the journal is longer than nMaster bytes (including a -** nul-terminator), then this is handled as if no master journal name -** were present in the journal. -** -** If a master journal file name is present at the end of the journal -** file, then it is copied into the buffer pointed to by zMaster. A -** nul-terminator byte is appended to the buffer following the master -** journal file name. -** -** If it is determined that no master journal file name is present -** zMaster[0] is set to 0 and SQLITE_OK returned. -** -** If an error occurs while reading from the journal file, an SQLite -** error code is returned. -*/ -static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){ - int rc; /* Return code */ - u32 len; /* Length in bytes of master journal name */ - i64 szJ; /* Total size in bytes of journal file pJrnl */ - u32 cksum; /* MJ checksum value read from journal */ - u32 u; /* Unsigned loop counter */ - unsigned char aMagic[8]; /* A buffer to hold the magic header */ - zMaster[0] = '\0'; - - if( SQLITE_OK!=(rc = sqlite3OsFileSize(pJrnl, &szJ)) - || szJ<16 - || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) - || len>=nMaster - || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) - || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) - || memcmp(aMagic, aJournalMagic, 8) - || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len)) - ){ - return rc; - } - - /* See if the checksum matches the master journal name */ - for(u=0; ujournalOff, assuming a sector -** size of pPager->sectorSize bytes. -** -** i.e for a sector size of 512: -** -** Pager.journalOff Return value -** --------------------------------------- -** 0 0 -** 512 512 -** 100 512 -** 2000 2048 -** -*/ -static i64 journalHdrOffset(Pager *pPager){ - i64 offset = 0; - i64 c = pPager->journalOff; - if( c ){ - offset = ((c-1)/JOURNAL_HDR_SZ(pPager) + 1) * JOURNAL_HDR_SZ(pPager); - } - assert( offset%JOURNAL_HDR_SZ(pPager)==0 ); - assert( offset>=c ); - assert( (offset-c)jfd) ); - if( pPager->journalOff ){ - const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */ - - IOTRACE(("JZEROHDR %p\n", pPager)) - if( doTruncate || iLimit==0 ){ - rc = sqlite3OsTruncate(pPager->jfd, 0); - }else{ - static const char zeroHdr[28] = {0}; - rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0); - } - if( rc==SQLITE_OK && !pPager->noSync ){ - rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags); - } - - /* At this point the transaction is committed but the write lock - ** is still held on the file. If there is a size limit configured for - ** the persistent journal and the journal file currently consumes more - ** space than that limit allows for, truncate it now. There is no need - ** to sync the file following this operation. - */ - if( rc==SQLITE_OK && iLimit>0 ){ - i64 sz; - rc = sqlite3OsFileSize(pPager->jfd, &sz); - if( rc==SQLITE_OK && sz>iLimit ){ - rc = sqlite3OsTruncate(pPager->jfd, iLimit); - } - } - } - return rc; -} - -/* -** The journal file must be open when this routine is called. A journal -** header (JOURNAL_HDR_SZ bytes) is written into the journal file at the -** current location. -** -** The format for the journal header is as follows: -** - 8 bytes: Magic identifying journal format. -** - 4 bytes: Number of records in journal, or -1 no-sync mode is on. -** - 4 bytes: Random number used for page hash. -** - 4 bytes: Initial database page count. -** - 4 bytes: Sector size used by the process that wrote this journal. -** - 4 bytes: Database page size. -** -** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space. -*/ -static int writeJournalHdr(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */ - u32 nHeader = (u32)pPager->pageSize;/* Size of buffer pointed to by zHeader */ - u32 nWrite; /* Bytes of header sector written */ - int ii; /* Loop counter */ - - assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ - - if( nHeader>JOURNAL_HDR_SZ(pPager) ){ - nHeader = JOURNAL_HDR_SZ(pPager); - } - - /* If there are active savepoints and any of them were created - ** since the most recent journal header was written, update the - ** PagerSavepoint.iHdrOffset fields now. - */ - for(ii=0; iinSavepoint; ii++){ - if( pPager->aSavepoint[ii].iHdrOffset==0 ){ - pPager->aSavepoint[ii].iHdrOffset = pPager->journalOff; - } - } - - pPager->journalHdr = pPager->journalOff = journalHdrOffset(pPager); - - /* - ** Write the nRec Field - the number of page records that follow this - ** journal header. Normally, zero is written to this value at this time. - ** After the records are added to the journal (and the journal synced, - ** if in full-sync mode), the zero is overwritten with the true number - ** of records (see syncJournal()). - ** - ** A faster alternative is to write 0xFFFFFFFF to the nRec field. When - ** reading the journal this value tells SQLite to assume that the - ** rest of the journal file contains valid page records. This assumption - ** is dangerous, as if a failure occurred whilst writing to the journal - ** file it may contain some garbage data. There are two scenarios - ** where this risk can be ignored: - ** - ** * When the pager is in no-sync mode. Corruption can follow a - ** power failure in this case anyway. - ** - ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees - ** that garbage data is never appended to the journal file. - */ - assert( isOpen(pPager->fd) || pPager->noSync ); - if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY) - || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) - ){ - memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); - put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff); - }else{ - memset(zHeader, 0, sizeof(aJournalMagic)+4); - } - - /* The random check-hash initializer */ - sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); - put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); - /* The initial database size */ - put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize); - /* The assumed sector size for this process */ - put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize); - - /* The page size */ - put32bits(&zHeader[sizeof(aJournalMagic)+16], pPager->pageSize); - - /* Initializing the tail of the buffer is not necessary. Everything - ** works find if the following memset() is omitted. But initializing - ** the memory prevents valgrind from complaining, so we are willing to - ** take the performance hit. - */ - memset(&zHeader[sizeof(aJournalMagic)+20], 0, - nHeader-(sizeof(aJournalMagic)+20)); - - /* In theory, it is only necessary to write the 28 bytes that the - ** journal header consumes to the journal file here. Then increment the - ** Pager.journalOff variable by JOURNAL_HDR_SZ so that the next - ** record is written to the following sector (leaving a gap in the file - ** that will be implicitly filled in by the OS). - ** - ** However it has been discovered that on some systems this pattern can - ** be significantly slower than contiguously writing data to the file, - ** even if that means explicitly writing data to the block of - ** (JOURNAL_HDR_SZ - 28) bytes that will not be used. So that is what - ** is done. - ** - ** The loop is required here in case the sector-size is larger than the - ** database page size. Since the zHeader buffer is only Pager.pageSize - ** bytes in size, more than one call to sqlite3OsWrite() may be required - ** to populate the entire journal header sector. - */ - for(nWrite=0; rc==SQLITE_OK&&nWritejournalHdr, nHeader)) - rc = sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff); - assert( pPager->journalHdr <= pPager->journalOff ); - pPager->journalOff += nHeader; - } - - return rc; -} - -/* -** The journal file must be open when this is called. A journal header file -** (JOURNAL_HDR_SZ bytes) is read from the current location in the journal -** file. The current location in the journal file is given by -** pPager->journalOff. See comments above function writeJournalHdr() for -** a description of the journal header format. -** -** If the header is read successfully, *pNRec is set to the number of -** page records following this header and *pDbSize is set to the size of the -** database before the transaction began, in pages. Also, pPager->cksumInit -** is set to the value read from the journal header. SQLITE_OK is returned -** in this case. -** -** If the journal header file appears to be corrupted, SQLITE_DONE is -** returned and *pNRec and *PDbSize are undefined. If JOURNAL_HDR_SZ bytes -** cannot be read from the journal file an error code is returned. -*/ -static int readJournalHdr( - Pager *pPager, /* Pager object */ - int isHot, - i64 journalSize, /* Size of the open journal file in bytes */ - u32 *pNRec, /* OUT: Value read from the nRec field */ - u32 *pDbSize /* OUT: Value of original database size field */ -){ - int rc; /* Return code */ - unsigned char aMagic[8]; /* A buffer to hold the magic header */ - i64 iHdrOff; /* Offset of journal header being read */ - - assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ - - /* Advance Pager.journalOff to the start of the next sector. If the - ** journal file is too small for there to be a header stored at this - ** point, return SQLITE_DONE. - */ - pPager->journalOff = journalHdrOffset(pPager); - if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){ - return SQLITE_DONE; - } - iHdrOff = pPager->journalOff; - - /* Read in the first 8 bytes of the journal header. If they do not match - ** the magic string found at the start of each journal header, return - ** SQLITE_DONE. If an IO error occurs, return an error code. Otherwise, - ** proceed. - */ - if( isHot || iHdrOff!=pPager->journalHdr ){ - rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), iHdrOff); - if( rc ){ - return rc; - } - if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ - return SQLITE_DONE; - } - } - - /* Read the first three 32-bit fields of the journal header: The nRec - ** field, the checksum-initializer and the database size at the start - ** of the transaction. Return an error code if anything goes wrong. - */ - if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+8, pNRec)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+12, &pPager->cksumInit)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+16, pDbSize)) - ){ - return rc; - } - - if( pPager->journalOff==0 ){ - u32 iPageSize; /* Page-size field of journal header */ - u32 iSectorSize; /* Sector-size field of journal header */ - - /* Read the page-size and sector-size journal header fields. */ - if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize)) - ){ - return rc; - } - - /* Versions of SQLite prior to 3.5.8 set the page-size field of the - ** journal header to zero. In this case, assume that the Pager.pageSize - ** variable is already set to the correct page size. - */ - if( iPageSize==0 ){ - iPageSize = pPager->pageSize; - } - - /* Check that the values read from the page-size and sector-size fields - ** are within range. To be 'in range', both values need to be a power - ** of two greater than or equal to 512 or 32, and not greater than their - ** respective compile time maximum limits. - */ - if( iPageSize<512 || iSectorSize<32 - || iPageSize>SQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE - || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0 - ){ - /* If the either the page-size or sector-size in the journal-header is - ** invalid, then the process that wrote the journal-header must have - ** crashed before the header was synced. In this case stop reading - ** the journal file here. - */ - return SQLITE_DONE; - } - - /* Update the page-size to match the value read from the journal. - ** Use a testcase() macro to make sure that malloc failure within - ** PagerSetPagesize() is tested. - */ - rc = sqlite3PagerSetPagesize(pPager, &iPageSize, -1); - testcase( rc!=SQLITE_OK ); - - /* Update the assumed sector-size to match the value used by - ** the process that created this journal. If this journal was - ** created by a process other than this one, then this routine - ** is being called from within pager_playback(). The local value - ** of Pager.sectorSize is restored at the end of that routine. - */ - pPager->sectorSize = iSectorSize; - } - - pPager->journalOff += JOURNAL_HDR_SZ(pPager); - return rc; -} - - -/* -** Write the supplied master journal name into the journal file for pager -** pPager at the current location. The master journal name must be the last -** thing written to a journal file. If the pager is in full-sync mode, the -** journal file descriptor is advanced to the next sector boundary before -** anything is written. The format is: -** -** + 4 bytes: PAGER_MJ_PGNO. -** + N bytes: Master journal filename in utf-8. -** + 4 bytes: N (length of master journal name in bytes, no nul-terminator). -** + 4 bytes: Master journal name checksum. -** + 8 bytes: aJournalMagic[]. -** -** The master journal page checksum is the sum of the bytes in the master -** journal name, where each byte is interpreted as a signed 8-bit integer. -** -** If zMaster is a NULL pointer (occurs for a single database transaction), -** this call is a no-op. -*/ -static int writeMasterJournal(Pager *pPager, const char *zMaster){ - int rc; /* Return code */ - int nMaster; /* Length of string zMaster */ - i64 iHdrOff; /* Offset of header in journal file */ - i64 jrnlSize; /* Size of journal file on disk */ - u32 cksum = 0; /* Checksum of string zMaster */ - - assert( pPager->setMaster==0 ); - assert( !pagerUseWal(pPager) ); - - if( !zMaster - || pPager->journalMode==PAGER_JOURNALMODE_MEMORY - || pPager->journalMode==PAGER_JOURNALMODE_OFF - ){ - return SQLITE_OK; - } - pPager->setMaster = 1; - assert( isOpen(pPager->jfd) ); - assert( pPager->journalHdr <= pPager->journalOff ); - - /* Calculate the length in bytes and the checksum of zMaster */ - for(nMaster=0; zMaster[nMaster]; nMaster++){ - cksum += zMaster[nMaster]; - } - - /* If in full-sync mode, advance to the next disk sector before writing - ** the master journal name. This is in case the previous page written to - ** the journal has already been synced. - */ - if( pPager->fullSync ){ - pPager->journalOff = journalHdrOffset(pPager); - } - iHdrOff = pPager->journalOff; - - /* Write the master journal data to the end of the journal file. If - ** an error occurs, return the error code to the caller. - */ - if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_MJ_PGNO(pPager)))) - || (0 != (rc = sqlite3OsWrite(pPager->jfd, zMaster, nMaster, iHdrOff+4))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum))) - || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8))) - ){ - return rc; - } - pPager->journalOff += (nMaster+20); - - /* If the pager is in peristent-journal mode, then the physical - ** journal-file may extend past the end of the master-journal name - ** and 8 bytes of magic data just written to the file. This is - ** dangerous because the code to rollback a hot-journal file - ** will not be able to find the master-journal name to determine - ** whether or not the journal is hot. - ** - ** Easiest thing to do in this scenario is to truncate the journal - ** file to the required size. - */ - if( SQLITE_OK==(rc = sqlite3OsFileSize(pPager->jfd, &jrnlSize)) - && jrnlSize>pPager->journalOff - ){ - rc = sqlite3OsTruncate(pPager->jfd, pPager->journalOff); - } - return rc; -} - -/* -** Find a page in the hash table given its page number. Return -** a pointer to the page or NULL if the requested page is not -** already in memory. -*/ -static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){ - PgHdr *p; /* Return value */ - - /* It is not possible for a call to PcacheFetch() with createFlag==0 to - ** fail, since no attempt to allocate dynamic memory will be made. - */ - (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p); - return p; -} - -/* -** Discard the entire contents of the in-memory page-cache. -*/ -static void pager_reset(Pager *pPager){ - sqlite3BackupRestart(pPager->pBackup); - sqlite3PcacheClear(pPager->pPCache); -} - -/* -** Free all structures in the Pager.aSavepoint[] array and set both -** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal -** if it is open and the pager is not in exclusive mode. -*/ -static void releaseAllSavepoints(Pager *pPager){ - int ii; /* Iterator for looping through Pager.aSavepoint */ - for(ii=0; iinSavepoint; ii++){ - sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); - } - if( !pPager->exclusiveMode || sqlite3IsMemJournal(pPager->sjfd) ){ - sqlite3OsClose(pPager->sjfd); - } - sqlite3_free(pPager->aSavepoint); - pPager->aSavepoint = 0; - pPager->nSavepoint = 0; - pPager->nSubRec = 0; -} - -/* -** Set the bit number pgno in the PagerSavepoint.pInSavepoint -** bitvecs of all open savepoints. Return SQLITE_OK if successful -** or SQLITE_NOMEM if a malloc failure occurs. -*/ -static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){ - int ii; /* Loop counter */ - int rc = SQLITE_OK; /* Result code */ - - for(ii=0; iinSavepoint; ii++){ - PagerSavepoint *p = &pPager->aSavepoint[ii]; - if( pgno<=p->nOrig ){ - rc |= sqlite3BitvecSet(p->pInSavepoint, pgno); - testcase( rc==SQLITE_NOMEM ); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); - } - } - return rc; -} - -/* -** This function is a no-op if the pager is in exclusive mode and not -** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN -** state. -** -** If the pager is not in exclusive-access mode, the database file is -** completely unlocked. If the file is unlocked and the file-system does -** not exhibit the UNDELETABLE_WHEN_OPEN property, the journal file is -** closed (if it is open). -** -** If the pager is in ERROR state when this function is called, the -** contents of the pager cache are discarded before switching back to -** the OPEN state. Regardless of whether the pager is in exclusive-mode -** or not, any journal file left in the file-system will be treated -** as a hot-journal and rolled back the next time a read-transaction -** is opened (by this or by any other connection). -*/ -static void pager_unlock(Pager *pPager){ - - assert( pPager->eState==PAGER_READER - || pPager->eState==PAGER_OPEN - || pPager->eState==PAGER_ERROR - ); - - sqlite3BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - releaseAllSavepoints(pPager); - - if( pagerUseWal(pPager) ){ - assert( !isOpen(pPager->jfd) ); - sqlite3WalEndReadTransaction(pPager->pWal); - pPager->eState = PAGER_OPEN; - }else if( !pPager->exclusiveMode ){ - int rc; /* Error code returned by pagerUnlockDb() */ - int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0; - - /* If the operating system support deletion of open files, then - ** close the journal file when dropping the database lock. Otherwise - ** another connection with journal_mode=delete might delete the file - ** out from under us. - */ - assert( (PAGER_JOURNALMODE_MEMORY & 5)!=1 ); - assert( (PAGER_JOURNALMODE_OFF & 5)!=1 ); - assert( (PAGER_JOURNALMODE_WAL & 5)!=1 ); - assert( (PAGER_JOURNALMODE_DELETE & 5)!=1 ); - assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); - assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); - if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN) - || 1!=(pPager->journalMode & 5) - ){ - sqlite3OsClose(pPager->jfd); - } - - /* If the pager is in the ERROR state and the call to unlock the database - ** file fails, set the current lock to UNKNOWN_LOCK. See the comment - ** above the #define for UNKNOWN_LOCK for an explanation of why this - ** is necessary. - */ - rc = pagerUnlockDb(pPager, NO_LOCK); - if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){ - pPager->eLock = UNKNOWN_LOCK; - } - - /* The pager state may be changed from PAGER_ERROR to PAGER_OPEN here - ** without clearing the error code. This is intentional - the error - ** code is cleared and the cache reset in the block below. - */ - assert( pPager->errCode || pPager->eState!=PAGER_ERROR ); - pPager->changeCountDone = 0; - pPager->eState = PAGER_OPEN; - } - - /* If Pager.errCode is set, the contents of the pager cache cannot be - ** trusted. Now that there are no outstanding references to the pager, - ** it can safely move back to PAGER_OPEN state. This happens in both - ** normal and exclusive-locking mode. - */ - if( pPager->errCode ){ - assert( !MEMDB ); - pager_reset(pPager); - pPager->changeCountDone = pPager->tempFile; - pPager->eState = PAGER_OPEN; - pPager->errCode = SQLITE_OK; - } - - pPager->journalOff = 0; - pPager->journalHdr = 0; - pPager->setMaster = 0; -} - -/* -** This function is called whenever an IOERR or FULL error that requires -** the pager to transition into the ERROR state may ahve occurred. -** The first argument is a pointer to the pager structure, the second -** the error-code about to be returned by a pager API function. The -** value returned is a copy of the second argument to this function. -** -** If the second argument is SQLITE_FULL, SQLITE_IOERR or one of the -** IOERR sub-codes, the pager enters the ERROR state and the error code -** is stored in Pager.errCode. While the pager remains in the ERROR state, -** all major API calls on the Pager will immediately return Pager.errCode. -** -** The ERROR state indicates that the contents of the pager-cache -** cannot be trusted. This state can be cleared by completely discarding -** the contents of the pager-cache. If a transaction was active when -** the persistent error occurred, then the rollback journal may need -** to be replayed to restore the contents of the database file (as if -** it were a hot-journal). -*/ -static int pager_error(Pager *pPager, int rc){ - int rc2 = rc & 0xff; - assert( rc==SQLITE_OK || !MEMDB ); - assert( - pPager->errCode==SQLITE_FULL || - pPager->errCode==SQLITE_OK || - (pPager->errCode & 0xff)==SQLITE_IOERR - ); - if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ - pPager->errCode = rc; - pPager->eState = PAGER_ERROR; - } - return rc; -} - -static int pager_truncate(Pager *pPager, Pgno nPage); - -/* -** This routine ends a transaction. A transaction is usually ended by -** either a COMMIT or a ROLLBACK operation. This routine may be called -** after rollback of a hot-journal, or if an error occurs while opening -** the journal file or writing the very first journal-header of a -** database transaction. -** -** This routine is never called in PAGER_ERROR state. If it is called -** in PAGER_NONE or PAGER_SHARED state and the lock held is less -** exclusive than a RESERVED lock, it is a no-op. -** -** Otherwise, any active savepoints are released. -** -** If the journal file is open, then it is "finalized". Once a journal -** file has been finalized it is not possible to use it to roll back a -** transaction. Nor will it be considered to be a hot-journal by this -** or any other database connection. Exactly how a journal is finalized -** depends on whether or not the pager is running in exclusive mode and -** the current journal-mode (Pager.journalMode value), as follows: -** -** journalMode==MEMORY -** Journal file descriptor is simply closed. This destroys an -** in-memory journal. -** -** journalMode==TRUNCATE -** Journal file is truncated to zero bytes in size. -** -** journalMode==PERSIST -** The first 28 bytes of the journal file are zeroed. This invalidates -** the first journal header in the file, and hence the entire journal -** file. An invalid journal file cannot be rolled back. -** -** journalMode==DELETE -** The journal file is closed and deleted using sqlite3OsDelete(). -** -** If the pager is running in exclusive mode, this method of finalizing -** the journal file is never used. Instead, if the journalMode is -** DELETE and the pager is in exclusive mode, the method described under -** journalMode==PERSIST is used instead. -** -** After the journal is finalized, the pager moves to PAGER_READER state. -** If running in non-exclusive rollback mode, the lock on the file is -** downgraded to a SHARED_LOCK. -** -** SQLITE_OK is returned if no error occurs. If an error occurs during -** any of the IO operations to finalize the journal file or unlock the -** database then the IO error code is returned to the user. If the -** operation to finalize the journal file fails, then the code still -** tries to unlock the database file if not in exclusive mode. If the -** unlock operation fails as well, then the first error code related -** to the first error encountered (the journal finalization one) is -** returned. -*/ -static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ - int rc = SQLITE_OK; /* Error code from journal finalization operation */ - int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ - - /* Do nothing if the pager does not have an open write transaction - ** or at least a RESERVED lock. This function may be called when there - ** is no write-transaction active but a RESERVED or greater lock is - ** held under two circumstances: - ** - ** 1. After a successful hot-journal rollback, it is called with - ** eState==PAGER_NONE and eLock==EXCLUSIVE_LOCK. - ** - ** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE - ** lock switches back to locking_mode=normal and then executes a - ** read-transaction, this function is called with eState==PAGER_READER - ** and eLock==EXCLUSIVE_LOCK when the read-transaction is closed. - */ - assert( assert_pager_state(pPager) ); - assert( pPager->eState!=PAGER_ERROR ); - if( pPager->eStateeLockjfd) || pPager->pInJournal==0 ); - if( isOpen(pPager->jfd) ){ - assert( !pagerUseWal(pPager) ); - - /* Finalize the journal file. */ - if( sqlite3IsMemJournal(pPager->jfd) ){ - assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); - sqlite3OsClose(pPager->jfd); - }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ - if( pPager->journalOff==0 ){ - rc = SQLITE_OK; - }else{ - rc = sqlite3OsTruncate(pPager->jfd, 0); - } - pPager->journalOff = 0; - }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST - || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) - ){ - rc = zeroJournalHdr(pPager, hasMaster); - pPager->journalOff = 0; - }else{ - /* This branch may be executed with Pager.journalMode==MEMORY if - ** a hot-journal was just rolled back. In this case the journal - ** file should be closed and deleted. If this connection writes to - ** the database file, it will do so using an in-memory journal. - */ - int bDelete = (!pPager->tempFile && sqlite3JournalExists(pPager->jfd)); - assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE - || pPager->journalMode==PAGER_JOURNALMODE_MEMORY - || pPager->journalMode==PAGER_JOURNALMODE_WAL - ); - sqlite3OsClose(pPager->jfd); - if( bDelete ){ - rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); - } - } - } - -#ifdef SQLITE_CHECK_PAGES - sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash); - if( pPager->dbSize==0 && sqlite3PcacheRefCount(pPager->pPCache)>0 ){ - PgHdr *p = pager_lookup(pPager, 1); - if( p ){ - p->pageHash = 0; - sqlite3PagerUnref(p); - } - } -#endif - - sqlite3BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - pPager->nRec = 0; - sqlite3PcacheCleanAll(pPager->pPCache); - sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); - - if( pagerUseWal(pPager) ){ - /* Drop the WAL write-lock, if any. Also, if the connection was in - ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE - ** lock held on the database file. - */ - rc2 = sqlite3WalEndWriteTransaction(pPager->pWal); - assert( rc2==SQLITE_OK ); - }else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){ - /* This branch is taken when committing a transaction in rollback-journal - ** mode if the database file on disk is larger than the database image. - ** At this point the journal has been finalized and the transaction - ** successfully committed, but the EXCLUSIVE lock is still held on the - ** file. So it is safe to truncate the database file to its minimum - ** required size. */ - assert( pPager->eLock==EXCLUSIVE_LOCK ); - rc = pager_truncate(pPager, pPager->dbSize); - } - - if( !pPager->exclusiveMode - && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0)) - ){ - rc2 = pagerUnlockDb(pPager, SHARED_LOCK); - pPager->changeCountDone = 0; - } - pPager->eState = PAGER_READER; - pPager->setMaster = 0; - - return (rc==SQLITE_OK?rc2:rc); -} - -/* -** Execute a rollback if a transaction is active and unlock the -** database file. -** -** If the pager has already entered the ERROR state, do not attempt -** the rollback at this time. Instead, pager_unlock() is called. The -** call to pager_unlock() will discard all in-memory pages, unlock -** the database file and move the pager back to OPEN state. If this -** means that there is a hot-journal left in the file-system, the next -** connection to obtain a shared lock on the pager (which may be this one) -** will roll it back. -** -** If the pager has not already entered the ERROR state, but an IO or -** malloc error occurs during a rollback, then this will itself cause -** the pager to enter the ERROR state. Which will be cleared by the -** call to pager_unlock(), as described above. -*/ -static void pagerUnlockAndRollback(Pager *pPager){ - if( pPager->eState!=PAGER_ERROR && pPager->eState!=PAGER_OPEN ){ - assert( assert_pager_state(pPager) ); - if( pPager->eState>=PAGER_WRITER_LOCKED ){ - sqlite3BeginBenignMalloc(); - sqlite3PagerRollback(pPager); - sqlite3EndBenignMalloc(); - }else if( !pPager->exclusiveMode ){ - assert( pPager->eState==PAGER_READER ); - pager_end_transaction(pPager, 0, 0); - } - } - pager_unlock(pPager); -} - -/* -** Parameter aData must point to a buffer of pPager->pageSize bytes -** of data. Compute and return a checksum based ont the contents of the -** page of data and the current value of pPager->cksumInit. -** -** This is not a real checksum. It is really just the sum of the -** random initial value (pPager->cksumInit) and every 200th byte -** of the page data, starting with byte offset (pPager->pageSize%200). -** Each byte is interpreted as an 8-bit unsigned integer. -** -** Changing the formula used to compute this checksum results in an -** incompatible journal file format. -** -** If journal corruption occurs due to a power failure, the most likely -** scenario is that one end or the other of the record will be changed. -** It is much less likely that the two ends of the journal record will be -** correct and the middle be corrupt. Thus, this "checksum" scheme, -** though fast and simple, catches the mostly likely kind of corruption. -*/ -static u32 pager_cksum(Pager *pPager, const u8 *aData){ - u32 cksum = pPager->cksumInit; /* Checksum value to return */ - int i = pPager->pageSize-200; /* Loop counter */ - while( i>0 ){ - cksum += aData[i]; - i -= 200; - } - return cksum; -} - -/* -** Report the current page size and number of reserved bytes back -** to the codec. -*/ -#ifdef SQLITE_HAS_CODEC -static void pagerReportSize(Pager *pPager){ - if( pPager->xCodecSizeChng ){ - pPager->xCodecSizeChng(pPager->pCodec, pPager->pageSize, - (int)pPager->nReserve); - } -} -#else -# define pagerReportSize(X) /* No-op if we do not support a codec */ -#endif - -/* -** Read a single page from either the journal file (if isMainJrnl==1) or -** from the sub-journal (if isMainJrnl==0) and playback that page. -** The page begins at offset *pOffset into the file. The *pOffset -** value is increased to the start of the next page in the journal. -** -** The main rollback journal uses checksums - the statement journal does -** not. -** -** If the page number of the page record read from the (sub-)journal file -** is greater than the current value of Pager.dbSize, then playback is -** skipped and SQLITE_OK is returned. -** -** If pDone is not NULL, then it is a record of pages that have already -** been played back. If the page at *pOffset has already been played back -** (if the corresponding pDone bit is set) then skip the playback. -** Make sure the pDone bit corresponding to the *pOffset page is set -** prior to returning. -** -** If the page record is successfully read from the (sub-)journal file -** and played back, then SQLITE_OK is returned. If an IO error occurs -** while reading the record from the (sub-)journal file or while writing -** to the database file, then the IO error code is returned. If data -** is successfully read from the (sub-)journal file but appears to be -** corrupted, SQLITE_DONE is returned. Data is considered corrupted in -** two circumstances: -** -** * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or -** * If the record is being rolled back from the main journal file -** and the checksum field does not match the record content. -** -** Neither of these two scenarios are possible during a savepoint rollback. -** -** If this is a savepoint rollback, then memory may have to be dynamically -** allocated by this function. If this is the case and an allocation fails, -** SQLITE_NOMEM is returned. -*/ -static int pager_playback_one_page( - Pager *pPager, /* The pager being played back */ - i64 *pOffset, /* Offset of record to playback */ - Bitvec *pDone, /* Bitvec of pages already played back */ - int isMainJrnl, /* 1 -> main journal. 0 -> sub-journal. */ - int isSavepnt /* True for a savepoint rollback */ -){ - int rc; - PgHdr *pPg; /* An existing page in the cache */ - Pgno pgno; /* The page number of a page in journal */ - u32 cksum; /* Checksum used for sanity checking */ - char *aData; /* Temporary storage for the page */ - sqlite3_file *jfd; /* The file descriptor for the journal file */ - int isSynced; /* True if journal page is synced */ - - assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ - assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ - assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ - assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ - - aData = pPager->pTmpSpace; - assert( aData ); /* Temp storage must have already been allocated */ - assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) ); - - /* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction - ** or savepoint rollback done at the request of the caller) or this is - ** a hot-journal rollback. If it is a hot-journal rollback, the pager - ** is in state OPEN and holds an EXCLUSIVE lock. Hot-journal rollback - ** only reads from the main journal, not the sub-journal. - */ - assert( pPager->eState>=PAGER_WRITER_CACHEMOD - || (pPager->eState==PAGER_OPEN && pPager->eLock==EXCLUSIVE_LOCK) - ); - assert( pPager->eState>=PAGER_WRITER_CACHEMOD || isMainJrnl ); - - /* Read the page number and page data from the journal or sub-journal - ** file. Return an error code to the caller if an IO error occurs. - */ - jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; - rc = read32bits(jfd, *pOffset, &pgno); - if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsRead(jfd, (u8*)aData, pPager->pageSize, (*pOffset)+4); - if( rc!=SQLITE_OK ) return rc; - *pOffset += pPager->pageSize + 4 + isMainJrnl*4; - - /* Sanity checking on the page. This is more important that I originally - ** thought. If a power failure occurs while the journal is being written, - ** it could cause invalid data to be written into the journal. We need to - ** detect this invalid data (with high probability) and ignore it. - */ - if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ - assert( !isSavepnt ); - return SQLITE_DONE; - } - if( pgno>(Pgno)pPager->dbSize || sqlite3BitvecTest(pDone, pgno) ){ - return SQLITE_OK; - } - if( isMainJrnl ){ - rc = read32bits(jfd, (*pOffset)-4, &cksum); - if( rc ) return rc; - if( !isSavepnt && pager_cksum(pPager, (u8*)aData)!=cksum ){ - return SQLITE_DONE; - } - } - - /* If this page has already been played by before during the current - ** rollback, then don't bother to play it back again. - */ - if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){ - return rc; - } - - /* When playing back page 1, restore the nReserve setting - */ - if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){ - pPager->nReserve = ((u8*)aData)[20]; - pagerReportSize(pPager); - } - - /* If the pager is in CACHEMOD state, then there must be a copy of this - ** page in the pager cache. In this case just update the pager cache, - ** not the database file. The page is left marked dirty in this case. - ** - ** An exception to the above rule: If the database is in no-sync mode - ** and a page is moved during an incremental vacuum then the page may - ** not be in the pager cache. Later: if a malloc() or IO error occurs - ** during a Movepage() call, then the page may not be in the cache - ** either. So the condition described in the above paragraph is not - ** assert()able. - ** - ** If in WRITER_DBMOD, WRITER_FINISHED or OPEN state, then we update the - ** pager cache if it exists and the main file. The page is then marked - ** not dirty. Since this code is only executed in PAGER_OPEN state for - ** a hot-journal rollback, it is guaranteed that the page-cache is empty - ** if the pager is in OPEN state. - ** - ** Ticket #1171: The statement journal might contain page content that is - ** different from the page content at the start of the transaction. - ** This occurs when a page is changed prior to the start of a statement - ** then changed again within the statement. When rolling back such a - ** statement we must not write to the original database unless we know - ** for certain that original page contents are synced into the main rollback - ** journal. Otherwise, a power loss might leave modified data in the - ** database file without an entry in the rollback journal that can - ** restore the database to its original form. Two conditions must be - ** met before writing to the database files. (1) the database must be - ** locked. (2) we know that the original page content is fully synced - ** in the main journal either because the page is not in cache or else - ** the page is marked as needSync==0. - ** - ** 2008-04-14: When attempting to vacuum a corrupt database file, it - ** is possible to fail a statement on a database that does not yet exist. - ** Do not attempt to write if database file has never been opened. - */ - if( pagerUseWal(pPager) ){ - pPg = 0; - }else{ - pPg = pager_lookup(pPager, pgno); - } - assert( pPg || !MEMDB ); - assert( pPager->eState!=PAGER_OPEN || pPg==0 ); - PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", - PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), - (isMainJrnl?"main-journal":"sub-journal") - )); - if( isMainJrnl ){ - isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr); - }else{ - isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC)); - } - if( isOpen(pPager->fd) - && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) - && isSynced - ){ - i64 ofst = (pgno-1)*(i64)pPager->pageSize; - testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); - assert( !pagerUseWal(pPager) ); - rc = sqlite3OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst); - if( pgno>pPager->dbFileSize ){ - pPager->dbFileSize = pgno; - } - if( pPager->pBackup ){ - CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM); - sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); - CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM, aData); - } - }else if( !isMainJrnl && pPg==0 ){ - /* If this is a rollback of a savepoint and data was not written to - ** the database and the page is not in-memory, there is a potential - ** problem. When the page is next fetched by the b-tree layer, it - ** will be read from the database file, which may or may not be - ** current. - ** - ** There are a couple of different ways this can happen. All are quite - ** obscure. When running in synchronous mode, this can only happen - ** if the page is on the free-list at the start of the transaction, then - ** populated, then moved using sqlite3PagerMovepage(). - ** - ** The solution is to add an in-memory page to the cache containing - ** the data just read from the sub-journal. Mark the page as dirty - ** and if the pager requires a journal-sync, then mark the page as - ** requiring a journal-sync before it is written. - */ - assert( isSavepnt ); - assert( pPager->doNotSpill==0 ); - pPager->doNotSpill++; - rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1); - assert( pPager->doNotSpill==1 ); - pPager->doNotSpill--; - if( rc!=SQLITE_OK ) return rc; - pPg->flags &= ~PGHDR_NEED_READ; - sqlite3PcacheMakeDirty(pPg); - } - if( pPg ){ - /* No page should ever be explicitly rolled back that is in use, except - ** for page 1 which is held in use in order to keep the lock on the - ** database active. However such a page may be rolled back as a result - ** of an internal error resulting in an automatic call to - ** sqlite3PagerRollback(). - */ - void *pData; - pData = pPg->pData; - memcpy(pData, (u8*)aData, pPager->pageSize); - pPager->xReiniter(pPg); - if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){ - /* If the contents of this page were just restored from the main - ** journal file, then its content must be as they were when the - ** transaction was first opened. In this case we can mark the page - ** as clean, since there will be no need to write it out to the - ** database. - ** - ** There is one exception to this rule. If the page is being rolled - ** back as part of a savepoint (or statement) rollback from an - ** unsynced portion of the main journal file, then it is not safe - ** to mark the page as clean. This is because marking the page as - ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is - ** already in the journal file (recorded in Pager.pInJournal) and - ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to - ** again within this transaction, it will be marked as dirty but - ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially - ** be written out into the database file before its journal file - ** segment is synced. If a crash occurs during or following this, - ** database corruption may ensue. - */ - assert( !pagerUseWal(pPager) ); - sqlite3PcacheMakeClean(pPg); - } - pager_set_pagehash(pPg); - - /* If this was page 1, then restore the value of Pager.dbFileVers. - ** Do this before any decoding. */ - if( pgno==1 ){ - memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); - } - - /* Decode the page just read from disk */ - CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM); - sqlite3PcacheRelease(pPg); - } - return rc; -} - -/* -** Parameter zMaster is the name of a master journal file. A single journal -** file that referred to the master journal file has just been rolled back. -** This routine checks if it is possible to delete the master journal file, -** and does so if it is. -** -** Argument zMaster may point to Pager.pTmpSpace. So that buffer is not -** available for use within this function. -** -** When a master journal file is created, it is populated with the names -** of all of its child journals, one after another, formatted as utf-8 -** encoded text. The end of each child journal file is marked with a -** nul-terminator byte (0x00). i.e. the entire contents of a master journal -** file for a transaction involving two databases might be: -** -** "/home/bill/a.db-journal\x00/home/bill/b.db-journal\x00" -** -** A master journal file may only be deleted once all of its child -** journals have been rolled back. -** -** This function reads the contents of the master-journal file into -** memory and loops through each of the child journal names. For -** each child journal, it checks if: -** -** * if the child journal exists, and if so -** * if the child journal contains a reference to master journal -** file zMaster -** -** If a child journal can be found that matches both of the criteria -** above, this function returns without doing anything. Otherwise, if -** no such child journal can be found, file zMaster is deleted from -** the file-system using sqlite3OsDelete(). -** -** If an IO error within this function, an error code is returned. This -** function allocates memory by calling sqlite3Malloc(). If an allocation -** fails, SQLITE_NOMEM is returned. Otherwise, if no IO or malloc errors -** occur, SQLITE_OK is returned. -** -** TODO: This function allocates a single block of memory to load -** the entire contents of the master journal file. This could be -** a couple of kilobytes or so - potentially larger than the page -** size. -*/ -static int pager_delmaster(Pager *pPager, const char *zMaster){ - sqlite3_vfs *pVfs = pPager->pVfs; - int rc; /* Return code */ - sqlite3_file *pMaster; /* Malloc'd master-journal file descriptor */ - sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ - char *zMasterJournal = 0; /* Contents of master journal file */ - i64 nMasterJournal; /* Size of master journal file */ - char *zJournal; /* Pointer to one journal within MJ file */ - char *zMasterPtr; /* Space to hold MJ filename from a journal file */ - int nMasterPtr; /* Amount of space allocated to zMasterPtr[] */ - - /* Allocate space for both the pJournal and pMaster file descriptors. - ** If successful, open the master journal file for reading. - */ - pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); - pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile); - if( !pMaster ){ - rc = SQLITE_NOMEM; - }else{ - const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); - rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0); - } - if( rc!=SQLITE_OK ) goto delmaster_out; - - /* Load the entire master journal file into space obtained from - ** sqlite3_malloc() and pointed to by zMasterJournal. Also obtain - ** sufficient space (in zMasterPtr) to hold the names of master - ** journal files extracted from regular rollback-journals. - */ - rc = sqlite3OsFileSize(pMaster, &nMasterJournal); - if( rc!=SQLITE_OK ) goto delmaster_out; - nMasterPtr = pVfs->mxPathname+1; - zMasterJournal = sqlite3Malloc((int)nMasterJournal + nMasterPtr + 1); - if( !zMasterJournal ){ - rc = SQLITE_NOMEM; - goto delmaster_out; - } - zMasterPtr = &zMasterJournal[nMasterJournal+1]; - rc = sqlite3OsRead(pMaster, zMasterJournal, (int)nMasterJournal, 0); - if( rc!=SQLITE_OK ) goto delmaster_out; - zMasterJournal[nMasterJournal] = 0; - - zJournal = zMasterJournal; - while( (zJournal-zMasterJournal)pageSize bytes). -** If the file on disk is currently larger than nPage pages, then use the VFS -** xTruncate() method to truncate it. -** -** Or, it might might be the case that the file on disk is smaller than -** nPage pages. Some operating system implementations can get confused if -** you try to truncate a file to some size that is larger than it -** currently is, so detect this case and write a single zero byte to -** the end of the new file instead. -** -** If successful, return SQLITE_OK. If an IO error occurs while modifying -** the database file, return the error code to the caller. -*/ -static int pager_truncate(Pager *pPager, Pgno nPage){ - int rc = SQLITE_OK; - assert( pPager->eState!=PAGER_ERROR ); - assert( pPager->eState!=PAGER_READER ); - - if( isOpen(pPager->fd) - && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) - ){ - i64 currentSize, newSize; - int szPage = pPager->pageSize; - assert( pPager->eLock==EXCLUSIVE_LOCK ); - /* TODO: Is it safe to use Pager.dbFileSize here? */ - rc = sqlite3OsFileSize(pPager->fd, ¤tSize); - newSize = szPage*(i64)nPage; - if( rc==SQLITE_OK && currentSize!=newSize ){ - if( currentSize>newSize ){ - rc = sqlite3OsTruncate(pPager->fd, newSize); - }else if( (currentSize+szPage)<=newSize ){ - char *pTmp = pPager->pTmpSpace; - memset(pTmp, 0, szPage); - testcase( (newSize-szPage) == currentSize ); - testcase( (newSize-szPage) > currentSize ); - rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); - } - if( rc==SQLITE_OK ){ - pPager->dbFileSize = nPage; - } - } - } - return rc; -} - -/* -** Return a sanitized version of the sector-size of OS file pFile. The -** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE. -*/ -SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *pFile){ - int iRet = sqlite3OsSectorSize(pFile); - if( iRet<32 ){ - iRet = 512; - }else if( iRet>MAX_SECTOR_SIZE ){ - assert( MAX_SECTOR_SIZE>=512 ); - iRet = MAX_SECTOR_SIZE; - } - return iRet; -} - -/* -** Set the value of the Pager.sectorSize variable for the given -** pager based on the value returned by the xSectorSize method -** of the open database file. The sector size will be used used -** to determine the size and alignment of journal header and -** master journal pointers within created journal files. -** -** For temporary files the effective sector size is always 512 bytes. -** -** Otherwise, for non-temporary files, the effective sector size is -** the value returned by the xSectorSize() method rounded up to 32 if -** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it -** is greater than MAX_SECTOR_SIZE. -** -** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set -** the effective sector size to its minimum value (512). The purpose of -** pPager->sectorSize is to define the "blast radius" of bytes that -** might change if a crash occurs while writing to a single byte in -** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero -** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector -** size. For backwards compatibility of the rollback journal file format, -** we cannot reduce the effective sector size below 512. -*/ -static void setSectorSize(Pager *pPager){ - assert( isOpen(pPager->fd) || pPager->tempFile ); - - if( pPager->tempFile - || (sqlite3OsDeviceCharacteristics(pPager->fd) & - SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0 - ){ - /* Sector size doesn't matter for temporary files. Also, the file - ** may not have been opened yet, in which case the OsSectorSize() - ** call will segfault. */ - pPager->sectorSize = 512; - }else{ - pPager->sectorSize = sqlite3SectorSize(pPager->fd); - } -} - -/* -** Playback the journal and thus restore the database file to -** the state it was in before we started making changes. -** -** The journal file format is as follows: -** -** (1) 8 byte prefix. A copy of aJournalMagic[]. -** (2) 4 byte big-endian integer which is the number of valid page records -** in the journal. If this value is 0xffffffff, then compute the -** number of page records from the journal size. -** (3) 4 byte big-endian integer which is the initial value for the -** sanity checksum. -** (4) 4 byte integer which is the number of pages to truncate the -** database to during a rollback. -** (5) 4 byte big-endian integer which is the sector size. The header -** is this many bytes in size. -** (6) 4 byte big-endian integer which is the page size. -** (7) zero padding out to the next sector size. -** (8) Zero or more pages instances, each as follows: -** + 4 byte page number. -** + pPager->pageSize bytes of data. -** + 4 byte checksum -** -** When we speak of the journal header, we mean the first 7 items above. -** Each entry in the journal is an instance of the 8th item. -** -** Call the value from the second bullet "nRec". nRec is the number of -** valid page entries in the journal. In most cases, you can compute the -** value of nRec from the size of the journal file. But if a power -** failure occurred while the journal was being written, it could be the -** case that the size of the journal file had already been increased but -** the extra entries had not yet made it safely to disk. In such a case, -** the value of nRec computed from the file size would be too large. For -** that reason, we always use the nRec value in the header. -** -** If the nRec value is 0xffffffff it means that nRec should be computed -** from the file size. This value is used when the user selects the -** no-sync option for the journal. A power failure could lead to corruption -** in this case. But for things like temporary table (which will be -** deleted when the power is restored) we don't care. -** -** If the file opened as the journal file is not a well-formed -** journal file then all pages up to the first corrupted page are rolled -** back (or no pages if the journal header is corrupted). The journal file -** is then deleted and SQLITE_OK returned, just as if no corruption had -** been encountered. -** -** If an I/O or malloc() error occurs, the journal-file is not deleted -** and an error code is returned. -** -** The isHot parameter indicates that we are trying to rollback a journal -** that might be a hot journal. Or, it could be that the journal is -** preserved because of JOURNALMODE_PERSIST or JOURNALMODE_TRUNCATE. -** If the journal really is hot, reset the pager cache prior rolling -** back any content. If the journal is merely persistent, no reset is -** needed. -*/ -static int pager_playback(Pager *pPager, int isHot){ - sqlite3_vfs *pVfs = pPager->pVfs; - i64 szJ; /* Size of the journal file in bytes */ - u32 nRec; /* Number of Records in the journal */ - u32 u; /* Unsigned loop counter */ - Pgno mxPg = 0; /* Size of the original file in pages */ - int rc; /* Result code of a subroutine */ - int res = 1; /* Value returned by sqlite3OsAccess() */ - char *zMaster = 0; /* Name of master journal file if any */ - int needPagerReset; /* True to reset page prior to first page rollback */ - - /* Figure out how many records are in the journal. Abort early if - ** the journal is empty. - */ - assert( isOpen(pPager->jfd) ); - rc = sqlite3OsFileSize(pPager->jfd, &szJ); - if( rc!=SQLITE_OK ){ - goto end_playback; - } - - /* Read the master journal name from the journal, if it is present. - ** If a master journal file name is specified, but the file is not - ** present on disk, then the journal is not hot and does not need to be - ** played back. - ** - ** TODO: Technically the following is an error because it assumes that - ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that - ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c, - ** mxPathname is 512, which is the same as the minimum allowable value - ** for pageSize. - */ - zMaster = pPager->pTmpSpace; - rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); - if( rc==SQLITE_OK && zMaster[0] ){ - rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res); - } - zMaster = 0; - if( rc!=SQLITE_OK || !res ){ - goto end_playback; - } - pPager->journalOff = 0; - needPagerReset = isHot; - - /* This loop terminates either when a readJournalHdr() or - ** pager_playback_one_page() call returns SQLITE_DONE or an IO error - ** occurs. - */ - while( 1 ){ - /* Read the next journal header from the journal file. If there are - ** not enough bytes left in the journal file for a complete header, or - ** it is corrupted, then a process must have failed while writing it. - ** This indicates nothing more needs to be rolled back. - */ - rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - } - goto end_playback; - } - - /* If nRec is 0xffffffff, then this journal was created by a process - ** working in no-sync mode. This means that the rest of the journal - ** file consists of pages, there are no more journal headers. Compute - ** the value of nRec based on this assumption. - */ - if( nRec==0xffffffff ){ - assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ); - nRec = (int)((szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager)); - } - - /* If nRec is 0 and this rollback is of a transaction created by this - ** process and if this is the final header in the journal, then it means - ** that this part of the journal was being filled but has not yet been - ** synced to disk. Compute the number of pages based on the remaining - ** size of the file. - ** - ** The third term of the test was added to fix ticket #2565. - ** When rolling back a hot journal, nRec==0 always means that the next - ** chunk of the journal contains zero pages to be rolled back. But - ** when doing a ROLLBACK and the nRec==0 chunk is the last chunk in - ** the journal, it means that the journal might contain additional - ** pages that need to be rolled back and that the number of pages - ** should be computed based on the journal file size. - */ - if( nRec==0 && !isHot && - pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ - nRec = (int)((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager)); - } - - /* If this is the first header read from the journal, truncate the - ** database file back to its original size. - */ - if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ - rc = pager_truncate(pPager, mxPg); - if( rc!=SQLITE_OK ){ - goto end_playback; - } - pPager->dbSize = mxPg; - } - - /* Copy original pages out of the journal and back into the - ** database file and/or page cache. - */ - for(u=0; ujournalOff,0,1,0); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ - pPager->journalOff = szJ; - break; - }else if( rc==SQLITE_IOERR_SHORT_READ ){ - /* If the journal has been truncated, simply stop reading and - ** processing the journal. This might happen if the journal was - ** not completely written and synced prior to a crash. In that - ** case, the database should have never been written in the - ** first place so it is OK to simply abandon the rollback. */ - rc = SQLITE_OK; - goto end_playback; - }else{ - /* If we are unable to rollback, quit and return the error - ** code. This will cause the pager to enter the error state - ** so that no further harm will be done. Perhaps the next - ** process to come along will be able to rollback the database. - */ - goto end_playback; - } - } - } - } - /*NOTREACHED*/ - assert( 0 ); - -end_playback: - /* Following a rollback, the database file should be back in its original - ** state prior to the start of the transaction, so invoke the - ** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the - ** assertion that the transaction counter was modified. - */ -#ifdef SQLITE_DEBUG - if( pPager->fd->pMethods ){ - sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); - } -#endif - - /* If this playback is happening automatically as a result of an IO or - ** malloc error that occurred after the change-counter was updated but - ** before the transaction was committed, then the change-counter - ** modification may just have been reverted. If this happens in exclusive - ** mode, then subsequent transactions performed by the connection will not - ** update the change-counter at all. This may lead to cache inconsistency - ** problems for other processes at some point in the future. So, just - ** in case this has happened, clear the changeCountDone flag now. - */ - pPager->changeCountDone = pPager->tempFile; - - if( rc==SQLITE_OK ){ - zMaster = pPager->pTmpSpace; - rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); - testcase( rc!=SQLITE_OK ); - } - if( rc==SQLITE_OK - && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) - ){ - rc = sqlite3PagerSync(pPager); - } - if( rc==SQLITE_OK ){ - rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0); - testcase( rc!=SQLITE_OK ); - } - if( rc==SQLITE_OK && zMaster[0] && res ){ - /* If there was a master journal and this routine will return success, - ** see if it is possible to delete the master journal. - */ - rc = pager_delmaster(pPager, zMaster); - testcase( rc!=SQLITE_OK ); - } - - /* The Pager.sectorSize variable may have been updated while rolling - ** back a journal created by a process with a different sector size - ** value. Reset it to the correct value for this process. - */ - setSectorSize(pPager); - return rc; -} - - -/* -** Read the content for page pPg out of the database file and into -** pPg->pData. A shared lock or greater must be held on the database -** file before this function is called. -** -** If page 1 is read, then the value of Pager.dbFileVers[] is set to -** the value read from the database file. -** -** If an IO error occurs, then the IO error is returned to the caller. -** Otherwise, SQLITE_OK is returned. -*/ -static int readDbPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ - Pgno pgno = pPg->pgno; /* Page number to read */ - int rc = SQLITE_OK; /* Return code */ - int isInWal = 0; /* True if page is in log file */ - int pgsz = pPager->pageSize; /* Number of bytes to read */ - - assert( pPager->eState>=PAGER_READER && !MEMDB ); - assert( isOpen(pPager->fd) ); - - if( NEVER(!isOpen(pPager->fd)) ){ - assert( pPager->tempFile ); - memset(pPg->pData, 0, pPager->pageSize); - return SQLITE_OK; - } - - if( pagerUseWal(pPager) ){ - /* Try to pull the page from the write-ahead log. */ - rc = sqlite3WalRead(pPager->pWal, pgno, &isInWal, pgsz, pPg->pData); - } - if( rc==SQLITE_OK && !isInWal ){ - i64 iOffset = (pgno-1)*(i64)pPager->pageSize; - rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset); - if( rc==SQLITE_IOERR_SHORT_READ ){ - rc = SQLITE_OK; - } - } - - if( pgno==1 ){ - if( rc ){ - /* If the read is unsuccessful, set the dbFileVers[] to something - ** that will never be a valid file version. dbFileVers[] is a copy - ** of bytes 24..39 of the database. Bytes 28..31 should always be - ** zero or the size of the database in page. Bytes 32..35 and 35..39 - ** should be page numbers which are never 0xffffffff. So filling - ** pPager->dbFileVers[] with all 0xff bytes should suffice. - ** - ** For an encrypted database, the situation is more complex: bytes - ** 24..39 of the database are white noise. But the probability of - ** white noising equaling 16 bytes of 0xff is vanishingly small so - ** we should still be ok. - */ - memset(pPager->dbFileVers, 0xff, sizeof(pPager->dbFileVers)); - }else{ - u8 *dbFileVers = &((u8*)pPg->pData)[24]; - memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); - } - } - CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM); - - PAGER_INCR(sqlite3_pager_readdb_count); - PAGER_INCR(pPager->nRead); - IOTRACE(("PGIN %p %d\n", pPager, pgno)); - PAGERTRACE(("FETCH %d page %d hash(%08x)\n", - PAGERID(pPager), pgno, pager_pagehash(pPg))); - - return rc; -} - -/* -** Update the value of the change-counter at offsets 24 and 92 in -** the header and the sqlite version number at offset 96. -** -** This is an unconditional update. See also the pager_incr_changecounter() -** routine which only updates the change-counter if the update is actually -** needed, as determined by the pPager->changeCountDone state variable. -*/ -static void pager_write_changecounter(PgHdr *pPg){ - u32 change_counter; - - /* Increment the value just read and write it back to byte 24. */ - change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; - put32bits(((char*)pPg->pData)+24, change_counter); - - /* Also store the SQLite version number in bytes 96..99 and in - ** bytes 92..95 store the change counter for which the version number - ** is valid. */ - put32bits(((char*)pPg->pData)+92, change_counter); - put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER); -} - -#ifndef SQLITE_OMIT_WAL -/* -** This function is invoked once for each page that has already been -** written into the log file when a WAL transaction is rolled back. -** Parameter iPg is the page number of said page. The pCtx argument -** is actually a pointer to the Pager structure. -** -** If page iPg is present in the cache, and has no outstanding references, -** it is discarded. Otherwise, if there are one or more outstanding -** references, the page content is reloaded from the database. If the -** attempt to reload content from the database is required and fails, -** return an SQLite error code. Otherwise, SQLITE_OK. -*/ -static int pagerUndoCallback(void *pCtx, Pgno iPg){ - int rc = SQLITE_OK; - Pager *pPager = (Pager *)pCtx; - PgHdr *pPg; - - pPg = sqlite3PagerLookup(pPager, iPg); - if( pPg ){ - if( sqlite3PcachePageRefcount(pPg)==1 ){ - sqlite3PcacheDrop(pPg); - }else{ - rc = readDbPage(pPg); - if( rc==SQLITE_OK ){ - pPager->xReiniter(pPg); - } - sqlite3PagerUnref(pPg); - } - } - - /* Normally, if a transaction is rolled back, any backup processes are - ** updated as data is copied out of the rollback journal and into the - ** database. This is not generally possible with a WAL database, as - ** rollback involves simply truncating the log file. Therefore, if one - ** or more frames have already been written to the log (and therefore - ** also copied into the backup databases) as part of this transaction, - ** the backups must be restarted. - */ - sqlite3BackupRestart(pPager->pBackup); - - return rc; -} - -/* -** This function is called to rollback a transaction on a WAL database. -*/ -static int pagerRollbackWal(Pager *pPager){ - int rc; /* Return Code */ - PgHdr *pList; /* List of dirty pages to revert */ - - /* For all pages in the cache that are currently dirty or have already - ** been written (but not committed) to the log file, do one of the - ** following: - ** - ** + Discard the cached page (if refcount==0), or - ** + Reload page content from the database (if refcount>0). - */ - pPager->dbSize = pPager->dbOrigSize; - rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager); - pList = sqlite3PcacheDirtyList(pPager->pPCache); - while( pList && rc==SQLITE_OK ){ - PgHdr *pNext = pList->pDirty; - rc = pagerUndoCallback((void *)pPager, pList->pgno); - pList = pNext; - } - - return rc; -} - -/* -** This function is a wrapper around sqlite3WalFrames(). As well as logging -** the contents of the list of pages headed by pList (connected by pDirty), -** this function notifies any active backup processes that the pages have -** changed. -** -** The list of pages passed into this routine is always sorted by page number. -** Hence, if page 1 appears anywhere on the list, it will be the first page. -*/ -static int pagerWalFrames( - Pager *pPager, /* Pager object */ - PgHdr *pList, /* List of frames to log */ - Pgno nTruncate, /* Database size after this commit */ - int isCommit /* True if this is a commit */ -){ - int rc; /* Return code */ - int nList; /* Number of pages in pList */ -#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES) - PgHdr *p; /* For looping over pages */ -#endif - - assert( pPager->pWal ); - assert( pList ); -#ifdef SQLITE_DEBUG - /* Verify that the page list is in accending order */ - for(p=pList; p && p->pDirty; p=p->pDirty){ - assert( p->pgno < p->pDirty->pgno ); - } -#endif - - assert( pList->pDirty==0 || isCommit ); - if( isCommit ){ - /* If a WAL transaction is being committed, there is no point in writing - ** any pages with page numbers greater than nTruncate into the WAL file. - ** They will never be read by any client. So remove them from the pDirty - ** list here. */ - PgHdr *p; - PgHdr **ppNext = &pList; - nList = 0; - for(p=pList; (*ppNext = p)!=0; p=p->pDirty){ - if( p->pgno<=nTruncate ){ - ppNext = &p->pDirty; - nList++; - } - } - assert( pList ); - }else{ - nList = 1; - } - pPager->aStat[PAGER_STAT_WRITE] += nList; - - if( pList->pgno==1 ) pager_write_changecounter(pList); - rc = sqlite3WalFrames(pPager->pWal, - pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags - ); - if( rc==SQLITE_OK && pPager->pBackup ){ - PgHdr *p; - for(p=pList; p; p=p->pDirty){ - sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); - } - } - -#ifdef SQLITE_CHECK_PAGES - pList = sqlite3PcacheDirtyList(pPager->pPCache); - for(p=pList; p; p=p->pDirty){ - pager_set_pagehash(p); - } -#endif - - return rc; -} - -/* -** Begin a read transaction on the WAL. -** -** This routine used to be called "pagerOpenSnapshot()" because it essentially -** makes a snapshot of the database at the current point in time and preserves -** that snapshot for use by the reader in spite of concurrently changes by -** other writers or checkpointers. -*/ -static int pagerBeginReadTransaction(Pager *pPager){ - int rc; /* Return code */ - int changed = 0; /* True if cache must be reset */ - - assert( pagerUseWal(pPager) ); - assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); - - /* sqlite3WalEndReadTransaction() was not called for the previous - ** transaction in locking_mode=EXCLUSIVE. So call it now. If we - ** are in locking_mode=NORMAL and EndRead() was previously called, - ** the duplicate call is harmless. - */ - sqlite3WalEndReadTransaction(pPager->pWal); - - rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); - if( rc!=SQLITE_OK || changed ){ - pager_reset(pPager); - } - - return rc; -} -#endif - -/* -** This function is called as part of the transition from PAGER_OPEN -** to PAGER_READER state to determine the size of the database file -** in pages (assuming the page size currently stored in Pager.pageSize). -** -** If no error occurs, SQLITE_OK is returned and the size of the database -** in pages is stored in *pnPage. Otherwise, an error code (perhaps -** SQLITE_IOERR_FSTAT) is returned and *pnPage is left unmodified. -*/ -static int pagerPagecount(Pager *pPager, Pgno *pnPage){ - Pgno nPage; /* Value to return via *pnPage */ - - /* Query the WAL sub-system for the database size. The WalDbsize() - ** function returns zero if the WAL is not open (i.e. Pager.pWal==0), or - ** if the database size is not available. The database size is not - ** available from the WAL sub-system if the log file is empty or - ** contains no valid committed transactions. - */ - assert( pPager->eState==PAGER_OPEN ); - assert( pPager->eLock>=SHARED_LOCK ); - nPage = sqlite3WalDbsize(pPager->pWal); - - /* If the database size was not available from the WAL sub-system, - ** determine it based on the size of the database file. If the size - ** of the database file is not an integer multiple of the page-size, - ** round down to the nearest page. Except, any file larger than 0 - ** bytes in size is considered to contain at least one page. - */ - if( nPage==0 ){ - i64 n = 0; /* Size of db file in bytes */ - assert( isOpen(pPager->fd) || pPager->tempFile ); - if( isOpen(pPager->fd) ){ - int rc = sqlite3OsFileSize(pPager->fd, &n); - if( rc!=SQLITE_OK ){ - return rc; - } - } - nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize); - } - - /* If the current number of pages in the file is greater than the - ** configured maximum pager number, increase the allowed limit so - ** that the file can be read. - */ - if( nPage>pPager->mxPgno ){ - pPager->mxPgno = (Pgno)nPage; - } - - *pnPage = nPage; - return SQLITE_OK; -} - -#ifndef SQLITE_OMIT_WAL -/* -** Check if the *-wal file that corresponds to the database opened by pPager -** exists if the database is not empy, or verify that the *-wal file does -** not exist (by deleting it) if the database file is empty. -** -** If the database is not empty and the *-wal file exists, open the pager -** in WAL mode. If the database is empty or if no *-wal file exists and -** if no error occurs, make sure Pager.journalMode is not set to -** PAGER_JOURNALMODE_WAL. -** -** Return SQLITE_OK or an error code. -** -** The caller must hold a SHARED lock on the database file to call this -** function. Because an EXCLUSIVE lock on the db file is required to delete -** a WAL on a none-empty database, this ensures there is no race condition -** between the xAccess() below and an xDelete() being executed by some -** other connection. -*/ -static int pagerOpenWalIfPresent(Pager *pPager){ - int rc = SQLITE_OK; - assert( pPager->eState==PAGER_OPEN ); - assert( pPager->eLock>=SHARED_LOCK ); - - if( !pPager->tempFile ){ - int isWal; /* True if WAL file exists */ - Pgno nPage; /* Size of the database file */ - - rc = pagerPagecount(pPager, &nPage); - if( rc ) return rc; - if( nPage==0 ){ - rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0); - if( rc==SQLITE_IOERR_DELETE_NOENT ) rc = SQLITE_OK; - isWal = 0; - }else{ - rc = sqlite3OsAccess( - pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal - ); - } - if( rc==SQLITE_OK ){ - if( isWal ){ - testcase( sqlite3PcachePagecount(pPager->pPCache)==0 ); - rc = sqlite3PagerOpenWal(pPager, 0); - }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){ - pPager->journalMode = PAGER_JOURNALMODE_DELETE; - } - } - } - return rc; -} -#endif - -/* -** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback -** the entire master journal file. The case pSavepoint==NULL occurs when -** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction -** savepoint. -** -** When pSavepoint is not NULL (meaning a non-transaction savepoint is -** being rolled back), then the rollback consists of up to three stages, -** performed in the order specified: -** -** * Pages are played back from the main journal starting at byte -** offset PagerSavepoint.iOffset and continuing to -** PagerSavepoint.iHdrOffset, or to the end of the main journal -** file if PagerSavepoint.iHdrOffset is zero. -** -** * If PagerSavepoint.iHdrOffset is not zero, then pages are played -** back starting from the journal header immediately following -** PagerSavepoint.iHdrOffset to the end of the main journal file. -** -** * Pages are then played back from the sub-journal file, starting -** with the PagerSavepoint.iSubRec and continuing to the end of -** the journal file. -** -** Throughout the rollback process, each time a page is rolled back, the -** corresponding bit is set in a bitvec structure (variable pDone in the -** implementation below). This is used to ensure that a page is only -** rolled back the first time it is encountered in either journal. -** -** If pSavepoint is NULL, then pages are only played back from the main -** journal file. There is no need for a bitvec in this case. -** -** In either case, before playback commences the Pager.dbSize variable -** is reset to the value that it held at the start of the savepoint -** (or transaction). No page with a page-number greater than this value -** is played back. If one is encountered it is simply skipped. -*/ -static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ - i64 szJ; /* Effective size of the main journal */ - i64 iHdrOff; /* End of first segment of main-journal records */ - int rc = SQLITE_OK; /* Return code */ - Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */ - - assert( pPager->eState!=PAGER_ERROR ); - assert( pPager->eState>=PAGER_WRITER_LOCKED ); - - /* Allocate a bitvec to use to store the set of pages rolled back */ - if( pSavepoint ){ - pDone = sqlite3BitvecCreate(pSavepoint->nOrig); - if( !pDone ){ - return SQLITE_NOMEM; - } - } - - /* Set the database size back to the value it was before the savepoint - ** being reverted was opened. - */ - pPager->dbSize = pSavepoint ? pSavepoint->nOrig : pPager->dbOrigSize; - pPager->changeCountDone = pPager->tempFile; - - if( !pSavepoint && pagerUseWal(pPager) ){ - return pagerRollbackWal(pPager); - } - - /* Use pPager->journalOff as the effective size of the main rollback - ** journal. The actual file might be larger than this in - ** PAGER_JOURNALMODE_TRUNCATE or PAGER_JOURNALMODE_PERSIST. But anything - ** past pPager->journalOff is off-limits to us. - */ - szJ = pPager->journalOff; - assert( pagerUseWal(pPager)==0 || szJ==0 ); - - /* Begin by rolling back records from the main journal starting at - ** PagerSavepoint.iOffset and continuing to the next journal header. - ** There might be records in the main journal that have a page number - ** greater than the current database size (pPager->dbSize) but those - ** will be skipped automatically. Pages are added to pDone as they - ** are played back. - */ - if( pSavepoint && !pagerUseWal(pPager) ){ - iHdrOff = pSavepoint->iHdrOffset ? pSavepoint->iHdrOffset : szJ; - pPager->journalOff = pSavepoint->iOffset; - while( rc==SQLITE_OK && pPager->journalOffjournalOff, pDone, 1, 1); - } - assert( rc!=SQLITE_DONE ); - }else{ - pPager->journalOff = 0; - } - - /* Continue rolling back records out of the main journal starting at - ** the first journal header seen and continuing until the effective end - ** of the main journal file. Continue to skip out-of-range pages and - ** continue adding pages rolled back to pDone. - */ - while( rc==SQLITE_OK && pPager->journalOffjournalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff" - ** test is related to ticket #2565. See the discussion in the - ** pager_playback() function for additional information. - */ - if( nJRec==0 - && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff - ){ - nJRec = (u32)((szJ - pPager->journalOff)/JOURNAL_PG_SZ(pPager)); - } - for(ii=0; rc==SQLITE_OK && iijournalOffjournalOff, pDone, 1, 1); - } - assert( rc!=SQLITE_DONE ); - } - assert( rc!=SQLITE_OK || pPager->journalOff>=szJ ); - - /* Finally, rollback pages from the sub-journal. Page that were - ** previously rolled back out of the main journal (and are hence in pDone) - ** will be skipped. Out-of-range pages are also skipped. - */ - if( pSavepoint ){ - u32 ii; /* Loop counter */ - i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize); - - if( pagerUseWal(pPager) ){ - rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData); - } - for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && iinSubRec; ii++){ - assert( offset==(i64)ii*(4+pPager->pageSize) ); - rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1); - } - assert( rc!=SQLITE_DONE ); - } - - sqlite3BitvecDestroy(pDone); - if( rc==SQLITE_OK ){ - pPager->journalOff = szJ; - } - - return rc; -} - -/* -** Change the maximum number of in-memory pages that are allowed. -*/ -SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){ - sqlite3PcacheSetCachesize(pPager->pPCache, mxPage); -} - -/* -** Free as much memory as possible from the pager. -*/ -SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){ - sqlite3PcacheShrink(pPager->pPCache); -} - -/* -** Adjust the robustness of the database to damage due to OS crashes -** or power failures by changing the number of syncs()s when writing -** the rollback journal. There are three levels: -** -** OFF sqlite3OsSync() is never called. This is the default -** for temporary and transient files. -** -** NORMAL The journal is synced once before writes begin on the -** database. This is normally adequate protection, but -** it is theoretically possible, though very unlikely, -** that an inopertune power failure could leave the journal -** in a state which would cause damage to the database -** when it is rolled back. -** -** FULL The journal is synced twice before writes begin on the -** database (with some additional information - the nRec field -** of the journal header - being written in between the two -** syncs). If we assume that writing a -** single disk sector is atomic, then this mode provides -** assurance that the journal will not be corrupted to the -** point of causing damage to the database during rollback. -** -** The above is for a rollback-journal mode. For WAL mode, OFF continues -** to mean that no syncs ever occur. NORMAL means that the WAL is synced -** prior to the start of checkpoint and that the database file is synced -** at the conclusion of the checkpoint if the entire content of the WAL -** was written back into the database. But no sync operations occur for -** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL -** file is synced following each commit operation, in addition to the -** syncs associated with NORMAL. -** -** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The -** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync -** using fcntl(F_FULLFSYNC). SQLITE_SYNC_NORMAL means to do an -** ordinary fsync() call. There is no difference between SQLITE_SYNC_FULL -** and SQLITE_SYNC_NORMAL on platforms other than MacOSX. But the -** synchronous=FULL versus synchronous=NORMAL setting determines when -** the xSync primitive is called and is relevant to all platforms. -** -** Numeric values associated with these states are OFF==1, NORMAL=2, -** and FULL=3. -*/ -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel( - Pager *pPager, /* The pager to set safety level for */ - int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */ - int bFullFsync, /* PRAGMA fullfsync */ - int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */ -){ - assert( level>=1 && level<=3 ); - pPager->noSync = (level==1 || pPager->tempFile) ?1:0; - pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0; - if( pPager->noSync ){ - pPager->syncFlags = 0; - pPager->ckptSyncFlags = 0; - }else if( bFullFsync ){ - pPager->syncFlags = SQLITE_SYNC_FULL; - pPager->ckptSyncFlags = SQLITE_SYNC_FULL; - }else if( bCkptFullFsync ){ - pPager->syncFlags = SQLITE_SYNC_NORMAL; - pPager->ckptSyncFlags = SQLITE_SYNC_FULL; - }else{ - pPager->syncFlags = SQLITE_SYNC_NORMAL; - pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; - } - pPager->walSyncFlags = pPager->syncFlags; - if( pPager->fullSync ){ - pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS; - } -} -#endif - -/* -** The following global variable is incremented whenever the library -** attempts to open a temporary file. This information is used for -** testing and analysis only. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_opentemp_count = 0; -#endif - -/* -** Open a temporary file. -** -** Write the file descriptor into *pFile. Return SQLITE_OK on success -** or some other error code if we fail. The OS will automatically -** delete the temporary file when it is closed. -** -** The flags passed to the VFS layer xOpen() call are those specified -** by parameter vfsFlags ORed with the following: -** -** SQLITE_OPEN_READWRITE -** SQLITE_OPEN_CREATE -** SQLITE_OPEN_EXCLUSIVE -** SQLITE_OPEN_DELETEONCLOSE -*/ -static int pagerOpentemp( - Pager *pPager, /* The pager object */ - sqlite3_file *pFile, /* Write the file descriptor here */ - int vfsFlags /* Flags passed through to the VFS */ -){ - int rc; /* Return code */ - -#ifdef SQLITE_TEST - sqlite3_opentemp_count++; /* Used for testing and analysis only */ -#endif - - vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE; - rc = sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0); - assert( rc!=SQLITE_OK || isOpen(pFile) ); - return rc; -} - -/* -** Set the busy handler function. -** -** The pager invokes the busy-handler if sqlite3OsLock() returns -** SQLITE_BUSY when trying to upgrade from no-lock to a SHARED lock, -** or when trying to upgrade from a RESERVED lock to an EXCLUSIVE -** lock. It does *not* invoke the busy handler when upgrading from -** SHARED to RESERVED, or when upgrading from SHARED to EXCLUSIVE -** (which occurs during hot-journal rollback). Summary: -** -** Transition | Invokes xBusyHandler -** -------------------------------------------------------- -** NO_LOCK -> SHARED_LOCK | Yes -** SHARED_LOCK -> RESERVED_LOCK | No -** SHARED_LOCK -> EXCLUSIVE_LOCK | No -** RESERVED_LOCK -> EXCLUSIVE_LOCK | Yes -** -** If the busy-handler callback returns non-zero, the lock is -** retried. If it returns zero, then the SQLITE_BUSY error is -** returned to the caller of the pager API function. -*/ -SQLITE_PRIVATE void sqlite3PagerSetBusyhandler( - Pager *pPager, /* Pager object */ - int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ - void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ -){ - pPager->xBusyHandler = xBusyHandler; - pPager->pBusyHandlerArg = pBusyHandlerArg; - - if( isOpen(pPager->fd) ){ - void **ap = (void **)&pPager->xBusyHandler; - assert( ((int(*)(void *))(ap[0]))==xBusyHandler ); - assert( ap[1]==pBusyHandlerArg ); - sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap); - } -} - -/* -** Change the page size used by the Pager object. The new page size -** is passed in *pPageSize. -** -** If the pager is in the error state when this function is called, it -** is a no-op. The value returned is the error state error code (i.e. -** one of SQLITE_IOERR, an SQLITE_IOERR_xxx sub-code or SQLITE_FULL). -** -** Otherwise, if all of the following are true: -** -** * the new page size (value of *pPageSize) is valid (a power -** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and -** -** * there are no outstanding page references, and -** -** * the database is either not an in-memory database or it is -** an in-memory database that currently consists of zero pages. -** -** then the pager object page size is set to *pPageSize. -** -** If the page size is changed, then this function uses sqlite3PagerMalloc() -** to obtain a new Pager.pTmpSpace buffer. If this allocation attempt -** fails, SQLITE_NOMEM is returned and the page size remains unchanged. -** In all other cases, SQLITE_OK is returned. -** -** If the page size is not changed, either because one of the enumerated -** conditions above is not true, the pager was in error state when this -** function was called, or because the memory allocation attempt failed, -** then *pPageSize is set to the old, retained page size before returning. -*/ -SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ - int rc = SQLITE_OK; - - /* It is not possible to do a full assert_pager_state() here, as this - ** function may be called from within PagerOpen(), before the state - ** of the Pager object is internally consistent. - ** - ** At one point this function returned an error if the pager was in - ** PAGER_ERROR state. But since PAGER_ERROR state guarantees that - ** there is at least one outstanding page reference, this function - ** is a no-op for that case anyhow. - */ - - u32 pageSize = *pPageSize; - assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); - if( (pPager->memDb==0 || pPager->dbSize==0) - && sqlite3PcacheRefCount(pPager->pPCache)==0 - && pageSize && pageSize!=(u32)pPager->pageSize - ){ - char *pNew = NULL; /* New temp space */ - i64 nByte = 0; - - if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){ - rc = sqlite3OsFileSize(pPager->fd, &nByte); - } - if( rc==SQLITE_OK ){ - pNew = (char *)sqlite3PageMalloc(pageSize); - if( !pNew ) rc = SQLITE_NOMEM; - } - - if( rc==SQLITE_OK ){ - pager_reset(pPager); - pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); - pPager->pageSize = pageSize; - sqlite3PageFree(pPager->pTmpSpace); - pPager->pTmpSpace = pNew; - sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); - } - } - - *pPageSize = pPager->pageSize; - if( rc==SQLITE_OK ){ - if( nReserve<0 ) nReserve = pPager->nReserve; - assert( nReserve>=0 && nReserve<1000 ); - pPager->nReserve = (i16)nReserve; - pagerReportSize(pPager); - } - return rc; -} - -/* -** Return a pointer to the "temporary page" buffer held internally -** by the pager. This is a buffer that is big enough to hold the -** entire content of a database page. This buffer is used internally -** during rollback and will be overwritten whenever a rollback -** occurs. But other modules are free to use it too, as long as -** no rollbacks are happening. -*/ -SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager *pPager){ - return pPager->pTmpSpace; -} - -/* -** Attempt to set the maximum database page count if mxPage is positive. -** Make no changes if mxPage is zero or negative. And never reduce the -** maximum page count below the current size of the database. -** -** Regardless of mxPage, return the current maximum page count. -*/ -SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ - if( mxPage>0 ){ - pPager->mxPgno = mxPage; - } - assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ - assert( pPager->mxPgno>=pPager->dbSize ); /* OP_MaxPgcnt enforces this */ - return pPager->mxPgno; -} - -/* -** The following set of routines are used to disable the simulated -** I/O error mechanism. These routines are used to avoid simulated -** errors in places where we do not care about errors. -** -** Unless -DSQLITE_TEST=1 is used, these routines are all no-ops -** and generate no code. -*/ -#ifdef SQLITE_TEST -SQLITE_API extern int sqlite3_io_error_pending; -SQLITE_API extern int sqlite3_io_error_hit; -static int saved_cnt; -void disable_simulated_io_errors(void){ - saved_cnt = sqlite3_io_error_pending; - sqlite3_io_error_pending = -1; -} -void enable_simulated_io_errors(void){ - sqlite3_io_error_pending = saved_cnt; -} -#else -# define disable_simulated_io_errors() -# define enable_simulated_io_errors() -#endif - -/* -** Read the first N bytes from the beginning of the file into memory -** that pDest points to. -** -** If the pager was opened on a transient file (zFilename==""), or -** opened on a file less than N bytes in size, the output buffer is -** zeroed and SQLITE_OK returned. The rationale for this is that this -** function is used to read database headers, and a new transient or -** zero sized database has a header than consists entirely of zeroes. -** -** If any IO error apart from SQLITE_IOERR_SHORT_READ is encountered, -** the error code is returned to the caller and the contents of the -** output buffer undefined. -*/ -SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){ - int rc = SQLITE_OK; - memset(pDest, 0, N); - assert( isOpen(pPager->fd) || pPager->tempFile ); - - /* This routine is only called by btree immediately after creating - ** the Pager object. There has not been an opportunity to transition - ** to WAL mode yet. - */ - assert( !pagerUseWal(pPager) ); - - if( isOpen(pPager->fd) ){ - IOTRACE(("DBHDR %p 0 %d\n", pPager, N)) - rc = sqlite3OsRead(pPager->fd, pDest, N, 0); - if( rc==SQLITE_IOERR_SHORT_READ ){ - rc = SQLITE_OK; - } - } - return rc; -} - -/* -** This function may only be called when a read-transaction is open on -** the pager. It returns the total number of pages in the database. -** -** However, if the file is between 1 and bytes in size, then -** this is considered a 1 page file. -*/ -SQLITE_PRIVATE void sqlite3PagerPagecount(Pager *pPager, int *pnPage){ - assert( pPager->eState>=PAGER_READER ); - assert( pPager->eState!=PAGER_WRITER_FINISHED ); - *pnPage = (int)pPager->dbSize; -} - - -/* -** Try to obtain a lock of type locktype on the database file. If -** a similar or greater lock is already held, this function is a no-op -** (returning SQLITE_OK immediately). -** -** Otherwise, attempt to obtain the lock using sqlite3OsLock(). Invoke -** the busy callback if the lock is currently not available. Repeat -** until the busy callback returns false or until the attempt to -** obtain the lock succeeds. -** -** Return SQLITE_OK on success and an error code if we cannot obtain -** the lock. If the lock is obtained successfully, set the Pager.state -** variable to locktype before returning. -*/ -static int pager_wait_on_lock(Pager *pPager, int locktype){ - int rc; /* Return code */ - - /* Check that this is either a no-op (because the requested lock is - ** already held, or one of the transistions that the busy-handler - ** may be invoked during, according to the comment above - ** sqlite3PagerSetBusyhandler(). - */ - assert( (pPager->eLock>=locktype) - || (pPager->eLock==NO_LOCK && locktype==SHARED_LOCK) - || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK) - ); - - do { - rc = pagerLockDb(pPager, locktype); - }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) ); - return rc; -} - -/* -** Function assertTruncateConstraint(pPager) checks that one of the -** following is true for all dirty pages currently in the page-cache: -** -** a) The page number is less than or equal to the size of the -** current database image, in pages, OR -** -** b) if the page content were written at this time, it would not -** be necessary to write the current content out to the sub-journal -** (as determined by function subjRequiresPage()). -** -** If the condition asserted by this function were not true, and the -** dirty page were to be discarded from the cache via the pagerStress() -** routine, pagerStress() would not write the current page content to -** the database file. If a savepoint transaction were rolled back after -** this happened, the correct behavior would be to restore the current -** content of the page. However, since this content is not present in either -** the database file or the portion of the rollback journal and -** sub-journal rolled back the content could not be restored and the -** database image would become corrupt. It is therefore fortunate that -** this circumstance cannot arise. -*/ -#if defined(SQLITE_DEBUG) -static void assertTruncateConstraintCb(PgHdr *pPg){ - assert( pPg->flags&PGHDR_DIRTY ); - assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize ); -} -static void assertTruncateConstraint(Pager *pPager){ - sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb); -} -#else -# define assertTruncateConstraint(pPager) -#endif - -/* -** Truncate the in-memory database file image to nPage pages. This -** function does not actually modify the database file on disk. It -** just sets the internal state of the pager object so that the -** truncation will be done when the current transaction is committed. -** -** This function is only called right before committing a transaction. -** Once this function has been called, the transaction must either be -** rolled back or committed. It is not safe to call this function and -** then continue writing to the database. -*/ -SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ - assert( pPager->dbSize>=nPage ); - assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); - pPager->dbSize = nPage; - - /* At one point the code here called assertTruncateConstraint() to - ** ensure that all pages being truncated away by this operation are, - ** if one or more savepoints are open, present in the savepoint - ** journal so that they can be restored if the savepoint is rolled - ** back. This is no longer necessary as this function is now only - ** called right before committing a transaction. So although the - ** Pager object may still have open savepoints (Pager.nSavepoint!=0), - ** they cannot be rolled back. So the assertTruncateConstraint() call - ** is no longer correct. */ -} - - -/* -** This function is called before attempting a hot-journal rollback. It -** syncs the journal file to disk, then sets pPager->journalHdr to the -** size of the journal file so that the pager_playback() routine knows -** that the entire journal file has been synced. -** -** Syncing a hot-journal to disk before attempting to roll it back ensures -** that if a power-failure occurs during the rollback, the process that -** attempts rollback following system recovery sees the same journal -** content as this process. -** -** If everything goes as planned, SQLITE_OK is returned. Otherwise, -** an SQLite error code. -*/ -static int pagerSyncHotJournal(Pager *pPager){ - int rc = SQLITE_OK; - if( !pPager->noSync ){ - rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_NORMAL); - } - if( rc==SQLITE_OK ){ - rc = sqlite3OsFileSize(pPager->jfd, &pPager->journalHdr); - } - return rc; -} - -/* -** Shutdown the page cache. Free all memory and close all files. -** -** If a transaction was in progress when this routine is called, that -** transaction is rolled back. All outstanding pages are invalidated -** and their memory is freed. Any attempt to use a page associated -** with this page cache after this function returns will likely -** result in a coredump. -** -** This function always succeeds. If a transaction is active an attempt -** is made to roll it back. If an error occurs during the rollback -** a hot journal may be left in the filesystem but no error is returned -** to the caller. -*/ -SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){ - u8 *pTmp = (u8 *)pPager->pTmpSpace; - - assert( assert_pager_state(pPager) ); - disable_simulated_io_errors(); - sqlite3BeginBenignMalloc(); - /* pPager->errCode = 0; */ - pPager->exclusiveMode = 0; -#ifndef SQLITE_OMIT_WAL - sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp); - pPager->pWal = 0; -#endif - pager_reset(pPager); - if( MEMDB ){ - pager_unlock(pPager); - }else{ - /* If it is open, sync the journal file before calling UnlockAndRollback. - ** If this is not done, then an unsynced portion of the open journal - ** file may be played back into the database. If a power failure occurs - ** while this is happening, the database could become corrupt. - ** - ** If an error occurs while trying to sync the journal, shift the pager - ** into the ERROR state. This causes UnlockAndRollback to unlock the - ** database and close the journal file without attempting to roll it - ** back or finalize it. The next database user will have to do hot-journal - ** rollback before accessing the database file. - */ - if( isOpen(pPager->jfd) ){ - pager_error(pPager, pagerSyncHotJournal(pPager)); - } - pagerUnlockAndRollback(pPager); - } - sqlite3EndBenignMalloc(); - enable_simulated_io_errors(); - PAGERTRACE(("CLOSE %d\n", PAGERID(pPager))); - IOTRACE(("CLOSE %p\n", pPager)) - sqlite3OsClose(pPager->jfd); - sqlite3OsClose(pPager->fd); - sqlite3PageFree(pTmp); - sqlite3PcacheClose(pPager->pPCache); - -#ifdef SQLITE_HAS_CODEC - if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); -#endif - - assert( !pPager->aSavepoint && !pPager->pInJournal ); - assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ); - - sqlite3_free(pPager); - return SQLITE_OK; -} - -#if !defined(NDEBUG) || defined(SQLITE_TEST) -/* -** Return the page number for page pPg. -*/ -SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage *pPg){ - return pPg->pgno; -} -#endif - -/* -** Increment the reference count for page pPg. -*/ -SQLITE_PRIVATE void sqlite3PagerRef(DbPage *pPg){ - sqlite3PcacheRef(pPg); -} - -/* -** Sync the journal. In other words, make sure all the pages that have -** been written to the journal have actually reached the surface of the -** disk and can be restored in the event of a hot-journal rollback. -** -** If the Pager.noSync flag is set, then this function is a no-op. -** Otherwise, the actions required depend on the journal-mode and the -** device characteristics of the file-system, as follows: -** -** * If the journal file is an in-memory journal file, no action need -** be taken. -** -** * Otherwise, if the device does not support the SAFE_APPEND property, -** then the nRec field of the most recently written journal header -** is updated to contain the number of journal records that have -** been written following it. If the pager is operating in full-sync -** mode, then the journal file is synced before this field is updated. -** -** * If the device does not support the SEQUENTIAL property, then -** journal file is synced. -** -** Or, in pseudo-code: -** -** if( NOT ){ -** if( NOT SAFE_APPEND ){ -** if( ) xSync(); -** -** } -** if( NOT SEQUENTIAL ) xSync(); -** } -** -** If successful, this routine clears the PGHDR_NEED_SYNC flag of every -** page currently held in memory before returning SQLITE_OK. If an IO -** error is encountered, then the IO error code is returned to the caller. -*/ -static int syncJournal(Pager *pPager, int newHdr){ - int rc; /* Return code */ - - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - assert( !pagerUseWal(pPager) ); - - rc = sqlite3PagerExclusiveLock(pPager); - if( rc!=SQLITE_OK ) return rc; - - if( !pPager->noSync ){ - assert( !pPager->tempFile ); - if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){ - const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); - assert( isOpen(pPager->jfd) ); - - if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ - /* This block deals with an obscure problem. If the last connection - ** that wrote to this database was operating in persistent-journal - ** mode, then the journal file may at this point actually be larger - ** than Pager.journalOff bytes. If the next thing in the journal - ** file happens to be a journal-header (written as part of the - ** previous connection's transaction), and a crash or power-failure - ** occurs after nRec is updated but before this connection writes - ** anything else to the journal file (or commits/rolls back its - ** transaction), then SQLite may become confused when doing the - ** hot-journal rollback following recovery. It may roll back all - ** of this connections data, then proceed to rolling back the old, - ** out-of-date data that follows it. Database corruption. - ** - ** To work around this, if the journal file does appear to contain - ** a valid header following Pager.journalOff, then write a 0x00 - ** byte to the start of it to prevent it from being recognized. - ** - ** Variable iNextHdrOffset is set to the offset at which this - ** problematic header will occur, if it exists. aMagic is used - ** as a temporary buffer to inspect the first couple of bytes of - ** the potential journal header. - */ - i64 iNextHdrOffset; - u8 aMagic[8]; - u8 zHeader[sizeof(aJournalMagic)+4]; - - memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); - put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec); - - iNextHdrOffset = journalHdrOffset(pPager); - rc = sqlite3OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset); - if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){ - static const u8 zerobyte = 0; - rc = sqlite3OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset); - } - if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ - return rc; - } - - /* Write the nRec value into the journal file header. If in - ** full-synchronous mode, sync the journal first. This ensures that - ** all data has really hit the disk before nRec is updated to mark - ** it as a candidate for rollback. - ** - ** This is not required if the persistent media supports the - ** SAFE_APPEND property. Because in this case it is not possible - ** for garbage data to be appended to the file, the nRec field - ** is populated with 0xFFFFFFFF when the journal header is written - ** and never needs to be updated. - */ - if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ - PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); - IOTRACE(("JSYNC %p\n", pPager)) - rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags); - if( rc!=SQLITE_OK ) return rc; - } - IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); - rc = sqlite3OsWrite( - pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr - ); - if( rc!=SQLITE_OK ) return rc; - } - if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ - PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); - IOTRACE(("JSYNC %p\n", pPager)) - rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags| - (pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) - ); - if( rc!=SQLITE_OK ) return rc; - } - - pPager->journalHdr = pPager->journalOff; - if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ - pPager->nRec = 0; - rc = writeJournalHdr(pPager); - if( rc!=SQLITE_OK ) return rc; - } - }else{ - pPager->journalHdr = pPager->journalOff; - } - } - - /* Unless the pager is in noSync mode, the journal file was just - ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on - ** all pages. - */ - sqlite3PcacheClearSyncFlags(pPager->pPCache); - pPager->eState = PAGER_WRITER_DBMOD; - assert( assert_pager_state(pPager) ); - return SQLITE_OK; -} - -/* -** The argument is the first in a linked list of dirty pages connected -** by the PgHdr.pDirty pointer. This function writes each one of the -** in-memory pages in the list to the database file. The argument may -** be NULL, representing an empty list. In this case this function is -** a no-op. -** -** The pager must hold at least a RESERVED lock when this function -** is called. Before writing anything to the database file, this lock -** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained, -** SQLITE_BUSY is returned and no data is written to the database file. -** -** If the pager is a temp-file pager and the actual file-system file -** is not yet open, it is created and opened before any data is -** written out. -** -** Once the lock has been upgraded and, if necessary, the file opened, -** the pages are written out to the database file in list order. Writing -** a page is skipped if it meets either of the following criteria: -** -** * The page number is greater than Pager.dbSize, or -** * The PGHDR_DONT_WRITE flag is set on the page. -** -** If writing out a page causes the database file to grow, Pager.dbFileSize -** is updated accordingly. If page 1 is written out, then the value cached -** in Pager.dbFileVers[] is updated to match the new value stored in -** the database file. -** -** If everything is successful, SQLITE_OK is returned. If an IO error -** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot -** be obtained, SQLITE_BUSY is returned. -*/ -static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ - int rc = SQLITE_OK; /* Return code */ - - /* This function is only called for rollback pagers in WRITER_DBMOD state. */ - assert( !pagerUseWal(pPager) ); - assert( pPager->eState==PAGER_WRITER_DBMOD ); - assert( pPager->eLock==EXCLUSIVE_LOCK ); - - /* If the file is a temp-file has not yet been opened, open it now. It - ** is not possible for rc to be other than SQLITE_OK if this branch - ** is taken, as pager_wait_on_lock() is a no-op for temp-files. - */ - if( !isOpen(pPager->fd) ){ - assert( pPager->tempFile && rc==SQLITE_OK ); - rc = pagerOpentemp(pPager, pPager->fd, pPager->vfsFlags); - } - - /* Before the first write, give the VFS a hint of what the final - ** file size will be. - */ - assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); - if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){ - sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize; - sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); - pPager->dbHintSize = pPager->dbSize; - } - - while( rc==SQLITE_OK && pList ){ - Pgno pgno = pList->pgno; - - /* If there are dirty pages in the page cache with page numbers greater - ** than Pager.dbSize, this means sqlite3PagerTruncateImage() was called to - ** make the file smaller (presumably by auto-vacuum code). Do not write - ** any such pages to the file. - ** - ** Also, do not write out any page that has the PGHDR_DONT_WRITE flag - ** set (set by sqlite3PagerDontWrite()). - */ - if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ - i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */ - char *pData; /* Data to write */ - - assert( (pList->flags&PGHDR_NEED_SYNC)==0 ); - if( pList->pgno==1 ) pager_write_changecounter(pList); - - /* Encode the database */ - CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData); - - /* Write out the page data. */ - rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); - - /* If page 1 was just written, update Pager.dbFileVers to match - ** the value now stored in the database file. If writing this - ** page caused the database file to grow, update dbFileSize. - */ - if( pgno==1 ){ - memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers)); - } - if( pgno>pPager->dbFileSize ){ - pPager->dbFileSize = pgno; - } - pPager->aStat[PAGER_STAT_WRITE]++; - - /* Update any backup objects copying the contents of this pager. */ - sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData); - - PAGERTRACE(("STORE %d page %d hash(%08x)\n", - PAGERID(pPager), pgno, pager_pagehash(pList))); - IOTRACE(("PGOUT %p %d\n", pPager, pgno)); - PAGER_INCR(sqlite3_pager_writedb_count); - }else{ - PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno)); - } - pager_set_pagehash(pList); - pList = pList->pDirty; - } - - return rc; -} - -/* -** Ensure that the sub-journal file is open. If it is already open, this -** function is a no-op. -** -** SQLITE_OK is returned if everything goes according to plan. An -** SQLITE_IOERR_XXX error code is returned if a call to sqlite3OsOpen() -** fails. -*/ -static int openSubJournal(Pager *pPager){ - int rc = SQLITE_OK; - if( !isOpen(pPager->sjfd) ){ - if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){ - sqlite3MemJournalOpen(pPager->sjfd); - }else{ - rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL); - } - } - return rc; -} - -/* -** Append a record of the current state of page pPg to the sub-journal. -** It is the callers responsibility to use subjRequiresPage() to check -** that it is really required before calling this function. -** -** If successful, set the bit corresponding to pPg->pgno in the bitvecs -** for all open savepoints before returning. -** -** This function returns SQLITE_OK if everything is successful, an IO -** error code if the attempt to write to the sub-journal fails, or -** SQLITE_NOMEM if a malloc fails while setting a bit in a savepoint -** bitvec. -*/ -static int subjournalPage(PgHdr *pPg){ - int rc = SQLITE_OK; - Pager *pPager = pPg->pPager; - if( pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ - - /* Open the sub-journal, if it has not already been opened */ - assert( pPager->useJournal ); - assert( isOpen(pPager->jfd) || pagerUseWal(pPager) ); - assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 ); - assert( pagerUseWal(pPager) - || pageInJournal(pPg) - || pPg->pgno>pPager->dbOrigSize - ); - rc = openSubJournal(pPager); - - /* If the sub-journal was opened successfully (or was already open), - ** write the journal record into the file. */ - if( rc==SQLITE_OK ){ - void *pData = pPg->pData; - i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); - char *pData2; - - CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); - PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); - rc = write32bits(pPager->sjfd, offset, pPg->pgno); - if( rc==SQLITE_OK ){ - rc = sqlite3OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4); - } - } - } - if( rc==SQLITE_OK ){ - pPager->nSubRec++; - assert( pPager->nSavepoint>0 ); - rc = addToSavepointBitvecs(pPager, pPg->pgno); - } - return rc; -} - -/* -** This function is called by the pcache layer when it has reached some -** soft memory limit. The first argument is a pointer to a Pager object -** (cast as a void*). The pager is always 'purgeable' (not an in-memory -** database). The second argument is a reference to a page that is -** currently dirty but has no outstanding references. The page -** is always associated with the Pager object passed as the first -** argument. -** -** The job of this function is to make pPg clean by writing its contents -** out to the database file, if possible. This may involve syncing the -** journal file. -** -** If successful, sqlite3PcacheMakeClean() is called on the page and -** SQLITE_OK returned. If an IO error occurs while trying to make the -** page clean, the IO error code is returned. If the page cannot be -** made clean for some other reason, but no error occurs, then SQLITE_OK -** is returned by sqlite3PcacheMakeClean() is not called. -*/ -static int pagerStress(void *p, PgHdr *pPg){ - Pager *pPager = (Pager *)p; - int rc = SQLITE_OK; - - assert( pPg->pPager==pPager ); - assert( pPg->flags&PGHDR_DIRTY ); - - /* The doNotSyncSpill flag is set during times when doing a sync of - ** journal (and adding a new header) is not allowed. This occurs - ** during calls to sqlite3PagerWrite() while trying to journal multiple - ** pages belonging to the same sector. - ** - ** The doNotSpill flag inhibits all cache spilling regardless of whether - ** or not a sync is required. This is set during a rollback. - ** - ** Spilling is also prohibited when in an error state since that could - ** lead to database corruption. In the current implementaton it - ** is impossible for sqlite3PcacheFetch() to be called with createFlag==1 - ** while in the error state, hence it is impossible for this routine to - ** be called in the error state. Nevertheless, we include a NEVER() - ** test for the error state as a safeguard against future changes. - */ - if( NEVER(pPager->errCode) ) return SQLITE_OK; - if( pPager->doNotSpill ) return SQLITE_OK; - if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){ - return SQLITE_OK; - } - - pPg->pDirty = 0; - if( pagerUseWal(pPager) ){ - /* Write a single frame for this page to the log. */ - if( subjRequiresPage(pPg) ){ - rc = subjournalPage(pPg); - } - if( rc==SQLITE_OK ){ - rc = pagerWalFrames(pPager, pPg, 0, 0); - } - }else{ - - /* Sync the journal file if required. */ - if( pPg->flags&PGHDR_NEED_SYNC - || pPager->eState==PAGER_WRITER_CACHEMOD - ){ - rc = syncJournal(pPager, 1); - } - - /* If the page number of this page is larger than the current size of - ** the database image, it may need to be written to the sub-journal. - ** This is because the call to pager_write_pagelist() below will not - ** actually write data to the file in this case. - ** - ** Consider the following sequence of events: - ** - ** BEGIN; - ** - ** - ** SAVEPOINT sp; - ** - ** pagerStress(page X) - ** ROLLBACK TO sp; - ** - ** If (X>Y), then when pagerStress is called page X will not be written - ** out to the database file, but will be dropped from the cache. Then, - ** following the "ROLLBACK TO sp" statement, reading page X will read - ** data from the database file. This will be the copy of page X as it - ** was when the transaction started, not as it was when "SAVEPOINT sp" - ** was executed. - ** - ** The solution is to write the current data for page X into the - ** sub-journal file now (if it is not already there), so that it will - ** be restored to its current value when the "ROLLBACK TO sp" is - ** executed. - */ - if( NEVER( - rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) - ) ){ - rc = subjournalPage(pPg); - } - - /* Write the contents of the page out to the database file. */ - if( rc==SQLITE_OK ){ - assert( (pPg->flags&PGHDR_NEED_SYNC)==0 ); - rc = pager_write_pagelist(pPager, pPg); - } - } - - /* Mark the page as clean. */ - if( rc==SQLITE_OK ){ - PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno)); - sqlite3PcacheMakeClean(pPg); - } - - return pager_error(pPager, rc); -} - - -/* -** Allocate and initialize a new Pager object and put a pointer to it -** in *ppPager. The pager should eventually be freed by passing it -** to sqlite3PagerClose(). -** -** The zFilename argument is the path to the database file to open. -** If zFilename is NULL then a randomly-named temporary file is created -** and used as the file to be cached. Temporary files are be deleted -** automatically when they are closed. If zFilename is ":memory:" then -** all information is held in cache. It is never written to disk. -** This can be used to implement an in-memory database. -** -** The nExtra parameter specifies the number of bytes of space allocated -** along with each page reference. This space is available to the user -** via the sqlite3PagerGetExtra() API. -** -** The flags argument is used to specify properties that affect the -** operation of the pager. It should be passed some bitwise combination -** of the PAGER_* flags. -** -** The vfsFlags parameter is a bitmask to pass to the flags parameter -** of the xOpen() method of the supplied VFS when opening files. -** -** If the pager object is allocated and the specified file opened -** successfully, SQLITE_OK is returned and *ppPager set to point to -** the new pager object. If an error occurs, *ppPager is set to NULL -** and error code returned. This function may return SQLITE_NOMEM -** (sqlite3Malloc() is used to allocate memory), SQLITE_CANTOPEN or -** various SQLITE_IO_XXX errors. -*/ -SQLITE_PRIVATE int sqlite3PagerOpen( - sqlite3_vfs *pVfs, /* The virtual file system to use */ - Pager **ppPager, /* OUT: Return the Pager structure here */ - const char *zFilename, /* Name of the database file to open */ - int nExtra, /* Extra bytes append to each in-memory page */ - int flags, /* flags controlling this file */ - int vfsFlags, /* flags passed through to sqlite3_vfs.xOpen() */ - void (*xReinit)(DbPage*) /* Function to reinitialize pages */ -){ - u8 *pPtr; - Pager *pPager = 0; /* Pager object to allocate and return */ - int rc = SQLITE_OK; /* Return code */ - int tempFile = 0; /* True for temp files (incl. in-memory files) */ - int memDb = 0; /* True if this is an in-memory file */ - int readOnly = 0; /* True if this is a read-only file */ - int journalFileSize; /* Bytes to allocate for each journal fd */ - char *zPathname = 0; /* Full path to database file */ - int nPathname = 0; /* Number of bytes in zPathname */ - int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ - int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ - u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ - const char *zUri = 0; /* URI args to copy */ - int nUri = 0; /* Number of bytes of URI args at *zUri */ - - /* Figure out how much space is required for each journal file-handle - ** (there are two of them, the main journal and the sub-journal). This - ** is the maximum space required for an in-memory journal file handle - ** and a regular journal file-handle. Note that a "regular journal-handle" - ** may be a wrapper capable of caching the first portion of the journal - ** file in memory to implement the atomic-write optimization (see - ** source file journal.c). - */ - if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){ - journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); - }else{ - journalFileSize = ROUND8(sqlite3MemJournalSize()); - } - - /* Set the output variable to NULL in case an error occurs. */ - *ppPager = 0; - -#ifndef SQLITE_OMIT_MEMORYDB - if( flags & PAGER_MEMORY ){ - memDb = 1; - if( zFilename && zFilename[0] ){ - zPathname = sqlite3DbStrDup(0, zFilename); - if( zPathname==0 ) return SQLITE_NOMEM; - nPathname = sqlite3Strlen30(zPathname); - zFilename = 0; - } - } -#endif - - /* Compute and store the full pathname in an allocated buffer pointed - ** to by zPathname, length nPathname. Or, if this is a temporary file, - ** leave both nPathname and zPathname set to 0. - */ - if( zFilename && zFilename[0] ){ - const char *z; - nPathname = pVfs->mxPathname+1; - zPathname = sqlite3DbMallocRaw(0, nPathname*2); - if( zPathname==0 ){ - return SQLITE_NOMEM; - } - zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ - rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); - nPathname = sqlite3Strlen30(zPathname); - z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1]; - while( *z ){ - z += sqlite3Strlen30(z)+1; - z += sqlite3Strlen30(z)+1; - } - nUri = (int)(&z[1] - zUri); - assert( nUri>=0 ); - if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ - /* This branch is taken when the journal path required by - ** the database being opened will be more than pVfs->mxPathname - ** bytes in length. This means the database cannot be opened, - ** as it will not be possible to open the journal file or even - ** check for a hot-journal before reading. - */ - rc = SQLITE_CANTOPEN_BKPT; - } - if( rc!=SQLITE_OK ){ - sqlite3DbFree(0, zPathname); - return rc; - } - } - - /* Allocate memory for the Pager structure, PCache object, the - ** three file descriptors, the database file name and the journal - ** file name. The layout in memory is as follows: - ** - ** Pager object (sizeof(Pager) bytes) - ** PCache object (sqlite3PcacheSize() bytes) - ** Database file handle (pVfs->szOsFile bytes) - ** Sub-journal file handle (journalFileSize bytes) - ** Main journal file handle (journalFileSize bytes) - ** Database file name (nPathname+1 bytes) - ** Journal file name (nPathname+8+1 bytes) - */ - pPtr = (u8 *)sqlite3MallocZero( - ROUND8(sizeof(*pPager)) + /* Pager structure */ - ROUND8(pcacheSize) + /* PCache object */ - ROUND8(pVfs->szOsFile) + /* The main db file */ - journalFileSize * 2 + /* The two journal files */ - nPathname + 1 + nUri + /* zFilename */ - nPathname + 8 + 2 /* zJournal */ -#ifndef SQLITE_OMIT_WAL - + nPathname + 4 + 2 /* zWal */ -#endif - ); - assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); - if( !pPtr ){ - sqlite3DbFree(0, zPathname); - return SQLITE_NOMEM; - } - pPager = (Pager*)(pPtr); - pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager))); - pPager->fd = (sqlite3_file*)(pPtr += ROUND8(pcacheSize)); - pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile)); - pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize); - pPager->zFilename = (char*)(pPtr += journalFileSize); - assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); - - /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ - if( zPathname ){ - assert( nPathname>0 ); - pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri); - memcpy(pPager->zFilename, zPathname, nPathname); - if( nUri ) memcpy(&pPager->zFilename[nPathname+1], zUri, nUri); - memcpy(pPager->zJournal, zPathname, nPathname); - memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+2); - sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal); -#ifndef SQLITE_OMIT_WAL - pPager->zWal = &pPager->zJournal[nPathname+8+1]; - memcpy(pPager->zWal, zPathname, nPathname); - memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1); - sqlite3FileSuffix3(pPager->zFilename, pPager->zWal); -#endif - sqlite3DbFree(0, zPathname); - } - pPager->pVfs = pVfs; - pPager->vfsFlags = vfsFlags; - - /* Open the pager file. - */ - if( zFilename && zFilename[0] ){ - int fout = 0; /* VFS flags returned by xOpen() */ - rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); - assert( !memDb ); - readOnly = (fout&SQLITE_OPEN_READONLY); - - /* If the file was successfully opened for read/write access, - ** choose a default page size in case we have to create the - ** database file. The default page size is the maximum of: - ** - ** + SQLITE_DEFAULT_PAGE_SIZE, - ** + The value returned by sqlite3OsSectorSize() - ** + The largest page size that can be written atomically. - */ - if( rc==SQLITE_OK && !readOnly ){ - setSectorSize(pPager); - assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE); - if( szPageDfltsectorSize ){ - if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){ - szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE; - }else{ - szPageDflt = (u32)pPager->sectorSize; - } - } -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - { - int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); - int ii; - assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); - assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); - assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536); - for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){ - if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){ - szPageDflt = ii; - } - } - } -#endif - } - }else{ - /* If a temporary file is requested, it is not opened immediately. - ** In this case we accept the default page size and delay actually - ** opening the file until the first call to OsWrite(). - ** - ** This branch is also run for an in-memory database. An in-memory - ** database is the same as a temp-file that is never written out to - ** disk and uses an in-memory rollback journal. - */ - tempFile = 1; - pPager->eState = PAGER_READER; - pPager->eLock = EXCLUSIVE_LOCK; - readOnly = (vfsFlags&SQLITE_OPEN_READONLY); - } - - /* The following call to PagerSetPagesize() serves to set the value of - ** Pager.pageSize and to allocate the Pager.pTmpSpace buffer. - */ - if( rc==SQLITE_OK ){ - assert( pPager->memDb==0 ); - rc = sqlite3PagerSetPagesize(pPager, &szPageDflt, -1); - testcase( rc!=SQLITE_OK ); - } - - /* If an error occurred in either of the blocks above, free the - ** Pager structure and close the file. - */ - if( rc!=SQLITE_OK ){ - assert( !pPager->pTmpSpace ); - sqlite3OsClose(pPager->fd); - sqlite3_free(pPager); - return rc; - } - - /* Initialize the PCache object. */ - assert( nExtra<1000 ); - nExtra = ROUND8(nExtra); - sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, - !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); - - PAGERTRACE(("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename)); - IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename)) - - pPager->useJournal = (u8)useJournal; - /* pPager->stmtOpen = 0; */ - /* pPager->stmtInUse = 0; */ - /* pPager->nRef = 0; */ - /* pPager->stmtSize = 0; */ - /* pPager->stmtJSize = 0; */ - /* pPager->nPage = 0; */ - pPager->mxPgno = SQLITE_MAX_PAGE_COUNT; - /* pPager->state = PAGER_UNLOCK; */ -#if 0 - assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) ); -#endif - /* pPager->errMask = 0; */ - pPager->tempFile = (u8)tempFile; - assert( tempFile==PAGER_LOCKINGMODE_NORMAL - || tempFile==PAGER_LOCKINGMODE_EXCLUSIVE ); - assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 ); - pPager->exclusiveMode = (u8)tempFile; - pPager->changeCountDone = pPager->tempFile; - pPager->memDb = (u8)memDb; - pPager->readOnly = (u8)readOnly; - assert( useJournal || pPager->tempFile ); - pPager->noSync = pPager->tempFile; - if( pPager->noSync ){ - assert( pPager->fullSync==0 ); - assert( pPager->syncFlags==0 ); - assert( pPager->walSyncFlags==0 ); - assert( pPager->ckptSyncFlags==0 ); - }else{ - pPager->fullSync = 1; - pPager->syncFlags = SQLITE_SYNC_NORMAL; - pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS; - pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; - } - /* pPager->pFirst = 0; */ - /* pPager->pFirstSynced = 0; */ - /* pPager->pLast = 0; */ - pPager->nExtra = (u16)nExtra; - pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; - assert( isOpen(pPager->fd) || tempFile ); - setSectorSize(pPager); - if( !useJournal ){ - pPager->journalMode = PAGER_JOURNALMODE_OFF; - }else if( memDb ){ - pPager->journalMode = PAGER_JOURNALMODE_MEMORY; - } - /* pPager->xBusyHandler = 0; */ - /* pPager->pBusyHandlerArg = 0; */ - pPager->xReiniter = xReinit; - /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ - - *ppPager = pPager; - return SQLITE_OK; -} - - - -/* -** This function is called after transitioning from PAGER_UNLOCK to -** PAGER_SHARED state. It tests if there is a hot journal present in -** the file-system for the given pager. A hot journal is one that -** needs to be played back. According to this function, a hot-journal -** file exists if the following criteria are met: -** -** * The journal file exists in the file system, and -** * No process holds a RESERVED or greater lock on the database file, and -** * The database file itself is greater than 0 bytes in size, and -** * The first byte of the journal file exists and is not 0x00. -** -** If the current size of the database file is 0 but a journal file -** exists, that is probably an old journal left over from a prior -** database with the same name. In this case the journal file is -** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK -** is returned. -** -** This routine does not check if there is a master journal filename -** at the end of the file. If there is, and that master journal file -** does not exist, then the journal file is not really hot. In this -** case this routine will return a false-positive. The pager_playback() -** routine will discover that the journal file is not really hot and -** will not roll it back. -** -** If a hot-journal file is found to exist, *pExists is set to 1 and -** SQLITE_OK returned. If no hot-journal file is present, *pExists is -** set to 0 and SQLITE_OK returned. If an IO error occurs while trying -** to determine whether or not a hot-journal file exists, the IO error -** code is returned and the value of *pExists is undefined. -*/ -static int hasHotJournal(Pager *pPager, int *pExists){ - sqlite3_vfs * const pVfs = pPager->pVfs; - int rc = SQLITE_OK; /* Return code */ - int exists = 1; /* True if a journal file is present */ - int jrnlOpen = !!isOpen(pPager->jfd); - - assert( pPager->useJournal ); - assert( isOpen(pPager->fd) ); - assert( pPager->eState==PAGER_OPEN ); - - assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) & - SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN - )); - - *pExists = 0; - if( !jrnlOpen ){ - rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); - } - if( rc==SQLITE_OK && exists ){ - int locked = 0; /* True if some process holds a RESERVED lock */ - - /* Race condition here: Another process might have been holding the - ** the RESERVED lock and have a journal open at the sqlite3OsAccess() - ** call above, but then delete the journal and drop the lock before - ** we get to the following sqlite3OsCheckReservedLock() call. If that - ** is the case, this routine might think there is a hot journal when - ** in fact there is none. This results in a false-positive which will - ** be dealt with by the playback routine. Ticket #3883. - */ - rc = sqlite3OsCheckReservedLock(pPager->fd, &locked); - if( rc==SQLITE_OK && !locked ){ - Pgno nPage; /* Number of pages in database file */ - - /* Check the size of the database file. If it consists of 0 pages, - ** then delete the journal file. See the header comment above for - ** the reasoning here. Delete the obsolete journal file under - ** a RESERVED lock to avoid race conditions and to avoid violating - ** [H33020]. - */ - rc = pagerPagecount(pPager, &nPage); - if( rc==SQLITE_OK ){ - if( nPage==0 ){ - sqlite3BeginBenignMalloc(); - if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ - sqlite3OsDelete(pVfs, pPager->zJournal, 0); - if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); - } - sqlite3EndBenignMalloc(); - }else{ - /* The journal file exists and no other connection has a reserved - ** or greater lock on the database file. Now check that there is - ** at least one non-zero bytes at the start of the journal file. - ** If there is, then we consider this journal to be hot. If not, - ** it can be ignored. - */ - if( !jrnlOpen ){ - int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; - rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f); - } - if( rc==SQLITE_OK ){ - u8 first = 0; - rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0); - if( rc==SQLITE_IOERR_SHORT_READ ){ - rc = SQLITE_OK; - } - if( !jrnlOpen ){ - sqlite3OsClose(pPager->jfd); - } - *pExists = (first!=0); - }else if( rc==SQLITE_CANTOPEN ){ - /* If we cannot open the rollback journal file in order to see if - ** its has a zero header, that might be due to an I/O error, or - ** it might be due to the race condition described above and in - ** ticket #3883. Either way, assume that the journal is hot. - ** This might be a false positive. But if it is, then the - ** automatic journal playback and recovery mechanism will deal - ** with it under an EXCLUSIVE lock where we do not need to - ** worry so much with race conditions. - */ - *pExists = 1; - rc = SQLITE_OK; - } - } - } - } - } - - return rc; -} - -/* -** This function is called to obtain a shared lock on the database file. -** It is illegal to call sqlite3PagerAcquire() until after this function -** has been successfully called. If a shared-lock is already held when -** this function is called, it is a no-op. -** -** The following operations are also performed by this function. -** -** 1) If the pager is currently in PAGER_OPEN state (no lock held -** on the database file), then an attempt is made to obtain a -** SHARED lock on the database file. Immediately after obtaining -** the SHARED lock, the file-system is checked for a hot-journal, -** which is played back if present. Following any hot-journal -** rollback, the contents of the cache are validated by checking -** the 'change-counter' field of the database file header and -** discarded if they are found to be invalid. -** -** 2) If the pager is running in exclusive-mode, and there are currently -** no outstanding references to any pages, and is in the error state, -** then an attempt is made to clear the error state by discarding -** the contents of the page cache and rolling back any open journal -** file. -** -** If everything is successful, SQLITE_OK is returned. If an IO error -** occurs while locking the database, checking for a hot-journal file or -** rolling back a journal file, the IO error code is returned. -*/ -SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - - /* This routine is only called from b-tree and only when there are no - ** outstanding pages. This implies that the pager state should either - ** be OPEN or READER. READER is only possible if the pager is or was in - ** exclusive access mode. - */ - assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); - assert( assert_pager_state(pPager) ); - assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); - if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; } - - if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){ - int bHotJournal = 1; /* True if there exists a hot journal-file */ - - assert( !MEMDB ); - - rc = pager_wait_on_lock(pPager, SHARED_LOCK); - if( rc!=SQLITE_OK ){ - assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); - goto failed; - } - - /* If a journal file exists, and there is no RESERVED lock on the - ** database file, then it either needs to be played back or deleted. - */ - if( pPager->eLock<=SHARED_LOCK ){ - rc = hasHotJournal(pPager, &bHotJournal); - } - if( rc!=SQLITE_OK ){ - goto failed; - } - if( bHotJournal ){ - if( pPager->readOnly ){ - rc = SQLITE_READONLY_ROLLBACK; - goto failed; - } - - /* Get an EXCLUSIVE lock on the database file. At this point it is - ** important that a RESERVED lock is not obtained on the way to the - ** EXCLUSIVE lock. If it were, another process might open the - ** database file, detect the RESERVED lock, and conclude that the - ** database is safe to read while this process is still rolling the - ** hot-journal back. - ** - ** Because the intermediate RESERVED lock is not requested, any - ** other process attempting to access the database file will get to - ** this point in the code and fail to obtain its own EXCLUSIVE lock - ** on the database file. - ** - ** Unless the pager is in locking_mode=exclusive mode, the lock is - ** downgraded to SHARED_LOCK before this function returns. - */ - rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - goto failed; - } - - /* If it is not already open and the file exists on disk, open the - ** journal for read/write access. Write access is required because - ** in exclusive-access mode the file descriptor will be kept open - ** and possibly used for a transaction later on. Also, write-access - ** is usually required to finalize the journal in journal_mode=persist - ** mode (and also for journal_mode=truncate on some systems). - ** - ** If the journal does not exist, it usually means that some - ** other connection managed to get in and roll it back before - ** this connection obtained the exclusive lock above. Or, it - ** may mean that the pager was in the error-state when this - ** function was called and the journal file does not exist. - */ - if( !isOpen(pPager->jfd) ){ - sqlite3_vfs * const pVfs = pPager->pVfs; - int bExists; /* True if journal file exists */ - rc = sqlite3OsAccess( - pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists); - if( rc==SQLITE_OK && bExists ){ - int fout = 0; - int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL; - assert( !pPager->tempFile ); - rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout); - assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); - if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){ - rc = SQLITE_CANTOPEN_BKPT; - sqlite3OsClose(pPager->jfd); - } - } - } - - /* Playback and delete the journal. Drop the database write - ** lock and reacquire the read lock. Purge the cache before - ** playing back the hot-journal so that we don't end up with - ** an inconsistent cache. Sync the hot journal before playing - ** it back since the process that crashed and left the hot journal - ** probably did not sync it and we are required to always sync - ** the journal before playing it back. - */ - if( isOpen(pPager->jfd) ){ - assert( rc==SQLITE_OK ); - rc = pagerSyncHotJournal(pPager); - if( rc==SQLITE_OK ){ - rc = pager_playback(pPager, 1); - pPager->eState = PAGER_OPEN; - } - }else if( !pPager->exclusiveMode ){ - pagerUnlockDb(pPager, SHARED_LOCK); - } - - if( rc!=SQLITE_OK ){ - /* This branch is taken if an error occurs while trying to open - ** or roll back a hot-journal while holding an EXCLUSIVE lock. The - ** pager_unlock() routine will be called before returning to unlock - ** the file. If the unlock attempt fails, then Pager.eLock must be - ** set to UNKNOWN_LOCK (see the comment above the #define for - ** UNKNOWN_LOCK above for an explanation). - ** - ** In order to get pager_unlock() to do this, set Pager.eState to - ** PAGER_ERROR now. This is not actually counted as a transition - ** to ERROR state in the state diagram at the top of this file, - ** since we know that the same call to pager_unlock() will very - ** shortly transition the pager object to the OPEN state. Calling - ** assert_pager_state() would fail now, as it should not be possible - ** to be in ERROR state when there are zero outstanding page - ** references. - */ - pager_error(pPager, rc); - goto failed; - } - - assert( pPager->eState==PAGER_OPEN ); - assert( (pPager->eLock==SHARED_LOCK) - || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK) - ); - } - - if( !pPager->tempFile - && (pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0) - ){ - /* The shared-lock has just been acquired on the database file - ** and there are already pages in the cache (from a previous - ** read or write transaction). Check to see if the database - ** has been modified. If the database has changed, flush the - ** cache. - ** - ** Database changes is detected by looking at 15 bytes beginning - ** at offset 24 into the file. The first 4 of these 16 bytes are - ** a 32-bit counter that is incremented with each change. The - ** other bytes change randomly with each file change when - ** a codec is in use. - ** - ** There is a vanishingly small chance that a change will not be - ** detected. The chance of an undetected change is so small that - ** it can be neglected. - */ - Pgno nPage = 0; - char dbFileVers[sizeof(pPager->dbFileVers)]; - - rc = pagerPagecount(pPager, &nPage); - if( rc ) goto failed; - - if( nPage>0 ){ - IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); - rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); - if( rc!=SQLITE_OK ){ - goto failed; - } - }else{ - memset(dbFileVers, 0, sizeof(dbFileVers)); - } - - if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ - pager_reset(pPager); - } - } - - /* If there is a WAL file in the file-system, open this database in WAL - ** mode. Otherwise, the following function call is a no-op. - */ - rc = pagerOpenWalIfPresent(pPager); -#ifndef SQLITE_OMIT_WAL - assert( pPager->pWal==0 || rc==SQLITE_OK ); -#endif - } - - if( pagerUseWal(pPager) ){ - assert( rc==SQLITE_OK ); - rc = pagerBeginReadTransaction(pPager); - } - - if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ - rc = pagerPagecount(pPager, &pPager->dbSize); - } - - failed: - if( rc!=SQLITE_OK ){ - assert( !MEMDB ); - pager_unlock(pPager); - assert( pPager->eState==PAGER_OPEN ); - }else{ - pPager->eState = PAGER_READER; - } - return rc; -} - -/* -** If the reference count has reached zero, rollback any active -** transaction and unlock the pager. -** -** Except, in locking_mode=EXCLUSIVE when there is nothing to in -** the rollback journal, the unlock is not performed and there is -** nothing to rollback, so this routine is a no-op. -*/ -static void pagerUnlockIfUnused(Pager *pPager){ - if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){ - pagerUnlockAndRollback(pPager); - } -} - -/* -** Acquire a reference to page number pgno in pager pPager (a page -** reference has type DbPage*). If the requested reference is -** successfully obtained, it is copied to *ppPage and SQLITE_OK returned. -** -** If the requested page is already in the cache, it is returned. -** Otherwise, a new page object is allocated and populated with data -** read from the database file. In some cases, the pcache module may -** choose not to allocate a new page object and may reuse an existing -** object with no outstanding references. -** -** The extra data appended to a page is always initialized to zeros the -** first time a page is loaded into memory. If the page requested is -** already in the cache when this function is called, then the extra -** data is left as it was when the page object was last used. -** -** If the database image is smaller than the requested page or if a -** non-zero value is passed as the noContent parameter and the -** requested page is not already stored in the cache, then no -** actual disk read occurs. In this case the memory image of the -** page is initialized to all zeros. -** -** If noContent is true, it means that we do not care about the contents -** of the page. This occurs in two seperate scenarios: -** -** a) When reading a free-list leaf page from the database, and -** -** b) When a savepoint is being rolled back and we need to load -** a new page into the cache to be filled with the data read -** from the savepoint journal. -** -** If noContent is true, then the data returned is zeroed instead of -** being read from the database. Additionally, the bits corresponding -** to pgno in Pager.pInJournal (bitvec of pages already written to the -** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open -** savepoints are set. This means if the page is made writable at any -** point in the future, using a call to sqlite3PagerWrite(), its contents -** will not be journaled. This saves IO. -** -** The acquisition might fail for several reasons. In all cases, -** an appropriate error code is returned and *ppPage is set to NULL. -** -** See also sqlite3PagerLookup(). Both this routine and Lookup() attempt -** to find a page in the in-memory cache first. If the page is not already -** in memory, this routine goes to disk to read it in whereas Lookup() -** just returns 0. This routine acquires a read-lock the first time it -** has to go to disk, and could also playback an old journal if necessary. -** Since Lookup() never goes to disk, it never has to deal with locks -** or journal files. -*/ -SQLITE_PRIVATE int sqlite3PagerAcquire( - Pager *pPager, /* The pager open on the database file */ - Pgno pgno, /* Page number to fetch */ - DbPage **ppPage, /* Write a pointer to the page here */ - int noContent /* Do not bother reading content from disk if true */ -){ - int rc; - PgHdr *pPg; - - assert( pPager->eState>=PAGER_READER ); - assert( assert_pager_state(pPager) ); - - if( pgno==0 ){ - return SQLITE_CORRUPT_BKPT; - } - - /* If the pager is in the error state, return an error immediately. - ** Otherwise, request the page from the PCache layer. */ - if( pPager->errCode!=SQLITE_OK ){ - rc = pPager->errCode; - }else{ - rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage); - } - - if( rc!=SQLITE_OK ){ - /* Either the call to sqlite3PcacheFetch() returned an error or the - ** pager was already in the error-state when this function was called. - ** Set pPg to 0 and jump to the exception handler. */ - pPg = 0; - goto pager_acquire_err; - } - assert( (*ppPage)->pgno==pgno ); - assert( (*ppPage)->pPager==pPager || (*ppPage)->pPager==0 ); - - if( (*ppPage)->pPager && !noContent ){ - /* In this case the pcache already contains an initialized copy of - ** the page. Return without further ado. */ - assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); - pPager->aStat[PAGER_STAT_HIT]++; - return SQLITE_OK; - - }else{ - /* The pager cache has created a new page. Its content needs to - ** be initialized. */ - - pPg = *ppPage; - pPg->pPager = pPager; - - /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page - ** number greater than this, or the unused locking-page, is requested. */ - if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ - rc = SQLITE_CORRUPT_BKPT; - goto pager_acquire_err; - } - - if( MEMDB || pPager->dbSizefd) ){ - if( pgno>pPager->mxPgno ){ - rc = SQLITE_FULL; - goto pager_acquire_err; - } - if( noContent ){ - /* Failure to set the bits in the InJournal bit-vectors is benign. - ** It merely means that we might do some extra work to journal a - ** page that does not need to be journaled. Nevertheless, be sure - ** to test the case where a malloc error occurs while trying to set - ** a bit in a bit vector. - */ - sqlite3BeginBenignMalloc(); - if( pgno<=pPager->dbOrigSize ){ - TESTONLY( rc = ) sqlite3BitvecSet(pPager->pInJournal, pgno); - testcase( rc==SQLITE_NOMEM ); - } - TESTONLY( rc = ) addToSavepointBitvecs(pPager, pgno); - testcase( rc==SQLITE_NOMEM ); - sqlite3EndBenignMalloc(); - } - memset(pPg->pData, 0, pPager->pageSize); - IOTRACE(("ZERO %p %d\n", pPager, pgno)); - }else{ - assert( pPg->pPager==pPager ); - pPager->aStat[PAGER_STAT_MISS]++; - rc = readDbPage(pPg); - if( rc!=SQLITE_OK ){ - goto pager_acquire_err; - } - } - pager_set_pagehash(pPg); - } - - return SQLITE_OK; - -pager_acquire_err: - assert( rc!=SQLITE_OK ); - if( pPg ){ - sqlite3PcacheDrop(pPg); - } - pagerUnlockIfUnused(pPager); - - *ppPage = 0; - return rc; -} - -/* -** Acquire a page if it is already in the in-memory cache. Do -** not read the page from disk. Return a pointer to the page, -** or 0 if the page is not in cache. -** -** See also sqlite3PagerGet(). The difference between this routine -** and sqlite3PagerGet() is that _get() will go to the disk and read -** in the page if the page is not already in cache. This routine -** returns NULL if the page is not in cache or if a disk I/O error -** has ever happened. -*/ -SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ - PgHdr *pPg = 0; - assert( pPager!=0 ); - assert( pgno!=0 ); - assert( pPager->pPCache!=0 ); - assert( pPager->eState>=PAGER_READER && pPager->eState!=PAGER_ERROR ); - sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg); - return pPg; -} - -/* -** Release a page reference. -** -** If the number of references to the page drop to zero, then the -** page is added to the LRU list. When all references to all pages -** are released, a rollback occurs and the lock on the database is -** removed. -*/ -SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){ - if( pPg ){ - Pager *pPager = pPg->pPager; - sqlite3PcacheRelease(pPg); - pagerUnlockIfUnused(pPager); - } -} - -/* -** This function is called at the start of every write transaction. -** There must already be a RESERVED or EXCLUSIVE lock on the database -** file when this routine is called. -** -** Open the journal file for pager pPager and write a journal header -** to the start of it. If there are active savepoints, open the sub-journal -** as well. This function is only used when the journal file is being -** opened to write a rollback log for a transaction. It is not used -** when opening a hot journal file to roll it back. -** -** If the journal file is already open (as it may be in exclusive mode), -** then this function just writes a journal header to the start of the -** already open file. -** -** Whether or not the journal file is opened by this function, the -** Pager.pInJournal bitvec structure is allocated. -** -** Return SQLITE_OK if everything is successful. Otherwise, return -** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or -** an IO error code if opening or writing the journal file fails. -*/ -static int pager_open_journal(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */ - - assert( pPager->eState==PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); - assert( pPager->pInJournal==0 ); - - /* If already in the error state, this function is a no-op. But on - ** the other hand, this routine is never called if we are already in - ** an error state. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ - pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize); - if( pPager->pInJournal==0 ){ - return SQLITE_NOMEM; - } - - /* Open the journal file if it is not already open. */ - if( !isOpen(pPager->jfd) ){ - if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ - sqlite3MemJournalOpen(pPager->jfd); - }else{ - const int flags = /* VFS flags to open journal file */ - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| - (pPager->tempFile ? - (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL): - (SQLITE_OPEN_MAIN_JOURNAL) - ); - #ifdef SQLITE_ENABLE_ATOMIC_WRITE - rc = sqlite3JournalOpen( - pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager) - ); - #else - rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); - #endif - } - assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); - } - - - /* Write the first journal header to the journal file and open - ** the sub-journal if necessary. - */ - if( rc==SQLITE_OK ){ - /* TODO: Check if all of these are really required. */ - pPager->nRec = 0; - pPager->journalOff = 0; - pPager->setMaster = 0; - pPager->journalHdr = 0; - rc = writeJournalHdr(pPager); - } - } - - if( rc!=SQLITE_OK ){ - sqlite3BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - }else{ - assert( pPager->eState==PAGER_WRITER_LOCKED ); - pPager->eState = PAGER_WRITER_CACHEMOD; - } - - return rc; -} - -/* -** Begin a write-transaction on the specified pager object. If a -** write-transaction has already been opened, this function is a no-op. -** -** If the exFlag argument is false, then acquire at least a RESERVED -** lock on the database file. If exFlag is true, then acquire at least -** an EXCLUSIVE lock. If such a lock is already held, no locking -** functions need be called. -** -** If the subjInMemory argument is non-zero, then any sub-journal opened -** within this transaction will be opened as an in-memory file. This -** has no effect if the sub-journal is already opened (as it may be when -** running in exclusive mode) or if the transaction does not require a -** sub-journal. If the subjInMemory argument is zero, then any required -** sub-journal is implemented in-memory if pPager is an in-memory database, -** or using a temporary file otherwise. -*/ -SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ - int rc = SQLITE_OK; - - if( pPager->errCode ) return pPager->errCode; - assert( pPager->eState>=PAGER_READER && pPager->eStatesubjInMemory = (u8)subjInMemory; - - if( ALWAYS(pPager->eState==PAGER_READER) ){ - assert( pPager->pInJournal==0 ); - - if( pagerUseWal(pPager) ){ - /* If the pager is configured to use locking_mode=exclusive, and an - ** exclusive lock on the database is not already held, obtain it now. - */ - if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ - rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - return rc; - } - sqlite3WalExclusiveMode(pPager->pWal, 1); - } - - /* Grab the write lock on the log file. If successful, upgrade to - ** PAGER_RESERVED state. Otherwise, return an error code to the caller. - ** The busy-handler is not invoked if another connection already - ** holds the write-lock. If possible, the upper layer will call it. - */ - rc = sqlite3WalBeginWriteTransaction(pPager->pWal); - }else{ - /* Obtain a RESERVED lock on the database file. If the exFlag parameter - ** is true, then immediately upgrade this to an EXCLUSIVE lock. The - ** busy-handler callback can be used when upgrading to the EXCLUSIVE - ** lock, but not when obtaining the RESERVED lock. - */ - rc = pagerLockDb(pPager, RESERVED_LOCK); - if( rc==SQLITE_OK && exFlag ){ - rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); - } - } - - if( rc==SQLITE_OK ){ - /* Change to WRITER_LOCKED state. - ** - ** WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD - ** when it has an open transaction, but never to DBMOD or FINISHED. - ** This is because in those states the code to roll back savepoint - ** transactions may copy data from the sub-journal into the database - ** file as well as into the page cache. Which would be incorrect in - ** WAL mode. - */ - pPager->eState = PAGER_WRITER_LOCKED; - pPager->dbHintSize = pPager->dbSize; - pPager->dbFileSize = pPager->dbSize; - pPager->dbOrigSize = pPager->dbSize; - pPager->journalOff = 0; - } - - assert( rc==SQLITE_OK || pPager->eState==PAGER_READER ); - assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); - } - - PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); - return rc; -} - -/* -** Mark a single data page as writeable. The page is written into the -** main journal or sub-journal as required. If the page is written into -** one of the journals, the corresponding bit is set in the -** Pager.pInJournal bitvec and the PagerSavepoint.pInSavepoint bitvecs -** of any open savepoints as appropriate. -*/ -static int pager_write(PgHdr *pPg){ - void *pData = pPg->pData; - Pager *pPager = pPg->pPager; - int rc = SQLITE_OK; - - /* This routine is not called unless a write-transaction has already - ** been started. The journal file may or may not be open at this point. - ** It is never called in the ERROR state. - */ - assert( pPager->eState==PAGER_WRITER_LOCKED - || pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - - /* If an error has been previously detected, report the same error - ** again. This should not happen, but the check provides robustness. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - /* Higher-level routines never call this function if database is not - ** writable. But check anyway, just for robustness. */ - if( NEVER(pPager->readOnly) ) return SQLITE_PERM; - - CHECK_PAGE(pPg); - - /* The journal file needs to be opened. Higher level routines have already - ** obtained the necessary locks to begin the write-transaction, but the - ** rollback journal might not yet be open. Open it now if this is the case. - ** - ** This is done before calling sqlite3PcacheMakeDirty() on the page. - ** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then - ** an error might occur and the pager would end up in WRITER_LOCKED state - ** with pages marked as dirty in the cache. - */ - if( pPager->eState==PAGER_WRITER_LOCKED ){ - rc = pager_open_journal(pPager); - if( rc!=SQLITE_OK ) return rc; - } - assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); - assert( assert_pager_state(pPager) ); - - /* Mark the page as dirty. If the page has already been written - ** to the journal then we can return right away. - */ - sqlite3PcacheMakeDirty(pPg); - if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ - assert( !pagerUseWal(pPager) ); - }else{ - - /* The transaction journal now exists and we have a RESERVED or an - ** EXCLUSIVE lock on the main database file. Write the current page to - ** the transaction journal if it is not there already. - */ - if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){ - assert( pagerUseWal(pPager)==0 ); - if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){ - u32 cksum; - char *pData2; - i64 iOff = pPager->journalOff; - - /* We should never write to the journal file the page that - ** contains the database locks. The following assert verifies - ** that we do not. */ - assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); - - assert( pPager->journalHdr<=pPager->journalOff ); - CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); - cksum = pager_cksum(pPager, (u8*)pData2); - - /* Even if an IO or diskfull error occurs while journalling the - ** page in the block above, set the need-sync flag for the page. - ** Otherwise, when the transaction is rolled back, the logic in - ** playback_one_page() will think that the page needs to be restored - ** in the database file. And if an IO error occurs while doing so, - ** then corruption may follow. - */ - pPg->flags |= PGHDR_NEED_SYNC; - - rc = write32bits(pPager->jfd, iOff, pPg->pgno); - if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4); - if( rc!=SQLITE_OK ) return rc; - rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum); - if( rc!=SQLITE_OK ) return rc; - - IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, - pPager->journalOff, pPager->pageSize)); - PAGER_INCR(sqlite3_pager_writej_count); - PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n", - PAGERID(pPager), pPg->pgno, - ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg))); - - pPager->journalOff += 8 + pPager->pageSize; - pPager->nRec++; - assert( pPager->pInJournal!=0 ); - rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno); - testcase( rc==SQLITE_NOMEM ); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); - rc |= addToSavepointBitvecs(pPager, pPg->pgno); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_NOMEM ); - return rc; - } - }else{ - if( pPager->eState!=PAGER_WRITER_DBMOD ){ - pPg->flags |= PGHDR_NEED_SYNC; - } - PAGERTRACE(("APPEND %d page %d needSync=%d\n", - PAGERID(pPager), pPg->pgno, - ((pPg->flags&PGHDR_NEED_SYNC)?1:0))); - } - } - - /* If the statement journal is open and the page is not in it, - ** then write the current page to the statement journal. Note that - ** the statement journal format differs from the standard journal format - ** in that it omits the checksums and the header. - */ - if( subjRequiresPage(pPg) ){ - rc = subjournalPage(pPg); - } - } - - /* Update the database size and return. - */ - if( pPager->dbSizepgno ){ - pPager->dbSize = pPg->pgno; - } - return rc; -} - -/* -** Mark a data page as writeable. This routine must be called before -** making changes to a page. The caller must check the return value -** of this function and be careful not to change any page data unless -** this routine returns SQLITE_OK. -** -** The difference between this function and pager_write() is that this -** function also deals with the special case where 2 or more pages -** fit on a single disk sector. In this case all co-resident pages -** must have been written to the journal file before returning. -** -** If an error occurs, SQLITE_NOMEM or an IO error code is returned -** as appropriate. Otherwise, SQLITE_OK. -*/ -SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){ - int rc = SQLITE_OK; - - PgHdr *pPg = pDbPage; - Pager *pPager = pPg->pPager; - Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); - - assert( pPager->eState>=PAGER_WRITER_LOCKED ); - assert( pPager->eState!=PAGER_ERROR ); - assert( assert_pager_state(pPager) ); - - if( nPagePerSector>1 ){ - Pgno nPageCount; /* Total number of pages in database file */ - Pgno pg1; /* First page of the sector pPg is located on. */ - int nPage = 0; /* Number of pages starting at pg1 to journal */ - int ii; /* Loop counter */ - int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ - - /* Set the doNotSyncSpill flag to 1. This is because we cannot allow - ** a journal header to be written between the pages journaled by - ** this function. - */ - assert( !MEMDB ); - assert( pPager->doNotSyncSpill==0 ); - pPager->doNotSyncSpill++; - - /* This trick assumes that both the page-size and sector-size are - ** an integer power of 2. It sets variable pg1 to the identifier - ** of the first page of the sector pPg is located on. - */ - pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1; - - nPageCount = pPager->dbSize; - if( pPg->pgno>nPageCount ){ - nPage = (pPg->pgno - pg1)+1; - }else if( (pg1+nPagePerSector-1)>nPageCount ){ - nPage = nPageCount+1-pg1; - }else{ - nPage = nPagePerSector; - } - assert(nPage>0); - assert(pg1<=pPg->pgno); - assert((pg1+nPage)>pPg->pgno); - - for(ii=0; iipgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){ - if( pg!=PAGER_MJ_PGNO(pPager) ){ - rc = sqlite3PagerGet(pPager, pg, &pPage); - if( rc==SQLITE_OK ){ - rc = pager_write(pPage); - if( pPage->flags&PGHDR_NEED_SYNC ){ - needSync = 1; - } - sqlite3PagerUnref(pPage); - } - } - }else if( (pPage = pager_lookup(pPager, pg))!=0 ){ - if( pPage->flags&PGHDR_NEED_SYNC ){ - needSync = 1; - } - sqlite3PagerUnref(pPage); - } - } - - /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages - ** starting at pg1, then it needs to be set for all of them. Because - ** writing to any of these nPage pages may damage the others, the - ** journal file must contain sync()ed copies of all of them - ** before any of them can be written out to the database file. - */ - if( rc==SQLITE_OK && needSync ){ - assert( !MEMDB ); - for(ii=0; iiflags |= PGHDR_NEED_SYNC; - sqlite3PagerUnref(pPage); - } - } - } - - assert( pPager->doNotSyncSpill==1 ); - pPager->doNotSyncSpill--; - }else{ - rc = pager_write(pDbPage); - } - return rc; -} - -/* -** Return TRUE if the page given in the argument was previously passed -** to sqlite3PagerWrite(). In other words, return TRUE if it is ok -** to change the content of the page. -*/ -#ifndef NDEBUG -SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage *pPg){ - return pPg->flags&PGHDR_DIRTY; -} -#endif - -/* -** A call to this routine tells the pager that it is not necessary to -** write the information on page pPg back to the disk, even though -** that page might be marked as dirty. This happens, for example, when -** the page has been added as a leaf of the freelist and so its -** content no longer matters. -** -** The overlying software layer calls this routine when all of the data -** on the given page is unused. The pager marks the page as clean so -** that it does not get written to disk. -** -** Tests show that this optimization can quadruple the speed of large -** DELETE operations. -*/ -SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ - PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); - IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) - pPg->flags |= PGHDR_DONT_WRITE; - pager_set_pagehash(pPg); - } -} - -/* -** This routine is called to increment the value of the database file -** change-counter, stored as a 4-byte big-endian integer starting at -** byte offset 24 of the pager file. The secondary change counter at -** 92 is also updated, as is the SQLite version number at offset 96. -** -** But this only happens if the pPager->changeCountDone flag is false. -** To avoid excess churning of page 1, the update only happens once. -** See also the pager_write_changecounter() routine that does an -** unconditional update of the change counters. -** -** If the isDirectMode flag is zero, then this is done by calling -** sqlite3PagerWrite() on page 1, then modifying the contents of the -** page data. In this case the file will be updated when the current -** transaction is committed. -** -** The isDirectMode flag may only be non-zero if the library was compiled -** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case, -** if isDirect is non-zero, then the database file is updated directly -** by writing an updated version of page 1 using a call to the -** sqlite3OsWrite() function. -*/ -static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ - int rc = SQLITE_OK; - - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - - /* Declare and initialize constant integer 'isDirect'. If the - ** atomic-write optimization is enabled in this build, then isDirect - ** is initialized to the value passed as the isDirectMode parameter - ** to this function. Otherwise, it is always set to zero. - ** - ** The idea is that if the atomic-write optimization is not - ** enabled at compile time, the compiler can omit the tests of - ** 'isDirect' below, as well as the block enclosed in the - ** "if( isDirect )" condition. - */ -#ifndef SQLITE_ENABLE_ATOMIC_WRITE -# define DIRECT_MODE 0 - assert( isDirectMode==0 ); - UNUSED_PARAMETER(isDirectMode); -#else -# define DIRECT_MODE isDirectMode -#endif - - if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){ - PgHdr *pPgHdr; /* Reference to page 1 */ - - assert( !pPager->tempFile && isOpen(pPager->fd) ); - - /* Open page 1 of the file for writing. */ - rc = sqlite3PagerGet(pPager, 1, &pPgHdr); - assert( pPgHdr==0 || rc==SQLITE_OK ); - - /* If page one was fetched successfully, and this function is not - ** operating in direct-mode, make page 1 writable. When not in - ** direct mode, page 1 is always held in cache and hence the PagerGet() - ** above is always successful - hence the ALWAYS on rc==SQLITE_OK. - */ - if( !DIRECT_MODE && ALWAYS(rc==SQLITE_OK) ){ - rc = sqlite3PagerWrite(pPgHdr); - } - - if( rc==SQLITE_OK ){ - /* Actually do the update of the change counter */ - pager_write_changecounter(pPgHdr); - - /* If running in direct mode, write the contents of page 1 to the file. */ - if( DIRECT_MODE ){ - const void *zBuf; - assert( pPager->dbFileSize>0 ); - CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf); - if( rc==SQLITE_OK ){ - rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); - pPager->aStat[PAGER_STAT_WRITE]++; - } - if( rc==SQLITE_OK ){ - pPager->changeCountDone = 1; - } - }else{ - pPager->changeCountDone = 1; - } - } - - /* Release the page reference. */ - sqlite3PagerUnref(pPgHdr); - } - return rc; -} - -/* -** Sync the database file to disk. This is a no-op for in-memory databases -** or pages with the Pager.noSync flag set. -** -** If successful, or if called on a pager for which it is a no-op, this -** function returns SQLITE_OK. Otherwise, an IO error code is returned. -*/ -SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager){ - int rc = SQLITE_OK; - if( !pPager->noSync ){ - assert( !MEMDB ); - rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); - }else if( isOpen(pPager->fd) ){ - assert( !MEMDB ); - rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, 0); - if( rc==SQLITE_NOTFOUND ){ - rc = SQLITE_OK; - } - } - return rc; -} - -/* -** This function may only be called while a write-transaction is active in -** rollback. If the connection is in WAL mode, this call is a no-op. -** Otherwise, if the connection does not already have an EXCLUSIVE lock on -** the database file, an attempt is made to obtain one. -** -** If the EXCLUSIVE lock is already held or the attempt to obtain it is -** successful, or the connection is in WAL mode, SQLITE_OK is returned. -** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is -** returned. -*/ -SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){ - int rc = SQLITE_OK; - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - || pPager->eState==PAGER_WRITER_LOCKED - ); - assert( assert_pager_state(pPager) ); - if( 0==pagerUseWal(pPager) ){ - rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); - } - return rc; -} - -/* -** Sync the database file for the pager pPager. zMaster points to the name -** of a master journal file that should be written into the individual -** journal file. zMaster may be NULL, which is interpreted as no master -** journal (a single database transaction). -** -** This routine ensures that: -** -** * The database file change-counter is updated, -** * the journal is synced (unless the atomic-write optimization is used), -** * all dirty pages are written to the database file, -** * the database file is truncated (if required), and -** * the database file synced. -** -** The only thing that remains to commit the transaction is to finalize -** (delete, truncate or zero the first part of) the journal file (or -** delete the master journal file if specified). -** -** Note that if zMaster==NULL, this does not overwrite a previous value -** passed to an sqlite3PagerCommitPhaseOne() call. -** -** If the final parameter - noSync - is true, then the database file itself -** is not synced. The caller must call sqlite3PagerSync() directly to -** sync the database file before calling CommitPhaseTwo() to delete the -** journal file in this case. -*/ -SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( - Pager *pPager, /* Pager object */ - const char *zMaster, /* If not NULL, the master journal name */ - int noSync /* True to omit the xSync on the db file */ -){ - int rc = SQLITE_OK; /* Return code */ - - assert( pPager->eState==PAGER_WRITER_LOCKED - || pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - || pPager->eState==PAGER_ERROR - ); - assert( assert_pager_state(pPager) ); - - /* If a prior error occurred, report that error again. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", - pPager->zFilename, zMaster, pPager->dbSize)); - - /* If no database changes have been made, return early. */ - if( pPager->eStatepBackup); - }else{ - if( pagerUseWal(pPager) ){ - PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); - PgHdr *pPageOne = 0; - if( pList==0 ){ - /* Must have at least one page for the WAL commit flag. - ** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */ - rc = sqlite3PagerGet(pPager, 1, &pPageOne); - pList = pPageOne; - pList->pDirty = 0; - } - assert( rc==SQLITE_OK ); - if( ALWAYS(pList) ){ - rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1); - } - sqlite3PagerUnref(pPageOne); - if( rc==SQLITE_OK ){ - sqlite3PcacheCleanAll(pPager->pPCache); - } - }else{ - /* The following block updates the change-counter. Exactly how it - ** does this depends on whether or not the atomic-update optimization - ** was enabled at compile time, and if this transaction meets the - ** runtime criteria to use the operation: - ** - ** * The file-system supports the atomic-write property for - ** blocks of size page-size, and - ** * This commit is not part of a multi-file transaction, and - ** * Exactly one page has been modified and store in the journal file. - ** - ** If the optimization was not enabled at compile time, then the - ** pager_incr_changecounter() function is called to update the change - ** counter in 'indirect-mode'. If the optimization is compiled in but - ** is not applicable to this transaction, call sqlite3JournalCreate() - ** to make sure the journal file has actually been created, then call - ** pager_incr_changecounter() to update the change-counter in indirect - ** mode. - ** - ** Otherwise, if the optimization is both enabled and applicable, - ** then call pager_incr_changecounter() to update the change-counter - ** in 'direct' mode. In this case the journal file will never be - ** created for this transaction. - */ - #ifdef SQLITE_ENABLE_ATOMIC_WRITE - PgHdr *pPg; - assert( isOpen(pPager->jfd) - || pPager->journalMode==PAGER_JOURNALMODE_OFF - || pPager->journalMode==PAGER_JOURNALMODE_WAL - ); - if( !zMaster && isOpen(pPager->jfd) - && pPager->journalOff==jrnlBufferSize(pPager) - && pPager->dbSize>=pPager->dbOrigSize - && (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty) - ){ - /* Update the db file change counter via the direct-write method. The - ** following call will modify the in-memory representation of page 1 - ** to include the updated change counter and then write page 1 - ** directly to the database file. Because of the atomic-write - ** property of the host file-system, this is safe. - */ - rc = pager_incr_changecounter(pPager, 1); - }else{ - rc = sqlite3JournalCreate(pPager->jfd); - if( rc==SQLITE_OK ){ - rc = pager_incr_changecounter(pPager, 0); - } - } - #else - rc = pager_incr_changecounter(pPager, 0); - #endif - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - - /* Write the master journal name into the journal file. If a master - ** journal file name has already been written to the journal file, - ** or if zMaster is NULL (no master journal), then this call is a no-op. - */ - rc = writeMasterJournal(pPager, zMaster); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - - /* Sync the journal file and write all dirty pages to the database. - ** If the atomic-update optimization is being used, this sync will not - ** create the journal file or perform any real IO. - ** - ** Because the change-counter page was just modified, unless the - ** atomic-update optimization is used it is almost certain that the - ** journal requires a sync here. However, in locking_mode=exclusive - ** on a system under memory pressure it is just possible that this is - ** not the case. In this case it is likely enough that the redundant - ** xSync() call will be changed to a no-op by the OS anyhow. - */ - rc = syncJournal(pPager, 0); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - - rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache)); - if( rc!=SQLITE_OK ){ - assert( rc!=SQLITE_IOERR_BLOCKED ); - goto commit_phase_one_exit; - } - sqlite3PcacheCleanAll(pPager->pPCache); - - /* If the file on disk is smaller than the database image, use - ** pager_truncate to grow the file here. This can happen if the database - ** image was extended as part of the current transaction and then the - ** last page in the db image moved to the free-list. In this case the - ** last page is never written out to disk, leaving the database file - ** undersized. Fix this now if it is the case. */ - if( pPager->dbSize>pPager->dbFileSize ){ - Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); - assert( pPager->eState==PAGER_WRITER_DBMOD ); - rc = pager_truncate(pPager, nNew); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - } - - /* Finally, sync the database file. */ - if( !noSync ){ - rc = sqlite3PagerSync(pPager); - } - IOTRACE(("DBSYNC %p\n", pPager)) - } - } - -commit_phase_one_exit: - if( rc==SQLITE_OK && !pagerUseWal(pPager) ){ - pPager->eState = PAGER_WRITER_FINISHED; - } - return rc; -} - - -/* -** When this function is called, the database file has been completely -** updated to reflect the changes made by the current transaction and -** synced to disk. The journal file still exists in the file-system -** though, and if a failure occurs at this point it will eventually -** be used as a hot-journal and the current transaction rolled back. -** -** This function finalizes the journal file, either by deleting, -** truncating or partially zeroing it, so that it cannot be used -** for hot-journal rollback. Once this is done the transaction is -** irrevocably committed. -** -** If an error occurs, an IO error code is returned and the pager -** moves into the error state. Otherwise, SQLITE_OK is returned. -*/ -SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - - /* This routine should not be called if a prior error has occurred. - ** But if (due to a coding error elsewhere in the system) it does get - ** called, just return the same error code without doing anything. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - assert( pPager->eState==PAGER_WRITER_LOCKED - || pPager->eState==PAGER_WRITER_FINISHED - || (pagerUseWal(pPager) && pPager->eState==PAGER_WRITER_CACHEMOD) - ); - assert( assert_pager_state(pPager) ); - - /* An optimization. If the database was not actually modified during - ** this transaction, the pager is running in exclusive-mode and is - ** using persistent journals, then this function is a no-op. - ** - ** The start of the journal file currently contains a single journal - ** header with the nRec field set to 0. If such a journal is used as - ** a hot-journal during hot-journal rollback, 0 changes will be made - ** to the database file. So there is no need to zero the journal - ** header. Since the pager is in exclusive mode, there is no need - ** to drop any locks either. - */ - if( pPager->eState==PAGER_WRITER_LOCKED - && pPager->exclusiveMode - && pPager->journalMode==PAGER_JOURNALMODE_PERSIST - ){ - assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff ); - pPager->eState = PAGER_READER; - return SQLITE_OK; - } - - PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); - rc = pager_end_transaction(pPager, pPager->setMaster, 1); - return pager_error(pPager, rc); -} - -/* -** If a write transaction is open, then all changes made within the -** transaction are reverted and the current write-transaction is closed. -** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR -** state if an error occurs. -** -** If the pager is already in PAGER_ERROR state when this function is called, -** it returns Pager.errCode immediately. No work is performed in this case. -** -** Otherwise, in rollback mode, this function performs two functions: -** -** 1) It rolls back the journal file, restoring all database file and -** in-memory cache pages to the state they were in when the transaction -** was opened, and -** -** 2) It finalizes the journal file, so that it is not used for hot -** rollback at any point in the future. -** -** Finalization of the journal file (task 2) is only performed if the -** rollback is successful. -** -** In WAL mode, all cache-entries containing data modified within the -** current transaction are either expelled from the cache or reverted to -** their pre-transaction state by re-reading data from the database or -** WAL files. The WAL transaction is then closed. -*/ -SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager))); - - /* PagerRollback() is a no-op if called in READER or OPEN state. If - ** the pager is already in the ERROR state, the rollback is not - ** attempted here. Instead, the error code is returned to the caller. - */ - assert( assert_pager_state(pPager) ); - if( pPager->eState==PAGER_ERROR ) return pPager->errCode; - if( pPager->eState<=PAGER_READER ) return SQLITE_OK; - - if( pagerUseWal(pPager) ){ - int rc2; - rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); - rc2 = pager_end_transaction(pPager, pPager->setMaster, 0); - if( rc==SQLITE_OK ) rc = rc2; - }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){ - int eState = pPager->eState; - rc = pager_end_transaction(pPager, 0, 0); - if( !MEMDB && eState>PAGER_WRITER_LOCKED ){ - /* This can happen using journal_mode=off. Move the pager to the error - ** state to indicate that the contents of the cache may not be trusted. - ** Any active readers will get SQLITE_ABORT. - */ - pPager->errCode = SQLITE_ABORT; - pPager->eState = PAGER_ERROR; - return rc; - } - }else{ - rc = pager_playback(pPager, 0); - } - - assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); - assert( rc==SQLITE_OK || rc==SQLITE_FULL - || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR ); - - /* If an error occurs during a ROLLBACK, we can no longer trust the pager - ** cache. So call pager_error() on the way out to make any error persistent. - */ - return pager_error(pPager, rc); -} - -/* -** Return TRUE if the database file is opened read-only. Return FALSE -** if the database is (in theory) writable. -*/ -SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager *pPager){ - return pPager->readOnly; -} - -/* -** Return the number of references to the pager. -*/ -SQLITE_PRIVATE int sqlite3PagerRefcount(Pager *pPager){ - return sqlite3PcacheRefCount(pPager->pPCache); -} - -/* -** Return the approximate number of bytes of memory currently -** used by the pager and its associated cache. -*/ -SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager *pPager){ - int perPageSize = pPager->pageSize + pPager->nExtra + sizeof(PgHdr) - + 5*sizeof(void*); - return perPageSize*sqlite3PcachePagecount(pPager->pPCache) - + sqlite3MallocSize(pPager) - + pPager->pageSize; -} - -/* -** Return the number of references to the specified page. -*/ -SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage *pPage){ - return sqlite3PcachePageRefcount(pPage); -} - -#ifdef SQLITE_TEST -/* -** This routine is used for testing and analysis only. -*/ -SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){ - static int a[11]; - a[0] = sqlite3PcacheRefCount(pPager->pPCache); - a[1] = sqlite3PcachePagecount(pPager->pPCache); - a[2] = sqlite3PcacheGetCachesize(pPager->pPCache); - a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; - a[4] = pPager->eState; - a[5] = pPager->errCode; - a[6] = pPager->aStat[PAGER_STAT_HIT]; - a[7] = pPager->aStat[PAGER_STAT_MISS]; - a[8] = 0; /* Used to be pPager->nOvfl */ - a[9] = pPager->nRead; - a[10] = pPager->aStat[PAGER_STAT_WRITE]; - return a; -} -#endif - -/* -** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or -** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the -** current cache hit or miss count, according to the value of eStat. If the -** reset parameter is non-zero, the cache hit or miss count is zeroed before -** returning. -*/ -SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ - - assert( eStat==SQLITE_DBSTATUS_CACHE_HIT - || eStat==SQLITE_DBSTATUS_CACHE_MISS - || eStat==SQLITE_DBSTATUS_CACHE_WRITE - ); - - assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS ); - assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE ); - assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 ); - - *pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT]; - if( reset ){ - pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0; - } -} - -/* -** Return true if this is an in-memory pager. -*/ -SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){ - return MEMDB; -} - -/* -** Check that there are at least nSavepoint savepoints open. If there are -** currently less than nSavepoints open, then open one or more savepoints -** to make up the difference. If the number of savepoints is already -** equal to nSavepoint, then this function is a no-op. -** -** If a memory allocation fails, SQLITE_NOMEM is returned. If an error -** occurs while opening the sub-journal file, then an IO error code is -** returned. Otherwise, SQLITE_OK. -*/ -SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ - int rc = SQLITE_OK; /* Return code */ - int nCurrent = pPager->nSavepoint; /* Current number of savepoints */ - - assert( pPager->eState>=PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); - - if( nSavepoint>nCurrent && pPager->useJournal ){ - int ii; /* Iterator variable */ - PagerSavepoint *aNew; /* New Pager.aSavepoint array */ - - /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM - ** if the allocation fails. Otherwise, zero the new portion in case a - ** malloc failure occurs while populating it in the for(...) loop below. - */ - aNew = (PagerSavepoint *)sqlite3Realloc( - pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint - ); - if( !aNew ){ - return SQLITE_NOMEM; - } - memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint)); - pPager->aSavepoint = aNew; - - /* Populate the PagerSavepoint structures just allocated. */ - for(ii=nCurrent; iidbSize; - if( isOpen(pPager->jfd) && pPager->journalOff>0 ){ - aNew[ii].iOffset = pPager->journalOff; - }else{ - aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); - } - aNew[ii].iSubRec = pPager->nSubRec; - aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize); - if( !aNew[ii].pInSavepoint ){ - return SQLITE_NOMEM; - } - if( pagerUseWal(pPager) ){ - sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData); - } - pPager->nSavepoint = ii+1; - } - assert( pPager->nSavepoint==nSavepoint ); - assertTruncateConstraint(pPager); - } - - return rc; -} - -/* -** This function is called to rollback or release (commit) a savepoint. -** The savepoint to release or rollback need not be the most recently -** created savepoint. -** -** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE. -** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with -** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes -** that have occurred since the specified savepoint was created. -** -** The savepoint to rollback or release is identified by parameter -** iSavepoint. A value of 0 means to operate on the outermost savepoint -** (the first created). A value of (Pager.nSavepoint-1) means operate -** on the most recently created savepoint. If iSavepoint is greater than -** (Pager.nSavepoint-1), then this function is a no-op. -** -** If a negative value is passed to this function, then the current -** transaction is rolled back. This is different to calling -** sqlite3PagerRollback() because this function does not terminate -** the transaction or unlock the database, it just restores the -** contents of the database to its original state. -** -** In any case, all savepoints with an index greater than iSavepoint -** are destroyed. If this is a release operation (op==SAVEPOINT_RELEASE), -** then savepoint iSavepoint is also destroyed. -** -** This function may return SQLITE_NOMEM if a memory allocation fails, -** or an IO error code if an IO error occurs while rolling back a -** savepoint. If no errors occur, SQLITE_OK is returned. -*/ -SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ - int rc = pPager->errCode; /* Return code */ - - assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); - assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK ); - - if( rc==SQLITE_OK && iSavepointnSavepoint ){ - int ii; /* Iterator variable */ - int nNew; /* Number of remaining savepoints after this op. */ - - /* Figure out how many savepoints will still be active after this - ** operation. Store this value in nNew. Then free resources associated - ** with any savepoints that are destroyed by this operation. - */ - nNew = iSavepoint + (( op==SAVEPOINT_RELEASE ) ? 0 : 1); - for(ii=nNew; iinSavepoint; ii++){ - sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); - } - pPager->nSavepoint = nNew; - - /* If this is a release of the outermost savepoint, truncate - ** the sub-journal to zero bytes in size. */ - if( op==SAVEPOINT_RELEASE ){ - if( nNew==0 && isOpen(pPager->sjfd) ){ - /* Only truncate if it is an in-memory sub-journal. */ - if( sqlite3IsMemJournal(pPager->sjfd) ){ - rc = sqlite3OsTruncate(pPager->sjfd, 0); - assert( rc==SQLITE_OK ); - } - pPager->nSubRec = 0; - } - } - /* Else this is a rollback operation, playback the specified savepoint. - ** If this is a temp-file, it is possible that the journal file has - ** not yet been opened. In this case there have been no changes to - ** the database file, so the playback operation can be skipped. - */ - else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){ - PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1]; - rc = pagerPlaybackSavepoint(pPager, pSavepoint); - assert(rc!=SQLITE_DONE); - } - } - - return rc; -} - -/* -** Return the full pathname of the database file. -** -** Except, if the pager is in-memory only, then return an empty string if -** nullIfMemDb is true. This routine is called with nullIfMemDb==1 when -** used to report the filename to the user, for compatibility with legacy -** behavior. But when the Btree needs to know the filename for matching to -** shared cache, it uses nullIfMemDb==0 so that in-memory databases can -** participate in shared-cache. -*/ -SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){ - return (nullIfMemDb && pPager->memDb) ? "" : pPager->zFilename; -} - -/* -** Return the VFS structure for the pager. -*/ -SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){ - return pPager->pVfs; -} - -/* -** Return the file handle for the database file associated -** with the pager. This might return NULL if the file has -** not yet been opened. -*/ -SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ - return pPager->fd; -} - -/* -** Return the full pathname of the journal file. -*/ -SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){ - return pPager->zJournal; -} - -/* -** Return true if fsync() calls are disabled for this pager. Return FALSE -** if fsync()s are executed normally. -*/ -SQLITE_PRIVATE int sqlite3PagerNosync(Pager *pPager){ - return pPager->noSync; -} - -#ifdef SQLITE_HAS_CODEC -/* -** Set or retrieve the codec for this pager -*/ -SQLITE_PRIVATE void sqlite3PagerSetCodec( - Pager *pPager, - void *(*xCodec)(void*,void*,Pgno,int), - void (*xCodecSizeChng)(void*,int,int), - void (*xCodecFree)(void*), - void *pCodec -){ - if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); - pPager->xCodec = pPager->memDb ? 0 : xCodec; - pPager->xCodecSizeChng = xCodecSizeChng; - pPager->xCodecFree = xCodecFree; - pPager->pCodec = pCodec; - pagerReportSize(pPager); -} -SQLITE_PRIVATE void *sqlite3PagerGetCodec(Pager *pPager){ - return pPager->pCodec; -} -#endif - -#ifndef SQLITE_OMIT_AUTOVACUUM -/* -** Move the page pPg to location pgno in the file. -** -** There must be no references to the page previously located at -** pgno (which we call pPgOld) though that page is allowed to be -** in cache. If the page previously located at pgno is not already -** in the rollback journal, it is not put there by by this routine. -** -** References to the page pPg remain valid. Updating any -** meta-data associated with pPg (i.e. data stored in the nExtra bytes -** allocated along with the page) is the responsibility of the caller. -** -** A transaction must be active when this routine is called. It used to be -** required that a statement transaction was not active, but this restriction -** has been removed (CREATE INDEX needs to move a page when a statement -** transaction is active). -** -** If the fourth argument, isCommit, is non-zero, then this page is being -** moved as part of a database reorganization just before the transaction -** is being committed. In this case, it is guaranteed that the database page -** pPg refers to will not be written to again within this transaction. -** -** This function may return SQLITE_NOMEM or an IO error code if an error -** occurs. Otherwise, it returns SQLITE_OK. -*/ -SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ - PgHdr *pPgOld; /* The page being overwritten. */ - Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */ - int rc; /* Return code */ - Pgno origPgno; /* The original page number */ - - assert( pPg->nRef>0 ); - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - - /* In order to be able to rollback, an in-memory database must journal - ** the page we are moving from. - */ - if( MEMDB ){ - rc = sqlite3PagerWrite(pPg); - if( rc ) return rc; - } - - /* If the page being moved is dirty and has not been saved by the latest - ** savepoint, then save the current contents of the page into the - ** sub-journal now. This is required to handle the following scenario: - ** - ** BEGIN; - ** - ** SAVEPOINT one; - ** - ** ROLLBACK TO one; - ** - ** If page X were not written to the sub-journal here, it would not - ** be possible to restore its contents when the "ROLLBACK TO one" - ** statement were is processed. - ** - ** subjournalPage() may need to allocate space to store pPg->pgno into - ** one or more savepoint bitvecs. This is the reason this function - ** may return SQLITE_NOMEM. - */ - if( pPg->flags&PGHDR_DIRTY - && subjRequiresPage(pPg) - && SQLITE_OK!=(rc = subjournalPage(pPg)) - ){ - return rc; - } - - PAGERTRACE(("MOVE %d page %d (needSync=%d) moves to %d\n", - PAGERID(pPager), pPg->pgno, (pPg->flags&PGHDR_NEED_SYNC)?1:0, pgno)); - IOTRACE(("MOVE %p %d %d\n", pPager, pPg->pgno, pgno)) - - /* If the journal needs to be sync()ed before page pPg->pgno can - ** be written to, store pPg->pgno in local variable needSyncPgno. - ** - ** If the isCommit flag is set, there is no need to remember that - ** the journal needs to be sync()ed before database page pPg->pgno - ** can be written to. The caller has already promised not to write to it. - */ - if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){ - needSyncPgno = pPg->pgno; - assert( pPager->journalMode==PAGER_JOURNALMODE_OFF || - pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); - assert( pPg->flags&PGHDR_DIRTY ); - } - - /* If the cache contains a page with page-number pgno, remove it - ** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for - ** page pgno before the 'move' operation, it needs to be retained - ** for the page moved there. - */ - pPg->flags &= ~PGHDR_NEED_SYNC; - pPgOld = pager_lookup(pPager, pgno); - assert( !pPgOld || pPgOld->nRef==1 ); - if( pPgOld ){ - pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); - if( MEMDB ){ - /* Do not discard pages from an in-memory database since we might - ** need to rollback later. Just move the page out of the way. */ - sqlite3PcacheMove(pPgOld, pPager->dbSize+1); - }else{ - sqlite3PcacheDrop(pPgOld); - } - } - - origPgno = pPg->pgno; - sqlite3PcacheMove(pPg, pgno); - sqlite3PcacheMakeDirty(pPg); - - /* For an in-memory database, make sure the original page continues - ** to exist, in case the transaction needs to roll back. Use pPgOld - ** as the original page since it has already been allocated. - */ - if( MEMDB ){ - assert( pPgOld ); - sqlite3PcacheMove(pPgOld, origPgno); - sqlite3PagerUnref(pPgOld); - } - - if( needSyncPgno ){ - /* If needSyncPgno is non-zero, then the journal file needs to be - ** sync()ed before any data is written to database file page needSyncPgno. - ** Currently, no such page exists in the page-cache and the - ** "is journaled" bitvec flag has been set. This needs to be remedied by - ** loading the page into the pager-cache and setting the PGHDR_NEED_SYNC - ** flag. - ** - ** If the attempt to load the page into the page-cache fails, (due - ** to a malloc() or IO failure), clear the bit in the pInJournal[] - ** array. Otherwise, if the page is loaded and written again in - ** this transaction, it may be written to the database file before - ** it is synced into the journal file. This way, it may end up in - ** the journal file twice, but that is not a problem. - */ - PgHdr *pPgHdr; - rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr); - if( rc!=SQLITE_OK ){ - if( needSyncPgno<=pPager->dbOrigSize ){ - assert( pPager->pTmpSpace!=0 ); - sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace); - } - return rc; - } - pPgHdr->flags |= PGHDR_NEED_SYNC; - sqlite3PcacheMakeDirty(pPgHdr); - sqlite3PagerUnref(pPgHdr); - } - - return SQLITE_OK; -} -#endif - -/* -** Return a pointer to the data for the specified page. -*/ -SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *pPg){ - assert( pPg->nRef>0 || pPg->pPager->memDb ); - return pPg->pData; -} - -/* -** Return a pointer to the Pager.nExtra bytes of "extra" space -** allocated along with the specified page. -*/ -SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *pPg){ - return pPg->pExtra; -} - -/* -** Get/set the locking-mode for this pager. Parameter eMode must be one -** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or -** PAGER_LOCKINGMODE_EXCLUSIVE. If the parameter is not _QUERY, then -** the locking-mode is set to the value specified. -** -** The returned value is either PAGER_LOCKINGMODE_NORMAL or -** PAGER_LOCKINGMODE_EXCLUSIVE, indicating the current (possibly updated) -** locking-mode. -*/ -SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *pPager, int eMode){ - assert( eMode==PAGER_LOCKINGMODE_QUERY - || eMode==PAGER_LOCKINGMODE_NORMAL - || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); - assert( PAGER_LOCKINGMODE_QUERY<0 ); - assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); - assert( pPager->exclusiveMode || 0==sqlite3WalHeapMemory(pPager->pWal) ); - if( eMode>=0 && !pPager->tempFile && !sqlite3WalHeapMemory(pPager->pWal) ){ - pPager->exclusiveMode = (u8)eMode; - } - return (int)pPager->exclusiveMode; -} - -/* -** Set the journal-mode for this pager. Parameter eMode must be one of: -** -** PAGER_JOURNALMODE_DELETE -** PAGER_JOURNALMODE_TRUNCATE -** PAGER_JOURNALMODE_PERSIST -** PAGER_JOURNALMODE_OFF -** PAGER_JOURNALMODE_MEMORY -** PAGER_JOURNALMODE_WAL -** -** The journalmode is set to the value specified if the change is allowed. -** The change may be disallowed for the following reasons: -** -** * An in-memory database can only have its journal_mode set to _OFF -** or _MEMORY. -** -** * Temporary databases cannot have _WAL journalmode. -** -** The returned indicate the current (possibly updated) journal-mode. -*/ -SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ - u8 eOld = pPager->journalMode; /* Prior journalmode */ - -#ifdef SQLITE_DEBUG - /* The print_pager_state() routine is intended to be used by the debugger - ** only. We invoke it once here to suppress a compiler warning. */ - print_pager_state(pPager); -#endif - - - /* The eMode parameter is always valid */ - assert( eMode==PAGER_JOURNALMODE_DELETE - || eMode==PAGER_JOURNALMODE_TRUNCATE - || eMode==PAGER_JOURNALMODE_PERSIST - || eMode==PAGER_JOURNALMODE_OFF - || eMode==PAGER_JOURNALMODE_WAL - || eMode==PAGER_JOURNALMODE_MEMORY ); - - /* This routine is only called from the OP_JournalMode opcode, and - ** the logic there will never allow a temporary file to be changed - ** to WAL mode. - */ - assert( pPager->tempFile==0 || eMode!=PAGER_JOURNALMODE_WAL ); - - /* Do allow the journalmode of an in-memory database to be set to - ** anything other than MEMORY or OFF - */ - if( MEMDB ){ - assert( eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF ); - if( eMode!=PAGER_JOURNALMODE_MEMORY && eMode!=PAGER_JOURNALMODE_OFF ){ - eMode = eOld; - } - } - - if( eMode!=eOld ){ - - /* Change the journal mode. */ - assert( pPager->eState!=PAGER_ERROR ); - pPager->journalMode = (u8)eMode; - - /* When transistioning from TRUNCATE or PERSIST to any other journal - ** mode except WAL, unless the pager is in locking_mode=exclusive mode, - ** delete the journal file. - */ - assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); - assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); - assert( (PAGER_JOURNALMODE_DELETE & 5)==0 ); - assert( (PAGER_JOURNALMODE_MEMORY & 5)==4 ); - assert( (PAGER_JOURNALMODE_OFF & 5)==0 ); - assert( (PAGER_JOURNALMODE_WAL & 5)==5 ); - - assert( isOpen(pPager->fd) || pPager->exclusiveMode ); - if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){ - - /* In this case we would like to delete the journal file. If it is - ** not possible, then that is not a problem. Deleting the journal file - ** here is an optimization only. - ** - ** Before deleting the journal file, obtain a RESERVED lock on the - ** database file. This ensures that the journal file is not deleted - ** while it is in use by some other client. - */ - sqlite3OsClose(pPager->jfd); - if( pPager->eLock>=RESERVED_LOCK ){ - sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); - }else{ - int rc = SQLITE_OK; - int state = pPager->eState; - assert( state==PAGER_OPEN || state==PAGER_READER ); - if( state==PAGER_OPEN ){ - rc = sqlite3PagerSharedLock(pPager); - } - if( pPager->eState==PAGER_READER ){ - assert( rc==SQLITE_OK ); - rc = pagerLockDb(pPager, RESERVED_LOCK); - } - if( rc==SQLITE_OK ){ - sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); - } - if( rc==SQLITE_OK && state==PAGER_READER ){ - pagerUnlockDb(pPager, SHARED_LOCK); - }else if( state==PAGER_OPEN ){ - pager_unlock(pPager); - } - assert( state==pPager->eState ); - } - } - } - - /* Return the new journal mode */ - return (int)pPager->journalMode; -} - -/* -** Return the current journal mode. -*/ -SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager *pPager){ - return (int)pPager->journalMode; -} - -/* -** Return TRUE if the pager is in a state where it is OK to change the -** journalmode. Journalmode changes can only happen when the database -** is unmodified. -*/ -SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager *pPager){ - assert( assert_pager_state(pPager) ); - if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0; - if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0; - return 1; -} - -/* -** Get/set the size-limit used for persistent journal files. -** -** Setting the size limit to -1 means no limit is enforced. -** An attempt to set a limit smaller than -1 is a no-op. -*/ -SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){ - if( iLimit>=-1 ){ - pPager->journalSizeLimit = iLimit; - sqlite3WalLimit(pPager->pWal, iLimit); - } - return pPager->journalSizeLimit; -} - -/* -** Return a pointer to the pPager->pBackup variable. The backup module -** in backup.c maintains the content of this variable. This module -** uses it opaquely as an argument to sqlite3BackupRestart() and -** sqlite3BackupUpdate() only. -*/ -SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ - return &pPager->pBackup; -} - -#ifndef SQLITE_OMIT_VACUUM -/* -** Unless this is an in-memory or temporary database, clear the pager cache. -*/ -SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *pPager){ - if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager); -} -#endif - -#ifndef SQLITE_OMIT_WAL -/* -** This function is called when the user invokes "PRAGMA wal_checkpoint", -** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint() -** or wal_blocking_checkpoint() API functions. -** -** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. -*/ -SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){ - int rc = SQLITE_OK; - if( pPager->pWal ){ - rc = sqlite3WalCheckpoint(pPager->pWal, eMode, - pPager->xBusyHandler, pPager->pBusyHandlerArg, - pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, - pnLog, pnCkpt - ); - } - return rc; -} - -SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){ - return sqlite3WalCallback(pPager->pWal); -} - -/* -** Return true if the underlying VFS for the given pager supports the -** primitives necessary for write-ahead logging. -*/ -SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager){ - const sqlite3_io_methods *pMethods = pPager->fd->pMethods; - return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap); -} - -/* -** Attempt to take an exclusive lock on the database file. If a PENDING lock -** is obtained instead, immediately release it. -*/ -static int pagerExclusiveLock(Pager *pPager){ - int rc; /* Return code */ - - assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); - rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - /* If the attempt to grab the exclusive lock failed, release the - ** pending lock that may have been obtained instead. */ - pagerUnlockDb(pPager, SHARED_LOCK); - } - - return rc; -} - -/* -** Call sqlite3WalOpen() to open the WAL handle. If the pager is in -** exclusive-locking mode when this function is called, take an EXCLUSIVE -** lock on the database file and use heap-memory to store the wal-index -** in. Otherwise, use the normal shared-memory. -*/ -static int pagerOpenWal(Pager *pPager){ - int rc = SQLITE_OK; - - assert( pPager->pWal==0 && pPager->tempFile==0 ); - assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); - - /* If the pager is already in exclusive-mode, the WAL module will use - ** heap-memory for the wal-index instead of the VFS shared-memory - ** implementation. Take the exclusive lock now, before opening the WAL - ** file, to make sure this is safe. - */ - if( pPager->exclusiveMode ){ - rc = pagerExclusiveLock(pPager); - } - - /* Open the connection to the log file. If this operation fails, - ** (e.g. due to malloc() failure), return an error code. - */ - if( rc==SQLITE_OK ){ - rc = sqlite3WalOpen(pPager->pVfs, - pPager->fd, pPager->zWal, pPager->exclusiveMode, - pPager->journalSizeLimit, &pPager->pWal - ); - } - - return rc; -} - - -/* -** The caller must be holding a SHARED lock on the database file to call -** this function. -** -** If the pager passed as the first argument is open on a real database -** file (not a temp file or an in-memory database), and the WAL file -** is not already open, make an attempt to open it now. If successful, -** return SQLITE_OK. If an error occurs or the VFS used by the pager does -** not support the xShmXXX() methods, return an error code. *pbOpen is -** not modified in either case. -** -** If the pager is open on a temp-file (or in-memory database), or if -** the WAL file is already open, set *pbOpen to 1 and return SQLITE_OK -** without doing anything. -*/ -SQLITE_PRIVATE int sqlite3PagerOpenWal( - Pager *pPager, /* Pager object */ - int *pbOpen /* OUT: Set to true if call is a no-op */ -){ - int rc = SQLITE_OK; /* Return code */ - - assert( assert_pager_state(pPager) ); - assert( pPager->eState==PAGER_OPEN || pbOpen ); - assert( pPager->eState==PAGER_READER || !pbOpen ); - assert( pbOpen==0 || *pbOpen==0 ); - assert( pbOpen!=0 || (!pPager->tempFile && !pPager->pWal) ); - - if( !pPager->tempFile && !pPager->pWal ){ - if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; - - /* Close any rollback journal previously open */ - sqlite3OsClose(pPager->jfd); - - rc = pagerOpenWal(pPager); - if( rc==SQLITE_OK ){ - pPager->journalMode = PAGER_JOURNALMODE_WAL; - pPager->eState = PAGER_OPEN; - } - }else{ - *pbOpen = 1; - } - - return rc; -} - -/* -** This function is called to close the connection to the log file prior -** to switching from WAL to rollback mode. -** -** Before closing the log file, this function attempts to take an -** EXCLUSIVE lock on the database file. If this cannot be obtained, an -** error (SQLITE_BUSY) is returned and the log connection is not closed. -** If successful, the EXCLUSIVE lock is not released before returning. -*/ -SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager){ - int rc = SQLITE_OK; - - assert( pPager->journalMode==PAGER_JOURNALMODE_WAL ); - - /* If the log file is not already open, but does exist in the file-system, - ** it may need to be checkpointed before the connection can switch to - ** rollback mode. Open it now so this can happen. - */ - if( !pPager->pWal ){ - int logexists = 0; - rc = pagerLockDb(pPager, SHARED_LOCK); - if( rc==SQLITE_OK ){ - rc = sqlite3OsAccess( - pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists - ); - } - if( rc==SQLITE_OK && logexists ){ - rc = pagerOpenWal(pPager); - } - } - - /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on - ** the database file, the log and log-summary files will be deleted. - */ - if( rc==SQLITE_OK && pPager->pWal ){ - rc = pagerExclusiveLock(pPager); - if( rc==SQLITE_OK ){ - rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, - pPager->pageSize, (u8*)pPager->pTmpSpace); - pPager->pWal = 0; - } - } - return rc; -} - -#endif /* !SQLITE_OMIT_WAL */ - -#ifdef SQLITE_ENABLE_ZIPVFS -/* -** A read-lock must be held on the pager when this function is called. If -** the pager is in WAL mode and the WAL file currently contains one or more -** frames, return the size in bytes of the page images stored within the -** WAL frames. Otherwise, if this is not a WAL database or the WAL file -** is empty, return 0. -*/ -SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){ - assert( pPager->eState==PAGER_READER ); - return sqlite3WalFramesize(pPager->pWal); -} -#endif - -#ifdef SQLITE_HAS_CODEC -/* -** This function is called by the wal module when writing page content -** into the log file. -** -** This function returns a pointer to a buffer containing the encrypted -** page content. If a malloc fails, this function may return NULL. -*/ -SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){ - void *aData = 0; - CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData); - return aData; -} -#endif /* SQLITE_HAS_CODEC */ - -#endif /* SQLITE_OMIT_DISKIO */ - -/************** End of pager.c ***********************************************/ -/************** Begin file wal.c *********************************************/ -/* -** 2010 February 1 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains the implementation of a write-ahead log (WAL) used in -** "journal_mode=WAL" mode. -** -** WRITE-AHEAD LOG (WAL) FILE FORMAT -** -** A WAL file consists of a header followed by zero or more "frames". -** Each frame records the revised content of a single page from the -** database file. All changes to the database are recorded by writing -** frames into the WAL. Transactions commit when a frame is written that -** contains a commit marker. A single WAL can and usually does record -** multiple transactions. Periodically, the content of the WAL is -** transferred back into the database file in an operation called a -** "checkpoint". -** -** A single WAL file can be used multiple times. In other words, the -** WAL can fill up with frames and then be checkpointed and then new -** frames can overwrite the old ones. A WAL always grows from beginning -** toward the end. Checksums and counters attached to each frame are -** used to determine which frames within the WAL are valid and which -** are leftovers from prior checkpoints. -** -** The WAL header is 32 bytes in size and consists of the following eight -** big-endian 32-bit unsigned integer values: -** -** 0: Magic number. 0x377f0682 or 0x377f0683 -** 4: File format version. Currently 3007000 -** 8: Database page size. Example: 1024 -** 12: Checkpoint sequence number -** 16: Salt-1, random integer incremented with each checkpoint -** 20: Salt-2, a different random integer changing with each ckpt -** 24: Checksum-1 (first part of checksum for first 24 bytes of header). -** 28: Checksum-2 (second part of checksum for first 24 bytes of header). -** -** Immediately following the wal-header are zero or more frames. Each -** frame consists of a 24-byte frame-header followed by a bytes -** of page data. The frame-header is six big-endian 32-bit unsigned -** integer values, as follows: -** -** 0: Page number. -** 4: For commit records, the size of the database image in pages -** after the commit. For all other records, zero. -** 8: Salt-1 (copied from the header) -** 12: Salt-2 (copied from the header) -** 16: Checksum-1. -** 20: Checksum-2. -** -** A frame is considered valid if and only if the following conditions are -** true: -** -** (1) The salt-1 and salt-2 values in the frame-header match -** salt values in the wal-header -** -** (2) The checksum values in the final 8 bytes of the frame-header -** exactly match the checksum computed consecutively on the -** WAL header and the first 8 bytes and the content of all frames -** up to and including the current frame. -** -** The checksum is computed using 32-bit big-endian integers if the -** magic number in the first 4 bytes of the WAL is 0x377f0683 and it -** is computed using little-endian if the magic number is 0x377f0682. -** The checksum values are always stored in the frame header in a -** big-endian format regardless of which byte order is used to compute -** the checksum. The checksum is computed by interpreting the input as -** an even number of unsigned 32-bit integers: x[0] through x[N]. The -** algorithm used for the checksum is as follows: -** -** for i from 0 to n-1 step 2: -** s0 += x[i] + s1; -** s1 += x[i+1] + s0; -** endfor -** -** Note that s0 and s1 are both weighted checksums using fibonacci weights -** in reverse order (the largest fibonacci weight occurs on the first element -** of the sequence being summed.) The s1 value spans all 32-bit -** terms of the sequence whereas s0 omits the final term. -** -** On a checkpoint, the WAL is first VFS.xSync-ed, then valid content of the -** WAL is transferred into the database, then the database is VFS.xSync-ed. -** The VFS.xSync operations serve as write barriers - all writes launched -** before the xSync must complete before any write that launches after the -** xSync begins. -** -** After each checkpoint, the salt-1 value is incremented and the salt-2 -** value is randomized. This prevents old and new frames in the WAL from -** being considered valid at the same time and being checkpointing together -** following a crash. -** -** READER ALGORITHM -** -** To read a page from the database (call it page number P), a reader -** first checks the WAL to see if it contains page P. If so, then the -** last valid instance of page P that is a followed by a commit frame -** or is a commit frame itself becomes the value read. If the WAL -** contains no copies of page P that are valid and which are a commit -** frame or are followed by a commit frame, then page P is read from -** the database file. -** -** To start a read transaction, the reader records the index of the last -** valid frame in the WAL. The reader uses this recorded "mxFrame" value -** for all subsequent read operations. New transactions can be appended -** to the WAL, but as long as the reader uses its original mxFrame value -** and ignores the newly appended content, it will see a consistent snapshot -** of the database from a single point in time. This technique allows -** multiple concurrent readers to view different versions of the database -** content simultaneously. -** -** The reader algorithm in the previous paragraphs works correctly, but -** because frames for page P can appear anywhere within the WAL, the -** reader has to scan the entire WAL looking for page P frames. If the -** WAL is large (multiple megabytes is typical) that scan can be slow, -** and read performance suffers. To overcome this problem, a separate -** data structure called the wal-index is maintained to expedite the -** search for frames of a particular page. -** -** WAL-INDEX FORMAT -** -** Conceptually, the wal-index is shared memory, though VFS implementations -** might choose to implement the wal-index using a mmapped file. Because -** the wal-index is shared memory, SQLite does not support journal_mode=WAL -** on a network filesystem. All users of the database must be able to -** share memory. -** -** The wal-index is transient. After a crash, the wal-index can (and should -** be) reconstructed from the original WAL file. In fact, the VFS is required -** to either truncate or zero the header of the wal-index when the last -** connection to it closes. Because the wal-index is transient, it can -** use an architecture-specific format; it does not have to be cross-platform. -** Hence, unlike the database and WAL file formats which store all values -** as big endian, the wal-index can store multi-byte values in the native -** byte order of the host computer. -** -** The purpose of the wal-index is to answer this question quickly: Given -** a page number P and a maximum frame index M, return the index of the -** last frame in the wal before frame M for page P in the WAL, or return -** NULL if there are no frames for page P in the WAL prior to M. -** -** The wal-index consists of a header region, followed by an one or -** more index blocks. -** -** The wal-index header contains the total number of frames within the WAL -** in the mxFrame field. -** -** Each index block except for the first contains information on -** HASHTABLE_NPAGE frames. The first index block contains information on -** HASHTABLE_NPAGE_ONE frames. The values of HASHTABLE_NPAGE_ONE and -** HASHTABLE_NPAGE are selected so that together the wal-index header and -** first index block are the same size as all other index blocks in the -** wal-index. -** -** Each index block contains two sections, a page-mapping that contains the -** database page number associated with each wal frame, and a hash-table -** that allows readers to query an index block for a specific page number. -** The page-mapping is an array of HASHTABLE_NPAGE (or HASHTABLE_NPAGE_ONE -** for the first index block) 32-bit page numbers. The first entry in the -** first index-block contains the database page number corresponding to the -** first frame in the WAL file. The first entry in the second index block -** in the WAL file corresponds to the (HASHTABLE_NPAGE_ONE+1)th frame in -** the log, and so on. -** -** The last index block in a wal-index usually contains less than the full -** complement of HASHTABLE_NPAGE (or HASHTABLE_NPAGE_ONE) page-numbers, -** depending on the contents of the WAL file. This does not change the -** allocated size of the page-mapping array - the page-mapping array merely -** contains unused entries. -** -** Even without using the hash table, the last frame for page P -** can be found by scanning the page-mapping sections of each index block -** starting with the last index block and moving toward the first, and -** within each index block, starting at the end and moving toward the -** beginning. The first entry that equals P corresponds to the frame -** holding the content for that page. -** -** The hash table consists of HASHTABLE_NSLOT 16-bit unsigned integers. -** HASHTABLE_NSLOT = 2*HASHTABLE_NPAGE, and there is one entry in the -** hash table for each page number in the mapping section, so the hash -** table is never more than half full. The expected number of collisions -** prior to finding a match is 1. Each entry of the hash table is an -** 1-based index of an entry in the mapping section of the same -** index block. Let K be the 1-based index of the largest entry in -** the mapping section. (For index blocks other than the last, K will -** always be exactly HASHTABLE_NPAGE (4096) and for the last index block -** K will be (mxFrame%HASHTABLE_NPAGE).) Unused slots of the hash table -** contain a value of 0. -** -** To look for page P in the hash table, first compute a hash iKey on -** P as follows: -** -** iKey = (P * 383) % HASHTABLE_NSLOT -** -** Then start scanning entries of the hash table, starting with iKey -** (wrapping around to the beginning when the end of the hash table is -** reached) until an unused hash slot is found. Let the first unused slot -** be at index iUnused. (iUnused might be less than iKey if there was -** wrap-around.) Because the hash table is never more than half full, -** the search is guaranteed to eventually hit an unused entry. Let -** iMax be the value between iKey and iUnused, closest to iUnused, -** where aHash[iMax]==P. If there is no iMax entry (if there exists -** no hash slot such that aHash[i]==p) then page P is not in the -** current index block. Otherwise the iMax-th mapping entry of the -** current index block corresponds to the last entry that references -** page P. -** -** A hash search begins with the last index block and moves toward the -** first index block, looking for entries corresponding to page P. On -** average, only two or three slots in each index block need to be -** examined in order to either find the last entry for page P, or to -** establish that no such entry exists in the block. Each index block -** holds over 4000 entries. So two or three index blocks are sufficient -** to cover a typical 10 megabyte WAL file, assuming 1K pages. 8 or 10 -** comparisons (on average) suffice to either locate a frame in the -** WAL or to establish that the frame does not exist in the WAL. This -** is much faster than scanning the entire 10MB WAL. -** -** Note that entries are added in order of increasing K. Hence, one -** reader might be using some value K0 and a second reader that started -** at a later time (after additional transactions were added to the WAL -** and to the wal-index) might be using a different value K1, where K1>K0. -** Both readers can use the same hash table and mapping section to get -** the correct result. There may be entries in the hash table with -** K>K0 but to the first reader, those entries will appear to be unused -** slots in the hash table and so the first reader will get an answer as -** if no values greater than K0 had ever been inserted into the hash table -** in the first place - which is what reader one wants. Meanwhile, the -** second reader using K1 will see additional values that were inserted -** later, which is exactly what reader two wants. -** -** When a rollback occurs, the value of K is decreased. Hash table entries -** that correspond to frames greater than the new K value are removed -** from the hash table at this point. -*/ -#ifndef SQLITE_OMIT_WAL - - -/* -** Trace output macros -*/ -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -SQLITE_PRIVATE int sqlite3WalTrace = 0; -# define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X -#else -# define WALTRACE(X) -#endif - -/* -** The maximum (and only) versions of the wal and wal-index formats -** that may be interpreted by this version of SQLite. -** -** If a client begins recovering a WAL file and finds that (a) the checksum -** values in the wal-header are correct and (b) the version field is not -** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN. -** -** Similarly, if a client successfully reads a wal-index header (i.e. the -** checksum test is successful) and finds that the version field is not -** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite -** returns SQLITE_CANTOPEN. -*/ -#define WAL_MAX_VERSION 3007000 -#define WALINDEX_MAX_VERSION 3007000 - -/* -** Indices of various locking bytes. WAL_NREADER is the number -** of available reader locks and should be at least 3. -*/ -#define WAL_WRITE_LOCK 0 -#define WAL_ALL_BUT_WRITE 1 -#define WAL_CKPT_LOCK 1 -#define WAL_RECOVER_LOCK 2 -#define WAL_READ_LOCK(I) (3+(I)) -#define WAL_NREADER (SQLITE_SHM_NLOCK-3) - - -/* Object declarations */ -typedef struct WalIndexHdr WalIndexHdr; -typedef struct WalIterator WalIterator; -typedef struct WalCkptInfo WalCkptInfo; - - -/* -** The following object holds a copy of the wal-index header content. -** -** The actual header in the wal-index consists of two copies of this -** object. -** -** The szPage value can be any power of 2 between 512 and 32768, inclusive. -** Or it can be 1 to represent a 65536-byte page. The latter case was -** added in 3.7.1 when support for 64K pages was added. -*/ -struct WalIndexHdr { - u32 iVersion; /* Wal-index version */ - u32 unused; /* Unused (padding) field */ - u32 iChange; /* Counter incremented each transaction */ - u8 isInit; /* 1 when initialized */ - u8 bigEndCksum; /* True if checksums in WAL are big-endian */ - u16 szPage; /* Database page size in bytes. 1==64K */ - u32 mxFrame; /* Index of last valid frame in the WAL */ - u32 nPage; /* Size of database in pages */ - u32 aFrameCksum[2]; /* Checksum of last frame in log */ - u32 aSalt[2]; /* Two salt values copied from WAL header */ - u32 aCksum[2]; /* Checksum over all prior fields */ -}; - -/* -** A copy of the following object occurs in the wal-index immediately -** following the second copy of the WalIndexHdr. This object stores -** information used by checkpoint. -** -** nBackfill is the number of frames in the WAL that have been written -** back into the database. (We call the act of moving content from WAL to -** database "backfilling".) The nBackfill number is never greater than -** WalIndexHdr.mxFrame. nBackfill can only be increased by threads -** holding the WAL_CKPT_LOCK lock (which includes a recovery thread). -** However, a WAL_WRITE_LOCK thread can move the value of nBackfill from -** mxFrame back to zero when the WAL is reset. -** -** There is one entry in aReadMark[] for each reader lock. If a reader -** holds read-lock K, then the value in aReadMark[K] is no greater than -** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff) -** for any aReadMark[] means that entry is unused. aReadMark[0] is -** a special case; its value is never used and it exists as a place-holder -** to avoid having to offset aReadMark[] indexs by one. Readers holding -** WAL_READ_LOCK(0) always ignore the entire WAL and read all content -** directly from the database. -** -** The value of aReadMark[K] may only be changed by a thread that -** is holding an exclusive lock on WAL_READ_LOCK(K). Thus, the value of -** aReadMark[K] cannot changed while there is a reader is using that mark -** since the reader will be holding a shared lock on WAL_READ_LOCK(K). -** -** The checkpointer may only transfer frames from WAL to database where -** the frame numbers are less than or equal to every aReadMark[] that is -** in use (that is, every aReadMark[j] for which there is a corresponding -** WAL_READ_LOCK(j)). New readers (usually) pick the aReadMark[] with the -** largest value and will increase an unused aReadMark[] to mxFrame if there -** is not already an aReadMark[] equal to mxFrame. The exception to the -** previous sentence is when nBackfill equals mxFrame (meaning that everything -** in the WAL has been backfilled into the database) then new readers -** will choose aReadMark[0] which has value 0 and hence such reader will -** get all their all content directly from the database file and ignore -** the WAL. -** -** Writers normally append new frames to the end of the WAL. However, -** if nBackfill equals mxFrame (meaning that all WAL content has been -** written back into the database) and if no readers are using the WAL -** (in other words, if there are no WAL_READ_LOCK(i) where i>0) then -** the writer will first "reset" the WAL back to the beginning and start -** writing new content beginning at frame 1. -** -** We assume that 32-bit loads are atomic and so no locks are needed in -** order to read from any aReadMark[] entries. -*/ -struct WalCkptInfo { - u32 nBackfill; /* Number of WAL frames backfilled into DB */ - u32 aReadMark[WAL_NREADER]; /* Reader marks */ -}; -#define READMARK_NOT_USED 0xffffffff - - -/* A block of WALINDEX_LOCK_RESERVED bytes beginning at -** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems -** only support mandatory file-locks, we do not read or write data -** from the region of the file on which locks are applied. -*/ -#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2 + sizeof(WalCkptInfo)) -#define WALINDEX_LOCK_RESERVED 16 -#define WALINDEX_HDR_SIZE (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED) - -/* Size of header before each frame in wal */ -#define WAL_FRAME_HDRSIZE 24 - -/* Size of write ahead log header, including checksum. */ -/* #define WAL_HDRSIZE 24 */ -#define WAL_HDRSIZE 32 - -/* WAL magic value. Either this value, or the same value with the least -** significant bit also set (WAL_MAGIC | 0x00000001) is stored in 32-bit -** big-endian format in the first 4 bytes of a WAL file. -** -** If the LSB is set, then the checksums for each frame within the WAL -** file are calculated by treating all data as an array of 32-bit -** big-endian words. Otherwise, they are calculated by interpreting -** all data as 32-bit little-endian words. -*/ -#define WAL_MAGIC 0x377f0682 - -/* -** Return the offset of frame iFrame in the write-ahead log file, -** assuming a database page size of szPage bytes. The offset returned -** is to the start of the write-ahead log frame-header. -*/ -#define walFrameOffset(iFrame, szPage) ( \ - WAL_HDRSIZE + ((iFrame)-1)*(i64)((szPage)+WAL_FRAME_HDRSIZE) \ -) - -/* -** An open write-ahead log file is represented by an instance of the -** following object. -*/ -struct Wal { - sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ - sqlite3_file *pDbFd; /* File handle for the database file */ - sqlite3_file *pWalFd; /* File handle for WAL file */ - u32 iCallback; /* Value to pass to log callback (or 0) */ - i64 mxWalSize; /* Truncate WAL to this size upon reset */ - int nWiData; /* Size of array apWiData */ - int szFirstBlock; /* Size of first block written to WAL file */ - volatile u32 **apWiData; /* Pointer to wal-index content in memory */ - u32 szPage; /* Database page size */ - i16 readLock; /* Which read lock is being held. -1 for none */ - u8 syncFlags; /* Flags to use to sync header writes */ - u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ - u8 writeLock; /* True if in a write transaction */ - u8 ckptLock; /* True if holding a checkpoint lock */ - u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */ - u8 truncateOnCommit; /* True to truncate WAL file on commit */ - u8 syncHeader; /* Fsync the WAL header if true */ - u8 padToSectorBoundary; /* Pad transactions out to the next sector */ - WalIndexHdr hdr; /* Wal-index header for current transaction */ - const char *zWalName; /* Name of WAL file */ - u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ -#ifdef SQLITE_DEBUG - u8 lockError; /* True if a locking error has occurred */ -#endif -}; - -/* -** Candidate values for Wal.exclusiveMode. -*/ -#define WAL_NORMAL_MODE 0 -#define WAL_EXCLUSIVE_MODE 1 -#define WAL_HEAPMEMORY_MODE 2 - -/* -** Possible values for WAL.readOnly -*/ -#define WAL_RDWR 0 /* Normal read/write connection */ -#define WAL_RDONLY 1 /* The WAL file is readonly */ -#define WAL_SHM_RDONLY 2 /* The SHM file is readonly */ - -/* -** Each page of the wal-index mapping contains a hash-table made up of -** an array of HASHTABLE_NSLOT elements of the following type. -*/ -typedef u16 ht_slot; - -/* -** This structure is used to implement an iterator that loops through -** all frames in the WAL in database page order. Where two or more frames -** correspond to the same database page, the iterator visits only the -** frame most recently written to the WAL (in other words, the frame with -** the largest index). -** -** The internals of this structure are only accessed by: -** -** walIteratorInit() - Create a new iterator, -** walIteratorNext() - Step an iterator, -** walIteratorFree() - Free an iterator. -** -** This functionality is used by the checkpoint code (see walCheckpoint()). -*/ -struct WalIterator { - int iPrior; /* Last result returned from the iterator */ - int nSegment; /* Number of entries in aSegment[] */ - struct WalSegment { - int iNext; /* Next slot in aIndex[] not yet returned */ - ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ - u32 *aPgno; /* Array of page numbers. */ - int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ - int iZero; /* Frame number associated with aPgno[0] */ - } aSegment[1]; /* One for every 32KB page in the wal-index */ -}; - -/* -** Define the parameters of the hash tables in the wal-index file. There -** is a hash-table following every HASHTABLE_NPAGE page numbers in the -** wal-index. -** -** Changing any of these constants will alter the wal-index format and -** create incompatibilities. -*/ -#define HASHTABLE_NPAGE 4096 /* Must be power of 2 */ -#define HASHTABLE_HASH_1 383 /* Should be prime */ -#define HASHTABLE_NSLOT (HASHTABLE_NPAGE*2) /* Must be a power of 2 */ - -/* -** The block of page numbers associated with the first hash-table in a -** wal-index is smaller than usual. This is so that there is a complete -** hash-table on each aligned 32KB page of the wal-index. -*/ -#define HASHTABLE_NPAGE_ONE (HASHTABLE_NPAGE - (WALINDEX_HDR_SIZE/sizeof(u32))) - -/* The wal-index is divided into pages of WALINDEX_PGSZ bytes each. */ -#define WALINDEX_PGSZ ( \ - sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \ -) - -/* -** Obtain a pointer to the iPage'th page of the wal-index. The wal-index -** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are -** numbered from zero. -** -** If this call is successful, *ppPage is set to point to the wal-index -** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs, -** then an SQLite error code is returned and *ppPage is set to 0. -*/ -static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ - int rc = SQLITE_OK; - - /* Enlarge the pWal->apWiData[] array if required */ - if( pWal->nWiData<=iPage ){ - int nByte = sizeof(u32*)*(iPage+1); - volatile u32 **apNew; - apNew = (volatile u32 **)sqlite3_realloc((void *)pWal->apWiData, nByte); - if( !apNew ){ - *ppPage = 0; - return SQLITE_NOMEM; - } - memset((void*)&apNew[pWal->nWiData], 0, - sizeof(u32*)*(iPage+1-pWal->nWiData)); - pWal->apWiData = apNew; - pWal->nWiData = iPage+1; - } - - /* Request a pointer to the required page from the VFS */ - if( pWal->apWiData[iPage]==0 ){ - if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ - pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ); - if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM; - }else{ - rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, - pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] - ); - if( rc==SQLITE_READONLY ){ - pWal->readOnly |= WAL_SHM_RDONLY; - rc = SQLITE_OK; - } - } - } - - *ppPage = pWal->apWiData[iPage]; - assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); - return rc; -} - -/* -** Return a pointer to the WalCkptInfo structure in the wal-index. -*/ -static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ - assert( pWal->nWiData>0 && pWal->apWiData[0] ); - return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]); -} - -/* -** Return a pointer to the WalIndexHdr structure in the wal-index. -*/ -static volatile WalIndexHdr *walIndexHdr(Wal *pWal){ - assert( pWal->nWiData>0 && pWal->apWiData[0] ); - return (volatile WalIndexHdr*)pWal->apWiData[0]; -} - -/* -** The argument to this macro must be of type u32. On a little-endian -** architecture, it returns the u32 value that results from interpreting -** the 4 bytes as a big-endian value. On a big-endian architecture, it -** returns the value that would be produced by intepreting the 4 bytes -** of the input value as a little-endian integer. -*/ -#define BYTESWAP32(x) ( \ - (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \ - + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \ -) - -/* -** Generate or extend an 8 byte checksum based on the data in -** array aByte[] and the initial values of aIn[0] and aIn[1] (or -** initial values of 0 and 0 if aIn==NULL). -** -** The checksum is written back into aOut[] before returning. -** -** nByte must be a positive multiple of 8. -*/ -static void walChecksumBytes( - int nativeCksum, /* True for native byte-order, false for non-native */ - u8 *a, /* Content to be checksummed */ - int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */ - const u32 *aIn, /* Initial checksum value input */ - u32 *aOut /* OUT: Final checksum value output */ -){ - u32 s1, s2; - u32 *aData = (u32 *)a; - u32 *aEnd = (u32 *)&a[nByte]; - - if( aIn ){ - s1 = aIn[0]; - s2 = aIn[1]; - }else{ - s1 = s2 = 0; - } - - assert( nByte>=8 ); - assert( (nByte&0x00000007)==0 ); - - if( nativeCksum ){ - do { - s1 += *aData++ + s2; - s2 += *aData++ + s1; - }while( aDataexclusiveMode!=WAL_HEAPMEMORY_MODE ){ - sqlite3OsShmBarrier(pWal->pDbFd); - } -} - -/* -** Write the header information in pWal->hdr into the wal-index. -** -** The checksum on pWal->hdr is updated before it is written. -*/ -static void walIndexWriteHdr(Wal *pWal){ - volatile WalIndexHdr *aHdr = walIndexHdr(pWal); - const int nCksum = offsetof(WalIndexHdr, aCksum); - - assert( pWal->writeLock ); - pWal->hdr.isInit = 1; - pWal->hdr.iVersion = WALINDEX_MAX_VERSION; - walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); - memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr)); - walShmBarrier(pWal); - memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr)); -} - -/* -** This function encodes a single frame header and writes it to a buffer -** supplied by the caller. A frame-header is made up of a series of -** 4-byte big-endian integers, as follows: -** -** 0: Page number. -** 4: For commit records, the size of the database image in pages -** after the commit. For all other records, zero. -** 8: Salt-1 (copied from the wal-header) -** 12: Salt-2 (copied from the wal-header) -** 16: Checksum-1. -** 20: Checksum-2. -*/ -static void walEncodeFrame( - Wal *pWal, /* The write-ahead log */ - u32 iPage, /* Database page number for frame */ - u32 nTruncate, /* New db size (or 0 for non-commit frames) */ - u8 *aData, /* Pointer to page data */ - u8 *aFrame /* OUT: Write encoded frame here */ -){ - int nativeCksum; /* True for native byte-order checksums */ - u32 *aCksum = pWal->hdr.aFrameCksum; - assert( WAL_FRAME_HDRSIZE==24 ); - sqlite3Put4byte(&aFrame[0], iPage); - sqlite3Put4byte(&aFrame[4], nTruncate); - memcpy(&aFrame[8], pWal->hdr.aSalt, 8); - - nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); - walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); - walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); - - sqlite3Put4byte(&aFrame[16], aCksum[0]); - sqlite3Put4byte(&aFrame[20], aCksum[1]); -} - -/* -** Check to see if the frame with header in aFrame[] and content -** in aData[] is valid. If it is a valid frame, fill *piPage and -** *pnTruncate and return true. Return if the frame is not valid. -*/ -static int walDecodeFrame( - Wal *pWal, /* The write-ahead log */ - u32 *piPage, /* OUT: Database page number for frame */ - u32 *pnTruncate, /* OUT: New db size (or 0 if not commit) */ - u8 *aData, /* Pointer to page data (for checksum) */ - u8 *aFrame /* Frame data */ -){ - int nativeCksum; /* True for native byte-order checksums */ - u32 *aCksum = pWal->hdr.aFrameCksum; - u32 pgno; /* Page number of the frame */ - assert( WAL_FRAME_HDRSIZE==24 ); - - /* A frame is only valid if the salt values in the frame-header - ** match the salt values in the wal-header. - */ - if( memcmp(&pWal->hdr.aSalt, &aFrame[8], 8)!=0 ){ - return 0; - } - - /* A frame is only valid if the page number is creater than zero. - */ - pgno = sqlite3Get4byte(&aFrame[0]); - if( pgno==0 ){ - return 0; - } - - /* A frame is only valid if a checksum of the WAL header, - ** all prior frams, the first 16 bytes of this frame-header, - ** and the frame-data matches the checksum in the last 8 - ** bytes of this frame-header. - */ - nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); - walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); - walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); - if( aCksum[0]!=sqlite3Get4byte(&aFrame[16]) - || aCksum[1]!=sqlite3Get4byte(&aFrame[20]) - ){ - /* Checksum failed. */ - return 0; - } - - /* If we reach this point, the frame is valid. Return the page number - ** and the new database size. - */ - *piPage = pgno; - *pnTruncate = sqlite3Get4byte(&aFrame[4]); - return 1; -} - - -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -/* -** Names of locks. This routine is used to provide debugging output and is not -** a part of an ordinary build. -*/ -static const char *walLockName(int lockIdx){ - if( lockIdx==WAL_WRITE_LOCK ){ - return "WRITE-LOCK"; - }else if( lockIdx==WAL_CKPT_LOCK ){ - return "CKPT-LOCK"; - }else if( lockIdx==WAL_RECOVER_LOCK ){ - return "RECOVER-LOCK"; - }else{ - static char zName[15]; - sqlite3_snprintf(sizeof(zName), zName, "READ-LOCK[%d]", - lockIdx-WAL_READ_LOCK(0)); - return zName; - } -} -#endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */ - - -/* -** Set or release locks on the WAL. Locks are either shared or exclusive. -** A lock cannot be moved directly between shared and exclusive - it must go -** through the unlocked state first. -** -** In locking_mode=EXCLUSIVE, all of these routines become no-ops. -*/ -static int walLockShared(Wal *pWal, int lockIdx){ - int rc; - if( pWal->exclusiveMode ) return SQLITE_OK; - rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, - SQLITE_SHM_LOCK | SQLITE_SHM_SHARED); - WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, - walLockName(lockIdx), rc ? "failed" : "ok")); - VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) - return rc; -} -static void walUnlockShared(Wal *pWal, int lockIdx){ - if( pWal->exclusiveMode ) return; - (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, - SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); - WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); -} -static int walLockExclusive(Wal *pWal, int lockIdx, int n){ - int rc; - if( pWal->exclusiveMode ) return SQLITE_OK; - rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, - SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); - WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, - walLockName(lockIdx), n, rc ? "failed" : "ok")); - VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) - return rc; -} -static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ - if( pWal->exclusiveMode ) return; - (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, - SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE); - WALTRACE(("WAL%p: release EXCLUSIVE-%s cnt=%d\n", pWal, - walLockName(lockIdx), n)); -} - -/* -** Compute a hash on a page number. The resulting hash value must land -** between 0 and (HASHTABLE_NSLOT-1). The walHashNext() function advances -** the hash to the next value in the event of a collision. -*/ -static int walHash(u32 iPage){ - assert( iPage>0 ); - assert( (HASHTABLE_NSLOT & (HASHTABLE_NSLOT-1))==0 ); - return (iPage*HASHTABLE_HASH_1) & (HASHTABLE_NSLOT-1); -} -static int walNextHash(int iPriorHash){ - return (iPriorHash+1)&(HASHTABLE_NSLOT-1); -} - -/* -** Return pointers to the hash table and page number array stored on -** page iHash of the wal-index. The wal-index is broken into 32KB pages -** numbered starting from 0. -** -** Set output variable *paHash to point to the start of the hash table -** in the wal-index file. Set *piZero to one less than the frame -** number of the first frame indexed by this hash table. If a -** slot in the hash table is set to N, it refers to frame number -** (*piZero+N) in the log. -** -** Finally, set *paPgno so that *paPgno[1] is the page number of the -** first frame indexed by the hash table, frame (*piZero+1). -*/ -static int walHashGet( - Wal *pWal, /* WAL handle */ - int iHash, /* Find the iHash'th table */ - volatile ht_slot **paHash, /* OUT: Pointer to hash index */ - volatile u32 **paPgno, /* OUT: Pointer to page number array */ - u32 *piZero /* OUT: Frame associated with *paPgno[0] */ -){ - int rc; /* Return code */ - volatile u32 *aPgno; - - rc = walIndexPage(pWal, iHash, &aPgno); - assert( rc==SQLITE_OK || iHash>0 ); - - if( rc==SQLITE_OK ){ - u32 iZero; - volatile ht_slot *aHash; - - aHash = (volatile ht_slot *)&aPgno[HASHTABLE_NPAGE]; - if( iHash==0 ){ - aPgno = &aPgno[WALINDEX_HDR_SIZE/sizeof(u32)]; - iZero = 0; - }else{ - iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE; - } - - *paPgno = &aPgno[-1]; - *paHash = aHash; - *piZero = iZero; - } - return rc; -} - -/* -** Return the number of the wal-index page that contains the hash-table -** and page-number array that contain entries corresponding to WAL frame -** iFrame. The wal-index is broken up into 32KB pages. Wal-index pages -** are numbered starting from 0. -*/ -static int walFramePage(u32 iFrame){ - int iHash = (iFrame+HASHTABLE_NPAGE-HASHTABLE_NPAGE_ONE-1) / HASHTABLE_NPAGE; - assert( (iHash==0 || iFrame>HASHTABLE_NPAGE_ONE) - && (iHash>=1 || iFrame<=HASHTABLE_NPAGE_ONE) - && (iHash<=1 || iFrame>(HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)) - && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) - && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) - ); - return iHash; -} - -/* -** Return the page number associated with frame iFrame in this WAL. -*/ -static u32 walFramePgno(Wal *pWal, u32 iFrame){ - int iHash = walFramePage(iFrame); - if( iHash==0 ){ - return pWal->apWiData[0][WALINDEX_HDR_SIZE/sizeof(u32) + iFrame - 1]; - } - return pWal->apWiData[iHash][(iFrame-1-HASHTABLE_NPAGE_ONE)%HASHTABLE_NPAGE]; -} - -/* -** Remove entries from the hash table that point to WAL slots greater -** than pWal->hdr.mxFrame. -** -** This function is called whenever pWal->hdr.mxFrame is decreased due -** to a rollback or savepoint. -** -** At most only the hash table containing pWal->hdr.mxFrame needs to be -** updated. Any later hash tables will be automatically cleared when -** pWal->hdr.mxFrame advances to the point where those hash tables are -** actually needed. -*/ -static void walCleanupHash(Wal *pWal){ - volatile ht_slot *aHash = 0; /* Pointer to hash table to clear */ - volatile u32 *aPgno = 0; /* Page number array for hash table */ - u32 iZero = 0; /* frame == (aHash[x]+iZero) */ - int iLimit = 0; /* Zero values greater than this */ - int nByte; /* Number of bytes to zero in aPgno[] */ - int i; /* Used to iterate through aHash[] */ - - assert( pWal->writeLock ); - testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); - testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE ); - testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE+1 ); - - if( pWal->hdr.mxFrame==0 ) return; - - /* Obtain pointers to the hash-table and page-number array containing - ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed - ** that the page said hash-table and array reside on is already mapped. - */ - assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); - assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); - walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &aHash, &aPgno, &iZero); - - /* Zero all hash-table entries that correspond to frame numbers greater - ** than pWal->hdr.mxFrame. - */ - iLimit = pWal->hdr.mxFrame - iZero; - assert( iLimit>0 ); - for(i=0; iiLimit ){ - aHash[i] = 0; - } - } - - /* Zero the entries in the aPgno array that correspond to frames with - ** frame numbers greater than pWal->hdr.mxFrame. - */ - nByte = (int)((char *)aHash - (char *)&aPgno[iLimit+1]); - memset((void *)&aPgno[iLimit+1], 0, nByte); - -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT - /* Verify that the every entry in the mapping region is still reachable - ** via the hash table even after the cleanup. - */ - if( iLimit ){ - int i; /* Loop counter */ - int iKey; /* Hash key */ - for(i=1; i<=iLimit; i++){ - for(iKey=walHash(aPgno[i]); aHash[iKey]; iKey=walNextHash(iKey)){ - if( aHash[iKey]==i ) break; - } - assert( aHash[iKey]==i ); - } - } -#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */ -} - - -/* -** Set an entry in the wal-index that will map database page number -** pPage into WAL frame iFrame. -*/ -static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ - int rc; /* Return code */ - u32 iZero = 0; /* One less than frame number of aPgno[1] */ - volatile u32 *aPgno = 0; /* Page number array */ - volatile ht_slot *aHash = 0; /* Hash table */ - - rc = walHashGet(pWal, walFramePage(iFrame), &aHash, &aPgno, &iZero); - - /* Assuming the wal-index file was successfully mapped, populate the - ** page number array and hash table entry. - */ - if( rc==SQLITE_OK ){ - int iKey; /* Hash table key */ - int idx; /* Value to write to hash-table slot */ - int nCollide; /* Number of hash collisions */ - - idx = iFrame - iZero; - assert( idx <= HASHTABLE_NSLOT/2 + 1 ); - - /* If this is the first entry to be added to this hash-table, zero the - ** entire hash table and aPgno[] array before proceding. - */ - if( idx==1 ){ - int nByte = (int)((u8 *)&aHash[HASHTABLE_NSLOT] - (u8 *)&aPgno[1]); - memset((void*)&aPgno[1], 0, nByte); - } - - /* If the entry in aPgno[] is already set, then the previous writer - ** must have exited unexpectedly in the middle of a transaction (after - ** writing one or more dirty pages to the WAL to free up memory). - ** Remove the remnants of that writers uncommitted transaction from - ** the hash-table before writing any new entries. - */ - if( aPgno[idx] ){ - walCleanupHash(pWal); - assert( !aPgno[idx] ); - } - - /* Write the aPgno[] array entry and the hash-table slot. */ - nCollide = idx; - for(iKey=walHash(iPage); aHash[iKey]; iKey=walNextHash(iKey)){ - if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT; - } - aPgno[idx] = iPage; - aHash[iKey] = (ht_slot)idx; - -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT - /* Verify that the number of entries in the hash table exactly equals - ** the number of entries in the mapping region. - */ - { - int i; /* Loop counter */ - int nEntry = 0; /* Number of entries in the hash table */ - for(i=0; ickptLock==1 || pWal->ckptLock==0 ); - assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); - assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); - assert( pWal->writeLock ); - iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; - nLock = SQLITE_SHM_NLOCK - iLock; - rc = walLockExclusive(pWal, iLock, nLock); - if( rc ){ - return rc; - } - WALTRACE(("WAL%p: recovery begin...\n", pWal)); - - memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); - - rc = sqlite3OsFileSize(pWal->pWalFd, &nSize); - if( rc!=SQLITE_OK ){ - goto recovery_error; - } - - if( nSize>WAL_HDRSIZE ){ - u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ - u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ - int szFrame; /* Number of bytes in buffer aFrame[] */ - u8 *aData; /* Pointer to data part of aFrame buffer */ - int iFrame; /* Index of last frame read */ - i64 iOffset; /* Next offset to read from log file */ - int szPage; /* Page size according to the log */ - u32 magic; /* Magic value read from WAL header */ - u32 version; /* Magic value read from WAL header */ - int isValid; /* True if this frame is valid */ - - /* Read in the WAL header. */ - rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); - if( rc!=SQLITE_OK ){ - goto recovery_error; - } - - /* If the database page size is not a power of two, or is greater than - ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid - ** data. Similarly, if the 'magic' value is invalid, ignore the whole - ** WAL file. - */ - magic = sqlite3Get4byte(&aBuf[0]); - szPage = sqlite3Get4byte(&aBuf[8]); - if( (magic&0xFFFFFFFE)!=WAL_MAGIC - || szPage&(szPage-1) - || szPage>SQLITE_MAX_PAGE_SIZE - || szPage<512 - ){ - goto finished; - } - pWal->hdr.bigEndCksum = (u8)(magic&0x00000001); - pWal->szPage = szPage; - pWal->nCkpt = sqlite3Get4byte(&aBuf[12]); - memcpy(&pWal->hdr.aSalt, &aBuf[16], 8); - - /* Verify that the WAL header checksum is correct */ - walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, - aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum - ); - if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24]) - || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[28]) - ){ - goto finished; - } - - /* Verify that the version number on the WAL format is one that - ** are able to understand */ - version = sqlite3Get4byte(&aBuf[4]); - if( version!=WAL_MAX_VERSION ){ - rc = SQLITE_CANTOPEN_BKPT; - goto finished; - } - - /* Malloc a buffer to read frames into. */ - szFrame = szPage + WAL_FRAME_HDRSIZE; - aFrame = (u8 *)sqlite3_malloc(szFrame); - if( !aFrame ){ - rc = SQLITE_NOMEM; - goto recovery_error; - } - aData = &aFrame[WAL_FRAME_HDRSIZE]; - - /* Read all frames from the log file. */ - iFrame = 0; - for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){ - u32 pgno; /* Database page number for frame */ - u32 nTruncate; /* dbsize field from frame header */ - - /* Read and decode the next log frame. */ - iFrame++; - rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); - if( rc!=SQLITE_OK ) break; - isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); - if( !isValid ) break; - rc = walIndexAppend(pWal, iFrame, pgno); - if( rc!=SQLITE_OK ) break; - - /* If nTruncate is non-zero, this is a commit record. */ - if( nTruncate ){ - pWal->hdr.mxFrame = iFrame; - pWal->hdr.nPage = nTruncate; - pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); - testcase( szPage<=32768 ); - testcase( szPage>=65536 ); - aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; - aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; - } - } - - sqlite3_free(aFrame); - } - -finished: - if( rc==SQLITE_OK ){ - volatile WalCkptInfo *pInfo; - int i; - pWal->hdr.aFrameCksum[0] = aFrameCksum[0]; - pWal->hdr.aFrameCksum[1] = aFrameCksum[1]; - walIndexWriteHdr(pWal); - - /* Reset the checkpoint-header. This is safe because this thread is - ** currently holding locks that exclude all other readers, writers and - ** checkpointers. - */ - pInfo = walCkptInfo(pWal); - pInfo->nBackfill = 0; - pInfo->aReadMark[0] = 0; - for(i=1; iaReadMark[i] = READMARK_NOT_USED; - if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame; - - /* If more than one frame was recovered from the log file, report an - ** event via sqlite3_log(). This is to help with identifying performance - ** problems caused by applications routinely shutting down without - ** checkpointing the log file. - */ - if( pWal->hdr.nPage ){ - sqlite3_log(SQLITE_OK, "Recovered %d frames from WAL file %s", - pWal->hdr.nPage, pWal->zWalName - ); - } - } - -recovery_error: - WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); - walUnlockExclusive(pWal, iLock, nLock); - return rc; -} - -/* -** Close an open wal-index. -*/ -static void walIndexClose(Wal *pWal, int isDelete){ - if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ - int i; - for(i=0; inWiData; i++){ - sqlite3_free((void *)pWal->apWiData[i]); - pWal->apWiData[i] = 0; - } - }else{ - sqlite3OsShmUnmap(pWal->pDbFd, isDelete); - } -} - -/* -** Open a connection to the WAL file zWalName. The database file must -** already be opened on connection pDbFd. The buffer that zWalName points -** to must remain valid for the lifetime of the returned Wal* handle. -** -** A SHARED lock should be held on the database file when this function -** is called. The purpose of this SHARED lock is to prevent any other -** client from unlinking the WAL or wal-index file. If another process -** were to do this just after this client opened one of these files, the -** system would be badly broken. -** -** If the log file is successfully opened, SQLITE_OK is returned and -** *ppWal is set to point to a new WAL handle. If an error occurs, -** an SQLite error code is returned and *ppWal is left unmodified. -*/ -SQLITE_PRIVATE int sqlite3WalOpen( - sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ - sqlite3_file *pDbFd, /* The open database file */ - const char *zWalName, /* Name of the WAL file */ - int bNoShm, /* True to run in heap-memory mode */ - i64 mxWalSize, /* Truncate WAL to this size on reset */ - Wal **ppWal /* OUT: Allocated Wal handle */ -){ - int rc; /* Return Code */ - Wal *pRet; /* Object to allocate and return */ - int flags; /* Flags passed to OsOpen() */ - - assert( zWalName && zWalName[0] ); - assert( pDbFd ); - - /* In the amalgamation, the os_unix.c and os_win.c source files come before - ** this source file. Verify that the #defines of the locking byte offsets - ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value. - */ -#ifdef WIN_SHM_BASE - assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET ); -#endif -#ifdef UNIX_SHM_BASE - assert( UNIX_SHM_BASE==WALINDEX_LOCK_OFFSET ); -#endif - - - /* Allocate an instance of struct Wal to return. */ - *ppWal = 0; - pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile); - if( !pRet ){ - return SQLITE_NOMEM; - } - - pRet->pVfs = pVfs; - pRet->pWalFd = (sqlite3_file *)&pRet[1]; - pRet->pDbFd = pDbFd; - pRet->readLock = -1; - pRet->mxWalSize = mxWalSize; - pRet->zWalName = zWalName; - pRet->syncHeader = 1; - pRet->padToSectorBoundary = 1; - pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE); - - /* Open file handle on the write-ahead log file. */ - flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); - rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); - if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ - pRet->readOnly = WAL_RDONLY; - } - - if( rc!=SQLITE_OK ){ - walIndexClose(pRet, 0); - sqlite3OsClose(pRet->pWalFd); - sqlite3_free(pRet); - }else{ - int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd); - if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; } - if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){ - pRet->padToSectorBoundary = 0; - } - *ppWal = pRet; - WALTRACE(("WAL%d: opened\n", pRet)); - } - return rc; -} - -/* -** Change the size to which the WAL file is trucated on each reset. -*/ -SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){ - if( pWal ) pWal->mxWalSize = iLimit; -} - -/* -** Find the smallest page number out of all pages held in the WAL that -** has not been returned by any prior invocation of this method on the -** same WalIterator object. Write into *piFrame the frame index where -** that page was last written into the WAL. Write into *piPage the page -** number. -** -** Return 0 on success. If there are no pages in the WAL with a page -** number larger than *piPage, then return 1. -*/ -static int walIteratorNext( - WalIterator *p, /* Iterator */ - u32 *piPage, /* OUT: The page number of the next page */ - u32 *piFrame /* OUT: Wal frame index of next page */ -){ - u32 iMin; /* Result pgno must be greater than iMin */ - u32 iRet = 0xFFFFFFFF; /* 0xffffffff is never a valid page number */ - int i; /* For looping through segments */ - - iMin = p->iPrior; - assert( iMin<0xffffffff ); - for(i=p->nSegment-1; i>=0; i--){ - struct WalSegment *pSegment = &p->aSegment[i]; - while( pSegment->iNextnEntry ){ - u32 iPg = pSegment->aPgno[pSegment->aIndex[pSegment->iNext]]; - if( iPg>iMin ){ - if( iPgiZero + pSegment->aIndex[pSegment->iNext]; - } - break; - } - pSegment->iNext++; - } - } - - *piPage = p->iPrior = iRet; - return (iRet==0xFFFFFFFF); -} - -/* -** This function merges two sorted lists into a single sorted list. -** -** aLeft[] and aRight[] are arrays of indices. The sort key is -** aContent[aLeft[]] and aContent[aRight[]]. Upon entry, the following -** is guaranteed for all J0 && nRight>0 ); - while( iRight=nRight || aContent[aLeft[iLeft]]=nLeft || aContent[aLeft[iLeft]]>dbpage ); - assert( iRight>=nRight || aContent[aRight[iRight]]>dbpage ); - } - - *paRight = aLeft; - *pnRight = iOut; - memcpy(aLeft, aTmp, sizeof(aTmp[0])*iOut); -} - -/* -** Sort the elements in list aList using aContent[] as the sort key. -** Remove elements with duplicate keys, preferring to keep the -** larger aList[] values. -** -** The aList[] entries are indices into aContent[]. The values in -** aList[] are to be sorted so that for all J0 ); - assert( HASHTABLE_NPAGE==(1<<(ArraySize(aSub)-1)) ); - - for(iList=0; iListaList && p->nList<=(1<aList==&aList[iList&~((2<aList, p->nList, &aMerge, &nMerge, aBuffer); - } - aSub[iSub].aList = aMerge; - aSub[iSub].nList = nMerge; - } - - for(iSub++; iSubnList<=(1<aList==&aList[nList&~((2<aList, p->nList, &aMerge, &nMerge, aBuffer); - } - } - assert( aMerge==aList ); - *pnList = nMerge; - -#ifdef SQLITE_DEBUG - { - int i; - for(i=1; i<*pnList; i++){ - assert( aContent[aList[i]] > aContent[aList[i-1]] ); - } - } -#endif -} - -/* -** Free an iterator allocated by walIteratorInit(). -*/ -static void walIteratorFree(WalIterator *p){ - sqlite3ScratchFree(p); -} - -/* -** Construct a WalInterator object that can be used to loop over all -** pages in the WAL in ascending order. The caller must hold the checkpoint -** lock. -** -** On success, make *pp point to the newly allocated WalInterator object -** return SQLITE_OK. Otherwise, return an error code. If this routine -** returns an error, the value of *pp is undefined. -** -** The calling routine should invoke walIteratorFree() to destroy the -** WalIterator object when it has finished with it. -*/ -static int walIteratorInit(Wal *pWal, WalIterator **pp){ - WalIterator *p; /* Return value */ - int nSegment; /* Number of segments to merge */ - u32 iLast; /* Last frame in log */ - int nByte; /* Number of bytes to allocate */ - int i; /* Iterator variable */ - ht_slot *aTmp; /* Temp space used by merge-sort */ - int rc = SQLITE_OK; /* Return Code */ - - /* This routine only runs while holding the checkpoint lock. And - ** it only runs if there is actually content in the log (mxFrame>0). - */ - assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); - iLast = pWal->hdr.mxFrame; - - /* Allocate space for the WalIterator object. */ - nSegment = walFramePage(iLast) + 1; - nByte = sizeof(WalIterator) - + (nSegment-1)*sizeof(struct WalSegment) - + iLast*sizeof(ht_slot); - p = (WalIterator *)sqlite3ScratchMalloc(nByte); - if( !p ){ - return SQLITE_NOMEM; - } - memset(p, 0, nByte); - p->nSegment = nSegment; - - /* Allocate temporary space used by the merge-sort routine. This block - ** of memory will be freed before this function returns. - */ - aTmp = (ht_slot *)sqlite3ScratchMalloc( - sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) - ); - if( !aTmp ){ - rc = SQLITE_NOMEM; - } - - for(i=0; rc==SQLITE_OK && iaSegment[p->nSegment])[iZero]; - iZero++; - - for(j=0; jaSegment[i].iZero = iZero; - p->aSegment[i].nEntry = nEntry; - p->aSegment[i].aIndex = aIndex; - p->aSegment[i].aPgno = (u32 *)aPgno; - } - } - sqlite3ScratchFree(aTmp); - - if( rc!=SQLITE_OK ){ - walIteratorFree(p); - } - *pp = p; - return rc; -} - -/* -** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and -** n. If the attempt fails and parameter xBusy is not NULL, then it is a -** busy-handler function. Invoke it and retry the lock until either the -** lock is successfully obtained or the busy-handler returns 0. -*/ -static int walBusyLock( - Wal *pWal, /* WAL connection */ - int (*xBusy)(void*), /* Function to call when busy */ - void *pBusyArg, /* Context argument for xBusyHandler */ - int lockIdx, /* Offset of first byte to lock */ - int n /* Number of bytes to lock */ -){ - int rc; - do { - rc = walLockExclusive(pWal, lockIdx, n); - }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); - return rc; -} - -/* -** The cache of the wal-index header must be valid to call this function. -** Return the page-size in bytes used by the database. -*/ -static int walPagesize(Wal *pWal){ - return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); -} - -/* -** Copy as much content as we can from the WAL back into the database file -** in response to an sqlite3_wal_checkpoint() request or the equivalent. -** -** The amount of information copies from WAL to database might be limited -** by active readers. This routine will never overwrite a database page -** that a concurrent reader might be using. -** -** All I/O barrier operations (a.k.a fsyncs) occur in this routine when -** SQLite is in WAL-mode in synchronous=NORMAL. That means that if -** checkpoints are always run by a background thread or background -** process, foreground threads will never block on a lengthy fsync call. -** -** Fsync is called on the WAL before writing content out of the WAL and -** into the database. This ensures that if the new content is persistent -** in the WAL and can be recovered following a power-loss or hard reset. -** -** Fsync is also called on the database file if (and only if) the entire -** WAL content is copied into the database file. This second fsync makes -** it safe to delete the WAL since the new content will persist in the -** database file. -** -** This routine uses and updates the nBackfill field of the wal-index header. -** This is the only routine tha will increase the value of nBackfill. -** (A WAL reset or recovery will revert nBackfill to zero, but not increase -** its value.) -** -** The caller must be holding sufficient locks to ensure that no other -** checkpoint is running (in any other thread or process) at the same -** time. -*/ -static int walCheckpoint( - Wal *pWal, /* Wal connection */ - int eMode, /* One of PASSIVE, FULL or RESTART */ - int (*xBusyCall)(void*), /* Function to call when busy */ - void *pBusyArg, /* Context argument for xBusyHandler */ - int sync_flags, /* Flags for OsSync() (or 0) */ - u8 *zBuf /* Temporary buffer to use */ -){ - int rc; /* Return code */ - int szPage; /* Database page-size */ - WalIterator *pIter = 0; /* Wal iterator context */ - u32 iDbpage = 0; /* Next database page to write */ - u32 iFrame = 0; /* Wal frame containing data for iDbpage */ - u32 mxSafeFrame; /* Max frame that can be backfilled */ - u32 mxPage; /* Max database page to write */ - int i; /* Loop counter */ - volatile WalCkptInfo *pInfo; /* The checkpoint status information */ - int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */ - - szPage = walPagesize(pWal); - testcase( szPage<=32768 ); - testcase( szPage>=65536 ); - pInfo = walCkptInfo(pWal); - if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK; - - /* Allocate the iterator */ - rc = walIteratorInit(pWal, &pIter); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pIter ); - - if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall; - - /* Compute in mxSafeFrame the index of the last frame of the WAL that is - ** safe to write into the database. Frames beyond mxSafeFrame might - ** overwrite database pages that are in use by active readers and thus - ** cannot be backfilled from the WAL. - */ - mxSafeFrame = pWal->hdr.mxFrame; - mxPage = pWal->hdr.nPage; - for(i=1; iaReadMark[i]; - if( mxSafeFrame>y ){ - assert( y<=pWal->hdr.mxFrame ); - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); - if( rc==SQLITE_OK ){ - pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - }else if( rc==SQLITE_BUSY ){ - mxSafeFrame = y; - xBusy = 0; - }else{ - goto walcheckpoint_out; - } - } - } - - if( pInfo->nBackfillnBackfill; - - /* Sync the WAL to disk */ - if( sync_flags ){ - rc = sqlite3OsSync(pWal->pWalFd, sync_flags); - } - - /* If the database file may grow as a result of this checkpoint, hint - ** about the eventual size of the db file to the VFS layer. - */ - if( rc==SQLITE_OK ){ - i64 nReq = ((i64)mxPage * szPage); - rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); - if( rc==SQLITE_OK && nSizepDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); - } - } - - /* Iterate through the contents of the WAL, copying data to the db file. */ - while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ - i64 iOffset; - assert( walFramePgno(pWal, iFrame)==iDbpage ); - if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue; - iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ - rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); - if( rc!=SQLITE_OK ) break; - iOffset = (iDbpage-1)*(i64)szPage; - testcase( IS_BIG_INT(iOffset) ); - rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); - if( rc!=SQLITE_OK ) break; - } - - /* If work was actually accomplished... */ - if( rc==SQLITE_OK ){ - if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ - i64 szDb = pWal->hdr.nPage*(i64)szPage; - testcase( IS_BIG_INT(szDb) ); - rc = sqlite3OsTruncate(pWal->pDbFd, szDb); - if( rc==SQLITE_OK && sync_flags ){ - rc = sqlite3OsSync(pWal->pDbFd, sync_flags); - } - } - if( rc==SQLITE_OK ){ - pInfo->nBackfill = mxSafeFrame; - } - } - - /* Release the reader lock held while backfilling */ - walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); - } - - if( rc==SQLITE_BUSY ){ - /* Reset the return code so as not to report a checkpoint failure - ** just because there are active readers. */ - rc = SQLITE_OK; - } - - /* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal - ** file has been copied into the database file, then block until all - ** readers have finished using the wal file. This ensures that the next - ** process to write to the database restarts the wal file. - */ - if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){ - assert( pWal->writeLock ); - if( pInfo->nBackfillhdr.mxFrame ){ - rc = SQLITE_BUSY; - }else if( eMode==SQLITE_CHECKPOINT_RESTART ){ - assert( mxSafeFrame==pWal->hdr.mxFrame ); - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); - if( rc==SQLITE_OK ){ - walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); - } - } - } - - walcheckpoint_out: - walIteratorFree(pIter); - return rc; -} - -/* -** If the WAL file is currently larger than nMax bytes in size, truncate -** it to exactly nMax bytes. If an error occurs while doing so, ignore it. -*/ -static void walLimitSize(Wal *pWal, i64 nMax){ - i64 sz; - int rx; - sqlite3BeginBenignMalloc(); - rx = sqlite3OsFileSize(pWal->pWalFd, &sz); - if( rx==SQLITE_OK && (sz > nMax ) ){ - rx = sqlite3OsTruncate(pWal->pWalFd, nMax); - } - sqlite3EndBenignMalloc(); - if( rx ){ - sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName); - } -} - -/* -** Close a connection to a log file. -*/ -SQLITE_PRIVATE int sqlite3WalClose( - Wal *pWal, /* Wal to close */ - int sync_flags, /* Flags to pass to OsSync() (or 0) */ - int nBuf, - u8 *zBuf /* Buffer of at least nBuf bytes */ -){ - int rc = SQLITE_OK; - if( pWal ){ - int isDelete = 0; /* True to unlink wal and wal-index files */ - - /* If an EXCLUSIVE lock can be obtained on the database file (using the - ** ordinary, rollback-mode locking methods, this guarantees that the - ** connection associated with this log file is the only connection to - ** the database. In this case checkpoint the database and unlink both - ** the wal and wal-index files. - ** - ** The EXCLUSIVE lock is not released before returning. - */ - rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); - if( rc==SQLITE_OK ){ - if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ - pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; - } - rc = sqlite3WalCheckpoint( - pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 - ); - if( rc==SQLITE_OK ){ - int bPersist = -1; - sqlite3OsFileControlHint( - pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist - ); - if( bPersist!=1 ){ - /* Try to delete the WAL file if the checkpoint completed and - ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal - ** mode (!bPersist) */ - isDelete = 1; - }else if( pWal->mxWalSize>=0 ){ - /* Try to truncate the WAL file to zero bytes if the checkpoint - ** completed and fsynced (rc==SQLITE_OK) and we are in persistent - ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a - ** non-negative value (pWal->mxWalSize>=0). Note that we truncate - ** to zero bytes as truncating to the journal_size_limit might - ** leave a corrupt WAL file on disk. */ - walLimitSize(pWal, 0); - } - } - } - - walIndexClose(pWal, isDelete); - sqlite3OsClose(pWal->pWalFd); - if( isDelete ){ - sqlite3BeginBenignMalloc(); - sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0); - sqlite3EndBenignMalloc(); - } - WALTRACE(("WAL%p: closed\n", pWal)); - sqlite3_free((void *)pWal->apWiData); - sqlite3_free(pWal); - } - return rc; -} - -/* -** Try to read the wal-index header. Return 0 on success and 1 if -** there is a problem. -** -** The wal-index is in shared memory. Another thread or process might -** be writing the header at the same time this procedure is trying to -** read it, which might result in inconsistency. A dirty read is detected -** by verifying that both copies of the header are the same and also by -** a checksum on the header. -** -** If and only if the read is consistent and the header is different from -** pWal->hdr, then pWal->hdr is updated to the content of the new header -** and *pChanged is set to 1. -** -** If the checksum cannot be verified return non-zero. If the header -** is read successfully and the checksum verified, return zero. -*/ -static int walIndexTryHdr(Wal *pWal, int *pChanged){ - u32 aCksum[2]; /* Checksum on the header content */ - WalIndexHdr h1, h2; /* Two copies of the header content */ - WalIndexHdr volatile *aHdr; /* Header in shared memory */ - - /* The first page of the wal-index must be mapped at this point. */ - assert( pWal->nWiData>0 && pWal->apWiData[0] ); - - /* Read the header. This might happen concurrently with a write to the - ** same area of shared memory on a different CPU in a SMP, - ** meaning it is possible that an inconsistent snapshot is read - ** from the file. If this happens, return non-zero. - ** - ** There are two copies of the header at the beginning of the wal-index. - ** When reading, read [0] first then [1]. Writes are in the reverse order. - ** Memory barriers are used to prevent the compiler or the hardware from - ** reordering the reads and writes. - */ - aHdr = walIndexHdr(pWal); - memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); - walShmBarrier(pWal); - memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); - - if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ - return 1; /* Dirty read */ - } - if( h1.isInit==0 ){ - return 1; /* Malformed header - probably all zeros */ - } - walChecksumBytes(1, (u8*)&h1, sizeof(h1)-sizeof(h1.aCksum), 0, aCksum); - if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){ - return 1; /* Checksum does not match */ - } - - if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){ - *pChanged = 1; - memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr)); - pWal->szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); - testcase( pWal->szPage<=32768 ); - testcase( pWal->szPage>=65536 ); - } - - /* The header was successfully read. Return zero. */ - return 0; -} - -/* -** Read the wal-index header from the wal-index and into pWal->hdr. -** If the wal-header appears to be corrupt, try to reconstruct the -** wal-index from the WAL before returning. -** -** Set *pChanged to 1 if the wal-index header value in pWal->hdr is -** changed by this opertion. If pWal->hdr is unchanged, set *pChanged -** to 0. -** -** If the wal-index header is successfully read, return SQLITE_OK. -** Otherwise an SQLite error code. -*/ -static int walIndexReadHdr(Wal *pWal, int *pChanged){ - int rc; /* Return code */ - int badHdr; /* True if a header read failed */ - volatile u32 *page0; /* Chunk of wal-index containing header */ - - /* Ensure that page 0 of the wal-index (the page that contains the - ** wal-index header) is mapped. Return early if an error occurs here. - */ - assert( pChanged ); - rc = walIndexPage(pWal, 0, &page0); - if( rc!=SQLITE_OK ){ - return rc; - }; - assert( page0 || pWal->writeLock==0 ); - - /* If the first page of the wal-index has been mapped, try to read the - ** wal-index header immediately, without holding any lock. This usually - ** works, but may fail if the wal-index header is corrupt or currently - ** being modified by another thread or process. - */ - badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1); - - /* If the first attempt failed, it might have been due to a race - ** with a writer. So get a WRITE lock and try again. - */ - assert( badHdr==0 || pWal->writeLock==0 ); - if( badHdr ){ - if( pWal->readOnly & WAL_SHM_RDONLY ){ - if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ - walUnlockShared(pWal, WAL_WRITE_LOCK); - rc = SQLITE_READONLY_RECOVERY; - } - }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ - pWal->writeLock = 1; - if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ - badHdr = walIndexTryHdr(pWal, pChanged); - if( badHdr ){ - /* If the wal-index header is still malformed even while holding - ** a WRITE lock, it can only mean that the header is corrupted and - ** needs to be reconstructed. So run recovery to do exactly that. - */ - rc = walIndexRecover(pWal); - *pChanged = 1; - } - } - pWal->writeLock = 0; - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); - } - } - - /* If the header is read successfully, check the version number to make - ** sure the wal-index was not constructed with some future format that - ** this version of SQLite cannot understand. - */ - if( badHdr==0 && pWal->hdr.iVersion!=WALINDEX_MAX_VERSION ){ - rc = SQLITE_CANTOPEN_BKPT; - } - - return rc; -} - -/* -** This is the value that walTryBeginRead returns when it needs to -** be retried. -*/ -#define WAL_RETRY (-1) - -/* -** Attempt to start a read transaction. This might fail due to a race or -** other transient condition. When that happens, it returns WAL_RETRY to -** indicate to the caller that it is safe to retry immediately. -** -** On success return SQLITE_OK. On a permanent failure (such an -** I/O error or an SQLITE_BUSY because another process is running -** recovery) return a positive error code. -** -** The useWal parameter is true to force the use of the WAL and disable -** the case where the WAL is bypassed because it has been completely -** checkpointed. If useWal==0 then this routine calls walIndexReadHdr() -** to make a copy of the wal-index header into pWal->hdr. If the -** wal-index header has changed, *pChanged is set to 1 (as an indication -** to the caller that the local paget cache is obsolete and needs to be -** flushed.) When useWal==1, the wal-index header is assumed to already -** be loaded and the pChanged parameter is unused. -** -** The caller must set the cnt parameter to the number of prior calls to -** this routine during the current read attempt that returned WAL_RETRY. -** This routine will start taking more aggressive measures to clear the -** race conditions after multiple WAL_RETRY returns, and after an excessive -** number of errors will ultimately return SQLITE_PROTOCOL. The -** SQLITE_PROTOCOL return indicates that some other process has gone rogue -** and is not honoring the locking protocol. There is a vanishingly small -** chance that SQLITE_PROTOCOL could be returned because of a run of really -** bad luck when there is lots of contention for the wal-index, but that -** possibility is so small that it can be safely neglected, we believe. -** -** On success, this routine obtains a read lock on -** WAL_READ_LOCK(pWal->readLock). The pWal->readLock integer is -** in the range 0 <= pWal->readLock < WAL_NREADER. If pWal->readLock==(-1) -** that means the Wal does not hold any read lock. The reader must not -** access any database page that is modified by a WAL frame up to and -** including frame number aReadMark[pWal->readLock]. The reader will -** use WAL frames up to and including pWal->hdr.mxFrame if pWal->readLock>0 -** Or if pWal->readLock==0, then the reader will ignore the WAL -** completely and get all content directly from the database file. -** If the useWal parameter is 1 then the WAL will never be ignored and -** this routine will always set pWal->readLock>0 on success. -** When the read transaction is completed, the caller must release the -** lock on WAL_READ_LOCK(pWal->readLock) and set pWal->readLock to -1. -** -** This routine uses the nBackfill and aReadMark[] fields of the header -** to select a particular WAL_READ_LOCK() that strives to let the -** checkpoint process do as much work as possible. This routine might -** update values of the aReadMark[] array in the header, but if it does -** so it takes care to hold an exclusive lock on the corresponding -** WAL_READ_LOCK() while changing values. -*/ -static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ - volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ - u32 mxReadMark; /* Largest aReadMark[] value */ - int mxI; /* Index of largest aReadMark[] value */ - int i; /* Loop counter */ - int rc = SQLITE_OK; /* Return code */ - - assert( pWal->readLock<0 ); /* Not currently locked */ - - /* Take steps to avoid spinning forever if there is a protocol error. - ** - ** Circumstances that cause a RETRY should only last for the briefest - ** instances of time. No I/O or other system calls are done while the - ** locks are held, so the locks should not be held for very long. But - ** if we are unlucky, another process that is holding a lock might get - ** paged out or take a page-fault that is time-consuming to resolve, - ** during the few nanoseconds that it is holding the lock. In that case, - ** it might take longer than normal for the lock to free. - ** - ** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few - ** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this - ** is more of a scheduler yield than an actual delay. But on the 10th - ** an subsequent retries, the delays start becoming longer and longer, - ** so that on the 100th (and last) RETRY we delay for 21 milliseconds. - ** The total delay time before giving up is less than 1 second. - */ - if( cnt>5 ){ - int nDelay = 1; /* Pause time in microseconds */ - if( cnt>100 ){ - VVA_ONLY( pWal->lockError = 1; ) - return SQLITE_PROTOCOL; - } - if( cnt>=10 ) nDelay = (cnt-9)*238; /* Max delay 21ms. Total delay 996ms */ - sqlite3OsSleep(pWal->pVfs, nDelay); - } - - if( !useWal ){ - rc = walIndexReadHdr(pWal, pChanged); - if( rc==SQLITE_BUSY ){ - /* If there is not a recovery running in another thread or process - ** then convert BUSY errors to WAL_RETRY. If recovery is known to - ** be running, convert BUSY to BUSY_RECOVERY. There is a race here - ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY - ** would be technically correct. But the race is benign since with - ** WAL_RETRY this routine will be called again and will probably be - ** right on the second iteration. - */ - if( pWal->apWiData[0]==0 ){ - /* This branch is taken when the xShmMap() method returns SQLITE_BUSY. - ** We assume this is a transient condition, so return WAL_RETRY. The - ** xShmMap() implementation used by the default unix and win32 VFS - ** modules may return SQLITE_BUSY due to a race condition in the - ** code that determines whether or not the shared-memory region - ** must be zeroed before the requested page is returned. - */ - rc = WAL_RETRY; - }else if( SQLITE_OK==(rc = walLockShared(pWal, WAL_RECOVER_LOCK)) ){ - walUnlockShared(pWal, WAL_RECOVER_LOCK); - rc = WAL_RETRY; - }else if( rc==SQLITE_BUSY ){ - rc = SQLITE_BUSY_RECOVERY; - } - } - if( rc!=SQLITE_OK ){ - return rc; - } - } - - pInfo = walCkptInfo(pWal); - if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){ - /* The WAL has been completely backfilled (or it is empty). - ** and can be safely ignored. - */ - rc = walLockShared(pWal, WAL_READ_LOCK(0)); - walShmBarrier(pWal); - if( rc==SQLITE_OK ){ - if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ - /* It is not safe to allow the reader to continue here if frames - ** may have been appended to the log before READ_LOCK(0) was obtained. - ** When holding READ_LOCK(0), the reader ignores the entire log file, - ** which implies that the database file contains a trustworthy - ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from - ** happening, this is usually correct. - ** - ** However, if frames have been appended to the log (or if the log - ** is wrapped and written for that matter) before the READ_LOCK(0) - ** is obtained, that is not necessarily true. A checkpointer may - ** have started to backfill the appended frames but crashed before - ** it finished. Leaving a corrupt image in the database file. - */ - walUnlockShared(pWal, WAL_READ_LOCK(0)); - return WAL_RETRY; - } - pWal->readLock = 0; - return SQLITE_OK; - }else if( rc!=SQLITE_BUSY ){ - return rc; - } - } - - /* If we get this far, it means that the reader will want to use - ** the WAL to get at content from recent commits. The job now is - ** to select one of the aReadMark[] entries that is closest to - ** but not exceeding pWal->hdr.mxFrame and lock that entry. - */ - mxReadMark = 0; - mxI = 0; - for(i=1; iaReadMark[i]; - if( mxReadMark<=thisMark && thisMark<=pWal->hdr.mxFrame ){ - assert( thisMark!=READMARK_NOT_USED ); - mxReadMark = thisMark; - mxI = i; - } - } - /* There was once an "if" here. The extra "{" is to preserve indentation. */ - { - if( (pWal->readOnly & WAL_SHM_RDONLY)==0 - && (mxReadMarkhdr.mxFrame || mxI==0) - ){ - for(i=1; iaReadMark[i] = pWal->hdr.mxFrame; - mxI = i; - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - break; - }else if( rc!=SQLITE_BUSY ){ - return rc; - } - } - } - if( mxI==0 ){ - assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); - return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK; - } - - rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); - if( rc ){ - return rc==SQLITE_BUSY ? WAL_RETRY : rc; - } - /* Now that the read-lock has been obtained, check that neither the - ** value in the aReadMark[] array or the contents of the wal-index - ** header have changed. - ** - ** It is necessary to check that the wal-index header did not change - ** between the time it was read and when the shared-lock was obtained - ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility - ** that the log file may have been wrapped by a writer, or that frames - ** that occur later in the log than pWal->hdr.mxFrame may have been - ** copied into the database by a checkpointer. If either of these things - ** happened, then reading the database with the current value of - ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry - ** instead. - ** - ** This does not guarantee that the copy of the wal-index header is up to - ** date before proceeding. That would not be possible without somehow - ** blocking writers. It only guarantees that a dangerous checkpoint or - ** log-wrap (either of which would require an exclusive lock on - ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid. - */ - walShmBarrier(pWal); - if( pInfo->aReadMark[mxI]!=mxReadMark - || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) - ){ - walUnlockShared(pWal, WAL_READ_LOCK(mxI)); - return WAL_RETRY; - }else{ - assert( mxReadMark<=pWal->hdr.mxFrame ); - pWal->readLock = (i16)mxI; - } - } - return rc; -} - -/* -** Begin a read transaction on the database. -** -** This routine used to be called sqlite3OpenSnapshot() and with good reason: -** it takes a snapshot of the state of the WAL and wal-index for the current -** instant in time. The current thread will continue to use this snapshot. -** Other threads might append new content to the WAL and wal-index but -** that extra content is ignored by the current thread. -** -** If the database contents have changes since the previous read -** transaction, then *pChanged is set to 1 before returning. The -** Pager layer will use this to know that is cache is stale and -** needs to be flushed. -*/ -SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ - int rc; /* Return code */ - int cnt = 0; /* Number of TryBeginRead attempts */ - - do{ - rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); - }while( rc==WAL_RETRY ); - testcase( (rc&0xff)==SQLITE_BUSY ); - testcase( (rc&0xff)==SQLITE_IOERR ); - testcase( rc==SQLITE_PROTOCOL ); - testcase( rc==SQLITE_OK ); - return rc; -} - -/* -** Finish with a read transaction. All this does is release the -** read-lock. -*/ -SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ - sqlite3WalEndWriteTransaction(pWal); - if( pWal->readLock>=0 ){ - walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); - pWal->readLock = -1; - } -} - -/* -** Read a page from the WAL, if it is present in the WAL and if the -** current read transaction is configured to use the WAL. -** -** The *pInWal is set to 1 if the requested page is in the WAL and -** has been loaded. Or *pInWal is set to 0 if the page was not in -** the WAL and needs to be read out of the database. -*/ -SQLITE_PRIVATE int sqlite3WalRead( - Wal *pWal, /* WAL handle */ - Pgno pgno, /* Database page number to read data for */ - int *pInWal, /* OUT: True if data is read from WAL */ - int nOut, /* Size of buffer pOut in bytes */ - u8 *pOut /* Buffer to write page data to */ -){ - u32 iRead = 0; /* If !=0, WAL frame to return data from */ - u32 iLast = pWal->hdr.mxFrame; /* Last page in WAL for this reader */ - int iHash; /* Used to loop through N hash tables */ - - /* This routine is only be called from within a read transaction. */ - assert( pWal->readLock>=0 || pWal->lockError ); - - /* If the "last page" field of the wal-index header snapshot is 0, then - ** no data will be read from the wal under any circumstances. Return early - ** in this case as an optimization. Likewise, if pWal->readLock==0, - ** then the WAL is ignored by the reader so return early, as if the - ** WAL were empty. - */ - if( iLast==0 || pWal->readLock==0 ){ - *pInWal = 0; - return SQLITE_OK; - } - - /* Search the hash table or tables for an entry matching page number - ** pgno. Each iteration of the following for() loop searches one - ** hash table (each hash table indexes up to HASHTABLE_NPAGE frames). - ** - ** This code might run concurrently to the code in walIndexAppend() - ** that adds entries to the wal-index (and possibly to this hash - ** table). This means the value just read from the hash - ** slot (aHash[iKey]) may have been added before or after the - ** current read transaction was opened. Values added after the - ** read transaction was opened may have been written incorrectly - - ** i.e. these slots may contain garbage data. However, we assume - ** that any slots written before the current read transaction was - ** opened remain unmodified. - ** - ** For the reasons above, the if(...) condition featured in the inner - ** loop of the following block is more stringent that would be required - ** if we had exclusive access to the hash-table: - ** - ** (aPgno[iFrame]==pgno): - ** This condition filters out normal hash-table collisions. - ** - ** (iFrame<=iLast): - ** This condition filters out entries that were added to the hash - ** table after the current read-transaction had started. - */ - for(iHash=walFramePage(iLast); iHash>=0 && iRead==0; iHash--){ - volatile ht_slot *aHash; /* Pointer to hash table */ - volatile u32 *aPgno; /* Pointer to array of page numbers */ - u32 iZero; /* Frame number corresponding to aPgno[0] */ - int iKey; /* Hash slot index */ - int nCollide; /* Number of hash collisions remaining */ - int rc; /* Error code */ - - rc = walHashGet(pWal, iHash, &aHash, &aPgno, &iZero); - if( rc!=SQLITE_OK ){ - return rc; - } - nCollide = HASHTABLE_NSLOT; - for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){ - u32 iFrame = aHash[iKey] + iZero; - if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){ - /* assert( iFrame>iRead ); -- not true if there is corruption */ - iRead = iFrame; - } - if( (nCollide--)==0 ){ - return SQLITE_CORRUPT_BKPT; - } - } - } - -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT - /* If expensive assert() statements are available, do a linear search - ** of the wal-index file content. Make sure the results agree with the - ** result obtained using the hash indexes above. */ - { - u32 iRead2 = 0; - u32 iTest; - for(iTest=iLast; iTest>0; iTest--){ - if( walFramePgno(pWal, iTest)==pgno ){ - iRead2 = iTest; - break; - } - } - assert( iRead==iRead2 ); - } -#endif - - /* If iRead is non-zero, then it is the log frame number that contains the - ** required page. Read and return data from the log file. - */ - if( iRead ){ - int sz; - i64 iOffset; - sz = pWal->hdr.szPage; - sz = (sz&0xfe00) + ((sz&0x0001)<<16); - testcase( sz<=32768 ); - testcase( sz>=65536 ); - iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE; - *pInWal = 1; - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ - return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset); - } - - *pInWal = 0; - return SQLITE_OK; -} - - -/* -** Return the size of the database in pages (or zero, if unknown). -*/ -SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal){ - if( pWal && ALWAYS(pWal->readLock>=0) ){ - return pWal->hdr.nPage; - } - return 0; -} - - -/* -** This function starts a write transaction on the WAL. -** -** A read transaction must have already been started by a prior call -** to sqlite3WalBeginReadTransaction(). -** -** If another thread or process has written into the database since -** the read transaction was started, then it is not possible for this -** thread to write as doing so would cause a fork. So this routine -** returns SQLITE_BUSY in that case and no write transaction is started. -** -** There can only be a single writer active at a time. -*/ -SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ - int rc; - - /* Cannot start a write transaction without first holding a read - ** transaction. */ - assert( pWal->readLock>=0 ); - - if( pWal->readOnly ){ - return SQLITE_READONLY; - } - - /* Only one writer allowed at a time. Get the write lock. Return - ** SQLITE_BUSY if unable. - */ - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); - if( rc ){ - return rc; - } - pWal->writeLock = 1; - - /* If another connection has written to the database file since the - ** time the read transaction on this connection was started, then - ** the write is disallowed. - */ - if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); - pWal->writeLock = 0; - rc = SQLITE_BUSY; - } - - return rc; -} - -/* -** End a write transaction. The commit has already been done. This -** routine merely releases the lock. -*/ -SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){ - if( pWal->writeLock ){ - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); - pWal->writeLock = 0; - pWal->truncateOnCommit = 0; - } - return SQLITE_OK; -} - -/* -** If any data has been written (but not committed) to the log file, this -** function moves the write-pointer back to the start of the transaction. -** -** Additionally, the callback function is invoked for each frame written -** to the WAL since the start of the transaction. If the callback returns -** other than SQLITE_OK, it is not invoked again and the error code is -** returned to the caller. -** -** Otherwise, if the callback function does not return an error, this -** function returns SQLITE_OK. -*/ -SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ - int rc = SQLITE_OK; - if( ALWAYS(pWal->writeLock) ){ - Pgno iMax = pWal->hdr.mxFrame; - Pgno iFrame; - - /* Restore the clients cache of the wal-index header to the state it - ** was in before the client began writing to the database. - */ - memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); - - for(iFrame=pWal->hdr.mxFrame+1; - ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; - iFrame++ - ){ - /* This call cannot fail. Unless the page for which the page number - ** is passed as the second argument is (a) in the cache and - ** (b) has an outstanding reference, then xUndo is either a no-op - ** (if (a) is false) or simply expels the page from the cache (if (b) - ** is false). - ** - ** If the upper layer is doing a rollback, it is guaranteed that there - ** are no outstanding references to any page other than page 1. And - ** page 1 is never written to the log until the transaction is - ** committed. As a result, the call to xUndo may not fail. - */ - assert( walFramePgno(pWal, iFrame)!=1 ); - rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); - } - if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal); - } - assert( rc==SQLITE_OK ); - return rc; -} - -/* -** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 -** values. This function populates the array with values required to -** "rollback" the write position of the WAL handle back to the current -** point in the event of a savepoint rollback (via WalSavepointUndo()). -*/ -SQLITE_PRIVATE void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){ - assert( pWal->writeLock ); - aWalData[0] = pWal->hdr.mxFrame; - aWalData[1] = pWal->hdr.aFrameCksum[0]; - aWalData[2] = pWal->hdr.aFrameCksum[1]; - aWalData[3] = pWal->nCkpt; -} - -/* -** Move the write position of the WAL back to the point identified by -** the values in the aWalData[] array. aWalData must point to an array -** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated -** by a call to WalSavepoint(). -*/ -SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){ - int rc = SQLITE_OK; - - assert( pWal->writeLock ); - assert( aWalData[3]!=pWal->nCkpt || aWalData[0]<=pWal->hdr.mxFrame ); - - if( aWalData[3]!=pWal->nCkpt ){ - /* This savepoint was opened immediately after the write-transaction - ** was started. Right after that, the writer decided to wrap around - ** to the start of the log. Update the savepoint values to match. - */ - aWalData[0] = 0; - aWalData[3] = pWal->nCkpt; - } - - if( aWalData[0]hdr.mxFrame ){ - pWal->hdr.mxFrame = aWalData[0]; - pWal->hdr.aFrameCksum[0] = aWalData[1]; - pWal->hdr.aFrameCksum[1] = aWalData[2]; - walCleanupHash(pWal); - } - - return rc; -} - - -/* -** This function is called just before writing a set of frames to the log -** file (see sqlite3WalFrames()). It checks to see if, instead of appending -** to the current log file, it is possible to overwrite the start of the -** existing log file with the new frames (i.e. "reset" the log). If so, -** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left -** unchanged. -** -** SQLITE_OK is returned if no error is encountered (regardless of whether -** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned -** if an error occurs. -*/ -static int walRestartLog(Wal *pWal){ - int rc = SQLITE_OK; - int cnt; - - if( pWal->readLock==0 ){ - volatile WalCkptInfo *pInfo = walCkptInfo(pWal); - assert( pInfo->nBackfill==pWal->hdr.mxFrame ); - if( pInfo->nBackfill>0 ){ - u32 salt1; - sqlite3_randomness(4, &salt1); - rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); - if( rc==SQLITE_OK ){ - /* If all readers are using WAL_READ_LOCK(0) (in other words if no - ** readers are currently using the WAL), then the transactions - ** frames will overwrite the start of the existing log. Update the - ** wal-index header to reflect this. - ** - ** In theory it would be Ok to update the cache of the header only - ** at this point. But updating the actual wal-index header is also - ** safe and means there is no special case for sqlite3WalUndo() - ** to handle if this transaction is rolled back. - */ - int i; /* Loop counter */ - u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ - - pWal->nCkpt++; - pWal->hdr.mxFrame = 0; - sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); - aSalt[1] = salt1; - walIndexWriteHdr(pWal); - pInfo->nBackfill = 0; - pInfo->aReadMark[1] = 0; - for(i=2; iaReadMark[i] = READMARK_NOT_USED; - assert( pInfo->aReadMark[0]==0 ); - walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); - }else if( rc!=SQLITE_BUSY ){ - return rc; - } - } - walUnlockShared(pWal, WAL_READ_LOCK(0)); - pWal->readLock = -1; - cnt = 0; - do{ - int notUsed; - rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); - }while( rc==WAL_RETRY ); - assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ - testcase( (rc&0xff)==SQLITE_IOERR ); - testcase( rc==SQLITE_PROTOCOL ); - testcase( rc==SQLITE_OK ); - } - return rc; -} - -/* -** Information about the current state of the WAL file and where -** the next fsync should occur - passed from sqlite3WalFrames() into -** walWriteToLog(). -*/ -typedef struct WalWriter { - Wal *pWal; /* The complete WAL information */ - sqlite3_file *pFd; /* The WAL file to which we write */ - sqlite3_int64 iSyncPoint; /* Fsync at this offset */ - int syncFlags; /* Flags for the fsync */ - int szPage; /* Size of one page */ -} WalWriter; - -/* -** Write iAmt bytes of content into the WAL file beginning at iOffset. -** Do a sync when crossing the p->iSyncPoint boundary. -** -** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt, -** first write the part before iSyncPoint, then sync, then write the -** rest. -*/ -static int walWriteToLog( - WalWriter *p, /* WAL to write to */ - void *pContent, /* Content to be written */ - int iAmt, /* Number of bytes to write */ - sqlite3_int64 iOffset /* Start writing at this offset */ -){ - int rc; - if( iOffsetiSyncPoint && iOffset+iAmt>=p->iSyncPoint ){ - int iFirstAmt = (int)(p->iSyncPoint - iOffset); - rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset); - if( rc ) return rc; - iOffset += iFirstAmt; - iAmt -= iFirstAmt; - pContent = (void*)(iFirstAmt + (char*)pContent); - assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) ); - rc = sqlite3OsSync(p->pFd, p->syncFlags); - if( iAmt==0 || rc ) return rc; - } - rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset); - return rc; -} - -/* -** Write out a single frame of the WAL -*/ -static int walWriteOneFrame( - WalWriter *p, /* Where to write the frame */ - PgHdr *pPage, /* The page of the frame to be written */ - int nTruncate, /* The commit flag. Usually 0. >0 for commit */ - sqlite3_int64 iOffset /* Byte offset at which to write */ -){ - int rc; /* Result code from subfunctions */ - void *pData; /* Data actually written */ - u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ -#if defined(SQLITE_HAS_CODEC) - if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM; -#else - pData = pPage->pData; -#endif - walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame); - rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset); - if( rc ) return rc; - /* Write the page data */ - rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame)); - return rc; -} - -/* -** Write a set of frames to the log. The caller must hold the write-lock -** on the log file (obtained using sqlite3WalBeginWriteTransaction()). -*/ -SQLITE_PRIVATE int sqlite3WalFrames( - Wal *pWal, /* Wal handle to write to */ - int szPage, /* Database page-size in bytes */ - PgHdr *pList, /* List of dirty pages to write */ - Pgno nTruncate, /* Database size after this commit */ - int isCommit, /* True if this is a commit */ - int sync_flags /* Flags to pass to OsSync() (or 0) */ -){ - int rc; /* Used to catch return codes */ - u32 iFrame; /* Next frame address */ - PgHdr *p; /* Iterator to run through pList with. */ - PgHdr *pLast = 0; /* Last frame in list */ - int nExtra = 0; /* Number of extra copies of last page */ - int szFrame; /* The size of a single frame */ - i64 iOffset; /* Next byte to write in WAL file */ - WalWriter w; /* The writer */ - - assert( pList ); - assert( pWal->writeLock ); - - /* If this frame set completes a transaction, then nTruncate>0. If - ** nTruncate==0 then this frame set does not complete the transaction. */ - assert( (isCommit!=0)==(nTruncate!=0) ); - -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) - { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){} - WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n", - pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill")); - } -#endif - - /* See if it is possible to write these frames into the start of the - ** log file, instead of appending to it at pWal->hdr.mxFrame. - */ - if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){ - return rc; - } - - /* If this is the first frame written into the log, write the WAL - ** header to the start of the WAL file. See comments at the top of - ** this source file for a description of the WAL header format. - */ - iFrame = pWal->hdr.mxFrame; - if( iFrame==0 ){ - u8 aWalHdr[WAL_HDRSIZE]; /* Buffer to assemble wal-header in */ - u32 aCksum[2]; /* Checksum for wal-header */ - - sqlite3Put4byte(&aWalHdr[0], (WAL_MAGIC | SQLITE_BIGENDIAN)); - sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION); - sqlite3Put4byte(&aWalHdr[8], szPage); - sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt); - if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt); - memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8); - walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum); - sqlite3Put4byte(&aWalHdr[24], aCksum[0]); - sqlite3Put4byte(&aWalHdr[28], aCksum[1]); - - pWal->szPage = szPage; - pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN; - pWal->hdr.aFrameCksum[0] = aCksum[0]; - pWal->hdr.aFrameCksum[1] = aCksum[1]; - pWal->truncateOnCommit = 1; - - rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); - WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); - if( rc!=SQLITE_OK ){ - return rc; - } - - /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless - ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise - ** an out-of-order write following a WAL restart could result in - ** database corruption. See the ticket: - ** - ** http://localhost:591/sqlite/info/ff5be73dee - */ - if( pWal->syncHeader && sync_flags ){ - rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK); - if( rc ) return rc; - } - } - assert( (int)pWal->szPage==szPage ); - - /* Setup information needed to write frames into the WAL */ - w.pWal = pWal; - w.pFd = pWal->pWalFd; - w.iSyncPoint = 0; - w.syncFlags = sync_flags; - w.szPage = szPage; - iOffset = walFrameOffset(iFrame+1, szPage); - szFrame = szPage + WAL_FRAME_HDRSIZE; - - /* Write all frames into the log file exactly once */ - for(p=pList; p; p=p->pDirty){ - int nDbSize; /* 0 normally. Positive == commit flag */ - iFrame++; - assert( iOffset==walFrameOffset(iFrame, szPage) ); - nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0; - rc = walWriteOneFrame(&w, p, nDbSize, iOffset); - if( rc ) return rc; - pLast = p; - iOffset += szFrame; - } - - /* If this is the end of a transaction, then we might need to pad - ** the transaction and/or sync the WAL file. - ** - ** Padding and syncing only occur if this set of frames complete a - ** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL - ** or synchonous==OFF, then no padding or syncing are needed. - ** - ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not - ** needed and only the sync is done. If padding is needed, then the - ** final frame is repeated (with its commit mark) until the next sector - ** boundary is crossed. Only the part of the WAL prior to the last - ** sector boundary is synced; the part of the last frame that extends - ** past the sector boundary is written after the sync. - */ - if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){ - if( pWal->padToSectorBoundary ){ - int sectorSize = sqlite3SectorSize(pWal->pWalFd); - w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize; - while( iOffsettruncateOnCommit && pWal->mxWalSize>=0 ){ - i64 sz = pWal->mxWalSize; - if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){ - sz = walFrameOffset(iFrame+nExtra+1, szPage); - } - walLimitSize(pWal, sz); - pWal->truncateOnCommit = 0; - } - - /* Append data to the wal-index. It is not necessary to lock the - ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index - ** guarantees that there are no other writers, and no data that may - ** be in use by existing readers is being overwritten. - */ - iFrame = pWal->hdr.mxFrame; - for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){ - iFrame++; - rc = walIndexAppend(pWal, iFrame, p->pgno); - } - while( rc==SQLITE_OK && nExtra>0 ){ - iFrame++; - nExtra--; - rc = walIndexAppend(pWal, iFrame, pLast->pgno); - } - - if( rc==SQLITE_OK ){ - /* Update the private copy of the header. */ - pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); - testcase( szPage<=32768 ); - testcase( szPage>=65536 ); - pWal->hdr.mxFrame = iFrame; - if( isCommit ){ - pWal->hdr.iChange++; - pWal->hdr.nPage = nTruncate; - } - /* If this is a commit, update the wal-index header too. */ - if( isCommit ){ - walIndexWriteHdr(pWal); - pWal->iCallback = iFrame; - } - } - - WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok")); - return rc; -} - -/* -** This routine is called to implement sqlite3_wal_checkpoint() and -** related interfaces. -** -** Obtain a CHECKPOINT lock and then backfill as much information as -** we can from WAL into the database. -** -** If parameter xBusy is not NULL, it is a pointer to a busy-handler -** callback. In this case this function runs a blocking checkpoint. -*/ -SQLITE_PRIVATE int sqlite3WalCheckpoint( - Wal *pWal, /* Wal connection */ - int eMode, /* PASSIVE, FULL or RESTART */ - int (*xBusy)(void*), /* Function to call when busy */ - void *pBusyArg, /* Context argument for xBusyHandler */ - int sync_flags, /* Flags to sync db file with (or 0) */ - int nBuf, /* Size of temporary buffer */ - u8 *zBuf, /* Temporary buffer to use */ - int *pnLog, /* OUT: Number of frames in WAL */ - int *pnCkpt /* OUT: Number of backfilled frames in WAL */ -){ - int rc; /* Return code */ - int isChanged = 0; /* True if a new wal-index header is loaded */ - int eMode2 = eMode; /* Mode to pass to walCheckpoint() */ - - assert( pWal->ckptLock==0 ); - assert( pWal->writeLock==0 ); - - if( pWal->readOnly ) return SQLITE_READONLY; - WALTRACE(("WAL%p: checkpoint begins\n", pWal)); - rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); - if( rc ){ - /* Usually this is SQLITE_BUSY meaning that another thread or process - ** is already running a checkpoint, or maybe a recovery. But it might - ** also be SQLITE_IOERR. */ - return rc; - } - pWal->ckptLock = 1; - - /* If this is a blocking-checkpoint, then obtain the write-lock as well - ** to prevent any writers from running while the checkpoint is underway. - ** This has to be done before the call to walIndexReadHdr() below. - ** - ** If the writer lock cannot be obtained, then a passive checkpoint is - ** run instead. Since the checkpointer is not holding the writer lock, - ** there is no point in blocking waiting for any readers. Assuming no - ** other error occurs, this function will return SQLITE_BUSY to the caller. - */ - if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1); - if( rc==SQLITE_OK ){ - pWal->writeLock = 1; - }else if( rc==SQLITE_BUSY ){ - eMode2 = SQLITE_CHECKPOINT_PASSIVE; - rc = SQLITE_OK; - } - } - - /* Read the wal-index header. */ - if( rc==SQLITE_OK ){ - rc = walIndexReadHdr(pWal, &isChanged); - } - - /* Copy data from the log to the database file. */ - if( rc==SQLITE_OK ){ - if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf); - } - - /* If no error occurred, set the output variables. */ - if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ - if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; - if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); - } - } - - if( isChanged ){ - /* If a new wal-index header was loaded before the checkpoint was - ** performed, then the pager-cache associated with pWal is now - ** out of date. So zero the cached wal-index header to ensure that - ** next time the pager opens a snapshot on this database it knows that - ** the cache needs to be reset. - */ - memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); - } - - /* Release the locks. */ - sqlite3WalEndWriteTransaction(pWal); - walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); - pWal->ckptLock = 0; - WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); - return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); -} - -/* Return the value to pass to a sqlite3_wal_hook callback, the -** number of frames in the WAL at the point of the last commit since -** sqlite3WalCallback() was called. If no commits have occurred since -** the last call, then return 0. -*/ -SQLITE_PRIVATE int sqlite3WalCallback(Wal *pWal){ - u32 ret = 0; - if( pWal ){ - ret = pWal->iCallback; - pWal->iCallback = 0; - } - return (int)ret; -} - -/* -** This function is called to change the WAL subsystem into or out -** of locking_mode=EXCLUSIVE. -** -** If op is zero, then attempt to change from locking_mode=EXCLUSIVE -** into locking_mode=NORMAL. This means that we must acquire a lock -** on the pWal->readLock byte. If the WAL is already in locking_mode=NORMAL -** or if the acquisition of the lock fails, then return 0. If the -** transition out of exclusive-mode is successful, return 1. This -** operation must occur while the pager is still holding the exclusive -** lock on the main database file. -** -** If op is one, then change from locking_mode=NORMAL into -** locking_mode=EXCLUSIVE. This means that the pWal->readLock must -** be released. Return 1 if the transition is made and 0 if the -** WAL is already in exclusive-locking mode - meaning that this -** routine is a no-op. The pager must already hold the exclusive lock -** on the main database file before invoking this operation. -** -** If op is negative, then do a dry-run of the op==1 case but do -** not actually change anything. The pager uses this to see if it -** should acquire the database exclusive lock prior to invoking -** the op==1 case. -*/ -SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op){ - int rc; - assert( pWal->writeLock==0 ); - assert( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE || op==-1 ); - - /* pWal->readLock is usually set, but might be -1 if there was a - ** prior error while attempting to acquire are read-lock. This cannot - ** happen if the connection is actually in exclusive mode (as no xShmLock - ** locks are taken in this case). Nor should the pager attempt to - ** upgrade to exclusive-mode following such an error. - */ - assert( pWal->readLock>=0 || pWal->lockError ); - assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) ); - - if( op==0 ){ - if( pWal->exclusiveMode ){ - pWal->exclusiveMode = 0; - if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){ - pWal->exclusiveMode = 1; - } - rc = pWal->exclusiveMode==0; - }else{ - /* Already in locking_mode=NORMAL */ - rc = 0; - } - }else if( op>0 ){ - assert( pWal->exclusiveMode==0 ); - assert( pWal->readLock>=0 ); - walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); - pWal->exclusiveMode = 1; - rc = 1; - }else{ - rc = pWal->exclusiveMode==0; - } - return rc; -} - -/* -** Return true if the argument is non-NULL and the WAL module is using -** heap-memory for the wal-index. Otherwise, if the argument is NULL or the -** WAL module is using shared-memory, return false. -*/ -SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){ - return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ); -} - -#ifdef SQLITE_ENABLE_ZIPVFS -/* -** If the argument is not NULL, it points to a Wal object that holds a -** read-lock. This function returns the database page-size if it is known, -** or zero if it is not (or if pWal is NULL). -*/ -SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){ - assert( pWal==0 || pWal->readLock>=0 ); - return (pWal ? pWal->szPage : 0); -} -#endif - -#endif /* #ifndef SQLITE_OMIT_WAL */ - -/************** End of wal.c *************************************************/ -/************** Begin file btmutex.c *****************************************/ -/* -** 2007 August 27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code used to implement mutexes on Btree objects. -** This code really belongs in btree.c. But btree.c is getting too -** big and we want to break it down some. This packaged seemed like -** a good breakout. -*/ -/************** Include btreeInt.h in the middle of btmutex.c ****************/ -/************** Begin file btreeInt.h ****************************************/ -/* -** 2004 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file implements a external (disk-based) database using BTrees. -** For a detailed discussion of BTrees, refer to -** -** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: -** "Sorting And Searching", pages 473-480. Addison-Wesley -** Publishing Company, Reading, Massachusetts. -** -** The basic idea is that each page of the file contains N database -** entries and N+1 pointers to subpages. -** -** ---------------------------------------------------------------- -** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) | -** ---------------------------------------------------------------- -** -** All of the keys on the page that Ptr(0) points to have values less -** than Key(0). All of the keys on page Ptr(1) and its subpages have -** values greater than Key(0) and less than Key(1). All of the keys -** on Ptr(N) and its subpages have values greater than Key(N-1). And -** so forth. -** -** Finding a particular key requires reading O(log(M)) pages from the -** disk where M is the number of entries in the tree. -** -** In this implementation, a single file can hold one or more separate -** BTrees. Each BTree is identified by the index of its root page. The -** key and data for any entry are combined to form the "payload". A -** fixed amount of payload can be carried directly on the database -** page. If the payload is larger than the preset amount then surplus -** bytes are stored on overflow pages. The payload for an entry -** and the preceding pointer are combined to form a "Cell". Each -** page has a small header which contains the Ptr(N) pointer and other -** information such as the size of key and data. -** -** FORMAT DETAILS -** -** The file is divided into pages. The first page is called page 1, -** the second is page 2, and so forth. A page number of zero indicates -** "no such page". The page size can be any power of 2 between 512 and 65536. -** Each page can be either a btree page, a freelist page, an overflow -** page, or a pointer-map page. -** -** The first page is always a btree page. The first 100 bytes of the first -** page contain a special header (the "file header") that describes the file. -** The format of the file header is as follows: -** -** OFFSET SIZE DESCRIPTION -** 0 16 Header string: "SQLite format 3\000" -** 16 2 Page size in bytes. -** 18 1 File format write version -** 19 1 File format read version -** 20 1 Bytes of unused space at the end of each page -** 21 1 Max embedded payload fraction -** 22 1 Min embedded payload fraction -** 23 1 Min leaf payload fraction -** 24 4 File change counter -** 28 4 Reserved for future use -** 32 4 First freelist page -** 36 4 Number of freelist pages in the file -** 40 60 15 4-byte meta values passed to higher layers -** -** 40 4 Schema cookie -** 44 4 File format of schema layer -** 48 4 Size of page cache -** 52 4 Largest root-page (auto/incr_vacuum) -** 56 4 1=UTF-8 2=UTF16le 3=UTF16be -** 60 4 User version -** 64 4 Incremental vacuum mode -** 68 4 unused -** 72 4 unused -** 76 4 unused -** -** All of the integer values are big-endian (most significant byte first). -** -** The file change counter is incremented when the database is changed -** This counter allows other processes to know when the file has changed -** and thus when they need to flush their cache. -** -** The max embedded payload fraction is the amount of the total usable -** space in a page that can be consumed by a single cell for standard -** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default -** is to limit the maximum cell size so that at least 4 cells will fit -** on one page. Thus the default max embedded payload fraction is 64. -** -** If the payload for a cell is larger than the max payload, then extra -** payload is spilled to overflow pages. Once an overflow page is allocated, -** as many bytes as possible are moved into the overflow pages without letting -** the cell size drop below the min embedded payload fraction. -** -** The min leaf payload fraction is like the min embedded payload fraction -** except that it applies to leaf nodes in a LEAFDATA tree. The maximum -** payload fraction for a LEAFDATA tree is always 100% (or 255) and it -** not specified in the header. -** -** Each btree pages is divided into three sections: The header, the -** cell pointer array, and the cell content area. Page 1 also has a 100-byte -** file header that occurs before the page header. -** -** |----------------| -** | file header | 100 bytes. Page 1 only. -** |----------------| -** | page header | 8 bytes for leaves. 12 bytes for interior nodes -** |----------------| -** | cell pointer | | 2 bytes per cell. Sorted order. -** | array | | Grows downward -** | | v -** |----------------| -** | unallocated | -** | space | -** |----------------| ^ Grows upwards -** | cell content | | Arbitrary order interspersed with freeblocks. -** | area | | and free space fragments. -** |----------------| -** -** The page headers looks like this: -** -** OFFSET SIZE DESCRIPTION -** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf -** 1 2 byte offset to the first freeblock -** 3 2 number of cells on this page -** 5 2 first byte of the cell content area -** 7 1 number of fragmented free bytes -** 8 4 Right child (the Ptr(N) value). Omitted on leaves. -** -** The flags define the format of this btree page. The leaf flag means that -** this page has no children. The zerodata flag means that this page carries -** only keys and no data. The intkey flag means that the key is a integer -** which is stored in the key size entry of the cell header rather than in -** the payload area. -** -** The cell pointer array begins on the first byte after the page header. -** The cell pointer array contains zero or more 2-byte numbers which are -** offsets from the beginning of the page to the cell content in the cell -** content area. The cell pointers occur in sorted order. The system strives -** to keep free space after the last cell pointer so that new cells can -** be easily added without having to defragment the page. -** -** Cell content is stored at the very end of the page and grows toward the -** beginning of the page. -** -** Unused space within the cell content area is collected into a linked list of -** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset -** to the first freeblock is given in the header. Freeblocks occur in -** increasing order. Because a freeblock must be at least 4 bytes in size, -** any group of 3 or fewer unused bytes in the cell content area cannot -** exist on the freeblock chain. A group of 3 or fewer free bytes is called -** a fragment. The total number of bytes in all fragments is recorded. -** in the page header at offset 7. -** -** SIZE DESCRIPTION -** 2 Byte offset of the next freeblock -** 2 Bytes in this freeblock -** -** Cells are of variable length. Cells are stored in the cell content area at -** the end of the page. Pointers to the cells are in the cell pointer array -** that immediately follows the page header. Cells is not necessarily -** contiguous or in order, but cell pointers are contiguous and in order. -** -** Cell content makes use of variable length integers. A variable -** length integer is 1 to 9 bytes where the lower 7 bits of each -** byte are used. The integer consists of all bytes that have bit 8 set and -** the first byte with bit 8 clear. The most significant byte of the integer -** appears first. A variable-length integer may not be more than 9 bytes long. -** As a special case, all 8 bytes of the 9th byte are used as data. This -** allows a 64-bit integer to be encoded in 9 bytes. -** -** 0x00 becomes 0x00000000 -** 0x7f becomes 0x0000007f -** 0x81 0x00 becomes 0x00000080 -** 0x82 0x00 becomes 0x00000100 -** 0x80 0x7f becomes 0x0000007f -** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678 -** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081 -** -** Variable length integers are used for rowids and to hold the number of -** bytes of key and data in a btree cell. -** -** The content of a cell looks like this: -** -** SIZE DESCRIPTION -** 4 Page number of the left child. Omitted if leaf flag is set. -** var Number of bytes of data. Omitted if the zerodata flag is set. -** var Number of bytes of key. Or the key itself if intkey flag is set. -** * Payload -** 4 First page of the overflow chain. Omitted if no overflow -** -** Overflow pages form a linked list. Each page except the last is completely -** filled with data (pagesize - 4 bytes). The last page can have as little -** as 1 byte of data. -** -** SIZE DESCRIPTION -** 4 Page number of next overflow page -** * Data -** -** Freelist pages come in two subtypes: trunk pages and leaf pages. The -** file header points to the first in a linked list of trunk page. Each trunk -** page points to multiple leaf pages. The content of a leaf page is -** unspecified. A trunk page looks like this: -** -** SIZE DESCRIPTION -** 4 Page number of next trunk page -** 4 Number of leaf pointers on this page -** * zero or more pages numbers of leaves -*/ - - -/* The following value is the maximum cell size assuming a maximum page -** size give above. -*/ -#define MX_CELL_SIZE(pBt) ((int)(pBt->pageSize-8)) - -/* The maximum number of cells on a single page of the database. This -** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself -** plus 2 bytes for the index to the cell in the page header). Such -** small cells will be rare, but they are possible. -*/ -#define MX_CELL(pBt) ((pBt->pageSize-8)/6) - -/* Forward declarations */ -typedef struct MemPage MemPage; -typedef struct BtLock BtLock; - -/* -** This is a magic string that appears at the beginning of every -** SQLite database in order to identify the file as a real database. -** -** You can change this value at compile-time by specifying a -** -DSQLITE_FILE_HEADER="..." on the compiler command-line. The -** header must be exactly 16 bytes including the zero-terminator so -** the string itself should be 15 characters long. If you change -** the header, then your custom library will not be able to read -** databases generated by the standard tools and the standard tools -** will not be able to read databases created by your custom library. -*/ -#ifndef SQLITE_FILE_HEADER /* 123456789 123456 */ -# define SQLITE_FILE_HEADER "SQLite format 3" -#endif - -/* -** Page type flags. An ORed combination of these flags appear as the -** first byte of on-disk image of every BTree page. -*/ -#define PTF_INTKEY 0x01 -#define PTF_ZERODATA 0x02 -#define PTF_LEAFDATA 0x04 -#define PTF_LEAF 0x08 - -/* -** As each page of the file is loaded into memory, an instance of the following -** structure is appended and initialized to zero. This structure stores -** information about the page that is decoded from the raw file page. -** -** The pParent field points back to the parent page. This allows us to -** walk up the BTree from any leaf to the root. Care must be taken to -** unref() the parent page pointer when this page is no longer referenced. -** The pageDestructor() routine handles that chore. -** -** Access to all fields of this structure is controlled by the mutex -** stored in MemPage.pBt->mutex. -*/ -struct MemPage { - u8 isInit; /* True if previously initialized. MUST BE FIRST! */ - u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ - u8 intKey; /* True if intkey flag is set */ - u8 leaf; /* True if leaf flag is set */ - u8 hasData; /* True if this page stores data */ - u8 hdrOffset; /* 100 for page 1. 0 otherwise */ - u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ - u8 max1bytePayload; /* min(maxLocal,127) */ - u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ - u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ - u16 cellOffset; /* Index in aData of first cell pointer */ - u16 nFree; /* Number of free bytes on the page */ - u16 nCell; /* Number of cells on this page, local and ovfl */ - u16 maskPage; /* Mask for page offset */ - u16 aiOvfl[5]; /* Insert the i-th overflow cell before the aiOvfl-th - ** non-overflow cell */ - u8 *apOvfl[5]; /* Pointers to the body of overflow cells */ - BtShared *pBt; /* Pointer to BtShared that this page is part of */ - u8 *aData; /* Pointer to disk image of the page data */ - u8 *aDataEnd; /* One byte past the end of usable data */ - u8 *aCellIdx; /* The cell index area */ - DbPage *pDbPage; /* Pager page handle */ - Pgno pgno; /* Page number for this page */ -}; - -/* -** The in-memory image of a disk page has the auxiliary information appended -** to the end. EXTRA_SIZE is the number of bytes of space needed to hold -** that extra information. -*/ -#define EXTRA_SIZE sizeof(MemPage) - -/* -** A linked list of the following structures is stored at BtShared.pLock. -** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor -** is opened on the table with root page BtShared.iTable. Locks are removed -** from this list when a transaction is committed or rolled back, or when -** a btree handle is closed. -*/ -struct BtLock { - Btree *pBtree; /* Btree handle holding this lock */ - Pgno iTable; /* Root page of table */ - u8 eLock; /* READ_LOCK or WRITE_LOCK */ - BtLock *pNext; /* Next in BtShared.pLock list */ -}; - -/* Candidate values for BtLock.eLock */ -#define READ_LOCK 1 -#define WRITE_LOCK 2 - -/* A Btree handle -** -** A database connection contains a pointer to an instance of -** this object for every database file that it has open. This structure -** is opaque to the database connection. The database connection cannot -** see the internals of this structure and only deals with pointers to -** this structure. -** -** For some database files, the same underlying database cache might be -** shared between multiple connections. In that case, each connection -** has it own instance of this object. But each instance of this object -** points to the same BtShared object. The database cache and the -** schema associated with the database file are all contained within -** the BtShared object. -** -** All fields in this structure are accessed under sqlite3.mutex. -** The pBt pointer itself may not be changed while there exists cursors -** in the referenced BtShared that point back to this Btree since those -** cursors have to go through this Btree to find their BtShared and -** they often do so without holding sqlite3.mutex. -*/ -struct Btree { - sqlite3 *db; /* The database connection holding this btree */ - BtShared *pBt; /* Sharable content of this btree */ - u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ - u8 sharable; /* True if we can share pBt with another db */ - u8 locked; /* True if db currently has pBt locked */ - int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ - int nBackup; /* Number of backup operations reading this btree */ - Btree *pNext; /* List of other sharable Btrees from the same db */ - Btree *pPrev; /* Back pointer of the same list */ -#ifndef SQLITE_OMIT_SHARED_CACHE - BtLock lock; /* Object used to lock page 1 */ -#endif -}; - -/* -** Btree.inTrans may take one of the following values. -** -** If the shared-data extension is enabled, there may be multiple users -** of the Btree structure. At most one of these may open a write transaction, -** but any number may have active read transactions. -*/ -#define TRANS_NONE 0 -#define TRANS_READ 1 -#define TRANS_WRITE 2 - -/* -** An instance of this object represents a single database file. -** -** A single database file can be in use at the same time by two -** or more database connections. When two or more connections are -** sharing the same database file, each connection has it own -** private Btree object for the file and each of those Btrees points -** to this one BtShared object. BtShared.nRef is the number of -** connections currently sharing this database file. -** -** Fields in this structure are accessed under the BtShared.mutex -** mutex, except for nRef and pNext which are accessed under the -** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field -** may not be modified once it is initially set as long as nRef>0. -** The pSchema field may be set once under BtShared.mutex and -** thereafter is unchanged as long as nRef>0. -** -** isPending: -** -** If a BtShared client fails to obtain a write-lock on a database -** table (because there exists one or more read-locks on the table), -** the shared-cache enters 'pending-lock' state and isPending is -** set to true. -** -** The shared-cache leaves the 'pending lock' state when either of -** the following occur: -** -** 1) The current writer (BtShared.pWriter) concludes its transaction, OR -** 2) The number of locks held by other connections drops to zero. -** -** while in the 'pending-lock' state, no connection may start a new -** transaction. -** -** This feature is included to help prevent writer-starvation. -*/ -struct BtShared { - Pager *pPager; /* The page cache */ - sqlite3 *db; /* Database connection currently using this Btree */ - BtCursor *pCursor; /* A list of all open cursors */ - MemPage *pPage1; /* First page of the database */ - u8 openFlags; /* Flags to sqlite3BtreeOpen() */ -#ifndef SQLITE_OMIT_AUTOVACUUM - u8 autoVacuum; /* True if auto-vacuum is enabled */ - u8 incrVacuum; /* True if incr-vacuum is enabled */ - u8 bDoTruncate; /* True to truncate db on commit */ -#endif - u8 inTransaction; /* Transaction state */ - u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ - u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ - u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ - u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ - u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ - u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ - u32 pageSize; /* Total number of bytes on a page */ - u32 usableSize; /* Number of usable bytes on each page */ - int nTransaction; /* Number of open transactions (read + write) */ - u32 nPage; /* Number of pages in the database */ - void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ - void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ - sqlite3_mutex *mutex; /* Non-recursive mutex required to access this object */ - Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ -#ifndef SQLITE_OMIT_SHARED_CACHE - int nRef; /* Number of references to this structure */ - BtShared *pNext; /* Next on a list of sharable BtShared structs */ - BtLock *pLock; /* List of locks held on this shared-btree struct */ - Btree *pWriter; /* Btree with currently open write transaction */ -#endif - u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */ -}; - -/* -** Allowed values for BtShared.btsFlags -*/ -#define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */ -#define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */ -#define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */ -#define BTS_INITIALLY_EMPTY 0x0008 /* Database was empty at trans start */ -#define BTS_NO_WAL 0x0010 /* Do not open write-ahead-log files */ -#define BTS_EXCLUSIVE 0x0020 /* pWriter has an exclusive lock */ -#define BTS_PENDING 0x0040 /* Waiting for read-locks to clear */ - -/* -** An instance of the following structure is used to hold information -** about a cell. The parseCellPtr() function fills in this structure -** based on information extract from the raw disk page. -*/ -typedef struct CellInfo CellInfo; -struct CellInfo { - i64 nKey; /* The key for INTKEY tables, or number of bytes in key */ - u8 *pCell; /* Pointer to the start of cell content */ - u32 nData; /* Number of bytes of data */ - u32 nPayload; /* Total amount of payload */ - u16 nHeader; /* Size of the cell content header in bytes */ - u16 nLocal; /* Amount of payload held locally */ - u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */ - u16 nSize; /* Size of the cell content on the main b-tree page */ -}; - -/* -** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than -** this will be declared corrupt. This value is calculated based on a -** maximum database size of 2^31 pages a minimum fanout of 2 for a -** root-node and 3 for all other internal nodes. -** -** If a tree that appears to be taller than this is encountered, it is -** assumed that the database is corrupt. -*/ -#define BTCURSOR_MAX_DEPTH 20 - -/* -** A cursor is a pointer to a particular entry within a particular -** b-tree within a database file. -** -** The entry is identified by its MemPage and the index in -** MemPage.aCell[] of the entry. -** -** A single database file can be shared by two more database connections, -** but cursors cannot be shared. Each cursor is associated with a -** particular database connection identified BtCursor.pBtree.db. -** -** Fields in this structure are accessed under the BtShared.mutex -** found at self->pBt->mutex. -*/ -struct BtCursor { - Btree *pBtree; /* The Btree to which this cursor belongs */ - BtShared *pBt; /* The BtShared this cursor points to */ - BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ - struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */ -#ifndef SQLITE_OMIT_INCRBLOB - Pgno *aOverflow; /* Cache of overflow page locations */ -#endif - Pgno pgnoRoot; /* The root page of this tree */ - sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */ - CellInfo info; /* A parse of the cell we are pointing at */ - i64 nKey; /* Size of pKey, or last integer key */ - void *pKey; /* Saved key that was cursor's last known position */ - int skipNext; /* Prev() is noop if negative. Next() is noop if positive */ - u8 wrFlag; /* True if writable */ - u8 atLast; /* Cursor pointing to the last entry */ - u8 validNKey; /* True if info.nKey is valid */ - u8 eState; /* One of the CURSOR_XXX constants (see below) */ -#ifndef SQLITE_OMIT_INCRBLOB - u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */ -#endif - u8 hints; /* As configured by CursorSetHints() */ - i16 iPage; /* Index of current page in apPage */ - u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */ - MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */ -}; - -/* -** Potential values for BtCursor.eState. -** -** CURSOR_VALID: -** Cursor points to a valid entry. getPayload() etc. may be called. -** -** CURSOR_INVALID: -** Cursor does not point to a valid entry. This can happen (for example) -** because the table is empty or because BtreeCursorFirst() has not been -** called. -** -** CURSOR_REQUIRESEEK: -** The table that this cursor was opened on still exists, but has been -** modified since the cursor was last used. The cursor position is saved -** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in -** this state, restoreCursorPosition() can be called to attempt to -** seek the cursor to the saved position. -** -** CURSOR_FAULT: -** A unrecoverable error (an I/O error or a malloc failure) has occurred -** on a different connection that shares the BtShared cache with this -** cursor. The error has left the cache in an inconsistent state. -** Do nothing else with this cursor. Any attempt to use the cursor -** should return the error code stored in BtCursor.skip -*/ -#define CURSOR_INVALID 0 -#define CURSOR_VALID 1 -#define CURSOR_REQUIRESEEK 2 -#define CURSOR_FAULT 3 - -/* -** The database page the PENDING_BYTE occupies. This page is never used. -*/ -# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) - -/* -** These macros define the location of the pointer-map entry for a -** database page. The first argument to each is the number of usable -** bytes on each page of the database (often 1024). The second is the -** page number to look up in the pointer map. -** -** PTRMAP_PAGENO returns the database page number of the pointer-map -** page that stores the required pointer. PTRMAP_PTROFFSET returns -** the offset of the requested map entry. -** -** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page, -** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be -** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements -** this test. -*/ -#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno) -#define PTRMAP_PTROFFSET(pgptrmap, pgno) (5*(pgno-pgptrmap-1)) -#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno)) - -/* -** The pointer map is a lookup table that identifies the parent page for -** each child page in the database file. The parent page is the page that -** contains a pointer to the child. Every page in the database contains -** 0 or 1 parent pages. (In this context 'database page' refers -** to any page that is not part of the pointer map itself.) Each pointer map -** entry consists of a single byte 'type' and a 4 byte parent page number. -** The PTRMAP_XXX identifiers below are the valid types. -** -** The purpose of the pointer map is to facility moving pages from one -** position in the file to another as part of autovacuum. When a page -** is moved, the pointer in its parent must be updated to point to the -** new location. The pointer map is used to locate the parent page quickly. -** -** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not -** used in this case. -** -** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number -** is not used in this case. -** -** PTRMAP_OVERFLOW1: The database page is the first page in a list of -** overflow pages. The page number identifies the page that -** contains the cell with a pointer to this overflow page. -** -** PTRMAP_OVERFLOW2: The database page is the second or later page in a list of -** overflow pages. The page-number identifies the previous -** page in the overflow page list. -** -** PTRMAP_BTREE: The database page is a non-root btree page. The page number -** identifies the parent page in the btree. -*/ -#define PTRMAP_ROOTPAGE 1 -#define PTRMAP_FREEPAGE 2 -#define PTRMAP_OVERFLOW1 3 -#define PTRMAP_OVERFLOW2 4 -#define PTRMAP_BTREE 5 - -/* A bunch of assert() statements to check the transaction state variables -** of handle p (type Btree*) are internally consistent. -*/ -#define btreeIntegrity(p) \ - assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \ - assert( p->pBt->inTransaction>=p->inTrans ); - - -/* -** The ISAUTOVACUUM macro is used within balance_nonroot() to determine -** if the database supports auto-vacuum or not. Because it is used -** within an expression that is an argument to another macro -** (sqliteMallocRaw), it is not possible to use conditional compilation. -** So, this macro is defined instead. -*/ -#ifndef SQLITE_OMIT_AUTOVACUUM -#define ISAUTOVACUUM (pBt->autoVacuum) -#else -#define ISAUTOVACUUM 0 -#endif - - -/* -** This structure is passed around through all the sanity checking routines -** in order to keep track of some global state information. -** -** The aRef[] array is allocated so that there is 1 bit for each page in -** the database. As the integrity-check proceeds, for each page used in -** the database the corresponding bit is set. This allows integrity-check to -** detect pages that are used twice and orphaned pages (both of which -** indicate corruption). -*/ -typedef struct IntegrityCk IntegrityCk; -struct IntegrityCk { - BtShared *pBt; /* The tree being checked out */ - Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ - u8 *aPgRef; /* 1 bit per page in the db (see above) */ - Pgno nPage; /* Number of pages in the database */ - int mxErr; /* Stop accumulating errors when this reaches zero */ - int nErr; /* Number of messages written to zErrMsg so far */ - int mallocFailed; /* A memory allocation error has occurred */ - StrAccum errMsg; /* Accumulate the error message text here */ -}; - -/* -** Routines to read or write a two- and four-byte big-endian integer values. -*/ -#define get2byte(x) ((x)[0]<<8 | (x)[1]) -#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v)) -#define get4byte sqlite3Get4byte -#define put4byte sqlite3Put4byte - -/************** End of btreeInt.h ********************************************/ -/************** Continuing where we left off in btmutex.c ********************/ -#ifndef SQLITE_OMIT_SHARED_CACHE -#if SQLITE_THREADSAFE - -/* -** Obtain the BtShared mutex associated with B-Tree handle p. Also, -** set BtShared.db to the database handle associated with p and the -** p->locked boolean to true. -*/ -static void lockBtreeMutex(Btree *p){ - assert( p->locked==0 ); - assert( sqlite3_mutex_notheld(p->pBt->mutex) ); - assert( sqlite3_mutex_held(p->db->mutex) ); - - sqlite3_mutex_enter(p->pBt->mutex); - p->pBt->db = p->db; - p->locked = 1; -} - -/* -** Release the BtShared mutex associated with B-Tree handle p and -** clear the p->locked boolean. -*/ -static void unlockBtreeMutex(Btree *p){ - BtShared *pBt = p->pBt; - assert( p->locked==1 ); - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( sqlite3_mutex_held(p->db->mutex) ); - assert( p->db==pBt->db ); - - sqlite3_mutex_leave(pBt->mutex); - p->locked = 0; -} - -/* -** Enter a mutex on the given BTree object. -** -** If the object is not sharable, then no mutex is ever required -** and this routine is a no-op. The underlying mutex is non-recursive. -** But we keep a reference count in Btree.wantToLock so the behavior -** of this interface is recursive. -** -** To avoid deadlocks, multiple Btrees are locked in the same order -** by all database connections. The p->pNext is a list of other -** Btrees belonging to the same database connection as the p Btree -** which need to be locked after p. If we cannot get a lock on -** p, then first unlock all of the others on p->pNext, then wait -** for the lock to become available on p, then relock all of the -** subsequent Btrees that desire a lock. -*/ -SQLITE_PRIVATE void sqlite3BtreeEnter(Btree *p){ - Btree *pLater; - - /* Some basic sanity checking on the Btree. The list of Btrees - ** connected by pNext and pPrev should be in sorted order by - ** Btree.pBt value. All elements of the list should belong to - ** the same connection. Only shared Btrees are on the list. */ - assert( p->pNext==0 || p->pNext->pBt>p->pBt ); - assert( p->pPrev==0 || p->pPrev->pBtpBt ); - assert( p->pNext==0 || p->pNext->db==p->db ); - assert( p->pPrev==0 || p->pPrev->db==p->db ); - assert( p->sharable || (p->pNext==0 && p->pPrev==0) ); - - /* Check for locking consistency */ - assert( !p->locked || p->wantToLock>0 ); - assert( p->sharable || p->wantToLock==0 ); - - /* We should already hold a lock on the database connection */ - assert( sqlite3_mutex_held(p->db->mutex) ); - - /* Unless the database is sharable and unlocked, then BtShared.db - ** should already be set correctly. */ - assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db ); - - if( !p->sharable ) return; - p->wantToLock++; - if( p->locked ) return; - - /* In most cases, we should be able to acquire the lock we - ** want without having to go throught the ascending lock - ** procedure that follows. Just be sure not to block. - */ - if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){ - p->pBt->db = p->db; - p->locked = 1; - return; - } - - /* To avoid deadlock, first release all locks with a larger - ** BtShared address. Then acquire our lock. Then reacquire - ** the other BtShared locks that we used to hold in ascending - ** order. - */ - for(pLater=p->pNext; pLater; pLater=pLater->pNext){ - assert( pLater->sharable ); - assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt ); - assert( !pLater->locked || pLater->wantToLock>0 ); - if( pLater->locked ){ - unlockBtreeMutex(pLater); - } - } - lockBtreeMutex(p); - for(pLater=p->pNext; pLater; pLater=pLater->pNext){ - if( pLater->wantToLock ){ - lockBtreeMutex(pLater); - } - } -} - -/* -** Exit the recursive mutex on a Btree. -*/ -SQLITE_PRIVATE void sqlite3BtreeLeave(Btree *p){ - if( p->sharable ){ - assert( p->wantToLock>0 ); - p->wantToLock--; - if( p->wantToLock==0 ){ - unlockBtreeMutex(p); - } - } -} - -#ifndef NDEBUG -/* -** Return true if the BtShared mutex is held on the btree, or if the -** B-Tree is not marked as sharable. -** -** This routine is used only from within assert() statements. -*/ -SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree *p){ - assert( p->sharable==0 || p->locked==0 || p->wantToLock>0 ); - assert( p->sharable==0 || p->locked==0 || p->db==p->pBt->db ); - assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->pBt->mutex) ); - assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->db->mutex) ); - - return (p->sharable==0 || p->locked); -} -#endif - - -#ifndef SQLITE_OMIT_INCRBLOB -/* -** Enter and leave a mutex on a Btree given a cursor owned by that -** Btree. These entry points are used by incremental I/O and can be -** omitted if that module is not used. -*/ -SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){ - sqlite3BtreeEnter(pCur->pBtree); -} -SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){ - sqlite3BtreeLeave(pCur->pBtree); -} -#endif /* SQLITE_OMIT_INCRBLOB */ - - -/* -** Enter the mutex on every Btree associated with a database -** connection. This is needed (for example) prior to parsing -** a statement since we will be comparing table and column names -** against all schemas and we do not want those schemas being -** reset out from under us. -** -** There is a corresponding leave-all procedures. -** -** Enter the mutexes in accending order by BtShared pointer address -** to avoid the possibility of deadlock when two threads with -** two or more btrees in common both try to lock all their btrees -** at the same instant. -*/ -SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){ - int i; - Btree *p; - assert( sqlite3_mutex_held(db->mutex) ); - for(i=0; inDb; i++){ - p = db->aDb[i].pBt; - if( p ) sqlite3BtreeEnter(p); - } -} -SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3 *db){ - int i; - Btree *p; - assert( sqlite3_mutex_held(db->mutex) ); - for(i=0; inDb; i++){ - p = db->aDb[i].pBt; - if( p ) sqlite3BtreeLeave(p); - } -} - -/* -** Return true if a particular Btree requires a lock. Return FALSE if -** no lock is ever required since it is not sharable. -*/ -SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){ - return p->sharable; -} - -#ifndef NDEBUG -/* -** Return true if the current thread holds the database connection -** mutex and all required BtShared mutexes. -** -** This routine is used inside assert() statements only. -*/ -SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){ - int i; - if( !sqlite3_mutex_held(db->mutex) ){ - return 0; - } - for(i=0; inDb; i++){ - Btree *p; - p = db->aDb[i].pBt; - if( p && p->sharable && - (p->wantToLock==0 || !sqlite3_mutex_held(p->pBt->mutex)) ){ - return 0; - } - } - return 1; -} -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* -** Return true if the correct mutexes are held for accessing the -** db->aDb[iDb].pSchema structure. The mutexes required for schema -** access are: -** -** (1) The mutex on db -** (2) if iDb!=1, then the mutex on db->aDb[iDb].pBt. -** -** If pSchema is not NULL, then iDb is computed from pSchema and -** db using sqlite3SchemaToIndex(). -*/ -SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){ - Btree *p; - assert( db!=0 ); - if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema); - assert( iDb>=0 && iDbnDb ); - if( !sqlite3_mutex_held(db->mutex) ) return 0; - if( iDb==1 ) return 1; - p = db->aDb[iDb].pBt; - assert( p!=0 ); - return p->sharable==0 || p->locked==1; -} -#endif /* NDEBUG */ - -#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */ -/* -** The following are special cases for mutex enter routines for use -** in single threaded applications that use shared cache. Except for -** these two routines, all mutex operations are no-ops in that case and -** are null #defines in btree.h. -** -** If shared cache is disabled, then all btree mutex routines, including -** the ones below, are no-ops and are null #defines in btree.h. -*/ - -SQLITE_PRIVATE void sqlite3BtreeEnter(Btree *p){ - p->pBt->db = p->db; -} -SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){ - int i; - for(i=0; inDb; i++){ - Btree *p = db->aDb[i].pBt; - if( p ){ - p->pBt->db = p->db; - } - } -} -#endif /* if SQLITE_THREADSAFE */ -#endif /* ifndef SQLITE_OMIT_SHARED_CACHE */ - -/************** End of btmutex.c *********************************************/ -/************** Begin file btree.c *******************************************/ -/* -** 2004 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file implements a external (disk-based) database using BTrees. -** See the header comment on "btreeInt.h" for additional information. -** Including a description of file format and an overview of operation. -*/ - -/* -** The header string that appears at the beginning of every -** SQLite database. -*/ -static const char zMagicHeader[] = SQLITE_FILE_HEADER; - -/* -** Set this global variable to 1 to enable tracing using the TRACE -** macro. -*/ -#if 0 -int sqlite3BtreeTrace=1; /* True to enable tracing */ -# define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);} -#else -# define TRACE(X) -#endif - -/* -** Extract a 2-byte big-endian integer from an array of unsigned bytes. -** But if the value is zero, make it 65536. -** -** This routine is used to extract the "offset to cell content area" value -** from the header of a btree page. If the page size is 65536 and the page -** is empty, the offset should be 65536, but the 2-byte value stores zero. -** This routine makes the necessary adjustment to 65536. -*/ -#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1) - -/* -** Values passed as the 5th argument to allocateBtreePage() -*/ -#define BTALLOC_ANY 0 /* Allocate any page */ -#define BTALLOC_EXACT 1 /* Allocate exact page if possible */ -#define BTALLOC_LE 2 /* Allocate any page <= the parameter */ - -/* -** Macro IfNotOmitAV(x) returns (x) if SQLITE_OMIT_AUTOVACUUM is not -** defined, or 0 if it is. For example: -** -** bIncrVacuum = IfNotOmitAV(pBtShared->incrVacuum); -*/ -#ifndef SQLITE_OMIT_AUTOVACUUM -#define IfNotOmitAV(expr) (expr) -#else -#define IfNotOmitAV(expr) 0 -#endif - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** A list of BtShared objects that are eligible for participation -** in shared cache. This variable has file scope during normal builds, -** but the test harness needs to access it so we make it global for -** test builds. -** -** Access to this variable is protected by SQLITE_MUTEX_STATIC_MASTER. -*/ -#ifdef SQLITE_TEST -SQLITE_PRIVATE BtShared *SQLITE_WSD sqlite3SharedCacheList = 0; -#else -static BtShared *SQLITE_WSD sqlite3SharedCacheList = 0; -#endif -#endif /* SQLITE_OMIT_SHARED_CACHE */ - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Enable or disable the shared pager and schema features. -** -** This routine has no effect on existing database connections. -** The shared cache setting effects only future calls to -** sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2(). -*/ -SQLITE_API int sqlite3_enable_shared_cache(int enable){ - sqlite3GlobalConfig.sharedCacheEnabled = enable; - return SQLITE_OK; -} -#endif - - - -#ifdef SQLITE_OMIT_SHARED_CACHE - /* - ** The functions querySharedCacheTableLock(), setSharedCacheTableLock(), - ** and clearAllSharedCacheTableLocks() - ** manipulate entries in the BtShared.pLock linked list used to store - ** shared-cache table level locks. If the library is compiled with the - ** shared-cache feature disabled, then there is only ever one user - ** of each BtShared structure and so this locking is not necessary. - ** So define the lock related functions as no-ops. - */ - #define querySharedCacheTableLock(a,b,c) SQLITE_OK - #define setSharedCacheTableLock(a,b,c) SQLITE_OK - #define clearAllSharedCacheTableLocks(a) - #define downgradeAllSharedCacheTableLocks(a) - #define hasSharedCacheTableLock(a,b,c,d) 1 - #define hasReadConflicts(a, b) 0 -#endif - -#ifndef SQLITE_OMIT_SHARED_CACHE - -#ifdef SQLITE_DEBUG -/* -**** This function is only used as part of an assert() statement. *** -** -** Check to see if pBtree holds the required locks to read or write to the -** table with root page iRoot. Return 1 if it does and 0 if not. -** -** For example, when writing to a table with root-page iRoot via -** Btree connection pBtree: -** -** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) ); -** -** When writing to an index that resides in a sharable database, the -** caller should have first obtained a lock specifying the root page of -** the corresponding table. This makes things a bit more complicated, -** as this module treats each table as a separate structure. To determine -** the table corresponding to the index being written, this -** function has to search through the database schema. -** -** Instead of a lock on the table/index rooted at page iRoot, the caller may -** hold a write-lock on the schema table (root page 1). This is also -** acceptable. -*/ -static int hasSharedCacheTableLock( - Btree *pBtree, /* Handle that must hold lock */ - Pgno iRoot, /* Root page of b-tree */ - int isIndex, /* True if iRoot is the root of an index b-tree */ - int eLockType /* Required lock type (READ_LOCK or WRITE_LOCK) */ -){ - Schema *pSchema = (Schema *)pBtree->pBt->pSchema; - Pgno iTab = 0; - BtLock *pLock; - - /* If this database is not shareable, or if the client is reading - ** and has the read-uncommitted flag set, then no lock is required. - ** Return true immediately. - */ - if( (pBtree->sharable==0) - || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted)) - ){ - return 1; - } - - /* If the client is reading or writing an index and the schema is - ** not loaded, then it is too difficult to actually check to see if - ** the correct locks are held. So do not bother - just return true. - ** This case does not come up very often anyhow. - */ - if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){ - return 1; - } - - /* Figure out the root-page that the lock should be held on. For table - ** b-trees, this is just the root page of the b-tree being read or - ** written. For index b-trees, it is the root page of the associated - ** table. */ - if( isIndex ){ - HashElem *p; - for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ - Index *pIdx = (Index *)sqliteHashData(p); - if( pIdx->tnum==(int)iRoot ){ - iTab = pIdx->pTable->tnum; - } - } - }else{ - iTab = iRoot; - } - - /* Search for the required lock. Either a write-lock on root-page iTab, a - ** write-lock on the schema table, or (if the client is reading) a - ** read-lock on iTab will suffice. Return 1 if any of these are found. */ - for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ - if( pLock->pBtree==pBtree - && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1)) - && pLock->eLock>=eLockType - ){ - return 1; - } - } - - /* Failed to find the required lock. */ - return 0; -} -#endif /* SQLITE_DEBUG */ - -#ifdef SQLITE_DEBUG -/* -**** This function may be used as part of assert() statements only. **** -** -** Return true if it would be illegal for pBtree to write into the -** table or index rooted at iRoot because other shared connections are -** simultaneously reading that same table or index. -** -** It is illegal for pBtree to write if some other Btree object that -** shares the same BtShared object is currently reading or writing -** the iRoot table. Except, if the other Btree object has the -** read-uncommitted flag set, then it is OK for the other object to -** have a read cursor. -** -** For example, before writing to any part of the table or index -** rooted at page iRoot, one should call: -** -** assert( !hasReadConflicts(pBtree, iRoot) ); -*/ -static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ - BtCursor *p; - for(p=pBtree->pBt->pCursor; p; p=p->pNext){ - if( p->pgnoRoot==iRoot - && p->pBtree!=pBtree - && 0==(p->pBtree->db->flags & SQLITE_ReadUncommitted) - ){ - return 1; - } - } - return 0; -} -#endif /* #ifdef SQLITE_DEBUG */ - -/* -** Query to see if Btree handle p may obtain a lock of type eLock -** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return -** SQLITE_OK if the lock may be obtained (by calling -** setSharedCacheTableLock()), or SQLITE_LOCKED if not. -*/ -static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ - BtShared *pBt = p->pBt; - BtLock *pIter; - - assert( sqlite3BtreeHoldsMutex(p) ); - assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); - assert( p->db!=0 ); - assert( !(p->db->flags&SQLITE_ReadUncommitted)||eLock==WRITE_LOCK||iTab==1 ); - - /* If requesting a write-lock, then the Btree must have an open write - ** transaction on this file. And, obviously, for this to be so there - ** must be an open write transaction on the file itself. - */ - assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) ); - assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE ); - - /* This routine is a no-op if the shared-cache is not enabled */ - if( !p->sharable ){ - return SQLITE_OK; - } - - /* If some other connection is holding an exclusive lock, the - ** requested lock may not be obtained. - */ - if( pBt->pWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){ - sqlite3ConnectionBlocked(p->db, pBt->pWriter->db); - return SQLITE_LOCKED_SHAREDCACHE; - } - - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - /* The condition (pIter->eLock!=eLock) in the following if(...) - ** statement is a simplification of: - ** - ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) - ** - ** since we know that if eLock==WRITE_LOCK, then no other connection - ** may hold a WRITE_LOCK on any table in this file (since there can - ** only be a single writer). - */ - assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); - assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); - if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ - sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); - if( eLock==WRITE_LOCK ){ - assert( p==pBt->pWriter ); - pBt->btsFlags |= BTS_PENDING; - } - return SQLITE_LOCKED_SHAREDCACHE; - } - } - return SQLITE_OK; -} -#endif /* !SQLITE_OMIT_SHARED_CACHE */ - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Add a lock on the table with root-page iTable to the shared-btree used -** by Btree handle p. Parameter eLock must be either READ_LOCK or -** WRITE_LOCK. -** -** This function assumes the following: -** -** (a) The specified Btree object p is connected to a sharable -** database (one with the BtShared.sharable flag set), and -** -** (b) No other Btree objects hold a lock that conflicts -** with the requested lock (i.e. querySharedCacheTableLock() has -** already been called and returned SQLITE_OK). -** -** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM -** is returned if a malloc attempt fails. -*/ -static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ - BtShared *pBt = p->pBt; - BtLock *pLock = 0; - BtLock *pIter; - - assert( sqlite3BtreeHoldsMutex(p) ); - assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); - assert( p->db!=0 ); - - /* A connection with the read-uncommitted flag set will never try to - ** obtain a read-lock using this function. The only read-lock obtained - ** by a connection in read-uncommitted mode is on the sqlite_master - ** table, and that lock is obtained in BtreeBeginTrans(). */ - assert( 0==(p->db->flags&SQLITE_ReadUncommitted) || eLock==WRITE_LOCK ); - - /* This function should only be called on a sharable b-tree after it - ** has been determined that no other b-tree holds a conflicting lock. */ - assert( p->sharable ); - assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); - - /* First search the list for an existing lock on this table. */ - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - if( pIter->iTable==iTable && pIter->pBtree==p ){ - pLock = pIter; - break; - } - } - - /* If the above search did not find a BtLock struct associating Btree p - ** with table iTable, allocate one and link it into the list. - */ - if( !pLock ){ - pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock)); - if( !pLock ){ - return SQLITE_NOMEM; - } - pLock->iTable = iTable; - pLock->pBtree = p; - pLock->pNext = pBt->pLock; - pBt->pLock = pLock; - } - - /* Set the BtLock.eLock variable to the maximum of the current lock - ** and the requested lock. This means if a write-lock was already held - ** and a read-lock requested, we don't incorrectly downgrade the lock. - */ - assert( WRITE_LOCK>READ_LOCK ); - if( eLock>pLock->eLock ){ - pLock->eLock = eLock; - } - - return SQLITE_OK; -} -#endif /* !SQLITE_OMIT_SHARED_CACHE */ - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Release all the table locks (locks obtained via calls to -** the setSharedCacheTableLock() procedure) held by Btree object p. -** -** This function assumes that Btree p has an open read or write -** transaction. If it does not, then the BTS_PENDING flag -** may be incorrectly cleared. -*/ -static void clearAllSharedCacheTableLocks(Btree *p){ - BtShared *pBt = p->pBt; - BtLock **ppIter = &pBt->pLock; - - assert( sqlite3BtreeHoldsMutex(p) ); - assert( p->sharable || 0==*ppIter ); - assert( p->inTrans>0 ); - - while( *ppIter ){ - BtLock *pLock = *ppIter; - assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); - assert( pLock->pBtree->inTrans>=pLock->eLock ); - if( pLock->pBtree==p ){ - *ppIter = pLock->pNext; - assert( pLock->iTable!=1 || pLock==&p->lock ); - if( pLock->iTable!=1 ){ - sqlite3_free(pLock); - } - }else{ - ppIter = &pLock->pNext; - } - } - - assert( (pBt->btsFlags & BTS_PENDING)==0 || pBt->pWriter ); - if( pBt->pWriter==p ){ - pBt->pWriter = 0; - pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); - }else if( pBt->nTransaction==2 ){ - /* This function is called when Btree p is concluding its - ** transaction. If there currently exists a writer, and p is not - ** that writer, then the number of locks held by connections other - ** than the writer must be about to drop to zero. In this case - ** set the BTS_PENDING flag to 0. - ** - ** If there is not currently a writer, then BTS_PENDING must - ** be zero already. So this next line is harmless in that case. - */ - pBt->btsFlags &= ~BTS_PENDING; - } -} - -/* -** This function changes all write-locks held by Btree p into read-locks. -*/ -static void downgradeAllSharedCacheTableLocks(Btree *p){ - BtShared *pBt = p->pBt; - if( pBt->pWriter==p ){ - BtLock *pLock; - pBt->pWriter = 0; - pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); - for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ - assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); - pLock->eLock = READ_LOCK; - } - } -} - -#endif /* SQLITE_OMIT_SHARED_CACHE */ - -static void releasePage(MemPage *pPage); /* Forward reference */ - -/* -***** This routine is used inside of assert() only **** -** -** Verify that the cursor holds the mutex on its BtShared -*/ -#ifdef SQLITE_DEBUG -static int cursorHoldsMutex(BtCursor *p){ - return sqlite3_mutex_held(p->pBt->mutex); -} -#endif - - -#ifndef SQLITE_OMIT_INCRBLOB -/* -** Invalidate the overflow page-list cache for cursor pCur, if any. -*/ -static void invalidateOverflowCache(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - sqlite3_free(pCur->aOverflow); - pCur->aOverflow = 0; -} - -/* -** Invalidate the overflow page-list cache for all cursors opened -** on the shared btree structure pBt. -*/ -static void invalidateAllOverflowCache(BtShared *pBt){ - BtCursor *p; - assert( sqlite3_mutex_held(pBt->mutex) ); - for(p=pBt->pCursor; p; p=p->pNext){ - invalidateOverflowCache(p); - } -} - -/* -** This function is called before modifying the contents of a table -** to invalidate any incrblob cursors that are open on the -** row or one of the rows being modified. -** -** If argument isClearTable is true, then the entire contents of the -** table is about to be deleted. In this case invalidate all incrblob -** cursors open on any row within the table with root-page pgnoRoot. -** -** Otherwise, if argument isClearTable is false, then the row with -** rowid iRow is being replaced or deleted. In this case invalidate -** only those incrblob cursors open on that specific row. -*/ -static void invalidateIncrblobCursors( - Btree *pBtree, /* The database file to check */ - i64 iRow, /* The rowid that might be changing */ - int isClearTable /* True if all rows are being deleted */ -){ - BtCursor *p; - BtShared *pBt = pBtree->pBt; - assert( sqlite3BtreeHoldsMutex(pBtree) ); - for(p=pBt->pCursor; p; p=p->pNext){ - if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){ - p->eState = CURSOR_INVALID; - } - } -} - -#else - /* Stub functions when INCRBLOB is omitted */ - #define invalidateOverflowCache(x) - #define invalidateAllOverflowCache(x) - #define invalidateIncrblobCursors(x,y,z) -#endif /* SQLITE_OMIT_INCRBLOB */ - -/* -** Set bit pgno of the BtShared.pHasContent bitvec. This is called -** when a page that previously contained data becomes a free-list leaf -** page. -** -** The BtShared.pHasContent bitvec exists to work around an obscure -** bug caused by the interaction of two useful IO optimizations surrounding -** free-list leaf pages: -** -** 1) When all data is deleted from a page and the page becomes -** a free-list leaf page, the page is not written to the database -** (as free-list leaf pages contain no meaningful data). Sometimes -** such a page is not even journalled (as it will not be modified, -** why bother journalling it?). -** -** 2) When a free-list leaf page is reused, its content is not read -** from the database or written to the journal file (why should it -** be, if it is not at all meaningful?). -** -** By themselves, these optimizations work fine and provide a handy -** performance boost to bulk delete or insert operations. However, if -** a page is moved to the free-list and then reused within the same -** transaction, a problem comes up. If the page is not journalled when -** it is moved to the free-list and it is also not journalled when it -** is extracted from the free-list and reused, then the original data -** may be lost. In the event of a rollback, it may not be possible -** to restore the database to its original configuration. -** -** The solution is the BtShared.pHasContent bitvec. Whenever a page is -** moved to become a free-list leaf page, the corresponding bit is -** set in the bitvec. Whenever a leaf page is extracted from the free-list, -** optimization 2 above is omitted if the corresponding bit is already -** set in BtShared.pHasContent. The contents of the bitvec are cleared -** at the end of every transaction. -*/ -static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ - int rc = SQLITE_OK; - if( !pBt->pHasContent ){ - assert( pgno<=pBt->nPage ); - pBt->pHasContent = sqlite3BitvecCreate(pBt->nPage); - if( !pBt->pHasContent ){ - rc = SQLITE_NOMEM; - } - } - if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){ - rc = sqlite3BitvecSet(pBt->pHasContent, pgno); - } - return rc; -} - -/* -** Query the BtShared.pHasContent vector. -** -** This function is called when a free-list leaf page is removed from the -** free-list for reuse. It returns false if it is safe to retrieve the -** page from the pager layer with the 'no-content' flag set. True otherwise. -*/ -static int btreeGetHasContent(BtShared *pBt, Pgno pgno){ - Bitvec *p = pBt->pHasContent; - return (p && (pgno>sqlite3BitvecSize(p) || sqlite3BitvecTest(p, pgno))); -} - -/* -** Clear (destroy) the BtShared.pHasContent bitvec. This should be -** invoked at the conclusion of each write-transaction. -*/ -static void btreeClearHasContent(BtShared *pBt){ - sqlite3BitvecDestroy(pBt->pHasContent); - pBt->pHasContent = 0; -} - -/* -** Release all of the apPage[] pages for a cursor. -*/ -static void btreeReleaseAllCursorPages(BtCursor *pCur){ - int i; - for(i=0; i<=pCur->iPage; i++){ - releasePage(pCur->apPage[i]); - pCur->apPage[i] = 0; - } - pCur->iPage = -1; -} - - -/* -** Save the current cursor position in the variables BtCursor.nKey -** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. -** -** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID) -** prior to calling this routine. -*/ -static int saveCursorPosition(BtCursor *pCur){ - int rc; - - assert( CURSOR_VALID==pCur->eState ); - assert( 0==pCur->pKey ); - assert( cursorHoldsMutex(pCur) ); - - rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); - assert( rc==SQLITE_OK ); /* KeySize() cannot fail */ - - /* If this is an intKey table, then the above call to BtreeKeySize() - ** stores the integer key in pCur->nKey. In this case this value is - ** all that is required. Otherwise, if pCur is not open on an intKey - ** table, then malloc space for and store the pCur->nKey bytes of key - ** data. - */ - if( 0==pCur->apPage[0]->intKey ){ - void *pKey = sqlite3Malloc( (int)pCur->nKey ); - if( pKey ){ - rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey); - if( rc==SQLITE_OK ){ - pCur->pKey = pKey; - }else{ - sqlite3_free(pKey); - } - }else{ - rc = SQLITE_NOMEM; - } - } - assert( !pCur->apPage[0]->intKey || !pCur->pKey ); - - if( rc==SQLITE_OK ){ - btreeReleaseAllCursorPages(pCur); - pCur->eState = CURSOR_REQUIRESEEK; - } - - invalidateOverflowCache(pCur); - return rc; -} - -/* -** Save the positions of all cursors (except pExcept) that are open on -** the table with root-page iRoot. Usually, this is called just before cursor -** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()). -*/ -static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ - BtCursor *p; - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( pExcept==0 || pExcept->pBt==pBt ); - for(p=pBt->pCursor; p; p=p->pNext){ - if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){ - if( p->eState==CURSOR_VALID ){ - int rc = saveCursorPosition(p); - if( SQLITE_OK!=rc ){ - return rc; - } - }else{ - testcase( p->iPage>0 ); - btreeReleaseAllCursorPages(p); - } - } - } - return SQLITE_OK; -} - -/* -** Clear the current cursor position. -*/ -SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - sqlite3_free(pCur->pKey); - pCur->pKey = 0; - pCur->eState = CURSOR_INVALID; -} - -/* -** In this version of BtreeMoveto, pKey is a packed index record -** such as is generated by the OP_MakeRecord opcode. Unpack the -** record and then call BtreeMovetoUnpacked() to do the work. -*/ -static int btreeMoveto( - BtCursor *pCur, /* Cursor open on the btree to be searched */ - const void *pKey, /* Packed key if the btree is an index */ - i64 nKey, /* Integer key for tables. Size of pKey for indices */ - int bias, /* Bias search to the high end */ - int *pRes /* Write search results here */ -){ - int rc; /* Status code */ - UnpackedRecord *pIdxKey; /* Unpacked index key */ - char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ - char *pFree = 0; - - if( pKey ){ - assert( nKey==(i64)(int)nKey ); - pIdxKey = sqlite3VdbeAllocUnpackedRecord( - pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree - ); - if( pIdxKey==0 ) return SQLITE_NOMEM; - sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey); - }else{ - pIdxKey = 0; - } - rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); - if( pFree ){ - sqlite3DbFree(pCur->pKeyInfo->db, pFree); - } - return rc; -} - -/* -** Restore the cursor to the position it was in (or as close to as possible) -** when saveCursorPosition() was called. Note that this call deletes the -** saved position info stored by saveCursorPosition(), so there can be -** at most one effective restoreCursorPosition() call after each -** saveCursorPosition(). -*/ -static int btreeRestoreCursorPosition(BtCursor *pCur){ - int rc; - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState>=CURSOR_REQUIRESEEK ); - if( pCur->eState==CURSOR_FAULT ){ - return pCur->skipNext; - } - pCur->eState = CURSOR_INVALID; - rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skipNext); - if( rc==SQLITE_OK ){ - sqlite3_free(pCur->pKey); - pCur->pKey = 0; - assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); - } - return rc; -} - -#define restoreCursorPosition(p) \ - (p->eState>=CURSOR_REQUIRESEEK ? \ - btreeRestoreCursorPosition(p) : \ - SQLITE_OK) - -/* -** Determine whether or not a cursor has moved from the position it -** was last placed at. Cursors can move when the row they are pointing -** at is deleted out from under them. -** -** This routine returns an error code if something goes wrong. The -** integer *pHasMoved is set to one if the cursor has moved and 0 if not. -*/ -SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){ - int rc; - - rc = restoreCursorPosition(pCur); - if( rc ){ - *pHasMoved = 1; - return rc; - } - if( pCur->eState!=CURSOR_VALID || pCur->skipNext!=0 ){ - *pHasMoved = 1; - }else{ - *pHasMoved = 0; - } - return SQLITE_OK; -} - -#ifndef SQLITE_OMIT_AUTOVACUUM -/* -** Given a page number of a regular database page, return the page -** number for the pointer-map page that contains the entry for the -** input page number. -** -** Return 0 (not a valid page) for pgno==1 since there is -** no pointer map associated with page 1. The integrity_check logic -** requires that ptrmapPageno(*,1)!=1. -*/ -static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){ - int nPagesPerMapPage; - Pgno iPtrMap, ret; - assert( sqlite3_mutex_held(pBt->mutex) ); - if( pgno<2 ) return 0; - nPagesPerMapPage = (pBt->usableSize/5)+1; - iPtrMap = (pgno-2)/nPagesPerMapPage; - ret = (iPtrMap*nPagesPerMapPage) + 2; - if( ret==PENDING_BYTE_PAGE(pBt) ){ - ret++; - } - return ret; -} - -/* -** Write an entry into the pointer map. -** -** This routine updates the pointer map entry for page number 'key' -** so that it maps to type 'eType' and parent page number 'pgno'. -** -** If *pRC is initially non-zero (non-SQLITE_OK) then this routine is -** a no-op. If an error occurs, the appropriate error code is written -** into *pRC. -*/ -static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ - DbPage *pDbPage; /* The pointer map page */ - u8 *pPtrmap; /* The pointer map data */ - Pgno iPtrmap; /* The pointer map page number */ - int offset; /* Offset in pointer map page */ - int rc; /* Return code from subfunctions */ - - if( *pRC ) return; - - assert( sqlite3_mutex_held(pBt->mutex) ); - /* The master-journal page number must never be used as a pointer map page */ - assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); - - assert( pBt->autoVacuum ); - if( key==0 ){ - *pRC = SQLITE_CORRUPT_BKPT; - return; - } - iPtrmap = PTRMAP_PAGENO(pBt, key); - rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; - } - offset = PTRMAP_PTROFFSET(iPtrmap, key); - if( offset<0 ){ - *pRC = SQLITE_CORRUPT_BKPT; - goto ptrmap_exit; - } - assert( offset <= (int)pBt->usableSize-5 ); - pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); - - if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ - TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); - *pRC= rc = sqlite3PagerWrite(pDbPage); - if( rc==SQLITE_OK ){ - pPtrmap[offset] = eType; - put4byte(&pPtrmap[offset+1], parent); - } - } - -ptrmap_exit: - sqlite3PagerUnref(pDbPage); -} - -/* -** Read an entry from the pointer map. -** -** This routine retrieves the pointer map entry for page 'key', writing -** the type and parent page number to *pEType and *pPgno respectively. -** An error code is returned if something goes wrong, otherwise SQLITE_OK. -*/ -static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ - DbPage *pDbPage; /* The pointer map page */ - int iPtrmap; /* Pointer map page index */ - u8 *pPtrmap; /* Pointer map page data */ - int offset; /* Offset of entry in pointer map */ - int rc; - - assert( sqlite3_mutex_held(pBt->mutex) ); - - iPtrmap = PTRMAP_PAGENO(pBt, key); - rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage); - if( rc!=0 ){ - return rc; - } - pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); - - offset = PTRMAP_PTROFFSET(iPtrmap, key); - if( offset<0 ){ - sqlite3PagerUnref(pDbPage); - return SQLITE_CORRUPT_BKPT; - } - assert( offset <= (int)pBt->usableSize-5 ); - assert( pEType!=0 ); - *pEType = pPtrmap[offset]; - if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); - - sqlite3PagerUnref(pDbPage); - if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_BKPT; - return SQLITE_OK; -} - -#else /* if defined SQLITE_OMIT_AUTOVACUUM */ - #define ptrmapPut(w,x,y,z,rc) - #define ptrmapGet(w,x,y,z) SQLITE_OK - #define ptrmapPutOvflPtr(x, y, rc) -#endif - -/* -** Given a btree page and a cell index (0 means the first cell on -** the page, 1 means the second cell, and so forth) return a pointer -** to the cell content. -** -** This routine works only for pages that do not contain overflow cells. -*/ -#define findCell(P,I) \ - ((P)->aData + ((P)->maskPage & get2byte(&(P)->aCellIdx[2*(I)]))) -#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I))))) - - -/* -** This a more complex version of findCell() that works for -** pages that do contain overflow cells. -*/ -static u8 *findOverflowCell(MemPage *pPage, int iCell){ - int i; - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - for(i=pPage->nOverflow-1; i>=0; i--){ - int k; - k = pPage->aiOvfl[i]; - if( k<=iCell ){ - if( k==iCell ){ - return pPage->apOvfl[i]; - } - iCell--; - } - } - return findCell(pPage, iCell); -} - -/* -** Parse a cell content block and fill in the CellInfo structure. There -** are two versions of this function. btreeParseCell() takes a -** cell index as the second argument and btreeParseCellPtr() -** takes a pointer to the body of the cell as its second argument. -** -** Within this file, the parseCell() macro can be called instead of -** btreeParseCellPtr(). Using some compilers, this will be faster. -*/ -static void btreeParseCellPtr( - MemPage *pPage, /* Page containing the cell */ - u8 *pCell, /* Pointer to the cell text. */ - CellInfo *pInfo /* Fill in this structure */ -){ - u16 n; /* Number bytes in cell content header */ - u32 nPayload; /* Number of bytes of cell payload */ - - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - - pInfo->pCell = pCell; - assert( pPage->leaf==0 || pPage->leaf==1 ); - n = pPage->childPtrSize; - assert( n==4-4*pPage->leaf ); - if( pPage->intKey ){ - if( pPage->hasData ){ - n += getVarint32(&pCell[n], nPayload); - }else{ - nPayload = 0; - } - n += getVarint(&pCell[n], (u64*)&pInfo->nKey); - pInfo->nData = nPayload; - }else{ - pInfo->nData = 0; - n += getVarint32(&pCell[n], nPayload); - pInfo->nKey = nPayload; - } - pInfo->nPayload = nPayload; - pInfo->nHeader = n; - testcase( nPayload==pPage->maxLocal ); - testcase( nPayload==pPage->maxLocal+1 ); - if( likely(nPayload<=pPage->maxLocal) ){ - /* This is the (easy) common case where the entire payload fits - ** on the local page. No overflow is required. - */ - if( (pInfo->nSize = (u16)(n+nPayload))<4 ) pInfo->nSize = 4; - pInfo->nLocal = (u16)nPayload; - pInfo->iOverflow = 0; - }else{ - /* If the payload will not fit completely on the local page, we have - ** to decide how much to store locally and how much to spill onto - ** overflow pages. The strategy is to minimize the amount of unused - ** space on overflow pages while keeping the amount of local storage - ** in between minLocal and maxLocal. - ** - ** Warning: changing the way overflow payload is distributed in any - ** way will result in an incompatible file format. - */ - int minLocal; /* Minimum amount of payload held locally */ - int maxLocal; /* Maximum amount of payload held locally */ - int surplus; /* Overflow payload available for local storage */ - - minLocal = pPage->minLocal; - maxLocal = pPage->maxLocal; - surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4); - testcase( surplus==maxLocal ); - testcase( surplus==maxLocal+1 ); - if( surplus <= maxLocal ){ - pInfo->nLocal = (u16)surplus; - }else{ - pInfo->nLocal = (u16)minLocal; - } - pInfo->iOverflow = (u16)(pInfo->nLocal + n); - pInfo->nSize = pInfo->iOverflow + 4; - } -} -#define parseCell(pPage, iCell, pInfo) \ - btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo)) -static void btreeParseCell( - MemPage *pPage, /* Page containing the cell */ - int iCell, /* The cell index. First cell is 0 */ - CellInfo *pInfo /* Fill in this structure */ -){ - parseCell(pPage, iCell, pInfo); -} - -/* -** Compute the total number of bytes that a Cell needs in the cell -** data area of the btree-page. The return number includes the cell -** data header and the local payload, but not any overflow page or -** the space used by the cell pointer. -*/ -static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ - u8 *pIter = &pCell[pPage->childPtrSize]; - u32 nSize; - -#ifdef SQLITE_DEBUG - /* The value returned by this function should always be the same as - ** the (CellInfo.nSize) value found by doing a full parse of the - ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of - ** this function verifies that this invariant is not violated. */ - CellInfo debuginfo; - btreeParseCellPtr(pPage, pCell, &debuginfo); -#endif - - if( pPage->intKey ){ - u8 *pEnd; - if( pPage->hasData ){ - pIter += getVarint32(pIter, nSize); - }else{ - nSize = 0; - } - - /* pIter now points at the 64-bit integer key value, a variable length - ** integer. The following block moves pIter to point at the first byte - ** past the end of the key value. */ - pEnd = &pIter[9]; - while( (*pIter++)&0x80 && pItermaxLocal ); - testcase( nSize==pPage->maxLocal+1 ); - if( nSize>pPage->maxLocal ){ - int minLocal = pPage->minLocal; - nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); - testcase( nSize==pPage->maxLocal ); - testcase( nSize==pPage->maxLocal+1 ); - if( nSize>pPage->maxLocal ){ - nSize = minLocal; - } - nSize += 4; - } - nSize += (u32)(pIter - pCell); - - /* The minimum size of any cell is 4 bytes. */ - if( nSize<4 ){ - nSize = 4; - } - - assert( nSize==debuginfo.nSize ); - return (u16)nSize; -} - -#ifdef SQLITE_DEBUG -/* This variation on cellSizePtr() is used inside of assert() statements -** only. */ -static u16 cellSize(MemPage *pPage, int iCell){ - return cellSizePtr(pPage, findCell(pPage, iCell)); -} -#endif - -#ifndef SQLITE_OMIT_AUTOVACUUM -/* -** If the cell pCell, part of page pPage contains a pointer -** to an overflow page, insert an entry into the pointer-map -** for the overflow page. -*/ -static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){ - CellInfo info; - if( *pRC ) return; - assert( pCell!=0 ); - btreeParseCellPtr(pPage, pCell, &info); - assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload ); - if( info.iOverflow ){ - Pgno ovfl = get4byte(&pCell[info.iOverflow]); - ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); - } -} -#endif - - -/* -** Defragment the page given. All Cells are moved to the -** end of the page and all free space is collected into one -** big FreeBlk that occurs in between the header and cell -** pointer array and the cell content area. -*/ -static int defragmentPage(MemPage *pPage){ - int i; /* Loop counter */ - int pc; /* Address of a i-th cell */ - int hdr; /* Offset to the page header */ - int size; /* Size of a cell */ - int usableSize; /* Number of usable bytes on a page */ - int cellOffset; /* Offset to the cell pointer array */ - int cbrk; /* Offset to the cell content area */ - int nCell; /* Number of cells on the page */ - unsigned char *data; /* The page data */ - unsigned char *temp; /* Temp area for cell content */ - int iCellFirst; /* First allowable cell index */ - int iCellLast; /* Last possible cell index */ - - - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( pPage->pBt!=0 ); - assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); - assert( pPage->nOverflow==0 ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - temp = sqlite3PagerTempSpace(pPage->pBt->pPager); - data = pPage->aData; - hdr = pPage->hdrOffset; - cellOffset = pPage->cellOffset; - nCell = pPage->nCell; - assert( nCell==get2byte(&data[hdr+3]) ); - usableSize = pPage->pBt->usableSize; - cbrk = get2byte(&data[hdr+5]); - memcpy(&temp[cbrk], &data[cbrk], usableSize - cbrk); - cbrk = usableSize; - iCellFirst = cellOffset + 2*nCell; - iCellLast = usableSize - 4; - for(i=0; iiCellLast ){ - return SQLITE_CORRUPT_BKPT; - } -#endif - assert( pc>=iCellFirst && pc<=iCellLast ); - size = cellSizePtr(pPage, &temp[pc]); - cbrk -= size; -#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) - if( cbrkusableSize ){ - return SQLITE_CORRUPT_BKPT; - } -#endif - assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); - testcase( cbrk+size==usableSize ); - testcase( pc+size==usableSize ); - memcpy(&data[cbrk], &temp[pc], size); - put2byte(pAddr, cbrk); - } - assert( cbrk>=iCellFirst ); - put2byte(&data[hdr+5], cbrk); - data[hdr+1] = 0; - data[hdr+2] = 0; - data[hdr+7] = 0; - memset(&data[iCellFirst], 0, cbrk-iCellFirst); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - if( cbrk-iCellFirst!=pPage->nFree ){ - return SQLITE_CORRUPT_BKPT; - } - return SQLITE_OK; -} - -/* -** Allocate nByte bytes of space from within the B-Tree page passed -** as the first argument. Write into *pIdx the index into pPage->aData[] -** of the first byte of allocated space. Return either SQLITE_OK or -** an error code (usually SQLITE_CORRUPT). -** -** The caller guarantees that there is sufficient space to make the -** allocation. This routine might need to defragment in order to bring -** all the space together, however. This routine will avoid using -** the first two bytes past the cell pointer area since presumably this -** allocation is being made in order to insert a new cell, so we will -** also end up needing a new cell pointer. -*/ -static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ - const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ - u8 * const data = pPage->aData; /* Local cache of pPage->aData */ - int nFrag; /* Number of fragmented bytes on pPage */ - int top; /* First byte of cell content area */ - int gap; /* First byte of gap between cell pointers and cell content */ - int rc; /* Integer return code */ - int usableSize; /* Usable size of the page */ - - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( pPage->pBt ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( nByte>=0 ); /* Minimum cell size is 4 */ - assert( pPage->nFree>=nByte ); - assert( pPage->nOverflow==0 ); - usableSize = pPage->pBt->usableSize; - assert( nByte < usableSize-8 ); - - nFrag = data[hdr+7]; - assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); - gap = pPage->cellOffset + 2*pPage->nCell; - top = get2byteNotZero(&data[hdr+5]); - if( gap>top ) return SQLITE_CORRUPT_BKPT; - testcase( gap+2==top ); - testcase( gap+1==top ); - testcase( gap==top ); - - if( nFrag>=60 ){ - /* Always defragment highly fragmented pages */ - rc = defragmentPage(pPage); - if( rc ) return rc; - top = get2byteNotZero(&data[hdr+5]); - }else if( gap+2<=top ){ - /* Search the freelist looking for a free slot big enough to satisfy - ** the request. The allocation is made from the first free slot in - ** the list that is large enough to accomadate it. - */ - int pc, addr; - for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){ - int size; /* Size of the free slot */ - if( pc>usableSize-4 || pc=nByte ){ - int x = size - nByte; - testcase( x==4 ); - testcase( x==3 ); - if( x<4 ){ - /* Remove the slot from the free-list. Update the number of - ** fragmented bytes within the page. */ - memcpy(&data[addr], &data[pc], 2); - data[hdr+7] = (u8)(nFrag + x); - }else if( size+pc > usableSize ){ - return SQLITE_CORRUPT_BKPT; - }else{ - /* The slot remains on the free-list. Reduce its size to account - ** for the portion used by the new allocation. */ - put2byte(&data[pc+2], x); - } - *pIdx = pc + x; - return SQLITE_OK; - } - } - } - - /* Check to make sure there is enough space in the gap to satisfy - ** the allocation. If not, defragment. - */ - testcase( gap+2+nByte==top ); - if( gap+2+nByte>top ){ - rc = defragmentPage(pPage); - if( rc ) return rc; - top = get2byteNotZero(&data[hdr+5]); - assert( gap+nByte<=top ); - } - - - /* Allocate memory from the gap in between the cell pointer array - ** and the cell content area. The btreeInitPage() call has already - ** validated the freelist. Given that the freelist is valid, there - ** is no way that the allocation can extend off the end of the page. - ** The assert() below verifies the previous sentence. - */ - top -= nByte; - put2byte(&data[hdr+5], top); - assert( top+nByte <= (int)pPage->pBt->usableSize ); - *pIdx = top; - return SQLITE_OK; -} - -/* -** Return a section of the pPage->aData to the freelist. -** The first byte of the new free block is pPage->aDisk[start] -** and the size of the block is "size" bytes. -** -** Most of the effort here is involved in coalesing adjacent -** free blocks into a single big free block. -*/ -static int freeSpace(MemPage *pPage, int start, int size){ - int addr, pbegin, hdr; - int iLast; /* Largest possible freeblock offset */ - unsigned char *data = pPage->aData; - - assert( pPage->pBt!=0 ); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( start>=pPage->hdrOffset+6+pPage->childPtrSize ); - assert( (start + size) <= (int)pPage->pBt->usableSize ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( size>=0 ); /* Minimum cell size is 4 */ - - if( pPage->pBt->btsFlags & BTS_SECURE_DELETE ){ - /* Overwrite deleted information with zeros when the secure_delete - ** option is enabled */ - memset(&data[start], 0, size); - } - - /* Add the space back into the linked list of freeblocks. Note that - ** even though the freeblock list was checked by btreeInitPage(), - ** btreeInitPage() did not detect overlapping cells or - ** freeblocks that overlapped cells. Nor does it detect when the - ** cell content area exceeds the value in the page header. If these - ** situations arise, then subsequent insert operations might corrupt - ** the freelist. So we do need to check for corruption while scanning - ** the freelist. - */ - hdr = pPage->hdrOffset; - addr = hdr + 1; - iLast = pPage->pBt->usableSize - 4; - assert( start<=iLast ); - while( (pbegin = get2byte(&data[addr]))0 ){ - if( pbeginiLast ){ - return SQLITE_CORRUPT_BKPT; - } - assert( pbegin>addr || pbegin==0 ); - put2byte(&data[addr], start); - put2byte(&data[start], pbegin); - put2byte(&data[start+2], size); - pPage->nFree = pPage->nFree + (u16)size; - - /* Coalesce adjacent free blocks */ - addr = hdr + 1; - while( (pbegin = get2byte(&data[addr]))>0 ){ - int pnext, psize, x; - assert( pbegin>addr ); - assert( pbegin <= (int)pPage->pBt->usableSize-4 ); - pnext = get2byte(&data[pbegin]); - psize = get2byte(&data[pbegin+2]); - if( pbegin + psize + 3 >= pnext && pnext>0 ){ - int frag = pnext - (pbegin+psize); - if( (frag<0) || (frag>(int)data[hdr+7]) ){ - return SQLITE_CORRUPT_BKPT; - } - data[hdr+7] -= (u8)frag; - x = get2byte(&data[pnext]); - put2byte(&data[pbegin], x); - x = pnext + get2byte(&data[pnext+2]) - pbegin; - put2byte(&data[pbegin+2], x); - }else{ - addr = pbegin; - } - } - - /* If the cell content area begins with a freeblock, remove it. */ - if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){ - int top; - pbegin = get2byte(&data[hdr+1]); - memcpy(&data[hdr+1], &data[pbegin], 2); - top = get2byte(&data[hdr+5]) + get2byte(&data[pbegin+2]); - put2byte(&data[hdr+5], top); - } - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - return SQLITE_OK; -} - -/* -** Decode the flags byte (the first byte of the header) for a page -** and initialize fields of the MemPage structure accordingly. -** -** Only the following combinations are supported. Anything different -** indicates a corrupt database files: -** -** PTF_ZERODATA -** PTF_ZERODATA | PTF_LEAF -** PTF_LEAFDATA | PTF_INTKEY -** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF -*/ -static int decodeFlags(MemPage *pPage, int flagByte){ - BtShared *pBt; /* A copy of pPage->pBt */ - - assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 ); - flagByte &= ~PTF_LEAF; - pPage->childPtrSize = 4-4*pPage->leaf; - pBt = pPage->pBt; - if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ - pPage->intKey = 1; - pPage->hasData = pPage->leaf; - pPage->maxLocal = pBt->maxLeaf; - pPage->minLocal = pBt->minLeaf; - }else if( flagByte==PTF_ZERODATA ){ - pPage->intKey = 0; - pPage->hasData = 0; - pPage->maxLocal = pBt->maxLocal; - pPage->minLocal = pBt->minLocal; - }else{ - return SQLITE_CORRUPT_BKPT; - } - pPage->max1bytePayload = pBt->max1bytePayload; - return SQLITE_OK; -} - -/* -** Initialize the auxiliary information for a disk block. -** -** Return SQLITE_OK on success. If we see that the page does -** not contain a well-formed database page, then return -** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not -** guarantee that the page is well-formed. It only shows that -** we failed to detect any corruption. -*/ -static int btreeInitPage(MemPage *pPage){ - - assert( pPage->pBt!=0 ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) ); - assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) ); - assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); - - if( !pPage->isInit ){ - u16 pc; /* Address of a freeblock within pPage->aData[] */ - u8 hdr; /* Offset to beginning of page header */ - u8 *data; /* Equal to pPage->aData */ - BtShared *pBt; /* The main btree structure */ - int usableSize; /* Amount of usable space on each page */ - u16 cellOffset; /* Offset from start of page to first cell pointer */ - int nFree; /* Number of unused bytes on the page */ - int top; /* First byte of the cell content area */ - int iCellFirst; /* First allowable cell or freeblock offset */ - int iCellLast; /* Last possible cell or freeblock offset */ - - pBt = pPage->pBt; - - hdr = pPage->hdrOffset; - data = pPage->aData; - if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT; - assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); - pPage->maskPage = (u16)(pBt->pageSize - 1); - pPage->nOverflow = 0; - usableSize = pBt->usableSize; - pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf; - pPage->aDataEnd = &data[usableSize]; - pPage->aCellIdx = &data[cellOffset]; - top = get2byteNotZero(&data[hdr+5]); - pPage->nCell = get2byte(&data[hdr+3]); - if( pPage->nCell>MX_CELL(pBt) ){ - /* To many cells for a single page. The page must be corrupt */ - return SQLITE_CORRUPT_BKPT; - } - testcase( pPage->nCell==MX_CELL(pBt) ); - - /* A malformed database page might cause us to read past the end - ** of page when parsing a cell. - ** - ** The following block of code checks early to see if a cell extends - ** past the end of a page boundary and causes SQLITE_CORRUPT to be - ** returned if it does. - */ - iCellFirst = cellOffset + 2*pPage->nCell; - iCellLast = usableSize - 4; -#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) - { - int i; /* Index into the cell pointer array */ - int sz; /* Size of a cell */ - - if( !pPage->leaf ) iCellLast--; - for(i=0; inCell; i++){ - pc = get2byte(&data[cellOffset+i*2]); - testcase( pc==iCellFirst ); - testcase( pc==iCellLast ); - if( pciCellLast ){ - return SQLITE_CORRUPT_BKPT; - } - sz = cellSizePtr(pPage, &data[pc]); - testcase( pc+sz==usableSize ); - if( pc+sz>usableSize ){ - return SQLITE_CORRUPT_BKPT; - } - } - if( !pPage->leaf ) iCellLast++; - } -#endif - - /* Compute the total free space on the page */ - pc = get2byte(&data[hdr+1]); - nFree = data[hdr+7] + top; - while( pc>0 ){ - u16 next, size; - if( pciCellLast ){ - /* Start of free block is off the page */ - return SQLITE_CORRUPT_BKPT; - } - next = get2byte(&data[pc]); - size = get2byte(&data[pc+2]); - if( (next>0 && next<=pc+size+3) || pc+size>usableSize ){ - /* Free blocks must be in ascending order. And the last byte of - ** the free-block must lie on the database page. */ - return SQLITE_CORRUPT_BKPT; - } - nFree = nFree + size; - pc = next; - } - - /* At this point, nFree contains the sum of the offset to the start - ** of the cell-content area plus the number of free bytes within - ** the cell-content area. If this is greater than the usable-size - ** of the page, then the page must be corrupted. This check also - ** serves to verify that the offset to the start of the cell-content - ** area, according to the page header, lies within the page. - */ - if( nFree>usableSize ){ - return SQLITE_CORRUPT_BKPT; - } - pPage->nFree = (u16)(nFree - iCellFirst); - pPage->isInit = 1; - } - return SQLITE_OK; -} - -/* -** Set up a raw page so that it looks like a database page holding -** no entries. -*/ -static void zeroPage(MemPage *pPage, int flags){ - unsigned char *data = pPage->aData; - BtShared *pBt = pPage->pBt; - u8 hdr = pPage->hdrOffset; - u16 first; - - assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno ); - assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); - assert( sqlite3PagerGetData(pPage->pDbPage) == data ); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( sqlite3_mutex_held(pBt->mutex) ); - if( pBt->btsFlags & BTS_SECURE_DELETE ){ - memset(&data[hdr], 0, pBt->usableSize - hdr); - } - data[hdr] = (char)flags; - first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0); - memset(&data[hdr+1], 0, 4); - data[hdr+7] = 0; - put2byte(&data[hdr+5], pBt->usableSize); - pPage->nFree = (u16)(pBt->usableSize - first); - decodeFlags(pPage, flags); - pPage->hdrOffset = hdr; - pPage->cellOffset = first; - pPage->aDataEnd = &data[pBt->usableSize]; - pPage->aCellIdx = &data[first]; - pPage->nOverflow = 0; - assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); - pPage->maskPage = (u16)(pBt->pageSize - 1); - pPage->nCell = 0; - pPage->isInit = 1; -} - - -/* -** Convert a DbPage obtained from the pager into a MemPage used by -** the btree layer. -*/ -static MemPage *btreePageFromDbPage(DbPage *pDbPage, Pgno pgno, BtShared *pBt){ - MemPage *pPage = (MemPage*)sqlite3PagerGetExtra(pDbPage); - pPage->aData = sqlite3PagerGetData(pDbPage); - pPage->pDbPage = pDbPage; - pPage->pBt = pBt; - pPage->pgno = pgno; - pPage->hdrOffset = pPage->pgno==1 ? 100 : 0; - return pPage; -} - -/* -** Get a page from the pager. Initialize the MemPage.pBt and -** MemPage.aData elements if needed. -** -** If the noContent flag is set, it means that we do not care about -** the content of the page at this time. So do not go to the disk -** to fetch the content. Just fill in the content with zeros for now. -** If in the future we call sqlite3PagerWrite() on this page, that -** means we have started to be concerned about content and the disk -** read should occur at that point. -*/ -static int btreeGetPage( - BtShared *pBt, /* The btree */ - Pgno pgno, /* Number of the page to fetch */ - MemPage **ppPage, /* Return the page in this parameter */ - int noContent /* Do not load page content if true */ -){ - int rc; - DbPage *pDbPage; - - assert( sqlite3_mutex_held(pBt->mutex) ); - rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent); - if( rc ) return rc; - *ppPage = btreePageFromDbPage(pDbPage, pgno, pBt); - return SQLITE_OK; -} - -/* -** Retrieve a page from the pager cache. If the requested page is not -** already in the pager cache return NULL. Initialize the MemPage.pBt and -** MemPage.aData elements if needed. -*/ -static MemPage *btreePageLookup(BtShared *pBt, Pgno pgno){ - DbPage *pDbPage; - assert( sqlite3_mutex_held(pBt->mutex) ); - pDbPage = sqlite3PagerLookup(pBt->pPager, pgno); - if( pDbPage ){ - return btreePageFromDbPage(pDbPage, pgno, pBt); - } - return 0; -} - -/* -** Return the size of the database file in pages. If there is any kind of -** error, return ((unsigned int)-1). -*/ -static Pgno btreePagecount(BtShared *pBt){ - return pBt->nPage; -} -SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree *p){ - assert( sqlite3BtreeHoldsMutex(p) ); - assert( ((p->pBt->nPage)&0x8000000)==0 ); - return (int)btreePagecount(p->pBt); -} - -/* -** Get a page from the pager and initialize it. This routine is just a -** convenience wrapper around separate calls to btreeGetPage() and -** btreeInitPage(). -** -** If an error occurs, then the value *ppPage is set to is undefined. It -** may remain unchanged, or it may be set to an invalid value. -*/ -static int getAndInitPage( - BtShared *pBt, /* The database file */ - Pgno pgno, /* Number of the page to get */ - MemPage **ppPage /* Write the page pointer here */ -){ - int rc; - assert( sqlite3_mutex_held(pBt->mutex) ); - - if( pgno>btreePagecount(pBt) ){ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = btreeGetPage(pBt, pgno, ppPage, 0); - if( rc==SQLITE_OK ){ - rc = btreeInitPage(*ppPage); - if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - } - } - } - - testcase( pgno==0 ); - assert( pgno!=0 || rc==SQLITE_CORRUPT ); - return rc; -} - -/* -** Release a MemPage. This should be called once for each prior -** call to btreeGetPage. -*/ -static void releasePage(MemPage *pPage){ - if( pPage ){ - assert( pPage->aData ); - assert( pPage->pBt ); - assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); - assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - sqlite3PagerUnref(pPage->pDbPage); - } -} - -/* -** During a rollback, when the pager reloads information into the cache -** so that the cache is restored to its original state at the start of -** the transaction, for each page restored this routine is called. -** -** This routine needs to reset the extra data section at the end of the -** page to agree with the restored data. -*/ -static void pageReinit(DbPage *pData){ - MemPage *pPage; - pPage = (MemPage *)sqlite3PagerGetExtra(pData); - assert( sqlite3PagerPageRefcount(pData)>0 ); - if( pPage->isInit ){ - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - pPage->isInit = 0; - if( sqlite3PagerPageRefcount(pData)>1 ){ - /* pPage might not be a btree page; it might be an overflow page - ** or ptrmap page or a free page. In those cases, the following - ** call to btreeInitPage() will likely return SQLITE_CORRUPT. - ** But no harm is done by this. And it is very important that - ** btreeInitPage() be called on every btree page so we make - ** the call for every page that comes in for re-initing. */ - btreeInitPage(pPage); - } - } -} - -/* -** Invoke the busy handler for a btree. -*/ -static int btreeInvokeBusyHandler(void *pArg){ - BtShared *pBt = (BtShared*)pArg; - assert( pBt->db ); - assert( sqlite3_mutex_held(pBt->db->mutex) ); - return sqlite3InvokeBusyHandler(&pBt->db->busyHandler); -} - -/* -** Open a database file. -** -** zFilename is the name of the database file. If zFilename is NULL -** then an ephemeral database is created. The ephemeral database might -** be exclusively in memory, or it might use a disk-based memory cache. -** Either way, the ephemeral database will be automatically deleted -** when sqlite3BtreeClose() is called. -** -** If zFilename is ":memory:" then an in-memory database is created -** that is automatically destroyed when it is closed. -** -** The "flags" parameter is a bitmask that might contain bits like -** BTREE_OMIT_JOURNAL and/or BTREE_MEMORY. -** -** If the database is already opened in the same database connection -** and we are in shared cache mode, then the open will fail with an -** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared -** objects in the same database connection since doing so will lead -** to problems with locking. -*/ -SQLITE_PRIVATE int sqlite3BtreeOpen( - sqlite3_vfs *pVfs, /* VFS to use for this b-tree */ - const char *zFilename, /* Name of the file containing the BTree database */ - sqlite3 *db, /* Associated database handle */ - Btree **ppBtree, /* Pointer to new Btree object written here */ - int flags, /* Options */ - int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */ -){ - BtShared *pBt = 0; /* Shared part of btree structure */ - Btree *p; /* Handle to return */ - sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */ - int rc = SQLITE_OK; /* Result code from this function */ - u8 nReserve; /* Byte of unused space on each page */ - unsigned char zDbHeader[100]; /* Database header content */ - - /* True if opening an ephemeral, temporary database */ - const int isTempDb = zFilename==0 || zFilename[0]==0; - - /* Set the variable isMemdb to true for an in-memory database, or - ** false for a file-based database. - */ -#ifdef SQLITE_OMIT_MEMORYDB - const int isMemdb = 0; -#else - const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0) - || (isTempDb && sqlite3TempInMemory(db)) - || (vfsFlags & SQLITE_OPEN_MEMORY)!=0; -#endif - - assert( db!=0 ); - assert( pVfs!=0 ); - assert( sqlite3_mutex_held(db->mutex) ); - assert( (flags&0xff)==flags ); /* flags fit in 8 bits */ - - /* Only a BTREE_SINGLE database can be BTREE_UNORDERED */ - assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 ); - - /* A BTREE_SINGLE database is always a temporary and/or ephemeral */ - assert( (flags & BTREE_SINGLE)==0 || isTempDb ); - - if( isMemdb ){ - flags |= BTREE_MEMORY; - } - if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){ - vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB; - } - p = sqlite3MallocZero(sizeof(Btree)); - if( !p ){ - return SQLITE_NOMEM; - } - p->inTrans = TRANS_NONE; - p->db = db; -#ifndef SQLITE_OMIT_SHARED_CACHE - p->lock.pBtree = p; - p->lock.iTable = 1; -#endif - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* - ** If this Btree is a candidate for shared cache, try to find an - ** existing BtShared object that we can share with - */ - if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){ - if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ - int nFullPathname = pVfs->mxPathname+1; - char *zFullPathname = sqlite3Malloc(nFullPathname); - MUTEX_LOGIC( sqlite3_mutex *mutexShared; ) - p->sharable = 1; - if( !zFullPathname ){ - sqlite3_free(p); - return SQLITE_NOMEM; - } - if( isMemdb ){ - memcpy(zFullPathname, zFilename, sqlite3Strlen30(zFilename)+1); - }else{ - rc = sqlite3OsFullPathname(pVfs, zFilename, - nFullPathname, zFullPathname); - if( rc ){ - sqlite3_free(zFullPathname); - sqlite3_free(p); - return rc; - } - } -#if SQLITE_THREADSAFE - mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN); - sqlite3_mutex_enter(mutexOpen); - mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); - sqlite3_mutex_enter(mutexShared); -#endif - for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){ - assert( pBt->nRef>0 ); - if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager, 0)) - && sqlite3PagerVfs(pBt->pPager)==pVfs ){ - int iDb; - for(iDb=db->nDb-1; iDb>=0; iDb--){ - Btree *pExisting = db->aDb[iDb].pBt; - if( pExisting && pExisting->pBt==pBt ){ - sqlite3_mutex_leave(mutexShared); - sqlite3_mutex_leave(mutexOpen); - sqlite3_free(zFullPathname); - sqlite3_free(p); - return SQLITE_CONSTRAINT; - } - } - p->pBt = pBt; - pBt->nRef++; - break; - } - } - sqlite3_mutex_leave(mutexShared); - sqlite3_free(zFullPathname); - } -#ifdef SQLITE_DEBUG - else{ - /* In debug mode, we mark all persistent databases as sharable - ** even when they are not. This exercises the locking code and - ** gives more opportunity for asserts(sqlite3_mutex_held()) - ** statements to find locking problems. - */ - p->sharable = 1; - } -#endif - } -#endif - if( pBt==0 ){ - /* - ** The following asserts make sure that structures used by the btree are - ** the right size. This is to guard against size changes that result - ** when compiling on a different architecture. - */ - assert( sizeof(i64)==8 || sizeof(i64)==4 ); - assert( sizeof(u64)==8 || sizeof(u64)==4 ); - assert( sizeof(u32)==4 ); - assert( sizeof(u16)==2 ); - assert( sizeof(Pgno)==4 ); - - pBt = sqlite3MallocZero( sizeof(*pBt) ); - if( pBt==0 ){ - rc = SQLITE_NOMEM; - goto btree_open_out; - } - rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, - EXTRA_SIZE, flags, vfsFlags, pageReinit); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); - } - if( rc!=SQLITE_OK ){ - goto btree_open_out; - } - pBt->openFlags = (u8)flags; - pBt->db = db; - sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); - p->pBt = pBt; - - pBt->pCursor = 0; - pBt->pPage1 = 0; - if( sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY; -#ifdef SQLITE_SECURE_DELETE - pBt->btsFlags |= BTS_SECURE_DELETE; -#endif - pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16); - if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE - || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ - pBt->pageSize = 0; -#ifndef SQLITE_OMIT_AUTOVACUUM - /* If the magic name ":memory:" will create an in-memory database, then - ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if - ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if - ** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a - ** regular file-name. In this case the auto-vacuum applies as per normal. - */ - if( zFilename && !isMemdb ){ - pBt->autoVacuum = (SQLITE_DEFAULT_AUTOVACUUM ? 1 : 0); - pBt->incrVacuum = (SQLITE_DEFAULT_AUTOVACUUM==2 ? 1 : 0); - } -#endif - nReserve = 0; - }else{ - nReserve = zDbHeader[20]; - pBt->btsFlags |= BTS_PAGESIZE_FIXED; -#ifndef SQLITE_OMIT_AUTOVACUUM - pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0); - pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0); -#endif - } - rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); - if( rc ) goto btree_open_out; - pBt->usableSize = pBt->pageSize - nReserve; - assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */ - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* Add the new BtShared object to the linked list sharable BtShareds. - */ - if( p->sharable ){ - MUTEX_LOGIC( sqlite3_mutex *mutexShared; ) - pBt->nRef = 1; - MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);) - if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){ - pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST); - if( pBt->mutex==0 ){ - rc = SQLITE_NOMEM; - db->mallocFailed = 0; - goto btree_open_out; - } - } - sqlite3_mutex_enter(mutexShared); - pBt->pNext = GLOBAL(BtShared*,sqlite3SharedCacheList); - GLOBAL(BtShared*,sqlite3SharedCacheList) = pBt; - sqlite3_mutex_leave(mutexShared); - } -#endif - } - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* If the new Btree uses a sharable pBtShared, then link the new - ** Btree into the list of all sharable Btrees for the same connection. - ** The list is kept in ascending order by pBt address. - */ - if( p->sharable ){ - int i; - Btree *pSib; - for(i=0; inDb; i++){ - if( (pSib = db->aDb[i].pBt)!=0 && pSib->sharable ){ - while( pSib->pPrev ){ pSib = pSib->pPrev; } - if( p->pBtpBt ){ - p->pNext = pSib; - p->pPrev = 0; - pSib->pPrev = p; - }else{ - while( pSib->pNext && pSib->pNext->pBtpBt ){ - pSib = pSib->pNext; - } - p->pNext = pSib->pNext; - p->pPrev = pSib; - if( p->pNext ){ - p->pNext->pPrev = p; - } - pSib->pNext = p; - } - break; - } - } - } -#endif - *ppBtree = p; - -btree_open_out: - if( rc!=SQLITE_OK ){ - if( pBt && pBt->pPager ){ - sqlite3PagerClose(pBt->pPager); - } - sqlite3_free(pBt); - sqlite3_free(p); - *ppBtree = 0; - }else{ - /* If the B-Tree was successfully opened, set the pager-cache size to the - ** default value. Except, when opening on an existing shared pager-cache, - ** do not change the pager-cache size. - */ - if( sqlite3BtreeSchema(p, 0, 0)==0 ){ - sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE); - } - } - if( mutexOpen ){ - assert( sqlite3_mutex_held(mutexOpen) ); - sqlite3_mutex_leave(mutexOpen); - } - return rc; -} - -/* -** Decrement the BtShared.nRef counter. When it reaches zero, -** remove the BtShared structure from the sharing list. Return -** true if the BtShared.nRef counter reaches zero and return -** false if it is still positive. -*/ -static int removeFromSharingList(BtShared *pBt){ -#ifndef SQLITE_OMIT_SHARED_CACHE - MUTEX_LOGIC( sqlite3_mutex *pMaster; ) - BtShared *pList; - int removed = 0; - - assert( sqlite3_mutex_notheld(pBt->mutex) ); - MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) - sqlite3_mutex_enter(pMaster); - pBt->nRef--; - if( pBt->nRef<=0 ){ - if( GLOBAL(BtShared*,sqlite3SharedCacheList)==pBt ){ - GLOBAL(BtShared*,sqlite3SharedCacheList) = pBt->pNext; - }else{ - pList = GLOBAL(BtShared*,sqlite3SharedCacheList); - while( ALWAYS(pList) && pList->pNext!=pBt ){ - pList=pList->pNext; - } - if( ALWAYS(pList) ){ - pList->pNext = pBt->pNext; - } - } - if( SQLITE_THREADSAFE ){ - sqlite3_mutex_free(pBt->mutex); - } - removed = 1; - } - sqlite3_mutex_leave(pMaster); - return removed; -#else - return 1; -#endif -} - -/* -** Make sure pBt->pTmpSpace points to an allocation of -** MX_CELL_SIZE(pBt) bytes. -*/ -static void allocateTempSpace(BtShared *pBt){ - if( !pBt->pTmpSpace ){ - pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize ); - } -} - -/* -** Free the pBt->pTmpSpace allocation -*/ -static void freeTempSpace(BtShared *pBt){ - sqlite3PageFree( pBt->pTmpSpace); - pBt->pTmpSpace = 0; -} - -/* -** Close an open database and invalidate all cursors. -*/ -SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){ - BtShared *pBt = p->pBt; - BtCursor *pCur; - - /* Close all cursors opened via this handle. */ - assert( sqlite3_mutex_held(p->db->mutex) ); - sqlite3BtreeEnter(p); - pCur = pBt->pCursor; - while( pCur ){ - BtCursor *pTmp = pCur; - pCur = pCur->pNext; - if( pTmp->pBtree==p ){ - sqlite3BtreeCloseCursor(pTmp); - } - } - - /* Rollback any active transaction and free the handle structure. - ** The call to sqlite3BtreeRollback() drops any table-locks held by - ** this handle. - */ - sqlite3BtreeRollback(p, SQLITE_OK); - sqlite3BtreeLeave(p); - - /* If there are still other outstanding references to the shared-btree - ** structure, return now. The remainder of this procedure cleans - ** up the shared-btree. - */ - assert( p->wantToLock==0 && p->locked==0 ); - if( !p->sharable || removeFromSharingList(pBt) ){ - /* The pBt is no longer on the sharing list, so we can access - ** it without having to hold the mutex. - ** - ** Clean out and delete the BtShared object. - */ - assert( !pBt->pCursor ); - sqlite3PagerClose(pBt->pPager); - if( pBt->xFreeSchema && pBt->pSchema ){ - pBt->xFreeSchema(pBt->pSchema); - } - sqlite3DbFree(0, pBt->pSchema); - freeTempSpace(pBt); - sqlite3_free(pBt); - } - -#ifndef SQLITE_OMIT_SHARED_CACHE - assert( p->wantToLock==0 ); - assert( p->locked==0 ); - if( p->pPrev ) p->pPrev->pNext = p->pNext; - if( p->pNext ) p->pNext->pPrev = p->pPrev; -#endif - - sqlite3_free(p); - return SQLITE_OK; -} - -/* -** Change the limit on the number of pages allowed in the cache. -** -** The maximum number of cache pages is set to the absolute -** value of mxPage. If mxPage is negative, the pager will -** operate asynchronously - it will not stop to do fsync()s -** to insure data is written to the disk surface before -** continuing. Transactions still work if synchronous is off, -** and the database cannot be corrupted if this program -** crashes. But if the operating system crashes or there is -** an abrupt power failure when synchronous is off, the database -** could be left in an inconsistent and unrecoverable state. -** Synchronous is on by default so database corruption is not -** normally a worry. -*/ -SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){ - BtShared *pBt = p->pBt; - assert( sqlite3_mutex_held(p->db->mutex) ); - sqlite3BtreeEnter(p); - sqlite3PagerSetCachesize(pBt->pPager, mxPage); - sqlite3BtreeLeave(p); - return SQLITE_OK; -} - -/* -** Change the way data is synced to disk in order to increase or decrease -** how well the database resists damage due to OS crashes and power -** failures. Level 1 is the same as asynchronous (no syncs() occur and -** there is a high probability of damage) Level 2 is the default. There -** is a very low but non-zero probability of damage. Level 3 reduces the -** probability of damage to near zero but with a write performance reduction. -*/ -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel( - Btree *p, /* The btree to set the safety level on */ - int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */ - int fullSync, /* PRAGMA fullfsync. */ - int ckptFullSync /* PRAGMA checkpoint_fullfync */ -){ - BtShared *pBt = p->pBt; - assert( sqlite3_mutex_held(p->db->mutex) ); - assert( level>=1 && level<=3 ); - sqlite3BtreeEnter(p); - sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync); - sqlite3BtreeLeave(p); - return SQLITE_OK; -} -#endif - -/* -** Return TRUE if the given btree is set to safety level 1. In other -** words, return TRUE if no sync() occurs on the disk files. -*/ -SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree *p){ - BtShared *pBt = p->pBt; - int rc; - assert( sqlite3_mutex_held(p->db->mutex) ); - sqlite3BtreeEnter(p); - assert( pBt && pBt->pPager ); - rc = sqlite3PagerNosync(pBt->pPager); - sqlite3BtreeLeave(p); - return rc; -} - -/* -** Change the default pages size and the number of reserved bytes per page. -** Or, if the page size has already been fixed, return SQLITE_READONLY -** without changing anything. -** -** The page size must be a power of 2 between 512 and 65536. If the page -** size supplied does not meet this constraint then the page size is not -** changed. -** -** Page sizes are constrained to be a power of two so that the region -** of the database file used for locking (beginning at PENDING_BYTE, -** the first byte past the 1GB boundary, 0x40000000) needs to occur -** at the beginning of a page. -** -** If parameter nReserve is less than zero, then the number of reserved -** bytes per page is left unchanged. -** -** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size -** and autovacuum mode can no longer be changed. -*/ -SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ - int rc = SQLITE_OK; - BtShared *pBt = p->pBt; - assert( nReserve>=-1 && nReserve<=255 ); - sqlite3BtreeEnter(p); - if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ - sqlite3BtreeLeave(p); - return SQLITE_READONLY; - } - if( nReserve<0 ){ - nReserve = pBt->pageSize - pBt->usableSize; - } - assert( nReserve>=0 && nReserve<=255 ); - if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && - ((pageSize-1)&pageSize)==0 ){ - assert( (pageSize & 7)==0 ); - assert( !pBt->pPage1 && !pBt->pCursor ); - pBt->pageSize = (u32)pageSize; - freeTempSpace(pBt); - } - rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); - pBt->usableSize = pBt->pageSize - (u16)nReserve; - if( iFix ) pBt->btsFlags |= BTS_PAGESIZE_FIXED; - sqlite3BtreeLeave(p); - return rc; -} - -/* -** Return the currently defined page size -*/ -SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){ - return p->pBt->pageSize; -} - -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) -/* -** This function is similar to sqlite3BtreeGetReserve(), except that it -** may only be called if it is guaranteed that the b-tree mutex is already -** held. -** -** This is useful in one special case in the backup API code where it is -** known that the shared b-tree mutex is held, but the mutex on the -** database handle that owns *p is not. In this case if sqlite3BtreeEnter() -** were to be called, it might collide with some other operation on the -** database handle that owns *p, causing undefined behavior. -*/ -SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){ - assert( sqlite3_mutex_held(p->pBt->mutex) ); - return p->pBt->pageSize - p->pBt->usableSize; -} -#endif /* SQLITE_HAS_CODEC || SQLITE_DEBUG */ - -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) -/* -** Return the number of bytes of space at the end of every page that -** are intentually left unused. This is the "reserved" space that is -** sometimes used by extensions. -*/ -SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree *p){ - int n; - sqlite3BtreeEnter(p); - n = p->pBt->pageSize - p->pBt->usableSize; - sqlite3BtreeLeave(p); - return n; -} - -/* -** Set the maximum page count for a database if mxPage is positive. -** No changes are made if mxPage is 0 or negative. -** Regardless of the value of mxPage, return the maximum page count. -*/ -SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){ - int n; - sqlite3BtreeEnter(p); - n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage); - sqlite3BtreeLeave(p); - return n; -} - -/* -** Set the BTS_SECURE_DELETE flag if newFlag is 0 or 1. If newFlag is -1, -** then make no changes. Always return the value of the BTS_SECURE_DELETE -** setting after the change. -*/ -SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){ - int b; - if( p==0 ) return 0; - sqlite3BtreeEnter(p); - if( newFlag>=0 ){ - p->pBt->btsFlags &= ~BTS_SECURE_DELETE; - if( newFlag ) p->pBt->btsFlags |= BTS_SECURE_DELETE; - } - b = (p->pBt->btsFlags & BTS_SECURE_DELETE)!=0; - sqlite3BtreeLeave(p); - return b; -} -#endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */ - -/* -** Change the 'auto-vacuum' property of the database. If the 'autoVacuum' -** parameter is non-zero, then auto-vacuum mode is enabled. If zero, it -** is disabled. The default value for the auto-vacuum property is -** determined by the SQLITE_DEFAULT_AUTOVACUUM macro. -*/ -SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){ -#ifdef SQLITE_OMIT_AUTOVACUUM - return SQLITE_READONLY; -#else - BtShared *pBt = p->pBt; - int rc = SQLITE_OK; - u8 av = (u8)autoVacuum; - - sqlite3BtreeEnter(p); - if( (pBt->btsFlags & BTS_PAGESIZE_FIXED)!=0 && (av ?1:0)!=pBt->autoVacuum ){ - rc = SQLITE_READONLY; - }else{ - pBt->autoVacuum = av ?1:0; - pBt->incrVacuum = av==2 ?1:0; - } - sqlite3BtreeLeave(p); - return rc; -#endif -} - -/* -** Return the value of the 'auto-vacuum' property. If auto-vacuum is -** enabled 1 is returned. Otherwise 0. -*/ -SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *p){ -#ifdef SQLITE_OMIT_AUTOVACUUM - return BTREE_AUTOVACUUM_NONE; -#else - int rc; - sqlite3BtreeEnter(p); - rc = ( - (!p->pBt->autoVacuum)?BTREE_AUTOVACUUM_NONE: - (!p->pBt->incrVacuum)?BTREE_AUTOVACUUM_FULL: - BTREE_AUTOVACUUM_INCR - ); - sqlite3BtreeLeave(p); - return rc; -#endif -} - - -/* -** Get a reference to pPage1 of the database file. This will -** also acquire a readlock on that file. -** -** SQLITE_OK is returned on success. If the file is not a -** well-formed database file, then SQLITE_CORRUPT is returned. -** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM -** is returned if we run out of memory. -*/ -static int lockBtree(BtShared *pBt){ - int rc; /* Result code from subfunctions */ - MemPage *pPage1; /* Page 1 of the database file */ - int nPage; /* Number of pages in the database */ - int nPageFile = 0; /* Number of pages in the database file */ - int nPageHeader; /* Number of pages in the database according to hdr */ - - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( pBt->pPage1==0 ); - rc = sqlite3PagerSharedLock(pBt->pPager); - if( rc!=SQLITE_OK ) return rc; - rc = btreeGetPage(pBt, 1, &pPage1, 0); - if( rc!=SQLITE_OK ) return rc; - - /* Do some checking to help insure the file we opened really is - ** a valid database file. - */ - nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); - sqlite3PagerPagecount(pBt->pPager, &nPageFile); - if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ - nPage = nPageFile; - } - if( nPage>0 ){ - u32 pageSize; - u32 usableSize; - u8 *page1 = pPage1->aData; - rc = SQLITE_NOTADB; - if( memcmp(page1, zMagicHeader, 16)!=0 ){ - goto page1_init_failed; - } - -#ifdef SQLITE_OMIT_WAL - if( page1[18]>1 ){ - pBt->btsFlags |= BTS_READ_ONLY; - } - if( page1[19]>1 ){ - goto page1_init_failed; - } -#else - if( page1[18]>2 ){ - pBt->btsFlags |= BTS_READ_ONLY; - } - if( page1[19]>2 ){ - goto page1_init_failed; - } - - /* If the write version is set to 2, this database should be accessed - ** in WAL mode. If the log is not already open, open it now. Then - ** return SQLITE_OK and return without populating BtShared.pPage1. - ** The caller detects this and calls this function again. This is - ** required as the version of page 1 currently in the page1 buffer - ** may not be the latest version - there may be a newer one in the log - ** file. - */ - if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){ - int isOpen = 0; - rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen); - if( rc!=SQLITE_OK ){ - goto page1_init_failed; - }else if( isOpen==0 ){ - releasePage(pPage1); - return SQLITE_OK; - } - rc = SQLITE_NOTADB; - } -#endif - - /* The maximum embedded fraction must be exactly 25%. And the minimum - ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data. - ** The original design allowed these amounts to vary, but as of - ** version 3.6.0, we require them to be fixed. - */ - if( memcmp(&page1[21], "\100\040\040",3)!=0 ){ - goto page1_init_failed; - } - pageSize = (page1[16]<<8) | (page1[17]<<16); - if( ((pageSize-1)&pageSize)!=0 - || pageSize>SQLITE_MAX_PAGE_SIZE - || pageSize<=256 - ){ - goto page1_init_failed; - } - assert( (pageSize & 7)==0 ); - usableSize = pageSize - page1[20]; - if( (u32)pageSize!=pBt->pageSize ){ - /* After reading the first page of the database assuming a page size - ** of BtShared.pageSize, we have discovered that the page-size is - ** actually pageSize. Unlock the database, leave pBt->pPage1 at - ** zero and return SQLITE_OK. The caller will call this function - ** again with the correct page-size. - */ - releasePage(pPage1); - pBt->usableSize = usableSize; - pBt->pageSize = pageSize; - freeTempSpace(pBt); - rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, - pageSize-usableSize); - return rc; - } - if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPage>nPageFile ){ - rc = SQLITE_CORRUPT_BKPT; - goto page1_init_failed; - } - if( usableSize<480 ){ - goto page1_init_failed; - } - pBt->pageSize = pageSize; - pBt->usableSize = usableSize; -#ifndef SQLITE_OMIT_AUTOVACUUM - pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0); - pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0); -#endif - } - - /* maxLocal is the maximum amount of payload to store locally for - ** a cell. Make sure it is small enough so that at least minFanout - ** cells can will fit on one page. We assume a 10-byte page header. - ** Besides the payload, the cell must store: - ** 2-byte pointer to the cell - ** 4-byte child pointer - ** 9-byte nKey value - ** 4-byte nData value - ** 4-byte overflow page pointer - ** So a cell consists of a 2-byte pointer, a header which is as much as - ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow - ** page pointer. - */ - pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23); - pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23); - pBt->maxLeaf = (u16)(pBt->usableSize - 35); - pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23); - if( pBt->maxLocal>127 ){ - pBt->max1bytePayload = 127; - }else{ - pBt->max1bytePayload = (u8)pBt->maxLocal; - } - assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); - pBt->pPage1 = pPage1; - pBt->nPage = nPage; - return SQLITE_OK; - -page1_init_failed: - releasePage(pPage1); - pBt->pPage1 = 0; - return rc; -} - -/* -** If there are no outstanding cursors and we are not in the middle -** of a transaction but there is a read lock on the database, then -** this routine unrefs the first page of the database file which -** has the effect of releasing the read lock. -** -** If there is a transaction in progress, this routine is a no-op. -*/ -static void unlockBtreeIfUnused(BtShared *pBt){ - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( pBt->pCursor==0 || pBt->inTransaction>TRANS_NONE ); - if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ - assert( pBt->pPage1->aData ); - assert( sqlite3PagerRefcount(pBt->pPager)==1 ); - assert( pBt->pPage1->aData ); - releasePage(pBt->pPage1); - pBt->pPage1 = 0; - } -} - -/* -** If pBt points to an empty file then convert that empty file -** into a new empty database by initializing the first page of -** the database. -*/ -static int newDatabase(BtShared *pBt){ - MemPage *pP1; - unsigned char *data; - int rc; - - assert( sqlite3_mutex_held(pBt->mutex) ); - if( pBt->nPage>0 ){ - return SQLITE_OK; - } - pP1 = pBt->pPage1; - assert( pP1!=0 ); - data = pP1->aData; - rc = sqlite3PagerWrite(pP1->pDbPage); - if( rc ) return rc; - memcpy(data, zMagicHeader, sizeof(zMagicHeader)); - assert( sizeof(zMagicHeader)==16 ); - data[16] = (u8)((pBt->pageSize>>8)&0xff); - data[17] = (u8)((pBt->pageSize>>16)&0xff); - data[18] = 1; - data[19] = 1; - assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize); - data[20] = (u8)(pBt->pageSize - pBt->usableSize); - data[21] = 64; - data[22] = 32; - data[23] = 32; - memset(&data[24], 0, 100-24); - zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); - pBt->btsFlags |= BTS_PAGESIZE_FIXED; -#ifndef SQLITE_OMIT_AUTOVACUUM - assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 ); - assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 ); - put4byte(&data[36 + 4*4], pBt->autoVacuum); - put4byte(&data[36 + 7*4], pBt->incrVacuum); -#endif - pBt->nPage = 1; - data[31] = 1; - return SQLITE_OK; -} - -/* -** Initialize the first page of the database file (creating a database -** consisting of a single page and no schema objects). Return SQLITE_OK -** if successful, or an SQLite error code otherwise. -*/ -SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){ - int rc; - sqlite3BtreeEnter(p); - p->pBt->nPage = 0; - rc = newDatabase(p->pBt); - sqlite3BtreeLeave(p); - return rc; -} - -/* -** Attempt to start a new transaction. A write-transaction -** is started if the second argument is nonzero, otherwise a read- -** transaction. If the second argument is 2 or more and exclusive -** transaction is started, meaning that no other process is allowed -** to access the database. A preexisting transaction may not be -** upgraded to exclusive by calling this routine a second time - the -** exclusivity flag only works for a new transaction. -** -** A write-transaction must be started before attempting any -** changes to the database. None of the following routines -** will work unless a transaction is started first: -** -** sqlite3BtreeCreateTable() -** sqlite3BtreeCreateIndex() -** sqlite3BtreeClearTable() -** sqlite3BtreeDropTable() -** sqlite3BtreeInsert() -** sqlite3BtreeDelete() -** sqlite3BtreeUpdateMeta() -** -** If an initial attempt to acquire the lock fails because of lock contention -** and the database was previously unlocked, then invoke the busy handler -** if there is one. But if there was previously a read-lock, do not -** invoke the busy handler - just return SQLITE_BUSY. SQLITE_BUSY is -** returned when there is already a read-lock in order to avoid a deadlock. -** -** Suppose there are two processes A and B. A has a read lock and B has -** a reserved lock. B tries to promote to exclusive but is blocked because -** of A's read lock. A tries to promote to reserved but is blocked by B. -** One or the other of the two processes must give way or there can be -** no progress. By returning SQLITE_BUSY and not invoking the busy callback -** when A already has a read lock, we encourage A to give up and let B -** proceed. -*/ -SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ - sqlite3 *pBlock = 0; - BtShared *pBt = p->pBt; - int rc = SQLITE_OK; - - sqlite3BtreeEnter(p); - btreeIntegrity(p); - - /* If the btree is already in a write-transaction, or it - ** is already in a read-transaction and a read-transaction - ** is requested, this is a no-op. - */ - if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ - goto trans_begun; - } - assert( IfNotOmitAV(pBt->bDoTruncate)==0 ); - - /* Write transactions are not possible on a read-only database */ - if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){ - rc = SQLITE_READONLY; - goto trans_begun; - } - -#ifndef SQLITE_OMIT_SHARED_CACHE - /* If another database handle has already opened a write transaction - ** on this shared-btree structure and a second write transaction is - ** requested, return SQLITE_LOCKED. - */ - if( (wrflag && pBt->inTransaction==TRANS_WRITE) - || (pBt->btsFlags & BTS_PENDING)!=0 - ){ - pBlock = pBt->pWriter->db; - }else if( wrflag>1 ){ - BtLock *pIter; - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - if( pIter->pBtree!=p ){ - pBlock = pIter->pBtree->db; - break; - } - } - } - if( pBlock ){ - sqlite3ConnectionBlocked(p->db, pBlock); - rc = SQLITE_LOCKED_SHAREDCACHE; - goto trans_begun; - } -#endif - - /* Any read-only or read-write transaction implies a read-lock on - ** page 1. So if some other shared-cache client already has a write-lock - ** on page 1, the transaction cannot be opened. */ - rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); - if( SQLITE_OK!=rc ) goto trans_begun; - - pBt->btsFlags &= ~BTS_INITIALLY_EMPTY; - if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY; - do { - /* Call lockBtree() until either pBt->pPage1 is populated or - ** lockBtree() returns something other than SQLITE_OK. lockBtree() - ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after - ** reading page 1 it discovers that the page-size of the database - ** file is not pBt->pageSize. In this case lockBtree() will update - ** pBt->pageSize to the page-size of the file on disk. - */ - while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) ); - - if( rc==SQLITE_OK && wrflag ){ - if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ - rc = SQLITE_READONLY; - }else{ - rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db)); - if( rc==SQLITE_OK ){ - rc = newDatabase(pBt); - } - } - } - - if( rc!=SQLITE_OK ){ - unlockBtreeIfUnused(pBt); - } - }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && - btreeInvokeBusyHandler(pBt) ); - - if( rc==SQLITE_OK ){ - if( p->inTrans==TRANS_NONE ){ - pBt->nTransaction++; -#ifndef SQLITE_OMIT_SHARED_CACHE - if( p->sharable ){ - assert( p->lock.pBtree==p && p->lock.iTable==1 ); - p->lock.eLock = READ_LOCK; - p->lock.pNext = pBt->pLock; - pBt->pLock = &p->lock; - } -#endif - } - p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); - if( p->inTrans>pBt->inTransaction ){ - pBt->inTransaction = p->inTrans; - } - if( wrflag ){ - MemPage *pPage1 = pBt->pPage1; -#ifndef SQLITE_OMIT_SHARED_CACHE - assert( !pBt->pWriter ); - pBt->pWriter = p; - pBt->btsFlags &= ~BTS_EXCLUSIVE; - if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE; -#endif - - /* If the db-size header field is incorrect (as it may be if an old - ** client has been writing the database file), update it now. Doing - ** this sooner rather than later means the database size can safely - ** re-read the database size from page 1 if a savepoint or transaction - ** rollback occurs within the transaction. - */ - if( pBt->nPage!=get4byte(&pPage1->aData[28]) ){ - rc = sqlite3PagerWrite(pPage1->pDbPage); - if( rc==SQLITE_OK ){ - put4byte(&pPage1->aData[28], pBt->nPage); - } - } - } - } - - -trans_begun: - if( rc==SQLITE_OK && wrflag ){ - /* This call makes sure that the pager has the correct number of - ** open savepoints. If the second parameter is greater than 0 and - ** the sub-journal is not already open, then it will be opened here. - */ - rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint); - } - - btreeIntegrity(p); - sqlite3BtreeLeave(p); - return rc; -} - -#ifndef SQLITE_OMIT_AUTOVACUUM - -/* -** Set the pointer-map entries for all children of page pPage. Also, if -** pPage contains cells that point to overflow pages, set the pointer -** map entries for the overflow pages as well. -*/ -static int setChildPtrmaps(MemPage *pPage){ - int i; /* Counter variable */ - int nCell; /* Number of cells in page pPage */ - int rc; /* Return code */ - BtShared *pBt = pPage->pBt; - u8 isInitOrig = pPage->isInit; - Pgno pgno = pPage->pgno; - - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - rc = btreeInitPage(pPage); - if( rc!=SQLITE_OK ){ - goto set_child_ptrmaps_out; - } - nCell = pPage->nCell; - - for(i=0; ileaf ){ - Pgno childPgno = get4byte(pCell); - ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); - } - } - - if( !pPage->leaf ){ - Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); - } - -set_child_ptrmaps_out: - pPage->isInit = isInitOrig; - return rc; -} - -/* -** Somewhere on pPage is a pointer to page iFrom. Modify this pointer so -** that it points to iTo. Parameter eType describes the type of pointer to -** be modified, as follows: -** -** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child -** page of pPage. -** -** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow -** page pointed to by one of the cells on pPage. -** -** PTRMAP_OVERFLOW2: pPage is an overflow-page. The pointer points at the next -** overflow page in the list. -*/ -static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - if( eType==PTRMAP_OVERFLOW2 ){ - /* The pointer is always the first 4 bytes of the page in this case. */ - if( get4byte(pPage->aData)!=iFrom ){ - return SQLITE_CORRUPT_BKPT; - } - put4byte(pPage->aData, iTo); - }else{ - u8 isInitOrig = pPage->isInit; - int i; - int nCell; - - btreeInitPage(pPage); - nCell = pPage->nCell; - - for(i=0; iaData+pPage->maskPage - && iFrom==get4byte(&pCell[info.iOverflow]) - ){ - put4byte(&pCell[info.iOverflow], iTo); - break; - } - }else{ - if( get4byte(pCell)==iFrom ){ - put4byte(pCell, iTo); - break; - } - } - } - - if( i==nCell ){ - if( eType!=PTRMAP_BTREE || - get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){ - return SQLITE_CORRUPT_BKPT; - } - put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); - } - - pPage->isInit = isInitOrig; - } - return SQLITE_OK; -} - - -/* -** Move the open database page pDbPage to location iFreePage in the -** database. The pDbPage reference remains valid. -** -** The isCommit flag indicates that there is no need to remember that -** the journal needs to be sync()ed before database page pDbPage->pgno -** can be written to. The caller has already promised not to write to that -** page. -*/ -static int relocatePage( - BtShared *pBt, /* Btree */ - MemPage *pDbPage, /* Open page to move */ - u8 eType, /* Pointer map 'type' entry for pDbPage */ - Pgno iPtrPage, /* Pointer map 'page-no' entry for pDbPage */ - Pgno iFreePage, /* The location to move pDbPage to */ - int isCommit /* isCommit flag passed to sqlite3PagerMovepage */ -){ - MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */ - Pgno iDbPage = pDbPage->pgno; - Pager *pPager = pBt->pPager; - int rc; - - assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 || - eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ); - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( pDbPage->pBt==pBt ); - - /* Move page iDbPage from its current location to page number iFreePage */ - TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", - iDbPage, iFreePage, iPtrPage, eType)); - rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit); - if( rc!=SQLITE_OK ){ - return rc; - } - pDbPage->pgno = iFreePage; - - /* If pDbPage was a btree-page, then it may have child pages and/or cells - ** that point to overflow pages. The pointer map entries for all these - ** pages need to be changed. - ** - ** If pDbPage is an overflow page, then the first 4 bytes may store a - ** pointer to a subsequent overflow page. If this is the case, then - ** the pointer map needs to be updated for the subsequent overflow page. - */ - if( eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ){ - rc = setChildPtrmaps(pDbPage); - if( rc!=SQLITE_OK ){ - return rc; - } - }else{ - Pgno nextOvfl = get4byte(pDbPage->aData); - if( nextOvfl!=0 ){ - ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage, &rc); - if( rc!=SQLITE_OK ){ - return rc; - } - } - } - - /* Fix the database pointer on page iPtrPage that pointed at iDbPage so - ** that it points at iFreePage. Also fix the pointer map entry for - ** iPtrPage. - */ - if( eType!=PTRMAP_ROOTPAGE ){ - rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlite3PagerWrite(pPtrPage->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(pPtrPage); - return rc; - } - rc = modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); - releasePage(pPtrPage); - if( rc==SQLITE_OK ){ - ptrmapPut(pBt, iFreePage, eType, iPtrPage, &rc); - } - } - return rc; -} - -/* Forward declaration required by incrVacuumStep(). */ -static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8); - -/* -** Perform a single step of an incremental-vacuum. If successful, return -** SQLITE_OK. If there is no work to do (and therefore no point in -** calling this function again), return SQLITE_DONE. Or, if an error -** occurs, return some other error code. -** -** More specificly, this function attempts to re-organize the database so -** that the last page of the file currently in use is no longer in use. -** -** Parameter nFin is the number of pages that this database would contain -** were this function called until it returns SQLITE_DONE. -** -** If the bCommit parameter is non-zero, this function assumes that the -** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE -** or an error. bCommit is passed true for an auto-vacuum-on-commmit -** operation, or false for an incremental vacuum. -*/ -static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){ - Pgno nFreeList; /* Number of pages still on the free-list */ - int rc; - - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( iLastPg>nFin ); - - if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ - u8 eType; - Pgno iPtrPage; - - nFreeList = get4byte(&pBt->pPage1->aData[36]); - if( nFreeList==0 ){ - return SQLITE_DONE; - } - - rc = ptrmapGet(pBt, iLastPg, &eType, &iPtrPage); - if( rc!=SQLITE_OK ){ - return rc; - } - if( eType==PTRMAP_ROOTPAGE ){ - return SQLITE_CORRUPT_BKPT; - } - - if( eType==PTRMAP_FREEPAGE ){ - if( bCommit==0 ){ - /* Remove the page from the files free-list. This is not required - ** if bCommit is non-zero. In that case, the free-list will be - ** truncated to zero after this function returns, so it doesn't - ** matter if it still contains some garbage entries. - */ - Pgno iFreePg; - MemPage *pFreePg; - rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, BTALLOC_EXACT); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( iFreePg==iLastPg ); - releasePage(pFreePg); - } - } else { - Pgno iFreePg; /* Index of free page to move pLastPg to */ - MemPage *pLastPg; - u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */ - Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */ - - rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - - /* If bCommit is zero, this loop runs exactly once and page pLastPg - ** is swapped with the first free page pulled off the free list. - ** - ** On the other hand, if bCommit is greater than zero, then keep - ** looping until a free-page located within the first nFin pages - ** of the file is found. - */ - if( bCommit==0 ){ - eMode = BTALLOC_LE; - iNear = nFin; - } - do { - MemPage *pFreePg; - rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode); - if( rc!=SQLITE_OK ){ - releasePage(pLastPg); - return rc; - } - releasePage(pFreePg); - }while( bCommit && iFreePg>nFin ); - assert( iFreePgbDoTruncate = 1; - pBt->nPage = iLastPg; - } - return SQLITE_OK; -} - -/* -** The database opened by the first argument is an auto-vacuum database -** nOrig pages in size containing nFree free pages. Return the expected -** size of the database in pages following an auto-vacuum operation. -*/ -static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){ - int nEntry; /* Number of entries on one ptrmap page */ - Pgno nPtrmap; /* Number of PtrMap pages to be freed */ - Pgno nFin; /* Return value */ - - nEntry = pBt->usableSize/5; - nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry; - nFin = nOrig - nFree - nPtrmap; - if( nOrig>PENDING_BYTE_PAGE(pBt) && nFinpBt; - - sqlite3BtreeEnter(p); - assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE ); - if( !pBt->autoVacuum ){ - rc = SQLITE_DONE; - }else{ - Pgno nOrig = btreePagecount(pBt); - Pgno nFree = get4byte(&pBt->pPage1->aData[36]); - Pgno nFin = finalDbSize(pBt, nOrig, nFree); - - if( nOrig0 ){ - invalidateAllOverflowCache(pBt); - rc = incrVacuumStep(pBt, nFin, nOrig, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); - put4byte(&pBt->pPage1->aData[28], pBt->nPage); - } - }else{ - rc = SQLITE_DONE; - } - } - sqlite3BtreeLeave(p); - return rc; -} - -/* -** This routine is called prior to sqlite3PagerCommit when a transaction -** is commited for an auto-vacuum database. -** -** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages -** the database file should be truncated to during the commit process. -** i.e. the database has been reorganized so that only the first *pnTrunc -** pages are in use. -*/ -static int autoVacuumCommit(BtShared *pBt){ - int rc = SQLITE_OK; - Pager *pPager = pBt->pPager; - VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager) ); - - assert( sqlite3_mutex_held(pBt->mutex) ); - invalidateAllOverflowCache(pBt); - assert(pBt->autoVacuum); - if( !pBt->incrVacuum ){ - Pgno nFin; /* Number of pages in database after autovacuuming */ - Pgno nFree; /* Number of pages on the freelist initially */ - Pgno iFree; /* The next page to be freed */ - Pgno nOrig; /* Database size before freeing */ - - nOrig = btreePagecount(pBt); - if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){ - /* It is not possible to create a database for which the final page - ** is either a pointer-map page or the pending-byte page. If one - ** is encountered, this indicates corruption. - */ - return SQLITE_CORRUPT_BKPT; - } - - nFree = get4byte(&pBt->pPage1->aData[36]); - nFin = finalDbSize(pBt, nOrig, nFree); - if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT; - - for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){ - rc = incrVacuumStep(pBt, nFin, iFree, 1); - } - if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ - rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); - put4byte(&pBt->pPage1->aData[32], 0); - put4byte(&pBt->pPage1->aData[36], 0); - put4byte(&pBt->pPage1->aData[28], nFin); - pBt->bDoTruncate = 1; - pBt->nPage = nFin; - } - if( rc!=SQLITE_OK ){ - sqlite3PagerRollback(pPager); - } - } - - assert( nRef==sqlite3PagerRefcount(pPager) ); - return rc; -} - -#else /* ifndef SQLITE_OMIT_AUTOVACUUM */ -# define setChildPtrmaps(x) SQLITE_OK -#endif - -/* -** This routine does the first phase of a two-phase commit. This routine -** causes a rollback journal to be created (if it does not already exist) -** and populated with enough information so that if a power loss occurs -** the database can be restored to its original state by playing back -** the journal. Then the contents of the journal are flushed out to -** the disk. After the journal is safely on oxide, the changes to the -** database are written into the database file and flushed to oxide. -** At the end of this call, the rollback journal still exists on the -** disk and we are still holding all locks, so the transaction has not -** committed. See sqlite3BtreeCommitPhaseTwo() for the second phase of the -** commit process. -** -** This call is a no-op if no write-transaction is currently active on pBt. -** -** Otherwise, sync the database file for the btree pBt. zMaster points to -** the name of a master journal file that should be written into the -** individual journal file, or is NULL, indicating no master journal file -** (single database transaction). -** -** When this is called, the master journal should already have been -** created, populated with this journal pointer and synced to disk. -** -** Once this is routine has returned, the only thing required to commit -** the write-transaction for this database file is to delete the journal. -*/ -SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){ - int rc = SQLITE_OK; - if( p->inTrans==TRANS_WRITE ){ - BtShared *pBt = p->pBt; - sqlite3BtreeEnter(p); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - rc = autoVacuumCommit(pBt); - if( rc!=SQLITE_OK ){ - sqlite3BtreeLeave(p); - return rc; - } - } - if( pBt->bDoTruncate ){ - sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage); - } -#endif - rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0); - sqlite3BtreeLeave(p); - } - return rc; -} - -/* -** This function is called from both BtreeCommitPhaseTwo() and BtreeRollback() -** at the conclusion of a transaction. -*/ -static void btreeEndTransaction(Btree *p){ - BtShared *pBt = p->pBt; - assert( sqlite3BtreeHoldsMutex(p) ); - -#ifndef SQLITE_OMIT_AUTOVACUUM - pBt->bDoTruncate = 0; -#endif - btreeClearHasContent(pBt); - if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){ - /* If there are other active statements that belong to this database - ** handle, downgrade to a read-only transaction. The other statements - ** may still be reading from the database. */ - downgradeAllSharedCacheTableLocks(p); - p->inTrans = TRANS_READ; - }else{ - /* If the handle had any kind of transaction open, decrement the - ** transaction count of the shared btree. If the transaction count - ** reaches 0, set the shared state to TRANS_NONE. The unlockBtreeIfUnused() - ** call below will unlock the pager. */ - if( p->inTrans!=TRANS_NONE ){ - clearAllSharedCacheTableLocks(p); - pBt->nTransaction--; - if( 0==pBt->nTransaction ){ - pBt->inTransaction = TRANS_NONE; - } - } - - /* Set the current transaction state to TRANS_NONE and unlock the - ** pager if this call closed the only read or write transaction. */ - p->inTrans = TRANS_NONE; - unlockBtreeIfUnused(pBt); - } - - btreeIntegrity(p); -} - -/* -** Commit the transaction currently in progress. -** -** This routine implements the second phase of a 2-phase commit. The -** sqlite3BtreeCommitPhaseOne() routine does the first phase and should -** be invoked prior to calling this routine. The sqlite3BtreeCommitPhaseOne() -** routine did all the work of writing information out to disk and flushing the -** contents so that they are written onto the disk platter. All this -** routine has to do is delete or truncate or zero the header in the -** the rollback journal (which causes the transaction to commit) and -** drop locks. -** -** Normally, if an error occurs while the pager layer is attempting to -** finalize the underlying journal file, this function returns an error and -** the upper layer will attempt a rollback. However, if the second argument -** is non-zero then this b-tree transaction is part of a multi-file -** transaction. In this case, the transaction has already been committed -** (by deleting a master journal file) and the caller will ignore this -** functions return code. So, even if an error occurs in the pager layer, -** reset the b-tree objects internal state to indicate that the write -** transaction has been closed. This is quite safe, as the pager will have -** transitioned to the error state. -** -** This will release the write lock on the database file. If there -** are no active cursors, it also releases the read lock. -*/ -SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){ - - if( p->inTrans==TRANS_NONE ) return SQLITE_OK; - sqlite3BtreeEnter(p); - btreeIntegrity(p); - - /* If the handle has a write-transaction open, commit the shared-btrees - ** transaction and set the shared state to TRANS_READ. - */ - if( p->inTrans==TRANS_WRITE ){ - int rc; - BtShared *pBt = p->pBt; - assert( pBt->inTransaction==TRANS_WRITE ); - assert( pBt->nTransaction>0 ); - rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); - if( rc!=SQLITE_OK && bCleanup==0 ){ - sqlite3BtreeLeave(p); - return rc; - } - pBt->inTransaction = TRANS_READ; - } - - btreeEndTransaction(p); - sqlite3BtreeLeave(p); - return SQLITE_OK; -} - -/* -** Do both phases of a commit. -*/ -SQLITE_PRIVATE int sqlite3BtreeCommit(Btree *p){ - int rc; - sqlite3BtreeEnter(p); - rc = sqlite3BtreeCommitPhaseOne(p, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeCommitPhaseTwo(p, 0); - } - sqlite3BtreeLeave(p); - return rc; -} - -#ifndef NDEBUG -/* -** Return the number of write-cursors open on this handle. This is for use -** in assert() expressions, so it is only compiled if NDEBUG is not -** defined. -** -** For the purposes of this routine, a write-cursor is any cursor that -** is capable of writing to the databse. That means the cursor was -** originally opened for writing and the cursor has not be disabled -** by having its state changed to CURSOR_FAULT. -*/ -static int countWriteCursors(BtShared *pBt){ - BtCursor *pCur; - int r = 0; - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( pCur->wrFlag && pCur->eState!=CURSOR_FAULT ) r++; - } - return r; -} -#endif - -/* -** This routine sets the state to CURSOR_FAULT and the error -** code to errCode for every cursor on BtShared that pBtree -** references. -** -** Every cursor is tripped, including cursors that belong -** to other database connections that happen to be sharing -** the cache with pBtree. -** -** This routine gets called when a rollback occurs. -** All cursors using the same cache must be tripped -** to prevent them from trying to use the btree after -** the rollback. The rollback may have deleted tables -** or moved root pages, so it is not sufficient to -** save the state of the cursor. The cursor must be -** invalidated. -*/ -SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){ - BtCursor *p; - if( pBtree==0 ) return; - sqlite3BtreeEnter(pBtree); - for(p=pBtree->pBt->pCursor; p; p=p->pNext){ - int i; - sqlite3BtreeClearCursor(p); - p->eState = CURSOR_FAULT; - p->skipNext = errCode; - for(i=0; i<=p->iPage; i++){ - releasePage(p->apPage[i]); - p->apPage[i] = 0; - } - } - sqlite3BtreeLeave(pBtree); -} - -/* -** Rollback the transaction in progress. All cursors will be -** invalided by this operation. Any attempt to use a cursor -** that was open at the beginning of this operation will result -** in an error. -** -** This will release the write lock on the database file. If there -** are no active cursors, it also releases the read lock. -*/ -SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p, int tripCode){ - int rc; - BtShared *pBt = p->pBt; - MemPage *pPage1; - - sqlite3BtreeEnter(p); - if( tripCode==SQLITE_OK ){ - rc = tripCode = saveAllCursors(pBt, 0, 0); - }else{ - rc = SQLITE_OK; - } - if( tripCode ){ - sqlite3BtreeTripAllCursors(p, tripCode); - } - btreeIntegrity(p); - - if( p->inTrans==TRANS_WRITE ){ - int rc2; - - assert( TRANS_WRITE==pBt->inTransaction ); - rc2 = sqlite3PagerRollback(pBt->pPager); - if( rc2!=SQLITE_OK ){ - rc = rc2; - } - - /* The rollback may have destroyed the pPage1->aData value. So - ** call btreeGetPage() on page 1 again to make - ** sure pPage1->aData is set correctly. */ - if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ - int nPage = get4byte(28+(u8*)pPage1->aData); - testcase( nPage==0 ); - if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage); - testcase( pBt->nPage!=nPage ); - pBt->nPage = nPage; - releasePage(pPage1); - } - assert( countWriteCursors(pBt)==0 ); - pBt->inTransaction = TRANS_READ; - } - - btreeEndTransaction(p); - sqlite3BtreeLeave(p); - return rc; -} - -/* -** Start a statement subtransaction. The subtransaction can can be rolled -** back independently of the main transaction. You must start a transaction -** before starting a subtransaction. The subtransaction is ended automatically -** if the main transaction commits or rolls back. -** -** Statement subtransactions are used around individual SQL statements -** that are contained within a BEGIN...COMMIT block. If a constraint -** error occurs within the statement, the effect of that one statement -** can be rolled back without having to rollback the entire transaction. -** -** A statement sub-transaction is implemented as an anonymous savepoint. The -** value passed as the second parameter is the total number of savepoints, -** including the new anonymous savepoint, open on the B-Tree. i.e. if there -** are no active savepoints and no other statement-transactions open, -** iStatement is 1. This anonymous savepoint can be released or rolled back -** using the sqlite3BtreeSavepoint() function. -*/ -SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree *p, int iStatement){ - int rc; - BtShared *pBt = p->pBt; - sqlite3BtreeEnter(p); - assert( p->inTrans==TRANS_WRITE ); - assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( iStatement>0 ); - assert( iStatement>p->db->nSavepoint ); - assert( pBt->inTransaction==TRANS_WRITE ); - /* At the pager level, a statement transaction is a savepoint with - ** an index greater than all savepoints created explicitly using - ** SQL statements. It is illegal to open, release or rollback any - ** such savepoints while the statement transaction savepoint is active. - */ - rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement); - sqlite3BtreeLeave(p); - return rc; -} - -/* -** The second argument to this function, op, is always SAVEPOINT_ROLLBACK -** or SAVEPOINT_RELEASE. This function either releases or rolls back the -** savepoint identified by parameter iSavepoint, depending on the value -** of op. -** -** Normally, iSavepoint is greater than or equal to zero. However, if op is -** SAVEPOINT_ROLLBACK, then iSavepoint may also be -1. In this case the -** contents of the entire transaction are rolled back. This is different -** from a normal transaction rollback, as no locks are released and the -** transaction remains open. -*/ -SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ - int rc = SQLITE_OK; - if( p && p->inTrans==TRANS_WRITE ){ - BtShared *pBt = p->pBt; - assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); - assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); - sqlite3BtreeEnter(p); - rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); - if( rc==SQLITE_OK ){ - if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ - pBt->nPage = 0; - } - rc = newDatabase(pBt); - pBt->nPage = get4byte(28 + pBt->pPage1->aData); - - /* The database size was written into the offset 28 of the header - ** when the transaction started, so we know that the value at offset - ** 28 is nonzero. */ - assert( pBt->nPage>0 ); - } - sqlite3BtreeLeave(p); - } - return rc; -} - -/* -** Create a new cursor for the BTree whose root is on the page -** iTable. If a read-only cursor is requested, it is assumed that -** the caller already has at least a read-only transaction open -** on the database already. If a write-cursor is requested, then -** the caller is assumed to have an open write transaction. -** -** If wrFlag==0, then the cursor can only be used for reading. -** If wrFlag==1, then the cursor can be used for reading or for -** writing if other conditions for writing are also met. These -** are the conditions that must be met in order for writing to -** be allowed: -** -** 1: The cursor must have been opened with wrFlag==1 -** -** 2: Other database connections that share the same pager cache -** but which are not in the READ_UNCOMMITTED state may not have -** cursors open with wrFlag==0 on the same table. Otherwise -** the changes made by this write cursor would be visible to -** the read cursors in the other database connection. -** -** 3: The database must be writable (not on read-only media) -** -** 4: There must be an active transaction. -** -** No checking is done to make sure that page iTable really is the -** root page of a b-tree. If it is not, then the cursor acquired -** will not work correctly. -** -** It is assumed that the sqlite3BtreeCursorZero() has been called -** on pCur to initialize the memory space prior to invoking this routine. -*/ -static int btreeCursor( - Btree *p, /* The btree */ - int iTable, /* Root page of table to open */ - int wrFlag, /* 1 to write. 0 read-only */ - struct KeyInfo *pKeyInfo, /* First arg to comparison function */ - BtCursor *pCur /* Space for new cursor */ -){ - BtShared *pBt = p->pBt; /* Shared b-tree handle */ - - assert( sqlite3BtreeHoldsMutex(p) ); - assert( wrFlag==0 || wrFlag==1 ); - - /* The following assert statements verify that if this is a sharable - ** b-tree database, the connection is holding the required table locks, - ** and that no other connection has any open cursor that conflicts with - ** this lock. */ - assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) ); - assert( wrFlag==0 || !hasReadConflicts(p, iTable) ); - - /* Assert that the caller has opened the required transaction. */ - assert( p->inTrans>TRANS_NONE ); - assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); - assert( pBt->pPage1 && pBt->pPage1->aData ); - - if( NEVER(wrFlag && (pBt->btsFlags & BTS_READ_ONLY)!=0) ){ - return SQLITE_READONLY; - } - if( iTable==1 && btreePagecount(pBt)==0 ){ - assert( wrFlag==0 ); - iTable = 0; - } - - /* Now that no other errors can occur, finish filling in the BtCursor - ** variables and link the cursor into the BtShared list. */ - pCur->pgnoRoot = (Pgno)iTable; - pCur->iPage = -1; - pCur->pKeyInfo = pKeyInfo; - pCur->pBtree = p; - pCur->pBt = pBt; - pCur->wrFlag = (u8)wrFlag; - pCur->pNext = pBt->pCursor; - if( pCur->pNext ){ - pCur->pNext->pPrev = pCur; - } - pBt->pCursor = pCur; - pCur->eState = CURSOR_INVALID; - pCur->cachedRowid = 0; - return SQLITE_OK; -} -SQLITE_PRIVATE int sqlite3BtreeCursor( - Btree *p, /* The btree */ - int iTable, /* Root page of table to open */ - int wrFlag, /* 1 to write. 0 read-only */ - struct KeyInfo *pKeyInfo, /* First arg to xCompare() */ - BtCursor *pCur /* Write new cursor here */ -){ - int rc; - sqlite3BtreeEnter(p); - rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur); - sqlite3BtreeLeave(p); - return rc; -} - -/* -** Return the size of a BtCursor object in bytes. -** -** This interfaces is needed so that users of cursors can preallocate -** sufficient storage to hold a cursor. The BtCursor object is opaque -** to users so they cannot do the sizeof() themselves - they must call -** this routine. -*/ -SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ - return ROUND8(sizeof(BtCursor)); -} - -/* -** Initialize memory that will be converted into a BtCursor object. -** -** The simple approach here would be to memset() the entire object -** to zero. But it turns out that the apPage[] and aiIdx[] arrays -** do not need to be zeroed and they are large, so we can save a lot -** of run-time by skipping the initialization of those elements. -*/ -SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor *p){ - memset(p, 0, offsetof(BtCursor, iPage)); -} - -/* -** Set the cached rowid value of every cursor in the same database file -** as pCur and having the same root page number as pCur. The value is -** set to iRowid. -** -** Only positive rowid values are considered valid for this cache. -** The cache is initialized to zero, indicating an invalid cache. -** A btree will work fine with zero or negative rowids. We just cannot -** cache zero or negative rowids, which means tables that use zero or -** negative rowids might run a little slower. But in practice, zero -** or negative rowids are very uncommon so this should not be a problem. -*/ -SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor *pCur, sqlite3_int64 iRowid){ - BtCursor *p; - for(p=pCur->pBt->pCursor; p; p=p->pNext){ - if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid; - } - assert( pCur->cachedRowid==iRowid ); -} - -/* -** Return the cached rowid for the given cursor. A negative or zero -** return value indicates that the rowid cache is invalid and should be -** ignored. If the rowid cache has never before been set, then a -** zero is returned. -*/ -SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor *pCur){ - return pCur->cachedRowid; -} - -/* -** Close a cursor. The read lock on the database file is released -** when the last cursor is closed. -*/ -SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){ - Btree *pBtree = pCur->pBtree; - if( pBtree ){ - int i; - BtShared *pBt = pCur->pBt; - sqlite3BtreeEnter(pBtree); - sqlite3BtreeClearCursor(pCur); - if( pCur->pPrev ){ - pCur->pPrev->pNext = pCur->pNext; - }else{ - pBt->pCursor = pCur->pNext; - } - if( pCur->pNext ){ - pCur->pNext->pPrev = pCur->pPrev; - } - for(i=0; i<=pCur->iPage; i++){ - releasePage(pCur->apPage[i]); - } - unlockBtreeIfUnused(pBt); - invalidateOverflowCache(pCur); - /* sqlite3_free(pCur); */ - sqlite3BtreeLeave(pBtree); - } - return SQLITE_OK; -} - -/* -** Make sure the BtCursor* given in the argument has a valid -** BtCursor.info structure. If it is not already valid, call -** btreeParseCell() to fill it in. -** -** BtCursor.info is a cache of the information in the current cell. -** Using this cache reduces the number of calls to btreeParseCell(). -** -** 2007-06-25: There is a bug in some versions of MSVC that cause the -** compiler to crash when getCellInfo() is implemented as a macro. -** But there is a measureable speed advantage to using the macro on gcc -** (when less compiler optimizations like -Os or -O0 are used and the -** compiler is not doing agressive inlining.) So we use a real function -** for MSVC and a macro for everything else. Ticket #2457. -*/ -#ifndef NDEBUG - static void assertCellInfo(BtCursor *pCur){ - CellInfo info; - int iPage = pCur->iPage; - memset(&info, 0, sizeof(info)); - btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); - assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); - } -#else - #define assertCellInfo(x) -#endif -#ifdef _MSC_VER - /* Use a real function in MSVC to work around bugs in that compiler. */ - static void getCellInfo(BtCursor *pCur){ - if( pCur->info.nSize==0 ){ - int iPage = pCur->iPage; - btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); - pCur->validNKey = 1; - }else{ - assertCellInfo(pCur); - } - } -#else /* if not _MSC_VER */ - /* Use a macro in all other compilers so that the function is inlined */ -#define getCellInfo(pCur) \ - if( pCur->info.nSize==0 ){ \ - int iPage = pCur->iPage; \ - btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ - pCur->validNKey = 1; \ - }else{ \ - assertCellInfo(pCur); \ - } -#endif /* _MSC_VER */ - -#ifndef NDEBUG /* The next routine used only within assert() statements */ -/* -** Return true if the given BtCursor is valid. A valid cursor is one -** that is currently pointing to a row in a (non-empty) table. -** This is a verification routine is used only within assert() statements. -*/ -SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor *pCur){ - return pCur && pCur->eState==CURSOR_VALID; -} -#endif /* NDEBUG */ - -/* -** Set *pSize to the size of the buffer needed to hold the value of -** the key for the current entry. If the cursor is not pointing -** to a valid entry, *pSize is set to 0. -** -** For a table with the INTKEY flag set, this routine returns the key -** itself, not the number of bytes in the key. -** -** The caller must position the cursor prior to invoking this routine. -** -** This routine cannot fail. It always returns SQLITE_OK. -*/ -SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); - if( pCur->eState!=CURSOR_VALID ){ - *pSize = 0; - }else{ - getCellInfo(pCur); - *pSize = pCur->info.nKey; - } - return SQLITE_OK; -} - -/* -** Set *pSize to the number of bytes of data in the entry the -** cursor currently points to. -** -** The caller must guarantee that the cursor is pointing to a non-NULL -** valid entry. In other words, the calling procedure must guarantee -** that the cursor has Cursor.eState==CURSOR_VALID. -** -** Failure is not possible. This function always returns SQLITE_OK. -** It might just as well be a procedure (returning void) but we continue -** to return an integer result code for historical reasons. -*/ -SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - getCellInfo(pCur); - *pSize = pCur->info.nData; - return SQLITE_OK; -} - -/* -** Given the page number of an overflow page in the database (parameter -** ovfl), this function finds the page number of the next page in the -** linked list of overflow pages. If possible, it uses the auto-vacuum -** pointer-map data instead of reading the content of page ovfl to do so. -** -** If an error occurs an SQLite error code is returned. Otherwise: -** -** The page number of the next overflow page in the linked list is -** written to *pPgnoNext. If page ovfl is the last page in its linked -** list, *pPgnoNext is set to zero. -** -** If ppPage is not NULL, and a reference to the MemPage object corresponding -** to page number pOvfl was obtained, then *ppPage is set to point to that -** reference. It is the responsibility of the caller to call releasePage() -** on *ppPage to free the reference. In no reference was obtained (because -** the pointer-map was used to obtain the value for *pPgnoNext), then -** *ppPage is set to zero. -*/ -static int getOverflowPage( - BtShared *pBt, /* The database file */ - Pgno ovfl, /* Current overflow page number */ - MemPage **ppPage, /* OUT: MemPage handle (may be NULL) */ - Pgno *pPgnoNext /* OUT: Next overflow page number */ -){ - Pgno next = 0; - MemPage *pPage = 0; - int rc = SQLITE_OK; - - assert( sqlite3_mutex_held(pBt->mutex) ); - assert(pPgnoNext); - -#ifndef SQLITE_OMIT_AUTOVACUUM - /* Try to find the next page in the overflow list using the - ** autovacuum pointer-map pages. Guess that the next page in - ** the overflow list is page number (ovfl+1). If that guess turns - ** out to be wrong, fall back to loading the data of page - ** number ovfl to determine the next page number. - */ - if( pBt->autoVacuum ){ - Pgno pgno; - Pgno iGuess = ovfl+1; - u8 eType; - - while( PTRMAP_ISPAGE(pBt, iGuess) || iGuess==PENDING_BYTE_PAGE(pBt) ){ - iGuess++; - } - - if( iGuess<=btreePagecount(pBt) ){ - rc = ptrmapGet(pBt, iGuess, &eType, &pgno); - if( rc==SQLITE_OK && eType==PTRMAP_OVERFLOW2 && pgno==ovfl ){ - next = iGuess; - rc = SQLITE_DONE; - } - } - } -#endif - - assert( next==0 || rc==SQLITE_DONE ); - if( rc==SQLITE_OK ){ - rc = btreeGetPage(pBt, ovfl, &pPage, 0); - assert( rc==SQLITE_OK || pPage==0 ); - if( rc==SQLITE_OK ){ - next = get4byte(pPage->aData); - } - } - - *pPgnoNext = next; - if( ppPage ){ - *ppPage = pPage; - }else{ - releasePage(pPage); - } - return (rc==SQLITE_DONE ? SQLITE_OK : rc); -} - -/* -** Copy data from a buffer to a page, or from a page to a buffer. -** -** pPayload is a pointer to data stored on database page pDbPage. -** If argument eOp is false, then nByte bytes of data are copied -** from pPayload to the buffer pointed at by pBuf. If eOp is true, -** then sqlite3PagerWrite() is called on pDbPage and nByte bytes -** of data are copied from the buffer pBuf to pPayload. -** -** SQLITE_OK is returned on success, otherwise an error code. -*/ -static int copyPayload( - void *pPayload, /* Pointer to page data */ - void *pBuf, /* Pointer to buffer */ - int nByte, /* Number of bytes to copy */ - int eOp, /* 0 -> copy from page, 1 -> copy to page */ - DbPage *pDbPage /* Page containing pPayload */ -){ - if( eOp ){ - /* Copy data from buffer to page (a write operation) */ - int rc = sqlite3PagerWrite(pDbPage); - if( rc!=SQLITE_OK ){ - return rc; - } - memcpy(pPayload, pBuf, nByte); - }else{ - /* Copy data from page to buffer (a read operation) */ - memcpy(pBuf, pPayload, nByte); - } - return SQLITE_OK; -} - -/* -** This function is used to read or overwrite payload information -** for the entry that the pCur cursor is pointing to. If the eOp -** parameter is 0, this is a read operation (data copied into -** buffer pBuf). If it is non-zero, a write (data copied from -** buffer pBuf). -** -** A total of "amt" bytes are read or written beginning at "offset". -** Data is read to or from the buffer pBuf. -** -** The content being read or written might appear on the main page -** or be scattered out on multiple overflow pages. -** -** If the BtCursor.isIncrblobHandle flag is set, and the current -** cursor entry uses one or more overflow pages, this function -** allocates space for and lazily popluates the overflow page-list -** cache array (BtCursor.aOverflow). Subsequent calls use this -** cache to make seeking to the supplied offset more efficient. -** -** Once an overflow page-list cache has been allocated, it may be -** invalidated if some other cursor writes to the same table, or if -** the cursor is moved to a different row. Additionally, in auto-vacuum -** mode, the following events may invalidate an overflow page-list cache. -** -** * An incremental vacuum, -** * A commit in auto_vacuum="full" mode, -** * Creating a table (may require moving an overflow page). -*/ -static int accessPayload( - BtCursor *pCur, /* Cursor pointing to entry to read from */ - u32 offset, /* Begin reading this far into payload */ - u32 amt, /* Read this many bytes */ - unsigned char *pBuf, /* Write the bytes into this buffer */ - int eOp /* zero to read. non-zero to write. */ -){ - unsigned char *aPayload; - int rc = SQLITE_OK; - u32 nKey; - int iIdx = 0; - MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */ - BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */ - - assert( pPage ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->aiIdx[pCur->iPage]nCell ); - assert( cursorHoldsMutex(pCur) ); - - getCellInfo(pCur); - aPayload = pCur->info.pCell + pCur->info.nHeader; - nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey); - - if( NEVER(offset+amt > nKey+pCur->info.nData) - || &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] - ){ - /* Trying to read or write past the end of the data is an error */ - return SQLITE_CORRUPT_BKPT; - } - - /* Check if data must be read/written to/from the btree page itself. */ - if( offsetinfo.nLocal ){ - int a = amt; - if( a+offset>pCur->info.nLocal ){ - a = pCur->info.nLocal - offset; - } - rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage); - offset = 0; - pBuf += a; - amt -= a; - }else{ - offset -= pCur->info.nLocal; - } - - if( rc==SQLITE_OK && amt>0 ){ - const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */ - Pgno nextPage; - - nextPage = get4byte(&aPayload[pCur->info.nLocal]); - -#ifndef SQLITE_OMIT_INCRBLOB - /* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[] - ** has not been allocated, allocate it now. The array is sized at - ** one entry for each overflow page in the overflow chain. The - ** page number of the first overflow page is stored in aOverflow[0], - ** etc. A value of 0 in the aOverflow[] array means "not yet known" - ** (the cache is lazily populated). - */ - if( pCur->isIncrblobHandle && !pCur->aOverflow ){ - int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; - pCur->aOverflow = (Pgno *)sqlite3MallocZero(sizeof(Pgno)*nOvfl); - /* nOvfl is always positive. If it were zero, fetchPayload would have - ** been used instead of this routine. */ - if( ALWAYS(nOvfl) && !pCur->aOverflow ){ - rc = SQLITE_NOMEM; - } - } - - /* If the overflow page-list cache has been allocated and the - ** entry for the first required overflow page is valid, skip - ** directly to it. - */ - if( pCur->aOverflow && pCur->aOverflow[offset/ovflSize] ){ - iIdx = (offset/ovflSize); - nextPage = pCur->aOverflow[iIdx]; - offset = (offset%ovflSize); - } -#endif - - for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){ - -#ifndef SQLITE_OMIT_INCRBLOB - /* If required, populate the overflow page-list cache. */ - if( pCur->aOverflow ){ - assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage); - pCur->aOverflow[iIdx] = nextPage; - } -#endif - - if( offset>=ovflSize ){ - /* The only reason to read this page is to obtain the page - ** number for the next page in the overflow chain. The page - ** data is not required. So first try to lookup the overflow - ** page-list cache, if any, then fall back to the getOverflowPage() - ** function. - */ -#ifndef SQLITE_OMIT_INCRBLOB - if( pCur->aOverflow && pCur->aOverflow[iIdx+1] ){ - nextPage = pCur->aOverflow[iIdx+1]; - } else -#endif - rc = getOverflowPage(pBt, nextPage, 0, &nextPage); - offset -= ovflSize; - }else{ - /* Need to read this page properly. It contains some of the - ** range of data that is being read (eOp==0) or written (eOp!=0). - */ -#ifdef SQLITE_DIRECT_OVERFLOW_READ - sqlite3_file *fd; -#endif - int a = amt; - if( a + offset > ovflSize ){ - a = ovflSize - offset; - } - -#ifdef SQLITE_DIRECT_OVERFLOW_READ - /* If all the following are true: - ** - ** 1) this is a read operation, and - ** 2) data is required from the start of this overflow page, and - ** 3) the database is file-backed, and - ** 4) there is no open write-transaction, and - ** 5) the database is not a WAL database, - ** - ** then data can be read directly from the database file into the - ** output buffer, bypassing the page-cache altogether. This speeds - ** up loading large records that span many overflow pages. - */ - if( eOp==0 /* (1) */ - && offset==0 /* (2) */ - && pBt->inTransaction==TRANS_READ /* (4) */ - && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */ - && pBt->pPage1->aData[19]==0x01 /* (5) */ - ){ - u8 aSave[4]; - u8 *aWrite = &pBuf[-4]; - memcpy(aSave, aWrite, 4); - rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); - nextPage = get4byte(aWrite); - memcpy(aWrite, aSave, 4); - }else -#endif - - { - DbPage *pDbPage; - rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage); - if( rc==SQLITE_OK ){ - aPayload = sqlite3PagerGetData(pDbPage); - nextPage = get4byte(aPayload); - rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); - sqlite3PagerUnref(pDbPage); - offset = 0; - } - } - amt -= a; - pBuf += a; - } - } - } - - if( rc==SQLITE_OK && amt>0 ){ - return SQLITE_CORRUPT_BKPT; - } - return rc; -} - -/* -** Read part of the key associated with cursor pCur. Exactly -** "amt" bytes will be transfered into pBuf[]. The transfer -** begins at "offset". -** -** The caller must ensure that pCur is pointing to a valid row -** in the table. -** -** Return SQLITE_OK on success or an error code if anything goes -** wrong. An error is returned if "offset+amt" is larger than -** the available payload. -*/ -SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); - assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); -} - -/* -** Read part of the data associated with cursor pCur. Exactly -** "amt" bytes will be transfered into pBuf[]. The transfer -** begins at "offset". -** -** Return SQLITE_OK on success or an error code if anything goes -** wrong. An error is returned if "offset+amt" is larger than -** the available payload. -*/ -SQLITE_PRIVATE int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - int rc; - -#ifndef SQLITE_OMIT_INCRBLOB - if ( pCur->eState==CURSOR_INVALID ){ - return SQLITE_ABORT; - } -#endif - - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); - assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - rc = accessPayload(pCur, offset, amt, pBuf, 0); - } - return rc; -} - -/* -** Return a pointer to payload information from the entry that the -** pCur cursor is pointing to. The pointer is to the beginning of -** the key if skipKey==0 and it points to the beginning of data if -** skipKey==1. The number of bytes of available key/data is written -** into *pAmt. If *pAmt==0, then the value returned will not be -** a valid pointer. -** -** This routine is an optimization. It is common for the entire key -** and data to fit on the local page and for there to be no overflow -** pages. When that is so, this routine can be used to access the -** key and data without making a copy. If the key and/or data spills -** onto overflow pages, then accessPayload() must be used to reassemble -** the key/data and copy it into a preallocated buffer. -** -** The pointer returned by this routine looks directly into the cached -** page of the database. The data might change or move the next time -** any btree routine is called. -*/ -static const unsigned char *fetchPayload( - BtCursor *pCur, /* Cursor pointing to entry to read from */ - int *pAmt, /* Write the number of available bytes here */ - int skipKey /* read beginning at data if this is true */ -){ - unsigned char *aPayload; - MemPage *pPage; - u32 nKey; - u32 nLocal; - - assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); - assert( pCur->eState==CURSOR_VALID ); - assert( cursorHoldsMutex(pCur) ); - pPage = pCur->apPage[pCur->iPage]; - assert( pCur->aiIdx[pCur->iPage]nCell ); - if( NEVER(pCur->info.nSize==0) ){ - btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage], - &pCur->info); - } - aPayload = pCur->info.pCell; - aPayload += pCur->info.nHeader; - if( pPage->intKey ){ - nKey = 0; - }else{ - nKey = (int)pCur->info.nKey; - } - if( skipKey ){ - aPayload += nKey; - nLocal = pCur->info.nLocal - nKey; - }else{ - nLocal = pCur->info.nLocal; - assert( nLocal<=nKey ); - } - *pAmt = nLocal; - return aPayload; -} - - -/* -** For the entry that cursor pCur is point to, return as -** many bytes of the key or data as are available on the local -** b-tree page. Write the number of available bytes into *pAmt. -** -** The pointer returned is ephemeral. The key/data may move -** or be destroyed on the next call to any Btree routine, -** including calls from other threads against the same cache. -** Hence, a mutex on the BtShared should be held prior to calling -** this routine. -** -** These routines is used to get quick access to key and data -** in the common case where no overflow pages are used. -*/ -SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){ - const void *p = 0; - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - assert( cursorHoldsMutex(pCur) ); - if( ALWAYS(pCur->eState==CURSOR_VALID) ){ - p = (const void*)fetchPayload(pCur, pAmt, 0); - } - return p; -} -SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){ - const void *p = 0; - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - assert( cursorHoldsMutex(pCur) ); - if( ALWAYS(pCur->eState==CURSOR_VALID) ){ - p = (const void*)fetchPayload(pCur, pAmt, 1); - } - return p; -} - - -/* -** Move the cursor down to a new child page. The newPgno argument is the -** page number of the child page to move to. -** -** This function returns SQLITE_CORRUPT if the page-header flags field of -** the new child page does not match the flags field of the parent (i.e. -** if an intkey page appears to be the parent of a non-intkey page, or -** vice-versa). -*/ -static int moveToChild(BtCursor *pCur, u32 newPgno){ - int rc; - int i = pCur->iPage; - MemPage *pNewPage; - BtShared *pBt = pCur->pBt; - - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPageiPage>=(BTCURSOR_MAX_DEPTH-1) ){ - return SQLITE_CORRUPT_BKPT; - } - rc = getAndInitPage(pBt, newPgno, &pNewPage); - if( rc ) return rc; - pCur->apPage[i+1] = pNewPage; - pCur->aiIdx[i+1] = 0; - pCur->iPage++; - - pCur->info.nSize = 0; - pCur->validNKey = 0; - if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){ - return SQLITE_CORRUPT_BKPT; - } - return SQLITE_OK; -} - -#if 0 -/* -** Page pParent is an internal (non-leaf) tree page. This function -** asserts that page number iChild is the left-child if the iIdx'th -** cell in page pParent. Or, if iIdx is equal to the total number of -** cells in pParent, that page number iChild is the right-child of -** the page. -*/ -static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){ - assert( iIdx<=pParent->nCell ); - if( iIdx==pParent->nCell ){ - assert( get4byte(&pParent->aData[pParent->hdrOffset+8])==iChild ); - }else{ - assert( get4byte(findCell(pParent, iIdx))==iChild ); - } -} -#else -# define assertParentIndex(x,y,z) -#endif - -/* -** Move the cursor up to the parent page. -** -** pCur->idx is set to the cell index that contains the pointer -** to the page we are coming from. If we are coming from the -** right-most child page then pCur->idx is set to one more than -** the largest cell index. -*/ -static void moveToParent(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>0 ); - assert( pCur->apPage[pCur->iPage] ); - - /* UPDATE: It is actually possible for the condition tested by the assert - ** below to be untrue if the database file is corrupt. This can occur if - ** one cursor has modified page pParent while a reference to it is held - ** by a second cursor. Which can only happen if a single page is linked - ** into more than one b-tree structure in a corrupt database. */ -#if 0 - assertParentIndex( - pCur->apPage[pCur->iPage-1], - pCur->aiIdx[pCur->iPage-1], - pCur->apPage[pCur->iPage]->pgno - ); -#endif - testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell ); - - releasePage(pCur->apPage[pCur->iPage]); - pCur->iPage--; - pCur->info.nSize = 0; - pCur->validNKey = 0; -} - -/* -** Move the cursor to point to the root page of its b-tree structure. -** -** If the table has a virtual root page, then the cursor is moved to point -** to the virtual root page instead of the actual root page. A table has a -** virtual root page when the actual root page contains no cells and a -** single child page. This can only happen with the table rooted at page 1. -** -** If the b-tree structure is empty, the cursor state is set to -** CURSOR_INVALID. Otherwise, the cursor is set to point to the first -** cell located on the root (or virtual root) page and the cursor state -** is set to CURSOR_VALID. -** -** If this function returns successfully, it may be assumed that the -** page-header flags indicate that the [virtual] root-page is the expected -** kind of b-tree page (i.e. if when opening the cursor the caller did not -** specify a KeyInfo structure the flags byte is set to 0x05 or 0x0D, -** indicating a table b-tree, or if the caller did specify a KeyInfo -** structure the flags byte is set to 0x02 or 0x0A, indicating an index -** b-tree). -*/ -static int moveToRoot(BtCursor *pCur){ - MemPage *pRoot; - int rc = SQLITE_OK; - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; - - assert( cursorHoldsMutex(pCur) ); - assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); - assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); - assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); - if( pCur->eState>=CURSOR_REQUIRESEEK ){ - if( pCur->eState==CURSOR_FAULT ){ - assert( pCur->skipNext!=SQLITE_OK ); - return pCur->skipNext; - } - sqlite3BtreeClearCursor(pCur); - } - - if( pCur->iPage>=0 ){ - int i; - for(i=1; i<=pCur->iPage; i++){ - releasePage(pCur->apPage[i]); - } - pCur->iPage = 0; - }else if( pCur->pgnoRoot==0 ){ - pCur->eState = CURSOR_INVALID; - return SQLITE_OK; - }else{ - rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]); - if( rc!=SQLITE_OK ){ - pCur->eState = CURSOR_INVALID; - return rc; - } - pCur->iPage = 0; - - /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor - ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is - ** NULL, the caller expects a table b-tree. If this is not the case, - ** return an SQLITE_CORRUPT error. */ - assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 ); - if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){ - return SQLITE_CORRUPT_BKPT; - } - } - - /* Assert that the root page is of the correct type. This must be the - ** case as the call to this function that loaded the root-page (either - ** this call or a previous invocation) would have detected corruption - ** if the assumption were not true, and it is not possible for the flags - ** byte to have been modified while this cursor is holding a reference - ** to the page. */ - pRoot = pCur->apPage[0]; - assert( pRoot->pgno==pCur->pgnoRoot ); - assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey ); - - pCur->aiIdx[0] = 0; - pCur->info.nSize = 0; - pCur->atLast = 0; - pCur->validNKey = 0; - - if( pRoot->nCell==0 && !pRoot->leaf ){ - Pgno subpage; - if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT; - subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); - pCur->eState = CURSOR_VALID; - rc = moveToChild(pCur, subpage); - }else{ - pCur->eState = ((pRoot->nCell>0)?CURSOR_VALID:CURSOR_INVALID); - } - return rc; -} - -/* -** Move the cursor down to the left-most leaf entry beneath the -** entry to which it is currently pointing. -** -** The left-most leaf is the one with the smallest key - the first -** in ascending order. -*/ -static int moveToLeftmost(BtCursor *pCur){ - Pgno pgno; - int rc = SQLITE_OK; - MemPage *pPage; - - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){ - assert( pCur->aiIdx[pCur->iPage]nCell ); - pgno = get4byte(findCell(pPage, pCur->aiIdx[pCur->iPage])); - rc = moveToChild(pCur, pgno); - } - return rc; -} - -/* -** Move the cursor down to the right-most leaf entry beneath the -** page to which it is currently pointing. Notice the difference -** between moveToLeftmost() and moveToRightmost(). moveToLeftmost() -** finds the left-most entry beneath the *entry* whereas moveToRightmost() -** finds the right-most entry beneath the *page*. -** -** The right-most entry is the one with the largest key - the last -** key in ascending order. -*/ -static int moveToRightmost(BtCursor *pCur){ - Pgno pgno; - int rc = SQLITE_OK; - MemPage *pPage = 0; - - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){ - pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - pCur->aiIdx[pCur->iPage] = pPage->nCell; - rc = moveToChild(pCur, pgno); - } - if( rc==SQLITE_OK ){ - pCur->aiIdx[pCur->iPage] = pPage->nCell-1; - pCur->info.nSize = 0; - pCur->validNKey = 0; - } - return rc; -} - -/* Move the cursor to the first entry in the table. Return SQLITE_OK -** on success. Set *pRes to 0 if the cursor actually points to something -** or set *pRes to 1 if the table is empty. -*/ -SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ - int rc; - - assert( cursorHoldsMutex(pCur) ); - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - rc = moveToRoot(pCur); - if( rc==SQLITE_OK ){ - if( pCur->eState==CURSOR_INVALID ){ - assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 ); - *pRes = 1; - }else{ - assert( pCur->apPage[pCur->iPage]->nCell>0 ); - *pRes = 0; - rc = moveToLeftmost(pCur); - } - } - return rc; -} - -/* Move the cursor to the last entry in the table. Return SQLITE_OK -** on success. Set *pRes to 0 if the cursor actually points to something -** or set *pRes to 1 if the table is empty. -*/ -SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ - int rc; - - assert( cursorHoldsMutex(pCur) ); - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - - /* If the cursor already points to the last entry, this is a no-op. */ - if( CURSOR_VALID==pCur->eState && pCur->atLast ){ -#ifdef SQLITE_DEBUG - /* This block serves to assert() that the cursor really does point - ** to the last entry in the b-tree. */ - int ii; - for(ii=0; iiiPage; ii++){ - assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); - } - assert( pCur->aiIdx[pCur->iPage]==pCur->apPage[pCur->iPage]->nCell-1 ); - assert( pCur->apPage[pCur->iPage]->leaf ); -#endif - return SQLITE_OK; - } - - rc = moveToRoot(pCur); - if( rc==SQLITE_OK ){ - if( CURSOR_INVALID==pCur->eState ){ - assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 ); - *pRes = 1; - }else{ - assert( pCur->eState==CURSOR_VALID ); - *pRes = 0; - rc = moveToRightmost(pCur); - pCur->atLast = rc==SQLITE_OK ?1:0; - } - } - return rc; -} - -/* Move the cursor so that it points to an entry near the key -** specified by pIdxKey or intKey. Return a success code. -** -** For INTKEY tables, the intKey parameter is used. pIdxKey -** must be NULL. For index tables, pIdxKey is used and intKey -** is ignored. -** -** If an exact match is not found, then the cursor is always -** left pointing at a leaf page which would hold the entry if it -** were present. The cursor might point to an entry that comes -** before or after the key. -** -** An integer is written into *pRes which is the result of -** comparing the key with the entry to which the cursor is -** pointing. The meaning of the integer written into -** *pRes is as follows: -** -** *pRes<0 The cursor is left pointing at an entry that -** is smaller than intKey/pIdxKey or if the table is empty -** and the cursor is therefore left point to nothing. -** -** *pRes==0 The cursor is left pointing at an entry that -** exactly matches intKey/pIdxKey. -** -** *pRes>0 The cursor is left pointing at an entry that -** is larger than intKey/pIdxKey. -** -*/ -SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( - BtCursor *pCur, /* The cursor to be moved */ - UnpackedRecord *pIdxKey, /* Unpacked index key */ - i64 intKey, /* The table key */ - int biasRight, /* If true, bias the search to the high end */ - int *pRes /* Write search results here */ -){ - int rc; - - assert( cursorHoldsMutex(pCur) ); - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - assert( pRes ); - assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); - - /* If the cursor is already positioned at the point we are trying - ** to move to, then just return without doing any work */ - if( pCur->eState==CURSOR_VALID && pCur->validNKey - && pCur->apPage[0]->intKey - ){ - if( pCur->info.nKey==intKey ){ - *pRes = 0; - return SQLITE_OK; - } - if( pCur->atLast && pCur->info.nKeypgnoRoot==0 || pCur->apPage[pCur->iPage] ); - assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->isInit ); - assert( pCur->eState==CURSOR_INVALID || pCur->apPage[pCur->iPage]->nCell>0 ); - if( pCur->eState==CURSOR_INVALID ){ - *pRes = -1; - assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 ); - return SQLITE_OK; - } - assert( pCur->apPage[0]->intKey || pIdxKey ); - for(;;){ - int lwr, upr, idx; - Pgno chldPg; - MemPage *pPage = pCur->apPage[pCur->iPage]; - int c; - - /* pPage->nCell must be greater than zero. If this is the root-page - ** the cursor would have been INVALID above and this for(;;) loop - ** not run. If this is not the root-page, then the moveToChild() routine - ** would have already detected db corruption. Similarly, pPage must - ** be the right kind (index or table) of b-tree page. Otherwise - ** a moveToChild() or moveToRoot() call would have detected corruption. */ - assert( pPage->nCell>0 ); - assert( pPage->intKey==(pIdxKey==0) ); - lwr = 0; - upr = pPage->nCell-1; - if( biasRight ){ - pCur->aiIdx[pCur->iPage] = (u16)(idx = upr); - }else{ - pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2); - } - for(;;){ - u8 *pCell; /* Pointer to current cell in pPage */ - - assert( idx==pCur->aiIdx[pCur->iPage] ); - pCur->info.nSize = 0; - pCell = findCell(pPage, idx) + pPage->childPtrSize; - if( pPage->intKey ){ - i64 nCellKey; - if( pPage->hasData ){ - u32 dummy; - pCell += getVarint32(pCell, dummy); - } - getVarint(pCell, (u64*)&nCellKey); - if( nCellKey==intKey ){ - c = 0; - }else if( nCellKeyintKey ); - c = +1; - } - pCur->validNKey = 1; - pCur->info.nKey = nCellKey; - }else{ - /* The maximum supported page-size is 65536 bytes. This means that - ** the maximum number of record bytes stored on an index B-Tree - ** page is less than 16384 bytes and may be stored as a 2-byte - ** varint. This information is used to attempt to avoid parsing - ** the entire cell by checking for the cases where the record is - ** stored entirely within the b-tree page by inspecting the first - ** 2 bytes of the cell. - */ - int nCell = pCell[0]; - if( nCell<=pPage->max1bytePayload - /* && (pCell+nCell)aDataEnd */ - ){ - /* This branch runs if the record-size field of the cell is a - ** single byte varint and the record fits entirely on the main - ** b-tree page. */ - testcase( pCell+nCell+1==pPage->aDataEnd ); - c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey); - }else if( !(pCell[1] & 0x80) - && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal - /* && (pCell+nCell+2)<=pPage->aDataEnd */ - ){ - /* The record-size field is a 2 byte varint and the record - ** fits entirely on the main b-tree page. */ - testcase( pCell+nCell+2==pPage->aDataEnd ); - c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey); - }else{ - /* The record flows over onto one or more overflow pages. In - ** this case the whole cell needs to be parsed, a buffer allocated - ** and accessPayload() used to retrieve the record into the - ** buffer before VdbeRecordCompare() can be called. */ - void *pCellKey; - u8 * const pCellBody = pCell - pPage->childPtrSize; - btreeParseCellPtr(pPage, pCellBody, &pCur->info); - nCell = (int)pCur->info.nKey; - pCellKey = sqlite3Malloc( nCell ); - if( pCellKey==0 ){ - rc = SQLITE_NOMEM; - goto moveto_finish; - } - rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); - if( rc ){ - sqlite3_free(pCellKey); - goto moveto_finish; - } - c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); - sqlite3_free(pCellKey); - } - } - if( c==0 ){ - if( pPage->intKey && !pPage->leaf ){ - lwr = idx; - break; - }else{ - *pRes = 0; - rc = SQLITE_OK; - goto moveto_finish; - } - } - if( c<0 ){ - lwr = idx+1; - }else{ - upr = idx-1; - } - if( lwr>upr ){ - break; - } - pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2); - } - assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); - assert( pPage->isInit ); - if( pPage->leaf ){ - chldPg = 0; - }else if( lwr>=pPage->nCell ){ - chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); - }else{ - chldPg = get4byte(findCell(pPage, lwr)); - } - if( chldPg==0 ){ - assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - *pRes = c; - rc = SQLITE_OK; - goto moveto_finish; - } - pCur->aiIdx[pCur->iPage] = (u16)lwr; - pCur->info.nSize = 0; - pCur->validNKey = 0; - rc = moveToChild(pCur, chldPg); - if( rc ) goto moveto_finish; - } -moveto_finish: - return rc; -} - - -/* -** Return TRUE if the cursor is not pointing at an entry of the table. -** -** TRUE will be returned after a call to sqlite3BtreeNext() moves -** past the last entry in the table or sqlite3BtreePrev() moves past -** the first entry. TRUE is also returned if the table is empty. -*/ -SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor *pCur){ - /* TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries - ** have been deleted? This API will need to change to return an error code - ** as well as the boolean result value. - */ - return (CURSOR_VALID!=pCur->eState); -} - -/* -** Advance the cursor to the next entry in the database. If -** successful then set *pRes=0. If the cursor -** was already pointing to the last entry in the database before -** this routine was called, then set *pRes=1. -*/ -SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ - int rc; - int idx; - MemPage *pPage; - - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pRes!=0 ); - if( CURSOR_INVALID==pCur->eState ){ - *pRes = 1; - return SQLITE_OK; - } - if( pCur->skipNext>0 ){ - pCur->skipNext = 0; - *pRes = 0; - return SQLITE_OK; - } - pCur->skipNext = 0; - - pPage = pCur->apPage[pCur->iPage]; - idx = ++pCur->aiIdx[pCur->iPage]; - assert( pPage->isInit ); - - /* If the database file is corrupt, it is possible for the value of idx - ** to be invalid here. This can only occur if a second cursor modifies - ** the page while cursor pCur is holding a reference to it. Which can - ** only happen if the database is corrupt in such a way as to link the - ** page into more than one b-tree structure. */ - testcase( idx>pPage->nCell ); - - pCur->info.nSize = 0; - pCur->validNKey = 0; - if( idx>=pPage->nCell ){ - if( !pPage->leaf ){ - rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); - if( rc ) return rc; - rc = moveToLeftmost(pCur); - *pRes = 0; - return rc; - } - do{ - if( pCur->iPage==0 ){ - *pRes = 1; - pCur->eState = CURSOR_INVALID; - return SQLITE_OK; - } - moveToParent(pCur); - pPage = pCur->apPage[pCur->iPage]; - }while( pCur->aiIdx[pCur->iPage]>=pPage->nCell ); - *pRes = 0; - if( pPage->intKey ){ - rc = sqlite3BtreeNext(pCur, pRes); - }else{ - rc = SQLITE_OK; - } - return rc; - } - *pRes = 0; - if( pPage->leaf ){ - return SQLITE_OK; - } - rc = moveToLeftmost(pCur); - return rc; -} - - -/* -** Step the cursor to the back to the previous entry in the database. If -** successful then set *pRes=0. If the cursor -** was already pointing to the first entry in the database before -** this routine was called, then set *pRes=1. -*/ -SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ - int rc; - MemPage *pPage; - - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc!=SQLITE_OK ){ - return rc; - } - pCur->atLast = 0; - if( CURSOR_INVALID==pCur->eState ){ - *pRes = 1; - return SQLITE_OK; - } - if( pCur->skipNext<0 ){ - pCur->skipNext = 0; - *pRes = 0; - return SQLITE_OK; - } - pCur->skipNext = 0; - - pPage = pCur->apPage[pCur->iPage]; - assert( pPage->isInit ); - if( !pPage->leaf ){ - int idx = pCur->aiIdx[pCur->iPage]; - rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); - if( rc ){ - return rc; - } - rc = moveToRightmost(pCur); - }else{ - while( pCur->aiIdx[pCur->iPage]==0 ){ - if( pCur->iPage==0 ){ - pCur->eState = CURSOR_INVALID; - *pRes = 1; - return SQLITE_OK; - } - moveToParent(pCur); - } - pCur->info.nSize = 0; - pCur->validNKey = 0; - - pCur->aiIdx[pCur->iPage]--; - pPage = pCur->apPage[pCur->iPage]; - if( pPage->intKey && !pPage->leaf ){ - rc = sqlite3BtreePrevious(pCur, pRes); - }else{ - rc = SQLITE_OK; - } - } - *pRes = 0; - return rc; -} - -/* -** Allocate a new page from the database file. -** -** The new page is marked as dirty. (In other words, sqlite3PagerWrite() -** has already been called on the new page.) The new page has also -** been referenced and the calling routine is responsible for calling -** sqlite3PagerUnref() on the new page when it is done. -** -** SQLITE_OK is returned on success. Any other return value indicates -** an error. *ppPage and *pPgno are undefined in the event of an error. -** Do not invoke sqlite3PagerUnref() on *ppPage if an error is returned. -** -** If the "nearby" parameter is not 0, then an effort is made to -** locate a page close to the page number "nearby". This can be used in an -** attempt to keep related pages close to each other in the database file, -** which in turn can make database access faster. -** -** If the eMode parameter is BTALLOC_EXACT and the nearby page exists -** anywhere on the free-list, then it is guaranteed to be returned. If -** eMode is BTALLOC_LT then the page returned will be less than or equal -** to nearby if any such page exists. If eMode is BTALLOC_ANY then there -** are no restrictions on which page is returned. -*/ -static int allocateBtreePage( - BtShared *pBt, /* The btree */ - MemPage **ppPage, /* Store pointer to the allocated page here */ - Pgno *pPgno, /* Store the page number here */ - Pgno nearby, /* Search for a page near this one */ - u8 eMode /* BTALLOC_EXACT, BTALLOC_LT, or BTALLOC_ANY */ -){ - MemPage *pPage1; - int rc; - u32 n; /* Number of pages on the freelist */ - u32 k; /* Number of leaves on the trunk of the freelist */ - MemPage *pTrunk = 0; - MemPage *pPrevTrunk = 0; - Pgno mxPage; /* Total size of the database file */ - - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) ); - pPage1 = pBt->pPage1; - mxPage = btreePagecount(pBt); - n = get4byte(&pPage1->aData[36]); - testcase( n==mxPage-1 ); - if( n>=mxPage ){ - return SQLITE_CORRUPT_BKPT; - } - if( n>0 ){ - /* There are pages on the freelist. Reuse one of those pages. */ - Pgno iTrunk; - u8 searchList = 0; /* If the free-list must be searched for 'nearby' */ - - /* If eMode==BTALLOC_EXACT and a query of the pointer-map - ** shows that the page 'nearby' is somewhere on the free-list, then - ** the entire-list will be searched for that page. - */ -#ifndef SQLITE_OMIT_AUTOVACUUM - if( eMode==BTALLOC_EXACT ){ - if( nearby<=mxPage ){ - u8 eType; - assert( nearby>0 ); - assert( pBt->autoVacuum ); - rc = ptrmapGet(pBt, nearby, &eType, 0); - if( rc ) return rc; - if( eType==PTRMAP_FREEPAGE ){ - searchList = 1; - } - } - }else if( eMode==BTALLOC_LE ){ - searchList = 1; - } -#endif - - /* Decrement the free-list count by 1. Set iTrunk to the index of the - ** first free-list trunk page. iPrevTrunk is initially 1. - */ - rc = sqlite3PagerWrite(pPage1->pDbPage); - if( rc ) return rc; - put4byte(&pPage1->aData[36], n-1); - - /* The code within this loop is run only once if the 'searchList' variable - ** is not true. Otherwise, it runs once for each trunk-page on the - ** free-list until the page 'nearby' is located (eMode==BTALLOC_EXACT) - ** or until a page less than 'nearby' is located (eMode==BTALLOC_LT) - */ - do { - pPrevTrunk = pTrunk; - if( pPrevTrunk ){ - iTrunk = get4byte(&pPrevTrunk->aData[0]); - }else{ - iTrunk = get4byte(&pPage1->aData[32]); - } - testcase( iTrunk==mxPage ); - if( iTrunk>mxPage ){ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); - } - if( rc ){ - pTrunk = 0; - goto end_allocate_page; - } - assert( pTrunk!=0 ); - assert( pTrunk->aData!=0 ); - - k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */ - if( k==0 && !searchList ){ - /* The trunk has no leaves and the list is not being searched. - ** So extract the trunk page itself and use it as the newly - ** allocated page */ - assert( pPrevTrunk==0 ); - rc = sqlite3PagerWrite(pTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } - *pPgno = iTrunk; - memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); - *ppPage = pTrunk; - pTrunk = 0; - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); - }else if( k>(u32)(pBt->usableSize/4 - 2) ){ - /* Value of k is out of range. Database corruption */ - rc = SQLITE_CORRUPT_BKPT; - goto end_allocate_page; -#ifndef SQLITE_OMIT_AUTOVACUUM - }else if( searchList - && (nearby==iTrunk || (iTrunkpDbPage); - if( rc ){ - goto end_allocate_page; - } - if( k==0 ){ - if( !pPrevTrunk ){ - memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); - }else{ - rc = sqlite3PagerWrite(pPrevTrunk->pDbPage); - if( rc!=SQLITE_OK ){ - goto end_allocate_page; - } - memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4); - } - }else{ - /* The trunk page is required by the caller but it contains - ** pointers to free-list leaves. The first leaf becomes a trunk - ** page in this case. - */ - MemPage *pNewTrunk; - Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); - if( iNewTrunk>mxPage ){ - rc = SQLITE_CORRUPT_BKPT; - goto end_allocate_page; - } - testcase( iNewTrunk==mxPage ); - rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0); - if( rc!=SQLITE_OK ){ - goto end_allocate_page; - } - rc = sqlite3PagerWrite(pNewTrunk->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(pNewTrunk); - goto end_allocate_page; - } - memcpy(&pNewTrunk->aData[0], &pTrunk->aData[0], 4); - put4byte(&pNewTrunk->aData[4], k-1); - memcpy(&pNewTrunk->aData[8], &pTrunk->aData[12], (k-1)*4); - releasePage(pNewTrunk); - if( !pPrevTrunk ){ - assert( sqlite3PagerIswriteable(pPage1->pDbPage) ); - put4byte(&pPage1->aData[32], iNewTrunk); - }else{ - rc = sqlite3PagerWrite(pPrevTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } - put4byte(&pPrevTrunk->aData[0], iNewTrunk); - } - } - pTrunk = 0; - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); -#endif - }else if( k>0 ){ - /* Extract a leaf from the trunk */ - u32 closest; - Pgno iPage; - unsigned char *aData = pTrunk->aData; - if( nearby>0 ){ - u32 i; - closest = 0; - if( eMode==BTALLOC_LE ){ - for(i=0; imxPage ){ - rc = SQLITE_CORRUPT_BKPT; - goto end_allocate_page; - } - testcase( iPage==mxPage ); - if( !searchList - || (iPage==nearby || (iPagepgno, n-1)); - rc = sqlite3PagerWrite(pTrunk->pDbPage); - if( rc ) goto end_allocate_page; - if( closestpDbPage); - if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - } - } - searchList = 0; - } - } - releasePage(pPrevTrunk); - pPrevTrunk = 0; - }while( searchList ); - }else{ - /* There are no pages on the freelist, so append a new page to the - ** database image. - ** - ** Normally, new pages allocated by this block can be requested from the - ** pager layer with the 'no-content' flag set. This prevents the pager - ** from trying to read the pages content from disk. However, if the - ** current transaction has already run one or more incremental-vacuum - ** steps, then the page we are about to allocate may contain content - ** that is required in the event of a rollback. In this case, do - ** not set the no-content flag. This causes the pager to load and journal - ** the current page content before overwriting it. - ** - ** Note that the pager will not actually attempt to load or journal - ** content for any page that really does lie past the end of the database - ** file on disk. So the effects of disabling the no-content optimization - ** here are confined to those pages that lie between the end of the - ** database image and the end of the database file. - */ - int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate)); - - rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); - if( rc ) return rc; - pBt->nPage++; - if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++; - -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, pBt->nPage) ){ - /* If *pPgno refers to a pointer-map page, allocate two new pages - ** at the end of the file instead of one. The first allocated page - ** becomes a new pointer-map page, the second is used by the caller. - */ - MemPage *pPg = 0; - TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); - assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); - rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerWrite(pPg->pDbPage); - releasePage(pPg); - } - if( rc ) return rc; - pBt->nPage++; - if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ){ pBt->nPage++; } - } -#endif - put4byte(28 + (u8*)pBt->pPage1->aData, pBt->nPage); - *pPgno = pBt->nPage; - - assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent); - if( rc ) return rc; - rc = sqlite3PagerWrite((*ppPage)->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - } - TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); - } - - assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - -end_allocate_page: - releasePage(pTrunk); - releasePage(pPrevTrunk); - if( rc==SQLITE_OK ){ - if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){ - releasePage(*ppPage); - return SQLITE_CORRUPT_BKPT; - } - (*ppPage)->isInit = 0; - }else{ - *ppPage = 0; - } - assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) ); - return rc; -} - -/* -** This function is used to add page iPage to the database file free-list. -** It is assumed that the page is not already a part of the free-list. -** -** The value passed as the second argument to this function is optional. -** If the caller happens to have a pointer to the MemPage object -** corresponding to page iPage handy, it may pass it as the second value. -** Otherwise, it may pass NULL. -** -** If a pointer to a MemPage object is passed as the second argument, -** its reference count is not altered by this function. -*/ -static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ - MemPage *pTrunk = 0; /* Free-list trunk page */ - Pgno iTrunk = 0; /* Page number of free-list trunk page */ - MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */ - MemPage *pPage; /* Page being freed. May be NULL. */ - int rc; /* Return Code */ - int nFree; /* Initial number of pages on free-list */ - - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( iPage>1 ); - assert( !pMemPage || pMemPage->pgno==iPage ); - - if( pMemPage ){ - pPage = pMemPage; - sqlite3PagerRef(pPage->pDbPage); - }else{ - pPage = btreePageLookup(pBt, iPage); - } - - /* Increment the free page count on pPage1 */ - rc = sqlite3PagerWrite(pPage1->pDbPage); - if( rc ) goto freepage_out; - nFree = get4byte(&pPage1->aData[36]); - put4byte(&pPage1->aData[36], nFree+1); - - if( pBt->btsFlags & BTS_SECURE_DELETE ){ - /* If the secure_delete option is enabled, then - ** always fully overwrite deleted information with zeros. - */ - if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) ) - || ((rc = sqlite3PagerWrite(pPage->pDbPage))!=0) - ){ - goto freepage_out; - } - memset(pPage->aData, 0, pPage->pBt->pageSize); - } - - /* If the database supports auto-vacuum, write an entry in the pointer-map - ** to indicate that the page is free. - */ - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc); - if( rc ) goto freepage_out; - } - - /* Now manipulate the actual database free-list structure. There are two - ** possibilities. If the free-list is currently empty, or if the first - ** trunk page in the free-list is full, then this page will become a - ** new free-list trunk page. Otherwise, it will become a leaf of the - ** first trunk page in the current free-list. This block tests if it - ** is possible to add the page as a new free-list leaf. - */ - if( nFree!=0 ){ - u32 nLeaf; /* Initial number of leaf cells on trunk page */ - - iTrunk = get4byte(&pPage1->aData[32]); - rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); - if( rc!=SQLITE_OK ){ - goto freepage_out; - } - - nLeaf = get4byte(&pTrunk->aData[4]); - assert( pBt->usableSize>32 ); - if( nLeaf > (u32)pBt->usableSize/4 - 2 ){ - rc = SQLITE_CORRUPT_BKPT; - goto freepage_out; - } - if( nLeaf < (u32)pBt->usableSize/4 - 8 ){ - /* In this case there is room on the trunk page to insert the page - ** being freed as a new leaf. - ** - ** Note that the trunk page is not really full until it contains - ** usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have - ** coded. But due to a coding error in versions of SQLite prior to - ** 3.6.0, databases with freelist trunk pages holding more than - ** usableSize/4 - 8 entries will be reported as corrupt. In order - ** to maintain backwards compatibility with older versions of SQLite, - ** we will continue to restrict the number of entries to usableSize/4 - 8 - ** for now. At some point in the future (once everyone has upgraded - ** to 3.6.0 or later) we should consider fixing the conditional above - ** to read "usableSize/4-2" instead of "usableSize/4-8". - */ - rc = sqlite3PagerWrite(pTrunk->pDbPage); - if( rc==SQLITE_OK ){ - put4byte(&pTrunk->aData[4], nLeaf+1); - put4byte(&pTrunk->aData[8+nLeaf*4], iPage); - if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){ - sqlite3PagerDontWrite(pPage->pDbPage); - } - rc = btreeSetHasContent(pBt, iPage); - } - TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); - goto freepage_out; - } - } - - /* If control flows to this point, then it was not possible to add the - ** the page being freed as a leaf page of the first trunk in the free-list. - ** Possibly because the free-list is empty, or possibly because the - ** first trunk in the free-list is full. Either way, the page being freed - ** will become the new first trunk page in the free-list. - */ - if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){ - goto freepage_out; - } - rc = sqlite3PagerWrite(pPage->pDbPage); - if( rc!=SQLITE_OK ){ - goto freepage_out; - } - put4byte(pPage->aData, iTrunk); - put4byte(&pPage->aData[4], 0); - put4byte(&pPage1->aData[32], iPage); - TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", pPage->pgno, iTrunk)); - -freepage_out: - if( pPage ){ - pPage->isInit = 0; - } - releasePage(pPage); - releasePage(pTrunk); - return rc; -} -static void freePage(MemPage *pPage, int *pRC){ - if( (*pRC)==SQLITE_OK ){ - *pRC = freePage2(pPage->pBt, pPage, pPage->pgno); - } -} - -/* -** Free any overflow pages associated with the given Cell. -*/ -static int clearCell(MemPage *pPage, unsigned char *pCell){ - BtShared *pBt = pPage->pBt; - CellInfo info; - Pgno ovflPgno; - int rc; - int nOvfl; - u32 ovflPageSize; - - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - btreeParseCellPtr(pPage, pCell, &info); - if( info.iOverflow==0 ){ - return SQLITE_OK; /* No overflow pages. Return without doing anything */ - } - if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){ - return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */ - } - ovflPgno = get4byte(&pCell[info.iOverflow]); - assert( pBt->usableSize > 4 ); - ovflPageSize = pBt->usableSize - 4; - nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize; - assert( ovflPgno==0 || nOvfl>0 ); - while( nOvfl-- ){ - Pgno iNext = 0; - MemPage *pOvfl = 0; - if( ovflPgno<2 || ovflPgno>btreePagecount(pBt) ){ - /* 0 is not a legal page number and page 1 cannot be an - ** overflow page. Therefore if ovflPgno<2 or past the end of the - ** file the database must be corrupt. */ - return SQLITE_CORRUPT_BKPT; - } - if( nOvfl ){ - rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext); - if( rc ) return rc; - } - - if( ( pOvfl || ((pOvfl = btreePageLookup(pBt, ovflPgno))!=0) ) - && sqlite3PagerPageRefcount(pOvfl->pDbPage)!=1 - ){ - /* There is no reason any cursor should have an outstanding reference - ** to an overflow page belonging to a cell that is being deleted/updated. - ** So if there exists more than one reference to this page, then it - ** must not really be an overflow page and the database must be corrupt. - ** It is helpful to detect this before calling freePage2(), as - ** freePage2() may zero the page contents if secure-delete mode is - ** enabled. If this 'overflow' page happens to be a page that the - ** caller is iterating through or using in some other way, this - ** can be problematic. - */ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = freePage2(pBt, pOvfl, ovflPgno); - } - - if( pOvfl ){ - sqlite3PagerUnref(pOvfl->pDbPage); - } - if( rc ) return rc; - ovflPgno = iNext; - } - return SQLITE_OK; -} - -/* -** Create the byte sequence used to represent a cell on page pPage -** and write that byte sequence into pCell[]. Overflow pages are -** allocated and filled in as necessary. The calling procedure -** is responsible for making sure sufficient space has been allocated -** for pCell[]. -** -** Note that pCell does not necessary need to point to the pPage->aData -** area. pCell might point to some temporary storage. The cell will -** be constructed in this temporary area then copied into pPage->aData -** later. -*/ -static int fillInCell( - MemPage *pPage, /* The page that contains the cell */ - unsigned char *pCell, /* Complete text of the cell */ - const void *pKey, i64 nKey, /* The key */ - const void *pData,int nData, /* The data */ - int nZero, /* Extra zero bytes to append to pData */ - int *pnSize /* Write cell size here */ -){ - int nPayload; - const u8 *pSrc; - int nSrc, n, rc; - int spaceLeft; - MemPage *pOvfl = 0; - MemPage *pToRelease = 0; - unsigned char *pPrior; - unsigned char *pPayload; - BtShared *pBt = pPage->pBt; - Pgno pgnoOvfl = 0; - int nHeader; - CellInfo info; - - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - - /* pPage is not necessarily writeable since pCell might be auxiliary - ** buffer space that is separate from the pPage buffer area */ - assert( pCellaData || pCell>=&pPage->aData[pBt->pageSize] - || sqlite3PagerIswriteable(pPage->pDbPage) ); - - /* Fill in the header. */ - nHeader = 0; - if( !pPage->leaf ){ - nHeader += 4; - } - if( pPage->hasData ){ - nHeader += putVarint(&pCell[nHeader], nData+nZero); - }else{ - nData = nZero = 0; - } - nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); - btreeParseCellPtr(pPage, pCell, &info); - assert( info.nHeader==nHeader ); - assert( info.nKey==nKey ); - assert( info.nData==(u32)(nData+nZero) ); - - /* Fill in the payload */ - nPayload = nData + nZero; - if( pPage->intKey ){ - pSrc = pData; - nSrc = nData; - nData = 0; - }else{ - if( NEVER(nKey>0x7fffffff || pKey==0) ){ - return SQLITE_CORRUPT_BKPT; - } - nPayload += (int)nKey; - pSrc = pKey; - nSrc = (int)nKey; - } - *pnSize = info.nSize; - spaceLeft = info.nLocal; - pPayload = &pCell[nHeader]; - pPrior = &pCell[info.iOverflow]; - - while( nPayload>0 ){ - if( spaceLeft==0 ){ -#ifndef SQLITE_OMIT_AUTOVACUUM - Pgno pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */ - if( pBt->autoVacuum ){ - do{ - pgnoOvfl++; - } while( - PTRMAP_ISPAGE(pBt, pgnoOvfl) || pgnoOvfl==PENDING_BYTE_PAGE(pBt) - ); - } -#endif - rc = allocateBtreePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl, 0); -#ifndef SQLITE_OMIT_AUTOVACUUM - /* If the database supports auto-vacuum, and the second or subsequent - ** overflow page is being allocated, add an entry to the pointer-map - ** for that page now. - ** - ** If this is the first overflow page, then write a partial entry - ** to the pointer-map. If we write nothing to this pointer-map slot, - ** then the optimistic overflow chain processing in clearCell() - ** may misinterpret the uninitialized values and delete the - ** wrong pages from the database. - */ - if( pBt->autoVacuum && rc==SQLITE_OK ){ - u8 eType = (pgnoPtrmap?PTRMAP_OVERFLOW2:PTRMAP_OVERFLOW1); - ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap, &rc); - if( rc ){ - releasePage(pOvfl); - } - } -#endif - if( rc ){ - releasePage(pToRelease); - return rc; - } - - /* If pToRelease is not zero than pPrior points into the data area - ** of pToRelease. Make sure pToRelease is still writeable. */ - assert( pToRelease==0 || sqlite3PagerIswriteable(pToRelease->pDbPage) ); - - /* If pPrior is part of the data area of pPage, then make sure pPage - ** is still writeable */ - assert( pPrioraData || pPrior>=&pPage->aData[pBt->pageSize] - || sqlite3PagerIswriteable(pPage->pDbPage) ); - - put4byte(pPrior, pgnoOvfl); - releasePage(pToRelease); - pToRelease = pOvfl; - pPrior = pOvfl->aData; - put4byte(pPrior, 0); - pPayload = &pOvfl->aData[4]; - spaceLeft = pBt->usableSize - 4; - } - n = nPayload; - if( n>spaceLeft ) n = spaceLeft; - - /* If pToRelease is not zero than pPayload points into the data area - ** of pToRelease. Make sure pToRelease is still writeable. */ - assert( pToRelease==0 || sqlite3PagerIswriteable(pToRelease->pDbPage) ); - - /* If pPayload is part of the data area of pPage, then make sure pPage - ** is still writeable */ - assert( pPayloadaData || pPayload>=&pPage->aData[pBt->pageSize] - || sqlite3PagerIswriteable(pPage->pDbPage) ); - - if( nSrc>0 ){ - if( n>nSrc ) n = nSrc; - assert( pSrc ); - memcpy(pPayload, pSrc, n); - }else{ - memset(pPayload, 0, n); - } - nPayload -= n; - pPayload += n; - pSrc += n; - nSrc -= n; - spaceLeft -= n; - if( nSrc==0 ){ - nSrc = nData; - pSrc = pData; - } - } - releasePage(pToRelease); - return SQLITE_OK; -} - -/* -** Remove the i-th cell from pPage. This routine effects pPage only. -** The cell content is not freed or deallocated. It is assumed that -** the cell content has been copied someplace else. This routine just -** removes the reference to the cell from pPage. -** -** "sz" must be the number of bytes in the cell. -*/ -static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ - u32 pc; /* Offset to cell content of cell being deleted */ - u8 *data; /* pPage->aData */ - u8 *ptr; /* Used to move bytes around within data[] */ - u8 *endPtr; /* End of loop */ - int rc; /* The return code */ - int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ - - if( *pRC ) return; - - assert( idx>=0 && idxnCell ); - assert( sz==cellSize(pPage, idx) ); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - data = pPage->aData; - ptr = &pPage->aCellIdx[2*idx]; - pc = get2byte(ptr); - hdr = pPage->hdrOffset; - testcase( pc==get2byte(&data[hdr+5]) ); - testcase( pc+sz==pPage->pBt->usableSize ); - if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){ - *pRC = SQLITE_CORRUPT_BKPT; - return; - } - rc = freeSpace(pPage, pc, sz); - if( rc ){ - *pRC = rc; - return; - } - endPtr = &pPage->aCellIdx[2*pPage->nCell - 2]; - assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */ - while( ptrnCell--; - put2byte(&data[hdr+3], pPage->nCell); - pPage->nFree += 2; -} - -/* -** Insert a new cell on pPage at cell index "i". pCell points to the -** content of the cell. -** -** If the cell content will fit on the page, then put it there. If it -** will not fit, then make a copy of the cell content into pTemp if -** pTemp is not null. Regardless of pTemp, allocate a new entry -** in pPage->apOvfl[] and make it point to the cell content (either -** in pTemp or the original pCell) and also record its index. -** Allocating a new entry in pPage->aCell[] implies that -** pPage->nOverflow is incremented. -** -** If nSkip is non-zero, then do not copy the first nSkip bytes of the -** cell. The caller will overwrite them after this function returns. If -** nSkip is non-zero, then pCell may not point to an invalid memory location -** (but pCell+nSkip is always valid). -*/ -static void insertCell( - MemPage *pPage, /* Page into which we are copying */ - int i, /* New cell becomes the i-th cell of the page */ - u8 *pCell, /* Content of the new cell */ - int sz, /* Bytes of content in pCell */ - u8 *pTemp, /* Temp storage space for pCell, if needed */ - Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ - int *pRC /* Read and write return code from here */ -){ - int idx = 0; /* Where to write new cell content in data[] */ - int j; /* Loop counter */ - int end; /* First byte past the last cell pointer in data[] */ - int ins; /* Index in data[] where new cell pointer is inserted */ - int cellOffset; /* Address of first cell pointer in data[] */ - u8 *data; /* The content of the whole page */ - u8 *ptr; /* Used for moving information around in data[] */ - u8 *endPtr; /* End of the loop */ - - int nSkip = (iChild ? 4 : 0); - - if( *pRC ) return; - - assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); - assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 ); - assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) ); - assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - /* The cell should normally be sized correctly. However, when moving a - ** malformed cell from a leaf page to an interior page, if the cell size - ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size - ** might be less than 8 (leaf-size + pointer) on the interior node. Hence - ** the term after the || in the following assert(). */ - assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) ); - if( pPage->nOverflow || sz+2>pPage->nFree ){ - if( pTemp ){ - memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip); - pCell = pTemp; - } - if( iChild ){ - put4byte(pCell, iChild); - } - j = pPage->nOverflow++; - assert( j<(int)(sizeof(pPage->apOvfl)/sizeof(pPage->apOvfl[0])) ); - pPage->apOvfl[j] = pCell; - pPage->aiOvfl[j] = (u16)i; - }else{ - int rc = sqlite3PagerWrite(pPage->pDbPage); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; - } - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - data = pPage->aData; - cellOffset = pPage->cellOffset; - end = cellOffset + 2*pPage->nCell; - ins = cellOffset + 2*i; - rc = allocateSpace(pPage, sz, &idx); - if( rc ){ *pRC = rc; return; } - /* The allocateSpace() routine guarantees the following two properties - ** if it returns success */ - assert( idx >= end+2 ); - assert( idx+sz <= (int)pPage->pBt->usableSize ); - pPage->nCell++; - pPage->nFree -= (u16)(2 + sz); - memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip); - if( iChild ){ - put4byte(&data[idx], iChild); - } - ptr = &data[end]; - endPtr = &data[ins]; - assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */ - while( ptr>endPtr ){ - *(u16*)ptr = *(u16*)&ptr[-2]; - ptr -= 2; - } - put2byte(&data[ins], idx); - put2byte(&data[pPage->hdrOffset+3], pPage->nCell); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pPage->pBt->autoVacuum ){ - /* The cell may contain a pointer to an overflow page. If so, write - ** the entry for the overflow page into the pointer map. - */ - ptrmapPutOvflPtr(pPage, pCell, pRC); - } -#endif - } -} - -/* -** Add a list of cells to a page. The page should be initially empty. -** The cells are guaranteed to fit on the page. -*/ -static void assemblePage( - MemPage *pPage, /* The page to be assemblied */ - int nCell, /* The number of cells to add to this page */ - u8 **apCell, /* Pointers to cell bodies */ - u16 *aSize /* Sizes of the cells */ -){ - int i; /* Loop counter */ - u8 *pCellptr; /* Address of next cell pointer */ - int cellbody; /* Address of next cell body */ - u8 * const data = pPage->aData; /* Pointer to data for pPage */ - const int hdr = pPage->hdrOffset; /* Offset of header on pPage */ - const int nUsable = pPage->pBt->usableSize; /* Usable size of page */ - - assert( pPage->nOverflow==0 ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( nCell>=0 && nCell<=(int)MX_CELL(pPage->pBt) - && (int)MX_CELL(pPage->pBt)<=10921); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - - /* Check that the page has just been zeroed by zeroPage() */ - assert( pPage->nCell==0 ); - assert( get2byteNotZero(&data[hdr+5])==nUsable ); - - pCellptr = &pPage->aCellIdx[nCell*2]; - cellbody = nUsable; - for(i=nCell-1; i>=0; i--){ - u16 sz = aSize[i]; - pCellptr -= 2; - cellbody -= sz; - put2byte(pCellptr, cellbody); - memcpy(&data[cellbody], apCell[i], sz); - } - put2byte(&data[hdr+3], nCell); - put2byte(&data[hdr+5], cellbody); - pPage->nFree -= (nCell*2 + nUsable - cellbody); - pPage->nCell = (u16)nCell; -} - -/* -** The following parameters determine how many adjacent pages get involved -** in a balancing operation. NN is the number of neighbors on either side -** of the page that participate in the balancing operation. NB is the -** total number of pages that participate, including the target page and -** NN neighbors on either side. -** -** The minimum value of NN is 1 (of course). Increasing NN above 1 -** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance -** in exchange for a larger degradation in INSERT and UPDATE performance. -** The value of NN appears to give the best results overall. -*/ -#define NN 1 /* Number of neighbors on either side of pPage */ -#define NB (NN*2+1) /* Total pages involved in the balance */ - - -#ifndef SQLITE_OMIT_QUICKBALANCE -/* -** This version of balance() handles the common special case where -** a new entry is being inserted on the extreme right-end of the -** tree, in other words, when the new entry will become the largest -** entry in the tree. -** -** Instead of trying to balance the 3 right-most leaf pages, just add -** a new page to the right-hand side and put the one new entry in -** that page. This leaves the right side of the tree somewhat -** unbalanced. But odds are that we will be inserting new entries -** at the end soon afterwards so the nearly empty page will quickly -** fill up. On average. -** -** pPage is the leaf page which is the right-most page in the tree. -** pParent is its parent. pPage must have a single overflow entry -** which is also the right-most entry on the page. -** -** The pSpace buffer is used to store a temporary copy of the divider -** cell that will be inserted into pParent. Such a cell consists of a 4 -** byte page number followed by a variable length integer. In other -** words, at most 13 bytes. Hence the pSpace buffer must be at -** least 13 bytes in size. -*/ -static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ - BtShared *const pBt = pPage->pBt; /* B-Tree Database */ - MemPage *pNew; /* Newly allocated page */ - int rc; /* Return Code */ - Pgno pgnoNew; /* Page number of pNew */ - - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( sqlite3PagerIswriteable(pParent->pDbPage) ); - assert( pPage->nOverflow==1 ); - - /* This error condition is now caught prior to reaching this function */ - if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT; - - /* Allocate a new page. This page will become the right-sibling of - ** pPage. Make the parent page writable, so that the new divider cell - ** may be inserted. If both these operations are successful, proceed. - */ - rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); - - if( rc==SQLITE_OK ){ - - u8 *pOut = &pSpace[4]; - u8 *pCell = pPage->apOvfl[0]; - u16 szCell = cellSizePtr(pPage, pCell); - u8 *pStop; - - assert( sqlite3PagerIswriteable(pNew->pDbPage) ); - assert( pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) ); - zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF); - assemblePage(pNew, 1, &pCell, &szCell); - - /* If this is an auto-vacuum database, update the pointer map - ** with entries for the new page, and any pointer from the - ** cell on the page to an overflow page. If either of these - ** operations fails, the return code is set, but the contents - ** of the parent page are still manipulated by thh code below. - ** That is Ok, at this point the parent page is guaranteed to - ** be marked as dirty. Returning an error code will cause a - ** rollback, undoing any changes made to the parent page. - */ - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); - if( szCell>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, pCell, &rc); - } - } - - /* Create a divider cell to insert into pParent. The divider cell - ** consists of a 4-byte page number (the page number of pPage) and - ** a variable length key value (which must be the same value as the - ** largest key on pPage). - ** - ** To find the largest key value on pPage, first find the right-most - ** cell on pPage. The first two fields of this cell are the - ** record-length (a variable length integer at most 32-bits in size) - ** and the key value (a variable length integer, may have any value). - ** The first of the while(...) loops below skips over the record-length - ** field. The second while(...) loop copies the key value from the - ** cell on pPage into the pSpace buffer. - */ - pCell = findCell(pPage, pPage->nCell-1); - pStop = &pCell[9]; - while( (*(pCell++)&0x80) && pCellnCell, pSpace, (int)(pOut-pSpace), - 0, pPage->pgno, &rc); - - /* Set the right-child pointer of pParent to point to the new page. */ - put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew); - - /* Release the reference to the new page. */ - releasePage(pNew); - } - - return rc; -} -#endif /* SQLITE_OMIT_QUICKBALANCE */ - -#if 0 -/* -** This function does not contribute anything to the operation of SQLite. -** it is sometimes activated temporarily while debugging code responsible -** for setting pointer-map entries. -*/ -static int ptrmapCheckPages(MemPage **apPage, int nPage){ - int i, j; - for(i=0; ipBt; - assert( pPage->isInit ); - - for(j=0; jnCell; j++){ - CellInfo info; - u8 *z; - - z = findCell(pPage, j); - btreeParseCellPtr(pPage, z, &info); - if( info.iOverflow ){ - Pgno ovfl = get4byte(&z[info.iOverflow]); - ptrmapGet(pBt, ovfl, &e, &n); - assert( n==pPage->pgno && e==PTRMAP_OVERFLOW1 ); - } - if( !pPage->leaf ){ - Pgno child = get4byte(z); - ptrmapGet(pBt, child, &e, &n); - assert( n==pPage->pgno && e==PTRMAP_BTREE ); - } - } - if( !pPage->leaf ){ - Pgno child = get4byte(&pPage->aData[pPage->hdrOffset+8]); - ptrmapGet(pBt, child, &e, &n); - assert( n==pPage->pgno && e==PTRMAP_BTREE ); - } - } - return 1; -} -#endif - -/* -** This function is used to copy the contents of the b-tree node stored -** on page pFrom to page pTo. If page pFrom was not a leaf page, then -** the pointer-map entries for each child page are updated so that the -** parent page stored in the pointer map is page pTo. If pFrom contained -** any cells with overflow page pointers, then the corresponding pointer -** map entries are also updated so that the parent page is page pTo. -** -** If pFrom is currently carrying any overflow cells (entries in the -** MemPage.apOvfl[] array), they are not copied to pTo. -** -** Before returning, page pTo is reinitialized using btreeInitPage(). -** -** The performance of this function is not critical. It is only used by -** the balance_shallower() and balance_deeper() procedures, neither of -** which are called often under normal circumstances. -*/ -static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ - if( (*pRC)==SQLITE_OK ){ - BtShared * const pBt = pFrom->pBt; - u8 * const aFrom = pFrom->aData; - u8 * const aTo = pTo->aData; - int const iFromHdr = pFrom->hdrOffset; - int const iToHdr = ((pTo->pgno==1) ? 100 : 0); - int rc; - int iData; - - - assert( pFrom->isInit ); - assert( pFrom->nFree>=iToHdr ); - assert( get2byte(&aFrom[iFromHdr+5]) <= (int)pBt->usableSize ); - - /* Copy the b-tree node content from page pFrom to page pTo. */ - iData = get2byte(&aFrom[iFromHdr+5]); - memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); - memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); - - /* Reinitialize page pTo so that the contents of the MemPage structure - ** match the new data. The initialization of pTo can actually fail under - ** fairly obscure circumstances, even though it is a copy of initialized - ** page pFrom. - */ - pTo->isInit = 0; - rc = btreeInitPage(pTo); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; - } - - /* If this is an auto-vacuum database, update the pointer-map entries - ** for any b-tree or overflow pages that pTo now contains the pointers to. - */ - if( ISAUTOVACUUM ){ - *pRC = setChildPtrmaps(pTo); - } - } -} - -/* -** This routine redistributes cells on the iParentIdx'th child of pParent -** (hereafter "the page") and up to 2 siblings so that all pages have about the -** same amount of free space. Usually a single sibling on either side of the -** page are used in the balancing, though both siblings might come from one -** side if the page is the first or last child of its parent. If the page -** has fewer than 2 siblings (something which can only happen if the page -** is a root page or a child of a root page) then all available siblings -** participate in the balancing. -** -** The number of siblings of the page might be increased or decreased by -** one or two in an effort to keep pages nearly full but not over full. -** -** Note that when this routine is called, some of the cells on the page -** might not actually be stored in MemPage.aData[]. This can happen -** if the page is overfull. This routine ensures that all cells allocated -** to the page and its siblings fit into MemPage.aData[] before returning. -** -** In the course of balancing the page and its siblings, cells may be -** inserted into or removed from the parent page (pParent). Doing so -** may cause the parent page to become overfull or underfull. If this -** happens, it is the responsibility of the caller to invoke the correct -** balancing routine to fix this problem (see the balance() routine). -** -** If this routine fails for any reason, it might leave the database -** in a corrupted state. So if this routine fails, the database should -** be rolled back. -** -** The third argument to this function, aOvflSpace, is a pointer to a -** buffer big enough to hold one page. If while inserting cells into the parent -** page (pParent) the parent page becomes overfull, this buffer is -** used to store the parent's overflow cells. Because this function inserts -** a maximum of four divider cells into the parent page, and the maximum -** size of a cell stored within an internal node is always less than 1/4 -** of the page-size, the aOvflSpace[] buffer is guaranteed to be large -** enough for all overflow cells. -** -** If aOvflSpace is set to a null pointer, this function returns -** SQLITE_NOMEM. -*/ -#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM) -#pragma optimize("", off) -#endif -static int balance_nonroot( - MemPage *pParent, /* Parent page of siblings being balanced */ - int iParentIdx, /* Index of "the page" in pParent */ - u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */ - int isRoot, /* True if pParent is a root-page */ - int bBulk /* True if this call is part of a bulk load */ -){ - BtShared *pBt; /* The whole database */ - int nCell = 0; /* Number of cells in apCell[] */ - int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */ - int nNew = 0; /* Number of pages in apNew[] */ - int nOld; /* Number of pages in apOld[] */ - int i, j, k; /* Loop counters */ - int nxDiv; /* Next divider slot in pParent->aCell[] */ - int rc = SQLITE_OK; /* The return code */ - u16 leafCorrection; /* 4 if pPage is a leaf. 0 if not */ - int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ - int usableSpace; /* Bytes in pPage beyond the header */ - int pageFlags; /* Value of pPage->aData[0] */ - int subtotal; /* Subtotal of bytes in cells on one page */ - int iSpace1 = 0; /* First unused byte of aSpace1[] */ - int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */ - int szScratch; /* Size of scratch memory requested */ - MemPage *apOld[NB]; /* pPage and up to two siblings */ - MemPage *apCopy[NB]; /* Private copies of apOld[] pages */ - MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ - u8 *pRight; /* Location in parent of right-sibling pointer */ - u8 *apDiv[NB-1]; /* Divider cells in pParent */ - int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */ - int szNew[NB+2]; /* Combined size of cells place on i-th page */ - u8 **apCell = 0; /* All cells begin balanced */ - u16 *szCell; /* Local size of all cells in apCell[] */ - u8 *aSpace1; /* Space for copies of dividers cells */ - Pgno pgno; /* Temp var to store a page number in */ - - pBt = pParent->pBt; - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( sqlite3PagerIswriteable(pParent->pDbPage) ); - -#if 0 - TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno)); -#endif - - /* At this point pParent may have at most one overflow cell. And if - ** this overflow cell is present, it must be the cell with - ** index iParentIdx. This scenario comes about when this function - ** is called (indirectly) from sqlite3BtreeDelete(). - */ - assert( pParent->nOverflow==0 || pParent->nOverflow==1 ); - assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx ); - - if( !aOvflSpace ){ - return SQLITE_NOMEM; - } - - /* Find the sibling pages to balance. Also locate the cells in pParent - ** that divide the siblings. An attempt is made to find NN siblings on - ** either side of pPage. More siblings are taken from one side, however, - ** if there are fewer than NN siblings on the other side. If pParent - ** has NB or fewer children then all children of pParent are taken. - ** - ** This loop also drops the divider cells from the parent page. This - ** way, the remainder of the function does not have to deal with any - ** overflow cells in the parent page, since if any existed they will - ** have already been removed. - */ - i = pParent->nOverflow + pParent->nCell; - if( i<2 ){ - nxDiv = 0; - }else{ - assert( bBulk==0 || bBulk==1 ); - if( iParentIdx==0 ){ - nxDiv = 0; - }else if( iParentIdx==i ){ - nxDiv = i-2+bBulk; - }else{ - assert( bBulk==0 ); - nxDiv = iParentIdx-1; - } - i = 2-bBulk; - } - nOld = i+1; - if( (i+nxDiv-pParent->nOverflow)==pParent->nCell ){ - pRight = &pParent->aData[pParent->hdrOffset+8]; - }else{ - pRight = findCell(pParent, i+nxDiv-pParent->nOverflow); - } - pgno = get4byte(pRight); - while( 1 ){ - rc = getAndInitPage(pBt, pgno, &apOld[i]); - if( rc ){ - memset(apOld, 0, (i+1)*sizeof(MemPage*)); - goto balance_cleanup; - } - nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; - if( (i--)==0 ) break; - - if( i+nxDiv==pParent->aiOvfl[0] && pParent->nOverflow ){ - apDiv[i] = pParent->apOvfl[0]; - pgno = get4byte(apDiv[i]); - szNew[i] = cellSizePtr(pParent, apDiv[i]); - pParent->nOverflow = 0; - }else{ - apDiv[i] = findCell(pParent, i+nxDiv-pParent->nOverflow); - pgno = get4byte(apDiv[i]); - szNew[i] = cellSizePtr(pParent, apDiv[i]); - - /* Drop the cell from the parent page. apDiv[i] still points to - ** the cell within the parent, even though it has been dropped. - ** This is safe because dropping a cell only overwrites the first - ** four bytes of it, and this function does not need the first - ** four bytes of the divider cell. So the pointer is safe to use - ** later on. - ** - ** But not if we are in secure-delete mode. In secure-delete mode, - ** the dropCell() routine will overwrite the entire cell with zeroes. - ** In this case, temporarily copy the cell into the aOvflSpace[] - ** buffer. It will be copied out again as soon as the aSpace[] buffer - ** is allocated. */ - if( pBt->btsFlags & BTS_SECURE_DELETE ){ - int iOff; - - iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); - if( (iOff+szNew[i])>(int)pBt->usableSize ){ - rc = SQLITE_CORRUPT_BKPT; - memset(apOld, 0, (i+1)*sizeof(MemPage*)); - goto balance_cleanup; - }else{ - memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]); - apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; - } - } - dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc); - } - } - - /* Make nMaxCells a multiple of 4 in order to preserve 8-byte - ** alignment */ - nMaxCells = (nMaxCells + 3)&~3; - - /* - ** Allocate space for memory structures - */ - k = pBt->pageSize + ROUND8(sizeof(MemPage)); - szScratch = - nMaxCells*sizeof(u8*) /* apCell */ - + nMaxCells*sizeof(u16) /* szCell */ - + pBt->pageSize /* aSpace1 */ - + k*nOld; /* Page copies (apCopy) */ - apCell = sqlite3ScratchMalloc( szScratch ); - if( apCell==0 ){ - rc = SQLITE_NOMEM; - goto balance_cleanup; - } - szCell = (u16*)&apCell[nMaxCells]; - aSpace1 = (u8*)&szCell[nMaxCells]; - assert( EIGHT_BYTE_ALIGNMENT(aSpace1) ); - - /* - ** Load pointers to all cells on sibling pages and the divider cells - ** into the local apCell[] array. Make copies of the divider cells - ** into space obtained from aSpace1[] and remove the divider cells - ** from pParent. - ** - ** If the siblings are on leaf pages, then the child pointers of the - ** divider cells are stripped from the cells before they are copied - ** into aSpace1[]. In this way, all cells in apCell[] are without - ** child pointers. If siblings are not leaves, then all cell in - ** apCell[] include child pointers. Either way, all cells in apCell[] - ** are alike. - ** - ** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf. - ** leafData: 1 if pPage holds key+data and pParent holds only keys. - */ - leafCorrection = apOld[0]->leaf*4; - leafData = apOld[0]->hasData; - for(i=0; ipageSize + k*i]; - memcpy(pOld, apOld[i], sizeof(MemPage)); - pOld->aData = (void*)&pOld[1]; - memcpy(pOld->aData, apOld[i]->aData, pBt->pageSize); - - limit = pOld->nCell+pOld->nOverflow; - if( pOld->nOverflow>0 ){ - for(j=0; jaData; - u16 maskPage = pOld->maskPage; - u16 cellOffset = pOld->cellOffset; - for(j=0; jmaxLocal+23 ); - assert( iSpace1 <= (int)pBt->pageSize ); - memcpy(pTemp, apDiv[i], sz); - apCell[nCell] = pTemp+leafCorrection; - assert( leafCorrection==0 || leafCorrection==4 ); - szCell[nCell] = szCell[nCell] - leafCorrection; - if( !pOld->leaf ){ - assert( leafCorrection==0 ); - assert( pOld->hdrOffset==0 ); - /* The right pointer of the child page pOld becomes the left - ** pointer of the divider cell */ - memcpy(apCell[nCell], &pOld->aData[8], 4); - }else{ - assert( leafCorrection==4 ); - if( szCell[nCell]<4 ){ - /* Do not allow any cells smaller than 4 bytes. */ - szCell[nCell] = 4; - } - } - nCell++; - } - } - - /* - ** Figure out the number of pages needed to hold all nCell cells. - ** Store this number in "k". Also compute szNew[] which is the total - ** size of all cells on the i-th page and cntNew[] which is the index - ** in apCell[] of the cell that divides page i from page i+1. - ** cntNew[k] should equal nCell. - ** - ** Values computed by this block: - ** - ** k: The total number of sibling pages - ** szNew[i]: Spaced used on the i-th sibling page. - ** cntNew[i]: Index in apCell[] and szCell[] for the first cell to - ** the right of the i-th sibling page. - ** usableSpace: Number of bytes of space available on each sibling. - ** - */ - usableSpace = pBt->usableSize - 12 + leafCorrection; - for(subtotal=k=i=0; i usableSpace ){ - szNew[k] = subtotal - szCell[i]; - cntNew[k] = i; - if( leafData ){ i--; } - subtotal = 0; - k++; - if( k>NB+1 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; } - } - } - szNew[k] = subtotal; - cntNew[k] = nCell; - k++; - - /* - ** The packing computed by the previous block is biased toward the siblings - ** on the left side. The left siblings are always nearly full, while the - ** right-most sibling might be nearly empty. This block of code attempts - ** to adjust the packing of siblings to get a better balance. - ** - ** This adjustment is more than an optimization. The packing above might - ** be so out of balance as to be illegal. For example, the right-most - ** sibling might be completely empty. This adjustment is not optional. - */ - for(i=k-1; i>0; i--){ - int szRight = szNew[i]; /* Size of sibling on the right */ - int szLeft = szNew[i-1]; /* Size of sibling on the left */ - int r; /* Index of right-most cell in left sibling */ - int d; /* Index of first cell to the left of right sibling */ - - r = cntNew[i-1] - 1; - d = r + 1 - leafData; - assert( d0) or pPage is - ** a virtual root page. A virtual root page is when the real root - ** page is page 1 and we are the only child of that page. - ** - ** UPDATE: The assert() below is not necessarily true if the database - ** file is corrupt. The corruption will be detected and reported later - ** in this procedure so there is no need to act upon it now. - */ -#if 0 - assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) ); -#endif - - TRACE(("BALANCE: old: %d %d %d ", - apOld[0]->pgno, - nOld>=2 ? apOld[1]->pgno : 0, - nOld>=3 ? apOld[2]->pgno : 0 - )); - - /* - ** Allocate k new pages. Reuse old pages where possible. - */ - if( apOld[0]->pgno<=1 ){ - rc = SQLITE_CORRUPT_BKPT; - goto balance_cleanup; - } - pageFlags = apOld[0]->aData[0]; - for(i=0; ipDbPage); - nNew++; - if( rc ) goto balance_cleanup; - }else{ - assert( i>0 ); - rc = allocateBtreePage(pBt, &pNew, &pgno, (bBulk ? 1 : pgno), 0); - if( rc ) goto balance_cleanup; - apNew[i] = pNew; - nNew++; - - /* Set the pointer-map entry for the new sibling page. */ - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc); - if( rc!=SQLITE_OK ){ - goto balance_cleanup; - } - } - } - } - - /* Free any old pages that were not reused as new pages. - */ - while( ipgno; - int minI = i; - for(j=i+1; jpgno<(unsigned)minV ){ - minI = j; - minV = apNew[j]->pgno; - } - } - if( minI>i ){ - MemPage *pT; - pT = apNew[i]; - apNew[i] = apNew[minI]; - apNew[minI] = pT; - } - } - TRACE(("new: %d(%d) %d(%d) %d(%d) %d(%d) %d(%d)\n", - apNew[0]->pgno, szNew[0], - nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0, - nNew>=3 ? apNew[2]->pgno : 0, nNew>=3 ? szNew[2] : 0, - nNew>=4 ? apNew[3]->pgno : 0, nNew>=4 ? szNew[3] : 0, - nNew>=5 ? apNew[4]->pgno : 0, nNew>=5 ? szNew[4] : 0)); - - assert( sqlite3PagerIswriteable(pParent->pDbPage) ); - put4byte(pRight, apNew[nNew-1]->pgno); - - /* - ** Evenly distribute the data in apCell[] across the new pages. - ** Insert divider cells into pParent as necessary. - */ - j = 0; - for(i=0; inCell>0 || (nNew==1 && cntNew[0]==0) ); - assert( pNew->nOverflow==0 ); - - j = cntNew[i]; - - /* If the sibling page assembled above was not the right-most sibling, - ** insert a divider cell into the parent page. - */ - assert( ileaf ){ - memcpy(&pNew->aData[8], pCell, 4); - }else if( leafData ){ - /* If the tree is a leaf-data tree, and the siblings are leaves, - ** then there is no divider cell in apCell[]. Instead, the divider - ** cell consists of the integer key for the right-most cell of - ** the sibling-page assembled above only. - */ - CellInfo info; - j--; - btreeParseCellPtr(pNew, apCell[j], &info); - pCell = pTemp; - sz = 4 + putVarint(&pCell[4], info.nKey); - pTemp = 0; - }else{ - pCell -= 4; - /* Obscure case for non-leaf-data trees: If the cell at pCell was - ** previously stored on a leaf node, and its reported size was 4 - ** bytes, then it may actually be smaller than this - ** (see btreeParseCellPtr(), 4 bytes is the minimum size of - ** any cell). But it is important to pass the correct size to - ** insertCell(), so reparse the cell now. - ** - ** Note that this can never happen in an SQLite data file, as all - ** cells are at least 4 bytes. It only happens in b-trees used - ** to evaluate "IN (SELECT ...)" and similar clauses. - */ - if( szCell[j]==4 ){ - assert(leafCorrection==4); - sz = cellSizePtr(pParent, pCell); - } - } - iOvflSpace += sz; - assert( sz<=pBt->maxLocal+23 ); - assert( iOvflSpace <= (int)pBt->pageSize ); - insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc); - if( rc!=SQLITE_OK ) goto balance_cleanup; - assert( sqlite3PagerIswriteable(pParent->pDbPage) ); - - j++; - nxDiv++; - } - } - assert( j==nCell ); - assert( nOld>0 ); - assert( nNew>0 ); - if( (pageFlags & PTF_LEAF)==0 ){ - u8 *zChild = &apCopy[nOld-1]->aData[8]; - memcpy(&apNew[nNew-1]->aData[8], zChild, 4); - } - - if( isRoot && pParent->nCell==0 && pParent->hdrOffset<=apNew[0]->nFree ){ - /* The root page of the b-tree now contains no cells. The only sibling - ** page is the right-child of the parent. Copy the contents of the - ** child page into the parent, decreasing the overall height of the - ** b-tree structure by one. This is described as the "balance-shallower" - ** sub-algorithm in some documentation. - ** - ** If this is an auto-vacuum database, the call to copyNodeContent() - ** sets all pointer-map entries corresponding to database image pages - ** for which the pointer is stored within the content being copied. - ** - ** The second assert below verifies that the child page is defragmented - ** (it must be, as it was just reconstructed using assemblePage()). This - ** is important if the parent page happens to be page 1 of the database - ** image. */ - assert( nNew==1 ); - assert( apNew[0]->nFree == - (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2) - ); - copyNodeContent(apNew[0], pParent, &rc); - freePage(apNew[0], &rc); - }else if( ISAUTOVACUUM ){ - /* Fix the pointer-map entries for all the cells that were shifted around. - ** There are several different types of pointer-map entries that need to - ** be dealt with by this routine. Some of these have been set already, but - ** many have not. The following is a summary: - ** - ** 1) The entries associated with new sibling pages that were not - ** siblings when this function was called. These have already - ** been set. We don't need to worry about old siblings that were - ** moved to the free-list - the freePage() code has taken care - ** of those. - ** - ** 2) The pointer-map entries associated with the first overflow - ** page in any overflow chains used by new divider cells. These - ** have also already been taken care of by the insertCell() code. - ** - ** 3) If the sibling pages are not leaves, then the child pages of - ** cells stored on the sibling pages may need to be updated. - ** - ** 4) If the sibling pages are not internal intkey nodes, then any - ** overflow pages used by these cells may need to be updated - ** (internal intkey nodes never contain pointers to overflow pages). - ** - ** 5) If the sibling pages are not leaves, then the pointer-map - ** entries for the right-child pages of each sibling may need - ** to be updated. - ** - ** Cases 1 and 2 are dealt with above by other code. The next - ** block deals with cases 3 and 4 and the one after that, case 5. Since - ** setting a pointer map entry is a relatively expensive operation, this - ** code only sets pointer map entries for child or overflow pages that have - ** actually moved between pages. */ - MemPage *pNew = apNew[0]; - MemPage *pOld = apCopy[0]; - int nOverflow = pOld->nOverflow; - int iNextOld = pOld->nCell + nOverflow; - int iOverflow = (nOverflow ? pOld->aiOvfl[0] : -1); - j = 0; /* Current 'old' sibling page */ - k = 0; /* Current 'new' sibling page */ - for(i=0; inCell + pOld->nOverflow; - if( pOld->nOverflow ){ - nOverflow = pOld->nOverflow; - iOverflow = i + !leafData + pOld->aiOvfl[0]; - } - isDivider = !leafData; - } - - assert(nOverflow>0 || iOverflowaiOvfl[0]==pOld->aiOvfl[1]-1); - assert(nOverflow<3 || pOld->aiOvfl[1]==pOld->aiOvfl[2]-1); - if( i==iOverflow ){ - isDivider = 1; - if( (--nOverflow)>0 ){ - iOverflow++; - } - } - - if( i==cntNew[k] ){ - /* Cell i is the cell immediately following the last cell on new - ** sibling page k. If the siblings are not leaf pages of an - ** intkey b-tree, then cell i is a divider cell. */ - pNew = apNew[++k]; - if( !leafData ) continue; - } - assert( jpgno!=pNew->pgno ){ - if( !leafCorrection ){ - ptrmapPut(pBt, get4byte(apCell[i]), PTRMAP_BTREE, pNew->pgno, &rc); - } - if( szCell[i]>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, apCell[i], &rc); - } - } - } - - if( !leafCorrection ){ - for(i=0; iaData[8]); - ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc); - } - } - -#if 0 - /* The ptrmapCheckPages() contains assert() statements that verify that - ** all pointer map pages are set correctly. This is helpful while - ** debugging. This is usually disabled because a corrupt database may - ** cause an assert() statement to fail. */ - ptrmapCheckPages(apNew, nNew); - ptrmapCheckPages(&pParent, 1); -#endif - } - - assert( pParent->isInit ); - TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n", - nOld, nNew, nCell)); - - /* - ** Cleanup before returning. - */ -balance_cleanup: - sqlite3ScratchFree(apCell); - for(i=0; i= 1700 && defined(_M_ARM) -#pragma optimize("", on) -#endif - - -/* -** This function is called when the root page of a b-tree structure is -** overfull (has one or more overflow pages). -** -** A new child page is allocated and the contents of the current root -** page, including overflow cells, are copied into the child. The root -** page is then overwritten to make it an empty page with the right-child -** pointer pointing to the new page. -** -** Before returning, all pointer-map entries corresponding to pages -** that the new child-page now contains pointers to are updated. The -** entry corresponding to the new right-child pointer of the root -** page is also updated. -** -** If successful, *ppChild is set to contain a reference to the child -** page and SQLITE_OK is returned. In this case the caller is required -** to call releasePage() on *ppChild exactly once. If an error occurs, -** an error code is returned and *ppChild is set to 0. -*/ -static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ - int rc; /* Return value from subprocedures */ - MemPage *pChild = 0; /* Pointer to a new child page */ - Pgno pgnoChild = 0; /* Page number of the new child page */ - BtShared *pBt = pRoot->pBt; /* The BTree */ - - assert( pRoot->nOverflow>0 ); - assert( sqlite3_mutex_held(pBt->mutex) ); - - /* Make pRoot, the root page of the b-tree, writable. Allocate a new - ** page that will become the new right-child of pPage. Copy the contents - ** of the node stored on pRoot into the new child page. - */ - rc = sqlite3PagerWrite(pRoot->pDbPage); - if( rc==SQLITE_OK ){ - rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0); - copyNodeContent(pRoot, pChild, &rc); - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc); - } - } - if( rc ){ - *ppChild = 0; - releasePage(pChild); - return rc; - } - assert( sqlite3PagerIswriteable(pChild->pDbPage) ); - assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); - assert( pChild->nCell==pRoot->nCell ); - - TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); - - /* Copy the overflow cells from pRoot to pChild */ - memcpy(pChild->aiOvfl, pRoot->aiOvfl, - pRoot->nOverflow*sizeof(pRoot->aiOvfl[0])); - memcpy(pChild->apOvfl, pRoot->apOvfl, - pRoot->nOverflow*sizeof(pRoot->apOvfl[0])); - pChild->nOverflow = pRoot->nOverflow; - - /* Zero the contents of pRoot. Then install pChild as the right-child. */ - zeroPage(pRoot, pChild->aData[0] & ~PTF_LEAF); - put4byte(&pRoot->aData[pRoot->hdrOffset+8], pgnoChild); - - *ppChild = pChild; - return SQLITE_OK; -} - -/* -** The page that pCur currently points to has just been modified in -** some way. This function figures out if this modification means the -** tree needs to be balanced, and if so calls the appropriate balancing -** routine. Balancing routines are: -** -** balance_quick() -** balance_deeper() -** balance_nonroot() -*/ -static int balance(BtCursor *pCur){ - int rc = SQLITE_OK; - const int nMin = pCur->pBt->usableSize * 2 / 3; - u8 aBalanceQuickSpace[13]; - u8 *pFree = 0; - - TESTONLY( int balance_quick_called = 0 ); - TESTONLY( int balance_deeper_called = 0 ); - - do { - int iPage = pCur->iPage; - MemPage *pPage = pCur->apPage[iPage]; - - if( iPage==0 ){ - if( pPage->nOverflow ){ - /* The root page of the b-tree is overfull. In this case call the - ** balance_deeper() function to create a new child for the root-page - ** and copy the current contents of the root-page to it. The - ** next iteration of the do-loop will balance the child page. - */ - assert( (balance_deeper_called++)==0 ); - rc = balance_deeper(pPage, &pCur->apPage[1]); - if( rc==SQLITE_OK ){ - pCur->iPage = 1; - pCur->aiIdx[0] = 0; - pCur->aiIdx[1] = 0; - assert( pCur->apPage[1]->nOverflow ); - } - }else{ - break; - } - }else if( pPage->nOverflow==0 && pPage->nFree<=nMin ){ - break; - }else{ - MemPage * const pParent = pCur->apPage[iPage-1]; - int const iIdx = pCur->aiIdx[iPage-1]; - - rc = sqlite3PagerWrite(pParent->pDbPage); - if( rc==SQLITE_OK ){ -#ifndef SQLITE_OMIT_QUICKBALANCE - if( pPage->hasData - && pPage->nOverflow==1 - && pPage->aiOvfl[0]==pPage->nCell - && pParent->pgno!=1 - && pParent->nCell==iIdx - ){ - /* Call balance_quick() to create a new sibling of pPage on which - ** to store the overflow cell. balance_quick() inserts a new cell - ** into pParent, which may cause pParent overflow. If this - ** happens, the next interation of the do-loop will balance pParent - ** use either balance_nonroot() or balance_deeper(). Until this - ** happens, the overflow cell is stored in the aBalanceQuickSpace[] - ** buffer. - ** - ** The purpose of the following assert() is to check that only a - ** single call to balance_quick() is made for each call to this - ** function. If this were not verified, a subtle bug involving reuse - ** of the aBalanceQuickSpace[] might sneak in. - */ - assert( (balance_quick_called++)==0 ); - rc = balance_quick(pParent, pPage, aBalanceQuickSpace); - }else -#endif - { - /* In this case, call balance_nonroot() to redistribute cells - ** between pPage and up to 2 of its sibling pages. This involves - ** modifying the contents of pParent, which may cause pParent to - ** become overfull or underfull. The next iteration of the do-loop - ** will balance the parent page to correct this. - ** - ** If the parent page becomes overfull, the overflow cell or cells - ** are stored in the pSpace buffer allocated immediately below. - ** A subsequent iteration of the do-loop will deal with this by - ** calling balance_nonroot() (balance_deeper() may be called first, - ** but it doesn't deal with overflow cells - just moves them to a - ** different page). Once this subsequent call to balance_nonroot() - ** has completed, it is safe to release the pSpace buffer used by - ** the previous call, as the overflow cell data will have been - ** copied either into the body of a database page or into the new - ** pSpace buffer passed to the latter call to balance_nonroot(). - */ - u8 *pSpace = sqlite3PageMalloc(pCur->pBt->pageSize); - rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1, pCur->hints); - if( pFree ){ - /* If pFree is not NULL, it points to the pSpace buffer used - ** by a previous call to balance_nonroot(). Its contents are - ** now stored either on real database pages or within the - ** new pSpace buffer, so it may be safely freed here. */ - sqlite3PageFree(pFree); - } - - /* The pSpace buffer will be freed after the next call to - ** balance_nonroot(), or just before this function returns, whichever - ** comes first. */ - pFree = pSpace; - } - } - - pPage->nOverflow = 0; - - /* The next iteration of the do-loop balances the parent page. */ - releasePage(pPage); - pCur->iPage--; - } - }while( rc==SQLITE_OK ); - - if( pFree ){ - sqlite3PageFree(pFree); - } - return rc; -} - - -/* -** Insert a new record into the BTree. The key is given by (pKey,nKey) -** and the data is given by (pData,nData). The cursor is used only to -** define what table the record should be inserted into. The cursor -** is left pointing at a random location. -** -** For an INTKEY table, only the nKey value of the key is used. pKey is -** ignored. For a ZERODATA table, the pData and nData are both ignored. -** -** If the seekResult parameter is non-zero, then a successful call to -** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already -** been performed. seekResult is the search result returned (a negative -** number if pCur points at an entry that is smaller than (pKey, nKey), or -** a positive value if pCur points at an etry that is larger than -** (pKey, nKey)). -** -** If the seekResult parameter is non-zero, then the caller guarantees that -** cursor pCur is pointing at the existing copy of a row that is to be -** overwritten. If the seekResult parameter is 0, then cursor pCur may -** point to any entry or to no entry at all and so this function has to seek -** the cursor before the new key can be inserted. -*/ -SQLITE_PRIVATE int sqlite3BtreeInsert( - BtCursor *pCur, /* Insert data into the table of this cursor */ - const void *pKey, i64 nKey, /* The key of the new record */ - const void *pData, int nData, /* The data of the new record */ - int nZero, /* Number of extra 0 bytes to append to data */ - int appendBias, /* True if this is likely an append */ - int seekResult /* Result of prior MovetoUnpacked() call */ -){ - int rc; - int loc = seekResult; /* -1: before desired location +1: after */ - int szNew = 0; - int idx; - MemPage *pPage; - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; - unsigned char *oldCell; - unsigned char *newCell = 0; - - if( pCur->eState==CURSOR_FAULT ){ - assert( pCur->skipNext!=SQLITE_OK ); - return pCur->skipNext; - } - - assert( cursorHoldsMutex(pCur) ); - assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE - && (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); - - /* Assert that the caller has been consistent. If this cursor was opened - ** expecting an index b-tree, then the caller should be inserting blob - ** keys with no associated data. If the cursor was opened expecting an - ** intkey table, the caller should be inserting integer keys with a - ** blob of associated data. */ - assert( (pKey==0)==(pCur->pKeyInfo==0) ); - - /* Save the positions of any other cursors open on this table. - ** - ** In some cases, the call to btreeMoveto() below is a no-op. For - ** example, when inserting data into a table with auto-generated integer - ** keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the - ** integer key to use. It then calls this function to actually insert the - ** data into the intkey B-Tree. In this case btreeMoveto() recognizes - ** that the cursor is already where it needs to be and returns without - ** doing any work. To avoid thwarting these optimizations, it is important - ** not to clear the cursor here. - */ - rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); - if( rc ) return rc; - - /* If this is an insert into a table b-tree, invalidate any incrblob - ** cursors open on the row being replaced (assuming this is a replace - ** operation - if it is not, the following is a no-op). */ - if( pCur->pKeyInfo==0 ){ - invalidateIncrblobCursors(p, nKey, 0); - } - - if( !loc ){ - rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc); - if( rc ) return rc; - } - assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) ); - - pPage = pCur->apPage[pCur->iPage]; - assert( pPage->intKey || nKey>=0 ); - assert( pPage->leaf || !pPage->intKey ); - - TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", - pCur->pgnoRoot, nKey, nData, pPage->pgno, - loc==0 ? "overwrite" : "new entry")); - assert( pPage->isInit ); - allocateTempSpace(pBt); - newCell = pBt->pTmpSpace; - if( newCell==0 ) return SQLITE_NOMEM; - rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew); - if( rc ) goto end_insert; - assert( szNew==cellSizePtr(pPage, newCell) ); - assert( szNew <= MX_CELL_SIZE(pBt) ); - idx = pCur->aiIdx[pCur->iPage]; - if( loc==0 ){ - u16 szOld; - assert( idxnCell ); - rc = sqlite3PagerWrite(pPage->pDbPage); - if( rc ){ - goto end_insert; - } - oldCell = findCell(pPage, idx); - if( !pPage->leaf ){ - memcpy(newCell, oldCell, 4); - } - szOld = cellSizePtr(pPage, oldCell); - rc = clearCell(pPage, oldCell); - dropCell(pPage, idx, szOld, &rc); - if( rc ) goto end_insert; - }else if( loc<0 && pPage->nCell>0 ){ - assert( pPage->leaf ); - idx = ++pCur->aiIdx[pCur->iPage]; - }else{ - assert( pPage->leaf ); - } - insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); - assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); - - /* If no error has occurred and pPage has an overflow cell, call balance() - ** to redistribute the cells within the tree. Since balance() may move - ** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey - ** variables. - ** - ** Previous versions of SQLite called moveToRoot() to move the cursor - ** back to the root page as balance() used to invalidate the contents - ** of BtCursor.apPage[] and BtCursor.aiIdx[]. Instead of doing that, - ** set the cursor state to "invalid". This makes common insert operations - ** slightly faster. - ** - ** There is a subtle but important optimization here too. When inserting - ** multiple records into an intkey b-tree using a single cursor (as can - ** happen while processing an "INSERT INTO ... SELECT" statement), it - ** is advantageous to leave the cursor pointing to the last entry in - ** the b-tree if possible. If the cursor is left pointing to the last - ** entry in the table, and the next row inserted has an integer key - ** larger than the largest existing key, it is possible to insert the - ** row without seeking the cursor. This can be a big performance boost. - */ - pCur->info.nSize = 0; - pCur->validNKey = 0; - if( rc==SQLITE_OK && pPage->nOverflow ){ - rc = balance(pCur); - - /* Must make sure nOverflow is reset to zero even if the balance() - ** fails. Internal data structure corruption will result otherwise. - ** Also, set the cursor state to invalid. This stops saveCursorPosition() - ** from trying to save the current position of the cursor. */ - pCur->apPage[pCur->iPage]->nOverflow = 0; - pCur->eState = CURSOR_INVALID; - } - assert( pCur->apPage[pCur->iPage]->nOverflow==0 ); - -end_insert: - return rc; -} - -/* -** Delete the entry that the cursor is pointing to. The cursor -** is left pointing at a arbitrary location. -*/ -SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){ - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; - int rc; /* Return code */ - MemPage *pPage; /* Page to delete cell from */ - unsigned char *pCell; /* Pointer to cell to delete */ - int iCellIdx; /* Index of cell to delete */ - int iCellDepth; /* Depth of node containing pCell */ - - assert( cursorHoldsMutex(pCur) ); - assert( pBt->inTransaction==TRANS_WRITE ); - assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( pCur->wrFlag ); - assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); - assert( !hasReadConflicts(p, pCur->pgnoRoot) ); - - if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell) - || NEVER(pCur->eState!=CURSOR_VALID) - ){ - return SQLITE_ERROR; /* Something has gone awry. */ - } - - iCellDepth = pCur->iPage; - iCellIdx = pCur->aiIdx[iCellDepth]; - pPage = pCur->apPage[iCellDepth]; - pCell = findCell(pPage, iCellIdx); - - /* If the page containing the entry to delete is not a leaf page, move - ** the cursor to the largest entry in the tree that is smaller than - ** the entry being deleted. This cell will replace the cell being deleted - ** from the internal node. The 'previous' entry is used for this instead - ** of the 'next' entry, as the previous entry is always a part of the - ** sub-tree headed by the child page of the cell being deleted. This makes - ** balancing the tree following the delete operation easier. */ - if( !pPage->leaf ){ - int notUsed; - rc = sqlite3BtreePrevious(pCur, ¬Used); - if( rc ) return rc; - } - - /* Save the positions of any other cursors open on this table before - ** making any modifications. Make the page containing the entry to be - ** deleted writable. Then free any overflow pages associated with the - ** entry and finally remove the cell itself from within the page. - */ - rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); - if( rc ) return rc; - - /* If this is a delete operation to remove a row from a table b-tree, - ** invalidate any incrblob cursors open on the row being deleted. */ - if( pCur->pKeyInfo==0 ){ - invalidateIncrblobCursors(p, pCur->info.nKey, 0); - } - - rc = sqlite3PagerWrite(pPage->pDbPage); - if( rc ) return rc; - rc = clearCell(pPage, pCell); - dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell), &rc); - if( rc ) return rc; - - /* If the cell deleted was not located on a leaf page, then the cursor - ** is currently pointing to the largest entry in the sub-tree headed - ** by the child-page of the cell that was just deleted from an internal - ** node. The cell from the leaf node needs to be moved to the internal - ** node to replace the deleted cell. */ - if( !pPage->leaf ){ - MemPage *pLeaf = pCur->apPage[pCur->iPage]; - int nCell; - Pgno n = pCur->apPage[iCellDepth+1]->pgno; - unsigned char *pTmp; - - pCell = findCell(pLeaf, pLeaf->nCell-1); - nCell = cellSizePtr(pLeaf, pCell); - assert( MX_CELL_SIZE(pBt) >= nCell ); - - allocateTempSpace(pBt); - pTmp = pBt->pTmpSpace; - - rc = sqlite3PagerWrite(pLeaf->pDbPage); - insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); - dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); - if( rc ) return rc; - } - - /* Balance the tree. If the entry deleted was located on a leaf page, - ** then the cursor still points to that page. In this case the first - ** call to balance() repairs the tree, and the if(...) condition is - ** never true. - ** - ** Otherwise, if the entry deleted was on an internal node page, then - ** pCur is pointing to the leaf page from which a cell was removed to - ** replace the cell deleted from the internal node. This is slightly - ** tricky as the leaf node may be underfull, and the internal node may - ** be either under or overfull. In this case run the balancing algorithm - ** on the leaf node first. If the balance proceeds far enough up the - ** tree that we can be sure that any problem in the internal node has - ** been corrected, so be it. Otherwise, after balancing the leaf node, - ** walk the cursor up the tree to the internal node and balance it as - ** well. */ - rc = balance(pCur); - if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){ - while( pCur->iPage>iCellDepth ){ - releasePage(pCur->apPage[pCur->iPage--]); - } - rc = balance(pCur); - } - - if( rc==SQLITE_OK ){ - moveToRoot(pCur); - } - return rc; -} - -/* -** Create a new BTree table. Write into *piTable the page -** number for the root page of the new table. -** -** The type of type is determined by the flags parameter. Only the -** following values of flags are currently in use. Other values for -** flags might not work: -** -** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys -** BTREE_ZERODATA Used for SQL indices -*/ -static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){ - BtShared *pBt = p->pBt; - MemPage *pRoot; - Pgno pgnoRoot; - int rc; - int ptfFlags; /* Page-type flage for the root page of new table */ - - assert( sqlite3BtreeHoldsMutex(p) ); - assert( pBt->inTransaction==TRANS_WRITE ); - assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - -#ifdef SQLITE_OMIT_AUTOVACUUM - rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); - if( rc ){ - return rc; - } -#else - if( pBt->autoVacuum ){ - Pgno pgnoMove; /* Move a page here to make room for the root-page */ - MemPage *pPageMove; /* The page to move to. */ - - /* Creating a new table may probably require moving an existing database - ** to make room for the new tables root page. In case this page turns - ** out to be an overflow page, delete all overflow page-map caches - ** held by open cursors. - */ - invalidateAllOverflowCache(pBt); - - /* Read the value of meta[3] from the database to determine where the - ** root page of the new table should go. meta[3] is the largest root-page - ** created so far, so the new root-page is (meta[3]+1). - */ - sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); - pgnoRoot++; - - /* The new root-page may not be allocated on a pointer-map page, or the - ** PENDING_BYTE page. - */ - while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || - pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ - pgnoRoot++; - } - assert( pgnoRoot>=3 ); - - /* Allocate a page. The page that currently resides at pgnoRoot will - ** be moved to the allocated page (unless the allocated page happens - ** to reside at pgnoRoot). - */ - rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT); - if( rc!=SQLITE_OK ){ - return rc; - } - - if( pgnoMove!=pgnoRoot ){ - /* pgnoRoot is the page that will be used for the root-page of - ** the new table (assuming an error did not occur). But we were - ** allocated pgnoMove. If required (i.e. if it was not allocated - ** by extending the file), the current page at position pgnoMove - ** is already journaled. - */ - u8 eType = 0; - Pgno iPtrPage = 0; - - releasePage(pPageMove); - - /* Move the page currently at pgnoRoot to pgnoMove. */ - rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); - if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ - rc = SQLITE_CORRUPT_BKPT; - } - if( rc!=SQLITE_OK ){ - releasePage(pRoot); - return rc; - } - assert( eType!=PTRMAP_ROOTPAGE ); - assert( eType!=PTRMAP_FREEPAGE ); - rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0); - releasePage(pRoot); - - /* Obtain the page at pgnoRoot */ - if( rc!=SQLITE_OK ){ - return rc; - } - rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlite3PagerWrite(pRoot->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(pRoot); - return rc; - } - }else{ - pRoot = pPageMove; - } - - /* Update the pointer-map and meta-data with the new root-page number. */ - ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, &rc); - if( rc ){ - releasePage(pRoot); - return rc; - } - - /* When the new root page was allocated, page 1 was made writable in - ** order either to increase the database filesize, or to decrement the - ** freelist count. Hence, the sqlite3BtreeUpdateMeta() call cannot fail. - */ - assert( sqlite3PagerIswriteable(pBt->pPage1->pDbPage) ); - rc = sqlite3BtreeUpdateMeta(p, 4, pgnoRoot); - if( NEVER(rc) ){ - releasePage(pRoot); - return rc; - } - - }else{ - rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); - if( rc ) return rc; - } -#endif - assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); - if( createTabFlags & BTREE_INTKEY ){ - ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF; - }else{ - ptfFlags = PTF_ZERODATA | PTF_LEAF; - } - zeroPage(pRoot, ptfFlags); - sqlite3PagerUnref(pRoot->pDbPage); - assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); - *piTable = (int)pgnoRoot; - return SQLITE_OK; -} -SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){ - int rc; - sqlite3BtreeEnter(p); - rc = btreeCreateTable(p, piTable, flags); - sqlite3BtreeLeave(p); - return rc; -} - -/* -** Erase the given database page and all its children. Return -** the page to the freelist. -*/ -static int clearDatabasePage( - BtShared *pBt, /* The BTree that contains the table */ - Pgno pgno, /* Page number to clear */ - int freePageFlag, /* Deallocate page if true */ - int *pnChange /* Add number of Cells freed to this counter */ -){ - MemPage *pPage; - int rc; - unsigned char *pCell; - int i; - - assert( sqlite3_mutex_held(pBt->mutex) ); - if( pgno>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; - } - - rc = getAndInitPage(pBt, pgno, &pPage); - if( rc ) return rc; - for(i=0; inCell; i++){ - pCell = findCell(pPage, i); - if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); - if( rc ) goto cleardatabasepage_out; - } - rc = clearCell(pPage, pCell); - if( rc ) goto cleardatabasepage_out; - } - if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), 1, pnChange); - if( rc ) goto cleardatabasepage_out; - }else if( pnChange ){ - assert( pPage->intKey ); - *pnChange += pPage->nCell; - } - if( freePageFlag ){ - freePage(pPage, &rc); - }else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){ - zeroPage(pPage, pPage->aData[0] | PTF_LEAF); - } - -cleardatabasepage_out: - releasePage(pPage); - return rc; -} - -/* -** Delete all information from a single table in the database. iTable is -** the page number of the root of the table. After this routine returns, -** the root page is empty, but still exists. -** -** This routine will fail with SQLITE_LOCKED if there are any open -** read cursors on the table. Open write cursors are moved to the -** root of the table. -** -** If pnChange is not NULL, then table iTable must be an intkey table. The -** integer value pointed to by pnChange is incremented by the number of -** entries in the table. -*/ -SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ - int rc; - BtShared *pBt = p->pBt; - sqlite3BtreeEnter(p); - assert( p->inTrans==TRANS_WRITE ); - - rc = saveAllCursors(pBt, (Pgno)iTable, 0); - - if( SQLITE_OK==rc ){ - /* Invalidate all incrblob cursors open on table iTable (assuming iTable - ** is the root of a table b-tree - if it is not, the following call is - ** a no-op). */ - invalidateIncrblobCursors(p, 0, 1); - rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); - } - sqlite3BtreeLeave(p); - return rc; -} - -/* -** Erase all information in a table and add the root of the table to -** the freelist. Except, the root of the principle table (the one on -** page 1) is never added to the freelist. -** -** This routine will fail with SQLITE_LOCKED if there are any open -** cursors on the table. -** -** If AUTOVACUUM is enabled and the page at iTable is not the last -** root page in the database file, then the last root page -** in the database file is moved into the slot formerly occupied by -** iTable and that last slot formerly occupied by the last root page -** is added to the freelist instead of iTable. In this say, all -** root pages are kept at the beginning of the database file, which -** is necessary for AUTOVACUUM to work right. *piMoved is set to the -** page number that used to be the last root page in the file before -** the move. If no page gets moved, *piMoved is set to 0. -** The last root page is recorded in meta[3] and the value of -** meta[3] is updated by this procedure. -*/ -static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ - int rc; - MemPage *pPage = 0; - BtShared *pBt = p->pBt; - - assert( sqlite3BtreeHoldsMutex(p) ); - assert( p->inTrans==TRANS_WRITE ); - - /* It is illegal to drop a table if any cursors are open on the - ** database. This is because in auto-vacuum mode the backend may - ** need to move another root-page to fill a gap left by the deleted - ** root page. If an open cursor was using this page a problem would - ** occur. - ** - ** This error is caught long before control reaches this point. - */ - if( NEVER(pBt->pCursor) ){ - sqlite3ConnectionBlocked(p->db, pBt->pCursor->pBtree->db); - return SQLITE_LOCKED_SHAREDCACHE; - } - - rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); - if( rc ) return rc; - rc = sqlite3BtreeClearTable(p, iTable, 0); - if( rc ){ - releasePage(pPage); - return rc; - } - - *piMoved = 0; - - if( iTable>1 ){ -#ifdef SQLITE_OMIT_AUTOVACUUM - freePage(pPage, &rc); - releasePage(pPage); -#else - if( pBt->autoVacuum ){ - Pgno maxRootPgno; - sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); - - if( iTable==maxRootPgno ){ - /* If the table being dropped is the table with the largest root-page - ** number in the database, put the root page on the free list. - */ - freePage(pPage, &rc); - releasePage(pPage); - if( rc!=SQLITE_OK ){ - return rc; - } - }else{ - /* The table being dropped does not have the largest root-page - ** number in the database. So move the page that does into the - ** gap left by the deleted root-page. - */ - MemPage *pMove; - releasePage(pPage); - rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0); - releasePage(pMove); - if( rc!=SQLITE_OK ){ - return rc; - } - pMove = 0; - rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); - freePage(pMove, &rc); - releasePage(pMove); - if( rc!=SQLITE_OK ){ - return rc; - } - *piMoved = maxRootPgno; - } - - /* Set the new 'max-root-page' value in the database header. This - ** is the old value less one, less one more if that happens to - ** be a root-page number, less one again if that is the - ** PENDING_BYTE_PAGE. - */ - maxRootPgno--; - while( maxRootPgno==PENDING_BYTE_PAGE(pBt) - || PTRMAP_ISPAGE(pBt, maxRootPgno) ){ - maxRootPgno--; - } - assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); - - rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno); - }else{ - freePage(pPage, &rc); - releasePage(pPage); - } -#endif - }else{ - /* If sqlite3BtreeDropTable was called on page 1. - ** This really never should happen except in a corrupt - ** database. - */ - zeroPage(pPage, PTF_INTKEY|PTF_LEAF ); - releasePage(pPage); - } - return rc; -} -SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){ - int rc; - sqlite3BtreeEnter(p); - rc = btreeDropTable(p, iTable, piMoved); - sqlite3BtreeLeave(p); - return rc; -} - - -/* -** This function may only be called if the b-tree connection already -** has a read or write transaction open on the database. -** -** Read the meta-information out of a database file. Meta[0] -** is the number of free pages currently in the database. Meta[1] -** through meta[15] are available for use by higher layers. Meta[0] -** is read-only, the others are read/write. -** -** The schema layer numbers meta values differently. At the schema -** layer (and the SetCookie and ReadCookie opcodes) the number of -** free pages is not visible. So Cookie[0] is the same as Meta[1]. -*/ -SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ - BtShared *pBt = p->pBt; - - sqlite3BtreeEnter(p); - assert( p->inTrans>TRANS_NONE ); - assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) ); - assert( pBt->pPage1 ); - assert( idx>=0 && idx<=15 ); - - *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); - - /* If auto-vacuum is disabled in this build and this is an auto-vacuum - ** database, mark the database as read-only. */ -#ifdef SQLITE_OMIT_AUTOVACUUM - if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){ - pBt->btsFlags |= BTS_READ_ONLY; - } -#endif - - sqlite3BtreeLeave(p); -} - -/* -** Write meta-information back into the database. Meta[0] is -** read-only and may not be written. -*/ -SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){ - BtShared *pBt = p->pBt; - unsigned char *pP1; - int rc; - assert( idx>=1 && idx<=15 ); - sqlite3BtreeEnter(p); - assert( p->inTrans==TRANS_WRITE ); - assert( pBt->pPage1!=0 ); - pP1 = pBt->pPage1->aData; - rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); - if( rc==SQLITE_OK ){ - put4byte(&pP1[36 + idx*4], iMeta); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( idx==BTREE_INCR_VACUUM ){ - assert( pBt->autoVacuum || iMeta==0 ); - assert( iMeta==0 || iMeta==1 ); - pBt->incrVacuum = (u8)iMeta; - } -#endif - } - sqlite3BtreeLeave(p); - return rc; -} - -#ifndef SQLITE_OMIT_BTREECOUNT -/* -** The first argument, pCur, is a cursor opened on some b-tree. Count the -** number of entries in the b-tree and write the result to *pnEntry. -** -** SQLITE_OK is returned if the operation is successfully executed. -** Otherwise, if an error is encountered (i.e. an IO error or database -** corruption) an SQLite error code is returned. -*/ -SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){ - i64 nEntry = 0; /* Value to return in *pnEntry */ - int rc; /* Return code */ - - if( pCur->pgnoRoot==0 ){ - *pnEntry = 0; - return SQLITE_OK; - } - rc = moveToRoot(pCur); - - /* Unless an error occurs, the following loop runs one iteration for each - ** page in the B-Tree structure (not including overflow pages). - */ - while( rc==SQLITE_OK ){ - int iIdx; /* Index of child node in parent */ - MemPage *pPage; /* Current page of the b-tree */ - - /* If this is a leaf page or the tree is not an int-key tree, then - ** this page contains countable entries. Increment the entry counter - ** accordingly. - */ - pPage = pCur->apPage[pCur->iPage]; - if( pPage->leaf || !pPage->intKey ){ - nEntry += pPage->nCell; - } - - /* pPage is a leaf node. This loop navigates the cursor so that it - ** points to the first interior cell that it points to the parent of - ** the next page in the tree that has not yet been visited. The - ** pCur->aiIdx[pCur->iPage] value is set to the index of the parent cell - ** of the page, or to the number of cells in the page if the next page - ** to visit is the right-child of its parent. - ** - ** If all pages in the tree have been visited, return SQLITE_OK to the - ** caller. - */ - if( pPage->leaf ){ - do { - if( pCur->iPage==0 ){ - /* All pages of the b-tree have been visited. Return successfully. */ - *pnEntry = nEntry; - return SQLITE_OK; - } - moveToParent(pCur); - }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); - - pCur->aiIdx[pCur->iPage]++; - pPage = pCur->apPage[pCur->iPage]; - } - - /* Descend to the child node of the cell that the cursor currently - ** points at. This is the right-child if (iIdx==pPage->nCell). - */ - iIdx = pCur->aiIdx[pCur->iPage]; - if( iIdx==pPage->nCell ){ - rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); - }else{ - rc = moveToChild(pCur, get4byte(findCell(pPage, iIdx))); - } - } - - /* An error has occurred. Return an error code. */ - return rc; -} -#endif - -/* -** Return the pager associated with a BTree. This routine is used for -** testing and debugging only. -*/ -SQLITE_PRIVATE Pager *sqlite3BtreePager(Btree *p){ - return p->pBt->pPager; -} - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* -** Append a message to the error message string. -*/ -static void checkAppendMsg( - IntegrityCk *pCheck, - char *zMsg1, - const char *zFormat, - ... -){ - va_list ap; - if( !pCheck->mxErr ) return; - pCheck->mxErr--; - pCheck->nErr++; - va_start(ap, zFormat); - if( pCheck->errMsg.nChar ){ - sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1); - } - if( zMsg1 ){ - sqlite3StrAccumAppend(&pCheck->errMsg, zMsg1, -1); - } - sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap); - va_end(ap); - if( pCheck->errMsg.mallocFailed ){ - pCheck->mallocFailed = 1; - } -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK - -/* -** Return non-zero if the bit in the IntegrityCk.aPgRef[] array that -** corresponds to page iPg is already set. -*/ -static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ - assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); - return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07))); -} - -/* -** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg. -*/ -static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ - assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); - pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07)); -} - - -/* -** Add 1 to the reference count for page iPage. If this is the second -** reference to the page, add an error message to pCheck->zErrMsg. -** Return 1 if there are 2 ore more references to the page and 0 if -** if this is the first reference to the page. -** -** Also check that the page number is in bounds. -*/ -static int checkRef(IntegrityCk *pCheck, Pgno iPage, char *zContext){ - if( iPage==0 ) return 1; - if( iPage>pCheck->nPage ){ - checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage); - return 1; - } - if( getPageReferenced(pCheck, iPage) ){ - checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage); - return 1; - } - setPageReferenced(pCheck, iPage); - return 0; -} - -#ifndef SQLITE_OMIT_AUTOVACUUM -/* -** Check that the entry in the pointer-map for page iChild maps to -** page iParent, pointer type ptrType. If not, append an error message -** to pCheck. -*/ -static void checkPtrmap( - IntegrityCk *pCheck, /* Integrity check context */ - Pgno iChild, /* Child page number */ - u8 eType, /* Expected pointer map type */ - Pgno iParent, /* Expected pointer map parent page number */ - char *zContext /* Context description (used for error msg) */ -){ - int rc; - u8 ePtrmapType; - Pgno iPtrmapParent; - - rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1; - checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild); - return; - } - - if( ePtrmapType!=eType || iPtrmapParent!=iParent ){ - checkAppendMsg(pCheck, zContext, - "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", - iChild, eType, iParent, ePtrmapType, iPtrmapParent); - } -} -#endif - -/* -** Check the integrity of the freelist or of an overflow page list. -** Verify that the number of pages on the list is N. -*/ -static void checkList( - IntegrityCk *pCheck, /* Integrity checking context */ - int isFreeList, /* True for a freelist. False for overflow page list */ - int iPage, /* Page number for first page in the list */ - int N, /* Expected number of pages in the list */ - char *zContext /* Context for error messages */ -){ - int i; - int expected = N; - int iFirst = iPage; - while( N-- > 0 && pCheck->mxErr ){ - DbPage *pOvflPage; - unsigned char *pOvflData; - if( iPage<1 ){ - checkAppendMsg(pCheck, zContext, - "%d of %d pages missing from overflow list starting at %d", - N+1, expected, iFirst); - break; - } - if( checkRef(pCheck, iPage, zContext) ) break; - if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage) ){ - checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage); - break; - } - pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage); - if( isFreeList ){ - int n = get4byte(&pOvflData[4]); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pCheck->pBt->autoVacuum ){ - checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext); - } -#endif - if( n>(int)pCheck->pBt->usableSize/4-2 ){ - checkAppendMsg(pCheck, zContext, - "freelist leaf count too big on page %d", iPage); - N--; - }else{ - for(i=0; ipBt->autoVacuum ){ - checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext); - } -#endif - checkRef(pCheck, iFreePage, zContext); - } - N -= n; - } - } -#ifndef SQLITE_OMIT_AUTOVACUUM - else{ - /* If this database supports auto-vacuum and iPage is not the last - ** page in this overflow list, check that the pointer-map entry for - ** the following page matches iPage. - */ - if( pCheck->pBt->autoVacuum && N>0 ){ - i = get4byte(pOvflData); - checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage, zContext); - } - } -#endif - iPage = get4byte(pOvflData); - sqlite3PagerUnref(pOvflPage); - } -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* -** Do various sanity checks on a single page of a tree. Return -** the tree depth. Root pages return 0. Parents of root pages -** return 1, and so forth. -** -** These checks are done: -** -** 1. Make sure that cells and freeblocks do not overlap -** but combine to completely cover the page. -** NO 2. Make sure cell keys are in order. -** NO 3. Make sure no key is less than or equal to zLowerBound. -** NO 4. Make sure no key is greater than or equal to zUpperBound. -** 5. Check the integrity of overflow pages. -** 6. Recursively call checkTreePage on all children. -** 7. Verify that the depth of all children is the same. -** 8. Make sure this page is at least 33% full or else it is -** the root of the tree. -*/ -static int checkTreePage( - IntegrityCk *pCheck, /* Context for the sanity check */ - int iPage, /* Page number of the page to check */ - char *zParentContext, /* Parent context */ - i64 *pnParentMinKey, - i64 *pnParentMaxKey -){ - MemPage *pPage; - int i, rc, depth, d2, pgno, cnt; - int hdr, cellStart; - int nCell; - u8 *data; - BtShared *pBt; - int usableSize; - char zContext[100]; - char *hit = 0; - i64 nMinKey = 0; - i64 nMaxKey = 0; - - sqlite3_snprintf(sizeof(zContext), zContext, "Page %d: ", iPage); - - /* Check that the page exists - */ - pBt = pCheck->pBt; - usableSize = pBt->usableSize; - if( iPage==0 ) return 0; - if( checkRef(pCheck, iPage, zParentContext) ) return 0; - if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ - checkAppendMsg(pCheck, zContext, - "unable to get the page. error code=%d", rc); - return 0; - } - - /* Clear MemPage.isInit to make sure the corruption detection code in - ** btreeInitPage() is executed. */ - pPage->isInit = 0; - if( (rc = btreeInitPage(pPage))!=0 ){ - assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ - checkAppendMsg(pCheck, zContext, - "btreeInitPage() returns error code %d", rc); - releasePage(pPage); - return 0; - } - - /* Check out all the cells. - */ - depth = 0; - for(i=0; inCell && pCheck->mxErr; i++){ - u8 *pCell; - u32 sz; - CellInfo info; - - /* Check payload overflow pages - */ - sqlite3_snprintf(sizeof(zContext), zContext, - "On tree page %d cell %d: ", iPage, i); - pCell = findCell(pPage,i); - btreeParseCellPtr(pPage, pCell, &info); - sz = info.nData; - if( !pPage->intKey ) sz += (int)info.nKey; - /* For intKey pages, check that the keys are in order. - */ - else if( i==0 ) nMinKey = nMaxKey = info.nKey; - else{ - if( info.nKey <= nMaxKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey); - } - nMaxKey = info.nKey; - } - assert( sz==info.nPayload ); - if( (sz>info.nLocal) - && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize]) - ){ - int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4); - Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage, zContext); - } -#endif - checkList(pCheck, 0, pgnoOvfl, nPage, zContext); - } - - /* Check sanity of left child page. - */ - if( !pPage->leaf ){ - pgno = get4byte(pCell); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext); - } -#endif - d2 = checkTreePage(pCheck, pgno, zContext, &nMinKey, i==0 ? NULL : &nMaxKey); - if( i>0 && d2!=depth ){ - checkAppendMsg(pCheck, zContext, "Child page depth differs"); - } - depth = d2; - } - } - - if( !pPage->leaf ){ - pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - sqlite3_snprintf(sizeof(zContext), zContext, - "On page %d at right child: ", iPage); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext); - } -#endif - checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey); - } - - /* For intKey leaf pages, check that the min/max keys are in order - ** with any left/parent/right pages. - */ - if( pPage->leaf && pPage->intKey ){ - /* if we are a left child page */ - if( pnParentMinKey ){ - /* if we are the left most child page */ - if( !pnParentMaxKey ){ - if( nMaxKey > *pnParentMinKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (max larger than parent min of %lld)", - nMaxKey, *pnParentMinKey); - } - }else{ - if( nMinKey <= *pnParentMinKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (min less than parent min of %lld)", - nMinKey, *pnParentMinKey); - } - if( nMaxKey > *pnParentMaxKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (max larger than parent max of %lld)", - nMaxKey, *pnParentMaxKey); - } - *pnParentMinKey = nMaxKey; - } - /* else if we're a right child page */ - } else if( pnParentMaxKey ){ - if( nMinKey <= *pnParentMaxKey ){ - checkAppendMsg(pCheck, zContext, - "Rowid %lld out of order (min less than parent max of %lld)", - nMinKey, *pnParentMaxKey); - } - } - } - - /* Check for complete coverage of the page - */ - data = pPage->aData; - hdr = pPage->hdrOffset; - hit = sqlite3PageMalloc( pBt->pageSize ); - if( hit==0 ){ - pCheck->mallocFailed = 1; - }else{ - int contentOffset = get2byteNotZero(&data[hdr+5]); - assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ - memset(hit+contentOffset, 0, usableSize-contentOffset); - memset(hit, 1, contentOffset); - nCell = get2byte(&data[hdr+3]); - cellStart = hdr + 12 - 4*pPage->leaf; - for(i=0; i=usableSize ){ - checkAppendMsg(pCheck, 0, - "Corruption detected in cell %d on page %d",i,iPage); - }else{ - for(j=pc+size-1; j>=pc; j--) hit[j]++; - } - } - i = get2byte(&data[hdr+1]); - while( i>0 ){ - int size, j; - assert( i<=usableSize-4 ); /* Enforced by btreeInitPage() */ - size = get2byte(&data[i+2]); - assert( i+size<=usableSize ); /* Enforced by btreeInitPage() */ - for(j=i+size-1; j>=i; j--) hit[j]++; - j = get2byte(&data[i]); - assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */ - assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */ - i = j; - } - for(i=cnt=0; i1 ){ - checkAppendMsg(pCheck, 0, - "Multiple uses for byte %d of page %d", i, iPage); - break; - } - } - if( cnt!=data[hdr+7] ){ - checkAppendMsg(pCheck, 0, - "Fragmentation of %d bytes reported as %d on page %d", - cnt, data[hdr+7], iPage); - } - } - sqlite3PageFree(hit); - releasePage(pPage); - return depth+1; -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* -** This routine does a complete check of the given BTree file. aRoot[] is -** an array of pages numbers were each page number is the root page of -** a table. nRoot is the number of entries in aRoot. -** -** A read-only or read-write transaction must be opened before calling -** this function. -** -** Write the number of error seen in *pnErr. Except for some memory -** allocation errors, an error message held in memory obtained from -** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is -** returned. If a memory allocation error occurs, NULL is returned. -*/ -SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( - Btree *p, /* The btree to be checked */ - int *aRoot, /* An array of root pages numbers for individual trees */ - int nRoot, /* Number of entries in aRoot[] */ - int mxErr, /* Stop reporting errors after this many */ - int *pnErr /* Write number of errors seen to this variable */ -){ - Pgno i; - int nRef; - IntegrityCk sCheck; - BtShared *pBt = p->pBt; - char zErr[100]; - - sqlite3BtreeEnter(p); - assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); - nRef = sqlite3PagerRefcount(pBt->pPager); - sCheck.pBt = pBt; - sCheck.pPager = pBt->pPager; - sCheck.nPage = btreePagecount(sCheck.pBt); - sCheck.mxErr = mxErr; - sCheck.nErr = 0; - sCheck.mallocFailed = 0; - *pnErr = 0; - if( sCheck.nPage==0 ){ - sqlite3BtreeLeave(p); - return 0; - } - - sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1); - if( !sCheck.aPgRef ){ - *pnErr = 1; - sqlite3BtreeLeave(p); - return 0; - } - i = PENDING_BYTE_PAGE(pBt); - if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); - sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); - sCheck.errMsg.useMalloc = 2; - - /* Check the integrity of the freelist - */ - checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), - get4byte(&pBt->pPage1->aData[36]), "Main freelist: "); - - /* Check all the tables. - */ - for(i=0; (int)iautoVacuum && aRoot[i]>1 ){ - checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0); - } -#endif - checkTreePage(&sCheck, aRoot[i], "List of tree roots: ", NULL, NULL); - } - - /* Make sure every page in the file is referenced - */ - for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ -#ifdef SQLITE_OMIT_AUTOVACUUM - if( getPageReferenced(&sCheck, i)==0 ){ - checkAppendMsg(&sCheck, 0, "Page %d is never used", i); - } -#else - /* If the database supports auto-vacuum, make sure no tables contain - ** references to pointer-map pages. - */ - if( getPageReferenced(&sCheck, i)==0 && - (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ - checkAppendMsg(&sCheck, 0, "Page %d is never used", i); - } - if( getPageReferenced(&sCheck, i)!=0 && - (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ - checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i); - } -#endif - } - - /* Make sure this analysis did not leave any unref() pages. - ** This is an internal consistency check; an integrity check - ** of the integrity check. - */ - if( NEVER(nRef != sqlite3PagerRefcount(pBt->pPager)) ){ - checkAppendMsg(&sCheck, 0, - "Outstanding page count goes from %d to %d during this analysis", - nRef, sqlite3PagerRefcount(pBt->pPager) - ); - } - - /* Clean up and report errors. - */ - sqlite3BtreeLeave(p); - sqlite3_free(sCheck.aPgRef); - if( sCheck.mallocFailed ){ - sqlite3StrAccumReset(&sCheck.errMsg); - *pnErr = sCheck.nErr+1; - return 0; - } - *pnErr = sCheck.nErr; - if( sCheck.nErr==0 ) sqlite3StrAccumReset(&sCheck.errMsg); - return sqlite3StrAccumFinish(&sCheck.errMsg); -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -/* -** Return the full pathname of the underlying database file. Return -** an empty string if the database is in-memory or a TEMP database. -** -** The pager filename is invariant as long as the pager is -** open so it is safe to access without the BtShared mutex. -*/ -SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *p){ - assert( p->pBt->pPager!=0 ); - return sqlite3PagerFilename(p->pBt->pPager, 1); -} - -/* -** Return the pathname of the journal file for this database. The return -** value of this routine is the same regardless of whether the journal file -** has been created or not. -** -** The pager journal filename is invariant as long as the pager is -** open so it is safe to access without the BtShared mutex. -*/ -SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *p){ - assert( p->pBt->pPager!=0 ); - return sqlite3PagerJournalname(p->pBt->pPager); -} - -/* -** Return non-zero if a transaction is active. -*/ -SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree *p){ - assert( p==0 || sqlite3_mutex_held(p->db->mutex) ); - return (p && (p->inTrans==TRANS_WRITE)); -} - -#ifndef SQLITE_OMIT_WAL -/* -** Run a checkpoint on the Btree passed as the first argument. -** -** Return SQLITE_LOCKED if this or any other connection has an open -** transaction on the shared-cache the argument Btree is connected to. -** -** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. -*/ -SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){ - int rc = SQLITE_OK; - if( p ){ - BtShared *pBt = p->pBt; - sqlite3BtreeEnter(p); - if( pBt->inTransaction!=TRANS_NONE ){ - rc = SQLITE_LOCKED; - }else{ - rc = sqlite3PagerCheckpoint(pBt->pPager, eMode, pnLog, pnCkpt); - } - sqlite3BtreeLeave(p); - } - return rc; -} -#endif - -/* -** Return non-zero if a read (or write) transaction is active. -*/ -SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree *p){ - assert( p ); - assert( sqlite3_mutex_held(p->db->mutex) ); - return p->inTrans!=TRANS_NONE; -} - -SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree *p){ - assert( p ); - assert( sqlite3_mutex_held(p->db->mutex) ); - return p->nBackup!=0; -} - -/* -** This function returns a pointer to a blob of memory associated with -** a single shared-btree. The memory is used by client code for its own -** purposes (for example, to store a high-level schema associated with -** the shared-btree). The btree layer manages reference counting issues. -** -** The first time this is called on a shared-btree, nBytes bytes of memory -** are allocated, zeroed, and returned to the caller. For each subsequent -** call the nBytes parameter is ignored and a pointer to the same blob -** of memory returned. -** -** If the nBytes parameter is 0 and the blob of memory has not yet been -** allocated, a null pointer is returned. If the blob has already been -** allocated, it is returned as normal. -** -** Just before the shared-btree is closed, the function passed as the -** xFree argument when the memory allocation was made is invoked on the -** blob of allocated memory. The xFree function should not call sqlite3_free() -** on the memory, the btree layer does that. -*/ -SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ - BtShared *pBt = p->pBt; - sqlite3BtreeEnter(p); - if( !pBt->pSchema && nBytes ){ - pBt->pSchema = sqlite3DbMallocZero(0, nBytes); - pBt->xFreeSchema = xFree; - } - sqlite3BtreeLeave(p); - return pBt->pSchema; -} - -/* -** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared -** btree as the argument handle holds an exclusive lock on the -** sqlite_master table. Otherwise SQLITE_OK. -*/ -SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *p){ - int rc; - assert( sqlite3_mutex_held(p->db->mutex) ); - sqlite3BtreeEnter(p); - rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); - assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE ); - sqlite3BtreeLeave(p); - return rc; -} - - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Obtain a lock on the table whose root page is iTab. The -** lock is a write lock if isWritelock is true or a read lock -** if it is false. -*/ -SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ - int rc = SQLITE_OK; - assert( p->inTrans!=TRANS_NONE ); - if( p->sharable ){ - u8 lockType = READ_LOCK + isWriteLock; - assert( READ_LOCK+1==WRITE_LOCK ); - assert( isWriteLock==0 || isWriteLock==1 ); - - sqlite3BtreeEnter(p); - rc = querySharedCacheTableLock(p, iTab, lockType); - if( rc==SQLITE_OK ){ - rc = setSharedCacheTableLock(p, iTab, lockType); - } - sqlite3BtreeLeave(p); - } - return rc; -} -#endif - -#ifndef SQLITE_OMIT_INCRBLOB -/* -** Argument pCsr must be a cursor opened for writing on an -** INTKEY table currently pointing at a valid table entry. -** This function modifies the data stored as part of that entry. -** -** Only the data content may only be modified, it is not possible to -** change the length of the data stored. If this function is called with -** parameters that attempt to write past the end of the existing data, -** no modifications are made and SQLITE_CORRUPT is returned. -*/ -SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ - int rc; - assert( cursorHoldsMutex(pCsr) ); - assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); - assert( pCsr->isIncrblobHandle ); - - rc = restoreCursorPosition(pCsr); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pCsr->eState!=CURSOR_REQUIRESEEK ); - if( pCsr->eState!=CURSOR_VALID ){ - return SQLITE_ABORT; - } - - /* Check some assumptions: - ** (a) the cursor is open for writing, - ** (b) there is a read/write transaction open, - ** (c) the connection holds a write-lock on the table (if required), - ** (d) there are no conflicting read-locks, and - ** (e) the cursor points at a valid row of an intKey table. - */ - if( !pCsr->wrFlag ){ - return SQLITE_READONLY; - } - assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 - && pCsr->pBt->inTransaction==TRANS_WRITE ); - assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); - assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); - assert( pCsr->apPage[pCsr->iPage]->intKey ); - - return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1); -} - -/* -** Set a flag on this cursor to cache the locations of pages from the -** overflow list for the current row. This is used by cursors opened -** for incremental blob IO only. -** -** This function sets a flag only. The actual page location cache -** (stored in BtCursor.aOverflow[]) is allocated and used by function -** accessPayload() (the worker function for sqlite3BtreeData() and -** sqlite3BtreePutData()). -*/ -SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - invalidateOverflowCache(pCur); - pCur->isIncrblobHandle = 1; -} -#endif - -/* -** Set both the "read version" (single byte at byte offset 18) and -** "write version" (single byte at byte offset 19) fields in the database -** header to iVersion. -*/ -SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ - BtShared *pBt = pBtree->pBt; - int rc; /* Return code */ - - assert( iVersion==1 || iVersion==2 ); - - /* If setting the version fields to 1, do not automatically open the - ** WAL connection, even if the version fields are currently set to 2. - */ - pBt->btsFlags &= ~BTS_NO_WAL; - if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL; - - rc = sqlite3BtreeBeginTrans(pBtree, 0); - if( rc==SQLITE_OK ){ - u8 *aData = pBt->pPage1->aData; - if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){ - rc = sqlite3BtreeBeginTrans(pBtree, 2); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); - if( rc==SQLITE_OK ){ - aData[18] = (u8)iVersion; - aData[19] = (u8)iVersion; - } - } - } - } - - pBt->btsFlags &= ~BTS_NO_WAL; - return rc; -} - -/* -** set the mask of hint flags for cursor pCsr. Currently the only valid -** values are 0 and BTREE_BULKLOAD. -*/ -SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){ - assert( mask==BTREE_BULKLOAD || mask==0 ); - pCsr->hints = mask; -} - -/************** End of btree.c ***********************************************/ -/************** Begin file backup.c ******************************************/ -/* -** 2009 January 28 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the implementation of the sqlite3_backup_XXX() -** API functions and the related features. -*/ - -/* Macro to find the minimum of two numeric values. -*/ -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -/* -** Structure allocated for each backup operation. -*/ -struct sqlite3_backup { - sqlite3* pDestDb; /* Destination database handle */ - Btree *pDest; /* Destination b-tree file */ - u32 iDestSchema; /* Original schema cookie in destination */ - int bDestLocked; /* True once a write-transaction is open on pDest */ - - Pgno iNext; /* Page number of the next source page to copy */ - sqlite3* pSrcDb; /* Source database handle */ - Btree *pSrc; /* Source b-tree file */ - - int rc; /* Backup process error code */ - - /* These two variables are set by every call to backup_step(). They are - ** read by calls to backup_remaining() and backup_pagecount(). - */ - Pgno nRemaining; /* Number of pages left to copy */ - Pgno nPagecount; /* Total number of pages to copy */ - - int isAttached; /* True once backup has been registered with pager */ - sqlite3_backup *pNext; /* Next backup associated with source pager */ -}; - -/* -** THREAD SAFETY NOTES: -** -** Once it has been created using backup_init(), a single sqlite3_backup -** structure may be accessed via two groups of thread-safe entry points: -** -** * Via the sqlite3_backup_XXX() API function backup_step() and -** backup_finish(). Both these functions obtain the source database -** handle mutex and the mutex associated with the source BtShared -** structure, in that order. -** -** * Via the BackupUpdate() and BackupRestart() functions, which are -** invoked by the pager layer to report various state changes in -** the page cache associated with the source database. The mutex -** associated with the source database BtShared structure will always -** be held when either of these functions are invoked. -** -** The other sqlite3_backup_XXX() API functions, backup_remaining() and -** backup_pagecount() are not thread-safe functions. If they are called -** while some other thread is calling backup_step() or backup_finish(), -** the values returned may be invalid. There is no way for a call to -** BackupUpdate() or BackupRestart() to interfere with backup_remaining() -** or backup_pagecount(). -** -** Depending on the SQLite configuration, the database handles and/or -** the Btree objects may have their own mutexes that require locking. -** Non-sharable Btrees (in-memory databases for example), do not have -** associated mutexes. -*/ - -/* -** Return a pointer corresponding to database zDb (i.e. "main", "temp") -** in connection handle pDb. If such a database cannot be found, return -** a NULL pointer and write an error message to pErrorDb. -** -** If the "temp" database is requested, it may need to be opened by this -** function. If an error occurs while doing so, return 0 and write an -** error message to pErrorDb. -*/ -static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ - int i = sqlite3FindDbName(pDb, zDb); - - if( i==1 ){ - Parse *pParse; - int rc = 0; - pParse = sqlite3StackAllocZero(pErrorDb, sizeof(*pParse)); - if( pParse==0 ){ - sqlite3Error(pErrorDb, SQLITE_NOMEM, "out of memory"); - rc = SQLITE_NOMEM; - }else{ - pParse->db = pDb; - if( sqlite3OpenTempDatabase(pParse) ){ - sqlite3Error(pErrorDb, pParse->rc, "%s", pParse->zErrMsg); - rc = SQLITE_ERROR; - } - sqlite3DbFree(pErrorDb, pParse->zErrMsg); - sqlite3StackFree(pErrorDb, pParse); - } - if( rc ){ - return 0; - } - } - - if( i<0 ){ - sqlite3Error(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb); - return 0; - } - - return pDb->aDb[i].pBt; -} - -/* -** Attempt to set the page size of the destination to match the page size -** of the source. -*/ -static int setDestPgsz(sqlite3_backup *p){ - int rc; - rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0); - return rc; -} - -/* -** Create an sqlite3_backup process to copy the contents of zSrcDb from -** connection handle pSrcDb to zDestDb in pDestDb. If successful, return -** a pointer to the new sqlite3_backup object. -** -** If an error occurs, NULL is returned and an error code and error message -** stored in database handle pDestDb. -*/ -SQLITE_API sqlite3_backup *sqlite3_backup_init( - sqlite3* pDestDb, /* Database to write to */ - const char *zDestDb, /* Name of database within pDestDb */ - sqlite3* pSrcDb, /* Database connection to read from */ - const char *zSrcDb /* Name of database within pSrcDb */ -){ - sqlite3_backup *p; /* Value to return */ - - /* Lock the source database handle. The destination database - ** handle is not locked in this routine, but it is locked in - ** sqlite3_backup_step(). The user is required to ensure that no - ** other thread accesses the destination handle for the duration - ** of the backup operation. Any attempt to use the destination - ** database connection while a backup is in progress may cause - ** a malfunction or a deadlock. - */ - sqlite3_mutex_enter(pSrcDb->mutex); - sqlite3_mutex_enter(pDestDb->mutex); - - if( pSrcDb==pDestDb ){ - sqlite3Error( - pDestDb, SQLITE_ERROR, "source and destination must be distinct" - ); - p = 0; - }else { - /* Allocate space for a new sqlite3_backup object... - ** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a - ** call to sqlite3_backup_init() and is destroyed by a call to - ** sqlite3_backup_finish(). */ - p = (sqlite3_backup *)sqlite3MallocZero(sizeof(sqlite3_backup)); - if( !p ){ - sqlite3Error(pDestDb, SQLITE_NOMEM, 0); - } - } - - /* If the allocation succeeded, populate the new object. */ - if( p ){ - p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb); - p->pDest = findBtree(pDestDb, pDestDb, zDestDb); - p->pDestDb = pDestDb; - p->pSrcDb = pSrcDb; - p->iNext = 1; - p->isAttached = 0; - - if( 0==p->pSrc || 0==p->pDest || setDestPgsz(p)==SQLITE_NOMEM ){ - /* One (or both) of the named databases did not exist or an OOM - ** error was hit. The error has already been written into the - ** pDestDb handle. All that is left to do here is free the - ** sqlite3_backup structure. - */ - sqlite3_free(p); - p = 0; - } - } - if( p ){ - p->pSrc->nBackup++; - } - - sqlite3_mutex_leave(pDestDb->mutex); - sqlite3_mutex_leave(pSrcDb->mutex); - return p; -} - -/* -** Argument rc is an SQLite error code. Return true if this error is -** considered fatal if encountered during a backup operation. All errors -** are considered fatal except for SQLITE_BUSY and SQLITE_LOCKED. -*/ -static int isFatalError(int rc){ - return (rc!=SQLITE_OK && rc!=SQLITE_BUSY && ALWAYS(rc!=SQLITE_LOCKED)); -} - -/* -** Parameter zSrcData points to a buffer containing the data for -** page iSrcPg from the source database. Copy this data into the -** destination database. -*/ -static int backupOnePage( - sqlite3_backup *p, /* Backup handle */ - Pgno iSrcPg, /* Source database page to backup */ - const u8 *zSrcData, /* Source database page data */ - int bUpdate /* True for an update, false otherwise */ -){ - Pager * const pDestPager = sqlite3BtreePager(p->pDest); - const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc); - int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); - const int nCopy = MIN(nSrcPgsz, nDestPgsz); - const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; -#ifdef SQLITE_HAS_CODEC - /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is - ** guaranteed that the shared-mutex is held by this thread, handle - ** p->pSrc may not actually be the owner. */ - int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc); - int nDestReserve = sqlite3BtreeGetReserve(p->pDest); -#endif - int rc = SQLITE_OK; - i64 iOff; - - assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 ); - assert( p->bDestLocked ); - assert( !isFatalError(p->rc) ); - assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); - assert( zSrcData ); - - /* Catch the case where the destination is an in-memory database and the - ** page sizes of the source and destination differ. - */ - if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){ - rc = SQLITE_READONLY; - } - -#ifdef SQLITE_HAS_CODEC - /* Backup is not possible if the page size of the destination is changing - ** and a codec is in use. - */ - if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){ - rc = SQLITE_READONLY; - } - - /* Backup is not possible if the number of bytes of reserve space differ - ** between source and destination. If there is a difference, try to - ** fix the destination to agree with the source. If that is not possible, - ** then the backup cannot proceed. - */ - if( nSrcReserve!=nDestReserve ){ - u32 newPgsz = nSrcPgsz; - rc = sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve); - if( rc==SQLITE_OK && newPgsz!=nSrcPgsz ) rc = SQLITE_READONLY; - } -#endif - - /* This loop runs once for each destination page spanned by the source - ** page. For each iteration, variable iOff is set to the byte offset - ** of the destination page. - */ - for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOffpDest->pBt) ) continue; - if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg)) - && SQLITE_OK==(rc = sqlite3PagerWrite(pDestPg)) - ){ - const u8 *zIn = &zSrcData[iOff%nSrcPgsz]; - u8 *zDestData = sqlite3PagerGetData(pDestPg); - u8 *zOut = &zDestData[iOff%nDestPgsz]; - - /* Copy the data from the source page into the destination page. - ** Then clear the Btree layer MemPage.isInit flag. Both this module - ** and the pager code use this trick (clearing the first byte - ** of the page 'extra' space to invalidate the Btree layers - ** cached parse of the page). MemPage.isInit is marked - ** "MUST BE FIRST" for this purpose. - */ - memcpy(zOut, zIn, nCopy); - ((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0; - if( iOff==0 && bUpdate==0 ){ - sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc)); - } - } - sqlite3PagerUnref(pDestPg); - } - - return rc; -} - -/* -** If pFile is currently larger than iSize bytes, then truncate it to -** exactly iSize bytes. If pFile is not larger than iSize bytes, then -** this function is a no-op. -** -** Return SQLITE_OK if everything is successful, or an SQLite error -** code if an error occurs. -*/ -static int backupTruncateFile(sqlite3_file *pFile, i64 iSize){ - i64 iCurrent; - int rc = sqlite3OsFileSize(pFile, &iCurrent); - if( rc==SQLITE_OK && iCurrent>iSize ){ - rc = sqlite3OsTruncate(pFile, iSize); - } - return rc; -} - -/* -** Register this backup object with the associated source pager for -** callbacks when pages are changed or the cache invalidated. -*/ -static void attachBackupObject(sqlite3_backup *p){ - sqlite3_backup **pp; - assert( sqlite3BtreeHoldsMutex(p->pSrc) ); - pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc)); - p->pNext = *pp; - *pp = p; - p->isAttached = 1; -} - -/* -** Copy nPage pages from the source b-tree to the destination. -*/ -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ - int rc; - int destMode; /* Destination journal mode */ - int pgszSrc = 0; /* Source page size */ - int pgszDest = 0; /* Destination page size */ - - sqlite3_mutex_enter(p->pSrcDb->mutex); - sqlite3BtreeEnter(p->pSrc); - if( p->pDestDb ){ - sqlite3_mutex_enter(p->pDestDb->mutex); - } - - rc = p->rc; - if( !isFatalError(rc) ){ - Pager * const pSrcPager = sqlite3BtreePager(p->pSrc); /* Source pager */ - Pager * const pDestPager = sqlite3BtreePager(p->pDest); /* Dest pager */ - int ii; /* Iterator variable */ - int nSrcPage = -1; /* Size of source db in pages */ - int bCloseTrans = 0; /* True if src db requires unlocking */ - - /* If the source pager is currently in a write-transaction, return - ** SQLITE_BUSY immediately. - */ - if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ - rc = SQLITE_BUSY; - }else{ - rc = SQLITE_OK; - } - - /* Lock the destination database, if it is not locked already. */ - if( SQLITE_OK==rc && p->bDestLocked==0 - && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) - ){ - p->bDestLocked = 1; - sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema); - } - - /* If there is no open read-transaction on the source database, open - ** one now. If a transaction is opened here, then it will be closed - ** before this function exits. - */ - if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){ - rc = sqlite3BtreeBeginTrans(p->pSrc, 0); - bCloseTrans = 1; - } - - /* Do not allow backup if the destination database is in WAL mode - ** and the page sizes are different between source and destination */ - pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); - pgszDest = sqlite3BtreeGetPageSize(p->pDest); - destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); - if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ - rc = SQLITE_READONLY; - } - - /* Now that there is a read-lock on the source database, query the - ** source pager for the number of pages in the database. - */ - nSrcPage = (int)sqlite3BtreeLastPage(p->pSrc); - assert( nSrcPage>=0 ); - for(ii=0; (nPage<0 || iiiNext<=(Pgno)nSrcPage && !rc; ii++){ - const Pgno iSrcPg = p->iNext; /* Source page number */ - if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ - DbPage *pSrcPg; /* Source page object */ - rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); - if( rc==SQLITE_OK ){ - rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0); - sqlite3PagerUnref(pSrcPg); - } - } - p->iNext++; - } - if( rc==SQLITE_OK ){ - p->nPagecount = nSrcPage; - p->nRemaining = nSrcPage+1-p->iNext; - if( p->iNext>(Pgno)nSrcPage ){ - rc = SQLITE_DONE; - }else if( !p->isAttached ){ - attachBackupObject(p); - } - } - - /* Update the schema version field in the destination database. This - ** is to make sure that the schema-version really does change in - ** the case where the source and destination databases have the - ** same schema version. - */ - if( rc==SQLITE_DONE ){ - if( nSrcPage==0 ){ - rc = sqlite3BtreeNewDb(p->pDest); - nSrcPage = 1; - } - if( rc==SQLITE_OK || rc==SQLITE_DONE ){ - rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1); - } - if( rc==SQLITE_OK ){ - if( p->pDestDb ){ - sqlite3ResetAllSchemasOfConnection(p->pDestDb); - } - if( destMode==PAGER_JOURNALMODE_WAL ){ - rc = sqlite3BtreeSetVersion(p->pDest, 2); - } - } - if( rc==SQLITE_OK ){ - int nDestTruncate; - /* Set nDestTruncate to the final number of pages in the destination - ** database. The complication here is that the destination page - ** size may be different to the source page size. - ** - ** If the source page size is smaller than the destination page size, - ** round up. In this case the call to sqlite3OsTruncate() below will - ** fix the size of the file. However it is important to call - ** sqlite3PagerTruncateImage() here so that any pages in the - ** destination file that lie beyond the nDestTruncate page mark are - ** journalled by PagerCommitPhaseOne() before they are destroyed - ** by the file truncation. - */ - assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) ); - assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) ); - if( pgszSrcpDest->pBt) ){ - nDestTruncate--; - } - }else{ - nDestTruncate = nSrcPage * (pgszSrc/pgszDest); - } - assert( nDestTruncate>0 ); - - if( pgszSrc= iSize || ( - nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1) - && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest - )); - - /* This block ensures that all data required to recreate the original - ** database has been stored in the journal for pDestPager and the - ** journal synced to disk. So at this point we may safely modify - ** the database file in any way, knowing that if a power failure - ** occurs, the original database will be reconstructed from the - ** journal file. */ - sqlite3PagerPagecount(pDestPager, &nDstPage); - for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){ - if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){ - DbPage *pPg; - rc = sqlite3PagerGet(pDestPager, iPg, &pPg); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerWrite(pPg); - sqlite3PagerUnref(pPg); - } - } - } - if( rc==SQLITE_OK ){ - rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1); - } - - /* Write the extra pages and truncate the database file as required */ - iEnd = MIN(PENDING_BYTE + pgszDest, iSize); - for( - iOff=PENDING_BYTE+pgszSrc; - rc==SQLITE_OK && iOffpDest, 0)) - ){ - rc = SQLITE_DONE; - } - } - } - - /* If bCloseTrans is true, then this function opened a read transaction - ** on the source database. Close the read transaction here. There is - ** no need to check the return values of the btree methods here, as - ** "committing" a read-only transaction cannot fail. - */ - if( bCloseTrans ){ - TESTONLY( int rc2 ); - TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); - TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0); - assert( rc2==SQLITE_OK ); - } - - if( rc==SQLITE_IOERR_NOMEM ){ - rc = SQLITE_NOMEM; - } - p->rc = rc; - } - if( p->pDestDb ){ - sqlite3_mutex_leave(p->pDestDb->mutex); - } - sqlite3BtreeLeave(p->pSrc); - sqlite3_mutex_leave(p->pSrcDb->mutex); - return rc; -} - -/* -** Release all resources associated with an sqlite3_backup* handle. -*/ -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){ - sqlite3_backup **pp; /* Ptr to head of pagers backup list */ - sqlite3 *pSrcDb; /* Source database connection */ - int rc; /* Value to return */ - - /* Enter the mutexes */ - if( p==0 ) return SQLITE_OK; - pSrcDb = p->pSrcDb; - sqlite3_mutex_enter(pSrcDb->mutex); - sqlite3BtreeEnter(p->pSrc); - if( p->pDestDb ){ - sqlite3_mutex_enter(p->pDestDb->mutex); - } - - /* Detach this backup from the source pager. */ - if( p->pDestDb ){ - p->pSrc->nBackup--; - } - if( p->isAttached ){ - pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc)); - while( *pp!=p ){ - pp = &(*pp)->pNext; - } - *pp = p->pNext; - } - - /* If a transaction is still open on the Btree, roll it back. */ - sqlite3BtreeRollback(p->pDest, SQLITE_OK); - - /* Set the error code of the destination database handle. */ - rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; - sqlite3Error(p->pDestDb, rc, 0); - - /* Exit the mutexes and free the backup context structure. */ - if( p->pDestDb ){ - sqlite3LeaveMutexAndCloseZombie(p->pDestDb); - } - sqlite3BtreeLeave(p->pSrc); - if( p->pDestDb ){ - /* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a - ** call to sqlite3_backup_init() and is destroyed by a call to - ** sqlite3_backup_finish(). */ - sqlite3_free(p); - } - sqlite3LeaveMutexAndCloseZombie(pSrcDb); - return rc; -} - -/* -** Return the number of pages still to be backed up as of the most recent -** call to sqlite3_backup_step(). -*/ -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p){ - return p->nRemaining; -} - -/* -** Return the total number of pages in the source database as of the most -** recent call to sqlite3_backup_step(). -*/ -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p){ - return p->nPagecount; -} - -/* -** This function is called after the contents of page iPage of the -** source database have been modified. If page iPage has already been -** copied into the destination database, then the data written to the -** destination is now invalidated. The destination copy of iPage needs -** to be updated with the new data before the backup operation is -** complete. -** -** It is assumed that the mutex associated with the BtShared object -** corresponding to the source database is held when this function is -** called. -*/ -SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){ - sqlite3_backup *p; /* Iterator variable */ - for(p=pBackup; p; p=p->pNext){ - assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) ); - if( !isFatalError(p->rc) && iPageiNext ){ - /* The backup process p has already copied page iPage. But now it - ** has been modified by a transaction on the source pager. Copy - ** the new data into the backup. - */ - int rc; - assert( p->pDestDb ); - sqlite3_mutex_enter(p->pDestDb->mutex); - rc = backupOnePage(p, iPage, aData, 1); - sqlite3_mutex_leave(p->pDestDb->mutex); - assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); - if( rc!=SQLITE_OK ){ - p->rc = rc; - } - } - } -} - -/* -** Restart the backup process. This is called when the pager layer -** detects that the database has been modified by an external database -** connection. In this case there is no way of knowing which of the -** pages that have been copied into the destination database are still -** valid and which are not, so the entire process needs to be restarted. -** -** It is assumed that the mutex associated with the BtShared object -** corresponding to the source database is held when this function is -** called. -*/ -SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *pBackup){ - sqlite3_backup *p; /* Iterator variable */ - for(p=pBackup; p; p=p->pNext){ - assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) ); - p->iNext = 1; - } -} - -#ifndef SQLITE_OMIT_VACUUM -/* -** Copy the complete content of pBtFrom into pBtTo. A transaction -** must be active for both files. -** -** The size of file pTo may be reduced by this operation. If anything -** goes wrong, the transaction on pTo is rolled back. If successful, the -** transaction is committed before returning. -*/ -SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ - int rc; - sqlite3_file *pFd; /* File descriptor for database pTo */ - sqlite3_backup b; - sqlite3BtreeEnter(pTo); - sqlite3BtreeEnter(pFrom); - - assert( sqlite3BtreeIsInTrans(pTo) ); - pFd = sqlite3PagerFile(sqlite3BtreePager(pTo)); - if( pFd->pMethods ){ - i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom); - rc = sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte); - if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; - if( rc ) goto copy_finished; - } - - /* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set - ** to 0. This is used by the implementations of sqlite3_backup_step() - ** and sqlite3_backup_finish() to detect that they are being called - ** from this function, not directly by the user. - */ - memset(&b, 0, sizeof(b)); - b.pSrcDb = pFrom->db; - b.pSrc = pFrom; - b.pDest = pTo; - b.iNext = 1; - - /* 0x7FFFFFFF is the hard limit for the number of pages in a database - ** file. By passing this as the number of pages to copy to - ** sqlite3_backup_step(), we can guarantee that the copy finishes - ** within a single call (unless an error occurs). The assert() statement - ** checks this assumption - (p->rc) should be set to either SQLITE_DONE - ** or an error code. - */ - sqlite3_backup_step(&b, 0x7FFFFFFF); - assert( b.rc!=SQLITE_OK ); - rc = sqlite3_backup_finish(&b); - if( rc==SQLITE_OK ){ - pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; - }else{ - sqlite3PagerClearCache(sqlite3BtreePager(b.pDest)); - } - - assert( sqlite3BtreeIsInTrans(pTo)==0 ); -copy_finished: - sqlite3BtreeLeave(pFrom); - sqlite3BtreeLeave(pTo); - return rc; -} -#endif /* SQLITE_OMIT_VACUUM */ - -/************** End of backup.c **********************************************/ -/************** Begin file vdbemem.c *****************************************/ -/* -** 2004 May 26 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code use to manipulate "Mem" structure. A "Mem" -** stores a single value in the VDBE. Mem is an opaque structure visible -** only within the VDBE. Interface routines refer to a Mem using the -** name sqlite_value -*/ - -/* -** If pMem is an object with a valid string representation, this routine -** ensures the internal encoding for the string representation is -** 'desiredEnc', one of SQLITE_UTF8, SQLITE_UTF16LE or SQLITE_UTF16BE. -** -** If pMem is not a string object, or the encoding of the string -** representation is already stored using the requested encoding, then this -** routine is a no-op. -** -** SQLITE_OK is returned if the conversion is successful (or not required). -** SQLITE_NOMEM may be returned if a malloc() fails during conversion -** between formats. -*/ -SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ -#ifndef SQLITE_OMIT_UTF16 - int rc; -#endif - assert( (pMem->flags&MEM_RowSet)==0 ); - assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE - || desiredEnc==SQLITE_UTF16BE ); - if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){ - return SQLITE_OK; - } - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); -#ifdef SQLITE_OMIT_UTF16 - return SQLITE_ERROR; -#else - - /* MemTranslate() may return SQLITE_OK or SQLITE_NOMEM. If NOMEM is returned, - ** then the encoding of the value may not have changed. - */ - rc = sqlite3VdbeMemTranslate(pMem, (u8)desiredEnc); - assert(rc==SQLITE_OK || rc==SQLITE_NOMEM); - assert(rc==SQLITE_OK || pMem->enc!=desiredEnc); - assert(rc==SQLITE_NOMEM || pMem->enc==desiredEnc); - return rc; -#endif -} - -/* -** Make sure pMem->z points to a writable allocation of at least -** n bytes. -** -** If the third argument passed to this function is true, then memory -** cell pMem must contain a string or blob. In this case the content is -** preserved. Otherwise, if the third parameter to this function is false, -** any current string or blob value may be discarded. -** -** This function sets the MEM_Dyn flag and clears any xDel callback. -** It also clears MEM_Ephem and MEM_Static. If the preserve flag is -** not set, Mem.n is zeroed. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve){ - assert( 1 >= - ((pMem->zMalloc && pMem->zMalloc==pMem->z) ? 1 : 0) + - (((pMem->flags&MEM_Dyn)&&pMem->xDel) ? 1 : 0) + - ((pMem->flags&MEM_Ephem) ? 1 : 0) + - ((pMem->flags&MEM_Static) ? 1 : 0) - ); - assert( (pMem->flags&MEM_RowSet)==0 ); - - /* If the preserve flag is set to true, then the memory cell must already - ** contain a valid string or blob value. */ - assert( preserve==0 || pMem->flags&(MEM_Blob|MEM_Str) ); - - if( n<32 ) n = 32; - if( sqlite3DbMallocSize(pMem->db, pMem->zMalloc)z==pMem->zMalloc ){ - pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n); - preserve = 0; - }else{ - sqlite3DbFree(pMem->db, pMem->zMalloc); - pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n); - } - } - - if( pMem->z && preserve && pMem->zMalloc && pMem->z!=pMem->zMalloc ){ - memcpy(pMem->zMalloc, pMem->z, pMem->n); - } - if( pMem->flags&MEM_Dyn && pMem->xDel ){ - assert( pMem->xDel!=SQLITE_DYNAMIC ); - pMem->xDel((void *)(pMem->z)); - } - - pMem->z = pMem->zMalloc; - if( pMem->z==0 ){ - pMem->flags = MEM_Null; - }else{ - pMem->flags &= ~(MEM_Ephem|MEM_Static); - } - pMem->xDel = 0; - return (pMem->z ? SQLITE_OK : SQLITE_NOMEM); -} - -/* -** Make the given Mem object MEM_Dyn. In other words, make it so -** that any TEXT or BLOB content is stored in memory obtained from -** malloc(). In this way, we know that the memory is safe to be -** overwritten or altered. -** -** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){ - int f; - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( (pMem->flags&MEM_RowSet)==0 ); - ExpandBlob(pMem); - f = pMem->flags; - if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){ - if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){ - return SQLITE_NOMEM; - } - pMem->z[pMem->n] = 0; - pMem->z[pMem->n+1] = 0; - pMem->flags |= MEM_Term; -#ifdef SQLITE_DEBUG - pMem->pScopyFrom = 0; -#endif - } - - return SQLITE_OK; -} - -/* -** If the given Mem* has a zero-filled tail, turn it into an ordinary -** blob stored in dynamically allocated space. -*/ -#ifndef SQLITE_OMIT_INCRBLOB -SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){ - if( pMem->flags & MEM_Zero ){ - int nByte; - assert( pMem->flags&MEM_Blob ); - assert( (pMem->flags&MEM_RowSet)==0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - - /* Set nByte to the number of bytes required to store the expanded blob. */ - nByte = pMem->n + pMem->u.nZero; - if( nByte<=0 ){ - nByte = 1; - } - if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){ - return SQLITE_NOMEM; - } - - memset(&pMem->z[pMem->n], 0, pMem->u.nZero); - pMem->n += pMem->u.nZero; - pMem->flags &= ~(MEM_Zero|MEM_Term); - } - return SQLITE_OK; -} -#endif - - -/* -** Make sure the given Mem is \u0000 terminated. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){ - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - if( (pMem->flags & MEM_Term)!=0 || (pMem->flags & MEM_Str)==0 ){ - return SQLITE_OK; /* Nothing to do */ - } - if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){ - return SQLITE_NOMEM; - } - pMem->z[pMem->n] = 0; - pMem->z[pMem->n+1] = 0; - pMem->flags |= MEM_Term; - return SQLITE_OK; -} - -/* -** Add MEM_Str to the set of representations for the given Mem. Numbers -** are converted using sqlite3_snprintf(). Converting a BLOB to a string -** is a no-op. -** -** Existing representations MEM_Int and MEM_Real are *not* invalidated. -** -** A MEM_Null value will never be passed to this function. This function is -** used for converting values to text for returning to the user (i.e. via -** sqlite3_value_text()), or for ensuring that values to be used as btree -** keys are strings. In the former case a NULL pointer is returned the -** user and the later is an internal programming error. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, int enc){ - int rc = SQLITE_OK; - int fg = pMem->flags; - const int nByte = 32; - - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( !(fg&MEM_Zero) ); - assert( !(fg&(MEM_Str|MEM_Blob)) ); - assert( fg&(MEM_Int|MEM_Real) ); - assert( (pMem->flags&MEM_RowSet)==0 ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - - - if( sqlite3VdbeMemGrow(pMem, nByte, 0) ){ - return SQLITE_NOMEM; - } - - /* For a Real or Integer, use sqlite3_mprintf() to produce the UTF-8 - ** string representation of the value. Then, if the required encoding - ** is UTF-16le or UTF-16be do a translation. - ** - ** FIX ME: It would be better if sqlite3_snprintf() could do UTF-16. - */ - if( fg & MEM_Int ){ - sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i); - }else{ - assert( fg & MEM_Real ); - sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->r); - } - pMem->n = sqlite3Strlen30(pMem->z); - pMem->enc = SQLITE_UTF8; - pMem->flags |= MEM_Str|MEM_Term; - sqlite3VdbeChangeEncoding(pMem, enc); - return rc; -} - -/* -** Memory cell pMem contains the context of an aggregate function. -** This routine calls the finalize method for that function. The -** result of the aggregate is stored back into pMem. -** -** Return SQLITE_ERROR if the finalizer reports an error. SQLITE_OK -** otherwise. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ - int rc = SQLITE_OK; - if( ALWAYS(pFunc && pFunc->xFinalize) ){ - sqlite3_context ctx; - assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - memset(&ctx, 0, sizeof(ctx)); - ctx.s.flags = MEM_Null; - ctx.s.db = pMem->db; - ctx.pMem = pMem; - ctx.pFunc = pFunc; - pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ - assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel ); - sqlite3DbFree(pMem->db, pMem->zMalloc); - memcpy(pMem, &ctx.s, sizeof(ctx.s)); - rc = ctx.isError; - } - return rc; -} - -/* -** If the memory cell contains a string value that must be freed by -** invoking an external callback, free it now. Calling this function -** does not free any Mem.zMalloc buffer. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){ - assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) ); - if( p->flags&MEM_Agg ){ - sqlite3VdbeMemFinalize(p, p->u.pDef); - assert( (p->flags & MEM_Agg)==0 ); - sqlite3VdbeMemRelease(p); - }else if( p->flags&MEM_Dyn && p->xDel ){ - assert( (p->flags&MEM_RowSet)==0 ); - assert( p->xDel!=SQLITE_DYNAMIC ); - p->xDel((void *)p->z); - p->xDel = 0; - }else if( p->flags&MEM_RowSet ){ - sqlite3RowSetClear(p->u.pRowSet); - }else if( p->flags&MEM_Frame ){ - sqlite3VdbeMemSetNull(p); - } -} - -/* -** Release any memory held by the Mem. This may leave the Mem in an -** inconsistent state, for example with (Mem.z==0) and -** (Mem.type==SQLITE_TEXT). -*/ -SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){ - VdbeMemRelease(p); - sqlite3DbFree(p->db, p->zMalloc); - p->z = 0; - p->zMalloc = 0; - p->xDel = 0; -} - -/* -** Convert a 64-bit IEEE double into a 64-bit signed integer. -** If the double is too large, return 0x8000000000000000. -** -** Most systems appear to do this simply by assigning -** variables and without the extra range tests. But -** there are reports that windows throws an expection -** if the floating point value is out of range. (See ticket #2880.) -** Because we do not completely understand the problem, we will -** take the conservative approach and always do range tests -** before attempting the conversion. -*/ -static i64 doubleToInt64(double r){ -#ifdef SQLITE_OMIT_FLOATING_POINT - /* When floating-point is omitted, double and int64 are the same thing */ - return r; -#else - /* - ** Many compilers we encounter do not define constants for the - ** minimum and maximum 64-bit integers, or they define them - ** inconsistently. And many do not understand the "LL" notation. - ** So we define our own static constants here using nothing - ** larger than a 32-bit integer constant. - */ - static const i64 maxInt = LARGEST_INT64; - static const i64 minInt = SMALLEST_INT64; - - if( r<(double)minInt ){ - return minInt; - }else if( r>(double)maxInt ){ - /* minInt is correct here - not maxInt. It turns out that assigning - ** a very large positive number to an integer results in a very large - ** negative integer. This makes no sense, but it is what x86 hardware - ** does so for compatibility we will do the same in software. */ - return minInt; - }else{ - return (i64)r; - } -#endif -} - -/* -** Return some kind of integer value which is the best we can do -** at representing the value that *pMem describes as an integer. -** If pMem is an integer, then the value is exact. If pMem is -** a floating-point then the value returned is the integer part. -** If pMem is a string or blob, then we make an attempt to convert -** it into a integer and return that. If pMem represents an -** an SQL-NULL value, return 0. -** -** If pMem represents a string value, its encoding might be changed. -*/ -SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){ - int flags; - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - flags = pMem->flags; - if( flags & MEM_Int ){ - return pMem->u.i; - }else if( flags & MEM_Real ){ - return doubleToInt64(pMem->r); - }else if( flags & (MEM_Str|MEM_Blob) ){ - i64 value = 0; - assert( pMem->z || pMem->n==0 ); - testcase( pMem->z==0 ); - sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); - return value; - }else{ - return 0; - } -} - -/* -** Return the best representation of pMem that we can get into a -** double. If pMem is already a double or an integer, return its -** value. If it is a string or blob, try to convert it to a double. -** If it is a NULL, return 0.0. -*/ -SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){ - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - if( pMem->flags & MEM_Real ){ - return pMem->r; - }else if( pMem->flags & MEM_Int ){ - return (double)pMem->u.i; - }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - double val = (double)0; - sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc); - return val; - }else{ - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - return (double)0; - } -} - -/* -** The MEM structure is already a MEM_Real. Try to also make it a -** MEM_Int if we can. -*/ -SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){ - assert( pMem->flags & MEM_Real ); - assert( (pMem->flags & MEM_RowSet)==0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - - pMem->u.i = doubleToInt64(pMem->r); - - /* Only mark the value as an integer if - ** - ** (1) the round-trip conversion real->int->real is a no-op, and - ** (2) The integer is neither the largest nor the smallest - ** possible integer (ticket #3922) - ** - ** The second and third terms in the following conditional enforces - ** the second condition under the assumption that addition overflow causes - ** values to wrap around. On x86 hardware, the third term is always - ** true and could be omitted. But we leave it in because other - ** architectures might behave differently. - */ - if( pMem->r==(double)pMem->u.i - && pMem->u.i>SMALLEST_INT64 -#if defined(__i486__) || defined(__x86_64__) - && ALWAYS(pMem->u.iu.iflags |= MEM_Int; - } -} - -/* -** Convert pMem to type integer. Invalidate any prior representations. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){ - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( (pMem->flags & MEM_RowSet)==0 ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - - pMem->u.i = sqlite3VdbeIntValue(pMem); - MemSetTypeFlag(pMem, MEM_Int); - return SQLITE_OK; -} - -/* -** Convert pMem so that it is of type MEM_Real. -** Invalidate any prior representations. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem *pMem){ - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - - pMem->r = sqlite3VdbeRealValue(pMem); - MemSetTypeFlag(pMem, MEM_Real); - return SQLITE_OK; -} - -/* -** Convert pMem so that it has types MEM_Real or MEM_Int or both. -** Invalidate any prior representations. -** -** Every effort is made to force the conversion, even if the input -** is a string that does not look completely like a number. Convert -** as much of the string as we can and ignore the rest. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ - if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ - assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){ - MemSetTypeFlag(pMem, MEM_Int); - }else{ - pMem->r = sqlite3VdbeRealValue(pMem); - MemSetTypeFlag(pMem, MEM_Real); - sqlite3VdbeIntegerAffinity(pMem); - } - } - assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 ); - pMem->flags &= ~(MEM_Str|MEM_Blob); - return SQLITE_OK; -} - -/* -** Delete any previous value and set the value stored in *pMem to NULL. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){ - if( pMem->flags & MEM_Frame ){ - VdbeFrame *pFrame = pMem->u.pFrame; - pFrame->pParent = pFrame->v->pDelFrame; - pFrame->v->pDelFrame = pFrame; - } - if( pMem->flags & MEM_RowSet ){ - sqlite3RowSetClear(pMem->u.pRowSet); - } - MemSetTypeFlag(pMem, MEM_Null); - pMem->type = SQLITE_NULL; -} - -/* -** Delete any previous value and set the value to be a BLOB of length -** n containing all zeros. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ - sqlite3VdbeMemRelease(pMem); - pMem->flags = MEM_Blob|MEM_Zero; - pMem->type = SQLITE_BLOB; - pMem->n = 0; - if( n<0 ) n = 0; - pMem->u.nZero = n; - pMem->enc = SQLITE_UTF8; - -#ifdef SQLITE_OMIT_INCRBLOB - sqlite3VdbeMemGrow(pMem, n, 0); - if( pMem->z ){ - pMem->n = n; - memset(pMem->z, 0, n); - } -#endif -} - -/* -** Delete any previous value and set the value stored in *pMem to val, -** manifest type INTEGER. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ - sqlite3VdbeMemRelease(pMem); - pMem->u.i = val; - pMem->flags = MEM_Int; - pMem->type = SQLITE_INTEGER; -} - -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** Delete any previous value and set the value stored in *pMem to val, -** manifest type REAL. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem *pMem, double val){ - if( sqlite3IsNaN(val) ){ - sqlite3VdbeMemSetNull(pMem); - }else{ - sqlite3VdbeMemRelease(pMem); - pMem->r = val; - pMem->flags = MEM_Real; - pMem->type = SQLITE_FLOAT; - } -} -#endif - -/* -** Delete any previous value and set the value of pMem to be an -** empty boolean index. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemSetRowSet(Mem *pMem){ - sqlite3 *db = pMem->db; - assert( db!=0 ); - assert( (pMem->flags & MEM_RowSet)==0 ); - sqlite3VdbeMemRelease(pMem); - pMem->zMalloc = sqlite3DbMallocRaw(db, 64); - if( db->mallocFailed ){ - pMem->flags = MEM_Null; - }else{ - assert( pMem->zMalloc ); - pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc, - sqlite3DbMallocSize(db, pMem->zMalloc)); - assert( pMem->u.pRowSet!=0 ); - pMem->flags = MEM_RowSet; - } -} - -/* -** Return true if the Mem object contains a TEXT or BLOB that is -** too large - whose size exceeds SQLITE_MAX_LENGTH. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){ - assert( p->db!=0 ); - if( p->flags & (MEM_Str|MEM_Blob) ){ - int n = p->n; - if( p->flags & MEM_Zero ){ - n += p->u.nZero; - } - return n>p->db->aLimit[SQLITE_LIMIT_LENGTH]; - } - return 0; -} - -#ifdef SQLITE_DEBUG -/* -** This routine prepares a memory cell for modication by breaking -** its link to a shallow copy and by marking any current shallow -** copies of this cell as invalid. -** -** This is used for testing and debugging only - to make sure shallow -** copies are not misused. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ - int i; - Mem *pX; - for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){ - if( pX->pScopyFrom==pMem ){ - pX->flags |= MEM_Invalid; - pX->pScopyFrom = 0; - } - } - pMem->pScopyFrom = 0; -} -#endif /* SQLITE_DEBUG */ - -/* -** Size of struct Mem not including the Mem.zMalloc member. -*/ -#define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc)) - -/* -** Make an shallow copy of pFrom into pTo. Prior contents of -** pTo are freed. The pFrom->z field is not duplicated. If -** pFrom->z is used, then pTo->z points to the same thing as pFrom->z -** and flags gets srcType (either MEM_Ephem or MEM_Static). -*/ -SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){ - assert( (pFrom->flags & MEM_RowSet)==0 ); - VdbeMemRelease(pTo); - memcpy(pTo, pFrom, MEMCELLSIZE); - pTo->xDel = 0; - if( (pFrom->flags&MEM_Static)==0 ){ - pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem); - assert( srcType==MEM_Ephem || srcType==MEM_Static ); - pTo->flags |= srcType; - } -} - -/* -** Make a full copy of pFrom into pTo. Prior contents of pTo are -** freed before the copy is made. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){ - int rc = SQLITE_OK; - - assert( (pFrom->flags & MEM_RowSet)==0 ); - VdbeMemRelease(pTo); - memcpy(pTo, pFrom, MEMCELLSIZE); - pTo->flags &= ~MEM_Dyn; - - if( pTo->flags&(MEM_Str|MEM_Blob) ){ - if( 0==(pFrom->flags&MEM_Static) ){ - pTo->flags |= MEM_Ephem; - rc = sqlite3VdbeMemMakeWriteable(pTo); - } - } - - return rc; -} - -/* -** Transfer the contents of pFrom to pTo. Any existing value in pTo is -** freed. If pFrom contains ephemeral data, a copy is made. -** -** pFrom contains an SQL NULL when this routine returns. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){ - assert( pFrom->db==0 || sqlite3_mutex_held(pFrom->db->mutex) ); - assert( pTo->db==0 || sqlite3_mutex_held(pTo->db->mutex) ); - assert( pFrom->db==0 || pTo->db==0 || pFrom->db==pTo->db ); - - sqlite3VdbeMemRelease(pTo); - memcpy(pTo, pFrom, sizeof(Mem)); - pFrom->flags = MEM_Null; - pFrom->xDel = 0; - pFrom->zMalloc = 0; -} - -/* -** Change the value of a Mem to be a string or a BLOB. -** -** The memory management strategy depends on the value of the xDel -** parameter. If the value passed is SQLITE_TRANSIENT, then the -** string is copied into a (possibly existing) buffer managed by the -** Mem structure. Otherwise, any existing buffer is freed and the -** pointer copied. -** -** If the string is too large (if it exceeds the SQLITE_LIMIT_LENGTH -** size limit) then no memory allocation occurs. If the string can be -** stored without allocating memory, then it is. If a memory allocation -** is required to store the string, then value of pMem is unchanged. In -** either case, SQLITE_TOOBIG is returned. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemSetStr( - Mem *pMem, /* Memory cell to set to string value */ - const char *z, /* String pointer */ - int n, /* Bytes in string, or negative */ - u8 enc, /* Encoding of z. 0 for BLOBs */ - void (*xDel)(void*) /* Destructor function */ -){ - int nByte = n; /* New value for pMem->n */ - int iLimit; /* Maximum allowed string or blob size */ - u16 flags = 0; /* New value for pMem->flags */ - - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( (pMem->flags & MEM_RowSet)==0 ); - - /* If z is a NULL pointer, set pMem to contain an SQL NULL. */ - if( !z ){ - sqlite3VdbeMemSetNull(pMem); - return SQLITE_OK; - } - - if( pMem->db ){ - iLimit = pMem->db->aLimit[SQLITE_LIMIT_LENGTH]; - }else{ - iLimit = SQLITE_MAX_LENGTH; - } - flags = (enc==0?MEM_Blob:MEM_Str); - if( nByte<0 ){ - assert( enc!=0 ); - if( enc==SQLITE_UTF8 ){ - for(nByte=0; nByte<=iLimit && z[nByte]; nByte++){} - }else{ - for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} - } - flags |= MEM_Term; - } - - /* The following block sets the new values of Mem.z and Mem.xDel. It - ** also sets a flag in local variable "flags" to indicate the memory - ** management (one of MEM_Dyn or MEM_Static). - */ - if( xDel==SQLITE_TRANSIENT ){ - int nAlloc = nByte; - if( flags&MEM_Term ){ - nAlloc += (enc==SQLITE_UTF8?1:2); - } - if( nByte>iLimit ){ - return SQLITE_TOOBIG; - } - if( sqlite3VdbeMemGrow(pMem, nAlloc, 0) ){ - return SQLITE_NOMEM; - } - memcpy(pMem->z, z, nAlloc); - }else if( xDel==SQLITE_DYNAMIC ){ - sqlite3VdbeMemRelease(pMem); - pMem->zMalloc = pMem->z = (char *)z; - pMem->xDel = 0; - }else{ - sqlite3VdbeMemRelease(pMem); - pMem->z = (char *)z; - pMem->xDel = xDel; - flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); - } - - pMem->n = nByte; - pMem->flags = flags; - pMem->enc = (enc==0 ? SQLITE_UTF8 : enc); - pMem->type = (enc==0 ? SQLITE_BLOB : SQLITE_TEXT); - -#ifndef SQLITE_OMIT_UTF16 - if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){ - return SQLITE_NOMEM; - } -#endif - - if( nByte>iLimit ){ - return SQLITE_TOOBIG; - } - - return SQLITE_OK; -} - -/* -** Compare the values contained by the two memory cells, returning -** negative, zero or positive if pMem1 is less than, equal to, or greater -** than pMem2. Sorting order is NULL's first, followed by numbers (integers -** and reals) sorted numerically, followed by text ordered by the collating -** sequence pColl and finally blob's ordered by memcmp(). -** -** Two NULL values are considered equal by this function. -*/ -SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){ - int rc; - int f1, f2; - int combined_flags; - - f1 = pMem1->flags; - f2 = pMem2->flags; - combined_flags = f1|f2; - assert( (combined_flags & MEM_RowSet)==0 ); - - /* If one value is NULL, it is less than the other. If both values - ** are NULL, return 0. - */ - if( combined_flags&MEM_Null ){ - return (f2&MEM_Null) - (f1&MEM_Null); - } - - /* If one value is a number and the other is not, the number is less. - ** If both are numbers, compare as reals if one is a real, or as integers - ** if both values are integers. - */ - if( combined_flags&(MEM_Int|MEM_Real) ){ - if( !(f1&(MEM_Int|MEM_Real)) ){ - return 1; - } - if( !(f2&(MEM_Int|MEM_Real)) ){ - return -1; - } - if( (f1 & f2 & MEM_Int)==0 ){ - double r1, r2; - if( (f1&MEM_Real)==0 ){ - r1 = (double)pMem1->u.i; - }else{ - r1 = pMem1->r; - } - if( (f2&MEM_Real)==0 ){ - r2 = (double)pMem2->u.i; - }else{ - r2 = pMem2->r; - } - if( r1r2 ) return 1; - return 0; - }else{ - assert( f1&MEM_Int ); - assert( f2&MEM_Int ); - if( pMem1->u.i < pMem2->u.i ) return -1; - if( pMem1->u.i > pMem2->u.i ) return 1; - return 0; - } - } - - /* If one value is a string and the other is a blob, the string is less. - ** If both are strings, compare using the collating functions. - */ - if( combined_flags&MEM_Str ){ - if( (f1 & MEM_Str)==0 ){ - return 1; - } - if( (f2 & MEM_Str)==0 ){ - return -1; - } - - assert( pMem1->enc==pMem2->enc ); - assert( pMem1->enc==SQLITE_UTF8 || - pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE ); - - /* The collation sequence must be defined at this point, even if - ** the user deletes the collation sequence after the vdbe program is - ** compiled (this was not always the case). - */ - assert( !pColl || pColl->xCmp ); - - if( pColl ){ - if( pMem1->enc==pColl->enc ){ - /* The strings are already in the correct encoding. Call the - ** comparison function directly */ - return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); - }else{ - const void *v1, *v2; - int n1, n2; - Mem c1; - Mem c2; - memset(&c1, 0, sizeof(c1)); - memset(&c2, 0, sizeof(c2)); - sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem); - sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem); - v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc); - n1 = v1==0 ? 0 : c1.n; - v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc); - n2 = v2==0 ? 0 : c2.n; - rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2); - sqlite3VdbeMemRelease(&c1); - sqlite3VdbeMemRelease(&c2); - return rc; - } - } - /* If a NULL pointer was passed as the collate function, fall through - ** to the blob case and use memcmp(). */ - } - - /* Both values must be blobs. Compare using memcmp(). */ - rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n); - if( rc==0 ){ - rc = pMem1->n - pMem2->n; - } - return rc; -} - -/* -** Move data out of a btree key or data field and into a Mem structure. -** The data or key is taken from the entry that pCur is currently pointing -** to. offset and amt determine what portion of the data or key to retrieve. -** key is true to get the key or false to get data. The result is written -** into the pMem element. -** -** The pMem structure is assumed to be uninitialized. Any prior content -** is overwritten without being freed. -** -** If this routine fails for any reason (malloc returns NULL or unable -** to read from the disk) then the pMem is left in an inconsistent state. -*/ -SQLITE_PRIVATE int sqlite3VdbeMemFromBtree( - BtCursor *pCur, /* Cursor pointing at record to retrieve. */ - int offset, /* Offset from the start of data to return bytes from. */ - int amt, /* Number of bytes to return. */ - int key, /* If true, retrieve from the btree key, not data. */ - Mem *pMem /* OUT: Return data in this Mem structure. */ -){ - char *zData; /* Data from the btree layer */ - int available = 0; /* Number of bytes available on the local btree page */ - int rc = SQLITE_OK; /* Return code */ - - assert( sqlite3BtreeCursorIsValid(pCur) ); - - /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert() - ** that both the BtShared and database handle mutexes are held. */ - assert( (pMem->flags & MEM_RowSet)==0 ); - if( key ){ - zData = (char *)sqlite3BtreeKeyFetch(pCur, &available); - }else{ - zData = (char *)sqlite3BtreeDataFetch(pCur, &available); - } - assert( zData!=0 ); - - if( offset+amt<=available && (pMem->flags&MEM_Dyn)==0 ){ - sqlite3VdbeMemRelease(pMem); - pMem->z = &zData[offset]; - pMem->flags = MEM_Blob|MEM_Ephem; - }else if( SQLITE_OK==(rc = sqlite3VdbeMemGrow(pMem, amt+2, 0)) ){ - pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term; - pMem->enc = 0; - pMem->type = SQLITE_BLOB; - if( key ){ - rc = sqlite3BtreeKey(pCur, offset, amt, pMem->z); - }else{ - rc = sqlite3BtreeData(pCur, offset, amt, pMem->z); - } - pMem->z[amt] = 0; - pMem->z[amt+1] = 0; - if( rc!=SQLITE_OK ){ - sqlite3VdbeMemRelease(pMem); - } - } - pMem->n = amt; - - return rc; -} - -/* This function is only available internally, it is not part of the -** external API. It works in a similar way to sqlite3_value_text(), -** except the data returned is in the encoding specified by the second -** parameter, which must be one of SQLITE_UTF16BE, SQLITE_UTF16LE or -** SQLITE_UTF8. -** -** (2006-02-16:) The enc value can be or-ed with SQLITE_UTF16_ALIGNED. -** If that is the case, then the result must be aligned on an even byte -** boundary. -*/ -SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){ - if( !pVal ) return 0; - - assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) ); - assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) ); - assert( (pVal->flags & MEM_RowSet)==0 ); - - if( pVal->flags&MEM_Null ){ - return 0; - } - assert( (MEM_Blob>>3) == MEM_Str ); - pVal->flags |= (pVal->flags & MEM_Blob)>>3; - ExpandBlob(pVal); - if( pVal->flags&MEM_Str ){ - sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); - if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){ - assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); - if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){ - return 0; - } - } - sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */ - }else{ - assert( (pVal->flags&MEM_Blob)==0 ); - sqlite3VdbeMemStringify(pVal, enc); - assert( 0==(1&SQLITE_PTR_TO_INT(pVal->z)) ); - } - assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0 - || pVal->db->mallocFailed ); - if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){ - return pVal->z; - }else{ - return 0; - } -} - -/* -** Create a new sqlite3_value object. -*/ -SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *db){ - Mem *p = sqlite3DbMallocZero(db, sizeof(*p)); - if( p ){ - p->flags = MEM_Null; - p->type = SQLITE_NULL; - p->db = db; - } - return p; -} - -/* -** Create a new sqlite3_value object, containing the value of pExpr. -** -** This only works for very simple expressions that consist of one constant -** token (i.e. "5", "5.1", "'a string'"). If the expression can -** be converted directly into a value, then the value is allocated and -** a pointer written to *ppVal. The caller is responsible for deallocating -** the value by passing it to sqlite3ValueFree() later on. If the expression -** cannot be converted to a value, then *ppVal is set to NULL. -*/ -SQLITE_PRIVATE int sqlite3ValueFromExpr( - sqlite3 *db, /* The database connection */ - Expr *pExpr, /* The expression to evaluate */ - u8 enc, /* Encoding to use */ - u8 affinity, /* Affinity to use */ - sqlite3_value **ppVal /* Write the new value here */ -){ - int op; - char *zVal = 0; - sqlite3_value *pVal = 0; - int negInt = 1; - const char *zNeg = ""; - - if( !pExpr ){ - *ppVal = 0; - return SQLITE_OK; - } - op = pExpr->op; - - /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3. - ** The ifdef here is to enable us to achieve 100% branch test coverage even - ** when SQLITE_ENABLE_STAT3 is omitted. - */ -#ifdef SQLITE_ENABLE_STAT3 - if( op==TK_REGISTER ) op = pExpr->op2; -#else - if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; -#endif - - /* Handle negative integers in a single step. This is needed in the - ** case when the value is -9223372036854775808. - */ - if( op==TK_UMINUS - && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ - pExpr = pExpr->pLeft; - op = pExpr->op; - negInt = -1; - zNeg = "-"; - } - - if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ - pVal = sqlite3ValueNew(db); - if( pVal==0 ) goto no_mem; - if( ExprHasProperty(pExpr, EP_IntValue) ){ - sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); - }else{ - zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); - if( zVal==0 ) goto no_mem; - sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); - if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT; - } - if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){ - sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); - }else{ - sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); - } - if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str; - if( enc!=SQLITE_UTF8 ){ - sqlite3VdbeChangeEncoding(pVal, enc); - } - }else if( op==TK_UMINUS ) { - /* This branch happens for multiple negative signs. Ex: -(-5) */ - if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ - sqlite3VdbeMemNumerify(pVal); - if( pVal->u.i==SMALLEST_INT64 ){ - pVal->flags &= MEM_Int; - pVal->flags |= MEM_Real; - pVal->r = (double)LARGEST_INT64; - }else{ - pVal->u.i = -pVal->u.i; - } - pVal->r = -pVal->r; - sqlite3ValueApplyAffinity(pVal, affinity, enc); - } - }else if( op==TK_NULL ){ - pVal = sqlite3ValueNew(db); - if( pVal==0 ) goto no_mem; - } -#ifndef SQLITE_OMIT_BLOB_LITERAL - else if( op==TK_BLOB ){ - int nVal; - assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); - assert( pExpr->u.zToken[1]=='\'' ); - pVal = sqlite3ValueNew(db); - if( !pVal ) goto no_mem; - zVal = &pExpr->u.zToken[2]; - nVal = sqlite3Strlen30(zVal)-1; - assert( zVal[nVal]=='\'' ); - sqlite3VdbeMemSetStr(pVal, sqlite3HexToBlob(db, zVal, nVal), nVal/2, - 0, SQLITE_DYNAMIC); - } -#endif - - if( pVal ){ - sqlite3VdbeMemStoreType(pVal); - } - *ppVal = pVal; - return SQLITE_OK; - -no_mem: - db->mallocFailed = 1; - sqlite3DbFree(db, zVal); - sqlite3ValueFree(pVal); - *ppVal = 0; - return SQLITE_NOMEM; -} - -/* -** Change the string value of an sqlite3_value object -*/ -SQLITE_PRIVATE void sqlite3ValueSetStr( - sqlite3_value *v, /* Value to be set */ - int n, /* Length of string z */ - const void *z, /* Text of the new string */ - u8 enc, /* Encoding to use */ - void (*xDel)(void*) /* Destructor for the string */ -){ - if( v ) sqlite3VdbeMemSetStr((Mem *)v, z, n, enc, xDel); -} - -/* -** Free an sqlite3_value object -*/ -SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value *v){ - if( !v ) return; - sqlite3VdbeMemRelease((Mem *)v); - sqlite3DbFree(((Mem*)v)->db, v); -} - -/* -** Return the number of bytes in the sqlite3_value object assuming -** that it uses the encoding "enc" -*/ -SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){ - Mem *p = (Mem*)pVal; - if( (p->flags & MEM_Blob)!=0 || sqlite3ValueText(pVal, enc) ){ - if( p->flags & MEM_Zero ){ - return p->n + p->u.nZero; - }else{ - return p->n; - } - } - return 0; -} - -/************** End of vdbemem.c *********************************************/ -/************** Begin file vdbeaux.c *****************************************/ -/* -** 2003 September 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used for creating, destroying, and populating -** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior -** to version 2.8.7, all this code was combined into the vdbe.c source file. -** But that file was getting too big so this subroutines were split out. -*/ - -/* -** Create a new virtual database engine. -*/ -SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){ - Vdbe *p; - p = sqlite3DbMallocZero(db, sizeof(Vdbe) ); - if( p==0 ) return 0; - p->db = db; - if( db->pVdbe ){ - db->pVdbe->pPrev = p; - } - p->pNext = db->pVdbe; - p->pPrev = 0; - db->pVdbe = p; - p->magic = VDBE_MAGIC_INIT; - return p; -} - -/* -** Remember the SQL string for a prepared statement. -*/ -SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){ - assert( isPrepareV2==1 || isPrepareV2==0 ); - if( p==0 ) return; -#if defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_ENABLE_SQLLOG) - if( !isPrepareV2 ) return; -#endif - assert( p->zSql==0 ); - p->zSql = sqlite3DbStrNDup(p->db, z, n); - p->isPrepareV2 = (u8)isPrepareV2; -} - -/* -** Return the SQL associated with a prepared statement -*/ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe *)pStmt; - return (p && p->isPrepareV2) ? p->zSql : 0; -} - -/* -** Swap all content between two VDBE structures. -*/ -SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ - Vdbe tmp, *pTmp; - char *zTmp; - tmp = *pA; - *pA = *pB; - *pB = tmp; - pTmp = pA->pNext; - pA->pNext = pB->pNext; - pB->pNext = pTmp; - pTmp = pA->pPrev; - pA->pPrev = pB->pPrev; - pB->pPrev = pTmp; - zTmp = pA->zSql; - pA->zSql = pB->zSql; - pB->zSql = zTmp; - pB->isPrepareV2 = pA->isPrepareV2; -} - -#ifdef SQLITE_DEBUG -/* -** Turn tracing on or off -*/ -SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe *p, FILE *trace){ - p->trace = trace; -} -#endif - -/* -** Resize the Vdbe.aOp array so that it is at least one op larger than -** it was. -** -** If an out-of-memory error occurs while resizing the array, return -** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain -** unchanged (this is so that any opcodes already allocated can be -** correctly deallocated along with the rest of the Vdbe). -*/ -static int growOpArray(Vdbe *p){ - VdbeOp *pNew; - int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op))); - pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op)); - if( pNew ){ - p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op); - p->aOp = pNew; - } - return (pNew ? SQLITE_OK : SQLITE_NOMEM); -} - -/* -** Add a new instruction to the list of instructions current in the -** VDBE. Return the address of the new instruction. -** -** Parameters: -** -** p Pointer to the VDBE -** -** op The opcode for this instruction -** -** p1, p2, p3 Operands -** -** Use the sqlite3VdbeResolveLabel() function to fix an address and -** the sqlite3VdbeChangeP4() function to change the value of the P4 -** operand. -*/ -SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ - int i; - VdbeOp *pOp; - - i = p->nOp; - assert( p->magic==VDBE_MAGIC_INIT ); - assert( op>0 && op<0xff ); - if( p->nOpAlloc<=i ){ - if( growOpArray(p) ){ - return 1; - } - } - p->nOp++; - pOp = &p->aOp[i]; - pOp->opcode = (u8)op; - pOp->p5 = 0; - pOp->p1 = p1; - pOp->p2 = p2; - pOp->p3 = p3; - pOp->p4.p = 0; - pOp->p4type = P4_NOTUSED; -#ifdef SQLITE_DEBUG - pOp->zComment = 0; - if( p->db->flags & SQLITE_VdbeAddopTrace ){ - sqlite3VdbePrintOp(0, i, &p->aOp[i]); - } -#endif -#ifdef VDBE_PROFILE - pOp->cycles = 0; - pOp->cnt = 0; -#endif - return i; -} -SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){ - return sqlite3VdbeAddOp3(p, op, 0, 0, 0); -} -SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){ - return sqlite3VdbeAddOp3(p, op, p1, 0, 0); -} -SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){ - return sqlite3VdbeAddOp3(p, op, p1, p2, 0); -} - - -/* -** Add an opcode that includes the p4 value as a pointer. -*/ -SQLITE_PRIVATE int sqlite3VdbeAddOp4( - Vdbe *p, /* Add the opcode to this VM */ - int op, /* The new opcode */ - int p1, /* The P1 operand */ - int p2, /* The P2 operand */ - int p3, /* The P3 operand */ - const char *zP4, /* The P4 operand */ - int p4type /* P4 operand type */ -){ - int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); - sqlite3VdbeChangeP4(p, addr, zP4, p4type); - return addr; -} - -/* -** Add an OP_ParseSchema opcode. This routine is broken out from -** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees -** as having been used. -** -** The zWhere string must have been obtained from sqlite3_malloc(). -** This routine will take ownership of the allocated memory. -*/ -SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ - int j; - int addr = sqlite3VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0); - sqlite3VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC); - for(j=0; jdb->nDb; j++) sqlite3VdbeUsesBtree(p, j); -} - -/* -** Add an opcode that includes the p4 value as an integer. -*/ -SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( - Vdbe *p, /* Add the opcode to this VM */ - int op, /* The new opcode */ - int p1, /* The P1 operand */ - int p2, /* The P2 operand */ - int p3, /* The P3 operand */ - int p4 /* The P4 operand as an integer */ -){ - int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); - sqlite3VdbeChangeP4(p, addr, SQLITE_INT_TO_PTR(p4), P4_INT32); - return addr; -} - -/* -** Create a new symbolic label for an instruction that has yet to be -** coded. The symbolic label is really just a negative number. The -** label can be used as the P2 value of an operation. Later, when -** the label is resolved to a specific address, the VDBE will scan -** through its operation list and change all values of P2 which match -** the label into the resolved address. -** -** The VDBE knows that a P2 value is a label because labels are -** always negative and P2 values are suppose to be non-negative. -** Hence, a negative P2 value is a label that has yet to be resolved. -** -** Zero is returned if a malloc() fails. -*/ -SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){ - int i = p->nLabel++; - assert( p->magic==VDBE_MAGIC_INIT ); - if( (i & (i-1))==0 ){ - p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel, - (i*2+1)*sizeof(p->aLabel[0])); - } - if( p->aLabel ){ - p->aLabel[i] = -1; - } - return -1-i; -} - -/* -** Resolve label "x" to be the address of the next instruction to -** be inserted. The parameter "x" must have been obtained from -** a prior call to sqlite3VdbeMakeLabel(). -*/ -SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *p, int x){ - int j = -1-x; - assert( p->magic==VDBE_MAGIC_INIT ); - assert( j>=0 && jnLabel ); - if( p->aLabel ){ - p->aLabel[j] = p->nOp; - } -} - -/* -** Mark the VDBE as one that can only be run one time. -*/ -SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe *p){ - p->runOnlyOnce = 1; -} - -#ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */ - -/* -** The following type and function are used to iterate through all opcodes -** in a Vdbe main program and each of the sub-programs (triggers) it may -** invoke directly or indirectly. It should be used as follows: -** -** Op *pOp; -** VdbeOpIter sIter; -** -** memset(&sIter, 0, sizeof(sIter)); -** sIter.v = v; // v is of type Vdbe* -** while( (pOp = opIterNext(&sIter)) ){ -** // Do something with pOp -** } -** sqlite3DbFree(v->db, sIter.apSub); -** -*/ -typedef struct VdbeOpIter VdbeOpIter; -struct VdbeOpIter { - Vdbe *v; /* Vdbe to iterate through the opcodes of */ - SubProgram **apSub; /* Array of subprograms */ - int nSub; /* Number of entries in apSub */ - int iAddr; /* Address of next instruction to return */ - int iSub; /* 0 = main program, 1 = first sub-program etc. */ -}; -static Op *opIterNext(VdbeOpIter *p){ - Vdbe *v = p->v; - Op *pRet = 0; - Op *aOp; - int nOp; - - if( p->iSub<=p->nSub ){ - - if( p->iSub==0 ){ - aOp = v->aOp; - nOp = v->nOp; - }else{ - aOp = p->apSub[p->iSub-1]->aOp; - nOp = p->apSub[p->iSub-1]->nOp; - } - assert( p->iAddriAddr]; - p->iAddr++; - if( p->iAddr==nOp ){ - p->iSub++; - p->iAddr = 0; - } - - if( pRet->p4type==P4_SUBPROGRAM ){ - int nByte = (p->nSub+1)*sizeof(SubProgram*); - int j; - for(j=0; jnSub; j++){ - if( p->apSub[j]==pRet->p4.pProgram ) break; - } - if( j==p->nSub ){ - p->apSub = sqlite3DbReallocOrFree(v->db, p->apSub, nByte); - if( !p->apSub ){ - pRet = 0; - }else{ - p->apSub[p->nSub++] = pRet->p4.pProgram; - } - } - } - } - - return pRet; -} - -/* -** Check if the program stored in the VM associated with pParse may -** throw an ABORT exception (causing the statement, but not entire transaction -** to be rolled back). This condition is true if the main program or any -** sub-programs contains any of the following: -** -** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. -** * OP_HaltIfNull with P1=SQLITE_CONSTRAINT and P2=OE_Abort. -** * OP_Destroy -** * OP_VUpdate -** * OP_VRename -** * OP_FkCounter with P2==0 (immediate foreign key constraint) -** -** Then check that the value of Parse.mayAbort is true if an -** ABORT may be thrown, or false otherwise. Return true if it does -** match, or false otherwise. This function is intended to be used as -** part of an assert statement in the compiler. Similar to: -** -** assert( sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) ); -*/ -SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ - int hasAbort = 0; - Op *pOp; - VdbeOpIter sIter; - memset(&sIter, 0, sizeof(sIter)); - sIter.v = v; - - while( (pOp = opIterNext(&sIter))!=0 ){ - int opcode = pOp->opcode; - if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename -#ifndef SQLITE_OMIT_FOREIGN_KEY - || (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1) -#endif - || ((opcode==OP_Halt || opcode==OP_HaltIfNull) - && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) - ){ - hasAbort = 1; - break; - } - } - sqlite3DbFree(v->db, sIter.apSub); - - /* Return true if hasAbort==mayAbort. Or if a malloc failure occurred. - ** If malloc failed, then the while() loop above may not have iterated - ** through all opcodes and hasAbort may be set incorrectly. Return - ** true for this case to prevent the assert() in the callers frame - ** from failing. */ - return ( v->db->mallocFailed || hasAbort==mayAbort ); -} -#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */ - -/* -** Loop through the program looking for P2 values that are negative -** on jump instructions. Each such value is a label. Resolve the -** label by setting the P2 value to its correct non-zero value. -** -** This routine is called once after all opcodes have been inserted. -** -** Variable *pMaxFuncArgs is set to the maximum value of any P2 argument -** to an OP_Function, OP_AggStep or OP_VFilter opcode. This is used by -** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array. -** -** The Op.opflags field is set on all opcodes. -*/ -static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ - int i; - int nMaxArgs = *pMaxFuncArgs; - Op *pOp; - int *aLabel = p->aLabel; - p->readOnly = 1; - for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){ - u8 opcode = pOp->opcode; - - pOp->opflags = sqlite3OpcodeProperty[opcode]; - if( opcode==OP_Function || opcode==OP_AggStep ){ - if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5; - }else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){ - p->readOnly = 0; -#ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( opcode==OP_VUpdate ){ - if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; - }else if( opcode==OP_VFilter ){ - int n; - assert( p->nOp - i >= 3 ); - assert( pOp[-1].opcode==OP_Integer ); - n = pOp[-1].p1; - if( n>nMaxArgs ) nMaxArgs = n; -#endif - }else if( opcode==OP_Next || opcode==OP_SorterNext ){ - pOp->p4.xAdvance = sqlite3BtreeNext; - pOp->p4type = P4_ADVANCE; - }else if( opcode==OP_Prev ){ - pOp->p4.xAdvance = sqlite3BtreePrevious; - pOp->p4type = P4_ADVANCE; - } - - if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){ - assert( -1-pOp->p2nLabel ); - pOp->p2 = aLabel[-1-pOp->p2]; - } - } - sqlite3DbFree(p->db, p->aLabel); - p->aLabel = 0; - - *pMaxFuncArgs = nMaxArgs; -} - -/* -** Return the address of the next instruction to be inserted. -*/ -SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){ - assert( p->magic==VDBE_MAGIC_INIT ); - return p->nOp; -} - -/* -** This function returns a pointer to the array of opcodes associated with -** the Vdbe passed as the first argument. It is the callers responsibility -** to arrange for the returned array to be eventually freed using the -** vdbeFreeOpArray() function. -** -** Before returning, *pnOp is set to the number of entries in the returned -** array. Also, *pnMaxArg is set to the larger of its current value and -** the number of entries in the Vdbe.apArg[] array required to execute the -** returned program. -*/ -SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ - VdbeOp *aOp = p->aOp; - assert( aOp && !p->db->mallocFailed ); - - /* Check that sqlite3VdbeUsesBtree() was not called on this VM */ - assert( p->btreeMask==0 ); - - resolveP2Values(p, pnMaxArg); - *pnOp = p->nOp; - p->aOp = 0; - return aOp; -} - -/* -** Add a whole list of operations to the operation stack. Return the -** address of the first operation added. -*/ -SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){ - int addr; - assert( p->magic==VDBE_MAGIC_INIT ); - if( p->nOp + nOp > p->nOpAlloc && growOpArray(p) ){ - return 0; - } - addr = p->nOp; - if( ALWAYS(nOp>0) ){ - int i; - VdbeOpList const *pIn = aOp; - for(i=0; ip2; - VdbeOp *pOut = &p->aOp[i+addr]; - pOut->opcode = pIn->opcode; - pOut->p1 = pIn->p1; - if( p2<0 && (sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP)!=0 ){ - pOut->p2 = addr + ADDR(p2); - }else{ - pOut->p2 = p2; - } - pOut->p3 = pIn->p3; - pOut->p4type = P4_NOTUSED; - pOut->p4.p = 0; - pOut->p5 = 0; -#ifdef SQLITE_DEBUG - pOut->zComment = 0; - if( p->db->flags & SQLITE_VdbeAddopTrace ){ - sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]); - } -#endif - } - p->nOp += nOp; - } - return addr; -} - -/* -** Change the value of the P1 operand for a specific instruction. -** This routine is useful when a large program is loaded from a -** static array using sqlite3VdbeAddOpList but we want to make a -** few minor changes to the program. -*/ -SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){ - assert( p!=0 ); - if( ((u32)p->nOp)>addr ){ - p->aOp[addr].p1 = val; - } -} - -/* -** Change the value of the P2 operand for a specific instruction. -** This routine is useful for setting a jump destination. -*/ -SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){ - assert( p!=0 ); - if( ((u32)p->nOp)>addr ){ - p->aOp[addr].p2 = val; - } -} - -/* -** Change the value of the P3 operand for a specific instruction. -*/ -SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){ - assert( p!=0 ); - if( ((u32)p->nOp)>addr ){ - p->aOp[addr].p3 = val; - } -} - -/* -** Change the value of the P5 operand for the most recently -** added operation. -*/ -SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 val){ - assert( p!=0 ); - if( p->aOp ){ - assert( p->nOp>0 ); - p->aOp[p->nOp-1].p5 = val; - } -} - -/* -** Change the P2 operand of instruction addr so that it points to -** the address of the next instruction to be coded. -*/ -SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){ - assert( addr>=0 || p->db->mallocFailed ); - if( addr>=0 ) sqlite3VdbeChangeP2(p, addr, p->nOp); -} - - -/* -** If the input FuncDef structure is ephemeral, then free it. If -** the FuncDef is not ephermal, then do nothing. -*/ -static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ - if( ALWAYS(pDef) && (pDef->flags & SQLITE_FUNC_EPHEM)!=0 ){ - sqlite3DbFree(db, pDef); - } -} - -static void vdbeFreeOpArray(sqlite3 *, Op *, int); - -/* -** Delete a P4 value if necessary. -*/ -static void freeP4(sqlite3 *db, int p4type, void *p4){ - if( p4 ){ - assert( db ); - switch( p4type ){ - case P4_REAL: - case P4_INT64: - case P4_DYNAMIC: - case P4_KEYINFO: - case P4_INTARRAY: - case P4_KEYINFO_HANDOFF: { - sqlite3DbFree(db, p4); - break; - } - case P4_MPRINTF: { - if( db->pnBytesFreed==0 ) sqlite3_free(p4); - break; - } - case P4_VDBEFUNC: { - VdbeFunc *pVdbeFunc = (VdbeFunc *)p4; - freeEphemeralFunction(db, pVdbeFunc->pFunc); - if( db->pnBytesFreed==0 ) sqlite3VdbeDeleteAuxData(pVdbeFunc, 0); - sqlite3DbFree(db, pVdbeFunc); - break; - } - case P4_FUNCDEF: { - freeEphemeralFunction(db, (FuncDef*)p4); - break; - } - case P4_MEM: { - if( db->pnBytesFreed==0 ){ - sqlite3ValueFree((sqlite3_value*)p4); - }else{ - Mem *p = (Mem*)p4; - sqlite3DbFree(db, p->zMalloc); - sqlite3DbFree(db, p); - } - break; - } - case P4_VTAB : { - if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4); - break; - } - } - } -} - -/* -** Free the space allocated for aOp and any p4 values allocated for the -** opcodes contained within. If aOp is not NULL it is assumed to contain -** nOp entries. -*/ -static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ - if( aOp ){ - Op *pOp; - for(pOp=aOp; pOp<&aOp[nOp]; pOp++){ - freeP4(db, pOp->p4type, pOp->p4.p); -#ifdef SQLITE_DEBUG - sqlite3DbFree(db, pOp->zComment); -#endif - } - } - sqlite3DbFree(db, aOp); -} - -/* -** Link the SubProgram object passed as the second argument into the linked -** list at Vdbe.pSubProgram. This list is used to delete all sub-program -** objects when the VM is no longer required. -*/ -SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){ - p->pNext = pVdbe->pProgram; - pVdbe->pProgram = p; -} - -/* -** Change the opcode at addr into OP_Noop -*/ -SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){ - if( p->aOp ){ - VdbeOp *pOp = &p->aOp[addr]; - sqlite3 *db = p->db; - freeP4(db, pOp->p4type, pOp->p4.p); - memset(pOp, 0, sizeof(pOp[0])); - pOp->opcode = OP_Noop; - } -} - -/* -** Change the value of the P4 operand for a specific instruction. -** This routine is useful when a large program is loaded from a -** static array using sqlite3VdbeAddOpList but we want to make a -** few minor changes to the program. -** -** If n>=0 then the P4 operand is dynamic, meaning that a copy of -** the string is made into memory obtained from sqlite3_malloc(). -** A value of n==0 means copy bytes of zP4 up to and including the -** first null byte. If n>0 then copy n+1 bytes of zP4. -** -** If n==P4_KEYINFO it means that zP4 is a pointer to a KeyInfo structure. -** A copy is made of the KeyInfo structure into memory obtained from -** sqlite3_malloc, to be freed when the Vdbe is finalized. -** n==P4_KEYINFO_HANDOFF indicates that zP4 points to a KeyInfo structure -** stored in memory that the caller has obtained from sqlite3_malloc. The -** caller should not free the allocation, it will be freed when the Vdbe is -** finalized. -** -** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points -** to a string or structure that is guaranteed to exist for the lifetime of -** the Vdbe. In these cases we can just copy the pointer. -** -** If addr<0 then change P4 on the most recently inserted instruction. -*/ -SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){ - Op *pOp; - sqlite3 *db; - assert( p!=0 ); - db = p->db; - assert( p->magic==VDBE_MAGIC_INIT ); - if( p->aOp==0 || db->mallocFailed ){ - if ( n!=P4_KEYINFO && n!=P4_VTAB ) { - freeP4(db, n, (void*)*(char**)&zP4); - } - return; - } - assert( p->nOp>0 ); - assert( addrnOp ); - if( addr<0 ){ - addr = p->nOp - 1; - } - pOp = &p->aOp[addr]; - assert( pOp->p4type==P4_NOTUSED || pOp->p4type==P4_INT32 ); - freeP4(db, pOp->p4type, pOp->p4.p); - pOp->p4.p = 0; - if( n==P4_INT32 ){ - /* Note: this cast is safe, because the origin data point was an int - ** that was cast to a (const char *). */ - pOp->p4.i = SQLITE_PTR_TO_INT(zP4); - pOp->p4type = P4_INT32; - }else if( zP4==0 ){ - pOp->p4.p = 0; - pOp->p4type = P4_NOTUSED; - }else if( n==P4_KEYINFO ){ - KeyInfo *pKeyInfo; - int nField, nByte; - - nField = ((KeyInfo*)zP4)->nField; - nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]) + nField; - pKeyInfo = sqlite3DbMallocRaw(0, nByte); - pOp->p4.pKeyInfo = pKeyInfo; - if( pKeyInfo ){ - u8 *aSortOrder; - memcpy((char*)pKeyInfo, zP4, nByte - nField); - aSortOrder = pKeyInfo->aSortOrder; - assert( aSortOrder!=0 ); - pKeyInfo->aSortOrder = (unsigned char*)&pKeyInfo->aColl[nField]; - memcpy(pKeyInfo->aSortOrder, aSortOrder, nField); - pOp->p4type = P4_KEYINFO; - }else{ - p->db->mallocFailed = 1; - pOp->p4type = P4_NOTUSED; - } - }else if( n==P4_KEYINFO_HANDOFF ){ - pOp->p4.p = (void*)zP4; - pOp->p4type = P4_KEYINFO; - }else if( n==P4_VTAB ){ - pOp->p4.p = (void*)zP4; - pOp->p4type = P4_VTAB; - sqlite3VtabLock((VTable *)zP4); - assert( ((VTable *)zP4)->db==p->db ); - }else if( n<0 ){ - pOp->p4.p = (void*)zP4; - pOp->p4type = (signed char)n; - }else{ - if( n==0 ) n = sqlite3Strlen30(zP4); - pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n); - pOp->p4type = P4_DYNAMIC; - } -} - -#ifndef NDEBUG -/* -** Change the comment on the most recently coded instruction. Or -** insert a No-op and add the comment to that new instruction. This -** makes the code easier to read during debugging. None of this happens -** in a production build. -*/ -static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){ - assert( p->nOp>0 || p->aOp==0 ); - assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed ); - if( p->nOp ){ - assert( p->aOp ); - sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment); - p->aOp[p->nOp-1].zComment = sqlite3VMPrintf(p->db, zFormat, ap); - } -} -SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){ - va_list ap; - if( p ){ - va_start(ap, zFormat); - vdbeVComment(p, zFormat, ap); - va_end(ap); - } -} -SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ - va_list ap; - if( p ){ - sqlite3VdbeAddOp0(p, OP_Noop); - va_start(ap, zFormat); - vdbeVComment(p, zFormat, ap); - va_end(ap); - } -} -#endif /* NDEBUG */ - -/* -** Return the opcode for a given address. If the address is -1, then -** return the most recently inserted opcode. -** -** If a memory allocation error has occurred prior to the calling of this -** routine, then a pointer to a dummy VdbeOp will be returned. That opcode -** is readable but not writable, though it is cast to a writable value. -** The return of a dummy opcode allows the call to continue functioning -** after a OOM fault without having to check to see if the return from -** this routine is a valid pointer. But because the dummy.opcode is 0, -** dummy will never be written to. This is verified by code inspection and -** by running with Valgrind. -** -** About the #ifdef SQLITE_OMIT_TRACE: Normally, this routine is never called -** unless p->nOp>0. This is because in the absense of SQLITE_OMIT_TRACE, -** an OP_Trace instruction is always inserted by sqlite3VdbeGet() as soon as -** a new VDBE is created. So we are free to set addr to p->nOp-1 without -** having to double-check to make sure that the result is non-negative. But -** if SQLITE_OMIT_TRACE is defined, the OP_Trace is omitted and we do need to -** check the value of p->nOp-1 before continuing. -*/ -SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ - /* C89 specifies that the constant "dummy" will be initialized to all - ** zeros, which is correct. MSVC generates a warning, nevertheless. */ - static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */ - assert( p->magic==VDBE_MAGIC_INIT ); - if( addr<0 ){ -#ifdef SQLITE_OMIT_TRACE - if( p->nOp==0 ) return (VdbeOp*)&dummy; -#endif - addr = p->nOp - 1; - } - assert( (addr>=0 && addrnOp) || p->db->mallocFailed ); - if( p->db->mallocFailed ){ - return (VdbeOp*)&dummy; - }else{ - return &p->aOp[addr]; - } -} - -#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ - || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) -/* -** Compute a string that describes the P4 parameter for an opcode. -** Use zTemp for any required temporary buffer space. -*/ -static char *displayP4(Op *pOp, char *zTemp, int nTemp){ - char *zP4 = zTemp; - assert( nTemp>=20 ); - switch( pOp->p4type ){ - case P4_KEYINFO_STATIC: - case P4_KEYINFO: { - int i, j; - KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; - assert( pKeyInfo->aSortOrder!=0 ); - sqlite3_snprintf(nTemp, zTemp, "keyinfo(%d", pKeyInfo->nField); - i = sqlite3Strlen30(zTemp); - for(j=0; jnField; j++){ - CollSeq *pColl = pKeyInfo->aColl[j]; - const char *zColl = pColl ? pColl->zName : "nil"; - int n = sqlite3Strlen30(zColl); - if( i+n>nTemp-6 ){ - memcpy(&zTemp[i],",...",4); - break; - } - zTemp[i++] = ','; - if( pKeyInfo->aSortOrder[j] ){ - zTemp[i++] = '-'; - } - memcpy(&zTemp[i], zColl, n+1); - i += n; - } - zTemp[i++] = ')'; - zTemp[i] = 0; - assert( ip4.pColl; - sqlite3_snprintf(nTemp, zTemp, "collseq(%.20s)", pColl->zName); - break; - } - case P4_FUNCDEF: { - FuncDef *pDef = pOp->p4.pFunc; - sqlite3_snprintf(nTemp, zTemp, "%s(%d)", pDef->zName, pDef->nArg); - break; - } - case P4_INT64: { - sqlite3_snprintf(nTemp, zTemp, "%lld", *pOp->p4.pI64); - break; - } - case P4_INT32: { - sqlite3_snprintf(nTemp, zTemp, "%d", pOp->p4.i); - break; - } - case P4_REAL: { - sqlite3_snprintf(nTemp, zTemp, "%.16g", *pOp->p4.pReal); - break; - } - case P4_MEM: { - Mem *pMem = pOp->p4.pMem; - if( pMem->flags & MEM_Str ){ - zP4 = pMem->z; - }else if( pMem->flags & MEM_Int ){ - sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i); - }else if( pMem->flags & MEM_Real ){ - sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r); - }else if( pMem->flags & MEM_Null ){ - sqlite3_snprintf(nTemp, zTemp, "NULL"); - }else{ - assert( pMem->flags & MEM_Blob ); - zP4 = "(blob)"; - } - break; - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - case P4_VTAB: { - sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; - sqlite3_snprintf(nTemp, zTemp, "vtab:%p:%p", pVtab, pVtab->pModule); - break; - } -#endif - case P4_INTARRAY: { - sqlite3_snprintf(nTemp, zTemp, "intarray"); - break; - } - case P4_SUBPROGRAM: { - sqlite3_snprintf(nTemp, zTemp, "program"); - break; - } - case P4_ADVANCE: { - zTemp[0] = 0; - break; - } - default: { - zP4 = pOp->p4.z; - if( zP4==0 ){ - zP4 = zTemp; - zTemp[0] = 0; - } - } - } - assert( zP4!=0 ); - return zP4; -} -#endif - -/* -** Declare to the Vdbe that the BTree object at db->aDb[i] is used. -** -** The prepared statements need to know in advance the complete set of -** attached databases that will be use. A mask of these databases -** is maintained in p->btreeMask. The p->lockMask value is the subset of -** p->btreeMask of databases that will require a lock. -*/ -SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){ - assert( i>=0 && idb->nDb && i<(int)sizeof(yDbMask)*8 ); - assert( i<(int)sizeof(p->btreeMask)*8 ); - p->btreeMask |= ((yDbMask)1)<db->aDb[i].pBt) ){ - p->lockMask |= ((yDbMask)1)<0 -/* -** If SQLite is compiled to support shared-cache mode and to be threadsafe, -** this routine obtains the mutex associated with each BtShared structure -** that may be accessed by the VM passed as an argument. In doing so it also -** sets the BtShared.db member of each of the BtShared structures, ensuring -** that the correct busy-handler callback is invoked if required. -** -** If SQLite is not threadsafe but does support shared-cache mode, then -** sqlite3BtreeEnter() is invoked to set the BtShared.db variables -** of all of BtShared structures accessible via the database handle -** associated with the VM. -** -** If SQLite is not threadsafe and does not support shared-cache mode, this -** function is a no-op. -** -** The p->btreeMask field is a bitmask of all btrees that the prepared -** statement p will ever use. Let N be the number of bits in p->btreeMask -** corresponding to btrees that use shared cache. Then the runtime of -** this routine is N*N. But as N is rarely more than 1, this should not -** be a problem. -*/ -SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe *p){ - int i; - yDbMask mask; - sqlite3 *db; - Db *aDb; - int nDb; - if( p->lockMask==0 ) return; /* The common case */ - db = p->db; - aDb = db->aDb; - nDb = db->nDb; - for(i=0, mask=1; ilockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ - sqlite3BtreeEnter(aDb[i].pBt); - } - } -} -#endif - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 -/* -** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter(). -*/ -SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){ - int i; - yDbMask mask; - sqlite3 *db; - Db *aDb; - int nDb; - if( p->lockMask==0 ) return; /* The common case */ - db = p->db; - aDb = db->aDb; - nDb = db->nDb; - for(i=0, mask=1; ilockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ - sqlite3BtreeLeave(aDb[i].pBt); - } - } -} -#endif - -#if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) -/* -** Print a single opcode. This routine is used for debugging only. -*/ -SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){ - char *zP4; - char zPtr[50]; - static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-4s %.2X %s\n"; - if( pOut==0 ) pOut = stdout; - zP4 = displayP4(pOp, zPtr, sizeof(zPtr)); - fprintf(pOut, zFormat1, pc, - sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, -#ifdef SQLITE_DEBUG - pOp->zComment ? pOp->zComment : "" -#else - "" -#endif - ); - fflush(pOut); -} -#endif - -/* -** Release an array of N Mem elements -*/ -static void releaseMemArray(Mem *p, int N){ - if( p && N ){ - Mem *pEnd; - sqlite3 *db = p->db; - u8 malloc_failed = db->mallocFailed; - if( db->pnBytesFreed ){ - for(pEnd=&p[N]; pzMalloc); - } - return; - } - for(pEnd=&p[N]; pflags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){ - sqlite3VdbeMemRelease(p); - }else if( p->zMalloc ){ - sqlite3DbFree(db, p->zMalloc); - p->zMalloc = 0; - } - - p->flags = MEM_Invalid; - } - db->mallocFailed = malloc_failed; - } -} - -/* -** Delete a VdbeFrame object and its contents. VdbeFrame objects are -** allocated by the OP_Program opcode in sqlite3VdbeExec(). -*/ -SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame *p){ - int i; - Mem *aMem = VdbeFrameMem(p); - VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem]; - for(i=0; inChildCsr; i++){ - sqlite3VdbeFreeCursor(p->v, apCsr[i]); - } - releaseMemArray(aMem, p->nChildMem); - sqlite3DbFree(p->v->db, p); -} - -#ifndef SQLITE_OMIT_EXPLAIN -/* -** Give a listing of the program in the virtual machine. -** -** The interface is the same as sqlite3VdbeExec(). But instead of -** running the code, it invokes the callback once for each instruction. -** This feature is used to implement "EXPLAIN". -** -** When p->explain==1, each instruction is listed. When -** p->explain==2, only OP_Explain instructions are listed and these -** are shown in a different format. p->explain==2 is used to implement -** EXPLAIN QUERY PLAN. -** -** When p->explain==1, first the main program is listed, then each of -** the trigger subprograms are listed one by one. -*/ -SQLITE_PRIVATE int sqlite3VdbeList( - Vdbe *p /* The VDBE */ -){ - int nRow; /* Stop when row count reaches this */ - int nSub = 0; /* Number of sub-vdbes seen so far */ - SubProgram **apSub = 0; /* Array of sub-vdbes */ - Mem *pSub = 0; /* Memory cell hold array of subprogs */ - sqlite3 *db = p->db; /* The database connection */ - int i; /* Loop counter */ - int rc = SQLITE_OK; /* Return code */ - Mem *pMem = &p->aMem[1]; /* First Mem of result set */ - - assert( p->explain ); - assert( p->magic==VDBE_MAGIC_RUN ); - assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); - - /* Even though this opcode does not use dynamic strings for - ** the result, result columns may become dynamic if the user calls - ** sqlite3_column_text16(), causing a translation to UTF-16 encoding. - */ - releaseMemArray(pMem, 8); - p->pResultSet = 0; - - if( p->rc==SQLITE_NOMEM ){ - /* This happens if a malloc() inside a call to sqlite3_column_text() or - ** sqlite3_column_text16() failed. */ - db->mallocFailed = 1; - return SQLITE_ERROR; - } - - /* When the number of output rows reaches nRow, that means the - ** listing has finished and sqlite3_step() should return SQLITE_DONE. - ** nRow is the sum of the number of rows in the main program, plus - ** the sum of the number of rows in all trigger subprograms encountered - ** so far. The nRow value will increase as new trigger subprograms are - ** encountered, but p->pc will eventually catch up to nRow. - */ - nRow = p->nOp; - if( p->explain==1 ){ - /* The first 8 memory cells are used for the result set. So we will - ** commandeer the 9th cell to use as storage for an array of pointers - ** to trigger subprograms. The VDBE is guaranteed to have at least 9 - ** cells. */ - assert( p->nMem>9 ); - pSub = &p->aMem[9]; - if( pSub->flags&MEM_Blob ){ - /* On the first call to sqlite3_step(), pSub will hold a NULL. It is - ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */ - nSub = pSub->n/sizeof(Vdbe*); - apSub = (SubProgram **)pSub->z; - } - for(i=0; inOp; - } - } - - do{ - i = p->pc++; - }while( iexplain==2 && p->aOp[i].opcode!=OP_Explain ); - if( i>=nRow ){ - p->rc = SQLITE_OK; - rc = SQLITE_DONE; - }else if( db->u1.isInterrupted ){ - p->rc = SQLITE_INTERRUPT; - rc = SQLITE_ERROR; - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc)); - }else{ - char *z; - Op *pOp; - if( inOp ){ - /* The output line number is small enough that we are still in the - ** main program. */ - pOp = &p->aOp[i]; - }else{ - /* We are currently listing subprograms. Figure out which one and - ** pick up the appropriate opcode. */ - int j; - i -= p->nOp; - for(j=0; i>=apSub[j]->nOp; j++){ - i -= apSub[j]->nOp; - } - pOp = &apSub[j]->aOp[i]; - } - if( p->explain==1 ){ - pMem->flags = MEM_Int; - pMem->type = SQLITE_INTEGER; - pMem->u.i = i; /* Program counter */ - pMem++; - - pMem->flags = MEM_Static|MEM_Str|MEM_Term; - pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */ - assert( pMem->z!=0 ); - pMem->n = sqlite3Strlen30(pMem->z); - pMem->type = SQLITE_TEXT; - pMem->enc = SQLITE_UTF8; - pMem++; - - /* When an OP_Program opcode is encounter (the only opcode that has - ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms - ** kept in p->aMem[9].z to hold the new program - assuming this subprogram - ** has not already been seen. - */ - if( pOp->p4type==P4_SUBPROGRAM ){ - int nByte = (nSub+1)*sizeof(SubProgram*); - int j; - for(j=0; jp4.pProgram ) break; - } - if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, nSub!=0) ){ - apSub = (SubProgram **)pSub->z; - apSub[nSub++] = pOp->p4.pProgram; - pSub->flags |= MEM_Blob; - pSub->n = nSub*sizeof(SubProgram*); - } - } - } - - pMem->flags = MEM_Int; - pMem->u.i = pOp->p1; /* P1 */ - pMem->type = SQLITE_INTEGER; - pMem++; - - pMem->flags = MEM_Int; - pMem->u.i = pOp->p2; /* P2 */ - pMem->type = SQLITE_INTEGER; - pMem++; - - pMem->flags = MEM_Int; - pMem->u.i = pOp->p3; /* P3 */ - pMem->type = SQLITE_INTEGER; - pMem++; - - if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ - assert( p->db->mallocFailed ); - return SQLITE_ERROR; - } - pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; - z = displayP4(pOp, pMem->z, 32); - if( z!=pMem->z ){ - sqlite3VdbeMemSetStr(pMem, z, -1, SQLITE_UTF8, 0); - }else{ - assert( pMem->z!=0 ); - pMem->n = sqlite3Strlen30(pMem->z); - pMem->enc = SQLITE_UTF8; - } - pMem->type = SQLITE_TEXT; - pMem++; - - if( p->explain==1 ){ - if( sqlite3VdbeMemGrow(pMem, 4, 0) ){ - assert( p->db->mallocFailed ); - return SQLITE_ERROR; - } - pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; - pMem->n = 2; - sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */ - pMem->type = SQLITE_TEXT; - pMem->enc = SQLITE_UTF8; - pMem++; - -#ifdef SQLITE_DEBUG - if( pOp->zComment ){ - pMem->flags = MEM_Str|MEM_Term; - pMem->z = pOp->zComment; - pMem->n = sqlite3Strlen30(pMem->z); - pMem->enc = SQLITE_UTF8; - pMem->type = SQLITE_TEXT; - }else -#endif - { - pMem->flags = MEM_Null; /* Comment */ - pMem->type = SQLITE_NULL; - } - } - - p->nResColumn = 8 - 4*(p->explain-1); - p->pResultSet = &p->aMem[1]; - p->rc = SQLITE_OK; - rc = SQLITE_ROW; - } - return rc; -} -#endif /* SQLITE_OMIT_EXPLAIN */ - -#ifdef SQLITE_DEBUG -/* -** Print the SQL that was used to generate a VDBE program. -*/ -SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe *p){ - int nOp = p->nOp; - VdbeOp *pOp; - if( nOp<1 ) return; - pOp = &p->aOp[0]; - if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){ - const char *z = pOp->p4.z; - while( sqlite3Isspace(*z) ) z++; - printf("SQL: [%s]\n", z); - } -} -#endif - -#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) -/* -** Print an IOTRACE message showing SQL content. -*/ -SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){ - int nOp = p->nOp; - VdbeOp *pOp; - if( sqlite3IoTrace==0 ) return; - if( nOp<1 ) return; - pOp = &p->aOp[0]; - if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){ - int i, j; - char z[1000]; - sqlite3_snprintf(sizeof(z), z, "%s", pOp->p4.z); - for(i=0; sqlite3Isspace(z[i]); i++){} - for(j=0; z[i]; i++){ - if( sqlite3Isspace(z[i]) ){ - if( z[i-1]!=' ' ){ - z[j++] = ' '; - } - }else{ - z[j++] = z[i]; - } - } - z[j] = 0; - sqlite3IoTrace("SQL %s\n", z); - } -} -#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */ - -/* -** Allocate space from a fixed size buffer and return a pointer to -** that space. If insufficient space is available, return NULL. -** -** The pBuf parameter is the initial value of a pointer which will -** receive the new memory. pBuf is normally NULL. If pBuf is not -** NULL, it means that memory space has already been allocated and that -** this routine should not allocate any new memory. When pBuf is not -** NULL simply return pBuf. Only allocate new memory space when pBuf -** is NULL. -** -** nByte is the number of bytes of space needed. -** -** *ppFrom points to available space and pEnd points to the end of the -** available space. When space is allocated, *ppFrom is advanced past -** the end of the allocated space. -** -** *pnByte is a counter of the number of bytes of space that have failed -** to allocate. If there is insufficient space in *ppFrom to satisfy the -** request, then increment *pnByte by the amount of the request. -*/ -static void *allocSpace( - void *pBuf, /* Where return pointer will be stored */ - int nByte, /* Number of bytes to allocate */ - u8 **ppFrom, /* IN/OUT: Allocate from *ppFrom */ - u8 *pEnd, /* Pointer to 1 byte past the end of *ppFrom buffer */ - int *pnByte /* If allocation cannot be made, increment *pnByte */ -){ - assert( EIGHT_BYTE_ALIGNMENT(*ppFrom) ); - if( pBuf ) return pBuf; - nByte = ROUND8(nByte); - if( &(*ppFrom)[nByte] <= pEnd ){ - pBuf = (void*)*ppFrom; - *ppFrom += nByte; - }else{ - *pnByte += nByte; - } - return pBuf; -} - -/* -** Rewind the VDBE back to the beginning in preparation for -** running it. -*/ -SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){ -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) - int i; -#endif - assert( p!=0 ); - assert( p->magic==VDBE_MAGIC_INIT ); - - /* There should be at least one opcode. - */ - assert( p->nOp>0 ); - - /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */ - p->magic = VDBE_MAGIC_RUN; - -#ifdef SQLITE_DEBUG - for(i=1; inMem; i++){ - assert( p->aMem[i].db==p->db ); - } -#endif - p->pc = -1; - p->rc = SQLITE_OK; - p->errorAction = OE_Abort; - p->magic = VDBE_MAGIC_RUN; - p->nChange = 0; - p->cacheCtr = 1; - p->minWriteFileFormat = 255; - p->iStatement = 0; - p->nFkConstraint = 0; -#ifdef VDBE_PROFILE - for(i=0; inOp; i++){ - p->aOp[i].cnt = 0; - p->aOp[i].cycles = 0; - } -#endif -} - -/* -** Prepare a virtual machine for execution for the first time after -** creating the virtual machine. This involves things such -** as allocating stack space and initializing the program counter. -** After the VDBE has be prepped, it can be executed by one or more -** calls to sqlite3VdbeExec(). -** -** This function may be called exact once on a each virtual machine. -** After this routine is called the VM has been "packaged" and is ready -** to run. After this routine is called, futher calls to -** sqlite3VdbeAddOp() functions are prohibited. This routine disconnects -** the Vdbe from the Parse object that helped generate it so that the -** the Vdbe becomes an independent entity and the Parse object can be -** destroyed. -** -** Use the sqlite3VdbeRewind() procedure to restore a virtual machine back -** to its initial state after it has been run. -*/ -SQLITE_PRIVATE void sqlite3VdbeMakeReady( - Vdbe *p, /* The VDBE */ - Parse *pParse /* Parsing context */ -){ - sqlite3 *db; /* The database connection */ - int nVar; /* Number of parameters */ - int nMem; /* Number of VM memory registers */ - int nCursor; /* Number of cursors required */ - int nArg; /* Number of arguments in subprograms */ - int nOnce; /* Number of OP_Once instructions */ - int n; /* Loop counter */ - u8 *zCsr; /* Memory available for allocation */ - u8 *zEnd; /* First byte past allocated memory */ - int nByte; /* How much extra memory is needed */ - - assert( p!=0 ); - assert( p->nOp>0 ); - assert( pParse!=0 ); - assert( p->magic==VDBE_MAGIC_INIT ); - db = p->db; - assert( db->mallocFailed==0 ); - nVar = pParse->nVar; - nMem = pParse->nMem; - nCursor = pParse->nTab; - nArg = pParse->nMaxArg; - nOnce = pParse->nOnce; - if( nOnce==0 ) nOnce = 1; /* Ensure at least one byte in p->aOnceFlag[] */ - - /* For each cursor required, also allocate a memory cell. Memory - ** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by - ** the vdbe program. Instead they are used to allocate space for - ** VdbeCursor/BtCursor structures. The blob of memory associated with - ** cursor 0 is stored in memory cell nMem. Memory cell (nMem-1) - ** stores the blob of memory associated with cursor 1, etc. - ** - ** See also: allocateCursor(). - */ - nMem += nCursor; - - /* Allocate space for memory registers, SQL variables, VDBE cursors and - ** an array to marshal SQL function arguments in. - */ - zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */ - zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */ - - resolveP2Values(p, &nArg); - p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); - if( pParse->explain && nMem<10 ){ - nMem = 10; - } - memset(zCsr, 0, zEnd-zCsr); - zCsr += (zCsr - (u8*)0)&7; - assert( EIGHT_BYTE_ALIGNMENT(zCsr) ); - p->expired = 0; - - /* Memory for registers, parameters, cursor, etc, is allocated in two - ** passes. On the first pass, we try to reuse unused space at the - ** end of the opcode array. If we are unable to satisfy all memory - ** requirements by reusing the opcode array tail, then the second - ** pass will fill in the rest using a fresh allocation. - ** - ** This two-pass approach that reuses as much memory as possible from - ** the leftover space at the end of the opcode array can significantly - ** reduce the amount of memory held by a prepared statement. - */ - do { - nByte = 0; - p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte); - p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte); - p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte); - p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte); - p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*), - &zCsr, zEnd, &nByte); - p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte); - if( nByte ){ - p->pFree = sqlite3DbMallocZero(db, nByte); - } - zCsr = p->pFree; - zEnd = &zCsr[nByte]; - }while( nByte && !db->mallocFailed ); - - p->nCursor = nCursor; - p->nOnceFlag = nOnce; - if( p->aVar ){ - p->nVar = (ynVar)nVar; - for(n=0; naVar[n].flags = MEM_Null; - p->aVar[n].db = db; - } - } - if( p->azVar ){ - p->nzVar = pParse->nzVar; - memcpy(p->azVar, pParse->azVar, p->nzVar*sizeof(p->azVar[0])); - memset(pParse->azVar, 0, pParse->nzVar*sizeof(pParse->azVar[0])); - } - if( p->aMem ){ - p->aMem--; /* aMem[] goes from 1..nMem */ - p->nMem = nMem; /* not from 0..nMem-1 */ - for(n=1; n<=nMem; n++){ - p->aMem[n].flags = MEM_Invalid; - p->aMem[n].db = db; - } - } - p->explain = pParse->explain; - sqlite3VdbeRewind(p); -} - -/* -** Close a VDBE cursor and release all the resources that cursor -** happens to hold. -*/ -SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ - if( pCx==0 ){ - return; - } - sqlite3VdbeSorterClose(p->db, pCx); - if( pCx->pBt ){ - sqlite3BtreeClose(pCx->pBt); - /* The pCx->pCursor will be close automatically, if it exists, by - ** the call above. */ - }else if( pCx->pCursor ){ - sqlite3BtreeCloseCursor(pCx->pCursor); - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( pCx->pVtabCursor ){ - sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor; - const sqlite3_module *pModule = pCx->pModule; - p->inVtabMethod = 1; - pModule->xClose(pVtabCursor); - p->inVtabMethod = 0; - } -#endif -} - -/* -** Copy the values stored in the VdbeFrame structure to its Vdbe. This -** is used, for example, when a trigger sub-program is halted to restore -** control to the main program. -*/ -SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ - Vdbe *v = pFrame->v; - v->aOnceFlag = pFrame->aOnceFlag; - v->nOnceFlag = pFrame->nOnceFlag; - v->aOp = pFrame->aOp; - v->nOp = pFrame->nOp; - v->aMem = pFrame->aMem; - v->nMem = pFrame->nMem; - v->apCsr = pFrame->apCsr; - v->nCursor = pFrame->nCursor; - v->db->lastRowid = pFrame->lastRowid; - v->nChange = pFrame->nChange; - return pFrame->pc; -} - -/* -** Close all cursors. -** -** Also release any dynamic memory held by the VM in the Vdbe.aMem memory -** cell array. This is necessary as the memory cell array may contain -** pointers to VdbeFrame objects, which may in turn contain pointers to -** open cursors. -*/ -static void closeAllCursors(Vdbe *p){ - if( p->pFrame ){ - VdbeFrame *pFrame; - for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); - sqlite3VdbeFrameRestore(pFrame); - } - p->pFrame = 0; - p->nFrame = 0; - - if( p->apCsr ){ - int i; - for(i=0; inCursor; i++){ - VdbeCursor *pC = p->apCsr[i]; - if( pC ){ - sqlite3VdbeFreeCursor(p, pC); - p->apCsr[i] = 0; - } - } - } - if( p->aMem ){ - releaseMemArray(&p->aMem[1], p->nMem); - } - while( p->pDelFrame ){ - VdbeFrame *pDel = p->pDelFrame; - p->pDelFrame = pDel->pParent; - sqlite3VdbeFrameDelete(pDel); - } -} - -/* -** Clean up the VM after execution. -** -** This routine will automatically close any cursors, lists, and/or -** sorters that were left open. It also deletes the values of -** variables in the aVar[] array. -*/ -static void Cleanup(Vdbe *p){ - sqlite3 *db = p->db; - -#ifdef SQLITE_DEBUG - /* Execute assert() statements to ensure that the Vdbe.apCsr[] and - ** Vdbe.aMem[] arrays have already been cleaned up. */ - int i; - if( p->apCsr ) for(i=0; inCursor; i++) assert( p->apCsr[i]==0 ); - if( p->aMem ){ - for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Invalid ); - } -#endif - - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - p->pResultSet = 0; -} - -/* -** Set the number of result columns that will be returned by this SQL -** statement. This is now set at compile time, rather than during -** execution of the vdbe program so that sqlite3_column_count() can -** be called on an SQL statement before sqlite3_step(). -*/ -SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ - Mem *pColName; - int n; - sqlite3 *db = p->db; - - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); - sqlite3DbFree(db, p->aColName); - n = nResColumn*COLNAME_N; - p->nResColumn = (u16)nResColumn; - p->aColName = pColName = (Mem*)sqlite3DbMallocZero(db, sizeof(Mem)*n ); - if( p->aColName==0 ) return; - while( n-- > 0 ){ - pColName->flags = MEM_Null; - pColName->db = p->db; - pColName++; - } -} - -/* -** Set the name of the idx'th column to be returned by the SQL statement. -** zName must be a pointer to a nul terminated string. -** -** This call must be made after a call to sqlite3VdbeSetNumCols(). -** -** The final parameter, xDel, must be one of SQLITE_DYNAMIC, SQLITE_STATIC -** or SQLITE_TRANSIENT. If it is SQLITE_DYNAMIC, then the buffer pointed -** to by zName will be freed by sqlite3DbFree() when the vdbe is destroyed. -*/ -SQLITE_PRIVATE int sqlite3VdbeSetColName( - Vdbe *p, /* Vdbe being configured */ - int idx, /* Index of column zName applies to */ - int var, /* One of the COLNAME_* constants */ - const char *zName, /* Pointer to buffer containing name */ - void (*xDel)(void*) /* Memory management strategy for zName */ -){ - int rc; - Mem *pColName; - assert( idxnResColumn ); - assert( vardb->mallocFailed ){ - assert( !zName || xDel!=SQLITE_DYNAMIC ); - return SQLITE_NOMEM; - } - assert( p->aColName!=0 ); - pColName = &(p->aColName[idx+var*p->nResColumn]); - rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); - assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); - return rc; -} - -/* -** A read or write transaction may or may not be active on database handle -** db. If a transaction is active, commit it. If there is a -** write-transaction spanning more than one database file, this routine -** takes care of the master journal trickery. -*/ -static int vdbeCommit(sqlite3 *db, Vdbe *p){ - int i; - int nTrans = 0; /* Number of databases with an active write-transaction */ - int rc = SQLITE_OK; - int needXcommit = 0; - -#ifdef SQLITE_OMIT_VIRTUALTABLE - /* With this option, sqlite3VtabSync() is defined to be simply - ** SQLITE_OK so p is not used. - */ - UNUSED_PARAMETER(p); -#endif - - /* Before doing anything else, call the xSync() callback for any - ** virtual module tables written in this transaction. This has to - ** be done before determining whether a master journal file is - ** required, as an xSync() callback may add an attached database - ** to the transaction. - */ - rc = sqlite3VtabSync(db, &p->zErrMsg); - - /* This loop determines (a) if the commit hook should be invoked and - ** (b) how many database files have open write transactions, not - ** including the temp database. (b) is important because if more than - ** one database file has an open write transaction, a master journal - ** file is required for an atomic commit. - */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( sqlite3BtreeIsInTrans(pBt) ){ - needXcommit = 1; - if( i!=1 ) nTrans++; - sqlite3BtreeEnter(pBt); - rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt)); - sqlite3BtreeLeave(pBt); - } - } - if( rc!=SQLITE_OK ){ - return rc; - } - - /* If there are any write-transactions at all, invoke the commit hook */ - if( needXcommit && db->xCommitCallback ){ - rc = db->xCommitCallback(db->pCommitArg); - if( rc ){ - return SQLITE_CONSTRAINT_COMMITHOOK; - } - } - - /* The simple case - no more than one database file (not counting the - ** TEMP database) has a transaction active. There is no need for the - ** master-journal. - ** - ** If the return value of sqlite3BtreeGetFilename() is a zero length - ** string, it means the main database is :memory: or a temp file. In - ** that case we do not support atomic multi-file commits, so use the - ** simple case then too. - */ - if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt)) - || nTrans<=1 - ){ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlite3BtreeCommitPhaseOne(pBt, 0); - } - } - - /* Do the commit only if all databases successfully complete phase 1. - ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an - ** IO error while deleting or truncating a journal file. It is unlikely, - ** but could happen. In this case abandon processing and return the error. - */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlite3BtreeCommitPhaseTwo(pBt, 0); - } - } - if( rc==SQLITE_OK ){ - sqlite3VtabCommit(db); - } - } - - /* The complex case - There is a multi-file write-transaction active. - ** This requires a master journal file to ensure the transaction is - ** committed atomicly. - */ -#ifndef SQLITE_OMIT_DISKIO - else{ - sqlite3_vfs *pVfs = db->pVfs; - int needSync = 0; - char *zMaster = 0; /* File-name for the master journal */ - char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt); - sqlite3_file *pMaster = 0; - i64 offset = 0; - int res; - int retryCount = 0; - int nMainFile; - - /* Select a master journal file name */ - nMainFile = sqlite3Strlen30(zMainFile); - zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz", zMainFile); - if( zMaster==0 ) return SQLITE_NOMEM; - do { - u32 iRandom; - if( retryCount ){ - if( retryCount>100 ){ - sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster); - sqlite3OsDelete(pVfs, zMaster, 0); - break; - }else if( retryCount==1 ){ - sqlite3_log(SQLITE_FULL, "MJ collide: %s", zMaster); - } - } - retryCount++; - sqlite3_randomness(sizeof(iRandom), &iRandom); - sqlite3_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X", - (iRandom>>8)&0xffffff, iRandom&0xff); - /* The antipenultimate character of the master journal name must - ** be "9" to avoid name collisions when using 8+3 filenames. */ - assert( zMaster[sqlite3Strlen30(zMaster)-3]=='9' ); - sqlite3FileSuffix3(zMainFile, zMaster); - rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res); - }while( rc==SQLITE_OK && res ); - if( rc==SQLITE_OK ){ - /* Open the master journal. */ - rc = sqlite3OsOpenMalloc(pVfs, zMaster, &pMaster, - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| - SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0 - ); - } - if( rc!=SQLITE_OK ){ - sqlite3DbFree(db, zMaster); - return rc; - } - - /* Write the name of each database file in the transaction into the new - ** master journal file. If an error occurs at this point close - ** and delete the master journal file. All the individual journal files - ** still have 'null' as the master journal pointer, so they will roll - ** back independently if a failure occurs. - */ - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( sqlite3BtreeIsInTrans(pBt) ){ - char const *zFile = sqlite3BtreeGetJournalname(pBt); - if( zFile==0 ){ - continue; /* Ignore TEMP and :memory: databases */ - } - assert( zFile[0]!=0 ); - if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){ - needSync = 1; - } - rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset); - offset += sqlite3Strlen30(zFile)+1; - if( rc!=SQLITE_OK ){ - sqlite3OsCloseFree(pMaster); - sqlite3OsDelete(pVfs, zMaster, 0); - sqlite3DbFree(db, zMaster); - return rc; - } - } - } - - /* Sync the master journal file. If the IOCAP_SEQUENTIAL device - ** flag is set this is not required. - */ - if( needSync - && 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL) - && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL)) - ){ - sqlite3OsCloseFree(pMaster); - sqlite3OsDelete(pVfs, zMaster, 0); - sqlite3DbFree(db, zMaster); - return rc; - } - - /* Sync all the db files involved in the transaction. The same call - ** sets the master journal pointer in each individual journal. If - ** an error occurs here, do not delete the master journal file. - ** - ** If the error occurs during the first call to - ** sqlite3BtreeCommitPhaseOne(), then there is a chance that the - ** master journal file will be orphaned. But we cannot delete it, - ** in case the master journal file name was written into the journal - ** file before the failure occurred. - */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster); - } - } - sqlite3OsCloseFree(pMaster); - assert( rc!=SQLITE_BUSY ); - if( rc!=SQLITE_OK ){ - sqlite3DbFree(db, zMaster); - return rc; - } - - /* Delete the master journal file. This commits the transaction. After - ** doing this the directory is synced again before any individual - ** transaction files are deleted. - */ - rc = sqlite3OsDelete(pVfs, zMaster, 1); - sqlite3DbFree(db, zMaster); - zMaster = 0; - if( rc ){ - return rc; - } - - /* All files and directories have already been synced, so the following - ** calls to sqlite3BtreeCommitPhaseTwo() are only closing files and - ** deleting or truncating journals. If something goes wrong while - ** this is happening we don't really care. The integrity of the - ** transaction is already guaranteed, but some stray 'cold' journals - ** may be lying around. Returning an error code won't help matters. - */ - disable_simulated_io_errors(); - sqlite3BeginBenignMalloc(); - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - sqlite3BtreeCommitPhaseTwo(pBt, 1); - } - } - sqlite3EndBenignMalloc(); - enable_simulated_io_errors(); - - sqlite3VtabCommit(db); - } -#endif - - return rc; -} - -/* -** This routine checks that the sqlite3.activeVdbeCnt count variable -** matches the number of vdbe's in the list sqlite3.pVdbe that are -** currently active. An assertion fails if the two counts do not match. -** This is an internal self-check only - it is not an essential processing -** step. -** -** This is a no-op if NDEBUG is defined. -*/ -#ifndef NDEBUG -static void checkActiveVdbeCnt(sqlite3 *db){ - Vdbe *p; - int cnt = 0; - int nWrite = 0; - p = db->pVdbe; - while( p ){ - if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){ - cnt++; - if( p->readOnly==0 ) nWrite++; - } - p = p->pNext; - } - assert( cnt==db->activeVdbeCnt ); - assert( nWrite==db->writeVdbeCnt ); -} -#else -#define checkActiveVdbeCnt(x) -#endif - -/* -** If the Vdbe passed as the first argument opened a statement-transaction, -** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or -** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement -** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the -** statement transaction is commtted. -** -** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned. -** Otherwise SQLITE_OK. -*/ -SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ - sqlite3 *const db = p->db; - int rc = SQLITE_OK; - - /* If p->iStatement is greater than zero, then this Vdbe opened a - ** statement transaction that should be closed here. The only exception - ** is that an IO error may have occurred, causing an emergency rollback. - ** In this case (db->nStatement==0), and there is nothing to do. - */ - if( db->nStatement && p->iStatement ){ - int i; - const int iSavepoint = p->iStatement-1; - - assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE); - assert( db->nStatement>0 ); - assert( p->iStatement==(db->nStatement+db->nSavepoint) ); - - for(i=0; inDb; i++){ - int rc2 = SQLITE_OK; - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - if( eOp==SAVEPOINT_ROLLBACK ){ - rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint); - } - if( rc2==SQLITE_OK ){ - rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint); - } - if( rc==SQLITE_OK ){ - rc = rc2; - } - } - } - db->nStatement--; - p->iStatement = 0; - - if( rc==SQLITE_OK ){ - if( eOp==SAVEPOINT_ROLLBACK ){ - rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint); - } - if( rc==SQLITE_OK ){ - rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint); - } - } - - /* If the statement transaction is being rolled back, also restore the - ** database handles deferred constraint counter to the value it had when - ** the statement transaction was opened. */ - if( eOp==SAVEPOINT_ROLLBACK ){ - db->nDeferredCons = p->nStmtDefCons; - } - } - return rc; -} - -/* -** This function is called when a transaction opened by the database -** handle associated with the VM passed as an argument is about to be -** committed. If there are outstanding deferred foreign key constraint -** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK. -** -** If there are outstanding FK violations and this function returns -** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY -** and write an error message to it. Then return SQLITE_ERROR. -*/ -#ifndef SQLITE_OMIT_FOREIGN_KEY -SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){ - sqlite3 *db = p->db; - if( (deferred && db->nDeferredCons>0) || (!deferred && p->nFkConstraint>0) ){ - p->rc = SQLITE_CONSTRAINT_FOREIGNKEY; - p->errorAction = OE_Abort; - sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed"); - return SQLITE_ERROR; - } - return SQLITE_OK; -} -#endif - -/* -** This routine is called the when a VDBE tries to halt. If the VDBE -** has made changes and is in autocommit mode, then commit those -** changes. If a rollback is needed, then do the rollback. -** -** This routine is the only way to move the state of a VM from -** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT. It is harmless to -** call this on a VM that is in the SQLITE_MAGIC_HALT state. -** -** Return an error code. If the commit could not complete because of -** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it -** means the close did not happen and needs to be repeated. -*/ -SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ - int rc; /* Used to store transient return codes */ - sqlite3 *db = p->db; - - /* This function contains the logic that determines if a statement or - ** transaction will be committed or rolled back as a result of the - ** execution of this virtual machine. - ** - ** If any of the following errors occur: - ** - ** SQLITE_NOMEM - ** SQLITE_IOERR - ** SQLITE_FULL - ** SQLITE_INTERRUPT - ** - ** Then the internal cache might have been left in an inconsistent - ** state. We need to rollback the statement transaction, if there is - ** one, or the complete transaction if there is no statement transaction. - */ - - if( p->db->mallocFailed ){ - p->rc = SQLITE_NOMEM; - } - if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag); - closeAllCursors(p); - if( p->magic!=VDBE_MAGIC_RUN ){ - return SQLITE_OK; - } - checkActiveVdbeCnt(db); - - /* No commit or rollback needed if the program never started */ - if( p->pc>=0 ){ - int mrc; /* Primary error code from p->rc */ - int eStatementOp = 0; - int isSpecialError; /* Set to true if a 'special' error */ - - /* Lock all btrees used by the statement */ - sqlite3VdbeEnter(p); - - /* Check for one of the special errors */ - mrc = p->rc & 0xff; - assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */ - isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR - || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; - if( isSpecialError ){ - /* If the query was read-only and the error code is SQLITE_INTERRUPT, - ** no rollback is necessary. Otherwise, at least a savepoint - ** transaction must be rolled back to restore the database to a - ** consistent state. - ** - ** Even if the statement is read-only, it is important to perform - ** a statement or transaction rollback operation. If the error - ** occurred while writing to the journal, sub-journal or database - ** file as part of an effort to free up cache space (see function - ** pagerStress() in pager.c), the rollback is required to restore - ** the pager to a consistent state. - */ - if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ - if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ - eStatementOp = SAVEPOINT_ROLLBACK; - }else{ - /* We are forced to roll back the active transaction. Before doing - ** so, abort any other statements this handle currently has active. - */ - sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); - sqlite3CloseSavepoints(db); - db->autoCommit = 1; - } - } - } - - /* Check for immediate foreign key violations. */ - if( p->rc==SQLITE_OK ){ - sqlite3VdbeCheckFk(p, 0); - } - - /* If the auto-commit flag is set and this is the only active writer - ** VM, then we do either a commit or rollback of the current transaction. - ** - ** Note: This block also runs if one of the special errors handled - ** above has occurred. - */ - if( !sqlite3VtabInSync(db) - && db->autoCommit - && db->writeVdbeCnt==(p->readOnly==0) - ){ - if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - rc = sqlite3VdbeCheckFk(p, 1); - if( rc!=SQLITE_OK ){ - if( NEVER(p->readOnly) ){ - sqlite3VdbeLeave(p); - return SQLITE_ERROR; - } - rc = SQLITE_CONSTRAINT_FOREIGNKEY; - }else{ - /* The auto-commit flag is true, the vdbe program was successful - ** or hit an 'OR FAIL' constraint and there are no deferred foreign - ** key constraints to hold up the transaction. This means a commit - ** is required. */ - rc = vdbeCommit(db, p); - } - if( rc==SQLITE_BUSY && p->readOnly ){ - sqlite3VdbeLeave(p); - return SQLITE_BUSY; - }else if( rc!=SQLITE_OK ){ - p->rc = rc; - sqlite3RollbackAll(db, SQLITE_OK); - }else{ - db->nDeferredCons = 0; - sqlite3CommitInternalChanges(db); - } - }else{ - sqlite3RollbackAll(db, SQLITE_OK); - } - db->nStatement = 0; - }else if( eStatementOp==0 ){ - if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ - eStatementOp = SAVEPOINT_RELEASE; - }else if( p->errorAction==OE_Abort ){ - eStatementOp = SAVEPOINT_ROLLBACK; - }else{ - sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); - sqlite3CloseSavepoints(db); - db->autoCommit = 1; - } - } - - /* If eStatementOp is non-zero, then a statement transaction needs to - ** be committed or rolled back. Call sqlite3VdbeCloseStatement() to - ** do so. If this operation returns an error, and the current statement - ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then promote the - ** current statement error code. - */ - if( eStatementOp ){ - rc = sqlite3VdbeCloseStatement(p, eStatementOp); - if( rc ){ - if( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ){ - p->rc = rc; - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - } - sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); - sqlite3CloseSavepoints(db); - db->autoCommit = 1; - } - } - - /* If this was an INSERT, UPDATE or DELETE and no statement transaction - ** has been rolled back, update the database connection change-counter. - */ - if( p->changeCntOn ){ - if( eStatementOp!=SAVEPOINT_ROLLBACK ){ - sqlite3VdbeSetChanges(db, p->nChange); - }else{ - sqlite3VdbeSetChanges(db, 0); - } - p->nChange = 0; - } - - /* Release the locks */ - sqlite3VdbeLeave(p); - } - - /* We have successfully halted and closed the VM. Record this fact. */ - if( p->pc>=0 ){ - db->activeVdbeCnt--; - if( !p->readOnly ){ - db->writeVdbeCnt--; - } - assert( db->activeVdbeCnt>=db->writeVdbeCnt ); - } - p->magic = VDBE_MAGIC_HALT; - checkActiveVdbeCnt(db); - if( p->db->mallocFailed ){ - p->rc = SQLITE_NOMEM; - } - - /* If the auto-commit flag is set to true, then any locks that were held - ** by connection db have now been released. Call sqlite3ConnectionUnlocked() - ** to invoke any required unlock-notify callbacks. - */ - if( db->autoCommit ){ - sqlite3ConnectionUnlocked(db); - } - - assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 ); - return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK); -} - - -/* -** Each VDBE holds the result of the most recent sqlite3_step() call -** in p->rc. This routine sets that result back to SQLITE_OK. -*/ -SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){ - p->rc = SQLITE_OK; -} - -/* -** Copy the error code and error message belonging to the VDBE passed -** as the first argument to its database handle (so that they will be -** returned by calls to sqlite3_errcode() and sqlite3_errmsg()). -** -** This function does not clear the VDBE error code or message, just -** copies them to the database handle. -*/ -SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){ - sqlite3 *db = p->db; - int rc = p->rc; - if( p->zErrMsg ){ - u8 mallocFailed = db->mallocFailed; - sqlite3BeginBenignMalloc(); - sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); - sqlite3EndBenignMalloc(); - db->mallocFailed = mallocFailed; - db->errCode = rc; - }else{ - sqlite3Error(db, rc, 0); - } - return rc; -} - -#ifdef SQLITE_ENABLE_SQLLOG -/* -** If an SQLITE_CONFIG_SQLLOG hook is registered and the VM has been run, -** invoke it. -*/ -static void vdbeInvokeSqllog(Vdbe *v){ - if( sqlite3GlobalConfig.xSqllog && v->rc==SQLITE_OK && v->zSql && v->pc>=0 ){ - char *zExpanded = sqlite3VdbeExpandSql(v, v->zSql); - assert( v->db->init.busy==0 ); - if( zExpanded ){ - sqlite3GlobalConfig.xSqllog( - sqlite3GlobalConfig.pSqllogArg, v->db, zExpanded, 1 - ); - sqlite3DbFree(v->db, zExpanded); - } - } -} -#else -# define vdbeInvokeSqllog(x) -#endif - -/* -** Clean up a VDBE after execution but do not delete the VDBE just yet. -** Write any error messages into *pzErrMsg. Return the result code. -** -** After this routine is run, the VDBE should be ready to be executed -** again. -** -** To look at it another way, this routine resets the state of the -** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to -** VDBE_MAGIC_INIT. -*/ -SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ - sqlite3 *db; - db = p->db; - - /* If the VM did not run to completion or if it encountered an - ** error, then it might not have been halted properly. So halt - ** it now. - */ - sqlite3VdbeHalt(p); - - /* If the VDBE has be run even partially, then transfer the error code - ** and error message from the VDBE into the main database structure. But - ** if the VDBE has just been set to run but has not actually executed any - ** instructions yet, leave the main database error information unchanged. - */ - if( p->pc>=0 ){ - vdbeInvokeSqllog(p); - sqlite3VdbeTransferError(p); - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - if( p->runOnlyOnce ) p->expired = 1; - }else if( p->rc && p->expired ){ - /* The expired flag was set on the VDBE before the first call - ** to sqlite3_step(). For consistency (since sqlite3_step() was - ** called), set the database error in this case as well. - */ - sqlite3Error(db, p->rc, 0); - sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - } - - /* Reclaim all memory used by the VDBE - */ - Cleanup(p); - - /* Save profiling information from this VDBE run. - */ -#ifdef VDBE_PROFILE - { - FILE *out = fopen("vdbe_profile.out", "a"); - if( out ){ - int i; - fprintf(out, "---- "); - for(i=0; inOp; i++){ - fprintf(out, "%02x", p->aOp[i].opcode); - } - fprintf(out, "\n"); - for(i=0; inOp; i++){ - fprintf(out, "%6d %10lld %8lld ", - p->aOp[i].cnt, - p->aOp[i].cycles, - p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 - ); - sqlite3VdbePrintOp(out, i, &p->aOp[i]); - } - fclose(out); - } - } -#endif - p->magic = VDBE_MAGIC_INIT; - return p->rc & db->errMask; -} - -/* -** Clean up and delete a VDBE after execution. Return an integer which is -** the result code. Write any error message text into *pzErrMsg. -*/ -SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){ - int rc = SQLITE_OK; - if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ - rc = sqlite3VdbeReset(p); - assert( (rc & p->db->errMask)==rc ); - } - sqlite3VdbeDelete(p); - return rc; -} - -/* -** Call the destructor for each auxdata entry in pVdbeFunc for which -** the corresponding bit in mask is clear. Auxdata entries beyond 31 -** are always destroyed. To destroy all auxdata entries, call this -** routine with mask==0. -*/ -SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){ - int i; - for(i=0; inAux; i++){ - struct AuxData *pAux = &pVdbeFunc->apAux[i]; - if( (i>31 || !(mask&(((u32)1)<pAux ){ - if( pAux->xDelete ){ - pAux->xDelete(pAux->pAux); - } - pAux->pAux = 0; - } - } -} - -/* -** Free all memory associated with the Vdbe passed as the second argument, -** except for object itself, which is preserved. -** -** The difference between this function and sqlite3VdbeDelete() is that -** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with -** the database connection and frees the object itself. -*/ -SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ - SubProgram *pSub, *pNext; - int i; - assert( p->db==0 || p->db==db ); - releaseMemArray(p->aVar, p->nVar); - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); - for(pSub=p->pProgram; pSub; pSub=pNext){ - pNext = pSub->pNext; - vdbeFreeOpArray(db, pSub->aOp, pSub->nOp); - sqlite3DbFree(db, pSub); - } - for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]); - vdbeFreeOpArray(db, p->aOp, p->nOp); - sqlite3DbFree(db, p->aLabel); - sqlite3DbFree(db, p->aColName); - sqlite3DbFree(db, p->zSql); - sqlite3DbFree(db, p->pFree); -#if defined(SQLITE_ENABLE_TREE_EXPLAIN) - sqlite3DbFree(db, p->zExplain); - sqlite3DbFree(db, p->pExplain); -#endif -} - -/* -** Delete an entire VDBE. -*/ -SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){ - sqlite3 *db; - - if( NEVER(p==0) ) return; - db = p->db; - assert( sqlite3_mutex_held(db->mutex) ); - sqlite3VdbeClearObject(db, p); - if( p->pPrev ){ - p->pPrev->pNext = p->pNext; - }else{ - assert( db->pVdbe==p ); - db->pVdbe = p->pNext; - } - if( p->pNext ){ - p->pNext->pPrev = p->pPrev; - } - p->magic = VDBE_MAGIC_DEAD; - p->db = 0; - sqlite3DbFree(db, p); -} - -/* -** Make sure the cursor p is ready to read or write the row to which it -** was last positioned. Return an error code if an OOM fault or I/O error -** prevents us from positioning the cursor to its correct position. -** -** If a MoveTo operation is pending on the given cursor, then do that -** MoveTo now. If no move is pending, check to see if the row has been -** deleted out from under the cursor and if it has, mark the row as -** a NULL row. -** -** If the cursor is already pointing to the correct row and that row has -** not been deleted out from under the cursor, then this routine is a no-op. -*/ -SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){ - if( p->deferredMoveto ){ - int res, rc; -#ifdef SQLITE_TEST - extern int sqlite3_search_count; -#endif - assert( p->isTable ); - rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res); - if( rc ) return rc; - p->lastRowid = p->movetoTarget; - if( res!=0 ) return SQLITE_CORRUPT_BKPT; - p->rowidIsValid = 1; -#ifdef SQLITE_TEST - sqlite3_search_count++; -#endif - p->deferredMoveto = 0; - p->cacheStatus = CACHE_STALE; - }else if( ALWAYS(p->pCursor) ){ - int hasMoved; - int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved); - if( rc ) return rc; - if( hasMoved ){ - p->cacheStatus = CACHE_STALE; - p->nullRow = 1; - } - } - return SQLITE_OK; -} - -/* -** The following functions: -** -** sqlite3VdbeSerialType() -** sqlite3VdbeSerialTypeLen() -** sqlite3VdbeSerialLen() -** sqlite3VdbeSerialPut() -** sqlite3VdbeSerialGet() -** -** encapsulate the code that serializes values for storage in SQLite -** data and index records. Each serialized value consists of a -** 'serial-type' and a blob of data. The serial type is an 8-byte unsigned -** integer, stored as a varint. -** -** In an SQLite index record, the serial type is stored directly before -** the blob of data that it corresponds to. In a table record, all serial -** types are stored at the start of the record, and the blobs of data at -** the end. Hence these functions allow the caller to handle the -** serial-type and data blob separately. -** -** The following table describes the various storage classes for data: -** -** serial type bytes of data type -** -------------- --------------- --------------- -** 0 0 NULL -** 1 1 signed integer -** 2 2 signed integer -** 3 3 signed integer -** 4 4 signed integer -** 5 6 signed integer -** 6 8 signed integer -** 7 8 IEEE float -** 8 0 Integer constant 0 -** 9 0 Integer constant 1 -** 10,11 reserved for expansion -** N>=12 and even (N-12)/2 BLOB -** N>=13 and odd (N-13)/2 text -** -** The 8 and 9 types were added in 3.3.0, file format 4. Prior versions -** of SQLite will not understand those serial types. -*/ - -/* -** Return the serial-type for the value stored in pMem. -*/ -SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){ - int flags = pMem->flags; - int n; - - if( flags&MEM_Null ){ - return 0; - } - if( flags&MEM_Int ){ - /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ -# define MAX_6BYTE ((((i64)0x00008000)<<32)-1) - i64 i = pMem->u.i; - u64 u; - if( i<0 ){ - if( i<(-MAX_6BYTE) ) return 6; - /* Previous test prevents: u = -(-9223372036854775808) */ - u = -i; - }else{ - u = i; - } - if( u<=127 ){ - return ((i&1)==i && file_format>=4) ? 8+(u32)u : 1; - } - if( u<=32767 ) return 2; - if( u<=8388607 ) return 3; - if( u<=2147483647 ) return 4; - if( u<=MAX_6BYTE ) return 5; - return 6; - } - if( flags&MEM_Real ){ - return 7; - } - assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) ); - n = pMem->n; - if( flags & MEM_Zero ){ - n += pMem->u.nZero; - } - assert( n>=0 ); - return ((n*2) + 12 + ((flags&MEM_Str)!=0)); -} - -/* -** Return the length of the data corresponding to the supplied serial-type. -*/ -SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32 serial_type){ - if( serial_type>=12 ){ - return (serial_type-12)/2; - }else{ - static const u8 aSize[] = { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, 0, 0 }; - return aSize[serial_type]; - } -} - -/* -** If we are on an architecture with mixed-endian floating -** points (ex: ARM7) then swap the lower 4 bytes with the -** upper 4 bytes. Return the result. -** -** For most architectures, this is a no-op. -** -** (later): It is reported to me that the mixed-endian problem -** on ARM7 is an issue with GCC, not with the ARM7 chip. It seems -** that early versions of GCC stored the two words of a 64-bit -** float in the wrong order. And that error has been propagated -** ever since. The blame is not necessarily with GCC, though. -** GCC might have just copying the problem from a prior compiler. -** I am also told that newer versions of GCC that follow a different -** ABI get the byte order right. -** -** Developers using SQLite on an ARM7 should compile and run their -** application using -DSQLITE_DEBUG=1 at least once. With DEBUG -** enabled, some asserts below will ensure that the byte order of -** floating point values is correct. -** -** (2007-08-30) Frank van Vugt has studied this problem closely -** and has send his findings to the SQLite developers. Frank -** writes that some Linux kernels offer floating point hardware -** emulation that uses only 32-bit mantissas instead of a full -** 48-bits as required by the IEEE standard. (This is the -** CONFIG_FPE_FASTFPE option.) On such systems, floating point -** byte swapping becomes very complicated. To avoid problems, -** the necessary byte swapping is carried out using a 64-bit integer -** rather than a 64-bit float. Frank assures us that the code here -** works for him. We, the developers, have no way to independently -** verify this, but Frank seems to know what he is talking about -** so we trust him. -*/ -#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT -static u64 floatSwap(u64 in){ - union { - u64 r; - u32 i[2]; - } u; - u32 t; - - u.r = in; - t = u.i[0]; - u.i[0] = u.i[1]; - u.i[1] = t; - return u.r; -} -# define swapMixedEndianFloat(X) X = floatSwap(X) -#else -# define swapMixedEndianFloat(X) -#endif - -/* -** Write the serialized data blob for the value stored in pMem into -** buf. It is assumed that the caller has allocated sufficient space. -** Return the number of bytes written. -** -** nBuf is the amount of space left in buf[]. nBuf must always be -** large enough to hold the entire field. Except, if the field is -** a blob with a zero-filled tail, then buf[] might be just the right -** size to hold everything except for the zero-filled tail. If buf[] -** is only big enough to hold the non-zero prefix, then only write that -** prefix into buf[]. But if buf[] is large enough to hold both the -** prefix and the tail then write the prefix and set the tail to all -** zeros. -** -** Return the number of bytes actually written into buf[]. The number -** of bytes in the zero-filled tail is included in the return value only -** if those bytes were zeroed in buf[]. -*/ -SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){ - u32 serial_type = sqlite3VdbeSerialType(pMem, file_format); - u32 len; - - /* Integer and Real */ - if( serial_type<=7 && serial_type>0 ){ - u64 v; - u32 i; - if( serial_type==7 ){ - assert( sizeof(v)==sizeof(pMem->r) ); - memcpy(&v, &pMem->r, sizeof(v)); - swapMixedEndianFloat(v); - }else{ - v = pMem->u.i; - } - len = i = sqlite3VdbeSerialTypeLen(serial_type); - assert( len<=(u32)nBuf ); - while( i-- ){ - buf[i] = (u8)(v&0xFF); - v >>= 8; - } - return len; - } - - /* String or blob */ - if( serial_type>=12 ){ - assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0) - == (int)sqlite3VdbeSerialTypeLen(serial_type) ); - assert( pMem->n<=nBuf ); - len = pMem->n; - memcpy(buf, pMem->z, len); - if( pMem->flags & MEM_Zero ){ - len += pMem->u.nZero; - assert( nBuf>=0 ); - if( len > (u32)nBuf ){ - len = (u32)nBuf; - } - memset(&buf[pMem->n], 0, len-pMem->n); - } - return len; - } - - /* NULL or constants 0 or 1 */ - return 0; -} - -/* -** Deserialize the data blob pointed to by buf as serial type serial_type -** and store the result in pMem. Return the number of bytes read. -*/ -SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( - const unsigned char *buf, /* Buffer to deserialize from */ - u32 serial_type, /* Serial type to deserialize */ - Mem *pMem /* Memory cell to write value into */ -){ - switch( serial_type ){ - case 10: /* Reserved for future use */ - case 11: /* Reserved for future use */ - case 0: { /* NULL */ - pMem->flags = MEM_Null; - break; - } - case 1: { /* 1-byte signed integer */ - pMem->u.i = (signed char)buf[0]; - pMem->flags = MEM_Int; - return 1; - } - case 2: { /* 2-byte signed integer */ - pMem->u.i = (((signed char)buf[0])<<8) | buf[1]; - pMem->flags = MEM_Int; - return 2; - } - case 3: { /* 3-byte signed integer */ - pMem->u.i = (((signed char)buf[0])<<16) | (buf[1]<<8) | buf[2]; - pMem->flags = MEM_Int; - return 3; - } - case 4: { /* 4-byte signed integer */ - pMem->u.i = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; - pMem->flags = MEM_Int; - return 4; - } - case 5: { /* 6-byte signed integer */ - u64 x = (((signed char)buf[0])<<8) | buf[1]; - u32 y = (buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5]; - x = (x<<32) | y; - pMem->u.i = *(i64*)&x; - pMem->flags = MEM_Int; - return 6; - } - case 6: /* 8-byte signed integer */ - case 7: { /* IEEE floating point */ - u64 x; - u32 y; -#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT) - /* Verify that integers and floating point values use the same - ** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is - ** defined that 64-bit floating point values really are mixed - ** endian. - */ - static const u64 t1 = ((u64)0x3ff00000)<<32; - static const double r1 = 1.0; - u64 t2 = t1; - swapMixedEndianFloat(t2); - assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); -#endif - - x = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; - y = (buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7]; - x = (x<<32) | y; - if( serial_type==6 ){ - pMem->u.i = *(i64*)&x; - pMem->flags = MEM_Int; - }else{ - assert( sizeof(x)==8 && sizeof(pMem->r)==8 ); - swapMixedEndianFloat(x); - memcpy(&pMem->r, &x, sizeof(x)); - pMem->flags = sqlite3IsNaN(pMem->r) ? MEM_Null : MEM_Real; - } - return 8; - } - case 8: /* Integer 0 */ - case 9: { /* Integer 1 */ - pMem->u.i = serial_type-8; - pMem->flags = MEM_Int; - return 0; - } - default: { - u32 len = (serial_type-12)/2; - pMem->z = (char *)buf; - pMem->n = len; - pMem->xDel = 0; - if( serial_type&0x01 ){ - pMem->flags = MEM_Str | MEM_Ephem; - }else{ - pMem->flags = MEM_Blob | MEM_Ephem; - } - return len; - } - } - return 0; -} - -/* -** This routine is used to allocate sufficient space for an UnpackedRecord -** structure large enough to be used with sqlite3VdbeRecordUnpack() if -** the first argument is a pointer to KeyInfo structure pKeyInfo. -** -** The space is either allocated using sqlite3DbMallocRaw() or from within -** the unaligned buffer passed via the second and third arguments (presumably -** stack space). If the former, then *ppFree is set to a pointer that should -** be eventually freed by the caller using sqlite3DbFree(). Or, if the -** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL -** before returning. -** -** If an OOM error occurs, NULL is returned. -*/ -SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord( - KeyInfo *pKeyInfo, /* Description of the record */ - char *pSpace, /* Unaligned space available */ - int szSpace, /* Size of pSpace[] in bytes */ - char **ppFree /* OUT: Caller should free this pointer */ -){ - UnpackedRecord *p; /* Unpacked record to return */ - int nOff; /* Increment pSpace by nOff to align it */ - int nByte; /* Number of bytes required for *p */ - - /* We want to shift the pointer pSpace up such that it is 8-byte aligned. - ** Thus, we need to calculate a value, nOff, between 0 and 7, to shift - ** it by. If pSpace is already 8-byte aligned, nOff should be zero. - */ - nOff = (8 - (SQLITE_PTR_TO_INT(pSpace) & 7)) & 7; - nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1); - if( nByte>szSpace+nOff ){ - p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte); - *ppFree = (char *)p; - if( !p ) return 0; - }else{ - p = (UnpackedRecord*)&pSpace[nOff]; - *ppFree = 0; - } - - p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))]; - assert( pKeyInfo->aSortOrder!=0 ); - p->pKeyInfo = pKeyInfo; - p->nField = pKeyInfo->nField + 1; - return p; -} - -/* -** Given the nKey-byte encoding of a record in pKey[], populate the -** UnpackedRecord structure indicated by the fourth argument with the -** contents of the decoded record. -*/ -SQLITE_PRIVATE void sqlite3VdbeRecordUnpack( - KeyInfo *pKeyInfo, /* Information about the record format */ - int nKey, /* Size of the binary record */ - const void *pKey, /* The binary record */ - UnpackedRecord *p /* Populate this structure before returning. */ -){ - const unsigned char *aKey = (const unsigned char *)pKey; - int d; - u32 idx; /* Offset in aKey[] to read from */ - u16 u; /* Unsigned loop counter */ - u32 szHdr; - Mem *pMem = p->aMem; - - p->flags = 0; - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - idx = getVarint32(aKey, szHdr); - d = szHdr; - u = 0; - while( idxnField && d<=nKey ){ - u32 serial_type; - - idx += getVarint32(&aKey[idx], serial_type); - pMem->enc = pKeyInfo->enc; - pMem->db = pKeyInfo->db; - /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */ - pMem->zMalloc = 0; - d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); - pMem++; - u++; - } - assert( u<=pKeyInfo->nField + 1 ); - p->nField = u; -} - -/* -** This function compares the two table rows or index records -** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero -** or positive integer if key1 is less than, equal to or -** greater than key2. The {nKey1, pKey1} key must be a blob -** created by th OP_MakeRecord opcode of the VDBE. The pPKey2 -** key must be a parsed key such as obtained from -** sqlite3VdbeParseRecord. -** -** Key1 and Key2 do not have to contain the same number of fields. -** The key with fewer fields is usually compares less than the -** longer key. However if the UNPACKED_INCRKEY flags in pPKey2 is set -** and the common prefixes are equal, then key1 is less than key2. -** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are -** equal, then the keys are considered to be equal and -** the parts beyond the common prefix are ignored. -*/ -SQLITE_PRIVATE int sqlite3VdbeRecordCompare( - int nKey1, const void *pKey1, /* Left key */ - UnpackedRecord *pPKey2 /* Right key */ -){ - int d1; /* Offset into aKey[] of next data element */ - u32 idx1; /* Offset into aKey[] of next header element */ - u32 szHdr1; /* Number of bytes in header */ - int i = 0; - int nField; - int rc = 0; - const unsigned char *aKey1 = (const unsigned char *)pKey1; - KeyInfo *pKeyInfo; - Mem mem1; - - pKeyInfo = pPKey2->pKeyInfo; - mem1.enc = pKeyInfo->enc; - mem1.db = pKeyInfo->db; - /* mem1.flags = 0; // Will be initialized by sqlite3VdbeSerialGet() */ - VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */ - - /* Compilers may complain that mem1.u.i is potentially uninitialized. - ** We could initialize it, as shown here, to silence those complaints. - ** But in fact, mem1.u.i will never actually be used uninitialized, and doing - ** the unnecessary initialization has a measurable negative performance - ** impact, since this routine is a very high runner. And so, we choose - ** to ignore the compiler warnings and leave this variable uninitialized. - */ - /* mem1.u.i = 0; // not needed, here to silence compiler warning */ - - idx1 = getVarint32(aKey1, szHdr1); - d1 = szHdr1; - nField = pKeyInfo->nField; - assert( pKeyInfo->aSortOrder!=0 ); - while( idx1nField ){ - u32 serial_type1; - - /* Read the serial types for the next element in each key. */ - idx1 += getVarint32( aKey1+idx1, serial_type1 ); - if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break; - - /* Extract the values to be compared. - */ - d1 += sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); - - /* Do the comparison - */ - rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], - iaColl[i] : 0); - if( rc!=0 ){ - assert( mem1.zMalloc==0 ); /* See comment below */ - - /* Invert the result if we are using DESC sort order. */ - if( iaSortOrder[i] ){ - rc = -rc; - } - - /* If the PREFIX_SEARCH flag is set and all fields except the final - ** rowid field were equal, then clear the PREFIX_SEARCH flag and set - ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1). - ** This is used by the OP_IsUnique opcode. - */ - if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){ - assert( idx1==szHdr1 && rc ); - assert( mem1.flags & MEM_Int ); - pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH; - pPKey2->rowid = mem1.u.i; - } - - return rc; - } - i++; - } - - /* No memory allocation is ever used on mem1. Prove this using - ** the following assert(). If the assert() fails, it indicates a - ** memory leak and a need to call sqlite3VdbeMemRelease(&mem1). - */ - assert( mem1.zMalloc==0 ); - - /* rc==0 here means that one of the keys ran out of fields and - ** all the fields up to that point were equal. If the UNPACKED_INCRKEY - ** flag is set, then break the tie by treating key2 as larger. - ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes - ** are considered to be equal. Otherwise, the longer key is the - ** larger. As it happens, the pPKey2 will always be the longer - ** if there is a difference. - */ - assert( rc==0 ); - if( pPKey2->flags & UNPACKED_INCRKEY ){ - rc = -1; - }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){ - /* Leave rc==0 */ - }else if( idx1m.n) ){ - goto idx_rowid_corruption; - } - - /* The last field of the index should be an integer - the ROWID. - ** Verify that the last entry really is an integer. */ - (void)getVarint32((u8*)&m.z[szHdr-1], typeRowid); - testcase( typeRowid==1 ); - testcase( typeRowid==2 ); - testcase( typeRowid==3 ); - testcase( typeRowid==4 ); - testcase( typeRowid==5 ); - testcase( typeRowid==6 ); - testcase( typeRowid==8 ); - testcase( typeRowid==9 ); - if( unlikely(typeRowid<1 || typeRowid>9 || typeRowid==7) ){ - goto idx_rowid_corruption; - } - lenRowid = sqlite3VdbeSerialTypeLen(typeRowid); - testcase( (u32)m.n==szHdr+lenRowid ); - if( unlikely((u32)m.npCursor; - Mem m; - - assert( sqlite3BtreeCursorIsValid(pCur) ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey); - assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ - /* nCellKey will always be between 0 and 0xffffffff because of the say - ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ - if( nCellKey<=0 || nCellKey>0x7fffffff ){ - *res = 0; - return SQLITE_CORRUPT_BKPT; - } - memset(&m, 0, sizeof(m)); - rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m); - if( rc ){ - return rc; - } - assert( pUnpacked->flags & UNPACKED_PREFIX_MATCH ); - *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked); - sqlite3VdbeMemRelease(&m); - return SQLITE_OK; -} - -/* -** This routine sets the value to be returned by subsequent calls to -** sqlite3_changes() on the database handle 'db'. -*/ -SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *db, int nChange){ - assert( sqlite3_mutex_held(db->mutex) ); - db->nChange = nChange; - db->nTotalChange += nChange; -} - -/* -** Set a flag in the vdbe to update the change counter when it is finalised -** or reset. -*/ -SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe *v){ - v->changeCntOn = 1; -} - -/* -** Mark every prepared statement associated with a database connection -** as expired. -** -** An expired statement means that recompilation of the statement is -** recommend. Statements expire when things happen that make their -** programs obsolete. Removing user-defined functions or collating -** sequences, or changing an authorization function are the types of -** things that make prepared statements obsolete. -*/ -SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3 *db){ - Vdbe *p; - for(p = db->pVdbe; p; p=p->pNext){ - p->expired = 1; - } -} - -/* -** Return the database associated with the Vdbe. -*/ -SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe *v){ - return v->db; -} - -/* -** Return a pointer to an sqlite3_value structure containing the value bound -** parameter iVar of VM v. Except, if the value is an SQL NULL, return -** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_* -** constants) to the value before returning it. -** -** The returned value must be freed by the caller using sqlite3ValueFree(). -*/ -SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){ - assert( iVar>0 ); - if( v ){ - Mem *pMem = &v->aVar[iVar-1]; - if( 0==(pMem->flags & MEM_Null) ){ - sqlite3_value *pRet = sqlite3ValueNew(v->db); - if( pRet ){ - sqlite3VdbeMemCopy((Mem *)pRet, pMem); - sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8); - sqlite3VdbeMemStoreType((Mem *)pRet); - } - return pRet; - } - } - return 0; -} - -/* -** Configure SQL variable iVar so that binding a new value to it signals -** to sqlite3_reoptimize() that re-preparing the statement may result -** in a better query plan. -*/ -SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ - assert( iVar>0 ); - if( iVar>32 ){ - v->expmask = 0xffffffff; - }else{ - v->expmask |= ((u32)1 << (iVar-1)); - } -} - -/************** End of vdbeaux.c *********************************************/ -/************** Begin file vdbeapi.c *****************************************/ -/* -** 2004 May 26 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code use to implement APIs that are part of the -** VDBE. -*/ - -#ifndef SQLITE_OMIT_DEPRECATED -/* -** Return TRUE (non-zero) of the statement supplied as an argument needs -** to be recompiled. A statement needs to be recompiled whenever the -** execution environment changes in a way that would alter the program -** that sqlite3_prepare() generates. For example, if new functions or -** collating sequences are registered or if an authorizer function is -** added or changed. -*/ -SQLITE_API int sqlite3_expired(sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe*)pStmt; - return p==0 || p->expired; -} -#endif - -/* -** Check on a Vdbe to make sure it has not been finalized. Log -** an error and return true if it has been finalized (or is otherwise -** invalid). Return false if it is ok. -*/ -static int vdbeSafety(Vdbe *p){ - if( p->db==0 ){ - sqlite3_log(SQLITE_MISUSE, "API called with finalized prepared statement"); - return 1; - }else{ - return 0; - } -} -static int vdbeSafetyNotNull(Vdbe *p){ - if( p==0 ){ - sqlite3_log(SQLITE_MISUSE, "API called with NULL prepared statement"); - return 1; - }else{ - return vdbeSafety(p); - } -} - -/* -** The following routine destroys a virtual machine that is created by -** the sqlite3_compile() routine. The integer returned is an SQLITE_ -** success/failure code that describes the result of executing the virtual -** machine. -** -** This routine sets the error code and string returned by -** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). -*/ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){ - int rc; - if( pStmt==0 ){ - /* IMPLEMENTATION-OF: R-57228-12904 Invoking sqlite3_finalize() on a NULL - ** pointer is a harmless no-op. */ - rc = SQLITE_OK; - }else{ - Vdbe *v = (Vdbe*)pStmt; - sqlite3 *db = v->db; - if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT; - sqlite3_mutex_enter(db->mutex); - rc = sqlite3VdbeFinalize(v); - rc = sqlite3ApiExit(db, rc); - sqlite3LeaveMutexAndCloseZombie(db); - } - return rc; -} - -/* -** Terminate the current execution of an SQL statement and reset it -** back to its starting state so that it can be reused. A success code from -** the prior execution is returned. -** -** This routine sets the error code and string returned by -** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). -*/ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){ - int rc; - if( pStmt==0 ){ - rc = SQLITE_OK; - }else{ - Vdbe *v = (Vdbe*)pStmt; - sqlite3_mutex_enter(v->db->mutex); - rc = sqlite3VdbeReset(v); - sqlite3VdbeRewind(v); - assert( (rc & (v->db->errMask))==rc ); - rc = sqlite3ApiExit(v->db, rc); - sqlite3_mutex_leave(v->db->mutex); - } - return rc; -} - -/* -** Set all the parameters in the compiled SQL statement to NULL. -*/ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ - int i; - int rc = SQLITE_OK; - Vdbe *p = (Vdbe*)pStmt; -#if SQLITE_THREADSAFE - sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex; -#endif - sqlite3_mutex_enter(mutex); - for(i=0; inVar; i++){ - sqlite3VdbeMemRelease(&p->aVar[i]); - p->aVar[i].flags = MEM_Null; - } - if( p->isPrepareV2 && p->expmask ){ - p->expired = 1; - } - sqlite3_mutex_leave(mutex); - return rc; -} - - -/**************************** sqlite3_value_ ******************************* -** The following routines extract information from a Mem or sqlite3_value -** structure. -*/ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ - Mem *p = (Mem*)pVal; - if( p->flags & (MEM_Blob|MEM_Str) ){ - sqlite3VdbeMemExpandBlob(p); - p->flags &= ~MEM_Str; - p->flags |= MEM_Blob; - return p->n ? p->z : 0; - }else{ - return sqlite3_value_text(pVal); - } -} -SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){ - return sqlite3ValueBytes(pVal, SQLITE_UTF8); -} -SQLITE_API int sqlite3_value_bytes16(sqlite3_value *pVal){ - return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE); -} -SQLITE_API double sqlite3_value_double(sqlite3_value *pVal){ - return sqlite3VdbeRealValue((Mem*)pVal); -} -SQLITE_API int sqlite3_value_int(sqlite3_value *pVal){ - return (int)sqlite3VdbeIntValue((Mem*)pVal); -} -SQLITE_API sqlite_int64 sqlite3_value_int64(sqlite3_value *pVal){ - return sqlite3VdbeIntValue((Mem*)pVal); -} -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ - return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_value_text16(sqlite3_value* pVal){ - return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); -} -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value *pVal){ - return sqlite3ValueText(pVal, SQLITE_UTF16BE); -} -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value *pVal){ - return sqlite3ValueText(pVal, SQLITE_UTF16LE); -} -#endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){ - return pVal->type; -} - -/**************************** sqlite3_result_ ******************************* -** The following routines are used by user-defined functions to specify -** the function result. -** -** The setStrOrError() funtion calls sqlite3VdbeMemSetStr() to store the -** result as a string or blob but if the string or blob is too large, it -** then sets the error code to SQLITE_TOOBIG -*/ -static void setResultStrOrError( - sqlite3_context *pCtx, /* Function context */ - const char *z, /* String pointer */ - int n, /* Bytes in string, or negative */ - u8 enc, /* Encoding of z. 0 for BLOBs */ - void (*xDel)(void*) /* Destructor function */ -){ - if( sqlite3VdbeMemSetStr(&pCtx->s, z, n, enc, xDel)==SQLITE_TOOBIG ){ - sqlite3_result_error_toobig(pCtx); - } -} -SQLITE_API void sqlite3_result_blob( - sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - assert( n>=0 ); - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - setResultStrOrError(pCtx, z, n, 0, xDel); -} -SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetDouble(&pCtx->s, rVal); -} -SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - pCtx->isError = SQLITE_ERROR; - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - pCtx->isError = SQLITE_ERROR; - sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); -} -#endif -SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetInt64(&pCtx->s, (i64)iVal); -} -SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetInt64(&pCtx->s, iVal); -} -SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetNull(&pCtx->s); -} -SQLITE_API void sqlite3_result_text( - sqlite3_context *pCtx, - const char *z, - int n, - void (*xDel)(void *) -){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API void sqlite3_result_text16( - sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel); -} -SQLITE_API void sqlite3_result_text16be( - sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel); -} -SQLITE_API void sqlite3_result_text16le( - sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel); -} -#endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemCopy(&pCtx->s, pValue); -} -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetZeroBlob(&pCtx->s, n); -} -SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ - pCtx->isError = errCode; - if( pCtx->s.flags & MEM_Null ){ - sqlite3VdbeMemSetStr(&pCtx->s, sqlite3ErrStr(errCode), -1, - SQLITE_UTF8, SQLITE_STATIC); - } -} - -/* Force an SQLITE_TOOBIG error. */ -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - pCtx->isError = SQLITE_TOOBIG; - sqlite3VdbeMemSetStr(&pCtx->s, "string or blob too big", -1, - SQLITE_UTF8, SQLITE_STATIC); -} - -/* An SQLITE_NOMEM error. */ -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - sqlite3VdbeMemSetNull(&pCtx->s); - pCtx->isError = SQLITE_NOMEM; - pCtx->s.db->mallocFailed = 1; -} - -/* -** This function is called after a transaction has been committed. It -** invokes callbacks registered with sqlite3_wal_hook() as required. -*/ -static int doWalCallbacks(sqlite3 *db){ - int rc = SQLITE_OK; -#ifndef SQLITE_OMIT_WAL - int i; - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); - if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){ - rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry); - } - } - } -#endif - return rc; -} - -/* -** Execute the statement pStmt, either until a row of data is ready, the -** statement is completely executed or an error occurs. -** -** This routine implements the bulk of the logic behind the sqlite_step() -** API. The only thing omitted is the automatic recompile if a -** schema change has occurred. That detail is handled by the -** outer sqlite3_step() wrapper procedure. -*/ -static int sqlite3Step(Vdbe *p){ - sqlite3 *db; - int rc; - - assert(p); - if( p->magic!=VDBE_MAGIC_RUN ){ - /* We used to require that sqlite3_reset() be called before retrying - ** sqlite3_step() after any error or after SQLITE_DONE. But beginning - ** with version 3.7.0, we changed this so that sqlite3_reset() would - ** be called automatically instead of throwing the SQLITE_MISUSE error. - ** This "automatic-reset" change is not technically an incompatibility, - ** since any application that receives an SQLITE_MISUSE is broken by - ** definition. - ** - ** Nevertheless, some published applications that were originally written - ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE - ** returns, and those were broken by the automatic-reset change. As a - ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the - ** legacy behavior of returning SQLITE_MISUSE for cases where the - ** previous sqlite3_step() returned something other than a SQLITE_LOCKED - ** or SQLITE_BUSY error. - */ -#ifdef SQLITE_OMIT_AUTORESET - if( p->rc==SQLITE_BUSY || p->rc==SQLITE_LOCKED ){ - sqlite3_reset((sqlite3_stmt*)p); - }else{ - return SQLITE_MISUSE_BKPT; - } -#else - sqlite3_reset((sqlite3_stmt*)p); -#endif - } - - /* Check that malloc() has not failed. If it has, return early. */ - db = p->db; - if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM; - return SQLITE_NOMEM; - } - - if( p->pc<=0 && p->expired ){ - p->rc = SQLITE_SCHEMA; - rc = SQLITE_ERROR; - goto end_of_step; - } - if( p->pc<0 ){ - /* If there are no other statements currently running, then - ** reset the interrupt flag. This prevents a call to sqlite3_interrupt - ** from interrupting a statement that has not yet started. - */ - if( db->activeVdbeCnt==0 ){ - db->u1.isInterrupted = 0; - } - - assert( db->writeVdbeCnt>0 || db->autoCommit==0 || db->nDeferredCons==0 ); - -#ifndef SQLITE_OMIT_TRACE - if( db->xProfile && !db->init.busy ){ - sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); - } -#endif - - db->activeVdbeCnt++; - if( p->readOnly==0 ) db->writeVdbeCnt++; - p->pc = 0; - } -#ifndef SQLITE_OMIT_EXPLAIN - if( p->explain ){ - rc = sqlite3VdbeList(p); - }else -#endif /* SQLITE_OMIT_EXPLAIN */ - { - db->vdbeExecCnt++; - rc = sqlite3VdbeExec(p); - db->vdbeExecCnt--; - } - -#ifndef SQLITE_OMIT_TRACE - /* Invoke the profile callback if there is one - */ - if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->zSql ){ - sqlite3_int64 iNow; - sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); - db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000); - } -#endif - - if( rc==SQLITE_DONE ){ - assert( p->rc==SQLITE_OK ); - p->rc = doWalCallbacks(db); - if( p->rc!=SQLITE_OK ){ - rc = SQLITE_ERROR; - } - } - - db->errCode = rc; - if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){ - p->rc = SQLITE_NOMEM; - } -end_of_step: - /* At this point local variable rc holds the value that should be - ** returned if this statement was compiled using the legacy - ** sqlite3_prepare() interface. According to the docs, this can only - ** be one of the values in the first assert() below. Variable p->rc - ** contains the value that would be returned if sqlite3_finalize() - ** were called on statement p. - */ - assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR - || rc==SQLITE_BUSY || rc==SQLITE_MISUSE - ); - assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE ); - if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ - /* If this statement was prepared using sqlite3_prepare_v2(), and an - ** error has occurred, then return the error code in p->rc to the - ** caller. Set the error code in the database handle to the same value. - */ - rc = sqlite3VdbeTransferError(p); - } - return (rc&db->errMask); -} - -/* -** The maximum number of times that a statement will try to reparse -** itself before giving up and returning SQLITE_SCHEMA. -*/ -#ifndef SQLITE_MAX_SCHEMA_RETRY -# define SQLITE_MAX_SCHEMA_RETRY 5 -#endif - -/* -** This is the top-level implementation of sqlite3_step(). Call -** sqlite3Step() to do most of the work. If a schema error occurs, -** call sqlite3Reprepare() and try again. -*/ -SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ - int rc = SQLITE_OK; /* Result from sqlite3Step() */ - int rc2 = SQLITE_OK; /* Result from sqlite3Reprepare() */ - Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */ - int cnt = 0; /* Counter to prevent infinite loop of reprepares */ - sqlite3 *db; /* The database connection */ - - if( vdbeSafetyNotNull(v) ){ - return SQLITE_MISUSE_BKPT; - } - db = v->db; - sqlite3_mutex_enter(db->mutex); - v->doingRerun = 0; - while( (rc = sqlite3Step(v))==SQLITE_SCHEMA - && cnt++ < SQLITE_MAX_SCHEMA_RETRY - && (rc2 = rc = sqlite3Reprepare(v))==SQLITE_OK ){ - sqlite3_reset(pStmt); - v->doingRerun = 1; - assert( v->expired==0 ); - } - if( rc2!=SQLITE_OK && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){ - /* This case occurs after failing to recompile an sql statement. - ** The error message from the SQL compiler has already been loaded - ** into the database handle. This block copies the error message - ** from the database handle into the statement and sets the statement - ** program counter to 0 to ensure that when the statement is - ** finalized or reset the parser error message is available via - ** sqlite3_errmsg() and sqlite3_errcode(). - */ - const char *zErr = (const char *)sqlite3_value_text(db->pErr); - sqlite3DbFree(db, v->zErrMsg); - if( !db->mallocFailed ){ - v->zErrMsg = sqlite3DbStrDup(db, zErr); - v->rc = rc2; - } else { - v->zErrMsg = 0; - v->rc = rc = SQLITE_NOMEM; - } - } - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/* -** Extract the user data from a sqlite3_context structure and return a -** pointer to it. -*/ -SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ - assert( p && p->pFunc ); - return p->pFunc->pUserData; -} - -/* -** Extract the user data from a sqlite3_context structure and return a -** pointer to it. -** -** IMPLEMENTATION-OF: R-46798-50301 The sqlite3_context_db_handle() interface -** returns a copy of the pointer to the database connection (the 1st -** parameter) of the sqlite3_create_function() and -** sqlite3_create_function16() routines that originally registered the -** application defined function. -*/ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ - assert( p && p->pFunc ); - return p->s.db; -} - -/* -** The following is the implementation of an SQL function that always -** fails with an error message stating that the function is used in the -** wrong context. The sqlite3_overload_function() API might construct -** SQL function that use this routine so that the functions will exist -** for name resolution but are actually overloaded by the xFindFunction -** method of virtual tables. -*/ -SQLITE_PRIVATE void sqlite3InvalidFunction( - sqlite3_context *context, /* The function calling context */ - int NotUsed, /* Number of arguments to the function */ - sqlite3_value **NotUsed2 /* Value of each argument */ -){ - const char *zName = context->pFunc->zName; - char *zErr; - UNUSED_PARAMETER2(NotUsed, NotUsed2); - zErr = sqlite3_mprintf( - "unable to use function %s in the requested context", zName); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); -} - -/* -** Allocate or return the aggregate context for a user function. A new -** context is allocated on the first call. Subsequent calls return the -** same context that was returned on prior calls. -*/ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ - Mem *pMem; - assert( p && p->pFunc && p->pFunc->xStep ); - assert( sqlite3_mutex_held(p->s.db->mutex) ); - pMem = p->pMem; - testcase( nByte<0 ); - if( (pMem->flags & MEM_Agg)==0 ){ - if( nByte<=0 ){ - sqlite3VdbeMemReleaseExternal(pMem); - pMem->flags = MEM_Null; - pMem->z = 0; - }else{ - sqlite3VdbeMemGrow(pMem, nByte, 0); - pMem->flags = MEM_Agg; - pMem->u.pDef = p->pFunc; - if( pMem->z ){ - memset(pMem->z, 0, nByte); - } - } - } - return (void*)pMem->z; -} - -/* -** Return the auxilary data pointer, if any, for the iArg'th argument to -** the user-function defined by pCtx. -*/ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ - VdbeFunc *pVdbeFunc; - - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - pVdbeFunc = pCtx->pVdbeFunc; - if( !pVdbeFunc || iArg>=pVdbeFunc->nAux || iArg<0 ){ - return 0; - } - return pVdbeFunc->apAux[iArg].pAux; -} - -/* -** Set the auxilary data pointer and delete function, for the iArg'th -** argument to the user-function defined by pCtx. Any previous value is -** deleted by calling the delete function specified when it was set. -*/ -SQLITE_API void sqlite3_set_auxdata( - sqlite3_context *pCtx, - int iArg, - void *pAux, - void (*xDelete)(void*) -){ - struct AuxData *pAuxData; - VdbeFunc *pVdbeFunc; - if( iArg<0 ) goto failed; - - assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); - pVdbeFunc = pCtx->pVdbeFunc; - if( !pVdbeFunc || pVdbeFunc->nAux<=iArg ){ - int nAux = (pVdbeFunc ? pVdbeFunc->nAux : 0); - int nMalloc = sizeof(VdbeFunc) + sizeof(struct AuxData)*iArg; - pVdbeFunc = sqlite3DbRealloc(pCtx->s.db, pVdbeFunc, nMalloc); - if( !pVdbeFunc ){ - goto failed; - } - pCtx->pVdbeFunc = pVdbeFunc; - memset(&pVdbeFunc->apAux[nAux], 0, sizeof(struct AuxData)*(iArg+1-nAux)); - pVdbeFunc->nAux = iArg+1; - pVdbeFunc->pFunc = pCtx->pFunc; - } - - pAuxData = &pVdbeFunc->apAux[iArg]; - if( pAuxData->pAux && pAuxData->xDelete ){ - pAuxData->xDelete(pAuxData->pAux); - } - pAuxData->pAux = pAux; - pAuxData->xDelete = xDelete; - return; - -failed: - if( xDelete ){ - xDelete(pAux); - } -} - -#ifndef SQLITE_OMIT_DEPRECATED -/* -** Return the number of times the Step function of a aggregate has been -** called. -** -** This function is deprecated. Do not use it for new code. It is -** provide only to avoid breaking legacy code. New aggregate function -** implementations should keep their own counts within their aggregate -** context. -*/ -SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){ - assert( p && p->pMem && p->pFunc && p->pFunc->xStep ); - return p->pMem->n; -} -#endif - -/* -** Return the number of columns in the result set for the statement pStmt. -*/ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ - Vdbe *pVm = (Vdbe *)pStmt; - return pVm ? pVm->nResColumn : 0; -} - -/* -** Return the number of values available from the current row of the -** currently executing statement pStmt. -*/ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){ - Vdbe *pVm = (Vdbe *)pStmt; - if( pVm==0 || pVm->pResultSet==0 ) return 0; - return pVm->nResColumn; -} - - -/* -** Check to see if column iCol of the given statement is valid. If -** it is, return a pointer to the Mem for the value of that column. -** If iCol is not valid, return a pointer to a Mem which has a value -** of NULL. -*/ -static Mem *columnMem(sqlite3_stmt *pStmt, int i){ - Vdbe *pVm; - Mem *pOut; - - pVm = (Vdbe *)pStmt; - if( pVm && pVm->pResultSet!=0 && inResColumn && i>=0 ){ - sqlite3_mutex_enter(pVm->db->mutex); - pOut = &pVm->pResultSet[i]; - }else{ - /* If the value passed as the second argument is out of range, return - ** a pointer to the following static Mem object which contains the - ** value SQL NULL. Even though the Mem structure contains an element - ** of type i64, on certain architectures (x86) with certain compiler - ** switches (-Os), gcc may align this Mem object on a 4-byte boundary - ** instead of an 8-byte one. This all works fine, except that when - ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s - ** that a Mem structure is located on an 8-byte boundary. To prevent - ** these assert()s from failing, when building with SQLITE_DEBUG defined - ** using gcc, we force nullMem to be 8-byte aligned using the magical - ** __attribute__((aligned(8))) macro. */ - static const Mem nullMem -#if defined(SQLITE_DEBUG) && defined(__GNUC__) - __attribute__((aligned(8))) -#endif - = {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0, -#ifdef SQLITE_DEBUG - 0, 0, /* pScopyFrom, pFiller */ -#endif - 0, 0 }; - - if( pVm && ALWAYS(pVm->db) ){ - sqlite3_mutex_enter(pVm->db->mutex); - sqlite3Error(pVm->db, SQLITE_RANGE, 0); - } - pOut = (Mem*)&nullMem; - } - return pOut; -} - -/* -** This function is called after invoking an sqlite3_value_XXX function on a -** column value (i.e. a value returned by evaluating an SQL expression in the -** select list of a SELECT statement) that may cause a malloc() failure. If -** malloc() has failed, the threads mallocFailed flag is cleared and the result -** code of statement pStmt set to SQLITE_NOMEM. -** -** Specifically, this is called from within: -** -** sqlite3_column_int() -** sqlite3_column_int64() -** sqlite3_column_text() -** sqlite3_column_text16() -** sqlite3_column_real() -** sqlite3_column_bytes() -** sqlite3_column_bytes16() -** sqiite3_column_blob() -*/ -static void columnMallocFailure(sqlite3_stmt *pStmt) -{ - /* If malloc() failed during an encoding conversion within an - ** sqlite3_column_XXX API, then set the return code of the statement to - ** SQLITE_NOMEM. The next call to _step() (if any) will return SQLITE_ERROR - ** and _finalize() will return NOMEM. - */ - Vdbe *p = (Vdbe *)pStmt; - if( p ){ - p->rc = sqlite3ApiExit(p->db, p->rc); - sqlite3_mutex_leave(p->db->mutex); - } -} - -/**************************** sqlite3_column_ ******************************* -** The following routines are used to access elements of the current row -** in the result set. -*/ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){ - const void *val; - val = sqlite3_value_blob( columnMem(pStmt,i) ); - /* Even though there is no encoding conversion, value_blob() might - ** need to call malloc() to expand the result of a zeroblob() - ** expression. - */ - columnMallocFailure(pStmt); - return val; -} -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){ - int val = sqlite3_value_bytes( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){ - int val = sqlite3_value_bytes16( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API double sqlite3_column_double(sqlite3_stmt *pStmt, int i){ - double val = sqlite3_value_double( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API int sqlite3_column_int(sqlite3_stmt *pStmt, int i){ - int val = sqlite3_value_int( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API sqlite_int64 sqlite3_column_int64(sqlite3_stmt *pStmt, int i){ - sqlite_int64 val = sqlite3_value_int64( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){ - const unsigned char *val = sqlite3_value_text( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){ - Mem *pOut = columnMem(pStmt, i); - if( pOut->flags&MEM_Static ){ - pOut->flags &= ~MEM_Static; - pOut->flags |= MEM_Ephem; - } - columnMallocFailure(pStmt); - return (sqlite3_value *)pOut; -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){ - const void *val = sqlite3_value_text16( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -#endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ - int iType = sqlite3_value_type( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return iType; -} - -/* The following function is experimental and subject to change or -** removal */ -/*int sqlite3_column_numeric_type(sqlite3_stmt *pStmt, int i){ -** return sqlite3_value_numeric_type( columnMem(pStmt,i) ); -**} -*/ - -/* -** Convert the N-th element of pStmt->pColName[] into a string using -** xFunc() then return that string. If N is out of range, return 0. -** -** There are up to 5 names for each column. useType determines which -** name is returned. Here are the names: -** -** 0 The column name as it should be displayed for output -** 1 The datatype name for the column -** 2 The name of the database that the column derives from -** 3 The name of the table that the column derives from -** 4 The name of the table column that the result column derives from -** -** If the result is not a simple column reference (if it is an expression -** or a constant) then useTypes 2, 3, and 4 return NULL. -*/ -static const void *columnName( - sqlite3_stmt *pStmt, - int N, - const void *(*xFunc)(Mem*), - int useType -){ - const void *ret = 0; - Vdbe *p = (Vdbe *)pStmt; - int n; - sqlite3 *db = p->db; - - assert( db!=0 ); - n = sqlite3_column_count(pStmt); - if( N=0 ){ - N += useType*n; - sqlite3_mutex_enter(db->mutex); - assert( db->mallocFailed==0 ); - ret = xFunc(&p->aColName[N]); - /* A malloc may have failed inside of the xFunc() call. If this - ** is the case, clear the mallocFailed flag and return NULL. - */ - if( db->mallocFailed ){ - db->mallocFailed = 0; - ret = 0; - } - sqlite3_mutex_leave(db->mutex); - } - return ret; -} - -/* -** Return the name of the Nth column of the result set returned by SQL -** statement pStmt. -*/ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_NAME); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_NAME); -} -#endif - -/* -** Constraint: If you have ENABLE_COLUMN_METADATA then you must -** not define OMIT_DECLTYPE. -*/ -#if defined(SQLITE_OMIT_DECLTYPE) && defined(SQLITE_ENABLE_COLUMN_METADATA) -# error "Must not define both SQLITE_OMIT_DECLTYPE \ - and SQLITE_ENABLE_COLUMN_METADATA" -#endif - -#ifndef SQLITE_OMIT_DECLTYPE -/* -** Return the column declaration type (if applicable) of the 'i'th column -** of the result set of SQL statement pStmt. -*/ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DECLTYPE); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DECLTYPE); -} -#endif /* SQLITE_OMIT_UTF16 */ -#endif /* SQLITE_OMIT_DECLTYPE */ - -#ifdef SQLITE_ENABLE_COLUMN_METADATA -/* -** Return the name of the database from which a result column derives. -** NULL is returned if the result column is an expression or constant or -** anything else which is not an unabiguous reference to a database column. -*/ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DATABASE); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DATABASE); -} -#endif /* SQLITE_OMIT_UTF16 */ - -/* -** Return the name of the table from which a result column derives. -** NULL is returned if the result column is an expression or constant or -** anything else which is not an unabiguous reference to a database column. -*/ -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_TABLE); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_TABLE); -} -#endif /* SQLITE_OMIT_UTF16 */ - -/* -** Return the name of the table column from which a result column derives. -** NULL is returned if the result column is an expression or constant or -** anything else which is not an unabiguous reference to a database column. -*/ -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_COLUMN); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ - return columnName( - pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_COLUMN); -} -#endif /* SQLITE_OMIT_UTF16 */ -#endif /* SQLITE_ENABLE_COLUMN_METADATA */ - - -/******************************* sqlite3_bind_ *************************** -** -** Routines used to attach values to wildcards in a compiled SQL statement. -*/ -/* -** Unbind the value bound to variable i in virtual machine p. This is the -** the same as binding a NULL value to the column. If the "i" parameter is -** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK. -** -** A successful evaluation of this routine acquires the mutex on p. -** the mutex is released if any kind of error occurs. -** -** The error code stored in database p->db is overwritten with the return -** value in any case. -*/ -static int vdbeUnbind(Vdbe *p, int i){ - Mem *pVar; - if( vdbeSafetyNotNull(p) ){ - return SQLITE_MISUSE_BKPT; - } - sqlite3_mutex_enter(p->db->mutex); - if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){ - sqlite3Error(p->db, SQLITE_MISUSE, 0); - sqlite3_mutex_leave(p->db->mutex); - sqlite3_log(SQLITE_MISUSE, - "bind on a busy prepared statement: [%s]", p->zSql); - return SQLITE_MISUSE_BKPT; - } - if( i<1 || i>p->nVar ){ - sqlite3Error(p->db, SQLITE_RANGE, 0); - sqlite3_mutex_leave(p->db->mutex); - return SQLITE_RANGE; - } - i--; - pVar = &p->aVar[i]; - sqlite3VdbeMemRelease(pVar); - pVar->flags = MEM_Null; - sqlite3Error(p->db, SQLITE_OK, 0); - - /* If the bit corresponding to this variable in Vdbe.expmask is set, then - ** binding a new value to this variable invalidates the current query plan. - ** - ** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host - ** parameter in the WHERE clause might influence the choice of query plan - ** for a statement, then the statement will be automatically recompiled, - ** as if there had been a schema change, on the first sqlite3_step() call - ** following any change to the bindings of that parameter. - */ - if( p->isPrepareV2 && - ((i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff) - ){ - p->expired = 1; - } - return SQLITE_OK; -} - -/* -** Bind a text or BLOB value. -*/ -static int bindText( - sqlite3_stmt *pStmt, /* The statement to bind against */ - int i, /* Index of the parameter to bind */ - const void *zData, /* Pointer to the data to be bound */ - int nData, /* Number of bytes of data to be bound */ - void (*xDel)(void*), /* Destructor for the data */ - u8 encoding /* Encoding for the data */ -){ - Vdbe *p = (Vdbe *)pStmt; - Mem *pVar; - int rc; - - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - if( zData!=0 ){ - pVar = &p->aVar[i-1]; - rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); - if( rc==SQLITE_OK && encoding!=0 ){ - rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); - } - sqlite3Error(p->db, rc, 0); - rc = sqlite3ApiExit(p->db, rc); - } - sqlite3_mutex_leave(p->db->mutex); - }else if( xDel!=SQLITE_STATIC && xDel!=SQLITE_TRANSIENT ){ - xDel((void*)zData); - } - return rc; -} - - -/* -** Bind a blob value to an SQL statement variable. -*/ -SQLITE_API int sqlite3_bind_blob( - sqlite3_stmt *pStmt, - int i, - const void *zData, - int nData, - void (*xDel)(void*) -){ - return bindText(pStmt, i, zData, nData, xDel, 0); -} -SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ - int rc; - Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); - sqlite3_mutex_leave(p->db->mutex); - } - return rc; -} -SQLITE_API int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){ - return sqlite3_bind_int64(p, i, (i64)iValue); -} -SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ - int rc; - Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); - sqlite3_mutex_leave(p->db->mutex); - } - return rc; -} -SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ - int rc; - Vdbe *p = (Vdbe*)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlite3_mutex_leave(p->db->mutex); - } - return rc; -} -SQLITE_API int sqlite3_bind_text( - sqlite3_stmt *pStmt, - int i, - const char *zData, - int nData, - void (*xDel)(void*) -){ - return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API int sqlite3_bind_text16( - sqlite3_stmt *pStmt, - int i, - const void *zData, - int nData, - void (*xDel)(void*) -){ - return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE); -} -#endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ - int rc; - switch( pValue->type ){ - case SQLITE_INTEGER: { - rc = sqlite3_bind_int64(pStmt, i, pValue->u.i); - break; - } - case SQLITE_FLOAT: { - rc = sqlite3_bind_double(pStmt, i, pValue->r); - break; - } - case SQLITE_BLOB: { - if( pValue->flags & MEM_Zero ){ - rc = sqlite3_bind_zeroblob(pStmt, i, pValue->u.nZero); - }else{ - rc = sqlite3_bind_blob(pStmt, i, pValue->z, pValue->n,SQLITE_TRANSIENT); - } - break; - } - case SQLITE_TEXT: { - rc = bindText(pStmt,i, pValue->z, pValue->n, SQLITE_TRANSIENT, - pValue->enc); - break; - } - default: { - rc = sqlite3_bind_null(pStmt, i); - break; - } - } - return rc; -} -SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ - int rc; - Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); - sqlite3_mutex_leave(p->db->mutex); - } - return rc; -} - -/* -** Return the number of wildcards that can be potentially bound to. -** This routine is added to support DBD::SQLite. -*/ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe*)pStmt; - return p ? p->nVar : 0; -} - -/* -** Return the name of a wildcard parameter. Return NULL if the index -** is out of range or if the wildcard is unnamed. -** -** The result is always UTF-8. -*/ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ - Vdbe *p = (Vdbe*)pStmt; - if( p==0 || i<1 || i>p->nzVar ){ - return 0; - } - return p->azVar[i-1]; -} - -/* -** Given a wildcard parameter name, return the index of the variable -** with that name. If there is no variable with the given name, -** return 0. -*/ -SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nName){ - int i; - if( p==0 ){ - return 0; - } - if( zName ){ - for(i=0; inzVar; i++){ - const char *z = p->azVar[i]; - if( z && strncmp(z,zName,nName)==0 && z[nName]==0 ){ - return i+1; - } - } - } - return 0; -} -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ - return sqlite3VdbeParameterIndex((Vdbe*)pStmt, zName, sqlite3Strlen30(zName)); -} - -/* -** Transfer all bindings from the first statement over to the second. -*/ -SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ - Vdbe *pFrom = (Vdbe*)pFromStmt; - Vdbe *pTo = (Vdbe*)pToStmt; - int i; - assert( pTo->db==pFrom->db ); - assert( pTo->nVar==pFrom->nVar ); - sqlite3_mutex_enter(pTo->db->mutex); - for(i=0; inVar; i++){ - sqlite3VdbeMemMove(&pTo->aVar[i], &pFrom->aVar[i]); - } - sqlite3_mutex_leave(pTo->db->mutex); - return SQLITE_OK; -} - -#ifndef SQLITE_OMIT_DEPRECATED -/* -** Deprecated external interface. Internal/core SQLite code -** should call sqlite3TransferBindings. -** -** Is is misuse to call this routine with statements from different -** database connections. But as this is a deprecated interface, we -** will not bother to check for that condition. -** -** If the two statements contain a different number of bindings, then -** an SQLITE_ERROR is returned. Nothing else can go wrong, so otherwise -** SQLITE_OK is returned. -*/ -SQLITE_API int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ - Vdbe *pFrom = (Vdbe*)pFromStmt; - Vdbe *pTo = (Vdbe*)pToStmt; - if( pFrom->nVar!=pTo->nVar ){ - return SQLITE_ERROR; - } - if( pTo->isPrepareV2 && pTo->expmask ){ - pTo->expired = 1; - } - if( pFrom->isPrepareV2 && pFrom->expmask ){ - pFrom->expired = 1; - } - return sqlite3TransferBindings(pFromStmt, pToStmt); -} -#endif - -/* -** Return the sqlite3* database handle to which the prepared statement given -** in the argument belongs. This is the same database handle that was -** the first argument to the sqlite3_prepare() that was used to create -** the statement in the first place. -*/ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){ - return pStmt ? ((Vdbe*)pStmt)->db : 0; -} - -/* -** Return true if the prepared statement is guaranteed to not modify the -** database. -*/ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ - return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; -} - -/* -** Return true if the prepared statement is in need of being reset. -*/ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ - Vdbe *v = (Vdbe*)pStmt; - return v!=0 && v->pc>0 && v->magic==VDBE_MAGIC_RUN; -} - -/* -** Return a pointer to the next prepared statement after pStmt associated -** with database connection pDb. If pStmt is NULL, return the first -** prepared statement for the database connection. Return NULL if there -** are no more. -*/ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ - sqlite3_stmt *pNext; - sqlite3_mutex_enter(pDb->mutex); - if( pStmt==0 ){ - pNext = (sqlite3_stmt*)pDb->pVdbe; - }else{ - pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pNext; - } - sqlite3_mutex_leave(pDb->mutex); - return pNext; -} - -/* -** Return the value of a status counter for a prepared statement -*/ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ - Vdbe *pVdbe = (Vdbe*)pStmt; - int v = pVdbe->aCounter[op-1]; - if( resetFlag ) pVdbe->aCounter[op-1] = 0; - return v; -} - -/************** End of vdbeapi.c *********************************************/ -/************** Begin file vdbetrace.c ***************************************/ -/* -** 2009 November 25 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code used to insert the values of host parameters -** (aka "wildcards") into the SQL text output by sqlite3_trace(). -** -** The Vdbe parse-tree explainer is also found here. -*/ - -#ifndef SQLITE_OMIT_TRACE - -/* -** zSql is a zero-terminated string of UTF-8 SQL text. Return the number of -** bytes in this text up to but excluding the first character in -** a host parameter. If the text contains no host parameters, return -** the total number of bytes in the text. -*/ -static int findNextHostParameter(const char *zSql, int *pnToken){ - int tokenType; - int nTotal = 0; - int n; - - *pnToken = 0; - while( zSql[0] ){ - n = sqlite3GetToken((u8*)zSql, &tokenType); - assert( n>0 && tokenType!=TK_ILLEGAL ); - if( tokenType==TK_VARIABLE ){ - *pnToken = n; - break; - } - nTotal += n; - zSql += n; - } - return nTotal; -} - -/* -** This function returns a pointer to a nul-terminated string in memory -** obtained from sqlite3DbMalloc(). If sqlite3.vdbeExecCnt is 1, then the -** string contains a copy of zRawSql but with host parameters expanded to -** their current bindings. Or, if sqlite3.vdbeExecCnt is greater than 1, -** then the returned string holds a copy of zRawSql with "-- " prepended -** to each line of text. -** -** The calling function is responsible for making sure the memory returned -** is eventually freed. -** -** ALGORITHM: Scan the input string looking for host parameters in any of -** these forms: ?, ?N, $A, @A, :A. Take care to avoid text within -** string literals, quoted identifier names, and comments. For text forms, -** the host parameter index is found by scanning the perpared -** statement for the corresponding OP_Variable opcode. Once the host -** parameter index is known, locate the value in p->aVar[]. Then render -** the value as a literal in place of the host parameter name. -*/ -SQLITE_PRIVATE char *sqlite3VdbeExpandSql( - Vdbe *p, /* The prepared statement being evaluated */ - const char *zRawSql /* Raw text of the SQL statement */ -){ - sqlite3 *db; /* The database connection */ - int idx = 0; /* Index of a host parameter */ - int nextIndex = 1; /* Index of next ? host parameter */ - int n; /* Length of a token prefix */ - int nToken; /* Length of the parameter token */ - int i; /* Loop counter */ - Mem *pVar; /* Value of a host parameter */ - StrAccum out; /* Accumulate the output here */ - char zBase[100]; /* Initial working space */ - - db = p->db; - sqlite3StrAccumInit(&out, zBase, sizeof(zBase), - db->aLimit[SQLITE_LIMIT_LENGTH]); - out.db = db; - if( db->vdbeExecCnt>1 ){ - while( *zRawSql ){ - const char *zStart = zRawSql; - while( *(zRawSql++)!='\n' && *zRawSql ); - sqlite3StrAccumAppend(&out, "-- ", 3); - sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart)); - } - }else{ - while( zRawSql[0] ){ - n = findNextHostParameter(zRawSql, &nToken); - assert( n>0 ); - sqlite3StrAccumAppend(&out, zRawSql, n); - zRawSql += n; - assert( zRawSql[0] || nToken==0 ); - if( nToken==0 ) break; - if( zRawSql[0]=='?' ){ - if( nToken>1 ){ - assert( sqlite3Isdigit(zRawSql[1]) ); - sqlite3GetInt32(&zRawSql[1], &idx); - }else{ - idx = nextIndex; - } - }else{ - assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' ); - testcase( zRawSql[0]==':' ); - testcase( zRawSql[0]=='$' ); - testcase( zRawSql[0]=='@' ); - idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken); - assert( idx>0 ); - } - zRawSql += nToken; - nextIndex = idx + 1; - assert( idx>0 && idx<=p->nVar ); - pVar = &p->aVar[idx-1]; - if( pVar->flags & MEM_Null ){ - sqlite3StrAccumAppend(&out, "NULL", 4); - }else if( pVar->flags & MEM_Int ){ - sqlite3XPrintf(&out, "%lld", pVar->u.i); - }else if( pVar->flags & MEM_Real ){ - sqlite3XPrintf(&out, "%!.15g", pVar->r); - }else if( pVar->flags & MEM_Str ){ -#ifndef SQLITE_OMIT_UTF16 - u8 enc = ENC(db); - if( enc!=SQLITE_UTF8 ){ - Mem utf8; - memset(&utf8, 0, sizeof(utf8)); - utf8.db = db; - sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC); - sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8); - sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z); - sqlite3VdbeMemRelease(&utf8); - }else -#endif - { - sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z); - } - }else if( pVar->flags & MEM_Zero ){ - sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero); - }else{ - assert( pVar->flags & MEM_Blob ); - sqlite3StrAccumAppend(&out, "x'", 2); - for(i=0; in; i++){ - sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff); - } - sqlite3StrAccumAppend(&out, "'", 1); - } - } - } - return sqlite3StrAccumFinish(&out); -} - -#endif /* #ifndef SQLITE_OMIT_TRACE */ - -/***************************************************************************** -** The following code implements the data-structure explaining logic -** for the Vdbe. -*/ - -#if defined(SQLITE_ENABLE_TREE_EXPLAIN) - -/* -** Allocate a new Explain object -*/ -SQLITE_PRIVATE void sqlite3ExplainBegin(Vdbe *pVdbe){ - if( pVdbe ){ - Explain *p; - sqlite3BeginBenignMalloc(); - p = (Explain *)sqlite3MallocZero( sizeof(Explain) ); - if( p ){ - p->pVdbe = pVdbe; - sqlite3_free(pVdbe->pExplain); - pVdbe->pExplain = p; - sqlite3StrAccumInit(&p->str, p->zBase, sizeof(p->zBase), - SQLITE_MAX_LENGTH); - p->str.useMalloc = 2; - }else{ - sqlite3EndBenignMalloc(); - } - } -} - -/* -** Return true if the Explain ends with a new-line. -*/ -static int endsWithNL(Explain *p){ - return p && p->str.zText && p->str.nChar - && p->str.zText[p->str.nChar-1]=='\n'; -} - -/* -** Append text to the indentation -*/ -SQLITE_PRIVATE void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){ - Explain *p; - if( pVdbe && (p = pVdbe->pExplain)!=0 ){ - va_list ap; - if( p->nIndent && endsWithNL(p) ){ - int n = p->nIndent; - if( n>ArraySize(p->aIndent) ) n = ArraySize(p->aIndent); - sqlite3AppendSpace(&p->str, p->aIndent[n-1]); - } - va_start(ap, zFormat); - sqlite3VXPrintf(&p->str, 1, zFormat, ap); - va_end(ap); - } -} - -/* -** Append a '\n' if there is not already one. -*/ -SQLITE_PRIVATE void sqlite3ExplainNL(Vdbe *pVdbe){ - Explain *p; - if( pVdbe && (p = pVdbe->pExplain)!=0 && !endsWithNL(p) ){ - sqlite3StrAccumAppend(&p->str, "\n", 1); - } -} - -/* -** Push a new indentation level. Subsequent lines will be indented -** so that they begin at the current cursor position. -*/ -SQLITE_PRIVATE void sqlite3ExplainPush(Vdbe *pVdbe){ - Explain *p; - if( pVdbe && (p = pVdbe->pExplain)!=0 ){ - if( p->str.zText && p->nIndentaIndent) ){ - const char *z = p->str.zText; - int i = p->str.nChar-1; - int x; - while( i>=0 && z[i]!='\n' ){ i--; } - x = (p->str.nChar - 1) - i; - if( p->nIndent && xaIndent[p->nIndent-1] ){ - x = p->aIndent[p->nIndent-1]; - } - p->aIndent[p->nIndent] = x; - } - p->nIndent++; - } -} - -/* -** Pop the indentation stack by one level. -*/ -SQLITE_PRIVATE void sqlite3ExplainPop(Vdbe *p){ - if( p && p->pExplain ) p->pExplain->nIndent--; -} - -/* -** Free the indentation structure -*/ -SQLITE_PRIVATE void sqlite3ExplainFinish(Vdbe *pVdbe){ - if( pVdbe && pVdbe->pExplain ){ - sqlite3_free(pVdbe->zExplain); - sqlite3ExplainNL(pVdbe); - pVdbe->zExplain = sqlite3StrAccumFinish(&pVdbe->pExplain->str); - sqlite3_free(pVdbe->pExplain); - pVdbe->pExplain = 0; - sqlite3EndBenignMalloc(); - } -} - -/* -** Return the explanation of a virtual machine. -*/ -SQLITE_PRIVATE const char *sqlite3VdbeExplanation(Vdbe *pVdbe){ - return (pVdbe && pVdbe->zExplain) ? pVdbe->zExplain : 0; -} -#endif /* defined(SQLITE_DEBUG) */ - -/************** End of vdbetrace.c *******************************************/ -/************** Begin file vdbe.c ********************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** The code in this file implements execution method of the -** Virtual Database Engine (VDBE). A separate file ("vdbeaux.c") -** handles housekeeping details such as creating and deleting -** VDBE instances. This file is solely interested in executing -** the VDBE program. -** -** In the external interface, an "sqlite3_stmt*" is an opaque pointer -** to a VDBE. -** -** The SQL parser generates a program which is then executed by -** the VDBE to do the work of the SQL statement. VDBE programs are -** similar in form to assembly language. The program consists of -** a linear sequence of operations. Each operation has an opcode -** and 5 operands. Operands P1, P2, and P3 are integers. Operand P4 -** is a null-terminated string. Operand P5 is an unsigned character. -** Few opcodes use all 5 operands. -** -** Computation results are stored on a set of registers numbered beginning -** with 1 and going up to Vdbe.nMem. Each register can store -** either an integer, a null-terminated string, a floating point -** number, or the SQL "NULL" value. An implicit conversion from one -** type to the other occurs as necessary. -** -** Most of the code in this file is taken up by the sqlite3VdbeExec() -** function which does the work of interpreting a VDBE program. -** But other routines are also provided to help in building up -** a program instruction by instruction. -** -** Various scripts scan this source file in order to generate HTML -** documentation, headers files, or other derived files. The formatting -** of the code in this file is, therefore, important. See other comments -** in this file for details. If in doubt, do not deviate from existing -** commenting and indentation practices when changing or adding code. -*/ - -/* -** Invoke this macro on memory cells just prior to changing the -** value of the cell. This macro verifies that shallow copies are -** not misused. -*/ -#ifdef SQLITE_DEBUG -# define memAboutToChange(P,M) sqlite3VdbeMemAboutToChange(P,M) -#else -# define memAboutToChange(P,M) -#endif - -/* -** The following global variable is incremented every time a cursor -** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test -** procedures use this information to make sure that indices are -** working correctly. This variable has no function other than to -** help verify the correct operation of the library. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_search_count = 0; -#endif - -/* -** When this global variable is positive, it gets decremented once before -** each instruction in the VDBE. When it reaches zero, the u1.isInterrupted -** field of the sqlite3 structure is set in order to simulate an interrupt. -** -** This facility is used for testing purposes only. It does not function -** in an ordinary build. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_interrupt_count = 0; -#endif - -/* -** The next global variable is incremented each type the OP_Sort opcode -** is executed. The test procedures use this information to make sure that -** sorting is occurring or not occurring at appropriate times. This variable -** has no function other than to help verify the correct operation of the -** library. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_sort_count = 0; -#endif - -/* -** The next global variable records the size of the largest MEM_Blob -** or MEM_Str that has been used by a VDBE opcode. The test procedures -** use this information to make sure that the zero-blob functionality -** is working correctly. This variable has no function other than to -** help verify the correct operation of the library. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_max_blobsize = 0; -static void updateMaxBlobsize(Mem *p){ - if( (p->flags & (MEM_Str|MEM_Blob))!=0 && p->n>sqlite3_max_blobsize ){ - sqlite3_max_blobsize = p->n; - } -} -#endif - -/* -** The next global variable is incremented each type the OP_Found opcode -** is executed. This is used to test whether or not the foreign key -** operation implemented using OP_FkIsZero is working. This variable -** has no function other than to help verify the correct operation of the -** library. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_found_count = 0; -#endif - -/* -** Test a register to see if it exceeds the current maximum blob size. -** If it does, record the new maximum blob size. -*/ -#if defined(SQLITE_TEST) && !defined(SQLITE_OMIT_BUILTIN_TEST) -# define UPDATE_MAX_BLOBSIZE(P) updateMaxBlobsize(P) -#else -# define UPDATE_MAX_BLOBSIZE(P) -#endif - -/* -** Convert the given register into a string if it isn't one -** already. Return non-zero if a malloc() fails. -*/ -#define Stringify(P, enc) \ - if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlite3VdbeMemStringify(P,enc)) \ - { goto no_mem; } - -/* -** An ephemeral string value (signified by the MEM_Ephem flag) contains -** a pointer to a dynamically allocated string where some other entity -** is responsible for deallocating that string. Because the register -** does not control the string, it might be deleted without the register -** knowing it. -** -** This routine converts an ephemeral string into a dynamically allocated -** string that the register itself controls. In other words, it -** converts an MEM_Ephem string into an MEM_Dyn string. -*/ -#define Deephemeralize(P) \ - if( ((P)->flags&MEM_Ephem)!=0 \ - && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;} - -/* Return true if the cursor was opened using the OP_OpenSorter opcode. */ -# define isSorter(x) ((x)->pSorter!=0) - -/* -** Argument pMem points at a register that will be passed to a -** user-defined function or returned to the user as the result of a query. -** This routine sets the pMem->type variable used by the sqlite3_value_*() -** routines. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem){ - int flags = pMem->flags; - if( flags & MEM_Null ){ - pMem->type = SQLITE_NULL; - } - else if( flags & MEM_Int ){ - pMem->type = SQLITE_INTEGER; - } - else if( flags & MEM_Real ){ - pMem->type = SQLITE_FLOAT; - } - else if( flags & MEM_Str ){ - pMem->type = SQLITE_TEXT; - }else{ - pMem->type = SQLITE_BLOB; - } -} - -/* -** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL -** if we run out of memory. -*/ -static VdbeCursor *allocateCursor( - Vdbe *p, /* The virtual machine */ - int iCur, /* Index of the new VdbeCursor */ - int nField, /* Number of fields in the table or index */ - int iDb, /* Database the cursor belongs to, or -1 */ - int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */ -){ - /* Find the memory cell that will be used to store the blob of memory - ** required for this VdbeCursor structure. It is convenient to use a - ** vdbe memory cell to manage the memory allocation required for a - ** VdbeCursor structure for the following reasons: - ** - ** * Sometimes cursor numbers are used for a couple of different - ** purposes in a vdbe program. The different uses might require - ** different sized allocations. Memory cells provide growable - ** allocations. - ** - ** * When using ENABLE_MEMORY_MANAGEMENT, memory cell buffers can - ** be freed lazily via the sqlite3_release_memory() API. This - ** minimizes the number of malloc calls made by the system. - ** - ** Memory cells for cursors are allocated at the top of the address - ** space. Memory cell (p->nMem) corresponds to cursor 0. Space for - ** cursor 1 is managed by memory cell (p->nMem-1), etc. - */ - Mem *pMem = &p->aMem[p->nMem-iCur]; - - int nByte; - VdbeCursor *pCx = 0; - nByte = - ROUND8(sizeof(VdbeCursor)) + - (isBtreeCursor?sqlite3BtreeCursorSize():0) + - 2*nField*sizeof(u32); - - assert( iCurnCursor ); - if( p->apCsr[iCur] ){ - sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); - p->apCsr[iCur] = 0; - } - if( SQLITE_OK==sqlite3VdbeMemGrow(pMem, nByte, 0) ){ - p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; - memset(pCx, 0, sizeof(VdbeCursor)); - pCx->iDb = iDb; - pCx->nField = nField; - if( nField ){ - pCx->aType = (u32 *)&pMem->z[ROUND8(sizeof(VdbeCursor))]; - } - if( isBtreeCursor ){ - pCx->pCursor = (BtCursor*) - &pMem->z[ROUND8(sizeof(VdbeCursor))+2*nField*sizeof(u32)]; - sqlite3BtreeCursorZero(pCx->pCursor); - } - } - return pCx; -} - -/* -** Try to convert a value into a numeric representation if we can -** do so without loss of information. In other words, if the string -** looks like a number, convert it into a number. If it does not -** look like a number, leave it alone. -*/ -static void applyNumericAffinity(Mem *pRec){ - if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){ - double rValue; - i64 iValue; - u8 enc = pRec->enc; - if( (pRec->flags&MEM_Str)==0 ) return; - if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return; - if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){ - pRec->u.i = iValue; - pRec->flags |= MEM_Int; - }else{ - pRec->r = rValue; - pRec->flags |= MEM_Real; - } - } -} - -/* -** Processing is determine by the affinity parameter: -** -** SQLITE_AFF_INTEGER: -** SQLITE_AFF_REAL: -** SQLITE_AFF_NUMERIC: -** Try to convert pRec to an integer representation or a -** floating-point representation if an integer representation -** is not possible. Note that the integer representation is -** always preferred, even if the affinity is REAL, because -** an integer representation is more space efficient on disk. -** -** SQLITE_AFF_TEXT: -** Convert pRec to a text representation. -** -** SQLITE_AFF_NONE: -** No-op. pRec is unchanged. -*/ -static void applyAffinity( - Mem *pRec, /* The value to apply affinity to */ - char affinity, /* The affinity to be applied */ - u8 enc /* Use this text encoding */ -){ - if( affinity==SQLITE_AFF_TEXT ){ - /* Only attempt the conversion to TEXT if there is an integer or real - ** representation (blob and NULL do not get converted) but no string - ** representation. - */ - if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){ - sqlite3VdbeMemStringify(pRec, enc); - } - pRec->flags &= ~(MEM_Real|MEM_Int); - }else if( affinity!=SQLITE_AFF_NONE ){ - assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL - || affinity==SQLITE_AFF_NUMERIC ); - applyNumericAffinity(pRec); - if( pRec->flags & MEM_Real ){ - sqlite3VdbeIntegerAffinity(pRec); - } - } -} - -/* -** Try to convert the type of a function argument or a result column -** into a numeric representation. Use either INTEGER or REAL whichever -** is appropriate. But only do the conversion if it is possible without -** loss of information and return the revised type of the argument. -*/ -SQLITE_API int sqlite3_value_numeric_type(sqlite3_value *pVal){ - Mem *pMem = (Mem*)pVal; - if( pMem->type==SQLITE_TEXT ){ - applyNumericAffinity(pMem); - sqlite3VdbeMemStoreType(pMem); - } - return pMem->type; -} - -/* -** Exported version of applyAffinity(). This one works on sqlite3_value*, -** not the internal Mem* type. -*/ -SQLITE_PRIVATE void sqlite3ValueApplyAffinity( - sqlite3_value *pVal, - u8 affinity, - u8 enc -){ - applyAffinity((Mem *)pVal, affinity, enc); -} - -#ifdef SQLITE_DEBUG -/* -** Write a nice string representation of the contents of cell pMem -** into buffer zBuf, length nBuf. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){ - char *zCsr = zBuf; - int f = pMem->flags; - - static const char *const encnames[] = {"(X)", "(8)", "(16LE)", "(16BE)"}; - - if( f&MEM_Blob ){ - int i; - char c; - if( f & MEM_Dyn ){ - c = 'z'; - assert( (f & (MEM_Static|MEM_Ephem))==0 ); - }else if( f & MEM_Static ){ - c = 't'; - assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); - }else if( f & MEM_Ephem ){ - c = 'e'; - assert( (f & (MEM_Static|MEM_Dyn))==0 ); - }else{ - c = 's'; - } - - sqlite3_snprintf(100, zCsr, "%c", c); - zCsr += sqlite3Strlen30(zCsr); - sqlite3_snprintf(100, zCsr, "%d[", pMem->n); - zCsr += sqlite3Strlen30(zCsr); - for(i=0; i<16 && in; i++){ - sqlite3_snprintf(100, zCsr, "%02X", ((int)pMem->z[i] & 0xFF)); - zCsr += sqlite3Strlen30(zCsr); - } - for(i=0; i<16 && in; i++){ - char z = pMem->z[i]; - if( z<32 || z>126 ) *zCsr++ = '.'; - else *zCsr++ = z; - } - - sqlite3_snprintf(100, zCsr, "]%s", encnames[pMem->enc]); - zCsr += sqlite3Strlen30(zCsr); - if( f & MEM_Zero ){ - sqlite3_snprintf(100, zCsr,"+%dz",pMem->u.nZero); - zCsr += sqlite3Strlen30(zCsr); - } - *zCsr = '\0'; - }else if( f & MEM_Str ){ - int j, k; - zBuf[0] = ' '; - if( f & MEM_Dyn ){ - zBuf[1] = 'z'; - assert( (f & (MEM_Static|MEM_Ephem))==0 ); - }else if( f & MEM_Static ){ - zBuf[1] = 't'; - assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); - }else if( f & MEM_Ephem ){ - zBuf[1] = 'e'; - assert( (f & (MEM_Static|MEM_Dyn))==0 ); - }else{ - zBuf[1] = 's'; - } - k = 2; - sqlite3_snprintf(100, &zBuf[k], "%d", pMem->n); - k += sqlite3Strlen30(&zBuf[k]); - zBuf[k++] = '['; - for(j=0; j<15 && jn; j++){ - u8 c = pMem->z[j]; - if( c>=0x20 && c<0x7f ){ - zBuf[k++] = c; - }else{ - zBuf[k++] = '.'; - } - } - zBuf[k++] = ']'; - sqlite3_snprintf(100,&zBuf[k], encnames[pMem->enc]); - k += sqlite3Strlen30(&zBuf[k]); - zBuf[k++] = 0; - } -} -#endif - -#ifdef SQLITE_DEBUG -/* -** Print the value of a register for tracing purposes: -*/ -static void memTracePrint(FILE *out, Mem *p){ - if( p->flags & MEM_Invalid ){ - fprintf(out, " undefined"); - }else if( p->flags & MEM_Null ){ - fprintf(out, " NULL"); - }else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){ - fprintf(out, " si:%lld", p->u.i); - }else if( p->flags & MEM_Int ){ - fprintf(out, " i:%lld", p->u.i); -#ifndef SQLITE_OMIT_FLOATING_POINT - }else if( p->flags & MEM_Real ){ - fprintf(out, " r:%g", p->r); -#endif - }else if( p->flags & MEM_RowSet ){ - fprintf(out, " (rowset)"); - }else{ - char zBuf[200]; - sqlite3VdbeMemPrettyPrint(p, zBuf); - fprintf(out, " "); - fprintf(out, "%s", zBuf); - } -} -static void registerTrace(FILE *out, int iReg, Mem *p){ - fprintf(out, "REG[%d] = ", iReg); - memTracePrint(out, p); - fprintf(out, "\n"); -} -#endif - -#ifdef SQLITE_DEBUG -# define REGISTER_TRACE(R,M) if(p->trace)registerTrace(p->trace,R,M) -#else -# define REGISTER_TRACE(R,M) -#endif - - -#ifdef VDBE_PROFILE - -/* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of vdbe.c *********************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains inline asm code for retrieving "high-performance" -** counters for x86 class CPUs. -*/ -#ifndef _HWTIME_H_ -#define _HWTIME_H_ - -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - -#elif (defined(__GNUC__) && defined(__x86_64__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } - -#elif (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } - -#else - - #error Need implementation of sqlite3Hwtime() for your platform. - - /* - ** To compile without implementing sqlite3Hwtime() for your platform, - ** you can remove the above #error and use the following - ** stub function. You will lose timing support for many - ** of the debugging and testing utilities, but it should at - ** least compile and run. - */ -SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(_HWTIME_H_) */ - -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in vdbe.c ***********************/ - -#endif - -/* -** The CHECK_FOR_INTERRUPT macro defined here looks to see if the -** sqlite3_interrupt() routine has been called. If it has been, then -** processing of the VDBE program is interrupted. -** -** This macro added to every instruction that does a jump in order to -** implement a loop. This test used to be on every single instruction, -** but that meant we more testing than we needed. By only testing the -** flag on jump instructions, we get a (small) speed improvement. -*/ -#define CHECK_FOR_INTERRUPT \ - if( db->u1.isInterrupted ) goto abort_due_to_interrupt; - - -#ifndef NDEBUG -/* -** This function is only called from within an assert() expression. It -** checks that the sqlite3.nTransaction variable is correctly set to -** the number of non-transaction savepoints currently in the -** linked list starting at sqlite3.pSavepoint. -** -** Usage: -** -** assert( checkSavepointCount(db) ); -*/ -static int checkSavepointCount(sqlite3 *db){ - int n = 0; - Savepoint *p; - for(p=db->pSavepoint; p; p=p->pNext) n++; - assert( n==(db->nSavepoint + db->isTransactionSavepoint) ); - return 1; -} -#endif - -/* -** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored -** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored -** in memory obtained from sqlite3DbMalloc). -*/ -static void importVtabErrMsg(Vdbe *p, sqlite3_vtab *pVtab){ - sqlite3 *db = p->db; - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg); - sqlite3_free(pVtab->zErrMsg); - pVtab->zErrMsg = 0; -} - - -/* -** Execute as much of a VDBE program as we can then return. -** -** sqlite3VdbeMakeReady() must be called before this routine in order to -** close the program with a final OP_Halt and to set up the callbacks -** and the error message pointer. -** -** Whenever a row or result data is available, this routine will either -** invoke the result callback (if there is one) or return with -** SQLITE_ROW. -** -** If an attempt is made to open a locked database, then this routine -** will either invoke the busy callback (if there is one) or it will -** return SQLITE_BUSY. -** -** If an error occurs, an error message is written to memory obtained -** from sqlite3_malloc() and p->zErrMsg is made to point to that memory. -** The error code is stored in p->rc and this routine returns SQLITE_ERROR. -** -** If the callback ever returns non-zero, then the program exits -** immediately. There will be no error message but the p->rc field is -** set to SQLITE_ABORT and this routine will return SQLITE_ERROR. -** -** A memory allocation error causes p->rc to be set to SQLITE_NOMEM and this -** routine to return SQLITE_ERROR. -** -** Other fatal errors return SQLITE_ERROR. -** -** After this routine has finished, sqlite3VdbeFinalize() should be -** used to clean up the mess that was left behind. -*/ -SQLITE_PRIVATE int sqlite3VdbeExec( - Vdbe *p /* The VDBE */ -){ - int pc=0; /* The program counter */ - Op *aOp = p->aOp; /* Copy of p->aOp */ - Op *pOp; /* Current operation */ - int rc = SQLITE_OK; /* Value to return */ - sqlite3 *db = p->db; /* The database */ - u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ - u8 encoding = ENC(db); /* The database encoding */ -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - int checkProgress; /* True if progress callbacks are enabled */ - int nProgressOps = 0; /* Opcodes executed since progress callback. */ -#endif - Mem *aMem = p->aMem; /* Copy of p->aMem */ - Mem *pIn1 = 0; /* 1st input operand */ - Mem *pIn2 = 0; /* 2nd input operand */ - Mem *pIn3 = 0; /* 3rd input operand */ - Mem *pOut = 0; /* Output operand */ - int iCompare = 0; /* Result of last OP_Compare operation */ - int *aPermute = 0; /* Permutation of columns for OP_Compare */ - i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */ -#ifdef VDBE_PROFILE - u64 start; /* CPU clock count at start of opcode */ - int origPc; /* Program counter at start of opcode */ -#endif - /******************************************************************** - ** Automatically generated code - ** - ** The following union is automatically generated by the - ** vdbe-compress.tcl script. The purpose of this union is to - ** reduce the amount of stack space required by this function. - ** See comments in the vdbe-compress.tcl script for details. - */ - union vdbeExecUnion { - struct OP_Yield_stack_vars { - int pcDest; - } aa; - struct OP_Null_stack_vars { - int cnt; - u16 nullFlag; - } ab; - struct OP_Variable_stack_vars { - Mem *pVar; /* Value being transferred */ - } ac; - struct OP_Move_stack_vars { - char *zMalloc; /* Holding variable for allocated memory */ - int n; /* Number of registers left to copy */ - int p1; /* Register to copy from */ - int p2; /* Register to copy to */ - } ad; - struct OP_Copy_stack_vars { - int n; - } ae; - struct OP_ResultRow_stack_vars { - Mem *pMem; - int i; - } af; - struct OP_Concat_stack_vars { - i64 nByte; - } ag; - struct OP_Remainder_stack_vars { - char bIntint; /* Started out as two integer operands */ - int flags; /* Combined MEM_* flags from both inputs */ - i64 iA; /* Integer value of left operand */ - i64 iB; /* Integer value of right operand */ - double rA; /* Real value of left operand */ - double rB; /* Real value of right operand */ - } ah; - struct OP_Function_stack_vars { - int i; - Mem *pArg; - sqlite3_context ctx; - sqlite3_value **apVal; - int n; - } ai; - struct OP_ShiftRight_stack_vars { - i64 iA; - u64 uA; - i64 iB; - u8 op; - } aj; - struct OP_Ge_stack_vars { - int res; /* Result of the comparison of pIn1 against pIn3 */ - char affinity; /* Affinity to use for comparison */ - u16 flags1; /* Copy of initial value of pIn1->flags */ - u16 flags3; /* Copy of initial value of pIn3->flags */ - } ak; - struct OP_Compare_stack_vars { - int n; - int i; - int p1; - int p2; - const KeyInfo *pKeyInfo; - int idx; - CollSeq *pColl; /* Collating sequence to use on this term */ - int bRev; /* True for DESCENDING sort order */ - } al; - struct OP_Or_stack_vars { - int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - } am; - struct OP_IfNot_stack_vars { - int c; - } an; - struct OP_Column_stack_vars { - u32 payloadSize; /* Number of bytes in the record */ - i64 payloadSize64; /* Number of bytes in the record */ - int p1; /* P1 value of the opcode */ - int p2; /* column number to retrieve */ - VdbeCursor *pC; /* The VDBE cursor */ - char *zRec; /* Pointer to complete record-data */ - BtCursor *pCrsr; /* The BTree cursor */ - u32 *aType; /* aType[i] holds the numeric type of the i-th column */ - u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ - int nField; /* number of fields in the record */ - int len; /* The length of the serialized data for the column */ - int i; /* Loop counter */ - char *zData; /* Part of the record being decoded */ - Mem *pDest; /* Where to write the extracted value */ - Mem sMem; /* For storing the record being decoded */ - u8 *zIdx; /* Index into header */ - u8 *zEndHdr; /* Pointer to first byte after the header */ - u32 offset; /* Offset into the data */ - u32 szField; /* Number of bytes in the content of a field */ - int szHdr; /* Size of the header size field at start of record */ - int avail; /* Number of bytes of available data */ - u32 t; /* A type code from the record header */ - Mem *pReg; /* PseudoTable input register */ - } ao; - struct OP_Affinity_stack_vars { - const char *zAffinity; /* The affinity to be applied */ - char cAff; /* A single character of affinity */ - } ap; - struct OP_MakeRecord_stack_vars { - u8 *zNewRecord; /* A buffer to hold the data for the new record */ - Mem *pRec; /* The new record */ - u64 nData; /* Number of bytes of data space */ - int nHdr; /* Number of bytes of header space */ - i64 nByte; /* Data space required for this record */ - int nZero; /* Number of zero bytes at the end of the record */ - int nVarint; /* Number of bytes in a varint */ - u32 serial_type; /* Type field */ - Mem *pData0; /* First field to be combined into the record */ - Mem *pLast; /* Last field of the record */ - int nField; /* Number of fields in the record */ - char *zAffinity; /* The affinity string for the record */ - int file_format; /* File format to use for encoding */ - int i; /* Space used in zNewRecord[] */ - int len; /* Length of a field */ - } aq; - struct OP_Count_stack_vars { - i64 nEntry; - BtCursor *pCrsr; - } ar; - struct OP_Savepoint_stack_vars { - int p1; /* Value of P1 operand */ - char *zName; /* Name of savepoint */ - int nName; - Savepoint *pNew; - Savepoint *pSavepoint; - Savepoint *pTmp; - int iSavepoint; - int ii; - } as; - struct OP_AutoCommit_stack_vars { - int desiredAutoCommit; - int iRollback; - int turnOnAC; - } at; - struct OP_Transaction_stack_vars { - Btree *pBt; - } au; - struct OP_ReadCookie_stack_vars { - int iMeta; - int iDb; - int iCookie; - } av; - struct OP_SetCookie_stack_vars { - Db *pDb; - } aw; - struct OP_VerifyCookie_stack_vars { - int iMeta; - int iGen; - Btree *pBt; - } ax; - struct OP_OpenWrite_stack_vars { - int nField; - KeyInfo *pKeyInfo; - int p2; - int iDb; - int wrFlag; - Btree *pX; - VdbeCursor *pCur; - Db *pDb; - } ay; - struct OP_OpenEphemeral_stack_vars { - VdbeCursor *pCx; - } az; - struct OP_SorterOpen_stack_vars { - VdbeCursor *pCx; - } ba; - struct OP_OpenPseudo_stack_vars { - VdbeCursor *pCx; - } bb; - struct OP_SeekGt_stack_vars { - int res; - int oc; - VdbeCursor *pC; - UnpackedRecord r; - int nField; - i64 iKey; /* The rowid we are to seek to */ - } bc; - struct OP_Seek_stack_vars { - VdbeCursor *pC; - } bd; - struct OP_Found_stack_vars { - int alreadyExists; - VdbeCursor *pC; - int res; - char *pFree; - UnpackedRecord *pIdxKey; - UnpackedRecord r; - char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; - } be; - struct OP_IsUnique_stack_vars { - u16 ii; - VdbeCursor *pCx; - BtCursor *pCrsr; - u16 nField; - Mem *aMx; - UnpackedRecord r; /* B-Tree index search key */ - i64 R; /* Rowid stored in register P3 */ - } bf; - struct OP_NotExists_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - u64 iKey; - } bg; - struct OP_NewRowid_stack_vars { - i64 v; /* The new rowid */ - VdbeCursor *pC; /* Cursor of table to get the new rowid */ - int res; /* Result of an sqlite3BtreeLast() */ - int cnt; /* Counter to limit the number of searches */ - Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ - VdbeFrame *pFrame; /* Root frame of VDBE */ - } bh; - struct OP_InsertInt_stack_vars { - Mem *pData; /* MEM cell holding data for the record to be inserted */ - Mem *pKey; /* MEM cell holding key for the record */ - i64 iKey; /* The integer ROWID or key for the record to be inserted */ - VdbeCursor *pC; /* Cursor to table into which insert is written */ - int nZero; /* Number of zero-bytes to append */ - int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ - const char *zDb; /* database name - used by the update hook */ - const char *zTbl; /* Table name - used by the opdate hook */ - int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ - } bi; - struct OP_Delete_stack_vars { - i64 iKey; - VdbeCursor *pC; - } bj; - struct OP_SorterCompare_stack_vars { - VdbeCursor *pC; - int res; - } bk; - struct OP_SorterData_stack_vars { - VdbeCursor *pC; - } bl; - struct OP_RowData_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - u32 n; - i64 n64; - } bm; - struct OP_Rowid_stack_vars { - VdbeCursor *pC; - i64 v; - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; - } bn; - struct OP_NullRow_stack_vars { - VdbeCursor *pC; - } bo; - struct OP_Last_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - } bp; - struct OP_Rewind_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - } bq; - struct OP_Next_stack_vars { - VdbeCursor *pC; - int res; - } br; - struct OP_IdxInsert_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int nKey; - const char *zKey; - } bs; - struct OP_IdxDelete_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - UnpackedRecord r; - } bt; - struct OP_IdxRowid_stack_vars { - BtCursor *pCrsr; - VdbeCursor *pC; - i64 rowid; - } bu; - struct OP_IdxGE_stack_vars { - VdbeCursor *pC; - int res; - UnpackedRecord r; - } bv; - struct OP_Destroy_stack_vars { - int iMoved; - int iCnt; - Vdbe *pVdbe; - int iDb; - } bw; - struct OP_Clear_stack_vars { - int nChange; - } bx; - struct OP_CreateTable_stack_vars { - int pgno; - int flags; - Db *pDb; - } by; - struct OP_ParseSchema_stack_vars { - int iDb; - const char *zMaster; - char *zSql; - InitData initData; - } bz; - struct OP_IntegrityCk_stack_vars { - int nRoot; /* Number of tables to check. (Number of root pages.) */ - int *aRoot; /* Array of rootpage numbers for tables to be checked */ - int j; /* Loop counter */ - int nErr; /* Number of errors reported */ - char *z; /* Text of the error report */ - Mem *pnErr; /* Register keeping track of errors remaining */ - } ca; - struct OP_RowSetRead_stack_vars { - i64 val; - } cb; - struct OP_RowSetTest_stack_vars { - int iSet; - int exists; - } cc; - struct OP_Program_stack_vars { - int nMem; /* Number of memory registers for sub-program */ - int nByte; /* Bytes of runtime space required for sub-program */ - Mem *pRt; /* Register to allocate runtime space */ - Mem *pMem; /* Used to iterate through memory cells */ - Mem *pEnd; /* Last memory cell in new array */ - VdbeFrame *pFrame; /* New vdbe frame to execute in */ - SubProgram *pProgram; /* Sub-program to execute */ - void *t; /* Token identifying trigger */ - } cd; - struct OP_Param_stack_vars { - VdbeFrame *pFrame; - Mem *pIn; - } ce; - struct OP_MemMax_stack_vars { - Mem *pIn1; - VdbeFrame *pFrame; - } cf; - struct OP_AggStep_stack_vars { - int n; - int i; - Mem *pMem; - Mem *pRec; - sqlite3_context ctx; - sqlite3_value **apVal; - } cg; - struct OP_AggFinal_stack_vars { - Mem *pMem; - } ch; - struct OP_Checkpoint_stack_vars { - int i; /* Loop counter */ - int aRes[3]; /* Results */ - Mem *pMem; /* Write results here */ - } ci; - struct OP_JournalMode_stack_vars { - Btree *pBt; /* Btree to change journal mode of */ - Pager *pPager; /* Pager associated with pBt */ - int eNew; /* New journal mode */ - int eOld; /* The old journal mode */ -#ifndef SQLITE_OMIT_WAL - const char *zFilename; /* Name of database file for pPager */ -#endif - } cj; - struct OP_IncrVacuum_stack_vars { - Btree *pBt; - } ck; - struct OP_VBegin_stack_vars { - VTable *pVTab; - } cl; - struct OP_VOpen_stack_vars { - VdbeCursor *pCur; - sqlite3_vtab_cursor *pVtabCursor; - sqlite3_vtab *pVtab; - sqlite3_module *pModule; - } cm; - struct OP_VFilter_stack_vars { - int nArg; - int iQuery; - const sqlite3_module *pModule; - Mem *pQuery; - Mem *pArgc; - sqlite3_vtab_cursor *pVtabCursor; - sqlite3_vtab *pVtab; - VdbeCursor *pCur; - int res; - int i; - Mem **apArg; - } cn; - struct OP_VColumn_stack_vars { - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; - Mem *pDest; - sqlite3_context sContext; - } co; - struct OP_VNext_stack_vars { - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; - int res; - VdbeCursor *pCur; - } cp; - struct OP_VRename_stack_vars { - sqlite3_vtab *pVtab; - Mem *pName; - } cq; - struct OP_VUpdate_stack_vars { - sqlite3_vtab *pVtab; - sqlite3_module *pModule; - int nArg; - int i; - sqlite_int64 rowid; - Mem **apArg; - Mem *pX; - } cr; - struct OP_Trace_stack_vars { - char *zTrace; - char *z; - } cs; - } u; - /* End automatically generated code - ********************************************************************/ - - assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ - sqlite3VdbeEnter(p); - if( p->rc==SQLITE_NOMEM ){ - /* This happens if a malloc() inside a call to sqlite3_column_text() or - ** sqlite3_column_text16() failed. */ - goto no_mem; - } - assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY ); - p->rc = SQLITE_OK; - assert( p->explain==0 ); - p->pResultSet = 0; - db->busyHandler.nBusy = 0; - CHECK_FOR_INTERRUPT; - sqlite3VdbeIOTraceSql(p); -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - checkProgress = db->xProgress!=0; -#endif -#ifdef SQLITE_DEBUG - sqlite3BeginBenignMalloc(); - if( p->pc==0 && (p->db->flags & SQLITE_VdbeListing)!=0 ){ - int i; - printf("VDBE Program Listing:\n"); - sqlite3VdbePrintSql(p); - for(i=0; inOp; i++){ - sqlite3VdbePrintOp(stdout, i, &aOp[i]); - } - } - sqlite3EndBenignMalloc(); -#endif - for(pc=p->pc; rc==SQLITE_OK; pc++){ - assert( pc>=0 && pcnOp ); - if( db->mallocFailed ) goto no_mem; -#ifdef VDBE_PROFILE - origPc = pc; - start = sqlite3Hwtime(); -#endif - pOp = &aOp[pc]; - - /* Only allow tracing if SQLITE_DEBUG is defined. - */ -#ifdef SQLITE_DEBUG - if( p->trace ){ - if( pc==0 ){ - printf("VDBE Execution Trace:\n"); - sqlite3VdbePrintSql(p); - } - sqlite3VdbePrintOp(p->trace, pc, pOp); - } -#endif - - - /* Check to see if we need to simulate an interrupt. This only happens - ** if we have a special test build. - */ -#ifdef SQLITE_TEST - if( sqlite3_interrupt_count>0 ){ - sqlite3_interrupt_count--; - if( sqlite3_interrupt_count==0 ){ - sqlite3_interrupt(db); - } - } -#endif - -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - /* Call the progress callback if it is configured and the required number - ** of VDBE ops have been executed (either since this invocation of - ** sqlite3VdbeExec() or since last time the progress callback was called). - ** If the progress callback returns non-zero, exit the virtual machine with - ** a return code SQLITE_ABORT. - */ - if( checkProgress ){ - if( db->nProgressOps==nProgressOps ){ - int prc; - prc = db->xProgress(db->pProgressArg); - if( prc!=0 ){ - rc = SQLITE_INTERRUPT; - goto vdbe_error_halt; - } - nProgressOps = 0; - } - nProgressOps++; - } -#endif - - /* On any opcode with the "out2-prerelease" tag, free any - ** external allocations out of mem[p2] and set mem[p2] to be - ** an undefined integer. Opcodes will either fill in the integer - ** value or convert mem[p2] to a different type. - */ - assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); - if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ - assert( pOp->p2>0 ); - assert( pOp->p2<=p->nMem ); - pOut = &aMem[pOp->p2]; - memAboutToChange(p, pOut); - VdbeMemRelease(pOut); - pOut->flags = MEM_Int; - } - - /* Sanity checking on other operands */ -#ifdef SQLITE_DEBUG - if( (pOp->opflags & OPFLG_IN1)!=0 ){ - assert( pOp->p1>0 ); - assert( pOp->p1<=p->nMem ); - assert( memIsValid(&aMem[pOp->p1]) ); - REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); - } - if( (pOp->opflags & OPFLG_IN2)!=0 ){ - assert( pOp->p2>0 ); - assert( pOp->p2<=p->nMem ); - assert( memIsValid(&aMem[pOp->p2]) ); - REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); - } - if( (pOp->opflags & OPFLG_IN3)!=0 ){ - assert( pOp->p3>0 ); - assert( pOp->p3<=p->nMem ); - assert( memIsValid(&aMem[pOp->p3]) ); - REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); - } - if( (pOp->opflags & OPFLG_OUT2)!=0 ){ - assert( pOp->p2>0 ); - assert( pOp->p2<=p->nMem ); - memAboutToChange(p, &aMem[pOp->p2]); - } - if( (pOp->opflags & OPFLG_OUT3)!=0 ){ - assert( pOp->p3>0 ); - assert( pOp->p3<=p->nMem ); - memAboutToChange(p, &aMem[pOp->p3]); - } -#endif - - switch( pOp->opcode ){ - -/***************************************************************************** -** What follows is a massive switch statement where each case implements a -** separate instruction in the virtual machine. If we follow the usual -** indentation conventions, each case should be indented by 6 spaces. But -** that is a lot of wasted space on the left margin. So the code within -** the switch statement will break with convention and be flush-left. Another -** big comment (similar to this one) will mark the point in the code where -** we transition back to normal indentation. -** -** The formatting of each case is important. The makefile for SQLite -** generates two C files "opcodes.h" and "opcodes.c" by scanning this -** file looking for lines that begin with "case OP_". The opcodes.h files -** will be filled with #defines that give unique integer values to each -** opcode and the opcodes.c file is filled with an array of strings where -** each string is the symbolic name for the corresponding opcode. If the -** case statement is followed by a comment of the form "/# same as ... #/" -** that comment is used to determine the particular value of the opcode. -** -** Other keywords in the comment that follows each case are used to -** construct the OPFLG_INITIALIZER value that initializes opcodeProperty[]. -** Keywords include: in1, in2, in3, out2_prerelease, out2, out3. See -** the mkopcodeh.awk script for additional information. -** -** Documentation about VDBE opcodes is generated by scanning this file -** for lines of that contain "Opcode:". That line and all subsequent -** comment lines are used in the generation of the opcode.html documentation -** file. -** -** SUMMARY: -** -** Formatting is important to scripts that scan this file. -** Do not deviate from the formatting style currently in use. -** -*****************************************************************************/ - -/* Opcode: Goto * P2 * * * -** -** An unconditional jump to address P2. -** The next instruction executed will be -** the one at index P2 from the beginning of -** the program. -*/ -case OP_Goto: { /* jump */ - CHECK_FOR_INTERRUPT; - pc = pOp->p2 - 1; - break; -} - -/* Opcode: Gosub P1 P2 * * * -** -** Write the current address onto register P1 -** and then jump to address P2. -*/ -case OP_Gosub: { /* jump */ - assert( pOp->p1>0 && pOp->p1<=p->nMem ); - pIn1 = &aMem[pOp->p1]; - assert( (pIn1->flags & MEM_Dyn)==0 ); - memAboutToChange(p, pIn1); - pIn1->flags = MEM_Int; - pIn1->u.i = pc; - REGISTER_TRACE(pOp->p1, pIn1); - pc = pOp->p2 - 1; - break; -} - -/* Opcode: Return P1 * * * * -** -** Jump to the next instruction after the address in register P1. -*/ -case OP_Return: { /* in1 */ - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags & MEM_Int ); - pc = (int)pIn1->u.i; - break; -} - -/* Opcode: Yield P1 * * * * -** -** Swap the program counter with the value in register P1. -*/ -case OP_Yield: { /* in1 */ -#if 0 /* local variables moved into u.aa */ - int pcDest; -#endif /* local variables moved into u.aa */ - pIn1 = &aMem[pOp->p1]; - assert( (pIn1->flags & MEM_Dyn)==0 ); - pIn1->flags = MEM_Int; - u.aa.pcDest = (int)pIn1->u.i; - pIn1->u.i = pc; - REGISTER_TRACE(pOp->p1, pIn1); - pc = u.aa.pcDest; - break; -} - -/* Opcode: HaltIfNull P1 P2 P3 P4 * -** -** Check the value in register P3. If it is NULL then Halt using -** parameter P1, P2, and P4 as if this were a Halt instruction. If the -** value in register P3 is not NULL, then this routine is a no-op. -*/ -case OP_HaltIfNull: { /* in3 */ - pIn3 = &aMem[pOp->p3]; - if( (pIn3->flags & MEM_Null)==0 ) break; - /* Fall through into OP_Halt */ -} - -/* Opcode: Halt P1 P2 * P4 * -** -** Exit immediately. All open cursors, etc are closed -** automatically. -** -** P1 is the result code returned by sqlite3_exec(), sqlite3_reset(), -** or sqlite3_finalize(). For a normal halt, this should be SQLITE_OK (0). -** For errors, it can be some other value. If P1!=0 then P2 will determine -** whether or not to rollback the current transaction. Do not rollback -** if P2==OE_Fail. Do the rollback if P2==OE_Rollback. If P2==OE_Abort, -** then back out all changes that have occurred during this execution of the -** VDBE, but do not rollback the transaction. -** -** If P4 is not null then it is an error message string. -** -** There is an implied "Halt 0 0 0" instruction inserted at the very end of -** every program. So a jump past the last instruction of the program -** is the same as executing Halt. -*/ -case OP_Halt: { - if( pOp->p1==SQLITE_OK && p->pFrame ){ - /* Halt the sub-program. Return control to the parent frame. */ - VdbeFrame *pFrame = p->pFrame; - p->pFrame = pFrame->pParent; - p->nFrame--; - sqlite3VdbeSetChanges(db, p->nChange); - pc = sqlite3VdbeFrameRestore(pFrame); - lastRowid = db->lastRowid; - if( pOp->p2==OE_Ignore ){ - /* Instruction pc is the OP_Program that invoked the sub-program - ** currently being halted. If the p2 instruction of this OP_Halt - ** instruction is set to OE_Ignore, then the sub-program is throwing - ** an IGNORE exception. In this case jump to the address specified - ** as the p2 of the calling OP_Program. */ - pc = p->aOp[pc].p2-1; - } - aOp = p->aOp; - aMem = p->aMem; - break; - } - - p->rc = pOp->p1; - p->errorAction = (u8)pOp->p2; - p->pc = pc; - if( pOp->p4.z ){ - assert( p->rc!=SQLITE_OK ); - sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z); - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pc, p->zSql, pOp->p4.z); - }else if( p->rc ){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(pOp->p1, "constraint failed at %d in [%s]", pc, p->zSql); - } - rc = sqlite3VdbeHalt(p); - assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); - if( rc==SQLITE_BUSY ){ - p->rc = rc = SQLITE_BUSY; - }else{ - assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ); - assert( rc==SQLITE_OK || db->nDeferredCons>0 ); - rc = p->rc ? SQLITE_ERROR : SQLITE_DONE; - } - goto vdbe_return; -} - -/* Opcode: Integer P1 P2 * * * -** -** The 32-bit integer value P1 is written into register P2. -*/ -case OP_Integer: { /* out2-prerelease */ - pOut->u.i = pOp->p1; - break; -} - -/* Opcode: Int64 * P2 * P4 * -** -** P4 is a pointer to a 64-bit integer value. -** Write that value into register P2. -*/ -case OP_Int64: { /* out2-prerelease */ - assert( pOp->p4.pI64!=0 ); - pOut->u.i = *pOp->p4.pI64; - break; -} - -#ifndef SQLITE_OMIT_FLOATING_POINT -/* Opcode: Real * P2 * P4 * -** -** P4 is a pointer to a 64-bit floating point value. -** Write that value into register P2. -*/ -case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ - pOut->flags = MEM_Real; - assert( !sqlite3IsNaN(*pOp->p4.pReal) ); - pOut->r = *pOp->p4.pReal; - break; -} -#endif - -/* Opcode: String8 * P2 * P4 * -** -** P4 points to a nul terminated UTF-8 string. This opcode is transformed -** into an OP_String before it is executed for the first time. -*/ -case OP_String8: { /* same as TK_STRING, out2-prerelease */ - assert( pOp->p4.z!=0 ); - pOp->opcode = OP_String; - pOp->p1 = sqlite3Strlen30(pOp->p4.z); - -#ifndef SQLITE_OMIT_UTF16 - if( encoding!=SQLITE_UTF8 ){ - rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC); - if( rc==SQLITE_TOOBIG ) goto too_big; - if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem; - assert( pOut->zMalloc==pOut->z ); - assert( pOut->flags & MEM_Dyn ); - pOut->zMalloc = 0; - pOut->flags |= MEM_Static; - pOut->flags &= ~MEM_Dyn; - if( pOp->p4type==P4_DYNAMIC ){ - sqlite3DbFree(db, pOp->p4.z); - } - pOp->p4type = P4_DYNAMIC; - pOp->p4.z = pOut->z; - pOp->p1 = pOut->n; - } -#endif - if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - /* Fall through to the next case, OP_String */ -} - -/* Opcode: String P1 P2 * P4 * -** -** The string value P4 of length P1 (bytes) is stored in register P2. -*/ -case OP_String: { /* out2-prerelease */ - assert( pOp->p4.z!=0 ); - pOut->flags = MEM_Str|MEM_Static|MEM_Term; - pOut->z = pOp->p4.z; - pOut->n = pOp->p1; - pOut->enc = encoding; - UPDATE_MAX_BLOBSIZE(pOut); - break; -} - -/* Opcode: Null P1 P2 P3 * * -** -** Write a NULL into registers P2. If P3 greater than P2, then also write -** NULL into register P3 and every register in between P2 and P3. If P3 -** is less than P2 (typically P3 is zero) then only register P2 is -** set to NULL. -** -** If the P1 value is non-zero, then also set the MEM_Cleared flag so that -** NULL values will not compare equal even if SQLITE_NULLEQ is set on -** OP_Ne or OP_Eq. -*/ -case OP_Null: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ab */ - int cnt; - u16 nullFlag; -#endif /* local variables moved into u.ab */ - u.ab.cnt = pOp->p3-pOp->p2; - assert( pOp->p3<=p->nMem ); - pOut->flags = u.ab.nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null; - while( u.ab.cnt>0 ){ - pOut++; - memAboutToChange(p, pOut); - VdbeMemRelease(pOut); - pOut->flags = u.ab.nullFlag; - u.ab.cnt--; - } - break; -} - - -/* Opcode: Blob P1 P2 * P4 -** -** P4 points to a blob of data P1 bytes long. Store this -** blob in register P2. -*/ -case OP_Blob: { /* out2-prerelease */ - assert( pOp->p1 <= SQLITE_MAX_LENGTH ); - sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); - pOut->enc = encoding; - UPDATE_MAX_BLOBSIZE(pOut); - break; -} - -/* Opcode: Variable P1 P2 * P4 * -** -** Transfer the values of bound parameter P1 into register P2 -** -** If the parameter is named, then its name appears in P4 and P3==1. -** The P4 value is used by sqlite3_bind_parameter_name(). -*/ -case OP_Variable: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ac */ - Mem *pVar; /* Value being transferred */ -#endif /* local variables moved into u.ac */ - - assert( pOp->p1>0 && pOp->p1<=p->nVar ); - assert( pOp->p4.z==0 || pOp->p4.z==p->azVar[pOp->p1-1] ); - u.ac.pVar = &p->aVar[pOp->p1 - 1]; - if( sqlite3VdbeMemTooBig(u.ac.pVar) ){ - goto too_big; - } - sqlite3VdbeMemShallowCopy(pOut, u.ac.pVar, MEM_Static); - UPDATE_MAX_BLOBSIZE(pOut); - break; -} - -/* Opcode: Move P1 P2 P3 * * -** -** Move the values in register P1..P1+P3 over into -** registers P2..P2+P3. Registers P1..P1+P3 are -** left holding a NULL. It is an error for register ranges -** P1..P1+P3 and P2..P2+P3 to overlap. -*/ -case OP_Move: { -#if 0 /* local variables moved into u.ad */ - char *zMalloc; /* Holding variable for allocated memory */ - int n; /* Number of registers left to copy */ - int p1; /* Register to copy from */ - int p2; /* Register to copy to */ -#endif /* local variables moved into u.ad */ - - u.ad.n = pOp->p3 + 1; - u.ad.p1 = pOp->p1; - u.ad.p2 = pOp->p2; - assert( u.ad.n>0 && u.ad.p1>0 && u.ad.p2>0 ); - assert( u.ad.p1+u.ad.n<=u.ad.p2 || u.ad.p2+u.ad.n<=u.ad.p1 ); - - pIn1 = &aMem[u.ad.p1]; - pOut = &aMem[u.ad.p2]; - while( u.ad.n-- ){ - assert( pOut<=&aMem[p->nMem] ); - assert( pIn1<=&aMem[p->nMem] ); - assert( memIsValid(pIn1) ); - memAboutToChange(p, pOut); - u.ad.zMalloc = pOut->zMalloc; - pOut->zMalloc = 0; - sqlite3VdbeMemMove(pOut, pIn1); -#ifdef SQLITE_DEBUG - if( pOut->pScopyFrom>=&aMem[u.ad.p1] && pOut->pScopyFrom<&aMem[u.ad.p1+pOp->p3] ){ - pOut->pScopyFrom += u.ad.p1 - pOp->p2; - } -#endif - pIn1->zMalloc = u.ad.zMalloc; - REGISTER_TRACE(u.ad.p2++, pOut); - pIn1++; - pOut++; - } - break; -} - -/* Opcode: Copy P1 P2 P3 * * -** -** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. -** -** This instruction makes a deep copy of the value. A duplicate -** is made of any string or blob constant. See also OP_SCopy. -*/ -case OP_Copy: { -#if 0 /* local variables moved into u.ae */ - int n; -#endif /* local variables moved into u.ae */ - - u.ae.n = pOp->p3; - pIn1 = &aMem[pOp->p1]; - pOut = &aMem[pOp->p2]; - assert( pOut!=pIn1 ); - while( 1 ){ - sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); - Deephemeralize(pOut); -#ifdef SQLITE_DEBUG - pOut->pScopyFrom = 0; -#endif - REGISTER_TRACE(pOp->p2+pOp->p3-u.ae.n, pOut); - if( (u.ae.n--)==0 ) break; - pOut++; - pIn1++; - } - break; -} - -/* Opcode: SCopy P1 P2 * * * -** -** Make a shallow copy of register P1 into register P2. -** -** This instruction makes a shallow copy of the value. If the value -** is a string or blob, then the copy is only a pointer to the -** original and hence if the original changes so will the copy. -** Worse, if the original is deallocated, the copy becomes invalid. -** Thus the program must guarantee that the original will not change -** during the lifetime of the copy. Use OP_Copy to make a complete -** copy. -*/ -case OP_SCopy: { /* in1, out2 */ - pIn1 = &aMem[pOp->p1]; - pOut = &aMem[pOp->p2]; - assert( pOut!=pIn1 ); - sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); -#ifdef SQLITE_DEBUG - if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1; -#endif - REGISTER_TRACE(pOp->p2, pOut); - break; -} - -/* Opcode: ResultRow P1 P2 * * * -** -** The registers P1 through P1+P2-1 contain a single row of -** results. This opcode causes the sqlite3_step() call to terminate -** with an SQLITE_ROW return code and it sets up the sqlite3_stmt -** structure to provide access to the top P1 values as the result -** row. -*/ -case OP_ResultRow: { -#if 0 /* local variables moved into u.af */ - Mem *pMem; - int i; -#endif /* local variables moved into u.af */ - assert( p->nResColumn==pOp->p2 ); - assert( pOp->p1>0 ); - assert( pOp->p1+pOp->p2<=p->nMem+1 ); - - /* If this statement has violated immediate foreign key constraints, do - ** not return the number of rows modified. And do not RELEASE the statement - ** transaction. It needs to be rolled back. */ - if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){ - assert( db->flags&SQLITE_CountRows ); - assert( p->usesStmtJournal ); - break; - } - - /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then - ** DML statements invoke this opcode to return the number of rows - ** modified to the user. This is the only way that a VM that - ** opens a statement transaction may invoke this opcode. - ** - ** In case this is such a statement, close any statement transaction - ** opened by this VM before returning control to the user. This is to - ** ensure that statement-transactions are always nested, not overlapping. - ** If the open statement-transaction is not closed here, then the user - ** may step another VM that opens its own statement transaction. This - ** may lead to overlapping statement transactions. - ** - ** The statement transaction is never a top-level transaction. Hence - ** the RELEASE call below can never fail. - */ - assert( p->iStatement==0 || db->flags&SQLITE_CountRows ); - rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE); - if( NEVER(rc!=SQLITE_OK) ){ - break; - } - - /* Invalidate all ephemeral cursor row caches */ - p->cacheCtr = (p->cacheCtr + 2)|1; - - /* Make sure the results of the current row are \000 terminated - ** and have an assigned type. The results are de-ephemeralized as - ** a side effect. - */ - u.af.pMem = p->pResultSet = &aMem[pOp->p1]; - for(u.af.i=0; u.af.ip2; u.af.i++){ - assert( memIsValid(&u.af.pMem[u.af.i]) ); - Deephemeralize(&u.af.pMem[u.af.i]); - assert( (u.af.pMem[u.af.i].flags & MEM_Ephem)==0 - || (u.af.pMem[u.af.i].flags & (MEM_Str|MEM_Blob))==0 ); - sqlite3VdbeMemNulTerminate(&u.af.pMem[u.af.i]); - sqlite3VdbeMemStoreType(&u.af.pMem[u.af.i]); - REGISTER_TRACE(pOp->p1+u.af.i, &u.af.pMem[u.af.i]); - } - if( db->mallocFailed ) goto no_mem; - - /* Return SQLITE_ROW - */ - p->pc = pc + 1; - rc = SQLITE_ROW; - goto vdbe_return; -} - -/* Opcode: Concat P1 P2 P3 * * -** -** Add the text in register P1 onto the end of the text in -** register P2 and store the result in register P3. -** If either the P1 or P2 text are NULL then store NULL in P3. -** -** P3 = P2 || P1 -** -** It is illegal for P1 and P3 to be the same register. Sometimes, -** if P3 is the same register as P2, the implementation is able -** to avoid a memcpy(). -*/ -case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ -#if 0 /* local variables moved into u.ag */ - i64 nByte; -#endif /* local variables moved into u.ag */ - - pIn1 = &aMem[pOp->p1]; - pIn2 = &aMem[pOp->p2]; - pOut = &aMem[pOp->p3]; - assert( pIn1!=pOut ); - if( (pIn1->flags | pIn2->flags) & MEM_Null ){ - sqlite3VdbeMemSetNull(pOut); - break; - } - if( ExpandBlob(pIn1) || ExpandBlob(pIn2) ) goto no_mem; - Stringify(pIn1, encoding); - Stringify(pIn2, encoding); - u.ag.nByte = pIn1->n + pIn2->n; - if( u.ag.nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - MemSetTypeFlag(pOut, MEM_Str); - if( sqlite3VdbeMemGrow(pOut, (int)u.ag.nByte+2, pOut==pIn2) ){ - goto no_mem; - } - if( pOut!=pIn2 ){ - memcpy(pOut->z, pIn2->z, pIn2->n); - } - memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n); - pOut->z[u.ag.nByte] = 0; - pOut->z[u.ag.nByte+1] = 0; - pOut->flags |= MEM_Term; - pOut->n = (int)u.ag.nByte; - pOut->enc = encoding; - UPDATE_MAX_BLOBSIZE(pOut); - break; -} - -/* Opcode: Add P1 P2 P3 * * -** -** Add the value in register P1 to the value in register P2 -** and store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: Multiply P1 P2 P3 * * -** -** -** Multiply the value in register P1 by the value in register P2 -** and store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: Subtract P1 P2 P3 * * -** -** Subtract the value in register P1 from the value in register P2 -** and store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: Divide P1 P2 P3 * * -** -** Divide the value in register P1 by the value in register P2 -** and store the result in register P3 (P3=P2/P1). If the value in -** register P1 is zero, then the result is NULL. If either input is -** NULL, the result is NULL. -*/ -/* Opcode: Remainder P1 P2 P3 * * -** -** Compute the remainder after integer division of the value in -** register P1 by the value in register P2 and store the result in P3. -** If the value in register P2 is zero the result is NULL. -** If either operand is NULL, the result is NULL. -*/ -case OP_Add: /* same as TK_PLUS, in1, in2, out3 */ -case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ -case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ -case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ -case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ -#if 0 /* local variables moved into u.ah */ - char bIntint; /* Started out as two integer operands */ - int flags; /* Combined MEM_* flags from both inputs */ - i64 iA; /* Integer value of left operand */ - i64 iB; /* Integer value of right operand */ - double rA; /* Real value of left operand */ - double rB; /* Real value of right operand */ -#endif /* local variables moved into u.ah */ - - pIn1 = &aMem[pOp->p1]; - applyNumericAffinity(pIn1); - pIn2 = &aMem[pOp->p2]; - applyNumericAffinity(pIn2); - pOut = &aMem[pOp->p3]; - u.ah.flags = pIn1->flags | pIn2->flags; - if( (u.ah.flags & MEM_Null)!=0 ) goto arithmetic_result_is_null; - if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){ - u.ah.iA = pIn1->u.i; - u.ah.iB = pIn2->u.i; - u.ah.bIntint = 1; - switch( pOp->opcode ){ - case OP_Add: if( sqlite3AddInt64(&u.ah.iB,u.ah.iA) ) goto fp_math; break; - case OP_Subtract: if( sqlite3SubInt64(&u.ah.iB,u.ah.iA) ) goto fp_math; break; - case OP_Multiply: if( sqlite3MulInt64(&u.ah.iB,u.ah.iA) ) goto fp_math; break; - case OP_Divide: { - if( u.ah.iA==0 ) goto arithmetic_result_is_null; - if( u.ah.iA==-1 && u.ah.iB==SMALLEST_INT64 ) goto fp_math; - u.ah.iB /= u.ah.iA; - break; - } - default: { - if( u.ah.iA==0 ) goto arithmetic_result_is_null; - if( u.ah.iA==-1 ) u.ah.iA = 1; - u.ah.iB %= u.ah.iA; - break; - } - } - pOut->u.i = u.ah.iB; - MemSetTypeFlag(pOut, MEM_Int); - }else{ - u.ah.bIntint = 0; -fp_math: - u.ah.rA = sqlite3VdbeRealValue(pIn1); - u.ah.rB = sqlite3VdbeRealValue(pIn2); - switch( pOp->opcode ){ - case OP_Add: u.ah.rB += u.ah.rA; break; - case OP_Subtract: u.ah.rB -= u.ah.rA; break; - case OP_Multiply: u.ah.rB *= u.ah.rA; break; - case OP_Divide: { - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - if( u.ah.rA==(double)0 ) goto arithmetic_result_is_null; - u.ah.rB /= u.ah.rA; - break; - } - default: { - u.ah.iA = (i64)u.ah.rA; - u.ah.iB = (i64)u.ah.rB; - if( u.ah.iA==0 ) goto arithmetic_result_is_null; - if( u.ah.iA==-1 ) u.ah.iA = 1; - u.ah.rB = (double)(u.ah.iB % u.ah.iA); - break; - } - } -#ifdef SQLITE_OMIT_FLOATING_POINT - pOut->u.i = u.ah.rB; - MemSetTypeFlag(pOut, MEM_Int); -#else - if( sqlite3IsNaN(u.ah.rB) ){ - goto arithmetic_result_is_null; - } - pOut->r = u.ah.rB; - MemSetTypeFlag(pOut, MEM_Real); - if( (u.ah.flags & MEM_Real)==0 && !u.ah.bIntint ){ - sqlite3VdbeIntegerAffinity(pOut); - } -#endif - } - break; - -arithmetic_result_is_null: - sqlite3VdbeMemSetNull(pOut); - break; -} - -/* Opcode: CollSeq P1 * * P4 -** -** P4 is a pointer to a CollSeq struct. If the next call to a user function -** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will -** be returned. This is used by the built-in min(), max() and nullif() -** functions. -** -** If P1 is not zero, then it is a register that a subsequent min() or -** max() aggregate will set to 1 if the current row is not the minimum or -** maximum. The P1 register is initialized to 0 by this instruction. -** -** The interface used by the implementation of the aforementioned functions -** to retrieve the collation sequence set by this opcode is not available -** publicly, only to user functions defined in func.c. -*/ -case OP_CollSeq: { - assert( pOp->p4type==P4_COLLSEQ ); - if( pOp->p1 ){ - sqlite3VdbeMemSetInt64(&aMem[pOp->p1], 0); - } - break; -} - -/* Opcode: Function P1 P2 P3 P4 P5 -** -** Invoke a user function (P4 is a pointer to a Function structure that -** defines the function) with P5 arguments taken from register P2 and -** successors. The result of the function is stored in register P3. -** Register P3 must not be one of the function inputs. -** -** P1 is a 32-bit bitmask indicating whether or not each argument to the -** function was determined to be constant at compile time. If the first -** argument was constant then bit 0 of P1 is set. This is used to determine -** whether meta data associated with a user function argument using the -** sqlite3_set_auxdata() API may be safely retained until the next -** invocation of this opcode. -** -** See also: AggStep and AggFinal -*/ -case OP_Function: { -#if 0 /* local variables moved into u.ai */ - int i; - Mem *pArg; - sqlite3_context ctx; - sqlite3_value **apVal; - int n; -#endif /* local variables moved into u.ai */ - - u.ai.n = pOp->p5; - u.ai.apVal = p->apArg; - assert( u.ai.apVal || u.ai.n==0 ); - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - pOut = &aMem[pOp->p3]; - memAboutToChange(p, pOut); - - assert( u.ai.n==0 || (pOp->p2>0 && pOp->p2+u.ai.n<=p->nMem+1) ); - assert( pOp->p3p2 || pOp->p3>=pOp->p2+u.ai.n ); - u.ai.pArg = &aMem[pOp->p2]; - for(u.ai.i=0; u.ai.ip2+u.ai.i, u.ai.pArg); - } - - assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); - if( pOp->p4type==P4_FUNCDEF ){ - u.ai.ctx.pFunc = pOp->p4.pFunc; - u.ai.ctx.pVdbeFunc = 0; - }else{ - u.ai.ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc; - u.ai.ctx.pFunc = u.ai.ctx.pVdbeFunc->pFunc; - } - - u.ai.ctx.s.flags = MEM_Null; - u.ai.ctx.s.db = db; - u.ai.ctx.s.xDel = 0; - u.ai.ctx.s.zMalloc = 0; - - /* The output cell may already have a buffer allocated. Move - ** the pointer to u.ai.ctx.s so in case the user-function can use - ** the already allocated buffer instead of allocating a new one. - */ - sqlite3VdbeMemMove(&u.ai.ctx.s, pOut); - MemSetTypeFlag(&u.ai.ctx.s, MEM_Null); - - u.ai.ctx.isError = 0; - if( u.ai.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ - assert( pOp>aOp ); - assert( pOp[-1].p4type==P4_COLLSEQ ); - assert( pOp[-1].opcode==OP_CollSeq ); - u.ai.ctx.pColl = pOp[-1].p4.pColl; - } - db->lastRowid = lastRowid; - (*u.ai.ctx.pFunc->xFunc)(&u.ai.ctx, u.ai.n, u.ai.apVal); /* IMP: R-24505-23230 */ - lastRowid = db->lastRowid; - - /* If any auxiliary data functions have been called by this user function, - ** immediately call the destructor for any non-static values. - */ - if( u.ai.ctx.pVdbeFunc ){ - sqlite3VdbeDeleteAuxData(u.ai.ctx.pVdbeFunc, pOp->p1); - pOp->p4.pVdbeFunc = u.ai.ctx.pVdbeFunc; - pOp->p4type = P4_VDBEFUNC; - } - - if( db->mallocFailed ){ - /* Even though a malloc() has failed, the implementation of the - ** user function may have called an sqlite3_result_XXX() function - ** to return a value. The following call releases any resources - ** associated with such a value. - */ - sqlite3VdbeMemRelease(&u.ai.ctx.s); - goto no_mem; - } - - /* If the function returned an error, throw an exception */ - if( u.ai.ctx.isError ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ai.ctx.s)); - rc = u.ai.ctx.isError; - } - - /* Copy the result of the function into register P3 */ - sqlite3VdbeChangeEncoding(&u.ai.ctx.s, encoding); - sqlite3VdbeMemMove(pOut, &u.ai.ctx.s); - if( sqlite3VdbeMemTooBig(pOut) ){ - goto too_big; - } - -#if 0 - /* The app-defined function has done something that as caused this - ** statement to expire. (Perhaps the function called sqlite3_exec() - ** with a CREATE TABLE statement.) - */ - if( p->expired ) rc = SQLITE_ABORT; -#endif - - REGISTER_TRACE(pOp->p3, pOut); - UPDATE_MAX_BLOBSIZE(pOut); - break; -} - -/* Opcode: BitAnd P1 P2 P3 * * -** -** Take the bit-wise AND of the values in register P1 and P2 and -** store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: BitOr P1 P2 P3 * * -** -** Take the bit-wise OR of the values in register P1 and P2 and -** store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: ShiftLeft P1 P2 P3 * * -** -** Shift the integer value in register P2 to the left by the -** number of bits specified by the integer in register P1. -** Store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: ShiftRight P1 P2 P3 * * -** -** Shift the integer value in register P2 to the right by the -** number of bits specified by the integer in register P1. -** Store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */ -case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ -case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ -case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ -#if 0 /* local variables moved into u.aj */ - i64 iA; - u64 uA; - i64 iB; - u8 op; -#endif /* local variables moved into u.aj */ - - pIn1 = &aMem[pOp->p1]; - pIn2 = &aMem[pOp->p2]; - pOut = &aMem[pOp->p3]; - if( (pIn1->flags | pIn2->flags) & MEM_Null ){ - sqlite3VdbeMemSetNull(pOut); - break; - } - u.aj.iA = sqlite3VdbeIntValue(pIn2); - u.aj.iB = sqlite3VdbeIntValue(pIn1); - u.aj.op = pOp->opcode; - if( u.aj.op==OP_BitAnd ){ - u.aj.iA &= u.aj.iB; - }else if( u.aj.op==OP_BitOr ){ - u.aj.iA |= u.aj.iB; - }else if( u.aj.iB!=0 ){ - assert( u.aj.op==OP_ShiftRight || u.aj.op==OP_ShiftLeft ); - - /* If shifting by a negative amount, shift in the other direction */ - if( u.aj.iB<0 ){ - assert( OP_ShiftRight==OP_ShiftLeft+1 ); - u.aj.op = 2*OP_ShiftLeft + 1 - u.aj.op; - u.aj.iB = u.aj.iB>(-64) ? -u.aj.iB : 64; - } - - if( u.aj.iB>=64 ){ - u.aj.iA = (u.aj.iA>=0 || u.aj.op==OP_ShiftLeft) ? 0 : -1; - }else{ - memcpy(&u.aj.uA, &u.aj.iA, sizeof(u.aj.uA)); - if( u.aj.op==OP_ShiftLeft ){ - u.aj.uA <<= u.aj.iB; - }else{ - u.aj.uA >>= u.aj.iB; - /* Sign-extend on a right shift of a negative number */ - if( u.aj.iA<0 ) u.aj.uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-u.aj.iB); - } - memcpy(&u.aj.iA, &u.aj.uA, sizeof(u.aj.iA)); - } - } - pOut->u.i = u.aj.iA; - MemSetTypeFlag(pOut, MEM_Int); - break; -} - -/* Opcode: AddImm P1 P2 * * * -** -** Add the constant P2 to the value in register P1. -** The result is always an integer. -** -** To force any register to be an integer, just add 0. -*/ -case OP_AddImm: { /* in1 */ - pIn1 = &aMem[pOp->p1]; - memAboutToChange(p, pIn1); - sqlite3VdbeMemIntegerify(pIn1); - pIn1->u.i += pOp->p2; - break; -} - -/* Opcode: MustBeInt P1 P2 * * * -** -** Force the value in register P1 to be an integer. If the value -** in P1 is not an integer and cannot be converted into an integer -** without data loss, then jump immediately to P2, or if P2==0 -** raise an SQLITE_MISMATCH exception. -*/ -case OP_MustBeInt: { /* jump, in1 */ - pIn1 = &aMem[pOp->p1]; - applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); - if( (pIn1->flags & MEM_Int)==0 ){ - if( pOp->p2==0 ){ - rc = SQLITE_MISMATCH; - goto abort_due_to_error; - }else{ - pc = pOp->p2 - 1; - } - }else{ - MemSetTypeFlag(pIn1, MEM_Int); - } - break; -} - -#ifndef SQLITE_OMIT_FLOATING_POINT -/* Opcode: RealAffinity P1 * * * * -** -** If register P1 holds an integer convert it to a real value. -** -** This opcode is used when extracting information from a column that -** has REAL affinity. Such column values may still be stored as -** integers, for space efficiency, but after extraction we want them -** to have only a real value. -*/ -case OP_RealAffinity: { /* in1 */ - pIn1 = &aMem[pOp->p1]; - if( pIn1->flags & MEM_Int ){ - sqlite3VdbeMemRealify(pIn1); - } - break; -} -#endif - -#ifndef SQLITE_OMIT_CAST -/* Opcode: ToText P1 * * * * -** -** Force the value in register P1 to be text. -** If the value is numeric, convert it to a string using the -** equivalent of printf(). Blob values are unchanged and -** are afterwards simply interpreted as text. -** -** A NULL value is not changed by this routine. It remains NULL. -*/ -case OP_ToText: { /* same as TK_TO_TEXT, in1 */ - pIn1 = &aMem[pOp->p1]; - memAboutToChange(p, pIn1); - if( pIn1->flags & MEM_Null ) break; - assert( MEM_Str==(MEM_Blob>>3) ); - pIn1->flags |= (pIn1->flags&MEM_Blob)>>3; - applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); - rc = ExpandBlob(pIn1); - assert( pIn1->flags & MEM_Str || db->mallocFailed ); - pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero); - UPDATE_MAX_BLOBSIZE(pIn1); - break; -} - -/* Opcode: ToBlob P1 * * * * -** -** Force the value in register P1 to be a BLOB. -** If the value is numeric, convert it to a string first. -** Strings are simply reinterpreted as blobs with no change -** to the underlying data. -** -** A NULL value is not changed by this routine. It remains NULL. -*/ -case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */ - pIn1 = &aMem[pOp->p1]; - if( pIn1->flags & MEM_Null ) break; - if( (pIn1->flags & MEM_Blob)==0 ){ - applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); - assert( pIn1->flags & MEM_Str || db->mallocFailed ); - MemSetTypeFlag(pIn1, MEM_Blob); - }else{ - pIn1->flags &= ~(MEM_TypeMask&~MEM_Blob); - } - UPDATE_MAX_BLOBSIZE(pIn1); - break; -} - -/* Opcode: ToNumeric P1 * * * * -** -** Force the value in register P1 to be numeric (either an -** integer or a floating-point number.) -** If the value is text or blob, try to convert it to an using the -** equivalent of atoi() or atof() and store 0 if no such conversion -** is possible. -** -** A NULL value is not changed by this routine. It remains NULL. -*/ -case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ - pIn1 = &aMem[pOp->p1]; - sqlite3VdbeMemNumerify(pIn1); - break; -} -#endif /* SQLITE_OMIT_CAST */ - -/* Opcode: ToInt P1 * * * * -** -** Force the value in register P1 to be an integer. If -** The value is currently a real number, drop its fractional part. -** If the value is text or blob, try to convert it to an integer using the -** equivalent of atoi() and store 0 if no such conversion is possible. -** -** A NULL value is not changed by this routine. It remains NULL. -*/ -case OP_ToInt: { /* same as TK_TO_INT, in1 */ - pIn1 = &aMem[pOp->p1]; - if( (pIn1->flags & MEM_Null)==0 ){ - sqlite3VdbeMemIntegerify(pIn1); - } - break; -} - -#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) -/* Opcode: ToReal P1 * * * * -** -** Force the value in register P1 to be a floating point number. -** If The value is currently an integer, convert it. -** If the value is text or blob, try to convert it to an integer using the -** equivalent of atoi() and store 0.0 if no such conversion is possible. -** -** A NULL value is not changed by this routine. It remains NULL. -*/ -case OP_ToReal: { /* same as TK_TO_REAL, in1 */ - pIn1 = &aMem[pOp->p1]; - memAboutToChange(p, pIn1); - if( (pIn1->flags & MEM_Null)==0 ){ - sqlite3VdbeMemRealify(pIn1); - } - break; -} -#endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */ - -/* Opcode: Lt P1 P2 P3 P4 P5 -** -** Compare the values in register P1 and P3. If reg(P3)flags */ - u16 flags3; /* Copy of initial value of pIn3->flags */ -#endif /* local variables moved into u.ak */ - - pIn1 = &aMem[pOp->p1]; - pIn3 = &aMem[pOp->p3]; - u.ak.flags1 = pIn1->flags; - u.ak.flags3 = pIn3->flags; - if( (u.ak.flags1 | u.ak.flags3)&MEM_Null ){ - /* One or both operands are NULL */ - if( pOp->p5 & SQLITE_NULLEQ ){ - /* If SQLITE_NULLEQ is set (which will only happen if the operator is - ** OP_Eq or OP_Ne) then take the jump or not depending on whether - ** or not both operands are null. - */ - assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); - assert( (u.ak.flags1 & MEM_Cleared)==0 ); - if( (u.ak.flags1&MEM_Null)!=0 - && (u.ak.flags3&MEM_Null)!=0 - && (u.ak.flags3&MEM_Cleared)==0 - ){ - u.ak.res = 0; /* Results are equal */ - }else{ - u.ak.res = 1; /* Results are not equal */ - } - }else{ - /* SQLITE_NULLEQ is clear and at least one operand is NULL, - ** then the result is always NULL. - ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. - */ - if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &aMem[pOp->p2]; - MemSetTypeFlag(pOut, MEM_Null); - REGISTER_TRACE(pOp->p2, pOut); - }else if( pOp->p5 & SQLITE_JUMPIFNULL ){ - pc = pOp->p2-1; - } - break; - } - }else{ - /* Neither operand is NULL. Do a comparison. */ - u.ak.affinity = pOp->p5 & SQLITE_AFF_MASK; - if( u.ak.affinity ){ - applyAffinity(pIn1, u.ak.affinity, encoding); - applyAffinity(pIn3, u.ak.affinity, encoding); - if( db->mallocFailed ) goto no_mem; - } - - assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); - ExpandBlob(pIn1); - ExpandBlob(pIn3); - u.ak.res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); - } - switch( pOp->opcode ){ - case OP_Eq: u.ak.res = u.ak.res==0; break; - case OP_Ne: u.ak.res = u.ak.res!=0; break; - case OP_Lt: u.ak.res = u.ak.res<0; break; - case OP_Le: u.ak.res = u.ak.res<=0; break; - case OP_Gt: u.ak.res = u.ak.res>0; break; - default: u.ak.res = u.ak.res>=0; break; - } - - if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &aMem[pOp->p2]; - memAboutToChange(p, pOut); - MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = u.ak.res; - REGISTER_TRACE(pOp->p2, pOut); - }else if( u.ak.res ){ - pc = pOp->p2-1; - } - - /* Undo any changes made by applyAffinity() to the input registers. */ - pIn1->flags = (pIn1->flags&~MEM_TypeMask) | (u.ak.flags1&MEM_TypeMask); - pIn3->flags = (pIn3->flags&~MEM_TypeMask) | (u.ak.flags3&MEM_TypeMask); - break; -} - -/* Opcode: Permutation * * * P4 * -** -** Set the permutation used by the OP_Compare operator to be the array -** of integers in P4. -** -** The permutation is only valid until the next OP_Compare that has -** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should -** occur immediately prior to the OP_Compare. -*/ -case OP_Permutation: { - assert( pOp->p4type==P4_INTARRAY ); - assert( pOp->p4.ai ); - aPermute = pOp->p4.ai; - break; -} - -/* Opcode: Compare P1 P2 P3 P4 P5 -** -** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this -** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of -** the comparison for use by the next OP_Jump instruct. -** -** If P5 has the OPFLAG_PERMUTE bit set, then the order of comparison is -** determined by the most recent OP_Permutation operator. If the -** OPFLAG_PERMUTE bit is clear, then register are compared in sequential -** order. -** -** P4 is a KeyInfo structure that defines collating sequences and sort -** orders for the comparison. The permutation applies to registers -** only. The KeyInfo elements are used sequentially. -** -** The comparison is a sort comparison, so NULLs compare equal, -** NULLs are less than numbers, numbers are less than strings, -** and strings are less than blobs. -*/ -case OP_Compare: { -#if 0 /* local variables moved into u.al */ - int n; - int i; - int p1; - int p2; - const KeyInfo *pKeyInfo; - int idx; - CollSeq *pColl; /* Collating sequence to use on this term */ - int bRev; /* True for DESCENDING sort order */ -#endif /* local variables moved into u.al */ - - if( (pOp->p5 & OPFLAG_PERMUTE)==0 ) aPermute = 0; - u.al.n = pOp->p3; - u.al.pKeyInfo = pOp->p4.pKeyInfo; - assert( u.al.n>0 ); - assert( u.al.pKeyInfo!=0 ); - u.al.p1 = pOp->p1; - u.al.p2 = pOp->p2; -#if SQLITE_DEBUG - if( aPermute ){ - int k, mx = 0; - for(k=0; kmx ) mx = aPermute[k]; - assert( u.al.p1>0 && u.al.p1+mx<=p->nMem+1 ); - assert( u.al.p2>0 && u.al.p2+mx<=p->nMem+1 ); - }else{ - assert( u.al.p1>0 && u.al.p1+u.al.n<=p->nMem+1 ); - assert( u.al.p2>0 && u.al.p2+u.al.n<=p->nMem+1 ); - } -#endif /* SQLITE_DEBUG */ - for(u.al.i=0; u.al.inField ); - u.al.pColl = u.al.pKeyInfo->aColl[u.al.i]; - u.al.bRev = u.al.pKeyInfo->aSortOrder[u.al.i]; - iCompare = sqlite3MemCompare(&aMem[u.al.p1+u.al.idx], &aMem[u.al.p2+u.al.idx], u.al.pColl); - if( iCompare ){ - if( u.al.bRev ) iCompare = -iCompare; - break; - } - } - aPermute = 0; - break; -} - -/* Opcode: Jump P1 P2 P3 * * -** -** Jump to the instruction at address P1, P2, or P3 depending on whether -** in the most recent OP_Compare instruction the P1 vector was less than -** equal to, or greater than the P2 vector, respectively. -*/ -case OP_Jump: { /* jump */ - if( iCompare<0 ){ - pc = pOp->p1 - 1; - }else if( iCompare==0 ){ - pc = pOp->p2 - 1; - }else{ - pc = pOp->p3 - 1; - } - break; -} - -/* Opcode: And P1 P2 P3 * * -** -** Take the logical AND of the values in registers P1 and P2 and -** write the result into register P3. -** -** If either P1 or P2 is 0 (false) then the result is 0 even if -** the other input is NULL. A NULL and true or two NULLs give -** a NULL output. -*/ -/* Opcode: Or P1 P2 P3 * * -** -** Take the logical OR of the values in register P1 and P2 and -** store the answer in register P3. -** -** If either P1 or P2 is nonzero (true) then the result is 1 (true) -** even if the other input is NULL. A NULL and false or two NULLs -** give a NULL output. -*/ -case OP_And: /* same as TK_AND, in1, in2, out3 */ -case OP_Or: { /* same as TK_OR, in1, in2, out3 */ -#if 0 /* local variables moved into u.am */ - int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ -#endif /* local variables moved into u.am */ - - pIn1 = &aMem[pOp->p1]; - if( pIn1->flags & MEM_Null ){ - u.am.v1 = 2; - }else{ - u.am.v1 = sqlite3VdbeIntValue(pIn1)!=0; - } - pIn2 = &aMem[pOp->p2]; - if( pIn2->flags & MEM_Null ){ - u.am.v2 = 2; - }else{ - u.am.v2 = sqlite3VdbeIntValue(pIn2)!=0; - } - if( pOp->opcode==OP_And ){ - static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; - u.am.v1 = and_logic[u.am.v1*3+u.am.v2]; - }else{ - static const unsigned char or_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; - u.am.v1 = or_logic[u.am.v1*3+u.am.v2]; - } - pOut = &aMem[pOp->p3]; - if( u.am.v1==2 ){ - MemSetTypeFlag(pOut, MEM_Null); - }else{ - pOut->u.i = u.am.v1; - MemSetTypeFlag(pOut, MEM_Int); - } - break; -} - -/* Opcode: Not P1 P2 * * * -** -** Interpret the value in register P1 as a boolean value. Store the -** boolean complement in register P2. If the value in register P1 is -** NULL, then a NULL is stored in P2. -*/ -case OP_Not: { /* same as TK_NOT, in1, out2 */ - pIn1 = &aMem[pOp->p1]; - pOut = &aMem[pOp->p2]; - if( pIn1->flags & MEM_Null ){ - sqlite3VdbeMemSetNull(pOut); - }else{ - sqlite3VdbeMemSetInt64(pOut, !sqlite3VdbeIntValue(pIn1)); - } - break; -} - -/* Opcode: BitNot P1 P2 * * * -** -** Interpret the content of register P1 as an integer. Store the -** ones-complement of the P1 value into register P2. If P1 holds -** a NULL then store a NULL in P2. -*/ -case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ - pIn1 = &aMem[pOp->p1]; - pOut = &aMem[pOp->p2]; - if( pIn1->flags & MEM_Null ){ - sqlite3VdbeMemSetNull(pOut); - }else{ - sqlite3VdbeMemSetInt64(pOut, ~sqlite3VdbeIntValue(pIn1)); - } - break; -} - -/* Opcode: Once P1 P2 * * * -** -** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise, -** set the flag and fall through to the next instruction. -*/ -case OP_Once: { /* jump */ - assert( pOp->p1nOnceFlag ); - if( p->aOnceFlag[pOp->p1] ){ - pc = pOp->p2-1; - }else{ - p->aOnceFlag[pOp->p1] = 1; - } - break; -} - -/* Opcode: If P1 P2 P3 * * -** -** Jump to P2 if the value in register P1 is true. The value -** is considered true if it is numeric and non-zero. If the value -** in P1 is NULL then take the jump if P3 is non-zero. -*/ -/* Opcode: IfNot P1 P2 P3 * * -** -** Jump to P2 if the value in register P1 is False. The value -** is considered false if it has a numeric value of zero. If the value -** in P1 is NULL then take the jump if P3 is zero. -*/ -case OP_If: /* jump, in1 */ -case OP_IfNot: { /* jump, in1 */ -#if 0 /* local variables moved into u.an */ - int c; -#endif /* local variables moved into u.an */ - pIn1 = &aMem[pOp->p1]; - if( pIn1->flags & MEM_Null ){ - u.an.c = pOp->p3; - }else{ -#ifdef SQLITE_OMIT_FLOATING_POINT - u.an.c = sqlite3VdbeIntValue(pIn1)!=0; -#else - u.an.c = sqlite3VdbeRealValue(pIn1)!=0.0; -#endif - if( pOp->opcode==OP_IfNot ) u.an.c = !u.an.c; - } - if( u.an.c ){ - pc = pOp->p2-1; - } - break; -} - -/* Opcode: IsNull P1 P2 * * * -** -** Jump to P2 if the value in register P1 is NULL. -*/ -case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ - pIn1 = &aMem[pOp->p1]; - if( (pIn1->flags & MEM_Null)!=0 ){ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: NotNull P1 P2 * * * -** -** Jump to P2 if the value in register P1 is not NULL. -*/ -case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ - pIn1 = &aMem[pOp->p1]; - if( (pIn1->flags & MEM_Null)==0 ){ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: Column P1 P2 P3 P4 P5 -** -** Interpret the data that cursor P1 points to as a structure built using -** the MakeRecord instruction. (See the MakeRecord opcode for additional -** information about the format of the data.) Extract the P2-th column -** from this record. If there are less that (P2+1) -** values in the record, extract a NULL. -** -** The value extracted is stored in register P3. -** -** If the column contains fewer than P2 fields, then extract a NULL. Or, -** if the P4 argument is a P4_MEM use the value of the P4 argument as -** the result. -** -** If the OPFLAG_CLEARCACHE bit is set on P5 and P1 is a pseudo-table cursor, -** then the cache of the cursor is reset prior to extracting the column. -** The first OP_Column against a pseudo-table after the value of the content -** register has changed should have this bit set. -** -** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 when -** the result is guaranteed to only be used as the argument of a length() -** or typeof() function, respectively. The loading of large blobs can be -** skipped for length() and all content loading can be skipped for typeof(). -*/ -case OP_Column: { -#if 0 /* local variables moved into u.ao */ - u32 payloadSize; /* Number of bytes in the record */ - i64 payloadSize64; /* Number of bytes in the record */ - int p1; /* P1 value of the opcode */ - int p2; /* column number to retrieve */ - VdbeCursor *pC; /* The VDBE cursor */ - char *zRec; /* Pointer to complete record-data */ - BtCursor *pCrsr; /* The BTree cursor */ - u32 *aType; /* aType[i] holds the numeric type of the i-th column */ - u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ - int nField; /* number of fields in the record */ - int len; /* The length of the serialized data for the column */ - int i; /* Loop counter */ - char *zData; /* Part of the record being decoded */ - Mem *pDest; /* Where to write the extracted value */ - Mem sMem; /* For storing the record being decoded */ - u8 *zIdx; /* Index into header */ - u8 *zEndHdr; /* Pointer to first byte after the header */ - u32 offset; /* Offset into the data */ - u32 szField; /* Number of bytes in the content of a field */ - int szHdr; /* Size of the header size field at start of record */ - int avail; /* Number of bytes of available data */ - u32 t; /* A type code from the record header */ - Mem *pReg; /* PseudoTable input register */ -#endif /* local variables moved into u.ao */ - - - u.ao.p1 = pOp->p1; - u.ao.p2 = pOp->p2; - u.ao.pC = 0; - memset(&u.ao.sMem, 0, sizeof(u.ao.sMem)); - assert( u.ao.p1nCursor ); - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.ao.pDest = &aMem[pOp->p3]; - memAboutToChange(p, u.ao.pDest); - u.ao.zRec = 0; - - /* This block sets the variable u.ao.payloadSize to be the total number of - ** bytes in the record. - ** - ** u.ao.zRec is set to be the complete text of the record if it is available. - ** The complete record text is always available for pseudo-tables - ** If the record is stored in a cursor, the complete record text - ** might be available in the u.ao.pC->aRow cache. Or it might not be. - ** If the data is unavailable, u.ao.zRec is set to NULL. - ** - ** We also compute the number of columns in the record. For cursors, - ** the number of columns is stored in the VdbeCursor.nField element. - */ - u.ao.pC = p->apCsr[u.ao.p1]; - assert( u.ao.pC!=0 ); -#ifndef SQLITE_OMIT_VIRTUALTABLE - assert( u.ao.pC->pVtabCursor==0 ); -#endif - u.ao.pCrsr = u.ao.pC->pCursor; - if( u.ao.pCrsr!=0 ){ - /* The record is stored in a B-Tree */ - rc = sqlite3VdbeCursorMoveto(u.ao.pC); - if( rc ) goto abort_due_to_error; - if( u.ao.pC->nullRow ){ - u.ao.payloadSize = 0; - }else if( u.ao.pC->cacheStatus==p->cacheCtr ){ - u.ao.payloadSize = u.ao.pC->payloadSize; - u.ao.zRec = (char*)u.ao.pC->aRow; - }else if( u.ao.pC->isIndex ){ - assert( sqlite3BtreeCursorIsValid(u.ao.pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(u.ao.pCrsr, &u.ao.payloadSize64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the - ** payload size, so it is impossible for u.ao.payloadSize64 to be - ** larger than 32 bits. */ - assert( (u.ao.payloadSize64 & SQLITE_MAX_U32)==(u64)u.ao.payloadSize64 ); - u.ao.payloadSize = (u32)u.ao.payloadSize64; - }else{ - assert( sqlite3BtreeCursorIsValid(u.ao.pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeDataSize(u.ao.pCrsr, &u.ao.payloadSize); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - } - }else if( ALWAYS(u.ao.pC->pseudoTableReg>0) ){ - u.ao.pReg = &aMem[u.ao.pC->pseudoTableReg]; - if( u.ao.pC->multiPseudo ){ - sqlite3VdbeMemShallowCopy(u.ao.pDest, u.ao.pReg+u.ao.p2, MEM_Ephem); - Deephemeralize(u.ao.pDest); - goto op_column_out; - } - assert( u.ao.pReg->flags & MEM_Blob ); - assert( memIsValid(u.ao.pReg) ); - u.ao.payloadSize = u.ao.pReg->n; - u.ao.zRec = u.ao.pReg->z; - u.ao.pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; - assert( u.ao.payloadSize==0 || u.ao.zRec!=0 ); - }else{ - /* Consider the row to be NULL */ - u.ao.payloadSize = 0; - } - - /* If u.ao.payloadSize is 0, then just store a NULL. This can happen because of - ** nullRow or because of a corrupt database. */ - if( u.ao.payloadSize==0 ){ - MemSetTypeFlag(u.ao.pDest, MEM_Null); - goto op_column_out; - } - assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 ); - if( u.ao.payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - - u.ao.nField = u.ao.pC->nField; - assert( u.ao.p2aType; - if( u.ao.pC->cacheStatus==p->cacheCtr ){ - u.ao.aOffset = u.ao.pC->aOffset; - }else{ - assert(u.ao.aType); - u.ao.avail = 0; - u.ao.pC->aOffset = u.ao.aOffset = &u.ao.aType[u.ao.nField]; - u.ao.pC->payloadSize = u.ao.payloadSize; - u.ao.pC->cacheStatus = p->cacheCtr; - - /* Figure out how many bytes are in the header */ - if( u.ao.zRec ){ - u.ao.zData = u.ao.zRec; - }else{ - if( u.ao.pC->isIndex ){ - u.ao.zData = (char*)sqlite3BtreeKeyFetch(u.ao.pCrsr, &u.ao.avail); - }else{ - u.ao.zData = (char*)sqlite3BtreeDataFetch(u.ao.pCrsr, &u.ao.avail); - } - /* If KeyFetch()/DataFetch() managed to get the entire payload, - ** save the payload in the u.ao.pC->aRow cache. That will save us from - ** having to make additional calls to fetch the content portion of - ** the record. - */ - assert( u.ao.avail>=0 ); - if( u.ao.payloadSize <= (u32)u.ao.avail ){ - u.ao.zRec = u.ao.zData; - u.ao.pC->aRow = (u8*)u.ao.zData; - }else{ - u.ao.pC->aRow = 0; - } - } - /* The following assert is true in all cases except when - ** the database file has been corrupted externally. - ** assert( u.ao.zRec!=0 || u.ao.avail>=u.ao.payloadSize || u.ao.avail>=9 ); */ - u.ao.szHdr = getVarint32((u8*)u.ao.zData, u.ao.offset); - - /* Make sure a corrupt database has not given us an oversize header. - ** Do this now to avoid an oversize memory allocation. - ** - ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte - ** types use so much data space that there can only be 4096 and 32 of - ** them, respectively. So the maximum header length results from a - ** 3-byte type for each of the maximum of 32768 columns plus three - ** extra bytes for the header length itself. 32768*3 + 3 = 98307. - */ - if( u.ao.offset > 98307 ){ - rc = SQLITE_CORRUPT_BKPT; - goto op_column_out; - } - - /* Compute in u.ao.len the number of bytes of data we need to read in order - ** to get u.ao.nField type values. u.ao.offset is an upper bound on this. But - ** u.ao.nField might be significantly less than the true number of columns - ** in the table, and in that case, 5*u.ao.nField+3 might be smaller than u.ao.offset. - ** We want to minimize u.ao.len in order to limit the size of the memory - ** allocation, especially if a corrupt database file has caused u.ao.offset - ** to be oversized. Offset is limited to 98307 above. But 98307 might - ** still exceed Robson memory allocation limits on some configurations. - ** On systems that cannot tolerate large memory allocations, u.ao.nField*5+3 - ** will likely be much smaller since u.ao.nField will likely be less than - ** 20 or so. This insures that Robson memory allocation limits are - ** not exceeded even for corrupt database files. - */ - u.ao.len = u.ao.nField*5 + 3; - if( u.ao.len > (int)u.ao.offset ) u.ao.len = (int)u.ao.offset; - - /* The KeyFetch() or DataFetch() above are fast and will get the entire - ** record header in most cases. But they will fail to get the complete - ** record header if the record header does not fit on a single page - ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to - ** acquire the complete header text. - */ - if( !u.ao.zRec && u.ao.availisIndex, &u.ao.sMem); - if( rc!=SQLITE_OK ){ - goto op_column_out; - } - u.ao.zData = u.ao.sMem.z; - } - u.ao.zEndHdr = (u8 *)&u.ao.zData[u.ao.len]; - u.ao.zIdx = (u8 *)&u.ao.zData[u.ao.szHdr]; - - /* Scan the header and use it to fill in the u.ao.aType[] and u.ao.aOffset[] - ** arrays. u.ao.aType[u.ao.i] will contain the type integer for the u.ao.i-th - ** column and u.ao.aOffset[u.ao.i] will contain the u.ao.offset from the beginning - ** of the record to the start of the data for the u.ao.i-th column - */ - for(u.ao.i=0; u.ao.i u.ao.zEndHdr) || (u.ao.offset > u.ao.payloadSize) - || (u.ao.zIdx==u.ao.zEndHdr && u.ao.offset!=u.ao.payloadSize) ){ - rc = SQLITE_CORRUPT_BKPT; - goto op_column_out; - } - } - - /* Get the column information. If u.ao.aOffset[u.ao.p2] is non-zero, then - ** deserialize the value from the record. If u.ao.aOffset[u.ao.p2] is zero, - ** then there are not enough fields in the record to satisfy the - ** request. In this case, set the value NULL or to P4 if P4 is - ** a pointer to a Mem object. - */ - if( u.ao.aOffset[u.ao.p2] ){ - assert( rc==SQLITE_OK ); - if( u.ao.zRec ){ - /* This is the common case where the whole row fits on a single page */ - VdbeMemRelease(u.ao.pDest); - sqlite3VdbeSerialGet((u8 *)&u.ao.zRec[u.ao.aOffset[u.ao.p2]], u.ao.aType[u.ao.p2], u.ao.pDest); - }else{ - /* This branch happens only when the row overflows onto multiple pages */ - u.ao.t = u.ao.aType[u.ao.p2]; - if( (pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 - && ((u.ao.t>=12 && (u.ao.t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0) - ){ - /* Content is irrelevant for the typeof() function and for - ** the length(X) function if X is a blob. So we might as well use - ** bogus content rather than reading content from disk. NULL works - ** for text and blob and whatever is in the u.ao.payloadSize64 variable - ** will work for everything else. */ - u.ao.zData = u.ao.t<12 ? (char*)&u.ao.payloadSize64 : 0; - }else{ - u.ao.len = sqlite3VdbeSerialTypeLen(u.ao.t); - sqlite3VdbeMemMove(&u.ao.sMem, u.ao.pDest); - rc = sqlite3VdbeMemFromBtree(u.ao.pCrsr, u.ao.aOffset[u.ao.p2], u.ao.len, u.ao.pC->isIndex, - &u.ao.sMem); - if( rc!=SQLITE_OK ){ - goto op_column_out; - } - u.ao.zData = u.ao.sMem.z; - } - sqlite3VdbeSerialGet((u8*)u.ao.zData, u.ao.t, u.ao.pDest); - } - u.ao.pDest->enc = encoding; - }else{ - if( pOp->p4type==P4_MEM ){ - sqlite3VdbeMemShallowCopy(u.ao.pDest, pOp->p4.pMem, MEM_Static); - }else{ - MemSetTypeFlag(u.ao.pDest, MEM_Null); - } - } - - /* If we dynamically allocated space to hold the data (in the - ** sqlite3VdbeMemFromBtree() call above) then transfer control of that - ** dynamically allocated space over to the u.ao.pDest structure. - ** This prevents a memory copy. - */ - if( u.ao.sMem.zMalloc ){ - assert( u.ao.sMem.z==u.ao.sMem.zMalloc ); - assert( !(u.ao.pDest->flags & MEM_Dyn) ); - assert( !(u.ao.pDest->flags & (MEM_Blob|MEM_Str)) || u.ao.pDest->z==u.ao.sMem.z ); - u.ao.pDest->flags &= ~(MEM_Ephem|MEM_Static); - u.ao.pDest->flags |= MEM_Term; - u.ao.pDest->z = u.ao.sMem.z; - u.ao.pDest->zMalloc = u.ao.sMem.zMalloc; - } - - rc = sqlite3VdbeMemMakeWriteable(u.ao.pDest); - -op_column_out: - UPDATE_MAX_BLOBSIZE(u.ao.pDest); - REGISTER_TRACE(pOp->p3, u.ao.pDest); - break; -} - -/* Opcode: Affinity P1 P2 * P4 * -** -** Apply affinities to a range of P2 registers starting with P1. -** -** P4 is a string that is P2 characters long. The nth character of the -** string indicates the column affinity that should be used for the nth -** memory cell in the range. -*/ -case OP_Affinity: { -#if 0 /* local variables moved into u.ap */ - const char *zAffinity; /* The affinity to be applied */ - char cAff; /* A single character of affinity */ -#endif /* local variables moved into u.ap */ - - u.ap.zAffinity = pOp->p4.z; - assert( u.ap.zAffinity!=0 ); - assert( u.ap.zAffinity[pOp->p2]==0 ); - pIn1 = &aMem[pOp->p1]; - while( (u.ap.cAff = *(u.ap.zAffinity++))!=0 ){ - assert( pIn1 <= &p->aMem[p->nMem] ); - assert( memIsValid(pIn1) ); - ExpandBlob(pIn1); - applyAffinity(pIn1, u.ap.cAff, encoding); - pIn1++; - } - break; -} - -/* Opcode: MakeRecord P1 P2 P3 P4 * -** -** Convert P2 registers beginning with P1 into the [record format] -** use as a data record in a database table or as a key -** in an index. The OP_Column opcode can decode the record later. -** -** P4 may be a string that is P2 characters long. The nth character of the -** string indicates the column affinity that should be used for the nth -** field of the index key. -** -** The mapping from character to affinity is given by the SQLITE_AFF_ -** macros defined in sqliteInt.h. -** -** If P4 is NULL then all index fields have the affinity NONE. -*/ -case OP_MakeRecord: { -#if 0 /* local variables moved into u.aq */ - u8 *zNewRecord; /* A buffer to hold the data for the new record */ - Mem *pRec; /* The new record */ - u64 nData; /* Number of bytes of data space */ - int nHdr; /* Number of bytes of header space */ - i64 nByte; /* Data space required for this record */ - int nZero; /* Number of zero bytes at the end of the record */ - int nVarint; /* Number of bytes in a varint */ - u32 serial_type; /* Type field */ - Mem *pData0; /* First field to be combined into the record */ - Mem *pLast; /* Last field of the record */ - int nField; /* Number of fields in the record */ - char *zAffinity; /* The affinity string for the record */ - int file_format; /* File format to use for encoding */ - int i; /* Space used in zNewRecord[] */ - int len; /* Length of a field */ -#endif /* local variables moved into u.aq */ - - /* Assuming the record contains N fields, the record format looks - ** like this: - ** - ** ------------------------------------------------------------------------ - ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 | - ** ------------------------------------------------------------------------ - ** - ** Data(0) is taken from register P1. Data(1) comes from register P1+1 - ** and so froth. - ** - ** Each type field is a varint representing the serial type of the - ** corresponding data element (see sqlite3VdbeSerialType()). The - ** hdr-size field is also a varint which is the offset from the beginning - ** of the record to data0. - */ - u.aq.nData = 0; /* Number of bytes of data space */ - u.aq.nHdr = 0; /* Number of bytes of header space */ - u.aq.nZero = 0; /* Number of zero bytes at the end of the record */ - u.aq.nField = pOp->p1; - u.aq.zAffinity = pOp->p4.z; - assert( u.aq.nField>0 && pOp->p2>0 && pOp->p2+u.aq.nField<=p->nMem+1 ); - u.aq.pData0 = &aMem[u.aq.nField]; - u.aq.nField = pOp->p2; - u.aq.pLast = &u.aq.pData0[u.aq.nField-1]; - u.aq.file_format = p->minWriteFileFormat; - - /* Identify the output register */ - assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); - pOut = &aMem[pOp->p3]; - memAboutToChange(p, pOut); - - /* Loop through the elements that will make up the record to figure - ** out how much space is required for the new record. - */ - for(u.aq.pRec=u.aq.pData0; u.aq.pRec<=u.aq.pLast; u.aq.pRec++){ - assert( memIsValid(u.aq.pRec) ); - if( u.aq.zAffinity ){ - applyAffinity(u.aq.pRec, u.aq.zAffinity[u.aq.pRec-u.aq.pData0], encoding); - } - if( u.aq.pRec->flags&MEM_Zero && u.aq.pRec->n>0 ){ - sqlite3VdbeMemExpandBlob(u.aq.pRec); - } - u.aq.serial_type = sqlite3VdbeSerialType(u.aq.pRec, u.aq.file_format); - u.aq.len = sqlite3VdbeSerialTypeLen(u.aq.serial_type); - u.aq.nData += u.aq.len; - u.aq.nHdr += sqlite3VarintLen(u.aq.serial_type); - if( u.aq.pRec->flags & MEM_Zero ){ - /* Only pure zero-filled BLOBs can be input to this Opcode. - ** We do not allow blobs with a prefix and a zero-filled tail. */ - u.aq.nZero += u.aq.pRec->u.nZero; - }else if( u.aq.len ){ - u.aq.nZero = 0; - } - } - - /* Add the initial header varint and total the size */ - u.aq.nHdr += u.aq.nVarint = sqlite3VarintLen(u.aq.nHdr); - if( u.aq.nVarintdb->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - - /* Make sure the output register has a buffer large enough to store - ** the new record. The output register (pOp->p3) is not allowed to - ** be one of the input registers (because the following call to - ** sqlite3VdbeMemGrow() could clobber the value before it is used). - */ - if( sqlite3VdbeMemGrow(pOut, (int)u.aq.nByte, 0) ){ - goto no_mem; - } - u.aq.zNewRecord = (u8 *)pOut->z; - - /* Write the record */ - u.aq.i = putVarint32(u.aq.zNewRecord, u.aq.nHdr); - for(u.aq.pRec=u.aq.pData0; u.aq.pRec<=u.aq.pLast; u.aq.pRec++){ - u.aq.serial_type = sqlite3VdbeSerialType(u.aq.pRec, u.aq.file_format); - u.aq.i += putVarint32(&u.aq.zNewRecord[u.aq.i], u.aq.serial_type); /* serial type */ - } - for(u.aq.pRec=u.aq.pData0; u.aq.pRec<=u.aq.pLast; u.aq.pRec++){ /* serial data */ - u.aq.i += sqlite3VdbeSerialPut(&u.aq.zNewRecord[u.aq.i], (int)(u.aq.nByte-u.aq.i), u.aq.pRec,u.aq.file_format); - } - assert( u.aq.i==u.aq.nByte ); - - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - pOut->n = (int)u.aq.nByte; - pOut->flags = MEM_Blob | MEM_Dyn; - pOut->xDel = 0; - if( u.aq.nZero ){ - pOut->u.nZero = u.aq.nZero; - pOut->flags |= MEM_Zero; - } - pOut->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */ - REGISTER_TRACE(pOp->p3, pOut); - UPDATE_MAX_BLOBSIZE(pOut); - break; -} - -/* Opcode: Count P1 P2 * * * -** -** Store the number of entries (an integer value) in the table or index -** opened by cursor P1 in register P2 -*/ -#ifndef SQLITE_OMIT_BTREECOUNT -case OP_Count: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ar */ - i64 nEntry; - BtCursor *pCrsr; -#endif /* local variables moved into u.ar */ - - u.ar.pCrsr = p->apCsr[pOp->p1]->pCursor; - if( ALWAYS(u.ar.pCrsr) ){ - rc = sqlite3BtreeCount(u.ar.pCrsr, &u.ar.nEntry); - }else{ - u.ar.nEntry = 0; - } - pOut->u.i = u.ar.nEntry; - break; -} -#endif - -/* Opcode: Savepoint P1 * * P4 * -** -** Open, release or rollback the savepoint named by parameter P4, depending -** on the value of P1. To open a new savepoint, P1==0. To release (commit) an -** existing savepoint, P1==1, or to rollback an existing savepoint P1==2. -*/ -case OP_Savepoint: { -#if 0 /* local variables moved into u.as */ - int p1; /* Value of P1 operand */ - char *zName; /* Name of savepoint */ - int nName; - Savepoint *pNew; - Savepoint *pSavepoint; - Savepoint *pTmp; - int iSavepoint; - int ii; -#endif /* local variables moved into u.as */ - - u.as.p1 = pOp->p1; - u.as.zName = pOp->p4.z; - - /* Assert that the u.as.p1 parameter is valid. Also that if there is no open - ** transaction, then there cannot be any savepoints. - */ - assert( db->pSavepoint==0 || db->autoCommit==0 ); - assert( u.as.p1==SAVEPOINT_BEGIN||u.as.p1==SAVEPOINT_RELEASE||u.as.p1==SAVEPOINT_ROLLBACK ); - assert( db->pSavepoint || db->isTransactionSavepoint==0 ); - assert( checkSavepointCount(db) ); - - if( u.as.p1==SAVEPOINT_BEGIN ){ - if( db->writeVdbeCnt>0 ){ - /* A new savepoint cannot be created if there are active write - ** statements (i.e. open read/write incremental blob handles). - */ - sqlite3SetString(&p->zErrMsg, db, "cannot open savepoint - " - "SQL statements in progress"); - rc = SQLITE_BUSY; - }else{ - u.as.nName = sqlite3Strlen30(u.as.zName); - -#ifndef SQLITE_OMIT_VIRTUALTABLE - /* This call is Ok even if this savepoint is actually a transaction - ** savepoint (and therefore should not prompt xSavepoint()) callbacks. - ** If this is a transaction savepoint being opened, it is guaranteed - ** that the db->aVTrans[] array is empty. */ - assert( db->autoCommit==0 || db->nVTrans==0 ); - rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, - db->nStatement+db->nSavepoint); - if( rc!=SQLITE_OK ) goto abort_due_to_error; -#endif - - /* Create a new savepoint structure. */ - u.as.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.as.nName+1); - if( u.as.pNew ){ - u.as.pNew->zName = (char *)&u.as.pNew[1]; - memcpy(u.as.pNew->zName, u.as.zName, u.as.nName+1); - - /* If there is no open transaction, then mark this as a special - ** "transaction savepoint". */ - if( db->autoCommit ){ - db->autoCommit = 0; - db->isTransactionSavepoint = 1; - }else{ - db->nSavepoint++; - } - - /* Link the new savepoint into the database handle's list. */ - u.as.pNew->pNext = db->pSavepoint; - db->pSavepoint = u.as.pNew; - u.as.pNew->nDeferredCons = db->nDeferredCons; - } - } - }else{ - u.as.iSavepoint = 0; - - /* Find the named savepoint. If there is no such savepoint, then an - ** an error is returned to the user. */ - for( - u.as.pSavepoint = db->pSavepoint; - u.as.pSavepoint && sqlite3StrICmp(u.as.pSavepoint->zName, u.as.zName); - u.as.pSavepoint = u.as.pSavepoint->pNext - ){ - u.as.iSavepoint++; - } - if( !u.as.pSavepoint ){ - sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", u.as.zName); - rc = SQLITE_ERROR; - }else if( db->writeVdbeCnt>0 && u.as.p1==SAVEPOINT_RELEASE ){ - /* It is not possible to release (commit) a savepoint if there are - ** active write statements. - */ - sqlite3SetString(&p->zErrMsg, db, - "cannot release savepoint - SQL statements in progress" - ); - rc = SQLITE_BUSY; - }else{ - - /* Determine whether or not this is a transaction savepoint. If so, - ** and this is a RELEASE command, then the current transaction - ** is committed. - */ - int isTransaction = u.as.pSavepoint->pNext==0 && db->isTransactionSavepoint; - if( isTransaction && u.as.p1==SAVEPOINT_RELEASE ){ - if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ - goto vdbe_return; - } - db->autoCommit = 1; - if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = pc; - db->autoCommit = 0; - p->rc = rc = SQLITE_BUSY; - goto vdbe_return; - } - db->isTransactionSavepoint = 0; - rc = p->rc; - }else{ - u.as.iSavepoint = db->nSavepoint - u.as.iSavepoint - 1; - if( u.as.p1==SAVEPOINT_ROLLBACK ){ - for(u.as.ii=0; u.as.iinDb; u.as.ii++){ - sqlite3BtreeTripAllCursors(db->aDb[u.as.ii].pBt, SQLITE_ABORT); - } - } - for(u.as.ii=0; u.as.iinDb; u.as.ii++){ - rc = sqlite3BtreeSavepoint(db->aDb[u.as.ii].pBt, u.as.p1, u.as.iSavepoint); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - } - if( u.as.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ - sqlite3ExpirePreparedStatements(db); - sqlite3ResetAllSchemasOfConnection(db); - db->flags = (db->flags | SQLITE_InternChanges); - } - } - - /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all - ** savepoints nested inside of the savepoint being operated on. */ - while( db->pSavepoint!=u.as.pSavepoint ){ - u.as.pTmp = db->pSavepoint; - db->pSavepoint = u.as.pTmp->pNext; - sqlite3DbFree(db, u.as.pTmp); - db->nSavepoint--; - } - - /* If it is a RELEASE, then destroy the savepoint being operated on - ** too. If it is a ROLLBACK TO, then set the number of deferred - ** constraint violations present in the database to the value stored - ** when the savepoint was created. */ - if( u.as.p1==SAVEPOINT_RELEASE ){ - assert( u.as.pSavepoint==db->pSavepoint ); - db->pSavepoint = u.as.pSavepoint->pNext; - sqlite3DbFree(db, u.as.pSavepoint); - if( !isTransaction ){ - db->nSavepoint--; - } - }else{ - db->nDeferredCons = u.as.pSavepoint->nDeferredCons; - } - - if( !isTransaction ){ - rc = sqlite3VtabSavepoint(db, u.as.p1, u.as.iSavepoint); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - } - } - - break; -} - -/* Opcode: AutoCommit P1 P2 * * * -** -** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll -** back any currently active btree transactions. If there are any active -** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if -** there are active writing VMs or active VMs that use shared cache. -** -** This instruction causes the VM to halt. -*/ -case OP_AutoCommit: { -#if 0 /* local variables moved into u.at */ - int desiredAutoCommit; - int iRollback; - int turnOnAC; -#endif /* local variables moved into u.at */ - - u.at.desiredAutoCommit = pOp->p1; - u.at.iRollback = pOp->p2; - u.at.turnOnAC = u.at.desiredAutoCommit && !db->autoCommit; - assert( u.at.desiredAutoCommit==1 || u.at.desiredAutoCommit==0 ); - assert( u.at.desiredAutoCommit==1 || u.at.iRollback==0 ); - assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */ - -#if 0 - if( u.at.turnOnAC && u.at.iRollback && db->activeVdbeCnt>1 ){ - /* If this instruction implements a ROLLBACK and other VMs are - ** still running, and a transaction is active, return an error indicating - ** that the other VMs must complete first. - */ - sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - " - "SQL statements in progress"); - rc = SQLITE_BUSY; - }else -#endif - if( u.at.turnOnAC && !u.at.iRollback && db->writeVdbeCnt>0 ){ - /* If this instruction implements a COMMIT and other VMs are writing - ** return an error indicating that the other VMs must complete first. - */ - sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - " - "SQL statements in progress"); - rc = SQLITE_BUSY; - }else if( u.at.desiredAutoCommit!=db->autoCommit ){ - if( u.at.iRollback ){ - assert( u.at.desiredAutoCommit==1 ); - sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); - db->autoCommit = 1; - }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ - goto vdbe_return; - }else{ - db->autoCommit = (u8)u.at.desiredAutoCommit; - if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = pc; - db->autoCommit = (u8)(1-u.at.desiredAutoCommit); - p->rc = rc = SQLITE_BUSY; - goto vdbe_return; - } - } - assert( db->nStatement==0 ); - sqlite3CloseSavepoints(db); - if( p->rc==SQLITE_OK ){ - rc = SQLITE_DONE; - }else{ - rc = SQLITE_ERROR; - } - goto vdbe_return; - }else{ - sqlite3SetString(&p->zErrMsg, db, - (!u.at.desiredAutoCommit)?"cannot start a transaction within a transaction":( - (u.at.iRollback)?"cannot rollback - no transaction is active": - "cannot commit - no transaction is active")); - - rc = SQLITE_ERROR; - } - break; -} - -/* Opcode: Transaction P1 P2 * * * -** -** Begin a transaction. The transaction ends when a Commit or Rollback -** opcode is encountered. Depending on the ON CONFLICT setting, the -** transaction might also be rolled back if an error is encountered. -** -** P1 is the index of the database file on which the transaction is -** started. Index 0 is the main database file and index 1 is the -** file used for temporary tables. Indices of 2 or more are used for -** attached databases. -** -** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is -** obtained on the database file when a write-transaction is started. No -** other process can start another write transaction while this transaction is -** underway. Starting a write transaction also creates a rollback journal. A -** write transaction must be started before any changes can be made to the -** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained -** on the file. -** -** If a write-transaction is started and the Vdbe.usesStmtJournal flag is -** true (this flag is set if the Vdbe may modify more than one row and may -** throw an ABORT exception), a statement transaction may also be opened. -** More specifically, a statement transaction is opened iff the database -** connection is currently not in autocommit mode, or if there are other -** active statements. A statement transaction allows the changes made by this -** VDBE to be rolled back after an error without having to roll back the -** entire transaction. If no error is encountered, the statement transaction -** will automatically commit when the VDBE halts. -** -** If P2 is zero, then a read-lock is obtained on the database file. -*/ -case OP_Transaction: { -#if 0 /* local variables moved into u.au */ - Btree *pBt; -#endif /* local variables moved into u.au */ - - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - u.au.pBt = db->aDb[pOp->p1].pBt; - - if( u.au.pBt ){ - rc = sqlite3BtreeBeginTrans(u.au.pBt, pOp->p2); - if( rc==SQLITE_BUSY ){ - p->pc = pc; - p->rc = rc = SQLITE_BUSY; - goto vdbe_return; - } - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - - if( pOp->p2 && p->usesStmtJournal - && (db->autoCommit==0 || db->activeVdbeCnt>1) - ){ - assert( sqlite3BtreeIsInTrans(u.au.pBt) ); - if( p->iStatement==0 ){ - assert( db->nStatement>=0 && db->nSavepoint>=0 ); - db->nStatement++; - p->iStatement = db->nSavepoint + db->nStatement; - } - - rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginStmt(u.au.pBt, p->iStatement); - } - - /* Store the current value of the database handles deferred constraint - ** counter. If the statement transaction needs to be rolled back, - ** the value of this counter needs to be restored too. */ - p->nStmtDefCons = db->nDeferredCons; - } - } - break; -} - -/* Opcode: ReadCookie P1 P2 P3 * * -** -** Read cookie number P3 from database P1 and write it into register P2. -** P3==1 is the schema version. P3==2 is the database format. -** P3==3 is the recommended pager cache size, and so forth. P1==0 is -** the main database file and P1==1 is the database file used to store -** temporary tables. -** -** There must be a read-lock on the database (either a transaction -** must be started or there must be an open cursor) before -** executing this instruction. -*/ -case OP_ReadCookie: { /* out2-prerelease */ -#if 0 /* local variables moved into u.av */ - int iMeta; - int iDb; - int iCookie; -#endif /* local variables moved into u.av */ - - u.av.iDb = pOp->p1; - u.av.iCookie = pOp->p3; - assert( pOp->p3=0 && u.av.iDbnDb ); - assert( db->aDb[u.av.iDb].pBt!=0 ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[u.av.iDb].pBt, u.av.iCookie, (u32 *)&u.av.iMeta); - pOut->u.i = u.av.iMeta; - break; -} - -/* Opcode: SetCookie P1 P2 P3 * * -** -** Write the content of register P3 (interpreted as an integer) -** into cookie number P2 of database P1. P2==1 is the schema version. -** P2==2 is the database format. P2==3 is the recommended pager cache -** size, and so forth. P1==0 is the main database file and P1==1 is the -** database file used to store temporary tables. -** -** A transaction must be started before executing this opcode. -*/ -case OP_SetCookie: { /* in3 */ -#if 0 /* local variables moved into u.aw */ - Db *pDb; -#endif /* local variables moved into u.aw */ - assert( pOp->p2p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - u.aw.pDb = &db->aDb[pOp->p1]; - assert( u.aw.pDb->pBt!=0 ); - assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); - pIn3 = &aMem[pOp->p3]; - sqlite3VdbeMemIntegerify(pIn3); - /* See note about index shifting on OP_ReadCookie */ - rc = sqlite3BtreeUpdateMeta(u.aw.pDb->pBt, pOp->p2, (int)pIn3->u.i); - if( pOp->p2==BTREE_SCHEMA_VERSION ){ - /* When the schema cookie changes, record the new cookie internally */ - u.aw.pDb->pSchema->schema_cookie = (int)pIn3->u.i; - db->flags |= SQLITE_InternChanges; - }else if( pOp->p2==BTREE_FILE_FORMAT ){ - /* Record changes in the file format */ - u.aw.pDb->pSchema->file_format = (u8)pIn3->u.i; - } - if( pOp->p1==1 ){ - /* Invalidate all prepared statements whenever the TEMP database - ** schema is changed. Ticket #1644 */ - sqlite3ExpirePreparedStatements(db); - p->expired = 0; - } - break; -} - -/* Opcode: VerifyCookie P1 P2 P3 * * -** -** Check the value of global database parameter number 0 (the -** schema version) and make sure it is equal to P2 and that the -** generation counter on the local schema parse equals P3. -** -** P1 is the database number which is 0 for the main database file -** and 1 for the file holding temporary tables and some higher number -** for auxiliary databases. -** -** The cookie changes its value whenever the database schema changes. -** This operation is used to detect when that the cookie has changed -** and that the current process needs to reread the schema. -** -** Either a transaction needs to have been started or an OP_Open needs -** to be executed (to establish a read lock) before this opcode is -** invoked. -*/ -case OP_VerifyCookie: { -#if 0 /* local variables moved into u.ax */ - int iMeta; - int iGen; - Btree *pBt; -#endif /* local variables moved into u.ax */ - - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); - u.ax.pBt = db->aDb[pOp->p1].pBt; - if( u.ax.pBt ){ - sqlite3BtreeGetMeta(u.ax.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.ax.iMeta); - u.ax.iGen = db->aDb[pOp->p1].pSchema->iGeneration; - }else{ - u.ax.iGen = u.ax.iMeta = 0; - } - if( u.ax.iMeta!=pOp->p2 || u.ax.iGen!=pOp->p3 ){ - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); - /* If the schema-cookie from the database file matches the cookie - ** stored with the in-memory representation of the schema, do - ** not reload the schema from the database file. - ** - ** If virtual-tables are in use, this is not just an optimization. - ** Often, v-tables store their data in other SQLite tables, which - ** are queried from within xNext() and other v-table methods using - ** prepared queries. If such a query is out-of-date, we do not want to - ** discard the database schema, as the user code implementing the - ** v-table would have to be ready for the sqlite3_vtab structure itself - ** to be invalidated whenever sqlite3_step() is called from within - ** a v-table method. - */ - if( db->aDb[pOp->p1].pSchema->schema_cookie!=u.ax.iMeta ){ - sqlite3ResetOneSchema(db, pOp->p1); - } - - p->expired = 1; - rc = SQLITE_SCHEMA; - } - break; -} - -/* Opcode: OpenRead P1 P2 P3 P4 P5 -** -** Open a read-only cursor for the database table whose root page is -** P2 in a database file. The database file is determined by P3. -** P3==0 means the main database, P3==1 means the database used for -** temporary tables, and P3>1 means used the corresponding attached -** database. Give the new cursor an identifier of P1. The P1 -** values need not be contiguous but all P1 values should be small integers. -** It is an error for P1 to be negative. -** -** If P5!=0 then use the content of register P2 as the root page, not -** the value of P2 itself. -** -** There will be a read lock on the database whenever there is an -** open cursor. If the database was unlocked prior to this instruction -** then a read lock is acquired as part of this instruction. A read -** lock allows other processes to read the database but prohibits -** any other process from modifying the database. The read lock is -** released when all cursors are closed. If this instruction attempts -** to get a read lock but fails, the script terminates with an -** SQLITE_BUSY error code. -** -** The P4 value may be either an integer (P4_INT32) or a pointer to -** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo -** structure, then said structure defines the content and collating -** sequence of the index being opened. Otherwise, if P4 is an integer -** value, it is set to the number of columns in the table. -** -** See also OpenWrite. -*/ -/* Opcode: OpenWrite P1 P2 P3 P4 P5 -** -** Open a read/write cursor named P1 on the table or index whose root -** page is P2. Or if P5!=0 use the content of register P2 to find the -** root page. -** -** The P4 value may be either an integer (P4_INT32) or a pointer to -** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo -** structure, then said structure defines the content and collating -** sequence of the index being opened. Otherwise, if P4 is an integer -** value, it is set to the number of columns in the table, or to the -** largest index of any column of the table that is actually used. -** -** This instruction works just like OpenRead except that it opens the cursor -** in read/write mode. For a given table, there can be one or more read-only -** cursors or a single read/write cursor but not both. -** -** See also OpenRead. -*/ -case OP_OpenRead: -case OP_OpenWrite: { -#if 0 /* local variables moved into u.ay */ - int nField; - KeyInfo *pKeyInfo; - int p2; - int iDb; - int wrFlag; - Btree *pX; - VdbeCursor *pCur; - Db *pDb; -#endif /* local variables moved into u.ay */ - - assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 ); - assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 ); - - if( p->expired ){ - rc = SQLITE_ABORT; - break; - } - - u.ay.nField = 0; - u.ay.pKeyInfo = 0; - u.ay.p2 = pOp->p2; - u.ay.iDb = pOp->p3; - assert( u.ay.iDb>=0 && u.ay.iDbnDb ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[u.ay.iDb]; - u.ay.pX = u.ay.pDb->pBt; - assert( u.ay.pX!=0 ); - if( pOp->opcode==OP_OpenWrite ){ - u.ay.wrFlag = 1; - assert( sqlite3SchemaMutexHeld(db, u.ay.iDb, 0) ); - if( u.ay.pDb->pSchema->file_format < p->minWriteFileFormat ){ - p->minWriteFileFormat = u.ay.pDb->pSchema->file_format; - } - }else{ - u.ay.wrFlag = 0; - } - if( pOp->p5 & OPFLAG_P2ISREG ){ - assert( u.ay.p2>0 ); - assert( u.ay.p2<=p->nMem ); - pIn2 = &aMem[u.ay.p2]; - assert( memIsValid(pIn2) ); - assert( (pIn2->flags & MEM_Int)!=0 ); - sqlite3VdbeMemIntegerify(pIn2); - u.ay.p2 = (int)pIn2->u.i; - /* The u.ay.p2 value always comes from a prior OP_CreateTable opcode and - ** that opcode will always set the u.ay.p2 value to 2 or more or else fail. - ** If there were a failure, the prepared statement would have halted - ** before reaching this instruction. */ - if( NEVER(u.ay.p2<2) ) { - rc = SQLITE_CORRUPT_BKPT; - goto abort_due_to_error; - } - } - if( pOp->p4type==P4_KEYINFO ){ - u.ay.pKeyInfo = pOp->p4.pKeyInfo; - u.ay.pKeyInfo->enc = ENC(p->db); - u.ay.nField = u.ay.pKeyInfo->nField+1; - }else if( pOp->p4type==P4_INT32 ){ - u.ay.nField = pOp->p4.i; - } - assert( pOp->p1>=0 ); - u.ay.pCur = allocateCursor(p, pOp->p1, u.ay.nField, u.ay.iDb, 1); - if( u.ay.pCur==0 ) goto no_mem; - u.ay.pCur->nullRow = 1; - u.ay.pCur->isOrdered = 1; - rc = sqlite3BtreeCursor(u.ay.pX, u.ay.p2, u.ay.wrFlag, u.ay.pKeyInfo, u.ay.pCur->pCursor); - u.ay.pCur->pKeyInfo = u.ay.pKeyInfo; - assert( OPFLAG_BULKCSR==BTREE_BULKLOAD ); - sqlite3BtreeCursorHints(u.ay.pCur->pCursor, (pOp->p5 & OPFLAG_BULKCSR)); - - /* Since it performs no memory allocation or IO, the only value that - ** sqlite3BtreeCursor() may return is SQLITE_OK. */ - assert( rc==SQLITE_OK ); - - /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of - ** SQLite used to check if the root-page flags were sane at this point - ** and report database corruption if they were not, but this check has - ** since moved into the btree layer. */ - u.ay.pCur->isTable = pOp->p4type!=P4_KEYINFO; - u.ay.pCur->isIndex = !u.ay.pCur->isTable; - break; -} - -/* Opcode: OpenEphemeral P1 P2 * P4 P5 -** -** Open a new cursor P1 to a transient table. -** The cursor is always opened read/write even if -** the main database is read-only. The ephemeral -** table is deleted automatically when the cursor is closed. -** -** P2 is the number of columns in the ephemeral table. -** The cursor points to a BTree table if P4==0 and to a BTree index -** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure -** that defines the format of keys in the index. -** -** This opcode was once called OpenTemp. But that created -** confusion because the term "temp table", might refer either -** to a TEMP table at the SQL level, or to a table opened by -** this opcode. Then this opcode was call OpenVirtual. But -** that created confusion with the whole virtual-table idea. -** -** The P5 parameter can be a mask of the BTREE_* flags defined -** in btree.h. These flags control aspects of the operation of -** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are -** added automatically. -*/ -/* Opcode: OpenAutoindex P1 P2 * P4 * -** -** This opcode works the same as OP_OpenEphemeral. It has a -** different name to distinguish its use. Tables created using -** by this opcode will be used for automatically created transient -** indices in joins. -*/ -case OP_OpenAutoindex: -case OP_OpenEphemeral: { -#if 0 /* local variables moved into u.az */ - VdbeCursor *pCx; -#endif /* local variables moved into u.az */ - static const int vfsFlags = - SQLITE_OPEN_READWRITE | - SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXCLUSIVE | - SQLITE_OPEN_DELETEONCLOSE | - SQLITE_OPEN_TRANSIENT_DB; - - assert( pOp->p1>=0 ); - u.az.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); - if( u.az.pCx==0 ) goto no_mem; - u.az.pCx->nullRow = 1; - rc = sqlite3BtreeOpen(db->pVfs, 0, db, &u.az.pCx->pBt, - BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginTrans(u.az.pCx->pBt, 1); - } - if( rc==SQLITE_OK ){ - /* If a transient index is required, create it by calling - ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before - ** opening it. If a transient table is required, just use the - ** automatically created table with root-page 1 (an BLOB_INTKEY table). - */ - if( pOp->p4.pKeyInfo ){ - int pgno; - assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite3BtreeCreateTable(u.az.pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5); - if( rc==SQLITE_OK ){ - assert( pgno==MASTER_ROOT+1 ); - rc = sqlite3BtreeCursor(u.az.pCx->pBt, pgno, 1, - (KeyInfo*)pOp->p4.z, u.az.pCx->pCursor); - u.az.pCx->pKeyInfo = pOp->p4.pKeyInfo; - u.az.pCx->pKeyInfo->enc = ENC(p->db); - } - u.az.pCx->isTable = 0; - }else{ - rc = sqlite3BtreeCursor(u.az.pCx->pBt, MASTER_ROOT, 1, 0, u.az.pCx->pCursor); - u.az.pCx->isTable = 1; - } - } - u.az.pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); - u.az.pCx->isIndex = !u.az.pCx->isTable; - break; -} - -/* Opcode: SorterOpen P1 P2 * P4 * -** -** This opcode works like OP_OpenEphemeral except that it opens -** a transient index that is specifically designed to sort large -** tables using an external merge-sort algorithm. -*/ -case OP_SorterOpen: { -#if 0 /* local variables moved into u.ba */ - VdbeCursor *pCx; -#endif /* local variables moved into u.ba */ - - u.ba.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); - if( u.ba.pCx==0 ) goto no_mem; - u.ba.pCx->pKeyInfo = pOp->p4.pKeyInfo; - u.ba.pCx->pKeyInfo->enc = ENC(p->db); - u.ba.pCx->isSorter = 1; - rc = sqlite3VdbeSorterInit(db, u.ba.pCx); - break; -} - -/* Opcode: OpenPseudo P1 P2 P3 * P5 -** -** Open a new cursor that points to a fake table that contains a single -** row of data. The content of that one row in the content of memory -** register P2 when P5==0. In other words, cursor P1 becomes an alias for the -** MEM_Blob content contained in register P2. When P5==1, then the -** row is represented by P3 consecutive registers beginning with P2. -** -** A pseudo-table created by this opcode is used to hold a single -** row output from the sorter so that the row can be decomposed into -** individual columns using the OP_Column opcode. The OP_Column opcode -** is the only cursor opcode that works with a pseudo-table. -** -** P3 is the number of fields in the records that will be stored by -** the pseudo-table. -*/ -case OP_OpenPseudo: { -#if 0 /* local variables moved into u.bb */ - VdbeCursor *pCx; -#endif /* local variables moved into u.bb */ - - assert( pOp->p1>=0 ); - u.bb.pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); - if( u.bb.pCx==0 ) goto no_mem; - u.bb.pCx->nullRow = 1; - u.bb.pCx->pseudoTableReg = pOp->p2; - u.bb.pCx->isTable = 1; - u.bb.pCx->isIndex = 0; - u.bb.pCx->multiPseudo = pOp->p5; - break; -} - -/* Opcode: Close P1 * * * * -** -** Close a cursor previously opened as P1. If P1 is not -** currently open, this instruction is a no-op. -*/ -case OP_Close: { - assert( pOp->p1>=0 && pOp->p1nCursor ); - sqlite3VdbeFreeCursor(p, p->apCsr[pOp->p1]); - p->apCsr[pOp->p1] = 0; - break; -} - -/* Opcode: SeekGe P1 P2 P3 P4 * -** -** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), -** use the value in register P3 as the key. If cursor P1 refers -** to an SQL index, then P3 is the first in an array of P4 registers -** that are used as an unpacked index key. -** -** Reposition cursor P1 so that it points to the smallest entry that -** is greater than or equal to the key value. If there are no records -** greater than or equal to the key and P2 is not zero, then jump to P2. -** -** See also: Found, NotFound, Distinct, SeekLt, SeekGt, SeekLe -*/ -/* Opcode: SeekGt P1 P2 P3 P4 * -** -** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), -** use the value in register P3 as a key. If cursor P1 refers -** to an SQL index, then P3 is the first in an array of P4 registers -** that are used as an unpacked index key. -** -** Reposition cursor P1 so that it points to the smallest entry that -** is greater than the key value. If there are no records greater than -** the key and P2 is not zero, then jump to P2. -** -** See also: Found, NotFound, Distinct, SeekLt, SeekGe, SeekLe -*/ -/* Opcode: SeekLt P1 P2 P3 P4 * -** -** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), -** use the value in register P3 as a key. If cursor P1 refers -** to an SQL index, then P3 is the first in an array of P4 registers -** that are used as an unpacked index key. -** -** Reposition cursor P1 so that it points to the largest entry that -** is less than the key value. If there are no records less than -** the key and P2 is not zero, then jump to P2. -** -** See also: Found, NotFound, Distinct, SeekGt, SeekGe, SeekLe -*/ -/* Opcode: SeekLe P1 P2 P3 P4 * -** -** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), -** use the value in register P3 as a key. If cursor P1 refers -** to an SQL index, then P3 is the first in an array of P4 registers -** that are used as an unpacked index key. -** -** Reposition cursor P1 so that it points to the largest entry that -** is less than or equal to the key value. If there are no records -** less than or equal to the key and P2 is not zero, then jump to P2. -** -** See also: Found, NotFound, Distinct, SeekGt, SeekGe, SeekLt -*/ -case OP_SeekLt: /* jump, in3 */ -case OP_SeekLe: /* jump, in3 */ -case OP_SeekGe: /* jump, in3 */ -case OP_SeekGt: { /* jump, in3 */ -#if 0 /* local variables moved into u.bc */ - int res; - int oc; - VdbeCursor *pC; - UnpackedRecord r; - int nField; - i64 iKey; /* The rowid we are to seek to */ -#endif /* local variables moved into u.bc */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p2!=0 ); - u.bc.pC = p->apCsr[pOp->p1]; - assert( u.bc.pC!=0 ); - assert( u.bc.pC->pseudoTableReg==0 ); - assert( OP_SeekLe == OP_SeekLt+1 ); - assert( OP_SeekGe == OP_SeekLt+2 ); - assert( OP_SeekGt == OP_SeekLt+3 ); - assert( u.bc.pC->isOrdered ); - if( ALWAYS(u.bc.pC->pCursor!=0) ){ - u.bc.oc = pOp->opcode; - u.bc.pC->nullRow = 0; - if( u.bc.pC->isTable ){ - /* The input value in P3 might be of any type: integer, real, string, - ** blob, or NULL. But it needs to be an integer before we can do - ** the seek, so covert it. */ - pIn3 = &aMem[pOp->p3]; - applyNumericAffinity(pIn3); - u.bc.iKey = sqlite3VdbeIntValue(pIn3); - u.bc.pC->rowidIsValid = 0; - - /* If the P3 value could not be converted into an integer without - ** loss of information, then special processing is required... */ - if( (pIn3->flags & MEM_Int)==0 ){ - if( (pIn3->flags & MEM_Real)==0 ){ - /* If the P3 value cannot be converted into any kind of a number, - ** then the seek is not possible, so jump to P2 */ - pc = pOp->p2 - 1; - break; - } - /* If we reach this point, then the P3 value must be a floating - ** point number. */ - assert( (pIn3->flags & MEM_Real)!=0 ); - - if( u.bc.iKey==SMALLEST_INT64 && (pIn3->r<(double)u.bc.iKey || pIn3->r>0) ){ - /* The P3 value is too large in magnitude to be expressed as an - ** integer. */ - u.bc.res = 1; - if( pIn3->r<0 ){ - if( u.bc.oc>=OP_SeekGe ){ assert( u.bc.oc==OP_SeekGe || u.bc.oc==OP_SeekGt ); - rc = sqlite3BtreeFirst(u.bc.pC->pCursor, &u.bc.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - }else{ - if( u.bc.oc<=OP_SeekLe ){ assert( u.bc.oc==OP_SeekLt || u.bc.oc==OP_SeekLe ); - rc = sqlite3BtreeLast(u.bc.pC->pCursor, &u.bc.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - } - if( u.bc.res ){ - pc = pOp->p2 - 1; - } - break; - }else if( u.bc.oc==OP_SeekLt || u.bc.oc==OP_SeekGe ){ - /* Use the ceiling() function to convert real->int */ - if( pIn3->r > (double)u.bc.iKey ) u.bc.iKey++; - }else{ - /* Use the floor() function to convert real->int */ - assert( u.bc.oc==OP_SeekLe || u.bc.oc==OP_SeekGt ); - if( pIn3->r < (double)u.bc.iKey ) u.bc.iKey--; - } - } - rc = sqlite3BtreeMovetoUnpacked(u.bc.pC->pCursor, 0, (u64)u.bc.iKey, 0, &u.bc.res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( u.bc.res==0 ){ - u.bc.pC->rowidIsValid = 1; - u.bc.pC->lastRowid = u.bc.iKey; - } - }else{ - u.bc.nField = pOp->p4.i; - assert( pOp->p4type==P4_INT32 ); - assert( u.bc.nField>0 ); - u.bc.r.pKeyInfo = u.bc.pC->pKeyInfo; - u.bc.r.nField = (u16)u.bc.nField; - - /* The next line of code computes as follows, only faster: - ** if( u.bc.oc==OP_SeekGt || u.bc.oc==OP_SeekLe ){ - ** u.bc.r.flags = UNPACKED_INCRKEY; - ** }else{ - ** u.bc.r.flags = 0; - ** } - */ - u.bc.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.bc.oc - OP_SeekLt))); - assert( u.bc.oc!=OP_SeekGt || u.bc.r.flags==UNPACKED_INCRKEY ); - assert( u.bc.oc!=OP_SeekLe || u.bc.r.flags==UNPACKED_INCRKEY ); - assert( u.bc.oc!=OP_SeekGe || u.bc.r.flags==0 ); - assert( u.bc.oc!=OP_SeekLt || u.bc.r.flags==0 ); - - u.bc.r.aMem = &aMem[pOp->p3]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ipCursor, &u.bc.r, 0, 0, &u.bc.res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - u.bc.pC->rowidIsValid = 0; - } - u.bc.pC->deferredMoveto = 0; - u.bc.pC->cacheStatus = CACHE_STALE; -#ifdef SQLITE_TEST - sqlite3_search_count++; -#endif - if( u.bc.oc>=OP_SeekGe ){ assert( u.bc.oc==OP_SeekGe || u.bc.oc==OP_SeekGt ); - if( u.bc.res<0 || (u.bc.res==0 && u.bc.oc==OP_SeekGt) ){ - rc = sqlite3BtreeNext(u.bc.pC->pCursor, &u.bc.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - u.bc.pC->rowidIsValid = 0; - }else{ - u.bc.res = 0; - } - }else{ - assert( u.bc.oc==OP_SeekLt || u.bc.oc==OP_SeekLe ); - if( u.bc.res>0 || (u.bc.res==0 && u.bc.oc==OP_SeekLt) ){ - rc = sqlite3BtreePrevious(u.bc.pC->pCursor, &u.bc.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - u.bc.pC->rowidIsValid = 0; - }else{ - /* u.bc.res might be negative because the table is empty. Check to - ** see if this is the case. - */ - u.bc.res = sqlite3BtreeEof(u.bc.pC->pCursor); - } - } - assert( pOp->p2>0 ); - if( u.bc.res ){ - pc = pOp->p2 - 1; - } - }else{ - /* This happens when attempting to open the sqlite3_master table - ** for read access returns SQLITE_EMPTY. In this case always - ** take the jump (since there are no records in the table). - */ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: Seek P1 P2 * * * -** -** P1 is an open table cursor and P2 is a rowid integer. Arrange -** for P1 to move so that it points to the rowid given by P2. -** -** This is actually a deferred seek. Nothing actually happens until -** the cursor is used to read a record. That way, if no reads -** occur, no unnecessary I/O happens. -*/ -case OP_Seek: { /* in2 */ -#if 0 /* local variables moved into u.bd */ - VdbeCursor *pC; -#endif /* local variables moved into u.bd */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bd.pC = p->apCsr[pOp->p1]; - assert( u.bd.pC!=0 ); - if( ALWAYS(u.bd.pC->pCursor!=0) ){ - assert( u.bd.pC->isTable ); - u.bd.pC->nullRow = 0; - pIn2 = &aMem[pOp->p2]; - u.bd.pC->movetoTarget = sqlite3VdbeIntValue(pIn2); - u.bd.pC->rowidIsValid = 0; - u.bd.pC->deferredMoveto = 1; - } - break; -} - - -/* Opcode: Found P1 P2 P3 P4 * -** -** If P4==0 then register P3 holds a blob constructed by MakeRecord. If -** P4>0 then register P3 is the first of P4 registers that form an unpacked -** record. -** -** Cursor P1 is on an index btree. If the record identified by P3 and P4 -** is a prefix of any entry in P1 then a jump is made to P2 and -** P1 is left pointing at the matching entry. -*/ -/* Opcode: NotFound P1 P2 P3 P4 * -** -** If P4==0 then register P3 holds a blob constructed by MakeRecord. If -** P4>0 then register P3 is the first of P4 registers that form an unpacked -** record. -** -** Cursor P1 is on an index btree. If the record identified by P3 and P4 -** is not the prefix of any entry in P1 then a jump is made to P2. If P1 -** does contain an entry whose prefix matches the P3/P4 record then control -** falls through to the next instruction and P1 is left pointing at the -** matching entry. -** -** See also: Found, NotExists, IsUnique -*/ -case OP_NotFound: /* jump, in3 */ -case OP_Found: { /* jump, in3 */ -#if 0 /* local variables moved into u.be */ - int alreadyExists; - VdbeCursor *pC; - int res; - char *pFree; - UnpackedRecord *pIdxKey; - UnpackedRecord r; - char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; -#endif /* local variables moved into u.be */ - -#ifdef SQLITE_TEST - sqlite3_found_count++; -#endif - - u.be.alreadyExists = 0; - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p4type==P4_INT32 ); - u.be.pC = p->apCsr[pOp->p1]; - assert( u.be.pC!=0 ); - pIn3 = &aMem[pOp->p3]; - if( ALWAYS(u.be.pC->pCursor!=0) ){ - - assert( u.be.pC->isTable==0 ); - if( pOp->p4.i>0 ){ - u.be.r.pKeyInfo = u.be.pC->pKeyInfo; - u.be.r.nField = (u16)pOp->p4.i; - u.be.r.aMem = pIn3; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ipKeyInfo, u.be.aTempRec, sizeof(u.be.aTempRec), &u.be.pFree - ); - if( u.be.pIdxKey==0 ) goto no_mem; - assert( pIn3->flags & MEM_Blob ); - assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ - sqlite3VdbeRecordUnpack(u.be.pC->pKeyInfo, pIn3->n, pIn3->z, u.be.pIdxKey); - u.be.pIdxKey->flags |= UNPACKED_PREFIX_MATCH; - } - rc = sqlite3BtreeMovetoUnpacked(u.be.pC->pCursor, u.be.pIdxKey, 0, 0, &u.be.res); - if( pOp->p4.i==0 ){ - sqlite3DbFree(db, u.be.pFree); - } - if( rc!=SQLITE_OK ){ - break; - } - u.be.alreadyExists = (u.be.res==0); - u.be.pC->deferredMoveto = 0; - u.be.pC->cacheStatus = CACHE_STALE; - } - if( pOp->opcode==OP_Found ){ - if( u.be.alreadyExists ) pc = pOp->p2 - 1; - }else{ - if( !u.be.alreadyExists ) pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: IsUnique P1 P2 P3 P4 * -** -** Cursor P1 is open on an index b-tree - that is to say, a btree which -** no data and where the key are records generated by OP_MakeRecord with -** the list field being the integer ROWID of the entry that the index -** entry refers to. -** -** The P3 register contains an integer record number. Call this record -** number R. Register P4 is the first in a set of N contiguous registers -** that make up an unpacked index key that can be used with cursor P1. -** The value of N can be inferred from the cursor. N includes the rowid -** value appended to the end of the index record. This rowid value may -** or may not be the same as R. -** -** If any of the N registers beginning with register P4 contains a NULL -** value, jump immediately to P2. -** -** Otherwise, this instruction checks if cursor P1 contains an entry -** where the first (N-1) fields match but the rowid value at the end -** of the index entry is not R. If there is no such entry, control jumps -** to instruction P2. Otherwise, the rowid of the conflicting index -** entry is copied to register P3 and control falls through to the next -** instruction. -** -** See also: NotFound, NotExists, Found -*/ -case OP_IsUnique: { /* jump, in3 */ -#if 0 /* local variables moved into u.bf */ - u16 ii; - VdbeCursor *pCx; - BtCursor *pCrsr; - u16 nField; - Mem *aMx; - UnpackedRecord r; /* B-Tree index search key */ - i64 R; /* Rowid stored in register P3 */ -#endif /* local variables moved into u.bf */ - - pIn3 = &aMem[pOp->p3]; - u.bf.aMx = &aMem[pOp->p4.i]; - /* Assert that the values of parameters P1 and P4 are in range. */ - assert( pOp->p4type==P4_INT32 ); - assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - - /* Find the index cursor. */ - u.bf.pCx = p->apCsr[pOp->p1]; - assert( u.bf.pCx->deferredMoveto==0 ); - u.bf.pCx->seekResult = 0; - u.bf.pCx->cacheStatus = CACHE_STALE; - u.bf.pCrsr = u.bf.pCx->pCursor; - - /* If any of the values are NULL, take the jump. */ - u.bf.nField = u.bf.pCx->pKeyInfo->nField; - for(u.bf.ii=0; u.bf.iip2 - 1; - u.bf.pCrsr = 0; - break; - } - } - assert( (u.bf.aMx[u.bf.nField].flags & MEM_Null)==0 ); - - if( u.bf.pCrsr!=0 ){ - /* Populate the index search key. */ - u.bf.r.pKeyInfo = u.bf.pCx->pKeyInfo; - u.bf.r.nField = u.bf.nField + 1; - u.bf.r.flags = UNPACKED_PREFIX_SEARCH; - u.bf.r.aMem = u.bf.aMx; -#ifdef SQLITE_DEBUG - { int i; for(i=0; iu.i; - - /* Search the B-Tree index. If no conflicting record is found, jump - ** to P2. Otherwise, copy the rowid of the conflicting record to - ** register P3 and fall through to the next instruction. */ - rc = sqlite3BtreeMovetoUnpacked(u.bf.pCrsr, &u.bf.r, 0, 0, &u.bf.pCx->seekResult); - if( (u.bf.r.flags & UNPACKED_PREFIX_SEARCH) || u.bf.r.rowid==u.bf.R ){ - pc = pOp->p2 - 1; - }else{ - pIn3->u.i = u.bf.r.rowid; - } - } - break; -} - -/* Opcode: NotExists P1 P2 P3 * * -** -** Use the content of register P3 as an integer key. If a record -** with that key does not exist in table of P1, then jump to P2. -** If the record does exist, then fall through. The cursor is left -** pointing to the record if it exists. -** -** The difference between this operation and NotFound is that this -** operation assumes the key is an integer and that P1 is a table whereas -** NotFound assumes key is a blob constructed from MakeRecord and -** P1 is an index. -** -** See also: Found, NotFound, IsUnique -*/ -case OP_NotExists: { /* jump, in3 */ -#if 0 /* local variables moved into u.bg */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - u64 iKey; -#endif /* local variables moved into u.bg */ - - pIn3 = &aMem[pOp->p3]; - assert( pIn3->flags & MEM_Int ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bg.pC = p->apCsr[pOp->p1]; - assert( u.bg.pC!=0 ); - assert( u.bg.pC->isTable ); - assert( u.bg.pC->pseudoTableReg==0 ); - u.bg.pCrsr = u.bg.pC->pCursor; - if( ALWAYS(u.bg.pCrsr!=0) ){ - u.bg.res = 0; - u.bg.iKey = pIn3->u.i; - rc = sqlite3BtreeMovetoUnpacked(u.bg.pCrsr, 0, u.bg.iKey, 0, &u.bg.res); - u.bg.pC->lastRowid = pIn3->u.i; - u.bg.pC->rowidIsValid = u.bg.res==0 ?1:0; - u.bg.pC->nullRow = 0; - u.bg.pC->cacheStatus = CACHE_STALE; - u.bg.pC->deferredMoveto = 0; - if( u.bg.res!=0 ){ - pc = pOp->p2 - 1; - assert( u.bg.pC->rowidIsValid==0 ); - } - u.bg.pC->seekResult = u.bg.res; - }else{ - /* This happens when an attempt to open a read cursor on the - ** sqlite_master table returns SQLITE_EMPTY. - */ - pc = pOp->p2 - 1; - assert( u.bg.pC->rowidIsValid==0 ); - u.bg.pC->seekResult = 0; - } - break; -} - -/* Opcode: Sequence P1 P2 * * * -** -** Find the next available sequence number for cursor P1. -** Write the sequence number into register P2. -** The sequence number on the cursor is incremented after this -** instruction. -*/ -case OP_Sequence: { /* out2-prerelease */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( p->apCsr[pOp->p1]!=0 ); - pOut->u.i = p->apCsr[pOp->p1]->seqCount++; - break; -} - - -/* Opcode: NewRowid P1 P2 P3 * * -** -** Get a new integer record number (a.k.a "rowid") used as the key to a table. -** The record number is not previously used as a key in the database -** table that cursor P1 points to. The new record number is written -** written to register P2. -** -** If P3>0 then P3 is a register in the root frame of this VDBE that holds -** the largest previously generated record number. No new record numbers are -** allowed to be less than this value. When this value reaches its maximum, -** an SQLITE_FULL error is generated. The P3 register is updated with the ' -** generated record number. This P3 mechanism is used to help implement the -** AUTOINCREMENT feature. -*/ -case OP_NewRowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bh */ - i64 v; /* The new rowid */ - VdbeCursor *pC; /* Cursor of table to get the new rowid */ - int res; /* Result of an sqlite3BtreeLast() */ - int cnt; /* Counter to limit the number of searches */ - Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ - VdbeFrame *pFrame; /* Root frame of VDBE */ -#endif /* local variables moved into u.bh */ - - u.bh.v = 0; - u.bh.res = 0; - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bh.pC = p->apCsr[pOp->p1]; - assert( u.bh.pC!=0 ); - if( NEVER(u.bh.pC->pCursor==0) ){ - /* The zero initialization above is all that is needed */ - }else{ - /* The next rowid or record number (different terms for the same - ** thing) is obtained in a two-step algorithm. - ** - ** First we attempt to find the largest existing rowid and add one - ** to that. But if the largest existing rowid is already the maximum - ** positive integer, we have to fall through to the second - ** probabilistic algorithm - ** - ** The second algorithm is to select a rowid at random and see if - ** it already exists in the table. If it does not exist, we have - ** succeeded. If the random rowid does exist, we select a new one - ** and try again, up to 100 times. - */ - assert( u.bh.pC->isTable ); - -#ifdef SQLITE_32BIT_ROWID -# define MAX_ROWID 0x7fffffff -#else - /* Some compilers complain about constants of the form 0x7fffffffffffffff. - ** Others complain about 0x7ffffffffffffffffLL. The following macro seems - ** to provide the constant while making all compilers happy. - */ -# define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff ) -#endif - - if( !u.bh.pC->useRandomRowid ){ - u.bh.v = sqlite3BtreeGetCachedRowid(u.bh.pC->pCursor); - if( u.bh.v==0 ){ - rc = sqlite3BtreeLast(u.bh.pC->pCursor, &u.bh.res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( u.bh.res ){ - u.bh.v = 1; /* IMP: R-61914-48074 */ - }else{ - assert( sqlite3BtreeCursorIsValid(u.bh.pC->pCursor) ); - rc = sqlite3BtreeKeySize(u.bh.pC->pCursor, &u.bh.v); - assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ - if( u.bh.v>=MAX_ROWID ){ - u.bh.pC->useRandomRowid = 1; - }else{ - u.bh.v++; /* IMP: R-29538-34987 */ - } - } - } - -#ifndef SQLITE_OMIT_AUTOINCREMENT - if( pOp->p3 ){ - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3>0 ); - if( p->pFrame ){ - for(u.bh.pFrame=p->pFrame; u.bh.pFrame->pParent; u.bh.pFrame=u.bh.pFrame->pParent); - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=u.bh.pFrame->nMem ); - u.bh.pMem = &u.bh.pFrame->aMem[pOp->p3]; - }else{ - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=p->nMem ); - u.bh.pMem = &aMem[pOp->p3]; - memAboutToChange(p, u.bh.pMem); - } - assert( memIsValid(u.bh.pMem) ); - - REGISTER_TRACE(pOp->p3, u.bh.pMem); - sqlite3VdbeMemIntegerify(u.bh.pMem); - assert( (u.bh.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ - if( u.bh.pMem->u.i==MAX_ROWID || u.bh.pC->useRandomRowid ){ - rc = SQLITE_FULL; /* IMP: R-12275-61338 */ - goto abort_due_to_error; - } - if( u.bh.vu.i+1 ){ - u.bh.v = u.bh.pMem->u.i + 1; - } - u.bh.pMem->u.i = u.bh.v; - } -#endif - - sqlite3BtreeSetCachedRowid(u.bh.pC->pCursor, u.bh.vuseRandomRowid ){ - /* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the - ** largest possible integer (9223372036854775807) then the database - ** engine starts picking positive candidate ROWIDs at random until - ** it finds one that is not previously used. */ - assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is - ** an AUTOINCREMENT table. */ - /* on the first attempt, simply do one more than previous */ - u.bh.v = lastRowid; - u.bh.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ - u.bh.v++; /* ensure non-zero */ - u.bh.cnt = 0; - while( ((rc = sqlite3BtreeMovetoUnpacked(u.bh.pC->pCursor, 0, (u64)u.bh.v, - 0, &u.bh.res))==SQLITE_OK) - && (u.bh.res==0) - && (++u.bh.cnt<100)){ - /* collision - try another random rowid */ - sqlite3_randomness(sizeof(u.bh.v), &u.bh.v); - if( u.bh.cnt<5 ){ - /* try "small" random rowids for the initial attempts */ - u.bh.v &= 0xffffff; - }else{ - u.bh.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ - } - u.bh.v++; /* ensure non-zero */ - } - if( rc==SQLITE_OK && u.bh.res==0 ){ - rc = SQLITE_FULL; /* IMP: R-38219-53002 */ - goto abort_due_to_error; - } - assert( u.bh.v>0 ); /* EV: R-40812-03570 */ - } - u.bh.pC->rowidIsValid = 0; - u.bh.pC->deferredMoveto = 0; - u.bh.pC->cacheStatus = CACHE_STALE; - } - pOut->u.i = u.bh.v; - break; -} - -/* Opcode: Insert P1 P2 P3 P4 P5 -** -** Write an entry into the table of cursor P1. A new entry is -** created if it doesn't already exist or the data for an existing -** entry is overwritten. The data is the value MEM_Blob stored in register -** number P2. The key is stored in register P3. The key must -** be a MEM_Int. -** -** If the OPFLAG_NCHANGE flag of P5 is set, then the row change count is -** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P5 is set, -** then rowid is stored for subsequent return by the -** sqlite3_last_insert_rowid() function (otherwise it is unmodified). -** -** If the OPFLAG_USESEEKRESULT flag of P5 is set and if the result of -** the last seek operation (OP_NotExists) was a success, then this -** operation will not attempt to find the appropriate row before doing -** the insert but will instead overwrite the row that the cursor is -** currently pointing to. Presumably, the prior OP_NotExists opcode -** has already positioned the cursor correctly. This is an optimization -** that boosts performance by avoiding redundant seeks. -** -** If the OPFLAG_ISUPDATE flag is set, then this opcode is part of an -** UPDATE operation. Otherwise (if the flag is clear) then this opcode -** is part of an INSERT operation. The difference is only important to -** the update hook. -** -** Parameter P4 may point to a string containing the table-name, or -** may be NULL. If it is not NULL, then the update-hook -** (sqlite3.xUpdateCallback) is invoked following a successful insert. -** -** (WARNING/TODO: If P1 is a pseudo-cursor and P2 is dynamically -** allocated, then ownership of P2 is transferred to the pseudo-cursor -** and register P2 becomes ephemeral. If the cursor is changed, the -** value of register P2 will then change. Make sure this does not -** cause any problems.) -** -** This instruction only works on tables. The equivalent instruction -** for indices is OP_IdxInsert. -*/ -/* Opcode: InsertInt P1 P2 P3 P4 P5 -** -** This works exactly like OP_Insert except that the key is the -** integer value P3, not the value of the integer stored in register P3. -*/ -case OP_Insert: -case OP_InsertInt: { -#if 0 /* local variables moved into u.bi */ - Mem *pData; /* MEM cell holding data for the record to be inserted */ - Mem *pKey; /* MEM cell holding key for the record */ - i64 iKey; /* The integer ROWID or key for the record to be inserted */ - VdbeCursor *pC; /* Cursor to table into which insert is written */ - int nZero; /* Number of zero-bytes to append */ - int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ - const char *zDb; /* database name - used by the update hook */ - const char *zTbl; /* Table name - used by the opdate hook */ - int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ -#endif /* local variables moved into u.bi */ - - u.bi.pData = &aMem[pOp->p2]; - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( memIsValid(u.bi.pData) ); - u.bi.pC = p->apCsr[pOp->p1]; - assert( u.bi.pC!=0 ); - assert( u.bi.pC->pCursor!=0 ); - assert( u.bi.pC->pseudoTableReg==0 ); - assert( u.bi.pC->isTable ); - REGISTER_TRACE(pOp->p2, u.bi.pData); - - if( pOp->opcode==OP_Insert ){ - u.bi.pKey = &aMem[pOp->p3]; - assert( u.bi.pKey->flags & MEM_Int ); - assert( memIsValid(u.bi.pKey) ); - REGISTER_TRACE(pOp->p3, u.bi.pKey); - u.bi.iKey = u.bi.pKey->u.i; - }else{ - assert( pOp->opcode==OP_InsertInt ); - u.bi.iKey = pOp->p3; - } - - if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = u.bi.iKey; - if( u.bi.pData->flags & MEM_Null ){ - u.bi.pData->z = 0; - u.bi.pData->n = 0; - }else{ - assert( u.bi.pData->flags & (MEM_Blob|MEM_Str) ); - } - u.bi.seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bi.pC->seekResult : 0); - if( u.bi.pData->flags & MEM_Zero ){ - u.bi.nZero = u.bi.pData->u.nZero; - }else{ - u.bi.nZero = 0; - } - sqlite3BtreeSetCachedRowid(u.bi.pC->pCursor, 0); - rc = sqlite3BtreeInsert(u.bi.pC->pCursor, 0, u.bi.iKey, - u.bi.pData->z, u.bi.pData->n, u.bi.nZero, - pOp->p5 & OPFLAG_APPEND, u.bi.seekResult - ); - u.bi.pC->rowidIsValid = 0; - u.bi.pC->deferredMoveto = 0; - u.bi.pC->cacheStatus = CACHE_STALE; - - /* Invoke the update-hook if required. */ - if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ - u.bi.zDb = db->aDb[u.bi.pC->iDb].zName; - u.bi.zTbl = pOp->p4.z; - u.bi.op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); - assert( u.bi.pC->isTable ); - db->xUpdateCallback(db->pUpdateArg, u.bi.op, u.bi.zDb, u.bi.zTbl, u.bi.iKey); - assert( u.bi.pC->iDb>=0 ); - } - break; -} - -/* Opcode: Delete P1 P2 * P4 * -** -** Delete the record at which the P1 cursor is currently pointing. -** -** The cursor will be left pointing at either the next or the previous -** record in the table. If it is left pointing at the next record, then -** the next Next instruction will be a no-op. Hence it is OK to delete -** a record from within an Next loop. -** -** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is -** incremented (otherwise not). -** -** P1 must not be pseudo-table. It has to be a real table with -** multiple rows. -** -** If P4 is not NULL, then it is the name of the table that P1 is -** pointing to. The update hook will be invoked, if it exists. -** If P4 is not NULL then the P1 cursor must have been positioned -** using OP_NotFound prior to invoking this opcode. -*/ -case OP_Delete: { -#if 0 /* local variables moved into u.bj */ - i64 iKey; - VdbeCursor *pC; -#endif /* local variables moved into u.bj */ - - u.bj.iKey = 0; - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bj.pC = p->apCsr[pOp->p1]; - assert( u.bj.pC!=0 ); - assert( u.bj.pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ - - /* If the update-hook will be invoked, set u.bj.iKey to the rowid of the - ** row being deleted. - */ - if( db->xUpdateCallback && pOp->p4.z ){ - assert( u.bj.pC->isTable ); - assert( u.bj.pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */ - u.bj.iKey = u.bj.pC->lastRowid; - } - - /* The OP_Delete opcode always follows an OP_NotExists or OP_Last or - ** OP_Column on the same table without any intervening operations that - ** might move or invalidate the cursor. Hence cursor u.bj.pC is always pointing - ** to the row to be deleted and the sqlite3VdbeCursorMoveto() operation - ** below is always a no-op and cannot fail. We will run it anyhow, though, - ** to guard against future changes to the code generator. - **/ - assert( u.bj.pC->deferredMoveto==0 ); - rc = sqlite3VdbeCursorMoveto(u.bj.pC); - if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - - sqlite3BtreeSetCachedRowid(u.bj.pC->pCursor, 0); - rc = sqlite3BtreeDelete(u.bj.pC->pCursor); - u.bj.pC->cacheStatus = CACHE_STALE; - - /* Invoke the update-hook if required. */ - if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ - const char *zDb = db->aDb[u.bj.pC->iDb].zName; - const char *zTbl = pOp->p4.z; - db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, u.bj.iKey); - assert( u.bj.pC->iDb>=0 ); - } - if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; - break; -} -/* Opcode: ResetCount * * * * * -** -** The value of the change counter is copied to the database handle -** change counter (returned by subsequent calls to sqlite3_changes()). -** Then the VMs internal change counter resets to 0. -** This is used by trigger programs. -*/ -case OP_ResetCount: { - sqlite3VdbeSetChanges(db, p->nChange); - p->nChange = 0; - break; -} - -/* Opcode: SorterCompare P1 P2 P3 -** -** P1 is a sorter cursor. This instruction compares the record blob in -** register P3 with the entry that the sorter cursor currently points to. -** If, excluding the rowid fields at the end, the two records are a match, -** fall through to the next instruction. Otherwise, jump to instruction P2. -*/ -case OP_SorterCompare: { -#if 0 /* local variables moved into u.bk */ - VdbeCursor *pC; - int res; -#endif /* local variables moved into u.bk */ - - u.bk.pC = p->apCsr[pOp->p1]; - assert( isSorter(u.bk.pC) ); - pIn3 = &aMem[pOp->p3]; - rc = sqlite3VdbeSorterCompare(u.bk.pC, pIn3, &u.bk.res); - if( u.bk.res ){ - pc = pOp->p2-1; - } - break; -}; - -/* Opcode: SorterData P1 P2 * * * -** -** Write into register P2 the current sorter data for sorter cursor P1. -*/ -case OP_SorterData: { -#if 0 /* local variables moved into u.bl */ - VdbeCursor *pC; -#endif /* local variables moved into u.bl */ - - pOut = &aMem[pOp->p2]; - u.bl.pC = p->apCsr[pOp->p1]; - assert( u.bl.pC->isSorter ); - rc = sqlite3VdbeSorterRowkey(u.bl.pC, pOut); - break; -} - -/* Opcode: RowData P1 P2 * * * -** -** Write into register P2 the complete row data for cursor P1. -** There is no interpretation of the data. -** It is just copied onto the P2 register exactly as -** it is found in the database file. -** -** If the P1 cursor must be pointing to a valid row (not a NULL row) -** of a real table, not a pseudo-table. -*/ -/* Opcode: RowKey P1 P2 * * * -** -** Write into register P2 the complete row key for cursor P1. -** There is no interpretation of the data. -** The key is copied onto the P3 register exactly as -** it is found in the database file. -** -** If the P1 cursor must be pointing to a valid row (not a NULL row) -** of a real table, not a pseudo-table. -*/ -case OP_RowKey: -case OP_RowData: { -#if 0 /* local variables moved into u.bm */ - VdbeCursor *pC; - BtCursor *pCrsr; - u32 n; - i64 n64; -#endif /* local variables moved into u.bm */ - - pOut = &aMem[pOp->p2]; - memAboutToChange(p, pOut); - - /* Note that RowKey and RowData are really exactly the same instruction */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bm.pC = p->apCsr[pOp->p1]; - assert( u.bm.pC->isSorter==0 ); - assert( u.bm.pC->isTable || pOp->opcode!=OP_RowData ); - assert( u.bm.pC->isIndex || pOp->opcode==OP_RowData ); - assert( u.bm.pC!=0 ); - assert( u.bm.pC->nullRow==0 ); - assert( u.bm.pC->pseudoTableReg==0 ); - assert( u.bm.pC->pCursor!=0 ); - u.bm.pCrsr = u.bm.pC->pCursor; - assert( sqlite3BtreeCursorIsValid(u.bm.pCrsr) ); - - /* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or - ** OP_Rewind/Op_Next with no intervening instructions that might invalidate - ** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always - ** a no-op and can never fail. But we leave it in place as a safety. - */ - assert( u.bm.pC->deferredMoveto==0 ); - rc = sqlite3VdbeCursorMoveto(u.bm.pC); - if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - - if( u.bm.pC->isIndex ){ - assert( !u.bm.pC->isTable ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(u.bm.pCrsr, &u.bm.n64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - if( u.bm.n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - u.bm.n = (u32)u.bm.n64; - }else{ - VVA_ONLY(rc =) sqlite3BtreeDataSize(u.bm.pCrsr, &u.bm.n); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - if( u.bm.n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - } - if( sqlite3VdbeMemGrow(pOut, u.bm.n, 0) ){ - goto no_mem; - } - pOut->n = u.bm.n; - MemSetTypeFlag(pOut, MEM_Blob); - if( u.bm.pC->isIndex ){ - rc = sqlite3BtreeKey(u.bm.pCrsr, 0, u.bm.n, pOut->z); - }else{ - rc = sqlite3BtreeData(u.bm.pCrsr, 0, u.bm.n, pOut->z); - } - pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */ - UPDATE_MAX_BLOBSIZE(pOut); - break; -} - -/* Opcode: Rowid P1 P2 * * * -** -** Store in register P2 an integer which is the key of the table entry that -** P1 is currently point to. -** -** P1 can be either an ordinary table or a virtual table. There used to -** be a separate OP_VRowid opcode for use with virtual tables, but this -** one opcode now works for both table types. -*/ -case OP_Rowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bn */ - VdbeCursor *pC; - i64 v; - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; -#endif /* local variables moved into u.bn */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bn.pC = p->apCsr[pOp->p1]; - assert( u.bn.pC!=0 ); - assert( u.bn.pC->pseudoTableReg==0 || u.bn.pC->nullRow ); - if( u.bn.pC->nullRow ){ - pOut->flags = MEM_Null; - break; - }else if( u.bn.pC->deferredMoveto ){ - u.bn.v = u.bn.pC->movetoTarget; -#ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( u.bn.pC->pVtabCursor ){ - u.bn.pVtab = u.bn.pC->pVtabCursor->pVtab; - u.bn.pModule = u.bn.pVtab->pModule; - assert( u.bn.pModule->xRowid ); - rc = u.bn.pModule->xRowid(u.bn.pC->pVtabCursor, &u.bn.v); - importVtabErrMsg(p, u.bn.pVtab); -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - }else{ - assert( u.bn.pC->pCursor!=0 ); - rc = sqlite3VdbeCursorMoveto(u.bn.pC); - if( rc ) goto abort_due_to_error; - if( u.bn.pC->rowidIsValid ){ - u.bn.v = u.bn.pC->lastRowid; - }else{ - rc = sqlite3BtreeKeySize(u.bn.pC->pCursor, &u.bn.v); - assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */ - } - } - pOut->u.i = u.bn.v; - break; -} - -/* Opcode: NullRow P1 * * * * -** -** Move the cursor P1 to a null row. Any OP_Column operations -** that occur while the cursor is on the null row will always -** write a NULL. -*/ -case OP_NullRow: { -#if 0 /* local variables moved into u.bo */ - VdbeCursor *pC; -#endif /* local variables moved into u.bo */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bo.pC = p->apCsr[pOp->p1]; - assert( u.bo.pC!=0 ); - u.bo.pC->nullRow = 1; - u.bo.pC->rowidIsValid = 0; - assert( u.bo.pC->pCursor || u.bo.pC->pVtabCursor ); - if( u.bo.pC->pCursor ){ - sqlite3BtreeClearCursor(u.bo.pC->pCursor); - } - break; -} - -/* Opcode: Last P1 P2 * * * -** -** The next use of the Rowid or Column or Next instruction for P1 -** will refer to the last entry in the database table or index. -** If the table or index is empty and P2>0, then jump immediately to P2. -** If P2 is 0 or if the table or index is not empty, fall through -** to the following instruction. -*/ -case OP_Last: { /* jump */ -#if 0 /* local variables moved into u.bp */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; -#endif /* local variables moved into u.bp */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bp.pC = p->apCsr[pOp->p1]; - assert( u.bp.pC!=0 ); - u.bp.pCrsr = u.bp.pC->pCursor; - u.bp.res = 0; - if( ALWAYS(u.bp.pCrsr!=0) ){ - rc = sqlite3BtreeLast(u.bp.pCrsr, &u.bp.res); - } - u.bp.pC->nullRow = (u8)u.bp.res; - u.bp.pC->deferredMoveto = 0; - u.bp.pC->rowidIsValid = 0; - u.bp.pC->cacheStatus = CACHE_STALE; - if( pOp->p2>0 && u.bp.res ){ - pc = pOp->p2 - 1; - } - break; -} - - -/* Opcode: Sort P1 P2 * * * -** -** This opcode does exactly the same thing as OP_Rewind except that -** it increments an undocumented global variable used for testing. -** -** Sorting is accomplished by writing records into a sorting index, -** then rewinding that index and playing it back from beginning to -** end. We use the OP_Sort opcode instead of OP_Rewind to do the -** rewinding so that the global variable will be incremented and -** regression tests can determine whether or not the optimizer is -** correctly optimizing out sorts. -*/ -case OP_SorterSort: /* jump */ -case OP_Sort: { /* jump */ -#ifdef SQLITE_TEST - sqlite3_sort_count++; - sqlite3_search_count--; -#endif - p->aCounter[SQLITE_STMTSTATUS_SORT-1]++; - /* Fall through into OP_Rewind */ -} -/* Opcode: Rewind P1 P2 * * * -** -** The next use of the Rowid or Column or Next instruction for P1 -** will refer to the first entry in the database table or index. -** If the table or index is empty and P2>0, then jump immediately to P2. -** If P2 is 0 or if the table or index is not empty, fall through -** to the following instruction. -*/ -case OP_Rewind: { /* jump */ -#if 0 /* local variables moved into u.bq */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; -#endif /* local variables moved into u.bq */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bq.pC = p->apCsr[pOp->p1]; - assert( u.bq.pC!=0 ); - assert( u.bq.pC->isSorter==(pOp->opcode==OP_SorterSort) ); - u.bq.res = 1; - if( isSorter(u.bq.pC) ){ - rc = sqlite3VdbeSorterRewind(db, u.bq.pC, &u.bq.res); - }else{ - u.bq.pCrsr = u.bq.pC->pCursor; - assert( u.bq.pCrsr ); - rc = sqlite3BtreeFirst(u.bq.pCrsr, &u.bq.res); - u.bq.pC->atFirst = u.bq.res==0 ?1:0; - u.bq.pC->deferredMoveto = 0; - u.bq.pC->cacheStatus = CACHE_STALE; - u.bq.pC->rowidIsValid = 0; - } - u.bq.pC->nullRow = (u8)u.bq.res; - assert( pOp->p2>0 && pOp->p2nOp ); - if( u.bq.res ){ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: Next P1 P2 * P4 P5 -** -** Advance cursor P1 so that it points to the next key/data pair in its -** table or index. If there are no more key/value pairs then fall through -** to the following instruction. But if the cursor advance was successful, -** jump immediately to P2. -** -** The P1 cursor must be for a real table, not a pseudo-table. -** -** P4 is always of type P4_ADVANCE. The function pointer points to -** sqlite3BtreeNext(). -** -** If P5 is positive and the jump is taken, then event counter -** number P5-1 in the prepared statement is incremented. -** -** See also: Prev -*/ -/* Opcode: Prev P1 P2 * * P5 -** -** Back up cursor P1 so that it points to the previous key/data pair in its -** table or index. If there is no previous key/value pairs then fall through -** to the following instruction. But if the cursor backup was successful, -** jump immediately to P2. -** -** The P1 cursor must be for a real table, not a pseudo-table. -** -** P4 is always of type P4_ADVANCE. The function pointer points to -** sqlite3BtreePrevious(). -** -** If P5 is positive and the jump is taken, then event counter -** number P5-1 in the prepared statement is incremented. -*/ -case OP_SorterNext: /* jump */ -case OP_Prev: /* jump */ -case OP_Next: { /* jump */ -#if 0 /* local variables moved into u.br */ - VdbeCursor *pC; - int res; -#endif /* local variables moved into u.br */ - - CHECK_FOR_INTERRUPT; - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p5<=ArraySize(p->aCounter) ); - u.br.pC = p->apCsr[pOp->p1]; - if( u.br.pC==0 ){ - break; /* See ticket #2273 */ - } - assert( u.br.pC->isSorter==(pOp->opcode==OP_SorterNext) ); - if( isSorter(u.br.pC) ){ - assert( pOp->opcode==OP_SorterNext ); - rc = sqlite3VdbeSorterNext(db, u.br.pC, &u.br.res); - }else{ - u.br.res = 1; - assert( u.br.pC->deferredMoveto==0 ); - assert( u.br.pC->pCursor ); - assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); - assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); - rc = pOp->p4.xAdvance(u.br.pC->pCursor, &u.br.res); - } - u.br.pC->nullRow = (u8)u.br.res; - u.br.pC->cacheStatus = CACHE_STALE; - if( u.br.res==0 ){ - pc = pOp->p2 - 1; - if( pOp->p5 ) p->aCounter[pOp->p5-1]++; -#ifdef SQLITE_TEST - sqlite3_search_count++; -#endif - } - u.br.pC->rowidIsValid = 0; - break; -} - -/* Opcode: IdxInsert P1 P2 P3 * P5 -** -** Register P2 holds an SQL index key made using the -** MakeRecord instructions. This opcode writes that key -** into the index P1. Data for the entry is nil. -** -** P3 is a flag that provides a hint to the b-tree layer that this -** insert is likely to be an append. -** -** This instruction only works for indices. The equivalent instruction -** for tables is OP_Insert. -*/ -case OP_SorterInsert: /* in2 */ -case OP_IdxInsert: { /* in2 */ -#if 0 /* local variables moved into u.bs */ - VdbeCursor *pC; - BtCursor *pCrsr; - int nKey; - const char *zKey; -#endif /* local variables moved into u.bs */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bs.pC = p->apCsr[pOp->p1]; - assert( u.bs.pC!=0 ); - assert( u.bs.pC->isSorter==(pOp->opcode==OP_SorterInsert) ); - pIn2 = &aMem[pOp->p2]; - assert( pIn2->flags & MEM_Blob ); - u.bs.pCrsr = u.bs.pC->pCursor; - if( ALWAYS(u.bs.pCrsr!=0) ){ - assert( u.bs.pC->isTable==0 ); - rc = ExpandBlob(pIn2); - if( rc==SQLITE_OK ){ - if( isSorter(u.bs.pC) ){ - rc = sqlite3VdbeSorterWrite(db, u.bs.pC, pIn2); - }else{ - u.bs.nKey = pIn2->n; - u.bs.zKey = pIn2->z; - rc = sqlite3BtreeInsert(u.bs.pCrsr, u.bs.zKey, u.bs.nKey, "", 0, 0, pOp->p3, - ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bs.pC->seekResult : 0) - ); - assert( u.bs.pC->deferredMoveto==0 ); - u.bs.pC->cacheStatus = CACHE_STALE; - } - } - } - break; -} - -/* Opcode: IdxDelete P1 P2 P3 * * -** -** The content of P3 registers starting at register P2 form -** an unpacked index key. This opcode removes that entry from the -** index opened by cursor P1. -*/ -case OP_IdxDelete: { -#if 0 /* local variables moved into u.bt */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - UnpackedRecord r; -#endif /* local variables moved into u.bt */ - - assert( pOp->p3>0 ); - assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bt.pC = p->apCsr[pOp->p1]; - assert( u.bt.pC!=0 ); - u.bt.pCrsr = u.bt.pC->pCursor; - if( ALWAYS(u.bt.pCrsr!=0) ){ - u.bt.r.pKeyInfo = u.bt.pC->pKeyInfo; - u.bt.r.nField = (u16)pOp->p3; - u.bt.r.flags = 0; - u.bt.r.aMem = &aMem[pOp->p2]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ideferredMoveto==0 ); - u.bt.pC->cacheStatus = CACHE_STALE; - } - break; -} - -/* Opcode: IdxRowid P1 P2 * * * -** -** Write into register P2 an integer which is the last entry in the record at -** the end of the index key pointed to by cursor P1. This integer should be -** the rowid of the table entry to which this index entry points. -** -** See also: Rowid, MakeRecord. -*/ -case OP_IdxRowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bu */ - BtCursor *pCrsr; - VdbeCursor *pC; - i64 rowid; -#endif /* local variables moved into u.bu */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bu.pC = p->apCsr[pOp->p1]; - assert( u.bu.pC!=0 ); - u.bu.pCrsr = u.bu.pC->pCursor; - pOut->flags = MEM_Null; - if( ALWAYS(u.bu.pCrsr!=0) ){ - rc = sqlite3VdbeCursorMoveto(u.bu.pC); - if( NEVER(rc) ) goto abort_due_to_error; - assert( u.bu.pC->deferredMoveto==0 ); - assert( u.bu.pC->isTable==0 ); - if( !u.bu.pC->nullRow ){ - rc = sqlite3VdbeIdxRowid(db, u.bu.pCrsr, &u.bu.rowid); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - pOut->u.i = u.bu.rowid; - pOut->flags = MEM_Int; - } - } - break; -} - -/* Opcode: IdxGE P1 P2 P3 P4 P5 -** -** The P4 register values beginning with P3 form an unpacked index -** key that omits the ROWID. Compare this key value against the index -** that P1 is currently pointing to, ignoring the ROWID on the P1 index. -** -** If the P1 index entry is greater than or equal to the key value -** then jump to P2. Otherwise fall through to the next instruction. -** -** If P5 is non-zero then the key value is increased by an epsilon -** prior to the comparison. This make the opcode work like IdxGT except -** that if the key from register P3 is a prefix of the key in the cursor, -** the result is false whereas it would be true with IdxGT. -*/ -/* Opcode: IdxLT P1 P2 P3 P4 P5 -** -** The P4 register values beginning with P3 form an unpacked index -** key that omits the ROWID. Compare this key value against the index -** that P1 is currently pointing to, ignoring the ROWID on the P1 index. -** -** If the P1 index entry is less than the key value then jump to P2. -** Otherwise fall through to the next instruction. -** -** If P5 is non-zero then the key value is increased by an epsilon prior -** to the comparison. This makes the opcode work like IdxLE. -*/ -case OP_IdxLT: /* jump */ -case OP_IdxGE: { /* jump */ -#if 0 /* local variables moved into u.bv */ - VdbeCursor *pC; - int res; - UnpackedRecord r; -#endif /* local variables moved into u.bv */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bv.pC = p->apCsr[pOp->p1]; - assert( u.bv.pC!=0 ); - assert( u.bv.pC->isOrdered ); - if( ALWAYS(u.bv.pC->pCursor!=0) ){ - assert( u.bv.pC->deferredMoveto==0 ); - assert( pOp->p5==0 || pOp->p5==1 ); - assert( pOp->p4type==P4_INT32 ); - u.bv.r.pKeyInfo = u.bv.pC->pKeyInfo; - u.bv.r.nField = (u16)pOp->p4.i; - if( pOp->p5 ){ - u.bv.r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH; - }else{ - u.bv.r.flags = UNPACKED_PREFIX_MATCH; - } - u.bv.r.aMem = &aMem[pOp->p3]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; iopcode==OP_IdxLT ){ - u.bv.res = -u.bv.res; - }else{ - assert( pOp->opcode==OP_IdxGE ); - u.bv.res++; - } - if( u.bv.res>0 ){ - pc = pOp->p2 - 1 ; - } - } - break; -} - -/* Opcode: Destroy P1 P2 P3 * * -** -** Delete an entire database table or index whose root page in the database -** file is given by P1. -** -** The table being destroyed is in the main database file if P3==0. If -** P3==1 then the table to be clear is in the auxiliary database file -** that is used to store tables create using CREATE TEMPORARY TABLE. -** -** If AUTOVACUUM is enabled then it is possible that another root page -** might be moved into the newly deleted root page in order to keep all -** root pages contiguous at the beginning of the database. The former -** value of the root page that moved - its value before the move occurred - -** is stored in register P2. If no page -** movement was required (because the table being dropped was already -** the last one in the database) then a zero is stored in register P2. -** If AUTOVACUUM is disabled then a zero is stored in register P2. -** -** See also: Clear -*/ -case OP_Destroy: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bw */ - int iMoved; - int iCnt; - Vdbe *pVdbe; - int iDb; -#endif /* local variables moved into u.bw */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE - u.bw.iCnt = 0; - for(u.bw.pVdbe=db->pVdbe; u.bw.pVdbe; u.bw.pVdbe = u.bw.pVdbe->pNext){ - if( u.bw.pVdbe->magic==VDBE_MAGIC_RUN && u.bw.pVdbe->inVtabMethod<2 && u.bw.pVdbe->pc>=0 ){ - u.bw.iCnt++; - } - } -#else - u.bw.iCnt = db->activeVdbeCnt; -#endif - pOut->flags = MEM_Null; - if( u.bw.iCnt>1 ){ - rc = SQLITE_LOCKED; - p->errorAction = OE_Abort; - }else{ - u.bw.iDb = pOp->p3; - assert( u.bw.iCnt==1 ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[u.bw.iDb].pBt, pOp->p1, &u.bw.iMoved); - pOut->flags = MEM_Int; - pOut->u.i = u.bw.iMoved; -#ifndef SQLITE_OMIT_AUTOVACUUM - if( rc==SQLITE_OK && u.bw.iMoved!=0 ){ - sqlite3RootPageMoved(db, u.bw.iDb, u.bw.iMoved, pOp->p1); - /* All OP_Destroy operations occur on the same btree */ - assert( resetSchemaOnFault==0 || resetSchemaOnFault==u.bw.iDb+1 ); - resetSchemaOnFault = u.bw.iDb+1; - } -#endif - } - break; -} - -/* Opcode: Clear P1 P2 P3 -** -** Delete all contents of the database table or index whose root page -** in the database file is given by P1. But, unlike Destroy, do not -** remove the table or index from the database file. -** -** The table being clear is in the main database file if P2==0. If -** P2==1 then the table to be clear is in the auxiliary database file -** that is used to store tables create using CREATE TEMPORARY TABLE. -** -** If the P3 value is non-zero, then the table referred to must be an -** intkey table (an SQL table, not an index). In this case the row change -** count is incremented by the number of rows in the table being cleared. -** If P3 is greater than zero, then the value stored in register P3 is -** also incremented by the number of rows in the table being cleared. -** -** See also: Destroy -*/ -case OP_Clear: { -#if 0 /* local variables moved into u.bx */ - int nChange; -#endif /* local variables moved into u.bx */ - - u.bx.nChange = 0; - assert( (p->btreeMask & (((yDbMask)1)<p2))!=0 ); - rc = sqlite3BtreeClearTable( - db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bx.nChange : 0) - ); - if( pOp->p3 ){ - p->nChange += u.bx.nChange; - if( pOp->p3>0 ){ - assert( memIsValid(&aMem[pOp->p3]) ); - memAboutToChange(p, &aMem[pOp->p3]); - aMem[pOp->p3].u.i += u.bx.nChange; - } - } - break; -} - -/* Opcode: CreateTable P1 P2 * * * -** -** Allocate a new table in the main database file if P1==0 or in the -** auxiliary database file if P1==1 or in an attached database if -** P1>1. Write the root page number of the new table into -** register P2 -** -** The difference between a table and an index is this: A table must -** have a 4-byte integer key and can have arbitrary data. An index -** has an arbitrary key but no data. -** -** See also: CreateIndex -*/ -/* Opcode: CreateIndex P1 P2 * * * -** -** Allocate a new index in the main database file if P1==0 or in the -** auxiliary database file if P1==1 or in an attached database if -** P1>1. Write the root page number of the new table into -** register P2. -** -** See documentation on OP_CreateTable for additional information. -*/ -case OP_CreateIndex: /* out2-prerelease */ -case OP_CreateTable: { /* out2-prerelease */ -#if 0 /* local variables moved into u.by */ - int pgno; - int flags; - Db *pDb; -#endif /* local variables moved into u.by */ - - u.by.pgno = 0; - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - u.by.pDb = &db->aDb[pOp->p1]; - assert( u.by.pDb->pBt!=0 ); - if( pOp->opcode==OP_CreateTable ){ - /* u.by.flags = BTREE_INTKEY; */ - u.by.flags = BTREE_INTKEY; - }else{ - u.by.flags = BTREE_BLOBKEY; - } - rc = sqlite3BtreeCreateTable(u.by.pDb->pBt, &u.by.pgno, u.by.flags); - pOut->u.i = u.by.pgno; - break; -} - -/* Opcode: ParseSchema P1 * * P4 * -** -** Read and parse all entries from the SQLITE_MASTER table of database P1 -** that match the WHERE clause P4. -** -** This opcode invokes the parser to create a new virtual machine, -** then runs the new virtual machine. It is thus a re-entrant opcode. -*/ -case OP_ParseSchema: { -#if 0 /* local variables moved into u.bz */ - int iDb; - const char *zMaster; - char *zSql; - InitData initData; -#endif /* local variables moved into u.bz */ - - /* Any prepared statement that invokes this opcode will hold mutexes - ** on every btree. This is a prerequisite for invoking - ** sqlite3InitCallback(). - */ -#ifdef SQLITE_DEBUG - for(u.bz.iDb=0; u.bz.iDbnDb; u.bz.iDb++){ - assert( u.bz.iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[u.bz.iDb].pBt) ); - } -#endif - - u.bz.iDb = pOp->p1; - assert( u.bz.iDb>=0 && u.bz.iDbnDb ); - assert( DbHasProperty(db, u.bz.iDb, DB_SchemaLoaded) ); - /* Used to be a conditional */ { - u.bz.zMaster = SCHEMA_TABLE(u.bz.iDb); - u.bz.initData.db = db; - u.bz.initData.iDb = pOp->p1; - u.bz.initData.pzErrMsg = &p->zErrMsg; - u.bz.zSql = sqlite3MPrintf(db, - "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid", - db->aDb[u.bz.iDb].zName, u.bz.zMaster, pOp->p4.z); - if( u.bz.zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - assert( db->init.busy==0 ); - db->init.busy = 1; - u.bz.initData.rc = SQLITE_OK; - assert( !db->mallocFailed ); - rc = sqlite3_exec(db, u.bz.zSql, sqlite3InitCallback, &u.bz.initData, 0); - if( rc==SQLITE_OK ) rc = u.bz.initData.rc; - sqlite3DbFree(db, u.bz.zSql); - db->init.busy = 0; - } - } - if( rc ) sqlite3ResetAllSchemasOfConnection(db); - if( rc==SQLITE_NOMEM ){ - goto no_mem; - } - break; -} - -#if !defined(SQLITE_OMIT_ANALYZE) -/* Opcode: LoadAnalysis P1 * * * * -** -** Read the sqlite_stat1 table for database P1 and load the content -** of that table into the internal index hash table. This will cause -** the analysis to be used when preparing all subsequent queries. -*/ -case OP_LoadAnalysis: { - assert( pOp->p1>=0 && pOp->p1nDb ); - rc = sqlite3AnalysisLoad(db, pOp->p1); - break; -} -#endif /* !defined(SQLITE_OMIT_ANALYZE) */ - -/* Opcode: DropTable P1 * * P4 * -** -** Remove the internal (in-memory) data structures that describe -** the table named P4 in database P1. This is called after a table -** is dropped in order to keep the internal representation of the -** schema consistent with what is on disk. -*/ -case OP_DropTable: { - sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p4.z); - break; -} - -/* Opcode: DropIndex P1 * * P4 * -** -** Remove the internal (in-memory) data structures that describe -** the index named P4 in database P1. This is called after an index -** is dropped in order to keep the internal representation of the -** schema consistent with what is on disk. -*/ -case OP_DropIndex: { - sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p4.z); - break; -} - -/* Opcode: DropTrigger P1 * * P4 * -** -** Remove the internal (in-memory) data structures that describe -** the trigger named P4 in database P1. This is called after a trigger -** is dropped in order to keep the internal representation of the -** schema consistent with what is on disk. -*/ -case OP_DropTrigger: { - sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p4.z); - break; -} - - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* Opcode: IntegrityCk P1 P2 P3 * P5 -** -** Do an analysis of the currently open database. Store in -** register P1 the text of an error message describing any problems. -** If no problems are found, store a NULL in register P1. -** -** The register P3 contains the maximum number of allowed errors. -** At most reg(P3) errors will be reported. -** In other words, the analysis stops as soon as reg(P1) errors are -** seen. Reg(P1) is updated with the number of errors remaining. -** -** The root page numbers of all tables in the database are integer -** stored in reg(P1), reg(P1+1), reg(P1+2), .... There are P2 tables -** total. -** -** If P5 is not zero, the check is done on the auxiliary database -** file, not the main database file. -** -** This opcode is used to implement the integrity_check pragma. -*/ -case OP_IntegrityCk: { -#if 0 /* local variables moved into u.ca */ - int nRoot; /* Number of tables to check. (Number of root pages.) */ - int *aRoot; /* Array of rootpage numbers for tables to be checked */ - int j; /* Loop counter */ - int nErr; /* Number of errors reported */ - char *z; /* Text of the error report */ - Mem *pnErr; /* Register keeping track of errors remaining */ -#endif /* local variables moved into u.ca */ - - u.ca.nRoot = pOp->p2; - assert( u.ca.nRoot>0 ); - u.ca.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.ca.nRoot+1) ); - if( u.ca.aRoot==0 ) goto no_mem; - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.ca.pnErr = &aMem[pOp->p3]; - assert( (u.ca.pnErr->flags & MEM_Int)!=0 ); - assert( (u.ca.pnErr->flags & (MEM_Str|MEM_Blob))==0 ); - pIn1 = &aMem[pOp->p1]; - for(u.ca.j=0; u.ca.jp5nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p5))!=0 ); - u.ca.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.ca.aRoot, u.ca.nRoot, - (int)u.ca.pnErr->u.i, &u.ca.nErr); - sqlite3DbFree(db, u.ca.aRoot); - u.ca.pnErr->u.i -= u.ca.nErr; - sqlite3VdbeMemSetNull(pIn1); - if( u.ca.nErr==0 ){ - assert( u.ca.z==0 ); - }else if( u.ca.z==0 ){ - goto no_mem; - }else{ - sqlite3VdbeMemSetStr(pIn1, u.ca.z, -1, SQLITE_UTF8, sqlite3_free); - } - UPDATE_MAX_BLOBSIZE(pIn1); - sqlite3VdbeChangeEncoding(pIn1, encoding); - break; -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -/* Opcode: RowSetAdd P1 P2 * * * -** -** Insert the integer value held by register P2 into a boolean index -** held in register P1. -** -** An assertion fails if P2 is not an integer. -*/ -case OP_RowSetAdd: { /* in1, in2 */ - pIn1 = &aMem[pOp->p1]; - pIn2 = &aMem[pOp->p2]; - assert( (pIn2->flags & MEM_Int)!=0 ); - if( (pIn1->flags & MEM_RowSet)==0 ){ - sqlite3VdbeMemSetRowSet(pIn1); - if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem; - } - sqlite3RowSetInsert(pIn1->u.pRowSet, pIn2->u.i); - break; -} - -/* Opcode: RowSetRead P1 P2 P3 * * -** -** Extract the smallest value from boolean index P1 and put that value into -** register P3. Or, if boolean index P1 is initially empty, leave P3 -** unchanged and jump to instruction P2. -*/ -case OP_RowSetRead: { /* jump, in1, out3 */ -#if 0 /* local variables moved into u.cb */ - i64 val; -#endif /* local variables moved into u.cb */ - CHECK_FOR_INTERRUPT; - pIn1 = &aMem[pOp->p1]; - if( (pIn1->flags & MEM_RowSet)==0 - || sqlite3RowSetNext(pIn1->u.pRowSet, &u.cb.val)==0 - ){ - /* The boolean index is empty */ - sqlite3VdbeMemSetNull(pIn1); - pc = pOp->p2 - 1; - }else{ - /* A value was pulled from the index */ - sqlite3VdbeMemSetInt64(&aMem[pOp->p3], u.cb.val); - } - break; -} - -/* Opcode: RowSetTest P1 P2 P3 P4 -** -** Register P3 is assumed to hold a 64-bit integer value. If register P1 -** contains a RowSet object and that RowSet object contains -** the value held in P3, jump to register P2. Otherwise, insert the -** integer in P3 into the RowSet and continue on to the -** next opcode. -** -** The RowSet object is optimized for the case where successive sets -** of integers, where each set contains no duplicates. Each set -** of values is identified by a unique P4 value. The first set -** must have P4==0, the final set P4=-1. P4 must be either -1 or -** non-negative. For non-negative values of P4 only the lower 4 -** bits are significant. -** -** This allows optimizations: (a) when P4==0 there is no need to test -** the rowset object for P3, as it is guaranteed not to contain it, -** (b) when P4==-1 there is no need to insert the value, as it will -** never be tested for, and (c) when a value that is part of set X is -** inserted, there is no need to search to see if the same value was -** previously inserted as part of set X (only if it was previously -** inserted as part of some other set). -*/ -case OP_RowSetTest: { /* jump, in1, in3 */ -#if 0 /* local variables moved into u.cc */ - int iSet; - int exists; -#endif /* local variables moved into u.cc */ - - pIn1 = &aMem[pOp->p1]; - pIn3 = &aMem[pOp->p3]; - u.cc.iSet = pOp->p4.i; - assert( pIn3->flags&MEM_Int ); - - /* If there is anything other than a rowset object in memory cell P1, - ** delete it now and initialize P1 with an empty rowset - */ - if( (pIn1->flags & MEM_RowSet)==0 ){ - sqlite3VdbeMemSetRowSet(pIn1); - if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem; - } - - assert( pOp->p4type==P4_INT32 ); - assert( u.cc.iSet==-1 || u.cc.iSet>=0 ); - if( u.cc.iSet ){ - u.cc.exists = sqlite3RowSetTest(pIn1->u.pRowSet, - (u8)(u.cc.iSet>=0 ? u.cc.iSet & 0xf : 0xff), - pIn3->u.i); - if( u.cc.exists ){ - pc = pOp->p2 - 1; - break; - } - } - if( u.cc.iSet>=0 ){ - sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i); - } - break; -} - - -#ifndef SQLITE_OMIT_TRIGGER - -/* Opcode: Program P1 P2 P3 P4 * -** -** Execute the trigger program passed as P4 (type P4_SUBPROGRAM). -** -** P1 contains the address of the memory cell that contains the first memory -** cell in an array of values used as arguments to the sub-program. P2 -** contains the address to jump to if the sub-program throws an IGNORE -** exception using the RAISE() function. Register P3 contains the address -** of a memory cell in this (the parent) VM that is used to allocate the -** memory required by the sub-vdbe at runtime. -** -** P4 is a pointer to the VM containing the trigger program. -*/ -case OP_Program: { /* jump */ -#if 0 /* local variables moved into u.cd */ - int nMem; /* Number of memory registers for sub-program */ - int nByte; /* Bytes of runtime space required for sub-program */ - Mem *pRt; /* Register to allocate runtime space */ - Mem *pMem; /* Used to iterate through memory cells */ - Mem *pEnd; /* Last memory cell in new array */ - VdbeFrame *pFrame; /* New vdbe frame to execute in */ - SubProgram *pProgram; /* Sub-program to execute */ - void *t; /* Token identifying trigger */ -#endif /* local variables moved into u.cd */ - - u.cd.pProgram = pOp->p4.pProgram; - u.cd.pRt = &aMem[pOp->p3]; - assert( u.cd.pProgram->nOp>0 ); - - /* If the p5 flag is clear, then recursive invocation of triggers is - ** disabled for backwards compatibility (p5 is set if this sub-program - ** is really a trigger, not a foreign key action, and the flag set - ** and cleared by the "PRAGMA recursive_triggers" command is clear). - ** - ** It is recursive invocation of triggers, at the SQL level, that is - ** disabled. In some cases a single trigger may generate more than one - ** SubProgram (if the trigger may be executed with more than one different - ** ON CONFLICT algorithm). SubProgram structures associated with a - ** single trigger all have the same value for the SubProgram.token - ** variable. */ - if( pOp->p5 ){ - u.cd.t = u.cd.pProgram->token; - for(u.cd.pFrame=p->pFrame; u.cd.pFrame && u.cd.pFrame->token!=u.cd.t; u.cd.pFrame=u.cd.pFrame->pParent); - if( u.cd.pFrame ) break; - } - - if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ - rc = SQLITE_ERROR; - sqlite3SetString(&p->zErrMsg, db, "too many levels of trigger recursion"); - break; - } - - /* Register u.cd.pRt is used to store the memory required to save the state - ** of the current program, and the memory required at runtime to execute - ** the trigger program. If this trigger has been fired before, then u.cd.pRt - ** is already allocated. Otherwise, it must be initialized. */ - if( (u.cd.pRt->flags&MEM_Frame)==0 ){ - /* SubProgram.nMem is set to the number of memory cells used by the - ** program stored in SubProgram.aOp. As well as these, one memory - ** cell is required for each cursor used by the program. Set local - ** variable u.cd.nMem (and later, VdbeFrame.nChildMem) to this value. - */ - u.cd.nMem = u.cd.pProgram->nMem + u.cd.pProgram->nCsr; - u.cd.nByte = ROUND8(sizeof(VdbeFrame)) - + u.cd.nMem * sizeof(Mem) - + u.cd.pProgram->nCsr * sizeof(VdbeCursor *) - + u.cd.pProgram->nOnce * sizeof(u8); - u.cd.pFrame = sqlite3DbMallocZero(db, u.cd.nByte); - if( !u.cd.pFrame ){ - goto no_mem; - } - sqlite3VdbeMemRelease(u.cd.pRt); - u.cd.pRt->flags = MEM_Frame; - u.cd.pRt->u.pFrame = u.cd.pFrame; - - u.cd.pFrame->v = p; - u.cd.pFrame->nChildMem = u.cd.nMem; - u.cd.pFrame->nChildCsr = u.cd.pProgram->nCsr; - u.cd.pFrame->pc = pc; - u.cd.pFrame->aMem = p->aMem; - u.cd.pFrame->nMem = p->nMem; - u.cd.pFrame->apCsr = p->apCsr; - u.cd.pFrame->nCursor = p->nCursor; - u.cd.pFrame->aOp = p->aOp; - u.cd.pFrame->nOp = p->nOp; - u.cd.pFrame->token = u.cd.pProgram->token; - u.cd.pFrame->aOnceFlag = p->aOnceFlag; - u.cd.pFrame->nOnceFlag = p->nOnceFlag; - - u.cd.pEnd = &VdbeFrameMem(u.cd.pFrame)[u.cd.pFrame->nChildMem]; - for(u.cd.pMem=VdbeFrameMem(u.cd.pFrame); u.cd.pMem!=u.cd.pEnd; u.cd.pMem++){ - u.cd.pMem->flags = MEM_Invalid; - u.cd.pMem->db = db; - } - }else{ - u.cd.pFrame = u.cd.pRt->u.pFrame; - assert( u.cd.pProgram->nMem+u.cd.pProgram->nCsr==u.cd.pFrame->nChildMem ); - assert( u.cd.pProgram->nCsr==u.cd.pFrame->nChildCsr ); - assert( pc==u.cd.pFrame->pc ); - } - - p->nFrame++; - u.cd.pFrame->pParent = p->pFrame; - u.cd.pFrame->lastRowid = lastRowid; - u.cd.pFrame->nChange = p->nChange; - p->nChange = 0; - p->pFrame = u.cd.pFrame; - p->aMem = aMem = &VdbeFrameMem(u.cd.pFrame)[-1]; - p->nMem = u.cd.pFrame->nChildMem; - p->nCursor = (u16)u.cd.pFrame->nChildCsr; - p->apCsr = (VdbeCursor **)&aMem[p->nMem+1]; - p->aOp = aOp = u.cd.pProgram->aOp; - p->nOp = u.cd.pProgram->nOp; - p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor]; - p->nOnceFlag = u.cd.pProgram->nOnce; - pc = -1; - memset(p->aOnceFlag, 0, p->nOnceFlag); - - break; -} - -/* Opcode: Param P1 P2 * * * -** -** This opcode is only ever present in sub-programs called via the -** OP_Program instruction. Copy a value currently stored in a memory -** cell of the calling (parent) frame to cell P2 in the current frames -** address space. This is used by trigger programs to access the new.* -** and old.* values. -** -** The address of the cell in the parent frame is determined by adding -** the value of the P1 argument to the value of the P1 argument to the -** calling OP_Program instruction. -*/ -case OP_Param: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ce */ - VdbeFrame *pFrame; - Mem *pIn; -#endif /* local variables moved into u.ce */ - u.ce.pFrame = p->pFrame; - u.ce.pIn = &u.ce.pFrame->aMem[pOp->p1 + u.ce.pFrame->aOp[u.ce.pFrame->pc].p1]; - sqlite3VdbeMemShallowCopy(pOut, u.ce.pIn, MEM_Ephem); - break; -} - -#endif /* #ifndef SQLITE_OMIT_TRIGGER */ - -#ifndef SQLITE_OMIT_FOREIGN_KEY -/* Opcode: FkCounter P1 P2 * * * -** -** Increment a "constraint counter" by P2 (P2 may be negative or positive). -** If P1 is non-zero, the database constraint counter is incremented -** (deferred foreign key constraints). Otherwise, if P1 is zero, the -** statement counter is incremented (immediate foreign key constraints). -*/ -case OP_FkCounter: { - if( pOp->p1 ){ - db->nDeferredCons += pOp->p2; - }else{ - p->nFkConstraint += pOp->p2; - } - break; -} - -/* Opcode: FkIfZero P1 P2 * * * -** -** This opcode tests if a foreign key constraint-counter is currently zero. -** If so, jump to instruction P2. Otherwise, fall through to the next -** instruction. -** -** If P1 is non-zero, then the jump is taken if the database constraint-counter -** is zero (the one that counts deferred constraint violations). If P1 is -** zero, the jump is taken if the statement constraint-counter is zero -** (immediate foreign key constraint violations). -*/ -case OP_FkIfZero: { /* jump */ - if( pOp->p1 ){ - if( db->nDeferredCons==0 ) pc = pOp->p2-1; - }else{ - if( p->nFkConstraint==0 ) pc = pOp->p2-1; - } - break; -} -#endif /* #ifndef SQLITE_OMIT_FOREIGN_KEY */ - -#ifndef SQLITE_OMIT_AUTOINCREMENT -/* Opcode: MemMax P1 P2 * * * -** -** P1 is a register in the root frame of this VM (the root frame is -** different from the current frame if this instruction is being executed -** within a sub-program). Set the value of register P1 to the maximum of -** its current value and the value in register P2. -** -** This instruction throws an error if the memory cell is not initially -** an integer. -*/ -case OP_MemMax: { /* in2 */ -#if 0 /* local variables moved into u.cf */ - Mem *pIn1; - VdbeFrame *pFrame; -#endif /* local variables moved into u.cf */ - if( p->pFrame ){ - for(u.cf.pFrame=p->pFrame; u.cf.pFrame->pParent; u.cf.pFrame=u.cf.pFrame->pParent); - u.cf.pIn1 = &u.cf.pFrame->aMem[pOp->p1]; - }else{ - u.cf.pIn1 = &aMem[pOp->p1]; - } - assert( memIsValid(u.cf.pIn1) ); - sqlite3VdbeMemIntegerify(u.cf.pIn1); - pIn2 = &aMem[pOp->p2]; - sqlite3VdbeMemIntegerify(pIn2); - if( u.cf.pIn1->u.iu.i){ - u.cf.pIn1->u.i = pIn2->u.i; - } - break; -} -#endif /* SQLITE_OMIT_AUTOINCREMENT */ - -/* Opcode: IfPos P1 P2 * * * -** -** If the value of register P1 is 1 or greater, jump to P2. -** -** It is illegal to use this instruction on a register that does -** not contain an integer. An assertion fault will result if you try. -*/ -case OP_IfPos: { /* jump, in1 */ - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags&MEM_Int ); - if( pIn1->u.i>0 ){ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: IfNeg P1 P2 * * * -** -** If the value of register P1 is less than zero, jump to P2. -** -** It is illegal to use this instruction on a register that does -** not contain an integer. An assertion fault will result if you try. -*/ -case OP_IfNeg: { /* jump, in1 */ - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags&MEM_Int ); - if( pIn1->u.i<0 ){ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: IfZero P1 P2 P3 * * -** -** The register P1 must contain an integer. Add literal P3 to the -** value in register P1. If the result is exactly 0, jump to P2. -** -** It is illegal to use this instruction on a register that does -** not contain an integer. An assertion fault will result if you try. -*/ -case OP_IfZero: { /* jump, in1 */ - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags&MEM_Int ); - pIn1->u.i += pOp->p3; - if( pIn1->u.i==0 ){ - pc = pOp->p2 - 1; - } - break; -} - -/* Opcode: AggStep * P2 P3 P4 P5 -** -** Execute the step function for an aggregate. The -** function has P5 arguments. P4 is a pointer to the FuncDef -** structure that specifies the function. Use register -** P3 as the accumulator. -** -** The P5 arguments are taken from register P2 and its -** successors. -*/ -case OP_AggStep: { -#if 0 /* local variables moved into u.cg */ - int n; - int i; - Mem *pMem; - Mem *pRec; - sqlite3_context ctx; - sqlite3_value **apVal; -#endif /* local variables moved into u.cg */ - - u.cg.n = pOp->p5; - assert( u.cg.n>=0 ); - u.cg.pRec = &aMem[pOp->p2]; - u.cg.apVal = p->apArg; - assert( u.cg.apVal || u.cg.n==0 ); - for(u.cg.i=0; u.cg.ip4.pFunc; - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.cg.ctx.pMem = u.cg.pMem = &aMem[pOp->p3]; - u.cg.pMem->n++; - u.cg.ctx.s.flags = MEM_Null; - u.cg.ctx.s.z = 0; - u.cg.ctx.s.zMalloc = 0; - u.cg.ctx.s.xDel = 0; - u.cg.ctx.s.db = db; - u.cg.ctx.isError = 0; - u.cg.ctx.pColl = 0; - u.cg.ctx.skipFlag = 0; - if( u.cg.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ - assert( pOp>p->aOp ); - assert( pOp[-1].p4type==P4_COLLSEQ ); - assert( pOp[-1].opcode==OP_CollSeq ); - u.cg.ctx.pColl = pOp[-1].p4.pColl; - } - (u.cg.ctx.pFunc->xStep)(&u.cg.ctx, u.cg.n, u.cg.apVal); /* IMP: R-24505-23230 */ - if( u.cg.ctx.isError ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.cg.ctx.s)); - rc = u.cg.ctx.isError; - } - if( u.cg.ctx.skipFlag ){ - assert( pOp[-1].opcode==OP_CollSeq ); - u.cg.i = pOp[-1].p1; - if( u.cg.i ) sqlite3VdbeMemSetInt64(&aMem[u.cg.i], 1); - } - - sqlite3VdbeMemRelease(&u.cg.ctx.s); - - break; -} - -/* Opcode: AggFinal P1 P2 * P4 * -** -** Execute the finalizer function for an aggregate. P1 is -** the memory location that is the accumulator for the aggregate. -** -** P2 is the number of arguments that the step function takes and -** P4 is a pointer to the FuncDef for this function. The P2 -** argument is not used by this opcode. It is only there to disambiguate -** functions that can take varying numbers of arguments. The -** P4 argument is only needed for the degenerate case where -** the step function was not previously called. -*/ -case OP_AggFinal: { -#if 0 /* local variables moved into u.ch */ - Mem *pMem; -#endif /* local variables moved into u.ch */ - assert( pOp->p1>0 && pOp->p1<=p->nMem ); - u.ch.pMem = &aMem[pOp->p1]; - assert( (u.ch.pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); - rc = sqlite3VdbeMemFinalize(u.ch.pMem, pOp->p4.pFunc); - if( rc ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(u.ch.pMem)); - } - sqlite3VdbeChangeEncoding(u.ch.pMem, encoding); - UPDATE_MAX_BLOBSIZE(u.ch.pMem); - if( sqlite3VdbeMemTooBig(u.ch.pMem) ){ - goto too_big; - } - break; -} - -#ifndef SQLITE_OMIT_WAL -/* Opcode: Checkpoint P1 P2 P3 * * -** -** Checkpoint database P1. This is a no-op if P1 is not currently in -** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL -** or RESTART. Write 1 or 0 into mem[P3] if the checkpoint returns -** SQLITE_BUSY or not, respectively. Write the number of pages in the -** WAL after the checkpoint into mem[P3+1] and the number of pages -** in the WAL that have been checkpointed after the checkpoint -** completes into mem[P3+2]. However on an error, mem[P3+1] and -** mem[P3+2] are initialized to -1. -*/ -case OP_Checkpoint: { -#if 0 /* local variables moved into u.ci */ - int i; /* Loop counter */ - int aRes[3]; /* Results */ - Mem *pMem; /* Write results here */ -#endif /* local variables moved into u.ci */ - - u.ci.aRes[0] = 0; - u.ci.aRes[1] = u.ci.aRes[2] = -1; - assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE - || pOp->p2==SQLITE_CHECKPOINT_FULL - || pOp->p2==SQLITE_CHECKPOINT_RESTART - ); - rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &u.ci.aRes[1], &u.ci.aRes[2]); - if( rc==SQLITE_BUSY ){ - rc = SQLITE_OK; - u.ci.aRes[0] = 1; - } - for(u.ci.i=0, u.ci.pMem = &aMem[pOp->p3]; u.ci.i<3; u.ci.i++, u.ci.pMem++){ - sqlite3VdbeMemSetInt64(u.ci.pMem, (i64)u.ci.aRes[u.ci.i]); - } - break; -}; -#endif - -#ifndef SQLITE_OMIT_PRAGMA -/* Opcode: JournalMode P1 P2 P3 * P5 -** -** Change the journal mode of database P1 to P3. P3 must be one of the -** PAGER_JOURNALMODE_XXX values. If changing between the various rollback -** modes (delete, truncate, persist, off and memory), this is a simple -** operation. No IO is required. -** -** If changing into or out of WAL mode the procedure is more complicated. -** -** Write a string containing the final journal-mode to register P2. -*/ -case OP_JournalMode: { /* out2-prerelease */ -#if 0 /* local variables moved into u.cj */ - Btree *pBt; /* Btree to change journal mode of */ - Pager *pPager; /* Pager associated with pBt */ - int eNew; /* New journal mode */ - int eOld; /* The old journal mode */ -#ifndef SQLITE_OMIT_WAL - const char *zFilename; /* Name of database file for pPager */ -#endif -#endif /* local variables moved into u.cj */ - - u.cj.eNew = pOp->p3; - assert( u.cj.eNew==PAGER_JOURNALMODE_DELETE - || u.cj.eNew==PAGER_JOURNALMODE_TRUNCATE - || u.cj.eNew==PAGER_JOURNALMODE_PERSIST - || u.cj.eNew==PAGER_JOURNALMODE_OFF - || u.cj.eNew==PAGER_JOURNALMODE_MEMORY - || u.cj.eNew==PAGER_JOURNALMODE_WAL - || u.cj.eNew==PAGER_JOURNALMODE_QUERY - ); - assert( pOp->p1>=0 && pOp->p1nDb ); - - u.cj.pBt = db->aDb[pOp->p1].pBt; - u.cj.pPager = sqlite3BtreePager(u.cj.pBt); - u.cj.eOld = sqlite3PagerGetJournalMode(u.cj.pPager); - if( u.cj.eNew==PAGER_JOURNALMODE_QUERY ) u.cj.eNew = u.cj.eOld; - if( !sqlite3PagerOkToChangeJournalMode(u.cj.pPager) ) u.cj.eNew = u.cj.eOld; - -#ifndef SQLITE_OMIT_WAL - u.cj.zFilename = sqlite3PagerFilename(u.cj.pPager, 1); - - /* Do not allow a transition to journal_mode=WAL for a database - ** in temporary storage or if the VFS does not support shared memory - */ - if( u.cj.eNew==PAGER_JOURNALMODE_WAL - && (sqlite3Strlen30(u.cj.zFilename)==0 /* Temp file */ - || !sqlite3PagerWalSupported(u.cj.pPager)) /* No shared-memory support */ - ){ - u.cj.eNew = u.cj.eOld; - } - - if( (u.cj.eNew!=u.cj.eOld) - && (u.cj.eOld==PAGER_JOURNALMODE_WAL || u.cj.eNew==PAGER_JOURNALMODE_WAL) - ){ - if( !db->autoCommit || db->activeVdbeCnt>1 ){ - rc = SQLITE_ERROR; - sqlite3SetString(&p->zErrMsg, db, - "cannot change %s wal mode from within a transaction", - (u.cj.eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") - ); - break; - }else{ - - if( u.cj.eOld==PAGER_JOURNALMODE_WAL ){ - /* If leaving WAL mode, close the log file. If successful, the call - ** to PagerCloseWal() checkpoints and deletes the write-ahead-log - ** file. An EXCLUSIVE lock may still be held on the database file - ** after a successful return. - */ - rc = sqlite3PagerCloseWal(u.cj.pPager); - if( rc==SQLITE_OK ){ - sqlite3PagerSetJournalMode(u.cj.pPager, u.cj.eNew); - } - }else if( u.cj.eOld==PAGER_JOURNALMODE_MEMORY ){ - /* Cannot transition directly from MEMORY to WAL. Use mode OFF - ** as an intermediate */ - sqlite3PagerSetJournalMode(u.cj.pPager, PAGER_JOURNALMODE_OFF); - } - - /* Open a transaction on the database file. Regardless of the journal - ** mode, this transaction always uses a rollback journal. - */ - assert( sqlite3BtreeIsInTrans(u.cj.pBt)==0 ); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeSetVersion(u.cj.pBt, (u.cj.eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); - } - } - } -#endif /* ifndef SQLITE_OMIT_WAL */ - - if( rc ){ - u.cj.eNew = u.cj.eOld; - } - u.cj.eNew = sqlite3PagerSetJournalMode(u.cj.pPager, u.cj.eNew); - - pOut = &aMem[pOp->p2]; - pOut->flags = MEM_Str|MEM_Static|MEM_Term; - pOut->z = (char *)sqlite3JournalModename(u.cj.eNew); - pOut->n = sqlite3Strlen30(pOut->z); - pOut->enc = SQLITE_UTF8; - sqlite3VdbeChangeEncoding(pOut, encoding); - break; -}; -#endif /* SQLITE_OMIT_PRAGMA */ - -#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) -/* Opcode: Vacuum * * * * * -** -** Vacuum the entire database. This opcode will cause other virtual -** machines to be created and run. It may not be called from within -** a transaction. -*/ -case OP_Vacuum: { - rc = sqlite3RunVacuum(&p->zErrMsg, db); - break; -} -#endif - -#if !defined(SQLITE_OMIT_AUTOVACUUM) -/* Opcode: IncrVacuum P1 P2 * * * -** -** Perform a single step of the incremental vacuum procedure on -** the P1 database. If the vacuum has finished, jump to instruction -** P2. Otherwise, fall through to the next instruction. -*/ -case OP_IncrVacuum: { /* jump */ -#if 0 /* local variables moved into u.ck */ - Btree *pBt; -#endif /* local variables moved into u.ck */ - - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); - u.ck.pBt = db->aDb[pOp->p1].pBt; - rc = sqlite3BtreeIncrVacuum(u.ck.pBt); - if( rc==SQLITE_DONE ){ - pc = pOp->p2 - 1; - rc = SQLITE_OK; - } - break; -} -#endif - -/* Opcode: Expire P1 * * * * -** -** Cause precompiled statements to become expired. An expired statement -** fails with an error code of SQLITE_SCHEMA if it is ever executed -** (via sqlite3_step()). -** -** If P1 is 0, then all SQL statements become expired. If P1 is non-zero, -** then only the currently executing statement is affected. -*/ -case OP_Expire: { - if( !pOp->p1 ){ - sqlite3ExpirePreparedStatements(db); - }else{ - p->expired = 1; - } - break; -} - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* Opcode: TableLock P1 P2 P3 P4 * -** -** Obtain a lock on a particular table. This instruction is only used when -** the shared-cache feature is enabled. -** -** P1 is the index of the database in sqlite3.aDb[] of the database -** on which the lock is acquired. A readlock is obtained if P3==0 or -** a write lock if P3==1. -** -** P2 contains the root-page of the table to lock. -** -** P4 contains a pointer to the name of the table being locked. This is only -** used to generate an error message if the lock cannot be obtained. -*/ -case OP_TableLock: { - u8 isWriteLock = (u8)pOp->p3; - if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ - int p1 = pOp->p1; - assert( p1>=0 && p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[p1].pBt, pOp->p2, isWriteLock); - if( (rc&0xFF)==SQLITE_LOCKED ){ - const char *z = pOp->p4.z; - sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); - } - } - break; -} -#endif /* SQLITE_OMIT_SHARED_CACHE */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VBegin * * * P4 * -** -** P4 may be a pointer to an sqlite3_vtab structure. If so, call the -** xBegin method for that table. -** -** Also, whether or not P4 is set, check that this is not being called from -** within a callback to a virtual table xSync() method. If it is, the error -** code will be set to SQLITE_LOCKED. -*/ -case OP_VBegin: { -#if 0 /* local variables moved into u.cl */ - VTable *pVTab; -#endif /* local variables moved into u.cl */ - u.cl.pVTab = pOp->p4.pVtab; - rc = sqlite3VtabBegin(db, u.cl.pVTab); - if( u.cl.pVTab ) importVtabErrMsg(p, u.cl.pVTab->pVtab); - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VCreate P1 * * P4 * -** -** P4 is the name of a virtual table in database P1. Call the xCreate method -** for that table. -*/ -case OP_VCreate: { - rc = sqlite3VtabCallCreate(db, pOp->p1, pOp->p4.z, &p->zErrMsg); - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VDestroy P1 * * P4 * -** -** P4 is the name of a virtual table in database P1. Call the xDestroy method -** of that table. -*/ -case OP_VDestroy: { - p->inVtabMethod = 2; - rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z); - p->inVtabMethod = 0; - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VOpen P1 * * P4 * -** -** P4 is a pointer to a virtual table object, an sqlite3_vtab structure. -** P1 is a cursor number. This opcode opens a cursor to the virtual -** table and stores that cursor in P1. -*/ -case OP_VOpen: { -#if 0 /* local variables moved into u.cm */ - VdbeCursor *pCur; - sqlite3_vtab_cursor *pVtabCursor; - sqlite3_vtab *pVtab; - sqlite3_module *pModule; -#endif /* local variables moved into u.cm */ - - u.cm.pCur = 0; - u.cm.pVtabCursor = 0; - u.cm.pVtab = pOp->p4.pVtab->pVtab; - u.cm.pModule = (sqlite3_module *)u.cm.pVtab->pModule; - assert(u.cm.pVtab && u.cm.pModule); - rc = u.cm.pModule->xOpen(u.cm.pVtab, &u.cm.pVtabCursor); - importVtabErrMsg(p, u.cm.pVtab); - if( SQLITE_OK==rc ){ - /* Initialize sqlite3_vtab_cursor base class */ - u.cm.pVtabCursor->pVtab = u.cm.pVtab; - - /* Initialize vdbe cursor object */ - u.cm.pCur = allocateCursor(p, pOp->p1, 0, -1, 0); - if( u.cm.pCur ){ - u.cm.pCur->pVtabCursor = u.cm.pVtabCursor; - u.cm.pCur->pModule = u.cm.pVtabCursor->pVtab->pModule; - }else{ - db->mallocFailed = 1; - u.cm.pModule->xClose(u.cm.pVtabCursor); - } - } - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VFilter P1 P2 P3 P4 * -** -** P1 is a cursor opened using VOpen. P2 is an address to jump to if -** the filtered result set is empty. -** -** P4 is either NULL or a string that was generated by the xBestIndex -** method of the module. The interpretation of the P4 string is left -** to the module implementation. -** -** This opcode invokes the xFilter method on the virtual table specified -** by P1. The integer query plan parameter to xFilter is stored in register -** P3. Register P3+1 stores the argc parameter to be passed to the -** xFilter method. Registers P3+2..P3+1+argc are the argc -** additional parameters which are passed to -** xFilter as argv. Register P3+2 becomes argv[0] when passed to xFilter. -** -** A jump is made to P2 if the result set after filtering would be empty. -*/ -case OP_VFilter: { /* jump */ -#if 0 /* local variables moved into u.cn */ - int nArg; - int iQuery; - const sqlite3_module *pModule; - Mem *pQuery; - Mem *pArgc; - sqlite3_vtab_cursor *pVtabCursor; - sqlite3_vtab *pVtab; - VdbeCursor *pCur; - int res; - int i; - Mem **apArg; -#endif /* local variables moved into u.cn */ - - u.cn.pQuery = &aMem[pOp->p3]; - u.cn.pArgc = &u.cn.pQuery[1]; - u.cn.pCur = p->apCsr[pOp->p1]; - assert( memIsValid(u.cn.pQuery) ); - REGISTER_TRACE(pOp->p3, u.cn.pQuery); - assert( u.cn.pCur->pVtabCursor ); - u.cn.pVtabCursor = u.cn.pCur->pVtabCursor; - u.cn.pVtab = u.cn.pVtabCursor->pVtab; - u.cn.pModule = u.cn.pVtab->pModule; - - /* Grab the index number and argc parameters */ - assert( (u.cn.pQuery->flags&MEM_Int)!=0 && u.cn.pArgc->flags==MEM_Int ); - u.cn.nArg = (int)u.cn.pArgc->u.i; - u.cn.iQuery = (int)u.cn.pQuery->u.i; - - /* Invoke the xFilter method */ - { - u.cn.res = 0; - u.cn.apArg = p->apArg; - for(u.cn.i = 0; u.cn.iinVtabMethod = 1; - rc = u.cn.pModule->xFilter(u.cn.pVtabCursor, u.cn.iQuery, pOp->p4.z, u.cn.nArg, u.cn.apArg); - p->inVtabMethod = 0; - importVtabErrMsg(p, u.cn.pVtab); - if( rc==SQLITE_OK ){ - u.cn.res = u.cn.pModule->xEof(u.cn.pVtabCursor); - } - - if( u.cn.res ){ - pc = pOp->p2 - 1; - } - } - u.cn.pCur->nullRow = 0; - - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VColumn P1 P2 P3 * * -** -** Store the value of the P2-th column of -** the row of the virtual-table that the -** P1 cursor is pointing to into register P3. -*/ -case OP_VColumn: { -#if 0 /* local variables moved into u.co */ - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; - Mem *pDest; - sqlite3_context sContext; -#endif /* local variables moved into u.co */ - - VdbeCursor *pCur = p->apCsr[pOp->p1]; - assert( pCur->pVtabCursor ); - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.co.pDest = &aMem[pOp->p3]; - memAboutToChange(p, u.co.pDest); - if( pCur->nullRow ){ - sqlite3VdbeMemSetNull(u.co.pDest); - break; - } - u.co.pVtab = pCur->pVtabCursor->pVtab; - u.co.pModule = u.co.pVtab->pModule; - assert( u.co.pModule->xColumn ); - memset(&u.co.sContext, 0, sizeof(u.co.sContext)); - - /* The output cell may already have a buffer allocated. Move - ** the current contents to u.co.sContext.s so in case the user-function - ** can use the already allocated buffer instead of allocating a - ** new one. - */ - sqlite3VdbeMemMove(&u.co.sContext.s, u.co.pDest); - MemSetTypeFlag(&u.co.sContext.s, MEM_Null); - - rc = u.co.pModule->xColumn(pCur->pVtabCursor, &u.co.sContext, pOp->p2); - importVtabErrMsg(p, u.co.pVtab); - if( u.co.sContext.isError ){ - rc = u.co.sContext.isError; - } - - /* Copy the result of the function to the P3 register. We - ** do this regardless of whether or not an error occurred to ensure any - ** dynamic allocation in u.co.sContext.s (a Mem struct) is released. - */ - sqlite3VdbeChangeEncoding(&u.co.sContext.s, encoding); - sqlite3VdbeMemMove(u.co.pDest, &u.co.sContext.s); - REGISTER_TRACE(pOp->p3, u.co.pDest); - UPDATE_MAX_BLOBSIZE(u.co.pDest); - - if( sqlite3VdbeMemTooBig(u.co.pDest) ){ - goto too_big; - } - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VNext P1 P2 * * * -** -** Advance virtual table P1 to the next row in its result set and -** jump to instruction P2. Or, if the virtual table has reached -** the end of its result set, then fall through to the next instruction. -*/ -case OP_VNext: { /* jump */ -#if 0 /* local variables moved into u.cp */ - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; - int res; - VdbeCursor *pCur; -#endif /* local variables moved into u.cp */ - - u.cp.res = 0; - u.cp.pCur = p->apCsr[pOp->p1]; - assert( u.cp.pCur->pVtabCursor ); - if( u.cp.pCur->nullRow ){ - break; - } - u.cp.pVtab = u.cp.pCur->pVtabCursor->pVtab; - u.cp.pModule = u.cp.pVtab->pModule; - assert( u.cp.pModule->xNext ); - - /* Invoke the xNext() method of the module. There is no way for the - ** underlying implementation to return an error if one occurs during - ** xNext(). Instead, if an error occurs, true is returned (indicating that - ** data is available) and the error code returned when xColumn or - ** some other method is next invoked on the save virtual table cursor. - */ - p->inVtabMethod = 1; - rc = u.cp.pModule->xNext(u.cp.pCur->pVtabCursor); - p->inVtabMethod = 0; - importVtabErrMsg(p, u.cp.pVtab); - if( rc==SQLITE_OK ){ - u.cp.res = u.cp.pModule->xEof(u.cp.pCur->pVtabCursor); - } - - if( !u.cp.res ){ - /* If there is data, jump to P2 */ - pc = pOp->p2 - 1; - } - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VRename P1 * * P4 * -** -** P4 is a pointer to a virtual table object, an sqlite3_vtab structure. -** This opcode invokes the corresponding xRename method. The value -** in register P1 is passed as the zName argument to the xRename method. -*/ -case OP_VRename: { -#if 0 /* local variables moved into u.cq */ - sqlite3_vtab *pVtab; - Mem *pName; -#endif /* local variables moved into u.cq */ - - u.cq.pVtab = pOp->p4.pVtab->pVtab; - u.cq.pName = &aMem[pOp->p1]; - assert( u.cq.pVtab->pModule->xRename ); - assert( memIsValid(u.cq.pName) ); - REGISTER_TRACE(pOp->p1, u.cq.pName); - assert( u.cq.pName->flags & MEM_Str ); - testcase( u.cq.pName->enc==SQLITE_UTF8 ); - testcase( u.cq.pName->enc==SQLITE_UTF16BE ); - testcase( u.cq.pName->enc==SQLITE_UTF16LE ); - rc = sqlite3VdbeChangeEncoding(u.cq.pName, SQLITE_UTF8); - if( rc==SQLITE_OK ){ - rc = u.cq.pVtab->pModule->xRename(u.cq.pVtab, u.cq.pName->z); - importVtabErrMsg(p, u.cq.pVtab); - p->expired = 0; - } - break; -} -#endif - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VUpdate P1 P2 P3 P4 * -** -** P4 is a pointer to a virtual table object, an sqlite3_vtab structure. -** This opcode invokes the corresponding xUpdate method. P2 values -** are contiguous memory cells starting at P3 to pass to the xUpdate -** invocation. The value in register (P3+P2-1) corresponds to the -** p2th element of the argv array passed to xUpdate. -** -** The xUpdate method will do a DELETE or an INSERT or both. -** The argv[0] element (which corresponds to memory cell P3) -** is the rowid of a row to delete. If argv[0] is NULL then no -** deletion occurs. The argv[1] element is the rowid of the new -** row. This can be NULL to have the virtual table select the new -** rowid for itself. The subsequent elements in the array are -** the values of columns in the new row. -** -** If P2==1 then no insert is performed. argv[0] is the rowid of -** a row to delete. -** -** P1 is a boolean flag. If it is set to true and the xUpdate call -** is successful, then the value returned by sqlite3_last_insert_rowid() -** is set to the value of the rowid for the row just inserted. -*/ -case OP_VUpdate: { -#if 0 /* local variables moved into u.cr */ - sqlite3_vtab *pVtab; - sqlite3_module *pModule; - int nArg; - int i; - sqlite_int64 rowid; - Mem **apArg; - Mem *pX; -#endif /* local variables moved into u.cr */ - - assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback - || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace - ); - u.cr.pVtab = pOp->p4.pVtab->pVtab; - u.cr.pModule = (sqlite3_module *)u.cr.pVtab->pModule; - u.cr.nArg = pOp->p2; - assert( pOp->p4type==P4_VTAB ); - if( ALWAYS(u.cr.pModule->xUpdate) ){ - u8 vtabOnConflict = db->vtabOnConflict; - u.cr.apArg = p->apArg; - u.cr.pX = &aMem[pOp->p3]; - for(u.cr.i=0; u.cr.ivtabOnConflict = pOp->p5; - rc = u.cr.pModule->xUpdate(u.cr.pVtab, u.cr.nArg, u.cr.apArg, &u.cr.rowid); - db->vtabOnConflict = vtabOnConflict; - importVtabErrMsg(p, u.cr.pVtab); - if( rc==SQLITE_OK && pOp->p1 ){ - assert( u.cr.nArg>1 && u.cr.apArg[0] && (u.cr.apArg[0]->flags&MEM_Null) ); - db->lastRowid = lastRowid = u.cr.rowid; - } - if( (rc&0xff)==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ - if( pOp->p5==OE_Ignore ){ - rc = SQLITE_OK; - }else{ - p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5); - } - }else{ - p->nChange++; - } - } - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* Opcode: Pagecount P1 P2 * * * -** -** Write the current number of pages in database P1 to memory cell P2. -*/ -case OP_Pagecount: { /* out2-prerelease */ - pOut->u.i = sqlite3BtreeLastPage(db->aDb[pOp->p1].pBt); - break; -} -#endif - - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* Opcode: MaxPgcnt P1 P2 P3 * * -** -** Try to set the maximum page count for database P1 to the value in P3. -** Do not let the maximum page count fall below the current page count and -** do not change the maximum page count value if P3==0. -** -** Store the maximum page count after the change in register P2. -*/ -case OP_MaxPgcnt: { /* out2-prerelease */ - unsigned int newMax; - Btree *pBt; - - pBt = db->aDb[pOp->p1].pBt; - newMax = 0; - if( pOp->p3 ){ - newMax = sqlite3BtreeLastPage(pBt); - if( newMax < (unsigned)pOp->p3 ) newMax = (unsigned)pOp->p3; - } - pOut->u.i = sqlite3BtreeMaxPageCount(pBt, newMax); - break; -} -#endif - - -#ifndef SQLITE_OMIT_TRACE -/* Opcode: Trace * * * P4 * -** -** If tracing is enabled (by the sqlite3_trace()) interface, then -** the UTF-8 string contained in P4 is emitted on the trace callback. -*/ -case OP_Trace: { -#if 0 /* local variables moved into u.cs */ - char *zTrace; - char *z; -#endif /* local variables moved into u.cs */ - - if( db->xTrace - && !p->doingRerun - && (u.cs.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 - ){ - u.cs.z = sqlite3VdbeExpandSql(p, u.cs.zTrace); - db->xTrace(db->pTraceArg, u.cs.z); - sqlite3DbFree(db, u.cs.z); - } -#ifdef SQLITE_DEBUG - if( (db->flags & SQLITE_SqlTrace)!=0 - && (u.cs.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 - ){ - sqlite3DebugPrintf("SQL-trace: %s\n", u.cs.zTrace); - } -#endif /* SQLITE_DEBUG */ - break; -} -#endif - - -/* Opcode: Noop * * * * * -** -** Do nothing. This instruction is often useful as a jump -** destination. -*/ -/* -** The magic Explain opcode are only inserted when explain==2 (which -** is to say when the EXPLAIN QUERY PLAN syntax is used.) -** This opcode records information from the optimizer. It is the -** the same as a no-op. This opcodesnever appears in a real VM program. -*/ -default: { /* This is really OP_Noop and OP_Explain */ - assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain ); - break; -} - -/***************************************************************************** -** The cases of the switch statement above this line should all be indented -** by 6 spaces. But the left-most 6 spaces have been removed to improve the -** readability. From this point on down, the normal indentation rules are -** restored. -*****************************************************************************/ - } - -#ifdef VDBE_PROFILE - { - u64 elapsed = sqlite3Hwtime() - start; - pOp->cycles += elapsed; - pOp->cnt++; -#if 0 - fprintf(stdout, "%10llu ", elapsed); - sqlite3VdbePrintOp(stdout, origPc, &aOp[origPc]); -#endif - } -#endif - - /* The following code adds nothing to the actual functionality - ** of the program. It is only here for testing and debugging. - ** On the other hand, it does burn CPU cycles every time through - ** the evaluator loop. So we can leave it out when NDEBUG is defined. - */ -#ifndef NDEBUG - assert( pc>=-1 && pcnOp ); - -#ifdef SQLITE_DEBUG - if( p->trace ){ - if( rc!=0 ) fprintf(p->trace,"rc=%d\n",rc); - if( pOp->opflags & (OPFLG_OUT2_PRERELEASE|OPFLG_OUT2) ){ - registerTrace(p->trace, pOp->p2, &aMem[pOp->p2]); - } - if( pOp->opflags & OPFLG_OUT3 ){ - registerTrace(p->trace, pOp->p3, &aMem[pOp->p3]); - } - } -#endif /* SQLITE_DEBUG */ -#endif /* NDEBUG */ - } /* The end of the for(;;) loop the loops through opcodes */ - - /* If we reach this point, it means that execution is finished with - ** an error of some kind. - */ -vdbe_error_halt: - assert( rc ); - p->rc = rc; - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(rc, "statement aborts at %d: [%s] %s", - pc, p->zSql, p->zErrMsg); - sqlite3VdbeHalt(p); - if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; - rc = SQLITE_ERROR; - if( resetSchemaOnFault>0 ){ - sqlite3ResetOneSchema(db, resetSchemaOnFault-1); - } - - /* This is the only way out of this procedure. We have to - ** release the mutexes on btrees that were acquired at the - ** top. */ -vdbe_return: - db->lastRowid = lastRowid; - sqlite3VdbeLeave(p); - return rc; - - /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH - ** is encountered. - */ -too_big: - sqlite3SetString(&p->zErrMsg, db, "string or blob too big"); - rc = SQLITE_TOOBIG; - goto vdbe_error_halt; - - /* Jump to here if a malloc() fails. - */ -no_mem: - db->mallocFailed = 1; - sqlite3SetString(&p->zErrMsg, db, "out of memory"); - rc = SQLITE_NOMEM; - goto vdbe_error_halt; - - /* Jump to here for any other kind of fatal error. The "rc" variable - ** should hold the error number. - */ -abort_due_to_error: - assert( p->zErrMsg==0 ); - if( db->mallocFailed ) rc = SQLITE_NOMEM; - if( rc!=SQLITE_IOERR_NOMEM ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc)); - } - goto vdbe_error_halt; - - /* Jump to here if the sqlite3_interrupt() API sets the interrupt - ** flag. - */ -abort_due_to_interrupt: - assert( db->u1.isInterrupted ); - rc = SQLITE_INTERRUPT; - p->rc = rc; - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc)); - goto vdbe_error_halt; -} - -/************** End of vdbe.c ************************************************/ -/************** Begin file vdbeblob.c ****************************************/ -/* -** 2007 May 1 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code used to implement incremental BLOB I/O. -*/ - - -#ifndef SQLITE_OMIT_INCRBLOB - -/* -** Valid sqlite3_blob* handles point to Incrblob structures. -*/ -typedef struct Incrblob Incrblob; -struct Incrblob { - int flags; /* Copy of "flags" passed to sqlite3_blob_open() */ - int nByte; /* Size of open blob, in bytes */ - int iOffset; /* Byte offset of blob in cursor data */ - int iCol; /* Table column this handle is open on */ - BtCursor *pCsr; /* Cursor pointing at blob row */ - sqlite3_stmt *pStmt; /* Statement holding cursor open */ - sqlite3 *db; /* The associated database */ -}; - - -/* -** This function is used by both blob_open() and blob_reopen(). It seeks -** the b-tree cursor associated with blob handle p to point to row iRow. -** If successful, SQLITE_OK is returned and subsequent calls to -** sqlite3_blob_read() or sqlite3_blob_write() access the specified row. -** -** If an error occurs, or if the specified row does not exist or does not -** contain a value of type TEXT or BLOB in the column nominated when the -** blob handle was opened, then an error code is returned and *pzErr may -** be set to point to a buffer containing an error message. It is the -** responsibility of the caller to free the error message buffer using -** sqlite3DbFree(). -** -** If an error does occur, then the b-tree cursor is closed. All subsequent -** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will -** immediately return SQLITE_ABORT. -*/ -static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ - int rc; /* Error code */ - char *zErr = 0; /* Error message */ - Vdbe *v = (Vdbe *)p->pStmt; - - /* Set the value of the SQL statements only variable to integer iRow. - ** This is done directly instead of using sqlite3_bind_int64() to avoid - ** triggering asserts related to mutexes. - */ - assert( v->aVar[0].flags&MEM_Int ); - v->aVar[0].u.i = iRow; - - rc = sqlite3_step(p->pStmt); - if( rc==SQLITE_ROW ){ - u32 type = v->apCsr[0]->aType[p->iCol]; - if( type<12 ){ - zErr = sqlite3MPrintf(p->db, "cannot open value of type %s", - type==0?"null": type==7?"real": "integer" - ); - rc = SQLITE_ERROR; - sqlite3_finalize(p->pStmt); - p->pStmt = 0; - }else{ - p->iOffset = v->apCsr[0]->aOffset[p->iCol]; - p->nByte = sqlite3VdbeSerialTypeLen(type); - p->pCsr = v->apCsr[0]->pCursor; - sqlite3BtreeEnterCursor(p->pCsr); - sqlite3BtreeCacheOverflow(p->pCsr); - sqlite3BtreeLeaveCursor(p->pCsr); - } - } - - if( rc==SQLITE_ROW ){ - rc = SQLITE_OK; - }else if( p->pStmt ){ - rc = sqlite3_finalize(p->pStmt); - p->pStmt = 0; - if( rc==SQLITE_OK ){ - zErr = sqlite3MPrintf(p->db, "no such rowid: %lld", iRow); - rc = SQLITE_ERROR; - }else{ - zErr = sqlite3MPrintf(p->db, "%s", sqlite3_errmsg(p->db)); - } - } - - assert( rc!=SQLITE_OK || zErr==0 ); - assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE ); - - *pzErr = zErr; - return rc; -} - -/* -** Open a blob handle. -*/ -SQLITE_API int sqlite3_blob_open( - sqlite3* db, /* The database connection */ - const char *zDb, /* The attached database containing the blob */ - const char *zTable, /* The table containing the blob */ - const char *zColumn, /* The column containing the blob */ - sqlite_int64 iRow, /* The row containing the glob */ - int flags, /* True -> read/write access, false -> read-only */ - sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */ -){ - int nAttempt = 0; - int iCol; /* Index of zColumn in row-record */ - - /* This VDBE program seeks a btree cursor to the identified - ** db/table/row entry. The reason for using a vdbe program instead - ** of writing code to use the b-tree layer directly is that the - ** vdbe program will take advantage of the various transaction, - ** locking and error handling infrastructure built into the vdbe. - ** - ** After seeking the cursor, the vdbe executes an OP_ResultRow. - ** Code external to the Vdbe then "borrows" the b-tree cursor and - ** uses it to implement the blob_read(), blob_write() and - ** blob_bytes() functions. - ** - ** The sqlite3_blob_close() function finalizes the vdbe program, - ** which closes the b-tree cursor and (possibly) commits the - ** transaction. - */ - static const VdbeOpList openBlob[] = { - {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */ - {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */ - {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ - - /* One of the following two instructions is replaced by an OP_Noop. */ - {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ - {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ - - {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */ - {OP_NotExists, 0, 10, 1}, /* 6: Seek the cursor */ - {OP_Column, 0, 0, 1}, /* 7 */ - {OP_ResultRow, 1, 0, 0}, /* 8 */ - {OP_Goto, 0, 5, 0}, /* 9 */ - {OP_Close, 0, 0, 0}, /* 10 */ - {OP_Halt, 0, 0, 0}, /* 11 */ - }; - - int rc = SQLITE_OK; - char *zErr = 0; - Table *pTab; - Parse *pParse = 0; - Incrblob *pBlob = 0; - - flags = !!flags; /* flags = (flags ? 1 : 0); */ - *ppBlob = 0; - - sqlite3_mutex_enter(db->mutex); - - pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob)); - if( !pBlob ) goto blob_open_out; - pParse = sqlite3StackAllocRaw(db, sizeof(*pParse)); - if( !pParse ) goto blob_open_out; - - do { - memset(pParse, 0, sizeof(Parse)); - pParse->db = db; - sqlite3DbFree(db, zErr); - zErr = 0; - - sqlite3BtreeEnterAll(db); - pTab = sqlite3LocateTable(pParse, 0, zTable, zDb); - if( pTab && IsVirtual(pTab) ){ - pTab = 0; - sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable); - } -#ifndef SQLITE_OMIT_VIEW - if( pTab && pTab->pSelect ){ - pTab = 0; - sqlite3ErrorMsg(pParse, "cannot open view: %s", zTable); - } -#endif - if( !pTab ){ - if( pParse->zErrMsg ){ - sqlite3DbFree(db, zErr); - zErr = pParse->zErrMsg; - pParse->zErrMsg = 0; - } - rc = SQLITE_ERROR; - sqlite3BtreeLeaveAll(db); - goto blob_open_out; - } - - /* Now search pTab for the exact column. */ - for(iCol=0; iColnCol; iCol++) { - if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ - break; - } - } - if( iCol==pTab->nCol ){ - sqlite3DbFree(db, zErr); - zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); - rc = SQLITE_ERROR; - sqlite3BtreeLeaveAll(db); - goto blob_open_out; - } - - /* If the value is being opened for writing, check that the - ** column is not indexed, and that it is not part of a foreign key. - ** It is against the rules to open a column to which either of these - ** descriptions applies for writing. */ - if( flags ){ - const char *zFault = 0; - Index *pIdx; -#ifndef SQLITE_OMIT_FOREIGN_KEY - if( db->flags&SQLITE_ForeignKeys ){ - /* Check that the column is not part of an FK child key definition. It - ** is not necessary to check if it is part of a parent key, as parent - ** key columns must be indexed. The check below will pick up this - ** case. */ - FKey *pFKey; - for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ - int j; - for(j=0; jnCol; j++){ - if( pFKey->aCol[j].iFrom==iCol ){ - zFault = "foreign key"; - } - } - } - } -#endif - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int j; - for(j=0; jnColumn; j++){ - if( pIdx->aiColumn[j]==iCol ){ - zFault = "indexed"; - } - } - } - if( zFault ){ - sqlite3DbFree(db, zErr); - zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); - rc = SQLITE_ERROR; - sqlite3BtreeLeaveAll(db); - goto blob_open_out; - } - } - - pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db); - assert( pBlob->pStmt || db->mallocFailed ); - if( pBlob->pStmt ){ - Vdbe *v = (Vdbe *)pBlob->pStmt; - int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - - sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); - - - /* Configure the OP_Transaction */ - sqlite3VdbeChangeP1(v, 0, iDb); - sqlite3VdbeChangeP2(v, 0, flags); - - /* Configure the OP_VerifyCookie */ - sqlite3VdbeChangeP1(v, 1, iDb); - sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); - sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration); - - /* Make sure a mutex is held on the table to be accessed */ - sqlite3VdbeUsesBtree(v, iDb); - - /* Configure the OP_TableLock instruction */ -#ifdef SQLITE_OMIT_SHARED_CACHE - sqlite3VdbeChangeToNoop(v, 2); -#else - sqlite3VdbeChangeP1(v, 2, iDb); - sqlite3VdbeChangeP2(v, 2, pTab->tnum); - sqlite3VdbeChangeP3(v, 2, flags); - sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); -#endif - - /* Remove either the OP_OpenWrite or OpenRead. Set the P2 - ** parameter of the other to pTab->tnum. */ - sqlite3VdbeChangeToNoop(v, 4 - flags); - sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum); - sqlite3VdbeChangeP3(v, 3 + flags, iDb); - - /* Configure the number of columns. Configure the cursor to - ** think that the table has one more column than it really - ** does. An OP_Column to retrieve this imaginary column will - ** always return an SQL NULL. This is useful because it means - ** we can invoke OP_Column to fill in the vdbe cursors type - ** and offset cache without causing any IO. - */ - sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); - sqlite3VdbeChangeP2(v, 7, pTab->nCol); - if( !db->mallocFailed ){ - pParse->nVar = 1; - pParse->nMem = 1; - pParse->nTab = 1; - sqlite3VdbeMakeReady(v, pParse); - } - } - - pBlob->flags = flags; - pBlob->iCol = iCol; - pBlob->db = db; - sqlite3BtreeLeaveAll(db); - if( db->mallocFailed ){ - goto blob_open_out; - } - sqlite3_bind_int64(pBlob->pStmt, 1, iRow); - rc = blobSeekToRow(pBlob, iRow, &zErr); - } while( (++nAttempt)<5 && rc==SQLITE_SCHEMA ); - -blob_open_out: - if( rc==SQLITE_OK && db->mallocFailed==0 ){ - *ppBlob = (sqlite3_blob *)pBlob; - }else{ - if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); - sqlite3DbFree(db, pBlob); - } - sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr); - sqlite3DbFree(db, zErr); - sqlite3StackFree(db, pParse); - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/* -** Close a blob handle that was previously created using -** sqlite3_blob_open(). -*/ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *pBlob){ - Incrblob *p = (Incrblob *)pBlob; - int rc; - sqlite3 *db; - - if( p ){ - db = p->db; - sqlite3_mutex_enter(db->mutex); - rc = sqlite3_finalize(p->pStmt); - sqlite3DbFree(db, p); - sqlite3_mutex_leave(db->mutex); - }else{ - rc = SQLITE_OK; - } - return rc; -} - -/* -** Perform a read or write operation on a blob -*/ -static int blobReadWrite( - sqlite3_blob *pBlob, - void *z, - int n, - int iOffset, - int (*xCall)(BtCursor*, u32, u32, void*) -){ - int rc; - Incrblob *p = (Incrblob *)pBlob; - Vdbe *v; - sqlite3 *db; - - if( p==0 ) return SQLITE_MISUSE_BKPT; - db = p->db; - sqlite3_mutex_enter(db->mutex); - v = (Vdbe*)p->pStmt; - - if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){ - /* Request is out of range. Return a transient error. */ - rc = SQLITE_ERROR; - sqlite3Error(db, SQLITE_ERROR, 0); - }else if( v==0 ){ - /* If there is no statement handle, then the blob-handle has - ** already been invalidated. Return SQLITE_ABORT in this case. - */ - rc = SQLITE_ABORT; - }else{ - /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is - ** returned, clean-up the statement handle. - */ - assert( db == v->db ); - sqlite3BtreeEnterCursor(p->pCsr); - rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); - sqlite3BtreeLeaveCursor(p->pCsr); - if( rc==SQLITE_ABORT ){ - sqlite3VdbeFinalize(v); - p->pStmt = 0; - }else{ - db->errCode = rc; - v->rc = rc; - } - } - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/* -** Read data from a blob handle. -*/ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){ - return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData); -} - -/* -** Write data to a blob handle. -*/ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ - return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData); -} - -/* -** Query a blob handle for the size of the data. -** -** The Incrblob.nByte field is fixed for the lifetime of the Incrblob -** so no mutex is required for access. -*/ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){ - Incrblob *p = (Incrblob *)pBlob; - return (p && p->pStmt) ? p->nByte : 0; -} - -/* -** Move an existing blob handle to point to a different row of the same -** database table. -** -** If an error occurs, or if the specified row does not exist or does not -** contain a blob or text value, then an error code is returned and the -** database handle error code and message set. If this happens, then all -** subsequent calls to sqlite3_blob_xxx() functions (except blob_close()) -** immediately return SQLITE_ABORT. -*/ -SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ - int rc; - Incrblob *p = (Incrblob *)pBlob; - sqlite3 *db; - - if( p==0 ) return SQLITE_MISUSE_BKPT; - db = p->db; - sqlite3_mutex_enter(db->mutex); - - if( p->pStmt==0 ){ - /* If there is no statement handle, then the blob-handle has - ** already been invalidated. Return SQLITE_ABORT in this case. - */ - rc = SQLITE_ABORT; - }else{ - char *zErr; - rc = blobSeekToRow(p, iRow, &zErr); - if( rc!=SQLITE_OK ){ - sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr); - sqlite3DbFree(db, zErr); - } - assert( rc!=SQLITE_SCHEMA ); - } - - rc = sqlite3ApiExit(db, rc); - assert( rc==SQLITE_OK || p->pStmt==0 ); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -#endif /* #ifndef SQLITE_OMIT_INCRBLOB */ - -/************** End of vdbeblob.c ********************************************/ -/************** Begin file vdbesort.c ****************************************/ -/* -** 2011 July 9 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code for the VdbeSorter object, used in concert with -** a VdbeCursor to sort large numbers of keys (as may be required, for -** example, by CREATE INDEX statements on tables too large to fit in main -** memory). -*/ - - - -typedef struct VdbeSorterIter VdbeSorterIter; -typedef struct SorterRecord SorterRecord; -typedef struct FileWriter FileWriter; - -/* -** NOTES ON DATA STRUCTURE USED FOR N-WAY MERGES: -** -** As keys are added to the sorter, they are written to disk in a series -** of sorted packed-memory-arrays (PMAs). The size of each PMA is roughly -** the same as the cache-size allowed for temporary databases. In order -** to allow the caller to extract keys from the sorter in sorted order, -** all PMAs currently stored on disk must be merged together. This comment -** describes the data structure used to do so. The structure supports -** merging any number of arrays in a single pass with no redundant comparison -** operations. -** -** The aIter[] array contains an iterator for each of the PMAs being merged. -** An aIter[] iterator either points to a valid key or else is at EOF. For -** the purposes of the paragraphs below, we assume that the array is actually -** N elements in size, where N is the smallest power of 2 greater to or equal -** to the number of iterators being merged. The extra aIter[] elements are -** treated as if they are empty (always at EOF). -** -** The aTree[] array is also N elements in size. The value of N is stored in -** the VdbeSorter.nTree variable. -** -** The final (N/2) elements of aTree[] contain the results of comparing -** pairs of iterator keys together. Element i contains the result of -** comparing aIter[2*i-N] and aIter[2*i-N+1]. Whichever key is smaller, the -** aTree element is set to the index of it. -** -** For the purposes of this comparison, EOF is considered greater than any -** other key value. If the keys are equal (only possible with two EOF -** values), it doesn't matter which index is stored. -** -** The (N/4) elements of aTree[] that preceed the final (N/2) described -** above contains the index of the smallest of each block of 4 iterators. -** And so on. So that aTree[1] contains the index of the iterator that -** currently points to the smallest key value. aTree[0] is unused. -** -** Example: -** -** aIter[0] -> Banana -** aIter[1] -> Feijoa -** aIter[2] -> Elderberry -** aIter[3] -> Currant -** aIter[4] -> Grapefruit -** aIter[5] -> Apple -** aIter[6] -> Durian -** aIter[7] -> EOF -** -** aTree[] = { X, 5 0, 5 0, 3, 5, 6 } -** -** The current element is "Apple" (the value of the key indicated by -** iterator 5). When the Next() operation is invoked, iterator 5 will -** be advanced to the next key in its segment. Say the next key is -** "Eggplant": -** -** aIter[5] -> Eggplant -** -** The contents of aTree[] are updated first by comparing the new iterator -** 5 key to the current key of iterator 4 (still "Grapefruit"). The iterator -** 5 value is still smaller, so aTree[6] is set to 5. And so on up the tree. -** The value of iterator 6 - "Durian" - is now smaller than that of iterator -** 5, so aTree[3] is set to 6. Key 0 is smaller than key 6 (BananaaAlloc); - sqlite3DbFree(db, pIter->aBuffer); - memset(pIter, 0, sizeof(VdbeSorterIter)); -} - -/* -** Read nByte bytes of data from the stream of data iterated by object p. -** If successful, set *ppOut to point to a buffer containing the data -** and return SQLITE_OK. Otherwise, if an error occurs, return an SQLite -** error code. -** -** The buffer indicated by *ppOut may only be considered valid until the -** next call to this function. -*/ -static int vdbeSorterIterRead( - sqlite3 *db, /* Database handle (for malloc) */ - VdbeSorterIter *p, /* Iterator */ - int nByte, /* Bytes of data to read */ - u8 **ppOut /* OUT: Pointer to buffer containing data */ -){ - int iBuf; /* Offset within buffer to read from */ - int nAvail; /* Bytes of data available in buffer */ - assert( p->aBuffer ); - - /* If there is no more data to be read from the buffer, read the next - ** p->nBuffer bytes of data from the file into it. Or, if there are less - ** than p->nBuffer bytes remaining in the PMA, read all remaining data. */ - iBuf = p->iReadOff % p->nBuffer; - if( iBuf==0 ){ - int nRead; /* Bytes to read from disk */ - int rc; /* sqlite3OsRead() return code */ - - /* Determine how many bytes of data to read. */ - if( (p->iEof - p->iReadOff) > (i64)p->nBuffer ){ - nRead = p->nBuffer; - }else{ - nRead = (int)(p->iEof - p->iReadOff); - } - assert( nRead>0 ); - - /* Read data from the file. Return early if an error occurs. */ - rc = sqlite3OsRead(p->pFile, p->aBuffer, nRead, p->iReadOff); - assert( rc!=SQLITE_IOERR_SHORT_READ ); - if( rc!=SQLITE_OK ) return rc; - } - nAvail = p->nBuffer - iBuf; - - if( nByte<=nAvail ){ - /* The requested data is available in the in-memory buffer. In this - ** case there is no need to make a copy of the data, just return a - ** pointer into the buffer to the caller. */ - *ppOut = &p->aBuffer[iBuf]; - p->iReadOff += nByte; - }else{ - /* The requested data is not all available in the in-memory buffer. - ** In this case, allocate space at p->aAlloc[] to copy the requested - ** range into. Then return a copy of pointer p->aAlloc to the caller. */ - int nRem; /* Bytes remaining to copy */ - - /* Extend the p->aAlloc[] allocation if required. */ - if( p->nAllocnAlloc*2; - while( nByte>nNew ) nNew = nNew*2; - p->aAlloc = sqlite3DbReallocOrFree(db, p->aAlloc, nNew); - if( !p->aAlloc ) return SQLITE_NOMEM; - p->nAlloc = nNew; - } - - /* Copy as much data as is available in the buffer into the start of - ** p->aAlloc[]. */ - memcpy(p->aAlloc, &p->aBuffer[iBuf], nAvail); - p->iReadOff += nAvail; - nRem = nByte - nAvail; - - /* The following loop copies up to p->nBuffer bytes per iteration into - ** the p->aAlloc[] buffer. */ - while( nRem>0 ){ - int rc; /* vdbeSorterIterRead() return code */ - int nCopy; /* Number of bytes to copy */ - u8 *aNext; /* Pointer to buffer to copy data from */ - - nCopy = nRem; - if( nRem>p->nBuffer ) nCopy = p->nBuffer; - rc = vdbeSorterIterRead(db, p, nCopy, &aNext); - if( rc!=SQLITE_OK ) return rc; - assert( aNext!=p->aAlloc ); - memcpy(&p->aAlloc[nByte - nRem], aNext, nCopy); - nRem -= nCopy; - } - - *ppOut = p->aAlloc; - } - - return SQLITE_OK; -} - -/* -** Read a varint from the stream of data accessed by p. Set *pnOut to -** the value read. -*/ -static int vdbeSorterIterVarint(sqlite3 *db, VdbeSorterIter *p, u64 *pnOut){ - int iBuf; - - iBuf = p->iReadOff % p->nBuffer; - if( iBuf && (p->nBuffer-iBuf)>=9 ){ - p->iReadOff += sqlite3GetVarint(&p->aBuffer[iBuf], pnOut); - }else{ - u8 aVarint[16], *a; - int i = 0, rc; - do{ - rc = vdbeSorterIterRead(db, p, 1, &a); - if( rc ) return rc; - aVarint[(i++)&0xf] = a[0]; - }while( (a[0]&0x80)!=0 ); - sqlite3GetVarint(aVarint, pnOut); - } - - return SQLITE_OK; -} - - -/* -** Advance iterator pIter to the next key in its PMA. Return SQLITE_OK if -** no error occurs, or an SQLite error code if one does. -*/ -static int vdbeSorterIterNext( - sqlite3 *db, /* Database handle (for sqlite3DbMalloc() ) */ - VdbeSorterIter *pIter /* Iterator to advance */ -){ - int rc; /* Return Code */ - u64 nRec = 0; /* Size of record in bytes */ - - if( pIter->iReadOff>=pIter->iEof ){ - /* This is an EOF condition */ - vdbeSorterIterZero(db, pIter); - return SQLITE_OK; - } - - rc = vdbeSorterIterVarint(db, pIter, &nRec); - if( rc==SQLITE_OK ){ - pIter->nKey = (int)nRec; - rc = vdbeSorterIterRead(db, pIter, (int)nRec, &pIter->aKey); - } - - return rc; -} - -/* -** Initialize iterator pIter to scan through the PMA stored in file pFile -** starting at offset iStart and ending at offset iEof-1. This function -** leaves the iterator pointing to the first key in the PMA (or EOF if the -** PMA is empty). -*/ -static int vdbeSorterIterInit( - sqlite3 *db, /* Database handle */ - const VdbeSorter *pSorter, /* Sorter object */ - i64 iStart, /* Start offset in pFile */ - VdbeSorterIter *pIter, /* Iterator to populate */ - i64 *pnByte /* IN/OUT: Increment this value by PMA size */ -){ - int rc = SQLITE_OK; - int nBuf; - - nBuf = sqlite3BtreeGetPageSize(db->aDb[0].pBt); - - assert( pSorter->iWriteOff>iStart ); - assert( pIter->aAlloc==0 ); - assert( pIter->aBuffer==0 ); - pIter->pFile = pSorter->pTemp1; - pIter->iReadOff = iStart; - pIter->nAlloc = 128; - pIter->aAlloc = (u8 *)sqlite3DbMallocRaw(db, pIter->nAlloc); - pIter->nBuffer = nBuf; - pIter->aBuffer = (u8 *)sqlite3DbMallocRaw(db, nBuf); - - if( !pIter->aBuffer ){ - rc = SQLITE_NOMEM; - }else{ - int iBuf; - - iBuf = iStart % nBuf; - if( iBuf ){ - int nRead = nBuf - iBuf; - if( (iStart + nRead) > pSorter->iWriteOff ){ - nRead = (int)(pSorter->iWriteOff - iStart); - } - rc = sqlite3OsRead( - pSorter->pTemp1, &pIter->aBuffer[iBuf], nRead, iStart - ); - assert( rc!=SQLITE_IOERR_SHORT_READ ); - } - - if( rc==SQLITE_OK ){ - u64 nByte; /* Size of PMA in bytes */ - pIter->iEof = pSorter->iWriteOff; - rc = vdbeSorterIterVarint(db, pIter, &nByte); - pIter->iEof = pIter->iReadOff + nByte; - *pnByte += nByte; - } - } - - if( rc==SQLITE_OK ){ - rc = vdbeSorterIterNext(db, pIter); - } - return rc; -} - - -/* -** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2, -** size nKey2 bytes). Argument pKeyInfo supplies the collation functions -** used by the comparison. If an error occurs, return an SQLite error code. -** Otherwise, return SQLITE_OK and set *pRes to a negative, zero or positive -** value, depending on whether key1 is smaller, equal to or larger than key2. -** -** If the bOmitRowid argument is non-zero, assume both keys end in a rowid -** field. For the purposes of the comparison, ignore it. Also, if bOmitRowid -** is true and key1 contains even a single NULL value, it is considered to -** be less than key2. Even if key2 also contains NULL values. -** -** If pKey2 is passed a NULL pointer, then it is assumed that the pCsr->aSpace -** has been allocated and contains an unpacked record that is used as key2. -*/ -static void vdbeSorterCompare( - const VdbeCursor *pCsr, /* Cursor object (for pKeyInfo) */ - int bOmitRowid, /* Ignore rowid field at end of keys */ - const void *pKey1, int nKey1, /* Left side of comparison */ - const void *pKey2, int nKey2, /* Right side of comparison */ - int *pRes /* OUT: Result of comparison */ -){ - KeyInfo *pKeyInfo = pCsr->pKeyInfo; - VdbeSorter *pSorter = pCsr->pSorter; - UnpackedRecord *r2 = pSorter->pUnpacked; - int i; - - if( pKey2 ){ - sqlite3VdbeRecordUnpack(pKeyInfo, nKey2, pKey2, r2); - } - - if( bOmitRowid ){ - r2->nField = pKeyInfo->nField; - assert( r2->nField>0 ); - for(i=0; inField; i++){ - if( r2->aMem[i].flags & MEM_Null ){ - *pRes = -1; - return; - } - } - r2->flags |= UNPACKED_PREFIX_MATCH; - } - - *pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2); -} - -/* -** This function is called to compare two iterator keys when merging -** multiple b-tree segments. Parameter iOut is the index of the aTree[] -** value to recalculate. -*/ -static int vdbeSorterDoCompare(const VdbeCursor *pCsr, int iOut){ - VdbeSorter *pSorter = pCsr->pSorter; - int i1; - int i2; - int iRes; - VdbeSorterIter *p1; - VdbeSorterIter *p2; - - assert( iOutnTree && iOut>0 ); - - if( iOut>=(pSorter->nTree/2) ){ - i1 = (iOut - pSorter->nTree/2) * 2; - i2 = i1 + 1; - }else{ - i1 = pSorter->aTree[iOut*2]; - i2 = pSorter->aTree[iOut*2+1]; - } - - p1 = &pSorter->aIter[i1]; - p2 = &pSorter->aIter[i2]; - - if( p1->pFile==0 ){ - iRes = i2; - }else if( p2->pFile==0 ){ - iRes = i1; - }else{ - int res; - assert( pCsr->pSorter->pUnpacked!=0 ); /* allocated in vdbeSorterMerge() */ - vdbeSorterCompare( - pCsr, 0, p1->aKey, p1->nKey, p2->aKey, p2->nKey, &res - ); - if( res<=0 ){ - iRes = i1; - }else{ - iRes = i2; - } - } - - pSorter->aTree[iOut] = iRes; - return SQLITE_OK; -} - -/* -** Initialize the temporary index cursor just opened as a sorter cursor. -*/ -SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *db, VdbeCursor *pCsr){ - int pgsz; /* Page size of main database */ - int mxCache; /* Cache size */ - VdbeSorter *pSorter; /* The new sorter */ - char *d; /* Dummy */ - - assert( pCsr->pKeyInfo && pCsr->pBt==0 ); - pCsr->pSorter = pSorter = sqlite3DbMallocZero(db, sizeof(VdbeSorter)); - if( pSorter==0 ){ - return SQLITE_NOMEM; - } - - pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pCsr->pKeyInfo, 0, 0, &d); - if( pSorter->pUnpacked==0 ) return SQLITE_NOMEM; - assert( pSorter->pUnpacked==(UnpackedRecord *)d ); - - if( !sqlite3TempInMemory(db) ){ - pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt); - pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz; - mxCache = db->aDb[0].pSchema->cache_size; - if( mxCachemxPmaSize = mxCache * pgsz; - } - - return SQLITE_OK; -} - -/* -** Free the list of sorted records starting at pRecord. -*/ -static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){ - SorterRecord *p; - SorterRecord *pNext; - for(p=pRecord; p; p=pNext){ - pNext = p->pNext; - sqlite3DbFree(db, p); - } -} - -/* -** Free any cursor components allocated by sqlite3VdbeSorterXXX routines. -*/ -SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){ - VdbeSorter *pSorter = pCsr->pSorter; - if( pSorter ){ - if( pSorter->aIter ){ - int i; - for(i=0; inTree; i++){ - vdbeSorterIterZero(db, &pSorter->aIter[i]); - } - sqlite3DbFree(db, pSorter->aIter); - } - if( pSorter->pTemp1 ){ - sqlite3OsCloseFree(pSorter->pTemp1); - } - vdbeSorterRecordFree(db, pSorter->pRecord); - sqlite3DbFree(db, pSorter->pUnpacked); - sqlite3DbFree(db, pSorter); - pCsr->pSorter = 0; - } -} - -/* -** Allocate space for a file-handle and open a temporary file. If successful, -** set *ppFile to point to the malloc'd file-handle and return SQLITE_OK. -** Otherwise, set *ppFile to 0 and return an SQLite error code. -*/ -static int vdbeSorterOpenTempFile(sqlite3 *db, sqlite3_file **ppFile){ - int dummy; - return sqlite3OsOpenMalloc(db->pVfs, 0, ppFile, - SQLITE_OPEN_TEMP_JOURNAL | - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE, &dummy - ); -} - -/* -** Merge the two sorted lists p1 and p2 into a single list. -** Set *ppOut to the head of the new list. -*/ -static void vdbeSorterMerge( - const VdbeCursor *pCsr, /* For pKeyInfo */ - SorterRecord *p1, /* First list to merge */ - SorterRecord *p2, /* Second list to merge */ - SorterRecord **ppOut /* OUT: Head of merged list */ -){ - SorterRecord *pFinal = 0; - SorterRecord **pp = &pFinal; - void *pVal2 = p2 ? p2->pVal : 0; - - while( p1 && p2 ){ - int res; - vdbeSorterCompare(pCsr, 0, p1->pVal, p1->nVal, pVal2, p2->nVal, &res); - if( res<=0 ){ - *pp = p1; - pp = &p1->pNext; - p1 = p1->pNext; - pVal2 = 0; - }else{ - *pp = p2; - pp = &p2->pNext; - p2 = p2->pNext; - if( p2==0 ) break; - pVal2 = p2->pVal; - } - } - *pp = p1 ? p1 : p2; - *ppOut = pFinal; -} - -/* -** Sort the linked list of records headed at pCsr->pRecord. Return SQLITE_OK -** if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if an error -** occurs. -*/ -static int vdbeSorterSort(const VdbeCursor *pCsr){ - int i; - SorterRecord **aSlot; - SorterRecord *p; - VdbeSorter *pSorter = pCsr->pSorter; - - aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *)); - if( !aSlot ){ - return SQLITE_NOMEM; - } - - p = pSorter->pRecord; - while( p ){ - SorterRecord *pNext = p->pNext; - p->pNext = 0; - for(i=0; aSlot[i]; i++){ - vdbeSorterMerge(pCsr, p, aSlot[i], &p); - aSlot[i] = 0; - } - aSlot[i] = p; - p = pNext; - } - - p = 0; - for(i=0; i<64; i++){ - vdbeSorterMerge(pCsr, p, aSlot[i], &p); - } - pSorter->pRecord = p; - - sqlite3_free(aSlot); - return SQLITE_OK; -} - -/* -** Initialize a file-writer object. -*/ -static void fileWriterInit( - sqlite3 *db, /* Database (for malloc) */ - sqlite3_file *pFile, /* File to write to */ - FileWriter *p, /* Object to populate */ - i64 iStart /* Offset of pFile to begin writing at */ -){ - int nBuf = sqlite3BtreeGetPageSize(db->aDb[0].pBt); - - memset(p, 0, sizeof(FileWriter)); - p->aBuffer = (u8 *)sqlite3DbMallocRaw(db, nBuf); - if( !p->aBuffer ){ - p->eFWErr = SQLITE_NOMEM; - }else{ - p->iBufEnd = p->iBufStart = (iStart % nBuf); - p->iWriteOff = iStart - p->iBufStart; - p->nBuffer = nBuf; - p->pFile = pFile; - } -} - -/* -** Write nData bytes of data to the file-write object. Return SQLITE_OK -** if successful, or an SQLite error code if an error occurs. -*/ -static void fileWriterWrite(FileWriter *p, u8 *pData, int nData){ - int nRem = nData; - while( nRem>0 && p->eFWErr==0 ){ - int nCopy = nRem; - if( nCopy>(p->nBuffer - p->iBufEnd) ){ - nCopy = p->nBuffer - p->iBufEnd; - } - - memcpy(&p->aBuffer[p->iBufEnd], &pData[nData-nRem], nCopy); - p->iBufEnd += nCopy; - if( p->iBufEnd==p->nBuffer ){ - p->eFWErr = sqlite3OsWrite(p->pFile, - &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart, - p->iWriteOff + p->iBufStart - ); - p->iBufStart = p->iBufEnd = 0; - p->iWriteOff += p->nBuffer; - } - assert( p->iBufEndnBuffer ); - - nRem -= nCopy; - } -} - -/* -** Flush any buffered data to disk and clean up the file-writer object. -** The results of using the file-writer after this call are undefined. -** Return SQLITE_OK if flushing the buffered data succeeds or is not -** required. Otherwise, return an SQLite error code. -** -** Before returning, set *piEof to the offset immediately following the -** last byte written to the file. -*/ -static int fileWriterFinish(sqlite3 *db, FileWriter *p, i64 *piEof){ - int rc; - if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){ - p->eFWErr = sqlite3OsWrite(p->pFile, - &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart, - p->iWriteOff + p->iBufStart - ); - } - *piEof = (p->iWriteOff + p->iBufEnd); - sqlite3DbFree(db, p->aBuffer); - rc = p->eFWErr; - memset(p, 0, sizeof(FileWriter)); - return rc; -} - -/* -** Write value iVal encoded as a varint to the file-write object. Return -** SQLITE_OK if successful, or an SQLite error code if an error occurs. -*/ -static void fileWriterWriteVarint(FileWriter *p, u64 iVal){ - int nByte; - u8 aByte[10]; - nByte = sqlite3PutVarint(aByte, iVal); - fileWriterWrite(p, aByte, nByte); -} - -/* -** Write the current contents of the in-memory linked-list to a PMA. Return -** SQLITE_OK if successful, or an SQLite error code otherwise. -** -** The format of a PMA is: -** -** * A varint. This varint contains the total number of bytes of content -** in the PMA (not including the varint itself). -** -** * One or more records packed end-to-end in order of ascending keys. -** Each record consists of a varint followed by a blob of data (the -** key). The varint is the number of bytes in the blob of data. -*/ -static int vdbeSorterListToPMA(sqlite3 *db, const VdbeCursor *pCsr){ - int rc = SQLITE_OK; /* Return code */ - VdbeSorter *pSorter = pCsr->pSorter; - FileWriter writer; - - memset(&writer, 0, sizeof(FileWriter)); - - if( pSorter->nInMemory==0 ){ - assert( pSorter->pRecord==0 ); - return rc; - } - - rc = vdbeSorterSort(pCsr); - - /* If the first temporary PMA file has not been opened, open it now. */ - if( rc==SQLITE_OK && pSorter->pTemp1==0 ){ - rc = vdbeSorterOpenTempFile(db, &pSorter->pTemp1); - assert( rc!=SQLITE_OK || pSorter->pTemp1 ); - assert( pSorter->iWriteOff==0 ); - assert( pSorter->nPMA==0 ); - } - - if( rc==SQLITE_OK ){ - SorterRecord *p; - SorterRecord *pNext = 0; - - fileWriterInit(db, pSorter->pTemp1, &writer, pSorter->iWriteOff); - pSorter->nPMA++; - fileWriterWriteVarint(&writer, pSorter->nInMemory); - for(p=pSorter->pRecord; p; p=pNext){ - pNext = p->pNext; - fileWriterWriteVarint(&writer, p->nVal); - fileWriterWrite(&writer, p->pVal, p->nVal); - sqlite3DbFree(db, p); - } - pSorter->pRecord = p; - rc = fileWriterFinish(db, &writer, &pSorter->iWriteOff); - } - - return rc; -} - -/* -** Add a record to the sorter. -*/ -SQLITE_PRIVATE int sqlite3VdbeSorterWrite( - sqlite3 *db, /* Database handle */ - const VdbeCursor *pCsr, /* Sorter cursor */ - Mem *pVal /* Memory cell containing record */ -){ - VdbeSorter *pSorter = pCsr->pSorter; - int rc = SQLITE_OK; /* Return Code */ - SorterRecord *pNew; /* New list element */ - - assert( pSorter ); - pSorter->nInMemory += sqlite3VarintLen(pVal->n) + pVal->n; - - pNew = (SorterRecord *)sqlite3DbMallocRaw(db, pVal->n + sizeof(SorterRecord)); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - pNew->pVal = (void *)&pNew[1]; - memcpy(pNew->pVal, pVal->z, pVal->n); - pNew->nVal = pVal->n; - pNew->pNext = pSorter->pRecord; - pSorter->pRecord = pNew; - } - - /* See if the contents of the sorter should now be written out. They - ** are written out when either of the following are true: - ** - ** * The total memory allocated for the in-memory list is greater - ** than (page-size * cache-size), or - ** - ** * The total memory allocated for the in-memory list is greater - ** than (page-size * 10) and sqlite3HeapNearlyFull() returns true. - */ - if( rc==SQLITE_OK && pSorter->mxPmaSize>0 && ( - (pSorter->nInMemory>pSorter->mxPmaSize) - || (pSorter->nInMemory>pSorter->mnPmaSize && sqlite3HeapNearlyFull()) - )){ -#ifdef SQLITE_DEBUG - i64 nExpect = pSorter->iWriteOff - + sqlite3VarintLen(pSorter->nInMemory) - + pSorter->nInMemory; -#endif - rc = vdbeSorterListToPMA(db, pCsr); - pSorter->nInMemory = 0; - assert( rc!=SQLITE_OK || (nExpect==pSorter->iWriteOff) ); - } - - return rc; -} - -/* -** Helper function for sqlite3VdbeSorterRewind(). -*/ -static int vdbeSorterInitMerge( - sqlite3 *db, /* Database handle */ - const VdbeCursor *pCsr, /* Cursor handle for this sorter */ - i64 *pnByte /* Sum of bytes in all opened PMAs */ -){ - VdbeSorter *pSorter = pCsr->pSorter; - int rc = SQLITE_OK; /* Return code */ - int i; /* Used to iterator through aIter[] */ - i64 nByte = 0; /* Total bytes in all opened PMAs */ - - /* Initialize the iterators. */ - for(i=0; iaIter[i]; - rc = vdbeSorterIterInit(db, pSorter, pSorter->iReadOff, pIter, &nByte); - pSorter->iReadOff = pIter->iEof; - assert( rc!=SQLITE_OK || pSorter->iReadOff<=pSorter->iWriteOff ); - if( rc!=SQLITE_OK || pSorter->iReadOff>=pSorter->iWriteOff ) break; - } - - /* Initialize the aTree[] array. */ - for(i=pSorter->nTree-1; rc==SQLITE_OK && i>0; i--){ - rc = vdbeSorterDoCompare(pCsr, i); - } - - *pnByte = nByte; - return rc; -} - -/* -** Once the sorter has been populated, this function is called to prepare -** for iterating through its contents in sorted order. -*/ -SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){ - VdbeSorter *pSorter = pCsr->pSorter; - int rc; /* Return code */ - sqlite3_file *pTemp2 = 0; /* Second temp file to use */ - i64 iWrite2 = 0; /* Write offset for pTemp2 */ - int nIter; /* Number of iterators used */ - int nByte; /* Bytes of space required for aIter/aTree */ - int N = 2; /* Power of 2 >= nIter */ - - assert( pSorter ); - - /* If no data has been written to disk, then do not do so now. Instead, - ** sort the VdbeSorter.pRecord list. The vdbe layer will read data directly - ** from the in-memory list. */ - if( pSorter->nPMA==0 ){ - *pbEof = !pSorter->pRecord; - assert( pSorter->aTree==0 ); - return vdbeSorterSort(pCsr); - } - - /* Write the current in-memory list to a PMA. */ - rc = vdbeSorterListToPMA(db, pCsr); - if( rc!=SQLITE_OK ) return rc; - - /* Allocate space for aIter[] and aTree[]. */ - nIter = pSorter->nPMA; - if( nIter>SORTER_MAX_MERGE_COUNT ) nIter = SORTER_MAX_MERGE_COUNT; - assert( nIter>0 ); - while( NaIter = (VdbeSorterIter *)sqlite3DbMallocZero(db, nByte); - if( !pSorter->aIter ) return SQLITE_NOMEM; - pSorter->aTree = (int *)&pSorter->aIter[N]; - pSorter->nTree = N; - - do { - int iNew; /* Index of new, merged, PMA */ - - for(iNew=0; - rc==SQLITE_OK && iNew*SORTER_MAX_MERGE_COUNTnPMA; - iNew++ - ){ - int rc2; /* Return code from fileWriterFinish() */ - FileWriter writer; /* Object used to write to disk */ - i64 nWrite; /* Number of bytes in new PMA */ - - memset(&writer, 0, sizeof(FileWriter)); - - /* If there are SORTER_MAX_MERGE_COUNT or less PMAs in file pTemp1, - ** initialize an iterator for each of them and break out of the loop. - ** These iterators will be incrementally merged as the VDBE layer calls - ** sqlite3VdbeSorterNext(). - ** - ** Otherwise, if pTemp1 contains more than SORTER_MAX_MERGE_COUNT PMAs, - ** initialize interators for SORTER_MAX_MERGE_COUNT of them. These PMAs - ** are merged into a single PMA that is written to file pTemp2. - */ - rc = vdbeSorterInitMerge(db, pCsr, &nWrite); - assert( rc!=SQLITE_OK || pSorter->aIter[ pSorter->aTree[1] ].pFile ); - if( rc!=SQLITE_OK || pSorter->nPMA<=SORTER_MAX_MERGE_COUNT ){ - break; - } - - /* Open the second temp file, if it is not already open. */ - if( pTemp2==0 ){ - assert( iWrite2==0 ); - rc = vdbeSorterOpenTempFile(db, &pTemp2); - } - - if( rc==SQLITE_OK ){ - int bEof = 0; - fileWriterInit(db, pTemp2, &writer, iWrite2); - fileWriterWriteVarint(&writer, nWrite); - while( rc==SQLITE_OK && bEof==0 ){ - VdbeSorterIter *pIter = &pSorter->aIter[ pSorter->aTree[1] ]; - assert( pIter->pFile ); - - fileWriterWriteVarint(&writer, pIter->nKey); - fileWriterWrite(&writer, pIter->aKey, pIter->nKey); - rc = sqlite3VdbeSorterNext(db, pCsr, &bEof); - } - rc2 = fileWriterFinish(db, &writer, &iWrite2); - if( rc==SQLITE_OK ) rc = rc2; - } - } - - if( pSorter->nPMA<=SORTER_MAX_MERGE_COUNT ){ - break; - }else{ - sqlite3_file *pTmp = pSorter->pTemp1; - pSorter->nPMA = iNew; - pSorter->pTemp1 = pTemp2; - pTemp2 = pTmp; - pSorter->iWriteOff = iWrite2; - pSorter->iReadOff = 0; - iWrite2 = 0; - } - }while( rc==SQLITE_OK ); - - if( pTemp2 ){ - sqlite3OsCloseFree(pTemp2); - } - *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0); - return rc; -} - -/* -** Advance to the next element in the sorter. -*/ -SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){ - VdbeSorter *pSorter = pCsr->pSorter; - int rc; /* Return code */ - - if( pSorter->aTree ){ - int iPrev = pSorter->aTree[1];/* Index of iterator to advance */ - int i; /* Index of aTree[] to recalculate */ - - rc = vdbeSorterIterNext(db, &pSorter->aIter[iPrev]); - for(i=(pSorter->nTree+iPrev)/2; rc==SQLITE_OK && i>0; i=i/2){ - rc = vdbeSorterDoCompare(pCsr, i); - } - - *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0); - }else{ - SorterRecord *pFree = pSorter->pRecord; - pSorter->pRecord = pFree->pNext; - pFree->pNext = 0; - vdbeSorterRecordFree(db, pFree); - *pbEof = !pSorter->pRecord; - rc = SQLITE_OK; - } - return rc; -} - -/* -** Return a pointer to a buffer owned by the sorter that contains the -** current key. -*/ -static void *vdbeSorterRowkey( - const VdbeSorter *pSorter, /* Sorter object */ - int *pnKey /* OUT: Size of current key in bytes */ -){ - void *pKey; - if( pSorter->aTree ){ - VdbeSorterIter *pIter; - pIter = &pSorter->aIter[ pSorter->aTree[1] ]; - *pnKey = pIter->nKey; - pKey = pIter->aKey; - }else{ - *pnKey = pSorter->pRecord->nVal; - pKey = pSorter->pRecord->pVal; - } - return pKey; -} - -/* -** Copy the current sorter key into the memory cell pOut. -*/ -SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){ - VdbeSorter *pSorter = pCsr->pSorter; - void *pKey; int nKey; /* Sorter key to copy into pOut */ - - pKey = vdbeSorterRowkey(pSorter, &nKey); - if( sqlite3VdbeMemGrow(pOut, nKey, 0) ){ - return SQLITE_NOMEM; - } - pOut->n = nKey; - MemSetTypeFlag(pOut, MEM_Blob); - memcpy(pOut->z, pKey, nKey); - - return SQLITE_OK; -} - -/* -** Compare the key in memory cell pVal with the key that the sorter cursor -** passed as the first argument currently points to. For the purposes of -** the comparison, ignore the rowid field at the end of each record. -** -** If an error occurs, return an SQLite error code (i.e. SQLITE_NOMEM). -** Otherwise, set *pRes to a negative, zero or positive value if the -** key in pVal is smaller than, equal to or larger than the current sorter -** key. -*/ -SQLITE_PRIVATE int sqlite3VdbeSorterCompare( - const VdbeCursor *pCsr, /* Sorter cursor */ - Mem *pVal, /* Value to compare to current sorter key */ - int *pRes /* OUT: Result of comparison */ -){ - VdbeSorter *pSorter = pCsr->pSorter; - void *pKey; int nKey; /* Sorter key to compare pVal with */ - - pKey = vdbeSorterRowkey(pSorter, &nKey); - vdbeSorterCompare(pCsr, 1, pVal->z, pVal->n, pKey, nKey, pRes); - return SQLITE_OK; -} - -/************** End of vdbesort.c ********************************************/ -/************** Begin file journal.c *****************************************/ -/* -** 2007 August 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file implements a special kind of sqlite3_file object used -** by SQLite to create journal files if the atomic-write optimization -** is enabled. -** -** The distinctive characteristic of this sqlite3_file is that the -** actual on disk file is created lazily. When the file is created, -** the caller specifies a buffer size for an in-memory buffer to -** be used to service read() and write() requests. The actual file -** on disk is not created or populated until either: -** -** 1) The in-memory representation grows too large for the allocated -** buffer, or -** 2) The sqlite3JournalCreate() function is called. -*/ -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - - -/* -** A JournalFile object is a subclass of sqlite3_file used by -** as an open file handle for journal files. -*/ -struct JournalFile { - sqlite3_io_methods *pMethod; /* I/O methods on journal files */ - int nBuf; /* Size of zBuf[] in bytes */ - char *zBuf; /* Space to buffer journal writes */ - int iSize; /* Amount of zBuf[] currently used */ - int flags; /* xOpen flags */ - sqlite3_vfs *pVfs; /* The "real" underlying VFS */ - sqlite3_file *pReal; /* The "real" underlying file descriptor */ - const char *zJournal; /* Name of the journal file */ -}; -typedef struct JournalFile JournalFile; - -/* -** If it does not already exists, create and populate the on-disk file -** for JournalFile p. -*/ -static int createFile(JournalFile *p){ - int rc = SQLITE_OK; - if( !p->pReal ){ - sqlite3_file *pReal = (sqlite3_file *)&p[1]; - rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0); - if( rc==SQLITE_OK ){ - p->pReal = pReal; - if( p->iSize>0 ){ - assert(p->iSize<=p->nBuf); - rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0); - } - if( rc!=SQLITE_OK ){ - /* If an error occurred while writing to the file, close it before - ** returning. This way, SQLite uses the in-memory journal data to - ** roll back changes made to the internal page-cache before this - ** function was called. */ - sqlite3OsClose(pReal); - p->pReal = 0; - } - } - } - return rc; -} - -/* -** Close the file. -*/ -static int jrnlClose(sqlite3_file *pJfd){ - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - sqlite3OsClose(p->pReal); - } - sqlite3_free(p->zBuf); - return SQLITE_OK; -} - -/* -** Read data from the file. -*/ -static int jrnlRead( - sqlite3_file *pJfd, /* The journal file from which to read */ - void *zBuf, /* Put the results here */ - int iAmt, /* Number of bytes to read */ - sqlite_int64 iOfst /* Begin reading at this offset */ -){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst); - }else if( (iAmt+iOfst)>p->iSize ){ - rc = SQLITE_IOERR_SHORT_READ; - }else{ - memcpy(zBuf, &p->zBuf[iOfst], iAmt); - } - return rc; -} - -/* -** Write data to the file. -*/ -static int jrnlWrite( - sqlite3_file *pJfd, /* The journal file into which to write */ - const void *zBuf, /* Take data to be written from here */ - int iAmt, /* Number of bytes to write */ - sqlite_int64 iOfst /* Begin writing at this offset into the file */ -){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( !p->pReal && (iOfst+iAmt)>p->nBuf ){ - rc = createFile(p); - } - if( rc==SQLITE_OK ){ - if( p->pReal ){ - rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); - }else{ - memcpy(&p->zBuf[iOfst], zBuf, iAmt); - if( p->iSize<(iOfst+iAmt) ){ - p->iSize = (iOfst+iAmt); - } - } - } - return rc; -} - -/* -** Truncate the file. -*/ -static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite3OsTruncate(p->pReal, size); - }else if( sizeiSize ){ - p->iSize = size; - } - return rc; -} - -/* -** Sync the file. -*/ -static int jrnlSync(sqlite3_file *pJfd, int flags){ - int rc; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite3OsSync(p->pReal, flags); - }else{ - rc = SQLITE_OK; - } - return rc; -} - -/* -** Query the size of the file in bytes. -*/ -static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite3OsFileSize(p->pReal, pSize); - }else{ - *pSize = (sqlite_int64) p->iSize; - } - return rc; -} - -/* -** Table of methods for JournalFile sqlite3_file object. -*/ -static struct sqlite3_io_methods JournalFileMethods = { - 1, /* iVersion */ - jrnlClose, /* xClose */ - jrnlRead, /* xRead */ - jrnlWrite, /* xWrite */ - jrnlTruncate, /* xTruncate */ - jrnlSync, /* xSync */ - jrnlFileSize, /* xFileSize */ - 0, /* xLock */ - 0, /* xUnlock */ - 0, /* xCheckReservedLock */ - 0, /* xFileControl */ - 0, /* xSectorSize */ - 0, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0 /* xShmUnmap */ -}; - -/* -** Open a journal file. -*/ -SQLITE_PRIVATE int sqlite3JournalOpen( - sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */ - const char *zName, /* Name of the journal file */ - sqlite3_file *pJfd, /* Preallocated, blank file handle */ - int flags, /* Opening flags */ - int nBuf /* Bytes buffered before opening the file */ -){ - JournalFile *p = (JournalFile *)pJfd; - memset(p, 0, sqlite3JournalSize(pVfs)); - if( nBuf>0 ){ - p->zBuf = sqlite3MallocZero(nBuf); - if( !p->zBuf ){ - return SQLITE_NOMEM; - } - }else{ - return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0); - } - p->pMethod = &JournalFileMethods; - p->nBuf = nBuf; - p->flags = flags; - p->zJournal = zName; - p->pVfs = pVfs; - return SQLITE_OK; -} - -/* -** If the argument p points to a JournalFile structure, and the underlying -** file has not yet been created, create it now. -*/ -SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *p){ - if( p->pMethods!=&JournalFileMethods ){ - return SQLITE_OK; - } - return createFile((JournalFile *)p); -} - -/* -** The file-handle passed as the only argument is guaranteed to be an open -** file. It may or may not be of class JournalFile. If the file is a -** JournalFile, and the underlying file on disk has not yet been opened, -** return 0. Otherwise, return 1. -*/ -SQLITE_PRIVATE int sqlite3JournalExists(sqlite3_file *p){ - return (p->pMethods!=&JournalFileMethods || ((JournalFile *)p)->pReal!=0); -} - -/* -** Return the number of bytes required to store a JournalFile that uses vfs -** pVfs to create the underlying on-disk files. -*/ -SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){ - return (pVfs->szOsFile+sizeof(JournalFile)); -} -#endif - -/************** End of journal.c *********************************************/ -/************** Begin file memjournal.c **************************************/ -/* -** 2008 October 7 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains code use to implement an in-memory rollback journal. -** The in-memory rollback journal is used to journal transactions for -** ":memory:" databases and when the journal_mode=MEMORY pragma is used. -*/ - -/* Forward references to internal structures */ -typedef struct MemJournal MemJournal; -typedef struct FilePoint FilePoint; -typedef struct FileChunk FileChunk; - -/* Space to hold the rollback journal is allocated in increments of -** this many bytes. -** -** The size chosen is a little less than a power of two. That way, -** the FileChunk object will have a size that almost exactly fills -** a power-of-two allocation. This mimimizes wasted space in power-of-two -** memory allocators. -*/ -#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*))) - -/* Macro to find the minimum of two numeric values. -*/ -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -/* -** The rollback journal is composed of a linked list of these structures. -*/ -struct FileChunk { - FileChunk *pNext; /* Next chunk in the journal */ - u8 zChunk[JOURNAL_CHUNKSIZE]; /* Content of this chunk */ -}; - -/* -** An instance of this object serves as a cursor into the rollback journal. -** The cursor can be either for reading or writing. -*/ -struct FilePoint { - sqlite3_int64 iOffset; /* Offset from the beginning of the file */ - FileChunk *pChunk; /* Specific chunk into which cursor points */ -}; - -/* -** This subclass is a subclass of sqlite3_file. Each open memory-journal -** is an instance of this class. -*/ -struct MemJournal { - sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */ - FileChunk *pFirst; /* Head of in-memory chunk-list */ - FilePoint endpoint; /* Pointer to the end of the file */ - FilePoint readpoint; /* Pointer to the end of the last xRead() */ -}; - -/* -** Read data from the in-memory journal file. This is the implementation -** of the sqlite3_vfs.xRead method. -*/ -static int memjrnlRead( - sqlite3_file *pJfd, /* The journal file from which to read */ - void *zBuf, /* Put the results here */ - int iAmt, /* Number of bytes to read */ - sqlite_int64 iOfst /* Begin reading at this offset */ -){ - MemJournal *p = (MemJournal *)pJfd; - u8 *zOut = zBuf; - int nRead = iAmt; - int iChunkOffset; - FileChunk *pChunk; - - /* SQLite never tries to read past the end of a rollback journal file */ - assert( iOfst+iAmt<=p->endpoint.iOffset ); - - if( p->readpoint.iOffset!=iOfst || iOfst==0 ){ - sqlite3_int64 iOff = 0; - for(pChunk=p->pFirst; - ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst; - pChunk=pChunk->pNext - ){ - iOff += JOURNAL_CHUNKSIZE; - } - }else{ - pChunk = p->readpoint.pChunk; - } - - iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE); - do { - int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset; - int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset)); - memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy); - zOut += nCopy; - nRead -= iSpace; - iChunkOffset = 0; - } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 ); - p->readpoint.iOffset = iOfst+iAmt; - p->readpoint.pChunk = pChunk; - - return SQLITE_OK; -} - -/* -** Write data to the file. -*/ -static int memjrnlWrite( - sqlite3_file *pJfd, /* The journal file into which to write */ - const void *zBuf, /* Take data to be written from here */ - int iAmt, /* Number of bytes to write */ - sqlite_int64 iOfst /* Begin writing at this offset into the file */ -){ - MemJournal *p = (MemJournal *)pJfd; - int nWrite = iAmt; - u8 *zWrite = (u8 *)zBuf; - - /* An in-memory journal file should only ever be appended to. Random - ** access writes are not required by sqlite. - */ - assert( iOfst==p->endpoint.iOffset ); - UNUSED_PARAMETER(iOfst); - - while( nWrite>0 ){ - FileChunk *pChunk = p->endpoint.pChunk; - int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE); - int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset); - - if( iChunkOffset==0 ){ - /* New chunk is required to extend the file. */ - FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk)); - if( !pNew ){ - return SQLITE_IOERR_NOMEM; - } - pNew->pNext = 0; - if( pChunk ){ - assert( p->pFirst ); - pChunk->pNext = pNew; - }else{ - assert( !p->pFirst ); - p->pFirst = pNew; - } - p->endpoint.pChunk = pNew; - } - - memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace); - zWrite += iSpace; - nWrite -= iSpace; - p->endpoint.iOffset += iSpace; - } - - return SQLITE_OK; -} - -/* -** Truncate the file. -*/ -static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ - MemJournal *p = (MemJournal *)pJfd; - FileChunk *pChunk; - assert(size==0); - UNUSED_PARAMETER(size); - pChunk = p->pFirst; - while( pChunk ){ - FileChunk *pTmp = pChunk; - pChunk = pChunk->pNext; - sqlite3_free(pTmp); - } - sqlite3MemJournalOpen(pJfd); - return SQLITE_OK; -} - -/* -** Close the file. -*/ -static int memjrnlClose(sqlite3_file *pJfd){ - memjrnlTruncate(pJfd, 0); - return SQLITE_OK; -} - - -/* -** Sync the file. -** -** Syncing an in-memory journal is a no-op. And, in fact, this routine -** is never called in a working implementation. This implementation -** exists purely as a contingency, in case some malfunction in some other -** part of SQLite causes Sync to be called by mistake. -*/ -static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return SQLITE_OK; -} - -/* -** Query the size of the file in bytes. -*/ -static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){ - MemJournal *p = (MemJournal *)pJfd; - *pSize = (sqlite_int64) p->endpoint.iOffset; - return SQLITE_OK; -} - -/* -** Table of methods for MemJournal sqlite3_file object. -*/ -static const struct sqlite3_io_methods MemJournalMethods = { - 1, /* iVersion */ - memjrnlClose, /* xClose */ - memjrnlRead, /* xRead */ - memjrnlWrite, /* xWrite */ - memjrnlTruncate, /* xTruncate */ - memjrnlSync, /* xSync */ - memjrnlFileSize, /* xFileSize */ - 0, /* xLock */ - 0, /* xUnlock */ - 0, /* xCheckReservedLock */ - 0, /* xFileControl */ - 0, /* xSectorSize */ - 0, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0 /* xShmUnlock */ -}; - -/* -** Open a journal file. -*/ -SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *pJfd){ - MemJournal *p = (MemJournal *)pJfd; - assert( EIGHT_BYTE_ALIGNMENT(p) ); - memset(p, 0, sqlite3MemJournalSize()); - p->pMethod = (sqlite3_io_methods*)&MemJournalMethods; -} - -/* -** Return true if the file-handle passed as an argument is -** an in-memory journal -*/ -SQLITE_PRIVATE int sqlite3IsMemJournal(sqlite3_file *pJfd){ - return pJfd->pMethods==&MemJournalMethods; -} - -/* -** Return the number of bytes required to store a MemJournal file descriptor. -*/ -SQLITE_PRIVATE int sqlite3MemJournalSize(void){ - return sizeof(MemJournal); -} - -/************** End of memjournal.c ******************************************/ -/************** Begin file walker.c ******************************************/ -/* -** 2008 August 16 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains routines used for walking the parser tree for -** an SQL statement. -*/ -/* #include */ -/* #include */ - - -/* -** Walk an expression tree. Invoke the callback once for each node -** of the expression, while decending. (In other words, the callback -** is invoked before visiting children.) -** -** The return value from the callback should be one of the WRC_* -** constants to specify how to proceed with the walk. -** -** WRC_Continue Continue descending down the tree. -** -** WRC_Prune Do not descend into child nodes. But allow -** the walk to continue with sibling nodes. -** -** WRC_Abort Do no more callbacks. Unwind the stack and -** return the top-level walk call. -** -** The return value from this routine is WRC_Abort to abandon the tree walk -** and WRC_Continue to continue. -*/ -SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ - int rc; - if( pExpr==0 ) return WRC_Continue; - testcase( ExprHasProperty(pExpr, EP_TokenOnly) ); - testcase( ExprHasProperty(pExpr, EP_Reduced) ); - rc = pWalker->xExprCallback(pWalker, pExpr); - if( rc==WRC_Continue - && !ExprHasAnyProperty(pExpr,EP_TokenOnly) ){ - if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort; - if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort; - }else{ - if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort; - } - } - return rc & WRC_Abort; -} - -/* -** Call sqlite3WalkExpr() for every expression in list p or until -** an abort request is seen. -*/ -SQLITE_PRIVATE int sqlite3WalkExprList(Walker *pWalker, ExprList *p){ - int i; - struct ExprList_item *pItem; - if( p ){ - for(i=p->nExpr, pItem=p->a; i>0; i--, pItem++){ - if( sqlite3WalkExpr(pWalker, pItem->pExpr) ) return WRC_Abort; - } - } - return WRC_Continue; -} - -/* -** Walk all expressions associated with SELECT statement p. Do -** not invoke the SELECT callback on p, but do (of course) invoke -** any expr callbacks and SELECT callbacks that come from subqueries. -** Return WRC_Abort or WRC_Continue. -*/ -SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ - if( sqlite3WalkExprList(pWalker, p->pEList) ) return WRC_Abort; - if( sqlite3WalkExpr(pWalker, p->pWhere) ) return WRC_Abort; - if( sqlite3WalkExprList(pWalker, p->pGroupBy) ) return WRC_Abort; - if( sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort; - if( sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort; - if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort; - if( sqlite3WalkExpr(pWalker, p->pOffset) ) return WRC_Abort; - return WRC_Continue; -} - -/* -** Walk the parse trees associated with all subqueries in the -** FROM clause of SELECT statement p. Do not invoke the select -** callback on p, but do invoke it on each FROM clause subquery -** and on any subqueries further down in the tree. Return -** WRC_Abort or WRC_Continue; -*/ -SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ - SrcList *pSrc; - int i; - struct SrcList_item *pItem; - - pSrc = p->pSrc; - if( ALWAYS(pSrc) ){ - for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ - if( sqlite3WalkSelect(pWalker, pItem->pSelect) ){ - return WRC_Abort; - } - } - } - return WRC_Continue; -} - -/* -** Call sqlite3WalkExpr() for every expression in Select statement p. -** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and -** on the compound select chain, p->pPrior. -** -** Return WRC_Continue under normal conditions. Return WRC_Abort if -** there is an abort request. -** -** If the Walker does not have an xSelectCallback() then this routine -** is a no-op returning WRC_Continue. -*/ -SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){ - int rc; - if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue; - rc = WRC_Continue; - pWalker->walkerDepth++; - while( p ){ - rc = pWalker->xSelectCallback(pWalker, p); - if( rc ) break; - if( sqlite3WalkSelectExpr(pWalker, p) - || sqlite3WalkSelectFrom(pWalker, p) - ){ - pWalker->walkerDepth--; - return WRC_Abort; - } - p = p->pPrior; - } - pWalker->walkerDepth--; - return rc & WRC_Abort; -} - -/************** End of walker.c **********************************************/ -/************** Begin file resolve.c *****************************************/ -/* -** 2008 August 18 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains routines used for walking the parser tree and -** resolve all identifiers by associating them with a particular -** table and column. -*/ -/* #include */ -/* #include */ - -/* -** Walk the expression tree pExpr and increase the aggregate function -** depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node. -** This needs to occur when copying a TK_AGG_FUNCTION node from an -** outer query into an inner subquery. -** -** incrAggFunctionDepth(pExpr,n) is the main routine. incrAggDepth(..) -** is a helper function - a callback for the tree walker. -*/ -static int incrAggDepth(Walker *pWalker, Expr *pExpr){ - if( pExpr->op==TK_AGG_FUNCTION ) pExpr->op2 += pWalker->u.i; - return WRC_Continue; -} -static void incrAggFunctionDepth(Expr *pExpr, int N){ - if( N>0 ){ - Walker w; - memset(&w, 0, sizeof(w)); - w.xExprCallback = incrAggDepth; - w.u.i = N; - sqlite3WalkExpr(&w, pExpr); - } -} - -/* -** Turn the pExpr expression into an alias for the iCol-th column of the -** result set in pEList. -** -** If the result set column is a simple column reference, then this routine -** makes an exact copy. But for any other kind of expression, this -** routine make a copy of the result set column as the argument to the -** TK_AS operator. The TK_AS operator causes the expression to be -** evaluated just once and then reused for each alias. -** -** The reason for suppressing the TK_AS term when the expression is a simple -** column reference is so that the column reference will be recognized as -** usable by indices within the WHERE clause processing logic. -** -** Hack: The TK_AS operator is inhibited if zType[0]=='G'. This means -** that in a GROUP BY clause, the expression is evaluated twice. Hence: -** -** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x -** -** Is equivalent to: -** -** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5 -** -** The result of random()%5 in the GROUP BY clause is probably different -** from the result in the result-set. We might fix this someday. Or -** then again, we might not... -** -** If the reference is followed by a COLLATE operator, then make sure -** the COLLATE operator is preserved. For example: -** -** SELECT a+b, c+d FROM t1 ORDER BY 1 COLLATE nocase; -** -** Should be transformed into: -** -** SELECT a+b, c+d FROM t1 ORDER BY (a+b) COLLATE nocase; -** -** The nSubquery parameter specifies how many levels of subquery the -** alias is removed from the original expression. The usually value is -** zero but it might be more if the alias is contained within a subquery -** of the original expression. The Expr.op2 field of TK_AGG_FUNCTION -** structures must be increased by the nSubquery amount. -*/ -static void resolveAlias( - Parse *pParse, /* Parsing context */ - ExprList *pEList, /* A result set */ - int iCol, /* A column in the result set. 0..pEList->nExpr-1 */ - Expr *pExpr, /* Transform this into an alias to the result set */ - const char *zType, /* "GROUP" or "ORDER" or "" */ - int nSubquery /* Number of subqueries that the label is moving */ -){ - Expr *pOrig; /* The iCol-th column of the result set */ - Expr *pDup; /* Copy of pOrig */ - sqlite3 *db; /* The database connection */ - - assert( iCol>=0 && iColnExpr ); - pOrig = pEList->a[iCol].pExpr; - assert( pOrig!=0 ); - assert( pOrig->flags & EP_Resolved ); - db = pParse->db; - pDup = sqlite3ExprDup(db, pOrig, 0); - if( pDup==0 ) return; - if( pOrig->op!=TK_COLUMN && zType[0]!='G' ){ - incrAggFunctionDepth(pDup, nSubquery); - pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0); - if( pDup==0 ) return; - if( pEList->a[iCol].iAlias==0 ){ - pEList->a[iCol].iAlias = (u16)(++pParse->nAlias); - } - pDup->iTable = pEList->a[iCol].iAlias; - } - if( pExpr->op==TK_COLLATE ){ - pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); - } - - /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This - ** prevents ExprDelete() from deleting the Expr structure itself, - ** allowing it to be repopulated by the memcpy() on the following line. - ** The pExpr->u.zToken might point into memory that will be freed by the - ** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to - ** make a copy of the token before doing the sqlite3DbFree(). - */ - ExprSetProperty(pExpr, EP_Static); - sqlite3ExprDelete(db, pExpr); - memcpy(pExpr, pDup, sizeof(*pExpr)); - if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){ - assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 ); - pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken); - pExpr->flags2 |= EP2_MallocedToken; - } - sqlite3DbFree(db, pDup); -} - - -/* -** Return TRUE if the name zCol occurs anywhere in the USING clause. -** -** Return FALSE if the USING clause is NULL or if it does not contain -** zCol. -*/ -static int nameInUsingClause(IdList *pUsing, const char *zCol){ - if( pUsing ){ - int k; - for(k=0; knId; k++){ - if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1; - } - } - return 0; -} - -/* -** Subqueries stores the original database, table and column names for their -** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". -** Check to see if the zSpan given to this routine matches the zDb, zTab, -** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will -** match anything. -*/ -SQLITE_PRIVATE int sqlite3MatchSpanName( - const char *zSpan, - const char *zCol, - const char *zTab, - const char *zDb -){ - int n; - for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} - if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ - return 0; - } - zSpan += n+1; - for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} - if( zTab && (sqlite3StrNICmp(zSpan, zTab, n)!=0 || zTab[n]!=0) ){ - return 0; - } - zSpan += n+1; - if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ - return 0; - } - return 1; -} - -/* -** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up -** that name in the set of source tables in pSrcList and make the pExpr -** expression node refer back to that source column. The following changes -** are made to pExpr: -** -** pExpr->iDb Set the index in db->aDb[] of the database X -** (even if X is implied). -** pExpr->iTable Set to the cursor number for the table obtained -** from pSrcList. -** pExpr->pTab Points to the Table structure of X.Y (even if -** X and/or Y are implied.) -** pExpr->iColumn Set to the column number within the table. -** pExpr->op Set to TK_COLUMN. -** pExpr->pLeft Any expression this points to is deleted -** pExpr->pRight Any expression this points to is deleted. -** -** The zDb variable is the name of the database (the "X"). This value may be -** NULL meaning that name is of the form Y.Z or Z. Any available database -** can be used. The zTable variable is the name of the table (the "Y"). This -** value can be NULL if zDb is also NULL. If zTable is NULL it -** means that the form of the name is Z and that columns from any table -** can be used. -** -** If the name cannot be resolved unambiguously, leave an error message -** in pParse and return WRC_Abort. Return WRC_Prune on success. -*/ -static int lookupName( - Parse *pParse, /* The parsing context */ - const char *zDb, /* Name of the database containing table, or NULL */ - const char *zTab, /* Name of table containing column, or NULL */ - const char *zCol, /* Name of the column. */ - NameContext *pNC, /* The name context used to resolve the name */ - Expr *pExpr /* Make this EXPR node point to the selected column */ -){ - int i, j; /* Loop counters */ - int cnt = 0; /* Number of matching column names */ - int cntTab = 0; /* Number of matching table names */ - int nSubquery = 0; /* How many levels of subquery */ - sqlite3 *db = pParse->db; /* The database connection */ - struct SrcList_item *pItem; /* Use for looping over pSrcList items */ - struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ - NameContext *pTopNC = pNC; /* First namecontext in the list */ - Schema *pSchema = 0; /* Schema of the expression */ - int isTrigger = 0; - - assert( pNC ); /* the name context cannot be NULL. */ - assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ - assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) ); - - /* Initialize the node to no-match */ - pExpr->iTable = -1; - pExpr->pTab = 0; - ExprSetIrreducible(pExpr); - - /* Translate the schema name in zDb into a pointer to the corresponding - ** schema. If not found, pSchema will remain NULL and nothing will match - ** resulting in an appropriate error message toward the end of this routine - */ - if( zDb ){ - for(i=0; inDb; i++){ - assert( db->aDb[i].zName ); - if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){ - pSchema = db->aDb[i].pSchema; - break; - } - } - } - - /* Start at the inner-most context and move outward until a match is found */ - while( pNC && cnt==0 ){ - ExprList *pEList; - SrcList *pSrcList = pNC->pSrcList; - - if( pSrcList ){ - for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ - Table *pTab; - Column *pCol; - - pTab = pItem->pTab; - assert( pTab!=0 && pTab->zName!=0 ); - assert( pTab->nCol>0 ); - if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ - int hit = 0; - pEList = pItem->pSelect->pEList; - for(j=0; jnExpr; j++){ - if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){ - cnt++; - cntTab = 2; - pMatch = pItem; - pExpr->iColumn = j; - hit = 1; - } - } - if( hit || zTab==0 ) continue; - } - if( zDb && pTab->pSchema!=pSchema ){ - continue; - } - if( zTab ){ - const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; - assert( zTabName!=0 ); - if( sqlite3StrICmp(zTabName, zTab)!=0 ){ - continue; - } - } - if( 0==(cntTab++) ){ - pMatch = pItem; - } - for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ - /* If there has been exactly one prior match and this match - ** is for the right-hand table of a NATURAL JOIN or is in a - ** USING clause, then skip this match. - */ - if( cnt==1 ){ - if( pItem->jointype & JT_NATURAL ) continue; - if( nameInUsingClause(pItem->pUsing, zCol) ) continue; - } - cnt++; - pMatch = pItem; - /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ - pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; - break; - } - } - } - if( pMatch ){ - pExpr->iTable = pMatch->iCursor; - pExpr->pTab = pMatch->pTab; - pSchema = pExpr->pTab->pSchema; - } - } /* if( pSrcList ) */ - -#ifndef SQLITE_OMIT_TRIGGER - /* If we have not already resolved the name, then maybe - ** it is a new.* or old.* trigger argument reference - */ - if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){ - int op = pParse->eTriggerOp; - Table *pTab = 0; - assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); - if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ - pExpr->iTable = 1; - pTab = pParse->pTriggerTab; - }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ - pExpr->iTable = 0; - pTab = pParse->pTriggerTab; - } - - if( pTab ){ - int iCol; - pSchema = pTab->pSchema; - cntTab++; - for(iCol=0; iColnCol; iCol++){ - Column *pCol = &pTab->aCol[iCol]; - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ - if( iCol==pTab->iPKey ){ - iCol = -1; - } - break; - } - } - if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) ){ - iCol = -1; /* IMP: R-44911-55124 */ - } - if( iColnCol ){ - cnt++; - if( iCol<0 ){ - pExpr->affinity = SQLITE_AFF_INTEGER; - }else if( pExpr->iTable==0 ){ - testcase( iCol==31 ); - testcase( iCol==32 ); - pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<iColumn = (i16)iCol; - pExpr->pTab = pTab; - isTrigger = 1; - } - } - } -#endif /* !defined(SQLITE_OMIT_TRIGGER) */ - - /* - ** Perhaps the name is a reference to the ROWID - */ - if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){ - cnt = 1; - pExpr->iColumn = -1; /* IMP: R-44911-55124 */ - pExpr->affinity = SQLITE_AFF_INTEGER; - } - - /* - ** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z - ** might refer to an result-set alias. This happens, for example, when - ** we are resolving names in the WHERE clause of the following command: - ** - ** SELECT a+b AS x FROM table WHERE x<10; - ** - ** In cases like this, replace pExpr with a copy of the expression that - ** forms the result set entry ("a+b" in the example) and return immediately. - ** Note that the expression in the result set should have already been - ** resolved by the time the WHERE clause is resolved. - */ - if( cnt==0 && (pEList = pNC->pEList)!=0 && zTab==0 ){ - for(j=0; jnExpr; j++){ - char *zAs = pEList->a[j].zName; - if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ - Expr *pOrig; - assert( pExpr->pLeft==0 && pExpr->pRight==0 ); - assert( pExpr->x.pList==0 ); - assert( pExpr->x.pSelect==0 ); - pOrig = pEList->a[j].pExpr; - if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){ - sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); - return WRC_Abort; - } - resolveAlias(pParse, pEList, j, pExpr, "", nSubquery); - cnt = 1; - pMatch = 0; - assert( zTab==0 && zDb==0 ); - goto lookupname_end; - } - } - } - - /* Advance to the next name context. The loop will exit when either - ** we have a match (cnt>0) or when we run out of name contexts. - */ - if( cnt==0 ){ - pNC = pNC->pNext; - nSubquery++; - } - } - - /* - ** If X and Y are NULL (in other words if only the column name Z is - ** supplied) and the value of Z is enclosed in double-quotes, then - ** Z is a string literal if it doesn't match any column names. In that - ** case, we need to return right away and not make any changes to - ** pExpr. - ** - ** Because no reference was made to outer contexts, the pNC->nRef - ** fields are not changed in any context. - */ - if( cnt==0 && zTab==0 && ExprHasProperty(pExpr,EP_DblQuoted) ){ - pExpr->op = TK_STRING; - pExpr->pTab = 0; - return WRC_Prune; - } - - /* - ** cnt==0 means there was not match. cnt>1 means there were two or - ** more matches. Either way, we have an error. - */ - if( cnt!=1 ){ - const char *zErr; - zErr = cnt==0 ? "no such column" : "ambiguous column name"; - if( zDb ){ - sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); - }else if( zTab ){ - sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); - }else{ - sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); - } - pParse->checkSchema = 1; - pTopNC->nErr++; - } - - /* If a column from a table in pSrcList is referenced, then record - ** this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes - ** bit 0 to be set. Column 1 sets bit 1. And so forth. If the - ** column number is greater than the number of bits in the bitmask - ** then set the high-order bit of the bitmask. - */ - if( pExpr->iColumn>=0 && pMatch!=0 ){ - int n = pExpr->iColumn; - testcase( n==BMS-1 ); - if( n>=BMS ){ - n = BMS-1; - } - assert( pMatch->iCursor==pExpr->iTable ); - pMatch->colUsed |= ((Bitmask)1)<pLeft); - pExpr->pLeft = 0; - sqlite3ExprDelete(db, pExpr->pRight); - pExpr->pRight = 0; - pExpr->op = (isTrigger ? TK_TRIGGER : TK_COLUMN); -lookupname_end: - if( cnt==1 ){ - assert( pNC!=0 ); - sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); - /* Increment the nRef value on all name contexts from TopNC up to - ** the point where the name matched. */ - for(;;){ - assert( pTopNC!=0 ); - pTopNC->nRef++; - if( pTopNC==pNC ) break; - pTopNC = pTopNC->pNext; - } - return WRC_Prune; - } else { - return WRC_Abort; - } -} - -/* -** Allocate and return a pointer to an expression to load the column iCol -** from datasource iSrc in SrcList pSrc. -*/ -SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){ - Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); - if( p ){ - struct SrcList_item *pItem = &pSrc->a[iSrc]; - p->pTab = pItem->pTab; - p->iTable = pItem->iCursor; - if( p->pTab->iPKey==iCol ){ - p->iColumn = -1; - }else{ - p->iColumn = (ynVar)iCol; - testcase( iCol==BMS ); - testcase( iCol==BMS-1 ); - pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); - } - ExprSetProperty(p, EP_Resolved); - } - return p; -} - -/* -** This routine is callback for sqlite3WalkExpr(). -** -** Resolve symbolic names into TK_COLUMN operators for the current -** node in the expression tree. Return 0 to continue the search down -** the tree or 2 to abort the tree walk. -** -** This routine also does error checking and name resolution for -** function names. The operator for aggregate functions is changed -** to TK_AGG_FUNCTION. -*/ -static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - NameContext *pNC; - Parse *pParse; - - pNC = pWalker->u.pNC; - assert( pNC!=0 ); - pParse = pNC->pParse; - assert( pParse==pWalker->pParse ); - - if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return WRC_Prune; - ExprSetProperty(pExpr, EP_Resolved); -#ifndef NDEBUG - if( pNC->pSrcList && pNC->pSrcList->nAlloc>0 ){ - SrcList *pSrcList = pNC->pSrcList; - int i; - for(i=0; ipSrcList->nSrc; i++){ - assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursornTab); - } - } -#endif - switch( pExpr->op ){ - -#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) - /* The special operator TK_ROW means use the rowid for the first - ** column in the FROM clause. This is used by the LIMIT and ORDER BY - ** clause processing on UPDATE and DELETE statements. - */ - case TK_ROW: { - SrcList *pSrcList = pNC->pSrcList; - struct SrcList_item *pItem; - assert( pSrcList && pSrcList->nSrc==1 ); - pItem = pSrcList->a; - pExpr->op = TK_COLUMN; - pExpr->pTab = pItem->pTab; - pExpr->iTable = pItem->iCursor; - pExpr->iColumn = -1; - pExpr->affinity = SQLITE_AFF_INTEGER; - break; - } -#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */ - - /* A lone identifier is the name of a column. - */ - case TK_ID: { - return lookupName(pParse, 0, 0, pExpr->u.zToken, pNC, pExpr); - } - - /* A table name and column name: ID.ID - ** Or a database, table and column: ID.ID.ID - */ - case TK_DOT: { - const char *zColumn; - const char *zTable; - const char *zDb; - Expr *pRight; - - /* if( pSrcList==0 ) break; */ - pRight = pExpr->pRight; - if( pRight->op==TK_ID ){ - zDb = 0; - zTable = pExpr->pLeft->u.zToken; - zColumn = pRight->u.zToken; - }else{ - assert( pRight->op==TK_DOT ); - zDb = pExpr->pLeft->u.zToken; - zTable = pRight->pLeft->u.zToken; - zColumn = pRight->pRight->u.zToken; - } - return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); - } - - /* Resolve function names - */ - case TK_CONST_FUNC: - case TK_FUNCTION: { - ExprList *pList = pExpr->x.pList; /* The argument list */ - int n = pList ? pList->nExpr : 0; /* Number of arguments */ - int no_such_func = 0; /* True if no such function exists */ - int wrong_num_args = 0; /* True if wrong number of arguments */ - int is_agg = 0; /* True if is an aggregate function */ - int auth; /* Authorization to use the function */ - int nId; /* Number of characters in function name */ - const char *zId; /* The function name. */ - FuncDef *pDef; /* Information about the function */ - u8 enc = ENC(pParse->db); /* The database encoding */ - - testcase( pExpr->op==TK_CONST_FUNC ); - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - zId = pExpr->u.zToken; - nId = sqlite3Strlen30(zId); - pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0); - if( pDef==0 ){ - pDef = sqlite3FindFunction(pParse->db, zId, nId, -2, enc, 0); - if( pDef==0 ){ - no_such_func = 1; - }else{ - wrong_num_args = 1; - } - }else{ - is_agg = pDef->xFunc==0; - } -#ifndef SQLITE_OMIT_AUTHORIZATION - if( pDef ){ - auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0, pDef->zName, 0); - if( auth!=SQLITE_OK ){ - if( auth==SQLITE_DENY ){ - sqlite3ErrorMsg(pParse, "not authorized to use function: %s", - pDef->zName); - pNC->nErr++; - } - pExpr->op = TK_NULL; - return WRC_Prune; - } - } -#endif - if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ - sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); - pNC->nErr++; - is_agg = 0; - }else if( no_such_func && pParse->db->init.busy==0 ){ - sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); - pNC->nErr++; - }else if( wrong_num_args ){ - sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", - nId, zId); - pNC->nErr++; - } - if( is_agg ) pNC->ncFlags &= ~NC_AllowAgg; - sqlite3WalkExprList(pWalker, pList); - if( is_agg ){ - NameContext *pNC2 = pNC; - pExpr->op = TK_AGG_FUNCTION; - pExpr->op2 = 0; - while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){ - pExpr->op2++; - pNC2 = pNC2->pNext; - } - if( pNC2 ) pNC2->ncFlags |= NC_HasAgg; - pNC->ncFlags |= NC_AllowAgg; - } - /* FIX ME: Compute pExpr->affinity based on the expected return - ** type of the function - */ - return WRC_Prune; - } -#ifndef SQLITE_OMIT_SUBQUERY - case TK_SELECT: - case TK_EXISTS: testcase( pExpr->op==TK_EXISTS ); -#endif - case TK_IN: { - testcase( pExpr->op==TK_IN ); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - int nRef = pNC->nRef; -#ifndef SQLITE_OMIT_CHECK - if( (pNC->ncFlags & NC_IsCheck)!=0 ){ - sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints"); - } -#endif - sqlite3WalkSelect(pWalker, pExpr->x.pSelect); - assert( pNC->nRef>=nRef ); - if( nRef!=pNC->nRef ){ - ExprSetProperty(pExpr, EP_VarSelect); - } - } - break; - } -#ifndef SQLITE_OMIT_CHECK - case TK_VARIABLE: { - if( (pNC->ncFlags & NC_IsCheck)!=0 ){ - sqlite3ErrorMsg(pParse,"parameters prohibited in CHECK constraints"); - } - break; - } -#endif - } - return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; -} - -/* -** pEList is a list of expressions which are really the result set of the -** a SELECT statement. pE is a term in an ORDER BY or GROUP BY clause. -** This routine checks to see if pE is a simple identifier which corresponds -** to the AS-name of one of the terms of the expression list. If it is, -** this routine return an integer between 1 and N where N is the number of -** elements in pEList, corresponding to the matching entry. If there is -** no match, or if pE is not a simple identifier, then this routine -** return 0. -** -** pEList has been resolved. pE has not. -*/ -static int resolveAsName( - Parse *pParse, /* Parsing context for error messages */ - ExprList *pEList, /* List of expressions to scan */ - Expr *pE /* Expression we are trying to match */ -){ - int i; /* Loop counter */ - - UNUSED_PARAMETER(pParse); - - if( pE->op==TK_ID ){ - char *zCol = pE->u.zToken; - for(i=0; inExpr; i++){ - char *zAs = pEList->a[i].zName; - if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ - return i+1; - } - } - } - return 0; -} - -/* -** pE is a pointer to an expression which is a single term in the -** ORDER BY of a compound SELECT. The expression has not been -** name resolved. -** -** At the point this routine is called, we already know that the -** ORDER BY term is not an integer index into the result set. That -** case is handled by the calling routine. -** -** Attempt to match pE against result set columns in the left-most -** SELECT statement. Return the index i of the matching column, -** as an indication to the caller that it should sort by the i-th column. -** The left-most column is 1. In other words, the value returned is the -** same integer value that would be used in the SQL statement to indicate -** the column. -** -** If there is no match, return 0. Return -1 if an error occurs. -*/ -static int resolveOrderByTermToExprList( - Parse *pParse, /* Parsing context for error messages */ - Select *pSelect, /* The SELECT statement with the ORDER BY clause */ - Expr *pE /* The specific ORDER BY term */ -){ - int i; /* Loop counter */ - ExprList *pEList; /* The columns of the result set */ - NameContext nc; /* Name context for resolving pE */ - sqlite3 *db; /* Database connection */ - int rc; /* Return code from subprocedures */ - u8 savedSuppErr; /* Saved value of db->suppressErr */ - - assert( sqlite3ExprIsInteger(pE, &i)==0 ); - pEList = pSelect->pEList; - - /* Resolve all names in the ORDER BY term expression - */ - memset(&nc, 0, sizeof(nc)); - nc.pParse = pParse; - nc.pSrcList = pSelect->pSrc; - nc.pEList = pEList; - nc.ncFlags = NC_AllowAgg; - nc.nErr = 0; - db = pParse->db; - savedSuppErr = db->suppressErr; - db->suppressErr = 1; - rc = sqlite3ResolveExprNames(&nc, pE); - db->suppressErr = savedSuppErr; - if( rc ) return 0; - - /* Try to match the ORDER BY expression against an expression - ** in the result set. Return an 1-based index of the matching - ** result-set entry. - */ - for(i=0; inExpr; i++){ - if( sqlite3ExprCompare(pEList->a[i].pExpr, pE)<2 ){ - return i+1; - } - } - - /* If no match, return 0. */ - return 0; -} - -/* -** Generate an ORDER BY or GROUP BY term out-of-range error. -*/ -static void resolveOutOfRangeError( - Parse *pParse, /* The error context into which to write the error */ - const char *zType, /* "ORDER" or "GROUP" */ - int i, /* The index (1-based) of the term out of range */ - int mx /* Largest permissible value of i */ -){ - sqlite3ErrorMsg(pParse, - "%r %s BY term out of range - should be " - "between 1 and %d", i, zType, mx); -} - -/* -** Analyze the ORDER BY clause in a compound SELECT statement. Modify -** each term of the ORDER BY clause is a constant integer between 1 -** and N where N is the number of columns in the compound SELECT. -** -** ORDER BY terms that are already an integer between 1 and N are -** unmodified. ORDER BY terms that are integers outside the range of -** 1 through N generate an error. ORDER BY terms that are expressions -** are matched against result set expressions of compound SELECT -** beginning with the left-most SELECT and working toward the right. -** At the first match, the ORDER BY expression is transformed into -** the integer column number. -** -** Return the number of errors seen. -*/ -static int resolveCompoundOrderBy( - Parse *pParse, /* Parsing context. Leave error messages here */ - Select *pSelect /* The SELECT statement containing the ORDER BY */ -){ - int i; - ExprList *pOrderBy; - ExprList *pEList; - sqlite3 *db; - int moreToDo = 1; - - pOrderBy = pSelect->pOrderBy; - if( pOrderBy==0 ) return 0; - db = pParse->db; -#if SQLITE_MAX_COLUMN - if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){ - sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause"); - return 1; - } -#endif - for(i=0; inExpr; i++){ - pOrderBy->a[i].done = 0; - } - pSelect->pNext = 0; - while( pSelect->pPrior ){ - pSelect->pPrior->pNext = pSelect; - pSelect = pSelect->pPrior; - } - while( pSelect && moreToDo ){ - struct ExprList_item *pItem; - moreToDo = 0; - pEList = pSelect->pEList; - assert( pEList!=0 ); - for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ - int iCol = -1; - Expr *pE, *pDup; - if( pItem->done ) continue; - pE = sqlite3ExprSkipCollate(pItem->pExpr); - if( sqlite3ExprIsInteger(pE, &iCol) ){ - if( iCol<=0 || iCol>pEList->nExpr ){ - resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); - return 1; - } - }else{ - iCol = resolveAsName(pParse, pEList, pE); - if( iCol==0 ){ - pDup = sqlite3ExprDup(db, pE, 0); - if( !db->mallocFailed ){ - assert(pDup); - iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup); - } - sqlite3ExprDelete(db, pDup); - } - } - if( iCol>0 ){ - /* Convert the ORDER BY term into an integer column number iCol, - ** taking care to preserve the COLLATE clause if it exists */ - Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); - if( pNew==0 ) return 1; - pNew->flags |= EP_IntValue; - pNew->u.iValue = iCol; - if( pItem->pExpr==pE ){ - pItem->pExpr = pNew; - }else{ - assert( pItem->pExpr->op==TK_COLLATE ); - assert( pItem->pExpr->pLeft==pE ); - pItem->pExpr->pLeft = pNew; - } - sqlite3ExprDelete(db, pE); - pItem->iOrderByCol = (u16)iCol; - pItem->done = 1; - }else{ - moreToDo = 1; - } - } - pSelect = pSelect->pNext; - } - for(i=0; inExpr; i++){ - if( pOrderBy->a[i].done==0 ){ - sqlite3ErrorMsg(pParse, "%r ORDER BY term does not match any " - "column in the result set", i+1); - return 1; - } - } - return 0; -} - -/* -** Check every term in the ORDER BY or GROUP BY clause pOrderBy of -** the SELECT statement pSelect. If any term is reference to a -** result set expression (as determined by the ExprList.a.iCol field) -** then convert that term into a copy of the corresponding result set -** column. -** -** If any errors are detected, add an error message to pParse and -** return non-zero. Return zero if no errors are seen. -*/ -SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy( - Parse *pParse, /* Parsing context. Leave error messages here */ - Select *pSelect, /* The SELECT statement containing the clause */ - ExprList *pOrderBy, /* The ORDER BY or GROUP BY clause to be processed */ - const char *zType /* "ORDER" or "GROUP" */ -){ - int i; - sqlite3 *db = pParse->db; - ExprList *pEList; - struct ExprList_item *pItem; - - if( pOrderBy==0 || pParse->db->mallocFailed ) return 0; -#if SQLITE_MAX_COLUMN - if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){ - sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType); - return 1; - } -#endif - pEList = pSelect->pEList; - assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */ - for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ - if( pItem->iOrderByCol ){ - if( pItem->iOrderByCol>pEList->nExpr ){ - resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); - return 1; - } - resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType,0); - } - } - return 0; -} - -/* -** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect. -** The Name context of the SELECT statement is pNC. zType is either -** "ORDER" or "GROUP" depending on which type of clause pOrderBy is. -** -** This routine resolves each term of the clause into an expression. -** If the order-by term is an integer I between 1 and N (where N is the -** number of columns in the result set of the SELECT) then the expression -** in the resolution is a copy of the I-th result-set expression. If -** the order-by term is an identify that corresponds to the AS-name of -** a result-set expression, then the term resolves to a copy of the -** result-set expression. Otherwise, the expression is resolved in -** the usual way - using sqlite3ResolveExprNames(). -** -** This routine returns the number of errors. If errors occur, then -** an appropriate error message might be left in pParse. (OOM errors -** excepted.) -*/ -static int resolveOrderGroupBy( - NameContext *pNC, /* The name context of the SELECT statement */ - Select *pSelect, /* The SELECT statement holding pOrderBy */ - ExprList *pOrderBy, /* An ORDER BY or GROUP BY clause to resolve */ - const char *zType /* Either "ORDER" or "GROUP", as appropriate */ -){ - int i, j; /* Loop counters */ - int iCol; /* Column number */ - struct ExprList_item *pItem; /* A term of the ORDER BY clause */ - Parse *pParse; /* Parsing context */ - int nResult; /* Number of terms in the result set */ - - if( pOrderBy==0 ) return 0; - nResult = pSelect->pEList->nExpr; - pParse = pNC->pParse; - for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ - Expr *pE = pItem->pExpr; - iCol = resolveAsName(pParse, pSelect->pEList, pE); - if( iCol>0 ){ - /* If an AS-name match is found, mark this ORDER BY column as being - ** a copy of the iCol-th result-set column. The subsequent call to - ** sqlite3ResolveOrderGroupBy() will convert the expression to a - ** copy of the iCol-th result-set expression. */ - pItem->iOrderByCol = (u16)iCol; - continue; - } - if( sqlite3ExprIsInteger(sqlite3ExprSkipCollate(pE), &iCol) ){ - /* The ORDER BY term is an integer constant. Again, set the column - ** number so that sqlite3ResolveOrderGroupBy() will convert the - ** order-by term to a copy of the result-set expression */ - if( iCol<1 || iCol>0xffff ){ - resolveOutOfRangeError(pParse, zType, i+1, nResult); - return 1; - } - pItem->iOrderByCol = (u16)iCol; - continue; - } - - /* Otherwise, treat the ORDER BY term as an ordinary expression */ - pItem->iOrderByCol = 0; - if( sqlite3ResolveExprNames(pNC, pE) ){ - return 1; - } - for(j=0; jpEList->nExpr; j++){ - if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr)==0 ){ - pItem->iOrderByCol = j+1; - } - } - } - return sqlite3ResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType); -} - -/* -** Resolve names in the SELECT statement p and all of its descendents. -*/ -static int resolveSelectStep(Walker *pWalker, Select *p){ - NameContext *pOuterNC; /* Context that contains this SELECT */ - NameContext sNC; /* Name context of this SELECT */ - int isCompound; /* True if p is a compound select */ - int nCompound; /* Number of compound terms processed so far */ - Parse *pParse; /* Parsing context */ - ExprList *pEList; /* Result set expression list */ - int i; /* Loop counter */ - ExprList *pGroupBy; /* The GROUP BY clause */ - Select *pLeftmost; /* Left-most of SELECT of a compound */ - sqlite3 *db; /* Database connection */ - - - assert( p!=0 ); - if( p->selFlags & SF_Resolved ){ - return WRC_Prune; - } - pOuterNC = pWalker->u.pNC; - pParse = pWalker->pParse; - db = pParse->db; - - /* Normally sqlite3SelectExpand() will be called first and will have - ** already expanded this SELECT. However, if this is a subquery within - ** an expression, sqlite3ResolveExprNames() will be called without a - ** prior call to sqlite3SelectExpand(). When that happens, let - ** sqlite3SelectPrep() do all of the processing for this SELECT. - ** sqlite3SelectPrep() will invoke both sqlite3SelectExpand() and - ** this routine in the correct order. - */ - if( (p->selFlags & SF_Expanded)==0 ){ - sqlite3SelectPrep(pParse, p, pOuterNC); - return (pParse->nErr || db->mallocFailed) ? WRC_Abort : WRC_Prune; - } - - isCompound = p->pPrior!=0; - nCompound = 0; - pLeftmost = p; - while( p ){ - assert( (p->selFlags & SF_Expanded)!=0 ); - assert( (p->selFlags & SF_Resolved)==0 ); - p->selFlags |= SF_Resolved; - - /* Resolve the expressions in the LIMIT and OFFSET clauses. These - ** are not allowed to refer to any names, so pass an empty NameContext. - */ - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - if( sqlite3ResolveExprNames(&sNC, p->pLimit) || - sqlite3ResolveExprNames(&sNC, p->pOffset) ){ - return WRC_Abort; - } - - /* Recursively resolve names in all subqueries - */ - for(i=0; ipSrc->nSrc; i++){ - struct SrcList_item *pItem = &p->pSrc->a[i]; - if( pItem->pSelect ){ - NameContext *pNC; /* Used to iterate name contexts */ - int nRef = 0; /* Refcount for pOuterNC and outer contexts */ - const char *zSavedContext = pParse->zAuthContext; - - /* Count the total number of references to pOuterNC and all of its - ** parent contexts. After resolving references to expressions in - ** pItem->pSelect, check if this value has changed. If so, then - ** SELECT statement pItem->pSelect must be correlated. Set the - ** pItem->isCorrelated flag if this is the case. */ - for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef; - - if( pItem->zName ) pParse->zAuthContext = pItem->zName; - sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); - pParse->zAuthContext = zSavedContext; - if( pParse->nErr || db->mallocFailed ) return WRC_Abort; - - for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef; - assert( pItem->isCorrelated==0 && nRef<=0 ); - pItem->isCorrelated = (nRef!=0); - } - } - - /* Set up the local name-context to pass to sqlite3ResolveExprNames() to - ** resolve the result-set expression list. - */ - sNC.ncFlags = NC_AllowAgg; - sNC.pSrcList = p->pSrc; - sNC.pNext = pOuterNC; - - /* Resolve names in the result set. */ - pEList = p->pEList; - assert( pEList!=0 ); - for(i=0; inExpr; i++){ - Expr *pX = pEList->a[i].pExpr; - if( sqlite3ResolveExprNames(&sNC, pX) ){ - return WRC_Abort; - } - } - - /* If there are no aggregate functions in the result-set, and no GROUP BY - ** expression, do not allow aggregates in any of the other expressions. - */ - assert( (p->selFlags & SF_Aggregate)==0 ); - pGroupBy = p->pGroupBy; - if( pGroupBy || (sNC.ncFlags & NC_HasAgg)!=0 ){ - p->selFlags |= SF_Aggregate; - }else{ - sNC.ncFlags &= ~NC_AllowAgg; - } - - /* If a HAVING clause is present, then there must be a GROUP BY clause. - */ - if( p->pHaving && !pGroupBy ){ - sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); - return WRC_Abort; - } - - /* Add the expression list to the name-context before parsing the - ** other expressions in the SELECT statement. This is so that - ** expressions in the WHERE clause (etc.) can refer to expressions by - ** aliases in the result set. - ** - ** Minor point: If this is the case, then the expression will be - ** re-evaluated for each reference to it. - */ - sNC.pEList = p->pEList; - if( sqlite3ResolveExprNames(&sNC, p->pWhere) || - sqlite3ResolveExprNames(&sNC, p->pHaving) - ){ - return WRC_Abort; - } - - /* The ORDER BY and GROUP BY clauses may not refer to terms in - ** outer queries - */ - sNC.pNext = 0; - sNC.ncFlags |= NC_AllowAgg; - - /* Process the ORDER BY clause for singleton SELECT statements. - ** The ORDER BY clause for compounds SELECT statements is handled - ** below, after all of the result-sets for all of the elements of - ** the compound have been resolved. - */ - if( !isCompound && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") ){ - return WRC_Abort; - } - if( db->mallocFailed ){ - return WRC_Abort; - } - - /* Resolve the GROUP BY clause. At the same time, make sure - ** the GROUP BY clause does not contain aggregate functions. - */ - if( pGroupBy ){ - struct ExprList_item *pItem; - - if( resolveOrderGroupBy(&sNC, p, pGroupBy, "GROUP") || db->mallocFailed ){ - return WRC_Abort; - } - for(i=0, pItem=pGroupBy->a; inExpr; i++, pItem++){ - if( ExprHasProperty(pItem->pExpr, EP_Agg) ){ - sqlite3ErrorMsg(pParse, "aggregate functions are not allowed in " - "the GROUP BY clause"); - return WRC_Abort; - } - } - } - - /* Advance to the next term of the compound - */ - p = p->pPrior; - nCompound++; - } - - /* Resolve the ORDER BY on a compound SELECT after all terms of - ** the compound have been resolved. - */ - if( isCompound && resolveCompoundOrderBy(pParse, pLeftmost) ){ - return WRC_Abort; - } - - return WRC_Prune; -} - -/* -** This routine walks an expression tree and resolves references to -** table columns and result-set columns. At the same time, do error -** checking on function usage and set a flag if any aggregate functions -** are seen. -** -** To resolve table columns references we look for nodes (or subtrees) of the -** form X.Y.Z or Y.Z or just Z where -** -** X: The name of a database. Ex: "main" or "temp" or -** the symbolic name assigned to an ATTACH-ed database. -** -** Y: The name of a table in a FROM clause. Or in a trigger -** one of the special names "old" or "new". -** -** Z: The name of a column in table Y. -** -** The node at the root of the subtree is modified as follows: -** -** Expr.op Changed to TK_COLUMN -** Expr.pTab Points to the Table object for X.Y -** Expr.iColumn The column index in X.Y. -1 for the rowid. -** Expr.iTable The VDBE cursor number for X.Y -** -** -** To resolve result-set references, look for expression nodes of the -** form Z (with no X and Y prefix) where the Z matches the right-hand -** size of an AS clause in the result-set of a SELECT. The Z expression -** is replaced by a copy of the left-hand side of the result-set expression. -** Table-name and function resolution occurs on the substituted expression -** tree. For example, in: -** -** SELECT a+b AS x, c+d AS y FROM t1 ORDER BY x; -** -** The "x" term of the order by is replaced by "a+b" to render: -** -** SELECT a+b AS x, c+d AS y FROM t1 ORDER BY a+b; -** -** Function calls are checked to make sure that the function is -** defined and that the correct number of arguments are specified. -** If the function is an aggregate function, then the NC_HasAgg flag is -** set and the opcode is changed from TK_FUNCTION to TK_AGG_FUNCTION. -** If an expression contains aggregate functions then the EP_Agg -** property on the expression is set. -** -** An error message is left in pParse if anything is amiss. The number -** if errors is returned. -*/ -SQLITE_PRIVATE int sqlite3ResolveExprNames( - NameContext *pNC, /* Namespace to resolve expressions in. */ - Expr *pExpr /* The expression to be analyzed. */ -){ - u8 savedHasAgg; - Walker w; - - if( pExpr==0 ) return 0; -#if SQLITE_MAX_EXPR_DEPTH>0 - { - Parse *pParse = pNC->pParse; - if( sqlite3ExprCheckHeight(pParse, pExpr->nHeight+pNC->pParse->nHeight) ){ - return 1; - } - pParse->nHeight += pExpr->nHeight; - } -#endif - savedHasAgg = pNC->ncFlags & NC_HasAgg; - pNC->ncFlags &= ~NC_HasAgg; - w.xExprCallback = resolveExprStep; - w.xSelectCallback = resolveSelectStep; - w.pParse = pNC->pParse; - w.u.pNC = pNC; - sqlite3WalkExpr(&w, pExpr); -#if SQLITE_MAX_EXPR_DEPTH>0 - pNC->pParse->nHeight -= pExpr->nHeight; -#endif - if( pNC->nErr>0 || w.pParse->nErr>0 ){ - ExprSetProperty(pExpr, EP_Error); - } - if( pNC->ncFlags & NC_HasAgg ){ - ExprSetProperty(pExpr, EP_Agg); - }else if( savedHasAgg ){ - pNC->ncFlags |= NC_HasAgg; - } - return ExprHasProperty(pExpr, EP_Error); -} - - -/* -** Resolve all names in all expressions of a SELECT and in all -** decendents of the SELECT, including compounds off of p->pPrior, -** subqueries in expressions, and subqueries used as FROM clause -** terms. -** -** See sqlite3ResolveExprNames() for a description of the kinds of -** transformations that occur. -** -** All SELECT statements should have been expanded using -** sqlite3SelectExpand() prior to invoking this routine. -*/ -SQLITE_PRIVATE void sqlite3ResolveSelectNames( - Parse *pParse, /* The parser context */ - Select *p, /* The SELECT statement being coded. */ - NameContext *pOuterNC /* Name context for parent SELECT statement */ -){ - Walker w; - - assert( p!=0 ); - w.xExprCallback = resolveExprStep; - w.xSelectCallback = resolveSelectStep; - w.pParse = pParse; - w.u.pNC = pOuterNC; - sqlite3WalkSelect(&w, p); -} - -/************** End of resolve.c *********************************************/ -/************** Begin file expr.c ********************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains routines used for analyzing expressions and -** for generating VDBE code that evaluates expressions in SQLite. -*/ - -/* -** Return the 'affinity' of the expression pExpr if any. -** -** If pExpr is a column, a reference to a column via an 'AS' alias, -** or a sub-select with a column as the return value, then the -** affinity of that column is returned. Otherwise, 0x00 is returned, -** indicating no affinity for the expression. -** -** i.e. the WHERE clause expresssions in the following statements all -** have an affinity: -** -** CREATE TABLE t1(a); -** SELECT * FROM t1 WHERE a; -** SELECT a AS b FROM t1 WHERE b; -** SELECT * FROM t1 WHERE (select a from t1); -*/ -SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){ - int op; - pExpr = sqlite3ExprSkipCollate(pExpr); - op = pExpr->op; - if( op==TK_SELECT ){ - assert( pExpr->flags&EP_xIsSelect ); - return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); - } -#ifndef SQLITE_OMIT_CAST - if( op==TK_CAST ){ - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - return sqlite3AffinityType(pExpr->u.zToken); - } -#endif - if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER) - && pExpr->pTab!=0 - ){ - /* op==TK_REGISTER && pExpr->pTab!=0 happens when pExpr was originally - ** a TK_COLUMN but was previously evaluated and cached in a register */ - int j = pExpr->iColumn; - if( j<0 ) return SQLITE_AFF_INTEGER; - assert( pExpr->pTab && jpTab->nCol ); - return pExpr->pTab->aCol[j].affinity; - } - return pExpr->affinity; -} - -/* -** Set the collating sequence for expression pExpr to be the collating -** sequence named by pToken. Return a pointer to a new Expr node that -** implements the COLLATE operator. -** -** If a memory allocation error occurs, that fact is recorded in pParse->db -** and the pExpr parameter is returned unchanged. -*/ -SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr *pExpr, Token *pCollName){ - if( pCollName->n>0 ){ - Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, 1); - if( pNew ){ - pNew->pLeft = pExpr; - pNew->flags |= EP_Collate; - pExpr = pNew; - } - } - return pExpr; -} -SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){ - Token s; - assert( zC!=0 ); - s.z = zC; - s.n = sqlite3Strlen30(s.z); - return sqlite3ExprAddCollateToken(pParse, pExpr, &s); -} - -/* -** Skip over any TK_COLLATE and/or TK_AS operators at the root of -** an expression. -*/ -SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){ - while( pExpr && (pExpr->op==TK_COLLATE || pExpr->op==TK_AS) ){ - pExpr = pExpr->pLeft; - } - return pExpr; -} - -/* -** Return the collation sequence for the expression pExpr. If -** there is no defined collating sequence, return NULL. -** -** The collating sequence might be determined by a COLLATE operator -** or by the presence of a column with a defined collating sequence. -** COLLATE operators take first precedence. Left operands take -** precedence over right operands. -*/ -SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ - sqlite3 *db = pParse->db; - CollSeq *pColl = 0; - Expr *p = pExpr; - while( p ){ - int op = p->op; - if( op==TK_CAST || op==TK_UPLUS ){ - p = p->pLeft; - continue; - } - assert( op!=TK_REGISTER || p->op2!=TK_COLLATE ); - if( op==TK_COLLATE ){ - if( db->init.busy ){ - /* Do not report errors when parsing while the schema */ - pColl = sqlite3FindCollSeq(db, ENC(db), p->u.zToken, 0); - }else{ - pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); - } - break; - } - if( p->pTab!=0 - && (op==TK_AGG_COLUMN || op==TK_COLUMN - || op==TK_REGISTER || op==TK_TRIGGER) - ){ - /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally - ** a TK_COLUMN but was previously evaluated and cached in a register */ - int j = p->iColumn; - if( j>=0 ){ - const char *zColl = p->pTab->aCol[j].zColl; - pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); - } - break; - } - if( p->flags & EP_Collate ){ - if( ALWAYS(p->pLeft) && (p->pLeft->flags & EP_Collate)!=0 ){ - p = p->pLeft; - }else{ - p = p->pRight; - } - }else{ - break; - } - } - if( sqlite3CheckCollSeq(pParse, pColl) ){ - pColl = 0; - } - return pColl; -} - -/* -** pExpr is an operand of a comparison operator. aff2 is the -** type affinity of the other operand. This routine returns the -** type affinity that should be used for the comparison operator. -*/ -SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){ - char aff1 = sqlite3ExprAffinity(pExpr); - if( aff1 && aff2 ){ - /* Both sides of the comparison are columns. If one has numeric - ** affinity, use that. Otherwise use no affinity. - */ - if( sqlite3IsNumericAffinity(aff1) || sqlite3IsNumericAffinity(aff2) ){ - return SQLITE_AFF_NUMERIC; - }else{ - return SQLITE_AFF_NONE; - } - }else if( !aff1 && !aff2 ){ - /* Neither side of the comparison is a column. Compare the - ** results directly. - */ - return SQLITE_AFF_NONE; - }else{ - /* One side is a column, the other is not. Use the columns affinity. */ - assert( aff1==0 || aff2==0 ); - return (aff1 + aff2); - } -} - -/* -** pExpr is a comparison operator. Return the type affinity that should -** be applied to both operands prior to doing the comparison. -*/ -static char comparisonAffinity(Expr *pExpr){ - char aff; - assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT || - pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE || - pExpr->op==TK_NE || pExpr->op==TK_IS || pExpr->op==TK_ISNOT ); - assert( pExpr->pLeft ); - aff = sqlite3ExprAffinity(pExpr->pLeft); - if( pExpr->pRight ){ - aff = sqlite3CompareAffinity(pExpr->pRight, aff); - }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff); - }else if( !aff ){ - aff = SQLITE_AFF_NONE; - } - return aff; -} - -/* -** pExpr is a comparison expression, eg. '=', '<', IN(...) etc. -** idx_affinity is the affinity of an indexed column. Return true -** if the index with affinity idx_affinity may be used to implement -** the comparison in pExpr. -*/ -SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){ - char aff = comparisonAffinity(pExpr); - switch( aff ){ - case SQLITE_AFF_NONE: - return 1; - case SQLITE_AFF_TEXT: - return idx_affinity==SQLITE_AFF_TEXT; - default: - return sqlite3IsNumericAffinity(idx_affinity); - } -} - -/* -** Return the P5 value that should be used for a binary comparison -** opcode (OP_Eq, OP_Ge etc.) used to compare pExpr1 and pExpr2. -*/ -static u8 binaryCompareP5(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){ - u8 aff = (char)sqlite3ExprAffinity(pExpr2); - aff = (u8)sqlite3CompareAffinity(pExpr1, aff) | (u8)jumpIfNull; - return aff; -} - -/* -** Return a pointer to the collation sequence that should be used by -** a binary comparison operator comparing pLeft and pRight. -** -** If the left hand expression has a collating sequence type, then it is -** used. Otherwise the collation sequence for the right hand expression -** is used, or the default (BINARY) if neither expression has a collating -** type. -** -** Argument pRight (but not pLeft) may be a null pointer. In this case, -** it is not considered. -*/ -SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq( - Parse *pParse, - Expr *pLeft, - Expr *pRight -){ - CollSeq *pColl; - assert( pLeft ); - if( pLeft->flags & EP_Collate ){ - pColl = sqlite3ExprCollSeq(pParse, pLeft); - }else if( pRight && (pRight->flags & EP_Collate)!=0 ){ - pColl = sqlite3ExprCollSeq(pParse, pRight); - }else{ - pColl = sqlite3ExprCollSeq(pParse, pLeft); - if( !pColl ){ - pColl = sqlite3ExprCollSeq(pParse, pRight); - } - } - return pColl; -} - -/* -** Generate code for a comparison operator. -*/ -static int codeCompare( - Parse *pParse, /* The parsing (and code generating) context */ - Expr *pLeft, /* The left operand */ - Expr *pRight, /* The right operand */ - int opcode, /* The comparison opcode */ - int in1, int in2, /* Register holding operands */ - int dest, /* Jump here if true. */ - int jumpIfNull /* If true, jump if either operand is NULL */ -){ - int p5; - int addr; - CollSeq *p4; - - p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight); - p5 = binaryCompareP5(pLeft, pRight, jumpIfNull); - addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1, - (void*)p4, P4_COLLSEQ); - sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5); - return addr; -} - -#if SQLITE_MAX_EXPR_DEPTH>0 -/* -** Check that argument nHeight is less than or equal to the maximum -** expression depth allowed. If it is not, leave an error message in -** pParse. -*/ -SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse *pParse, int nHeight){ - int rc = SQLITE_OK; - int mxHeight = pParse->db->aLimit[SQLITE_LIMIT_EXPR_DEPTH]; - if( nHeight>mxHeight ){ - sqlite3ErrorMsg(pParse, - "Expression tree is too large (maximum depth %d)", mxHeight - ); - rc = SQLITE_ERROR; - } - return rc; -} - -/* The following three functions, heightOfExpr(), heightOfExprList() -** and heightOfSelect(), are used to determine the maximum height -** of any expression tree referenced by the structure passed as the -** first argument. -** -** If this maximum height is greater than the current value pointed -** to by pnHeight, the second parameter, then set *pnHeight to that -** value. -*/ -static void heightOfExpr(Expr *p, int *pnHeight){ - if( p ){ - if( p->nHeight>*pnHeight ){ - *pnHeight = p->nHeight; - } - } -} -static void heightOfExprList(ExprList *p, int *pnHeight){ - if( p ){ - int i; - for(i=0; inExpr; i++){ - heightOfExpr(p->a[i].pExpr, pnHeight); - } - } -} -static void heightOfSelect(Select *p, int *pnHeight){ - if( p ){ - heightOfExpr(p->pWhere, pnHeight); - heightOfExpr(p->pHaving, pnHeight); - heightOfExpr(p->pLimit, pnHeight); - heightOfExpr(p->pOffset, pnHeight); - heightOfExprList(p->pEList, pnHeight); - heightOfExprList(p->pGroupBy, pnHeight); - heightOfExprList(p->pOrderBy, pnHeight); - heightOfSelect(p->pPrior, pnHeight); - } -} - -/* -** Set the Expr.nHeight variable in the structure passed as an -** argument. An expression with no children, Expr.pList or -** Expr.pSelect member has a height of 1. Any other expression -** has a height equal to the maximum height of any other -** referenced Expr plus one. -*/ -static void exprSetHeight(Expr *p){ - int nHeight = 0; - heightOfExpr(p->pLeft, &nHeight); - heightOfExpr(p->pRight, &nHeight); - if( ExprHasProperty(p, EP_xIsSelect) ){ - heightOfSelect(p->x.pSelect, &nHeight); - }else{ - heightOfExprList(p->x.pList, &nHeight); - } - p->nHeight = nHeight + 1; -} - -/* -** Set the Expr.nHeight variable using the exprSetHeight() function. If -** the height is greater than the maximum allowed expression depth, -** leave an error in pParse. -*/ -SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p){ - exprSetHeight(p); - sqlite3ExprCheckHeight(pParse, p->nHeight); -} - -/* -** Return the maximum height of any expression tree referenced -** by the select statement passed as an argument. -*/ -SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){ - int nHeight = 0; - heightOfSelect(p, &nHeight); - return nHeight; -} -#else - #define exprSetHeight(y) -#endif /* SQLITE_MAX_EXPR_DEPTH>0 */ - -/* -** This routine is the core allocator for Expr nodes. -** -** Construct a new expression node and return a pointer to it. Memory -** for this node and for the pToken argument is a single allocation -** obtained from sqlite3DbMalloc(). The calling function -** is responsible for making sure the node eventually gets freed. -** -** If dequote is true, then the token (if it exists) is dequoted. -** If dequote is false, no dequoting is performance. The deQuote -** parameter is ignored if pToken is NULL or if the token does not -** appear to be quoted. If the quotes were of the form "..." (double-quotes) -** then the EP_DblQuoted flag is set on the expression node. -** -** Special case: If op==TK_INTEGER and pToken points to a string that -** can be translated into a 32-bit integer, then the token is not -** stored in u.zToken. Instead, the integer values is written -** into u.iValue and the EP_IntValue flag is set. No extra storage -** is allocated to hold the integer text and the dequote flag is ignored. -*/ -SQLITE_PRIVATE Expr *sqlite3ExprAlloc( - sqlite3 *db, /* Handle for sqlite3DbMallocZero() (may be null) */ - int op, /* Expression opcode */ - const Token *pToken, /* Token argument. Might be NULL */ - int dequote /* True to dequote */ -){ - Expr *pNew; - int nExtra = 0; - int iValue = 0; - - if( pToken ){ - if( op!=TK_INTEGER || pToken->z==0 - || sqlite3GetInt32(pToken->z, &iValue)==0 ){ - nExtra = pToken->n+1; - assert( iValue>=0 ); - } - } - pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra); - if( pNew ){ - pNew->op = (u8)op; - pNew->iAgg = -1; - if( pToken ){ - if( nExtra==0 ){ - pNew->flags |= EP_IntValue; - pNew->u.iValue = iValue; - }else{ - int c; - pNew->u.zToken = (char*)&pNew[1]; - assert( pToken->z!=0 || pToken->n==0 ); - if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); - pNew->u.zToken[pToken->n] = 0; - if( dequote && nExtra>=3 - && ((c = pToken->z[0])=='\'' || c=='"' || c=='[' || c=='`') ){ - sqlite3Dequote(pNew->u.zToken); - if( c=='"' ) pNew->flags |= EP_DblQuoted; - } - } - } -#if SQLITE_MAX_EXPR_DEPTH>0 - pNew->nHeight = 1; -#endif - } - return pNew; -} - -/* -** Allocate a new expression node from a zero-terminated token that has -** already been dequoted. -*/ -SQLITE_PRIVATE Expr *sqlite3Expr( - sqlite3 *db, /* Handle for sqlite3DbMallocZero() (may be null) */ - int op, /* Expression opcode */ - const char *zToken /* Token argument. Might be NULL */ -){ - Token x; - x.z = zToken; - x.n = zToken ? sqlite3Strlen30(zToken) : 0; - return sqlite3ExprAlloc(db, op, &x, 0); -} - -/* -** Attach subtrees pLeft and pRight to the Expr node pRoot. -** -** If pRoot==NULL that means that a memory allocation error has occurred. -** In that case, delete the subtrees pLeft and pRight. -*/ -SQLITE_PRIVATE void sqlite3ExprAttachSubtrees( - sqlite3 *db, - Expr *pRoot, - Expr *pLeft, - Expr *pRight -){ - if( pRoot==0 ){ - assert( db->mallocFailed ); - sqlite3ExprDelete(db, pLeft); - sqlite3ExprDelete(db, pRight); - }else{ - if( pRight ){ - pRoot->pRight = pRight; - pRoot->flags |= EP_Collate & pRight->flags; - } - if( pLeft ){ - pRoot->pLeft = pLeft; - pRoot->flags |= EP_Collate & pLeft->flags; - } - exprSetHeight(pRoot); - } -} - -/* -** Allocate a Expr node which joins as many as two subtrees. -** -** One or both of the subtrees can be NULL. Return a pointer to the new -** Expr node. Or, if an OOM error occurs, set pParse->db->mallocFailed, -** free the subtrees and return NULL. -*/ -SQLITE_PRIVATE Expr *sqlite3PExpr( - Parse *pParse, /* Parsing context */ - int op, /* Expression opcode */ - Expr *pLeft, /* Left operand */ - Expr *pRight, /* Right operand */ - const Token *pToken /* Argument token */ -){ - Expr *p; - if( op==TK_AND && pLeft && pRight ){ - /* Take advantage of short-circuit false optimization for AND */ - p = sqlite3ExprAnd(pParse->db, pLeft, pRight); - }else{ - p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); - sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); - } - if( p ) { - sqlite3ExprCheckHeight(pParse, p->nHeight); - } - return p; -} - -/* -** Return 1 if an expression must be FALSE in all cases and 0 if the -** expression might be true. This is an optimization. If is OK to -** return 0 here even if the expression really is always false (a -** false negative). But it is a bug to return 1 if the expression -** might be true in some rare circumstances (a false positive.) -** -** Note that if the expression is part of conditional for a -** LEFT JOIN, then we cannot determine at compile-time whether or not -** is it true or false, so always return 0. -*/ -static int exprAlwaysFalse(Expr *p){ - int v = 0; - if( ExprHasProperty(p, EP_FromJoin) ) return 0; - if( !sqlite3ExprIsInteger(p, &v) ) return 0; - return v==0; -} - -/* -** Join two expressions using an AND operator. If either expression is -** NULL, then just return the other expression. -** -** If one side or the other of the AND is known to be false, then instead -** of returning an AND expression, just return a constant expression with -** a value of false. -*/ -SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3 *db, Expr *pLeft, Expr *pRight){ - if( pLeft==0 ){ - return pRight; - }else if( pRight==0 ){ - return pLeft; - }else if( exprAlwaysFalse(pLeft) || exprAlwaysFalse(pRight) ){ - sqlite3ExprDelete(db, pLeft); - sqlite3ExprDelete(db, pRight); - return sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0); - }else{ - Expr *pNew = sqlite3ExprAlloc(db, TK_AND, 0, 0); - sqlite3ExprAttachSubtrees(db, pNew, pLeft, pRight); - return pNew; - } -} - -/* -** Construct a new expression node for a function with multiple -** arguments. -*/ -SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *pToken){ - Expr *pNew; - sqlite3 *db = pParse->db; - assert( pToken ); - pNew = sqlite3ExprAlloc(db, TK_FUNCTION, pToken, 1); - if( pNew==0 ){ - sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */ - return 0; - } - pNew->x.pList = pList; - assert( !ExprHasProperty(pNew, EP_xIsSelect) ); - sqlite3ExprSetHeight(pParse, pNew); - return pNew; -} - -/* -** Assign a variable number to an expression that encodes a wildcard -** in the original SQL statement. -** -** Wildcards consisting of a single "?" are assigned the next sequential -** variable number. -** -** Wildcards of the form "?nnn" are assigned the number "nnn". We make -** sure "nnn" is not too be to avoid a denial of service attack when -** the SQL statement comes from an external source. -** -** Wildcards of the form ":aaa", "@aaa", or "$aaa" are assigned the same number -** as the previous instance of the same wildcard. Or if this is the first -** instance of the wildcard, the next sequenial variable number is -** assigned. -*/ -SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ - sqlite3 *db = pParse->db; - const char *z; - - if( pExpr==0 ) return; - assert( !ExprHasAnyProperty(pExpr, EP_IntValue|EP_Reduced|EP_TokenOnly) ); - z = pExpr->u.zToken; - assert( z!=0 ); - assert( z[0]!=0 ); - if( z[1]==0 ){ - /* Wildcard of the form "?". Assign the next variable number */ - assert( z[0]=='?' ); - pExpr->iColumn = (ynVar)(++pParse->nVar); - }else{ - ynVar x = 0; - u32 n = sqlite3Strlen30(z); - if( z[0]=='?' ){ - /* Wildcard of the form "?nnn". Convert "nnn" to an integer and - ** use it as the variable number */ - i64 i; - int bOk = 0==sqlite3Atoi64(&z[1], &i, n-1, SQLITE_UTF8); - pExpr->iColumn = x = (ynVar)i; - testcase( i==0 ); - testcase( i==1 ); - testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); - testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); - if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ - sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", - db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]); - x = 0; - } - if( i>pParse->nVar ){ - pParse->nVar = (int)i; - } - }else{ - /* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable - ** number as the prior appearance of the same name, or if the name - ** has never appeared before, reuse the same variable number - */ - ynVar i; - for(i=0; inzVar; i++){ - if( pParse->azVar[i] && strcmp(pParse->azVar[i],z)==0 ){ - pExpr->iColumn = x = (ynVar)i+1; - break; - } - } - if( x==0 ) x = pExpr->iColumn = (ynVar)(++pParse->nVar); - } - if( x>0 ){ - if( x>pParse->nzVar ){ - char **a; - a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0])); - if( a==0 ) return; /* Error reported through db->mallocFailed */ - pParse->azVar = a; - memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0])); - pParse->nzVar = x; - } - if( z[0]!='?' || pParse->azVar[x-1]==0 ){ - sqlite3DbFree(db, pParse->azVar[x-1]); - pParse->azVar[x-1] = sqlite3DbStrNDup(db, z, n); - } - } - } - if( !pParse->nErr && pParse->nVar>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ - sqlite3ErrorMsg(pParse, "too many SQL variables"); - } -} - -/* -** Recursively delete an expression tree. -*/ -SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){ - if( p==0 ) return; - /* Sanity check: Assert that the IntValue is non-negative if it exists */ - assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); - if( !ExprHasAnyProperty(p, EP_TokenOnly) ){ - sqlite3ExprDelete(db, p->pLeft); - sqlite3ExprDelete(db, p->pRight); - if( !ExprHasProperty(p, EP_Reduced) && (p->flags2 & EP2_MallocedToken)!=0 ){ - sqlite3DbFree(db, p->u.zToken); - } - if( ExprHasProperty(p, EP_xIsSelect) ){ - sqlite3SelectDelete(db, p->x.pSelect); - }else{ - sqlite3ExprListDelete(db, p->x.pList); - } - } - if( !ExprHasProperty(p, EP_Static) ){ - sqlite3DbFree(db, p); - } -} - -/* -** Return the number of bytes allocated for the expression structure -** passed as the first argument. This is always one of EXPR_FULLSIZE, -** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE. -*/ -static int exprStructSize(Expr *p){ - if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE; - if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE; - return EXPR_FULLSIZE; -} - -/* -** The dupedExpr*Size() routines each return the number of bytes required -** to store a copy of an expression or expression tree. They differ in -** how much of the tree is measured. -** -** dupedExprStructSize() Size of only the Expr structure -** dupedExprNodeSize() Size of Expr + space for token -** dupedExprSize() Expr + token + subtree components -** -*************************************************************************** -** -** The dupedExprStructSize() function returns two values OR-ed together: -** (1) the space required for a copy of the Expr structure only and -** (2) the EP_xxx flags that indicate what the structure size should be. -** The return values is always one of: -** -** EXPR_FULLSIZE -** EXPR_REDUCEDSIZE | EP_Reduced -** EXPR_TOKENONLYSIZE | EP_TokenOnly -** -** The size of the structure can be found by masking the return value -** of this routine with 0xfff. The flags can be found by masking the -** return value with EP_Reduced|EP_TokenOnly. -** -** Note that with flags==EXPRDUP_REDUCE, this routines works on full-size -** (unreduced) Expr objects as they or originally constructed by the parser. -** During expression analysis, extra information is computed and moved into -** later parts of teh Expr object and that extra information might get chopped -** off if the expression is reduced. Note also that it does not work to -** make a EXPRDUP_REDUCE copy of a reduced expression. It is only legal -** to reduce a pristine expression tree from the parser. The implementation -** of dupedExprStructSize() contain multiple assert() statements that attempt -** to enforce this constraint. -*/ -static int dupedExprStructSize(Expr *p, int flags){ - int nSize; - assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */ - if( 0==(flags&EXPRDUP_REDUCE) ){ - nSize = EXPR_FULLSIZE; - }else{ - assert( !ExprHasAnyProperty(p, EP_TokenOnly|EP_Reduced) ); - assert( !ExprHasProperty(p, EP_FromJoin) ); - assert( (p->flags2 & EP2_MallocedToken)==0 ); - assert( (p->flags2 & EP2_Irreducible)==0 ); - if( p->pLeft || p->pRight || p->x.pList ){ - nSize = EXPR_REDUCEDSIZE | EP_Reduced; - }else{ - nSize = EXPR_TOKENONLYSIZE | EP_TokenOnly; - } - } - return nSize; -} - -/* -** This function returns the space in bytes required to store the copy -** of the Expr structure and a copy of the Expr.u.zToken string (if that -** string is defined.) -*/ -static int dupedExprNodeSize(Expr *p, int flags){ - int nByte = dupedExprStructSize(p, flags) & 0xfff; - if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ - nByte += sqlite3Strlen30(p->u.zToken)+1; - } - return ROUND8(nByte); -} - -/* -** Return the number of bytes required to create a duplicate of the -** expression passed as the first argument. The second argument is a -** mask containing EXPRDUP_XXX flags. -** -** The value returned includes space to create a copy of the Expr struct -** itself and the buffer referred to by Expr.u.zToken, if any. -** -** If the EXPRDUP_REDUCE flag is set, then the return value includes -** space to duplicate all Expr nodes in the tree formed by Expr.pLeft -** and Expr.pRight variables (but not for any structures pointed to or -** descended from the Expr.x.pList or Expr.x.pSelect variables). -*/ -static int dupedExprSize(Expr *p, int flags){ - int nByte = 0; - if( p ){ - nByte = dupedExprNodeSize(p, flags); - if( flags&EXPRDUP_REDUCE ){ - nByte += dupedExprSize(p->pLeft, flags) + dupedExprSize(p->pRight, flags); - } - } - return nByte; -} - -/* -** This function is similar to sqlite3ExprDup(), except that if pzBuffer -** is not NULL then *pzBuffer is assumed to point to a buffer large enough -** to store the copy of expression p, the copies of p->u.zToken -** (if applicable), and the copies of the p->pLeft and p->pRight expressions, -** if any. Before returning, *pzBuffer is set to the first byte passed the -** portion of the buffer copied into by this function. -*/ -static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){ - Expr *pNew = 0; /* Value to return */ - if( p ){ - const int isReduced = (flags&EXPRDUP_REDUCE); - u8 *zAlloc; - u32 staticFlag = 0; - - assert( pzBuffer==0 || isReduced ); - - /* Figure out where to write the new Expr structure. */ - if( pzBuffer ){ - zAlloc = *pzBuffer; - staticFlag = EP_Static; - }else{ - zAlloc = sqlite3DbMallocRaw(db, dupedExprSize(p, flags)); - } - pNew = (Expr *)zAlloc; - - if( pNew ){ - /* Set nNewSize to the size allocated for the structure pointed to - ** by pNew. This is either EXPR_FULLSIZE, EXPR_REDUCEDSIZE or - ** EXPR_TOKENONLYSIZE. nToken is set to the number of bytes consumed - ** by the copy of the p->u.zToken string (if any). - */ - const unsigned nStructSize = dupedExprStructSize(p, flags); - const int nNewSize = nStructSize & 0xfff; - int nToken; - if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ - nToken = sqlite3Strlen30(p->u.zToken) + 1; - }else{ - nToken = 0; - } - if( isReduced ){ - assert( ExprHasProperty(p, EP_Reduced)==0 ); - memcpy(zAlloc, p, nNewSize); - }else{ - int nSize = exprStructSize(p); - memcpy(zAlloc, p, nSize); - memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize); - } - - /* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */ - pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static); - pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly); - pNew->flags |= staticFlag; - - /* Copy the p->u.zToken string, if any. */ - if( nToken ){ - char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize]; - memcpy(zToken, p->u.zToken, nToken); - } - - if( 0==((p->flags|pNew->flags) & EP_TokenOnly) ){ - /* Fill in the pNew->x.pSelect or pNew->x.pList member. */ - if( ExprHasProperty(p, EP_xIsSelect) ){ - pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, isReduced); - }else{ - pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, isReduced); - } - } - - /* Fill in pNew->pLeft and pNew->pRight. */ - if( ExprHasAnyProperty(pNew, EP_Reduced|EP_TokenOnly) ){ - zAlloc += dupedExprNodeSize(p, flags); - if( ExprHasProperty(pNew, EP_Reduced) ){ - pNew->pLeft = exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc); - pNew->pRight = exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc); - } - if( pzBuffer ){ - *pzBuffer = zAlloc; - } - }else{ - pNew->flags2 = 0; - if( !ExprHasAnyProperty(p, EP_TokenOnly) ){ - pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); - pNew->pRight = sqlite3ExprDup(db, p->pRight, 0); - } - } - - } - } - return pNew; -} - -/* -** The following group of routines make deep copies of expressions, -** expression lists, ID lists, and select statements. The copies can -** be deleted (by being passed to their respective ...Delete() routines) -** without effecting the originals. -** -** The expression list, ID, and source lists return by sqlite3ExprListDup(), -** sqlite3IdListDup(), and sqlite3SrcListDup() can not be further expanded -** by subsequent calls to sqlite*ListAppend() routines. -** -** Any tables that the SrcList might point to are not duplicated. -** -** The flags parameter contains a combination of the EXPRDUP_XXX flags. -** If the EXPRDUP_REDUCE flag is set, then the structure returned is a -** truncated version of the usual Expr structure that will be stored as -** part of the in-memory representation of the database schema. -*/ -SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){ - return exprDup(db, p, flags, 0); -} -SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ - ExprList *pNew; - struct ExprList_item *pItem, *pOldItem; - int i; - if( p==0 ) return 0; - pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) ); - if( pNew==0 ) return 0; - pNew->iECursor = 0; - pNew->nExpr = i = p->nExpr; - if( (flags & EXPRDUP_REDUCE)==0 ) for(i=1; inExpr; i+=i){} - pNew->a = pItem = sqlite3DbMallocRaw(db, i*sizeof(p->a[0]) ); - if( pItem==0 ){ - sqlite3DbFree(db, pNew); - return 0; - } - pOldItem = p->a; - for(i=0; inExpr; i++, pItem++, pOldItem++){ - Expr *pOldExpr = pOldItem->pExpr; - pItem->pExpr = sqlite3ExprDup(db, pOldExpr, flags); - pItem->zName = sqlite3DbStrDup(db, pOldItem->zName); - pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan); - pItem->sortOrder = pOldItem->sortOrder; - pItem->done = 0; - pItem->iOrderByCol = pOldItem->iOrderByCol; - pItem->iAlias = pOldItem->iAlias; - } - return pNew; -} - -/* -** If cursors, triggers, views and subqueries are all omitted from -** the build, then none of the following routines, except for -** sqlite3SelectDup(), can be called. sqlite3SelectDup() is sometimes -** called with a NULL argument. -*/ -#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ - || !defined(SQLITE_OMIT_SUBQUERY) -SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ - SrcList *pNew; - int i; - int nByte; - if( p==0 ) return 0; - nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); - pNew = sqlite3DbMallocRaw(db, nByte ); - if( pNew==0 ) return 0; - pNew->nSrc = pNew->nAlloc = p->nSrc; - for(i=0; inSrc; i++){ - struct SrcList_item *pNewItem = &pNew->a[i]; - struct SrcList_item *pOldItem = &p->a[i]; - Table *pTab; - pNewItem->pSchema = pOldItem->pSchema; - pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); - pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); - pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); - pNewItem->jointype = pOldItem->jointype; - pNewItem->iCursor = pOldItem->iCursor; - pNewItem->addrFillSub = pOldItem->addrFillSub; - pNewItem->regReturn = pOldItem->regReturn; - pNewItem->isCorrelated = pOldItem->isCorrelated; - pNewItem->viaCoroutine = pOldItem->viaCoroutine; - pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex); - pNewItem->notIndexed = pOldItem->notIndexed; - pNewItem->pIndex = pOldItem->pIndex; - pTab = pNewItem->pTab = pOldItem->pTab; - if( pTab ){ - pTab->nRef++; - } - pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags); - pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags); - pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing); - pNewItem->colUsed = pOldItem->colUsed; - } - return pNew; -} -SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){ - IdList *pNew; - int i; - if( p==0 ) return 0; - pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) ); - if( pNew==0 ) return 0; - pNew->nId = p->nId; - pNew->a = sqlite3DbMallocRaw(db, p->nId*sizeof(p->a[0]) ); - if( pNew->a==0 ){ - sqlite3DbFree(db, pNew); - return 0; - } - /* Note that because the size of the allocation for p->a[] is not - ** necessarily a power of two, sqlite3IdListAppend() may not be called - ** on the duplicate created by this function. */ - for(i=0; inId; i++){ - struct IdList_item *pNewItem = &pNew->a[i]; - struct IdList_item *pOldItem = &p->a[i]; - pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); - pNewItem->idx = pOldItem->idx; - } - return pNew; -} -SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ - Select *pNew, *pPrior; - if( p==0 ) return 0; - pNew = sqlite3DbMallocRaw(db, sizeof(*p) ); - if( pNew==0 ) return 0; - pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags); - pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags); - pNew->pWhere = sqlite3ExprDup(db, p->pWhere, flags); - pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy, flags); - pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags); - pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags); - pNew->op = p->op; - pNew->pPrior = pPrior = sqlite3SelectDup(db, p->pPrior, flags); - if( pPrior ) pPrior->pNext = pNew; - pNew->pNext = 0; - pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); - pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags); - pNew->iLimit = 0; - pNew->iOffset = 0; - pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; - pNew->pRightmost = 0; - pNew->addrOpenEphm[0] = -1; - pNew->addrOpenEphm[1] = -1; - pNew->addrOpenEphm[2] = -1; - return pNew; -} -#else -SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ - assert( p==0 ); - return 0; -} -#endif - - -/* -** Add a new element to the end of an expression list. If pList is -** initially NULL, then create a new expression list. -** -** If a memory allocation error occurs, the entire list is freed and -** NULL is returned. If non-NULL is returned, then it is guaranteed -** that the new entry was successfully appended. -*/ -SQLITE_PRIVATE ExprList *sqlite3ExprListAppend( - Parse *pParse, /* Parsing context */ - ExprList *pList, /* List to which to append. Might be NULL */ - Expr *pExpr /* Expression to be appended. Might be NULL */ -){ - sqlite3 *db = pParse->db; - if( pList==0 ){ - pList = sqlite3DbMallocZero(db, sizeof(ExprList) ); - if( pList==0 ){ - goto no_mem; - } - pList->a = sqlite3DbMallocRaw(db, sizeof(pList->a[0])); - if( pList->a==0 ) goto no_mem; - }else if( (pList->nExpr & (pList->nExpr-1))==0 ){ - struct ExprList_item *a; - assert( pList->nExpr>0 ); - a = sqlite3DbRealloc(db, pList->a, pList->nExpr*2*sizeof(pList->a[0])); - if( a==0 ){ - goto no_mem; - } - pList->a = a; - } - assert( pList->a!=0 ); - if( 1 ){ - struct ExprList_item *pItem = &pList->a[pList->nExpr++]; - memset(pItem, 0, sizeof(*pItem)); - pItem->pExpr = pExpr; - } - return pList; - -no_mem: - /* Avoid leaking memory if malloc has failed. */ - sqlite3ExprDelete(db, pExpr); - sqlite3ExprListDelete(db, pList); - return 0; -} - -/* -** Set the ExprList.a[].zName element of the most recently added item -** on the expression list. -** -** pList might be NULL following an OOM error. But pName should never be -** NULL. If a memory allocation fails, the pParse->db->mallocFailed flag -** is set. -*/ -SQLITE_PRIVATE void sqlite3ExprListSetName( - Parse *pParse, /* Parsing context */ - ExprList *pList, /* List to which to add the span. */ - Token *pName, /* Name to be added */ - int dequote /* True to cause the name to be dequoted */ -){ - assert( pList!=0 || pParse->db->mallocFailed!=0 ); - if( pList ){ - struct ExprList_item *pItem; - assert( pList->nExpr>0 ); - pItem = &pList->a[pList->nExpr-1]; - assert( pItem->zName==0 ); - pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n); - if( dequote && pItem->zName ) sqlite3Dequote(pItem->zName); - } -} - -/* -** Set the ExprList.a[].zSpan element of the most recently added item -** on the expression list. -** -** pList might be NULL following an OOM error. But pSpan should never be -** NULL. If a memory allocation fails, the pParse->db->mallocFailed flag -** is set. -*/ -SQLITE_PRIVATE void sqlite3ExprListSetSpan( - Parse *pParse, /* Parsing context */ - ExprList *pList, /* List to which to add the span. */ - ExprSpan *pSpan /* The span to be added */ -){ - sqlite3 *db = pParse->db; - assert( pList!=0 || db->mallocFailed!=0 ); - if( pList ){ - struct ExprList_item *pItem = &pList->a[pList->nExpr-1]; - assert( pList->nExpr>0 ); - assert( db->mallocFailed || pItem->pExpr==pSpan->pExpr ); - sqlite3DbFree(db, pItem->zSpan); - pItem->zSpan = sqlite3DbStrNDup(db, (char*)pSpan->zStart, - (int)(pSpan->zEnd - pSpan->zStart)); - } -} - -/* -** If the expression list pEList contains more than iLimit elements, -** leave an error message in pParse. -*/ -SQLITE_PRIVATE void sqlite3ExprListCheckLength( - Parse *pParse, - ExprList *pEList, - const char *zObject -){ - int mx = pParse->db->aLimit[SQLITE_LIMIT_COLUMN]; - testcase( pEList && pEList->nExpr==mx ); - testcase( pEList && pEList->nExpr==mx+1 ); - if( pEList && pEList->nExpr>mx ){ - sqlite3ErrorMsg(pParse, "too many columns in %s", zObject); - } -} - -/* -** Delete an entire expression list. -*/ -SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){ - int i; - struct ExprList_item *pItem; - if( pList==0 ) return; - assert( pList->a!=0 || pList->nExpr==0 ); - for(pItem=pList->a, i=0; inExpr; i++, pItem++){ - sqlite3ExprDelete(db, pItem->pExpr); - sqlite3DbFree(db, pItem->zName); - sqlite3DbFree(db, pItem->zSpan); - } - sqlite3DbFree(db, pList->a); - sqlite3DbFree(db, pList); -} - -/* -** These routines are Walker callbacks. Walker.u.pi is a pointer -** to an integer. These routines are checking an expression to see -** if it is a constant. Set *Walker.u.pi to 0 if the expression is -** not constant. -** -** These callback routines are used to implement the following: -** -** sqlite3ExprIsConstant() -** sqlite3ExprIsConstantNotJoin() -** sqlite3ExprIsConstantOrFunction() -** -*/ -static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ - - /* If pWalker->u.i is 3 then any term of the expression that comes from - ** the ON or USING clauses of a join disqualifies the expression - ** from being considered constant. */ - if( pWalker->u.i==3 && ExprHasAnyProperty(pExpr, EP_FromJoin) ){ - pWalker->u.i = 0; - return WRC_Abort; - } - - switch( pExpr->op ){ - /* Consider functions to be constant if all their arguments are constant - ** and pWalker->u.i==2 */ - case TK_FUNCTION: - if( pWalker->u.i==2 ) return 0; - /* Fall through */ - case TK_ID: - case TK_COLUMN: - case TK_AGG_FUNCTION: - case TK_AGG_COLUMN: - testcase( pExpr->op==TK_ID ); - testcase( pExpr->op==TK_COLUMN ); - testcase( pExpr->op==TK_AGG_FUNCTION ); - testcase( pExpr->op==TK_AGG_COLUMN ); - pWalker->u.i = 0; - return WRC_Abort; - default: - testcase( pExpr->op==TK_SELECT ); /* selectNodeIsConstant will disallow */ - testcase( pExpr->op==TK_EXISTS ); /* selectNodeIsConstant will disallow */ - return WRC_Continue; - } -} -static int selectNodeIsConstant(Walker *pWalker, Select *NotUsed){ - UNUSED_PARAMETER(NotUsed); - pWalker->u.i = 0; - return WRC_Abort; -} -static int exprIsConst(Expr *p, int initFlag){ - Walker w; - w.u.i = initFlag; - w.xExprCallback = exprNodeIsConstant; - w.xSelectCallback = selectNodeIsConstant; - sqlite3WalkExpr(&w, p); - return w.u.i; -} - -/* -** Walk an expression tree. Return 1 if the expression is constant -** and 0 if it involves variables or function calls. -** -** For the purposes of this function, a double-quoted string (ex: "abc") -** is considered a variable but a single-quoted string (ex: 'abc') is -** a constant. -*/ -SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){ - return exprIsConst(p, 1); -} - -/* -** Walk an expression tree. Return 1 if the expression is constant -** that does no originate from the ON or USING clauses of a join. -** Return 0 if it involves variables or function calls or terms from -** an ON or USING clause. -*/ -SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){ - return exprIsConst(p, 3); -} - -/* -** Walk an expression tree. Return 1 if the expression is constant -** or a function call with constant arguments. Return and 0 if there -** are any variables. -** -** For the purposes of this function, a double-quoted string (ex: "abc") -** is considered a variable but a single-quoted string (ex: 'abc') is -** a constant. -*/ -SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p){ - return exprIsConst(p, 2); -} - -/* -** If the expression p codes a constant integer that is small enough -** to fit in a 32-bit integer, return 1 and put the value of the integer -** in *pValue. If the expression is not an integer or if it is too big -** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. -*/ -SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ - int rc = 0; - - /* If an expression is an integer literal that fits in a signed 32-bit - ** integer, then the EP_IntValue flag will have already been set */ - assert( p->op!=TK_INTEGER || (p->flags & EP_IntValue)!=0 - || sqlite3GetInt32(p->u.zToken, &rc)==0 ); - - if( p->flags & EP_IntValue ){ - *pValue = p->u.iValue; - return 1; - } - switch( p->op ){ - case TK_UPLUS: { - rc = sqlite3ExprIsInteger(p->pLeft, pValue); - break; - } - case TK_UMINUS: { - int v; - if( sqlite3ExprIsInteger(p->pLeft, &v) ){ - *pValue = -v; - rc = 1; - } - break; - } - default: break; - } - return rc; -} - -/* -** Return FALSE if there is no chance that the expression can be NULL. -** -** If the expression might be NULL or if the expression is too complex -** to tell return TRUE. -** -** This routine is used as an optimization, to skip OP_IsNull opcodes -** when we know that a value cannot be NULL. Hence, a false positive -** (returning TRUE when in fact the expression can never be NULL) might -** be a small performance hit but is otherwise harmless. On the other -** hand, a false negative (returning FALSE when the result could be NULL) -** will likely result in an incorrect answer. So when in doubt, return -** TRUE. -*/ -SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ - u8 op; - while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; } - op = p->op; - if( op==TK_REGISTER ) op = p->op2; - switch( op ){ - case TK_INTEGER: - case TK_STRING: - case TK_FLOAT: - case TK_BLOB: - return 0; - default: - return 1; - } -} - -/* -** Generate an OP_IsNull instruction that tests register iReg and jumps -** to location iDest if the value in iReg is NULL. The value in iReg -** was computed by pExpr. If we can look at pExpr at compile-time and -** determine that it can never generate a NULL, then the OP_IsNull operation -** can be omitted. -*/ -SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump( - Vdbe *v, /* The VDBE under construction */ - const Expr *pExpr, /* Only generate OP_IsNull if this expr can be NULL */ - int iReg, /* Test the value in this register for NULL */ - int iDest /* Jump here if the value is null */ -){ - if( sqlite3ExprCanBeNull(pExpr) ){ - sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iDest); - } -} - -/* -** Return TRUE if the given expression is a constant which would be -** unchanged by OP_Affinity with the affinity given in the second -** argument. -** -** This routine is used to determine if the OP_Affinity operation -** can be omitted. When in doubt return FALSE. A false negative -** is harmless. A false positive, however, can result in the wrong -** answer. -*/ -SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr *p, char aff){ - u8 op; - if( aff==SQLITE_AFF_NONE ) return 1; - while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; } - op = p->op; - if( op==TK_REGISTER ) op = p->op2; - switch( op ){ - case TK_INTEGER: { - return aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC; - } - case TK_FLOAT: { - return aff==SQLITE_AFF_REAL || aff==SQLITE_AFF_NUMERIC; - } - case TK_STRING: { - return aff==SQLITE_AFF_TEXT; - } - case TK_BLOB: { - return 1; - } - case TK_COLUMN: { - assert( p->iTable>=0 ); /* p cannot be part of a CHECK constraint */ - return p->iColumn<0 - && (aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC); - } - default: { - return 0; - } - } -} - -/* -** Return TRUE if the given string is a row-id column name. -*/ -SQLITE_PRIVATE int sqlite3IsRowid(const char *z){ - if( sqlite3StrICmp(z, "_ROWID_")==0 ) return 1; - if( sqlite3StrICmp(z, "ROWID")==0 ) return 1; - if( sqlite3StrICmp(z, "OID")==0 ) return 1; - return 0; -} - -/* -** Return true if we are able to the IN operator optimization on a -** query of the form -** -** x IN (SELECT ...) -** -** Where the SELECT... clause is as specified by the parameter to this -** routine. -** -** The Select object passed in has already been preprocessed and no -** errors have been found. -*/ -#ifndef SQLITE_OMIT_SUBQUERY -static int isCandidateForInOpt(Select *p){ - SrcList *pSrc; - ExprList *pEList; - Table *pTab; - if( p==0 ) return 0; /* right-hand side of IN is SELECT */ - if( p->pPrior ) return 0; /* Not a compound SELECT */ - if( p->selFlags & (SF_Distinct|SF_Aggregate) ){ - testcase( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ); - testcase( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate ); - return 0; /* No DISTINCT keyword and no aggregate functions */ - } - assert( p->pGroupBy==0 ); /* Has no GROUP BY clause */ - if( p->pLimit ) return 0; /* Has no LIMIT clause */ - assert( p->pOffset==0 ); /* No LIMIT means no OFFSET */ - if( p->pWhere ) return 0; /* Has no WHERE clause */ - pSrc = p->pSrc; - assert( pSrc!=0 ); - if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */ - if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */ - pTab = pSrc->a[0].pTab; - if( NEVER(pTab==0) ) return 0; - assert( pTab->pSelect==0 ); /* FROM clause is not a view */ - if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ - pEList = p->pEList; - if( pEList->nExpr!=1 ) return 0; /* One column in the result set */ - if( pEList->a[0].pExpr->op!=TK_COLUMN ) return 0; /* Result is a column */ - return 1; -} -#endif /* SQLITE_OMIT_SUBQUERY */ - -/* -** Code an OP_Once instruction and allocate space for its flag. Return the -** address of the new instruction. -*/ -SQLITE_PRIVATE int sqlite3CodeOnce(Parse *pParse){ - Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ - return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++); -} - -/* -** This function is used by the implementation of the IN (...) operator. -** The pX parameter is the expression on the RHS of the IN operator, which -** might be either a list of expressions or a subquery. -** -** The job of this routine is to find or create a b-tree object that can -** be used either to test for membership in the RHS set or to iterate through -** all members of the RHS set, skipping duplicates. -** -** A cursor is opened on the b-tree object that the RHS of the IN operator -** and pX->iTable is set to the index of that cursor. -** -** The returned value of this function indicates the b-tree type, as follows: -** -** IN_INDEX_ROWID - The cursor was opened on a database table. -** IN_INDEX_INDEX_ASC - The cursor was opened on an ascending index. -** IN_INDEX_INDEX_DESC - The cursor was opened on a descending index. -** IN_INDEX_EPH - The cursor was opened on a specially created and -** populated epheremal table. -** -** An existing b-tree might be used if the RHS expression pX is a simple -** subquery such as: -** -** SELECT FROM -** -** If the RHS of the IN operator is a list or a more complex subquery, then -** an ephemeral table might need to be generated from the RHS and then -** pX->iTable made to point to the ephermeral table instead of an -** existing table. -** -** If the prNotFound parameter is 0, then the b-tree will be used to iterate -** through the set members, skipping any duplicates. In this case an -** epheremal table must be used unless the selected is guaranteed -** to be unique - either because it is an INTEGER PRIMARY KEY or it -** has a UNIQUE constraint or UNIQUE index. -** -** If the prNotFound parameter is not 0, then the b-tree will be used -** for fast set membership tests. In this case an epheremal table must -** be used unless is an INTEGER PRIMARY KEY or an index can -** be found with as its left-most column. -** -** When the b-tree is being used for membership tests, the calling function -** needs to know whether or not the structure contains an SQL NULL -** value in order to correctly evaluate expressions like "X IN (Y, Z)". -** If there is any chance that the (...) might contain a NULL value at -** runtime, then a register is allocated and the register number written -** to *prNotFound. If there is no chance that the (...) contains a -** NULL value, then *prNotFound is left unchanged. -** -** If a register is allocated and its location stored in *prNotFound, then -** its initial value is NULL. If the (...) does not remain constant -** for the duration of the query (i.e. the SELECT within the (...) -** is a correlated subquery) then the value of the allocated register is -** reset to NULL each time the subquery is rerun. This allows the -** caller to use vdbe code equivalent to the following: -** -** if( register==NULL ){ -** has_null = -** register = 1 -** } -** -** in order to avoid running the -** test more often than is necessary. -*/ -#ifndef SQLITE_OMIT_SUBQUERY -SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ - Select *p; /* SELECT to the right of IN operator */ - int eType = 0; /* Type of RHS table. IN_INDEX_* */ - int iTab = pParse->nTab++; /* Cursor of the RHS table */ - int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */ - Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ - - assert( pX->op==TK_IN ); - - /* Check to see if an existing table or index can be used to - ** satisfy the query. This is preferable to generating a new - ** ephemeral table. - */ - p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0); - if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){ - sqlite3 *db = pParse->db; /* Database connection */ - Table *pTab; /* Table
    . */ - Expr *pExpr; /* Expression */ - int iCol; /* Index of column */ - int iDb; /* Database idx for pTab */ - - assert( p ); /* Because of isCandidateForInOpt(p) */ - assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ - assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ - assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ - pTab = p->pSrc->a[0].pTab; - pExpr = p->pEList->a[0].pExpr; - iCol = pExpr->iColumn; - - /* Code an OP_VerifyCookie and OP_TableLock for
    . */ - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - sqlite3CodeVerifySchema(pParse, iDb); - sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - - /* This function is only called from two places. In both cases the vdbe - ** has already been allocated. So assume sqlite3GetVdbe() is always - ** successful here. - */ - assert(v); - if( iCol<0 ){ - int iAddr; - - iAddr = sqlite3CodeOnce(pParse); - - sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); - eType = IN_INDEX_ROWID; - - sqlite3VdbeJumpHere(v, iAddr); - }else{ - Index *pIdx; /* Iterator variable */ - - /* The collation sequence used by the comparison. If an index is to - ** be used in place of a temp-table, it must be ordered according - ** to this collation sequence. */ - CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pExpr); - - /* Check that the affinity that will be used to perform the - ** comparison is the same as the affinity of the column. If - ** it is not, it is not possible to use any index. - */ - int affinity_ok = sqlite3IndexAffinityOk(pX, pTab->aCol[iCol].affinity); - - for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){ - if( (pIdx->aiColumn[0]==iCol) - && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq - && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None)) - ){ - int iAddr; - char *pKey; - - pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx); - iAddr = sqlite3CodeOnce(pParse); - - sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, - pKey,P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pIdx->zName)); - assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); - eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; - - sqlite3VdbeJumpHere(v, iAddr); - if( prNotFound && !pTab->aCol[iCol].notNull ){ - *prNotFound = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound); - } - } - } - } - } - - if( eType==0 ){ - /* Could not found an existing table or index to use as the RHS b-tree. - ** We will have to generate an ephemeral table to do the job. - */ - double savedNQueryLoop = pParse->nQueryLoop; - int rMayHaveNull = 0; - eType = IN_INDEX_EPH; - if( prNotFound ){ - *prNotFound = rMayHaveNull = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound); - }else{ - testcase( pParse->nQueryLoop>(double)1 ); - pParse->nQueryLoop = (double)1; - if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){ - eType = IN_INDEX_ROWID; - } - } - sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID); - pParse->nQueryLoop = savedNQueryLoop; - }else{ - pX->iTable = iTab; - } - return eType; -} -#endif - -/* -** Generate code for scalar subqueries used as a subquery expression, EXISTS, -** or IN operators. Examples: -** -** (SELECT a FROM b) -- subquery -** EXISTS (SELECT a FROM b) -- EXISTS subquery -** x IN (4,5,11) -- IN operator with list on right-hand side -** x IN (SELECT a FROM b) -- IN operator with subquery on the right -** -** The pExpr parameter describes the expression that contains the IN -** operator or subquery. -** -** If parameter isRowid is non-zero, then expression pExpr is guaranteed -** to be of the form " IN (?, ?, ?)", where is a reference -** to some integer key column of a table B-Tree. In this case, use an -** intkey B-Tree to store the set of IN(...) values instead of the usual -** (slower) variable length keys B-Tree. -** -** If rMayHaveNull is non-zero, that means that the operation is an IN -** (not a SELECT or EXISTS) and that the RHS might contains NULLs. -** Furthermore, the IN is in a WHERE clause and that we really want -** to iterate over the RHS of the IN operator in order to quickly locate -** all corresponding LHS elements. All this routine does is initialize -** the register given by rMayHaveNull to NULL. Calling routines will take -** care of changing this register value to non-NULL if the RHS is NULL-free. -** -** If rMayHaveNull is zero, that means that the subquery is being used -** for membership testing only. There is no need to initialize any -** registers to indicate the presense or absence of NULLs on the RHS. -** -** For a SELECT or EXISTS operator, return the register that holds the -** result. For IN operators or if an error occurs, the return value is 0. -*/ -#ifndef SQLITE_OMIT_SUBQUERY -SQLITE_PRIVATE int sqlite3CodeSubselect( - Parse *pParse, /* Parsing context */ - Expr *pExpr, /* The IN, SELECT, or EXISTS operator */ - int rMayHaveNull, /* Register that records whether NULLs exist in RHS */ - int isRowid /* If true, LHS of IN operator is a rowid */ -){ - int testAddr = -1; /* One-time test address */ - int rReg = 0; /* Register storing resulting */ - Vdbe *v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return 0; - sqlite3ExprCachePush(pParse); - - /* This code must be run in its entirety every time it is encountered - ** if any of the following is true: - ** - ** * The right-hand side is a correlated subquery - ** * The right-hand side is an expression list containing variables - ** * We are inside a trigger - ** - ** If all of the above are false, then we can run this code just once - ** save the results, and reuse the same result on subsequent invocations. - */ - if( !ExprHasAnyProperty(pExpr, EP_VarSelect) ){ - testAddr = sqlite3CodeOnce(pParse); - } - -#ifndef SQLITE_OMIT_EXPLAIN - if( pParse->explain==2 ){ - char *zMsg = sqlite3MPrintf( - pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr>=0?"":"CORRELATED ", - pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId - ); - sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); - } -#endif - - switch( pExpr->op ){ - case TK_IN: { - char affinity; /* Affinity of the LHS of the IN */ - KeyInfo keyInfo; /* Keyinfo for the generated table */ - static u8 sortOrder = 0; /* Fake aSortOrder for keyInfo */ - int addr; /* Address of OP_OpenEphemeral instruction */ - Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */ - - if( rMayHaveNull ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull); - } - - affinity = sqlite3ExprAffinity(pLeft); - - /* Whether this is an 'x IN(SELECT...)' or an 'x IN()' - ** expression it is handled the same way. An ephemeral table is - ** filled with single-field index keys representing the results - ** from the SELECT or the . - ** - ** If the 'x' expression is a column value, or the SELECT... - ** statement returns a column value, then the affinity of that - ** column is used to build the index keys. If both 'x' and the - ** SELECT... statement are columns, then numeric affinity is used - ** if either column has NUMERIC or INTEGER affinity. If neither - ** 'x' nor the SELECT... statement are columns, then numeric affinity - ** is used. - */ - pExpr->iTable = pParse->nTab++; - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); - if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED); - memset(&keyInfo, 0, sizeof(keyInfo)); - keyInfo.nField = 1; - keyInfo.aSortOrder = &sortOrder; - - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - /* Case 1: expr IN (SELECT ...) - ** - ** Generate code to write the results of the select into the temporary - ** table allocated and opened above. - */ - SelectDest dest; - ExprList *pEList; - - assert( !isRowid ); - sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); - dest.affSdst = (u8)affinity; - assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); - pExpr->x.pSelect->iLimit = 0; - if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){ - return 0; - } - pEList = pExpr->x.pSelect->pEList; - if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){ - keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, - pEList->a[0].pExpr); - } - }else if( ALWAYS(pExpr->x.pList!=0) ){ - /* Case 2: expr IN (exprlist) - ** - ** For each expression, build an index key from the evaluation and - ** store it in the temporary table. If is a column, then use - ** that columns affinity when building index keys. If is not - ** a column, use numeric affinity. - */ - int i; - ExprList *pList = pExpr->x.pList; - struct ExprList_item *pItem; - int r1, r2, r3; - - if( !affinity ){ - affinity = SQLITE_AFF_NONE; - } - keyInfo.aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); - keyInfo.aSortOrder = &sortOrder; - - /* Loop through each expression in . */ - r1 = sqlite3GetTempReg(pParse); - r2 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Null, 0, r2); - for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){ - Expr *pE2 = pItem->pExpr; - int iValToIns; - - /* If the expression is not constant then we will need to - ** disable the test that was generated above that makes sure - ** this code only executes once. Because for a non-constant - ** expression we need to rerun this code each time. - */ - if( testAddr>=0 && !sqlite3ExprIsConstant(pE2) ){ - sqlite3VdbeChangeToNoop(v, testAddr); - testAddr = -1; - } - - /* Evaluate the expression and insert it into the temp table */ - if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){ - sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns); - }else{ - r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); - if( isRowid ){ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, - sqlite3VdbeCurrentAddr(v)+2); - sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); - }else{ - sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); - sqlite3ExprCacheAffinityChange(pParse, r3, 1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2); - } - } - } - sqlite3ReleaseTempReg(pParse, r1); - sqlite3ReleaseTempReg(pParse, r2); - } - if( !isRowid ){ - sqlite3VdbeChangeP4(v, addr, (void *)&keyInfo, P4_KEYINFO); - } - break; - } - - case TK_EXISTS: - case TK_SELECT: - default: { - /* If this has to be a scalar SELECT. Generate code to put the - ** value of this select in a memory cell and record the number - ** of the memory cell in iColumn. If this is an EXISTS, write - ** an integer 0 (not exists) or 1 (exists) into a memory cell - ** and record that memory cell in iColumn. - */ - Select *pSel; /* SELECT statement to encode */ - SelectDest dest; /* How to deal with SELECt result */ - - testcase( pExpr->op==TK_EXISTS ); - testcase( pExpr->op==TK_SELECT ); - assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); - - assert( ExprHasProperty(pExpr, EP_xIsSelect) ); - pSel = pExpr->x.pSelect; - sqlite3SelectDestInit(&dest, 0, ++pParse->nMem); - if( pExpr->op==TK_SELECT ){ - dest.eDest = SRT_Mem; - sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iSDParm); - VdbeComment((v, "Init subquery result")); - }else{ - dest.eDest = SRT_Exists; - sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm); - VdbeComment((v, "Init EXISTS result")); - } - sqlite3ExprDelete(pParse->db, pSel->pLimit); - pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, - &sqlite3IntTokens[1]); - pSel->iLimit = 0; - if( sqlite3Select(pParse, pSel, &dest) ){ - return 0; - } - rReg = dest.iSDParm; - ExprSetIrreducible(pExpr); - break; - } - } - - if( testAddr>=0 ){ - sqlite3VdbeJumpHere(v, testAddr); - } - sqlite3ExprCachePop(pParse, 1); - - return rReg; -} -#endif /* SQLITE_OMIT_SUBQUERY */ - -#ifndef SQLITE_OMIT_SUBQUERY -/* -** Generate code for an IN expression. -** -** x IN (SELECT ...) -** x IN (value, value, ...) -** -** The left-hand side (LHS) is a scalar expression. The right-hand side (RHS) -** is an array of zero or more values. The expression is true if the LHS is -** contained within the RHS. The value of the expression is unknown (NULL) -** if the LHS is NULL or if the LHS is not contained within the RHS and the -** RHS contains one or more NULL values. -** -** This routine generates code will jump to destIfFalse if the LHS is not -** contained within the RHS. If due to NULLs we cannot determine if the LHS -** is contained in the RHS then jump to destIfNull. If the LHS is contained -** within the RHS then fall through. -*/ -static void sqlite3ExprCodeIN( - Parse *pParse, /* Parsing and code generating context */ - Expr *pExpr, /* The IN expression */ - int destIfFalse, /* Jump here if LHS is not contained in the RHS */ - int destIfNull /* Jump here if the results are unknown due to NULLs */ -){ - int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */ - char affinity; /* Comparison affinity to use */ - int eType; /* Type of the RHS */ - int r1; /* Temporary use register */ - Vdbe *v; /* Statement under construction */ - - /* Compute the RHS. After this step, the table with cursor - ** pExpr->iTable will contains the values that make up the RHS. - */ - v = pParse->pVdbe; - assert( v!=0 ); /* OOM detected prior to this routine */ - VdbeNoopComment((v, "begin IN expr")); - eType = sqlite3FindInIndex(pParse, pExpr, &rRhsHasNull); - - /* Figure out the affinity to use to create a key from the results - ** of the expression. affinityStr stores a static string suitable for - ** P4 of OP_MakeRecord. - */ - affinity = comparisonAffinity(pExpr); - - /* Code the LHS, the from " IN (...)". - */ - sqlite3ExprCachePush(pParse); - r1 = sqlite3GetTempReg(pParse); - sqlite3ExprCode(pParse, pExpr->pLeft, r1); - - /* If the LHS is NULL, then the result is either false or NULL depending - ** on whether the RHS is empty or not, respectively. - */ - if( destIfNull==destIfFalse ){ - /* Shortcut for the common case where the false and NULL outcomes are - ** the same. */ - sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull); - }else{ - int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); - sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); - sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull); - sqlite3VdbeJumpHere(v, addr1); - } - - if( eType==IN_INDEX_ROWID ){ - /* In this case, the RHS is the ROWID of table b-tree - */ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); - sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1); - }else{ - /* In this case, the RHS is an index b-tree. - */ - sqlite3VdbeAddOp4(v, OP_Affinity, r1, 1, 0, &affinity, 1); - - /* If the set membership test fails, then the result of the - ** "x IN (...)" expression must be either 0 or NULL. If the set - ** contains no NULL values, then the result is 0. If the set - ** contains one or more NULL values, then the result of the - ** expression is also NULL. - */ - if( rRhsHasNull==0 || destIfFalse==destIfNull ){ - /* This branch runs if it is known at compile time that the RHS - ** cannot contain NULL values. This happens as the result - ** of a "NOT NULL" constraint in the database schema. - ** - ** Also run this branch if NULL is equivalent to FALSE - ** for this particular IN operator. - */ - sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1); - - }else{ - /* In this branch, the RHS of the IN might contain a NULL and - ** the presence of a NULL on the RHS makes a difference in the - ** outcome. - */ - int j1, j2, j3; - - /* First check to see if the LHS is contained in the RHS. If so, - ** then the presence of NULLs in the RHS does not matter, so jump - ** over all of the code that follows. - */ - j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1); - - /* Here we begin generating code that runs if the LHS is not - ** contained within the RHS. Generate additional code that - ** tests the RHS for NULLs. If the RHS contains a NULL then - ** jump to destIfNull. If there are no NULLs in the RHS then - ** jump to destIfFalse. - */ - j2 = sqlite3VdbeAddOp1(v, OP_NotNull, rRhsHasNull); - j3 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, rRhsHasNull, 1); - sqlite3VdbeAddOp2(v, OP_Integer, -1, rRhsHasNull); - sqlite3VdbeJumpHere(v, j3); - sqlite3VdbeAddOp2(v, OP_AddImm, rRhsHasNull, 1); - sqlite3VdbeJumpHere(v, j2); - - /* Jump to the appropriate target depending on whether or not - ** the RHS contains a NULL - */ - sqlite3VdbeAddOp2(v, OP_If, rRhsHasNull, destIfNull); - sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse); - - /* The OP_Found at the top of this branch jumps here when true, - ** causing the overall IN expression evaluation to fall through. - */ - sqlite3VdbeJumpHere(v, j1); - } - } - sqlite3ReleaseTempReg(pParse, r1); - sqlite3ExprCachePop(pParse, 1); - VdbeComment((v, "end IN expr")); -} -#endif /* SQLITE_OMIT_SUBQUERY */ - -/* -** Duplicate an 8-byte value -*/ -static char *dup8bytes(Vdbe *v, const char *in){ - char *out = sqlite3DbMallocRaw(sqlite3VdbeDb(v), 8); - if( out ){ - memcpy(out, in, 8); - } - return out; -} - -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** Generate an instruction that will put the floating point -** value described by z[0..n-1] into register iMem. -** -** The z[] string will probably not be zero-terminated. But the -** z[n] character is guaranteed to be something that does not look -** like the continuation of the number. -*/ -static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ - if( ALWAYS(z!=0) ){ - double value; - char *zV; - sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); - assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */ - if( negateFlag ) value = -value; - zV = dup8bytes(v, (char*)&value); - sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); - } -} -#endif - - -/* -** Generate an instruction that will put the integer describe by -** text z[0..n-1] into register iMem. -** -** Expr.u.zToken is always UTF8 and zero-terminated. -*/ -static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ - Vdbe *v = pParse->pVdbe; - if( pExpr->flags & EP_IntValue ){ - int i = pExpr->u.iValue; - assert( i>=0 ); - if( negFlag ) i = -i; - sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); - }else{ - int c; - i64 value; - const char *z = pExpr->u.zToken; - assert( z!=0 ); - c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); - if( c==0 || (c==2 && negFlag) ){ - char *zV; - if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; } - zV = dup8bytes(v, (char*)&value); - sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); - }else{ -#ifdef SQLITE_OMIT_FLOATING_POINT - sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); -#else - codeReal(v, z, negFlag, iMem); -#endif - } - } -} - -/* -** Clear a cache entry. -*/ -static void cacheEntryClear(Parse *pParse, struct yColCache *p){ - if( p->tempReg ){ - if( pParse->nTempRegaTempReg) ){ - pParse->aTempReg[pParse->nTempReg++] = p->iReg; - } - p->tempReg = 0; - } -} - - -/* -** Record in the column cache that a particular column from a -** particular table is stored in a particular register. -*/ -SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int iReg){ - int i; - int minLru; - int idxLru; - struct yColCache *p; - - assert( iReg>0 ); /* Register numbers are always positive */ - assert( iCol>=-1 && iCol<32768 ); /* Finite column numbers */ - - /* The SQLITE_ColumnCache flag disables the column cache. This is used - ** for testing only - to verify that SQLite always gets the same answer - ** with and without the column cache. - */ - if( OptimizationDisabled(pParse->db, SQLITE_ColumnCache) ) return; - - /* First replace any existing entry. - ** - ** Actually, the way the column cache is currently used, we are guaranteed - ** that the object will never already be in cache. Verify this guarantee. - */ -#ifndef NDEBUG - for(i=0, p=pParse->aColCache; iiReg==0 || p->iTable!=iTab || p->iColumn!=iCol ); - } -#endif - - /* Find an empty slot and replace it */ - for(i=0, p=pParse->aColCache; iiReg==0 ){ - p->iLevel = pParse->iCacheLevel; - p->iTable = iTab; - p->iColumn = iCol; - p->iReg = iReg; - p->tempReg = 0; - p->lru = pParse->iCacheCnt++; - return; - } - } - - /* Replace the last recently used */ - minLru = 0x7fffffff; - idxLru = -1; - for(i=0, p=pParse->aColCache; ilrulru; - } - } - if( ALWAYS(idxLru>=0) ){ - p = &pParse->aColCache[idxLru]; - p->iLevel = pParse->iCacheLevel; - p->iTable = iTab; - p->iColumn = iCol; - p->iReg = iReg; - p->tempReg = 0; - p->lru = pParse->iCacheCnt++; - return; - } -} - -/* -** Indicate that registers between iReg..iReg+nReg-1 are being overwritten. -** Purge the range of registers from the column cache. -*/ -SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse *pParse, int iReg, int nReg){ - int i; - int iLast = iReg + nReg - 1; - struct yColCache *p; - for(i=0, p=pParse->aColCache; iiReg; - if( r>=iReg && r<=iLast ){ - cacheEntryClear(pParse, p); - p->iReg = 0; - } - } -} - -/* -** Remember the current column cache context. Any new entries added -** added to the column cache after this call are removed when the -** corresponding pop occurs. -*/ -SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){ - pParse->iCacheLevel++; -} - -/* -** Remove from the column cache any entries that were added since the -** the previous N Push operations. In other words, restore the cache -** to the state it was in N Pushes ago. -*/ -SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse, int N){ - int i; - struct yColCache *p; - assert( N>0 ); - assert( pParse->iCacheLevel>=N ); - pParse->iCacheLevel -= N; - for(i=0, p=pParse->aColCache; iiReg && p->iLevel>pParse->iCacheLevel ){ - cacheEntryClear(pParse, p); - p->iReg = 0; - } - } -} - -/* -** When a cached column is reused, make sure that its register is -** no longer available as a temp register. ticket #3879: that same -** register might be in the cache in multiple places, so be sure to -** get them all. -*/ -static void sqlite3ExprCachePinRegister(Parse *pParse, int iReg){ - int i; - struct yColCache *p; - for(i=0, p=pParse->aColCache; iiReg==iReg ){ - p->tempReg = 0; - } - } -} - -/* -** Generate code to extract the value of the iCol-th column of a table. -*/ -SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable( - Vdbe *v, /* The VDBE under construction */ - Table *pTab, /* The table containing the value */ - int iTabCur, /* The cursor for this table */ - int iCol, /* Index of the column to extract */ - int regOut /* Extract the valud into this register */ -){ - if( iCol<0 || iCol==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); - }else{ - int op = IsVirtual(pTab) ? OP_VColumn : OP_Column; - sqlite3VdbeAddOp3(v, op, iTabCur, iCol, regOut); - } - if( iCol>=0 ){ - sqlite3ColumnDefault(v, pTab, iCol, regOut); - } -} - -/* -** Generate code that will extract the iColumn-th column from -** table pTab and store the column value in a register. An effort -** is made to store the column value in register iReg, but this is -** not guaranteed. The location of the column value is returned. -** -** There must be an open cursor to pTab in iTable when this routine -** is called. If iColumn<0 then code is generated that extracts the rowid. -*/ -SQLITE_PRIVATE int sqlite3ExprCodeGetColumn( - Parse *pParse, /* Parsing and code generating context */ - Table *pTab, /* Description of the table we are reading from */ - int iColumn, /* Index of the table column */ - int iTable, /* The cursor pointing to the table */ - int iReg, /* Store results here */ - u8 p5 /* P5 value for OP_Column */ -){ - Vdbe *v = pParse->pVdbe; - int i; - struct yColCache *p; - - for(i=0, p=pParse->aColCache; iiReg>0 && p->iTable==iTable && p->iColumn==iColumn ){ - p->lru = pParse->iCacheCnt++; - sqlite3ExprCachePinRegister(pParse, p->iReg); - return p->iReg; - } - } - assert( v!=0 ); - sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg); - if( p5 ){ - sqlite3VdbeChangeP5(v, p5); - }else{ - sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg); - } - return iReg; -} - -/* -** Clear all column cache entries. -*/ -SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){ - int i; - struct yColCache *p; - - for(i=0, p=pParse->aColCache; iiReg ){ - cacheEntryClear(pParse, p); - p->iReg = 0; - } - } -} - -/* -** Record the fact that an affinity change has occurred on iCount -** registers starting with iStart. -*/ -SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, int iCount){ - sqlite3ExprCacheRemove(pParse, iStart, iCount); -} - -/* -** Generate code to move content from registers iFrom...iFrom+nReg-1 -** over to iTo..iTo+nReg-1. Keep the column cache up-to-date. -*/ -SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){ - int i; - struct yColCache *p; - assert( iFrom>=iTo+nReg || iFrom+nReg<=iTo ); - sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg-1); - for(i=0, p=pParse->aColCache; iiReg; - if( x>=iFrom && xiReg += iTo-iFrom; - } - } -} - -#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) -/* -** Return true if any register in the range iFrom..iTo (inclusive) -** is used as part of the column cache. -** -** This routine is used within assert() and testcase() macros only -** and does not appear in a normal build. -*/ -static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){ - int i; - struct yColCache *p; - for(i=0, p=pParse->aColCache; iiReg; - if( r>=iFrom && r<=iTo ) return 1; /*NO_TEST*/ - } - return 0; -} -#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */ - -/* -** Generate code into the current Vdbe to evaluate the given -** expression. Attempt to store the results in register "target". -** Return the register where results are stored. -** -** With this routine, there is no guarantee that results will -** be stored in target. The result might be stored in some other -** register if it is convenient to do so. The calling function -** must check the return code and move the results to the desired -** register. -*/ -SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ - Vdbe *v = pParse->pVdbe; /* The VM under construction */ - int op; /* The opcode being coded */ - int inReg = target; /* Results stored in register inReg */ - int regFree1 = 0; /* If non-zero free this temporary register */ - int regFree2 = 0; /* If non-zero free this temporary register */ - int r1, r2, r3, r4; /* Various register numbers */ - sqlite3 *db = pParse->db; /* The database connection */ - - assert( target>0 && target<=pParse->nMem ); - if( v==0 ){ - assert( pParse->db->mallocFailed ); - return 0; - } - - if( pExpr==0 ){ - op = TK_NULL; - }else{ - op = pExpr->op; - } - switch( op ){ - case TK_AGG_COLUMN: { - AggInfo *pAggInfo = pExpr->pAggInfo; - struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg]; - if( !pAggInfo->directMode ){ - assert( pCol->iMem>0 ); - inReg = pCol->iMem; - break; - }else if( pAggInfo->useSortingIdx ){ - sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab, - pCol->iSorterColumn, target); - break; - } - /* Otherwise, fall thru into the TK_COLUMN case */ - } - case TK_COLUMN: { - if( pExpr->iTable<0 ){ - /* This only happens when coding check constraints */ - assert( pParse->ckBase>0 ); - inReg = pExpr->iColumn + pParse->ckBase; - }else{ - inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab, - pExpr->iColumn, pExpr->iTable, target, - pExpr->op2); - } - break; - } - case TK_INTEGER: { - codeInteger(pParse, pExpr, 0, target); - break; - } -#ifndef SQLITE_OMIT_FLOATING_POINT - case TK_FLOAT: { - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - codeReal(v, pExpr->u.zToken, 0, target); - break; - } -#endif - case TK_STRING: { - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3VdbeAddOp4(v, OP_String8, 0, target, 0, pExpr->u.zToken, 0); - break; - } - case TK_NULL: { - sqlite3VdbeAddOp2(v, OP_Null, 0, target); - break; - } -#ifndef SQLITE_OMIT_BLOB_LITERAL - case TK_BLOB: { - int n; - const char *z; - char *zBlob; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); - assert( pExpr->u.zToken[1]=='\'' ); - z = &pExpr->u.zToken[2]; - n = sqlite3Strlen30(z) - 1; - assert( z[n]=='\'' ); - zBlob = sqlite3HexToBlob(sqlite3VdbeDb(v), z, n); - sqlite3VdbeAddOp4(v, OP_Blob, n/2, target, 0, zBlob, P4_DYNAMIC); - break; - } -#endif - case TK_VARIABLE: { - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - assert( pExpr->u.zToken!=0 ); - assert( pExpr->u.zToken[0]!=0 ); - sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); - if( pExpr->u.zToken[1]!=0 ){ - assert( pExpr->u.zToken[0]=='?' - || strcmp(pExpr->u.zToken, pParse->azVar[pExpr->iColumn-1])==0 ); - sqlite3VdbeChangeP4(v, -1, pParse->azVar[pExpr->iColumn-1], P4_STATIC); - } - break; - } - case TK_REGISTER: { - inReg = pExpr->iTable; - break; - } - case TK_AS: { - inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); - break; - } -#ifndef SQLITE_OMIT_CAST - case TK_CAST: { - /* Expressions of the form: CAST(pLeft AS token) */ - int aff, to_op; - inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - aff = sqlite3AffinityType(pExpr->u.zToken); - to_op = aff - SQLITE_AFF_TEXT + OP_ToText; - assert( to_op==OP_ToText || aff!=SQLITE_AFF_TEXT ); - assert( to_op==OP_ToBlob || aff!=SQLITE_AFF_NONE ); - assert( to_op==OP_ToNumeric || aff!=SQLITE_AFF_NUMERIC ); - assert( to_op==OP_ToInt || aff!=SQLITE_AFF_INTEGER ); - assert( to_op==OP_ToReal || aff!=SQLITE_AFF_REAL ); - testcase( to_op==OP_ToText ); - testcase( to_op==OP_ToBlob ); - testcase( to_op==OP_ToNumeric ); - testcase( to_op==OP_ToInt ); - testcase( to_op==OP_ToReal ); - if( inReg!=target ){ - sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); - inReg = target; - } - sqlite3VdbeAddOp1(v, to_op, inReg); - testcase( usedAsColumnCache(pParse, inReg, inReg) ); - sqlite3ExprCacheAffinityChange(pParse, inReg, 1); - break; - } -#endif /* SQLITE_OMIT_CAST */ - case TK_LT: - case TK_LE: - case TK_GT: - case TK_GE: - case TK_NE: - case TK_EQ: { - assert( TK_LT==OP_Lt ); - assert( TK_LE==OP_Le ); - assert( TK_GT==OP_Gt ); - assert( TK_GE==OP_Ge ); - assert( TK_EQ==OP_Eq ); - assert( TK_NE==OP_Ne ); - testcase( op==TK_LT ); - testcase( op==TK_LE ); - testcase( op==TK_GT ); - testcase( op==TK_GE ); - testcase( op==TK_EQ ); - testcase( op==TK_NE ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, - r1, r2, inReg, SQLITE_STOREP2); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - break; - } - case TK_IS: - case TK_ISNOT: { - testcase( op==TK_IS ); - testcase( op==TK_ISNOT ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - op = (op==TK_IS) ? TK_EQ : TK_NE; - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, - r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - break; - } - case TK_AND: - case TK_OR: - case TK_PLUS: - case TK_STAR: - case TK_MINUS: - case TK_REM: - case TK_BITAND: - case TK_BITOR: - case TK_SLASH: - case TK_LSHIFT: - case TK_RSHIFT: - case TK_CONCAT: { - assert( TK_AND==OP_And ); - assert( TK_OR==OP_Or ); - assert( TK_PLUS==OP_Add ); - assert( TK_MINUS==OP_Subtract ); - assert( TK_REM==OP_Remainder ); - assert( TK_BITAND==OP_BitAnd ); - assert( TK_BITOR==OP_BitOr ); - assert( TK_SLASH==OP_Divide ); - assert( TK_LSHIFT==OP_ShiftLeft ); - assert( TK_RSHIFT==OP_ShiftRight ); - assert( TK_CONCAT==OP_Concat ); - testcase( op==TK_AND ); - testcase( op==TK_OR ); - testcase( op==TK_PLUS ); - testcase( op==TK_MINUS ); - testcase( op==TK_REM ); - testcase( op==TK_BITAND ); - testcase( op==TK_BITOR ); - testcase( op==TK_SLASH ); - testcase( op==TK_LSHIFT ); - testcase( op==TK_RSHIFT ); - testcase( op==TK_CONCAT ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - sqlite3VdbeAddOp3(v, op, r2, r1, target); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - break; - } - case TK_UMINUS: { - Expr *pLeft = pExpr->pLeft; - assert( pLeft ); - if( pLeft->op==TK_INTEGER ){ - codeInteger(pParse, pLeft, 1, target); -#ifndef SQLITE_OMIT_FLOATING_POINT - }else if( pLeft->op==TK_FLOAT ){ - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - codeReal(v, pLeft->u.zToken, 1, target); -#endif - }else{ - regFree1 = r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Integer, 0, r1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free2); - sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target); - testcase( regFree2==0 ); - } - inReg = target; - break; - } - case TK_BITNOT: - case TK_NOT: { - assert( TK_BITNOT==OP_BitNot ); - assert( TK_NOT==OP_Not ); - testcase( op==TK_BITNOT ); - testcase( op==TK_NOT ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - testcase( regFree1==0 ); - inReg = target; - sqlite3VdbeAddOp2(v, op, r1, inReg); - break; - } - case TK_ISNULL: - case TK_NOTNULL: { - int addr; - assert( TK_ISNULL==OP_IsNull ); - assert( TK_NOTNULL==OP_NotNull ); - testcase( op==TK_ISNULL ); - testcase( op==TK_NOTNULL ); - sqlite3VdbeAddOp2(v, OP_Integer, 1, target); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - testcase( regFree1==0 ); - addr = sqlite3VdbeAddOp1(v, op, r1); - sqlite3VdbeAddOp2(v, OP_AddImm, target, -1); - sqlite3VdbeJumpHere(v, addr); - break; - } - case TK_AGG_FUNCTION: { - AggInfo *pInfo = pExpr->pAggInfo; - if( pInfo==0 ){ - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken); - }else{ - inReg = pInfo->aFunc[pExpr->iAgg].iMem; - } - break; - } - case TK_CONST_FUNC: - case TK_FUNCTION: { - ExprList *pFarg; /* List of function arguments */ - int nFarg; /* Number of function arguments */ - FuncDef *pDef; /* The function definition object */ - int nId; /* Length of the function name in bytes */ - const char *zId; /* The function name */ - int constMask = 0; /* Mask of function arguments that are constant */ - int i; /* Loop counter */ - u8 enc = ENC(db); /* The text encoding used by this database */ - CollSeq *pColl = 0; /* A collating sequence */ - - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - testcase( op==TK_CONST_FUNC ); - testcase( op==TK_FUNCTION ); - if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){ - pFarg = 0; - }else{ - pFarg = pExpr->x.pList; - } - nFarg = pFarg ? pFarg->nExpr : 0; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - zId = pExpr->u.zToken; - nId = sqlite3Strlen30(zId); - pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0); - if( pDef==0 ){ - sqlite3ErrorMsg(pParse, "unknown function: %.*s()", nId, zId); - break; - } - - /* Attempt a direct implementation of the built-in COALESCE() and - ** IFNULL() functions. This avoids unnecessary evalation of - ** arguments past the first non-NULL argument. - */ - if( pDef->flags & SQLITE_FUNC_COALESCE ){ - int endCoalesce = sqlite3VdbeMakeLabel(v); - assert( nFarg>=2 ); - sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target); - for(i=1; ia[i].pExpr, target); - sqlite3ExprCachePop(pParse, 1); - } - sqlite3VdbeResolveLabel(v, endCoalesce); - break; - } - - - if( pFarg ){ - r1 = sqlite3GetTempRange(pParse, nFarg); - - /* For length() and typeof() functions with a column argument, - ** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG - ** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data - ** loading. - */ - if( (pDef->flags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){ - u8 exprOp; - assert( nFarg==1 ); - assert( pFarg->a[0].pExpr!=0 ); - exprOp = pFarg->a[0].pExpr->op; - if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){ - assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG ); - assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG ); - testcase( pDef->flags==SQLITE_FUNC_LENGTH ); - pFarg->a[0].pExpr->op2 = pDef->flags; - } - } - - sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */ - sqlite3ExprCodeExprList(pParse, pFarg, r1, 1); - sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */ - }else{ - r1 = 0; - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - /* Possibly overload the function if the first argument is - ** a virtual table column. - ** - ** For infix functions (LIKE, GLOB, REGEXP, and MATCH) use the - ** second argument, not the first, as the argument to test to - ** see if it is a column in a virtual table. This is done because - ** the left operand of infix functions (the operand we want to - ** control overloading) ends up as the second argument to the - ** function. The expression "A glob B" is equivalent to - ** "glob(B,A). We want to use the A in "A glob B" to test - ** for function overloading. But we use the B term in "glob(B,A)". - */ - if( nFarg>=2 && (pExpr->flags & EP_InfixFunc) ){ - pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[1].pExpr); - }else if( nFarg>0 ){ - pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr); - } -#endif - for(i=0; ia[i].pExpr) ){ - constMask |= (1<flags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){ - pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr); - } - } - if( pDef->flags & SQLITE_FUNC_NEEDCOLL ){ - if( !pColl ) pColl = db->pDfltColl; - sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); - } - sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target, - (char*)pDef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, (u8)nFarg); - if( nFarg ){ - sqlite3ReleaseTempRange(pParse, r1, nFarg); - } - break; - } -#ifndef SQLITE_OMIT_SUBQUERY - case TK_EXISTS: - case TK_SELECT: { - testcase( op==TK_EXISTS ); - testcase( op==TK_SELECT ); - inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0); - break; - } - case TK_IN: { - int destIfFalse = sqlite3VdbeMakeLabel(v); - int destIfNull = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp2(v, OP_Null, 0, target); - sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); - sqlite3VdbeAddOp2(v, OP_Integer, 1, target); - sqlite3VdbeResolveLabel(v, destIfFalse); - sqlite3VdbeAddOp2(v, OP_AddImm, target, 0); - sqlite3VdbeResolveLabel(v, destIfNull); - break; - } -#endif /* SQLITE_OMIT_SUBQUERY */ - - - /* - ** x BETWEEN y AND z - ** - ** This is equivalent to - ** - ** x>=y AND x<=z - ** - ** X is stored in pExpr->pLeft. - ** Y is stored in pExpr->pList->a[0].pExpr. - ** Z is stored in pExpr->pList->a[1].pExpr. - */ - case TK_BETWEEN: { - Expr *pLeft = pExpr->pLeft; - struct ExprList_item *pLItem = pExpr->x.pList->a; - Expr *pRight = pLItem->pExpr; - - r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pRight, ®Free2); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - r3 = sqlite3GetTempReg(pParse); - r4 = sqlite3GetTempReg(pParse); - codeCompare(pParse, pLeft, pRight, OP_Ge, - r1, r2, r3, SQLITE_STOREP2); - pLItem++; - pRight = pLItem->pExpr; - sqlite3ReleaseTempReg(pParse, regFree2); - r2 = sqlite3ExprCodeTemp(pParse, pRight, ®Free2); - testcase( regFree2==0 ); - codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r4, SQLITE_STOREP2); - sqlite3VdbeAddOp3(v, OP_And, r3, r4, target); - sqlite3ReleaseTempReg(pParse, r3); - sqlite3ReleaseTempReg(pParse, r4); - break; - } - case TK_COLLATE: - case TK_UPLUS: { - inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); - break; - } - - case TK_TRIGGER: { - /* If the opcode is TK_TRIGGER, then the expression is a reference - ** to a column in the new.* or old.* pseudo-tables available to - ** trigger programs. In this case Expr.iTable is set to 1 for the - ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn - ** is set to the column of the pseudo-table to read, or to -1 to - ** read the rowid field. - ** - ** The expression is implemented using an OP_Param opcode. The p1 - ** parameter is set to 0 for an old.rowid reference, or to (i+1) - ** to reference another column of the old.* pseudo-table, where - ** i is the index of the column. For a new.rowid reference, p1 is - ** set to (n+1), where n is the number of columns in each pseudo-table. - ** For a reference to any other column in the new.* pseudo-table, p1 - ** is set to (n+2+i), where n and i are as defined previously. For - ** example, if the table on which triggers are being fired is - ** declared as: - ** - ** CREATE TABLE t1(a, b); - ** - ** Then p1 is interpreted as follows: - ** - ** p1==0 -> old.rowid p1==3 -> new.rowid - ** p1==1 -> old.a p1==4 -> new.a - ** p1==2 -> old.b p1==5 -> new.b - */ - Table *pTab = pExpr->pTab; - int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn; - - assert( pExpr->iTable==0 || pExpr->iTable==1 ); - assert( pExpr->iColumn>=-1 && pExpr->iColumnnCol ); - assert( pTab->iPKey<0 || pExpr->iColumn!=pTab->iPKey ); - assert( p1>=0 && p1<(pTab->nCol*2+2) ); - - sqlite3VdbeAddOp2(v, OP_Param, p1, target); - VdbeComment((v, "%s.%s -> $%d", - (pExpr->iTable ? "new" : "old"), - (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName), - target - )); - -#ifndef SQLITE_OMIT_FLOATING_POINT - /* If the column has REAL affinity, it may currently be stored as an - ** integer. Use OP_RealAffinity to make sure it is really real. */ - if( pExpr->iColumn>=0 - && pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL - ){ - sqlite3VdbeAddOp1(v, OP_RealAffinity, target); - } -#endif - break; - } - - - /* - ** Form A: - ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END - ** - ** Form B: - ** CASE WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END - ** - ** Form A is can be transformed into the equivalent form B as follows: - ** CASE WHEN x=e1 THEN r1 WHEN x=e2 THEN r2 ... - ** WHEN x=eN THEN rN ELSE y END - ** - ** X (if it exists) is in pExpr->pLeft. - ** Y is in pExpr->pRight. The Y is also optional. If there is no - ** ELSE clause and no other term matches, then the result of the - ** exprssion is NULL. - ** Ei is in pExpr->pList->a[i*2] and Ri is pExpr->pList->a[i*2+1]. - ** - ** The result of the expression is the Ri for the first matching Ei, - ** or if there is no matching Ei, the ELSE term Y, or if there is - ** no ELSE term, NULL. - */ - default: assert( op==TK_CASE ); { - int endLabel; /* GOTO label for end of CASE stmt */ - int nextCase; /* GOTO label for next WHEN clause */ - int nExpr; /* 2x number of WHEN terms */ - int i; /* Loop counter */ - ExprList *pEList; /* List of WHEN terms */ - struct ExprList_item *aListelem; /* Array of WHEN terms */ - Expr opCompare; /* The X==Ei expression */ - Expr cacheX; /* Cached expression X */ - Expr *pX; /* The X expression */ - Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */ - VVA_ONLY( int iCacheLevel = pParse->iCacheLevel; ) - - assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList ); - assert((pExpr->x.pList->nExpr % 2) == 0); - assert(pExpr->x.pList->nExpr > 0); - pEList = pExpr->x.pList; - aListelem = pEList->a; - nExpr = pEList->nExpr; - endLabel = sqlite3VdbeMakeLabel(v); - if( (pX = pExpr->pLeft)!=0 ){ - cacheX = *pX; - testcase( pX->op==TK_COLUMN ); - testcase( pX->op==TK_REGISTER ); - cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, ®Free1); - testcase( regFree1==0 ); - cacheX.op = TK_REGISTER; - opCompare.op = TK_EQ; - opCompare.pLeft = &cacheX; - pTest = &opCompare; - /* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001: - ** The value in regFree1 might get SCopy-ed into the file result. - ** So make sure that the regFree1 register is not reused for other - ** purposes and possibly overwritten. */ - regFree1 = 0; - } - for(i=0; iop==TK_COLUMN ); - sqlite3ExprIfFalse(pParse, pTest, nextCase, SQLITE_JUMPIFNULL); - testcase( aListelem[i+1].pExpr->op==TK_COLUMN ); - testcase( aListelem[i+1].pExpr->op==TK_REGISTER ); - sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target); - sqlite3VdbeAddOp2(v, OP_Goto, 0, endLabel); - sqlite3ExprCachePop(pParse, 1); - sqlite3VdbeResolveLabel(v, nextCase); - } - if( pExpr->pRight ){ - sqlite3ExprCachePush(pParse); - sqlite3ExprCode(pParse, pExpr->pRight, target); - sqlite3ExprCachePop(pParse, 1); - }else{ - sqlite3VdbeAddOp2(v, OP_Null, 0, target); - } - assert( db->mallocFailed || pParse->nErr>0 - || pParse->iCacheLevel==iCacheLevel ); - sqlite3VdbeResolveLabel(v, endLabel); - break; - } -#ifndef SQLITE_OMIT_TRIGGER - case TK_RAISE: { - assert( pExpr->affinity==OE_Rollback - || pExpr->affinity==OE_Abort - || pExpr->affinity==OE_Fail - || pExpr->affinity==OE_Ignore - ); - if( !pParse->pTriggerTab ){ - sqlite3ErrorMsg(pParse, - "RAISE() may only be used within a trigger-program"); - return 0; - } - if( pExpr->affinity==OE_Abort ){ - sqlite3MayAbort(pParse); - } - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - if( pExpr->affinity==OE_Ignore ){ - sqlite3VdbeAddOp4( - v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); - }else{ - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER, - pExpr->affinity, pExpr->u.zToken, 0); - } - - break; - } -#endif - } - sqlite3ReleaseTempReg(pParse, regFree1); - sqlite3ReleaseTempReg(pParse, regFree2); - return inReg; -} - -/* -** Generate code to evaluate an expression and store the results -** into a register. Return the register number where the results -** are stored. -** -** If the register is a temporary register that can be deallocated, -** then write its number into *pReg. If the result register is not -** a temporary, then set *pReg to zero. -*/ -SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ - int r1 = sqlite3GetTempReg(pParse); - int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); - if( r2==r1 ){ - *pReg = r1; - }else{ - sqlite3ReleaseTempReg(pParse, r1); - *pReg = 0; - } - return r2; -} - -/* -** Generate code that will evaluate expression pExpr and store the -** results in register target. The results are guaranteed to appear -** in register target. -*/ -SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ - int inReg; - - assert( target>0 && target<=pParse->nMem ); - if( pExpr && pExpr->op==TK_REGISTER ){ - sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, pExpr->iTable, target); - }else{ - inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); - assert( pParse->pVdbe || pParse->db->mallocFailed ); - if( inReg!=target && pParse->pVdbe ){ - sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target); - } - } - return target; -} - -/* -** Generate code that evalutes the given expression and puts the result -** in register target. -** -** Also make a copy of the expression results into another "cache" register -** and modify the expression so that the next time it is evaluated, -** the result is a copy of the cache register. -** -** This routine is used for expressions that are used multiple -** times. They are evaluated once and the results of the expression -** are reused. -*/ -SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){ - Vdbe *v = pParse->pVdbe; - int inReg; - inReg = sqlite3ExprCode(pParse, pExpr, target); - assert( target>0 ); - /* This routine is called for terms to INSERT or UPDATE. And the only - ** other place where expressions can be converted into TK_REGISTER is - ** in WHERE clause processing. So as currently implemented, there is - ** no way for a TK_REGISTER to exist here. But it seems prudent to - ** keep the ALWAYS() in case the conditions above change with future - ** modifications or enhancements. */ - if( ALWAYS(pExpr->op!=TK_REGISTER) ){ - int iMem; - iMem = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem); - pExpr->iTable = iMem; - pExpr->op2 = pExpr->op; - pExpr->op = TK_REGISTER; - } - return inReg; -} - -#if defined(SQLITE_ENABLE_TREE_EXPLAIN) -/* -** Generate a human-readable explanation of an expression tree. -*/ -SQLITE_PRIVATE void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){ - int op; /* The opcode being coded */ - const char *zBinOp = 0; /* Binary operator */ - const char *zUniOp = 0; /* Unary operator */ - if( pExpr==0 ){ - op = TK_NULL; - }else{ - op = pExpr->op; - } - switch( op ){ - case TK_AGG_COLUMN: { - sqlite3ExplainPrintf(pOut, "AGG{%d:%d}", - pExpr->iTable, pExpr->iColumn); - break; - } - case TK_COLUMN: { - if( pExpr->iTable<0 ){ - /* This only happens when coding check constraints */ - sqlite3ExplainPrintf(pOut, "COLUMN(%d)", pExpr->iColumn); - }else{ - sqlite3ExplainPrintf(pOut, "{%d:%d}", - pExpr->iTable, pExpr->iColumn); - } - break; - } - case TK_INTEGER: { - if( pExpr->flags & EP_IntValue ){ - sqlite3ExplainPrintf(pOut, "%d", pExpr->u.iValue); - }else{ - sqlite3ExplainPrintf(pOut, "%s", pExpr->u.zToken); - } - break; - } -#ifndef SQLITE_OMIT_FLOATING_POINT - case TK_FLOAT: { - sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken); - break; - } -#endif - case TK_STRING: { - sqlite3ExplainPrintf(pOut,"%Q", pExpr->u.zToken); - break; - } - case TK_NULL: { - sqlite3ExplainPrintf(pOut,"NULL"); - break; - } -#ifndef SQLITE_OMIT_BLOB_LITERAL - case TK_BLOB: { - sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken); - break; - } -#endif - case TK_VARIABLE: { - sqlite3ExplainPrintf(pOut,"VARIABLE(%s,%d)", - pExpr->u.zToken, pExpr->iColumn); - break; - } - case TK_REGISTER: { - sqlite3ExplainPrintf(pOut,"REGISTER(%d)", pExpr->iTable); - break; - } - case TK_AS: { - sqlite3ExplainExpr(pOut, pExpr->pLeft); - break; - } -#ifndef SQLITE_OMIT_CAST - case TK_CAST: { - /* Expressions of the form: CAST(pLeft AS token) */ - const char *zAff = "unk"; - switch( sqlite3AffinityType(pExpr->u.zToken) ){ - case SQLITE_AFF_TEXT: zAff = "TEXT"; break; - case SQLITE_AFF_NONE: zAff = "NONE"; break; - case SQLITE_AFF_NUMERIC: zAff = "NUMERIC"; break; - case SQLITE_AFF_INTEGER: zAff = "INTEGER"; break; - case SQLITE_AFF_REAL: zAff = "REAL"; break; - } - sqlite3ExplainPrintf(pOut, "CAST-%s(", zAff); - sqlite3ExplainExpr(pOut, pExpr->pLeft); - sqlite3ExplainPrintf(pOut, ")"); - break; - } -#endif /* SQLITE_OMIT_CAST */ - case TK_LT: zBinOp = "LT"; break; - case TK_LE: zBinOp = "LE"; break; - case TK_GT: zBinOp = "GT"; break; - case TK_GE: zBinOp = "GE"; break; - case TK_NE: zBinOp = "NE"; break; - case TK_EQ: zBinOp = "EQ"; break; - case TK_IS: zBinOp = "IS"; break; - case TK_ISNOT: zBinOp = "ISNOT"; break; - case TK_AND: zBinOp = "AND"; break; - case TK_OR: zBinOp = "OR"; break; - case TK_PLUS: zBinOp = "ADD"; break; - case TK_STAR: zBinOp = "MUL"; break; - case TK_MINUS: zBinOp = "SUB"; break; - case TK_REM: zBinOp = "REM"; break; - case TK_BITAND: zBinOp = "BITAND"; break; - case TK_BITOR: zBinOp = "BITOR"; break; - case TK_SLASH: zBinOp = "DIV"; break; - case TK_LSHIFT: zBinOp = "LSHIFT"; break; - case TK_RSHIFT: zBinOp = "RSHIFT"; break; - case TK_CONCAT: zBinOp = "CONCAT"; break; - - case TK_UMINUS: zUniOp = "UMINUS"; break; - case TK_UPLUS: zUniOp = "UPLUS"; break; - case TK_BITNOT: zUniOp = "BITNOT"; break; - case TK_NOT: zUniOp = "NOT"; break; - case TK_ISNULL: zUniOp = "ISNULL"; break; - case TK_NOTNULL: zUniOp = "NOTNULL"; break; - - case TK_COLLATE: { - sqlite3ExplainExpr(pOut, pExpr->pLeft); - sqlite3ExplainPrintf(pOut,".COLLATE(%s)",pExpr->u.zToken); - break; - } - - case TK_AGG_FUNCTION: - case TK_CONST_FUNC: - case TK_FUNCTION: { - ExprList *pFarg; /* List of function arguments */ - if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){ - pFarg = 0; - }else{ - pFarg = pExpr->x.pList; - } - if( op==TK_AGG_FUNCTION ){ - sqlite3ExplainPrintf(pOut, "AGG_FUNCTION%d:%s(", - pExpr->op2, pExpr->u.zToken); - }else{ - sqlite3ExplainPrintf(pOut, "FUNCTION:%s(", pExpr->u.zToken); - } - if( pFarg ){ - sqlite3ExplainExprList(pOut, pFarg); - } - sqlite3ExplainPrintf(pOut, ")"); - break; - } -#ifndef SQLITE_OMIT_SUBQUERY - case TK_EXISTS: { - sqlite3ExplainPrintf(pOut, "EXISTS("); - sqlite3ExplainSelect(pOut, pExpr->x.pSelect); - sqlite3ExplainPrintf(pOut,")"); - break; - } - case TK_SELECT: { - sqlite3ExplainPrintf(pOut, "("); - sqlite3ExplainSelect(pOut, pExpr->x.pSelect); - sqlite3ExplainPrintf(pOut, ")"); - break; - } - case TK_IN: { - sqlite3ExplainPrintf(pOut, "IN("); - sqlite3ExplainExpr(pOut, pExpr->pLeft); - sqlite3ExplainPrintf(pOut, ","); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - sqlite3ExplainSelect(pOut, pExpr->x.pSelect); - }else{ - sqlite3ExplainExprList(pOut, pExpr->x.pList); - } - sqlite3ExplainPrintf(pOut, ")"); - break; - } -#endif /* SQLITE_OMIT_SUBQUERY */ - - /* - ** x BETWEEN y AND z - ** - ** This is equivalent to - ** - ** x>=y AND x<=z - ** - ** X is stored in pExpr->pLeft. - ** Y is stored in pExpr->pList->a[0].pExpr. - ** Z is stored in pExpr->pList->a[1].pExpr. - */ - case TK_BETWEEN: { - Expr *pX = pExpr->pLeft; - Expr *pY = pExpr->x.pList->a[0].pExpr; - Expr *pZ = pExpr->x.pList->a[1].pExpr; - sqlite3ExplainPrintf(pOut, "BETWEEN("); - sqlite3ExplainExpr(pOut, pX); - sqlite3ExplainPrintf(pOut, ","); - sqlite3ExplainExpr(pOut, pY); - sqlite3ExplainPrintf(pOut, ","); - sqlite3ExplainExpr(pOut, pZ); - sqlite3ExplainPrintf(pOut, ")"); - break; - } - case TK_TRIGGER: { - /* If the opcode is TK_TRIGGER, then the expression is a reference - ** to a column in the new.* or old.* pseudo-tables available to - ** trigger programs. In this case Expr.iTable is set to 1 for the - ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn - ** is set to the column of the pseudo-table to read, or to -1 to - ** read the rowid field. - */ - sqlite3ExplainPrintf(pOut, "%s(%d)", - pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn); - break; - } - case TK_CASE: { - sqlite3ExplainPrintf(pOut, "CASE("); - sqlite3ExplainExpr(pOut, pExpr->pLeft); - sqlite3ExplainPrintf(pOut, ","); - sqlite3ExplainExprList(pOut, pExpr->x.pList); - break; - } -#ifndef SQLITE_OMIT_TRIGGER - case TK_RAISE: { - const char *zType = "unk"; - switch( pExpr->affinity ){ - case OE_Rollback: zType = "rollback"; break; - case OE_Abort: zType = "abort"; break; - case OE_Fail: zType = "fail"; break; - case OE_Ignore: zType = "ignore"; break; - } - sqlite3ExplainPrintf(pOut, "RAISE-%s(%s)", zType, pExpr->u.zToken); - break; - } -#endif - } - if( zBinOp ){ - sqlite3ExplainPrintf(pOut,"%s(", zBinOp); - sqlite3ExplainExpr(pOut, pExpr->pLeft); - sqlite3ExplainPrintf(pOut,","); - sqlite3ExplainExpr(pOut, pExpr->pRight); - sqlite3ExplainPrintf(pOut,")"); - }else if( zUniOp ){ - sqlite3ExplainPrintf(pOut,"%s(", zUniOp); - sqlite3ExplainExpr(pOut, pExpr->pLeft); - sqlite3ExplainPrintf(pOut,")"); - } -} -#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */ - -#if defined(SQLITE_ENABLE_TREE_EXPLAIN) -/* -** Generate a human-readable explanation of an expression list. -*/ -SQLITE_PRIVATE void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){ - int i; - if( pList==0 || pList->nExpr==0 ){ - sqlite3ExplainPrintf(pOut, "(empty-list)"); - return; - }else if( pList->nExpr==1 ){ - sqlite3ExplainExpr(pOut, pList->a[0].pExpr); - }else{ - sqlite3ExplainPush(pOut); - for(i=0; inExpr; i++){ - sqlite3ExplainPrintf(pOut, "item[%d] = ", i); - sqlite3ExplainPush(pOut); - sqlite3ExplainExpr(pOut, pList->a[i].pExpr); - sqlite3ExplainPop(pOut); - if( pList->a[i].zName ){ - sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName); - } - if( pList->a[i].bSpanIsTab ){ - sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan); - } - if( inExpr-1 ){ - sqlite3ExplainNL(pOut); - } - } - sqlite3ExplainPop(pOut); - } -} -#endif /* SQLITE_DEBUG */ - -/* -** Return TRUE if pExpr is an constant expression that is appropriate -** for factoring out of a loop. Appropriate expressions are: -** -** * Any expression that evaluates to two or more opcodes. -** -** * Any OP_Integer, OP_Real, OP_String, OP_Blob, OP_Null, -** or OP_Variable that does not need to be placed in a -** specific register. -** -** There is no point in factoring out single-instruction constant -** expressions that need to be placed in a particular register. -** We could factor them out, but then we would end up adding an -** OP_SCopy instruction to move the value into the correct register -** later. We might as well just use the original instruction and -** avoid the OP_SCopy. -*/ -static int isAppropriateForFactoring(Expr *p){ - if( !sqlite3ExprIsConstantNotJoin(p) ){ - return 0; /* Only constant expressions are appropriate for factoring */ - } - if( (p->flags & EP_FixedDest)==0 ){ - return 1; /* Any constant without a fixed destination is appropriate */ - } - while( p->op==TK_UPLUS ) p = p->pLeft; - switch( p->op ){ -#ifndef SQLITE_OMIT_BLOB_LITERAL - case TK_BLOB: -#endif - case TK_VARIABLE: - case TK_INTEGER: - case TK_FLOAT: - case TK_NULL: - case TK_STRING: { - testcase( p->op==TK_BLOB ); - testcase( p->op==TK_VARIABLE ); - testcase( p->op==TK_INTEGER ); - testcase( p->op==TK_FLOAT ); - testcase( p->op==TK_NULL ); - testcase( p->op==TK_STRING ); - /* Single-instruction constants with a fixed destination are - ** better done in-line. If we factor them, they will just end - ** up generating an OP_SCopy to move the value to the destination - ** register. */ - return 0; - } - case TK_UMINUS: { - if( p->pLeft->op==TK_FLOAT || p->pLeft->op==TK_INTEGER ){ - return 0; - } - break; - } - default: { - break; - } - } - return 1; -} - -/* -** If pExpr is a constant expression that is appropriate for -** factoring out of a loop, then evaluate the expression -** into a register and convert the expression into a TK_REGISTER -** expression. -*/ -static int evalConstExpr(Walker *pWalker, Expr *pExpr){ - Parse *pParse = pWalker->pParse; - switch( pExpr->op ){ - case TK_IN: - case TK_REGISTER: { - return WRC_Prune; - } - case TK_COLLATE: { - return WRC_Continue; - } - case TK_FUNCTION: - case TK_AGG_FUNCTION: - case TK_CONST_FUNC: { - /* The arguments to a function have a fixed destination. - ** Mark them this way to avoid generated unneeded OP_SCopy - ** instructions. - */ - ExprList *pList = pExpr->x.pList; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - if( pList ){ - int i = pList->nExpr; - struct ExprList_item *pItem = pList->a; - for(; i>0; i--, pItem++){ - if( ALWAYS(pItem->pExpr) ) pItem->pExpr->flags |= EP_FixedDest; - } - } - break; - } - } - if( isAppropriateForFactoring(pExpr) ){ - int r1 = ++pParse->nMem; - int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); - /* If r2!=r1, it means that register r1 is never used. That is harmless - ** but suboptimal, so we want to know about the situation to fix it. - ** Hence the following assert: */ - assert( r2==r1 ); - pExpr->op2 = pExpr->op; - pExpr->op = TK_REGISTER; - pExpr->iTable = r2; - return WRC_Prune; - } - return WRC_Continue; -} - -/* -** Preevaluate constant subexpressions within pExpr and store the -** results in registers. Modify pExpr so that the constant subexpresions -** are TK_REGISTER opcodes that refer to the precomputed values. -** -** This routine is a no-op if the jump to the cookie-check code has -** already occur. Since the cookie-check jump is generated prior to -** any other serious processing, this check ensures that there is no -** way to accidently bypass the constant initializations. -** -** This routine is also a no-op if the SQLITE_FactorOutConst optimization -** is disabled via the sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) -** interface. This allows test logic to verify that the same answer is -** obtained for queries regardless of whether or not constants are -** precomputed into registers or if they are inserted in-line. -*/ -SQLITE_PRIVATE void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){ - Walker w; - if( pParse->cookieGoto ) return; - if( OptimizationDisabled(pParse->db, SQLITE_FactorOutConst) ) return; - w.xExprCallback = evalConstExpr; - w.xSelectCallback = 0; - w.pParse = pParse; - sqlite3WalkExpr(&w, pExpr); -} - - -/* -** Generate code that pushes the value of every element of the given -** expression list into a sequence of registers beginning at target. -** -** Return the number of elements evaluated. -*/ -SQLITE_PRIVATE int sqlite3ExprCodeExprList( - Parse *pParse, /* Parsing context */ - ExprList *pList, /* The expression list to be coded */ - int target, /* Where to write results */ - int doHardCopy /* Make a hard copy of every element */ -){ - struct ExprList_item *pItem; - int i, n; - assert( pList!=0 ); - assert( target>0 ); - assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */ - n = pList->nExpr; - for(pItem=pList->a, i=0; ipExpr; - int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); - if( inReg!=target+i ){ - sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy, - inReg, target+i); - } - } - return n; -} - -/* -** Generate code for a BETWEEN operator. -** -** x BETWEEN y AND z -** -** The above is equivalent to -** -** x>=y AND x<=z -** -** Code it as such, taking care to do the common subexpression -** elementation of x. -*/ -static void exprCodeBetween( - Parse *pParse, /* Parsing and code generating context */ - Expr *pExpr, /* The BETWEEN expression */ - int dest, /* Jump here if the jump is taken */ - int jumpIfTrue, /* Take the jump if the BETWEEN is true */ - int jumpIfNull /* Take the jump if the BETWEEN is NULL */ -){ - Expr exprAnd; /* The AND operator in x>=y AND x<=z */ - Expr compLeft; /* The x>=y term */ - Expr compRight; /* The x<=z term */ - Expr exprX; /* The x subexpression */ - int regFree1 = 0; /* Temporary use register */ - - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - exprX = *pExpr->pLeft; - exprAnd.op = TK_AND; - exprAnd.pLeft = &compLeft; - exprAnd.pRight = &compRight; - compLeft.op = TK_GE; - compLeft.pLeft = &exprX; - compLeft.pRight = pExpr->x.pList->a[0].pExpr; - compRight.op = TK_LE; - compRight.pLeft = &exprX; - compRight.pRight = pExpr->x.pList->a[1].pExpr; - exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1); - exprX.op = TK_REGISTER; - if( jumpIfTrue ){ - sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull); - }else{ - sqlite3ExprIfFalse(pParse, &exprAnd, dest, jumpIfNull); - } - sqlite3ReleaseTempReg(pParse, regFree1); - - /* Ensure adequate test coverage */ - testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1==0 ); - testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1!=0 ); - testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1==0 ); - testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1!=0 ); - testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1==0 ); - testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1!=0 ); - testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1==0 ); - testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1!=0 ); -} - -/* -** Generate code for a boolean expression such that a jump is made -** to the label "dest" if the expression is true but execution -** continues straight thru if the expression is false. -** -** If the expression evaluates to NULL (neither true nor false), then -** take the jump if the jumpIfNull flag is SQLITE_JUMPIFNULL. -** -** This code depends on the fact that certain token values (ex: TK_EQ) -** are the same as opcode values (ex: OP_Eq) that implement the corresponding -** operation. Special comments in vdbe.c and the mkopcodeh.awk script in -** the make process cause these values to align. Assert()s in the code -** below verify that the numbers are aligned correctly. -*/ -SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ - Vdbe *v = pParse->pVdbe; - int op = 0; - int regFree1 = 0; - int regFree2 = 0; - int r1, r2; - - assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 ); - if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */ - if( NEVER(pExpr==0) ) return; /* No way this can happen */ - op = pExpr->op; - switch( op ){ - case TK_AND: { - int d2 = sqlite3VdbeMakeLabel(v); - testcase( jumpIfNull==0 ); - sqlite3ExprCachePush(pParse); - sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL); - sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); - sqlite3VdbeResolveLabel(v, d2); - sqlite3ExprCachePop(pParse, 1); - break; - } - case TK_OR: { - testcase( jumpIfNull==0 ); - sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); - sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); - break; - } - case TK_NOT: { - testcase( jumpIfNull==0 ); - sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); - break; - } - case TK_LT: - case TK_LE: - case TK_GT: - case TK_GE: - case TK_NE: - case TK_EQ: { - assert( TK_LT==OP_Lt ); - assert( TK_LE==OP_Le ); - assert( TK_GT==OP_Gt ); - assert( TK_GE==OP_Ge ); - assert( TK_EQ==OP_Eq ); - assert( TK_NE==OP_Ne ); - testcase( op==TK_LT ); - testcase( op==TK_LE ); - testcase( op==TK_GT ); - testcase( op==TK_GE ); - testcase( op==TK_EQ ); - testcase( op==TK_NE ); - testcase( jumpIfNull==0 ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, - r1, r2, dest, jumpIfNull); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - break; - } - case TK_IS: - case TK_ISNOT: { - testcase( op==TK_IS ); - testcase( op==TK_ISNOT ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - op = (op==TK_IS) ? TK_EQ : TK_NE; - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, - r1, r2, dest, SQLITE_NULLEQ); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - break; - } - case TK_ISNULL: - case TK_NOTNULL: { - assert( TK_ISNULL==OP_IsNull ); - assert( TK_NOTNULL==OP_NotNull ); - testcase( op==TK_ISNULL ); - testcase( op==TK_NOTNULL ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - sqlite3VdbeAddOp2(v, op, r1, dest); - testcase( regFree1==0 ); - break; - } - case TK_BETWEEN: { - testcase( jumpIfNull==0 ); - exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull); - break; - } -#ifndef SQLITE_OMIT_SUBQUERY - case TK_IN: { - int destIfFalse = sqlite3VdbeMakeLabel(v); - int destIfNull = jumpIfNull ? dest : destIfFalse; - sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); - sqlite3VdbeAddOp2(v, OP_Goto, 0, dest); - sqlite3VdbeResolveLabel(v, destIfFalse); - break; - } -#endif - default: { - r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); - sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); - testcase( regFree1==0 ); - testcase( jumpIfNull==0 ); - break; - } - } - sqlite3ReleaseTempReg(pParse, regFree1); - sqlite3ReleaseTempReg(pParse, regFree2); -} - -/* -** Generate code for a boolean expression such that a jump is made -** to the label "dest" if the expression is false but execution -** continues straight thru if the expression is true. -** -** If the expression evaluates to NULL (neither true nor false) then -** jump if jumpIfNull is SQLITE_JUMPIFNULL or fall through if jumpIfNull -** is 0. -*/ -SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ - Vdbe *v = pParse->pVdbe; - int op = 0; - int regFree1 = 0; - int regFree2 = 0; - int r1, r2; - - assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 ); - if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */ - if( pExpr==0 ) return; - - /* The value of pExpr->op and op are related as follows: - ** - ** pExpr->op op - ** --------- ---------- - ** TK_ISNULL OP_NotNull - ** TK_NOTNULL OP_IsNull - ** TK_NE OP_Eq - ** TK_EQ OP_Ne - ** TK_GT OP_Le - ** TK_LE OP_Gt - ** TK_GE OP_Lt - ** TK_LT OP_Ge - ** - ** For other values of pExpr->op, op is undefined and unused. - ** The value of TK_ and OP_ constants are arranged such that we - ** can compute the mapping above using the following expression. - ** Assert()s verify that the computation is correct. - */ - op = ((pExpr->op+(TK_ISNULL&1))^1)-(TK_ISNULL&1); - - /* Verify correct alignment of TK_ and OP_ constants - */ - assert( pExpr->op!=TK_ISNULL || op==OP_NotNull ); - assert( pExpr->op!=TK_NOTNULL || op==OP_IsNull ); - assert( pExpr->op!=TK_NE || op==OP_Eq ); - assert( pExpr->op!=TK_EQ || op==OP_Ne ); - assert( pExpr->op!=TK_LT || op==OP_Ge ); - assert( pExpr->op!=TK_LE || op==OP_Gt ); - assert( pExpr->op!=TK_GT || op==OP_Le ); - assert( pExpr->op!=TK_GE || op==OP_Lt ); - - switch( pExpr->op ){ - case TK_AND: { - testcase( jumpIfNull==0 ); - sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); - sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); - break; - } - case TK_OR: { - int d2 = sqlite3VdbeMakeLabel(v); - testcase( jumpIfNull==0 ); - sqlite3ExprCachePush(pParse); - sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL); - sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); - sqlite3VdbeResolveLabel(v, d2); - sqlite3ExprCachePop(pParse, 1); - break; - } - case TK_NOT: { - testcase( jumpIfNull==0 ); - sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); - break; - } - case TK_LT: - case TK_LE: - case TK_GT: - case TK_GE: - case TK_NE: - case TK_EQ: { - testcase( op==TK_LT ); - testcase( op==TK_LE ); - testcase( op==TK_GT ); - testcase( op==TK_GE ); - testcase( op==TK_EQ ); - testcase( op==TK_NE ); - testcase( jumpIfNull==0 ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, - r1, r2, dest, jumpIfNull); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - break; - } - case TK_IS: - case TK_ISNOT: { - testcase( pExpr->op==TK_IS ); - testcase( pExpr->op==TK_ISNOT ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; - codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, - r1, r2, dest, SQLITE_NULLEQ); - testcase( regFree1==0 ); - testcase( regFree2==0 ); - break; - } - case TK_ISNULL: - case TK_NOTNULL: { - testcase( op==TK_ISNULL ); - testcase( op==TK_NOTNULL ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); - sqlite3VdbeAddOp2(v, op, r1, dest); - testcase( regFree1==0 ); - break; - } - case TK_BETWEEN: { - testcase( jumpIfNull==0 ); - exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull); - break; - } -#ifndef SQLITE_OMIT_SUBQUERY - case TK_IN: { - if( jumpIfNull ){ - sqlite3ExprCodeIN(pParse, pExpr, dest, dest); - }else{ - int destIfNull = sqlite3VdbeMakeLabel(v); - sqlite3ExprCodeIN(pParse, pExpr, dest, destIfNull); - sqlite3VdbeResolveLabel(v, destIfNull); - } - break; - } -#endif - default: { - r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); - sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); - testcase( regFree1==0 ); - testcase( jumpIfNull==0 ); - break; - } - } - sqlite3ReleaseTempReg(pParse, regFree1); - sqlite3ReleaseTempReg(pParse, regFree2); -} - -/* -** Do a deep comparison of two expression trees. Return 0 if the two -** expressions are completely identical. Return 1 if they differ only -** by a COLLATE operator at the top level. Return 2 if there are differences -** other than the top-level COLLATE operator. -** -** Sometimes this routine will return 2 even if the two expressions -** really are equivalent. If we cannot prove that the expressions are -** identical, we return 2 just to be safe. So if this routine -** returns 2, then you do not really know for certain if the two -** expressions are the same. But if you get a 0 or 1 return, then you -** can be sure the expressions are the same. In the places where -** this routine is used, it does not hurt to get an extra 2 - that -** just might result in some slightly slower code. But returning -** an incorrect 0 or 1 could lead to a malfunction. -*/ -SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB){ - if( pA==0||pB==0 ){ - return pB==pA ? 0 : 2; - } - assert( !ExprHasAnyProperty(pA, EP_TokenOnly|EP_Reduced) ); - assert( !ExprHasAnyProperty(pB, EP_TokenOnly|EP_Reduced) ); - if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){ - return 2; - } - if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2; - if( pA->op!=pB->op ){ - if( pA->op==TK_COLLATE && sqlite3ExprCompare(pA->pLeft, pB)<2 ){ - return 1; - } - if( pB->op==TK_COLLATE && sqlite3ExprCompare(pA, pB->pLeft)<2 ){ - return 1; - } - return 2; - } - if( sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 2; - if( sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 2; - if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList) ) return 2; - if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 2; - if( ExprHasProperty(pA, EP_IntValue) ){ - if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){ - return 2; - } - }else if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken){ - if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2; - if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ - return pA->op==TK_COLLATE ? 1 : 2; - } - } - return 0; -} - -/* -** Compare two ExprList objects. Return 0 if they are identical and -** non-zero if they differ in any way. -** -** This routine might return non-zero for equivalent ExprLists. The -** only consequence will be disabled optimizations. But this routine -** must never return 0 if the two ExprList objects are different, or -** a malfunction will result. -** -** Two NULL pointers are considered to be the same. But a NULL pointer -** always differs from a non-NULL pointer. -*/ -SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){ - int i; - if( pA==0 && pB==0 ) return 0; - if( pA==0 || pB==0 ) return 1; - if( pA->nExpr!=pB->nExpr ) return 1; - for(i=0; inExpr; i++){ - Expr *pExprA = pA->a[i].pExpr; - Expr *pExprB = pB->a[i].pExpr; - if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1; - if( sqlite3ExprCompare(pExprA, pExprB) ) return 1; - } - return 0; -} - -/* -** An instance of the following structure is used by the tree walker -** to count references to table columns in the arguments of an -** aggregate function, in order to implement the -** sqlite3FunctionThisSrc() routine. -*/ -struct SrcCount { - SrcList *pSrc; /* One particular FROM clause in a nested query */ - int nThis; /* Number of references to columns in pSrcList */ - int nOther; /* Number of references to columns in other FROM clauses */ -}; - -/* -** Count the number of references to columns. -*/ -static int exprSrcCount(Walker *pWalker, Expr *pExpr){ - /* The NEVER() on the second term is because sqlite3FunctionUsesThisSrc() - ** is always called before sqlite3ExprAnalyzeAggregates() and so the - ** TK_COLUMNs have not yet been converted into TK_AGG_COLUMN. If - ** sqlite3FunctionUsesThisSrc() is used differently in the future, the - ** NEVER() will need to be removed. */ - if( pExpr->op==TK_COLUMN || NEVER(pExpr->op==TK_AGG_COLUMN) ){ - int i; - struct SrcCount *p = pWalker->u.pSrcCount; - SrcList *pSrc = p->pSrc; - for(i=0; inSrc; i++){ - if( pExpr->iTable==pSrc->a[i].iCursor ) break; - } - if( inSrc ){ - p->nThis++; - }else{ - p->nOther++; - } - } - return WRC_Continue; -} - -/* -** Determine if any of the arguments to the pExpr Function reference -** pSrcList. Return true if they do. Also return true if the function -** has no arguments or has only constant arguments. Return false if pExpr -** references columns but not columns of tables found in pSrcList. -*/ -SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ - Walker w; - struct SrcCount cnt; - assert( pExpr->op==TK_AGG_FUNCTION ); - memset(&w, 0, sizeof(w)); - w.xExprCallback = exprSrcCount; - w.u.pSrcCount = &cnt; - cnt.pSrc = pSrcList; - cnt.nThis = 0; - cnt.nOther = 0; - sqlite3WalkExprList(&w, pExpr->x.pList); - return cnt.nThis>0 || cnt.nOther==0; -} - -/* -** Add a new element to the pAggInfo->aCol[] array. Return the index of -** the new element. Return a negative number if malloc fails. -*/ -static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){ - int i; - pInfo->aCol = sqlite3ArrayAllocate( - db, - pInfo->aCol, - sizeof(pInfo->aCol[0]), - &pInfo->nColumn, - &i - ); - return i; -} - -/* -** Add a new element to the pAggInfo->aFunc[] array. Return the index of -** the new element. Return a negative number if malloc fails. -*/ -static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){ - int i; - pInfo->aFunc = sqlite3ArrayAllocate( - db, - pInfo->aFunc, - sizeof(pInfo->aFunc[0]), - &pInfo->nFunc, - &i - ); - return i; -} - -/* -** This is the xExprCallback for a tree walker. It is used to -** implement sqlite3ExprAnalyzeAggregates(). See sqlite3ExprAnalyzeAggregates -** for additional information. -*/ -static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ - int i; - NameContext *pNC = pWalker->u.pNC; - Parse *pParse = pNC->pParse; - SrcList *pSrcList = pNC->pSrcList; - AggInfo *pAggInfo = pNC->pAggInfo; - - switch( pExpr->op ){ - case TK_AGG_COLUMN: - case TK_COLUMN: { - testcase( pExpr->op==TK_AGG_COLUMN ); - testcase( pExpr->op==TK_COLUMN ); - /* Check to see if the column is in one of the tables in the FROM - ** clause of the aggregate query */ - if( ALWAYS(pSrcList!=0) ){ - struct SrcList_item *pItem = pSrcList->a; - for(i=0; inSrc; i++, pItem++){ - struct AggInfo_col *pCol; - assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) ); - if( pExpr->iTable==pItem->iCursor ){ - /* If we reach this point, it means that pExpr refers to a table - ** that is in the FROM clause of the aggregate query. - ** - ** Make an entry for the column in pAggInfo->aCol[] if there - ** is not an entry there already. - */ - int k; - pCol = pAggInfo->aCol; - for(k=0; knColumn; k++, pCol++){ - if( pCol->iTable==pExpr->iTable && - pCol->iColumn==pExpr->iColumn ){ - break; - } - } - if( (k>=pAggInfo->nColumn) - && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0 - ){ - pCol = &pAggInfo->aCol[k]; - pCol->pTab = pExpr->pTab; - pCol->iTable = pExpr->iTable; - pCol->iColumn = pExpr->iColumn; - pCol->iMem = ++pParse->nMem; - pCol->iSorterColumn = -1; - pCol->pExpr = pExpr; - if( pAggInfo->pGroupBy ){ - int j, n; - ExprList *pGB = pAggInfo->pGroupBy; - struct ExprList_item *pTerm = pGB->a; - n = pGB->nExpr; - for(j=0; jpExpr; - if( pE->op==TK_COLUMN && pE->iTable==pExpr->iTable && - pE->iColumn==pExpr->iColumn ){ - pCol->iSorterColumn = j; - break; - } - } - } - if( pCol->iSorterColumn<0 ){ - pCol->iSorterColumn = pAggInfo->nSortingColumn++; - } - } - /* There is now an entry for pExpr in pAggInfo->aCol[] (either - ** because it was there before or because we just created it). - ** Convert the pExpr to be a TK_AGG_COLUMN referring to that - ** pAggInfo->aCol[] entry. - */ - ExprSetIrreducible(pExpr); - pExpr->pAggInfo = pAggInfo; - pExpr->op = TK_AGG_COLUMN; - pExpr->iAgg = (i16)k; - break; - } /* endif pExpr->iTable==pItem->iCursor */ - } /* end loop over pSrcList */ - } - return WRC_Prune; - } - case TK_AGG_FUNCTION: { - if( (pNC->ncFlags & NC_InAggFunc)==0 - && pWalker->walkerDepth==pExpr->op2 - ){ - /* Check to see if pExpr is a duplicate of another aggregate - ** function that is already in the pAggInfo structure - */ - struct AggInfo_func *pItem = pAggInfo->aFunc; - for(i=0; inFunc; i++, pItem++){ - if( sqlite3ExprCompare(pItem->pExpr, pExpr)==0 ){ - break; - } - } - if( i>=pAggInfo->nFunc ){ - /* pExpr is original. Make a new entry in pAggInfo->aFunc[] - */ - u8 enc = ENC(pParse->db); - i = addAggInfoFunc(pParse->db, pAggInfo); - if( i>=0 ){ - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - pItem = &pAggInfo->aFunc[i]; - pItem->pExpr = pExpr; - pItem->iMem = ++pParse->nMem; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - pItem->pFunc = sqlite3FindFunction(pParse->db, - pExpr->u.zToken, sqlite3Strlen30(pExpr->u.zToken), - pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0); - if( pExpr->flags & EP_Distinct ){ - pItem->iDistinct = pParse->nTab++; - }else{ - pItem->iDistinct = -1; - } - } - } - /* Make pExpr point to the appropriate pAggInfo->aFunc[] entry - */ - assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) ); - ExprSetIrreducible(pExpr); - pExpr->iAgg = (i16)i; - pExpr->pAggInfo = pAggInfo; - return WRC_Prune; - }else{ - return WRC_Continue; - } - } - } - return WRC_Continue; -} -static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ - UNUSED_PARAMETER(pWalker); - UNUSED_PARAMETER(pSelect); - return WRC_Continue; -} - -/* -** Analyze the pExpr expression looking for aggregate functions and -** for variables that need to be added to AggInfo object that pNC->pAggInfo -** points to. Additional entries are made on the AggInfo object as -** necessary. -** -** This routine should only be called after the expression has been -** analyzed by sqlite3ResolveExprNames(). -*/ -SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){ - Walker w; - memset(&w, 0, sizeof(w)); - w.xExprCallback = analyzeAggregate; - w.xSelectCallback = analyzeAggregatesInSelect; - w.u.pNC = pNC; - assert( pNC->pSrcList!=0 ); - sqlite3WalkExpr(&w, pExpr); -} - -/* -** Call sqlite3ExprAnalyzeAggregates() for every expression in an -** expression list. Return the number of errors. -** -** If an error is found, the analysis is cut short. -*/ -SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext *pNC, ExprList *pList){ - struct ExprList_item *pItem; - int i; - if( pList ){ - for(pItem=pList->a, i=0; inExpr; i++, pItem++){ - sqlite3ExprAnalyzeAggregates(pNC, pItem->pExpr); - } - } -} - -/* -** Allocate a single new register for use to hold some intermediate result. -*/ -SQLITE_PRIVATE int sqlite3GetTempReg(Parse *pParse){ - if( pParse->nTempReg==0 ){ - return ++pParse->nMem; - } - return pParse->aTempReg[--pParse->nTempReg]; -} - -/* -** Deallocate a register, making available for reuse for some other -** purpose. -** -** If a register is currently being used by the column cache, then -** the dallocation is deferred until the column cache line that uses -** the register becomes stale. -*/ -SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse *pParse, int iReg){ - if( iReg && pParse->nTempRegaTempReg) ){ - int i; - struct yColCache *p; - for(i=0, p=pParse->aColCache; iiReg==iReg ){ - p->tempReg = 1; - return; - } - } - pParse->aTempReg[pParse->nTempReg++] = iReg; - } -} - -/* -** Allocate or deallocate a block of nReg consecutive registers -*/ -SQLITE_PRIVATE int sqlite3GetTempRange(Parse *pParse, int nReg){ - int i, n; - i = pParse->iRangeReg; - n = pParse->nRangeReg; - if( nReg<=n ){ - assert( !usedAsColumnCache(pParse, i, i+n-1) ); - pParse->iRangeReg += nReg; - pParse->nRangeReg -= nReg; - }else{ - i = pParse->nMem+1; - pParse->nMem += nReg; - } - return i; -} -SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){ - sqlite3ExprCacheRemove(pParse, iReg, nReg); - if( nReg>pParse->nRangeReg ){ - pParse->nRangeReg = nReg; - pParse->iRangeReg = iReg; - } -} - -/* -** Mark all temporary registers as being unavailable for reuse. -*/ -SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){ - pParse->nTempReg = 0; - pParse->nRangeReg = 0; -} - -/************** End of expr.c ************************************************/ -/************** Begin file alter.c *******************************************/ -/* -** 2005 February 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that used to generate VDBE code -** that implements the ALTER TABLE command. -*/ - -/* -** The code in this file only exists if we are not omitting the -** ALTER TABLE logic from the build. -*/ -#ifndef SQLITE_OMIT_ALTERTABLE - - -/* -** This function is used by SQL generated to implement the -** ALTER TABLE command. The first argument is the text of a CREATE TABLE or -** CREATE INDEX command. The second is a table name. The table name in -** the CREATE TABLE or CREATE INDEX statement is replaced with the third -** argument and the result returned. Examples: -** -** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def') -** -> 'CREATE TABLE def(a, b, c)' -** -** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def') -** -> 'CREATE INDEX i ON def(a, b, c)' -*/ -static void renameTableFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - unsigned char const *zSql = sqlite3_value_text(argv[0]); - unsigned char const *zTableName = sqlite3_value_text(argv[1]); - - int token; - Token tname; - unsigned char const *zCsr = zSql; - int len = 0; - char *zRet; - - sqlite3 *db = sqlite3_context_db_handle(context); - - UNUSED_PARAMETER(NotUsed); - - /* The principle used to locate the table name in the CREATE TABLE - ** statement is that the table name is the first non-space token that - ** is immediately followed by a TK_LP or TK_USING token. - */ - if( zSql ){ - do { - if( !*zCsr ){ - /* Ran out of input before finding an opening bracket. Return NULL. */ - return; - } - - /* Store the token that zCsr points to in tname. */ - tname.z = (char*)zCsr; - tname.n = len; - - /* Advance zCsr to the next token. Store that token type in 'token', - ** and its length in 'len' (to be used next iteration of this loop). - */ - do { - zCsr += len; - len = sqlite3GetToken(zCsr, &token); - } while( token==TK_SPACE ); - assert( len>0 ); - } while( token!=TK_LP && token!=TK_USING ); - - zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, - zTableName, tname.z+tname.n); - sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC); - } -} - -/* -** This C function implements an SQL user function that is used by SQL code -** generated by the ALTER TABLE ... RENAME command to modify the definition -** of any foreign key constraints that use the table being renamed as the -** parent table. It is passed three arguments: -** -** 1) The complete text of the CREATE TABLE statement being modified, -** 2) The old name of the table being renamed, and -** 3) The new name of the table being renamed. -** -** It returns the new CREATE TABLE statement. For example: -** -** sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3') -** -> 'CREATE TABLE t1(a REFERENCES t3)' -*/ -#ifndef SQLITE_OMIT_FOREIGN_KEY -static void renameParentFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - sqlite3 *db = sqlite3_context_db_handle(context); - char *zOutput = 0; - char *zResult; - unsigned char const *zInput = sqlite3_value_text(argv[0]); - unsigned char const *zOld = sqlite3_value_text(argv[1]); - unsigned char const *zNew = sqlite3_value_text(argv[2]); - - unsigned const char *z; /* Pointer to token */ - int n; /* Length of token z */ - int token; /* Type of token */ - - UNUSED_PARAMETER(NotUsed); - for(z=zInput; *z; z=z+n){ - n = sqlite3GetToken(z, &token); - if( token==TK_REFERENCES ){ - char *zParent; - do { - z += n; - n = sqlite3GetToken(z, &token); - }while( token==TK_SPACE ); - - zParent = sqlite3DbStrNDup(db, (const char *)z, n); - if( zParent==0 ) break; - sqlite3Dequote(zParent); - if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){ - char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"", - (zOutput?zOutput:""), z-zInput, zInput, (const char *)zNew - ); - sqlite3DbFree(db, zOutput); - zOutput = zOut; - zInput = &z[n]; - } - sqlite3DbFree(db, zParent); - } - } - - zResult = sqlite3MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput), - sqlite3_result_text(context, zResult, -1, SQLITE_DYNAMIC); - sqlite3DbFree(db, zOutput); -} -#endif - -#ifndef SQLITE_OMIT_TRIGGER -/* This function is used by SQL generated to implement the -** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER -** statement. The second is a table name. The table name in the CREATE -** TRIGGER statement is replaced with the third argument and the result -** returned. This is analagous to renameTableFunc() above, except for CREATE -** TRIGGER, not CREATE INDEX and CREATE TABLE. -*/ -static void renameTriggerFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - unsigned char const *zSql = sqlite3_value_text(argv[0]); - unsigned char const *zTableName = sqlite3_value_text(argv[1]); - - int token; - Token tname; - int dist = 3; - unsigned char const *zCsr = zSql; - int len = 0; - char *zRet; - sqlite3 *db = sqlite3_context_db_handle(context); - - UNUSED_PARAMETER(NotUsed); - - /* The principle used to locate the table name in the CREATE TRIGGER - ** statement is that the table name is the first token that is immediatedly - ** preceded by either TK_ON or TK_DOT and immediatedly followed by one - ** of TK_WHEN, TK_BEGIN or TK_FOR. - */ - if( zSql ){ - do { - - if( !*zCsr ){ - /* Ran out of input before finding the table name. Return NULL. */ - return; - } - - /* Store the token that zCsr points to in tname. */ - tname.z = (char*)zCsr; - tname.n = len; - - /* Advance zCsr to the next token. Store that token type in 'token', - ** and its length in 'len' (to be used next iteration of this loop). - */ - do { - zCsr += len; - len = sqlite3GetToken(zCsr, &token); - }while( token==TK_SPACE ); - assert( len>0 ); - - /* Variable 'dist' stores the number of tokens read since the most - ** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN - ** token is read and 'dist' equals 2, the condition stated above - ** to be met. - ** - ** Note that ON cannot be a database, table or column name, so - ** there is no need to worry about syntax like - ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc. - */ - dist++; - if( token==TK_DOT || token==TK_ON ){ - dist = 0; - } - } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) ); - - /* Variable tname now contains the token that is the old table-name - ** in the CREATE TRIGGER statement. - */ - zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, - zTableName, tname.z+tname.n); - sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC); - } -} -#endif /* !SQLITE_OMIT_TRIGGER */ - -/* -** Register built-in functions used to help implement ALTER TABLE -*/ -SQLITE_PRIVATE void sqlite3AlterFunctions(void){ - static SQLITE_WSD FuncDef aAlterTableFuncs[] = { - FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), -#ifndef SQLITE_OMIT_TRIGGER - FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc), -#endif -#ifndef SQLITE_OMIT_FOREIGN_KEY - FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc), -#endif - }; - int i; - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); - FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAlterTableFuncs); - - for(i=0; i OR name= OR ... -** -** If argument zWhere is NULL, then a pointer string containing the text -** "name=" is returned, where is the quoted version -** of the string passed as argument zConstant. The returned buffer is -** allocated using sqlite3DbMalloc(). It is the responsibility of the -** caller to ensure that it is eventually freed. -** -** If argument zWhere is not NULL, then the string returned is -** " OR name=", where is the contents of zWhere. -** In this case zWhere is passed to sqlite3DbFree() before returning. -** -*/ -static char *whereOrName(sqlite3 *db, char *zWhere, char *zConstant){ - char *zNew; - if( !zWhere ){ - zNew = sqlite3MPrintf(db, "name=%Q", zConstant); - }else{ - zNew = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, zConstant); - sqlite3DbFree(db, zWhere); - } - return zNew; -} - -#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) -/* -** Generate the text of a WHERE expression which can be used to select all -** tables that have foreign key constraints that refer to table pTab (i.e. -** constraints for which pTab is the parent table) from the sqlite_master -** table. -*/ -static char *whereForeignKeys(Parse *pParse, Table *pTab){ - FKey *p; - char *zWhere = 0; - for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ - zWhere = whereOrName(pParse->db, zWhere, p->pFrom->zName); - } - return zWhere; -} -#endif - -/* -** Generate the text of a WHERE expression which can be used to select all -** temporary triggers on table pTab from the sqlite_temp_master table. If -** table pTab has no temporary triggers, or is itself stored in the -** temporary database, NULL is returned. -*/ -static char *whereTempTriggers(Parse *pParse, Table *pTab){ - Trigger *pTrig; - char *zWhere = 0; - const Schema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */ - - /* If the table is not located in the temp-db (in which case NULL is - ** returned, loop through the tables list of triggers. For each trigger - ** that is not part of the temp-db schema, add a clause to the WHERE - ** expression being built up in zWhere. - */ - if( pTab->pSchema!=pTempSchema ){ - sqlite3 *db = pParse->db; - for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ - if( pTrig->pSchema==pTempSchema ){ - zWhere = whereOrName(db, zWhere, pTrig->zName); - } - } - } - if( zWhere ){ - char *zNew = sqlite3MPrintf(pParse->db, "type='trigger' AND (%s)", zWhere); - sqlite3DbFree(pParse->db, zWhere); - zWhere = zNew; - } - return zWhere; -} - -/* -** Generate code to drop and reload the internal representation of table -** pTab from the database, including triggers and temporary triggers. -** Argument zName is the name of the table in the database schema at -** the time the generated code is executed. This can be different from -** pTab->zName if this function is being called to code part of an -** "ALTER TABLE RENAME TO" statement. -*/ -static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ - Vdbe *v; - char *zWhere; - int iDb; /* Index of database containing pTab */ -#ifndef SQLITE_OMIT_TRIGGER - Trigger *pTrig; -#endif - - v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return; - assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - assert( iDb>=0 ); - -#ifndef SQLITE_OMIT_TRIGGER - /* Drop any table triggers from the internal schema. */ - for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ - int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); - assert( iTrigDb==iDb || iTrigDb==1 ); - sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->zName, 0); - } -#endif - - /* Drop the table and index from the internal schema. */ - sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); - - /* Reload the table, index and permanent trigger schemas. */ - zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName); - if( !zWhere ) return; - sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); - -#ifndef SQLITE_OMIT_TRIGGER - /* Now, if the table is not stored in the temp database, reload any temp - ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined. - */ - if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){ - sqlite3VdbeAddParseSchemaOp(v, 1, zWhere); - } -#endif -} - -/* -** Parameter zName is the name of a table that is about to be altered -** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). -** If the table is a system table, this function leaves an error message -** in pParse->zErr (system tables may not be altered) and returns non-zero. -** -** Or, if zName is not a system table, zero is returned. -*/ -static int isSystemTable(Parse *pParse, const char *zName){ - if( sqlite3Strlen30(zName)>6 && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ - sqlite3ErrorMsg(pParse, "table %s may not be altered", zName); - return 1; - } - return 0; -} - -/* -** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" -** command. -*/ -SQLITE_PRIVATE void sqlite3AlterRenameTable( - Parse *pParse, /* Parser context. */ - SrcList *pSrc, /* The table to rename. */ - Token *pName /* The new table name. */ -){ - int iDb; /* Database that contains the table */ - char *zDb; /* Name of database iDb */ - Table *pTab; /* Table being renamed */ - char *zName = 0; /* NULL-terminated version of pName */ - sqlite3 *db = pParse->db; /* Database connection */ - int nTabName; /* Number of UTF-8 characters in zTabName */ - const char *zTabName; /* Original name of the table */ - Vdbe *v; -#ifndef SQLITE_OMIT_TRIGGER - char *zWhere = 0; /* Where clause to locate temp triggers */ -#endif - VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ - int savedDbFlags; /* Saved value of db->flags */ - - savedDbFlags = db->flags; - if( NEVER(db->mallocFailed) ) goto exit_rename_table; - assert( pSrc->nSrc==1 ); - assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); - - pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); - if( !pTab ) goto exit_rename_table; - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - zDb = db->aDb[iDb].zName; - db->flags |= SQLITE_PreferBuiltin; - - /* Get a NULL terminated version of the new table name. */ - zName = sqlite3NameFromToken(db, pName); - if( !zName ) goto exit_rename_table; - - /* Check that a table or index named 'zName' does not already exist - ** in database iDb. If so, this is an error. - */ - if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){ - sqlite3ErrorMsg(pParse, - "there is already another table or index with this name: %s", zName); - goto exit_rename_table; - } - - /* Make sure it is not a system table being altered, or a reserved name - ** that the table is being renamed to. - */ - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ - goto exit_rename_table; - } - if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto - exit_rename_table; - } - -#ifndef SQLITE_OMIT_VIEW - if( pTab->pSelect ){ - sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName); - goto exit_rename_table; - } -#endif - -#ifndef SQLITE_OMIT_AUTHORIZATION - /* Invoke the authorization callback. */ - if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ - goto exit_rename_table; - } -#endif - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto exit_rename_table; - } - if( IsVirtual(pTab) ){ - pVTab = sqlite3GetVTable(db, pTab); - if( pVTab->pVtab->pModule->xRename==0 ){ - pVTab = 0; - } - } -#endif - - /* Begin a transaction and code the VerifyCookie for database iDb. - ** Then modify the schema cookie (since the ALTER TABLE modifies the - ** schema). Open a statement transaction if the table is a virtual - ** table. - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ){ - goto exit_rename_table; - } - sqlite3BeginWriteOperation(pParse, pVTab!=0, iDb); - sqlite3ChangeCookie(pParse, iDb); - - /* If this is a virtual table, invoke the xRename() function if - ** one is defined. The xRename() callback will modify the names - ** of any resources used by the v-table implementation (including other - ** SQLite tables) that are identified by the name of the virtual table. - */ -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( pVTab ){ - int i = ++pParse->nMem; - sqlite3VdbeAddOp4(v, OP_String8, 0, i, 0, zName, 0); - sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); - sqlite3MayAbort(pParse); - } -#endif - - /* figure out how many UTF-8 characters are in zName */ - zTabName = pTab->zName; - nTabName = sqlite3Utf8CharLen(zTabName, -1); - -#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) - if( db->flags&SQLITE_ForeignKeys ){ - /* If foreign-key support is enabled, rewrite the CREATE TABLE - ** statements corresponding to all child tables of foreign key constraints - ** for which the renamed table is the parent table. */ - if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){ - sqlite3NestedParse(pParse, - "UPDATE \"%w\".%s SET " - "sql = sqlite_rename_parent(sql, %Q, %Q) " - "WHERE %s;", zDb, SCHEMA_TABLE(iDb), zTabName, zName, zWhere); - sqlite3DbFree(db, zWhere); - } - } -#endif - - /* Modify the sqlite_master table to use the new table name. */ - sqlite3NestedParse(pParse, - "UPDATE %Q.%s SET " -#ifdef SQLITE_OMIT_TRIGGER - "sql = sqlite_rename_table(sql, %Q), " -#else - "sql = CASE " - "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" - "ELSE sqlite_rename_table(sql, %Q) END, " -#endif - "tbl_name = %Q, " - "name = CASE " - "WHEN type='table' THEN %Q " - "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " - "'sqlite_autoindex_' || %Q || substr(name,%d+18) " - "ELSE name END " - "WHERE tbl_name=%Q COLLATE nocase AND " - "(type='table' OR type='index' OR type='trigger');", - zDb, SCHEMA_TABLE(iDb), zName, zName, zName, -#ifndef SQLITE_OMIT_TRIGGER - zName, -#endif - zName, nTabName, zTabName - ); - -#ifndef SQLITE_OMIT_AUTOINCREMENT - /* If the sqlite_sequence table exists in this database, then update - ** it with the new table name. - */ - if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){ - sqlite3NestedParse(pParse, - "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q", - zDb, zName, pTab->zName); - } -#endif - -#ifndef SQLITE_OMIT_TRIGGER - /* If there are TEMP triggers on this table, modify the sqlite_temp_master - ** table. Don't do this if the table being ALTERed is itself located in - ** the temp database. - */ - if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){ - sqlite3NestedParse(pParse, - "UPDATE sqlite_temp_master SET " - "sql = sqlite_rename_trigger(sql, %Q), " - "tbl_name = %Q " - "WHERE %s;", zName, zName, zWhere); - sqlite3DbFree(db, zWhere); - } -#endif - -#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) - if( db->flags&SQLITE_ForeignKeys ){ - FKey *p; - for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ - Table *pFrom = p->pFrom; - if( pFrom!=pTab ){ - reloadTableSchema(pParse, p->pFrom, pFrom->zName); - } - } - } -#endif - - /* Drop and reload the internal table schema. */ - reloadTableSchema(pParse, pTab, zName); - -exit_rename_table: - sqlite3SrcListDelete(db, pSrc); - sqlite3DbFree(db, zName); - db->flags = savedDbFlags; -} - - -/* -** Generate code to make sure the file format number is at least minFormat. -** The generated code will increase the file format number if necessary. -*/ -SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){ - Vdbe *v; - v = sqlite3GetVdbe(pParse); - /* The VDBE should have been allocated before this routine is called. - ** If that allocation failed, we would have quit before reaching this - ** point */ - if( ALWAYS(v) ){ - int r1 = sqlite3GetTempReg(pParse); - int r2 = sqlite3GetTempReg(pParse); - int j1; - sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); - sqlite3VdbeUsesBtree(v, iDb); - sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2); - j1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1); - sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2); - sqlite3VdbeJumpHere(v, j1); - sqlite3ReleaseTempReg(pParse, r1); - sqlite3ReleaseTempReg(pParse, r2); - } -} - -/* -** This function is called after an "ALTER TABLE ... ADD" statement -** has been parsed. Argument pColDef contains the text of the new -** column definition. -** -** The Table structure pParse->pNewTable was extended to include -** the new column during parsing. -*/ -SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ - Table *pNew; /* Copy of pParse->pNewTable */ - Table *pTab; /* Table being altered */ - int iDb; /* Database number */ - const char *zDb; /* Database name */ - const char *zTab; /* Table name */ - char *zCol; /* Null-terminated column definition */ - Column *pCol; /* The new column */ - Expr *pDflt; /* Default value for the new column */ - sqlite3 *db; /* The database connection; */ - - db = pParse->db; - if( pParse->nErr || db->mallocFailed ) return; - pNew = pParse->pNewTable; - assert( pNew ); - - assert( sqlite3BtreeHoldsAllMutexes(db) ); - iDb = sqlite3SchemaToIndex(db, pNew->pSchema); - zDb = db->aDb[iDb].zName; - zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */ - pCol = &pNew->aCol[pNew->nCol-1]; - pDflt = pCol->pDflt; - pTab = sqlite3FindTable(db, zTab, zDb); - assert( pTab ); - -#ifndef SQLITE_OMIT_AUTHORIZATION - /* Invoke the authorization callback. */ - if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ - return; - } -#endif - - /* If the default value for the new column was specified with a - ** literal NULL, then set pDflt to 0. This simplifies checking - ** for an SQL NULL default below. - */ - if( pDflt && pDflt->op==TK_NULL ){ - pDflt = 0; - } - - /* Check that the new column is not specified as PRIMARY KEY or UNIQUE. - ** If there is a NOT NULL constraint, then the default value for the - ** column must not be NULL. - */ - if( pCol->colFlags & COLFLAG_PRIMKEY ){ - sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column"); - return; - } - if( pNew->pIndex ){ - sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column"); - return; - } - if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ - sqlite3ErrorMsg(pParse, - "Cannot add a REFERENCES column with non-NULL default value"); - return; - } - if( pCol->notNull && !pDflt ){ - sqlite3ErrorMsg(pParse, - "Cannot add a NOT NULL column with default value NULL"); - return; - } - - /* Ensure the default expression is something that sqlite3ValueFromExpr() - ** can handle (i.e. not CURRENT_TIME etc.) - */ - if( pDflt ){ - sqlite3_value *pVal; - if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){ - db->mallocFailed = 1; - return; - } - if( !pVal ){ - sqlite3ErrorMsg(pParse, "Cannot add a column with non-constant default"); - return; - } - sqlite3ValueFree(pVal); - } - - /* Modify the CREATE TABLE statement. */ - zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); - if( zCol ){ - char *zEnd = &zCol[pColDef->n-1]; - int savedDbFlags = db->flags; - while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ - *zEnd-- = '\0'; - } - db->flags |= SQLITE_PreferBuiltin; - sqlite3NestedParse(pParse, - "UPDATE \"%w\".%s SET " - "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " - "WHERE type = 'table' AND name = %Q", - zDb, SCHEMA_TABLE(iDb), pNew->addColOffset, zCol, pNew->addColOffset+1, - zTab - ); - sqlite3DbFree(db, zCol); - db->flags = savedDbFlags; - } - - /* If the default value of the new column is NULL, then set the file - ** format to 2. If the default value of the new column is not NULL, - ** the file format becomes 3. - */ - sqlite3MinimumFileFormat(pParse, iDb, pDflt ? 3 : 2); - - /* Reload the schema of the modified table. */ - reloadTableSchema(pParse, pTab, pTab->zName); -} - -/* -** This function is called by the parser after the table-name in -** an "ALTER TABLE ADD" statement is parsed. Argument -** pSrc is the full-name of the table being altered. -** -** This routine makes a (partial) copy of the Table structure -** for the table being altered and sets Parse.pNewTable to point -** to it. Routines called by the parser as the column definition -** is parsed (i.e. sqlite3AddColumn()) add the new Column data to -** the copy. The copy of the Table structure is deleted by tokenize.c -** after parsing is finished. -** -** Routine sqlite3AlterFinishAddColumn() will be called to complete -** coding the "ALTER TABLE ... ADD" statement. -*/ -SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ - Table *pNew; - Table *pTab; - Vdbe *v; - int iDb; - int i; - int nAlloc; - sqlite3 *db = pParse->db; - - /* Look up the table being altered. */ - assert( pParse->pNewTable==0 ); - assert( sqlite3BtreeHoldsAllMutexes(db) ); - if( db->mallocFailed ) goto exit_begin_add_column; - pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); - if( !pTab ) goto exit_begin_add_column; - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) ){ - sqlite3ErrorMsg(pParse, "virtual tables may not be altered"); - goto exit_begin_add_column; - } -#endif - - /* Make sure this is not an attempt to ALTER a view. */ - if( pTab->pSelect ){ - sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); - goto exit_begin_add_column; - } - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ - goto exit_begin_add_column; - } - - assert( pTab->addColOffset>0 ); - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - - /* Put a copy of the Table struct in Parse.pNewTable for the - ** sqlite3AddColumn() function and friends to modify. But modify - ** the name by adding an "sqlite_altertab_" prefix. By adding this - ** prefix, we insure that the name will not collide with an existing - ** table because user table are not allowed to have the "sqlite_" - ** prefix on their name. - */ - pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table)); - if( !pNew ) goto exit_begin_add_column; - pParse->pNewTable = pNew; - pNew->nRef = 1; - pNew->nCol = pTab->nCol; - assert( pNew->nCol>0 ); - nAlloc = (((pNew->nCol-1)/8)*8)+8; - assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 ); - pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc); - pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); - if( !pNew->aCol || !pNew->zName ){ - db->mallocFailed = 1; - goto exit_begin_add_column; - } - memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); - for(i=0; inCol; i++){ - Column *pCol = &pNew->aCol[i]; - pCol->zName = sqlite3DbStrDup(db, pCol->zName); - pCol->zColl = 0; - pCol->zType = 0; - pCol->pDflt = 0; - pCol->zDflt = 0; - } - pNew->pSchema = db->aDb[iDb].pSchema; - pNew->addColOffset = pTab->addColOffset; - pNew->nRef = 1; - - /* Begin a transaction and increment the schema cookie. */ - sqlite3BeginWriteOperation(pParse, 0, iDb); - v = sqlite3GetVdbe(pParse); - if( !v ) goto exit_begin_add_column; - sqlite3ChangeCookie(pParse, iDb); - -exit_begin_add_column: - sqlite3SrcListDelete(db, pSrc); - return; -} -#endif /* SQLITE_ALTER_TABLE */ - -/************** End of alter.c ***********************************************/ -/************** Begin file analyze.c *****************************************/ -/* -** 2005 July 8 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code associated with the ANALYZE command. -** -** The ANALYZE command gather statistics about the content of tables -** and indices. These statistics are made available to the query planner -** to help it make better decisions about how to perform queries. -** -** The following system tables are or have been supported: -** -** CREATE TABLE sqlite_stat1(tbl, idx, stat); -** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample); -** CREATE TABLE sqlite_stat3(tbl, idx, nEq, nLt, nDLt, sample); -** -** Additional tables might be added in future releases of SQLite. -** The sqlite_stat2 table is not created or used unless the SQLite version -** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled -** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated. -** The sqlite_stat2 table is superceded by sqlite_stat3, which is only -** created and used by SQLite versions 3.7.9 and later and with -** SQLITE_ENABLE_STAT3 defined. The fucntionality of sqlite_stat3 -** is a superset of sqlite_stat2. -** -** Format of sqlite_stat1: -** -** There is normally one row per index, with the index identified by the -** name in the idx column. The tbl column is the name of the table to -** which the index belongs. In each such row, the stat column will be -** a string consisting of a list of integers. The first integer in this -** list is the number of rows in the index and in the table. The second -** integer is the average number of rows in the index that have the same -** value in the first column of the index. The third integer is the average -** number of rows in the index that have the same value for the first two -** columns. The N-th integer (for N>1) is the average number of rows in -** the index which have the same value for the first N-1 columns. For -** a K-column index, there will be K+1 integers in the stat column. If -** the index is unique, then the last integer will be 1. -** -** The list of integers in the stat column can optionally be followed -** by the keyword "unordered". The "unordered" keyword, if it is present, -** must be separated from the last integer by a single space. If the -** "unordered" keyword is present, then the query planner assumes that -** the index is unordered and will not use the index for a range query. -** -** If the sqlite_stat1.idx column is NULL, then the sqlite_stat1.stat -** column contains a single integer which is the (estimated) number of -** rows in the table identified by sqlite_stat1.tbl. -** -** Format of sqlite_stat2: -** -** The sqlite_stat2 is only created and is only used if SQLite is compiled -** with SQLITE_ENABLE_STAT2 and if the SQLite version number is between -** 3.6.18 and 3.7.8. The "stat2" table contains additional information -** about the distribution of keys within an index. The index is identified by -** the "idx" column and the "tbl" column is the name of the table to which -** the index belongs. There are usually 10 rows in the sqlite_stat2 -** table for each index. -** -** The sqlite_stat2 entries for an index that have sampleno between 0 and 9 -** inclusive are samples of the left-most key value in the index taken at -** evenly spaced points along the index. Let the number of samples be S -** (10 in the standard build) and let C be the number of rows in the index. -** Then the sampled rows are given by: -** -** rownumber = (i*C*2 + C)/(S*2) -** -** For i between 0 and S-1. Conceptually, the index space is divided into -** S uniform buckets and the samples are the middle row from each bucket. -** -** The format for sqlite_stat2 is recorded here for legacy reference. This -** version of SQLite does not support sqlite_stat2. It neither reads nor -** writes the sqlite_stat2 table. This version of SQLite only supports -** sqlite_stat3. -** -** Format for sqlite_stat3: -** -** The sqlite_stat3 is an enhancement to sqlite_stat2. A new name is -** used to avoid compatibility problems. -** -** The format of the sqlite_stat3 table is similar to the format of -** the sqlite_stat2 table. There are multiple entries for each index. -** The idx column names the index and the tbl column is the table of the -** index. If the idx and tbl columns are the same, then the sample is -** of the INTEGER PRIMARY KEY. The sample column is a value taken from -** the left-most column of the index. The nEq column is the approximate -** number of entires in the index whose left-most column exactly matches -** the sample. nLt is the approximate number of entires whose left-most -** column is less than the sample. The nDLt column is the approximate -** number of distinct left-most entries in the index that are less than -** the sample. -** -** Future versions of SQLite might change to store a string containing -** multiple integers values in the nDLt column of sqlite_stat3. The first -** integer will be the number of prior index entires that are distinct in -** the left-most column. The second integer will be the number of prior index -** entries that are distinct in the first two columns. The third integer -** will be the number of prior index entries that are distinct in the first -** three columns. And so forth. With that extension, the nDLt field is -** similar in function to the sqlite_stat1.stat field. -** -** There can be an arbitrary number of sqlite_stat3 entries per index. -** The ANALYZE command will typically generate sqlite_stat3 tables -** that contain between 10 and 40 samples which are distributed across -** the key space, though not uniformly, and which include samples with -** largest possible nEq values. -*/ -#ifndef SQLITE_OMIT_ANALYZE - -/* -** This routine generates code that opens the sqlite_stat1 table for -** writing with cursor iStatCur. If the library was built with the -** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is -** opened for writing using cursor (iStatCur+1) -** -** If the sqlite_stat1 tables does not previously exist, it is created. -** Similarly, if the sqlite_stat3 table does not exist and the library -** is compiled with SQLITE_ENABLE_STAT3 defined, it is created. -** -** Argument zWhere may be a pointer to a buffer containing a table name, -** or it may be a NULL pointer. If it is not NULL, then all entries in -** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated -** with the named table are deleted. If zWhere==0, then code is generated -** to delete all stat table entries. -*/ -static void openStatTable( - Parse *pParse, /* Parsing context */ - int iDb, /* The database we are looking in */ - int iStatCur, /* Open the sqlite_stat1 table on this cursor */ - const char *zWhere, /* Delete entries for this table or index */ - const char *zWhereType /* Either "tbl" or "idx" */ -){ - static const struct { - const char *zName; - const char *zCols; - } aTable[] = { - { "sqlite_stat1", "tbl,idx,stat" }, -#ifdef SQLITE_ENABLE_STAT3 - { "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" }, -#endif - }; - - int aRoot[] = {0, 0}; - u8 aCreateTbl[] = {0, 0}; - - int i; - sqlite3 *db = pParse->db; - Db *pDb; - Vdbe *v = sqlite3GetVdbe(pParse); - if( v==0 ) return; - assert( sqlite3BtreeHoldsAllMutexes(db) ); - assert( sqlite3VdbeDb(v)==db ); - pDb = &db->aDb[iDb]; - - /* Create new statistic tables if they do not exist, or clear them - ** if they do already exist. - */ - for(i=0; izName))==0 ){ - /* The sqlite_stat[12] table does not exist. Create it. Note that a - ** side-effect of the CREATE TABLE statement is to leave the rootpage - ** of the new table in register pParse->regRoot. This is important - ** because the OpenWrite opcode below will be needing it. */ - sqlite3NestedParse(pParse, - "CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols - ); - aRoot[i] = pParse->regRoot; - aCreateTbl[i] = OPFLAG_P2ISREG; - }else{ - /* The table already exists. If zWhere is not NULL, delete all entries - ** associated with the table zWhere. If zWhere is NULL, delete the - ** entire contents of the table. */ - aRoot[i] = pStat->tnum; - sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); - if( zWhere ){ - sqlite3NestedParse(pParse, - "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere - ); - }else{ - /* The sqlite_stat[12] table already exists. Delete all rows. */ - sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb); - } - } - } - - /* Open the sqlite_stat[13] tables for writing. */ - for(i=0; ia[0])*mxSample; - p = sqlite3MallocZero( n ); - if( p==0 ){ - sqlite3_result_error_nomem(context); - return; - } - p->a = (struct Stat3Sample*)&p[1]; - p->nRow = nRow; - p->mxSample = mxSample; - p->nPSample = p->nRow/(mxSample/3+1) + 1; - sqlite3_randomness(sizeof(p->iPrn), &p->iPrn); - sqlite3_result_blob(context, p, sizeof(p), sqlite3_free); -} -static const FuncDef stat3InitFuncdef = { - 2, /* nArg */ - SQLITE_UTF8, /* iPrefEnc */ - 0, /* flags */ - 0, /* pUserData */ - 0, /* pNext */ - stat3Init, /* xFunc */ - 0, /* xStep */ - 0, /* xFinalize */ - "stat3_init", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ -}; - - -/* -** Implementation of the stat3_push(nEq,nLt,nDLt,rowid,P) SQL function. The -** arguments describe a single key instance. This routine makes the -** decision about whether or not to retain this key for the sqlite_stat3 -** table. -** -** The return value is NULL. -*/ -static void stat3Push( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[4]); - tRowcnt nEq = sqlite3_value_int64(argv[0]); - tRowcnt nLt = sqlite3_value_int64(argv[1]); - tRowcnt nDLt = sqlite3_value_int64(argv[2]); - i64 rowid = sqlite3_value_int64(argv[3]); - u8 isPSample = 0; - u8 doInsert = 0; - int iMin = p->iMin; - struct Stat3Sample *pSample; - int i; - u32 h; - - UNUSED_PARAMETER(context); - UNUSED_PARAMETER(argc); - if( nEq==0 ) return; - h = p->iPrn = p->iPrn*1103515245 + 12345; - if( (nLt/p->nPSample)!=((nEq+nLt)/p->nPSample) ){ - doInsert = isPSample = 1; - }else if( p->nSamplemxSample ){ - doInsert = 1; - }else{ - if( nEq>p->a[iMin].nEq || (nEq==p->a[iMin].nEq && h>p->a[iMin].iHash) ){ - doInsert = 1; - } - } - if( !doInsert ) return; - if( p->nSample==p->mxSample ){ - assert( p->nSample - iMin - 1 >= 0 ); - memmove(&p->a[iMin], &p->a[iMin+1], sizeof(p->a[0])*(p->nSample-iMin-1)); - pSample = &p->a[p->nSample-1]; - }else{ - pSample = &p->a[p->nSample++]; - } - pSample->iRowid = rowid; - pSample->nEq = nEq; - pSample->nLt = nLt; - pSample->nDLt = nDLt; - pSample->iHash = h; - pSample->isPSample = isPSample; - - /* Find the new minimum */ - if( p->nSample==p->mxSample ){ - pSample = p->a; - i = 0; - while( pSample->isPSample ){ - i++; - pSample++; - assert( inSample ); - } - nEq = pSample->nEq; - h = pSample->iHash; - iMin = i; - for(i++, pSample++; inSample; i++, pSample++){ - if( pSample->isPSample ) continue; - if( pSample->nEqnEq==nEq && pSample->iHashnEq; - h = pSample->iHash; - } - } - p->iMin = iMin; - } -} -static const FuncDef stat3PushFuncdef = { - 5, /* nArg */ - SQLITE_UTF8, /* iPrefEnc */ - 0, /* flags */ - 0, /* pUserData */ - 0, /* pNext */ - stat3Push, /* xFunc */ - 0, /* xStep */ - 0, /* xFinalize */ - "stat3_push", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ -}; - -/* -** Implementation of the stat3_get(P,N,...) SQL function. This routine is -** used to query the results. Content is returned for the Nth sqlite_stat3 -** row where N is between 0 and S-1 and S is the number of samples. The -** value returned depends on the number of arguments. -** -** argc==2 result: rowid -** argc==3 result: nEq -** argc==4 result: nLt -** argc==5 result: nDLt -*/ -static void stat3Get( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int n = sqlite3_value_int(argv[1]); - Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[0]); - - assert( p!=0 ); - if( p->nSample<=n ) return; - switch( argc ){ - case 2: sqlite3_result_int64(context, p->a[n].iRowid); break; - case 3: sqlite3_result_int64(context, p->a[n].nEq); break; - case 4: sqlite3_result_int64(context, p->a[n].nLt); break; - default: sqlite3_result_int64(context, p->a[n].nDLt); break; - } -} -static const FuncDef stat3GetFuncdef = { - -1, /* nArg */ - SQLITE_UTF8, /* iPrefEnc */ - 0, /* flags */ - 0, /* pUserData */ - 0, /* pNext */ - stat3Get, /* xFunc */ - 0, /* xStep */ - 0, /* xFinalize */ - "stat3_get", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ -}; -#endif /* SQLITE_ENABLE_STAT3 */ - - - - -/* -** Generate code to do an analysis of all indices associated with -** a single table. -*/ -static void analyzeOneTable( - Parse *pParse, /* Parser context */ - Table *pTab, /* Table whose indices are to be analyzed */ - Index *pOnlyIdx, /* If not NULL, only analyze this one index */ - int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */ - int iMem /* Available memory locations begin here */ -){ - sqlite3 *db = pParse->db; /* Database handle */ - Index *pIdx; /* An index to being analyzed */ - int iIdxCur; /* Cursor open on index being analyzed */ - Vdbe *v; /* The virtual machine being built up */ - int i; /* Loop counter */ - int topOfLoop; /* The top of the loop */ - int endOfLoop; /* The end of the loop */ - int jZeroRows = -1; /* Jump from here if number of rows is zero */ - int iDb; /* Index of database containing pTab */ - int regTabname = iMem++; /* Register containing table name */ - int regIdxname = iMem++; /* Register containing index name */ - int regStat1 = iMem++; /* The stat column of sqlite_stat1 */ -#ifdef SQLITE_ENABLE_STAT3 - int regNumEq = regStat1; /* Number of instances. Same as regStat1 */ - int regNumLt = iMem++; /* Number of keys less than regSample */ - int regNumDLt = iMem++; /* Number of distinct keys less than regSample */ - int regSample = iMem++; /* The next sample value */ - int regRowid = regSample; /* Rowid of a sample */ - int regAccum = iMem++; /* Register to hold Stat3Accum object */ - int regLoop = iMem++; /* Loop counter */ - int regCount = iMem++; /* Number of rows in the table or index */ - int regTemp1 = iMem++; /* Intermediate register */ - int regTemp2 = iMem++; /* Intermediate register */ - int once = 1; /* One-time initialization */ - int shortJump = 0; /* Instruction address */ - int iTabCur = pParse->nTab++; /* Table cursor */ -#endif - int regCol = iMem++; /* Content of a column in analyzed table */ - int regRec = iMem++; /* Register holding completed record */ - int regTemp = iMem++; /* Temporary use register */ - int regNewRowid = iMem++; /* Rowid for the inserted record */ - - - v = sqlite3GetVdbe(pParse); - if( v==0 || NEVER(pTab==0) ){ - return; - } - if( pTab->tnum==0 ){ - /* Do not gather statistics on views or virtual tables */ - return; - } - if( sqlite3_strnicmp(pTab->zName, "sqlite_", 7)==0 ){ - /* Do not gather statistics on system tables */ - return; - } - assert( sqlite3BtreeHoldsAllMutexes(db) ); - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - assert( iDb>=0 ); - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); -#ifndef SQLITE_OMIT_AUTHORIZATION - if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, - db->aDb[iDb].zName ) ){ - return; - } -#endif - - /* Establish a read-lock on the table at the shared-cache level. */ - sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - - iIdxCur = pParse->nTab++; - sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int nCol; - KeyInfo *pKey; - int addrIfNot = 0; /* address of OP_IfNot */ - int *aChngAddr; /* Array of jump instruction addresses */ - - if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; - VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName)); - nCol = pIdx->nColumn; - aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*nCol); - if( aChngAddr==0 ) continue; - pKey = sqlite3IndexKeyinfo(pParse, pIdx); - if( iMem+1+(nCol*2)>pParse->nMem ){ - pParse->nMem = iMem+1+(nCol*2); - } - - /* Open a cursor to the index to be analyzed. */ - assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); - sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb, - (char *)pKey, P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pIdx->zName)); - - /* Populate the register containing the index name. */ - sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); - -#ifdef SQLITE_ENABLE_STAT3 - if( once ){ - once = 0; - sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); - } - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount); - sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_STAT3_SAMPLES, regTemp1); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt); - sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt); - sqlite3VdbeAddOp3(v, OP_Null, 0, regSample, regAccum); - sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum, - (char*)&stat3InitFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 2); -#endif /* SQLITE_ENABLE_STAT3 */ - - /* The block of memory cells initialized here is used as follows. - ** - ** iMem: - ** The total number of rows in the table. - ** - ** iMem+1 .. iMem+nCol: - ** Number of distinct entries in index considering the - ** left-most N columns only, where N is between 1 and nCol, - ** inclusive. - ** - ** iMem+nCol+1 .. Mem+2*nCol: - ** Previous value of indexed columns, from left to right. - ** - ** Cells iMem through iMem+nCol are initialized to 0. The others are - ** initialized to contain an SQL NULL. - */ - for(i=0; i<=nCol; i++){ - sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i); - } - for(i=0; iazColl!=0 ); - assert( pIdx->azColl[i]!=0 ); - pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); - aChngAddr[i] = sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1, - (char*)pColl, P4_COLLSEQ); - sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); - VdbeComment((v, "jump if column %d changed", i)); -#ifdef SQLITE_ENABLE_STAT3 - if( i==0 ){ - sqlite3VdbeAddOp2(v, OP_AddImm, regNumEq, 1); - VdbeComment((v, "incr repeat count")); - } -#endif - } - sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop); - for(i=0; inColumn, regRowid); - sqlite3VdbeAddOp3(v, OP_Add, regNumEq, regNumLt, regNumLt); - sqlite3VdbeAddOp2(v, OP_AddImm, regNumDLt, 1); - sqlite3VdbeAddOp2(v, OP_Integer, 1, regNumEq); -#endif - } - sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1); - sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1); - } - sqlite3DbFree(db, aChngAddr); - - /* Always jump here after updating the iMem+1...iMem+1+nCol counters */ - sqlite3VdbeResolveLabel(v, endOfLoop); - - sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop); - sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); -#ifdef SQLITE_ENABLE_STAT3 - sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2, - (char*)&stat3PushFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 5); - sqlite3VdbeAddOp2(v, OP_Integer, -1, regLoop); - shortJump = - sqlite3VdbeAddOp2(v, OP_AddImm, regLoop, 1); - sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regTemp1, - (char*)&stat3GetFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 2); - sqlite3VdbeAddOp1(v, OP_IsNull, regTemp1); - sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, shortJump, regTemp1); - sqlite3VdbeAddOp3(v, OP_Column, iTabCur, pIdx->aiColumn[0], regSample); - sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[0], regSample); - sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumEq, - (char*)&stat3GetFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 3); - sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumLt, - (char*)&stat3GetFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 4); - sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumDLt, - (char*)&stat3GetFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 5); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regRec, "bbbbbb", 0); - sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid); - sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regNewRowid); - sqlite3VdbeAddOp2(v, OP_Goto, 0, shortJump); - sqlite3VdbeJumpHere(v, shortJump+2); -#endif - - /* Store the results in sqlite_stat1. - ** - ** The result is a single row of the sqlite_stat1 table. The first - ** two columns are the names of the table and index. The third column - ** is a string composed of a list of integer statistics about the - ** index. The first integer in the list is the total number of entries - ** in the index. There is one additional integer in the list for each - ** column of the table. This additional integer is a guess of how many - ** rows of the table the index will select. If D is the count of distinct - ** values and K is the total number of rows, then the integer is computed - ** as: - ** - ** I = (K+D-1)/D - ** - ** If K==0 then no entry is made into the sqlite_stat1 table. - ** If K>0 then it is always the case the D>0 so division by zero - ** is never possible. - */ - sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regStat1); - if( jZeroRows<0 ){ - jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); - } - for(i=0; ipIndex==0 ){ - sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb); - VdbeComment((v, "%s", pTab->zName)); - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat1); - sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); - jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); - }else{ - sqlite3VdbeJumpHere(v, jZeroRows); - jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto); - } - sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); - sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); - sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid); - sqlite3VdbeChangeP5(v, OPFLAG_APPEND); - if( pParse->nMemnMem = regRec; - sqlite3VdbeJumpHere(v, jZeroRows); -} - - -/* -** Generate code that will cause the most recent index analysis to -** be loaded into internal hash tables where is can be used. -*/ -static void loadAnalysis(Parse *pParse, int iDb){ - Vdbe *v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3VdbeAddOp1(v, OP_LoadAnalysis, iDb); - } -} - -/* -** Generate code that will do an analysis of an entire database -*/ -static void analyzeDatabase(Parse *pParse, int iDb){ - sqlite3 *db = pParse->db; - Schema *pSchema = db->aDb[iDb].pSchema; /* Schema of database iDb */ - HashElem *k; - int iStatCur; - int iMem; - - sqlite3BeginWriteOperation(pParse, 0, iDb); - iStatCur = pParse->nTab; - pParse->nTab += 3; - openStatTable(pParse, iDb, iStatCur, 0, 0); - iMem = pParse->nMem+1; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ - Table *pTab = (Table*)sqliteHashData(k); - analyzeOneTable(pParse, pTab, 0, iStatCur, iMem); - } - loadAnalysis(pParse, iDb); -} - -/* -** Generate code that will do an analysis of a single table in -** a database. If pOnlyIdx is not NULL then it is a single index -** in pTab that should be analyzed. -*/ -static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){ - int iDb; - int iStatCur; - - assert( pTab!=0 ); - assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - sqlite3BeginWriteOperation(pParse, 0, iDb); - iStatCur = pParse->nTab; - pParse->nTab += 3; - if( pOnlyIdx ){ - openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx"); - }else{ - openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl"); - } - analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1); - loadAnalysis(pParse, iDb); -} - -/* -** Generate code for the ANALYZE command. The parser calls this routine -** when it recognizes an ANALYZE command. -** -** ANALYZE -- 1 -** ANALYZE -- 2 -** ANALYZE ?.? -- 3 -** -** Form 1 causes all indices in all attached databases to be analyzed. -** Form 2 analyzes all indices the single database named. -** Form 3 analyzes all indices associated with the named table. -*/ -SQLITE_PRIVATE void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ - sqlite3 *db = pParse->db; - int iDb; - int i; - char *z, *zDb; - Table *pTab; - Index *pIdx; - Token *pTableName; - - /* Read the database schema. If an error occurs, leave an error message - ** and code in pParse and return NULL. */ - assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - return; - } - - assert( pName2!=0 || pName1==0 ); - if( pName1==0 ){ - /* Form 1: Analyze everything */ - for(i=0; inDb; i++){ - if( i==1 ) continue; /* Do not analyze the TEMP database */ - analyzeDatabase(pParse, i); - } - }else if( pName2->n==0 ){ - /* Form 2: Analyze the database or table named */ - iDb = sqlite3FindDb(db, pName1); - if( iDb>=0 ){ - analyzeDatabase(pParse, iDb); - }else{ - z = sqlite3NameFromToken(db, pName1); - if( z ){ - if( (pIdx = sqlite3FindIndex(db, z, 0))!=0 ){ - analyzeTable(pParse, pIdx->pTable, pIdx); - }else if( (pTab = sqlite3LocateTable(pParse, 0, z, 0))!=0 ){ - analyzeTable(pParse, pTab, 0); - } - sqlite3DbFree(db, z); - } - } - }else{ - /* Form 3: Analyze the fully qualified table name */ - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName); - if( iDb>=0 ){ - zDb = db->aDb[iDb].zName; - z = sqlite3NameFromToken(db, pTableName); - if( z ){ - if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){ - analyzeTable(pParse, pIdx->pTable, pIdx); - }else if( (pTab = sqlite3LocateTable(pParse, 0, z, zDb))!=0 ){ - analyzeTable(pParse, pTab, 0); - } - sqlite3DbFree(db, z); - } - } - } -} - -/* -** Used to pass information from the analyzer reader through to the -** callback routine. -*/ -typedef struct analysisInfo analysisInfo; -struct analysisInfo { - sqlite3 *db; - const char *zDatabase; -}; - -/* -** This callback is invoked once for each index when reading the -** sqlite_stat1 table. -** -** argv[0] = name of the table -** argv[1] = name of the index (might be NULL) -** argv[2] = results of analysis - on integer for each column -** -** Entries for which argv[1]==NULL simply record the number of rows in -** the table. -*/ -static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ - analysisInfo *pInfo = (analysisInfo*)pData; - Index *pIndex; - Table *pTable; - int i, c, n; - tRowcnt v; - const char *z; - - assert( argc==3 ); - UNUSED_PARAMETER2(NotUsed, argc); - - if( argv==0 || argv[0]==0 || argv[2]==0 ){ - return 0; - } - pTable = sqlite3FindTable(pInfo->db, argv[0], pInfo->zDatabase); - if( pTable==0 ){ - return 0; - } - if( argv[1] ){ - pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase); - }else{ - pIndex = 0; - } - n = pIndex ? pIndex->nColumn : 0; - z = argv[2]; - for(i=0; *z && i<=n; i++){ - v = 0; - while( (c=z[0])>='0' && c<='9' ){ - v = v*10 + c - '0'; - z++; - } - if( i==0 ) pTable->nRowEst = v; - if( pIndex==0 ) break; - pIndex->aiRowEst[i] = v; - if( *z==' ' ) z++; - if( strcmp(z, "unordered")==0 ){ - pIndex->bUnordered = 1; - break; - } - } - return 0; -} - -/* -** If the Index.aSample variable is not NULL, delete the aSample[] array -** and its contents. -*/ -SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ -#ifdef SQLITE_ENABLE_STAT3 - if( pIdx->aSample ){ - int j; - for(j=0; jnSample; j++){ - IndexSample *p = &pIdx->aSample[j]; - if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){ - sqlite3DbFree(db, p->u.z); - } - } - sqlite3DbFree(db, pIdx->aSample); - } - if( db && db->pnBytesFreed==0 ){ - pIdx->nSample = 0; - pIdx->aSample = 0; - } -#else - UNUSED_PARAMETER(db); - UNUSED_PARAMETER(pIdx); -#endif -} - -#ifdef SQLITE_ENABLE_STAT3 -/* -** Load content from the sqlite_stat3 table into the Index.aSample[] -** arrays of all indices. -*/ -static int loadStat3(sqlite3 *db, const char *zDb){ - int rc; /* Result codes from subroutines */ - sqlite3_stmt *pStmt = 0; /* An SQL statement being run */ - char *zSql; /* Text of the SQL statement */ - Index *pPrevIdx = 0; /* Previous index in the loop */ - int idx = 0; /* slot in pIdx->aSample[] for next sample */ - int eType; /* Datatype of a sample */ - IndexSample *pSample; /* A slot in pIdx->aSample[] */ - - assert( db->lookaside.bEnabled==0 ); - if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){ - return SQLITE_OK; - } - - zSql = sqlite3MPrintf(db, - "SELECT idx,count(*) FROM %Q.sqlite_stat3" - " GROUP BY idx", zDb); - if( !zSql ){ - return SQLITE_NOMEM; - } - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - sqlite3DbFree(db, zSql); - if( rc ) return rc; - - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - char *zIndex; /* Index name */ - Index *pIdx; /* Pointer to the index object */ - int nSample; /* Number of samples */ - - zIndex = (char *)sqlite3_column_text(pStmt, 0); - if( zIndex==0 ) continue; - nSample = sqlite3_column_int(pStmt, 1); - pIdx = sqlite3FindIndex(db, zIndex, zDb); - if( pIdx==0 ) continue; - assert( pIdx->nSample==0 ); - pIdx->nSample = nSample; - pIdx->aSample = sqlite3DbMallocZero(db, nSample*sizeof(IndexSample)); - pIdx->avgEq = pIdx->aiRowEst[1]; - if( pIdx->aSample==0 ){ - db->mallocFailed = 1; - sqlite3_finalize(pStmt); - return SQLITE_NOMEM; - } - } - rc = sqlite3_finalize(pStmt); - if( rc ) return rc; - - zSql = sqlite3MPrintf(db, - "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat3", zDb); - if( !zSql ){ - return SQLITE_NOMEM; - } - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - sqlite3DbFree(db, zSql); - if( rc ) return rc; - - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - char *zIndex; /* Index name */ - Index *pIdx; /* Pointer to the index object */ - int i; /* Loop counter */ - tRowcnt sumEq; /* Sum of the nEq values */ - - zIndex = (char *)sqlite3_column_text(pStmt, 0); - if( zIndex==0 ) continue; - pIdx = sqlite3FindIndex(db, zIndex, zDb); - if( pIdx==0 ) continue; - if( pIdx==pPrevIdx ){ - idx++; - }else{ - pPrevIdx = pIdx; - idx = 0; - } - assert( idxnSample ); - pSample = &pIdx->aSample[idx]; - pSample->nEq = (tRowcnt)sqlite3_column_int64(pStmt, 1); - pSample->nLt = (tRowcnt)sqlite3_column_int64(pStmt, 2); - pSample->nDLt = (tRowcnt)sqlite3_column_int64(pStmt, 3); - if( idx==pIdx->nSample-1 ){ - if( pSample->nDLt>0 ){ - for(i=0, sumEq=0; i<=idx-1; i++) sumEq += pIdx->aSample[i].nEq; - pIdx->avgEq = (pSample->nLt - sumEq)/pSample->nDLt; - } - if( pIdx->avgEq<=0 ) pIdx->avgEq = 1; - } - eType = sqlite3_column_type(pStmt, 4); - pSample->eType = (u8)eType; - switch( eType ){ - case SQLITE_INTEGER: { - pSample->u.i = sqlite3_column_int64(pStmt, 4); - break; - } - case SQLITE_FLOAT: { - pSample->u.r = sqlite3_column_double(pStmt, 4); - break; - } - case SQLITE_NULL: { - break; - } - default: assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); { - const char *z = (const char *)( - (eType==SQLITE_BLOB) ? - sqlite3_column_blob(pStmt, 4): - sqlite3_column_text(pStmt, 4) - ); - int n = z ? sqlite3_column_bytes(pStmt, 4) : 0; - pSample->nByte = n; - if( n < 1){ - pSample->u.z = 0; - }else{ - pSample->u.z = sqlite3DbMallocRaw(db, n); - if( pSample->u.z==0 ){ - db->mallocFailed = 1; - sqlite3_finalize(pStmt); - return SQLITE_NOMEM; - } - memcpy(pSample->u.z, z, n); - } - } - } - } - return sqlite3_finalize(pStmt); -} -#endif /* SQLITE_ENABLE_STAT3 */ - -/* -** Load the content of the sqlite_stat1 and sqlite_stat3 tables. The -** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] -** arrays. The contents of sqlite_stat3 are used to populate the -** Index.aSample[] arrays. -** -** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR -** is returned. In this case, even if SQLITE_ENABLE_STAT3 was defined -** during compilation and the sqlite_stat3 table is present, no data is -** read from it. -** -** If SQLITE_ENABLE_STAT3 was defined during compilation and the -** sqlite_stat3 table is not present in the database, SQLITE_ERROR is -** returned. However, in this case, data is read from the sqlite_stat1 -** table (if it is present) before returning. -** -** If an OOM error occurs, this function always sets db->mallocFailed. -** This means if the caller does not care about other errors, the return -** code may be ignored. -*/ -SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ - analysisInfo sInfo; - HashElem *i; - char *zSql; - int rc; - - assert( iDb>=0 && iDbnDb ); - assert( db->aDb[iDb].pBt!=0 ); - - /* Clear any prior statistics */ - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ - Index *pIdx = sqliteHashData(i); - sqlite3DefaultRowEst(pIdx); -#ifdef SQLITE_ENABLE_STAT3 - sqlite3DeleteIndexSamples(db, pIdx); - pIdx->aSample = 0; -#endif - } - - /* Check to make sure the sqlite_stat1 table exists */ - sInfo.db = db; - sInfo.zDatabase = db->aDb[iDb].zName; - if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){ - return SQLITE_ERROR; - } - - /* Load new statistics out of the sqlite_stat1 table */ - zSql = sqlite3MPrintf(db, - "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); - sqlite3DbFree(db, zSql); - } - - - /* Load the statistics from the sqlite_stat3 table. */ -#ifdef SQLITE_ENABLE_STAT3 - if( rc==SQLITE_OK ){ - int lookasideEnabled = db->lookaside.bEnabled; - db->lookaside.bEnabled = 0; - rc = loadStat3(db, sInfo.zDatabase); - db->lookaside.bEnabled = lookasideEnabled; - } -#endif - - if( rc==SQLITE_NOMEM ){ - db->mallocFailed = 1; - } - return rc; -} - - -#endif /* SQLITE_OMIT_ANALYZE */ - -/************** End of analyze.c *********************************************/ -/************** Begin file attach.c ******************************************/ -/* -** 2003 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to implement the ATTACH and DETACH commands. -*/ - -#ifndef SQLITE_OMIT_ATTACH -/* -** Resolve an expression that was part of an ATTACH or DETACH statement. This -** is slightly different from resolving a normal SQL expression, because simple -** identifiers are treated as strings, not possible column names or aliases. -** -** i.e. if the parser sees: -** -** ATTACH DATABASE abc AS def -** -** it treats the two expressions as literal strings 'abc' and 'def' instead of -** looking for columns of the same name. -** -** This only applies to the root node of pExpr, so the statement: -** -** ATTACH DATABASE abc||def AS 'db2' -** -** will fail because neither abc or def can be resolved. -*/ -static int resolveAttachExpr(NameContext *pName, Expr *pExpr) -{ - int rc = SQLITE_OK; - if( pExpr ){ - if( pExpr->op!=TK_ID ){ - rc = sqlite3ResolveExprNames(pName, pExpr); - if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){ - sqlite3ErrorMsg(pName->pParse, "invalid name: \"%s\"", pExpr->u.zToken); - return SQLITE_ERROR; - } - }else{ - pExpr->op = TK_STRING; - } - } - return rc; -} - -/* -** An SQL user-function registered to do the work of an ATTACH statement. The -** three arguments to the function come directly from an attach statement: -** -** ATTACH DATABASE x AS y KEY z -** -** SELECT sqlite_attach(x, y, z) -** -** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the -** third argument. -*/ -static void attachFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - int i; - int rc = 0; - sqlite3 *db = sqlite3_context_db_handle(context); - const char *zName; - const char *zFile; - char *zPath = 0; - char *zErr = 0; - unsigned int flags; - Db *aNew; - char *zErrDyn = 0; - sqlite3_vfs *pVfs; - - UNUSED_PARAMETER(NotUsed); - - zFile = (const char *)sqlite3_value_text(argv[0]); - zName = (const char *)sqlite3_value_text(argv[1]); - if( zFile==0 ) zFile = ""; - if( zName==0 ) zName = ""; - - /* Check for the following errors: - ** - ** * Too many attached databases, - ** * Transaction currently open - ** * Specified database name already being used. - */ - if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ - zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", - db->aLimit[SQLITE_LIMIT_ATTACHED] - ); - goto attach_error; - } - if( !db->autoCommit ){ - zErrDyn = sqlite3MPrintf(db, "cannot ATTACH database within transaction"); - goto attach_error; - } - for(i=0; inDb; i++){ - char *z = db->aDb[i].zName; - assert( z && zName ); - if( sqlite3StrICmp(z, zName)==0 ){ - zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); - goto attach_error; - } - } - - /* Allocate the new entry in the db->aDb[] array and initialize the schema - ** hash tables. - */ - if( db->aDb==db->aDbStatic ){ - aNew = sqlite3DbMallocRaw(db, sizeof(db->aDb[0])*3 ); - if( aNew==0 ) return; - memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); - }else{ - aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); - if( aNew==0 ) return; - } - db->aDb = aNew; - aNew = &db->aDb[db->nDb]; - memset(aNew, 0, sizeof(*aNew)); - - /* Open the database file. If the btree is successfully opened, use - ** it to obtain the database schema. At this point the schema may - ** or may not be initialized. - */ - flags = db->openFlags; - rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); - return; - } - assert( pVfs ); - flags |= SQLITE_OPEN_MAIN_DB; - rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags); - sqlite3_free( zPath ); - db->nDb++; - if( rc==SQLITE_CONSTRAINT ){ - rc = SQLITE_ERROR; - zErrDyn = sqlite3MPrintf(db, "database is already attached"); - }else if( rc==SQLITE_OK ){ - Pager *pPager; - aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt); - if( !aNew->pSchema ){ - rc = SQLITE_NOMEM; - }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ - zErrDyn = sqlite3MPrintf(db, - "attached databases must use the same text encoding as main database"); - rc = SQLITE_ERROR; - } - pPager = sqlite3BtreePager(aNew->pBt); - sqlite3PagerLockingMode(pPager, db->dfltLockMode); - sqlite3BtreeSecureDelete(aNew->pBt, - sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) ); - } - aNew->safety_level = 3; - aNew->zName = sqlite3DbStrDup(db, zName); - if( rc==SQLITE_OK && aNew->zName==0 ){ - rc = SQLITE_NOMEM; - } - - -#ifdef SQLITE_HAS_CODEC - if( rc==SQLITE_OK ){ - extern int sqlite3CodecAttach(sqlite3*, int, const void*, int); - extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); - int nKey; - char *zKey; - int t = sqlite3_value_type(argv[2]); - switch( t ){ - case SQLITE_INTEGER: - case SQLITE_FLOAT: - zErrDyn = sqlite3DbStrDup(db, "Invalid key value"); - rc = SQLITE_ERROR; - break; - - case SQLITE_TEXT: - case SQLITE_BLOB: - nKey = sqlite3_value_bytes(argv[2]); - zKey = (char *)sqlite3_value_blob(argv[2]); - rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); - break; - - case SQLITE_NULL: - /* No key specified. Use the key from the main database */ - sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - if( nKey>0 || sqlite3BtreeGetReserve(db->aDb[0].pBt)>0 ){ - rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); - } - break; - } - } -#endif - - /* If the file was opened successfully, read the schema for the new database. - ** If this fails, or if opening the file failed, then close the file and - ** remove the entry from the db->aDb[] array. i.e. put everything back the way - ** we found it. - */ - if( rc==SQLITE_OK ){ - sqlite3BtreeEnterAll(db); - rc = sqlite3Init(db, &zErrDyn); - sqlite3BtreeLeaveAll(db); - } - if( rc ){ - int iDb = db->nDb - 1; - assert( iDb>=2 ); - if( db->aDb[iDb].pBt ){ - sqlite3BtreeClose(db->aDb[iDb].pBt); - db->aDb[iDb].pBt = 0; - db->aDb[iDb].pSchema = 0; - } - sqlite3ResetAllSchemasOfConnection(db); - db->nDb = iDb; - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ - db->mallocFailed = 1; - sqlite3DbFree(db, zErrDyn); - zErrDyn = sqlite3MPrintf(db, "out of memory"); - }else if( zErrDyn==0 ){ - zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile); - } - goto attach_error; - } - - return; - -attach_error: - /* Return an error if we get here */ - if( zErrDyn ){ - sqlite3_result_error(context, zErrDyn, -1); - sqlite3DbFree(db, zErrDyn); - } - if( rc ) sqlite3_result_error_code(context, rc); -} - -/* -** An SQL user-function registered to do the work of an DETACH statement. The -** three arguments to the function come directly from a detach statement: -** -** DETACH DATABASE x -** -** SELECT sqlite_detach(x) -*/ -static void detachFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - const char *zName = (const char *)sqlite3_value_text(argv[0]); - sqlite3 *db = sqlite3_context_db_handle(context); - int i; - Db *pDb = 0; - char zErr[128]; - - UNUSED_PARAMETER(NotUsed); - - if( zName==0 ) zName = ""; - for(i=0; inDb; i++){ - pDb = &db->aDb[i]; - if( pDb->pBt==0 ) continue; - if( sqlite3StrICmp(pDb->zName, zName)==0 ) break; - } - - if( i>=db->nDb ){ - sqlite3_snprintf(sizeof(zErr),zErr, "no such database: %s", zName); - goto detach_error; - } - if( i<2 ){ - sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName); - goto detach_error; - } - if( !db->autoCommit ){ - sqlite3_snprintf(sizeof(zErr), zErr, - "cannot DETACH database within transaction"); - goto detach_error; - } - if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){ - sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName); - goto detach_error; - } - - sqlite3BtreeClose(pDb->pBt); - pDb->pBt = 0; - pDb->pSchema = 0; - sqlite3ResetAllSchemasOfConnection(db); - return; - -detach_error: - sqlite3_result_error(context, zErr, -1); -} - -/* -** This procedure generates VDBE code for a single invocation of either the -** sqlite_detach() or sqlite_attach() SQL user functions. -*/ -static void codeAttach( - Parse *pParse, /* The parser context */ - int type, /* Either SQLITE_ATTACH or SQLITE_DETACH */ - FuncDef const *pFunc,/* FuncDef wrapper for detachFunc() or attachFunc() */ - Expr *pAuthArg, /* Expression to pass to authorization callback */ - Expr *pFilename, /* Name of database file */ - Expr *pDbname, /* Name of the database to use internally */ - Expr *pKey /* Database key for encryption extension */ -){ - int rc; - NameContext sName; - Vdbe *v; - sqlite3* db = pParse->db; - int regArgs; - - memset(&sName, 0, sizeof(NameContext)); - sName.pParse = pParse; - - if( - SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) || - SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || - SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) - ){ - pParse->nErr++; - goto attach_end; - } - -#ifndef SQLITE_OMIT_AUTHORIZATION - if( pAuthArg ){ - char *zAuthArg; - if( pAuthArg->op==TK_STRING ){ - zAuthArg = pAuthArg->u.zToken; - }else{ - zAuthArg = 0; - } - rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0); - if(rc!=SQLITE_OK ){ - goto attach_end; - } - } -#endif /* SQLITE_OMIT_AUTHORIZATION */ - - - v = sqlite3GetVdbe(pParse); - regArgs = sqlite3GetTempRange(pParse, 4); - sqlite3ExprCode(pParse, pFilename, regArgs); - sqlite3ExprCode(pParse, pDbname, regArgs+1); - sqlite3ExprCode(pParse, pKey, regArgs+2); - - assert( v || db->mallocFailed ); - if( v ){ - sqlite3VdbeAddOp3(v, OP_Function, 0, regArgs+3-pFunc->nArg, regArgs+3); - assert( pFunc->nArg==-1 || (pFunc->nArg&0xff)==pFunc->nArg ); - sqlite3VdbeChangeP5(v, (u8)(pFunc->nArg)); - sqlite3VdbeChangeP4(v, -1, (char *)pFunc, P4_FUNCDEF); - - /* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this - ** statement only). For DETACH, set it to false (expire all existing - ** statements). - */ - sqlite3VdbeAddOp1(v, OP_Expire, (type==SQLITE_ATTACH)); - } - -attach_end: - sqlite3ExprDelete(db, pFilename); - sqlite3ExprDelete(db, pDbname); - sqlite3ExprDelete(db, pKey); -} - -/* -** Called by the parser to compile a DETACH statement. -** -** DETACH pDbname -*/ -SQLITE_PRIVATE void sqlite3Detach(Parse *pParse, Expr *pDbname){ - static const FuncDef detach_func = { - 1, /* nArg */ - SQLITE_UTF8, /* iPrefEnc */ - 0, /* flags */ - 0, /* pUserData */ - 0, /* pNext */ - detachFunc, /* xFunc */ - 0, /* xStep */ - 0, /* xFinalize */ - "sqlite_detach", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ - }; - codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname); -} - -/* -** Called by the parser to compile an ATTACH statement. -** -** ATTACH p AS pDbname KEY pKey -*/ -SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){ - static const FuncDef attach_func = { - 3, /* nArg */ - SQLITE_UTF8, /* iPrefEnc */ - 0, /* flags */ - 0, /* pUserData */ - 0, /* pNext */ - attachFunc, /* xFunc */ - 0, /* xStep */ - 0, /* xFinalize */ - "sqlite_attach", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ - }; - codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey); -} -#endif /* SQLITE_OMIT_ATTACH */ - -/* -** Initialize a DbFixer structure. This routine must be called prior -** to passing the structure to one of the sqliteFixAAAA() routines below. -** -** The return value indicates whether or not fixation is required. TRUE -** means we do need to fix the database references, FALSE means we do not. -*/ -SQLITE_PRIVATE int sqlite3FixInit( - DbFixer *pFix, /* The fixer to be initialized */ - Parse *pParse, /* Error messages will be written here */ - int iDb, /* This is the database that must be used */ - const char *zType, /* "view", "trigger", or "index" */ - const Token *pName /* Name of the view, trigger, or index */ -){ - sqlite3 *db; - - if( NEVER(iDb<0) || iDb==1 ) return 0; - db = pParse->db; - assert( db->nDb>iDb ); - pFix->pParse = pParse; - pFix->zDb = db->aDb[iDb].zName; - pFix->pSchema = db->aDb[iDb].pSchema; - pFix->zType = zType; - pFix->pName = pName; - return 1; -} - -/* -** The following set of routines walk through the parse tree and assign -** a specific database to all table references where the database name -** was left unspecified in the original SQL statement. The pFix structure -** must have been initialized by a prior call to sqlite3FixInit(). -** -** These routines are used to make sure that an index, trigger, or -** view in one database does not refer to objects in a different database. -** (Exception: indices, triggers, and views in the TEMP database are -** allowed to refer to anything.) If a reference is explicitly made -** to an object in a different database, an error message is added to -** pParse->zErrMsg and these routines return non-zero. If everything -** checks out, these routines return 0. -*/ -SQLITE_PRIVATE int sqlite3FixSrcList( - DbFixer *pFix, /* Context of the fixation */ - SrcList *pList /* The Source list to check and modify */ -){ - int i; - const char *zDb; - struct SrcList_item *pItem; - - if( NEVER(pList==0) ) return 0; - zDb = pFix->zDb; - for(i=0, pItem=pList->a; inSrc; i++, pItem++){ - if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){ - sqlite3ErrorMsg(pFix->pParse, - "%s %T cannot reference objects in database %s", - pFix->zType, pFix->pName, pItem->zDatabase); - return 1; - } - sqlite3DbFree(pFix->pParse->db, pItem->zDatabase); - pItem->zDatabase = 0; - pItem->pSchema = pFix->pSchema; -#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) - if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1; - if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1; -#endif - } - return 0; -} -#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) -SQLITE_PRIVATE int sqlite3FixSelect( - DbFixer *pFix, /* Context of the fixation */ - Select *pSelect /* The SELECT statement to be fixed to one database */ -){ - while( pSelect ){ - if( sqlite3FixExprList(pFix, pSelect->pEList) ){ - return 1; - } - if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){ - return 1; - } - if( sqlite3FixExpr(pFix, pSelect->pWhere) ){ - return 1; - } - if( sqlite3FixExpr(pFix, pSelect->pHaving) ){ - return 1; - } - pSelect = pSelect->pPrior; - } - return 0; -} -SQLITE_PRIVATE int sqlite3FixExpr( - DbFixer *pFix, /* Context of the fixation */ - Expr *pExpr /* The expression to be fixed to one database */ -){ - while( pExpr ){ - if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ) break; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1; - }else{ - if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1; - } - if( sqlite3FixExpr(pFix, pExpr->pRight) ){ - return 1; - } - pExpr = pExpr->pLeft; - } - return 0; -} -SQLITE_PRIVATE int sqlite3FixExprList( - DbFixer *pFix, /* Context of the fixation */ - ExprList *pList /* The expression to be fixed to one database */ -){ - int i; - struct ExprList_item *pItem; - if( pList==0 ) return 0; - for(i=0, pItem=pList->a; inExpr; i++, pItem++){ - if( sqlite3FixExpr(pFix, pItem->pExpr) ){ - return 1; - } - } - return 0; -} -#endif - -#ifndef SQLITE_OMIT_TRIGGER -SQLITE_PRIVATE int sqlite3FixTriggerStep( - DbFixer *pFix, /* Context of the fixation */ - TriggerStep *pStep /* The trigger step be fixed to one database */ -){ - while( pStep ){ - if( sqlite3FixSelect(pFix, pStep->pSelect) ){ - return 1; - } - if( sqlite3FixExpr(pFix, pStep->pWhere) ){ - return 1; - } - if( sqlite3FixExprList(pFix, pStep->pExprList) ){ - return 1; - } - pStep = pStep->pNext; - } - return 0; -} -#endif - -/************** End of attach.c **********************************************/ -/************** Begin file auth.c ********************************************/ -/* -** 2003 January 11 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to implement the sqlite3_set_authorizer() -** API. This facility is an optional feature of the library. Embedded -** systems that do not need this facility may omit it by recompiling -** the library with -DSQLITE_OMIT_AUTHORIZATION=1 -*/ - -/* -** All of the code in this file may be omitted by defining a single -** macro. -*/ -#ifndef SQLITE_OMIT_AUTHORIZATION - -/* -** Set or clear the access authorization function. -** -** The access authorization function is be called during the compilation -** phase to verify that the user has read and/or write access permission on -** various fields of the database. The first argument to the auth function -** is a copy of the 3rd argument to this routine. The second argument -** to the auth function is one of these constants: -** -** SQLITE_CREATE_INDEX -** SQLITE_CREATE_TABLE -** SQLITE_CREATE_TEMP_INDEX -** SQLITE_CREATE_TEMP_TABLE -** SQLITE_CREATE_TEMP_TRIGGER -** SQLITE_CREATE_TEMP_VIEW -** SQLITE_CREATE_TRIGGER -** SQLITE_CREATE_VIEW -** SQLITE_DELETE -** SQLITE_DROP_INDEX -** SQLITE_DROP_TABLE -** SQLITE_DROP_TEMP_INDEX -** SQLITE_DROP_TEMP_TABLE -** SQLITE_DROP_TEMP_TRIGGER -** SQLITE_DROP_TEMP_VIEW -** SQLITE_DROP_TRIGGER -** SQLITE_DROP_VIEW -** SQLITE_INSERT -** SQLITE_PRAGMA -** SQLITE_READ -** SQLITE_SELECT -** SQLITE_TRANSACTION -** SQLITE_UPDATE -** -** The third and fourth arguments to the auth function are the name of -** the table and the column that are being accessed. The auth function -** should return either SQLITE_OK, SQLITE_DENY, or SQLITE_IGNORE. If -** SQLITE_OK is returned, it means that access is allowed. SQLITE_DENY -** means that the SQL statement will never-run - the sqlite3_exec() call -** will return with an error. SQLITE_IGNORE means that the SQL statement -** should run but attempts to read the specified column will return NULL -** and attempts to write the column will be ignored. -** -** Setting the auth function to NULL disables this hook. The default -** setting of the auth function is NULL. -*/ -SQLITE_API int sqlite3_set_authorizer( - sqlite3 *db, - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), - void *pArg -){ - sqlite3_mutex_enter(db->mutex); - db->xAuth = xAuth; - db->pAuthArg = pArg; - sqlite3ExpirePreparedStatements(db); - sqlite3_mutex_leave(db->mutex); - return SQLITE_OK; -} - -/* -** Write an error message into pParse->zErrMsg that explains that the -** user-supplied authorization function returned an illegal value. -*/ -static void sqliteAuthBadReturnCode(Parse *pParse){ - sqlite3ErrorMsg(pParse, "authorizer malfunction"); - pParse->rc = SQLITE_ERROR; -} - -/* -** Invoke the authorization callback for permission to read column zCol from -** table zTab in database zDb. This function assumes that an authorization -** callback has been registered (i.e. that sqlite3.xAuth is not NULL). -** -** If SQLITE_IGNORE is returned and pExpr is not NULL, then pExpr is changed -** to an SQL NULL expression. Otherwise, if pExpr is NULL, then SQLITE_IGNORE -** is treated as SQLITE_DENY. In this case an error is left in pParse. -*/ -SQLITE_PRIVATE int sqlite3AuthReadCol( - Parse *pParse, /* The parser context */ - const char *zTab, /* Table name */ - const char *zCol, /* Column name */ - int iDb /* Index of containing database. */ -){ - sqlite3 *db = pParse->db; /* Database handle */ - char *zDb = db->aDb[iDb].zName; /* Name of attached database */ - int rc; /* Auth callback return code */ - - rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext); - if( rc==SQLITE_DENY ){ - if( db->nDb>2 || iDb!=0 ){ - sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",zDb,zTab,zCol); - }else{ - sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited", zTab, zCol); - } - pParse->rc = SQLITE_AUTH; - }else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){ - sqliteAuthBadReturnCode(pParse); - } - return rc; -} - -/* -** The pExpr should be a TK_COLUMN expression. The table referred to -** is in pTabList or else it is the NEW or OLD table of a trigger. -** Check to see if it is OK to read this particular column. -** -** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN -** instruction into a TK_NULL. If the auth function returns SQLITE_DENY, -** then generate an error. -*/ -SQLITE_PRIVATE void sqlite3AuthRead( - Parse *pParse, /* The parser context */ - Expr *pExpr, /* The expression to check authorization on */ - Schema *pSchema, /* The schema of the expression */ - SrcList *pTabList /* All table that pExpr might refer to */ -){ - sqlite3 *db = pParse->db; - Table *pTab = 0; /* The table being read */ - const char *zCol; /* Name of the column of the table */ - int iSrc; /* Index in pTabList->a[] of table being read */ - int iDb; /* The index of the database the expression refers to */ - int iCol; /* Index of column in table */ - - if( db->xAuth==0 ) return; - iDb = sqlite3SchemaToIndex(pParse->db, pSchema); - if( iDb<0 ){ - /* An attempt to read a column out of a subquery or other - ** temporary table. */ - return; - } - - assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER ); - if( pExpr->op==TK_TRIGGER ){ - pTab = pParse->pTriggerTab; - }else{ - assert( pTabList ); - for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){ - if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ - pTab = pTabList->a[iSrc].pTab; - break; - } - } - } - iCol = pExpr->iColumn; - if( NEVER(pTab==0) ) return; - - if( iCol>=0 ){ - assert( iColnCol ); - zCol = pTab->aCol[iCol].zName; - }else if( pTab->iPKey>=0 ){ - assert( pTab->iPKeynCol ); - zCol = pTab->aCol[pTab->iPKey].zName; - }else{ - zCol = "ROWID"; - } - assert( iDb>=0 && iDbnDb ); - if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){ - pExpr->op = TK_NULL; - } -} - -/* -** Do an authorization check using the code and arguments given. Return -** either SQLITE_OK (zero) or SQLITE_IGNORE or SQLITE_DENY. If SQLITE_DENY -** is returned, then the error count and error message in pParse are -** modified appropriately. -*/ -SQLITE_PRIVATE int sqlite3AuthCheck( - Parse *pParse, - int code, - const char *zArg1, - const char *zArg2, - const char *zArg3 -){ - sqlite3 *db = pParse->db; - int rc; - - /* Don't do any authorization checks if the database is initialising - ** or if the parser is being invoked from within sqlite3_declare_vtab. - */ - if( db->init.busy || IN_DECLARE_VTAB ){ - return SQLITE_OK; - } - - if( db->xAuth==0 ){ - return SQLITE_OK; - } - rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext); - if( rc==SQLITE_DENY ){ - sqlite3ErrorMsg(pParse, "not authorized"); - pParse->rc = SQLITE_AUTH; - }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){ - rc = SQLITE_DENY; - sqliteAuthBadReturnCode(pParse); - } - return rc; -} - -/* -** Push an authorization context. After this routine is called, the -** zArg3 argument to authorization callbacks will be zContext until -** popped. Or if pParse==0, this routine is a no-op. -*/ -SQLITE_PRIVATE void sqlite3AuthContextPush( - Parse *pParse, - AuthContext *pContext, - const char *zContext -){ - assert( pParse ); - pContext->pParse = pParse; - pContext->zAuthContext = pParse->zAuthContext; - pParse->zAuthContext = zContext; -} - -/* -** Pop an authorization context that was previously pushed -** by sqlite3AuthContextPush -*/ -SQLITE_PRIVATE void sqlite3AuthContextPop(AuthContext *pContext){ - if( pContext->pParse ){ - pContext->pParse->zAuthContext = pContext->zAuthContext; - pContext->pParse = 0; - } -} - -#endif /* SQLITE_OMIT_AUTHORIZATION */ - -/************** End of auth.c ************************************************/ -/************** Begin file build.c *******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the SQLite parser -** when syntax rules are reduced. The routines in this file handle the -** following kinds of SQL syntax: -** -** CREATE TABLE -** DROP TABLE -** CREATE INDEX -** DROP INDEX -** creating ID lists -** BEGIN TRANSACTION -** COMMIT -** ROLLBACK -*/ - -/* -** This routine is called when a new SQL statement is beginning to -** be parsed. Initialize the pParse structure as needed. -*/ -SQLITE_PRIVATE void sqlite3BeginParse(Parse *pParse, int explainFlag){ - pParse->explain = (u8)explainFlag; - pParse->nVar = 0; -} - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** The TableLock structure is only used by the sqlite3TableLock() and -** codeTableLocks() functions. -*/ -struct TableLock { - int iDb; /* The database containing the table to be locked */ - int iTab; /* The root page of the table to be locked */ - u8 isWriteLock; /* True for write lock. False for a read lock */ - const char *zName; /* Name of the table */ -}; - -/* -** Record the fact that we want to lock a table at run-time. -** -** The table to be locked has root page iTab and is found in database iDb. -** A read or a write lock can be taken depending on isWritelock. -** -** This routine just records the fact that the lock is desired. The -** code to make the lock occur is generated by a later call to -** codeTableLocks() which occurs during sqlite3FinishCoding(). -*/ -SQLITE_PRIVATE void sqlite3TableLock( - Parse *pParse, /* Parsing context */ - int iDb, /* Index of the database containing the table to lock */ - int iTab, /* Root page number of the table to be locked */ - u8 isWriteLock, /* True for a write lock */ - const char *zName /* Name of the table to be locked */ -){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); - int i; - int nBytes; - TableLock *p; - assert( iDb>=0 ); - - for(i=0; inTableLock; i++){ - p = &pToplevel->aTableLock[i]; - if( p->iDb==iDb && p->iTab==iTab ){ - p->isWriteLock = (p->isWriteLock || isWriteLock); - return; - } - } - - nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1); - pToplevel->aTableLock = - sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes); - if( pToplevel->aTableLock ){ - p = &pToplevel->aTableLock[pToplevel->nTableLock++]; - p->iDb = iDb; - p->iTab = iTab; - p->isWriteLock = isWriteLock; - p->zName = zName; - }else{ - pToplevel->nTableLock = 0; - pToplevel->db->mallocFailed = 1; - } -} - -/* -** Code an OP_TableLock instruction for each table locked by the -** statement (configured by calls to sqlite3TableLock()). -*/ -static void codeTableLocks(Parse *pParse){ - int i; - Vdbe *pVdbe; - - pVdbe = sqlite3GetVdbe(pParse); - assert( pVdbe!=0 ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */ - - for(i=0; inTableLock; i++){ - TableLock *p = &pParse->aTableLock[i]; - int p1 = p->iDb; - sqlite3VdbeAddOp4(pVdbe, OP_TableLock, p1, p->iTab, p->isWriteLock, - p->zName, P4_STATIC); - } -} -#else - #define codeTableLocks(x) -#endif - -/* -** This routine is called after a single SQL statement has been -** parsed and a VDBE program to execute that statement has been -** prepared. This routine puts the finishing touches on the -** VDBE program and resets the pParse structure for the next -** parse. -** -** Note that if an error occurred, it might be the case that -** no VDBE code was generated. -*/ -SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ - sqlite3 *db; - Vdbe *v; - - assert( pParse->pToplevel==0 ); - db = pParse->db; - if( db->mallocFailed ) return; - if( pParse->nested ) return; - if( pParse->nErr ) return; - - /* Begin by generating some termination code at the end of the - ** vdbe program - */ - v = sqlite3GetVdbe(pParse); - assert( !pParse->isMultiWrite - || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort)); - if( v ){ - sqlite3VdbeAddOp0(v, OP_Halt); - - /* The cookie mask contains one bit for each database file open. - ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are - ** set for each database that is used. Generate code to start a - ** transaction on each used database and to verify the schema cookie - ** on each used database. - */ - if( pParse->cookieGoto>0 ){ - yDbMask mask; - int iDb; - sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); - for(iDb=0, mask=1; iDbnDb; mask<<=1, iDb++){ - if( (mask & pParse->cookieMask)==0 ) continue; - sqlite3VdbeUsesBtree(v, iDb); - sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0); - if( db->init.busy==0 ){ - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - sqlite3VdbeAddOp3(v, OP_VerifyCookie, - iDb, pParse->cookieValue[iDb], - db->aDb[iDb].pSchema->iGeneration); - } - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - { - int i; - for(i=0; inVtabLock; i++){ - char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); - sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); - } - pParse->nVtabLock = 0; - } -#endif - - /* Once all the cookies have been verified and transactions opened, - ** obtain the required table-locks. This is a no-op unless the - ** shared-cache feature is enabled. - */ - codeTableLocks(pParse); - - /* Initialize any AUTOINCREMENT data structures required. - */ - sqlite3AutoincrementBegin(pParse); - - /* Finally, jump back to the beginning of the executable code. */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto); - } - } - - - /* Get the VDBE program ready for execution - */ - if( v && ALWAYS(pParse->nErr==0) && !db->mallocFailed ){ -#ifdef SQLITE_DEBUG - FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; - sqlite3VdbeTrace(v, trace); -#endif - assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ - /* A minimum of one cursor is required if autoincrement is used - * See ticket [a696379c1f08866] */ - if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1; - sqlite3VdbeMakeReady(v, pParse); - pParse->rc = SQLITE_DONE; - pParse->colNamesSet = 0; - }else{ - pParse->rc = SQLITE_ERROR; - } - pParse->nTab = 0; - pParse->nMem = 0; - pParse->nSet = 0; - pParse->nVar = 0; - pParse->cookieMask = 0; - pParse->cookieGoto = 0; -} - -/* -** Run the parser and code generator recursively in order to generate -** code for the SQL statement given onto the end of the pParse context -** currently under construction. When the parser is run recursively -** this way, the final OP_Halt is not appended and other initialization -** and finalization steps are omitted because those are handling by the -** outermost parser. -** -** Not everything is nestable. This facility is designed to permit -** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER. Use -** care if you decide to try to use this routine for some other purposes. -*/ -SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ - va_list ap; - char *zSql; - char *zErrMsg = 0; - sqlite3 *db = pParse->db; -# define SAVE_SZ (sizeof(Parse) - offsetof(Parse,nVar)) - char saveBuf[SAVE_SZ]; - - if( pParse->nErr ) return; - assert( pParse->nested<10 ); /* Nesting should only be of limited depth */ - va_start(ap, zFormat); - zSql = sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - if( zSql==0 ){ - return; /* A malloc must have failed */ - } - pParse->nested++; - memcpy(saveBuf, &pParse->nVar, SAVE_SZ); - memset(&pParse->nVar, 0, SAVE_SZ); - sqlite3RunParser(pParse, zSql, &zErrMsg); - sqlite3DbFree(db, zErrMsg); - sqlite3DbFree(db, zSql); - memcpy(&pParse->nVar, saveBuf, SAVE_SZ); - pParse->nested--; -} - -/* -** Locate the in-memory structure that describes a particular database -** table given the name of that table and (optionally) the name of the -** database containing the table. Return NULL if not found. -** -** If zDatabase is 0, all databases are searched for the table and the -** first matching table is returned. (No checking for duplicate table -** names is done.) The search order is TEMP first, then MAIN, then any -** auxiliary databases added using the ATTACH command. -** -** See also sqlite3LocateTable(). -*/ -SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){ - Table *p = 0; - int i; - int nName; - assert( zName!=0 ); - nName = sqlite3Strlen30(zName); - /* All mutexes are required for schema access. Make sure we hold them. */ - assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); - for(i=OMIT_TEMPDB; inDb; i++){ - int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ - if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue; - assert( sqlite3SchemaMutexHeld(db, j, 0) ); - p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, nName); - if( p ) break; - } - return p; -} - -/* -** Locate the in-memory structure that describes a particular database -** table given the name of that table and (optionally) the name of the -** database containing the table. Return NULL if not found. Also leave an -** error message in pParse->zErrMsg. -** -** The difference between this routine and sqlite3FindTable() is that this -** routine leaves an error message in pParse->zErrMsg where -** sqlite3FindTable() does not. -*/ -SQLITE_PRIVATE Table *sqlite3LocateTable( - Parse *pParse, /* context in which to report errors */ - int isView, /* True if looking for a VIEW rather than a TABLE */ - const char *zName, /* Name of the table we are looking for */ - const char *zDbase /* Name of the database. Might be NULL */ -){ - Table *p; - - /* Read the database schema. If an error occurs, leave an error message - ** and code in pParse and return NULL. */ - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - return 0; - } - - p = sqlite3FindTable(pParse->db, zName, zDbase); - if( p==0 ){ - const char *zMsg = isView ? "no such view" : "no such table"; - if( zDbase ){ - sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName); - }else{ - sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); - } - pParse->checkSchema = 1; - } - return p; -} - -/* -** Locate the table identified by *p. -** -** This is a wrapper around sqlite3LocateTable(). The difference between -** sqlite3LocateTable() and this function is that this function restricts -** the search to schema (p->pSchema) if it is not NULL. p->pSchema may be -** non-NULL if it is part of a view or trigger program definition. See -** sqlite3FixSrcList() for details. -*/ -SQLITE_PRIVATE Table *sqlite3LocateTableItem( - Parse *pParse, - int isView, - struct SrcList_item *p -){ - const char *zDb; - assert( p->pSchema==0 || p->zDatabase==0 ); - if( p->pSchema ){ - int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); - zDb = pParse->db->aDb[iDb].zName; - }else{ - zDb = p->zDatabase; - } - return sqlite3LocateTable(pParse, isView, p->zName, zDb); -} - -/* -** Locate the in-memory structure that describes -** a particular index given the name of that index -** and the name of the database that contains the index. -** Return NULL if not found. -** -** If zDatabase is 0, all databases are searched for the -** table and the first matching index is returned. (No checking -** for duplicate index names is done.) The search order is -** TEMP first, then MAIN, then any auxiliary databases added -** using the ATTACH command. -*/ -SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){ - Index *p = 0; - int i; - int nName = sqlite3Strlen30(zName); - /* All mutexes are required for schema access. Make sure we hold them. */ - assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); - for(i=OMIT_TEMPDB; inDb; i++){ - int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ - Schema *pSchema = db->aDb[j].pSchema; - assert( pSchema ); - if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue; - assert( sqlite3SchemaMutexHeld(db, j, 0) ); - p = sqlite3HashFind(&pSchema->idxHash, zName, nName); - if( p ) break; - } - return p; -} - -/* -** Reclaim the memory used by an index -*/ -static void freeIndex(sqlite3 *db, Index *p){ -#ifndef SQLITE_OMIT_ANALYZE - sqlite3DeleteIndexSamples(db, p); -#endif - sqlite3DbFree(db, p->zColAff); - sqlite3DbFree(db, p); -} - -/* -** For the index called zIdxName which is found in the database iDb, -** unlike that index from its Table then remove the index from -** the index hash table and free all memory structures associated -** with the index. -*/ -SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ - Index *pIndex; - int len; - Hash *pHash; - - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - pHash = &db->aDb[iDb].pSchema->idxHash; - len = sqlite3Strlen30(zIdxName); - pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0); - if( ALWAYS(pIndex) ){ - if( pIndex->pTable->pIndex==pIndex ){ - pIndex->pTable->pIndex = pIndex->pNext; - }else{ - Index *p; - /* Justification of ALWAYS(); The index must be on the list of - ** indices. */ - p = pIndex->pTable->pIndex; - while( ALWAYS(p) && p->pNext!=pIndex ){ p = p->pNext; } - if( ALWAYS(p && p->pNext==pIndex) ){ - p->pNext = pIndex->pNext; - } - } - freeIndex(db, pIndex); - } - db->flags |= SQLITE_InternChanges; -} - -/* -** Look through the list of open database files in db->aDb[] and if -** any have been closed, remove them from the list. Reallocate the -** db->aDb[] structure to a smaller size, if possible. -** -** Entry 0 (the "main" database) and entry 1 (the "temp" database) -** are never candidates for being collapsed. -*/ -SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3 *db){ - int i, j; - for(i=j=2; inDb; i++){ - struct Db *pDb = &db->aDb[i]; - if( pDb->pBt==0 ){ - sqlite3DbFree(db, pDb->zName); - pDb->zName = 0; - continue; - } - if( jaDb[j] = db->aDb[i]; - } - j++; - } - memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j])); - db->nDb = j; - if( db->nDb<=2 && db->aDb!=db->aDbStatic ){ - memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0])); - sqlite3DbFree(db, db->aDb); - db->aDb = db->aDbStatic; - } -} - -/* -** Reset the schema for the database at index iDb. Also reset the -** TEMP schema. -*/ -SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3 *db, int iDb){ - Db *pDb; - assert( iDbnDb ); - - /* Case 1: Reset the single schema identified by iDb */ - pDb = &db->aDb[iDb]; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - assert( pDb->pSchema!=0 ); - sqlite3SchemaClear(pDb->pSchema); - - /* If any database other than TEMP is reset, then also reset TEMP - ** since TEMP might be holding triggers that reference tables in the - ** other database. - */ - if( iDb!=1 ){ - pDb = &db->aDb[1]; - assert( pDb->pSchema!=0 ); - sqlite3SchemaClear(pDb->pSchema); - } - return; -} - -/* -** Erase all schema information from all attached databases (including -** "main" and "temp") for a single database connection. -*/ -SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){ - int i; - sqlite3BtreeEnterAll(db); - for(i=0; inDb; i++){ - Db *pDb = &db->aDb[i]; - if( pDb->pSchema ){ - sqlite3SchemaClear(pDb->pSchema); - } - } - db->flags &= ~SQLITE_InternChanges; - sqlite3VtabUnlockList(db); - sqlite3BtreeLeaveAll(db); - sqlite3CollapseDatabaseArray(db); -} - -/* -** This routine is called when a commit occurs. -*/ -SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3 *db){ - db->flags &= ~SQLITE_InternChanges; -} - -/* -** Delete memory allocated for the column names of a table or view (the -** Table.aCol[] array). -*/ -static void sqliteDeleteColumnNames(sqlite3 *db, Table *pTable){ - int i; - Column *pCol; - assert( pTable!=0 ); - if( (pCol = pTable->aCol)!=0 ){ - for(i=0; inCol; i++, pCol++){ - sqlite3DbFree(db, pCol->zName); - sqlite3ExprDelete(db, pCol->pDflt); - sqlite3DbFree(db, pCol->zDflt); - sqlite3DbFree(db, pCol->zType); - sqlite3DbFree(db, pCol->zColl); - } - sqlite3DbFree(db, pTable->aCol); - } -} - -/* -** Remove the memory data structures associated with the given -** Table. No changes are made to disk by this routine. -** -** This routine just deletes the data structure. It does not unlink -** the table data structure from the hash table. But it does destroy -** memory structures of the indices and foreign keys associated with -** the table. -** -** The db parameter is optional. It is needed if the Table object -** contains lookaside memory. (Table objects in the schema do not use -** lookaside memory, but some ephemeral Table objects do.) Or the -** db parameter can be used with db->pnBytesFreed to measure the memory -** used by the Table object. -*/ -SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ - Index *pIndex, *pNext; - TESTONLY( int nLookaside; ) /* Used to verify lookaside not used for schema */ - - assert( !pTable || pTable->nRef>0 ); - - /* Do not delete the table until the reference count reaches zero. */ - if( !pTable ) return; - if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return; - - /* Record the number of outstanding lookaside allocations in schema Tables - ** prior to doing any free() operations. Since schema Tables do not use - ** lookaside, this number should not change. */ - TESTONLY( nLookaside = (db && (pTable->tabFlags & TF_Ephemeral)==0) ? - db->lookaside.nOut : 0 ); - - /* Delete all indices associated with this table. */ - for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ - pNext = pIndex->pNext; - assert( pIndex->pSchema==pTable->pSchema ); - if( !db || db->pnBytesFreed==0 ){ - char *zName = pIndex->zName; - TESTONLY ( Index *pOld = ) sqlite3HashInsert( - &pIndex->pSchema->idxHash, zName, sqlite3Strlen30(zName), 0 - ); - assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); - assert( pOld==pIndex || pOld==0 ); - } - freeIndex(db, pIndex); - } - - /* Delete any foreign keys attached to this table. */ - sqlite3FkDelete(db, pTable); - - /* Delete the Table structure itself. - */ - sqliteDeleteColumnNames(db, pTable); - sqlite3DbFree(db, pTable->zName); - sqlite3DbFree(db, pTable->zColAff); - sqlite3SelectDelete(db, pTable->pSelect); -#ifndef SQLITE_OMIT_CHECK - sqlite3ExprListDelete(db, pTable->pCheck); -#endif -#ifndef SQLITE_OMIT_VIRTUALTABLE - sqlite3VtabClear(db, pTable); -#endif - sqlite3DbFree(db, pTable); - - /* Verify that no lookaside memory was used by schema tables */ - assert( nLookaside==0 || nLookaside==db->lookaside.nOut ); -} - -/* -** Unlink the given table from the hash tables and the delete the -** table structure with all its indices and foreign keys. -*/ -SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){ - Table *p; - Db *pDb; - - assert( db!=0 ); - assert( iDb>=0 && iDbnDb ); - assert( zTabName ); - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */ - pDb = &db->aDb[iDb]; - p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, - sqlite3Strlen30(zTabName),0); - sqlite3DeleteTable(db, p); - db->flags |= SQLITE_InternChanges; -} - -/* -** Given a token, return a string that consists of the text of that -** token. Space to hold the returned string -** is obtained from sqliteMalloc() and must be freed by the calling -** function. -** -** Any quotation marks (ex: "name", 'name', [name], or `name`) that -** surround the body of the token are removed. -** -** Tokens are often just pointers into the original SQL text and so -** are not \000 terminated and are not persistent. The returned string -** is \000 terminated and is persistent. -*/ -SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, Token *pName){ - char *zName; - if( pName ){ - zName = sqlite3DbStrNDup(db, (char*)pName->z, pName->n); - sqlite3Dequote(zName); - }else{ - zName = 0; - } - return zName; -} - -/* -** Open the sqlite_master table stored in database number iDb for -** writing. The table is opened using cursor 0. -*/ -SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *p, int iDb){ - Vdbe *v = sqlite3GetVdbe(p); - sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb)); - sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb); - sqlite3VdbeChangeP4(v, -1, (char *)5, P4_INT32); /* 5 column table */ - if( p->nTab==0 ){ - p->nTab = 1; - } -} - -/* -** Parameter zName points to a nul-terminated buffer containing the name -** of a database ("main", "temp" or the name of an attached db). This -** function returns the index of the named database in db->aDb[], or -** -1 if the named db cannot be found. -*/ -SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *db, const char *zName){ - int i = -1; /* Database number */ - if( zName ){ - Db *pDb; - int n = sqlite3Strlen30(zName); - for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){ - if( (!OMIT_TEMPDB || i!=1 ) && n==sqlite3Strlen30(pDb->zName) && - 0==sqlite3StrICmp(pDb->zName, zName) ){ - break; - } - } - } - return i; -} - -/* -** The token *pName contains the name of a database (either "main" or -** "temp" or the name of an attached db). This routine returns the -** index of the named database in db->aDb[], or -1 if the named db -** does not exist. -*/ -SQLITE_PRIVATE int sqlite3FindDb(sqlite3 *db, Token *pName){ - int i; /* Database number */ - char *zName; /* Name we are searching for */ - zName = sqlite3NameFromToken(db, pName); - i = sqlite3FindDbName(db, zName); - sqlite3DbFree(db, zName); - return i; -} - -/* The table or view or trigger name is passed to this routine via tokens -** pName1 and pName2. If the table name was fully qualified, for example: -** -** CREATE TABLE xxx.yyy (...); -** -** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if -** the table name is not fully qualified, i.e.: -** -** CREATE TABLE yyy(...); -** -** Then pName1 is set to "yyy" and pName2 is "". -** -** This routine sets the *ppUnqual pointer to point at the token (pName1 or -** pName2) that stores the unqualified table name. The index of the -** database "xxx" is returned. -*/ -SQLITE_PRIVATE int sqlite3TwoPartName( - Parse *pParse, /* Parsing and code generating context */ - Token *pName1, /* The "xxx" in the name "xxx.yyy" or "xxx" */ - Token *pName2, /* The "yyy" in the name "xxx.yyy" */ - Token **pUnqual /* Write the unqualified object name here */ -){ - int iDb; /* Database holding the object */ - sqlite3 *db = pParse->db; - - if( ALWAYS(pName2!=0) && pName2->n>0 ){ - if( db->init.busy ) { - sqlite3ErrorMsg(pParse, "corrupt database"); - pParse->nErr++; - return -1; - } - *pUnqual = pName2; - iDb = sqlite3FindDb(db, pName1); - if( iDb<0 ){ - sqlite3ErrorMsg(pParse, "unknown database %T", pName1); - pParse->nErr++; - return -1; - } - }else{ - assert( db->init.iDb==0 || db->init.busy ); - iDb = db->init.iDb; - *pUnqual = pName1; - } - return iDb; -} - -/* -** This routine is used to check if the UTF-8 string zName is a legal -** unqualified name for a new schema object (table, index, view or -** trigger). All names are legal except those that begin with the string -** "sqlite_" (in upper, lower or mixed case). This portion of the namespace -** is reserved for internal use. -*/ -SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *pParse, const char *zName){ - if( !pParse->db->init.busy && pParse->nested==0 - && (pParse->db->flags & SQLITE_WriteSchema)==0 - && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ - sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName); - return SQLITE_ERROR; - } - return SQLITE_OK; -} - -/* -** Begin constructing a new table representation in memory. This is -** the first of several action routines that get called in response -** to a CREATE TABLE statement. In particular, this routine is called -** after seeing tokens "CREATE" and "TABLE" and the table name. The isTemp -** flag is true if the table should be stored in the auxiliary database -** file instead of in the main database file. This is normally the case -** when the "TEMP" or "TEMPORARY" keyword occurs in between -** CREATE and TABLE. -** -** The new table record is initialized and put in pParse->pNewTable. -** As more of the CREATE TABLE statement is parsed, additional action -** routines will be called to add more information to this record. -** At the end of the CREATE TABLE statement, the sqlite3EndTable() routine -** is called to complete the construction of the new table record. -*/ -SQLITE_PRIVATE void sqlite3StartTable( - Parse *pParse, /* Parser context */ - Token *pName1, /* First part of the name of the table or view */ - Token *pName2, /* Second part of the name of the table or view */ - int isTemp, /* True if this is a TEMP table */ - int isView, /* True if this is a VIEW */ - int isVirtual, /* True if this is a VIRTUAL table */ - int noErr /* Do nothing if table already exists */ -){ - Table *pTable; - char *zName = 0; /* The name of the new table */ - sqlite3 *db = pParse->db; - Vdbe *v; - int iDb; /* Database number to create the table in */ - Token *pName; /* Unqualified name of the table to create */ - - /* The table or view name to create is passed to this routine via tokens - ** pName1 and pName2. If the table name was fully qualified, for example: - ** - ** CREATE TABLE xxx.yyy (...); - ** - ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if - ** the table name is not fully qualified, i.e.: - ** - ** CREATE TABLE yyy(...); - ** - ** Then pName1 is set to "yyy" and pName2 is "". - ** - ** The call below sets the pName pointer to point at the token (pName1 or - ** pName2) that stores the unqualified table name. The variable iDb is - ** set to the index of the database that the table or view is to be - ** created in. - */ - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); - if( iDb<0 ) return; - if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){ - /* If creating a temp table, the name may not be qualified. Unless - ** the database name is "temp" anyway. */ - sqlite3ErrorMsg(pParse, "temporary table name must be unqualified"); - return; - } - if( !OMIT_TEMPDB && isTemp ) iDb = 1; - - pParse->sNameToken = *pName; - zName = sqlite3NameFromToken(db, pName); - if( zName==0 ) return; - if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ - goto begin_table_error; - } - if( db->init.iDb==1 ) isTemp = 1; -#ifndef SQLITE_OMIT_AUTHORIZATION - assert( (isTemp & 1)==isTemp ); - { - int code; - char *zDb = db->aDb[iDb].zName; - if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){ - goto begin_table_error; - } - if( isView ){ - if( !OMIT_TEMPDB && isTemp ){ - code = SQLITE_CREATE_TEMP_VIEW; - }else{ - code = SQLITE_CREATE_VIEW; - } - }else{ - if( !OMIT_TEMPDB && isTemp ){ - code = SQLITE_CREATE_TEMP_TABLE; - }else{ - code = SQLITE_CREATE_TABLE; - } - } - if( !isVirtual && sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){ - goto begin_table_error; - } - } -#endif - - /* Make sure the new table name does not collide with an existing - ** index or table name in the same database. Issue an error message if - ** it does. The exception is if the statement being parsed was passed - ** to an sqlite3_declare_vtab() call. In that case only the column names - ** and types will be used, so there is no need to test for namespace - ** collisions. - */ - if( !IN_DECLARE_VTAB ){ - char *zDb = db->aDb[iDb].zName; - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - goto begin_table_error; - } - pTable = sqlite3FindTable(db, zName, zDb); - if( pTable ){ - if( !noErr ){ - sqlite3ErrorMsg(pParse, "table %T already exists", pName); - }else{ - assert( !db->init.busy ); - sqlite3CodeVerifySchema(pParse, iDb); - } - goto begin_table_error; - } - if( sqlite3FindIndex(db, zName, zDb)!=0 ){ - sqlite3ErrorMsg(pParse, "there is already an index named %s", zName); - goto begin_table_error; - } - } - - pTable = sqlite3DbMallocZero(db, sizeof(Table)); - if( pTable==0 ){ - db->mallocFailed = 1; - pParse->rc = SQLITE_NOMEM; - pParse->nErr++; - goto begin_table_error; - } - pTable->zName = zName; - pTable->iPKey = -1; - pTable->pSchema = db->aDb[iDb].pSchema; - pTable->nRef = 1; - pTable->nRowEst = 1000000; - assert( pParse->pNewTable==0 ); - pParse->pNewTable = pTable; - - /* If this is the magic sqlite_sequence table used by autoincrement, - ** then record a pointer to this table in the main database structure - ** so that INSERT can find the table easily. - */ -#ifndef SQLITE_OMIT_AUTOINCREMENT - if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - pTable->pSchema->pSeqTab = pTable; - } -#endif - - /* Begin generating the code that will insert the table record into - ** the SQLITE_MASTER table. Note in particular that we must go ahead - ** and allocate the record number for the table entry now. Before any - ** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause - ** indices to be created and the table record must come before the - ** indices. Hence, the record number for the table must be allocated - ** now. - */ - if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){ - int j1; - int fileFormat; - int reg1, reg2, reg3; - sqlite3BeginWriteOperation(pParse, 0, iDb); - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( isVirtual ){ - sqlite3VdbeAddOp0(v, OP_VBegin); - } -#endif - - /* If the file format and encoding in the database have not been set, - ** set them now. - */ - reg1 = pParse->regRowid = ++pParse->nMem; - reg2 = pParse->regRoot = ++pParse->nMem; - reg3 = ++pParse->nMem; - sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT); - sqlite3VdbeUsesBtree(v, iDb); - j1 = sqlite3VdbeAddOp1(v, OP_If, reg3); - fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ? - 1 : SQLITE_MAX_FILE_FORMAT; - sqlite3VdbeAddOp2(v, OP_Integer, fileFormat, reg3); - sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, reg3); - sqlite3VdbeAddOp2(v, OP_Integer, ENC(db), reg3); - sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, reg3); - sqlite3VdbeJumpHere(v, j1); - - /* This just creates a place-holder record in the sqlite_master table. - ** The record created does not contain anything yet. It will be replaced - ** by the real entry in code generated at sqlite3EndTable(). - ** - ** The rowid for the new entry is left in register pParse->regRowid. - ** The root page number of the new table is left in reg pParse->regRoot. - ** The rowid and root page number values are needed by the code that - ** sqlite3EndTable will generate. - */ -#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) - if( isView || isVirtual ){ - sqlite3VdbeAddOp2(v, OP_Integer, 0, reg2); - }else -#endif - { - sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2); - } - sqlite3OpenMasterTable(pParse, iDb); - sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1); - sqlite3VdbeAddOp2(v, OP_Null, 0, reg3); - sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1); - sqlite3VdbeChangeP5(v, OPFLAG_APPEND); - sqlite3VdbeAddOp0(v, OP_Close); - } - - /* Normal (non-error) return. */ - return; - - /* If an error occurs, we jump here */ -begin_table_error: - sqlite3DbFree(db, zName); - return; -} - -/* -** This macro is used to compare two strings in a case-insensitive manner. -** It is slightly faster than calling sqlite3StrICmp() directly, but -** produces larger code. -** -** WARNING: This macro is not compatible with the strcmp() family. It -** returns true if the two strings are equal, otherwise false. -*/ -#define STRICMP(x, y) (\ -sqlite3UpperToLower[*(unsigned char *)(x)]== \ -sqlite3UpperToLower[*(unsigned char *)(y)] \ -&& sqlite3StrICmp((x)+1,(y)+1)==0 ) - -/* -** Add a new column to the table currently being constructed. -** -** The parser calls this routine once for each column declaration -** in a CREATE TABLE statement. sqlite3StartTable() gets called -** first to get things going. Then this routine is called for each -** column. -*/ -SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName){ - Table *p; - int i; - char *z; - Column *pCol; - sqlite3 *db = pParse->db; - if( (p = pParse->pNewTable)==0 ) return; -#if SQLITE_MAX_COLUMN - if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){ - sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName); - return; - } -#endif - z = sqlite3NameFromToken(db, pName); - if( z==0 ) return; - for(i=0; inCol; i++){ - if( STRICMP(z, p->aCol[i].zName) ){ - sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); - sqlite3DbFree(db, z); - return; - } - } - if( (p->nCol & 0x7)==0 ){ - Column *aNew; - aNew = sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0])); - if( aNew==0 ){ - sqlite3DbFree(db, z); - return; - } - p->aCol = aNew; - } - pCol = &p->aCol[p->nCol]; - memset(pCol, 0, sizeof(p->aCol[0])); - pCol->zName = z; - - /* If there is no type specified, columns have the default affinity - ** 'NONE'. If there is a type specified, then sqlite3AddColumnType() will - ** be called next to set pCol->affinity correctly. - */ - pCol->affinity = SQLITE_AFF_NONE; - p->nCol++; -} - -/* -** This routine is called by the parser while in the middle of -** parsing a CREATE TABLE statement. A "NOT NULL" constraint has -** been seen on a column. This routine sets the notNull flag on -** the column currently under construction. -*/ -SQLITE_PRIVATE void sqlite3AddNotNull(Parse *pParse, int onError){ - Table *p; - p = pParse->pNewTable; - if( p==0 || NEVER(p->nCol<1) ) return; - p->aCol[p->nCol-1].notNull = (u8)onError; -} - -/* -** Scan the column type name zType (length nType) and return the -** associated affinity type. -** -** This routine does a case-independent search of zType for the -** substrings in the following table. If one of the substrings is -** found, the corresponding affinity is returned. If zType contains -** more than one of the substrings, entries toward the top of -** the table take priority. For example, if zType is 'BLOBINT', -** SQLITE_AFF_INTEGER is returned. -** -** Substring | Affinity -** -------------------------------- -** 'INT' | SQLITE_AFF_INTEGER -** 'CHAR' | SQLITE_AFF_TEXT -** 'CLOB' | SQLITE_AFF_TEXT -** 'TEXT' | SQLITE_AFF_TEXT -** 'BLOB' | SQLITE_AFF_NONE -** 'REAL' | SQLITE_AFF_REAL -** 'FLOA' | SQLITE_AFF_REAL -** 'DOUB' | SQLITE_AFF_REAL -** -** If none of the substrings in the above table are found, -** SQLITE_AFF_NUMERIC is returned. -*/ -SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn){ - u32 h = 0; - char aff = SQLITE_AFF_NUMERIC; - - if( zIn ) while( zIn[0] ){ - h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff]; - zIn++; - if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */ - aff = SQLITE_AFF_TEXT; - }else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){ /* CLOB */ - aff = SQLITE_AFF_TEXT; - }else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){ /* TEXT */ - aff = SQLITE_AFF_TEXT; - }else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b') /* BLOB */ - && (aff==SQLITE_AFF_NUMERIC || aff==SQLITE_AFF_REAL) ){ - aff = SQLITE_AFF_NONE; -#ifndef SQLITE_OMIT_FLOATING_POINT - }else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l') /* REAL */ - && aff==SQLITE_AFF_NUMERIC ){ - aff = SQLITE_AFF_REAL; - }else if( h==(('f'<<24)+('l'<<16)+('o'<<8)+'a') /* FLOA */ - && aff==SQLITE_AFF_NUMERIC ){ - aff = SQLITE_AFF_REAL; - }else if( h==(('d'<<24)+('o'<<16)+('u'<<8)+'b') /* DOUB */ - && aff==SQLITE_AFF_NUMERIC ){ - aff = SQLITE_AFF_REAL; -#endif - }else if( (h&0x00FFFFFF)==(('i'<<16)+('n'<<8)+'t') ){ /* INT */ - aff = SQLITE_AFF_INTEGER; - break; - } - } - - return aff; -} - -/* -** This routine is called by the parser while in the middle of -** parsing a CREATE TABLE statement. The pFirst token is the first -** token in the sequence of tokens that describe the type of the -** column currently under construction. pLast is the last token -** in the sequence. Use this information to construct a string -** that contains the typename of the column and store that string -** in zType. -*/ -SQLITE_PRIVATE void sqlite3AddColumnType(Parse *pParse, Token *pType){ - Table *p; - Column *pCol; - - p = pParse->pNewTable; - if( p==0 || NEVER(p->nCol<1) ) return; - pCol = &p->aCol[p->nCol-1]; - assert( pCol->zType==0 ); - pCol->zType = sqlite3NameFromToken(pParse->db, pType); - pCol->affinity = sqlite3AffinityType(pCol->zType); -} - -/* -** The expression is the default value for the most recently added column -** of the table currently under construction. -** -** Default value expressions must be constant. Raise an exception if this -** is not the case. -** -** This routine is called by the parser while in the middle of -** parsing a CREATE TABLE statement. -*/ -SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){ - Table *p; - Column *pCol; - sqlite3 *db = pParse->db; - p = pParse->pNewTable; - if( p!=0 ){ - pCol = &(p->aCol[p->nCol-1]); - if( !sqlite3ExprIsConstantOrFunction(pSpan->pExpr) ){ - sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant", - pCol->zName); - }else{ - /* A copy of pExpr is used instead of the original, as pExpr contains - ** tokens that point to volatile memory. The 'span' of the expression - ** is required by pragma table_info. - */ - sqlite3ExprDelete(db, pCol->pDflt); - pCol->pDflt = sqlite3ExprDup(db, pSpan->pExpr, EXPRDUP_REDUCE); - sqlite3DbFree(db, pCol->zDflt); - pCol->zDflt = sqlite3DbStrNDup(db, (char*)pSpan->zStart, - (int)(pSpan->zEnd - pSpan->zStart)); - } - } - sqlite3ExprDelete(db, pSpan->pExpr); -} - -/* -** Designate the PRIMARY KEY for the table. pList is a list of names -** of columns that form the primary key. If pList is NULL, then the -** most recently added column of the table is the primary key. -** -** A table can have at most one primary key. If the table already has -** a primary key (and this is the second primary key) then create an -** error. -** -** If the PRIMARY KEY is on a single column whose datatype is INTEGER, -** then we will try to use that column as the rowid. Set the Table.iPKey -** field of the table under construction to be the index of the -** INTEGER PRIMARY KEY column. Table.iPKey is set to -1 if there is -** no INTEGER PRIMARY KEY. -** -** If the key is not an INTEGER PRIMARY KEY, then create a unique -** index for the key. No index is created for INTEGER PRIMARY KEYs. -*/ -SQLITE_PRIVATE void sqlite3AddPrimaryKey( - Parse *pParse, /* Parsing context */ - ExprList *pList, /* List of field names to be indexed */ - int onError, /* What to do with a uniqueness conflict */ - int autoInc, /* True if the AUTOINCREMENT keyword is present */ - int sortOrder /* SQLITE_SO_ASC or SQLITE_SO_DESC */ -){ - Table *pTab = pParse->pNewTable; - char *zType = 0; - int iCol = -1, i; - if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit; - if( pTab->tabFlags & TF_HasPrimaryKey ){ - sqlite3ErrorMsg(pParse, - "table \"%s\" has more than one primary key", pTab->zName); - goto primary_key_exit; - } - pTab->tabFlags |= TF_HasPrimaryKey; - if( pList==0 ){ - iCol = pTab->nCol - 1; - pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY; - }else{ - for(i=0; inExpr; i++){ - for(iCol=0; iColnCol; iCol++){ - if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){ - break; - } - } - if( iColnCol ){ - pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY; - } - } - if( pList->nExpr>1 ) iCol = -1; - } - if( iCol>=0 && iColnCol ){ - zType = pTab->aCol[iCol].zType; - } - if( zType && sqlite3StrICmp(zType, "INTEGER")==0 - && sortOrder==SQLITE_SO_ASC ){ - pTab->iPKey = iCol; - pTab->keyConf = (u8)onError; - assert( autoInc==0 || autoInc==1 ); - pTab->tabFlags |= autoInc*TF_Autoincrement; - }else if( autoInc ){ -#ifndef SQLITE_OMIT_AUTOINCREMENT - sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an " - "INTEGER PRIMARY KEY"); -#endif - }else{ - Index *p; - p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0); - if( p ){ - p->autoIndex = 2; - } - pList = 0; - } - -primary_key_exit: - sqlite3ExprListDelete(pParse->db, pList); - return; -} - -/* -** Add a new CHECK constraint to the table currently under construction. -*/ -SQLITE_PRIVATE void sqlite3AddCheckConstraint( - Parse *pParse, /* Parsing context */ - Expr *pCheckExpr /* The check expression */ -){ -#ifndef SQLITE_OMIT_CHECK - Table *pTab = pParse->pNewTable; - if( pTab && !IN_DECLARE_VTAB ){ - pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr); - if( pParse->constraintName.n ){ - sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1); - } - }else -#endif - { - sqlite3ExprDelete(pParse->db, pCheckExpr); - } -} - -/* -** Set the collation function of the most recently parsed table column -** to the CollSeq given. -*/ -SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){ - Table *p; - int i; - char *zColl; /* Dequoted name of collation sequence */ - sqlite3 *db; - - if( (p = pParse->pNewTable)==0 ) return; - i = p->nCol-1; - db = pParse->db; - zColl = sqlite3NameFromToken(db, pToken); - if( !zColl ) return; - - if( sqlite3LocateCollSeq(pParse, zColl) ){ - Index *pIdx; - p->aCol[i].zColl = zColl; - - /* If the column is declared as " PRIMARY KEY COLLATE ", - ** then an index may have been created on this column before the - ** collation type was added. Correct this if it is the case. - */ - for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ - assert( pIdx->nColumn==1 ); - if( pIdx->aiColumn[0]==i ){ - pIdx->azColl[0] = p->aCol[i].zColl; - } - } - }else{ - sqlite3DbFree(db, zColl); - } -} - -/* -** This function returns the collation sequence for database native text -** encoding identified by the string zName, length nName. -** -** If the requested collation sequence is not available, or not available -** in the database native encoding, the collation factory is invoked to -** request it. If the collation factory does not supply such a sequence, -** and the sequence is available in another text encoding, then that is -** returned instead. -** -** If no versions of the requested collations sequence are available, or -** another error occurs, NULL is returned and an error message written into -** pParse. -** -** This routine is a wrapper around sqlite3FindCollSeq(). This routine -** invokes the collation factory if the named collation cannot be found -** and generates an error message. -** -** See also: sqlite3FindCollSeq(), sqlite3GetCollSeq() -*/ -SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){ - sqlite3 *db = pParse->db; - u8 enc = ENC(db); - u8 initbusy = db->init.busy; - CollSeq *pColl; - - pColl = sqlite3FindCollSeq(db, enc, zName, initbusy); - if( !initbusy && (!pColl || !pColl->xCmp) ){ - pColl = sqlite3GetCollSeq(pParse, enc, pColl, zName); - } - - return pColl; -} - - -/* -** Generate code that will increment the schema cookie. -** -** The schema cookie is used to determine when the schema for the -** database changes. After each schema change, the cookie value -** changes. When a process first reads the schema it records the -** cookie. Thereafter, whenever it goes to access the database, -** it checks the cookie to make sure the schema has not changed -** since it was last read. -** -** This plan is not completely bullet-proof. It is possible for -** the schema to change multiple times and for the cookie to be -** set back to prior value. But schema changes are infrequent -** and the probability of hitting the same cookie value is only -** 1 chance in 2^32. So we're safe enough. -*/ -SQLITE_PRIVATE void sqlite3ChangeCookie(Parse *pParse, int iDb){ - int r1 = sqlite3GetTempReg(pParse); - sqlite3 *db = pParse->db; - Vdbe *v = pParse->pVdbe; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - sqlite3VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1); - sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1); - sqlite3ReleaseTempReg(pParse, r1); -} - -/* -** Measure the number of characters needed to output the given -** identifier. The number returned includes any quotes used -** but does not include the null terminator. -** -** The estimate is conservative. It might be larger that what is -** really needed. -*/ -static int identLength(const char *z){ - int n; - for(n=0; *z; n++, z++){ - if( *z=='"' ){ n++; } - } - return n + 2; -} - -/* -** The first parameter is a pointer to an output buffer. The second -** parameter is a pointer to an integer that contains the offset at -** which to write into the output buffer. This function copies the -** nul-terminated string pointed to by the third parameter, zSignedIdent, -** to the specified offset in the buffer and updates *pIdx to refer -** to the first byte after the last byte written before returning. -** -** If the string zSignedIdent consists entirely of alpha-numeric -** characters, does not begin with a digit and is not an SQL keyword, -** then it is copied to the output buffer exactly as it is. Otherwise, -** it is quoted using double-quotes. -*/ -static void identPut(char *z, int *pIdx, char *zSignedIdent){ - unsigned char *zIdent = (unsigned char*)zSignedIdent; - int i, j, needQuote; - i = *pIdx; - - for(j=0; zIdent[j]; j++){ - if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break; - } - needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j)!=TK_ID; - if( !needQuote ){ - needQuote = zIdent[j]; - } - - if( needQuote ) z[i++] = '"'; - for(j=0; zIdent[j]; j++){ - z[i++] = zIdent[j]; - if( zIdent[j]=='"' ) z[i++] = '"'; - } - if( needQuote ) z[i++] = '"'; - z[i] = 0; - *pIdx = i; -} - -/* -** Generate a CREATE TABLE statement appropriate for the given -** table. Memory to hold the text of the statement is obtained -** from sqliteMalloc() and must be freed by the calling function. -*/ -static char *createTableStmt(sqlite3 *db, Table *p){ - int i, k, n; - char *zStmt; - char *zSep, *zSep2, *zEnd; - Column *pCol; - n = 0; - for(pCol = p->aCol, i=0; inCol; i++, pCol++){ - n += identLength(pCol->zName) + 5; - } - n += identLength(p->zName); - if( n<50 ){ - zSep = ""; - zSep2 = ","; - zEnd = ")"; - }else{ - zSep = "\n "; - zSep2 = ",\n "; - zEnd = "\n)"; - } - n += 35 + 6*p->nCol; - zStmt = sqlite3DbMallocRaw(0, n); - if( zStmt==0 ){ - db->mallocFailed = 1; - return 0; - } - sqlite3_snprintf(n, zStmt, "CREATE TABLE "); - k = sqlite3Strlen30(zStmt); - identPut(zStmt, &k, p->zName); - zStmt[k++] = '('; - for(pCol=p->aCol, i=0; inCol; i++, pCol++){ - static const char * const azType[] = { - /* SQLITE_AFF_TEXT */ " TEXT", - /* SQLITE_AFF_NONE */ "", - /* SQLITE_AFF_NUMERIC */ " NUM", - /* SQLITE_AFF_INTEGER */ " INT", - /* SQLITE_AFF_REAL */ " REAL" - }; - int len; - const char *zType; - - sqlite3_snprintf(n-k, &zStmt[k], zSep); - k += sqlite3Strlen30(&zStmt[k]); - zSep = zSep2; - identPut(zStmt, &k, pCol->zName); - assert( pCol->affinity-SQLITE_AFF_TEXT >= 0 ); - assert( pCol->affinity-SQLITE_AFF_TEXT < ArraySize(azType) ); - testcase( pCol->affinity==SQLITE_AFF_TEXT ); - testcase( pCol->affinity==SQLITE_AFF_NONE ); - testcase( pCol->affinity==SQLITE_AFF_NUMERIC ); - testcase( pCol->affinity==SQLITE_AFF_INTEGER ); - testcase( pCol->affinity==SQLITE_AFF_REAL ); - - zType = azType[pCol->affinity - SQLITE_AFF_TEXT]; - len = sqlite3Strlen30(zType); - assert( pCol->affinity==SQLITE_AFF_NONE - || pCol->affinity==sqlite3AffinityType(zType) ); - memcpy(&zStmt[k], zType, len); - k += len; - assert( k<=n ); - } - sqlite3_snprintf(n-k, &zStmt[k], "%s", zEnd); - return zStmt; -} - -/* -** This routine is called to report the final ")" that terminates -** a CREATE TABLE statement. -** -** The table structure that other action routines have been building -** is added to the internal hash tables, assuming no errors have -** occurred. -** -** An entry for the table is made in the master table on disk, unless -** this is a temporary table or db->init.busy==1. When db->init.busy==1 -** it means we are reading the sqlite_master table because we just -** connected to the database or because the sqlite_master table has -** recently changed, so the entry for this table already exists in -** the sqlite_master table. We do not want to create it again. -** -** If the pSelect argument is not NULL, it means that this routine -** was called to create a table generated from a -** "CREATE TABLE ... AS SELECT ..." statement. The column names of -** the new table will match the result set of the SELECT. -*/ -SQLITE_PRIVATE void sqlite3EndTable( - Parse *pParse, /* Parse context */ - Token *pCons, /* The ',' token after the last column defn. */ - Token *pEnd, /* The final ')' token in the CREATE TABLE */ - Select *pSelect /* Select from a "CREATE ... AS SELECT" */ -){ - Table *p; - sqlite3 *db = pParse->db; - int iDb; - - if( (pEnd==0 && pSelect==0) || db->mallocFailed ){ - return; - } - p = pParse->pNewTable; - if( p==0 ) return; - - assert( !db->init.busy || !pSelect ); - - iDb = sqlite3SchemaToIndex(db, p->pSchema); - -#ifndef SQLITE_OMIT_CHECK - /* Resolve names in all CHECK constraint expressions. - */ - if( p->pCheck ){ - SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ - NameContext sNC; /* Name context for pParse->pNewTable */ - ExprList *pList; /* List of all CHECK constraints */ - int i; /* Loop counter */ - - memset(&sNC, 0, sizeof(sNC)); - memset(&sSrc, 0, sizeof(sSrc)); - sSrc.nSrc = 1; - sSrc.a[0].zName = p->zName; - sSrc.a[0].pTab = p; - sSrc.a[0].iCursor = -1; - sNC.pParse = pParse; - sNC.pSrcList = &sSrc; - sNC.ncFlags = NC_IsCheck; - pList = p->pCheck; - for(i=0; inExpr; i++){ - if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){ - return; - } - } - } -#endif /* !defined(SQLITE_OMIT_CHECK) */ - - /* If the db->init.busy is 1 it means we are reading the SQL off the - ** "sqlite_master" or "sqlite_temp_master" table on the disk. - ** So do not write to the disk again. Extract the root page number - ** for the table from the db->init.newTnum field. (The page number - ** should have been put there by the sqliteOpenCb routine.) - */ - if( db->init.busy ){ - p->tnum = db->init.newTnum; - } - - /* If not initializing, then create a record for the new table - ** in the SQLITE_MASTER table of the database. - ** - ** If this is a TEMPORARY table, write the entry into the auxiliary - ** file instead of into the main database file. - */ - if( !db->init.busy ){ - int n; - Vdbe *v; - char *zType; /* "view" or "table" */ - char *zType2; /* "VIEW" or "TABLE" */ - char *zStmt; /* Text of the CREATE TABLE or CREATE VIEW statement */ - - v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return; - - sqlite3VdbeAddOp1(v, OP_Close, 0); - - /* - ** Initialize zType for the new view or table. - */ - if( p->pSelect==0 ){ - /* A regular table */ - zType = "table"; - zType2 = "TABLE"; -#ifndef SQLITE_OMIT_VIEW - }else{ - /* A view */ - zType = "view"; - zType2 = "VIEW"; -#endif - } - - /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT - ** statement to populate the new table. The root-page number for the - ** new table is in register pParse->regRoot. - ** - ** Once the SELECT has been coded by sqlite3Select(), it is in a - ** suitable state to query for the column names and types to be used - ** by the new table. - ** - ** A shared-cache write-lock is not required to write to the new table, - ** as a schema-lock must have already been obtained to create it. Since - ** a schema-lock excludes all other database users, the write-lock would - ** be redundant. - */ - if( pSelect ){ - SelectDest dest; - Table *pSelTab; - - assert(pParse->nTab==1); - sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb); - sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG); - pParse->nTab = 2; - sqlite3SelectDestInit(&dest, SRT_Table, 1); - sqlite3Select(pParse, pSelect, &dest); - sqlite3VdbeAddOp1(v, OP_Close, 1); - if( pParse->nErr==0 ){ - pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect); - if( pSelTab==0 ) return; - assert( p->aCol==0 ); - p->nCol = pSelTab->nCol; - p->aCol = pSelTab->aCol; - pSelTab->nCol = 0; - pSelTab->aCol = 0; - sqlite3DeleteTable(db, pSelTab); - } - } - - /* Compute the complete text of the CREATE statement */ - if( pSelect ){ - zStmt = createTableStmt(db, p); - }else{ - n = (int)(pEnd->z - pParse->sNameToken.z) + 1; - zStmt = sqlite3MPrintf(db, - "CREATE %s %.*s", zType2, n, pParse->sNameToken.z - ); - } - - /* A slot for the record has already been allocated in the - ** SQLITE_MASTER table. We just need to update that slot with all - ** the information we've collected. - */ - sqlite3NestedParse(pParse, - "UPDATE %Q.%s " - "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " - "WHERE rowid=#%d", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), - zType, - p->zName, - p->zName, - pParse->regRoot, - zStmt, - pParse->regRowid - ); - sqlite3DbFree(db, zStmt); - sqlite3ChangeCookie(pParse, iDb); - -#ifndef SQLITE_OMIT_AUTOINCREMENT - /* Check to see if we need to create an sqlite_sequence table for - ** keeping track of autoincrement keys. - */ - if( p->tabFlags & TF_Autoincrement ){ - Db *pDb = &db->aDb[iDb]; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - if( pDb->pSchema->pSeqTab==0 ){ - sqlite3NestedParse(pParse, - "CREATE TABLE %Q.sqlite_sequence(name,seq)", - pDb->zName - ); - } - } -#endif - - /* Reparse everything to update our internal data structures */ - sqlite3VdbeAddParseSchemaOp(v, iDb, - sqlite3MPrintf(db, "tbl_name='%q'", p->zName)); - } - - - /* Add the table to the in-memory representation of the database. - */ - if( db->init.busy ){ - Table *pOld; - Schema *pSchema = p->pSchema; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, - sqlite3Strlen30(p->zName),p); - if( pOld ){ - assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ - db->mallocFailed = 1; - return; - } - pParse->pNewTable = 0; - db->flags |= SQLITE_InternChanges; - -#ifndef SQLITE_OMIT_ALTERTABLE - if( !p->pSelect ){ - const char *zName = (const char *)pParse->sNameToken.z; - int nName; - assert( !pSelect && pCons && pEnd ); - if( pCons->z==0 ){ - pCons = pEnd; - } - nName = (int)((const char *)pCons->z - zName); - p->addColOffset = 13 + sqlite3Utf8CharLen(zName, nName); - } -#endif - } -} - -#ifndef SQLITE_OMIT_VIEW -/* -** The parser calls this routine in order to create a new VIEW -*/ -SQLITE_PRIVATE void sqlite3CreateView( - Parse *pParse, /* The parsing context */ - Token *pBegin, /* The CREATE token that begins the statement */ - Token *pName1, /* The token that holds the name of the view */ - Token *pName2, /* The token that holds the name of the view */ - Select *pSelect, /* A SELECT statement that will become the new view */ - int isTemp, /* TRUE for a TEMPORARY view */ - int noErr /* Suppress error messages if VIEW already exists */ -){ - Table *p; - int n; - const char *z; - Token sEnd; - DbFixer sFix; - Token *pName = 0; - int iDb; - sqlite3 *db = pParse->db; - - if( pParse->nVar>0 ){ - sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); - sqlite3SelectDelete(db, pSelect); - return; - } - sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); - p = pParse->pNewTable; - if( p==0 || pParse->nErr ){ - sqlite3SelectDelete(db, pSelect); - return; - } - sqlite3TwoPartName(pParse, pName1, pName2, &pName); - iDb = sqlite3SchemaToIndex(db, p->pSchema); - if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName) - && sqlite3FixSelect(&sFix, pSelect) - ){ - sqlite3SelectDelete(db, pSelect); - return; - } - - /* Make a copy of the entire SELECT statement that defines the view. - ** This will force all the Expr.token.z values to be dynamically - ** allocated rather than point to the input string - which means that - ** they will persist after the current sqlite3_exec() call returns. - */ - p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); - sqlite3SelectDelete(db, pSelect); - if( db->mallocFailed ){ - return; - } - if( !db->init.busy ){ - sqlite3ViewGetColumnNames(pParse, p); - } - - /* Locate the end of the CREATE VIEW statement. Make sEnd point to - ** the end. - */ - sEnd = pParse->sLastToken; - if( ALWAYS(sEnd.z[0]!=0) && sEnd.z[0]!=';' ){ - sEnd.z += sEnd.n; - } - sEnd.n = 0; - n = (int)(sEnd.z - pBegin->z); - z = pBegin->z; - while( ALWAYS(n>0) && sqlite3Isspace(z[n-1]) ){ n--; } - sEnd.z = &z[n-1]; - sEnd.n = 1; - - /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */ - sqlite3EndTable(pParse, 0, &sEnd, 0); - return; -} -#endif /* SQLITE_OMIT_VIEW */ - -#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) -/* -** The Table structure pTable is really a VIEW. Fill in the names of -** the columns of the view in the pTable structure. Return the number -** of errors. If an error is seen leave an error message in pParse->zErrMsg. -*/ -SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ - Table *pSelTab; /* A fake table from which we get the result set */ - Select *pSel; /* Copy of the SELECT that implements the view */ - int nErr = 0; /* Number of errors encountered */ - int n; /* Temporarily holds the number of cursors assigned */ - sqlite3 *db = pParse->db; /* Database connection for malloc errors */ - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); - - assert( pTable ); - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( sqlite3VtabCallConnect(pParse, pTable) ){ - return SQLITE_ERROR; - } - if( IsVirtual(pTable) ) return 0; -#endif - -#ifndef SQLITE_OMIT_VIEW - /* A positive nCol means the columns names for this view are - ** already known. - */ - if( pTable->nCol>0 ) return 0; - - /* A negative nCol is a special marker meaning that we are currently - ** trying to compute the column names. If we enter this routine with - ** a negative nCol, it means two or more views form a loop, like this: - ** - ** CREATE VIEW one AS SELECT * FROM two; - ** CREATE VIEW two AS SELECT * FROM one; - ** - ** Actually, the error above is now caught prior to reaching this point. - ** But the following test is still important as it does come up - ** in the following: - ** - ** CREATE TABLE main.ex1(a); - ** CREATE TEMP VIEW ex1 AS SELECT a FROM ex1; - ** SELECT * FROM temp.ex1; - */ - if( pTable->nCol<0 ){ - sqlite3ErrorMsg(pParse, "view %s is circularly defined", pTable->zName); - return 1; - } - assert( pTable->nCol>=0 ); - - /* If we get this far, it means we need to compute the table names. - ** Note that the call to sqlite3ResultSetOfSelect() will expand any - ** "*" elements in the results set of the view and will assign cursors - ** to the elements of the FROM clause. But we do not want these changes - ** to be permanent. So the computation is done on a copy of the SELECT - ** statement that defines the view. - */ - assert( pTable->pSelect ); - pSel = sqlite3SelectDup(db, pTable->pSelect, 0); - if( pSel ){ - u8 enableLookaside = db->lookaside.bEnabled; - n = pParse->nTab; - sqlite3SrcListAssignCursors(pParse, pSel->pSrc); - pTable->nCol = -1; - db->lookaside.bEnabled = 0; -#ifndef SQLITE_OMIT_AUTHORIZATION - xAuth = db->xAuth; - db->xAuth = 0; - pSelTab = sqlite3ResultSetOfSelect(pParse, pSel); - db->xAuth = xAuth; -#else - pSelTab = sqlite3ResultSetOfSelect(pParse, pSel); -#endif - db->lookaside.bEnabled = enableLookaside; - pParse->nTab = n; - if( pSelTab ){ - assert( pTable->aCol==0 ); - pTable->nCol = pSelTab->nCol; - pTable->aCol = pSelTab->aCol; - pSelTab->nCol = 0; - pSelTab->aCol = 0; - sqlite3DeleteTable(db, pSelTab); - assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); - pTable->pSchema->flags |= DB_UnresetViews; - }else{ - pTable->nCol = 0; - nErr++; - } - sqlite3SelectDelete(db, pSel); - } else { - nErr++; - } -#endif /* SQLITE_OMIT_VIEW */ - return nErr; -} -#endif /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */ - -#ifndef SQLITE_OMIT_VIEW -/* -** Clear the column names from every VIEW in database idx. -*/ -static void sqliteViewResetAll(sqlite3 *db, int idx){ - HashElem *i; - assert( sqlite3SchemaMutexHeld(db, idx, 0) ); - if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; - for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ - Table *pTab = sqliteHashData(i); - if( pTab->pSelect ){ - sqliteDeleteColumnNames(db, pTab); - pTab->aCol = 0; - pTab->nCol = 0; - } - } - DbClearProperty(db, idx, DB_UnresetViews); -} -#else -# define sqliteViewResetAll(A,B) -#endif /* SQLITE_OMIT_VIEW */ - -/* -** This function is called by the VDBE to adjust the internal schema -** used by SQLite when the btree layer moves a table root page. The -** root-page of a table or index in database iDb has changed from iFrom -** to iTo. -** -** Ticket #1728: The symbol table might still contain information -** on tables and/or indices that are the process of being deleted. -** If you are unlucky, one of those deleted indices or tables might -** have the same rootpage number as the real table or index that is -** being moved. So we cannot stop searching after the first match -** because the first match might be for one of the deleted indices -** or tables and not the table/index that is actually being moved. -** We must continue looping until all tables and indices with -** rootpage==iFrom have been converted to have a rootpage of iTo -** in order to be certain that we got the right one. -*/ -#ifndef SQLITE_OMIT_AUTOVACUUM -SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){ - HashElem *pElem; - Hash *pHash; - Db *pDb; - - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - pDb = &db->aDb[iDb]; - pHash = &pDb->pSchema->tblHash; - for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){ - Table *pTab = sqliteHashData(pElem); - if( pTab->tnum==iFrom ){ - pTab->tnum = iTo; - } - } - pHash = &pDb->pSchema->idxHash; - for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){ - Index *pIdx = sqliteHashData(pElem); - if( pIdx->tnum==iFrom ){ - pIdx->tnum = iTo; - } - } -} -#endif - -/* -** Write code to erase the table with root-page iTable from database iDb. -** Also write code to modify the sqlite_master table and internal schema -** if a root-page of another table is moved by the btree-layer whilst -** erasing iTable (this can happen with an auto-vacuum database). -*/ -static void destroyRootPage(Parse *pParse, int iTable, int iDb){ - Vdbe *v = sqlite3GetVdbe(pParse); - int r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb); - sqlite3MayAbort(pParse); -#ifndef SQLITE_OMIT_AUTOVACUUM - /* OP_Destroy stores an in integer r1. If this integer - ** is non-zero, then it is the root page number of a table moved to - ** location iTable. The following code modifies the sqlite_master table to - ** reflect this. - ** - ** The "#NNN" in the SQL is a special constant that means whatever value - ** is in register NNN. See grammar rules associated with the TK_REGISTER - ** token for additional information. - */ - sqlite3NestedParse(pParse, - "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d", - pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1); -#endif - sqlite3ReleaseTempReg(pParse, r1); -} - -/* -** Write VDBE code to erase table pTab and all associated indices on disk. -** Code to update the sqlite_master tables and internal schema definitions -** in case a root-page belonging to another table is moved by the btree layer -** is also added (this can happen with an auto-vacuum database). -*/ -static void destroyTable(Parse *pParse, Table *pTab){ -#ifdef SQLITE_OMIT_AUTOVACUUM - Index *pIdx; - int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - destroyRootPage(pParse, pTab->tnum, iDb); - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - destroyRootPage(pParse, pIdx->tnum, iDb); - } -#else - /* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM - ** is not defined), then it is important to call OP_Destroy on the - ** table and index root-pages in order, starting with the numerically - ** largest root-page number. This guarantees that none of the root-pages - ** to be destroyed is relocated by an earlier OP_Destroy. i.e. if the - ** following were coded: - ** - ** OP_Destroy 4 0 - ** ... - ** OP_Destroy 5 0 - ** - ** and root page 5 happened to be the largest root-page number in the - ** database, then root page 5 would be moved to page 4 by the - ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit - ** a free-list page. - */ - int iTab = pTab->tnum; - int iDestroyed = 0; - - while( 1 ){ - Index *pIdx; - int iLargest = 0; - - if( iDestroyed==0 || iTabpIndex; pIdx; pIdx=pIdx->pNext){ - int iIdx = pIdx->tnum; - assert( pIdx->pSchema==pTab->pSchema ); - if( (iDestroyed==0 || (iIdxiLargest ){ - iLargest = iIdx; - } - } - if( iLargest==0 ){ - return; - }else{ - int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - assert( iDb>=0 && iDbdb->nDb ); - destroyRootPage(pParse, iLargest, iDb); - iDestroyed = iLargest; - } - } -#endif -} - -/* -** Remove entries from the sqlite_statN tables (for N in (1,2,3)) -** after a DROP INDEX or DROP TABLE command. -*/ -static void sqlite3ClearStatTables( - Parse *pParse, /* The parsing context */ - int iDb, /* The database number */ - const char *zType, /* "idx" or "tbl" */ - const char *zName /* Name of index or table */ -){ - int i; - const char *zDbName = pParse->db->aDb[iDb].zName; - for(i=1; i<=3; i++){ - char zTab[24]; - sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i); - if( sqlite3FindTable(pParse->db, zTab, zDbName) ){ - sqlite3NestedParse(pParse, - "DELETE FROM %Q.%s WHERE %s=%Q", - zDbName, zTab, zType, zName - ); - } - } -} - -/* -** Generate code to drop a table. -*/ -SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){ - Vdbe *v; - sqlite3 *db = pParse->db; - Trigger *pTrigger; - Db *pDb = &db->aDb[iDb]; - - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); - sqlite3BeginWriteOperation(pParse, 1, iDb); - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) ){ - sqlite3VdbeAddOp0(v, OP_VBegin); - } -#endif - - /* Drop all triggers associated with the table being dropped. Code - ** is generated to remove entries from sqlite_master and/or - ** sqlite_temp_master if required. - */ - pTrigger = sqlite3TriggerList(pParse, pTab); - while( pTrigger ){ - assert( pTrigger->pSchema==pTab->pSchema || - pTrigger->pSchema==db->aDb[1].pSchema ); - sqlite3DropTriggerPtr(pParse, pTrigger); - pTrigger = pTrigger->pNext; - } - -#ifndef SQLITE_OMIT_AUTOINCREMENT - /* Remove any entries of the sqlite_sequence table associated with - ** the table being dropped. This is done before the table is dropped - ** at the btree level, in case the sqlite_sequence table needs to - ** move as a result of the drop (can happen in auto-vacuum mode). - */ - if( pTab->tabFlags & TF_Autoincrement ){ - sqlite3NestedParse(pParse, - "DELETE FROM %Q.sqlite_sequence WHERE name=%Q", - pDb->zName, pTab->zName - ); - } -#endif - - /* Drop all SQLITE_MASTER table and index entries that refer to the - ** table. The program name loops through the master table and deletes - ** every row that refers to a table of the same name as the one being - ** dropped. Triggers are handled separately because a trigger can be - ** created in the temp database that refers to a table in another - ** database. - */ - sqlite3NestedParse(pParse, - "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'", - pDb->zName, SCHEMA_TABLE(iDb), pTab->zName); - if( !isView && !IsVirtual(pTab) ){ - destroyTable(pParse, pTab); - } - - /* Remove the table entry from SQLite's internal schema and modify - ** the schema cookie. - */ - if( IsVirtual(pTab) ){ - sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0); - } - sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); - sqlite3ChangeCookie(pParse, iDb); - sqliteViewResetAll(db, iDb); -} - -/* -** This routine is called to do the work of a DROP TABLE statement. -** pName is the name of the table to be dropped. -*/ -SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ - Table *pTab; - Vdbe *v; - sqlite3 *db = pParse->db; - int iDb; - - if( db->mallocFailed ){ - goto exit_drop_table; - } - assert( pParse->nErr==0 ); - assert( pName->nSrc==1 ); - if( noErr ) db->suppressErr++; - pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]); - if( noErr ) db->suppressErr--; - - if( pTab==0 ){ - if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); - goto exit_drop_table; - } - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - assert( iDb>=0 && iDbnDb ); - - /* If pTab is a virtual table, call ViewGetColumnNames() to ensure - ** it is initialized. - */ - if( IsVirtual(pTab) && sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto exit_drop_table; - } -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int code; - const char *zTab = SCHEMA_TABLE(iDb); - const char *zDb = db->aDb[iDb].zName; - const char *zArg2 = 0; - if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){ - goto exit_drop_table; - } - if( isView ){ - if( !OMIT_TEMPDB && iDb==1 ){ - code = SQLITE_DROP_TEMP_VIEW; - }else{ - code = SQLITE_DROP_VIEW; - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( IsVirtual(pTab) ){ - code = SQLITE_DROP_VTABLE; - zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName; -#endif - }else{ - if( !OMIT_TEMPDB && iDb==1 ){ - code = SQLITE_DROP_TEMP_TABLE; - }else{ - code = SQLITE_DROP_TABLE; - } - } - if( sqlite3AuthCheck(pParse, code, pTab->zName, zArg2, zDb) ){ - goto exit_drop_table; - } - if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ - goto exit_drop_table; - } - } -#endif - if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 - && sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){ - sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName); - goto exit_drop_table; - } - -#ifndef SQLITE_OMIT_VIEW - /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used - ** on a table. - */ - if( isView && pTab->pSelect==0 ){ - sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName); - goto exit_drop_table; - } - if( !isView && pTab->pSelect ){ - sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName); - goto exit_drop_table; - } -#endif - - /* Generate code to remove the table from the master table - ** on disk. - */ - v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3BeginWriteOperation(pParse, 1, iDb); - sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName); - sqlite3FkDropTable(pParse, pName, pTab); - sqlite3CodeDropTable(pParse, pTab, iDb, isView); - } - -exit_drop_table: - sqlite3SrcListDelete(db, pName); -} - -/* -** This routine is called to create a new foreign key on the table -** currently under construction. pFromCol determines which columns -** in the current table point to the foreign key. If pFromCol==0 then -** connect the key to the last column inserted. pTo is the name of -** the table referred to. pToCol is a list of tables in the other -** pTo table that the foreign key points to. flags contains all -** information about the conflict resolution algorithms specified -** in the ON DELETE, ON UPDATE and ON INSERT clauses. -** -** An FKey structure is created and added to the table currently -** under construction in the pParse->pNewTable field. -** -** The foreign key is set for IMMEDIATE processing. A subsequent call -** to sqlite3DeferForeignKey() might change this to DEFERRED. -*/ -SQLITE_PRIVATE void sqlite3CreateForeignKey( - Parse *pParse, /* Parsing context */ - ExprList *pFromCol, /* Columns in this table that point to other table */ - Token *pTo, /* Name of the other table */ - ExprList *pToCol, /* Columns in the other table */ - int flags /* Conflict resolution algorithms. */ -){ - sqlite3 *db = pParse->db; -#ifndef SQLITE_OMIT_FOREIGN_KEY - FKey *pFKey = 0; - FKey *pNextTo; - Table *p = pParse->pNewTable; - int nByte; - int i; - int nCol; - char *z; - - assert( pTo!=0 ); - if( p==0 || IN_DECLARE_VTAB ) goto fk_end; - if( pFromCol==0 ){ - int iCol = p->nCol-1; - if( NEVER(iCol<0) ) goto fk_end; - if( pToCol && pToCol->nExpr!=1 ){ - sqlite3ErrorMsg(pParse, "foreign key on %s" - " should reference only one column of table %T", - p->aCol[iCol].zName, pTo); - goto fk_end; - } - nCol = 1; - }else if( pToCol && pToCol->nExpr!=pFromCol->nExpr ){ - sqlite3ErrorMsg(pParse, - "number of columns in foreign key does not match the number of " - "columns in the referenced table"); - goto fk_end; - }else{ - nCol = pFromCol->nExpr; - } - nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1; - if( pToCol ){ - for(i=0; inExpr; i++){ - nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1; - } - } - pFKey = sqlite3DbMallocZero(db, nByte ); - if( pFKey==0 ){ - goto fk_end; - } - pFKey->pFrom = p; - pFKey->pNextFrom = p->pFKey; - z = (char*)&pFKey->aCol[nCol]; - pFKey->zTo = z; - memcpy(z, pTo->z, pTo->n); - z[pTo->n] = 0; - sqlite3Dequote(z); - z += pTo->n+1; - pFKey->nCol = nCol; - if( pFromCol==0 ){ - pFKey->aCol[0].iFrom = p->nCol-1; - }else{ - for(i=0; inCol; j++){ - if( sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zName)==0 ){ - pFKey->aCol[i].iFrom = j; - break; - } - } - if( j>=p->nCol ){ - sqlite3ErrorMsg(pParse, - "unknown column \"%s\" in foreign key definition", - pFromCol->a[i].zName); - goto fk_end; - } - } - } - if( pToCol ){ - for(i=0; ia[i].zName); - pFKey->aCol[i].zCol = z; - memcpy(z, pToCol->a[i].zName, n); - z[n] = 0; - z += n+1; - } - } - pFKey->isDeferred = 0; - pFKey->aAction[0] = (u8)(flags & 0xff); /* ON DELETE action */ - pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff); /* ON UPDATE action */ - - assert( sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); - pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash, - pFKey->zTo, sqlite3Strlen30(pFKey->zTo), (void *)pFKey - ); - if( pNextTo==pFKey ){ - db->mallocFailed = 1; - goto fk_end; - } - if( pNextTo ){ - assert( pNextTo->pPrevTo==0 ); - pFKey->pNextTo = pNextTo; - pNextTo->pPrevTo = pFKey; - } - - /* Link the foreign key to the table as the last step. - */ - p->pFKey = pFKey; - pFKey = 0; - -fk_end: - sqlite3DbFree(db, pFKey); -#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ - sqlite3ExprListDelete(db, pFromCol); - sqlite3ExprListDelete(db, pToCol); -} - -/* -** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED -** clause is seen as part of a foreign key definition. The isDeferred -** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE. -** The behavior of the most recently created foreign key is adjusted -** accordingly. -*/ -SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){ -#ifndef SQLITE_OMIT_FOREIGN_KEY - Table *pTab; - FKey *pFKey; - if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; - assert( isDeferred==0 || isDeferred==1 ); /* EV: R-30323-21917 */ - pFKey->isDeferred = (u8)isDeferred; -#endif -} - -/* -** Generate code that will erase and refill index *pIdx. This is -** used to initialize a newly created index or to recompute the -** content of an index in response to a REINDEX command. -** -** if memRootPage is not negative, it means that the index is newly -** created. The register specified by memRootPage contains the -** root page number of the index. If memRootPage is negative, then -** the index already exists and must be cleared before being refilled and -** the root page number of the index is taken from pIndex->tnum. -*/ -static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ - Table *pTab = pIndex->pTable; /* The table that is indexed */ - int iTab = pParse->nTab++; /* Btree cursor used for pTab */ - int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */ - int iSorter; /* Cursor opened by OpenSorter (if in use) */ - int addr1; /* Address of top of loop */ - int addr2; /* Address to jump to for next iteration */ - int tnum; /* Root page of index */ - Vdbe *v; /* Generate code into this virtual machine */ - KeyInfo *pKey; /* KeyInfo for index */ - int regRecord; /* Register holding assemblied index record */ - sqlite3 *db = pParse->db; /* The database connection */ - int iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); - -#ifndef SQLITE_OMIT_AUTHORIZATION - if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0, - db->aDb[iDb].zName ) ){ - return; - } -#endif - - /* Require a write-lock on the table to perform this operation */ - sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); - - v = sqlite3GetVdbe(pParse); - if( v==0 ) return; - if( memRootPage>=0 ){ - tnum = memRootPage; - }else{ - tnum = pIndex->tnum; - sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); - } - pKey = sqlite3IndexKeyinfo(pParse, pIndex); - sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, - (char *)pKey, P4_KEYINFO_HANDOFF); - sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); - - /* Open the sorter cursor if we are to use one. */ - iSorter = pParse->nTab++; - sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO); - - /* Open the table. Loop through all rows of the table, inserting index - ** records into the sorter. */ - sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); - addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); - regRecord = sqlite3GetTempReg(pParse); - - sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1); - sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); - sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); - sqlite3VdbeJumpHere(v, addr1); - addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); - if( pIndex->onError!=OE_None ){ - int j2 = sqlite3VdbeCurrentAddr(v) + 3; - sqlite3VdbeAddOp2(v, OP_Goto, 0, j2); - addr2 = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord); - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE, - OE_Abort, "indexed columns are not unique", P4_STATIC - ); - }else{ - addr2 = sqlite3VdbeCurrentAddr(v); - } - sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord); - sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1); - sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); - sqlite3ReleaseTempReg(pParse, regRecord); - sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); - sqlite3VdbeJumpHere(v, addr1); - - sqlite3VdbeAddOp1(v, OP_Close, iTab); - sqlite3VdbeAddOp1(v, OP_Close, iIdx); - sqlite3VdbeAddOp1(v, OP_Close, iSorter); -} - -/* -** Create a new index for an SQL table. pName1.pName2 is the name of the index -** and pTblList is the name of the table that is to be indexed. Both will -** be NULL for a primary key or an index that is created to satisfy a -** UNIQUE constraint. If pTable and pIndex are NULL, use pParse->pNewTable -** as the table to be indexed. pParse->pNewTable is a table that is -** currently being constructed by a CREATE TABLE statement. -** -** pList is a list of columns to be indexed. pList will be NULL if this -** is a primary key or unique-constraint on the most recent column added -** to the table currently under construction. -** -** If the index is created successfully, return a pointer to the new Index -** structure. This is used by sqlite3AddPrimaryKey() to mark the index -** as the tables primary key (Index.autoIndex==2). -*/ -SQLITE_PRIVATE Index *sqlite3CreateIndex( - Parse *pParse, /* All information about this parse */ - Token *pName1, /* First part of index name. May be NULL */ - Token *pName2, /* Second part of index name. May be NULL */ - SrcList *pTblName, /* Table to index. Use pParse->pNewTable if 0 */ - ExprList *pList, /* A list of columns to be indexed */ - int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - Token *pStart, /* The CREATE token that begins this statement */ - Token *pEnd, /* The ")" that closes the CREATE INDEX statement */ - int sortOrder, /* Sort order of primary key when pList==NULL */ - int ifNotExist /* Omit error if index already exists */ -){ - Index *pRet = 0; /* Pointer to return */ - Table *pTab = 0; /* Table to be indexed */ - Index *pIndex = 0; /* The index to be created */ - char *zName = 0; /* Name of the index */ - int nName; /* Number of characters in zName */ - int i, j; - Token nullId; /* Fake token for an empty ID list */ - DbFixer sFix; /* For assigning database names to pTable */ - int sortOrderMask; /* 1 to honor DESC in index. 0 to ignore. */ - sqlite3 *db = pParse->db; - Db *pDb; /* The specific table containing the indexed database */ - int iDb; /* Index of the database that is being written */ - Token *pName = 0; /* Unqualified name of the index to create */ - struct ExprList_item *pListItem; /* For looping over pList */ - int nCol; - int nExtra = 0; - char *zExtra; - - assert( pStart==0 || pEnd!=0 ); /* pEnd must be non-NULL if pStart is */ - assert( pParse->nErr==0 ); /* Never called with prior errors */ - if( db->mallocFailed || IN_DECLARE_VTAB ){ - goto exit_create_index; - } - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - goto exit_create_index; - } - - /* - ** Find the table that is to be indexed. Return early if not found. - */ - if( pTblName!=0 ){ - - /* Use the two-part index name to determine the database - ** to search for the table. 'Fix' the table name to this db - ** before looking up the table. - */ - assert( pName1 && pName2 ); - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); - if( iDb<0 ) goto exit_create_index; - assert( pName && pName->z ); - -#ifndef SQLITE_OMIT_TEMPDB - /* If the index name was unqualified, check if the table - ** is a temp table. If so, set the database to 1. Do not do this - ** if initialising a database schema. - */ - if( !db->init.busy ){ - pTab = sqlite3SrcListLookup(pParse, pTblName); - if( pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){ - iDb = 1; - } - } -#endif - - if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) && - sqlite3FixSrcList(&sFix, pTblName) - ){ - /* Because the parser constructs pTblName from a single identifier, - ** sqlite3FixSrcList can never fail. */ - assert(0); - } - pTab = sqlite3LocateTableItem(pParse, 0, &pTblName->a[0]); - assert( db->mallocFailed==0 || pTab==0 ); - if( pTab==0 ) goto exit_create_index; - assert( db->aDb[iDb].pSchema==pTab->pSchema ); - }else{ - assert( pName==0 ); - assert( pStart==0 ); - pTab = pParse->pNewTable; - if( !pTab ) goto exit_create_index; - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - } - pDb = &db->aDb[iDb]; - - assert( pTab!=0 ); - assert( pParse->nErr==0 ); - if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 - && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0 ){ - sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); - goto exit_create_index; - } -#ifndef SQLITE_OMIT_VIEW - if( pTab->pSelect ){ - sqlite3ErrorMsg(pParse, "views may not be indexed"); - goto exit_create_index; - } -#endif -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) ){ - sqlite3ErrorMsg(pParse, "virtual tables may not be indexed"); - goto exit_create_index; - } -#endif - - /* - ** Find the name of the index. Make sure there is not already another - ** index or table with the same name. - ** - ** Exception: If we are reading the names of permanent indices from the - ** sqlite_master table (because some other process changed the schema) and - ** one of the index names collides with the name of a temporary table or - ** index, then we will continue to process this index. - ** - ** If pName==0 it means that we are - ** dealing with a primary key or UNIQUE constraint. We have to invent our - ** own name. - */ - if( pName ){ - zName = sqlite3NameFromToken(db, pName); - if( zName==0 ) goto exit_create_index; - assert( pName->z!=0 ); - if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ - goto exit_create_index; - } - if( !db->init.busy ){ - if( sqlite3FindTable(db, zName, 0)!=0 ){ - sqlite3ErrorMsg(pParse, "there is already a table named %s", zName); - goto exit_create_index; - } - } - if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){ - if( !ifNotExist ){ - sqlite3ErrorMsg(pParse, "index %s already exists", zName); - }else{ - assert( !db->init.busy ); - sqlite3CodeVerifySchema(pParse, iDb); - } - goto exit_create_index; - } - }else{ - int n; - Index *pLoop; - for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){} - zName = sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", pTab->zName, n); - if( zName==0 ){ - goto exit_create_index; - } - } - - /* Check for authorization to create an index. - */ -#ifndef SQLITE_OMIT_AUTHORIZATION - { - const char *zDb = pDb->zName; - if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){ - goto exit_create_index; - } - i = SQLITE_CREATE_INDEX; - if( !OMIT_TEMPDB && iDb==1 ) i = SQLITE_CREATE_TEMP_INDEX; - if( sqlite3AuthCheck(pParse, i, zName, pTab->zName, zDb) ){ - goto exit_create_index; - } - } -#endif - - /* If pList==0, it means this routine was called to make a primary - ** key out of the last column added to the table under construction. - ** So create a fake list to simulate this. - */ - if( pList==0 ){ - nullId.z = pTab->aCol[pTab->nCol-1].zName; - nullId.n = sqlite3Strlen30((char*)nullId.z); - pList = sqlite3ExprListAppend(pParse, 0, 0); - if( pList==0 ) goto exit_create_index; - sqlite3ExprListSetName(pParse, pList, &nullId, 0); - pList->a[0].sortOrder = (u8)sortOrder; - } - - /* Figure out how many bytes of space are required to store explicitly - ** specified collation sequence names. - */ - for(i=0; inExpr; i++){ - Expr *pExpr = pList->a[i].pExpr; - if( pExpr ){ - CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr); - if( pColl ){ - nExtra += (1 + sqlite3Strlen30(pColl->zName)); - } - } - } - - /* - ** Allocate the index structure. - */ - nName = sqlite3Strlen30(zName); - nCol = pList->nExpr; - pIndex = sqlite3DbMallocZero(db, - ROUND8(sizeof(Index)) + /* Index structure */ - ROUND8(sizeof(tRowcnt)*(nCol+1)) + /* Index.aiRowEst */ - sizeof(char *)*nCol + /* Index.azColl */ - sizeof(int)*nCol + /* Index.aiColumn */ - sizeof(u8)*nCol + /* Index.aSortOrder */ - nName + 1 + /* Index.zName */ - nExtra /* Collation sequence names */ - ); - if( db->mallocFailed ){ - goto exit_create_index; - } - zExtra = (char*)pIndex; - pIndex->aiRowEst = (tRowcnt*)&zExtra[ROUND8(sizeof(Index))]; - pIndex->azColl = (char**) - ((char*)pIndex->aiRowEst + ROUND8(sizeof(tRowcnt)*nCol+1)); - assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) ); - assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) ); - pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]); - pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]); - pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]); - zExtra = (char *)(&pIndex->zName[nName+1]); - memcpy(pIndex->zName, zName, nName+1); - pIndex->pTable = pTab; - pIndex->nColumn = pList->nExpr; - pIndex->onError = (u8)onError; - pIndex->autoIndex = (u8)(pName==0); - pIndex->pSchema = db->aDb[iDb].pSchema; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - - /* Check to see if we should honor DESC requests on index columns - */ - if( pDb->pSchema->file_format>=4 ){ - sortOrderMask = -1; /* Honor DESC */ - }else{ - sortOrderMask = 0; /* Ignore DESC */ - } - - /* Scan the names of the columns of the table to be indexed and - ** load the column indices into the Index structure. Report an error - ** if any column is not found. - ** - ** TODO: Add a test to make sure that the same column is not named - ** more than once within the same index. Only the first instance of - ** the column will ever be used by the optimizer. Note that using the - ** same column more than once cannot be an error because that would - ** break backwards compatibility - it needs to be a warning. - */ - for(i=0, pListItem=pList->a; inExpr; i++, pListItem++){ - const char *zColName = pListItem->zName; - Column *pTabCol; - int requestedSortOrder; - CollSeq *pColl; /* Collating sequence */ - char *zColl; /* Collation sequence name */ - - for(j=0, pTabCol=pTab->aCol; jnCol; j++, pTabCol++){ - if( sqlite3StrICmp(zColName, pTabCol->zName)==0 ) break; - } - if( j>=pTab->nCol ){ - sqlite3ErrorMsg(pParse, "table %s has no column named %s", - pTab->zName, zColName); - pParse->checkSchema = 1; - goto exit_create_index; - } - pIndex->aiColumn[i] = j; - if( pListItem->pExpr - && (pColl = sqlite3ExprCollSeq(pParse, pListItem->pExpr))!=0 - ){ - int nColl; - zColl = pColl->zName; - nColl = sqlite3Strlen30(zColl) + 1; - assert( nExtra>=nColl ); - memcpy(zExtra, zColl, nColl); - zColl = zExtra; - zExtra += nColl; - nExtra -= nColl; - }else{ - zColl = pTab->aCol[j].zColl; - if( !zColl ){ - zColl = "BINARY"; - } - } - if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){ - goto exit_create_index; - } - pIndex->azColl[i] = zColl; - requestedSortOrder = pListItem->sortOrder & sortOrderMask; - pIndex->aSortOrder[i] = (u8)requestedSortOrder; - } - sqlite3DefaultRowEst(pIndex); - - if( pTab==pParse->pNewTable ){ - /* This routine has been called to create an automatic index as a - ** result of a PRIMARY KEY or UNIQUE clause on a column definition, or - ** a PRIMARY KEY or UNIQUE clause following the column definitions. - ** i.e. one of: - ** - ** CREATE TABLE t(x PRIMARY KEY, y); - ** CREATE TABLE t(x, y, UNIQUE(x, y)); - ** - ** Either way, check to see if the table already has such an index. If - ** so, don't bother creating this one. This only applies to - ** automatically created indices. Users can do as they wish with - ** explicit indices. - ** - ** Two UNIQUE or PRIMARY KEY constraints are considered equivalent - ** (and thus suppressing the second one) even if they have different - ** sort orders. - ** - ** If there are different collating sequences or if the columns of - ** the constraint occur in different orders, then the constraints are - ** considered distinct and both result in separate indices. - */ - Index *pIdx; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int k; - assert( pIdx->onError!=OE_None ); - assert( pIdx->autoIndex ); - assert( pIndex->onError!=OE_None ); - - if( pIdx->nColumn!=pIndex->nColumn ) continue; - for(k=0; knColumn; k++){ - const char *z1; - const char *z2; - if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break; - z1 = pIdx->azColl[k]; - z2 = pIndex->azColl[k]; - if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break; - } - if( k==pIdx->nColumn ){ - if( pIdx->onError!=pIndex->onError ){ - /* This constraint creates the same index as a previous - ** constraint specified somewhere in the CREATE TABLE statement. - ** However the ON CONFLICT clauses are different. If both this - ** constraint and the previous equivalent constraint have explicit - ** ON CONFLICT clauses this is an error. Otherwise, use the - ** explicitly specified behavior for the index. - */ - if( !(pIdx->onError==OE_Default || pIndex->onError==OE_Default) ){ - sqlite3ErrorMsg(pParse, - "conflicting ON CONFLICT clauses specified", 0); - } - if( pIdx->onError==OE_Default ){ - pIdx->onError = pIndex->onError; - } - } - goto exit_create_index; - } - } - } - - /* Link the new Index structure to its table and to the other - ** in-memory database structures. - */ - if( db->init.busy ){ - Index *p; - assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); - p = sqlite3HashInsert(&pIndex->pSchema->idxHash, - pIndex->zName, sqlite3Strlen30(pIndex->zName), - pIndex); - if( p ){ - assert( p==pIndex ); /* Malloc must have failed */ - db->mallocFailed = 1; - goto exit_create_index; - } - db->flags |= SQLITE_InternChanges; - if( pTblName!=0 ){ - pIndex->tnum = db->init.newTnum; - } - } - - /* If the db->init.busy is 0 then create the index on disk. This - ** involves writing the index into the master table and filling in the - ** index with the current table contents. - ** - ** The db->init.busy is 0 when the user first enters a CREATE INDEX - ** command. db->init.busy is 1 when a database is opened and - ** CREATE INDEX statements are read out of the master table. In - ** the latter case the index already exists on disk, which is why - ** we don't want to recreate it. - ** - ** If pTblName==0 it means this index is generated as a primary key - ** or UNIQUE constraint of a CREATE TABLE statement. Since the table - ** has just been created, it contains no data and the index initialization - ** step can be skipped. - */ - else{ /* if( db->init.busy==0 ) */ - Vdbe *v; - char *zStmt; - int iMem = ++pParse->nMem; - - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto exit_create_index; - - - /* Create the rootpage for the index - */ - sqlite3BeginWriteOperation(pParse, 1, iDb); - sqlite3VdbeAddOp2(v, OP_CreateIndex, iDb, iMem); - - /* Gather the complete text of the CREATE INDEX statement into - ** the zStmt variable - */ - if( pStart ){ - assert( pEnd!=0 ); - /* A named index with an explicit CREATE INDEX statement */ - zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s", - onError==OE_None ? "" : " UNIQUE", - (int)(pEnd->z - pName->z) + 1, - pName->z); - }else{ - /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ - /* zStmt = sqlite3MPrintf(""); */ - zStmt = 0; - } - - /* Add an entry in sqlite_master for this index - */ - sqlite3NestedParse(pParse, - "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), - pIndex->zName, - pTab->zName, - iMem, - zStmt - ); - sqlite3DbFree(db, zStmt); - - /* Fill the index with data and reparse the schema. Code an OP_Expire - ** to invalidate all pre-compiled statements. - */ - if( pTblName ){ - sqlite3RefillIndex(pParse, pIndex, iMem); - sqlite3ChangeCookie(pParse, iDb); - sqlite3VdbeAddParseSchemaOp(v, iDb, - sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); - sqlite3VdbeAddOp1(v, OP_Expire, 0); - } - } - - /* When adding an index to the list of indices for a table, make - ** sure all indices labeled OE_Replace come after all those labeled - ** OE_Ignore. This is necessary for the correct constraint check - ** processing (in sqlite3GenerateConstraintChecks()) as part of - ** UPDATE and INSERT statements. - */ - if( db->init.busy || pTblName==0 ){ - if( onError!=OE_Replace || pTab->pIndex==0 - || pTab->pIndex->onError==OE_Replace){ - pIndex->pNext = pTab->pIndex; - pTab->pIndex = pIndex; - }else{ - Index *pOther = pTab->pIndex; - while( pOther->pNext && pOther->pNext->onError!=OE_Replace ){ - pOther = pOther->pNext; - } - pIndex->pNext = pOther->pNext; - pOther->pNext = pIndex; - } - pRet = pIndex; - pIndex = 0; - } - - /* Clean up before exiting */ -exit_create_index: - if( pIndex ){ - sqlite3DbFree(db, pIndex->zColAff); - sqlite3DbFree(db, pIndex); - } - sqlite3ExprListDelete(db, pList); - sqlite3SrcListDelete(db, pTblName); - sqlite3DbFree(db, zName); - return pRet; -} - -/* -** Fill the Index.aiRowEst[] array with default information - information -** to be used when we have not run the ANALYZE command. -** -** aiRowEst[0] is suppose to contain the number of elements in the index. -** Since we do not know, guess 1 million. aiRowEst[1] is an estimate of the -** number of rows in the table that match any particular value of the -** first column of the index. aiRowEst[2] is an estimate of the number -** of rows that match any particular combiniation of the first 2 columns -** of the index. And so forth. It must always be the case that -* -** aiRowEst[N]<=aiRowEst[N-1] -** aiRowEst[N]>=1 -** -** Apart from that, we have little to go on besides intuition as to -** how aiRowEst[] should be initialized. The numbers generated here -** are based on typical values found in actual indices. -*/ -SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){ - tRowcnt *a = pIdx->aiRowEst; - int i; - tRowcnt n; - assert( a!=0 ); - a[0] = pIdx->pTable->nRowEst; - if( a[0]<10 ) a[0] = 10; - n = 10; - for(i=1; i<=pIdx->nColumn; i++){ - a[i] = n; - if( n>5 ) n--; - } - if( pIdx->onError!=OE_None ){ - a[pIdx->nColumn] = 1; - } -} - -/* -** This routine will drop an existing named index. This routine -** implements the DROP INDEX statement. -*/ -SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ - Index *pIndex; - Vdbe *v; - sqlite3 *db = pParse->db; - int iDb; - - assert( pParse->nErr==0 ); /* Never called with prior errors */ - if( db->mallocFailed ){ - goto exit_drop_index; - } - assert( pName->nSrc==1 ); - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - goto exit_drop_index; - } - pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); - if( pIndex==0 ){ - if( !ifExists ){ - sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); - }else{ - sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); - } - pParse->checkSchema = 1; - goto exit_drop_index; - } - if( pIndex->autoIndex ){ - sqlite3ErrorMsg(pParse, "index associated with UNIQUE " - "or PRIMARY KEY constraint cannot be dropped", 0); - goto exit_drop_index; - } - iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int code = SQLITE_DROP_INDEX; - Table *pTab = pIndex->pTable; - const char *zDb = db->aDb[iDb].zName; - const char *zTab = SCHEMA_TABLE(iDb); - if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ - goto exit_drop_index; - } - if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX; - if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ - goto exit_drop_index; - } - } -#endif - - /* Generate code to remove the index and from the master table */ - v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3BeginWriteOperation(pParse, 1, iDb); - sqlite3NestedParse(pParse, - "DELETE FROM %Q.%s WHERE name=%Q AND type='index'", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName - ); - sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); - sqlite3ChangeCookie(pParse, iDb); - destroyRootPage(pParse, pIndex->tnum, iDb); - sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0); - } - -exit_drop_index: - sqlite3SrcListDelete(db, pName); -} - -/* -** pArray is a pointer to an array of objects. Each object in the -** array is szEntry bytes in size. This routine uses sqlite3DbRealloc() -** to extend the array so that there is space for a new object at the end. -** -** When this function is called, *pnEntry contains the current size of -** the array (in entries - so the allocation is ((*pnEntry) * szEntry) bytes -** in total). -** -** If the realloc() is successful (i.e. if no OOM condition occurs), the -** space allocated for the new object is zeroed, *pnEntry updated to -** reflect the new size of the array and a pointer to the new allocation -** returned. *pIdx is set to the index of the new array entry in this case. -** -** Otherwise, if the realloc() fails, *pIdx is set to -1, *pnEntry remains -** unchanged and a copy of pArray returned. -*/ -SQLITE_PRIVATE void *sqlite3ArrayAllocate( - sqlite3 *db, /* Connection to notify of malloc failures */ - void *pArray, /* Array of objects. Might be reallocated */ - int szEntry, /* Size of each object in the array */ - int *pnEntry, /* Number of objects currently in use */ - int *pIdx /* Write the index of a new slot here */ -){ - char *z; - int n = *pnEntry; - if( (n & (n-1))==0 ){ - int sz = (n==0) ? 1 : 2*n; - void *pNew = sqlite3DbRealloc(db, pArray, sz*szEntry); - if( pNew==0 ){ - *pIdx = -1; - return pArray; - } - pArray = pNew; - } - z = (char*)pArray; - memset(&z[n * szEntry], 0, szEntry); - *pIdx = n; - ++*pnEntry; - return pArray; -} - -/* -** Append a new element to the given IdList. Create a new IdList if -** need be. -** -** A new IdList is returned, or NULL if malloc() fails. -*/ -SQLITE_PRIVATE IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){ - int i; - if( pList==0 ){ - pList = sqlite3DbMallocZero(db, sizeof(IdList) ); - if( pList==0 ) return 0; - } - pList->a = sqlite3ArrayAllocate( - db, - pList->a, - sizeof(pList->a[0]), - &pList->nId, - &i - ); - if( i<0 ){ - sqlite3IdListDelete(db, pList); - return 0; - } - pList->a[i].zName = sqlite3NameFromToken(db, pToken); - return pList; -} - -/* -** Delete an IdList. -*/ -SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ - int i; - if( pList==0 ) return; - for(i=0; inId; i++){ - sqlite3DbFree(db, pList->a[i].zName); - } - sqlite3DbFree(db, pList->a); - sqlite3DbFree(db, pList); -} - -/* -** Return the index in pList of the identifier named zId. Return -1 -** if not found. -*/ -SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){ - int i; - if( pList==0 ) return -1; - for(i=0; inId; i++){ - if( sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i; - } - return -1; -} - -/* -** Expand the space allocated for the given SrcList object by -** creating nExtra new slots beginning at iStart. iStart is zero based. -** New slots are zeroed. -** -** For example, suppose a SrcList initially contains two entries: A,B. -** To append 3 new entries onto the end, do this: -** -** sqlite3SrcListEnlarge(db, pSrclist, 3, 2); -** -** After the call above it would contain: A, B, nil, nil, nil. -** If the iStart argument had been 1 instead of 2, then the result -** would have been: A, nil, nil, nil, B. To prepend the new slots, -** the iStart value would be 0. The result then would -** be: nil, nil, nil, A, B. -** -** If a memory allocation fails the SrcList is unchanged. The -** db->mallocFailed flag will be set to true. -*/ -SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( - sqlite3 *db, /* Database connection to notify of OOM errors */ - SrcList *pSrc, /* The SrcList to be enlarged */ - int nExtra, /* Number of new slots to add to pSrc->a[] */ - int iStart /* Index in pSrc->a[] of first new slot */ -){ - int i; - - /* Sanity checking on calling parameters */ - assert( iStart>=0 ); - assert( nExtra>=1 ); - assert( pSrc!=0 ); - assert( iStart<=pSrc->nSrc ); - - /* Allocate additional space if needed */ - if( pSrc->nSrc+nExtra>pSrc->nAlloc ){ - SrcList *pNew; - int nAlloc = pSrc->nSrc+nExtra; - int nGot; - pNew = sqlite3DbRealloc(db, pSrc, - sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); - if( pNew==0 ){ - assert( db->mallocFailed ); - return pSrc; - } - pSrc = pNew; - nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1; - pSrc->nAlloc = (u16)nGot; - } - - /* Move existing slots that come after the newly inserted slots - ** out of the way */ - for(i=pSrc->nSrc-1; i>=iStart; i--){ - pSrc->a[i+nExtra] = pSrc->a[i]; - } - pSrc->nSrc += (i16)nExtra; - - /* Zero the newly allocated slots */ - memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0])*nExtra); - for(i=iStart; ia[i].iCursor = -1; - } - - /* Return a pointer to the enlarged SrcList */ - return pSrc; -} - - -/* -** Append a new table name to the given SrcList. Create a new SrcList if -** need be. A new entry is created in the SrcList even if pTable is NULL. -** -** A SrcList is returned, or NULL if there is an OOM error. The returned -** SrcList might be the same as the SrcList that was input or it might be -** a new one. If an OOM error does occurs, then the prior value of pList -** that is input to this routine is automatically freed. -** -** If pDatabase is not null, it means that the table has an optional -** database name prefix. Like this: "database.table". The pDatabase -** points to the table name and the pTable points to the database name. -** The SrcList.a[].zName field is filled with the table name which might -** come from pTable (if pDatabase is NULL) or from pDatabase. -** SrcList.a[].zDatabase is filled with the database name from pTable, -** or with NULL if no database is specified. -** -** In other words, if call like this: -** -** sqlite3SrcListAppend(D,A,B,0); -** -** Then B is a table name and the database name is unspecified. If called -** like this: -** -** sqlite3SrcListAppend(D,A,B,C); -** -** Then C is the table name and B is the database name. If C is defined -** then so is B. In other words, we never have a case where: -** -** sqlite3SrcListAppend(D,A,0,C); -** -** Both pTable and pDatabase are assumed to be quoted. They are dequoted -** before being added to the SrcList. -*/ -SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( - sqlite3 *db, /* Connection to notify of malloc failures */ - SrcList *pList, /* Append to this SrcList. NULL creates a new SrcList */ - Token *pTable, /* Table to append */ - Token *pDatabase /* Database of the table */ -){ - struct SrcList_item *pItem; - assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ - if( pList==0 ){ - pList = sqlite3DbMallocZero(db, sizeof(SrcList) ); - if( pList==0 ) return 0; - pList->nAlloc = 1; - } - pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc); - if( db->mallocFailed ){ - sqlite3SrcListDelete(db, pList); - return 0; - } - pItem = &pList->a[pList->nSrc-1]; - if( pDatabase && pDatabase->z==0 ){ - pDatabase = 0; - } - if( pDatabase ){ - Token *pTemp = pDatabase; - pDatabase = pTable; - pTable = pTemp; - } - pItem->zName = sqlite3NameFromToken(db, pTable); - pItem->zDatabase = sqlite3NameFromToken(db, pDatabase); - return pList; -} - -/* -** Assign VdbeCursor index numbers to all tables in a SrcList -*/ -SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ - int i; - struct SrcList_item *pItem; - assert(pList || pParse->db->mallocFailed ); - if( pList ){ - for(i=0, pItem=pList->a; inSrc; i++, pItem++){ - if( pItem->iCursor>=0 ) break; - pItem->iCursor = pParse->nTab++; - if( pItem->pSelect ){ - sqlite3SrcListAssignCursors(pParse, pItem->pSelect->pSrc); - } - } - } -} - -/* -** Delete an entire SrcList including all its substructure. -*/ -SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){ - int i; - struct SrcList_item *pItem; - if( pList==0 ) return; - for(pItem=pList->a, i=0; inSrc; i++, pItem++){ - sqlite3DbFree(db, pItem->zDatabase); - sqlite3DbFree(db, pItem->zName); - sqlite3DbFree(db, pItem->zAlias); - sqlite3DbFree(db, pItem->zIndex); - sqlite3DeleteTable(db, pItem->pTab); - sqlite3SelectDelete(db, pItem->pSelect); - sqlite3ExprDelete(db, pItem->pOn); - sqlite3IdListDelete(db, pItem->pUsing); - } - sqlite3DbFree(db, pList); -} - -/* -** This routine is called by the parser to add a new term to the -** end of a growing FROM clause. The "p" parameter is the part of -** the FROM clause that has already been constructed. "p" is NULL -** if this is the first term of the FROM clause. pTable and pDatabase -** are the name of the table and database named in the FROM clause term. -** pDatabase is NULL if the database name qualifier is missing - the -** usual case. If the term has a alias, then pAlias points to the -** alias token. If the term is a subquery, then pSubquery is the -** SELECT statement that the subquery encodes. The pTable and -** pDatabase parameters are NULL for subqueries. The pOn and pUsing -** parameters are the content of the ON and USING clauses. -** -** Return a new SrcList which encodes is the FROM with the new -** term added. -*/ -SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( - Parse *pParse, /* Parsing context */ - SrcList *p, /* The left part of the FROM clause already seen */ - Token *pTable, /* Name of the table to add to the FROM clause */ - Token *pDatabase, /* Name of the database containing pTable */ - Token *pAlias, /* The right-hand side of the AS subexpression */ - Select *pSubquery, /* A subquery used in place of a table name */ - Expr *pOn, /* The ON clause of a join */ - IdList *pUsing /* The USING clause of a join */ -){ - struct SrcList_item *pItem; - sqlite3 *db = pParse->db; - if( !p && (pOn || pUsing) ){ - sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", - (pOn ? "ON" : "USING") - ); - goto append_from_error; - } - p = sqlite3SrcListAppend(db, p, pTable, pDatabase); - if( p==0 || NEVER(p->nSrc==0) ){ - goto append_from_error; - } - pItem = &p->a[p->nSrc-1]; - assert( pAlias!=0 ); - if( pAlias->n ){ - pItem->zAlias = sqlite3NameFromToken(db, pAlias); - } - pItem->pSelect = pSubquery; - pItem->pOn = pOn; - pItem->pUsing = pUsing; - return p; - - append_from_error: - assert( p==0 ); - sqlite3ExprDelete(db, pOn); - sqlite3IdListDelete(db, pUsing); - sqlite3SelectDelete(db, pSubquery); - return 0; -} - -/* -** Add an INDEXED BY or NOT INDEXED clause to the most recently added -** element of the source-list passed as the second argument. -*/ -SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ - assert( pIndexedBy!=0 ); - if( p && ALWAYS(p->nSrc>0) ){ - struct SrcList_item *pItem = &p->a[p->nSrc-1]; - assert( pItem->notIndexed==0 && pItem->zIndex==0 ); - if( pIndexedBy->n==1 && !pIndexedBy->z ){ - /* A "NOT INDEXED" clause was supplied. See parse.y - ** construct "indexed_opt" for details. */ - pItem->notIndexed = 1; - }else{ - pItem->zIndex = sqlite3NameFromToken(pParse->db, pIndexedBy); - } - } -} - -/* -** When building up a FROM clause in the parser, the join operator -** is initially attached to the left operand. But the code generator -** expects the join operator to be on the right operand. This routine -** Shifts all join operators from left to right for an entire FROM -** clause. -** -** Example: Suppose the join is like this: -** -** A natural cross join B -** -** The operator is "natural cross join". The A and B operands are stored -** in p->a[0] and p->a[1], respectively. The parser initially stores the -** operator with A. This routine shifts that operator over to B. -*/ -SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList *p){ - if( p ){ - int i; - assert( p->a || p->nSrc==0 ); - for(i=p->nSrc-1; i>0; i--){ - p->a[i].jointype = p->a[i-1].jointype; - } - p->a[0].jointype = 0; - } -} - -/* -** Begin a transaction -*/ -SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){ - sqlite3 *db; - Vdbe *v; - int i; - - assert( pParse!=0 ); - db = pParse->db; - assert( db!=0 ); -/* if( db->aDb[0].pBt==0 ) return; */ - if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){ - return; - } - v = sqlite3GetVdbe(pParse); - if( !v ) return; - if( type!=TK_DEFERRED ){ - for(i=0; inDb; i++){ - sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); - sqlite3VdbeUsesBtree(v, i); - } - } - sqlite3VdbeAddOp2(v, OP_AutoCommit, 0, 0); -} - -/* -** Commit a transaction -*/ -SQLITE_PRIVATE void sqlite3CommitTransaction(Parse *pParse){ - Vdbe *v; - - assert( pParse!=0 ); - assert( pParse->db!=0 ); - if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ){ - return; - } - v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 0); - } -} - -/* -** Rollback a transaction -*/ -SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse *pParse){ - Vdbe *v; - - assert( pParse!=0 ); - assert( pParse->db!=0 ); - if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ){ - return; - } - v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 1); - } -} - -/* -** This function is called by the parser when it parses a command to create, -** release or rollback an SQL savepoint. -*/ -SQLITE_PRIVATE void sqlite3Savepoint(Parse *pParse, int op, Token *pName){ - char *zName = sqlite3NameFromToken(pParse->db, pName); - if( zName ){ - Vdbe *v = sqlite3GetVdbe(pParse); -#ifndef SQLITE_OMIT_AUTHORIZATION - static const char * const az[] = { "BEGIN", "RELEASE", "ROLLBACK" }; - assert( !SAVEPOINT_BEGIN && SAVEPOINT_RELEASE==1 && SAVEPOINT_ROLLBACK==2 ); -#endif - if( !v || sqlite3AuthCheck(pParse, SQLITE_SAVEPOINT, az[op], zName, 0) ){ - sqlite3DbFree(pParse->db, zName); - return; - } - sqlite3VdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC); - } -} - -/* -** Make sure the TEMP database is open and available for use. Return -** the number of errors. Leave any error messages in the pParse structure. -*/ -SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){ - sqlite3 *db = pParse->db; - if( db->aDb[1].pBt==0 && !pParse->explain ){ - int rc; - Btree *pBt; - static const int flags = - SQLITE_OPEN_READWRITE | - SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXCLUSIVE | - SQLITE_OPEN_DELETEONCLOSE | - SQLITE_OPEN_TEMP_DB; - - rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pBt, 0, flags); - if( rc!=SQLITE_OK ){ - sqlite3ErrorMsg(pParse, "unable to open a temporary database " - "file for storing temporary tables"); - pParse->rc = rc; - return 1; - } - db->aDb[1].pBt = pBt; - assert( db->aDb[1].pSchema ); - if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){ - db->mallocFailed = 1; - return 1; - } - } - return 0; -} - -/* -** Generate VDBE code that will verify the schema cookie and start -** a read-transaction for all named database files. -** -** It is important that all schema cookies be verified and all -** read transactions be started before anything else happens in -** the VDBE program. But this routine can be called after much other -** code has been generated. So here is what we do: -** -** The first time this routine is called, we code an OP_Goto that -** will jump to a subroutine at the end of the program. Then we -** record every database that needs its schema verified in the -** pParse->cookieMask field. Later, after all other code has been -** generated, the subroutine that does the cookie verifications and -** starts the transactions will be coded and the OP_Goto P2 value -** will be made to point to that subroutine. The generation of the -** cookie verification subroutine code happens in sqlite3FinishCoding(). -** -** If iDb<0 then code the OP_Goto only - don't set flag to verify the -** schema on any databases. This can be used to position the OP_Goto -** early in the code, before we know if any database tables will be used. -*/ -SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); - -#ifndef SQLITE_OMIT_TRIGGER - if( pToplevel!=pParse ){ - /* This branch is taken if a trigger is currently being coded. In this - ** case, set cookieGoto to a non-zero value to show that this function - ** has been called. This is used by the sqlite3ExprCodeConstants() - ** function. */ - pParse->cookieGoto = -1; - } -#endif - if( pToplevel->cookieGoto==0 ){ - Vdbe *v = sqlite3GetVdbe(pToplevel); - if( v==0 ) return; /* This only happens if there was a prior error */ - pToplevel->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; - } - if( iDb>=0 ){ - sqlite3 *db = pToplevel->db; - yDbMask mask; - - assert( iDbnDb ); - assert( db->aDb[iDb].pBt!=0 || iDb==1 ); - assert( iDbcookieMask & mask)==0 ){ - pToplevel->cookieMask |= mask; - pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; - if( !OMIT_TEMPDB && iDb==1 ){ - sqlite3OpenTempDatabase(pToplevel); - } - } - } -} - -/* -** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each -** attached database. Otherwise, invoke it for the database named zDb only. -*/ -SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){ - sqlite3 *db = pParse->db; - int i; - for(i=0; inDb; i++){ - Db *pDb = &db->aDb[i]; - if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){ - sqlite3CodeVerifySchema(pParse, i); - } - } -} - -/* -** Generate VDBE code that prepares for doing an operation that -** might change the database. -** -** This routine starts a new transaction if we are not already within -** a transaction. If we are already within a transaction, then a checkpoint -** is set if the setStatement parameter is true. A checkpoint should -** be set for operations that might fail (due to a constraint) part of -** the way through and which will need to undo some writes without having to -** rollback the whole transaction. For operations where all constraints -** can be checked before any changes are made to the database, it is never -** necessary to undo a write and the checkpoint should not be set. -*/ -SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); - sqlite3CodeVerifySchema(pParse, iDb); - pToplevel->writeMask |= ((yDbMask)1)<isMultiWrite |= setStatement; -} - -/* -** Indicate that the statement currently under construction might write -** more than one entry (example: deleting one row then inserting another, -** inserting multiple rows in a table, or inserting a row and index entries.) -** If an abort occurs after some of these writes have completed, then it will -** be necessary to undo the completed writes. -*/ -SQLITE_PRIVATE void sqlite3MultiWrite(Parse *pParse){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); - pToplevel->isMultiWrite = 1; -} - -/* -** The code generator calls this routine if is discovers that it is -** possible to abort a statement prior to completion. In order to -** perform this abort without corrupting the database, we need to make -** sure that the statement is protected by a statement transaction. -** -** Technically, we only need to set the mayAbort flag if the -** isMultiWrite flag was previously set. There is a time dependency -** such that the abort must occur after the multiwrite. This makes -** some statements involving the REPLACE conflict resolution algorithm -** go a little faster. But taking advantage of this time dependency -** makes it more difficult to prove that the code is correct (in -** particular, it prevents us from writing an effective -** implementation of sqlite3AssertMayAbort()) and so we have chosen -** to take the safe route and skip the optimization. -*/ -SQLITE_PRIVATE void sqlite3MayAbort(Parse *pParse){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); - pToplevel->mayAbort = 1; -} - -/* -** Code an OP_Halt that causes the vdbe to return an SQLITE_CONSTRAINT -** error. The onError parameter determines which (if any) of the statement -** and/or current transaction is rolled back. -*/ -SQLITE_PRIVATE void sqlite3HaltConstraint( - Parse *pParse, /* Parsing context */ - int errCode, /* extended error code */ - int onError, /* Constraint type */ - char *p4, /* Error message */ - int p4type /* P4_STATIC or P4_TRANSIENT */ -){ - Vdbe *v = sqlite3GetVdbe(pParse); - assert( (errCode&0xff)==SQLITE_CONSTRAINT ); - if( onError==OE_Abort ){ - sqlite3MayAbort(pParse); - } - sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type); -} - -/* -** Check to see if pIndex uses the collating sequence pColl. Return -** true if it does and false if it does not. -*/ -#ifndef SQLITE_OMIT_REINDEX -static int collationMatch(const char *zColl, Index *pIndex){ - int i; - assert( zColl!=0 ); - for(i=0; inColumn; i++){ - const char *z = pIndex->azColl[i]; - assert( z!=0 ); - if( 0==sqlite3StrICmp(z, zColl) ){ - return 1; - } - } - return 0; -} -#endif - -/* -** Recompute all indices of pTab that use the collating sequence pColl. -** If pColl==0 then recompute all indices of pTab. -*/ -#ifndef SQLITE_OMIT_REINDEX -static void reindexTable(Parse *pParse, Table *pTab, char const *zColl){ - Index *pIndex; /* An index associated with pTab */ - - for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ - if( zColl==0 || collationMatch(zColl, pIndex) ){ - int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3RefillIndex(pParse, pIndex, -1); - } - } -} -#endif - -/* -** Recompute all indices of all tables in all databases where the -** indices use the collating sequence pColl. If pColl==0 then recompute -** all indices everywhere. -*/ -#ifndef SQLITE_OMIT_REINDEX -static void reindexDatabases(Parse *pParse, char const *zColl){ - Db *pDb; /* A single database */ - int iDb; /* The database index number */ - sqlite3 *db = pParse->db; /* The database connection */ - HashElem *k; /* For looping over tables in pDb */ - Table *pTab; /* A table in the database */ - - assert( sqlite3BtreeHoldsAllMutexes(db) ); /* Needed for schema access */ - for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ - assert( pDb!=0 ); - for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ - pTab = (Table*)sqliteHashData(k); - reindexTable(pParse, pTab, zColl); - } - } -} -#endif - -/* -** Generate code for the REINDEX command. -** -** REINDEX -- 1 -** REINDEX -- 2 -** REINDEX ?.? -- 3 -** REINDEX ?.? -- 4 -** -** Form 1 causes all indices in all attached databases to be rebuilt. -** Form 2 rebuilds all indices in all databases that use the named -** collating function. Forms 3 and 4 rebuild the named index or all -** indices associated with the named table. -*/ -#ifndef SQLITE_OMIT_REINDEX -SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ - CollSeq *pColl; /* Collating sequence to be reindexed, or NULL */ - char *z; /* Name of a table or index */ - const char *zDb; /* Name of the database */ - Table *pTab; /* A table in the database */ - Index *pIndex; /* An index associated with pTab */ - int iDb; /* The database index number */ - sqlite3 *db = pParse->db; /* The database connection */ - Token *pObjName; /* Name of the table or index to be reindexed */ - - /* Read the database schema. If an error occurs, leave an error message - ** and code in pParse and return NULL. */ - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - return; - } - - if( pName1==0 ){ - reindexDatabases(pParse, 0); - return; - }else if( NEVER(pName2==0) || pName2->z==0 ){ - char *zColl; - assert( pName1->z ); - zColl = sqlite3NameFromToken(pParse->db, pName1); - if( !zColl ) return; - pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); - if( pColl ){ - reindexDatabases(pParse, zColl); - sqlite3DbFree(db, zColl); - return; - } - sqlite3DbFree(db, zColl); - } - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); - if( iDb<0 ) return; - z = sqlite3NameFromToken(db, pObjName); - if( z==0 ) return; - zDb = db->aDb[iDb].zName; - pTab = sqlite3FindTable(db, z, zDb); - if( pTab ){ - reindexTable(pParse, pTab, 0); - sqlite3DbFree(db, z); - return; - } - pIndex = sqlite3FindIndex(db, z, zDb); - sqlite3DbFree(db, z); - if( pIndex ){ - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3RefillIndex(pParse, pIndex, -1); - return; - } - sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); -} -#endif - -/* -** Return a dynamicly allocated KeyInfo structure that can be used -** with OP_OpenRead or OP_OpenWrite to access database index pIdx. -** -** If successful, a pointer to the new structure is returned. In this case -** the caller is responsible for calling sqlite3DbFree(db, ) on the returned -** pointer. If an error occurs (out of memory or missing collation -** sequence), NULL is returned and the state of pParse updated to reflect -** the error. -*/ -SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){ - int i; - int nCol = pIdx->nColumn; - int nBytes = sizeof(KeyInfo) + (nCol-1)*sizeof(CollSeq*) + nCol; - sqlite3 *db = pParse->db; - KeyInfo *pKey = (KeyInfo *)sqlite3DbMallocZero(db, nBytes); - - if( pKey ){ - pKey->db = pParse->db; - pKey->aSortOrder = (u8 *)&(pKey->aColl[nCol]); - assert( &pKey->aSortOrder[nCol]==&(((u8 *)pKey)[nBytes]) ); - for(i=0; iazColl[i]; - assert( zColl ); - pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl); - pKey->aSortOrder[i] = pIdx->aSortOrder[i]; - } - pKey->nField = (u16)nCol; - } - - if( pParse->nErr ){ - sqlite3DbFree(db, pKey); - pKey = 0; - } - return pKey; -} - -/************** End of build.c ***********************************************/ -/************** Begin file callback.c ****************************************/ -/* -** 2005 May 23 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains functions used to access the internal hash tables -** of user defined functions and collation sequences. -*/ - - -/* -** Invoke the 'collation needed' callback to request a collation sequence -** in the encoding enc of name zName, length nName. -*/ -static void callCollNeeded(sqlite3 *db, int enc, const char *zName){ - assert( !db->xCollNeeded || !db->xCollNeeded16 ); - if( db->xCollNeeded ){ - char *zExternal = sqlite3DbStrDup(db, zName); - if( !zExternal ) return; - db->xCollNeeded(db->pCollNeededArg, db, enc, zExternal); - sqlite3DbFree(db, zExternal); - } -#ifndef SQLITE_OMIT_UTF16 - if( db->xCollNeeded16 ){ - char const *zExternal; - sqlite3_value *pTmp = sqlite3ValueNew(db); - sqlite3ValueSetStr(pTmp, -1, zName, SQLITE_UTF8, SQLITE_STATIC); - zExternal = sqlite3ValueText(pTmp, SQLITE_UTF16NATIVE); - if( zExternal ){ - db->xCollNeeded16(db->pCollNeededArg, db, (int)ENC(db), zExternal); - } - sqlite3ValueFree(pTmp); - } -#endif -} - -/* -** This routine is called if the collation factory fails to deliver a -** collation function in the best encoding but there may be other versions -** of this collation function (for other text encodings) available. Use one -** of these instead if they exist. Avoid a UTF-8 <-> UTF-16 conversion if -** possible. -*/ -static int synthCollSeq(sqlite3 *db, CollSeq *pColl){ - CollSeq *pColl2; - char *z = pColl->zName; - int i; - static const u8 aEnc[] = { SQLITE_UTF16BE, SQLITE_UTF16LE, SQLITE_UTF8 }; - for(i=0; i<3; i++){ - pColl2 = sqlite3FindCollSeq(db, aEnc[i], z, 0); - if( pColl2->xCmp!=0 ){ - memcpy(pColl, pColl2, sizeof(CollSeq)); - pColl->xDel = 0; /* Do not copy the destructor */ - return SQLITE_OK; - } - } - return SQLITE_ERROR; -} - -/* -** This function is responsible for invoking the collation factory callback -** or substituting a collation sequence of a different encoding when the -** requested collation sequence is not available in the desired encoding. -** -** If it is not NULL, then pColl must point to the database native encoding -** collation sequence with name zName, length nName. -** -** The return value is either the collation sequence to be used in database -** db for collation type name zName, length nName, or NULL, if no collation -** sequence can be found. If no collation is found, leave an error message. -** -** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq() -*/ -SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq( - Parse *pParse, /* Parsing context */ - u8 enc, /* The desired encoding for the collating sequence */ - CollSeq *pColl, /* Collating sequence with native encoding, or NULL */ - const char *zName /* Collating sequence name */ -){ - CollSeq *p; - sqlite3 *db = pParse->db; - - p = pColl; - if( !p ){ - p = sqlite3FindCollSeq(db, enc, zName, 0); - } - if( !p || !p->xCmp ){ - /* No collation sequence of this type for this encoding is registered. - ** Call the collation factory to see if it can supply us with one. - */ - callCollNeeded(db, enc, zName); - p = sqlite3FindCollSeq(db, enc, zName, 0); - } - if( p && !p->xCmp && synthCollSeq(db, p) ){ - p = 0; - } - assert( !p || p->xCmp ); - if( p==0 ){ - sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); - } - return p; -} - -/* -** This routine is called on a collation sequence before it is used to -** check that it is defined. An undefined collation sequence exists when -** a database is loaded that contains references to collation sequences -** that have not been defined by sqlite3_create_collation() etc. -** -** If required, this routine calls the 'collation needed' callback to -** request a definition of the collating sequence. If this doesn't work, -** an equivalent collating sequence that uses a text encoding different -** from the main database is substituted, if one is available. -*/ -SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){ - if( pColl ){ - const char *zName = pColl->zName; - sqlite3 *db = pParse->db; - CollSeq *p = sqlite3GetCollSeq(pParse, ENC(db), pColl, zName); - if( !p ){ - return SQLITE_ERROR; - } - assert( p==pColl ); - } - return SQLITE_OK; -} - - - -/* -** Locate and return an entry from the db.aCollSeq hash table. If the entry -** specified by zName and nName is not found and parameter 'create' is -** true, then create a new entry. Otherwise return NULL. -** -** Each pointer stored in the sqlite3.aCollSeq hash table contains an -** array of three CollSeq structures. The first is the collation sequence -** prefferred for UTF-8, the second UTF-16le, and the third UTF-16be. -** -** Stored immediately after the three collation sequences is a copy of -** the collation sequence name. A pointer to this string is stored in -** each collation sequence structure. -*/ -static CollSeq *findCollSeqEntry( - sqlite3 *db, /* Database connection */ - const char *zName, /* Name of the collating sequence */ - int create /* Create a new entry if true */ -){ - CollSeq *pColl; - int nName = sqlite3Strlen30(zName); - pColl = sqlite3HashFind(&db->aCollSeq, zName, nName); - - if( 0==pColl && create ){ - pColl = sqlite3DbMallocZero(db, 3*sizeof(*pColl) + nName + 1 ); - if( pColl ){ - CollSeq *pDel = 0; - pColl[0].zName = (char*)&pColl[3]; - pColl[0].enc = SQLITE_UTF8; - pColl[1].zName = (char*)&pColl[3]; - pColl[1].enc = SQLITE_UTF16LE; - pColl[2].zName = (char*)&pColl[3]; - pColl[2].enc = SQLITE_UTF16BE; - memcpy(pColl[0].zName, zName, nName); - pColl[0].zName[nName] = 0; - pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl); - - /* If a malloc() failure occurred in sqlite3HashInsert(), it will - ** return the pColl pointer to be deleted (because it wasn't added - ** to the hash table). - */ - assert( pDel==0 || pDel==pColl ); - if( pDel!=0 ){ - db->mallocFailed = 1; - sqlite3DbFree(db, pDel); - pColl = 0; - } - } - } - return pColl; -} - -/* -** Parameter zName points to a UTF-8 encoded string nName bytes long. -** Return the CollSeq* pointer for the collation sequence named zName -** for the encoding 'enc' from the database 'db'. -** -** If the entry specified is not found and 'create' is true, then create a -** new entry. Otherwise return NULL. -** -** A separate function sqlite3LocateCollSeq() is a wrapper around -** this routine. sqlite3LocateCollSeq() invokes the collation factory -** if necessary and generates an error message if the collating sequence -** cannot be found. -** -** See also: sqlite3LocateCollSeq(), sqlite3GetCollSeq() -*/ -SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq( - sqlite3 *db, - u8 enc, - const char *zName, - int create -){ - CollSeq *pColl; - if( zName ){ - pColl = findCollSeqEntry(db, zName, create); - }else{ - pColl = db->pDfltColl; - } - assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); - assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE ); - if( pColl ) pColl += enc-1; - return pColl; -} - -/* During the search for the best function definition, this procedure -** is called to test how well the function passed as the first argument -** matches the request for a function with nArg arguments in a system -** that uses encoding enc. The value returned indicates how well the -** request is matched. A higher value indicates a better match. -** -** If nArg is -1 that means to only return a match (non-zero) if p->nArg -** is also -1. In other words, we are searching for a function that -** takes a variable number of arguments. -** -** If nArg is -2 that means that we are searching for any function -** regardless of the number of arguments it uses, so return a positive -** match score for any -** -** The returned value is always between 0 and 6, as follows: -** -** 0: Not a match. -** 1: UTF8/16 conversion required and function takes any number of arguments. -** 2: UTF16 byte order change required and function takes any number of args. -** 3: encoding matches and function takes any number of arguments -** 4: UTF8/16 conversion required - argument count matches exactly -** 5: UTF16 byte order conversion required - argument count matches exactly -** 6: Perfect match: encoding and argument count match exactly. -** -** If nArg==(-2) then any function with a non-null xStep or xFunc is -** a perfect match and any function with both xStep and xFunc NULL is -** a non-match. -*/ -#define FUNC_PERFECT_MATCH 6 /* The score for a perfect match */ -static int matchQuality( - FuncDef *p, /* The function we are evaluating for match quality */ - int nArg, /* Desired number of arguments. (-1)==any */ - u8 enc /* Desired text encoding */ -){ - int match; - - /* nArg of -2 is a special case */ - if( nArg==(-2) ) return (p->xFunc==0 && p->xStep==0) ? 0 : FUNC_PERFECT_MATCH; - - /* Wrong number of arguments means "no match" */ - if( p->nArg!=nArg && p->nArg>=0 ) return 0; - - /* Give a better score to a function with a specific number of arguments - ** than to function that accepts any number of arguments. */ - if( p->nArg==nArg ){ - match = 4; - }else{ - match = 1; - } - - /* Bonus points if the text encoding matches */ - if( enc==p->iPrefEnc ){ - match += 2; /* Exact encoding match */ - }else if( (enc & p->iPrefEnc & 2)!=0 ){ - match += 1; /* Both are UTF16, but with different byte orders */ - } - - return match; -} - -/* -** Search a FuncDefHash for a function with the given name. Return -** a pointer to the matching FuncDef if found, or 0 if there is no match. -*/ -static FuncDef *functionSearch( - FuncDefHash *pHash, /* Hash table to search */ - int h, /* Hash of the name */ - const char *zFunc, /* Name of function */ - int nFunc /* Number of bytes in zFunc */ -){ - FuncDef *p; - for(p=pHash->a[h]; p; p=p->pHash){ - if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 && p->zName[nFunc]==0 ){ - return p; - } - } - return 0; -} - -/* -** Insert a new FuncDef into a FuncDefHash hash table. -*/ -SQLITE_PRIVATE void sqlite3FuncDefInsert( - FuncDefHash *pHash, /* The hash table into which to insert */ - FuncDef *pDef /* The function definition to insert */ -){ - FuncDef *pOther; - int nName = sqlite3Strlen30(pDef->zName); - u8 c1 = (u8)pDef->zName[0]; - int h = (sqlite3UpperToLower[c1] + nName) % ArraySize(pHash->a); - pOther = functionSearch(pHash, h, pDef->zName, nName); - if( pOther ){ - assert( pOther!=pDef && pOther->pNext!=pDef ); - pDef->pNext = pOther->pNext; - pOther->pNext = pDef; - }else{ - pDef->pNext = 0; - pDef->pHash = pHash->a[h]; - pHash->a[h] = pDef; - } -} - - - -/* -** Locate a user function given a name, a number of arguments and a flag -** indicating whether the function prefers UTF-16 over UTF-8. Return a -** pointer to the FuncDef structure that defines that function, or return -** NULL if the function does not exist. -** -** If the createFlag argument is true, then a new (blank) FuncDef -** structure is created and liked into the "db" structure if a -** no matching function previously existed. -** -** If nArg is -2, then the first valid function found is returned. A -** function is valid if either xFunc or xStep is non-zero. The nArg==(-2) -** case is used to see if zName is a valid function name for some number -** of arguments. If nArg is -2, then createFlag must be 0. -** -** If createFlag is false, then a function with the required name and -** number of arguments may be returned even if the eTextRep flag does not -** match that requested. -*/ -SQLITE_PRIVATE FuncDef *sqlite3FindFunction( - sqlite3 *db, /* An open database */ - const char *zName, /* Name of the function. Not null-terminated */ - int nName, /* Number of characters in the name */ - int nArg, /* Number of arguments. -1 means any number */ - u8 enc, /* Preferred text encoding */ - u8 createFlag /* Create new entry if true and does not otherwise exist */ -){ - FuncDef *p; /* Iterator variable */ - FuncDef *pBest = 0; /* Best match found so far */ - int bestScore = 0; /* Score of best match */ - int h; /* Hash value */ - - assert( nArg>=(-2) ); - assert( nArg>=(-1) || createFlag==0 ); - assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); - h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a); - - /* First search for a match amongst the application-defined functions. - */ - p = functionSearch(&db->aFunc, h, zName, nName); - while( p ){ - int score = matchQuality(p, nArg, enc); - if( score>bestScore ){ - pBest = p; - bestScore = score; - } - p = p->pNext; - } - - /* If no match is found, search the built-in functions. - ** - ** If the SQLITE_PreferBuiltin flag is set, then search the built-in - ** functions even if a prior app-defined function was found. And give - ** priority to built-in functions. - ** - ** Except, if createFlag is true, that means that we are trying to - ** install a new function. Whatever FuncDef structure is returned it will - ** have fields overwritten with new information appropriate for the - ** new function. But the FuncDefs for built-in functions are read-only. - ** So we must not search for built-ins when creating a new function. - */ - if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){ - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); - bestScore = 0; - p = functionSearch(pHash, h, zName, nName); - while( p ){ - int score = matchQuality(p, nArg, enc); - if( score>bestScore ){ - pBest = p; - bestScore = score; - } - p = p->pNext; - } - } - - /* If the createFlag parameter is true and the search did not reveal an - ** exact match for the name, number of arguments and encoding, then add a - ** new entry to the hash table and return it. - */ - if( createFlag && bestScorezName = (char *)&pBest[1]; - pBest->nArg = (u16)nArg; - pBest->iPrefEnc = enc; - memcpy(pBest->zName, zName, nName); - pBest->zName[nName] = 0; - sqlite3FuncDefInsert(&db->aFunc, pBest); - } - - if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){ - return pBest; - } - return 0; -} - -/* -** Free all resources held by the schema structure. The void* argument points -** at a Schema struct. This function does not call sqlite3DbFree(db, ) on the -** pointer itself, it just cleans up subsidiary resources (i.e. the contents -** of the schema hash tables). -** -** The Schema.cache_size variable is not cleared. -*/ -SQLITE_PRIVATE void sqlite3SchemaClear(void *p){ - Hash temp1; - Hash temp2; - HashElem *pElem; - Schema *pSchema = (Schema *)p; - - temp1 = pSchema->tblHash; - temp2 = pSchema->trigHash; - sqlite3HashInit(&pSchema->trigHash); - sqlite3HashClear(&pSchema->idxHash); - for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){ - sqlite3DeleteTrigger(0, (Trigger*)sqliteHashData(pElem)); - } - sqlite3HashClear(&temp2); - sqlite3HashInit(&pSchema->tblHash); - for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ - Table *pTab = sqliteHashData(pElem); - sqlite3DeleteTable(0, pTab); - } - sqlite3HashClear(&temp1); - sqlite3HashClear(&pSchema->fkeyHash); - pSchema->pSeqTab = 0; - if( pSchema->flags & DB_SchemaLoaded ){ - pSchema->iGeneration++; - pSchema->flags &= ~DB_SchemaLoaded; - } -} - -/* -** Find and return the schema associated with a BTree. Create -** a new one if necessary. -*/ -SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ - Schema * p; - if( pBt ){ - p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear); - }else{ - p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema)); - } - if( !p ){ - db->mallocFailed = 1; - }else if ( 0==p->file_format ){ - sqlite3HashInit(&p->tblHash); - sqlite3HashInit(&p->idxHash); - sqlite3HashInit(&p->trigHash); - sqlite3HashInit(&p->fkeyHash); - p->enc = SQLITE_UTF8; - } - return p; -} - -/************** End of callback.c ********************************************/ -/************** Begin file delete.c ******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the parser -** in order to generate code for DELETE FROM statements. -*/ - -/* -** While a SrcList can in general represent multiple tables and subqueries -** (as in the FROM clause of a SELECT statement) in this case it contains -** the name of a single table, as one might find in an INSERT, DELETE, -** or UPDATE statement. Look up that table in the symbol table and -** return a pointer. Set an error message and return NULL if the table -** name is not found or if any other error occurs. -** -** The following fields are initialized appropriate in pSrc: -** -** pSrc->a[0].pTab Pointer to the Table object -** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one -** -*/ -SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ - struct SrcList_item *pItem = pSrc->a; - Table *pTab; - assert( pItem && pSrc->nSrc==1 ); - pTab = sqlite3LocateTableItem(pParse, 0, pItem); - sqlite3DeleteTable(pParse->db, pItem->pTab); - pItem->pTab = pTab; - if( pTab ){ - pTab->nRef++; - } - if( sqlite3IndexedByLookup(pParse, pItem) ){ - pTab = 0; - } - return pTab; -} - -/* -** Check to make sure the given table is writable. If it is not -** writable, generate an error message and return 1. If it is -** writable return 0; -*/ -SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ - /* A table is not writable under the following circumstances: - ** - ** 1) It is a virtual table and no implementation of the xUpdate method - ** has been provided, or - ** 2) It is a system table (i.e. sqlite_master), this call is not - ** part of a nested parse and writable_schema pragma has not - ** been specified. - ** - ** In either case leave an error message in pParse and return non-zero. - */ - if( ( IsVirtual(pTab) - && sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ) - || ( (pTab->tabFlags & TF_Readonly)!=0 - && (pParse->db->flags & SQLITE_WriteSchema)==0 - && pParse->nested==0 ) - ){ - sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); - return 1; - } - -#ifndef SQLITE_OMIT_VIEW - if( !viewOk && pTab->pSelect ){ - sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); - return 1; - } -#endif - return 0; -} - - -#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) -/* -** Evaluate a view and store its result in an ephemeral table. The -** pWhere argument is an optional WHERE clause that restricts the -** set of rows in the view that are to be added to the ephemeral table. -*/ -SQLITE_PRIVATE void sqlite3MaterializeView( - Parse *pParse, /* Parsing context */ - Table *pView, /* View definition */ - Expr *pWhere, /* Optional WHERE clause to be added */ - int iCur /* Cursor number for ephemerial table */ -){ - SelectDest dest; - Select *pSel; - SrcList *pFrom; - sqlite3 *db = pParse->db; - int iDb = sqlite3SchemaToIndex(db, pView->pSchema); - - pWhere = sqlite3ExprDup(db, pWhere, 0); - pFrom = sqlite3SrcListAppend(db, 0, 0, 0); - - if( pFrom ){ - assert( pFrom->nSrc==1 ); - pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); - pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName); - assert( pFrom->a[0].pOn==0 ); - assert( pFrom->a[0].pUsing==0 ); - } - - pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0); - if( pSel ) pSel->selFlags |= SF_Materialize; - - sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur); - sqlite3Select(pParse, pSel, &dest); - sqlite3SelectDelete(db, pSel); -} -#endif /* !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) */ - -#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) -/* -** Generate an expression tree to implement the WHERE, ORDER BY, -** and LIMIT/OFFSET portion of DELETE and UPDATE statements. -** -** DELETE FROM table_wxyz WHERE a<5 ORDER BY a LIMIT 1; -** \__________________________/ -** pLimitWhere (pInClause) -*/ -SQLITE_PRIVATE Expr *sqlite3LimitWhere( - Parse *pParse, /* The parser context */ - SrcList *pSrc, /* the FROM clause -- which tables to scan */ - Expr *pWhere, /* The WHERE clause. May be null */ - ExprList *pOrderBy, /* The ORDER BY clause. May be null */ - Expr *pLimit, /* The LIMIT clause. May be null */ - Expr *pOffset, /* The OFFSET clause. May be null */ - char *zStmtType /* Either DELETE or UPDATE. For error messages. */ -){ - Expr *pWhereRowid = NULL; /* WHERE rowid .. */ - Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */ - Expr *pSelectRowid = NULL; /* SELECT rowid ... */ - ExprList *pEList = NULL; /* Expression list contaning only pSelectRowid */ - SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */ - Select *pSelect = NULL; /* Complete SELECT tree */ - - /* Check that there isn't an ORDER BY without a LIMIT clause. - */ - if( pOrderBy && (pLimit == 0) ) { - sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType); - goto limit_where_cleanup_2; - } - - /* We only need to generate a select expression if there - ** is a limit/offset term to enforce. - */ - if( pLimit == 0 ) { - /* if pLimit is null, pOffset will always be null as well. */ - assert( pOffset == 0 ); - return pWhere; - } - - /* Generate a select expression tree to enforce the limit/offset - ** term for the DELETE or UPDATE statement. For example: - ** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 - ** becomes: - ** DELETE FROM table_a WHERE rowid IN ( - ** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 - ** ); - */ - - pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0); - if( pSelectRowid == 0 ) goto limit_where_cleanup_2; - pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid); - if( pEList == 0 ) goto limit_where_cleanup_2; - - /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree - ** and the SELECT subtree. */ - pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0); - if( pSelectSrc == 0 ) { - sqlite3ExprListDelete(pParse->db, pEList); - goto limit_where_cleanup_2; - } - - /* generate the SELECT expression tree. */ - pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0, - pOrderBy,0,pLimit,pOffset); - if( pSelect == 0 ) return 0; - - /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */ - pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0); - if( pWhereRowid == 0 ) goto limit_where_cleanup_1; - pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0); - if( pInClause == 0 ) goto limit_where_cleanup_1; - - pInClause->x.pSelect = pSelect; - pInClause->flags |= EP_xIsSelect; - sqlite3ExprSetHeight(pParse, pInClause); - return pInClause; - - /* something went wrong. clean up anything allocated. */ -limit_where_cleanup_1: - sqlite3SelectDelete(pParse->db, pSelect); - return 0; - -limit_where_cleanup_2: - sqlite3ExprDelete(pParse->db, pWhere); - sqlite3ExprListDelete(pParse->db, pOrderBy); - sqlite3ExprDelete(pParse->db, pLimit); - sqlite3ExprDelete(pParse->db, pOffset); - return 0; -} -#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */ - -/* -** Generate code for a DELETE FROM statement. -** -** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL; -** \________/ \________________/ -** pTabList pWhere -*/ -SQLITE_PRIVATE void sqlite3DeleteFrom( - Parse *pParse, /* The parser context */ - SrcList *pTabList, /* The table from which we should delete things */ - Expr *pWhere /* The WHERE clause. May be null */ -){ - Vdbe *v; /* The virtual database engine */ - Table *pTab; /* The table from which records will be deleted */ - const char *zDb; /* Name of database holding pTab */ - int end, addr = 0; /* A couple addresses of generated code */ - int i; /* Loop counter */ - WhereInfo *pWInfo; /* Information about the WHERE clause */ - Index *pIdx; /* For looping over indices of the table */ - int iCur; /* VDBE Cursor number for pTab */ - sqlite3 *db; /* Main database structure */ - AuthContext sContext; /* Authorization context */ - NameContext sNC; /* Name context to resolve expressions in */ - int iDb; /* Database number */ - int memCnt = -1; /* Memory cell used for change counting */ - int rcauth; /* Value returned by authorization callback */ - -#ifndef SQLITE_OMIT_TRIGGER - int isView; /* True if attempting to delete from a view */ - Trigger *pTrigger; /* List of table triggers, if required */ -#endif - - memset(&sContext, 0, sizeof(sContext)); - db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ - goto delete_from_cleanup; - } - assert( pTabList->nSrc==1 ); - - /* Locate the table which we want to delete. This table has to be - ** put in an SrcList structure because some of the subroutines we - ** will be calling are designed to work with multiple tables and expect - ** an SrcList* parameter instead of just a Table* parameter. - */ - pTab = sqlite3SrcListLookup(pParse, pTabList); - if( pTab==0 ) goto delete_from_cleanup; - - /* Figure out if we have any triggers and if the table being - ** deleted from is a view - */ -#ifndef SQLITE_OMIT_TRIGGER - pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); - isView = pTab->pSelect!=0; -#else -# define pTrigger 0 -# define isView 0 -#endif -#ifdef SQLITE_OMIT_VIEW -# undef isView -# define isView 0 -#endif - - /* If pTab is really a view, make sure it has been initialized. - */ - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto delete_from_cleanup; - } - - if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ - goto delete_from_cleanup; - } - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - assert( iDbnDb ); - zDb = db->aDb[iDb].zName; - rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb); - assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE ); - if( rcauth==SQLITE_DENY ){ - goto delete_from_cleanup; - } - assert(!isView || pTrigger); - - /* Assign cursor number to the table and all its indices. - */ - assert( pTabList->nSrc==1 ); - iCur = pTabList->a[0].iCursor = pParse->nTab++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - pParse->nTab++; - } - - /* Start the view context - */ - if( isView ){ - sqlite3AuthContextPush(pParse, &sContext, pTab->zName); - } - - /* Begin generating code. - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ){ - goto delete_from_cleanup; - } - if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, 1, iDb); - - /* If we are trying to delete from a view, realize that view into - ** a ephemeral table. - */ -#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) - if( isView ){ - sqlite3MaterializeView(pParse, pTab, pWhere, iCur); - } -#endif - - /* Resolve the column names in the WHERE clause. - */ - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - sNC.pSrcList = pTabList; - if( sqlite3ResolveExprNames(&sNC, pWhere) ){ - goto delete_from_cleanup; - } - - /* Initialize the counter of the number of rows deleted, if - ** we are counting rows. - */ - if( db->flags & SQLITE_CountRows ){ - memCnt = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt); - } - -#ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION - /* Special case: A DELETE without a WHERE clause deletes everything. - ** It is easier just to erase the whole table. Prior to version 3.6.5, - ** this optimization caused the row change count (the value returned by - ** API function sqlite3_count_changes) to be set incorrectly. */ - if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) - && 0==sqlite3FkRequired(pParse, pTab, 0, 0) - ){ - assert( !isView ); - sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt, - pTab->zName, P4_STATIC); - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - assert( pIdx->pSchema==pTab->pSchema ); - sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); - } - }else -#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ - /* The usual case: There is a WHERE clause so we have to scan through - ** the table and pick which records to delete. - */ - { - int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */ - int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ - int regRowid; /* Actual register containing rowids */ - - /* Collect rowids of every row to be deleted. - */ - sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); - pWInfo = sqlite3WhereBegin( - pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0 - ); - if( pWInfo==0 ) goto delete_from_cleanup; - regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0); - sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); - if( db->flags & SQLITE_CountRows ){ - sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); - } - sqlite3WhereEnd(pWInfo); - - /* Delete every item whose key was written to the list during the - ** database scan. We have to delete items after the scan is complete - ** because deleting an item can change the scan order. */ - end = sqlite3VdbeMakeLabel(v); - - /* Unless this is a view, open cursors for the table we are - ** deleting from and all its indices. If this is a view, then the - ** only effect this statement has is to fire the INSTEAD OF - ** triggers. */ - if( !isView ){ - sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); - } - - addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); - - /* Delete the row */ -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) ){ - const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); - sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); - sqlite3VdbeChangeP5(v, OE_Abort); - sqlite3MayAbort(pParse); - }else -#endif - { - int count = (pParse->nested==0); /* True to count changes */ - sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default); - } - - /* End of the delete loop */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); - sqlite3VdbeResolveLabel(v, end); - - /* Close the cursors open on the table and its indexes. */ - if( !isView && !IsVirtual(pTab) ){ - for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum); - } - sqlite3VdbeAddOp1(v, OP_Close, iCur); - } - } - - /* Update the sqlite_sequence table by storing the content of the - ** maximum rowid counter values recorded while inserting into - ** autoincrement tables. - */ - if( pParse->nested==0 && pParse->pTriggerTab==0 ){ - sqlite3AutoincrementEnd(pParse); - } - - /* Return the number of rows that were deleted. If this routine is - ** generating code because of a call to sqlite3NestedParse(), do not - ** invoke the callback function. - */ - if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){ - sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); - } - -delete_from_cleanup: - sqlite3AuthContextPop(&sContext); - sqlite3SrcListDelete(db, pTabList); - sqlite3ExprDelete(db, pWhere); - return; -} -/* Make sure "isView" and other macros defined above are undefined. Otherwise -** thely may interfere with compilation of other functions in this file -** (or in another file, if this file becomes part of the amalgamation). */ -#ifdef isView - #undef isView -#endif -#ifdef pTrigger - #undef pTrigger -#endif - -/* -** This routine generates VDBE code that causes a single row of a -** single table to be deleted. -** -** The VDBE must be in a particular state when this routine is called. -** These are the requirements: -** -** 1. A read/write cursor pointing to pTab, the table containing the row -** to be deleted, must be opened as cursor number $iCur. -** -** 2. Read/write cursors for all indices of pTab must be open as -** cursor number base+i for the i-th index. -** -** 3. The record number of the row to be deleted must be stored in -** memory cell iRowid. -** -** This routine generates code to remove both the table record and all -** index entries that point to that record. -*/ -SQLITE_PRIVATE void sqlite3GenerateRowDelete( - Parse *pParse, /* Parsing context */ - Table *pTab, /* Table containing the row to be deleted */ - int iCur, /* Cursor number for the table */ - int iRowid, /* Memory cell that contains the rowid to delete */ - int count, /* If non-zero, increment the row change counter */ - Trigger *pTrigger, /* List of triggers to (potentially) fire */ - int onconf /* Default ON CONFLICT policy for triggers */ -){ - Vdbe *v = pParse->pVdbe; /* Vdbe */ - int iOld = 0; /* First register in OLD.* array */ - int iLabel; /* Label resolved to end of generated code */ - - /* Vdbe is guaranteed to have been allocated by this stage. */ - assert( v ); - - /* Seek cursor iCur to the row to delete. If this row no longer exists - ** (this can happen if a trigger program has already deleted it), do - ** not attempt to delete it or fire any DELETE triggers. */ - iLabel = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid); - - /* If there are any triggers to fire, allocate a range of registers to - ** use for the old.* references in the triggers. */ - if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){ - u32 mask; /* Mask of OLD.* columns in use */ - int iCol; /* Iterator used while populating OLD.* */ - - /* TODO: Could use temporary registers here. Also could attempt to - ** avoid copying the contents of the rowid register. */ - mask = sqlite3TriggerColmask( - pParse, pTrigger, 0, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onconf - ); - mask |= sqlite3FkOldmask(pParse, pTab); - iOld = pParse->nMem+1; - pParse->nMem += (1 + pTab->nCol); - - /* Populate the OLD.* pseudo-table register array. These values will be - ** used by any BEFORE and AFTER triggers that exist. */ - sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld); - for(iCol=0; iColnCol; iCol++){ - if( mask==0xffffffff || mask&(1<pSelect==0 ){ - sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0); - sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); - if( count ){ - sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); - } - } - - /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to - ** handle rows (possibly in other tables) that refer via a foreign key - ** to the row just deleted. */ - sqlite3FkActions(pParse, pTab, 0, iOld); - - /* Invoke AFTER DELETE trigger programs. */ - sqlite3CodeRowTrigger(pParse, pTrigger, - TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel - ); - - /* Jump here if the row had already been deleted before any BEFORE - ** trigger programs were invoked. Or if a trigger program throws a - ** RAISE(IGNORE) exception. */ - sqlite3VdbeResolveLabel(v, iLabel); -} - -/* -** This routine generates VDBE code that causes the deletion of all -** index entries associated with a single row of a single table. -** -** The VDBE must be in a particular state when this routine is called. -** These are the requirements: -** -** 1. A read/write cursor pointing to pTab, the table containing the row -** to be deleted, must be opened as cursor number "iCur". -** -** 2. Read/write cursors for all indices of pTab must be open as -** cursor number iCur+i for the i-th index. -** -** 3. The "iCur" cursor must be pointing to the row that is to be -** deleted. -*/ -SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete( - Parse *pParse, /* Parsing and code generating context */ - Table *pTab, /* Table containing the row to be deleted */ - int iCur, /* Cursor number for the table */ - int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */ -){ - int i; - Index *pIdx; - int r1; - - for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - if( aRegIdx!=0 && aRegIdx[i-1]==0 ) continue; - r1 = sqlite3GenerateIndexKey(pParse, pIdx, iCur, 0, 0); - sqlite3VdbeAddOp3(pParse->pVdbe, OP_IdxDelete, iCur+i, r1,pIdx->nColumn+1); - } -} - -/* -** Generate code that will assemble an index key and put it in register -** regOut. The key with be for index pIdx which is an index on pTab. -** iCur is the index of a cursor open on the pTab table and pointing to -** the entry that needs indexing. -** -** Return a register number which is the first in a block of -** registers that holds the elements of the index key. The -** block of registers has already been deallocated by the time -** this routine returns. -*/ -SQLITE_PRIVATE int sqlite3GenerateIndexKey( - Parse *pParse, /* Parsing context */ - Index *pIdx, /* The index for which to generate a key */ - int iCur, /* Cursor number for the pIdx->pTable table */ - int regOut, /* Write the new index key to this register */ - int doMakeRec /* Run the OP_MakeRecord instruction if true */ -){ - Vdbe *v = pParse->pVdbe; - int j; - Table *pTab = pIdx->pTable; - int regBase; - int nCol; - - nCol = pIdx->nColumn; - regBase = sqlite3GetTempRange(pParse, nCol+1); - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regBase+nCol); - for(j=0; jaiColumn[j]; - if( idx==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j); - }else{ - sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j); - sqlite3ColumnDefault(v, pTab, idx, -1); - } - } - if( doMakeRec ){ - const char *zAff; - if( pTab->pSelect - || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt) - ){ - zAff = 0; - }else{ - zAff = sqlite3IndexAffinityStr(v, pIdx); - } - sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut); - sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT); - } - sqlite3ReleaseTempRange(pParse, regBase, nCol+1); - return regBase; -} - -/************** End of delete.c **********************************************/ -/************** Begin file func.c ********************************************/ -/* -** 2002 February 23 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the C functions that implement various SQL -** functions of SQLite. -** -** There is only one exported symbol in this file - the function -** sqliteRegisterBuildinFunctions() found at the bottom of the file. -** All other code has file scope. -*/ -/* #include */ -/* #include */ - -/* -** Return the collating function associated with a function. -*/ -static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){ - return context->pColl; -} - -/* -** Indicate that the accumulator load should be skipped on this -** iteration of the aggregate loop. -*/ -static void sqlite3SkipAccumulatorLoad(sqlite3_context *context){ - context->skipFlag = 1; -} - -/* -** Implementation of the non-aggregate min() and max() functions -*/ -static void minmaxFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int i; - int mask; /* 0 for min() or 0xffffffff for max() */ - int iBest; - CollSeq *pColl; - - assert( argc>1 ); - mask = sqlite3_user_data(context)==0 ? 0 : -1; - pColl = sqlite3GetFuncCollSeq(context); - assert( pColl ); - assert( mask==-1 || mask==0 ); - iBest = 0; - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - for(i=1; i=0 ){ - testcase( mask==0 ); - iBest = i; - } - } - sqlite3_result_value(context, argv[iBest]); -} - -/* -** Return the type of the argument. -*/ -static void typeofFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - const char *z = 0; - UNUSED_PARAMETER(NotUsed); - switch( sqlite3_value_type(argv[0]) ){ - case SQLITE_INTEGER: z = "integer"; break; - case SQLITE_TEXT: z = "text"; break; - case SQLITE_FLOAT: z = "real"; break; - case SQLITE_BLOB: z = "blob"; break; - default: z = "null"; break; - } - sqlite3_result_text(context, z, -1, SQLITE_STATIC); -} - - -/* -** Implementation of the length() function -*/ -static void lengthFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int len; - - assert( argc==1 ); - UNUSED_PARAMETER(argc); - switch( sqlite3_value_type(argv[0]) ){ - case SQLITE_BLOB: - case SQLITE_INTEGER: - case SQLITE_FLOAT: { - sqlite3_result_int(context, sqlite3_value_bytes(argv[0])); - break; - } - case SQLITE_TEXT: { - const unsigned char *z = sqlite3_value_text(argv[0]); - if( z==0 ) return; - len = 0; - while( *z ){ - len++; - SQLITE_SKIP_UTF8(z); - } - sqlite3_result_int(context, len); - break; - } - default: { - sqlite3_result_null(context); - break; - } - } -} - -/* -** Implementation of the abs() function. -** -** IMP: R-23979-26855 The abs(X) function returns the absolute value of -** the numeric argument X. -*/ -static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - assert( argc==1 ); - UNUSED_PARAMETER(argc); - switch( sqlite3_value_type(argv[0]) ){ - case SQLITE_INTEGER: { - i64 iVal = sqlite3_value_int64(argv[0]); - if( iVal<0 ){ - if( (iVal<<1)==0 ){ - /* IMP: R-35460-15084 If X is the integer -9223372036854775807 then - ** abs(X) throws an integer overflow error since there is no - ** equivalent positive 64-bit two complement value. */ - sqlite3_result_error(context, "integer overflow", -1); - return; - } - iVal = -iVal; - } - sqlite3_result_int64(context, iVal); - break; - } - case SQLITE_NULL: { - /* IMP: R-37434-19929 Abs(X) returns NULL if X is NULL. */ - sqlite3_result_null(context); - break; - } - default: { - /* Because sqlite3_value_double() returns 0.0 if the argument is not - ** something that can be converted into a number, we have: - ** IMP: R-57326-31541 Abs(X) return 0.0 if X is a string or blob that - ** cannot be converted to a numeric value. - */ - double rVal = sqlite3_value_double(argv[0]); - if( rVal<0 ) rVal = -rVal; - sqlite3_result_double(context, rVal); - break; - } - } -} - -/* -** Implementation of the instr() function. -** -** instr(haystack,needle) finds the first occurrence of needle -** in haystack and returns the number of previous characters plus 1, -** or 0 if needle does not occur within haystack. -** -** If both haystack and needle are BLOBs, then the result is one more than -** the number of bytes in haystack prior to the first occurrence of needle, -** or 0 if needle never occurs in haystack. -*/ -static void instrFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zHaystack; - const unsigned char *zNeedle; - int nHaystack; - int nNeedle; - int typeHaystack, typeNeedle; - int N = 1; - int isText; - - UNUSED_PARAMETER(argc); - typeHaystack = sqlite3_value_type(argv[0]); - typeNeedle = sqlite3_value_type(argv[1]); - if( typeHaystack==SQLITE_NULL || typeNeedle==SQLITE_NULL ) return; - nHaystack = sqlite3_value_bytes(argv[0]); - nNeedle = sqlite3_value_bytes(argv[1]); - if( typeHaystack==SQLITE_BLOB && typeNeedle==SQLITE_BLOB ){ - zHaystack = sqlite3_value_blob(argv[0]); - zNeedle = sqlite3_value_blob(argv[1]); - isText = 0; - }else{ - zHaystack = sqlite3_value_text(argv[0]); - zNeedle = sqlite3_value_text(argv[1]); - isText = 1; - } - while( nNeedle<=nHaystack && memcmp(zHaystack, zNeedle, nNeedle)!=0 ){ - N++; - do{ - nHaystack--; - zHaystack++; - }while( isText && (zHaystack[0]&0xc0)==0x80 ); - } - if( nNeedle>nHaystack ) N = 0; - sqlite3_result_int(context, N); -} - -/* -** Implementation of the substr() function. -** -** substr(x,p1,p2) returns p2 characters of x[] beginning with p1. -** p1 is 1-indexed. So substr(x,1,1) returns the first character -** of x. If x is text, then we actually count UTF-8 characters. -** If x is a blob, then we count bytes. -** -** If p1 is negative, then we begin abs(p1) from the end of x[]. -** -** If p2 is negative, return the p2 characters preceeding p1. -*/ -static void substrFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *z; - const unsigned char *z2; - int len; - int p0type; - i64 p1, p2; - int negP2 = 0; - - assert( argc==3 || argc==2 ); - if( sqlite3_value_type(argv[1])==SQLITE_NULL - || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL) - ){ - return; - } - p0type = sqlite3_value_type(argv[0]); - p1 = sqlite3_value_int(argv[1]); - if( p0type==SQLITE_BLOB ){ - len = sqlite3_value_bytes(argv[0]); - z = sqlite3_value_blob(argv[0]); - if( z==0 ) return; - assert( len==sqlite3_value_bytes(argv[0]) ); - }else{ - z = sqlite3_value_text(argv[0]); - if( z==0 ) return; - len = 0; - if( p1<0 ){ - for(z2=z; *z2; len++){ - SQLITE_SKIP_UTF8(z2); - } - } - } - if( argc==3 ){ - p2 = sqlite3_value_int(argv[2]); - if( p2<0 ){ - p2 = -p2; - negP2 = 1; - } - }else{ - p2 = sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH]; - } - if( p1<0 ){ - p1 += len; - if( p1<0 ){ - p2 += p1; - if( p2<0 ) p2 = 0; - p1 = 0; - } - }else if( p1>0 ){ - p1--; - }else if( p2>0 ){ - p2--; - } - if( negP2 ){ - p1 -= p2; - if( p1<0 ){ - p2 += p1; - p1 = 0; - } - } - assert( p1>=0 && p2>=0 ); - if( p0type!=SQLITE_BLOB ){ - while( *z && p1 ){ - SQLITE_SKIP_UTF8(z); - p1--; - } - for(z2=z; *z2 && p2; p2--){ - SQLITE_SKIP_UTF8(z2); - } - sqlite3_result_text(context, (char*)z, (int)(z2-z), SQLITE_TRANSIENT); - }else{ - if( p1+p2>len ){ - p2 = len-p1; - if( p2<0 ) p2 = 0; - } - sqlite3_result_blob(context, (char*)&z[p1], (int)p2, SQLITE_TRANSIENT); - } -} - -/* -** Implementation of the round() function -*/ -#ifndef SQLITE_OMIT_FLOATING_POINT -static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - int n = 0; - double r; - char *zBuf; - assert( argc==1 || argc==2 ); - if( argc==2 ){ - if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return; - n = sqlite3_value_int(argv[1]); - if( n>30 ) n = 30; - if( n<0 ) n = 0; - } - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - r = sqlite3_value_double(argv[0]); - /* If Y==0 and X will fit in a 64-bit int, - ** handle the rounding directly, - ** otherwise use printf. - */ - if( n==0 && r>=0 && r0 ); - testcase( nByte==db->aLimit[SQLITE_LIMIT_LENGTH] ); - testcase( nByte==db->aLimit[SQLITE_LIMIT_LENGTH]+1 ); - if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - sqlite3_result_error_toobig(context); - z = 0; - }else{ - z = sqlite3Malloc((int)nByte); - if( !z ){ - sqlite3_result_error_nomem(context); - } - } - return z; -} - -/* -** Implementation of the upper() and lower() SQL functions. -*/ -static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - char *z1; - const char *z2; - int i, n; - UNUSED_PARAMETER(argc); - z2 = (char*)sqlite3_value_text(argv[0]); - n = sqlite3_value_bytes(argv[0]); - /* Verify that the call to _bytes() does not invalidate the _text() pointer */ - assert( z2==(char*)sqlite3_value_text(argv[0]) ); - if( z2 ){ - z1 = contextMalloc(context, ((i64)n)+1); - if( z1 ){ - for(i=0; imatchOne; - u8 matchAll = pInfo->matchAll; - u8 matchSet = pInfo->matchSet; - u8 noCase = pInfo->noCase; - int prevEscape = 0; /* True if the previous character was 'escape' */ - - while( (c = sqlite3Utf8Read(&zPattern))!=0 ){ - if( c==matchAll && !prevEscape ){ - while( (c=sqlite3Utf8Read(&zPattern)) == matchAll - || c == matchOne ){ - if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){ - return 0; - } - } - if( c==0 ){ - return 1; - }else if( c==esc ){ - c = sqlite3Utf8Read(&zPattern); - if( c==0 ){ - return 0; - } - }else if( c==matchSet ){ - assert( esc==0 ); /* This is GLOB, not LIKE */ - assert( matchSet<0x80 ); /* '[' is a single-byte character */ - while( *zString && patternCompare(&zPattern[-1],zString,pInfo,esc)==0 ){ - SQLITE_SKIP_UTF8(zString); - } - return *zString!=0; - } - while( (c2 = sqlite3Utf8Read(&zString))!=0 ){ - if( noCase ){ - GlogUpperToLower(c2); - GlogUpperToLower(c); - while( c2 != 0 && c2 != c ){ - c2 = sqlite3Utf8Read(&zString); - GlogUpperToLower(c2); - } - }else{ - while( c2 != 0 && c2 != c ){ - c2 = sqlite3Utf8Read(&zString); - } - } - if( c2==0 ) return 0; - if( patternCompare(zPattern,zString,pInfo,esc) ) return 1; - } - return 0; - }else if( c==matchOne && !prevEscape ){ - if( sqlite3Utf8Read(&zString)==0 ){ - return 0; - } - }else if( c==matchSet ){ - u32 prior_c = 0; - assert( esc==0 ); /* This only occurs for GLOB, not LIKE */ - seen = 0; - invert = 0; - c = sqlite3Utf8Read(&zString); - if( c==0 ) return 0; - c2 = sqlite3Utf8Read(&zPattern); - if( c2=='^' ){ - invert = 1; - c2 = sqlite3Utf8Read(&zPattern); - } - if( c2==']' ){ - if( c==']' ) seen = 1; - c2 = sqlite3Utf8Read(&zPattern); - } - while( c2 && c2!=']' ){ - if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){ - c2 = sqlite3Utf8Read(&zPattern); - if( c>=prior_c && c<=c2 ) seen = 1; - prior_c = 0; - }else{ - if( c==c2 ){ - seen = 1; - } - prior_c = c2; - } - c2 = sqlite3Utf8Read(&zPattern); - } - if( c2==0 || (seen ^ invert)==0 ){ - return 0; - } - }else if( esc==c && !prevEscape ){ - prevEscape = 1; - }else{ - c2 = sqlite3Utf8Read(&zString); - if( noCase ){ - GlogUpperToLower(c); - GlogUpperToLower(c2); - } - if( c!=c2 ){ - return 0; - } - prevEscape = 0; - } - } - return *zString==0; -} - -/* -** Count the number of times that the LIKE operator (or GLOB which is -** just a variation of LIKE) gets called. This is used for testing -** only. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_like_count = 0; -#endif - - -/* -** Implementation of the like() SQL function. This function implements -** the build-in LIKE operator. The first argument to the function is the -** pattern and the second argument is the string. So, the SQL statements: -** -** A LIKE B -** -** is implemented as like(B,A). -** -** This same function (with a different compareInfo structure) computes -** the GLOB operator. -*/ -static void likeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zA, *zB; - u32 escape = 0; - int nPat; - sqlite3 *db = sqlite3_context_db_handle(context); - - zB = sqlite3_value_text(argv[0]); - zA = sqlite3_value_text(argv[1]); - - /* Limit the length of the LIKE or GLOB pattern to avoid problems - ** of deep recursion and N*N behavior in patternCompare(). - */ - nPat = sqlite3_value_bytes(argv[0]); - testcase( nPat==db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] ); - testcase( nPat==db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]+1 ); - if( nPat > db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] ){ - sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1); - return; - } - assert( zB==sqlite3_value_text(argv[0]) ); /* Encoding did not change */ - - if( argc==3 ){ - /* The escape character string must consist of a single UTF-8 character. - ** Otherwise, return an error. - */ - const unsigned char *zEsc = sqlite3_value_text(argv[2]); - if( zEsc==0 ) return; - if( sqlite3Utf8CharLen((char*)zEsc, -1)!=1 ){ - sqlite3_result_error(context, - "ESCAPE expression must be a single character", -1); - return; - } - escape = sqlite3Utf8Read(&zEsc); - } - if( zA && zB ){ - struct compareInfo *pInfo = sqlite3_user_data(context); -#ifdef SQLITE_TEST - sqlite3_like_count++; -#endif - - sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape)); - } -} - -/* -** Implementation of the NULLIF(x,y) function. The result is the first -** argument if the arguments are different. The result is NULL if the -** arguments are equal to each other. -*/ -static void nullifFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - CollSeq *pColl = sqlite3GetFuncCollSeq(context); - UNUSED_PARAMETER(NotUsed); - if( sqlite3MemCompare(argv[0], argv[1], pColl)!=0 ){ - sqlite3_result_value(context, argv[0]); - } -} - -/* -** Implementation of the sqlite_version() function. The result is the version -** of the SQLite library that is running. -*/ -static void versionFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **NotUsed2 -){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - /* IMP: R-48699-48617 This function is an SQL wrapper around the - ** sqlite3_libversion() C-interface. */ - sqlite3_result_text(context, sqlite3_libversion(), -1, SQLITE_STATIC); -} - -/* -** Implementation of the sqlite_source_id() function. The result is a string -** that identifies the particular version of the source code used to build -** SQLite. -*/ -static void sourceidFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **NotUsed2 -){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - /* IMP: R-24470-31136 This function is an SQL wrapper around the - ** sqlite3_sourceid() C interface. */ - sqlite3_result_text(context, sqlite3_sourceid(), -1, SQLITE_STATIC); -} - -/* -** Implementation of the sqlite_log() function. This is a wrapper around -** sqlite3_log(). The return value is NULL. The function exists purely for -** its side-effects. -*/ -static void errlogFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - UNUSED_PARAMETER(argc); - UNUSED_PARAMETER(context); - sqlite3_log(sqlite3_value_int(argv[0]), "%s", sqlite3_value_text(argv[1])); -} - -/* -** Implementation of the sqlite_compileoption_used() function. -** The result is an integer that identifies if the compiler option -** was used to build SQLite. -*/ -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS -static void compileoptionusedFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zOptName; - assert( argc==1 ); - UNUSED_PARAMETER(argc); - /* IMP: R-39564-36305 The sqlite_compileoption_used() SQL - ** function is a wrapper around the sqlite3_compileoption_used() C/C++ - ** function. - */ - if( (zOptName = (const char*)sqlite3_value_text(argv[0]))!=0 ){ - sqlite3_result_int(context, sqlite3_compileoption_used(zOptName)); - } -} -#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ - -/* -** Implementation of the sqlite_compileoption_get() function. -** The result is a string that identifies the compiler options -** used to build SQLite. -*/ -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS -static void compileoptiongetFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int n; - assert( argc==1 ); - UNUSED_PARAMETER(argc); - /* IMP: R-04922-24076 The sqlite_compileoption_get() SQL function - ** is a wrapper around the sqlite3_compileoption_get() C/C++ function. - */ - n = sqlite3_value_int(argv[0]); - sqlite3_result_text(context, sqlite3_compileoption_get(n), -1, SQLITE_STATIC); -} -#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ - -/* Array for converting from half-bytes (nybbles) into ASCII hex -** digits. */ -static const char hexdigits[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' -}; - -/* -** EXPERIMENTAL - This is not an official function. The interface may -** change. This function may disappear. Do not write code that depends -** on this function. -** -** Implementation of the QUOTE() function. This function takes a single -** argument. If the argument is numeric, the return value is the same as -** the argument. If the argument is NULL, the return value is the string -** "NULL". Otherwise, the argument is enclosed in single quotes with -** single-quote escapes. -*/ -static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - assert( argc==1 ); - UNUSED_PARAMETER(argc); - switch( sqlite3_value_type(argv[0]) ){ - case SQLITE_FLOAT: { - double r1, r2; - char zBuf[50]; - r1 = sqlite3_value_double(argv[0]); - sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1); - sqlite3AtoF(zBuf, &r2, 20, SQLITE_UTF8); - if( r1!=r2 ){ - sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.20e", r1); - } - sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); - break; - } - case SQLITE_INTEGER: { - sqlite3_result_value(context, argv[0]); - break; - } - case SQLITE_BLOB: { - char *zText = 0; - char const *zBlob = sqlite3_value_blob(argv[0]); - int nBlob = sqlite3_value_bytes(argv[0]); - assert( zBlob==sqlite3_value_blob(argv[0]) ); /* No encoding change */ - zText = (char *)contextMalloc(context, (2*(i64)nBlob)+4); - if( zText ){ - int i; - for(i=0; i>4)&0x0F]; - zText[(i*2)+3] = hexdigits[(zBlob[i])&0x0F]; - } - zText[(nBlob*2)+2] = '\''; - zText[(nBlob*2)+3] = '\0'; - zText[0] = 'X'; - zText[1] = '\''; - sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); - sqlite3_free(zText); - } - break; - } - case SQLITE_TEXT: { - int i,j; - u64 n; - const unsigned char *zArg = sqlite3_value_text(argv[0]); - char *z; - - if( zArg==0 ) return; - for(i=0, n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; } - z = contextMalloc(context, ((i64)i)+((i64)n)+3); - if( z ){ - z[0] = '\''; - for(i=0, j=1; zArg[i]; i++){ - z[j++] = zArg[i]; - if( zArg[i]=='\'' ){ - z[j++] = '\''; - } - } - z[j++] = '\''; - z[j] = 0; - sqlite3_result_text(context, z, j, sqlite3_free); - } - break; - } - default: { - assert( sqlite3_value_type(argv[0])==SQLITE_NULL ); - sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC); - break; - } - } -} - -/* -** The unicode() function. Return the integer unicode code-point value -** for the first character of the input string. -*/ -static void unicodeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *z = sqlite3_value_text(argv[0]); - (void)argc; - if( z && z[0] ) sqlite3_result_int(context, sqlite3Utf8Read(&z)); -} - -/* -** The char() function takes zero or more arguments, each of which is -** an integer. It constructs a string where each character of the string -** is the unicode character for the corresponding integer argument. -*/ -static void charFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - unsigned char *z, *zOut; - int i; - zOut = z = sqlite3_malloc( argc*4 ); - if( z==0 ){ - sqlite3_result_error_nomem(context); - return; - } - for(i=0; i0x10ffff ) x = 0xfffd; - c = (unsigned)(x & 0x1fffff); - if( c<0x00080 ){ - *zOut++ = (u8)(c&0xFF); - }else if( c<0x00800 ){ - *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); - *zOut++ = 0x80 + (u8)(c & 0x3F); - }else if( c<0x10000 ){ - *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); - *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); - *zOut++ = 0x80 + (u8)(c & 0x3F); - }else{ - *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); - *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); - *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); - *zOut++ = 0x80 + (u8)(c & 0x3F); - } \ - } - sqlite3_result_text(context, (char*)z, (int)(zOut-z), sqlite3_free); -} - -/* -** The hex() function. Interpret the argument as a blob. Return -** a hexadecimal rendering as text. -*/ -static void hexFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int i, n; - const unsigned char *pBlob; - char *zHex, *z; - assert( argc==1 ); - UNUSED_PARAMETER(argc); - pBlob = sqlite3_value_blob(argv[0]); - n = sqlite3_value_bytes(argv[0]); - assert( pBlob==sqlite3_value_blob(argv[0]) ); /* No encoding change */ - z = zHex = contextMalloc(context, ((i64)n)*2 + 1); - if( zHex ){ - for(i=0; i>4)&0xf]; - *(z++) = hexdigits[c&0xf]; - } - *z = 0; - sqlite3_result_text(context, zHex, n*2, sqlite3_free); - } -} - -/* -** The zeroblob(N) function returns a zero-filled blob of size N bytes. -*/ -static void zeroblobFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - i64 n; - sqlite3 *db = sqlite3_context_db_handle(context); - assert( argc==1 ); - UNUSED_PARAMETER(argc); - n = sqlite3_value_int64(argv[0]); - testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH] ); - testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH]+1 ); - if( n>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - sqlite3_result_error_toobig(context); - }else{ - sqlite3_result_zeroblob(context, (int)n); /* IMP: R-00293-64994 */ - } -} - -/* -** The replace() function. Three arguments are all strings: call -** them A, B, and C. The result is also a string which is derived -** from A by replacing every occurance of B with C. The match -** must be exact. Collating sequences are not used. -*/ -static void replaceFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zStr; /* The input string A */ - const unsigned char *zPattern; /* The pattern string B */ - const unsigned char *zRep; /* The replacement string C */ - unsigned char *zOut; /* The output */ - int nStr; /* Size of zStr */ - int nPattern; /* Size of zPattern */ - int nRep; /* Size of zRep */ - i64 nOut; /* Maximum size of zOut */ - int loopLimit; /* Last zStr[] that might match zPattern[] */ - int i, j; /* Loop counters */ - - assert( argc==3 ); - UNUSED_PARAMETER(argc); - zStr = sqlite3_value_text(argv[0]); - if( zStr==0 ) return; - nStr = sqlite3_value_bytes(argv[0]); - assert( zStr==sqlite3_value_text(argv[0]) ); /* No encoding change */ - zPattern = sqlite3_value_text(argv[1]); - if( zPattern==0 ){ - assert( sqlite3_value_type(argv[1])==SQLITE_NULL - || sqlite3_context_db_handle(context)->mallocFailed ); - return; - } - if( zPattern[0]==0 ){ - assert( sqlite3_value_type(argv[1])!=SQLITE_NULL ); - sqlite3_result_value(context, argv[0]); - return; - } - nPattern = sqlite3_value_bytes(argv[1]); - assert( zPattern==sqlite3_value_text(argv[1]) ); /* No encoding change */ - zRep = sqlite3_value_text(argv[2]); - if( zRep==0 ) return; - nRep = sqlite3_value_bytes(argv[2]); - assert( zRep==sqlite3_value_text(argv[2]) ); - nOut = nStr + 1; - assert( nOutaLimit[SQLITE_LIMIT_LENGTH] ); - testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] ); - if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - sqlite3_result_error_toobig(context); - sqlite3_free(zOut); - return; - } - zOld = zOut; - zOut = sqlite3_realloc(zOut, (int)nOut); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - sqlite3_free(zOld); - return; - } - memcpy(&zOut[j], zRep, nRep); - j += nRep; - i += nPattern-1; - } - } - assert( j+nStr-i+1==nOut ); - memcpy(&zOut[j], &zStr[i], nStr-i); - j += nStr - i; - assert( j<=nOut ); - zOut[j] = 0; - sqlite3_result_text(context, (char*)zOut, j, sqlite3_free); -} - -/* -** Implementation of the TRIM(), LTRIM(), and RTRIM() functions. -** The userdata is 0x1 for left trim, 0x2 for right trim, 0x3 for both. -*/ -static void trimFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zIn; /* Input string */ - const unsigned char *zCharSet; /* Set of characters to trim */ - int nIn; /* Number of bytes in input */ - int flags; /* 1: trimleft 2: trimright 3: trim */ - int i; /* Loop counter */ - unsigned char *aLen = 0; /* Length of each character in zCharSet */ - unsigned char **azChar = 0; /* Individual characters in zCharSet */ - int nChar; /* Number of characters in zCharSet */ - - if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ - return; - } - zIn = sqlite3_value_text(argv[0]); - if( zIn==0 ) return; - nIn = sqlite3_value_bytes(argv[0]); - assert( zIn==sqlite3_value_text(argv[0]) ); - if( argc==1 ){ - static const unsigned char lenOne[] = { 1 }; - static unsigned char * const azOne[] = { (u8*)" " }; - nChar = 1; - aLen = (u8*)lenOne; - azChar = (unsigned char **)azOne; - zCharSet = 0; - }else if( (zCharSet = sqlite3_value_text(argv[1]))==0 ){ - return; - }else{ - const unsigned char *z; - for(z=zCharSet, nChar=0; *z; nChar++){ - SQLITE_SKIP_UTF8(z); - } - if( nChar>0 ){ - azChar = contextMalloc(context, ((i64)nChar)*(sizeof(char*)+1)); - if( azChar==0 ){ - return; - } - aLen = (unsigned char*)&azChar[nChar]; - for(z=zCharSet, nChar=0; *z; nChar++){ - azChar[nChar] = (unsigned char *)z; - SQLITE_SKIP_UTF8(z); - aLen[nChar] = (u8)(z - azChar[nChar]); - } - } - } - if( nChar>0 ){ - flags = SQLITE_PTR_TO_INT(sqlite3_user_data(context)); - if( flags & 1 ){ - while( nIn>0 ){ - int len = 0; - for(i=0; i=nChar ) break; - zIn += len; - nIn -= len; - } - } - if( flags & 2 ){ - while( nIn>0 ){ - int len = 0; - for(i=0; i=nChar ) break; - nIn -= len; - } - } - if( zCharSet ){ - sqlite3_free(azChar); - } - } - sqlite3_result_text(context, (char*)zIn, nIn, SQLITE_TRANSIENT); -} - - -/* IMP: R-25361-16150 This function is omitted from SQLite by default. It -** is only available if the SQLITE_SOUNDEX compile-time option is used -** when SQLite is built. -*/ -#ifdef SQLITE_SOUNDEX -/* -** Compute the soundex encoding of a word. -** -** IMP: R-59782-00072 The soundex(X) function returns a string that is the -** soundex encoding of the string X. -*/ -static void soundexFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - char zResult[8]; - const u8 *zIn; - int i, j; - static const unsigned char iCode[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, - 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, - 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, - }; - assert( argc==1 ); - zIn = (u8*)sqlite3_value_text(argv[0]); - if( zIn==0 ) zIn = (u8*)""; - for(i=0; zIn[i] && !sqlite3Isalpha(zIn[i]); i++){} - if( zIn[i] ){ - u8 prevcode = iCode[zIn[i]&0x7f]; - zResult[0] = sqlite3Toupper(zIn[i]); - for(j=1; j<4 && zIn[i]; i++){ - int code = iCode[zIn[i]&0x7f]; - if( code>0 ){ - if( code!=prevcode ){ - prevcode = code; - zResult[j++] = code + '0'; - } - }else{ - prevcode = 0; - } - } - while( j<4 ){ - zResult[j++] = '0'; - } - zResult[j] = 0; - sqlite3_result_text(context, zResult, 4, SQLITE_TRANSIENT); - }else{ - /* IMP: R-64894-50321 The string "?000" is returned if the argument - ** is NULL or contains no ASCII alphabetic characters. */ - sqlite3_result_text(context, "?000", 4, SQLITE_STATIC); - } -} -#endif /* SQLITE_SOUNDEX */ - -#ifndef SQLITE_OMIT_LOAD_EXTENSION -/* -** A function that loads a shared-library extension then returns NULL. -*/ -static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){ - const char *zFile = (const char *)sqlite3_value_text(argv[0]); - const char *zProc; - sqlite3 *db = sqlite3_context_db_handle(context); - char *zErrMsg = 0; - - if( argc==2 ){ - zProc = (const char *)sqlite3_value_text(argv[1]); - }else{ - zProc = 0; - } - if( zFile && sqlite3_load_extension(db, zFile, zProc, &zErrMsg) ){ - sqlite3_result_error(context, zErrMsg, -1); - sqlite3_free(zErrMsg); - } -} -#endif - - -/* -** An instance of the following structure holds the context of a -** sum() or avg() aggregate computation. -*/ -typedef struct SumCtx SumCtx; -struct SumCtx { - double rSum; /* Floating point sum */ - i64 iSum; /* Integer sum */ - i64 cnt; /* Number of elements summed */ - u8 overflow; /* True if integer overflow seen */ - u8 approx; /* True if non-integer value was input to the sum */ -}; - -/* -** Routines used to compute the sum, average, and total. -** -** The SUM() function follows the (broken) SQL standard which means -** that it returns NULL if it sums over no inputs. TOTAL returns -** 0.0 in that case. In addition, TOTAL always returns a float where -** SUM might return an integer if it never encounters a floating point -** value. TOTAL never fails, but SUM might through an exception if -** it overflows an integer. -*/ -static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ - SumCtx *p; - int type; - assert( argc==1 ); - UNUSED_PARAMETER(argc); - p = sqlite3_aggregate_context(context, sizeof(*p)); - type = sqlite3_value_numeric_type(argv[0]); - if( p && type!=SQLITE_NULL ){ - p->cnt++; - if( type==SQLITE_INTEGER ){ - i64 v = sqlite3_value_int64(argv[0]); - p->rSum += v; - if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){ - p->overflow = 1; - } - }else{ - p->rSum += sqlite3_value_double(argv[0]); - p->approx = 1; - } - } -} -static void sumFinalize(sqlite3_context *context){ - SumCtx *p; - p = sqlite3_aggregate_context(context, 0); - if( p && p->cnt>0 ){ - if( p->overflow ){ - sqlite3_result_error(context,"integer overflow",-1); - }else if( p->approx ){ - sqlite3_result_double(context, p->rSum); - }else{ - sqlite3_result_int64(context, p->iSum); - } - } -} -static void avgFinalize(sqlite3_context *context){ - SumCtx *p; - p = sqlite3_aggregate_context(context, 0); - if( p && p->cnt>0 ){ - sqlite3_result_double(context, p->rSum/(double)p->cnt); - } -} -static void totalFinalize(sqlite3_context *context){ - SumCtx *p; - p = sqlite3_aggregate_context(context, 0); - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - sqlite3_result_double(context, p ? p->rSum : (double)0); -} - -/* -** The following structure keeps track of state information for the -** count() aggregate function. -*/ -typedef struct CountCtx CountCtx; -struct CountCtx { - i64 n; -}; - -/* -** Routines to implement the count() aggregate function. -*/ -static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){ - CountCtx *p; - p = sqlite3_aggregate_context(context, sizeof(*p)); - if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0])) && p ){ - p->n++; - } - -#ifndef SQLITE_OMIT_DEPRECATED - /* The sqlite3_aggregate_count() function is deprecated. But just to make - ** sure it still operates correctly, verify that its count agrees with our - ** internal count when using count(*) and when the total count can be - ** expressed as a 32-bit integer. */ - assert( argc==1 || p==0 || p->n>0x7fffffff - || p->n==sqlite3_aggregate_count(context) ); -#endif -} -static void countFinalize(sqlite3_context *context){ - CountCtx *p; - p = sqlite3_aggregate_context(context, 0); - sqlite3_result_int64(context, p ? p->n : 0); -} - -/* -** Routines to implement min() and max() aggregate functions. -*/ -static void minmaxStep( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - Mem *pArg = (Mem *)argv[0]; - Mem *pBest; - UNUSED_PARAMETER(NotUsed); - - pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest)); - if( !pBest ) return; - - if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ - if( pBest->flags ) sqlite3SkipAccumulatorLoad(context); - }else if( pBest->flags ){ - int max; - int cmp; - CollSeq *pColl = sqlite3GetFuncCollSeq(context); - /* This step function is used for both the min() and max() aggregates, - ** the only difference between the two being that the sense of the - ** comparison is inverted. For the max() aggregate, the - ** sqlite3_user_data() function returns (void *)-1. For min() it - ** returns (void *)db, where db is the sqlite3* database pointer. - ** Therefore the next statement sets variable 'max' to 1 for the max() - ** aggregate, or 0 for min(). - */ - max = sqlite3_user_data(context)!=0; - cmp = sqlite3MemCompare(pBest, pArg, pColl); - if( (max && cmp<0) || (!max && cmp>0) ){ - sqlite3VdbeMemCopy(pBest, pArg); - }else{ - sqlite3SkipAccumulatorLoad(context); - } - }else{ - sqlite3VdbeMemCopy(pBest, pArg); - } -} -static void minMaxFinalize(sqlite3_context *context){ - sqlite3_value *pRes; - pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0); - if( pRes ){ - if( pRes->flags ){ - sqlite3_result_value(context, pRes); - } - sqlite3VdbeMemRelease(pRes); - } -} - -/* -** group_concat(EXPR, ?SEPARATOR?) -*/ -static void groupConcatStep( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zVal; - StrAccum *pAccum; - const char *zSep; - int nVal, nSep; - assert( argc==1 || argc==2 ); - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum)); - - if( pAccum ){ - sqlite3 *db = sqlite3_context_db_handle(context); - int firstTerm = pAccum->useMalloc==0; - pAccum->useMalloc = 2; - pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; - if( !firstTerm ){ - if( argc==2 ){ - zSep = (char*)sqlite3_value_text(argv[1]); - nSep = sqlite3_value_bytes(argv[1]); - }else{ - zSep = ","; - nSep = 1; - } - sqlite3StrAccumAppend(pAccum, zSep, nSep); - } - zVal = (char*)sqlite3_value_text(argv[0]); - nVal = sqlite3_value_bytes(argv[0]); - sqlite3StrAccumAppend(pAccum, zVal, nVal); - } -} -static void groupConcatFinalize(sqlite3_context *context){ - StrAccum *pAccum; - pAccum = sqlite3_aggregate_context(context, 0); - if( pAccum ){ - if( pAccum->tooBig ){ - sqlite3_result_error_toobig(context); - }else if( pAccum->mallocFailed ){ - sqlite3_result_error_nomem(context); - }else{ - sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1, - sqlite3_free); - } - } -} - -/* -** This routine does per-connection function registration. Most -** of the built-in functions above are part of the global function set. -** This routine only deals with those that are not global. -*/ -SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ - int rc = sqlite3_overload_function(db, "MATCH", 2); - assert( rc==SQLITE_NOMEM || rc==SQLITE_OK ); - if( rc==SQLITE_NOMEM ){ - db->mallocFailed = 1; - } -} - -/* -** Set the LIKEOPT flag on the 2-argument function with the given name. -*/ -static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){ - FuncDef *pDef; - pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName), - 2, SQLITE_UTF8, 0); - if( ALWAYS(pDef) ){ - pDef->flags = flagVal; - } -} - -/* -** Register the built-in LIKE and GLOB functions. The caseSensitive -** parameter determines whether or not the LIKE operator is case -** sensitive. GLOB is always case sensitive. -*/ -SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ - struct compareInfo *pInfo; - if( caseSensitive ){ - pInfo = (struct compareInfo*)&likeInfoAlt; - }else{ - pInfo = (struct compareInfo*)&likeInfoNorm; - } - sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0); - sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0); - sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8, - (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0); - setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE); - setLikeOptFlag(db, "like", - caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE); -} - -/* -** pExpr points to an expression which implements a function. If -** it is appropriate to apply the LIKE optimization to that function -** then set aWc[0] through aWc[2] to the wildcard characters and -** return TRUE. If the function is not a LIKE-style function then -** return FALSE. -*/ -SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ - FuncDef *pDef; - if( pExpr->op!=TK_FUNCTION - || !pExpr->x.pList - || pExpr->x.pList->nExpr!=2 - ){ - return 0; - } - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - pDef = sqlite3FindFunction(db, pExpr->u.zToken, - sqlite3Strlen30(pExpr->u.zToken), - 2, SQLITE_UTF8, 0); - if( NEVER(pDef==0) || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){ - return 0; - } - - /* The memcpy() statement assumes that the wildcard characters are - ** the first three statements in the compareInfo structure. The - ** asserts() that follow verify that assumption - */ - memcpy(aWc, pDef->pUserData, 3); - assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll ); - assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne ); - assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet ); - *pIsNocase = (pDef->flags & SQLITE_FUNC_CASE)==0; - return 1; -} - -/* -** All all of the FuncDef structures in the aBuiltinFunc[] array above -** to the global function hash table. This occurs at start-time (as -** a consequence of calling sqlite3_initialize()). -** -** After this routine runs -*/ -SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){ - /* - ** The following array holds FuncDef structures for all of the functions - ** defined in this file. - ** - ** The array cannot be constant since changes are made to the - ** FuncDef.pHash elements at start-time. The elements of this array - ** are read-only after initialization is complete. - */ - static SQLITE_WSD FuncDef aBuiltinFunc[] = { - FUNCTION(ltrim, 1, 1, 0, trimFunc ), - FUNCTION(ltrim, 2, 1, 0, trimFunc ), - FUNCTION(rtrim, 1, 2, 0, trimFunc ), - FUNCTION(rtrim, 2, 2, 0, trimFunc ), - FUNCTION(trim, 1, 3, 0, trimFunc ), - FUNCTION(trim, 2, 3, 0, trimFunc ), - FUNCTION(min, -1, 0, 1, minmaxFunc ), - FUNCTION(min, 0, 0, 1, 0 ), - AGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize ), - FUNCTION(max, -1, 1, 1, minmaxFunc ), - FUNCTION(max, 0, 1, 1, 0 ), - AGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize ), - FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), - FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), - FUNCTION(instr, 2, 0, 0, instrFunc ), - FUNCTION(substr, 2, 0, 0, substrFunc ), - FUNCTION(substr, 3, 0, 0, substrFunc ), - FUNCTION(unicode, 1, 0, 0, unicodeFunc ), - FUNCTION(char, -1, 0, 0, charFunc ), - FUNCTION(abs, 1, 0, 0, absFunc ), -#ifndef SQLITE_OMIT_FLOATING_POINT - FUNCTION(round, 1, 0, 0, roundFunc ), - FUNCTION(round, 2, 0, 0, roundFunc ), -#endif - FUNCTION(upper, 1, 0, 0, upperFunc ), - FUNCTION(lower, 1, 0, 0, lowerFunc ), - FUNCTION(coalesce, 1, 0, 0, 0 ), - FUNCTION(coalesce, 0, 0, 0, 0 ), - FUNCTION2(coalesce, -1, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE), - FUNCTION(hex, 1, 0, 0, hexFunc ), - FUNCTION2(ifnull, 2, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE), - FUNCTION(random, 0, 0, 0, randomFunc ), - FUNCTION(randomblob, 1, 0, 0, randomBlob ), - FUNCTION(nullif, 2, 0, 1, nullifFunc ), - FUNCTION(sqlite_version, 0, 0, 0, versionFunc ), - FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), - FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ), -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS - FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), - FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), -#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ - FUNCTION(quote, 1, 0, 0, quoteFunc ), - FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), - FUNCTION(changes, 0, 0, 0, changes ), - FUNCTION(total_changes, 0, 0, 0, total_changes ), - FUNCTION(replace, 3, 0, 0, replaceFunc ), - FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), - #ifdef SQLITE_SOUNDEX - FUNCTION(soundex, 1, 0, 0, soundexFunc ), - #endif - #ifndef SQLITE_OMIT_LOAD_EXTENSION - FUNCTION(load_extension, 1, 0, 0, loadExt ), - FUNCTION(load_extension, 2, 0, 0, loadExt ), - #endif - AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), - AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), - AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ), - /* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */ - {0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0}, - AGGREGATE(count, 1, 0, 0, countStep, countFinalize ), - AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize), - AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize), - - LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), - #ifdef SQLITE_CASE_SENSITIVE_LIKE - LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), - LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), - #else - LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE), - LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE), - #endif - }; - - int i; - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); - FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aBuiltinFunc); - - for(i=0; idb->mallocFailed flag is set. -*/ -SQLITE_PRIVATE int sqlite3FkLocateIndex( - Parse *pParse, /* Parse context to store any error in */ - Table *pParent, /* Parent table of FK constraint pFKey */ - FKey *pFKey, /* Foreign key to find index for */ - Index **ppIdx, /* OUT: Unique index on parent table */ - int **paiCol /* OUT: Map of index columns in pFKey */ -){ - Index *pIdx = 0; /* Value to return via *ppIdx */ - int *aiCol = 0; /* Value to return via *paiCol */ - int nCol = pFKey->nCol; /* Number of columns in parent key */ - char *zKey = pFKey->aCol[0].zCol; /* Name of left-most parent key column */ - - /* The caller is responsible for zeroing output parameters. */ - assert( ppIdx && *ppIdx==0 ); - assert( !paiCol || *paiCol==0 ); - assert( pParse ); - - /* If this is a non-composite (single column) foreign key, check if it - ** maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx - ** and *paiCol set to zero and return early. - ** - ** Otherwise, for a composite foreign key (more than one column), allocate - ** space for the aiCol array (returned via output parameter *paiCol). - ** Non-composite foreign keys do not require the aiCol array. - */ - if( nCol==1 ){ - /* The FK maps to the IPK if any of the following are true: - ** - ** 1) There is an INTEGER PRIMARY KEY column and the FK is implicitly - ** mapped to the primary key of table pParent, or - ** 2) The FK is explicitly mapped to a column declared as INTEGER - ** PRIMARY KEY. - */ - if( pParent->iPKey>=0 ){ - if( !zKey ) return 0; - if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0; - } - }else if( paiCol ){ - assert( nCol>1 ); - aiCol = (int *)sqlite3DbMallocRaw(pParse->db, nCol*sizeof(int)); - if( !aiCol ) return 1; - *paiCol = aiCol; - } - - for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){ - /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number - ** of columns. If each indexed column corresponds to a foreign key - ** column of pFKey, then this index is a winner. */ - - if( zKey==0 ){ - /* If zKey is NULL, then this foreign key is implicitly mapped to - ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be - ** identified by the test (Index.autoIndex==2). */ - if( pIdx->autoIndex==2 ){ - if( aiCol ){ - int i; - for(i=0; iaCol[i].iFrom; - } - break; - } - }else{ - /* If zKey is non-NULL, then this foreign key was declared to - ** map to an explicit list of columns in table pParent. Check if this - ** index matches those columns. Also, check that the index uses - ** the default collation sequences for each column. */ - int i, j; - for(i=0; iaiColumn[i]; /* Index of column in parent tbl */ - char *zDfltColl; /* Def. collation for column */ - char *zIdxCol; /* Name of indexed column */ - - /* If the index uses a collation sequence that is different from - ** the default collation sequence for the column, this index is - ** unusable. Bail out early in this case. */ - zDfltColl = pParent->aCol[iCol].zColl; - if( !zDfltColl ){ - zDfltColl = "BINARY"; - } - if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break; - - zIdxCol = pParent->aCol[iCol].zName; - for(j=0; jaCol[j].zCol, zIdxCol)==0 ){ - if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom; - break; - } - } - if( j==nCol ) break; - } - if( i==nCol ) break; /* pIdx is usable */ - } - } - } - - if( !pIdx ){ - if( !pParse->disableTriggers ){ - sqlite3ErrorMsg(pParse, - "foreign key mismatch - \"%w\" referencing \"%w\"", - pFKey->pFrom->zName, pFKey->zTo); - } - sqlite3DbFree(pParse->db, aiCol); - return 1; - } - - *ppIdx = pIdx; - return 0; -} - -/* -** This function is called when a row is inserted into or deleted from the -** child table of foreign key constraint pFKey. If an SQL UPDATE is executed -** on the child table of pFKey, this function is invoked twice for each row -** affected - once to "delete" the old row, and then again to "insert" the -** new row. -** -** Each time it is called, this function generates VDBE code to locate the -** row in the parent table that corresponds to the row being inserted into -** or deleted from the child table. If the parent row can be found, no -** special action is taken. Otherwise, if the parent row can *not* be -** found in the parent table: -** -** Operation | FK type | Action taken -** -------------------------------------------------------------------------- -** INSERT immediate Increment the "immediate constraint counter". -** -** DELETE immediate Decrement the "immediate constraint counter". -** -** INSERT deferred Increment the "deferred constraint counter". -** -** DELETE deferred Decrement the "deferred constraint counter". -** -** These operations are identified in the comment at the top of this file -** (fkey.c) as "I.1" and "D.1". -*/ -static void fkLookupParent( - Parse *pParse, /* Parse context */ - int iDb, /* Index of database housing pTab */ - Table *pTab, /* Parent table of FK pFKey */ - Index *pIdx, /* Unique index on parent key columns in pTab */ - FKey *pFKey, /* Foreign key constraint */ - int *aiCol, /* Map from parent key columns to child table columns */ - int regData, /* Address of array containing child table row */ - int nIncr, /* Increment constraint counter by this */ - int isIgnore /* If true, pretend pTab contains all NULL values */ -){ - int i; /* Iterator variable */ - Vdbe *v = sqlite3GetVdbe(pParse); /* Vdbe to add code to */ - int iCur = pParse->nTab - 1; /* Cursor number to use */ - int iOk = sqlite3VdbeMakeLabel(v); /* jump here if parent key found */ - - /* If nIncr is less than zero, then check at runtime if there are any - ** outstanding constraints to resolve. If there are not, there is no need - ** to check if deleting this row resolves any outstanding violations. - ** - ** Check if any of the key columns in the child table row are NULL. If - ** any are, then the constraint is considered satisfied. No need to - ** search for a matching row in the parent table. */ - if( nIncr<0 ){ - sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk); - } - for(i=0; inCol; i++){ - int iReg = aiCol[i] + regData + 1; - sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); - } - - if( isIgnore==0 ){ - if( pIdx==0 ){ - /* If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY - ** column of the parent table (table pTab). */ - int iMustBeInt; /* Address of MustBeInt instruction */ - int regTemp = sqlite3GetTempReg(pParse); - - /* Invoke MustBeInt to coerce the child key value to an integer (i.e. - ** apply the affinity of the parent key). If this fails, then there - ** is no matching parent key. Before using MustBeInt, make a copy of - ** the value. Otherwise, the value inserted into the child key column - ** will have INTEGER affinity applied to it, which may not be correct. */ - sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp); - iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0); - - /* If the parent table is the same as the child table, and we are about - ** to increment the constraint-counter (i.e. this is an INSERT operation), - ** then check if the row being inserted matches itself. If so, do not - ** increment the constraint-counter. */ - if( pTab==pFKey->pFrom && nIncr==1 ){ - sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); - } - - sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); - sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); - sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); - sqlite3VdbeJumpHere(v, iMustBeInt); - sqlite3ReleaseTempReg(pParse, regTemp); - }else{ - int nCol = pFKey->nCol; - int regTemp = sqlite3GetTempRange(pParse, nCol); - int regRec = sqlite3GetTempReg(pParse); - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); - - sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); - for(i=0; ipFrom && nIncr==1 ){ - int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1; - for(i=0; iaiColumn[i]+1+regData; - assert( aiCol[i]!=pTab->iPKey ); - if( pIdx->aiColumn[i]==pTab->iPKey ){ - /* The parent key is a composite key that includes the IPK column */ - iParent = regData; - } - sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); - sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); - } - sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); - } - - sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); - sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); - - sqlite3ReleaseTempReg(pParse, regRec); - sqlite3ReleaseTempRange(pParse, regTemp, nCol); - } - } - - if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){ - /* Special case: If this is an INSERT statement that will insert exactly - ** one row into the table, raise a constraint immediately instead of - ** incrementing a counter. This is necessary as the VM code is being - ** generated for will not open a statement transaction. */ - assert( nIncr==1 ); - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, - OE_Abort, "foreign key constraint failed", P4_STATIC - ); - }else{ - if( nIncr>0 && pFKey->isDeferred==0 ){ - sqlite3ParseToplevel(pParse)->mayAbort = 1; - } - sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); - } - - sqlite3VdbeResolveLabel(v, iOk); - sqlite3VdbeAddOp1(v, OP_Close, iCur); -} - -/* -** This function is called to generate code executed when a row is deleted -** from the parent table of foreign key constraint pFKey and, if pFKey is -** deferred, when a row is inserted into the same table. When generating -** code for an SQL UPDATE operation, this function may be called twice - -** once to "delete" the old row and once to "insert" the new row. -** -** The code generated by this function scans through the rows in the child -** table that correspond to the parent table row being deleted or inserted. -** For each child row found, one of the following actions is taken: -** -** Operation | FK type | Action taken -** -------------------------------------------------------------------------- -** DELETE immediate Increment the "immediate constraint counter". -** Or, if the ON (UPDATE|DELETE) action is RESTRICT, -** throw a "foreign key constraint failed" exception. -** -** INSERT immediate Decrement the "immediate constraint counter". -** -** DELETE deferred Increment the "deferred constraint counter". -** Or, if the ON (UPDATE|DELETE) action is RESTRICT, -** throw a "foreign key constraint failed" exception. -** -** INSERT deferred Decrement the "deferred constraint counter". -** -** These operations are identified in the comment at the top of this file -** (fkey.c) as "I.2" and "D.2". -*/ -static void fkScanChildren( - Parse *pParse, /* Parse context */ - SrcList *pSrc, /* SrcList containing the table to scan */ - Table *pTab, - Index *pIdx, /* Foreign key index */ - FKey *pFKey, /* Foreign key relationship */ - int *aiCol, /* Map from pIdx cols to child table cols */ - int regData, /* Referenced table data starts here */ - int nIncr /* Amount to increment deferred counter by */ -){ - sqlite3 *db = pParse->db; /* Database handle */ - int i; /* Iterator variable */ - Expr *pWhere = 0; /* WHERE clause to scan with */ - NameContext sNameContext; /* Context used to resolve WHERE clause */ - WhereInfo *pWInfo; /* Context used by sqlite3WhereXXX() */ - int iFkIfZero = 0; /* Address of OP_FkIfZero */ - Vdbe *v = sqlite3GetVdbe(pParse); - - assert( !pIdx || pIdx->pTable==pTab ); - - if( nIncr<0 ){ - iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0); - } - - /* Create an Expr object representing an SQL expression like: - ** - ** = AND = ... - ** - ** The collation sequence used for the comparison should be that of - ** the parent key columns. The affinity of the parent key column should - ** be applied to each child key value before the comparison takes place. - */ - for(i=0; inCol; i++){ - Expr *pLeft; /* Value from parent table row */ - Expr *pRight; /* Column ref to child table */ - Expr *pEq; /* Expression (pLeft = pRight) */ - int iCol; /* Index of column in child table */ - const char *zCol; /* Name of column in child table */ - - pLeft = sqlite3Expr(db, TK_REGISTER, 0); - if( pLeft ){ - /* Set the collation sequence and affinity of the LHS of each TK_EQ - ** expression to the parent key column defaults. */ - if( pIdx ){ - Column *pCol; - const char *zColl; - iCol = pIdx->aiColumn[i]; - pCol = &pTab->aCol[iCol]; - if( pTab->iPKey==iCol ) iCol = -1; - pLeft->iTable = regData+iCol+1; - pLeft->affinity = pCol->affinity; - zColl = pCol->zColl; - if( zColl==0 ) zColl = db->pDfltColl->zName; - pLeft = sqlite3ExprAddCollateString(pParse, pLeft, zColl); - }else{ - pLeft->iTable = regData; - pLeft->affinity = SQLITE_AFF_INTEGER; - } - } - iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; - assert( iCol>=0 ); - zCol = pFKey->pFrom->aCol[iCol].zName; - pRight = sqlite3Expr(db, TK_ID, zCol); - pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0); - pWhere = sqlite3ExprAnd(db, pWhere, pEq); - } - - /* If the child table is the same as the parent table, and this scan - ** is taking place as part of a DELETE operation (operation D.2), omit the - ** row being deleted from the scan by adding ($rowid != rowid) to the WHERE - ** clause, where $rowid is the rowid of the row being deleted. */ - if( pTab==pFKey->pFrom && nIncr>0 ){ - Expr *pEq; /* Expression (pLeft = pRight) */ - Expr *pLeft; /* Value from parent table row */ - Expr *pRight; /* Column ref to child table */ - pLeft = sqlite3Expr(db, TK_REGISTER, 0); - pRight = sqlite3Expr(db, TK_COLUMN, 0); - if( pLeft && pRight ){ - pLeft->iTable = regData; - pLeft->affinity = SQLITE_AFF_INTEGER; - pRight->iTable = pSrc->a[0].iCursor; - pRight->iColumn = -1; - } - pEq = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0); - pWhere = sqlite3ExprAnd(db, pWhere, pEq); - } - - /* Resolve the references in the WHERE clause. */ - memset(&sNameContext, 0, sizeof(NameContext)); - sNameContext.pSrcList = pSrc; - sNameContext.pParse = pParse; - sqlite3ResolveExprNames(&sNameContext, pWhere); - - /* Create VDBE to loop through the entries in pSrc that match the WHERE - ** clause. If the constraint is not deferred, throw an exception for - ** each row found. Otherwise, for deferred constraints, increment the - ** deferred constraint counter by nIncr for each row selected. */ - pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); - if( nIncr>0 && pFKey->isDeferred==0 ){ - sqlite3ParseToplevel(pParse)->mayAbort = 1; - } - sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); - if( pWInfo ){ - sqlite3WhereEnd(pWInfo); - } - - /* Clean up the WHERE clause constructed above. */ - sqlite3ExprDelete(db, pWhere); - if( iFkIfZero ){ - sqlite3VdbeJumpHere(v, iFkIfZero); - } -} - -/* -** This function returns a pointer to the head of a linked list of FK -** constraints for which table pTab is the parent table. For example, -** given the following schema: -** -** CREATE TABLE t1(a PRIMARY KEY); -** CREATE TABLE t2(b REFERENCES t1(a); -** -** Calling this function with table "t1" as an argument returns a pointer -** to the FKey structure representing the foreign key constraint on table -** "t2". Calling this function with "t2" as the argument would return a -** NULL pointer (as there are no FK constraints for which t2 is the parent -** table). -*/ -SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *pTab){ - int nName = sqlite3Strlen30(pTab->zName); - return (FKey *)sqlite3HashFind(&pTab->pSchema->fkeyHash, pTab->zName, nName); -} - -/* -** The second argument is a Trigger structure allocated by the -** fkActionTrigger() routine. This function deletes the Trigger structure -** and all of its sub-components. -** -** The Trigger structure or any of its sub-components may be allocated from -** the lookaside buffer belonging to database handle dbMem. -*/ -static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){ - if( p ){ - TriggerStep *pStep = p->step_list; - sqlite3ExprDelete(dbMem, pStep->pWhere); - sqlite3ExprListDelete(dbMem, pStep->pExprList); - sqlite3SelectDelete(dbMem, pStep->pSelect); - sqlite3ExprDelete(dbMem, p->pWhen); - sqlite3DbFree(dbMem, p); - } -} - -/* -** This function is called to generate code that runs when table pTab is -** being dropped from the database. The SrcList passed as the second argument -** to this function contains a single entry guaranteed to resolve to -** table pTab. -** -** Normally, no code is required. However, if either -** -** (a) The table is the parent table of a FK constraint, or -** (b) The table is the child table of a deferred FK constraint and it is -** determined at runtime that there are outstanding deferred FK -** constraint violations in the database, -** -** then the equivalent of "DELETE FROM " is executed before dropping -** the table from the database. Triggers are disabled while running this -** DELETE, but foreign key actions are not. -*/ -SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ - sqlite3 *db = pParse->db; - if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) && !pTab->pSelect ){ - int iSkip = 0; - Vdbe *v = sqlite3GetVdbe(pParse); - - assert( v ); /* VDBE has already been allocated */ - if( sqlite3FkReferences(pTab)==0 ){ - /* Search for a deferred foreign key constraint for which this table - ** is the child table. If one cannot be found, return without - ** generating any VDBE code. If one can be found, then jump over - ** the entire DELETE if there are no outstanding deferred constraints - ** when this statement is run. */ - FKey *p; - for(p=pTab->pFKey; p; p=p->pNextFrom){ - if( p->isDeferred ) break; - } - if( !p ) return; - iSkip = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); - } - - pParse->disableTriggers = 1; - sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0); - pParse->disableTriggers = 0; - - /* If the DELETE has generated immediate foreign key constraint - ** violations, halt the VDBE and return an error at this point, before - ** any modifications to the schema are made. This is because statement - ** transactions are not able to rollback schema changes. */ - sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2); - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, - OE_Abort, "foreign key constraint failed", P4_STATIC - ); - - if( iSkip ){ - sqlite3VdbeResolveLabel(v, iSkip); - } - } -} - -/* -** This function is called when inserting, deleting or updating a row of -** table pTab to generate VDBE code to perform foreign key constraint -** processing for the operation. -** -** For a DELETE operation, parameter regOld is passed the index of the -** first register in an array of (pTab->nCol+1) registers containing the -** rowid of the row being deleted, followed by each of the column values -** of the row being deleted, from left to right. Parameter regNew is passed -** zero in this case. -** -** For an INSERT operation, regOld is passed zero and regNew is passed the -** first register of an array of (pTab->nCol+1) registers containing the new -** row data. -** -** For an UPDATE operation, this function is called twice. Once before -** the original record is deleted from the table using the calling convention -** described for DELETE. Then again after the original record is deleted -** but before the new record is inserted using the INSERT convention. -*/ -SQLITE_PRIVATE void sqlite3FkCheck( - Parse *pParse, /* Parse context */ - Table *pTab, /* Row is being deleted from this table */ - int regOld, /* Previous row data is stored here */ - int regNew /* New row data is stored here */ -){ - sqlite3 *db = pParse->db; /* Database handle */ - FKey *pFKey; /* Used to iterate through FKs */ - int iDb; /* Index of database containing pTab */ - const char *zDb; /* Name of database containing pTab */ - int isIgnoreErrors = pParse->disableTriggers; - - /* Exactly one of regOld and regNew should be non-zero. */ - assert( (regOld==0)!=(regNew==0) ); - - /* If foreign-keys are disabled, this function is a no-op. */ - if( (db->flags&SQLITE_ForeignKeys)==0 ) return; - - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - zDb = db->aDb[iDb].zName; - - /* Loop through all the foreign key constraints for which pTab is the - ** child table (the table that the foreign key definition is part of). */ - for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ - Table *pTo; /* Parent table of foreign key pFKey */ - Index *pIdx = 0; /* Index on key columns in pTo */ - int *aiFree = 0; - int *aiCol; - int iCol; - int i; - int isIgnore = 0; - - /* Find the parent table of this foreign key. Also find a unique index - ** on the parent key columns in the parent table. If either of these - ** schema items cannot be located, set an error in pParse and return - ** early. */ - if( pParse->disableTriggers ){ - pTo = sqlite3FindTable(db, pFKey->zTo, zDb); - }else{ - pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb); - } - if( !pTo || sqlite3FkLocateIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){ - assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) ); - if( !isIgnoreErrors || db->mallocFailed ) return; - if( pTo==0 ){ - /* If isIgnoreErrors is true, then a table is being dropped. In this - ** case SQLite runs a "DELETE FROM xxx" on the table being dropped - ** before actually dropping it in order to check FK constraints. - ** If the parent table of an FK constraint on the current table is - ** missing, behave as if it is empty. i.e. decrement the relevant - ** FK counter for each row of the current table with non-NULL keys. - */ - Vdbe *v = sqlite3GetVdbe(pParse); - int iJump = sqlite3VdbeCurrentAddr(v) + pFKey->nCol + 1; - for(i=0; inCol; i++){ - int iReg = pFKey->aCol[i].iFrom + regOld + 1; - sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump); - } - sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, -1); - } - continue; - } - assert( pFKey->nCol==1 || (aiFree && pIdx) ); - - if( aiFree ){ - aiCol = aiFree; - }else{ - iCol = pFKey->aCol[0].iFrom; - aiCol = &iCol; - } - for(i=0; inCol; i++){ - if( aiCol[i]==pTab->iPKey ){ - aiCol[i] = -1; - } -#ifndef SQLITE_OMIT_AUTHORIZATION - /* Request permission to read the parent key columns. If the - ** authorization callback returns SQLITE_IGNORE, behave as if any - ** values read from the parent table are NULL. */ - if( db->xAuth ){ - int rcauth; - char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; - rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); - isIgnore = (rcauth==SQLITE_IGNORE); - } -#endif - } - - /* Take a shared-cache advisory read-lock on the parent table. Allocate - ** a cursor to use to search the unique index on the parent key columns - ** in the parent table. */ - sqlite3TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName); - pParse->nTab++; - - if( regOld!=0 ){ - /* A row is being removed from the child table. Search for the parent. - ** If the parent does not exist, removing the child row resolves an - ** outstanding foreign key constraint violation. */ - fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore); - } - if( regNew!=0 ){ - /* A row is being added to the child table. If a parent row cannot - ** be found, adding the child row has violated the FK constraint. */ - fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore); - } - - sqlite3DbFree(db, aiFree); - } - - /* Loop through all the foreign key constraints that refer to this table */ - for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){ - Index *pIdx = 0; /* Foreign key index for pFKey */ - SrcList *pSrc; - int *aiCol = 0; - - if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){ - assert( regOld==0 && regNew!=0 ); - /* Inserting a single row into a parent table cannot cause an immediate - ** foreign key violation. So do nothing in this case. */ - continue; - } - - if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ - if( !isIgnoreErrors || db->mallocFailed ) return; - continue; - } - assert( aiCol || pFKey->nCol==1 ); - - /* Create a SrcList structure containing a single table (the table - ** the foreign key that refers to this table is attached to). This - ** is required for the sqlite3WhereXXX() interface. */ - pSrc = sqlite3SrcListAppend(db, 0, 0, 0); - if( pSrc ){ - struct SrcList_item *pItem = pSrc->a; - pItem->pTab = pFKey->pFrom; - pItem->zName = pFKey->pFrom->zName; - pItem->pTab->nRef++; - pItem->iCursor = pParse->nTab++; - - if( regNew!=0 ){ - fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); - } - if( regOld!=0 ){ - /* If there is a RESTRICT action configured for the current operation - ** on the parent table of this FK, then throw an exception - ** immediately if the FK constraint is violated, even if this is a - ** deferred trigger. That's what RESTRICT means. To defer checking - ** the constraint, the FK should specify NO ACTION (represented - ** using OE_None). NO ACTION is the default. */ - fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); - } - pItem->zName = 0; - sqlite3SrcListDelete(db, pSrc); - } - sqlite3DbFree(db, aiCol); - } -} - -#define COLUMN_MASK(x) (((x)>31) ? 0xffffffff : ((u32)1<<(x))) - -/* -** This function is called before generating code to update or delete a -** row contained in table pTab. -*/ -SQLITE_PRIVATE u32 sqlite3FkOldmask( - Parse *pParse, /* Parse context */ - Table *pTab /* Table being modified */ -){ - u32 mask = 0; - if( pParse->db->flags&SQLITE_ForeignKeys ){ - FKey *p; - int i; - for(p=pTab->pFKey; p; p=p->pNextFrom){ - for(i=0; inCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); - } - for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ - Index *pIdx = 0; - sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0); - if( pIdx ){ - for(i=0; inColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); - } - } - } - return mask; -} - -/* -** This function is called before generating code to update or delete a -** row contained in table pTab. If the operation is a DELETE, then -** parameter aChange is passed a NULL value. For an UPDATE, aChange points -** to an array of size N, where N is the number of columns in table pTab. -** If the i'th column is not modified by the UPDATE, then the corresponding -** entry in the aChange[] array is set to -1. If the column is modified, -** the value is 0 or greater. Parameter chngRowid is set to true if the -** UPDATE statement modifies the rowid fields of the table. -** -** If any foreign key processing will be required, this function returns -** true. If there is no foreign key related processing, this function -** returns false. -*/ -SQLITE_PRIVATE int sqlite3FkRequired( - Parse *pParse, /* Parse context */ - Table *pTab, /* Table being modified */ - int *aChange, /* Non-NULL for UPDATE operations */ - int chngRowid /* True for UPDATE that affects rowid */ -){ - if( pParse->db->flags&SQLITE_ForeignKeys ){ - if( !aChange ){ - /* A DELETE operation. Foreign key processing is required if the - ** table in question is either the child or parent table for any - ** foreign key constraint. */ - return (sqlite3FkReferences(pTab) || pTab->pFKey); - }else{ - /* This is an UPDATE. Foreign key processing is only required if the - ** operation modifies one or more child or parent key columns. */ - int i; - FKey *p; - - /* Check if any child key columns are being modified. */ - for(p=pTab->pFKey; p; p=p->pNextFrom){ - for(i=0; inCol; i++){ - int iChildKey = p->aCol[i].iFrom; - if( aChange[iChildKey]>=0 ) return 1; - if( iChildKey==pTab->iPKey && chngRowid ) return 1; - } - } - - /* Check if any parent key columns are being modified. */ - for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ - for(i=0; inCol; i++){ - char *zKey = p->aCol[i].zCol; - int iKey; - for(iKey=0; iKeynCol; iKey++){ - Column *pCol = &pTab->aCol[iKey]; - if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey) - : (pCol->colFlags & COLFLAG_PRIMKEY)!=0) ){ - if( aChange[iKey]>=0 ) return 1; - if( iKey==pTab->iPKey && chngRowid ) return 1; - } - } - } - } - } - } - return 0; -} - -/* -** This function is called when an UPDATE or DELETE operation is being -** compiled on table pTab, which is the parent table of foreign-key pFKey. -** If the current operation is an UPDATE, then the pChanges parameter is -** passed a pointer to the list of columns being modified. If it is a -** DELETE, pChanges is passed a NULL pointer. -** -** It returns a pointer to a Trigger structure containing a trigger -** equivalent to the ON UPDATE or ON DELETE action specified by pFKey. -** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is -** returned (these actions require no special handling by the triggers -** sub-system, code for them is created by fkScanChildren()). -** -** For example, if pFKey is the foreign key and pTab is table "p" in -** the following schema: -** -** CREATE TABLE p(pk PRIMARY KEY); -** CREATE TABLE c(ck REFERENCES p ON DELETE CASCADE); -** -** then the returned trigger structure is equivalent to: -** -** CREATE TRIGGER ... DELETE ON p BEGIN -** DELETE FROM c WHERE ck = old.pk; -** END; -** -** The returned pointer is cached as part of the foreign key object. It -** is eventually freed along with the rest of the foreign key object by -** sqlite3FkDelete(). -*/ -static Trigger *fkActionTrigger( - Parse *pParse, /* Parse context */ - Table *pTab, /* Table being updated or deleted from */ - FKey *pFKey, /* Foreign key to get action for */ - ExprList *pChanges /* Change-list for UPDATE, NULL for DELETE */ -){ - sqlite3 *db = pParse->db; /* Database handle */ - int action; /* One of OE_None, OE_Cascade etc. */ - Trigger *pTrigger; /* Trigger definition to return */ - int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */ - - action = pFKey->aAction[iAction]; - pTrigger = pFKey->apTrigger[iAction]; - - if( action!=OE_None && !pTrigger ){ - u8 enableLookaside; /* Copy of db->lookaside.bEnabled */ - char const *zFrom; /* Name of child table */ - int nFrom; /* Length in bytes of zFrom */ - Index *pIdx = 0; /* Parent key index for this FK */ - int *aiCol = 0; /* child table cols -> parent key cols */ - TriggerStep *pStep = 0; /* First (only) step of trigger program */ - Expr *pWhere = 0; /* WHERE clause of trigger step */ - ExprList *pList = 0; /* Changes list if ON UPDATE CASCADE */ - Select *pSelect = 0; /* If RESTRICT, "SELECT RAISE(...)" */ - int i; /* Iterator variable */ - Expr *pWhen = 0; /* WHEN clause for the trigger */ - - if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0; - assert( aiCol || pFKey->nCol==1 ); - - for(i=0; inCol; i++){ - Token tOld = { "old", 3 }; /* Literal "old" token */ - Token tNew = { "new", 3 }; /* Literal "new" token */ - Token tFromCol; /* Name of column in child table */ - Token tToCol; /* Name of column in parent table */ - int iFromCol; /* Idx of column in child table */ - Expr *pEq; /* tFromCol = OLD.tToCol */ - - iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; - assert( iFromCol>=0 ); - tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid"; - tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName; - - tToCol.n = sqlite3Strlen30(tToCol.z); - tFromCol.n = sqlite3Strlen30(tFromCol.z); - - /* Create the expression "OLD.zToCol = zFromCol". It is important - ** that the "OLD.zToCol" term is on the LHS of the = operator, so - ** that the affinity and collation sequence associated with the - ** parent table are used for the comparison. */ - pEq = sqlite3PExpr(pParse, TK_EQ, - sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) - , 0), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tFromCol) - , 0); - pWhere = sqlite3ExprAnd(db, pWhere, pEq); - - /* For ON UPDATE, construct the next term of the WHEN clause. - ** The final WHEN clause will be like this: - ** - ** WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN) - */ - if( pChanges ){ - pEq = sqlite3PExpr(pParse, TK_IS, - sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), - 0), - sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), - 0), - 0); - pWhen = sqlite3ExprAnd(db, pWhen, pEq); - } - - if( action!=OE_Restrict && (action!=OE_Cascade || pChanges) ){ - Expr *pNew; - if( action==OE_Cascade ){ - pNew = sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) - , 0); - }else if( action==OE_SetDflt ){ - Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt; - if( pDflt ){ - pNew = sqlite3ExprDup(db, pDflt, 0); - }else{ - pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); - } - }else{ - pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); - } - pList = sqlite3ExprListAppend(pParse, pList, pNew); - sqlite3ExprListSetName(pParse, pList, &tFromCol, 0); - } - } - sqlite3DbFree(db, aiCol); - - zFrom = pFKey->pFrom->zName; - nFrom = sqlite3Strlen30(zFrom); - - if( action==OE_Restrict ){ - Token tFrom; - Expr *pRaise; - - tFrom.z = zFrom; - tFrom.n = nFrom; - pRaise = sqlite3Expr(db, TK_RAISE, "foreign key constraint failed"); - if( pRaise ){ - pRaise->affinity = OE_Abort; - } - pSelect = sqlite3SelectNew(pParse, - sqlite3ExprListAppend(pParse, 0, pRaise), - sqlite3SrcListAppend(db, 0, &tFrom, 0), - pWhere, - 0, 0, 0, 0, 0, 0 - ); - pWhere = 0; - } - - /* Disable lookaside memory allocation */ - enableLookaside = db->lookaside.bEnabled; - db->lookaside.bEnabled = 0; - - pTrigger = (Trigger *)sqlite3DbMallocZero(db, - sizeof(Trigger) + /* struct Trigger */ - sizeof(TriggerStep) + /* Single step in trigger program */ - nFrom + 1 /* Space for pStep->target.z */ - ); - if( pTrigger ){ - pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1]; - pStep->target.z = (char *)&pStep[1]; - pStep->target.n = nFrom; - memcpy((char *)pStep->target.z, zFrom, nFrom); - - pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); - pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); - pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); - if( pWhen ){ - pWhen = sqlite3PExpr(pParse, TK_NOT, pWhen, 0, 0); - pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); - } - } - - /* Re-enable the lookaside buffer, if it was disabled earlier. */ - db->lookaside.bEnabled = enableLookaside; - - sqlite3ExprDelete(db, pWhere); - sqlite3ExprDelete(db, pWhen); - sqlite3ExprListDelete(db, pList); - sqlite3SelectDelete(db, pSelect); - if( db->mallocFailed==1 ){ - fkTriggerDelete(db, pTrigger); - return 0; - } - assert( pStep!=0 ); - - switch( action ){ - case OE_Restrict: - pStep->op = TK_SELECT; - break; - case OE_Cascade: - if( !pChanges ){ - pStep->op = TK_DELETE; - break; - } - default: - pStep->op = TK_UPDATE; - } - pStep->pTrig = pTrigger; - pTrigger->pSchema = pTab->pSchema; - pTrigger->pTabSchema = pTab->pSchema; - pFKey->apTrigger[iAction] = pTrigger; - pTrigger->op = (pChanges ? TK_UPDATE : TK_DELETE); - } - - return pTrigger; -} - -/* -** This function is called when deleting or updating a row to implement -** any required CASCADE, SET NULL or SET DEFAULT actions. -*/ -SQLITE_PRIVATE void sqlite3FkActions( - Parse *pParse, /* Parse context */ - Table *pTab, /* Table being updated or deleted from */ - ExprList *pChanges, /* Change-list for UPDATE, NULL for DELETE */ - int regOld /* Address of array containing old row */ -){ - /* If foreign-key support is enabled, iterate through all FKs that - ** refer to table pTab. If there is an action associated with the FK - ** for this operation (either update or delete), invoke the associated - ** trigger sub-program. */ - if( pParse->db->flags&SQLITE_ForeignKeys ){ - FKey *pFKey; /* Iterator variable */ - for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){ - Trigger *pAction = fkActionTrigger(pParse, pTab, pFKey, pChanges); - if( pAction ){ - sqlite3CodeRowTriggerDirect(pParse, pAction, pTab, regOld, OE_Abort, 0); - } - } - } -} - -#endif /* ifndef SQLITE_OMIT_TRIGGER */ - -/* -** Free all memory associated with foreign key definitions attached to -** table pTab. Remove the deleted foreign keys from the Schema.fkeyHash -** hash table. -*/ -SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){ - FKey *pFKey; /* Iterator variable */ - FKey *pNext; /* Copy of pFKey->pNextFrom */ - - assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); - for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ - - /* Remove the FK from the fkeyHash hash table. */ - if( !db || db->pnBytesFreed==0 ){ - if( pFKey->pPrevTo ){ - pFKey->pPrevTo->pNextTo = pFKey->pNextTo; - }else{ - void *p = (void *)pFKey->pNextTo; - const char *z = (p ? pFKey->pNextTo->zTo : pFKey->zTo); - sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, sqlite3Strlen30(z), p); - } - if( pFKey->pNextTo ){ - pFKey->pNextTo->pPrevTo = pFKey->pPrevTo; - } - } - - /* EV: R-30323-21917 Each foreign key constraint in SQLite is - ** classified as either immediate or deferred. - */ - assert( pFKey->isDeferred==0 || pFKey->isDeferred==1 ); - - /* Delete any triggers created to implement actions for this FK. */ -#ifndef SQLITE_OMIT_TRIGGER - fkTriggerDelete(db, pFKey->apTrigger[0]); - fkTriggerDelete(db, pFKey->apTrigger[1]); -#endif - - pNext = pFKey->pNextFrom; - sqlite3DbFree(db, pFKey); - } -} -#endif /* ifndef SQLITE_OMIT_FOREIGN_KEY */ - -/************** End of fkey.c ************************************************/ -/************** Begin file insert.c ******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the parser -** to handle INSERT statements in SQLite. -*/ - -/* -** Generate code that will open a table for reading. -*/ -SQLITE_PRIVATE void sqlite3OpenTable( - Parse *p, /* Generate code into this VDBE */ - int iCur, /* The cursor number of the table */ - int iDb, /* The database index in sqlite3.aDb[] */ - Table *pTab, /* The table to be opened */ - int opcode /* OP_OpenRead or OP_OpenWrite */ -){ - Vdbe *v; - assert( !IsVirtual(pTab) ); - v = sqlite3GetVdbe(p); - assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); - sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName); - sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32); - VdbeComment((v, "%s", pTab->zName)); -} - -/* -** Return a pointer to the column affinity string associated with index -** pIdx. A column affinity string has one character for each column in -** the table, according to the affinity of the column: -** -** Character Column affinity -** ------------------------------ -** 'a' TEXT -** 'b' NONE -** 'c' NUMERIC -** 'd' INTEGER -** 'e' REAL -** -** An extra 'd' is appended to the end of the string to cover the -** rowid that appears as the last column in every index. -** -** Memory for the buffer containing the column index affinity string -** is managed along with the rest of the Index structure. It will be -** released when sqlite3DeleteIndex() is called. -*/ -SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ - if( !pIdx->zColAff ){ - /* The first time a column affinity string for a particular index is - ** required, it is allocated and populated here. It is then stored as - ** a member of the Index structure for subsequent use. - ** - ** The column affinity string will eventually be deleted by - ** sqliteDeleteIndex() when the Index structure itself is cleaned - ** up. - */ - int n; - Table *pTab = pIdx->pTable; - sqlite3 *db = sqlite3VdbeDb(v); - pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+2); - if( !pIdx->zColAff ){ - db->mallocFailed = 1; - return 0; - } - for(n=0; nnColumn; n++){ - pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; - } - pIdx->zColAff[n++] = SQLITE_AFF_INTEGER; - pIdx->zColAff[n] = 0; - } - - return pIdx->zColAff; -} - -/* -** Set P4 of the most recently inserted opcode to a column affinity -** string for table pTab. A column affinity string has one character -** for each column indexed by the index, according to the affinity of the -** column: -** -** Character Column affinity -** ------------------------------ -** 'a' TEXT -** 'b' NONE -** 'c' NUMERIC -** 'd' INTEGER -** 'e' REAL -*/ -SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ - /* The first time a column affinity string for a particular table - ** is required, it is allocated and populated here. It is then - ** stored as a member of the Table structure for subsequent use. - ** - ** The column affinity string will eventually be deleted by - ** sqlite3DeleteTable() when the Table structure itself is cleaned up. - */ - if( !pTab->zColAff ){ - char *zColAff; - int i; - sqlite3 *db = sqlite3VdbeDb(v); - - zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1); - if( !zColAff ){ - db->mallocFailed = 1; - return; - } - - for(i=0; inCol; i++){ - zColAff[i] = pTab->aCol[i].affinity; - } - zColAff[pTab->nCol] = '\0'; - - pTab->zColAff = zColAff; - } - - sqlite3VdbeChangeP4(v, -1, pTab->zColAff, P4_TRANSIENT); -} - -/* -** Return non-zero if the table pTab in database iDb or any of its indices -** have been opened at any point in the VDBE program beginning at location -** iStartAddr throught the end of the program. This is used to see if -** a statement of the form "INSERT INTO SELECT ..." can -** run without using temporary table for the results of the SELECT. -*/ -static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){ - Vdbe *v = sqlite3GetVdbe(p); - int i; - int iEnd = sqlite3VdbeCurrentAddr(v); -#ifndef SQLITE_OMIT_VIRTUALTABLE - VTable *pVTab = IsVirtual(pTab) ? sqlite3GetVTable(p->db, pTab) : 0; -#endif - - for(i=iStartAddr; iopcode==OP_OpenRead && pOp->p3==iDb ){ - Index *pIndex; - int tnum = pOp->p2; - if( tnum==pTab->tnum ){ - return 1; - } - for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ - if( tnum==pIndex->tnum ){ - return 1; - } - } - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( pOp->opcode==OP_VOpen && pOp->p4.pVtab==pVTab ){ - assert( pOp->p4.pVtab!=0 ); - assert( pOp->p4type==P4_VTAB ); - return 1; - } -#endif - } - return 0; -} - -#ifndef SQLITE_OMIT_AUTOINCREMENT -/* -** Locate or create an AutoincInfo structure associated with table pTab -** which is in database iDb. Return the register number for the register -** that holds the maximum rowid. -** -** There is at most one AutoincInfo structure per table even if the -** same table is autoincremented multiple times due to inserts within -** triggers. A new AutoincInfo structure is created if this is the -** first use of table pTab. On 2nd and subsequent uses, the original -** AutoincInfo structure is used. -** -** Three memory locations are allocated: -** -** (1) Register to hold the name of the pTab table. -** (2) Register to hold the maximum ROWID of pTab. -** (3) Register to hold the rowid in sqlite_sequence of pTab -** -** The 2nd register is the one that is returned. That is all the -** insert routine needs to know about. -*/ -static int autoIncBegin( - Parse *pParse, /* Parsing context */ - int iDb, /* Index of the database holding pTab */ - Table *pTab /* The table we are writing to */ -){ - int memId = 0; /* Register holding maximum rowid */ - if( pTab->tabFlags & TF_Autoincrement ){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); - AutoincInfo *pInfo; - - pInfo = pToplevel->pAinc; - while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } - if( pInfo==0 ){ - pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo)); - if( pInfo==0 ) return 0; - pInfo->pNext = pToplevel->pAinc; - pToplevel->pAinc = pInfo; - pInfo->pTab = pTab; - pInfo->iDb = iDb; - pToplevel->nMem++; /* Register to hold name of table */ - pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */ - pToplevel->nMem++; /* Rowid in sqlite_sequence */ - } - memId = pInfo->regCtr; - } - return memId; -} - -/* -** This routine generates code that will initialize all of the -** register used by the autoincrement tracker. -*/ -SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){ - AutoincInfo *p; /* Information about an AUTOINCREMENT */ - sqlite3 *db = pParse->db; /* The database connection */ - Db *pDb; /* Database only autoinc table */ - int memId; /* Register holding max rowid */ - int addr; /* A VDBE address */ - Vdbe *v = pParse->pVdbe; /* VDBE under construction */ - - /* This routine is never called during trigger-generation. It is - ** only called from the top-level */ - assert( pParse->pTriggerTab==0 ); - assert( pParse==sqlite3ParseToplevel(pParse) ); - - assert( v ); /* We failed long ago if this is not so */ - for(p = pParse->pAinc; p; p = p->pNext){ - pDb = &db->aDb[p->iDb]; - memId = p->regCtr; - assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); - sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); - sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1); - addr = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); - sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); - sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId); - sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); - sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); - sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); - sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9); - sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); - sqlite3VdbeAddOp2(v, OP_Integer, 0, memId); - sqlite3VdbeAddOp0(v, OP_Close); - } -} - -/* -** Update the maximum rowid for an autoincrement calculation. -** -** This routine should be called when the top of the stack holds a -** new rowid that is about to be inserted. If that new rowid is -** larger than the maximum rowid in the memId memory cell, then the -** memory cell is updated. The stack is unchanged. -*/ -static void autoIncStep(Parse *pParse, int memId, int regRowid){ - if( memId>0 ){ - sqlite3VdbeAddOp2(pParse->pVdbe, OP_MemMax, memId, regRowid); - } -} - -/* -** This routine generates the code needed to write autoincrement -** maximum rowid values back into the sqlite_sequence register. -** Every statement that might do an INSERT into an autoincrement -** table (either directly or through triggers) needs to call this -** routine just before the "exit" code. -*/ -SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){ - AutoincInfo *p; - Vdbe *v = pParse->pVdbe; - sqlite3 *db = pParse->db; - - assert( v ); - for(p = pParse->pAinc; p; p = p->pNext){ - Db *pDb = &db->aDb[p->iDb]; - int j1, j2, j3, j4, j5; - int iRec; - int memId = p->regCtr; - - iRec = sqlite3GetTempReg(pParse); - assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); - sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); - j2 = sqlite3VdbeAddOp0(v, OP_Rewind); - j3 = sqlite3VdbeAddOp3(v, OP_Column, 0, 0, iRec); - j4 = sqlite3VdbeAddOp3(v, OP_Eq, memId-1, 0, iRec); - sqlite3VdbeAddOp2(v, OP_Next, 0, j3); - sqlite3VdbeJumpHere(v, j2); - sqlite3VdbeAddOp2(v, OP_NewRowid, 0, memId+1); - j5 = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, j4); - sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); - sqlite3VdbeJumpHere(v, j1); - sqlite3VdbeJumpHere(v, j5); - sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec); - sqlite3VdbeAddOp3(v, OP_Insert, 0, iRec, memId+1); - sqlite3VdbeChangeP5(v, OPFLAG_APPEND); - sqlite3VdbeAddOp0(v, OP_Close); - sqlite3ReleaseTempReg(pParse, iRec); - } -} -#else -/* -** If SQLITE_OMIT_AUTOINCREMENT is defined, then the three routines -** above are all no-ops -*/ -# define autoIncBegin(A,B,C) (0) -# define autoIncStep(A,B,C) -#endif /* SQLITE_OMIT_AUTOINCREMENT */ - - -/* -** Generate code for a co-routine that will evaluate a subquery one -** row at a time. -** -** The pSelect parameter is the subquery that the co-routine will evaluation. -** Information about the location of co-routine and the registers it will use -** is returned by filling in the pDest object. -** -** Registers are allocated as follows: -** -** pDest->iSDParm The register holding the next entry-point of the -** co-routine. Run the co-routine to its next breakpoint -** by calling "OP_Yield $X" where $X is pDest->iSDParm. -** -** pDest->iSDParm+1 The register holding the "completed" flag for the -** co-routine. This register is 0 if the previous Yield -** generated a new result row, or 1 if the subquery -** has completed. If the Yield is called again -** after this register becomes 1, then the VDBE will -** halt with an SQLITE_INTERNAL error. -** -** pDest->iSdst First result register. -** -** pDest->nSdst Number of result registers. -** -** This routine handles all of the register allocation and fills in the -** pDest structure appropriately. -** -** Here is a schematic of the generated code assuming that X is the -** co-routine entry-point register reg[pDest->iSDParm], that EOF is the -** completed flag reg[pDest->iSDParm+1], and R and S are the range of -** registers that hold the result set, reg[pDest->iSdst] through -** reg[pDest->iSdst+pDest->nSdst-1]: -** -** X <- A -** EOF <- 0 -** goto B -** A: setup for the SELECT -** loop rows in the SELECT -** load results into registers R..S -** yield X -** end loop -** cleanup after the SELECT -** EOF <- 1 -** yield X -** halt-error -** B: -** -** To use this subroutine, the caller generates code as follows: -** -** [ Co-routine generated by this subroutine, shown above ] -** S: yield X -** if EOF goto E -** if skip this row, goto C -** if terminate loop, goto E -** deal with this row -** C: goto S -** E: -*/ -SQLITE_PRIVATE int sqlite3CodeCoroutine(Parse *pParse, Select *pSelect, SelectDest *pDest){ - int regYield; /* Register holding co-routine entry-point */ - int regEof; /* Register holding co-routine completion flag */ - int addrTop; /* Top of the co-routine */ - int j1; /* Jump instruction */ - int rc; /* Result code */ - Vdbe *v; /* VDBE under construction */ - - regYield = ++pParse->nMem; - regEof = ++pParse->nMem; - v = sqlite3GetVdbe(pParse); - addrTop = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_Integer, addrTop+2, regYield); /* X <- A */ - VdbeComment((v, "Co-routine entry point")); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regEof); /* EOF <- 0 */ - VdbeComment((v, "Co-routine completion flag")); - sqlite3SelectDestInit(pDest, SRT_Coroutine, regYield); - j1 = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); - rc = sqlite3Select(pParse, pSelect, pDest); - assert( pParse->nErr==0 || rc ); - if( pParse->db->mallocFailed && rc==SQLITE_OK ) rc = SQLITE_NOMEM; - if( rc ) return rc; - sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); /* EOF <- 1 */ - sqlite3VdbeAddOp1(v, OP_Yield, regYield); /* yield X */ - sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_INTERNAL, OE_Abort); - VdbeComment((v, "End of coroutine")); - sqlite3VdbeJumpHere(v, j1); /* label B: */ - return rc; -} - - - -/* Forward declaration */ -static int xferOptimization( - Parse *pParse, /* Parser context */ - Table *pDest, /* The table we are inserting into */ - Select *pSelect, /* A SELECT statement to use as the data source */ - int onError, /* How to handle constraint errors */ - int iDbDest /* The database of pDest */ -); - -/* -** This routine is call to handle SQL of the following forms: -** -** insert into TABLE (IDLIST) values(EXPRLIST) -** insert into TABLE (IDLIST) select -** -** The IDLIST following the table name is always optional. If omitted, -** then a list of all columns for the table is substituted. The IDLIST -** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted. -** -** The pList parameter holds EXPRLIST in the first form of the INSERT -** statement above, and pSelect is NULL. For the second form, pList is -** NULL and pSelect is a pointer to the select statement used to generate -** data for the insert. -** -** The code generated follows one of four templates. For a simple -** select with data coming from a VALUES clause, the code executes -** once straight down through. Pseudo-code follows (we call this -** the "1st template"): -** -** open write cursor to
    and its indices -** puts VALUES clause expressions onto the stack -** write the resulting record into
    -** cleanup -** -** The three remaining templates assume the statement is of the form -** -** INSERT INTO
    SELECT ... -** -** If the SELECT clause is of the restricted form "SELECT * FROM " - -** in other words if the SELECT pulls all columns from a single table -** and there is no WHERE or LIMIT or GROUP BY or ORDER BY clauses, and -** if and are distinct tables but have identical -** schemas, including all the same indices, then a special optimization -** is invoked that copies raw records from over to . -** See the xferOptimization() function for the implementation of this -** template. This is the 2nd template. -** -** open a write cursor to
    -** open read cursor on -** transfer all records in over to
    -** close cursors -** foreach index on
    -** open a write cursor on the
    index -** open a read cursor on the corresponding index -** transfer all records from the read to the write cursors -** close cursors -** end foreach -** -** The 3rd template is for when the second template does not apply -** and the SELECT clause does not read from
    at any time. -** The generated code follows this template: -** -** EOF <- 0 -** X <- A -** goto B -** A: setup for the SELECT -** loop over the rows in the SELECT -** load values into registers R..R+n -** yield X -** end loop -** cleanup after the SELECT -** EOF <- 1 -** yield X -** goto A -** B: open write cursor to
    and its indices -** C: yield X -** if EOF goto D -** insert the select result into
    from R..R+n -** goto C -** D: cleanup -** -** The 4th template is used if the insert statement takes its -** values from a SELECT but the data is being inserted into a table -** that is also read as part of the SELECT. In the third form, -** we have to use a intermediate table to store the results of -** the select. The template is like this: -** -** EOF <- 0 -** X <- A -** goto B -** A: setup for the SELECT -** loop over the tables in the SELECT -** load value into register R..R+n -** yield X -** end loop -** cleanup after the SELECT -** EOF <- 1 -** yield X -** halt-error -** B: open temp table -** L: yield X -** if EOF goto M -** insert row from R..R+n into temp table -** goto L -** M: open write cursor to
    and its indices -** rewind temp table -** C: loop over rows of intermediate table -** transfer values form intermediate table into
    -** end loop -** D: cleanup -*/ -SQLITE_PRIVATE void sqlite3Insert( - Parse *pParse, /* Parser context */ - SrcList *pTabList, /* Name of table into which we are inserting */ - ExprList *pList, /* List of values to be inserted */ - Select *pSelect, /* A SELECT statement to use as the data source */ - IdList *pColumn, /* Column names corresponding to IDLIST. */ - int onError /* How to handle constraint errors */ -){ - sqlite3 *db; /* The main database structure */ - Table *pTab; /* The table to insert into. aka TABLE */ - char *zTab; /* Name of the table into which we are inserting */ - const char *zDb; /* Name of the database holding this table */ - int i, j, idx; /* Loop counters */ - Vdbe *v; /* Generate code into this virtual machine */ - Index *pIdx; /* For looping over indices of the table */ - int nColumn; /* Number of columns in the data */ - int nHidden = 0; /* Number of hidden columns if TABLE is virtual */ - int baseCur = 0; /* VDBE Cursor number for pTab */ - int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ - int endOfLoop; /* Label for the end of the insertion loop */ - int useTempTable = 0; /* Store SELECT results in intermediate table */ - int srcTab = 0; /* Data comes from this temporary cursor if >=0 */ - int addrInsTop = 0; /* Jump to label "D" */ - int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */ - int addrSelect = 0; /* Address of coroutine that implements the SELECT */ - SelectDest dest; /* Destination for SELECT on rhs of INSERT */ - int iDb; /* Index of database holding TABLE */ - Db *pDb; /* The database containing table being inserted into */ - int appendFlag = 0; /* True if the insert is likely to be an append */ - - /* Register allocations */ - int regFromSelect = 0;/* Base register for data coming from SELECT */ - int regAutoinc = 0; /* Register holding the AUTOINCREMENT counter */ - int regRowCount = 0; /* Memory cell used for the row counter */ - int regIns; /* Block of regs holding rowid+data being inserted */ - int regRowid; /* registers holding insert rowid */ - int regData; /* register holding first column to insert */ - int regEof = 0; /* Register recording end of SELECT data */ - int *aRegIdx = 0; /* One register allocated to each index */ - -#ifndef SQLITE_OMIT_TRIGGER - int isView; /* True if attempting to insert into a view */ - Trigger *pTrigger; /* List of triggers on pTab, if required */ - int tmask; /* Mask of trigger times */ -#endif - - db = pParse->db; - memset(&dest, 0, sizeof(dest)); - if( pParse->nErr || db->mallocFailed ){ - goto insert_cleanup; - } - - /* Locate the table into which we will be inserting new information. - */ - assert( pTabList->nSrc==1 ); - zTab = pTabList->a[0].zName; - if( NEVER(zTab==0) ) goto insert_cleanup; - pTab = sqlite3SrcListLookup(pParse, pTabList); - if( pTab==0 ){ - goto insert_cleanup; - } - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - assert( iDbnDb ); - pDb = &db->aDb[iDb]; - zDb = pDb->zName; - if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){ - goto insert_cleanup; - } - - /* Figure out if we have any triggers and if the table being - ** inserted into is a view - */ -#ifndef SQLITE_OMIT_TRIGGER - pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask); - isView = pTab->pSelect!=0; -#else -# define pTrigger 0 -# define tmask 0 -# define isView 0 -#endif -#ifdef SQLITE_OMIT_VIEW -# undef isView -# define isView 0 -#endif - assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); - - /* If pTab is really a view, make sure it has been initialized. - ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual - ** module table). - */ - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto insert_cleanup; - } - - /* Ensure that: - * (a) the table is not read-only, - * (b) that if it is a view then ON INSERT triggers exist - */ - if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ - goto insert_cleanup; - } - - /* Allocate a VDBE - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto insert_cleanup; - if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb); - -#ifndef SQLITE_OMIT_XFER_OPT - /* If the statement is of the form - ** - ** INSERT INTO SELECT * FROM ; - ** - ** Then special optimizations can be applied that make the transfer - ** very fast and which reduce fragmentation of indices. - ** - ** This is the 2nd template. - */ - if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ - assert( !pTrigger ); - assert( pList==0 ); - goto insert_end; - } -#endif /* SQLITE_OMIT_XFER_OPT */ - - /* If this is an AUTOINCREMENT table, look up the sequence number in the - ** sqlite_sequence table and store it in memory cell regAutoinc. - */ - regAutoinc = autoIncBegin(pParse, iDb, pTab); - - /* Figure out how many columns of data are supplied. If the data - ** is coming from a SELECT statement, then generate a co-routine that - ** produces a single row of the SELECT on each invocation. The - ** co-routine is the common header to the 3rd and 4th templates. - */ - if( pSelect ){ - /* Data is coming from a SELECT. Generate a co-routine to run that - ** SELECT. */ - int rc = sqlite3CodeCoroutine(pParse, pSelect, &dest); - if( rc ) goto insert_cleanup; - - regEof = dest.iSDParm + 1; - regFromSelect = dest.iSdst; - assert( pSelect->pEList ); - nColumn = pSelect->pEList->nExpr; - assert( dest.nSdst==nColumn ); - - /* Set useTempTable to TRUE if the result of the SELECT statement - ** should be written into a temporary table (template 4). Set to - ** FALSE if each* row of the SELECT can be written directly into - ** the destination table (template 3). - ** - ** A temp table must be used if the table being updated is also one - ** of the tables being read by the SELECT statement. Also use a - ** temp table in the case of row triggers. - */ - if( pTrigger || readsTable(pParse, addrSelect, iDb, pTab) ){ - useTempTable = 1; - } - - if( useTempTable ){ - /* Invoke the coroutine to extract information from the SELECT - ** and add it to a transient table srcTab. The code generated - ** here is from the 4th template: - ** - ** B: open temp table - ** L: yield X - ** if EOF goto M - ** insert row from R..R+n into temp table - ** goto L - ** M: ... - */ - int regRec; /* Register to hold packed record */ - int regTempRowid; /* Register to hold temp table ROWID */ - int addrTop; /* Label "L" */ - int addrIf; /* Address of jump to M */ - - srcTab = pParse->nTab++; - regRec = sqlite3GetTempReg(pParse); - regTempRowid = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn); - addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); - addrIf = sqlite3VdbeAddOp1(v, OP_If, regEof); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec); - sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regTempRowid); - sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regTempRowid); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); - sqlite3VdbeJumpHere(v, addrIf); - sqlite3ReleaseTempReg(pParse, regRec); - sqlite3ReleaseTempReg(pParse, regTempRowid); - } - }else{ - /* This is the case if the data for the INSERT is coming from a VALUES - ** clause - */ - NameContext sNC; - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - srcTab = -1; - assert( useTempTable==0 ); - nColumn = pList ? pList->nExpr : 0; - for(i=0; ia[i].pExpr) ){ - goto insert_cleanup; - } - } - } - - /* Make sure the number of columns in the source data matches the number - ** of columns to be inserted into the table. - */ - if( IsVirtual(pTab) ){ - for(i=0; inCol; i++){ - nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0); - } - } - if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){ - sqlite3ErrorMsg(pParse, - "table %S has %d columns but %d values were supplied", - pTabList, 0, pTab->nCol-nHidden, nColumn); - goto insert_cleanup; - } - if( pColumn!=0 && nColumn!=pColumn->nId ){ - sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); - goto insert_cleanup; - } - - /* If the INSERT statement included an IDLIST term, then make sure - ** all elements of the IDLIST really are columns of the table and - ** remember the column indices. - ** - ** If the table has an INTEGER PRIMARY KEY column and that column - ** is named in the IDLIST, then record in the keyColumn variable - ** the index into IDLIST of the primary key column. keyColumn is - ** the index of the primary key as it appears in IDLIST, not as - ** is appears in the original table. (The index of the primary - ** key in the original table is pTab->iPKey.) - */ - if( pColumn ){ - for(i=0; inId; i++){ - pColumn->a[i].idx = -1; - } - for(i=0; inId; i++){ - for(j=0; jnCol; j++){ - if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ - pColumn->a[i].idx = j; - if( j==pTab->iPKey ){ - keyColumn = i; - } - break; - } - } - if( j>=pTab->nCol ){ - if( sqlite3IsRowid(pColumn->a[i].zName) ){ - keyColumn = i; - }else{ - sqlite3ErrorMsg(pParse, "table %S has no column named %s", - pTabList, 0, pColumn->a[i].zName); - pParse->checkSchema = 1; - goto insert_cleanup; - } - } - } - } - - /* If there is no IDLIST term but the table has an integer primary - ** key, the set the keyColumn variable to the primary key column index - ** in the original table definition. - */ - if( pColumn==0 && nColumn>0 ){ - keyColumn = pTab->iPKey; - } - - /* Initialize the count of rows to be inserted - */ - if( db->flags & SQLITE_CountRows ){ - regRowCount = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); - } - - /* If this is not a view, open the table and and all indices */ - if( !isView ){ - int nIdx; - - baseCur = pParse->nTab; - nIdx = sqlite3OpenTableAndIndices(pParse, pTab, baseCur, OP_OpenWrite); - aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1)); - if( aRegIdx==0 ){ - goto insert_cleanup; - } - for(i=0; inMem; - } - } - - /* This is the top of the main insertion loop */ - if( useTempTable ){ - /* This block codes the top of loop only. The complete loop is the - ** following pseudocode (template 4): - ** - ** rewind temp table - ** C: loop over rows of intermediate table - ** transfer values form intermediate table into
    - ** end loop - ** D: ... - */ - addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab); - addrCont = sqlite3VdbeCurrentAddr(v); - }else if( pSelect ){ - /* This block codes the top of loop only. The complete loop is the - ** following pseudocode (template 3): - ** - ** C: yield X - ** if EOF goto D - ** insert the select result into
    from R..R+n - ** goto C - ** D: ... - */ - addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); - addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof); - } - - /* Allocate registers for holding the rowid of the new row, - ** the content of the new row, and the assemblied row record. - */ - regRowid = regIns = pParse->nMem+1; - pParse->nMem += pTab->nCol + 1; - if( IsVirtual(pTab) ){ - regRowid++; - pParse->nMem++; - } - regData = regRowid+1; - - /* Run the BEFORE and INSTEAD OF triggers, if there are any - */ - endOfLoop = sqlite3VdbeMakeLabel(v); - if( tmask & TRIGGER_BEFORE ){ - int regCols = sqlite3GetTempRange(pParse, pTab->nCol+1); - - /* build the NEW.* reference row. Note that if there is an INTEGER - ** PRIMARY KEY into which a NULL is being inserted, that NULL will be - ** translated into a unique ID for the row. But on a BEFORE trigger, - ** we do not know what the unique ID will be (because the insert has - ** not happened yet) so we substitute a rowid of -1 - */ - if( keyColumn<0 ){ - sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); - }else{ - int j1; - if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regCols); - }else{ - assert( pSelect==0 ); /* Otherwise useTempTable is true */ - sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regCols); - } - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); - sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); - sqlite3VdbeJumpHere(v, j1); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); - } - - /* Cannot have triggers on a virtual table. If it were possible, - ** this block would have to account for hidden column. - */ - assert( !IsVirtual(pTab) ); - - /* Create the new column data - */ - for(i=0; inCol; i++){ - if( pColumn==0 ){ - j = i; - }else{ - for(j=0; jnId; j++){ - if( pColumn->a[j].idx==i ) break; - } - } - if( (!useTempTable && !pList) || (pColumn && j>=pColumn->nId) ){ - sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1); - }else if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1); - }else{ - assert( pSelect==0 ); /* Otherwise useTempTable is true */ - sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1); - } - } - - /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger, - ** do not attempt any conversions before assembling the record. - ** If this is a real table, attempt conversions as required by the - ** table column affinities. - */ - if( !isView ){ - sqlite3VdbeAddOp2(v, OP_Affinity, regCols+1, pTab->nCol); - sqlite3TableAffinityStr(v, pTab); - } - - /* Fire BEFORE or INSTEAD OF triggers */ - sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, - pTab, regCols-pTab->nCol-1, onError, endOfLoop); - - sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1); - } - - /* Push the record number for the new entry onto the stack. The - ** record number is a randomly generate integer created by NewRowid - ** except when the table has an INTEGER PRIMARY KEY column, in which - ** case the record number is the same as that column. - */ - if( !isView ){ - if( IsVirtual(pTab) ){ - /* The row that the VUpdate opcode will delete: none */ - sqlite3VdbeAddOp2(v, OP_Null, 0, regIns); - } - if( keyColumn>=0 ){ - if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regRowid); - }else if( pSelect ){ - sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+keyColumn, regRowid); - }else{ - VdbeOp *pOp; - sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regRowid); - pOp = sqlite3VdbeGetOp(v, -1); - if( ALWAYS(pOp) && pOp->opcode==OP_Null && !IsVirtual(pTab) ){ - appendFlag = 1; - pOp->opcode = OP_NewRowid; - pOp->p1 = baseCur; - pOp->p2 = regRowid; - pOp->p3 = regAutoinc; - } - } - /* If the PRIMARY KEY expression is NULL, then use OP_NewRowid - ** to generate a unique primary key value. - */ - if( !appendFlag ){ - int j1; - if( !IsVirtual(pTab) ){ - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); - sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc); - sqlite3VdbeJumpHere(v, j1); - }else{ - j1 = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, j1+2); - } - sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); - } - }else if( IsVirtual(pTab) ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, regRowid); - }else{ - sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc); - appendFlag = 1; - } - autoIncStep(pParse, regAutoinc, regRowid); - - /* Push onto the stack, data for all columns of the new entry, beginning - ** with the first column. - */ - nHidden = 0; - for(i=0; inCol; i++){ - int iRegStore = regRowid+1+i; - if( i==pTab->iPKey ){ - /* The value of the INTEGER PRIMARY KEY column is always a NULL. - ** Whenever this column is read, the record number will be substituted - ** in its place. So will fill this column with a NULL to avoid - ** taking up data space with information that will never be used. */ - sqlite3VdbeAddOp2(v, OP_Null, 0, iRegStore); - continue; - } - if( pColumn==0 ){ - if( IsHiddenColumn(&pTab->aCol[i]) ){ - assert( IsVirtual(pTab) ); - j = -1; - nHidden++; - }else{ - j = i - nHidden; - } - }else{ - for(j=0; jnId; j++){ - if( pColumn->a[j].idx==i ) break; - } - } - if( j<0 || nColumn==0 || (pColumn && j>=pColumn->nId) ){ - sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, iRegStore); - }else if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore); - }else if( pSelect ){ - sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore); - }else{ - sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore); - } - } - - /* Generate code to check constraints and generate index keys and - ** do the insertion. - */ -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) ){ - const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); - sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); - sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); - sqlite3MayAbort(pParse); - }else -#endif - { - int isReplace; /* Set to true if constraints may cause a replace */ - sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx, - keyColumn>=0, 0, onError, endOfLoop, &isReplace - ); - sqlite3FkCheck(pParse, pTab, 0, regIns); - sqlite3CompleteInsertion( - pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0 - ); - } - } - - /* Update the count of rows that are inserted - */ - if( (db->flags & SQLITE_CountRows)!=0 ){ - sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); - } - - if( pTrigger ){ - /* Code AFTER triggers */ - sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, - pTab, regData-2-pTab->nCol, onError, endOfLoop); - } - - /* The bottom of the main insertion loop, if the data source - ** is a SELECT statement. - */ - sqlite3VdbeResolveLabel(v, endOfLoop); - if( useTempTable ){ - sqlite3VdbeAddOp2(v, OP_Next, srcTab, addrCont); - sqlite3VdbeJumpHere(v, addrInsTop); - sqlite3VdbeAddOp1(v, OP_Close, srcTab); - }else if( pSelect ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrCont); - sqlite3VdbeJumpHere(v, addrInsTop); - } - - if( !IsVirtual(pTab) && !isView ){ - /* Close all tables opened */ - sqlite3VdbeAddOp1(v, OP_Close, baseCur); - for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqlite3VdbeAddOp1(v, OP_Close, idx+baseCur); - } - } - -insert_end: - /* Update the sqlite_sequence table by storing the content of the - ** maximum rowid counter values recorded while inserting into - ** autoincrement tables. - */ - if( pParse->nested==0 && pParse->pTriggerTab==0 ){ - sqlite3AutoincrementEnd(pParse); - } - - /* - ** Return the number of rows inserted. If this routine is - ** generating code because of a call to sqlite3NestedParse(), do not - ** invoke the callback function. - */ - if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){ - sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC); - } - -insert_cleanup: - sqlite3SrcListDelete(db, pTabList); - sqlite3ExprListDelete(db, pList); - sqlite3SelectDelete(db, pSelect); - sqlite3IdListDelete(db, pColumn); - sqlite3DbFree(db, aRegIdx); -} - -/* Make sure "isView" and other macros defined above are undefined. Otherwise -** thely may interfere with compilation of other functions in this file -** (or in another file, if this file becomes part of the amalgamation). */ -#ifdef isView - #undef isView -#endif -#ifdef pTrigger - #undef pTrigger -#endif -#ifdef tmask - #undef tmask -#endif - - -/* -** Generate code to do constraint checks prior to an INSERT or an UPDATE. -** -** The input is a range of consecutive registers as follows: -** -** 1. The rowid of the row after the update. -** -** 2. The data in the first column of the entry after the update. -** -** i. Data from middle columns... -** -** N. The data in the last column of the entry after the update. -** -** The regRowid parameter is the index of the register containing (1). -** -** If isUpdate is true and rowidChng is non-zero, then rowidChng contains -** the address of a register containing the rowid before the update takes -** place. isUpdate is true for UPDATEs and false for INSERTs. If isUpdate -** is false, indicating an INSERT statement, then a non-zero rowidChng -** indicates that the rowid was explicitly specified as part of the -** INSERT statement. If rowidChng is false, it means that the rowid is -** computed automatically in an insert or that the rowid value is not -** modified by an update. -** -** The code generated by this routine store new index entries into -** registers identified by aRegIdx[]. No index entry is created for -** indices where aRegIdx[i]==0. The order of indices in aRegIdx[] is -** the same as the order of indices on the linked list of indices -** attached to the table. -** -** This routine also generates code to check constraints. NOT NULL, -** CHECK, and UNIQUE constraints are all checked. If a constraint fails, -** then the appropriate action is performed. There are five possible -** actions: ROLLBACK, ABORT, FAIL, REPLACE, and IGNORE. -** -** Constraint type Action What Happens -** --------------- ---------- ---------------------------------------- -** any ROLLBACK The current transaction is rolled back and -** sqlite3_exec() returns immediately with a -** return code of SQLITE_CONSTRAINT. -** -** any ABORT Back out changes from the current command -** only (do not do a complete rollback) then -** cause sqlite3_exec() to return immediately -** with SQLITE_CONSTRAINT. -** -** any FAIL Sqlite3_exec() returns immediately with a -** return code of SQLITE_CONSTRAINT. The -** transaction is not rolled back and any -** prior changes are retained. -** -** any IGNORE The record number and data is popped from -** the stack and there is an immediate jump -** to label ignoreDest. -** -** NOT NULL REPLACE The NULL value is replace by the default -** value for that column. If the default value -** is NULL, the action is the same as ABORT. -** -** UNIQUE REPLACE The other row that conflicts with the row -** being inserted is removed. -** -** CHECK REPLACE Illegal. The results in an exception. -** -** Which action to take is determined by the overrideError parameter. -** Or if overrideError==OE_Default, then the pParse->onError parameter -** is used. Or if pParse->onError==OE_Default then the onError value -** for the constraint is used. -** -** The calling routine must open a read/write cursor for pTab with -** cursor number "baseCur". All indices of pTab must also have open -** read/write cursors with cursor number baseCur+i for the i-th cursor. -** Except, if there is no possibility of a REPLACE action then -** cursors do not need to be open for indices where aRegIdx[i]==0. -*/ -SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - Parse *pParse, /* The parser context */ - Table *pTab, /* the table into which we are inserting */ - int baseCur, /* Index of a read/write cursor pointing at pTab */ - int regRowid, /* Index of the range of input registers */ - int *aRegIdx, /* Register used by each index. 0 for unused indices */ - int rowidChng, /* True if the rowid might collide with existing entry */ - int isUpdate, /* True for UPDATE, False for INSERT */ - int overrideError, /* Override onError to this if not OE_Default */ - int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ - int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */ -){ - int i; /* loop counter */ - Vdbe *v; /* VDBE under constrution */ - int nCol; /* Number of columns */ - int onError; /* Conflict resolution strategy */ - int j1; /* Addresss of jump instruction */ - int j2 = 0, j3; /* Addresses of jump instructions */ - int regData; /* Register containing first data column */ - int iCur; /* Table cursor number */ - Index *pIdx; /* Pointer to one of the indices */ - sqlite3 *db; /* Database connection */ - int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ - int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid; - - db = pParse->db; - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); - assert( pTab->pSelect==0 ); /* This table is not a VIEW */ - nCol = pTab->nCol; - regData = regRowid + 1; - - /* Test all NOT NULL constraints. - */ - for(i=0; iiPKey ){ - continue; - } - onError = pTab->aCol[i].notNull; - if( onError==OE_None ) continue; - if( overrideError!=OE_Default ){ - onError = overrideError; - }else if( onError==OE_Default ){ - onError = OE_Abort; - } - if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){ - onError = OE_Abort; - } - assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail - || onError==OE_Ignore || onError==OE_Replace ); - switch( onError ){ - case OE_Abort: - sqlite3MayAbort(pParse); - case OE_Rollback: - case OE_Fail: { - char *zMsg; - sqlite3VdbeAddOp3(v, OP_HaltIfNull, - SQLITE_CONSTRAINT_NOTNULL, onError, regData+i); - zMsg = sqlite3MPrintf(db, "%s.%s may not be NULL", - pTab->zName, pTab->aCol[i].zName); - sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC); - break; - } - case OE_Ignore: { - sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest); - break; - } - default: { - assert( onError==OE_Replace ); - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i); - sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i); - sqlite3VdbeJumpHere(v, j1); - break; - } - } - } - - /* Test all CHECK constraints - */ -#ifndef SQLITE_OMIT_CHECK - if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ - ExprList *pCheck = pTab->pCheck; - pParse->ckBase = regData; - onError = overrideError!=OE_Default ? overrideError : OE_Abort; - for(i=0; inExpr; i++){ - int allOk = sqlite3VdbeMakeLabel(v); - sqlite3ExprIfTrue(pParse, pCheck->a[i].pExpr, allOk, SQLITE_JUMPIFNULL); - if( onError==OE_Ignore ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - }else{ - char *zConsName = pCheck->a[i].zName; - if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ - if( zConsName ){ - zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName); - }else{ - zConsName = 0; - } - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK, - onError, zConsName, P4_DYNAMIC); - } - sqlite3VdbeResolveLabel(v, allOk); - } - } -#endif /* !defined(SQLITE_OMIT_CHECK) */ - - /* If we have an INTEGER PRIMARY KEY, make sure the primary key - ** of the new record does not previously exist. Except, if this - ** is an UPDATE and the primary key is not changing, that is OK. - */ - if( rowidChng ){ - onError = pTab->keyConf; - if( overrideError!=OE_Default ){ - onError = overrideError; - }else if( onError==OE_Default ){ - onError = OE_Abort; - } - - if( isUpdate ){ - j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, rowidChng); - } - j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid); - switch( onError ){ - default: { - onError = OE_Abort; - /* Fall thru into the next case */ - } - case OE_Rollback: - case OE_Abort: - case OE_Fail: { - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY, - onError, "PRIMARY KEY must be unique", P4_STATIC); - break; - } - case OE_Replace: { - /* If there are DELETE triggers on this table and the - ** recursive-triggers flag is set, call GenerateRowDelete() to - ** remove the conflicting row from the table. This will fire - ** the triggers and remove both the table and index b-tree entries. - ** - ** Otherwise, if there are no triggers or the recursive-triggers - ** flag is not set, but the table has one or more indexes, call - ** GenerateRowIndexDelete(). This removes the index b-tree entries - ** only. The table b-tree entry will be replaced by the new entry - ** when it is inserted. - ** - ** If either GenerateRowDelete() or GenerateRowIndexDelete() is called, - ** also invoke MultiWrite() to indicate that this VDBE may require - ** statement rollback (if the statement is aborted after the delete - ** takes place). Earlier versions called sqlite3MultiWrite() regardless, - ** but being more selective here allows statements like: - ** - ** REPLACE INTO t(rowid) VALUES($newrowid) - ** - ** to run without a statement journal if there are no indexes on the - ** table. - */ - Trigger *pTrigger = 0; - if( db->flags&SQLITE_RecTriggers ){ - pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); - } - if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ - sqlite3MultiWrite(pParse); - sqlite3GenerateRowDelete( - pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace - ); - }else if( pTab->pIndex ){ - sqlite3MultiWrite(pParse); - sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0); - } - seenReplace = 1; - break; - } - case OE_Ignore: { - assert( seenReplace==0 ); - sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - break; - } - } - sqlite3VdbeJumpHere(v, j3); - if( isUpdate ){ - sqlite3VdbeJumpHere(v, j2); - } - } - - /* Test all UNIQUE constraints by creating entries for each UNIQUE - ** index and making sure that duplicate entries do not already exist. - ** Add the new records to the indices as we go. - */ - for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ - int regIdx; - int regR; - - if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */ - - /* Create a key for accessing the index entry */ - regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1); - for(i=0; inColumn; i++){ - int idx = pIdx->aiColumn[i]; - if( idx==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); - }else{ - sqlite3VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i); - } - } - sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT); - sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); - - /* Find out what action to take in case there is an indexing conflict */ - onError = pIdx->onError; - if( onError==OE_None ){ - sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); - continue; /* pIdx is not a UNIQUE index */ - } - if( overrideError!=OE_Default ){ - onError = overrideError; - }else if( onError==OE_Default ){ - onError = OE_Abort; - } - if( seenReplace ){ - if( onError==OE_Ignore ) onError = OE_Replace; - else if( onError==OE_Fail ) onError = OE_Abort; - } - - /* Check to see if the new index entry will be unique */ - regR = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_SCopy, regOldRowid, regR); - j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0, - regR, SQLITE_INT_TO_PTR(regIdx), - P4_INT32); - sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); - - /* Generate code that executes if the new index entry is not unique */ - assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail - || onError==OE_Ignore || onError==OE_Replace ); - switch( onError ){ - case OE_Rollback: - case OE_Abort: - case OE_Fail: { - int j; - StrAccum errMsg; - const char *zSep; - char *zErr; - - sqlite3StrAccumInit(&errMsg, 0, 0, 200); - errMsg.db = db; - zSep = pIdx->nColumn>1 ? "columns " : "column "; - for(j=0; jnColumn; j++){ - char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; - sqlite3StrAccumAppend(&errMsg, zSep, -1); - zSep = ", "; - sqlite3StrAccumAppend(&errMsg, zCol, -1); - } - sqlite3StrAccumAppend(&errMsg, - pIdx->nColumn>1 ? " are not unique" : " is not unique", -1); - zErr = sqlite3StrAccumFinish(&errMsg); - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE, - onError, zErr, 0); - sqlite3DbFree(errMsg.db, zErr); - break; - } - case OE_Ignore: { - assert( seenReplace==0 ); - sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - break; - } - default: { - Trigger *pTrigger = 0; - assert( onError==OE_Replace ); - sqlite3MultiWrite(pParse); - if( db->flags&SQLITE_RecTriggers ){ - pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); - } - sqlite3GenerateRowDelete( - pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace - ); - seenReplace = 1; - break; - } - } - sqlite3VdbeJumpHere(v, j3); - sqlite3ReleaseTempReg(pParse, regR); - } - - if( pbMayReplace ){ - *pbMayReplace = seenReplace; - } -} - -/* -** This routine generates code to finish the INSERT or UPDATE operation -** that was started by a prior call to sqlite3GenerateConstraintChecks. -** A consecutive range of registers starting at regRowid contains the -** rowid and the content to be inserted. -** -** The arguments to this routine should be the same as the first six -** arguments to sqlite3GenerateConstraintChecks. -*/ -SQLITE_PRIVATE void sqlite3CompleteInsertion( - Parse *pParse, /* The parser context */ - Table *pTab, /* the table into which we are inserting */ - int baseCur, /* Index of a read/write cursor pointing at pTab */ - int regRowid, /* Range of content */ - int *aRegIdx, /* Register used by each index. 0 for unused indices */ - int isUpdate, /* True for UPDATE, False for INSERT */ - int appendBias, /* True if this is likely to be an append */ - int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */ -){ - int i; - Vdbe *v; - int nIdx; - Index *pIdx; - u8 pik_flags; - int regData; - int regRec; - - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); - assert( pTab->pSelect==0 ); /* This table is not a VIEW */ - for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} - for(i=nIdx-1; i>=0; i--){ - if( aRegIdx[i]==0 ) continue; - sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]); - if( useSeekResult ){ - sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); - } - } - regData = regRowid + 1; - regRec = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec); - sqlite3TableAffinityStr(v, pTab); - sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol); - if( pParse->nested ){ - pik_flags = 0; - }else{ - pik_flags = OPFLAG_NCHANGE; - pik_flags |= (isUpdate?OPFLAG_ISUPDATE:OPFLAG_LASTROWID); - } - if( appendBias ){ - pik_flags |= OPFLAG_APPEND; - } - if( useSeekResult ){ - pik_flags |= OPFLAG_USESEEKRESULT; - } - sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid); - if( !pParse->nested ){ - sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); - } - sqlite3VdbeChangeP5(v, pik_flags); -} - -/* -** Generate code that will open cursors for a table and for all -** indices of that table. The "baseCur" parameter is the cursor number used -** for the table. Indices are opened on subsequent cursors. -** -** Return the number of indices on the table. -*/ -SQLITE_PRIVATE int sqlite3OpenTableAndIndices( - Parse *pParse, /* Parsing context */ - Table *pTab, /* Table to be opened */ - int baseCur, /* Cursor number assigned to the table */ - int op /* OP_OpenRead or OP_OpenWrite */ -){ - int i; - int iDb; - Index *pIdx; - Vdbe *v; - - if( IsVirtual(pTab) ) return 0; - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); - sqlite3OpenTable(pParse, baseCur, iDb, pTab, op); - for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); - assert( pIdx->pSchema==pTab->pSchema ); - sqlite3VdbeAddOp4(v, op, i+baseCur, pIdx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pIdx->zName)); - } - if( pParse->nTabnTab = baseCur+i; - } - return i-1; -} - - -#ifdef SQLITE_TEST -/* -** The following global variable is incremented whenever the -** transfer optimization is used. This is used for testing -** purposes only - to make sure the transfer optimization really -** is happening when it is suppose to. -*/ -SQLITE_API int sqlite3_xferopt_count; -#endif /* SQLITE_TEST */ - - -#ifndef SQLITE_OMIT_XFER_OPT -/* -** Check to collation names to see if they are compatible. -*/ -static int xferCompatibleCollation(const char *z1, const char *z2){ - if( z1==0 ){ - return z2==0; - } - if( z2==0 ){ - return 0; - } - return sqlite3StrICmp(z1, z2)==0; -} - - -/* -** Check to see if index pSrc is compatible as a source of data -** for index pDest in an insert transfer optimization. The rules -** for a compatible index: -** -** * The index is over the same set of columns -** * The same DESC and ASC markings occurs on all columns -** * The same onError processing (OE_Abort, OE_Ignore, etc) -** * The same collating sequence on each column -*/ -static int xferCompatibleIndex(Index *pDest, Index *pSrc){ - int i; - assert( pDest && pSrc ); - assert( pDest->pTable!=pSrc->pTable ); - if( pDest->nColumn!=pSrc->nColumn ){ - return 0; /* Different number of columns */ - } - if( pDest->onError!=pSrc->onError ){ - return 0; /* Different conflict resolution strategies */ - } - for(i=0; inColumn; i++){ - if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){ - return 0; /* Different columns indexed */ - } - if( pSrc->aSortOrder[i]!=pDest->aSortOrder[i] ){ - return 0; /* Different sort orders */ - } - if( !xferCompatibleCollation(pSrc->azColl[i],pDest->azColl[i]) ){ - return 0; /* Different collating sequences */ - } - } - - /* If no test above fails then the indices must be compatible */ - return 1; -} - -/* -** Attempt the transfer optimization on INSERTs of the form -** -** INSERT INTO tab1 SELECT * FROM tab2; -** -** The xfer optimization transfers raw records from tab2 over to tab1. -** Columns are not decoded and reassemblied, which greatly improves -** performance. Raw index records are transferred in the same way. -** -** The xfer optimization is only attempted if tab1 and tab2 are compatible. -** There are lots of rules for determining compatibility - see comments -** embedded in the code for details. -** -** This routine returns TRUE if the optimization is guaranteed to be used. -** Sometimes the xfer optimization will only work if the destination table -** is empty - a factor that can only be determined at run-time. In that -** case, this routine generates code for the xfer optimization but also -** does a test to see if the destination table is empty and jumps over the -** xfer optimization code if the test fails. In that case, this routine -** returns FALSE so that the caller will know to go ahead and generate -** an unoptimized transfer. This routine also returns FALSE if there -** is no chance that the xfer optimization can be applied. -** -** This optimization is particularly useful at making VACUUM run faster. -*/ -static int xferOptimization( - Parse *pParse, /* Parser context */ - Table *pDest, /* The table we are inserting into */ - Select *pSelect, /* A SELECT statement to use as the data source */ - int onError, /* How to handle constraint errors */ - int iDbDest /* The database of pDest */ -){ - ExprList *pEList; /* The result set of the SELECT */ - Table *pSrc; /* The table in the FROM clause of SELECT */ - Index *pSrcIdx, *pDestIdx; /* Source and destination indices */ - struct SrcList_item *pItem; /* An element of pSelect->pSrc */ - int i; /* Loop counter */ - int iDbSrc; /* The database of pSrc */ - int iSrc, iDest; /* Cursors from source and destination */ - int addr1, addr2; /* Loop addresses */ - int emptyDestTest; /* Address of test for empty pDest */ - int emptySrcTest; /* Address of test for empty pSrc */ - Vdbe *v; /* The VDBE we are building */ - KeyInfo *pKey; /* Key information for an index */ - int regAutoinc; /* Memory register used by AUTOINC */ - int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */ - int regData, regRowid; /* Registers holding data and rowid */ - - if( pSelect==0 ){ - return 0; /* Must be of the form INSERT INTO ... SELECT ... */ - } - if( sqlite3TriggerList(pParse, pDest) ){ - return 0; /* tab1 must not have triggers */ - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( pDest->tabFlags & TF_Virtual ){ - return 0; /* tab1 must not be a virtual table */ - } -#endif - if( onError==OE_Default ){ - if( pDest->iPKey>=0 ) onError = pDest->keyConf; - if( onError==OE_Default ) onError = OE_Abort; - } - assert(pSelect->pSrc); /* allocated even if there is no FROM clause */ - if( pSelect->pSrc->nSrc!=1 ){ - return 0; /* FROM clause must have exactly one term */ - } - if( pSelect->pSrc->a[0].pSelect ){ - return 0; /* FROM clause cannot contain a subquery */ - } - if( pSelect->pWhere ){ - return 0; /* SELECT may not have a WHERE clause */ - } - if( pSelect->pOrderBy ){ - return 0; /* SELECT may not have an ORDER BY clause */ - } - /* Do not need to test for a HAVING clause. If HAVING is present but - ** there is no ORDER BY, we will get an error. */ - if( pSelect->pGroupBy ){ - return 0; /* SELECT may not have a GROUP BY clause */ - } - if( pSelect->pLimit ){ - return 0; /* SELECT may not have a LIMIT clause */ - } - assert( pSelect->pOffset==0 ); /* Must be so if pLimit==0 */ - if( pSelect->pPrior ){ - return 0; /* SELECT may not be a compound query */ - } - if( pSelect->selFlags & SF_Distinct ){ - return 0; /* SELECT may not be DISTINCT */ - } - pEList = pSelect->pEList; - assert( pEList!=0 ); - if( pEList->nExpr!=1 ){ - return 0; /* The result set must have exactly one column */ - } - assert( pEList->a[0].pExpr ); - if( pEList->a[0].pExpr->op!=TK_ALL ){ - return 0; /* The result set must be the special operator "*" */ - } - - /* At this point we have established that the statement is of the - ** correct syntactic form to participate in this optimization. Now - ** we have to check the semantics. - */ - pItem = pSelect->pSrc->a; - pSrc = sqlite3LocateTableItem(pParse, 0, pItem); - if( pSrc==0 ){ - return 0; /* FROM clause does not contain a real table */ - } - if( pSrc==pDest ){ - return 0; /* tab1 and tab2 may not be the same table */ - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( pSrc->tabFlags & TF_Virtual ){ - return 0; /* tab2 must not be a virtual table */ - } -#endif - if( pSrc->pSelect ){ - return 0; /* tab2 may not be a view */ - } - if( pDest->nCol!=pSrc->nCol ){ - return 0; /* Number of columns must be the same in tab1 and tab2 */ - } - if( pDest->iPKey!=pSrc->iPKey ){ - return 0; /* Both tables must have the same INTEGER PRIMARY KEY */ - } - for(i=0; inCol; i++){ - if( pDest->aCol[i].affinity!=pSrc->aCol[i].affinity ){ - return 0; /* Affinity must be the same on all columns */ - } - if( !xferCompatibleCollation(pDest->aCol[i].zColl, pSrc->aCol[i].zColl) ){ - return 0; /* Collating sequence must be the same on all columns */ - } - if( pDest->aCol[i].notNull && !pSrc->aCol[i].notNull ){ - return 0; /* tab2 must be NOT NULL if tab1 is */ - } - } - for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ - if( pDestIdx->onError!=OE_None ){ - destHasUniqueIdx = 1; - } - for(pSrcIdx=pSrc->pIndex; pSrcIdx; pSrcIdx=pSrcIdx->pNext){ - if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; - } - if( pSrcIdx==0 ){ - return 0; /* pDestIdx has no corresponding index in pSrc */ - } - } -#ifndef SQLITE_OMIT_CHECK - if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck, pDest->pCheck) ){ - return 0; /* Tables have different CHECK constraints. Ticket #2252 */ - } -#endif -#ifndef SQLITE_OMIT_FOREIGN_KEY - /* Disallow the transfer optimization if the destination table constains - ** any foreign key constraints. This is more restrictive than necessary. - ** But the main beneficiary of the transfer optimization is the VACUUM - ** command, and the VACUUM command disables foreign key constraints. So - ** the extra complication to make this rule less restrictive is probably - ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] - */ - if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ - return 0; - } -#endif - if( (pParse->db->flags & SQLITE_CountRows)!=0 ){ - return 0; /* xfer opt does not play well with PRAGMA count_changes */ - } - - /* If we get this far, it means that the xfer optimization is at - ** least a possibility, though it might only work if the destination - ** table (tab1) is initially empty. - */ -#ifdef SQLITE_TEST - sqlite3_xferopt_count++; -#endif - iDbSrc = sqlite3SchemaToIndex(pParse->db, pSrc->pSchema); - v = sqlite3GetVdbe(pParse); - sqlite3CodeVerifySchema(pParse, iDbSrc); - iSrc = pParse->nTab++; - iDest = pParse->nTab++; - regAutoinc = autoIncBegin(pParse, iDbDest, pDest); - sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); - if( (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */ - || destHasUniqueIdx /* (2) */ - || (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */ - ){ - /* In some circumstances, we are able to run the xfer optimization - ** only if the destination table is initially empty. This code makes - ** that determination. Conditions under which the destination must - ** be empty: - ** - ** (1) There is no INTEGER PRIMARY KEY but there are indices. - ** (If the destination is not initially empty, the rowid fields - ** of index entries might need to change.) - ** - ** (2) The destination has a unique index. (The xfer optimization - ** is unable to test uniqueness.) - ** - ** (3) onError is something other than OE_Abort and OE_Rollback. - */ - addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0); - emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); - sqlite3VdbeJumpHere(v, addr1); - }else{ - emptyDestTest = 0; - } - sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead); - emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); - regData = sqlite3GetTempReg(pParse); - regRowid = sqlite3GetTempReg(pParse); - if( pDest->iPKey>=0 ){ - addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); - addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY, - onError, "PRIMARY KEY must be unique", P4_STATIC); - sqlite3VdbeJumpHere(v, addr2); - autoIncStep(pParse, regAutoinc, regRowid); - }else if( pDest->pIndex==0 ){ - addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); - }else{ - addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); - assert( (pDest->tabFlags & TF_Autoincrement)==0 ); - } - sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData); - sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); - sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND); - sqlite3VdbeChangeP4(v, -1, pDest->zName, 0); - sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); - for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ - for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ - if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; - } - assert( pSrcIdx ); - sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); - sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); - pKey = sqlite3IndexKeyinfo(pParse, pSrcIdx); - sqlite3VdbeAddOp4(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc, - (char*)pKey, P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pSrcIdx->zName)); - pKey = sqlite3IndexKeyinfo(pParse, pDestIdx); - sqlite3VdbeAddOp4(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest, - (char*)pKey, P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pDestIdx->zName)); - addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); - sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData); - sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1); - sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); - sqlite3VdbeJumpHere(v, addr1); - } - sqlite3VdbeJumpHere(v, emptySrcTest); - sqlite3ReleaseTempReg(pParse, regRowid); - sqlite3ReleaseTempReg(pParse, regData); - sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); - sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); - if( emptyDestTest ){ - sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, 0); - sqlite3VdbeJumpHere(v, emptyDestTest); - sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); - return 0; - }else{ - return 1; - } -} -#endif /* SQLITE_OMIT_XFER_OPT */ - -/************** End of insert.c **********************************************/ -/************** Begin file legacy.c ******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Main file for the SQLite library. The routines in this file -** implement the programmer interface to the library. Routines in -** other files are for internal use by SQLite and should not be -** accessed by users of the library. -*/ - - -/* -** Execute SQL code. Return one of the SQLITE_ success/failure -** codes. Also write an error message into memory obtained from -** malloc() and make *pzErrMsg point to that message. -** -** If the SQL is a query, then for each row in the query result -** the xCallback() function is called. pArg becomes the first -** argument to xCallback(). If xCallback=NULL then no callback -** is invoked, even for queries. -*/ -SQLITE_API int sqlite3_exec( - sqlite3 *db, /* The database on which the SQL executes */ - const char *zSql, /* The SQL to be executed */ - sqlite3_callback xCallback, /* Invoke this callback routine */ - void *pArg, /* First argument to xCallback() */ - char **pzErrMsg /* Write error messages here */ -){ - int rc = SQLITE_OK; /* Return code */ - const char *zLeftover; /* Tail of unprocessed SQL */ - sqlite3_stmt *pStmt = 0; /* The current SQL statement */ - char **azCols = 0; /* Names of result columns */ - int nRetry = 0; /* Number of retry attempts */ - int callbackIsInit; /* True if callback data is initialized */ - - if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; - if( zSql==0 ) zSql = ""; - - sqlite3_mutex_enter(db->mutex); - sqlite3Error(db, SQLITE_OK, 0); - while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){ - int nCol; - char **azVals = 0; - - pStmt = 0; - rc = sqlite3_prepare(db, zSql, -1, &pStmt, &zLeftover); - assert( rc==SQLITE_OK || pStmt==0 ); - if( rc!=SQLITE_OK ){ - continue; - } - if( !pStmt ){ - /* this happens for a comment or white-space */ - zSql = zLeftover; - continue; - } - - callbackIsInit = 0; - nCol = sqlite3_column_count(pStmt); - - while( 1 ){ - int i; - rc = sqlite3_step(pStmt); - - /* Invoke the callback function if required */ - if( xCallback && (SQLITE_ROW==rc || - (SQLITE_DONE==rc && !callbackIsInit - && db->flags&SQLITE_NullCallback)) ){ - if( !callbackIsInit ){ - azCols = sqlite3DbMallocZero(db, 2*nCol*sizeof(const char*) + 1); - if( azCols==0 ){ - goto exec_out; - } - for(i=0; imallocFailed = 1; - goto exec_out; - } - } - } - if( xCallback(pArg, nCol, azVals, azCols) ){ - rc = SQLITE_ABORT; - sqlite3VdbeFinalize((Vdbe *)pStmt); - pStmt = 0; - sqlite3Error(db, SQLITE_ABORT, 0); - goto exec_out; - } - } - - if( rc!=SQLITE_ROW ){ - rc = sqlite3VdbeFinalize((Vdbe *)pStmt); - pStmt = 0; - if( rc!=SQLITE_SCHEMA ){ - nRetry = 0; - zSql = zLeftover; - while( sqlite3Isspace(zSql[0]) ) zSql++; - } - break; - } - } - - sqlite3DbFree(db, azCols); - azCols = 0; - } - -exec_out: - if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt); - sqlite3DbFree(db, azCols); - - rc = sqlite3ApiExit(db, rc); - if( rc!=SQLITE_OK && ALWAYS(rc==sqlite3_errcode(db)) && pzErrMsg ){ - int nErrMsg = 1 + sqlite3Strlen30(sqlite3_errmsg(db)); - *pzErrMsg = sqlite3Malloc(nErrMsg); - if( *pzErrMsg ){ - memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg); - }else{ - rc = SQLITE_NOMEM; - sqlite3Error(db, SQLITE_NOMEM, 0); - } - }else if( pzErrMsg ){ - *pzErrMsg = 0; - } - - assert( (rc&db->errMask)==rc ); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/************** End of legacy.c **********************************************/ -/************** Begin file loadext.c *****************************************/ -/* -** 2006 June 7 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to dynamically load extensions into -** the SQLite library. -*/ - -#ifndef SQLITE_CORE - #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */ -#endif -/************** Include sqlite3ext.h in the middle of loadext.c **************/ -/************** Begin file sqlite3ext.h **************************************/ -/* -** 2006 June 7 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the SQLite interface for use by -** shared libraries that want to be imported as extensions into -** an SQLite instance. Shared libraries that intend to be loaded -** as extensions by SQLite should #include this file instead of -** sqlite3.h. -*/ -#ifndef _SQLITE3EXT_H_ -#define _SQLITE3EXT_H_ - -typedef struct sqlite3_api_routines sqlite3_api_routines; - -/* -** The following structure holds pointers to all of the SQLite API -** routines. -** -** WARNING: In order to maintain backwards compatibility, add new -** interfaces to the end of this structure only. If you insert new -** interfaces in the middle of this structure, then older different -** versions of SQLite will not be able to load each others' shared -** libraries! -*/ -struct sqlite3_api_routines { - void * (*aggregate_context)(sqlite3_context*,int nBytes); - int (*aggregate_count)(sqlite3_context*); - int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*)); - int (*bind_double)(sqlite3_stmt*,int,double); - int (*bind_int)(sqlite3_stmt*,int,int); - int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64); - int (*bind_null)(sqlite3_stmt*,int); - int (*bind_parameter_count)(sqlite3_stmt*); - int (*bind_parameter_index)(sqlite3_stmt*,const char*zName); - const char * (*bind_parameter_name)(sqlite3_stmt*,int); - int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*)); - int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*)); - int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); - int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); - int (*busy_timeout)(sqlite3*,int ms); - int (*changes)(sqlite3*); - int (*close)(sqlite3*); - int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, - int eTextRep,const char*)); - int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, - int eTextRep,const void*)); - const void * (*column_blob)(sqlite3_stmt*,int iCol); - int (*column_bytes)(sqlite3_stmt*,int iCol); - int (*column_bytes16)(sqlite3_stmt*,int iCol); - int (*column_count)(sqlite3_stmt*pStmt); - const char * (*column_database_name)(sqlite3_stmt*,int); - const void * (*column_database_name16)(sqlite3_stmt*,int); - const char * (*column_decltype)(sqlite3_stmt*,int i); - const void * (*column_decltype16)(sqlite3_stmt*,int); - double (*column_double)(sqlite3_stmt*,int iCol); - int (*column_int)(sqlite3_stmt*,int iCol); - sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol); - const char * (*column_name)(sqlite3_stmt*,int); - const void * (*column_name16)(sqlite3_stmt*,int); - const char * (*column_origin_name)(sqlite3_stmt*,int); - const void * (*column_origin_name16)(sqlite3_stmt*,int); - const char * (*column_table_name)(sqlite3_stmt*,int); - const void * (*column_table_name16)(sqlite3_stmt*,int); - const unsigned char * (*column_text)(sqlite3_stmt*,int iCol); - const void * (*column_text16)(sqlite3_stmt*,int iCol); - int (*column_type)(sqlite3_stmt*,int iCol); - sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); - void * (*commit_hook)(sqlite3*,int(*)(void*),void*); - int (*complete)(const char*sql); - int (*complete16)(const void*sql); - int (*create_collation)(sqlite3*,const char*,int,void*, - int(*)(void*,int,const void*,int,const void*)); - int (*create_collation16)(sqlite3*,const void*,int,void*, - int(*)(void*,int,const void*,int,const void*)); - int (*create_function)(sqlite3*,const char*,int,int,void*, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*)); - int (*create_function16)(sqlite3*,const void*,int,int,void*, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*)); - int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); - int (*data_count)(sqlite3_stmt*pStmt); - sqlite3 * (*db_handle)(sqlite3_stmt*); - int (*declare_vtab)(sqlite3*,const char*); - int (*enable_shared_cache)(int); - int (*errcode)(sqlite3*db); - const char * (*errmsg)(sqlite3*); - const void * (*errmsg16)(sqlite3*); - int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**); - int (*expired)(sqlite3_stmt*); - int (*finalize)(sqlite3_stmt*pStmt); - void (*free)(void*); - void (*free_table)(char**result); - int (*get_autocommit)(sqlite3*); - void * (*get_auxdata)(sqlite3_context*,int); - int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**); - int (*global_recover)(void); - void (*interruptx)(sqlite3*); - sqlite_int64 (*last_insert_rowid)(sqlite3*); - const char * (*libversion)(void); - int (*libversion_number)(void); - void *(*malloc)(int); - char * (*mprintf)(const char*,...); - int (*open)(const char*,sqlite3**); - int (*open16)(const void*,sqlite3**); - int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); - int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); - void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*); - void (*progress_handler)(sqlite3*,int,int(*)(void*),void*); - void *(*realloc)(void*,int); - int (*reset)(sqlite3_stmt*pStmt); - void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*)); - void (*result_double)(sqlite3_context*,double); - void (*result_error)(sqlite3_context*,const char*,int); - void (*result_error16)(sqlite3_context*,const void*,int); - void (*result_int)(sqlite3_context*,int); - void (*result_int64)(sqlite3_context*,sqlite_int64); - void (*result_null)(sqlite3_context*); - void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*)); - void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); - void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); - void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); - void (*result_value)(sqlite3_context*,sqlite3_value*); - void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); - int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, - const char*,const char*),void*); - void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); - char * (*snprintf)(int,char*,const char*,...); - int (*step)(sqlite3_stmt*); - int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, - char const**,char const**,int*,int*,int*); - void (*thread_cleanup)(void); - int (*total_changes)(sqlite3*); - void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); - int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); - void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, - sqlite_int64),void*); - void * (*user_data)(sqlite3_context*); - const void * (*value_blob)(sqlite3_value*); - int (*value_bytes)(sqlite3_value*); - int (*value_bytes16)(sqlite3_value*); - double (*value_double)(sqlite3_value*); - int (*value_int)(sqlite3_value*); - sqlite_int64 (*value_int64)(sqlite3_value*); - int (*value_numeric_type)(sqlite3_value*); - const unsigned char * (*value_text)(sqlite3_value*); - const void * (*value_text16)(sqlite3_value*); - const void * (*value_text16be)(sqlite3_value*); - const void * (*value_text16le)(sqlite3_value*); - int (*value_type)(sqlite3_value*); - char *(*vmprintf)(const char*,va_list); - /* Added ??? */ - int (*overload_function)(sqlite3*, const char *zFuncName, int nArg); - /* Added by 3.3.13 */ - int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); - int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); - int (*clear_bindings)(sqlite3_stmt*); - /* Added by 3.4.1 */ - int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, - void (*xDestroy)(void *)); - /* Added by 3.5.0 */ - int (*bind_zeroblob)(sqlite3_stmt*,int,int); - int (*blob_bytes)(sqlite3_blob*); - int (*blob_close)(sqlite3_blob*); - int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, - int,sqlite3_blob**); - int (*blob_read)(sqlite3_blob*,void*,int,int); - int (*blob_write)(sqlite3_blob*,const void*,int,int); - int (*create_collation_v2)(sqlite3*,const char*,int,void*, - int(*)(void*,int,const void*,int,const void*), - void(*)(void*)); - int (*file_control)(sqlite3*,const char*,int,void*); - sqlite3_int64 (*memory_highwater)(int); - sqlite3_int64 (*memory_used)(void); - sqlite3_mutex *(*mutex_alloc)(int); - void (*mutex_enter)(sqlite3_mutex*); - void (*mutex_free)(sqlite3_mutex*); - void (*mutex_leave)(sqlite3_mutex*); - int (*mutex_try)(sqlite3_mutex*); - int (*open_v2)(const char*,sqlite3**,int,const char*); - int (*release_memory)(int); - void (*result_error_nomem)(sqlite3_context*); - void (*result_error_toobig)(sqlite3_context*); - int (*sleep)(int); - void (*soft_heap_limit)(int); - sqlite3_vfs *(*vfs_find)(const char*); - int (*vfs_register)(sqlite3_vfs*,int); - int (*vfs_unregister)(sqlite3_vfs*); - int (*xthreadsafe)(void); - void (*result_zeroblob)(sqlite3_context*,int); - void (*result_error_code)(sqlite3_context*,int); - int (*test_control)(int, ...); - void (*randomness)(int,void*); - sqlite3 *(*context_db_handle)(sqlite3_context*); - int (*extended_result_codes)(sqlite3*,int); - int (*limit)(sqlite3*,int,int); - sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); - const char *(*sql)(sqlite3_stmt*); - int (*status)(int,int*,int*,int); - int (*backup_finish)(sqlite3_backup*); - sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*); - int (*backup_pagecount)(sqlite3_backup*); - int (*backup_remaining)(sqlite3_backup*); - int (*backup_step)(sqlite3_backup*,int); - const char *(*compileoption_get)(int); - int (*compileoption_used)(const char*); - int (*create_function_v2)(sqlite3*,const char*,int,int,void*, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*), - void(*xDestroy)(void*)); - int (*db_config)(sqlite3*,int,...); - sqlite3_mutex *(*db_mutex)(sqlite3*); - int (*db_status)(sqlite3*,int,int*,int*,int); - int (*extended_errcode)(sqlite3*); - void (*log)(int,const char*,...); - sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64); - const char *(*sourceid)(void); - int (*stmt_status)(sqlite3_stmt*,int,int); - int (*strnicmp)(const char*,const char*,int); - int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*); - int (*wal_autocheckpoint)(sqlite3*,int); - int (*wal_checkpoint)(sqlite3*,const char*); - void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); - int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); - int (*vtab_config)(sqlite3*,int op,...); - int (*vtab_on_conflict)(sqlite3*); - /* Version 3.7.16 and later */ - int (*close_v2)(sqlite3*); - const char *(*db_filename)(sqlite3*,const char*); - int (*db_readonly)(sqlite3*,const char*); - int (*db_release_memory)(sqlite3*); - const char *(*errstr)(int); - int (*stmt_busy)(sqlite3_stmt*); - int (*stmt_readonly)(sqlite3_stmt*); - int (*stricmp)(const char*,const char*); - int (*uri_boolean)(const char*,const char*,int); - sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); - const char *(*uri_parameter)(const char*,const char*); - char *(*vsnprintf)(int,char*,const char*,va_list); - int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); -}; - -/* -** The following macros redefine the API routines so that they are -** redirected throught the global sqlite3_api structure. -** -** This header file is also used by the loadext.c source file -** (part of the main SQLite library - not an extension) so that -** it can get access to the sqlite3_api_routines structure -** definition. But the main library does not want to redefine -** the API. So the redefinition macros are only valid if the -** SQLITE_CORE macros is undefined. -*/ -#ifndef SQLITE_CORE -#define sqlite3_aggregate_context sqlite3_api->aggregate_context -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_aggregate_count sqlite3_api->aggregate_count -#endif -#define sqlite3_bind_blob sqlite3_api->bind_blob -#define sqlite3_bind_double sqlite3_api->bind_double -#define sqlite3_bind_int sqlite3_api->bind_int -#define sqlite3_bind_int64 sqlite3_api->bind_int64 -#define sqlite3_bind_null sqlite3_api->bind_null -#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count -#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index -#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name -#define sqlite3_bind_text sqlite3_api->bind_text -#define sqlite3_bind_text16 sqlite3_api->bind_text16 -#define sqlite3_bind_value sqlite3_api->bind_value -#define sqlite3_busy_handler sqlite3_api->busy_handler -#define sqlite3_busy_timeout sqlite3_api->busy_timeout -#define sqlite3_changes sqlite3_api->changes -#define sqlite3_close sqlite3_api->close -#define sqlite3_collation_needed sqlite3_api->collation_needed -#define sqlite3_collation_needed16 sqlite3_api->collation_needed16 -#define sqlite3_column_blob sqlite3_api->column_blob -#define sqlite3_column_bytes sqlite3_api->column_bytes -#define sqlite3_column_bytes16 sqlite3_api->column_bytes16 -#define sqlite3_column_count sqlite3_api->column_count -#define sqlite3_column_database_name sqlite3_api->column_database_name -#define sqlite3_column_database_name16 sqlite3_api->column_database_name16 -#define sqlite3_column_decltype sqlite3_api->column_decltype -#define sqlite3_column_decltype16 sqlite3_api->column_decltype16 -#define sqlite3_column_double sqlite3_api->column_double -#define sqlite3_column_int sqlite3_api->column_int -#define sqlite3_column_int64 sqlite3_api->column_int64 -#define sqlite3_column_name sqlite3_api->column_name -#define sqlite3_column_name16 sqlite3_api->column_name16 -#define sqlite3_column_origin_name sqlite3_api->column_origin_name -#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 -#define sqlite3_column_table_name sqlite3_api->column_table_name -#define sqlite3_column_table_name16 sqlite3_api->column_table_name16 -#define sqlite3_column_text sqlite3_api->column_text -#define sqlite3_column_text16 sqlite3_api->column_text16 -#define sqlite3_column_type sqlite3_api->column_type -#define sqlite3_column_value sqlite3_api->column_value -#define sqlite3_commit_hook sqlite3_api->commit_hook -#define sqlite3_complete sqlite3_api->complete -#define sqlite3_complete16 sqlite3_api->complete16 -#define sqlite3_create_collation sqlite3_api->create_collation -#define sqlite3_create_collation16 sqlite3_api->create_collation16 -#define sqlite3_create_function sqlite3_api->create_function -#define sqlite3_create_function16 sqlite3_api->create_function16 -#define sqlite3_create_module sqlite3_api->create_module -#define sqlite3_create_module_v2 sqlite3_api->create_module_v2 -#define sqlite3_data_count sqlite3_api->data_count -#define sqlite3_db_handle sqlite3_api->db_handle -#define sqlite3_declare_vtab sqlite3_api->declare_vtab -#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache -#define sqlite3_errcode sqlite3_api->errcode -#define sqlite3_errmsg sqlite3_api->errmsg -#define sqlite3_errmsg16 sqlite3_api->errmsg16 -#define sqlite3_exec sqlite3_api->exec -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_expired sqlite3_api->expired -#endif -#define sqlite3_finalize sqlite3_api->finalize -#define sqlite3_free sqlite3_api->free -#define sqlite3_free_table sqlite3_api->free_table -#define sqlite3_get_autocommit sqlite3_api->get_autocommit -#define sqlite3_get_auxdata sqlite3_api->get_auxdata -#define sqlite3_get_table sqlite3_api->get_table -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_global_recover sqlite3_api->global_recover -#endif -#define sqlite3_interrupt sqlite3_api->interruptx -#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid -#define sqlite3_libversion sqlite3_api->libversion -#define sqlite3_libversion_number sqlite3_api->libversion_number -#define sqlite3_malloc sqlite3_api->malloc -#define sqlite3_mprintf sqlite3_api->mprintf -#define sqlite3_open sqlite3_api->open -#define sqlite3_open16 sqlite3_api->open16 -#define sqlite3_prepare sqlite3_api->prepare -#define sqlite3_prepare16 sqlite3_api->prepare16 -#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 -#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 -#define sqlite3_profile sqlite3_api->profile -#define sqlite3_progress_handler sqlite3_api->progress_handler -#define sqlite3_realloc sqlite3_api->realloc -#define sqlite3_reset sqlite3_api->reset -#define sqlite3_result_blob sqlite3_api->result_blob -#define sqlite3_result_double sqlite3_api->result_double -#define sqlite3_result_error sqlite3_api->result_error -#define sqlite3_result_error16 sqlite3_api->result_error16 -#define sqlite3_result_int sqlite3_api->result_int -#define sqlite3_result_int64 sqlite3_api->result_int64 -#define sqlite3_result_null sqlite3_api->result_null -#define sqlite3_result_text sqlite3_api->result_text -#define sqlite3_result_text16 sqlite3_api->result_text16 -#define sqlite3_result_text16be sqlite3_api->result_text16be -#define sqlite3_result_text16le sqlite3_api->result_text16le -#define sqlite3_result_value sqlite3_api->result_value -#define sqlite3_rollback_hook sqlite3_api->rollback_hook -#define sqlite3_set_authorizer sqlite3_api->set_authorizer -#define sqlite3_set_auxdata sqlite3_api->set_auxdata -#define sqlite3_snprintf sqlite3_api->snprintf -#define sqlite3_step sqlite3_api->step -#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata -#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup -#define sqlite3_total_changes sqlite3_api->total_changes -#define sqlite3_trace sqlite3_api->trace -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings -#endif -#define sqlite3_update_hook sqlite3_api->update_hook -#define sqlite3_user_data sqlite3_api->user_data -#define sqlite3_value_blob sqlite3_api->value_blob -#define sqlite3_value_bytes sqlite3_api->value_bytes -#define sqlite3_value_bytes16 sqlite3_api->value_bytes16 -#define sqlite3_value_double sqlite3_api->value_double -#define sqlite3_value_int sqlite3_api->value_int -#define sqlite3_value_int64 sqlite3_api->value_int64 -#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type -#define sqlite3_value_text sqlite3_api->value_text -#define sqlite3_value_text16 sqlite3_api->value_text16 -#define sqlite3_value_text16be sqlite3_api->value_text16be -#define sqlite3_value_text16le sqlite3_api->value_text16le -#define sqlite3_value_type sqlite3_api->value_type -#define sqlite3_vmprintf sqlite3_api->vmprintf -#define sqlite3_overload_function sqlite3_api->overload_function -#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 -#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 -#define sqlite3_clear_bindings sqlite3_api->clear_bindings -#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob -#define sqlite3_blob_bytes sqlite3_api->blob_bytes -#define sqlite3_blob_close sqlite3_api->blob_close -#define sqlite3_blob_open sqlite3_api->blob_open -#define sqlite3_blob_read sqlite3_api->blob_read -#define sqlite3_blob_write sqlite3_api->blob_write -#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 -#define sqlite3_file_control sqlite3_api->file_control -#define sqlite3_memory_highwater sqlite3_api->memory_highwater -#define sqlite3_memory_used sqlite3_api->memory_used -#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc -#define sqlite3_mutex_enter sqlite3_api->mutex_enter -#define sqlite3_mutex_free sqlite3_api->mutex_free -#define sqlite3_mutex_leave sqlite3_api->mutex_leave -#define sqlite3_mutex_try sqlite3_api->mutex_try -#define sqlite3_open_v2 sqlite3_api->open_v2 -#define sqlite3_release_memory sqlite3_api->release_memory -#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem -#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig -#define sqlite3_sleep sqlite3_api->sleep -#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit -#define sqlite3_vfs_find sqlite3_api->vfs_find -#define sqlite3_vfs_register sqlite3_api->vfs_register -#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister -#define sqlite3_threadsafe sqlite3_api->xthreadsafe -#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob -#define sqlite3_result_error_code sqlite3_api->result_error_code -#define sqlite3_test_control sqlite3_api->test_control -#define sqlite3_randomness sqlite3_api->randomness -#define sqlite3_context_db_handle sqlite3_api->context_db_handle -#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes -#define sqlite3_limit sqlite3_api->limit -#define sqlite3_next_stmt sqlite3_api->next_stmt -#define sqlite3_sql sqlite3_api->sql -#define sqlite3_status sqlite3_api->status -#define sqlite3_backup_finish sqlite3_api->backup_finish -#define sqlite3_backup_init sqlite3_api->backup_init -#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount -#define sqlite3_backup_remaining sqlite3_api->backup_remaining -#define sqlite3_backup_step sqlite3_api->backup_step -#define sqlite3_compileoption_get sqlite3_api->compileoption_get -#define sqlite3_compileoption_used sqlite3_api->compileoption_used -#define sqlite3_create_function_v2 sqlite3_api->create_function_v2 -#define sqlite3_db_config sqlite3_api->db_config -#define sqlite3_db_mutex sqlite3_api->db_mutex -#define sqlite3_db_status sqlite3_api->db_status -#define sqlite3_extended_errcode sqlite3_api->extended_errcode -#define sqlite3_log sqlite3_api->log -#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 -#define sqlite3_sourceid sqlite3_api->sourceid -#define sqlite3_stmt_status sqlite3_api->stmt_status -#define sqlite3_strnicmp sqlite3_api->strnicmp -#define sqlite3_unlock_notify sqlite3_api->unlock_notify -#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint -#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint -#define sqlite3_wal_hook sqlite3_api->wal_hook -#define sqlite3_blob_reopen sqlite3_api->blob_reopen -#define sqlite3_vtab_config sqlite3_api->vtab_config -#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict -/* Version 3.7.16 and later */ -#define sqlite3_close_v2 sqlite3_api->close_v2 -#define sqlite3_db_filename sqlite3_api->db_filename -#define sqlite3_db_readonly sqlite3_api->db_readonly -#define sqlite3_db_release_memory sqlite3_api->db_release_memory -#define sqlite3_errstr sqlite3_api->errstr -#define sqlite3_stmt_busy sqlite3_api->stmt_busy -#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly -#define sqlite3_stricmp sqlite3_api->stricmp -#define sqlite3_uri_boolean sqlite3_api->uri_boolean -#define sqlite3_uri_int64 sqlite3_api->uri_int64 -#define sqlite3_uri_parameter sqlite3_api->uri_parameter -#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf -#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 -#endif /* SQLITE_CORE */ - -#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; -#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v; - -#endif /* _SQLITE3EXT_H_ */ - -/************** End of sqlite3ext.h ******************************************/ -/************** Continuing where we left off in loadext.c ********************/ -/* #include */ - -#ifndef SQLITE_OMIT_LOAD_EXTENSION - -/* -** Some API routines are omitted when various features are -** excluded from a build of SQLite. Substitute a NULL pointer -** for any missing APIs. -*/ -#ifndef SQLITE_ENABLE_COLUMN_METADATA -# define sqlite3_column_database_name 0 -# define sqlite3_column_database_name16 0 -# define sqlite3_column_table_name 0 -# define sqlite3_column_table_name16 0 -# define sqlite3_column_origin_name 0 -# define sqlite3_column_origin_name16 0 -# define sqlite3_table_column_metadata 0 -#endif - -#ifdef SQLITE_OMIT_AUTHORIZATION -# define sqlite3_set_authorizer 0 -#endif - -#ifdef SQLITE_OMIT_UTF16 -# define sqlite3_bind_text16 0 -# define sqlite3_collation_needed16 0 -# define sqlite3_column_decltype16 0 -# define sqlite3_column_name16 0 -# define sqlite3_column_text16 0 -# define sqlite3_complete16 0 -# define sqlite3_create_collation16 0 -# define sqlite3_create_function16 0 -# define sqlite3_errmsg16 0 -# define sqlite3_open16 0 -# define sqlite3_prepare16 0 -# define sqlite3_prepare16_v2 0 -# define sqlite3_result_error16 0 -# define sqlite3_result_text16 0 -# define sqlite3_result_text16be 0 -# define sqlite3_result_text16le 0 -# define sqlite3_value_text16 0 -# define sqlite3_value_text16be 0 -# define sqlite3_value_text16le 0 -# define sqlite3_column_database_name16 0 -# define sqlite3_column_table_name16 0 -# define sqlite3_column_origin_name16 0 -#endif - -#ifdef SQLITE_OMIT_COMPLETE -# define sqlite3_complete 0 -# define sqlite3_complete16 0 -#endif - -#ifdef SQLITE_OMIT_DECLTYPE -# define sqlite3_column_decltype16 0 -# define sqlite3_column_decltype 0 -#endif - -#ifdef SQLITE_OMIT_PROGRESS_CALLBACK -# define sqlite3_progress_handler 0 -#endif - -#ifdef SQLITE_OMIT_VIRTUALTABLE -# define sqlite3_create_module 0 -# define sqlite3_create_module_v2 0 -# define sqlite3_declare_vtab 0 -# define sqlite3_vtab_config 0 -# define sqlite3_vtab_on_conflict 0 -#endif - -#ifdef SQLITE_OMIT_SHARED_CACHE -# define sqlite3_enable_shared_cache 0 -#endif - -#ifdef SQLITE_OMIT_TRACE -# define sqlite3_profile 0 -# define sqlite3_trace 0 -#endif - -#ifdef SQLITE_OMIT_GET_TABLE -# define sqlite3_free_table 0 -# define sqlite3_get_table 0 -#endif - -#ifdef SQLITE_OMIT_INCRBLOB -#define sqlite3_bind_zeroblob 0 -#define sqlite3_blob_bytes 0 -#define sqlite3_blob_close 0 -#define sqlite3_blob_open 0 -#define sqlite3_blob_read 0 -#define sqlite3_blob_write 0 -#define sqlite3_blob_reopen 0 -#endif - -/* -** The following structure contains pointers to all SQLite API routines. -** A pointer to this structure is passed into extensions when they are -** loaded so that the extension can make calls back into the SQLite -** library. -** -** When adding new APIs, add them to the bottom of this structure -** in order to preserve backwards compatibility. -** -** Extensions that use newer APIs should first call the -** sqlite3_libversion_number() to make sure that the API they -** intend to use is supported by the library. Extensions should -** also check to make sure that the pointer to the function is -** not NULL before calling it. -*/ -static const sqlite3_api_routines sqlite3Apis = { - sqlite3_aggregate_context, -#ifndef SQLITE_OMIT_DEPRECATED - sqlite3_aggregate_count, -#else - 0, -#endif - sqlite3_bind_blob, - sqlite3_bind_double, - sqlite3_bind_int, - sqlite3_bind_int64, - sqlite3_bind_null, - sqlite3_bind_parameter_count, - sqlite3_bind_parameter_index, - sqlite3_bind_parameter_name, - sqlite3_bind_text, - sqlite3_bind_text16, - sqlite3_bind_value, - sqlite3_busy_handler, - sqlite3_busy_timeout, - sqlite3_changes, - sqlite3_close, - sqlite3_collation_needed, - sqlite3_collation_needed16, - sqlite3_column_blob, - sqlite3_column_bytes, - sqlite3_column_bytes16, - sqlite3_column_count, - sqlite3_column_database_name, - sqlite3_column_database_name16, - sqlite3_column_decltype, - sqlite3_column_decltype16, - sqlite3_column_double, - sqlite3_column_int, - sqlite3_column_int64, - sqlite3_column_name, - sqlite3_column_name16, - sqlite3_column_origin_name, - sqlite3_column_origin_name16, - sqlite3_column_table_name, - sqlite3_column_table_name16, - sqlite3_column_text, - sqlite3_column_text16, - sqlite3_column_type, - sqlite3_column_value, - sqlite3_commit_hook, - sqlite3_complete, - sqlite3_complete16, - sqlite3_create_collation, - sqlite3_create_collation16, - sqlite3_create_function, - sqlite3_create_function16, - sqlite3_create_module, - sqlite3_data_count, - sqlite3_db_handle, - sqlite3_declare_vtab, - sqlite3_enable_shared_cache, - sqlite3_errcode, - sqlite3_errmsg, - sqlite3_errmsg16, - sqlite3_exec, -#ifndef SQLITE_OMIT_DEPRECATED - sqlite3_expired, -#else - 0, -#endif - sqlite3_finalize, - sqlite3_free, - sqlite3_free_table, - sqlite3_get_autocommit, - sqlite3_get_auxdata, - sqlite3_get_table, - 0, /* Was sqlite3_global_recover(), but that function is deprecated */ - sqlite3_interrupt, - sqlite3_last_insert_rowid, - sqlite3_libversion, - sqlite3_libversion_number, - sqlite3_malloc, - sqlite3_mprintf, - sqlite3_open, - sqlite3_open16, - sqlite3_prepare, - sqlite3_prepare16, - sqlite3_profile, - sqlite3_progress_handler, - sqlite3_realloc, - sqlite3_reset, - sqlite3_result_blob, - sqlite3_result_double, - sqlite3_result_error, - sqlite3_result_error16, - sqlite3_result_int, - sqlite3_result_int64, - sqlite3_result_null, - sqlite3_result_text, - sqlite3_result_text16, - sqlite3_result_text16be, - sqlite3_result_text16le, - sqlite3_result_value, - sqlite3_rollback_hook, - sqlite3_set_authorizer, - sqlite3_set_auxdata, - sqlite3_snprintf, - sqlite3_step, - sqlite3_table_column_metadata, -#ifndef SQLITE_OMIT_DEPRECATED - sqlite3_thread_cleanup, -#else - 0, -#endif - sqlite3_total_changes, - sqlite3_trace, -#ifndef SQLITE_OMIT_DEPRECATED - sqlite3_transfer_bindings, -#else - 0, -#endif - sqlite3_update_hook, - sqlite3_user_data, - sqlite3_value_blob, - sqlite3_value_bytes, - sqlite3_value_bytes16, - sqlite3_value_double, - sqlite3_value_int, - sqlite3_value_int64, - sqlite3_value_numeric_type, - sqlite3_value_text, - sqlite3_value_text16, - sqlite3_value_text16be, - sqlite3_value_text16le, - sqlite3_value_type, - sqlite3_vmprintf, - /* - ** The original API set ends here. All extensions can call any - ** of the APIs above provided that the pointer is not NULL. But - ** before calling APIs that follow, extension should check the - ** sqlite3_libversion_number() to make sure they are dealing with - ** a library that is new enough to support that API. - ************************************************************************* - */ - sqlite3_overload_function, - - /* - ** Added after 3.3.13 - */ - sqlite3_prepare_v2, - sqlite3_prepare16_v2, - sqlite3_clear_bindings, - - /* - ** Added for 3.4.1 - */ - sqlite3_create_module_v2, - - /* - ** Added for 3.5.0 - */ - sqlite3_bind_zeroblob, - sqlite3_blob_bytes, - sqlite3_blob_close, - sqlite3_blob_open, - sqlite3_blob_read, - sqlite3_blob_write, - sqlite3_create_collation_v2, - sqlite3_file_control, - sqlite3_memory_highwater, - sqlite3_memory_used, -#ifdef SQLITE_MUTEX_OMIT - 0, - 0, - 0, - 0, - 0, -#else - sqlite3_mutex_alloc, - sqlite3_mutex_enter, - sqlite3_mutex_free, - sqlite3_mutex_leave, - sqlite3_mutex_try, -#endif - sqlite3_open_v2, - sqlite3_release_memory, - sqlite3_result_error_nomem, - sqlite3_result_error_toobig, - sqlite3_sleep, - sqlite3_soft_heap_limit, - sqlite3_vfs_find, - sqlite3_vfs_register, - sqlite3_vfs_unregister, - - /* - ** Added for 3.5.8 - */ - sqlite3_threadsafe, - sqlite3_result_zeroblob, - sqlite3_result_error_code, - sqlite3_test_control, - sqlite3_randomness, - sqlite3_context_db_handle, - - /* - ** Added for 3.6.0 - */ - sqlite3_extended_result_codes, - sqlite3_limit, - sqlite3_next_stmt, - sqlite3_sql, - sqlite3_status, - - /* - ** Added for 3.7.4 - */ - sqlite3_backup_finish, - sqlite3_backup_init, - sqlite3_backup_pagecount, - sqlite3_backup_remaining, - sqlite3_backup_step, -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS - sqlite3_compileoption_get, - sqlite3_compileoption_used, -#else - 0, - 0, -#endif - sqlite3_create_function_v2, - sqlite3_db_config, - sqlite3_db_mutex, - sqlite3_db_status, - sqlite3_extended_errcode, - sqlite3_log, - sqlite3_soft_heap_limit64, - sqlite3_sourceid, - sqlite3_stmt_status, - sqlite3_strnicmp, -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY - sqlite3_unlock_notify, -#else - 0, -#endif -#ifndef SQLITE_OMIT_WAL - sqlite3_wal_autocheckpoint, - sqlite3_wal_checkpoint, - sqlite3_wal_hook, -#else - 0, - 0, - 0, -#endif - sqlite3_blob_reopen, - sqlite3_vtab_config, - sqlite3_vtab_on_conflict, - sqlite3_close_v2, - sqlite3_db_filename, - sqlite3_db_readonly, - sqlite3_db_release_memory, - sqlite3_errstr, - sqlite3_stmt_busy, - sqlite3_stmt_readonly, - sqlite3_stricmp, - sqlite3_uri_boolean, - sqlite3_uri_int64, - sqlite3_uri_parameter, - sqlite3_vsnprintf, - sqlite3_wal_checkpoint_v2 -}; - -/* -** Attempt to load an SQLite extension library contained in the file -** zFile. The entry point is zProc. zProc may be 0 in which case a -** default entry point name (sqlite3_extension_init) is used. Use -** of the default name is recommended. -** -** Return SQLITE_OK on success and SQLITE_ERROR if something goes wrong. -** -** If an error occurs and pzErrMsg is not 0, then fill *pzErrMsg with -** error message text. The calling function should free this memory -** by calling sqlite3DbFree(db, ). -*/ -static int sqlite3LoadExtension( - sqlite3 *db, /* Load the extension into this database connection */ - const char *zFile, /* Name of the shared library containing extension */ - const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */ - char **pzErrMsg /* Put error message here if not 0 */ -){ - sqlite3_vfs *pVfs = db->pVfs; - void *handle; - int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*); - char *zErrmsg = 0; - void **aHandle; - int nMsg = 300 + sqlite3Strlen30(zFile); - - if( pzErrMsg ) *pzErrMsg = 0; - - /* Ticket #1863. To avoid a creating security problems for older - ** applications that relink against newer versions of SQLite, the - ** ability to run load_extension is turned off by default. One - ** must call sqlite3_enable_load_extension() to turn on extension - ** loading. Otherwise you get the following error. - */ - if( (db->flags & SQLITE_LoadExtension)==0 ){ - if( pzErrMsg ){ - *pzErrMsg = sqlite3_mprintf("not authorized"); - } - return SQLITE_ERROR; - } - - if( zProc==0 ){ - zProc = "sqlite3_extension_init"; - } - - handle = sqlite3OsDlOpen(pVfs, zFile); - if( handle==0 ){ - if( pzErrMsg ){ - *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg); - if( zErrmsg ){ - sqlite3_snprintf(nMsg, zErrmsg, - "unable to open shared library [%s]", zFile); - sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); - } - } - return SQLITE_ERROR; - } - xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*)) - sqlite3OsDlSym(pVfs, handle, zProc); - if( xInit==0 ){ - if( pzErrMsg ){ - nMsg += sqlite3Strlen30(zProc); - *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg); - if( zErrmsg ){ - sqlite3_snprintf(nMsg, zErrmsg, - "no entry point [%s] in shared library [%s]", zProc,zFile); - sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); - } - sqlite3OsDlClose(pVfs, handle); - } - return SQLITE_ERROR; - }else if( xInit(db, &zErrmsg, &sqlite3Apis) ){ - if( pzErrMsg ){ - *pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg); - } - sqlite3_free(zErrmsg); - sqlite3OsDlClose(pVfs, handle); - return SQLITE_ERROR; - } - - /* Append the new shared library handle to the db->aExtension array. */ - aHandle = sqlite3DbMallocZero(db, sizeof(handle)*(db->nExtension+1)); - if( aHandle==0 ){ - return SQLITE_NOMEM; - } - if( db->nExtension>0 ){ - memcpy(aHandle, db->aExtension, sizeof(handle)*db->nExtension); - } - sqlite3DbFree(db, db->aExtension); - db->aExtension = aHandle; - - db->aExtension[db->nExtension++] = handle; - return SQLITE_OK; -} -SQLITE_API int sqlite3_load_extension( - sqlite3 *db, /* Load the extension into this database connection */ - const char *zFile, /* Name of the shared library containing extension */ - const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */ - char **pzErrMsg /* Put error message here if not 0 */ -){ - int rc; - sqlite3_mutex_enter(db->mutex); - rc = sqlite3LoadExtension(db, zFile, zProc, pzErrMsg); - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/* -** Call this routine when the database connection is closing in order -** to clean up loaded extensions -*/ -SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){ - int i; - assert( sqlite3_mutex_held(db->mutex) ); - for(i=0; inExtension; i++){ - sqlite3OsDlClose(db->pVfs, db->aExtension[i]); - } - sqlite3DbFree(db, db->aExtension); -} - -/* -** Enable or disable extension loading. Extension loading is disabled by -** default so as not to open security holes in older applications. -*/ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ - sqlite3_mutex_enter(db->mutex); - if( onoff ){ - db->flags |= SQLITE_LoadExtension; - }else{ - db->flags &= ~SQLITE_LoadExtension; - } - sqlite3_mutex_leave(db->mutex); - return SQLITE_OK; -} - -#endif /* SQLITE_OMIT_LOAD_EXTENSION */ - -/* -** The auto-extension code added regardless of whether or not extension -** loading is supported. We need a dummy sqlite3Apis pointer for that -** code if regular extension loading is not available. This is that -** dummy pointer. -*/ -#ifdef SQLITE_OMIT_LOAD_EXTENSION -static const sqlite3_api_routines sqlite3Apis = { 0 }; -#endif - - -/* -** The following object holds the list of automatically loaded -** extensions. -** -** This list is shared across threads. The SQLITE_MUTEX_STATIC_MASTER -** mutex must be held while accessing this list. -*/ -typedef struct sqlite3AutoExtList sqlite3AutoExtList; -static SQLITE_WSD struct sqlite3AutoExtList { - int nExt; /* Number of entries in aExt[] */ - void (**aExt)(void); /* Pointers to the extension init functions */ -} sqlite3Autoext = { 0, 0 }; - -/* The "wsdAutoext" macro will resolve to the autoextension -** state vector. If writable static data is unsupported on the target, -** we have to locate the state vector at run-time. In the more common -** case where writable static data is supported, wsdStat can refer directly -** to the "sqlite3Autoext" state vector declared above. -*/ -#ifdef SQLITE_OMIT_WSD -# define wsdAutoextInit \ - sqlite3AutoExtList *x = &GLOBAL(sqlite3AutoExtList,sqlite3Autoext) -# define wsdAutoext x[0] -#else -# define wsdAutoextInit -# define wsdAutoext sqlite3Autoext -#endif - - -/* -** Register a statically linked extension that is automatically -** loaded by every new database connection. -*/ -SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){ - int rc = SQLITE_OK; -#ifndef SQLITE_OMIT_AUTOINIT - rc = sqlite3_initialize(); - if( rc ){ - return rc; - }else -#endif - { - int i; -#if SQLITE_THREADSAFE - sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); -#endif - wsdAutoextInit; - sqlite3_mutex_enter(mutex); - for(i=0; i=wsdAutoext.nExt ){ - xInit = 0; - go = 0; - }else{ - xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*)) - wsdAutoext.aExt[i]; - } - sqlite3_mutex_leave(mutex); - zErrmsg = 0; - if( xInit && (rc = xInit(db, &zErrmsg, &sqlite3Apis))!=0 ){ - sqlite3Error(db, rc, - "automatic extension loading failed: %s", zErrmsg); - go = 0; - } - sqlite3_free(zErrmsg); - } -} - -/************** End of loadext.c *********************************************/ -/************** Begin file pragma.c ******************************************/ -/* -** 2003 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to implement the PRAGMA command. -*/ - -/* -** Interpret the given string as a safety level. Return 0 for OFF, -** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or -** unrecognized string argument. The FULL option is disallowed -** if the omitFull parameter it 1. -** -** Note that the values returned are one less that the values that -** should be passed into sqlite3BtreeSetSafetyLevel(). The is done -** to support legacy SQL code. The safety level used to be boolean -** and older scripts may have used numbers 0 for OFF and 1 for ON. -*/ -static u8 getSafetyLevel(const char *z, int omitFull, int dflt){ - /* 123456789 123456789 */ - static const char zText[] = "onoffalseyestruefull"; - static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 16}; - static const u8 iLength[] = {2, 2, 3, 5, 3, 4, 4}; - static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 2}; - int i, n; - if( sqlite3Isdigit(*z) ){ - return (u8)sqlite3Atoi(z); - } - n = sqlite3Strlen30(z); - for(i=0; i=0&&i<=2)?i:0); -} -#endif /* ifndef SQLITE_OMIT_AUTOVACUUM */ - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* -** Interpret the given string as a temp db location. Return 1 for file -** backed temporary databases, 2 for the Red-Black tree in memory database -** and 0 to use the compile-time default. -*/ -static int getTempStore(const char *z){ - if( z[0]>='0' && z[0]<='2' ){ - return z[0] - '0'; - }else if( sqlite3StrICmp(z, "file")==0 ){ - return 1; - }else if( sqlite3StrICmp(z, "memory")==0 ){ - return 2; - }else{ - return 0; - } -} -#endif /* SQLITE_PAGER_PRAGMAS */ - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* -** Invalidate temp storage, either when the temp storage is changed -** from default, or when 'file' and the temp_store_directory has changed -*/ -static int invalidateTempStorage(Parse *pParse){ - sqlite3 *db = pParse->db; - if( db->aDb[1].pBt!=0 ){ - if( !db->autoCommit || sqlite3BtreeIsInReadTrans(db->aDb[1].pBt) ){ - sqlite3ErrorMsg(pParse, "temporary storage cannot be changed " - "from within a transaction"); - return SQLITE_ERROR; - } - sqlite3BtreeClose(db->aDb[1].pBt); - db->aDb[1].pBt = 0; - sqlite3ResetAllSchemasOfConnection(db); - } - return SQLITE_OK; -} -#endif /* SQLITE_PAGER_PRAGMAS */ - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* -** If the TEMP database is open, close it and mark the database schema -** as needing reloading. This must be done when using the SQLITE_TEMP_STORE -** or DEFAULT_TEMP_STORE pragmas. -*/ -static int changeTempStorage(Parse *pParse, const char *zStorageType){ - int ts = getTempStore(zStorageType); - sqlite3 *db = pParse->db; - if( db->temp_store==ts ) return SQLITE_OK; - if( invalidateTempStorage( pParse ) != SQLITE_OK ){ - return SQLITE_ERROR; - } - db->temp_store = (u8)ts; - return SQLITE_OK; -} -#endif /* SQLITE_PAGER_PRAGMAS */ - -/* -** Generate code to return a single integer value. -*/ -static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){ - Vdbe *v = sqlite3GetVdbe(pParse); - int mem = ++pParse->nMem; - i64 *pI64 = sqlite3DbMallocRaw(pParse->db, sizeof(value)); - if( pI64 ){ - memcpy(pI64, &value, sizeof(value)); - } - sqlite3VdbeAddOp4(v, OP_Int64, 0, mem, 0, (char*)pI64, P4_INT64); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC); - sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1); -} - -#ifndef SQLITE_OMIT_FLAG_PRAGMAS -/* -** Check to see if zRight and zLeft refer to a pragma that queries -** or changes one of the flags in db->flags. Return 1 if so and 0 if not. -** Also, implement the pragma. -*/ -static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){ - static const struct sPragmaType { - const char *zName; /* Name of the pragma */ - int mask; /* Mask for the db->flags value */ - } aPragma[] = { - { "full_column_names", SQLITE_FullColNames }, - { "short_column_names", SQLITE_ShortColNames }, - { "count_changes", SQLITE_CountRows }, - { "empty_result_callbacks", SQLITE_NullCallback }, - { "legacy_file_format", SQLITE_LegacyFileFmt }, - { "fullfsync", SQLITE_FullFSync }, - { "checkpoint_fullfsync", SQLITE_CkptFullFSync }, - { "reverse_unordered_selects", SQLITE_ReverseOrder }, -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX - { "automatic_index", SQLITE_AutoIndex }, -#endif -#ifdef SQLITE_DEBUG - { "sql_trace", SQLITE_SqlTrace }, - { "vdbe_listing", SQLITE_VdbeListing }, - { "vdbe_trace", SQLITE_VdbeTrace }, - { "vdbe_addoptrace", SQLITE_VdbeAddopTrace}, - { "vdbe_debug", SQLITE_SqlTrace | SQLITE_VdbeListing - | SQLITE_VdbeTrace }, -#endif -#ifndef SQLITE_OMIT_CHECK - { "ignore_check_constraints", SQLITE_IgnoreChecks }, -#endif - /* The following is VERY experimental */ - { "writable_schema", SQLITE_WriteSchema|SQLITE_RecoveryMode }, - - /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted - ** flag if there are any active statements. */ - { "read_uncommitted", SQLITE_ReadUncommitted }, - { "recursive_triggers", SQLITE_RecTriggers }, - - /* This flag may only be set if both foreign-key and trigger support - ** are present in the build. */ -#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) - { "foreign_keys", SQLITE_ForeignKeys }, -#endif - }; - int i; - const struct sPragmaType *p; - for(i=0, p=aPragma; izName)==0 ){ - sqlite3 *db = pParse->db; - Vdbe *v; - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); /* Already allocated by sqlite3Pragma() */ - if( ALWAYS(v) ){ - if( zRight==0 ){ - returnSingleInt(pParse, p->zName, (db->flags & p->mask)!=0 ); - }else{ - int mask = p->mask; /* Mask of bits to set or clear. */ - if( db->autoCommit==0 ){ - /* Foreign key support may not be enabled or disabled while not - ** in auto-commit mode. */ - mask &= ~(SQLITE_ForeignKeys); - } - - if( sqlite3GetBoolean(zRight, 0) ){ - db->flags |= mask; - }else{ - db->flags &= ~mask; - } - - /* Many of the flag-pragmas modify the code generated by the SQL - ** compiler (eg. count_changes). So add an opcode to expire all - ** compiled SQL statements after modifying a pragma value. - */ - sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); - } - } - - return 1; - } - } - return 0; -} -#endif /* SQLITE_OMIT_FLAG_PRAGMAS */ - -/* -** Return a human-readable name for a constraint resolution action. -*/ -#ifndef SQLITE_OMIT_FOREIGN_KEY -static const char *actionName(u8 action){ - const char *zName; - switch( action ){ - case OE_SetNull: zName = "SET NULL"; break; - case OE_SetDflt: zName = "SET DEFAULT"; break; - case OE_Cascade: zName = "CASCADE"; break; - case OE_Restrict: zName = "RESTRICT"; break; - default: zName = "NO ACTION"; - assert( action==OE_None ); break; - } - return zName; -} -#endif - - -/* -** Parameter eMode must be one of the PAGER_JOURNALMODE_XXX constants -** defined in pager.h. This function returns the associated lowercase -** journal-mode name. -*/ -SQLITE_PRIVATE const char *sqlite3JournalModename(int eMode){ - static char * const azModeName[] = { - "delete", "persist", "off", "truncate", "memory" -#ifndef SQLITE_OMIT_WAL - , "wal" -#endif - }; - assert( PAGER_JOURNALMODE_DELETE==0 ); - assert( PAGER_JOURNALMODE_PERSIST==1 ); - assert( PAGER_JOURNALMODE_OFF==2 ); - assert( PAGER_JOURNALMODE_TRUNCATE==3 ); - assert( PAGER_JOURNALMODE_MEMORY==4 ); - assert( PAGER_JOURNALMODE_WAL==5 ); - assert( eMode>=0 && eMode<=ArraySize(azModeName) ); - - if( eMode==ArraySize(azModeName) ) return 0; - return azModeName[eMode]; -} - -/* -** Process a pragma statement. -** -** Pragmas are of this form: -** -** PRAGMA [database.]id [= value] -** -** The identifier might also be a string. The value is a string, and -** identifier, or a number. If minusFlag is true, then the value is -** a number that was preceded by a minus sign. -** -** If the left side is "database.id" then pId1 is the database name -** and pId2 is the id. If the left side is just "id" then pId1 is the -** id and pId2 is any empty string. -*/ -SQLITE_PRIVATE void sqlite3Pragma( - Parse *pParse, - Token *pId1, /* First part of [database.]id field */ - Token *pId2, /* Second part of [database.]id field, or NULL */ - Token *pValue, /* Token for , or NULL */ - int minusFlag /* True if a '-' sign preceded */ -){ - char *zLeft = 0; /* Nul-terminated UTF-8 string */ - char *zRight = 0; /* Nul-terminated UTF-8 string , or NULL */ - const char *zDb = 0; /* The database name */ - Token *pId; /* Pointer to token */ - int iDb; /* Database index for */ - char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */ - int rc; /* return value form SQLITE_FCNTL_PRAGMA */ - sqlite3 *db = pParse->db; /* The database connection */ - Db *pDb; /* The specific database being pragmaed */ - Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(db); /* Prepared statement */ - - if( v==0 ) return; - sqlite3VdbeRunOnlyOnce(v); - pParse->nMem = 2; - - /* Interpret the [database.] part of the pragma statement. iDb is the - ** index of the database this pragma is being applied to in db.aDb[]. */ - iDb = sqlite3TwoPartName(pParse, pId1, pId2, &pId); - if( iDb<0 ) return; - pDb = &db->aDb[iDb]; - - /* If the temp database has been explicitly named as part of the - ** pragma, make sure it is open. - */ - if( iDb==1 && sqlite3OpenTempDatabase(pParse) ){ - return; - } - - zLeft = sqlite3NameFromToken(db, pId); - if( !zLeft ) return; - if( minusFlag ){ - zRight = sqlite3MPrintf(db, "-%T", pValue); - }else{ - zRight = sqlite3NameFromToken(db, pValue); - } - - assert( pId2 ); - zDb = pId2->n>0 ? pDb->zName : 0; - if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){ - goto pragma_out; - } - - /* Send an SQLITE_FCNTL_PRAGMA file-control to the underlying VFS - ** connection. If it returns SQLITE_OK, then assume that the VFS - ** handled the pragma and generate a no-op prepared statement. - */ - aFcntl[0] = 0; - aFcntl[1] = zLeft; - aFcntl[2] = zRight; - aFcntl[3] = 0; - db->busyHandler.nBusy = 0; - rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl); - if( rc==SQLITE_OK ){ - if( aFcntl[0] ){ - int mem = ++pParse->nMem; - sqlite3VdbeAddOp4(v, OP_String8, 0, mem, 0, aFcntl[0], 0); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "result", SQLITE_STATIC); - sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1); - sqlite3_free(aFcntl[0]); - } - }else if( rc!=SQLITE_NOTFOUND ){ - if( aFcntl[0] ){ - sqlite3ErrorMsg(pParse, "%s", aFcntl[0]); - sqlite3_free(aFcntl[0]); - } - pParse->nErr++; - pParse->rc = rc; - }else - - -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) - /* - ** PRAGMA [database.]default_cache_size - ** PRAGMA [database.]default_cache_size=N - ** - ** The first form reports the current persistent setting for the - ** page cache size. The value returned is the maximum number of - ** pages in the page cache. The second form sets both the current - ** page cache size value and the persistent page cache size value - ** stored in the database file. - ** - ** Older versions of SQLite would set the default cache size to a - ** negative number to indicate synchronous=OFF. These days, synchronous - ** is always on by default regardless of the sign of the default cache - ** size. But continue to take the absolute value of the default cache - ** size of historical compatibility. - */ - if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){ - static const VdbeOpList getCacheSize[] = { - { OP_Transaction, 0, 0, 0}, /* 0 */ - { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */ - { OP_IfPos, 1, 7, 0}, - { OP_Integer, 0, 2, 0}, - { OP_Subtract, 1, 2, 1}, - { OP_IfPos, 1, 7, 0}, - { OP_Integer, 0, 1, 0}, /* 6 */ - { OP_ResultRow, 1, 1, 0}, - }; - int addr; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeUsesBtree(v, iDb); - if( !zRight ){ - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC); - pParse->nMem += 2; - addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); - sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP1(v, addr+1, iDb); - sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); - }else{ - int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3VdbeAddOp2(v, OP_Integer, size, 1); - sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1); - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - pDb->pSchema->cache_size = size; - sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); - } - }else -#endif /* !SQLITE_OMIT_PAGER_PRAGMAS && !SQLITE_OMIT_DEPRECATED */ - -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) - /* - ** PRAGMA [database.]page_size - ** PRAGMA [database.]page_size=N - ** - ** The first form reports the current setting for the - ** database page size in bytes. The second form sets the - ** database page size value. The value can only be set if - ** the database has not yet been created. - */ - if( sqlite3StrICmp(zLeft,"page_size")==0 ){ - Btree *pBt = pDb->pBt; - assert( pBt!=0 ); - if( !zRight ){ - int size = ALWAYS(pBt) ? sqlite3BtreeGetPageSize(pBt) : 0; - returnSingleInt(pParse, "page_size", size); - }else{ - /* Malloc may fail when setting the page-size, as there is an internal - ** buffer that the pager module resizes using sqlite3_realloc(). - */ - db->nextPagesize = sqlite3Atoi(zRight); - if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){ - db->mallocFailed = 1; - } - } - }else - - /* - ** PRAGMA [database.]secure_delete - ** PRAGMA [database.]secure_delete=ON/OFF - ** - ** The first form reports the current setting for the - ** secure_delete flag. The second form changes the secure_delete - ** flag setting and reports thenew value. - */ - if( sqlite3StrICmp(zLeft,"secure_delete")==0 ){ - Btree *pBt = pDb->pBt; - int b = -1; - assert( pBt!=0 ); - if( zRight ){ - b = sqlite3GetBoolean(zRight, 0); - } - if( pId2->n==0 && b>=0 ){ - int ii; - for(ii=0; iinDb; ii++){ - sqlite3BtreeSecureDelete(db->aDb[ii].pBt, b); - } - } - b = sqlite3BtreeSecureDelete(pBt, b); - returnSingleInt(pParse, "secure_delete", b); - }else - - /* - ** PRAGMA [database.]max_page_count - ** PRAGMA [database.]max_page_count=N - ** - ** The first form reports the current setting for the - ** maximum number of pages in the database file. The - ** second form attempts to change this setting. Both - ** forms return the current setting. - ** - ** The absolute value of N is used. This is undocumented and might - ** change. The only purpose is to provide an easy way to test - ** the sqlite3AbsInt32() function. - ** - ** PRAGMA [database.]page_count - ** - ** Return the number of pages in the specified database. - */ - if( sqlite3StrICmp(zLeft,"page_count")==0 - || sqlite3StrICmp(zLeft,"max_page_count")==0 - ){ - int iReg; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3CodeVerifySchema(pParse, iDb); - iReg = ++pParse->nMem; - if( sqlite3Tolower(zLeft[0])=='p' ){ - sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); - }else{ - sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, - sqlite3AbsInt32(sqlite3Atoi(zRight))); - } - sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT); - }else - - /* - ** PRAGMA [database.]locking_mode - ** PRAGMA [database.]locking_mode = (normal|exclusive) - */ - if( sqlite3StrICmp(zLeft,"locking_mode")==0 ){ - const char *zRet = "normal"; - int eMode = getLockingMode(zRight); - - if( pId2->n==0 && eMode==PAGER_LOCKINGMODE_QUERY ){ - /* Simple "PRAGMA locking_mode;" statement. This is a query for - ** the current default locking mode (which may be different to - ** the locking-mode of the main database). - */ - eMode = db->dfltLockMode; - }else{ - Pager *pPager; - if( pId2->n==0 ){ - /* This indicates that no database name was specified as part - ** of the PRAGMA command. In this case the locking-mode must be - ** set on all attached databases, as well as the main db file. - ** - ** Also, the sqlite3.dfltLockMode variable is set so that - ** any subsequently attached databases also use the specified - ** locking mode. - */ - int ii; - assert(pDb==&db->aDb[0]); - for(ii=2; iinDb; ii++){ - pPager = sqlite3BtreePager(db->aDb[ii].pBt); - sqlite3PagerLockingMode(pPager, eMode); - } - db->dfltLockMode = (u8)eMode; - } - pPager = sqlite3BtreePager(pDb->pBt); - eMode = sqlite3PagerLockingMode(pPager, eMode); - } - - assert(eMode==PAGER_LOCKINGMODE_NORMAL||eMode==PAGER_LOCKINGMODE_EXCLUSIVE); - if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){ - zRet = "exclusive"; - } - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", SQLITE_STATIC); - sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - }else - - /* - ** PRAGMA [database.]journal_mode - ** PRAGMA [database.]journal_mode = - ** (delete|persist|off|truncate|memory|wal|off) - */ - if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){ - int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */ - int ii; /* Loop counter */ - - /* Force the schema to be loaded on all databases. This causes all - ** database files to be opened and the journal_modes set. This is - ** necessary because subsequent processing must know if the databases - ** are in WAL mode. */ - if( sqlite3ReadSchema(pParse) ){ - goto pragma_out; - } - - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC); - - if( zRight==0 ){ - /* If there is no "=MODE" part of the pragma, do a query for the - ** current mode */ - eMode = PAGER_JOURNALMODE_QUERY; - }else{ - const char *zMode; - int n = sqlite3Strlen30(zRight); - for(eMode=0; (zMode = sqlite3JournalModename(eMode))!=0; eMode++){ - if( sqlite3StrNICmp(zRight, zMode, n)==0 ) break; - } - if( !zMode ){ - /* If the "=MODE" part does not match any known journal mode, - ** then do a query */ - eMode = PAGER_JOURNALMODE_QUERY; - } - } - if( eMode==PAGER_JOURNALMODE_QUERY && pId2->n==0 ){ - /* Convert "PRAGMA journal_mode" into "PRAGMA main.journal_mode" */ - iDb = 0; - pId2->n = 1; - } - for(ii=db->nDb-1; ii>=0; ii--){ - if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){ - sqlite3VdbeUsesBtree(v, ii); - sqlite3VdbeAddOp3(v, OP_JournalMode, ii, 1, eMode); - } - } - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - }else - - /* - ** PRAGMA [database.]journal_size_limit - ** PRAGMA [database.]journal_size_limit=N - ** - ** Get or set the size limit on rollback journal files. - */ - if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){ - Pager *pPager = sqlite3BtreePager(pDb->pBt); - i64 iLimit = -2; - if( zRight ){ - sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8); - if( iLimit<-1 ) iLimit = -1; - } - iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); - returnSingleInt(pParse, "journal_size_limit", iLimit); - }else - -#endif /* SQLITE_OMIT_PAGER_PRAGMAS */ - - /* - ** PRAGMA [database.]auto_vacuum - ** PRAGMA [database.]auto_vacuum=N - ** - ** Get or set the value of the database 'auto-vacuum' parameter. - ** The value is one of: 0 NONE 1 FULL 2 INCREMENTAL - */ -#ifndef SQLITE_OMIT_AUTOVACUUM - if( sqlite3StrICmp(zLeft,"auto_vacuum")==0 ){ - Btree *pBt = pDb->pBt; - assert( pBt!=0 ); - if( sqlite3ReadSchema(pParse) ){ - goto pragma_out; - } - if( !zRight ){ - int auto_vacuum; - if( ALWAYS(pBt) ){ - auto_vacuum = sqlite3BtreeGetAutoVacuum(pBt); - }else{ - auto_vacuum = SQLITE_DEFAULT_AUTOVACUUM; - } - returnSingleInt(pParse, "auto_vacuum", auto_vacuum); - }else{ - int eAuto = getAutoVacuum(zRight); - assert( eAuto>=0 && eAuto<=2 ); - db->nextAutovac = (u8)eAuto; - if( ALWAYS(eAuto>=0) ){ - /* Call SetAutoVacuum() to set initialize the internal auto and - ** incr-vacuum flags. This is required in case this connection - ** creates the database file. It is important that it is created - ** as an auto-vacuum capable db. - */ - rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto); - if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){ - /* When setting the auto_vacuum mode to either "full" or - ** "incremental", write the value of meta[6] in the database - ** file. Before writing to meta[6], check that meta[3] indicates - ** that this really is an auto-vacuum capable database. - */ - static const VdbeOpList setMeta6[] = { - { OP_Transaction, 0, 1, 0}, /* 0 */ - { OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE}, - { OP_If, 1, 0, 0}, /* 2 */ - { OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */ - { OP_Integer, 0, 1, 0}, /* 4 */ - { OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */ - }; - int iAddr; - iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6); - sqlite3VdbeChangeP1(v, iAddr, iDb); - sqlite3VdbeChangeP1(v, iAddr+1, iDb); - sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4); - sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1); - sqlite3VdbeChangeP1(v, iAddr+5, iDb); - sqlite3VdbeUsesBtree(v, iDb); - } - } - } - }else -#endif - - /* - ** PRAGMA [database.]incremental_vacuum(N) - ** - ** Do N steps of incremental vacuuming on a database. - */ -#ifndef SQLITE_OMIT_AUTOVACUUM - if( sqlite3StrICmp(zLeft,"incremental_vacuum")==0 ){ - int iLimit, addr; - if( sqlite3ReadSchema(pParse) ){ - goto pragma_out; - } - if( zRight==0 || !sqlite3GetInt32(zRight, &iLimit) || iLimit<=0 ){ - iLimit = 0x7fffffff; - } - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3VdbeAddOp2(v, OP_Integer, iLimit, 1); - addr = sqlite3VdbeAddOp1(v, OP_IncrVacuum, iDb); - sqlite3VdbeAddOp1(v, OP_ResultRow, 1); - sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); - sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr); - sqlite3VdbeJumpHere(v, addr); - }else -#endif - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS - /* - ** PRAGMA [database.]cache_size - ** PRAGMA [database.]cache_size=N - ** - ** The first form reports the current local setting for the - ** page cache size. The second form sets the local - ** page cache size value. If N is positive then that is the - ** number of pages in the cache. If N is negative, then the - ** number of pages is adjusted so that the cache uses -N kibibytes - ** of memory. - */ - if( sqlite3StrICmp(zLeft,"cache_size")==0 ){ - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - if( !zRight ){ - returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size); - }else{ - int size = sqlite3Atoi(zRight); - pDb->pSchema->cache_size = size; - sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); - } - }else - - /* - ** PRAGMA temp_store - ** PRAGMA temp_store = "default"|"memory"|"file" - ** - ** Return or set the local value of the temp_store flag. Changing - ** the local value does not make changes to the disk file and the default - ** value will be restored the next time the database is opened. - ** - ** Note that it is possible for the library compile-time options to - ** override this setting - */ - if( sqlite3StrICmp(zLeft, "temp_store")==0 ){ - if( !zRight ){ - returnSingleInt(pParse, "temp_store", db->temp_store); - }else{ - changeTempStorage(pParse, zRight); - } - }else - - /* - ** PRAGMA temp_store_directory - ** PRAGMA temp_store_directory = ""|"directory_name" - ** - ** Return or set the local value of the temp_store_directory flag. Changing - ** the value sets a specific directory to be used for temporary files. - ** Setting to a null string reverts to the default temporary directory search. - ** If temporary directory is changed, then invalidateTempStorage. - ** - */ - if( sqlite3StrICmp(zLeft, "temp_store_directory")==0 ){ - if( !zRight ){ - if( sqlite3_temp_directory ){ - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, - "temp_store_directory", SQLITE_STATIC); - sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite3_temp_directory, 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - } - }else{ -#ifndef SQLITE_OMIT_WSD - if( zRight[0] ){ - int res; - rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); - if( rc!=SQLITE_OK || res==0 ){ - sqlite3ErrorMsg(pParse, "not a writable directory"); - goto pragma_out; - } - } - if( SQLITE_TEMP_STORE==0 - || (SQLITE_TEMP_STORE==1 && db->temp_store<=1) - || (SQLITE_TEMP_STORE==2 && db->temp_store==1) - ){ - invalidateTempStorage(pParse); - } - sqlite3_free(sqlite3_temp_directory); - if( zRight[0] ){ - sqlite3_temp_directory = sqlite3_mprintf("%s", zRight); - }else{ - sqlite3_temp_directory = 0; - } -#endif /* SQLITE_OMIT_WSD */ - } - }else - -#if SQLITE_OS_WIN - /* - ** PRAGMA data_store_directory - ** PRAGMA data_store_directory = ""|"directory_name" - ** - ** Return or set the local value of the data_store_directory flag. Changing - ** the value sets a specific directory to be used for database files that - ** were specified with a relative pathname. Setting to a null string reverts - ** to the default database directory, which for database files specified with - ** a relative path will probably be based on the current directory for the - ** process. Database file specified with an absolute path are not impacted - ** by this setting, regardless of its value. - ** - */ - if( sqlite3StrICmp(zLeft, "data_store_directory")==0 ){ - if( !zRight ){ - if( sqlite3_data_directory ){ - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, - "data_store_directory", SQLITE_STATIC); - sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite3_data_directory, 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - } - }else{ -#ifndef SQLITE_OMIT_WSD - if( zRight[0] ){ - int res; - rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); - if( rc!=SQLITE_OK || res==0 ){ - sqlite3ErrorMsg(pParse, "not a writable directory"); - goto pragma_out; - } - } - sqlite3_free(sqlite3_data_directory); - if( zRight[0] ){ - sqlite3_data_directory = sqlite3_mprintf("%s", zRight); - }else{ - sqlite3_data_directory = 0; - } -#endif /* SQLITE_OMIT_WSD */ - } - }else -#endif - -#if !defined(SQLITE_ENABLE_LOCKING_STYLE) -# if defined(__APPLE__) -# define SQLITE_ENABLE_LOCKING_STYLE 1 -# else -# define SQLITE_ENABLE_LOCKING_STYLE 0 -# endif -#endif -#if SQLITE_ENABLE_LOCKING_STYLE - /* - ** PRAGMA [database.]lock_proxy_file - ** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path" - ** - ** Return or set the value of the lock_proxy_file flag. Changing - ** the value sets a specific file to be used for database access locks. - ** - */ - if( sqlite3StrICmp(zLeft, "lock_proxy_file")==0 ){ - if( !zRight ){ - Pager *pPager = sqlite3BtreePager(pDb->pBt); - char *proxy_file_path = NULL; - sqlite3_file *pFile = sqlite3PagerFile(pPager); - sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE, - &proxy_file_path); - - if( proxy_file_path ){ - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, - "lock_proxy_file", SQLITE_STATIC); - sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, proxy_file_path, 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - } - }else{ - Pager *pPager = sqlite3BtreePager(pDb->pBt); - sqlite3_file *pFile = sqlite3PagerFile(pPager); - int res; - if( zRight[0] ){ - res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE, - zRight); - } else { - res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE, - NULL); - } - if( res!=SQLITE_OK ){ - sqlite3ErrorMsg(pParse, "failed to set lock proxy file"); - goto pragma_out; - } - } - }else -#endif /* SQLITE_ENABLE_LOCKING_STYLE */ - - /* - ** PRAGMA [database.]synchronous - ** PRAGMA [database.]synchronous=OFF|ON|NORMAL|FULL - ** - ** Return or set the local value of the synchronous flag. Changing - ** the local value does not make changes to the disk file and the - ** default value will be restored the next time the database is - ** opened. - */ - if( sqlite3StrICmp(zLeft,"synchronous")==0 ){ - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - if( !zRight ){ - returnSingleInt(pParse, "synchronous", pDb->safety_level-1); - }else{ - if( !db->autoCommit ){ - sqlite3ErrorMsg(pParse, - "Safety level may not be changed inside a transaction"); - }else{ - pDb->safety_level = getSafetyLevel(zRight,0,1)+1; - } - } - }else -#endif /* SQLITE_OMIT_PAGER_PRAGMAS */ - -#ifndef SQLITE_OMIT_FLAG_PRAGMAS - if( flagPragma(pParse, zLeft, zRight) ){ - /* The flagPragma() subroutine also generates any necessary code - ** there is nothing more to do here */ - }else -#endif /* SQLITE_OMIT_FLAG_PRAGMAS */ - -#ifndef SQLITE_OMIT_SCHEMA_PRAGMAS - /* - ** PRAGMA table_info(
    ) - ** - ** Return a single row for each column of the named table. The columns of - ** the returned data set are: - ** - ** cid: Column id (numbered from left to right, starting at 0) - ** name: Column name - ** type: Column declaration type. - ** notnull: True if 'NOT NULL' is part of column declaration - ** dflt_value: The default value for the column, if any. - */ - if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){ - Table *pTab; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pTab = sqlite3FindTable(db, zRight, zDb); - if( pTab ){ - int i, k; - int nHidden = 0; - Column *pCol; - Index *pPk; - for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){} - sqlite3VdbeSetNumCols(v, 6); - pParse->nMem = 6; - sqlite3CodeVerifySchema(pParse, iDb); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "notnull", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "dflt_value", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", SQLITE_STATIC); - sqlite3ViewGetColumnNames(pParse, pTab); - for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ - if( IsHiddenColumn(pCol) ){ - nHidden++; - continue; - } - sqlite3VdbeAddOp2(v, OP_Integer, i-nHidden, 1); - sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pCol->zName, 0); - sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, - pCol->zType ? pCol->zType : "", 0); - sqlite3VdbeAddOp2(v, OP_Integer, (pCol->notNull ? 1 : 0), 4); - if( pCol->zDflt ){ - sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0); - }else{ - sqlite3VdbeAddOp2(v, OP_Null, 0, 5); - } - if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ - k = 0; - }else if( pPk==0 ){ - k = 1; - }else{ - for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} - } - sqlite3VdbeAddOp2(v, OP_Integer, k, 6); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); - } - } - }else - - if( sqlite3StrICmp(zLeft, "index_info")==0 && zRight ){ - Index *pIdx; - Table *pTab; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pIdx = sqlite3FindIndex(db, zRight, zDb); - if( pIdx ){ - int i; - pTab = pIdx->pTable; - sqlite3VdbeSetNumCols(v, 3); - pParse->nMem = 3; - sqlite3CodeVerifySchema(pParse, iDb); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", SQLITE_STATIC); - for(i=0; inColumn; i++){ - int cnum = pIdx->aiColumn[i]; - sqlite3VdbeAddOp2(v, OP_Integer, i, 1); - sqlite3VdbeAddOp2(v, OP_Integer, cnum, 2); - assert( pTab->nCol>cnum ); - sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pTab->aCol[cnum].zName, 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); - } - } - }else - - if( sqlite3StrICmp(zLeft, "index_list")==0 && zRight ){ - Index *pIdx; - Table *pTab; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pTab = sqlite3FindTable(db, zRight, zDb); - if( pTab ){ - v = sqlite3GetVdbe(pParse); - pIdx = pTab->pIndex; - if( pIdx ){ - int i = 0; - sqlite3VdbeSetNumCols(v, 3); - pParse->nMem = 3; - sqlite3CodeVerifySchema(pParse, iDb); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC); - while(pIdx){ - sqlite3VdbeAddOp2(v, OP_Integer, i, 1); - sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0); - sqlite3VdbeAddOp2(v, OP_Integer, pIdx->onError!=OE_None, 3); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); - ++i; - pIdx = pIdx->pNext; - } - } - } - }else - - if( sqlite3StrICmp(zLeft, "database_list")==0 ){ - int i; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeSetNumCols(v, 3); - pParse->nMem = 3; - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "file", SQLITE_STATIC); - for(i=0; inDb; i++){ - if( db->aDb[i].pBt==0 ) continue; - assert( db->aDb[i].zName!=0 ); - sqlite3VdbeAddOp2(v, OP_Integer, i, 1); - sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, db->aDb[i].zName, 0); - sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, - sqlite3BtreeGetFilename(db->aDb[i].pBt), 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); - } - }else - - if( sqlite3StrICmp(zLeft, "collation_list")==0 ){ - int i = 0; - HashElem *p; - sqlite3VdbeSetNumCols(v, 2); - pParse->nMem = 2; - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); - for(p=sqliteHashFirst(&db->aCollSeq); p; p=sqliteHashNext(p)){ - CollSeq *pColl = (CollSeq *)sqliteHashData(p); - sqlite3VdbeAddOp2(v, OP_Integer, i++, 1); - sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pColl->zName, 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2); - } - }else -#endif /* SQLITE_OMIT_SCHEMA_PRAGMAS */ - -#ifndef SQLITE_OMIT_FOREIGN_KEY - if( sqlite3StrICmp(zLeft, "foreign_key_list")==0 && zRight ){ - FKey *pFK; - Table *pTab; - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pTab = sqlite3FindTable(db, zRight, zDb); - if( pTab ){ - v = sqlite3GetVdbe(pParse); - pFK = pTab->pFKey; - if( pFK ){ - int i = 0; - sqlite3VdbeSetNumCols(v, 8); - pParse->nMem = 8; - sqlite3CodeVerifySchema(pParse, iDb); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "id", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "seq", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "table", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "from", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "to", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "on_update", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 6, COLNAME_NAME, "on_delete", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 7, COLNAME_NAME, "match", SQLITE_STATIC); - while(pFK){ - int j; - for(j=0; jnCol; j++){ - char *zCol = pFK->aCol[j].zCol; - char *zOnDelete = (char *)actionName(pFK->aAction[0]); - char *zOnUpdate = (char *)actionName(pFK->aAction[1]); - sqlite3VdbeAddOp2(v, OP_Integer, i, 1); - sqlite3VdbeAddOp2(v, OP_Integer, j, 2); - sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pFK->zTo, 0); - sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, - pTab->aCol[pFK->aCol[j].iFrom].zName, 0); - sqlite3VdbeAddOp4(v, zCol ? OP_String8 : OP_Null, 0, 5, 0, zCol, 0); - sqlite3VdbeAddOp4(v, OP_String8, 0, 6, 0, zOnUpdate, 0); - sqlite3VdbeAddOp4(v, OP_String8, 0, 7, 0, zOnDelete, 0); - sqlite3VdbeAddOp4(v, OP_String8, 0, 8, 0, "NONE", 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 8); - } - ++i; - pFK = pFK->pNextFrom; - } - } - } - }else -#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ - -#ifndef SQLITE_OMIT_FOREIGN_KEY -#ifndef SQLITE_OMIT_TRIGGER - if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 ){ - FKey *pFK; /* A foreign key constraint */ - Table *pTab; /* Child table contain "REFERENCES" keyword */ - Table *pParent; /* Parent table that child points to */ - Index *pIdx; /* Index in the parent table */ - int i; /* Loop counter: Foreign key number for pTab */ - int j; /* Loop counter: Field of the foreign key */ - HashElem *k; /* Loop counter: Next table in schema */ - int x; /* result variable */ - int regResult; /* 3 registers to hold a result row */ - int regKey; /* Register to hold key for checking the FK */ - int regRow; /* Registers to hold a row from pTab */ - int addrTop; /* Top of a loop checking foreign keys */ - int addrOk; /* Jump here if the key is OK */ - int *aiCols; /* child to parent column mapping */ - - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - regResult = pParse->nMem+1; - pParse->nMem += 4; - regKey = ++pParse->nMem; - regRow = ++pParse->nMem; - v = sqlite3GetVdbe(pParse); - sqlite3VdbeSetNumCols(v, 4); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "parent", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "fkid", SQLITE_STATIC); - sqlite3CodeVerifySchema(pParse, iDb); - k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); - while( k ){ - if( zRight ){ - pTab = sqlite3LocateTable(pParse, 0, zRight, zDb); - k = 0; - }else{ - pTab = (Table*)sqliteHashData(k); - k = sqliteHashNext(k); - } - if( pTab==0 || pTab->pFKey==0 ) continue; - sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; - sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); - sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName, - P4_TRANSIENT); - for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ - pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); - if( pParent==0 ) break; - pIdx = 0; - sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName); - x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); - if( x==0 ){ - if( pIdx==0 ){ - sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead); - }else{ - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); - sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); - } - }else{ - k = 0; - break; - } - } - if( pFK ) break; - if( pParse->nTabnTab = i; - addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); - for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ - pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); - assert( pParent!=0 ); - pIdx = 0; - aiCols = 0; - x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); - assert( x==0 ); - addrOk = sqlite3VdbeMakeLabel(v); - if( pIdx==0 ){ - int iKey = pFK->aCol[0].iFrom; - assert( iKey>=0 && iKeynCol ); - if( iKey!=pTab->iPKey ){ - sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow); - sqlite3ColumnDefault(v, pTab, iKey, regRow); - sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); - sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow, - sqlite3VdbeCurrentAddr(v)+3); - }else{ - sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow); - } - sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk); - sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); - }else{ - for(j=0; jnCol; j++){ - sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, - aiCols ? aiCols[j] : pFK->aCol[0].iFrom, regRow+j); - sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); - } - sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey); - sqlite3VdbeChangeP4(v, -1, - sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); - sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); - } - sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1); - sqlite3VdbeAddOp4(v, OP_String8, 0, regResult+2, 0, - pFK->zTo, P4_TRANSIENT); - sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+3); - sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4); - sqlite3VdbeResolveLabel(v, addrOk); - sqlite3DbFree(db, aiCols); - } - sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); - sqlite3VdbeJumpHere(v, addrTop); - } - }else -#endif /* !defined(SQLITE_OMIT_TRIGGER) */ -#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ - -#ifndef NDEBUG - if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){ - if( zRight ){ - if( sqlite3GetBoolean(zRight, 0) ){ - sqlite3ParserTrace(stderr, "parser: "); - }else{ - sqlite3ParserTrace(0, 0); - } - } - }else -#endif - - /* Reinstall the LIKE and GLOB functions. The variant of LIKE - ** used will be case sensitive or not depending on the RHS. - */ - if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){ - if( zRight ){ - sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight, 0)); - } - }else - -#ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX -# define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100 -#endif - -#ifndef SQLITE_OMIT_INTEGRITY_CHECK - /* Pragma "quick_check" is an experimental reduced version of - ** integrity_check designed to detect most database corruption - ** without most of the overhead of a full integrity-check. - */ - if( sqlite3StrICmp(zLeft, "integrity_check")==0 - || sqlite3StrICmp(zLeft, "quick_check")==0 - ){ - int i, j, addr, mxErr; - - /* Code that appears at the end of the integrity check. If no error - ** messages have been generated, output OK. Otherwise output the - ** error message - */ - static const VdbeOpList endCode[] = { - { OP_AddImm, 1, 0, 0}, /* 0 */ - { OP_IfNeg, 1, 0, 0}, /* 1 */ - { OP_String8, 0, 3, 0}, /* 2 */ - { OP_ResultRow, 3, 1, 0}, - }; - - int isQuick = (sqlite3Tolower(zLeft[0])=='q'); - - /* If the PRAGMA command was of the form "PRAGMA .integrity_check", - ** then iDb is set to the index of the database identified by . - ** In this case, the integrity of database iDb only is verified by - ** the VDBE created below. - ** - ** Otherwise, if the command was simply "PRAGMA integrity_check" (or - ** "PRAGMA quick_check"), then iDb is set to 0. In this case, set iDb - ** to -1 here, to indicate that the VDBE should verify the integrity - ** of all attached databases. */ - assert( iDb>=0 ); - assert( iDb==0 || pId2->z ); - if( pId2->z==0 ) iDb = -1; - - /* Initialize the VDBE program */ - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pParse->nMem = 6; - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", SQLITE_STATIC); - - /* Set the maximum error count */ - mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; - if( zRight ){ - sqlite3GetInt32(zRight, &mxErr); - if( mxErr<=0 ){ - mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; - } - } - sqlite3VdbeAddOp2(v, OP_Integer, mxErr, 1); /* reg[1] holds errors left */ - - /* Do an integrity check on each database file */ - for(i=0; inDb; i++){ - HashElem *x; - Hash *pTbls; - int cnt = 0; - - if( OMIT_TEMPDB && i==1 ) continue; - if( iDb>=0 && i!=iDb ) continue; - - sqlite3CodeVerifySchema(pParse, i); - addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Halt if out of errors */ - sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); - sqlite3VdbeJumpHere(v, addr); - - /* Do an integrity check of the B-Tree - ** - ** Begin by filling registers 2, 3, ... with the root pages numbers - ** for all tables and indices in the database. - */ - assert( sqlite3SchemaMutexHeld(db, i, 0) ); - pTbls = &db->aDb[i].pSchema->tblHash; - for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ - Table *pTab = sqliteHashData(x); - Index *pIdx; - sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt); - cnt++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - sqlite3VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt); - cnt++; - } - } - - /* Make sure sufficient number of registers have been allocated */ - if( pParse->nMem < cnt+4 ){ - pParse->nMem = cnt+4; - } - - /* Do the b-tree integrity checks */ - sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1); - sqlite3VdbeChangeP5(v, (u8)i); - addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); - sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, - sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName), - P4_DYNAMIC); - sqlite3VdbeAddOp2(v, OP_Move, 2, 4); - sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 2); - sqlite3VdbeAddOp2(v, OP_ResultRow, 2, 1); - sqlite3VdbeJumpHere(v, addr); - - /* Make sure all the indices are constructed correctly. - */ - for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){ - Table *pTab = sqliteHashData(x); - Index *pIdx; - int loopTop; - - if( pTab->pIndex==0 ) continue; - addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */ - sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); - sqlite3VdbeJumpHere(v, addr); - sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead); - sqlite3VdbeAddOp2(v, OP_Integer, 0, 2); /* reg(2) will count entries */ - loopTop = sqlite3VdbeAddOp2(v, OP_Rewind, 1, 0); - sqlite3VdbeAddOp2(v, OP_AddImm, 2, 1); /* increment entry count */ - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - int jmp2; - int r1; - static const VdbeOpList idxErr[] = { - { OP_AddImm, 1, -1, 0}, - { OP_String8, 0, 3, 0}, /* 1 */ - { OP_Rowid, 1, 4, 0}, - { OP_String8, 0, 5, 0}, /* 3 */ - { OP_String8, 0, 6, 0}, /* 4 */ - { OP_Concat, 4, 3, 3}, - { OP_Concat, 5, 3, 3}, - { OP_Concat, 6, 3, 3}, - { OP_ResultRow, 3, 1, 0}, - { OP_IfPos, 1, 0, 0}, /* 9 */ - { OP_Halt, 0, 0, 0}, - }; - r1 = sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 0); - jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, j+2, 0, r1, pIdx->nColumn+1); - addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr); - sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC); - sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC); - sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT); - sqlite3VdbeJumpHere(v, addr+9); - sqlite3VdbeJumpHere(v, jmp2); - } - sqlite3VdbeAddOp2(v, OP_Next, 1, loopTop+1); - sqlite3VdbeJumpHere(v, loopTop); - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - static const VdbeOpList cntIdx[] = { - { OP_Integer, 0, 3, 0}, - { OP_Rewind, 0, 0, 0}, /* 1 */ - { OP_AddImm, 3, 1, 0}, - { OP_Next, 0, 0, 0}, /* 3 */ - { OP_Eq, 2, 0, 3}, /* 4 */ - { OP_AddImm, 1, -1, 0}, - { OP_String8, 0, 2, 0}, /* 6 */ - { OP_String8, 0, 3, 0}, /* 7 */ - { OP_Concat, 3, 2, 2}, - { OP_ResultRow, 2, 1, 0}, - }; - addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); - sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); - sqlite3VdbeJumpHere(v, addr); - addr = sqlite3VdbeAddOpList(v, ArraySize(cntIdx), cntIdx); - sqlite3VdbeChangeP1(v, addr+1, j+2); - sqlite3VdbeChangeP2(v, addr+1, addr+4); - sqlite3VdbeChangeP1(v, addr+3, j+2); - sqlite3VdbeChangeP2(v, addr+3, addr+2); - sqlite3VdbeJumpHere(v, addr+4); - sqlite3VdbeChangeP4(v, addr+6, - "wrong # of entries in index ", P4_STATIC); - sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_TRANSIENT); - } - } - } - addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode); - sqlite3VdbeChangeP2(v, addr, -mxErr); - sqlite3VdbeJumpHere(v, addr+1); - sqlite3VdbeChangeP4(v, addr+2, "ok", P4_STATIC); - }else -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -#ifndef SQLITE_OMIT_UTF16 - /* - ** PRAGMA encoding - ** PRAGMA encoding = "utf-8"|"utf-16"|"utf-16le"|"utf-16be" - ** - ** In its first form, this pragma returns the encoding of the main - ** database. If the database is not initialized, it is initialized now. - ** - ** The second form of this pragma is a no-op if the main database file - ** has not already been initialized. In this case it sets the default - ** encoding that will be used for the main database file if a new file - ** is created. If an existing main database file is opened, then the - ** default text encoding for the existing database is used. - ** - ** In all cases new databases created using the ATTACH command are - ** created to use the same default text encoding as the main database. If - ** the main database has not been initialized and/or created when ATTACH - ** is executed, this is done before the ATTACH operation. - ** - ** In the second form this pragma sets the text encoding to be used in - ** new database files created using this database handle. It is only - ** useful if invoked immediately after the main database i - */ - if( sqlite3StrICmp(zLeft, "encoding")==0 ){ - static const struct EncName { - char *zName; - u8 enc; - } encnames[] = { - { "UTF8", SQLITE_UTF8 }, - { "UTF-8", SQLITE_UTF8 }, /* Must be element [1] */ - { "UTF-16le", SQLITE_UTF16LE }, /* Must be element [2] */ - { "UTF-16be", SQLITE_UTF16BE }, /* Must be element [3] */ - { "UTF16le", SQLITE_UTF16LE }, - { "UTF16be", SQLITE_UTF16BE }, - { "UTF-16", 0 }, /* SQLITE_UTF16NATIVE */ - { "UTF16", 0 }, /* SQLITE_UTF16NATIVE */ - { 0, 0 } - }; - const struct EncName *pEnc; - if( !zRight ){ /* "PRAGMA encoding" */ - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "encoding", SQLITE_STATIC); - sqlite3VdbeAddOp2(v, OP_String8, 0, 1); - assert( encnames[SQLITE_UTF8].enc==SQLITE_UTF8 ); - assert( encnames[SQLITE_UTF16LE].enc==SQLITE_UTF16LE ); - assert( encnames[SQLITE_UTF16BE].enc==SQLITE_UTF16BE ); - sqlite3VdbeChangeP4(v, -1, encnames[ENC(pParse->db)].zName, P4_STATIC); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - }else{ /* "PRAGMA encoding = XXX" */ - /* Only change the value of sqlite.enc if the database handle is not - ** initialized. If the main database exists, the new sqlite.enc value - ** will be overwritten when the schema is next loaded. If it does not - ** already exists, it will be created to use the new encoding value. - */ - if( - !(DbHasProperty(db, 0, DB_SchemaLoaded)) || - DbHasProperty(db, 0, DB_Empty) - ){ - for(pEnc=&encnames[0]; pEnc->zName; pEnc++){ - if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){ - ENC(pParse->db) = pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE; - break; - } - } - if( !pEnc->zName ){ - sqlite3ErrorMsg(pParse, "unsupported encoding: %s", zRight); - } - } - } - }else -#endif /* SQLITE_OMIT_UTF16 */ - -#ifndef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS - /* - ** PRAGMA [database.]schema_version - ** PRAGMA [database.]schema_version = - ** - ** PRAGMA [database.]user_version - ** PRAGMA [database.]user_version = - ** - ** The pragma's schema_version and user_version are used to set or get - ** the value of the schema-version and user-version, respectively. Both - ** the schema-version and the user-version are 32-bit signed integers - ** stored in the database header. - ** - ** The schema-cookie is usually only manipulated internally by SQLite. It - ** is incremented by SQLite whenever the database schema is modified (by - ** creating or dropping a table or index). The schema version is used by - ** SQLite each time a query is executed to ensure that the internal cache - ** of the schema used when compiling the SQL query matches the schema of - ** the database against which the compiled query is actually executed. - ** Subverting this mechanism by using "PRAGMA schema_version" to modify - ** the schema-version is potentially dangerous and may lead to program - ** crashes or database corruption. Use with caution! - ** - ** The user-version is not used internally by SQLite. It may be used by - ** applications for any purpose. - */ - if( sqlite3StrICmp(zLeft, "schema_version")==0 - || sqlite3StrICmp(zLeft, "user_version")==0 - || sqlite3StrICmp(zLeft, "freelist_count")==0 - ){ - int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */ - sqlite3VdbeUsesBtree(v, iDb); - switch( zLeft[0] ){ - case 'f': case 'F': - iCookie = BTREE_FREE_PAGE_COUNT; - break; - case 's': case 'S': - iCookie = BTREE_SCHEMA_VERSION; - break; - default: - iCookie = BTREE_USER_VERSION; - break; - } - - if( zRight && iCookie!=BTREE_FREE_PAGE_COUNT ){ - /* Write the specified cookie value */ - static const VdbeOpList setCookie[] = { - { OP_Transaction, 0, 1, 0}, /* 0 */ - { OP_Integer, 0, 1, 0}, /* 1 */ - { OP_SetCookie, 0, 0, 1}, /* 2 */ - }; - int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie); - sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP1(v, addr+1, sqlite3Atoi(zRight)); - sqlite3VdbeChangeP1(v, addr+2, iDb); - sqlite3VdbeChangeP2(v, addr+2, iCookie); - }else{ - /* Read the specified cookie value */ - static const VdbeOpList readCookie[] = { - { OP_Transaction, 0, 0, 0}, /* 0 */ - { OP_ReadCookie, 0, 1, 0}, /* 1 */ - { OP_ResultRow, 1, 1, 0} - }; - int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie); - sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP1(v, addr+1, iDb); - sqlite3VdbeChangeP3(v, addr+1, iCookie); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT); - } - }else -#endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */ - -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS - /* - ** PRAGMA compile_options - ** - ** Return the names of all compile-time options used in this build, - ** one option per row. - */ - if( sqlite3StrICmp(zLeft, "compile_options")==0 ){ - int i = 0; - const char *zOpt; - sqlite3VdbeSetNumCols(v, 1); - pParse->nMem = 1; - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "compile_option", SQLITE_STATIC); - while( (zOpt = sqlite3_compileoption_get(i++))!=0 ){ - sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - } - }else -#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ - -#ifndef SQLITE_OMIT_WAL - /* - ** PRAGMA [database.]wal_checkpoint = passive|full|restart - ** - ** Checkpoint the database. - */ - if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){ - int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); - int eMode = SQLITE_CHECKPOINT_PASSIVE; - if( zRight ){ - if( sqlite3StrICmp(zRight, "full")==0 ){ - eMode = SQLITE_CHECKPOINT_FULL; - }else if( sqlite3StrICmp(zRight, "restart")==0 ){ - eMode = SQLITE_CHECKPOINT_RESTART; - } - } - if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeSetNumCols(v, 3); - pParse->nMem = 3; - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "log", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "checkpointed", SQLITE_STATIC); - - sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); - }else - - /* - ** PRAGMA wal_autocheckpoint - ** PRAGMA wal_autocheckpoint = N - ** - ** Configure a database connection to automatically checkpoint a database - ** after accumulating N frames in the log. Or query for the current value - ** of N. - */ - if( sqlite3StrICmp(zLeft, "wal_autocheckpoint")==0 ){ - if( zRight ){ - sqlite3_wal_autocheckpoint(db, sqlite3Atoi(zRight)); - } - returnSingleInt(pParse, "wal_autocheckpoint", - db->xWalCallback==sqlite3WalDefaultHook ? - SQLITE_PTR_TO_INT(db->pWalArg) : 0); - }else -#endif - - /* - ** PRAGMA shrink_memory - ** - ** This pragma attempts to free as much memory as possible from the - ** current database connection. - */ - if( sqlite3StrICmp(zLeft, "shrink_memory")==0 ){ - sqlite3_db_release_memory(db); - }else - - /* - ** PRAGMA busy_timeout - ** PRAGMA busy_timeout = N - ** - ** Call sqlite3_busy_timeout(db, N). Return the current timeout value - ** if one is set. If no busy handler or a different busy handler is set - ** then 0 is returned. Setting the busy_timeout to 0 or negative - ** disables the timeout. - */ - if( sqlite3StrICmp(zLeft, "busy_timeout")==0 ){ - if( zRight ){ - sqlite3_busy_timeout(db, sqlite3Atoi(zRight)); - } - returnSingleInt(pParse, "timeout", db->busyTimeout); - }else - -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - /* - ** Report the current state of file logs for all databases - */ - if( sqlite3StrICmp(zLeft, "lock_status")==0 ){ - static const char *const azLockName[] = { - "unlocked", "shared", "reserved", "pending", "exclusive" - }; - int i; - sqlite3VdbeSetNumCols(v, 2); - pParse->nMem = 2; - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "database", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "status", SQLITE_STATIC); - for(i=0; inDb; i++){ - Btree *pBt; - const char *zState = "unknown"; - int j; - if( db->aDb[i].zName==0 ) continue; - sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, db->aDb[i].zName, P4_STATIC); - pBt = db->aDb[i].pBt; - if( pBt==0 || sqlite3BtreePager(pBt)==0 ){ - zState = "closed"; - }else if( sqlite3_file_control(db, i ? db->aDb[i].zName : 0, - SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){ - zState = azLockName[j]; - } - sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, zState, P4_STATIC); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2); - } - - }else -#endif - -#ifdef SQLITE_HAS_CODEC - if( sqlite3StrICmp(zLeft, "key")==0 && zRight ){ - sqlite3_key(db, zRight, sqlite3Strlen30(zRight)); - }else - if( sqlite3StrICmp(zLeft, "rekey")==0 && zRight ){ - sqlite3_rekey(db, zRight, sqlite3Strlen30(zRight)); - }else - if( zRight && (sqlite3StrICmp(zLeft, "hexkey")==0 || - sqlite3StrICmp(zLeft, "hexrekey")==0) ){ - int i, h1, h2; - char zKey[40]; - for(i=0; (h1 = zRight[i])!=0 && (h2 = zRight[i+1])!=0; i+=2){ - h1 += 9*(1&(h1>>6)); - h2 += 9*(1&(h2>>6)); - zKey[i/2] = (h2 & 0x0f) | ((h1 & 0xf)<<4); - } - if( (zLeft[3] & 0xf)==0xb ){ - sqlite3_key(db, zKey, i/2); - }else{ - sqlite3_rekey(db, zKey, i/2); - } - }else -#endif -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) - if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){ -#ifdef SQLITE_HAS_CODEC - if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ - sqlite3_activate_see(&zRight[4]); - } -#endif -#ifdef SQLITE_ENABLE_CEROD - if( sqlite3StrNICmp(zRight, "cerod-", 6)==0 ){ - sqlite3_activate_cerod(&zRight[6]); - } -#endif - }else -#endif - - - {/* Empty ELSE clause */} - - /* - ** Reset the safety level, in case the fullfsync flag or synchronous - ** setting changed. - */ -#ifndef SQLITE_OMIT_PAGER_PRAGMAS - if( db->autoCommit ){ - sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level, - (db->flags&SQLITE_FullFSync)!=0, - (db->flags&SQLITE_CkptFullFSync)!=0); - } -#endif -pragma_out: - sqlite3DbFree(db, zLeft); - sqlite3DbFree(db, zRight); -} - -#endif /* SQLITE_OMIT_PRAGMA */ - -/************** End of pragma.c **********************************************/ -/************** Begin file prepare.c *****************************************/ -/* -** 2005 May 25 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the implementation of the sqlite3_prepare() -** interface, and routines that contribute to loading the database schema -** from disk. -*/ - -/* -** Fill the InitData structure with an error message that indicates -** that the database is corrupt. -*/ -static void corruptSchema( - InitData *pData, /* Initialization context */ - const char *zObj, /* Object being parsed at the point of error */ - const char *zExtra /* Error information */ -){ - sqlite3 *db = pData->db; - if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){ - if( zObj==0 ) zObj = "?"; - sqlite3SetString(pData->pzErrMsg, db, - "malformed database schema (%s)", zObj); - if( zExtra ){ - *pData->pzErrMsg = sqlite3MAppendf(db, *pData->pzErrMsg, - "%s - %s", *pData->pzErrMsg, zExtra); - } - } - pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT; -} - -/* -** This is the callback routine for the code that initializes the -** database. See sqlite3Init() below for additional information. -** This routine is also called from the OP_ParseSchema opcode of the VDBE. -** -** Each callback contains the following information: -** -** argv[0] = name of thing being created -** argv[1] = root page number for table or index. 0 for trigger or view. -** argv[2] = SQL text for the CREATE statement. -** -*/ -SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ - InitData *pData = (InitData*)pInit; - sqlite3 *db = pData->db; - int iDb = pData->iDb; - - assert( argc==3 ); - UNUSED_PARAMETER2(NotUsed, argc); - assert( sqlite3_mutex_held(db->mutex) ); - DbClearProperty(db, iDb, DB_Empty); - if( db->mallocFailed ){ - corruptSchema(pData, argv[0], 0); - return 1; - } - - assert( iDb>=0 && iDbnDb ); - if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ - if( argv[1]==0 ){ - corruptSchema(pData, argv[0], 0); - }else if( argv[2] && argv[2][0] ){ - /* Call the parser to process a CREATE TABLE, INDEX or VIEW. - ** But because db->init.busy is set to 1, no VDBE code is generated - ** or executed. All the parser does is build the internal data - ** structures that describe the table, index, or view. - */ - int rc; - sqlite3_stmt *pStmt; - TESTONLY(int rcp); /* Return code from sqlite3_prepare() */ - - assert( db->init.busy ); - db->init.iDb = iDb; - db->init.newTnum = sqlite3Atoi(argv[1]); - db->init.orphanTrigger = 0; - TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0); - rc = db->errCode; - assert( (rc&0xFF)==(rcp&0xFF) ); - db->init.iDb = 0; - if( SQLITE_OK!=rc ){ - if( db->init.orphanTrigger ){ - assert( iDb==1 ); - }else{ - pData->rc = rc; - if( rc==SQLITE_NOMEM ){ - db->mallocFailed = 1; - }else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){ - corruptSchema(pData, argv[0], sqlite3_errmsg(db)); - } - } - } - sqlite3_finalize(pStmt); - }else if( argv[0]==0 ){ - corruptSchema(pData, 0, 0); - }else{ - /* If the SQL column is blank it means this is an index that - ** was created to be the PRIMARY KEY or to fulfill a UNIQUE - ** constraint for a CREATE TABLE. The index should have already - ** been created when we processed the CREATE TABLE. All we have - ** to do here is record the root page number for that index. - */ - Index *pIndex; - pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zName); - if( pIndex==0 ){ - /* This can occur if there exists an index on a TEMP table which - ** has the same name as another index on a permanent index. Since - ** the permanent table is hidden by the TEMP table, we can also - ** safely ignore the index on the permanent table. - */ - /* Do Nothing */; - }else if( sqlite3GetInt32(argv[1], &pIndex->tnum)==0 ){ - corruptSchema(pData, argv[0], "invalid rootpage"); - } - } - return 0; -} - -/* -** Attempt to read the database schema and initialize internal -** data structures for a single database file. The index of the -** database file is given by iDb. iDb==0 is used for the main -** database. iDb==1 should never be used. iDb>=2 is used for -** auxiliary databases. Return one of the SQLITE_ error codes to -** indicate success or failure. -*/ -static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ - int rc; - int i; -#ifndef SQLITE_OMIT_DEPRECATED - int size; -#endif - Table *pTab; - Db *pDb; - char const *azArg[4]; - int meta[5]; - InitData initData; - char const *zMasterSchema; - char const *zMasterName; - int openedTransaction = 0; - - /* - ** The master database table has a structure like this - */ - static const char master_schema[] = - "CREATE TABLE sqlite_master(\n" - " type text,\n" - " name text,\n" - " tbl_name text,\n" - " rootpage integer,\n" - " sql text\n" - ")" - ; -#ifndef SQLITE_OMIT_TEMPDB - static const char temp_master_schema[] = - "CREATE TEMP TABLE sqlite_temp_master(\n" - " type text,\n" - " name text,\n" - " tbl_name text,\n" - " rootpage integer,\n" - " sql text\n" - ")" - ; -#else - #define temp_master_schema 0 -#endif - - assert( iDb>=0 && iDbnDb ); - assert( db->aDb[iDb].pSchema ); - assert( sqlite3_mutex_held(db->mutex) ); - assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); - - /* zMasterSchema and zInitScript are set to point at the master schema - ** and initialisation script appropriate for the database being - ** initialized. zMasterName is the name of the master table. - */ - if( !OMIT_TEMPDB && iDb==1 ){ - zMasterSchema = temp_master_schema; - }else{ - zMasterSchema = master_schema; - } - zMasterName = SCHEMA_TABLE(iDb); - - /* Construct the schema tables. */ - azArg[0] = zMasterName; - azArg[1] = "1"; - azArg[2] = zMasterSchema; - azArg[3] = 0; - initData.db = db; - initData.iDb = iDb; - initData.rc = SQLITE_OK; - initData.pzErrMsg = pzErrMsg; - sqlite3InitCallback(&initData, 3, (char **)azArg, 0); - if( initData.rc ){ - rc = initData.rc; - goto error_out; - } - pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName); - if( ALWAYS(pTab) ){ - pTab->tabFlags |= TF_Readonly; - } - - /* Create a cursor to hold the database open - */ - pDb = &db->aDb[iDb]; - if( pDb->pBt==0 ){ - if( !OMIT_TEMPDB && ALWAYS(iDb==1) ){ - DbSetProperty(db, 1, DB_SchemaLoaded); - } - return SQLITE_OK; - } - - /* If there is not already a read-only (or read-write) transaction opened - ** on the b-tree database, open one now. If a transaction is opened, it - ** will be closed before this function returns. */ - sqlite3BtreeEnter(pDb->pBt); - if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){ - rc = sqlite3BtreeBeginTrans(pDb->pBt, 0); - if( rc!=SQLITE_OK ){ - sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc)); - goto initone_error_out; - } - openedTransaction = 1; - } - - /* Get the database meta information. - ** - ** Meta values are as follows: - ** meta[0] Schema cookie. Changes with each schema change. - ** meta[1] File format of schema layer. - ** meta[2] Size of the page cache. - ** meta[3] Largest rootpage (auto/incr_vacuum mode) - ** meta[4] Db text encoding. 1:UTF-8 2:UTF-16LE 3:UTF-16BE - ** meta[5] User version - ** meta[6] Incremental vacuum mode - ** meta[7] unused - ** meta[8] unused - ** meta[9] unused - ** - ** Note: The #defined SQLITE_UTF* symbols in sqliteInt.h correspond to - ** the possible values of meta[4]. - */ - for(i=0; ipBt, i+1, (u32 *)&meta[i]); - } - pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1]; - - /* If opening a non-empty database, check the text encoding. For the - ** main database, set sqlite3.enc to the encoding of the main database. - ** For an attached db, it is an error if the encoding is not the same - ** as sqlite3.enc. - */ - if( meta[BTREE_TEXT_ENCODING-1] ){ /* text encoding */ - if( iDb==0 ){ -#ifndef SQLITE_OMIT_UTF16 - u8 encoding; - /* If opening the main database, set ENC(db). */ - encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; - if( encoding==0 ) encoding = SQLITE_UTF8; - ENC(db) = encoding; -#else - ENC(db) = SQLITE_UTF8; -#endif - }else{ - /* If opening an attached database, the encoding much match ENC(db) */ - if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){ - sqlite3SetString(pzErrMsg, db, "attached databases must use the same" - " text encoding as main database"); - rc = SQLITE_ERROR; - goto initone_error_out; - } - } - }else{ - DbSetProperty(db, iDb, DB_Empty); - } - pDb->pSchema->enc = ENC(db); - - if( pDb->pSchema->cache_size==0 ){ -#ifndef SQLITE_OMIT_DEPRECATED - size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]); - if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; } - pDb->pSchema->cache_size = size; -#else - pDb->pSchema->cache_size = SQLITE_DEFAULT_CACHE_SIZE; -#endif - sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); - } - - /* - ** file_format==1 Version 3.0.0. - ** file_format==2 Version 3.1.3. // ALTER TABLE ADD COLUMN - ** file_format==3 Version 3.1.4. // ditto but with non-NULL defaults - ** file_format==4 Version 3.3.0. // DESC indices. Boolean constants - */ - pDb->pSchema->file_format = (u8)meta[BTREE_FILE_FORMAT-1]; - if( pDb->pSchema->file_format==0 ){ - pDb->pSchema->file_format = 1; - } - if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){ - sqlite3SetString(pzErrMsg, db, "unsupported file format"); - rc = SQLITE_ERROR; - goto initone_error_out; - } - - /* Ticket #2804: When we open a database in the newer file format, - ** clear the legacy_file_format pragma flag so that a VACUUM will - ** not downgrade the database and thus invalidate any descending - ** indices that the user might have created. - */ - if( iDb==0 && meta[BTREE_FILE_FORMAT-1]>=4 ){ - db->flags &= ~SQLITE_LegacyFileFmt; - } - - /* Read the schema information out of the schema tables - */ - assert( db->init.busy ); - { - char *zSql; - zSql = sqlite3MPrintf(db, - "SELECT name, rootpage, sql FROM '%q'.%s ORDER BY rowid", - db->aDb[iDb].zName, zMasterName); -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); - xAuth = db->xAuth; - db->xAuth = 0; -#endif - rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); -#ifndef SQLITE_OMIT_AUTHORIZATION - db->xAuth = xAuth; - } -#endif - if( rc==SQLITE_OK ) rc = initData.rc; - sqlite3DbFree(db, zSql); -#ifndef SQLITE_OMIT_ANALYZE - if( rc==SQLITE_OK ){ - sqlite3AnalysisLoad(db, iDb); - } -#endif - } - if( db->mallocFailed ){ - rc = SQLITE_NOMEM; - sqlite3ResetAllSchemasOfConnection(db); - } - if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ - /* Black magic: If the SQLITE_RecoveryMode flag is set, then consider - ** the schema loaded, even if errors occurred. In this situation the - ** current sqlite3_prepare() operation will fail, but the following one - ** will attempt to compile the supplied statement against whatever subset - ** of the schema was loaded before the error occurred. The primary - ** purpose of this is to allow access to the sqlite_master table - ** even when its contents have been corrupted. - */ - DbSetProperty(db, iDb, DB_SchemaLoaded); - rc = SQLITE_OK; - } - - /* Jump here for an error that occurs after successfully allocating - ** curMain and calling sqlite3BtreeEnter(). For an error that occurs - ** before that point, jump to error_out. - */ -initone_error_out: - if( openedTransaction ){ - sqlite3BtreeCommit(pDb->pBt); - } - sqlite3BtreeLeave(pDb->pBt); - -error_out: - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ - db->mallocFailed = 1; - } - return rc; -} - -/* -** Initialize all database files - the main database file, the file -** used to store temporary tables, and any additional database files -** created using ATTACH statements. Return a success code. If an -** error occurs, write an error message into *pzErrMsg. -** -** After a database is initialized, the DB_SchemaLoaded bit is set -** bit is set in the flags field of the Db structure. If the database -** file was of zero-length, then the DB_Empty flag is also set. -*/ -SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){ - int i, rc; - int commit_internal = !(db->flags&SQLITE_InternChanges); - - assert( sqlite3_mutex_held(db->mutex) ); - rc = SQLITE_OK; - db->init.busy = 1; - for(i=0; rc==SQLITE_OK && inDb; i++){ - if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue; - rc = sqlite3InitOne(db, i, pzErrMsg); - if( rc ){ - sqlite3ResetOneSchema(db, i); - } - } - - /* Once all the other databases have been initialized, load the schema - ** for the TEMP database. This is loaded last, as the TEMP database - ** schema may contain references to objects in other databases. - */ -#ifndef SQLITE_OMIT_TEMPDB - if( rc==SQLITE_OK && ALWAYS(db->nDb>1) - && !DbHasProperty(db, 1, DB_SchemaLoaded) ){ - rc = sqlite3InitOne(db, 1, pzErrMsg); - if( rc ){ - sqlite3ResetOneSchema(db, 1); - } - } -#endif - - db->init.busy = 0; - if( rc==SQLITE_OK && commit_internal ){ - sqlite3CommitInternalChanges(db); - } - - return rc; -} - -/* -** This routine is a no-op if the database schema is already initialized. -** Otherwise, the schema is loaded. An error code is returned. -*/ -SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse){ - int rc = SQLITE_OK; - sqlite3 *db = pParse->db; - assert( sqlite3_mutex_held(db->mutex) ); - if( !db->init.busy ){ - rc = sqlite3Init(db, &pParse->zErrMsg); - } - if( rc!=SQLITE_OK ){ - pParse->rc = rc; - pParse->nErr++; - } - return rc; -} - - -/* -** Check schema cookies in all databases. If any cookie is out -** of date set pParse->rc to SQLITE_SCHEMA. If all schema cookies -** make no changes to pParse->rc. -*/ -static void schemaIsValid(Parse *pParse){ - sqlite3 *db = pParse->db; - int iDb; - int rc; - int cookie; - - assert( pParse->checkSchema ); - assert( sqlite3_mutex_held(db->mutex) ); - for(iDb=0; iDbnDb; iDb++){ - int openedTransaction = 0; /* True if a transaction is opened */ - Btree *pBt = db->aDb[iDb].pBt; /* Btree database to read cookie from */ - if( pBt==0 ) continue; - - /* If there is not already a read-only (or read-write) transaction opened - ** on the b-tree database, open one now. If a transaction is opened, it - ** will be closed immediately after reading the meta-value. */ - if( !sqlite3BtreeIsInReadTrans(pBt) ){ - rc = sqlite3BtreeBeginTrans(pBt, 0); - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ - db->mallocFailed = 1; - } - if( rc!=SQLITE_OK ) return; - openedTransaction = 1; - } - - /* Read the schema cookie from the database. If it does not match the - ** value stored as part of the in-memory schema representation, - ** set Parse.rc to SQLITE_SCHEMA. */ - sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ - sqlite3ResetOneSchema(db, iDb); - pParse->rc = SQLITE_SCHEMA; - } - - /* Close the transaction, if one was opened. */ - if( openedTransaction ){ - sqlite3BtreeCommit(pBt); - } - } -} - -/* -** Convert a schema pointer into the iDb index that indicates -** which database file in db->aDb[] the schema refers to. -** -** If the same database is attached more than once, the first -** attached database is returned. -*/ -SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ - int i = -1000000; - - /* If pSchema is NULL, then return -1000000. This happens when code in - ** expr.c is trying to resolve a reference to a transient table (i.e. one - ** created by a sub-select). In this case the return value of this - ** function should never be used. - ** - ** We return -1000000 instead of the more usual -1 simply because using - ** -1000000 as the incorrect index into db->aDb[] is much - ** more likely to cause a segfault than -1 (of course there are assert() - ** statements too, but it never hurts to play the odds). - */ - assert( sqlite3_mutex_held(db->mutex) ); - if( pSchema ){ - for(i=0; ALWAYS(inDb); i++){ - if( db->aDb[i].pSchema==pSchema ){ - break; - } - } - assert( i>=0 && inDb ); - } - return i; -} - -/* -** Compile the UTF-8 encoded SQL statement zSql into a statement handle. -*/ -static int sqlite3Prepare( - sqlite3 *db, /* Database handle. */ - const char *zSql, /* UTF-8 encoded SQL statement. */ - int nBytes, /* Length of zSql in bytes. */ - int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */ - Vdbe *pReprepare, /* VM being reprepared */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const char **pzTail /* OUT: End of parsed string */ -){ - Parse *pParse; /* Parsing context */ - char *zErrMsg = 0; /* Error message */ - int rc = SQLITE_OK; /* Result code */ - int i; /* Loop counter */ - - /* Allocate the parsing context */ - pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); - if( pParse==0 ){ - rc = SQLITE_NOMEM; - goto end_prepare; - } - pParse->pReprepare = pReprepare; - assert( ppStmt && *ppStmt==0 ); - assert( !db->mallocFailed ); - assert( sqlite3_mutex_held(db->mutex) ); - - /* Check to verify that it is possible to get a read lock on all - ** database schemas. The inability to get a read lock indicates that - ** some other database connection is holding a write-lock, which in - ** turn means that the other connection has made uncommitted changes - ** to the schema. - ** - ** Were we to proceed and prepare the statement against the uncommitted - ** schema changes and if those schema changes are subsequently rolled - ** back and different changes are made in their place, then when this - ** prepared statement goes to run the schema cookie would fail to detect - ** the schema change. Disaster would follow. - ** - ** This thread is currently holding mutexes on all Btrees (because - ** of the sqlite3BtreeEnterAll() in sqlite3LockAndPrepare()) so it - ** is not possible for another thread to start a new schema change - ** while this routine is running. Hence, we do not need to hold - ** locks on the schema, we just need to make sure nobody else is - ** holding them. - ** - ** Note that setting READ_UNCOMMITTED overrides most lock detection, - ** but it does *not* override schema lock detection, so this all still - ** works even if READ_UNCOMMITTED is set. - */ - for(i=0; inDb; i++) { - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - assert( sqlite3BtreeHoldsMutex(pBt) ); - rc = sqlite3BtreeSchemaLocked(pBt); - if( rc ){ - const char *zDb = db->aDb[i].zName; - sqlite3Error(db, rc, "database schema is locked: %s", zDb); - testcase( db->flags & SQLITE_ReadUncommitted ); - goto end_prepare; - } - } - } - - sqlite3VtabUnlockList(db); - - pParse->db = db; - pParse->nQueryLoop = (double)1; - if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ - char *zSqlCopy; - int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; - testcase( nBytes==mxLen ); - testcase( nBytes==mxLen+1 ); - if( nBytes>mxLen ){ - sqlite3Error(db, SQLITE_TOOBIG, "statement too long"); - rc = sqlite3ApiExit(db, SQLITE_TOOBIG); - goto end_prepare; - } - zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes); - if( zSqlCopy ){ - sqlite3RunParser(pParse, zSqlCopy, &zErrMsg); - sqlite3DbFree(db, zSqlCopy); - pParse->zTail = &zSql[pParse->zTail-zSqlCopy]; - }else{ - pParse->zTail = &zSql[nBytes]; - } - }else{ - sqlite3RunParser(pParse, zSql, &zErrMsg); - } - assert( 1==(int)pParse->nQueryLoop ); - - if( db->mallocFailed ){ - pParse->rc = SQLITE_NOMEM; - } - if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK; - if( pParse->checkSchema ){ - schemaIsValid(pParse); - } - if( db->mallocFailed ){ - pParse->rc = SQLITE_NOMEM; - } - if( pzTail ){ - *pzTail = pParse->zTail; - } - rc = pParse->rc; - -#ifndef SQLITE_OMIT_EXPLAIN - if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){ - static const char * const azColName[] = { - "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", - "selectid", "order", "from", "detail" - }; - int iFirst, mx; - if( pParse->explain==2 ){ - sqlite3VdbeSetNumCols(pParse->pVdbe, 4); - iFirst = 8; - mx = 12; - }else{ - sqlite3VdbeSetNumCols(pParse->pVdbe, 8); - iFirst = 0; - mx = 8; - } - for(i=iFirst; ipVdbe, i-iFirst, COLNAME_NAME, - azColName[i], SQLITE_STATIC); - } - } -#endif - - assert( db->init.busy==0 || saveSqlFlag==0 ); - if( db->init.busy==0 ){ - Vdbe *pVdbe = pParse->pVdbe; - sqlite3VdbeSetSql(pVdbe, zSql, (int)(pParse->zTail-zSql), saveSqlFlag); - } - if( pParse->pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){ - sqlite3VdbeFinalize(pParse->pVdbe); - assert(!(*ppStmt)); - }else{ - *ppStmt = (sqlite3_stmt*)pParse->pVdbe; - } - - if( zErrMsg ){ - sqlite3Error(db, rc, "%s", zErrMsg); - sqlite3DbFree(db, zErrMsg); - }else{ - sqlite3Error(db, rc, 0); - } - - /* Delete any TriggerPrg structures allocated while parsing this statement. */ - while( pParse->pTriggerPrg ){ - TriggerPrg *pT = pParse->pTriggerPrg; - pParse->pTriggerPrg = pT->pNext; - sqlite3DbFree(db, pT); - } - -end_prepare: - - sqlite3StackFree(db, pParse); - rc = sqlite3ApiExit(db, rc); - assert( (rc&db->errMask)==rc ); - return rc; -} -static int sqlite3LockAndPrepare( - sqlite3 *db, /* Database handle. */ - const char *zSql, /* UTF-8 encoded SQL statement. */ - int nBytes, /* Length of zSql in bytes. */ - int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */ - Vdbe *pOld, /* VM being reprepared */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const char **pzTail /* OUT: End of parsed string */ -){ - int rc; - assert( ppStmt!=0 ); - *ppStmt = 0; - if( !sqlite3SafetyCheckOk(db) ){ - return SQLITE_MISUSE_BKPT; - } - sqlite3_mutex_enter(db->mutex); - sqlite3BtreeEnterAll(db); - rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); - if( rc==SQLITE_SCHEMA ){ - sqlite3_finalize(*ppStmt); - rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); - } - sqlite3BtreeLeaveAll(db); - sqlite3_mutex_leave(db->mutex); - assert( rc==SQLITE_OK || *ppStmt==0 ); - return rc; -} - -/* -** Rerun the compilation of a statement after a schema change. -** -** If the statement is successfully recompiled, return SQLITE_OK. Otherwise, -** if the statement cannot be recompiled because another connection has -** locked the sqlite3_master table, return SQLITE_LOCKED. If any other error -** occurs, return SQLITE_SCHEMA. -*/ -SQLITE_PRIVATE int sqlite3Reprepare(Vdbe *p){ - int rc; - sqlite3_stmt *pNew; - const char *zSql; - sqlite3 *db; - - assert( sqlite3_mutex_held(sqlite3VdbeDb(p)->mutex) ); - zSql = sqlite3_sql((sqlite3_stmt *)p); - assert( zSql!=0 ); /* Reprepare only called for prepare_v2() statements */ - db = sqlite3VdbeDb(p); - assert( sqlite3_mutex_held(db->mutex) ); - rc = sqlite3LockAndPrepare(db, zSql, -1, 0, p, &pNew, 0); - if( rc ){ - if( rc==SQLITE_NOMEM ){ - db->mallocFailed = 1; - } - assert( pNew==0 ); - return rc; - }else{ - assert( pNew!=0 ); - } - sqlite3VdbeSwap((Vdbe*)pNew, p); - sqlite3TransferBindings(pNew, (sqlite3_stmt*)p); - sqlite3VdbeResetStepResult((Vdbe*)pNew); - sqlite3VdbeFinalize((Vdbe*)pNew); - return SQLITE_OK; -} - - -/* -** Two versions of the official API. Legacy and new use. In the legacy -** version, the original SQL text is not saved in the prepared statement -** and so if a schema change occurs, SQLITE_SCHEMA is returned by -** sqlite3_step(). In the new version, the original SQL text is retained -** and the statement is automatically recompiled if an schema change -** occurs. -*/ -SQLITE_API int sqlite3_prepare( - sqlite3 *db, /* Database handle. */ - const char *zSql, /* UTF-8 encoded SQL statement. */ - int nBytes, /* Length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const char **pzTail /* OUT: End of parsed string */ -){ - int rc; - rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,0,ppStmt,pzTail); - assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ - return rc; -} -SQLITE_API int sqlite3_prepare_v2( - sqlite3 *db, /* Database handle. */ - const char *zSql, /* UTF-8 encoded SQL statement. */ - int nBytes, /* Length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const char **pzTail /* OUT: End of parsed string */ -){ - int rc; - rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail); - assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ - return rc; -} - - -#ifndef SQLITE_OMIT_UTF16 -/* -** Compile the UTF-16 encoded SQL statement zSql into a statement handle. -*/ -static int sqlite3Prepare16( - sqlite3 *db, /* Database handle. */ - const void *zSql, /* UTF-16 encoded SQL statement. */ - int nBytes, /* Length of zSql in bytes. */ - int saveSqlFlag, /* True to save SQL text into the sqlite3_stmt */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const void **pzTail /* OUT: End of parsed string */ -){ - /* This function currently works by first transforming the UTF-16 - ** encoded string to UTF-8, then invoking sqlite3_prepare(). The - ** tricky bit is figuring out the pointer to return in *pzTail. - */ - char *zSql8; - const char *zTail8 = 0; - int rc = SQLITE_OK; - - assert( ppStmt ); - *ppStmt = 0; - if( !sqlite3SafetyCheckOk(db) ){ - return SQLITE_MISUSE_BKPT; - } - sqlite3_mutex_enter(db->mutex); - zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE); - if( zSql8 ){ - rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, 0, ppStmt, &zTail8); - } - - if( zTail8 && pzTail ){ - /* If sqlite3_prepare returns a tail pointer, we calculate the - ** equivalent pointer into the UTF-16 string by counting the unicode - ** characters between zSql8 and zTail8, and then returning a pointer - ** the same number of characters into the UTF-16 string. - */ - int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8)); - *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, chars_parsed); - } - sqlite3DbFree(db, zSql8); - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/* -** Two versions of the official API. Legacy and new use. In the legacy -** version, the original SQL text is not saved in the prepared statement -** and so if a schema change occurs, SQLITE_SCHEMA is returned by -** sqlite3_step(). In the new version, the original SQL text is retained -** and the statement is automatically recompiled if an schema change -** occurs. -*/ -SQLITE_API int sqlite3_prepare16( - sqlite3 *db, /* Database handle. */ - const void *zSql, /* UTF-16 encoded SQL statement. */ - int nBytes, /* Length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const void **pzTail /* OUT: End of parsed string */ -){ - int rc; - rc = sqlite3Prepare16(db,zSql,nBytes,0,ppStmt,pzTail); - assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ - return rc; -} -SQLITE_API int sqlite3_prepare16_v2( - sqlite3 *db, /* Database handle. */ - const void *zSql, /* UTF-16 encoded SQL statement. */ - int nBytes, /* Length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const void **pzTail /* OUT: End of parsed string */ -){ - int rc; - rc = sqlite3Prepare16(db,zSql,nBytes,1,ppStmt,pzTail); - assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ - return rc; -} - -#endif /* SQLITE_OMIT_UTF16 */ - -/************** End of prepare.c *********************************************/ -/************** Begin file select.c ******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the parser -** to handle SELECT statements in SQLite. -*/ - - -/* -** Delete all the content of a Select structure but do not deallocate -** the select structure itself. -*/ -static void clearSelect(sqlite3 *db, Select *p){ - sqlite3ExprListDelete(db, p->pEList); - sqlite3SrcListDelete(db, p->pSrc); - sqlite3ExprDelete(db, p->pWhere); - sqlite3ExprListDelete(db, p->pGroupBy); - sqlite3ExprDelete(db, p->pHaving); - sqlite3ExprListDelete(db, p->pOrderBy); - sqlite3SelectDelete(db, p->pPrior); - sqlite3ExprDelete(db, p->pLimit); - sqlite3ExprDelete(db, p->pOffset); -} - -/* -** Initialize a SelectDest structure. -*/ -SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){ - pDest->eDest = (u8)eDest; - pDest->iSDParm = iParm; - pDest->affSdst = 0; - pDest->iSdst = 0; - pDest->nSdst = 0; -} - - -/* -** Allocate a new Select structure and return a pointer to that -** structure. -*/ -SQLITE_PRIVATE Select *sqlite3SelectNew( - Parse *pParse, /* Parsing context */ - ExprList *pEList, /* which columns to include in the result */ - SrcList *pSrc, /* the FROM clause -- which tables to scan */ - Expr *pWhere, /* the WHERE clause */ - ExprList *pGroupBy, /* the GROUP BY clause */ - Expr *pHaving, /* the HAVING clause */ - ExprList *pOrderBy, /* the ORDER BY clause */ - u16 selFlags, /* Flag parameters, such as SF_Distinct */ - Expr *pLimit, /* LIMIT value. NULL means not used */ - Expr *pOffset /* OFFSET value. NULL means no offset */ -){ - Select *pNew; - Select standin; - sqlite3 *db = pParse->db; - pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); - assert( db->mallocFailed || !pOffset || pLimit ); /* OFFSET implies LIMIT */ - if( pNew==0 ){ - assert( db->mallocFailed ); - pNew = &standin; - memset(pNew, 0, sizeof(*pNew)); - } - if( pEList==0 ){ - pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0)); - } - pNew->pEList = pEList; - if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc)); - pNew->pSrc = pSrc; - pNew->pWhere = pWhere; - pNew->pGroupBy = pGroupBy; - pNew->pHaving = pHaving; - pNew->pOrderBy = pOrderBy; - pNew->selFlags = selFlags; - pNew->op = TK_SELECT; - pNew->pLimit = pLimit; - pNew->pOffset = pOffset; - assert( pOffset==0 || pLimit!=0 ); - pNew->addrOpenEphm[0] = -1; - pNew->addrOpenEphm[1] = -1; - pNew->addrOpenEphm[2] = -1; - if( db->mallocFailed ) { - clearSelect(db, pNew); - if( pNew!=&standin ) sqlite3DbFree(db, pNew); - pNew = 0; - }else{ - assert( pNew->pSrc!=0 || pParse->nErr>0 ); - } - assert( pNew!=&standin ); - return pNew; -} - -/* -** Delete the given Select structure and all of its substructures. -*/ -SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){ - if( p ){ - clearSelect(db, p); - sqlite3DbFree(db, p); - } -} - -/* -** Given 1 to 3 identifiers preceeding the JOIN keyword, determine the -** type of join. Return an integer constant that expresses that type -** in terms of the following bit values: -** -** JT_INNER -** JT_CROSS -** JT_OUTER -** JT_NATURAL -** JT_LEFT -** JT_RIGHT -** -** A full outer join is the combination of JT_LEFT and JT_RIGHT. -** -** If an illegal or unsupported join type is seen, then still return -** a join type, but put an error in the pParse structure. -*/ -SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){ - int jointype = 0; - Token *apAll[3]; - Token *p; - /* 0123456789 123456789 123456789 123 */ - static const char zKeyText[] = "naturaleftouterightfullinnercross"; - static const struct { - u8 i; /* Beginning of keyword text in zKeyText[] */ - u8 nChar; /* Length of the keyword in characters */ - u8 code; /* Join type mask */ - } aKeyword[] = { - /* natural */ { 0, 7, JT_NATURAL }, - /* left */ { 6, 4, JT_LEFT|JT_OUTER }, - /* outer */ { 10, 5, JT_OUTER }, - /* right */ { 14, 5, JT_RIGHT|JT_OUTER }, - /* full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER }, - /* inner */ { 23, 5, JT_INNER }, - /* cross */ { 28, 5, JT_INNER|JT_CROSS }, - }; - int i, j; - apAll[0] = pA; - apAll[1] = pB; - apAll[2] = pC; - for(i=0; i<3 && apAll[i]; i++){ - p = apAll[i]; - for(j=0; jn==aKeyword[j].nChar - && sqlite3StrNICmp((char*)p->z, &zKeyText[aKeyword[j].i], p->n)==0 ){ - jointype |= aKeyword[j].code; - break; - } - } - testcase( j==0 || j==1 || j==2 || j==3 || j==4 || j==5 || j==6 ); - if( j>=ArraySize(aKeyword) ){ - jointype |= JT_ERROR; - break; - } - } - if( - (jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) || - (jointype & JT_ERROR)!=0 - ){ - const char *zSp = " "; - assert( pB!=0 ); - if( pC==0 ){ zSp++; } - sqlite3ErrorMsg(pParse, "unknown or unsupported join type: " - "%T %T%s%T", pA, pB, zSp, pC); - jointype = JT_INNER; - }else if( (jointype & JT_OUTER)!=0 - && (jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ){ - sqlite3ErrorMsg(pParse, - "RIGHT and FULL OUTER JOINs are not currently supported"); - jointype = JT_INNER; - } - return jointype; -} - -/* -** Return the index of a column in a table. Return -1 if the column -** is not contained in the table. -*/ -static int columnIndex(Table *pTab, const char *zCol){ - int i; - for(i=0; inCol; i++){ - if( sqlite3StrICmp(pTab->aCol[i].zName, zCol)==0 ) return i; - } - return -1; -} - -/* -** Search the first N tables in pSrc, from left to right, looking for a -** table that has a column named zCol. -** -** When found, set *piTab and *piCol to the table index and column index -** of the matching column and return TRUE. -** -** If not found, return FALSE. -*/ -static int tableAndColumnIndex( - SrcList *pSrc, /* Array of tables to search */ - int N, /* Number of tables in pSrc->a[] to search */ - const char *zCol, /* Name of the column we are looking for */ - int *piTab, /* Write index of pSrc->a[] here */ - int *piCol /* Write index of pSrc->a[*piTab].pTab->aCol[] here */ -){ - int i; /* For looping over tables in pSrc */ - int iCol; /* Index of column matching zCol */ - - assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ - for(i=0; ia[i].pTab, zCol); - if( iCol>=0 ){ - if( piTab ){ - *piTab = i; - *piCol = iCol; - } - return 1; - } - } - return 0; -} - -/* -** This function is used to add terms implied by JOIN syntax to the -** WHERE clause expression of a SELECT statement. The new term, which -** is ANDed with the existing WHERE clause, is of the form: -** -** (tab1.col1 = tab2.col2) -** -** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the -** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is -** column iColRight of tab2. -*/ -static void addWhereTerm( - Parse *pParse, /* Parsing context */ - SrcList *pSrc, /* List of tables in FROM clause */ - int iLeft, /* Index of first table to join in pSrc */ - int iColLeft, /* Index of column in first table */ - int iRight, /* Index of second table in pSrc */ - int iColRight, /* Index of column in second table */ - int isOuterJoin, /* True if this is an OUTER join */ - Expr **ppWhere /* IN/OUT: The WHERE clause to add to */ -){ - sqlite3 *db = pParse->db; - Expr *pE1; - Expr *pE2; - Expr *pEq; - - assert( iLeftnSrc>iRight ); - assert( pSrc->a[iLeft].pTab ); - assert( pSrc->a[iRight].pTab ); - - pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft); - pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); - - pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2, 0); - if( pEq && isOuterJoin ){ - ExprSetProperty(pEq, EP_FromJoin); - assert( !ExprHasAnyProperty(pEq, EP_TokenOnly|EP_Reduced) ); - ExprSetIrreducible(pEq); - pEq->iRightJoinTable = (i16)pE2->iTable; - } - *ppWhere = sqlite3ExprAnd(db, *ppWhere, pEq); -} - -/* -** Set the EP_FromJoin property on all terms of the given expression. -** And set the Expr.iRightJoinTable to iTable for every term in the -** expression. -** -** The EP_FromJoin property is used on terms of an expression to tell -** the LEFT OUTER JOIN processing logic that this term is part of the -** join restriction specified in the ON or USING clause and not a part -** of the more general WHERE clause. These terms are moved over to the -** WHERE clause during join processing but we need to remember that they -** originated in the ON or USING clause. -** -** The Expr.iRightJoinTable tells the WHERE clause processing that the -** expression depends on table iRightJoinTable even if that table is not -** explicitly mentioned in the expression. That information is needed -** for cases like this: -** -** SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.b AND t1.x=5 -** -** The where clause needs to defer the handling of the t1.x=5 -** term until after the t2 loop of the join. In that way, a -** NULL t2 row will be inserted whenever t1.x!=5. If we do not -** defer the handling of t1.x=5, it will be processed immediately -** after the t1 loop and rows with t1.x!=5 will never appear in -** the output, which is incorrect. -*/ -static void setJoinExpr(Expr *p, int iTable){ - while( p ){ - ExprSetProperty(p, EP_FromJoin); - assert( !ExprHasAnyProperty(p, EP_TokenOnly|EP_Reduced) ); - ExprSetIrreducible(p); - p->iRightJoinTable = (i16)iTable; - setJoinExpr(p->pLeft, iTable); - p = p->pRight; - } -} - -/* -** This routine processes the join information for a SELECT statement. -** ON and USING clauses are converted into extra terms of the WHERE clause. -** NATURAL joins also create extra WHERE clause terms. -** -** The terms of a FROM clause are contained in the Select.pSrc structure. -** The left most table is the first entry in Select.pSrc. The right-most -** table is the last entry. The join operator is held in the entry to -** the left. Thus entry 0 contains the join operator for the join between -** entries 0 and 1. Any ON or USING clauses associated with the join are -** also attached to the left entry. -** -** This routine returns the number of errors encountered. -*/ -static int sqliteProcessJoin(Parse *pParse, Select *p){ - SrcList *pSrc; /* All tables in the FROM clause */ - int i, j; /* Loop counters */ - struct SrcList_item *pLeft; /* Left table being joined */ - struct SrcList_item *pRight; /* Right table being joined */ - - pSrc = p->pSrc; - pLeft = &pSrc->a[0]; - pRight = &pLeft[1]; - for(i=0; inSrc-1; i++, pRight++, pLeft++){ - Table *pLeftTab = pLeft->pTab; - Table *pRightTab = pRight->pTab; - int isOuter; - - if( NEVER(pLeftTab==0 || pRightTab==0) ) continue; - isOuter = (pRight->jointype & JT_OUTER)!=0; - - /* When the NATURAL keyword is present, add WHERE clause terms for - ** every column that the two tables have in common. - */ - if( pRight->jointype & JT_NATURAL ){ - if( pRight->pOn || pRight->pUsing ){ - sqlite3ErrorMsg(pParse, "a NATURAL join may not have " - "an ON or USING clause", 0); - return 1; - } - for(j=0; jnCol; j++){ - char *zName; /* Name of column in the right table */ - int iLeft; /* Matching left table */ - int iLeftCol; /* Matching column in the left table */ - - zName = pRightTab->aCol[j].zName; - if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol) ){ - addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j, - isOuter, &p->pWhere); - } - } - } - - /* Disallow both ON and USING clauses in the same join - */ - if( pRight->pOn && pRight->pUsing ){ - sqlite3ErrorMsg(pParse, "cannot have both ON and USING " - "clauses in the same join"); - return 1; - } - - /* Add the ON clause to the end of the WHERE clause, connected by - ** an AND operator. - */ - if( pRight->pOn ){ - if( isOuter ) setJoinExpr(pRight->pOn, pRight->iCursor); - p->pWhere = sqlite3ExprAnd(pParse->db, p->pWhere, pRight->pOn); - pRight->pOn = 0; - } - - /* Create extra terms on the WHERE clause for each column named - ** in the USING clause. Example: If the two tables to be joined are - ** A and B and the USING clause names X, Y, and Z, then add this - ** to the WHERE clause: A.X=B.X AND A.Y=B.Y AND A.Z=B.Z - ** Report an error if any column mentioned in the USING clause is - ** not contained in both tables to be joined. - */ - if( pRight->pUsing ){ - IdList *pList = pRight->pUsing; - for(j=0; jnId; j++){ - char *zName; /* Name of the term in the USING clause */ - int iLeft; /* Table on the left with matching column name */ - int iLeftCol; /* Column number of matching column on the left */ - int iRightCol; /* Column number of matching column on the right */ - - zName = pList->a[j].zName; - iRightCol = columnIndex(pRightTab, zName); - if( iRightCol<0 - || !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol) - ){ - sqlite3ErrorMsg(pParse, "cannot join using column %s - column " - "not present in both tables", zName); - return 1; - } - addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, iRightCol, - isOuter, &p->pWhere); - } - } - } - return 0; -} - -/* -** Insert code into "v" that will push the record on the top of the -** stack into the sorter. -*/ -static void pushOntoSorter( - Parse *pParse, /* Parser context */ - ExprList *pOrderBy, /* The ORDER BY clause */ - Select *pSelect, /* The whole SELECT statement */ - int regData /* Register holding data to be sorted */ -){ - Vdbe *v = pParse->pVdbe; - int nExpr = pOrderBy->nExpr; - int regBase = sqlite3GetTempRange(pParse, nExpr+2); - int regRecord = sqlite3GetTempReg(pParse); - int op; - sqlite3ExprCacheClear(pParse); - sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0); - sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr); - sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nExpr + 2, regRecord); - if( pSelect->selFlags & SF_UseSorter ){ - op = OP_SorterInsert; - }else{ - op = OP_IdxInsert; - } - sqlite3VdbeAddOp2(v, op, pOrderBy->iECursor, regRecord); - sqlite3ReleaseTempReg(pParse, regRecord); - sqlite3ReleaseTempRange(pParse, regBase, nExpr+2); - if( pSelect->iLimit ){ - int addr1, addr2; - int iLimit; - if( pSelect->iOffset ){ - iLimit = pSelect->iOffset+1; - }else{ - iLimit = pSelect->iLimit; - } - addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); - sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1); - addr2 = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, addr1); - sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); - sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor); - sqlite3VdbeJumpHere(v, addr2); - } -} - -/* -** Add code to implement the OFFSET -*/ -static void codeOffset( - Vdbe *v, /* Generate code into this VM */ - Select *p, /* The SELECT statement being coded */ - int iContinue /* Jump here to skip the current record */ -){ - if( p->iOffset && iContinue!=0 ){ - int addr; - sqlite3VdbeAddOp2(v, OP_AddImm, p->iOffset, -1); - addr = sqlite3VdbeAddOp1(v, OP_IfNeg, p->iOffset); - sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue); - VdbeComment((v, "skip OFFSET records")); - sqlite3VdbeJumpHere(v, addr); - } -} - -/* -** Add code that will check to make sure the N registers starting at iMem -** form a distinct entry. iTab is a sorting index that holds previously -** seen combinations of the N values. A new entry is made in iTab -** if the current N values are new. -** -** A jump to addrRepeat is made and the N+1 values are popped from the -** stack if the top N elements are not distinct. -*/ -static void codeDistinct( - Parse *pParse, /* Parsing and code generating context */ - int iTab, /* A sorting index used to test for distinctness */ - int addrRepeat, /* Jump to here if not distinct */ - int N, /* Number of elements */ - int iMem /* First element */ -){ - Vdbe *v; - int r1; - - v = pParse->pVdbe; - r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); - sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1); - sqlite3ReleaseTempReg(pParse, r1); -} - -#ifndef SQLITE_OMIT_SUBQUERY -/* -** Generate an error message when a SELECT is used within a subexpression -** (example: "a IN (SELECT * FROM table)") but it has more than 1 result -** column. We do this in a subroutine because the error used to occur -** in multiple places. (The error only occurs in one place now, but we -** retain the subroutine to minimize code disruption.) -*/ -static int checkForMultiColumnSelectError( - Parse *pParse, /* Parse context. */ - SelectDest *pDest, /* Destination of SELECT results */ - int nExpr /* Number of result columns returned by SELECT */ -){ - int eDest = pDest->eDest; - if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){ - sqlite3ErrorMsg(pParse, "only a single result allowed for " - "a SELECT that is part of an expression"); - return 1; - }else{ - return 0; - } -} -#endif - -/* -** An instance of the following object is used to record information about -** how to process the DISTINCT keyword, to simplify passing that information -** into the selectInnerLoop() routine. -*/ -typedef struct DistinctCtx DistinctCtx; -struct DistinctCtx { - u8 isTnct; /* True if the DISTINCT keyword is present */ - u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */ - int tabTnct; /* Ephemeral table used for DISTINCT processing */ - int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */ -}; - -/* -** This routine generates the code for the inside of the inner loop -** of a SELECT. -** -** If srcTab and nColumn are both zero, then the pEList expressions -** are evaluated in order to get the data for this row. If nColumn>0 -** then data is pulled from srcTab and pEList is used only to get the -** datatypes for each column. -*/ -static void selectInnerLoop( - Parse *pParse, /* The parser context */ - Select *p, /* The complete select statement being coded */ - ExprList *pEList, /* List of values being extracted */ - int srcTab, /* Pull data from this table */ - int nColumn, /* Number of columns in the source table */ - ExprList *pOrderBy, /* If not NULL, sort results using this key */ - DistinctCtx *pDistinct, /* If not NULL, info on how to process DISTINCT */ - SelectDest *pDest, /* How to dispose of the results */ - int iContinue, /* Jump here to continue with next row */ - int iBreak /* Jump here to break out of the inner loop */ -){ - Vdbe *v = pParse->pVdbe; - int i; - int hasDistinct; /* True if the DISTINCT keyword is present */ - int regResult; /* Start of memory holding result set */ - int eDest = pDest->eDest; /* How to dispose of results */ - int iParm = pDest->iSDParm; /* First argument to disposal method */ - int nResultCol; /* Number of result columns */ - - assert( v ); - if( NEVER(v==0) ) return; - assert( pEList!=0 ); - hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP; - if( pOrderBy==0 && !hasDistinct ){ - codeOffset(v, p, iContinue); - } - - /* Pull the requested columns. - */ - if( nColumn>0 ){ - nResultCol = nColumn; - }else{ - nResultCol = pEList->nExpr; - } - if( pDest->iSdst==0 ){ - pDest->iSdst = pParse->nMem+1; - pDest->nSdst = nResultCol; - pParse->nMem += nResultCol; - }else{ - assert( pDest->nSdst==nResultCol ); - } - regResult = pDest->iSdst; - if( nColumn>0 ){ - for(i=0; inExpr==nColumn ); - switch( pDistinct->eTnctType ){ - case WHERE_DISTINCT_ORDERED: { - VdbeOp *pOp; /* No longer required OpenEphemeral instr. */ - int iJump; /* Jump destination */ - int regPrev; /* Previous row content */ - - /* Allocate space for the previous row */ - regPrev = pParse->nMem+1; - pParse->nMem += nColumn; - - /* Change the OP_OpenEphemeral coded earlier to an OP_Null - ** sets the MEM_Cleared bit on the first register of the - ** previous value. This will cause the OP_Ne below to always - ** fail on the first iteration of the loop even if the first - ** row is all NULLs. - */ - sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct); - pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct); - pOp->opcode = OP_Null; - pOp->p1 = 1; - pOp->p2 = regPrev; - - iJump = sqlite3VdbeCurrentAddr(v) + nColumn; - for(i=0; ia[i].pExpr); - if( iaddrTnct); - break; - } - - default: { - assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED ); - codeDistinct(pParse, pDistinct->tabTnct, iContinue, nColumn, regResult); - break; - } - } - if( pOrderBy==0 ){ - codeOffset(v, p, iContinue); - } - } - - switch( eDest ){ - /* In this mode, write each query result to the key of the temporary - ** table iParm. - */ -#ifndef SQLITE_OMIT_COMPOUND_SELECT - case SRT_Union: { - int r1; - r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); - sqlite3ReleaseTempReg(pParse, r1); - break; - } - - /* Construct a record from the query result, but instead of - ** saving that record, use it as a key to delete elements from - ** the temporary table iParm. - */ - case SRT_Except: { - sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nColumn); - break; - } -#endif - - /* Store the result as data using a unique key. - */ - case SRT_Table: - case SRT_EphemTab: { - int r1 = sqlite3GetTempReg(pParse); - testcase( eDest==SRT_Table ); - testcase( eDest==SRT_EphemTab ); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); - if( pOrderBy ){ - pushOntoSorter(pParse, pOrderBy, p, r1); - }else{ - int r2 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2); - sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, r2); - sqlite3VdbeChangeP5(v, OPFLAG_APPEND); - sqlite3ReleaseTempReg(pParse, r2); - } - sqlite3ReleaseTempReg(pParse, r1); - break; - } - -#ifndef SQLITE_OMIT_SUBQUERY - /* If we are creating a set for an "expr IN (SELECT ...)" construct, - ** then there should be a single item on the stack. Write this - ** item into the set table with bogus data. - */ - case SRT_Set: { - assert( nColumn==1 ); - pDest->affSdst = - sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst); - if( pOrderBy ){ - /* At first glance you would think we could optimize out the - ** ORDER BY in this case since the order of entries in the set - ** does not matter. But there might be a LIMIT clause, in which - ** case the order does matter */ - pushOntoSorter(pParse, pOrderBy, p, regResult); - }else{ - int r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1); - sqlite3ExprCacheAffinityChange(pParse, regResult, 1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); - sqlite3ReleaseTempReg(pParse, r1); - } - break; - } - - /* If any row exist in the result set, record that fact and abort. - */ - case SRT_Exists: { - sqlite3VdbeAddOp2(v, OP_Integer, 1, iParm); - /* The LIMIT clause will terminate the loop for us */ - break; - } - - /* If this is a scalar select that is part of an expression, then - ** store the results in the appropriate memory cell and break out - ** of the scan loop. - */ - case SRT_Mem: { - assert( nColumn==1 ); - if( pOrderBy ){ - pushOntoSorter(pParse, pOrderBy, p, regResult); - }else{ - sqlite3ExprCodeMove(pParse, regResult, iParm, 1); - /* The LIMIT clause will jump out of the loop for us */ - } - break; - } -#endif /* #ifndef SQLITE_OMIT_SUBQUERY */ - - /* Send the data to the callback function or to a subroutine. In the - ** case of a subroutine, the subroutine itself is responsible for - ** popping the data from the stack. - */ - case SRT_Coroutine: - case SRT_Output: { - testcase( eDest==SRT_Coroutine ); - testcase( eDest==SRT_Output ); - if( pOrderBy ){ - int r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); - pushOntoSorter(pParse, pOrderBy, p, r1); - sqlite3ReleaseTempReg(pParse, r1); - }else if( eDest==SRT_Coroutine ){ - sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); - }else{ - sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn); - sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn); - } - break; - } - -#if !defined(SQLITE_OMIT_TRIGGER) - /* Discard the results. This is used for SELECT statements inside - ** the body of a TRIGGER. The purpose of such selects is to call - ** user-defined functions that have side effects. We do not care - ** about the actual results of the select. - */ - default: { - assert( eDest==SRT_Discard ); - break; - } -#endif - } - - /* Jump to the end of the loop if the LIMIT is reached. Except, if - ** there is a sorter, in which case the sorter has already limited - ** the output for us. - */ - if( pOrderBy==0 && p->iLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); - } -} - -/* -** Given an expression list, generate a KeyInfo structure that records -** the collating sequence for each expression in that expression list. -** -** If the ExprList is an ORDER BY or GROUP BY clause then the resulting -** KeyInfo structure is appropriate for initializing a virtual index to -** implement that clause. If the ExprList is the result set of a SELECT -** then the KeyInfo structure is appropriate for initializing a virtual -** index to implement a DISTINCT test. -** -** Space to hold the KeyInfo structure is obtain from malloc. The calling -** function is responsible for seeing that this structure is eventually -** freed. Add the KeyInfo structure to the P4 field of an opcode using -** P4_KEYINFO_HANDOFF is the usual way of dealing with this. -*/ -static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){ - sqlite3 *db = pParse->db; - int nExpr; - KeyInfo *pInfo; - struct ExprList_item *pItem; - int i; - - nExpr = pList->nExpr; - pInfo = sqlite3DbMallocZero(db, sizeof(*pInfo) + nExpr*(sizeof(CollSeq*)+1) ); - if( pInfo ){ - pInfo->aSortOrder = (u8*)&pInfo->aColl[nExpr]; - pInfo->nField = (u16)nExpr; - pInfo->enc = ENC(db); - pInfo->db = db; - for(i=0, pItem=pList->a; ipExpr); - if( !pColl ){ - pColl = db->pDfltColl; - } - pInfo->aColl[i] = pColl; - pInfo->aSortOrder[i] = pItem->sortOrder; - } - } - return pInfo; -} - -#ifndef SQLITE_OMIT_COMPOUND_SELECT -/* -** Name of the connection operator, used for error messages. -*/ -static const char *selectOpName(int id){ - char *z; - switch( id ){ - case TK_ALL: z = "UNION ALL"; break; - case TK_INTERSECT: z = "INTERSECT"; break; - case TK_EXCEPT: z = "EXCEPT"; break; - default: z = "UNION"; break; - } - return z; -} -#endif /* SQLITE_OMIT_COMPOUND_SELECT */ - -#ifndef SQLITE_OMIT_EXPLAIN -/* -** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function -** is a no-op. Otherwise, it adds a single row of output to the EQP result, -** where the caption is of the form: -** -** "USE TEMP B-TREE FOR xxx" -** -** where xxx is one of "DISTINCT", "ORDER BY" or "GROUP BY". Exactly which -** is determined by the zUsage argument. -*/ -static void explainTempTable(Parse *pParse, const char *zUsage){ - if( pParse->explain==2 ){ - Vdbe *v = pParse->pVdbe; - char *zMsg = sqlite3MPrintf(pParse->db, "USE TEMP B-TREE FOR %s", zUsage); - sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); - } -} - -/* -** Assign expression b to lvalue a. A second, no-op, version of this macro -** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code -** in sqlite3Select() to assign values to structure member variables that -** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the -** code with #ifndef directives. -*/ -# define explainSetInteger(a, b) a = b - -#else -/* No-op versions of the explainXXX() functions and macros. */ -# define explainTempTable(y,z) -# define explainSetInteger(y,z) -#endif - -#if !defined(SQLITE_OMIT_EXPLAIN) && !defined(SQLITE_OMIT_COMPOUND_SELECT) -/* -** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function -** is a no-op. Otherwise, it adds a single row of output to the EQP result, -** where the caption is of one of the two forms: -** -** "COMPOSITE SUBQUERIES iSub1 and iSub2 (op)" -** "COMPOSITE SUBQUERIES iSub1 and iSub2 USING TEMP B-TREE (op)" -** -** where iSub1 and iSub2 are the integers passed as the corresponding -** function parameters, and op is the text representation of the parameter -** of the same name. The parameter "op" must be one of TK_UNION, TK_EXCEPT, -** TK_INTERSECT or TK_ALL. The first form is used if argument bUseTmp is -** false, or the second form if it is true. -*/ -static void explainComposite( - Parse *pParse, /* Parse context */ - int op, /* One of TK_UNION, TK_EXCEPT etc. */ - int iSub1, /* Subquery id 1 */ - int iSub2, /* Subquery id 2 */ - int bUseTmp /* True if a temp table was used */ -){ - assert( op==TK_UNION || op==TK_EXCEPT || op==TK_INTERSECT || op==TK_ALL ); - if( pParse->explain==2 ){ - Vdbe *v = pParse->pVdbe; - char *zMsg = sqlite3MPrintf( - pParse->db, "COMPOUND SUBQUERIES %d AND %d %s(%s)", iSub1, iSub2, - bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op) - ); - sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); - } -} -#else -/* No-op versions of the explainXXX() functions and macros. */ -# define explainComposite(v,w,x,y,z) -#endif - -/* -** If the inner loop was generated using a non-null pOrderBy argument, -** then the results were placed in a sorter. After the loop is terminated -** we need to run the sorter and output the results. The following -** routine generates the code needed to do that. -*/ -static void generateSortTail( - Parse *pParse, /* Parsing context */ - Select *p, /* The SELECT statement */ - Vdbe *v, /* Generate code into this VDBE */ - int nColumn, /* Number of columns of data */ - SelectDest *pDest /* Write the sorted results here */ -){ - int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */ - int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */ - int addr; - int iTab; - int pseudoTab = 0; - ExprList *pOrderBy = p->pOrderBy; - - int eDest = pDest->eDest; - int iParm = pDest->iSDParm; - - int regRow; - int regRowid; - - iTab = pOrderBy->iECursor; - regRow = sqlite3GetTempReg(pParse); - if( eDest==SRT_Output || eDest==SRT_Coroutine ){ - pseudoTab = pParse->nTab++; - sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, regRow, nColumn); - regRowid = 0; - }else{ - regRowid = sqlite3GetTempReg(pParse); - } - if( p->selFlags & SF_UseSorter ){ - int regSortOut = ++pParse->nMem; - int ptab2 = pParse->nTab++; - sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2); - addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); - codeOffset(v, p, addrContinue); - sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut); - sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow); - sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); - }else{ - addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); - codeOffset(v, p, addrContinue); - sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow); - } - switch( eDest ){ - case SRT_Table: - case SRT_EphemTab: { - testcase( eDest==SRT_Table ); - testcase( eDest==SRT_EphemTab ); - sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid); - sqlite3VdbeChangeP5(v, OPFLAG_APPEND); - break; - } -#ifndef SQLITE_OMIT_SUBQUERY - case SRT_Set: { - assert( nColumn==1 ); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid, - &pDest->affSdst, 1); - sqlite3ExprCacheAffinityChange(pParse, regRow, 1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid); - break; - } - case SRT_Mem: { - assert( nColumn==1 ); - sqlite3ExprCodeMove(pParse, regRow, iParm, 1); - /* The LIMIT clause will terminate the loop for us */ - break; - } -#endif - default: { - int i; - assert( eDest==SRT_Output || eDest==SRT_Coroutine ); - testcase( eDest==SRT_Output ); - testcase( eDest==SRT_Coroutine ); - for(i=0; iiSdst+i ); - sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iSdst+i); - if( i==0 ){ - sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); - } - } - if( eDest==SRT_Output ){ - sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iSdst, nColumn); - sqlite3ExprCacheAffinityChange(pParse, pDest->iSdst, nColumn); - }else{ - sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); - } - break; - } - } - sqlite3ReleaseTempReg(pParse, regRow); - sqlite3ReleaseTempReg(pParse, regRowid); - - /* The bottom of the loop - */ - sqlite3VdbeResolveLabel(v, addrContinue); - if( p->selFlags & SF_UseSorter ){ - sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr); - }else{ - sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); - } - sqlite3VdbeResolveLabel(v, addrBreak); - if( eDest==SRT_Output || eDest==SRT_Coroutine ){ - sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0); - } -} - -/* -** Return a pointer to a string containing the 'declaration type' of the -** expression pExpr. The string may be treated as static by the caller. -** -** The declaration type is the exact datatype definition extracted from the -** original CREATE TABLE statement if the expression is a column. The -** declaration type for a ROWID field is INTEGER. Exactly when an expression -** is considered a column can be complex in the presence of subqueries. The -** result-set expression in all of the following SELECT statements is -** considered a column by this function. -** -** SELECT col FROM tbl; -** SELECT (SELECT col FROM tbl; -** SELECT (SELECT col FROM tbl); -** SELECT abc FROM (SELECT col AS abc FROM tbl); -** -** The declaration type for any expression other than a column is NULL. -*/ -static const char *columnType( - NameContext *pNC, - Expr *pExpr, - const char **pzOriginDb, - const char **pzOriginTab, - const char **pzOriginCol -){ - char const *zType = 0; - char const *zOriginDb = 0; - char const *zOriginTab = 0; - char const *zOriginCol = 0; - int j; - if( NEVER(pExpr==0) || pNC->pSrcList==0 ) return 0; - - switch( pExpr->op ){ - case TK_AGG_COLUMN: - case TK_COLUMN: { - /* The expression is a column. Locate the table the column is being - ** extracted from in NameContext.pSrcList. This table may be real - ** database table or a subquery. - */ - Table *pTab = 0; /* Table structure column is extracted from */ - Select *pS = 0; /* Select the column is extracted from */ - int iCol = pExpr->iColumn; /* Index of column in pTab */ - testcase( pExpr->op==TK_AGG_COLUMN ); - testcase( pExpr->op==TK_COLUMN ); - while( pNC && !pTab ){ - SrcList *pTabList = pNC->pSrcList; - for(j=0;jnSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++); - if( jnSrc ){ - pTab = pTabList->a[j].pTab; - pS = pTabList->a[j].pSelect; - }else{ - pNC = pNC->pNext; - } - } - - if( pTab==0 ){ - /* At one time, code such as "SELECT new.x" within a trigger would - ** cause this condition to run. Since then, we have restructured how - ** trigger code is generated and so this condition is no longer - ** possible. However, it can still be true for statements like - ** the following: - ** - ** CREATE TABLE t1(col INTEGER); - ** SELECT (SELECT t1.col) FROM FROM t1; - ** - ** when columnType() is called on the expression "t1.col" in the - ** sub-select. In this case, set the column type to NULL, even - ** though it should really be "INTEGER". - ** - ** This is not a problem, as the column type of "t1.col" is never - ** used. When columnType() is called on the expression - ** "(SELECT t1.col)", the correct type is returned (see the TK_SELECT - ** branch below. */ - break; - } - - assert( pTab && pExpr->pTab==pTab ); - if( pS ){ - /* The "table" is actually a sub-select or a view in the FROM clause - ** of the SELECT statement. Return the declaration type and origin - ** data for the result-set column of the sub-select. - */ - if( iCol>=0 && ALWAYS(iColpEList->nExpr) ){ - /* If iCol is less than zero, then the expression requests the - ** rowid of the sub-select or view. This expression is legal (see - ** test case misc2.2.2) - it always evaluates to NULL. - */ - NameContext sNC; - Expr *p = pS->pEList->a[iCol].pExpr; - sNC.pSrcList = pS->pSrc; - sNC.pNext = pNC; - sNC.pParse = pNC->pParse; - zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol); - } - }else if( ALWAYS(pTab->pSchema) ){ - /* A real table */ - assert( !pS ); - if( iCol<0 ) iCol = pTab->iPKey; - assert( iCol==-1 || (iCol>=0 && iColnCol) ); - if( iCol<0 ){ - zType = "INTEGER"; - zOriginCol = "rowid"; - }else{ - zType = pTab->aCol[iCol].zType; - zOriginCol = pTab->aCol[iCol].zName; - } - zOriginTab = pTab->zName; - if( pNC->pParse ){ - int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema); - zOriginDb = pNC->pParse->db->aDb[iDb].zName; - } - } - break; - } -#ifndef SQLITE_OMIT_SUBQUERY - case TK_SELECT: { - /* The expression is a sub-select. Return the declaration type and - ** origin info for the single column in the result set of the SELECT - ** statement. - */ - NameContext sNC; - Select *pS = pExpr->x.pSelect; - Expr *p = pS->pEList->a[0].pExpr; - assert( ExprHasProperty(pExpr, EP_xIsSelect) ); - sNC.pSrcList = pS->pSrc; - sNC.pNext = pNC; - sNC.pParse = pNC->pParse; - zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol); - break; - } -#endif - } - - if( pzOriginDb ){ - assert( pzOriginTab && pzOriginCol ); - *pzOriginDb = zOriginDb; - *pzOriginTab = zOriginTab; - *pzOriginCol = zOriginCol; - } - return zType; -} - -/* -** Generate code that will tell the VDBE the declaration types of columns -** in the result set. -*/ -static void generateColumnTypes( - Parse *pParse, /* Parser context */ - SrcList *pTabList, /* List of tables */ - ExprList *pEList /* Expressions defining the result set */ -){ -#ifndef SQLITE_OMIT_DECLTYPE - Vdbe *v = pParse->pVdbe; - int i; - NameContext sNC; - sNC.pSrcList = pTabList; - sNC.pParse = pParse; - for(i=0; inExpr; i++){ - Expr *p = pEList->a[i].pExpr; - const char *zType; -#ifdef SQLITE_ENABLE_COLUMN_METADATA - const char *zOrigDb = 0; - const char *zOrigTab = 0; - const char *zOrigCol = 0; - zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol); - - /* The vdbe must make its own copy of the column-type and other - ** column specific strings, in case the schema is reset before this - ** virtual machine is deleted. - */ - sqlite3VdbeSetColName(v, i, COLNAME_DATABASE, zOrigDb, SQLITE_TRANSIENT); - sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT); - sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT); -#else - zType = columnType(&sNC, p, 0, 0, 0); -#endif - sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT); - } -#endif /* SQLITE_OMIT_DECLTYPE */ -} - -/* -** Generate code that will tell the VDBE the names of columns -** in the result set. This information is used to provide the -** azCol[] values in the callback. -*/ -static void generateColumnNames( - Parse *pParse, /* Parser context */ - SrcList *pTabList, /* List of tables */ - ExprList *pEList /* Expressions defining the result set */ -){ - Vdbe *v = pParse->pVdbe; - int i, j; - sqlite3 *db = pParse->db; - int fullNames, shortNames; - -#ifndef SQLITE_OMIT_EXPLAIN - /* If this is an EXPLAIN, skip this step */ - if( pParse->explain ){ - return; - } -#endif - - if( pParse->colNamesSet || NEVER(v==0) || db->mallocFailed ) return; - pParse->colNamesSet = 1; - fullNames = (db->flags & SQLITE_FullColNames)!=0; - shortNames = (db->flags & SQLITE_ShortColNames)!=0; - sqlite3VdbeSetNumCols(v, pEList->nExpr); - for(i=0; inExpr; i++){ - Expr *p; - p = pEList->a[i].pExpr; - if( NEVER(p==0) ) continue; - if( pEList->a[i].zName ){ - char *zName = pEList->a[i].zName; - sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT); - }else if( (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN) && pTabList ){ - Table *pTab; - char *zCol; - int iCol = p->iColumn; - for(j=0; ALWAYS(jnSrc); j++){ - if( pTabList->a[j].iCursor==p->iTable ) break; - } - assert( jnSrc ); - pTab = pTabList->a[j].pTab; - if( iCol<0 ) iCol = pTab->iPKey; - assert( iCol==-1 || (iCol>=0 && iColnCol) ); - if( iCol<0 ){ - zCol = "rowid"; - }else{ - zCol = pTab->aCol[iCol].zName; - } - if( !shortNames && !fullNames ){ - sqlite3VdbeSetColName(v, i, COLNAME_NAME, - sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); - }else if( fullNames ){ - char *zName = 0; - zName = sqlite3MPrintf(db, "%s.%s", pTab->zName, zCol); - sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_DYNAMIC); - }else{ - sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT); - } - }else{ - sqlite3VdbeSetColName(v, i, COLNAME_NAME, - sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); - } - } - generateColumnTypes(pParse, pTabList, pEList); -} - -/* -** Given a an expression list (which is really the list of expressions -** that form the result set of a SELECT statement) compute appropriate -** column names for a table that would hold the expression list. -** -** All column names will be unique. -** -** Only the column names are computed. Column.zType, Column.zColl, -** and other fields of Column are zeroed. -** -** Return SQLITE_OK on success. If a memory allocation error occurs, -** store NULL in *paCol and 0 in *pnCol and return SQLITE_NOMEM. -*/ -static int selectColumnsFromExprList( - Parse *pParse, /* Parsing context */ - ExprList *pEList, /* Expr list from which to derive column names */ - i16 *pnCol, /* Write the number of columns here */ - Column **paCol /* Write the new column list here */ -){ - sqlite3 *db = pParse->db; /* Database connection */ - int i, j; /* Loop counters */ - int cnt; /* Index added to make the name unique */ - Column *aCol, *pCol; /* For looping over result columns */ - int nCol; /* Number of columns in the result set */ - Expr *p; /* Expression for a single result column */ - char *zName; /* Column name */ - int nName; /* Size of name in zName[] */ - - if( pEList ){ - nCol = pEList->nExpr; - aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); - testcase( aCol==0 ); - }else{ - nCol = 0; - aCol = 0; - } - *pnCol = nCol; - *paCol = aCol; - - for(i=0, pCol=aCol; ia[i].pExpr); - if( (zName = pEList->a[i].zName)!=0 ){ - /* If the column contains an "AS " phrase, use as the name */ - zName = sqlite3DbStrDup(db, zName); - }else{ - Expr *pColExpr = p; /* The expression that is the result column name */ - Table *pTab; /* Table associated with this expression */ - while( pColExpr->op==TK_DOT ){ - pColExpr = pColExpr->pRight; - assert( pColExpr!=0 ); - } - if( pColExpr->op==TK_COLUMN && ALWAYS(pColExpr->pTab!=0) ){ - /* For columns use the column name name */ - int iCol = pColExpr->iColumn; - pTab = pColExpr->pTab; - if( iCol<0 ) iCol = pTab->iPKey; - zName = sqlite3MPrintf(db, "%s", - iCol>=0 ? pTab->aCol[iCol].zName : "rowid"); - }else if( pColExpr->op==TK_ID ){ - assert( !ExprHasProperty(pColExpr, EP_IntValue) ); - zName = sqlite3MPrintf(db, "%s", pColExpr->u.zToken); - }else{ - /* Use the original text of the column expression as its name */ - zName = sqlite3MPrintf(db, "%s", pEList->a[i].zSpan); - } - } - if( db->mallocFailed ){ - sqlite3DbFree(db, zName); - break; - } - - /* Make sure the column name is unique. If the name is not unique, - ** append a integer to the name so that it becomes unique. - */ - nName = sqlite3Strlen30(zName); - for(j=cnt=0; j1 && sqlite3Isdigit(zName[k]); k--){} - if( zName[k]==':' ) nName = k; - zName[nName] = 0; - zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); - sqlite3DbFree(db, zName); - zName = zNewName; - j = -1; - if( zName==0 ) break; - } - } - pCol->zName = zName; - } - if( db->mallocFailed ){ - for(j=0; jdb; - NameContext sNC; - Column *pCol; - CollSeq *pColl; - int i; - Expr *p; - struct ExprList_item *a; - - assert( pSelect!=0 ); - assert( (pSelect->selFlags & SF_Resolved)!=0 ); - assert( nCol==pSelect->pEList->nExpr || db->mallocFailed ); - if( db->mallocFailed ) return; - memset(&sNC, 0, sizeof(sNC)); - sNC.pSrcList = pSelect->pSrc; - a = pSelect->pEList->a; - for(i=0, pCol=aCol; izType = sqlite3DbStrDup(db, columnType(&sNC, p, 0, 0, 0)); - pCol->affinity = sqlite3ExprAffinity(p); - if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_NONE; - pColl = sqlite3ExprCollSeq(pParse, p); - if( pColl ){ - pCol->zColl = sqlite3DbStrDup(db, pColl->zName); - } - } -} - -/* -** Given a SELECT statement, generate a Table structure that describes -** the result set of that SELECT. -*/ -SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){ - Table *pTab; - sqlite3 *db = pParse->db; - int savedFlags; - - savedFlags = db->flags; - db->flags &= ~SQLITE_FullColNames; - db->flags |= SQLITE_ShortColNames; - sqlite3SelectPrep(pParse, pSelect, 0); - if( pParse->nErr ) return 0; - while( pSelect->pPrior ) pSelect = pSelect->pPrior; - db->flags = savedFlags; - pTab = sqlite3DbMallocZero(db, sizeof(Table) ); - if( pTab==0 ){ - return 0; - } - /* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside - ** is disabled */ - assert( db->lookaside.bEnabled==0 ); - pTab->nRef = 1; - pTab->zName = 0; - pTab->nRowEst = 1000000; - selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); - selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect); - pTab->iPKey = -1; - if( db->mallocFailed ){ - sqlite3DeleteTable(db, pTab); - return 0; - } - return pTab; -} - -/* -** Get a VDBE for the given parser context. Create a new one if necessary. -** If an error occurs, return NULL and leave a message in pParse. -*/ -SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ - Vdbe *v = pParse->pVdbe; - if( v==0 ){ - v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db); -#ifndef SQLITE_OMIT_TRACE - if( v ){ - sqlite3VdbeAddOp0(v, OP_Trace); - } -#endif - } - return v; -} - - -/* -** Compute the iLimit and iOffset fields of the SELECT based on the -** pLimit and pOffset expressions. pLimit and pOffset hold the expressions -** that appear in the original SQL statement after the LIMIT and OFFSET -** keywords. Or NULL if those keywords are omitted. iLimit and iOffset -** are the integer memory register numbers for counters used to compute -** the limit and offset. If there is no limit and/or offset, then -** iLimit and iOffset are negative. -** -** This routine changes the values of iLimit and iOffset only if -** a limit or offset is defined by pLimit and pOffset. iLimit and -** iOffset should have been preset to appropriate default values -** (usually but not always -1) prior to calling this routine. -** Only if pLimit!=0 or pOffset!=0 do the limit registers get -** redefined. The UNION ALL operator uses this property to force -** the reuse of the same limit and offset registers across multiple -** SELECT statements. -*/ -static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ - Vdbe *v = 0; - int iLimit = 0; - int iOffset; - int addr1, n; - if( p->iLimit ) return; - - /* - ** "LIMIT -1" always shows all rows. There is some - ** contraversy about what the correct behavior should be. - ** The current implementation interprets "LIMIT 0" to mean - ** no rows. - */ - sqlite3ExprCacheClear(pParse); - assert( p->pOffset==0 || p->pLimit!=0 ); - if( p->pLimit ){ - p->iLimit = iLimit = ++pParse->nMem; - v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return; /* VDBE should have already been allocated */ - if( sqlite3ExprIsInteger(p->pLimit, &n) ){ - sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); - VdbeComment((v, "LIMIT counter")); - if( n==0 ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak); - }else{ - if( p->nSelectRow > (double)n ) p->nSelectRow = (double)n; - } - }else{ - sqlite3ExprCode(pParse, p->pLimit, iLimit); - sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); - VdbeComment((v, "LIMIT counter")); - sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); - } - if( p->pOffset ){ - p->iOffset = iOffset = ++pParse->nMem; - pParse->nMem++; /* Allocate an extra register for limit+offset */ - sqlite3ExprCode(pParse, p->pOffset, iOffset); - sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); - VdbeComment((v, "OFFSET counter")); - addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset); - sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset); - sqlite3VdbeJumpHere(v, addr1); - sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1); - VdbeComment((v, "LIMIT+OFFSET")); - addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); - sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1); - sqlite3VdbeJumpHere(v, addr1); - } - } -} - -#ifndef SQLITE_OMIT_COMPOUND_SELECT -/* -** Return the appropriate collating sequence for the iCol-th column of -** the result set for the compound-select statement "p". Return NULL if -** the column has no default collating sequence. -** -** The collating sequence for the compound select is taken from the -** left-most term of the select that has a collating sequence. -*/ -static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){ - CollSeq *pRet; - if( p->pPrior ){ - pRet = multiSelectCollSeq(pParse, p->pPrior, iCol); - }else{ - pRet = 0; - } - assert( iCol>=0 ); - if( pRet==0 && iColpEList->nExpr ){ - pRet = sqlite3ExprCollSeq(pParse, p->pEList->a[iCol].pExpr); - } - return pRet; -} -#endif /* SQLITE_OMIT_COMPOUND_SELECT */ - -/* Forward reference */ -static int multiSelectOrderBy( - Parse *pParse, /* Parsing context */ - Select *p, /* The right-most of SELECTs to be coded */ - SelectDest *pDest /* What to do with query results */ -); - - -#ifndef SQLITE_OMIT_COMPOUND_SELECT -/* -** This routine is called to process a compound query form from -** two or more separate queries using UNION, UNION ALL, EXCEPT, or -** INTERSECT -** -** "p" points to the right-most of the two queries. the query on the -** left is p->pPrior. The left query could also be a compound query -** in which case this routine will be called recursively. -** -** The results of the total query are to be written into a destination -** of type eDest with parameter iParm. -** -** Example 1: Consider a three-way compound SQL statement. -** -** SELECT a FROM t1 UNION SELECT b FROM t2 UNION SELECT c FROM t3 -** -** This statement is parsed up as follows: -** -** SELECT c FROM t3 -** | -** `-----> SELECT b FROM t2 -** | -** `------> SELECT a FROM t1 -** -** The arrows in the diagram above represent the Select.pPrior pointer. -** So if this routine is called with p equal to the t3 query, then -** pPrior will be the t2 query. p->op will be TK_UNION in this case. -** -** Notice that because of the way SQLite parses compound SELECTs, the -** individual selects always group from left to right. -*/ -static int multiSelect( - Parse *pParse, /* Parsing context */ - Select *p, /* The right-most of SELECTs to be coded */ - SelectDest *pDest /* What to do with query results */ -){ - int rc = SQLITE_OK; /* Success code from a subroutine */ - Select *pPrior; /* Another SELECT immediately to our left */ - Vdbe *v; /* Generate code to this VDBE */ - SelectDest dest; /* Alternative data destination */ - Select *pDelete = 0; /* Chain of simple selects to delete */ - sqlite3 *db; /* Database connection */ -#ifndef SQLITE_OMIT_EXPLAIN - int iSub1; /* EQP id of left-hand query */ - int iSub2; /* EQP id of right-hand query */ -#endif - - /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only - ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT. - */ - assert( p && p->pPrior ); /* Calling function guarantees this much */ - db = pParse->db; - pPrior = p->pPrior; - assert( pPrior->pRightmost!=pPrior ); - assert( pPrior->pRightmost==p->pRightmost ); - dest = *pDest; - if( pPrior->pOrderBy ){ - sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before", - selectOpName(p->op)); - rc = 1; - goto multi_select_end; - } - if( pPrior->pLimit ){ - sqlite3ErrorMsg(pParse,"LIMIT clause should come after %s not before", - selectOpName(p->op)); - rc = 1; - goto multi_select_end; - } - - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); /* The VDBE already created by calling function */ - - /* Create the destination temporary table if necessary - */ - if( dest.eDest==SRT_EphemTab ){ - assert( p->pEList ); - sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr); - sqlite3VdbeChangeP5(v, BTREE_UNORDERED); - dest.eDest = SRT_Table; - } - - /* Make sure all SELECTs in the statement have the same number of elements - ** in their result sets. - */ - assert( p->pEList && pPrior->pEList ); - if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ - if( p->selFlags & SF_Values ){ - sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); - }else{ - sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" - " do not have the same number of result columns", selectOpName(p->op)); - } - rc = 1; - goto multi_select_end; - } - - /* Compound SELECTs that have an ORDER BY clause are handled separately. - */ - if( p->pOrderBy ){ - return multiSelectOrderBy(pParse, p, pDest); - } - - /* Generate code for the left and right SELECT statements. - */ - switch( p->op ){ - case TK_ALL: { - int addr = 0; - int nLimit; - assert( !pPrior->pLimit ); - pPrior->iLimit = p->iLimit; - pPrior->iOffset = p->iOffset; - pPrior->pLimit = p->pLimit; - pPrior->pOffset = p->pOffset; - explainSetInteger(iSub1, pParse->iNextSelectId); - rc = sqlite3Select(pParse, pPrior, &dest); - p->pLimit = 0; - p->pOffset = 0; - if( rc ){ - goto multi_select_end; - } - p->pPrior = 0; - p->iLimit = pPrior->iLimit; - p->iOffset = pPrior->iOffset; - if( p->iLimit ){ - addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); - VdbeComment((v, "Jump ahead if LIMIT reached")); - } - explainSetInteger(iSub2, pParse->iNextSelectId); - rc = sqlite3Select(pParse, p, &dest); - testcase( rc!=SQLITE_OK ); - pDelete = p->pPrior; - p->pPrior = pPrior; - p->nSelectRow += pPrior->nSelectRow; - if( pPrior->pLimit - && sqlite3ExprIsInteger(pPrior->pLimit, &nLimit) - && p->nSelectRow > (double)nLimit - ){ - p->nSelectRow = (double)nLimit; - } - if( addr ){ - sqlite3VdbeJumpHere(v, addr); - } - break; - } - case TK_EXCEPT: - case TK_UNION: { - int unionTab; /* Cursor number of the temporary table holding result */ - u8 op = 0; /* One of the SRT_ operations to apply to self */ - int priorOp; /* The SRT_ operation to apply to prior selects */ - Expr *pLimit, *pOffset; /* Saved values of p->nLimit and p->nOffset */ - int addr; - SelectDest uniondest; - - testcase( p->op==TK_EXCEPT ); - testcase( p->op==TK_UNION ); - priorOp = SRT_Union; - if( dest.eDest==priorOp && ALWAYS(!p->pLimit &&!p->pOffset) ){ - /* We can reuse a temporary table generated by a SELECT to our - ** right. - */ - assert( p->pRightmost!=p ); /* Can only happen for leftward elements - ** of a 3-way or more compound */ - assert( p->pLimit==0 ); /* Not allowed on leftward elements */ - assert( p->pOffset==0 ); /* Not allowed on leftward elements */ - unionTab = dest.iSDParm; - }else{ - /* We will need to create our own temporary table to hold the - ** intermediate results. - */ - unionTab = pParse->nTab++; - assert( p->pOrderBy==0 ); - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0); - assert( p->addrOpenEphm[0] == -1 ); - p->addrOpenEphm[0] = addr; - p->pRightmost->selFlags |= SF_UsesEphemeral; - assert( p->pEList ); - } - - /* Code the SELECT statements to our left - */ - assert( !pPrior->pOrderBy ); - sqlite3SelectDestInit(&uniondest, priorOp, unionTab); - explainSetInteger(iSub1, pParse->iNextSelectId); - rc = sqlite3Select(pParse, pPrior, &uniondest); - if( rc ){ - goto multi_select_end; - } - - /* Code the current SELECT statement - */ - if( p->op==TK_EXCEPT ){ - op = SRT_Except; - }else{ - assert( p->op==TK_UNION ); - op = SRT_Union; - } - p->pPrior = 0; - pLimit = p->pLimit; - p->pLimit = 0; - pOffset = p->pOffset; - p->pOffset = 0; - uniondest.eDest = op; - explainSetInteger(iSub2, pParse->iNextSelectId); - rc = sqlite3Select(pParse, p, &uniondest); - testcase( rc!=SQLITE_OK ); - /* Query flattening in sqlite3Select() might refill p->pOrderBy. - ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */ - sqlite3ExprListDelete(db, p->pOrderBy); - pDelete = p->pPrior; - p->pPrior = pPrior; - p->pOrderBy = 0; - if( p->op==TK_UNION ) p->nSelectRow += pPrior->nSelectRow; - sqlite3ExprDelete(db, p->pLimit); - p->pLimit = pLimit; - p->pOffset = pOffset; - p->iLimit = 0; - p->iOffset = 0; - - /* Convert the data in the temporary table into whatever form - ** it is that we currently need. - */ - assert( unionTab==dest.iSDParm || dest.eDest!=priorOp ); - if( dest.eDest!=priorOp ){ - int iCont, iBreak, iStart; - assert( p->pEList ); - if( dest.eDest==SRT_Output ){ - Select *pFirst = p; - while( pFirst->pPrior ) pFirst = pFirst->pPrior; - generateColumnNames(pParse, 0, pFirst->pEList); - } - iBreak = sqlite3VdbeMakeLabel(v); - iCont = sqlite3VdbeMakeLabel(v); - computeLimitRegisters(pParse, p, iBreak); - sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); - iStart = sqlite3VdbeCurrentAddr(v); - selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr, - 0, 0, &dest, iCont, iBreak); - sqlite3VdbeResolveLabel(v, iCont); - sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); - sqlite3VdbeResolveLabel(v, iBreak); - sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0); - } - break; - } - default: assert( p->op==TK_INTERSECT ); { - int tab1, tab2; - int iCont, iBreak, iStart; - Expr *pLimit, *pOffset; - int addr; - SelectDest intersectdest; - int r1; - - /* INTERSECT is different from the others since it requires - ** two temporary tables. Hence it has its own case. Begin - ** by allocating the tables we will need. - */ - tab1 = pParse->nTab++; - tab2 = pParse->nTab++; - assert( p->pOrderBy==0 ); - - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0); - assert( p->addrOpenEphm[0] == -1 ); - p->addrOpenEphm[0] = addr; - p->pRightmost->selFlags |= SF_UsesEphemeral; - assert( p->pEList ); - - /* Code the SELECTs to our left into temporary table "tab1". - */ - sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); - explainSetInteger(iSub1, pParse->iNextSelectId); - rc = sqlite3Select(pParse, pPrior, &intersectdest); - if( rc ){ - goto multi_select_end; - } - - /* Code the current SELECT into temporary table "tab2" - */ - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0); - assert( p->addrOpenEphm[1] == -1 ); - p->addrOpenEphm[1] = addr; - p->pPrior = 0; - pLimit = p->pLimit; - p->pLimit = 0; - pOffset = p->pOffset; - p->pOffset = 0; - intersectdest.iSDParm = tab2; - explainSetInteger(iSub2, pParse->iNextSelectId); - rc = sqlite3Select(pParse, p, &intersectdest); - testcase( rc!=SQLITE_OK ); - pDelete = p->pPrior; - p->pPrior = pPrior; - if( p->nSelectRow>pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; - sqlite3ExprDelete(db, p->pLimit); - p->pLimit = pLimit; - p->pOffset = pOffset; - - /* Generate code to take the intersection of the two temporary - ** tables. - */ - assert( p->pEList ); - if( dest.eDest==SRT_Output ){ - Select *pFirst = p; - while( pFirst->pPrior ) pFirst = pFirst->pPrior; - generateColumnNames(pParse, 0, pFirst->pEList); - } - iBreak = sqlite3VdbeMakeLabel(v); - iCont = sqlite3VdbeMakeLabel(v); - computeLimitRegisters(pParse, p, iBreak); - sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); - r1 = sqlite3GetTempReg(pParse); - iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1); - sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); - sqlite3ReleaseTempReg(pParse, r1); - selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr, - 0, 0, &dest, iCont, iBreak); - sqlite3VdbeResolveLabel(v, iCont); - sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); - sqlite3VdbeResolveLabel(v, iBreak); - sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); - sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); - break; - } - } - - explainComposite(pParse, p->op, iSub1, iSub2, p->op!=TK_ALL); - - /* Compute collating sequences used by - ** temporary tables needed to implement the compound select. - ** Attach the KeyInfo structure to all temporary tables. - ** - ** This section is run by the right-most SELECT statement only. - ** SELECT statements to the left always skip this part. The right-most - ** SELECT might also skip this part if it has no ORDER BY clause and - ** no temp tables are required. - */ - if( p->selFlags & SF_UsesEphemeral ){ - int i; /* Loop counter */ - KeyInfo *pKeyInfo; /* Collating sequence for the result set */ - Select *pLoop; /* For looping through SELECT statements */ - CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */ - int nCol; /* Number of columns in result set */ - - assert( p->pRightmost==p ); - nCol = p->pEList->nExpr; - pKeyInfo = sqlite3DbMallocZero(db, - sizeof(*pKeyInfo)+nCol*(sizeof(CollSeq*) + 1)); - if( !pKeyInfo ){ - rc = SQLITE_NOMEM; - goto multi_select_end; - } - - pKeyInfo->enc = ENC(db); - pKeyInfo->nField = (u16)nCol; - - for(i=0, apColl=pKeyInfo->aColl; ipDfltColl; - } - } - pKeyInfo->aSortOrder = (u8*)apColl; - - for(pLoop=p; pLoop; pLoop=pLoop->pPrior){ - for(i=0; i<2; i++){ - int addr = pLoop->addrOpenEphm[i]; - if( addr<0 ){ - /* If [0] is unused then [1] is also unused. So we can - ** always safely abort as soon as the first unused slot is found */ - assert( pLoop->addrOpenEphm[1]<0 ); - break; - } - sqlite3VdbeChangeP2(v, addr, nCol); - sqlite3VdbeChangeP4(v, addr, (char*)pKeyInfo, P4_KEYINFO); - pLoop->addrOpenEphm[i] = -1; - } - } - sqlite3DbFree(db, pKeyInfo); - } - -multi_select_end: - pDest->iSdst = dest.iSdst; - pDest->nSdst = dest.nSdst; - sqlite3SelectDelete(db, pDelete); - return rc; -} -#endif /* SQLITE_OMIT_COMPOUND_SELECT */ - -/* -** Code an output subroutine for a coroutine implementation of a -** SELECT statment. -** -** The data to be output is contained in pIn->iSdst. There are -** pIn->nSdst columns to be output. pDest is where the output should -** be sent. -** -** regReturn is the number of the register holding the subroutine -** return address. -** -** If regPrev>0 then it is the first register in a vector that -** records the previous output. mem[regPrev] is a flag that is false -** if there has been no previous output. If regPrev>0 then code is -** generated to suppress duplicates. pKeyInfo is used for comparing -** keys. -** -** If the LIMIT found in p->iLimit is reached, jump immediately to -** iBreak. -*/ -static int generateOutputSubroutine( - Parse *pParse, /* Parsing context */ - Select *p, /* The SELECT statement */ - SelectDest *pIn, /* Coroutine supplying data */ - SelectDest *pDest, /* Where to send the data */ - int regReturn, /* The return address register */ - int regPrev, /* Previous result register. No uniqueness if 0 */ - KeyInfo *pKeyInfo, /* For comparing with previous entry */ - int p4type, /* The p4 type for pKeyInfo */ - int iBreak /* Jump here if we hit the LIMIT */ -){ - Vdbe *v = pParse->pVdbe; - int iContinue; - int addr; - - addr = sqlite3VdbeCurrentAddr(v); - iContinue = sqlite3VdbeMakeLabel(v); - - /* Suppress duplicates for UNION, EXCEPT, and INTERSECT - */ - if( regPrev ){ - int j1, j2; - j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); - j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst, - (char*)pKeyInfo, p4type); - sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2); - sqlite3VdbeJumpHere(v, j1); - sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1); - sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev); - } - if( pParse->db->mallocFailed ) return 0; - - /* Suppress the first OFFSET entries if there is an OFFSET clause - */ - codeOffset(v, p, iContinue); - - switch( pDest->eDest ){ - /* Store the result as data using a unique key. - */ - case SRT_Table: - case SRT_EphemTab: { - int r1 = sqlite3GetTempReg(pParse); - int r2 = sqlite3GetTempReg(pParse); - testcase( pDest->eDest==SRT_Table ); - testcase( pDest->eDest==SRT_EphemTab ); - sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1); - sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iSDParm, r2); - sqlite3VdbeAddOp3(v, OP_Insert, pDest->iSDParm, r1, r2); - sqlite3VdbeChangeP5(v, OPFLAG_APPEND); - sqlite3ReleaseTempReg(pParse, r2); - sqlite3ReleaseTempReg(pParse, r1); - break; - } - -#ifndef SQLITE_OMIT_SUBQUERY - /* If we are creating a set for an "expr IN (SELECT ...)" construct, - ** then there should be a single item on the stack. Write this - ** item into the set table with bogus data. - */ - case SRT_Set: { - int r1; - assert( pIn->nSdst==1 ); - pDest->affSdst = - sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst); - r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, 1, r1, &pDest->affSdst,1); - sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1); - sqlite3ReleaseTempReg(pParse, r1); - break; - } - -#if 0 /* Never occurs on an ORDER BY query */ - /* If any row exist in the result set, record that fact and abort. - */ - case SRT_Exists: { - sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iSDParm); - /* The LIMIT clause will terminate the loop for us */ - break; - } -#endif - - /* If this is a scalar select that is part of an expression, then - ** store the results in the appropriate memory cell and break out - ** of the scan loop. - */ - case SRT_Mem: { - assert( pIn->nSdst==1 ); - sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1); - /* The LIMIT clause will jump out of the loop for us */ - break; - } -#endif /* #ifndef SQLITE_OMIT_SUBQUERY */ - - /* The results are stored in a sequence of registers - ** starting at pDest->iSdst. Then the co-routine yields. - */ - case SRT_Coroutine: { - if( pDest->iSdst==0 ){ - pDest->iSdst = sqlite3GetTempRange(pParse, pIn->nSdst); - pDest->nSdst = pIn->nSdst; - } - sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pDest->nSdst); - sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); - break; - } - - /* If none of the above, then the result destination must be - ** SRT_Output. This routine is never called with any other - ** destination other than the ones handled above or SRT_Output. - ** - ** For SRT_Output, results are stored in a sequence of registers. - ** Then the OP_ResultRow opcode is used to cause sqlite3_step() to - ** return the next row of result. - */ - default: { - assert( pDest->eDest==SRT_Output ); - sqlite3VdbeAddOp2(v, OP_ResultRow, pIn->iSdst, pIn->nSdst); - sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst); - break; - } - } - - /* Jump to the end of the loop if the LIMIT is reached. - */ - if( p->iLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); - } - - /* Generate the subroutine return - */ - sqlite3VdbeResolveLabel(v, iContinue); - sqlite3VdbeAddOp1(v, OP_Return, regReturn); - - return addr; -} - -/* -** Alternative compound select code generator for cases when there -** is an ORDER BY clause. -** -** We assume a query of the following form: -** -** ORDER BY -** -** is one of UNION ALL, UNION, EXCEPT, or INTERSECT. The idea -** is to code both and with the ORDER BY clause as -** co-routines. Then run the co-routines in parallel and merge the results -** into the output. In addition to the two coroutines (called selectA and -** selectB) there are 7 subroutines: -** -** outA: Move the output of the selectA coroutine into the output -** of the compound query. -** -** outB: Move the output of the selectB coroutine into the output -** of the compound query. (Only generated for UNION and -** UNION ALL. EXCEPT and INSERTSECT never output a row that -** appears only in B.) -** -** AltB: Called when there is data from both coroutines and AB. -** -** EofA: Called when data is exhausted from selectA. -** -** EofB: Called when data is exhausted from selectB. -** -** The implementation of the latter five subroutines depend on which -** is used: -** -** -** UNION ALL UNION EXCEPT INTERSECT -** ------------- ----------------- -------------- ----------------- -** AltB: outA, nextA outA, nextA outA, nextA nextA -** -** AeqB: outA, nextA nextA nextA outA, nextA -** -** AgtB: outB, nextB outB, nextB nextB nextB -** -** EofA: outB, nextB outB, nextB halt halt -** -** EofB: outA, nextA outA, nextA outA, nextA halt -** -** In the AltB, AeqB, and AgtB subroutines, an EOF on A following nextA -** causes an immediate jump to EofA and an EOF on B following nextB causes -** an immediate jump to EofB. Within EofA and EofB, and EOF on entry or -** following nextX causes a jump to the end of the select processing. -** -** Duplicate removal in the UNION, EXCEPT, and INTERSECT cases is handled -** within the output subroutine. The regPrev register set holds the previously -** output value. A comparison is made against this value and the output -** is skipped if the next results would be the same as the previous. -** -** The implementation plan is to implement the two coroutines and seven -** subroutines first, then put the control logic at the bottom. Like this: -** -** goto Init -** coA: coroutine for left query (A) -** coB: coroutine for right query (B) -** outA: output one row of A -** outB: output one row of B (UNION and UNION ALL only) -** EofA: ... -** EofB: ... -** AltB: ... -** AeqB: ... -** AgtB: ... -** Init: initialize coroutine registers -** yield coA -** if eof(A) goto EofA -** yield coB -** if eof(B) goto EofB -** Cmpr: Compare A, B -** Jump AltB, AeqB, AgtB -** End: ... -** -** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not -** actually called using Gosub and they do not Return. EofA and EofB loop -** until all data is exhausted then jump to the "end" labe. AltB, AeqB, -** and AgtB jump to either L2 or to one of EofA or EofB. -*/ -#ifndef SQLITE_OMIT_COMPOUND_SELECT -static int multiSelectOrderBy( - Parse *pParse, /* Parsing context */ - Select *p, /* The right-most of SELECTs to be coded */ - SelectDest *pDest /* What to do with query results */ -){ - int i, j; /* Loop counters */ - Select *pPrior; /* Another SELECT immediately to our left */ - Vdbe *v; /* Generate code to this VDBE */ - SelectDest destA; /* Destination for coroutine A */ - SelectDest destB; /* Destination for coroutine B */ - int regAddrA; /* Address register for select-A coroutine */ - int regEofA; /* Flag to indicate when select-A is complete */ - int regAddrB; /* Address register for select-B coroutine */ - int regEofB; /* Flag to indicate when select-B is complete */ - int addrSelectA; /* Address of the select-A coroutine */ - int addrSelectB; /* Address of the select-B coroutine */ - int regOutA; /* Address register for the output-A subroutine */ - int regOutB; /* Address register for the output-B subroutine */ - int addrOutA; /* Address of the output-A subroutine */ - int addrOutB = 0; /* Address of the output-B subroutine */ - int addrEofA; /* Address of the select-A-exhausted subroutine */ - int addrEofB; /* Address of the select-B-exhausted subroutine */ - int addrAltB; /* Address of the AB subroutine */ - int regLimitA; /* Limit register for select-A */ - int regLimitB; /* Limit register for select-A */ - int regPrev; /* A range of registers to hold previous output */ - int savedLimit; /* Saved value of p->iLimit */ - int savedOffset; /* Saved value of p->iOffset */ - int labelCmpr; /* Label for the start of the merge algorithm */ - int labelEnd; /* Label for the end of the overall SELECT stmt */ - int j1; /* Jump instructions that get retargetted */ - int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ - KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ - KeyInfo *pKeyMerge; /* Comparison information for merging rows */ - sqlite3 *db; /* Database connection */ - ExprList *pOrderBy; /* The ORDER BY clause */ - int nOrderBy; /* Number of terms in the ORDER BY clause */ - int *aPermute; /* Mapping from ORDER BY terms to result set columns */ -#ifndef SQLITE_OMIT_EXPLAIN - int iSub1; /* EQP id of left-hand query */ - int iSub2; /* EQP id of right-hand query */ -#endif - - assert( p->pOrderBy!=0 ); - assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */ - db = pParse->db; - v = pParse->pVdbe; - assert( v!=0 ); /* Already thrown the error if VDBE alloc failed */ - labelEnd = sqlite3VdbeMakeLabel(v); - labelCmpr = sqlite3VdbeMakeLabel(v); - - - /* Patch up the ORDER BY clause - */ - op = p->op; - pPrior = p->pPrior; - assert( pPrior->pOrderBy==0 ); - pOrderBy = p->pOrderBy; - assert( pOrderBy ); - nOrderBy = pOrderBy->nExpr; - - /* For operators other than UNION ALL we have to make sure that - ** the ORDER BY clause covers every term of the result set. Add - ** terms to the ORDER BY clause as necessary. - */ - if( op!=TK_ALL ){ - for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){ - struct ExprList_item *pItem; - for(j=0, pItem=pOrderBy->a; jiOrderByCol>0 ); - if( pItem->iOrderByCol==i ) break; - } - if( j==nOrderBy ){ - Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); - if( pNew==0 ) return SQLITE_NOMEM; - pNew->flags |= EP_IntValue; - pNew->u.iValue = i; - pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew); - if( pOrderBy ) pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i; - } - } - } - - /* Compute the comparison permutation and keyinfo that is used with - ** the permutation used to determine if the next - ** row of results comes from selectA or selectB. Also add explicit - ** collations to the ORDER BY clause terms so that when the subqueries - ** to the right and the left are evaluated, they use the correct - ** collation. - */ - aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy); - if( aPermute ){ - struct ExprList_item *pItem; - for(i=0, pItem=pOrderBy->a; iiOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr ); - aPermute[i] = pItem->iOrderByCol - 1; - } - pKeyMerge = - sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1)); - if( pKeyMerge ){ - pKeyMerge->aSortOrder = (u8*)&pKeyMerge->aColl[nOrderBy]; - pKeyMerge->nField = (u16)nOrderBy; - pKeyMerge->enc = ENC(db); - for(i=0; ia[i].pExpr; - if( pTerm->flags & EP_Collate ){ - pColl = sqlite3ExprCollSeq(pParse, pTerm); - }else{ - pColl = multiSelectCollSeq(pParse, p, aPermute[i]); - if( pColl==0 ) pColl = db->pDfltColl; - pOrderBy->a[i].pExpr = - sqlite3ExprAddCollateString(pParse, pTerm, pColl->zName); - } - pKeyMerge->aColl[i] = pColl; - pKeyMerge->aSortOrder[i] = pOrderBy->a[i].sortOrder; - } - } - }else{ - pKeyMerge = 0; - } - - /* Reattach the ORDER BY clause to the query. - */ - p->pOrderBy = pOrderBy; - pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0); - - /* Allocate a range of temporary registers and the KeyInfo needed - ** for the logic that removes duplicate result rows when the - ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). - */ - if( op==TK_ALL ){ - regPrev = 0; - }else{ - int nExpr = p->pEList->nExpr; - assert( nOrderBy>=nExpr || db->mallocFailed ); - regPrev = pParse->nMem+1; - pParse->nMem += nExpr+1; - sqlite3VdbeAddOp2(v, OP_Integer, 0, regPrev); - pKeyDup = sqlite3DbMallocZero(db, - sizeof(*pKeyDup) + nExpr*(sizeof(CollSeq*)+1) ); - if( pKeyDup ){ - pKeyDup->aSortOrder = (u8*)&pKeyDup->aColl[nExpr]; - pKeyDup->nField = (u16)nExpr; - pKeyDup->enc = ENC(db); - for(i=0; iaColl[i] = multiSelectCollSeq(pParse, p, i); - pKeyDup->aSortOrder[i] = 0; - } - } - } - - /* Separate the left and the right query from one another - */ - p->pPrior = 0; - sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); - if( pPrior->pPrior==0 ){ - sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); - } - - /* Compute the limit registers */ - computeLimitRegisters(pParse, p, labelEnd); - if( p->iLimit && op==TK_ALL ){ - regLimitA = ++pParse->nMem; - regLimitB = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Copy, p->iOffset ? p->iOffset+1 : p->iLimit, - regLimitA); - sqlite3VdbeAddOp2(v, OP_Copy, regLimitA, regLimitB); - }else{ - regLimitA = regLimitB = 0; - } - sqlite3ExprDelete(db, p->pLimit); - p->pLimit = 0; - sqlite3ExprDelete(db, p->pOffset); - p->pOffset = 0; - - regAddrA = ++pParse->nMem; - regEofA = ++pParse->nMem; - regAddrB = ++pParse->nMem; - regEofB = ++pParse->nMem; - regOutA = ++pParse->nMem; - regOutB = ++pParse->nMem; - sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA); - sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB); - - /* Jump past the various subroutines and coroutines to the main - ** merge loop - */ - j1 = sqlite3VdbeAddOp0(v, OP_Goto); - addrSelectA = sqlite3VdbeCurrentAddr(v); - - - /* Generate a coroutine to evaluate the SELECT statement to the - ** left of the compound operator - the "A" select. - */ - VdbeNoopComment((v, "Begin coroutine for left SELECT")); - pPrior->iLimit = regLimitA; - explainSetInteger(iSub1, pParse->iNextSelectId); - sqlite3Select(pParse, pPrior, &destA); - sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); - VdbeNoopComment((v, "End coroutine for left SELECT")); - - /* Generate a coroutine to evaluate the SELECT statement on - ** the right - the "B" select - */ - addrSelectB = sqlite3VdbeCurrentAddr(v); - VdbeNoopComment((v, "Begin coroutine for right SELECT")); - savedLimit = p->iLimit; - savedOffset = p->iOffset; - p->iLimit = regLimitB; - p->iOffset = 0; - explainSetInteger(iSub2, pParse->iNextSelectId); - sqlite3Select(pParse, p, &destB); - p->iLimit = savedLimit; - p->iOffset = savedOffset; - sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); - VdbeNoopComment((v, "End coroutine for right SELECT")); - - /* Generate a subroutine that outputs the current row of the A - ** select as the next output row of the compound select. - */ - VdbeNoopComment((v, "Output routine for A")); - addrOutA = generateOutputSubroutine(pParse, - p, &destA, pDest, regOutA, - regPrev, pKeyDup, P4_KEYINFO_HANDOFF, labelEnd); - - /* Generate a subroutine that outputs the current row of the B - ** select as the next output row of the compound select. - */ - if( op==TK_ALL || op==TK_UNION ){ - VdbeNoopComment((v, "Output routine for B")); - addrOutB = generateOutputSubroutine(pParse, - p, &destB, pDest, regOutB, - regPrev, pKeyDup, P4_KEYINFO_STATIC, labelEnd); - } - - /* Generate a subroutine to run when the results from select A - ** are exhausted and only data in select B remains. - */ - VdbeNoopComment((v, "eof-A subroutine")); - if( op==TK_EXCEPT || op==TK_INTERSECT ){ - addrEofA = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelEnd); - }else{ - addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); - sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA); - p->nSelectRow += pPrior->nSelectRow; - } - - /* Generate a subroutine to run when the results from select B - ** are exhausted and only data in select A remains. - */ - if( op==TK_INTERSECT ){ - addrEofB = addrEofA; - if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; - }else{ - VdbeNoopComment((v, "eof-B subroutine")); - addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd); - sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB); - } - - /* Generate code to handle the case of AB - */ - VdbeNoopComment((v, "A-gt-B subroutine")); - addrAgtB = sqlite3VdbeCurrentAddr(v); - if( op==TK_ALL || op==TK_UNION ){ - sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); - } - sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); - sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB); - sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr); - - /* This code runs once to initialize everything. - */ - sqlite3VdbeJumpHere(v, j1); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofA); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofB); - sqlite3VdbeAddOp2(v, OP_Gosub, regAddrA, addrSelectA); - sqlite3VdbeAddOp2(v, OP_Gosub, regAddrB, addrSelectB); - sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA); - sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB); - - /* Implement the main merge loop - */ - sqlite3VdbeResolveLabel(v, labelCmpr); - sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); - sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy, - (char*)pKeyMerge, P4_KEYINFO_HANDOFF); - sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); - sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); - - /* Jump to the this point in order to terminate the query. - */ - sqlite3VdbeResolveLabel(v, labelEnd); - - /* Set the number of output columns - */ - if( pDest->eDest==SRT_Output ){ - Select *pFirst = pPrior; - while( pFirst->pPrior ) pFirst = pFirst->pPrior; - generateColumnNames(pParse, 0, pFirst->pEList); - } - - /* Reassembly the compound query so that it will be freed correctly - ** by the calling function */ - if( p->pPrior ){ - sqlite3SelectDelete(db, p->pPrior); - } - p->pPrior = pPrior; - - /*** TBD: Insert subroutine calls to close cursors on incomplete - **** subqueries ****/ - explainComposite(pParse, p->op, iSub1, iSub2, 0); - return SQLITE_OK; -} -#endif - -#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) -/* Forward Declarations */ -static void substExprList(sqlite3*, ExprList*, int, ExprList*); -static void substSelect(sqlite3*, Select *, int, ExprList *); - -/* -** Scan through the expression pExpr. Replace every reference to -** a column in table number iTable with a copy of the iColumn-th -** entry in pEList. (But leave references to the ROWID column -** unchanged.) -** -** This routine is part of the flattening procedure. A subquery -** whose result set is defined by pEList appears as entry in the -** FROM clause of a SELECT such that the VDBE cursor assigned to that -** FORM clause entry is iTable. This routine make the necessary -** changes to pExpr so that it refers directly to the source table -** of the subquery rather the result set of the subquery. -*/ -static Expr *substExpr( - sqlite3 *db, /* Report malloc errors to this connection */ - Expr *pExpr, /* Expr in which substitution occurs */ - int iTable, /* Table to be substituted */ - ExprList *pEList /* Substitute expressions */ -){ - if( pExpr==0 ) return 0; - if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){ - if( pExpr->iColumn<0 ){ - pExpr->op = TK_NULL; - }else{ - Expr *pNew; - assert( pEList!=0 && pExpr->iColumnnExpr ); - assert( pExpr->pLeft==0 && pExpr->pRight==0 ); - pNew = sqlite3ExprDup(db, pEList->a[pExpr->iColumn].pExpr, 0); - sqlite3ExprDelete(db, pExpr); - pExpr = pNew; - } - }else{ - pExpr->pLeft = substExpr(db, pExpr->pLeft, iTable, pEList); - pExpr->pRight = substExpr(db, pExpr->pRight, iTable, pEList); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - substSelect(db, pExpr->x.pSelect, iTable, pEList); - }else{ - substExprList(db, pExpr->x.pList, iTable, pEList); - } - } - return pExpr; -} -static void substExprList( - sqlite3 *db, /* Report malloc errors here */ - ExprList *pList, /* List to scan and in which to make substitutes */ - int iTable, /* Table to be substituted */ - ExprList *pEList /* Substitute values */ -){ - int i; - if( pList==0 ) return; - for(i=0; inExpr; i++){ - pList->a[i].pExpr = substExpr(db, pList->a[i].pExpr, iTable, pEList); - } -} -static void substSelect( - sqlite3 *db, /* Report malloc errors here */ - Select *p, /* SELECT statement in which to make substitutions */ - int iTable, /* Table to be replaced */ - ExprList *pEList /* Substitute values */ -){ - SrcList *pSrc; - struct SrcList_item *pItem; - int i; - if( !p ) return; - substExprList(db, p->pEList, iTable, pEList); - substExprList(db, p->pGroupBy, iTable, pEList); - substExprList(db, p->pOrderBy, iTable, pEList); - p->pHaving = substExpr(db, p->pHaving, iTable, pEList); - p->pWhere = substExpr(db, p->pWhere, iTable, pEList); - substSelect(db, p->pPrior, iTable, pEList); - pSrc = p->pSrc; - assert( pSrc ); /* Even for (SELECT 1) we have: pSrc!=0 but pSrc->nSrc==0 */ - if( ALWAYS(pSrc) ){ - for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ - substSelect(db, pItem->pSelect, iTable, pEList); - } - } -} -#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ - -#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) -/* -** This routine attempts to flatten subqueries as a performance optimization. -** This routine returns 1 if it makes changes and 0 if no flattening occurs. -** -** To understand the concept of flattening, consider the following -** query: -** -** SELECT a FROM (SELECT x+y AS a FROM t1 WHERE z<100) WHERE a>5 -** -** The default way of implementing this query is to execute the -** subquery first and store the results in a temporary table, then -** run the outer query on that temporary table. This requires two -** passes over the data. Furthermore, because the temporary table -** has no indices, the WHERE clause on the outer query cannot be -** optimized. -** -** This routine attempts to rewrite queries such as the above into -** a single flat select, like this: -** -** SELECT x+y AS a FROM t1 WHERE z<100 AND a>5 -** -** The code generated for this simpification gives the same result -** but only has to scan the data once. And because indices might -** exist on the table t1, a complete scan of the data might be -** avoided. -** -** Flattening is only attempted if all of the following are true: -** -** (1) The subquery and the outer query do not both use aggregates. -** -** (2) The subquery is not an aggregate or the outer query is not a join. -** -** (3) The subquery is not the right operand of a left outer join -** (Originally ticket #306. Strengthened by ticket #3300) -** -** (4) The subquery is not DISTINCT. -** -** (**) At one point restrictions (4) and (5) defined a subset of DISTINCT -** sub-queries that were excluded from this optimization. Restriction -** (4) has since been expanded to exclude all DISTINCT subqueries. -** -** (6) The subquery does not use aggregates or the outer query is not -** DISTINCT. -** -** (7) The subquery has a FROM clause. TODO: For subqueries without -** A FROM clause, consider adding a FROM close with the special -** table sqlite_once that consists of a single row containing a -** single NULL. -** -** (8) The subquery does not use LIMIT or the outer query is not a join. -** -** (9) The subquery does not use LIMIT or the outer query does not use -** aggregates. -** -** (10) The subquery does not use aggregates or the outer query does not -** use LIMIT. -** -** (11) The subquery and the outer query do not both have ORDER BY clauses. -** -** (**) Not implemented. Subsumed into restriction (3). Was previously -** a separate restriction deriving from ticket #350. -** -** (13) The subquery and outer query do not both use LIMIT. -** -** (14) The subquery does not use OFFSET. -** -** (15) The outer query is not part of a compound select or the -** subquery does not have a LIMIT clause. -** (See ticket #2339 and ticket [02a8e81d44]). -** -** (16) The outer query is not an aggregate or the subquery does -** not contain ORDER BY. (Ticket #2942) This used to not matter -** until we introduced the group_concat() function. -** -** (17) The sub-query is not a compound select, or it is a UNION ALL -** compound clause made up entirely of non-aggregate queries, and -** the parent query: -** -** * is not itself part of a compound select, -** * is not an aggregate or DISTINCT query, and -** * is not a join -** -** The parent and sub-query may contain WHERE clauses. Subject to -** rules (11), (13) and (14), they may also contain ORDER BY, -** LIMIT and OFFSET clauses. The subquery cannot use any compound -** operator other than UNION ALL because all the other compound -** operators have an implied DISTINCT which is disallowed by -** restriction (4). -** -** Also, each component of the sub-query must return the same number -** of result columns. This is actually a requirement for any compound -** SELECT statement, but all the code here does is make sure that no -** such (illegal) sub-query is flattened. The caller will detect the -** syntax error and return a detailed message. -** -** (18) If the sub-query is a compound select, then all terms of the -** ORDER by clause of the parent must be simple references to -** columns of the sub-query. -** -** (19) The subquery does not use LIMIT or the outer query does not -** have a WHERE clause. -** -** (20) If the sub-query is a compound select, then it must not use -** an ORDER BY clause. Ticket #3773. We could relax this constraint -** somewhat by saying that the terms of the ORDER BY clause must -** appear as unmodified result columns in the outer query. But we -** have other optimizations in mind to deal with that case. -** -** (21) The subquery does not use LIMIT or the outer query is not -** DISTINCT. (See ticket [752e1646fc]). -** -** In this routine, the "p" parameter is a pointer to the outer query. -** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query -** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. -** -** If flattening is not attempted, this routine is a no-op and returns 0. -** If flattening is attempted this routine returns 1. -** -** All of the expression analysis must occur on both the outer query and -** the subquery before this routine runs. -*/ -static int flattenSubquery( - Parse *pParse, /* Parsing context */ - Select *p, /* The parent or outer SELECT statement */ - int iFrom, /* Index in p->pSrc->a[] of the inner subquery */ - int isAgg, /* True if outer SELECT uses aggregate functions */ - int subqueryIsAgg /* True if the subquery uses aggregate functions */ -){ - const char *zSavedAuthContext = pParse->zAuthContext; - Select *pParent; - Select *pSub; /* The inner query or "subquery" */ - Select *pSub1; /* Pointer to the rightmost select in sub-query */ - SrcList *pSrc; /* The FROM clause of the outer query */ - SrcList *pSubSrc; /* The FROM clause of the subquery */ - ExprList *pList; /* The result set of the outer query */ - int iParent; /* VDBE cursor number of the pSub result set temp table */ - int i; /* Loop counter */ - Expr *pWhere; /* The WHERE clause */ - struct SrcList_item *pSubitem; /* The subquery */ - sqlite3 *db = pParse->db; - - /* Check to see if flattening is permitted. Return 0 if not. - */ - assert( p!=0 ); - assert( p->pPrior==0 ); /* Unable to flatten compound queries */ - if( OptimizationDisabled(db, SQLITE_QueryFlattener) ) return 0; - pSrc = p->pSrc; - assert( pSrc && iFrom>=0 && iFromnSrc ); - pSubitem = &pSrc->a[iFrom]; - iParent = pSubitem->iCursor; - pSub = pSubitem->pSelect; - assert( pSub!=0 ); - if( isAgg && subqueryIsAgg ) return 0; /* Restriction (1) */ - if( subqueryIsAgg && pSrc->nSrc>1 ) return 0; /* Restriction (2) */ - pSubSrc = pSub->pSrc; - assert( pSubSrc ); - /* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants, - ** not arbitrary expresssions, we allowed some combining of LIMIT and OFFSET - ** because they could be computed at compile-time. But when LIMIT and OFFSET - ** became arbitrary expressions, we were forced to add restrictions (13) - ** and (14). */ - if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */ - if( pSub->pOffset ) return 0; /* Restriction (14) */ - if( p->pRightmost && pSub->pLimit ){ - return 0; /* Restriction (15) */ - } - if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */ - if( pSub->selFlags & SF_Distinct ) return 0; /* Restriction (5) */ - if( pSub->pLimit && (pSrc->nSrc>1 || isAgg) ){ - return 0; /* Restrictions (8)(9) */ - } - if( (p->selFlags & SF_Distinct)!=0 && subqueryIsAgg ){ - return 0; /* Restriction (6) */ - } - if( p->pOrderBy && pSub->pOrderBy ){ - return 0; /* Restriction (11) */ - } - if( isAgg && pSub->pOrderBy ) return 0; /* Restriction (16) */ - if( pSub->pLimit && p->pWhere ) return 0; /* Restriction (19) */ - if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){ - return 0; /* Restriction (21) */ - } - - /* OBSOLETE COMMENT 1: - ** Restriction 3: If the subquery is a join, make sure the subquery is - ** not used as the right operand of an outer join. Examples of why this - ** is not allowed: - ** - ** t1 LEFT OUTER JOIN (t2 JOIN t3) - ** - ** If we flatten the above, we would get - ** - ** (t1 LEFT OUTER JOIN t2) JOIN t3 - ** - ** which is not at all the same thing. - ** - ** OBSOLETE COMMENT 2: - ** Restriction 12: If the subquery is the right operand of a left outer - ** join, make sure the subquery has no WHERE clause. - ** An examples of why this is not allowed: - ** - ** t1 LEFT OUTER JOIN (SELECT * FROM t2 WHERE t2.x>0) - ** - ** If we flatten the above, we would get - ** - ** (t1 LEFT OUTER JOIN t2) WHERE t2.x>0 - ** - ** But the t2.x>0 test will always fail on a NULL row of t2, which - ** effectively converts the OUTER JOIN into an INNER JOIN. - ** - ** THIS OVERRIDES OBSOLETE COMMENTS 1 AND 2 ABOVE: - ** Ticket #3300 shows that flattening the right term of a LEFT JOIN - ** is fraught with danger. Best to avoid the whole thing. If the - ** subquery is the right term of a LEFT JOIN, then do not flatten. - */ - if( (pSubitem->jointype & JT_OUTER)!=0 ){ - return 0; - } - - /* Restriction 17: If the sub-query is a compound SELECT, then it must - ** use only the UNION ALL operator. And none of the simple select queries - ** that make up the compound SELECT are allowed to be aggregate or distinct - ** queries. - */ - if( pSub->pPrior ){ - if( pSub->pOrderBy ){ - return 0; /* Restriction 20 */ - } - if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){ - return 0; - } - for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){ - testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ); - testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate ); - assert( pSub->pSrc!=0 ); - if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 - || (pSub1->pPrior && pSub1->op!=TK_ALL) - || pSub1->pSrc->nSrc<1 - || pSub->pEList->nExpr!=pSub1->pEList->nExpr - ){ - return 0; - } - testcase( pSub1->pSrc->nSrc>1 ); - } - - /* Restriction 18. */ - if( p->pOrderBy ){ - int ii; - for(ii=0; iipOrderBy->nExpr; ii++){ - if( p->pOrderBy->a[ii].iOrderByCol==0 ) return 0; - } - } - } - - /***** If we reach this point, flattening is permitted. *****/ - - /* Authorize the subquery */ - pParse->zAuthContext = pSubitem->zName; - TESTONLY(i =) sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0); - testcase( i==SQLITE_DENY ); - pParse->zAuthContext = zSavedAuthContext; - - /* If the sub-query is a compound SELECT statement, then (by restrictions - ** 17 and 18 above) it must be a UNION ALL and the parent query must - ** be of the form: - ** - ** SELECT FROM () - ** - ** followed by any ORDER BY, LIMIT and/or OFFSET clauses. This block - ** creates N-1 copies of the parent query without any ORDER BY, LIMIT or - ** OFFSET clauses and joins them to the left-hand-side of the original - ** using UNION ALL operators. In this case N is the number of simple - ** select statements in the compound sub-query. - ** - ** Example: - ** - ** SELECT a+1 FROM ( - ** SELECT x FROM tab - ** UNION ALL - ** SELECT y FROM tab - ** UNION ALL - ** SELECT abs(z*2) FROM tab2 - ** ) WHERE a!=5 ORDER BY 1 - ** - ** Transformed into: - ** - ** SELECT x+1 FROM tab WHERE x+1!=5 - ** UNION ALL - ** SELECT y+1 FROM tab WHERE y+1!=5 - ** UNION ALL - ** SELECT abs(z*2)+1 FROM tab2 WHERE abs(z*2)+1!=5 - ** ORDER BY 1 - ** - ** We call this the "compound-subquery flattening". - */ - for(pSub=pSub->pPrior; pSub; pSub=pSub->pPrior){ - Select *pNew; - ExprList *pOrderBy = p->pOrderBy; - Expr *pLimit = p->pLimit; - Expr *pOffset = p->pOffset; - Select *pPrior = p->pPrior; - p->pOrderBy = 0; - p->pSrc = 0; - p->pPrior = 0; - p->pLimit = 0; - p->pOffset = 0; - pNew = sqlite3SelectDup(db, p, 0); - p->pOffset = pOffset; - p->pLimit = pLimit; - p->pOrderBy = pOrderBy; - p->pSrc = pSrc; - p->op = TK_ALL; - p->pRightmost = 0; - if( pNew==0 ){ - pNew = pPrior; - }else{ - pNew->pPrior = pPrior; - pNew->pRightmost = 0; - } - p->pPrior = pNew; - if( db->mallocFailed ) return 1; - } - - /* Begin flattening the iFrom-th entry of the FROM clause - ** in the outer query. - */ - pSub = pSub1 = pSubitem->pSelect; - - /* Delete the transient table structure associated with the - ** subquery - */ - sqlite3DbFree(db, pSubitem->zDatabase); - sqlite3DbFree(db, pSubitem->zName); - sqlite3DbFree(db, pSubitem->zAlias); - pSubitem->zDatabase = 0; - pSubitem->zName = 0; - pSubitem->zAlias = 0; - pSubitem->pSelect = 0; - - /* Defer deleting the Table object associated with the - ** subquery until code generation is - ** complete, since there may still exist Expr.pTab entries that - ** refer to the subquery even after flattening. Ticket #3346. - ** - ** pSubitem->pTab is always non-NULL by test restrictions and tests above. - */ - if( ALWAYS(pSubitem->pTab!=0) ){ - Table *pTabToDel = pSubitem->pTab; - if( pTabToDel->nRef==1 ){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); - pTabToDel->pNextZombie = pToplevel->pZombieTab; - pToplevel->pZombieTab = pTabToDel; - }else{ - pTabToDel->nRef--; - } - pSubitem->pTab = 0; - } - - /* The following loop runs once for each term in a compound-subquery - ** flattening (as described above). If we are doing a different kind - ** of flattening - a flattening other than a compound-subquery flattening - - ** then this loop only runs once. - ** - ** This loop moves all of the FROM elements of the subquery into the - ** the FROM clause of the outer query. Before doing this, remember - ** the cursor number for the original outer query FROM element in - ** iParent. The iParent cursor will never be used. Subsequent code - ** will scan expressions looking for iParent references and replace - ** those references with expressions that resolve to the subquery FROM - ** elements we are now copying in. - */ - for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){ - int nSubSrc; - u8 jointype = 0; - pSubSrc = pSub->pSrc; /* FROM clause of subquery */ - nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */ - pSrc = pParent->pSrc; /* FROM clause of the outer query */ - - if( pSrc ){ - assert( pParent==p ); /* First time through the loop */ - jointype = pSubitem->jointype; - }else{ - assert( pParent!=p ); /* 2nd and subsequent times through the loop */ - pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0); - if( pSrc==0 ){ - assert( db->mallocFailed ); - break; - } - } - - /* The subquery uses a single slot of the FROM clause of the outer - ** query. If the subquery has more than one element in its FROM clause, - ** then expand the outer query to make space for it to hold all elements - ** of the subquery. - ** - ** Example: - ** - ** SELECT * FROM tabA, (SELECT * FROM sub1, sub2), tabB; - ** - ** The outer query has 3 slots in its FROM clause. One slot of the - ** outer query (the middle slot) is used by the subquery. The next - ** block of code will expand the out query to 4 slots. The middle - ** slot is expanded to two slots in order to make space for the - ** two elements in the FROM clause of the subquery. - */ - if( nSubSrc>1 ){ - pParent->pSrc = pSrc = sqlite3SrcListEnlarge(db, pSrc, nSubSrc-1,iFrom+1); - if( db->mallocFailed ){ - break; - } - } - - /* Transfer the FROM clause terms from the subquery into the - ** outer query. - */ - for(i=0; ia[i+iFrom].pUsing); - pSrc->a[i+iFrom] = pSubSrc->a[i]; - memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); - } - pSrc->a[iFrom].jointype = jointype; - - /* Now begin substituting subquery result set expressions for - ** references to the iParent in the outer query. - ** - ** Example: - ** - ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b; - ** \ \_____________ subquery __________/ / - ** \_____________________ outer query ______________________________/ - ** - ** We look at every expression in the outer query and every place we see - ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". - */ - pList = pParent->pEList; - for(i=0; inExpr; i++){ - if( pList->a[i].zName==0 ){ - char *zName = sqlite3DbStrDup(db, pList->a[i].zSpan); - sqlite3Dequote(zName); - pList->a[i].zName = zName; - } - } - substExprList(db, pParent->pEList, iParent, pSub->pEList); - if( isAgg ){ - substExprList(db, pParent->pGroupBy, iParent, pSub->pEList); - pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList); - } - if( pSub->pOrderBy ){ - assert( pParent->pOrderBy==0 ); - pParent->pOrderBy = pSub->pOrderBy; - pSub->pOrderBy = 0; - }else if( pParent->pOrderBy ){ - substExprList(db, pParent->pOrderBy, iParent, pSub->pEList); - } - if( pSub->pWhere ){ - pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); - }else{ - pWhere = 0; - } - if( subqueryIsAgg ){ - assert( pParent->pHaving==0 ); - pParent->pHaving = pParent->pWhere; - pParent->pWhere = pWhere; - pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList); - pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving, - sqlite3ExprDup(db, pSub->pHaving, 0)); - assert( pParent->pGroupBy==0 ); - pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0); - }else{ - pParent->pWhere = substExpr(db, pParent->pWhere, iParent, pSub->pEList); - pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere); - } - - /* The flattened query is distinct if either the inner or the - ** outer query is distinct. - */ - pParent->selFlags |= pSub->selFlags & SF_Distinct; - - /* - ** SELECT ... FROM (SELECT ... LIMIT a OFFSET b) LIMIT x OFFSET y; - ** - ** One is tempted to try to add a and b to combine the limits. But this - ** does not work if either limit is negative. - */ - if( pSub->pLimit ){ - pParent->pLimit = pSub->pLimit; - pSub->pLimit = 0; - } - } - - /* Finially, delete what is left of the subquery and return - ** success. - */ - sqlite3SelectDelete(db, pSub1); - - return 1; -} -#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ - -/* -** Based on the contents of the AggInfo structure indicated by the first -** argument, this function checks if the following are true: -** -** * the query contains just a single aggregate function, -** * the aggregate function is either min() or max(), and -** * the argument to the aggregate function is a column value. -** -** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX -** is returned as appropriate. Also, *ppMinMax is set to point to the -** list of arguments passed to the aggregate before returning. -** -** Or, if the conditions above are not met, *ppMinMax is set to 0 and -** WHERE_ORDERBY_NORMAL is returned. -*/ -static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){ - int eRet = WHERE_ORDERBY_NORMAL; /* Return value */ - - *ppMinMax = 0; - if( pAggInfo->nFunc==1 ){ - Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */ - ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */ - - assert( pExpr->op==TK_AGG_FUNCTION ); - if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){ - const char *zFunc = pExpr->u.zToken; - if( sqlite3StrICmp(zFunc, "min")==0 ){ - eRet = WHERE_ORDERBY_MIN; - *ppMinMax = pEList; - }else if( sqlite3StrICmp(zFunc, "max")==0 ){ - eRet = WHERE_ORDERBY_MAX; - *ppMinMax = pEList; - } - } - } - - assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 ); - return eRet; -} - -/* -** The select statement passed as the first argument is an aggregate query. -** The second argment is the associated aggregate-info object. This -** function tests if the SELECT is of the form: -** -** SELECT count(*) FROM -** -** where table is a database table, not a sub-select or view. If the query -** does match this pattern, then a pointer to the Table object representing -** is returned. Otherwise, 0 is returned. -*/ -static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ - Table *pTab; - Expr *pExpr; - - assert( !p->pGroupBy ); - - if( p->pWhere || p->pEList->nExpr!=1 - || p->pSrc->nSrc!=1 || p->pSrc->a[0].pSelect - ){ - return 0; - } - pTab = p->pSrc->a[0].pTab; - pExpr = p->pEList->a[0].pExpr; - assert( pTab && !pTab->pSelect && pExpr ); - - if( IsVirtual(pTab) ) return 0; - if( pExpr->op!=TK_AGG_FUNCTION ) return 0; - if( NEVER(pAggInfo->nFunc==0) ) return 0; - if( (pAggInfo->aFunc[0].pFunc->flags&SQLITE_FUNC_COUNT)==0 ) return 0; - if( pExpr->flags&EP_Distinct ) return 0; - - return pTab; -} - -/* -** If the source-list item passed as an argument was augmented with an -** INDEXED BY clause, then try to locate the specified index. If there -** was such a clause and the named index cannot be found, return -** SQLITE_ERROR and leave an error in pParse. Otherwise, populate -** pFrom->pIndex and return SQLITE_OK. -*/ -SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){ - if( pFrom->pTab && pFrom->zIndex ){ - Table *pTab = pFrom->pTab; - char *zIndex = pFrom->zIndex; - Index *pIdx; - for(pIdx=pTab->pIndex; - pIdx && sqlite3StrICmp(pIdx->zName, zIndex); - pIdx=pIdx->pNext - ); - if( !pIdx ){ - sqlite3ErrorMsg(pParse, "no such index: %s", zIndex, 0); - pParse->checkSchema = 1; - return SQLITE_ERROR; - } - pFrom->pIndex = pIdx; - } - return SQLITE_OK; -} - -/* -** This routine is a Walker callback for "expanding" a SELECT statement. -** "Expanding" means to do the following: -** -** (1) Make sure VDBE cursor numbers have been assigned to every -** element of the FROM clause. -** -** (2) Fill in the pTabList->a[].pTab fields in the SrcList that -** defines FROM clause. When views appear in the FROM clause, -** fill pTabList->a[].pSelect with a copy of the SELECT statement -** that implements the view. A copy is made of the view's SELECT -** statement so that we can freely modify or delete that statement -** without worrying about messing up the presistent representation -** of the view. -** -** (3) Add terms to the WHERE clause to accomodate the NATURAL keyword -** on joins and the ON and USING clause of joins. -** -** (4) Scan the list of columns in the result set (pEList) looking -** for instances of the "*" operator or the TABLE.* operator. -** If found, expand each "*" to be every column in every table -** and TABLE.* to be every column in TABLE. -** -*/ -static int selectExpander(Walker *pWalker, Select *p){ - Parse *pParse = pWalker->pParse; - int i, j, k; - SrcList *pTabList; - ExprList *pEList; - struct SrcList_item *pFrom; - sqlite3 *db = pParse->db; - Expr *pE, *pRight, *pExpr; - u16 selFlags = p->selFlags; - - p->selFlags |= SF_Expanded; - if( db->mallocFailed ){ - return WRC_Abort; - } - if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){ - return WRC_Prune; - } - pTabList = p->pSrc; - pEList = p->pEList; - - /* Make sure cursor numbers have been assigned to all entries in - ** the FROM clause of the SELECT statement. - */ - sqlite3SrcListAssignCursors(pParse, pTabList); - - /* Look up every table named in the FROM clause of the select. If - ** an entry of the FROM clause is a subquery instead of a table or view, - ** then create a transient table structure to describe the subquery. - */ - for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ - Table *pTab; - if( pFrom->pTab!=0 ){ - /* This statement has already been prepared. There is no need - ** to go further. */ - assert( i==0 ); - return WRC_Prune; - } - if( pFrom->zName==0 ){ -#ifndef SQLITE_OMIT_SUBQUERY - Select *pSel = pFrom->pSelect; - /* A sub-query in the FROM clause of a SELECT */ - assert( pSel!=0 ); - assert( pFrom->pTab==0 ); - sqlite3WalkSelect(pWalker, pSel); - pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); - if( pTab==0 ) return WRC_Abort; - pTab->nRef = 1; - pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab); - while( pSel->pPrior ){ pSel = pSel->pPrior; } - selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol); - pTab->iPKey = -1; - pTab->nRowEst = 1000000; - pTab->tabFlags |= TF_Ephemeral; -#endif - }else{ - /* An ordinary table or view name in the FROM clause */ - assert( pFrom->pTab==0 ); - pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); - if( pTab==0 ) return WRC_Abort; - if( pTab->nRef==0xffff ){ - sqlite3ErrorMsg(pParse, "too many references to \"%s\": max 65535", - pTab->zName); - pFrom->pTab = 0; - return WRC_Abort; - } - pTab->nRef++; -#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE) - if( pTab->pSelect || IsVirtual(pTab) ){ - /* We reach here if the named table is a really a view */ - if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; - assert( pFrom->pSelect==0 ); - pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0); - sqlite3WalkSelect(pWalker, pFrom->pSelect); - } -#endif - } - - /* Locate the index named by the INDEXED BY clause, if any. */ - if( sqlite3IndexedByLookup(pParse, pFrom) ){ - return WRC_Abort; - } - } - - /* Process NATURAL keywords, and ON and USING clauses of joins. - */ - if( db->mallocFailed || sqliteProcessJoin(pParse, p) ){ - return WRC_Abort; - } - - /* For every "*" that occurs in the column list, insert the names of - ** all columns in all tables. And for every TABLE.* insert the names - ** of all columns in TABLE. The parser inserted a special expression - ** with the TK_ALL operator for each "*" that it found in the column list. - ** The following code just has to locate the TK_ALL expressions and expand - ** each one to the list of all columns in all tables. - ** - ** The first loop just checks to see if there are any "*" operators - ** that need expanding. - */ - for(k=0; knExpr; k++){ - pE = pEList->a[k].pExpr; - if( pE->op==TK_ALL ) break; - assert( pE->op!=TK_DOT || pE->pRight!=0 ); - assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); - if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break; - } - if( knExpr ){ - /* - ** If we get here it means the result set contains one or more "*" - ** operators that need to be expanded. Loop through each expression - ** in the result set and expand them one by one. - */ - struct ExprList_item *a = pEList->a; - ExprList *pNew = 0; - int flags = pParse->db->flags; - int longNames = (flags & SQLITE_FullColNames)!=0 - && (flags & SQLITE_ShortColNames)==0; - - /* When processing FROM-clause subqueries, it is always the case - ** that full_column_names=OFF and short_column_names=ON. The - ** sqlite3ResultSetOfSelect() routine makes it so. */ - assert( (p->selFlags & SF_NestedFrom)==0 - || ((flags & SQLITE_FullColNames)==0 && - (flags & SQLITE_ShortColNames)!=0) ); - - for(k=0; knExpr; k++){ - pE = a[k].pExpr; - pRight = pE->pRight; - assert( pE->op!=TK_DOT || pRight!=0 ); - if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){ - /* This particular expression does not need to be expanded. - */ - pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); - if( pNew ){ - pNew->a[pNew->nExpr-1].zName = a[k].zName; - pNew->a[pNew->nExpr-1].zSpan = a[k].zSpan; - a[k].zName = 0; - a[k].zSpan = 0; - } - a[k].pExpr = 0; - }else{ - /* This expression is a "*" or a "TABLE.*" and needs to be - ** expanded. */ - int tableSeen = 0; /* Set to 1 when TABLE matches */ - char *zTName = 0; /* text of name of TABLE */ - if( pE->op==TK_DOT ){ - assert( pE->pLeft!=0 ); - assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); - zTName = pE->pLeft->u.zToken; - } - for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ - Table *pTab = pFrom->pTab; - Select *pSub = pFrom->pSelect; - char *zTabName = pFrom->zAlias; - const char *zSchemaName = 0; - int iDb; - if( zTabName==0 ){ - zTabName = pTab->zName; - } - if( db->mallocFailed ) break; - if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ - pSub = 0; - if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ - continue; - } - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*"; - } - for(j=0; jnCol; j++){ - char *zName = pTab->aCol[j].zName; - char *zColname; /* The computed column name */ - char *zToFree; /* Malloced string that needs to be freed */ - Token sColname; /* Computed column name as a token */ - - assert( zName ); - if( zTName && pSub - && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0 - ){ - continue; - } - - /* If a column is marked as 'hidden' (currently only possible - ** for virtual tables), do not include it in the expanded - ** result-set list. - */ - if( IsHiddenColumn(&pTab->aCol[j]) ){ - assert(IsVirtual(pTab)); - continue; - } - tableSeen = 1; - - if( i>0 && zTName==0 ){ - if( (pFrom->jointype & JT_NATURAL)!=0 - && tableAndColumnIndex(pTabList, i, zName, 0, 0) - ){ - /* In a NATURAL join, omit the join columns from the - ** table to the right of the join */ - continue; - } - if( sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){ - /* In a join with a USING clause, omit columns in the - ** using clause from the table on the right. */ - continue; - } - } - pRight = sqlite3Expr(db, TK_ID, zName); - zColname = zName; - zToFree = 0; - if( longNames || pTabList->nSrc>1 ){ - Expr *pLeft; - pLeft = sqlite3Expr(db, TK_ID, zTabName); - pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); - if( zSchemaName ){ - pLeft = sqlite3Expr(db, TK_ID, zSchemaName); - pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0); - } - if( longNames ){ - zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); - zToFree = zColname; - } - }else{ - pExpr = pRight; - } - pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); - sColname.z = zColname; - sColname.n = sqlite3Strlen30(zColname); - sqlite3ExprListSetName(pParse, pNew, &sColname, 0); - if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){ - struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; - if( pSub ){ - pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan); - testcase( pX->zSpan==0 ); - }else{ - pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s", - zSchemaName, zTabName, zColname); - testcase( pX->zSpan==0 ); - } - pX->bSpanIsTab = 1; - } - sqlite3DbFree(db, zToFree); - } - } - if( !tableSeen ){ - if( zTName ){ - sqlite3ErrorMsg(pParse, "no such table: %s", zTName); - }else{ - sqlite3ErrorMsg(pParse, "no tables specified"); - } - } - } - } - sqlite3ExprListDelete(db, pEList); - p->pEList = pNew; - } -#if SQLITE_MAX_COLUMN - if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){ - sqlite3ErrorMsg(pParse, "too many columns in result set"); - } -#endif - return WRC_Continue; -} - -/* -** No-op routine for the parse-tree walker. -** -** When this routine is the Walker.xExprCallback then expression trees -** are walked without any actions being taken at each node. Presumably, -** when this routine is used for Walker.xExprCallback then -** Walker.xSelectCallback is set to do something useful for every -** subquery in the parser tree. -*/ -static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return WRC_Continue; -} - -/* -** This routine "expands" a SELECT statement and all of its subqueries. -** For additional information on what it means to "expand" a SELECT -** statement, see the comment on the selectExpand worker callback above. -** -** Expanding a SELECT statement is the first step in processing a -** SELECT statement. The SELECT statement must be expanded before -** name resolution is performed. -** -** If anything goes wrong, an error message is written into pParse. -** The calling function can detect the problem by looking at pParse->nErr -** and/or pParse->db->mallocFailed. -*/ -static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ - Walker w; - w.xSelectCallback = selectExpander; - w.xExprCallback = exprWalkNoop; - w.pParse = pParse; - sqlite3WalkSelect(&w, pSelect); -} - - -#ifndef SQLITE_OMIT_SUBQUERY -/* -** This is a Walker.xSelectCallback callback for the sqlite3SelectTypeInfo() -** interface. -** -** For each FROM-clause subquery, add Column.zType and Column.zColl -** information to the Table structure that represents the result set -** of that subquery. -** -** The Table structure that represents the result set was constructed -** by selectExpander() but the type and collation information was omitted -** at that point because identifiers had not yet been resolved. This -** routine is called after identifier resolution. -*/ -static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ - Parse *pParse; - int i; - SrcList *pTabList; - struct SrcList_item *pFrom; - - assert( p->selFlags & SF_Resolved ); - if( (p->selFlags & SF_HasTypeInfo)==0 ){ - p->selFlags |= SF_HasTypeInfo; - pParse = pWalker->pParse; - pTabList = p->pSrc; - for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ - Table *pTab = pFrom->pTab; - if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){ - /* A sub-query in the FROM clause of a SELECT */ - Select *pSel = pFrom->pSelect; - assert( pSel ); - while( pSel->pPrior ) pSel = pSel->pPrior; - selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSel); - } - } - } - return WRC_Continue; -} -#endif - - -/* -** This routine adds datatype and collating sequence information to -** the Table structures of all FROM-clause subqueries in a -** SELECT statement. -** -** Use this routine after name resolution. -*/ -static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){ -#ifndef SQLITE_OMIT_SUBQUERY - Walker w; - w.xSelectCallback = selectAddSubqueryTypeInfo; - w.xExprCallback = exprWalkNoop; - w.pParse = pParse; - sqlite3WalkSelect(&w, pSelect); -#endif -} - - -/* -** This routine sets up a SELECT statement for processing. The -** following is accomplished: -** -** * VDBE Cursor numbers are assigned to all FROM-clause terms. -** * Ephemeral Table objects are created for all FROM-clause subqueries. -** * ON and USING clauses are shifted into WHERE statements -** * Wildcards "*" and "TABLE.*" in result sets are expanded. -** * Identifiers in expression are matched to tables. -** -** This routine acts recursively on all subqueries within the SELECT. -*/ -SQLITE_PRIVATE void sqlite3SelectPrep( - Parse *pParse, /* The parser context */ - Select *p, /* The SELECT statement being coded. */ - NameContext *pOuterNC /* Name context for container */ -){ - sqlite3 *db; - if( NEVER(p==0) ) return; - db = pParse->db; - if( db->mallocFailed ) return; - if( p->selFlags & SF_HasTypeInfo ) return; - sqlite3SelectExpand(pParse, p); - if( pParse->nErr || db->mallocFailed ) return; - sqlite3ResolveSelectNames(pParse, p, pOuterNC); - if( pParse->nErr || db->mallocFailed ) return; - sqlite3SelectAddTypeInfo(pParse, p); -} - -/* -** Reset the aggregate accumulator. -** -** The aggregate accumulator is a set of memory cells that hold -** intermediate results while calculating an aggregate. This -** routine generates code that stores NULLs in all of those memory -** cells. -*/ -static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ - Vdbe *v = pParse->pVdbe; - int i; - struct AggInfo_func *pFunc; - if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){ - return; - } - for(i=0; inColumn; i++){ - sqlite3VdbeAddOp2(v, OP_Null, 0, pAggInfo->aCol[i].iMem); - } - for(pFunc=pAggInfo->aFunc, i=0; inFunc; i++, pFunc++){ - sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem); - if( pFunc->iDistinct>=0 ){ - Expr *pE = pFunc->pExpr; - assert( !ExprHasProperty(pE, EP_xIsSelect) ); - if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ - sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " - "argument"); - pFunc->iDistinct = -1; - }else{ - KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList); - sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, - (char*)pKeyInfo, P4_KEYINFO_HANDOFF); - } - } - } -} - -/* -** Invoke the OP_AggFinalize opcode for every aggregate function -** in the AggInfo structure. -*/ -static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ - Vdbe *v = pParse->pVdbe; - int i; - struct AggInfo_func *pF; - for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ - ExprList *pList = pF->pExpr->x.pList; - assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); - sqlite3VdbeAddOp4(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0, 0, - (void*)pF->pFunc, P4_FUNCDEF); - } -} - -/* -** Update the accumulator memory cells for an aggregate based on -** the current cursor position. -*/ -static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ - Vdbe *v = pParse->pVdbe; - int i; - int regHit = 0; - int addrHitTest = 0; - struct AggInfo_func *pF; - struct AggInfo_col *pC; - - pAggInfo->directMode = 1; - sqlite3ExprCacheClear(pParse); - for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ - int nArg; - int addrNext = 0; - int regAgg; - ExprList *pList = pF->pExpr->x.pList; - assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); - if( pList ){ - nArg = pList->nExpr; - regAgg = sqlite3GetTempRange(pParse, nArg); - sqlite3ExprCodeExprList(pParse, pList, regAgg, 1); - }else{ - nArg = 0; - regAgg = 0; - } - if( pF->iDistinct>=0 ){ - addrNext = sqlite3VdbeMakeLabel(v); - assert( nArg==1 ); - codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg); - } - if( pF->pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ - CollSeq *pColl = 0; - struct ExprList_item *pItem; - int j; - assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */ - for(j=0, pItem=pList->a; !pColl && jpExpr); - } - if( !pColl ){ - pColl = pParse->db->pDfltColl; - } - if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; - sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ); - } - sqlite3VdbeAddOp4(v, OP_AggStep, 0, regAgg, pF->iMem, - (void*)pF->pFunc, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, (u8)nArg); - sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg); - sqlite3ReleaseTempRange(pParse, regAgg, nArg); - if( addrNext ){ - sqlite3VdbeResolveLabel(v, addrNext); - sqlite3ExprCacheClear(pParse); - } - } - - /* Before populating the accumulator registers, clear the column cache. - ** Otherwise, if any of the required column values are already present - ** in registers, sqlite3ExprCode() may use OP_SCopy to copy the value - ** to pC->iMem. But by the time the value is used, the original register - ** may have been used, invalidating the underlying buffer holding the - ** text or blob value. See ticket [883034dcb5]. - ** - ** Another solution would be to change the OP_SCopy used to copy cached - ** values to an OP_Copy. - */ - if( regHit ){ - addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); - } - sqlite3ExprCacheClear(pParse); - for(i=0, pC=pAggInfo->aCol; inAccumulator; i++, pC++){ - sqlite3ExprCode(pParse, pC->pExpr, pC->iMem); - } - pAggInfo->directMode = 0; - sqlite3ExprCacheClear(pParse); - if( addrHitTest ){ - sqlite3VdbeJumpHere(v, addrHitTest); - } -} - -/* -** Add a single OP_Explain instruction to the VDBE to explain a simple -** count(*) query ("SELECT count(*) FROM pTab"). -*/ -#ifndef SQLITE_OMIT_EXPLAIN -static void explainSimpleCount( - Parse *pParse, /* Parse context */ - Table *pTab, /* Table being queried */ - Index *pIdx /* Index used to optimize scan, or NULL */ -){ - if( pParse->explain==2 ){ - char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)", - pTab->zName, - pIdx ? "USING COVERING INDEX " : "", - pIdx ? pIdx->zName : "", - pTab->nRowEst - ); - sqlite3VdbeAddOp4( - pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC - ); - } -} -#else -# define explainSimpleCount(a,b,c) -#endif - -/* -** Generate code for the SELECT statement given in the p argument. -** -** The results are distributed in various ways depending on the -** contents of the SelectDest structure pointed to by argument pDest -** as follows: -** -** pDest->eDest Result -** ------------ ------------------------------------------- -** SRT_Output Generate a row of output (using the OP_ResultRow -** opcode) for each row in the result set. -** -** SRT_Mem Only valid if the result is a single column. -** Store the first column of the first result row -** in register pDest->iSDParm then abandon the rest -** of the query. This destination implies "LIMIT 1". -** -** SRT_Set The result must be a single column. Store each -** row of result as the key in table pDest->iSDParm. -** Apply the affinity pDest->affSdst before storing -** results. Used to implement "IN (SELECT ...)". -** -** SRT_Union Store results as a key in a temporary table -** identified by pDest->iSDParm. -** -** SRT_Except Remove results from the temporary table pDest->iSDParm. -** -** SRT_Table Store results in temporary table pDest->iSDParm. -** This is like SRT_EphemTab except that the table -** is assumed to already be open. -** -** SRT_EphemTab Create an temporary table pDest->iSDParm and store -** the result there. The cursor is left open after -** returning. This is like SRT_Table except that -** this destination uses OP_OpenEphemeral to create -** the table first. -** -** SRT_Coroutine Generate a co-routine that returns a new row of -** results each time it is invoked. The entry point -** of the co-routine is stored in register pDest->iSDParm. -** -** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result -** set is not empty. -** -** SRT_Discard Throw the results away. This is used by SELECT -** statements within triggers whose only purpose is -** the side-effects of functions. -** -** This routine returns the number of errors. If any errors are -** encountered, then an appropriate error message is left in -** pParse->zErrMsg. -** -** This routine does NOT free the Select structure passed in. The -** calling function needs to do that. -*/ -SQLITE_PRIVATE int sqlite3Select( - Parse *pParse, /* The parser context */ - Select *p, /* The SELECT statement being coded. */ - SelectDest *pDest /* What to do with the query results */ -){ - int i, j; /* Loop counters */ - WhereInfo *pWInfo; /* Return from sqlite3WhereBegin() */ - Vdbe *v; /* The virtual machine under construction */ - int isAgg; /* True for select lists like "count(*)" */ - ExprList *pEList; /* List of columns to extract. */ - SrcList *pTabList; /* List of tables to select from */ - Expr *pWhere; /* The WHERE clause. May be NULL */ - ExprList *pOrderBy; /* The ORDER BY clause. May be NULL */ - ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */ - Expr *pHaving; /* The HAVING clause. May be NULL */ - int rc = 1; /* Value to return from this function */ - int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */ - DistinctCtx sDistinct; /* Info on how to code the DISTINCT keyword */ - AggInfo sAggInfo; /* Information used by aggregate queries */ - int iEnd; /* Address of the end of the query */ - sqlite3 *db; /* The database connection */ - -#ifndef SQLITE_OMIT_EXPLAIN - int iRestoreSelectId = pParse->iSelectId; - pParse->iSelectId = pParse->iNextSelectId++; -#endif - - db = pParse->db; - if( p==0 || db->mallocFailed || pParse->nErr ){ - return 1; - } - if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; - memset(&sAggInfo, 0, sizeof(sAggInfo)); - - if( IgnorableOrderby(pDest) ){ - assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || - pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard); - /* If ORDER BY makes no difference in the output then neither does - ** DISTINCT so it can be removed too. */ - sqlite3ExprListDelete(db, p->pOrderBy); - p->pOrderBy = 0; - p->selFlags &= ~SF_Distinct; - } - sqlite3SelectPrep(pParse, p, 0); - pOrderBy = p->pOrderBy; - pTabList = p->pSrc; - pEList = p->pEList; - if( pParse->nErr || db->mallocFailed ){ - goto select_end; - } - isAgg = (p->selFlags & SF_Aggregate)!=0; - assert( pEList!=0 ); - - /* Begin generating code. - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto select_end; - - /* If writing to memory or generating a set - ** only a single column may be output. - */ -#ifndef SQLITE_OMIT_SUBQUERY - if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){ - goto select_end; - } -#endif - - /* Generate code for all sub-queries in the FROM clause - */ -#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) - for(i=0; !p->pPrior && inSrc; i++){ - struct SrcList_item *pItem = &pTabList->a[i]; - SelectDest dest; - Select *pSub = pItem->pSelect; - int isAggSub; - - if( pSub==0 ) continue; - - /* Sometimes the code for a subquery will be generated more than - ** once, if the subquery is part of the WHERE clause in a LEFT JOIN, - ** for example. In that case, do not regenerate the code to manifest - ** a view or the co-routine to implement a view. The first instance - ** is sufficient, though the subroutine to manifest the view does need - ** to be invoked again. */ - if( pItem->addrFillSub ){ - if( pItem->viaCoroutine==0 ){ - sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub); - } - continue; - } - - /* Increment Parse.nHeight by the height of the largest expression - ** tree refered to by this, the parent select. The child select - ** may contain expression trees of at most - ** (SQLITE_MAX_EXPR_DEPTH-Parse.nHeight) height. This is a bit - ** more conservative than necessary, but much easier than enforcing - ** an exact limit. - */ - pParse->nHeight += sqlite3SelectExprHeight(p); - - isAggSub = (pSub->selFlags & SF_Aggregate)!=0; - if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){ - /* This subquery can be absorbed into its parent. */ - if( isAggSub ){ - isAgg = 1; - p->selFlags |= SF_Aggregate; - } - i = -1; - }else if( pTabList->nSrc==1 && (p->selFlags & SF_Materialize)==0 - && OptimizationEnabled(db, SQLITE_SubqCoroutine) - ){ - /* Implement a co-routine that will return a single row of the result - ** set on each invocation. - */ - int addrTop; - int addrEof; - pItem->regReturn = ++pParse->nMem; - addrEof = ++pParse->nMem; - /* Before coding the OP_Goto to jump to the start of the main routine, - ** ensure that the jump to the verify-schema routine has already - ** been coded. Otherwise, the verify-schema would likely be coded as - ** part of the co-routine. If the main routine then accessed the - ** database before invoking the co-routine for the first time (for - ** example to initialize a LIMIT register from a sub-select), it would - ** be doing so without having verified the schema version and obtained - ** the required db locks. See ticket d6b36be38. */ - sqlite3CodeVerifySchema(pParse, -1); - sqlite3VdbeAddOp0(v, OP_Goto); - addrTop = sqlite3VdbeAddOp1(v, OP_OpenPseudo, pItem->iCursor); - sqlite3VdbeChangeP5(v, 1); - VdbeComment((v, "coroutine for %s", pItem->pTab->zName)); - pItem->addrFillSub = addrTop; - sqlite3VdbeAddOp2(v, OP_Integer, 0, addrEof); - sqlite3VdbeChangeP5(v, 1); - sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); - explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); - sqlite3Select(pParse, pSub, &dest); - pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow; - pItem->viaCoroutine = 1; - sqlite3VdbeChangeP2(v, addrTop, dest.iSdst); - sqlite3VdbeChangeP3(v, addrTop, dest.nSdst); - sqlite3VdbeAddOp2(v, OP_Integer, 1, addrEof); - sqlite3VdbeAddOp1(v, OP_Yield, pItem->regReturn); - VdbeComment((v, "end %s", pItem->pTab->zName)); - sqlite3VdbeJumpHere(v, addrTop-1); - sqlite3ClearTempRegCache(pParse); - }else{ - /* Generate a subroutine that will fill an ephemeral table with - ** the content of this subquery. pItem->addrFillSub will point - ** to the address of the generated subroutine. pItem->regReturn - ** is a register allocated to hold the subroutine return address - */ - int topAddr; - int onceAddr = 0; - int retAddr; - assert( pItem->addrFillSub==0 ); - pItem->regReturn = ++pParse->nMem; - topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); - pItem->addrFillSub = topAddr+1; - VdbeNoopComment((v, "materialize %s", pItem->pTab->zName)); - if( pItem->isCorrelated==0 ){ - /* If the subquery is no correlated and if we are not inside of - ** a trigger, then we only need to compute the value of the subquery - ** once. */ - onceAddr = sqlite3CodeOnce(pParse); - } - sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); - explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); - sqlite3Select(pParse, pSub, &dest); - pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow; - if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); - retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); - VdbeComment((v, "end %s", pItem->pTab->zName)); - sqlite3VdbeChangeP1(v, topAddr, retAddr); - sqlite3ClearTempRegCache(pParse); - } - if( /*pParse->nErr ||*/ db->mallocFailed ){ - goto select_end; - } - pParse->nHeight -= sqlite3SelectExprHeight(p); - pTabList = p->pSrc; - if( !IgnorableOrderby(pDest) ){ - pOrderBy = p->pOrderBy; - } - } - pEList = p->pEList; -#endif - pWhere = p->pWhere; - pGroupBy = p->pGroupBy; - pHaving = p->pHaving; - sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; - -#ifndef SQLITE_OMIT_COMPOUND_SELECT - /* If there is are a sequence of queries, do the earlier ones first. - */ - if( p->pPrior ){ - if( p->pRightmost==0 ){ - Select *pLoop, *pRight = 0; - int cnt = 0; - int mxSelect; - for(pLoop=p; pLoop; pLoop=pLoop->pPrior, cnt++){ - pLoop->pRightmost = p; - pLoop->pNext = pRight; - pRight = pLoop; - } - mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; - if( mxSelect && cnt>mxSelect ){ - sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); - goto select_end; - } - } - rc = multiSelect(pParse, p, pDest); - explainSetInteger(pParse->iSelectId, iRestoreSelectId); - return rc; - } -#endif - - /* If there is both a GROUP BY and an ORDER BY clause and they are - ** identical, then disable the ORDER BY clause since the GROUP BY - ** will cause elements to come out in the correct order. This is - ** an optimization - the correct answer should result regardless. - ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER - ** to disable this optimization for testing purposes. - */ - if( sqlite3ExprListCompare(p->pGroupBy, pOrderBy)==0 - && OptimizationEnabled(db, SQLITE_GroupByOrder) ){ - pOrderBy = 0; - } - - /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and - ** if the select-list is the same as the ORDER BY list, then this query - ** can be rewritten as a GROUP BY. In other words, this: - ** - ** SELECT DISTINCT xyz FROM ... ORDER BY xyz - ** - ** is transformed to: - ** - ** SELECT xyz FROM ... GROUP BY xyz - ** - ** The second form is preferred as a single index (or temp-table) may be - ** used for both the ORDER BY and DISTINCT processing. As originally - ** written the query must use a temp-table for at least one of the ORDER - ** BY and DISTINCT, and an index or separate temp-table for the other. - */ - if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct - && sqlite3ExprListCompare(pOrderBy, p->pEList)==0 - ){ - p->selFlags &= ~SF_Distinct; - p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0); - pGroupBy = p->pGroupBy; - pOrderBy = 0; - /* Notice that even thought SF_Distinct has been cleared from p->selFlags, - ** the sDistinct.isTnct is still set. Hence, isTnct represents the - ** original setting of the SF_Distinct flag, not the current setting */ - assert( sDistinct.isTnct ); - } - - /* If there is an ORDER BY clause, then this sorting - ** index might end up being unused if the data can be - ** extracted in pre-sorted order. If that is the case, then the - ** OP_OpenEphemeral instruction will be changed to an OP_Noop once - ** we figure out that the sorting index is not needed. The addrSortIndex - ** variable is used to facilitate that change. - */ - if( pOrderBy ){ - KeyInfo *pKeyInfo; - pKeyInfo = keyInfoFromExprList(pParse, pOrderBy); - pOrderBy->iECursor = pParse->nTab++; - p->addrOpenEphm[2] = addrSortIndex = - sqlite3VdbeAddOp4(v, OP_OpenEphemeral, - pOrderBy->iECursor, pOrderBy->nExpr+2, 0, - (char*)pKeyInfo, P4_KEYINFO_HANDOFF); - }else{ - addrSortIndex = -1; - } - - /* If the output is destined for a temporary table, open that table. - */ - if( pDest->eDest==SRT_EphemTab ){ - sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr); - } - - /* Set the limiter. - */ - iEnd = sqlite3VdbeMakeLabel(v); - p->nSelectRow = (double)LARGEST_INT64; - computeLimitRegisters(pParse, p, iEnd); - if( p->iLimit==0 && addrSortIndex>=0 ){ - sqlite3VdbeGetOp(v, addrSortIndex)->opcode = OP_SorterOpen; - p->selFlags |= SF_UseSorter; - } - - /* Open a virtual index to use for the distinct set. - */ - if( p->selFlags & SF_Distinct ){ - sDistinct.tabTnct = pParse->nTab++; - sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, - sDistinct.tabTnct, 0, 0, - (char*)keyInfoFromExprList(pParse, p->pEList), - P4_KEYINFO_HANDOFF); - sqlite3VdbeChangeP5(v, BTREE_UNORDERED); - sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED; - }else{ - sDistinct.eTnctType = WHERE_DISTINCT_NOOP; - } - - if( !isAgg && pGroupBy==0 ){ - /* No aggregate functions and no GROUP BY clause */ - ExprList *pDist = (sDistinct.isTnct ? p->pEList : 0); - - /* Begin the database scan. */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pOrderBy, pDist, 0,0); - if( pWInfo==0 ) goto select_end; - if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut; - if( pWInfo->eDistinct ) sDistinct.eTnctType = pWInfo->eDistinct; - if( pOrderBy && pWInfo->nOBSat==pOrderBy->nExpr ) pOrderBy = 0; - - /* If sorting index that was created by a prior OP_OpenEphemeral - ** instruction ended up not being needed, then change the OP_OpenEphemeral - ** into an OP_Noop. - */ - if( addrSortIndex>=0 && pOrderBy==0 ){ - sqlite3VdbeChangeToNoop(v, addrSortIndex); - p->addrOpenEphm[2] = -1; - } - - /* Use the standard inner loop. */ - selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, &sDistinct, pDest, - pWInfo->iContinue, pWInfo->iBreak); - - /* End the database scan loop. - */ - sqlite3WhereEnd(pWInfo); - }else{ - /* This case when there exist aggregate functions or a GROUP BY clause - ** or both */ - NameContext sNC; /* Name context for processing aggregate information */ - int iAMem; /* First Mem address for storing current GROUP BY */ - int iBMem; /* First Mem address for previous GROUP BY */ - int iUseFlag; /* Mem address holding flag indicating that at least - ** one row of the input to the aggregator has been - ** processed */ - int iAbortFlag; /* Mem address which causes query abort if positive */ - int groupBySort; /* Rows come from source in GROUP BY order */ - int addrEnd; /* End of processing for this SELECT */ - int sortPTab = 0; /* Pseudotable used to decode sorting results */ - int sortOut = 0; /* Output register from the sorter */ - - /* Remove any and all aliases between the result set and the - ** GROUP BY clause. - */ - if( pGroupBy ){ - int k; /* Loop counter */ - struct ExprList_item *pItem; /* For looping over expression in a list */ - - for(k=p->pEList->nExpr, pItem=p->pEList->a; k>0; k--, pItem++){ - pItem->iAlias = 0; - } - for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ - pItem->iAlias = 0; - } - if( p->nSelectRow>(double)100 ) p->nSelectRow = (double)100; - }else{ - p->nSelectRow = (double)1; - } - - - /* Create a label to jump to when we want to abort the query */ - addrEnd = sqlite3VdbeMakeLabel(v); - - /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in - ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the - ** SELECT statement. - */ - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - sNC.pSrcList = pTabList; - sNC.pAggInfo = &sAggInfo; - sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0; - sAggInfo.pGroupBy = pGroupBy; - sqlite3ExprAnalyzeAggList(&sNC, pEList); - sqlite3ExprAnalyzeAggList(&sNC, pOrderBy); - if( pHaving ){ - sqlite3ExprAnalyzeAggregates(&sNC, pHaving); - } - sAggInfo.nAccumulator = sAggInfo.nColumn; - for(i=0; ix.pList); - sNC.ncFlags &= ~NC_InAggFunc; - } - if( db->mallocFailed ) goto select_end; - - /* Processing for aggregates with GROUP BY is very different and - ** much more complex than aggregates without a GROUP BY. - */ - if( pGroupBy ){ - KeyInfo *pKeyInfo; /* Keying information for the group by clause */ - int j1; /* A-vs-B comparision jump */ - int addrOutputRow; /* Start of subroutine that outputs a result row */ - int regOutputRow; /* Return address register for output subroutine */ - int addrSetAbort; /* Set the abort flag and return */ - int addrTopOfLoop; /* Top of the input loop */ - int addrSortingIdx; /* The OP_OpenEphemeral for the sorting index */ - int addrReset; /* Subroutine for resetting the accumulator */ - int regReset; /* Return address register for reset subroutine */ - - /* If there is a GROUP BY clause we might need a sorting index to - ** implement it. Allocate that sorting index now. If it turns out - ** that we do not need it after all, the OP_SorterOpen instruction - ** will be converted into a Noop. - */ - sAggInfo.sortingIdx = pParse->nTab++; - pKeyInfo = keyInfoFromExprList(pParse, pGroupBy); - addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, - sAggInfo.sortingIdx, sAggInfo.nSortingColumn, - 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); - - /* Initialize memory locations used by GROUP BY aggregate processing - */ - iUseFlag = ++pParse->nMem; - iAbortFlag = ++pParse->nMem; - regOutputRow = ++pParse->nMem; - addrOutputRow = sqlite3VdbeMakeLabel(v); - regReset = ++pParse->nMem; - addrReset = sqlite3VdbeMakeLabel(v); - iAMem = pParse->nMem + 1; - pParse->nMem += pGroupBy->nExpr; - iBMem = pParse->nMem + 1; - pParse->nMem += pGroupBy->nExpr; - sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag); - VdbeComment((v, "clear abort flag")); - sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag); - VdbeComment((v, "indicate accumulator empty")); - sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1); - - /* Begin a loop that will extract all source rows in GROUP BY order. - ** This might involve two separate loops with an OP_Sort in between, or - ** it might be a single loop that uses an index to extract information - ** in the right order to begin with. - */ - sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, 0, 0); - if( pWInfo==0 ) goto select_end; - if( pWInfo->nOBSat==pGroupBy->nExpr ){ - /* The optimizer is able to deliver rows in group by order so - ** we do not have to sort. The OP_OpenEphemeral table will be - ** cancelled later because we still need to use the pKeyInfo - */ - groupBySort = 0; - }else{ - /* Rows are coming out in undetermined order. We have to push - ** each row into a sorting index, terminate the first loop, - ** then loop over the sorting index in order to get the output - ** in sorted order - */ - int regBase; - int regRecord; - int nCol; - int nGroupBy; - - explainTempTable(pParse, - (sDistinct.isTnct && (p->selFlags&SF_Distinct)==0) ? - "DISTINCT" : "GROUP BY"); - - groupBySort = 1; - nGroupBy = pGroupBy->nExpr; - nCol = nGroupBy + 1; - j = nGroupBy+1; - for(i=0; i=j ){ - nCol++; - j++; - } - } - regBase = sqlite3GetTempRange(pParse, nCol); - sqlite3ExprCacheClear(pParse); - sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0); - sqlite3VdbeAddOp2(v, OP_Sequence, sAggInfo.sortingIdx,regBase+nGroupBy); - j = nGroupBy+1; - for(i=0; iiSorterColumn>=j ){ - int r1 = j + regBase; - int r2; - - r2 = sqlite3ExprCodeGetColumn(pParse, - pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0); - if( r1!=r2 ){ - sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1); - } - j++; - } - } - regRecord = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord); - sqlite3VdbeAddOp2(v, OP_SorterInsert, sAggInfo.sortingIdx, regRecord); - sqlite3ReleaseTempReg(pParse, regRecord); - sqlite3ReleaseTempRange(pParse, regBase, nCol); - sqlite3WhereEnd(pWInfo); - sAggInfo.sortingIdxPTab = sortPTab = pParse->nTab++; - sortOut = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol); - sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd); - VdbeComment((v, "GROUP BY sort")); - sAggInfo.useSortingIdx = 1; - sqlite3ExprCacheClear(pParse); - } - - /* Evaluate the current GROUP BY terms and store in b0, b1, b2... - ** (b0 is memory location iBMem+0, b1 is iBMem+1, and so forth) - ** Then compare the current GROUP BY terms against the GROUP BY terms - ** from the previous row currently stored in a0, a1, a2... - */ - addrTopOfLoop = sqlite3VdbeCurrentAddr(v); - sqlite3ExprCacheClear(pParse); - if( groupBySort ){ - sqlite3VdbeAddOp2(v, OP_SorterData, sAggInfo.sortingIdx, sortOut); - } - for(j=0; jnExpr; j++){ - if( groupBySort ){ - sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j); - if( j==0 ) sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); - }else{ - sAggInfo.directMode = 1; - sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j); - } - } - sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr, - (char*)pKeyInfo, P4_KEYINFO); - j1 = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1); - - /* Generate code that runs whenever the GROUP BY changes. - ** Changes in the GROUP BY are detected by the previous code - ** block. If there were no changes, this block is skipped. - ** - ** This code copies current group by terms in b0,b1,b2,... - ** over to a0,a1,a2. It then calls the output subroutine - ** and resets the aggregate accumulator registers in preparation - ** for the next GROUP BY batch. - */ - sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr); - sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow); - VdbeComment((v, "output one row")); - sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); - VdbeComment((v, "check abort flag")); - sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); - VdbeComment((v, "reset accumulator")); - - /* Update the aggregate accumulators based on the content of - ** the current row - */ - sqlite3VdbeJumpHere(v, j1); - updateAccumulator(pParse, &sAggInfo); - sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag); - VdbeComment((v, "indicate data in accumulator")); - - /* End of the loop - */ - if( groupBySort ){ - sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop); - }else{ - sqlite3WhereEnd(pWInfo); - sqlite3VdbeChangeToNoop(v, addrSortingIdx); - } - - /* Output the final row of result - */ - sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow); - VdbeComment((v, "output final row")); - - /* Jump over the subroutines - */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEnd); - - /* Generate a subroutine that outputs a single row of the result - ** set. This subroutine first looks at the iUseFlag. If iUseFlag - ** is less than or equal to zero, the subroutine is a no-op. If - ** the processing calls for the query to abort, this subroutine - ** increments the iAbortFlag memory location before returning in - ** order to signal the caller to abort. - */ - addrSetAbort = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_Integer, 1, iAbortFlag); - VdbeComment((v, "set abort flag")); - sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); - sqlite3VdbeResolveLabel(v, addrOutputRow); - addrOutputRow = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2); - VdbeComment((v, "Groupby result generator entry point")); - sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); - finalizeAggFunctions(pParse, &sAggInfo); - sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL); - selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy, - &sDistinct, pDest, - addrOutputRow+1, addrSetAbort); - sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); - VdbeComment((v, "end groupby result generator")); - - /* Generate a subroutine that will reset the group-by accumulator - */ - sqlite3VdbeResolveLabel(v, addrReset); - resetAccumulator(pParse, &sAggInfo); - sqlite3VdbeAddOp1(v, OP_Return, regReset); - - } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ - else { - ExprList *pDel = 0; -#ifndef SQLITE_OMIT_BTREECOUNT - Table *pTab; - if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){ - /* If isSimpleCount() returns a pointer to a Table structure, then - ** the SQL statement is of the form: - ** - ** SELECT count(*) FROM - ** - ** where the Table structure returned represents table . - ** - ** This statement is so common that it is optimized specially. The - ** OP_Count instruction is executed either on the intkey table that - ** contains the data for table or on one of its indexes. It - ** is better to execute the op on an index, as indexes are almost - ** always spread across less pages than their corresponding tables. - */ - const int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - const int iCsr = pParse->nTab++; /* Cursor to scan b-tree */ - Index *pIdx; /* Iterator variable */ - KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */ - Index *pBest = 0; /* Best index found so far */ - int iRoot = pTab->tnum; /* Root page of scanned b-tree */ - - sqlite3CodeVerifySchema(pParse, iDb); - sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - - /* Search for the index that has the least amount of columns. If - ** there is such an index, and it has less columns than the table - ** does, then we can assume that it consumes less space on disk and - ** will therefore be cheaper to scan to determine the query result. - ** In this case set iRoot to the root page number of the index b-tree - ** and pKeyInfo to the KeyInfo structure required to navigate the - ** index. - ** - ** (2011-04-15) Do not do a full scan of an unordered index. - ** - ** In practice the KeyInfo structure will not be used. It is only - ** passed to keep OP_OpenRead happy. - */ - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->bUnordered==0 && (!pBest || pIdx->nColumnnColumn) ){ - pBest = pIdx; - } - } - if( pBest && pBest->nColumnnCol ){ - iRoot = pBest->tnum; - pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest); - } - - /* Open a read-only cursor, execute the OP_Count, close the cursor. */ - sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb); - if( pKeyInfo ){ - sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF); - } - sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); - sqlite3VdbeAddOp1(v, OP_Close, iCsr); - explainSimpleCount(pParse, pTab, pBest); - }else -#endif /* SQLITE_OMIT_BTREECOUNT */ - { - /* Check if the query is of one of the following forms: - ** - ** SELECT min(x) FROM ... - ** SELECT max(x) FROM ... - ** - ** If it is, then ask the code in where.c to attempt to sort results - ** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause. - ** If where.c is able to produce results sorted in this order, then - ** add vdbe code to break out of the processing loop after the - ** first iteration (since the first iteration of the loop is - ** guaranteed to operate on the row with the minimum or maximum - ** value of x, the only row required). - ** - ** A special flag must be passed to sqlite3WhereBegin() to slightly - ** modify behavior as follows: - ** - ** + If the query is a "SELECT min(x)", then the loop coded by - ** where.c should not iterate over any values with a NULL value - ** for x. - ** - ** + The optimizer code in where.c (the thing that decides which - ** index or indices to use) should place a different priority on - ** satisfying the 'ORDER BY' clause than it does in other cases. - ** Refer to code and comments in where.c for details. - */ - ExprList *pMinMax = 0; - u8 flag = WHERE_ORDERBY_NORMAL; - - assert( p->pGroupBy==0 ); - assert( flag==0 ); - if( p->pHaving==0 ){ - flag = minMaxQuery(&sAggInfo, &pMinMax); - } - assert( flag==0 || (pMinMax!=0 && pMinMax->nExpr==1) ); - - if( flag ){ - pMinMax = sqlite3ExprListDup(db, pMinMax, 0); - pDel = pMinMax; - if( pMinMax && !db->mallocFailed ){ - pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0; - pMinMax->a[0].pExpr->op = TK_COLUMN; - } - } - - /* This case runs if the aggregate has no GROUP BY clause. The - ** processing is much simpler since there is only a single row - ** of output. - */ - resetAccumulator(pParse, &sAggInfo); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMax,0,flag,0); - if( pWInfo==0 ){ - sqlite3ExprListDelete(db, pDel); - goto select_end; - } - updateAccumulator(pParse, &sAggInfo); - assert( pMinMax==0 || pMinMax->nExpr==1 ); - if( pWInfo->nOBSat>0 ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak); - VdbeComment((v, "%s() by index", - (flag==WHERE_ORDERBY_MIN?"min":"max"))); - } - sqlite3WhereEnd(pWInfo); - finalizeAggFunctions(pParse, &sAggInfo); - } - - pOrderBy = 0; - sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL); - selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, 0, - pDest, addrEnd, addrEnd); - sqlite3ExprListDelete(db, pDel); - } - sqlite3VdbeResolveLabel(v, addrEnd); - - } /* endif aggregate query */ - - if( sDistinct.eTnctType==WHERE_DISTINCT_UNORDERED ){ - explainTempTable(pParse, "DISTINCT"); - } - - /* If there is an ORDER BY clause, then we need to sort the results - ** and send them to the callback one by one. - */ - if( pOrderBy ){ - explainTempTable(pParse, "ORDER BY"); - generateSortTail(pParse, p, v, pEList->nExpr, pDest); - } - - /* Jump here to skip this query - */ - sqlite3VdbeResolveLabel(v, iEnd); - - /* The SELECT was successfully coded. Set the return code to 0 - ** to indicate no errors. - */ - rc = 0; - - /* Control jumps to here if an error is encountered above, or upon - ** successful coding of the SELECT. - */ -select_end: - explainSetInteger(pParse->iSelectId, iRestoreSelectId); - - /* Identify column names if results of the SELECT are to be output. - */ - if( rc==SQLITE_OK && pDest->eDest==SRT_Output ){ - generateColumnNames(pParse, pTabList, pEList); - } - - sqlite3DbFree(db, sAggInfo.aCol); - sqlite3DbFree(db, sAggInfo.aFunc); - return rc; -} - -#if defined(SQLITE_ENABLE_TREE_EXPLAIN) -/* -** Generate a human-readable description of a the Select object. -*/ -static void explainOneSelect(Vdbe *pVdbe, Select *p){ - sqlite3ExplainPrintf(pVdbe, "SELECT "); - if( p->selFlags & (SF_Distinct|SF_Aggregate) ){ - if( p->selFlags & SF_Distinct ){ - sqlite3ExplainPrintf(pVdbe, "DISTINCT "); - } - if( p->selFlags & SF_Aggregate ){ - sqlite3ExplainPrintf(pVdbe, "agg_flag "); - } - sqlite3ExplainNL(pVdbe); - sqlite3ExplainPrintf(pVdbe, " "); - } - sqlite3ExplainExprList(pVdbe, p->pEList); - sqlite3ExplainNL(pVdbe); - if( p->pSrc && p->pSrc->nSrc ){ - int i; - sqlite3ExplainPrintf(pVdbe, "FROM "); - sqlite3ExplainPush(pVdbe); - for(i=0; ipSrc->nSrc; i++){ - struct SrcList_item *pItem = &p->pSrc->a[i]; - sqlite3ExplainPrintf(pVdbe, "{%d,*} = ", pItem->iCursor); - if( pItem->pSelect ){ - sqlite3ExplainSelect(pVdbe, pItem->pSelect); - if( pItem->pTab ){ - sqlite3ExplainPrintf(pVdbe, " (tabname=%s)", pItem->pTab->zName); - } - }else if( pItem->zName ){ - sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName); - } - if( pItem->zAlias ){ - sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias); - } - if( pItem->jointype & JT_LEFT ){ - sqlite3ExplainPrintf(pVdbe, " LEFT-JOIN"); - } - sqlite3ExplainNL(pVdbe); - } - sqlite3ExplainPop(pVdbe); - } - if( p->pWhere ){ - sqlite3ExplainPrintf(pVdbe, "WHERE "); - sqlite3ExplainExpr(pVdbe, p->pWhere); - sqlite3ExplainNL(pVdbe); - } - if( p->pGroupBy ){ - sqlite3ExplainPrintf(pVdbe, "GROUPBY "); - sqlite3ExplainExprList(pVdbe, p->pGroupBy); - sqlite3ExplainNL(pVdbe); - } - if( p->pHaving ){ - sqlite3ExplainPrintf(pVdbe, "HAVING "); - sqlite3ExplainExpr(pVdbe, p->pHaving); - sqlite3ExplainNL(pVdbe); - } - if( p->pOrderBy ){ - sqlite3ExplainPrintf(pVdbe, "ORDERBY "); - sqlite3ExplainExprList(pVdbe, p->pOrderBy); - sqlite3ExplainNL(pVdbe); - } - if( p->pLimit ){ - sqlite3ExplainPrintf(pVdbe, "LIMIT "); - sqlite3ExplainExpr(pVdbe, p->pLimit); - sqlite3ExplainNL(pVdbe); - } - if( p->pOffset ){ - sqlite3ExplainPrintf(pVdbe, "OFFSET "); - sqlite3ExplainExpr(pVdbe, p->pOffset); - sqlite3ExplainNL(pVdbe); - } -} -SQLITE_PRIVATE void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){ - if( p==0 ){ - sqlite3ExplainPrintf(pVdbe, "(null-select)"); - return; - } - while( p->pPrior ){ - p->pPrior->pNext = p; - p = p->pPrior; - } - sqlite3ExplainPush(pVdbe); - while( p ){ - explainOneSelect(pVdbe, p); - p = p->pNext; - if( p==0 ) break; - sqlite3ExplainNL(pVdbe); - sqlite3ExplainPrintf(pVdbe, "%s\n", selectOpName(p->op)); - } - sqlite3ExplainPrintf(pVdbe, "END"); - sqlite3ExplainPop(pVdbe); -} - -/* End of the structure debug printing code -*****************************************************************************/ -#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */ - -/************** End of select.c **********************************************/ -/************** Begin file table.c *******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains the sqlite3_get_table() and sqlite3_free_table() -** interface routines. These are just wrappers around the main -** interface routine of sqlite3_exec(). -** -** These routines are in a separate files so that they will not be linked -** if they are not used. -*/ -/* #include */ -/* #include */ - -#ifndef SQLITE_OMIT_GET_TABLE - -/* -** This structure is used to pass data from sqlite3_get_table() through -** to the callback function is uses to build the result. -*/ -typedef struct TabResult { - char **azResult; /* Accumulated output */ - char *zErrMsg; /* Error message text, if an error occurs */ - int nAlloc; /* Slots allocated for azResult[] */ - int nRow; /* Number of rows in the result */ - int nColumn; /* Number of columns in the result */ - int nData; /* Slots used in azResult[]. (nRow+1)*nColumn */ - int rc; /* Return code from sqlite3_exec() */ -} TabResult; - -/* -** This routine is called once for each row in the result table. Its job -** is to fill in the TabResult structure appropriately, allocating new -** memory as necessary. -*/ -static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){ - TabResult *p = (TabResult*)pArg; /* Result accumulator */ - int need; /* Slots needed in p->azResult[] */ - int i; /* Loop counter */ - char *z; /* A single column of result */ - - /* Make sure there is enough space in p->azResult to hold everything - ** we need to remember from this invocation of the callback. - */ - if( p->nRow==0 && argv!=0 ){ - need = nCol*2; - }else{ - need = nCol; - } - if( p->nData + need > p->nAlloc ){ - char **azNew; - p->nAlloc = p->nAlloc*2 + need; - azNew = sqlite3_realloc( p->azResult, sizeof(char*)*p->nAlloc ); - if( azNew==0 ) goto malloc_failed; - p->azResult = azNew; - } - - /* If this is the first row, then generate an extra row containing - ** the names of all columns. - */ - if( p->nRow==0 ){ - p->nColumn = nCol; - for(i=0; iazResult[p->nData++] = z; - } - }else if( p->nColumn!=nCol ){ - sqlite3_free(p->zErrMsg); - p->zErrMsg = sqlite3_mprintf( - "sqlite3_get_table() called with two or more incompatible queries" - ); - p->rc = SQLITE_ERROR; - return 1; - } - - /* Copy over the row data - */ - if( argv!=0 ){ - for(i=0; iazResult[p->nData++] = z; - } - p->nRow++; - } - return 0; - -malloc_failed: - p->rc = SQLITE_NOMEM; - return 1; -} - -/* -** Query the database. But instead of invoking a callback for each row, -** malloc() for space to hold the result and return the entire results -** at the conclusion of the call. -** -** The result that is written to ***pazResult is held in memory obtained -** from malloc(). But the caller cannot free this memory directly. -** Instead, the entire table should be passed to sqlite3_free_table() when -** the calling procedure is finished using it. -*/ -SQLITE_API int sqlite3_get_table( - sqlite3 *db, /* The database on which the SQL executes */ - const char *zSql, /* The SQL to be executed */ - char ***pazResult, /* Write the result table here */ - int *pnRow, /* Write the number of rows in the result here */ - int *pnColumn, /* Write the number of columns of result here */ - char **pzErrMsg /* Write error messages here */ -){ - int rc; - TabResult res; - - *pazResult = 0; - if( pnColumn ) *pnColumn = 0; - if( pnRow ) *pnRow = 0; - if( pzErrMsg ) *pzErrMsg = 0; - res.zErrMsg = 0; - res.nRow = 0; - res.nColumn = 0; - res.nData = 1; - res.nAlloc = 20; - res.rc = SQLITE_OK; - res.azResult = sqlite3_malloc(sizeof(char*)*res.nAlloc ); - if( res.azResult==0 ){ - db->errCode = SQLITE_NOMEM; - return SQLITE_NOMEM; - } - res.azResult[0] = 0; - rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg); - assert( sizeof(res.azResult[0])>= sizeof(res.nData) ); - res.azResult[0] = SQLITE_INT_TO_PTR(res.nData); - if( (rc&0xff)==SQLITE_ABORT ){ - sqlite3_free_table(&res.azResult[1]); - if( res.zErrMsg ){ - if( pzErrMsg ){ - sqlite3_free(*pzErrMsg); - *pzErrMsg = sqlite3_mprintf("%s",res.zErrMsg); - } - sqlite3_free(res.zErrMsg); - } - db->errCode = res.rc; /* Assume 32-bit assignment is atomic */ - return res.rc; - } - sqlite3_free(res.zErrMsg); - if( rc!=SQLITE_OK ){ - sqlite3_free_table(&res.azResult[1]); - return rc; - } - if( res.nAlloc>res.nData ){ - char **azNew; - azNew = sqlite3_realloc( res.azResult, sizeof(char*)*res.nData ); - if( azNew==0 ){ - sqlite3_free_table(&res.azResult[1]); - db->errCode = SQLITE_NOMEM; - return SQLITE_NOMEM; - } - res.azResult = azNew; - } - *pazResult = &res.azResult[1]; - if( pnColumn ) *pnColumn = res.nColumn; - if( pnRow ) *pnRow = res.nRow; - return rc; -} - -/* -** This routine frees the space the sqlite3_get_table() malloced. -*/ -SQLITE_API void sqlite3_free_table( - char **azResult /* Result returned from from sqlite3_get_table() */ -){ - if( azResult ){ - int i, n; - azResult--; - assert( azResult!=0 ); - n = SQLITE_PTR_TO_INT(azResult[0]); - for(i=1; ipNext; - - sqlite3ExprDelete(db, pTmp->pWhere); - sqlite3ExprListDelete(db, pTmp->pExprList); - sqlite3SelectDelete(db, pTmp->pSelect); - sqlite3IdListDelete(db, pTmp->pIdList); - - sqlite3DbFree(db, pTmp); - } -} - -/* -** Given table pTab, return a list of all the triggers attached to -** the table. The list is connected by Trigger.pNext pointers. -** -** All of the triggers on pTab that are in the same database as pTab -** are already attached to pTab->pTrigger. But there might be additional -** triggers on pTab in the TEMP schema. This routine prepends all -** TEMP triggers on pTab to the beginning of the pTab->pTrigger list -** and returns the combined list. -** -** To state it another way: This routine returns a list of all triggers -** that fire off of pTab. The list will include any TEMP triggers on -** pTab as well as the triggers lised in pTab->pTrigger. -*/ -SQLITE_PRIVATE Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ - Schema * const pTmpSchema = pParse->db->aDb[1].pSchema; - Trigger *pList = 0; /* List of triggers to return */ - - if( pParse->disableTriggers ){ - return 0; - } - - if( pTmpSchema!=pTab->pSchema ){ - HashElem *p; - assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) ); - for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ - Trigger *pTrig = (Trigger *)sqliteHashData(p); - if( pTrig->pTabSchema==pTab->pSchema - && 0==sqlite3StrICmp(pTrig->table, pTab->zName) - ){ - pTrig->pNext = (pList ? pList : pTab->pTrigger); - pList = pTrig; - } - } - } - - return (pList ? pList : pTab->pTrigger); -} - -/* -** This is called by the parser when it sees a CREATE TRIGGER statement -** up to the point of the BEGIN before the trigger actions. A Trigger -** structure is generated based on the information available and stored -** in pParse->pNewTrigger. After the trigger actions have been parsed, the -** sqlite3FinishTrigger() function is called to complete the trigger -** construction process. -*/ -SQLITE_PRIVATE void sqlite3BeginTrigger( - Parse *pParse, /* The parse context of the CREATE TRIGGER statement */ - Token *pName1, /* The name of the trigger */ - Token *pName2, /* The name of the trigger */ - int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ - int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ - IdList *pColumns, /* column list if this is an UPDATE OF trigger */ - SrcList *pTableName,/* The name of the table/view the trigger applies to */ - Expr *pWhen, /* WHEN clause */ - int isTemp, /* True if the TEMPORARY keyword is present */ - int noErr /* Suppress errors if the trigger already exists */ -){ - Trigger *pTrigger = 0; /* The new trigger */ - Table *pTab; /* Table that the trigger fires off of */ - char *zName = 0; /* Name of the trigger */ - sqlite3 *db = pParse->db; /* The database connection */ - int iDb; /* The database to store the trigger in */ - Token *pName; /* The unqualified db name */ - DbFixer sFix; /* State vector for the DB fixer */ - int iTabDb; /* Index of the database holding pTab */ - - assert( pName1!=0 ); /* pName1->z might be NULL, but not pName1 itself */ - assert( pName2!=0 ); - assert( op==TK_INSERT || op==TK_UPDATE || op==TK_DELETE ); - assert( op>0 && op<0xff ); - if( isTemp ){ - /* If TEMP was specified, then the trigger name may not be qualified. */ - if( pName2->n>0 ){ - sqlite3ErrorMsg(pParse, "temporary trigger may not have qualified name"); - goto trigger_cleanup; - } - iDb = 1; - pName = pName1; - }else{ - /* Figure out the db that the trigger will be created in */ - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); - if( iDb<0 ){ - goto trigger_cleanup; - } - } - if( !pTableName || db->mallocFailed ){ - goto trigger_cleanup; - } - - /* A long-standing parser bug is that this syntax was allowed: - ** - ** CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab .... - ** ^^^^^^^^ - ** - ** To maintain backwards compatibility, ignore the database - ** name on pTableName if we are reparsing our of SQLITE_MASTER. - */ - if( db->init.busy && iDb!=1 ){ - sqlite3DbFree(db, pTableName->a[0].zDatabase); - pTableName->a[0].zDatabase = 0; - } - - /* If the trigger name was unqualified, and the table is a temp table, - ** then set iDb to 1 to create the trigger in the temporary database. - ** If sqlite3SrcListLookup() returns 0, indicating the table does not - ** exist, the error is caught by the block below. - */ - pTab = sqlite3SrcListLookup(pParse, pTableName); - if( db->init.busy==0 && pName2->n==0 && pTab - && pTab->pSchema==db->aDb[1].pSchema ){ - iDb = 1; - } - - /* Ensure the table name matches database name and that the table exists */ - if( db->mallocFailed ) goto trigger_cleanup; - assert( pTableName->nSrc==1 ); - if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) && - sqlite3FixSrcList(&sFix, pTableName) ){ - goto trigger_cleanup; - } - pTab = sqlite3SrcListLookup(pParse, pTableName); - if( !pTab ){ - /* The table does not exist. */ - if( db->init.iDb==1 ){ - /* Ticket #3810. - ** Normally, whenever a table is dropped, all associated triggers are - ** dropped too. But if a TEMP trigger is created on a non-TEMP table - ** and the table is dropped by a different database connection, the - ** trigger is not visible to the database connection that does the - ** drop so the trigger cannot be dropped. This results in an - ** "orphaned trigger" - a trigger whose associated table is missing. - */ - db->init.orphanTrigger = 1; - } - goto trigger_cleanup; - } - if( IsVirtual(pTab) ){ - sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); - goto trigger_cleanup; - } - - /* Check that the trigger name is not reserved and that no trigger of the - ** specified name exists */ - zName = sqlite3NameFromToken(db, pName); - if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ - goto trigger_cleanup; - } - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash), - zName, sqlite3Strlen30(zName)) ){ - if( !noErr ){ - sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); - }else{ - assert( !db->init.busy ); - sqlite3CodeVerifySchema(pParse, iDb); - } - goto trigger_cleanup; - } - - /* Do not create a trigger on a system table */ - if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){ - sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); - pParse->nErr++; - goto trigger_cleanup; - } - - /* INSTEAD of triggers are only for views and views only support INSTEAD - ** of triggers. - */ - if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ - sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", - (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); - goto trigger_cleanup; - } - if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ - sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" - " trigger on table: %S", pTableName, 0); - goto trigger_cleanup; - } - iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); - -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int code = SQLITE_CREATE_TRIGGER; - const char *zDb = db->aDb[iTabDb].zName; - const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb; - if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER; - if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){ - goto trigger_cleanup; - } - if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iTabDb),0,zDb)){ - goto trigger_cleanup; - } - } -#endif - - /* INSTEAD OF triggers can only appear on views and BEFORE triggers - ** cannot appear on views. So we might as well translate every - ** INSTEAD OF trigger into a BEFORE trigger. It simplifies code - ** elsewhere. - */ - if (tr_tm == TK_INSTEAD){ - tr_tm = TK_BEFORE; - } - - /* Build the Trigger object */ - pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger)); - if( pTrigger==0 ) goto trigger_cleanup; - pTrigger->zName = zName; - zName = 0; - pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName); - pTrigger->pSchema = db->aDb[iDb].pSchema; - pTrigger->pTabSchema = pTab->pSchema; - pTrigger->op = (u8)op; - pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; - pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); - pTrigger->pColumns = sqlite3IdListDup(db, pColumns); - assert( pParse->pNewTrigger==0 ); - pParse->pNewTrigger = pTrigger; - -trigger_cleanup: - sqlite3DbFree(db, zName); - sqlite3SrcListDelete(db, pTableName); - sqlite3IdListDelete(db, pColumns); - sqlite3ExprDelete(db, pWhen); - if( !pParse->pNewTrigger ){ - sqlite3DeleteTrigger(db, pTrigger); - }else{ - assert( pParse->pNewTrigger==pTrigger ); - } -} - -/* -** This routine is called after all of the trigger actions have been parsed -** in order to complete the process of building the trigger. -*/ -SQLITE_PRIVATE void sqlite3FinishTrigger( - Parse *pParse, /* Parser context */ - TriggerStep *pStepList, /* The triggered program */ - Token *pAll /* Token that describes the complete CREATE TRIGGER */ -){ - Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */ - char *zName; /* Name of trigger */ - sqlite3 *db = pParse->db; /* The database */ - DbFixer sFix; /* Fixer object */ - int iDb; /* Database containing the trigger */ - Token nameToken; /* Trigger name for error reporting */ - - pParse->pNewTrigger = 0; - if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; - zName = pTrig->zName; - iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); - pTrig->step_list = pStepList; - while( pStepList ){ - pStepList->pTrig = pTrig; - pStepList = pStepList->pNext; - } - nameToken.z = pTrig->zName; - nameToken.n = sqlite3Strlen30(nameToken.z); - if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken) - && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){ - goto triggerfinish_cleanup; - } - - /* if we are not initializing, - ** build the sqlite_master entry - */ - if( !db->init.busy ){ - Vdbe *v; - char *z; - - /* Make an entry in the sqlite_master table */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto triggerfinish_cleanup; - sqlite3BeginWriteOperation(pParse, 0, iDb); - z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); - sqlite3NestedParse(pParse, - "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName, - pTrig->table, z); - sqlite3DbFree(db, z); - sqlite3ChangeCookie(pParse, iDb); - sqlite3VdbeAddParseSchemaOp(v, iDb, - sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName)); - } - - if( db->init.busy ){ - Trigger *pLink = pTrig; - Hash *pHash = &db->aDb[iDb].pSchema->trigHash; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig); - if( pTrig ){ - db->mallocFailed = 1; - }else if( pLink->pSchema==pLink->pTabSchema ){ - Table *pTab; - int n = sqlite3Strlen30(pLink->table); - pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table, n); - assert( pTab!=0 ); - pLink->pNext = pTab->pTrigger; - pTab->pTrigger = pLink; - } - } - -triggerfinish_cleanup: - sqlite3DeleteTrigger(db, pTrig); - assert( !pParse->pNewTrigger ); - sqlite3DeleteTriggerStep(db, pStepList); -} - -/* -** Turn a SELECT statement (that the pSelect parameter points to) into -** a trigger step. Return a pointer to a TriggerStep structure. -** -** The parser calls this routine when it finds a SELECT statement in -** body of a TRIGGER. -*/ -SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3 *db, Select *pSelect){ - TriggerStep *pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep)); - if( pTriggerStep==0 ) { - sqlite3SelectDelete(db, pSelect); - return 0; - } - pTriggerStep->op = TK_SELECT; - pTriggerStep->pSelect = pSelect; - pTriggerStep->orconf = OE_Default; - return pTriggerStep; -} - -/* -** Allocate space to hold a new trigger step. The allocated space -** holds both the TriggerStep object and the TriggerStep.target.z string. -** -** If an OOM error occurs, NULL is returned and db->mallocFailed is set. -*/ -static TriggerStep *triggerStepAllocate( - sqlite3 *db, /* Database connection */ - u8 op, /* Trigger opcode */ - Token *pName /* The target name */ -){ - TriggerStep *pTriggerStep; - - pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n); - if( pTriggerStep ){ - char *z = (char*)&pTriggerStep[1]; - memcpy(z, pName->z, pName->n); - pTriggerStep->target.z = z; - pTriggerStep->target.n = pName->n; - pTriggerStep->op = op; - } - return pTriggerStep; -} - -/* -** Build a trigger step out of an INSERT statement. Return a pointer -** to the new trigger step. -** -** The parser calls this routine when it sees an INSERT inside the -** body of a trigger. -*/ -SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep( - sqlite3 *db, /* The database connection */ - Token *pTableName, /* Name of the table into which we insert */ - IdList *pColumn, /* List of columns in pTableName to insert into */ - ExprList *pEList, /* The VALUE clause: a list of values to be inserted */ - Select *pSelect, /* A SELECT statement that supplies values */ - u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ -){ - TriggerStep *pTriggerStep; - - assert(pEList == 0 || pSelect == 0); - assert(pEList != 0 || pSelect != 0 || db->mallocFailed); - - pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName); - if( pTriggerStep ){ - pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); - pTriggerStep->pIdList = pColumn; - pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); - pTriggerStep->orconf = orconf; - }else{ - sqlite3IdListDelete(db, pColumn); - } - sqlite3ExprListDelete(db, pEList); - sqlite3SelectDelete(db, pSelect); - - return pTriggerStep; -} - -/* -** Construct a trigger step that implements an UPDATE statement and return -** a pointer to that trigger step. The parser calls this routine when it -** sees an UPDATE statement inside the body of a CREATE TRIGGER. -*/ -SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep( - sqlite3 *db, /* The database connection */ - Token *pTableName, /* Name of the table to be updated */ - ExprList *pEList, /* The SET clause: list of column and new values */ - Expr *pWhere, /* The WHERE clause */ - u8 orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ -){ - TriggerStep *pTriggerStep; - - pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName); - if( pTriggerStep ){ - pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); - pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); - pTriggerStep->orconf = orconf; - } - sqlite3ExprListDelete(db, pEList); - sqlite3ExprDelete(db, pWhere); - return pTriggerStep; -} - -/* -** Construct a trigger step that implements a DELETE statement and return -** a pointer to that trigger step. The parser calls this routine when it -** sees a DELETE statement inside the body of a CREATE TRIGGER. -*/ -SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep( - sqlite3 *db, /* Database connection */ - Token *pTableName, /* The table from which rows are deleted */ - Expr *pWhere /* The WHERE clause */ -){ - TriggerStep *pTriggerStep; - - pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName); - if( pTriggerStep ){ - pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); - pTriggerStep->orconf = OE_Default; - } - sqlite3ExprDelete(db, pWhere); - return pTriggerStep; -} - -/* -** Recursively delete a Trigger structure -*/ -SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){ - if( pTrigger==0 ) return; - sqlite3DeleteTriggerStep(db, pTrigger->step_list); - sqlite3DbFree(db, pTrigger->zName); - sqlite3DbFree(db, pTrigger->table); - sqlite3ExprDelete(db, pTrigger->pWhen); - sqlite3IdListDelete(db, pTrigger->pColumns); - sqlite3DbFree(db, pTrigger); -} - -/* -** This function is called to drop a trigger from the database schema. -** -** This may be called directly from the parser and therefore identifies -** the trigger by name. The sqlite3DropTriggerPtr() routine does the -** same job as this routine except it takes a pointer to the trigger -** instead of the trigger name. -**/ -SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){ - Trigger *pTrigger = 0; - int i; - const char *zDb; - const char *zName; - int nName; - sqlite3 *db = pParse->db; - - if( db->mallocFailed ) goto drop_trigger_cleanup; - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - goto drop_trigger_cleanup; - } - - assert( pName->nSrc==1 ); - zDb = pName->a[0].zDatabase; - zName = pName->a[0].zName; - nName = sqlite3Strlen30(zName); - assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); - for(i=OMIT_TEMPDB; inDb; i++){ - int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ - if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue; - assert( sqlite3SchemaMutexHeld(db, j, 0) ); - pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName); - if( pTrigger ) break; - } - if( !pTrigger ){ - if( !noErr ){ - sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); - }else{ - sqlite3CodeVerifyNamedSchema(pParse, zDb); - } - pParse->checkSchema = 1; - goto drop_trigger_cleanup; - } - sqlite3DropTriggerPtr(pParse, pTrigger); - -drop_trigger_cleanup: - sqlite3SrcListDelete(db, pName); -} - -/* -** Return a pointer to the Table structure for the table that a trigger -** is set on. -*/ -static Table *tableOfTrigger(Trigger *pTrigger){ - int n = sqlite3Strlen30(pTrigger->table); - return sqlite3HashFind(&pTrigger->pTabSchema->tblHash, pTrigger->table, n); -} - - -/* -** Drop a trigger given a pointer to that trigger. -*/ -SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ - Table *pTable; - Vdbe *v; - sqlite3 *db = pParse->db; - int iDb; - - iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema); - assert( iDb>=0 && iDbnDb ); - pTable = tableOfTrigger(pTrigger); - assert( pTable ); - assert( pTable->pSchema==pTrigger->pSchema || iDb==1 ); -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int code = SQLITE_DROP_TRIGGER; - const char *zDb = db->aDb[iDb].zName; - const char *zTab = SCHEMA_TABLE(iDb); - if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER; - if( sqlite3AuthCheck(pParse, code, pTrigger->zName, pTable->zName, zDb) || - sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ - return; - } - } -#endif - - /* Generate code to destroy the database record of the trigger. - */ - assert( pTable!=0 ); - if( (v = sqlite3GetVdbe(pParse))!=0 ){ - int base; - static const VdbeOpList dropTrigger[] = { - { OP_Rewind, 0, ADDR(9), 0}, - { OP_String8, 0, 1, 0}, /* 1 */ - { OP_Column, 0, 1, 2}, - { OP_Ne, 2, ADDR(8), 1}, - { OP_String8, 0, 1, 0}, /* 4: "trigger" */ - { OP_Column, 0, 0, 2}, - { OP_Ne, 2, ADDR(8), 1}, - { OP_Delete, 0, 0, 0}, - { OP_Next, 0, ADDR(1), 0}, /* 8 */ - }; - - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3OpenMasterTable(pParse, iDb); - base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); - sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, P4_TRANSIENT); - sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC); - sqlite3ChangeCookie(pParse, iDb); - sqlite3VdbeAddOp2(v, OP_Close, 0, 0); - sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0); - if( pParse->nMem<3 ){ - pParse->nMem = 3; - } - } -} - -/* -** Remove a trigger from the hash tables of the sqlite* pointer. -*/ -SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){ - Trigger *pTrigger; - Hash *pHash; - - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - pHash = &(db->aDb[iDb].pSchema->trigHash); - pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0); - if( ALWAYS(pTrigger) ){ - if( pTrigger->pSchema==pTrigger->pTabSchema ){ - Table *pTab = tableOfTrigger(pTrigger); - Trigger **pp; - for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext)); - *pp = (*pp)->pNext; - } - sqlite3DeleteTrigger(db, pTrigger); - db->flags |= SQLITE_InternChanges; - } -} - -/* -** pEList is the SET clause of an UPDATE statement. Each entry -** in pEList is of the format =. If any of the entries -** in pEList have an which matches an identifier in pIdList, -** then return TRUE. If pIdList==NULL, then it is considered a -** wildcard that matches anything. Likewise if pEList==NULL then -** it matches anything so always return true. Return false only -** if there is no match. -*/ -static int checkColumnOverlap(IdList *pIdList, ExprList *pEList){ - int e; - if( pIdList==0 || NEVER(pEList==0) ) return 1; - for(e=0; enExpr; e++){ - if( sqlite3IdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1; - } - return 0; -} - -/* -** Return a list of all triggers on table pTab if there exists at least -** one trigger that must be fired when an operation of type 'op' is -** performed on the table, and, if that operation is an UPDATE, if at -** least one of the columns in pChanges is being modified. -*/ -SQLITE_PRIVATE Trigger *sqlite3TriggersExist( - Parse *pParse, /* Parse context */ - Table *pTab, /* The table the contains the triggers */ - int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ - ExprList *pChanges, /* Columns that change in an UPDATE statement */ - int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ -){ - int mask = 0; - Trigger *pList = 0; - Trigger *p; - - if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){ - pList = sqlite3TriggerList(pParse, pTab); - } - assert( pList==0 || IsVirtual(pTab)==0 ); - for(p=pList; p; p=p->pNext){ - if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ - mask |= p->tr_tm; - } - } - if( pMask ){ - *pMask = mask; - } - return (mask ? pList : 0); -} - -/* -** Convert the pStep->target token into a SrcList and return a pointer -** to that SrcList. -** -** This routine adds a specific database name, if needed, to the target when -** forming the SrcList. This prevents a trigger in one database from -** referring to a target in another database. An exception is when the -** trigger is in TEMP in which case it can refer to any other database it -** wants. -*/ -static SrcList *targetSrcList( - Parse *pParse, /* The parsing context */ - TriggerStep *pStep /* The trigger containing the target token */ -){ - int iDb; /* Index of the database to use */ - SrcList *pSrc; /* SrcList to be returned */ - - pSrc = sqlite3SrcListAppend(pParse->db, 0, &pStep->target, 0); - if( pSrc ){ - assert( pSrc->nSrc>0 ); - assert( pSrc->a!=0 ); - iDb = sqlite3SchemaToIndex(pParse->db, pStep->pTrig->pSchema); - if( iDb==0 || iDb>=2 ){ - sqlite3 *db = pParse->db; - assert( iDbdb->nDb ); - pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName); - } - } - return pSrc; -} - -/* -** Generate VDBE code for the statements inside the body of a single -** trigger. -*/ -static int codeTriggerProgram( - Parse *pParse, /* The parser context */ - TriggerStep *pStepList, /* List of statements inside the trigger body */ - int orconf /* Conflict algorithm. (OE_Abort, etc) */ -){ - TriggerStep *pStep; - Vdbe *v = pParse->pVdbe; - sqlite3 *db = pParse->db; - - assert( pParse->pTriggerTab && pParse->pToplevel ); - assert( pStepList ); - assert( v!=0 ); - for(pStep=pStepList; pStep; pStep=pStep->pNext){ - /* Figure out the ON CONFLICT policy that will be used for this step - ** of the trigger program. If the statement that caused this trigger - ** to fire had an explicit ON CONFLICT, then use it. Otherwise, use - ** the ON CONFLICT policy that was specified as part of the trigger - ** step statement. Example: - ** - ** CREATE TRIGGER AFTER INSERT ON t1 BEGIN; - ** INSERT OR REPLACE INTO t2 VALUES(new.a, new.b); - ** END; - ** - ** INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy - ** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy - */ - pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf; - - /* Clear the cookieGoto flag. When coding triggers, the cookieGoto - ** variable is used as a flag to indicate to sqlite3ExprCodeConstants() - ** that it is not safe to refactor constants (this happens after the - ** start of the first loop in the SQL statement is coded - at that - ** point code may be conditionally executed, so it is no longer safe to - ** initialize constant register values). */ - assert( pParse->cookieGoto==0 || pParse->cookieGoto==-1 ); - pParse->cookieGoto = 0; - - switch( pStep->op ){ - case TK_UPDATE: { - sqlite3Update(pParse, - targetSrcList(pParse, pStep), - sqlite3ExprListDup(db, pStep->pExprList, 0), - sqlite3ExprDup(db, pStep->pWhere, 0), - pParse->eOrconf - ); - break; - } - case TK_INSERT: { - sqlite3Insert(pParse, - targetSrcList(pParse, pStep), - sqlite3ExprListDup(db, pStep->pExprList, 0), - sqlite3SelectDup(db, pStep->pSelect, 0), - sqlite3IdListDup(db, pStep->pIdList), - pParse->eOrconf - ); - break; - } - case TK_DELETE: { - sqlite3DeleteFrom(pParse, - targetSrcList(pParse, pStep), - sqlite3ExprDup(db, pStep->pWhere, 0) - ); - break; - } - default: assert( pStep->op==TK_SELECT ); { - SelectDest sDest; - Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0); - sqlite3SelectDestInit(&sDest, SRT_Discard, 0); - sqlite3Select(pParse, pSelect, &sDest); - sqlite3SelectDelete(db, pSelect); - break; - } - } - if( pStep->op!=TK_SELECT ){ - sqlite3VdbeAddOp0(v, OP_ResetCount); - } - } - - return 0; -} - -#ifdef SQLITE_DEBUG -/* -** This function is used to add VdbeComment() annotations to a VDBE -** program. It is not used in production code, only for debugging. -*/ -static const char *onErrorText(int onError){ - switch( onError ){ - case OE_Abort: return "abort"; - case OE_Rollback: return "rollback"; - case OE_Fail: return "fail"; - case OE_Replace: return "replace"; - case OE_Ignore: return "ignore"; - case OE_Default: return "default"; - } - return "n/a"; -} -#endif - -/* -** Parse context structure pFrom has just been used to create a sub-vdbe -** (trigger program). If an error has occurred, transfer error information -** from pFrom to pTo. -*/ -static void transferParseError(Parse *pTo, Parse *pFrom){ - assert( pFrom->zErrMsg==0 || pFrom->nErr ); - assert( pTo->zErrMsg==0 || pTo->nErr ); - if( pTo->nErr==0 ){ - pTo->zErrMsg = pFrom->zErrMsg; - pTo->nErr = pFrom->nErr; - }else{ - sqlite3DbFree(pFrom->db, pFrom->zErrMsg); - } -} - -/* -** Create and populate a new TriggerPrg object with a sub-program -** implementing trigger pTrigger with ON CONFLICT policy orconf. -*/ -static TriggerPrg *codeRowTrigger( - Parse *pParse, /* Current parse context */ - Trigger *pTrigger, /* Trigger to code */ - Table *pTab, /* The table pTrigger is attached to */ - int orconf /* ON CONFLICT policy to code trigger program with */ -){ - Parse *pTop = sqlite3ParseToplevel(pParse); - sqlite3 *db = pParse->db; /* Database handle */ - TriggerPrg *pPrg; /* Value to return */ - Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */ - Vdbe *v; /* Temporary VM */ - NameContext sNC; /* Name context for sub-vdbe */ - SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ - Parse *pSubParse; /* Parse context for sub-vdbe */ - int iEndTrigger = 0; /* Label to jump to if WHEN is false */ - - assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); - assert( pTop->pVdbe ); - - /* Allocate the TriggerPrg and SubProgram objects. To ensure that they - ** are freed if an error occurs, link them into the Parse.pTriggerPrg - ** list of the top-level Parse object sooner rather than later. */ - pPrg = sqlite3DbMallocZero(db, sizeof(TriggerPrg)); - if( !pPrg ) return 0; - pPrg->pNext = pTop->pTriggerPrg; - pTop->pTriggerPrg = pPrg; - pPrg->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram)); - if( !pProgram ) return 0; - sqlite3VdbeLinkSubProgram(pTop->pVdbe, pProgram); - pPrg->pTrigger = pTrigger; - pPrg->orconf = orconf; - pPrg->aColmask[0] = 0xffffffff; - pPrg->aColmask[1] = 0xffffffff; - - /* Allocate and populate a new Parse context to use for coding the - ** trigger sub-program. */ - pSubParse = sqlite3StackAllocZero(db, sizeof(Parse)); - if( !pSubParse ) return 0; - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pSubParse; - pSubParse->db = db; - pSubParse->pTriggerTab = pTab; - pSubParse->pToplevel = pTop; - pSubParse->zAuthContext = pTrigger->zName; - pSubParse->eTriggerOp = pTrigger->op; - pSubParse->nQueryLoop = pParse->nQueryLoop; - - v = sqlite3GetVdbe(pSubParse); - if( v ){ - VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", - pTrigger->zName, onErrorText(orconf), - (pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"), - (pTrigger->op==TK_UPDATE ? "UPDATE" : ""), - (pTrigger->op==TK_INSERT ? "INSERT" : ""), - (pTrigger->op==TK_DELETE ? "DELETE" : ""), - pTab->zName - )); -#ifndef SQLITE_OMIT_TRACE - sqlite3VdbeChangeP4(v, -1, - sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC - ); -#endif - - /* If one was specified, code the WHEN clause. If it evaluates to false - ** (or NULL) the sub-vdbe is immediately halted by jumping to the - ** OP_Halt inserted at the end of the program. */ - if( pTrigger->pWhen ){ - pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0); - if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) - && db->mallocFailed==0 - ){ - iEndTrigger = sqlite3VdbeMakeLabel(v); - sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); - } - sqlite3ExprDelete(db, pWhen); - } - - /* Code the trigger program into the sub-vdbe. */ - codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); - - /* Insert an OP_Halt at the end of the sub-program. */ - if( iEndTrigger ){ - sqlite3VdbeResolveLabel(v, iEndTrigger); - } - sqlite3VdbeAddOp0(v, OP_Halt); - VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf))); - - transferParseError(pParse, pSubParse); - if( db->mallocFailed==0 ){ - pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); - } - pProgram->nMem = pSubParse->nMem; - pProgram->nCsr = pSubParse->nTab; - pProgram->nOnce = pSubParse->nOnce; - pProgram->token = (void *)pTrigger; - pPrg->aColmask[0] = pSubParse->oldmask; - pPrg->aColmask[1] = pSubParse->newmask; - sqlite3VdbeDelete(v); - } - - assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); - assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); - sqlite3StackFree(db, pSubParse); - - return pPrg; -} - -/* -** Return a pointer to a TriggerPrg object containing the sub-program for -** trigger pTrigger with default ON CONFLICT algorithm orconf. If no such -** TriggerPrg object exists, a new object is allocated and populated before -** being returned. -*/ -static TriggerPrg *getRowTrigger( - Parse *pParse, /* Current parse context */ - Trigger *pTrigger, /* Trigger to code */ - Table *pTab, /* The table trigger pTrigger is attached to */ - int orconf /* ON CONFLICT algorithm. */ -){ - Parse *pRoot = sqlite3ParseToplevel(pParse); - TriggerPrg *pPrg; - - assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); - - /* It may be that this trigger has already been coded (or is in the - ** process of being coded). If this is the case, then an entry with - ** a matching TriggerPrg.pTrigger field will be present somewhere - ** in the Parse.pTriggerPrg list. Search for such an entry. */ - for(pPrg=pRoot->pTriggerPrg; - pPrg && (pPrg->pTrigger!=pTrigger || pPrg->orconf!=orconf); - pPrg=pPrg->pNext - ); - - /* If an existing TriggerPrg could not be located, create a new one. */ - if( !pPrg ){ - pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf); - } - - return pPrg; -} - -/* -** Generate code for the trigger program associated with trigger p on -** table pTab. The reg, orconf and ignoreJump parameters passed to this -** function are the same as those described in the header function for -** sqlite3CodeRowTrigger() -*/ -SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect( - Parse *pParse, /* Parse context */ - Trigger *p, /* Trigger to code */ - Table *pTab, /* The table to code triggers from */ - int reg, /* Reg array containing OLD.* and NEW.* values */ - int orconf, /* ON CONFLICT policy */ - int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ -){ - Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */ - TriggerPrg *pPrg; - pPrg = getRowTrigger(pParse, p, pTab, orconf); - assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); - - /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program - ** is a pointer to the sub-vdbe containing the trigger program. */ - if( pPrg ){ - int bRecursive = (p->zName && 0==(pParse->db->flags&SQLITE_RecTriggers)); - - sqlite3VdbeAddOp3(v, OP_Program, reg, ignoreJump, ++pParse->nMem); - sqlite3VdbeChangeP4(v, -1, (const char *)pPrg->pProgram, P4_SUBPROGRAM); - VdbeComment( - (v, "Call: %s.%s", (p->zName?p->zName:"fkey"), onErrorText(orconf))); - - /* Set the P5 operand of the OP_Program instruction to non-zero if - ** recursive invocation of this trigger program is disallowed. Recursive - ** invocation is disallowed if (a) the sub-program is really a trigger, - ** not a foreign key action, and (b) the flag to enable recursive triggers - ** is clear. */ - sqlite3VdbeChangeP5(v, (u8)bRecursive); - } -} - -/* -** This is called to code the required FOR EACH ROW triggers for an operation -** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE) -** is given by the op paramater. The tr_tm parameter determines whether the -** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then -** parameter pChanges is passed the list of columns being modified. -** -** If there are no triggers that fire at the specified time for the specified -** operation on pTab, this function is a no-op. -** -** The reg argument is the address of the first in an array of registers -** that contain the values substituted for the new.* and old.* references -** in the trigger program. If N is the number of columns in table pTab -** (a copy of pTab->nCol), then registers are populated as follows: -** -** Register Contains -** ------------------------------------------------------ -** reg+0 OLD.rowid -** reg+1 OLD.* value of left-most column of pTab -** ... ... -** reg+N OLD.* value of right-most column of pTab -** reg+N+1 NEW.rowid -** reg+N+2 OLD.* value of left-most column of pTab -** ... ... -** reg+N+N+1 NEW.* value of right-most column of pTab -** -** For ON DELETE triggers, the registers containing the NEW.* values will -** never be accessed by the trigger program, so they are not allocated or -** populated by the caller (there is no data to populate them with anyway). -** Similarly, for ON INSERT triggers the values stored in the OLD.* registers -** are never accessed, and so are not allocated by the caller. So, for an -** ON INSERT trigger, the value passed to this function as parameter reg -** is not a readable register, although registers (reg+N) through -** (reg+N+N+1) are. -** -** Parameter orconf is the default conflict resolution algorithm for the -** trigger program to use (REPLACE, IGNORE etc.). Parameter ignoreJump -** is the instruction that control should jump to if a trigger program -** raises an IGNORE exception. -*/ -SQLITE_PRIVATE void sqlite3CodeRowTrigger( - Parse *pParse, /* Parse context */ - Trigger *pTrigger, /* List of triggers on table pTab */ - int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ - ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ - int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ - Table *pTab, /* The table to code triggers from */ - int reg, /* The first in an array of registers (see above) */ - int orconf, /* ON CONFLICT policy */ - int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ -){ - Trigger *p; /* Used to iterate through pTrigger list */ - - assert( op==TK_UPDATE || op==TK_INSERT || op==TK_DELETE ); - assert( tr_tm==TRIGGER_BEFORE || tr_tm==TRIGGER_AFTER ); - assert( (op==TK_UPDATE)==(pChanges!=0) ); - - for(p=pTrigger; p; p=p->pNext){ - - /* Sanity checking: The schema for the trigger and for the table are - ** always defined. The trigger must be in the same schema as the table - ** or else it must be a TEMP trigger. */ - assert( p->pSchema!=0 ); - assert( p->pTabSchema!=0 ); - assert( p->pSchema==p->pTabSchema - || p->pSchema==pParse->db->aDb[1].pSchema ); - - /* Determine whether we should code this trigger */ - if( p->op==op - && p->tr_tm==tr_tm - && checkColumnOverlap(p->pColumns, pChanges) - ){ - sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump); - } - } -} - -/* -** Triggers may access values stored in the old.* or new.* pseudo-table. -** This function returns a 32-bit bitmask indicating which columns of the -** old.* or new.* tables actually are used by triggers. This information -** may be used by the caller, for example, to avoid having to load the entire -** old.* record into memory when executing an UPDATE or DELETE command. -** -** Bit 0 of the returned mask is set if the left-most column of the -** table may be accessed using an [old|new].reference. Bit 1 is set if -** the second leftmost column value is required, and so on. If there -** are more than 32 columns in the table, and at least one of the columns -** with an index greater than 32 may be accessed, 0xffffffff is returned. -** -** It is not possible to determine if the old.rowid or new.rowid column is -** accessed by triggers. The caller must always assume that it is. -** -** Parameter isNew must be either 1 or 0. If it is 0, then the mask returned -** applies to the old.* table. If 1, the new.* table. -** -** Parameter tr_tm must be a mask with one or both of the TRIGGER_BEFORE -** and TRIGGER_AFTER bits set. Values accessed by BEFORE triggers are only -** included in the returned mask if the TRIGGER_BEFORE bit is set in the -** tr_tm parameter. Similarly, values accessed by AFTER triggers are only -** included in the returned mask if the TRIGGER_AFTER bit is set in tr_tm. -*/ -SQLITE_PRIVATE u32 sqlite3TriggerColmask( - Parse *pParse, /* Parse context */ - Trigger *pTrigger, /* List of triggers on table pTab */ - ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ - int isNew, /* 1 for new.* ref mask, 0 for old.* ref mask */ - int tr_tm, /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ - Table *pTab, /* The table to code triggers from */ - int orconf /* Default ON CONFLICT policy for trigger steps */ -){ - const int op = pChanges ? TK_UPDATE : TK_DELETE; - u32 mask = 0; - Trigger *p; - - assert( isNew==1 || isNew==0 ); - for(p=pTrigger; p; p=p->pNext){ - if( p->op==op && (tr_tm&p->tr_tm) - && checkColumnOverlap(p->pColumns,pChanges) - ){ - TriggerPrg *pPrg; - pPrg = getRowTrigger(pParse, p, pTab, orconf); - if( pPrg ){ - mask |= pPrg->aColmask[isNew]; - } - } - } - - return mask; -} - -#endif /* !defined(SQLITE_OMIT_TRIGGER) */ - -/************** End of trigger.c *********************************************/ -/************** Begin file update.c ******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code routines that are called by the parser -** to handle UPDATE statements. -*/ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Forward declaration */ -static void updateVirtualTable( - Parse *pParse, /* The parsing context */ - SrcList *pSrc, /* The virtual table to be modified */ - Table *pTab, /* The virtual table */ - ExprList *pChanges, /* The columns to change in the UPDATE statement */ - Expr *pRowidExpr, /* Expression used to recompute the rowid */ - int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ - Expr *pWhere, /* WHERE clause of the UPDATE statement */ - int onError /* ON CONFLICT strategy */ -); -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -/* -** The most recently coded instruction was an OP_Column to retrieve the -** i-th column of table pTab. This routine sets the P4 parameter of the -** OP_Column to the default value, if any. -** -** The default value of a column is specified by a DEFAULT clause in the -** column definition. This was either supplied by the user when the table -** was created, or added later to the table definition by an ALTER TABLE -** command. If the latter, then the row-records in the table btree on disk -** may not contain a value for the column and the default value, taken -** from the P4 parameter of the OP_Column instruction, is returned instead. -** If the former, then all row-records are guaranteed to include a value -** for the column and the P4 value is not required. -** -** Column definitions created by an ALTER TABLE command may only have -** literal default values specified: a number, null or a string. (If a more -** complicated default expression value was provided, it is evaluated -** when the ALTER TABLE is executed and one of the literal values written -** into the sqlite_master table.) -** -** Therefore, the P4 parameter is only required if the default value for -** the column is a literal number, string or null. The sqlite3ValueFromExpr() -** function is capable of transforming these types of expressions into -** sqlite3_value objects. -** -** If parameter iReg is not negative, code an OP_RealAffinity instruction -** on register iReg. This is used when an equivalent integer value is -** stored in place of an 8-byte floating point value in order to save -** space. -*/ -SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ - assert( pTab!=0 ); - if( !pTab->pSelect ){ - sqlite3_value *pValue; - u8 enc = ENC(sqlite3VdbeDb(v)); - Column *pCol = &pTab->aCol[i]; - VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); - assert( inCol ); - sqlite3ValueFromExpr(sqlite3VdbeDb(v), pCol->pDflt, enc, - pCol->affinity, &pValue); - if( pValue ){ - sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); - } -#ifndef SQLITE_OMIT_FLOATING_POINT - if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ - sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); - } -#endif - } -} - -/* -** Process an UPDATE statement. -** -** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL; -** \_______/ \________/ \______/ \________________/ -* onError pTabList pChanges pWhere -*/ -SQLITE_PRIVATE void sqlite3Update( - Parse *pParse, /* The parser context */ - SrcList *pTabList, /* The table in which we should change things */ - ExprList *pChanges, /* Things to be changed */ - Expr *pWhere, /* The WHERE clause. May be null */ - int onError /* How to handle constraint errors */ -){ - int i, j; /* Loop counters */ - Table *pTab; /* The table to be updated */ - int addr = 0; /* VDBE instruction address of the start of the loop */ - WhereInfo *pWInfo; /* Information about the WHERE clause */ - Vdbe *v; /* The virtual database engine */ - Index *pIdx; /* For looping over indices */ - int nIdx; /* Number of indices that need updating */ - int iCur; /* VDBE Cursor number of pTab */ - sqlite3 *db; /* The database structure */ - int *aRegIdx = 0; /* One register assigned to each index to be updated */ - int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the - ** an expression for the i-th column of the table. - ** aXRef[i]==-1 if the i-th column is not changed. */ - int chngRowid; /* True if the record number is being changed */ - Expr *pRowidExpr = 0; /* Expression defining the new record number */ - int openAll = 0; /* True if all indices need to be opened */ - AuthContext sContext; /* The authorization context */ - NameContext sNC; /* The name-context to resolve expressions in */ - int iDb; /* Database containing the table being updated */ - int okOnePass; /* True for one-pass algorithm without the FIFO */ - int hasFK; /* True if foreign key processing is required */ - -#ifndef SQLITE_OMIT_TRIGGER - int isView; /* True when updating a view (INSTEAD OF trigger) */ - Trigger *pTrigger; /* List of triggers on pTab, if required */ - int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ -#endif - int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */ - - /* Register Allocations */ - int regRowCount = 0; /* A count of rows changed */ - int regOldRowid; /* The old rowid */ - int regNewRowid; /* The new rowid */ - int regNew; /* Content of the NEW.* table in triggers */ - int regOld = 0; /* Content of OLD.* table in triggers */ - int regRowSet = 0; /* Rowset of rows to be updated */ - - memset(&sContext, 0, sizeof(sContext)); - db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ - goto update_cleanup; - } - assert( pTabList->nSrc==1 ); - - /* Locate the table which we want to update. - */ - pTab = sqlite3SrcListLookup(pParse, pTabList); - if( pTab==0 ) goto update_cleanup; - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - - /* Figure out if we have any triggers and if the table being - ** updated is a view. - */ -#ifndef SQLITE_OMIT_TRIGGER - pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask); - isView = pTab->pSelect!=0; - assert( pTrigger || tmask==0 ); -#else -# define pTrigger 0 -# define isView 0 -# define tmask 0 -#endif -#ifdef SQLITE_OMIT_VIEW -# undef isView -# define isView 0 -#endif - - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto update_cleanup; - } - if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ - goto update_cleanup; - } - aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol ); - if( aXRef==0 ) goto update_cleanup; - for(i=0; inCol; i++) aXRef[i] = -1; - - /* Allocate a cursors for the main database table and for all indices. - ** The index cursors might not be used, but if they are used they - ** need to occur right after the database cursor. So go ahead and - ** allocate enough space, just in case. - */ - pTabList->a[0].iCursor = iCur = pParse->nTab++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - pParse->nTab++; - } - - /* Initialize the name-context */ - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - sNC.pSrcList = pTabList; - - /* Resolve the column names in all the expressions of the - ** of the UPDATE statement. Also find the column index - ** for each column to be updated in the pChanges array. For each - ** column to be updated, make sure we have authorization to change - ** that column. - */ - chngRowid = 0; - for(i=0; inExpr; i++){ - if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ - goto update_cleanup; - } - for(j=0; jnCol; j++){ - if( sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){ - if( j==pTab->iPKey ){ - chngRowid = 1; - pRowidExpr = pChanges->a[i].pExpr; - } - aXRef[j] = i; - break; - } - } - if( j>=pTab->nCol ){ - if( sqlite3IsRowid(pChanges->a[i].zName) ){ - chngRowid = 1; - pRowidExpr = pChanges->a[i].pExpr; - }else{ - sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName); - pParse->checkSchema = 1; - goto update_cleanup; - } - } -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int rc; - rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, - pTab->aCol[j].zName, db->aDb[iDb].zName); - if( rc==SQLITE_DENY ){ - goto update_cleanup; - }else if( rc==SQLITE_IGNORE ){ - aXRef[j] = -1; - } - } -#endif - } - - hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid); - - /* Allocate memory for the array aRegIdx[]. There is one entry in the - ** array for each index associated with table being updated. Fill in - ** the value with a register number for indices that are to be used - ** and with zero for unused indices. - */ - for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} - if( nIdx>0 ){ - aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx ); - if( aRegIdx==0 ) goto update_cleanup; - } - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - int reg; - if( hasFK || chngRowid ){ - reg = ++pParse->nMem; - }else{ - reg = 0; - for(i=0; inColumn; i++){ - if( aXRef[pIdx->aiColumn[i]]>=0 ){ - reg = ++pParse->nMem; - break; - } - } - } - aRegIdx[j] = reg; - } - - /* Begin generating code. */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto update_cleanup; - if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, 1, iDb); - -#ifndef SQLITE_OMIT_VIRTUALTABLE - /* Virtual tables must be handled separately */ - if( IsVirtual(pTab) ){ - updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, - pWhere, onError); - pWhere = 0; - pTabList = 0; - goto update_cleanup; - } -#endif - - /* Allocate required registers. */ - regRowSet = ++pParse->nMem; - regOldRowid = regNewRowid = ++pParse->nMem; - if( pTrigger || hasFK ){ - regOld = pParse->nMem + 1; - pParse->nMem += pTab->nCol; - } - if( chngRowid || pTrigger || hasFK ){ - regNewRowid = ++pParse->nMem; - } - regNew = pParse->nMem + 1; - pParse->nMem += pTab->nCol; - - /* Start the view context. */ - if( isView ){ - sqlite3AuthContextPush(pParse, &sContext, pTab->zName); - } - - /* If we are trying to update a view, realize that view into - ** a ephemeral table. - */ -#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) - if( isView ){ - sqlite3MaterializeView(pParse, pTab, pWhere, iCur); - } -#endif - - /* Resolve the column names in all the expressions in the - ** WHERE clause. - */ - if( sqlite3ResolveExprNames(&sNC, pWhere) ){ - goto update_cleanup; - } - - /* Begin the database scan - */ - sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); - pWInfo = sqlite3WhereBegin( - pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0 - ); - if( pWInfo==0 ) goto update_cleanup; - okOnePass = pWInfo->okOnePass; - - /* Remember the rowid of every item to be updated. - */ - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid); - if( !okOnePass ){ - sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); - } - - /* End the database scan loop. - */ - sqlite3WhereEnd(pWInfo); - - /* Initialize the count of updated rows - */ - if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){ - regRowCount = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); - } - - if( !isView ){ - /* - ** Open every index that needs updating. Note that if any - ** index could potentially invoke a REPLACE conflict resolution - ** action, then we need to open all indices because we might need - ** to be deleting some records. - */ - if( !okOnePass ) sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); - if( onError==OE_Replace ){ - openAll = 1; - }else{ - openAll = 0; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->onError==OE_Replace ){ - openAll = 1; - break; - } - } - } - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - assert( aRegIdx ); - if( openAll || aRegIdx[i]>0 ){ - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); - sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); - assert( pParse->nTab>iCur+i+1 ); - } - } - } - - /* Top of the update loop */ - if( okOnePass ){ - int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid); - addr = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, a1); - }else{ - addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); - } - - /* Make cursor iCur point to the record that is being updated. If - ** this record does not exist for some reason (deleted by a trigger, - ** for example, then jump to the next iteration of the RowSet loop. */ - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); - - /* If the record number will change, set register regNewRowid to - ** contain the new value. If the record number is not being modified, - ** then regNewRowid is the same register as regOldRowid, which is - ** already populated. */ - assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid ); - if( chngRowid ){ - sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); - } - - /* If there are triggers on this table, populate an array of registers - ** with the required old.* column data. */ - if( hasFK || pTrigger ){ - u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); - oldmask |= sqlite3TriggerColmask(pParse, - pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError - ); - for(i=0; inCol; i++){ - if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<nCol-1); - for(i=0; inCol; i++){ - if( i==pTab->iPKey ){ - /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/ - }else{ - j = aXRef[i]; - if( j>=0 ){ - sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); - }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<nCol); - sqlite3TableAffinityStr(v, pTab); - sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, - TRIGGER_BEFORE, pTab, regOldRowid, onError, addr); - - /* The row-trigger may have deleted the row being updated. In this - ** case, jump to the next row. No updates or AFTER triggers are - ** required. This behavior - what happens when the row being updated - ** is deleted or renamed by a BEFORE trigger - is left undefined in the - ** documentation. - */ - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); - - /* If it did not delete it, the row-trigger may still have modified - ** some of the columns of the row being updated. Load the values for - ** all columns not modified by the update statement into their - ** registers in case this has happened. - */ - for(i=0; inCol; i++){ - if( aXRef[i]<0 && i!=pTab->iPKey ){ - sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i); - sqlite3ColumnDefault(v, pTab, i, regNew+i); - } - } - } - - if( !isView ){ - int j1; /* Address of jump instruction */ - - /* Do constraint checks. */ - sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid, - aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0); - - /* Do FK constraint checks. */ - if( hasFK ){ - sqlite3FkCheck(pParse, pTab, regOldRowid, 0); - } - - /* Delete the index entries associated with the current record. */ - j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); - sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); - - /* If changing the record number, delete the old record. */ - if( hasFK || chngRowid ){ - sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0); - } - sqlite3VdbeJumpHere(v, j1); - - if( hasFK ){ - sqlite3FkCheck(pParse, pTab, 0, regNewRowid); - } - - /* Insert the new index entries and the new record. */ - sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0); - - /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to - ** handle rows (possibly in other tables) that refer via a foreign key - ** to the row just updated. */ - if( hasFK ){ - sqlite3FkActions(pParse, pTab, pChanges, regOldRowid); - } - } - - /* Increment the row counter - */ - if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){ - sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); - } - - sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, - TRIGGER_AFTER, pTab, regOldRowid, onError, addr); - - /* Repeat the above with the next record to be updated, until - ** all record selected by the WHERE clause have been updated. - */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); - sqlite3VdbeJumpHere(v, addr); - - /* Close all tables */ - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - assert( aRegIdx ); - if( openAll || aRegIdx[i]>0 ){ - sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0); - } - } - sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); - - /* Update the sqlite_sequence table by storing the content of the - ** maximum rowid counter values recorded while inserting into - ** autoincrement tables. - */ - if( pParse->nested==0 && pParse->pTriggerTab==0 ){ - sqlite3AutoincrementEnd(pParse); - } - - /* - ** Return the number of rows that were changed. If this routine is - ** generating code because of a call to sqlite3NestedParse(), do not - ** invoke the callback function. - */ - if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){ - sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); - } - -update_cleanup: - sqlite3AuthContextPop(&sContext); - sqlite3DbFree(db, aRegIdx); - sqlite3DbFree(db, aXRef); - sqlite3SrcListDelete(db, pTabList); - sqlite3ExprListDelete(db, pChanges); - sqlite3ExprDelete(db, pWhere); - return; -} -/* Make sure "isView" and other macros defined above are undefined. Otherwise -** thely may interfere with compilation of other functions in this file -** (or in another file, if this file becomes part of the amalgamation). */ -#ifdef isView - #undef isView -#endif -#ifdef pTrigger - #undef pTrigger -#endif - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* -** Generate code for an UPDATE of a virtual table. -** -** The strategy is that we create an ephemerial table that contains -** for each row to be changed: -** -** (A) The original rowid of that row. -** (B) The revised rowid for the row. (note1) -** (C) The content of every column in the row. -** -** Then we loop over this ephemeral table and for each row in -** the ephermeral table call VUpdate. -** -** When finished, drop the ephemeral table. -** -** (note1) Actually, if we know in advance that (A) is always the same -** as (B) we only store (A), then duplicate (A) when pulling -** it out of the ephemeral table before calling VUpdate. -*/ -static void updateVirtualTable( - Parse *pParse, /* The parsing context */ - SrcList *pSrc, /* The virtual table to be modified */ - Table *pTab, /* The virtual table */ - ExprList *pChanges, /* The columns to change in the UPDATE statement */ - Expr *pRowid, /* Expression used to recompute the rowid */ - int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ - Expr *pWhere, /* WHERE clause of the UPDATE statement */ - int onError /* ON CONFLICT strategy */ -){ - Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */ - ExprList *pEList = 0; /* The result set of the SELECT statement */ - Select *pSelect = 0; /* The SELECT statement */ - Expr *pExpr; /* Temporary expression */ - int ephemTab; /* Table holding the result of the SELECT */ - int i; /* Loop counter */ - int addr; /* Address of top of loop */ - int iReg; /* First register in set passed to OP_VUpdate */ - sqlite3 *db = pParse->db; /* Database connection */ - const char *pVTab = (const char*)sqlite3GetVTable(db, pTab); - SelectDest dest; - - /* Construct the SELECT statement that will find the new values for - ** all updated rows. - */ - pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_")); - if( pRowid ){ - pEList = sqlite3ExprListAppend(pParse, pEList, - sqlite3ExprDup(db, pRowid, 0)); - } - assert( pTab->iPKey<0 ); - for(i=0; inCol; i++){ - if( aXRef[i]>=0 ){ - pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0); - }else{ - pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName); - } - pEList = sqlite3ExprListAppend(pParse, pEList, pExpr); - } - pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0); - - /* Create the ephemeral table into which the update results will - ** be stored. - */ - assert( v ); - ephemTab = pParse->nTab++; - sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); - sqlite3VdbeChangeP5(v, BTREE_UNORDERED); - - /* fill the ephemeral table - */ - sqlite3SelectDestInit(&dest, SRT_Table, ephemTab); - sqlite3Select(pParse, pSelect, &dest); - - /* Generate code to scan the ephemeral table and call VUpdate. */ - iReg = ++pParse->nMem; - pParse->nMem += pTab->nCol+1; - addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); - sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg); - sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1); - for(i=0; inCol; i++){ - sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i); - } - sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB); - sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); - sqlite3MayAbort(pParse); - sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); - sqlite3VdbeJumpHere(v, addr); - sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); - - /* Cleanup */ - sqlite3SelectDelete(db, pSelect); -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -/************** End of update.c **********************************************/ -/************** Begin file vacuum.c ******************************************/ -/* -** 2003 April 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to implement the VACUUM command. -** -** Most of the code in this file may be omitted by defining the -** SQLITE_OMIT_VACUUM macro. -*/ - -#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) -/* -** Finalize a prepared statement. If there was an error, store the -** text of the error message in *pzErrMsg. Return the result code. -*/ -static int vacuumFinalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){ - int rc; - rc = sqlite3VdbeFinalize((Vdbe*)pStmt); - if( rc ){ - sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db)); - } - return rc; -} - -/* -** Execute zSql on database db. Return an error code. -*/ -static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){ - sqlite3_stmt *pStmt; - VVA_ONLY( int rc; ) - if( !zSql ){ - return SQLITE_NOMEM; - } - if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){ - sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db)); - return sqlite3_errcode(db); - } - VVA_ONLY( rc = ) sqlite3_step(pStmt); - assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) ); - return vacuumFinalize(db, pStmt, pzErrMsg); -} - -/* -** Execute zSql on database db. The statement returns exactly -** one column. Execute this as SQL on the same database. -*/ -static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){ - sqlite3_stmt *pStmt; - int rc; - - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ) return rc; - - while( SQLITE_ROW==sqlite3_step(pStmt) ){ - rc = execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0)); - if( rc!=SQLITE_OK ){ - vacuumFinalize(db, pStmt, pzErrMsg); - return rc; - } - } - - return vacuumFinalize(db, pStmt, pzErrMsg); -} - -/* -** The non-standard VACUUM command is used to clean up the database, -** collapse free space, etc. It is modelled after the VACUUM command -** in PostgreSQL. -** -** In version 1.0.x of SQLite, the VACUUM command would call -** gdbm_reorganize() on all the database tables. But beginning -** with 2.0.0, SQLite no longer uses GDBM so this command has -** become a no-op. -*/ -SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse){ - Vdbe *v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0); - sqlite3VdbeUsesBtree(v, 0); - } - return; -} - -/* -** This routine implements the OP_Vacuum opcode of the VDBE. -*/ -SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ - int rc = SQLITE_OK; /* Return code from service routines */ - Btree *pMain; /* The database being vacuumed */ - Btree *pTemp; /* The temporary database we vacuum into */ - char *zSql = 0; /* SQL statements */ - int saved_flags; /* Saved value of the db->flags */ - int saved_nChange; /* Saved value of db->nChange */ - int saved_nTotalChange; /* Saved value of db->nTotalChange */ - void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */ - Db *pDb = 0; /* Database to detach at end of vacuum */ - int isMemDb; /* True if vacuuming a :memory: database */ - int nRes; /* Bytes of reserved space at the end of each page */ - int nDb; /* Number of attached databases */ - - if( !db->autoCommit ){ - sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); - return SQLITE_ERROR; - } - if( db->activeVdbeCnt>1 ){ - sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); - return SQLITE_ERROR; - } - - /* Save the current value of the database flags so that it can be - ** restored before returning. Then set the writable-schema flag, and - ** disable CHECK and foreign key constraints. */ - saved_flags = db->flags; - saved_nChange = db->nChange; - saved_nTotalChange = db->nTotalChange; - saved_xTrace = db->xTrace; - db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin; - db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder); - db->xTrace = 0; - - pMain = db->aDb[0].pBt; - isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); - - /* Attach the temporary database as 'vacuum_db'. The synchronous pragma - ** can be set to 'off' for this file, as it is not recovered if a crash - ** occurs anyway. The integrity of the database is maintained by a - ** (possibly synchronous) transaction opened on the main database before - ** sqlite3BtreeCopyFile() is called. - ** - ** An optimisation would be to use a non-journaled pager. - ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but - ** that actually made the VACUUM run slower. Very little journalling - ** actually occurs when doing a vacuum since the vacuum_db is initially - ** empty. Only the journal header is written. Apparently it takes more - ** time to parse and run the PRAGMA to turn journalling off than it does - ** to write the journal header file. - */ - nDb = db->nDb; - if( sqlite3TempInMemory(db) ){ - zSql = "ATTACH ':memory:' AS vacuum_db;"; - }else{ - zSql = "ATTACH '' AS vacuum_db;"; - } - rc = execSql(db, pzErrMsg, zSql); - if( db->nDb>nDb ){ - pDb = &db->aDb[db->nDb-1]; - assert( strcmp(pDb->zName,"vacuum_db")==0 ); - } - if( rc!=SQLITE_OK ) goto end_of_vacuum; - pTemp = db->aDb[db->nDb-1].pBt; - - /* The call to execSql() to attach the temp database has left the file - ** locked (as there was more than one active statement when the transaction - ** to read the schema was concluded. Unlock it here so that this doesn't - ** cause problems for the call to BtreeSetPageSize() below. */ - sqlite3BtreeCommit(pTemp); - - nRes = sqlite3BtreeGetReserve(pMain); - - /* A VACUUM cannot change the pagesize of an encrypted database. */ -#ifdef SQLITE_HAS_CODEC - if( db->nextPagesize ){ - extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); - int nKey; - char *zKey; - sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - if( nKey ) db->nextPagesize = 0; - } -#endif - - rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF"); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - /* Begin a transaction and take an exclusive lock on the main database - ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below, - ** to ensure that we do not try to change the page-size on a WAL database. - */ - rc = execSql(db, pzErrMsg, "BEGIN;"); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = sqlite3BtreeBeginTrans(pMain, 2); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - /* Do not attempt to change the page size for a WAL database */ - if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain)) - ==PAGER_JOURNALMODE_WAL ){ - db->nextPagesize = 0; - } - - if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0) - || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0)) - || NEVER(db->mallocFailed) - ){ - rc = SQLITE_NOMEM; - goto end_of_vacuum; - } - -#ifndef SQLITE_OMIT_AUTOVACUUM - sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac : - sqlite3BtreeGetAutoVacuum(pMain)); -#endif - - /* Query the schema of the main database. Create a mirror schema - ** in the temporary database. - */ - rc = execExecSql(db, pzErrMsg, - "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) " - " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'" - " AND rootpage>0" - ); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = execExecSql(db, pzErrMsg, - "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)" - " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' "); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = execExecSql(db, pzErrMsg, - "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) " - " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'"); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - /* Loop through the tables in the main database. For each, do - ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy - ** the contents to the temporary database. - */ - rc = execExecSql(db, pzErrMsg, - "SELECT 'INSERT INTO vacuum_db.' || quote(name) " - "|| ' SELECT * FROM main.' || quote(name) || ';'" - "FROM main.sqlite_master " - "WHERE type = 'table' AND name!='sqlite_sequence' " - " AND rootpage>0" - ); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - /* Copy over the sequence table - */ - rc = execExecSql(db, pzErrMsg, - "SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' " - "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' " - ); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = execExecSql(db, pzErrMsg, - "SELECT 'INSERT INTO vacuum_db.' || quote(name) " - "|| ' SELECT * FROM main.' || quote(name) || ';' " - "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';" - ); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - - - /* Copy the triggers, views, and virtual tables from the main database - ** over to the temporary database. None of these objects has any - ** associated storage, so all we have to do is copy their entries - ** from the SQLITE_MASTER table. - */ - rc = execSql(db, pzErrMsg, - "INSERT INTO vacuum_db.sqlite_master " - " SELECT type, name, tbl_name, rootpage, sql" - " FROM main.sqlite_master" - " WHERE type='view' OR type='trigger'" - " OR (type='table' AND rootpage=0)" - ); - if( rc ) goto end_of_vacuum; - - /* At this point, there is a write transaction open on both the - ** vacuum database and the main database. Assuming no error occurs, - ** both transactions are closed by this block - the main database - ** transaction by sqlite3BtreeCopyFile() and the other by an explicit - ** call to sqlite3BtreeCommit(). - */ - { - u32 meta; - int i; - - /* This array determines which meta meta values are preserved in the - ** vacuum. Even entries are the meta value number and odd entries - ** are an increment to apply to the meta value after the vacuum. - ** The increment is used to increase the schema cookie so that other - ** connections to the same database will know to reread the schema. - */ - static const unsigned char aCopy[] = { - BTREE_SCHEMA_VERSION, 1, /* Add one to the old schema cookie */ - BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */ - BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */ - BTREE_USER_VERSION, 0, /* Preserve the user version */ - }; - - assert( 1==sqlite3BtreeIsInTrans(pTemp) ); - assert( 1==sqlite3BtreeIsInTrans(pMain) ); - - /* Copy Btree meta values */ - for(i=0; iflags */ - db->flags = saved_flags; - db->nChange = saved_nChange; - db->nTotalChange = saved_nTotalChange; - db->xTrace = saved_xTrace; - sqlite3BtreeSetPageSize(pMain, -1, -1, 1); - - /* Currently there is an SQL level transaction open on the vacuum - ** database. No locks are held on any other files (since the main file - ** was committed at the btree level). So it safe to end the transaction - ** by manually setting the autoCommit flag to true and detaching the - ** vacuum database. The vacuum_db journal file is deleted when the pager - ** is closed by the DETACH. - */ - db->autoCommit = 1; - - if( pDb ){ - sqlite3BtreeClose(pDb->pBt); - pDb->pBt = 0; - pDb->pSchema = 0; - } - - /* This both clears the schemas and reduces the size of the db->aDb[] - ** array. */ - sqlite3ResetAllSchemasOfConnection(db); - - return rc; -} - -#endif /* SQLITE_OMIT_VACUUM && SQLITE_OMIT_ATTACH */ - -/************** End of vacuum.c **********************************************/ -/************** Begin file vtab.c ********************************************/ -/* -** 2006 June 10 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code used to help implement virtual tables. -*/ -#ifndef SQLITE_OMIT_VIRTUALTABLE - -/* -** Before a virtual table xCreate() or xConnect() method is invoked, the -** sqlite3.pVtabCtx member variable is set to point to an instance of -** this struct allocated on the stack. It is used by the implementation of -** the sqlite3_declare_vtab() and sqlite3_vtab_config() APIs, both of which -** are invoked only from within xCreate and xConnect methods. -*/ -struct VtabCtx { - VTable *pVTable; /* The virtual table being constructed */ - Table *pTab; /* The Table object to which the virtual table belongs */ -}; - -/* -** The actual function that does the work of creating a new module. -** This function implements the sqlite3_create_module() and -** sqlite3_create_module_v2() interfaces. -*/ -static int createModule( - sqlite3 *db, /* Database in which module is registered */ - const char *zName, /* Name assigned to this module */ - const sqlite3_module *pModule, /* The definition of the module */ - void *pAux, /* Context pointer for xCreate/xConnect */ - void (*xDestroy)(void *) /* Module destructor function */ -){ - int rc = SQLITE_OK; - int nName; - - sqlite3_mutex_enter(db->mutex); - nName = sqlite3Strlen30(zName); - if( sqlite3HashFind(&db->aModule, zName, nName) ){ - rc = SQLITE_MISUSE_BKPT; - }else{ - Module *pMod; - pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1); - if( pMod ){ - Module *pDel; - char *zCopy = (char *)(&pMod[1]); - memcpy(zCopy, zName, nName+1); - pMod->zName = zCopy; - pMod->pModule = pModule; - pMod->pAux = pAux; - pMod->xDestroy = xDestroy; - pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,nName,(void*)pMod); - assert( pDel==0 || pDel==pMod ); - if( pDel ){ - db->mallocFailed = 1; - sqlite3DbFree(db, pDel); - } - } - } - rc = sqlite3ApiExit(db, rc); - if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux); - - sqlite3_mutex_leave(db->mutex); - return rc; -} - - -/* -** External API function used to create a new virtual-table module. -*/ -SQLITE_API int sqlite3_create_module( - sqlite3 *db, /* Database in which module is registered */ - const char *zName, /* Name assigned to this module */ - const sqlite3_module *pModule, /* The definition of the module */ - void *pAux /* Context pointer for xCreate/xConnect */ -){ - return createModule(db, zName, pModule, pAux, 0); -} - -/* -** External API function used to create a new virtual-table module. -*/ -SQLITE_API int sqlite3_create_module_v2( - sqlite3 *db, /* Database in which module is registered */ - const char *zName, /* Name assigned to this module */ - const sqlite3_module *pModule, /* The definition of the module */ - void *pAux, /* Context pointer for xCreate/xConnect */ - void (*xDestroy)(void *) /* Module destructor function */ -){ - return createModule(db, zName, pModule, pAux, xDestroy); -} - -/* -** Lock the virtual table so that it cannot be disconnected. -** Locks nest. Every lock should have a corresponding unlock. -** If an unlock is omitted, resources leaks will occur. -** -** If a disconnect is attempted while a virtual table is locked, -** the disconnect is deferred until all locks have been removed. -*/ -SQLITE_PRIVATE void sqlite3VtabLock(VTable *pVTab){ - pVTab->nRef++; -} - - -/* -** pTab is a pointer to a Table structure representing a virtual-table. -** Return a pointer to the VTable object used by connection db to access -** this virtual-table, if one has been created, or NULL otherwise. -*/ -SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){ - VTable *pVtab; - assert( IsVirtual(pTab) ); - for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); - return pVtab; -} - -/* -** Decrement the ref-count on a virtual table object. When the ref-count -** reaches zero, call the xDisconnect() method to delete the object. -*/ -SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){ - sqlite3 *db = pVTab->db; - - assert( db ); - assert( pVTab->nRef>0 ); - assert( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE ); - - pVTab->nRef--; - if( pVTab->nRef==0 ){ - sqlite3_vtab *p = pVTab->pVtab; - if( p ){ - p->pModule->xDisconnect(p); - } - sqlite3DbFree(db, pVTab); - } -} - -/* -** Table p is a virtual table. This function moves all elements in the -** p->pVTable list to the sqlite3.pDisconnect lists of their associated -** database connections to be disconnected at the next opportunity. -** Except, if argument db is not NULL, then the entry associated with -** connection db is left in the p->pVTable list. -*/ -static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ - VTable *pRet = 0; - VTable *pVTable = p->pVTable; - p->pVTable = 0; - - /* Assert that the mutex (if any) associated with the BtShared database - ** that contains table p is held by the caller. See header comments - ** above function sqlite3VtabUnlockList() for an explanation of why - ** this makes it safe to access the sqlite3.pDisconnect list of any - ** database connection that may have an entry in the p->pVTable list. - */ - assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); - - while( pVTable ){ - sqlite3 *db2 = pVTable->db; - VTable *pNext = pVTable->pNext; - assert( db2 ); - if( db2==db ){ - pRet = pVTable; - p->pVTable = pRet; - pRet->pNext = 0; - }else{ - pVTable->pNext = db2->pDisconnect; - db2->pDisconnect = pVTable; - } - pVTable = pNext; - } - - assert( !db || pRet ); - return pRet; -} - -/* -** Table *p is a virtual table. This function removes the VTable object -** for table *p associated with database connection db from the linked -** list in p->pVTab. It also decrements the VTable ref count. This is -** used when closing database connection db to free all of its VTable -** objects without disturbing the rest of the Schema object (which may -** be being used by other shared-cache connections). -*/ -SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p){ - VTable **ppVTab; - - assert( IsVirtual(p) ); - assert( sqlite3BtreeHoldsAllMutexes(db) ); - assert( sqlite3_mutex_held(db->mutex) ); - - for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){ - if( (*ppVTab)->db==db ){ - VTable *pVTab = *ppVTab; - *ppVTab = pVTab->pNext; - sqlite3VtabUnlock(pVTab); - break; - } - } -} - - -/* -** Disconnect all the virtual table objects in the sqlite3.pDisconnect list. -** -** This function may only be called when the mutexes associated with all -** shared b-tree databases opened using connection db are held by the -** caller. This is done to protect the sqlite3.pDisconnect list. The -** sqlite3.pDisconnect list is accessed only as follows: -** -** 1) By this function. In this case, all BtShared mutexes and the mutex -** associated with the database handle itself must be held. -** -** 2) By function vtabDisconnectAll(), when it adds a VTable entry to -** the sqlite3.pDisconnect list. In this case either the BtShared mutex -** associated with the database the virtual table is stored in is held -** or, if the virtual table is stored in a non-sharable database, then -** the database handle mutex is held. -** -** As a result, a sqlite3.pDisconnect cannot be accessed simultaneously -** by multiple threads. It is thread-safe. -*/ -SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){ - VTable *p = db->pDisconnect; - db->pDisconnect = 0; - - assert( sqlite3BtreeHoldsAllMutexes(db) ); - assert( sqlite3_mutex_held(db->mutex) ); - - if( p ){ - sqlite3ExpirePreparedStatements(db); - do { - VTable *pNext = p->pNext; - sqlite3VtabUnlock(p); - p = pNext; - }while( p ); - } -} - -/* -** Clear any and all virtual-table information from the Table record. -** This routine is called, for example, just before deleting the Table -** record. -** -** Since it is a virtual-table, the Table structure contains a pointer -** to the head of a linked list of VTable structures. Each VTable -** structure is associated with a single sqlite3* user of the schema. -** The reference count of the VTable structure associated with database -** connection db is decremented immediately (which may lead to the -** structure being xDisconnected and free). Any other VTable structures -** in the list are moved to the sqlite3.pDisconnect list of the associated -** database connection. -*/ -SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){ - if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); - if( p->azModuleArg ){ - int i; - for(i=0; inModuleArg; i++){ - if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]); - } - sqlite3DbFree(db, p->azModuleArg); - } -} - -/* -** Add a new module argument to pTable->azModuleArg[]. -** The string is not copied - the pointer is stored. The -** string will be freed automatically when the table is -** deleted. -*/ -static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){ - int i = pTable->nModuleArg++; - int nBytes = sizeof(char *)*(1+pTable->nModuleArg); - char **azModuleArg; - azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); - if( azModuleArg==0 ){ - int j; - for(j=0; jazModuleArg[j]); - } - sqlite3DbFree(db, zArg); - sqlite3DbFree(db, pTable->azModuleArg); - pTable->nModuleArg = 0; - }else{ - azModuleArg[i] = zArg; - azModuleArg[i+1] = 0; - } - pTable->azModuleArg = azModuleArg; -} - -/* -** The parser calls this routine when it first sees a CREATE VIRTUAL TABLE -** statement. The module name has been parsed, but the optional list -** of parameters that follow the module name are still pending. -*/ -SQLITE_PRIVATE void sqlite3VtabBeginParse( - Parse *pParse, /* Parsing context */ - Token *pName1, /* Name of new table, or database name */ - Token *pName2, /* Name of new table or NULL */ - Token *pModuleName, /* Name of the module for the virtual table */ - int ifNotExists /* No error if the table already exists */ -){ - int iDb; /* The database the table is being created in */ - Table *pTable; /* The new virtual table */ - sqlite3 *db; /* Database connection */ - - sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, ifNotExists); - pTable = pParse->pNewTable; - if( pTable==0 ) return; - assert( 0==pTable->pIndex ); - - db = pParse->db; - iDb = sqlite3SchemaToIndex(db, pTable->pSchema); - assert( iDb>=0 ); - - pTable->tabFlags |= TF_Virtual; - pTable->nModuleArg = 0; - addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName)); - addModuleArgument(db, pTable, 0); - addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName)); - pParse->sNameToken.n = (int)(&pModuleName->z[pModuleName->n] - pName1->z); - -#ifndef SQLITE_OMIT_AUTHORIZATION - /* Creating a virtual table invokes the authorization callback twice. - ** The first invocation, to obtain permission to INSERT a row into the - ** sqlite_master table, has already been made by sqlite3StartTable(). - ** The second call, to obtain permission to create the table, is made now. - */ - if( pTable->azModuleArg ){ - sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName, - pTable->azModuleArg[0], pParse->db->aDb[iDb].zName); - } -#endif -} - -/* -** This routine takes the module argument that has been accumulating -** in pParse->zArg[] and appends it to the list of arguments on the -** virtual table currently under construction in pParse->pTable. -*/ -static void addArgumentToVtab(Parse *pParse){ - if( pParse->sArg.z && pParse->pNewTable ){ - const char *z = (const char*)pParse->sArg.z; - int n = pParse->sArg.n; - sqlite3 *db = pParse->db; - addModuleArgument(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n)); - } -} - -/* -** The parser calls this routine after the CREATE VIRTUAL TABLE statement -** has been completely parsed. -*/ -SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ - Table *pTab = pParse->pNewTable; /* The table being constructed */ - sqlite3 *db = pParse->db; /* The database connection */ - - if( pTab==0 ) return; - addArgumentToVtab(pParse); - pParse->sArg.z = 0; - if( pTab->nModuleArg<1 ) return; - - /* If the CREATE VIRTUAL TABLE statement is being entered for the - ** first time (in other words if the virtual table is actually being - ** created now instead of just being read out of sqlite_master) then - ** do additional initialization work and store the statement text - ** in the sqlite_master table. - */ - if( !db->init.busy ){ - char *zStmt; - char *zWhere; - int iDb; - Vdbe *v; - - /* Compute the complete text of the CREATE VIRTUAL TABLE statement */ - if( pEnd ){ - pParse->sNameToken.n = (int)(pEnd->z - pParse->sNameToken.z) + pEnd->n; - } - zStmt = sqlite3MPrintf(db, "CREATE VIRTUAL TABLE %T", &pParse->sNameToken); - - /* A slot for the record has already been allocated in the - ** SQLITE_MASTER table. We just need to update that slot with all - ** the information we've collected. - ** - ** The VM register number pParse->regRowid holds the rowid of an - ** entry in the sqlite_master table tht was created for this vtab - ** by sqlite3StartTable(). - */ - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - sqlite3NestedParse(pParse, - "UPDATE %Q.%s " - "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " - "WHERE rowid=#%d", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), - pTab->zName, - pTab->zName, - zStmt, - pParse->regRowid - ); - sqlite3DbFree(db, zStmt); - v = sqlite3GetVdbe(pParse); - sqlite3ChangeCookie(pParse, iDb); - - sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); - zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName); - sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); - sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0, - pTab->zName, sqlite3Strlen30(pTab->zName) + 1); - } - - /* If we are rereading the sqlite_master table create the in-memory - ** record of the table. The xConnect() method is not called until - ** the first time the virtual table is used in an SQL statement. This - ** allows a schema that contains virtual tables to be loaded before - ** the required virtual table implementations are registered. */ - else { - Table *pOld; - Schema *pSchema = pTab->pSchema; - const char *zName = pTab->zName; - int nName = sqlite3Strlen30(zName); - assert( sqlite3SchemaMutexHeld(db, 0, pSchema) ); - pOld = sqlite3HashInsert(&pSchema->tblHash, zName, nName, pTab); - if( pOld ){ - db->mallocFailed = 1; - assert( pTab==pOld ); /* Malloc must have failed inside HashInsert() */ - return; - } - pParse->pNewTable = 0; - } -} - -/* -** The parser calls this routine when it sees the first token -** of an argument to the module name in a CREATE VIRTUAL TABLE statement. -*/ -SQLITE_PRIVATE void sqlite3VtabArgInit(Parse *pParse){ - addArgumentToVtab(pParse); - pParse->sArg.z = 0; - pParse->sArg.n = 0; -} - -/* -** The parser calls this routine for each token after the first token -** in an argument to the module name in a CREATE VIRTUAL TABLE statement. -*/ -SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse *pParse, Token *p){ - Token *pArg = &pParse->sArg; - if( pArg->z==0 ){ - pArg->z = p->z; - pArg->n = p->n; - }else{ - assert(pArg->z < p->z); - pArg->n = (int)(&p->z[p->n] - pArg->z); - } -} - -/* -** Invoke a virtual table constructor (either xCreate or xConnect). The -** pointer to the function to invoke is passed as the fourth parameter -** to this procedure. -*/ -static int vtabCallConstructor( - sqlite3 *db, - Table *pTab, - Module *pMod, - int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**), - char **pzErr -){ - VtabCtx sCtx, *pPriorCtx; - VTable *pVTable; - int rc; - const char *const*azArg = (const char *const*)pTab->azModuleArg; - int nArg = pTab->nModuleArg; - char *zErr = 0; - char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName); - int iDb; - - if( !zModuleName ){ - return SQLITE_NOMEM; - } - - pVTable = sqlite3DbMallocZero(db, sizeof(VTable)); - if( !pVTable ){ - sqlite3DbFree(db, zModuleName); - return SQLITE_NOMEM; - } - pVTable->db = db; - pVTable->pMod = pMod; - - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - pTab->azModuleArg[1] = db->aDb[iDb].zName; - - /* Invoke the virtual table constructor */ - assert( &db->pVtabCtx ); - assert( xConstruct ); - sCtx.pTab = pTab; - sCtx.pVTable = pVTable; - pPriorCtx = db->pVtabCtx; - db->pVtabCtx = &sCtx; - rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); - db->pVtabCtx = pPriorCtx; - if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; - - if( SQLITE_OK!=rc ){ - if( zErr==0 ){ - *pzErr = sqlite3MPrintf(db, "vtable constructor failed: %s", zModuleName); - }else { - *pzErr = sqlite3MPrintf(db, "%s", zErr); - sqlite3_free(zErr); - } - sqlite3DbFree(db, pVTable); - }else if( ALWAYS(pVTable->pVtab) ){ - /* Justification of ALWAYS(): A correct vtab constructor must allocate - ** the sqlite3_vtab object if successful. */ - pVTable->pVtab->pModule = pMod->pModule; - pVTable->nRef = 1; - if( sCtx.pTab ){ - const char *zFormat = "vtable constructor did not declare schema: %s"; - *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); - sqlite3VtabUnlock(pVTable); - rc = SQLITE_ERROR; - }else{ - int iCol; - /* If everything went according to plan, link the new VTable structure - ** into the linked list headed by pTab->pVTable. Then loop through the - ** columns of the table to see if any of them contain the token "hidden". - ** If so, set the Column COLFLAG_HIDDEN flag and remove the token from - ** the type string. */ - pVTable->pNext = pTab->pVTable; - pTab->pVTable = pVTable; - - for(iCol=0; iColnCol; iCol++){ - char *zType = pTab->aCol[iCol].zType; - int nType; - int i = 0; - if( !zType ) continue; - nType = sqlite3Strlen30(zType); - if( sqlite3StrNICmp("hidden", zType, 6)||(zType[6] && zType[6]!=' ') ){ - for(i=0; i0 ){ - assert(zType[i-1]==' '); - zType[i-1] = '\0'; - } - pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN; - } - } - } - } - - sqlite3DbFree(db, zModuleName); - return rc; -} - -/* -** This function is invoked by the parser to call the xConnect() method -** of the virtual table pTab. If an error occurs, an error code is returned -** and an error left in pParse. -** -** This call is a no-op if table pTab is not a virtual table. -*/ -SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ - sqlite3 *db = pParse->db; - const char *zMod; - Module *pMod; - int rc; - - assert( pTab ); - if( (pTab->tabFlags & TF_Virtual)==0 || sqlite3GetVTable(db, pTab) ){ - return SQLITE_OK; - } - - /* Locate the required virtual table module */ - zMod = pTab->azModuleArg[0]; - pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); - - if( !pMod ){ - const char *zModule = pTab->azModuleArg[0]; - sqlite3ErrorMsg(pParse, "no such module: %s", zModule); - rc = SQLITE_ERROR; - }else{ - char *zErr = 0; - rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr); - if( rc!=SQLITE_OK ){ - sqlite3ErrorMsg(pParse, "%s", zErr); - } - sqlite3DbFree(db, zErr); - } - - return rc; -} -/* -** Grow the db->aVTrans[] array so that there is room for at least one -** more v-table. Return SQLITE_NOMEM if a malloc fails, or SQLITE_OK otherwise. -*/ -static int growVTrans(sqlite3 *db){ - const int ARRAY_INCR = 5; - - /* Grow the sqlite3.aVTrans array if required */ - if( (db->nVTrans%ARRAY_INCR)==0 ){ - VTable **aVTrans; - int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR); - aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes); - if( !aVTrans ){ - return SQLITE_NOMEM; - } - memset(&aVTrans[db->nVTrans], 0, sizeof(sqlite3_vtab *)*ARRAY_INCR); - db->aVTrans = aVTrans; - } - - return SQLITE_OK; -} - -/* -** Add the virtual table pVTab to the array sqlite3.aVTrans[]. Space should -** have already been reserved using growVTrans(). -*/ -static void addToVTrans(sqlite3 *db, VTable *pVTab){ - /* Add pVtab to the end of sqlite3.aVTrans */ - db->aVTrans[db->nVTrans++] = pVTab; - sqlite3VtabLock(pVTab); -} - -/* -** This function is invoked by the vdbe to call the xCreate method -** of the virtual table named zTab in database iDb. -** -** If an error occurs, *pzErr is set to point an an English language -** description of the error and an SQLITE_XXX error code is returned. -** In this case the caller must call sqlite3DbFree(db, ) on *pzErr. -*/ -SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ - int rc = SQLITE_OK; - Table *pTab; - Module *pMod; - const char *zMod; - - pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - assert( pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVTable ); - - /* Locate the required virtual table module */ - zMod = pTab->azModuleArg[0]; - pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); - - /* If the module has been registered and includes a Create method, - ** invoke it now. If the module has not been registered, return an - ** error. Otherwise, do nothing. - */ - if( !pMod ){ - *pzErr = sqlite3MPrintf(db, "no such module: %s", zMod); - rc = SQLITE_ERROR; - }else{ - rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr); - } - - /* Justification of ALWAYS(): The xConstructor method is required to - ** create a valid sqlite3_vtab if it returns SQLITE_OK. */ - if( rc==SQLITE_OK && ALWAYS(sqlite3GetVTable(db, pTab)) ){ - rc = growVTrans(db); - if( rc==SQLITE_OK ){ - addToVTrans(db, sqlite3GetVTable(db, pTab)); - } - } - - return rc; -} - -/* -** This function is used to set the schema of a virtual table. It is only -** valid to call this function from within the xCreate() or xConnect() of a -** virtual table module. -*/ -SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ - Parse *pParse; - - int rc = SQLITE_OK; - Table *pTab; - char *zErr = 0; - - sqlite3_mutex_enter(db->mutex); - if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){ - sqlite3Error(db, SQLITE_MISUSE, 0); - sqlite3_mutex_leave(db->mutex); - return SQLITE_MISUSE_BKPT; - } - assert( (pTab->tabFlags & TF_Virtual)!=0 ); - - pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); - if( pParse==0 ){ - rc = SQLITE_NOMEM; - }else{ - pParse->declareVtab = 1; - pParse->db = db; - pParse->nQueryLoop = 1; - - if( SQLITE_OK==sqlite3RunParser(pParse, zCreateTable, &zErr) - && pParse->pNewTable - && !db->mallocFailed - && !pParse->pNewTable->pSelect - && (pParse->pNewTable->tabFlags & TF_Virtual)==0 - ){ - if( !pTab->aCol ){ - pTab->aCol = pParse->pNewTable->aCol; - pTab->nCol = pParse->pNewTable->nCol; - pParse->pNewTable->nCol = 0; - pParse->pNewTable->aCol = 0; - } - db->pVtabCtx->pTab = 0; - }else{ - sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); - sqlite3DbFree(db, zErr); - rc = SQLITE_ERROR; - } - pParse->declareVtab = 0; - - if( pParse->pVdbe ){ - sqlite3VdbeFinalize(pParse->pVdbe); - } - sqlite3DeleteTable(db, pParse->pNewTable); - sqlite3StackFree(db, pParse); - } - - assert( (rc&0xff)==rc ); - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/* -** This function is invoked by the vdbe to call the xDestroy method -** of the virtual table named zTab in database iDb. This occurs -** when a DROP TABLE is mentioned. -** -** This call is a no-op if zTab is not a virtual table. -*/ -SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){ - int rc = SQLITE_OK; - Table *pTab; - - pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ - VTable *p = vtabDisconnectAll(db, pTab); - - assert( rc==SQLITE_OK ); - rc = p->pMod->pModule->xDestroy(p->pVtab); - - /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ - if( rc==SQLITE_OK ){ - assert( pTab->pVTable==p && p->pNext==0 ); - p->pVtab = 0; - pTab->pVTable = 0; - sqlite3VtabUnlock(p); - } - } - - return rc; -} - -/* -** This function invokes either the xRollback or xCommit method -** of each of the virtual tables in the sqlite3.aVTrans array. The method -** called is identified by the second argument, "offset", which is -** the offset of the method to call in the sqlite3_module structure. -** -** The array is cleared after invoking the callbacks. -*/ -static void callFinaliser(sqlite3 *db, int offset){ - int i; - if( db->aVTrans ){ - for(i=0; inVTrans; i++){ - VTable *pVTab = db->aVTrans[i]; - sqlite3_vtab *p = pVTab->pVtab; - if( p ){ - int (*x)(sqlite3_vtab *); - x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset); - if( x ) x(p); - } - pVTab->iSavepoint = 0; - sqlite3VtabUnlock(pVTab); - } - sqlite3DbFree(db, db->aVTrans); - db->nVTrans = 0; - db->aVTrans = 0; - } -} - -/* -** Invoke the xSync method of all virtual tables in the sqlite3.aVTrans -** array. Return the error code for the first error that occurs, or -** SQLITE_OK if all xSync operations are successful. -** -** Set *pzErrmsg to point to a buffer that should be released using -** sqlite3DbFree() containing an error message, if one is available. -*/ -SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){ - int i; - int rc = SQLITE_OK; - VTable **aVTrans = db->aVTrans; - - db->aVTrans = 0; - for(i=0; rc==SQLITE_OK && inVTrans; i++){ - int (*x)(sqlite3_vtab *); - sqlite3_vtab *pVtab = aVTrans[i]->pVtab; - if( pVtab && (x = pVtab->pModule->xSync)!=0 ){ - rc = x(pVtab); - sqlite3DbFree(db, *pzErrmsg); - *pzErrmsg = sqlite3DbStrDup(db, pVtab->zErrMsg); - sqlite3_free(pVtab->zErrMsg); - } - } - db->aVTrans = aVTrans; - return rc; -} - -/* -** Invoke the xRollback method of all virtual tables in the -** sqlite3.aVTrans array. Then clear the array itself. -*/ -SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db){ - callFinaliser(db, offsetof(sqlite3_module,xRollback)); - return SQLITE_OK; -} - -/* -** Invoke the xCommit method of all virtual tables in the -** sqlite3.aVTrans array. Then clear the array itself. -*/ -SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db){ - callFinaliser(db, offsetof(sqlite3_module,xCommit)); - return SQLITE_OK; -} - -/* -** If the virtual table pVtab supports the transaction interface -** (xBegin/xRollback/xCommit and optionally xSync) and a transaction is -** not currently open, invoke the xBegin method now. -** -** If the xBegin call is successful, place the sqlite3_vtab pointer -** in the sqlite3.aVTrans array. -*/ -SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ - int rc = SQLITE_OK; - const sqlite3_module *pModule; - - /* Special case: If db->aVTrans is NULL and db->nVTrans is greater - ** than zero, then this function is being called from within a - ** virtual module xSync() callback. It is illegal to write to - ** virtual module tables in this case, so return SQLITE_LOCKED. - */ - if( sqlite3VtabInSync(db) ){ - return SQLITE_LOCKED; - } - if( !pVTab ){ - return SQLITE_OK; - } - pModule = pVTab->pVtab->pModule; - - if( pModule->xBegin ){ - int i; - - /* If pVtab is already in the aVTrans array, return early */ - for(i=0; inVTrans; i++){ - if( db->aVTrans[i]==pVTab ){ - return SQLITE_OK; - } - } - - /* Invoke the xBegin method. If successful, add the vtab to the - ** sqlite3.aVTrans[] array. */ - rc = growVTrans(db); - if( rc==SQLITE_OK ){ - rc = pModule->xBegin(pVTab->pVtab); - if( rc==SQLITE_OK ){ - addToVTrans(db, pVTab); - } - } - } - return rc; -} - -/* -** Invoke either the xSavepoint, xRollbackTo or xRelease method of all -** virtual tables that currently have an open transaction. Pass iSavepoint -** as the second argument to the virtual table method invoked. -** -** If op is SAVEPOINT_BEGIN, the xSavepoint method is invoked. If it is -** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is -** SAVEPOINT_RELEASE, then the xRelease method of each virtual table with -** an open transaction is invoked. -** -** If any virtual table method returns an error code other than SQLITE_OK, -** processing is abandoned and the error returned to the caller of this -** function immediately. If all calls to virtual table methods are successful, -** SQLITE_OK is returned. -*/ -SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ - int rc = SQLITE_OK; - - assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN ); - assert( iSavepoint>=0 ); - if( db->aVTrans ){ - int i; - for(i=0; rc==SQLITE_OK && inVTrans; i++){ - VTable *pVTab = db->aVTrans[i]; - const sqlite3_module *pMod = pVTab->pMod->pModule; - if( pVTab->pVtab && pMod->iVersion>=2 ){ - int (*xMethod)(sqlite3_vtab *, int); - switch( op ){ - case SAVEPOINT_BEGIN: - xMethod = pMod->xSavepoint; - pVTab->iSavepoint = iSavepoint+1; - break; - case SAVEPOINT_ROLLBACK: - xMethod = pMod->xRollbackTo; - break; - default: - xMethod = pMod->xRelease; - break; - } - if( xMethod && pVTab->iSavepoint>iSavepoint ){ - rc = xMethod(pVTab->pVtab, iSavepoint); - } - } - } - } - return rc; -} - -/* -** The first parameter (pDef) is a function implementation. The -** second parameter (pExpr) is the first argument to this function. -** If pExpr is a column in a virtual table, then let the virtual -** table implementation have an opportunity to overload the function. -** -** This routine is used to allow virtual table implementations to -** overload MATCH, LIKE, GLOB, and REGEXP operators. -** -** Return either the pDef argument (indicating no change) or a -** new FuncDef structure that is marked as ephemeral using the -** SQLITE_FUNC_EPHEM flag. -*/ -SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction( - sqlite3 *db, /* Database connection for reporting malloc problems */ - FuncDef *pDef, /* Function to possibly overload */ - int nArg, /* Number of arguments to the function */ - Expr *pExpr /* First argument to the function */ -){ - Table *pTab; - sqlite3_vtab *pVtab; - sqlite3_module *pMod; - void (*xFunc)(sqlite3_context*,int,sqlite3_value**) = 0; - void *pArg = 0; - FuncDef *pNew; - int rc = 0; - char *zLowerName; - unsigned char *z; - - - /* Check to see the left operand is a column in a virtual table */ - if( NEVER(pExpr==0) ) return pDef; - if( pExpr->op!=TK_COLUMN ) return pDef; - pTab = pExpr->pTab; - if( NEVER(pTab==0) ) return pDef; - if( (pTab->tabFlags & TF_Virtual)==0 ) return pDef; - pVtab = sqlite3GetVTable(db, pTab)->pVtab; - assert( pVtab!=0 ); - assert( pVtab->pModule!=0 ); - pMod = (sqlite3_module *)pVtab->pModule; - if( pMod->xFindFunction==0 ) return pDef; - - /* Call the xFindFunction method on the virtual table implementation - ** to see if the implementation wants to overload this function - */ - zLowerName = sqlite3DbStrDup(db, pDef->zName); - if( zLowerName ){ - for(z=(unsigned char*)zLowerName; *z; z++){ - *z = sqlite3UpperToLower[*z]; - } - rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xFunc, &pArg); - sqlite3DbFree(db, zLowerName); - } - if( rc==0 ){ - return pDef; - } - - /* Create a new ephemeral function definition for the overloaded - ** function */ - pNew = sqlite3DbMallocZero(db, sizeof(*pNew) - + sqlite3Strlen30(pDef->zName) + 1); - if( pNew==0 ){ - return pDef; - } - *pNew = *pDef; - pNew->zName = (char *)&pNew[1]; - memcpy(pNew->zName, pDef->zName, sqlite3Strlen30(pDef->zName)+1); - pNew->xFunc = xFunc; - pNew->pUserData = pArg; - pNew->flags |= SQLITE_FUNC_EPHEM; - return pNew; -} - -/* -** Make sure virtual table pTab is contained in the pParse->apVirtualLock[] -** array so that an OP_VBegin will get generated for it. Add pTab to the -** array if it is missing. If pTab is already in the array, this routine -** is a no-op. -*/ -SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); - int i, n; - Table **apVtabLock; - - assert( IsVirtual(pTab) ); - for(i=0; inVtabLock; i++){ - if( pTab==pToplevel->apVtabLock[i] ) return; - } - n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); - apVtabLock = sqlite3_realloc(pToplevel->apVtabLock, n); - if( apVtabLock ){ - pToplevel->apVtabLock = apVtabLock; - pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; - }else{ - pToplevel->db->mallocFailed = 1; - } -} - -/* -** Return the ON CONFLICT resolution mode in effect for the virtual -** table update operation currently in progress. -** -** The results of this routine are undefined unless it is called from -** within an xUpdate method. -*/ -SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){ - static const unsigned char aMap[] = { - SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE - }; - assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 ); - assert( OE_Ignore==4 && OE_Replace==5 ); - assert( db->vtabOnConflict>=1 && db->vtabOnConflict<=5 ); - return (int)aMap[db->vtabOnConflict-1]; -} - -/* -** Call from within the xCreate() or xConnect() methods to provide -** the SQLite core with additional information about the behavior -** of the virtual table being implemented. -*/ -SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ - va_list ap; - int rc = SQLITE_OK; - - sqlite3_mutex_enter(db->mutex); - - va_start(ap, op); - switch( op ){ - case SQLITE_VTAB_CONSTRAINT_SUPPORT: { - VtabCtx *p = db->pVtabCtx; - if( !p ){ - rc = SQLITE_MISUSE_BKPT; - }else{ - assert( p->pTab==0 || (p->pTab->tabFlags & TF_Virtual)!=0 ); - p->pVTable->bConstraint = (u8)va_arg(ap, int); - } - break; - } - default: - rc = SQLITE_MISUSE_BKPT; - break; - } - va_end(ap); - - if( rc!=SQLITE_OK ) sqlite3Error(db, rc, 0); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -/************** End of vtab.c ************************************************/ -/************** Begin file where.c *******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This module contains C code that generates VDBE code used to process -** the WHERE clause of SQL statements. This module is responsible for -** generating the code that loops through a table looking for applicable -** rows. Indices are selected and used to speed the search when doing -** so is applicable. Because this module is responsible for selecting -** indices, you might also think of this module as the "query optimizer". -*/ - - -/* -** Trace output macros -*/ -#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) -/***/ int sqlite3WhereTrace = 0; -#endif -#if defined(SQLITE_DEBUG) \ - && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE)) -# define WHERETRACE(X) if(sqlite3WhereTrace) sqlite3DebugPrintf X -#else -# define WHERETRACE(X) -#endif - -/* Forward reference -*/ -typedef struct WhereClause WhereClause; -typedef struct WhereMaskSet WhereMaskSet; -typedef struct WhereOrInfo WhereOrInfo; -typedef struct WhereAndInfo WhereAndInfo; -typedef struct WhereCost WhereCost; - -/* -** The query generator uses an array of instances of this structure to -** help it analyze the subexpressions of the WHERE clause. Each WHERE -** clause subexpression is separated from the others by AND operators, -** usually, or sometimes subexpressions separated by OR. -** -** All WhereTerms are collected into a single WhereClause structure. -** The following identity holds: -** -** WhereTerm.pWC->a[WhereTerm.idx] == WhereTerm -** -** When a term is of the form: -** -** X -** -** where X is a column name and is one of certain operators, -** then WhereTerm.leftCursor and WhereTerm.u.leftColumn record the -** cursor number and column number for X. WhereTerm.eOperator records -** the using a bitmask encoding defined by WO_xxx below. The -** use of a bitmask encoding for the operator allows us to search -** quickly for terms that match any of several different operators. -** -** A WhereTerm might also be two or more subterms connected by OR: -** -** (t1.X ) OR (t1.Y ) OR .... -** -** In this second case, wtFlag as the TERM_ORINFO set and eOperator==WO_OR -** and the WhereTerm.u.pOrInfo field points to auxiliary information that -** is collected about the -** -** If a term in the WHERE clause does not match either of the two previous -** categories, then eOperator==0. The WhereTerm.pExpr field is still set -** to the original subexpression content and wtFlags is set up appropriately -** but no other fields in the WhereTerm object are meaningful. -** -** When eOperator!=0, prereqRight and prereqAll record sets of cursor numbers, -** but they do so indirectly. A single WhereMaskSet structure translates -** cursor number into bits and the translated bit is stored in the prereq -** fields. The translation is used in order to maximize the number of -** bits that will fit in a Bitmask. The VDBE cursor numbers might be -** spread out over the non-negative integers. For example, the cursor -** numbers might be 3, 8, 9, 10, 20, 23, 41, and 45. The WhereMaskSet -** translates these sparse cursor numbers into consecutive integers -** beginning with 0 in order to make the best possible use of the available -** bits in the Bitmask. So, in the example above, the cursor numbers -** would be mapped into integers 0 through 7. -** -** The number of terms in a join is limited by the number of bits -** in prereqRight and prereqAll. The default is 64 bits, hence SQLite -** is only able to process joins with 64 or fewer tables. -*/ -typedef struct WhereTerm WhereTerm; -struct WhereTerm { - Expr *pExpr; /* Pointer to the subexpression that is this term */ - int iParent; /* Disable pWC->a[iParent] when this term disabled */ - int leftCursor; /* Cursor number of X in "X " */ - union { - int leftColumn; /* Column number of X in "X " */ - WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ - WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ - } u; - u16 eOperator; /* A WO_xx value describing */ - u8 wtFlags; /* TERM_xxx bit flags. See below */ - u8 nChild; /* Number of children that must disable us */ - WhereClause *pWC; /* The clause this term is part of */ - Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */ - Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */ -}; - -/* -** Allowed values of WhereTerm.wtFlags -*/ -#define TERM_DYNAMIC 0x01 /* Need to call sqlite3ExprDelete(db, pExpr) */ -#define TERM_VIRTUAL 0x02 /* Added by the optimizer. Do not code */ -#define TERM_CODED 0x04 /* This term is already coded */ -#define TERM_COPIED 0x08 /* Has a child */ -#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */ -#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */ -#define TERM_OR_OK 0x40 /* Used during OR-clause processing */ -#ifdef SQLITE_ENABLE_STAT3 -# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */ -#else -# define TERM_VNULL 0x00 /* Disabled if not using stat3 */ -#endif - -/* -** An instance of the following structure holds all information about a -** WHERE clause. Mostly this is a container for one or more WhereTerms. -** -** Explanation of pOuter: For a WHERE clause of the form -** -** a AND ((b AND c) OR (d AND e)) AND f -** -** There are separate WhereClause objects for the whole clause and for -** the subclauses "(b AND c)" and "(d AND e)". The pOuter field of the -** subclauses points to the WhereClause object for the whole clause. -*/ -struct WhereClause { - Parse *pParse; /* The parser context */ - WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */ - WhereClause *pOuter; /* Outer conjunction */ - u8 op; /* Split operator. TK_AND or TK_OR */ - u16 wctrlFlags; /* Might include WHERE_AND_ONLY */ - int nTerm; /* Number of terms */ - int nSlot; /* Number of entries in a[] */ - WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ -#if defined(SQLITE_SMALL_STACK) - WhereTerm aStatic[1]; /* Initial static space for a[] */ -#else - WhereTerm aStatic[8]; /* Initial static space for a[] */ -#endif -}; - -/* -** A WhereTerm with eOperator==WO_OR has its u.pOrInfo pointer set to -** a dynamically allocated instance of the following structure. -*/ -struct WhereOrInfo { - WhereClause wc; /* Decomposition into subterms */ - Bitmask indexable; /* Bitmask of all indexable tables in the clause */ -}; - -/* -** A WhereTerm with eOperator==WO_AND has its u.pAndInfo pointer set to -** a dynamically allocated instance of the following structure. -*/ -struct WhereAndInfo { - WhereClause wc; /* The subexpression broken out */ -}; - -/* -** An instance of the following structure keeps track of a mapping -** between VDBE cursor numbers and bits of the bitmasks in WhereTerm. -** -** The VDBE cursor numbers are small integers contained in -** SrcList_item.iCursor and Expr.iTable fields. For any given WHERE -** clause, the cursor numbers might not begin with 0 and they might -** contain gaps in the numbering sequence. But we want to make maximum -** use of the bits in our bitmasks. This structure provides a mapping -** from the sparse cursor numbers into consecutive integers beginning -** with 0. -** -** If WhereMaskSet.ix[A]==B it means that The A-th bit of a Bitmask -** corresponds VDBE cursor number B. The A-th bit of a bitmask is 1<3, 5->1, 8->2, 29->0, -** 57->5, 73->4. Or one of 719 other combinations might be used. It -** does not really matter. What is important is that sparse cursor -** numbers all get mapped into bit numbers that begin with 0 and contain -** no gaps. -*/ -struct WhereMaskSet { - int n; /* Number of assigned cursor values */ - int ix[BMS]; /* Cursor assigned to each bit */ -}; - -/* -** A WhereCost object records a lookup strategy and the estimated -** cost of pursuing that strategy. -*/ -struct WhereCost { - WherePlan plan; /* The lookup strategy */ - double rCost; /* Overall cost of pursuing this search strategy */ - Bitmask used; /* Bitmask of cursors used by this plan */ -}; - -/* -** Bitmasks for the operators that indices are able to exploit. An -** OR-ed combination of these values can be used when searching for -** terms in the where clause. -*/ -#define WO_IN 0x001 -#define WO_EQ 0x002 -#define WO_LT (WO_EQ<<(TK_LT-TK_EQ)) -#define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) -#define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) -#define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) -#define WO_MATCH 0x040 -#define WO_ISNULL 0x080 -#define WO_OR 0x100 /* Two or more OR-connected terms */ -#define WO_AND 0x200 /* Two or more AND-connected terms */ -#define WO_EQUIV 0x400 /* Of the form A==B, both columns */ -#define WO_NOOP 0x800 /* This term does not restrict search space */ - -#define WO_ALL 0xfff /* Mask of all possible WO_* values */ -#define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ - -/* -** Value for wsFlags returned by bestIndex() and stored in -** WhereLevel.wsFlags. These flags determine which search -** strategies are appropriate. -** -** The least significant 12 bits is reserved as a mask for WO_ values above. -** The WhereLevel.wsFlags field is usually set to WO_IN|WO_EQ|WO_ISNULL. -** But if the table is the right table of a left join, WhereLevel.wsFlags -** is set to WO_IN|WO_EQ. The WhereLevel.wsFlags field can then be used as -** the "op" parameter to findTerm when we are resolving equality constraints. -** ISNULL constraints will then not be used on the right table of a left -** join. Tickets #2177 and #2189. -*/ -#define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */ -#define WHERE_ROWID_RANGE 0x00002000 /* rowidEXPR */ -#define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ -#define WHERE_COLUMN_RANGE 0x00020000 /* xEXPR */ -#define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ -#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ -#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ -#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */ -#define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */ -#define WHERE_TOP_LIMIT 0x00100000 /* xEXPR or x>=EXPR constraint */ -#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and xrCostrCost ) return 1; - if( pProbe->rCost>pBaseline->rCost ) return 0; - if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1; - if( pProbe->plan.nRowplan.nRow ) return 1; - return 0; -} - -/* -** Initialize a preallocated WhereClause structure. -*/ -static void whereClauseInit( - WhereClause *pWC, /* The WhereClause to be initialized */ - Parse *pParse, /* The parsing context */ - WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmasks */ - u16 wctrlFlags /* Might include WHERE_AND_ONLY */ -){ - pWC->pParse = pParse; - pWC->pMaskSet = pMaskSet; - pWC->pOuter = 0; - pWC->nTerm = 0; - pWC->nSlot = ArraySize(pWC->aStatic); - pWC->a = pWC->aStatic; - pWC->wctrlFlags = wctrlFlags; -} - -/* Forward reference */ -static void whereClauseClear(WhereClause*); - -/* -** Deallocate all memory associated with a WhereOrInfo object. -*/ -static void whereOrInfoDelete(sqlite3 *db, WhereOrInfo *p){ - whereClauseClear(&p->wc); - sqlite3DbFree(db, p); -} - -/* -** Deallocate all memory associated with a WhereAndInfo object. -*/ -static void whereAndInfoDelete(sqlite3 *db, WhereAndInfo *p){ - whereClauseClear(&p->wc); - sqlite3DbFree(db, p); -} - -/* -** Deallocate a WhereClause structure. The WhereClause structure -** itself is not freed. This routine is the inverse of whereClauseInit(). -*/ -static void whereClauseClear(WhereClause *pWC){ - int i; - WhereTerm *a; - sqlite3 *db = pWC->pParse->db; - for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){ - if( a->wtFlags & TERM_DYNAMIC ){ - sqlite3ExprDelete(db, a->pExpr); - } - if( a->wtFlags & TERM_ORINFO ){ - whereOrInfoDelete(db, a->u.pOrInfo); - }else if( a->wtFlags & TERM_ANDINFO ){ - whereAndInfoDelete(db, a->u.pAndInfo); - } - } - if( pWC->a!=pWC->aStatic ){ - sqlite3DbFree(db, pWC->a); - } -} - -/* -** Add a single new WhereTerm entry to the WhereClause object pWC. -** The new WhereTerm object is constructed from Expr p and with wtFlags. -** The index in pWC->a[] of the new WhereTerm is returned on success. -** 0 is returned if the new WhereTerm could not be added due to a memory -** allocation error. The memory allocation failure will be recorded in -** the db->mallocFailed flag so that higher-level functions can detect it. -** -** This routine will increase the size of the pWC->a[] array as necessary. -** -** If the wtFlags argument includes TERM_DYNAMIC, then responsibility -** for freeing the expression p is assumed by the WhereClause object pWC. -** This is true even if this routine fails to allocate a new WhereTerm. -** -** WARNING: This routine might reallocate the space used to store -** WhereTerms. All pointers to WhereTerms should be invalidated after -** calling this routine. Such pointers may be reinitialized by referencing -** the pWC->a[] array. -*/ -static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ - WhereTerm *pTerm; - int idx; - testcase( wtFlags & TERM_VIRTUAL ); /* EV: R-00211-15100 */ - if( pWC->nTerm>=pWC->nSlot ){ - WhereTerm *pOld = pWC->a; - sqlite3 *db = pWC->pParse->db; - pWC->a = sqlite3DbMallocRaw(db, sizeof(pWC->a[0])*pWC->nSlot*2 ); - if( pWC->a==0 ){ - if( wtFlags & TERM_DYNAMIC ){ - sqlite3ExprDelete(db, p); - } - pWC->a = pOld; - return 0; - } - memcpy(pWC->a, pOld, sizeof(pWC->a[0])*pWC->nTerm); - if( pOld!=pWC->aStatic ){ - sqlite3DbFree(db, pOld); - } - pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]); - } - pTerm = &pWC->a[idx = pWC->nTerm++]; - pTerm->pExpr = sqlite3ExprSkipCollate(p); - pTerm->wtFlags = wtFlags; - pTerm->pWC = pWC; - pTerm->iParent = -1; - return idx; -} - -/* -** This routine identifies subexpressions in the WHERE clause where -** each subexpression is separated by the AND operator or some other -** operator specified in the op parameter. The WhereClause structure -** is filled with pointers to subexpressions. For example: -** -** WHERE a=='hello' AND coalesce(b,11)<10 AND (c+12!=d OR c==22) -** \________/ \_______________/ \________________/ -** slot[0] slot[1] slot[2] -** -** The original WHERE clause in pExpr is unaltered. All this routine -** does is make slot[] entries point to substructure within pExpr. -** -** In the previous sentence and in the diagram, "slot[]" refers to -** the WhereClause.a[] array. The slot[] array grows as needed to contain -** all terms of the WHERE clause. -*/ -static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){ - pWC->op = (u8)op; - if( pExpr==0 ) return; - if( pExpr->op!=op ){ - whereClauseInsert(pWC, pExpr, 0); - }else{ - whereSplit(pWC, pExpr->pLeft, op); - whereSplit(pWC, pExpr->pRight, op); - } -} - -/* -** Initialize an expression mask set (a WhereMaskSet object) -*/ -#define initMaskSet(P) memset(P, 0, sizeof(*P)) - -/* -** Return the bitmask for the given cursor number. Return 0 if -** iCursor is not in the set. -*/ -static Bitmask getMask(WhereMaskSet *pMaskSet, int iCursor){ - int i; - assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); - for(i=0; in; i++){ - if( pMaskSet->ix[i]==iCursor ){ - return ((Bitmask)1)<ix[] -** array will never overflow. -*/ -static void createMask(WhereMaskSet *pMaskSet, int iCursor){ - assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); - pMaskSet->ix[pMaskSet->n++] = iCursor; -} - -/* -** This routine walks (recursively) an expression tree and generates -** a bitmask indicating which tables are used in that expression -** tree. -** -** In order for this routine to work, the calling function must have -** previously invoked sqlite3ResolveExprNames() on the expression. See -** the header comment on that routine for additional information. -** The sqlite3ResolveExprNames() routines looks for column names and -** sets their opcodes to TK_COLUMN and their Expr.iTable fields to -** the VDBE cursor number of the table. This routine just has to -** translate the cursor numbers into bitmask values and OR all -** the bitmasks together. -*/ -static Bitmask exprListTableUsage(WhereMaskSet*, ExprList*); -static Bitmask exprSelectTableUsage(WhereMaskSet*, Select*); -static Bitmask exprTableUsage(WhereMaskSet *pMaskSet, Expr *p){ - Bitmask mask = 0; - if( p==0 ) return 0; - if( p->op==TK_COLUMN ){ - mask = getMask(pMaskSet, p->iTable); - return mask; - } - mask = exprTableUsage(pMaskSet, p->pRight); - mask |= exprTableUsage(pMaskSet, p->pLeft); - if( ExprHasProperty(p, EP_xIsSelect) ){ - mask |= exprSelectTableUsage(pMaskSet, p->x.pSelect); - }else{ - mask |= exprListTableUsage(pMaskSet, p->x.pList); - } - return mask; -} -static Bitmask exprListTableUsage(WhereMaskSet *pMaskSet, ExprList *pList){ - int i; - Bitmask mask = 0; - if( pList ){ - for(i=0; inExpr; i++){ - mask |= exprTableUsage(pMaskSet, pList->a[i].pExpr); - } - } - return mask; -} -static Bitmask exprSelectTableUsage(WhereMaskSet *pMaskSet, Select *pS){ - Bitmask mask = 0; - while( pS ){ - SrcList *pSrc = pS->pSrc; - mask |= exprListTableUsage(pMaskSet, pS->pEList); - mask |= exprListTableUsage(pMaskSet, pS->pGroupBy); - mask |= exprListTableUsage(pMaskSet, pS->pOrderBy); - mask |= exprTableUsage(pMaskSet, pS->pWhere); - mask |= exprTableUsage(pMaskSet, pS->pHaving); - if( ALWAYS(pSrc!=0) ){ - int i; - for(i=0; inSrc; i++){ - mask |= exprSelectTableUsage(pMaskSet, pSrc->a[i].pSelect); - mask |= exprTableUsage(pMaskSet, pSrc->a[i].pOn); - } - } - pS = pS->pPrior; - } - return mask; -} - -/* -** Return TRUE if the given operator is one of the operators that is -** allowed for an indexable WHERE clause term. The allowed operators are -** "=", "<", ">", "<=", ">=", and "IN". -** -** IMPLEMENTATION-OF: R-59926-26393 To be usable by an index a term must be -** of one of the following forms: column = expression column > expression -** column >= expression column < expression column <= expression -** expression = column expression > column expression >= column -** expression < column expression <= column column IN -** (expression-list) column IN (subquery) column IS NULL -*/ -static int allowedOp(int op){ - assert( TK_GT>TK_EQ && TK_GTTK_EQ && TK_LTTK_EQ && TK_LE=TK_EQ && op<=TK_GE) || op==TK_ISNULL; -} - -/* -** Swap two objects of type TYPE. -*/ -#define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;} - -/* -** Commute a comparison operator. Expressions of the form "X op Y" -** are converted into "Y op X". -** -** If left/right precedence rules come into play when determining the -** collating -** side of the comparison, it remains associated with the same side after -** the commutation. So "Y collate NOCASE op X" becomes -** "X op Y". This is because any collation sequence on -** the left hand side of a comparison overrides any collation sequence -** attached to the right. For the same reason the EP_Collate flag -** is not commuted. -*/ -static void exprCommute(Parse *pParse, Expr *pExpr){ - u16 expRight = (pExpr->pRight->flags & EP_Collate); - u16 expLeft = (pExpr->pLeft->flags & EP_Collate); - assert( allowedOp(pExpr->op) && pExpr->op!=TK_IN ); - if( expRight==expLeft ){ - /* Either X and Y both have COLLATE operator or neither do */ - if( expRight ){ - /* Both X and Y have COLLATE operators. Make sure X is always - ** used by clearing the EP_Collate flag from Y. */ - pExpr->pRight->flags &= ~EP_Collate; - }else if( sqlite3ExprCollSeq(pParse, pExpr->pLeft)!=0 ){ - /* Neither X nor Y have COLLATE operators, but X has a non-default - ** collating sequence. So add the EP_Collate marker on X to cause - ** it to be searched first. */ - pExpr->pLeft->flags |= EP_Collate; - } - } - SWAP(Expr*,pExpr->pRight,pExpr->pLeft); - if( pExpr->op>=TK_GT ){ - assert( TK_LT==TK_GT+2 ); - assert( TK_GE==TK_LE+2 ); - assert( TK_GT>TK_EQ ); - assert( TK_GTop>=TK_GT && pExpr->op<=TK_GE ); - pExpr->op = ((pExpr->op-TK_GT)^2)+TK_GT; - } -} - -/* -** Translate from TK_xx operator to WO_xx bitmask. -*/ -static u16 operatorMask(int op){ - u16 c; - assert( allowedOp(op) ); - if( op==TK_IN ){ - c = WO_IN; - }else if( op==TK_ISNULL ){ - c = WO_ISNULL; - }else{ - assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); - c = (u16)(WO_EQ<<(op-TK_EQ)); - } - assert( op!=TK_ISNULL || c==WO_ISNULL ); - assert( op!=TK_IN || c==WO_IN ); - assert( op!=TK_EQ || c==WO_EQ ); - assert( op!=TK_LT || c==WO_LT ); - assert( op!=TK_LE || c==WO_LE ); - assert( op!=TK_GT || c==WO_GT ); - assert( op!=TK_GE || c==WO_GE ); - return c; -} - -/* -** Search for a term in the WHERE clause that is of the form "X " -** where X is a reference to the iColumn of table iCur and is one of -** the WO_xx operator codes specified by the op parameter. -** Return a pointer to the term. Return 0 if not found. -** -** The term returned might by Y= if there is another constraint in -** the WHERE clause that specifies that X=Y. Any such constraints will be -** identified by the WO_EQUIV bit in the pTerm->eOperator field. The -** aEquiv[] array holds X and all its equivalents, with each SQL variable -** taking up two slots in aEquiv[]. The first slot is for the cursor number -** and the second is for the column number. There are 22 slots in aEquiv[] -** so that means we can look for X plus up to 10 other equivalent values. -** Hence a search for X will return if X=A1 and A1=A2 and A2=A3 -** and ... and A9=A10 and A10=. -** -** If there are multiple terms in the WHERE clause of the form "X " -** then try for the one with no dependencies on - in other words where -** is a constant expression of some kind. Only return entries of -** the form "X Y" where Y is a column in another table if no terms of -** the form "X " exist. If no terms with a constant RHS -** exist, try to return a term that does not use WO_EQUIV. -*/ -static WhereTerm *findTerm( - WhereClause *pWC, /* The WHERE clause to be searched */ - int iCur, /* Cursor number of LHS */ - int iColumn, /* Column number of LHS */ - Bitmask notReady, /* RHS must not overlap with this mask */ - u32 op, /* Mask of WO_xx values describing operator */ - Index *pIdx /* Must be compatible with this index, if not NULL */ -){ - WhereTerm *pTerm; /* Term being examined as possible result */ - WhereTerm *pResult = 0; /* The answer to return */ - WhereClause *pWCOrig = pWC; /* Original pWC value */ - int j, k; /* Loop counters */ - Expr *pX; /* Pointer to an expression */ - Parse *pParse; /* Parsing context */ - int iOrigCol = iColumn; /* Original value of iColumn */ - int nEquiv = 2; /* Number of entires in aEquiv[] */ - int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */ - int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */ - - assert( iCur>=0 ); - aEquiv[0] = iCur; - aEquiv[1] = iColumn; - for(;;){ - for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){ - for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ - if( pTerm->leftCursor==iCur - && pTerm->u.leftColumn==iColumn - ){ - if( (pTerm->prereqRight & notReady)==0 - && (pTerm->eOperator & op & WO_ALL)!=0 - ){ - if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){ - CollSeq *pColl; - char idxaff; - - pX = pTerm->pExpr; - pParse = pWC->pParse; - idxaff = pIdx->pTable->aCol[iOrigCol].affinity; - if( !sqlite3IndexAffinityOk(pX, idxaff) ){ - continue; - } - - /* Figure out the collation sequence required from an index for - ** it to be useful for optimising expression pX. Store this - ** value in variable pColl. - */ - assert(pX->pLeft); - pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight); - if( pColl==0 ) pColl = pParse->db->pDfltColl; - - for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){ - if( NEVER(j>=pIdx->nColumn) ) return 0; - } - if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){ - continue; - } - } - if( pTerm->prereqRight==0 ){ - pResult = pTerm; - goto findTerm_success; - }else if( pResult==0 ){ - pResult = pTerm; - } - } - if( (pTerm->eOperator & WO_EQUIV)!=0 - && nEquivpExpr->pRight); - assert( pX->op==TK_COLUMN ); - for(j=0; jiTable && aEquiv[j+1]==pX->iColumn ) break; - } - if( j==nEquiv ){ - aEquiv[j] = pX->iTable; - aEquiv[j+1] = pX->iColumn; - nEquiv += 2; - } - } - } - } - } - if( iEquiv>=nEquiv ) break; - iCur = aEquiv[iEquiv++]; - iColumn = aEquiv[iEquiv++]; - } -findTerm_success: - return pResult; -} - -/* Forward reference */ -static void exprAnalyze(SrcList*, WhereClause*, int); - -/* -** Call exprAnalyze on all terms in a WHERE clause. -** -** -*/ -static void exprAnalyzeAll( - SrcList *pTabList, /* the FROM clause */ - WhereClause *pWC /* the WHERE clause to be analyzed */ -){ - int i; - for(i=pWC->nTerm-1; i>=0; i--){ - exprAnalyze(pTabList, pWC, i); - } -} - -#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION -/* -** Check to see if the given expression is a LIKE or GLOB operator that -** can be optimized using inequality constraints. Return TRUE if it is -** so and false if not. -** -** In order for the operator to be optimizible, the RHS must be a string -** literal that does not begin with a wildcard. -*/ -static int isLikeOrGlob( - Parse *pParse, /* Parsing and code generating context */ - Expr *pExpr, /* Test this expression */ - Expr **ppPrefix, /* Pointer to TK_STRING expression with pattern prefix */ - int *pisComplete, /* True if the only wildcard is % in the last character */ - int *pnoCase /* True if uppercase is equivalent to lowercase */ -){ - const char *z = 0; /* String on RHS of LIKE operator */ - Expr *pRight, *pLeft; /* Right and left size of LIKE operator */ - ExprList *pList; /* List of operands to the LIKE operator */ - int c; /* One character in z[] */ - int cnt; /* Number of non-wildcard prefix characters */ - char wc[3]; /* Wildcard characters */ - sqlite3 *db = pParse->db; /* Database connection */ - sqlite3_value *pVal = 0; - int op; /* Opcode of pRight */ - - if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){ - return 0; - } -#ifdef SQLITE_EBCDIC - if( *pnoCase ) return 0; -#endif - pList = pExpr->x.pList; - pLeft = pList->a[1].pExpr; - if( pLeft->op!=TK_COLUMN - || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT - || IsVirtual(pLeft->pTab) - ){ - /* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator must - ** be the name of an indexed column with TEXT affinity. */ - return 0; - } - assert( pLeft->iColumn!=(-1) ); /* Because IPK never has AFF_TEXT */ - - pRight = pList->a[0].pExpr; - op = pRight->op; - if( op==TK_REGISTER ){ - op = pRight->op2; - } - if( op==TK_VARIABLE ){ - Vdbe *pReprepare = pParse->pReprepare; - int iCol = pRight->iColumn; - pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE); - if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ - z = (char *)sqlite3_value_text(pVal); - } - sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); - assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); - }else if( op==TK_STRING ){ - z = pRight->u.zToken; - } - if( z ){ - cnt = 0; - while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ - cnt++; - } - if( cnt!=0 && 255!=(u8)z[cnt-1] ){ - Expr *pPrefix; - *pisComplete = c==wc[0] && z[cnt+1]==0; - pPrefix = sqlite3Expr(db, TK_STRING, z); - if( pPrefix ) pPrefix->u.zToken[cnt] = 0; - *ppPrefix = pPrefix; - if( op==TK_VARIABLE ){ - Vdbe *v = pParse->pVdbe; - sqlite3VdbeSetVarmask(v, pRight->iColumn); - if( *pisComplete && pRight->u.zToken[1] ){ - /* If the rhs of the LIKE expression is a variable, and the current - ** value of the variable means there is no need to invoke the LIKE - ** function, then no OP_Variable will be added to the program. - ** This causes problems for the sqlite3_bind_parameter_name() - ** API. To workaround them, add a dummy OP_Variable here. - */ - int r1 = sqlite3GetTempReg(pParse); - sqlite3ExprCodeTarget(pParse, pRight, r1); - sqlite3VdbeChangeP3(v, sqlite3VdbeCurrentAddr(v)-1, 0); - sqlite3ReleaseTempReg(pParse, r1); - } - } - }else{ - z = 0; - } - } - - sqlite3ValueFree(pVal); - return (z!=0); -} -#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ - - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* -** Check to see if the given expression is of the form -** -** column MATCH expr -** -** If it is then return TRUE. If not, return FALSE. -*/ -static int isMatchOfColumn( - Expr *pExpr /* Test this expression */ -){ - ExprList *pList; - - if( pExpr->op!=TK_FUNCTION ){ - return 0; - } - if( sqlite3StrICmp(pExpr->u.zToken,"match")!=0 ){ - return 0; - } - pList = pExpr->x.pList; - if( pList->nExpr!=2 ){ - return 0; - } - if( pList->a[1].pExpr->op != TK_COLUMN ){ - return 0; - } - return 1; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -/* -** If the pBase expression originated in the ON or USING clause of -** a join, then transfer the appropriate markings over to derived. -*/ -static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ - pDerived->flags |= pBase->flags & EP_FromJoin; - pDerived->iRightJoinTable = pBase->iRightJoinTable; -} - -#if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY) -/* -** Analyze a term that consists of two or more OR-connected -** subterms. So in: -** -** ... WHERE (a=5) AND (b=7 OR c=9 OR d=13) AND (d=13) -** ^^^^^^^^^^^^^^^^^^^^ -** -** This routine analyzes terms such as the middle term in the above example. -** A WhereOrTerm object is computed and attached to the term under -** analysis, regardless of the outcome of the analysis. Hence: -** -** WhereTerm.wtFlags |= TERM_ORINFO -** WhereTerm.u.pOrInfo = a dynamically allocated WhereOrTerm object -** -** The term being analyzed must have two or more of OR-connected subterms. -** A single subterm might be a set of AND-connected sub-subterms. -** Examples of terms under analysis: -** -** (A) t1.x=t2.y OR t1.x=t2.z OR t1.y=15 OR t1.z=t3.a+5 -** (B) x=expr1 OR expr2=x OR x=expr3 -** (C) t1.x=t2.y OR (t1.x=t2.z AND t1.y=15) -** (D) x=expr1 OR (y>11 AND y<22 AND z LIKE '*hello*') -** (E) (p.a=1 AND q.b=2 AND r.c=3) OR (p.x=4 AND q.y=5 AND r.z=6) -** -** CASE 1: -** -** If all subterms are of the form T.C=expr for some single column of C and -** a single table T (as shown in example B above) then create a new virtual -** term that is an equivalent IN expression. In other words, if the term -** being analyzed is: -** -** x = expr1 OR expr2 = x OR x = expr3 -** -** then create a new virtual term like this: -** -** x IN (expr1,expr2,expr3) -** -** CASE 2: -** -** If all subterms are indexable by a single table T, then set -** -** WhereTerm.eOperator = WO_OR -** WhereTerm.u.pOrInfo->indexable |= the cursor number for table T -** -** A subterm is "indexable" if it is of the form -** "T.C " where C is any column of table T and -** is one of "=", "<", "<=", ">", ">=", "IS NULL", or "IN". -** A subterm is also indexable if it is an AND of two or more -** subsubterms at least one of which is indexable. Indexable AND -** subterms have their eOperator set to WO_AND and they have -** u.pAndInfo set to a dynamically allocated WhereAndTerm object. -** -** From another point of view, "indexable" means that the subterm could -** potentially be used with an index if an appropriate index exists. -** This analysis does not consider whether or not the index exists; that -** is something the bestIndex() routine will determine. This analysis -** only looks at whether subterms appropriate for indexing exist. -** -** All examples A through E above all satisfy case 2. But if a term -** also statisfies case 1 (such as B) we know that the optimizer will -** always prefer case 1, so in that case we pretend that case 2 is not -** satisfied. -** -** It might be the case that multiple tables are indexable. For example, -** (E) above is indexable on tables P, Q, and R. -** -** Terms that satisfy case 2 are candidates for lookup by using -** separate indices to find rowids for each subterm and composing -** the union of all rowids using a RowSet object. This is similar -** to "bitmap indices" in other database engines. -** -** OTHERWISE: -** -** If neither case 1 nor case 2 apply, then leave the eOperator set to -** zero. This term is not useful for search. -*/ -static void exprAnalyzeOrTerm( - SrcList *pSrc, /* the FROM clause */ - WhereClause *pWC, /* the complete WHERE clause */ - int idxTerm /* Index of the OR-term to be analyzed */ -){ - Parse *pParse = pWC->pParse; /* Parser context */ - sqlite3 *db = pParse->db; /* Database connection */ - WhereTerm *pTerm = &pWC->a[idxTerm]; /* The term to be analyzed */ - Expr *pExpr = pTerm->pExpr; /* The expression of the term */ - WhereMaskSet *pMaskSet = pWC->pMaskSet; /* Table use masks */ - int i; /* Loop counters */ - WhereClause *pOrWc; /* Breakup of pTerm into subterms */ - WhereTerm *pOrTerm; /* A Sub-term within the pOrWc */ - WhereOrInfo *pOrInfo; /* Additional information associated with pTerm */ - Bitmask chngToIN; /* Tables that might satisfy case 1 */ - Bitmask indexable; /* Tables that are indexable, satisfying case 2 */ - - /* - ** Break the OR clause into its separate subterms. The subterms are - ** stored in a WhereClause structure containing within the WhereOrInfo - ** object that is attached to the original OR clause term. - */ - assert( (pTerm->wtFlags & (TERM_DYNAMIC|TERM_ORINFO|TERM_ANDINFO))==0 ); - assert( pExpr->op==TK_OR ); - pTerm->u.pOrInfo = pOrInfo = sqlite3DbMallocZero(db, sizeof(*pOrInfo)); - if( pOrInfo==0 ) return; - pTerm->wtFlags |= TERM_ORINFO; - pOrWc = &pOrInfo->wc; - whereClauseInit(pOrWc, pWC->pParse, pMaskSet, pWC->wctrlFlags); - whereSplit(pOrWc, pExpr, TK_OR); - exprAnalyzeAll(pSrc, pOrWc); - if( db->mallocFailed ) return; - assert( pOrWc->nTerm>=2 ); - - /* - ** Compute the set of tables that might satisfy cases 1 or 2. - */ - indexable = ~(Bitmask)0; - chngToIN = ~(Bitmask)0; - for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ - if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ - WhereAndInfo *pAndInfo; - assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); - chngToIN = 0; - pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); - if( pAndInfo ){ - WhereClause *pAndWC; - WhereTerm *pAndTerm; - int j; - Bitmask b = 0; - pOrTerm->u.pAndInfo = pAndInfo; - pOrTerm->wtFlags |= TERM_ANDINFO; - pOrTerm->eOperator = WO_AND; - pAndWC = &pAndInfo->wc; - whereClauseInit(pAndWC, pWC->pParse, pMaskSet, pWC->wctrlFlags); - whereSplit(pAndWC, pOrTerm->pExpr, TK_AND); - exprAnalyzeAll(pSrc, pAndWC); - pAndWC->pOuter = pWC; - testcase( db->mallocFailed ); - if( !db->mallocFailed ){ - for(j=0, pAndTerm=pAndWC->a; jnTerm; j++, pAndTerm++){ - assert( pAndTerm->pExpr ); - if( allowedOp(pAndTerm->pExpr->op) ){ - b |= getMask(pMaskSet, pAndTerm->leftCursor); - } - } - } - indexable &= b; - } - }else if( pOrTerm->wtFlags & TERM_COPIED ){ - /* Skip this term for now. We revisit it when we process the - ** corresponding TERM_VIRTUAL term */ - }else{ - Bitmask b; - b = getMask(pMaskSet, pOrTerm->leftCursor); - if( pOrTerm->wtFlags & TERM_VIRTUAL ){ - WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; - b |= getMask(pMaskSet, pOther->leftCursor); - } - indexable &= b; - if( (pOrTerm->eOperator & WO_EQ)==0 ){ - chngToIN = 0; - }else{ - chngToIN &= b; - } - } - } - - /* - ** Record the set of tables that satisfy case 2. The set might be - ** empty. - */ - pOrInfo->indexable = indexable; - pTerm->eOperator = indexable==0 ? 0 : WO_OR; - - /* - ** chngToIN holds a set of tables that *might* satisfy case 1. But - ** we have to do some additional checking to see if case 1 really - ** is satisfied. - ** - ** chngToIN will hold either 0, 1, or 2 bits. The 0-bit case means - ** that there is no possibility of transforming the OR clause into an - ** IN operator because one or more terms in the OR clause contain - ** something other than == on a column in the single table. The 1-bit - ** case means that every term of the OR clause is of the form - ** "table.column=expr" for some single table. The one bit that is set - ** will correspond to the common table. We still need to check to make - ** sure the same column is used on all terms. The 2-bit case is when - ** the all terms are of the form "table1.column=table2.column". It - ** might be possible to form an IN operator with either table1.column - ** or table2.column as the LHS if either is common to every term of - ** the OR clause. - ** - ** Note that terms of the form "table.column1=table.column2" (the - ** same table on both sizes of the ==) cannot be optimized. - */ - if( chngToIN ){ - int okToChngToIN = 0; /* True if the conversion to IN is valid */ - int iColumn = -1; /* Column index on lhs of IN operator */ - int iCursor = -1; /* Table cursor common to all terms */ - int j = 0; /* Loop counter */ - - /* Search for a table and column that appears on one side or the - ** other of the == operator in every subterm. That table and column - ** will be recorded in iCursor and iColumn. There might not be any - ** such table and column. Set okToChngToIN if an appropriate table - ** and column is found but leave okToChngToIN false if not found. - */ - for(j=0; j<2 && !okToChngToIN; j++){ - pOrTerm = pOrWc->a; - for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ - assert( pOrTerm->eOperator & WO_EQ ); - pOrTerm->wtFlags &= ~TERM_OR_OK; - if( pOrTerm->leftCursor==iCursor ){ - /* This is the 2-bit case and we are on the second iteration and - ** current term is from the first iteration. So skip this term. */ - assert( j==1 ); - continue; - } - if( (chngToIN & getMask(pMaskSet, pOrTerm->leftCursor))==0 ){ - /* This term must be of the form t1.a==t2.b where t2 is in the - ** chngToIN set but t1 is not. This term will be either preceeded - ** or follwed by an inverted copy (t2.b==t1.a). Skip this term - ** and use its inversion. */ - testcase( pOrTerm->wtFlags & TERM_COPIED ); - testcase( pOrTerm->wtFlags & TERM_VIRTUAL ); - assert( pOrTerm->wtFlags & (TERM_COPIED|TERM_VIRTUAL) ); - continue; - } - iColumn = pOrTerm->u.leftColumn; - iCursor = pOrTerm->leftCursor; - break; - } - if( i<0 ){ - /* No candidate table+column was found. This can only occur - ** on the second iteration */ - assert( j==1 ); - assert( IsPowerOfTwo(chngToIN) ); - assert( chngToIN==getMask(pMaskSet, iCursor) ); - break; - } - testcase( j==1 ); - - /* We have found a candidate table and column. Check to see if that - ** table and column is common to every term in the OR clause */ - okToChngToIN = 1; - for(; i>=0 && okToChngToIN; i--, pOrTerm++){ - assert( pOrTerm->eOperator & WO_EQ ); - if( pOrTerm->leftCursor!=iCursor ){ - pOrTerm->wtFlags &= ~TERM_OR_OK; - }else if( pOrTerm->u.leftColumn!=iColumn ){ - okToChngToIN = 0; - }else{ - int affLeft, affRight; - /* If the right-hand side is also a column, then the affinities - ** of both right and left sides must be such that no type - ** conversions are required on the right. (Ticket #2249) - */ - affRight = sqlite3ExprAffinity(pOrTerm->pExpr->pRight); - affLeft = sqlite3ExprAffinity(pOrTerm->pExpr->pLeft); - if( affRight!=0 && affRight!=affLeft ){ - okToChngToIN = 0; - }else{ - pOrTerm->wtFlags |= TERM_OR_OK; - } - } - } - } - - /* At this point, okToChngToIN is true if original pTerm satisfies - ** case 1. In that case, construct a new virtual term that is - ** pTerm converted into an IN operator. - ** - ** EV: R-00211-15100 - */ - if( okToChngToIN ){ - Expr *pDup; /* A transient duplicate expression */ - ExprList *pList = 0; /* The RHS of the IN operator */ - Expr *pLeft = 0; /* The LHS of the IN operator */ - Expr *pNew; /* The complete IN operator */ - - for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ - if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; - assert( pOrTerm->eOperator & WO_EQ ); - assert( pOrTerm->leftCursor==iCursor ); - assert( pOrTerm->u.leftColumn==iColumn ); - pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); - pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); - pLeft = pOrTerm->pExpr->pLeft; - } - assert( pLeft!=0 ); - pDup = sqlite3ExprDup(db, pLeft, 0); - pNew = sqlite3PExpr(pParse, TK_IN, pDup, 0, 0); - if( pNew ){ - int idxNew; - transferJoinMarkings(pNew, pExpr); - assert( !ExprHasProperty(pNew, EP_xIsSelect) ); - pNew->x.pList = pList; - idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); - testcase( idxNew==0 ); - exprAnalyze(pSrc, pWC, idxNew); - pTerm = &pWC->a[idxTerm]; - pWC->a[idxNew].iParent = idxTerm; - pTerm->nChild = 1; - }else{ - sqlite3ExprListDelete(db, pList); - } - pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ - } - } -} -#endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ - -/* -** The input to this routine is an WhereTerm structure with only the -** "pExpr" field filled in. The job of this routine is to analyze the -** subexpression and populate all the other fields of the WhereTerm -** structure. -** -** If the expression is of the form " X" it gets commuted -** to the standard form of "X ". -** -** If the expression is of the form "X Y" where both X and Y are -** columns, then the original expression is unchanged and a new virtual -** term of the form "Y X" is added to the WHERE clause and -** analyzed separately. The original term is marked with TERM_COPIED -** and the new term is marked with TERM_DYNAMIC (because it's pExpr -** needs to be freed with the WhereClause) and TERM_VIRTUAL (because it -** is a commuted copy of a prior term.) The original term has nChild=1 -** and the copy has idxParent set to the index of the original term. -*/ -static void exprAnalyze( - SrcList *pSrc, /* the FROM clause */ - WhereClause *pWC, /* the WHERE clause */ - int idxTerm /* Index of the term to be analyzed */ -){ - WhereTerm *pTerm; /* The term to be analyzed */ - WhereMaskSet *pMaskSet; /* Set of table index masks */ - Expr *pExpr; /* The expression to be analyzed */ - Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */ - Bitmask prereqAll; /* Prerequesites of pExpr */ - Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */ - Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ - int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ - int noCase = 0; /* LIKE/GLOB distinguishes case */ - int op; /* Top-level operator. pExpr->op */ - Parse *pParse = pWC->pParse; /* Parsing context */ - sqlite3 *db = pParse->db; /* Database connection */ - - if( db->mallocFailed ){ - return; - } - pTerm = &pWC->a[idxTerm]; - pMaskSet = pWC->pMaskSet; - pExpr = pTerm->pExpr; - assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE ); - prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft); - op = pExpr->op; - if( op==TK_IN ){ - assert( pExpr->pRight==0 ); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - pTerm->prereqRight = exprSelectTableUsage(pMaskSet, pExpr->x.pSelect); - }else{ - pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->x.pList); - } - }else if( op==TK_ISNULL ){ - pTerm->prereqRight = 0; - }else{ - pTerm->prereqRight = exprTableUsage(pMaskSet, pExpr->pRight); - } - prereqAll = exprTableUsage(pMaskSet, pExpr); - if( ExprHasProperty(pExpr, EP_FromJoin) ){ - Bitmask x = getMask(pMaskSet, pExpr->iRightJoinTable); - prereqAll |= x; - extraRight = x-1; /* ON clause terms may not be used with an index - ** on left table of a LEFT JOIN. Ticket #3015 */ - } - pTerm->prereqAll = prereqAll; - pTerm->leftCursor = -1; - pTerm->iParent = -1; - pTerm->eOperator = 0; - if( allowedOp(op) ){ - Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); - Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); - u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; - if( pLeft->op==TK_COLUMN ){ - pTerm->leftCursor = pLeft->iTable; - pTerm->u.leftColumn = pLeft->iColumn; - pTerm->eOperator = operatorMask(op) & opMask; - } - if( pRight && pRight->op==TK_COLUMN ){ - WhereTerm *pNew; - Expr *pDup; - u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ - if( pTerm->leftCursor>=0 ){ - int idxNew; - pDup = sqlite3ExprDup(db, pExpr, 0); - if( db->mallocFailed ){ - sqlite3ExprDelete(db, pDup); - return; - } - idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC); - if( idxNew==0 ) return; - pNew = &pWC->a[idxNew]; - pNew->iParent = idxTerm; - pTerm = &pWC->a[idxTerm]; - pTerm->nChild = 1; - pTerm->wtFlags |= TERM_COPIED; - if( pExpr->op==TK_EQ - && !ExprHasProperty(pExpr, EP_FromJoin) - && OptimizationEnabled(db, SQLITE_Transitive) - ){ - pTerm->eOperator |= WO_EQUIV; - eExtraOp = WO_EQUIV; - } - }else{ - pDup = pExpr; - pNew = pTerm; - } - exprCommute(pParse, pDup); - pLeft = sqlite3ExprSkipCollate(pDup->pLeft); - pNew->leftCursor = pLeft->iTable; - pNew->u.leftColumn = pLeft->iColumn; - testcase( (prereqLeft | extraRight) != prereqLeft ); - pNew->prereqRight = prereqLeft | extraRight; - pNew->prereqAll = prereqAll; - pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; - } - } - -#ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION - /* If a term is the BETWEEN operator, create two new virtual terms - ** that define the range that the BETWEEN implements. For example: - ** - ** a BETWEEN b AND c - ** - ** is converted into: - ** - ** (a BETWEEN b AND c) AND (a>=b) AND (a<=c) - ** - ** The two new terms are added onto the end of the WhereClause object. - ** The new terms are "dynamic" and are children of the original BETWEEN - ** term. That means that if the BETWEEN term is coded, the children are - ** skipped. Or, if the children are satisfied by an index, the original - ** BETWEEN term is skipped. - */ - else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){ - ExprList *pList = pExpr->x.pList; - int i; - static const u8 ops[] = {TK_GE, TK_LE}; - assert( pList!=0 ); - assert( pList->nExpr==2 ); - for(i=0; i<2; i++){ - Expr *pNewExpr; - int idxNew; - pNewExpr = sqlite3PExpr(pParse, ops[i], - sqlite3ExprDup(db, pExpr->pLeft, 0), - sqlite3ExprDup(db, pList->a[i].pExpr, 0), 0); - idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); - testcase( idxNew==0 ); - exprAnalyze(pSrc, pWC, idxNew); - pTerm = &pWC->a[idxTerm]; - pWC->a[idxNew].iParent = idxTerm; - } - pTerm->nChild = 2; - } -#endif /* SQLITE_OMIT_BETWEEN_OPTIMIZATION */ - -#if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY) - /* Analyze a term that is composed of two or more subterms connected by - ** an OR operator. - */ - else if( pExpr->op==TK_OR ){ - assert( pWC->op==TK_AND ); - exprAnalyzeOrTerm(pSrc, pWC, idxTerm); - pTerm = &pWC->a[idxTerm]; - } -#endif /* SQLITE_OMIT_OR_OPTIMIZATION */ - -#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION - /* Add constraints to reduce the search space on a LIKE or GLOB - ** operator. - ** - ** A like pattern of the form "x LIKE 'abc%'" is changed into constraints - ** - ** x>='abc' AND x<'abd' AND x LIKE 'abc%' - ** - ** The last character of the prefix "abc" is incremented to form the - ** termination condition "abd". - */ - if( pWC->op==TK_AND - && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase) - ){ - Expr *pLeft; /* LHS of LIKE/GLOB operator */ - Expr *pStr2; /* Copy of pStr1 - RHS of LIKE/GLOB operator */ - Expr *pNewExpr1; - Expr *pNewExpr2; - int idxNew1; - int idxNew2; - Token sCollSeqName; /* Name of collating sequence */ - - pLeft = pExpr->x.pList->a[1].pExpr; - pStr2 = sqlite3ExprDup(db, pStr1, 0); - if( !db->mallocFailed ){ - u8 c, *pC; /* Last character before the first wildcard */ - pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; - c = *pC; - if( noCase ){ - /* The point is to increment the last character before the first - ** wildcard. But if we increment '@', that will push it into the - ** alphabetic range where case conversions will mess up the - ** inequality. To avoid this, make sure to also run the full - ** LIKE on all candidate expressions by clearing the isComplete flag - */ - if( c=='A'-1 ) isComplete = 0; /* EV: R-64339-08207 */ - - - c = sqlite3UpperToLower[c]; - } - *pC = c + 1; - } - sCollSeqName.z = noCase ? "NOCASE" : "BINARY"; - sCollSeqName.n = 6; - pNewExpr1 = sqlite3ExprDup(db, pLeft, 0); - pNewExpr1 = sqlite3PExpr(pParse, TK_GE, - sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName), - pStr1, 0); - idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC); - testcase( idxNew1==0 ); - exprAnalyze(pSrc, pWC, idxNew1); - pNewExpr2 = sqlite3ExprDup(db, pLeft, 0); - pNewExpr2 = sqlite3PExpr(pParse, TK_LT, - sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName), - pStr2, 0); - idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC); - testcase( idxNew2==0 ); - exprAnalyze(pSrc, pWC, idxNew2); - pTerm = &pWC->a[idxTerm]; - if( isComplete ){ - pWC->a[idxNew1].iParent = idxTerm; - pWC->a[idxNew2].iParent = idxTerm; - pTerm->nChild = 2; - } - } -#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE - /* Add a WO_MATCH auxiliary term to the constraint set if the - ** current expression is of the form: column MATCH expr. - ** This information is used by the xBestIndex methods of - ** virtual tables. The native query optimizer does not attempt - ** to do anything with MATCH functions. - */ - if( isMatchOfColumn(pExpr) ){ - int idxNew; - Expr *pRight, *pLeft; - WhereTerm *pNewTerm; - Bitmask prereqColumn, prereqExpr; - - pRight = pExpr->x.pList->a[0].pExpr; - pLeft = pExpr->x.pList->a[1].pExpr; - prereqExpr = exprTableUsage(pMaskSet, pRight); - prereqColumn = exprTableUsage(pMaskSet, pLeft); - if( (prereqExpr & prereqColumn)==0 ){ - Expr *pNewExpr; - pNewExpr = sqlite3PExpr(pParse, TK_MATCH, - 0, sqlite3ExprDup(db, pRight, 0), 0); - idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); - testcase( idxNew==0 ); - pNewTerm = &pWC->a[idxNew]; - pNewTerm->prereqRight = prereqExpr; - pNewTerm->leftCursor = pLeft->iTable; - pNewTerm->u.leftColumn = pLeft->iColumn; - pNewTerm->eOperator = WO_MATCH; - pNewTerm->iParent = idxTerm; - pTerm = &pWC->a[idxTerm]; - pTerm->nChild = 1; - pTerm->wtFlags |= TERM_COPIED; - pNewTerm->prereqAll = pTerm->prereqAll; - } - } -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifdef SQLITE_ENABLE_STAT3 - /* When sqlite_stat3 histogram data is available an operator of the - ** form "x IS NOT NULL" can sometimes be evaluated more efficiently - ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a - ** virtual term of that form. - ** - ** Note that the virtual term must be tagged with TERM_VNULL. This - ** TERM_VNULL tag will suppress the not-null check at the beginning - ** of the loop. Without the TERM_VNULL flag, the not-null check at - ** the start of the loop will prevent any results from being returned. - */ - if( pExpr->op==TK_NOTNULL - && pExpr->pLeft->op==TK_COLUMN - && pExpr->pLeft->iColumn>=0 - ){ - Expr *pNewExpr; - Expr *pLeft = pExpr->pLeft; - int idxNew; - WhereTerm *pNewTerm; - - pNewExpr = sqlite3PExpr(pParse, TK_GT, - sqlite3ExprDup(db, pLeft, 0), - sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0); - - idxNew = whereClauseInsert(pWC, pNewExpr, - TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); - if( idxNew ){ - pNewTerm = &pWC->a[idxNew]; - pNewTerm->prereqRight = 0; - pNewTerm->leftCursor = pLeft->iTable; - pNewTerm->u.leftColumn = pLeft->iColumn; - pNewTerm->eOperator = WO_GT; - pNewTerm->iParent = idxTerm; - pTerm = &pWC->a[idxTerm]; - pTerm->nChild = 1; - pTerm->wtFlags |= TERM_COPIED; - pNewTerm->prereqAll = pTerm->prereqAll; - } - } -#endif /* SQLITE_ENABLE_STAT */ - - /* Prevent ON clause terms of a LEFT JOIN from being used to drive - ** an index for tables to the left of the join. - */ - pTerm->prereqRight |= extraRight; -} - -/* -** This function searches the expression list passed as the second argument -** for an expression of type TK_COLUMN that refers to the same column and -** uses the same collation sequence as the iCol'th column of index pIdx. -** Argument iBase is the cursor number used for the table that pIdx refers -** to. -** -** If such an expression is found, its index in pList->a[] is returned. If -** no expression is found, -1 is returned. -*/ -static int findIndexCol( - Parse *pParse, /* Parse context */ - ExprList *pList, /* Expression list to search */ - int iBase, /* Cursor for table associated with pIdx */ - Index *pIdx, /* Index to match column of */ - int iCol /* Column of index to match */ -){ - int i; - const char *zColl = pIdx->azColl[iCol]; - - for(i=0; inExpr; i++){ - Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr); - if( p->op==TK_COLUMN - && p->iColumn==pIdx->aiColumn[iCol] - && p->iTable==iBase - ){ - CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr); - if( ALWAYS(pColl) && 0==sqlite3StrICmp(pColl->zName, zColl) ){ - return i; - } - } - } - - return -1; -} - -/* -** This routine determines if pIdx can be used to assist in processing a -** DISTINCT qualifier. In other words, it tests whether or not using this -** index for the outer loop guarantees that rows with equal values for -** all expressions in the pDistinct list are delivered grouped together. -** -** For example, the query -** -** SELECT DISTINCT a, b, c FROM tbl WHERE a = ? -** -** can benefit from any index on columns "b" and "c". -*/ -static int isDistinctIndex( - Parse *pParse, /* Parsing context */ - WhereClause *pWC, /* The WHERE clause */ - Index *pIdx, /* The index being considered */ - int base, /* Cursor number for the table pIdx is on */ - ExprList *pDistinct, /* The DISTINCT expressions */ - int nEqCol /* Number of index columns with == */ -){ - Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */ - int i; /* Iterator variable */ - - assert( pDistinct!=0 ); - if( pIdx->zName==0 || pDistinct->nExpr>=BMS ) return 0; - testcase( pDistinct->nExpr==BMS-1 ); - - /* Loop through all the expressions in the distinct list. If any of them - ** are not simple column references, return early. Otherwise, test if the - ** WHERE clause contains a "col=X" clause. If it does, the expression - ** can be ignored. If it does not, and the column does not belong to the - ** same table as index pIdx, return early. Finally, if there is no - ** matching "col=X" expression and the column is on the same table as pIdx, - ** set the corresponding bit in variable mask. - */ - for(i=0; inExpr; i++){ - WhereTerm *pTerm; - Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr); - if( p->op!=TK_COLUMN ) return 0; - pTerm = findTerm(pWC, p->iTable, p->iColumn, ~(Bitmask)0, WO_EQ, 0); - if( pTerm ){ - Expr *pX = pTerm->pExpr; - CollSeq *p1 = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - CollSeq *p2 = sqlite3ExprCollSeq(pParse, p); - if( p1==p2 ) continue; - } - if( p->iTable!=base ) return 0; - mask |= (((Bitmask)1) << i); - } - - for(i=nEqCol; mask && inColumn; i++){ - int iExpr = findIndexCol(pParse, pDistinct, base, pIdx, i); - if( iExpr<0 ) break; - mask &= ~(((Bitmask)1) << iExpr); - } - - return (mask==0); -} - - -/* -** Return true if the DISTINCT expression-list passed as the third argument -** is redundant. A DISTINCT list is redundant if the database contains a -** UNIQUE index that guarantees that the result of the query will be distinct -** anyway. -*/ -static int isDistinctRedundant( - Parse *pParse, - SrcList *pTabList, - WhereClause *pWC, - ExprList *pDistinct -){ - Table *pTab; - Index *pIdx; - int i; - int iBase; - - /* If there is more than one table or sub-select in the FROM clause of - ** this query, then it will not be possible to show that the DISTINCT - ** clause is redundant. */ - if( pTabList->nSrc!=1 ) return 0; - iBase = pTabList->a[0].iCursor; - pTab = pTabList->a[0].pTab; - - /* If any of the expressions is an IPK column on table iBase, then return - ** true. Note: The (p->iTable==iBase) part of this test may be false if the - ** current SELECT is a correlated sub-query. - */ - for(i=0; inExpr; i++){ - Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr); - if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; - } - - /* Loop through all indices on the table, checking each to see if it makes - ** the DISTINCT qualifier redundant. It does so if: - ** - ** 1. The index is itself UNIQUE, and - ** - ** 2. All of the columns in the index are either part of the pDistinct - ** list, or else the WHERE clause contains a term of the form "col=X", - ** where X is a constant value. The collation sequences of the - ** comparison and select-list expressions must match those of the index. - ** - ** 3. All of those index columns for which the WHERE clause does not - ** contain a "col=X" term are subject to a NOT NULL constraint. - */ - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->onError==OE_None ) continue; - for(i=0; inColumn; i++){ - int iCol = pIdx->aiColumn[i]; - if( 0==findTerm(pWC, iBase, iCol, ~(Bitmask)0, WO_EQ, pIdx) ){ - int iIdxCol = findIndexCol(pParse, pDistinct, iBase, pIdx, i); - if( iIdxCol<0 || pTab->aCol[pIdx->aiColumn[i]].notNull==0 ){ - break; - } - } - } - if( i==pIdx->nColumn ){ - /* This index implies that the DISTINCT qualifier is redundant. */ - return 1; - } - } - - return 0; -} - -/* -** Prepare a crude estimate of the logarithm of the input value. -** The results need not be exact. This is only used for estimating -** the total cost of performing operations with O(logN) or O(NlogN) -** complexity. Because N is just a guess, it is no great tragedy if -** logN is a little off. -*/ -static double estLog(double N){ - double logN = 1; - double x = 10; - while( N>x ){ - logN += 1; - x *= 10; - } - return logN; -} - -/* -** Two routines for printing the content of an sqlite3_index_info -** structure. Used for testing and debugging only. If neither -** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines -** are no-ops. -*/ -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_DEBUG) -static void TRACE_IDX_INPUTS(sqlite3_index_info *p){ - int i; - if( !sqlite3WhereTrace ) return; - for(i=0; inConstraint; i++){ - sqlite3DebugPrintf(" constraint[%d]: col=%d termid=%d op=%d usabled=%d\n", - i, - p->aConstraint[i].iColumn, - p->aConstraint[i].iTermOffset, - p->aConstraint[i].op, - p->aConstraint[i].usable); - } - for(i=0; inOrderBy; i++){ - sqlite3DebugPrintf(" orderby[%d]: col=%d desc=%d\n", - i, - p->aOrderBy[i].iColumn, - p->aOrderBy[i].desc); - } -} -static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){ - int i; - if( !sqlite3WhereTrace ) return; - for(i=0; inConstraint; i++){ - sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n", - i, - p->aConstraintUsage[i].argvIndex, - p->aConstraintUsage[i].omit); - } - sqlite3DebugPrintf(" idxNum=%d\n", p->idxNum); - sqlite3DebugPrintf(" idxStr=%s\n", p->idxStr); - sqlite3DebugPrintf(" orderByConsumed=%d\n", p->orderByConsumed); - sqlite3DebugPrintf(" estimatedCost=%g\n", p->estimatedCost); -} -#else -#define TRACE_IDX_INPUTS(A) -#define TRACE_IDX_OUTPUTS(A) -#endif - -/* -** Required because bestIndex() is called by bestOrClauseIndex() -*/ -static void bestIndex(WhereBestIdx*); - -/* -** This routine attempts to find an scanning strategy that can be used -** to optimize an 'OR' expression that is part of a WHERE clause. -** -** The table associated with FROM clause term pSrc may be either a -** regular B-Tree table or a virtual table. -*/ -static void bestOrClauseIndex(WhereBestIdx *p){ -#ifndef SQLITE_OMIT_OR_OPTIMIZATION - WhereClause *pWC = p->pWC; /* The WHERE clause */ - struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */ - const int iCur = pSrc->iCursor; /* The cursor of the table */ - const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ - WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ - WhereTerm *pTerm; /* A single term of the WHERE clause */ - - /* The OR-clause optimization is disallowed if the INDEXED BY or - ** NOT INDEXED clauses are used or if the WHERE_AND_ONLY bit is set. */ - if( pSrc->notIndexed || pSrc->pIndex!=0 ){ - return; - } - if( pWC->wctrlFlags & WHERE_AND_ONLY ){ - return; - } - - /* Search the WHERE clause terms for a usable WO_OR term. */ - for(pTerm=pWC->a; pTermeOperator & WO_OR)!=0 - && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 - && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 - ){ - WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; - WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; - WhereTerm *pOrTerm; - int flags = WHERE_MULTI_OR; - double rTotal = 0; - double nRow = 0; - Bitmask used = 0; - WhereBestIdx sBOI; - - sBOI = *p; - sBOI.pOrderBy = 0; - sBOI.pDistinct = 0; - sBOI.ppIdxInfo = 0; - for(pOrTerm=pOrWC->a; pOrTerma), (pTerm - pWC->a) - )); - if( (pOrTerm->eOperator& WO_AND)!=0 ){ - sBOI.pWC = &pOrTerm->u.pAndInfo->wc; - bestIndex(&sBOI); - }else if( pOrTerm->leftCursor==iCur ){ - WhereClause tempWC; - tempWC.pParse = pWC->pParse; - tempWC.pMaskSet = pWC->pMaskSet; - tempWC.pOuter = pWC; - tempWC.op = TK_AND; - tempWC.a = pOrTerm; - tempWC.wctrlFlags = 0; - tempWC.nTerm = 1; - sBOI.pWC = &tempWC; - bestIndex(&sBOI); - }else{ - continue; - } - rTotal += sBOI.cost.rCost; - nRow += sBOI.cost.plan.nRow; - used |= sBOI.cost.used; - if( rTotal>=p->cost.rCost ) break; - } - - /* If there is an ORDER BY clause, increase the scan cost to account - ** for the cost of the sort. */ - if( p->pOrderBy!=0 ){ - WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n", - rTotal, rTotal+nRow*estLog(nRow))); - rTotal += nRow*estLog(nRow); - } - - /* If the cost of scanning using this OR term for optimization is - ** less than the current cost stored in pCost, replace the contents - ** of pCost. */ - WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow)); - if( rTotalcost.rCost ){ - p->cost.rCost = rTotal; - p->cost.used = used; - p->cost.plan.nRow = nRow; - p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0; - p->cost.plan.wsFlags = flags; - p->cost.plan.u.pTerm = pTerm; - } - } - } -#endif /* SQLITE_OMIT_OR_OPTIMIZATION */ -} - -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX -/* -** Return TRUE if the WHERE clause term pTerm is of a form where it -** could be used with an index to access pSrc, assuming an appropriate -** index existed. -*/ -static int termCanDriveIndex( - WhereTerm *pTerm, /* WHERE clause term to check */ - struct SrcList_item *pSrc, /* Table we are trying to access */ - Bitmask notReady /* Tables in outer loops of the join */ -){ - char aff; - if( pTerm->leftCursor!=pSrc->iCursor ) return 0; - if( (pTerm->eOperator & WO_EQ)==0 ) return 0; - if( (pTerm->prereqRight & notReady)!=0 ) return 0; - aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; - if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; - return 1; -} -#endif - -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX -/* -** If the query plan for pSrc specified in pCost is a full table scan -** and indexing is allows (if there is no NOT INDEXED clause) and it -** possible to construct a transient index that would perform better -** than a full table scan even when the cost of constructing the index -** is taken into account, then alter the query plan to use the -** transient index. -*/ -static void bestAutomaticIndex(WhereBestIdx *p){ - Parse *pParse = p->pParse; /* The parsing context */ - WhereClause *pWC = p->pWC; /* The WHERE clause */ - struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */ - double nTableRow; /* Rows in the input table */ - double logN; /* log(nTableRow) */ - double costTempIdx; /* per-query cost of the transient index */ - WhereTerm *pTerm; /* A single term of the WHERE clause */ - WhereTerm *pWCEnd; /* End of pWC->a[] */ - Table *pTable; /* Table tht might be indexed */ - - if( pParse->nQueryLoop<=(double)1 ){ - /* There is no point in building an automatic index for a single scan */ - return; - } - if( (pParse->db->flags & SQLITE_AutoIndex)==0 ){ - /* Automatic indices are disabled at run-time */ - return; - } - if( (p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0 - && (p->cost.plan.wsFlags & WHERE_COVER_SCAN)==0 - ){ - /* We already have some kind of index in use for this query. */ - return; - } - if( pSrc->viaCoroutine ){ - /* Cannot index a co-routine */ - return; - } - if( pSrc->notIndexed ){ - /* The NOT INDEXED clause appears in the SQL. */ - return; - } - if( pSrc->isCorrelated ){ - /* The source is a correlated sub-query. No point in indexing it. */ - return; - } - - assert( pParse->nQueryLoop >= (double)1 ); - pTable = pSrc->pTab; - nTableRow = pTable->nRowEst; - logN = estLog(nTableRow); - costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1); - if( costTempIdx>=p->cost.rCost ){ - /* The cost of creating the transient table would be greater than - ** doing the full table scan */ - return; - } - - /* Search for any equality comparison term */ - pWCEnd = &pWC->a[pWC->nTerm]; - for(pTerm=pWC->a; pTermnotReady) ){ - WHERETRACE(("auto-index reduces cost from %.1f to %.1f\n", - p->cost.rCost, costTempIdx)); - p->cost.rCost = costTempIdx; - p->cost.plan.nRow = logN + 1; - p->cost.plan.wsFlags = WHERE_TEMP_INDEX; - p->cost.used = pTerm->prereqRight; - break; - } - } -} -#else -# define bestAutomaticIndex(A) /* no-op */ -#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ - - -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX -/* -** Generate code to construct the Index object for an automatic index -** and to set up the WhereLevel object pLevel so that the code generator -** makes use of the automatic index. -*/ -static void constructAutomaticIndex( - Parse *pParse, /* The parsing context */ - WhereClause *pWC, /* The WHERE clause */ - struct SrcList_item *pSrc, /* The FROM clause term to get the next index */ - Bitmask notReady, /* Mask of cursors that are not available */ - WhereLevel *pLevel /* Write new index here */ -){ - int nColumn; /* Number of columns in the constructed index */ - WhereTerm *pTerm; /* A single term of the WHERE clause */ - WhereTerm *pWCEnd; /* End of pWC->a[] */ - int nByte; /* Byte of memory needed for pIdx */ - Index *pIdx; /* Object describing the transient index */ - Vdbe *v; /* Prepared statement under construction */ - int addrInit; /* Address of the initialization bypass jump */ - Table *pTable; /* The table being indexed */ - KeyInfo *pKeyinfo; /* Key information for the index */ - int addrTop; /* Top of the index fill loop */ - int regRecord; /* Register holding an index record */ - int n; /* Column counter */ - int i; /* Loop counter */ - int mxBitCol; /* Maximum column in pSrc->colUsed */ - CollSeq *pColl; /* Collating sequence to on a column */ - Bitmask idxCols; /* Bitmap of columns used for indexing */ - Bitmask extraCols; /* Bitmap of additional columns */ - - /* Generate code to skip over the creation and initialization of the - ** transient index on 2nd and subsequent iterations of the loop. */ - v = pParse->pVdbe; - assert( v!=0 ); - addrInit = sqlite3CodeOnce(pParse); - - /* Count the number of columns that will be added to the index - ** and used to match WHERE clause constraints */ - nColumn = 0; - pTable = pSrc->pTab; - pWCEnd = &pWC->a[pWC->nTerm]; - idxCols = 0; - for(pTerm=pWC->a; pTermu.leftColumn; - Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<0 ); - pLevel->plan.nEq = nColumn; - - /* Count the number of additional columns needed to create a - ** covering index. A "covering index" is an index that contains all - ** columns that are needed by the query. With a covering index, the - ** original table never needs to be accessed. Automatic indices must - ** be a covering index because the index will not be updated if the - ** original table changes and the index and table cannot both be used - ** if they go out of sync. - */ - extraCols = pSrc->colUsed & (~idxCols | (((Bitmask)1)<<(BMS-1))); - mxBitCol = (pTable->nCol >= BMS-1) ? BMS-1 : pTable->nCol; - testcase( pTable->nCol==BMS-1 ); - testcase( pTable->nCol==BMS-2 ); - for(i=0; icolUsed & (((Bitmask)1)<<(BMS-1)) ){ - nColumn += pTable->nCol - BMS + 1; - } - pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WO_EQ; - - /* Construct the Index object to describe this index */ - nByte = sizeof(Index); - nByte += nColumn*sizeof(int); /* Index.aiColumn */ - nByte += nColumn*sizeof(char*); /* Index.azColl */ - nByte += nColumn; /* Index.aSortOrder */ - pIdx = sqlite3DbMallocZero(pParse->db, nByte); - if( pIdx==0 ) return; - pLevel->plan.u.pIdx = pIdx; - pIdx->azColl = (char**)&pIdx[1]; - pIdx->aiColumn = (int*)&pIdx->azColl[nColumn]; - pIdx->aSortOrder = (u8*)&pIdx->aiColumn[nColumn]; - pIdx->zName = "auto-index"; - pIdx->nColumn = nColumn; - pIdx->pTable = pTable; - n = 0; - idxCols = 0; - for(pTerm=pWC->a; pTermu.leftColumn; - Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<pExpr; - idxCols |= cMask; - pIdx->aiColumn[n] = pTerm->u.leftColumn; - pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - pIdx->azColl[n] = ALWAYS(pColl) ? pColl->zName : "BINARY"; - n++; - } - } - } - assert( (u32)n==pLevel->plan.nEq ); - - /* Add additional columns needed to make the automatic index into - ** a covering index */ - for(i=0; iaiColumn[n] = i; - pIdx->azColl[n] = "BINARY"; - n++; - } - } - if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){ - for(i=BMS-1; inCol; i++){ - pIdx->aiColumn[n] = i; - pIdx->azColl[n] = "BINARY"; - n++; - } - } - assert( n==nColumn ); - - /* Create the automatic index */ - pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx); - assert( pLevel->iIdxCur>=0 ); - sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nColumn+1, 0, - (char*)pKeyinfo, P4_KEYINFO_HANDOFF); - VdbeComment((v, "for %s", pTable->zName)); - - /* Fill the automatic index with content */ - addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); - regRecord = sqlite3GetTempReg(pParse); - sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); - sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); - sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); - sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); - sqlite3VdbeJumpHere(v, addrTop); - sqlite3ReleaseTempReg(pParse, regRecord); - - /* Jump here when skipping the initialization */ - sqlite3VdbeJumpHere(v, addrInit); -} -#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ - -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* -** Allocate and populate an sqlite3_index_info structure. It is the -** responsibility of the caller to eventually release the structure -** by passing the pointer returned by this function to sqlite3_free(). -*/ -static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ - Parse *pParse = p->pParse; - WhereClause *pWC = p->pWC; - struct SrcList_item *pSrc = p->pSrc; - ExprList *pOrderBy = p->pOrderBy; - int i, j; - int nTerm; - struct sqlite3_index_constraint *pIdxCons; - struct sqlite3_index_orderby *pIdxOrderBy; - struct sqlite3_index_constraint_usage *pUsage; - WhereTerm *pTerm; - int nOrderBy; - sqlite3_index_info *pIdxInfo; - - WHERETRACE(("Recomputing index info for %s...\n", pSrc->pTab->zName)); - - /* Count the number of possible WHERE clause constraints referring - ** to this virtual table */ - for(i=nTerm=0, pTerm=pWC->a; inTerm; i++, pTerm++){ - if( pTerm->leftCursor != pSrc->iCursor ) continue; - assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); - testcase( pTerm->eOperator & WO_IN ); - testcase( pTerm->eOperator & WO_ISNULL ); - if( pTerm->eOperator & (WO_ISNULL) ) continue; - if( pTerm->wtFlags & TERM_VNULL ) continue; - nTerm++; - } - - /* If the ORDER BY clause contains only columns in the current - ** virtual table then allocate space for the aOrderBy part of - ** the sqlite3_index_info structure. - */ - nOrderBy = 0; - if( pOrderBy ){ - int n = pOrderBy->nExpr; - for(i=0; ia[i].pExpr; - if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break; - } - if( i==n){ - nOrderBy = n; - } - } - - /* Allocate the sqlite3_index_info structure - */ - pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) - + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm - + sizeof(*pIdxOrderBy)*nOrderBy ); - if( pIdxInfo==0 ){ - sqlite3ErrorMsg(pParse, "out of memory"); - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - return 0; - } - - /* Initialize the structure. The sqlite3_index_info structure contains - ** many fields that are declared "const" to prevent xBestIndex from - ** changing them. We have to do some funky casting in order to - ** initialize those fields. - */ - pIdxCons = (struct sqlite3_index_constraint*)&pIdxInfo[1]; - pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm]; - pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy]; - *(int*)&pIdxInfo->nConstraint = nTerm; - *(int*)&pIdxInfo->nOrderBy = nOrderBy; - *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint = pIdxCons; - *(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy; - *(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage = - pUsage; - - for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ - u8 op; - if( pTerm->leftCursor != pSrc->iCursor ) continue; - assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); - testcase( pTerm->eOperator & WO_IN ); - testcase( pTerm->eOperator & WO_ISNULL ); - if( pTerm->eOperator & (WO_ISNULL) ) continue; - if( pTerm->wtFlags & TERM_VNULL ) continue; - pIdxCons[j].iColumn = pTerm->u.leftColumn; - pIdxCons[j].iTermOffset = i; - op = (u8)pTerm->eOperator & WO_ALL; - if( op==WO_IN ) op = WO_EQ; - pIdxCons[j].op = op; - /* The direct assignment in the previous line is possible only because - ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The - ** following asserts verify this fact. */ - assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); - assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); - assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); - assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); - assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); - assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH ); - assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) ); - j++; - } - for(i=0; ia[i].pExpr; - pIdxOrderBy[i].iColumn = pExpr->iColumn; - pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder; - } - - return pIdxInfo; -} - -/* -** The table object reference passed as the second argument to this function -** must represent a virtual table. This function invokes the xBestIndex() -** method of the virtual table with the sqlite3_index_info pointer passed -** as the argument. -** -** If an error occurs, pParse is populated with an error message and a -** non-zero value is returned. Otherwise, 0 is returned and the output -** part of the sqlite3_index_info structure is left populated. -** -** Whether or not an error is returned, it is the responsibility of the -** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates -** that this is required. -*/ -static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ - sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; - int i; - int rc; - - WHERETRACE(("xBestIndex for %s\n", pTab->zName)); - TRACE_IDX_INPUTS(p); - rc = pVtab->pModule->xBestIndex(pVtab, p); - TRACE_IDX_OUTPUTS(p); - - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM ){ - pParse->db->mallocFailed = 1; - }else if( !pVtab->zErrMsg ){ - sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc)); - }else{ - sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg); - } - } - sqlite3_free(pVtab->zErrMsg); - pVtab->zErrMsg = 0; - - for(i=0; inConstraint; i++){ - if( !p->aConstraint[i].usable && p->aConstraintUsage[i].argvIndex>0 ){ - sqlite3ErrorMsg(pParse, - "table %s: xBestIndex returned an invalid plan", pTab->zName); - } - } - - return pParse->nErr; -} - - -/* -** Compute the best index for a virtual table. -** -** The best index is computed by the xBestIndex method of the virtual -** table module. This routine is really just a wrapper that sets up -** the sqlite3_index_info structure that is used to communicate with -** xBestIndex. -** -** In a join, this routine might be called multiple times for the -** same virtual table. The sqlite3_index_info structure is created -** and initialized on the first invocation and reused on all subsequent -** invocations. The sqlite3_index_info structure is also used when -** code is generated to access the virtual table. The whereInfoDelete() -** routine takes care of freeing the sqlite3_index_info structure after -** everybody has finished with it. -*/ -static void bestVirtualIndex(WhereBestIdx *p){ - Parse *pParse = p->pParse; /* The parsing context */ - WhereClause *pWC = p->pWC; /* The WHERE clause */ - struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */ - Table *pTab = pSrc->pTab; - sqlite3_index_info *pIdxInfo; - struct sqlite3_index_constraint *pIdxCons; - struct sqlite3_index_constraint_usage *pUsage; - WhereTerm *pTerm; - int i, j, k; - int nOrderBy; - int sortOrder; /* Sort order for IN clauses */ - int bAllowIN; /* Allow IN optimizations */ - double rCost; - - /* Make sure wsFlags is initialized to some sane value. Otherwise, if the - ** malloc in allocateIndexInfo() fails and this function returns leaving - ** wsFlags in an uninitialized state, the caller may behave unpredictably. - */ - memset(&p->cost, 0, sizeof(p->cost)); - p->cost.plan.wsFlags = WHERE_VIRTUALTABLE; - - /* If the sqlite3_index_info structure has not been previously - ** allocated and initialized, then allocate and initialize it now. - */ - pIdxInfo = *p->ppIdxInfo; - if( pIdxInfo==0 ){ - *p->ppIdxInfo = pIdxInfo = allocateIndexInfo(p); - } - if( pIdxInfo==0 ){ - return; - } - - /* At this point, the sqlite3_index_info structure that pIdxInfo points - ** to will have been initialized, either during the current invocation or - ** during some prior invocation. Now we just have to customize the - ** details of pIdxInfo for the current invocation and pass it to - ** xBestIndex. - */ - - /* The module name must be defined. Also, by this point there must - ** be a pointer to an sqlite3_vtab structure. Otherwise - ** sqlite3ViewGetColumnNames() would have picked up the error. - */ - assert( pTab->azModuleArg && pTab->azModuleArg[0] ); - assert( sqlite3GetVTable(pParse->db, pTab) ); - - /* Try once or twice. On the first attempt, allow IN optimizations. - ** If an IN optimization is accepted by the virtual table xBestIndex - ** method, but the pInfo->aConstrainUsage.omit flag is not set, then - ** the query will not work because it might allow duplicate rows in - ** output. In that case, run the xBestIndex method a second time - ** without the IN constraints. Usually this loop only runs once. - ** The loop will exit using a "break" statement. - */ - for(bAllowIN=1; 1; bAllowIN--){ - assert( bAllowIN==0 || bAllowIN==1 ); - - /* Set the aConstraint[].usable fields and initialize all - ** output variables to zero. - ** - ** aConstraint[].usable is true for constraints where the right-hand - ** side contains only references to tables to the left of the current - ** table. In other words, if the constraint is of the form: - ** - ** column = expr - ** - ** and we are evaluating a join, then the constraint on column is - ** only valid if all tables referenced in expr occur to the left - ** of the table containing column. - ** - ** The aConstraints[] array contains entries for all constraints - ** on the current table. That way we only have to compute it once - ** even though we might try to pick the best index multiple times. - ** For each attempt at picking an index, the order of tables in the - ** join might be different so we have to recompute the usable flag - ** each time. - */ - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - pUsage = pIdxInfo->aConstraintUsage; - for(i=0; inConstraint; i++, pIdxCons++){ - j = pIdxCons->iTermOffset; - pTerm = &pWC->a[j]; - if( (pTerm->prereqRight&p->notReady)==0 - && (bAllowIN || (pTerm->eOperator & WO_IN)==0) - ){ - pIdxCons->usable = 1; - }else{ - pIdxCons->usable = 0; - } - } - memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); - if( pIdxInfo->needToFreeIdxStr ){ - sqlite3_free(pIdxInfo->idxStr); - } - pIdxInfo->idxStr = 0; - pIdxInfo->idxNum = 0; - pIdxInfo->needToFreeIdxStr = 0; - pIdxInfo->orderByConsumed = 0; - /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */ - pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2); - nOrderBy = pIdxInfo->nOrderBy; - if( !p->pOrderBy ){ - pIdxInfo->nOrderBy = 0; - } - - if( vtabBestIndex(pParse, pTab, pIdxInfo) ){ - return; - } - - sortOrder = SQLITE_SO_ASC; - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pIdxCons++){ - if( pUsage[i].argvIndex>0 ){ - j = pIdxCons->iTermOffset; - pTerm = &pWC->a[j]; - p->cost.used |= pTerm->prereqRight; - if( (pTerm->eOperator & WO_IN)!=0 ){ - if( pUsage[i].omit==0 ){ - /* Do not attempt to use an IN constraint if the virtual table - ** says that the equivalent EQ constraint cannot be safely omitted. - ** If we do attempt to use such a constraint, some rows might be - ** repeated in the output. */ - break; - } - for(k=0; knOrderBy; k++){ - if( pIdxInfo->aOrderBy[k].iColumn==pIdxCons->iColumn ){ - sortOrder = pIdxInfo->aOrderBy[k].desc; - break; - } - } - } - } - } - if( i>=pIdxInfo->nConstraint ) break; - } - - /* If there is an ORDER BY clause, and the selected virtual table index - ** does not satisfy it, increase the cost of the scan accordingly. This - ** matches the processing for non-virtual tables in bestBtreeIndex(). - */ - rCost = pIdxInfo->estimatedCost; - if( p->pOrderBy && pIdxInfo->orderByConsumed==0 ){ - rCost += estLog(rCost)*rCost; - } - - /* The cost is not allowed to be larger than SQLITE_BIG_DBL (the - ** inital value of lowestCost in this loop. If it is, then the - ** (costcost.rCost = (SQLITE_BIG_DBL/((double)2)); - }else{ - p->cost.rCost = rCost; - } - p->cost.plan.u.pVtabIdx = pIdxInfo; - if( pIdxInfo->orderByConsumed ){ - assert( sortOrder==0 || sortOrder==1 ); - p->cost.plan.wsFlags |= WHERE_ORDERED + sortOrder*WHERE_REVERSE; - p->cost.plan.nOBSat = nOrderBy; - }else{ - p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0; - } - p->cost.plan.nEq = 0; - pIdxInfo->nOrderBy = nOrderBy; - - /* Try to find a more efficient access pattern by using multiple indexes - ** to optimize an OR expression within the WHERE clause. - */ - bestOrClauseIndex(p); -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifdef SQLITE_ENABLE_STAT3 -/* -** Estimate the location of a particular key among all keys in an -** index. Store the results in aStat as follows: -** -** aStat[0] Est. number of rows less than pVal -** aStat[1] Est. number of rows equal to pVal -** -** Return SQLITE_OK on success. -*/ -static int whereKeyStats( - Parse *pParse, /* Database connection */ - Index *pIdx, /* Index to consider domain of */ - sqlite3_value *pVal, /* Value to consider */ - int roundUp, /* Round up if true. Round down if false */ - tRowcnt *aStat /* OUT: stats written here */ -){ - tRowcnt n; - IndexSample *aSample; - int i, eType; - int isEq = 0; - i64 v; - double r, rS; - - assert( roundUp==0 || roundUp==1 ); - assert( pIdx->nSample>0 ); - if( pVal==0 ) return SQLITE_ERROR; - n = pIdx->aiRowEst[0]; - aSample = pIdx->aSample; - eType = sqlite3_value_type(pVal); - - if( eType==SQLITE_INTEGER ){ - v = sqlite3_value_int64(pVal); - r = (i64)v; - for(i=0; inSample; i++){ - if( aSample[i].eType==SQLITE_NULL ) continue; - if( aSample[i].eType>=SQLITE_TEXT ) break; - if( aSample[i].eType==SQLITE_INTEGER ){ - if( aSample[i].u.i>=v ){ - isEq = aSample[i].u.i==v; - break; - } - }else{ - assert( aSample[i].eType==SQLITE_FLOAT ); - if( aSample[i].u.r>=r ){ - isEq = aSample[i].u.r==r; - break; - } - } - } - }else if( eType==SQLITE_FLOAT ){ - r = sqlite3_value_double(pVal); - for(i=0; inSample; i++){ - if( aSample[i].eType==SQLITE_NULL ) continue; - if( aSample[i].eType>=SQLITE_TEXT ) break; - if( aSample[i].eType==SQLITE_FLOAT ){ - rS = aSample[i].u.r; - }else{ - rS = aSample[i].u.i; - } - if( rS>=r ){ - isEq = rS==r; - break; - } - } - }else if( eType==SQLITE_NULL ){ - i = 0; - if( aSample[0].eType==SQLITE_NULL ) isEq = 1; - }else{ - assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); - for(i=0; inSample; i++){ - if( aSample[i].eType==SQLITE_TEXT || aSample[i].eType==SQLITE_BLOB ){ - break; - } - } - if( inSample ){ - sqlite3 *db = pParse->db; - CollSeq *pColl; - const u8 *z; - if( eType==SQLITE_BLOB ){ - z = (const u8 *)sqlite3_value_blob(pVal); - pColl = db->pDfltColl; - assert( pColl->enc==SQLITE_UTF8 ); - }else{ - pColl = sqlite3GetCollSeq(pParse, SQLITE_UTF8, 0, *pIdx->azColl); - if( pColl==0 ){ - return SQLITE_ERROR; - } - z = (const u8 *)sqlite3ValueText(pVal, pColl->enc); - if( !z ){ - return SQLITE_NOMEM; - } - assert( z && pColl && pColl->xCmp ); - } - n = sqlite3ValueBytes(pVal, pColl->enc); - - for(; inSample; i++){ - int c; - int eSampletype = aSample[i].eType; - if( eSampletypeenc!=SQLITE_UTF8 ){ - int nSample; - char *zSample = sqlite3Utf8to16( - db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample - ); - if( !zSample ){ - assert( db->mallocFailed ); - return SQLITE_NOMEM; - } - c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); - sqlite3DbFree(db, zSample); - }else -#endif - { - c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); - } - if( c>=0 ){ - if( c==0 ) isEq = 1; - break; - } - } - } - } - - /* At this point, aSample[i] is the first sample that is greater than - ** or equal to pVal. Or if i==pIdx->nSample, then all samples are less - ** than pVal. If aSample[i]==pVal, then isEq==1. - */ - if( isEq ){ - assert( inSample ); - aStat[0] = aSample[i].nLt; - aStat[1] = aSample[i].nEq; - }else{ - tRowcnt iLower, iUpper, iGap; - if( i==0 ){ - iLower = 0; - iUpper = aSample[0].nLt; - }else{ - iUpper = i>=pIdx->nSample ? n : aSample[i].nLt; - iLower = aSample[i-1].nEq + aSample[i-1].nLt; - } - aStat[1] = pIdx->avgEq; - if( iLower>=iUpper ){ - iGap = 0; - }else{ - iGap = iUpper - iLower; - } - if( roundUp ){ - iGap = (iGap*2)/3; - }else{ - iGap = iGap/3; - } - aStat[0] = iLower + iGap; - } - return SQLITE_OK; -} -#endif /* SQLITE_ENABLE_STAT3 */ - -/* -** If expression pExpr represents a literal value, set *pp to point to -** an sqlite3_value structure containing the same value, with affinity -** aff applied to it, before returning. It is the responsibility of the -** caller to eventually release this structure by passing it to -** sqlite3ValueFree(). -** -** If the current parse is a recompile (sqlite3Reprepare()) and pExpr -** is an SQL variable that currently has a non-NULL value bound to it, -** create an sqlite3_value structure containing this value, again with -** affinity aff applied to it, instead. -** -** If neither of the above apply, set *pp to NULL. -** -** If an error occurs, return an error code. Otherwise, SQLITE_OK. -*/ -#ifdef SQLITE_ENABLE_STAT3 -static int valueFromExpr( - Parse *pParse, - Expr *pExpr, - u8 aff, - sqlite3_value **pp -){ - if( pExpr->op==TK_VARIABLE - || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) - ){ - int iVar = pExpr->iColumn; - sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); - *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); - return SQLITE_OK; - } - return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); -} -#endif - -/* -** This function is used to estimate the number of rows that will be visited -** by scanning an index for a range of values. The range may have an upper -** bound, a lower bound, or both. The WHERE clause terms that set the upper -** and lower bounds are represented by pLower and pUpper respectively. For -** example, assuming that index p is on t1(a): -** -** ... FROM t1 WHERE a > ? AND a < ? ... -** |_____| |_____| -** | | -** pLower pUpper -** -** If either of the upper or lower bound is not present, then NULL is passed in -** place of the corresponding WhereTerm. -** -** The nEq parameter is passed the index of the index column subject to the -** range constraint. Or, equivalently, the number of equality constraints -** optimized by the proposed index scan. For example, assuming index p is -** on t1(a, b), and the SQL query is: -** -** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ... -** -** then nEq should be passed the value 1 (as the range restricted column, -** b, is the second left-most column of the index). Or, if the query is: -** -** ... FROM t1 WHERE a > ? AND a < ? ... -** -** then nEq should be passed 0. -** -** The returned value is an integer divisor to reduce the estimated -** search space. A return value of 1 means that range constraints are -** no help at all. A return value of 2 means range constraints are -** expected to reduce the search space by half. And so forth... -** -** In the absence of sqlite_stat3 ANALYZE data, each range inequality -** reduces the search space by a factor of 4. Hence a single constraint (x>?) -** results in a return of 4 and a range constraint (x>? AND xaCol[] of the range-compared column */ - WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ - WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ - double *pRangeDiv /* OUT: Reduce search space by this divisor */ -){ - int rc = SQLITE_OK; - -#ifdef SQLITE_ENABLE_STAT3 - - if( nEq==0 && p->nSample ){ - sqlite3_value *pRangeVal; - tRowcnt iLower = 0; - tRowcnt iUpper = p->aiRowEst[0]; - tRowcnt a[2]; - u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; - - if( pLower ){ - Expr *pExpr = pLower->pExpr->pRight; - rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); - assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); - if( rc==SQLITE_OK - && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK - ){ - iLower = a[0]; - if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; - } - sqlite3ValueFree(pRangeVal); - } - if( rc==SQLITE_OK && pUpper ){ - Expr *pExpr = pUpper->pExpr->pRight; - rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); - assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); - if( rc==SQLITE_OK - && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK - ){ - iUpper = a[0]; - if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; - } - sqlite3ValueFree(pRangeVal); - } - if( rc==SQLITE_OK ){ - if( iUpper<=iLower ){ - *pRangeDiv = (double)p->aiRowEst[0]; - }else{ - *pRangeDiv = (double)p->aiRowEst[0]/(double)(iUpper - iLower); - } - WHERETRACE(("range scan regions: %u..%u div=%g\n", - (u32)iLower, (u32)iUpper, *pRangeDiv)); - return SQLITE_OK; - } - } -#else - UNUSED_PARAMETER(pParse); - UNUSED_PARAMETER(p); - UNUSED_PARAMETER(nEq); -#endif - assert( pLower || pUpper ); - *pRangeDiv = (double)1; - if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *pRangeDiv *= (double)4; - if( pUpper ) *pRangeDiv *= (double)4; - return rc; -} - -#ifdef SQLITE_ENABLE_STAT3 -/* -** Estimate the number of rows that will be returned based on -** an equality constraint x=VALUE and where that VALUE occurs in -** the histogram data. This only works when x is the left-most -** column of an index and sqlite_stat3 histogram data is available -** for that index. When pExpr==NULL that means the constraint is -** "x IS NULL" instead of "x=VALUE". -** -** Write the estimated row count into *pnRow and return SQLITE_OK. -** If unable to make an estimate, leave *pnRow unchanged and return -** non-zero. -** -** This routine can fail if it is unable to load a collating sequence -** required for string comparison, or if unable to allocate memory -** for a UTF conversion required for comparison. The error is stored -** in the pParse structure. -*/ -static int whereEqualScanEst( - Parse *pParse, /* Parsing & code generating context */ - Index *p, /* The index whose left-most column is pTerm */ - Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ - double *pnRow /* Write the revised row estimate here */ -){ - sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */ - u8 aff; /* Column affinity */ - int rc; /* Subfunction return code */ - tRowcnt a[2]; /* Statistics */ - - assert( p->aSample!=0 ); - assert( p->nSample>0 ); - aff = p->pTable->aCol[p->aiColumn[0]].affinity; - if( pExpr ){ - rc = valueFromExpr(pParse, pExpr, aff, &pRhs); - if( rc ) goto whereEqualScanEst_cancel; - }else{ - pRhs = sqlite3ValueNew(pParse->db); - } - if( pRhs==0 ) return SQLITE_NOTFOUND; - rc = whereKeyStats(pParse, p, pRhs, 0, a); - if( rc==SQLITE_OK ){ - WHERETRACE(("equality scan regions: %d\n", (int)a[1])); - *pnRow = a[1]; - } -whereEqualScanEst_cancel: - sqlite3ValueFree(pRhs); - return rc; -} -#endif /* defined(SQLITE_ENABLE_STAT3) */ - -#ifdef SQLITE_ENABLE_STAT3 -/* -** Estimate the number of rows that will be returned based on -** an IN constraint where the right-hand side of the IN operator -** is a list of values. Example: -** -** WHERE x IN (1,2,3,4) -** -** Write the estimated row count into *pnRow and return SQLITE_OK. -** If unable to make an estimate, leave *pnRow unchanged and return -** non-zero. -** -** This routine can fail if it is unable to load a collating sequence -** required for string comparison, or if unable to allocate memory -** for a UTF conversion required for comparison. The error is stored -** in the pParse structure. -*/ -static int whereInScanEst( - Parse *pParse, /* Parsing & code generating context */ - Index *p, /* The index whose left-most column is pTerm */ - ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ - double *pnRow /* Write the revised row estimate here */ -){ - int rc = SQLITE_OK; /* Subfunction return code */ - double nEst; /* Number of rows for a single term */ - double nRowEst = (double)0; /* New estimate of the number of rows */ - int i; /* Loop counter */ - - assert( p->aSample!=0 ); - for(i=0; rc==SQLITE_OK && inExpr; i++){ - nEst = p->aiRowEst[0]; - rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst); - nRowEst += nEst; - } - if( rc==SQLITE_OK ){ - if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; - *pnRow = nRowEst; - WHERETRACE(("IN row estimate: est=%g\n", nRowEst)); - } - return rc; -} -#endif /* defined(SQLITE_ENABLE_STAT3) */ - -/* -** Check to see if column iCol of the table with cursor iTab will appear -** in sorted order according to the current query plan. -** -** Return values: -** -** 0 iCol is not ordered -** 1 iCol has only a single value -** 2 iCol is in ASC order -** 3 iCol is in DESC order -*/ -static int isOrderedColumn( - WhereBestIdx *p, - int iTab, - int iCol -){ - int i, j; - WhereLevel *pLevel = &p->aLevel[p->i-1]; - Index *pIdx; - u8 sortOrder; - for(i=p->i-1; i>=0; i--, pLevel--){ - if( pLevel->iTabCur!=iTab ) continue; - if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ - return 1; - } - assert( (pLevel->plan.wsFlags & WHERE_ORDERED)!=0 ); - if( (pIdx = pLevel->plan.u.pIdx)!=0 ){ - if( iCol<0 ){ - sortOrder = 0; - testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ); - }else{ - int n = pIdx->nColumn; - for(j=0; jaiColumn[j] ) break; - } - if( j>=n ) return 0; - sortOrder = pIdx->aSortOrder[j]; - testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ); - } - }else{ - if( iCol!=(-1) ) return 0; - sortOrder = 0; - testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ); - } - if( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ){ - assert( sortOrder==0 || sortOrder==1 ); - testcase( sortOrder==1 ); - sortOrder = 1 - sortOrder; - } - return sortOrder+2; - } - return 0; -} - -/* -** This routine decides if pIdx can be used to satisfy the ORDER BY -** clause, either in whole or in part. The return value is the -** cumulative number of terms in the ORDER BY clause that are satisfied -** by the index pIdx and other indices in outer loops. -** -** The table being queried has a cursor number of "base". pIdx is the -** index that is postulated for use to access the table. -** -** The *pbRev value is set to 0 order 1 depending on whether or not -** pIdx should be run in the forward order or in reverse order. -*/ -static int isSortingIndex( - WhereBestIdx *p, /* Best index search context */ - Index *pIdx, /* The index we are testing */ - int base, /* Cursor number for the table to be sorted */ - int *pbRev, /* Set to 1 for reverse-order scan of pIdx */ - int *pbObUnique /* ORDER BY column values will different in every row */ -){ - int i; /* Number of pIdx terms used */ - int j; /* Number of ORDER BY terms satisfied */ - int sortOrder = 2; /* 0: forward. 1: backward. 2: unknown */ - int nTerm; /* Number of ORDER BY terms */ - struct ExprList_item *pOBItem;/* A term of the ORDER BY clause */ - Table *pTab = pIdx->pTable; /* Table that owns index pIdx */ - ExprList *pOrderBy; /* The ORDER BY clause */ - Parse *pParse = p->pParse; /* Parser context */ - sqlite3 *db = pParse->db; /* Database connection */ - int nPriorSat; /* ORDER BY terms satisfied by outer loops */ - int seenRowid = 0; /* True if an ORDER BY rowid term is seen */ - int uniqueNotNull; /* pIdx is UNIQUE with all terms are NOT NULL */ - int outerObUnique; /* Outer loops generate different values in - ** every row for the ORDER BY columns */ - - if( p->i==0 ){ - nPriorSat = 0; - outerObUnique = 1; - }else{ - u32 wsFlags = p->aLevel[p->i-1].plan.wsFlags; - nPriorSat = p->aLevel[p->i-1].plan.nOBSat; - if( (wsFlags & WHERE_ORDERED)==0 ){ - /* This loop cannot be ordered unless the next outer loop is - ** also ordered */ - return nPriorSat; - } - if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ){ - /* Only look at the outer-most loop if the OrderByIdxJoin - ** optimization is disabled */ - return nPriorSat; - } - testcase( wsFlags & WHERE_OB_UNIQUE ); - testcase( wsFlags & WHERE_ALL_UNIQUE ); - outerObUnique = (wsFlags & (WHERE_OB_UNIQUE|WHERE_ALL_UNIQUE))!=0; - } - pOrderBy = p->pOrderBy; - assert( pOrderBy!=0 ); - if( pIdx->bUnordered ){ - /* Hash indices (indicated by the "unordered" tag on sqlite_stat1) cannot - ** be used for sorting */ - return nPriorSat; - } - nTerm = pOrderBy->nExpr; - uniqueNotNull = pIdx->onError!=OE_None; - assert( nTerm>0 ); - - /* Argument pIdx must either point to a 'real' named index structure, - ** or an index structure allocated on the stack by bestBtreeIndex() to - ** represent the rowid index that is part of every table. */ - assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) ); - - /* Match terms of the ORDER BY clause against columns of - ** the index. - ** - ** Note that indices have pIdx->nColumn regular columns plus - ** one additional column containing the rowid. The rowid column - ** of the index is also allowed to match against the ORDER BY - ** clause. - */ - j = nPriorSat; - for(i=0,pOBItem=&pOrderBy->a[j]; jnColumn; i++){ - Expr *pOBExpr; /* The expression of the ORDER BY pOBItem */ - CollSeq *pColl; /* The collating sequence of pOBExpr */ - int termSortOrder; /* Sort order for this term */ - int iColumn; /* The i-th column of the index. -1 for rowid */ - int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */ - int isEq; /* Subject to an == or IS NULL constraint */ - int isMatch; /* ORDER BY term matches the index term */ - const char *zColl; /* Name of collating sequence for i-th index term */ - WhereTerm *pConstraint; /* A constraint in the WHERE clause */ - - /* If the next term of the ORDER BY clause refers to anything other than - ** a column in the "base" table, then this index will not be of any - ** further use in handling the ORDER BY. */ - pOBExpr = sqlite3ExprSkipCollate(pOBItem->pExpr); - if( pOBExpr->op!=TK_COLUMN || pOBExpr->iTable!=base ){ - break; - } - - /* Find column number and collating sequence for the next entry - ** in the index */ - if( pIdx->zName && inColumn ){ - iColumn = pIdx->aiColumn[i]; - if( iColumn==pIdx->pTable->iPKey ){ - iColumn = -1; - } - iSortOrder = pIdx->aSortOrder[i]; - zColl = pIdx->azColl[i]; - assert( zColl!=0 ); - }else{ - iColumn = -1; - iSortOrder = 0; - zColl = 0; - } - - /* Check to see if the column number and collating sequence of the - ** index match the column number and collating sequence of the ORDER BY - ** clause entry. Set isMatch to 1 if they both match. */ - if( pOBExpr->iColumn==iColumn ){ - if( zColl ){ - pColl = sqlite3ExprCollSeq(pParse, pOBItem->pExpr); - if( !pColl ) pColl = db->pDfltColl; - isMatch = sqlite3StrICmp(pColl->zName, zColl)==0; - }else{ - isMatch = 1; - } - }else{ - isMatch = 0; - } - - /* termSortOrder is 0 or 1 for whether or not the access loop should - ** run forward or backwards (respectively) in order to satisfy this - ** term of the ORDER BY clause. */ - assert( pOBItem->sortOrder==0 || pOBItem->sortOrder==1 ); - assert( iSortOrder==0 || iSortOrder==1 ); - termSortOrder = iSortOrder ^ pOBItem->sortOrder; - - /* If X is the column in the index and ORDER BY clause, check to see - ** if there are any X= or X IS NULL constraints in the WHERE clause. */ - pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, - WO_EQ|WO_ISNULL|WO_IN, pIdx); - if( pConstraint==0 ){ - isEq = 0; - }else if( (pConstraint->eOperator & WO_IN)!=0 ){ - isEq = 0; - }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ - uniqueNotNull = 0; - isEq = 1; /* "X IS NULL" means X has only a single value */ - }else if( pConstraint->prereqRight==0 ){ - isEq = 1; /* Constraint "X=constant" means X has only a single value */ - }else{ - Expr *pRight = pConstraint->pExpr->pRight; - if( pRight->op==TK_COLUMN ){ - WHERETRACE((" .. isOrderedColumn(tab=%d,col=%d)", - pRight->iTable, pRight->iColumn)); - isEq = isOrderedColumn(p, pRight->iTable, pRight->iColumn); - WHERETRACE((" -> isEq=%d\n", isEq)); - - /* If the constraint is of the form X=Y where Y is an ordered value - ** in an outer loop, then make sure the sort order of Y matches the - ** sort order required for X. */ - if( isMatch && isEq>=2 && isEq!=pOBItem->sortOrder+2 ){ - testcase( isEq==2 ); - testcase( isEq==3 ); - break; - } - }else{ - isEq = 0; /* "X=expr" places no ordering constraints on X */ - } - } - if( !isMatch ){ - if( isEq==0 ){ - break; - }else{ - continue; - } - }else if( isEq!=1 ){ - if( sortOrder==2 ){ - sortOrder = termSortOrder; - }else if( termSortOrder!=sortOrder ){ - break; - } - } - j++; - pOBItem++; - if( iColumn<0 ){ - seenRowid = 1; - break; - }else if( pTab->aCol[iColumn].notNull==0 && isEq!=1 ){ - testcase( isEq==0 ); - testcase( isEq==2 ); - testcase( isEq==3 ); - uniqueNotNull = 0; - } - } - if( seenRowid ){ - uniqueNotNull = 1; - }else if( uniqueNotNull==0 || inColumn ){ - uniqueNotNull = 0; - } - - /* If we have not found at least one ORDER BY term that matches the - ** index, then show no progress. */ - if( pOBItem==&pOrderBy->a[nPriorSat] ) return nPriorSat; - - /* Either the outer queries must generate rows where there are no two - ** rows with the same values in all ORDER BY columns, or else this - ** loop must generate just a single row of output. Example: Suppose - ** the outer loops generate A=1 and A=1, and this loop generates B=3 - ** and B=4. Then without the following test, ORDER BY A,B would - ** generate the wrong order output: 1,3 1,4 1,3 1,4 - */ - if( outerObUnique==0 && uniqueNotNull==0 ) return nPriorSat; - *pbObUnique = uniqueNotNull; - - /* Return the necessary scan order back to the caller */ - *pbRev = sortOrder & 1; - - /* If there was an "ORDER BY rowid" term that matched, or it is only - ** possible for a single row from this table to match, then skip over - ** any additional ORDER BY terms dealing with this table. - */ - if( uniqueNotNull ){ - /* Advance j over additional ORDER BY terms associated with base */ - WhereMaskSet *pMS = p->pWC->pMaskSet; - Bitmask m = ~getMask(pMS, base); - while( ja[j].pExpr)&m)==0 ){ - j++; - } - } - return j; -} - -/* -** Find the best query plan for accessing a particular table. Write the -** best query plan and its cost into the p->cost. -** -** The lowest cost plan wins. The cost is an estimate of the amount of -** CPU and disk I/O needed to process the requested result. -** Factors that influence cost include: -** -** * The estimated number of rows that will be retrieved. (The -** fewer the better.) -** -** * Whether or not sorting must occur. -** -** * Whether or not there must be separate lookups in the -** index and in the main table. -** -** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in -** the SQL statement, then this function only considers plans using the -** named index. If no such plan is found, then the returned cost is -** SQLITE_BIG_DBL. If a plan is found that uses the named index, -** then the cost is calculated in the usual way. -** -** If a NOT INDEXED clause was attached to the table -** in the SELECT statement, then no indexes are considered. However, the -** selected plan may still take advantage of the built-in rowid primary key -** index. -*/ -static void bestBtreeIndex(WhereBestIdx *p){ - Parse *pParse = p->pParse; /* The parsing context */ - WhereClause *pWC = p->pWC; /* The WHERE clause */ - struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */ - int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ - Index *pProbe; /* An index we are evaluating */ - Index *pIdx; /* Copy of pProbe, or zero for IPK index */ - int eqTermMask; /* Current mask of valid equality operators */ - int idxEqTermMask; /* Index mask of valid equality operators */ - Index sPk; /* A fake index object for the primary key */ - tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */ - int aiColumnPk = -1; /* The aColumn[] value for the sPk index */ - int wsFlagMask; /* Allowed flags in p->cost.plan.wsFlag */ - int nPriorSat; /* ORDER BY terms satisfied by outer loops */ - int nOrderBy; /* Number of ORDER BY terms */ - char bSortInit; /* Initializer for bSort in inner loop */ - char bDistInit; /* Initializer for bDist in inner loop */ - - - /* Initialize the cost to a worst-case value */ - memset(&p->cost, 0, sizeof(p->cost)); - p->cost.rCost = SQLITE_BIG_DBL; - - /* If the pSrc table is the right table of a LEFT JOIN then we may not - ** use an index to satisfy IS NULL constraints on that table. This is - ** because columns might end up being NULL if the table does not match - - ** a circumstance which the index cannot help us discover. Ticket #2177. - */ - if( pSrc->jointype & JT_LEFT ){ - idxEqTermMask = WO_EQ|WO_IN; - }else{ - idxEqTermMask = WO_EQ|WO_IN|WO_ISNULL; - } - - if( pSrc->pIndex ){ - /* An INDEXED BY clause specifies a particular index to use */ - pIdx = pProbe = pSrc->pIndex; - wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); - eqTermMask = idxEqTermMask; - }else{ - /* There is no INDEXED BY clause. Create a fake Index object in local - ** variable sPk to represent the rowid primary key index. Make this - ** fake index the first in a chain of Index objects with all of the real - ** indices to follow */ - Index *pFirst; /* First of real indices on the table */ - memset(&sPk, 0, sizeof(Index)); - sPk.nColumn = 1; - sPk.aiColumn = &aiColumnPk; - sPk.aiRowEst = aiRowEstPk; - sPk.onError = OE_Replace; - sPk.pTable = pSrc->pTab; - aiRowEstPk[0] = pSrc->pTab->nRowEst; - aiRowEstPk[1] = 1; - pFirst = pSrc->pTab->pIndex; - if( pSrc->notIndexed==0 ){ - /* The real indices of the table are only considered if the - ** NOT INDEXED qualifier is omitted from the FROM clause */ - sPk.pNext = pFirst; - } - pProbe = &sPk; - wsFlagMask = ~( - WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE - ); - eqTermMask = WO_EQ|WO_IN; - pIdx = 0; - } - - nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0; - if( p->i ){ - nPriorSat = p->aLevel[p->i-1].plan.nOBSat; - bSortInit = nPriorSat0; - bDistInit = p->pDistinct!=0; - } - - /* Loop over all indices looking for the best one to use - */ - for(; pProbe; pIdx=pProbe=pProbe->pNext){ - const tRowcnt * const aiRowEst = pProbe->aiRowEst; - WhereCost pc; /* Cost of using pProbe */ - double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */ - - /* The following variables are populated based on the properties of - ** index being evaluated. They are then used to determine the expected - ** cost and number of rows returned. - ** - ** pc.plan.nEq: - ** Number of equality terms that can be implemented using the index. - ** In other words, the number of initial fields in the index that - ** are used in == or IN or NOT NULL constraints of the WHERE clause. - ** - ** nInMul: - ** The "in-multiplier". This is an estimate of how many seek operations - ** SQLite must perform on the index in question. For example, if the - ** WHERE clause is: - ** - ** WHERE a IN (1, 2, 3) AND b IN (4, 5, 6) - ** - ** SQLite must perform 9 lookups on an index on (a, b), so nInMul is - ** set to 9. Given the same schema and either of the following WHERE - ** clauses: - ** - ** WHERE a = 1 - ** WHERE a >= 2 - ** - ** nInMul is set to 1. - ** - ** If there exists a WHERE term of the form "x IN (SELECT ...)", then - ** the sub-select is assumed to return 25 rows for the purposes of - ** determining nInMul. - ** - ** bInEst: - ** Set to true if there was at least one "x IN (SELECT ...)" term used - ** in determining the value of nInMul. Note that the RHS of the - ** IN operator must be a SELECT, not a value list, for this variable - ** to be true. - ** - ** rangeDiv: - ** An estimate of a divisor by which to reduce the search space due - ** to inequality constraints. In the absence of sqlite_stat3 ANALYZE - ** data, a single inequality reduces the search space to 1/4rd its - ** original size (rangeDiv==4). Two inequalities reduce the search - ** space to 1/16th of its original size (rangeDiv==16). - ** - ** bSort: - ** Boolean. True if there is an ORDER BY clause that will require an - ** external sort (i.e. scanning the index being evaluated will not - ** correctly order records). - ** - ** bDist: - ** Boolean. True if there is a DISTINCT clause that will require an - ** external btree. - ** - ** bLookup: - ** Boolean. True if a table lookup is required for each index entry - ** visited. In other words, true if this is not a covering index. - ** This is always false for the rowid primary key index of a table. - ** For other indexes, it is true unless all the columns of the table - ** used by the SELECT statement are present in the index (such an - ** index is sometimes described as a covering index). - ** For example, given the index on (a, b), the second of the following - ** two queries requires table b-tree lookups in order to find the value - ** of column c, but the first does not because columns a and b are - ** both available in the index. - ** - ** SELECT a, b FROM tbl WHERE a = 1; - ** SELECT a, b, c FROM tbl WHERE a = 1; - */ - int bInEst = 0; /* True if "x IN (SELECT...)" seen */ - int nInMul = 1; /* Number of distinct equalities to lookup */ - double rangeDiv = (double)1; /* Estimated reduction in search space */ - int nBound = 0; /* Number of range constraints seen */ - char bSort = bSortInit; /* True if external sort required */ - char bDist = bDistInit; /* True if index cannot help with DISTINCT */ - char bLookup = 0; /* True if not a covering index */ - WhereTerm *pTerm; /* A single term of the WHERE clause */ -#ifdef SQLITE_ENABLE_STAT3 - WhereTerm *pFirstTerm = 0; /* First term matching the index */ -#endif - - WHERETRACE(( - " %s(%s):\n", - pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk") - )); - memset(&pc, 0, sizeof(pc)); - pc.plan.nOBSat = nPriorSat; - - /* Determine the values of pc.plan.nEq and nInMul */ - for(pc.plan.nEq=0; pc.plan.nEqnColumn; pc.plan.nEq++){ - int j = pProbe->aiColumn[pc.plan.nEq]; - pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx); - if( pTerm==0 ) break; - pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); - testcase( pTerm->pWC!=pWC ); - if( pTerm->eOperator & WO_IN ){ - Expr *pExpr = pTerm->pExpr; - pc.plan.wsFlags |= WHERE_COLUMN_IN; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */ - nInMul *= 25; - bInEst = 1; - }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ - /* "x IN (value, value, ...)" */ - nInMul *= pExpr->x.pList->nExpr; - } - }else if( pTerm->eOperator & WO_ISNULL ){ - pc.plan.wsFlags |= WHERE_COLUMN_NULL; - } -#ifdef SQLITE_ENABLE_STAT3 - if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; -#endif - pc.used |= pTerm->prereqRight; - } - - /* If the index being considered is UNIQUE, and there is an equality - ** constraint for all columns in the index, then this search will find - ** at most a single row. In this case set the WHERE_UNIQUE flag to - ** indicate this to the caller. - ** - ** Otherwise, if the search may find more than one row, test to see if - ** there is a range constraint on indexed column (pc.plan.nEq+1) that - ** can be optimized using the index. - */ - if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){ - testcase( pc.plan.wsFlags & WHERE_COLUMN_IN ); - testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL ); - if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ - pc.plan.wsFlags |= WHERE_UNIQUE; - if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ - pc.plan.wsFlags |= WHERE_ALL_UNIQUE; - } - } - }else if( pProbe->bUnordered==0 ){ - int j; - j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]); - if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ - WhereTerm *pTop, *pBtm; - pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx); - pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx); - whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv); - if( pTop ){ - nBound = 1; - pc.plan.wsFlags |= WHERE_TOP_LIMIT; - pc.used |= pTop->prereqRight; - testcase( pTop->pWC!=pWC ); - } - if( pBtm ){ - nBound++; - pc.plan.wsFlags |= WHERE_BTM_LIMIT; - pc.used |= pBtm->prereqRight; - testcase( pBtm->pWC!=pWC ); - } - pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); - } - } - - /* If there is an ORDER BY clause and the index being considered will - ** naturally scan rows in the required order, set the appropriate flags - ** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but - ** the index will scan rows in a different order, set the bSort - ** variable. */ - if( bSort && (pSrc->jointype & JT_LEFT)==0 ){ - int bRev = 2; - int bObUnique = 0; - WHERETRACE((" --> before isSortIndex: nPriorSat=%d\n",nPriorSat)); - pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev, &bObUnique); - WHERETRACE((" --> after isSortIndex: bRev=%d bObU=%d nOBSat=%d\n", - bRev, bObUnique, pc.plan.nOBSat)); - if( nPriorSatpDistinct, pc.plan.nEq) - && (pc.plan.wsFlags & WHERE_COLUMN_IN)==0 - ){ - bDist = 0; - pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT; - } - - /* If currently calculating the cost of using an index (not the IPK - ** index), determine if all required column data may be obtained without - ** using the main table (i.e. if the index is a covering - ** index for this query). If it is, set the WHERE_IDX_ONLY flag in - ** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */ - if( pIdx ){ - Bitmask m = pSrc->colUsed; - int j; - for(j=0; jnColumn; j++){ - int x = pIdx->aiColumn[j]; - if( xaiRowEst[0] ){ - pc.plan.nRow = aiRowEst[0]/2; - nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]); - } - -#ifdef SQLITE_ENABLE_STAT3 - /* If the constraint is of the form x=VALUE or x IN (E1,E2,...) - ** and we do not think that values of x are unique and if histogram - ** data is available for column x, then it might be possible - ** to get a better estimate on the number of rows based on - ** VALUE and how common that value is according to the histogram. - */ - if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 - && pFirstTerm!=0 && aiRowEst[1]>1 ){ - assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); - if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ - testcase( pFirstTerm->eOperator & WO_EQ ); - testcase( pFirstTerm->eOperator & WO_EQUIV ); - testcase( pFirstTerm->eOperator & WO_ISNULL ); - whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, - &pc.plan.nRow); - }else if( bInEst==0 ){ - assert( pFirstTerm->eOperator & WO_IN ); - whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, - &pc.plan.nRow); - } - } -#endif /* SQLITE_ENABLE_STAT3 */ - - /* Adjust the number of output rows and downward to reflect rows - ** that are excluded by range constraints. - */ - pc.plan.nRow = pc.plan.nRow/rangeDiv; - if( pc.plan.nRow<1 ) pc.plan.nRow = 1; - - /* Experiments run on real SQLite databases show that the time needed - ** to do a binary search to locate a row in a table or index is roughly - ** log10(N) times the time to move from one row to the next row within - ** a table or index. The actual times can vary, with the size of - ** records being an important factor. Both moves and searches are - ** slower with larger records, presumably because fewer records fit - ** on one page and hence more pages have to be fetched. - ** - ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do - ** not give us data on the relative sizes of table and index records. - ** So this computation assumes table records are about twice as big - ** as index records - */ - if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED|WHERE_OB_UNIQUE)) - ==WHERE_IDX_ONLY - && (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 - && sqlite3GlobalConfig.bUseCis - && OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan) - ){ - /* This index is not useful for indexing, but it is a covering index. - ** A full-scan of the index might be a little faster than a full-scan - ** of the table, so give this case a cost slightly less than a table - ** scan. */ - pc.rCost = aiRowEst[0]*3 + pProbe->nColumn; - pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE; - }else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ - /* The cost of a full table scan is a number of move operations equal - ** to the number of rows in the table. - ** - ** We add an additional 4x penalty to full table scans. This causes - ** the cost function to err on the side of choosing an index over - ** choosing a full scan. This 4x full-scan penalty is an arguable - ** decision and one which we expect to revisit in the future. But - ** it seems to be working well enough at the moment. - */ - pc.rCost = aiRowEst[0]*4; - pc.plan.wsFlags &= ~WHERE_IDX_ONLY; - if( pIdx ){ - pc.plan.wsFlags &= ~WHERE_ORDERED; - pc.plan.nOBSat = nPriorSat; - } - }else{ - log10N = estLog(aiRowEst[0]); - pc.rCost = pc.plan.nRow; - if( pIdx ){ - if( bLookup ){ - /* For an index lookup followed by a table lookup: - ** nInMul index searches to find the start of each index range - ** + nRow steps through the index - ** + nRow table searches to lookup the table entry using the rowid - */ - pc.rCost += (nInMul + pc.plan.nRow)*log10N; - }else{ - /* For a covering index: - ** nInMul index searches to find the initial entry - ** + nRow steps through the index - */ - pc.rCost += nInMul*log10N; - } - }else{ - /* For a rowid primary key lookup: - ** nInMult table searches to find the initial entry for each range - ** + nRow steps through the table - */ - pc.rCost += nInMul*log10N; - } - } - - /* Add in the estimated cost of sorting the result. Actual experimental - ** measurements of sorting performance in SQLite show that sorting time - ** adds C*N*log10(N) to the cost, where N is the number of rows to be - ** sorted and C is a factor between 1.95 and 4.3. We will split the - ** difference and select C of 3.0. - */ - if( bSort ){ - double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy); - m *= (double)(pc.plan.nOBSat ? 2 : 3); - pc.rCost += pc.plan.nRow*m; - } - if( bDist ){ - pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3; - } - - /**** Cost of using this index has now been computed ****/ - - /* If there are additional constraints on this table that cannot - ** be used with the current index, but which might lower the number - ** of output rows, adjust the nRow value accordingly. This only - ** matters if the current index is the least costly, so do not bother - ** with this step if we already know this index will not be chosen. - ** Also, never reduce the output row count below 2 using this step. - ** - ** It is critical that the notValid mask be used here instead of - ** the notReady mask. When computing an "optimal" index, the notReady - ** mask will only have one bit set - the bit for the current table. - ** The notValid mask, on the other hand, always has all bits set for - ** tables that are not in outer loops. If notReady is used here instead - ** of notValid, then a optimal index that depends on inner joins loops - ** might be selected even when there exists an optimal index that has - ** no such dependency. - */ - if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){ - int k; /* Loop counter */ - int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */ - int nSkipRange = nBound; /* Number of < constraints to skip */ - Bitmask thisTab; /* Bitmap for pSrc */ - - thisTab = getMask(pWC->pMaskSet, iCur); - for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){ - if( pTerm->wtFlags & TERM_VIRTUAL ) continue; - if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue; - if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ - if( nSkipEq ){ - /* Ignore the first pc.plan.nEq equality matches since the index - ** has already accounted for these */ - nSkipEq--; - }else{ - /* Assume each additional equality match reduces the result - ** set size by a factor of 10 */ - pc.plan.nRow /= 10; - } - }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ - if( nSkipRange ){ - /* Ignore the first nSkipRange range constraints since the index - ** has already accounted for these */ - nSkipRange--; - }else{ - /* Assume each additional range constraint reduces the result - ** set size by a factor of 3. Indexed range constraints reduce - ** the search space by a larger factor: 4. We make indexed range - ** more selective intentionally because of the subjective - ** observation that indexed range constraints really are more - ** selective in practice, on average. */ - pc.plan.nRow /= 3; - } - }else if( (pTerm->eOperator & WO_NOOP)==0 ){ - /* Any other expression lowers the output row count by half */ - pc.plan.nRow /= 2; - } - } - if( pc.plan.nRow<2 ) pc.plan.nRow = 2; - } - - - WHERETRACE(( - " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n" - " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n" - " used=0x%llx nOBSat=%d\n", - pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags, - p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, - pc.plan.nOBSat - )); - - /* If this index is the best we have seen so far, then record this - ** index and its cost in the p->cost structure. - */ - if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){ - p->cost = pc; - p->cost.plan.wsFlags &= wsFlagMask; - p->cost.plan.u.pIdx = pIdx; - } - - /* If there was an INDEXED BY clause, then only that one index is - ** considered. */ - if( pSrc->pIndex ) break; - - /* Reset masks for the next index in the loop */ - wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); - eqTermMask = idxEqTermMask; - } - - /* If there is no ORDER BY clause and the SQLITE_ReverseOrder flag - ** is set, then reverse the order that the index will be scanned - ** in. This is used for application testing, to help find cases - ** where application behavior depends on the (undefined) order that - ** SQLite outputs rows in in the absence of an ORDER BY clause. */ - if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){ - p->cost.plan.wsFlags |= WHERE_REVERSE; - } - - assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 ); - assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 ); - assert( pSrc->pIndex==0 - || p->cost.plan.u.pIdx==0 - || p->cost.plan.u.pIdx==pSrc->pIndex - ); - - WHERETRACE((" best index is %s cost=%.1f\n", - p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", - p->cost.rCost)); - - bestOrClauseIndex(p); - bestAutomaticIndex(p); - p->cost.plan.wsFlags |= eqTermMask; -} - -/* -** Find the query plan for accessing table pSrc->pTab. Write the -** best query plan and its cost into the WhereCost object supplied -** as the last parameter. This function may calculate the cost of -** both real and virtual table scans. -** -** This function does not take ORDER BY or DISTINCT into account. Nor -** does it remember the virtual table query plan. All it does is compute -** the cost while determining if an OR optimization is applicable. The -** details will be reconsidered later if the optimization is found to be -** applicable. -*/ -static void bestIndex(WhereBestIdx *p){ -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(p->pSrc->pTab) ){ - sqlite3_index_info *pIdxInfo = 0; - p->ppIdxInfo = &pIdxInfo; - bestVirtualIndex(p); - assert( pIdxInfo!=0 || p->pParse->db->mallocFailed ); - if( pIdxInfo && pIdxInfo->needToFreeIdxStr ){ - sqlite3_free(pIdxInfo->idxStr); - } - sqlite3DbFree(p->pParse->db, pIdxInfo); - }else -#endif - { - bestBtreeIndex(p); - } -} - -/* -** Disable a term in the WHERE clause. Except, do not disable the term -** if it controls a LEFT OUTER JOIN and it did not originate in the ON -** or USING clause of that join. -** -** Consider the term t2.z='ok' in the following queries: -** -** (1) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x WHERE t2.z='ok' -** (2) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x AND t2.z='ok' -** (3) SELECT * FROM t1, t2 WHERE t1.a=t2.x AND t2.z='ok' -** -** The t2.z='ok' is disabled in the in (2) because it originates -** in the ON clause. The term is disabled in (3) because it is not part -** of a LEFT OUTER JOIN. In (1), the term is not disabled. -** -** IMPLEMENTATION-OF: R-24597-58655 No tests are done for terms that are -** completely satisfied by indices. -** -** Disabling a term causes that term to not be tested in the inner loop -** of the join. Disabling is an optimization. When terms are satisfied -** by indices, we disable them to prevent redundant tests in the inner -** loop. We would get the correct results if nothing were ever disabled, -** but joins might run a little slower. The trick is to disable as much -** as we can without disabling too much. If we disabled in (1), we'd get -** the wrong answer. See ticket #813. -*/ -static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ - if( pTerm - && (pTerm->wtFlags & TERM_CODED)==0 - && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) - ){ - pTerm->wtFlags |= TERM_CODED; - if( pTerm->iParent>=0 ){ - WhereTerm *pOther = &pTerm->pWC->a[pTerm->iParent]; - if( (--pOther->nChild)==0 ){ - disableTerm(pLevel, pOther); - } - } - } -} - -/* -** Code an OP_Affinity opcode to apply the column affinity string zAff -** to the n registers starting at base. -** -** As an optimization, SQLITE_AFF_NONE entries (which are no-ops) at the -** beginning and end of zAff are ignored. If all entries in zAff are -** SQLITE_AFF_NONE, then no code gets generated. -** -** This routine makes its own copy of zAff so that the caller is free -** to modify zAff after this routine returns. -*/ -static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){ - Vdbe *v = pParse->pVdbe; - if( zAff==0 ){ - assert( pParse->db->mallocFailed ); - return; - } - assert( v!=0 ); - - /* Adjust base and n to skip over SQLITE_AFF_NONE entries at the beginning - ** and end of the affinity string. - */ - while( n>0 && zAff[0]==SQLITE_AFF_NONE ){ - n--; - base++; - zAff++; - } - while( n>1 && zAff[n-1]==SQLITE_AFF_NONE ){ - n--; - } - - /* Code the OP_Affinity opcode if there is anything left to do. */ - if( n>0 ){ - sqlite3VdbeAddOp2(v, OP_Affinity, base, n); - sqlite3VdbeChangeP4(v, -1, zAff, n); - sqlite3ExprCacheAffinityChange(pParse, base, n); - } -} - - -/* -** Generate code for a single equality term of the WHERE clause. An equality -** term can be either X=expr or X IN (...). pTerm is the term to be -** coded. -** -** The current value for the constraint is left in register iReg. -** -** For a constraint of the form X=expr, the expression is evaluated and its -** result is left on the stack. For constraints of the form X IN (...) -** this routine sets up a loop that will iterate over all values of X. -*/ -static int codeEqualityTerm( - Parse *pParse, /* The parsing context */ - WhereTerm *pTerm, /* The term of the WHERE clause to be coded */ - WhereLevel *pLevel, /* The level of the FROM clause we are working on */ - int iEq, /* Index of the equality term within this level */ - int iTarget /* Attempt to leave results in this register */ -){ - Expr *pX = pTerm->pExpr; - Vdbe *v = pParse->pVdbe; - int iReg; /* Register holding results */ - - assert( iTarget>0 ); - if( pX->op==TK_EQ ){ - iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget); - }else if( pX->op==TK_ISNULL ){ - iReg = iTarget; - sqlite3VdbeAddOp2(v, OP_Null, 0, iReg); -#ifndef SQLITE_OMIT_SUBQUERY - }else{ - int eType; - int iTab; - struct InLoop *pIn; - u8 bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0; - - if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 - && pLevel->plan.u.pIdx->aSortOrder[iEq] - ){ - testcase( iEq==0 ); - testcase( iEq==pLevel->plan.u.pIdx->nColumn-1 ); - testcase( iEq>0 && iEq+1plan.u.pIdx->nColumn ); - testcase( bRev ); - bRev = !bRev; - } - assert( pX->op==TK_IN ); - iReg = iTarget; - eType = sqlite3FindInIndex(pParse, pX, 0); - if( eType==IN_INDEX_INDEX_DESC ){ - testcase( bRev ); - bRev = !bRev; - } - iTab = pX->iTable; - sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); - assert( pLevel->plan.wsFlags & WHERE_IN_ABLE ); - if( pLevel->u.in.nIn==0 ){ - pLevel->addrNxt = sqlite3VdbeMakeLabel(v); - } - pLevel->u.in.nIn++; - pLevel->u.in.aInLoop = - sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop, - sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); - pIn = pLevel->u.in.aInLoop; - if( pIn ){ - pIn += pLevel->u.in.nIn - 1; - pIn->iCur = iTab; - if( eType==IN_INDEX_ROWID ){ - pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg); - }else{ - pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg); - } - pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; - sqlite3VdbeAddOp1(v, OP_IsNull, iReg); - }else{ - pLevel->u.in.nIn = 0; - } -#endif - } - disableTerm(pLevel, pTerm); - return iReg; -} - -/* -** Generate code that will evaluate all == and IN constraints for an -** index. -** -** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c). -** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10 -** The index has as many as three equality constraints, but in this -** example, the third "c" value is an inequality. So only two -** constraints are coded. This routine will generate code to evaluate -** a==5 and b IN (1,2,3). The current values for a and b will be stored -** in consecutive registers and the index of the first register is returned. -** -** In the example above nEq==2. But this subroutine works for any value -** of nEq including 0. If nEq==0, this routine is nearly a no-op. -** The only thing it does is allocate the pLevel->iMem memory cell and -** compute the affinity string. -** -** This routine always allocates at least one memory cell and returns -** the index of that memory cell. The code that -** calls this routine will use that memory cell to store the termination -** key value of the loop. If one or more IN operators appear, then -** this routine allocates an additional nEq memory cells for internal -** use. -** -** Before returning, *pzAff is set to point to a buffer containing a -** copy of the column affinity string of the index allocated using -** sqlite3DbMalloc(). Except, entries in the copy of the string associated -** with equality constraints that use NONE affinity are set to -** SQLITE_AFF_NONE. This is to deal with SQL such as the following: -** -** CREATE TABLE t1(a TEXT PRIMARY KEY, b); -** SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b; -** -** In the example above, the index on t1(a) has TEXT affinity. But since -** the right hand side of the equality constraint (t2.b) has NONE affinity, -** no conversion should be attempted before using a t2.b value as part of -** a key to search the index. Hence the first byte in the returned affinity -** string in this example would be set to SQLITE_AFF_NONE. -*/ -static int codeAllEqualityTerms( - Parse *pParse, /* Parsing context */ - WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */ - WhereClause *pWC, /* The WHERE clause */ - Bitmask notReady, /* Which parts of FROM have not yet been coded */ - int nExtraReg, /* Number of extra registers to allocate */ - char **pzAff /* OUT: Set to point to affinity string */ -){ - int nEq = pLevel->plan.nEq; /* The number of == or IN constraints to code */ - Vdbe *v = pParse->pVdbe; /* The vm under construction */ - Index *pIdx; /* The index being used for this loop */ - int iCur = pLevel->iTabCur; /* The cursor of the table */ - WhereTerm *pTerm; /* A single constraint term */ - int j; /* Loop counter */ - int regBase; /* Base register */ - int nReg; /* Number of registers to allocate */ - char *zAff; /* Affinity string to return */ - - /* This module is only called on query plans that use an index. */ - assert( pLevel->plan.wsFlags & WHERE_INDEXED ); - pIdx = pLevel->plan.u.pIdx; - - /* Figure out how many memory cells we will need then allocate them. - */ - regBase = pParse->nMem + 1; - nReg = pLevel->plan.nEq + nExtraReg; - pParse->nMem += nReg; - - zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx)); - if( !zAff ){ - pParse->db->mallocFailed = 1; - } - - /* Evaluate the equality constraints - */ - assert( pIdx->nColumn>=nEq ); - for(j=0; jaiColumn[j]; - pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx); - if( pTerm==0 ) break; - /* The following true for indices with redundant columns. - ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */ - testcase( (pTerm->wtFlags & TERM_CODED)!=0 ); - testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, regBase+j); - if( r1!=regBase+j ){ - if( nReg==1 ){ - sqlite3ReleaseTempReg(pParse, regBase); - regBase = r1; - }else{ - sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j); - } - } - testcase( pTerm->eOperator & WO_ISNULL ); - testcase( pTerm->eOperator & WO_IN ); - if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ - Expr *pRight = pTerm->pExpr->pRight; - sqlite3ExprCodeIsNullJump(v, pRight, regBase+j, pLevel->addrBrk); - if( zAff ){ - if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_NONE ){ - zAff[j] = SQLITE_AFF_NONE; - } - if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){ - zAff[j] = SQLITE_AFF_NONE; - } - } - } - } - *pzAff = zAff; - return regBase; -} - -#ifndef SQLITE_OMIT_EXPLAIN -/* -** This routine is a helper for explainIndexRange() below -** -** pStr holds the text of an expression that we are building up one term -** at a time. This routine adds a new term to the end of the expression. -** Terms are separated by AND so add the "AND" text for second and subsequent -** terms only. -*/ -static void explainAppendTerm( - StrAccum *pStr, /* The text expression being built */ - int iTerm, /* Index of this term. First is zero */ - const char *zColumn, /* Name of the column */ - const char *zOp /* Name of the operator */ -){ - if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5); - sqlite3StrAccumAppend(pStr, zColumn, -1); - sqlite3StrAccumAppend(pStr, zOp, 1); - sqlite3StrAccumAppend(pStr, "?", 1); -} - -/* -** Argument pLevel describes a strategy for scanning table pTab. This -** function returns a pointer to a string buffer containing a description -** of the subset of table rows scanned by the strategy in the form of an -** SQL expression. Or, if all rows are scanned, NULL is returned. -** -** For example, if the query: -** -** SELECT * FROM t1 WHERE a=1 AND b>2; -** -** is run and there is an index on (a, b), then this function returns a -** string similar to: -** -** "a=? AND b>?" -** -** The returned pointer points to memory obtained from sqlite3DbMalloc(). -** It is the responsibility of the caller to free the buffer when it is -** no longer required. -*/ -static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){ - WherePlan *pPlan = &pLevel->plan; - Index *pIndex = pPlan->u.pIdx; - int nEq = pPlan->nEq; - int i, j; - Column *aCol = pTab->aCol; - int *aiColumn = pIndex->aiColumn; - StrAccum txt; - - if( nEq==0 && (pPlan->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){ - return 0; - } - sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH); - txt.db = db; - sqlite3StrAccumAppend(&txt, " (", 2); - for(i=0; i"); - } - if( pPlan->wsFlags&WHERE_TOP_LIMIT ){ - char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName; - explainAppendTerm(&txt, i, z, "<"); - } - sqlite3StrAccumAppend(&txt, ")", 1); - return sqlite3StrAccumFinish(&txt); -} - -/* -** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN -** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single -** record is added to the output to describe the table scan strategy in -** pLevel. -*/ -static void explainOneScan( - Parse *pParse, /* Parse context */ - SrcList *pTabList, /* Table list this loop refers to */ - WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ - int iLevel, /* Value for "level" column of output */ - int iFrom, /* Value for "from" column of output */ - u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ -){ - if( pParse->explain==2 ){ - u32 flags = pLevel->plan.wsFlags; - struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; - Vdbe *v = pParse->pVdbe; /* VM being constructed */ - sqlite3 *db = pParse->db; /* Database handle */ - char *zMsg; /* Text to add to EQP output */ - sqlite3_int64 nRow; /* Expected number of rows visited by scan */ - int iId = pParse->iSelectId; /* Select id (left-most output column) */ - int isSearch; /* True for a SEARCH. False for SCAN. */ - - if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return; - - isSearch = (pLevel->plan.nEq>0) - || (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 - || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); - - zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN"); - if( pItem->pSelect ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s SUBQUERY %d", zMsg,pItem->iSelectId); - }else{ - zMsg = sqlite3MAppendf(db, zMsg, "%s TABLE %s", zMsg, pItem->zName); - } - - if( pItem->zAlias ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); - } - if( (flags & WHERE_INDEXED)!=0 ){ - char *zWhere = explainIndexRange(db, pLevel, pItem->pTab); - zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg, - ((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""), - ((flags & WHERE_IDX_ONLY)?"COVERING ":""), - ((flags & WHERE_TEMP_INDEX)?"":" "), - ((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName), - zWhere - ); - sqlite3DbFree(db, zWhere); - }else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg); - - if( flags&WHERE_ROWID_EQ ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg); - }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid?)", zMsg); - }else if( flags&WHERE_TOP_LIMIT ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s (rowidplan.u.pVtabIdx; - zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg, - pVtabIdx->idxNum, pVtabIdx->idxStr); - } -#endif - if( wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) ){ - testcase( wctrlFlags & WHERE_ORDERBY_MIN ); - nRow = 1; - }else{ - nRow = (sqlite3_int64)pLevel->plan.nRow; - } - zMsg = sqlite3MAppendf(db, zMsg, "%s (~%lld rows)", zMsg, nRow); - sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC); - } -} -#else -# define explainOneScan(u,v,w,x,y,z) -#endif /* SQLITE_OMIT_EXPLAIN */ - - -/* -** Generate code for the start of the iLevel-th loop in the WHERE clause -** implementation described by pWInfo. -*/ -static Bitmask codeOneLoopStart( - WhereInfo *pWInfo, /* Complete information about the WHERE clause */ - int iLevel, /* Which level of pWInfo->a[] should be coded */ - u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */ - Bitmask notReady /* Which tables are currently available */ -){ - int j, k; /* Loop counters */ - int iCur; /* The VDBE cursor for the table */ - int addrNxt; /* Where to jump to continue with the next IN case */ - int omitTable; /* True if we use the index only */ - int bRev; /* True if we need to scan in reverse order */ - WhereLevel *pLevel; /* The where level to be coded */ - WhereClause *pWC; /* Decomposition of the entire WHERE clause */ - WhereTerm *pTerm; /* A WHERE clause term */ - Parse *pParse; /* Parsing context */ - Vdbe *v; /* The prepared stmt under constructions */ - struct SrcList_item *pTabItem; /* FROM clause term being coded */ - int addrBrk; /* Jump here to break out of the loop */ - int addrCont; /* Jump here to continue with next cycle */ - int iRowidReg = 0; /* Rowid is stored in this register, if not zero */ - int iReleaseReg = 0; /* Temp register to free before returning */ - - pParse = pWInfo->pParse; - v = pParse->pVdbe; - pWC = pWInfo->pWC; - pLevel = &pWInfo->a[iLevel]; - pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; - iCur = pTabItem->iCursor; - bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0; - omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0 - && (wctrlFlags & WHERE_FORCE_TABLE)==0; - - /* Create labels for the "break" and "continue" instructions - ** for the current loop. Jump to addrBrk to break out of a loop. - ** Jump to cont to go immediately to the next iteration of the - ** loop. - ** - ** When there is an IN operator, we also have a "addrNxt" label that - ** means to continue with the next IN value combination. When - ** there are no IN operators in the constraints, the "addrNxt" label - ** is the same as "addrBrk". - */ - addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v); - addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v); - - /* If this is the right table of a LEFT OUTER JOIN, allocate and - ** initialize a memory cell that records if this table matches any - ** row of the left table of the join. - */ - if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){ - pLevel->iLeftJoin = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); - VdbeComment((v, "init LEFT JOIN no-match flag")); - } - - /* Special case of a FROM clause subquery implemented as a co-routine */ - if( pTabItem->viaCoroutine ){ - int regYield = pTabItem->regReturn; - sqlite3VdbeAddOp2(v, OP_Integer, pTabItem->addrFillSub-1, regYield); - pLevel->p2 = sqlite3VdbeAddOp1(v, OP_Yield, regYield); - VdbeComment((v, "next row of co-routine %s", pTabItem->pTab->zName)); - sqlite3VdbeAddOp2(v, OP_If, regYield+1, addrBrk); - pLevel->op = OP_Goto; - }else - -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ - /* Case 0: The table is a virtual-table. Use the VFilter and VNext - ** to access the data. - */ - int iReg; /* P3 Value for OP_VFilter */ - int addrNotFound; - sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; - int nConstraint = pVtabIdx->nConstraint; - struct sqlite3_index_constraint_usage *aUsage = - pVtabIdx->aConstraintUsage; - const struct sqlite3_index_constraint *aConstraint = - pVtabIdx->aConstraint; - - sqlite3ExprCachePush(pParse); - iReg = sqlite3GetTempRange(pParse, nConstraint+2); - addrNotFound = pLevel->addrBrk; - for(j=1; j<=nConstraint; j++){ - for(k=0; ka[aConstraint[k].iTermOffset]; - if( pTerm->eOperator & WO_IN ){ - codeEqualityTerm(pParse, pTerm, pLevel, k, iTarget); - addrNotFound = pLevel->addrNxt; - }else{ - sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget); - } - break; - } - } - if( k==nConstraint ) break; - } - sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg); - sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1); - sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pVtabIdx->idxStr, - pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC); - pVtabIdx->needToFreeIdxStr = 0; - for(j=0; ja[iTerm]); - } - } - pLevel->op = OP_VNext; - pLevel->p1 = iCur; - pLevel->p2 = sqlite3VdbeCurrentAddr(v); - sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2); - sqlite3ExprCachePop(pParse, 1); - }else -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - - if( pLevel->plan.wsFlags & WHERE_ROWID_EQ ){ - /* Case 1: We can directly reference a single row using an - ** equality comparison against the ROWID field. Or - ** we reference multiple rows using a "rowid IN (...)" - ** construct. - */ - iReleaseReg = sqlite3GetTempReg(pParse); - pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); - assert( pTerm!=0 ); - assert( pTerm->pExpr!=0 ); - assert( omitTable==0 ); - testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, iReleaseReg); - addrNxt = pLevel->addrNxt; - sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg); - sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1); - sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); - VdbeComment((v, "pk")); - pLevel->op = OP_Noop; - }else if( pLevel->plan.wsFlags & WHERE_ROWID_RANGE ){ - /* Case 2: We have an inequality comparison against the ROWID field. - */ - int testOp = OP_Noop; - int start; - int memEndValue = 0; - WhereTerm *pStart, *pEnd; - - assert( omitTable==0 ); - pStart = findTerm(pWC, iCur, -1, notReady, WO_GT|WO_GE, 0); - pEnd = findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE, 0); - if( bRev ){ - pTerm = pStart; - pStart = pEnd; - pEnd = pTerm; - } - if( pStart ){ - Expr *pX; /* The expression that defines the start bound */ - int r1, rTemp; /* Registers for holding the start boundary */ - - /* The following constant maps TK_xx codes into corresponding - ** seek opcodes. It depends on a particular ordering of TK_xx - */ - const u8 aMoveOp[] = { - /* TK_GT */ OP_SeekGt, - /* TK_LE */ OP_SeekLe, - /* TK_LT */ OP_SeekLt, - /* TK_GE */ OP_SeekGe - }; - assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */ - assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */ - assert( TK_GE==TK_GT+3 ); /* ... is correcct. */ - - testcase( pStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - pX = pStart->pExpr; - assert( pX!=0 ); - assert( pStart->leftCursor==iCur ); - r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp); - sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1); - VdbeComment((v, "pk")); - sqlite3ExprCacheAffinityChange(pParse, r1, 1); - sqlite3ReleaseTempReg(pParse, rTemp); - disableTerm(pLevel, pStart); - }else{ - sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk); - } - if( pEnd ){ - Expr *pX; - pX = pEnd->pExpr; - assert( pX!=0 ); - assert( pEnd->leftCursor==iCur ); - testcase( pEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - memEndValue = ++pParse->nMem; - sqlite3ExprCode(pParse, pX->pRight, memEndValue); - if( pX->op==TK_LT || pX->op==TK_GT ){ - testOp = bRev ? OP_Le : OP_Ge; - }else{ - testOp = bRev ? OP_Lt : OP_Gt; - } - disableTerm(pLevel, pEnd); - } - start = sqlite3VdbeCurrentAddr(v); - pLevel->op = bRev ? OP_Prev : OP_Next; - pLevel->p1 = iCur; - pLevel->p2 = start; - if( pStart==0 && pEnd==0 ){ - pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; - }else{ - assert( pLevel->p5==0 ); - } - if( testOp!=OP_Noop ){ - iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg); - sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); - sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg); - sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); - } - }else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){ - /* Case 3: A scan using an index. - ** - ** The WHERE clause may contain zero or more equality - ** terms ("==" or "IN" operators) that refer to the N - ** left-most columns of the index. It may also contain - ** inequality constraints (>, <, >= or <=) on the indexed - ** column that immediately follows the N equalities. Only - ** the right-most column can be an inequality - the rest must - ** use the "==" and "IN" operators. For example, if the - ** index is on (x,y,z), then the following clauses are all - ** optimized: - ** - ** x=5 - ** x=5 AND y=10 - ** x=5 AND y<10 - ** x=5 AND y>5 AND y<10 - ** x=5 AND y=5 AND z<=10 - ** - ** The z<10 term of the following cannot be used, only - ** the x=5 term: - ** - ** x=5 AND z<10 - ** - ** N may be zero if there are inequality constraints. - ** If there are no inequality constraints, then N is at - ** least one. - ** - ** This case is also used when there are no WHERE clause - ** constraints but an index is selected anyway, in order - ** to force the output order to conform to an ORDER BY. - */ - static const u8 aStartOp[] = { - 0, - 0, - OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */ - OP_Last, /* 3: (!start_constraints && startEq && bRev) */ - OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */ - OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */ - OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */ - OP_SeekLe /* 7: (start_constraints && startEq && bRev) */ - }; - static const u8 aEndOp[] = { - OP_Noop, /* 0: (!end_constraints) */ - OP_IdxGE, /* 1: (end_constraints && !bRev) */ - OP_IdxLT /* 2: (end_constraints && bRev) */ - }; - int nEq = pLevel->plan.nEq; /* Number of == or IN terms */ - int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */ - int regBase; /* Base register holding constraint values */ - int r1; /* Temp register */ - WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ - WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */ - int startEq; /* True if range start uses ==, >= or <= */ - int endEq; /* True if range end uses ==, >= or <= */ - int start_constraints; /* Start of range is constrained */ - int nConstraint; /* Number of constraint terms */ - Index *pIdx; /* The index we will be using */ - int iIdxCur; /* The VDBE cursor for the index */ - int nExtraReg = 0; /* Number of extra registers needed */ - int op; /* Instruction opcode */ - char *zStartAff; /* Affinity for start of range constraint */ - char *zEndAff; /* Affinity for end of range constraint */ - - pIdx = pLevel->plan.u.pIdx; - iIdxCur = pLevel->iIdxCur; - k = (nEq==pIdx->nColumn ? -1 : pIdx->aiColumn[nEq]); - - /* If this loop satisfies a sort order (pOrderBy) request that - ** was passed to this function to implement a "SELECT min(x) ..." - ** query, then the caller will only allow the loop to run for - ** a single iteration. This means that the first row returned - ** should not have a NULL value stored in 'x'. If column 'x' is - ** the first one after the nEq equality constraints in the index, - ** this requires some special handling. - */ - if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0 - && (pLevel->plan.wsFlags&WHERE_ORDERED) - && (pIdx->nColumn>nEq) - ){ - /* assert( pOrderBy->nExpr==1 ); */ - /* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */ - isMinQuery = 1; - nExtraReg = 1; - } - - /* Find any inequality constraint terms for the start and end - ** of the range. - */ - if( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ){ - pRangeEnd = findTerm(pWC, iCur, k, notReady, (WO_LT|WO_LE), pIdx); - nExtraReg = 1; - } - if( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ){ - pRangeStart = findTerm(pWC, iCur, k, notReady, (WO_GT|WO_GE), pIdx); - nExtraReg = 1; - } - - /* Generate code to evaluate all constraint terms using == or IN - ** and store the values of those terms in an array of registers - ** starting at regBase. - */ - regBase = codeAllEqualityTerms( - pParse, pLevel, pWC, notReady, nExtraReg, &zStartAff - ); - zEndAff = sqlite3DbStrDup(pParse->db, zStartAff); - addrNxt = pLevel->addrNxt; - - /* If we are doing a reverse order scan on an ascending index, or - ** a forward order scan on a descending index, interchange the - ** start and end terms (pRangeStart and pRangeEnd). - */ - if( (nEqnColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) - || (bRev && pIdx->nColumn==nEq) - ){ - SWAP(WhereTerm *, pRangeEnd, pRangeStart); - } - - testcase( pRangeStart && pRangeStart->eOperator & WO_LE ); - testcase( pRangeStart && pRangeStart->eOperator & WO_GE ); - testcase( pRangeEnd && pRangeEnd->eOperator & WO_LE ); - testcase( pRangeEnd && pRangeEnd->eOperator & WO_GE ); - startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE); - endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE); - start_constraints = pRangeStart || nEq>0; - - /* Seek the index cursor to the start of the range. */ - nConstraint = nEq; - if( pRangeStart ){ - Expr *pRight = pRangeStart->pExpr->pRight; - sqlite3ExprCode(pParse, pRight, regBase+nEq); - if( (pRangeStart->wtFlags & TERM_VNULL)==0 ){ - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); - } - if( zStartAff ){ - if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){ - /* Since the comparison is to be performed with no conversions - ** applied to the operands, set the affinity to apply to pRight to - ** SQLITE_AFF_NONE. */ - zStartAff[nEq] = SQLITE_AFF_NONE; - } - if( sqlite3ExprNeedsNoAffinityChange(pRight, zStartAff[nEq]) ){ - zStartAff[nEq] = SQLITE_AFF_NONE; - } - } - nConstraint++; - testcase( pRangeStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - }else if( isMinQuery ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); - nConstraint++; - startEq = 0; - start_constraints = 1; - } - codeApplyAffinity(pParse, regBase, nConstraint, zStartAff); - op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; - assert( op!=0 ); - testcase( op==OP_Rewind ); - testcase( op==OP_Last ); - testcase( op==OP_SeekGt ); - testcase( op==OP_SeekGe ); - testcase( op==OP_SeekLe ); - testcase( op==OP_SeekLt ); - sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); - - /* Load the value for the inequality constraint at the end of the - ** range (if any). - */ - nConstraint = nEq; - if( pRangeEnd ){ - Expr *pRight = pRangeEnd->pExpr->pRight; - sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); - sqlite3ExprCode(pParse, pRight, regBase+nEq); - if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); - } - if( zEndAff ){ - if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ - /* Since the comparison is to be performed with no conversions - ** applied to the operands, set the affinity to apply to pRight to - ** SQLITE_AFF_NONE. */ - zEndAff[nEq] = SQLITE_AFF_NONE; - } - if( sqlite3ExprNeedsNoAffinityChange(pRight, zEndAff[nEq]) ){ - zEndAff[nEq] = SQLITE_AFF_NONE; - } - } - codeApplyAffinity(pParse, regBase, nEq+1, zEndAff); - nConstraint++; - testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - } - sqlite3DbFree(pParse->db, zStartAff); - sqlite3DbFree(pParse->db, zEndAff); - - /* Top of the loop body */ - pLevel->p2 = sqlite3VdbeCurrentAddr(v); - - /* Check if the index cursor is past the end of the range. */ - op = aEndOp[(pRangeEnd || nEq) * (1 + bRev)]; - testcase( op==OP_Noop ); - testcase( op==OP_IdxGE ); - testcase( op==OP_IdxLT ); - if( op!=OP_Noop ){ - sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); - sqlite3VdbeChangeP5(v, endEq!=bRev ?1:0); - } - - /* If there are inequality constraints, check that the value - ** of the table column that the inequality contrains is not NULL. - ** If it is, jump to the next iteration of the loop. - */ - r1 = sqlite3GetTempReg(pParse); - testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ); - testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ); - if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){ - sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1); - sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont); - } - sqlite3ReleaseTempReg(pParse, r1); - - /* Seek the table cursor, if required */ - disableTerm(pLevel, pRangeStart); - disableTerm(pLevel, pRangeEnd); - if( !omitTable ){ - iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); - sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); - sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */ - } - - /* Record the instruction used to terminate the loop. Disable - ** WHERE clause terms made redundant by the index range scan. - */ - if( pLevel->plan.wsFlags & WHERE_UNIQUE ){ - pLevel->op = OP_Noop; - }else if( bRev ){ - pLevel->op = OP_Prev; - }else{ - pLevel->op = OP_Next; - } - pLevel->p1 = iIdxCur; - if( pLevel->plan.wsFlags & WHERE_COVER_SCAN ){ - pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; - }else{ - assert( pLevel->p5==0 ); - } - }else - -#ifndef SQLITE_OMIT_OR_OPTIMIZATION - if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ - /* Case 4: Two or more separately indexed terms connected by OR - ** - ** Example: - ** - ** CREATE TABLE t1(a,b,c,d); - ** CREATE INDEX i1 ON t1(a); - ** CREATE INDEX i2 ON t1(b); - ** CREATE INDEX i3 ON t1(c); - ** - ** SELECT * FROM t1 WHERE a=5 OR b=7 OR (c=11 AND d=13) - ** - ** In the example, there are three indexed terms connected by OR. - ** The top of the loop looks like this: - ** - ** Null 1 # Zero the rowset in reg 1 - ** - ** Then, for each indexed term, the following. The arguments to - ** RowSetTest are such that the rowid of the current row is inserted - ** into the RowSet. If it is already present, control skips the - ** Gosub opcode and jumps straight to the code generated by WhereEnd(). - ** - ** sqlite3WhereBegin() - ** RowSetTest # Insert rowid into rowset - ** Gosub 2 A - ** sqlite3WhereEnd() - ** - ** Following the above, code to terminate the loop. Label A, the target - ** of the Gosub above, jumps to the instruction right after the Goto. - ** - ** Null 1 # Zero the rowset in reg 1 - ** Goto B # The loop is finished. - ** - ** A: # Return data, whatever. - ** - ** Return 2 # Jump back to the Gosub - ** - ** B: - ** - */ - WhereClause *pOrWc; /* The OR-clause broken out into subterms */ - SrcList *pOrTab; /* Shortened table list or OR-clause generation */ - Index *pCov = 0; /* Potential covering index (or NULL) */ - int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */ - - int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ - int regRowset = 0; /* Register for RowSet object */ - int regRowid = 0; /* Register holding rowid */ - int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ - int iRetInit; /* Address of regReturn init */ - int untestedTerms = 0; /* Some terms not completely tested */ - int ii; /* Loop counter */ - Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ - - pTerm = pLevel->plan.u.pTerm; - assert( pTerm!=0 ); - assert( pTerm->eOperator & WO_OR ); - assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); - pOrWc = &pTerm->u.pOrInfo->wc; - pLevel->op = OP_Return; - pLevel->p1 = regReturn; - - /* Set up a new SrcList in pOrTab containing the table being scanned - ** by this loop in the a[0] slot and all notReady tables in a[1..] slots. - ** This becomes the SrcList in the recursive call to sqlite3WhereBegin(). - */ - if( pWInfo->nLevel>1 ){ - int nNotReady; /* The number of notReady tables */ - struct SrcList_item *origSrc; /* Original list of tables */ - nNotReady = pWInfo->nLevel - iLevel - 1; - pOrTab = sqlite3StackAllocRaw(pParse->db, - sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); - if( pOrTab==0 ) return notReady; - pOrTab->nAlloc = (i16)(nNotReady + 1); - pOrTab->nSrc = pOrTab->nAlloc; - memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); - origSrc = pWInfo->pTabList->a; - for(k=1; k<=nNotReady; k++){ - memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k])); - } - }else{ - pOrTab = pWInfo->pTabList; - } - - /* Initialize the rowset register to contain NULL. An SQL NULL is - ** equivalent to an empty rowset. - ** - ** Also initialize regReturn to contain the address of the instruction - ** immediately following the OP_Return at the bottom of the loop. This - ** is required in a few obscure LEFT JOIN cases where control jumps - ** over the top of the loop into the body of it. In this case the - ** correct response for the end-of-loop code (the OP_Return) is to - ** fall through to the next instruction, just as an OP_Next does if - ** called on an uninitialized cursor. - */ - if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ - regRowset = ++pParse->nMem; - regRowid = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset); - } - iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn); - - /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y - ** Then for every term xN, evaluate as the subexpression: xN AND z - ** That way, terms in y that are factored into the disjunction will - ** be picked up by the recursive calls to sqlite3WhereBegin() below. - ** - ** Actually, each subexpression is converted to "xN AND w" where w is - ** the "interesting" terms of z - terms that did not originate in the - ** ON or USING clause of a LEFT JOIN, and terms that are usable as - ** indices. - */ - if( pWC->nTerm>1 ){ - int iTerm; - for(iTerm=0; iTermnTerm; iTerm++){ - Expr *pExpr = pWC->a[iTerm].pExpr; - if( ExprHasProperty(pExpr, EP_FromJoin) ) continue; - if( pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_ORINFO) ) continue; - if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; - pExpr = sqlite3ExprDup(pParse->db, pExpr, 0); - pAndExpr = sqlite3ExprAnd(pParse->db, pAndExpr, pExpr); - } - if( pAndExpr ){ - pAndExpr = sqlite3PExpr(pParse, TK_AND, 0, pAndExpr, 0); - } - } - - for(ii=0; iinTerm; ii++){ - WhereTerm *pOrTerm = &pOrWc->a[ii]; - if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ - WhereInfo *pSubWInfo; /* Info for single OR-term scan */ - Expr *pOrExpr = pOrTerm->pExpr; - if( pAndExpr ){ - pAndExpr->pLeft = pOrExpr; - pOrExpr = pAndExpr; - } - /* Loop through table entries that match term pOrTerm. */ - pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, - WHERE_OMIT_OPEN_CLOSE | WHERE_AND_ONLY | - WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY, iCovCur); - assert( pSubWInfo || pParse->nErr || pParse->db->mallocFailed ); - if( pSubWInfo ){ - WhereLevel *pLvl; - explainOneScan( - pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0 - ); - if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ - int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); - int r; - r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur, - regRowid, 0); - sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, - sqlite3VdbeCurrentAddr(v)+2, r, iSet); - } - sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody); - - /* The pSubWInfo->untestedTerms flag means that this OR term - ** contained one or more AND term from a notReady table. The - ** terms from the notReady table could not be tested and will - ** need to be tested later. - */ - if( pSubWInfo->untestedTerms ) untestedTerms = 1; - - /* If all of the OR-connected terms are optimized using the same - ** index, and the index is opened using the same cursor number - ** by each call to sqlite3WhereBegin() made by this loop, it may - ** be possible to use that index as a covering index. - ** - ** If the call to sqlite3WhereBegin() above resulted in a scan that - ** uses an index, and this is either the first OR-connected term - ** processed or the index is the same as that used by all previous - ** terms, set pCov to the candidate covering index. Otherwise, set - ** pCov to NULL to indicate that no candidate covering index will - ** be available. - */ - pLvl = &pSubWInfo->a[0]; - if( (pLvl->plan.wsFlags & WHERE_INDEXED)!=0 - && (pLvl->plan.wsFlags & WHERE_TEMP_INDEX)==0 - && (ii==0 || pLvl->plan.u.pIdx==pCov) - ){ - assert( pLvl->iIdxCur==iCovCur ); - pCov = pLvl->plan.u.pIdx; - }else{ - pCov = 0; - } - - /* Finish the loop through table entries that match term pOrTerm. */ - sqlite3WhereEnd(pSubWInfo); - } - } - } - pLevel->u.pCovidx = pCov; - if( pCov ) pLevel->iIdxCur = iCovCur; - if( pAndExpr ){ - pAndExpr->pLeft = 0; - sqlite3ExprDelete(pParse->db, pAndExpr); - } - sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v)); - sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk); - sqlite3VdbeResolveLabel(v, iLoopBody); - - if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab); - if( !untestedTerms ) disableTerm(pLevel, pTerm); - }else -#endif /* SQLITE_OMIT_OR_OPTIMIZATION */ - - { - /* Case 5: There is no usable index. We must do a complete - ** scan of the entire table. - */ - static const u8 aStep[] = { OP_Next, OP_Prev }; - static const u8 aStart[] = { OP_Rewind, OP_Last }; - assert( bRev==0 || bRev==1 ); - assert( omitTable==0 ); - pLevel->op = aStep[bRev]; - pLevel->p1 = iCur; - pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); - pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; - } - notReady &= ~getMask(pWC->pMaskSet, iCur); - - /* Insert code to test every subexpression that can be completely - ** computed using the current set of tables. - ** - ** IMPLEMENTATION-OF: R-49525-50935 Terms that cannot be satisfied through - ** the use of indices become tests that are evaluated against each row of - ** the relevant input tables. - */ - for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ - Expr *pE; - testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */ - testcase( pTerm->wtFlags & TERM_CODED ); - if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->prereqAll & notReady)!=0 ){ - testcase( pWInfo->untestedTerms==0 - && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ); - pWInfo->untestedTerms = 1; - continue; - } - pE = pTerm->pExpr; - assert( pE!=0 ); - if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){ - continue; - } - sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); - pTerm->wtFlags |= TERM_CODED; - } - - /* For a LEFT OUTER JOIN, generate code that will record the fact that - ** at least one row of the right table has matched the left table. - */ - if( pLevel->iLeftJoin ){ - pLevel->addrFirst = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin); - VdbeComment((v, "record LEFT JOIN hit")); - sqlite3ExprCacheClear(pParse); - for(pTerm=pWC->a, j=0; jnTerm; j++, pTerm++){ - testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */ - testcase( pTerm->wtFlags & TERM_CODED ); - if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->prereqAll & notReady)!=0 ){ - assert( pWInfo->untestedTerms ); - continue; - } - assert( pTerm->pExpr ); - sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); - pTerm->wtFlags |= TERM_CODED; - } - } - sqlite3ReleaseTempReg(pParse, iReleaseReg); - - return notReady; -} - -#if defined(SQLITE_TEST) -/* -** The following variable holds a text description of query plan generated -** by the most recent call to sqlite3WhereBegin(). Each call to WhereBegin -** overwrites the previous. This information is used for testing and -** analysis only. -*/ -SQLITE_API char sqlite3_query_plan[BMS*2*40]; /* Text of the join */ -static int nQPlan = 0; /* Next free slow in _query_plan[] */ - -#endif /* SQLITE_TEST */ - - -/* -** Free a WhereInfo structure -*/ -static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ - if( ALWAYS(pWInfo) ){ - int i; - for(i=0; inLevel; i++){ - sqlite3_index_info *pInfo = pWInfo->a[i].pIdxInfo; - if( pInfo ){ - /* assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); */ - if( pInfo->needToFreeIdxStr ){ - sqlite3_free(pInfo->idxStr); - } - sqlite3DbFree(db, pInfo); - } - if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){ - Index *pIdx = pWInfo->a[i].plan.u.pIdx; - if( pIdx ){ - sqlite3DbFree(db, pIdx->zColAff); - sqlite3DbFree(db, pIdx); - } - } - } - whereClauseClear(pWInfo->pWC); - sqlite3DbFree(db, pWInfo); - } -} - - -/* -** Generate the beginning of the loop used for WHERE clause processing. -** The return value is a pointer to an opaque structure that contains -** information needed to terminate the loop. Later, the calling routine -** should invoke sqlite3WhereEnd() with the return value of this function -** in order to complete the WHERE clause processing. -** -** If an error occurs, this routine returns NULL. -** -** The basic idea is to do a nested loop, one loop for each table in -** the FROM clause of a select. (INSERT and UPDATE statements are the -** same as a SELECT with only a single table in the FROM clause.) For -** example, if the SQL is this: -** -** SELECT * FROM t1, t2, t3 WHERE ...; -** -** Then the code generated is conceptually like the following: -** -** foreach row1 in t1 do \ Code generated -** foreach row2 in t2 do |-- by sqlite3WhereBegin() -** foreach row3 in t3 do / -** ... -** end \ Code generated -** end |-- by sqlite3WhereEnd() -** end / -** -** Note that the loops might not be nested in the order in which they -** appear in the FROM clause if a different order is better able to make -** use of indices. Note also that when the IN operator appears in -** the WHERE clause, it might result in additional nested loops for -** scanning through all values on the right-hand side of the IN. -** -** There are Btree cursors associated with each table. t1 uses cursor -** number pTabList->a[0].iCursor. t2 uses the cursor pTabList->a[1].iCursor. -** And so forth. This routine generates code to open those VDBE cursors -** and sqlite3WhereEnd() generates the code to close them. -** -** The code that sqlite3WhereBegin() generates leaves the cursors named -** in pTabList pointing at their appropriate entries. The [...] code -** can use OP_Column and OP_Rowid opcodes on these cursors to extract -** data from the various tables of the loop. -** -** If the WHERE clause is empty, the foreach loops must each scan their -** entire tables. Thus a three-way join is an O(N^3) operation. But if -** the tables have indices and there are terms in the WHERE clause that -** refer to those indices, a complete table scan can be avoided and the -** code will run much faster. Most of the work of this routine is checking -** to see if there are indices that can be used to speed up the loop. -** -** Terms of the WHERE clause are also used to limit which rows actually -** make it to the "..." in the middle of the loop. After each "foreach", -** terms of the WHERE clause that use only terms in that loop and outer -** loops are evaluated and if false a jump is made around all subsequent -** inner loops (or around the "..." if the test occurs within the inner- -** most loop) -** -** OUTER JOINS -** -** An outer join of tables t1 and t2 is conceptally coded as follows: -** -** foreach row1 in t1 do -** flag = 0 -** foreach row2 in t2 do -** start: -** ... -** flag = 1 -** end -** if flag==0 then -** move the row2 cursor to a null row -** goto start -** fi -** end -** -** ORDER BY CLAUSE PROCESSING -** -** pOrderBy is a pointer to the ORDER BY clause of a SELECT statement, -** if there is one. If there is no ORDER BY clause or if this routine -** is called from an UPDATE or DELETE statement, then pOrderBy is NULL. -** -** If an index can be used so that the natural output order of the table -** scan is correct for the ORDER BY clause, then that index is used and -** the returned WhereInfo.nOBSat field is set to pOrderBy->nExpr. This -** is an optimization that prevents an unnecessary sort of the result set -** if an index appropriate for the ORDER BY clause already exists. -** -** If the where clause loops cannot be arranged to provide the correct -** output order, then WhereInfo.nOBSat is 0. -*/ -SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - Parse *pParse, /* The parser context */ - SrcList *pTabList, /* A list of all tables to be scanned */ - Expr *pWhere, /* The WHERE clause */ - ExprList *pOrderBy, /* An ORDER BY clause, or NULL */ - ExprList *pDistinct, /* The select-list for DISTINCT queries - or NULL */ - u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */ - int iIdxCur /* If WHERE_ONETABLE_ONLY is set, index cursor number */ -){ - int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ - int nTabList; /* Number of elements in pTabList */ - WhereInfo *pWInfo; /* Will become the return value of this function */ - Vdbe *v = pParse->pVdbe; /* The virtual database engine */ - Bitmask notReady; /* Cursors that are not yet positioned */ - WhereBestIdx sWBI; /* Best index search context */ - WhereMaskSet *pMaskSet; /* The expression mask set */ - WhereLevel *pLevel; /* A single level in pWInfo->a[] */ - int iFrom; /* First unused FROM clause element */ - int andFlags; /* AND-ed combination of all pWC->a[].wtFlags */ - int ii; /* Loop counter */ - sqlite3 *db; /* Database connection */ - - - /* Variable initialization */ - memset(&sWBI, 0, sizeof(sWBI)); - sWBI.pParse = pParse; - - /* The number of tables in the FROM clause is limited by the number of - ** bits in a Bitmask - */ - testcase( pTabList->nSrc==BMS ); - if( pTabList->nSrc>BMS ){ - sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS); - return 0; - } - - /* This function normally generates a nested loop for all tables in - ** pTabList. But if the WHERE_ONETABLE_ONLY flag is set, then we should - ** only generate code for the first table in pTabList and assume that - ** any cursors associated with subsequent tables are uninitialized. - */ - nTabList = (wctrlFlags & WHERE_ONETABLE_ONLY) ? 1 : pTabList->nSrc; - - /* Allocate and initialize the WhereInfo structure that will become the - ** return value. A single allocation is used to store the WhereInfo - ** struct, the contents of WhereInfo.a[], the WhereClause structure - ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte - ** field (type Bitmask) it must be aligned on an 8-byte boundary on - ** some architectures. Hence the ROUND8() below. - */ - db = pParse->db; - nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); - pWInfo = sqlite3DbMallocZero(db, - nByteWInfo + - sizeof(WhereClause) + - sizeof(WhereMaskSet) - ); - if( db->mallocFailed ){ - sqlite3DbFree(db, pWInfo); - pWInfo = 0; - goto whereBeginError; - } - pWInfo->nLevel = nTabList; - pWInfo->pParse = pParse; - pWInfo->pTabList = pTabList; - pWInfo->iBreak = sqlite3VdbeMakeLabel(v); - pWInfo->pWC = sWBI.pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo]; - pWInfo->wctrlFlags = wctrlFlags; - pWInfo->savedNQueryLoop = pParse->nQueryLoop; - pMaskSet = (WhereMaskSet*)&sWBI.pWC[1]; - sWBI.aLevel = pWInfo->a; - - /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via - ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ - if( OptimizationDisabled(db, SQLITE_DistinctOpt) ) pDistinct = 0; - - /* Split the WHERE clause into separate subexpressions where each - ** subexpression is separated by an AND operator. - */ - initMaskSet(pMaskSet); - whereClauseInit(sWBI.pWC, pParse, pMaskSet, wctrlFlags); - sqlite3ExprCodeConstants(pParse, pWhere); - whereSplit(sWBI.pWC, pWhere, TK_AND); /* IMP: R-15842-53296 */ - - /* Special case: a WHERE clause that is constant. Evaluate the - ** expression and either jump over all of the code or fall thru. - */ - if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){ - sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL); - pWhere = 0; - } - - /* Assign a bit from the bitmask to every term in the FROM clause. - ** - ** When assigning bitmask values to FROM clause cursors, it must be - ** the case that if X is the bitmask for the N-th FROM clause term then - ** the bitmask for all FROM clause terms to the left of the N-th term - ** is (X-1). An expression from the ON clause of a LEFT JOIN can use - ** its Expr.iRightJoinTable value to find the bitmask of the right table - ** of the join. Subtracting one from the right table bitmask gives a - ** bitmask for all tables to the left of the join. Knowing the bitmask - ** for all tables to the left of a left join is important. Ticket #3015. - ** - ** Note that bitmasks are created for all pTabList->nSrc tables in - ** pTabList, not just the first nTabList tables. nTabList is normally - ** equal to pTabList->nSrc but might be shortened to 1 if the - ** WHERE_ONETABLE_ONLY flag is set. - */ - for(ii=0; iinSrc; ii++){ - createMask(pMaskSet, pTabList->a[ii].iCursor); - } -#ifndef NDEBUG - { - Bitmask toTheLeft = 0; - for(ii=0; iinSrc; ii++){ - Bitmask m = getMask(pMaskSet, pTabList->a[ii].iCursor); - assert( (m-1)==toTheLeft ); - toTheLeft |= m; - } - } -#endif - - /* Analyze all of the subexpressions. Note that exprAnalyze() might - ** add new virtual terms onto the end of the WHERE clause. We do not - ** want to analyze these virtual terms, so start analyzing at the end - ** and work forward so that the added virtual terms are never processed. - */ - exprAnalyzeAll(pTabList, sWBI.pWC); - if( db->mallocFailed ){ - goto whereBeginError; - } - - /* Check if the DISTINCT qualifier, if there is one, is redundant. - ** If it is, then set pDistinct to NULL and WhereInfo.eDistinct to - ** WHERE_DISTINCT_UNIQUE to tell the caller to ignore the DISTINCT. - */ - if( pDistinct && isDistinctRedundant(pParse, pTabList, sWBI.pWC, pDistinct) ){ - pDistinct = 0; - pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; - } - - /* Chose the best index to use for each table in the FROM clause. - ** - ** This loop fills in the following fields: - ** - ** pWInfo->a[].pIdx The index to use for this level of the loop. - ** pWInfo->a[].wsFlags WHERE_xxx flags associated with pIdx - ** pWInfo->a[].nEq The number of == and IN constraints - ** pWInfo->a[].iFrom Which term of the FROM clause is being coded - ** pWInfo->a[].iTabCur The VDBE cursor for the database table - ** pWInfo->a[].iIdxCur The VDBE cursor for the index - ** pWInfo->a[].pTerm When wsFlags==WO_OR, the OR-clause term - ** - ** This loop also figures out the nesting order of tables in the FROM - ** clause. - */ - sWBI.notValid = ~(Bitmask)0; - sWBI.pOrderBy = pOrderBy; - sWBI.n = nTabList; - sWBI.pDistinct = pDistinct; - andFlags = ~0; - WHERETRACE(("*** Optimizer Start ***\n")); - for(sWBI.i=iFrom=0, pLevel=pWInfo->a; sWBI.ia[j]; jiCursor); - if( (m & sWBI.notValid)==0 ){ - if( j==iFrom ) iFrom++; - continue; - } - if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break; - if( ++ckOptimal ) break; - if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; - } - } - assert( ckOptimal==0 || ckOptimal==1 ); - - for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){ - for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; jiFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){ - /* This break and one like it in the ckOptimal computation loop - ** above prevent table reordering across LEFT and CROSS JOINs. - ** The LEFT JOIN case is necessary for correctness. The prohibition - ** against reordering across a CROSS JOIN is an SQLite feature that - ** allows the developer to control table reordering */ - break; - } - m = getMask(pMaskSet, sWBI.pSrc->iCursor); - if( (m & sWBI.notValid)==0 ){ - assert( j>iFrom ); - continue; - } - sWBI.notReady = (isOptimal ? m : sWBI.notValid); - if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; - - WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", - j, sWBI.pSrc->pTab->zName, isOptimal)); - assert( sWBI.pSrc->pTab ); -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(sWBI.pSrc->pTab) ){ - sWBI.ppIdxInfo = &pWInfo->a[j].pIdxInfo; - bestVirtualIndex(&sWBI); - }else -#endif - { - bestBtreeIndex(&sWBI); - } - assert( isOptimal || (sWBI.cost.used&sWBI.notValid)==0 ); - - /* If an INDEXED BY clause is present, then the plan must use that - ** index if it uses any index at all */ - assert( sWBI.pSrc->pIndex==0 - || (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 - || sWBI.cost.plan.u.pIdx==sWBI.pSrc->pIndex ); - - if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ - notIndexed |= m; - } - if( isOptimal ){ - pWInfo->a[j].rOptCost = sWBI.cost.rCost; - }else if( ckOptimal ){ - /* If two or more tables have nearly the same outer loop cost, but - ** very different inner loop (optimal) cost, we want to choose - ** for the outer loop that table which benefits the least from - ** being in the inner loop. The following code scales the - ** outer loop cost estimate to accomplish that. */ - WHERETRACE((" scaling cost from %.1f to %.1f\n", - sWBI.cost.rCost, - sWBI.cost.rCost/pWInfo->a[j].rOptCost)); - sWBI.cost.rCost /= pWInfo->a[j].rOptCost; - } - - /* Conditions under which this table becomes the best so far: - ** - ** (1) The table must not depend on other tables that have not - ** yet run. (In other words, it must not depend on tables - ** in inner loops.) - ** - ** (2) (This rule was removed on 2012-11-09. The scaling of the - ** cost using the optimal scan cost made this rule obsolete.) - ** - ** (3) All tables have an INDEXED BY clause or this table lacks an - ** INDEXED BY clause or this table uses the specific - ** index specified by its INDEXED BY clause. This rule ensures - ** that a best-so-far is always selected even if an impossible - ** combination of INDEXED BY clauses are given. The error - ** will be detected and relayed back to the application later. - ** The NEVER() comes about because rule (2) above prevents - ** An indexable full-table-scan from reaching rule (3). - ** - ** (4) The plan cost must be lower than prior plans, where "cost" - ** is defined by the compareCost() function above. - */ - if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */ - && (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */ - || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) - && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */ - ){ - WHERETRACE((" === table %d (%s) is best so far\n" - " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n", - j, sWBI.pSrc->pTab->zName, - sWBI.cost.rCost, sWBI.cost.plan.nRow, - sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); - bestPlan = sWBI.cost; - bestJ = j; - } - - /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that - ** table y (and not table z) is always the next inner loop inside - ** of table x. */ - if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; - } - } - assert( bestJ>=0 ); - assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); - assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 ); - testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); - testcase( bestJ>iFrom && bestJa[bestJ+1].jointype & JT_LEFT)!=0 ); - WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" - " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", - bestJ, pTabList->a[bestJ].pTab->zName, - pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, - bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); - if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){ - assert( pWInfo->eDistinct==0 ); - pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; - } - andFlags &= bestPlan.plan.wsFlags; - pLevel->plan = bestPlan.plan; - pLevel->iTabCur = pTabList->a[bestJ].iCursor; - testcase( bestPlan.plan.wsFlags & WHERE_INDEXED ); - testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX ); - if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){ - if( (wctrlFlags & WHERE_ONETABLE_ONLY) - && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0 - ){ - pLevel->iIdxCur = iIdxCur; - }else{ - pLevel->iIdxCur = pParse->nTab++; - } - }else{ - pLevel->iIdxCur = -1; - } - sWBI.notValid &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor); - pLevel->iFrom = (u8)bestJ; - if( bestPlan.plan.nRow>=(double)1 ){ - pParse->nQueryLoop *= bestPlan.plan.nRow; - } - - /* Check that if the table scanned by this loop iteration had an - ** INDEXED BY clause attached to it, that the named index is being - ** used for the scan. If not, then query compilation has failed. - ** Return an error. - */ - pIdx = pTabList->a[bestJ].pIndex; - if( pIdx ){ - if( (bestPlan.plan.wsFlags & WHERE_INDEXED)==0 ){ - sqlite3ErrorMsg(pParse, "cannot use index: %s", pIdx->zName); - goto whereBeginError; - }else{ - /* If an INDEXED BY clause is used, the bestIndex() function is - ** guaranteed to find the index specified in the INDEXED BY clause - ** if it find an index at all. */ - assert( bestPlan.plan.u.pIdx==pIdx ); - } - } - } - WHERETRACE(("*** Optimizer Finished ***\n")); - if( pParse->nErr || db->mallocFailed ){ - goto whereBeginError; - } - if( nTabList ){ - pLevel--; - pWInfo->nOBSat = pLevel->plan.nOBSat; - }else{ - pWInfo->nOBSat = 0; - } - - /* If the total query only selects a single row, then the ORDER BY - ** clause is irrelevant. - */ - if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){ - assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ); - pWInfo->nOBSat = pOrderBy->nExpr; - } - - /* If the caller is an UPDATE or DELETE statement that is requesting - ** to use a one-pass algorithm, determine if this is appropriate. - ** The one-pass algorithm only works if the WHERE clause constraints - ** the statement to update a single row. - */ - assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 ); - if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 && (andFlags & WHERE_UNIQUE)!=0 ){ - pWInfo->okOnePass = 1; - pWInfo->a[0].plan.wsFlags &= ~WHERE_IDX_ONLY; - } - - /* Open all tables in the pTabList and any indices selected for - ** searching those tables. - */ - sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ - notReady = ~(Bitmask)0; - pWInfo->nRowOut = (double)1; - for(ii=0, pLevel=pWInfo->a; iia[pLevel->iFrom]; - pTab = pTabItem->pTab; - pWInfo->nRowOut *= pLevel->plan.nRow; - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ - /* Do nothing */ - }else -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ - const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); - int iCur = pTabItem->iCursor; - sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB); - }else if( IsVirtual(pTab) ){ - /* noop */ - }else -#endif - if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 - && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ - int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead; - sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); - testcase( pTab->nCol==BMS-1 ); - testcase( pTab->nCol==BMS ); - if( !pWInfo->okOnePass && pTab->nColcolUsed; - int n = 0; - for(; b; b=b>>1, n++){} - sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, - SQLITE_INT_TO_PTR(n), P4_INT32); - assert( n<=pTab->nCol ); - } - }else{ - sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - } -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX - if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){ - constructAutomaticIndex(pParse, sWBI.pWC, pTabItem, notReady, pLevel); - }else -#endif - if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ - Index *pIx = pLevel->plan.u.pIdx; - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx); - int iIndexCur = pLevel->iIdxCur; - assert( pIx->pSchema==pTab->pSchema ); - assert( iIndexCur>=0 ); - sqlite3VdbeAddOp4(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pIx->zName)); - } - sqlite3CodeVerifySchema(pParse, iDb); - notReady &= ~getMask(sWBI.pWC->pMaskSet, pTabItem->iCursor); - } - pWInfo->iTop = sqlite3VdbeCurrentAddr(v); - if( db->mallocFailed ) goto whereBeginError; - - /* Generate the code to do the search. Each iteration of the for - ** loop below generates code for a single nested loop of the VM - ** program. - */ - notReady = ~(Bitmask)0; - for(ii=0; iia[ii]; - explainOneScan(pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags); - notReady = codeOneLoopStart(pWInfo, ii, wctrlFlags, notReady); - pWInfo->iContinue = pLevel->addrCont; - } - -#ifdef SQLITE_TEST /* For testing and debugging use only */ - /* Record in the query plan information about the current table - ** and the index used to access it (if any). If the table itself - ** is not used, its name is just '{}'. If no index is used - ** the index is listed as "{}". If the primary key is used the - ** index name is '*'. - */ - for(ii=0; iia[ii]; - w = pLevel->plan.wsFlags; - pTabItem = &pTabList->a[pLevel->iFrom]; - z = pTabItem->zAlias; - if( z==0 ) z = pTabItem->pTab->zName; - n = sqlite3Strlen30(z); - if( n+nQPlan < sizeof(sqlite3_query_plan)-10 ){ - if( (w & WHERE_IDX_ONLY)!=0 && (w & WHERE_COVER_SCAN)==0 ){ - memcpy(&sqlite3_query_plan[nQPlan], "{}", 2); - nQPlan += 2; - }else{ - memcpy(&sqlite3_query_plan[nQPlan], z, n); - nQPlan += n; - } - sqlite3_query_plan[nQPlan++] = ' '; - } - testcase( w & WHERE_ROWID_EQ ); - testcase( w & WHERE_ROWID_RANGE ); - if( w & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ - memcpy(&sqlite3_query_plan[nQPlan], "* ", 2); - nQPlan += 2; - }else if( (w & WHERE_INDEXED)!=0 && (w & WHERE_COVER_SCAN)==0 ){ - n = sqlite3Strlen30(pLevel->plan.u.pIdx->zName); - if( n+nQPlan < sizeof(sqlite3_query_plan)-2 ){ - memcpy(&sqlite3_query_plan[nQPlan], pLevel->plan.u.pIdx->zName, n); - nQPlan += n; - sqlite3_query_plan[nQPlan++] = ' '; - } - }else{ - memcpy(&sqlite3_query_plan[nQPlan], "{} ", 3); - nQPlan += 3; - } - } - while( nQPlan>0 && sqlite3_query_plan[nQPlan-1]==' ' ){ - sqlite3_query_plan[--nQPlan] = 0; - } - sqlite3_query_plan[nQPlan] = 0; - nQPlan = 0; -#endif /* SQLITE_TEST // Testing and debugging use only */ - - /* Record the continuation address in the WhereInfo structure. Then - ** clean up and return. - */ - return pWInfo; - - /* Jump here if malloc fails */ -whereBeginError: - if( pWInfo ){ - pParse->nQueryLoop = pWInfo->savedNQueryLoop; - whereInfoFree(db, pWInfo); - } - return 0; -} - -/* -** Generate the end of the WHERE loop. See comments on -** sqlite3WhereBegin() for additional information. -*/ -SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - Parse *pParse = pWInfo->pParse; - Vdbe *v = pParse->pVdbe; - int i; - WhereLevel *pLevel; - SrcList *pTabList = pWInfo->pTabList; - sqlite3 *db = pParse->db; - - /* Generate loop termination code. - */ - sqlite3ExprCacheClear(pParse); - for(i=pWInfo->nLevel-1; i>=0; i--){ - pLevel = &pWInfo->a[i]; - sqlite3VdbeResolveLabel(v, pLevel->addrCont); - if( pLevel->op!=OP_Noop ){ - sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2); - sqlite3VdbeChangeP5(v, pLevel->p5); - } - if( pLevel->plan.wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ - struct InLoop *pIn; - int j; - sqlite3VdbeResolveLabel(v, pLevel->addrNxt); - for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){ - sqlite3VdbeJumpHere(v, pIn->addrInTop+1); - sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); - sqlite3VdbeJumpHere(v, pIn->addrInTop-1); - } - sqlite3DbFree(db, pLevel->u.in.aInLoop); - } - sqlite3VdbeResolveLabel(v, pLevel->addrBrk); - if( pLevel->iLeftJoin ){ - int addr; - addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); - assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 - || (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ); - if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 ){ - sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor); - } - if( pLevel->iIdxCur>=0 ){ - sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); - } - if( pLevel->op==OP_Return ){ - sqlite3VdbeAddOp2(v, OP_Gosub, pLevel->p1, pLevel->addrFirst); - }else{ - sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrFirst); - } - sqlite3VdbeJumpHere(v, addr); - } - } - - /* The "break" point is here, just past the end of the outer loop. - ** Set it. - */ - sqlite3VdbeResolveLabel(v, pWInfo->iBreak); - - /* Close all of the cursors that were opened by sqlite3WhereBegin. - */ - assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc ); - for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ - Index *pIdx = 0; - struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; - Table *pTab = pTabItem->pTab; - assert( pTab!=0 ); - if( (pTab->tabFlags & TF_Ephemeral)==0 - && pTab->pSelect==0 - && (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 - ){ - int ws = pLevel->plan.wsFlags; - if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ - sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); - } - if( (ws & WHERE_INDEXED)!=0 && (ws & WHERE_TEMP_INDEX)==0 ){ - sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); - } - } - - /* If this scan uses an index, make code substitutions to read data - ** from the index in preference to the table. Sometimes, this means - ** the table need never be read from. This is a performance boost, - ** as the vdbe level waits until the table is read before actually - ** seeking the table cursor to the record corresponding to the current - ** position in the index. - ** - ** Calls to the code generator in between sqlite3WhereBegin and - ** sqlite3WhereEnd will have created code that references the table - ** directly. This loop scans all that code looking for opcodes - ** that reference the table and converts them into opcodes that - ** reference the index. - */ - if( pLevel->plan.wsFlags & WHERE_INDEXED ){ - pIdx = pLevel->plan.u.pIdx; - }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ - pIdx = pLevel->u.pCovidx; - } - if( pIdx && !db->mallocFailed){ - int k, j, last; - VdbeOp *pOp; - - pOp = sqlite3VdbeGetOp(v, pWInfo->iTop); - last = sqlite3VdbeCurrentAddr(v); - for(k=pWInfo->iTop; kp1!=pLevel->iTabCur ) continue; - if( pOp->opcode==OP_Column ){ - for(j=0; jnColumn; j++){ - if( pOp->p2==pIdx->aiColumn[j] ){ - pOp->p2 = j; - pOp->p1 = pLevel->iIdxCur; - break; - } - } - assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 - || jnColumn ); - }else if( pOp->opcode==OP_Rowid ){ - pOp->p1 = pLevel->iIdxCur; - pOp->opcode = OP_IdxRowid; - } - } - } - } - - /* Final cleanup - */ - pParse->nQueryLoop = pWInfo->savedNQueryLoop; - whereInfoFree(db, pWInfo); - return; -} - -/************** End of where.c ***********************************************/ -/************** Begin file parse.c *******************************************/ -/* Driver template for the LEMON parser generator. -** The author disclaims copyright to this source code. -** -** This version of "lempar.c" is modified, slightly, for use by SQLite. -** The only modifications are the addition of a couple of NEVER() -** macros to disable tests that are needed in the case of a general -** LALR(1) grammar but which are always false in the -** specific grammar used by SQLite. -*/ -/* First off, code is included that follows the "include" declaration -** in the input grammar file. */ -/* #include */ - - -/* -** Disable all error recovery processing in the parser push-down -** automaton. -*/ -#define YYNOERRORRECOVERY 1 - -/* -** Make yytestcase() the same as testcase() -*/ -#define yytestcase(X) testcase(X) - -/* -** An instance of this structure holds information about the -** LIMIT clause of a SELECT statement. -*/ -struct LimitVal { - Expr *pLimit; /* The LIMIT expression. NULL if there is no limit */ - Expr *pOffset; /* The OFFSET expression. NULL if there is none */ -}; - -/* -** An instance of this structure is used to store the LIKE, -** GLOB, NOT LIKE, and NOT GLOB operators. -*/ -struct LikeOp { - Token eOperator; /* "like" or "glob" or "regexp" */ - int bNot; /* True if the NOT keyword is present */ -}; - -/* -** An instance of the following structure describes the event of a -** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT, -** TK_DELETE, or TK_INSTEAD. If the event is of the form -** -** UPDATE ON (a,b,c) -** -** Then the "b" IdList records the list "a,b,c". -*/ -struct TrigEvent { int a; IdList * b; }; - -/* -** An instance of this structure holds the ATTACH key and the key type. -*/ -struct AttachKey { int type; Token key; }; - -/* -** One or more VALUES claues -*/ -struct ValueList { - ExprList *pList; - Select *pSelect; -}; - - - /* This is a utility routine used to set the ExprSpan.zStart and - ** ExprSpan.zEnd values of pOut so that the span covers the complete - ** range of text beginning with pStart and going to the end of pEnd. - */ - static void spanSet(ExprSpan *pOut, Token *pStart, Token *pEnd){ - pOut->zStart = pStart->z; - pOut->zEnd = &pEnd->z[pEnd->n]; - } - - /* Construct a new Expr object from a single identifier. Use the - ** new Expr to populate pOut. Set the span of pOut to be the identifier - ** that created the expression. - */ - static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token *pValue){ - pOut->pExpr = sqlite3PExpr(pParse, op, 0, 0, pValue); - pOut->zStart = pValue->z; - pOut->zEnd = &pValue->z[pValue->n]; - } - - /* This routine constructs a binary expression node out of two ExprSpan - ** objects and uses the result to populate a new ExprSpan object. - */ - static void spanBinaryExpr( - ExprSpan *pOut, /* Write the result here */ - Parse *pParse, /* The parsing context. Errors accumulate here */ - int op, /* The binary operation */ - ExprSpan *pLeft, /* The left operand */ - ExprSpan *pRight /* The right operand */ - ){ - pOut->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr, 0); - pOut->zStart = pLeft->zStart; - pOut->zEnd = pRight->zEnd; - } - - /* Construct an expression node for a unary postfix operator - */ - static void spanUnaryPostfix( - ExprSpan *pOut, /* Write the new expression node here */ - Parse *pParse, /* Parsing context to record errors */ - int op, /* The operator */ - ExprSpan *pOperand, /* The operand */ - Token *pPostOp /* The operand token for setting the span */ - ){ - pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0); - pOut->zStart = pOperand->zStart; - pOut->zEnd = &pPostOp->z[pPostOp->n]; - } - - /* A routine to convert a binary TK_IS or TK_ISNOT expression into a - ** unary TK_ISNULL or TK_NOTNULL expression. */ - static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){ - sqlite3 *db = pParse->db; - if( db->mallocFailed==0 && pY->op==TK_NULL ){ - pA->op = (u8)op; - sqlite3ExprDelete(db, pA->pRight); - pA->pRight = 0; - } - } - - /* Construct an expression node for a unary prefix operator - */ - static void spanUnaryPrefix( - ExprSpan *pOut, /* Write the new expression node here */ - Parse *pParse, /* Parsing context to record errors */ - int op, /* The operator */ - ExprSpan *pOperand, /* The operand */ - Token *pPreOp /* The operand token for setting the span */ - ){ - pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0); - pOut->zStart = pPreOp->z; - pOut->zEnd = pOperand->zEnd; - } -/* Next is all token values, in a form suitable for use by makeheaders. -** This section will be null unless lemon is run with the -m switch. -*/ -/* -** These constants (all generated automatically by the parser generator) -** specify the various kinds of tokens (terminals) that the parser -** understands. -** -** Each symbol here is a terminal symbol in the grammar. -*/ -/* Make sure the INTERFACE macro is defined. -*/ -#ifndef INTERFACE -# define INTERFACE 1 -#endif -/* The next thing included is series of defines which control -** various aspects of the generated parser. -** YYCODETYPE is the data type used for storing terminal -** and nonterminal numbers. "unsigned char" is -** used if there are fewer than 250 terminals -** and nonterminals. "int" is used otherwise. -** YYNOCODE is a number of type YYCODETYPE which corresponds -** to no legal terminal or nonterminal number. This -** number is used to fill in empty slots of the hash -** table. -** YYFALLBACK If defined, this indicates that one or more tokens -** have fall-back values which should be used if the -** original value of the token will not parse. -** YYACTIONTYPE is the data type used for storing terminal -** and nonterminal numbers. "unsigned char" is -** used if there are fewer than 250 rules and -** states combined. "int" is used otherwise. -** sqlite3ParserTOKENTYPE is the data type used for minor tokens given -** directly to the parser from the tokenizer. -** YYMINORTYPE is the data type used for all minor tokens. -** This is typically a union of many types, one of -** which is sqlite3ParserTOKENTYPE. The entry in the union -** for base tokens is called "yy0". -** YYSTACKDEPTH is the maximum depth of the parser's stack. If -** zero the stack is dynamically sized using realloc() -** sqlite3ParserARG_SDECL A static variable declaration for the %extra_argument -** sqlite3ParserARG_PDECL A parameter declaration for the %extra_argument -** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser -** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser -** YYNSTATE the combined number of states. -** YYNRULE the number of rules in the grammar -** YYERRORSYMBOL is the code number of the error symbol. If not -** defined, then do no error processing. -*/ -#define YYCODETYPE unsigned char -#define YYNOCODE 251 -#define YYACTIONTYPE unsigned short int -#define YYWILDCARD 67 -#define sqlite3ParserTOKENTYPE Token -typedef union { - int yyinit; - sqlite3ParserTOKENTYPE yy0; - struct LimitVal yy64; - Expr* yy122; - Select* yy159; - IdList* yy180; - struct {int value; int mask;} yy207; - u8 yy258; - u16 yy305; - struct LikeOp yy318; - TriggerStep* yy327; - ExprSpan yy342; - SrcList* yy347; - int yy392; - struct TrigEvent yy410; - ExprList* yy442; - struct ValueList yy487; -} YYMINORTYPE; -#ifndef YYSTACKDEPTH -#define YYSTACKDEPTH 100 -#endif -#define sqlite3ParserARG_SDECL Parse *pParse; -#define sqlite3ParserARG_PDECL ,Parse *pParse -#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse -#define sqlite3ParserARG_STORE yypParser->pParse = pParse -#define YYNSTATE 627 -#define YYNRULE 327 -#define YYFALLBACK 1 -#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) -#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) -#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) - -/* The yyzerominor constant is used to initialize instances of -** YYMINORTYPE objects to zero. */ -static const YYMINORTYPE yyzerominor = { 0 }; - -/* Define the yytestcase() macro to be a no-op if is not already defined -** otherwise. -** -** Applications can choose to define yytestcase() in the %include section -** to a macro that can assist in verifying code coverage. For production -** code the yytestcase() macro should be turned off. But it is useful -** for testing. -*/ -#ifndef yytestcase -# define yytestcase(X) -#endif - - -/* Next are the tables used to determine what action to take based on the -** current state and lookahead token. These tables are used to implement -** functions that take a state number and lookahead value and return an -** action integer. -** -** Suppose the action integer is N. Then the action is determined as -** follows -** -** 0 <= N < YYNSTATE Shift N. That is, push the lookahead -** token onto the stack and goto state N. -** -** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. -** -** N == YYNSTATE+YYNRULE A syntax error has occurred. -** -** N == YYNSTATE+YYNRULE+1 The parser accepts its input. -** -** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused -** slots in the yy_action[] table. -** -** The action table is constructed as a single large table named yy_action[]. -** Given state S and lookahead X, the action is computed as -** -** yy_action[ yy_shift_ofst[S] + X ] -** -** If the index value yy_shift_ofst[S]+X is out of range or if the value -** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] -** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table -** and that yy_default[S] should be used instead. -** -** The formula above is for computing the action when the lookahead is -** a terminal symbol. If the lookahead is a non-terminal (as occurs after -** a reduce action) then the yy_reduce_ofst[] array is used in place of -** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of -** YY_SHIFT_USE_DFLT. -** -** The following are the tables generated in this section: -** -** yy_action[] A single table containing all actions. -** yy_lookahead[] A table containing the lookahead for each entry in -** yy_action. Used to detect hash collisions. -** yy_shift_ofst[] For each state, the offset into yy_action for -** shifting terminals. -** yy_reduce_ofst[] For each state, the offset into yy_action for -** shifting non-terminals after a reduce. -** yy_default[] Default action for each state. -*/ -#define YY_ACTTAB_COUNT (1564) -static const YYACTIONTYPE yy_action[] = { - /* 0 */ 309, 955, 184, 417, 2, 171, 624, 594, 56, 56, - /* 10 */ 56, 56, 49, 54, 54, 54, 54, 53, 53, 52, - /* 20 */ 52, 52, 51, 233, 620, 619, 298, 620, 619, 234, - /* 30 */ 587, 581, 56, 56, 56, 56, 19, 54, 54, 54, - /* 40 */ 54, 53, 53, 52, 52, 52, 51, 233, 605, 57, - /* 50 */ 58, 48, 579, 578, 580, 580, 55, 55, 56, 56, - /* 60 */ 56, 56, 541, 54, 54, 54, 54, 53, 53, 52, - /* 70 */ 52, 52, 51, 233, 309, 594, 325, 196, 195, 194, - /* 80 */ 33, 54, 54, 54, 54, 53, 53, 52, 52, 52, - /* 90 */ 51, 233, 617, 616, 165, 617, 616, 380, 377, 376, - /* 100 */ 407, 532, 576, 576, 587, 581, 303, 422, 375, 59, - /* 110 */ 53, 53, 52, 52, 52, 51, 233, 50, 47, 146, - /* 120 */ 574, 545, 65, 57, 58, 48, 579, 578, 580, 580, - /* 130 */ 55, 55, 56, 56, 56, 56, 213, 54, 54, 54, - /* 140 */ 54, 53, 53, 52, 52, 52, 51, 233, 309, 223, - /* 150 */ 539, 420, 170, 176, 138, 280, 383, 275, 382, 168, - /* 160 */ 489, 551, 409, 668, 620, 619, 271, 438, 409, 438, - /* 170 */ 550, 604, 67, 482, 507, 618, 599, 412, 587, 581, - /* 180 */ 600, 483, 618, 412, 618, 598, 91, 439, 440, 439, - /* 190 */ 335, 598, 73, 669, 222, 266, 480, 57, 58, 48, - /* 200 */ 579, 578, 580, 580, 55, 55, 56, 56, 56, 56, - /* 210 */ 670, 54, 54, 54, 54, 53, 53, 52, 52, 52, - /* 220 */ 51, 233, 309, 279, 232, 231, 1, 132, 200, 385, - /* 230 */ 620, 619, 617, 616, 278, 435, 289, 563, 175, 262, - /* 240 */ 409, 264, 437, 497, 436, 166, 441, 568, 336, 568, - /* 250 */ 201, 537, 587, 581, 599, 412, 165, 594, 600, 380, - /* 260 */ 377, 376, 597, 598, 92, 523, 618, 569, 569, 592, - /* 270 */ 375, 57, 58, 48, 579, 578, 580, 580, 55, 55, - /* 280 */ 56, 56, 56, 56, 597, 54, 54, 54, 54, 53, - /* 290 */ 53, 52, 52, 52, 51, 233, 309, 463, 617, 616, - /* 300 */ 590, 590, 590, 174, 272, 396, 409, 272, 409, 548, - /* 310 */ 397, 620, 619, 68, 326, 620, 619, 620, 619, 618, - /* 320 */ 546, 412, 618, 412, 471, 594, 587, 581, 472, 598, - /* 330 */ 92, 598, 92, 52, 52, 52, 51, 233, 513, 512, - /* 340 */ 206, 322, 363, 464, 221, 57, 58, 48, 579, 578, - /* 350 */ 580, 580, 55, 55, 56, 56, 56, 56, 529, 54, - /* 360 */ 54, 54, 54, 53, 53, 52, 52, 52, 51, 233, - /* 370 */ 309, 396, 409, 396, 597, 372, 386, 530, 347, 617, - /* 380 */ 616, 575, 202, 617, 616, 617, 616, 412, 620, 619, - /* 390 */ 145, 255, 346, 254, 577, 598, 74, 351, 45, 489, - /* 400 */ 587, 581, 235, 189, 464, 544, 167, 296, 187, 469, - /* 410 */ 479, 67, 62, 39, 618, 546, 597, 345, 573, 57, - /* 420 */ 58, 48, 579, 578, 580, 580, 55, 55, 56, 56, - /* 430 */ 56, 56, 6, 54, 54, 54, 54, 53, 53, 52, - /* 440 */ 52, 52, 51, 233, 309, 562, 558, 407, 528, 576, - /* 450 */ 576, 344, 255, 346, 254, 182, 617, 616, 503, 504, - /* 460 */ 314, 409, 557, 235, 166, 271, 409, 352, 564, 181, - /* 470 */ 407, 546, 576, 576, 587, 581, 412, 537, 556, 561, - /* 480 */ 517, 412, 618, 249, 598, 16, 7, 36, 467, 598, - /* 490 */ 92, 516, 618, 57, 58, 48, 579, 578, 580, 580, - /* 500 */ 55, 55, 56, 56, 56, 56, 541, 54, 54, 54, - /* 510 */ 54, 53, 53, 52, 52, 52, 51, 233, 309, 327, - /* 520 */ 572, 571, 525, 558, 560, 394, 871, 246, 409, 248, - /* 530 */ 171, 392, 594, 219, 407, 409, 576, 576, 502, 557, - /* 540 */ 364, 145, 510, 412, 407, 229, 576, 576, 587, 581, - /* 550 */ 412, 598, 92, 381, 269, 556, 166, 400, 598, 69, - /* 560 */ 501, 419, 945, 199, 945, 198, 546, 57, 58, 48, - /* 570 */ 579, 578, 580, 580, 55, 55, 56, 56, 56, 56, - /* 580 */ 568, 54, 54, 54, 54, 53, 53, 52, 52, 52, - /* 590 */ 51, 233, 309, 317, 419, 944, 508, 944, 308, 597, - /* 600 */ 594, 565, 490, 212, 173, 247, 423, 615, 614, 613, - /* 610 */ 323, 197, 143, 405, 572, 571, 489, 66, 50, 47, - /* 620 */ 146, 594, 587, 581, 232, 231, 559, 427, 67, 555, - /* 630 */ 15, 618, 186, 543, 303, 421, 35, 206, 432, 423, - /* 640 */ 552, 57, 58, 48, 579, 578, 580, 580, 55, 55, - /* 650 */ 56, 56, 56, 56, 205, 54, 54, 54, 54, 53, - /* 660 */ 53, 52, 52, 52, 51, 233, 309, 569, 569, 260, - /* 670 */ 268, 597, 12, 373, 568, 166, 409, 313, 409, 420, - /* 680 */ 409, 473, 473, 365, 618, 50, 47, 146, 597, 594, - /* 690 */ 468, 412, 166, 412, 351, 412, 587, 581, 32, 598, - /* 700 */ 94, 598, 97, 598, 95, 627, 625, 329, 142, 50, - /* 710 */ 47, 146, 333, 349, 358, 57, 58, 48, 579, 578, - /* 720 */ 580, 580, 55, 55, 56, 56, 56, 56, 409, 54, - /* 730 */ 54, 54, 54, 53, 53, 52, 52, 52, 51, 233, - /* 740 */ 309, 409, 388, 412, 409, 22, 565, 404, 212, 362, - /* 750 */ 389, 598, 104, 359, 409, 156, 412, 409, 603, 412, - /* 760 */ 537, 331, 569, 569, 598, 103, 493, 598, 105, 412, - /* 770 */ 587, 581, 412, 260, 549, 618, 11, 598, 106, 521, - /* 780 */ 598, 133, 169, 457, 456, 170, 35, 601, 618, 57, - /* 790 */ 58, 48, 579, 578, 580, 580, 55, 55, 56, 56, - /* 800 */ 56, 56, 409, 54, 54, 54, 54, 53, 53, 52, - /* 810 */ 52, 52, 51, 233, 309, 409, 259, 412, 409, 50, - /* 820 */ 47, 146, 357, 318, 355, 598, 134, 527, 352, 337, - /* 830 */ 412, 409, 356, 412, 357, 409, 357, 618, 598, 98, - /* 840 */ 129, 598, 102, 618, 587, 581, 412, 21, 235, 618, - /* 850 */ 412, 618, 211, 143, 598, 101, 30, 167, 598, 93, - /* 860 */ 350, 535, 203, 57, 58, 48, 579, 578, 580, 580, - /* 870 */ 55, 55, 56, 56, 56, 56, 409, 54, 54, 54, - /* 880 */ 54, 53, 53, 52, 52, 52, 51, 233, 309, 409, - /* 890 */ 526, 412, 409, 425, 215, 305, 597, 551, 141, 598, - /* 900 */ 100, 40, 409, 38, 412, 409, 550, 412, 409, 228, - /* 910 */ 220, 314, 598, 77, 500, 598, 96, 412, 587, 581, - /* 920 */ 412, 338, 253, 412, 218, 598, 137, 379, 598, 136, - /* 930 */ 28, 598, 135, 270, 715, 210, 481, 57, 58, 48, - /* 940 */ 579, 578, 580, 580, 55, 55, 56, 56, 56, 56, - /* 950 */ 409, 54, 54, 54, 54, 53, 53, 52, 52, 52, - /* 960 */ 51, 233, 309, 409, 272, 412, 409, 315, 147, 597, - /* 970 */ 272, 626, 2, 598, 76, 209, 409, 127, 412, 618, - /* 980 */ 126, 412, 409, 621, 235, 618, 598, 90, 374, 598, - /* 990 */ 89, 412, 587, 581, 27, 260, 350, 412, 618, 598, - /* 1000 */ 75, 321, 541, 541, 125, 598, 88, 320, 278, 597, - /* 1010 */ 618, 57, 46, 48, 579, 578, 580, 580, 55, 55, - /* 1020 */ 56, 56, 56, 56, 409, 54, 54, 54, 54, 53, - /* 1030 */ 53, 52, 52, 52, 51, 233, 309, 409, 450, 412, - /* 1040 */ 164, 284, 282, 272, 609, 424, 304, 598, 87, 370, - /* 1050 */ 409, 477, 412, 409, 608, 409, 607, 602, 618, 618, - /* 1060 */ 598, 99, 586, 585, 122, 412, 587, 581, 412, 618, - /* 1070 */ 412, 618, 618, 598, 86, 366, 598, 17, 598, 85, - /* 1080 */ 319, 185, 519, 518, 583, 582, 58, 48, 579, 578, - /* 1090 */ 580, 580, 55, 55, 56, 56, 56, 56, 409, 54, - /* 1100 */ 54, 54, 54, 53, 53, 52, 52, 52, 51, 233, - /* 1110 */ 309, 584, 409, 412, 409, 260, 260, 260, 408, 591, - /* 1120 */ 474, 598, 84, 170, 409, 466, 518, 412, 121, 412, - /* 1130 */ 618, 618, 618, 618, 618, 598, 83, 598, 72, 412, - /* 1140 */ 587, 581, 51, 233, 625, 329, 470, 598, 71, 257, - /* 1150 */ 159, 120, 14, 462, 157, 158, 117, 260, 448, 447, - /* 1160 */ 446, 48, 579, 578, 580, 580, 55, 55, 56, 56, - /* 1170 */ 56, 56, 618, 54, 54, 54, 54, 53, 53, 52, - /* 1180 */ 52, 52, 51, 233, 44, 403, 260, 3, 409, 459, - /* 1190 */ 260, 413, 619, 118, 398, 10, 25, 24, 554, 348, - /* 1200 */ 217, 618, 406, 412, 409, 618, 4, 44, 403, 618, - /* 1210 */ 3, 598, 82, 618, 413, 619, 455, 542, 115, 412, - /* 1220 */ 538, 401, 536, 274, 506, 406, 251, 598, 81, 216, - /* 1230 */ 273, 563, 618, 243, 453, 618, 154, 618, 618, 618, - /* 1240 */ 449, 416, 623, 110, 401, 618, 409, 236, 64, 123, - /* 1250 */ 487, 41, 42, 531, 563, 204, 409, 267, 43, 411, - /* 1260 */ 410, 412, 265, 592, 108, 618, 107, 434, 332, 598, - /* 1270 */ 80, 412, 618, 263, 41, 42, 443, 618, 409, 598, - /* 1280 */ 70, 43, 411, 410, 433, 261, 592, 149, 618, 597, - /* 1290 */ 256, 237, 188, 412, 590, 590, 590, 589, 588, 13, - /* 1300 */ 618, 598, 18, 328, 235, 618, 44, 403, 360, 3, - /* 1310 */ 418, 461, 339, 413, 619, 227, 124, 590, 590, 590, - /* 1320 */ 589, 588, 13, 618, 406, 409, 618, 409, 139, 34, - /* 1330 */ 403, 387, 3, 148, 622, 312, 413, 619, 311, 330, - /* 1340 */ 412, 460, 412, 401, 180, 353, 412, 406, 598, 79, - /* 1350 */ 598, 78, 250, 563, 598, 9, 618, 612, 611, 610, - /* 1360 */ 618, 8, 452, 442, 242, 415, 401, 618, 239, 235, - /* 1370 */ 179, 238, 428, 41, 42, 288, 563, 618, 618, 618, - /* 1380 */ 43, 411, 410, 618, 144, 592, 618, 618, 177, 61, - /* 1390 */ 618, 596, 391, 620, 619, 287, 41, 42, 414, 618, - /* 1400 */ 293, 30, 393, 43, 411, 410, 292, 618, 592, 31, - /* 1410 */ 618, 395, 291, 60, 230, 37, 590, 590, 590, 589, - /* 1420 */ 588, 13, 214, 553, 183, 290, 172, 301, 300, 299, - /* 1430 */ 178, 297, 595, 563, 451, 29, 285, 390, 540, 590, - /* 1440 */ 590, 590, 589, 588, 13, 283, 520, 534, 150, 533, - /* 1450 */ 241, 281, 384, 192, 191, 324, 515, 514, 276, 240, - /* 1460 */ 510, 523, 307, 511, 128, 592, 509, 225, 226, 486, - /* 1470 */ 485, 224, 152, 491, 464, 306, 484, 163, 153, 371, - /* 1480 */ 478, 151, 162, 258, 369, 161, 367, 208, 475, 476, - /* 1490 */ 26, 160, 465, 140, 361, 131, 590, 590, 590, 116, - /* 1500 */ 119, 454, 343, 155, 114, 342, 113, 112, 445, 111, - /* 1510 */ 130, 109, 431, 316, 426, 430, 23, 429, 20, 606, - /* 1520 */ 190, 507, 255, 341, 244, 63, 294, 593, 310, 570, - /* 1530 */ 277, 402, 354, 235, 567, 496, 495, 492, 494, 302, - /* 1540 */ 458, 378, 286, 245, 566, 5, 252, 547, 193, 444, - /* 1550 */ 233, 340, 207, 524, 368, 505, 334, 522, 499, 399, - /* 1560 */ 295, 498, 956, 488, -}; -static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 19, 142, 143, 144, 145, 24, 1, 26, 77, 78, - /* 10 */ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, - /* 20 */ 89, 90, 91, 92, 26, 27, 15, 26, 27, 197, - /* 30 */ 49, 50, 77, 78, 79, 80, 204, 82, 83, 84, - /* 40 */ 85, 86, 87, 88, 89, 90, 91, 92, 23, 68, - /* 50 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 60 */ 79, 80, 166, 82, 83, 84, 85, 86, 87, 88, - /* 70 */ 89, 90, 91, 92, 19, 94, 19, 105, 106, 107, - /* 80 */ 25, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 90 */ 91, 92, 94, 95, 96, 94, 95, 99, 100, 101, - /* 100 */ 112, 205, 114, 115, 49, 50, 22, 23, 110, 54, - /* 110 */ 86, 87, 88, 89, 90, 91, 92, 221, 222, 223, - /* 120 */ 23, 120, 25, 68, 69, 70, 71, 72, 73, 74, - /* 130 */ 75, 76, 77, 78, 79, 80, 22, 82, 83, 84, - /* 140 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 92, - /* 150 */ 23, 67, 25, 96, 97, 98, 99, 100, 101, 102, - /* 160 */ 150, 32, 150, 118, 26, 27, 109, 150, 150, 150, - /* 170 */ 41, 161, 162, 180, 181, 165, 113, 165, 49, 50, - /* 180 */ 117, 188, 165, 165, 165, 173, 174, 170, 171, 170, - /* 190 */ 171, 173, 174, 118, 184, 16, 186, 68, 69, 70, - /* 200 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - /* 210 */ 118, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 220 */ 91, 92, 19, 98, 86, 87, 22, 24, 160, 88, - /* 230 */ 26, 27, 94, 95, 109, 97, 224, 66, 118, 60, - /* 240 */ 150, 62, 104, 23, 106, 25, 229, 230, 229, 230, - /* 250 */ 160, 150, 49, 50, 113, 165, 96, 26, 117, 99, - /* 260 */ 100, 101, 194, 173, 174, 94, 165, 129, 130, 98, - /* 270 */ 110, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 280 */ 77, 78, 79, 80, 194, 82, 83, 84, 85, 86, - /* 290 */ 87, 88, 89, 90, 91, 92, 19, 11, 94, 95, - /* 300 */ 129, 130, 131, 118, 150, 215, 150, 150, 150, 25, - /* 310 */ 220, 26, 27, 22, 213, 26, 27, 26, 27, 165, - /* 320 */ 25, 165, 165, 165, 30, 94, 49, 50, 34, 173, - /* 330 */ 174, 173, 174, 88, 89, 90, 91, 92, 7, 8, - /* 340 */ 160, 187, 48, 57, 187, 68, 69, 70, 71, 72, - /* 350 */ 73, 74, 75, 76, 77, 78, 79, 80, 23, 82, - /* 360 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - /* 370 */ 19, 215, 150, 215, 194, 19, 220, 88, 220, 94, - /* 380 */ 95, 23, 160, 94, 95, 94, 95, 165, 26, 27, - /* 390 */ 95, 105, 106, 107, 113, 173, 174, 217, 22, 150, - /* 400 */ 49, 50, 116, 119, 57, 120, 50, 158, 22, 21, - /* 410 */ 161, 162, 232, 136, 165, 120, 194, 237, 23, 68, - /* 420 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 430 */ 79, 80, 22, 82, 83, 84, 85, 86, 87, 88, - /* 440 */ 89, 90, 91, 92, 19, 23, 12, 112, 23, 114, - /* 450 */ 115, 63, 105, 106, 107, 23, 94, 95, 97, 98, - /* 460 */ 104, 150, 28, 116, 25, 109, 150, 150, 23, 23, - /* 470 */ 112, 25, 114, 115, 49, 50, 165, 150, 44, 11, - /* 480 */ 46, 165, 165, 16, 173, 174, 76, 136, 100, 173, - /* 490 */ 174, 57, 165, 68, 69, 70, 71, 72, 73, 74, - /* 500 */ 75, 76, 77, 78, 79, 80, 166, 82, 83, 84, - /* 510 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 169, - /* 520 */ 170, 171, 23, 12, 23, 214, 138, 60, 150, 62, - /* 530 */ 24, 215, 26, 216, 112, 150, 114, 115, 36, 28, - /* 540 */ 213, 95, 103, 165, 112, 205, 114, 115, 49, 50, - /* 550 */ 165, 173, 174, 51, 23, 44, 25, 46, 173, 174, - /* 560 */ 58, 22, 23, 22, 25, 160, 120, 68, 69, 70, - /* 570 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - /* 580 */ 230, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 590 */ 91, 92, 19, 215, 22, 23, 23, 25, 163, 194, - /* 600 */ 94, 166, 167, 168, 25, 138, 67, 7, 8, 9, - /* 610 */ 108, 206, 207, 169, 170, 171, 150, 22, 221, 222, - /* 620 */ 223, 26, 49, 50, 86, 87, 23, 161, 162, 23, - /* 630 */ 22, 165, 24, 120, 22, 23, 25, 160, 241, 67, - /* 640 */ 176, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 650 */ 77, 78, 79, 80, 160, 82, 83, 84, 85, 86, - /* 660 */ 87, 88, 89, 90, 91, 92, 19, 129, 130, 150, - /* 670 */ 23, 194, 35, 23, 230, 25, 150, 155, 150, 67, - /* 680 */ 150, 105, 106, 107, 165, 221, 222, 223, 194, 94, - /* 690 */ 23, 165, 25, 165, 217, 165, 49, 50, 25, 173, - /* 700 */ 174, 173, 174, 173, 174, 0, 1, 2, 118, 221, - /* 710 */ 222, 223, 193, 219, 237, 68, 69, 70, 71, 72, - /* 720 */ 73, 74, 75, 76, 77, 78, 79, 80, 150, 82, - /* 730 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - /* 740 */ 19, 150, 19, 165, 150, 24, 166, 167, 168, 227, - /* 750 */ 27, 173, 174, 231, 150, 25, 165, 150, 172, 165, - /* 760 */ 150, 242, 129, 130, 173, 174, 180, 173, 174, 165, - /* 770 */ 49, 50, 165, 150, 176, 165, 35, 173, 174, 165, - /* 780 */ 173, 174, 35, 23, 23, 25, 25, 173, 165, 68, - /* 790 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 800 */ 79, 80, 150, 82, 83, 84, 85, 86, 87, 88, - /* 810 */ 89, 90, 91, 92, 19, 150, 193, 165, 150, 221, - /* 820 */ 222, 223, 150, 213, 19, 173, 174, 23, 150, 97, - /* 830 */ 165, 150, 27, 165, 150, 150, 150, 165, 173, 174, - /* 840 */ 22, 173, 174, 165, 49, 50, 165, 52, 116, 165, - /* 850 */ 165, 165, 206, 207, 173, 174, 126, 50, 173, 174, - /* 860 */ 128, 27, 160, 68, 69, 70, 71, 72, 73, 74, - /* 870 */ 75, 76, 77, 78, 79, 80, 150, 82, 83, 84, - /* 880 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 150, - /* 890 */ 23, 165, 150, 23, 216, 25, 194, 32, 39, 173, - /* 900 */ 174, 135, 150, 137, 165, 150, 41, 165, 150, 52, - /* 910 */ 238, 104, 173, 174, 29, 173, 174, 165, 49, 50, - /* 920 */ 165, 219, 238, 165, 238, 173, 174, 52, 173, 174, - /* 930 */ 22, 173, 174, 23, 23, 160, 25, 68, 69, 70, - /* 940 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - /* 950 */ 150, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 960 */ 91, 92, 19, 150, 150, 165, 150, 245, 246, 194, - /* 970 */ 150, 144, 145, 173, 174, 160, 150, 22, 165, 165, - /* 980 */ 22, 165, 150, 150, 116, 165, 173, 174, 52, 173, - /* 990 */ 174, 165, 49, 50, 22, 150, 128, 165, 165, 173, - /* 1000 */ 174, 187, 166, 166, 22, 173, 174, 187, 109, 194, - /* 1010 */ 165, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 1020 */ 77, 78, 79, 80, 150, 82, 83, 84, 85, 86, - /* 1030 */ 87, 88, 89, 90, 91, 92, 19, 150, 193, 165, - /* 1040 */ 102, 205, 205, 150, 150, 247, 248, 173, 174, 19, - /* 1050 */ 150, 20, 165, 150, 150, 150, 150, 150, 165, 165, - /* 1060 */ 173, 174, 49, 50, 104, 165, 49, 50, 165, 165, - /* 1070 */ 165, 165, 165, 173, 174, 43, 173, 174, 173, 174, - /* 1080 */ 187, 24, 190, 191, 71, 72, 69, 70, 71, 72, - /* 1090 */ 73, 74, 75, 76, 77, 78, 79, 80, 150, 82, - /* 1100 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - /* 1110 */ 19, 98, 150, 165, 150, 150, 150, 150, 150, 150, - /* 1120 */ 59, 173, 174, 25, 150, 190, 191, 165, 53, 165, - /* 1130 */ 165, 165, 165, 165, 165, 173, 174, 173, 174, 165, - /* 1140 */ 49, 50, 91, 92, 1, 2, 53, 173, 174, 138, - /* 1150 */ 104, 22, 5, 1, 35, 118, 127, 150, 193, 193, - /* 1160 */ 193, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 1170 */ 79, 80, 165, 82, 83, 84, 85, 86, 87, 88, - /* 1180 */ 89, 90, 91, 92, 19, 20, 150, 22, 150, 27, - /* 1190 */ 150, 26, 27, 108, 150, 22, 76, 76, 150, 25, - /* 1200 */ 193, 165, 37, 165, 150, 165, 22, 19, 20, 165, - /* 1210 */ 22, 173, 174, 165, 26, 27, 23, 150, 119, 165, - /* 1220 */ 150, 56, 150, 150, 150, 37, 16, 173, 174, 193, - /* 1230 */ 150, 66, 165, 193, 1, 165, 121, 165, 165, 165, - /* 1240 */ 20, 146, 147, 119, 56, 165, 150, 152, 16, 154, - /* 1250 */ 150, 86, 87, 88, 66, 160, 150, 150, 93, 94, - /* 1260 */ 95, 165, 150, 98, 108, 165, 127, 23, 65, 173, - /* 1270 */ 174, 165, 165, 150, 86, 87, 128, 165, 150, 173, - /* 1280 */ 174, 93, 94, 95, 23, 150, 98, 15, 165, 194, - /* 1290 */ 150, 140, 22, 165, 129, 130, 131, 132, 133, 134, - /* 1300 */ 165, 173, 174, 3, 116, 165, 19, 20, 150, 22, - /* 1310 */ 4, 150, 217, 26, 27, 179, 179, 129, 130, 131, - /* 1320 */ 132, 133, 134, 165, 37, 150, 165, 150, 164, 19, - /* 1330 */ 20, 150, 22, 246, 149, 249, 26, 27, 249, 244, - /* 1340 */ 165, 150, 165, 56, 6, 150, 165, 37, 173, 174, - /* 1350 */ 173, 174, 150, 66, 173, 174, 165, 149, 149, 13, - /* 1360 */ 165, 25, 150, 150, 150, 149, 56, 165, 150, 116, - /* 1370 */ 151, 150, 150, 86, 87, 150, 66, 165, 165, 165, - /* 1380 */ 93, 94, 95, 165, 150, 98, 165, 165, 151, 22, - /* 1390 */ 165, 194, 150, 26, 27, 150, 86, 87, 159, 165, - /* 1400 */ 199, 126, 123, 93, 94, 95, 200, 165, 98, 124, - /* 1410 */ 165, 122, 201, 125, 225, 135, 129, 130, 131, 132, - /* 1420 */ 133, 134, 5, 157, 157, 202, 118, 10, 11, 12, - /* 1430 */ 13, 14, 203, 66, 17, 104, 210, 121, 211, 129, - /* 1440 */ 130, 131, 132, 133, 134, 210, 175, 211, 31, 211, - /* 1450 */ 33, 210, 104, 86, 87, 47, 175, 183, 175, 42, - /* 1460 */ 103, 94, 178, 177, 22, 98, 175, 92, 228, 175, - /* 1470 */ 175, 228, 55, 183, 57, 178, 175, 156, 61, 18, - /* 1480 */ 157, 64, 156, 235, 157, 156, 45, 157, 236, 157, - /* 1490 */ 135, 156, 189, 68, 157, 218, 129, 130, 131, 22, - /* 1500 */ 189, 199, 157, 156, 192, 18, 192, 192, 199, 192, - /* 1510 */ 218, 189, 40, 157, 38, 157, 240, 157, 240, 153, - /* 1520 */ 196, 181, 105, 106, 107, 243, 198, 166, 111, 230, - /* 1530 */ 176, 226, 239, 116, 230, 176, 166, 166, 176, 148, - /* 1540 */ 199, 177, 209, 209, 166, 196, 239, 208, 185, 199, - /* 1550 */ 92, 209, 233, 173, 234, 182, 139, 173, 182, 191, - /* 1560 */ 195, 182, 250, 186, -}; -#define YY_SHIFT_USE_DFLT (-70) -#define YY_SHIFT_COUNT (416) -#define YY_SHIFT_MIN (-69) -#define YY_SHIFT_MAX (1487) -static const short yy_shift_ofst[] = { - /* 0 */ 1143, 1188, 1417, 1188, 1287, 1287, 138, 138, -2, -19, - /* 10 */ 1287, 1287, 1287, 1287, 347, 362, 129, 129, 795, 1165, - /* 20 */ 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - /* 30 */ 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - /* 40 */ 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1310, 1287, - /* 50 */ 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - /* 60 */ 1287, 1287, 286, 362, 362, 538, 538, 231, 1253, 55, - /* 70 */ 721, 647, 573, 499, 425, 351, 277, 203, 869, 869, - /* 80 */ 869, 869, 869, 869, 869, 869, 869, 869, 869, 869, - /* 90 */ 869, 869, 869, 943, 869, 1017, 1091, 1091, -69, -45, - /* 100 */ -45, -45, -45, -45, -1, 24, 245, 362, 362, 362, - /* 110 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, - /* 120 */ 362, 362, 362, 388, 356, 362, 362, 362, 362, 362, - /* 130 */ 732, 868, 231, 1051, 1458, -70, -70, -70, 1367, 57, - /* 140 */ 434, 434, 289, 291, 285, 1, 204, 572, 539, 362, - /* 150 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, - /* 160 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, - /* 170 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, - /* 180 */ 362, 506, 506, 506, 705, 1253, 1253, 1253, -70, -70, - /* 190 */ -70, 171, 171, 160, 502, 502, 502, 446, 432, 511, - /* 200 */ 422, 358, 335, -12, -12, -12, -12, 576, 294, -12, - /* 210 */ -12, 295, 595, 141, 600, 730, 723, 723, 805, 730, - /* 220 */ 805, 439, 911, 231, 865, 231, 865, 807, 865, 723, - /* 230 */ 766, 633, 633, 231, 284, 63, 608, 1476, 1308, 1308, - /* 240 */ 1472, 1472, 1308, 1477, 1425, 1275, 1487, 1487, 1487, 1487, - /* 250 */ 1308, 1461, 1275, 1477, 1425, 1425, 1308, 1461, 1355, 1441, - /* 260 */ 1308, 1308, 1461, 1308, 1461, 1308, 1461, 1442, 1348, 1348, - /* 270 */ 1348, 1408, 1375, 1375, 1442, 1348, 1357, 1348, 1408, 1348, - /* 280 */ 1348, 1316, 1331, 1316, 1331, 1316, 1331, 1308, 1308, 1280, - /* 290 */ 1288, 1289, 1285, 1279, 1275, 1253, 1336, 1346, 1346, 1338, - /* 300 */ 1338, 1338, 1338, -70, -70, -70, -70, -70, -70, 1013, - /* 310 */ 467, 612, 84, 179, -28, 870, 410, 761, 760, 667, - /* 320 */ 650, 531, 220, 361, 331, 125, 127, 97, 1306, 1300, - /* 330 */ 1270, 1151, 1272, 1203, 1232, 1261, 1244, 1148, 1174, 1139, - /* 340 */ 1156, 1124, 1220, 1115, 1210, 1233, 1099, 1193, 1184, 1174, - /* 350 */ 1173, 1029, 1121, 1120, 1085, 1162, 1119, 1037, 1152, 1147, - /* 360 */ 1129, 1046, 1011, 1093, 1098, 1075, 1061, 1032, 960, 1057, - /* 370 */ 1031, 1030, 899, 938, 982, 936, 972, 958, 910, 955, - /* 380 */ 875, 885, 908, 857, 859, 867, 804, 590, 834, 747, - /* 390 */ 818, 513, 611, 741, 673, 637, 611, 606, 603, 579, - /* 400 */ 501, 541, 468, 386, 445, 395, 376, 281, 185, 120, - /* 410 */ 92, 75, 45, 114, 25, 11, 5, -}; -#define YY_REDUCE_USE_DFLT (-169) -#define YY_REDUCE_COUNT (308) -#define YY_REDUCE_MIN (-168) -#define YY_REDUCE_MAX (1391) -static const short yy_reduce_ofst[] = { - /* 0 */ -141, 90, 1095, 222, 158, 156, 19, 17, 10, -104, - /* 10 */ 378, 316, 311, 12, 180, 249, 598, 464, 397, 1181, - /* 20 */ 1177, 1175, 1128, 1106, 1096, 1054, 1038, 974, 964, 962, - /* 30 */ 948, 905, 903, 900, 887, 874, 832, 826, 816, 813, - /* 40 */ 800, 758, 755, 752, 742, 739, 726, 685, 681, 668, - /* 50 */ 665, 652, 607, 604, 594, 591, 578, 530, 528, 526, - /* 60 */ 385, 18, 477, 466, 519, 444, 350, 435, 405, 488, - /* 70 */ 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - /* 80 */ 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - /* 90 */ 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - /* 100 */ 488, 488, 488, 488, 488, 488, 488, 1040, 678, 1036, - /* 110 */ 1007, 967, 966, 965, 845, 686, 610, 684, 317, 672, - /* 120 */ 893, 327, 623, 522, -7, 820, 814, 157, 154, 101, - /* 130 */ 702, 494, 580, 488, 488, 488, 488, 488, 614, 586, - /* 140 */ 935, 892, 968, 1245, 1242, 1234, 1225, 798, 798, 1222, - /* 150 */ 1221, 1218, 1214, 1213, 1212, 1202, 1195, 1191, 1161, 1158, - /* 160 */ 1140, 1135, 1123, 1112, 1107, 1100, 1080, 1074, 1073, 1072, - /* 170 */ 1070, 1067, 1048, 1044, 969, 968, 907, 906, 904, 894, - /* 180 */ 833, 837, 836, 340, 827, 815, 775, 68, 722, 646, - /* 190 */ -168, 1384, 1380, 1377, 1379, 1376, 1373, 1339, 1365, 1368, - /* 200 */ 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1320, 1319, 1365, - /* 210 */ 1365, 1339, 1378, 1349, 1391, 1350, 1342, 1334, 1307, 1341, - /* 220 */ 1293, 1364, 1363, 1371, 1362, 1370, 1359, 1340, 1354, 1333, - /* 230 */ 1305, 1304, 1299, 1361, 1328, 1324, 1366, 1282, 1360, 1358, - /* 240 */ 1278, 1276, 1356, 1292, 1322, 1309, 1317, 1315, 1314, 1312, - /* 250 */ 1345, 1347, 1302, 1277, 1311, 1303, 1337, 1335, 1252, 1248, - /* 260 */ 1332, 1330, 1329, 1327, 1326, 1323, 1321, 1297, 1301, 1295, - /* 270 */ 1294, 1290, 1243, 1240, 1284, 1291, 1286, 1283, 1274, 1281, - /* 280 */ 1271, 1238, 1241, 1236, 1235, 1227, 1226, 1267, 1266, 1189, - /* 290 */ 1229, 1223, 1211, 1206, 1201, 1197, 1239, 1237, 1219, 1216, - /* 300 */ 1209, 1208, 1185, 1089, 1086, 1087, 1137, 1136, 1164, -}; -static const YYACTIONTYPE yy_default[] = { - /* 0 */ 632, 866, 954, 954, 866, 866, 954, 954, 954, 756, - /* 10 */ 954, 954, 954, 864, 954, 954, 784, 784, 928, 954, - /* 20 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 30 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 40 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 50 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 60 */ 954, 954, 954, 954, 954, 954, 954, 671, 760, 790, - /* 70 */ 954, 954, 954, 954, 954, 954, 954, 954, 927, 929, - /* 80 */ 798, 797, 907, 771, 795, 788, 792, 867, 860, 861, - /* 90 */ 859, 863, 868, 954, 791, 827, 844, 826, 838, 843, - /* 100 */ 850, 842, 839, 829, 828, 830, 831, 954, 954, 954, - /* 110 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 120 */ 954, 954, 954, 658, 725, 954, 954, 954, 954, 954, - /* 130 */ 954, 954, 954, 832, 833, 847, 846, 845, 954, 663, - /* 140 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 150 */ 934, 932, 954, 879, 954, 954, 954, 954, 954, 954, - /* 160 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 170 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 180 */ 638, 756, 756, 756, 632, 954, 954, 954, 946, 760, - /* 190 */ 750, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 200 */ 954, 954, 954, 800, 739, 917, 919, 954, 900, 737, - /* 210 */ 660, 758, 673, 748, 640, 794, 773, 773, 912, 794, - /* 220 */ 912, 696, 719, 954, 784, 954, 784, 693, 784, 773, - /* 230 */ 862, 954, 954, 954, 757, 748, 954, 939, 764, 764, - /* 240 */ 931, 931, 764, 806, 729, 794, 736, 736, 736, 736, - /* 250 */ 764, 655, 794, 806, 729, 729, 764, 655, 906, 904, - /* 260 */ 764, 764, 655, 764, 655, 764, 655, 872, 727, 727, - /* 270 */ 727, 711, 876, 876, 872, 727, 696, 727, 711, 727, - /* 280 */ 727, 777, 772, 777, 772, 777, 772, 764, 764, 954, - /* 290 */ 789, 778, 787, 785, 794, 954, 714, 648, 648, 637, - /* 300 */ 637, 637, 637, 951, 951, 946, 698, 698, 681, 954, - /* 310 */ 954, 954, 954, 954, 954, 954, 881, 954, 954, 954, - /* 320 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 633, - /* 330 */ 941, 954, 954, 938, 954, 954, 954, 954, 799, 954, - /* 340 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 916, - /* 350 */ 954, 954, 954, 954, 954, 954, 954, 910, 954, 954, - /* 360 */ 954, 954, 954, 954, 903, 902, 954, 954, 954, 954, - /* 370 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 380 */ 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - /* 390 */ 954, 954, 786, 954, 779, 954, 865, 954, 954, 954, - /* 400 */ 954, 954, 954, 954, 954, 954, 954, 742, 815, 954, - /* 410 */ 814, 818, 813, 665, 954, 646, 954, 629, 634, 950, - /* 420 */ 953, 952, 949, 948, 947, 942, 940, 937, 936, 935, - /* 430 */ 933, 930, 926, 885, 883, 890, 889, 888, 887, 886, - /* 440 */ 884, 882, 880, 801, 796, 793, 925, 878, 738, 735, - /* 450 */ 734, 654, 943, 909, 918, 805, 804, 807, 915, 914, - /* 460 */ 913, 911, 908, 895, 803, 802, 730, 870, 869, 657, - /* 470 */ 899, 898, 897, 901, 905, 896, 766, 656, 653, 662, - /* 480 */ 717, 718, 726, 724, 723, 722, 721, 720, 716, 664, - /* 490 */ 672, 710, 695, 694, 875, 877, 874, 873, 703, 702, - /* 500 */ 708, 707, 706, 705, 704, 701, 700, 699, 692, 691, - /* 510 */ 697, 690, 713, 712, 709, 689, 733, 732, 731, 728, - /* 520 */ 688, 687, 686, 818, 685, 684, 824, 823, 811, 854, - /* 530 */ 753, 752, 751, 763, 762, 775, 774, 809, 808, 776, - /* 540 */ 761, 755, 754, 770, 769, 768, 767, 759, 749, 781, - /* 550 */ 783, 782, 780, 856, 765, 853, 924, 923, 922, 921, - /* 560 */ 920, 858, 857, 825, 822, 676, 677, 893, 892, 894, - /* 570 */ 891, 679, 678, 675, 674, 855, 744, 743, 851, 848, - /* 580 */ 840, 836, 852, 849, 841, 837, 835, 834, 820, 819, - /* 590 */ 817, 816, 812, 821, 667, 745, 741, 740, 810, 747, - /* 600 */ 746, 683, 682, 680, 661, 659, 652, 650, 649, 651, - /* 610 */ 647, 645, 644, 643, 642, 641, 670, 669, 668, 666, - /* 620 */ 665, 639, 636, 635, 631, 630, 628, -}; - -/* The next table maps tokens into fallback tokens. If a construct -** like the following: -** -** %fallback ID X Y Z. -** -** appears in the grammar, then ID becomes a fallback token for X, Y, -** and Z. Whenever one of the tokens X, Y, or Z is input to the parser -** but it does not parse, the type of the token is changed to ID and -** the parse is retried before an error is thrown. -*/ -#ifdef YYFALLBACK -static const YYCODETYPE yyFallback[] = { - 0, /* $ => nothing */ - 0, /* SEMI => nothing */ - 26, /* EXPLAIN => ID */ - 26, /* QUERY => ID */ - 26, /* PLAN => ID */ - 26, /* BEGIN => ID */ - 0, /* TRANSACTION => nothing */ - 26, /* DEFERRED => ID */ - 26, /* IMMEDIATE => ID */ - 26, /* EXCLUSIVE => ID */ - 0, /* COMMIT => nothing */ - 26, /* END => ID */ - 26, /* ROLLBACK => ID */ - 26, /* SAVEPOINT => ID */ - 26, /* RELEASE => ID */ - 0, /* TO => nothing */ - 0, /* TABLE => nothing */ - 0, /* CREATE => nothing */ - 26, /* IF => ID */ - 0, /* NOT => nothing */ - 0, /* EXISTS => nothing */ - 26, /* TEMP => ID */ - 0, /* LP => nothing */ - 0, /* RP => nothing */ - 0, /* AS => nothing */ - 0, /* COMMA => nothing */ - 0, /* ID => nothing */ - 0, /* INDEXED => nothing */ - 26, /* ABORT => ID */ - 26, /* ACTION => ID */ - 26, /* AFTER => ID */ - 26, /* ANALYZE => ID */ - 26, /* ASC => ID */ - 26, /* ATTACH => ID */ - 26, /* BEFORE => ID */ - 26, /* BY => ID */ - 26, /* CASCADE => ID */ - 26, /* CAST => ID */ - 26, /* COLUMNKW => ID */ - 26, /* CONFLICT => ID */ - 26, /* DATABASE => ID */ - 26, /* DESC => ID */ - 26, /* DETACH => ID */ - 26, /* EACH => ID */ - 26, /* FAIL => ID */ - 26, /* FOR => ID */ - 26, /* IGNORE => ID */ - 26, /* INITIALLY => ID */ - 26, /* INSTEAD => ID */ - 26, /* LIKE_KW => ID */ - 26, /* MATCH => ID */ - 26, /* NO => ID */ - 26, /* KEY => ID */ - 26, /* OF => ID */ - 26, /* OFFSET => ID */ - 26, /* PRAGMA => ID */ - 26, /* RAISE => ID */ - 26, /* REPLACE => ID */ - 26, /* RESTRICT => ID */ - 26, /* ROW => ID */ - 26, /* TRIGGER => ID */ - 26, /* VACUUM => ID */ - 26, /* VIEW => ID */ - 26, /* VIRTUAL => ID */ - 26, /* REINDEX => ID */ - 26, /* RENAME => ID */ - 26, /* CTIME_KW => ID */ -}; -#endif /* YYFALLBACK */ - -/* The following structure represents a single element of the -** parser's stack. Information stored includes: -** -** + The state number for the parser at this level of the stack. -** -** + The value of the token stored at this level of the stack. -** (In other words, the "major" token.) -** -** + The semantic value stored at this level of the stack. This is -** the information used by the action routines in the grammar. -** It is sometimes called the "minor" token. -*/ -struct yyStackEntry { - YYACTIONTYPE stateno; /* The state-number */ - YYCODETYPE major; /* The major token value. This is the code - ** number for the token at this stack level */ - YYMINORTYPE minor; /* The user-supplied minor token value. This - ** is the value of the token */ -}; -typedef struct yyStackEntry yyStackEntry; - -/* The state of the parser is completely contained in an instance of -** the following structure */ -struct yyParser { - int yyidx; /* Index of top element in stack */ -#ifdef YYTRACKMAXSTACKDEPTH - int yyidxMax; /* Maximum value of yyidx */ -#endif - int yyerrcnt; /* Shifts left before out of the error */ - sqlite3ParserARG_SDECL /* A place to hold %extra_argument */ -#if YYSTACKDEPTH<=0 - int yystksz; /* Current side of the stack */ - yyStackEntry *yystack; /* The parser's stack */ -#else - yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ -#endif -}; -typedef struct yyParser yyParser; - -#ifndef NDEBUG -/* #include */ -static FILE *yyTraceFILE = 0; -static char *yyTracePrompt = 0; -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* -** Turn parser tracing on by giving a stream to which to write the trace -** and a prompt to preface each trace message. Tracing is turned off -** by making either argument NULL -** -** Inputs: -**
      -**
    • A FILE* to which trace output should be written. -** If NULL, then tracing is turned off. -**
    • A prefix string written at the beginning of every -** line of trace output. If NULL, then tracing is -** turned off. -**
    -** -** Outputs: -** None. -*/ -SQLITE_PRIVATE void sqlite3ParserTrace(FILE *TraceFILE, char *zTracePrompt){ - yyTraceFILE = TraceFILE; - yyTracePrompt = zTracePrompt; - if( yyTraceFILE==0 ) yyTracePrompt = 0; - else if( yyTracePrompt==0 ) yyTraceFILE = 0; -} -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* For tracing shifts, the names of all terminals and nonterminals -** are required. The following table supplies these names */ -static const char *const yyTokenName[] = { - "$", "SEMI", "EXPLAIN", "QUERY", - "PLAN", "BEGIN", "TRANSACTION", "DEFERRED", - "IMMEDIATE", "EXCLUSIVE", "COMMIT", "END", - "ROLLBACK", "SAVEPOINT", "RELEASE", "TO", - "TABLE", "CREATE", "IF", "NOT", - "EXISTS", "TEMP", "LP", "RP", - "AS", "COMMA", "ID", "INDEXED", - "ABORT", "ACTION", "AFTER", "ANALYZE", - "ASC", "ATTACH", "BEFORE", "BY", - "CASCADE", "CAST", "COLUMNKW", "CONFLICT", - "DATABASE", "DESC", "DETACH", "EACH", - "FAIL", "FOR", "IGNORE", "INITIALLY", - "INSTEAD", "LIKE_KW", "MATCH", "NO", - "KEY", "OF", "OFFSET", "PRAGMA", - "RAISE", "REPLACE", "RESTRICT", "ROW", - "TRIGGER", "VACUUM", "VIEW", "VIRTUAL", - "REINDEX", "RENAME", "CTIME_KW", "ANY", - "OR", "AND", "IS", "BETWEEN", - "IN", "ISNULL", "NOTNULL", "NE", - "EQ", "GT", "LE", "LT", - "GE", "ESCAPE", "BITAND", "BITOR", - "LSHIFT", "RSHIFT", "PLUS", "MINUS", - "STAR", "SLASH", "REM", "CONCAT", - "COLLATE", "BITNOT", "STRING", "JOIN_KW", - "CONSTRAINT", "DEFAULT", "NULL", "PRIMARY", - "UNIQUE", "CHECK", "REFERENCES", "AUTOINCR", - "ON", "INSERT", "DELETE", "UPDATE", - "SET", "DEFERRABLE", "FOREIGN", "DROP", - "UNION", "ALL", "EXCEPT", "INTERSECT", - "SELECT", "DISTINCT", "DOT", "FROM", - "JOIN", "USING", "ORDER", "GROUP", - "HAVING", "LIMIT", "WHERE", "INTO", - "VALUES", "INTEGER", "FLOAT", "BLOB", - "REGISTER", "VARIABLE", "CASE", "WHEN", - "THEN", "ELSE", "INDEX", "ALTER", - "ADD", "error", "input", "cmdlist", - "ecmd", "explain", "cmdx", "cmd", - "transtype", "trans_opt", "nm", "savepoint_opt", - "create_table", "create_table_args", "createkw", "temp", - "ifnotexists", "dbnm", "columnlist", "conslist_opt", - "select", "column", "columnid", "type", - "carglist", "id", "ids", "typetoken", - "typename", "signed", "plus_num", "minus_num", - "ccons", "term", "expr", "onconf", - "sortorder", "autoinc", "idxlist_opt", "refargs", - "defer_subclause", "refarg", "refact", "init_deferred_pred_opt", - "conslist", "tconscomma", "tcons", "idxlist", - "defer_subclause_opt", "orconf", "resolvetype", "raisetype", - "ifexists", "fullname", "oneselect", "multiselect_op", - "distinct", "selcollist", "from", "where_opt", - "groupby_opt", "having_opt", "orderby_opt", "limit_opt", - "sclp", "as", "seltablist", "stl_prefix", - "joinop", "indexed_opt", "on_opt", "using_opt", - "joinop2", "inscollist", "sortlist", "nexprlist", - "setlist", "insert_cmd", "inscollist_opt", "valuelist", - "exprlist", "likeop", "between_op", "in_op", - "case_operand", "case_exprlist", "case_else", "uniqueflag", - "collate", "nmnum", "number", "trigger_decl", - "trigger_cmd_list", "trigger_time", "trigger_event", "foreach_clause", - "when_clause", "trigger_cmd", "trnm", "tridxby", - "database_kw_opt", "key_opt", "add_column_fullname", "kwcolumn_opt", - "create_vtab", "vtabarglist", "vtabarg", "vtabargtoken", - "lp", "anylist", -}; -#endif /* NDEBUG */ - -#ifndef NDEBUG -/* For tracing reduce actions, the names of all rules are required. -*/ -static const char *const yyRuleName[] = { - /* 0 */ "input ::= cmdlist", - /* 1 */ "cmdlist ::= cmdlist ecmd", - /* 2 */ "cmdlist ::= ecmd", - /* 3 */ "ecmd ::= SEMI", - /* 4 */ "ecmd ::= explain cmdx SEMI", - /* 5 */ "explain ::=", - /* 6 */ "explain ::= EXPLAIN", - /* 7 */ "explain ::= EXPLAIN QUERY PLAN", - /* 8 */ "cmdx ::= cmd", - /* 9 */ "cmd ::= BEGIN transtype trans_opt", - /* 10 */ "trans_opt ::=", - /* 11 */ "trans_opt ::= TRANSACTION", - /* 12 */ "trans_opt ::= TRANSACTION nm", - /* 13 */ "transtype ::=", - /* 14 */ "transtype ::= DEFERRED", - /* 15 */ "transtype ::= IMMEDIATE", - /* 16 */ "transtype ::= EXCLUSIVE", - /* 17 */ "cmd ::= COMMIT trans_opt", - /* 18 */ "cmd ::= END trans_opt", - /* 19 */ "cmd ::= ROLLBACK trans_opt", - /* 20 */ "savepoint_opt ::= SAVEPOINT", - /* 21 */ "savepoint_opt ::=", - /* 22 */ "cmd ::= SAVEPOINT nm", - /* 23 */ "cmd ::= RELEASE savepoint_opt nm", - /* 24 */ "cmd ::= ROLLBACK trans_opt TO savepoint_opt nm", - /* 25 */ "cmd ::= create_table create_table_args", - /* 26 */ "create_table ::= createkw temp TABLE ifnotexists nm dbnm", - /* 27 */ "createkw ::= CREATE", - /* 28 */ "ifnotexists ::=", - /* 29 */ "ifnotexists ::= IF NOT EXISTS", - /* 30 */ "temp ::= TEMP", - /* 31 */ "temp ::=", - /* 32 */ "create_table_args ::= LP columnlist conslist_opt RP", - /* 33 */ "create_table_args ::= AS select", - /* 34 */ "columnlist ::= columnlist COMMA column", - /* 35 */ "columnlist ::= column", - /* 36 */ "column ::= columnid type carglist", - /* 37 */ "columnid ::= nm", - /* 38 */ "id ::= ID", - /* 39 */ "id ::= INDEXED", - /* 40 */ "ids ::= ID|STRING", - /* 41 */ "nm ::= id", - /* 42 */ "nm ::= STRING", - /* 43 */ "nm ::= JOIN_KW", - /* 44 */ "type ::=", - /* 45 */ "type ::= typetoken", - /* 46 */ "typetoken ::= typename", - /* 47 */ "typetoken ::= typename LP signed RP", - /* 48 */ "typetoken ::= typename LP signed COMMA signed RP", - /* 49 */ "typename ::= ids", - /* 50 */ "typename ::= typename ids", - /* 51 */ "signed ::= plus_num", - /* 52 */ "signed ::= minus_num", - /* 53 */ "carglist ::= carglist ccons", - /* 54 */ "carglist ::=", - /* 55 */ "ccons ::= CONSTRAINT nm", - /* 56 */ "ccons ::= DEFAULT term", - /* 57 */ "ccons ::= DEFAULT LP expr RP", - /* 58 */ "ccons ::= DEFAULT PLUS term", - /* 59 */ "ccons ::= DEFAULT MINUS term", - /* 60 */ "ccons ::= DEFAULT id", - /* 61 */ "ccons ::= NULL onconf", - /* 62 */ "ccons ::= NOT NULL onconf", - /* 63 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", - /* 64 */ "ccons ::= UNIQUE onconf", - /* 65 */ "ccons ::= CHECK LP expr RP", - /* 66 */ "ccons ::= REFERENCES nm idxlist_opt refargs", - /* 67 */ "ccons ::= defer_subclause", - /* 68 */ "ccons ::= COLLATE ids", - /* 69 */ "autoinc ::=", - /* 70 */ "autoinc ::= AUTOINCR", - /* 71 */ "refargs ::=", - /* 72 */ "refargs ::= refargs refarg", - /* 73 */ "refarg ::= MATCH nm", - /* 74 */ "refarg ::= ON INSERT refact", - /* 75 */ "refarg ::= ON DELETE refact", - /* 76 */ "refarg ::= ON UPDATE refact", - /* 77 */ "refact ::= SET NULL", - /* 78 */ "refact ::= SET DEFAULT", - /* 79 */ "refact ::= CASCADE", - /* 80 */ "refact ::= RESTRICT", - /* 81 */ "refact ::= NO ACTION", - /* 82 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", - /* 83 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", - /* 84 */ "init_deferred_pred_opt ::=", - /* 85 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", - /* 86 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", - /* 87 */ "conslist_opt ::=", - /* 88 */ "conslist_opt ::= COMMA conslist", - /* 89 */ "conslist ::= conslist tconscomma tcons", - /* 90 */ "conslist ::= tcons", - /* 91 */ "tconscomma ::= COMMA", - /* 92 */ "tconscomma ::=", - /* 93 */ "tcons ::= CONSTRAINT nm", - /* 94 */ "tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf", - /* 95 */ "tcons ::= UNIQUE LP idxlist RP onconf", - /* 96 */ "tcons ::= CHECK LP expr RP onconf", - /* 97 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt", - /* 98 */ "defer_subclause_opt ::=", - /* 99 */ "defer_subclause_opt ::= defer_subclause", - /* 100 */ "onconf ::=", - /* 101 */ "onconf ::= ON CONFLICT resolvetype", - /* 102 */ "orconf ::=", - /* 103 */ "orconf ::= OR resolvetype", - /* 104 */ "resolvetype ::= raisetype", - /* 105 */ "resolvetype ::= IGNORE", - /* 106 */ "resolvetype ::= REPLACE", - /* 107 */ "cmd ::= DROP TABLE ifexists fullname", - /* 108 */ "ifexists ::= IF EXISTS", - /* 109 */ "ifexists ::=", - /* 110 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select", - /* 111 */ "cmd ::= DROP VIEW ifexists fullname", - /* 112 */ "cmd ::= select", - /* 113 */ "select ::= oneselect", - /* 114 */ "select ::= select multiselect_op oneselect", - /* 115 */ "multiselect_op ::= UNION", - /* 116 */ "multiselect_op ::= UNION ALL", - /* 117 */ "multiselect_op ::= EXCEPT|INTERSECT", - /* 118 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", - /* 119 */ "distinct ::= DISTINCT", - /* 120 */ "distinct ::= ALL", - /* 121 */ "distinct ::=", - /* 122 */ "sclp ::= selcollist COMMA", - /* 123 */ "sclp ::=", - /* 124 */ "selcollist ::= sclp expr as", - /* 125 */ "selcollist ::= sclp STAR", - /* 126 */ "selcollist ::= sclp nm DOT STAR", - /* 127 */ "as ::= AS nm", - /* 128 */ "as ::= ids", - /* 129 */ "as ::=", - /* 130 */ "from ::=", - /* 131 */ "from ::= FROM seltablist", - /* 132 */ "stl_prefix ::= seltablist joinop", - /* 133 */ "stl_prefix ::=", - /* 134 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", - /* 135 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", - /* 136 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", - /* 137 */ "dbnm ::=", - /* 138 */ "dbnm ::= DOT nm", - /* 139 */ "fullname ::= nm dbnm", - /* 140 */ "joinop ::= COMMA|JOIN", - /* 141 */ "joinop ::= JOIN_KW JOIN", - /* 142 */ "joinop ::= JOIN_KW nm JOIN", - /* 143 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 144 */ "on_opt ::= ON expr", - /* 145 */ "on_opt ::=", - /* 146 */ "indexed_opt ::=", - /* 147 */ "indexed_opt ::= INDEXED BY nm", - /* 148 */ "indexed_opt ::= NOT INDEXED", - /* 149 */ "using_opt ::= USING LP inscollist RP", - /* 150 */ "using_opt ::=", - /* 151 */ "orderby_opt ::=", - /* 152 */ "orderby_opt ::= ORDER BY sortlist", - /* 153 */ "sortlist ::= sortlist COMMA expr sortorder", - /* 154 */ "sortlist ::= expr sortorder", - /* 155 */ "sortorder ::= ASC", - /* 156 */ "sortorder ::= DESC", - /* 157 */ "sortorder ::=", - /* 158 */ "groupby_opt ::=", - /* 159 */ "groupby_opt ::= GROUP BY nexprlist", - /* 160 */ "having_opt ::=", - /* 161 */ "having_opt ::= HAVING expr", - /* 162 */ "limit_opt ::=", - /* 163 */ "limit_opt ::= LIMIT expr", - /* 164 */ "limit_opt ::= LIMIT expr OFFSET expr", - /* 165 */ "limit_opt ::= LIMIT expr COMMA expr", - /* 166 */ "cmd ::= DELETE FROM fullname indexed_opt where_opt", - /* 167 */ "where_opt ::=", - /* 168 */ "where_opt ::= WHERE expr", - /* 169 */ "cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt", - /* 170 */ "setlist ::= setlist COMMA nm EQ expr", - /* 171 */ "setlist ::= nm EQ expr", - /* 172 */ "cmd ::= insert_cmd INTO fullname inscollist_opt valuelist", - /* 173 */ "cmd ::= insert_cmd INTO fullname inscollist_opt select", - /* 174 */ "cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES", - /* 175 */ "insert_cmd ::= INSERT orconf", - /* 176 */ "insert_cmd ::= REPLACE", - /* 177 */ "valuelist ::= VALUES LP nexprlist RP", - /* 178 */ "valuelist ::= valuelist COMMA LP exprlist RP", - /* 179 */ "inscollist_opt ::=", - /* 180 */ "inscollist_opt ::= LP inscollist RP", - /* 181 */ "inscollist ::= inscollist COMMA nm", - /* 182 */ "inscollist ::= nm", - /* 183 */ "expr ::= term", - /* 184 */ "expr ::= LP expr RP", - /* 185 */ "term ::= NULL", - /* 186 */ "expr ::= id", - /* 187 */ "expr ::= JOIN_KW", - /* 188 */ "expr ::= nm DOT nm", - /* 189 */ "expr ::= nm DOT nm DOT nm", - /* 190 */ "term ::= INTEGER|FLOAT|BLOB", - /* 191 */ "term ::= STRING", - /* 192 */ "expr ::= REGISTER", - /* 193 */ "expr ::= VARIABLE", - /* 194 */ "expr ::= expr COLLATE ids", - /* 195 */ "expr ::= CAST LP expr AS typetoken RP", - /* 196 */ "expr ::= ID LP distinct exprlist RP", - /* 197 */ "expr ::= ID LP STAR RP", - /* 198 */ "term ::= CTIME_KW", - /* 199 */ "expr ::= expr AND expr", - /* 200 */ "expr ::= expr OR expr", - /* 201 */ "expr ::= expr LT|GT|GE|LE expr", - /* 202 */ "expr ::= expr EQ|NE expr", - /* 203 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 204 */ "expr ::= expr PLUS|MINUS expr", - /* 205 */ "expr ::= expr STAR|SLASH|REM expr", - /* 206 */ "expr ::= expr CONCAT expr", - /* 207 */ "likeop ::= LIKE_KW", - /* 208 */ "likeop ::= NOT LIKE_KW", - /* 209 */ "likeop ::= MATCH", - /* 210 */ "likeop ::= NOT MATCH", - /* 211 */ "expr ::= expr likeop expr", - /* 212 */ "expr ::= expr likeop expr ESCAPE expr", - /* 213 */ "expr ::= expr ISNULL|NOTNULL", - /* 214 */ "expr ::= expr NOT NULL", - /* 215 */ "expr ::= expr IS expr", - /* 216 */ "expr ::= expr IS NOT expr", - /* 217 */ "expr ::= NOT expr", - /* 218 */ "expr ::= BITNOT expr", - /* 219 */ "expr ::= MINUS expr", - /* 220 */ "expr ::= PLUS expr", - /* 221 */ "between_op ::= BETWEEN", - /* 222 */ "between_op ::= NOT BETWEEN", - /* 223 */ "expr ::= expr between_op expr AND expr", - /* 224 */ "in_op ::= IN", - /* 225 */ "in_op ::= NOT IN", - /* 226 */ "expr ::= expr in_op LP exprlist RP", - /* 227 */ "expr ::= LP select RP", - /* 228 */ "expr ::= expr in_op LP select RP", - /* 229 */ "expr ::= expr in_op nm dbnm", - /* 230 */ "expr ::= EXISTS LP select RP", - /* 231 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 232 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 233 */ "case_exprlist ::= WHEN expr THEN expr", - /* 234 */ "case_else ::= ELSE expr", - /* 235 */ "case_else ::=", - /* 236 */ "case_operand ::= expr", - /* 237 */ "case_operand ::=", - /* 238 */ "exprlist ::= nexprlist", - /* 239 */ "exprlist ::=", - /* 240 */ "nexprlist ::= nexprlist COMMA expr", - /* 241 */ "nexprlist ::= expr", - /* 242 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP", - /* 243 */ "uniqueflag ::= UNIQUE", - /* 244 */ "uniqueflag ::=", - /* 245 */ "idxlist_opt ::=", - /* 246 */ "idxlist_opt ::= LP idxlist RP", - /* 247 */ "idxlist ::= idxlist COMMA nm collate sortorder", - /* 248 */ "idxlist ::= nm collate sortorder", - /* 249 */ "collate ::=", - /* 250 */ "collate ::= COLLATE ids", - /* 251 */ "cmd ::= DROP INDEX ifexists fullname", - /* 252 */ "cmd ::= VACUUM", - /* 253 */ "cmd ::= VACUUM nm", - /* 254 */ "cmd ::= PRAGMA nm dbnm", - /* 255 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 256 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 257 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 258 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 259 */ "nmnum ::= plus_num", - /* 260 */ "nmnum ::= nm", - /* 261 */ "nmnum ::= ON", - /* 262 */ "nmnum ::= DELETE", - /* 263 */ "nmnum ::= DEFAULT", - /* 264 */ "plus_num ::= PLUS number", - /* 265 */ "plus_num ::= number", - /* 266 */ "minus_num ::= MINUS number", - /* 267 */ "number ::= INTEGER|FLOAT", - /* 268 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 269 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 270 */ "trigger_time ::= BEFORE", - /* 271 */ "trigger_time ::= AFTER", - /* 272 */ "trigger_time ::= INSTEAD OF", - /* 273 */ "trigger_time ::=", - /* 274 */ "trigger_event ::= DELETE|INSERT", - /* 275 */ "trigger_event ::= UPDATE", - /* 276 */ "trigger_event ::= UPDATE OF inscollist", - /* 277 */ "foreach_clause ::=", - /* 278 */ "foreach_clause ::= FOR EACH ROW", - /* 279 */ "when_clause ::=", - /* 280 */ "when_clause ::= WHEN expr", - /* 281 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 282 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 283 */ "trnm ::= nm", - /* 284 */ "trnm ::= nm DOT nm", - /* 285 */ "tridxby ::=", - /* 286 */ "tridxby ::= INDEXED BY nm", - /* 287 */ "tridxby ::= NOT INDEXED", - /* 288 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt", - /* 289 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt valuelist", - /* 290 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select", - /* 291 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt", - /* 292 */ "trigger_cmd ::= select", - /* 293 */ "expr ::= RAISE LP IGNORE RP", - /* 294 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 295 */ "raisetype ::= ROLLBACK", - /* 296 */ "raisetype ::= ABORT", - /* 297 */ "raisetype ::= FAIL", - /* 298 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 299 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 300 */ "cmd ::= DETACH database_kw_opt expr", - /* 301 */ "key_opt ::=", - /* 302 */ "key_opt ::= KEY expr", - /* 303 */ "database_kw_opt ::= DATABASE", - /* 304 */ "database_kw_opt ::=", - /* 305 */ "cmd ::= REINDEX", - /* 306 */ "cmd ::= REINDEX nm dbnm", - /* 307 */ "cmd ::= ANALYZE", - /* 308 */ "cmd ::= ANALYZE nm dbnm", - /* 309 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 310 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column", - /* 311 */ "add_column_fullname ::= fullname", - /* 312 */ "kwcolumn_opt ::=", - /* 313 */ "kwcolumn_opt ::= COLUMNKW", - /* 314 */ "cmd ::= create_vtab", - /* 315 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 316 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 317 */ "vtabarglist ::= vtabarg", - /* 318 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 319 */ "vtabarg ::=", - /* 320 */ "vtabarg ::= vtabarg vtabargtoken", - /* 321 */ "vtabargtoken ::= ANY", - /* 322 */ "vtabargtoken ::= lp anylist RP", - /* 323 */ "lp ::= LP", - /* 324 */ "anylist ::=", - /* 325 */ "anylist ::= anylist LP anylist RP", - /* 326 */ "anylist ::= anylist ANY", -}; -#endif /* NDEBUG */ - - -#if YYSTACKDEPTH<=0 -/* -** Try to increase the size of the parser stack. -*/ -static void yyGrowStack(yyParser *p){ - int newSize; - yyStackEntry *pNew; - - newSize = p->yystksz*2 + 100; - pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); - if( pNew ){ - p->yystack = pNew; - p->yystksz = newSize; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", - yyTracePrompt, p->yystksz); - } -#endif - } -} -#endif - -/* -** This function allocates a new parser. -** The only argument is a pointer to a function which works like -** malloc. -** -** Inputs: -** A pointer to the function used to allocate memory. -** -** Outputs: -** A pointer to a parser. This pointer is used in subsequent calls -** to sqlite3Parser and sqlite3ParserFree. -*/ -SQLITE_PRIVATE void *sqlite3ParserAlloc(void *(*mallocProc)(size_t)){ - yyParser *pParser; - pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); - if( pParser ){ - pParser->yyidx = -1; -#ifdef YYTRACKMAXSTACKDEPTH - pParser->yyidxMax = 0; -#endif -#if YYSTACKDEPTH<=0 - pParser->yystack = NULL; - pParser->yystksz = 0; - yyGrowStack(pParser); -#endif - } - return pParser; -} - -/* The following function deletes the value associated with a -** symbol. The symbol can be either a terminal or nonterminal. -** "yymajor" is the symbol code, and "yypminor" is a pointer to -** the value. -*/ -static void yy_destructor( - yyParser *yypParser, /* The parser */ - YYCODETYPE yymajor, /* Type code for object to destroy */ - YYMINORTYPE *yypminor /* The object to be destroyed */ -){ - sqlite3ParserARG_FETCH; - switch( yymajor ){ - /* Here is inserted the actions which take place when a - ** terminal or non-terminal is destroyed. This can happen - ** when the symbol is popped from the stack during a - ** reduce or during error processing or when a parser is - ** being destroyed before it is finished parsing. - ** - ** Note: during a reduce, the only symbols destroyed are those - ** which appear on the RHS of the rule, but which are not used - ** inside the C code. - */ - case 160: /* select */ - case 194: /* oneselect */ -{ -sqlite3SelectDelete(pParse->db, (yypminor->yy159)); -} - break; - case 173: /* term */ - case 174: /* expr */ -{ -sqlite3ExprDelete(pParse->db, (yypminor->yy342).pExpr); -} - break; - case 178: /* idxlist_opt */ - case 187: /* idxlist */ - case 197: /* selcollist */ - case 200: /* groupby_opt */ - case 202: /* orderby_opt */ - case 204: /* sclp */ - case 214: /* sortlist */ - case 215: /* nexprlist */ - case 216: /* setlist */ - case 220: /* exprlist */ - case 225: /* case_exprlist */ -{ -sqlite3ExprListDelete(pParse->db, (yypminor->yy442)); -} - break; - case 193: /* fullname */ - case 198: /* from */ - case 206: /* seltablist */ - case 207: /* stl_prefix */ -{ -sqlite3SrcListDelete(pParse->db, (yypminor->yy347)); -} - break; - case 199: /* where_opt */ - case 201: /* having_opt */ - case 210: /* on_opt */ - case 224: /* case_operand */ - case 226: /* case_else */ - case 236: /* when_clause */ - case 241: /* key_opt */ -{ -sqlite3ExprDelete(pParse->db, (yypminor->yy122)); -} - break; - case 211: /* using_opt */ - case 213: /* inscollist */ - case 218: /* inscollist_opt */ -{ -sqlite3IdListDelete(pParse->db, (yypminor->yy180)); -} - break; - case 219: /* valuelist */ -{ - - sqlite3ExprListDelete(pParse->db, (yypminor->yy487).pList); - sqlite3SelectDelete(pParse->db, (yypminor->yy487).pSelect); - -} - break; - case 232: /* trigger_cmd_list */ - case 237: /* trigger_cmd */ -{ -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy327)); -} - break; - case 234: /* trigger_event */ -{ -sqlite3IdListDelete(pParse->db, (yypminor->yy410).b); -} - break; - default: break; /* If no destructor action specified: do nothing */ - } -} - -/* -** Pop the parser's stack once. -** -** If there is a destructor routine associated with the token which -** is popped from the stack, then call it. -** -** Return the major token number for the symbol popped. -*/ -static int yy_pop_parser_stack(yyParser *pParser){ - YYCODETYPE yymajor; - yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; - - /* There is no mechanism by which the parser stack can be popped below - ** empty in SQLite. */ - if( NEVER(pParser->yyidx<0) ) return 0; -#ifndef NDEBUG - if( yyTraceFILE && pParser->yyidx>=0 ){ - fprintf(yyTraceFILE,"%sPopping %s\n", - yyTracePrompt, - yyTokenName[yytos->major]); - } -#endif - yymajor = yytos->major; - yy_destructor(pParser, yymajor, &yytos->minor); - pParser->yyidx--; - return yymajor; -} - -/* -** Deallocate and destroy a parser. Destructors are all called for -** all stack elements before shutting the parser down. -** -** Inputs: -**
      -**
    • A pointer to the parser. This should be a pointer -** obtained from sqlite3ParserAlloc. -**
    • A pointer to a function used to reclaim memory obtained -** from malloc. -**
    -*/ -SQLITE_PRIVATE void sqlite3ParserFree( - void *p, /* The parser to be deleted */ - void (*freeProc)(void*) /* Function used to reclaim memory */ -){ - yyParser *pParser = (yyParser*)p; - /* In SQLite, we never try to destroy a parser that was not successfully - ** created in the first place. */ - if( NEVER(pParser==0) ) return; - while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); -#if YYSTACKDEPTH<=0 - free(pParser->yystack); -#endif - (*freeProc)((void*)pParser); -} - -/* -** Return the peak depth of the stack for a parser. -*/ -#ifdef YYTRACKMAXSTACKDEPTH -SQLITE_PRIVATE int sqlite3ParserStackPeak(void *p){ - yyParser *pParser = (yyParser*)p; - return pParser->yyidxMax; -} -#endif - -/* -** Find the appropriate action for a parser given the terminal -** look-ahead token iLookAhead. -** -** If the look-ahead token is YYNOCODE, then check to see if the action is -** independent of the look-ahead. If it is, return the action, otherwise -** return YY_NO_ACTION. -*/ -static int yy_find_shift_action( - yyParser *pParser, /* The parser */ - YYCODETYPE iLookAhead /* The look-ahead token */ -){ - int i; - int stateno = pParser->yystack[pParser->yyidx].stateno; - - if( stateno>YY_SHIFT_COUNT - || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ - return yy_default[stateno]; - } - assert( iLookAhead!=YYNOCODE ); - i += iLookAhead; - if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ - if( iLookAhead>0 ){ -#ifdef YYFALLBACK - YYCODETYPE iFallback; /* Fallback token */ - if( iLookAhead %s\n", - yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); - } -#endif - return yy_find_shift_action(pParser, iFallback); - } -#endif -#ifdef YYWILDCARD - { - int j = i - iLookAhead + YYWILDCARD; - if( -#if YY_SHIFT_MIN+YYWILDCARD<0 - j>=0 && -#endif -#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT - j %s\n", - yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); - } -#endif /* NDEBUG */ - return yy_action[j]; - } - } -#endif /* YYWILDCARD */ - } - return yy_default[stateno]; - }else{ - return yy_action[i]; - } -} - -/* -** Find the appropriate action for a parser given the non-terminal -** look-ahead token iLookAhead. -** -** If the look-ahead token is YYNOCODE, then check to see if the action is -** independent of the look-ahead. If it is, return the action, otherwise -** return YY_NO_ACTION. -*/ -static int yy_find_reduce_action( - int stateno, /* Current state number */ - YYCODETYPE iLookAhead /* The look-ahead token */ -){ - int i; -#ifdef YYERRORSYMBOL - if( stateno>YY_REDUCE_COUNT ){ - return yy_default[stateno]; - } -#else - assert( stateno<=YY_REDUCE_COUNT ); -#endif - i = yy_reduce_ofst[stateno]; - assert( i!=YY_REDUCE_USE_DFLT ); - assert( iLookAhead!=YYNOCODE ); - i += iLookAhead; -#ifdef YYERRORSYMBOL - if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ - return yy_default[stateno]; - } -#else - assert( i>=0 && iyyidx--; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will execute if the parser - ** stack every overflows */ - - UNUSED_PARAMETER(yypMinor); /* Silence some compiler warnings */ - sqlite3ErrorMsg(pParse, "parser stack overflow"); - sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument var */ -} - -/* -** Perform a shift action. -*/ -static void yy_shift( - yyParser *yypParser, /* The parser to be shifted */ - int yyNewState, /* The new state to shift in */ - int yyMajor, /* The major token to shift in */ - YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ -){ - yyStackEntry *yytos; - yypParser->yyidx++; -#ifdef YYTRACKMAXSTACKDEPTH - if( yypParser->yyidx>yypParser->yyidxMax ){ - yypParser->yyidxMax = yypParser->yyidx; - } -#endif -#if YYSTACKDEPTH>0 - if( yypParser->yyidx>=YYSTACKDEPTH ){ - yyStackOverflow(yypParser, yypMinor); - return; - } -#else - if( yypParser->yyidx>=yypParser->yystksz ){ - yyGrowStack(yypParser); - if( yypParser->yyidx>=yypParser->yystksz ){ - yyStackOverflow(yypParser, yypMinor); - return; - } - } -#endif - yytos = &yypParser->yystack[yypParser->yyidx]; - yytos->stateno = (YYACTIONTYPE)yyNewState; - yytos->major = (YYCODETYPE)yyMajor; - yytos->minor = *yypMinor; -#ifndef NDEBUG - if( yyTraceFILE && yypParser->yyidx>0 ){ - int i; - fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); - fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); - for(i=1; i<=yypParser->yyidx; i++) - fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); - fprintf(yyTraceFILE,"\n"); - } -#endif -} - -/* The following table contains information about every rule that -** is used during the reduce. -*/ -static const struct { - YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ - unsigned char nrhs; /* Number of right-hand side symbols in the rule */ -} yyRuleInfo[] = { - { 142, 1 }, - { 143, 2 }, - { 143, 1 }, - { 144, 1 }, - { 144, 3 }, - { 145, 0 }, - { 145, 1 }, - { 145, 3 }, - { 146, 1 }, - { 147, 3 }, - { 149, 0 }, - { 149, 1 }, - { 149, 2 }, - { 148, 0 }, - { 148, 1 }, - { 148, 1 }, - { 148, 1 }, - { 147, 2 }, - { 147, 2 }, - { 147, 2 }, - { 151, 1 }, - { 151, 0 }, - { 147, 2 }, - { 147, 3 }, - { 147, 5 }, - { 147, 2 }, - { 152, 6 }, - { 154, 1 }, - { 156, 0 }, - { 156, 3 }, - { 155, 1 }, - { 155, 0 }, - { 153, 4 }, - { 153, 2 }, - { 158, 3 }, - { 158, 1 }, - { 161, 3 }, - { 162, 1 }, - { 165, 1 }, - { 165, 1 }, - { 166, 1 }, - { 150, 1 }, - { 150, 1 }, - { 150, 1 }, - { 163, 0 }, - { 163, 1 }, - { 167, 1 }, - { 167, 4 }, - { 167, 6 }, - { 168, 1 }, - { 168, 2 }, - { 169, 1 }, - { 169, 1 }, - { 164, 2 }, - { 164, 0 }, - { 172, 2 }, - { 172, 2 }, - { 172, 4 }, - { 172, 3 }, - { 172, 3 }, - { 172, 2 }, - { 172, 2 }, - { 172, 3 }, - { 172, 5 }, - { 172, 2 }, - { 172, 4 }, - { 172, 4 }, - { 172, 1 }, - { 172, 2 }, - { 177, 0 }, - { 177, 1 }, - { 179, 0 }, - { 179, 2 }, - { 181, 2 }, - { 181, 3 }, - { 181, 3 }, - { 181, 3 }, - { 182, 2 }, - { 182, 2 }, - { 182, 1 }, - { 182, 1 }, - { 182, 2 }, - { 180, 3 }, - { 180, 2 }, - { 183, 0 }, - { 183, 2 }, - { 183, 2 }, - { 159, 0 }, - { 159, 2 }, - { 184, 3 }, - { 184, 1 }, - { 185, 1 }, - { 185, 0 }, - { 186, 2 }, - { 186, 7 }, - { 186, 5 }, - { 186, 5 }, - { 186, 10 }, - { 188, 0 }, - { 188, 1 }, - { 175, 0 }, - { 175, 3 }, - { 189, 0 }, - { 189, 2 }, - { 190, 1 }, - { 190, 1 }, - { 190, 1 }, - { 147, 4 }, - { 192, 2 }, - { 192, 0 }, - { 147, 8 }, - { 147, 4 }, - { 147, 1 }, - { 160, 1 }, - { 160, 3 }, - { 195, 1 }, - { 195, 2 }, - { 195, 1 }, - { 194, 9 }, - { 196, 1 }, - { 196, 1 }, - { 196, 0 }, - { 204, 2 }, - { 204, 0 }, - { 197, 3 }, - { 197, 2 }, - { 197, 4 }, - { 205, 2 }, - { 205, 1 }, - { 205, 0 }, - { 198, 0 }, - { 198, 2 }, - { 207, 2 }, - { 207, 0 }, - { 206, 7 }, - { 206, 7 }, - { 206, 7 }, - { 157, 0 }, - { 157, 2 }, - { 193, 2 }, - { 208, 1 }, - { 208, 2 }, - { 208, 3 }, - { 208, 4 }, - { 210, 2 }, - { 210, 0 }, - { 209, 0 }, - { 209, 3 }, - { 209, 2 }, - { 211, 4 }, - { 211, 0 }, - { 202, 0 }, - { 202, 3 }, - { 214, 4 }, - { 214, 2 }, - { 176, 1 }, - { 176, 1 }, - { 176, 0 }, - { 200, 0 }, - { 200, 3 }, - { 201, 0 }, - { 201, 2 }, - { 203, 0 }, - { 203, 2 }, - { 203, 4 }, - { 203, 4 }, - { 147, 5 }, - { 199, 0 }, - { 199, 2 }, - { 147, 7 }, - { 216, 5 }, - { 216, 3 }, - { 147, 5 }, - { 147, 5 }, - { 147, 6 }, - { 217, 2 }, - { 217, 1 }, - { 219, 4 }, - { 219, 5 }, - { 218, 0 }, - { 218, 3 }, - { 213, 3 }, - { 213, 1 }, - { 174, 1 }, - { 174, 3 }, - { 173, 1 }, - { 174, 1 }, - { 174, 1 }, - { 174, 3 }, - { 174, 5 }, - { 173, 1 }, - { 173, 1 }, - { 174, 1 }, - { 174, 1 }, - { 174, 3 }, - { 174, 6 }, - { 174, 5 }, - { 174, 4 }, - { 173, 1 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 221, 1 }, - { 221, 2 }, - { 221, 1 }, - { 221, 2 }, - { 174, 3 }, - { 174, 5 }, - { 174, 2 }, - { 174, 3 }, - { 174, 3 }, - { 174, 4 }, - { 174, 2 }, - { 174, 2 }, - { 174, 2 }, - { 174, 2 }, - { 222, 1 }, - { 222, 2 }, - { 174, 5 }, - { 223, 1 }, - { 223, 2 }, - { 174, 5 }, - { 174, 3 }, - { 174, 5 }, - { 174, 4 }, - { 174, 4 }, - { 174, 5 }, - { 225, 5 }, - { 225, 4 }, - { 226, 2 }, - { 226, 0 }, - { 224, 1 }, - { 224, 0 }, - { 220, 1 }, - { 220, 0 }, - { 215, 3 }, - { 215, 1 }, - { 147, 11 }, - { 227, 1 }, - { 227, 0 }, - { 178, 0 }, - { 178, 3 }, - { 187, 5 }, - { 187, 3 }, - { 228, 0 }, - { 228, 2 }, - { 147, 4 }, - { 147, 1 }, - { 147, 2 }, - { 147, 3 }, - { 147, 5 }, - { 147, 6 }, - { 147, 5 }, - { 147, 6 }, - { 229, 1 }, - { 229, 1 }, - { 229, 1 }, - { 229, 1 }, - { 229, 1 }, - { 170, 2 }, - { 170, 1 }, - { 171, 2 }, - { 230, 1 }, - { 147, 5 }, - { 231, 11 }, - { 233, 1 }, - { 233, 1 }, - { 233, 2 }, - { 233, 0 }, - { 234, 1 }, - { 234, 1 }, - { 234, 3 }, - { 235, 0 }, - { 235, 3 }, - { 236, 0 }, - { 236, 2 }, - { 232, 3 }, - { 232, 2 }, - { 238, 1 }, - { 238, 3 }, - { 239, 0 }, - { 239, 3 }, - { 239, 2 }, - { 237, 7 }, - { 237, 5 }, - { 237, 5 }, - { 237, 5 }, - { 237, 1 }, - { 174, 4 }, - { 174, 6 }, - { 191, 1 }, - { 191, 1 }, - { 191, 1 }, - { 147, 4 }, - { 147, 6 }, - { 147, 3 }, - { 241, 0 }, - { 241, 2 }, - { 240, 1 }, - { 240, 0 }, - { 147, 1 }, - { 147, 3 }, - { 147, 1 }, - { 147, 3 }, - { 147, 6 }, - { 147, 6 }, - { 242, 1 }, - { 243, 0 }, - { 243, 1 }, - { 147, 1 }, - { 147, 4 }, - { 244, 8 }, - { 245, 1 }, - { 245, 3 }, - { 246, 0 }, - { 246, 2 }, - { 247, 1 }, - { 247, 3 }, - { 248, 1 }, - { 249, 0 }, - { 249, 4 }, - { 249, 2 }, -}; - -static void yy_accept(yyParser*); /* Forward Declaration */ - -/* -** Perform a reduce action and the shift that must immediately -** follow the reduce. -*/ -static void yy_reduce( - yyParser *yypParser, /* The parser */ - int yyruleno /* Number of the rule by which to reduce */ -){ - int yygoto; /* The next state */ - int yyact; /* The next action */ - YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ - yyStackEntry *yymsp; /* The top of the parser's stack */ - int yysize; /* Amount to pop the stack */ - sqlite3ParserARG_FETCH; - yymsp = &yypParser->yystack[yypParser->yyidx]; -#ifndef NDEBUG - if( yyTraceFILE && yyruleno>=0 - && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ - fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, - yyRuleName[yyruleno]); - } -#endif /* NDEBUG */ - - /* Silence complaints from purify about yygotominor being uninitialized - ** in some cases when it is copied into the stack after the following - ** switch. yygotominor is uninitialized when a rule reduces that does - ** not set the value of its left-hand side nonterminal. Leaving the - ** value of the nonterminal uninitialized is utterly harmless as long - ** as the value is never used. So really the only thing this code - ** accomplishes is to quieten purify. - ** - ** 2007-01-16: The wireshark project (www.wireshark.org) reports that - ** without this code, their parser segfaults. I'm not sure what there - ** parser is doing to make this happen. This is the second bug report - ** from wireshark this week. Clearly they are stressing Lemon in ways - ** that it has not been previously stressed... (SQLite ticket #2172) - */ - /*memset(&yygotominor, 0, sizeof(yygotominor));*/ - yygotominor = yyzerominor; - - - switch( yyruleno ){ - /* Beginning here are the reduction cases. A typical example - ** follows: - ** case 0: - ** #line - ** { ... } // User supplied code - ** #line - ** break; - */ - case 5: /* explain ::= */ -{ sqlite3BeginParse(pParse, 0); } - break; - case 6: /* explain ::= EXPLAIN */ -{ sqlite3BeginParse(pParse, 1); } - break; - case 7: /* explain ::= EXPLAIN QUERY PLAN */ -{ sqlite3BeginParse(pParse, 2); } - break; - case 8: /* cmdx ::= cmd */ -{ sqlite3FinishCoding(pParse); } - break; - case 9: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy392);} - break; - case 13: /* transtype ::= */ -{yygotominor.yy392 = TK_DEFERRED;} - break; - case 14: /* transtype ::= DEFERRED */ - case 15: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==15); - case 16: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==16); - case 115: /* multiselect_op ::= UNION */ yytestcase(yyruleno==115); - case 117: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==117); -{yygotominor.yy392 = yymsp[0].major;} - break; - case 17: /* cmd ::= COMMIT trans_opt */ - case 18: /* cmd ::= END trans_opt */ yytestcase(yyruleno==18); -{sqlite3CommitTransaction(pParse);} - break; - case 19: /* cmd ::= ROLLBACK trans_opt */ -{sqlite3RollbackTransaction(pParse);} - break; - case 22: /* cmd ::= SAVEPOINT nm */ -{ - sqlite3Savepoint(pParse, SAVEPOINT_BEGIN, &yymsp[0].minor.yy0); -} - break; - case 23: /* cmd ::= RELEASE savepoint_opt nm */ -{ - sqlite3Savepoint(pParse, SAVEPOINT_RELEASE, &yymsp[0].minor.yy0); -} - break; - case 24: /* cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ -{ - sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &yymsp[0].minor.yy0); -} - break; - case 26: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ -{ - sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy392,0,0,yymsp[-2].minor.yy392); -} - break; - case 27: /* createkw ::= CREATE */ -{ - pParse->db->lookaside.bEnabled = 0; - yygotominor.yy0 = yymsp[0].minor.yy0; -} - break; - case 28: /* ifnotexists ::= */ - case 31: /* temp ::= */ yytestcase(yyruleno==31); - case 69: /* autoinc ::= */ yytestcase(yyruleno==69); - case 82: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==82); - case 84: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==84); - case 86: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ yytestcase(yyruleno==86); - case 98: /* defer_subclause_opt ::= */ yytestcase(yyruleno==98); - case 109: /* ifexists ::= */ yytestcase(yyruleno==109); - case 221: /* between_op ::= BETWEEN */ yytestcase(yyruleno==221); - case 224: /* in_op ::= IN */ yytestcase(yyruleno==224); -{yygotominor.yy392 = 0;} - break; - case 29: /* ifnotexists ::= IF NOT EXISTS */ - case 30: /* temp ::= TEMP */ yytestcase(yyruleno==30); - case 70: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==70); - case 85: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ yytestcase(yyruleno==85); - case 108: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==108); - case 222: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==222); - case 225: /* in_op ::= NOT IN */ yytestcase(yyruleno==225); -{yygotominor.yy392 = 1;} - break; - case 32: /* create_table_args ::= LP columnlist conslist_opt RP */ -{ - sqlite3EndTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0); -} - break; - case 33: /* create_table_args ::= AS select */ -{ - sqlite3EndTable(pParse,0,0,yymsp[0].minor.yy159); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy159); -} - break; - case 36: /* column ::= columnid type carglist */ -{ - yygotominor.yy0.z = yymsp[-2].minor.yy0.z; - yygotominor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-2].minor.yy0.z) + pParse->sLastToken.n; -} - break; - case 37: /* columnid ::= nm */ -{ - sqlite3AddColumn(pParse,&yymsp[0].minor.yy0); - yygotominor.yy0 = yymsp[0].minor.yy0; - pParse->constraintName.n = 0; -} - break; - case 38: /* id ::= ID */ - case 39: /* id ::= INDEXED */ yytestcase(yyruleno==39); - case 40: /* ids ::= ID|STRING */ yytestcase(yyruleno==40); - case 41: /* nm ::= id */ yytestcase(yyruleno==41); - case 42: /* nm ::= STRING */ yytestcase(yyruleno==42); - case 43: /* nm ::= JOIN_KW */ yytestcase(yyruleno==43); - case 46: /* typetoken ::= typename */ yytestcase(yyruleno==46); - case 49: /* typename ::= ids */ yytestcase(yyruleno==49); - case 127: /* as ::= AS nm */ yytestcase(yyruleno==127); - case 128: /* as ::= ids */ yytestcase(yyruleno==128); - case 138: /* dbnm ::= DOT nm */ yytestcase(yyruleno==138); - case 147: /* indexed_opt ::= INDEXED BY nm */ yytestcase(yyruleno==147); - case 250: /* collate ::= COLLATE ids */ yytestcase(yyruleno==250); - case 259: /* nmnum ::= plus_num */ yytestcase(yyruleno==259); - case 260: /* nmnum ::= nm */ yytestcase(yyruleno==260); - case 261: /* nmnum ::= ON */ yytestcase(yyruleno==261); - case 262: /* nmnum ::= DELETE */ yytestcase(yyruleno==262); - case 263: /* nmnum ::= DEFAULT */ yytestcase(yyruleno==263); - case 264: /* plus_num ::= PLUS number */ yytestcase(yyruleno==264); - case 265: /* plus_num ::= number */ yytestcase(yyruleno==265); - case 266: /* minus_num ::= MINUS number */ yytestcase(yyruleno==266); - case 267: /* number ::= INTEGER|FLOAT */ yytestcase(yyruleno==267); - case 283: /* trnm ::= nm */ yytestcase(yyruleno==283); -{yygotominor.yy0 = yymsp[0].minor.yy0;} - break; - case 45: /* type ::= typetoken */ -{sqlite3AddColumnType(pParse,&yymsp[0].minor.yy0);} - break; - case 47: /* typetoken ::= typename LP signed RP */ -{ - yygotominor.yy0.z = yymsp[-3].minor.yy0.z; - yygotominor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z); -} - break; - case 48: /* typetoken ::= typename LP signed COMMA signed RP */ -{ - yygotominor.yy0.z = yymsp[-5].minor.yy0.z; - yygotominor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z); -} - break; - case 50: /* typename ::= typename ids */ -{yygotominor.yy0.z=yymsp[-1].minor.yy0.z; yygotominor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);} - break; - case 55: /* ccons ::= CONSTRAINT nm */ - case 93: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==93); -{pParse->constraintName = yymsp[0].minor.yy0;} - break; - case 56: /* ccons ::= DEFAULT term */ - case 58: /* ccons ::= DEFAULT PLUS term */ yytestcase(yyruleno==58); -{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy342);} - break; - case 57: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy342);} - break; - case 59: /* ccons ::= DEFAULT MINUS term */ -{ - ExprSpan v; - v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy342.pExpr, 0, 0); - v.zStart = yymsp[-1].minor.yy0.z; - v.zEnd = yymsp[0].minor.yy342.zEnd; - sqlite3AddDefaultValue(pParse,&v); -} - break; - case 60: /* ccons ::= DEFAULT id */ -{ - ExprSpan v; - spanExpr(&v, pParse, TK_STRING, &yymsp[0].minor.yy0); - sqlite3AddDefaultValue(pParse,&v); -} - break; - case 62: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy392);} - break; - case 63: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy392,yymsp[0].minor.yy392,yymsp[-2].minor.yy392);} - break; - case 64: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy392,0,0,0,0);} - break; - case 65: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy342.pExpr);} - break; - case 66: /* ccons ::= REFERENCES nm idxlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy442,yymsp[0].minor.yy392);} - break; - case 67: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy392);} - break; - case 68: /* ccons ::= COLLATE ids */ -{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} - break; - case 71: /* refargs ::= */ -{ yygotominor.yy392 = OE_None*0x0101; /* EV: R-19803-45884 */} - break; - case 72: /* refargs ::= refargs refarg */ -{ yygotominor.yy392 = (yymsp[-1].minor.yy392 & ~yymsp[0].minor.yy207.mask) | yymsp[0].minor.yy207.value; } - break; - case 73: /* refarg ::= MATCH nm */ - case 74: /* refarg ::= ON INSERT refact */ yytestcase(yyruleno==74); -{ yygotominor.yy207.value = 0; yygotominor.yy207.mask = 0x000000; } - break; - case 75: /* refarg ::= ON DELETE refact */ -{ yygotominor.yy207.value = yymsp[0].minor.yy392; yygotominor.yy207.mask = 0x0000ff; } - break; - case 76: /* refarg ::= ON UPDATE refact */ -{ yygotominor.yy207.value = yymsp[0].minor.yy392<<8; yygotominor.yy207.mask = 0x00ff00; } - break; - case 77: /* refact ::= SET NULL */ -{ yygotominor.yy392 = OE_SetNull; /* EV: R-33326-45252 */} - break; - case 78: /* refact ::= SET DEFAULT */ -{ yygotominor.yy392 = OE_SetDflt; /* EV: R-33326-45252 */} - break; - case 79: /* refact ::= CASCADE */ -{ yygotominor.yy392 = OE_Cascade; /* EV: R-33326-45252 */} - break; - case 80: /* refact ::= RESTRICT */ -{ yygotominor.yy392 = OE_Restrict; /* EV: R-33326-45252 */} - break; - case 81: /* refact ::= NO ACTION */ -{ yygotominor.yy392 = OE_None; /* EV: R-33326-45252 */} - break; - case 83: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - case 99: /* defer_subclause_opt ::= defer_subclause */ yytestcase(yyruleno==99); - case 101: /* onconf ::= ON CONFLICT resolvetype */ yytestcase(yyruleno==101); - case 104: /* resolvetype ::= raisetype */ yytestcase(yyruleno==104); -{yygotominor.yy392 = yymsp[0].minor.yy392;} - break; - case 87: /* conslist_opt ::= */ -{yygotominor.yy0.n = 0; yygotominor.yy0.z = 0;} - break; - case 88: /* conslist_opt ::= COMMA conslist */ -{yygotominor.yy0 = yymsp[-1].minor.yy0;} - break; - case 91: /* tconscomma ::= COMMA */ -{pParse->constraintName.n = 0;} - break; - case 94: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy442,yymsp[0].minor.yy392,yymsp[-2].minor.yy392,0);} - break; - case 95: /* tcons ::= UNIQUE LP idxlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy442,yymsp[0].minor.yy392,0,0,0,0);} - break; - case 96: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy342.pExpr);} - break; - case 97: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */ -{ - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy442, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy442, yymsp[-1].minor.yy392); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy392); -} - break; - case 100: /* onconf ::= */ -{yygotominor.yy392 = OE_Default;} - break; - case 102: /* orconf ::= */ -{yygotominor.yy258 = OE_Default;} - break; - case 103: /* orconf ::= OR resolvetype */ -{yygotominor.yy258 = (u8)yymsp[0].minor.yy392;} - break; - case 105: /* resolvetype ::= IGNORE */ -{yygotominor.yy392 = OE_Ignore;} - break; - case 106: /* resolvetype ::= REPLACE */ -{yygotominor.yy392 = OE_Replace;} - break; - case 107: /* cmd ::= DROP TABLE ifexists fullname */ -{ - sqlite3DropTable(pParse, yymsp[0].minor.yy347, 0, yymsp[-1].minor.yy392); -} - break; - case 110: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select */ -{ - sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy159, yymsp[-6].minor.yy392, yymsp[-4].minor.yy392); -} - break; - case 111: /* cmd ::= DROP VIEW ifexists fullname */ -{ - sqlite3DropTable(pParse, yymsp[0].minor.yy347, 1, yymsp[-1].minor.yy392); -} - break; - case 112: /* cmd ::= select */ -{ - SelectDest dest = {SRT_Output, 0, 0, 0, 0}; - sqlite3Select(pParse, yymsp[0].minor.yy159, &dest); - sqlite3ExplainBegin(pParse->pVdbe); - sqlite3ExplainSelect(pParse->pVdbe, yymsp[0].minor.yy159); - sqlite3ExplainFinish(pParse->pVdbe); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy159); -} - break; - case 113: /* select ::= oneselect */ -{yygotominor.yy159 = yymsp[0].minor.yy159;} - break; - case 114: /* select ::= select multiselect_op oneselect */ -{ - if( yymsp[0].minor.yy159 ){ - yymsp[0].minor.yy159->op = (u8)yymsp[-1].minor.yy392; - yymsp[0].minor.yy159->pPrior = yymsp[-2].minor.yy159; - }else{ - sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy159); - } - yygotominor.yy159 = yymsp[0].minor.yy159; -} - break; - case 116: /* multiselect_op ::= UNION ALL */ -{yygotominor.yy392 = TK_ALL;} - break; - case 118: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ -{ - yygotominor.yy159 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy442,yymsp[-5].minor.yy347,yymsp[-4].minor.yy122,yymsp[-3].minor.yy442,yymsp[-2].minor.yy122,yymsp[-1].minor.yy442,yymsp[-7].minor.yy305,yymsp[0].minor.yy64.pLimit,yymsp[0].minor.yy64.pOffset); -} - break; - case 119: /* distinct ::= DISTINCT */ -{yygotominor.yy305 = SF_Distinct;} - break; - case 120: /* distinct ::= ALL */ - case 121: /* distinct ::= */ yytestcase(yyruleno==121); -{yygotominor.yy305 = 0;} - break; - case 122: /* sclp ::= selcollist COMMA */ - case 246: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==246); -{yygotominor.yy442 = yymsp[-1].minor.yy442;} - break; - case 123: /* sclp ::= */ - case 151: /* orderby_opt ::= */ yytestcase(yyruleno==151); - case 158: /* groupby_opt ::= */ yytestcase(yyruleno==158); - case 239: /* exprlist ::= */ yytestcase(yyruleno==239); - case 245: /* idxlist_opt ::= */ yytestcase(yyruleno==245); -{yygotominor.yy442 = 0;} - break; - case 124: /* selcollist ::= sclp expr as */ -{ - yygotominor.yy442 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy442, yymsp[-1].minor.yy342.pExpr); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yygotominor.yy442, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yygotominor.yy442,&yymsp[-1].minor.yy342); -} - break; - case 125: /* selcollist ::= sclp STAR */ -{ - Expr *p = sqlite3Expr(pParse->db, TK_ALL, 0); - yygotominor.yy442 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy442, p); -} - break; - case 126: /* selcollist ::= sclp nm DOT STAR */ -{ - Expr *pRight = sqlite3PExpr(pParse, TK_ALL, 0, 0, &yymsp[0].minor.yy0); - Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0); - Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy442, pDot); -} - break; - case 129: /* as ::= */ -{yygotominor.yy0.n = 0;} - break; - case 130: /* from ::= */ -{yygotominor.yy347 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy347));} - break; - case 131: /* from ::= FROM seltablist */ -{ - yygotominor.yy347 = yymsp[0].minor.yy347; - sqlite3SrcListShiftJoinType(yygotominor.yy347); -} - break; - case 132: /* stl_prefix ::= seltablist joinop */ -{ - yygotominor.yy347 = yymsp[-1].minor.yy347; - if( ALWAYS(yygotominor.yy347 && yygotominor.yy347->nSrc>0) ) yygotominor.yy347->a[yygotominor.yy347->nSrc-1].jointype = (u8)yymsp[0].minor.yy392; -} - break; - case 133: /* stl_prefix ::= */ -{yygotominor.yy347 = 0;} - break; - case 134: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ -{ - yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy122,yymsp[0].minor.yy180); - sqlite3SrcListIndexedBy(pParse, yygotominor.yy347, &yymsp[-2].minor.yy0); -} - break; - case 135: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ -{ - yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy159,yymsp[-1].minor.yy122,yymsp[0].minor.yy180); - } - break; - case 136: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ -{ - if( yymsp[-6].minor.yy347==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy122==0 && yymsp[0].minor.yy180==0 ){ - yygotominor.yy347 = yymsp[-4].minor.yy347; - }else if( yymsp[-4].minor.yy347->nSrc==1 ){ - yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy122,yymsp[0].minor.yy180); - if( yygotominor.yy347 ){ - struct SrcList_item *pNew = &yygotominor.yy347->a[yygotominor.yy347->nSrc-1]; - struct SrcList_item *pOld = yymsp[-4].minor.yy347->a; - pNew->zName = pOld->zName; - pNew->zDatabase = pOld->zDatabase; - pOld->zName = pOld->zDatabase = 0; - } - sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy347); - }else{ - Select *pSubquery; - sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy347); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy347,0,0,0,0,SF_NestedFrom,0,0); - yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy122,yymsp[0].minor.yy180); - } - } - break; - case 137: /* dbnm ::= */ - case 146: /* indexed_opt ::= */ yytestcase(yyruleno==146); -{yygotominor.yy0.z=0; yygotominor.yy0.n=0;} - break; - case 139: /* fullname ::= nm dbnm */ -{yygotominor.yy347 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} - break; - case 140: /* joinop ::= COMMA|JOIN */ -{ yygotominor.yy392 = JT_INNER; } - break; - case 141: /* joinop ::= JOIN_KW JOIN */ -{ yygotominor.yy392 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); } - break; - case 142: /* joinop ::= JOIN_KW nm JOIN */ -{ yygotominor.yy392 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); } - break; - case 143: /* joinop ::= JOIN_KW nm nm JOIN */ -{ yygotominor.yy392 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); } - break; - case 144: /* on_opt ::= ON expr */ - case 161: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==161); - case 168: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==168); - case 234: /* case_else ::= ELSE expr */ yytestcase(yyruleno==234); - case 236: /* case_operand ::= expr */ yytestcase(yyruleno==236); -{yygotominor.yy122 = yymsp[0].minor.yy342.pExpr;} - break; - case 145: /* on_opt ::= */ - case 160: /* having_opt ::= */ yytestcase(yyruleno==160); - case 167: /* where_opt ::= */ yytestcase(yyruleno==167); - case 235: /* case_else ::= */ yytestcase(yyruleno==235); - case 237: /* case_operand ::= */ yytestcase(yyruleno==237); -{yygotominor.yy122 = 0;} - break; - case 148: /* indexed_opt ::= NOT INDEXED */ -{yygotominor.yy0.z=0; yygotominor.yy0.n=1;} - break; - case 149: /* using_opt ::= USING LP inscollist RP */ - case 180: /* inscollist_opt ::= LP inscollist RP */ yytestcase(yyruleno==180); -{yygotominor.yy180 = yymsp[-1].minor.yy180;} - break; - case 150: /* using_opt ::= */ - case 179: /* inscollist_opt ::= */ yytestcase(yyruleno==179); -{yygotominor.yy180 = 0;} - break; - case 152: /* orderby_opt ::= ORDER BY sortlist */ - case 159: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==159); - case 238: /* exprlist ::= nexprlist */ yytestcase(yyruleno==238); -{yygotominor.yy442 = yymsp[0].minor.yy442;} - break; - case 153: /* sortlist ::= sortlist COMMA expr sortorder */ -{ - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy442,yymsp[-1].minor.yy342.pExpr); - if( yygotominor.yy442 ) yygotominor.yy442->a[yygotominor.yy442->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy392; -} - break; - case 154: /* sortlist ::= expr sortorder */ -{ - yygotominor.yy442 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy342.pExpr); - if( yygotominor.yy442 && ALWAYS(yygotominor.yy442->a) ) yygotominor.yy442->a[0].sortOrder = (u8)yymsp[0].minor.yy392; -} - break; - case 155: /* sortorder ::= ASC */ - case 157: /* sortorder ::= */ yytestcase(yyruleno==157); -{yygotominor.yy392 = SQLITE_SO_ASC;} - break; - case 156: /* sortorder ::= DESC */ -{yygotominor.yy392 = SQLITE_SO_DESC;} - break; - case 162: /* limit_opt ::= */ -{yygotominor.yy64.pLimit = 0; yygotominor.yy64.pOffset = 0;} - break; - case 163: /* limit_opt ::= LIMIT expr */ -{yygotominor.yy64.pLimit = yymsp[0].minor.yy342.pExpr; yygotominor.yy64.pOffset = 0;} - break; - case 164: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yygotominor.yy64.pLimit = yymsp[-2].minor.yy342.pExpr; yygotominor.yy64.pOffset = yymsp[0].minor.yy342.pExpr;} - break; - case 165: /* limit_opt ::= LIMIT expr COMMA expr */ -{yygotominor.yy64.pOffset = yymsp[-2].minor.yy342.pExpr; yygotominor.yy64.pLimit = yymsp[0].minor.yy342.pExpr;} - break; - case 166: /* cmd ::= DELETE FROM fullname indexed_opt where_opt */ -{ - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy347, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy347,yymsp[0].minor.yy122); -} - break; - case 169: /* cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt */ -{ - sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy347, &yymsp[-3].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy442,"set list"); - sqlite3Update(pParse,yymsp[-4].minor.yy347,yymsp[-1].minor.yy442,yymsp[0].minor.yy122,yymsp[-5].minor.yy258); -} - break; - case 170: /* setlist ::= setlist COMMA nm EQ expr */ -{ - yygotominor.yy442 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy442, yymsp[0].minor.yy342.pExpr); - sqlite3ExprListSetName(pParse, yygotominor.yy442, &yymsp[-2].minor.yy0, 1); -} - break; - case 171: /* setlist ::= nm EQ expr */ -{ - yygotominor.yy442 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy342.pExpr); - sqlite3ExprListSetName(pParse, yygotominor.yy442, &yymsp[-2].minor.yy0, 1); -} - break; - case 172: /* cmd ::= insert_cmd INTO fullname inscollist_opt valuelist */ -{sqlite3Insert(pParse, yymsp[-2].minor.yy347, yymsp[0].minor.yy487.pList, yymsp[0].minor.yy487.pSelect, yymsp[-1].minor.yy180, yymsp[-4].minor.yy258);} - break; - case 173: /* cmd ::= insert_cmd INTO fullname inscollist_opt select */ -{sqlite3Insert(pParse, yymsp[-2].minor.yy347, 0, yymsp[0].minor.yy159, yymsp[-1].minor.yy180, yymsp[-4].minor.yy258);} - break; - case 174: /* cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES */ -{sqlite3Insert(pParse, yymsp[-3].minor.yy347, 0, 0, yymsp[-2].minor.yy180, yymsp[-5].minor.yy258);} - break; - case 175: /* insert_cmd ::= INSERT orconf */ -{yygotominor.yy258 = yymsp[0].minor.yy258;} - break; - case 176: /* insert_cmd ::= REPLACE */ -{yygotominor.yy258 = OE_Replace;} - break; - case 177: /* valuelist ::= VALUES LP nexprlist RP */ -{ - yygotominor.yy487.pList = yymsp[-1].minor.yy442; - yygotominor.yy487.pSelect = 0; -} - break; - case 178: /* valuelist ::= valuelist COMMA LP exprlist RP */ -{ - Select *pRight = sqlite3SelectNew(pParse, yymsp[-1].minor.yy442, 0, 0, 0, 0, 0, 0, 0, 0); - if( yymsp[-4].minor.yy487.pList ){ - yymsp[-4].minor.yy487.pSelect = sqlite3SelectNew(pParse, yymsp[-4].minor.yy487.pList, 0, 0, 0, 0, 0, 0, 0, 0); - yymsp[-4].minor.yy487.pList = 0; - } - yygotominor.yy487.pList = 0; - if( yymsp[-4].minor.yy487.pSelect==0 || pRight==0 ){ - sqlite3SelectDelete(pParse->db, pRight); - sqlite3SelectDelete(pParse->db, yymsp[-4].minor.yy487.pSelect); - yygotominor.yy487.pSelect = 0; - }else{ - pRight->op = TK_ALL; - pRight->pPrior = yymsp[-4].minor.yy487.pSelect; - pRight->selFlags |= SF_Values; - pRight->pPrior->selFlags |= SF_Values; - yygotominor.yy487.pSelect = pRight; - } -} - break; - case 181: /* inscollist ::= inscollist COMMA nm */ -{yygotominor.yy180 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy180,&yymsp[0].minor.yy0);} - break; - case 182: /* inscollist ::= nm */ -{yygotominor.yy180 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);} - break; - case 183: /* expr ::= term */ -{yygotominor.yy342 = yymsp[0].minor.yy342;} - break; - case 184: /* expr ::= LP expr RP */ -{yygotominor.yy342.pExpr = yymsp[-1].minor.yy342.pExpr; spanSet(&yygotominor.yy342,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);} - break; - case 185: /* term ::= NULL */ - case 190: /* term ::= INTEGER|FLOAT|BLOB */ yytestcase(yyruleno==190); - case 191: /* term ::= STRING */ yytestcase(yyruleno==191); -{spanExpr(&yygotominor.yy342, pParse, yymsp[0].major, &yymsp[0].minor.yy0);} - break; - case 186: /* expr ::= id */ - case 187: /* expr ::= JOIN_KW */ yytestcase(yyruleno==187); -{spanExpr(&yygotominor.yy342, pParse, TK_ID, &yymsp[0].minor.yy0);} - break; - case 188: /* expr ::= nm DOT nm */ -{ - Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0); - Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0); - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0); - spanSet(&yygotominor.yy342,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); -} - break; - case 189: /* expr ::= nm DOT nm DOT nm */ -{ - Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-4].minor.yy0); - Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0); - Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0); - Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0); - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0); - spanSet(&yygotominor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); -} - break; - case 192: /* expr ::= REGISTER */ -{ - /* When doing a nested parse, one can include terms in an expression - ** that look like this: #1 #2 ... These terms refer to registers - ** in the virtual machine. #N is the N-th register. */ - if( pParse->nested==0 ){ - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &yymsp[0].minor.yy0); - yygotominor.yy342.pExpr = 0; - }else{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &yymsp[0].minor.yy0); - if( yygotominor.yy342.pExpr ) sqlite3GetInt32(&yymsp[0].minor.yy0.z[1], &yygotominor.yy342.pExpr->iTable); - } - spanSet(&yygotominor.yy342, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); -} - break; - case 193: /* expr ::= VARIABLE */ -{ - spanExpr(&yygotominor.yy342, pParse, TK_VARIABLE, &yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yygotominor.yy342.pExpr); - spanSet(&yygotominor.yy342, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); -} - break; - case 194: /* expr ::= expr COLLATE ids */ -{ - yygotominor.yy342.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy342.pExpr, &yymsp[0].minor.yy0); - yygotominor.yy342.zStart = yymsp[-2].minor.yy342.zStart; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; -} - break; - case 195: /* expr ::= CAST LP expr AS typetoken RP */ -{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy342.pExpr, 0, &yymsp[-1].minor.yy0); - spanSet(&yygotominor.yy342,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); -} - break; - case 196: /* expr ::= ID LP distinct exprlist RP */ -{ - if( yymsp[-1].minor.yy442 && yymsp[-1].minor.yy442->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ - sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0); - } - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy442, &yymsp[-4].minor.yy0); - spanSet(&yygotominor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); - if( yymsp[-2].minor.yy305 && yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->flags |= EP_Distinct; - } -} - break; - case 197: /* expr ::= ID LP STAR RP */ -{ - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0); - spanSet(&yygotominor.yy342,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); -} - break; - case 198: /* term ::= CTIME_KW */ -{ - /* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are - ** treated as functions that return constants */ - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, 0,&yymsp[0].minor.yy0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->op = TK_CONST_FUNC; - } - spanSet(&yygotominor.yy342, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); -} - break; - case 199: /* expr ::= expr AND expr */ - case 200: /* expr ::= expr OR expr */ yytestcase(yyruleno==200); - case 201: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==201); - case 202: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==202); - case 203: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==203); - case 204: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==204); - case 205: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==205); - case 206: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==206); -{spanBinaryExpr(&yygotominor.yy342,pParse,yymsp[-1].major,&yymsp[-2].minor.yy342,&yymsp[0].minor.yy342);} - break; - case 207: /* likeop ::= LIKE_KW */ - case 209: /* likeop ::= MATCH */ yytestcase(yyruleno==209); -{yygotominor.yy318.eOperator = yymsp[0].minor.yy0; yygotominor.yy318.bNot = 0;} - break; - case 208: /* likeop ::= NOT LIKE_KW */ - case 210: /* likeop ::= NOT MATCH */ yytestcase(yyruleno==210); -{yygotominor.yy318.eOperator = yymsp[0].minor.yy0; yygotominor.yy318.bNot = 1;} - break; - case 211: /* expr ::= expr likeop expr */ -{ - ExprList *pList; - pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy342.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy342.pExpr); - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy318.eOperator); - if( yymsp[-1].minor.yy318.bNot ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-2].minor.yy342.zStart; - yygotominor.yy342.zEnd = yymsp[0].minor.yy342.zEnd; - if( yygotominor.yy342.pExpr ) yygotominor.yy342.pExpr->flags |= EP_InfixFunc; -} - break; - case 212: /* expr ::= expr likeop expr ESCAPE expr */ -{ - ExprList *pList; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy342.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy342.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy342.pExpr); - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy318.eOperator); - if( yymsp[-3].minor.yy318.bNot ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-4].minor.yy342.zStart; - yygotominor.yy342.zEnd = yymsp[0].minor.yy342.zEnd; - if( yygotominor.yy342.pExpr ) yygotominor.yy342.pExpr->flags |= EP_InfixFunc; -} - break; - case 213: /* expr ::= expr ISNULL|NOTNULL */ -{spanUnaryPostfix(&yygotominor.yy342,pParse,yymsp[0].major,&yymsp[-1].minor.yy342,&yymsp[0].minor.yy0);} - break; - case 214: /* expr ::= expr NOT NULL */ -{spanUnaryPostfix(&yygotominor.yy342,pParse,TK_NOTNULL,&yymsp[-2].minor.yy342,&yymsp[0].minor.yy0);} - break; - case 215: /* expr ::= expr IS expr */ -{ - spanBinaryExpr(&yygotominor.yy342,pParse,TK_IS,&yymsp[-2].minor.yy342,&yymsp[0].minor.yy342); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy342.pExpr, yygotominor.yy342.pExpr, TK_ISNULL); -} - break; - case 216: /* expr ::= expr IS NOT expr */ -{ - spanBinaryExpr(&yygotominor.yy342,pParse,TK_ISNOT,&yymsp[-3].minor.yy342,&yymsp[0].minor.yy342); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy342.pExpr, yygotominor.yy342.pExpr, TK_NOTNULL); -} - break; - case 217: /* expr ::= NOT expr */ - case 218: /* expr ::= BITNOT expr */ yytestcase(yyruleno==218); -{spanUnaryPrefix(&yygotominor.yy342,pParse,yymsp[-1].major,&yymsp[0].minor.yy342,&yymsp[-1].minor.yy0);} - break; - case 219: /* expr ::= MINUS expr */ -{spanUnaryPrefix(&yygotominor.yy342,pParse,TK_UMINUS,&yymsp[0].minor.yy342,&yymsp[-1].minor.yy0);} - break; - case 220: /* expr ::= PLUS expr */ -{spanUnaryPrefix(&yygotominor.yy342,pParse,TK_UPLUS,&yymsp[0].minor.yy342,&yymsp[-1].minor.yy0);} - break; - case 223: /* expr ::= expr between_op expr AND expr */ -{ - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy342.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy342.pExpr); - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy342.pExpr, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pList = pList; - }else{ - sqlite3ExprListDelete(pParse->db, pList); - } - if( yymsp[-3].minor.yy392 ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-4].minor.yy342.zStart; - yygotominor.yy342.zEnd = yymsp[0].minor.yy342.zEnd; -} - break; - case 226: /* expr ::= expr in_op LP exprlist RP */ -{ - if( yymsp[-1].minor.yy442==0 ){ - /* Expressions of the form - ** - ** expr1 IN () - ** expr1 NOT IN () - ** - ** simplify to constants 0 (false) and 1 (true), respectively, - ** regardless of the value of expr1. - */ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[yymsp[-3].minor.yy392]); - sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy342.pExpr); - }else{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy342.pExpr, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pList = yymsp[-1].minor.yy442; - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); - }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy442); - } - if( yymsp[-3].minor.yy392 ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - } - yygotominor.yy342.zStart = yymsp[-4].minor.yy342.zStart; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; - } - break; - case 227: /* expr ::= LP select RP */ -{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pSelect = yymsp[-1].minor.yy159; - ExprSetProperty(yygotominor.yy342.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); - }else{ - sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy159); - } - yygotominor.yy342.zStart = yymsp[-2].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; - } - break; - case 228: /* expr ::= expr in_op LP select RP */ -{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy342.pExpr, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pSelect = yymsp[-1].minor.yy159; - ExprSetProperty(yygotominor.yy342.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); - }else{ - sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy159); - } - if( yymsp[-3].minor.yy392 ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-4].minor.yy342.zStart; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; - } - break; - case 229: /* expr ::= expr in_op nm dbnm */ -{ - SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0); - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy342.pExpr, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); - ExprSetProperty(yygotominor.yy342.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); - }else{ - sqlite3SrcListDelete(pParse->db, pSrc); - } - if( yymsp[-2].minor.yy392 ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-3].minor.yy342.zStart; - yygotominor.yy342.zEnd = yymsp[0].minor.yy0.z ? &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] : &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]; - } - break; - case 230: /* expr ::= EXISTS LP select RP */ -{ - Expr *p = yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); - if( p ){ - p->x.pSelect = yymsp[-1].minor.yy159; - ExprSetProperty(p, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, p); - }else{ - sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy159); - } - yygotominor.yy342.zStart = yymsp[-3].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; - } - break; - case 231: /* expr ::= CASE case_operand case_exprlist case_else END */ -{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy122, yymsp[-1].minor.yy122, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pList = yymsp[-2].minor.yy442; - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); - }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy442); - } - yygotominor.yy342.zStart = yymsp[-4].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; -} - break; - case 232: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ -{ - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy442, yymsp[-2].minor.yy342.pExpr); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yygotominor.yy442, yymsp[0].minor.yy342.pExpr); -} - break; - case 233: /* case_exprlist ::= WHEN expr THEN expr */ -{ - yygotominor.yy442 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy342.pExpr); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yygotominor.yy442, yymsp[0].minor.yy342.pExpr); -} - break; - case 240: /* nexprlist ::= nexprlist COMMA expr */ -{yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy442,yymsp[0].minor.yy342.pExpr);} - break; - case 241: /* nexprlist ::= expr */ -{yygotominor.yy442 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy342.pExpr);} - break; - case 242: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP */ -{ - sqlite3CreateIndex(pParse, &yymsp[-6].minor.yy0, &yymsp[-5].minor.yy0, - sqlite3SrcListAppend(pParse->db,0,&yymsp[-3].minor.yy0,0), yymsp[-1].minor.yy442, yymsp[-9].minor.yy392, - &yymsp[-10].minor.yy0, &yymsp[0].minor.yy0, SQLITE_SO_ASC, yymsp[-7].minor.yy392); -} - break; - case 243: /* uniqueflag ::= UNIQUE */ - case 296: /* raisetype ::= ABORT */ yytestcase(yyruleno==296); -{yygotominor.yy392 = OE_Abort;} - break; - case 244: /* uniqueflag ::= */ -{yygotominor.yy392 = OE_None;} - break; - case 247: /* idxlist ::= idxlist COMMA nm collate sortorder */ -{ - Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy442, p); - sqlite3ExprListSetName(pParse,yygotominor.yy442,&yymsp[-2].minor.yy0,1); - sqlite3ExprListCheckLength(pParse, yygotominor.yy442, "index"); - if( yygotominor.yy442 ) yygotominor.yy442->a[yygotominor.yy442->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy392; -} - break; - case 248: /* idxlist ::= nm collate sortorder */ -{ - Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,0, p); - sqlite3ExprListSetName(pParse, yygotominor.yy442, &yymsp[-2].minor.yy0, 1); - sqlite3ExprListCheckLength(pParse, yygotominor.yy442, "index"); - if( yygotominor.yy442 ) yygotominor.yy442->a[yygotominor.yy442->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy392; -} - break; - case 249: /* collate ::= */ -{yygotominor.yy0.z = 0; yygotominor.yy0.n = 0;} - break; - case 251: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy347, yymsp[-1].minor.yy392);} - break; - case 252: /* cmd ::= VACUUM */ - case 253: /* cmd ::= VACUUM nm */ yytestcase(yyruleno==253); -{sqlite3Vacuum(pParse);} - break; - case 254: /* cmd ::= PRAGMA nm dbnm */ -{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} - break; - case 255: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ -{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} - break; - case 256: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ -{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} - break; - case 257: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ -{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} - break; - case 258: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ -{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} - break; - case 268: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ -{ - Token all; - all.z = yymsp[-3].minor.yy0.z; - all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy327, &all); -} - break; - case 269: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ -{ - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy392, yymsp[-4].minor.yy410.a, yymsp[-4].minor.yy410.b, yymsp[-2].minor.yy347, yymsp[0].minor.yy122, yymsp[-10].minor.yy392, yymsp[-8].minor.yy392); - yygotominor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); -} - break; - case 270: /* trigger_time ::= BEFORE */ - case 273: /* trigger_time ::= */ yytestcase(yyruleno==273); -{ yygotominor.yy392 = TK_BEFORE; } - break; - case 271: /* trigger_time ::= AFTER */ -{ yygotominor.yy392 = TK_AFTER; } - break; - case 272: /* trigger_time ::= INSTEAD OF */ -{ yygotominor.yy392 = TK_INSTEAD;} - break; - case 274: /* trigger_event ::= DELETE|INSERT */ - case 275: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==275); -{yygotominor.yy410.a = yymsp[0].major; yygotominor.yy410.b = 0;} - break; - case 276: /* trigger_event ::= UPDATE OF inscollist */ -{yygotominor.yy410.a = TK_UPDATE; yygotominor.yy410.b = yymsp[0].minor.yy180;} - break; - case 279: /* when_clause ::= */ - case 301: /* key_opt ::= */ yytestcase(yyruleno==301); -{ yygotominor.yy122 = 0; } - break; - case 280: /* when_clause ::= WHEN expr */ - case 302: /* key_opt ::= KEY expr */ yytestcase(yyruleno==302); -{ yygotominor.yy122 = yymsp[0].minor.yy342.pExpr; } - break; - case 281: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ -{ - assert( yymsp[-2].minor.yy327!=0 ); - yymsp[-2].minor.yy327->pLast->pNext = yymsp[-1].minor.yy327; - yymsp[-2].minor.yy327->pLast = yymsp[-1].minor.yy327; - yygotominor.yy327 = yymsp[-2].minor.yy327; -} - break; - case 282: /* trigger_cmd_list ::= trigger_cmd SEMI */ -{ - assert( yymsp[-1].minor.yy327!=0 ); - yymsp[-1].minor.yy327->pLast = yymsp[-1].minor.yy327; - yygotominor.yy327 = yymsp[-1].minor.yy327; -} - break; - case 284: /* trnm ::= nm DOT nm */ -{ - yygotominor.yy0 = yymsp[0].minor.yy0; - sqlite3ErrorMsg(pParse, - "qualified table names are not allowed on INSERT, UPDATE, and DELETE " - "statements within triggers"); -} - break; - case 286: /* tridxby ::= INDEXED BY nm */ -{ - sqlite3ErrorMsg(pParse, - "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " - "within triggers"); -} - break; - case 287: /* tridxby ::= NOT INDEXED */ -{ - sqlite3ErrorMsg(pParse, - "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " - "within triggers"); -} - break; - case 288: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */ -{ yygotominor.yy327 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy442, yymsp[0].minor.yy122, yymsp[-5].minor.yy258); } - break; - case 289: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt valuelist */ -{yygotominor.yy327 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy180, yymsp[0].minor.yy487.pList, yymsp[0].minor.yy487.pSelect, yymsp[-4].minor.yy258);} - break; - case 290: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select */ -{yygotominor.yy327 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy180, 0, yymsp[0].minor.yy159, yymsp[-4].minor.yy258);} - break; - case 291: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */ -{yygotominor.yy327 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy122);} - break; - case 292: /* trigger_cmd ::= select */ -{yygotominor.yy327 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy159); } - break; - case 293: /* expr ::= RAISE LP IGNORE RP */ -{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->affinity = OE_Ignore; - } - yygotominor.yy342.zStart = yymsp[-3].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; -} - break; - case 294: /* expr ::= RAISE LP raisetype COMMA nm RP */ -{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0); - if( yygotominor.yy342.pExpr ) { - yygotominor.yy342.pExpr->affinity = (char)yymsp[-3].minor.yy392; - } - yygotominor.yy342.zStart = yymsp[-5].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; -} - break; - case 295: /* raisetype ::= ROLLBACK */ -{yygotominor.yy392 = OE_Rollback;} - break; - case 297: /* raisetype ::= FAIL */ -{yygotominor.yy392 = OE_Fail;} - break; - case 298: /* cmd ::= DROP TRIGGER ifexists fullname */ -{ - sqlite3DropTrigger(pParse,yymsp[0].minor.yy347,yymsp[-1].minor.yy392); -} - break; - case 299: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ -{ - sqlite3Attach(pParse, yymsp[-3].minor.yy342.pExpr, yymsp[-1].minor.yy342.pExpr, yymsp[0].minor.yy122); -} - break; - case 300: /* cmd ::= DETACH database_kw_opt expr */ -{ - sqlite3Detach(pParse, yymsp[0].minor.yy342.pExpr); -} - break; - case 305: /* cmd ::= REINDEX */ -{sqlite3Reindex(pParse, 0, 0);} - break; - case 306: /* cmd ::= REINDEX nm dbnm */ -{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} - break; - case 307: /* cmd ::= ANALYZE */ -{sqlite3Analyze(pParse, 0, 0);} - break; - case 308: /* cmd ::= ANALYZE nm dbnm */ -{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} - break; - case 309: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ -{ - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy347,&yymsp[0].minor.yy0); -} - break; - case 310: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */ -{ - sqlite3AlterFinishAddColumn(pParse, &yymsp[0].minor.yy0); -} - break; - case 311: /* add_column_fullname ::= fullname */ -{ - pParse->db->lookaside.bEnabled = 0; - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy347); -} - break; - case 314: /* cmd ::= create_vtab */ -{sqlite3VtabFinishParse(pParse,0);} - break; - case 315: /* cmd ::= create_vtab LP vtabarglist RP */ -{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} - break; - case 316: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ -{ - sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy392); -} - break; - case 319: /* vtabarg ::= */ -{sqlite3VtabArgInit(pParse);} - break; - case 321: /* vtabargtoken ::= ANY */ - case 322: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==322); - case 323: /* lp ::= LP */ yytestcase(yyruleno==323); -{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} - break; - default: - /* (0) input ::= cmdlist */ yytestcase(yyruleno==0); - /* (1) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==1); - /* (2) cmdlist ::= ecmd */ yytestcase(yyruleno==2); - /* (3) ecmd ::= SEMI */ yytestcase(yyruleno==3); - /* (4) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==4); - /* (10) trans_opt ::= */ yytestcase(yyruleno==10); - /* (11) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==11); - /* (12) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==12); - /* (20) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==20); - /* (21) savepoint_opt ::= */ yytestcase(yyruleno==21); - /* (25) cmd ::= create_table create_table_args */ yytestcase(yyruleno==25); - /* (34) columnlist ::= columnlist COMMA column */ yytestcase(yyruleno==34); - /* (35) columnlist ::= column */ yytestcase(yyruleno==35); - /* (44) type ::= */ yytestcase(yyruleno==44); - /* (51) signed ::= plus_num */ yytestcase(yyruleno==51); - /* (52) signed ::= minus_num */ yytestcase(yyruleno==52); - /* (53) carglist ::= carglist ccons */ yytestcase(yyruleno==53); - /* (54) carglist ::= */ yytestcase(yyruleno==54); - /* (61) ccons ::= NULL onconf */ yytestcase(yyruleno==61); - /* (89) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==89); - /* (90) conslist ::= tcons */ yytestcase(yyruleno==90); - /* (92) tconscomma ::= */ yytestcase(yyruleno==92); - /* (277) foreach_clause ::= */ yytestcase(yyruleno==277); - /* (278) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==278); - /* (285) tridxby ::= */ yytestcase(yyruleno==285); - /* (303) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==303); - /* (304) database_kw_opt ::= */ yytestcase(yyruleno==304); - /* (312) kwcolumn_opt ::= */ yytestcase(yyruleno==312); - /* (313) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==313); - /* (317) vtabarglist ::= vtabarg */ yytestcase(yyruleno==317); - /* (318) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==318); - /* (320) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==320); - /* (324) anylist ::= */ yytestcase(yyruleno==324); - /* (325) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==325); - /* (326) anylist ::= anylist ANY */ yytestcase(yyruleno==326); - break; - }; - assert( yyruleno>=0 && yyrulenoyyidx -= yysize; - yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); - if( yyact < YYNSTATE ){ -#ifdef NDEBUG - /* If we are not debugging and the reduce action popped at least - ** one element off the stack, then we can push the new element back - ** onto the stack here, and skip the stack overflow test in yy_shift(). - ** That gives a significant speed improvement. */ - if( yysize ){ - yypParser->yyidx++; - yymsp -= yysize-1; - yymsp->stateno = (YYACTIONTYPE)yyact; - yymsp->major = (YYCODETYPE)yygoto; - yymsp->minor = yygotominor; - }else -#endif - { - yy_shift(yypParser,yyact,yygoto,&yygotominor); - } - }else{ - assert( yyact == YYNSTATE + YYNRULE + 1 ); - yy_accept(yypParser); - } -} - -/* -** The following code executes when the parse fails -*/ -#ifndef YYNOERRORRECOVERY -static void yy_parse_failed( - yyParser *yypParser /* The parser */ -){ - sqlite3ParserARG_FETCH; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will be executed whenever the - ** parser fails */ - sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ -} -#endif /* YYNOERRORRECOVERY */ - -/* -** The following code executes when a syntax error first occurs. -*/ -static void yy_syntax_error( - yyParser *yypParser, /* The parser */ - int yymajor, /* The major type of the error token */ - YYMINORTYPE yyminor /* The minor type of the error token */ -){ - sqlite3ParserARG_FETCH; -#define TOKEN (yyminor.yy0) - - UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */ - assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */ - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); - sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ -} - -/* -** The following is executed when the parser accepts -*/ -static void yy_accept( - yyParser *yypParser /* The parser */ -){ - sqlite3ParserARG_FETCH; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will be executed whenever the - ** parser accepts */ - sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ -} - -/* The main parser program. -** The first argument is a pointer to a structure obtained from -** "sqlite3ParserAlloc" which describes the current state of the parser. -** The second argument is the major token number. The third is -** the minor token. The fourth optional argument is whatever the -** user wants (and specified in the grammar) and is available for -** use by the action routines. -** -** Inputs: -**
      -**
    • A pointer to the parser (an opaque structure.) -**
    • The major token number. -**
    • The minor token number. -**
    • An option argument of a grammar-specified type. -**
    -** -** Outputs: -** None. -*/ -SQLITE_PRIVATE void sqlite3Parser( - void *yyp, /* The parser */ - int yymajor, /* The major token code number */ - sqlite3ParserTOKENTYPE yyminor /* The value for the token */ - sqlite3ParserARG_PDECL /* Optional %extra_argument parameter */ -){ - YYMINORTYPE yyminorunion; - int yyact; /* The parser action. */ -#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) - int yyendofinput; /* True if we are at the end of input */ -#endif -#ifdef YYERRORSYMBOL - int yyerrorhit = 0; /* True if yymajor has invoked an error */ -#endif - yyParser *yypParser; /* The parser */ - - /* (re)initialize the parser, if necessary */ - yypParser = (yyParser*)yyp; - if( yypParser->yyidx<0 ){ -#if YYSTACKDEPTH<=0 - if( yypParser->yystksz <=0 ){ - /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ - yyminorunion = yyzerominor; - yyStackOverflow(yypParser, &yyminorunion); - return; - } -#endif - yypParser->yyidx = 0; - yypParser->yyerrcnt = -1; - yypParser->yystack[0].stateno = 0; - yypParser->yystack[0].major = 0; - } - yyminorunion.yy0 = yyminor; -#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) - yyendofinput = (yymajor==0); -#endif - sqlite3ParserARG_STORE; - -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); - } -#endif - - do{ - yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); - if( yyactyyerrcnt--; - yymajor = YYNOCODE; - }else if( yyact < YYNSTATE + YYNRULE ){ - yy_reduce(yypParser,yyact-YYNSTATE); - }else{ - assert( yyact == YY_ERROR_ACTION ); -#ifdef YYERRORSYMBOL - int yymx; -#endif -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); - } -#endif -#ifdef YYERRORSYMBOL - /* A syntax error has occurred. - ** The response to an error depends upon whether or not the - ** grammar defines an error token "ERROR". - ** - ** This is what we do if the grammar does define ERROR: - ** - ** * Call the %syntax_error function. - ** - ** * Begin popping the stack until we enter a state where - ** it is legal to shift the error symbol, then shift - ** the error symbol. - ** - ** * Set the error count to three. - ** - ** * Begin accepting and shifting new tokens. No new error - ** processing will occur until three tokens have been - ** shifted successfully. - ** - */ - if( yypParser->yyerrcnt<0 ){ - yy_syntax_error(yypParser,yymajor,yyminorunion); - } - yymx = yypParser->yystack[yypParser->yyidx].major; - if( yymx==YYERRORSYMBOL || yyerrorhit ){ -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sDiscard input token %s\n", - yyTracePrompt,yyTokenName[yymajor]); - } -#endif - yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); - yymajor = YYNOCODE; - }else{ - while( - yypParser->yyidx >= 0 && - yymx != YYERRORSYMBOL && - (yyact = yy_find_reduce_action( - yypParser->yystack[yypParser->yyidx].stateno, - YYERRORSYMBOL)) >= YYNSTATE - ){ - yy_pop_parser_stack(yypParser); - } - if( yypParser->yyidx < 0 || yymajor==0 ){ - yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); - yy_parse_failed(yypParser); - yymajor = YYNOCODE; - }else if( yymx!=YYERRORSYMBOL ){ - YYMINORTYPE u2; - u2.YYERRSYMDT = 0; - yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); - } - } - yypParser->yyerrcnt = 3; - yyerrorhit = 1; -#elif defined(YYNOERRORRECOVERY) - /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to - ** do any kind of error recovery. Instead, simply invoke the syntax - ** error routine and continue going as if nothing had happened. - ** - ** Applications can set this macro (for example inside %include) if - ** they intend to abandon the parse upon the first syntax error seen. - */ - yy_syntax_error(yypParser,yymajor,yyminorunion); - yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); - yymajor = YYNOCODE; - -#else /* YYERRORSYMBOL is not defined */ - /* This is what we do if the grammar does not define ERROR: - ** - ** * Report an error message, and throw away the input token. - ** - ** * If the input token is $, then fail the parse. - ** - ** As before, subsequent error messages are suppressed until - ** three input tokens have been successfully shifted. - */ - if( yypParser->yyerrcnt<=0 ){ - yy_syntax_error(yypParser,yymajor,yyminorunion); - } - yypParser->yyerrcnt = 3; - yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); - if( yyendofinput ){ - yy_parse_failed(yypParser); - } - yymajor = YYNOCODE; -#endif - } - }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); - return; -} - -/************** End of parse.c ***********************************************/ -/************** Begin file tokenize.c ****************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** An tokenizer for SQL -** -** This file contains C code that splits an SQL input string up into -** individual tokens and sends those tokens one-by-one over to the -** parser for analysis. -*/ -/* #include */ - -/* -** The charMap() macro maps alphabetic characters into their -** lower-case ASCII equivalent. On ASCII machines, this is just -** an upper-to-lower case map. On EBCDIC machines we also need -** to adjust the encoding. Only alphabetic characters and underscores -** need to be translated. -*/ -#ifdef SQLITE_ASCII -# define charMap(X) sqlite3UpperToLower[(unsigned char)X] -#endif -#ifdef SQLITE_EBCDIC -# define charMap(X) ebcdicToAscii[(unsigned char)X] -const unsigned char ebcdicToAscii[] = { -/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3x */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4x */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5x */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 0, /* 6x */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7x */ - 0, 97, 98, 99,100,101,102,103,104,105, 0, 0, 0, 0, 0, 0, /* 8x */ - 0,106,107,108,109,110,111,112,113,114, 0, 0, 0, 0, 0, 0, /* 9x */ - 0, 0,115,116,117,118,119,120,121,122, 0, 0, 0, 0, 0, 0, /* Ax */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Bx */ - 0, 97, 98, 99,100,101,102,103,104,105, 0, 0, 0, 0, 0, 0, /* Cx */ - 0,106,107,108,109,110,111,112,113,114, 0, 0, 0, 0, 0, 0, /* Dx */ - 0, 0,115,116,117,118,119,120,121,122, 0, 0, 0, 0, 0, 0, /* Ex */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Fx */ -}; -#endif - -/* -** The sqlite3KeywordCode function looks up an identifier to determine if -** it is a keyword. If it is a keyword, the token code of that keyword is -** returned. If the input is not a keyword, TK_ID is returned. -** -** The implementation of this routine was generated by a program, -** mkkeywordhash.h, located in the tool subdirectory of the distribution. -** The output of the mkkeywordhash.c program is written into a file -** named keywordhash.h and then included into this source file by -** the #include below. -*/ -/************** Include keywordhash.h in the middle of tokenize.c ************/ -/************** Begin file keywordhash.h *************************************/ -/***** This file contains automatically generated code ****** -** -** The code in this file has been automatically generated by -** -** sqlite/tool/mkkeywordhash.c -** -** The code in this file implements a function that determines whether -** or not a given identifier is really an SQL keyword. The same thing -** might be implemented more directly using a hand-written hash table. -** But by using this automatically generated code, the size of the code -** is substantially reduced. This is important for embedded applications -** on platforms with limited memory. -*/ -/* Hash score: 175 */ -static int keywordCode(const char *z, int n){ - /* zText[] encodes 811 bytes of keywords in 541 bytes */ - /* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */ - /* ABLEFTHENDEFERRABLELSEXCEPTRANSACTIONATURALTERAISEXCLUSIVE */ - /* XISTSAVEPOINTERSECTRIGGEREFERENCESCONSTRAINTOFFSETEMPORARY */ - /* UNIQUERYATTACHAVINGROUPDATEBEGINNERELEASEBETWEENOTNULLIKE */ - /* CASCADELETECASECOLLATECREATECURRENT_DATEDETACHIMMEDIATEJOIN */ - /* SERTMATCHPLANALYZEPRAGMABORTVALUESVIRTUALIMITWHENWHERENAME */ - /* AFTEREPLACEANDEFAULTAUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSS */ - /* CURRENT_TIMESTAMPRIMARYDEFERREDISTINCTDROPFAILFROMFULLGLOBYIF */ - /* ISNULLORDERESTRICTOUTERIGHTROLLBACKROWUNIONUSINGVACUUMVIEW */ - /* INITIALLY */ - static const char zText[540] = { - 'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H', - 'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G', - 'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A', - 'S','E','L','E','C','T','A','B','L','E','F','T','H','E','N','D','E','F', - 'E','R','R','A','B','L','E','L','S','E','X','C','E','P','T','R','A','N', - 'S','A','C','T','I','O','N','A','T','U','R','A','L','T','E','R','A','I', - 'S','E','X','C','L','U','S','I','V','E','X','I','S','T','S','A','V','E', - 'P','O','I','N','T','E','R','S','E','C','T','R','I','G','G','E','R','E', - 'F','E','R','E','N','C','E','S','C','O','N','S','T','R','A','I','N','T', - 'O','F','F','S','E','T','E','M','P','O','R','A','R','Y','U','N','I','Q', - 'U','E','R','Y','A','T','T','A','C','H','A','V','I','N','G','R','O','U', - 'P','D','A','T','E','B','E','G','I','N','N','E','R','E','L','E','A','S', - 'E','B','E','T','W','E','E','N','O','T','N','U','L','L','I','K','E','C', - 'A','S','C','A','D','E','L','E','T','E','C','A','S','E','C','O','L','L', - 'A','T','E','C','R','E','A','T','E','C','U','R','R','E','N','T','_','D', - 'A','T','E','D','E','T','A','C','H','I','M','M','E','D','I','A','T','E', - 'J','O','I','N','S','E','R','T','M','A','T','C','H','P','L','A','N','A', - 'L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','V','A','L','U', - 'E','S','V','I','R','T','U','A','L','I','M','I','T','W','H','E','N','W', - 'H','E','R','E','N','A','M','E','A','F','T','E','R','E','P','L','A','C', - 'E','A','N','D','E','F','A','U','L','T','A','U','T','O','I','N','C','R', - 'E','M','E','N','T','C','A','S','T','C','O','L','U','M','N','C','O','M', - 'M','I','T','C','O','N','F','L','I','C','T','C','R','O','S','S','C','U', - 'R','R','E','N','T','_','T','I','M','E','S','T','A','M','P','R','I','M', - 'A','R','Y','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T', - 'D','R','O','P','F','A','I','L','F','R','O','M','F','U','L','L','G','L', - 'O','B','Y','I','F','I','S','N','U','L','L','O','R','D','E','R','E','S', - 'T','R','I','C','T','O','U','T','E','R','I','G','H','T','R','O','L','L', - 'B','A','C','K','R','O','W','U','N','I','O','N','U','S','I','N','G','V', - 'A','C','U','U','M','V','I','E','W','I','N','I','T','I','A','L','L','Y', - }; - static const unsigned char aHash[127] = { - 72, 101, 114, 70, 0, 45, 0, 0, 78, 0, 73, 0, 0, - 42, 12, 74, 15, 0, 113, 81, 50, 108, 0, 19, 0, 0, - 118, 0, 116, 111, 0, 22, 89, 0, 9, 0, 0, 66, 67, - 0, 65, 6, 0, 48, 86, 98, 0, 115, 97, 0, 0, 44, - 0, 99, 24, 0, 17, 0, 119, 49, 23, 0, 5, 106, 25, - 92, 0, 0, 121, 102, 56, 120, 53, 28, 51, 0, 87, 0, - 96, 26, 0, 95, 0, 0, 0, 91, 88, 93, 84, 105, 14, - 39, 104, 0, 77, 0, 18, 85, 107, 32, 0, 117, 76, 109, - 58, 46, 80, 0, 0, 90, 40, 0, 112, 0, 36, 0, 0, - 29, 0, 82, 59, 60, 0, 20, 57, 0, 52, - }; - static const unsigned char aNext[121] = { - 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, - 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 33, 0, 21, 0, 0, 0, 43, 3, 47, - 0, 0, 0, 0, 30, 0, 54, 0, 38, 0, 0, 0, 1, - 62, 0, 0, 63, 0, 41, 0, 0, 0, 0, 0, 0, 0, - 61, 0, 0, 0, 0, 31, 55, 16, 34, 10, 0, 0, 0, - 0, 0, 0, 0, 11, 68, 75, 0, 8, 0, 100, 94, 0, - 103, 0, 83, 0, 71, 0, 0, 110, 27, 37, 69, 79, 0, - 35, 64, 0, 0, - }; - static const unsigned char aLen[121] = { - 7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6, - 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 6, - 11, 6, 2, 7, 5, 5, 9, 6, 9, 9, 7, 10, 10, - 4, 6, 2, 3, 9, 4, 2, 6, 5, 6, 6, 5, 6, - 5, 5, 7, 7, 7, 3, 2, 4, 4, 7, 3, 6, 4, - 7, 6, 12, 6, 9, 4, 6, 5, 4, 7, 6, 5, 6, - 7, 5, 4, 5, 6, 5, 7, 3, 7, 13, 2, 2, 4, - 6, 6, 8, 5, 17, 12, 7, 8, 8, 2, 4, 4, 4, - 4, 4, 2, 2, 6, 5, 8, 5, 5, 8, 3, 5, 5, - 6, 4, 9, 3, - }; - static const unsigned short int aOffset[121] = { - 0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33, - 36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81, - 86, 91, 95, 96, 101, 105, 109, 117, 122, 128, 136, 142, 152, - 159, 162, 162, 165, 167, 167, 171, 176, 179, 184, 189, 194, 197, - 203, 206, 210, 217, 223, 223, 223, 226, 229, 233, 234, 238, 244, - 248, 255, 261, 273, 279, 288, 290, 296, 301, 303, 310, 315, 320, - 326, 332, 337, 341, 344, 350, 354, 361, 363, 370, 372, 374, 383, - 387, 393, 399, 407, 412, 412, 428, 435, 442, 443, 450, 454, 458, - 462, 466, 469, 471, 473, 479, 483, 491, 495, 500, 508, 511, 516, - 521, 527, 531, 536, - }; - static const unsigned char aCode[121] = { - TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE, - TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN, - TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD, - TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE, - TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE, - TK_EXCEPT, TK_TRANSACTION,TK_ACTION, TK_ON, TK_JOIN_KW, - TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_SAVEPOINT, - TK_INTERSECT, TK_TRIGGER, TK_REFERENCES, TK_CONSTRAINT, TK_INTO, - TK_OFFSET, TK_OF, TK_SET, TK_TEMP, TK_TEMP, - TK_OR, TK_UNIQUE, TK_QUERY, TK_ATTACH, TK_HAVING, - TK_GROUP, TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RELEASE, - TK_BETWEEN, TK_NOTNULL, TK_NOT, TK_NO, TK_NULL, - TK_LIKE_KW, TK_CASCADE, TK_ASC, TK_DELETE, TK_CASE, - TK_COLLATE, TK_CREATE, TK_CTIME_KW, TK_DETACH, TK_IMMEDIATE, - TK_JOIN, TK_INSERT, TK_MATCH, TK_PLAN, TK_ANALYZE, - TK_PRAGMA, TK_ABORT, TK_VALUES, TK_VIRTUAL, TK_LIMIT, - TK_WHEN, TK_WHERE, TK_RENAME, TK_AFTER, TK_REPLACE, - TK_AND, TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN, - TK_CAST, TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, - TK_CTIME_KW, TK_CTIME_KW, TK_PRIMARY, TK_DEFERRED, TK_DISTINCT, - TK_IS, TK_DROP, TK_FAIL, TK_FROM, TK_JOIN_KW, - TK_LIKE_KW, TK_BY, TK_IF, TK_ISNULL, TK_ORDER, - TK_RESTRICT, TK_JOIN_KW, TK_JOIN_KW, TK_ROLLBACK, TK_ROW, - TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_INITIALLY, - TK_ALL, - }; - int h, i; - if( n<2 ) return TK_ID; - h = ((charMap(z[0])*4) ^ - (charMap(z[n-1])*3) ^ - n) % 127; - for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){ - if( aLen[i]==n && sqlite3StrNICmp(&zText[aOffset[i]],z,n)==0 ){ - testcase( i==0 ); /* REINDEX */ - testcase( i==1 ); /* INDEXED */ - testcase( i==2 ); /* INDEX */ - testcase( i==3 ); /* DESC */ - testcase( i==4 ); /* ESCAPE */ - testcase( i==5 ); /* EACH */ - testcase( i==6 ); /* CHECK */ - testcase( i==7 ); /* KEY */ - testcase( i==8 ); /* BEFORE */ - testcase( i==9 ); /* FOREIGN */ - testcase( i==10 ); /* FOR */ - testcase( i==11 ); /* IGNORE */ - testcase( i==12 ); /* REGEXP */ - testcase( i==13 ); /* EXPLAIN */ - testcase( i==14 ); /* INSTEAD */ - testcase( i==15 ); /* ADD */ - testcase( i==16 ); /* DATABASE */ - testcase( i==17 ); /* AS */ - testcase( i==18 ); /* SELECT */ - testcase( i==19 ); /* TABLE */ - testcase( i==20 ); /* LEFT */ - testcase( i==21 ); /* THEN */ - testcase( i==22 ); /* END */ - testcase( i==23 ); /* DEFERRABLE */ - testcase( i==24 ); /* ELSE */ - testcase( i==25 ); /* EXCEPT */ - testcase( i==26 ); /* TRANSACTION */ - testcase( i==27 ); /* ACTION */ - testcase( i==28 ); /* ON */ - testcase( i==29 ); /* NATURAL */ - testcase( i==30 ); /* ALTER */ - testcase( i==31 ); /* RAISE */ - testcase( i==32 ); /* EXCLUSIVE */ - testcase( i==33 ); /* EXISTS */ - testcase( i==34 ); /* SAVEPOINT */ - testcase( i==35 ); /* INTERSECT */ - testcase( i==36 ); /* TRIGGER */ - testcase( i==37 ); /* REFERENCES */ - testcase( i==38 ); /* CONSTRAINT */ - testcase( i==39 ); /* INTO */ - testcase( i==40 ); /* OFFSET */ - testcase( i==41 ); /* OF */ - testcase( i==42 ); /* SET */ - testcase( i==43 ); /* TEMPORARY */ - testcase( i==44 ); /* TEMP */ - testcase( i==45 ); /* OR */ - testcase( i==46 ); /* UNIQUE */ - testcase( i==47 ); /* QUERY */ - testcase( i==48 ); /* ATTACH */ - testcase( i==49 ); /* HAVING */ - testcase( i==50 ); /* GROUP */ - testcase( i==51 ); /* UPDATE */ - testcase( i==52 ); /* BEGIN */ - testcase( i==53 ); /* INNER */ - testcase( i==54 ); /* RELEASE */ - testcase( i==55 ); /* BETWEEN */ - testcase( i==56 ); /* NOTNULL */ - testcase( i==57 ); /* NOT */ - testcase( i==58 ); /* NO */ - testcase( i==59 ); /* NULL */ - testcase( i==60 ); /* LIKE */ - testcase( i==61 ); /* CASCADE */ - testcase( i==62 ); /* ASC */ - testcase( i==63 ); /* DELETE */ - testcase( i==64 ); /* CASE */ - testcase( i==65 ); /* COLLATE */ - testcase( i==66 ); /* CREATE */ - testcase( i==67 ); /* CURRENT_DATE */ - testcase( i==68 ); /* DETACH */ - testcase( i==69 ); /* IMMEDIATE */ - testcase( i==70 ); /* JOIN */ - testcase( i==71 ); /* INSERT */ - testcase( i==72 ); /* MATCH */ - testcase( i==73 ); /* PLAN */ - testcase( i==74 ); /* ANALYZE */ - testcase( i==75 ); /* PRAGMA */ - testcase( i==76 ); /* ABORT */ - testcase( i==77 ); /* VALUES */ - testcase( i==78 ); /* VIRTUAL */ - testcase( i==79 ); /* LIMIT */ - testcase( i==80 ); /* WHEN */ - testcase( i==81 ); /* WHERE */ - testcase( i==82 ); /* RENAME */ - testcase( i==83 ); /* AFTER */ - testcase( i==84 ); /* REPLACE */ - testcase( i==85 ); /* AND */ - testcase( i==86 ); /* DEFAULT */ - testcase( i==87 ); /* AUTOINCREMENT */ - testcase( i==88 ); /* TO */ - testcase( i==89 ); /* IN */ - testcase( i==90 ); /* CAST */ - testcase( i==91 ); /* COLUMN */ - testcase( i==92 ); /* COMMIT */ - testcase( i==93 ); /* CONFLICT */ - testcase( i==94 ); /* CROSS */ - testcase( i==95 ); /* CURRENT_TIMESTAMP */ - testcase( i==96 ); /* CURRENT_TIME */ - testcase( i==97 ); /* PRIMARY */ - testcase( i==98 ); /* DEFERRED */ - testcase( i==99 ); /* DISTINCT */ - testcase( i==100 ); /* IS */ - testcase( i==101 ); /* DROP */ - testcase( i==102 ); /* FAIL */ - testcase( i==103 ); /* FROM */ - testcase( i==104 ); /* FULL */ - testcase( i==105 ); /* GLOB */ - testcase( i==106 ); /* BY */ - testcase( i==107 ); /* IF */ - testcase( i==108 ); /* ISNULL */ - testcase( i==109 ); /* ORDER */ - testcase( i==110 ); /* RESTRICT */ - testcase( i==111 ); /* OUTER */ - testcase( i==112 ); /* RIGHT */ - testcase( i==113 ); /* ROLLBACK */ - testcase( i==114 ); /* ROW */ - testcase( i==115 ); /* UNION */ - testcase( i==116 ); /* USING */ - testcase( i==117 ); /* VACUUM */ - testcase( i==118 ); /* VIEW */ - testcase( i==119 ); /* INITIALLY */ - testcase( i==120 ); /* ALL */ - return aCode[i]; - } - } - return TK_ID; -} -SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){ - return keywordCode((char*)z, n); -} -#define SQLITE_N_KEYWORD 121 - -/************** End of keywordhash.h *****************************************/ -/************** Continuing where we left off in tokenize.c *******************/ - - -/* -** If X is a character that can be used in an identifier then -** IdChar(X) will be true. Otherwise it is false. -** -** For ASCII, any character with the high-order bit set is -** allowed in an identifier. For 7-bit characters, -** sqlite3IsIdChar[X] must be 1. -** -** For EBCDIC, the rules are more complex but have the same -** end result. -** -** Ticket #1066. the SQL standard does not allow '$' in the -** middle of identfiers. But many SQL implementations do. -** SQLite will allow '$' in identifiers for compatibility. -** But the feature is undocumented. -*/ -#ifdef SQLITE_ASCII -#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0) -#endif -#ifdef SQLITE_EBCDIC -SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[] = { -/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 4x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, /* 5x */ - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 6x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, /* 7x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, /* 8x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, /* 9x */ - 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, /* Ax */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Bx */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Cx */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Dx */ - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Ex */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, /* Fx */ -}; -#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40])) -#endif - - -/* -** Return the length of the token that begins at z[0]. -** Store the token type in *tokenType before returning. -*/ -SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ - int i, c; - switch( *z ){ - case ' ': case '\t': case '\n': case '\f': case '\r': { - testcase( z[0]==' ' ); - testcase( z[0]=='\t' ); - testcase( z[0]=='\n' ); - testcase( z[0]=='\f' ); - testcase( z[0]=='\r' ); - for(i=1; sqlite3Isspace(z[i]); i++){} - *tokenType = TK_SPACE; - return i; - } - case '-': { - if( z[1]=='-' ){ - /* IMP: R-50417-27976 -- syntax diagram for comments */ - for(i=2; (c=z[i])!=0 && c!='\n'; i++){} - *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ - return i; - } - *tokenType = TK_MINUS; - return 1; - } - case '(': { - *tokenType = TK_LP; - return 1; - } - case ')': { - *tokenType = TK_RP; - return 1; - } - case ';': { - *tokenType = TK_SEMI; - return 1; - } - case '+': { - *tokenType = TK_PLUS; - return 1; - } - case '*': { - *tokenType = TK_STAR; - return 1; - } - case '/': { - if( z[1]!='*' || z[2]==0 ){ - *tokenType = TK_SLASH; - return 1; - } - /* IMP: R-50417-27976 -- syntax diagram for comments */ - for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} - if( c ) i++; - *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ - return i; - } - case '%': { - *tokenType = TK_REM; - return 1; - } - case '=': { - *tokenType = TK_EQ; - return 1 + (z[1]=='='); - } - case '<': { - if( (c=z[1])=='=' ){ - *tokenType = TK_LE; - return 2; - }else if( c=='>' ){ - *tokenType = TK_NE; - return 2; - }else if( c=='<' ){ - *tokenType = TK_LSHIFT; - return 2; - }else{ - *tokenType = TK_LT; - return 1; - } - } - case '>': { - if( (c=z[1])=='=' ){ - *tokenType = TK_GE; - return 2; - }else if( c=='>' ){ - *tokenType = TK_RSHIFT; - return 2; - }else{ - *tokenType = TK_GT; - return 1; - } - } - case '!': { - if( z[1]!='=' ){ - *tokenType = TK_ILLEGAL; - return 2; - }else{ - *tokenType = TK_NE; - return 2; - } - } - case '|': { - if( z[1]!='|' ){ - *tokenType = TK_BITOR; - return 1; - }else{ - *tokenType = TK_CONCAT; - return 2; - } - } - case ',': { - *tokenType = TK_COMMA; - return 1; - } - case '&': { - *tokenType = TK_BITAND; - return 1; - } - case '~': { - *tokenType = TK_BITNOT; - return 1; - } - case '`': - case '\'': - case '"': { - int delim = z[0]; - testcase( delim=='`' ); - testcase( delim=='\'' ); - testcase( delim=='"' ); - for(i=1; (c=z[i])!=0; i++){ - if( c==delim ){ - if( z[i+1]==delim ){ - i++; - }else{ - break; - } - } - } - if( c=='\'' ){ - *tokenType = TK_STRING; - return i+1; - }else if( c!=0 ){ - *tokenType = TK_ID; - return i+1; - }else{ - *tokenType = TK_ILLEGAL; - return i; - } - } - case '.': { -#ifndef SQLITE_OMIT_FLOATING_POINT - if( !sqlite3Isdigit(z[1]) ) -#endif - { - *tokenType = TK_DOT; - return 1; - } - /* If the next character is a digit, this is a floating point - ** number that begins with ".". Fall thru into the next case */ - } - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': { - testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); - testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); - testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); - testcase( z[0]=='9' ); - *tokenType = TK_INTEGER; - for(i=0; sqlite3Isdigit(z[i]); i++){} -#ifndef SQLITE_OMIT_FLOATING_POINT - if( z[i]=='.' ){ - i++; - while( sqlite3Isdigit(z[i]) ){ i++; } - *tokenType = TK_FLOAT; - } - if( (z[i]=='e' || z[i]=='E') && - ( sqlite3Isdigit(z[i+1]) - || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) - ) - ){ - i += 2; - while( sqlite3Isdigit(z[i]) ){ i++; } - *tokenType = TK_FLOAT; - } -#endif - while( IdChar(z[i]) ){ - *tokenType = TK_ILLEGAL; - i++; - } - return i; - } - case '[': { - for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){} - *tokenType = c==']' ? TK_ID : TK_ILLEGAL; - return i; - } - case '?': { - *tokenType = TK_VARIABLE; - for(i=1; sqlite3Isdigit(z[i]); i++){} - return i; - } - case '#': { - for(i=1; sqlite3Isdigit(z[i]); i++){} - if( i>1 ){ - /* Parameters of the form #NNN (where NNN is a number) are used - ** internally by sqlite3NestedParse. */ - *tokenType = TK_REGISTER; - return i; - } - /* Fall through into the next case if the '#' is not followed by - ** a digit. Try to match #AAAA where AAAA is a parameter name. */ - } -#ifndef SQLITE_OMIT_TCL_VARIABLE - case '$': -#endif - case '@': /* For compatibility with MS SQL Server */ - case ':': { - int n = 0; - testcase( z[0]=='$' ); testcase( z[0]=='@' ); testcase( z[0]==':' ); - *tokenType = TK_VARIABLE; - for(i=1; (c=z[i])!=0; i++){ - if( IdChar(c) ){ - n++; -#ifndef SQLITE_OMIT_TCL_VARIABLE - }else if( c=='(' && n>0 ){ - do{ - i++; - }while( (c=z[i])!=0 && !sqlite3Isspace(c) && c!=')' ); - if( c==')' ){ - i++; - }else{ - *tokenType = TK_ILLEGAL; - } - break; - }else if( c==':' && z[i+1]==':' ){ - i++; -#endif - }else{ - break; - } - } - if( n==0 ) *tokenType = TK_ILLEGAL; - return i; - } -#ifndef SQLITE_OMIT_BLOB_LITERAL - case 'x': case 'X': { - testcase( z[0]=='x' ); testcase( z[0]=='X' ); - if( z[1]=='\'' ){ - *tokenType = TK_BLOB; - for(i=2; sqlite3Isxdigit(z[i]); i++){} - if( z[i]!='\'' || i%2 ){ - *tokenType = TK_ILLEGAL; - while( z[i] && z[i]!='\'' ){ i++; } - } - if( z[i] ) i++; - return i; - } - /* Otherwise fall through to the next case */ - } -#endif - default: { - if( !IdChar(*z) ){ - break; - } - for(i=1; IdChar(z[i]); i++){} - *tokenType = keywordCode((char*)z, i); - return i; - } - } - *tokenType = TK_ILLEGAL; - return 1; -} - -/* -** Run the parser on the given SQL string. The parser structure is -** passed in. An SQLITE_ status code is returned. If an error occurs -** then an and attempt is made to write an error message into -** memory obtained from sqlite3_malloc() and to make *pzErrMsg point to that -** error message. -*/ -SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ - int nErr = 0; /* Number of errors encountered */ - int i; /* Loop counter */ - void *pEngine; /* The LEMON-generated LALR(1) parser */ - int tokenType; /* type of the next token */ - int lastTokenParsed = -1; /* type of the previous token */ - u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */ - sqlite3 *db = pParse->db; /* The database connection */ - int mxSqlLen; /* Max length of an SQL string */ - - - mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; - if( db->activeVdbeCnt==0 ){ - db->u1.isInterrupted = 0; - } - pParse->rc = SQLITE_OK; - pParse->zTail = zSql; - i = 0; - assert( pzErrMsg!=0 ); - pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc); - if( pEngine==0 ){ - db->mallocFailed = 1; - return SQLITE_NOMEM; - } - assert( pParse->pNewTable==0 ); - assert( pParse->pNewTrigger==0 ); - assert( pParse->nVar==0 ); - assert( pParse->nzVar==0 ); - assert( pParse->azVar==0 ); - enableLookaside = db->lookaside.bEnabled; - if( db->lookaside.pStart ) db->lookaside.bEnabled = 1; - while( !db->mallocFailed && zSql[i]!=0 ){ - assert( i>=0 ); - pParse->sLastToken.z = &zSql[i]; - pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType); - i += pParse->sLastToken.n; - if( i>mxSqlLen ){ - pParse->rc = SQLITE_TOOBIG; - break; - } - switch( tokenType ){ - case TK_SPACE: { - if( db->u1.isInterrupted ){ - sqlite3ErrorMsg(pParse, "interrupt"); - pParse->rc = SQLITE_INTERRUPT; - goto abort_parse; - } - break; - } - case TK_ILLEGAL: { - sqlite3DbFree(db, *pzErrMsg); - *pzErrMsg = sqlite3MPrintf(db, "unrecognized token: \"%T\"", - &pParse->sLastToken); - nErr++; - goto abort_parse; - } - case TK_SEMI: { - pParse->zTail = &zSql[i]; - /* Fall thru into the default case */ - } - default: { - sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse); - lastTokenParsed = tokenType; - if( pParse->rc!=SQLITE_OK ){ - goto abort_parse; - } - break; - } - } - } -abort_parse: - if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){ - if( lastTokenParsed!=TK_SEMI ){ - sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse); - pParse->zTail = &zSql[i]; - } - sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse); - } -#ifdef YYTRACKMAXSTACKDEPTH - sqlite3StatusSet(SQLITE_STATUS_PARSER_STACK, - sqlite3ParserStackPeak(pEngine) - ); -#endif /* YYDEBUG */ - sqlite3ParserFree(pEngine, sqlite3_free); - db->lookaside.bEnabled = enableLookaside; - if( db->mallocFailed ){ - pParse->rc = SQLITE_NOMEM; - } - if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ - sqlite3SetString(&pParse->zErrMsg, db, "%s", sqlite3ErrStr(pParse->rc)); - } - assert( pzErrMsg!=0 ); - if( pParse->zErrMsg ){ - *pzErrMsg = pParse->zErrMsg; - sqlite3_log(pParse->rc, "%s", *pzErrMsg); - pParse->zErrMsg = 0; - nErr++; - } - if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){ - sqlite3VdbeDelete(pParse->pVdbe); - pParse->pVdbe = 0; - } -#ifndef SQLITE_OMIT_SHARED_CACHE - if( pParse->nested==0 ){ - sqlite3DbFree(db, pParse->aTableLock); - pParse->aTableLock = 0; - pParse->nTableLock = 0; - } -#endif -#ifndef SQLITE_OMIT_VIRTUALTABLE - sqlite3_free(pParse->apVtabLock); -#endif - - if( !IN_DECLARE_VTAB ){ - /* If the pParse->declareVtab flag is set, do not delete any table - ** structure built up in pParse->pNewTable. The calling code (see vtab.c) - ** will take responsibility for freeing the Table structure. - */ - sqlite3DeleteTable(db, pParse->pNewTable); - } - - sqlite3DeleteTrigger(db, pParse->pNewTrigger); - for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]); - sqlite3DbFree(db, pParse->azVar); - sqlite3DbFree(db, pParse->aAlias); - while( pParse->pAinc ){ - AutoincInfo *p = pParse->pAinc; - pParse->pAinc = p->pNext; - sqlite3DbFree(db, p); - } - while( pParse->pZombieTab ){ - Table *p = pParse->pZombieTab; - pParse->pZombieTab = p->pNextZombie; - sqlite3DeleteTable(db, p); - } - if( nErr>0 && pParse->rc==SQLITE_OK ){ - pParse->rc = SQLITE_ERROR; - } - return nErr; -} - -/************** End of tokenize.c ********************************************/ -/************** Begin file complete.c ****************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** An tokenizer for SQL -** -** This file contains C code that implements the sqlite3_complete() API. -** This code used to be part of the tokenizer.c source file. But by -** separating it out, the code will be automatically omitted from -** static links that do not use it. -*/ -#ifndef SQLITE_OMIT_COMPLETE - -/* -** This is defined in tokenize.c. We just have to import the definition. -*/ -#ifndef SQLITE_AMALGAMATION -#ifdef SQLITE_ASCII -#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0) -#endif -#ifdef SQLITE_EBCDIC -SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[]; -#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40])) -#endif -#endif /* SQLITE_AMALGAMATION */ - - -/* -** Token types used by the sqlite3_complete() routine. See the header -** comments on that procedure for additional information. -*/ -#define tkSEMI 0 -#define tkWS 1 -#define tkOTHER 2 -#ifndef SQLITE_OMIT_TRIGGER -#define tkEXPLAIN 3 -#define tkCREATE 4 -#define tkTEMP 5 -#define tkTRIGGER 6 -#define tkEND 7 -#endif - -/* -** Return TRUE if the given SQL string ends in a semicolon. -** -** Special handling is require for CREATE TRIGGER statements. -** Whenever the CREATE TRIGGER keywords are seen, the statement -** must end with ";END;". -** -** This implementation uses a state machine with 8 states: -** -** (0) INVALID We have not yet seen a non-whitespace character. -** -** (1) START At the beginning or end of an SQL statement. This routine -** returns 1 if it ends in the START state and 0 if it ends -** in any other state. -** -** (2) NORMAL We are in the middle of statement which ends with a single -** semicolon. -** -** (3) EXPLAIN The keyword EXPLAIN has been seen at the beginning of -** a statement. -** -** (4) CREATE The keyword CREATE has been seen at the beginning of a -** statement, possibly preceeded by EXPLAIN and/or followed by -** TEMP or TEMPORARY -** -** (5) TRIGGER We are in the middle of a trigger definition that must be -** ended by a semicolon, the keyword END, and another semicolon. -** -** (6) SEMI We've seen the first semicolon in the ";END;" that occurs at -** the end of a trigger definition. -** -** (7) END We've seen the ";END" of the ";END;" that occurs at the end -** of a trigger difinition. -** -** Transitions between states above are determined by tokens extracted -** from the input. The following tokens are significant: -** -** (0) tkSEMI A semicolon. -** (1) tkWS Whitespace. -** (2) tkOTHER Any other SQL token. -** (3) tkEXPLAIN The "explain" keyword. -** (4) tkCREATE The "create" keyword. -** (5) tkTEMP The "temp" or "temporary" keyword. -** (6) tkTRIGGER The "trigger" keyword. -** (7) tkEND The "end" keyword. -** -** Whitespace never causes a state transition and is always ignored. -** This means that a SQL string of all whitespace is invalid. -** -** If we compile with SQLITE_OMIT_TRIGGER, all of the computation needed -** to recognize the end of a trigger can be omitted. All we have to do -** is look for a semicolon that is not part of an string or comment. -*/ -SQLITE_API int sqlite3_complete(const char *zSql){ - u8 state = 0; /* Current state, using numbers defined in header comment */ - u8 token; /* Value of the next token */ - -#ifndef SQLITE_OMIT_TRIGGER - /* A complex statement machine used to detect the end of a CREATE TRIGGER - ** statement. This is the normal case. - */ - static const u8 trans[8][8] = { - /* Token: */ - /* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */ - /* 0 INVALID: */ { 1, 0, 2, 3, 4, 2, 2, 2, }, - /* 1 START: */ { 1, 1, 2, 3, 4, 2, 2, 2, }, - /* 2 NORMAL: */ { 1, 2, 2, 2, 2, 2, 2, 2, }, - /* 3 EXPLAIN: */ { 1, 3, 3, 2, 4, 2, 2, 2, }, - /* 4 CREATE: */ { 1, 4, 2, 2, 2, 4, 5, 2, }, - /* 5 TRIGGER: */ { 6, 5, 5, 5, 5, 5, 5, 5, }, - /* 6 SEMI: */ { 6, 6, 5, 5, 5, 5, 5, 7, }, - /* 7 END: */ { 1, 7, 5, 5, 5, 5, 5, 5, }, - }; -#else - /* If triggers are not supported by this compile then the statement machine - ** used to detect the end of a statement is much simplier - */ - static const u8 trans[3][3] = { - /* Token: */ - /* State: ** SEMI WS OTHER */ - /* 0 INVALID: */ { 1, 0, 2, }, - /* 1 START: */ { 1, 1, 2, }, - /* 2 NORMAL: */ { 1, 2, 2, }, - }; -#endif /* SQLITE_OMIT_TRIGGER */ - - while( *zSql ){ - switch( *zSql ){ - case ';': { /* A semicolon */ - token = tkSEMI; - break; - } - case ' ': - case '\r': - case '\t': - case '\n': - case '\f': { /* White space is ignored */ - token = tkWS; - break; - } - case '/': { /* C-style comments */ - if( zSql[1]!='*' ){ - token = tkOTHER; - break; - } - zSql += 2; - while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; } - if( zSql[0]==0 ) return 0; - zSql++; - token = tkWS; - break; - } - case '-': { /* SQL-style comments from "--" to end of line */ - if( zSql[1]!='-' ){ - token = tkOTHER; - break; - } - while( *zSql && *zSql!='\n' ){ zSql++; } - if( *zSql==0 ) return state==1; - token = tkWS; - break; - } - case '[': { /* Microsoft-style identifiers in [...] */ - zSql++; - while( *zSql && *zSql!=']' ){ zSql++; } - if( *zSql==0 ) return 0; - token = tkOTHER; - break; - } - case '`': /* Grave-accent quoted symbols used by MySQL */ - case '"': /* single- and double-quoted strings */ - case '\'': { - int c = *zSql; - zSql++; - while( *zSql && *zSql!=c ){ zSql++; } - if( *zSql==0 ) return 0; - token = tkOTHER; - break; - } - default: { -#ifdef SQLITE_EBCDIC - unsigned char c; -#endif - if( IdChar((u8)*zSql) ){ - /* Keywords and unquoted identifiers */ - int nId; - for(nId=1; IdChar(zSql[nId]); nId++){} -#ifdef SQLITE_OMIT_TRIGGER - token = tkOTHER; -#else - switch( *zSql ){ - case 'c': case 'C': { - if( nId==6 && sqlite3StrNICmp(zSql, "create", 6)==0 ){ - token = tkCREATE; - }else{ - token = tkOTHER; - } - break; - } - case 't': case 'T': { - if( nId==7 && sqlite3StrNICmp(zSql, "trigger", 7)==0 ){ - token = tkTRIGGER; - }else if( nId==4 && sqlite3StrNICmp(zSql, "temp", 4)==0 ){ - token = tkTEMP; - }else if( nId==9 && sqlite3StrNICmp(zSql, "temporary", 9)==0 ){ - token = tkTEMP; - }else{ - token = tkOTHER; - } - break; - } - case 'e': case 'E': { - if( nId==3 && sqlite3StrNICmp(zSql, "end", 3)==0 ){ - token = tkEND; - }else -#ifndef SQLITE_OMIT_EXPLAIN - if( nId==7 && sqlite3StrNICmp(zSql, "explain", 7)==0 ){ - token = tkEXPLAIN; - }else -#endif - { - token = tkOTHER; - } - break; - } - default: { - token = tkOTHER; - break; - } - } -#endif /* SQLITE_OMIT_TRIGGER */ - zSql += nId-1; - }else{ - /* Operators and special symbols */ - token = tkOTHER; - } - break; - } - } - state = trans[state][token]; - zSql++; - } - return state==1; -} - -#ifndef SQLITE_OMIT_UTF16 -/* -** This routine is the same as the sqlite3_complete() routine described -** above, except that the parameter is required to be UTF-16 encoded, not -** UTF-8. -*/ -SQLITE_API int sqlite3_complete16(const void *zSql){ - sqlite3_value *pVal; - char const *zSql8; - int rc = SQLITE_NOMEM; - -#ifndef SQLITE_OMIT_AUTOINIT - rc = sqlite3_initialize(); - if( rc ) return rc; -#endif - pVal = sqlite3ValueNew(0); - sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC); - zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8); - if( zSql8 ){ - rc = sqlite3_complete(zSql8); - }else{ - rc = SQLITE_NOMEM; - } - sqlite3ValueFree(pVal); - return sqlite3ApiExit(0, rc); -} -#endif /* SQLITE_OMIT_UTF16 */ -#endif /* SQLITE_OMIT_COMPLETE */ - -/************** End of complete.c ********************************************/ -/************** Begin file main.c ********************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Main file for the SQLite library. The routines in this file -** implement the programmer interface to the library. Routines in -** other files are for internal use by SQLite and should not be -** accessed by users of the library. -*/ - -#ifdef SQLITE_ENABLE_FTS3 -/************** Include fts3.h in the middle of main.c ***********************/ -/************** Begin file fts3.h ********************************************/ -/* -** 2006 Oct 10 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This header file is used by programs that want to link against the -** FTS3 library. All it does is declare the sqlite3Fts3Init() interface. -*/ - -#if 0 -extern "C" { -#endif /* __cplusplus */ - -SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db); - -#if 0 -} /* extern "C" */ -#endif /* __cplusplus */ - -/************** End of fts3.h ************************************************/ -/************** Continuing where we left off in main.c ***********************/ -#endif -#ifdef SQLITE_ENABLE_RTREE -/************** Include rtree.h in the middle of main.c **********************/ -/************** Begin file rtree.h *******************************************/ -/* -** 2008 May 26 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This header file is used by programs that want to link against the -** RTREE library. All it does is declare the sqlite3RtreeInit() interface. -*/ - -#if 0 -extern "C" { -#endif /* __cplusplus */ - -SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db); - -#if 0 -} /* extern "C" */ -#endif /* __cplusplus */ - -/************** End of rtree.h ***********************************************/ -/************** Continuing where we left off in main.c ***********************/ -#endif -#ifdef SQLITE_ENABLE_ICU -/************** Include sqliteicu.h in the middle of main.c ******************/ -/************** Begin file sqliteicu.h ***************************************/ -/* -** 2008 May 26 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This header file is used by programs that want to link against the -** ICU extension. All it does is declare the sqlite3IcuInit() interface. -*/ - -#if 0 -extern "C" { -#endif /* __cplusplus */ - -SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db); - -#if 0 -} /* extern "C" */ -#endif /* __cplusplus */ - - -/************** End of sqliteicu.h *******************************************/ -/************** Continuing where we left off in main.c ***********************/ -#endif - -#ifndef SQLITE_AMALGAMATION -/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant -** contains the text of SQLITE_VERSION macro. -*/ -SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; -#endif - -/* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns -** a pointer to the to the sqlite3_version[] string constant. -*/ -SQLITE_API const char *sqlite3_libversion(void){ return sqlite3_version; } - -/* IMPLEMENTATION-OF: R-63124-39300 The sqlite3_sourceid() function returns a -** pointer to a string constant whose value is the same as the -** SQLITE_SOURCE_ID C preprocessor macro. -*/ -SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } - -/* IMPLEMENTATION-OF: R-35210-63508 The sqlite3_libversion_number() function -** returns an integer equal to SQLITE_VERSION_NUMBER. -*/ -SQLITE_API int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } - -/* IMPLEMENTATION-OF: R-20790-14025 The sqlite3_threadsafe() function returns -** zero if and only if SQLite was compiled with mutexing code omitted due to -** the SQLITE_THREADSAFE compile-time option being set to 0. -*/ -SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } - -#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) -/* -** If the following function pointer is not NULL and if -** SQLITE_ENABLE_IOTRACE is enabled, then messages describing -** I/O active are written using this function. These messages -** are intended for debugging activity only. -*/ -SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*, ...) = 0; -#endif - -/* -** If the following global variable points to a string which is the -** name of a directory, then that directory will be used to store -** temporary files. -** -** See also the "PRAGMA temp_store_directory" SQL command. -*/ -SQLITE_API char *sqlite3_temp_directory = 0; - -/* -** If the following global variable points to a string which is the -** name of a directory, then that directory will be used to store -** all database files specified with a relative pathname. -** -** See also the "PRAGMA data_store_directory" SQL command. -*/ -SQLITE_API char *sqlite3_data_directory = 0; - -/* -** Initialize SQLite. -** -** This routine must be called to initialize the memory allocation, -** VFS, and mutex subsystems prior to doing any serious work with -** SQLite. But as long as you do not compile with SQLITE_OMIT_AUTOINIT -** this routine will be called automatically by key routines such as -** sqlite3_open(). -** -** This routine is a no-op except on its very first call for the process, -** or for the first call after a call to sqlite3_shutdown. -** -** The first thread to call this routine runs the initialization to -** completion. If subsequent threads call this routine before the first -** thread has finished the initialization process, then the subsequent -** threads must block until the first thread finishes with the initialization. -** -** The first thread might call this routine recursively. Recursive -** calls to this routine should not block, of course. Otherwise the -** initialization process would never complete. -** -** Let X be the first thread to enter this routine. Let Y be some other -** thread. Then while the initial invocation of this routine by X is -** incomplete, it is required that: -** -** * Calls to this routine from Y must block until the outer-most -** call by X completes. -** -** * Recursive calls to this routine from thread X return immediately -** without blocking. -*/ -SQLITE_API int sqlite3_initialize(void){ - MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ - int rc; /* Result code */ - -#ifdef SQLITE_OMIT_WSD - rc = sqlite3_wsd_init(4096, 24); - if( rc!=SQLITE_OK ){ - return rc; - } -#endif - - /* If SQLite is already completely initialized, then this call - ** to sqlite3_initialize() should be a no-op. But the initialization - ** must be complete. So isInit must not be set until the very end - ** of this routine. - */ - if( sqlite3GlobalConfig.isInit ) return SQLITE_OK; - -#ifdef SQLITE_ENABLE_SQLLOG - { - extern void sqlite3_init_sqllog(void); - sqlite3_init_sqllog(); - } -#endif - - /* Make sure the mutex subsystem is initialized. If unable to - ** initialize the mutex subsystem, return early with the error. - ** If the system is so sick that we are unable to allocate a mutex, - ** there is not much SQLite is going to be able to do. - ** - ** The mutex subsystem must take care of serializing its own - ** initialization. - */ - rc = sqlite3MutexInit(); - if( rc ) return rc; - - /* Initialize the malloc() system and the recursive pInitMutex mutex. - ** This operation is protected by the STATIC_MASTER mutex. Note that - ** MutexAlloc() is called for a static mutex prior to initializing the - ** malloc subsystem - this implies that the allocation of a static - ** mutex must not require support from the malloc subsystem. - */ - MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) - sqlite3_mutex_enter(pMaster); - sqlite3GlobalConfig.isMutexInit = 1; - if( !sqlite3GlobalConfig.isMallocInit ){ - rc = sqlite3MallocInit(); - } - if( rc==SQLITE_OK ){ - sqlite3GlobalConfig.isMallocInit = 1; - if( !sqlite3GlobalConfig.pInitMutex ){ - sqlite3GlobalConfig.pInitMutex = - sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); - if( sqlite3GlobalConfig.bCoreMutex && !sqlite3GlobalConfig.pInitMutex ){ - rc = SQLITE_NOMEM; - } - } - } - if( rc==SQLITE_OK ){ - sqlite3GlobalConfig.nRefInitMutex++; - } - sqlite3_mutex_leave(pMaster); - - /* If rc is not SQLITE_OK at this point, then either the malloc - ** subsystem could not be initialized or the system failed to allocate - ** the pInitMutex mutex. Return an error in either case. */ - if( rc!=SQLITE_OK ){ - return rc; - } - - /* Do the rest of the initialization under the recursive mutex so - ** that we will be able to handle recursive calls into - ** sqlite3_initialize(). The recursive calls normally come through - ** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other - ** recursive calls might also be possible. - ** - ** IMPLEMENTATION-OF: R-00140-37445 SQLite automatically serializes calls - ** to the xInit method, so the xInit method need not be threadsafe. - ** - ** The following mutex is what serializes access to the appdef pcache xInit - ** methods. The sqlite3_pcache_methods.xInit() all is embedded in the - ** call to sqlite3PcacheInitialize(). - */ - sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex); - if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){ - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); - sqlite3GlobalConfig.inProgress = 1; - memset(pHash, 0, sizeof(sqlite3GlobalFunctions)); - sqlite3RegisterGlobalFunctions(); - if( sqlite3GlobalConfig.isPCacheInit==0 ){ - rc = sqlite3PcacheInitialize(); - } - if( rc==SQLITE_OK ){ - sqlite3GlobalConfig.isPCacheInit = 1; - rc = sqlite3OsInit(); - } - if( rc==SQLITE_OK ){ - sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, - sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); - sqlite3GlobalConfig.isInit = 1; - } - sqlite3GlobalConfig.inProgress = 0; - } - sqlite3_mutex_leave(sqlite3GlobalConfig.pInitMutex); - - /* Go back under the static mutex and clean up the recursive - ** mutex to prevent a resource leak. - */ - sqlite3_mutex_enter(pMaster); - sqlite3GlobalConfig.nRefInitMutex--; - if( sqlite3GlobalConfig.nRefInitMutex<=0 ){ - assert( sqlite3GlobalConfig.nRefInitMutex==0 ); - sqlite3_mutex_free(sqlite3GlobalConfig.pInitMutex); - sqlite3GlobalConfig.pInitMutex = 0; - } - sqlite3_mutex_leave(pMaster); - - /* The following is just a sanity check to make sure SQLite has - ** been compiled correctly. It is important to run this code, but - ** we don't want to run it too often and soak up CPU cycles for no - ** reason. So we run it once during initialization. - */ -#ifndef NDEBUG -#ifndef SQLITE_OMIT_FLOATING_POINT - /* This section of code's only "output" is via assert() statements. */ - if ( rc==SQLITE_OK ){ - u64 x = (((u64)1)<<63)-1; - double y; - assert(sizeof(x)==8); - assert(sizeof(x)==sizeof(y)); - memcpy(&y, &x, 8); - assert( sqlite3IsNaN(y) ); - } -#endif -#endif - - /* Do extra initialization steps requested by the SQLITE_EXTRA_INIT - ** compile-time option. - */ -#ifdef SQLITE_EXTRA_INIT - if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){ - int SQLITE_EXTRA_INIT(const char*); - rc = SQLITE_EXTRA_INIT(0); - } -#endif - - return rc; -} - -/* -** Undo the effects of sqlite3_initialize(). Must not be called while -** there are outstanding database connections or memory allocations or -** while any part of SQLite is otherwise in use in any thread. This -** routine is not threadsafe. But it is safe to invoke this routine -** on when SQLite is already shut down. If SQLite is already shut down -** when this routine is invoked, then this routine is a harmless no-op. -*/ -SQLITE_API int sqlite3_shutdown(void){ - if( sqlite3GlobalConfig.isInit ){ -#ifdef SQLITE_EXTRA_SHUTDOWN - void SQLITE_EXTRA_SHUTDOWN(void); - SQLITE_EXTRA_SHUTDOWN(); -#endif - sqlite3_os_end(); - sqlite3_reset_auto_extension(); - sqlite3GlobalConfig.isInit = 0; - } - if( sqlite3GlobalConfig.isPCacheInit ){ - sqlite3PcacheShutdown(); - sqlite3GlobalConfig.isPCacheInit = 0; - } - if( sqlite3GlobalConfig.isMallocInit ){ - sqlite3MallocEnd(); - sqlite3GlobalConfig.isMallocInit = 0; - -#ifndef SQLITE_OMIT_SHUTDOWN_DIRECTORIES - /* The heap subsystem has now been shutdown and these values are supposed - ** to be NULL or point to memory that was obtained from sqlite3_malloc(), - ** which would rely on that heap subsystem; therefore, make sure these - ** values cannot refer to heap memory that was just invalidated when the - ** heap subsystem was shutdown. This is only done if the current call to - ** this function resulted in the heap subsystem actually being shutdown. - */ - sqlite3_data_directory = 0; - sqlite3_temp_directory = 0; -#endif - } - if( sqlite3GlobalConfig.isMutexInit ){ - sqlite3MutexEnd(); - sqlite3GlobalConfig.isMutexInit = 0; - } - - return SQLITE_OK; -} - -/* -** This API allows applications to modify the global configuration of -** the SQLite library at run-time. -** -** This routine should only be called when there are no outstanding -** database connections or memory allocations. This routine is not -** threadsafe. Failure to heed these warnings can lead to unpredictable -** behavior. -*/ -SQLITE_API int sqlite3_config(int op, ...){ - va_list ap; - int rc = SQLITE_OK; - - /* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while - ** the SQLite library is in use. */ - if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE_BKPT; - - va_start(ap, op); - switch( op ){ - - /* Mutex configuration options are only available in a threadsafe - ** compile. - */ -#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 - case SQLITE_CONFIG_SINGLETHREAD: { - /* Disable all mutexing */ - sqlite3GlobalConfig.bCoreMutex = 0; - sqlite3GlobalConfig.bFullMutex = 0; - break; - } - case SQLITE_CONFIG_MULTITHREAD: { - /* Disable mutexing of database connections */ - /* Enable mutexing of core data structures */ - sqlite3GlobalConfig.bCoreMutex = 1; - sqlite3GlobalConfig.bFullMutex = 0; - break; - } - case SQLITE_CONFIG_SERIALIZED: { - /* Enable all mutexing */ - sqlite3GlobalConfig.bCoreMutex = 1; - sqlite3GlobalConfig.bFullMutex = 1; - break; - } - case SQLITE_CONFIG_MUTEX: { - /* Specify an alternative mutex implementation */ - sqlite3GlobalConfig.mutex = *va_arg(ap, sqlite3_mutex_methods*); - break; - } - case SQLITE_CONFIG_GETMUTEX: { - /* Retrieve the current mutex implementation */ - *va_arg(ap, sqlite3_mutex_methods*) = sqlite3GlobalConfig.mutex; - break; - } -#endif - - - case SQLITE_CONFIG_MALLOC: { - /* Specify an alternative malloc implementation */ - sqlite3GlobalConfig.m = *va_arg(ap, sqlite3_mem_methods*); - break; - } - case SQLITE_CONFIG_GETMALLOC: { - /* Retrieve the current malloc() implementation */ - if( sqlite3GlobalConfig.m.xMalloc==0 ) sqlite3MemSetDefault(); - *va_arg(ap, sqlite3_mem_methods*) = sqlite3GlobalConfig.m; - break; - } - case SQLITE_CONFIG_MEMSTATUS: { - /* Enable or disable the malloc status collection */ - sqlite3GlobalConfig.bMemstat = va_arg(ap, int); - break; - } - case SQLITE_CONFIG_SCRATCH: { - /* Designate a buffer for scratch memory space */ - sqlite3GlobalConfig.pScratch = va_arg(ap, void*); - sqlite3GlobalConfig.szScratch = va_arg(ap, int); - sqlite3GlobalConfig.nScratch = va_arg(ap, int); - break; - } - case SQLITE_CONFIG_PAGECACHE: { - /* Designate a buffer for page cache memory space */ - sqlite3GlobalConfig.pPage = va_arg(ap, void*); - sqlite3GlobalConfig.szPage = va_arg(ap, int); - sqlite3GlobalConfig.nPage = va_arg(ap, int); - break; - } - - case SQLITE_CONFIG_PCACHE: { - /* no-op */ - break; - } - case SQLITE_CONFIG_GETPCACHE: { - /* now an error */ - rc = SQLITE_ERROR; - break; - } - - case SQLITE_CONFIG_PCACHE2: { - /* Specify an alternative page cache implementation */ - sqlite3GlobalConfig.pcache2 = *va_arg(ap, sqlite3_pcache_methods2*); - break; - } - case SQLITE_CONFIG_GETPCACHE2: { - if( sqlite3GlobalConfig.pcache2.xInit==0 ){ - sqlite3PCacheSetDefault(); - } - *va_arg(ap, sqlite3_pcache_methods2*) = sqlite3GlobalConfig.pcache2; - break; - } - -#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) - case SQLITE_CONFIG_HEAP: { - /* Designate a buffer for heap memory space */ - sqlite3GlobalConfig.pHeap = va_arg(ap, void*); - sqlite3GlobalConfig.nHeap = va_arg(ap, int); - sqlite3GlobalConfig.mnReq = va_arg(ap, int); - - if( sqlite3GlobalConfig.mnReq<1 ){ - sqlite3GlobalConfig.mnReq = 1; - }else if( sqlite3GlobalConfig.mnReq>(1<<12) ){ - /* cap min request size at 2^12 */ - sqlite3GlobalConfig.mnReq = (1<<12); - } - - if( sqlite3GlobalConfig.pHeap==0 ){ - /* If the heap pointer is NULL, then restore the malloc implementation - ** back to NULL pointers too. This will cause the malloc to go - ** back to its default implementation when sqlite3_initialize() is - ** run. - */ - memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.m)); - }else{ - /* The heap pointer is not NULL, then install one of the - ** mem5.c/mem3.c methods. If neither ENABLE_MEMSYS3 nor - ** ENABLE_MEMSYS5 is defined, return an error. - */ -#ifdef SQLITE_ENABLE_MEMSYS3 - sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3(); -#endif -#ifdef SQLITE_ENABLE_MEMSYS5 - sqlite3GlobalConfig.m = *sqlite3MemGetMemsys5(); -#endif - } - break; - } -#endif - - case SQLITE_CONFIG_LOOKASIDE: { - sqlite3GlobalConfig.szLookaside = va_arg(ap, int); - sqlite3GlobalConfig.nLookaside = va_arg(ap, int); - break; - } - - /* Record a pointer to the logger funcction and its first argument. - ** The default is NULL. Logging is disabled if the function pointer is - ** NULL. - */ - case SQLITE_CONFIG_LOG: { - /* MSVC is picky about pulling func ptrs from va lists. - ** http://support.microsoft.com/kb/47961 - ** sqlite3GlobalConfig.xLog = va_arg(ap, void(*)(void*,int,const char*)); - */ - typedef void(*LOGFUNC_t)(void*,int,const char*); - sqlite3GlobalConfig.xLog = va_arg(ap, LOGFUNC_t); - sqlite3GlobalConfig.pLogArg = va_arg(ap, void*); - break; - } - - case SQLITE_CONFIG_URI: { - sqlite3GlobalConfig.bOpenUri = va_arg(ap, int); - break; - } - - case SQLITE_CONFIG_COVERING_INDEX_SCAN: { - sqlite3GlobalConfig.bUseCis = va_arg(ap, int); - break; - } - -#ifdef SQLITE_ENABLE_SQLLOG - case SQLITE_CONFIG_SQLLOG: { - typedef void(*SQLLOGFUNC_t)(void*, sqlite3*, const char*, int); - sqlite3GlobalConfig.xSqllog = va_arg(ap, SQLLOGFUNC_t); - sqlite3GlobalConfig.pSqllogArg = va_arg(ap, void *); - break; - } -#endif - - default: { - rc = SQLITE_ERROR; - break; - } - } - va_end(ap); - return rc; -} - -/* -** Set up the lookaside buffers for a database connection. -** Return SQLITE_OK on success. -** If lookaside is already active, return SQLITE_BUSY. -** -** The sz parameter is the number of bytes in each lookaside slot. -** The cnt parameter is the number of slots. If pStart is NULL the -** space for the lookaside memory is obtained from sqlite3_malloc(). -** If pStart is not NULL then it is sz*cnt bytes of memory to use for -** the lookaside memory. -*/ -static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ - void *pStart; - if( db->lookaside.nOut ){ - return SQLITE_BUSY; - } - /* Free any existing lookaside buffer for this handle before - ** allocating a new one so we don't have to have space for - ** both at the same time. - */ - if( db->lookaside.bMalloced ){ - sqlite3_free(db->lookaside.pStart); - } - /* The size of a lookaside slot after ROUNDDOWN8 needs to be larger - ** than a pointer to be useful. - */ - sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ - if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0; - if( cnt<0 ) cnt = 0; - if( sz==0 || cnt==0 ){ - sz = 0; - pStart = 0; - }else if( pBuf==0 ){ - sqlite3BeginBenignMalloc(); - pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ - sqlite3EndBenignMalloc(); - if( pStart ) cnt = sqlite3MallocSize(pStart)/sz; - }else{ - pStart = pBuf; - } - db->lookaside.pStart = pStart; - db->lookaside.pFree = 0; - db->lookaside.sz = (u16)sz; - if( pStart ){ - int i; - LookasideSlot *p; - assert( sz > (int)sizeof(LookasideSlot*) ); - p = (LookasideSlot*)pStart; - for(i=cnt-1; i>=0; i--){ - p->pNext = db->lookaside.pFree; - db->lookaside.pFree = p; - p = (LookasideSlot*)&((u8*)p)[sz]; - } - db->lookaside.pEnd = p; - db->lookaside.bEnabled = 1; - db->lookaside.bMalloced = pBuf==0 ?1:0; - }else{ - db->lookaside.pEnd = 0; - db->lookaside.bEnabled = 0; - db->lookaside.bMalloced = 0; - } - return SQLITE_OK; -} - -/* -** Return the mutex associated with a database connection. -*/ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){ - return db->mutex; -} - -/* -** Free up as much memory as we can from the given database -** connection. -*/ -SQLITE_API int sqlite3_db_release_memory(sqlite3 *db){ - int i; - sqlite3_mutex_enter(db->mutex); - sqlite3BtreeEnterAll(db); - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - Pager *pPager = sqlite3BtreePager(pBt); - sqlite3PagerShrink(pPager); - } - } - sqlite3BtreeLeaveAll(db); - sqlite3_mutex_leave(db->mutex); - return SQLITE_OK; -} - -/* -** Configuration settings for an individual database connection -*/ -SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ - va_list ap; - int rc; - va_start(ap, op); - switch( op ){ - case SQLITE_DBCONFIG_LOOKASIDE: { - void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */ - int sz = va_arg(ap, int); /* IMP: R-47871-25994 */ - int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */ - rc = setupLookaside(db, pBuf, sz, cnt); - break; - } - default: { - static const struct { - int op; /* The opcode */ - u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ - } aFlagOp[] = { - { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, - { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, - }; - unsigned int i; - rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ - for(i=0; iflags; - if( onoff>0 ){ - db->flags |= aFlagOp[i].mask; - }else if( onoff==0 ){ - db->flags &= ~aFlagOp[i].mask; - } - if( oldFlags!=db->flags ){ - sqlite3ExpirePreparedStatements(db); - } - if( pRes ){ - *pRes = (db->flags & aFlagOp[i].mask)!=0; - } - rc = SQLITE_OK; - break; - } - } - break; - } - } - va_end(ap); - return rc; -} - - -/* -** Return true if the buffer z[0..n-1] contains all spaces. -*/ -static int allSpaces(const char *z, int n){ - while( n>0 && z[n-1]==' ' ){ n--; } - return n==0; -} - -/* -** This is the default collating function named "BINARY" which is always -** available. -** -** If the padFlag argument is not NULL then space padding at the end -** of strings is ignored. This implements the RTRIM collation. -*/ -static int binCollFunc( - void *padFlag, - int nKey1, const void *pKey1, - int nKey2, const void *pKey2 -){ - int rc, n; - n = nKey1lastRowid; -} - -/* -** Return the number of changes in the most recent call to sqlite3_exec(). -*/ -SQLITE_API int sqlite3_changes(sqlite3 *db){ - return db->nChange; -} - -/* -** Return the number of changes since the database handle was opened. -*/ -SQLITE_API int sqlite3_total_changes(sqlite3 *db){ - return db->nTotalChange; -} - -/* -** Close all open savepoints. This function only manipulates fields of the -** database handle object, it does not close any savepoints that may be open -** at the b-tree/pager level. -*/ -SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *db){ - while( db->pSavepoint ){ - Savepoint *pTmp = db->pSavepoint; - db->pSavepoint = pTmp->pNext; - sqlite3DbFree(db, pTmp); - } - db->nSavepoint = 0; - db->nStatement = 0; - db->isTransactionSavepoint = 0; -} - -/* -** Invoke the destructor function associated with FuncDef p, if any. Except, -** if this is not the last copy of the function, do not invoke it. Multiple -** copies of a single function are created when create_function() is called -** with SQLITE_ANY as the encoding. -*/ -static void functionDestroy(sqlite3 *db, FuncDef *p){ - FuncDestructor *pDestructor = p->pDestructor; - if( pDestructor ){ - pDestructor->nRef--; - if( pDestructor->nRef==0 ){ - pDestructor->xDestroy(pDestructor->pUserData); - sqlite3DbFree(db, pDestructor); - } - } -} - -/* -** Disconnect all sqlite3_vtab objects that belong to database connection -** db. This is called when db is being closed. -*/ -static void disconnectAllVtab(sqlite3 *db){ -#ifndef SQLITE_OMIT_VIRTUALTABLE - int i; - sqlite3BtreeEnterAll(db); - for(i=0; inDb; i++){ - Schema *pSchema = db->aDb[i].pSchema; - if( db->aDb[i].pSchema ){ - HashElem *p; - for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){ - Table *pTab = (Table *)sqliteHashData(p); - if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab); - } - } - } - sqlite3BtreeLeaveAll(db); -#else - UNUSED_PARAMETER(db); -#endif -} - -/* -** Return TRUE if database connection db has unfinalized prepared -** statements or unfinished sqlite3_backup objects. -*/ -static int connectionIsBusy(sqlite3 *db){ - int j; - assert( sqlite3_mutex_held(db->mutex) ); - if( db->pVdbe ) return 1; - for(j=0; jnDb; j++){ - Btree *pBt = db->aDb[j].pBt; - if( pBt && sqlite3BtreeIsInBackup(pBt) ) return 1; - } - return 0; -} - -/* -** Close an existing SQLite database -*/ -static int sqlite3Close(sqlite3 *db, int forceZombie){ - if( !db ){ - return SQLITE_OK; - } - if( !sqlite3SafetyCheckSickOrOk(db) ){ - return SQLITE_MISUSE_BKPT; - } - sqlite3_mutex_enter(db->mutex); - - /* Force xDisconnect calls on all virtual tables */ - disconnectAllVtab(db); - - /* If a transaction is open, the disconnectAllVtab() call above - ** will not have called the xDisconnect() method on any virtual - ** tables in the db->aVTrans[] array. The following sqlite3VtabRollback() - ** call will do so. We need to do this before the check for active - ** SQL statements below, as the v-table implementation may be storing - ** some prepared statements internally. - */ - sqlite3VtabRollback(db); - - /* Legacy behavior (sqlite3_close() behavior) is to return - ** SQLITE_BUSY if the connection can not be closed immediately. - */ - if( !forceZombie && connectionIsBusy(db) ){ - sqlite3Error(db, SQLITE_BUSY, "unable to close due to unfinalized " - "statements or unfinished backups"); - sqlite3_mutex_leave(db->mutex); - return SQLITE_BUSY; - } - -#ifdef SQLITE_ENABLE_SQLLOG - if( sqlite3GlobalConfig.xSqllog ){ - /* Closing the handle. Fourth parameter is passed the value 2. */ - sqlite3GlobalConfig.xSqllog(sqlite3GlobalConfig.pSqllogArg, db, 0, 2); - } -#endif - - /* Convert the connection into a zombie and then close it. - */ - db->magic = SQLITE_MAGIC_ZOMBIE; - sqlite3LeaveMutexAndCloseZombie(db); - return SQLITE_OK; -} - -/* -** Two variations on the public interface for closing a database -** connection. The sqlite3_close() version returns SQLITE_BUSY and -** leaves the connection option if there are unfinalized prepared -** statements or unfinished sqlite3_backups. The sqlite3_close_v2() -** version forces the connection to become a zombie if there are -** unclosed resources, and arranges for deallocation when the last -** prepare statement or sqlite3_backup closes. -*/ -SQLITE_API int sqlite3_close(sqlite3 *db){ return sqlite3Close(db,0); } -SQLITE_API int sqlite3_close_v2(sqlite3 *db){ return sqlite3Close(db,1); } - - -/* -** Close the mutex on database connection db. -** -** Furthermore, if database connection db is a zombie (meaning that there -** has been a prior call to sqlite3_close(db) or sqlite3_close_v2(db)) and -** every sqlite3_stmt has now been finalized and every sqlite3_backup has -** finished, then free all resources. -*/ -SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ - HashElem *i; /* Hash table iterator */ - int j; - - /* If there are outstanding sqlite3_stmt or sqlite3_backup objects - ** or if the connection has not yet been closed by sqlite3_close_v2(), - ** then just leave the mutex and return. - */ - if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){ - sqlite3_mutex_leave(db->mutex); - return; - } - - /* If we reach this point, it means that the database connection has - ** closed all sqlite3_stmt and sqlite3_backup objects and has been - ** passed to sqlite3_close (meaning that it is a zombie). Therefore, - ** go ahead and free all resources. - */ - - /* Free any outstanding Savepoint structures. */ - sqlite3CloseSavepoints(db); - - /* Close all database connections */ - for(j=0; jnDb; j++){ - struct Db *pDb = &db->aDb[j]; - if( pDb->pBt ){ - sqlite3BtreeClose(pDb->pBt); - pDb->pBt = 0; - if( j!=1 ){ - pDb->pSchema = 0; - } - } - } - /* Clear the TEMP schema separately and last */ - if( db->aDb[1].pSchema ){ - sqlite3SchemaClear(db->aDb[1].pSchema); - } - sqlite3VtabUnlockList(db); - - /* Free up the array of auxiliary databases */ - sqlite3CollapseDatabaseArray(db); - assert( db->nDb<=2 ); - assert( db->aDb==db->aDbStatic ); - - /* Tell the code in notify.c that the connection no longer holds any - ** locks and does not require any further unlock-notify callbacks. - */ - sqlite3ConnectionClosed(db); - - for(j=0; jaFunc.a); j++){ - FuncDef *pNext, *pHash, *p; - for(p=db->aFunc.a[j]; p; p=pHash){ - pHash = p->pHash; - while( p ){ - functionDestroy(db, p); - pNext = p->pNext; - sqlite3DbFree(db, p); - p = pNext; - } - } - } - for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){ - CollSeq *pColl = (CollSeq *)sqliteHashData(i); - /* Invoke any destructors registered for collation sequence user data. */ - for(j=0; j<3; j++){ - if( pColl[j].xDel ){ - pColl[j].xDel(pColl[j].pUser); - } - } - sqlite3DbFree(db, pColl); - } - sqlite3HashClear(&db->aCollSeq); -#ifndef SQLITE_OMIT_VIRTUALTABLE - for(i=sqliteHashFirst(&db->aModule); i; i=sqliteHashNext(i)){ - Module *pMod = (Module *)sqliteHashData(i); - if( pMod->xDestroy ){ - pMod->xDestroy(pMod->pAux); - } - sqlite3DbFree(db, pMod); - } - sqlite3HashClear(&db->aModule); -#endif - - sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */ - if( db->pErr ){ - sqlite3ValueFree(db->pErr); - } - sqlite3CloseExtensions(db); - - db->magic = SQLITE_MAGIC_ERROR; - - /* The temp-database schema is allocated differently from the other schema - ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). - ** So it needs to be freed here. Todo: Why not roll the temp schema into - ** the same sqliteMalloc() as the one that allocates the database - ** structure? - */ - sqlite3DbFree(db, db->aDb[1].pSchema); - sqlite3_mutex_leave(db->mutex); - db->magic = SQLITE_MAGIC_CLOSED; - sqlite3_mutex_free(db->mutex); - assert( db->lookaside.nOut==0 ); /* Fails on a lookaside memory leak */ - if( db->lookaside.bMalloced ){ - sqlite3_free(db->lookaside.pStart); - } - sqlite3_free(db); -} - -/* -** Rollback all database files. If tripCode is not SQLITE_OK, then -** any open cursors are invalidated ("tripped" - as in "tripping a circuit -** breaker") and made to return tripCode if there are any further -** attempts to use that cursor. -*/ -SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ - int i; - int inTrans = 0; - assert( sqlite3_mutex_held(db->mutex) ); - sqlite3BeginBenignMalloc(); - for(i=0; inDb; i++){ - Btree *p = db->aDb[i].pBt; - if( p ){ - if( sqlite3BtreeIsInTrans(p) ){ - inTrans = 1; - } - sqlite3BtreeRollback(p, tripCode); - db->aDb[i].inTrans = 0; - } - } - sqlite3VtabRollback(db); - sqlite3EndBenignMalloc(); - - if( (db->flags&SQLITE_InternChanges)!=0 && db->init.busy==0 ){ - sqlite3ExpirePreparedStatements(db); - sqlite3ResetAllSchemasOfConnection(db); - } - - /* Any deferred constraint violations have now been resolved. */ - db->nDeferredCons = 0; - - /* If one has been configured, invoke the rollback-hook callback */ - if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ - db->xRollbackCallback(db->pRollbackArg); - } -} - -/* -** Return a static string that describes the kind of error specified in the -** argument. -*/ -SQLITE_PRIVATE const char *sqlite3ErrStr(int rc){ - static const char* const aMsg[] = { - /* SQLITE_OK */ "not an error", - /* SQLITE_ERROR */ "SQL logic error or missing database", - /* SQLITE_INTERNAL */ 0, - /* SQLITE_PERM */ "access permission denied", - /* SQLITE_ABORT */ "callback requested query abort", - /* SQLITE_BUSY */ "database is locked", - /* SQLITE_LOCKED */ "database table is locked", - /* SQLITE_NOMEM */ "out of memory", - /* SQLITE_READONLY */ "attempt to write a readonly database", - /* SQLITE_INTERRUPT */ "interrupted", - /* SQLITE_IOERR */ "disk I/O error", - /* SQLITE_CORRUPT */ "database disk image is malformed", - /* SQLITE_NOTFOUND */ "unknown operation", - /* SQLITE_FULL */ "database or disk is full", - /* SQLITE_CANTOPEN */ "unable to open database file", - /* SQLITE_PROTOCOL */ "locking protocol", - /* SQLITE_EMPTY */ "table contains no data", - /* SQLITE_SCHEMA */ "database schema has changed", - /* SQLITE_TOOBIG */ "string or blob too big", - /* SQLITE_CONSTRAINT */ "constraint failed", - /* SQLITE_MISMATCH */ "datatype mismatch", - /* SQLITE_MISUSE */ "library routine called out of sequence", - /* SQLITE_NOLFS */ "large file support is disabled", - /* SQLITE_AUTH */ "authorization denied", - /* SQLITE_FORMAT */ "auxiliary database format error", - /* SQLITE_RANGE */ "bind or column index out of range", - /* SQLITE_NOTADB */ "file is encrypted or is not a database", - }; - const char *zErr = "unknown error"; - switch( rc ){ - case SQLITE_ABORT_ROLLBACK: { - zErr = "abort due to ROLLBACK"; - break; - } - default: { - rc &= 0xff; - if( ALWAYS(rc>=0) && rcbusyTimeout; - int delay, prior; - - assert( count>=0 ); - if( count < NDELAY ){ - delay = delays[count]; - prior = totals[count]; - }else{ - delay = delays[NDELAY-1]; - prior = totals[NDELAY-1] + delay*(count-(NDELAY-1)); - } - if( prior + delay > timeout ){ - delay = timeout - prior; - if( delay<=0 ) return 0; - } - sqlite3OsSleep(db->pVfs, delay*1000); - return 1; -#else - sqlite3 *db = (sqlite3 *)ptr; - int timeout = ((sqlite3 *)ptr)->busyTimeout; - if( (count+1)*1000 > timeout ){ - return 0; - } - sqlite3OsSleep(db->pVfs, 1000000); - return 1; -#endif -} - -/* -** Invoke the given busy handler. -** -** This routine is called when an operation failed with a lock. -** If this routine returns non-zero, the lock is retried. If it -** returns 0, the operation aborts with an SQLITE_BUSY error. -*/ -SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){ - int rc; - if( NEVER(p==0) || p->xFunc==0 || p->nBusy<0 ) return 0; - rc = p->xFunc(p->pArg, p->nBusy); - if( rc==0 ){ - p->nBusy = -1; - }else{ - p->nBusy++; - } - return rc; -} - -/* -** This routine sets the busy callback for an Sqlite database to the -** given callback function with the given argument. -*/ -SQLITE_API int sqlite3_busy_handler( - sqlite3 *db, - int (*xBusy)(void*,int), - void *pArg -){ - sqlite3_mutex_enter(db->mutex); - db->busyHandler.xFunc = xBusy; - db->busyHandler.pArg = pArg; - db->busyHandler.nBusy = 0; - db->busyTimeout = 0; - sqlite3_mutex_leave(db->mutex); - return SQLITE_OK; -} - -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK -/* -** This routine sets the progress callback for an Sqlite database to the -** given callback function with the given argument. The progress callback will -** be invoked every nOps opcodes. -*/ -SQLITE_API void sqlite3_progress_handler( - sqlite3 *db, - int nOps, - int (*xProgress)(void*), - void *pArg -){ - sqlite3_mutex_enter(db->mutex); - if( nOps>0 ){ - db->xProgress = xProgress; - db->nProgressOps = nOps; - db->pProgressArg = pArg; - }else{ - db->xProgress = 0; - db->nProgressOps = 0; - db->pProgressArg = 0; - } - sqlite3_mutex_leave(db->mutex); -} -#endif - - -/* -** This routine installs a default busy handler that waits for the -** specified number of milliseconds before returning 0. -*/ -SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){ - if( ms>0 ){ - sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db); - db->busyTimeout = ms; - }else{ - sqlite3_busy_handler(db, 0, 0); - } - return SQLITE_OK; -} - -/* -** Cause any pending operation to stop at its earliest opportunity. -*/ -SQLITE_API void sqlite3_interrupt(sqlite3 *db){ - db->u1.isInterrupted = 1; -} - - -/* -** This function is exactly the same as sqlite3_create_function(), except -** that it is designed to be called by internal code. The difference is -** that if a malloc() fails in sqlite3_create_function(), an error code -** is returned and the mallocFailed flag cleared. -*/ -SQLITE_PRIVATE int sqlite3CreateFunc( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int enc, - void *pUserData, - void (*xFunc)(sqlite3_context*,int,sqlite3_value **), - void (*xStep)(sqlite3_context*,int,sqlite3_value **), - void (*xFinal)(sqlite3_context*), - FuncDestructor *pDestructor -){ - FuncDef *p; - int nName; - - assert( sqlite3_mutex_held(db->mutex) ); - if( zFunctionName==0 || - (xFunc && (xFinal || xStep)) || - (!xFunc && (xFinal && !xStep)) || - (!xFunc && (!xFinal && xStep)) || - (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) || - (255<(nName = sqlite3Strlen30( zFunctionName))) ){ - return SQLITE_MISUSE_BKPT; - } - -#ifndef SQLITE_OMIT_UTF16 - /* If SQLITE_UTF16 is specified as the encoding type, transform this - ** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the - ** SQLITE_UTF16NATIVE macro. SQLITE_UTF16 is not used internally. - ** - ** If SQLITE_ANY is specified, add three versions of the function - ** to the hash table. - */ - if( enc==SQLITE_UTF16 ){ - enc = SQLITE_UTF16NATIVE; - }else if( enc==SQLITE_ANY ){ - int rc; - rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8, - pUserData, xFunc, xStep, xFinal, pDestructor); - if( rc==SQLITE_OK ){ - rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE, - pUserData, xFunc, xStep, xFinal, pDestructor); - } - if( rc!=SQLITE_OK ){ - return rc; - } - enc = SQLITE_UTF16BE; - } -#else - enc = SQLITE_UTF8; -#endif - - /* Check if an existing function is being overridden or deleted. If so, - ** and there are active VMs, then return SQLITE_BUSY. If a function - ** is being overridden/deleted but there are no active VMs, allow the - ** operation to continue but invalidate all precompiled statements. - */ - p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0); - if( p && p->iPrefEnc==enc && p->nArg==nArg ){ - if( db->activeVdbeCnt ){ - sqlite3Error(db, SQLITE_BUSY, - "unable to delete/modify user-function due to active statements"); - assert( !db->mallocFailed ); - return SQLITE_BUSY; - }else{ - sqlite3ExpirePreparedStatements(db); - } - } - - p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1); - assert(p || db->mallocFailed); - if( !p ){ - return SQLITE_NOMEM; - } - - /* If an older version of the function with a configured destructor is - ** being replaced invoke the destructor function here. */ - functionDestroy(db, p); - - if( pDestructor ){ - pDestructor->nRef++; - } - p->pDestructor = pDestructor; - p->flags = 0; - p->xFunc = xFunc; - p->xStep = xStep; - p->xFinalize = xFinal; - p->pUserData = pUserData; - p->nArg = (u16)nArg; - return SQLITE_OK; -} - -/* -** Create new user functions. -*/ -SQLITE_API int sqlite3_create_function( - sqlite3 *db, - const char *zFunc, - int nArg, - int enc, - void *p, - void (*xFunc)(sqlite3_context*,int,sqlite3_value **), - void (*xStep)(sqlite3_context*,int,sqlite3_value **), - void (*xFinal)(sqlite3_context*) -){ - return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xFunc, xStep, - xFinal, 0); -} - -SQLITE_API int sqlite3_create_function_v2( - sqlite3 *db, - const char *zFunc, - int nArg, - int enc, - void *p, - void (*xFunc)(sqlite3_context*,int,sqlite3_value **), - void (*xStep)(sqlite3_context*,int,sqlite3_value **), - void (*xFinal)(sqlite3_context*), - void (*xDestroy)(void *) -){ - int rc = SQLITE_ERROR; - FuncDestructor *pArg = 0; - sqlite3_mutex_enter(db->mutex); - if( xDestroy ){ - pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor)); - if( !pArg ){ - xDestroy(p); - goto out; - } - pArg->xDestroy = xDestroy; - pArg->pUserData = p; - } - rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg); - if( pArg && pArg->nRef==0 ){ - assert( rc!=SQLITE_OK ); - xDestroy(p); - sqlite3DbFree(db, pArg); - } - - out: - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API int sqlite3_create_function16( - sqlite3 *db, - const void *zFunctionName, - int nArg, - int eTextRep, - void *p, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -){ - int rc; - char *zFunc8; - sqlite3_mutex_enter(db->mutex); - assert( !db->mallocFailed ); - zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE); - rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal,0); - sqlite3DbFree(db, zFunc8); - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} -#endif - - -/* -** Declare that a function has been overloaded by a virtual table. -** -** If the function already exists as a regular global function, then -** this routine is a no-op. If the function does not exist, then create -** a new one that always throws a run-time error. -** -** When virtual tables intend to provide an overloaded function, they -** should call this routine to make sure the global function exists. -** A global function must exist in order for name resolution to work -** properly. -*/ -SQLITE_API int sqlite3_overload_function( - sqlite3 *db, - const char *zName, - int nArg -){ - int nName = sqlite3Strlen30(zName); - int rc = SQLITE_OK; - sqlite3_mutex_enter(db->mutex); - if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){ - rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8, - 0, sqlite3InvalidFunction, 0, 0, 0); - } - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -#ifndef SQLITE_OMIT_TRACE -/* -** Register a trace function. The pArg from the previously registered trace -** is returned. -** -** A NULL trace function means that no tracing is executes. A non-NULL -** trace is a pointer to a function that is invoked at the start of each -** SQL statement. -*/ -SQLITE_API void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ - void *pOld; - sqlite3_mutex_enter(db->mutex); - pOld = db->pTraceArg; - db->xTrace = xTrace; - db->pTraceArg = pArg; - sqlite3_mutex_leave(db->mutex); - return pOld; -} -/* -** Register a profile function. The pArg from the previously registered -** profile function is returned. -** -** A NULL profile function means that no profiling is executes. A non-NULL -** profile is a pointer to a function that is invoked at the conclusion of -** each SQL statement that is run. -*/ -SQLITE_API void *sqlite3_profile( - sqlite3 *db, - void (*xProfile)(void*,const char*,sqlite_uint64), - void *pArg -){ - void *pOld; - sqlite3_mutex_enter(db->mutex); - pOld = db->pProfileArg; - db->xProfile = xProfile; - db->pProfileArg = pArg; - sqlite3_mutex_leave(db->mutex); - return pOld; -} -#endif /* SQLITE_OMIT_TRACE */ - -/* -** Register a function to be invoked when a transaction commits. -** If the invoked function returns non-zero, then the commit becomes a -** rollback. -*/ -SQLITE_API void *sqlite3_commit_hook( - sqlite3 *db, /* Attach the hook to this database */ - int (*xCallback)(void*), /* Function to invoke on each commit */ - void *pArg /* Argument to the function */ -){ - void *pOld; - sqlite3_mutex_enter(db->mutex); - pOld = db->pCommitArg; - db->xCommitCallback = xCallback; - db->pCommitArg = pArg; - sqlite3_mutex_leave(db->mutex); - return pOld; -} - -/* -** Register a callback to be invoked each time a row is updated, -** inserted or deleted using this database connection. -*/ -SQLITE_API void *sqlite3_update_hook( - sqlite3 *db, /* Attach the hook to this database */ - void (*xCallback)(void*,int,char const *,char const *,sqlite_int64), - void *pArg /* Argument to the function */ -){ - void *pRet; - sqlite3_mutex_enter(db->mutex); - pRet = db->pUpdateArg; - db->xUpdateCallback = xCallback; - db->pUpdateArg = pArg; - sqlite3_mutex_leave(db->mutex); - return pRet; -} - -/* -** Register a callback to be invoked each time a transaction is rolled -** back by this database connection. -*/ -SQLITE_API void *sqlite3_rollback_hook( - sqlite3 *db, /* Attach the hook to this database */ - void (*xCallback)(void*), /* Callback function */ - void *pArg /* Argument to the function */ -){ - void *pRet; - sqlite3_mutex_enter(db->mutex); - pRet = db->pRollbackArg; - db->xRollbackCallback = xCallback; - db->pRollbackArg = pArg; - sqlite3_mutex_leave(db->mutex); - return pRet; -} - -#ifndef SQLITE_OMIT_WAL -/* -** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint(). -** Invoke sqlite3_wal_checkpoint if the number of frames in the log file -** is greater than sqlite3.pWalArg cast to an integer (the value configured by -** wal_autocheckpoint()). -*/ -SQLITE_PRIVATE int sqlite3WalDefaultHook( - void *pClientData, /* Argument */ - sqlite3 *db, /* Connection */ - const char *zDb, /* Database */ - int nFrame /* Size of WAL */ -){ - if( nFrame>=SQLITE_PTR_TO_INT(pClientData) ){ - sqlite3BeginBenignMalloc(); - sqlite3_wal_checkpoint(db, zDb); - sqlite3EndBenignMalloc(); - } - return SQLITE_OK; -} -#endif /* SQLITE_OMIT_WAL */ - -/* -** Configure an sqlite3_wal_hook() callback to automatically checkpoint -** a database after committing a transaction if there are nFrame or -** more frames in the log file. Passing zero or a negative value as the -** nFrame parameter disables automatic checkpoints entirely. -** -** The callback registered by this function replaces any existing callback -** registered using sqlite3_wal_hook(). Likewise, registering a callback -** using sqlite3_wal_hook() disables the automatic checkpoint mechanism -** configured by this function. -*/ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ -#ifdef SQLITE_OMIT_WAL - UNUSED_PARAMETER(db); - UNUSED_PARAMETER(nFrame); -#else - if( nFrame>0 ){ - sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame)); - }else{ - sqlite3_wal_hook(db, 0, 0); - } -#endif - return SQLITE_OK; -} - -/* -** Register a callback to be invoked each time a transaction is written -** into the write-ahead-log by this database connection. -*/ -SQLITE_API void *sqlite3_wal_hook( - sqlite3 *db, /* Attach the hook to this db handle */ - int(*xCallback)(void *, sqlite3*, const char*, int), - void *pArg /* First argument passed to xCallback() */ -){ -#ifndef SQLITE_OMIT_WAL - void *pRet; - sqlite3_mutex_enter(db->mutex); - pRet = db->pWalArg; - db->xWalCallback = xCallback; - db->pWalArg = pArg; - sqlite3_mutex_leave(db->mutex); - return pRet; -#else - return 0; -#endif -} - -/* -** Checkpoint database zDb. -*/ -SQLITE_API int sqlite3_wal_checkpoint_v2( - sqlite3 *db, /* Database handle */ - const char *zDb, /* Name of attached database (or NULL) */ - int eMode, /* SQLITE_CHECKPOINT_* value */ - int *pnLog, /* OUT: Size of WAL log in frames */ - int *pnCkpt /* OUT: Total number of frames checkpointed */ -){ -#ifdef SQLITE_OMIT_WAL - return SQLITE_OK; -#else - int rc; /* Return code */ - int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */ - - /* Initialize the output variables to -1 in case an error occurs. */ - if( pnLog ) *pnLog = -1; - if( pnCkpt ) *pnCkpt = -1; - - assert( SQLITE_CHECKPOINT_FULL>SQLITE_CHECKPOINT_PASSIVE ); - assert( SQLITE_CHECKPOINT_FULLSQLITE_CHECKPOINT_RESTART ){ - return SQLITE_MISUSE; - } - - sqlite3_mutex_enter(db->mutex); - if( zDb && zDb[0] ){ - iDb = sqlite3FindDbName(db, zDb); - } - if( iDb<0 ){ - rc = SQLITE_ERROR; - sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb); - }else{ - rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); - sqlite3Error(db, rc, 0); - } - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -#endif -} - - -/* -** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points -** to contains a zero-length string, all attached databases are -** checkpointed. -*/ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ - return sqlite3_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0); -} - -#ifndef SQLITE_OMIT_WAL -/* -** Run a checkpoint on database iDb. This is a no-op if database iDb is -** not currently open in WAL mode. -** -** If a transaction is open on the database being checkpointed, this -** function returns SQLITE_LOCKED and a checkpoint is not attempted. If -** an error occurs while running the checkpoint, an SQLite error code is -** returned (i.e. SQLITE_IOERR). Otherwise, SQLITE_OK. -** -** The mutex on database handle db should be held by the caller. The mutex -** associated with the specific b-tree being checkpointed is taken by -** this function while the checkpoint is running. -** -** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are -** checkpointed. If an error is encountered it is returned immediately - -** no attempt is made to checkpoint any remaining databases. -** -** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. -*/ -SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){ - int rc = SQLITE_OK; /* Return code */ - int i; /* Used to iterate through attached dbs */ - int bBusy = 0; /* True if SQLITE_BUSY has been encountered */ - - assert( sqlite3_mutex_held(db->mutex) ); - assert( !pnLog || *pnLog==-1 ); - assert( !pnCkpt || *pnCkpt==-1 ); - - for(i=0; inDb && rc==SQLITE_OK; i++){ - if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ - rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); - pnLog = 0; - pnCkpt = 0; - if( rc==SQLITE_BUSY ){ - bBusy = 1; - rc = SQLITE_OK; - } - } - } - - return (rc==SQLITE_OK && bBusy) ? SQLITE_BUSY : rc; -} -#endif /* SQLITE_OMIT_WAL */ - -/* -** This function returns true if main-memory should be used instead of -** a temporary file for transient pager files and statement journals. -** The value returned depends on the value of db->temp_store (runtime -** parameter) and the compile time value of SQLITE_TEMP_STORE. The -** following table describes the relationship between these two values -** and this functions return value. -** -** SQLITE_TEMP_STORE db->temp_store Location of temporary database -** ----------------- -------------- ------------------------------ -** 0 any file (return 0) -** 1 1 file (return 0) -** 1 2 memory (return 1) -** 1 0 file (return 0) -** 2 1 file (return 0) -** 2 2 memory (return 1) -** 2 0 memory (return 1) -** 3 any memory (return 1) -*/ -SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3 *db){ -#if SQLITE_TEMP_STORE==1 - return ( db->temp_store==2 ); -#endif -#if SQLITE_TEMP_STORE==2 - return ( db->temp_store!=1 ); -#endif -#if SQLITE_TEMP_STORE==3 - return 1; -#endif -#if SQLITE_TEMP_STORE<1 || SQLITE_TEMP_STORE>3 - return 0; -#endif -} - -/* -** Return UTF-8 encoded English language explanation of the most recent -** error. -*/ -SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ - const char *z; - if( !db ){ - return sqlite3ErrStr(SQLITE_NOMEM); - } - if( !sqlite3SafetyCheckSickOrOk(db) ){ - return sqlite3ErrStr(SQLITE_MISUSE_BKPT); - } - sqlite3_mutex_enter(db->mutex); - if( db->mallocFailed ){ - z = sqlite3ErrStr(SQLITE_NOMEM); - }else{ - z = (char*)sqlite3_value_text(db->pErr); - assert( !db->mallocFailed ); - if( z==0 ){ - z = sqlite3ErrStr(db->errCode); - } - } - sqlite3_mutex_leave(db->mutex); - return z; -} - -#ifndef SQLITE_OMIT_UTF16 -/* -** Return UTF-16 encoded English language explanation of the most recent -** error. -*/ -SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db){ - static const u16 outOfMem[] = { - 'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0 - }; - static const u16 misuse[] = { - 'l', 'i', 'b', 'r', 'a', 'r', 'y', ' ', - 'r', 'o', 'u', 't', 'i', 'n', 'e', ' ', - 'c', 'a', 'l', 'l', 'e', 'd', ' ', - 'o', 'u', 't', ' ', - 'o', 'f', ' ', - 's', 'e', 'q', 'u', 'e', 'n', 'c', 'e', 0 - }; - - const void *z; - if( !db ){ - return (void *)outOfMem; - } - if( !sqlite3SafetyCheckSickOrOk(db) ){ - return (void *)misuse; - } - sqlite3_mutex_enter(db->mutex); - if( db->mallocFailed ){ - z = (void *)outOfMem; - }else{ - z = sqlite3_value_text16(db->pErr); - if( z==0 ){ - sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode), - SQLITE_UTF8, SQLITE_STATIC); - z = sqlite3_value_text16(db->pErr); - } - /* A malloc() may have failed within the call to sqlite3_value_text16() - ** above. If this is the case, then the db->mallocFailed flag needs to - ** be cleared before returning. Do this directly, instead of via - ** sqlite3ApiExit(), to avoid setting the database handle error message. - */ - db->mallocFailed = 0; - } - sqlite3_mutex_leave(db->mutex); - return z; -} -#endif /* SQLITE_OMIT_UTF16 */ - -/* -** Return the most recent error code generated by an SQLite routine. If NULL is -** passed to this function, we assume a malloc() failed during sqlite3_open(). -*/ -SQLITE_API int sqlite3_errcode(sqlite3 *db){ - if( db && !sqlite3SafetyCheckSickOrOk(db) ){ - return SQLITE_MISUSE_BKPT; - } - if( !db || db->mallocFailed ){ - return SQLITE_NOMEM; - } - return db->errCode & db->errMask; -} -SQLITE_API int sqlite3_extended_errcode(sqlite3 *db){ - if( db && !sqlite3SafetyCheckSickOrOk(db) ){ - return SQLITE_MISUSE_BKPT; - } - if( !db || db->mallocFailed ){ - return SQLITE_NOMEM; - } - return db->errCode; -} - -/* -** Return a string that describes the kind of error specified in the -** argument. For now, this simply calls the internal sqlite3ErrStr() -** function. -*/ -SQLITE_API const char *sqlite3_errstr(int rc){ - return sqlite3ErrStr(rc); -} - -/* -** Create a new collating function for database "db". The name is zName -** and the encoding is enc. -*/ -static int createCollation( - sqlite3* db, - const char *zName, - u8 enc, - void* pCtx, - int(*xCompare)(void*,int,const void*,int,const void*), - void(*xDel)(void*) -){ - CollSeq *pColl; - int enc2; - int nName = sqlite3Strlen30(zName); - - assert( sqlite3_mutex_held(db->mutex) ); - - /* If SQLITE_UTF16 is specified as the encoding type, transform this - ** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the - ** SQLITE_UTF16NATIVE macro. SQLITE_UTF16 is not used internally. - */ - enc2 = enc; - testcase( enc2==SQLITE_UTF16 ); - testcase( enc2==SQLITE_UTF16_ALIGNED ); - if( enc2==SQLITE_UTF16 || enc2==SQLITE_UTF16_ALIGNED ){ - enc2 = SQLITE_UTF16NATIVE; - } - if( enc2SQLITE_UTF16BE ){ - return SQLITE_MISUSE_BKPT; - } - - /* Check if this call is removing or replacing an existing collation - ** sequence. If so, and there are active VMs, return busy. If there - ** are no active VMs, invalidate any pre-compiled statements. - */ - pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 0); - if( pColl && pColl->xCmp ){ - if( db->activeVdbeCnt ){ - sqlite3Error(db, SQLITE_BUSY, - "unable to delete/modify collation sequence due to active statements"); - return SQLITE_BUSY; - } - sqlite3ExpirePreparedStatements(db); - - /* If collation sequence pColl was created directly by a call to - ** sqlite3_create_collation, and not generated by synthCollSeq(), - ** then any copies made by synthCollSeq() need to be invalidated. - ** Also, collation destructor - CollSeq.xDel() - function may need - ** to be called. - */ - if( (pColl->enc & ~SQLITE_UTF16_ALIGNED)==enc2 ){ - CollSeq *aColl = sqlite3HashFind(&db->aCollSeq, zName, nName); - int j; - for(j=0; j<3; j++){ - CollSeq *p = &aColl[j]; - if( p->enc==pColl->enc ){ - if( p->xDel ){ - p->xDel(p->pUser); - } - p->xCmp = 0; - } - } - } - } - - pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1); - if( pColl==0 ) return SQLITE_NOMEM; - pColl->xCmp = xCompare; - pColl->pUser = pCtx; - pColl->xDel = xDel; - pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED)); - sqlite3Error(db, SQLITE_OK, 0); - return SQLITE_OK; -} - - -/* -** This array defines hard upper bounds on limit values. The -** initializer must be kept in sync with the SQLITE_LIMIT_* -** #defines in sqlite3.h. -*/ -static const int aHardLimit[] = { - SQLITE_MAX_LENGTH, - SQLITE_MAX_SQL_LENGTH, - SQLITE_MAX_COLUMN, - SQLITE_MAX_EXPR_DEPTH, - SQLITE_MAX_COMPOUND_SELECT, - SQLITE_MAX_VDBE_OP, - SQLITE_MAX_FUNCTION_ARG, - SQLITE_MAX_ATTACHED, - SQLITE_MAX_LIKE_PATTERN_LENGTH, - SQLITE_MAX_VARIABLE_NUMBER, - SQLITE_MAX_TRIGGER_DEPTH, -}; - -/* -** Make sure the hard limits are set to reasonable values -*/ -#if SQLITE_MAX_LENGTH<100 -# error SQLITE_MAX_LENGTH must be at least 100 -#endif -#if SQLITE_MAX_SQL_LENGTH<100 -# error SQLITE_MAX_SQL_LENGTH must be at least 100 -#endif -#if SQLITE_MAX_SQL_LENGTH>SQLITE_MAX_LENGTH -# error SQLITE_MAX_SQL_LENGTH must not be greater than SQLITE_MAX_LENGTH -#endif -#if SQLITE_MAX_COMPOUND_SELECT<2 -# error SQLITE_MAX_COMPOUND_SELECT must be at least 2 -#endif -#if SQLITE_MAX_VDBE_OP<40 -# error SQLITE_MAX_VDBE_OP must be at least 40 -#endif -#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000 -# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000 -#endif -#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>62 -# error SQLITE_MAX_ATTACHED must be between 0 and 62 -#endif -#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 -# error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1 -#endif -#if SQLITE_MAX_COLUMN>32767 -# error SQLITE_MAX_COLUMN must not exceed 32767 -#endif -#if SQLITE_MAX_TRIGGER_DEPTH<1 -# error SQLITE_MAX_TRIGGER_DEPTH must be at least 1 -#endif - - -/* -** Change the value of a limit. Report the old value. -** If an invalid limit index is supplied, report -1. -** Make no changes but still report the old value if the -** new limit is negative. -** -** A new lower limit does not shrink existing constructs. -** It merely prevents new constructs that exceed the limit -** from forming. -*/ -SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ - int oldLimit; - - - /* EVIDENCE-OF: R-30189-54097 For each limit category SQLITE_LIMIT_NAME - ** there is a hard upper bound set at compile-time by a C preprocessor - ** macro called SQLITE_MAX_NAME. (The "_LIMIT_" in the name is changed to - ** "_MAX_".) - */ - assert( aHardLimit[SQLITE_LIMIT_LENGTH]==SQLITE_MAX_LENGTH ); - assert( aHardLimit[SQLITE_LIMIT_SQL_LENGTH]==SQLITE_MAX_SQL_LENGTH ); - assert( aHardLimit[SQLITE_LIMIT_COLUMN]==SQLITE_MAX_COLUMN ); - assert( aHardLimit[SQLITE_LIMIT_EXPR_DEPTH]==SQLITE_MAX_EXPR_DEPTH ); - assert( aHardLimit[SQLITE_LIMIT_COMPOUND_SELECT]==SQLITE_MAX_COMPOUND_SELECT); - assert( aHardLimit[SQLITE_LIMIT_VDBE_OP]==SQLITE_MAX_VDBE_OP ); - assert( aHardLimit[SQLITE_LIMIT_FUNCTION_ARG]==SQLITE_MAX_FUNCTION_ARG ); - assert( aHardLimit[SQLITE_LIMIT_ATTACHED]==SQLITE_MAX_ATTACHED ); - assert( aHardLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]== - SQLITE_MAX_LIKE_PATTERN_LENGTH ); - assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER); - assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH ); - assert( SQLITE_LIMIT_TRIGGER_DEPTH==(SQLITE_N_LIMIT-1) ); - - - if( limitId<0 || limitId>=SQLITE_N_LIMIT ){ - return -1; - } - oldLimit = db->aLimit[limitId]; - if( newLimit>=0 ){ /* IMP: R-52476-28732 */ - if( newLimit>aHardLimit[limitId] ){ - newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ - } - db->aLimit[limitId] = newLimit; - } - return oldLimit; /* IMP: R-53341-35419 */ -} - -/* -** This function is used to parse both URIs and non-URI filenames passed by the -** user to API functions sqlite3_open() or sqlite3_open_v2(), and for database -** URIs specified as part of ATTACH statements. -** -** The first argument to this function is the name of the VFS to use (or -** a NULL to signify the default VFS) if the URI does not contain a "vfs=xxx" -** query parameter. The second argument contains the URI (or non-URI filename) -** itself. When this function is called the *pFlags variable should contain -** the default flags to open the database handle with. The value stored in -** *pFlags may be updated before returning if the URI filename contains -** "cache=xxx" or "mode=xxx" query parameters. -** -** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to -** the VFS that should be used to open the database file. *pzFile is set to -** point to a buffer containing the name of the file to open. It is the -** responsibility of the caller to eventually call sqlite3_free() to release -** this buffer. -** -** If an error occurs, then an SQLite error code is returned and *pzErrMsg -** may be set to point to a buffer containing an English language error -** message. It is the responsibility of the caller to eventually release -** this buffer by calling sqlite3_free(). -*/ -SQLITE_PRIVATE int sqlite3ParseUri( - const char *zDefaultVfs, /* VFS to use if no "vfs=xxx" query option */ - const char *zUri, /* Nul-terminated URI to parse */ - unsigned int *pFlags, /* IN/OUT: SQLITE_OPEN_XXX flags */ - sqlite3_vfs **ppVfs, /* OUT: VFS to use */ - char **pzFile, /* OUT: Filename component of URI */ - char **pzErrMsg /* OUT: Error message (if rc!=SQLITE_OK) */ -){ - int rc = SQLITE_OK; - unsigned int flags = *pFlags; - const char *zVfs = zDefaultVfs; - char *zFile; - char c; - int nUri = sqlite3Strlen30(zUri); - - assert( *pzErrMsg==0 ); - - if( ((flags & SQLITE_OPEN_URI) || sqlite3GlobalConfig.bOpenUri) - && nUri>=5 && memcmp(zUri, "file:", 5)==0 - ){ - char *zOpt; - int eState; /* Parser state when parsing URI */ - int iIn; /* Input character index */ - int iOut = 0; /* Output character index */ - int nByte = nUri+2; /* Bytes of space to allocate */ - - /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen - ** method that there may be extra parameters following the file-name. */ - flags |= SQLITE_OPEN_URI; - - for(iIn=0; iIn=0 && octet<256 ); - if( octet==0 ){ - /* This branch is taken when "%00" appears within the URI. In this - ** case we ignore all text in the remainder of the path, name or - ** value currently being parsed. So ignore the current character - ** and skip to the next "?", "=" or "&", as appropriate. */ - while( (c = zUri[iIn])!=0 && c!='#' - && (eState!=0 || c!='?') - && (eState!=1 || (c!='=' && c!='&')) - && (eState!=2 || c!='&') - ){ - iIn++; - } - continue; - } - c = octet; - }else if( eState==1 && (c=='&' || c=='=') ){ - if( zFile[iOut-1]==0 ){ - /* An empty option name. Ignore this option altogether. */ - while( zUri[iIn] && zUri[iIn]!='#' && zUri[iIn-1]!='&' ) iIn++; - continue; - } - if( c=='&' ){ - zFile[iOut++] = '\0'; - }else{ - eState = 2; - } - c = 0; - }else if( (eState==0 && c=='?') || (eState==2 && c=='&') ){ - c = 0; - eState = 1; - } - zFile[iOut++] = c; - } - if( eState==1 ) zFile[iOut++] = '\0'; - zFile[iOut++] = '\0'; - zFile[iOut++] = '\0'; - - /* Check if there were any options specified that should be interpreted - ** here. Options that are interpreted here include "vfs" and those that - ** correspond to flags that may be passed to the sqlite3_open_v2() - ** method. */ - zOpt = &zFile[sqlite3Strlen30(zFile)+1]; - while( zOpt[0] ){ - int nOpt = sqlite3Strlen30(zOpt); - char *zVal = &zOpt[nOpt+1]; - int nVal = sqlite3Strlen30(zVal); - - if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){ - zVfs = zVal; - }else{ - struct OpenMode { - const char *z; - int mode; - } *aMode = 0; - char *zModeType = 0; - int mask = 0; - int limit = 0; - - if( nOpt==5 && memcmp("cache", zOpt, 5)==0 ){ - static struct OpenMode aCacheMode[] = { - { "shared", SQLITE_OPEN_SHAREDCACHE }, - { "private", SQLITE_OPEN_PRIVATECACHE }, - { 0, 0 } - }; - - mask = SQLITE_OPEN_SHAREDCACHE|SQLITE_OPEN_PRIVATECACHE; - aMode = aCacheMode; - limit = mask; - zModeType = "cache"; - } - if( nOpt==4 && memcmp("mode", zOpt, 4)==0 ){ - static struct OpenMode aOpenMode[] = { - { "ro", SQLITE_OPEN_READONLY }, - { "rw", SQLITE_OPEN_READWRITE }, - { "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE }, - { "memory", SQLITE_OPEN_MEMORY }, - { 0, 0 } - }; - - mask = SQLITE_OPEN_READONLY | SQLITE_OPEN_READWRITE - | SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY; - aMode = aOpenMode; - limit = mask & flags; - zModeType = "access"; - } - - if( aMode ){ - int i; - int mode = 0; - for(i=0; aMode[i].z; i++){ - const char *z = aMode[i].z; - if( nVal==sqlite3Strlen30(z) && 0==memcmp(zVal, z, nVal) ){ - mode = aMode[i].mode; - break; - } - } - if( mode==0 ){ - *pzErrMsg = sqlite3_mprintf("no such %s mode: %s", zModeType, zVal); - rc = SQLITE_ERROR; - goto parse_uri_out; - } - if( (mode & ~SQLITE_OPEN_MEMORY)>limit ){ - *pzErrMsg = sqlite3_mprintf("%s mode not allowed: %s", - zModeType, zVal); - rc = SQLITE_PERM; - goto parse_uri_out; - } - flags = (flags & ~mask) | mode; - } - } - - zOpt = &zVal[nVal+1]; - } - - }else{ - zFile = sqlite3_malloc(nUri+2); - if( !zFile ) return SQLITE_NOMEM; - memcpy(zFile, zUri, nUri); - zFile[nUri] = '\0'; - zFile[nUri+1] = '\0'; - flags &= ~SQLITE_OPEN_URI; - } - - *ppVfs = sqlite3_vfs_find(zVfs); - if( *ppVfs==0 ){ - *pzErrMsg = sqlite3_mprintf("no such vfs: %s", zVfs); - rc = SQLITE_ERROR; - } - parse_uri_out: - if( rc!=SQLITE_OK ){ - sqlite3_free(zFile); - zFile = 0; - } - *pFlags = flags; - *pzFile = zFile; - return rc; -} - - -/* -** This routine does the work of opening a database on behalf of -** sqlite3_open() and sqlite3_open16(). The database filename "zFilename" -** is UTF-8 encoded. -*/ -static int openDatabase( - const char *zFilename, /* Database filename UTF-8 encoded */ - sqlite3 **ppDb, /* OUT: Returned database handle */ - unsigned int flags, /* Operational flags */ - const char *zVfs /* Name of the VFS to use */ -){ - sqlite3 *db; /* Store allocated handle here */ - int rc; /* Return code */ - int isThreadsafe; /* True for threadsafe connections */ - char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ - char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ - - *ppDb = 0; -#ifndef SQLITE_OMIT_AUTOINIT - rc = sqlite3_initialize(); - if( rc ) return rc; -#endif - - /* Only allow sensible combinations of bits in the flags argument. - ** Throw an error if any non-sense combination is used. If we - ** do not block illegal combinations here, it could trigger - ** assert() statements in deeper layers. Sensible combinations - ** are: - ** - ** 1: SQLITE_OPEN_READONLY - ** 2: SQLITE_OPEN_READWRITE - ** 6: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE - */ - assert( SQLITE_OPEN_READONLY == 0x01 ); - assert( SQLITE_OPEN_READWRITE == 0x02 ); - assert( SQLITE_OPEN_CREATE == 0x04 ); - testcase( (1<<(flags&7))==0x02 ); /* READONLY */ - testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ - testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ - if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE_BKPT; - - if( sqlite3GlobalConfig.bCoreMutex==0 ){ - isThreadsafe = 0; - }else if( flags & SQLITE_OPEN_NOMUTEX ){ - isThreadsafe = 0; - }else if( flags & SQLITE_OPEN_FULLMUTEX ){ - isThreadsafe = 1; - }else{ - isThreadsafe = sqlite3GlobalConfig.bFullMutex; - } - if( flags & SQLITE_OPEN_PRIVATECACHE ){ - flags &= ~SQLITE_OPEN_SHAREDCACHE; - }else if( sqlite3GlobalConfig.sharedCacheEnabled ){ - flags |= SQLITE_OPEN_SHAREDCACHE; - } - - /* Remove harmful bits from the flags parameter - ** - ** The SQLITE_OPEN_NOMUTEX and SQLITE_OPEN_FULLMUTEX flags were - ** dealt with in the previous code block. Besides these, the only - ** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY, - ** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE, - ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask - ** off all other flags. - */ - flags &= ~( SQLITE_OPEN_DELETEONCLOSE | - SQLITE_OPEN_EXCLUSIVE | - SQLITE_OPEN_MAIN_DB | - SQLITE_OPEN_TEMP_DB | - SQLITE_OPEN_TRANSIENT_DB | - SQLITE_OPEN_MAIN_JOURNAL | - SQLITE_OPEN_TEMP_JOURNAL | - SQLITE_OPEN_SUBJOURNAL | - SQLITE_OPEN_MASTER_JOURNAL | - SQLITE_OPEN_NOMUTEX | - SQLITE_OPEN_FULLMUTEX | - SQLITE_OPEN_WAL - ); - - /* Allocate the sqlite data structure */ - db = sqlite3MallocZero( sizeof(sqlite3) ); - if( db==0 ) goto opendb_out; - if( isThreadsafe ){ - db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); - if( db->mutex==0 ){ - sqlite3_free(db); - db = 0; - goto opendb_out; - } - } - sqlite3_mutex_enter(db->mutex); - db->errMask = 0xff; - db->nDb = 2; - db->magic = SQLITE_MAGIC_BUSY; - db->aDb = db->aDbStatic; - - assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); - memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); - db->autoCommit = 1; - db->nextAutovac = -1; - db->nextPagesize = 0; - db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger -#if SQLITE_DEFAULT_FILE_FORMAT<4 - | SQLITE_LegacyFileFmt -#endif -#ifdef SQLITE_ENABLE_LOAD_EXTENSION - | SQLITE_LoadExtension -#endif -#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS - | SQLITE_RecTriggers -#endif -#if defined(SQLITE_DEFAULT_FOREIGN_KEYS) && SQLITE_DEFAULT_FOREIGN_KEYS - | SQLITE_ForeignKeys -#endif - ; - sqlite3HashInit(&db->aCollSeq); -#ifndef SQLITE_OMIT_VIRTUALTABLE - sqlite3HashInit(&db->aModule); -#endif - - /* Add the default collation sequence BINARY. BINARY works for both UTF-8 - ** and UTF-16, so add a version for each to avoid any unnecessary - ** conversions. The only error that can occur here is a malloc() failure. - */ - createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0); - createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0); - createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0); - createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0); - if( db->mallocFailed ){ - goto opendb_out; - } - db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0); - assert( db->pDfltColl!=0 ); - - /* Also add a UTF-8 case-insensitive collation sequence. */ - createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0); - - /* Parse the filename/URI argument. */ - db->openFlags = flags; - rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; - sqlite3Error(db, rc, zErrMsg ? "%s" : 0, zErrMsg); - sqlite3_free(zErrMsg); - goto opendb_out; - } - - /* Open the backend database driver */ - rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0, - flags | SQLITE_OPEN_MAIN_DB); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_IOERR_NOMEM ){ - rc = SQLITE_NOMEM; - } - sqlite3Error(db, rc, 0); - goto opendb_out; - } - db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); - db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); - - - /* The default safety_level for the main database is 'full'; for the temp - ** database it is 'NONE'. This matches the pager layer defaults. - */ - db->aDb[0].zName = "main"; - db->aDb[0].safety_level = 3; - db->aDb[1].zName = "temp"; - db->aDb[1].safety_level = 1; - - db->magic = SQLITE_MAGIC_OPEN; - if( db->mallocFailed ){ - goto opendb_out; - } - - /* Register all built-in functions, but do not attempt to read the - ** database schema yet. This is delayed until the first time the database - ** is accessed. - */ - sqlite3Error(db, SQLITE_OK, 0); - sqlite3RegisterBuiltinFunctions(db); - - /* Load automatic extensions - extensions that have been registered - ** using the sqlite3_automatic_extension() API. - */ - rc = sqlite3_errcode(db); - if( rc==SQLITE_OK ){ - sqlite3AutoLoadExtensions(db); - rc = sqlite3_errcode(db); - if( rc!=SQLITE_OK ){ - goto opendb_out; - } - } - -#ifdef SQLITE_ENABLE_FTS1 - if( !db->mallocFailed ){ - extern int sqlite3Fts1Init(sqlite3*); - rc = sqlite3Fts1Init(db); - } -#endif - -#ifdef SQLITE_ENABLE_FTS2 - if( !db->mallocFailed && rc==SQLITE_OK ){ - extern int sqlite3Fts2Init(sqlite3*); - rc = sqlite3Fts2Init(db); - } -#endif - -#ifdef SQLITE_ENABLE_FTS3 - if( !db->mallocFailed && rc==SQLITE_OK ){ - rc = sqlite3Fts3Init(db); - } -#endif - -#ifdef SQLITE_ENABLE_ICU - if( !db->mallocFailed && rc==SQLITE_OK ){ - rc = sqlite3IcuInit(db); - } -#endif - -#ifdef SQLITE_ENABLE_RTREE - if( !db->mallocFailed && rc==SQLITE_OK){ - rc = sqlite3RtreeInit(db); - } -#endif - - sqlite3Error(db, rc, 0); - - /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking - ** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking - ** mode. Doing nothing at all also makes NORMAL the default. - */ -#ifdef SQLITE_DEFAULT_LOCKING_MODE - db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; - sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt), - SQLITE_DEFAULT_LOCKING_MODE); -#endif - - /* Enable the lookaside-malloc subsystem */ - setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, - sqlite3GlobalConfig.nLookaside); - - sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT); - -opendb_out: - sqlite3_free(zOpen); - if( db ){ - assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 ); - sqlite3_mutex_leave(db->mutex); - } - rc = sqlite3_errcode(db); - assert( db!=0 || rc==SQLITE_NOMEM ); - if( rc==SQLITE_NOMEM ){ - sqlite3_close(db); - db = 0; - }else if( rc!=SQLITE_OK ){ - db->magic = SQLITE_MAGIC_SICK; - } - *ppDb = db; -#ifdef SQLITE_ENABLE_SQLLOG - if( sqlite3GlobalConfig.xSqllog ){ - /* Opening a db handle. Fourth parameter is passed 0. */ - void *pArg = sqlite3GlobalConfig.pSqllogArg; - sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0); - } -#endif - return sqlite3ApiExit(0, rc); -} - -/* -** Open a new database handle. -*/ -SQLITE_API int sqlite3_open( - const char *zFilename, - sqlite3 **ppDb -){ - return openDatabase(zFilename, ppDb, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); -} -SQLITE_API int sqlite3_open_v2( - const char *filename, /* Database filename (UTF-8) */ - sqlite3 **ppDb, /* OUT: SQLite db handle */ - int flags, /* Flags */ - const char *zVfs /* Name of VFS module to use */ -){ - return openDatabase(filename, ppDb, (unsigned int)flags, zVfs); -} - -#ifndef SQLITE_OMIT_UTF16 -/* -** Open a new database handle. -*/ -SQLITE_API int sqlite3_open16( - const void *zFilename, - sqlite3 **ppDb -){ - char const *zFilename8; /* zFilename encoded in UTF-8 instead of UTF-16 */ - sqlite3_value *pVal; - int rc; - - assert( zFilename ); - assert( ppDb ); - *ppDb = 0; -#ifndef SQLITE_OMIT_AUTOINIT - rc = sqlite3_initialize(); - if( rc ) return rc; -#endif - pVal = sqlite3ValueNew(0); - sqlite3ValueSetStr(pVal, -1, zFilename, SQLITE_UTF16NATIVE, SQLITE_STATIC); - zFilename8 = sqlite3ValueText(pVal, SQLITE_UTF8); - if( zFilename8 ){ - rc = openDatabase(zFilename8, ppDb, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); - assert( *ppDb || rc==SQLITE_NOMEM ); - if( rc==SQLITE_OK && !DbHasProperty(*ppDb, 0, DB_SchemaLoaded) ){ - ENC(*ppDb) = SQLITE_UTF16NATIVE; - } - }else{ - rc = SQLITE_NOMEM; - } - sqlite3ValueFree(pVal); - - return sqlite3ApiExit(0, rc); -} -#endif /* SQLITE_OMIT_UTF16 */ - -/* -** Register a new collation sequence with the database handle db. -*/ -SQLITE_API int sqlite3_create_collation( - sqlite3* db, - const char *zName, - int enc, - void* pCtx, - int(*xCompare)(void*,int,const void*,int,const void*) -){ - int rc; - sqlite3_mutex_enter(db->mutex); - assert( !db->mallocFailed ); - rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, 0); - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/* -** Register a new collation sequence with the database handle db. -*/ -SQLITE_API int sqlite3_create_collation_v2( - sqlite3* db, - const char *zName, - int enc, - void* pCtx, - int(*xCompare)(void*,int,const void*,int,const void*), - void(*xDel)(void*) -){ - int rc; - sqlite3_mutex_enter(db->mutex); - assert( !db->mallocFailed ); - rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, xDel); - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -#ifndef SQLITE_OMIT_UTF16 -/* -** Register a new collation sequence with the database handle db. -*/ -SQLITE_API int sqlite3_create_collation16( - sqlite3* db, - const void *zName, - int enc, - void* pCtx, - int(*xCompare)(void*,int,const void*,int,const void*) -){ - int rc = SQLITE_OK; - char *zName8; - sqlite3_mutex_enter(db->mutex); - assert( !db->mallocFailed ); - zName8 = sqlite3Utf16to8(db, zName, -1, SQLITE_UTF16NATIVE); - if( zName8 ){ - rc = createCollation(db, zName8, (u8)enc, pCtx, xCompare, 0); - sqlite3DbFree(db, zName8); - } - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} -#endif /* SQLITE_OMIT_UTF16 */ - -/* -** Register a collation sequence factory callback with the database handle -** db. Replace any previously installed collation sequence factory. -*/ -SQLITE_API int sqlite3_collation_needed( - sqlite3 *db, - void *pCollNeededArg, - void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*) -){ - sqlite3_mutex_enter(db->mutex); - db->xCollNeeded = xCollNeeded; - db->xCollNeeded16 = 0; - db->pCollNeededArg = pCollNeededArg; - sqlite3_mutex_leave(db->mutex); - return SQLITE_OK; -} - -#ifndef SQLITE_OMIT_UTF16 -/* -** Register a collation sequence factory callback with the database handle -** db. Replace any previously installed collation sequence factory. -*/ -SQLITE_API int sqlite3_collation_needed16( - sqlite3 *db, - void *pCollNeededArg, - void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*) -){ - sqlite3_mutex_enter(db->mutex); - db->xCollNeeded = 0; - db->xCollNeeded16 = xCollNeeded16; - db->pCollNeededArg = pCollNeededArg; - sqlite3_mutex_leave(db->mutex); - return SQLITE_OK; -} -#endif /* SQLITE_OMIT_UTF16 */ - -#ifndef SQLITE_OMIT_DEPRECATED -/* -** This function is now an anachronism. It used to be used to recover from a -** malloc() failure, but SQLite now does this automatically. -*/ -SQLITE_API int sqlite3_global_recover(void){ - return SQLITE_OK; -} -#endif - -/* -** Test to see whether or not the database connection is in autocommit -** mode. Return TRUE if it is and FALSE if not. Autocommit mode is on -** by default. Autocommit is disabled by a BEGIN statement and reenabled -** by the next COMMIT or ROLLBACK. -** -******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** -*/ -SQLITE_API int sqlite3_get_autocommit(sqlite3 *db){ - return db->autoCommit; -} - -/* -** The following routines are subtitutes for constants SQLITE_CORRUPT, -** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_IOERR and possibly other error -** constants. They server two purposes: -** -** 1. Serve as a convenient place to set a breakpoint in a debugger -** to detect when version error conditions occurs. -** -** 2. Invoke sqlite3_log() to provide the source code location where -** a low-level error is first detected. -*/ -SQLITE_PRIVATE int sqlite3CorruptError(int lineno){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_CORRUPT, - "database corruption at line %d of [%.10s]", - lineno, 20+sqlite3_sourceid()); - return SQLITE_CORRUPT; -} -SQLITE_PRIVATE int sqlite3MisuseError(int lineno){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_MISUSE, - "misuse at line %d of [%.10s]", - lineno, 20+sqlite3_sourceid()); - return SQLITE_MISUSE; -} -SQLITE_PRIVATE int sqlite3CantopenError(int lineno){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_CANTOPEN, - "cannot open file at line %d of [%.10s]", - lineno, 20+sqlite3_sourceid()); - return SQLITE_CANTOPEN; -} - - -#ifndef SQLITE_OMIT_DEPRECATED -/* -** This is a convenience routine that makes sure that all thread-specific -** data for this thread has been deallocated. -** -** SQLite no longer uses thread-specific data so this routine is now a -** no-op. It is retained for historical compatibility. -*/ -SQLITE_API void sqlite3_thread_cleanup(void){ -} -#endif - -/* -** Return meta information about a specific column of a database table. -** See comment in sqlite3.h (sqlite.h.in) for details. -*/ -#ifdef SQLITE_ENABLE_COLUMN_METADATA -SQLITE_API int sqlite3_table_column_metadata( - sqlite3 *db, /* Connection handle */ - const char *zDbName, /* Database name or NULL */ - const char *zTableName, /* Table name */ - const char *zColumnName, /* Column name */ - char const **pzDataType, /* OUTPUT: Declared data type */ - char const **pzCollSeq, /* OUTPUT: Collation sequence name */ - int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ - int *pPrimaryKey, /* OUTPUT: True if column part of PK */ - int *pAutoinc /* OUTPUT: True if column is auto-increment */ -){ - int rc; - char *zErrMsg = 0; - Table *pTab = 0; - Column *pCol = 0; - int iCol; - - char const *zDataType = 0; - char const *zCollSeq = 0; - int notnull = 0; - int primarykey = 0; - int autoinc = 0; - - /* Ensure the database schema has been loaded */ - sqlite3_mutex_enter(db->mutex); - sqlite3BtreeEnterAll(db); - rc = sqlite3Init(db, &zErrMsg); - if( SQLITE_OK!=rc ){ - goto error_out; - } - - /* Locate the table in question */ - pTab = sqlite3FindTable(db, zTableName, zDbName); - if( !pTab || pTab->pSelect ){ - pTab = 0; - goto error_out; - } - - /* Find the column for which info is requested */ - if( sqlite3IsRowid(zColumnName) ){ - iCol = pTab->iPKey; - if( iCol>=0 ){ - pCol = &pTab->aCol[iCol]; - } - }else{ - for(iCol=0; iColnCol; iCol++){ - pCol = &pTab->aCol[iCol]; - if( 0==sqlite3StrICmp(pCol->zName, zColumnName) ){ - break; - } - } - if( iCol==pTab->nCol ){ - pTab = 0; - goto error_out; - } - } - - /* The following block stores the meta information that will be returned - ** to the caller in local variables zDataType, zCollSeq, notnull, primarykey - ** and autoinc. At this point there are two possibilities: - ** - ** 1. The specified column name was rowid", "oid" or "_rowid_" - ** and there is no explicitly declared IPK column. - ** - ** 2. The table is not a view and the column name identified an - ** explicitly declared column. Copy meta information from *pCol. - */ - if( pCol ){ - zDataType = pCol->zType; - zCollSeq = pCol->zColl; - notnull = pCol->notNull!=0; - primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0; - autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0; - }else{ - zDataType = "INTEGER"; - primarykey = 1; - } - if( !zCollSeq ){ - zCollSeq = "BINARY"; - } - -error_out: - sqlite3BtreeLeaveAll(db); - - /* Whether the function call succeeded or failed, set the output parameters - ** to whatever their local counterparts contain. If an error did occur, - ** this has the effect of zeroing all output parameters. - */ - if( pzDataType ) *pzDataType = zDataType; - if( pzCollSeq ) *pzCollSeq = zCollSeq; - if( pNotNull ) *pNotNull = notnull; - if( pPrimaryKey ) *pPrimaryKey = primarykey; - if( pAutoinc ) *pAutoinc = autoinc; - - if( SQLITE_OK==rc && !pTab ){ - sqlite3DbFree(db, zErrMsg); - zErrMsg = sqlite3MPrintf(db, "no such table column: %s.%s", zTableName, - zColumnName); - rc = SQLITE_ERROR; - } - sqlite3Error(db, rc, (zErrMsg?"%s":0), zErrMsg); - sqlite3DbFree(db, zErrMsg); - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -} -#endif - -/* -** Sleep for a little while. Return the amount of time slept. -*/ -SQLITE_API int sqlite3_sleep(int ms){ - sqlite3_vfs *pVfs; - int rc; - pVfs = sqlite3_vfs_find(0); - if( pVfs==0 ) return 0; - - /* This function works in milliseconds, but the underlying OsSleep() - ** API uses microseconds. Hence the 1000's. - */ - rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000); - return rc; -} - -/* -** Enable or disable the extended result codes. -*/ -SQLITE_API int sqlite3_extended_result_codes(sqlite3 *db, int onoff){ - sqlite3_mutex_enter(db->mutex); - db->errMask = onoff ? 0xffffffff : 0xff; - sqlite3_mutex_leave(db->mutex); - return SQLITE_OK; -} - -/* -** Invoke the xFileControl method on a particular database. -*/ -SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ - int rc = SQLITE_ERROR; - Btree *pBtree; - - sqlite3_mutex_enter(db->mutex); - pBtree = sqlite3DbNameToBtree(db, zDbName); - if( pBtree ){ - Pager *pPager; - sqlite3_file *fd; - sqlite3BtreeEnter(pBtree); - pPager = sqlite3BtreePager(pBtree); - assert( pPager!=0 ); - fd = sqlite3PagerFile(pPager); - assert( fd!=0 ); - if( op==SQLITE_FCNTL_FILE_POINTER ){ - *(sqlite3_file**)pArg = fd; - rc = SQLITE_OK; - }else if( fd->pMethods ){ - rc = sqlite3OsFileControl(fd, op, pArg); - }else{ - rc = SQLITE_NOTFOUND; - } - sqlite3BtreeLeave(pBtree); - } - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/* -** Interface to the testing logic. -*/ -SQLITE_API int sqlite3_test_control(int op, ...){ - int rc = 0; -#ifndef SQLITE_OMIT_BUILTIN_TEST - va_list ap; - va_start(ap, op); - switch( op ){ - - /* - ** Save the current state of the PRNG. - */ - case SQLITE_TESTCTRL_PRNG_SAVE: { - sqlite3PrngSaveState(); - break; - } - - /* - ** Restore the state of the PRNG to the last state saved using - ** PRNG_SAVE. If PRNG_SAVE has never before been called, then - ** this verb acts like PRNG_RESET. - */ - case SQLITE_TESTCTRL_PRNG_RESTORE: { - sqlite3PrngRestoreState(); - break; - } - - /* - ** Reset the PRNG back to its uninitialized state. The next call - ** to sqlite3_randomness() will reseed the PRNG using a single call - ** to the xRandomness method of the default VFS. - */ - case SQLITE_TESTCTRL_PRNG_RESET: { - sqlite3PrngResetState(); - break; - } - - /* - ** sqlite3_test_control(BITVEC_TEST, size, program) - ** - ** Run a test against a Bitvec object of size. The program argument - ** is an array of integers that defines the test. Return -1 on a - ** memory allocation error, 0 on success, or non-zero for an error. - ** See the sqlite3BitvecBuiltinTest() for additional information. - */ - case SQLITE_TESTCTRL_BITVEC_TEST: { - int sz = va_arg(ap, int); - int *aProg = va_arg(ap, int*); - rc = sqlite3BitvecBuiltinTest(sz, aProg); - break; - } - - /* - ** sqlite3_test_control(BENIGN_MALLOC_HOOKS, xBegin, xEnd) - ** - ** Register hooks to call to indicate which malloc() failures - ** are benign. - */ - case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: { - typedef void (*void_function)(void); - void_function xBenignBegin; - void_function xBenignEnd; - xBenignBegin = va_arg(ap, void_function); - xBenignEnd = va_arg(ap, void_function); - sqlite3BenignMallocHooks(xBenignBegin, xBenignEnd); - break; - } - - /* - ** sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, unsigned int X) - ** - ** Set the PENDING byte to the value in the argument, if X>0. - ** Make no changes if X==0. Return the value of the pending byte - ** as it existing before this routine was called. - ** - ** IMPORTANT: Changing the PENDING byte from 0x40000000 results in - ** an incompatible database file format. Changing the PENDING byte - ** while any database connection is open results in undefined and - ** dileterious behavior. - */ - case SQLITE_TESTCTRL_PENDING_BYTE: { - rc = PENDING_BYTE; -#ifndef SQLITE_OMIT_WSD - { - unsigned int newVal = va_arg(ap, unsigned int); - if( newVal ) sqlite3PendingByte = newVal; - } -#endif - break; - } - - /* - ** sqlite3_test_control(SQLITE_TESTCTRL_ASSERT, int X) - ** - ** This action provides a run-time test to see whether or not - ** assert() was enabled at compile-time. If X is true and assert() - ** is enabled, then the return value is true. If X is true and - ** assert() is disabled, then the return value is zero. If X is - ** false and assert() is enabled, then the assertion fires and the - ** process aborts. If X is false and assert() is disabled, then the - ** return value is zero. - */ - case SQLITE_TESTCTRL_ASSERT: { - volatile int x = 0; - assert( (x = va_arg(ap,int))!=0 ); - rc = x; - break; - } - - - /* - ** sqlite3_test_control(SQLITE_TESTCTRL_ALWAYS, int X) - ** - ** This action provides a run-time test to see how the ALWAYS and - ** NEVER macros were defined at compile-time. - ** - ** The return value is ALWAYS(X). - ** - ** The recommended test is X==2. If the return value is 2, that means - ** ALWAYS() and NEVER() are both no-op pass-through macros, which is the - ** default setting. If the return value is 1, then ALWAYS() is either - ** hard-coded to true or else it asserts if its argument is false. - ** The first behavior (hard-coded to true) is the case if - ** SQLITE_TESTCTRL_ASSERT shows that assert() is disabled and the second - ** behavior (assert if the argument to ALWAYS() is false) is the case if - ** SQLITE_TESTCTRL_ASSERT shows that assert() is enabled. - ** - ** The run-time test procedure might look something like this: - ** - ** if( sqlite3_test_control(SQLITE_TESTCTRL_ALWAYS, 2)==2 ){ - ** // ALWAYS() and NEVER() are no-op pass-through macros - ** }else if( sqlite3_test_control(SQLITE_TESTCTRL_ASSERT, 1) ){ - ** // ALWAYS(x) asserts that x is true. NEVER(x) asserts x is false. - ** }else{ - ** // ALWAYS(x) is a constant 1. NEVER(x) is a constant 0. - ** } - */ - case SQLITE_TESTCTRL_ALWAYS: { - int x = va_arg(ap,int); - rc = ALWAYS(x); - break; - } - - /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N) - ** - ** Set the nReserve size to N for the main database on the database - ** connection db. - */ - case SQLITE_TESTCTRL_RESERVE: { - sqlite3 *db = va_arg(ap, sqlite3*); - int x = va_arg(ap,int); - sqlite3_mutex_enter(db->mutex); - sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0); - sqlite3_mutex_leave(db->mutex); - break; - } - - /* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N) - ** - ** Enable or disable various optimizations for testing purposes. The - ** argument N is a bitmask of optimizations to be disabled. For normal - ** operation N should be 0. The idea is that a test program (like the - ** SQL Logic Test or SLT test module) can run the same SQL multiple times - ** with various optimizations disabled to verify that the same answer - ** is obtained in every case. - */ - case SQLITE_TESTCTRL_OPTIMIZATIONS: { - sqlite3 *db = va_arg(ap, sqlite3*); - db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff); - break; - } - -#ifdef SQLITE_N_KEYWORD - /* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord) - ** - ** If zWord is a keyword recognized by the parser, then return the - ** number of keywords. Or if zWord is not a keyword, return 0. - ** - ** This test feature is only available in the amalgamation since - ** the SQLITE_N_KEYWORD macro is not defined in this file if SQLite - ** is built using separate source files. - */ - case SQLITE_TESTCTRL_ISKEYWORD: { - const char *zWord = va_arg(ap, const char*); - int n = sqlite3Strlen30(zWord); - rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0; - break; - } -#endif - - /* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree); - ** - ** Pass pFree into sqlite3ScratchFree(). - ** If sz>0 then allocate a scratch buffer into pNew. - */ - case SQLITE_TESTCTRL_SCRATCHMALLOC: { - void *pFree, **ppNew; - int sz; - sz = va_arg(ap, int); - ppNew = va_arg(ap, void**); - pFree = va_arg(ap, void*); - if( sz ) *ppNew = sqlite3ScratchMalloc(sz); - sqlite3ScratchFree(pFree); - break; - } - - /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff); - ** - ** If parameter onoff is non-zero, configure the wrappers so that all - ** subsequent calls to localtime() and variants fail. If onoff is zero, - ** undo this setting. - */ - case SQLITE_TESTCTRL_LOCALTIME_FAULT: { - sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int); - break; - } - -#if defined(SQLITE_ENABLE_TREE_EXPLAIN) - /* sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, - ** sqlite3_stmt*,const char**); - ** - ** If compiled with SQLITE_ENABLE_TREE_EXPLAIN, each sqlite3_stmt holds - ** a string that describes the optimized parse tree. This test-control - ** returns a pointer to that string. - */ - case SQLITE_TESTCTRL_EXPLAIN_STMT: { - sqlite3_stmt *pStmt = va_arg(ap, sqlite3_stmt*); - const char **pzRet = va_arg(ap, const char**); - *pzRet = sqlite3VdbeExplanation((Vdbe*)pStmt); - break; - } -#endif - - } - va_end(ap); -#endif /* SQLITE_OMIT_BUILTIN_TEST */ - return rc; -} - -/* -** This is a utility routine, useful to VFS implementations, that checks -** to see if a database file was a URI that contained a specific query -** parameter, and if so obtains the value of the query parameter. -** -** The zFilename argument is the filename pointer passed into the xOpen() -** method of a VFS implementation. The zParam argument is the name of the -** query parameter we seek. This routine returns the value of the zParam -** parameter if it exists. If the parameter does not exist, this routine -** returns a NULL pointer. -*/ -SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){ - if( zFilename==0 ) return 0; - zFilename += sqlite3Strlen30(zFilename) + 1; - while( zFilename[0] ){ - int x = strcmp(zFilename, zParam); - zFilename += sqlite3Strlen30(zFilename) + 1; - if( x==0 ) return zFilename; - zFilename += sqlite3Strlen30(zFilename) + 1; - } - return 0; -} - -/* -** Return a boolean value for a query parameter. -*/ -SQLITE_API int sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){ - const char *z = sqlite3_uri_parameter(zFilename, zParam); - bDflt = bDflt!=0; - return z ? sqlite3GetBoolean(z, bDflt) : bDflt; -} - -/* -** Return a 64-bit integer value for a query parameter. -*/ -SQLITE_API sqlite3_int64 sqlite3_uri_int64( - const char *zFilename, /* Filename as passed to xOpen */ - const char *zParam, /* URI parameter sought */ - sqlite3_int64 bDflt /* return if parameter is missing */ -){ - const char *z = sqlite3_uri_parameter(zFilename, zParam); - sqlite3_int64 v; - if( z && sqlite3Atoi64(z, &v, sqlite3Strlen30(z), SQLITE_UTF8)==SQLITE_OK ){ - bDflt = v; - } - return bDflt; -} - -/* -** Return the Btree pointer identified by zDbName. Return NULL if not found. -*/ -SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ - int i; - for(i=0; inDb; i++){ - if( db->aDb[i].pBt - && (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zName)==0) - ){ - return db->aDb[i].pBt; - } - } - return 0; -} - -/* -** Return the filename of the database associated with a database -** connection. -*/ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ - Btree *pBt = sqlite3DbNameToBtree(db, zDbName); - return pBt ? sqlite3BtreeGetFilename(pBt) : 0; -} - -/* -** Return 1 if database is read-only or 0 if read/write. Return -1 if -** no such database exists. -*/ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ - Btree *pBt = sqlite3DbNameToBtree(db, zDbName); - return pBt ? sqlite3PagerIsreadonly(sqlite3BtreePager(pBt)) : -1; -} - -/************** End of main.c ************************************************/ -/************** Begin file notify.c ******************************************/ -/* -** 2009 March 3 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains the implementation of the sqlite3_unlock_notify() -** API method and its associated functionality. -*/ - -/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */ -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY - -/* -** Public interfaces: -** -** sqlite3ConnectionBlocked() -** sqlite3ConnectionUnlocked() -** sqlite3ConnectionClosed() -** sqlite3_unlock_notify() -*/ - -#define assertMutexHeld() \ - assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) ) - -/* -** Head of a linked list of all sqlite3 objects created by this process -** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection -** is not NULL. This variable may only accessed while the STATIC_MASTER -** mutex is held. -*/ -static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0; - -#ifndef NDEBUG -/* -** This function is a complex assert() that verifies the following -** properties of the blocked connections list: -** -** 1) Each entry in the list has a non-NULL value for either -** pUnlockConnection or pBlockingConnection, or both. -** -** 2) All entries in the list that share a common value for -** xUnlockNotify are grouped together. -** -** 3) If the argument db is not NULL, then none of the entries in the -** blocked connections list have pUnlockConnection or pBlockingConnection -** set to db. This is used when closing connection db. -*/ -static void checkListProperties(sqlite3 *db){ - sqlite3 *p; - for(p=sqlite3BlockedList; p; p=p->pNextBlocked){ - int seen = 0; - sqlite3 *p2; - - /* Verify property (1) */ - assert( p->pUnlockConnection || p->pBlockingConnection ); - - /* Verify property (2) */ - for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){ - if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1; - assert( p2->xUnlockNotify==p->xUnlockNotify || !seen ); - assert( db==0 || p->pUnlockConnection!=db ); - assert( db==0 || p->pBlockingConnection!=db ); - } - } -} -#else -# define checkListProperties(x) -#endif - -/* -** Remove connection db from the blocked connections list. If connection -** db is not currently a part of the list, this function is a no-op. -*/ -static void removeFromBlockedList(sqlite3 *db){ - sqlite3 **pp; - assertMutexHeld(); - for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){ - if( *pp==db ){ - *pp = (*pp)->pNextBlocked; - break; - } - } -} - -/* -** Add connection db to the blocked connections list. It is assumed -** that it is not already a part of the list. -*/ -static void addToBlockedList(sqlite3 *db){ - sqlite3 **pp; - assertMutexHeld(); - for( - pp=&sqlite3BlockedList; - *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify; - pp=&(*pp)->pNextBlocked - ); - db->pNextBlocked = *pp; - *pp = db; -} - -/* -** Obtain the STATIC_MASTER mutex. -*/ -static void enterMutex(void){ - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - checkListProperties(0); -} - -/* -** Release the STATIC_MASTER mutex. -*/ -static void leaveMutex(void){ - assertMutexHeld(); - checkListProperties(0); - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); -} - -/* -** Register an unlock-notify callback. -** -** This is called after connection "db" has attempted some operation -** but has received an SQLITE_LOCKED error because another connection -** (call it pOther) in the same process was busy using the same shared -** cache. pOther is found by looking at db->pBlockingConnection. -** -** If there is no blocking connection, the callback is invoked immediately, -** before this routine returns. -** -** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate -** a deadlock. -** -** Otherwise, make arrangements to invoke xNotify when pOther drops -** its locks. -** -** Each call to this routine overrides any prior callbacks registered -** on the same "db". If xNotify==0 then any prior callbacks are immediately -** cancelled. -*/ -SQLITE_API int sqlite3_unlock_notify( - sqlite3 *db, - void (*xNotify)(void **, int), - void *pArg -){ - int rc = SQLITE_OK; - - sqlite3_mutex_enter(db->mutex); - enterMutex(); - - if( xNotify==0 ){ - removeFromBlockedList(db); - db->pBlockingConnection = 0; - db->pUnlockConnection = 0; - db->xUnlockNotify = 0; - db->pUnlockArg = 0; - }else if( 0==db->pBlockingConnection ){ - /* The blocking transaction has been concluded. Or there never was a - ** blocking transaction. In either case, invoke the notify callback - ** immediately. - */ - xNotify(&pArg, 1); - }else{ - sqlite3 *p; - - for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){} - if( p ){ - rc = SQLITE_LOCKED; /* Deadlock detected. */ - }else{ - db->pUnlockConnection = db->pBlockingConnection; - db->xUnlockNotify = xNotify; - db->pUnlockArg = pArg; - removeFromBlockedList(db); - addToBlockedList(db); - } - } - - leaveMutex(); - assert( !db->mallocFailed ); - sqlite3Error(db, rc, (rc?"database is deadlocked":0)); - sqlite3_mutex_leave(db->mutex); - return rc; -} - -/* -** This function is called while stepping or preparing a statement -** associated with connection db. The operation will return SQLITE_LOCKED -** to the user because it requires a lock that will not be available -** until connection pBlocker concludes its current transaction. -*/ -SQLITE_PRIVATE void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){ - enterMutex(); - if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){ - addToBlockedList(db); - } - db->pBlockingConnection = pBlocker; - leaveMutex(); -} - -/* -** This function is called when -** the transaction opened by database db has just finished. Locks held -** by database connection db have been released. -** -** This function loops through each entry in the blocked connections -** list and does the following: -** -** 1) If the sqlite3.pBlockingConnection member of a list entry is -** set to db, then set pBlockingConnection=0. -** -** 2) If the sqlite3.pUnlockConnection member of a list entry is -** set to db, then invoke the configured unlock-notify callback and -** set pUnlockConnection=0. -** -** 3) If the two steps above mean that pBlockingConnection==0 and -** pUnlockConnection==0, remove the entry from the blocked connections -** list. -*/ -SQLITE_PRIVATE void sqlite3ConnectionUnlocked(sqlite3 *db){ - void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */ - int nArg = 0; /* Number of entries in aArg[] */ - sqlite3 **pp; /* Iterator variable */ - void **aArg; /* Arguments to the unlock callback */ - void **aDyn = 0; /* Dynamically allocated space for aArg[] */ - void *aStatic[16]; /* Starter space for aArg[]. No malloc required */ - - aArg = aStatic; - enterMutex(); /* Enter STATIC_MASTER mutex */ - - /* This loop runs once for each entry in the blocked-connections list. */ - for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){ - sqlite3 *p = *pp; - - /* Step 1. */ - if( p->pBlockingConnection==db ){ - p->pBlockingConnection = 0; - } - - /* Step 2. */ - if( p->pUnlockConnection==db ){ - assert( p->xUnlockNotify ); - if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){ - xUnlockNotify(aArg, nArg); - nArg = 0; - } - - sqlite3BeginBenignMalloc(); - assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) ); - assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn ); - if( (!aDyn && nArg==(int)ArraySize(aStatic)) - || (aDyn && nArg==(int)(sqlite3MallocSize(aDyn)/sizeof(void*))) - ){ - /* The aArg[] array needs to grow. */ - void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2); - if( pNew ){ - memcpy(pNew, aArg, nArg*sizeof(void *)); - sqlite3_free(aDyn); - aDyn = aArg = pNew; - }else{ - /* This occurs when the array of context pointers that need to - ** be passed to the unlock-notify callback is larger than the - ** aStatic[] array allocated on the stack and the attempt to - ** allocate a larger array from the heap has failed. - ** - ** This is a difficult situation to handle. Returning an error - ** code to the caller is insufficient, as even if an error code - ** is returned the transaction on connection db will still be - ** closed and the unlock-notify callbacks on blocked connections - ** will go unissued. This might cause the application to wait - ** indefinitely for an unlock-notify callback that will never - ** arrive. - ** - ** Instead, invoke the unlock-notify callback with the context - ** array already accumulated. We can then clear the array and - ** begin accumulating any further context pointers without - ** requiring any dynamic allocation. This is sub-optimal because - ** it means that instead of one callback with a large array of - ** context pointers the application will receive two or more - ** callbacks with smaller arrays of context pointers, which will - ** reduce the applications ability to prioritize multiple - ** connections. But it is the best that can be done under the - ** circumstances. - */ - xUnlockNotify(aArg, nArg); - nArg = 0; - } - } - sqlite3EndBenignMalloc(); - - aArg[nArg++] = p->pUnlockArg; - xUnlockNotify = p->xUnlockNotify; - p->pUnlockConnection = 0; - p->xUnlockNotify = 0; - p->pUnlockArg = 0; - } - - /* Step 3. */ - if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){ - /* Remove connection p from the blocked connections list. */ - *pp = p->pNextBlocked; - p->pNextBlocked = 0; - }else{ - pp = &p->pNextBlocked; - } - } - - if( nArg!=0 ){ - xUnlockNotify(aArg, nArg); - } - sqlite3_free(aDyn); - leaveMutex(); /* Leave STATIC_MASTER mutex */ -} - -/* -** This is called when the database connection passed as an argument is -** being closed. The connection is removed from the blocked list. -*/ -SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){ - sqlite3ConnectionUnlocked(db); - enterMutex(); - removeFromBlockedList(db); - checkListProperties(db); - leaveMutex(); -} -#endif - -/************** End of notify.c **********************************************/ -/************** Begin file fts3.c ********************************************/ -/* -** 2006 Oct 10 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This is an SQLite module implementing full-text search. -*/ - -/* -** The code in this file is only compiled if: -** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or -** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). -*/ - -/* The full-text index is stored in a series of b+tree (-like) -** structures called segments which map terms to doclists. The -** structures are like b+trees in layout, but are constructed from the -** bottom up in optimal fashion and are not updatable. Since trees -** are built from the bottom up, things will be described from the -** bottom up. -** -** -**** Varints **** -** The basic unit of encoding is a variable-length integer called a -** varint. We encode variable-length integers in little-endian order -** using seven bits * per byte as follows: -** -** KEY: -** A = 0xxxxxxx 7 bits of data and one flag bit -** B = 1xxxxxxx 7 bits of data and one flag bit -** -** 7 bits - A -** 14 bits - BA -** 21 bits - BBA -** and so on. -** -** This is similar in concept to how sqlite encodes "varints" but -** the encoding is not the same. SQLite varints are big-endian -** are are limited to 9 bytes in length whereas FTS3 varints are -** little-endian and can be up to 10 bytes in length (in theory). -** -** Example encodings: -** -** 1: 0x01 -** 127: 0x7f -** 128: 0x81 0x00 -** -** -**** Document lists **** -** A doclist (document list) holds a docid-sorted list of hits for a -** given term. Doclists hold docids and associated token positions. -** A docid is the unique integer identifier for a single document. -** A position is the index of a word within the document. The first -** word of the document has a position of 0. -** -** FTS3 used to optionally store character offsets using a compile-time -** option. But that functionality is no longer supported. -** -** A doclist is stored like this: -** -** array { -** varint docid; (delta from previous doclist) -** array { (position list for column 0) -** varint position; (2 more than the delta from previous position) -** } -** array { -** varint POS_COLUMN; (marks start of position list for new column) -** varint column; (index of new column) -** array { -** varint position; (2 more than the delta from previous position) -** } -** } -** varint POS_END; (marks end of positions for this document. -** } -** -** Here, array { X } means zero or more occurrences of X, adjacent in -** memory. A "position" is an index of a token in the token stream -** generated by the tokenizer. Note that POS_END and POS_COLUMN occur -** in the same logical place as the position element, and act as sentinals -** ending a position list array. POS_END is 0. POS_COLUMN is 1. -** The positions numbers are not stored literally but rather as two more -** than the difference from the prior position, or the just the position plus -** 2 for the first position. Example: -** -** label: A B C D E F G H I J K -** value: 123 5 9 1 1 14 35 0 234 72 0 -** -** The 123 value is the first docid. For column zero in this document -** there are two matches at positions 3 and 10 (5-2 and 9-2+3). The 1 -** at D signals the start of a new column; the 1 at E indicates that the -** new column is column number 1. There are two positions at 12 and 45 -** (14-2 and 35-2+12). The 0 at H indicate the end-of-document. The -** 234 at I is the delta to next docid (357). It has one position 70 -** (72-2) and then terminates with the 0 at K. -** -** A "position-list" is the list of positions for multiple columns for -** a single docid. A "column-list" is the set of positions for a single -** column. Hence, a position-list consists of one or more column-lists, -** a document record consists of a docid followed by a position-list and -** a doclist consists of one or more document records. -** -** A bare doclist omits the position information, becoming an -** array of varint-encoded docids. -** -**** Segment leaf nodes **** -** Segment leaf nodes store terms and doclists, ordered by term. Leaf -** nodes are written using LeafWriter, and read using LeafReader (to -** iterate through a single leaf node's data) and LeavesReader (to -** iterate through a segment's entire leaf layer). Leaf nodes have -** the format: -** -** varint iHeight; (height from leaf level, always 0) -** varint nTerm; (length of first term) -** char pTerm[nTerm]; (content of first term) -** varint nDoclist; (length of term's associated doclist) -** char pDoclist[nDoclist]; (content of doclist) -** array { -** (further terms are delta-encoded) -** varint nPrefix; (length of prefix shared with previous term) -** varint nSuffix; (length of unshared suffix) -** char pTermSuffix[nSuffix];(unshared suffix of next term) -** varint nDoclist; (length of term's associated doclist) -** char pDoclist[nDoclist]; (content of doclist) -** } -** -** Here, array { X } means zero or more occurrences of X, adjacent in -** memory. -** -** Leaf nodes are broken into blocks which are stored contiguously in -** the %_segments table in sorted order. This means that when the end -** of a node is reached, the next term is in the node with the next -** greater node id. -** -** New data is spilled to a new leaf node when the current node -** exceeds LEAF_MAX bytes (default 2048). New data which itself is -** larger than STANDALONE_MIN (default 1024) is placed in a standalone -** node (a leaf node with a single term and doclist). The goal of -** these settings is to pack together groups of small doclists while -** making it efficient to directly access large doclists. The -** assumption is that large doclists represent terms which are more -** likely to be query targets. -** -** TODO(shess) It may be useful for blocking decisions to be more -** dynamic. For instance, it may make more sense to have a 2.5k leaf -** node rather than splitting into 2k and .5k nodes. My intuition is -** that this might extend through 2x or 4x the pagesize. -** -** -**** Segment interior nodes **** -** Segment interior nodes store blockids for subtree nodes and terms -** to describe what data is stored by the each subtree. Interior -** nodes are written using InteriorWriter, and read using -** InteriorReader. InteriorWriters are created as needed when -** SegmentWriter creates new leaf nodes, or when an interior node -** itself grows too big and must be split. The format of interior -** nodes: -** -** varint iHeight; (height from leaf level, always >0) -** varint iBlockid; (block id of node's leftmost subtree) -** optional { -** varint nTerm; (length of first term) -** char pTerm[nTerm]; (content of first term) -** array { -** (further terms are delta-encoded) -** varint nPrefix; (length of shared prefix with previous term) -** varint nSuffix; (length of unshared suffix) -** char pTermSuffix[nSuffix]; (unshared suffix of next term) -** } -** } -** -** Here, optional { X } means an optional element, while array { X } -** means zero or more occurrences of X, adjacent in memory. -** -** An interior node encodes n terms separating n+1 subtrees. The -** subtree blocks are contiguous, so only the first subtree's blockid -** is encoded. The subtree at iBlockid will contain all terms less -** than the first term encoded (or all terms if no term is encoded). -** Otherwise, for terms greater than or equal to pTerm[i] but less -** than pTerm[i+1], the subtree for that term will be rooted at -** iBlockid+i. Interior nodes only store enough term data to -** distinguish adjacent children (if the rightmost term of the left -** child is "something", and the leftmost term of the right child is -** "wicked", only "w" is stored). -** -** New data is spilled to a new interior node at the same height when -** the current node exceeds INTERIOR_MAX bytes (default 2048). -** INTERIOR_MIN_TERMS (default 7) keeps large terms from monopolizing -** interior nodes and making the tree too skinny. The interior nodes -** at a given height are naturally tracked by interior nodes at -** height+1, and so on. -** -** -**** Segment directory **** -** The segment directory in table %_segdir stores meta-information for -** merging and deleting segments, and also the root node of the -** segment's tree. -** -** The root node is the top node of the segment's tree after encoding -** the entire segment, restricted to ROOT_MAX bytes (default 1024). -** This could be either a leaf node or an interior node. If the top -** node requires more than ROOT_MAX bytes, it is flushed to %_segments -** and a new root interior node is generated (which should always fit -** within ROOT_MAX because it only needs space for 2 varints, the -** height and the blockid of the previous root). -** -** The meta-information in the segment directory is: -** level - segment level (see below) -** idx - index within level -** - (level,idx uniquely identify a segment) -** start_block - first leaf node -** leaves_end_block - last leaf node -** end_block - last block (including interior nodes) -** root - contents of root node -** -** If the root node is a leaf node, then start_block, -** leaves_end_block, and end_block are all 0. -** -** -**** Segment merging **** -** To amortize update costs, segments are grouped into levels and -** merged in batches. Each increase in level represents exponentially -** more documents. -** -** New documents (actually, document updates) are tokenized and -** written individually (using LeafWriter) to a level 0 segment, with -** incrementing idx. When idx reaches MERGE_COUNT (default 16), all -** level 0 segments are merged into a single level 1 segment. Level 1 -** is populated like level 0, and eventually MERGE_COUNT level 1 -** segments are merged to a single level 2 segment (representing -** MERGE_COUNT^2 updates), and so on. -** -** A segment merge traverses all segments at a given level in -** parallel, performing a straightforward sorted merge. Since segment -** leaf nodes are written in to the %_segments table in order, this -** merge traverses the underlying sqlite disk structures efficiently. -** After the merge, all segment blocks from the merged level are -** deleted. -** -** MERGE_COUNT controls how often we merge segments. 16 seems to be -** somewhat of a sweet spot for insertion performance. 32 and 64 show -** very similar performance numbers to 16 on insertion, though they're -** a tiny bit slower (perhaps due to more overhead in merge-time -** sorting). 8 is about 20% slower than 16, 4 about 50% slower than -** 16, 2 about 66% slower than 16. -** -** At query time, high MERGE_COUNT increases the number of segments -** which need to be scanned and merged. For instance, with 100k docs -** inserted: -** -** MERGE_COUNT segments -** 16 25 -** 8 12 -** 4 10 -** 2 6 -** -** This appears to have only a moderate impact on queries for very -** frequent terms (which are somewhat dominated by segment merge -** costs), and infrequent and non-existent terms still seem to be fast -** even with many segments. -** -** TODO(shess) That said, it would be nice to have a better query-side -** argument for MERGE_COUNT of 16. Also, it is possible/likely that -** optimizations to things like doclist merging will swing the sweet -** spot around. -** -** -** -**** Handling of deletions and updates **** -** Since we're using a segmented structure, with no docid-oriented -** index into the term index, we clearly cannot simply update the term -** index when a document is deleted or updated. For deletions, we -** write an empty doclist (varint(docid) varint(POS_END)), for updates -** we simply write the new doclist. Segment merges overwrite older -** data for a particular docid with newer data, so deletes or updates -** will eventually overtake the earlier data and knock it out. The -** query logic likewise merges doclists so that newer data knocks out -** older data. -*/ - -/************** Include fts3Int.h in the middle of fts3.c ********************/ -/************** Begin file fts3Int.h *****************************************/ -/* -** 2009 Nov 12 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -*/ -#ifndef _FTSINT_H -#define _FTSINT_H - -#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) -# define NDEBUG 1 -#endif - -/* -** FTS4 is really an extension for FTS3. It is enabled using the -** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all -** the SQLITE_ENABLE_FTS4 macro to serve as an alisse for SQLITE_ENABLE_FTS3. -*/ -#if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) -# define SQLITE_ENABLE_FTS3 -#endif - -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* If not building as part of the core, include sqlite3ext.h. */ -#ifndef SQLITE_CORE -SQLITE_API extern const sqlite3_api_routines *sqlite3_api; -#endif - -/************** Include fts3_tokenizer.h in the middle of fts3Int.h **********/ -/************** Begin file fts3_tokenizer.h **********************************/ -/* -** 2006 July 10 -** -** The author disclaims copyright to this source code. -** -************************************************************************* -** Defines the interface to tokenizers used by fulltext-search. There -** are three basic components: -** -** sqlite3_tokenizer_module is a singleton defining the tokenizer -** interface functions. This is essentially the class structure for -** tokenizers. -** -** sqlite3_tokenizer is used to define a particular tokenizer, perhaps -** including customization information defined at creation time. -** -** sqlite3_tokenizer_cursor is generated by a tokenizer to generate -** tokens from a particular input. -*/ -#ifndef _FTS3_TOKENIZER_H_ -#define _FTS3_TOKENIZER_H_ - -/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time. -** If tokenizers are to be allowed to call sqlite3_*() functions, then -** we will need a way to register the API consistently. -*/ - -/* -** Structures used by the tokenizer interface. When a new tokenizer -** implementation is registered, the caller provides a pointer to -** an sqlite3_tokenizer_module containing pointers to the callback -** functions that make up an implementation. -** -** When an fts3 table is created, it passes any arguments passed to -** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the -** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer -** implementation. The xCreate() function in turn returns an -** sqlite3_tokenizer structure representing the specific tokenizer to -** be used for the fts3 table (customized by the tokenizer clause arguments). -** -** To tokenize an input buffer, the sqlite3_tokenizer_module.xOpen() -** method is called. It returns an sqlite3_tokenizer_cursor object -** that may be used to tokenize a specific input buffer based on -** the tokenization rules supplied by a specific sqlite3_tokenizer -** object. -*/ -typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module; -typedef struct sqlite3_tokenizer sqlite3_tokenizer; -typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor; - -struct sqlite3_tokenizer_module { - - /* - ** Structure version. Should always be set to 0 or 1. - */ - int iVersion; - - /* - ** Create a new tokenizer. The values in the argv[] array are the - ** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL - ** TABLE statement that created the fts3 table. For example, if - ** the following SQL is executed: - ** - ** CREATE .. USING fts3( ... , tokenizer arg1 arg2) - ** - ** then argc is set to 2, and the argv[] array contains pointers - ** to the strings "arg1" and "arg2". - ** - ** This method should return either SQLITE_OK (0), or an SQLite error - ** code. If SQLITE_OK is returned, then *ppTokenizer should be set - ** to point at the newly created tokenizer structure. The generic - ** sqlite3_tokenizer.pModule variable should not be initialized by - ** this callback. The caller will do so. - */ - int (*xCreate)( - int argc, /* Size of argv array */ - const char *const*argv, /* Tokenizer argument strings */ - sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */ - ); - - /* - ** Destroy an existing tokenizer. The fts3 module calls this method - ** exactly once for each successful call to xCreate(). - */ - int (*xDestroy)(sqlite3_tokenizer *pTokenizer); - - /* - ** Create a tokenizer cursor to tokenize an input buffer. The caller - ** is responsible for ensuring that the input buffer remains valid - ** until the cursor is closed (using the xClose() method). - */ - int (*xOpen)( - sqlite3_tokenizer *pTokenizer, /* Tokenizer object */ - const char *pInput, int nBytes, /* Input buffer */ - sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */ - ); - - /* - ** Destroy an existing tokenizer cursor. The fts3 module calls this - ** method exactly once for each successful call to xOpen(). - */ - int (*xClose)(sqlite3_tokenizer_cursor *pCursor); - - /* - ** Retrieve the next token from the tokenizer cursor pCursor. This - ** method should either return SQLITE_OK and set the values of the - ** "OUT" variables identified below, or SQLITE_DONE to indicate that - ** the end of the buffer has been reached, or an SQLite error code. - ** - ** *ppToken should be set to point at a buffer containing the - ** normalized version of the token (i.e. after any case-folding and/or - ** stemming has been performed). *pnBytes should be set to the length - ** of this buffer in bytes. The input text that generated the token is - ** identified by the byte offsets returned in *piStartOffset and - ** *piEndOffset. *piStartOffset should be set to the index of the first - ** byte of the token in the input buffer. *piEndOffset should be set - ** to the index of the first byte just past the end of the token in - ** the input buffer. - ** - ** The buffer *ppToken is set to point at is managed by the tokenizer - ** implementation. It is only required to be valid until the next call - ** to xNext() or xClose(). - */ - /* TODO(shess) current implementation requires pInput to be - ** nul-terminated. This should either be fixed, or pInput/nBytes - ** should be converted to zInput. - */ - int (*xNext)( - sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */ - const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */ - int *piStartOffset, /* OUT: Byte offset of token in input buffer */ - int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */ - int *piPosition /* OUT: Number of tokens returned before this one */ - ); - - /*********************************************************************** - ** Methods below this point are only available if iVersion>=1. - */ - - /* - ** Configure the language id of a tokenizer cursor. - */ - int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid); -}; - -struct sqlite3_tokenizer { - const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */ - /* Tokenizer implementations will typically add additional fields */ -}; - -struct sqlite3_tokenizer_cursor { - sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */ - /* Tokenizer implementations will typically add additional fields */ -}; - -int fts3_global_term_cnt(int iTerm, int iCol); -int fts3_term_cnt(int iTerm, int iCol); - - -#endif /* _FTS3_TOKENIZER_H_ */ - -/************** End of fts3_tokenizer.h **************************************/ -/************** Continuing where we left off in fts3Int.h ********************/ -/************** Include fts3_hash.h in the middle of fts3Int.h ***************/ -/************** Begin file fts3_hash.h ***************************************/ -/* -** 2001 September 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This is the header file for the generic hash-table implementation -** used in SQLite. We've modified it slightly to serve as a standalone -** hash table implementation for the full-text indexing module. -** -*/ -#ifndef _FTS3_HASH_H_ -#define _FTS3_HASH_H_ - -/* Forward declarations of structures. */ -typedef struct Fts3Hash Fts3Hash; -typedef struct Fts3HashElem Fts3HashElem; - -/* A complete hash table is an instance of the following structure. -** The internals of this structure are intended to be opaque -- client -** code should not attempt to access or modify the fields of this structure -** directly. Change this structure only by using the routines below. -** However, many of the "procedures" and "functions" for modifying and -** accessing this structure are really macros, so we can't really make -** this structure opaque. -*/ -struct Fts3Hash { - char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */ - char copyKey; /* True if copy of key made on insert */ - int count; /* Number of entries in this table */ - Fts3HashElem *first; /* The first element of the array */ - int htsize; /* Number of buckets in the hash table */ - struct _fts3ht { /* the hash table */ - int count; /* Number of entries with this hash */ - Fts3HashElem *chain; /* Pointer to first entry with this hash */ - } *ht; -}; - -/* Each element in the hash table is an instance of the following -** structure. All elements are stored on a single doubly-linked list. -** -** Again, this structure is intended to be opaque, but it can't really -** be opaque because it is used by macros. -*/ -struct Fts3HashElem { - Fts3HashElem *next, *prev; /* Next and previous elements in the table */ - void *data; /* Data associated with this element */ - void *pKey; int nKey; /* Key associated with this element */ -}; - -/* -** There are 2 different modes of operation for a hash table: -** -** FTS3_HASH_STRING pKey points to a string that is nKey bytes long -** (including the null-terminator, if any). Case -** is respected in comparisons. -** -** FTS3_HASH_BINARY pKey points to binary data nKey bytes long. -** memcmp() is used to compare keys. -** -** A copy of the key is made if the copyKey parameter to fts3HashInit is 1. -*/ -#define FTS3_HASH_STRING 1 -#define FTS3_HASH_BINARY 2 - -/* -** Access routines. To delete, insert a NULL pointer. -*/ -SQLITE_PRIVATE void sqlite3Fts3HashInit(Fts3Hash *pNew, char keyClass, char copyKey); -SQLITE_PRIVATE void *sqlite3Fts3HashInsert(Fts3Hash*, const void *pKey, int nKey, void *pData); -SQLITE_PRIVATE void *sqlite3Fts3HashFind(const Fts3Hash*, const void *pKey, int nKey); -SQLITE_PRIVATE void sqlite3Fts3HashClear(Fts3Hash*); -SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const void *, int); - -/* -** Shorthand for the functions above -*/ -#define fts3HashInit sqlite3Fts3HashInit -#define fts3HashInsert sqlite3Fts3HashInsert -#define fts3HashFind sqlite3Fts3HashFind -#define fts3HashClear sqlite3Fts3HashClear -#define fts3HashFindElem sqlite3Fts3HashFindElem - -/* -** Macros for looping over all elements of a hash table. The idiom is -** like this: -** -** Fts3Hash h; -** Fts3HashElem *p; -** ... -** for(p=fts3HashFirst(&h); p; p=fts3HashNext(p)){ -** SomeStructure *pData = fts3HashData(p); -** // do something with pData -** } -*/ -#define fts3HashFirst(H) ((H)->first) -#define fts3HashNext(E) ((E)->next) -#define fts3HashData(E) ((E)->data) -#define fts3HashKey(E) ((E)->pKey) -#define fts3HashKeysize(E) ((E)->nKey) - -/* -** Number of entries in a hash table -*/ -#define fts3HashCount(H) ((H)->count) - -#endif /* _FTS3_HASH_H_ */ - -/************** End of fts3_hash.h *******************************************/ -/************** Continuing where we left off in fts3Int.h ********************/ - -/* -** This constant controls how often segments are merged. Once there are -** FTS3_MERGE_COUNT segments of level N, they are merged into a single -** segment of level N+1. -*/ -#define FTS3_MERGE_COUNT 16 - -/* -** This is the maximum amount of data (in bytes) to store in the -** Fts3Table.pendingTerms hash table. Normally, the hash table is -** populated as documents are inserted/updated/deleted in a transaction -** and used to create a new segment when the transaction is committed. -** However if this limit is reached midway through a transaction, a new -** segment is created and the hash table cleared immediately. -*/ -#define FTS3_MAX_PENDING_DATA (1*1024*1024) - -/* -** Macro to return the number of elements in an array. SQLite has a -** similar macro called ArraySize(). Use a different name to avoid -** a collision when building an amalgamation with built-in FTS3. -*/ -#define SizeofArray(X) ((int)(sizeof(X)/sizeof(X[0]))) - - -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif -#ifndef MAX -# define MAX(x,y) ((x)>(y)?(x):(y)) -#endif - -/* -** Maximum length of a varint encoded integer. The varint format is different -** from that used by SQLite, so the maximum length is 10, not 9. -*/ -#define FTS3_VARINT_MAX 10 - -/* -** FTS4 virtual tables may maintain multiple indexes - one index of all terms -** in the document set and zero or more prefix indexes. All indexes are stored -** as one or more b+-trees in the %_segments and %_segdir tables. -** -** It is possible to determine which index a b+-tree belongs to based on the -** value stored in the "%_segdir.level" column. Given this value L, the index -** that the b+-tree belongs to is (L<<10). In other words, all b+-trees with -** level values between 0 and 1023 (inclusive) belong to index 0, all levels -** between 1024 and 2047 to index 1, and so on. -** -** It is considered impossible for an index to use more than 1024 levels. In -** theory though this may happen, but only after at least -** (FTS3_MERGE_COUNT^1024) separate flushes of the pending-terms tables. -*/ -#define FTS3_SEGDIR_MAXLEVEL 1024 -#define FTS3_SEGDIR_MAXLEVEL_STR "1024" - -/* -** The testcase() macro is only used by the amalgamation. If undefined, -** make it a no-op. -*/ -#ifndef testcase -# define testcase(X) -#endif - -/* -** Terminator values for position-lists and column-lists. -*/ -#define POS_COLUMN (1) /* Column-list terminator */ -#define POS_END (0) /* Position-list terminator */ - -/* -** This section provides definitions to allow the -** FTS3 extension to be compiled outside of the -** amalgamation. -*/ -#ifndef SQLITE_AMALGAMATION -/* -** Macros indicating that conditional expressions are always true or -** false. -*/ -#ifdef SQLITE_COVERAGE_TEST -# define ALWAYS(x) (1) -# define NEVER(X) (0) -#else -# define ALWAYS(x) (x) -# define NEVER(x) (x) -#endif - -/* -** Internal types used by SQLite. -*/ -typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */ -typedef short int i16; /* 2-byte (or larger) signed integer */ -typedef unsigned int u32; /* 4-byte unsigned integer */ -typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */ -typedef sqlite3_int64 i64; /* 8-byte signed integer */ - -/* -** Macro used to suppress compiler warnings for unused parameters. -*/ -#define UNUSED_PARAMETER(x) (void)(x) - -/* -** Activate assert() only if SQLITE_TEST is enabled. -*/ -#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) -# define NDEBUG 1 -#endif - -/* -** The TESTONLY macro is used to enclose variable declarations or -** other bits of code that are needed to support the arguments -** within testcase() and assert() macros. -*/ -#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) -# define TESTONLY(X) X -#else -# define TESTONLY(X) -#endif - -#endif /* SQLITE_AMALGAMATION */ - -#ifdef SQLITE_DEBUG -SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); -# define FTS_CORRUPT_VTAB sqlite3Fts3Corrupt() -#else -# define FTS_CORRUPT_VTAB SQLITE_CORRUPT_VTAB -#endif - -typedef struct Fts3Table Fts3Table; -typedef struct Fts3Cursor Fts3Cursor; -typedef struct Fts3Expr Fts3Expr; -typedef struct Fts3Phrase Fts3Phrase; -typedef struct Fts3PhraseToken Fts3PhraseToken; - -typedef struct Fts3Doclist Fts3Doclist; -typedef struct Fts3SegFilter Fts3SegFilter; -typedef struct Fts3DeferredToken Fts3DeferredToken; -typedef struct Fts3SegReader Fts3SegReader; -typedef struct Fts3MultiSegReader Fts3MultiSegReader; - -/* -** A connection to a fulltext index is an instance of the following -** structure. The xCreate and xConnect methods create an instance -** of this structure and xDestroy and xDisconnect free that instance. -** All other methods receive a pointer to the structure as one of their -** arguments. -*/ -struct Fts3Table { - sqlite3_vtab base; /* Base class used by SQLite core */ - sqlite3 *db; /* The database connection */ - const char *zDb; /* logical database name */ - const char *zName; /* virtual table name */ - int nColumn; /* number of named columns in virtual table */ - char **azColumn; /* column names. malloced */ - sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ - char *zContentTbl; /* content=xxx option, or NULL */ - char *zLanguageid; /* languageid=xxx option, or NULL */ - u8 bAutoincrmerge; /* True if automerge=1 */ - u32 nLeafAdd; /* Number of leaf blocks added this trans */ - - /* Precompiled statements used by the implementation. Each of these - ** statements is run and reset within a single virtual table API call. - */ - sqlite3_stmt *aStmt[37]; - - char *zReadExprlist; - char *zWriteExprlist; - - int nNodeSize; /* Soft limit for node size */ - u8 bFts4; /* True for FTS4, false for FTS3 */ - u8 bHasStat; /* True if %_stat table exists */ - u8 bHasDocsize; /* True if %_docsize table exists */ - u8 bDescIdx; /* True if doclists are in reverse order */ - u8 bIgnoreSavepoint; /* True to ignore xSavepoint invocations */ - int nPgsz; /* Page size for host database */ - char *zSegmentsTbl; /* Name of %_segments table */ - sqlite3_blob *pSegments; /* Blob handle open on %_segments table */ - - /* - ** The following array of hash tables is used to buffer pending index - ** updates during transactions. All pending updates buffered at any one - ** time must share a common language-id (see the FTS4 langid= feature). - ** The current language id is stored in variable iPrevLangid. - ** - ** A single FTS4 table may have multiple full-text indexes. For each index - ** there is an entry in the aIndex[] array. Index 0 is an index of all the - ** terms that appear in the document set. Each subsequent index in aIndex[] - ** is an index of prefixes of a specific length. - ** - ** Variable nPendingData contains an estimate the memory consumed by the - ** pending data structures, including hash table overhead, but not including - ** malloc overhead. When nPendingData exceeds nMaxPendingData, all hash - ** tables are flushed to disk. Variable iPrevDocid is the docid of the most - ** recently inserted record. - */ - int nIndex; /* Size of aIndex[] */ - struct Fts3Index { - int nPrefix; /* Prefix length (0 for main terms index) */ - Fts3Hash hPending; /* Pending terms table for this index */ - } *aIndex; - int nMaxPendingData; /* Max pending data before flush to disk */ - int nPendingData; /* Current bytes of pending data */ - sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */ - int iPrevLangid; /* Langid of recently inserted document */ - -#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) - /* State variables used for validating that the transaction control - ** methods of the virtual table are called at appropriate times. These - ** values do not contribute to FTS functionality; they are used for - ** verifying the operation of the SQLite core. - */ - int inTransaction; /* True after xBegin but before xCommit/xRollback */ - int mxSavepoint; /* Largest valid xSavepoint integer */ -#endif -}; - -/* -** When the core wants to read from the virtual table, it creates a -** virtual table cursor (an instance of the following structure) using -** the xOpen method. Cursors are destroyed using the xClose method. -*/ -struct Fts3Cursor { - sqlite3_vtab_cursor base; /* Base class used by SQLite core */ - i16 eSearch; /* Search strategy (see below) */ - u8 isEof; /* True if at End Of Results */ - u8 isRequireSeek; /* True if must seek pStmt to %_content row */ - sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ - Fts3Expr *pExpr; /* Parsed MATCH query string */ - int iLangid; /* Language being queried for */ - int nPhrase; /* Number of matchable phrases in query */ - Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */ - sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ - char *pNextId; /* Pointer into the body of aDoclist */ - char *aDoclist; /* List of docids for full-text queries */ - int nDoclist; /* Size of buffer at aDoclist */ - u8 bDesc; /* True to sort in descending order */ - int eEvalmode; /* An FTS3_EVAL_XX constant */ - int nRowAvg; /* Average size of database rows, in pages */ - sqlite3_int64 nDoc; /* Documents in table */ - - int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */ - u32 *aMatchinfo; /* Information about most recent match */ - int nMatchinfo; /* Number of elements in aMatchinfo[] */ - char *zMatchinfo; /* Matchinfo specification */ -}; - -#define FTS3_EVAL_FILTER 0 -#define FTS3_EVAL_NEXT 1 -#define FTS3_EVAL_MATCHINFO 2 - -/* -** The Fts3Cursor.eSearch member is always set to one of the following. -** Actualy, Fts3Cursor.eSearch can be greater than or equal to -** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index -** of the column to be searched. For example, in -** -** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d); -** SELECT docid FROM ex1 WHERE b MATCH 'one two three'; -** -** Because the LHS of the MATCH operator is 2nd column "b", -** Fts3Cursor.eSearch will be set to FTS3_FULLTEXT_SEARCH+1. (+0 for a, -** +1 for b, +2 for c, +3 for d.) If the LHS of MATCH were "ex1" -** indicating that all columns should be searched, -** then eSearch would be set to FTS3_FULLTEXT_SEARCH+4. -*/ -#define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */ -#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ -#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ - - -struct Fts3Doclist { - char *aAll; /* Array containing doclist (or NULL) */ - int nAll; /* Size of a[] in bytes */ - char *pNextDocid; /* Pointer to next docid */ - - sqlite3_int64 iDocid; /* Current docid (if pList!=0) */ - int bFreeList; /* True if pList should be sqlite3_free()d */ - char *pList; /* Pointer to position list following iDocid */ - int nList; /* Length of position list */ -}; - -/* -** A "phrase" is a sequence of one or more tokens that must match in -** sequence. A single token is the base case and the most common case. -** For a sequence of tokens contained in double-quotes (i.e. "one two three") -** nToken will be the number of tokens in the string. -*/ -struct Fts3PhraseToken { - char *z; /* Text of the token */ - int n; /* Number of bytes in buffer z */ - int isPrefix; /* True if token ends with a "*" character */ - int bFirst; /* True if token must appear at position 0 */ - - /* Variables above this point are populated when the expression is - ** parsed (by code in fts3_expr.c). Below this point the variables are - ** used when evaluating the expression. */ - Fts3DeferredToken *pDeferred; /* Deferred token object for this token */ - Fts3MultiSegReader *pSegcsr; /* Segment-reader for this token */ -}; - -struct Fts3Phrase { - /* Cache of doclist for this phrase. */ - Fts3Doclist doclist; - int bIncr; /* True if doclist is loaded incrementally */ - int iDoclistToken; - - /* Variables below this point are populated by fts3_expr.c when parsing - ** a MATCH expression. Everything above is part of the evaluation phase. - */ - int nToken; /* Number of tokens in the phrase */ - int iColumn; /* Index of column this phrase must match */ - Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ -}; - -/* -** A tree of these objects forms the RHS of a MATCH operator. -** -** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist -** points to a malloced buffer, size nDoclist bytes, containing the results -** of this phrase query in FTS3 doclist format. As usual, the initial -** "Length" field found in doclists stored on disk is omitted from this -** buffer. -** -** Variable aMI is used only for FTSQUERY_NEAR nodes to store the global -** matchinfo data. If it is not NULL, it points to an array of size nCol*3, -** where nCol is the number of columns in the queried FTS table. The array -** is populated as follows: -** -** aMI[iCol*3 + 0] = Undefined -** aMI[iCol*3 + 1] = Number of occurrences -** aMI[iCol*3 + 2] = Number of rows containing at least one instance -** -** The aMI array is allocated using sqlite3_malloc(). It should be freed -** when the expression node is. -*/ -struct Fts3Expr { - int eType; /* One of the FTSQUERY_XXX values defined below */ - int nNear; /* Valid if eType==FTSQUERY_NEAR */ - Fts3Expr *pParent; /* pParent->pLeft==this or pParent->pRight==this */ - Fts3Expr *pLeft; /* Left operand */ - Fts3Expr *pRight; /* Right operand */ - Fts3Phrase *pPhrase; /* Valid if eType==FTSQUERY_PHRASE */ - - /* The following are used by the fts3_eval.c module. */ - sqlite3_int64 iDocid; /* Current docid */ - u8 bEof; /* True this expression is at EOF already */ - u8 bStart; /* True if iDocid is valid */ - u8 bDeferred; /* True if this expression is entirely deferred */ - - u32 *aMI; -}; - -/* -** Candidate values for Fts3Query.eType. Note that the order of the first -** four values is in order of precedence when parsing expressions. For -** example, the following: -** -** "a OR b AND c NOT d NEAR e" -** -** is equivalent to: -** -** "a OR (b AND (c NOT (d NEAR e)))" -*/ -#define FTSQUERY_NEAR 1 -#define FTSQUERY_NOT 2 -#define FTSQUERY_AND 3 -#define FTSQUERY_OR 4 -#define FTSQUERY_PHRASE 5 - - -/* fts3_write.c */ -SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*); -SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *); -SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *); -SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *); -SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(int, int, sqlite3_int64, - sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); -SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( - Fts3Table*,int,const char*,int,int,Fts3SegReader**); -SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *); -SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, int, sqlite3_stmt **); -SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *); -SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*); - -SQLITE_PRIVATE int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **); -SQLITE_PRIVATE int sqlite3Fts3SelectDocsize(Fts3Table *, sqlite3_int64, sqlite3_stmt **); - -#ifndef SQLITE_DISABLE_FTS4_DEFERRED -SQLITE_PRIVATE void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *); -SQLITE_PRIVATE int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int); -SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *); -SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *); -SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *); -#else -# define sqlite3Fts3FreeDeferredTokens(x) -# define sqlite3Fts3DeferToken(x,y,z) SQLITE_OK -# define sqlite3Fts3CacheDeferredDoclists(x) SQLITE_OK -# define sqlite3Fts3FreeDeferredDoclists(x) -# define sqlite3Fts3DeferredTokenList(x,y,z) SQLITE_OK -#endif - -SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *); -SQLITE_PRIVATE int sqlite3Fts3MaxLevel(Fts3Table *, int *); - -/* Special values interpreted by sqlite3SegReaderCursor() */ -#define FTS3_SEGCURSOR_PENDING -1 -#define FTS3_SEGCURSOR_ALL -2 - -SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3MultiSegReader*, Fts3SegFilter*); -SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *); -SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *); - -SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(Fts3Table *, - int, int, int, const char *, int, int, int, Fts3MultiSegReader *); - -/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */ -#define FTS3_SEGMENT_REQUIRE_POS 0x00000001 -#define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002 -#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004 -#define FTS3_SEGMENT_PREFIX 0x00000008 -#define FTS3_SEGMENT_SCAN 0x00000010 -#define FTS3_SEGMENT_FIRST 0x00000020 - -/* Type passed as 4th argument to SegmentReaderIterate() */ -struct Fts3SegFilter { - const char *zTerm; - int nTerm; - int iCol; - int flags; -}; - -struct Fts3MultiSegReader { - /* Used internally by sqlite3Fts3SegReaderXXX() calls */ - Fts3SegReader **apSegment; /* Array of Fts3SegReader objects */ - int nSegment; /* Size of apSegment array */ - int nAdvance; /* How many seg-readers to advance */ - Fts3SegFilter *pFilter; /* Pointer to filter object */ - char *aBuffer; /* Buffer to merge doclists in */ - int nBuffer; /* Allocated size of aBuffer[] in bytes */ - - int iColFilter; /* If >=0, filter for this column */ - int bRestart; - - /* Used by fts3.c only. */ - int nCost; /* Cost of running iterator */ - int bLookup; /* True if a lookup of a single entry. */ - - /* Output values. Valid only after Fts3SegReaderStep() returns SQLITE_ROW. */ - char *zTerm; /* Pointer to term buffer */ - int nTerm; /* Size of zTerm in bytes */ - char *aDoclist; /* Pointer to doclist buffer */ - int nDoclist; /* Size of aDoclist[] in bytes */ -}; - -SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int); - -/* fts3.c */ -SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); -SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); -SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *); -SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64); -SQLITE_PRIVATE void sqlite3Fts3Dequote(char *); -SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*); -SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *); -SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *); -SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*); - -/* fts3_tokenizer.c */ -SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *); -SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *); -SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *, - sqlite3_tokenizer **, char ** -); -SQLITE_PRIVATE int sqlite3Fts3IsIdChar(char); - -/* fts3_snippet.c */ -SQLITE_PRIVATE void sqlite3Fts3Offsets(sqlite3_context*, Fts3Cursor*); -SQLITE_PRIVATE void sqlite3Fts3Snippet(sqlite3_context *, Fts3Cursor *, const char *, - const char *, const char *, int, int -); -SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *); - -/* fts3_expr.c */ -SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int, - char **, int, int, int, const char *, int, Fts3Expr ** -); -SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *); -#ifdef SQLITE_TEST -SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db); -SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db); -#endif - -SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer(sqlite3_tokenizer *, int, const char *, int, - sqlite3_tokenizer_cursor ** -); - -/* fts3_aux.c */ -SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db); - -SQLITE_PRIVATE void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *); - -SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart( - Fts3Table*, Fts3MultiSegReader*, int, const char*, int); -SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext( - Fts3Table *, Fts3MultiSegReader *, sqlite3_int64 *, char **, int *); -SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **); -SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *); -SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); - -/* fts3_unicode2.c (functions generated by parsing unicode text files) */ -#ifdef SQLITE_ENABLE_FTS4_UNICODE61 -SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int, int); -SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int); -SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); -#endif - -#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ -#endif /* _FTSINT_H */ - -/************** End of fts3Int.h *********************************************/ -/************** Continuing where we left off in fts3.c ***********************/ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) -# define SQLITE_CORE 1 -#endif - -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ - -#ifndef SQLITE_CORE - SQLITE_EXTENSION_INIT1 -#endif - -static int fts3EvalNext(Fts3Cursor *pCsr); -static int fts3EvalStart(Fts3Cursor *pCsr); -static int fts3TermSegReaderCursor( - Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **); - -/* -** Write a 64-bit variable-length integer to memory starting at p[0]. -** The length of data written will be between 1 and FTS3_VARINT_MAX bytes. -** The number of bytes written is returned. -*/ -SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){ - unsigned char *q = (unsigned char *) p; - sqlite_uint64 vu = v; - do{ - *q++ = (unsigned char) ((vu & 0x7f) | 0x80); - vu >>= 7; - }while( vu!=0 ); - q[-1] &= 0x7f; /* turn off high bit in final byte */ - assert( q - (unsigned char *)p <= FTS3_VARINT_MAX ); - return (int) (q - (unsigned char *)p); -} - -/* -** Read a 64-bit variable-length integer from memory starting at p[0]. -** Return the number of bytes read, or 0 on error. -** The value is stored in *v. -*/ -SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){ - const unsigned char *q = (const unsigned char *) p; - sqlite_uint64 x = 0, y = 1; - while( (*q&0x80)==0x80 && q-(unsigned char *)p>= 7; - }while( v!=0 ); - return i; -} - -/* -** Convert an SQL-style quoted string into a normal string by removing -** the quote characters. The conversion is done in-place. If the -** input does not begin with a quote character, then this routine -** is a no-op. -** -** Examples: -** -** "abc" becomes abc -** 'xyz' becomes xyz -** [pqr] becomes pqr -** `mno` becomes mno -** -*/ -SQLITE_PRIVATE void sqlite3Fts3Dequote(char *z){ - char quote; /* Quote character (if any ) */ - - quote = z[0]; - if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){ - int iIn = 1; /* Index of next byte to read from input */ - int iOut = 0; /* Index of next byte to write to output */ - - /* If the first byte was a '[', then the close-quote character is a ']' */ - if( quote=='[' ) quote = ']'; - - while( ALWAYS(z[iIn]) ){ - if( z[iIn]==quote ){ - if( z[iIn+1]!=quote ) break; - z[iOut++] = quote; - iIn += 2; - }else{ - z[iOut++] = z[iIn++]; - } - } - z[iOut] = '\0'; - } -} - -/* -** Read a single varint from the doclist at *pp and advance *pp to point -** to the first byte past the end of the varint. Add the value of the varint -** to *pVal. -*/ -static void fts3GetDeltaVarint(char **pp, sqlite3_int64 *pVal){ - sqlite3_int64 iVal; - *pp += sqlite3Fts3GetVarint(*pp, &iVal); - *pVal += iVal; -} - -/* -** When this function is called, *pp points to the first byte following a -** varint that is part of a doclist (or position-list, or any other list -** of varints). This function moves *pp to point to the start of that varint, -** and sets *pVal by the varint value. -** -** Argument pStart points to the first byte of the doclist that the -** varint is part of. -*/ -static void fts3GetReverseVarint( - char **pp, - char *pStart, - sqlite3_int64 *pVal -){ - sqlite3_int64 iVal; - char *p; - - /* Pointer p now points at the first byte past the varint we are - ** interested in. So, unless the doclist is corrupt, the 0x80 bit is - ** clear on character p[-1]. */ - for(p = (*pp)-2; p>=pStart && *p&0x80; p--); - p++; - *pp = p; - - sqlite3Fts3GetVarint(p, &iVal); - *pVal = iVal; -} - -/* -** The xDisconnect() virtual table method. -*/ -static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ - Fts3Table *p = (Fts3Table *)pVtab; - int i; - - assert( p->nPendingData==0 ); - assert( p->pSegments==0 ); - - /* Free any prepared statements held */ - for(i=0; iaStmt); i++){ - sqlite3_finalize(p->aStmt[i]); - } - sqlite3_free(p->zSegmentsTbl); - sqlite3_free(p->zReadExprlist); - sqlite3_free(p->zWriteExprlist); - sqlite3_free(p->zContentTbl); - sqlite3_free(p->zLanguageid); - - /* Invoke the tokenizer destructor to free the tokenizer. */ - p->pTokenizer->pModule->xDestroy(p->pTokenizer); - - sqlite3_free(p); - return SQLITE_OK; -} - -/* -** Construct one or more SQL statements from the format string given -** and then evaluate those statements. The success code is written -** into *pRc. -** -** If *pRc is initially non-zero then this routine is a no-op. -*/ -static void fts3DbExec( - int *pRc, /* Success code */ - sqlite3 *db, /* Database in which to run SQL */ - const char *zFormat, /* Format string for SQL */ - ... /* Arguments to the format string */ -){ - va_list ap; - char *zSql; - if( *pRc ) return; - va_start(ap, zFormat); - zSql = sqlite3_vmprintf(zFormat, ap); - va_end(ap); - if( zSql==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - *pRc = sqlite3_exec(db, zSql, 0, 0, 0); - sqlite3_free(zSql); - } -} - -/* -** The xDestroy() virtual table method. -*/ -static int fts3DestroyMethod(sqlite3_vtab *pVtab){ - Fts3Table *p = (Fts3Table *)pVtab; - int rc = SQLITE_OK; /* Return code */ - const char *zDb = p->zDb; /* Name of database (e.g. "main", "temp") */ - sqlite3 *db = p->db; /* Database handle */ - - /* Drop the shadow tables */ - if( p->zContentTbl==0 ){ - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", zDb, p->zName); - } - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", zDb,p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", zDb, p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", zDb, p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", zDb, p->zName); - - /* If everything has worked, invoke fts3DisconnectMethod() to free the - ** memory associated with the Fts3Table structure and return SQLITE_OK. - ** Otherwise, return an SQLite error code. - */ - return (rc==SQLITE_OK ? fts3DisconnectMethod(pVtab) : rc); -} - - -/* -** Invoke sqlite3_declare_vtab() to declare the schema for the FTS3 table -** passed as the first argument. This is done as part of the xConnect() -** and xCreate() methods. -** -** If *pRc is non-zero when this function is called, it is a no-op. -** Otherwise, if an error occurs, an SQLite error code is stored in *pRc -** before returning. -*/ -static void fts3DeclareVtab(int *pRc, Fts3Table *p){ - if( *pRc==SQLITE_OK ){ - int i; /* Iterator variable */ - int rc; /* Return code */ - char *zSql; /* SQL statement passed to declare_vtab() */ - char *zCols; /* List of user defined columns */ - const char *zLanguageid; - - zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid"); - sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); - - /* Create a list of user columns for the virtual table */ - zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]); - for(i=1; zCols && inColumn; i++){ - zCols = sqlite3_mprintf("%z%Q, ", zCols, p->azColumn[i]); - } - - /* Create the whole "CREATE TABLE" statement to pass to SQLite */ - zSql = sqlite3_mprintf( - "CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN, %Q HIDDEN)", - zCols, p->zName, zLanguageid - ); - if( !zCols || !zSql ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_declare_vtab(p->db, zSql); - } - - sqlite3_free(zSql); - sqlite3_free(zCols); - *pRc = rc; - } -} - -/* -** Create the %_stat table if it does not already exist. -*/ -SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int *pRc, Fts3Table *p){ - fts3DbExec(pRc, p->db, - "CREATE TABLE IF NOT EXISTS %Q.'%q_stat'" - "(id INTEGER PRIMARY KEY, value BLOB);", - p->zDb, p->zName - ); - if( (*pRc)==SQLITE_OK ) p->bHasStat = 1; -} - -/* -** Create the backing store tables (%_content, %_segments and %_segdir) -** required by the FTS3 table passed as the only argument. This is done -** as part of the vtab xCreate() method. -** -** If the p->bHasDocsize boolean is true (indicating that this is an -** FTS4 table, not an FTS3 table) then also create the %_docsize and -** %_stat tables required by FTS4. -*/ -static int fts3CreateTables(Fts3Table *p){ - int rc = SQLITE_OK; /* Return code */ - int i; /* Iterator variable */ - sqlite3 *db = p->db; /* The database connection */ - - if( p->zContentTbl==0 ){ - const char *zLanguageid = p->zLanguageid; - char *zContentCols; /* Columns of %_content table */ - - /* Create a list of user columns for the content table */ - zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY"); - for(i=0; zContentCols && inColumn; i++){ - char *z = p->azColumn[i]; - zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z); - } - if( zLanguageid && zContentCols ){ - zContentCols = sqlite3_mprintf("%z, langid", zContentCols, zLanguageid); - } - if( zContentCols==0 ) rc = SQLITE_NOMEM; - - /* Create the content table */ - fts3DbExec(&rc, db, - "CREATE TABLE %Q.'%q_content'(%s)", - p->zDb, p->zName, zContentCols - ); - sqlite3_free(zContentCols); - } - - /* Create other tables */ - fts3DbExec(&rc, db, - "CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);", - p->zDb, p->zName - ); - fts3DbExec(&rc, db, - "CREATE TABLE %Q.'%q_segdir'(" - "level INTEGER," - "idx INTEGER," - "start_block INTEGER," - "leaves_end_block INTEGER," - "end_block INTEGER," - "root BLOB," - "PRIMARY KEY(level, idx)" - ");", - p->zDb, p->zName - ); - if( p->bHasDocsize ){ - fts3DbExec(&rc, db, - "CREATE TABLE %Q.'%q_docsize'(docid INTEGER PRIMARY KEY, size BLOB);", - p->zDb, p->zName - ); - } - assert( p->bHasStat==p->bFts4 ); - if( p->bHasStat ){ - sqlite3Fts3CreateStatTable(&rc, p); - } - return rc; -} - -/* -** Store the current database page-size in bytes in p->nPgsz. -** -** If *pRc is non-zero when this function is called, it is a no-op. -** Otherwise, if an error occurs, an SQLite error code is stored in *pRc -** before returning. -*/ -static void fts3DatabasePageSize(int *pRc, Fts3Table *p){ - if( *pRc==SQLITE_OK ){ - int rc; /* Return code */ - char *zSql; /* SQL text "PRAGMA %Q.page_size" */ - sqlite3_stmt *pStmt; /* Compiled "PRAGMA %Q.page_size" statement */ - - zSql = sqlite3_mprintf("PRAGMA %Q.page_size", p->zDb); - if( !zSql ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); - if( rc==SQLITE_OK ){ - sqlite3_step(pStmt); - p->nPgsz = sqlite3_column_int(pStmt, 0); - rc = sqlite3_finalize(pStmt); - }else if( rc==SQLITE_AUTH ){ - p->nPgsz = 1024; - rc = SQLITE_OK; - } - } - assert( p->nPgsz>0 || rc!=SQLITE_OK ); - sqlite3_free(zSql); - *pRc = rc; - } -} - -/* -** "Special" FTS4 arguments are column specifications of the following form: -** -** = -** -** There may not be whitespace surrounding the "=" character. The -** term may be quoted, but the may not. -*/ -static int fts3IsSpecialColumn( - const char *z, - int *pnKey, - char **pzValue -){ - char *zValue; - const char *zCsr = z; - - while( *zCsr!='=' ){ - if( *zCsr=='\0' ) return 0; - zCsr++; - } - - *pnKey = (int)(zCsr-z); - zValue = sqlite3_mprintf("%s", &zCsr[1]); - if( zValue ){ - sqlite3Fts3Dequote(zValue); - } - *pzValue = zValue; - return 1; -} - -/* -** Append the output of a printf() style formatting to an existing string. -*/ -static void fts3Appendf( - int *pRc, /* IN/OUT: Error code */ - char **pz, /* IN/OUT: Pointer to string buffer */ - const char *zFormat, /* Printf format string to append */ - ... /* Arguments for printf format string */ -){ - if( *pRc==SQLITE_OK ){ - va_list ap; - char *z; - va_start(ap, zFormat); - z = sqlite3_vmprintf(zFormat, ap); - va_end(ap); - if( z && *pz ){ - char *z2 = sqlite3_mprintf("%s%s", *pz, z); - sqlite3_free(z); - z = z2; - } - if( z==0 ) *pRc = SQLITE_NOMEM; - sqlite3_free(*pz); - *pz = z; - } -} - -/* -** Return a copy of input string zInput enclosed in double-quotes (") and -** with all double quote characters escaped. For example: -** -** fts3QuoteId("un \"zip\"") -> "un \"\"zip\"\"" -** -** The pointer returned points to memory obtained from sqlite3_malloc(). It -** is the callers responsibility to call sqlite3_free() to release this -** memory. -*/ -static char *fts3QuoteId(char const *zInput){ - int nRet; - char *zRet; - nRet = 2 + (int)strlen(zInput)*2 + 1; - zRet = sqlite3_malloc(nRet); - if( zRet ){ - int i; - char *z = zRet; - *(z++) = '"'; - for(i=0; zInput[i]; i++){ - if( zInput[i]=='"' ) *(z++) = '"'; - *(z++) = zInput[i]; - } - *(z++) = '"'; - *(z++) = '\0'; - } - return zRet; -} - -/* -** Return a list of comma separated SQL expressions and a FROM clause that -** could be used in a SELECT statement such as the following: -** -** SELECT FROM %_content AS x ... -** -** to return the docid, followed by each column of text data in order -** from left to write. If parameter zFunc is not NULL, then instead of -** being returned directly each column of text data is passed to an SQL -** function named zFunc first. For example, if zFunc is "unzip" and the -** table has the three user-defined columns "a", "b", and "c", the following -** string is returned: -** -** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c') FROM %_content AS x" -** -** The pointer returned points to a buffer allocated by sqlite3_malloc(). It -** is the responsibility of the caller to eventually free it. -** -** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and -** a NULL pointer is returned). Otherwise, if an OOM error is encountered -** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If -** no error occurs, *pRc is left unmodified. -*/ -static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){ - char *zRet = 0; - char *zFree = 0; - char *zFunction; - int i; - - if( p->zContentTbl==0 ){ - if( !zFunc ){ - zFunction = ""; - }else{ - zFree = zFunction = fts3QuoteId(zFunc); - } - fts3Appendf(pRc, &zRet, "docid"); - for(i=0; inColumn; i++){ - fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]); - } - if( p->zLanguageid ){ - fts3Appendf(pRc, &zRet, ", x.%Q", "langid"); - } - sqlite3_free(zFree); - }else{ - fts3Appendf(pRc, &zRet, "rowid"); - for(i=0; inColumn; i++){ - fts3Appendf(pRc, &zRet, ", x.'%q'", p->azColumn[i]); - } - if( p->zLanguageid ){ - fts3Appendf(pRc, &zRet, ", x.%Q", p->zLanguageid); - } - } - fts3Appendf(pRc, &zRet, " FROM '%q'.'%q%s' AS x", - p->zDb, - (p->zContentTbl ? p->zContentTbl : p->zName), - (p->zContentTbl ? "" : "_content") - ); - return zRet; -} - -/* -** Return a list of N comma separated question marks, where N is the number -** of columns in the %_content table (one for the docid plus one for each -** user-defined text column). -** -** If argument zFunc is not NULL, then all but the first question mark -** is preceded by zFunc and an open bracket, and followed by a closed -** bracket. For example, if zFunc is "zip" and the FTS3 table has three -** user-defined text columns, the following string is returned: -** -** "?, zip(?), zip(?), zip(?)" -** -** The pointer returned points to a buffer allocated by sqlite3_malloc(). It -** is the responsibility of the caller to eventually free it. -** -** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and -** a NULL pointer is returned). Otherwise, if an OOM error is encountered -** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If -** no error occurs, *pRc is left unmodified. -*/ -static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){ - char *zRet = 0; - char *zFree = 0; - char *zFunction; - int i; - - if( !zFunc ){ - zFunction = ""; - }else{ - zFree = zFunction = fts3QuoteId(zFunc); - } - fts3Appendf(pRc, &zRet, "?"); - for(i=0; inColumn; i++){ - fts3Appendf(pRc, &zRet, ",%s(?)", zFunction); - } - if( p->zLanguageid ){ - fts3Appendf(pRc, &zRet, ", ?"); - } - sqlite3_free(zFree); - return zRet; -} - -/* -** This function interprets the string at (*pp) as a non-negative integer -** value. It reads the integer and sets *pnOut to the value read, then -** sets *pp to point to the byte immediately following the last byte of -** the integer value. -** -** Only decimal digits ('0'..'9') may be part of an integer value. -** -** If *pp does not being with a decimal digit SQLITE_ERROR is returned and -** the output value undefined. Otherwise SQLITE_OK is returned. -** -** This function is used when parsing the "prefix=" FTS4 parameter. -*/ -static int fts3GobbleInt(const char **pp, int *pnOut){ - const char *p; /* Iterator pointer */ - int nInt = 0; /* Output value */ - - for(p=*pp; p[0]>='0' && p[0]<='9'; p++){ - nInt = nInt * 10 + (p[0] - '0'); - } - if( p==*pp ) return SQLITE_ERROR; - *pnOut = nInt; - *pp = p; - return SQLITE_OK; -} - -/* -** This function is called to allocate an array of Fts3Index structures -** representing the indexes maintained by the current FTS table. FTS tables -** always maintain the main "terms" index, but may also maintain one or -** more "prefix" indexes, depending on the value of the "prefix=" parameter -** (if any) specified as part of the CREATE VIRTUAL TABLE statement. -** -** Argument zParam is passed the value of the "prefix=" option if one was -** specified, or NULL otherwise. -** -** If no error occurs, SQLITE_OK is returned and *apIndex set to point to -** the allocated array. *pnIndex is set to the number of elements in the -** array. If an error does occur, an SQLite error code is returned. -** -** Regardless of whether or not an error is returned, it is the responsibility -** of the caller to call sqlite3_free() on the output array to free it. -*/ -static int fts3PrefixParameter( - const char *zParam, /* ABC in prefix=ABC parameter to parse */ - int *pnIndex, /* OUT: size of *apIndex[] array */ - struct Fts3Index **apIndex /* OUT: Array of indexes for this table */ -){ - struct Fts3Index *aIndex; /* Allocated array */ - int nIndex = 1; /* Number of entries in array */ - - if( zParam && zParam[0] ){ - const char *p; - nIndex++; - for(p=zParam; *p; p++){ - if( *p==',' ) nIndex++; - } - } - - aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex); - *apIndex = aIndex; - *pnIndex = nIndex; - if( !aIndex ){ - return SQLITE_NOMEM; - } - - memset(aIndex, 0, sizeof(struct Fts3Index) * nIndex); - if( zParam ){ - const char *p = zParam; - int i; - for(i=1; i module name ("fts3" or "fts4") -** argv[1] -> database name -** argv[2] -> table name -** argv[...] -> "column name" and other module argument fields. -*/ -static int fts3InitVtab( - int isCreate, /* True for xCreate, false for xConnect */ - sqlite3 *db, /* The SQLite database connection */ - void *pAux, /* Hash table containing tokenizers */ - int argc, /* Number of elements in argv array */ - const char * const *argv, /* xCreate/xConnect argument array */ - sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ - char **pzErr /* Write any error message here */ -){ - Fts3Hash *pHash = (Fts3Hash *)pAux; - Fts3Table *p = 0; /* Pointer to allocated vtab */ - int rc = SQLITE_OK; /* Return code */ - int i; /* Iterator variable */ - int nByte; /* Size of allocation used for *p */ - int iCol; /* Column index */ - int nString = 0; /* Bytes required to hold all column names */ - int nCol = 0; /* Number of columns in the FTS table */ - char *zCsr; /* Space for holding column names */ - int nDb; /* Bytes required to hold database name */ - int nName; /* Bytes required to hold table name */ - int isFts4 = (argv[0][3]=='4'); /* True for FTS4, false for FTS3 */ - const char **aCol; /* Array of column names */ - sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ - - int nIndex; /* Size of aIndex[] array */ - struct Fts3Index *aIndex = 0; /* Array of indexes for this table */ - - /* The results of parsing supported FTS4 key=value options: */ - int bNoDocsize = 0; /* True to omit %_docsize table */ - int bDescIdx = 0; /* True to store descending indexes */ - char *zPrefix = 0; /* Prefix parameter value (or NULL) */ - char *zCompress = 0; /* compress=? parameter (or NULL) */ - char *zUncompress = 0; /* uncompress=? parameter (or NULL) */ - char *zContent = 0; /* content=? parameter (or NULL) */ - char *zLanguageid = 0; /* languageid=? parameter (or NULL) */ - - assert( strlen(argv[0])==4 ); - assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4) - || (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4) - ); - - nDb = (int)strlen(argv[1]) + 1; - nName = (int)strlen(argv[2]) + 1; - - aCol = (const char **)sqlite3_malloc(sizeof(const char *) * (argc-2) ); - if( !aCol ) return SQLITE_NOMEM; - memset((void *)aCol, 0, sizeof(const char *) * (argc-2)); - - /* Loop through all of the arguments passed by the user to the FTS3/4 - ** module (i.e. all the column names and special arguments). This loop - ** does the following: - ** - ** + Figures out the number of columns the FTSX table will have, and - ** the number of bytes of space that must be allocated to store copies - ** of the column names. - ** - ** + If there is a tokenizer specification included in the arguments, - ** initializes the tokenizer pTokenizer. - */ - for(i=3; rc==SQLITE_OK && i8 - && 0==sqlite3_strnicmp(z, "tokenize", 8) - && 0==sqlite3Fts3IsIdChar(z[8]) - ){ - rc = sqlite3Fts3InitTokenizer(pHash, &z[9], &pTokenizer, pzErr); - } - - /* Check if it is an FTS4 special argument. */ - else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){ - struct Fts4Option { - const char *zOpt; - int nOpt; - } aFts4Opt[] = { - { "matchinfo", 9 }, /* 0 -> MATCHINFO */ - { "prefix", 6 }, /* 1 -> PREFIX */ - { "compress", 8 }, /* 2 -> COMPRESS */ - { "uncompress", 10 }, /* 3 -> UNCOMPRESS */ - { "order", 5 }, /* 4 -> ORDER */ - { "content", 7 }, /* 5 -> CONTENT */ - { "languageid", 10 } /* 6 -> LANGUAGEID */ - }; - - int iOpt; - if( !zVal ){ - rc = SQLITE_NOMEM; - }else{ - for(iOpt=0; iOptnOpt && !sqlite3_strnicmp(z, pOp->zOpt, pOp->nOpt) ){ - break; - } - } - if( iOpt==SizeofArray(aFts4Opt) ){ - *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z); - rc = SQLITE_ERROR; - }else{ - switch( iOpt ){ - case 0: /* MATCHINFO */ - if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){ - *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); - rc = SQLITE_ERROR; - } - bNoDocsize = 1; - break; - - case 1: /* PREFIX */ - sqlite3_free(zPrefix); - zPrefix = zVal; - zVal = 0; - break; - - case 2: /* COMPRESS */ - sqlite3_free(zCompress); - zCompress = zVal; - zVal = 0; - break; - - case 3: /* UNCOMPRESS */ - sqlite3_free(zUncompress); - zUncompress = zVal; - zVal = 0; - break; - - case 4: /* ORDER */ - if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3)) - && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4)) - ){ - *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal); - rc = SQLITE_ERROR; - } - bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); - break; - - case 5: /* CONTENT */ - sqlite3_free(zContent); - zContent = zVal; - zVal = 0; - break; - - case 6: /* LANGUAGEID */ - assert( iOpt==6 ); - sqlite3_free(zLanguageid); - zLanguageid = zVal; - zVal = 0; - break; - } - } - sqlite3_free(zVal); - } - } - - /* Otherwise, the argument is a column name. */ - else { - nString += (int)(strlen(z) + 1); - aCol[nCol++] = z; - } - } - - /* If a content=xxx option was specified, the following: - ** - ** 1. Ignore any compress= and uncompress= options. - ** - ** 2. If no column names were specified as part of the CREATE VIRTUAL - ** TABLE statement, use all columns from the content table. - */ - if( rc==SQLITE_OK && zContent ){ - sqlite3_free(zCompress); - sqlite3_free(zUncompress); - zCompress = 0; - zUncompress = 0; - if( nCol==0 ){ - sqlite3_free((void*)aCol); - aCol = 0; - rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString); - - /* If a languageid= option was specified, remove the language id - ** column from the aCol[] array. */ - if( rc==SQLITE_OK && zLanguageid ){ - int j; - for(j=0; jdb = db; - p->nColumn = nCol; - p->nPendingData = 0; - p->azColumn = (char **)&p[1]; - p->pTokenizer = pTokenizer; - p->nMaxPendingData = FTS3_MAX_PENDING_DATA; - p->bHasDocsize = (isFts4 && bNoDocsize==0); - p->bHasStat = isFts4; - p->bFts4 = isFts4; - p->bDescIdx = bDescIdx; - p->bAutoincrmerge = 0xff; /* 0xff means setting unknown */ - p->zContentTbl = zContent; - p->zLanguageid = zLanguageid; - zContent = 0; - zLanguageid = 0; - TESTONLY( p->inTransaction = -1 ); - TESTONLY( p->mxSavepoint = -1 ); - - p->aIndex = (struct Fts3Index *)&p->azColumn[nCol]; - memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex); - p->nIndex = nIndex; - for(i=0; iaIndex[i].hPending, FTS3_HASH_STRING, 1); - } - - /* Fill in the zName and zDb fields of the vtab structure. */ - zCsr = (char *)&p->aIndex[nIndex]; - p->zName = zCsr; - memcpy(zCsr, argv[2], nName); - zCsr += nName; - p->zDb = zCsr; - memcpy(zCsr, argv[1], nDb); - zCsr += nDb; - - /* Fill in the azColumn array */ - for(iCol=0; iColazColumn[iCol] = zCsr; - zCsr += n+1; - assert( zCsr <= &((char *)p)[nByte] ); - } - - if( (zCompress==0)!=(zUncompress==0) ){ - char const *zMiss = (zCompress==0 ? "compress" : "uncompress"); - rc = SQLITE_ERROR; - *pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss); - } - p->zReadExprlist = fts3ReadExprList(p, zUncompress, &rc); - p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc); - if( rc!=SQLITE_OK ) goto fts3_init_out; - - /* If this is an xCreate call, create the underlying tables in the - ** database. TODO: For xConnect(), it could verify that said tables exist. - */ - if( isCreate ){ - rc = fts3CreateTables(p); - } - - /* Check to see if a legacy fts3 table has been "upgraded" by the - ** addition of a %_stat table so that it can use incremental merge. - */ - if( !isFts4 && !isCreate ){ - int rc2 = SQLITE_OK; - fts3DbExec(&rc2, db, "SELECT 1 FROM %Q.'%q_stat' WHERE id=2", - p->zDb, p->zName); - if( rc2==SQLITE_OK ) p->bHasStat = 1; - } - - /* Figure out the page-size for the database. This is required in order to - ** estimate the cost of loading large doclists from the database. */ - fts3DatabasePageSize(&rc, p); - p->nNodeSize = p->nPgsz-35; - - /* Declare the table schema to SQLite. */ - fts3DeclareVtab(&rc, p); - -fts3_init_out: - sqlite3_free(zPrefix); - sqlite3_free(aIndex); - sqlite3_free(zCompress); - sqlite3_free(zUncompress); - sqlite3_free(zContent); - sqlite3_free(zLanguageid); - sqlite3_free((void *)aCol); - if( rc!=SQLITE_OK ){ - if( p ){ - fts3DisconnectMethod((sqlite3_vtab *)p); - }else if( pTokenizer ){ - pTokenizer->pModule->xDestroy(pTokenizer); - } - }else{ - assert( p->pSegments==0 ); - *ppVTab = &p->base; - } - return rc; -} - -/* -** The xConnect() and xCreate() methods for the virtual table. All the -** work is done in function fts3InitVtab(). -*/ -static int fts3ConnectMethod( - sqlite3 *db, /* Database connection */ - void *pAux, /* Pointer to tokenizer hash table */ - int argc, /* Number of elements in argv array */ - const char * const *argv, /* xCreate/xConnect argument array */ - sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ - char **pzErr /* OUT: sqlite3_malloc'd error message */ -){ - return fts3InitVtab(0, db, pAux, argc, argv, ppVtab, pzErr); -} -static int fts3CreateMethod( - sqlite3 *db, /* Database connection */ - void *pAux, /* Pointer to tokenizer hash table */ - int argc, /* Number of elements in argv array */ - const char * const *argv, /* xCreate/xConnect argument array */ - sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ - char **pzErr /* OUT: sqlite3_malloc'd error message */ -){ - return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr); -} - -/* -** Implementation of the xBestIndex method for FTS3 tables. There -** are three possible strategies, in order of preference: -** -** 1. Direct lookup by rowid or docid. -** 2. Full-text search using a MATCH operator on a non-docid column. -** 3. Linear scan of %_content table. -*/ -static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ - Fts3Table *p = (Fts3Table *)pVTab; - int i; /* Iterator variable */ - int iCons = -1; /* Index of constraint to use */ - int iLangidCons = -1; /* Index of langid=x constraint, if present */ - - /* By default use a full table scan. This is an expensive option, - ** so search through the constraints to see if a more efficient - ** strategy is possible. - */ - pInfo->idxNum = FTS3_FULLSCAN_SEARCH; - pInfo->estimatedCost = 500000; - for(i=0; inConstraint; i++){ - struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i]; - if( pCons->usable==0 ) continue; - - /* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */ - if( iCons<0 - && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ - && (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1 ) - ){ - pInfo->idxNum = FTS3_DOCID_SEARCH; - pInfo->estimatedCost = 1.0; - iCons = i; - } - - /* A MATCH constraint. Use a full-text search. - ** - ** If there is more than one MATCH constraint available, use the first - ** one encountered. If there is both a MATCH constraint and a direct - ** rowid/docid lookup, prefer the MATCH strategy. This is done even - ** though the rowid/docid lookup is faster than a MATCH query, selecting - ** it would lead to an "unable to use function MATCH in the requested - ** context" error. - */ - if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH - && pCons->iColumn>=0 && pCons->iColumn<=p->nColumn - ){ - pInfo->idxNum = FTS3_FULLTEXT_SEARCH + pCons->iColumn; - pInfo->estimatedCost = 2.0; - iCons = i; - } - - /* Equality constraint on the langid column */ - if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ - && pCons->iColumn==p->nColumn + 2 - ){ - iLangidCons = i; - } - } - - if( iCons>=0 ){ - pInfo->aConstraintUsage[iCons].argvIndex = 1; - pInfo->aConstraintUsage[iCons].omit = 1; - } - if( iLangidCons>=0 ){ - pInfo->aConstraintUsage[iLangidCons].argvIndex = 2; - } - - /* Regardless of the strategy selected, FTS can deliver rows in rowid (or - ** docid) order. Both ascending and descending are possible. - */ - if( pInfo->nOrderBy==1 ){ - struct sqlite3_index_orderby *pOrder = &pInfo->aOrderBy[0]; - if( pOrder->iColumn<0 || pOrder->iColumn==p->nColumn+1 ){ - if( pOrder->desc ){ - pInfo->idxStr = "DESC"; - }else{ - pInfo->idxStr = "ASC"; - } - pInfo->orderByConsumed = 1; - } - } - - assert( p->pSegments==0 ); - return SQLITE_OK; -} - -/* -** Implementation of xOpen method. -*/ -static int fts3OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ - sqlite3_vtab_cursor *pCsr; /* Allocated cursor */ - - UNUSED_PARAMETER(pVTab); - - /* Allocate a buffer large enough for an Fts3Cursor structure. If the - ** allocation succeeds, zero it and return SQLITE_OK. Otherwise, - ** if the allocation fails, return SQLITE_NOMEM. - */ - *ppCsr = pCsr = (sqlite3_vtab_cursor *)sqlite3_malloc(sizeof(Fts3Cursor)); - if( !pCsr ){ - return SQLITE_NOMEM; - } - memset(pCsr, 0, sizeof(Fts3Cursor)); - return SQLITE_OK; -} - -/* -** Close the cursor. For additional information see the documentation -** on the xClose method of the virtual table interface. -*/ -static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){ - Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; - assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); - sqlite3_finalize(pCsr->pStmt); - sqlite3Fts3ExprFree(pCsr->pExpr); - sqlite3Fts3FreeDeferredTokens(pCsr); - sqlite3_free(pCsr->aDoclist); - sqlite3_free(pCsr->aMatchinfo); - assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); - sqlite3_free(pCsr); - return SQLITE_OK; -} - -/* -** If pCsr->pStmt has not been prepared (i.e. if pCsr->pStmt==0), then -** compose and prepare an SQL statement of the form: -** -** "SELECT FROM %_content WHERE rowid = ?" -** -** (or the equivalent for a content=xxx table) and set pCsr->pStmt to -** it. If an error occurs, return an SQLite error code. -** -** Otherwise, set *ppStmt to point to pCsr->pStmt and return SQLITE_OK. -*/ -static int fts3CursorSeekStmt(Fts3Cursor *pCsr, sqlite3_stmt **ppStmt){ - int rc = SQLITE_OK; - if( pCsr->pStmt==0 ){ - Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; - char *zSql; - zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist); - if( !zSql ) return SQLITE_NOMEM; - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); - sqlite3_free(zSql); - } - *ppStmt = pCsr->pStmt; - return rc; -} - -/* -** Position the pCsr->pStmt statement so that it is on the row -** of the %_content table that contains the last match. Return -** SQLITE_OK on success. -*/ -static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ - int rc = SQLITE_OK; - if( pCsr->isRequireSeek ){ - sqlite3_stmt *pStmt = 0; - - rc = fts3CursorSeekStmt(pCsr, &pStmt); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId); - pCsr->isRequireSeek = 0; - if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){ - return SQLITE_OK; - }else{ - rc = sqlite3_reset(pCsr->pStmt); - if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){ - /* If no row was found and no error has occurred, then the %_content - ** table is missing a row that is present in the full-text index. - ** The data structures are corrupt. */ - rc = FTS_CORRUPT_VTAB; - pCsr->isEof = 1; - } - } - } - } - - if( rc!=SQLITE_OK && pContext ){ - sqlite3_result_error_code(pContext, rc); - } - return rc; -} - -/* -** This function is used to process a single interior node when searching -** a b-tree for a term or term prefix. The node data is passed to this -** function via the zNode/nNode parameters. The term to search for is -** passed in zTerm/nTerm. -** -** If piFirst is not NULL, then this function sets *piFirst to the blockid -** of the child node that heads the sub-tree that may contain the term. -** -** If piLast is not NULL, then *piLast is set to the right-most child node -** that heads a sub-tree that may contain a term for which zTerm/nTerm is -** a prefix. -** -** If an OOM error occurs, SQLITE_NOMEM is returned. Otherwise, SQLITE_OK. -*/ -static int fts3ScanInteriorNode( - const char *zTerm, /* Term to select leaves for */ - int nTerm, /* Size of term zTerm in bytes */ - const char *zNode, /* Buffer containing segment interior node */ - int nNode, /* Size of buffer at zNode */ - sqlite3_int64 *piFirst, /* OUT: Selected child node */ - sqlite3_int64 *piLast /* OUT: Selected child node */ -){ - int rc = SQLITE_OK; /* Return code */ - const char *zCsr = zNode; /* Cursor to iterate through node */ - const char *zEnd = &zCsr[nNode];/* End of interior node buffer */ - char *zBuffer = 0; /* Buffer to load terms into */ - int nAlloc = 0; /* Size of allocated buffer */ - int isFirstTerm = 1; /* True when processing first term on page */ - sqlite3_int64 iChild; /* Block id of child node to descend to */ - - /* Skip over the 'height' varint that occurs at the start of every - ** interior node. Then load the blockid of the left-child of the b-tree - ** node into variable iChild. - ** - ** Even if the data structure on disk is corrupted, this (reading two - ** varints from the buffer) does not risk an overread. If zNode is a - ** root node, then the buffer comes from a SELECT statement. SQLite does - ** not make this guarantee explicitly, but in practice there are always - ** either more than 20 bytes of allocated space following the nNode bytes of - ** contents, or two zero bytes. Or, if the node is read from the %_segments - ** table, then there are always 20 bytes of zeroed padding following the - ** nNode bytes of content (see sqlite3Fts3ReadBlock() for details). - */ - zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); - zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); - if( zCsr>zEnd ){ - return FTS_CORRUPT_VTAB; - } - - while( zCsrzEnd ){ - rc = FTS_CORRUPT_VTAB; - goto finish_scan; - } - if( nPrefix+nSuffix>nAlloc ){ - char *zNew; - nAlloc = (nPrefix+nSuffix) * 2; - zNew = (char *)sqlite3_realloc(zBuffer, nAlloc); - if( !zNew ){ - rc = SQLITE_NOMEM; - goto finish_scan; - } - zBuffer = zNew; - } - assert( zBuffer ); - memcpy(&zBuffer[nPrefix], zCsr, nSuffix); - nBuffer = nPrefix + nSuffix; - zCsr += nSuffix; - - /* Compare the term we are searching for with the term just loaded from - ** the interior node. If the specified term is greater than or equal - ** to the term from the interior node, then all terms on the sub-tree - ** headed by node iChild are smaller than zTerm. No need to search - ** iChild. - ** - ** If the interior node term is larger than the specified term, then - ** the tree headed by iChild may contain the specified term. - */ - cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); - if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){ - *piFirst = iChild; - piFirst = 0; - } - - if( piLast && cmp<0 ){ - *piLast = iChild; - piLast = 0; - } - - iChild++; - }; - - if( piFirst ) *piFirst = iChild; - if( piLast ) *piLast = iChild; - - finish_scan: - sqlite3_free(zBuffer); - return rc; -} - - -/* -** The buffer pointed to by argument zNode (size nNode bytes) contains an -** interior node of a b-tree segment. The zTerm buffer (size nTerm bytes) -** contains a term. This function searches the sub-tree headed by the zNode -** node for the range of leaf nodes that may contain the specified term -** or terms for which the specified term is a prefix. -** -** If piLeaf is not NULL, then *piLeaf is set to the blockid of the -** left-most leaf node in the tree that may contain the specified term. -** If piLeaf2 is not NULL, then *piLeaf2 is set to the blockid of the -** right-most leaf node that may contain a term for which the specified -** term is a prefix. -** -** It is possible that the range of returned leaf nodes does not contain -** the specified term or any terms for which it is a prefix. However, if the -** segment does contain any such terms, they are stored within the identified -** range. Because this function only inspects interior segment nodes (and -** never loads leaf nodes into memory), it is not possible to be sure. -** -** If an error occurs, an error code other than SQLITE_OK is returned. -*/ -static int fts3SelectLeaf( - Fts3Table *p, /* Virtual table handle */ - const char *zTerm, /* Term to select leaves for */ - int nTerm, /* Size of term zTerm in bytes */ - const char *zNode, /* Buffer containing segment interior node */ - int nNode, /* Size of buffer at zNode */ - sqlite3_int64 *piLeaf, /* Selected leaf node */ - sqlite3_int64 *piLeaf2 /* Selected leaf node */ -){ - int rc; /* Return code */ - int iHeight; /* Height of this node in tree */ - - assert( piLeaf || piLeaf2 ); - - sqlite3Fts3GetVarint32(zNode, &iHeight); - rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); - assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) ); - - if( rc==SQLITE_OK && iHeight>1 ){ - char *zBlob = 0; /* Blob read from %_segments table */ - int nBlob; /* Size of zBlob in bytes */ - - if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ - rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0); - if( rc==SQLITE_OK ){ - rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0); - } - sqlite3_free(zBlob); - piLeaf = 0; - zBlob = 0; - } - - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3ReadBlock(p, piLeaf?*piLeaf:*piLeaf2, &zBlob, &nBlob, 0); - } - if( rc==SQLITE_OK ){ - rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2); - } - sqlite3_free(zBlob); - } - - return rc; -} - -/* -** This function is used to create delta-encoded serialized lists of FTS3 -** varints. Each call to this function appends a single varint to a list. -*/ -static void fts3PutDeltaVarint( - char **pp, /* IN/OUT: Output pointer */ - sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */ - sqlite3_int64 iVal /* Write this value to the list */ -){ - assert( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) ); - *pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev); - *piPrev = iVal; -} - -/* -** When this function is called, *ppPoslist is assumed to point to the -** start of a position-list. After it returns, *ppPoslist points to the -** first byte after the position-list. -** -** A position list is list of positions (delta encoded) and columns for -** a single document record of a doclist. So, in other words, this -** routine advances *ppPoslist so that it points to the next docid in -** the doclist, or to the first byte past the end of the doclist. -** -** If pp is not NULL, then the contents of the position list are copied -** to *pp. *pp is set to point to the first byte past the last byte copied -** before this function returns. -*/ -static void fts3PoslistCopy(char **pp, char **ppPoslist){ - char *pEnd = *ppPoslist; - char c = 0; - - /* The end of a position list is marked by a zero encoded as an FTS3 - ** varint. A single POS_END (0) byte. Except, if the 0 byte is preceded by - ** a byte with the 0x80 bit set, then it is not a varint 0, but the tail - ** of some other, multi-byte, value. - ** - ** The following while-loop moves pEnd to point to the first byte that is not - ** immediately preceded by a byte with the 0x80 bit set. Then increments - ** pEnd once more so that it points to the byte immediately following the - ** last byte in the position-list. - */ - while( *pEnd | c ){ - c = *pEnd++ & 0x80; - testcase( c!=0 && (*pEnd)==0 ); - } - pEnd++; /* Advance past the POS_END terminator byte */ - - if( pp ){ - int n = (int)(pEnd - *ppPoslist); - char *p = *pp; - memcpy(p, *ppPoslist, n); - p += n; - *pp = p; - } - *ppPoslist = pEnd; -} - -/* -** When this function is called, *ppPoslist is assumed to point to the -** start of a column-list. After it returns, *ppPoslist points to the -** to the terminator (POS_COLUMN or POS_END) byte of the column-list. -** -** A column-list is list of delta-encoded positions for a single column -** within a single document within a doclist. -** -** The column-list is terminated either by a POS_COLUMN varint (1) or -** a POS_END varint (0). This routine leaves *ppPoslist pointing to -** the POS_COLUMN or POS_END that terminates the column-list. -** -** If pp is not NULL, then the contents of the column-list are copied -** to *pp. *pp is set to point to the first byte past the last byte copied -** before this function returns. The POS_COLUMN or POS_END terminator -** is not copied into *pp. -*/ -static void fts3ColumnlistCopy(char **pp, char **ppPoslist){ - char *pEnd = *ppPoslist; - char c = 0; - - /* A column-list is terminated by either a 0x01 or 0x00 byte that is - ** not part of a multi-byte varint. - */ - while( 0xFE & (*pEnd | c) ){ - c = *pEnd++ & 0x80; - testcase( c!=0 && ((*pEnd)&0xfe)==0 ); - } - if( pp ){ - int n = (int)(pEnd - *ppPoslist); - char *p = *pp; - memcpy(p, *ppPoslist, n); - p += n; - *pp = p; - } - *ppPoslist = pEnd; -} - -/* -** Value used to signify the end of an position-list. This is safe because -** it is not possible to have a document with 2^31 terms. -*/ -#define POSITION_LIST_END 0x7fffffff - -/* -** This function is used to help parse position-lists. When this function is -** called, *pp may point to the start of the next varint in the position-list -** being parsed, or it may point to 1 byte past the end of the position-list -** (in which case **pp will be a terminator bytes POS_END (0) or -** (1)). -** -** If *pp points past the end of the current position-list, set *pi to -** POSITION_LIST_END and return. Otherwise, read the next varint from *pp, -** increment the current value of *pi by the value read, and set *pp to -** point to the next value before returning. -** -** Before calling this routine *pi must be initialized to the value of -** the previous position, or zero if we are reading the first position -** in the position-list. Because positions are delta-encoded, the value -** of the previous position is needed in order to compute the value of -** the next position. -*/ -static void fts3ReadNextPos( - char **pp, /* IN/OUT: Pointer into position-list buffer */ - sqlite3_int64 *pi /* IN/OUT: Value read from position-list */ -){ - if( (**pp)&0xFE ){ - fts3GetDeltaVarint(pp, pi); - *pi -= 2; - }else{ - *pi = POSITION_LIST_END; - } -} - -/* -** If parameter iCol is not 0, write an POS_COLUMN (1) byte followed by -** the value of iCol encoded as a varint to *pp. This will start a new -** column list. -** -** Set *pp to point to the byte just after the last byte written before -** returning (do not modify it if iCol==0). Return the total number of bytes -** written (0 if iCol==0). -*/ -static int fts3PutColNumber(char **pp, int iCol){ - int n = 0; /* Number of bytes written */ - if( iCol ){ - char *p = *pp; /* Output pointer */ - n = 1 + sqlite3Fts3PutVarint(&p[1], iCol); - *p = 0x01; - *pp = &p[n]; - } - return n; -} - -/* -** Compute the union of two position lists. The output written -** into *pp contains all positions of both *pp1 and *pp2 in sorted -** order and with any duplicates removed. All pointers are -** updated appropriately. The caller is responsible for insuring -** that there is enough space in *pp to hold the complete output. -*/ -static void fts3PoslistMerge( - char **pp, /* Output buffer */ - char **pp1, /* Left input list */ - char **pp2 /* Right input list */ -){ - char *p = *pp; - char *p1 = *pp1; - char *p2 = *pp2; - - while( *p1 || *p2 ){ - int iCol1; /* The current column index in pp1 */ - int iCol2; /* The current column index in pp2 */ - - if( *p1==POS_COLUMN ) sqlite3Fts3GetVarint32(&p1[1], &iCol1); - else if( *p1==POS_END ) iCol1 = POSITION_LIST_END; - else iCol1 = 0; - - if( *p2==POS_COLUMN ) sqlite3Fts3GetVarint32(&p2[1], &iCol2); - else if( *p2==POS_END ) iCol2 = POSITION_LIST_END; - else iCol2 = 0; - - if( iCol1==iCol2 ){ - sqlite3_int64 i1 = 0; /* Last position from pp1 */ - sqlite3_int64 i2 = 0; /* Last position from pp2 */ - sqlite3_int64 iPrev = 0; - int n = fts3PutColNumber(&p, iCol1); - p1 += n; - p2 += n; - - /* At this point, both p1 and p2 point to the start of column-lists - ** for the same column (the column with index iCol1 and iCol2). - ** A column-list is a list of non-negative delta-encoded varints, each - ** incremented by 2 before being stored. Each list is terminated by a - ** POS_END (0) or POS_COLUMN (1). The following block merges the two lists - ** and writes the results to buffer p. p is left pointing to the byte - ** after the list written. No terminator (POS_END or POS_COLUMN) is - ** written to the output. - */ - fts3GetDeltaVarint(&p1, &i1); - fts3GetDeltaVarint(&p2, &i2); - do { - fts3PutDeltaVarint(&p, &iPrev, (i1pos(*pp1) && pos(*pp2)-pos(*pp1)<=nToken). i.e. -** when the *pp1 token appears before the *pp2 token, but not more than nToken -** slots before it. -** -** e.g. nToken==1 searches for adjacent positions. -*/ -static int fts3PoslistPhraseMerge( - char **pp, /* IN/OUT: Preallocated output buffer */ - int nToken, /* Maximum difference in token positions */ - int isSaveLeft, /* Save the left position */ - int isExact, /* If *pp1 is exactly nTokens before *pp2 */ - char **pp1, /* IN/OUT: Left input list */ - char **pp2 /* IN/OUT: Right input list */ -){ - char *p = *pp; - char *p1 = *pp1; - char *p2 = *pp2; - int iCol1 = 0; - int iCol2 = 0; - - /* Never set both isSaveLeft and isExact for the same invocation. */ - assert( isSaveLeft==0 || isExact==0 ); - - assert( p!=0 && *p1!=0 && *p2!=0 ); - if( *p1==POS_COLUMN ){ - p1++; - p1 += sqlite3Fts3GetVarint32(p1, &iCol1); - } - if( *p2==POS_COLUMN ){ - p2++; - p2 += sqlite3Fts3GetVarint32(p2, &iCol2); - } - - while( 1 ){ - if( iCol1==iCol2 ){ - char *pSave = p; - sqlite3_int64 iPrev = 0; - sqlite3_int64 iPos1 = 0; - sqlite3_int64 iPos2 = 0; - - if( iCol1 ){ - *p++ = POS_COLUMN; - p += sqlite3Fts3PutVarint(p, iCol1); - } - - assert( *p1!=POS_END && *p1!=POS_COLUMN ); - assert( *p2!=POS_END && *p2!=POS_COLUMN ); - fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; - fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; - - while( 1 ){ - if( iPos2==iPos1+nToken - || (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken) - ){ - sqlite3_int64 iSave; - iSave = isSaveLeft ? iPos1 : iPos2; - fts3PutDeltaVarint(&p, &iPrev, iSave+2); iPrev -= 2; - pSave = 0; - assert( p ); - } - if( (!isSaveLeft && iPos2<=(iPos1+nToken)) || iPos2<=iPos1 ){ - if( (*p2&0xFE)==0 ) break; - fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; - }else{ - if( (*p1&0xFE)==0 ) break; - fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; - } - } - - if( pSave ){ - assert( pp && p ); - p = pSave; - } - - fts3ColumnlistCopy(0, &p1); - fts3ColumnlistCopy(0, &p2); - assert( (*p1&0xFE)==0 && (*p2&0xFE)==0 ); - if( 0==*p1 || 0==*p2 ) break; - - p1++; - p1 += sqlite3Fts3GetVarint32(p1, &iCol1); - p2++; - p2 += sqlite3Fts3GetVarint32(p2, &iCol2); - } - - /* Advance pointer p1 or p2 (whichever corresponds to the smaller of - ** iCol1 and iCol2) so that it points to either the 0x00 that marks the - ** end of the position list, or the 0x01 that precedes the next - ** column-number in the position list. - */ - else if( iCol1=pEnd ){ - *pp = 0; - }else{ - sqlite3_int64 iVal; - *pp += sqlite3Fts3GetVarint(*pp, &iVal); - if( bDescIdx ){ - *pVal -= iVal; - }else{ - *pVal += iVal; - } - } -} - -/* -** This function is used to write a single varint to a buffer. The varint -** is written to *pp. Before returning, *pp is set to point 1 byte past the -** end of the value written. -** -** If *pbFirst is zero when this function is called, the value written to -** the buffer is that of parameter iVal. -** -** If *pbFirst is non-zero when this function is called, then the value -** written is either (iVal-*piPrev) (if bDescIdx is zero) or (*piPrev-iVal) -** (if bDescIdx is non-zero). -** -** Before returning, this function always sets *pbFirst to 1 and *piPrev -** to the value of parameter iVal. -*/ -static void fts3PutDeltaVarint3( - char **pp, /* IN/OUT: Output pointer */ - int bDescIdx, /* True for descending docids */ - sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */ - int *pbFirst, /* IN/OUT: True after first int written */ - sqlite3_int64 iVal /* Write this value to the list */ -){ - sqlite3_int64 iWrite; - if( bDescIdx==0 || *pbFirst==0 ){ - iWrite = iVal - *piPrev; - }else{ - iWrite = *piPrev - iVal; - } - assert( *pbFirst || *piPrev==0 ); - assert( *pbFirst==0 || iWrite>0 ); - *pp += sqlite3Fts3PutVarint(*pp, iWrite); - *piPrev = iVal; - *pbFirst = 1; -} - - -/* -** This macro is used by various functions that merge doclists. The two -** arguments are 64-bit docid values. If the value of the stack variable -** bDescDoclist is 0 when this macro is invoked, then it returns (i1-i2). -** Otherwise, (i2-i1). -** -** Using this makes it easier to write code that can merge doclists that are -** sorted in either ascending or descending order. -*/ -#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1-i2)) - -/* -** This function does an "OR" merge of two doclists (output contains all -** positions contained in either argument doclist). If the docids in the -** input doclists are sorted in ascending order, parameter bDescDoclist -** should be false. If they are sorted in ascending order, it should be -** passed a non-zero value. -** -** If no error occurs, *paOut is set to point at an sqlite3_malloc'd buffer -** containing the output doclist and SQLITE_OK is returned. In this case -** *pnOut is set to the number of bytes in the output doclist. -** -** If an error occurs, an SQLite error code is returned. The output values -** are undefined in this case. -*/ -static int fts3DoclistOrMerge( - int bDescDoclist, /* True if arguments are desc */ - char *a1, int n1, /* First doclist */ - char *a2, int n2, /* Second doclist */ - char **paOut, int *pnOut /* OUT: Malloc'd doclist */ -){ - sqlite3_int64 i1 = 0; - sqlite3_int64 i2 = 0; - sqlite3_int64 iPrev = 0; - char *pEnd1 = &a1[n1]; - char *pEnd2 = &a2[n2]; - char *p1 = a1; - char *p2 = a2; - char *p; - char *aOut; - int bFirstOut = 0; - - *paOut = 0; - *pnOut = 0; - - /* Allocate space for the output. Both the input and output doclists - ** are delta encoded. If they are in ascending order (bDescDoclist==0), - ** then the first docid in each list is simply encoded as a varint. For - ** each subsequent docid, the varint stored is the difference between the - ** current and previous docid (a positive number - since the list is in - ** ascending order). - ** - ** The first docid written to the output is therefore encoded using the - ** same number of bytes as it is in whichever of the input lists it is - ** read from. And each subsequent docid read from the same input list - ** consumes either the same or less bytes as it did in the input (since - ** the difference between it and the previous value in the output must - ** be a positive value less than or equal to the delta value read from - ** the input list). The same argument applies to all but the first docid - ** read from the 'other' list. And to the contents of all position lists - ** that will be copied and merged from the input to the output. - ** - ** However, if the first docid copied to the output is a negative number, - ** then the encoding of the first docid from the 'other' input list may - ** be larger in the output than it was in the input (since the delta value - ** may be a larger positive integer than the actual docid). - ** - ** The space required to store the output is therefore the sum of the - ** sizes of the two inputs, plus enough space for exactly one of the input - ** docids to grow. - ** - ** A symetric argument may be made if the doclists are in descending - ** order. - */ - aOut = sqlite3_malloc(n1+n2+FTS3_VARINT_MAX-1); - if( !aOut ) return SQLITE_NOMEM; - - p = aOut; - fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1); - fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2); - while( p1 || p2 ){ - sqlite3_int64 iDiff = DOCID_CMP(i1, i2); - - if( p2 && p1 && iDiff==0 ){ - fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i1); - fts3PoslistMerge(&p, &p1, &p2); - fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1); - fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2); - }else if( !p2 || (p1 && iDiff<0) ){ - fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i1); - fts3PoslistCopy(&p, &p1); - fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1); - }else{ - fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i2); - fts3PoslistCopy(&p, &p2); - fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2); - } - } - - *paOut = aOut; - *pnOut = (int)(p-aOut); - assert( *pnOut<=n1+n2+FTS3_VARINT_MAX-1 ); - return SQLITE_OK; -} - -/* -** This function does a "phrase" merge of two doclists. In a phrase merge, -** the output contains a copy of each position from the right-hand input -** doclist for which there is a position in the left-hand input doclist -** exactly nDist tokens before it. -** -** If the docids in the input doclists are sorted in ascending order, -** parameter bDescDoclist should be false. If they are sorted in ascending -** order, it should be passed a non-zero value. -** -** The right-hand input doclist is overwritten by this function. -*/ -static void fts3DoclistPhraseMerge( - int bDescDoclist, /* True if arguments are desc */ - int nDist, /* Distance from left to right (1=adjacent) */ - char *aLeft, int nLeft, /* Left doclist */ - char *aRight, int *pnRight /* IN/OUT: Right/output doclist */ -){ - sqlite3_int64 i1 = 0; - sqlite3_int64 i2 = 0; - sqlite3_int64 iPrev = 0; - char *pEnd1 = &aLeft[nLeft]; - char *pEnd2 = &aRight[*pnRight]; - char *p1 = aLeft; - char *p2 = aRight; - char *p; - int bFirstOut = 0; - char *aOut = aRight; - - assert( nDist>0 ); - - p = aOut; - fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1); - fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2); - - while( p1 && p2 ){ - sqlite3_int64 iDiff = DOCID_CMP(i1, i2); - if( iDiff==0 ){ - char *pSave = p; - sqlite3_int64 iPrevSave = iPrev; - int bFirstOutSave = bFirstOut; - - fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i1); - if( 0==fts3PoslistPhraseMerge(&p, nDist, 0, 1, &p1, &p2) ){ - p = pSave; - iPrev = iPrevSave; - bFirstOut = bFirstOutSave; - } - fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1); - fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2); - }else if( iDiff<0 ){ - fts3PoslistCopy(0, &p1); - fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1); - }else{ - fts3PoslistCopy(0, &p2); - fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2); - } - } - - *pnRight = (int)(p - aOut); -} - -/* -** Argument pList points to a position list nList bytes in size. This -** function checks to see if the position list contains any entries for -** a token in position 0 (of any column). If so, it writes argument iDelta -** to the output buffer pOut, followed by a position list consisting only -** of the entries from pList at position 0, and terminated by an 0x00 byte. -** The value returned is the number of bytes written to pOut (if any). -*/ -SQLITE_PRIVATE int sqlite3Fts3FirstFilter( - sqlite3_int64 iDelta, /* Varint that may be written to pOut */ - char *pList, /* Position list (no 0x00 term) */ - int nList, /* Size of pList in bytes */ - char *pOut /* Write output here */ -){ - int nOut = 0; - int bWritten = 0; /* True once iDelta has been written */ - char *p = pList; - char *pEnd = &pList[nList]; - - if( *p!=0x01 ){ - if( *p==0x02 ){ - nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta); - pOut[nOut++] = 0x02; - bWritten = 1; - } - fts3ColumnlistCopy(0, &p); - } - - while( paaOutput); i++){ - if( pTS->aaOutput[i] ){ - if( !aOut ){ - aOut = pTS->aaOutput[i]; - nOut = pTS->anOutput[i]; - pTS->aaOutput[i] = 0; - }else{ - int nNew; - char *aNew; - - int rc = fts3DoclistOrMerge(p->bDescIdx, - pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut, &aNew, &nNew - ); - if( rc!=SQLITE_OK ){ - sqlite3_free(aOut); - return rc; - } - - sqlite3_free(pTS->aaOutput[i]); - sqlite3_free(aOut); - pTS->aaOutput[i] = 0; - aOut = aNew; - nOut = nNew; - } - } - } - - pTS->aaOutput[0] = aOut; - pTS->anOutput[0] = nOut; - return SQLITE_OK; -} - -/* -** Merge the doclist aDoclist/nDoclist into the TermSelect object passed -** as the first argument. The merge is an "OR" merge (see function -** fts3DoclistOrMerge() for details). -** -** This function is called with the doclist for each term that matches -** a queried prefix. It merges all these doclists into one, the doclist -** for the specified prefix. Since there can be a very large number of -** doclists to merge, the merging is done pair-wise using the TermSelect -** object. -** -** This function returns SQLITE_OK if the merge is successful, or an -** SQLite error code (SQLITE_NOMEM) if an error occurs. -*/ -static int fts3TermSelectMerge( - Fts3Table *p, /* FTS table handle */ - TermSelect *pTS, /* TermSelect object to merge into */ - char *aDoclist, /* Pointer to doclist */ - int nDoclist /* Size of aDoclist in bytes */ -){ - if( pTS->aaOutput[0]==0 ){ - /* If this is the first term selected, copy the doclist to the output - ** buffer using memcpy(). */ - pTS->aaOutput[0] = sqlite3_malloc(nDoclist); - pTS->anOutput[0] = nDoclist; - if( pTS->aaOutput[0] ){ - memcpy(pTS->aaOutput[0], aDoclist, nDoclist); - }else{ - return SQLITE_NOMEM; - } - }else{ - char *aMerge = aDoclist; - int nMerge = nDoclist; - int iOut; - - for(iOut=0; iOutaaOutput); iOut++){ - if( pTS->aaOutput[iOut]==0 ){ - assert( iOut>0 ); - pTS->aaOutput[iOut] = aMerge; - pTS->anOutput[iOut] = nMerge; - break; - }else{ - char *aNew; - int nNew; - - int rc = fts3DoclistOrMerge(p->bDescIdx, aMerge, nMerge, - pTS->aaOutput[iOut], pTS->anOutput[iOut], &aNew, &nNew - ); - if( rc!=SQLITE_OK ){ - if( aMerge!=aDoclist ) sqlite3_free(aMerge); - return rc; - } - - if( aMerge!=aDoclist ) sqlite3_free(aMerge); - sqlite3_free(pTS->aaOutput[iOut]); - pTS->aaOutput[iOut] = 0; - - aMerge = aNew; - nMerge = nNew; - if( (iOut+1)==SizeofArray(pTS->aaOutput) ){ - pTS->aaOutput[iOut] = aMerge; - pTS->anOutput[iOut] = nMerge; - } - } - } - } - return SQLITE_OK; -} - -/* -** Append SegReader object pNew to the end of the pCsr->apSegment[] array. -*/ -static int fts3SegReaderCursorAppend( - Fts3MultiSegReader *pCsr, - Fts3SegReader *pNew -){ - if( (pCsr->nSegment%16)==0 ){ - Fts3SegReader **apNew; - int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*); - apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte); - if( !apNew ){ - sqlite3Fts3SegReaderFree(pNew); - return SQLITE_NOMEM; - } - pCsr->apSegment = apNew; - } - pCsr->apSegment[pCsr->nSegment++] = pNew; - return SQLITE_OK; -} - -/* -** Add seg-reader objects to the Fts3MultiSegReader object passed as the -** 8th argument. -** -** This function returns SQLITE_OK if successful, or an SQLite error code -** otherwise. -*/ -static int fts3SegReaderCursor( - Fts3Table *p, /* FTS3 table handle */ - int iLangid, /* Language id */ - int iIndex, /* Index to search (from 0 to p->nIndex-1) */ - int iLevel, /* Level of segments to scan */ - const char *zTerm, /* Term to query for */ - int nTerm, /* Size of zTerm in bytes */ - int isPrefix, /* True for a prefix search */ - int isScan, /* True to scan from zTerm to EOF */ - Fts3MultiSegReader *pCsr /* Cursor object to populate */ -){ - int rc = SQLITE_OK; /* Error code */ - sqlite3_stmt *pStmt = 0; /* Statement to iterate through segments */ - int rc2; /* Result of sqlite3_reset() */ - - /* If iLevel is less than 0 and this is not a scan, include a seg-reader - ** for the pending-terms. If this is a scan, then this call must be being - ** made by an fts4aux module, not an FTS table. In this case calling - ** Fts3SegReaderPending might segfault, as the data structures used by - ** fts4aux are not completely populated. So it's easiest to filter these - ** calls out here. */ - if( iLevel<0 && p->aIndex ){ - Fts3SegReader *pSeg = 0; - rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg); - if( rc==SQLITE_OK && pSeg ){ - rc = fts3SegReaderCursorAppend(pCsr, pSeg); - } - } - - if( iLevel!=FTS3_SEGCURSOR_PENDING ){ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3AllSegdirs(p, iLangid, iIndex, iLevel, &pStmt); - } - - while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ - Fts3SegReader *pSeg = 0; - - /* Read the values returned by the SELECT into local variables. */ - sqlite3_int64 iStartBlock = sqlite3_column_int64(pStmt, 1); - sqlite3_int64 iLeavesEndBlock = sqlite3_column_int64(pStmt, 2); - sqlite3_int64 iEndBlock = sqlite3_column_int64(pStmt, 3); - int nRoot = sqlite3_column_bytes(pStmt, 4); - char const *zRoot = sqlite3_column_blob(pStmt, 4); - - /* If zTerm is not NULL, and this segment is not stored entirely on its - ** root node, the range of leaves scanned can be reduced. Do this. */ - if( iStartBlock && zTerm ){ - sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0); - rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi); - if( rc!=SQLITE_OK ) goto finished; - if( isPrefix==0 && isScan==0 ) iLeavesEndBlock = iStartBlock; - } - - rc = sqlite3Fts3SegReaderNew(pCsr->nSegment+1, - (isPrefix==0 && isScan==0), - iStartBlock, iLeavesEndBlock, - iEndBlock, zRoot, nRoot, &pSeg - ); - if( rc!=SQLITE_OK ) goto finished; - rc = fts3SegReaderCursorAppend(pCsr, pSeg); - } - } - - finished: - rc2 = sqlite3_reset(pStmt); - if( rc==SQLITE_DONE ) rc = rc2; - - return rc; -} - -/* -** Set up a cursor object for iterating through a full-text index or a -** single level therein. -*/ -SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor( - Fts3Table *p, /* FTS3 table handle */ - int iLangid, /* Language-id to search */ - int iIndex, /* Index to search (from 0 to p->nIndex-1) */ - int iLevel, /* Level of segments to scan */ - const char *zTerm, /* Term to query for */ - int nTerm, /* Size of zTerm in bytes */ - int isPrefix, /* True for a prefix search */ - int isScan, /* True to scan from zTerm to EOF */ - Fts3MultiSegReader *pCsr /* Cursor object to populate */ -){ - assert( iIndex>=0 && iIndexnIndex ); - assert( iLevel==FTS3_SEGCURSOR_ALL - || iLevel==FTS3_SEGCURSOR_PENDING - || iLevel>=0 - ); - assert( iLevelbase.pVtab; - - if( isPrefix ){ - for(i=1; bFound==0 && inIndex; i++){ - if( p->aIndex[i].nPrefix==nTerm ){ - bFound = 1; - rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid, - i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr - ); - pSegcsr->bLookup = 1; - } - } - - for(i=1; bFound==0 && inIndex; i++){ - if( p->aIndex[i].nPrefix==nTerm+1 ){ - bFound = 1; - rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid, - i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr - ); - if( rc==SQLITE_OK ){ - rc = fts3SegReaderCursorAddZero( - p, pCsr->iLangid, zTerm, nTerm, pSegcsr - ); - } - } - } - } - - if( bFound==0 ){ - rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid, - 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr - ); - pSegcsr->bLookup = !isPrefix; - } - } - - *ppSegcsr = pSegcsr; - return rc; -} - -/* -** Free an Fts3MultiSegReader allocated by fts3TermSegReaderCursor(). -*/ -static void fts3SegReaderCursorFree(Fts3MultiSegReader *pSegcsr){ - sqlite3Fts3SegReaderFinish(pSegcsr); - sqlite3_free(pSegcsr); -} - -/* -** This function retrieves the doclist for the specified term (or term -** prefix) from the database. -*/ -static int fts3TermSelect( - Fts3Table *p, /* Virtual table handle */ - Fts3PhraseToken *pTok, /* Token to query for */ - int iColumn, /* Column to query (or -ve for all columns) */ - int *pnOut, /* OUT: Size of buffer at *ppOut */ - char **ppOut /* OUT: Malloced result buffer */ -){ - int rc; /* Return code */ - Fts3MultiSegReader *pSegcsr; /* Seg-reader cursor for this term */ - TermSelect tsc; /* Object for pair-wise doclist merging */ - Fts3SegFilter filter; /* Segment term filter configuration */ - - pSegcsr = pTok->pSegcsr; - memset(&tsc, 0, sizeof(TermSelect)); - - filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | FTS3_SEGMENT_REQUIRE_POS - | (pTok->isPrefix ? FTS3_SEGMENT_PREFIX : 0) - | (pTok->bFirst ? FTS3_SEGMENT_FIRST : 0) - | (iColumnnColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0); - filter.iCol = iColumn; - filter.zTerm = pTok->z; - filter.nTerm = pTok->n; - - rc = sqlite3Fts3SegReaderStart(p, pSegcsr, &filter); - while( SQLITE_OK==rc - && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pSegcsr)) - ){ - rc = fts3TermSelectMerge(p, &tsc, pSegcsr->aDoclist, pSegcsr->nDoclist); - } - - if( rc==SQLITE_OK ){ - rc = fts3TermSelectFinishMerge(p, &tsc); - } - if( rc==SQLITE_OK ){ - *ppOut = tsc.aaOutput[0]; - *pnOut = tsc.anOutput[0]; - }else{ - int i; - for(i=0; ipSegcsr = 0; - return rc; -} - -/* -** This function counts the total number of docids in the doclist stored -** in buffer aList[], size nList bytes. -** -** If the isPoslist argument is true, then it is assumed that the doclist -** contains a position-list following each docid. Otherwise, it is assumed -** that the doclist is simply a list of docids stored as delta encoded -** varints. -*/ -static int fts3DoclistCountDocids(char *aList, int nList){ - int nDoc = 0; /* Return value */ - if( aList ){ - char *aEnd = &aList[nList]; /* Pointer to one byte after EOF */ - char *p = aList; /* Cursor */ - while( peSearch==FTS3_DOCID_SEARCH || pCsr->eSearch==FTS3_FULLSCAN_SEARCH ){ - if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){ - pCsr->isEof = 1; - rc = sqlite3_reset(pCsr->pStmt); - }else{ - pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0); - rc = SQLITE_OK; - } - }else{ - rc = fts3EvalNext((Fts3Cursor *)pCursor); - } - assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); - return rc; -} - -/* -** This is the xFilter interface for the virtual table. See -** the virtual table xFilter method documentation for additional -** information. -** -** If idxNum==FTS3_FULLSCAN_SEARCH then do a full table scan against -** the %_content table. -** -** If idxNum==FTS3_DOCID_SEARCH then do a docid lookup for a single entry -** in the %_content table. -** -** If idxNum>=FTS3_FULLTEXT_SEARCH then use the full text index. The -** column on the left-hand side of the MATCH operator is column -** number idxNum-FTS3_FULLTEXT_SEARCH, 0 indexed. argv[0] is the right-hand -** side of the MATCH operator. -*/ -static int fts3FilterMethod( - sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ - int idxNum, /* Strategy index */ - const char *idxStr, /* Unused */ - int nVal, /* Number of elements in apVal */ - sqlite3_value **apVal /* Arguments for the indexing scheme */ -){ - int rc; - char *zSql; /* SQL statement used to access %_content */ - Fts3Table *p = (Fts3Table *)pCursor->pVtab; - Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; - - UNUSED_PARAMETER(idxStr); - UNUSED_PARAMETER(nVal); - - assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) ); - assert( nVal==0 || nVal==1 || nVal==2 ); - assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) ); - assert( p->pSegments==0 ); - - /* In case the cursor has been used before, clear it now. */ - sqlite3_finalize(pCsr->pStmt); - sqlite3_free(pCsr->aDoclist); - sqlite3Fts3ExprFree(pCsr->pExpr); - memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); - - if( idxStr ){ - pCsr->bDesc = (idxStr[0]=='D'); - }else{ - pCsr->bDesc = p->bDescIdx; - } - pCsr->eSearch = (i16)idxNum; - - if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){ - int iCol = idxNum-FTS3_FULLTEXT_SEARCH; - const char *zQuery = (const char *)sqlite3_value_text(apVal[0]); - - if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ - return SQLITE_NOMEM; - } - - pCsr->iLangid = 0; - if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]); - - rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid, - p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr - ); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_ERROR ){ - static const char *zErr = "malformed MATCH expression: [%s]"; - p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery); - } - return rc; - } - - rc = sqlite3Fts3ReadLock(p); - if( rc!=SQLITE_OK ) return rc; - - rc = fts3EvalStart(pCsr); - - sqlite3Fts3SegmentsClose(p); - if( rc!=SQLITE_OK ) return rc; - pCsr->pNextId = pCsr->aDoclist; - pCsr->iPrevId = 0; - } - - /* Compile a SELECT statement for this cursor. For a full-table-scan, the - ** statement loops through all rows of the %_content table. For a - ** full-text query or docid lookup, the statement retrieves a single - ** row by docid. - */ - if( idxNum==FTS3_FULLSCAN_SEARCH ){ - zSql = sqlite3_mprintf( - "SELECT %s ORDER BY rowid %s", - p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") - ); - if( zSql ){ - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); - sqlite3_free(zSql); - }else{ - rc = SQLITE_NOMEM; - } - }else if( idxNum==FTS3_DOCID_SEARCH ){ - rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt); - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); - } - } - if( rc!=SQLITE_OK ) return rc; - - return fts3NextMethod(pCursor); -} - -/* -** This is the xEof method of the virtual table. SQLite calls this -** routine to find out if it has reached the end of a result set. -*/ -static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){ - return ((Fts3Cursor *)pCursor)->isEof; -} - -/* -** This is the xRowid method. The SQLite core calls this routine to -** retrieve the rowid for the current row of the result set. fts3 -** exposes %_content.docid as the rowid for the virtual table. The -** rowid should be written to *pRowid. -*/ -static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ - Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; - *pRowid = pCsr->iPrevId; - return SQLITE_OK; -} - -/* -** This is the xColumn method, called by SQLite to request a value from -** the row that the supplied cursor currently points to. -** -** If: -** -** (iCol < p->nColumn) -> The value of the iCol'th user column. -** (iCol == p->nColumn) -> Magic column with the same name as the table. -** (iCol == p->nColumn+1) -> Docid column -** (iCol == p->nColumn+2) -> Langid column -*/ -static int fts3ColumnMethod( - sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ - sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ - int iCol /* Index of column to read value from */ -){ - int rc = SQLITE_OK; /* Return Code */ - Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; - Fts3Table *p = (Fts3Table *)pCursor->pVtab; - - /* The column value supplied by SQLite must be in range. */ - assert( iCol>=0 && iCol<=p->nColumn+2 ); - - if( iCol==p->nColumn+1 ){ - /* This call is a request for the "docid" column. Since "docid" is an - ** alias for "rowid", use the xRowid() method to obtain the value. - */ - sqlite3_result_int64(pCtx, pCsr->iPrevId); - }else if( iCol==p->nColumn ){ - /* The extra column whose name is the same as the table. - ** Return a blob which is a pointer to the cursor. */ - sqlite3_result_blob(pCtx, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT); - }else if( iCol==p->nColumn+2 && pCsr->pExpr ){ - sqlite3_result_int64(pCtx, pCsr->iLangid); - }else{ - /* The requested column is either a user column (one that contains - ** indexed data), or the language-id column. */ - rc = fts3CursorSeek(0, pCsr); - - if( rc==SQLITE_OK ){ - if( iCol==p->nColumn+2 ){ - int iLangid = 0; - if( p->zLanguageid ){ - iLangid = sqlite3_column_int(pCsr->pStmt, p->nColumn+1); - } - sqlite3_result_int(pCtx, iLangid); - }else if( sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){ - sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); - } - } - } - - assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); - return rc; -} - -/* -** This function is the implementation of the xUpdate callback used by -** FTS3 virtual tables. It is invoked by SQLite each time a row is to be -** inserted, updated or deleted. -*/ -static int fts3UpdateMethod( - sqlite3_vtab *pVtab, /* Virtual table handle */ - int nArg, /* Size of argument array */ - sqlite3_value **apVal, /* Array of arguments */ - sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ -){ - return sqlite3Fts3UpdateMethod(pVtab, nArg, apVal, pRowid); -} - -/* -** Implementation of xSync() method. Flush the contents of the pending-terms -** hash-table to the database. -*/ -static int fts3SyncMethod(sqlite3_vtab *pVtab){ - - /* Following an incremental-merge operation, assuming that the input - ** segments are not completely consumed (the usual case), they are updated - ** in place to remove the entries that have already been merged. This - ** involves updating the leaf block that contains the smallest unmerged - ** entry and each block (if any) between the leaf and the root node. So - ** if the height of the input segment b-trees is N, and input segments - ** are merged eight at a time, updating the input segments at the end - ** of an incremental-merge requires writing (8*(1+N)) blocks. N is usually - ** small - often between 0 and 2. So the overhead of the incremental - ** merge is somewhere between 8 and 24 blocks. To avoid this overhead - ** dwarfing the actual productive work accomplished, the incremental merge - ** is only attempted if it will write at least 64 leaf blocks. Hence - ** nMinMerge. - ** - ** Of course, updating the input segments also involves deleting a bunch - ** of blocks from the segments table. But this is not considered overhead - ** as it would also be required by a crisis-merge that used the same input - ** segments. - */ - const u32 nMinMerge = 64; /* Minimum amount of incr-merge work to do */ - - Fts3Table *p = (Fts3Table*)pVtab; - int rc = sqlite3Fts3PendingTermsFlush(p); - - if( rc==SQLITE_OK && p->bAutoincrmerge==1 && p->nLeafAdd>(nMinMerge/16) ){ - int mxLevel = 0; /* Maximum relative level value in db */ - int A; /* Incr-merge parameter A */ - - rc = sqlite3Fts3MaxLevel(p, &mxLevel); - assert( rc==SQLITE_OK || mxLevel==0 ); - A = p->nLeafAdd * mxLevel; - A += (A/2); - if( A>(int)nMinMerge ) rc = sqlite3Fts3Incrmerge(p, A, 8); - } - sqlite3Fts3SegmentsClose(p); - return rc; -} - -/* -** Implementation of xBegin() method. This is a no-op. -*/ -static int fts3BeginMethod(sqlite3_vtab *pVtab){ - Fts3Table *p = (Fts3Table*)pVtab; - UNUSED_PARAMETER(pVtab); - assert( p->pSegments==0 ); - assert( p->nPendingData==0 ); - assert( p->inTransaction!=1 ); - TESTONLY( p->inTransaction = 1 ); - TESTONLY( p->mxSavepoint = -1; ); - p->nLeafAdd = 0; - return SQLITE_OK; -} - -/* -** Implementation of xCommit() method. This is a no-op. The contents of -** the pending-terms hash-table have already been flushed into the database -** by fts3SyncMethod(). -*/ -static int fts3CommitMethod(sqlite3_vtab *pVtab){ - TESTONLY( Fts3Table *p = (Fts3Table*)pVtab ); - UNUSED_PARAMETER(pVtab); - assert( p->nPendingData==0 ); - assert( p->inTransaction!=0 ); - assert( p->pSegments==0 ); - TESTONLY( p->inTransaction = 0 ); - TESTONLY( p->mxSavepoint = -1; ); - return SQLITE_OK; -} - -/* -** Implementation of xRollback(). Discard the contents of the pending-terms -** hash-table. Any changes made to the database are reverted by SQLite. -*/ -static int fts3RollbackMethod(sqlite3_vtab *pVtab){ - Fts3Table *p = (Fts3Table*)pVtab; - sqlite3Fts3PendingTermsClear(p); - assert( p->inTransaction!=0 ); - TESTONLY( p->inTransaction = 0 ); - TESTONLY( p->mxSavepoint = -1; ); - return SQLITE_OK; -} - -/* -** When called, *ppPoslist must point to the byte immediately following the -** end of a position-list. i.e. ( (*ppPoslist)[-1]==POS_END ). This function -** moves *ppPoslist so that it instead points to the first byte of the -** same position list. -*/ -static void fts3ReversePoslist(char *pStart, char **ppPoslist){ - char *p = &(*ppPoslist)[-2]; - char c = 0; - - while( p>pStart && (c=*p--)==0 ); - while( p>pStart && (*p & 0x80) | c ){ - c = *p--; - } - if( p>pStart ){ p = &p[2]; } - while( *p++&0x80 ); - *ppPoslist = p; -} - -/* -** Helper function used by the implementation of the overloaded snippet(), -** offsets() and optimize() SQL functions. -** -** If the value passed as the third argument is a blob of size -** sizeof(Fts3Cursor*), then the blob contents are copied to the -** output variable *ppCsr and SQLITE_OK is returned. Otherwise, an error -** message is written to context pContext and SQLITE_ERROR returned. The -** string passed via zFunc is used as part of the error message. -*/ -static int fts3FunctionArg( - sqlite3_context *pContext, /* SQL function call context */ - const char *zFunc, /* Function name */ - sqlite3_value *pVal, /* argv[0] passed to function */ - Fts3Cursor **ppCsr /* OUT: Store cursor handle here */ -){ - Fts3Cursor *pRet; - if( sqlite3_value_type(pVal)!=SQLITE_BLOB - || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *) - ){ - char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc); - sqlite3_result_error(pContext, zErr, -1); - sqlite3_free(zErr); - return SQLITE_ERROR; - } - memcpy(&pRet, sqlite3_value_blob(pVal), sizeof(Fts3Cursor *)); - *ppCsr = pRet; - return SQLITE_OK; -} - -/* -** Implementation of the snippet() function for FTS3 -*/ -static void fts3SnippetFunc( - sqlite3_context *pContext, /* SQLite function call context */ - int nVal, /* Size of apVal[] array */ - sqlite3_value **apVal /* Array of arguments */ -){ - Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ - const char *zStart = ""; - const char *zEnd = ""; - const char *zEllipsis = "..."; - int iCol = -1; - int nToken = 15; /* Default number of tokens in snippet */ - - /* There must be at least one argument passed to this function (otherwise - ** the non-overloaded version would have been called instead of this one). - */ - assert( nVal>=1 ); - - if( nVal>6 ){ - sqlite3_result_error(pContext, - "wrong number of arguments to function snippet()", -1); - return; - } - if( fts3FunctionArg(pContext, "snippet", apVal[0], &pCsr) ) return; - - switch( nVal ){ - case 6: nToken = sqlite3_value_int(apVal[5]); - case 5: iCol = sqlite3_value_int(apVal[4]); - case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]); - case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]); - case 2: zStart = (const char*)sqlite3_value_text(apVal[1]); - } - if( !zEllipsis || !zEnd || !zStart ){ - sqlite3_result_error_nomem(pContext); - }else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){ - sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis, iCol, nToken); - } -} - -/* -** Implementation of the offsets() function for FTS3 -*/ -static void fts3OffsetsFunc( - sqlite3_context *pContext, /* SQLite function call context */ - int nVal, /* Size of argument array */ - sqlite3_value **apVal /* Array of arguments */ -){ - Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ - - UNUSED_PARAMETER(nVal); - - assert( nVal==1 ); - if( fts3FunctionArg(pContext, "offsets", apVal[0], &pCsr) ) return; - assert( pCsr ); - if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){ - sqlite3Fts3Offsets(pContext, pCsr); - } -} - -/* -** Implementation of the special optimize() function for FTS3. This -** function merges all segments in the database to a single segment. -** Example usage is: -** -** SELECT optimize(t) FROM t LIMIT 1; -** -** where 't' is the name of an FTS3 table. -*/ -static void fts3OptimizeFunc( - sqlite3_context *pContext, /* SQLite function call context */ - int nVal, /* Size of argument array */ - sqlite3_value **apVal /* Array of arguments */ -){ - int rc; /* Return code */ - Fts3Table *p; /* Virtual table handle */ - Fts3Cursor *pCursor; /* Cursor handle passed through apVal[0] */ - - UNUSED_PARAMETER(nVal); - - assert( nVal==1 ); - if( fts3FunctionArg(pContext, "optimize", apVal[0], &pCursor) ) return; - p = (Fts3Table *)pCursor->base.pVtab; - assert( p ); - - rc = sqlite3Fts3Optimize(p); - - switch( rc ){ - case SQLITE_OK: - sqlite3_result_text(pContext, "Index optimized", -1, SQLITE_STATIC); - break; - case SQLITE_DONE: - sqlite3_result_text(pContext, "Index already optimal", -1, SQLITE_STATIC); - break; - default: - sqlite3_result_error_code(pContext, rc); - break; - } -} - -/* -** Implementation of the matchinfo() function for FTS3 -*/ -static void fts3MatchinfoFunc( - sqlite3_context *pContext, /* SQLite function call context */ - int nVal, /* Size of argument array */ - sqlite3_value **apVal /* Array of arguments */ -){ - Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ - assert( nVal==1 || nVal==2 ); - if( SQLITE_OK==fts3FunctionArg(pContext, "matchinfo", apVal[0], &pCsr) ){ - const char *zArg = 0; - if( nVal>1 ){ - zArg = (const char *)sqlite3_value_text(apVal[1]); - } - sqlite3Fts3Matchinfo(pContext, pCsr, zArg); - } -} - -/* -** This routine implements the xFindFunction method for the FTS3 -** virtual table. -*/ -static int fts3FindFunctionMethod( - sqlite3_vtab *pVtab, /* Virtual table handle */ - int nArg, /* Number of SQL function arguments */ - const char *zName, /* Name of SQL function */ - void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ - void **ppArg /* Unused */ -){ - struct Overloaded { - const char *zName; - void (*xFunc)(sqlite3_context*,int,sqlite3_value**); - } aOverload[] = { - { "snippet", fts3SnippetFunc }, - { "offsets", fts3OffsetsFunc }, - { "optimize", fts3OptimizeFunc }, - { "matchinfo", fts3MatchinfoFunc }, - }; - int i; /* Iterator variable */ - - UNUSED_PARAMETER(pVtab); - UNUSED_PARAMETER(nArg); - UNUSED_PARAMETER(ppArg); - - for(i=0; idb; /* Database connection */ - int rc; /* Return Code */ - - /* As it happens, the pending terms table is always empty here. This is - ** because an "ALTER TABLE RENAME TABLE" statement inside a transaction - ** always opens a savepoint transaction. And the xSavepoint() method - ** flushes the pending terms table. But leave the (no-op) call to - ** PendingTermsFlush() in in case that changes. - */ - assert( p->nPendingData==0 ); - rc = sqlite3Fts3PendingTermsFlush(p); - - if( p->zContentTbl==0 ){ - fts3DbExec(&rc, db, - "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", - p->zDb, p->zName, zName - ); - } - - if( p->bHasDocsize ){ - fts3DbExec(&rc, db, - "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';", - p->zDb, p->zName, zName - ); - } - if( p->bHasStat ){ - fts3DbExec(&rc, db, - "ALTER TABLE %Q.'%q_stat' RENAME TO '%q_stat';", - p->zDb, p->zName, zName - ); - } - fts3DbExec(&rc, db, - "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';", - p->zDb, p->zName, zName - ); - fts3DbExec(&rc, db, - "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';", - p->zDb, p->zName, zName - ); - return rc; -} - -/* -** The xSavepoint() method. -** -** Flush the contents of the pending-terms table to disk. -*/ -static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ - int rc = SQLITE_OK; - UNUSED_PARAMETER(iSavepoint); - assert( ((Fts3Table *)pVtab)->inTransaction ); - assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint ); - TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint ); - if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){ - rc = fts3SyncMethod(pVtab); - } - return rc; -} - -/* -** The xRelease() method. -** -** This is a no-op. -*/ -static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ - TESTONLY( Fts3Table *p = (Fts3Table*)pVtab ); - UNUSED_PARAMETER(iSavepoint); - UNUSED_PARAMETER(pVtab); - assert( p->inTransaction ); - assert( p->mxSavepoint >= iSavepoint ); - TESTONLY( p->mxSavepoint = iSavepoint-1 ); - return SQLITE_OK; -} - -/* -** The xRollbackTo() method. -** -** Discard the contents of the pending terms table. -*/ -static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts3Table *p = (Fts3Table*)pVtab; - UNUSED_PARAMETER(iSavepoint); - assert( p->inTransaction ); - assert( p->mxSavepoint >= iSavepoint ); - TESTONLY( p->mxSavepoint = iSavepoint ); - sqlite3Fts3PendingTermsClear(p); - return SQLITE_OK; -} - -static const sqlite3_module fts3Module = { - /* iVersion */ 2, - /* xCreate */ fts3CreateMethod, - /* xConnect */ fts3ConnectMethod, - /* xBestIndex */ fts3BestIndexMethod, - /* xDisconnect */ fts3DisconnectMethod, - /* xDestroy */ fts3DestroyMethod, - /* xOpen */ fts3OpenMethod, - /* xClose */ fts3CloseMethod, - /* xFilter */ fts3FilterMethod, - /* xNext */ fts3NextMethod, - /* xEof */ fts3EofMethod, - /* xColumn */ fts3ColumnMethod, - /* xRowid */ fts3RowidMethod, - /* xUpdate */ fts3UpdateMethod, - /* xBegin */ fts3BeginMethod, - /* xSync */ fts3SyncMethod, - /* xCommit */ fts3CommitMethod, - /* xRollback */ fts3RollbackMethod, - /* xFindFunction */ fts3FindFunctionMethod, - /* xRename */ fts3RenameMethod, - /* xSavepoint */ fts3SavepointMethod, - /* xRelease */ fts3ReleaseMethod, - /* xRollbackTo */ fts3RollbackToMethod, -}; - -/* -** This function is registered as the module destructor (called when an -** FTS3 enabled database connection is closed). It frees the memory -** allocated for the tokenizer hash table. -*/ -static void hashDestroy(void *p){ - Fts3Hash *pHash = (Fts3Hash *)p; - sqlite3Fts3HashClear(pHash); - sqlite3_free(pHash); -} - -/* -** The fts3 built-in tokenizers - "simple", "porter" and "icu"- are -** implemented in files fts3_tokenizer1.c, fts3_porter.c and fts3_icu.c -** respectively. The following three forward declarations are for functions -** declared in these files used to retrieve the respective implementations. -** -** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed -** to by the argument to point to the "simple" tokenizer implementation. -** And so on. -*/ -SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); -SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule); -#ifdef SQLITE_ENABLE_FTS4_UNICODE61 -SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const**ppModule); -#endif -#ifdef SQLITE_ENABLE_ICU -SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); -#endif - -/* -** Initialize the fts3 extension. If this extension is built as part -** of the sqlite library, then this function is called directly by -** SQLite. If fts3 is built as a dynamically loadable extension, this -** function is called by the sqlite3_extension_init() entry point. -*/ -SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ - int rc = SQLITE_OK; - Fts3Hash *pHash = 0; - const sqlite3_tokenizer_module *pSimple = 0; - const sqlite3_tokenizer_module *pPorter = 0; -#ifdef SQLITE_ENABLE_FTS4_UNICODE61 - const sqlite3_tokenizer_module *pUnicode = 0; -#endif - -#ifdef SQLITE_ENABLE_ICU - const sqlite3_tokenizer_module *pIcu = 0; - sqlite3Fts3IcuTokenizerModule(&pIcu); -#endif - -#ifdef SQLITE_ENABLE_FTS4_UNICODE61 - sqlite3Fts3UnicodeTokenizer(&pUnicode); -#endif - -#ifdef SQLITE_TEST - rc = sqlite3Fts3InitTerm(db); - if( rc!=SQLITE_OK ) return rc; -#endif - - rc = sqlite3Fts3InitAux(db); - if( rc!=SQLITE_OK ) return rc; - - sqlite3Fts3SimpleTokenizerModule(&pSimple); - sqlite3Fts3PorterTokenizerModule(&pPorter); - - /* Allocate and initialize the hash-table used to store tokenizers. */ - pHash = sqlite3_malloc(sizeof(Fts3Hash)); - if( !pHash ){ - rc = SQLITE_NOMEM; - }else{ - sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); - } - - /* Load the built-in tokenizers into the hash table */ - if( rc==SQLITE_OK ){ - if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple) - || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter) - -#ifdef SQLITE_ENABLE_FTS4_UNICODE61 - || sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode) -#endif -#ifdef SQLITE_ENABLE_ICU - || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu)) -#endif - ){ - rc = SQLITE_NOMEM; - } - } - -#ifdef SQLITE_TEST - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3ExprInitTestInterface(db); - } -#endif - - /* Create the virtual table wrapper around the hash-table and overload - ** the two scalar functions. If this is successful, register the - ** module with sqlite. - */ - if( SQLITE_OK==rc - && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 2)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", 1)) - ){ - rc = sqlite3_create_module_v2( - db, "fts3", &fts3Module, (void *)pHash, hashDestroy - ); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_module_v2( - db, "fts4", &fts3Module, (void *)pHash, 0 - ); - } - return rc; - } - - /* An error has occurred. Delete the hash table and return the error code. */ - assert( rc!=SQLITE_OK ); - if( pHash ){ - sqlite3Fts3HashClear(pHash); - sqlite3_free(pHash); - } - return rc; -} - -/* -** Allocate an Fts3MultiSegReader for each token in the expression headed -** by pExpr. -** -** An Fts3SegReader object is a cursor that can seek or scan a range of -** entries within a single segment b-tree. An Fts3MultiSegReader uses multiple -** Fts3SegReader objects internally to provide an interface to seek or scan -** within the union of all segments of a b-tree. Hence the name. -** -** If the allocated Fts3MultiSegReader just seeks to a single entry in a -** segment b-tree (if the term is not a prefix or it is a prefix for which -** there exists prefix b-tree of the right length) then it may be traversed -** and merged incrementally. Otherwise, it has to be merged into an in-memory -** doclist and then traversed. -*/ -static void fts3EvalAllocateReaders( - Fts3Cursor *pCsr, /* FTS cursor handle */ - Fts3Expr *pExpr, /* Allocate readers for this expression */ - int *pnToken, /* OUT: Total number of tokens in phrase. */ - int *pnOr, /* OUT: Total number of OR nodes in expr. */ - int *pRc /* IN/OUT: Error code */ -){ - if( pExpr && SQLITE_OK==*pRc ){ - if( pExpr->eType==FTSQUERY_PHRASE ){ - int i; - int nToken = pExpr->pPhrase->nToken; - *pnToken += nToken; - for(i=0; ipPhrase->aToken[i]; - int rc = fts3TermSegReaderCursor(pCsr, - pToken->z, pToken->n, pToken->isPrefix, &pToken->pSegcsr - ); - if( rc!=SQLITE_OK ){ - *pRc = rc; - return; - } - } - assert( pExpr->pPhrase->iDoclistToken==0 ); - pExpr->pPhrase->iDoclistToken = -1; - }else{ - *pnOr += (pExpr->eType==FTSQUERY_OR); - fts3EvalAllocateReaders(pCsr, pExpr->pLeft, pnToken, pnOr, pRc); - fts3EvalAllocateReaders(pCsr, pExpr->pRight, pnToken, pnOr, pRc); - } - } -} - -/* -** Arguments pList/nList contain the doclist for token iToken of phrase p. -** It is merged into the main doclist stored in p->doclist.aAll/nAll. -** -** This function assumes that pList points to a buffer allocated using -** sqlite3_malloc(). This function takes responsibility for eventually -** freeing the buffer. -*/ -static void fts3EvalPhraseMergeToken( - Fts3Table *pTab, /* FTS Table pointer */ - Fts3Phrase *p, /* Phrase to merge pList/nList into */ - int iToken, /* Token pList/nList corresponds to */ - char *pList, /* Pointer to doclist */ - int nList /* Number of bytes in pList */ -){ - assert( iToken!=p->iDoclistToken ); - - if( pList==0 ){ - sqlite3_free(p->doclist.aAll); - p->doclist.aAll = 0; - p->doclist.nAll = 0; - } - - else if( p->iDoclistToken<0 ){ - p->doclist.aAll = pList; - p->doclist.nAll = nList; - } - - else if( p->doclist.aAll==0 ){ - sqlite3_free(pList); - } - - else { - char *pLeft; - char *pRight; - int nLeft; - int nRight; - int nDiff; - - if( p->iDoclistTokendoclist.aAll; - nLeft = p->doclist.nAll; - pRight = pList; - nRight = nList; - nDiff = iToken - p->iDoclistToken; - }else{ - pRight = p->doclist.aAll; - nRight = p->doclist.nAll; - pLeft = pList; - nLeft = nList; - nDiff = p->iDoclistToken - iToken; - } - - fts3DoclistPhraseMerge(pTab->bDescIdx, nDiff, pLeft, nLeft, pRight,&nRight); - sqlite3_free(pLeft); - p->doclist.aAll = pRight; - p->doclist.nAll = nRight; - } - - if( iToken>p->iDoclistToken ) p->iDoclistToken = iToken; -} - -/* -** Load the doclist for phrase p into p->doclist.aAll/nAll. The loaded doclist -** does not take deferred tokens into account. -** -** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code. -*/ -static int fts3EvalPhraseLoad( - Fts3Cursor *pCsr, /* FTS Cursor handle */ - Fts3Phrase *p /* Phrase object */ -){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - int iToken; - int rc = SQLITE_OK; - - for(iToken=0; rc==SQLITE_OK && iTokennToken; iToken++){ - Fts3PhraseToken *pToken = &p->aToken[iToken]; - assert( pToken->pDeferred==0 || pToken->pSegcsr==0 ); - - if( pToken->pSegcsr ){ - int nThis = 0; - char *pThis = 0; - rc = fts3TermSelect(pTab, pToken, p->iColumn, &nThis, &pThis); - if( rc==SQLITE_OK ){ - fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis); - } - } - assert( pToken->pSegcsr==0 ); - } - - return rc; -} - -/* -** This function is called on each phrase after the position lists for -** any deferred tokens have been loaded into memory. It updates the phrases -** current position list to include only those positions that are really -** instances of the phrase (after considering deferred tokens). If this -** means that the phrase does not appear in the current row, doclist.pList -** and doclist.nList are both zeroed. -** -** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code. -*/ -static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ - int iToken; /* Used to iterate through phrase tokens */ - char *aPoslist = 0; /* Position list for deferred tokens */ - int nPoslist = 0; /* Number of bytes in aPoslist */ - int iPrev = -1; /* Token number of previous deferred token */ - - assert( pPhrase->doclist.bFreeList==0 ); - - for(iToken=0; iTokennToken; iToken++){ - Fts3PhraseToken *pToken = &pPhrase->aToken[iToken]; - Fts3DeferredToken *pDeferred = pToken->pDeferred; - - if( pDeferred ){ - char *pList; - int nList; - int rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList); - if( rc!=SQLITE_OK ) return rc; - - if( pList==0 ){ - sqlite3_free(aPoslist); - pPhrase->doclist.pList = 0; - pPhrase->doclist.nList = 0; - return SQLITE_OK; - - }else if( aPoslist==0 ){ - aPoslist = pList; - nPoslist = nList; - - }else{ - char *aOut = pList; - char *p1 = aPoslist; - char *p2 = aOut; - - assert( iPrev>=0 ); - fts3PoslistPhraseMerge(&aOut, iToken-iPrev, 0, 1, &p1, &p2); - sqlite3_free(aPoslist); - aPoslist = pList; - nPoslist = (int)(aOut - aPoslist); - if( nPoslist==0 ){ - sqlite3_free(aPoslist); - pPhrase->doclist.pList = 0; - pPhrase->doclist.nList = 0; - return SQLITE_OK; - } - } - iPrev = iToken; - } - } - - if( iPrev>=0 ){ - int nMaxUndeferred = pPhrase->iDoclistToken; - if( nMaxUndeferred<0 ){ - pPhrase->doclist.pList = aPoslist; - pPhrase->doclist.nList = nPoslist; - pPhrase->doclist.iDocid = pCsr->iPrevId; - pPhrase->doclist.bFreeList = 1; - }else{ - int nDistance; - char *p1; - char *p2; - char *aOut; - - if( nMaxUndeferred>iPrev ){ - p1 = aPoslist; - p2 = pPhrase->doclist.pList; - nDistance = nMaxUndeferred - iPrev; - }else{ - p1 = pPhrase->doclist.pList; - p2 = aPoslist; - nDistance = iPrev - nMaxUndeferred; - } - - aOut = (char *)sqlite3_malloc(nPoslist+8); - if( !aOut ){ - sqlite3_free(aPoslist); - return SQLITE_NOMEM; - } - - pPhrase->doclist.pList = aOut; - if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){ - pPhrase->doclist.bFreeList = 1; - pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList); - }else{ - sqlite3_free(aOut); - pPhrase->doclist.pList = 0; - pPhrase->doclist.nList = 0; - } - sqlite3_free(aPoslist); - } - } - - return SQLITE_OK; -} - -/* -** This function is called for each Fts3Phrase in a full-text query -** expression to initialize the mechanism for returning rows. Once this -** function has been called successfully on an Fts3Phrase, it may be -** used with fts3EvalPhraseNext() to iterate through the matching docids. -** -** If parameter bOptOk is true, then the phrase may (or may not) use the -** incremental loading strategy. Otherwise, the entire doclist is loaded into -** memory within this call. -** -** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code. -*/ -static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){ - int rc; /* Error code */ - Fts3PhraseToken *pFirst = &p->aToken[0]; - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - - if( pCsr->bDesc==pTab->bDescIdx - && bOptOk==1 - && p->nToken==1 - && pFirst->pSegcsr - && pFirst->pSegcsr->bLookup - && pFirst->bFirst==0 - ){ - /* Use the incremental approach. */ - int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn); - rc = sqlite3Fts3MsrIncrStart( - pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n); - p->bIncr = 1; - - }else{ - /* Load the full doclist for the phrase into memory. */ - rc = fts3EvalPhraseLoad(pCsr, p); - p->bIncr = 0; - } - - assert( rc!=SQLITE_OK || p->nToken<1 || p->aToken[0].pSegcsr==0 || p->bIncr ); - return rc; -} - -/* -** This function is used to iterate backwards (from the end to start) -** through doclists. It is used by this module to iterate through phrase -** doclists in reverse and by the fts3_write.c module to iterate through -** pending-terms lists when writing to databases with "order=desc". -** -** The doclist may be sorted in ascending (parameter bDescIdx==0) or -** descending (parameter bDescIdx==1) order of docid. Regardless, this -** function iterates from the end of the doclist to the beginning. -*/ -SQLITE_PRIVATE void sqlite3Fts3DoclistPrev( - int bDescIdx, /* True if the doclist is desc */ - char *aDoclist, /* Pointer to entire doclist */ - int nDoclist, /* Length of aDoclist in bytes */ - char **ppIter, /* IN/OUT: Iterator pointer */ - sqlite3_int64 *piDocid, /* IN/OUT: Docid pointer */ - int *pnList, /* OUT: List length pointer */ - u8 *pbEof /* OUT: End-of-file flag */ -){ - char *p = *ppIter; - - assert( nDoclist>0 ); - assert( *pbEof==0 ); - assert( p || *piDocid==0 ); - assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) ); - - if( p==0 ){ - sqlite3_int64 iDocid = 0; - char *pNext = 0; - char *pDocid = aDoclist; - char *pEnd = &aDoclist[nDoclist]; - int iMul = 1; - - while( pDocid0 ); - assert( *pbEof==0 ); - assert( p || *piDocid==0 ); - assert( !p || (p>=aDoclist && p<=&aDoclist[nDoclist]) ); - - if( p==0 ){ - p = aDoclist; - p += sqlite3Fts3GetVarint(p, piDocid); - }else{ - fts3PoslistCopy(0, &p); - if( p>=&aDoclist[nDoclist] ){ - *pbEof = 1; - }else{ - sqlite3_int64 iVar; - p += sqlite3Fts3GetVarint(p, &iVar); - *piDocid += ((bDescIdx ? -1 : 1) * iVar); - } - } - - *ppIter = p; -} - -/* -** Attempt to move the phrase iterator to point to the next matching docid. -** If an error occurs, return an SQLite error code. Otherwise, return -** SQLITE_OK. -** -** If there is no "next" entry and no error occurs, then *pbEof is set to -** 1 before returning. Otherwise, if no error occurs and the iterator is -** successfully advanced, *pbEof is set to 0. -*/ -static int fts3EvalPhraseNext( - Fts3Cursor *pCsr, /* FTS Cursor handle */ - Fts3Phrase *p, /* Phrase object to advance to next docid */ - u8 *pbEof /* OUT: Set to 1 if EOF */ -){ - int rc = SQLITE_OK; - Fts3Doclist *pDL = &p->doclist; - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - - if( p->bIncr ){ - assert( p->nToken==1 ); - assert( pDL->pNextDocid==0 ); - rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr, - &pDL->iDocid, &pDL->pList, &pDL->nList - ); - if( rc==SQLITE_OK && !pDL->pList ){ - *pbEof = 1; - } - }else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){ - sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll, - &pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof - ); - pDL->pList = pDL->pNextDocid; - }else{ - char *pIter; /* Used to iterate through aAll */ - char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */ - if( pDL->pNextDocid ){ - pIter = pDL->pNextDocid; - }else{ - pIter = pDL->aAll; - } - - if( pIter>=pEnd ){ - /* We have already reached the end of this doclist. EOF. */ - *pbEof = 1; - }else{ - sqlite3_int64 iDelta; - pIter += sqlite3Fts3GetVarint(pIter, &iDelta); - if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){ - pDL->iDocid += iDelta; - }else{ - pDL->iDocid -= iDelta; - } - pDL->pList = pIter; - fts3PoslistCopy(0, &pIter); - pDL->nList = (int)(pIter - pDL->pList); - - /* pIter now points just past the 0x00 that terminates the position- - ** list for document pDL->iDocid. However, if this position-list was - ** edited in place by fts3EvalNearTrim(), then pIter may not actually - ** point to the start of the next docid value. The following line deals - ** with this case by advancing pIter past the zero-padding added by - ** fts3EvalNearTrim(). */ - while( pIterpNextDocid = pIter; - assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter ); - *pbEof = 0; - } - } - - return rc; -} - -/* -** -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, fts3EvalPhraseStart() is called on all phrases within the -** expression. Also the Fts3Expr.bDeferred variable is set to true for any -** expressions for which all descendent tokens are deferred. -** -** If parameter bOptOk is zero, then it is guaranteed that the -** Fts3Phrase.doclist.aAll/nAll variables contain the entire doclist for -** each phrase in the expression (subject to deferred token processing). -** Or, if bOptOk is non-zero, then one or more tokens within the expression -** may be loaded incrementally, meaning doclist.aAll/nAll is not available. -** -** If an error occurs within this function, *pRc is set to an SQLite error -** code before returning. -*/ -static void fts3EvalStartReaders( - Fts3Cursor *pCsr, /* FTS Cursor handle */ - Fts3Expr *pExpr, /* Expression to initialize phrases in */ - int bOptOk, /* True to enable incremental loading */ - int *pRc /* IN/OUT: Error code */ -){ - if( pExpr && SQLITE_OK==*pRc ){ - if( pExpr->eType==FTSQUERY_PHRASE ){ - int i; - int nToken = pExpr->pPhrase->nToken; - for(i=0; ipPhrase->aToken[i].pDeferred==0 ) break; - } - pExpr->bDeferred = (i==nToken); - *pRc = fts3EvalPhraseStart(pCsr, bOptOk, pExpr->pPhrase); - }else{ - fts3EvalStartReaders(pCsr, pExpr->pLeft, bOptOk, pRc); - fts3EvalStartReaders(pCsr, pExpr->pRight, bOptOk, pRc); - pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred); - } - } -} - -/* -** An array of the following structures is assembled as part of the process -** of selecting tokens to defer before the query starts executing (as part -** of the xFilter() method). There is one element in the array for each -** token in the FTS expression. -** -** Tokens are divided into AND/NEAR clusters. All tokens in a cluster belong -** to phrases that are connected only by AND and NEAR operators (not OR or -** NOT). When determining tokens to defer, each AND/NEAR cluster is considered -** separately. The root of a tokens AND/NEAR cluster is stored in -** Fts3TokenAndCost.pRoot. -*/ -typedef struct Fts3TokenAndCost Fts3TokenAndCost; -struct Fts3TokenAndCost { - Fts3Phrase *pPhrase; /* The phrase the token belongs to */ - int iToken; /* Position of token in phrase */ - Fts3PhraseToken *pToken; /* The token itself */ - Fts3Expr *pRoot; /* Root of NEAR/AND cluster */ - int nOvfl; /* Number of overflow pages to load doclist */ - int iCol; /* The column the token must match */ -}; - -/* -** This function is used to populate an allocated Fts3TokenAndCost array. -** -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, if an error occurs during execution, *pRc is set to an -** SQLite error code. -*/ -static void fts3EvalTokenCosts( - Fts3Cursor *pCsr, /* FTS Cursor handle */ - Fts3Expr *pRoot, /* Root of current AND/NEAR cluster */ - Fts3Expr *pExpr, /* Expression to consider */ - Fts3TokenAndCost **ppTC, /* Write new entries to *(*ppTC)++ */ - Fts3Expr ***ppOr, /* Write new OR root to *(*ppOr)++ */ - int *pRc /* IN/OUT: Error code */ -){ - if( *pRc==SQLITE_OK ){ - if( pExpr->eType==FTSQUERY_PHRASE ){ - Fts3Phrase *pPhrase = pExpr->pPhrase; - int i; - for(i=0; *pRc==SQLITE_OK && inToken; i++){ - Fts3TokenAndCost *pTC = (*ppTC)++; - pTC->pPhrase = pPhrase; - pTC->iToken = i; - pTC->pRoot = pRoot; - pTC->pToken = &pPhrase->aToken[i]; - pTC->iCol = pPhrase->iColumn; - *pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl); - } - }else if( pExpr->eType!=FTSQUERY_NOT ){ - assert( pExpr->eType==FTSQUERY_OR - || pExpr->eType==FTSQUERY_AND - || pExpr->eType==FTSQUERY_NEAR - ); - assert( pExpr->pLeft && pExpr->pRight ); - if( pExpr->eType==FTSQUERY_OR ){ - pRoot = pExpr->pLeft; - **ppOr = pRoot; - (*ppOr)++; - } - fts3EvalTokenCosts(pCsr, pRoot, pExpr->pLeft, ppTC, ppOr, pRc); - if( pExpr->eType==FTSQUERY_OR ){ - pRoot = pExpr->pRight; - **ppOr = pRoot; - (*ppOr)++; - } - fts3EvalTokenCosts(pCsr, pRoot, pExpr->pRight, ppTC, ppOr, pRc); - } - } -} - -/* -** Determine the average document (row) size in pages. If successful, -** write this value to *pnPage and return SQLITE_OK. Otherwise, return -** an SQLite error code. -** -** The average document size in pages is calculated by first calculating -** determining the average size in bytes, B. If B is less than the amount -** of data that will fit on a single leaf page of an intkey table in -** this database, then the average docsize is 1. Otherwise, it is 1 plus -** the number of overflow pages consumed by a record B bytes in size. -*/ -static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){ - if( pCsr->nRowAvg==0 ){ - /* The average document size, which is required to calculate the cost - ** of each doclist, has not yet been determined. Read the required - ** data from the %_stat table to calculate it. - ** - ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3 - ** varints, where nCol is the number of columns in the FTS3 table. - ** The first varint is the number of documents currently stored in - ** the table. The following nCol varints contain the total amount of - ** data stored in all rows of each column of the table, from left - ** to right. - */ - int rc; - Fts3Table *p = (Fts3Table*)pCsr->base.pVtab; - sqlite3_stmt *pStmt; - sqlite3_int64 nDoc = 0; - sqlite3_int64 nByte = 0; - const char *pEnd; - const char *a; - - rc = sqlite3Fts3SelectDoctotal(p, &pStmt); - if( rc!=SQLITE_OK ) return rc; - a = sqlite3_column_blob(pStmt, 0); - assert( a ); - - pEnd = &a[sqlite3_column_bytes(pStmt, 0)]; - a += sqlite3Fts3GetVarint(a, &nDoc); - while( anDoc = nDoc; - pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz); - assert( pCsr->nRowAvg>0 ); - rc = sqlite3_reset(pStmt); - if( rc!=SQLITE_OK ) return rc; - } - - *pnPage = pCsr->nRowAvg; - return SQLITE_OK; -} - -/* -** This function is called to select the tokens (if any) that will be -** deferred. The array aTC[] has already been populated when this is -** called. -** -** This function is called once for each AND/NEAR cluster in the -** expression. Each invocation determines which tokens to defer within -** the cluster with root node pRoot. See comments above the definition -** of struct Fts3TokenAndCost for more details. -** -** If no error occurs, SQLITE_OK is returned and sqlite3Fts3DeferToken() -** called on each token to defer. Otherwise, an SQLite error code is -** returned. -*/ -static int fts3EvalSelectDeferred( - Fts3Cursor *pCsr, /* FTS Cursor handle */ - Fts3Expr *pRoot, /* Consider tokens with this root node */ - Fts3TokenAndCost *aTC, /* Array of expression tokens and costs */ - int nTC /* Number of entries in aTC[] */ -){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - int nDocSize = 0; /* Number of pages per doc loaded */ - int rc = SQLITE_OK; /* Return code */ - int ii; /* Iterator variable for various purposes */ - int nOvfl = 0; /* Total overflow pages used by doclists */ - int nToken = 0; /* Total number of tokens in cluster */ - - int nMinEst = 0; /* The minimum count for any phrase so far. */ - int nLoad4 = 1; /* (Phrases that will be loaded)^4. */ - - /* Tokens are never deferred for FTS tables created using the content=xxx - ** option. The reason being that it is not guaranteed that the content - ** table actually contains the same data as the index. To prevent this from - ** causing any problems, the deferred token optimization is completely - ** disabled for content=xxx tables. */ - if( pTab->zContentTbl ){ - return SQLITE_OK; - } - - /* Count the tokens in this AND/NEAR cluster. If none of the doclists - ** associated with the tokens spill onto overflow pages, or if there is - ** only 1 token, exit early. No tokens to defer in this case. */ - for(ii=0; ii0 ); - - - /* Iterate through all tokens in this AND/NEAR cluster, in ascending order - ** of the number of overflow pages that will be loaded by the pager layer - ** to retrieve the entire doclist for the token from the full-text index. - ** Load the doclists for tokens that are either: - ** - ** a. The cheapest token in the entire query (i.e. the one visited by the - ** first iteration of this loop), or - ** - ** b. Part of a multi-token phrase. - ** - ** After each token doclist is loaded, merge it with the others from the - ** same phrase and count the number of documents that the merged doclist - ** contains. Set variable "nMinEst" to the smallest number of documents in - ** any phrase doclist for which 1 or more token doclists have been loaded. - ** Let nOther be the number of other phrases for which it is certain that - ** one or more tokens will not be deferred. - ** - ** Then, for each token, defer it if loading the doclist would result in - ** loading N or more overflow pages into memory, where N is computed as: - ** - ** (nMinEst + 4^nOther - 1) / (4^nOther) - */ - for(ii=0; iinOvfl) - ){ - pTC = &aTC[iTC]; - } - } - assert( pTC ); - - if( ii && pTC->nOvfl>=((nMinEst+(nLoad4/4)-1)/(nLoad4/4))*nDocSize ){ - /* The number of overflow pages to load for this (and therefore all - ** subsequent) tokens is greater than the estimated number of pages - ** that will be loaded if all subsequent tokens are deferred. - */ - Fts3PhraseToken *pToken = pTC->pToken; - rc = sqlite3Fts3DeferToken(pCsr, pToken, pTC->iCol); - fts3SegReaderCursorFree(pToken->pSegcsr); - pToken->pSegcsr = 0; - }else{ - /* Set nLoad4 to the value of (4^nOther) for the next iteration of the - ** for-loop. Except, limit the value to 2^24 to prevent it from - ** overflowing the 32-bit integer it is stored in. */ - if( ii<12 ) nLoad4 = nLoad4*4; - - if( ii==0 || pTC->pPhrase->nToken>1 ){ - /* Either this is the cheapest token in the entire query, or it is - ** part of a multi-token phrase. Either way, the entire doclist will - ** (eventually) be loaded into memory. It may as well be now. */ - Fts3PhraseToken *pToken = pTC->pToken; - int nList = 0; - char *pList = 0; - rc = fts3TermSelect(pTab, pToken, pTC->iCol, &nList, &pList); - assert( rc==SQLITE_OK || pList==0 ); - if( rc==SQLITE_OK ){ - int nCount; - fts3EvalPhraseMergeToken(pTab, pTC->pPhrase, pTC->iToken,pList,nList); - nCount = fts3DoclistCountDocids( - pTC->pPhrase->doclist.aAll, pTC->pPhrase->doclist.nAll - ); - if( ii==0 || nCountpToken = 0; - } - - return rc; -} - -/* -** This function is called from within the xFilter method. It initializes -** the full-text query currently stored in pCsr->pExpr. To iterate through -** the results of a query, the caller does: -** -** fts3EvalStart(pCsr); -** while( 1 ){ -** fts3EvalNext(pCsr); -** if( pCsr->bEof ) break; -** ... return row pCsr->iPrevId to the caller ... -** } -*/ -static int fts3EvalStart(Fts3Cursor *pCsr){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - int rc = SQLITE_OK; - int nToken = 0; - int nOr = 0; - - /* Allocate a MultiSegReader for each token in the expression. */ - fts3EvalAllocateReaders(pCsr, pCsr->pExpr, &nToken, &nOr, &rc); - - /* Determine which, if any, tokens in the expression should be deferred. */ -#ifndef SQLITE_DISABLE_FTS4_DEFERRED - if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){ - Fts3TokenAndCost *aTC; - Fts3Expr **apOr; - aTC = (Fts3TokenAndCost *)sqlite3_malloc( - sizeof(Fts3TokenAndCost) * nToken - + sizeof(Fts3Expr *) * nOr * 2 - ); - apOr = (Fts3Expr **)&aTC[nToken]; - - if( !aTC ){ - rc = SQLITE_NOMEM; - }else{ - int ii; - Fts3TokenAndCost *pTC = aTC; - Fts3Expr **ppOr = apOr; - - fts3EvalTokenCosts(pCsr, 0, pCsr->pExpr, &pTC, &ppOr, &rc); - nToken = (int)(pTC-aTC); - nOr = (int)(ppOr-apOr); - - if( rc==SQLITE_OK ){ - rc = fts3EvalSelectDeferred(pCsr, 0, aTC, nToken); - for(ii=0; rc==SQLITE_OK && iipExpr, 1, &rc); - return rc; -} - -/* -** Invalidate the current position list for phrase pPhrase. -*/ -static void fts3EvalInvalidatePoslist(Fts3Phrase *pPhrase){ - if( pPhrase->doclist.bFreeList ){ - sqlite3_free(pPhrase->doclist.pList); - } - pPhrase->doclist.pList = 0; - pPhrase->doclist.nList = 0; - pPhrase->doclist.bFreeList = 0; -} - -/* -** This function is called to edit the position list associated with -** the phrase object passed as the fifth argument according to a NEAR -** condition. For example: -** -** abc NEAR/5 "def ghi" -** -** Parameter nNear is passed the NEAR distance of the expression (5 in -** the example above). When this function is called, *paPoslist points to -** the position list, and *pnToken is the number of phrase tokens in, the -** phrase on the other side of the NEAR operator to pPhrase. For example, -** if pPhrase refers to the "def ghi" phrase, then *paPoslist points to -** the position list associated with phrase "abc". -** -** All positions in the pPhrase position list that are not sufficiently -** close to a position in the *paPoslist position list are removed. If this -** leaves 0 positions, zero is returned. Otherwise, non-zero. -** -** Before returning, *paPoslist is set to point to the position lsit -** associated with pPhrase. And *pnToken is set to the number of tokens in -** pPhrase. -*/ -static int fts3EvalNearTrim( - int nNear, /* NEAR distance. As in "NEAR/nNear". */ - char *aTmp, /* Temporary space to use */ - char **paPoslist, /* IN/OUT: Position list */ - int *pnToken, /* IN/OUT: Tokens in phrase of *paPoslist */ - Fts3Phrase *pPhrase /* The phrase object to trim the doclist of */ -){ - int nParam1 = nNear + pPhrase->nToken; - int nParam2 = nNear + *pnToken; - int nNew; - char *p2; - char *pOut; - int res; - - assert( pPhrase->doclist.pList ); - - p2 = pOut = pPhrase->doclist.pList; - res = fts3PoslistNearMerge( - &pOut, aTmp, nParam1, nParam2, paPoslist, &p2 - ); - if( res ){ - nNew = (int)(pOut - pPhrase->doclist.pList) - 1; - assert( pPhrase->doclist.pList[nNew]=='\0' ); - assert( nNew<=pPhrase->doclist.nList && nNew>0 ); - memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew); - pPhrase->doclist.nList = nNew; - *paPoslist = pPhrase->doclist.pList; - *pnToken = pPhrase->nToken; - } - - return res; -} - -/* -** This function is a no-op if *pRc is other than SQLITE_OK when it is called. -** Otherwise, it advances the expression passed as the second argument to -** point to the next matching row in the database. Expressions iterate through -** matching rows in docid order. Ascending order if Fts3Cursor.bDesc is zero, -** or descending if it is non-zero. -** -** If an error occurs, *pRc is set to an SQLite error code. Otherwise, if -** successful, the following variables in pExpr are set: -** -** Fts3Expr.bEof (non-zero if EOF - there is no next row) -** Fts3Expr.iDocid (valid if bEof==0. The docid of the next row) -** -** If the expression is of type FTSQUERY_PHRASE, and the expression is not -** at EOF, then the following variables are populated with the position list -** for the phrase for the visited row: -** -** FTs3Expr.pPhrase->doclist.nList (length of pList in bytes) -** FTs3Expr.pPhrase->doclist.pList (pointer to position list) -** -** It says above that this function advances the expression to the next -** matching row. This is usually true, but there are the following exceptions: -** -** 1. Deferred tokens are not taken into account. If a phrase consists -** entirely of deferred tokens, it is assumed to match every row in -** the db. In this case the position-list is not populated at all. -** -** Or, if a phrase contains one or more deferred tokens and one or -** more non-deferred tokens, then the expression is advanced to the -** next possible match, considering only non-deferred tokens. In other -** words, if the phrase is "A B C", and "B" is deferred, the expression -** is advanced to the next row that contains an instance of "A * C", -** where "*" may match any single token. The position list in this case -** is populated as for "A * C" before returning. -** -** 2. NEAR is treated as AND. If the expression is "x NEAR y", it is -** advanced to point to the next row that matches "x AND y". -** -** See fts3EvalTestDeferredAndNear() for details on testing if a row is -** really a match, taking into account deferred tokens and NEAR operators. -*/ -static void fts3EvalNextRow( - Fts3Cursor *pCsr, /* FTS Cursor handle */ - Fts3Expr *pExpr, /* Expr. to advance to next matching row */ - int *pRc /* IN/OUT: Error code */ -){ - if( *pRc==SQLITE_OK ){ - int bDescDoclist = pCsr->bDesc; /* Used by DOCID_CMP() macro */ - assert( pExpr->bEof==0 ); - pExpr->bStart = 1; - - switch( pExpr->eType ){ - case FTSQUERY_NEAR: - case FTSQUERY_AND: { - Fts3Expr *pLeft = pExpr->pLeft; - Fts3Expr *pRight = pExpr->pRight; - assert( !pLeft->bDeferred || !pRight->bDeferred ); - - if( pLeft->bDeferred ){ - /* LHS is entirely deferred. So we assume it matches every row. - ** Advance the RHS iterator to find the next row visited. */ - fts3EvalNextRow(pCsr, pRight, pRc); - pExpr->iDocid = pRight->iDocid; - pExpr->bEof = pRight->bEof; - }else if( pRight->bDeferred ){ - /* RHS is entirely deferred. So we assume it matches every row. - ** Advance the LHS iterator to find the next row visited. */ - fts3EvalNextRow(pCsr, pLeft, pRc); - pExpr->iDocid = pLeft->iDocid; - pExpr->bEof = pLeft->bEof; - }else{ - /* Neither the RHS or LHS are deferred. */ - fts3EvalNextRow(pCsr, pLeft, pRc); - fts3EvalNextRow(pCsr, pRight, pRc); - while( !pLeft->bEof && !pRight->bEof && *pRc==SQLITE_OK ){ - sqlite3_int64 iDiff = DOCID_CMP(pLeft->iDocid, pRight->iDocid); - if( iDiff==0 ) break; - if( iDiff<0 ){ - fts3EvalNextRow(pCsr, pLeft, pRc); - }else{ - fts3EvalNextRow(pCsr, pRight, pRc); - } - } - pExpr->iDocid = pLeft->iDocid; - pExpr->bEof = (pLeft->bEof || pRight->bEof); - } - break; - } - - case FTSQUERY_OR: { - Fts3Expr *pLeft = pExpr->pLeft; - Fts3Expr *pRight = pExpr->pRight; - sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); - - assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); - assert( pRight->bStart || pLeft->iDocid==pRight->iDocid ); - - if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ - fts3EvalNextRow(pCsr, pLeft, pRc); - }else if( pLeft->bEof || (pRight->bEof==0 && iCmp>0) ){ - fts3EvalNextRow(pCsr, pRight, pRc); - }else{ - fts3EvalNextRow(pCsr, pLeft, pRc); - fts3EvalNextRow(pCsr, pRight, pRc); - } - - pExpr->bEof = (pLeft->bEof && pRight->bEof); - iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); - if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ - pExpr->iDocid = pLeft->iDocid; - }else{ - pExpr->iDocid = pRight->iDocid; - } - - break; - } - - case FTSQUERY_NOT: { - Fts3Expr *pLeft = pExpr->pLeft; - Fts3Expr *pRight = pExpr->pRight; - - if( pRight->bStart==0 ){ - fts3EvalNextRow(pCsr, pRight, pRc); - assert( *pRc!=SQLITE_OK || pRight->bStart ); - } - - fts3EvalNextRow(pCsr, pLeft, pRc); - if( pLeft->bEof==0 ){ - while( !*pRc - && !pRight->bEof - && DOCID_CMP(pLeft->iDocid, pRight->iDocid)>0 - ){ - fts3EvalNextRow(pCsr, pRight, pRc); - } - } - pExpr->iDocid = pLeft->iDocid; - pExpr->bEof = pLeft->bEof; - break; - } - - default: { - Fts3Phrase *pPhrase = pExpr->pPhrase; - fts3EvalInvalidatePoslist(pPhrase); - *pRc = fts3EvalPhraseNext(pCsr, pPhrase, &pExpr->bEof); - pExpr->iDocid = pPhrase->doclist.iDocid; - break; - } - } - } -} - -/* -** If *pRc is not SQLITE_OK, or if pExpr is not the root node of a NEAR -** cluster, then this function returns 1 immediately. -** -** Otherwise, it checks if the current row really does match the NEAR -** expression, using the data currently stored in the position lists -** (Fts3Expr->pPhrase.doclist.pList/nList) for each phrase in the expression. -** -** If the current row is a match, the position list associated with each -** phrase in the NEAR expression is edited in place to contain only those -** phrase instances sufficiently close to their peers to satisfy all NEAR -** constraints. In this case it returns 1. If the NEAR expression does not -** match the current row, 0 is returned. The position lists may or may not -** be edited if 0 is returned. -*/ -static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ - int res = 1; - - /* The following block runs if pExpr is the root of a NEAR query. - ** For example, the query: - ** - ** "w" NEAR "x" NEAR "y" NEAR "z" - ** - ** which is represented in tree form as: - ** - ** | - ** +--NEAR--+ <-- root of NEAR query - ** | | - ** +--NEAR--+ "z" - ** | | - ** +--NEAR--+ "y" - ** | | - ** "w" "x" - ** - ** The right-hand child of a NEAR node is always a phrase. The - ** left-hand child may be either a phrase or a NEAR node. There are - ** no exceptions to this - it's the way the parser in fts3_expr.c works. - */ - if( *pRc==SQLITE_OK - && pExpr->eType==FTSQUERY_NEAR - && pExpr->bEof==0 - && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR) - ){ - Fts3Expr *p; - int nTmp = 0; /* Bytes of temp space */ - char *aTmp; /* Temp space for PoslistNearMerge() */ - - /* Allocate temporary working space. */ - for(p=pExpr; p->pLeft; p=p->pLeft){ - nTmp += p->pRight->pPhrase->doclist.nList; - } - nTmp += p->pPhrase->doclist.nList; - if( nTmp==0 ){ - res = 0; - }else{ - aTmp = sqlite3_malloc(nTmp*2); - if( !aTmp ){ - *pRc = SQLITE_NOMEM; - res = 0; - }else{ - char *aPoslist = p->pPhrase->doclist.pList; - int nToken = p->pPhrase->nToken; - - for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){ - Fts3Phrase *pPhrase = p->pRight->pPhrase; - int nNear = p->nNear; - res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); - } - - aPoslist = pExpr->pRight->pPhrase->doclist.pList; - nToken = pExpr->pRight->pPhrase->nToken; - for(p=pExpr->pLeft; p && res; p=p->pLeft){ - int nNear; - Fts3Phrase *pPhrase; - assert( p->pParent && p->pParent->pLeft==p ); - nNear = p->pParent->nNear; - pPhrase = ( - p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase - ); - res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); - } - } - - sqlite3_free(aTmp); - } - } - - return res; -} - -/* -** This function is a helper function for fts3EvalTestDeferredAndNear(). -** Assuming no error occurs or has occurred, It returns non-zero if the -** expression passed as the second argument matches the row that pCsr -** currently points to, or zero if it does not. -** -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. -** If an error occurs during execution of this function, *pRc is set to -** the appropriate SQLite error code. In this case the returned value is -** undefined. -*/ -static int fts3EvalTestExpr( - Fts3Cursor *pCsr, /* FTS cursor handle */ - Fts3Expr *pExpr, /* Expr to test. May or may not be root. */ - int *pRc /* IN/OUT: Error code */ -){ - int bHit = 1; /* Return value */ - if( *pRc==SQLITE_OK ){ - switch( pExpr->eType ){ - case FTSQUERY_NEAR: - case FTSQUERY_AND: - bHit = ( - fts3EvalTestExpr(pCsr, pExpr->pLeft, pRc) - && fts3EvalTestExpr(pCsr, pExpr->pRight, pRc) - && fts3EvalNearTest(pExpr, pRc) - ); - - /* If the NEAR expression does not match any rows, zero the doclist for - ** all phrases involved in the NEAR. This is because the snippet(), - ** offsets() and matchinfo() functions are not supposed to recognize - ** any instances of phrases that are part of unmatched NEAR queries. - ** For example if this expression: - ** - ** ... MATCH 'a OR (b NEAR c)' - ** - ** is matched against a row containing: - ** - ** 'a b d e' - ** - ** then any snippet() should ony highlight the "a" term, not the "b" - ** (as "b" is part of a non-matching NEAR clause). - */ - if( bHit==0 - && pExpr->eType==FTSQUERY_NEAR - && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR) - ){ - Fts3Expr *p; - for(p=pExpr; p->pPhrase==0; p=p->pLeft){ - if( p->pRight->iDocid==pCsr->iPrevId ){ - fts3EvalInvalidatePoslist(p->pRight->pPhrase); - } - } - if( p->iDocid==pCsr->iPrevId ){ - fts3EvalInvalidatePoslist(p->pPhrase); - } - } - - break; - - case FTSQUERY_OR: { - int bHit1 = fts3EvalTestExpr(pCsr, pExpr->pLeft, pRc); - int bHit2 = fts3EvalTestExpr(pCsr, pExpr->pRight, pRc); - bHit = bHit1 || bHit2; - break; - } - - case FTSQUERY_NOT: - bHit = ( - fts3EvalTestExpr(pCsr, pExpr->pLeft, pRc) - && !fts3EvalTestExpr(pCsr, pExpr->pRight, pRc) - ); - break; - - default: { -#ifndef SQLITE_DISABLE_FTS4_DEFERRED - if( pCsr->pDeferred - && (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred) - ){ - Fts3Phrase *pPhrase = pExpr->pPhrase; - assert( pExpr->bDeferred || pPhrase->doclist.bFreeList==0 ); - if( pExpr->bDeferred ){ - fts3EvalInvalidatePoslist(pPhrase); - } - *pRc = fts3EvalDeferredPhrase(pCsr, pPhrase); - bHit = (pPhrase->doclist.pList!=0); - pExpr->iDocid = pCsr->iPrevId; - }else -#endif - { - bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId); - } - break; - } - } - } - return bHit; -} - -/* -** This function is called as the second part of each xNext operation when -** iterating through the results of a full-text query. At this point the -** cursor points to a row that matches the query expression, with the -** following caveats: -** -** * Up until this point, "NEAR" operators in the expression have been -** treated as "AND". -** -** * Deferred tokens have not yet been considered. -** -** If *pRc is not SQLITE_OK when this function is called, it immediately -** returns 0. Otherwise, it tests whether or not after considering NEAR -** operators and deferred tokens the current row is still a match for the -** expression. It returns 1 if both of the following are true: -** -** 1. *pRc is SQLITE_OK when this function returns, and -** -** 2. After scanning the current FTS table row for the deferred tokens, -** it is determined that the row does *not* match the query. -** -** Or, if no error occurs and it seems the current row does match the FTS -** query, return 0. -*/ -static int fts3EvalTestDeferredAndNear(Fts3Cursor *pCsr, int *pRc){ - int rc = *pRc; - int bMiss = 0; - if( rc==SQLITE_OK ){ - - /* If there are one or more deferred tokens, load the current row into - ** memory and scan it to determine the position list for each deferred - ** token. Then, see if this row is really a match, considering deferred - ** tokens and NEAR operators (neither of which were taken into account - ** earlier, by fts3EvalNextRow()). - */ - if( pCsr->pDeferred ){ - rc = fts3CursorSeek(0, pCsr); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3CacheDeferredDoclists(pCsr); - } - } - bMiss = (0==fts3EvalTestExpr(pCsr, pCsr->pExpr, &rc)); - - /* Free the position-lists accumulated for each deferred token above. */ - sqlite3Fts3FreeDeferredDoclists(pCsr); - *pRc = rc; - } - return (rc==SQLITE_OK && bMiss); -} - -/* -** Advance to the next document that matches the FTS expression in -** Fts3Cursor.pExpr. -*/ -static int fts3EvalNext(Fts3Cursor *pCsr){ - int rc = SQLITE_OK; /* Return Code */ - Fts3Expr *pExpr = pCsr->pExpr; - assert( pCsr->isEof==0 ); - if( pExpr==0 ){ - pCsr->isEof = 1; - }else{ - do { - if( pCsr->isRequireSeek==0 ){ - sqlite3_reset(pCsr->pStmt); - } - assert( sqlite3_data_count(pCsr->pStmt)==0 ); - fts3EvalNextRow(pCsr, pExpr, &rc); - pCsr->isEof = pExpr->bEof; - pCsr->isRequireSeek = 1; - pCsr->isMatchinfoNeeded = 1; - pCsr->iPrevId = pExpr->iDocid; - }while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) ); - } - return rc; -} - -/* -** Restart interation for expression pExpr so that the next call to -** fts3EvalNext() visits the first row. Do not allow incremental -** loading or merging of phrase doclists for this iteration. -** -** If *pRc is other than SQLITE_OK when this function is called, it is -** a no-op. If an error occurs within this function, *pRc is set to an -** SQLite error code before returning. -*/ -static void fts3EvalRestart( - Fts3Cursor *pCsr, - Fts3Expr *pExpr, - int *pRc -){ - if( pExpr && *pRc==SQLITE_OK ){ - Fts3Phrase *pPhrase = pExpr->pPhrase; - - if( pPhrase ){ - fts3EvalInvalidatePoslist(pPhrase); - if( pPhrase->bIncr ){ - assert( pPhrase->nToken==1 ); - assert( pPhrase->aToken[0].pSegcsr ); - sqlite3Fts3MsrIncrRestart(pPhrase->aToken[0].pSegcsr); - *pRc = fts3EvalPhraseStart(pCsr, 0, pPhrase); - } - - pPhrase->doclist.pNextDocid = 0; - pPhrase->doclist.iDocid = 0; - } - - pExpr->iDocid = 0; - pExpr->bEof = 0; - pExpr->bStart = 0; - - fts3EvalRestart(pCsr, pExpr->pLeft, pRc); - fts3EvalRestart(pCsr, pExpr->pRight, pRc); - } -} - -/* -** After allocating the Fts3Expr.aMI[] array for each phrase in the -** expression rooted at pExpr, the cursor iterates through all rows matched -** by pExpr, calling this function for each row. This function increments -** the values in Fts3Expr.aMI[] according to the position-list currently -** found in Fts3Expr.pPhrase->doclist.pList for each of the phrase -** expression nodes. -*/ -static void fts3EvalUpdateCounts(Fts3Expr *pExpr){ - if( pExpr ){ - Fts3Phrase *pPhrase = pExpr->pPhrase; - if( pPhrase && pPhrase->doclist.pList ){ - int iCol = 0; - char *p = pPhrase->doclist.pList; - - assert( *p ); - while( 1 ){ - u8 c = 0; - int iCnt = 0; - while( 0xFE & (*p | c) ){ - if( (c&0x80)==0 ) iCnt++; - c = *p++ & 0x80; - } - - /* aMI[iCol*3 + 1] = Number of occurrences - ** aMI[iCol*3 + 2] = Number of rows containing at least one instance - */ - pExpr->aMI[iCol*3 + 1] += iCnt; - pExpr->aMI[iCol*3 + 2] += (iCnt>0); - if( *p==0x00 ) break; - p++; - p += sqlite3Fts3GetVarint32(p, &iCol); - } - } - - fts3EvalUpdateCounts(pExpr->pLeft); - fts3EvalUpdateCounts(pExpr->pRight); - } -} - -/* -** Expression pExpr must be of type FTSQUERY_PHRASE. -** -** If it is not already allocated and populated, this function allocates and -** populates the Fts3Expr.aMI[] array for expression pExpr. If pExpr is part -** of a NEAR expression, then it also allocates and populates the same array -** for all other phrases that are part of the NEAR expression. -** -** SQLITE_OK is returned if the aMI[] array is successfully allocated and -** populated. Otherwise, if an error occurs, an SQLite error code is returned. -*/ -static int fts3EvalGatherStats( - Fts3Cursor *pCsr, /* Cursor object */ - Fts3Expr *pExpr /* FTSQUERY_PHRASE expression */ -){ - int rc = SQLITE_OK; /* Return code */ - - assert( pExpr->eType==FTSQUERY_PHRASE ); - if( pExpr->aMI==0 ){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - Fts3Expr *pRoot; /* Root of NEAR expression */ - Fts3Expr *p; /* Iterator used for several purposes */ - - sqlite3_int64 iPrevId = pCsr->iPrevId; - sqlite3_int64 iDocid; - u8 bEof; - - /* Find the root of the NEAR expression */ - pRoot = pExpr; - while( pRoot->pParent && pRoot->pParent->eType==FTSQUERY_NEAR ){ - pRoot = pRoot->pParent; - } - iDocid = pRoot->iDocid; - bEof = pRoot->bEof; - assert( pRoot->bStart ); - - /* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */ - for(p=pRoot; p; p=p->pLeft){ - Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight); - assert( pE->aMI==0 ); - pE->aMI = (u32 *)sqlite3_malloc(pTab->nColumn * 3 * sizeof(u32)); - if( !pE->aMI ) return SQLITE_NOMEM; - memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); - } - - fts3EvalRestart(pCsr, pRoot, &rc); - - while( pCsr->isEof==0 && rc==SQLITE_OK ){ - - do { - /* Ensure the %_content statement is reset. */ - if( pCsr->isRequireSeek==0 ) sqlite3_reset(pCsr->pStmt); - assert( sqlite3_data_count(pCsr->pStmt)==0 ); - - /* Advance to the next document */ - fts3EvalNextRow(pCsr, pRoot, &rc); - pCsr->isEof = pRoot->bEof; - pCsr->isRequireSeek = 1; - pCsr->isMatchinfoNeeded = 1; - pCsr->iPrevId = pRoot->iDocid; - }while( pCsr->isEof==0 - && pRoot->eType==FTSQUERY_NEAR - && fts3EvalTestDeferredAndNear(pCsr, &rc) - ); - - if( rc==SQLITE_OK && pCsr->isEof==0 ){ - fts3EvalUpdateCounts(pRoot); - } - } - - pCsr->isEof = 0; - pCsr->iPrevId = iPrevId; - - if( bEof ){ - pRoot->bEof = bEof; - }else{ - /* Caution: pRoot may iterate through docids in ascending or descending - ** order. For this reason, even though it seems more defensive, the - ** do loop can not be written: - ** - ** do {...} while( pRoot->iDocidbEof==0 ); - }while( pRoot->iDocid!=iDocid && rc==SQLITE_OK ); - fts3EvalTestDeferredAndNear(pCsr, &rc); - } - } - return rc; -} - -/* -** This function is used by the matchinfo() module to query a phrase -** expression node for the following information: -** -** 1. The total number of occurrences of the phrase in each column of -** the FTS table (considering all rows), and -** -** 2. For each column, the number of rows in the table for which the -** column contains at least one instance of the phrase. -** -** If no error occurs, SQLITE_OK is returned and the values for each column -** written into the array aiOut as follows: -** -** aiOut[iCol*3 + 1] = Number of occurrences -** aiOut[iCol*3 + 2] = Number of rows containing at least one instance -** -** Caveats: -** -** * If a phrase consists entirely of deferred tokens, then all output -** values are set to the number of documents in the table. In other -** words we assume that very common tokens occur exactly once in each -** column of each row of the table. -** -** * If a phrase contains some deferred tokens (and some non-deferred -** tokens), count the potential occurrence identified by considering -** the non-deferred tokens instead of actual phrase occurrences. -** -** * If the phrase is part of a NEAR expression, then only phrase instances -** that meet the NEAR constraint are included in the counts. -*/ -SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats( - Fts3Cursor *pCsr, /* FTS cursor handle */ - Fts3Expr *pExpr, /* Phrase expression */ - u32 *aiOut /* Array to write results into (see above) */ -){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - int rc = SQLITE_OK; - int iCol; - - if( pExpr->bDeferred && pExpr->pParent->eType!=FTSQUERY_NEAR ){ - assert( pCsr->nDoc>0 ); - for(iCol=0; iColnColumn; iCol++){ - aiOut[iCol*3 + 1] = (u32)pCsr->nDoc; - aiOut[iCol*3 + 2] = (u32)pCsr->nDoc; - } - }else{ - rc = fts3EvalGatherStats(pCsr, pExpr); - if( rc==SQLITE_OK ){ - assert( pExpr->aMI ); - for(iCol=0; iColnColumn; iCol++){ - aiOut[iCol*3 + 1] = pExpr->aMI[iCol*3 + 1]; - aiOut[iCol*3 + 2] = pExpr->aMI[iCol*3 + 2]; - } - } - } - - return rc; -} - -/* -** The expression pExpr passed as the second argument to this function -** must be of type FTSQUERY_PHRASE. -** -** The returned value is either NULL or a pointer to a buffer containing -** a position-list indicating the occurrences of the phrase in column iCol -** of the current row. -** -** More specifically, the returned buffer contains 1 varint for each -** occurrence of the phrase in the column, stored using the normal (delta+2) -** compression and is terminated by either an 0x01 or 0x00 byte. For example, -** if the requested column contains "a b X c d X X" and the position-list -** for 'X' is requested, the buffer returned may contain: -** -** 0x04 0x05 0x03 0x01 or 0x04 0x05 0x03 0x00 -** -** This function works regardless of whether or not the phrase is deferred, -** incremental, or neither. -*/ -SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( - Fts3Cursor *pCsr, /* FTS3 cursor object */ - Fts3Expr *pExpr, /* Phrase to return doclist for */ - int iCol, /* Column to return position list for */ - char **ppOut /* OUT: Pointer to position list */ -){ - Fts3Phrase *pPhrase = pExpr->pPhrase; - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - char *pIter; - int iThis; - sqlite3_int64 iDocid; - - /* If this phrase is applies specifically to some column other than - ** column iCol, return a NULL pointer. */ - *ppOut = 0; - assert( iCol>=0 && iColnColumn ); - if( (pPhrase->iColumnnColumn && pPhrase->iColumn!=iCol) ){ - return SQLITE_OK; - } - - iDocid = pExpr->iDocid; - pIter = pPhrase->doclist.pList; - if( iDocid!=pCsr->iPrevId || pExpr->bEof ){ - int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */ - int bOr = 0; - u8 bEof = 0; - Fts3Expr *p; - - /* Check if this phrase descends from an OR expression node. If not, - ** return NULL. Otherwise, the entry that corresponds to docid - ** pCsr->iPrevId may lie earlier in the doclist buffer. */ - for(p=pExpr->pParent; p; p=p->pParent){ - if( p->eType==FTSQUERY_OR ) bOr = 1; - } - if( bOr==0 ) return SQLITE_OK; - - /* This is the descendent of an OR node. In this case we cannot use - ** an incremental phrase. Load the entire doclist for the phrase - ** into memory in this case. */ - if( pPhrase->bIncr ){ - int rc = SQLITE_OK; - int bEofSave = pExpr->bEof; - fts3EvalRestart(pCsr, pExpr, &rc); - while( rc==SQLITE_OK && !pExpr->bEof ){ - fts3EvalNextRow(pCsr, pExpr, &rc); - if( bEofSave==0 && pExpr->iDocid==iDocid ) break; - } - pIter = pPhrase->doclist.pList; - assert( rc!=SQLITE_OK || pPhrase->bIncr==0 ); - if( rc!=SQLITE_OK ) return rc; - } - - if( pExpr->bEof ){ - pIter = 0; - iDocid = 0; - } - bEof = (pPhrase->doclist.nAll==0); - assert( bDescDoclist==0 || bDescDoclist==1 ); - assert( pCsr->bDesc==0 || pCsr->bDesc==1 ); - - if( pCsr->bDesc==bDescDoclist ){ - int dummy; - while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){ - sqlite3Fts3DoclistPrev( - bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, - &pIter, &iDocid, &dummy, &bEof - ); - } - }else{ - while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){ - sqlite3Fts3DoclistNext( - bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, - &pIter, &iDocid, &bEof - ); - } - } - - if( bEof || iDocid!=pCsr->iPrevId ) pIter = 0; - } - if( pIter==0 ) return SQLITE_OK; - - if( *pIter==0x01 ){ - pIter++; - pIter += sqlite3Fts3GetVarint32(pIter, &iThis); - }else{ - iThis = 0; - } - while( iThisdoclist, and -** * any Fts3MultiSegReader objects held by phrase tokens. -*/ -SQLITE_PRIVATE void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *pPhrase){ - if( pPhrase ){ - int i; - sqlite3_free(pPhrase->doclist.aAll); - fts3EvalInvalidatePoslist(pPhrase); - memset(&pPhrase->doclist, 0, sizeof(Fts3Doclist)); - for(i=0; inToken; i++){ - fts3SegReaderCursorFree(pPhrase->aToken[i].pSegcsr); - pPhrase->aToken[i].pSegcsr = 0; - } - } -} - - -/* -** Return SQLITE_CORRUPT_VTAB. -*/ -#ifdef SQLITE_DEBUG -SQLITE_PRIVATE int sqlite3Fts3Corrupt(){ - return SQLITE_CORRUPT_VTAB; -} -#endif - -#if !SQLITE_CORE -/* -** Initialize API pointer table, if required. -*/ -SQLITE_API int sqlite3_extension_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi) - return sqlite3Fts3Init(db); -} -#endif - -#endif - -/************** End of fts3.c ************************************************/ -/************** Begin file fts3_aux.c ****************************************/ -/* -** 2011 Jan 27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -*/ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* #include */ -/* #include */ - -typedef struct Fts3auxTable Fts3auxTable; -typedef struct Fts3auxCursor Fts3auxCursor; - -struct Fts3auxTable { - sqlite3_vtab base; /* Base class used by SQLite core */ - Fts3Table *pFts3Tab; -}; - -struct Fts3auxCursor { - sqlite3_vtab_cursor base; /* Base class used by SQLite core */ - Fts3MultiSegReader csr; /* Must be right after "base" */ - Fts3SegFilter filter; - char *zStop; - int nStop; /* Byte-length of string zStop */ - int isEof; /* True if cursor is at EOF */ - sqlite3_int64 iRowid; /* Current rowid */ - - int iCol; /* Current value of 'col' column */ - int nStat; /* Size of aStat[] array */ - struct Fts3auxColstats { - sqlite3_int64 nDoc; /* 'documents' values for current csr row */ - sqlite3_int64 nOcc; /* 'occurrences' values for current csr row */ - } *aStat; -}; - -/* -** Schema of the terms table. -*/ -#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)" - -/* -** This function does all the work for both the xConnect and xCreate methods. -** These tables have no persistent representation of their own, so xConnect -** and xCreate are identical operations. -*/ -static int fts3auxConnectMethod( - sqlite3 *db, /* Database connection */ - void *pUnused, /* Unused */ - int argc, /* Number of elements in argv array */ - const char * const *argv, /* xCreate/xConnect argument array */ - sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ - char **pzErr /* OUT: sqlite3_malloc'd error message */ -){ - char const *zDb; /* Name of database (e.g. "main") */ - char const *zFts3; /* Name of fts3 table */ - int nDb; /* Result of strlen(zDb) */ - int nFts3; /* Result of strlen(zFts3) */ - int nByte; /* Bytes of space to allocate here */ - int rc; /* value returned by declare_vtab() */ - Fts3auxTable *p; /* Virtual table object to return */ - - UNUSED_PARAMETER(pUnused); - - /* The user should specify a single argument - the name of an fts3 table. */ - if( argc!=4 ){ - *pzErr = sqlite3_mprintf( - "wrong number of arguments to fts4aux constructor" - ); - return SQLITE_ERROR; - } - - zDb = argv[1]; - nDb = (int)strlen(zDb); - zFts3 = argv[3]; - nFts3 = (int)strlen(zFts3); - - rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA); - if( rc!=SQLITE_OK ) return rc; - - nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2; - p = (Fts3auxTable *)sqlite3_malloc(nByte); - if( !p ) return SQLITE_NOMEM; - memset(p, 0, nByte); - - p->pFts3Tab = (Fts3Table *)&p[1]; - p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; - p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; - p->pFts3Tab->db = db; - p->pFts3Tab->nIndex = 1; - - memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); - memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); - sqlite3Fts3Dequote((char *)p->pFts3Tab->zName); - - *ppVtab = (sqlite3_vtab *)p; - return SQLITE_OK; -} - -/* -** This function does the work for both the xDisconnect and xDestroy methods. -** These tables have no persistent representation of their own, so xDisconnect -** and xDestroy are identical operations. -*/ -static int fts3auxDisconnectMethod(sqlite3_vtab *pVtab){ - Fts3auxTable *p = (Fts3auxTable *)pVtab; - Fts3Table *pFts3 = p->pFts3Tab; - int i; - - /* Free any prepared statements held */ - for(i=0; iaStmt); i++){ - sqlite3_finalize(pFts3->aStmt[i]); - } - sqlite3_free(pFts3->zSegmentsTbl); - sqlite3_free(p); - return SQLITE_OK; -} - -#define FTS4AUX_EQ_CONSTRAINT 1 -#define FTS4AUX_GE_CONSTRAINT 2 -#define FTS4AUX_LE_CONSTRAINT 4 - -/* -** xBestIndex - Analyze a WHERE and ORDER BY clause. -*/ -static int fts3auxBestIndexMethod( - sqlite3_vtab *pVTab, - sqlite3_index_info *pInfo -){ - int i; - int iEq = -1; - int iGe = -1; - int iLe = -1; - - UNUSED_PARAMETER(pVTab); - - /* This vtab delivers always results in "ORDER BY term ASC" order. */ - if( pInfo->nOrderBy==1 - && pInfo->aOrderBy[0].iColumn==0 - && pInfo->aOrderBy[0].desc==0 - ){ - pInfo->orderByConsumed = 1; - } - - /* Search for equality and range constraints on the "term" column. */ - for(i=0; inConstraint; i++){ - if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){ - int op = pInfo->aConstraint[i].op; - if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i; - if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i; - if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i; - if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i; - if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i; - } - } - - if( iEq>=0 ){ - pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT; - pInfo->aConstraintUsage[iEq].argvIndex = 1; - pInfo->estimatedCost = 5; - }else{ - pInfo->idxNum = 0; - pInfo->estimatedCost = 20000; - if( iGe>=0 ){ - pInfo->idxNum += FTS4AUX_GE_CONSTRAINT; - pInfo->aConstraintUsage[iGe].argvIndex = 1; - pInfo->estimatedCost /= 2; - } - if( iLe>=0 ){ - pInfo->idxNum += FTS4AUX_LE_CONSTRAINT; - pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0); - pInfo->estimatedCost /= 2; - } - } - - return SQLITE_OK; -} - -/* -** xOpen - Open a cursor. -*/ -static int fts3auxOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ - Fts3auxCursor *pCsr; /* Pointer to cursor object to return */ - - UNUSED_PARAMETER(pVTab); - - pCsr = (Fts3auxCursor *)sqlite3_malloc(sizeof(Fts3auxCursor)); - if( !pCsr ) return SQLITE_NOMEM; - memset(pCsr, 0, sizeof(Fts3auxCursor)); - - *ppCsr = (sqlite3_vtab_cursor *)pCsr; - return SQLITE_OK; -} - -/* -** xClose - Close a cursor. -*/ -static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){ - Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; - Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; - - sqlite3Fts3SegmentsClose(pFts3); - sqlite3Fts3SegReaderFinish(&pCsr->csr); - sqlite3_free((void *)pCsr->filter.zTerm); - sqlite3_free(pCsr->zStop); - sqlite3_free(pCsr->aStat); - sqlite3_free(pCsr); - return SQLITE_OK; -} - -static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){ - if( nSize>pCsr->nStat ){ - struct Fts3auxColstats *aNew; - aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat, - sizeof(struct Fts3auxColstats) * nSize - ); - if( aNew==0 ) return SQLITE_NOMEM; - memset(&aNew[pCsr->nStat], 0, - sizeof(struct Fts3auxColstats) * (nSize - pCsr->nStat) - ); - pCsr->aStat = aNew; - pCsr->nStat = nSize; - } - return SQLITE_OK; -} - -/* -** xNext - Advance the cursor to the next row, if any. -*/ -static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ - Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; - Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; - int rc; - - /* Increment our pretend rowid value. */ - pCsr->iRowid++; - - for(pCsr->iCol++; pCsr->iColnStat; pCsr->iCol++){ - if( pCsr->aStat[pCsr->iCol].nDoc>0 ) return SQLITE_OK; - } - - rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr); - if( rc==SQLITE_ROW ){ - int i = 0; - int nDoclist = pCsr->csr.nDoclist; - char *aDoclist = pCsr->csr.aDoclist; - int iCol; - - int eState = 0; - - if( pCsr->zStop ){ - int n = (pCsr->nStopcsr.nTerm) ? pCsr->nStop : pCsr->csr.nTerm; - int mc = memcmp(pCsr->zStop, pCsr->csr.zTerm, n); - if( mc<0 || (mc==0 && pCsr->csr.nTerm>pCsr->nStop) ){ - pCsr->isEof = 1; - return SQLITE_OK; - } - } - - if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM; - memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat); - iCol = 0; - - while( iaStat[0].nDoc++; - eState = 1; - iCol = 0; - break; - - /* State 1. In this state we are expecting either a 1, indicating - ** that the following integer will be a column number, or the - ** start of a position list for column 0. - ** - ** The only difference between state 1 and state 2 is that if the - ** integer encountered in state 1 is not 0 or 1, then we need to - ** increment the column 0 "nDoc" count for this term. - */ - case 1: - assert( iCol==0 ); - if( v>1 ){ - pCsr->aStat[1].nDoc++; - } - eState = 2; - /* fall through */ - - case 2: - if( v==0 ){ /* 0x00. Next integer will be a docid. */ - eState = 0; - }else if( v==1 ){ /* 0x01. Next integer will be a column number. */ - eState = 3; - }else{ /* 2 or greater. A position. */ - pCsr->aStat[iCol+1].nOcc++; - pCsr->aStat[0].nOcc++; - } - break; - - /* State 3. The integer just read is a column number. */ - default: assert( eState==3 ); - iCol = (int)v; - if( fts3auxGrowStatArray(pCsr, iCol+2) ) return SQLITE_NOMEM; - pCsr->aStat[iCol+1].nDoc++; - eState = 2; - break; - } - } - - pCsr->iCol = 0; - rc = SQLITE_OK; - }else{ - pCsr->isEof = 1; - } - return rc; -} - -/* -** xFilter - Initialize a cursor to point at the start of its data. -*/ -static int fts3auxFilterMethod( - sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ - int idxNum, /* Strategy index */ - const char *idxStr, /* Unused */ - int nVal, /* Number of elements in apVal */ - sqlite3_value **apVal /* Arguments for the indexing scheme */ -){ - Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; - Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; - int rc; - int isScan; - - UNUSED_PARAMETER(nVal); - UNUSED_PARAMETER(idxStr); - - assert( idxStr==0 ); - assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0 - || idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT - || idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) - ); - isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT); - - /* In case this cursor is being reused, close and zero it. */ - testcase(pCsr->filter.zTerm); - sqlite3Fts3SegReaderFinish(&pCsr->csr); - sqlite3_free((void *)pCsr->filter.zTerm); - sqlite3_free(pCsr->aStat); - memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); - - pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; - if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN; - - if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){ - const unsigned char *zStr = sqlite3_value_text(apVal[0]); - if( zStr ){ - pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr); - pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]); - if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM; - } - } - if( idxNum&FTS4AUX_LE_CONSTRAINT ){ - int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0; - pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx])); - pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]); - if( pCsr->zStop==0 ) return SQLITE_NOMEM; - } - - rc = sqlite3Fts3SegReaderCursor(pFts3, 0, 0, FTS3_SEGCURSOR_ALL, - pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr - ); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter); - } - - if( rc==SQLITE_OK ) rc = fts3auxNextMethod(pCursor); - return rc; -} - -/* -** xEof - Return true if the cursor is at EOF, or false otherwise. -*/ -static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){ - Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; - return pCsr->isEof; -} - -/* -** xColumn - Return a column value. -*/ -static int fts3auxColumnMethod( - sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ - sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */ - int iCol /* Index of column to read value from */ -){ - Fts3auxCursor *p = (Fts3auxCursor *)pCursor; - - assert( p->isEof==0 ); - if( iCol==0 ){ /* Column "term" */ - sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT); - }else if( iCol==1 ){ /* Column "col" */ - if( p->iCol ){ - sqlite3_result_int(pContext, p->iCol-1); - }else{ - sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC); - } - }else if( iCol==2 ){ /* Column "documents" */ - sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc); - }else{ /* Column "occurrences" */ - sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc); - } - - return SQLITE_OK; -} - -/* -** xRowid - Return the current rowid for the cursor. -*/ -static int fts3auxRowidMethod( - sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ - sqlite_int64 *pRowid /* OUT: Rowid value */ -){ - Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; - *pRowid = pCsr->iRowid; - return SQLITE_OK; -} - -/* -** Register the fts3aux module with database connection db. Return SQLITE_OK -** if successful or an error code if sqlite3_create_module() fails. -*/ -SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){ - static const sqlite3_module fts3aux_module = { - 0, /* iVersion */ - fts3auxConnectMethod, /* xCreate */ - fts3auxConnectMethod, /* xConnect */ - fts3auxBestIndexMethod, /* xBestIndex */ - fts3auxDisconnectMethod, /* xDisconnect */ - fts3auxDisconnectMethod, /* xDestroy */ - fts3auxOpenMethod, /* xOpen */ - fts3auxCloseMethod, /* xClose */ - fts3auxFilterMethod, /* xFilter */ - fts3auxNextMethod, /* xNext */ - fts3auxEofMethod, /* xEof */ - fts3auxColumnMethod, /* xColumn */ - fts3auxRowidMethod, /* xRowid */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindFunction */ - 0, /* xRename */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0 /* xRollbackTo */ - }; - int rc; /* Return code */ - - rc = sqlite3_create_module(db, "fts4aux", &fts3aux_module, 0); - return rc; -} - -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - -/************** End of fts3_aux.c ********************************************/ -/************** Begin file fts3_expr.c ***************************************/ -/* -** 2008 Nov 28 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This module contains code that implements a parser for fts3 query strings -** (the right-hand argument to the MATCH operator). Because the supported -** syntax is relatively simple, the whole tokenizer/parser system is -** hand-coded. -*/ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* -** By default, this module parses the legacy syntax that has been -** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS -** is defined, then it uses the new syntax. The differences between -** the new and the old syntaxes are: -** -** a) The new syntax supports parenthesis. The old does not. -** -** b) The new syntax supports the AND and NOT operators. The old does not. -** -** c) The old syntax supports the "-" token qualifier. This is not -** supported by the new syntax (it is replaced by the NOT operator). -** -** d) When using the old syntax, the OR operator has a greater precedence -** than an implicit AND. When using the new, both implicity and explicit -** AND operators have a higher precedence than OR. -** -** If compiled with SQLITE_TEST defined, then this module exports the -** symbol "int sqlite3_fts3_enable_parentheses". Setting this variable -** to zero causes the module to use the old syntax. If it is set to -** non-zero the new syntax is activated. This is so both syntaxes can -** be tested using a single build of testfixture. -** -** The following describes the syntax supported by the fts3 MATCH -** operator in a similar format to that used by the lemon parser -** generator. This module does not use actually lemon, it uses a -** custom parser. -** -** query ::= andexpr (OR andexpr)*. -** -** andexpr ::= notexpr (AND? notexpr)*. -** -** notexpr ::= nearexpr (NOT nearexpr|-TOKEN)*. -** notexpr ::= LP query RP. -** -** nearexpr ::= phrase (NEAR distance_opt nearexpr)*. -** -** distance_opt ::= . -** distance_opt ::= / INTEGER. -** -** phrase ::= TOKEN. -** phrase ::= COLUMN:TOKEN. -** phrase ::= "TOKEN TOKEN TOKEN...". -*/ - -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_fts3_enable_parentheses = 0; -#else -# ifdef SQLITE_ENABLE_FTS3_PARENTHESIS -# define sqlite3_fts3_enable_parentheses 1 -# else -# define sqlite3_fts3_enable_parentheses 0 -# endif -#endif - -/* -** Default span for NEAR operators. -*/ -#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10 - -/* #include */ -/* #include */ - -/* -** isNot: -** This variable is used by function getNextNode(). When getNextNode() is -** called, it sets ParseContext.isNot to true if the 'next node' is a -** FTSQUERY_PHRASE with a unary "-" attached to it. i.e. "mysql" in the -** FTS3 query "sqlite -mysql". Otherwise, ParseContext.isNot is set to -** zero. -*/ -typedef struct ParseContext ParseContext; -struct ParseContext { - sqlite3_tokenizer *pTokenizer; /* Tokenizer module */ - int iLangid; /* Language id used with tokenizer */ - const char **azCol; /* Array of column names for fts3 table */ - int bFts4; /* True to allow FTS4-only syntax */ - int nCol; /* Number of entries in azCol[] */ - int iDefaultCol; /* Default column to query */ - int isNot; /* True if getNextNode() sees a unary - */ - sqlite3_context *pCtx; /* Write error message here */ - int nNest; /* Number of nested brackets */ -}; - -/* -** This function is equivalent to the standard isspace() function. -** -** The standard isspace() can be awkward to use safely, because although it -** is defined to accept an argument of type int, its behavior when passed -** an integer that falls outside of the range of the unsigned char type -** is undefined (and sometimes, "undefined" means segfault). This wrapper -** is defined to accept an argument of type char, and always returns 0 for -** any values that fall outside of the range of the unsigned char type (i.e. -** negative values). -*/ -static int fts3isspace(char c){ - return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; -} - -/* -** Allocate nByte bytes of memory using sqlite3_malloc(). If successful, -** zero the memory before returning a pointer to it. If unsuccessful, -** return NULL. -*/ -static void *fts3MallocZero(int nByte){ - void *pRet = sqlite3_malloc(nByte); - if( pRet ) memset(pRet, 0, nByte); - return pRet; -} - -SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer( - sqlite3_tokenizer *pTokenizer, - int iLangid, - const char *z, - int n, - sqlite3_tokenizer_cursor **ppCsr -){ - sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; - sqlite3_tokenizer_cursor *pCsr = 0; - int rc; - - rc = pModule->xOpen(pTokenizer, z, n, &pCsr); - assert( rc==SQLITE_OK || pCsr==0 ); - if( rc==SQLITE_OK ){ - pCsr->pTokenizer = pTokenizer; - if( pModule->iVersion>=1 ){ - rc = pModule->xLanguageid(pCsr, iLangid); - if( rc!=SQLITE_OK ){ - pModule->xClose(pCsr); - pCsr = 0; - } - } - } - *ppCsr = pCsr; - return rc; -} - - -/* -** Extract the next token from buffer z (length n) using the tokenizer -** and other information (column names etc.) in pParse. Create an Fts3Expr -** structure of type FTSQUERY_PHRASE containing a phrase consisting of this -** single token and set *ppExpr to point to it. If the end of the buffer is -** reached before a token is found, set *ppExpr to zero. It is the -** responsibility of the caller to eventually deallocate the allocated -** Fts3Expr structure (if any) by passing it to sqlite3_free(). -** -** Return SQLITE_OK if successful, or SQLITE_NOMEM if a memory allocation -** fails. -*/ -static int getNextToken( - ParseContext *pParse, /* fts3 query parse context */ - int iCol, /* Value for Fts3Phrase.iColumn */ - const char *z, int n, /* Input string */ - Fts3Expr **ppExpr, /* OUT: expression */ - int *pnConsumed /* OUT: Number of bytes consumed */ -){ - sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; - sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; - int rc; - sqlite3_tokenizer_cursor *pCursor; - Fts3Expr *pRet = 0; - int nConsumed = 0; - - rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor); - if( rc==SQLITE_OK ){ - const char *zToken; - int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; - int nByte; /* total space to allocate */ - - rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); - if( rc==SQLITE_OK ){ - nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; - pRet = (Fts3Expr *)fts3MallocZero(nByte); - if( !pRet ){ - rc = SQLITE_NOMEM; - }else{ - pRet->eType = FTSQUERY_PHRASE; - pRet->pPhrase = (Fts3Phrase *)&pRet[1]; - pRet->pPhrase->nToken = 1; - pRet->pPhrase->iColumn = iCol; - pRet->pPhrase->aToken[0].n = nToken; - pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; - memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); - - if( iEndpPhrase->aToken[0].isPrefix = 1; - iEnd++; - } - - while( 1 ){ - if( !sqlite3_fts3_enable_parentheses - && iStart>0 && z[iStart-1]=='-' - ){ - pParse->isNot = 1; - iStart--; - }else if( pParse->bFts4 && iStart>0 && z[iStart-1]=='^' ){ - pRet->pPhrase->aToken[0].bFirst = 1; - iStart--; - }else{ - break; - } - } - - } - nConsumed = iEnd; - } - - pModule->xClose(pCursor); - } - - *pnConsumed = nConsumed; - *ppExpr = pRet; - return rc; -} - - -/* -** Enlarge a memory allocation. If an out-of-memory allocation occurs, -** then free the old allocation. -*/ -static void *fts3ReallocOrFree(void *pOrig, int nNew){ - void *pRet = sqlite3_realloc(pOrig, nNew); - if( !pRet ){ - sqlite3_free(pOrig); - } - return pRet; -} - -/* -** Buffer zInput, length nInput, contains the contents of a quoted string -** that appeared as part of an fts3 query expression. Neither quote character -** is included in the buffer. This function attempts to tokenize the entire -** input buffer and create an Fts3Expr structure of type FTSQUERY_PHRASE -** containing the results. -** -** If successful, SQLITE_OK is returned and *ppExpr set to point at the -** allocated Fts3Expr structure. Otherwise, either SQLITE_NOMEM (out of memory -** error) or SQLITE_ERROR (tokenization error) is returned and *ppExpr set -** to 0. -*/ -static int getNextString( - ParseContext *pParse, /* fts3 query parse context */ - const char *zInput, int nInput, /* Input string */ - Fts3Expr **ppExpr /* OUT: expression */ -){ - sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; - sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; - int rc; - Fts3Expr *p = 0; - sqlite3_tokenizer_cursor *pCursor = 0; - char *zTemp = 0; - int nTemp = 0; - - const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase); - int nToken = 0; - - /* The final Fts3Expr data structure, including the Fts3Phrase, - ** Fts3PhraseToken structures token buffers are all stored as a single - ** allocation so that the expression can be freed with a single call to - ** sqlite3_free(). Setting this up requires a two pass approach. - ** - ** The first pass, in the block below, uses a tokenizer cursor to iterate - ** through the tokens in the expression. This pass uses fts3ReallocOrFree() - ** to assemble data in two dynamic buffers: - ** - ** Buffer p: Points to the Fts3Expr structure, followed by the Fts3Phrase - ** structure, followed by the array of Fts3PhraseToken - ** structures. This pass only populates the Fts3PhraseToken array. - ** - ** Buffer zTemp: Contains copies of all tokens. - ** - ** The second pass, in the block that begins "if( rc==SQLITE_DONE )" below, - ** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase - ** structures. - */ - rc = sqlite3Fts3OpenTokenizer( - pTokenizer, pParse->iLangid, zInput, nInput, &pCursor); - if( rc==SQLITE_OK ){ - int ii; - for(ii=0; rc==SQLITE_OK; ii++){ - const char *zByte; - int nByte = 0, iBegin = 0, iEnd = 0, iPos = 0; - rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos); - if( rc==SQLITE_OK ){ - Fts3PhraseToken *pToken; - - p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); - if( !p ) goto no_mem; - - zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); - if( !zTemp ) goto no_mem; - - assert( nToken==ii ); - pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; - memset(pToken, 0, sizeof(Fts3PhraseToken)); - - memcpy(&zTemp[nTemp], zByte, nByte); - nTemp += nByte; - - pToken->n = nByte; - pToken->isPrefix = (iEndbFirst = (iBegin>0 && zInput[iBegin-1]=='^'); - nToken = ii+1; - } - } - - pModule->xClose(pCursor); - pCursor = 0; - } - - if( rc==SQLITE_DONE ){ - int jj; - char *zBuf = 0; - - p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); - if( !p ) goto no_mem; - memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); - p->eType = FTSQUERY_PHRASE; - p->pPhrase = (Fts3Phrase *)&p[1]; - p->pPhrase->iColumn = pParse->iDefaultCol; - p->pPhrase->nToken = nToken; - - zBuf = (char *)&p->pPhrase->aToken[nToken]; - if( zTemp ){ - memcpy(zBuf, zTemp, nTemp); - sqlite3_free(zTemp); - }else{ - assert( nTemp==0 ); - } - - for(jj=0; jjpPhrase->nToken; jj++){ - p->pPhrase->aToken[jj].z = zBuf; - zBuf += p->pPhrase->aToken[jj].n; - } - rc = SQLITE_OK; - } - - *ppExpr = p; - return rc; -no_mem: - - if( pCursor ){ - pModule->xClose(pCursor); - } - sqlite3_free(zTemp); - sqlite3_free(p); - *ppExpr = 0; - return SQLITE_NOMEM; -} - -/* -** Function getNextNode(), which is called by fts3ExprParse(), may itself -** call fts3ExprParse(). So this forward declaration is required. -*/ -static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); - -/* -** The output variable *ppExpr is populated with an allocated Fts3Expr -** structure, or set to 0 if the end of the input buffer is reached. -** -** Returns an SQLite error code. SQLITE_OK if everything works, SQLITE_NOMEM -** if a malloc failure occurs, or SQLITE_ERROR if a parse error is encountered. -** If SQLITE_ERROR is returned, pContext is populated with an error message. -*/ -static int getNextNode( - ParseContext *pParse, /* fts3 query parse context */ - const char *z, int n, /* Input string */ - Fts3Expr **ppExpr, /* OUT: expression */ - int *pnConsumed /* OUT: Number of bytes consumed */ -){ - static const struct Fts3Keyword { - char *z; /* Keyword text */ - unsigned char n; /* Length of the keyword */ - unsigned char parenOnly; /* Only valid in paren mode */ - unsigned char eType; /* Keyword code */ - } aKeyword[] = { - { "OR" , 2, 0, FTSQUERY_OR }, - { "AND", 3, 1, FTSQUERY_AND }, - { "NOT", 3, 1, FTSQUERY_NOT }, - { "NEAR", 4, 0, FTSQUERY_NEAR } - }; - int ii; - int iCol; - int iColLen; - int rc; - Fts3Expr *pRet = 0; - - const char *zInput = z; - int nInput = n; - - pParse->isNot = 0; - - /* Skip over any whitespace before checking for a keyword, an open or - ** close bracket, or a quoted string. - */ - while( nInput>0 && fts3isspace(*zInput) ){ - nInput--; - zInput++; - } - if( nInput==0 ){ - return SQLITE_DONE; - } - - /* See if we are dealing with a keyword. */ - for(ii=0; ii<(int)(sizeof(aKeyword)/sizeof(struct Fts3Keyword)); ii++){ - const struct Fts3Keyword *pKey = &aKeyword[ii]; - - if( (pKey->parenOnly & ~sqlite3_fts3_enable_parentheses)!=0 ){ - continue; - } - - if( nInput>=pKey->n && 0==memcmp(zInput, pKey->z, pKey->n) ){ - int nNear = SQLITE_FTS3_DEFAULT_NEAR_PARAM; - int nKey = pKey->n; - char cNext; - - /* If this is a "NEAR" keyword, check for an explicit nearness. */ - if( pKey->eType==FTSQUERY_NEAR ){ - assert( nKey==4 ); - if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){ - nNear = 0; - for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){ - nNear = nNear * 10 + (zInput[nKey] - '0'); - } - } - } - - /* At this point this is probably a keyword. But for that to be true, - ** the next byte must contain either whitespace, an open or close - ** parenthesis, a quote character, or EOF. - */ - cNext = zInput[nKey]; - if( fts3isspace(cNext) - || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 - ){ - pRet = (Fts3Expr *)fts3MallocZero(sizeof(Fts3Expr)); - if( !pRet ){ - return SQLITE_NOMEM; - } - pRet->eType = pKey->eType; - pRet->nNear = nNear; - *ppExpr = pRet; - *pnConsumed = (int)((zInput - z) + nKey); - return SQLITE_OK; - } - - /* Turns out that wasn't a keyword after all. This happens if the - ** user has supplied a token such as "ORacle". Continue. - */ - } - } - - /* Check for an open bracket. */ - if( sqlite3_fts3_enable_parentheses ){ - if( *zInput=='(' ){ - int nConsumed; - pParse->nNest++; - rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed); - if( rc==SQLITE_OK && !*ppExpr ){ - rc = SQLITE_DONE; - } - *pnConsumed = (int)((zInput - z) + 1 + nConsumed); - return rc; - } - - /* Check for a close bracket. */ - if( *zInput==')' ){ - pParse->nNest--; - *pnConsumed = (int)((zInput - z) + 1); - return SQLITE_DONE; - } - } - - /* See if we are dealing with a quoted phrase. If this is the case, then - ** search for the closing quote and pass the whole string to getNextString() - ** for processing. This is easy to do, as fts3 has no syntax for escaping - ** a quote character embedded in a string. - */ - if( *zInput=='"' ){ - for(ii=1; iiiDefaultCol; - iColLen = 0; - for(ii=0; iinCol; ii++){ - const char *zStr = pParse->azCol[ii]; - int nStr = (int)strlen(zStr); - if( nInput>nStr && zInput[nStr]==':' - && sqlite3_strnicmp(zStr, zInput, nStr)==0 - ){ - iCol = ii; - iColLen = (int)((zInput - z) + nStr + 1); - break; - } - } - rc = getNextToken(pParse, iCol, &z[iColLen], n-iColLen, ppExpr, pnConsumed); - *pnConsumed += iColLen; - return rc; -} - -/* -** The argument is an Fts3Expr structure for a binary operator (any type -** except an FTSQUERY_PHRASE). Return an integer value representing the -** precedence of the operator. Lower values have a higher precedence (i.e. -** group more tightly). For example, in the C language, the == operator -** groups more tightly than ||, and would therefore have a higher precedence. -** -** When using the new fts3 query syntax (when SQLITE_ENABLE_FTS3_PARENTHESIS -** is defined), the order of the operators in precedence from highest to -** lowest is: -** -** NEAR -** NOT -** AND (including implicit ANDs) -** OR -** -** Note that when using the old query syntax, the OR operator has a higher -** precedence than the AND operator. -*/ -static int opPrecedence(Fts3Expr *p){ - assert( p->eType!=FTSQUERY_PHRASE ); - if( sqlite3_fts3_enable_parentheses ){ - return p->eType; - }else if( p->eType==FTSQUERY_NEAR ){ - return 1; - }else if( p->eType==FTSQUERY_OR ){ - return 2; - } - assert( p->eType==FTSQUERY_AND ); - return 3; -} - -/* -** Argument ppHead contains a pointer to the current head of a query -** expression tree being parsed. pPrev is the expression node most recently -** inserted into the tree. This function adds pNew, which is always a binary -** operator node, into the expression tree based on the relative precedence -** of pNew and the existing nodes of the tree. This may result in the head -** of the tree changing, in which case *ppHead is set to the new root node. -*/ -static void insertBinaryOperator( - Fts3Expr **ppHead, /* Pointer to the root node of a tree */ - Fts3Expr *pPrev, /* Node most recently inserted into the tree */ - Fts3Expr *pNew /* New binary node to insert into expression tree */ -){ - Fts3Expr *pSplit = pPrev; - while( pSplit->pParent && opPrecedence(pSplit->pParent)<=opPrecedence(pNew) ){ - pSplit = pSplit->pParent; - } - - if( pSplit->pParent ){ - assert( pSplit->pParent->pRight==pSplit ); - pSplit->pParent->pRight = pNew; - pNew->pParent = pSplit->pParent; - }else{ - *ppHead = pNew; - } - pNew->pLeft = pSplit; - pSplit->pParent = pNew; -} - -/* -** Parse the fts3 query expression found in buffer z, length n. This function -** returns either when the end of the buffer is reached or an unmatched -** closing bracket - ')' - is encountered. -** -** If successful, SQLITE_OK is returned, *ppExpr is set to point to the -** parsed form of the expression and *pnConsumed is set to the number of -** bytes read from buffer z. Otherwise, *ppExpr is set to 0 and SQLITE_NOMEM -** (out of memory error) or SQLITE_ERROR (parse error) is returned. -*/ -static int fts3ExprParse( - ParseContext *pParse, /* fts3 query parse context */ - const char *z, int n, /* Text of MATCH query */ - Fts3Expr **ppExpr, /* OUT: Parsed query structure */ - int *pnConsumed /* OUT: Number of bytes consumed */ -){ - Fts3Expr *pRet = 0; - Fts3Expr *pPrev = 0; - Fts3Expr *pNotBranch = 0; /* Only used in legacy parse mode */ - int nIn = n; - const char *zIn = z; - int rc = SQLITE_OK; - int isRequirePhrase = 1; - - while( rc==SQLITE_OK ){ - Fts3Expr *p = 0; - int nByte = 0; - rc = getNextNode(pParse, zIn, nIn, &p, &nByte); - if( rc==SQLITE_OK ){ - int isPhrase; - - if( !sqlite3_fts3_enable_parentheses - && p->eType==FTSQUERY_PHRASE && pParse->isNot - ){ - /* Create an implicit NOT operator. */ - Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr)); - if( !pNot ){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_NOMEM; - goto exprparse_out; - } - pNot->eType = FTSQUERY_NOT; - pNot->pRight = p; - if( pNotBranch ){ - pNot->pLeft = pNotBranch; - } - pNotBranch = pNot; - p = pPrev; - }else{ - int eType = p->eType; - isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); - - /* The isRequirePhrase variable is set to true if a phrase or - ** an expression contained in parenthesis is required. If a - ** binary operator (AND, OR, NOT or NEAR) is encounted when - ** isRequirePhrase is set, this is a syntax error. - */ - if( !isPhrase && isRequirePhrase ){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_ERROR; - goto exprparse_out; - } - - if( isPhrase && !isRequirePhrase ){ - /* Insert an implicit AND operator. */ - Fts3Expr *pAnd; - assert( pRet && pPrev ); - pAnd = fts3MallocZero(sizeof(Fts3Expr)); - if( !pAnd ){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_NOMEM; - goto exprparse_out; - } - pAnd->eType = FTSQUERY_AND; - insertBinaryOperator(&pRet, pPrev, pAnd); - pPrev = pAnd; - } - - /* This test catches attempts to make either operand of a NEAR - ** operator something other than a phrase. For example, either of - ** the following: - ** - ** (bracketed expression) NEAR phrase - ** phrase NEAR (bracketed expression) - ** - ** Return an error in either case. - */ - if( pPrev && ( - (eType==FTSQUERY_NEAR && !isPhrase && pPrev->eType!=FTSQUERY_PHRASE) - || (eType!=FTSQUERY_PHRASE && isPhrase && pPrev->eType==FTSQUERY_NEAR) - )){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_ERROR; - goto exprparse_out; - } - - if( isPhrase ){ - if( pRet ){ - assert( pPrev && pPrev->pLeft && pPrev->pRight==0 ); - pPrev->pRight = p; - p->pParent = pPrev; - }else{ - pRet = p; - } - }else{ - insertBinaryOperator(&pRet, pPrev, p); - } - isRequirePhrase = !isPhrase; - } - assert( nByte>0 ); - } - assert( rc!=SQLITE_OK || (nByte>0 && nByte<=nIn) ); - nIn -= nByte; - zIn += nByte; - pPrev = p; - } - - if( rc==SQLITE_DONE && pRet && isRequirePhrase ){ - rc = SQLITE_ERROR; - } - - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - if( !sqlite3_fts3_enable_parentheses && pNotBranch ){ - if( !pRet ){ - rc = SQLITE_ERROR; - }else{ - Fts3Expr *pIter = pNotBranch; - while( pIter->pLeft ){ - pIter = pIter->pLeft; - } - pIter->pLeft = pRet; - pRet = pNotBranch; - } - } - } - *pnConsumed = n - nIn; - -exprparse_out: - if( rc!=SQLITE_OK ){ - sqlite3Fts3ExprFree(pRet); - sqlite3Fts3ExprFree(pNotBranch); - pRet = 0; - } - *ppExpr = pRet; - return rc; -} - -/* -** Parameters z and n contain a pointer to and length of a buffer containing -** an fts3 query expression, respectively. This function attempts to parse the -** query expression and create a tree of Fts3Expr structures representing the -** parsed expression. If successful, *ppExpr is set to point to the head -** of the parsed expression tree and SQLITE_OK is returned. If an error -** occurs, either SQLITE_NOMEM (out-of-memory error) or SQLITE_ERROR (parse -** error) is returned and *ppExpr is set to 0. -** -** If parameter n is a negative number, then z is assumed to point to a -** nul-terminated string and the length is determined using strlen(). -** -** The first parameter, pTokenizer, is passed the fts3 tokenizer module to -** use to normalize query tokens while parsing the expression. The azCol[] -** array, which is assumed to contain nCol entries, should contain the names -** of each column in the target fts3 table, in order from left to right. -** Column names must be nul-terminated strings. -** -** The iDefaultCol parameter should be passed the index of the table column -** that appears on the left-hand-side of the MATCH operator (the default -** column to match against for tokens for which a column name is not explicitly -** specified as part of the query string), or -1 if tokens may by default -** match any table column. -*/ -SQLITE_PRIVATE int sqlite3Fts3ExprParse( - sqlite3_tokenizer *pTokenizer, /* Tokenizer module */ - int iLangid, /* Language id for tokenizer */ - char **azCol, /* Array of column names for fts3 table */ - int bFts4, /* True to allow FTS4-only syntax */ - int nCol, /* Number of entries in azCol[] */ - int iDefaultCol, /* Default column to query */ - const char *z, int n, /* Text of MATCH query */ - Fts3Expr **ppExpr /* OUT: Parsed query structure */ -){ - int nParsed; - int rc; - ParseContext sParse; - - memset(&sParse, 0, sizeof(ParseContext)); - sParse.pTokenizer = pTokenizer; - sParse.iLangid = iLangid; - sParse.azCol = (const char **)azCol; - sParse.nCol = nCol; - sParse.iDefaultCol = iDefaultCol; - sParse.bFts4 = bFts4; - if( z==0 ){ - *ppExpr = 0; - return SQLITE_OK; - } - if( n<0 ){ - n = (int)strlen(z); - } - rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed); - - /* Check for mismatched parenthesis */ - if( rc==SQLITE_OK && sParse.nNest ){ - rc = SQLITE_ERROR; - sqlite3Fts3ExprFree(*ppExpr); - *ppExpr = 0; - } - - return rc; -} - -/* -** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse(). -*/ -SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *p){ - if( p ){ - assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 ); - sqlite3Fts3ExprFree(p->pLeft); - sqlite3Fts3ExprFree(p->pRight); - sqlite3Fts3EvalPhraseCleanup(p->pPhrase); - sqlite3_free(p->aMI); - sqlite3_free(p); - } -} - -/**************************************************************************** -***************************************************************************** -** Everything after this point is just test code. -*/ - -#ifdef SQLITE_TEST - -/* #include */ - -/* -** Function to query the hash-table of tokenizers (see README.tokenizers). -*/ -static int queryTestTokenizer( - sqlite3 *db, - const char *zName, - const sqlite3_tokenizer_module **pp -){ - int rc; - sqlite3_stmt *pStmt; - const char zSql[] = "SELECT fts3_tokenizer(?)"; - - *pp = 0; - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - - sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC); - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){ - memcpy((void *)pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp)); - } - } - - return sqlite3_finalize(pStmt); -} - -/* -** Return a pointer to a buffer containing a text representation of the -** expression passed as the first argument. The buffer is obtained from -** sqlite3_malloc(). It is the responsibility of the caller to use -** sqlite3_free() to release the memory. If an OOM condition is encountered, -** NULL is returned. -** -** If the second argument is not NULL, then its contents are prepended to -** the returned expression text and then freed using sqlite3_free(). -*/ -static char *exprToString(Fts3Expr *pExpr, char *zBuf){ - switch( pExpr->eType ){ - case FTSQUERY_PHRASE: { - Fts3Phrase *pPhrase = pExpr->pPhrase; - int i; - zBuf = sqlite3_mprintf( - "%zPHRASE %d 0", zBuf, pPhrase->iColumn); - for(i=0; zBuf && inToken; i++){ - zBuf = sqlite3_mprintf("%z %.*s%s", zBuf, - pPhrase->aToken[i].n, pPhrase->aToken[i].z, - (pPhrase->aToken[i].isPrefix?"+":"") - ); - } - return zBuf; - } - - case FTSQUERY_NEAR: - zBuf = sqlite3_mprintf("%zNEAR/%d ", zBuf, pExpr->nNear); - break; - case FTSQUERY_NOT: - zBuf = sqlite3_mprintf("%zNOT ", zBuf); - break; - case FTSQUERY_AND: - zBuf = sqlite3_mprintf("%zAND ", zBuf); - break; - case FTSQUERY_OR: - zBuf = sqlite3_mprintf("%zOR ", zBuf); - break; - } - - if( zBuf ) zBuf = sqlite3_mprintf("%z{", zBuf); - if( zBuf ) zBuf = exprToString(pExpr->pLeft, zBuf); - if( zBuf ) zBuf = sqlite3_mprintf("%z} {", zBuf); - - if( zBuf ) zBuf = exprToString(pExpr->pRight, zBuf); - if( zBuf ) zBuf = sqlite3_mprintf("%z}", zBuf); - - return zBuf; -} - -/* -** This is the implementation of a scalar SQL function used to test the -** expression parser. It should be called as follows: -** -** fts3_exprtest(, , , ...); -** -** The first argument, , is the name of the fts3 tokenizer used -** to parse the query expression (see README.tokenizers). The second argument -** is the query expression to parse. Each subsequent argument is the name -** of a column of the fts3 table that the query expression may refer to. -** For example: -** -** SELECT fts3_exprtest('simple', 'Bill col2:Bloggs', 'col1', 'col2'); -*/ -static void fts3ExprTest( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - sqlite3_tokenizer_module const *pModule = 0; - sqlite3_tokenizer *pTokenizer = 0; - int rc; - char **azCol = 0; - const char *zExpr; - int nExpr; - int nCol; - int ii; - Fts3Expr *pExpr; - char *zBuf = 0; - sqlite3 *db = sqlite3_context_db_handle(context); - - if( argc<3 ){ - sqlite3_result_error(context, - "Usage: fts3_exprtest(tokenizer, expr, col1, ...", -1 - ); - return; - } - - rc = queryTestTokenizer(db, - (const char *)sqlite3_value_text(argv[0]), &pModule); - if( rc==SQLITE_NOMEM ){ - sqlite3_result_error_nomem(context); - goto exprtest_out; - }else if( !pModule ){ - sqlite3_result_error(context, "No such tokenizer module", -1); - goto exprtest_out; - } - - rc = pModule->xCreate(0, 0, &pTokenizer); - assert( rc==SQLITE_NOMEM || rc==SQLITE_OK ); - if( rc==SQLITE_NOMEM ){ - sqlite3_result_error_nomem(context); - goto exprtest_out; - } - pTokenizer->pModule = pModule; - - zExpr = (const char *)sqlite3_value_text(argv[1]); - nExpr = sqlite3_value_bytes(argv[1]); - nCol = argc-2; - azCol = (char **)sqlite3_malloc(nCol*sizeof(char *)); - if( !azCol ){ - sqlite3_result_error_nomem(context); - goto exprtest_out; - } - for(ii=0; iixDestroy(pTokenizer); - } - sqlite3_free(azCol); -} - -/* -** Register the query expression parser test function fts3_exprtest() -** with database connection db. -*/ -SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3* db){ - return sqlite3_create_function( - db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0 - ); -} - -#endif -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - -/************** End of fts3_expr.c *******************************************/ -/************** Begin file fts3_hash.c ***************************************/ -/* -** 2001 September 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This is the implementation of generic hash-tables used in SQLite. -** We've modified it slightly to serve as a standalone hash table -** implementation for the full-text indexing module. -*/ - -/* -** The code in this file is only compiled if: -** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or -** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). -*/ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* #include */ -/* #include */ -/* #include */ - - -/* -** Malloc and Free functions -*/ -static void *fts3HashMalloc(int n){ - void *p = sqlite3_malloc(n); - if( p ){ - memset(p, 0, n); - } - return p; -} -static void fts3HashFree(void *p){ - sqlite3_free(p); -} - -/* Turn bulk memory into a hash table object by initializing the -** fields of the Hash structure. -** -** "pNew" is a pointer to the hash table that is to be initialized. -** keyClass is one of the constants -** FTS3_HASH_BINARY or FTS3_HASH_STRING. The value of keyClass -** determines what kind of key the hash table will use. "copyKey" is -** true if the hash table should make its own private copy of keys and -** false if it should just use the supplied pointer. -*/ -SQLITE_PRIVATE void sqlite3Fts3HashInit(Fts3Hash *pNew, char keyClass, char copyKey){ - assert( pNew!=0 ); - assert( keyClass>=FTS3_HASH_STRING && keyClass<=FTS3_HASH_BINARY ); - pNew->keyClass = keyClass; - pNew->copyKey = copyKey; - pNew->first = 0; - pNew->count = 0; - pNew->htsize = 0; - pNew->ht = 0; -} - -/* Remove all entries from a hash table. Reclaim all memory. -** Call this routine to delete a hash table or to reset a hash table -** to the empty state. -*/ -SQLITE_PRIVATE void sqlite3Fts3HashClear(Fts3Hash *pH){ - Fts3HashElem *elem; /* For looping over all elements of the table */ - - assert( pH!=0 ); - elem = pH->first; - pH->first = 0; - fts3HashFree(pH->ht); - pH->ht = 0; - pH->htsize = 0; - while( elem ){ - Fts3HashElem *next_elem = elem->next; - if( pH->copyKey && elem->pKey ){ - fts3HashFree(elem->pKey); - } - fts3HashFree(elem); - elem = next_elem; - } - pH->count = 0; -} - -/* -** Hash and comparison functions when the mode is FTS3_HASH_STRING -*/ -static int fts3StrHash(const void *pKey, int nKey){ - const char *z = (const char *)pKey; - int h = 0; - if( nKey<=0 ) nKey = (int) strlen(z); - while( nKey > 0 ){ - h = (h<<3) ^ h ^ *z++; - nKey--; - } - return h & 0x7fffffff; -} -static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){ - if( n1!=n2 ) return 1; - return strncmp((const char*)pKey1,(const char*)pKey2,n1); -} - -/* -** Hash and comparison functions when the mode is FTS3_HASH_BINARY -*/ -static int fts3BinHash(const void *pKey, int nKey){ - int h = 0; - const char *z = (const char *)pKey; - while( nKey-- > 0 ){ - h = (h<<3) ^ h ^ *(z++); - } - return h & 0x7fffffff; -} -static int fts3BinCompare(const void *pKey1, int n1, const void *pKey2, int n2){ - if( n1!=n2 ) return 1; - return memcmp(pKey1,pKey2,n1); -} - -/* -** Return a pointer to the appropriate hash function given the key class. -** -** The C syntax in this function definition may be unfamilar to some -** programmers, so we provide the following additional explanation: -** -** The name of the function is "ftsHashFunction". The function takes a -** single parameter "keyClass". The return value of ftsHashFunction() -** is a pointer to another function. Specifically, the return value -** of ftsHashFunction() is a pointer to a function that takes two parameters -** with types "const void*" and "int" and returns an "int". -*/ -static int (*ftsHashFunction(int keyClass))(const void*,int){ - if( keyClass==FTS3_HASH_STRING ){ - return &fts3StrHash; - }else{ - assert( keyClass==FTS3_HASH_BINARY ); - return &fts3BinHash; - } -} - -/* -** Return a pointer to the appropriate hash function given the key class. -** -** For help in interpreted the obscure C code in the function definition, -** see the header comment on the previous function. -*/ -static int (*ftsCompareFunction(int keyClass))(const void*,int,const void*,int){ - if( keyClass==FTS3_HASH_STRING ){ - return &fts3StrCompare; - }else{ - assert( keyClass==FTS3_HASH_BINARY ); - return &fts3BinCompare; - } -} - -/* Link an element into the hash table -*/ -static void fts3HashInsertElement( - Fts3Hash *pH, /* The complete hash table */ - struct _fts3ht *pEntry, /* The entry into which pNew is inserted */ - Fts3HashElem *pNew /* The element to be inserted */ -){ - Fts3HashElem *pHead; /* First element already in pEntry */ - pHead = pEntry->chain; - if( pHead ){ - pNew->next = pHead; - pNew->prev = pHead->prev; - if( pHead->prev ){ pHead->prev->next = pNew; } - else { pH->first = pNew; } - pHead->prev = pNew; - }else{ - pNew->next = pH->first; - if( pH->first ){ pH->first->prev = pNew; } - pNew->prev = 0; - pH->first = pNew; - } - pEntry->count++; - pEntry->chain = pNew; -} - - -/* Resize the hash table so that it cantains "new_size" buckets. -** "new_size" must be a power of 2. The hash table might fail -** to resize if sqliteMalloc() fails. -** -** Return non-zero if a memory allocation error occurs. -*/ -static int fts3Rehash(Fts3Hash *pH, int new_size){ - struct _fts3ht *new_ht; /* The new hash table */ - Fts3HashElem *elem, *next_elem; /* For looping over existing elements */ - int (*xHash)(const void*,int); /* The hash function */ - - assert( (new_size & (new_size-1))==0 ); - new_ht = (struct _fts3ht *)fts3HashMalloc( new_size*sizeof(struct _fts3ht) ); - if( new_ht==0 ) return 1; - fts3HashFree(pH->ht); - pH->ht = new_ht; - pH->htsize = new_size; - xHash = ftsHashFunction(pH->keyClass); - for(elem=pH->first, pH->first=0; elem; elem = next_elem){ - int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1); - next_elem = elem->next; - fts3HashInsertElement(pH, &new_ht[h], elem); - } - return 0; -} - -/* This function (for internal use only) locates an element in an -** hash table that matches the given key. The hash for this key has -** already been computed and is passed as the 4th parameter. -*/ -static Fts3HashElem *fts3FindElementByHash( - const Fts3Hash *pH, /* The pH to be searched */ - const void *pKey, /* The key we are searching for */ - int nKey, - int h /* The hash for this key. */ -){ - Fts3HashElem *elem; /* Used to loop thru the element list */ - int count; /* Number of elements left to test */ - int (*xCompare)(const void*,int,const void*,int); /* comparison function */ - - if( pH->ht ){ - struct _fts3ht *pEntry = &pH->ht[h]; - elem = pEntry->chain; - count = pEntry->count; - xCompare = ftsCompareFunction(pH->keyClass); - while( count-- && elem ){ - if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){ - return elem; - } - elem = elem->next; - } - } - return 0; -} - -/* Remove a single entry from the hash table given a pointer to that -** element and a hash on the element's key. -*/ -static void fts3RemoveElementByHash( - Fts3Hash *pH, /* The pH containing "elem" */ - Fts3HashElem* elem, /* The element to be removed from the pH */ - int h /* Hash value for the element */ -){ - struct _fts3ht *pEntry; - if( elem->prev ){ - elem->prev->next = elem->next; - }else{ - pH->first = elem->next; - } - if( elem->next ){ - elem->next->prev = elem->prev; - } - pEntry = &pH->ht[h]; - if( pEntry->chain==elem ){ - pEntry->chain = elem->next; - } - pEntry->count--; - if( pEntry->count<=0 ){ - pEntry->chain = 0; - } - if( pH->copyKey && elem->pKey ){ - fts3HashFree(elem->pKey); - } - fts3HashFree( elem ); - pH->count--; - if( pH->count<=0 ){ - assert( pH->first==0 ); - assert( pH->count==0 ); - fts3HashClear(pH); - } -} - -SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem( - const Fts3Hash *pH, - const void *pKey, - int nKey -){ - int h; /* A hash on key */ - int (*xHash)(const void*,int); /* The hash function */ - - if( pH==0 || pH->ht==0 ) return 0; - xHash = ftsHashFunction(pH->keyClass); - assert( xHash!=0 ); - h = (*xHash)(pKey,nKey); - assert( (pH->htsize & (pH->htsize-1))==0 ); - return fts3FindElementByHash(pH,pKey,nKey, h & (pH->htsize-1)); -} - -/* -** Attempt to locate an element of the hash table pH with a key -** that matches pKey,nKey. Return the data for this element if it is -** found, or NULL if there is no match. -*/ -SQLITE_PRIVATE void *sqlite3Fts3HashFind(const Fts3Hash *pH, const void *pKey, int nKey){ - Fts3HashElem *pElem; /* The element that matches key (if any) */ - - pElem = sqlite3Fts3HashFindElem(pH, pKey, nKey); - return pElem ? pElem->data : 0; -} - -/* Insert an element into the hash table pH. The key is pKey,nKey -** and the data is "data". -** -** If no element exists with a matching key, then a new -** element is created. A copy of the key is made if the copyKey -** flag is set. NULL is returned. -** -** If another element already exists with the same key, then the -** new data replaces the old data and the old data is returned. -** The key is not copied in this instance. If a malloc fails, then -** the new data is returned and the hash table is unchanged. -** -** If the "data" parameter to this function is NULL, then the -** element corresponding to "key" is removed from the hash table. -*/ -SQLITE_PRIVATE void *sqlite3Fts3HashInsert( - Fts3Hash *pH, /* The hash table to insert into */ - const void *pKey, /* The key */ - int nKey, /* Number of bytes in the key */ - void *data /* The data */ -){ - int hraw; /* Raw hash value of the key */ - int h; /* the hash of the key modulo hash table size */ - Fts3HashElem *elem; /* Used to loop thru the element list */ - Fts3HashElem *new_elem; /* New element added to the pH */ - int (*xHash)(const void*,int); /* The hash function */ - - assert( pH!=0 ); - xHash = ftsHashFunction(pH->keyClass); - assert( xHash!=0 ); - hraw = (*xHash)(pKey, nKey); - assert( (pH->htsize & (pH->htsize-1))==0 ); - h = hraw & (pH->htsize-1); - elem = fts3FindElementByHash(pH,pKey,nKey,h); - if( elem ){ - void *old_data = elem->data; - if( data==0 ){ - fts3RemoveElementByHash(pH,elem,h); - }else{ - elem->data = data; - } - return old_data; - } - if( data==0 ) return 0; - if( (pH->htsize==0 && fts3Rehash(pH,8)) - || (pH->count>=pH->htsize && fts3Rehash(pH, pH->htsize*2)) - ){ - pH->count = 0; - return data; - } - assert( pH->htsize>0 ); - new_elem = (Fts3HashElem*)fts3HashMalloc( sizeof(Fts3HashElem) ); - if( new_elem==0 ) return data; - if( pH->copyKey && pKey!=0 ){ - new_elem->pKey = fts3HashMalloc( nKey ); - if( new_elem->pKey==0 ){ - fts3HashFree(new_elem); - return data; - } - memcpy((void*)new_elem->pKey, pKey, nKey); - }else{ - new_elem->pKey = (void*)pKey; - } - new_elem->nKey = nKey; - pH->count++; - assert( pH->htsize>0 ); - assert( (pH->htsize & (pH->htsize-1))==0 ); - h = hraw & (pH->htsize-1); - fts3HashInsertElement(pH, &pH->ht[h], new_elem); - new_elem->data = data; - return 0; -} - -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - -/************** End of fts3_hash.c *******************************************/ -/************** Begin file fts3_porter.c *************************************/ -/* -** 2006 September 30 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Implementation of the full-text-search tokenizer that implements -** a Porter stemmer. -*/ - -/* -** The code in this file is only compiled if: -** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or -** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). -*/ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* #include */ -/* #include */ -/* #include */ -/* #include */ - - -/* -** Class derived from sqlite3_tokenizer -*/ -typedef struct porter_tokenizer { - sqlite3_tokenizer base; /* Base class */ -} porter_tokenizer; - -/* -** Class derived from sqlite3_tokenizer_cursor -*/ -typedef struct porter_tokenizer_cursor { - sqlite3_tokenizer_cursor base; - const char *zInput; /* input we are tokenizing */ - int nInput; /* size of the input */ - int iOffset; /* current position in zInput */ - int iToken; /* index of next token to be returned */ - char *zToken; /* storage for current token */ - int nAllocated; /* space allocated to zToken buffer */ -} porter_tokenizer_cursor; - - -/* -** Create a new tokenizer instance. -*/ -static int porterCreate( - int argc, const char * const *argv, - sqlite3_tokenizer **ppTokenizer -){ - porter_tokenizer *t; - - UNUSED_PARAMETER(argc); - UNUSED_PARAMETER(argv); - - t = (porter_tokenizer *) sqlite3_malloc(sizeof(*t)); - if( t==NULL ) return SQLITE_NOMEM; - memset(t, 0, sizeof(*t)); - *ppTokenizer = &t->base; - return SQLITE_OK; -} - -/* -** Destroy a tokenizer -*/ -static int porterDestroy(sqlite3_tokenizer *pTokenizer){ - sqlite3_free(pTokenizer); - return SQLITE_OK; -} - -/* -** Prepare to begin tokenizing a particular string. The input -** string to be tokenized is zInput[0..nInput-1]. A cursor -** used to incrementally tokenize this string is returned in -** *ppCursor. -*/ -static int porterOpen( - sqlite3_tokenizer *pTokenizer, /* The tokenizer */ - const char *zInput, int nInput, /* String to be tokenized */ - sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */ -){ - porter_tokenizer_cursor *c; - - UNUSED_PARAMETER(pTokenizer); - - c = (porter_tokenizer_cursor *) sqlite3_malloc(sizeof(*c)); - if( c==NULL ) return SQLITE_NOMEM; - - c->zInput = zInput; - if( zInput==0 ){ - c->nInput = 0; - }else if( nInput<0 ){ - c->nInput = (int)strlen(zInput); - }else{ - c->nInput = nInput; - } - c->iOffset = 0; /* start tokenizing at the beginning */ - c->iToken = 0; - c->zToken = NULL; /* no space allocated, yet. */ - c->nAllocated = 0; - - *ppCursor = &c->base; - return SQLITE_OK; -} - -/* -** Close a tokenization cursor previously opened by a call to -** porterOpen() above. -*/ -static int porterClose(sqlite3_tokenizer_cursor *pCursor){ - porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; - sqlite3_free(c->zToken); - sqlite3_free(c); - return SQLITE_OK; -} -/* -** Vowel or consonant -*/ -static const char cType[] = { - 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 2, 1 -}; - -/* -** isConsonant() and isVowel() determine if their first character in -** the string they point to is a consonant or a vowel, according -** to Porter ruls. -** -** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'. -** 'Y' is a consonant unless it follows another consonant, -** in which case it is a vowel. -** -** In these routine, the letters are in reverse order. So the 'y' rule -** is that 'y' is a consonant unless it is followed by another -** consonent. -*/ -static int isVowel(const char*); -static int isConsonant(const char *z){ - int j; - char x = *z; - if( x==0 ) return 0; - assert( x>='a' && x<='z' ); - j = cType[x-'a']; - if( j<2 ) return j; - return z[1]==0 || isVowel(z + 1); -} -static int isVowel(const char *z){ - int j; - char x = *z; - if( x==0 ) return 0; - assert( x>='a' && x<='z' ); - j = cType[x-'a']; - if( j<2 ) return 1-j; - return isConsonant(z + 1); -} - -/* -** Let any sequence of one or more vowels be represented by V and let -** C be sequence of one or more consonants. Then every word can be -** represented as: -** -** [C] (VC){m} [V] -** -** In prose: A word is an optional consonant followed by zero or -** vowel-consonant pairs followed by an optional vowel. "m" is the -** number of vowel consonant pairs. This routine computes the value -** of m for the first i bytes of a word. -** -** Return true if the m-value for z is 1 or more. In other words, -** return true if z contains at least one vowel that is followed -** by a consonant. -** -** In this routine z[] is in reverse order. So we are really looking -** for an instance of of a consonant followed by a vowel. -*/ -static int m_gt_0(const char *z){ - while( isVowel(z) ){ z++; } - if( *z==0 ) return 0; - while( isConsonant(z) ){ z++; } - return *z!=0; -} - -/* Like mgt0 above except we are looking for a value of m which is -** exactly 1 -*/ -static int m_eq_1(const char *z){ - while( isVowel(z) ){ z++; } - if( *z==0 ) return 0; - while( isConsonant(z) ){ z++; } - if( *z==0 ) return 0; - while( isVowel(z) ){ z++; } - if( *z==0 ) return 1; - while( isConsonant(z) ){ z++; } - return *z==0; -} - -/* Like mgt0 above except we are looking for a value of m>1 instead -** or m>0 -*/ -static int m_gt_1(const char *z){ - while( isVowel(z) ){ z++; } - if( *z==0 ) return 0; - while( isConsonant(z) ){ z++; } - if( *z==0 ) return 0; - while( isVowel(z) ){ z++; } - if( *z==0 ) return 0; - while( isConsonant(z) ){ z++; } - return *z!=0; -} - -/* -** Return TRUE if there is a vowel anywhere within z[0..n-1] -*/ -static int hasVowel(const char *z){ - while( isConsonant(z) ){ z++; } - return *z!=0; -} - -/* -** Return TRUE if the word ends in a double consonant. -** -** The text is reversed here. So we are really looking at -** the first two characters of z[]. -*/ -static int doubleConsonant(const char *z){ - return isConsonant(z) && z[0]==z[1]; -} - -/* -** Return TRUE if the word ends with three letters which -** are consonant-vowel-consonent and where the final consonant -** is not 'w', 'x', or 'y'. -** -** The word is reversed here. So we are really checking the -** first three letters and the first one cannot be in [wxy]. -*/ -static int star_oh(const char *z){ - return - isConsonant(z) && - z[0]!='w' && z[0]!='x' && z[0]!='y' && - isVowel(z+1) && - isConsonant(z+2); -} - -/* -** If the word ends with zFrom and xCond() is true for the stem -** of the word that preceeds the zFrom ending, then change the -** ending to zTo. -** -** The input word *pz and zFrom are both in reverse order. zTo -** is in normal order. -** -** Return TRUE if zFrom matches. Return FALSE if zFrom does not -** match. Not that TRUE is returned even if xCond() fails and -** no substitution occurs. -*/ -static int stem( - char **pz, /* The word being stemmed (Reversed) */ - const char *zFrom, /* If the ending matches this... (Reversed) */ - const char *zTo, /* ... change the ending to this (not reversed) */ - int (*xCond)(const char*) /* Condition that must be true */ -){ - char *z = *pz; - while( *zFrom && *zFrom==*z ){ z++; zFrom++; } - if( *zFrom!=0 ) return 0; - if( xCond && !xCond(z) ) return 1; - while( *zTo ){ - *(--z) = *(zTo++); - } - *pz = z; - return 1; -} - -/* -** This is the fallback stemmer used when the porter stemmer is -** inappropriate. The input word is copied into the output with -** US-ASCII case folding. If the input word is too long (more -** than 20 bytes if it contains no digits or more than 6 bytes if -** it contains digits) then word is truncated to 20 or 6 bytes -** by taking 10 or 3 bytes from the beginning and end. -*/ -static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){ - int i, mx, j; - int hasDigit = 0; - for(i=0; i='A' && c<='Z' ){ - zOut[i] = c - 'A' + 'a'; - }else{ - if( c>='0' && c<='9' ) hasDigit = 1; - zOut[i] = c; - } - } - mx = hasDigit ? 3 : 10; - if( nIn>mx*2 ){ - for(j=mx, i=nIn-mx; i=(int)sizeof(zReverse)-7 ){ - /* The word is too big or too small for the porter stemmer. - ** Fallback to the copy stemmer */ - copy_stemmer(zIn, nIn, zOut, pnOut); - return; - } - for(i=0, j=sizeof(zReverse)-6; i='A' && c<='Z' ){ - zReverse[j] = c + 'a' - 'A'; - }else if( c>='a' && c<='z' ){ - zReverse[j] = c; - }else{ - /* The use of a character not in [a-zA-Z] means that we fallback - ** to the copy stemmer */ - copy_stemmer(zIn, nIn, zOut, pnOut); - return; - } - } - memset(&zReverse[sizeof(zReverse)-5], 0, 5); - z = &zReverse[j+1]; - - - /* Step 1a */ - if( z[0]=='s' ){ - if( - !stem(&z, "sess", "ss", 0) && - !stem(&z, "sei", "i", 0) && - !stem(&z, "ss", "ss", 0) - ){ - z++; - } - } - - /* Step 1b */ - z2 = z; - if( stem(&z, "dee", "ee", m_gt_0) ){ - /* Do nothing. The work was all in the test */ - }else if( - (stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel)) - && z!=z2 - ){ - if( stem(&z, "ta", "ate", 0) || - stem(&z, "lb", "ble", 0) || - stem(&z, "zi", "ize", 0) ){ - /* Do nothing. The work was all in the test */ - }else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){ - z++; - }else if( m_eq_1(z) && star_oh(z) ){ - *(--z) = 'e'; - } - } - - /* Step 1c */ - if( z[0]=='y' && hasVowel(z+1) ){ - z[0] = 'i'; - } - - /* Step 2 */ - switch( z[1] ){ - case 'a': - stem(&z, "lanoita", "ate", m_gt_0) || - stem(&z, "lanoit", "tion", m_gt_0); - break; - case 'c': - stem(&z, "icne", "ence", m_gt_0) || - stem(&z, "icna", "ance", m_gt_0); - break; - case 'e': - stem(&z, "rezi", "ize", m_gt_0); - break; - case 'g': - stem(&z, "igol", "log", m_gt_0); - break; - case 'l': - stem(&z, "ilb", "ble", m_gt_0) || - stem(&z, "illa", "al", m_gt_0) || - stem(&z, "iltne", "ent", m_gt_0) || - stem(&z, "ile", "e", m_gt_0) || - stem(&z, "ilsuo", "ous", m_gt_0); - break; - case 'o': - stem(&z, "noitazi", "ize", m_gt_0) || - stem(&z, "noita", "ate", m_gt_0) || - stem(&z, "rota", "ate", m_gt_0); - break; - case 's': - stem(&z, "msila", "al", m_gt_0) || - stem(&z, "ssenevi", "ive", m_gt_0) || - stem(&z, "ssenluf", "ful", m_gt_0) || - stem(&z, "ssensuo", "ous", m_gt_0); - break; - case 't': - stem(&z, "itila", "al", m_gt_0) || - stem(&z, "itivi", "ive", m_gt_0) || - stem(&z, "itilib", "ble", m_gt_0); - break; - } - - /* Step 3 */ - switch( z[0] ){ - case 'e': - stem(&z, "etaci", "ic", m_gt_0) || - stem(&z, "evita", "", m_gt_0) || - stem(&z, "ezila", "al", m_gt_0); - break; - case 'i': - stem(&z, "itici", "ic", m_gt_0); - break; - case 'l': - stem(&z, "laci", "ic", m_gt_0) || - stem(&z, "luf", "", m_gt_0); - break; - case 's': - stem(&z, "ssen", "", m_gt_0); - break; - } - - /* Step 4 */ - switch( z[1] ){ - case 'a': - if( z[0]=='l' && m_gt_1(z+2) ){ - z += 2; - } - break; - case 'c': - if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){ - z += 4; - } - break; - case 'e': - if( z[0]=='r' && m_gt_1(z+2) ){ - z += 2; - } - break; - case 'i': - if( z[0]=='c' && m_gt_1(z+2) ){ - z += 2; - } - break; - case 'l': - if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){ - z += 4; - } - break; - case 'n': - if( z[0]=='t' ){ - if( z[2]=='a' ){ - if( m_gt_1(z+3) ){ - z += 3; - } - }else if( z[2]=='e' ){ - stem(&z, "tneme", "", m_gt_1) || - stem(&z, "tnem", "", m_gt_1) || - stem(&z, "tne", "", m_gt_1); - } - } - break; - case 'o': - if( z[0]=='u' ){ - if( m_gt_1(z+2) ){ - z += 2; - } - }else if( z[3]=='s' || z[3]=='t' ){ - stem(&z, "noi", "", m_gt_1); - } - break; - case 's': - if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ - z += 3; - } - break; - case 't': - stem(&z, "eta", "", m_gt_1) || - stem(&z, "iti", "", m_gt_1); - break; - case 'u': - if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ - z += 3; - } - break; - case 'v': - case 'z': - if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){ - z += 3; - } - break; - } - - /* Step 5a */ - if( z[0]=='e' ){ - if( m_gt_1(z+1) ){ - z++; - }else if( m_eq_1(z+1) && !star_oh(z+1) ){ - z++; - } - } - - /* Step 5b */ - if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){ - z++; - } - - /* z[] is now the stemmed word in reverse order. Flip it back - ** around into forward order and return. - */ - *pnOut = i = (int)strlen(z); - zOut[i] = 0; - while( *z ){ - zOut[--i] = *(z++); - } -} - -/* -** Characters that can be part of a token. We assume any character -** whose value is greater than 0x80 (any UTF character) can be -** part of a token. In other words, delimiters all must have -** values of 0x7f or lower. -*/ -static const char porterIdChar[] = { -/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ -}; -#define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !porterIdChar[ch-0x30])) - -/* -** Extract the next token from a tokenization cursor. The cursor must -** have been opened by a prior call to porterOpen(). -*/ -static int porterNext( - sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */ - const char **pzToken, /* OUT: *pzToken is the token text */ - int *pnBytes, /* OUT: Number of bytes in token */ - int *piStartOffset, /* OUT: Starting offset of token */ - int *piEndOffset, /* OUT: Ending offset of token */ - int *piPosition /* OUT: Position integer of token */ -){ - porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; - const char *z = c->zInput; - - while( c->iOffsetnInput ){ - int iStartOffset, ch; - - /* Scan past delimiter characters */ - while( c->iOffsetnInput && isDelim(z[c->iOffset]) ){ - c->iOffset++; - } - - /* Count non-delimiter characters. */ - iStartOffset = c->iOffset; - while( c->iOffsetnInput && !isDelim(z[c->iOffset]) ){ - c->iOffset++; - } - - if( c->iOffset>iStartOffset ){ - int n = c->iOffset-iStartOffset; - if( n>c->nAllocated ){ - char *pNew; - c->nAllocated = n+20; - pNew = sqlite3_realloc(c->zToken, c->nAllocated); - if( !pNew ) return SQLITE_NOMEM; - c->zToken = pNew; - } - porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes); - *pzToken = c->zToken; - *piStartOffset = iStartOffset; - *piEndOffset = c->iOffset; - *piPosition = c->iToken++; - return SQLITE_OK; - } - } - return SQLITE_DONE; -} - -/* -** The set of routines that implement the porter-stemmer tokenizer -*/ -static const sqlite3_tokenizer_module porterTokenizerModule = { - 0, - porterCreate, - porterDestroy, - porterOpen, - porterClose, - porterNext, - 0 -}; - -/* -** Allocate a new porter tokenizer. Return a pointer to the new -** tokenizer in *ppModule -*/ -SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule( - sqlite3_tokenizer_module const**ppModule -){ - *ppModule = &porterTokenizerModule; -} - -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - -/************** End of fts3_porter.c *****************************************/ -/************** Begin file fts3_tokenizer.c **********************************/ -/* -** 2007 June 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This is part of an SQLite module implementing full-text search. -** This particular file implements the generic tokenizer interface. -*/ - -/* -** The code in this file is only compiled if: -** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or -** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). -*/ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* #include */ -/* #include */ - -/* -** Implementation of the SQL scalar function for accessing the underlying -** hash table. This function may be called as follows: -** -** SELECT (); -** SELECT (, ); -** -** where is the name passed as the second argument -** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer'). -** -** If the argument is specified, it must be a blob value -** containing a pointer to be stored as the hash data corresponding -** to the string . If is not specified, then -** the string must already exist in the has table. Otherwise, -** an error is returned. -** -** Whether or not the argument is specified, the value returned -** is a blob containing the pointer stored as the hash data corresponding -** to string (after the hash-table is updated, if applicable). -*/ -static void scalarFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Fts3Hash *pHash; - void *pPtr = 0; - const unsigned char *zName; - int nName; - - assert( argc==1 || argc==2 ); - - pHash = (Fts3Hash *)sqlite3_user_data(context); - - zName = sqlite3_value_text(argv[0]); - nName = sqlite3_value_bytes(argv[0])+1; - - if( argc==2 ){ - void *pOld; - int n = sqlite3_value_bytes(argv[1]); - if( n!=sizeof(pPtr) ){ - sqlite3_result_error(context, "argument type mismatch", -1); - return; - } - pPtr = *(void **)sqlite3_value_blob(argv[1]); - pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr); - if( pOld==pPtr ){ - sqlite3_result_error(context, "out of memory", -1); - return; - } - }else{ - pPtr = sqlite3Fts3HashFind(pHash, zName, nName); - if( !pPtr ){ - char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); - return; - } - } - - sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); -} - -SQLITE_PRIVATE int sqlite3Fts3IsIdChar(char c){ - static const char isFtsIdChar[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */ - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ - }; - return (c&0x80 || isFtsIdChar[(int)(c)]); -} - -SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *zStr, int *pn){ - const char *z1; - const char *z2 = 0; - - /* Find the start of the next token. */ - z1 = zStr; - while( z2==0 ){ - char c = *z1; - switch( c ){ - case '\0': return 0; /* No more tokens here */ - case '\'': - case '"': - case '`': { - z2 = z1; - while( *++z2 && (*z2!=c || *++z2==c) ); - break; - } - case '[': - z2 = &z1[1]; - while( *z2 && z2[0]!=']' ) z2++; - if( *z2 ) z2++; - break; - - default: - if( sqlite3Fts3IsIdChar(*z1) ){ - z2 = &z1[1]; - while( sqlite3Fts3IsIdChar(*z2) ) z2++; - }else{ - z1++; - } - } - } - - *pn = (int)(z2-z1); - return z1; -} - -SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( - Fts3Hash *pHash, /* Tokenizer hash table */ - const char *zArg, /* Tokenizer name */ - sqlite3_tokenizer **ppTok, /* OUT: Tokenizer (if applicable) */ - char **pzErr /* OUT: Set to malloced error message */ -){ - int rc; - char *z = (char *)zArg; - int n = 0; - char *zCopy; - char *zEnd; /* Pointer to nul-term of zCopy */ - sqlite3_tokenizer_module *m; - - zCopy = sqlite3_mprintf("%s", zArg); - if( !zCopy ) return SQLITE_NOMEM; - zEnd = &zCopy[strlen(zCopy)]; - - z = (char *)sqlite3Fts3NextToken(zCopy, &n); - z[n] = '\0'; - sqlite3Fts3Dequote(z); - - m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash,z,(int)strlen(z)+1); - if( !m ){ - *pzErr = sqlite3_mprintf("unknown tokenizer: %s", z); - rc = SQLITE_ERROR; - }else{ - char const **aArg = 0; - int iArg = 0; - z = &z[n+1]; - while( zxCreate(iArg, aArg, ppTok); - assert( rc!=SQLITE_OK || *ppTok ); - if( rc!=SQLITE_OK ){ - *pzErr = sqlite3_mprintf("unknown tokenizer"); - }else{ - (*ppTok)->pModule = m; - } - sqlite3_free((void *)aArg); - } - - sqlite3_free(zCopy); - return rc; -} - - -#ifdef SQLITE_TEST - -/* #include */ -/* #include */ - -/* -** Implementation of a special SQL scalar function for testing tokenizers -** designed to be used in concert with the Tcl testing framework. This -** function must be called with two or more arguments: -** -** SELECT (, ..., ); -** -** where is the name passed as the second argument -** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer') -** concatenated with the string '_test' (e.g. 'fts3_tokenizer_test'). -** -** The return value is a string that may be interpreted as a Tcl -** list. For each token in the , three elements are -** added to the returned list. The first is the token position, the -** second is the token text (folded, stemmed, etc.) and the third is the -** substring of associated with the token. For example, -** using the built-in "simple" tokenizer: -** -** SELECT fts_tokenizer_test('simple', 'I don't see how'); -** -** will return the string: -** -** "{0 i I 1 dont don't 2 see see 3 how how}" -** -*/ -static void testFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Fts3Hash *pHash; - sqlite3_tokenizer_module *p; - sqlite3_tokenizer *pTokenizer = 0; - sqlite3_tokenizer_cursor *pCsr = 0; - - const char *zErr = 0; - - const char *zName; - int nName; - const char *zInput; - int nInput; - - const char *azArg[64]; - - const char *zToken; - int nToken = 0; - int iStart = 0; - int iEnd = 0; - int iPos = 0; - int i; - - Tcl_Obj *pRet; - - if( argc<2 ){ - sqlite3_result_error(context, "insufficient arguments", -1); - return; - } - - nName = sqlite3_value_bytes(argv[0]); - zName = (const char *)sqlite3_value_text(argv[0]); - nInput = sqlite3_value_bytes(argv[argc-1]); - zInput = (const char *)sqlite3_value_text(argv[argc-1]); - - pHash = (Fts3Hash *)sqlite3_user_data(context); - p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1); - - if( !p ){ - char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); - return; - } - - pRet = Tcl_NewObj(); - Tcl_IncrRefCount(pRet); - - for(i=1; ixCreate(argc-2, azArg, &pTokenizer) ){ - zErr = "error in xCreate()"; - goto finish; - } - pTokenizer->pModule = p; - if( sqlite3Fts3OpenTokenizer(pTokenizer, 0, zInput, nInput, &pCsr) ){ - zErr = "error in xOpen()"; - goto finish; - } - - while( SQLITE_OK==p->xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos) ){ - Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iPos)); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zToken, nToken)); - zToken = &zInput[iStart]; - nToken = iEnd-iStart; - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zToken, nToken)); - } - - if( SQLITE_OK!=p->xClose(pCsr) ){ - zErr = "error in xClose()"; - goto finish; - } - if( SQLITE_OK!=p->xDestroy(pTokenizer) ){ - zErr = "error in xDestroy()"; - goto finish; - } - -finish: - if( zErr ){ - sqlite3_result_error(context, zErr, -1); - }else{ - sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT); - } - Tcl_DecrRefCount(pRet); -} - -static -int registerTokenizer( - sqlite3 *db, - char *zName, - const sqlite3_tokenizer_module *p -){ - int rc; - sqlite3_stmt *pStmt; - const char zSql[] = "SELECT fts3_tokenizer(?, ?)"; - - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - - sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC); - sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC); - sqlite3_step(pStmt); - - return sqlite3_finalize(pStmt); -} - -static -int queryTokenizer( - sqlite3 *db, - char *zName, - const sqlite3_tokenizer_module **pp -){ - int rc; - sqlite3_stmt *pStmt; - const char zSql[] = "SELECT fts3_tokenizer(?)"; - - *pp = 0; - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - - sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC); - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){ - memcpy((void *)pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp)); - } - } - - return sqlite3_finalize(pStmt); -} - -SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); - -/* -** Implementation of the scalar function fts3_tokenizer_internal_test(). -** This function is used for testing only, it is not included in the -** build unless SQLITE_TEST is defined. -** -** The purpose of this is to test that the fts3_tokenizer() function -** can be used as designed by the C-code in the queryTokenizer and -** registerTokenizer() functions above. These two functions are repeated -** in the README.tokenizer file as an example, so it is important to -** test them. -** -** To run the tests, evaluate the fts3_tokenizer_internal_test() scalar -** function with no arguments. An assert() will fail if a problem is -** detected. i.e.: -** -** SELECT fts3_tokenizer_internal_test(); -** -*/ -static void intTestFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int rc; - const sqlite3_tokenizer_module *p1; - const sqlite3_tokenizer_module *p2; - sqlite3 *db = (sqlite3 *)sqlite3_user_data(context); - - UNUSED_PARAMETER(argc); - UNUSED_PARAMETER(argv); - - /* Test the query function */ - sqlite3Fts3SimpleTokenizerModule(&p1); - rc = queryTokenizer(db, "simple", &p2); - assert( rc==SQLITE_OK ); - assert( p1==p2 ); - rc = queryTokenizer(db, "nosuchtokenizer", &p2); - assert( rc==SQLITE_ERROR ); - assert( p2==0 ); - assert( 0==strcmp(sqlite3_errmsg(db), "unknown tokenizer: nosuchtokenizer") ); - - /* Test the storage function */ - rc = registerTokenizer(db, "nosuchtokenizer", p1); - assert( rc==SQLITE_OK ); - rc = queryTokenizer(db, "nosuchtokenizer", &p2); - assert( rc==SQLITE_OK ); - assert( p2==p1 ); - - sqlite3_result_text(context, "ok", -1, SQLITE_STATIC); -} - -#endif - -/* -** Set up SQL objects in database db used to access the contents of -** the hash table pointed to by argument pHash. The hash table must -** been initialized to use string keys, and to take a private copy -** of the key when a value is inserted. i.e. by a call similar to: -** -** sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); -** -** This function adds a scalar function (see header comment above -** scalarFunc() in this file for details) and, if ENABLE_TABLE is -** defined at compilation time, a temporary virtual table (see header -** comment above struct HashTableVtab) to the database schema. Both -** provide read/write access to the contents of *pHash. -** -** The third argument to this function, zName, is used as the name -** of both the scalar and, if created, the virtual table. -*/ -SQLITE_PRIVATE int sqlite3Fts3InitHashTable( - sqlite3 *db, - Fts3Hash *pHash, - const char *zName -){ - int rc = SQLITE_OK; - void *p = (void *)pHash; - const int any = SQLITE_ANY; - -#ifdef SQLITE_TEST - char *zTest = 0; - char *zTest2 = 0; - void *pdb = (void *)db; - zTest = sqlite3_mprintf("%s_test", zName); - zTest2 = sqlite3_mprintf("%s_internal_test", zName); - if( !zTest || !zTest2 ){ - rc = SQLITE_NOMEM; - } -#endif - - if( SQLITE_OK==rc ){ - rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0); - } - if( SQLITE_OK==rc ){ - rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0); - } -#ifdef SQLITE_TEST - if( SQLITE_OK==rc ){ - rc = sqlite3_create_function(db, zTest, -1, any, p, testFunc, 0, 0); - } - if( SQLITE_OK==rc ){ - rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0); - } -#endif - -#ifdef SQLITE_TEST - sqlite3_free(zTest); - sqlite3_free(zTest2); -#endif - - return rc; -} - -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - -/************** End of fts3_tokenizer.c **************************************/ -/************** Begin file fts3_tokenizer1.c *********************************/ -/* -** 2006 Oct 10 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** Implementation of the "simple" full-text-search tokenizer. -*/ - -/* -** The code in this file is only compiled if: -** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or -** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). -*/ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* #include */ -/* #include */ -/* #include */ -/* #include */ - - -typedef struct simple_tokenizer { - sqlite3_tokenizer base; - char delim[128]; /* flag ASCII delimiters */ -} simple_tokenizer; - -typedef struct simple_tokenizer_cursor { - sqlite3_tokenizer_cursor base; - const char *pInput; /* input we are tokenizing */ - int nBytes; /* size of the input */ - int iOffset; /* current position in pInput */ - int iToken; /* index of next token to be returned */ - char *pToken; /* storage for current token */ - int nTokenAllocated; /* space allocated to zToken buffer */ -} simple_tokenizer_cursor; - - -static int simpleDelim(simple_tokenizer *t, unsigned char c){ - return c<0x80 && t->delim[c]; -} -static int fts3_isalnum(int x){ - return (x>='0' && x<='9') || (x>='A' && x<='Z') || (x>='a' && x<='z'); -} - -/* -** Create a new tokenizer instance. -*/ -static int simpleCreate( - int argc, const char * const *argv, - sqlite3_tokenizer **ppTokenizer -){ - simple_tokenizer *t; - - t = (simple_tokenizer *) sqlite3_malloc(sizeof(*t)); - if( t==NULL ) return SQLITE_NOMEM; - memset(t, 0, sizeof(*t)); - - /* TODO(shess) Delimiters need to remain the same from run to run, - ** else we need to reindex. One solution would be a meta-table to - ** track such information in the database, then we'd only want this - ** information on the initial create. - */ - if( argc>1 ){ - int i, n = (int)strlen(argv[1]); - for(i=0; i=0x80 ){ - sqlite3_free(t); - return SQLITE_ERROR; - } - t->delim[ch] = 1; - } - } else { - /* Mark non-alphanumeric ASCII characters as delimiters */ - int i; - for(i=1; i<0x80; i++){ - t->delim[i] = !fts3_isalnum(i) ? -1 : 0; - } - } - - *ppTokenizer = &t->base; - return SQLITE_OK; -} - -/* -** Destroy a tokenizer -*/ -static int simpleDestroy(sqlite3_tokenizer *pTokenizer){ - sqlite3_free(pTokenizer); - return SQLITE_OK; -} - -/* -** Prepare to begin tokenizing a particular string. The input -** string to be tokenized is pInput[0..nBytes-1]. A cursor -** used to incrementally tokenize this string is returned in -** *ppCursor. -*/ -static int simpleOpen( - sqlite3_tokenizer *pTokenizer, /* The tokenizer */ - const char *pInput, int nBytes, /* String to be tokenized */ - sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */ -){ - simple_tokenizer_cursor *c; - - UNUSED_PARAMETER(pTokenizer); - - c = (simple_tokenizer_cursor *) sqlite3_malloc(sizeof(*c)); - if( c==NULL ) return SQLITE_NOMEM; - - c->pInput = pInput; - if( pInput==0 ){ - c->nBytes = 0; - }else if( nBytes<0 ){ - c->nBytes = (int)strlen(pInput); - }else{ - c->nBytes = nBytes; - } - c->iOffset = 0; /* start tokenizing at the beginning */ - c->iToken = 0; - c->pToken = NULL; /* no space allocated, yet. */ - c->nTokenAllocated = 0; - - *ppCursor = &c->base; - return SQLITE_OK; -} - -/* -** Close a tokenization cursor previously opened by a call to -** simpleOpen() above. -*/ -static int simpleClose(sqlite3_tokenizer_cursor *pCursor){ - simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor; - sqlite3_free(c->pToken); - sqlite3_free(c); - return SQLITE_OK; -} - -/* -** Extract the next token from a tokenization cursor. The cursor must -** have been opened by a prior call to simpleOpen(). -*/ -static int simpleNext( - sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */ - const char **ppToken, /* OUT: *ppToken is the token text */ - int *pnBytes, /* OUT: Number of bytes in token */ - int *piStartOffset, /* OUT: Starting offset of token */ - int *piEndOffset, /* OUT: Ending offset of token */ - int *piPosition /* OUT: Position integer of token */ -){ - simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor; - simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer; - unsigned char *p = (unsigned char *)c->pInput; - - while( c->iOffsetnBytes ){ - int iStartOffset; - - /* Scan past delimiter characters */ - while( c->iOffsetnBytes && simpleDelim(t, p[c->iOffset]) ){ - c->iOffset++; - } - - /* Count non-delimiter characters. */ - iStartOffset = c->iOffset; - while( c->iOffsetnBytes && !simpleDelim(t, p[c->iOffset]) ){ - c->iOffset++; - } - - if( c->iOffset>iStartOffset ){ - int i, n = c->iOffset-iStartOffset; - if( n>c->nTokenAllocated ){ - char *pNew; - c->nTokenAllocated = n+20; - pNew = sqlite3_realloc(c->pToken, c->nTokenAllocated); - if( !pNew ) return SQLITE_NOMEM; - c->pToken = pNew; - } - for(i=0; ipToken[i] = (char)((ch>='A' && ch<='Z') ? ch-'A'+'a' : ch); - } - *ppToken = c->pToken; - *pnBytes = n; - *piStartOffset = iStartOffset; - *piEndOffset = c->iOffset; - *piPosition = c->iToken++; - - return SQLITE_OK; - } - } - return SQLITE_DONE; -} - -/* -** The set of routines that implement the simple tokenizer -*/ -static const sqlite3_tokenizer_module simpleTokenizerModule = { - 0, - simpleCreate, - simpleDestroy, - simpleOpen, - simpleClose, - simpleNext, - 0, -}; - -/* -** Allocate a new simple tokenizer. Return a pointer to the new -** tokenizer in *ppModule -*/ -SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule( - sqlite3_tokenizer_module const**ppModule -){ - *ppModule = &simpleTokenizerModule; -} - -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - -/************** End of fts3_tokenizer1.c *************************************/ -/************** Begin file fts3_write.c **************************************/ -/* -** 2009 Oct 23 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file is part of the SQLite FTS3 extension module. Specifically, -** this file contains code to insert, update and delete rows from FTS3 -** tables. It also contains code to merge FTS3 b-tree segments. Some -** of the sub-routines used to merge segments are also used by the query -** code in fts3.c. -*/ - -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* #include */ -/* #include */ -/* #include */ - - -#define FTS_MAX_APPENDABLE_HEIGHT 16 - -/* -** When full-text index nodes are loaded from disk, the buffer that they -** are loaded into has the following number of bytes of padding at the end -** of it. i.e. if a full-text index node is 900 bytes in size, then a buffer -** of 920 bytes is allocated for it. -** -** This means that if we have a pointer into a buffer containing node data, -** it is always safe to read up to two varints from it without risking an -** overread, even if the node data is corrupted. -*/ -#define FTS3_NODE_PADDING (FTS3_VARINT_MAX*2) - -/* -** Under certain circumstances, b-tree nodes (doclists) can be loaded into -** memory incrementally instead of all at once. This can be a big performance -** win (reduced IO and CPU) if SQLite stops calling the virtual table xNext() -** method before retrieving all query results (as may happen, for example, -** if a query has a LIMIT clause). -** -** Incremental loading is used for b-tree nodes FTS3_NODE_CHUNK_THRESHOLD -** bytes and larger. Nodes are loaded in chunks of FTS3_NODE_CHUNKSIZE bytes. -** The code is written so that the hard lower-limit for each of these values -** is 1. Clearly such small values would be inefficient, but can be useful -** for testing purposes. -** -** If this module is built with SQLITE_TEST defined, these constants may -** be overridden at runtime for testing purposes. File fts3_test.c contains -** a Tcl interface to read and write the values. -*/ -#ifdef SQLITE_TEST -int test_fts3_node_chunksize = (4*1024); -int test_fts3_node_chunk_threshold = (4*1024)*4; -# define FTS3_NODE_CHUNKSIZE test_fts3_node_chunksize -# define FTS3_NODE_CHUNK_THRESHOLD test_fts3_node_chunk_threshold -#else -# define FTS3_NODE_CHUNKSIZE (4*1024) -# define FTS3_NODE_CHUNK_THRESHOLD (FTS3_NODE_CHUNKSIZE*4) -#endif - -/* -** The two values that may be meaningfully bound to the :1 parameter in -** statements SQL_REPLACE_STAT and SQL_SELECT_STAT. -*/ -#define FTS_STAT_DOCTOTAL 0 -#define FTS_STAT_INCRMERGEHINT 1 -#define FTS_STAT_AUTOINCRMERGE 2 - -/* -** If FTS_LOG_MERGES is defined, call sqlite3_log() to report each automatic -** and incremental merge operation that takes place. This is used for -** debugging FTS only, it should not usually be turned on in production -** systems. -*/ -#ifdef FTS3_LOG_MERGES -static void fts3LogMerge(int nMerge, sqlite3_int64 iAbsLevel){ - sqlite3_log(SQLITE_OK, "%d-way merge from level %d", nMerge, (int)iAbsLevel); -} -#else -#define fts3LogMerge(x, y) -#endif - - -typedef struct PendingList PendingList; -typedef struct SegmentNode SegmentNode; -typedef struct SegmentWriter SegmentWriter; - -/* -** An instance of the following data structure is used to build doclists -** incrementally. See function fts3PendingListAppend() for details. -*/ -struct PendingList { - int nData; - char *aData; - int nSpace; - sqlite3_int64 iLastDocid; - sqlite3_int64 iLastCol; - sqlite3_int64 iLastPos; -}; - - -/* -** Each cursor has a (possibly empty) linked list of the following objects. -*/ -struct Fts3DeferredToken { - Fts3PhraseToken *pToken; /* Pointer to corresponding expr token */ - int iCol; /* Column token must occur in */ - Fts3DeferredToken *pNext; /* Next in list of deferred tokens */ - PendingList *pList; /* Doclist is assembled here */ -}; - -/* -** An instance of this structure is used to iterate through the terms on -** a contiguous set of segment b-tree leaf nodes. Although the details of -** this structure are only manipulated by code in this file, opaque handles -** of type Fts3SegReader* are also used by code in fts3.c to iterate through -** terms when querying the full-text index. See functions: -** -** sqlite3Fts3SegReaderNew() -** sqlite3Fts3SegReaderFree() -** sqlite3Fts3SegReaderIterate() -** -** Methods used to manipulate Fts3SegReader structures: -** -** fts3SegReaderNext() -** fts3SegReaderFirstDocid() -** fts3SegReaderNextDocid() -*/ -struct Fts3SegReader { - int iIdx; /* Index within level, or 0x7FFFFFFF for PT */ - u8 bLookup; /* True for a lookup only */ - u8 rootOnly; /* True for a root-only reader */ - - sqlite3_int64 iStartBlock; /* Rowid of first leaf block to traverse */ - sqlite3_int64 iLeafEndBlock; /* Rowid of final leaf block to traverse */ - sqlite3_int64 iEndBlock; /* Rowid of final block in segment (or 0) */ - sqlite3_int64 iCurrentBlock; /* Current leaf block (or 0) */ - - char *aNode; /* Pointer to node data (or NULL) */ - int nNode; /* Size of buffer at aNode (or 0) */ - int nPopulate; /* If >0, bytes of buffer aNode[] loaded */ - sqlite3_blob *pBlob; /* If not NULL, blob handle to read node */ - - Fts3HashElem **ppNextElem; - - /* Variables set by fts3SegReaderNext(). These may be read directly - ** by the caller. They are valid from the time SegmentReaderNew() returns - ** until SegmentReaderNext() returns something other than SQLITE_OK - ** (i.e. SQLITE_DONE). - */ - int nTerm; /* Number of bytes in current term */ - char *zTerm; /* Pointer to current term */ - int nTermAlloc; /* Allocated size of zTerm buffer */ - char *aDoclist; /* Pointer to doclist of current entry */ - int nDoclist; /* Size of doclist in current entry */ - - /* The following variables are used by fts3SegReaderNextDocid() to iterate - ** through the current doclist (aDoclist/nDoclist). - */ - char *pOffsetList; - int nOffsetList; /* For descending pending seg-readers only */ - sqlite3_int64 iDocid; -}; - -#define fts3SegReaderIsPending(p) ((p)->ppNextElem!=0) -#define fts3SegReaderIsRootOnly(p) ((p)->rootOnly!=0) - -/* -** An instance of this structure is used to create a segment b-tree in the -** database. The internal details of this type are only accessed by the -** following functions: -** -** fts3SegWriterAdd() -** fts3SegWriterFlush() -** fts3SegWriterFree() -*/ -struct SegmentWriter { - SegmentNode *pTree; /* Pointer to interior tree structure */ - sqlite3_int64 iFirst; /* First slot in %_segments written */ - sqlite3_int64 iFree; /* Next free slot in %_segments */ - char *zTerm; /* Pointer to previous term buffer */ - int nTerm; /* Number of bytes in zTerm */ - int nMalloc; /* Size of malloc'd buffer at zMalloc */ - char *zMalloc; /* Malloc'd space (possibly) used for zTerm */ - int nSize; /* Size of allocation at aData */ - int nData; /* Bytes of data in aData */ - char *aData; /* Pointer to block from malloc() */ -}; - -/* -** Type SegmentNode is used by the following three functions to create -** the interior part of the segment b+-tree structures (everything except -** the leaf nodes). These functions and type are only ever used by code -** within the fts3SegWriterXXX() family of functions described above. -** -** fts3NodeAddTerm() -** fts3NodeWrite() -** fts3NodeFree() -** -** When a b+tree is written to the database (either as a result of a merge -** or the pending-terms table being flushed), leaves are written into the -** database file as soon as they are completely populated. The interior of -** the tree is assembled in memory and written out only once all leaves have -** been populated and stored. This is Ok, as the b+-tree fanout is usually -** very large, meaning that the interior of the tree consumes relatively -** little memory. -*/ -struct SegmentNode { - SegmentNode *pParent; /* Parent node (or NULL for root node) */ - SegmentNode *pRight; /* Pointer to right-sibling */ - SegmentNode *pLeftmost; /* Pointer to left-most node of this depth */ - int nEntry; /* Number of terms written to node so far */ - char *zTerm; /* Pointer to previous term buffer */ - int nTerm; /* Number of bytes in zTerm */ - int nMalloc; /* Size of malloc'd buffer at zMalloc */ - char *zMalloc; /* Malloc'd space (possibly) used for zTerm */ - int nData; /* Bytes of valid data so far */ - char *aData; /* Node data */ -}; - -/* -** Valid values for the second argument to fts3SqlStmt(). -*/ -#define SQL_DELETE_CONTENT 0 -#define SQL_IS_EMPTY 1 -#define SQL_DELETE_ALL_CONTENT 2 -#define SQL_DELETE_ALL_SEGMENTS 3 -#define SQL_DELETE_ALL_SEGDIR 4 -#define SQL_DELETE_ALL_DOCSIZE 5 -#define SQL_DELETE_ALL_STAT 6 -#define SQL_SELECT_CONTENT_BY_ROWID 7 -#define SQL_NEXT_SEGMENT_INDEX 8 -#define SQL_INSERT_SEGMENTS 9 -#define SQL_NEXT_SEGMENTS_ID 10 -#define SQL_INSERT_SEGDIR 11 -#define SQL_SELECT_LEVEL 12 -#define SQL_SELECT_LEVEL_RANGE 13 -#define SQL_SELECT_LEVEL_COUNT 14 -#define SQL_SELECT_SEGDIR_MAX_LEVEL 15 -#define SQL_DELETE_SEGDIR_LEVEL 16 -#define SQL_DELETE_SEGMENTS_RANGE 17 -#define SQL_CONTENT_INSERT 18 -#define SQL_DELETE_DOCSIZE 19 -#define SQL_REPLACE_DOCSIZE 20 -#define SQL_SELECT_DOCSIZE 21 -#define SQL_SELECT_STAT 22 -#define SQL_REPLACE_STAT 23 - -#define SQL_SELECT_ALL_PREFIX_LEVEL 24 -#define SQL_DELETE_ALL_TERMS_SEGDIR 25 -#define SQL_DELETE_SEGDIR_RANGE 26 -#define SQL_SELECT_ALL_LANGID 27 -#define SQL_FIND_MERGE_LEVEL 28 -#define SQL_MAX_LEAF_NODE_ESTIMATE 29 -#define SQL_DELETE_SEGDIR_ENTRY 30 -#define SQL_SHIFT_SEGDIR_ENTRY 31 -#define SQL_SELECT_SEGDIR 32 -#define SQL_CHOMP_SEGDIR 33 -#define SQL_SEGMENT_IS_APPENDABLE 34 -#define SQL_SELECT_INDEXES 35 -#define SQL_SELECT_MXLEVEL 36 - -/* -** This function is used to obtain an SQLite prepared statement handle -** for the statement identified by the second argument. If successful, -** *pp is set to the requested statement handle and SQLITE_OK returned. -** Otherwise, an SQLite error code is returned and *pp is set to 0. -** -** If argument apVal is not NULL, then it must point to an array with -** at least as many entries as the requested statement has bound -** parameters. The values are bound to the statements parameters before -** returning. -*/ -static int fts3SqlStmt( - Fts3Table *p, /* Virtual table handle */ - int eStmt, /* One of the SQL_XXX constants above */ - sqlite3_stmt **pp, /* OUT: Statement handle */ - sqlite3_value **apVal /* Values to bind to statement */ -){ - const char *azSql[] = { -/* 0 */ "DELETE FROM %Q.'%q_content' WHERE rowid = ?", -/* 1 */ "SELECT NOT EXISTS(SELECT docid FROM %Q.'%q_content' WHERE rowid!=?)", -/* 2 */ "DELETE FROM %Q.'%q_content'", -/* 3 */ "DELETE FROM %Q.'%q_segments'", -/* 4 */ "DELETE FROM %Q.'%q_segdir'", -/* 5 */ "DELETE FROM %Q.'%q_docsize'", -/* 6 */ "DELETE FROM %Q.'%q_stat'", -/* 7 */ "SELECT %s WHERE rowid=?", -/* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1", -/* 9 */ "REPLACE INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)", -/* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)", -/* 11 */ "REPLACE INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)", - - /* Return segments in order from oldest to newest.*/ -/* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root " - "FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC", -/* 13 */ "SELECT idx, start_block, leaves_end_block, end_block, root " - "FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?" - "ORDER BY level DESC, idx ASC", - -/* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", -/* 15 */ "SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?", - -/* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", -/* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", -/* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%s)", -/* 19 */ "DELETE FROM %Q.'%q_docsize' WHERE docid = ?", -/* 20 */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", -/* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?", -/* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=?", -/* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(?,?)", -/* 24 */ "", -/* 25 */ "", - -/* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?", -/* 27 */ "SELECT DISTINCT level / (1024 * ?) FROM %Q.'%q_segdir'", - -/* This statement is used to determine which level to read the input from -** when performing an incremental merge. It returns the absolute level number -** of the oldest level in the db that contains at least ? segments. Or, -** if no level in the FTS index contains more than ? segments, the statement -** returns zero rows. */ -/* 28 */ "SELECT level FROM %Q.'%q_segdir' GROUP BY level HAVING count(*)>=?" - " ORDER BY (level %% 1024) ASC LIMIT 1", - -/* Estimate the upper limit on the number of leaf nodes in a new segment -** created by merging the oldest :2 segments from absolute level :1. See -** function sqlite3Fts3Incrmerge() for details. */ -/* 29 */ "SELECT 2 * total(1 + leaves_end_block - start_block) " - " FROM %Q.'%q_segdir' WHERE level = ? AND idx < ?", - -/* SQL_DELETE_SEGDIR_ENTRY -** Delete the %_segdir entry on absolute level :1 with index :2. */ -/* 30 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ? AND idx = ?", - -/* SQL_SHIFT_SEGDIR_ENTRY -** Modify the idx value for the segment with idx=:3 on absolute level :2 -** to :1. */ -/* 31 */ "UPDATE %Q.'%q_segdir' SET idx = ? WHERE level=? AND idx=?", - -/* SQL_SELECT_SEGDIR -** Read a single entry from the %_segdir table. The entry from absolute -** level :1 with index value :2. */ -/* 32 */ "SELECT idx, start_block, leaves_end_block, end_block, root " - "FROM %Q.'%q_segdir' WHERE level = ? AND idx = ?", - -/* SQL_CHOMP_SEGDIR -** Update the start_block (:1) and root (:2) fields of the %_segdir -** entry located on absolute level :3 with index :4. */ -/* 33 */ "UPDATE %Q.'%q_segdir' SET start_block = ?, root = ?" - "WHERE level = ? AND idx = ?", - -/* SQL_SEGMENT_IS_APPENDABLE -** Return a single row if the segment with end_block=? is appendable. Or -** no rows otherwise. */ -/* 34 */ "SELECT 1 FROM %Q.'%q_segments' WHERE blockid=? AND block IS NULL", - -/* SQL_SELECT_INDEXES -** Return the list of valid segment indexes for absolute level ? */ -/* 35 */ "SELECT idx FROM %Q.'%q_segdir' WHERE level=? ORDER BY 1 ASC", - -/* SQL_SELECT_MXLEVEL -** Return the largest relative level in the FTS index or indexes. */ -/* 36 */ "SELECT max( level %% 1024 ) FROM %Q.'%q_segdir'" - }; - int rc = SQLITE_OK; - sqlite3_stmt *pStmt; - - assert( SizeofArray(azSql)==SizeofArray(p->aStmt) ); - assert( eStmt=0 ); - - pStmt = p->aStmt[eStmt]; - if( !pStmt ){ - char *zSql; - if( eStmt==SQL_CONTENT_INSERT ){ - zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); - }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ - zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); - }else{ - zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); - } - if( !zSql ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, NULL); - sqlite3_free(zSql); - assert( rc==SQLITE_OK || pStmt==0 ); - p->aStmt[eStmt] = pStmt; - } - } - if( apVal ){ - int i; - int nParam = sqlite3_bind_parameter_count(pStmt); - for(i=0; rc==SQLITE_OK && izContentTbl==0 ){ - rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_null(pStmt, 1); - sqlite3_step(pStmt); - rc = sqlite3_reset(pStmt); - } - }else{ - rc = SQLITE_OK; - } - - return rc; -} - -/* -** FTS maintains a separate indexes for each language-id (a 32-bit integer). -** Within each language id, a separate index is maintained to store the -** document terms, and each configured prefix size (configured the FTS -** "prefix=" option). And each index consists of multiple levels ("relative -** levels"). -** -** All three of these values (the language id, the specific index and the -** level within the index) are encoded in 64-bit integer values stored -** in the %_segdir table on disk. This function is used to convert three -** separate component values into the single 64-bit integer value that -** can be used to query the %_segdir table. -** -** Specifically, each language-id/index combination is allocated 1024 -** 64-bit integer level values ("absolute levels"). The main terms index -** for language-id 0 is allocate values 0-1023. The first prefix index -** (if any) for language-id 0 is allocated values 1024-2047. And so on. -** Language 1 indexes are allocated immediately following language 0. -** -** So, for a system with nPrefix prefix indexes configured, the block of -** absolute levels that corresponds to language-id iLangid and index -** iIndex starts at absolute level ((iLangid * (nPrefix+1) + iIndex) * 1024). -*/ -static sqlite3_int64 getAbsoluteLevel( - Fts3Table *p, /* FTS3 table handle */ - int iLangid, /* Language id */ - int iIndex, /* Index in p->aIndex[] */ - int iLevel /* Level of segments */ -){ - sqlite3_int64 iBase; /* First absolute level for iLangid/iIndex */ - assert( iLangid>=0 ); - assert( p->nIndex>0 ); - assert( iIndex>=0 && iIndexnIndex ); - - iBase = ((sqlite3_int64)iLangid * p->nIndex + iIndex) * FTS3_SEGDIR_MAXLEVEL; - return iBase + iLevel; -} - -/* -** Set *ppStmt to a statement handle that may be used to iterate through -** all rows in the %_segdir table, from oldest to newest. If successful, -** return SQLITE_OK. If an error occurs while preparing the statement, -** return an SQLite error code. -** -** There is only ever one instance of this SQL statement compiled for -** each FTS3 table. -** -** The statement returns the following columns from the %_segdir table: -** -** 0: idx -** 1: start_block -** 2: leaves_end_block -** 3: end_block -** 4: root -*/ -SQLITE_PRIVATE int sqlite3Fts3AllSegdirs( - Fts3Table *p, /* FTS3 table */ - int iLangid, /* Language being queried */ - int iIndex, /* Index for p->aIndex[] */ - int iLevel, /* Level to select (relative level) */ - sqlite3_stmt **ppStmt /* OUT: Compiled statement */ -){ - int rc; - sqlite3_stmt *pStmt = 0; - - assert( iLevel==FTS3_SEGCURSOR_ALL || iLevel>=0 ); - assert( iLevel=0 && iIndexnIndex ); - - if( iLevel<0 ){ - /* "SELECT * FROM %_segdir WHERE level BETWEEN ? AND ? ORDER BY ..." */ - rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE, &pStmt, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, 0)); - sqlite3_bind_int64(pStmt, 2, - getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1) - ); - } - }else{ - /* "SELECT * FROM %_segdir WHERE level = ? ORDER BY ..." */ - rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex,iLevel)); - } - } - *ppStmt = pStmt; - return rc; -} - - -/* -** Append a single varint to a PendingList buffer. SQLITE_OK is returned -** if successful, or an SQLite error code otherwise. -** -** This function also serves to allocate the PendingList structure itself. -** For example, to create a new PendingList structure containing two -** varints: -** -** PendingList *p = 0; -** fts3PendingListAppendVarint(&p, 1); -** fts3PendingListAppendVarint(&p, 2); -*/ -static int fts3PendingListAppendVarint( - PendingList **pp, /* IN/OUT: Pointer to PendingList struct */ - sqlite3_int64 i /* Value to append to data */ -){ - PendingList *p = *pp; - - /* Allocate or grow the PendingList as required. */ - if( !p ){ - p = sqlite3_malloc(sizeof(*p) + 100); - if( !p ){ - return SQLITE_NOMEM; - } - p->nSpace = 100; - p->aData = (char *)&p[1]; - p->nData = 0; - } - else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){ - int nNew = p->nSpace * 2; - p = sqlite3_realloc(p, sizeof(*p) + nNew); - if( !p ){ - sqlite3_free(*pp); - *pp = 0; - return SQLITE_NOMEM; - } - p->nSpace = nNew; - p->aData = (char *)&p[1]; - } - - /* Append the new serialized varint to the end of the list. */ - p->nData += sqlite3Fts3PutVarint(&p->aData[p->nData], i); - p->aData[p->nData] = '\0'; - *pp = p; - return SQLITE_OK; -} - -/* -** Add a docid/column/position entry to a PendingList structure. Non-zero -** is returned if the structure is sqlite3_realloced as part of adding -** the entry. Otherwise, zero. -** -** If an OOM error occurs, *pRc is set to SQLITE_NOMEM before returning. -** Zero is always returned in this case. Otherwise, if no OOM error occurs, -** it is set to SQLITE_OK. -*/ -static int fts3PendingListAppend( - PendingList **pp, /* IN/OUT: PendingList structure */ - sqlite3_int64 iDocid, /* Docid for entry to add */ - sqlite3_int64 iCol, /* Column for entry to add */ - sqlite3_int64 iPos, /* Position of term for entry to add */ - int *pRc /* OUT: Return code */ -){ - PendingList *p = *pp; - int rc = SQLITE_OK; - - assert( !p || p->iLastDocid<=iDocid ); - - if( !p || p->iLastDocid!=iDocid ){ - sqlite3_int64 iDelta = iDocid - (p ? p->iLastDocid : 0); - if( p ){ - assert( p->nDatanSpace ); - assert( p->aData[p->nData]==0 ); - p->nData++; - } - if( SQLITE_OK!=(rc = fts3PendingListAppendVarint(&p, iDelta)) ){ - goto pendinglistappend_out; - } - p->iLastCol = -1; - p->iLastPos = 0; - p->iLastDocid = iDocid; - } - if( iCol>0 && p->iLastCol!=iCol ){ - if( SQLITE_OK!=(rc = fts3PendingListAppendVarint(&p, 1)) - || SQLITE_OK!=(rc = fts3PendingListAppendVarint(&p, iCol)) - ){ - goto pendinglistappend_out; - } - p->iLastCol = iCol; - p->iLastPos = 0; - } - if( iCol>=0 ){ - assert( iPos>p->iLastPos || (iPos==0 && p->iLastPos==0) ); - rc = fts3PendingListAppendVarint(&p, 2+iPos-p->iLastPos); - if( rc==SQLITE_OK ){ - p->iLastPos = iPos; - } - } - - pendinglistappend_out: - *pRc = rc; - if( p!=*pp ){ - *pp = p; - return 1; - } - return 0; -} - -/* -** Free a PendingList object allocated by fts3PendingListAppend(). -*/ -static void fts3PendingListDelete(PendingList *pList){ - sqlite3_free(pList); -} - -/* -** Add an entry to one of the pending-terms hash tables. -*/ -static int fts3PendingTermsAddOne( - Fts3Table *p, - int iCol, - int iPos, - Fts3Hash *pHash, /* Pending terms hash table to add entry to */ - const char *zToken, - int nToken -){ - PendingList *pList; - int rc = SQLITE_OK; - - pList = (PendingList *)fts3HashFind(pHash, zToken, nToken); - if( pList ){ - p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem)); - } - if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){ - if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){ - /* Malloc failed while inserting the new entry. This can only - ** happen if there was no previous entry for this token. - */ - assert( 0==fts3HashFind(pHash, zToken, nToken) ); - sqlite3_free(pList); - rc = SQLITE_NOMEM; - } - } - if( rc==SQLITE_OK ){ - p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem)); - } - return rc; -} - -/* -** Tokenize the nul-terminated string zText and add all tokens to the -** pending-terms hash-table. The docid used is that currently stored in -** p->iPrevDocid, and the column is specified by argument iCol. -** -** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. -*/ -static int fts3PendingTermsAdd( - Fts3Table *p, /* Table into which text will be inserted */ - int iLangid, /* Language id to use */ - const char *zText, /* Text of document to be inserted */ - int iCol, /* Column into which text is being inserted */ - u32 *pnWord /* IN/OUT: Incr. by number tokens inserted */ -){ - int rc; - int iStart = 0; - int iEnd = 0; - int iPos = 0; - int nWord = 0; - - char const *zToken; - int nToken = 0; - - sqlite3_tokenizer *pTokenizer = p->pTokenizer; - sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; - sqlite3_tokenizer_cursor *pCsr; - int (*xNext)(sqlite3_tokenizer_cursor *pCursor, - const char**,int*,int*,int*,int*); - - assert( pTokenizer && pModule ); - - /* If the user has inserted a NULL value, this function may be called with - ** zText==0. In this case, add zero token entries to the hash table and - ** return early. */ - if( zText==0 ){ - *pnWord = 0; - return SQLITE_OK; - } - - rc = sqlite3Fts3OpenTokenizer(pTokenizer, iLangid, zText, -1, &pCsr); - if( rc!=SQLITE_OK ){ - return rc; - } - - xNext = pModule->xNext; - while( SQLITE_OK==rc - && SQLITE_OK==(rc = xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos)) - ){ - int i; - if( iPos>=nWord ) nWord = iPos+1; - - /* Positions cannot be negative; we use -1 as a terminator internally. - ** Tokens must have a non-zero length. - */ - if( iPos<0 || !zToken || nToken<=0 ){ - rc = SQLITE_ERROR; - break; - } - - /* Add the term to the terms index */ - rc = fts3PendingTermsAddOne( - p, iCol, iPos, &p->aIndex[0].hPending, zToken, nToken - ); - - /* Add the term to each of the prefix indexes that it is not too - ** short for. */ - for(i=1; rc==SQLITE_OK && inIndex; i++){ - struct Fts3Index *pIndex = &p->aIndex[i]; - if( nTokennPrefix ) continue; - rc = fts3PendingTermsAddOne( - p, iCol, iPos, &pIndex->hPending, zToken, pIndex->nPrefix - ); - } - } - - pModule->xClose(pCsr); - *pnWord += nWord; - return (rc==SQLITE_DONE ? SQLITE_OK : rc); -} - -/* -** Calling this function indicates that subsequent calls to -** fts3PendingTermsAdd() are to add term/position-list pairs for the -** contents of the document with docid iDocid. -*/ -static int fts3PendingTermsDocid( - Fts3Table *p, /* Full-text table handle */ - int iLangid, /* Language id of row being written */ - sqlite_int64 iDocid /* Docid of row being written */ -){ - assert( iLangid>=0 ); - - /* TODO(shess) Explore whether partially flushing the buffer on - ** forced-flush would provide better performance. I suspect that if - ** we ordered the doclists by size and flushed the largest until the - ** buffer was half empty, that would let the less frequent terms - ** generate longer doclists. - */ - if( iDocid<=p->iPrevDocid - || p->iPrevLangid!=iLangid - || p->nPendingData>p->nMaxPendingData - ){ - int rc = sqlite3Fts3PendingTermsFlush(p); - if( rc!=SQLITE_OK ) return rc; - } - p->iPrevDocid = iDocid; - p->iPrevLangid = iLangid; - return SQLITE_OK; -} - -/* -** Discard the contents of the pending-terms hash tables. -*/ -SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *p){ - int i; - for(i=0; inIndex; i++){ - Fts3HashElem *pElem; - Fts3Hash *pHash = &p->aIndex[i].hPending; - for(pElem=fts3HashFirst(pHash); pElem; pElem=fts3HashNext(pElem)){ - PendingList *pList = (PendingList *)fts3HashData(pElem); - fts3PendingListDelete(pList); - } - fts3HashClear(pHash); - } - p->nPendingData = 0; -} - -/* -** This function is called by the xUpdate() method as part of an INSERT -** operation. It adds entries for each term in the new record to the -** pendingTerms hash table. -** -** Argument apVal is the same as the similarly named argument passed to -** fts3InsertData(). Parameter iDocid is the docid of the new row. -*/ -static int fts3InsertTerms( - Fts3Table *p, - int iLangid, - sqlite3_value **apVal, - u32 *aSz -){ - int i; /* Iterator variable */ - for(i=2; inColumn+2; i++){ - const char *zText = (const char *)sqlite3_value_text(apVal[i]); - int rc = fts3PendingTermsAdd(p, iLangid, zText, i-2, &aSz[i-2]); - if( rc!=SQLITE_OK ){ - return rc; - } - aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]); - } - return SQLITE_OK; -} - -/* -** This function is called by the xUpdate() method for an INSERT operation. -** The apVal parameter is passed a copy of the apVal argument passed by -** SQLite to the xUpdate() method. i.e: -** -** apVal[0] Not used for INSERT. -** apVal[1] rowid -** apVal[2] Left-most user-defined column -** ... -** apVal[p->nColumn+1] Right-most user-defined column -** apVal[p->nColumn+2] Hidden column with same name as table -** apVal[p->nColumn+3] Hidden "docid" column (alias for rowid) -** apVal[p->nColumn+4] Hidden languageid column -*/ -static int fts3InsertData( - Fts3Table *p, /* Full-text table */ - sqlite3_value **apVal, /* Array of values to insert */ - sqlite3_int64 *piDocid /* OUT: Docid for row just inserted */ -){ - int rc; /* Return code */ - sqlite3_stmt *pContentInsert; /* INSERT INTO %_content VALUES(...) */ - - if( p->zContentTbl ){ - sqlite3_value *pRowid = apVal[p->nColumn+3]; - if( sqlite3_value_type(pRowid)==SQLITE_NULL ){ - pRowid = apVal[1]; - } - if( sqlite3_value_type(pRowid)!=SQLITE_INTEGER ){ - return SQLITE_CONSTRAINT; - } - *piDocid = sqlite3_value_int64(pRowid); - return SQLITE_OK; - } - - /* Locate the statement handle used to insert data into the %_content - ** table. The SQL for this statement is: - ** - ** INSERT INTO %_content VALUES(?, ?, ?, ...) - ** - ** The statement features N '?' variables, where N is the number of user - ** defined columns in the FTS3 table, plus one for the docid field. - */ - rc = fts3SqlStmt(p, SQL_CONTENT_INSERT, &pContentInsert, &apVal[1]); - if( rc==SQLITE_OK && p->zLanguageid ){ - rc = sqlite3_bind_int( - pContentInsert, p->nColumn+2, - sqlite3_value_int(apVal[p->nColumn+4]) - ); - } - if( rc!=SQLITE_OK ) return rc; - - /* There is a quirk here. The users INSERT statement may have specified - ** a value for the "rowid" field, for the "docid" field, or for both. - ** Which is a problem, since "rowid" and "docid" are aliases for the - ** same value. For example: - ** - ** INSERT INTO fts3tbl(rowid, docid) VALUES(1, 2); - ** - ** In FTS3, this is an error. It is an error to specify non-NULL values - ** for both docid and some other rowid alias. - */ - if( SQLITE_NULL!=sqlite3_value_type(apVal[3+p->nColumn]) ){ - if( SQLITE_NULL==sqlite3_value_type(apVal[0]) - && SQLITE_NULL!=sqlite3_value_type(apVal[1]) - ){ - /* A rowid/docid conflict. */ - return SQLITE_ERROR; - } - rc = sqlite3_bind_value(pContentInsert, 1, apVal[3+p->nColumn]); - if( rc!=SQLITE_OK ) return rc; - } - - /* Execute the statement to insert the record. Set *piDocid to the - ** new docid value. - */ - sqlite3_step(pContentInsert); - rc = sqlite3_reset(pContentInsert); - - *piDocid = sqlite3_last_insert_rowid(p->db); - return rc; -} - - - -/* -** Remove all data from the FTS3 table. Clear the hash table containing -** pending terms. -*/ -static int fts3DeleteAll(Fts3Table *p, int bContent){ - int rc = SQLITE_OK; /* Return code */ - - /* Discard the contents of the pending-terms hash table. */ - sqlite3Fts3PendingTermsClear(p); - - /* Delete everything from the shadow tables. Except, leave %_content as - ** is if bContent is false. */ - assert( p->zContentTbl==0 || bContent==0 ); - if( bContent ) fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0); - fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0); - fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); - if( p->bHasDocsize ){ - fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0); - } - if( p->bHasStat ){ - fts3SqlExec(&rc, p, SQL_DELETE_ALL_STAT, 0); - } - return rc; -} - -/* -** -*/ -static int langidFromSelect(Fts3Table *p, sqlite3_stmt *pSelect){ - int iLangid = 0; - if( p->zLanguageid ) iLangid = sqlite3_column_int(pSelect, p->nColumn+1); - return iLangid; -} - -/* -** The first element in the apVal[] array is assumed to contain the docid -** (an integer) of a row about to be deleted. Remove all terms from the -** full-text index. -*/ -static void fts3DeleteTerms( - int *pRC, /* Result code */ - Fts3Table *p, /* The FTS table to delete from */ - sqlite3_value *pRowid, /* The docid to be deleted */ - u32 *aSz, /* Sizes of deleted document written here */ - int *pbFound /* OUT: Set to true if row really does exist */ -){ - int rc; - sqlite3_stmt *pSelect; - - assert( *pbFound==0 ); - if( *pRC ) return; - rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, &pRowid); - if( rc==SQLITE_OK ){ - if( SQLITE_ROW==sqlite3_step(pSelect) ){ - int i; - int iLangid = langidFromSelect(p, pSelect); - rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pSelect, 0)); - for(i=1; rc==SQLITE_OK && i<=p->nColumn; i++){ - const char *zText = (const char *)sqlite3_column_text(pSelect, i); - rc = fts3PendingTermsAdd(p, iLangid, zText, -1, &aSz[i-1]); - aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i); - } - if( rc!=SQLITE_OK ){ - sqlite3_reset(pSelect); - *pRC = rc; - return; - } - *pbFound = 1; - } - rc = sqlite3_reset(pSelect); - }else{ - sqlite3_reset(pSelect); - } - *pRC = rc; -} - -/* -** Forward declaration to account for the circular dependency between -** functions fts3SegmentMerge() and fts3AllocateSegdirIdx(). -*/ -static int fts3SegmentMerge(Fts3Table *, int, int, int); - -/* -** This function allocates a new level iLevel index in the segdir table. -** Usually, indexes are allocated within a level sequentially starting -** with 0, so the allocated index is one greater than the value returned -** by: -** -** SELECT max(idx) FROM %_segdir WHERE level = :iLevel -** -** However, if there are already FTS3_MERGE_COUNT indexes at the requested -** level, they are merged into a single level (iLevel+1) segment and the -** allocated index is 0. -** -** If successful, *piIdx is set to the allocated index slot and SQLITE_OK -** returned. Otherwise, an SQLite error code is returned. -*/ -static int fts3AllocateSegdirIdx( - Fts3Table *p, - int iLangid, /* Language id */ - int iIndex, /* Index for p->aIndex */ - int iLevel, - int *piIdx -){ - int rc; /* Return Code */ - sqlite3_stmt *pNextIdx; /* Query for next idx at level iLevel */ - int iNext = 0; /* Result of query pNextIdx */ - - assert( iLangid>=0 ); - assert( p->nIndex>=1 ); - - /* Set variable iNext to the next available segdir index at level iLevel. */ - rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pNextIdx, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64( - pNextIdx, 1, getAbsoluteLevel(p, iLangid, iIndex, iLevel) - ); - if( SQLITE_ROW==sqlite3_step(pNextIdx) ){ - iNext = sqlite3_column_int(pNextIdx, 0); - } - rc = sqlite3_reset(pNextIdx); - } - - if( rc==SQLITE_OK ){ - /* If iNext is FTS3_MERGE_COUNT, indicating that level iLevel is already - ** full, merge all segments in level iLevel into a single iLevel+1 - ** segment and allocate (newly freed) index 0 at level iLevel. Otherwise, - ** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext. - */ - if( iNext>=FTS3_MERGE_COUNT ){ - fts3LogMerge(16, getAbsoluteLevel(p, iLangid, iIndex, iLevel)); - rc = fts3SegmentMerge(p, iLangid, iIndex, iLevel); - *piIdx = 0; - }else{ - *piIdx = iNext; - } - } - - return rc; -} - -/* -** The %_segments table is declared as follows: -** -** CREATE TABLE %_segments(blockid INTEGER PRIMARY KEY, block BLOB) -** -** This function reads data from a single row of the %_segments table. The -** specific row is identified by the iBlockid parameter. If paBlob is not -** NULL, then a buffer is allocated using sqlite3_malloc() and populated -** with the contents of the blob stored in the "block" column of the -** identified table row is. Whether or not paBlob is NULL, *pnBlob is set -** to the size of the blob in bytes before returning. -** -** If an error occurs, or the table does not contain the specified row, -** an SQLite error code is returned. Otherwise, SQLITE_OK is returned. If -** paBlob is non-NULL, then it is the responsibility of the caller to -** eventually free the returned buffer. -** -** This function may leave an open sqlite3_blob* handle in the -** Fts3Table.pSegments variable. This handle is reused by subsequent calls -** to this function. The handle may be closed by calling the -** sqlite3Fts3SegmentsClose() function. Reusing a blob handle is a handy -** performance improvement, but the blob handle should always be closed -** before control is returned to the user (to prevent a lock being held -** on the database file for longer than necessary). Thus, any virtual table -** method (xFilter etc.) that may directly or indirectly call this function -** must call sqlite3Fts3SegmentsClose() before returning. -*/ -SQLITE_PRIVATE int sqlite3Fts3ReadBlock( - Fts3Table *p, /* FTS3 table handle */ - sqlite3_int64 iBlockid, /* Access the row with blockid=$iBlockid */ - char **paBlob, /* OUT: Blob data in malloc'd buffer */ - int *pnBlob, /* OUT: Size of blob data */ - int *pnLoad /* OUT: Bytes actually loaded */ -){ - int rc; /* Return code */ - - /* pnBlob must be non-NULL. paBlob may be NULL or non-NULL. */ - assert( pnBlob ); - - if( p->pSegments ){ - rc = sqlite3_blob_reopen(p->pSegments, iBlockid); - }else{ - if( 0==p->zSegmentsTbl ){ - p->zSegmentsTbl = sqlite3_mprintf("%s_segments", p->zName); - if( 0==p->zSegmentsTbl ) return SQLITE_NOMEM; - } - rc = sqlite3_blob_open( - p->db, p->zDb, p->zSegmentsTbl, "block", iBlockid, 0, &p->pSegments - ); - } - - if( rc==SQLITE_OK ){ - int nByte = sqlite3_blob_bytes(p->pSegments); - *pnBlob = nByte; - if( paBlob ){ - char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING); - if( !aByte ){ - rc = SQLITE_NOMEM; - }else{ - if( pnLoad && nByte>(FTS3_NODE_CHUNK_THRESHOLD) ){ - nByte = FTS3_NODE_CHUNKSIZE; - *pnLoad = nByte; - } - rc = sqlite3_blob_read(p->pSegments, aByte, nByte, 0); - memset(&aByte[nByte], 0, FTS3_NODE_PADDING); - if( rc!=SQLITE_OK ){ - sqlite3_free(aByte); - aByte = 0; - } - } - *paBlob = aByte; - } - } - - return rc; -} - -/* -** Close the blob handle at p->pSegments, if it is open. See comments above -** the sqlite3Fts3ReadBlock() function for details. -*/ -SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *p){ - sqlite3_blob_close(p->pSegments); - p->pSegments = 0; -} - -static int fts3SegReaderIncrRead(Fts3SegReader *pReader){ - int nRead; /* Number of bytes to read */ - int rc; /* Return code */ - - nRead = MIN(pReader->nNode - pReader->nPopulate, FTS3_NODE_CHUNKSIZE); - rc = sqlite3_blob_read( - pReader->pBlob, - &pReader->aNode[pReader->nPopulate], - nRead, - pReader->nPopulate - ); - - if( rc==SQLITE_OK ){ - pReader->nPopulate += nRead; - memset(&pReader->aNode[pReader->nPopulate], 0, FTS3_NODE_PADDING); - if( pReader->nPopulate==pReader->nNode ){ - sqlite3_blob_close(pReader->pBlob); - pReader->pBlob = 0; - pReader->nPopulate = 0; - } - } - return rc; -} - -static int fts3SegReaderRequire(Fts3SegReader *pReader, char *pFrom, int nByte){ - int rc = SQLITE_OK; - assert( !pReader->pBlob - || (pFrom>=pReader->aNode && pFrom<&pReader->aNode[pReader->nNode]) - ); - while( pReader->pBlob && rc==SQLITE_OK - && (pFrom - pReader->aNode + nByte)>pReader->nPopulate - ){ - rc = fts3SegReaderIncrRead(pReader); - } - return rc; -} - -/* -** Set an Fts3SegReader cursor to point at EOF. -*/ -static void fts3SegReaderSetEof(Fts3SegReader *pSeg){ - if( !fts3SegReaderIsRootOnly(pSeg) ){ - sqlite3_free(pSeg->aNode); - sqlite3_blob_close(pSeg->pBlob); - pSeg->pBlob = 0; - } - pSeg->aNode = 0; -} - -/* -** Move the iterator passed as the first argument to the next term in the -** segment. If successful, SQLITE_OK is returned. If there is no next term, -** SQLITE_DONE. Otherwise, an SQLite error code. -*/ -static int fts3SegReaderNext( - Fts3Table *p, - Fts3SegReader *pReader, - int bIncr -){ - int rc; /* Return code of various sub-routines */ - char *pNext; /* Cursor variable */ - int nPrefix; /* Number of bytes in term prefix */ - int nSuffix; /* Number of bytes in term suffix */ - - if( !pReader->aDoclist ){ - pNext = pReader->aNode; - }else{ - pNext = &pReader->aDoclist[pReader->nDoclist]; - } - - if( !pNext || pNext>=&pReader->aNode[pReader->nNode] ){ - - if( fts3SegReaderIsPending(pReader) ){ - Fts3HashElem *pElem = *(pReader->ppNextElem); - if( pElem==0 ){ - pReader->aNode = 0; - }else{ - PendingList *pList = (PendingList *)fts3HashData(pElem); - pReader->zTerm = (char *)fts3HashKey(pElem); - pReader->nTerm = fts3HashKeysize(pElem); - pReader->nNode = pReader->nDoclist = pList->nData + 1; - pReader->aNode = pReader->aDoclist = pList->aData; - pReader->ppNextElem++; - assert( pReader->aNode ); - } - return SQLITE_OK; - } - - fts3SegReaderSetEof(pReader); - - /* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf - ** blocks have already been traversed. */ - assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock ); - if( pReader->iCurrentBlock>=pReader->iLeafEndBlock ){ - return SQLITE_OK; - } - - rc = sqlite3Fts3ReadBlock( - p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode, - (bIncr ? &pReader->nPopulate : 0) - ); - if( rc!=SQLITE_OK ) return rc; - assert( pReader->pBlob==0 ); - if( bIncr && pReader->nPopulatenNode ){ - pReader->pBlob = p->pSegments; - p->pSegments = 0; - } - pNext = pReader->aNode; - } - - assert( !fts3SegReaderIsPending(pReader) ); - - rc = fts3SegReaderRequire(pReader, pNext, FTS3_VARINT_MAX*2); - if( rc!=SQLITE_OK ) return rc; - - /* Because of the FTS3_NODE_PADDING bytes of padding, the following is - ** safe (no risk of overread) even if the node data is corrupted. */ - pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix); - pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix); - if( nPrefix<0 || nSuffix<=0 - || &pNext[nSuffix]>&pReader->aNode[pReader->nNode] - ){ - return FTS_CORRUPT_VTAB; - } - - if( nPrefix+nSuffix>pReader->nTermAlloc ){ - int nNew = (nPrefix+nSuffix)*2; - char *zNew = sqlite3_realloc(pReader->zTerm, nNew); - if( !zNew ){ - return SQLITE_NOMEM; - } - pReader->zTerm = zNew; - pReader->nTermAlloc = nNew; - } - - rc = fts3SegReaderRequire(pReader, pNext, nSuffix+FTS3_VARINT_MAX); - if( rc!=SQLITE_OK ) return rc; - - memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix); - pReader->nTerm = nPrefix+nSuffix; - pNext += nSuffix; - pNext += sqlite3Fts3GetVarint32(pNext, &pReader->nDoclist); - pReader->aDoclist = pNext; - pReader->pOffsetList = 0; - - /* Check that the doclist does not appear to extend past the end of the - ** b-tree node. And that the final byte of the doclist is 0x00. If either - ** of these statements is untrue, then the data structure is corrupt. - */ - if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode] - || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) - ){ - return FTS_CORRUPT_VTAB; - } - return SQLITE_OK; -} - -/* -** Set the SegReader to point to the first docid in the doclist associated -** with the current term. -*/ -static int fts3SegReaderFirstDocid(Fts3Table *pTab, Fts3SegReader *pReader){ - int rc = SQLITE_OK; - assert( pReader->aDoclist ); - assert( !pReader->pOffsetList ); - if( pTab->bDescIdx && fts3SegReaderIsPending(pReader) ){ - u8 bEof = 0; - pReader->iDocid = 0; - pReader->nOffsetList = 0; - sqlite3Fts3DoclistPrev(0, - pReader->aDoclist, pReader->nDoclist, &pReader->pOffsetList, - &pReader->iDocid, &pReader->nOffsetList, &bEof - ); - }else{ - rc = fts3SegReaderRequire(pReader, pReader->aDoclist, FTS3_VARINT_MAX); - if( rc==SQLITE_OK ){ - int n = sqlite3Fts3GetVarint(pReader->aDoclist, &pReader->iDocid); - pReader->pOffsetList = &pReader->aDoclist[n]; - } - } - return rc; -} - -/* -** Advance the SegReader to point to the next docid in the doclist -** associated with the current term. -** -** If arguments ppOffsetList and pnOffsetList are not NULL, then -** *ppOffsetList is set to point to the first column-offset list -** in the doclist entry (i.e. immediately past the docid varint). -** *pnOffsetList is set to the length of the set of column-offset -** lists, not including the nul-terminator byte. For example: -*/ -static int fts3SegReaderNextDocid( - Fts3Table *pTab, - Fts3SegReader *pReader, /* Reader to advance to next docid */ - char **ppOffsetList, /* OUT: Pointer to current position-list */ - int *pnOffsetList /* OUT: Length of *ppOffsetList in bytes */ -){ - int rc = SQLITE_OK; - char *p = pReader->pOffsetList; - char c = 0; - - assert( p ); - - if( pTab->bDescIdx && fts3SegReaderIsPending(pReader) ){ - /* A pending-terms seg-reader for an FTS4 table that uses order=desc. - ** Pending-terms doclists are always built up in ascending order, so - ** we have to iterate through them backwards here. */ - u8 bEof = 0; - if( ppOffsetList ){ - *ppOffsetList = pReader->pOffsetList; - *pnOffsetList = pReader->nOffsetList - 1; - } - sqlite3Fts3DoclistPrev(0, - pReader->aDoclist, pReader->nDoclist, &p, &pReader->iDocid, - &pReader->nOffsetList, &bEof - ); - if( bEof ){ - pReader->pOffsetList = 0; - }else{ - pReader->pOffsetList = p; - } - }else{ - char *pEnd = &pReader->aDoclist[pReader->nDoclist]; - - /* Pointer p currently points at the first byte of an offset list. The - ** following block advances it to point one byte past the end of - ** the same offset list. */ - while( 1 ){ - - /* The following line of code (and the "p++" below the while() loop) is - ** normally all that is required to move pointer p to the desired - ** position. The exception is if this node is being loaded from disk - ** incrementally and pointer "p" now points to the first byte passed - ** the populated part of pReader->aNode[]. - */ - while( *p | c ) c = *p++ & 0x80; - assert( *p==0 ); - - if( pReader->pBlob==0 || p<&pReader->aNode[pReader->nPopulate] ) break; - rc = fts3SegReaderIncrRead(pReader); - if( rc!=SQLITE_OK ) return rc; - } - p++; - - /* If required, populate the output variables with a pointer to and the - ** size of the previous offset-list. - */ - if( ppOffsetList ){ - *ppOffsetList = pReader->pOffsetList; - *pnOffsetList = (int)(p - pReader->pOffsetList - 1); - } - - /* List may have been edited in place by fts3EvalNearTrim() */ - while( p=pEnd ){ - pReader->pOffsetList = 0; - }else{ - rc = fts3SegReaderRequire(pReader, p, FTS3_VARINT_MAX); - if( rc==SQLITE_OK ){ - sqlite3_int64 iDelta; - pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta); - if( pTab->bDescIdx ){ - pReader->iDocid -= iDelta; - }else{ - pReader->iDocid += iDelta; - } - } - } - } - - return SQLITE_OK; -} - - -SQLITE_PRIVATE int sqlite3Fts3MsrOvfl( - Fts3Cursor *pCsr, - Fts3MultiSegReader *pMsr, - int *pnOvfl -){ - Fts3Table *p = (Fts3Table*)pCsr->base.pVtab; - int nOvfl = 0; - int ii; - int rc = SQLITE_OK; - int pgsz = p->nPgsz; - - assert( p->bFts4 ); - assert( pgsz>0 ); - - for(ii=0; rc==SQLITE_OK && iinSegment; ii++){ - Fts3SegReader *pReader = pMsr->apSegment[ii]; - if( !fts3SegReaderIsPending(pReader) - && !fts3SegReaderIsRootOnly(pReader) - ){ - sqlite3_int64 jj; - for(jj=pReader->iStartBlock; jj<=pReader->iLeafEndBlock; jj++){ - int nBlob; - rc = sqlite3Fts3ReadBlock(p, jj, 0, &nBlob, 0); - if( rc!=SQLITE_OK ) break; - if( (nBlob+35)>pgsz ){ - nOvfl += (nBlob + 34)/pgsz; - } - } - } - } - *pnOvfl = nOvfl; - return rc; -} - -/* -** Free all allocations associated with the iterator passed as the -** second argument. -*/ -SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){ - if( pReader && !fts3SegReaderIsPending(pReader) ){ - sqlite3_free(pReader->zTerm); - if( !fts3SegReaderIsRootOnly(pReader) ){ - sqlite3_free(pReader->aNode); - sqlite3_blob_close(pReader->pBlob); - } - } - sqlite3_free(pReader); -} - -/* -** Allocate a new SegReader object. -*/ -SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( - int iAge, /* Segment "age". */ - int bLookup, /* True for a lookup only */ - sqlite3_int64 iStartLeaf, /* First leaf to traverse */ - sqlite3_int64 iEndLeaf, /* Final leaf to traverse */ - sqlite3_int64 iEndBlock, /* Final block of segment */ - const char *zRoot, /* Buffer containing root node */ - int nRoot, /* Size of buffer containing root node */ - Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */ -){ - Fts3SegReader *pReader; /* Newly allocated SegReader object */ - int nExtra = 0; /* Bytes to allocate segment root node */ - - assert( iStartLeaf<=iEndLeaf ); - if( iStartLeaf==0 ){ - nExtra = nRoot + FTS3_NODE_PADDING; - } - - pReader = (Fts3SegReader *)sqlite3_malloc(sizeof(Fts3SegReader) + nExtra); - if( !pReader ){ - return SQLITE_NOMEM; - } - memset(pReader, 0, sizeof(Fts3SegReader)); - pReader->iIdx = iAge; - pReader->bLookup = bLookup!=0; - pReader->iStartBlock = iStartLeaf; - pReader->iLeafEndBlock = iEndLeaf; - pReader->iEndBlock = iEndBlock; - - if( nExtra ){ - /* The entire segment is stored in the root node. */ - pReader->aNode = (char *)&pReader[1]; - pReader->rootOnly = 1; - pReader->nNode = nRoot; - memcpy(pReader->aNode, zRoot, nRoot); - memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING); - }else{ - pReader->iCurrentBlock = iStartLeaf-1; - } - *ppReader = pReader; - return SQLITE_OK; -} - -/* -** This is a comparison function used as a qsort() callback when sorting -** an array of pending terms by term. This occurs as part of flushing -** the contents of the pending-terms hash table to the database. -*/ -static int fts3CompareElemByTerm(const void *lhs, const void *rhs){ - char *z1 = fts3HashKey(*(Fts3HashElem **)lhs); - char *z2 = fts3HashKey(*(Fts3HashElem **)rhs); - int n1 = fts3HashKeysize(*(Fts3HashElem **)lhs); - int n2 = fts3HashKeysize(*(Fts3HashElem **)rhs); - - int n = (n1aIndex */ - const char *zTerm, /* Term to search for */ - int nTerm, /* Size of buffer zTerm */ - int bPrefix, /* True for a prefix iterator */ - Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */ -){ - Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */ - Fts3HashElem *pE; /* Iterator variable */ - Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */ - int nElem = 0; /* Size of array at aElem */ - int rc = SQLITE_OK; /* Return Code */ - Fts3Hash *pHash; - - pHash = &p->aIndex[iIndex].hPending; - if( bPrefix ){ - int nAlloc = 0; /* Size of allocated array at aElem */ - - for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){ - char *zKey = (char *)fts3HashKey(pE); - int nKey = fts3HashKeysize(pE); - if( nTerm==0 || (nKey>=nTerm && 0==memcmp(zKey, zTerm, nTerm)) ){ - if( nElem==nAlloc ){ - Fts3HashElem **aElem2; - nAlloc += 16; - aElem2 = (Fts3HashElem **)sqlite3_realloc( - aElem, nAlloc*sizeof(Fts3HashElem *) - ); - if( !aElem2 ){ - rc = SQLITE_NOMEM; - nElem = 0; - break; - } - aElem = aElem2; - } - - aElem[nElem++] = pE; - } - } - - /* If more than one term matches the prefix, sort the Fts3HashElem - ** objects in term order using qsort(). This uses the same comparison - ** callback as is used when flushing terms to disk. - */ - if( nElem>1 ){ - qsort(aElem, nElem, sizeof(Fts3HashElem *), fts3CompareElemByTerm); - } - - }else{ - /* The query is a simple term lookup that matches at most one term in - ** the index. All that is required is a straight hash-lookup. - ** - ** Because the stack address of pE may be accessed via the aElem pointer - ** below, the "Fts3HashElem *pE" must be declared so that it is valid - ** within this entire function, not just this "else{...}" block. - */ - pE = fts3HashFindElem(pHash, zTerm, nTerm); - if( pE ){ - aElem = &pE; - nElem = 1; - } - } - - if( nElem>0 ){ - int nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *); - pReader = (Fts3SegReader *)sqlite3_malloc(nByte); - if( !pReader ){ - rc = SQLITE_NOMEM; - }else{ - memset(pReader, 0, nByte); - pReader->iIdx = 0x7FFFFFFF; - pReader->ppNextElem = (Fts3HashElem **)&pReader[1]; - memcpy(pReader->ppNextElem, aElem, nElem*sizeof(Fts3HashElem *)); - } - } - - if( bPrefix ){ - sqlite3_free(aElem); - } - *ppReader = pReader; - return rc; -} - -/* -** Compare the entries pointed to by two Fts3SegReader structures. -** Comparison is as follows: -** -** 1) EOF is greater than not EOF. -** -** 2) The current terms (if any) are compared using memcmp(). If one -** term is a prefix of another, the longer term is considered the -** larger. -** -** 3) By segment age. An older segment is considered larger. -*/ -static int fts3SegReaderCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ - int rc; - if( pLhs->aNode && pRhs->aNode ){ - int rc2 = pLhs->nTerm - pRhs->nTerm; - if( rc2<0 ){ - rc = memcmp(pLhs->zTerm, pRhs->zTerm, pLhs->nTerm); - }else{ - rc = memcmp(pLhs->zTerm, pRhs->zTerm, pRhs->nTerm); - } - if( rc==0 ){ - rc = rc2; - } - }else{ - rc = (pLhs->aNode==0) - (pRhs->aNode==0); - } - if( rc==0 ){ - rc = pRhs->iIdx - pLhs->iIdx; - } - assert( rc!=0 ); - return rc; -} - -/* -** A different comparison function for SegReader structures. In this -** version, it is assumed that each SegReader points to an entry in -** a doclist for identical terms. Comparison is made as follows: -** -** 1) EOF (end of doclist in this case) is greater than not EOF. -** -** 2) By current docid. -** -** 3) By segment age. An older segment is considered larger. -*/ -static int fts3SegReaderDoclistCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ - int rc = (pLhs->pOffsetList==0)-(pRhs->pOffsetList==0); - if( rc==0 ){ - if( pLhs->iDocid==pRhs->iDocid ){ - rc = pRhs->iIdx - pLhs->iIdx; - }else{ - rc = (pLhs->iDocid > pRhs->iDocid) ? 1 : -1; - } - } - assert( pLhs->aNode && pRhs->aNode ); - return rc; -} -static int fts3SegReaderDoclistCmpRev(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ - int rc = (pLhs->pOffsetList==0)-(pRhs->pOffsetList==0); - if( rc==0 ){ - if( pLhs->iDocid==pRhs->iDocid ){ - rc = pRhs->iIdx - pLhs->iIdx; - }else{ - rc = (pLhs->iDocid < pRhs->iDocid) ? 1 : -1; - } - } - assert( pLhs->aNode && pRhs->aNode ); - return rc; -} - -/* -** Compare the term that the Fts3SegReader object passed as the first argument -** points to with the term specified by arguments zTerm and nTerm. -** -** If the pSeg iterator is already at EOF, return 0. Otherwise, return -** -ve if the pSeg term is less than zTerm/nTerm, 0 if the two terms are -** equal, or +ve if the pSeg term is greater than zTerm/nTerm. -*/ -static int fts3SegReaderTermCmp( - Fts3SegReader *pSeg, /* Segment reader object */ - const char *zTerm, /* Term to compare to */ - int nTerm /* Size of term zTerm in bytes */ -){ - int res = 0; - if( pSeg->aNode ){ - if( pSeg->nTerm>nTerm ){ - res = memcmp(pSeg->zTerm, zTerm, nTerm); - }else{ - res = memcmp(pSeg->zTerm, zTerm, pSeg->nTerm); - } - if( res==0 ){ - res = pSeg->nTerm-nTerm; - } - } - return res; -} - -/* -** Argument apSegment is an array of nSegment elements. It is known that -** the final (nSegment-nSuspect) members are already in sorted order -** (according to the comparison function provided). This function shuffles -** the array around until all entries are in sorted order. -*/ -static void fts3SegReaderSort( - Fts3SegReader **apSegment, /* Array to sort entries of */ - int nSegment, /* Size of apSegment array */ - int nSuspect, /* Unsorted entry count */ - int (*xCmp)(Fts3SegReader *, Fts3SegReader *) /* Comparison function */ -){ - int i; /* Iterator variable */ - - assert( nSuspect<=nSegment ); - - if( nSuspect==nSegment ) nSuspect--; - for(i=nSuspect-1; i>=0; i--){ - int j; - for(j=i; j<(nSegment-1); j++){ - Fts3SegReader *pTmp; - if( xCmp(apSegment[j], apSegment[j+1])<0 ) break; - pTmp = apSegment[j+1]; - apSegment[j+1] = apSegment[j]; - apSegment[j] = pTmp; - } - } - -#ifndef NDEBUG - /* Check that the list really is sorted now. */ - for(i=0; i<(nSuspect-1); i++){ - assert( xCmp(apSegment[i], apSegment[i+1])<0 ); - } -#endif -} - -/* -** Insert a record into the %_segments table. -*/ -static int fts3WriteSegment( - Fts3Table *p, /* Virtual table handle */ - sqlite3_int64 iBlock, /* Block id for new block */ - char *z, /* Pointer to buffer containing block data */ - int n /* Size of buffer z in bytes */ -){ - sqlite3_stmt *pStmt; - int rc = fts3SqlStmt(p, SQL_INSERT_SEGMENTS, &pStmt, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pStmt, 1, iBlock); - sqlite3_bind_blob(pStmt, 2, z, n, SQLITE_STATIC); - sqlite3_step(pStmt); - rc = sqlite3_reset(pStmt); - } - return rc; -} - -/* -** Find the largest relative level number in the table. If successful, set -** *pnMax to this value and return SQLITE_OK. Otherwise, if an error occurs, -** set *pnMax to zero and return an SQLite error code. -*/ -SQLITE_PRIVATE int sqlite3Fts3MaxLevel(Fts3Table *p, int *pnMax){ - int rc; - int mxLevel = 0; - sqlite3_stmt *pStmt = 0; - - rc = fts3SqlStmt(p, SQL_SELECT_MXLEVEL, &pStmt, 0); - if( rc==SQLITE_OK ){ - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - mxLevel = sqlite3_column_int(pStmt, 0); - } - rc = sqlite3_reset(pStmt); - } - *pnMax = mxLevel; - return rc; -} - -/* -** Insert a record into the %_segdir table. -*/ -static int fts3WriteSegdir( - Fts3Table *p, /* Virtual table handle */ - sqlite3_int64 iLevel, /* Value for "level" field (absolute level) */ - int iIdx, /* Value for "idx" field */ - sqlite3_int64 iStartBlock, /* Value for "start_block" field */ - sqlite3_int64 iLeafEndBlock, /* Value for "leaves_end_block" field */ - sqlite3_int64 iEndBlock, /* Value for "end_block" field */ - char *zRoot, /* Blob value for "root" field */ - int nRoot /* Number of bytes in buffer zRoot */ -){ - sqlite3_stmt *pStmt; - int rc = fts3SqlStmt(p, SQL_INSERT_SEGDIR, &pStmt, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pStmt, 1, iLevel); - sqlite3_bind_int(pStmt, 2, iIdx); - sqlite3_bind_int64(pStmt, 3, iStartBlock); - sqlite3_bind_int64(pStmt, 4, iLeafEndBlock); - sqlite3_bind_int64(pStmt, 5, iEndBlock); - sqlite3_bind_blob(pStmt, 6, zRoot, nRoot, SQLITE_STATIC); - sqlite3_step(pStmt); - rc = sqlite3_reset(pStmt); - } - return rc; -} - -/* -** Return the size of the common prefix (if any) shared by zPrev and -** zNext, in bytes. For example, -** -** fts3PrefixCompress("abc", 3, "abcdef", 6) // returns 3 -** fts3PrefixCompress("abX", 3, "abcdef", 6) // returns 2 -** fts3PrefixCompress("abX", 3, "Xbcdef", 6) // returns 0 -*/ -static int fts3PrefixCompress( - const char *zPrev, /* Buffer containing previous term */ - int nPrev, /* Size of buffer zPrev in bytes */ - const char *zNext, /* Buffer containing next term */ - int nNext /* Size of buffer zNext in bytes */ -){ - int n; - UNUSED_PARAMETER(nNext); - for(n=0; nnData; /* Current size of node in bytes */ - int nReq = nData; /* Required space after adding zTerm */ - int nPrefix; /* Number of bytes of prefix compression */ - int nSuffix; /* Suffix length */ - - nPrefix = fts3PrefixCompress(pTree->zTerm, pTree->nTerm, zTerm, nTerm); - nSuffix = nTerm-nPrefix; - - nReq += sqlite3Fts3VarintLen(nPrefix)+sqlite3Fts3VarintLen(nSuffix)+nSuffix; - if( nReq<=p->nNodeSize || !pTree->zTerm ){ - - if( nReq>p->nNodeSize ){ - /* An unusual case: this is the first term to be added to the node - ** and the static node buffer (p->nNodeSize bytes) is not large - ** enough. Use a separately malloced buffer instead This wastes - ** p->nNodeSize bytes, but since this scenario only comes about when - ** the database contain two terms that share a prefix of almost 2KB, - ** this is not expected to be a serious problem. - */ - assert( pTree->aData==(char *)&pTree[1] ); - pTree->aData = (char *)sqlite3_malloc(nReq); - if( !pTree->aData ){ - return SQLITE_NOMEM; - } - } - - if( pTree->zTerm ){ - /* There is no prefix-length field for first term in a node */ - nData += sqlite3Fts3PutVarint(&pTree->aData[nData], nPrefix); - } - - nData += sqlite3Fts3PutVarint(&pTree->aData[nData], nSuffix); - memcpy(&pTree->aData[nData], &zTerm[nPrefix], nSuffix); - pTree->nData = nData + nSuffix; - pTree->nEntry++; - - if( isCopyTerm ){ - if( pTree->nMalloczMalloc, nTerm*2); - if( !zNew ){ - return SQLITE_NOMEM; - } - pTree->nMalloc = nTerm*2; - pTree->zMalloc = zNew; - } - pTree->zTerm = pTree->zMalloc; - memcpy(pTree->zTerm, zTerm, nTerm); - pTree->nTerm = nTerm; - }else{ - pTree->zTerm = (char *)zTerm; - pTree->nTerm = nTerm; - } - return SQLITE_OK; - } - } - - /* If control flows to here, it was not possible to append zTerm to the - ** current node. Create a new node (a right-sibling of the current node). - ** If this is the first node in the tree, the term is added to it. - ** - ** Otherwise, the term is not added to the new node, it is left empty for - ** now. Instead, the term is inserted into the parent of pTree. If pTree - ** has no parent, one is created here. - */ - pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize); - if( !pNew ){ - return SQLITE_NOMEM; - } - memset(pNew, 0, sizeof(SegmentNode)); - pNew->nData = 1 + FTS3_VARINT_MAX; - pNew->aData = (char *)&pNew[1]; - - if( pTree ){ - SegmentNode *pParent = pTree->pParent; - rc = fts3NodeAddTerm(p, &pParent, isCopyTerm, zTerm, nTerm); - if( pTree->pParent==0 ){ - pTree->pParent = pParent; - } - pTree->pRight = pNew; - pNew->pLeftmost = pTree->pLeftmost; - pNew->pParent = pParent; - pNew->zMalloc = pTree->zMalloc; - pNew->nMalloc = pTree->nMalloc; - pTree->zMalloc = 0; - }else{ - pNew->pLeftmost = pNew; - rc = fts3NodeAddTerm(p, &pNew, isCopyTerm, zTerm, nTerm); - } - - *ppTree = pNew; - return rc; -} - -/* -** Helper function for fts3NodeWrite(). -*/ -static int fts3TreeFinishNode( - SegmentNode *pTree, - int iHeight, - sqlite3_int64 iLeftChild -){ - int nStart; - assert( iHeight>=1 && iHeight<128 ); - nStart = FTS3_VARINT_MAX - sqlite3Fts3VarintLen(iLeftChild); - pTree->aData[nStart] = (char)iHeight; - sqlite3Fts3PutVarint(&pTree->aData[nStart+1], iLeftChild); - return nStart; -} - -/* -** Write the buffer for the segment node pTree and all of its peers to the -** database. Then call this function recursively to write the parent of -** pTree and its peers to the database. -** -** Except, if pTree is a root node, do not write it to the database. Instead, -** set output variables *paRoot and *pnRoot to contain the root node. -** -** If successful, SQLITE_OK is returned and output variable *piLast is -** set to the largest blockid written to the database (or zero if no -** blocks were written to the db). Otherwise, an SQLite error code is -** returned. -*/ -static int fts3NodeWrite( - Fts3Table *p, /* Virtual table handle */ - SegmentNode *pTree, /* SegmentNode handle */ - int iHeight, /* Height of this node in tree */ - sqlite3_int64 iLeaf, /* Block id of first leaf node */ - sqlite3_int64 iFree, /* Block id of next free slot in %_segments */ - sqlite3_int64 *piLast, /* OUT: Block id of last entry written */ - char **paRoot, /* OUT: Data for root node */ - int *pnRoot /* OUT: Size of root node in bytes */ -){ - int rc = SQLITE_OK; - - if( !pTree->pParent ){ - /* Root node of the tree. */ - int nStart = fts3TreeFinishNode(pTree, iHeight, iLeaf); - *piLast = iFree-1; - *pnRoot = pTree->nData - nStart; - *paRoot = &pTree->aData[nStart]; - }else{ - SegmentNode *pIter; - sqlite3_int64 iNextFree = iFree; - sqlite3_int64 iNextLeaf = iLeaf; - for(pIter=pTree->pLeftmost; pIter && rc==SQLITE_OK; pIter=pIter->pRight){ - int nStart = fts3TreeFinishNode(pIter, iHeight, iNextLeaf); - int nWrite = pIter->nData - nStart; - - rc = fts3WriteSegment(p, iNextFree, &pIter->aData[nStart], nWrite); - iNextFree++; - iNextLeaf += (pIter->nEntry+1); - } - if( rc==SQLITE_OK ){ - assert( iNextLeaf==iFree ); - rc = fts3NodeWrite( - p, pTree->pParent, iHeight+1, iFree, iNextFree, piLast, paRoot, pnRoot - ); - } - } - - return rc; -} - -/* -** Free all memory allocations associated with the tree pTree. -*/ -static void fts3NodeFree(SegmentNode *pTree){ - if( pTree ){ - SegmentNode *p = pTree->pLeftmost; - fts3NodeFree(p->pParent); - while( p ){ - SegmentNode *pRight = p->pRight; - if( p->aData!=(char *)&p[1] ){ - sqlite3_free(p->aData); - } - assert( pRight==0 || p->zMalloc==0 ); - sqlite3_free(p->zMalloc); - sqlite3_free(p); - p = pRight; - } - } -} - -/* -** Add a term to the segment being constructed by the SegmentWriter object -** *ppWriter. When adding the first term to a segment, *ppWriter should -** be passed NULL. This function will allocate a new SegmentWriter object -** and return it via the input/output variable *ppWriter in this case. -** -** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. -*/ -static int fts3SegWriterAdd( - Fts3Table *p, /* Virtual table handle */ - SegmentWriter **ppWriter, /* IN/OUT: SegmentWriter handle */ - int isCopyTerm, /* True if buffer zTerm must be copied */ - const char *zTerm, /* Pointer to buffer containing term */ - int nTerm, /* Size of term in bytes */ - const char *aDoclist, /* Pointer to buffer containing doclist */ - int nDoclist /* Size of doclist in bytes */ -){ - int nPrefix; /* Size of term prefix in bytes */ - int nSuffix; /* Size of term suffix in bytes */ - int nReq; /* Number of bytes required on leaf page */ - int nData; - SegmentWriter *pWriter = *ppWriter; - - if( !pWriter ){ - int rc; - sqlite3_stmt *pStmt; - - /* Allocate the SegmentWriter structure */ - pWriter = (SegmentWriter *)sqlite3_malloc(sizeof(SegmentWriter)); - if( !pWriter ) return SQLITE_NOMEM; - memset(pWriter, 0, sizeof(SegmentWriter)); - *ppWriter = pWriter; - - /* Allocate a buffer in which to accumulate data */ - pWriter->aData = (char *)sqlite3_malloc(p->nNodeSize); - if( !pWriter->aData ) return SQLITE_NOMEM; - pWriter->nSize = p->nNodeSize; - - /* Find the next free blockid in the %_segments table */ - rc = fts3SqlStmt(p, SQL_NEXT_SEGMENTS_ID, &pStmt, 0); - if( rc!=SQLITE_OK ) return rc; - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - pWriter->iFree = sqlite3_column_int64(pStmt, 0); - pWriter->iFirst = pWriter->iFree; - } - rc = sqlite3_reset(pStmt); - if( rc!=SQLITE_OK ) return rc; - } - nData = pWriter->nData; - - nPrefix = fts3PrefixCompress(pWriter->zTerm, pWriter->nTerm, zTerm, nTerm); - nSuffix = nTerm-nPrefix; - - /* Figure out how many bytes are required by this new entry */ - nReq = sqlite3Fts3VarintLen(nPrefix) + /* varint containing prefix size */ - sqlite3Fts3VarintLen(nSuffix) + /* varint containing suffix size */ - nSuffix + /* Term suffix */ - sqlite3Fts3VarintLen(nDoclist) + /* Size of doclist */ - nDoclist; /* Doclist data */ - - if( nData>0 && nData+nReq>p->nNodeSize ){ - int rc; - - /* The current leaf node is full. Write it out to the database. */ - rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, nData); - if( rc!=SQLITE_OK ) return rc; - p->nLeafAdd++; - - /* Add the current term to the interior node tree. The term added to - ** the interior tree must: - ** - ** a) be greater than the largest term on the leaf node just written - ** to the database (still available in pWriter->zTerm), and - ** - ** b) be less than or equal to the term about to be added to the new - ** leaf node (zTerm/nTerm). - ** - ** In other words, it must be the prefix of zTerm 1 byte longer than - ** the common prefix (if any) of zTerm and pWriter->zTerm. - */ - assert( nPrefixpTree, isCopyTerm, zTerm, nPrefix+1); - if( rc!=SQLITE_OK ) return rc; - - nData = 0; - pWriter->nTerm = 0; - - nPrefix = 0; - nSuffix = nTerm; - nReq = 1 + /* varint containing prefix size */ - sqlite3Fts3VarintLen(nTerm) + /* varint containing suffix size */ - nTerm + /* Term suffix */ - sqlite3Fts3VarintLen(nDoclist) + /* Size of doclist */ - nDoclist; /* Doclist data */ - } - - /* If the buffer currently allocated is too small for this entry, realloc - ** the buffer to make it large enough. - */ - if( nReq>pWriter->nSize ){ - char *aNew = sqlite3_realloc(pWriter->aData, nReq); - if( !aNew ) return SQLITE_NOMEM; - pWriter->aData = aNew; - pWriter->nSize = nReq; - } - assert( nData+nReq<=pWriter->nSize ); - - /* Append the prefix-compressed term and doclist to the buffer. */ - nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nPrefix); - nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nSuffix); - memcpy(&pWriter->aData[nData], &zTerm[nPrefix], nSuffix); - nData += nSuffix; - nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nDoclist); - memcpy(&pWriter->aData[nData], aDoclist, nDoclist); - pWriter->nData = nData + nDoclist; - - /* Save the current term so that it can be used to prefix-compress the next. - ** If the isCopyTerm parameter is true, then the buffer pointed to by - ** zTerm is transient, so take a copy of the term data. Otherwise, just - ** store a copy of the pointer. - */ - if( isCopyTerm ){ - if( nTerm>pWriter->nMalloc ){ - char *zNew = sqlite3_realloc(pWriter->zMalloc, nTerm*2); - if( !zNew ){ - return SQLITE_NOMEM; - } - pWriter->nMalloc = nTerm*2; - pWriter->zMalloc = zNew; - pWriter->zTerm = zNew; - } - assert( pWriter->zTerm==pWriter->zMalloc ); - memcpy(pWriter->zTerm, zTerm, nTerm); - }else{ - pWriter->zTerm = (char *)zTerm; - } - pWriter->nTerm = nTerm; - - return SQLITE_OK; -} - -/* -** Flush all data associated with the SegmentWriter object pWriter to the -** database. This function must be called after all terms have been added -** to the segment using fts3SegWriterAdd(). If successful, SQLITE_OK is -** returned. Otherwise, an SQLite error code. -*/ -static int fts3SegWriterFlush( - Fts3Table *p, /* Virtual table handle */ - SegmentWriter *pWriter, /* SegmentWriter to flush to the db */ - sqlite3_int64 iLevel, /* Value for 'level' column of %_segdir */ - int iIdx /* Value for 'idx' column of %_segdir */ -){ - int rc; /* Return code */ - if( pWriter->pTree ){ - sqlite3_int64 iLast = 0; /* Largest block id written to database */ - sqlite3_int64 iLastLeaf; /* Largest leaf block id written to db */ - char *zRoot = NULL; /* Pointer to buffer containing root node */ - int nRoot = 0; /* Size of buffer zRoot */ - - iLastLeaf = pWriter->iFree; - rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, pWriter->nData); - if( rc==SQLITE_OK ){ - rc = fts3NodeWrite(p, pWriter->pTree, 1, - pWriter->iFirst, pWriter->iFree, &iLast, &zRoot, &nRoot); - } - if( rc==SQLITE_OK ){ - rc = fts3WriteSegdir( - p, iLevel, iIdx, pWriter->iFirst, iLastLeaf, iLast, zRoot, nRoot); - } - }else{ - /* The entire tree fits on the root node. Write it to the segdir table. */ - rc = fts3WriteSegdir( - p, iLevel, iIdx, 0, 0, 0, pWriter->aData, pWriter->nData); - } - p->nLeafAdd++; - return rc; -} - -/* -** Release all memory held by the SegmentWriter object passed as the -** first argument. -*/ -static void fts3SegWriterFree(SegmentWriter *pWriter){ - if( pWriter ){ - sqlite3_free(pWriter->aData); - sqlite3_free(pWriter->zMalloc); - fts3NodeFree(pWriter->pTree); - sqlite3_free(pWriter); - } -} - -/* -** The first value in the apVal[] array is assumed to contain an integer. -** This function tests if there exist any documents with docid values that -** are different from that integer. i.e. if deleting the document with docid -** pRowid would mean the FTS3 table were empty. -** -** If successful, *pisEmpty is set to true if the table is empty except for -** document pRowid, or false otherwise, and SQLITE_OK is returned. If an -** error occurs, an SQLite error code is returned. -*/ -static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){ - sqlite3_stmt *pStmt; - int rc; - if( p->zContentTbl ){ - /* If using the content=xxx option, assume the table is never empty */ - *pisEmpty = 0; - rc = SQLITE_OK; - }else{ - rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid); - if( rc==SQLITE_OK ){ - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - *pisEmpty = sqlite3_column_int(pStmt, 0); - } - rc = sqlite3_reset(pStmt); - } - } - return rc; -} - -/* -** Set *pnMax to the largest segment level in the database for the index -** iIndex. -** -** Segment levels are stored in the 'level' column of the %_segdir table. -** -** Return SQLITE_OK if successful, or an SQLite error code if not. -*/ -static int fts3SegmentMaxLevel( - Fts3Table *p, - int iLangid, - int iIndex, - sqlite3_int64 *pnMax -){ - sqlite3_stmt *pStmt; - int rc; - assert( iIndex>=0 && iIndexnIndex ); - - /* Set pStmt to the compiled version of: - ** - ** SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ? - ** - ** (1024 is actually the value of macro FTS3_SEGDIR_PREFIXLEVEL_STR). - */ - rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0); - if( rc!=SQLITE_OK ) return rc; - sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, 0)); - sqlite3_bind_int64(pStmt, 2, - getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1) - ); - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - *pnMax = sqlite3_column_int64(pStmt, 0); - } - return sqlite3_reset(pStmt); -} - -/* -** Delete all entries in the %_segments table associated with the segment -** opened with seg-reader pSeg. This function does not affect the contents -** of the %_segdir table. -*/ -static int fts3DeleteSegment( - Fts3Table *p, /* FTS table handle */ - Fts3SegReader *pSeg /* Segment to delete */ -){ - int rc = SQLITE_OK; /* Return code */ - if( pSeg->iStartBlock ){ - sqlite3_stmt *pDelete; /* SQL statement to delete rows */ - rc = fts3SqlStmt(p, SQL_DELETE_SEGMENTS_RANGE, &pDelete, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pDelete, 1, pSeg->iStartBlock); - sqlite3_bind_int64(pDelete, 2, pSeg->iEndBlock); - sqlite3_step(pDelete); - rc = sqlite3_reset(pDelete); - } - } - return rc; -} - -/* -** This function is used after merging multiple segments into a single large -** segment to delete the old, now redundant, segment b-trees. Specifically, -** it: -** -** 1) Deletes all %_segments entries for the segments associated with -** each of the SegReader objects in the array passed as the third -** argument, and -** -** 2) deletes all %_segdir entries with level iLevel, or all %_segdir -** entries regardless of level if (iLevel<0). -** -** SQLITE_OK is returned if successful, otherwise an SQLite error code. -*/ -static int fts3DeleteSegdir( - Fts3Table *p, /* Virtual table handle */ - int iLangid, /* Language id */ - int iIndex, /* Index for p->aIndex */ - int iLevel, /* Level of %_segdir entries to delete */ - Fts3SegReader **apSegment, /* Array of SegReader objects */ - int nReader /* Size of array apSegment */ -){ - int rc = SQLITE_OK; /* Return Code */ - int i; /* Iterator variable */ - sqlite3_stmt *pDelete = 0; /* SQL statement to delete rows */ - - for(i=0; rc==SQLITE_OK && i=0 || iLevel==FTS3_SEGCURSOR_ALL ); - if( iLevel==FTS3_SEGCURSOR_ALL ){ - rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_RANGE, &pDelete, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pDelete, 1, getAbsoluteLevel(p, iLangid, iIndex, 0)); - sqlite3_bind_int64(pDelete, 2, - getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1) - ); - } - }else{ - rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pDelete, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64( - pDelete, 1, getAbsoluteLevel(p, iLangid, iIndex, iLevel) - ); - } - } - - if( rc==SQLITE_OK ){ - sqlite3_step(pDelete); - rc = sqlite3_reset(pDelete); - } - - return rc; -} - -/* -** When this function is called, buffer *ppList (size *pnList bytes) contains -** a position list that may (or may not) feature multiple columns. This -** function adjusts the pointer *ppList and the length *pnList so that they -** identify the subset of the position list that corresponds to column iCol. -** -** If there are no entries in the input position list for column iCol, then -** *pnList is set to zero before returning. -** -** If parameter bZero is non-zero, then any part of the input list following -** the end of the output list is zeroed before returning. -*/ -static void fts3ColumnFilter( - int iCol, /* Column to filter on */ - int bZero, /* Zero out anything following *ppList */ - char **ppList, /* IN/OUT: Pointer to position list */ - int *pnList /* IN/OUT: Size of buffer *ppList in bytes */ -){ - char *pList = *ppList; - int nList = *pnList; - char *pEnd = &pList[nList]; - int iCurrent = 0; - char *p = pList; - - assert( iCol>=0 ); - while( 1 ){ - char c = 0; - while( ppMsr->nBuffer ){ - char *pNew; - pMsr->nBuffer = nList*2; - pNew = (char *)sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer); - if( !pNew ) return SQLITE_NOMEM; - pMsr->aBuffer = pNew; - } - - memcpy(pMsr->aBuffer, pList, nList); - return SQLITE_OK; -} - -SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext( - Fts3Table *p, /* Virtual table handle */ - Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */ - sqlite3_int64 *piDocid, /* OUT: Docid value */ - char **paPoslist, /* OUT: Pointer to position list */ - int *pnPoslist /* OUT: Size of position list in bytes */ -){ - int nMerge = pMsr->nAdvance; - Fts3SegReader **apSegment = pMsr->apSegment; - int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = ( - p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp - ); - - if( nMerge==0 ){ - *paPoslist = 0; - return SQLITE_OK; - } - - while( 1 ){ - Fts3SegReader *pSeg; - pSeg = pMsr->apSegment[0]; - - if( pSeg->pOffsetList==0 ){ - *paPoslist = 0; - break; - }else{ - int rc; - char *pList; - int nList; - int j; - sqlite3_int64 iDocid = apSegment[0]->iDocid; - - rc = fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList); - j = 1; - while( rc==SQLITE_OK - && jpOffsetList - && apSegment[j]->iDocid==iDocid - ){ - rc = fts3SegReaderNextDocid(p, apSegment[j], 0, 0); - j++; - } - if( rc!=SQLITE_OK ) return rc; - fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp); - - if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){ - rc = fts3MsrBufferData(pMsr, pList, nList+1); - if( rc!=SQLITE_OK ) return rc; - assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 ); - pList = pMsr->aBuffer; - } - - if( pMsr->iColFilter>=0 ){ - fts3ColumnFilter(pMsr->iColFilter, 1, &pList, &nList); - } - - if( nList>0 ){ - *paPoslist = pList; - *piDocid = iDocid; - *pnPoslist = nList; - break; - } - } - } - - return SQLITE_OK; -} - -static int fts3SegReaderStart( - Fts3Table *p, /* Virtual table handle */ - Fts3MultiSegReader *pCsr, /* Cursor object */ - const char *zTerm, /* Term searched for (or NULL) */ - int nTerm /* Length of zTerm in bytes */ -){ - int i; - int nSeg = pCsr->nSegment; - - /* If the Fts3SegFilter defines a specific term (or term prefix) to search - ** for, then advance each segment iterator until it points to a term of - ** equal or greater value than the specified term. This prevents many - ** unnecessary merge/sort operations for the case where single segment - ** b-tree leaf nodes contain more than one term. - */ - for(i=0; pCsr->bRestart==0 && inSegment; i++){ - int res = 0; - Fts3SegReader *pSeg = pCsr->apSegment[i]; - do { - int rc = fts3SegReaderNext(p, pSeg, 0); - if( rc!=SQLITE_OK ) return rc; - }while( zTerm && (res = fts3SegReaderTermCmp(pSeg, zTerm, nTerm))<0 ); - - if( pSeg->bLookup && res!=0 ){ - fts3SegReaderSetEof(pSeg); - } - } - fts3SegReaderSort(pCsr->apSegment, nSeg, nSeg, fts3SegReaderCmp); - - return SQLITE_OK; -} - -SQLITE_PRIVATE int sqlite3Fts3SegReaderStart( - Fts3Table *p, /* Virtual table handle */ - Fts3MultiSegReader *pCsr, /* Cursor object */ - Fts3SegFilter *pFilter /* Restrictions on range of iteration */ -){ - pCsr->pFilter = pFilter; - return fts3SegReaderStart(p, pCsr, pFilter->zTerm, pFilter->nTerm); -} - -SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart( - Fts3Table *p, /* Virtual table handle */ - Fts3MultiSegReader *pCsr, /* Cursor object */ - int iCol, /* Column to match on. */ - const char *zTerm, /* Term to iterate through a doclist for */ - int nTerm /* Number of bytes in zTerm */ -){ - int i; - int rc; - int nSegment = pCsr->nSegment; - int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = ( - p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp - ); - - assert( pCsr->pFilter==0 ); - assert( zTerm && nTerm>0 ); - - /* Advance each segment iterator until it points to the term zTerm/nTerm. */ - rc = fts3SegReaderStart(p, pCsr, zTerm, nTerm); - if( rc!=SQLITE_OK ) return rc; - - /* Determine how many of the segments actually point to zTerm/nTerm. */ - for(i=0; iapSegment[i]; - if( !pSeg->aNode || fts3SegReaderTermCmp(pSeg, zTerm, nTerm) ){ - break; - } - } - pCsr->nAdvance = i; - - /* Advance each of the segments to point to the first docid. */ - for(i=0; inAdvance; i++){ - rc = fts3SegReaderFirstDocid(p, pCsr->apSegment[i]); - if( rc!=SQLITE_OK ) return rc; - } - fts3SegReaderSort(pCsr->apSegment, i, i, xCmp); - - assert( iCol<0 || iColnColumn ); - pCsr->iColFilter = iCol; - - return SQLITE_OK; -} - -/* -** This function is called on a MultiSegReader that has been started using -** sqlite3Fts3MsrIncrStart(). One or more calls to MsrIncrNext() may also -** have been made. Calling this function puts the MultiSegReader in such -** a state that if the next two calls are: -** -** sqlite3Fts3SegReaderStart() -** sqlite3Fts3SegReaderStep() -** -** then the entire doclist for the term is available in -** MultiSegReader.aDoclist/nDoclist. -*/ -SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){ - int i; /* Used to iterate through segment-readers */ - - assert( pCsr->zTerm==0 ); - assert( pCsr->nTerm==0 ); - assert( pCsr->aDoclist==0 ); - assert( pCsr->nDoclist==0 ); - - pCsr->nAdvance = 0; - pCsr->bRestart = 1; - for(i=0; inSegment; i++){ - pCsr->apSegment[i]->pOffsetList = 0; - pCsr->apSegment[i]->nOffsetList = 0; - pCsr->apSegment[i]->iDocid = 0; - } - - return SQLITE_OK; -} - - -SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( - Fts3Table *p, /* Virtual table handle */ - Fts3MultiSegReader *pCsr /* Cursor object */ -){ - int rc = SQLITE_OK; - - int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY); - int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS); - int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER); - int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX); - int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN); - int isFirst = (pCsr->pFilter->flags & FTS3_SEGMENT_FIRST); - - Fts3SegReader **apSegment = pCsr->apSegment; - int nSegment = pCsr->nSegment; - Fts3SegFilter *pFilter = pCsr->pFilter; - int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = ( - p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp - ); - - if( pCsr->nSegment==0 ) return SQLITE_OK; - - do { - int nMerge; - int i; - - /* Advance the first pCsr->nAdvance entries in the apSegment[] array - ** forward. Then sort the list in order of current term again. - */ - for(i=0; inAdvance; i++){ - Fts3SegReader *pSeg = apSegment[i]; - if( pSeg->bLookup ){ - fts3SegReaderSetEof(pSeg); - }else{ - rc = fts3SegReaderNext(p, pSeg, 0); - } - if( rc!=SQLITE_OK ) return rc; - } - fts3SegReaderSort(apSegment, nSegment, pCsr->nAdvance, fts3SegReaderCmp); - pCsr->nAdvance = 0; - - /* If all the seg-readers are at EOF, we're finished. return SQLITE_OK. */ - assert( rc==SQLITE_OK ); - if( apSegment[0]->aNode==0 ) break; - - pCsr->nTerm = apSegment[0]->nTerm; - pCsr->zTerm = apSegment[0]->zTerm; - - /* If this is a prefix-search, and if the term that apSegment[0] points - ** to does not share a suffix with pFilter->zTerm/nTerm, then all - ** required callbacks have been made. In this case exit early. - ** - ** Similarly, if this is a search for an exact match, and the first term - ** of segment apSegment[0] is not a match, exit early. - */ - if( pFilter->zTerm && !isScan ){ - if( pCsr->nTermnTerm - || (!isPrefix && pCsr->nTerm>pFilter->nTerm) - || memcmp(pCsr->zTerm, pFilter->zTerm, pFilter->nTerm) - ){ - break; - } - } - - nMerge = 1; - while( nMergeaNode - && apSegment[nMerge]->nTerm==pCsr->nTerm - && 0==memcmp(pCsr->zTerm, apSegment[nMerge]->zTerm, pCsr->nTerm) - ){ - nMerge++; - } - - assert( isIgnoreEmpty || (isRequirePos && !isColFilter) ); - if( nMerge==1 - && !isIgnoreEmpty - && !isFirst - && (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0) - ){ - pCsr->nDoclist = apSegment[0]->nDoclist; - if( fts3SegReaderIsPending(apSegment[0]) ){ - rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist); - pCsr->aDoclist = pCsr->aBuffer; - }else{ - pCsr->aDoclist = apSegment[0]->aDoclist; - } - if( rc==SQLITE_OK ) rc = SQLITE_ROW; - }else{ - int nDoclist = 0; /* Size of doclist */ - sqlite3_int64 iPrev = 0; /* Previous docid stored in doclist */ - - /* The current term of the first nMerge entries in the array - ** of Fts3SegReader objects is the same. The doclists must be merged - ** and a single term returned with the merged doclist. - */ - for(i=0; ipOffsetList ){ - int j; /* Number of segments that share a docid */ - char *pList; - int nList; - int nByte; - sqlite3_int64 iDocid = apSegment[0]->iDocid; - fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList); - j = 1; - while( jpOffsetList - && apSegment[j]->iDocid==iDocid - ){ - fts3SegReaderNextDocid(p, apSegment[j], 0, 0); - j++; - } - - if( isColFilter ){ - fts3ColumnFilter(pFilter->iCol, 0, &pList, &nList); - } - - if( !isIgnoreEmpty || nList>0 ){ - - /* Calculate the 'docid' delta value to write into the merged - ** doclist. */ - sqlite3_int64 iDelta; - if( p->bDescIdx && nDoclist>0 ){ - iDelta = iPrev - iDocid; - }else{ - iDelta = iDocid - iPrev; - } - assert( iDelta>0 || (nDoclist==0 && iDelta==iDocid) ); - assert( nDoclist>0 || iDelta==iDocid ); - - nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); - if( nDoclist+nByte>pCsr->nBuffer ){ - char *aNew; - pCsr->nBuffer = (nDoclist+nByte)*2; - aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer); - if( !aNew ){ - return SQLITE_NOMEM; - } - pCsr->aBuffer = aNew; - } - - if( isFirst ){ - char *a = &pCsr->aBuffer[nDoclist]; - int nWrite; - - nWrite = sqlite3Fts3FirstFilter(iDelta, pList, nList, a); - if( nWrite ){ - iPrev = iDocid; - nDoclist += nWrite; - } - }else{ - nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta); - iPrev = iDocid; - if( isRequirePos ){ - memcpy(&pCsr->aBuffer[nDoclist], pList, nList); - nDoclist += nList; - pCsr->aBuffer[nDoclist++] = '\0'; - } - } - } - - fts3SegReaderSort(apSegment, nMerge, j, xCmp); - } - if( nDoclist>0 ){ - pCsr->aDoclist = pCsr->aBuffer; - pCsr->nDoclist = nDoclist; - rc = SQLITE_ROW; - } - } - pCsr->nAdvance = nMerge; - }while( rc==SQLITE_OK ); - - return rc; -} - - -SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish( - Fts3MultiSegReader *pCsr /* Cursor object */ -){ - if( pCsr ){ - int i; - for(i=0; inSegment; i++){ - sqlite3Fts3SegReaderFree(pCsr->apSegment[i]); - } - sqlite3_free(pCsr->apSegment); - sqlite3_free(pCsr->aBuffer); - - pCsr->nSegment = 0; - pCsr->apSegment = 0; - pCsr->aBuffer = 0; - } -} - -/* -** Merge all level iLevel segments in the database into a single -** iLevel+1 segment. Or, if iLevel<0, merge all segments into a -** single segment with a level equal to the numerically largest level -** currently present in the database. -** -** If this function is called with iLevel<0, but there is only one -** segment in the database, SQLITE_DONE is returned immediately. -** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, -** an SQLite error code is returned. -*/ -static int fts3SegmentMerge( - Fts3Table *p, - int iLangid, /* Language id to merge */ - int iIndex, /* Index in p->aIndex[] to merge */ - int iLevel /* Level to merge */ -){ - int rc; /* Return code */ - int iIdx = 0; /* Index of new segment */ - sqlite3_int64 iNewLevel = 0; /* Level/index to create new segment at */ - SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */ - Fts3SegFilter filter; /* Segment term filter condition */ - Fts3MultiSegReader csr; /* Cursor to iterate through level(s) */ - int bIgnoreEmpty = 0; /* True to ignore empty segments */ - - assert( iLevel==FTS3_SEGCURSOR_ALL - || iLevel==FTS3_SEGCURSOR_PENDING - || iLevel>=0 - ); - assert( iLevel=0 && iIndexnIndex ); - - rc = sqlite3Fts3SegReaderCursor(p, iLangid, iIndex, iLevel, 0, 0, 1, 0, &csr); - if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished; - - if( iLevel==FTS3_SEGCURSOR_ALL ){ - /* This call is to merge all segments in the database to a single - ** segment. The level of the new segment is equal to the numerically - ** greatest segment level currently present in the database for this - ** index. The idx of the new segment is always 0. */ - if( csr.nSegment==1 ){ - rc = SQLITE_DONE; - goto finished; - } - rc = fts3SegmentMaxLevel(p, iLangid, iIndex, &iNewLevel); - bIgnoreEmpty = 1; - - }else if( iLevel==FTS3_SEGCURSOR_PENDING ){ - iNewLevel = getAbsoluteLevel(p, iLangid, iIndex, 0); - rc = fts3AllocateSegdirIdx(p, iLangid, iIndex, 0, &iIdx); - }else{ - /* This call is to merge all segments at level iLevel. find the next - ** available segment index at level iLevel+1. The call to - ** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to - ** a single iLevel+2 segment if necessary. */ - rc = fts3AllocateSegdirIdx(p, iLangid, iIndex, iLevel+1, &iIdx); - iNewLevel = getAbsoluteLevel(p, iLangid, iIndex, iLevel+1); - } - if( rc!=SQLITE_OK ) goto finished; - assert( csr.nSegment>0 ); - assert( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) ); - assert( iNewLevelnIndex; i++){ - rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING); - if( rc==SQLITE_DONE ) rc = SQLITE_OK; - } - sqlite3Fts3PendingTermsClear(p); - - /* Determine the auto-incr-merge setting if unknown. If enabled, - ** estimate the number of leaf blocks of content to be written - */ - if( rc==SQLITE_OK && p->bHasStat - && p->bAutoincrmerge==0xff && p->nLeafAdd>0 - ){ - sqlite3_stmt *pStmt = 0; - rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pStmt, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE); - rc = sqlite3_step(pStmt); - p->bAutoincrmerge = (rc==SQLITE_ROW && sqlite3_column_int(pStmt, 0)); - rc = sqlite3_reset(pStmt); - } - } - return rc; -} - -/* -** Encode N integers as varints into a blob. -*/ -static void fts3EncodeIntArray( - int N, /* The number of integers to encode */ - u32 *a, /* The integer values */ - char *zBuf, /* Write the BLOB here */ - int *pNBuf /* Write number of bytes if zBuf[] used here */ -){ - int i, j; - for(i=j=0; iiPrevDocid. The sizes are encoded as -** a blob of varints. -*/ -static void fts3InsertDocsize( - int *pRC, /* Result code */ - Fts3Table *p, /* Table into which to insert */ - u32 *aSz /* Sizes of each column, in tokens */ -){ - char *pBlob; /* The BLOB encoding of the document size */ - int nBlob; /* Number of bytes in the BLOB */ - sqlite3_stmt *pStmt; /* Statement used to insert the encoding */ - int rc; /* Result code from subfunctions */ - - if( *pRC ) return; - pBlob = sqlite3_malloc( 10*p->nColumn ); - if( pBlob==0 ){ - *pRC = SQLITE_NOMEM; - return; - } - fts3EncodeIntArray(p->nColumn, aSz, pBlob, &nBlob); - rc = fts3SqlStmt(p, SQL_REPLACE_DOCSIZE, &pStmt, 0); - if( rc ){ - sqlite3_free(pBlob); - *pRC = rc; - return; - } - sqlite3_bind_int64(pStmt, 1, p->iPrevDocid); - sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, sqlite3_free); - sqlite3_step(pStmt); - *pRC = sqlite3_reset(pStmt); -} - -/* -** Record 0 of the %_stat table contains a blob consisting of N varints, -** where N is the number of user defined columns in the fts3 table plus -** two. If nCol is the number of user defined columns, then values of the -** varints are set as follows: -** -** Varint 0: Total number of rows in the table. -** -** Varint 1..nCol: For each column, the total number of tokens stored in -** the column for all rows of the table. -** -** Varint 1+nCol: The total size, in bytes, of all text values in all -** columns of all rows of the table. -** -*/ -static void fts3UpdateDocTotals( - int *pRC, /* The result code */ - Fts3Table *p, /* Table being updated */ - u32 *aSzIns, /* Size increases */ - u32 *aSzDel, /* Size decreases */ - int nChng /* Change in the number of documents */ -){ - char *pBlob; /* Storage for BLOB written into %_stat */ - int nBlob; /* Size of BLOB written into %_stat */ - u32 *a; /* Array of integers that becomes the BLOB */ - sqlite3_stmt *pStmt; /* Statement for reading and writing */ - int i; /* Loop counter */ - int rc; /* Result code from subfunctions */ - - const int nStat = p->nColumn+2; - - if( *pRC ) return; - a = sqlite3_malloc( (sizeof(u32)+10)*nStat ); - if( a==0 ){ - *pRC = SQLITE_NOMEM; - return; - } - pBlob = (char*)&a[nStat]; - rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pStmt, 0); - if( rc ){ - sqlite3_free(a); - *pRC = rc; - return; - } - sqlite3_bind_int(pStmt, 1, FTS_STAT_DOCTOTAL); - if( sqlite3_step(pStmt)==SQLITE_ROW ){ - fts3DecodeIntArray(nStat, a, - sqlite3_column_blob(pStmt, 0), - sqlite3_column_bytes(pStmt, 0)); - }else{ - memset(a, 0, sizeof(u32)*(nStat) ); - } - rc = sqlite3_reset(pStmt); - if( rc!=SQLITE_OK ){ - sqlite3_free(a); - *pRC = rc; - return; - } - if( nChng<0 && a[0]<(u32)(-nChng) ){ - a[0] = 0; - }else{ - a[0] += nChng; - } - for(i=0; inColumn+1; i++){ - u32 x = a[i+1]; - if( x+aSzIns[i] < aSzDel[i] ){ - x = 0; - }else{ - x = x + aSzIns[i] - aSzDel[i]; - } - a[i+1] = x; - } - fts3EncodeIntArray(nStat, a, pBlob, &nBlob); - rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pStmt, 0); - if( rc ){ - sqlite3_free(a); - *pRC = rc; - return; - } - sqlite3_bind_int(pStmt, 1, FTS_STAT_DOCTOTAL); - sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, SQLITE_STATIC); - sqlite3_step(pStmt); - *pRC = sqlite3_reset(pStmt); - sqlite3_free(a); -} - -/* -** Merge the entire database so that there is one segment for each -** iIndex/iLangid combination. -*/ -static int fts3DoOptimize(Fts3Table *p, int bReturnDone){ - int bSeenDone = 0; - int rc; - sqlite3_stmt *pAllLangid = 0; - - rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0); - if( rc==SQLITE_OK ){ - int rc2; - sqlite3_bind_int(pAllLangid, 1, p->nIndex); - while( sqlite3_step(pAllLangid)==SQLITE_ROW ){ - int i; - int iLangid = sqlite3_column_int(pAllLangid, 0); - for(i=0; rc==SQLITE_OK && inIndex; i++){ - rc = fts3SegmentMerge(p, iLangid, i, FTS3_SEGCURSOR_ALL); - if( rc==SQLITE_DONE ){ - bSeenDone = 1; - rc = SQLITE_OK; - } - } - } - rc2 = sqlite3_reset(pAllLangid); - if( rc==SQLITE_OK ) rc = rc2; - } - - sqlite3Fts3SegmentsClose(p); - sqlite3Fts3PendingTermsClear(p); - - return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc; -} - -/* -** This function is called when the user executes the following statement: -** -** INSERT INTO () VALUES('rebuild'); -** -** The entire FTS index is discarded and rebuilt. If the table is one -** created using the content=xxx option, then the new index is based on -** the current contents of the xxx table. Otherwise, it is rebuilt based -** on the contents of the %_content table. -*/ -static int fts3DoRebuild(Fts3Table *p){ - int rc; /* Return Code */ - - rc = fts3DeleteAll(p, 0); - if( rc==SQLITE_OK ){ - u32 *aSz = 0; - u32 *aSzIns = 0; - u32 *aSzDel = 0; - sqlite3_stmt *pStmt = 0; - int nEntry = 0; - - /* Compose and prepare an SQL statement to loop through the content table */ - char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist); - if( !zSql ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - } - - if( rc==SQLITE_OK ){ - int nByte = sizeof(u32) * (p->nColumn+1)*3; - aSz = (u32 *)sqlite3_malloc(nByte); - if( aSz==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(aSz, 0, nByte); - aSzIns = &aSz[p->nColumn+1]; - aSzDel = &aSzIns[p->nColumn+1]; - } - } - - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - int iCol; - int iLangid = langidFromSelect(p, pStmt); - rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pStmt, 0)); - memset(aSz, 0, sizeof(aSz[0]) * (p->nColumn+1)); - for(iCol=0; rc==SQLITE_OK && iColnColumn; iCol++){ - const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1); - rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]); - aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1); - } - if( p->bHasDocsize ){ - fts3InsertDocsize(&rc, p, aSz); - } - if( rc!=SQLITE_OK ){ - sqlite3_finalize(pStmt); - pStmt = 0; - }else{ - nEntry++; - for(iCol=0; iCol<=p->nColumn; iCol++){ - aSzIns[iCol] += aSz[iCol]; - } - } - } - if( p->bFts4 ){ - fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nEntry); - } - sqlite3_free(aSz); - - if( pStmt ){ - int rc2 = sqlite3_finalize(pStmt); - if( rc==SQLITE_OK ){ - rc = rc2; - } - } - } - - return rc; -} - - -/* -** This function opens a cursor used to read the input data for an -** incremental merge operation. Specifically, it opens a cursor to scan -** the oldest nSeg segments (idx=0 through idx=(nSeg-1)) in absolute -** level iAbsLevel. -*/ -static int fts3IncrmergeCsr( - Fts3Table *p, /* FTS3 table handle */ - sqlite3_int64 iAbsLevel, /* Absolute level to open */ - int nSeg, /* Number of segments to merge */ - Fts3MultiSegReader *pCsr /* Cursor object to populate */ -){ - int rc; /* Return Code */ - sqlite3_stmt *pStmt = 0; /* Statement used to read %_segdir entry */ - int nByte; /* Bytes allocated at pCsr->apSegment[] */ - - /* Allocate space for the Fts3MultiSegReader.aCsr[] array */ - memset(pCsr, 0, sizeof(*pCsr)); - nByte = sizeof(Fts3SegReader *) * nSeg; - pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte); - - if( pCsr->apSegment==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(pCsr->apSegment, 0, nByte); - rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0); - } - if( rc==SQLITE_OK ){ - int i; - int rc2; - sqlite3_bind_int64(pStmt, 1, iAbsLevel); - assert( pCsr->nSegment==0 ); - for(i=0; rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW && iapSegment[i] - ); - pCsr->nSegment++; - } - rc2 = sqlite3_reset(pStmt); - if( rc==SQLITE_OK ) rc = rc2; - } - - return rc; -} - -typedef struct IncrmergeWriter IncrmergeWriter; -typedef struct NodeWriter NodeWriter; -typedef struct Blob Blob; -typedef struct NodeReader NodeReader; - -/* -** An instance of the following structure is used as a dynamic buffer -** to build up nodes or other blobs of data in. -** -** The function blobGrowBuffer() is used to extend the allocation. -*/ -struct Blob { - char *a; /* Pointer to allocation */ - int n; /* Number of valid bytes of data in a[] */ - int nAlloc; /* Allocated size of a[] (nAlloc>=n) */ -}; - -/* -** This structure is used to build up buffers containing segment b-tree -** nodes (blocks). -*/ -struct NodeWriter { - sqlite3_int64 iBlock; /* Current block id */ - Blob key; /* Last key written to the current block */ - Blob block; /* Current block image */ -}; - -/* -** An object of this type contains the state required to create or append -** to an appendable b-tree segment. -*/ -struct IncrmergeWriter { - int nLeafEst; /* Space allocated for leaf blocks */ - int nWork; /* Number of leaf pages flushed */ - sqlite3_int64 iAbsLevel; /* Absolute level of input segments */ - int iIdx; /* Index of *output* segment in iAbsLevel+1 */ - sqlite3_int64 iStart; /* Block number of first allocated block */ - sqlite3_int64 iEnd; /* Block number of last allocated block */ - NodeWriter aNodeWriter[FTS_MAX_APPENDABLE_HEIGHT]; -}; - -/* -** An object of the following type is used to read data from a single -** FTS segment node. See the following functions: -** -** nodeReaderInit() -** nodeReaderNext() -** nodeReaderRelease() -*/ -struct NodeReader { - const char *aNode; - int nNode; - int iOff; /* Current offset within aNode[] */ - - /* Output variables. Containing the current node entry. */ - sqlite3_int64 iChild; /* Pointer to child node */ - Blob term; /* Current term */ - const char *aDoclist; /* Pointer to doclist */ - int nDoclist; /* Size of doclist in bytes */ -}; - -/* -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, if the allocation at pBlob->a is not already at least nMin -** bytes in size, extend (realloc) it to be so. -** -** If an OOM error occurs, set *pRc to SQLITE_NOMEM and leave pBlob->a -** unmodified. Otherwise, if the allocation succeeds, update pBlob->nAlloc -** to reflect the new size of the pBlob->a[] buffer. -*/ -static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){ - if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){ - int nAlloc = nMin; - char *a = (char *)sqlite3_realloc(pBlob->a, nAlloc); - if( a ){ - pBlob->nAlloc = nAlloc; - pBlob->a = a; - }else{ - *pRc = SQLITE_NOMEM; - } - } -} - -/* -** Attempt to advance the node-reader object passed as the first argument to -** the next entry on the node. -** -** Return an error code if an error occurs (SQLITE_NOMEM is possible). -** Otherwise return SQLITE_OK. If there is no next entry on the node -** (e.g. because the current entry is the last) set NodeReader->aNode to -** NULL to indicate EOF. Otherwise, populate the NodeReader structure output -** variables for the new entry. -*/ -static int nodeReaderNext(NodeReader *p){ - int bFirst = (p->term.n==0); /* True for first term on the node */ - int nPrefix = 0; /* Bytes to copy from previous term */ - int nSuffix = 0; /* Bytes to append to the prefix */ - int rc = SQLITE_OK; /* Return code */ - - assert( p->aNode ); - if( p->iChild && bFirst==0 ) p->iChild++; - if( p->iOff>=p->nNode ){ - /* EOF */ - p->aNode = 0; - }else{ - if( bFirst==0 ){ - p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &nPrefix); - } - p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &nSuffix); - - blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); - if( rc==SQLITE_OK ){ - memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix); - p->term.n = nPrefix+nSuffix; - p->iOff += nSuffix; - if( p->iChild==0 ){ - p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist); - p->aDoclist = &p->aNode[p->iOff]; - p->iOff += p->nDoclist; - } - } - } - - assert( p->iOff<=p->nNode ); - - return rc; -} - -/* -** Release all dynamic resources held by node-reader object *p. -*/ -static void nodeReaderRelease(NodeReader *p){ - sqlite3_free(p->term.a); -} - -/* -** Initialize a node-reader object to read the node in buffer aNode/nNode. -** -** If successful, SQLITE_OK is returned and the NodeReader object set to -** point to the first entry on the node (if any). Otherwise, an SQLite -** error code is returned. -*/ -static int nodeReaderInit(NodeReader *p, const char *aNode, int nNode){ - memset(p, 0, sizeof(NodeReader)); - p->aNode = aNode; - p->nNode = nNode; - - /* Figure out if this is a leaf or an internal node. */ - if( p->aNode[0] ){ - /* An internal node. */ - p->iOff = 1 + sqlite3Fts3GetVarint(&p->aNode[1], &p->iChild); - }else{ - p->iOff = 1; - } - - return nodeReaderNext(p); -} - -/* -** This function is called while writing an FTS segment each time a leaf o -** node is finished and written to disk. The key (zTerm/nTerm) is guaranteed -** to be greater than the largest key on the node just written, but smaller -** than or equal to the first key that will be written to the next leaf -** node. -** -** The block id of the leaf node just written to disk may be found in -** (pWriter->aNodeWriter[0].iBlock) when this function is called. -*/ -static int fts3IncrmergePush( - Fts3Table *p, /* Fts3 table handle */ - IncrmergeWriter *pWriter, /* Writer object */ - const char *zTerm, /* Term to write to internal node */ - int nTerm /* Bytes at zTerm */ -){ - sqlite3_int64 iPtr = pWriter->aNodeWriter[0].iBlock; - int iLayer; - - assert( nTerm>0 ); - for(iLayer=1; ALWAYS(iLayeraNodeWriter[iLayer]; - int rc = SQLITE_OK; - int nPrefix; - int nSuffix; - int nSpace; - - /* Figure out how much space the key will consume if it is written to - ** the current node of layer iLayer. Due to the prefix compression, - ** the space required changes depending on which node the key is to - ** be added to. */ - nPrefix = fts3PrefixCompress(pNode->key.a, pNode->key.n, zTerm, nTerm); - nSuffix = nTerm - nPrefix; - nSpace = sqlite3Fts3VarintLen(nPrefix); - nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix; - - if( pNode->key.n==0 || (pNode->block.n + nSpace)<=p->nNodeSize ){ - /* If the current node of layer iLayer contains zero keys, or if adding - ** the key to it will not cause it to grow to larger than nNodeSize - ** bytes in size, write the key here. */ - - Blob *pBlk = &pNode->block; - if( pBlk->n==0 ){ - blobGrowBuffer(pBlk, p->nNodeSize, &rc); - if( rc==SQLITE_OK ){ - pBlk->a[0] = (char)iLayer; - pBlk->n = 1 + sqlite3Fts3PutVarint(&pBlk->a[1], iPtr); - } - } - blobGrowBuffer(pBlk, pBlk->n + nSpace, &rc); - blobGrowBuffer(&pNode->key, nTerm, &rc); - - if( rc==SQLITE_OK ){ - if( pNode->key.n ){ - pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nPrefix); - } - pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nSuffix); - memcpy(&pBlk->a[pBlk->n], &zTerm[nPrefix], nSuffix); - pBlk->n += nSuffix; - - memcpy(pNode->key.a, zTerm, nTerm); - pNode->key.n = nTerm; - } - }else{ - /* Otherwise, flush the current node of layer iLayer to disk. - ** Then allocate a new, empty sibling node. The key will be written - ** into the parent of this node. */ - rc = fts3WriteSegment(p, pNode->iBlock, pNode->block.a, pNode->block.n); - - assert( pNode->block.nAlloc>=p->nNodeSize ); - pNode->block.a[0] = (char)iLayer; - pNode->block.n = 1 + sqlite3Fts3PutVarint(&pNode->block.a[1], iPtr+1); - - iNextPtr = pNode->iBlock; - pNode->iBlock++; - pNode->key.n = 0; - } - - if( rc!=SQLITE_OK || iNextPtr==0 ) return rc; - iPtr = iNextPtr; - } - - assert( 0 ); - return 0; -} - -/* -** Append a term and (optionally) doclist to the FTS segment node currently -** stored in blob *pNode. The node need not contain any terms, but the -** header must be written before this function is called. -** -** A node header is a single 0x00 byte for a leaf node, or a height varint -** followed by the left-hand-child varint for an internal node. -** -** The term to be appended is passed via arguments zTerm/nTerm. For a -** leaf node, the doclist is passed as aDoclist/nDoclist. For an internal -** node, both aDoclist and nDoclist must be passed 0. -** -** If the size of the value in blob pPrev is zero, then this is the first -** term written to the node. Otherwise, pPrev contains a copy of the -** previous term. Before this function returns, it is updated to contain a -** copy of zTerm/nTerm. -** -** It is assumed that the buffer associated with pNode is already large -** enough to accommodate the new entry. The buffer associated with pPrev -** is extended by this function if requrired. -** -** If an error (i.e. OOM condition) occurs, an SQLite error code is -** returned. Otherwise, SQLITE_OK. -*/ -static int fts3AppendToNode( - Blob *pNode, /* Current node image to append to */ - Blob *pPrev, /* Buffer containing previous term written */ - const char *zTerm, /* New term to write */ - int nTerm, /* Size of zTerm in bytes */ - const char *aDoclist, /* Doclist (or NULL) to write */ - int nDoclist /* Size of aDoclist in bytes */ -){ - int rc = SQLITE_OK; /* Return code */ - int bFirst = (pPrev->n==0); /* True if this is the first term written */ - int nPrefix; /* Size of term prefix in bytes */ - int nSuffix; /* Size of term suffix in bytes */ - - /* Node must have already been started. There must be a doclist for a - ** leaf node, and there must not be a doclist for an internal node. */ - assert( pNode->n>0 ); - assert( (pNode->a[0]=='\0')==(aDoclist!=0) ); - - blobGrowBuffer(pPrev, nTerm, &rc); - if( rc!=SQLITE_OK ) return rc; - - nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm); - nSuffix = nTerm - nPrefix; - memcpy(pPrev->a, zTerm, nTerm); - pPrev->n = nTerm; - - if( bFirst==0 ){ - pNode->n += sqlite3Fts3PutVarint(&pNode->a[pNode->n], nPrefix); - } - pNode->n += sqlite3Fts3PutVarint(&pNode->a[pNode->n], nSuffix); - memcpy(&pNode->a[pNode->n], &zTerm[nPrefix], nSuffix); - pNode->n += nSuffix; - - if( aDoclist ){ - pNode->n += sqlite3Fts3PutVarint(&pNode->a[pNode->n], nDoclist); - memcpy(&pNode->a[pNode->n], aDoclist, nDoclist); - pNode->n += nDoclist; - } - - assert( pNode->n<=pNode->nAlloc ); - - return SQLITE_OK; -} - -/* -** Append the current term and doclist pointed to by cursor pCsr to the -** appendable b-tree segment opened for writing by pWriter. -** -** Return SQLITE_OK if successful, or an SQLite error code otherwise. -*/ -static int fts3IncrmergeAppend( - Fts3Table *p, /* Fts3 table handle */ - IncrmergeWriter *pWriter, /* Writer object */ - Fts3MultiSegReader *pCsr /* Cursor containing term and doclist */ -){ - const char *zTerm = pCsr->zTerm; - int nTerm = pCsr->nTerm; - const char *aDoclist = pCsr->aDoclist; - int nDoclist = pCsr->nDoclist; - int rc = SQLITE_OK; /* Return code */ - int nSpace; /* Total space in bytes required on leaf */ - int nPrefix; /* Size of prefix shared with previous term */ - int nSuffix; /* Size of suffix (nTerm - nPrefix) */ - NodeWriter *pLeaf; /* Object used to write leaf nodes */ - - pLeaf = &pWriter->aNodeWriter[0]; - nPrefix = fts3PrefixCompress(pLeaf->key.a, pLeaf->key.n, zTerm, nTerm); - nSuffix = nTerm - nPrefix; - - nSpace = sqlite3Fts3VarintLen(nPrefix); - nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix; - nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist; - - /* If the current block is not empty, and if adding this term/doclist - ** to the current block would make it larger than Fts3Table.nNodeSize - ** bytes, write this block out to the database. */ - if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){ - rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n); - pWriter->nWork++; - - /* Add the current term to the parent node. The term added to the - ** parent must: - ** - ** a) be greater than the largest term on the leaf node just written - ** to the database (still available in pLeaf->key), and - ** - ** b) be less than or equal to the term about to be added to the new - ** leaf node (zTerm/nTerm). - ** - ** In other words, it must be the prefix of zTerm 1 byte longer than - ** the common prefix (if any) of zTerm and pWriter->zTerm. - */ - if( rc==SQLITE_OK ){ - rc = fts3IncrmergePush(p, pWriter, zTerm, nPrefix+1); - } - - /* Advance to the next output block */ - pLeaf->iBlock++; - pLeaf->key.n = 0; - pLeaf->block.n = 0; - - nSuffix = nTerm; - nSpace = 1; - nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix; - nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist; - } - - blobGrowBuffer(&pLeaf->block, pLeaf->block.n + nSpace, &rc); - - if( rc==SQLITE_OK ){ - if( pLeaf->block.n==0 ){ - pLeaf->block.n = 1; - pLeaf->block.a[0] = '\0'; - } - rc = fts3AppendToNode( - &pLeaf->block, &pLeaf->key, zTerm, nTerm, aDoclist, nDoclist - ); - } - - return rc; -} - -/* -** This function is called to release all dynamic resources held by the -** merge-writer object pWriter, and if no error has occurred, to flush -** all outstanding node buffers held by pWriter to disk. -** -** If *pRc is not SQLITE_OK when this function is called, then no attempt -** is made to write any data to disk. Instead, this function serves only -** to release outstanding resources. -** -** Otherwise, if *pRc is initially SQLITE_OK and an error occurs while -** flushing buffers to disk, *pRc is set to an SQLite error code before -** returning. -*/ -static void fts3IncrmergeRelease( - Fts3Table *p, /* FTS3 table handle */ - IncrmergeWriter *pWriter, /* Merge-writer object */ - int *pRc /* IN/OUT: Error code */ -){ - int i; /* Used to iterate through non-root layers */ - int iRoot; /* Index of root in pWriter->aNodeWriter */ - NodeWriter *pRoot; /* NodeWriter for root node */ - int rc = *pRc; /* Error code */ - - /* Set iRoot to the index in pWriter->aNodeWriter[] of the output segment - ** root node. If the segment fits entirely on a single leaf node, iRoot - ** will be set to 0. If the root node is the parent of the leaves, iRoot - ** will be 1. And so on. */ - for(iRoot=FTS_MAX_APPENDABLE_HEIGHT-1; iRoot>=0; iRoot--){ - NodeWriter *pNode = &pWriter->aNodeWriter[iRoot]; - if( pNode->block.n>0 ) break; - assert( *pRc || pNode->block.nAlloc==0 ); - assert( *pRc || pNode->key.nAlloc==0 ); - sqlite3_free(pNode->block.a); - sqlite3_free(pNode->key.a); - } - - /* Empty output segment. This is a no-op. */ - if( iRoot<0 ) return; - - /* The entire output segment fits on a single node. Normally, this means - ** the node would be stored as a blob in the "root" column of the %_segdir - ** table. However, this is not permitted in this case. The problem is that - ** space has already been reserved in the %_segments table, and so the - ** start_block and end_block fields of the %_segdir table must be populated. - ** And, by design or by accident, released versions of FTS cannot handle - ** segments that fit entirely on the root node with start_block!=0. - ** - ** Instead, create a synthetic root node that contains nothing but a - ** pointer to the single content node. So that the segment consists of a - ** single leaf and a single interior (root) node. - ** - ** Todo: Better might be to defer allocating space in the %_segments - ** table until we are sure it is needed. - */ - if( iRoot==0 ){ - Blob *pBlock = &pWriter->aNodeWriter[1].block; - blobGrowBuffer(pBlock, 1 + FTS3_VARINT_MAX, &rc); - if( rc==SQLITE_OK ){ - pBlock->a[0] = 0x01; - pBlock->n = 1 + sqlite3Fts3PutVarint( - &pBlock->a[1], pWriter->aNodeWriter[0].iBlock - ); - } - iRoot = 1; - } - pRoot = &pWriter->aNodeWriter[iRoot]; - - /* Flush all currently outstanding nodes to disk. */ - for(i=0; iaNodeWriter[i]; - if( pNode->block.n>0 && rc==SQLITE_OK ){ - rc = fts3WriteSegment(p, pNode->iBlock, pNode->block.a, pNode->block.n); - } - sqlite3_free(pNode->block.a); - sqlite3_free(pNode->key.a); - } - - /* Write the %_segdir record. */ - if( rc==SQLITE_OK ){ - rc = fts3WriteSegdir(p, - pWriter->iAbsLevel+1, /* level */ - pWriter->iIdx, /* idx */ - pWriter->iStart, /* start_block */ - pWriter->aNodeWriter[0].iBlock, /* leaves_end_block */ - pWriter->iEnd, /* end_block */ - pRoot->block.a, pRoot->block.n /* root */ - ); - } - sqlite3_free(pRoot->block.a); - sqlite3_free(pRoot->key.a); - - *pRc = rc; -} - -/* -** Compare the term in buffer zLhs (size in bytes nLhs) with that in -** zRhs (size in bytes nRhs) using memcmp. If one term is a prefix of -** the other, it is considered to be smaller than the other. -** -** Return -ve if zLhs is smaller than zRhs, 0 if it is equal, or +ve -** if it is greater. -*/ -static int fts3TermCmp( - const char *zLhs, int nLhs, /* LHS of comparison */ - const char *zRhs, int nRhs /* RHS of comparison */ -){ - int nCmp = MIN(nLhs, nRhs); - int res; - - res = memcmp(zLhs, zRhs, nCmp); - if( res==0 ) res = nLhs - nRhs; - - return res; -} - - -/* -** Query to see if the entry in the %_segments table with blockid iEnd is -** NULL. If no error occurs and the entry is NULL, set *pbRes 1 before -** returning. Otherwise, set *pbRes to 0. -** -** Or, if an error occurs while querying the database, return an SQLite -** error code. The final value of *pbRes is undefined in this case. -** -** This is used to test if a segment is an "appendable" segment. If it -** is, then a NULL entry has been inserted into the %_segments table -** with blockid %_segdir.end_block. -*/ -static int fts3IsAppendable(Fts3Table *p, sqlite3_int64 iEnd, int *pbRes){ - int bRes = 0; /* Result to set *pbRes to */ - sqlite3_stmt *pCheck = 0; /* Statement to query database with */ - int rc; /* Return code */ - - rc = fts3SqlStmt(p, SQL_SEGMENT_IS_APPENDABLE, &pCheck, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pCheck, 1, iEnd); - if( SQLITE_ROW==sqlite3_step(pCheck) ) bRes = 1; - rc = sqlite3_reset(pCheck); - } - - *pbRes = bRes; - return rc; -} - -/* -** This function is called when initializing an incremental-merge operation. -** It checks if the existing segment with index value iIdx at absolute level -** (iAbsLevel+1) can be appended to by the incremental merge. If it can, the -** merge-writer object *pWriter is initialized to write to it. -** -** An existing segment can be appended to by an incremental merge if: -** -** * It was initially created as an appendable segment (with all required -** space pre-allocated), and -** -** * The first key read from the input (arguments zKey and nKey) is -** greater than the largest key currently stored in the potential -** output segment. -*/ -static int fts3IncrmergeLoad( - Fts3Table *p, /* Fts3 table handle */ - sqlite3_int64 iAbsLevel, /* Absolute level of input segments */ - int iIdx, /* Index of candidate output segment */ - const char *zKey, /* First key to write */ - int nKey, /* Number of bytes in nKey */ - IncrmergeWriter *pWriter /* Populate this object */ -){ - int rc; /* Return code */ - sqlite3_stmt *pSelect = 0; /* SELECT to read %_segdir entry */ - - rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR, &pSelect, 0); - if( rc==SQLITE_OK ){ - sqlite3_int64 iStart = 0; /* Value of %_segdir.start_block */ - sqlite3_int64 iLeafEnd = 0; /* Value of %_segdir.leaves_end_block */ - sqlite3_int64 iEnd = 0; /* Value of %_segdir.end_block */ - const char *aRoot = 0; /* Pointer to %_segdir.root buffer */ - int nRoot = 0; /* Size of aRoot[] in bytes */ - int rc2; /* Return code from sqlite3_reset() */ - int bAppendable = 0; /* Set to true if segment is appendable */ - - /* Read the %_segdir entry for index iIdx absolute level (iAbsLevel+1) */ - sqlite3_bind_int64(pSelect, 1, iAbsLevel+1); - sqlite3_bind_int(pSelect, 2, iIdx); - if( sqlite3_step(pSelect)==SQLITE_ROW ){ - iStart = sqlite3_column_int64(pSelect, 1); - iLeafEnd = sqlite3_column_int64(pSelect, 2); - iEnd = sqlite3_column_int64(pSelect, 3); - nRoot = sqlite3_column_bytes(pSelect, 4); - aRoot = sqlite3_column_blob(pSelect, 4); - }else{ - return sqlite3_reset(pSelect); - } - - /* Check for the zero-length marker in the %_segments table */ - rc = fts3IsAppendable(p, iEnd, &bAppendable); - - /* Check that zKey/nKey is larger than the largest key the candidate */ - if( rc==SQLITE_OK && bAppendable ){ - char *aLeaf = 0; - int nLeaf = 0; - - rc = sqlite3Fts3ReadBlock(p, iLeafEnd, &aLeaf, &nLeaf, 0); - if( rc==SQLITE_OK ){ - NodeReader reader; - for(rc = nodeReaderInit(&reader, aLeaf, nLeaf); - rc==SQLITE_OK && reader.aNode; - rc = nodeReaderNext(&reader) - ){ - assert( reader.aNode ); - } - if( fts3TermCmp(zKey, nKey, reader.term.a, reader.term.n)<=0 ){ - bAppendable = 0; - } - nodeReaderRelease(&reader); - } - sqlite3_free(aLeaf); - } - - if( rc==SQLITE_OK && bAppendable ){ - /* It is possible to append to this segment. Set up the IncrmergeWriter - ** object to do so. */ - int i; - int nHeight = (int)aRoot[0]; - NodeWriter *pNode; - - pWriter->nLeafEst = (int)((iEnd - iStart) + 1)/FTS_MAX_APPENDABLE_HEIGHT; - pWriter->iStart = iStart; - pWriter->iEnd = iEnd; - pWriter->iAbsLevel = iAbsLevel; - pWriter->iIdx = iIdx; - - for(i=nHeight+1; iaNodeWriter[i].iBlock = pWriter->iStart + i*pWriter->nLeafEst; - } - - pNode = &pWriter->aNodeWriter[nHeight]; - pNode->iBlock = pWriter->iStart + pWriter->nLeafEst*nHeight; - blobGrowBuffer(&pNode->block, MAX(nRoot, p->nNodeSize), &rc); - if( rc==SQLITE_OK ){ - memcpy(pNode->block.a, aRoot, nRoot); - pNode->block.n = nRoot; - } - - for(i=nHeight; i>=0 && rc==SQLITE_OK; i--){ - NodeReader reader; - pNode = &pWriter->aNodeWriter[i]; - - rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n); - while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader); - blobGrowBuffer(&pNode->key, reader.term.n, &rc); - if( rc==SQLITE_OK ){ - memcpy(pNode->key.a, reader.term.a, reader.term.n); - pNode->key.n = reader.term.n; - if( i>0 ){ - char *aBlock = 0; - int nBlock = 0; - pNode = &pWriter->aNodeWriter[i-1]; - pNode->iBlock = reader.iChild; - rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0); - blobGrowBuffer(&pNode->block, MAX(nBlock, p->nNodeSize), &rc); - if( rc==SQLITE_OK ){ - memcpy(pNode->block.a, aBlock, nBlock); - pNode->block.n = nBlock; - } - sqlite3_free(aBlock); - } - } - nodeReaderRelease(&reader); - } - } - - rc2 = sqlite3_reset(pSelect); - if( rc==SQLITE_OK ) rc = rc2; - } - - return rc; -} - -/* -** Determine the largest segment index value that exists within absolute -** level iAbsLevel+1. If no error occurs, set *piIdx to this value plus -** one before returning SQLITE_OK. Or, if there are no segments at all -** within level iAbsLevel, set *piIdx to zero. -** -** If an error occurs, return an SQLite error code. The final value of -** *piIdx is undefined in this case. -*/ -static int fts3IncrmergeOutputIdx( - Fts3Table *p, /* FTS Table handle */ - sqlite3_int64 iAbsLevel, /* Absolute index of input segments */ - int *piIdx /* OUT: Next free index at iAbsLevel+1 */ -){ - int rc; - sqlite3_stmt *pOutputIdx = 0; /* SQL used to find output index */ - - rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pOutputIdx, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pOutputIdx, 1, iAbsLevel+1); - sqlite3_step(pOutputIdx); - *piIdx = sqlite3_column_int(pOutputIdx, 0); - rc = sqlite3_reset(pOutputIdx); - } - - return rc; -} - -/* -** Allocate an appendable output segment on absolute level iAbsLevel+1 -** with idx value iIdx. -** -** In the %_segdir table, a segment is defined by the values in three -** columns: -** -** start_block -** leaves_end_block -** end_block -** -** When an appendable segment is allocated, it is estimated that the -** maximum number of leaf blocks that may be required is the sum of the -** number of leaf blocks consumed by the input segments, plus the number -** of input segments, multiplied by two. This value is stored in stack -** variable nLeafEst. -** -** A total of 16*nLeafEst blocks are allocated when an appendable segment -** is created ((1 + end_block - start_block)==16*nLeafEst). The contiguous -** array of leaf nodes starts at the first block allocated. The array -** of interior nodes that are parents of the leaf nodes start at block -** (start_block + (1 + end_block - start_block) / 16). And so on. -** -** In the actual code below, the value "16" is replaced with the -** pre-processor macro FTS_MAX_APPENDABLE_HEIGHT. -*/ -static int fts3IncrmergeWriter( - Fts3Table *p, /* Fts3 table handle */ - sqlite3_int64 iAbsLevel, /* Absolute level of input segments */ - int iIdx, /* Index of new output segment */ - Fts3MultiSegReader *pCsr, /* Cursor that data will be read from */ - IncrmergeWriter *pWriter /* Populate this object */ -){ - int rc; /* Return Code */ - int i; /* Iterator variable */ - int nLeafEst = 0; /* Blocks allocated for leaf nodes */ - sqlite3_stmt *pLeafEst = 0; /* SQL used to determine nLeafEst */ - sqlite3_stmt *pFirstBlock = 0; /* SQL used to determine first block */ - - /* Calculate nLeafEst. */ - rc = fts3SqlStmt(p, SQL_MAX_LEAF_NODE_ESTIMATE, &pLeafEst, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pLeafEst, 1, iAbsLevel); - sqlite3_bind_int64(pLeafEst, 2, pCsr->nSegment); - if( SQLITE_ROW==sqlite3_step(pLeafEst) ){ - nLeafEst = sqlite3_column_int(pLeafEst, 0); - } - rc = sqlite3_reset(pLeafEst); - } - if( rc!=SQLITE_OK ) return rc; - - /* Calculate the first block to use in the output segment */ - rc = fts3SqlStmt(p, SQL_NEXT_SEGMENTS_ID, &pFirstBlock, 0); - if( rc==SQLITE_OK ){ - if( SQLITE_ROW==sqlite3_step(pFirstBlock) ){ - pWriter->iStart = sqlite3_column_int64(pFirstBlock, 0); - pWriter->iEnd = pWriter->iStart - 1; - pWriter->iEnd += nLeafEst * FTS_MAX_APPENDABLE_HEIGHT; - } - rc = sqlite3_reset(pFirstBlock); - } - if( rc!=SQLITE_OK ) return rc; - - /* Insert the marker in the %_segments table to make sure nobody tries - ** to steal the space just allocated. This is also used to identify - ** appendable segments. */ - rc = fts3WriteSegment(p, pWriter->iEnd, 0, 0); - if( rc!=SQLITE_OK ) return rc; - - pWriter->iAbsLevel = iAbsLevel; - pWriter->nLeafEst = nLeafEst; - pWriter->iIdx = iIdx; - - /* Set up the array of NodeWriter objects */ - for(i=0; iaNodeWriter[i].iBlock = pWriter->iStart + i*pWriter->nLeafEst; - } - return SQLITE_OK; -} - -/* -** Remove an entry from the %_segdir table. This involves running the -** following two statements: -** -** DELETE FROM %_segdir WHERE level = :iAbsLevel AND idx = :iIdx -** UPDATE %_segdir SET idx = idx - 1 WHERE level = :iAbsLevel AND idx > :iIdx -** -** The DELETE statement removes the specific %_segdir level. The UPDATE -** statement ensures that the remaining segments have contiguously allocated -** idx values. -*/ -static int fts3RemoveSegdirEntry( - Fts3Table *p, /* FTS3 table handle */ - sqlite3_int64 iAbsLevel, /* Absolute level to delete from */ - int iIdx /* Index of %_segdir entry to delete */ -){ - int rc; /* Return code */ - sqlite3_stmt *pDelete = 0; /* DELETE statement */ - - rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_ENTRY, &pDelete, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pDelete, 1, iAbsLevel); - sqlite3_bind_int(pDelete, 2, iIdx); - sqlite3_step(pDelete); - rc = sqlite3_reset(pDelete); - } - - return rc; -} - -/* -** One or more segments have just been removed from absolute level iAbsLevel. -** Update the 'idx' values of the remaining segments in the level so that -** the idx values are a contiguous sequence starting from 0. -*/ -static int fts3RepackSegdirLevel( - Fts3Table *p, /* FTS3 table handle */ - sqlite3_int64 iAbsLevel /* Absolute level to repack */ -){ - int rc; /* Return code */ - int *aIdx = 0; /* Array of remaining idx values */ - int nIdx = 0; /* Valid entries in aIdx[] */ - int nAlloc = 0; /* Allocated size of aIdx[] */ - int i; /* Iterator variable */ - sqlite3_stmt *pSelect = 0; /* Select statement to read idx values */ - sqlite3_stmt *pUpdate = 0; /* Update statement to modify idx values */ - - rc = fts3SqlStmt(p, SQL_SELECT_INDEXES, &pSelect, 0); - if( rc==SQLITE_OK ){ - int rc2; - sqlite3_bind_int64(pSelect, 1, iAbsLevel); - while( SQLITE_ROW==sqlite3_step(pSelect) ){ - if( nIdx>=nAlloc ){ - int *aNew; - nAlloc += 16; - aNew = sqlite3_realloc(aIdx, nAlloc*sizeof(int)); - if( !aNew ){ - rc = SQLITE_NOMEM; - break; - } - aIdx = aNew; - } - aIdx[nIdx++] = sqlite3_column_int(pSelect, 0); - } - rc2 = sqlite3_reset(pSelect); - if( rc==SQLITE_OK ) rc = rc2; - } - - if( rc==SQLITE_OK ){ - rc = fts3SqlStmt(p, SQL_SHIFT_SEGDIR_ENTRY, &pUpdate, 0); - } - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pUpdate, 2, iAbsLevel); - } - - assert( p->bIgnoreSavepoint==0 ); - p->bIgnoreSavepoint = 1; - for(i=0; rc==SQLITE_OK && ibIgnoreSavepoint = 0; - - sqlite3_free(aIdx); - return rc; -} - -static void fts3StartNode(Blob *pNode, int iHeight, sqlite3_int64 iChild){ - pNode->a[0] = (char)iHeight; - if( iChild ){ - assert( pNode->nAlloc>=1+sqlite3Fts3VarintLen(iChild) ); - pNode->n = 1 + sqlite3Fts3PutVarint(&pNode->a[1], iChild); - }else{ - assert( pNode->nAlloc>=1 ); - pNode->n = 1; - } -} - -/* -** The first two arguments are a pointer to and the size of a segment b-tree -** node. The node may be a leaf or an internal node. -** -** This function creates a new node image in blob object *pNew by copying -** all terms that are greater than or equal to zTerm/nTerm (for leaf nodes) -** or greater than zTerm/nTerm (for internal nodes) from aNode/nNode. -*/ -static int fts3TruncateNode( - const char *aNode, /* Current node image */ - int nNode, /* Size of aNode in bytes */ - Blob *pNew, /* OUT: Write new node image here */ - const char *zTerm, /* Omit all terms smaller than this */ - int nTerm, /* Size of zTerm in bytes */ - sqlite3_int64 *piBlock /* OUT: Block number in next layer down */ -){ - NodeReader reader; /* Reader object */ - Blob prev = {0, 0, 0}; /* Previous term written to new node */ - int rc = SQLITE_OK; /* Return code */ - int bLeaf = aNode[0]=='\0'; /* True for a leaf node */ - - /* Allocate required output space */ - blobGrowBuffer(pNew, nNode, &rc); - if( rc!=SQLITE_OK ) return rc; - pNew->n = 0; - - /* Populate new node buffer */ - for(rc = nodeReaderInit(&reader, aNode, nNode); - rc==SQLITE_OK && reader.aNode; - rc = nodeReaderNext(&reader) - ){ - if( pNew->n==0 ){ - int res = fts3TermCmp(reader.term.a, reader.term.n, zTerm, nTerm); - if( res<0 || (bLeaf==0 && res==0) ) continue; - fts3StartNode(pNew, (int)aNode[0], reader.iChild); - *piBlock = reader.iChild; - } - rc = fts3AppendToNode( - pNew, &prev, reader.term.a, reader.term.n, - reader.aDoclist, reader.nDoclist - ); - if( rc!=SQLITE_OK ) break; - } - if( pNew->n==0 ){ - fts3StartNode(pNew, (int)aNode[0], reader.iChild); - *piBlock = reader.iChild; - } - assert( pNew->n<=pNew->nAlloc ); - - nodeReaderRelease(&reader); - sqlite3_free(prev.a); - return rc; -} - -/* -** Remove all terms smaller than zTerm/nTerm from segment iIdx in absolute -** level iAbsLevel. This may involve deleting entries from the %_segments -** table, and modifying existing entries in both the %_segments and %_segdir -** tables. -** -** SQLITE_OK is returned if the segment is updated successfully. Or an -** SQLite error code otherwise. -*/ -static int fts3TruncateSegment( - Fts3Table *p, /* FTS3 table handle */ - sqlite3_int64 iAbsLevel, /* Absolute level of segment to modify */ - int iIdx, /* Index within level of segment to modify */ - const char *zTerm, /* Remove terms smaller than this */ - int nTerm /* Number of bytes in buffer zTerm */ -){ - int rc = SQLITE_OK; /* Return code */ - Blob root = {0,0,0}; /* New root page image */ - Blob block = {0,0,0}; /* Buffer used for any other block */ - sqlite3_int64 iBlock = 0; /* Block id */ - sqlite3_int64 iNewStart = 0; /* New value for iStartBlock */ - sqlite3_int64 iOldStart = 0; /* Old value for iStartBlock */ - sqlite3_stmt *pFetch = 0; /* Statement used to fetch segdir */ - - rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR, &pFetch, 0); - if( rc==SQLITE_OK ){ - int rc2; /* sqlite3_reset() return code */ - sqlite3_bind_int64(pFetch, 1, iAbsLevel); - sqlite3_bind_int(pFetch, 2, iIdx); - if( SQLITE_ROW==sqlite3_step(pFetch) ){ - const char *aRoot = sqlite3_column_blob(pFetch, 4); - int nRoot = sqlite3_column_bytes(pFetch, 4); - iOldStart = sqlite3_column_int64(pFetch, 1); - rc = fts3TruncateNode(aRoot, nRoot, &root, zTerm, nTerm, &iBlock); - } - rc2 = sqlite3_reset(pFetch); - if( rc==SQLITE_OK ) rc = rc2; - } - - while( rc==SQLITE_OK && iBlock ){ - char *aBlock = 0; - int nBlock = 0; - iNewStart = iBlock; - - rc = sqlite3Fts3ReadBlock(p, iBlock, &aBlock, &nBlock, 0); - if( rc==SQLITE_OK ){ - rc = fts3TruncateNode(aBlock, nBlock, &block, zTerm, nTerm, &iBlock); - } - if( rc==SQLITE_OK ){ - rc = fts3WriteSegment(p, iNewStart, block.a, block.n); - } - sqlite3_free(aBlock); - } - - /* Variable iNewStart now contains the first valid leaf node. */ - if( rc==SQLITE_OK && iNewStart ){ - sqlite3_stmt *pDel = 0; - rc = fts3SqlStmt(p, SQL_DELETE_SEGMENTS_RANGE, &pDel, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pDel, 1, iOldStart); - sqlite3_bind_int64(pDel, 2, iNewStart-1); - sqlite3_step(pDel); - rc = sqlite3_reset(pDel); - } - } - - if( rc==SQLITE_OK ){ - sqlite3_stmt *pChomp = 0; - rc = fts3SqlStmt(p, SQL_CHOMP_SEGDIR, &pChomp, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pChomp, 1, iNewStart); - sqlite3_bind_blob(pChomp, 2, root.a, root.n, SQLITE_STATIC); - sqlite3_bind_int64(pChomp, 3, iAbsLevel); - sqlite3_bind_int(pChomp, 4, iIdx); - sqlite3_step(pChomp); - rc = sqlite3_reset(pChomp); - } - } - - sqlite3_free(root.a); - sqlite3_free(block.a); - return rc; -} - -/* -** This function is called after an incrmental-merge operation has run to -** merge (or partially merge) two or more segments from absolute level -** iAbsLevel. -** -** Each input segment is either removed from the db completely (if all of -** its data was copied to the output segment by the incrmerge operation) -** or modified in place so that it no longer contains those entries that -** have been duplicated in the output segment. -*/ -static int fts3IncrmergeChomp( - Fts3Table *p, /* FTS table handle */ - sqlite3_int64 iAbsLevel, /* Absolute level containing segments */ - Fts3MultiSegReader *pCsr, /* Chomp all segments opened by this cursor */ - int *pnRem /* Number of segments not deleted */ -){ - int i; - int nRem = 0; - int rc = SQLITE_OK; - - for(i=pCsr->nSegment-1; i>=0 && rc==SQLITE_OK; i--){ - Fts3SegReader *pSeg = 0; - int j; - - /* Find the Fts3SegReader object with Fts3SegReader.iIdx==i. It is hiding - ** somewhere in the pCsr->apSegment[] array. */ - for(j=0; ALWAYS(jnSegment); j++){ - pSeg = pCsr->apSegment[j]; - if( pSeg->iIdx==i ) break; - } - assert( jnSegment && pSeg->iIdx==i ); - - if( pSeg->aNode==0 ){ - /* Seg-reader is at EOF. Remove the entire input segment. */ - rc = fts3DeleteSegment(p, pSeg); - if( rc==SQLITE_OK ){ - rc = fts3RemoveSegdirEntry(p, iAbsLevel, pSeg->iIdx); - } - *pnRem = 0; - }else{ - /* The incremental merge did not copy all the data from this - ** segment to the upper level. The segment is modified in place - ** so that it contains no keys smaller than zTerm/nTerm. */ - const char *zTerm = pSeg->zTerm; - int nTerm = pSeg->nTerm; - rc = fts3TruncateSegment(p, iAbsLevel, pSeg->iIdx, zTerm, nTerm); - nRem++; - } - } - - if( rc==SQLITE_OK && nRem!=pCsr->nSegment ){ - rc = fts3RepackSegdirLevel(p, iAbsLevel); - } - - *pnRem = nRem; - return rc; -} - -/* -** Store an incr-merge hint in the database. -*/ -static int fts3IncrmergeHintStore(Fts3Table *p, Blob *pHint){ - sqlite3_stmt *pReplace = 0; - int rc; /* Return code */ - - rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pReplace, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int(pReplace, 1, FTS_STAT_INCRMERGEHINT); - sqlite3_bind_blob(pReplace, 2, pHint->a, pHint->n, SQLITE_STATIC); - sqlite3_step(pReplace); - rc = sqlite3_reset(pReplace); - } - - return rc; -} - -/* -** Load an incr-merge hint from the database. The incr-merge hint, if one -** exists, is stored in the rowid==1 row of the %_stat table. -** -** If successful, populate blob *pHint with the value read from the %_stat -** table and return SQLITE_OK. Otherwise, if an error occurs, return an -** SQLite error code. -*/ -static int fts3IncrmergeHintLoad(Fts3Table *p, Blob *pHint){ - sqlite3_stmt *pSelect = 0; - int rc; - - pHint->n = 0; - rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pSelect, 0); - if( rc==SQLITE_OK ){ - int rc2; - sqlite3_bind_int(pSelect, 1, FTS_STAT_INCRMERGEHINT); - if( SQLITE_ROW==sqlite3_step(pSelect) ){ - const char *aHint = sqlite3_column_blob(pSelect, 0); - int nHint = sqlite3_column_bytes(pSelect, 0); - if( aHint ){ - blobGrowBuffer(pHint, nHint, &rc); - if( rc==SQLITE_OK ){ - memcpy(pHint->a, aHint, nHint); - pHint->n = nHint; - } - } - } - rc2 = sqlite3_reset(pSelect); - if( rc==SQLITE_OK ) rc = rc2; - } - - return rc; -} - -/* -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, append an entry to the hint stored in blob *pHint. Each entry -** consists of two varints, the absolute level number of the input segments -** and the number of input segments. -** -** If successful, leave *pRc set to SQLITE_OK and return. If an error occurs, -** set *pRc to an SQLite error code before returning. -*/ -static void fts3IncrmergeHintPush( - Blob *pHint, /* Hint blob to append to */ - i64 iAbsLevel, /* First varint to store in hint */ - int nInput, /* Second varint to store in hint */ - int *pRc /* IN/OUT: Error code */ -){ - blobGrowBuffer(pHint, pHint->n + 2*FTS3_VARINT_MAX, pRc); - if( *pRc==SQLITE_OK ){ - pHint->n += sqlite3Fts3PutVarint(&pHint->a[pHint->n], iAbsLevel); - pHint->n += sqlite3Fts3PutVarint(&pHint->a[pHint->n], (i64)nInput); - } -} - -/* -** Read the last entry (most recently pushed) from the hint blob *pHint -** and then remove the entry. Write the two values read to *piAbsLevel and -** *pnInput before returning. -** -** If no error occurs, return SQLITE_OK. If the hint blob in *pHint does -** not contain at least two valid varints, return SQLITE_CORRUPT_VTAB. -*/ -static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){ - const int nHint = pHint->n; - int i; - - i = pHint->n-2; - while( i>0 && (pHint->a[i-1] & 0x80) ) i--; - while( i>0 && (pHint->a[i-1] & 0x80) ) i--; - - pHint->n = i; - i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel); - i += sqlite3Fts3GetVarint32(&pHint->a[i], pnInput); - if( i!=nHint ) return SQLITE_CORRUPT_VTAB; - - return SQLITE_OK; -} - - -/* -** Attempt an incremental merge that writes nMerge leaf blocks. -** -** Incremental merges happen nMin segments at a time. The two -** segments to be merged are the nMin oldest segments (the ones with -** the smallest indexes) in the highest level that contains at least -** nMin segments. Multiple merges might occur in an attempt to write the -** quota of nMerge leaf blocks. -*/ -SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){ - int rc; /* Return code */ - int nRem = nMerge; /* Number of leaf pages yet to be written */ - Fts3MultiSegReader *pCsr; /* Cursor used to read input data */ - Fts3SegFilter *pFilter; /* Filter used with cursor pCsr */ - IncrmergeWriter *pWriter; /* Writer object */ - int nSeg = 0; /* Number of input segments */ - sqlite3_int64 iAbsLevel = 0; /* Absolute level number to work on */ - Blob hint = {0, 0, 0}; /* Hint read from %_stat table */ - int bDirtyHint = 0; /* True if blob 'hint' has been modified */ - - /* Allocate space for the cursor, filter and writer objects */ - const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter); - pWriter = (IncrmergeWriter *)sqlite3_malloc(nAlloc); - if( !pWriter ) return SQLITE_NOMEM; - pFilter = (Fts3SegFilter *)&pWriter[1]; - pCsr = (Fts3MultiSegReader *)&pFilter[1]; - - rc = fts3IncrmergeHintLoad(p, &hint); - while( rc==SQLITE_OK && nRem>0 ){ - const i64 nMod = FTS3_SEGDIR_MAXLEVEL * p->nIndex; - sqlite3_stmt *pFindLevel = 0; /* SQL used to determine iAbsLevel */ - int bUseHint = 0; /* True if attempting to append */ - - /* Search the %_segdir table for the absolute level with the smallest - ** relative level number that contains at least nMin segments, if any. - ** If one is found, set iAbsLevel to the absolute level number and - ** nSeg to nMin. If no level with at least nMin segments can be found, - ** set nSeg to -1. - */ - rc = fts3SqlStmt(p, SQL_FIND_MERGE_LEVEL, &pFindLevel, 0); - sqlite3_bind_int(pFindLevel, 1, nMin); - if( sqlite3_step(pFindLevel)==SQLITE_ROW ){ - iAbsLevel = sqlite3_column_int64(pFindLevel, 0); - nSeg = nMin; - }else{ - nSeg = -1; - } - rc = sqlite3_reset(pFindLevel); - - /* If the hint read from the %_stat table is not empty, check if the - ** last entry in it specifies a relative level smaller than or equal - ** to the level identified by the block above (if any). If so, this - ** iteration of the loop will work on merging at the hinted level. - */ - if( rc==SQLITE_OK && hint.n ){ - int nHint = hint.n; - sqlite3_int64 iHintAbsLevel = 0; /* Hint level */ - int nHintSeg = 0; /* Hint number of segments */ - - rc = fts3IncrmergeHintPop(&hint, &iHintAbsLevel, &nHintSeg); - if( nSeg<0 || (iAbsLevel % nMod) >= (iHintAbsLevel % nMod) ){ - iAbsLevel = iHintAbsLevel; - nSeg = nHintSeg; - bUseHint = 1; - bDirtyHint = 1; - }else{ - /* This undoes the effect of the HintPop() above - so that no entry - ** is removed from the hint blob. */ - hint.n = nHint; - } - } - - /* If nSeg is less that zero, then there is no level with at least - ** nMin segments and no hint in the %_stat table. No work to do. - ** Exit early in this case. */ - if( nSeg<0 ) break; - - /* Open a cursor to iterate through the contents of the oldest nSeg - ** indexes of absolute level iAbsLevel. If this cursor is opened using - ** the 'hint' parameters, it is possible that there are less than nSeg - ** segments available in level iAbsLevel. In this case, no work is - ** done on iAbsLevel - fall through to the next iteration of the loop - ** to start work on some other level. */ - memset(pWriter, 0, nAlloc); - pFilter->flags = FTS3_SEGMENT_REQUIRE_POS; - if( rc==SQLITE_OK ){ - rc = fts3IncrmergeCsr(p, iAbsLevel, nSeg, pCsr); - } - if( SQLITE_OK==rc && pCsr->nSegment==nSeg - && SQLITE_OK==(rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter)) - && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr)) - ){ - int iIdx = 0; /* Largest idx in level (iAbsLevel+1) */ - rc = fts3IncrmergeOutputIdx(p, iAbsLevel, &iIdx); - if( rc==SQLITE_OK ){ - if( bUseHint && iIdx>0 ){ - const char *zKey = pCsr->zTerm; - int nKey = pCsr->nTerm; - rc = fts3IncrmergeLoad(p, iAbsLevel, iIdx-1, zKey, nKey, pWriter); - }else{ - rc = fts3IncrmergeWriter(p, iAbsLevel, iIdx, pCsr, pWriter); - } - } - - if( rc==SQLITE_OK && pWriter->nLeafEst ){ - fts3LogMerge(nSeg, iAbsLevel); - do { - rc = fts3IncrmergeAppend(p, pWriter, pCsr); - if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr); - if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK; - }while( rc==SQLITE_ROW ); - - /* Update or delete the input segments */ - if( rc==SQLITE_OK ){ - nRem -= (1 + pWriter->nWork); - rc = fts3IncrmergeChomp(p, iAbsLevel, pCsr, &nSeg); - if( nSeg!=0 ){ - bDirtyHint = 1; - fts3IncrmergeHintPush(&hint, iAbsLevel, nSeg, &rc); - } - } - } - - fts3IncrmergeRelease(p, pWriter, &rc); - } - - sqlite3Fts3SegReaderFinish(pCsr); - } - - /* Write the hint values into the %_stat table for the next incr-merger */ - if( bDirtyHint && rc==SQLITE_OK ){ - rc = fts3IncrmergeHintStore(p, &hint); - } - - sqlite3_free(pWriter); - sqlite3_free(hint.a); - return rc; -} - -/* -** Convert the text beginning at *pz into an integer and return -** its value. Advance *pz to point to the first character past -** the integer. -*/ -static int fts3Getint(const char **pz){ - const char *z = *pz; - int i = 0; - while( (*z)>='0' && (*z)<='9' ) i = 10*i + *(z++) - '0'; - *pz = z; - return i; -} - -/* -** Process statements of the form: -** -** INSERT INTO table(table) VALUES('merge=A,B'); -** -** A and B are integers that decode to be the number of leaf pages -** written for the merge, and the minimum number of segments on a level -** before it will be selected for a merge, respectively. -*/ -static int fts3DoIncrmerge( - Fts3Table *p, /* FTS3 table handle */ - const char *zParam /* Nul-terminated string containing "A,B" */ -){ - int rc; - int nMin = (FTS3_MERGE_COUNT / 2); - int nMerge = 0; - const char *z = zParam; - - /* Read the first integer value */ - nMerge = fts3Getint(&z); - - /* If the first integer value is followed by a ',', read the second - ** integer value. */ - if( z[0]==',' && z[1]!='\0' ){ - z++; - nMin = fts3Getint(&z); - } - - if( z[0]!='\0' || nMin<2 ){ - rc = SQLITE_ERROR; - }else{ - rc = SQLITE_OK; - if( !p->bHasStat ){ - assert( p->bFts4==0 ); - sqlite3Fts3CreateStatTable(&rc, p); - } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3Incrmerge(p, nMerge, nMin); - } - sqlite3Fts3SegmentsClose(p); - } - return rc; -} - -/* -** Process statements of the form: -** -** INSERT INTO table(table) VALUES('automerge=X'); -** -** where X is an integer. X==0 means to turn automerge off. X!=0 means -** turn it on. The setting is persistent. -*/ -static int fts3DoAutoincrmerge( - Fts3Table *p, /* FTS3 table handle */ - const char *zParam /* Nul-terminated string containing boolean */ -){ - int rc = SQLITE_OK; - sqlite3_stmt *pStmt = 0; - p->bAutoincrmerge = fts3Getint(&zParam)!=0; - if( !p->bHasStat ){ - assert( p->bFts4==0 ); - sqlite3Fts3CreateStatTable(&rc, p); - if( rc ) return rc; - } - rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pStmt, 0); - if( rc ) return rc;; - sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE); - sqlite3_bind_int(pStmt, 2, p->bAutoincrmerge); - sqlite3_step(pStmt); - rc = sqlite3_reset(pStmt); - return rc; -} - -/* -** Return a 64-bit checksum for the FTS index entry specified by the -** arguments to this function. -*/ -static u64 fts3ChecksumEntry( - const char *zTerm, /* Pointer to buffer containing term */ - int nTerm, /* Size of zTerm in bytes */ - int iLangid, /* Language id for current row */ - int iIndex, /* Index (0..Fts3Table.nIndex-1) */ - i64 iDocid, /* Docid for current row. */ - int iCol, /* Column number */ - int iPos /* Position */ -){ - int i; - u64 ret = (u64)iDocid; - - ret += (ret<<3) + iLangid; - ret += (ret<<3) + iIndex; - ret += (ret<<3) + iCol; - ret += (ret<<3) + iPos; - for(i=0; inIndex-1) */ - int *pRc /* OUT: Return code */ -){ - Fts3SegFilter filter; - Fts3MultiSegReader csr; - int rc; - u64 cksum = 0; - - assert( *pRc==SQLITE_OK ); - - memset(&filter, 0, sizeof(filter)); - memset(&csr, 0, sizeof(csr)); - filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; - filter.flags |= FTS3_SEGMENT_SCAN; - - rc = sqlite3Fts3SegReaderCursor( - p, iLangid, iIndex, FTS3_SEGCURSOR_ALL, 0, 0, 0, 1,&csr - ); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3SegReaderStart(p, &csr, &filter); - } - - if( rc==SQLITE_OK ){ - while( SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, &csr)) ){ - char *pCsr = csr.aDoclist; - char *pEnd = &pCsr[csr.nDoclist]; - - i64 iDocid = 0; - i64 iCol = 0; - i64 iPos = 0; - - pCsr += sqlite3Fts3GetVarint(pCsr, &iDocid); - while( pCsrnIndex); - while( rc==SQLITE_OK && sqlite3_step(pAllLangid)==SQLITE_ROW ){ - int iLangid = sqlite3_column_int(pAllLangid, 0); - int i; - for(i=0; inIndex; i++){ - cksum1 = cksum1 ^ fts3ChecksumIndex(p, iLangid, i, &rc); - } - } - rc2 = sqlite3_reset(pAllLangid); - if( rc==SQLITE_OK ) rc = rc2; - } - - /* This block calculates the checksum according to the %_content table */ - rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0); - if( rc==SQLITE_OK ){ - sqlite3_tokenizer_module const *pModule = p->pTokenizer->pModule; - sqlite3_stmt *pStmt = 0; - char *zSql; - - zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist); - if( !zSql ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - } - - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - i64 iDocid = sqlite3_column_int64(pStmt, 0); - int iLang = langidFromSelect(p, pStmt); - int iCol; - - for(iCol=0; rc==SQLITE_OK && iColnColumn; iCol++){ - const char *zText = (const char *)sqlite3_column_text(pStmt, iCol+1); - int nText = sqlite3_column_bytes(pStmt, iCol+1); - sqlite3_tokenizer_cursor *pT = 0; - - rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText, &pT); - while( rc==SQLITE_OK ){ - char const *zToken; /* Buffer containing token */ - int nToken = 0; /* Number of bytes in token */ - int iDum1 = 0, iDum2 = 0; /* Dummy variables */ - int iPos = 0; /* Position of token in zText */ - - rc = pModule->xNext(pT, &zToken, &nToken, &iDum1, &iDum2, &iPos); - if( rc==SQLITE_OK ){ - int i; - cksum2 = cksum2 ^ fts3ChecksumEntry( - zToken, nToken, iLang, 0, iDocid, iCol, iPos - ); - for(i=1; inIndex; i++){ - if( p->aIndex[i].nPrefix<=nToken ){ - cksum2 = cksum2 ^ fts3ChecksumEntry( - zToken, p->aIndex[i].nPrefix, iLang, i, iDocid, iCol, iPos - ); - } - } - } - } - if( pT ) pModule->xClose(pT); - if( rc==SQLITE_DONE ) rc = SQLITE_OK; - } - } - - sqlite3_finalize(pStmt); - } - - *pbOk = (cksum1==cksum2); - return rc; -} - -/* -** Run the integrity-check. If no error occurs and the current contents of -** the FTS index are correct, return SQLITE_OK. Or, if the contents of the -** FTS index are incorrect, return SQLITE_CORRUPT_VTAB. -** -** Or, if an error (e.g. an OOM or IO error) occurs, return an SQLite -** error code. -** -** The integrity-check works as follows. For each token and indexed token -** prefix in the document set, a 64-bit checksum is calculated (by code -** in fts3ChecksumEntry()) based on the following: -** -** + The index number (0 for the main index, 1 for the first prefix -** index etc.), -** + The token (or token prefix) text itself, -** + The language-id of the row it appears in, -** + The docid of the row it appears in, -** + The column it appears in, and -** + The tokens position within that column. -** -** The checksums for all entries in the index are XORed together to create -** a single checksum for the entire index. -** -** The integrity-check code calculates the same checksum in two ways: -** -** 1. By scanning the contents of the FTS index, and -** 2. By scanning and tokenizing the content table. -** -** If the two checksums are identical, the integrity-check is deemed to have -** passed. -*/ -static int fts3DoIntegrityCheck( - Fts3Table *p /* FTS3 table handle */ -){ - int rc; - int bOk = 0; - rc = fts3IntegrityCheck(p, &bOk); - if( rc==SQLITE_OK && bOk==0 ) rc = SQLITE_CORRUPT_VTAB; - return rc; -} - -/* -** Handle a 'special' INSERT of the form: -** -** "INSERT INTO tbl(tbl) VALUES()" -** -** Argument pVal contains the result of . Currently the only -** meaningful value to insert is the text 'optimize'. -*/ -static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ - int rc; /* Return Code */ - const char *zVal = (const char *)sqlite3_value_text(pVal); - int nVal = sqlite3_value_bytes(pVal); - - if( !zVal ){ - return SQLITE_NOMEM; - }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ - rc = fts3DoOptimize(p, 0); - }else if( nVal==7 && 0==sqlite3_strnicmp(zVal, "rebuild", 7) ){ - rc = fts3DoRebuild(p); - }else if( nVal==15 && 0==sqlite3_strnicmp(zVal, "integrity-check", 15) ){ - rc = fts3DoIntegrityCheck(p); - }else if( nVal>6 && 0==sqlite3_strnicmp(zVal, "merge=", 6) ){ - rc = fts3DoIncrmerge(p, &zVal[6]); - }else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){ - rc = fts3DoAutoincrmerge(p, &zVal[10]); -#ifdef SQLITE_TEST - }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ - p->nNodeSize = atoi(&zVal[9]); - rc = SQLITE_OK; - }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ - p->nMaxPendingData = atoi(&zVal[11]); - rc = SQLITE_OK; -#endif - }else{ - rc = SQLITE_ERROR; - } - - return rc; -} - -#ifndef SQLITE_DISABLE_FTS4_DEFERRED -/* -** Delete all cached deferred doclists. Deferred doclists are cached -** (allocated) by the sqlite3Fts3CacheDeferredDoclists() function. -*/ -SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *pCsr){ - Fts3DeferredToken *pDef; - for(pDef=pCsr->pDeferred; pDef; pDef=pDef->pNext){ - fts3PendingListDelete(pDef->pList); - pDef->pList = 0; - } -} - -/* -** Free all entries in the pCsr->pDeffered list. Entries are added to -** this list using sqlite3Fts3DeferToken(). -*/ -SQLITE_PRIVATE void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *pCsr){ - Fts3DeferredToken *pDef; - Fts3DeferredToken *pNext; - for(pDef=pCsr->pDeferred; pDef; pDef=pNext){ - pNext = pDef->pNext; - fts3PendingListDelete(pDef->pList); - sqlite3_free(pDef); - } - pCsr->pDeferred = 0; -} - -/* -** Generate deferred-doclists for all tokens in the pCsr->pDeferred list -** based on the row that pCsr currently points to. -** -** A deferred-doclist is like any other doclist with position information -** included, except that it only contains entries for a single row of the -** table, not for all rows. -*/ -SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){ - int rc = SQLITE_OK; /* Return code */ - if( pCsr->pDeferred ){ - int i; /* Used to iterate through table columns */ - sqlite3_int64 iDocid; /* Docid of the row pCsr points to */ - Fts3DeferredToken *pDef; /* Used to iterate through deferred tokens */ - - Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; - sqlite3_tokenizer *pT = p->pTokenizer; - sqlite3_tokenizer_module const *pModule = pT->pModule; - - assert( pCsr->isRequireSeek==0 ); - iDocid = sqlite3_column_int64(pCsr->pStmt, 0); - - for(i=0; inColumn && rc==SQLITE_OK; i++){ - const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1); - sqlite3_tokenizer_cursor *pTC = 0; - - rc = sqlite3Fts3OpenTokenizer(pT, pCsr->iLangid, zText, -1, &pTC); - while( rc==SQLITE_OK ){ - char const *zToken; /* Buffer containing token */ - int nToken = 0; /* Number of bytes in token */ - int iDum1 = 0, iDum2 = 0; /* Dummy variables */ - int iPos = 0; /* Position of token in zText */ - - rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos); - for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){ - Fts3PhraseToken *pPT = pDef->pToken; - if( (pDef->iCol>=p->nColumn || pDef->iCol==i) - && (pPT->bFirst==0 || iPos==0) - && (pPT->n==nToken || (pPT->isPrefix && pPT->nz, pPT->n)) - ){ - fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc); - } - } - } - if( pTC ) pModule->xClose(pTC); - if( rc==SQLITE_DONE ) rc = SQLITE_OK; - } - - for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){ - if( pDef->pList ){ - rc = fts3PendingListAppendVarint(&pDef->pList, 0); - } - } - } - - return rc; -} - -SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList( - Fts3DeferredToken *p, - char **ppData, - int *pnData -){ - char *pRet; - int nSkip; - sqlite3_int64 dummy; - - *ppData = 0; - *pnData = 0; - - if( p->pList==0 ){ - return SQLITE_OK; - } - - pRet = (char *)sqlite3_malloc(p->pList->nData); - if( !pRet ) return SQLITE_NOMEM; - - nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy); - *pnData = p->pList->nData - nSkip; - *ppData = pRet; - - memcpy(pRet, &p->pList->aData[nSkip], *pnData); - return SQLITE_OK; -} - -/* -** Add an entry for token pToken to the pCsr->pDeferred list. -*/ -SQLITE_PRIVATE int sqlite3Fts3DeferToken( - Fts3Cursor *pCsr, /* Fts3 table cursor */ - Fts3PhraseToken *pToken, /* Token to defer */ - int iCol /* Column that token must appear in (or -1) */ -){ - Fts3DeferredToken *pDeferred; - pDeferred = sqlite3_malloc(sizeof(*pDeferred)); - if( !pDeferred ){ - return SQLITE_NOMEM; - } - memset(pDeferred, 0, sizeof(*pDeferred)); - pDeferred->pToken = pToken; - pDeferred->pNext = pCsr->pDeferred; - pDeferred->iCol = iCol; - pCsr->pDeferred = pDeferred; - - assert( pToken->pDeferred==0 ); - pToken->pDeferred = pDeferred; - - return SQLITE_OK; -} -#endif - -/* -** SQLite value pRowid contains the rowid of a row that may or may not be -** present in the FTS3 table. If it is, delete it and adjust the contents -** of subsiduary data structures accordingly. -*/ -static int fts3DeleteByRowid( - Fts3Table *p, - sqlite3_value *pRowid, - int *pnChng, /* IN/OUT: Decrement if row is deleted */ - u32 *aSzDel -){ - int rc = SQLITE_OK; /* Return code */ - int bFound = 0; /* True if *pRowid really is in the table */ - - fts3DeleteTerms(&rc, p, pRowid, aSzDel, &bFound); - if( bFound && rc==SQLITE_OK ){ - int isEmpty = 0; /* Deleting *pRowid leaves the table empty */ - rc = fts3IsEmpty(p, pRowid, &isEmpty); - if( rc==SQLITE_OK ){ - if( isEmpty ){ - /* Deleting this row means the whole table is empty. In this case - ** delete the contents of all three tables and throw away any - ** data in the pendingTerms hash table. */ - rc = fts3DeleteAll(p, 1); - *pnChng = 0; - memset(aSzDel, 0, sizeof(u32) * (p->nColumn+1) * 2); - }else{ - *pnChng = *pnChng - 1; - if( p->zContentTbl==0 ){ - fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid); - } - if( p->bHasDocsize ){ - fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid); - } - } - } - } - - return rc; -} - -/* -** This function does the work for the xUpdate method of FTS3 virtual -** tables. The schema of the virtual table being: -** -** CREATE TABLE
    ( -** , -**
    HIDDEN, -** docid HIDDEN, -** HIDDEN -** ); -** -** -*/ -SQLITE_PRIVATE int sqlite3Fts3UpdateMethod( - sqlite3_vtab *pVtab, /* FTS3 vtab object */ - int nArg, /* Size of argument array */ - sqlite3_value **apVal, /* Array of arguments */ - sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ -){ - Fts3Table *p = (Fts3Table *)pVtab; - int rc = SQLITE_OK; /* Return Code */ - int isRemove = 0; /* True for an UPDATE or DELETE */ - u32 *aSzIns = 0; /* Sizes of inserted documents */ - u32 *aSzDel = 0; /* Sizes of deleted documents */ - int nChng = 0; /* Net change in number of documents */ - int bInsertDone = 0; - - assert( p->pSegments==0 ); - assert( - nArg==1 /* DELETE operations */ - || nArg==(2 + p->nColumn + 3) /* INSERT or UPDATE operations */ - ); - - /* Check for a "special" INSERT operation. One of the form: - ** - ** INSERT INTO xyz(xyz) VALUES('command'); - */ - if( nArg>1 - && sqlite3_value_type(apVal[0])==SQLITE_NULL - && sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL - ){ - rc = fts3SpecialInsert(p, apVal[p->nColumn+2]); - goto update_out; - } - - if( nArg>1 && sqlite3_value_int(apVal[2 + p->nColumn + 2])<0 ){ - rc = SQLITE_CONSTRAINT; - goto update_out; - } - - /* Allocate space to hold the change in document sizes */ - aSzDel = sqlite3_malloc( sizeof(aSzDel[0])*(p->nColumn+1)*2 ); - if( aSzDel==0 ){ - rc = SQLITE_NOMEM; - goto update_out; - } - aSzIns = &aSzDel[p->nColumn+1]; - memset(aSzDel, 0, sizeof(aSzDel[0])*(p->nColumn+1)*2); - - /* If this is an INSERT operation, or an UPDATE that modifies the rowid - ** value, then this operation requires constraint handling. - ** - ** If the on-conflict mode is REPLACE, this means that the existing row - ** should be deleted from the database before inserting the new row. Or, - ** if the on-conflict mode is other than REPLACE, then this method must - ** detect the conflict and return SQLITE_CONSTRAINT before beginning to - ** modify the database file. - */ - if( nArg>1 && p->zContentTbl==0 ){ - /* Find the value object that holds the new rowid value. */ - sqlite3_value *pNewRowid = apVal[3+p->nColumn]; - if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){ - pNewRowid = apVal[1]; - } - - if( sqlite3_value_type(pNewRowid)!=SQLITE_NULL && ( - sqlite3_value_type(apVal[0])==SQLITE_NULL - || sqlite3_value_int64(apVal[0])!=sqlite3_value_int64(pNewRowid) - )){ - /* The new rowid is not NULL (in this case the rowid will be - ** automatically assigned and there is no chance of a conflict), and - ** the statement is either an INSERT or an UPDATE that modifies the - ** rowid column. So if the conflict mode is REPLACE, then delete any - ** existing row with rowid=pNewRowid. - ** - ** Or, if the conflict mode is not REPLACE, insert the new record into - ** the %_content table. If we hit the duplicate rowid constraint (or any - ** other error) while doing so, return immediately. - ** - ** This branch may also run if pNewRowid contains a value that cannot - ** be losslessly converted to an integer. In this case, the eventual - ** call to fts3InsertData() (either just below or further on in this - ** function) will return SQLITE_MISMATCH. If fts3DeleteByRowid is - ** invoked, it will delete zero rows (since no row will have - ** docid=$pNewRowid if $pNewRowid is not an integer value). - */ - if( sqlite3_vtab_on_conflict(p->db)==SQLITE_REPLACE ){ - rc = fts3DeleteByRowid(p, pNewRowid, &nChng, aSzDel); - }else{ - rc = fts3InsertData(p, apVal, pRowid); - bInsertDone = 1; - } - } - } - if( rc!=SQLITE_OK ){ - goto update_out; - } - - /* If this is a DELETE or UPDATE operation, remove the old record. */ - if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ - assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER ); - rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel); - isRemove = 1; - } - - /* If this is an INSERT or UPDATE operation, insert the new record. */ - if( nArg>1 && rc==SQLITE_OK ){ - int iLangid = sqlite3_value_int(apVal[2 + p->nColumn + 2]); - if( bInsertDone==0 ){ - rc = fts3InsertData(p, apVal, pRowid); - if( rc==SQLITE_CONSTRAINT && p->zContentTbl==0 ){ - rc = FTS_CORRUPT_VTAB; - } - } - if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){ - rc = fts3PendingTermsDocid(p, iLangid, *pRowid); - } - if( rc==SQLITE_OK ){ - assert( p->iPrevDocid==*pRowid ); - rc = fts3InsertTerms(p, iLangid, apVal, aSzIns); - } - if( p->bHasDocsize ){ - fts3InsertDocsize(&rc, p, aSzIns); - } - nChng++; - } - - if( p->bFts4 ){ - fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng); - } - - update_out: - sqlite3_free(aSzDel); - sqlite3Fts3SegmentsClose(p); - return rc; -} - -/* -** Flush any data in the pending-terms hash table to disk. If successful, -** merge all segments in the database (including the new segment, if -** there was any data to flush) into a single segment. -*/ -SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){ - int rc; - rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0); - if( rc==SQLITE_OK ){ - rc = fts3DoOptimize(p, 1); - if( rc==SQLITE_OK || rc==SQLITE_DONE ){ - int rc2 = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); - if( rc2!=SQLITE_OK ) rc = rc2; - }else{ - sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0); - sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); - } - } - sqlite3Fts3SegmentsClose(p); - return rc; -} - -#endif - -/************** End of fts3_write.c ******************************************/ -/************** Begin file fts3_snippet.c ************************************/ -/* -** 2009 Oct 23 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -*/ - -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* #include */ -/* #include */ - -/* -** Characters that may appear in the second argument to matchinfo(). -*/ -#define FTS3_MATCHINFO_NPHRASE 'p' /* 1 value */ -#define FTS3_MATCHINFO_NCOL 'c' /* 1 value */ -#define FTS3_MATCHINFO_NDOC 'n' /* 1 value */ -#define FTS3_MATCHINFO_AVGLENGTH 'a' /* nCol values */ -#define FTS3_MATCHINFO_LENGTH 'l' /* nCol values */ -#define FTS3_MATCHINFO_LCS 's' /* nCol values */ -#define FTS3_MATCHINFO_HITS 'x' /* 3*nCol*nPhrase values */ - -/* -** The default value for the second argument to matchinfo(). -*/ -#define FTS3_MATCHINFO_DEFAULT "pcx" - - -/* -** Used as an fts3ExprIterate() context when loading phrase doclists to -** Fts3Expr.aDoclist[]/nDoclist. -*/ -typedef struct LoadDoclistCtx LoadDoclistCtx; -struct LoadDoclistCtx { - Fts3Cursor *pCsr; /* FTS3 Cursor */ - int nPhrase; /* Number of phrases seen so far */ - int nToken; /* Number of tokens seen so far */ -}; - -/* -** The following types are used as part of the implementation of the -** fts3BestSnippet() routine. -*/ -typedef struct SnippetIter SnippetIter; -typedef struct SnippetPhrase SnippetPhrase; -typedef struct SnippetFragment SnippetFragment; - -struct SnippetIter { - Fts3Cursor *pCsr; /* Cursor snippet is being generated from */ - int iCol; /* Extract snippet from this column */ - int nSnippet; /* Requested snippet length (in tokens) */ - int nPhrase; /* Number of phrases in query */ - SnippetPhrase *aPhrase; /* Array of size nPhrase */ - int iCurrent; /* First token of current snippet */ -}; - -struct SnippetPhrase { - int nToken; /* Number of tokens in phrase */ - char *pList; /* Pointer to start of phrase position list */ - int iHead; /* Next value in position list */ - char *pHead; /* Position list data following iHead */ - int iTail; /* Next value in trailing position list */ - char *pTail; /* Position list data following iTail */ -}; - -struct SnippetFragment { - int iCol; /* Column snippet is extracted from */ - int iPos; /* Index of first token in snippet */ - u64 covered; /* Mask of query phrases covered */ - u64 hlmask; /* Mask of snippet terms to highlight */ -}; - -/* -** This type is used as an fts3ExprIterate() context object while -** accumulating the data returned by the matchinfo() function. -*/ -typedef struct MatchInfo MatchInfo; -struct MatchInfo { - Fts3Cursor *pCursor; /* FTS3 Cursor */ - int nCol; /* Number of columns in table */ - int nPhrase; /* Number of matchable phrases in query */ - sqlite3_int64 nDoc; /* Number of docs in database */ - u32 *aMatchinfo; /* Pre-allocated buffer */ -}; - - - -/* -** The snippet() and offsets() functions both return text values. An instance -** of the following structure is used to accumulate those values while the -** functions are running. See fts3StringAppend() for details. -*/ -typedef struct StrBuffer StrBuffer; -struct StrBuffer { - char *z; /* Pointer to buffer containing string */ - int n; /* Length of z in bytes (excl. nul-term) */ - int nAlloc; /* Allocated size of buffer z in bytes */ -}; - - -/* -** This function is used to help iterate through a position-list. A position -** list is a list of unique integers, sorted from smallest to largest. Each -** element of the list is represented by an FTS3 varint that takes the value -** of the difference between the current element and the previous one plus -** two. For example, to store the position-list: -** -** 4 9 113 -** -** the three varints: -** -** 6 7 106 -** -** are encoded. -** -** When this function is called, *pp points to the start of an element of -** the list. *piPos contains the value of the previous entry in the list. -** After it returns, *piPos contains the value of the next element of the -** list and *pp is advanced to the following varint. -*/ -static void fts3GetDeltaPosition(char **pp, int *piPos){ - int iVal; - *pp += sqlite3Fts3GetVarint32(*pp, &iVal); - *piPos += (iVal-2); -} - -/* -** Helper function for fts3ExprIterate() (see below). -*/ -static int fts3ExprIterate2( - Fts3Expr *pExpr, /* Expression to iterate phrases of */ - int *piPhrase, /* Pointer to phrase counter */ - int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */ - void *pCtx /* Second argument to pass to callback */ -){ - int rc; /* Return code */ - int eType = pExpr->eType; /* Type of expression node pExpr */ - - if( eType!=FTSQUERY_PHRASE ){ - assert( pExpr->pLeft && pExpr->pRight ); - rc = fts3ExprIterate2(pExpr->pLeft, piPhrase, x, pCtx); - if( rc==SQLITE_OK && eType!=FTSQUERY_NOT ){ - rc = fts3ExprIterate2(pExpr->pRight, piPhrase, x, pCtx); - } - }else{ - rc = x(pExpr, *piPhrase, pCtx); - (*piPhrase)++; - } - return rc; -} - -/* -** Iterate through all phrase nodes in an FTS3 query, except those that -** are part of a sub-tree that is the right-hand-side of a NOT operator. -** For each phrase node found, the supplied callback function is invoked. -** -** If the callback function returns anything other than SQLITE_OK, -** the iteration is abandoned and the error code returned immediately. -** Otherwise, SQLITE_OK is returned after a callback has been made for -** all eligible phrase nodes. -*/ -static int fts3ExprIterate( - Fts3Expr *pExpr, /* Expression to iterate phrases of */ - int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */ - void *pCtx /* Second argument to pass to callback */ -){ - int iPhrase = 0; /* Variable used as the phrase counter */ - return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx); -} - -/* -** This is an fts3ExprIterate() callback used while loading the doclists -** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also -** fts3ExprLoadDoclists(). -*/ -static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ - int rc = SQLITE_OK; - Fts3Phrase *pPhrase = pExpr->pPhrase; - LoadDoclistCtx *p = (LoadDoclistCtx *)ctx; - - UNUSED_PARAMETER(iPhrase); - - p->nPhrase++; - p->nToken += pPhrase->nToken; - - return rc; -} - -/* -** Load the doclists for each phrase in the query associated with FTS3 cursor -** pCsr. -** -** If pnPhrase is not NULL, then *pnPhrase is set to the number of matchable -** phrases in the expression (all phrases except those directly or -** indirectly descended from the right-hand-side of a NOT operator). If -** pnToken is not NULL, then it is set to the number of tokens in all -** matchable phrases of the expression. -*/ -static int fts3ExprLoadDoclists( - Fts3Cursor *pCsr, /* Fts3 cursor for current query */ - int *pnPhrase, /* OUT: Number of phrases in query */ - int *pnToken /* OUT: Number of tokens in query */ -){ - int rc; /* Return Code */ - LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */ - sCtx.pCsr = pCsr; - rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb, (void *)&sCtx); - if( pnPhrase ) *pnPhrase = sCtx.nPhrase; - if( pnToken ) *pnToken = sCtx.nToken; - return rc; -} - -static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ - (*(int *)ctx)++; - UNUSED_PARAMETER(pExpr); - UNUSED_PARAMETER(iPhrase); - return SQLITE_OK; -} -static int fts3ExprPhraseCount(Fts3Expr *pExpr){ - int nPhrase = 0; - (void)fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase); - return nPhrase; -} - -/* -** Advance the position list iterator specified by the first two -** arguments so that it points to the first element with a value greater -** than or equal to parameter iNext. -*/ -static void fts3SnippetAdvance(char **ppIter, int *piIter, int iNext){ - char *pIter = *ppIter; - if( pIter ){ - int iIter = *piIter; - - while( iIteriCurrent<0 ){ - /* The SnippetIter object has just been initialized. The first snippet - ** candidate always starts at offset 0 (even if this candidate has a - ** score of 0.0). - */ - pIter->iCurrent = 0; - - /* Advance the 'head' iterator of each phrase to the first offset that - ** is greater than or equal to (iNext+nSnippet). - */ - for(i=0; inPhrase; i++){ - SnippetPhrase *pPhrase = &pIter->aPhrase[i]; - fts3SnippetAdvance(&pPhrase->pHead, &pPhrase->iHead, pIter->nSnippet); - } - }else{ - int iStart; - int iEnd = 0x7FFFFFFF; - - for(i=0; inPhrase; i++){ - SnippetPhrase *pPhrase = &pIter->aPhrase[i]; - if( pPhrase->pHead && pPhrase->iHeadiHead; - } - } - if( iEnd==0x7FFFFFFF ){ - return 1; - } - - pIter->iCurrent = iStart = iEnd - pIter->nSnippet + 1; - for(i=0; inPhrase; i++){ - SnippetPhrase *pPhrase = &pIter->aPhrase[i]; - fts3SnippetAdvance(&pPhrase->pHead, &pPhrase->iHead, iEnd+1); - fts3SnippetAdvance(&pPhrase->pTail, &pPhrase->iTail, iStart); - } - } - - return 0; -} - -/* -** Retrieve information about the current candidate snippet of snippet -** iterator pIter. -*/ -static void fts3SnippetDetails( - SnippetIter *pIter, /* Snippet iterator */ - u64 mCovered, /* Bitmask of phrases already covered */ - int *piToken, /* OUT: First token of proposed snippet */ - int *piScore, /* OUT: "Score" for this snippet */ - u64 *pmCover, /* OUT: Bitmask of phrases covered */ - u64 *pmHighlight /* OUT: Bitmask of terms to highlight */ -){ - int iStart = pIter->iCurrent; /* First token of snippet */ - int iScore = 0; /* Score of this snippet */ - int i; /* Loop counter */ - u64 mCover = 0; /* Mask of phrases covered by this snippet */ - u64 mHighlight = 0; /* Mask of tokens to highlight in snippet */ - - for(i=0; inPhrase; i++){ - SnippetPhrase *pPhrase = &pIter->aPhrase[i]; - if( pPhrase->pTail ){ - char *pCsr = pPhrase->pTail; - int iCsr = pPhrase->iTail; - - while( iCsr<(iStart+pIter->nSnippet) ){ - int j; - u64 mPhrase = (u64)1 << i; - u64 mPos = (u64)1 << (iCsr - iStart); - assert( iCsr>=iStart ); - if( (mCover|mCovered)&mPhrase ){ - iScore++; - }else{ - iScore += 1000; - } - mCover |= mPhrase; - - for(j=0; jnToken; j++){ - mHighlight |= (mPos>>j); - } - - if( 0==(*pCsr & 0x0FE) ) break; - fts3GetDeltaPosition(&pCsr, &iCsr); - } - } - } - - /* Set the output variables before returning. */ - *piToken = iStart; - *piScore = iScore; - *pmCover = mCover; - *pmHighlight = mHighlight; -} - -/* -** This function is an fts3ExprIterate() callback used by fts3BestSnippet(). -** Each invocation populates an element of the SnippetIter.aPhrase[] array. -*/ -static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ - SnippetIter *p = (SnippetIter *)ctx; - SnippetPhrase *pPhrase = &p->aPhrase[iPhrase]; - char *pCsr; - int rc; - - pPhrase->nToken = pExpr->pPhrase->nToken; - rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pCsr); - assert( rc==SQLITE_OK || pCsr==0 ); - if( pCsr ){ - int iFirst = 0; - pPhrase->pList = pCsr; - fts3GetDeltaPosition(&pCsr, &iFirst); - assert( iFirst>=0 ); - pPhrase->pHead = pCsr; - pPhrase->pTail = pCsr; - pPhrase->iHead = iFirst; - pPhrase->iTail = iFirst; - }else{ - assert( rc!=SQLITE_OK || ( - pPhrase->pList==0 && pPhrase->pHead==0 && pPhrase->pTail==0 - )); - } - - return rc; -} - -/* -** Select the fragment of text consisting of nFragment contiguous tokens -** from column iCol that represent the "best" snippet. The best snippet -** is the snippet with the highest score, where scores are calculated -** by adding: -** -** (a) +1 point for each occurrence of a matchable phrase in the snippet. -** -** (b) +1000 points for the first occurrence of each matchable phrase in -** the snippet for which the corresponding mCovered bit is not set. -** -** The selected snippet parameters are stored in structure *pFragment before -** returning. The score of the selected snippet is stored in *piScore -** before returning. -*/ -static int fts3BestSnippet( - int nSnippet, /* Desired snippet length */ - Fts3Cursor *pCsr, /* Cursor to create snippet for */ - int iCol, /* Index of column to create snippet from */ - u64 mCovered, /* Mask of phrases already covered */ - u64 *pmSeen, /* IN/OUT: Mask of phrases seen */ - SnippetFragment *pFragment, /* OUT: Best snippet found */ - int *piScore /* OUT: Score of snippet pFragment */ -){ - int rc; /* Return Code */ - int nList; /* Number of phrases in expression */ - SnippetIter sIter; /* Iterates through snippet candidates */ - int nByte; /* Number of bytes of space to allocate */ - int iBestScore = -1; /* Best snippet score found so far */ - int i; /* Loop counter */ - - memset(&sIter, 0, sizeof(sIter)); - - /* Iterate through the phrases in the expression to count them. The same - ** callback makes sure the doclists are loaded for each phrase. - */ - rc = fts3ExprLoadDoclists(pCsr, &nList, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - - /* Now that it is known how many phrases there are, allocate and zero - ** the required space using malloc(). - */ - nByte = sizeof(SnippetPhrase) * nList; - sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc(nByte); - if( !sIter.aPhrase ){ - return SQLITE_NOMEM; - } - memset(sIter.aPhrase, 0, nByte); - - /* Initialize the contents of the SnippetIter object. Then iterate through - ** the set of phrases in the expression to populate the aPhrase[] array. - */ - sIter.pCsr = pCsr; - sIter.iCol = iCol; - sIter.nSnippet = nSnippet; - sIter.nPhrase = nList; - sIter.iCurrent = -1; - (void)fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void *)&sIter); - - /* Set the *pmSeen output variable. */ - for(i=0; iiCol = iCol; - while( !fts3SnippetNextCandidate(&sIter) ){ - int iPos; - int iScore; - u64 mCover; - u64 mHighlight; - fts3SnippetDetails(&sIter, mCovered, &iPos, &iScore, &mCover, &mHighlight); - assert( iScore>=0 ); - if( iScore>iBestScore ){ - pFragment->iPos = iPos; - pFragment->hlmask = mHighlight; - pFragment->covered = mCover; - iBestScore = iScore; - } - } - - sqlite3_free(sIter.aPhrase); - *piScore = iBestScore; - return SQLITE_OK; -} - - -/* -** Append a string to the string-buffer passed as the first argument. -** -** If nAppend is negative, then the length of the string zAppend is -** determined using strlen(). -*/ -static int fts3StringAppend( - StrBuffer *pStr, /* Buffer to append to */ - const char *zAppend, /* Pointer to data to append to buffer */ - int nAppend /* Size of zAppend in bytes (or -1) */ -){ - if( nAppend<0 ){ - nAppend = (int)strlen(zAppend); - } - - /* If there is insufficient space allocated at StrBuffer.z, use realloc() - ** to grow the buffer until so that it is big enough to accomadate the - ** appended data. - */ - if( pStr->n+nAppend+1>=pStr->nAlloc ){ - int nAlloc = pStr->nAlloc+nAppend+100; - char *zNew = sqlite3_realloc(pStr->z, nAlloc); - if( !zNew ){ - return SQLITE_NOMEM; - } - pStr->z = zNew; - pStr->nAlloc = nAlloc; - } - - /* Append the data to the string buffer. */ - memcpy(&pStr->z[pStr->n], zAppend, nAppend); - pStr->n += nAppend; - pStr->z[pStr->n] = '\0'; - - return SQLITE_OK; -} - -/* -** The fts3BestSnippet() function often selects snippets that end with a -** query term. That is, the final term of the snippet is always a term -** that requires highlighting. For example, if 'X' is a highlighted term -** and '.' is a non-highlighted term, BestSnippet() may select: -** -** ........X.....X -** -** This function "shifts" the beginning of the snippet forward in the -** document so that there are approximately the same number of -** non-highlighted terms to the right of the final highlighted term as there -** are to the left of the first highlighted term. For example, to this: -** -** ....X.....X.... -** -** This is done as part of extracting the snippet text, not when selecting -** the snippet. Snippet selection is done based on doclists only, so there -** is no way for fts3BestSnippet() to know whether or not the document -** actually contains terms that follow the final highlighted term. -*/ -static int fts3SnippetShift( - Fts3Table *pTab, /* FTS3 table snippet comes from */ - int iLangid, /* Language id to use in tokenizing */ - int nSnippet, /* Number of tokens desired for snippet */ - const char *zDoc, /* Document text to extract snippet from */ - int nDoc, /* Size of buffer zDoc in bytes */ - int *piPos, /* IN/OUT: First token of snippet */ - u64 *pHlmask /* IN/OUT: Mask of tokens to highlight */ -){ - u64 hlmask = *pHlmask; /* Local copy of initial highlight-mask */ - - if( hlmask ){ - int nLeft; /* Tokens to the left of first highlight */ - int nRight; /* Tokens to the right of last highlight */ - int nDesired; /* Ideal number of tokens to shift forward */ - - for(nLeft=0; !(hlmask & ((u64)1 << nLeft)); nLeft++); - for(nRight=0; !(hlmask & ((u64)1 << (nSnippet-1-nRight))); nRight++); - nDesired = (nLeft-nRight)/2; - - /* Ideally, the start of the snippet should be pushed forward in the - ** document nDesired tokens. This block checks if there are actually - ** nDesired tokens to the right of the snippet. If so, *piPos and - ** *pHlMask are updated to shift the snippet nDesired tokens to the - ** right. Otherwise, the snippet is shifted by the number of tokens - ** available. - */ - if( nDesired>0 ){ - int nShift; /* Number of tokens to shift snippet by */ - int iCurrent = 0; /* Token counter */ - int rc; /* Return Code */ - sqlite3_tokenizer_module *pMod; - sqlite3_tokenizer_cursor *pC; - pMod = (sqlite3_tokenizer_module *)pTab->pTokenizer->pModule; - - /* Open a cursor on zDoc/nDoc. Check if there are (nSnippet+nDesired) - ** or more tokens in zDoc/nDoc. - */ - rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, iLangid, zDoc, nDoc, &pC); - if( rc!=SQLITE_OK ){ - return rc; - } - while( rc==SQLITE_OK && iCurrent<(nSnippet+nDesired) ){ - const char *ZDUMMY; int DUMMY1 = 0, DUMMY2 = 0, DUMMY3 = 0; - rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &DUMMY2, &DUMMY3, &iCurrent); - } - pMod->xClose(pC); - if( rc!=SQLITE_OK && rc!=SQLITE_DONE ){ return rc; } - - nShift = (rc==SQLITE_DONE)+iCurrent-nSnippet; - assert( nShift<=nDesired ); - if( nShift>0 ){ - *piPos += nShift; - *pHlmask = hlmask >> nShift; - } - } - } - return SQLITE_OK; -} - -/* -** Extract the snippet text for fragment pFragment from cursor pCsr and -** append it to string buffer pOut. -*/ -static int fts3SnippetText( - Fts3Cursor *pCsr, /* FTS3 Cursor */ - SnippetFragment *pFragment, /* Snippet to extract */ - int iFragment, /* Fragment number */ - int isLast, /* True for final fragment in snippet */ - int nSnippet, /* Number of tokens in extracted snippet */ - const char *zOpen, /* String inserted before highlighted term */ - const char *zClose, /* String inserted after highlighted term */ - const char *zEllipsis, /* String inserted between snippets */ - StrBuffer *pOut /* Write output here */ -){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - int rc; /* Return code */ - const char *zDoc; /* Document text to extract snippet from */ - int nDoc; /* Size of zDoc in bytes */ - int iCurrent = 0; /* Current token number of document */ - int iEnd = 0; /* Byte offset of end of current token */ - int isShiftDone = 0; /* True after snippet is shifted */ - int iPos = pFragment->iPos; /* First token of snippet */ - u64 hlmask = pFragment->hlmask; /* Highlight-mask for snippet */ - int iCol = pFragment->iCol+1; /* Query column to extract text from */ - sqlite3_tokenizer_module *pMod; /* Tokenizer module methods object */ - sqlite3_tokenizer_cursor *pC; /* Tokenizer cursor open on zDoc/nDoc */ - - zDoc = (const char *)sqlite3_column_text(pCsr->pStmt, iCol); - if( zDoc==0 ){ - if( sqlite3_column_type(pCsr->pStmt, iCol)!=SQLITE_NULL ){ - return SQLITE_NOMEM; - } - return SQLITE_OK; - } - nDoc = sqlite3_column_bytes(pCsr->pStmt, iCol); - - /* Open a token cursor on the document. */ - pMod = (sqlite3_tokenizer_module *)pTab->pTokenizer->pModule; - rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, pCsr->iLangid, zDoc,nDoc,&pC); - if( rc!=SQLITE_OK ){ - return rc; - } - - while( rc==SQLITE_OK ){ - const char *ZDUMMY; /* Dummy argument used with tokenizer */ - int DUMMY1 = -1; /* Dummy argument used with tokenizer */ - int iBegin = 0; /* Offset in zDoc of start of token */ - int iFin = 0; /* Offset in zDoc of end of token */ - int isHighlight = 0; /* True for highlighted terms */ - - /* Variable DUMMY1 is initialized to a negative value above. Elsewhere - ** in the FTS code the variable that the third argument to xNext points to - ** is initialized to zero before the first (*but not necessarily - ** subsequent*) call to xNext(). This is done for a particular application - ** that needs to know whether or not the tokenizer is being used for - ** snippet generation or for some other purpose. - ** - ** Extreme care is required when writing code to depend on this - ** initialization. It is not a documented part of the tokenizer interface. - ** If a tokenizer is used directly by any code outside of FTS, this - ** convention might not be respected. */ - rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &iBegin, &iFin, &iCurrent); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ - /* Special case - the last token of the snippet is also the last token - ** of the column. Append any punctuation that occurred between the end - ** of the previous token and the end of the document to the output. - ** Then break out of the loop. */ - rc = fts3StringAppend(pOut, &zDoc[iEnd], -1); - } - break; - } - if( iCurrentiLangid, nSnippet, &zDoc[iBegin], n, &iPos, &hlmask - ); - isShiftDone = 1; - - /* Now that the shift has been done, check if the initial "..." are - ** required. They are required if (a) this is not the first fragment, - ** or (b) this fragment does not begin at position 0 of its column. - */ - if( rc==SQLITE_OK && (iPos>0 || iFragment>0) ){ - rc = fts3StringAppend(pOut, zEllipsis, -1); - } - if( rc!=SQLITE_OK || iCurrent=(iPos+nSnippet) ){ - if( isLast ){ - rc = fts3StringAppend(pOut, zEllipsis, -1); - } - break; - } - - /* Set isHighlight to true if this term should be highlighted. */ - isHighlight = (hlmask & ((u64)1 << (iCurrent-iPos)))!=0; - - if( iCurrent>iPos ) rc = fts3StringAppend(pOut, &zDoc[iEnd], iBegin-iEnd); - if( rc==SQLITE_OK && isHighlight ) rc = fts3StringAppend(pOut, zOpen, -1); - if( rc==SQLITE_OK ) rc = fts3StringAppend(pOut, &zDoc[iBegin], iFin-iBegin); - if( rc==SQLITE_OK && isHighlight ) rc = fts3StringAppend(pOut, zClose, -1); - - iEnd = iFin; - } - - pMod->xClose(pC); - return rc; -} - - -/* -** This function is used to count the entries in a column-list (a -** delta-encoded list of term offsets within a single column of a single -** row). When this function is called, *ppCollist should point to the -** beginning of the first varint in the column-list (the varint that -** contains the position of the first matching term in the column data). -** Before returning, *ppCollist is set to point to the first byte after -** the last varint in the column-list (either the 0x00 signifying the end -** of the position-list, or the 0x01 that precedes the column number of -** the next column in the position-list). -** -** The number of elements in the column-list is returned. -*/ -static int fts3ColumnlistCount(char **ppCollist){ - char *pEnd = *ppCollist; - char c = 0; - int nEntry = 0; - - /* A column-list is terminated by either a 0x01 or 0x00. */ - while( 0xFE & (*pEnd | c) ){ - c = *pEnd++ & 0x80; - if( !c ) nEntry++; - } - - *ppCollist = pEnd; - return nEntry; -} - -/* -** fts3ExprIterate() callback used to collect the "global" matchinfo stats -** for a single query. -** -** fts3ExprIterate() callback to load the 'global' elements of a -** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements -** of the matchinfo array that are constant for all rows returned by the -** current query. -** -** Argument pCtx is actually a pointer to a struct of type MatchInfo. This -** function populates Matchinfo.aMatchinfo[] as follows: -** -** for(iCol=0; iColpCursor, pExpr, &p->aMatchinfo[3*iPhrase*p->nCol] - ); -} - -/* -** fts3ExprIterate() callback used to collect the "local" part of the -** FTS3_MATCHINFO_HITS array. The local stats are those elements of the -** array that are different for each row returned by the query. -*/ -static int fts3ExprLocalHitsCb( - Fts3Expr *pExpr, /* Phrase expression node */ - int iPhrase, /* Phrase number */ - void *pCtx /* Pointer to MatchInfo structure */ -){ - int rc = SQLITE_OK; - MatchInfo *p = (MatchInfo *)pCtx; - int iStart = iPhrase * p->nCol * 3; - int i; - - for(i=0; inCol && rc==SQLITE_OK; i++){ - char *pCsr; - rc = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i, &pCsr); - if( pCsr ){ - p->aMatchinfo[iStart+i*3] = fts3ColumnlistCount(&pCsr); - }else{ - p->aMatchinfo[iStart+i*3] = 0; - } - } - - return rc; -} - -static int fts3MatchinfoCheck( - Fts3Table *pTab, - char cArg, - char **pzErr -){ - if( (cArg==FTS3_MATCHINFO_NPHRASE) - || (cArg==FTS3_MATCHINFO_NCOL) - || (cArg==FTS3_MATCHINFO_NDOC && pTab->bFts4) - || (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bFts4) - || (cArg==FTS3_MATCHINFO_LENGTH && pTab->bHasDocsize) - || (cArg==FTS3_MATCHINFO_LCS) - || (cArg==FTS3_MATCHINFO_HITS) - ){ - return SQLITE_OK; - } - *pzErr = sqlite3_mprintf("unrecognized matchinfo request: %c", cArg); - return SQLITE_ERROR; -} - -static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ - int nVal; /* Number of integers output by cArg */ - - switch( cArg ){ - case FTS3_MATCHINFO_NDOC: - case FTS3_MATCHINFO_NPHRASE: - case FTS3_MATCHINFO_NCOL: - nVal = 1; - break; - - case FTS3_MATCHINFO_AVGLENGTH: - case FTS3_MATCHINFO_LENGTH: - case FTS3_MATCHINFO_LCS: - nVal = pInfo->nCol; - break; - - default: - assert( cArg==FTS3_MATCHINFO_HITS ); - nVal = pInfo->nCol * pInfo->nPhrase * 3; - break; - } - - return nVal; -} - -static int fts3MatchinfoSelectDoctotal( - Fts3Table *pTab, - sqlite3_stmt **ppStmt, - sqlite3_int64 *pnDoc, - const char **paLen -){ - sqlite3_stmt *pStmt; - const char *a; - sqlite3_int64 nDoc; - - if( !*ppStmt ){ - int rc = sqlite3Fts3SelectDoctotal(pTab, ppStmt); - if( rc!=SQLITE_OK ) return rc; - } - pStmt = *ppStmt; - assert( sqlite3_data_count(pStmt)==1 ); - - a = sqlite3_column_blob(pStmt, 0); - a += sqlite3Fts3GetVarint(a, &nDoc); - if( nDoc==0 ) return FTS_CORRUPT_VTAB; - *pnDoc = (u32)nDoc; - - if( paLen ) *paLen = a; - return SQLITE_OK; -} - -/* -** An instance of the following structure is used to store state while -** iterating through a multi-column position-list corresponding to the -** hits for a single phrase on a single row in order to calculate the -** values for a matchinfo() FTS3_MATCHINFO_LCS request. -*/ -typedef struct LcsIterator LcsIterator; -struct LcsIterator { - Fts3Expr *pExpr; /* Pointer to phrase expression */ - int iPosOffset; /* Tokens count up to end of this phrase */ - char *pRead; /* Cursor used to iterate through aDoclist */ - int iPos; /* Current position */ -}; - -/* -** If LcsIterator.iCol is set to the following value, the iterator has -** finished iterating through all offsets for all columns. -*/ -#define LCS_ITERATOR_FINISHED 0x7FFFFFFF; - -static int fts3MatchinfoLcsCb( - Fts3Expr *pExpr, /* Phrase expression node */ - int iPhrase, /* Phrase number (numbered from zero) */ - void *pCtx /* Pointer to MatchInfo structure */ -){ - LcsIterator *aIter = (LcsIterator *)pCtx; - aIter[iPhrase].pExpr = pExpr; - return SQLITE_OK; -} - -/* -** Advance the iterator passed as an argument to the next position. Return -** 1 if the iterator is at EOF or if it now points to the start of the -** position list for the next column. -*/ -static int fts3LcsIteratorAdvance(LcsIterator *pIter){ - char *pRead = pIter->pRead; - sqlite3_int64 iRead; - int rc = 0; - - pRead += sqlite3Fts3GetVarint(pRead, &iRead); - if( iRead==0 || iRead==1 ){ - pRead = 0; - rc = 1; - }else{ - pIter->iPos += (int)(iRead-2); - } - - pIter->pRead = pRead; - return rc; -} - -/* -** This function implements the FTS3_MATCHINFO_LCS matchinfo() flag. -** -** If the call is successful, the longest-common-substring lengths for each -** column are written into the first nCol elements of the pInfo->aMatchinfo[] -** array before returning. SQLITE_OK is returned in this case. -** -** Otherwise, if an error occurs, an SQLite error code is returned and the -** data written to the first nCol elements of pInfo->aMatchinfo[] is -** undefined. -*/ -static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ - LcsIterator *aIter; - int i; - int iCol; - int nToken = 0; - - /* Allocate and populate the array of LcsIterator objects. The array - ** contains one element for each matchable phrase in the query. - **/ - aIter = sqlite3_malloc(sizeof(LcsIterator) * pCsr->nPhrase); - if( !aIter ) return SQLITE_NOMEM; - memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase); - (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); - - for(i=0; inPhrase; i++){ - LcsIterator *pIter = &aIter[i]; - nToken -= pIter->pExpr->pPhrase->nToken; - pIter->iPosOffset = nToken; - } - - for(iCol=0; iColnCol; iCol++){ - int nLcs = 0; /* LCS value for this column */ - int nLive = 0; /* Number of iterators in aIter not at EOF */ - - for(i=0; inPhrase; i++){ - int rc; - LcsIterator *pIt = &aIter[i]; - rc = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol, &pIt->pRead); - if( rc!=SQLITE_OK ) return rc; - if( pIt->pRead ){ - pIt->iPos = pIt->iPosOffset; - fts3LcsIteratorAdvance(&aIter[i]); - nLive++; - } - } - - while( nLive>0 ){ - LcsIterator *pAdv = 0; /* The iterator to advance by one position */ - int nThisLcs = 0; /* LCS for the current iterator positions */ - - for(i=0; inPhrase; i++){ - LcsIterator *pIter = &aIter[i]; - if( pIter->pRead==0 ){ - /* This iterator is already at EOF for this column. */ - nThisLcs = 0; - }else{ - if( pAdv==0 || pIter->iPosiPos ){ - pAdv = pIter; - } - if( nThisLcs==0 || pIter->iPos==pIter[-1].iPos ){ - nThisLcs++; - }else{ - nThisLcs = 1; - } - if( nThisLcs>nLcs ) nLcs = nThisLcs; - } - } - if( fts3LcsIteratorAdvance(pAdv) ) nLive--; - } - - pInfo->aMatchinfo[iCol] = nLcs; - } - - sqlite3_free(aIter); - return SQLITE_OK; -} - -/* -** Populate the buffer pInfo->aMatchinfo[] with an array of integers to -** be returned by the matchinfo() function. Argument zArg contains the -** format string passed as the second argument to matchinfo (or the -** default value "pcx" if no second argument was specified). The format -** string has already been validated and the pInfo->aMatchinfo[] array -** is guaranteed to be large enough for the output. -** -** If bGlobal is true, then populate all fields of the matchinfo() output. -** If it is false, then assume that those fields that do not change between -** rows (i.e. FTS3_MATCHINFO_NPHRASE, NCOL, NDOC, AVGLENGTH and part of HITS) -** have already been populated. -** -** Return SQLITE_OK if successful, or an SQLite error code if an error -** occurs. If a value other than SQLITE_OK is returned, the state the -** pInfo->aMatchinfo[] buffer is left in is undefined. -*/ -static int fts3MatchinfoValues( - Fts3Cursor *pCsr, /* FTS3 cursor object */ - int bGlobal, /* True to grab the global stats */ - MatchInfo *pInfo, /* Matchinfo context object */ - const char *zArg /* Matchinfo format string */ -){ - int rc = SQLITE_OK; - int i; - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - sqlite3_stmt *pSelect = 0; - - for(i=0; rc==SQLITE_OK && zArg[i]; i++){ - - switch( zArg[i] ){ - case FTS3_MATCHINFO_NPHRASE: - if( bGlobal ) pInfo->aMatchinfo[0] = pInfo->nPhrase; - break; - - case FTS3_MATCHINFO_NCOL: - if( bGlobal ) pInfo->aMatchinfo[0] = pInfo->nCol; - break; - - case FTS3_MATCHINFO_NDOC: - if( bGlobal ){ - sqlite3_int64 nDoc = 0; - rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0); - pInfo->aMatchinfo[0] = (u32)nDoc; - } - break; - - case FTS3_MATCHINFO_AVGLENGTH: - if( bGlobal ){ - sqlite3_int64 nDoc; /* Number of rows in table */ - const char *a; /* Aggregate column length array */ - - rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a); - if( rc==SQLITE_OK ){ - int iCol; - for(iCol=0; iColnCol; iCol++){ - u32 iVal; - sqlite3_int64 nToken; - a += sqlite3Fts3GetVarint(a, &nToken); - iVal = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc); - pInfo->aMatchinfo[iCol] = iVal; - } - } - } - break; - - case FTS3_MATCHINFO_LENGTH: { - sqlite3_stmt *pSelectDocsize = 0; - rc = sqlite3Fts3SelectDocsize(pTab, pCsr->iPrevId, &pSelectDocsize); - if( rc==SQLITE_OK ){ - int iCol; - const char *a = sqlite3_column_blob(pSelectDocsize, 0); - for(iCol=0; iColnCol; iCol++){ - sqlite3_int64 nToken; - a += sqlite3Fts3GetVarint(a, &nToken); - pInfo->aMatchinfo[iCol] = (u32)nToken; - } - } - sqlite3_reset(pSelectDocsize); - break; - } - - case FTS3_MATCHINFO_LCS: - rc = fts3ExprLoadDoclists(pCsr, 0, 0); - if( rc==SQLITE_OK ){ - rc = fts3MatchinfoLcs(pCsr, pInfo); - } - break; - - default: { - Fts3Expr *pExpr; - assert( zArg[i]==FTS3_MATCHINFO_HITS ); - pExpr = pCsr->pExpr; - rc = fts3ExprLoadDoclists(pCsr, 0, 0); - if( rc!=SQLITE_OK ) break; - if( bGlobal ){ - if( pCsr->pDeferred ){ - rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc, 0); - if( rc!=SQLITE_OK ) break; - } - rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo); - if( rc!=SQLITE_OK ) break; - } - (void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo); - break; - } - } - - pInfo->aMatchinfo += fts3MatchinfoSize(pInfo, zArg[i]); - } - - sqlite3_reset(pSelect); - return rc; -} - - -/* -** Populate pCsr->aMatchinfo[] with data for the current row. The -** 'matchinfo' data is an array of 32-bit unsigned integers (C type u32). -*/ -static int fts3GetMatchinfo( - Fts3Cursor *pCsr, /* FTS3 Cursor object */ - const char *zArg /* Second argument to matchinfo() function */ -){ - MatchInfo sInfo; - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - int rc = SQLITE_OK; - int bGlobal = 0; /* Collect 'global' stats as well as local */ - - memset(&sInfo, 0, sizeof(MatchInfo)); - sInfo.pCursor = pCsr; - sInfo.nCol = pTab->nColumn; - - /* If there is cached matchinfo() data, but the format string for the - ** cache does not match the format string for this request, discard - ** the cached data. */ - if( pCsr->zMatchinfo && strcmp(pCsr->zMatchinfo, zArg) ){ - assert( pCsr->aMatchinfo ); - sqlite3_free(pCsr->aMatchinfo); - pCsr->zMatchinfo = 0; - pCsr->aMatchinfo = 0; - } - - /* If Fts3Cursor.aMatchinfo[] is NULL, then this is the first time the - ** matchinfo function has been called for this query. In this case - ** allocate the array used to accumulate the matchinfo data and - ** initialize those elements that are constant for every row. - */ - if( pCsr->aMatchinfo==0 ){ - int nMatchinfo = 0; /* Number of u32 elements in match-info */ - int nArg; /* Bytes in zArg */ - int i; /* Used to iterate through zArg */ - - /* Determine the number of phrases in the query */ - pCsr->nPhrase = fts3ExprPhraseCount(pCsr->pExpr); - sInfo.nPhrase = pCsr->nPhrase; - - /* Determine the number of integers in the buffer returned by this call. */ - for(i=0; zArg[i]; i++){ - nMatchinfo += fts3MatchinfoSize(&sInfo, zArg[i]); - } - - /* Allocate space for Fts3Cursor.aMatchinfo[] and Fts3Cursor.zMatchinfo. */ - nArg = (int)strlen(zArg); - pCsr->aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo + nArg + 1); - if( !pCsr->aMatchinfo ) return SQLITE_NOMEM; - - pCsr->zMatchinfo = (char *)&pCsr->aMatchinfo[nMatchinfo]; - pCsr->nMatchinfo = nMatchinfo; - memcpy(pCsr->zMatchinfo, zArg, nArg+1); - memset(pCsr->aMatchinfo, 0, sizeof(u32)*nMatchinfo); - pCsr->isMatchinfoNeeded = 1; - bGlobal = 1; - } - - sInfo.aMatchinfo = pCsr->aMatchinfo; - sInfo.nPhrase = pCsr->nPhrase; - if( pCsr->isMatchinfoNeeded ){ - rc = fts3MatchinfoValues(pCsr, bGlobal, &sInfo, zArg); - pCsr->isMatchinfoNeeded = 0; - } - - return rc; -} - -/* -** Implementation of snippet() function. -*/ -SQLITE_PRIVATE void sqlite3Fts3Snippet( - sqlite3_context *pCtx, /* SQLite function call context */ - Fts3Cursor *pCsr, /* Cursor object */ - const char *zStart, /* Snippet start text - "" */ - const char *zEnd, /* Snippet end text - "" */ - const char *zEllipsis, /* Snippet ellipsis text - "..." */ - int iCol, /* Extract snippet from this column */ - int nToken /* Approximate number of tokens in snippet */ -){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - int rc = SQLITE_OK; - int i; - StrBuffer res = {0, 0, 0}; - - /* The returned text includes up to four fragments of text extracted from - ** the data in the current row. The first iteration of the for(...) loop - ** below attempts to locate a single fragment of text nToken tokens in - ** size that contains at least one instance of all phrases in the query - ** expression that appear in the current row. If such a fragment of text - ** cannot be found, the second iteration of the loop attempts to locate - ** a pair of fragments, and so on. - */ - int nSnippet = 0; /* Number of fragments in this snippet */ - SnippetFragment aSnippet[4]; /* Maximum of 4 fragments per snippet */ - int nFToken = -1; /* Number of tokens in each fragment */ - - if( !pCsr->pExpr ){ - sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC); - return; - } - - for(nSnippet=1; 1; nSnippet++){ - - int iSnip; /* Loop counter 0..nSnippet-1 */ - u64 mCovered = 0; /* Bitmask of phrases covered by snippet */ - u64 mSeen = 0; /* Bitmask of phrases seen by BestSnippet() */ - - if( nToken>=0 ){ - nFToken = (nToken+nSnippet-1) / nSnippet; - }else{ - nFToken = -1 * nToken; - } - - for(iSnip=0; iSnipnColumn; iRead++){ - SnippetFragment sF = {0, 0, 0, 0}; - int iS; - if( iCol>=0 && iRead!=iCol ) continue; - - /* Find the best snippet of nFToken tokens in column iRead. */ - rc = fts3BestSnippet(nFToken, pCsr, iRead, mCovered, &mSeen, &sF, &iS); - if( rc!=SQLITE_OK ){ - goto snippet_out; - } - if( iS>iBestScore ){ - *pFragment = sF; - iBestScore = iS; - } - } - - mCovered |= pFragment->covered; - } - - /* If all query phrases seen by fts3BestSnippet() are present in at least - ** one of the nSnippet snippet fragments, break out of the loop. - */ - assert( (mCovered&mSeen)==mCovered ); - if( mSeen==mCovered || nSnippet==SizeofArray(aSnippet) ) break; - } - - assert( nFToken>0 ); - - for(i=0; ipCsr, pExpr, p->iCol, &pList); - nTerm = pExpr->pPhrase->nToken; - if( pList ){ - fts3GetDeltaPosition(&pList, &iPos); - assert( iPos>=0 ); - } - - for(iTerm=0; iTermaTerm[p->iTerm++]; - pT->iOff = nTerm-iTerm-1; - pT->pList = pList; - pT->iPos = iPos; - } - - return rc; -} - -/* -** Implementation of offsets() function. -*/ -SQLITE_PRIVATE void sqlite3Fts3Offsets( - sqlite3_context *pCtx, /* SQLite function call context */ - Fts3Cursor *pCsr /* Cursor object */ -){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - sqlite3_tokenizer_module const *pMod = pTab->pTokenizer->pModule; - int rc; /* Return Code */ - int nToken; /* Number of tokens in query */ - int iCol; /* Column currently being processed */ - StrBuffer res = {0, 0, 0}; /* Result string */ - TermOffsetCtx sCtx; /* Context for fts3ExprTermOffsetInit() */ - - if( !pCsr->pExpr ){ - sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC); - return; - } - - memset(&sCtx, 0, sizeof(sCtx)); - assert( pCsr->isRequireSeek==0 ); - - /* Count the number of terms in the query */ - rc = fts3ExprLoadDoclists(pCsr, 0, &nToken); - if( rc!=SQLITE_OK ) goto offsets_out; - - /* Allocate the array of TermOffset iterators. */ - sCtx.aTerm = (TermOffset *)sqlite3_malloc(sizeof(TermOffset)*nToken); - if( 0==sCtx.aTerm ){ - rc = SQLITE_NOMEM; - goto offsets_out; - } - sCtx.iDocid = pCsr->iPrevId; - sCtx.pCsr = pCsr; - - /* Loop through the table columns, appending offset information to - ** string-buffer res for each column. - */ - for(iCol=0; iColnColumn; iCol++){ - sqlite3_tokenizer_cursor *pC; /* Tokenizer cursor */ - const char *ZDUMMY; /* Dummy argument used with xNext() */ - int NDUMMY = 0; /* Dummy argument used with xNext() */ - int iStart = 0; - int iEnd = 0; - int iCurrent = 0; - const char *zDoc; - int nDoc; - - /* Initialize the contents of sCtx.aTerm[] for column iCol. There is - ** no way that this operation can fail, so the return code from - ** fts3ExprIterate() can be discarded. - */ - sCtx.iCol = iCol; - sCtx.iTerm = 0; - (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void *)&sCtx); - - /* Retreive the text stored in column iCol. If an SQL NULL is stored - ** in column iCol, jump immediately to the next iteration of the loop. - ** If an OOM occurs while retrieving the data (this can happen if SQLite - ** needs to transform the data from utf-16 to utf-8), return SQLITE_NOMEM - ** to the caller. - */ - zDoc = (const char *)sqlite3_column_text(pCsr->pStmt, iCol+1); - nDoc = sqlite3_column_bytes(pCsr->pStmt, iCol+1); - if( zDoc==0 ){ - if( sqlite3_column_type(pCsr->pStmt, iCol+1)==SQLITE_NULL ){ - continue; - } - rc = SQLITE_NOMEM; - goto offsets_out; - } - - /* Initialize a tokenizer iterator to iterate through column iCol. */ - rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, pCsr->iLangid, - zDoc, nDoc, &pC - ); - if( rc!=SQLITE_OK ) goto offsets_out; - - rc = pMod->xNext(pC, &ZDUMMY, &NDUMMY, &iStart, &iEnd, &iCurrent); - while( rc==SQLITE_OK ){ - int i; /* Used to loop through terms */ - int iMinPos = 0x7FFFFFFF; /* Position of next token */ - TermOffset *pTerm = 0; /* TermOffset associated with next token */ - - for(i=0; ipList && (pT->iPos-pT->iOff)iPos-pT->iOff; - pTerm = pT; - } - } - - if( !pTerm ){ - /* All offsets for this column have been gathered. */ - rc = SQLITE_DONE; - }else{ - assert( iCurrent<=iMinPos ); - if( 0==(0xFE&*pTerm->pList) ){ - pTerm->pList = 0; - }else{ - fts3GetDeltaPosition(&pTerm->pList, &pTerm->iPos); - } - while( rc==SQLITE_OK && iCurrentxNext(pC, &ZDUMMY, &NDUMMY, &iStart, &iEnd, &iCurrent); - } - if( rc==SQLITE_OK ){ - char aBuffer[64]; - sqlite3_snprintf(sizeof(aBuffer), aBuffer, - "%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart - ); - rc = fts3StringAppend(&res, aBuffer, -1); - }else if( rc==SQLITE_DONE && pTab->zContentTbl==0 ){ - rc = FTS_CORRUPT_VTAB; - } - } - } - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - } - - pMod->xClose(pC); - if( rc!=SQLITE_OK ) goto offsets_out; - } - - offsets_out: - sqlite3_free(sCtx.aTerm); - assert( rc!=SQLITE_DONE ); - sqlite3Fts3SegmentsClose(pTab); - if( rc!=SQLITE_OK ){ - sqlite3_result_error_code(pCtx, rc); - sqlite3_free(res.z); - }else{ - sqlite3_result_text(pCtx, res.z, res.n-1, sqlite3_free); - } - return; -} - -/* -** Implementation of matchinfo() function. -*/ -SQLITE_PRIVATE void sqlite3Fts3Matchinfo( - sqlite3_context *pContext, /* Function call context */ - Fts3Cursor *pCsr, /* FTS3 table cursor */ - const char *zArg /* Second arg to matchinfo() function */ -){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - int rc; - int i; - const char *zFormat; - - if( zArg ){ - for(i=0; zArg[i]; i++){ - char *zErr = 0; - if( fts3MatchinfoCheck(pTab, zArg[i], &zErr) ){ - sqlite3_result_error(pContext, zErr, -1); - sqlite3_free(zErr); - return; - } - } - zFormat = zArg; - }else{ - zFormat = FTS3_MATCHINFO_DEFAULT; - } - - if( !pCsr->pExpr ){ - sqlite3_result_blob(pContext, "", 0, SQLITE_STATIC); - return; - } - - /* Retrieve matchinfo() data. */ - rc = fts3GetMatchinfo(pCsr, zFormat); - sqlite3Fts3SegmentsClose(pTab); - - if( rc!=SQLITE_OK ){ - sqlite3_result_error_code(pContext, rc); - }else{ - int n = pCsr->nMatchinfo * sizeof(u32); - sqlite3_result_blob(pContext, pCsr->aMatchinfo, n, SQLITE_TRANSIENT); - } -} - -#endif - -/************** End of fts3_snippet.c ****************************************/ -/************** Begin file fts3_unicode.c ************************************/ -/* -** 2012 May 24 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** Implementation of the "unicode" full-text-search tokenizer. -*/ - -#ifdef SQLITE_ENABLE_FTS4_UNICODE61 - -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -/* #include */ -/* #include */ -/* #include */ -/* #include */ - - -/* -** The following two macros - READ_UTF8 and WRITE_UTF8 - have been copied -** from the sqlite3 source file utf.c. If this file is compiled as part -** of the amalgamation, they are not required. -*/ -#ifndef SQLITE_AMALGAMATION - -static const unsigned char sqlite3Utf8Trans1[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, -}; - -#define READ_UTF8(zIn, zTerm, c) \ - c = *(zIn++); \ - if( c>=0xc0 ){ \ - c = sqlite3Utf8Trans1[c-0xc0]; \ - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ - c = (c<<6) + (0x3f & *(zIn++)); \ - } \ - if( c<0x80 \ - || (c&0xFFFFF800)==0xD800 \ - || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ - } - -#define WRITE_UTF8(zOut, c) { \ - if( c<0x00080 ){ \ - *zOut++ = (u8)(c&0xFF); \ - } \ - else if( c<0x00800 ){ \ - *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \ - *zOut++ = 0x80 + (u8)(c & 0x3F); \ - } \ - else if( c<0x10000 ){ \ - *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \ - *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (u8)(c & 0x3F); \ - }else{ \ - *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \ - *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \ - *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (u8)(c & 0x3F); \ - } \ -} - -#endif /* ifndef SQLITE_AMALGAMATION */ - -typedef struct unicode_tokenizer unicode_tokenizer; -typedef struct unicode_cursor unicode_cursor; - -struct unicode_tokenizer { - sqlite3_tokenizer base; - int bRemoveDiacritic; - int nException; - int *aiException; -}; - -struct unicode_cursor { - sqlite3_tokenizer_cursor base; - const unsigned char *aInput; /* Input text being tokenized */ - int nInput; /* Size of aInput[] in bytes */ - int iOff; /* Current offset within aInput[] */ - int iToken; /* Index of next token to be returned */ - char *zToken; /* storage for current token */ - int nAlloc; /* space allocated at zToken */ -}; - - -/* -** Destroy a tokenizer allocated by unicodeCreate(). -*/ -static int unicodeDestroy(sqlite3_tokenizer *pTokenizer){ - if( pTokenizer ){ - unicode_tokenizer *p = (unicode_tokenizer *)pTokenizer; - sqlite3_free(p->aiException); - sqlite3_free(p); - } - return SQLITE_OK; -} - -/* -** As part of a tokenchars= or separators= option, the CREATE VIRTUAL TABLE -** statement has specified that the tokenizer for this table shall consider -** all characters in string zIn/nIn to be separators (if bAlnum==0) or -** token characters (if bAlnum==1). -** -** For each codepoint in the zIn/nIn string, this function checks if the -** sqlite3FtsUnicodeIsalnum() function already returns the desired result. -** If so, no action is taken. Otherwise, the codepoint is added to the -** unicode_tokenizer.aiException[] array. For the purposes of tokenization, -** the return value of sqlite3FtsUnicodeIsalnum() is inverted for all -** codepoints in the aiException[] array. -** -** If a standalone diacritic mark (one that sqlite3FtsUnicodeIsdiacritic() -** identifies as a diacritic) occurs in the zIn/nIn string it is ignored. -** It is not possible to change the behavior of the tokenizer with respect -** to these codepoints. -*/ -static int unicodeAddExceptions( - unicode_tokenizer *p, /* Tokenizer to add exceptions to */ - int bAlnum, /* Replace Isalnum() return value with this */ - const char *zIn, /* Array of characters to make exceptions */ - int nIn /* Length of z in bytes */ -){ - const unsigned char *z = (const unsigned char *)zIn; - const unsigned char *zTerm = &z[nIn]; - int iCode; - int nEntry = 0; - - assert( bAlnum==0 || bAlnum==1 ); - - while( zaiException, (p->nException+nEntry)*sizeof(int)); - if( aNew==0 ) return SQLITE_NOMEM; - nNew = p->nException; - - z = (const unsigned char *)zIn; - while( zi; j--) aNew[j] = aNew[j-1]; - aNew[i] = iCode; - nNew++; - } - } - p->aiException = aNew; - p->nException = nNew; - } - - return SQLITE_OK; -} - -/* -** Return true if the p->aiException[] array contains the value iCode. -*/ -static int unicodeIsException(unicode_tokenizer *p, int iCode){ - if( p->nException>0 ){ - int *a = p->aiException; - int iLo = 0; - int iHi = p->nException-1; - - while( iHi>=iLo ){ - int iTest = (iHi + iLo) / 2; - if( iCode==a[iTest] ){ - return 1; - }else if( iCode>a[iTest] ){ - iLo = iTest+1; - }else{ - iHi = iTest-1; - } - } - } - - return 0; -} - -/* -** Return true if, for the purposes of tokenization, codepoint iCode is -** considered a token character (not a separator). -*/ -static int unicodeIsAlnum(unicode_tokenizer *p, int iCode){ - assert( (sqlite3FtsUnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 ); - return sqlite3FtsUnicodeIsalnum(iCode) ^ unicodeIsException(p, iCode); -} - -/* -** Create a new tokenizer instance. -*/ -static int unicodeCreate( - int nArg, /* Size of array argv[] */ - const char * const *azArg, /* Tokenizer creation arguments */ - sqlite3_tokenizer **pp /* OUT: New tokenizer handle */ -){ - unicode_tokenizer *pNew; /* New tokenizer object */ - int i; - int rc = SQLITE_OK; - - pNew = (unicode_tokenizer *) sqlite3_malloc(sizeof(unicode_tokenizer)); - if( pNew==NULL ) return SQLITE_NOMEM; - memset(pNew, 0, sizeof(unicode_tokenizer)); - pNew->bRemoveDiacritic = 1; - - for(i=0; rc==SQLITE_OK && ibRemoveDiacritic = 1; - } - else if( n==19 && memcmp("remove_diacritics=0", z, 19)==0 ){ - pNew->bRemoveDiacritic = 0; - } - else if( n>=11 && memcmp("tokenchars=", z, 11)==0 ){ - rc = unicodeAddExceptions(pNew, 1, &z[11], n-11); - } - else if( n>=11 && memcmp("separators=", z, 11)==0 ){ - rc = unicodeAddExceptions(pNew, 0, &z[11], n-11); - } - else{ - /* Unrecognized argument */ - rc = SQLITE_ERROR; - } - } - - if( rc!=SQLITE_OK ){ - unicodeDestroy((sqlite3_tokenizer *)pNew); - pNew = 0; - } - *pp = (sqlite3_tokenizer *)pNew; - return rc; -} - -/* -** Prepare to begin tokenizing a particular string. The input -** string to be tokenized is pInput[0..nBytes-1]. A cursor -** used to incrementally tokenize this string is returned in -** *ppCursor. -*/ -static int unicodeOpen( - sqlite3_tokenizer *p, /* The tokenizer */ - const char *aInput, /* Input string */ - int nInput, /* Size of string aInput in bytes */ - sqlite3_tokenizer_cursor **pp /* OUT: New cursor object */ -){ - unicode_cursor *pCsr; - - pCsr = (unicode_cursor *)sqlite3_malloc(sizeof(unicode_cursor)); - if( pCsr==0 ){ - return SQLITE_NOMEM; - } - memset(pCsr, 0, sizeof(unicode_cursor)); - - pCsr->aInput = (const unsigned char *)aInput; - if( aInput==0 ){ - pCsr->nInput = 0; - }else if( nInput<0 ){ - pCsr->nInput = (int)strlen(aInput); - }else{ - pCsr->nInput = nInput; - } - - *pp = &pCsr->base; - UNUSED_PARAMETER(p); - return SQLITE_OK; -} - -/* -** Close a tokenization cursor previously opened by a call to -** simpleOpen() above. -*/ -static int unicodeClose(sqlite3_tokenizer_cursor *pCursor){ - unicode_cursor *pCsr = (unicode_cursor *) pCursor; - sqlite3_free(pCsr->zToken); - sqlite3_free(pCsr); - return SQLITE_OK; -} - -/* -** Extract the next token from a tokenization cursor. The cursor must -** have been opened by a prior call to simpleOpen(). -*/ -static int unicodeNext( - sqlite3_tokenizer_cursor *pC, /* Cursor returned by simpleOpen */ - const char **paToken, /* OUT: Token text */ - int *pnToken, /* OUT: Number of bytes at *paToken */ - int *piStart, /* OUT: Starting offset of token */ - int *piEnd, /* OUT: Ending offset of token */ - int *piPos /* OUT: Position integer of token */ -){ - unicode_cursor *pCsr = (unicode_cursor *)pC; - unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer); - int iCode; - char *zOut; - const unsigned char *z = &pCsr->aInput[pCsr->iOff]; - const unsigned char *zStart = z; - const unsigned char *zEnd; - const unsigned char *zTerm = &pCsr->aInput[pCsr->nInput]; - - /* Scan past any delimiter characters before the start of the next token. - ** Return SQLITE_DONE early if this takes us all the way to the end of - ** the input. */ - while( z=zTerm ) return SQLITE_DONE; - - zOut = pCsr->zToken; - do { - int iOut; - - /* Grow the output buffer if required. */ - if( (zOut-pCsr->zToken)>=(pCsr->nAlloc-4) ){ - char *zNew = sqlite3_realloc(pCsr->zToken, pCsr->nAlloc+64); - if( !zNew ) return SQLITE_NOMEM; - zOut = &zNew[zOut - pCsr->zToken]; - pCsr->zToken = zNew; - pCsr->nAlloc += 64; - } - - /* Write the folded case of the last character read to the output */ - zEnd = z; - iOut = sqlite3FtsUnicodeFold(iCode, p->bRemoveDiacritic); - if( iOut ){ - WRITE_UTF8(zOut, iOut); - } - - /* If the cursor is not at EOF, read the next character */ - if( z>=zTerm ) break; - READ_UTF8(z, zTerm, iCode); - }while( unicodeIsAlnum(p, iCode) - || sqlite3FtsUnicodeIsdiacritic(iCode) - ); - - /* Set the output variables and return. */ - pCsr->iOff = (z - pCsr->aInput); - *paToken = pCsr->zToken; - *pnToken = zOut - pCsr->zToken; - *piStart = (zStart - pCsr->aInput); - *piEnd = (zEnd - pCsr->aInput); - *piPos = pCsr->iToken++; - return SQLITE_OK; -} - -/* -** Set *ppModule to a pointer to the sqlite3_tokenizer_module -** structure for the unicode tokenizer. -*/ -SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const **ppModule){ - static const sqlite3_tokenizer_module module = { - 0, - unicodeCreate, - unicodeDestroy, - unicodeOpen, - unicodeClose, - unicodeNext, - 0, - }; - *ppModule = &module; -} - -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ -#endif /* ifndef SQLITE_ENABLE_FTS4_UNICODE61 */ - -/************** End of fts3_unicode.c ****************************************/ -/************** Begin file fts3_unicode2.c ***********************************/ -/* -** 2012 May 25 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -*/ - -/* -** DO NOT EDIT THIS MACHINE GENERATED FILE. -*/ - -#if defined(SQLITE_ENABLE_FTS4_UNICODE61) -#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) - -/* #include */ - -/* -** Return true if the argument corresponds to a unicode codepoint -** classified as either a letter or a number. Otherwise false. -** -** The results are undefined if the value passed to this function -** is less than zero. -*/ -SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int c){ - /* Each unsigned integer in the following array corresponds to a contiguous - ** range of unicode codepoints that are not either letters or numbers (i.e. - ** codepoints for which this function should return 0). - ** - ** The most significant 22 bits in each 32-bit value contain the first - ** codepoint in the range. The least significant 10 bits are used to store - ** the size of the range (always at least 1). In other words, the value - ** ((C<<22) + N) represents a range of N codepoints starting with codepoint - ** C. It is not possible to represent a range larger than 1023 codepoints - ** using this format. - */ - const static unsigned int aEntry[] = { - 0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07, - 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01, - 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401, - 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01, - 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01, - 0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802, - 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F, - 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401, - 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804, - 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403, - 0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812, - 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001, - 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802, - 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805, - 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401, - 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03, - 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807, - 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001, - 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01, - 0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804, - 0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001, - 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802, - 0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01, - 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06, - 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007, - 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006, - 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417, - 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14, - 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07, - 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01, - 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001, - 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802, - 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F, - 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002, - 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802, - 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006, - 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D, - 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802, - 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027, - 0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403, - 0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805, - 0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04, - 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401, - 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005, - 0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B, - 0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A, - 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001, - 0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59, - 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807, - 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01, - 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E, - 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100, - 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10, - 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402, - 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804, - 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012, - 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004, - 0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002, - 0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803, - 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07, - 0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02, - 0x037FFC02, 0x03E3FC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, - 0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, - 0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, - 0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, - 0x04040003, 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, - 0x040E7C01, 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, - 0x04280403, 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, - 0x04294009, 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, - 0x04420003, 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, - 0x04460003, 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, - 0x05BD442E, 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, - 0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, - 0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, - 0x075EA401, 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, - 0x07C2800F, 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, - 0x07C4C03C, 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, - 0x07C94002, 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, - 0x07CE8025, 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, - 0x07D108B6, 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, - 0x07D7EC46, 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, - 0x38008060, 0x380400F0, 0x3C000001, 0x3FFFF401, 0x40000001, - 0x43FFF401, - }; - static const unsigned int aAscii[4] = { - 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001, - }; - - if( c<128 ){ - return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 ); - }else if( c<(1<<22) ){ - unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; - int iRes; - int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; - int iLo = 0; - while( iHi>=iLo ){ - int iTest = (iHi + iLo) / 2; - if( key >= aEntry[iTest] ){ - iRes = iTest; - iLo = iTest+1; - }else{ - iHi = iTest-1; - } - } - assert( aEntry[0]=aEntry[iRes] ); - return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF))); - } - return 1; -} - - -/* -** If the argument is a codepoint corresponding to a lowercase letter -** in the ASCII range with a diacritic added, return the codepoint -** of the ASCII letter only. For example, if passed 235 - "LATIN -** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER -** E"). The resuls of passing a codepoint that corresponds to an -** uppercase letter are undefined. -*/ -static int remove_diacritic(int c){ - unsigned short aDia[] = { - 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, - 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286, - 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732, - 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336, - 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928, - 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234, - 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504, - 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529, - 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726, - 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122, - 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536, - 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730, - 62924, 63050, 63082, 63274, 63390, - }; - char aChar[] = { - '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c', - 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r', - 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o', - 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r', - 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h', - 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't', - 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a', - 'e', 'i', 'o', 'u', 'y', - }; - - unsigned int key = (((unsigned int)c)<<3) | 0x00000007; - int iRes = 0; - int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1; - int iLo = 0; - while( iHi>=iLo ){ - int iTest = (iHi + iLo) / 2; - if( key >= aDia[iTest] ){ - iRes = iTest; - iLo = iTest+1; - }else{ - iHi = iTest-1; - } - } - assert( key>=aDia[iRes] ); - return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]); -}; - - -/* -** Return true if the argument interpreted as a unicode codepoint -** is a diacritical modifier character. -*/ -SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int c){ - unsigned int mask0 = 0x08029FDF; - unsigned int mask1 = 0x000361F8; - if( c<768 || c>817 ) return 0; - return (c < 768+32) ? - (mask0 & (1 << (c-768))) : - (mask1 & (1 << (c-768-32))); -} - - -/* -** Interpret the argument as a unicode codepoint. If the codepoint -** is an upper case character that has a lower case equivalent, -** return the codepoint corresponding to the lower case version. -** Otherwise, return a copy of the argument. -** -** The results are undefined if the value passed to this function -** is less than zero. -*/ -SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){ - /* Each entry in the following array defines a rule for folding a range - ** of codepoints to lower case. The rule applies to a range of nRange - ** codepoints starting at codepoint iCode. - ** - ** If the least significant bit in flags is clear, then the rule applies - ** to all nRange codepoints (i.e. all nRange codepoints are upper case and - ** need to be folded). Or, if it is set, then the rule only applies to - ** every second codepoint in the range, starting with codepoint C. - ** - ** The 7 most significant bits in flags are an index into the aiOff[] - ** array. If a specific codepoint C does require folding, then its lower - ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF). - ** - ** The contents of this array are generated by parsing the CaseFolding.txt - ** file distributed as part of the "Unicode Character Database". See - ** http://www.unicode.org for details. - */ - static const struct TableEntry { - unsigned short iCode; - unsigned char flags; - unsigned char nRange; - } aEntry[] = { - {65, 14, 26}, {181, 64, 1}, {192, 14, 23}, - {216, 14, 7}, {256, 1, 48}, {306, 1, 6}, - {313, 1, 16}, {330, 1, 46}, {376, 116, 1}, - {377, 1, 6}, {383, 104, 1}, {385, 50, 1}, - {386, 1, 4}, {390, 44, 1}, {391, 0, 1}, - {393, 42, 2}, {395, 0, 1}, {398, 32, 1}, - {399, 38, 1}, {400, 40, 1}, {401, 0, 1}, - {403, 42, 1}, {404, 46, 1}, {406, 52, 1}, - {407, 48, 1}, {408, 0, 1}, {412, 52, 1}, - {413, 54, 1}, {415, 56, 1}, {416, 1, 6}, - {422, 60, 1}, {423, 0, 1}, {425, 60, 1}, - {428, 0, 1}, {430, 60, 1}, {431, 0, 1}, - {433, 58, 2}, {435, 1, 4}, {439, 62, 1}, - {440, 0, 1}, {444, 0, 1}, {452, 2, 1}, - {453, 0, 1}, {455, 2, 1}, {456, 0, 1}, - {458, 2, 1}, {459, 1, 18}, {478, 1, 18}, - {497, 2, 1}, {498, 1, 4}, {502, 122, 1}, - {503, 134, 1}, {504, 1, 40}, {544, 110, 1}, - {546, 1, 18}, {570, 70, 1}, {571, 0, 1}, - {573, 108, 1}, {574, 68, 1}, {577, 0, 1}, - {579, 106, 1}, {580, 28, 1}, {581, 30, 1}, - {582, 1, 10}, {837, 36, 1}, {880, 1, 4}, - {886, 0, 1}, {902, 18, 1}, {904, 16, 3}, - {908, 26, 1}, {910, 24, 2}, {913, 14, 17}, - {931, 14, 9}, {962, 0, 1}, {975, 4, 1}, - {976, 140, 1}, {977, 142, 1}, {981, 146, 1}, - {982, 144, 1}, {984, 1, 24}, {1008, 136, 1}, - {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1}, - {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1}, - {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32}, - {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1}, - {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38}, - {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1}, - {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1}, - {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6}, - {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6}, - {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8}, - {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2}, - {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1}, - {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2}, - {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2}, - {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2}, - {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1}, - {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16}, - {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47}, - {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1}, - {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1}, - {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1}, - {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2}, - {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1}, - {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14}, - {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1}, - {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1}, - {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1}, - {65313, 14, 26}, - }; - static const unsigned short aiOff[] = { - 1, 2, 8, 15, 16, 26, 28, 32, - 37, 38, 40, 48, 63, 64, 69, 71, - 79, 80, 116, 202, 203, 205, 206, 207, - 209, 210, 211, 213, 214, 217, 218, 219, - 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721, - 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274, - 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406, - 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462, - 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511, - 65514, 65521, 65527, 65528, 65529, - }; - - int ret = c; - - assert( c>=0 ); - assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 ); - - if( c<128 ){ - if( c>='A' && c<='Z' ) ret = c + ('a' - 'A'); - }else if( c<65536 ){ - int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; - int iLo = 0; - int iRes = -1; - - while( iHi>=iLo ){ - int iTest = (iHi + iLo) / 2; - int cmp = (c - aEntry[iTest].iCode); - if( cmp>=0 ){ - iRes = iTest; - iLo = iTest+1; - }else{ - iHi = iTest-1; - } - } - assert( iRes<0 || c>=aEntry[iRes].iCode ); - - if( iRes>=0 ){ - const struct TableEntry *p = &aEntry[iRes]; - if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){ - ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF; - assert( ret>0 ); - } - } - - if( bRemoveDiacritic ) ret = remove_diacritic(ret); - } - - else if( c>=66560 && c<66600 ){ - ret = c + 40; - } - - return ret; -} -#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */ -#endif /* !defined(SQLITE_ENABLE_FTS4_UNICODE61) */ - -/************** End of fts3_unicode2.c ***************************************/ -/************** Begin file rtree.c *******************************************/ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code for implementations of the r-tree and r*-tree -** algorithms packaged as an SQLite virtual table module. -*/ - -/* -** Database Format of R-Tree Tables -** -------------------------------- -** -** The data structure for a single virtual r-tree table is stored in three -** native SQLite tables declared as follows. In each case, the '%' character -** in the table name is replaced with the user-supplied name of the r-tree -** table. -** -** CREATE TABLE %_node(nodeno INTEGER PRIMARY KEY, data BLOB) -** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER) -** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER) -** -** The data for each node of the r-tree structure is stored in the %_node -** table. For each node that is not the root node of the r-tree, there is -** an entry in the %_parent table associating the node with its parent. -** And for each row of data in the table, there is an entry in the %_rowid -** table that maps from the entries rowid to the id of the node that it -** is stored on. -** -** The root node of an r-tree always exists, even if the r-tree table is -** empty. The nodeno of the root node is always 1. All other nodes in the -** table must be the same size as the root node. The content of each node -** is formatted as follows: -** -** 1. If the node is the root node (node 1), then the first 2 bytes -** of the node contain the tree depth as a big-endian integer. -** For non-root nodes, the first 2 bytes are left unused. -** -** 2. The next 2 bytes contain the number of entries currently -** stored in the node. -** -** 3. The remainder of the node contains the node entries. Each entry -** consists of a single 8-byte integer followed by an even number -** of 4-byte coordinates. For leaf nodes the integer is the rowid -** of a record. For internal nodes it is the node number of a -** child page. -*/ - -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE) - -/* -** This file contains an implementation of a couple of different variants -** of the r-tree algorithm. See the README file for further details. The -** same data-structure is used for all, but the algorithms for insert and -** delete operations vary. The variants used are selected at compile time -** by defining the following symbols: -*/ - -/* Either, both or none of the following may be set to activate -** r*tree variant algorithms. -*/ -#define VARIANT_RSTARTREE_CHOOSESUBTREE 0 -#define VARIANT_RSTARTREE_REINSERT 1 - -/* -** Exactly one of the following must be set to 1. -*/ -#define VARIANT_GUTTMAN_QUADRATIC_SPLIT 0 -#define VARIANT_GUTTMAN_LINEAR_SPLIT 0 -#define VARIANT_RSTARTREE_SPLIT 1 - -#define VARIANT_GUTTMAN_SPLIT \ - (VARIANT_GUTTMAN_LINEAR_SPLIT||VARIANT_GUTTMAN_QUADRATIC_SPLIT) - -#if VARIANT_GUTTMAN_QUADRATIC_SPLIT - #define PickNext QuadraticPickNext - #define PickSeeds QuadraticPickSeeds - #define AssignCells splitNodeGuttman -#endif -#if VARIANT_GUTTMAN_LINEAR_SPLIT - #define PickNext LinearPickNext - #define PickSeeds LinearPickSeeds - #define AssignCells splitNodeGuttman -#endif -#if VARIANT_RSTARTREE_SPLIT - #define AssignCells splitNodeStartree -#endif - -#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) -# define NDEBUG 1 -#endif - -#ifndef SQLITE_CORE - SQLITE_EXTENSION_INIT1 -#else -#endif - -/* #include */ -/* #include */ - -#ifndef SQLITE_AMALGAMATION -#include "sqlite3rtree.h" -typedef sqlite3_int64 i64; -typedef unsigned char u8; -typedef unsigned int u32; -#endif - -/* The following macro is used to suppress compiler warnings. -*/ -#ifndef UNUSED_PARAMETER -# define UNUSED_PARAMETER(x) (void)(x) -#endif - -typedef struct Rtree Rtree; -typedef struct RtreeCursor RtreeCursor; -typedef struct RtreeNode RtreeNode; -typedef struct RtreeCell RtreeCell; -typedef struct RtreeConstraint RtreeConstraint; -typedef struct RtreeMatchArg RtreeMatchArg; -typedef struct RtreeGeomCallback RtreeGeomCallback; -typedef union RtreeCoord RtreeCoord; - -/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */ -#define RTREE_MAX_DIMENSIONS 5 - -/* Size of hash table Rtree.aHash. This hash table is not expected to -** ever contain very many entries, so a fixed number of buckets is -** used. -*/ -#define HASHSIZE 128 - -/* -** An rtree virtual-table object. -*/ -struct Rtree { - sqlite3_vtab base; - sqlite3 *db; /* Host database connection */ - int iNodeSize; /* Size in bytes of each node in the node table */ - int nDim; /* Number of dimensions */ - int nBytesPerCell; /* Bytes consumed per cell */ - int iDepth; /* Current depth of the r-tree structure */ - char *zDb; /* Name of database containing r-tree table */ - char *zName; /* Name of r-tree table */ - RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */ - int nBusy; /* Current number of users of this structure */ - - /* List of nodes removed during a CondenseTree operation. List is - ** linked together via the pointer normally used for hash chains - - ** RtreeNode.pNext. RtreeNode.iNode stores the depth of the sub-tree - ** headed by the node (leaf nodes have RtreeNode.iNode==0). - */ - RtreeNode *pDeleted; - int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */ - - /* Statements to read/write/delete a record from xxx_node */ - sqlite3_stmt *pReadNode; - sqlite3_stmt *pWriteNode; - sqlite3_stmt *pDeleteNode; - - /* Statements to read/write/delete a record from xxx_rowid */ - sqlite3_stmt *pReadRowid; - sqlite3_stmt *pWriteRowid; - sqlite3_stmt *pDeleteRowid; - - /* Statements to read/write/delete a record from xxx_parent */ - sqlite3_stmt *pReadParent; - sqlite3_stmt *pWriteParent; - sqlite3_stmt *pDeleteParent; - - int eCoordType; -}; - -/* Possible values for eCoordType: */ -#define RTREE_COORD_REAL32 0 -#define RTREE_COORD_INT32 1 - -/* -** If SQLITE_RTREE_INT_ONLY is defined, then this virtual table will -** only deal with integer coordinates. No floating point operations -** will be done. -*/ -#ifdef SQLITE_RTREE_INT_ONLY - typedef sqlite3_int64 RtreeDValue; /* High accuracy coordinate */ - typedef int RtreeValue; /* Low accuracy coordinate */ -#else - typedef double RtreeDValue; /* High accuracy coordinate */ - typedef float RtreeValue; /* Low accuracy coordinate */ -#endif - -/* -** The minimum number of cells allowed for a node is a third of the -** maximum. In Gutman's notation: -** -** m = M/3 -** -** If an R*-tree "Reinsert" operation is required, the same number of -** cells are removed from the overfull node and reinserted into the tree. -*/ -#define RTREE_MINCELLS(p) ((((p)->iNodeSize-4)/(p)->nBytesPerCell)/3) -#define RTREE_REINSERT(p) RTREE_MINCELLS(p) -#define RTREE_MAXCELLS 51 - -/* -** The smallest possible node-size is (512-64)==448 bytes. And the largest -** supported cell size is 48 bytes (8 byte rowid + ten 4 byte coordinates). -** Therefore all non-root nodes must contain at least 3 entries. Since -** 2^40 is greater than 2^64, an r-tree structure always has a depth of -** 40 or less. -*/ -#define RTREE_MAX_DEPTH 40 - -/* -** An rtree cursor object. -*/ -struct RtreeCursor { - sqlite3_vtab_cursor base; - RtreeNode *pNode; /* Node cursor is currently pointing at */ - int iCell; /* Index of current cell in pNode */ - int iStrategy; /* Copy of idxNum search parameter */ - int nConstraint; /* Number of entries in aConstraint */ - RtreeConstraint *aConstraint; /* Search constraints. */ -}; - -union RtreeCoord { - RtreeValue f; - int i; -}; - -/* -** The argument is an RtreeCoord. Return the value stored within the RtreeCoord -** formatted as a RtreeDValue (double or int64). This macro assumes that local -** variable pRtree points to the Rtree structure associated with the -** RtreeCoord. -*/ -#ifdef SQLITE_RTREE_INT_ONLY -# define DCOORD(coord) ((RtreeDValue)coord.i) -#else -# define DCOORD(coord) ( \ - (pRtree->eCoordType==RTREE_COORD_REAL32) ? \ - ((double)coord.f) : \ - ((double)coord.i) \ - ) -#endif - -/* -** A search constraint. -*/ -struct RtreeConstraint { - int iCoord; /* Index of constrained coordinate */ - int op; /* Constraining operation */ - RtreeDValue rValue; /* Constraint value. */ - int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*); - sqlite3_rtree_geometry *pGeom; /* Constraint callback argument for a MATCH */ -}; - -/* Possible values for RtreeConstraint.op */ -#define RTREE_EQ 0x41 -#define RTREE_LE 0x42 -#define RTREE_LT 0x43 -#define RTREE_GE 0x44 -#define RTREE_GT 0x45 -#define RTREE_MATCH 0x46 - -/* -** An rtree structure node. -*/ -struct RtreeNode { - RtreeNode *pParent; /* Parent node */ - i64 iNode; - int nRef; - int isDirty; - u8 *zData; - RtreeNode *pNext; /* Next node in this hash chain */ -}; -#define NCELL(pNode) readInt16(&(pNode)->zData[2]) - -/* -** Structure to store a deserialized rtree record. -*/ -struct RtreeCell { - i64 iRowid; - RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2]; -}; - - -/* -** Value for the first field of every RtreeMatchArg object. The MATCH -** operator tests that the first field of a blob operand matches this -** value to avoid operating on invalid blobs (which could cause a segfault). -*/ -#define RTREE_GEOMETRY_MAGIC 0x891245AB - -/* -** An instance of this structure must be supplied as a blob argument to -** the right-hand-side of an SQL MATCH operator used to constrain an -** r-tree query. -*/ -struct RtreeMatchArg { - u32 magic; /* Always RTREE_GEOMETRY_MAGIC */ - int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue*, int *); - void *pContext; - int nParam; - RtreeDValue aParam[1]; -}; - -/* -** When a geometry callback is created (see sqlite3_rtree_geometry_callback), -** a single instance of the following structure is allocated. It is used -** as the context for the user-function created by by s_r_g_c(). The object -** is eventually deleted by the destructor mechanism provided by -** sqlite3_create_function_v2() (which is called by s_r_g_c() to create -** the geometry callback function). -*/ -struct RtreeGeomCallback { - int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*); - void *pContext; -}; - -#ifndef MAX -# define MAX(x,y) ((x) < (y) ? (y) : (x)) -#endif -#ifndef MIN -# define MIN(x,y) ((x) > (y) ? (y) : (x)) -#endif - -/* -** Functions to deserialize a 16 bit integer, 32 bit real number and -** 64 bit integer. The deserialized value is returned. -*/ -static int readInt16(u8 *p){ - return (p[0]<<8) + p[1]; -} -static void readCoord(u8 *p, RtreeCoord *pCoord){ - u32 i = ( - (((u32)p[0]) << 24) + - (((u32)p[1]) << 16) + - (((u32)p[2]) << 8) + - (((u32)p[3]) << 0) - ); - *(u32 *)pCoord = i; -} -static i64 readInt64(u8 *p){ - return ( - (((i64)p[0]) << 56) + - (((i64)p[1]) << 48) + - (((i64)p[2]) << 40) + - (((i64)p[3]) << 32) + - (((i64)p[4]) << 24) + - (((i64)p[5]) << 16) + - (((i64)p[6]) << 8) + - (((i64)p[7]) << 0) - ); -} - -/* -** Functions to serialize a 16 bit integer, 32 bit real number and -** 64 bit integer. The value returned is the number of bytes written -** to the argument buffer (always 2, 4 and 8 respectively). -*/ -static int writeInt16(u8 *p, int i){ - p[0] = (i>> 8)&0xFF; - p[1] = (i>> 0)&0xFF; - return 2; -} -static int writeCoord(u8 *p, RtreeCoord *pCoord){ - u32 i; - assert( sizeof(RtreeCoord)==4 ); - assert( sizeof(u32)==4 ); - i = *(u32 *)pCoord; - p[0] = (i>>24)&0xFF; - p[1] = (i>>16)&0xFF; - p[2] = (i>> 8)&0xFF; - p[3] = (i>> 0)&0xFF; - return 4; -} -static int writeInt64(u8 *p, i64 i){ - p[0] = (i>>56)&0xFF; - p[1] = (i>>48)&0xFF; - p[2] = (i>>40)&0xFF; - p[3] = (i>>32)&0xFF; - p[4] = (i>>24)&0xFF; - p[5] = (i>>16)&0xFF; - p[6] = (i>> 8)&0xFF; - p[7] = (i>> 0)&0xFF; - return 8; -} - -/* -** Increment the reference count of node p. -*/ -static void nodeReference(RtreeNode *p){ - if( p ){ - p->nRef++; - } -} - -/* -** Clear the content of node p (set all bytes to 0x00). -*/ -static void nodeZero(Rtree *pRtree, RtreeNode *p){ - memset(&p->zData[2], 0, pRtree->iNodeSize-2); - p->isDirty = 1; -} - -/* -** Given a node number iNode, return the corresponding key to use -** in the Rtree.aHash table. -*/ -static int nodeHash(i64 iNode){ - return ( - (iNode>>56) ^ (iNode>>48) ^ (iNode>>40) ^ (iNode>>32) ^ - (iNode>>24) ^ (iNode>>16) ^ (iNode>> 8) ^ (iNode>> 0) - ) % HASHSIZE; -} - -/* -** Search the node hash table for node iNode. If found, return a pointer -** to it. Otherwise, return 0. -*/ -static RtreeNode *nodeHashLookup(Rtree *pRtree, i64 iNode){ - RtreeNode *p; - for(p=pRtree->aHash[nodeHash(iNode)]; p && p->iNode!=iNode; p=p->pNext); - return p; -} - -/* -** Add node pNode to the node hash table. -*/ -static void nodeHashInsert(Rtree *pRtree, RtreeNode *pNode){ - int iHash; - assert( pNode->pNext==0 ); - iHash = nodeHash(pNode->iNode); - pNode->pNext = pRtree->aHash[iHash]; - pRtree->aHash[iHash] = pNode; -} - -/* -** Remove node pNode from the node hash table. -*/ -static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){ - RtreeNode **pp; - if( pNode->iNode!=0 ){ - pp = &pRtree->aHash[nodeHash(pNode->iNode)]; - for( ; (*pp)!=pNode; pp = &(*pp)->pNext){ assert(*pp); } - *pp = pNode->pNext; - pNode->pNext = 0; - } -} - -/* -** Allocate and return new r-tree node. Initially, (RtreeNode.iNode==0), -** indicating that node has not yet been assigned a node number. It is -** assigned a node number when nodeWrite() is called to write the -** node contents out to the database. -*/ -static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ - RtreeNode *pNode; - pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode) + pRtree->iNodeSize); - if( pNode ){ - memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize); - pNode->zData = (u8 *)&pNode[1]; - pNode->nRef = 1; - pNode->pParent = pParent; - pNode->isDirty = 1; - nodeReference(pParent); - } - return pNode; -} - -/* -** Obtain a reference to an r-tree node. -*/ -static int -nodeAcquire( - Rtree *pRtree, /* R-tree structure */ - i64 iNode, /* Node number to load */ - RtreeNode *pParent, /* Either the parent node or NULL */ - RtreeNode **ppNode /* OUT: Acquired node */ -){ - int rc; - int rc2 = SQLITE_OK; - RtreeNode *pNode; - - /* Check if the requested node is already in the hash table. If so, - ** increase its reference count and return it. - */ - if( (pNode = nodeHashLookup(pRtree, iNode)) ){ - assert( !pParent || !pNode->pParent || pNode->pParent==pParent ); - if( pParent && !pNode->pParent ){ - nodeReference(pParent); - pNode->pParent = pParent; - } - pNode->nRef++; - *ppNode = pNode; - return SQLITE_OK; - } - - sqlite3_bind_int64(pRtree->pReadNode, 1, iNode); - rc = sqlite3_step(pRtree->pReadNode); - if( rc==SQLITE_ROW ){ - const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0); - if( pRtree->iNodeSize==sqlite3_column_bytes(pRtree->pReadNode, 0) ){ - pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize); - if( !pNode ){ - rc2 = SQLITE_NOMEM; - }else{ - pNode->pParent = pParent; - pNode->zData = (u8 *)&pNode[1]; - pNode->nRef = 1; - pNode->iNode = iNode; - pNode->isDirty = 0; - pNode->pNext = 0; - memcpy(pNode->zData, zBlob, pRtree->iNodeSize); - nodeReference(pParent); - } - } - } - rc = sqlite3_reset(pRtree->pReadNode); - if( rc==SQLITE_OK ) rc = rc2; - - /* If the root node was just loaded, set pRtree->iDepth to the height - ** of the r-tree structure. A height of zero means all data is stored on - ** the root node. A height of one means the children of the root node - ** are the leaves, and so on. If the depth as specified on the root node - ** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt. - */ - if( pNode && iNode==1 ){ - pRtree->iDepth = readInt16(pNode->zData); - if( pRtree->iDepth>RTREE_MAX_DEPTH ){ - rc = SQLITE_CORRUPT_VTAB; - } - } - - /* If no error has occurred so far, check if the "number of entries" - ** field on the node is too large. If so, set the return code to - ** SQLITE_CORRUPT_VTAB. - */ - if( pNode && rc==SQLITE_OK ){ - if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){ - rc = SQLITE_CORRUPT_VTAB; - } - } - - if( rc==SQLITE_OK ){ - if( pNode!=0 ){ - nodeHashInsert(pRtree, pNode); - }else{ - rc = SQLITE_CORRUPT_VTAB; - } - *ppNode = pNode; - }else{ - sqlite3_free(pNode); - *ppNode = 0; - } - - return rc; -} - -/* -** Overwrite cell iCell of node pNode with the contents of pCell. -*/ -static void nodeOverwriteCell( - Rtree *pRtree, - RtreeNode *pNode, - RtreeCell *pCell, - int iCell -){ - int ii; - u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; - p += writeInt64(p, pCell->iRowid); - for(ii=0; ii<(pRtree->nDim*2); ii++){ - p += writeCoord(p, &pCell->aCoord[ii]); - } - pNode->isDirty = 1; -} - -/* -** Remove cell the cell with index iCell from node pNode. -*/ -static void nodeDeleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell){ - u8 *pDst = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; - u8 *pSrc = &pDst[pRtree->nBytesPerCell]; - int nByte = (NCELL(pNode) - iCell - 1) * pRtree->nBytesPerCell; - memmove(pDst, pSrc, nByte); - writeInt16(&pNode->zData[2], NCELL(pNode)-1); - pNode->isDirty = 1; -} - -/* -** Insert the contents of cell pCell into node pNode. If the insert -** is successful, return SQLITE_OK. -** -** If there is not enough free space in pNode, return SQLITE_FULL. -*/ -static int -nodeInsertCell( - Rtree *pRtree, - RtreeNode *pNode, - RtreeCell *pCell -){ - int nCell; /* Current number of cells in pNode */ - int nMaxCell; /* Maximum number of cells for pNode */ - - nMaxCell = (pRtree->iNodeSize-4)/pRtree->nBytesPerCell; - nCell = NCELL(pNode); - - assert( nCell<=nMaxCell ); - if( nCellzData[2], nCell+1); - pNode->isDirty = 1; - } - - return (nCell==nMaxCell); -} - -/* -** If the node is dirty, write it out to the database. -*/ -static int -nodeWrite(Rtree *pRtree, RtreeNode *pNode){ - int rc = SQLITE_OK; - if( pNode->isDirty ){ - sqlite3_stmt *p = pRtree->pWriteNode; - if( pNode->iNode ){ - sqlite3_bind_int64(p, 1, pNode->iNode); - }else{ - sqlite3_bind_null(p, 1); - } - sqlite3_bind_blob(p, 2, pNode->zData, pRtree->iNodeSize, SQLITE_STATIC); - sqlite3_step(p); - pNode->isDirty = 0; - rc = sqlite3_reset(p); - if( pNode->iNode==0 && rc==SQLITE_OK ){ - pNode->iNode = sqlite3_last_insert_rowid(pRtree->db); - nodeHashInsert(pRtree, pNode); - } - } - return rc; -} - -/* -** Release a reference to a node. If the node is dirty and the reference -** count drops to zero, the node data is written to the database. -*/ -static int -nodeRelease(Rtree *pRtree, RtreeNode *pNode){ - int rc = SQLITE_OK; - if( pNode ){ - assert( pNode->nRef>0 ); - pNode->nRef--; - if( pNode->nRef==0 ){ - if( pNode->iNode==1 ){ - pRtree->iDepth = -1; - } - if( pNode->pParent ){ - rc = nodeRelease(pRtree, pNode->pParent); - } - if( rc==SQLITE_OK ){ - rc = nodeWrite(pRtree, pNode); - } - nodeHashDelete(pRtree, pNode); - sqlite3_free(pNode); - } - } - return rc; -} - -/* -** Return the 64-bit integer value associated with cell iCell of -** node pNode. If pNode is a leaf node, this is a rowid. If it is -** an internal node, then the 64-bit integer is a child page number. -*/ -static i64 nodeGetRowid( - Rtree *pRtree, - RtreeNode *pNode, - int iCell -){ - assert( iCellzData[4 + pRtree->nBytesPerCell*iCell]); -} - -/* -** Return coordinate iCoord from cell iCell in node pNode. -*/ -static void nodeGetCoord( - Rtree *pRtree, - RtreeNode *pNode, - int iCell, - int iCoord, - RtreeCoord *pCoord /* Space to write result to */ -){ - readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); -} - -/* -** Deserialize cell iCell of node pNode. Populate the structure pointed -** to by pCell with the results. -*/ -static void nodeGetCell( - Rtree *pRtree, - RtreeNode *pNode, - int iCell, - RtreeCell *pCell -){ - int ii; - pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); - for(ii=0; iinDim*2; ii++){ - nodeGetCoord(pRtree, pNode, iCell, ii, &pCell->aCoord[ii]); - } -} - - -/* Forward declaration for the function that does the work of -** the virtual table module xCreate() and xConnect() methods. -*/ -static int rtreeInit( - sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char **, int -); - -/* -** Rtree virtual table module xCreate method. -*/ -static int rtreeCreate( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 1); -} - -/* -** Rtree virtual table module xConnect method. -*/ -static int rtreeConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 0); -} - -/* -** Increment the r-tree reference count. -*/ -static void rtreeReference(Rtree *pRtree){ - pRtree->nBusy++; -} - -/* -** Decrement the r-tree reference count. When the reference count reaches -** zero the structure is deleted. -*/ -static void rtreeRelease(Rtree *pRtree){ - pRtree->nBusy--; - if( pRtree->nBusy==0 ){ - sqlite3_finalize(pRtree->pReadNode); - sqlite3_finalize(pRtree->pWriteNode); - sqlite3_finalize(pRtree->pDeleteNode); - sqlite3_finalize(pRtree->pReadRowid); - sqlite3_finalize(pRtree->pWriteRowid); - sqlite3_finalize(pRtree->pDeleteRowid); - sqlite3_finalize(pRtree->pReadParent); - sqlite3_finalize(pRtree->pWriteParent); - sqlite3_finalize(pRtree->pDeleteParent); - sqlite3_free(pRtree); - } -} - -/* -** Rtree virtual table module xDisconnect method. -*/ -static int rtreeDisconnect(sqlite3_vtab *pVtab){ - rtreeRelease((Rtree *)pVtab); - return SQLITE_OK; -} - -/* -** Rtree virtual table module xDestroy method. -*/ -static int rtreeDestroy(sqlite3_vtab *pVtab){ - Rtree *pRtree = (Rtree *)pVtab; - int rc; - char *zCreate = sqlite3_mprintf( - "DROP TABLE '%q'.'%q_node';" - "DROP TABLE '%q'.'%q_rowid';" - "DROP TABLE '%q'.'%q_parent';", - pRtree->zDb, pRtree->zName, - pRtree->zDb, pRtree->zName, - pRtree->zDb, pRtree->zName - ); - if( !zCreate ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_exec(pRtree->db, zCreate, 0, 0, 0); - sqlite3_free(zCreate); - } - if( rc==SQLITE_OK ){ - rtreeRelease(pRtree); - } - - return rc; -} - -/* -** Rtree virtual table module xOpen method. -*/ -static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - int rc = SQLITE_NOMEM; - RtreeCursor *pCsr; - - pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor)); - if( pCsr ){ - memset(pCsr, 0, sizeof(RtreeCursor)); - pCsr->base.pVtab = pVTab; - rc = SQLITE_OK; - } - *ppCursor = (sqlite3_vtab_cursor *)pCsr; - - return rc; -} - - -/* -** Free the RtreeCursor.aConstraint[] array and its contents. -*/ -static void freeCursorConstraints(RtreeCursor *pCsr){ - if( pCsr->aConstraint ){ - int i; /* Used to iterate through constraint array */ - for(i=0; inConstraint; i++){ - sqlite3_rtree_geometry *pGeom = pCsr->aConstraint[i].pGeom; - if( pGeom ){ - if( pGeom->xDelUser ) pGeom->xDelUser(pGeom->pUser); - sqlite3_free(pGeom); - } - } - sqlite3_free(pCsr->aConstraint); - pCsr->aConstraint = 0; - } -} - -/* -** Rtree virtual table module xClose method. -*/ -static int rtreeClose(sqlite3_vtab_cursor *cur){ - Rtree *pRtree = (Rtree *)(cur->pVtab); - int rc; - RtreeCursor *pCsr = (RtreeCursor *)cur; - freeCursorConstraints(pCsr); - rc = nodeRelease(pRtree, pCsr->pNode); - sqlite3_free(pCsr); - return rc; -} - -/* -** Rtree virtual table module xEof method. -** -** Return non-zero if the cursor does not currently point to a valid -** record (i.e if the scan has finished), or zero otherwise. -*/ -static int rtreeEof(sqlite3_vtab_cursor *cur){ - RtreeCursor *pCsr = (RtreeCursor *)cur; - return (pCsr->pNode==0); -} - -/* -** The r-tree constraint passed as the second argument to this function is -** guaranteed to be a MATCH constraint. -*/ -static int testRtreeGeom( - Rtree *pRtree, /* R-Tree object */ - RtreeConstraint *pConstraint, /* MATCH constraint to test */ - RtreeCell *pCell, /* Cell to test */ - int *pbRes /* OUT: Test result */ -){ - int i; - RtreeDValue aCoord[RTREE_MAX_DIMENSIONS*2]; - int nCoord = pRtree->nDim*2; - - assert( pConstraint->op==RTREE_MATCH ); - assert( pConstraint->pGeom ); - - for(i=0; iaCoord[i]); - } - return pConstraint->xGeom(pConstraint->pGeom, nCoord, aCoord, pbRes); -} - -/* -** Cursor pCursor currently points to a cell in a non-leaf page. -** Set *pbEof to true if the sub-tree headed by the cell is filtered -** (excluded) by the constraints in the pCursor->aConstraint[] -** array, or false otherwise. -** -** Return SQLITE_OK if successful or an SQLite error code if an error -** occurs within a geometry callback. -*/ -static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){ - RtreeCell cell; - int ii; - int bRes = 0; - int rc = SQLITE_OK; - - nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); - for(ii=0; bRes==0 && iinConstraint; ii++){ - RtreeConstraint *p = &pCursor->aConstraint[ii]; - RtreeDValue cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]); - RtreeDValue cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]); - - assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE - || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH - ); - - switch( p->op ){ - case RTREE_LE: case RTREE_LT: - bRes = p->rValuerValue>cell_max; - break; - - case RTREE_EQ: - bRes = (p->rValue>cell_max || p->rValueop==RTREE_MATCH ); - rc = testRtreeGeom(pRtree, p, &cell, &bRes); - bRes = !bRes; - break; - } - } - } - - *pbEof = bRes; - return rc; -} - -/* -** Test if the cell that cursor pCursor currently points to -** would be filtered (excluded) by the constraints in the -** pCursor->aConstraint[] array. If so, set *pbEof to true before -** returning. If the cell is not filtered (excluded) by the constraints, -** set pbEof to zero. -** -** Return SQLITE_OK if successful or an SQLite error code if an error -** occurs within a geometry callback. -** -** This function assumes that the cell is part of a leaf node. -*/ -static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){ - RtreeCell cell; - int ii; - *pbEof = 0; - - nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); - for(ii=0; iinConstraint; ii++){ - RtreeConstraint *p = &pCursor->aConstraint[ii]; - RtreeDValue coord = DCOORD(cell.aCoord[p->iCoord]); - int res; - assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE - || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH - ); - switch( p->op ){ - case RTREE_LE: res = (coord<=p->rValue); break; - case RTREE_LT: res = (coordrValue); break; - case RTREE_GE: res = (coord>=p->rValue); break; - case RTREE_GT: res = (coord>p->rValue); break; - case RTREE_EQ: res = (coord==p->rValue); break; - default: { - int rc; - assert( p->op==RTREE_MATCH ); - rc = testRtreeGeom(pRtree, p, &cell, &res); - if( rc!=SQLITE_OK ){ - return rc; - } - break; - } - } - - if( !res ){ - *pbEof = 1; - return SQLITE_OK; - } - } - - return SQLITE_OK; -} - -/* -** Cursor pCursor currently points at a node that heads a sub-tree of -** height iHeight (if iHeight==0, then the node is a leaf). Descend -** to point to the left-most cell of the sub-tree that matches the -** configured constraints. -*/ -static int descendToCell( - Rtree *pRtree, - RtreeCursor *pCursor, - int iHeight, - int *pEof /* OUT: Set to true if cannot descend */ -){ - int isEof; - int rc; - int ii; - RtreeNode *pChild; - sqlite3_int64 iRowid; - - RtreeNode *pSavedNode = pCursor->pNode; - int iSavedCell = pCursor->iCell; - - assert( iHeight>=0 ); - - if( iHeight==0 ){ - rc = testRtreeEntry(pRtree, pCursor, &isEof); - }else{ - rc = testRtreeCell(pRtree, pCursor, &isEof); - } - if( rc!=SQLITE_OK || isEof || iHeight==0 ){ - goto descend_to_cell_out; - } - - iRowid = nodeGetRowid(pRtree, pCursor->pNode, pCursor->iCell); - rc = nodeAcquire(pRtree, iRowid, pCursor->pNode, &pChild); - if( rc!=SQLITE_OK ){ - goto descend_to_cell_out; - } - - nodeRelease(pRtree, pCursor->pNode); - pCursor->pNode = pChild; - isEof = 1; - for(ii=0; isEof && iiiCell = ii; - rc = descendToCell(pRtree, pCursor, iHeight-1, &isEof); - if( rc!=SQLITE_OK ){ - goto descend_to_cell_out; - } - } - - if( isEof ){ - assert( pCursor->pNode==pChild ); - nodeReference(pSavedNode); - nodeRelease(pRtree, pChild); - pCursor->pNode = pSavedNode; - pCursor->iCell = iSavedCell; - } - -descend_to_cell_out: - *pEof = isEof; - return rc; -} - -/* -** One of the cells in node pNode is guaranteed to have a 64-bit -** integer value equal to iRowid. Return the index of this cell. -*/ -static int nodeRowidIndex( - Rtree *pRtree, - RtreeNode *pNode, - i64 iRowid, - int *piIndex -){ - int ii; - int nCell = NCELL(pNode); - for(ii=0; iipParent; - if( pParent ){ - return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex); - } - *piIndex = -1; - return SQLITE_OK; -} - -/* -** Rtree virtual table module xNext method. -*/ -static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){ - Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab); - RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; - int rc = SQLITE_OK; - - /* RtreeCursor.pNode must not be NULL. If is is NULL, then this cursor is - ** already at EOF. It is against the rules to call the xNext() method of - ** a cursor that has already reached EOF. - */ - assert( pCsr->pNode ); - - if( pCsr->iStrategy==1 ){ - /* This "scan" is a direct lookup by rowid. There is no next entry. */ - nodeRelease(pRtree, pCsr->pNode); - pCsr->pNode = 0; - }else{ - /* Move to the next entry that matches the configured constraints. */ - int iHeight = 0; - while( pCsr->pNode ){ - RtreeNode *pNode = pCsr->pNode; - int nCell = NCELL(pNode); - for(pCsr->iCell++; pCsr->iCelliCell++){ - int isEof; - rc = descendToCell(pRtree, pCsr, iHeight, &isEof); - if( rc!=SQLITE_OK || !isEof ){ - return rc; - } - } - pCsr->pNode = pNode->pParent; - rc = nodeParentIndex(pRtree, pNode, &pCsr->iCell); - if( rc!=SQLITE_OK ){ - return rc; - } - nodeReference(pCsr->pNode); - nodeRelease(pRtree, pNode); - iHeight++; - } - } - - return rc; -} - -/* -** Rtree virtual table module xRowid method. -*/ -static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ - Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; - RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; - - assert(pCsr->pNode); - *pRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell); - - return SQLITE_OK; -} - -/* -** Rtree virtual table module xColumn method. -*/ -static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ - Rtree *pRtree = (Rtree *)cur->pVtab; - RtreeCursor *pCsr = (RtreeCursor *)cur; - - if( i==0 ){ - i64 iRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell); - sqlite3_result_int64(ctx, iRowid); - }else{ - RtreeCoord c; - nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c); -#ifndef SQLITE_RTREE_INT_ONLY - if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ - sqlite3_result_double(ctx, c.f); - }else -#endif - { - assert( pRtree->eCoordType==RTREE_COORD_INT32 ); - sqlite3_result_int(ctx, c.i); - } - } - - return SQLITE_OK; -} - -/* -** Use nodeAcquire() to obtain the leaf node containing the record with -** rowid iRowid. If successful, set *ppLeaf to point to the node and -** return SQLITE_OK. If there is no such record in the table, set -** *ppLeaf to 0 and return SQLITE_OK. If an error occurs, set *ppLeaf -** to zero and return an SQLite error code. -*/ -static int findLeafNode(Rtree *pRtree, i64 iRowid, RtreeNode **ppLeaf){ - int rc; - *ppLeaf = 0; - sqlite3_bind_int64(pRtree->pReadRowid, 1, iRowid); - if( sqlite3_step(pRtree->pReadRowid)==SQLITE_ROW ){ - i64 iNode = sqlite3_column_int64(pRtree->pReadRowid, 0); - rc = nodeAcquire(pRtree, iNode, 0, ppLeaf); - sqlite3_reset(pRtree->pReadRowid); - }else{ - rc = sqlite3_reset(pRtree->pReadRowid); - } - return rc; -} - -/* -** This function is called to configure the RtreeConstraint object passed -** as the second argument for a MATCH constraint. The value passed as the -** first argument to this function is the right-hand operand to the MATCH -** operator. -*/ -static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ - RtreeMatchArg *p; - sqlite3_rtree_geometry *pGeom; - int nBlob; - - /* Check that value is actually a blob. */ - if( sqlite3_value_type(pValue)!=SQLITE_BLOB ) return SQLITE_ERROR; - - /* Check that the blob is roughly the right size. */ - nBlob = sqlite3_value_bytes(pValue); - if( nBlob<(int)sizeof(RtreeMatchArg) - || ((nBlob-sizeof(RtreeMatchArg))%sizeof(RtreeDValue))!=0 - ){ - return SQLITE_ERROR; - } - - pGeom = (sqlite3_rtree_geometry *)sqlite3_malloc( - sizeof(sqlite3_rtree_geometry) + nBlob - ); - if( !pGeom ) return SQLITE_NOMEM; - memset(pGeom, 0, sizeof(sqlite3_rtree_geometry)); - p = (RtreeMatchArg *)&pGeom[1]; - - memcpy(p, sqlite3_value_blob(pValue), nBlob); - if( p->magic!=RTREE_GEOMETRY_MAGIC - || nBlob!=(int)(sizeof(RtreeMatchArg) + (p->nParam-1)*sizeof(RtreeDValue)) - ){ - sqlite3_free(pGeom); - return SQLITE_ERROR; - } - - pGeom->pContext = p->pContext; - pGeom->nParam = p->nParam; - pGeom->aParam = p->aParam; - - pCons->xGeom = p->xGeom; - pCons->pGeom = pGeom; - return SQLITE_OK; -} - -/* -** Rtree virtual table module xFilter method. -*/ -static int rtreeFilter( - sqlite3_vtab_cursor *pVtabCursor, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; - RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; - - RtreeNode *pRoot = 0; - int ii; - int rc = SQLITE_OK; - - rtreeReference(pRtree); - - freeCursorConstraints(pCsr); - pCsr->iStrategy = idxNum; - - if( idxNum==1 ){ - /* Special case - lookup by rowid. */ - RtreeNode *pLeaf; /* Leaf on which the required cell resides */ - i64 iRowid = sqlite3_value_int64(argv[0]); - rc = findLeafNode(pRtree, iRowid, &pLeaf); - pCsr->pNode = pLeaf; - if( pLeaf ){ - assert( rc==SQLITE_OK ); - rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &pCsr->iCell); - } - }else{ - /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array - ** with the configured constraints. - */ - if( argc>0 ){ - pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc); - pCsr->nConstraint = argc; - if( !pCsr->aConstraint ){ - rc = SQLITE_NOMEM; - }else{ - memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc); - assert( (idxStr==0 && argc==0) - || (idxStr && (int)strlen(idxStr)==argc*2) ); - for(ii=0; iiaConstraint[ii]; - p->op = idxStr[ii*2]; - p->iCoord = idxStr[ii*2+1]-'a'; - if( p->op==RTREE_MATCH ){ - /* A MATCH operator. The right-hand-side must be a blob that - ** can be cast into an RtreeMatchArg object. One created using - ** an sqlite3_rtree_geometry_callback() SQL user function. - */ - rc = deserializeGeometry(argv[ii], p); - if( rc!=SQLITE_OK ){ - break; - } - }else{ -#ifdef SQLITE_RTREE_INT_ONLY - p->rValue = sqlite3_value_int64(argv[ii]); -#else - p->rValue = sqlite3_value_double(argv[ii]); -#endif - } - } - } - } - - if( rc==SQLITE_OK ){ - pCsr->pNode = 0; - rc = nodeAcquire(pRtree, 1, 0, &pRoot); - } - if( rc==SQLITE_OK ){ - int isEof = 1; - int nCell = NCELL(pRoot); - pCsr->pNode = pRoot; - for(pCsr->iCell=0; rc==SQLITE_OK && pCsr->iCelliCell++){ - assert( pCsr->pNode==pRoot ); - rc = descendToCell(pRtree, pCsr, pRtree->iDepth, &isEof); - if( !isEof ){ - break; - } - } - if( rc==SQLITE_OK && isEof ){ - assert( pCsr->pNode==pRoot ); - nodeRelease(pRtree, pRoot); - pCsr->pNode = 0; - } - assert( rc!=SQLITE_OK || !pCsr->pNode || pCsr->iCellpNode) ); - } - } - - rtreeRelease(pRtree); - return rc; -} - -/* -** Rtree virtual table module xBestIndex method. There are three -** table scan strategies to choose from (in order from most to -** least desirable): -** -** idxNum idxStr Strategy -** ------------------------------------------------ -** 1 Unused Direct lookup by rowid. -** 2 See below R-tree query or full-table scan. -** ------------------------------------------------ -** -** If strategy 1 is used, then idxStr is not meaningful. If strategy -** 2 is used, idxStr is formatted to contain 2 bytes for each -** constraint used. The first two bytes of idxStr correspond to -** the constraint in sqlite3_index_info.aConstraintUsage[] with -** (argvIndex==1) etc. -** -** The first of each pair of bytes in idxStr identifies the constraint -** operator as follows: -** -** Operator Byte Value -** ---------------------- -** = 0x41 ('A') -** <= 0x42 ('B') -** < 0x43 ('C') -** >= 0x44 ('D') -** > 0x45 ('E') -** MATCH 0x46 ('F') -** ---------------------- -** -** The second of each pair of bytes identifies the coordinate column -** to which the constraint applies. The leftmost coordinate column -** is 'a', the second from the left 'b' etc. -*/ -static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - int rc = SQLITE_OK; - int ii; - - int iIdx = 0; - char zIdxStr[RTREE_MAX_DIMENSIONS*8+1]; - memset(zIdxStr, 0, sizeof(zIdxStr)); - UNUSED_PARAMETER(tab); - - assert( pIdxInfo->idxStr==0 ); - for(ii=0; iinConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){ - struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; - - if( p->usable && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ - /* We have an equality constraint on the rowid. Use strategy 1. */ - int jj; - for(jj=0; jjaConstraintUsage[jj].argvIndex = 0; - pIdxInfo->aConstraintUsage[jj].omit = 0; - } - pIdxInfo->idxNum = 1; - pIdxInfo->aConstraintUsage[ii].argvIndex = 1; - pIdxInfo->aConstraintUsage[jj].omit = 1; - - /* This strategy involves a two rowid lookups on an B-Tree structures - ** and then a linear search of an R-Tree node. This should be - ** considered almost as quick as a direct rowid lookup (for which - ** sqlite uses an internal cost of 0.0). - */ - pIdxInfo->estimatedCost = 10.0; - return SQLITE_OK; - } - - if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){ - u8 op; - switch( p->op ){ - case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; - case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; - case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; - case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; - case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; - default: - assert( p->op==SQLITE_INDEX_CONSTRAINT_MATCH ); - op = RTREE_MATCH; - break; - } - zIdxStr[iIdx++] = op; - zIdxStr[iIdx++] = p->iColumn - 1 + 'a'; - pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); - pIdxInfo->aConstraintUsage[ii].omit = 1; - } - } - - pIdxInfo->idxNum = 2; - pIdxInfo->needToFreeIdxStr = 1; - if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){ - return SQLITE_NOMEM; - } - assert( iIdx>=0 ); - pIdxInfo->estimatedCost = (2000000.0 / (double)(iIdx + 1)); - return rc; -} - -/* -** Return the N-dimensional volumn of the cell stored in *p. -*/ -static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ - RtreeDValue area = (RtreeDValue)1; - int ii; - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ - area = (area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]))); - } - return area; -} - -/* -** Return the margin length of cell p. The margin length is the sum -** of the objects size in each dimension. -*/ -static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){ - RtreeDValue margin = (RtreeDValue)0; - int ii; - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ - margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])); - } - return margin; -} - -/* -** Store the union of cells p1 and p2 in p1. -*/ -static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ - int ii; - if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ - p1->aCoord[ii].f = MIN(p1->aCoord[ii].f, p2->aCoord[ii].f); - p1->aCoord[ii+1].f = MAX(p1->aCoord[ii+1].f, p2->aCoord[ii+1].f); - } - }else{ - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ - p1->aCoord[ii].i = MIN(p1->aCoord[ii].i, p2->aCoord[ii].i); - p1->aCoord[ii+1].i = MAX(p1->aCoord[ii+1].i, p2->aCoord[ii+1].i); - } - } -} - -/* -** Return true if the area covered by p2 is a subset of the area covered -** by p1. False otherwise. -*/ -static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ - int ii; - int isInt = (pRtree->eCoordType==RTREE_COORD_INT32); - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ - RtreeCoord *a1 = &p1->aCoord[ii]; - RtreeCoord *a2 = &p2->aCoord[ii]; - if( (!isInt && (a2[0].fa1[1].f)) - || ( isInt && (a2[0].ia1[1].i)) - ){ - return 0; - } - } - return 1; -} - -/* -** Return the amount cell p would grow by if it were unioned with pCell. -*/ -static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){ - RtreeDValue area; - RtreeCell cell; - memcpy(&cell, p, sizeof(RtreeCell)); - area = cellArea(pRtree, &cell); - cellUnion(pRtree, &cell, pCell); - return (cellArea(pRtree, &cell)-area); -} - -#if VARIANT_RSTARTREE_CHOOSESUBTREE || VARIANT_RSTARTREE_SPLIT -static RtreeDValue cellOverlap( - Rtree *pRtree, - RtreeCell *p, - RtreeCell *aCell, - int nCell, - int iExclude -){ - int ii; - RtreeDValue overlap = 0.0; - for(ii=0; iinDim*2); jj+=2){ - RtreeDValue x1, x2; - - x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj])); - x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1])); - - if( x2iDepth-iHeight); ii++){ - int iCell; - sqlite3_int64 iBest = 0; - - RtreeDValue fMinGrowth = 0.0; - RtreeDValue fMinArea = 0.0; -#if VARIANT_RSTARTREE_CHOOSESUBTREE - RtreeDValue fMinOverlap = 0.0; - RtreeDValue overlap; -#endif - - int nCell = NCELL(pNode); - RtreeCell cell; - RtreeNode *pChild; - - RtreeCell *aCell = 0; - -#if VARIANT_RSTARTREE_CHOOSESUBTREE - if( ii==(pRtree->iDepth-1) ){ - int jj; - aCell = sqlite3_malloc(sizeof(RtreeCell)*nCell); - if( !aCell ){ - rc = SQLITE_NOMEM; - nodeRelease(pRtree, pNode); - pNode = 0; - continue; - } - for(jj=0; jjiDepth-1) ){ - overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell); - }else{ - overlap = 0.0; - } - if( (iCell==0) - || (overlappParent ){ - RtreeNode *pParent = p->pParent; - RtreeCell cell; - int iCell; - - if( nodeParentIndex(pRtree, p, &iCell) ){ - return SQLITE_CORRUPT_VTAB; - } - - nodeGetCell(pRtree, pParent, iCell, &cell); - if( !cellContains(pRtree, &cell, pCell) ){ - cellUnion(pRtree, &cell, pCell); - nodeOverwriteCell(pRtree, pParent, &cell, iCell); - } - - p = pParent; - } - return SQLITE_OK; -} - -/* -** Write mapping (iRowid->iNode) to the _rowid table. -*/ -static int rowidWrite(Rtree *pRtree, sqlite3_int64 iRowid, sqlite3_int64 iNode){ - sqlite3_bind_int64(pRtree->pWriteRowid, 1, iRowid); - sqlite3_bind_int64(pRtree->pWriteRowid, 2, iNode); - sqlite3_step(pRtree->pWriteRowid); - return sqlite3_reset(pRtree->pWriteRowid); -} - -/* -** Write mapping (iNode->iPar) to the _parent table. -*/ -static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){ - sqlite3_bind_int64(pRtree->pWriteParent, 1, iNode); - sqlite3_bind_int64(pRtree->pWriteParent, 2, iPar); - sqlite3_step(pRtree->pWriteParent); - return sqlite3_reset(pRtree->pWriteParent); -} - -static int rtreeInsertCell(Rtree *, RtreeNode *, RtreeCell *, int); - -#if VARIANT_GUTTMAN_LINEAR_SPLIT -/* -** Implementation of the linear variant of the PickNext() function from -** Guttman[84]. -*/ -static RtreeCell *LinearPickNext( - Rtree *pRtree, - RtreeCell *aCell, - int nCell, - RtreeCell *pLeftBox, - RtreeCell *pRightBox, - int *aiUsed -){ - int ii; - for(ii=0; aiUsed[ii]; ii++); - aiUsed[ii] = 1; - return &aCell[ii]; -} - -/* -** Implementation of the linear variant of the PickSeeds() function from -** Guttman[84]. -*/ -static void LinearPickSeeds( - Rtree *pRtree, - RtreeCell *aCell, - int nCell, - int *piLeftSeed, - int *piRightSeed -){ - int i; - int iLeftSeed = 0; - int iRightSeed = 1; - RtreeDValue maxNormalInnerWidth = (RtreeDValue)0; - - /* Pick two "seed" cells from the array of cells. The algorithm used - ** here is the LinearPickSeeds algorithm from Gutman[1984]. The - ** indices of the two seed cells in the array are stored in local - ** variables iLeftSeek and iRightSeed. - */ - for(i=0; inDim; i++){ - RtreeDValue x1 = DCOORD(aCell[0].aCoord[i*2]); - RtreeDValue x2 = DCOORD(aCell[0].aCoord[i*2+1]); - RtreeDValue x3 = x1; - RtreeDValue x4 = x2; - int jj; - - int iCellLeft = 0; - int iCellRight = 0; - - for(jj=1; jjx4 ) x4 = right; - if( left>x3 ){ - x3 = left; - iCellRight = jj; - } - if( rightmaxNormalInnerWidth ){ - iLeftSeed = iCellLeft; - iRightSeed = iCellRight; - } - } - } - - *piLeftSeed = iLeftSeed; - *piRightSeed = iRightSeed; -} -#endif /* VARIANT_GUTTMAN_LINEAR_SPLIT */ - -#if VARIANT_GUTTMAN_QUADRATIC_SPLIT -/* -** Implementation of the quadratic variant of the PickNext() function from -** Guttman[84]. -*/ -static RtreeCell *QuadraticPickNext( - Rtree *pRtree, - RtreeCell *aCell, - int nCell, - RtreeCell *pLeftBox, - RtreeCell *pRightBox, - int *aiUsed -){ - #define FABS(a) ((a)<0.0?-1.0*(a):(a)) - - int iSelect = -1; - RtreeDValue fDiff; - int ii; - for(ii=0; iifDiff ){ - fDiff = diff; - iSelect = ii; - } - } - } - aiUsed[iSelect] = 1; - return &aCell[iSelect]; -} - -/* -** Implementation of the quadratic variant of the PickSeeds() function from -** Guttman[84]. -*/ -static void QuadraticPickSeeds( - Rtree *pRtree, - RtreeCell *aCell, - int nCell, - int *piLeftSeed, - int *piRightSeed -){ - int ii; - int jj; - - int iLeftSeed = 0; - int iRightSeed = 1; - RtreeDValue fWaste = 0.0; - - for(ii=0; iifWaste ){ - iLeftSeed = ii; - iRightSeed = jj; - fWaste = waste; - } - } - } - - *piLeftSeed = iLeftSeed; - *piRightSeed = iRightSeed; -} -#endif /* VARIANT_GUTTMAN_QUADRATIC_SPLIT */ - -/* -** Arguments aIdx, aDistance and aSpare all point to arrays of size -** nIdx. The aIdx array contains the set of integers from 0 to -** (nIdx-1) in no particular order. This function sorts the values -** in aIdx according to the indexed values in aDistance. For -** example, assuming the inputs: -** -** aIdx = { 0, 1, 2, 3 } -** aDistance = { 5.0, 2.0, 7.0, 6.0 } -** -** this function sets the aIdx array to contain: -** -** aIdx = { 0, 1, 2, 3 } -** -** The aSpare array is used as temporary working space by the -** sorting algorithm. -*/ -static void SortByDistance( - int *aIdx, - int nIdx, - RtreeDValue *aDistance, - int *aSpare -){ - if( nIdx>1 ){ - int iLeft = 0; - int iRight = 0; - - int nLeft = nIdx/2; - int nRight = nIdx-nLeft; - int *aLeft = aIdx; - int *aRight = &aIdx[nLeft]; - - SortByDistance(aLeft, nLeft, aDistance, aSpare); - SortByDistance(aRight, nRight, aDistance, aSpare); - - memcpy(aSpare, aLeft, sizeof(int)*nLeft); - aLeft = aSpare; - - while( iLeft1 ){ - - int iLeft = 0; - int iRight = 0; - - int nLeft = nIdx/2; - int nRight = nIdx-nLeft; - int *aLeft = aIdx; - int *aRight = &aIdx[nLeft]; - - SortByDimension(pRtree, aLeft, nLeft, iDim, aCell, aSpare); - SortByDimension(pRtree, aRight, nRight, iDim, aCell, aSpare); - - memcpy(aSpare, aLeft, sizeof(int)*nLeft); - aLeft = aSpare; - while( iLeftnDim+1)*(sizeof(int*)+nCell*sizeof(int)); - - aaSorted = (int **)sqlite3_malloc(nByte); - if( !aaSorted ){ - return SQLITE_NOMEM; - } - - aSpare = &((int *)&aaSorted[pRtree->nDim])[pRtree->nDim*nCell]; - memset(aaSorted, 0, nByte); - for(ii=0; iinDim; ii++){ - int jj; - aaSorted[ii] = &((int *)&aaSorted[pRtree->nDim])[ii*nCell]; - for(jj=0; jjnDim; ii++){ - RtreeDValue margin = 0.0; - RtreeDValue fBestOverlap = 0.0; - RtreeDValue fBestArea = 0.0; - int iBestLeft = 0; - int nLeft; - - for( - nLeft=RTREE_MINCELLS(pRtree); - nLeft<=(nCell-RTREE_MINCELLS(pRtree)); - nLeft++ - ){ - RtreeCell left; - RtreeCell right; - int kk; - RtreeDValue overlap; - RtreeDValue area; - - memcpy(&left, &aCell[aaSorted[ii][0]], sizeof(RtreeCell)); - memcpy(&right, &aCell[aaSorted[ii][nCell-1]], sizeof(RtreeCell)); - for(kk=1; kk<(nCell-1); kk++){ - if( kk0; i--){ - RtreeCell *pNext; - pNext = PickNext(pRtree, aCell, nCell, pBboxLeft, pBboxRight, aiUsed); - RtreeDValue diff = - cellGrowth(pRtree, pBboxLeft, pNext) - - cellGrowth(pRtree, pBboxRight, pNext) - ; - if( (RTREE_MINCELLS(pRtree)-NCELL(pRight)==i) - || (diff>0.0 && (RTREE_MINCELLS(pRtree)-NCELL(pLeft)!=i)) - ){ - nodeInsertCell(pRtree, pRight, pNext); - cellUnion(pRtree, pBboxRight, pNext); - }else{ - nodeInsertCell(pRtree, pLeft, pNext); - cellUnion(pRtree, pBboxLeft, pNext); - } - } - - sqlite3_free(aiUsed); - return SQLITE_OK; -} -#endif - -static int updateMapping( - Rtree *pRtree, - i64 iRowid, - RtreeNode *pNode, - int iHeight -){ - int (*xSetMapping)(Rtree *, sqlite3_int64, sqlite3_int64); - xSetMapping = ((iHeight==0)?rowidWrite:parentWrite); - if( iHeight>0 ){ - RtreeNode *pChild = nodeHashLookup(pRtree, iRowid); - if( pChild ){ - nodeRelease(pRtree, pChild->pParent); - nodeReference(pNode); - pChild->pParent = pNode; - } - } - return xSetMapping(pRtree, iRowid, pNode->iNode); -} - -static int SplitNode( - Rtree *pRtree, - RtreeNode *pNode, - RtreeCell *pCell, - int iHeight -){ - int i; - int newCellIsRight = 0; - - int rc = SQLITE_OK; - int nCell = NCELL(pNode); - RtreeCell *aCell; - int *aiUsed; - - RtreeNode *pLeft = 0; - RtreeNode *pRight = 0; - - RtreeCell leftbbox; - RtreeCell rightbbox; - - /* Allocate an array and populate it with a copy of pCell and - ** all cells from node pLeft. Then zero the original node. - */ - aCell = sqlite3_malloc((sizeof(RtreeCell)+sizeof(int))*(nCell+1)); - if( !aCell ){ - rc = SQLITE_NOMEM; - goto splitnode_out; - } - aiUsed = (int *)&aCell[nCell+1]; - memset(aiUsed, 0, sizeof(int)*(nCell+1)); - for(i=0; iiNode==1 ){ - pRight = nodeNew(pRtree, pNode); - pLeft = nodeNew(pRtree, pNode); - pRtree->iDepth++; - pNode->isDirty = 1; - writeInt16(pNode->zData, pRtree->iDepth); - }else{ - pLeft = pNode; - pRight = nodeNew(pRtree, pLeft->pParent); - nodeReference(pLeft); - } - - if( !pLeft || !pRight ){ - rc = SQLITE_NOMEM; - goto splitnode_out; - } - - memset(pLeft->zData, 0, pRtree->iNodeSize); - memset(pRight->zData, 0, pRtree->iNodeSize); - - rc = AssignCells(pRtree, aCell, nCell, pLeft, pRight, &leftbbox, &rightbbox); - if( rc!=SQLITE_OK ){ - goto splitnode_out; - } - - /* Ensure both child nodes have node numbers assigned to them by calling - ** nodeWrite(). Node pRight always needs a node number, as it was created - ** by nodeNew() above. But node pLeft sometimes already has a node number. - ** In this case avoid the all to nodeWrite(). - */ - if( SQLITE_OK!=(rc = nodeWrite(pRtree, pRight)) - || (0==pLeft->iNode && SQLITE_OK!=(rc = nodeWrite(pRtree, pLeft))) - ){ - goto splitnode_out; - } - - rightbbox.iRowid = pRight->iNode; - leftbbox.iRowid = pLeft->iNode; - - if( pNode->iNode==1 ){ - rc = rtreeInsertCell(pRtree, pLeft->pParent, &leftbbox, iHeight+1); - if( rc!=SQLITE_OK ){ - goto splitnode_out; - } - }else{ - RtreeNode *pParent = pLeft->pParent; - int iCell; - rc = nodeParentIndex(pRtree, pLeft, &iCell); - if( rc==SQLITE_OK ){ - nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell); - rc = AdjustTree(pRtree, pParent, &leftbbox); - } - if( rc!=SQLITE_OK ){ - goto splitnode_out; - } - } - if( (rc = rtreeInsertCell(pRtree, pRight->pParent, &rightbbox, iHeight+1)) ){ - goto splitnode_out; - } - - for(i=0; iiRowid ){ - newCellIsRight = 1; - } - if( rc!=SQLITE_OK ){ - goto splitnode_out; - } - } - if( pNode->iNode==1 ){ - for(i=0; iiRowid, pLeft, iHeight); - } - - if( rc==SQLITE_OK ){ - rc = nodeRelease(pRtree, pRight); - pRight = 0; - } - if( rc==SQLITE_OK ){ - rc = nodeRelease(pRtree, pLeft); - pLeft = 0; - } - -splitnode_out: - nodeRelease(pRtree, pRight); - nodeRelease(pRtree, pLeft); - sqlite3_free(aCell); - return rc; -} - -/* -** If node pLeaf is not the root of the r-tree and its pParent pointer is -** still NULL, load all ancestor nodes of pLeaf into memory and populate -** the pLeaf->pParent chain all the way up to the root node. -** -** This operation is required when a row is deleted (or updated - an update -** is implemented as a delete followed by an insert). SQLite provides the -** rowid of the row to delete, which can be used to find the leaf on which -** the entry resides (argument pLeaf). Once the leaf is located, this -** function is called to determine its ancestry. -*/ -static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ - int rc = SQLITE_OK; - RtreeNode *pChild = pLeaf; - while( rc==SQLITE_OK && pChild->iNode!=1 && pChild->pParent==0 ){ - int rc2 = SQLITE_OK; /* sqlite3_reset() return code */ - sqlite3_bind_int64(pRtree->pReadParent, 1, pChild->iNode); - rc = sqlite3_step(pRtree->pReadParent); - if( rc==SQLITE_ROW ){ - RtreeNode *pTest; /* Used to test for reference loops */ - i64 iNode; /* Node number of parent node */ - - /* Before setting pChild->pParent, test that we are not creating a - ** loop of references (as we would if, say, pChild==pParent). We don't - ** want to do this as it leads to a memory leak when trying to delete - ** the referenced counted node structures. - */ - iNode = sqlite3_column_int64(pRtree->pReadParent, 0); - for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent); - if( !pTest ){ - rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent); - } - } - rc = sqlite3_reset(pRtree->pReadParent); - if( rc==SQLITE_OK ) rc = rc2; - if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT_VTAB; - pChild = pChild->pParent; - } - return rc; -} - -static int deleteCell(Rtree *, RtreeNode *, int, int); - -static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ - int rc; - int rc2; - RtreeNode *pParent = 0; - int iCell; - - assert( pNode->nRef==1 ); - - /* Remove the entry in the parent cell. */ - rc = nodeParentIndex(pRtree, pNode, &iCell); - if( rc==SQLITE_OK ){ - pParent = pNode->pParent; - pNode->pParent = 0; - rc = deleteCell(pRtree, pParent, iCell, iHeight+1); - } - rc2 = nodeRelease(pRtree, pParent); - if( rc==SQLITE_OK ){ - rc = rc2; - } - if( rc!=SQLITE_OK ){ - return rc; - } - - /* Remove the xxx_node entry. */ - sqlite3_bind_int64(pRtree->pDeleteNode, 1, pNode->iNode); - sqlite3_step(pRtree->pDeleteNode); - if( SQLITE_OK!=(rc = sqlite3_reset(pRtree->pDeleteNode)) ){ - return rc; - } - - /* Remove the xxx_parent entry. */ - sqlite3_bind_int64(pRtree->pDeleteParent, 1, pNode->iNode); - sqlite3_step(pRtree->pDeleteParent); - if( SQLITE_OK!=(rc = sqlite3_reset(pRtree->pDeleteParent)) ){ - return rc; - } - - /* Remove the node from the in-memory hash table and link it into - ** the Rtree.pDeleted list. Its contents will be re-inserted later on. - */ - nodeHashDelete(pRtree, pNode); - pNode->iNode = iHeight; - pNode->pNext = pRtree->pDeleted; - pNode->nRef++; - pRtree->pDeleted = pNode; - - return SQLITE_OK; -} - -static int fixBoundingBox(Rtree *pRtree, RtreeNode *pNode){ - RtreeNode *pParent = pNode->pParent; - int rc = SQLITE_OK; - if( pParent ){ - int ii; - int nCell = NCELL(pNode); - RtreeCell box; /* Bounding box for pNode */ - nodeGetCell(pRtree, pNode, 0, &box); - for(ii=1; iiiNode; - rc = nodeParentIndex(pRtree, pNode, &ii); - if( rc==SQLITE_OK ){ - nodeOverwriteCell(pRtree, pParent, &box, ii); - rc = fixBoundingBox(pRtree, pParent); - } - } - return rc; -} - -/* -** Delete the cell at index iCell of node pNode. After removing the -** cell, adjust the r-tree data structure if required. -*/ -static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){ - RtreeNode *pParent; - int rc; - - if( SQLITE_OK!=(rc = fixLeafParent(pRtree, pNode)) ){ - return rc; - } - - /* Remove the cell from the node. This call just moves bytes around - ** the in-memory node image, so it cannot fail. - */ - nodeDeleteCell(pRtree, pNode, iCell); - - /* If the node is not the tree root and now has less than the minimum - ** number of cells, remove it from the tree. Otherwise, update the - ** cell in the parent node so that it tightly contains the updated - ** node. - */ - pParent = pNode->pParent; - assert( pParent || pNode->iNode==1 ); - if( pParent ){ - if( NCELL(pNode)nDim; iDim++){ - aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]); - aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]); - } - } - for(iDim=0; iDimnDim; iDim++){ - aCenterCoord[iDim] = (aCenterCoord[iDim]/(nCell*(RtreeDValue)2)); - } - - for(ii=0; iinDim; iDim++){ - RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) - - DCOORD(aCell[ii].aCoord[iDim*2])); - aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]); - } - } - - SortByDistance(aOrder, nCell, aDistance, aSpare); - nodeZero(pRtree, pNode); - - for(ii=0; rc==SQLITE_OK && ii<(nCell-(RTREE_MINCELLS(pRtree)+1)); ii++){ - RtreeCell *p = &aCell[aOrder[ii]]; - nodeInsertCell(pRtree, pNode, p); - if( p->iRowid==pCell->iRowid ){ - if( iHeight==0 ){ - rc = rowidWrite(pRtree, p->iRowid, pNode->iNode); - }else{ - rc = parentWrite(pRtree, p->iRowid, pNode->iNode); - } - } - } - if( rc==SQLITE_OK ){ - rc = fixBoundingBox(pRtree, pNode); - } - for(; rc==SQLITE_OK && iiiNode currently contains - ** the height of the sub-tree headed by the cell. - */ - RtreeNode *pInsert; - RtreeCell *p = &aCell[aOrder[ii]]; - rc = ChooseLeaf(pRtree, p, iHeight, &pInsert); - if( rc==SQLITE_OK ){ - int rc2; - rc = rtreeInsertCell(pRtree, pInsert, p, iHeight); - rc2 = nodeRelease(pRtree, pInsert); - if( rc==SQLITE_OK ){ - rc = rc2; - } - } - } - - sqlite3_free(aCell); - return rc; -} - -/* -** Insert cell pCell into node pNode. Node pNode is the head of a -** subtree iHeight high (leaf nodes have iHeight==0). -*/ -static int rtreeInsertCell( - Rtree *pRtree, - RtreeNode *pNode, - RtreeCell *pCell, - int iHeight -){ - int rc = SQLITE_OK; - if( iHeight>0 ){ - RtreeNode *pChild = nodeHashLookup(pRtree, pCell->iRowid); - if( pChild ){ - nodeRelease(pRtree, pChild->pParent); - nodeReference(pNode); - pChild->pParent = pNode; - } - } - if( nodeInsertCell(pRtree, pNode, pCell) ){ -#if VARIANT_RSTARTREE_REINSERT - if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){ - rc = SplitNode(pRtree, pNode, pCell, iHeight); - }else{ - pRtree->iReinsertHeight = iHeight; - rc = Reinsert(pRtree, pNode, pCell, iHeight); - } -#else - rc = SplitNode(pRtree, pNode, pCell, iHeight); -#endif - }else{ - rc = AdjustTree(pRtree, pNode, pCell); - if( rc==SQLITE_OK ){ - if( iHeight==0 ){ - rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); - }else{ - rc = parentWrite(pRtree, pCell->iRowid, pNode->iNode); - } - } - } - return rc; -} - -static int reinsertNodeContent(Rtree *pRtree, RtreeNode *pNode){ - int ii; - int rc = SQLITE_OK; - int nCell = NCELL(pNode); - - for(ii=0; rc==SQLITE_OK && iiiNode currently contains - ** the height of the sub-tree headed by the cell. - */ - rc = ChooseLeaf(pRtree, &cell, (int)pNode->iNode, &pInsert); - if( rc==SQLITE_OK ){ - int rc2; - rc = rtreeInsertCell(pRtree, pInsert, &cell, (int)pNode->iNode); - rc2 = nodeRelease(pRtree, pInsert); - if( rc==SQLITE_OK ){ - rc = rc2; - } - } - } - return rc; -} - -/* -** Select a currently unused rowid for a new r-tree record. -*/ -static int newRowid(Rtree *pRtree, i64 *piRowid){ - int rc; - sqlite3_bind_null(pRtree->pWriteRowid, 1); - sqlite3_bind_null(pRtree->pWriteRowid, 2); - sqlite3_step(pRtree->pWriteRowid); - rc = sqlite3_reset(pRtree->pWriteRowid); - *piRowid = sqlite3_last_insert_rowid(pRtree->db); - return rc; -} - -/* -** Remove the entry with rowid=iDelete from the r-tree structure. -*/ -static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ - int rc; /* Return code */ - RtreeNode *pLeaf = 0; /* Leaf node containing record iDelete */ - int iCell; /* Index of iDelete cell in pLeaf */ - RtreeNode *pRoot; /* Root node of rtree structure */ - - - /* Obtain a reference to the root node to initialize Rtree.iDepth */ - rc = nodeAcquire(pRtree, 1, 0, &pRoot); - - /* Obtain a reference to the leaf node that contains the entry - ** about to be deleted. - */ - if( rc==SQLITE_OK ){ - rc = findLeafNode(pRtree, iDelete, &pLeaf); - } - - /* Delete the cell in question from the leaf node. */ - if( rc==SQLITE_OK ){ - int rc2; - rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell); - if( rc==SQLITE_OK ){ - rc = deleteCell(pRtree, pLeaf, iCell, 0); - } - rc2 = nodeRelease(pRtree, pLeaf); - if( rc==SQLITE_OK ){ - rc = rc2; - } - } - - /* Delete the corresponding entry in the _rowid table. */ - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete); - sqlite3_step(pRtree->pDeleteRowid); - rc = sqlite3_reset(pRtree->pDeleteRowid); - } - - /* Check if the root node now has exactly one child. If so, remove - ** it, schedule the contents of the child for reinsertion and - ** reduce the tree height by one. - ** - ** This is equivalent to copying the contents of the child into - ** the root node (the operation that Gutman's paper says to perform - ** in this scenario). - */ - if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){ - int rc2; - RtreeNode *pChild; - i64 iChild = nodeGetRowid(pRtree, pRoot, 0); - rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); - if( rc==SQLITE_OK ){ - rc = removeNode(pRtree, pChild, pRtree->iDepth-1); - } - rc2 = nodeRelease(pRtree, pChild); - if( rc==SQLITE_OK ) rc = rc2; - if( rc==SQLITE_OK ){ - pRtree->iDepth--; - writeInt16(pRoot->zData, pRtree->iDepth); - pRoot->isDirty = 1; - } - } - - /* Re-insert the contents of any underfull nodes removed from the tree. */ - for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){ - if( rc==SQLITE_OK ){ - rc = reinsertNodeContent(pRtree, pLeaf); - } - pRtree->pDeleted = pLeaf->pNext; - sqlite3_free(pLeaf); - } - - /* Release the reference to the root node. */ - if( rc==SQLITE_OK ){ - rc = nodeRelease(pRtree, pRoot); - }else{ - nodeRelease(pRtree, pRoot); - } - - return rc; -} - -/* -** Rounding constants for float->double conversion. -*/ -#define RNDTOWARDS (1.0 - 1.0/8388608.0) /* Round towards zero */ -#define RNDAWAY (1.0 + 1.0/8388608.0) /* Round away from zero */ - -#if !defined(SQLITE_RTREE_INT_ONLY) -/* -** Convert an sqlite3_value into an RtreeValue (presumably a float) -** while taking care to round toward negative or positive, respectively. -*/ -static RtreeValue rtreeValueDown(sqlite3_value *v){ - double d = sqlite3_value_double(v); - float f = (float)d; - if( f>d ){ - f = (float)(d*(d<0 ? RNDAWAY : RNDTOWARDS)); - } - return f; -} -static RtreeValue rtreeValueUp(sqlite3_value *v){ - double d = sqlite3_value_double(v); - float f = (float)d; - if( f1 */ - int bHaveRowid = 0; /* Set to 1 after new rowid is determined */ - - rtreeReference(pRtree); - assert(nData>=1); - - /* Constraint handling. A write operation on an r-tree table may return - ** SQLITE_CONSTRAINT for two reasons: - ** - ** 1. A duplicate rowid value, or - ** 2. The supplied data violates the "x2>=x1" constraint. - ** - ** In the first case, if the conflict-handling mode is REPLACE, then - ** the conflicting row can be removed before proceeding. In the second - ** case, SQLITE_CONSTRAINT must be returned regardless of the - ** conflict-handling mode specified by the user. - */ - if( nData>1 ){ - int ii; - - /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */ - assert( nData==(pRtree->nDim*2 + 3) ); -#ifndef SQLITE_RTREE_INT_ONLY - if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ - cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]); - cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]); - if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){ - rc = SQLITE_CONSTRAINT; - goto constraint; - } - } - }else -#endif - { - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ - cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]); - cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]); - if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){ - rc = SQLITE_CONSTRAINT; - goto constraint; - } - } - } - - /* If a rowid value was supplied, check if it is already present in - ** the table. If so, the constraint has failed. */ - if( sqlite3_value_type(azData[2])!=SQLITE_NULL ){ - cell.iRowid = sqlite3_value_int64(azData[2]); - if( sqlite3_value_type(azData[0])==SQLITE_NULL - || sqlite3_value_int64(azData[0])!=cell.iRowid - ){ - int steprc; - sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid); - steprc = sqlite3_step(pRtree->pReadRowid); - rc = sqlite3_reset(pRtree->pReadRowid); - if( SQLITE_ROW==steprc ){ - if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){ - rc = rtreeDeleteRowid(pRtree, cell.iRowid); - }else{ - rc = SQLITE_CONSTRAINT; - goto constraint; - } - } - } - bHaveRowid = 1; - } - } - - /* If azData[0] is not an SQL NULL value, it is the rowid of a - ** record to delete from the r-tree table. The following block does - ** just that. - */ - if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){ - rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(azData[0])); - } - - /* If the azData[] array contains more than one element, elements - ** (azData[2]..azData[argc-1]) contain a new record to insert into - ** the r-tree structure. - */ - if( rc==SQLITE_OK && nData>1 ){ - /* Insert the new record into the r-tree */ - RtreeNode *pLeaf = 0; - - /* Figure out the rowid of the new row. */ - if( bHaveRowid==0 ){ - rc = newRowid(pRtree, &cell.iRowid); - } - *pRowid = cell.iRowid; - - if( rc==SQLITE_OK ){ - rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf); - } - if( rc==SQLITE_OK ){ - int rc2; - pRtree->iReinsertHeight = -1; - rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); - rc2 = nodeRelease(pRtree, pLeaf); - if( rc==SQLITE_OK ){ - rc = rc2; - } - } - } - -constraint: - rtreeRelease(pRtree); - return rc; -} - -/* -** The xRename method for rtree module virtual tables. -*/ -static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){ - Rtree *pRtree = (Rtree *)pVtab; - int rc = SQLITE_NOMEM; - char *zSql = sqlite3_mprintf( - "ALTER TABLE %Q.'%q_node' RENAME TO \"%w_node\";" - "ALTER TABLE %Q.'%q_parent' RENAME TO \"%w_parent\";" - "ALTER TABLE %Q.'%q_rowid' RENAME TO \"%w_rowid\";" - , pRtree->zDb, pRtree->zName, zNewName - , pRtree->zDb, pRtree->zName, zNewName - , pRtree->zDb, pRtree->zName, zNewName - ); - if( zSql ){ - rc = sqlite3_exec(pRtree->db, zSql, 0, 0, 0); - sqlite3_free(zSql); - } - return rc; -} - -static sqlite3_module rtreeModule = { - 0, /* iVersion */ - rtreeCreate, /* xCreate - create a table */ - rtreeConnect, /* xConnect - connect to an existing table */ - rtreeBestIndex, /* xBestIndex - Determine search strategy */ - rtreeDisconnect, /* xDisconnect - Disconnect from a table */ - rtreeDestroy, /* xDestroy - Drop a table */ - rtreeOpen, /* xOpen - open a cursor */ - rtreeClose, /* xClose - close a cursor */ - rtreeFilter, /* xFilter - configure scan constraints */ - rtreeNext, /* xNext - advance a cursor */ - rtreeEof, /* xEof */ - rtreeColumn, /* xColumn - read data */ - rtreeRowid, /* xRowid - read data */ - rtreeUpdate, /* xUpdate - write data */ - 0, /* xBegin - begin transaction */ - 0, /* xSync - sync transaction */ - 0, /* xCommit - commit transaction */ - 0, /* xRollback - rollback transaction */ - 0, /* xFindFunction - function overloading */ - rtreeRename, /* xRename - rename the table */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0 /* xRollbackTo */ -}; - -static int rtreeSqlInit( - Rtree *pRtree, - sqlite3 *db, - const char *zDb, - const char *zPrefix, - int isCreate -){ - int rc = SQLITE_OK; - - #define N_STATEMENT 9 - static const char *azSql[N_STATEMENT] = { - /* Read and write the xxx_node table */ - "SELECT data FROM '%q'.'%q_node' WHERE nodeno = :1", - "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(:1, :2)", - "DELETE FROM '%q'.'%q_node' WHERE nodeno = :1", - - /* Read and write the xxx_rowid table */ - "SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = :1", - "INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(:1, :2)", - "DELETE FROM '%q'.'%q_rowid' WHERE rowid = :1", - - /* Read and write the xxx_parent table */ - "SELECT parentnode FROM '%q'.'%q_parent' WHERE nodeno = :1", - "INSERT OR REPLACE INTO '%q'.'%q_parent' VALUES(:1, :2)", - "DELETE FROM '%q'.'%q_parent' WHERE nodeno = :1" - }; - sqlite3_stmt **appStmt[N_STATEMENT]; - int i; - - pRtree->db = db; - - if( isCreate ){ - char *zCreate = sqlite3_mprintf( -"CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY, data BLOB);" -"CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY, nodeno INTEGER);" -"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY, parentnode INTEGER);" -"INSERT INTO '%q'.'%q_node' VALUES(1, zeroblob(%d))", - zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, pRtree->iNodeSize - ); - if( !zCreate ){ - return SQLITE_NOMEM; - } - rc = sqlite3_exec(db, zCreate, 0, 0, 0); - sqlite3_free(zCreate); - if( rc!=SQLITE_OK ){ - return rc; - } - } - - appStmt[0] = &pRtree->pReadNode; - appStmt[1] = &pRtree->pWriteNode; - appStmt[2] = &pRtree->pDeleteNode; - appStmt[3] = &pRtree->pReadRowid; - appStmt[4] = &pRtree->pWriteRowid; - appStmt[5] = &pRtree->pDeleteRowid; - appStmt[6] = &pRtree->pReadParent; - appStmt[7] = &pRtree->pWriteParent; - appStmt[8] = &pRtree->pDeleteParent; - - for(i=0; iiNodeSize is populated and SQLITE_OK returned. -** Otherwise, an SQLite error code is returned. -** -** If this function is being called as part of an xConnect(), then the rtree -** table already exists. In this case the node-size is determined by inspecting -** the root node of the tree. -** -** Otherwise, for an xCreate(), use 64 bytes less than the database page-size. -** This ensures that each node is stored on a single database page. If the -** database page-size is so large that more than RTREE_MAXCELLS entries -** would fit in a single node, use a smaller node-size. -*/ -static int getNodeSize( - sqlite3 *db, /* Database handle */ - Rtree *pRtree, /* Rtree handle */ - int isCreate, /* True for xCreate, false for xConnect */ - char **pzErr /* OUT: Error message, if any */ -){ - int rc; - char *zSql; - if( isCreate ){ - int iPageSize = 0; - zSql = sqlite3_mprintf("PRAGMA %Q.page_size", pRtree->zDb); - rc = getIntFromStmt(db, zSql, &iPageSize); - if( rc==SQLITE_OK ){ - pRtree->iNodeSize = iPageSize-64; - if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)iNodeSize ){ - pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; - } - }else{ - *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); - } - }else{ - zSql = sqlite3_mprintf( - "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", - pRtree->zDb, pRtree->zName - ); - rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); - if( rc!=SQLITE_OK ){ - *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); - } - } - - sqlite3_free(zSql); - return rc; -} - -/* -** This function is the implementation of both the xConnect and xCreate -** methods of the r-tree virtual table. -** -** argv[0] -> module name -** argv[1] -> database name -** argv[2] -> table name -** argv[...] -> column names... -*/ -static int rtreeInit( - sqlite3 *db, /* Database connection */ - void *pAux, /* One of the RTREE_COORD_* constants */ - int argc, const char *const*argv, /* Parameters to CREATE TABLE statement */ - sqlite3_vtab **ppVtab, /* OUT: New virtual table */ - char **pzErr, /* OUT: Error message, if any */ - int isCreate /* True for xCreate, false for xConnect */ -){ - int rc = SQLITE_OK; - Rtree *pRtree; - int nDb; /* Length of string argv[1] */ - int nName; /* Length of string argv[2] */ - int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32); - - const char *aErrMsg[] = { - 0, /* 0 */ - "Wrong number of columns for an rtree table", /* 1 */ - "Too few columns for an rtree table", /* 2 */ - "Too many columns for an rtree table" /* 3 */ - }; - - int iErr = (argc<6) ? 2 : argc>(RTREE_MAX_DIMENSIONS*2+4) ? 3 : argc%2; - if( aErrMsg[iErr] ){ - *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]); - return SQLITE_ERROR; - } - - sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); - - /* Allocate the sqlite3_vtab structure */ - nDb = (int)strlen(argv[1]); - nName = (int)strlen(argv[2]); - pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2); - if( !pRtree ){ - return SQLITE_NOMEM; - } - memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); - pRtree->nBusy = 1; - pRtree->base.pModule = &rtreeModule; - pRtree->zDb = (char *)&pRtree[1]; - pRtree->zName = &pRtree->zDb[nDb+1]; - pRtree->nDim = (argc-4)/2; - pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2; - pRtree->eCoordType = eCoordType; - memcpy(pRtree->zDb, argv[1], nDb); - memcpy(pRtree->zName, argv[2], nName); - - /* Figure out the node size to use. */ - rc = getNodeSize(db, pRtree, isCreate, pzErr); - - /* Create/Connect to the underlying relational database schema. If - ** that is successful, call sqlite3_declare_vtab() to configure - ** the r-tree table schema. - */ - if( rc==SQLITE_OK ){ - if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){ - *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); - }else{ - char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]); - char *zTmp; - int ii; - for(ii=4; zSql && ii*2 coordinates. -*/ -static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ - char *zText = 0; - RtreeNode node; - Rtree tree; - int ii; - - UNUSED_PARAMETER(nArg); - memset(&node, 0, sizeof(RtreeNode)); - memset(&tree, 0, sizeof(Rtree)); - tree.nDim = sqlite3_value_int(apArg[0]); - tree.nBytesPerCell = 8 + 8 * tree.nDim; - node.zData = (u8 *)sqlite3_value_blob(apArg[1]); - - for(ii=0; iimagic = RTREE_GEOMETRY_MAGIC; - pBlob->xGeom = pGeomCtx->xGeom; - pBlob->pContext = pGeomCtx->pContext; - pBlob->nParam = nArg; - for(i=0; iaParam[i] = sqlite3_value_int64(aArg[i]); -#else - pBlob->aParam[i] = sqlite3_value_double(aArg[i]); -#endif - } - sqlite3_result_blob(ctx, pBlob, nBlob, doSqlite3Free); - } -} - -/* -** Register a new geometry function for use with the r-tree MATCH operator. -*/ -SQLITE_API int sqlite3_rtree_geometry_callback( - sqlite3 *db, - const char *zGeom, - int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue *, int *), - void *pContext -){ - RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */ - - /* Allocate and populate the context object. */ - pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback)); - if( !pGeomCtx ) return SQLITE_NOMEM; - pGeomCtx->xGeom = xGeom; - pGeomCtx->pContext = pContext; - - /* Create the new user-function. Register a destructor function to delete - ** the context object when it is no longer required. */ - return sqlite3_create_function_v2(db, zGeom, -1, SQLITE_ANY, - (void *)pGeomCtx, geomCallback, 0, 0, doSqlite3Free - ); -} - -#if !SQLITE_CORE -SQLITE_API int sqlite3_extension_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi) - return sqlite3RtreeInit(db); -} -#endif - -#endif - -/************** End of rtree.c ***********************************************/ -/************** Begin file icu.c *********************************************/ -/* -** 2007 May 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** $Id: icu.c,v 1.7 2007/12/13 21:54:11 drh Exp $ -** -** This file implements an integration between the ICU library -** ("International Components for Unicode", an open-source library -** for handling unicode data) and SQLite. The integration uses -** ICU to provide the following to SQLite: -** -** * An implementation of the SQL regexp() function (and hence REGEXP -** operator) using the ICU uregex_XX() APIs. -** -** * Implementations of the SQL scalar upper() and lower() functions -** for case mapping. -** -** * Integration of ICU and SQLite collation seqences. -** -** * An implementation of the LIKE operator that uses ICU to -** provide case-independent matching. -*/ - -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) - -/* Include ICU headers */ -#include -#include -#include -#include - -/* #include */ - -#ifndef SQLITE_CORE - SQLITE_EXTENSION_INIT1 -#else -#endif - -/* -** Maximum length (in bytes) of the pattern in a LIKE or GLOB -** operator. -*/ -#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH -# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 -#endif - -/* -** Version of sqlite3_free() that is always a function, never a macro. -*/ -static void xFree(void *p){ - sqlite3_free(p); -} - -/* -** Compare two UTF-8 strings for equality where the first string is -** a "LIKE" expression. Return true (1) if they are the same and -** false (0) if they are different. -*/ -static int icuLikeCompare( - const uint8_t *zPattern, /* LIKE pattern */ - const uint8_t *zString, /* The UTF-8 string to compare against */ - const UChar32 uEsc /* The escape character */ -){ - static const int MATCH_ONE = (UChar32)'_'; - static const int MATCH_ALL = (UChar32)'%'; - - int iPattern = 0; /* Current byte index in zPattern */ - int iString = 0; /* Current byte index in zString */ - - int prevEscape = 0; /* True if the previous character was uEsc */ - - while( zPattern[iPattern]!=0 ){ - - /* Read (and consume) the next character from the input pattern. */ - UChar32 uPattern; - U8_NEXT_UNSAFE(zPattern, iPattern, uPattern); - assert(uPattern!=0); - - /* There are now 4 possibilities: - ** - ** 1. uPattern is an unescaped match-all character "%", - ** 2. uPattern is an unescaped match-one character "_", - ** 3. uPattern is an unescaped escape character, or - ** 4. uPattern is to be handled as an ordinary character - */ - if( !prevEscape && uPattern==MATCH_ALL ){ - /* Case 1. */ - uint8_t c; - - /* Skip any MATCH_ALL or MATCH_ONE characters that follow a - ** MATCH_ALL. For each MATCH_ONE, skip one character in the - ** test string. - */ - while( (c=zPattern[iPattern]) == MATCH_ALL || c == MATCH_ONE ){ - if( c==MATCH_ONE ){ - if( zString[iString]==0 ) return 0; - U8_FWD_1_UNSAFE(zString, iString); - } - iPattern++; - } - - if( zPattern[iPattern]==0 ) return 1; - - while( zString[iString] ){ - if( icuLikeCompare(&zPattern[iPattern], &zString[iString], uEsc) ){ - return 1; - } - U8_FWD_1_UNSAFE(zString, iString); - } - return 0; - - }else if( !prevEscape && uPattern==MATCH_ONE ){ - /* Case 2. */ - if( zString[iString]==0 ) return 0; - U8_FWD_1_UNSAFE(zString, iString); - - }else if( !prevEscape && uPattern==uEsc){ - /* Case 3. */ - prevEscape = 1; - - }else{ - /* Case 4. */ - UChar32 uString; - U8_NEXT_UNSAFE(zString, iString, uString); - uString = u_foldCase(uString, U_FOLD_CASE_DEFAULT); - uPattern = u_foldCase(uPattern, U_FOLD_CASE_DEFAULT); - if( uString!=uPattern ){ - return 0; - } - prevEscape = 0; - } - } - - return zString[iString]==0; -} - -/* -** Implementation of the like() SQL function. This function implements -** the build-in LIKE operator. The first argument to the function is the -** pattern and the second argument is the string. So, the SQL statements: -** -** A LIKE B -** -** is implemented as like(B, A). If there is an escape character E, -** -** A LIKE B ESCAPE E -** -** is mapped to like(B, A, E). -*/ -static void icuLikeFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zA = sqlite3_value_text(argv[0]); - const unsigned char *zB = sqlite3_value_text(argv[1]); - UChar32 uEsc = 0; - - /* Limit the length of the LIKE or GLOB pattern to avoid problems - ** of deep recursion and N*N behavior in patternCompare(). - */ - if( sqlite3_value_bytes(argv[0])>SQLITE_MAX_LIKE_PATTERN_LENGTH ){ - sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1); - return; - } - - - if( argc==3 ){ - /* The escape character string must consist of a single UTF-8 character. - ** Otherwise, return an error. - */ - int nE= sqlite3_value_bytes(argv[2]); - const unsigned char *zE = sqlite3_value_text(argv[2]); - int i = 0; - if( zE==0 ) return; - U8_NEXT(zE, i, nE, uEsc); - if( i!=nE){ - sqlite3_result_error(context, - "ESCAPE expression must be a single character", -1); - return; - } - } - - if( zA && zB ){ - sqlite3_result_int(context, icuLikeCompare(zA, zB, uEsc)); - } -} - -/* -** This function is called when an ICU function called from within -** the implementation of an SQL scalar function returns an error. -** -** The scalar function context passed as the first argument is -** loaded with an error message based on the following two args. -*/ -static void icuFunctionError( - sqlite3_context *pCtx, /* SQLite scalar function context */ - const char *zName, /* Name of ICU function that failed */ - UErrorCode e /* Error code returned by ICU function */ -){ - char zBuf[128]; - sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e)); - zBuf[127] = '\0'; - sqlite3_result_error(pCtx, zBuf, -1); -} - -/* -** Function to delete compiled regexp objects. Registered as -** a destructor function with sqlite3_set_auxdata(). -*/ -static void icuRegexpDelete(void *p){ - URegularExpression *pExpr = (URegularExpression *)p; - uregex_close(pExpr); -} - -/* -** Implementation of SQLite REGEXP operator. This scalar function takes -** two arguments. The first is a regular expression pattern to compile -** the second is a string to match against that pattern. If either -** argument is an SQL NULL, then NULL Is returned. Otherwise, the result -** is 1 if the string matches the pattern, or 0 otherwise. -** -** SQLite maps the regexp() function to the regexp() operator such -** that the following two are equivalent: -** -** zString REGEXP zPattern -** regexp(zPattern, zString) -** -** Uses the following ICU regexp APIs: -** -** uregex_open() -** uregex_matches() -** uregex_close() -*/ -static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){ - UErrorCode status = U_ZERO_ERROR; - URegularExpression *pExpr; - UBool res; - const UChar *zString = sqlite3_value_text16(apArg[1]); - - (void)nArg; /* Unused parameter */ - - /* If the left hand side of the regexp operator is NULL, - ** then the result is also NULL. - */ - if( !zString ){ - return; - } - - pExpr = sqlite3_get_auxdata(p, 0); - if( !pExpr ){ - const UChar *zPattern = sqlite3_value_text16(apArg[0]); - if( !zPattern ){ - return; - } - pExpr = uregex_open(zPattern, -1, 0, 0, &status); - - if( U_SUCCESS(status) ){ - sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete); - }else{ - assert(!pExpr); - icuFunctionError(p, "uregex_open", status); - return; - } - } - - /* Configure the text that the regular expression operates on. */ - uregex_setText(pExpr, zString, -1, &status); - if( !U_SUCCESS(status) ){ - icuFunctionError(p, "uregex_setText", status); - return; - } - - /* Attempt the match */ - res = uregex_matches(pExpr, 0, &status); - if( !U_SUCCESS(status) ){ - icuFunctionError(p, "uregex_matches", status); - return; - } - - /* Set the text that the regular expression operates on to a NULL - ** pointer. This is not really necessary, but it is tidier than - ** leaving the regular expression object configured with an invalid - ** pointer after this function returns. - */ - uregex_setText(pExpr, 0, 0, &status); - - /* Return 1 or 0. */ - sqlite3_result_int(p, res ? 1 : 0); -} - -/* -** Implementations of scalar functions for case mapping - upper() and -** lower(). Function upper() converts its input to upper-case (ABC). -** Function lower() converts to lower-case (abc). -** -** ICU provides two types of case mapping, "general" case mapping and -** "language specific". Refer to ICU documentation for the differences -** between the two. -** -** To utilise "general" case mapping, the upper() or lower() scalar -** functions are invoked with one argument: -** -** upper('ABC') -> 'abc' -** lower('abc') -> 'ABC' -** -** To access ICU "language specific" case mapping, upper() or lower() -** should be invoked with two arguments. The second argument is the name -** of the locale to use. Passing an empty string ("") or SQL NULL value -** as the second argument is the same as invoking the 1 argument version -** of upper() or lower(). -** -** lower('I', 'en_us') -> 'i' -** lower('I', 'tr_tr') -> 'ı' (small dotless i) -** -** http://www.icu-project.org/userguide/posix.html#case_mappings -*/ -static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){ - const UChar *zInput; - UChar *zOutput; - int nInput; - int nOutput; - - UErrorCode status = U_ZERO_ERROR; - const char *zLocale = 0; - - assert(nArg==1 || nArg==2); - if( nArg==2 ){ - zLocale = (const char *)sqlite3_value_text(apArg[1]); - } - - zInput = sqlite3_value_text16(apArg[0]); - if( !zInput ){ - return; - } - nInput = sqlite3_value_bytes16(apArg[0]); - - nOutput = nInput * 2 + 2; - zOutput = sqlite3_malloc(nOutput); - if( !zOutput ){ - return; - } - - if( sqlite3_user_data(p) ){ - u_strToUpper(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status); - }else{ - u_strToLower(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status); - } - - if( !U_SUCCESS(status) ){ - icuFunctionError(p, "u_strToLower()/u_strToUpper", status); - return; - } - - sqlite3_result_text16(p, zOutput, -1, xFree); -} - -/* -** Collation sequence destructor function. The pCtx argument points to -** a UCollator structure previously allocated using ucol_open(). -*/ -static void icuCollationDel(void *pCtx){ - UCollator *p = (UCollator *)pCtx; - ucol_close(p); -} - -/* -** Collation sequence comparison function. The pCtx argument points to -** a UCollator structure previously allocated using ucol_open(). -*/ -static int icuCollationColl( - void *pCtx, - int nLeft, - const void *zLeft, - int nRight, - const void *zRight -){ - UCollationResult res; - UCollator *p = (UCollator *)pCtx; - res = ucol_strcoll(p, (UChar *)zLeft, nLeft/2, (UChar *)zRight, nRight/2); - switch( res ){ - case UCOL_LESS: return -1; - case UCOL_GREATER: return +1; - case UCOL_EQUAL: return 0; - } - assert(!"Unexpected return value from ucol_strcoll()"); - return 0; -} - -/* -** Implementation of the scalar function icu_load_collation(). -** -** This scalar function is used to add ICU collation based collation -** types to an SQLite database connection. It is intended to be called -** as follows: -** -** SELECT icu_load_collation(, ); -** -** Where is a string containing an ICU locale identifier (i.e. -** "en_AU", "tr_TR" etc.) and is the name of the -** collation sequence to create. -*/ -static void icuLoadCollation( - sqlite3_context *p, - int nArg, - sqlite3_value **apArg -){ - sqlite3 *db = (sqlite3 *)sqlite3_user_data(p); - UErrorCode status = U_ZERO_ERROR; - const char *zLocale; /* Locale identifier - (eg. "jp_JP") */ - const char *zName; /* SQL Collation sequence name (eg. "japanese") */ - UCollator *pUCollator; /* ICU library collation object */ - int rc; /* Return code from sqlite3_create_collation_x() */ - - assert(nArg==2); - zLocale = (const char *)sqlite3_value_text(apArg[0]); - zName = (const char *)sqlite3_value_text(apArg[1]); - - if( !zLocale || !zName ){ - return; - } - - pUCollator = ucol_open(zLocale, &status); - if( !U_SUCCESS(status) ){ - icuFunctionError(p, "ucol_open", status); - return; - } - assert(p); - - rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator, - icuCollationColl, icuCollationDel - ); - if( rc!=SQLITE_OK ){ - ucol_close(pUCollator); - sqlite3_result_error(p, "Error registering collation function", -1); - } -} - -/* -** Register the ICU extension functions with database db. -*/ -SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){ - struct IcuScalar { - const char *zName; /* Function name */ - int nArg; /* Number of arguments */ - int enc; /* Optimal text encoding */ - void *pContext; /* sqlite3_user_data() context */ - void (*xFunc)(sqlite3_context*,int,sqlite3_value**); - } scalars[] = { - {"regexp", 2, SQLITE_ANY, 0, icuRegexpFunc}, - - {"lower", 1, SQLITE_UTF16, 0, icuCaseFunc16}, - {"lower", 2, SQLITE_UTF16, 0, icuCaseFunc16}, - {"upper", 1, SQLITE_UTF16, (void*)1, icuCaseFunc16}, - {"upper", 2, SQLITE_UTF16, (void*)1, icuCaseFunc16}, - - {"lower", 1, SQLITE_UTF8, 0, icuCaseFunc16}, - {"lower", 2, SQLITE_UTF8, 0, icuCaseFunc16}, - {"upper", 1, SQLITE_UTF8, (void*)1, icuCaseFunc16}, - {"upper", 2, SQLITE_UTF8, (void*)1, icuCaseFunc16}, - - {"like", 2, SQLITE_UTF8, 0, icuLikeFunc}, - {"like", 3, SQLITE_UTF8, 0, icuLikeFunc}, - - {"icu_load_collation", 2, SQLITE_UTF8, (void*)db, icuLoadCollation}, - }; - - int rc = SQLITE_OK; - int i; - - for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){ - struct IcuScalar *p = &scalars[i]; - rc = sqlite3_create_function( - db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0 - ); - } - - return rc; -} - -#if !SQLITE_CORE -SQLITE_API int sqlite3_extension_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi) - return sqlite3IcuInit(db); -} -#endif - -#endif - -/************** End of icu.c *************************************************/ -/************** Begin file fts3_icu.c ****************************************/ -/* -** 2007 June 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file implements a tokenizer for fts3 based on the ICU library. -*/ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) -#ifdef SQLITE_ENABLE_ICU - -/* #include */ -/* #include */ - -#include -/* #include */ -/* #include */ -#include - -typedef struct IcuTokenizer IcuTokenizer; -typedef struct IcuCursor IcuCursor; - -struct IcuTokenizer { - sqlite3_tokenizer base; - char *zLocale; -}; - -struct IcuCursor { - sqlite3_tokenizer_cursor base; - - UBreakIterator *pIter; /* ICU break-iterator object */ - int nChar; /* Number of UChar elements in pInput */ - UChar *aChar; /* Copy of input using utf-16 encoding */ - int *aOffset; /* Offsets of each character in utf-8 input */ - - int nBuffer; - char *zBuffer; - - int iToken; -}; - -/* -** Create a new tokenizer instance. -*/ -static int icuCreate( - int argc, /* Number of entries in argv[] */ - const char * const *argv, /* Tokenizer creation arguments */ - sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */ -){ - IcuTokenizer *p; - int n = 0; - - if( argc>0 ){ - n = strlen(argv[0])+1; - } - p = (IcuTokenizer *)sqlite3_malloc(sizeof(IcuTokenizer)+n); - if( !p ){ - return SQLITE_NOMEM; - } - memset(p, 0, sizeof(IcuTokenizer)); - - if( n ){ - p->zLocale = (char *)&p[1]; - memcpy(p->zLocale, argv[0], n); - } - - *ppTokenizer = (sqlite3_tokenizer *)p; - - return SQLITE_OK; -} - -/* -** Destroy a tokenizer -*/ -static int icuDestroy(sqlite3_tokenizer *pTokenizer){ - IcuTokenizer *p = (IcuTokenizer *)pTokenizer; - sqlite3_free(p); - return SQLITE_OK; -} - -/* -** Prepare to begin tokenizing a particular string. The input -** string to be tokenized is pInput[0..nBytes-1]. A cursor -** used to incrementally tokenize this string is returned in -** *ppCursor. -*/ -static int icuOpen( - sqlite3_tokenizer *pTokenizer, /* The tokenizer */ - const char *zInput, /* Input string */ - int nInput, /* Length of zInput in bytes */ - sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */ -){ - IcuTokenizer *p = (IcuTokenizer *)pTokenizer; - IcuCursor *pCsr; - - const int32_t opt = U_FOLD_CASE_DEFAULT; - UErrorCode status = U_ZERO_ERROR; - int nChar; - - UChar32 c; - int iInput = 0; - int iOut = 0; - - *ppCursor = 0; - - if( zInput==0 ){ - nInput = 0; - zInput = ""; - }else if( nInput<0 ){ - nInput = strlen(zInput); - } - nChar = nInput+1; - pCsr = (IcuCursor *)sqlite3_malloc( - sizeof(IcuCursor) + /* IcuCursor */ - ((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */ - (nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */ - ); - if( !pCsr ){ - return SQLITE_NOMEM; - } - memset(pCsr, 0, sizeof(IcuCursor)); - pCsr->aChar = (UChar *)&pCsr[1]; - pCsr->aOffset = (int *)&pCsr->aChar[(nChar+3)&~3]; - - pCsr->aOffset[iOut] = iInput; - U8_NEXT(zInput, iInput, nInput, c); - while( c>0 ){ - int isError = 0; - c = u_foldCase(c, opt); - U16_APPEND(pCsr->aChar, iOut, nChar, c, isError); - if( isError ){ - sqlite3_free(pCsr); - return SQLITE_ERROR; - } - pCsr->aOffset[iOut] = iInput; - - if( iInputpIter = ubrk_open(UBRK_WORD, p->zLocale, pCsr->aChar, iOut, &status); - if( !U_SUCCESS(status) ){ - sqlite3_free(pCsr); - return SQLITE_ERROR; - } - pCsr->nChar = iOut; - - ubrk_first(pCsr->pIter); - *ppCursor = (sqlite3_tokenizer_cursor *)pCsr; - return SQLITE_OK; -} - -/* -** Close a tokenization cursor previously opened by a call to icuOpen(). -*/ -static int icuClose(sqlite3_tokenizer_cursor *pCursor){ - IcuCursor *pCsr = (IcuCursor *)pCursor; - ubrk_close(pCsr->pIter); - sqlite3_free(pCsr->zBuffer); - sqlite3_free(pCsr); - return SQLITE_OK; -} - -/* -** Extract the next token from a tokenization cursor. -*/ -static int icuNext( - sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */ - const char **ppToken, /* OUT: *ppToken is the token text */ - int *pnBytes, /* OUT: Number of bytes in token */ - int *piStartOffset, /* OUT: Starting offset of token */ - int *piEndOffset, /* OUT: Ending offset of token */ - int *piPosition /* OUT: Position integer of token */ -){ - IcuCursor *pCsr = (IcuCursor *)pCursor; - - int iStart = 0; - int iEnd = 0; - int nByte = 0; - - while( iStart==iEnd ){ - UChar32 c; - - iStart = ubrk_current(pCsr->pIter); - iEnd = ubrk_next(pCsr->pIter); - if( iEnd==UBRK_DONE ){ - return SQLITE_DONE; - } - - while( iStartaChar, iWhite, pCsr->nChar, c); - if( u_isspace(c) ){ - iStart = iWhite; - }else{ - break; - } - } - assert(iStart<=iEnd); - } - - do { - UErrorCode status = U_ZERO_ERROR; - if( nByte ){ - char *zNew = sqlite3_realloc(pCsr->zBuffer, nByte); - if( !zNew ){ - return SQLITE_NOMEM; - } - pCsr->zBuffer = zNew; - pCsr->nBuffer = nByte; - } - - u_strToUTF8( - pCsr->zBuffer, pCsr->nBuffer, &nByte, /* Output vars */ - &pCsr->aChar[iStart], iEnd-iStart, /* Input vars */ - &status /* Output success/failure */ - ); - } while( nByte>pCsr->nBuffer ); - - *ppToken = pCsr->zBuffer; - *pnBytes = nByte; - *piStartOffset = pCsr->aOffset[iStart]; - *piEndOffset = pCsr->aOffset[iEnd]; - *piPosition = pCsr->iToken++; - - return SQLITE_OK; -} - -/* -** The set of routines that implement the simple tokenizer -*/ -static const sqlite3_tokenizer_module icuTokenizerModule = { - 0, /* iVersion */ - icuCreate, /* xCreate */ - icuDestroy, /* xCreate */ - icuOpen, /* xOpen */ - icuClose, /* xClose */ - icuNext, /* xNext */ -}; - -/* -** Set *ppModule to point at the implementation of the ICU tokenizer. -*/ -SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule( - sqlite3_tokenizer_module const**ppModule -){ - *ppModule = &icuTokenizerModule; -} - -#endif /* defined(SQLITE_ENABLE_ICU) */ -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - -/************** End of fts3_icu.c ********************************************/ diff --git a/src/SQLite/sqlite3.h b/src/SQLite/sqlite3.h deleted file mode 100644 index 1332eb162..000000000 --- a/src/SQLite/sqlite3.h +++ /dev/null @@ -1,7174 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the interface that the SQLite library -** presents to client programs. If a C-function, structure, datatype, -** or constant definition does not appear in this file, then it is -** not a published API of SQLite, is subject to change without -** notice, and should not be referenced by programs that use SQLite. -** -** Some of the definitions that are in this file are marked as -** "experimental". Experimental interfaces are normally new -** features recently added to SQLite. We do not anticipate changes -** to experimental interfaces but reserve the right to make minor changes -** if experience from use "in the wild" suggest such changes are prudent. -** -** The official C-language API documentation for SQLite is derived -** from comments in this file. This file is the authoritative source -** on how SQLite interfaces are suppose to operate. -** -** The name of this file under configuration management is "sqlite.h.in". -** The makefile makes some minor changes to this file (such as inserting -** the version number) and changes its name to "sqlite3.h" as -** part of the build process. -*/ -#ifndef _SQLITE3_H_ -#define _SQLITE3_H_ -#include /* Needed for the definition of va_list */ - -/* -** Make sure we can call this stuff from C++. -*/ -#ifdef __cplusplus -extern "C" { -#endif - - -/* -** Add the ability to override 'extern' -*/ -#ifndef SQLITE_EXTERN -# define SQLITE_EXTERN extern -#endif - -#ifndef SQLITE_API -# define SQLITE_API -#endif - - -/* -** These no-op macros are used in front of interfaces to mark those -** interfaces as either deprecated or experimental. New applications -** should not use deprecated interfaces - they are support for backwards -** compatibility only. Application writers should be aware that -** experimental interfaces are subject to change in point releases. -** -** These macros used to resolve to various kinds of compiler magic that -** would generate warning messages when they were used. But that -** compiler magic ended up generating such a flurry of bug reports -** that we have taken it all out and gone back to using simple -** noop macros. -*/ -#define SQLITE_DEPRECATED -#define SQLITE_EXPERIMENTAL - -/* -** Ensure these symbols were not defined by some previous header file. -*/ -#ifdef SQLITE_VERSION -# undef SQLITE_VERSION -#endif -#ifdef SQLITE_VERSION_NUMBER -# undef SQLITE_VERSION_NUMBER -#endif - -/* -** CAPI3REF: Compile-Time Library Version Numbers -** -** ^(The [SQLITE_VERSION] C preprocessor macro in the sqlite3.h header -** evaluates to a string literal that is the SQLite version in the -** format "X.Y.Z" where X is the major version number (always 3 for -** SQLite3) and Y is the minor version number and Z is the release number.)^ -** ^(The [SQLITE_VERSION_NUMBER] C preprocessor macro resolves to an integer -** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same -** numbers used in [SQLITE_VERSION].)^ -** The SQLITE_VERSION_NUMBER for any given release of SQLite will also -** be larger than the release from which it is derived. Either Y will -** be held constant and Z will be incremented or else Y will be incremented -** and Z will be reset to zero. -** -** Since version 3.6.18, SQLite source code has been stored in the -** Fossil configuration management -** system. ^The SQLITE_SOURCE_ID macro evaluates to -** a string which identifies a particular check-in of SQLite -** within its configuration management system. ^The SQLITE_SOURCE_ID -** string contains the date and time of the check-in (UTC) and an SHA1 -** hash of the entire source tree. -** -** See also: [sqlite3_libversion()], -** [sqlite3_libversion_number()], [sqlite3_sourceid()], -** [sqlite_version()] and [sqlite_source_id()]. -*/ -#define SQLITE_VERSION "3.7.16.1" -#define SQLITE_VERSION_NUMBER 3007016 -#define SQLITE_SOURCE_ID "2013-03-29 13:44:34 527231bc67285f01fb18d4451b28f61da3c4e39d" - -/* -** CAPI3REF: Run-Time Library Version Numbers -** KEYWORDS: sqlite3_version, sqlite3_sourceid -** -** These interfaces provide the same information as the [SQLITE_VERSION], -** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros -** but are associated with the library instead of the header file. ^(Cautious -** programmers might include assert() statements in their application to -** verify that values returned by these interfaces match the macros in -** the header, and thus insure that the application is -** compiled with matching library and header files. -** -**
    -** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
    -** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );
    -** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
    -** 
    )^ -** -** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION] -** macro. ^The sqlite3_libversion() function returns a pointer to the -** to the sqlite3_version[] string constant. The sqlite3_libversion() -** function is provided for use in DLLs since DLL users usually do not have -** direct access to string constants within the DLL. ^The -** sqlite3_libversion_number() function returns an integer equal to -** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns -** a pointer to a string constant whose value is the same as the -** [SQLITE_SOURCE_ID] C preprocessor macro. -** -** See also: [sqlite_version()] and [sqlite_source_id()]. -*/ -SQLITE_API SQLITE_EXTERN const char sqlite3_version[]; -SQLITE_API const char *sqlite3_libversion(void); -SQLITE_API const char *sqlite3_sourceid(void); -SQLITE_API int sqlite3_libversion_number(void); - -/* -** CAPI3REF: Run-Time Library Compilation Options Diagnostics -** -** ^The sqlite3_compileoption_used() function returns 0 or 1 -** indicating whether the specified option was defined at -** compile time. ^The SQLITE_ prefix may be omitted from the -** option name passed to sqlite3_compileoption_used(). -** -** ^The sqlite3_compileoption_get() function allows iterating -** over the list of options that were defined at compile time by -** returning the N-th compile time option string. ^If N is out of range, -** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ -** prefix is omitted from any strings returned by -** sqlite3_compileoption_get(). -** -** ^Support for the diagnostic functions sqlite3_compileoption_used() -** and sqlite3_compileoption_get() may be omitted by specifying the -** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. -** -** See also: SQL functions [sqlite_compileoption_used()] and -** [sqlite_compileoption_get()] and the [compile_options pragma]. -*/ -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS -SQLITE_API int sqlite3_compileoption_used(const char *zOptName); -SQLITE_API const char *sqlite3_compileoption_get(int N); -#endif - -/* -** CAPI3REF: Test To See If The Library Is Threadsafe -** -** ^The sqlite3_threadsafe() function returns zero if and only if -** SQLite was compiled with mutexing code omitted due to the -** [SQLITE_THREADSAFE] compile-time option being set to 0. -** -** SQLite can be compiled with or without mutexes. When -** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes -** are enabled and SQLite is threadsafe. When the -** [SQLITE_THREADSAFE] macro is 0, -** the mutexes are omitted. Without the mutexes, it is not safe -** to use SQLite concurrently from more than one thread. -** -** Enabling mutexes incurs a measurable performance penalty. -** So if speed is of utmost importance, it makes sense to disable -** the mutexes. But for maximum safety, mutexes should be enabled. -** ^The default behavior is for mutexes to be enabled. -** -** This interface can be used by an application to make sure that the -** version of SQLite that it is linking against was compiled with -** the desired setting of the [SQLITE_THREADSAFE] macro. -** -** This interface only reports on the compile-time mutex setting -** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with -** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but -** can be fully or partially disabled using a call to [sqlite3_config()] -** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], -** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the -** sqlite3_threadsafe() function shows only the compile-time setting of -** thread safety, not any run-time changes to that setting made by -** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() -** is unchanged by calls to sqlite3_config().)^ -** -** See the [threading mode] documentation for additional information. -*/ -SQLITE_API int sqlite3_threadsafe(void); - -/* -** CAPI3REF: Database Connection Handle -** KEYWORDS: {database connection} {database connections} -** -** Each open SQLite database is represented by a pointer to an instance of -** the opaque structure named "sqlite3". It is useful to think of an sqlite3 -** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and -** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()] -** and [sqlite3_close_v2()] are its destructors. There are many other -** interfaces (such as -** [sqlite3_prepare_v2()], [sqlite3_create_function()], and -** [sqlite3_busy_timeout()] to name but three) that are methods on an -** sqlite3 object. -*/ -typedef struct sqlite3 sqlite3; - -/* -** CAPI3REF: 64-Bit Integer Types -** KEYWORDS: sqlite_int64 sqlite_uint64 -** -** Because there is no cross-platform way to specify 64-bit integer types -** SQLite includes typedefs for 64-bit signed and unsigned integers. -** -** The sqlite3_int64 and sqlite3_uint64 are the preferred type definitions. -** The sqlite_int64 and sqlite_uint64 types are supported for backwards -** compatibility only. -** -** ^The sqlite3_int64 and sqlite_int64 types can store integer values -** between -9223372036854775808 and +9223372036854775807 inclusive. ^The -** sqlite3_uint64 and sqlite_uint64 types can store integer values -** between 0 and +18446744073709551615 inclusive. -*/ -#ifdef SQLITE_INT64_TYPE - typedef SQLITE_INT64_TYPE sqlite_int64; - typedef unsigned SQLITE_INT64_TYPE sqlite_uint64; -#elif defined(_MSC_VER) || defined(__BORLANDC__) - typedef __int64 sqlite_int64; - typedef unsigned __int64 sqlite_uint64; -#else - typedef long long int sqlite_int64; - typedef unsigned long long int sqlite_uint64; -#endif -typedef sqlite_int64 sqlite3_int64; -typedef sqlite_uint64 sqlite3_uint64; - -/* -** If compiling for a processor that lacks floating point support, -** substitute integer for floating-point. -*/ -#ifdef SQLITE_OMIT_FLOATING_POINT -# define double sqlite3_int64 -#endif - -/* -** CAPI3REF: Closing A Database Connection -** -** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors -** for the [sqlite3] object. -** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if -** the [sqlite3] object is successfully destroyed and all associated -** resources are deallocated. -** -** ^If the database connection is associated with unfinalized prepared -** statements or unfinished sqlite3_backup objects then sqlite3_close() -** will leave the database connection open and return [SQLITE_BUSY]. -** ^If sqlite3_close_v2() is called with unfinalized prepared statements -** and unfinished sqlite3_backups, then the database connection becomes -** an unusable "zombie" which will automatically be deallocated when the -** last prepared statement is finalized or the last sqlite3_backup is -** finished. The sqlite3_close_v2() interface is intended for use with -** host languages that are garbage collected, and where the order in which -** destructors are called is arbitrary. -** -** Applications should [sqlite3_finalize | finalize] all [prepared statements], -** [sqlite3_blob_close | close] all [BLOB handles], and -** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated -** with the [sqlite3] object prior to attempting to close the object. ^If -** sqlite3_close_v2() is called on a [database connection] that still has -** outstanding [prepared statements], [BLOB handles], and/or -** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation -** of resources is deferred until all [prepared statements], [BLOB handles], -** and [sqlite3_backup] objects are also destroyed. -** -** ^If an [sqlite3] object is destroyed while a transaction is open, -** the transaction is automatically rolled back. -** -** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] -** must be either a NULL -** pointer or an [sqlite3] object pointer obtained -** from [sqlite3_open()], [sqlite3_open16()], or -** [sqlite3_open_v2()], and not previously closed. -** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer -** argument is a harmless no-op. -*/ -SQLITE_API int sqlite3_close(sqlite3*); -SQLITE_API int sqlite3_close_v2(sqlite3*); - -/* -** The type for a callback function. -** This is legacy and deprecated. It is included for historical -** compatibility and is not documented. -*/ -typedef int (*sqlite3_callback)(void*,int,char**, char**); - -/* -** CAPI3REF: One-Step Query Execution Interface -** -** The sqlite3_exec() interface is a convenience wrapper around -** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], -** that allows an application to run multiple statements of SQL -** without having to use a lot of C code. -** -** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, -** semicolon-separate SQL statements passed into its 2nd argument, -** in the context of the [database connection] passed in as its 1st -** argument. ^If the callback function of the 3rd argument to -** sqlite3_exec() is not NULL, then it is invoked for each result row -** coming out of the evaluated SQL statements. ^The 4th argument to -** sqlite3_exec() is relayed through to the 1st argument of each -** callback invocation. ^If the callback pointer to sqlite3_exec() -** is NULL, then no callback is ever invoked and result rows are -** ignored. -** -** ^If an error occurs while evaluating the SQL statements passed into -** sqlite3_exec(), then execution of the current statement stops and -** subsequent statements are skipped. ^If the 5th parameter to sqlite3_exec() -** is not NULL then any error message is written into memory obtained -** from [sqlite3_malloc()] and passed back through the 5th parameter. -** To avoid memory leaks, the application should invoke [sqlite3_free()] -** on error message strings returned through the 5th parameter of -** of sqlite3_exec() after the error message string is no longer needed. -** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors -** occur, then sqlite3_exec() sets the pointer in its 5th parameter to -** NULL before returning. -** -** ^If an sqlite3_exec() callback returns non-zero, the sqlite3_exec() -** routine returns SQLITE_ABORT without invoking the callback again and -** without running any subsequent SQL statements. -** -** ^The 2nd argument to the sqlite3_exec() callback function is the -** number of columns in the result. ^The 3rd argument to the sqlite3_exec() -** callback is an array of pointers to strings obtained as if from -** [sqlite3_column_text()], one for each column. ^If an element of a -** result row is NULL then the corresponding string pointer for the -** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the -** sqlite3_exec() callback is an array of pointers to strings where each -** entry represents the name of corresponding result column as obtained -** from [sqlite3_column_name()]. -** -** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer -** to an empty string, or a pointer that contains only whitespace and/or -** SQL comments, then no SQL statements are evaluated and the database -** is not changed. -** -** Restrictions: -** -**
      -**
    • The application must insure that the 1st parameter to sqlite3_exec() -** is a valid and open [database connection]. -**
    • The application must not close [database connection] specified by -** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. -**
    • The application must not modify the SQL statement text passed into -** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. -**
    -*/ -SQLITE_API int sqlite3_exec( - sqlite3*, /* An open database */ - const char *sql, /* SQL to be evaluated */ - int (*callback)(void*,int,char**,char**), /* Callback function */ - void *, /* 1st argument to callback */ - char **errmsg /* Error msg written here */ -); - -/* -** CAPI3REF: Result Codes -** KEYWORDS: SQLITE_OK {error code} {error codes} -** KEYWORDS: {result code} {result codes} -** -** Many SQLite functions return an integer result code from the set shown -** here in order to indicate success or failure. -** -** New error codes may be added in future versions of SQLite. -** -** See also: [SQLITE_IOERR_READ | extended result codes], -** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes]. -*/ -#define SQLITE_OK 0 /* Successful result */ -/* beginning-of-error-codes */ -#define SQLITE_ERROR 1 /* SQL error or missing database */ -#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ -#define SQLITE_PERM 3 /* Access permission denied */ -#define SQLITE_ABORT 4 /* Callback routine requested an abort */ -#define SQLITE_BUSY 5 /* The database file is locked */ -#define SQLITE_LOCKED 6 /* A table in the database is locked */ -#define SQLITE_NOMEM 7 /* A malloc() failed */ -#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ -#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ -#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ -#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ -#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ -#define SQLITE_FULL 13 /* Insertion failed because database is full */ -#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ -#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ -#define SQLITE_EMPTY 16 /* Database is empty */ -#define SQLITE_SCHEMA 17 /* The database schema changed */ -#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ -#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ -#define SQLITE_MISMATCH 20 /* Data type mismatch */ -#define SQLITE_MISUSE 21 /* Library used incorrectly */ -#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ -#define SQLITE_AUTH 23 /* Authorization denied */ -#define SQLITE_FORMAT 24 /* Auxiliary database format error */ -#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ -#define SQLITE_NOTADB 26 /* File opened that is not a database file */ -#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ -#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ -/* end-of-error-codes */ - -/* -** CAPI3REF: Extended Result Codes -** KEYWORDS: {extended error code} {extended error codes} -** KEYWORDS: {extended result code} {extended result codes} -** -** In its default configuration, SQLite API routines return one of 26 integer -** [SQLITE_OK | result codes]. However, experience has shown that many of -** these result codes are too coarse-grained. They do not provide as -** much information about problems as programmers might like. In an effort to -** address this, newer versions of SQLite (version 3.3.8 and later) include -** support for additional result codes that provide more detailed information -** about errors. The extended result codes are enabled or disabled -** on a per database connection basis using the -** [sqlite3_extended_result_codes()] API. -** -** Some of the available extended result codes are listed here. -** One may expect the number of extended result codes will be expand -** over time. Software that uses extended result codes should expect -** to see new result codes in future releases of SQLite. -** -** The SQLITE_OK result code will never be extended. It will always -** be exactly zero. -*/ -#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) -#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) -#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) -#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) -#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) -#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) -#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) -#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8)) -#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8)) -#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8)) -#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8)) -#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8)) -#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) -#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) -#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) -#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) -#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) -#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) -#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) -#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) -#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) -#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) -#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) -#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) -#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) -#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) -#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) -#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) -#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) -#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) -#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) -#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) -#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) -#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8)) -#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8)) -#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8)) -#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8)) -#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) -#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) -#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) -#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) -#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) - -/* -** CAPI3REF: Flags For File Open Operations -** -** These bit values are intended for use in the -** 3rd parameter to the [sqlite3_open_v2()] interface and -** in the 4th parameter to the [sqlite3_vfs.xOpen] method. -*/ -#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ -#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ -#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */ -#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_MEMORY 0x00000080 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */ -#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */ -#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */ -#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800 /* VFS only */ -#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */ -#define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */ -#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ -#define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ -#define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ - -/* Reserved: 0x00F00000 */ - -/* -** CAPI3REF: Device Characteristics -** -** The xDeviceCharacteristics method of the [sqlite3_io_methods] -** object returns an integer which is a vector of these -** bit values expressing I/O characteristics of the mass storage -** device that holds the file that the [sqlite3_io_methods] -** refers to. -** -** The SQLITE_IOCAP_ATOMIC property means that all writes of -** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values -** mean that writes of blocks that are nnn bytes in size and -** are aligned to an address which is an integer multiple of -** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means -** that when data is appended to a file, the data is appended -** first then the size of the file is extended, never the other -** way around. The SQLITE_IOCAP_SEQUENTIAL property means that -** information is written to disk in the same order as calls -** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that -** after reboot following a crash or power loss, the only bytes in a -** file that were written at the application level might have changed -** and that adjacent bytes, even bytes within the same sector are -** guaranteed to be unchanged. -*/ -#define SQLITE_IOCAP_ATOMIC 0x00000001 -#define SQLITE_IOCAP_ATOMIC512 0x00000002 -#define SQLITE_IOCAP_ATOMIC1K 0x00000004 -#define SQLITE_IOCAP_ATOMIC2K 0x00000008 -#define SQLITE_IOCAP_ATOMIC4K 0x00000010 -#define SQLITE_IOCAP_ATOMIC8K 0x00000020 -#define SQLITE_IOCAP_ATOMIC16K 0x00000040 -#define SQLITE_IOCAP_ATOMIC32K 0x00000080 -#define SQLITE_IOCAP_ATOMIC64K 0x00000100 -#define SQLITE_IOCAP_SAFE_APPEND 0x00000200 -#define SQLITE_IOCAP_SEQUENTIAL 0x00000400 -#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 -#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 - -/* -** CAPI3REF: File Locking Levels -** -** SQLite uses one of these integer values as the second -** argument to calls it makes to the xLock() and xUnlock() methods -** of an [sqlite3_io_methods] object. -*/ -#define SQLITE_LOCK_NONE 0 -#define SQLITE_LOCK_SHARED 1 -#define SQLITE_LOCK_RESERVED 2 -#define SQLITE_LOCK_PENDING 3 -#define SQLITE_LOCK_EXCLUSIVE 4 - -/* -** CAPI3REF: Synchronization Type Flags -** -** When SQLite invokes the xSync() method of an -** [sqlite3_io_methods] object it uses a combination of -** these integer values as the second argument. -** -** When the SQLITE_SYNC_DATAONLY flag is used, it means that the -** sync operation only needs to flush data to mass storage. Inode -** information need not be flushed. If the lower four bits of the flag -** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics. -** If the lower four bits equal SQLITE_SYNC_FULL, that means -** to use Mac OS X style fullsync instead of fsync(). -** -** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags -** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL -** settings. The [synchronous pragma] determines when calls to the -** xSync VFS method occur and applies uniformly across all platforms. -** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how -** energetic or rigorous or forceful the sync operations are and -** only make a difference on Mac OSX for the default SQLite code. -** (Third-party VFS implementations might also make the distinction -** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the -** operating systems natively supported by SQLite, only Mac OSX -** cares about the difference.) -*/ -#define SQLITE_SYNC_NORMAL 0x00002 -#define SQLITE_SYNC_FULL 0x00003 -#define SQLITE_SYNC_DATAONLY 0x00010 - -/* -** CAPI3REF: OS Interface Open File Handle -** -** An [sqlite3_file] object represents an open file in the -** [sqlite3_vfs | OS interface layer]. Individual OS interface -** implementations will -** want to subclass this object by appending additional fields -** for their own use. The pMethods entry is a pointer to an -** [sqlite3_io_methods] object that defines methods for performing -** I/O operations on the open file. -*/ -typedef struct sqlite3_file sqlite3_file; -struct sqlite3_file { - const struct sqlite3_io_methods *pMethods; /* Methods for an open file */ -}; - -/* -** CAPI3REF: OS Interface File Virtual Methods Object -** -** Every file opened by the [sqlite3_vfs.xOpen] method populates an -** [sqlite3_file] object (or, more commonly, a subclass of the -** [sqlite3_file] object) with a pointer to an instance of this object. -** This object defines the methods used to perform various operations -** against the open file represented by the [sqlite3_file] object. -** -** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element -** to a non-NULL pointer, then the sqlite3_io_methods.xClose method -** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The -** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen] -** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element -** to NULL. -** -** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or -** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). -** The second choice is a Mac OS X style fullsync. The [SQLITE_SYNC_DATAONLY] -** flag may be ORed in to indicate that only the data of the file -** and not its inode needs to be synced. -** -** The integer values to xLock() and xUnlock() are one of -**
      -**
    • [SQLITE_LOCK_NONE], -**
    • [SQLITE_LOCK_SHARED], -**
    • [SQLITE_LOCK_RESERVED], -**
    • [SQLITE_LOCK_PENDING], or -**
    • [SQLITE_LOCK_EXCLUSIVE]. -**
    -** xLock() increases the lock. xUnlock() decreases the lock. -** The xCheckReservedLock() method checks whether any database connection, -** either in this process or in some other process, is holding a RESERVED, -** PENDING, or EXCLUSIVE lock on the file. It returns true -** if such a lock exists and false otherwise. -** -** The xFileControl() method is a generic interface that allows custom -** VFS implementations to directly control an open file using the -** [sqlite3_file_control()] interface. The second "op" argument is an -** integer opcode. The third argument is a generic pointer intended to -** point to a structure that may contain arguments or space in which to -** write return values. Potential uses for xFileControl() might be -** functions to enable blocking locks with timeouts, to change the -** locking strategy (for example to use dot-file locks), to inquire -** about the status of a lock, or to break stale locks. The SQLite -** core reserves all opcodes less than 100 for its own use. -** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available. -** Applications that define a custom xFileControl method should use opcodes -** greater than 100 to avoid conflicts. VFS implementations should -** return [SQLITE_NOTFOUND] for file control opcodes that they do not -** recognize. -** -** The xSectorSize() method returns the sector size of the -** device that underlies the file. The sector size is the -** minimum write that can be performed without disturbing -** other bytes in the file. The xDeviceCharacteristics() -** method returns a bit vector describing behaviors of the -** underlying device: -** -**
      -**
    • [SQLITE_IOCAP_ATOMIC] -**
    • [SQLITE_IOCAP_ATOMIC512] -**
    • [SQLITE_IOCAP_ATOMIC1K] -**
    • [SQLITE_IOCAP_ATOMIC2K] -**
    • [SQLITE_IOCAP_ATOMIC4K] -**
    • [SQLITE_IOCAP_ATOMIC8K] -**
    • [SQLITE_IOCAP_ATOMIC16K] -**
    • [SQLITE_IOCAP_ATOMIC32K] -**
    • [SQLITE_IOCAP_ATOMIC64K] -**
    • [SQLITE_IOCAP_SAFE_APPEND] -**
    • [SQLITE_IOCAP_SEQUENTIAL] -**
    -** -** The SQLITE_IOCAP_ATOMIC property means that all writes of -** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values -** mean that writes of blocks that are nnn bytes in size and -** are aligned to an address which is an integer multiple of -** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means -** that when data is appended to a file, the data is appended -** first then the size of the file is extended, never the other -** way around. The SQLITE_IOCAP_SEQUENTIAL property means that -** information is written to disk in the same order as calls -** to xWrite(). -** -** If xRead() returns SQLITE_IOERR_SHORT_READ it must also fill -** in the unread portions of the buffer with zeros. A VFS that -** fails to zero-fill short reads might seem to work. However, -** failure to zero-fill short reads will eventually lead to -** database corruption. -*/ -typedef struct sqlite3_io_methods sqlite3_io_methods; -struct sqlite3_io_methods { - int iVersion; - int (*xClose)(sqlite3_file*); - int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); - int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst); - int (*xTruncate)(sqlite3_file*, sqlite3_int64 size); - int (*xSync)(sqlite3_file*, int flags); - int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize); - int (*xLock)(sqlite3_file*, int); - int (*xUnlock)(sqlite3_file*, int); - int (*xCheckReservedLock)(sqlite3_file*, int *pResOut); - int (*xFileControl)(sqlite3_file*, int op, void *pArg); - int (*xSectorSize)(sqlite3_file*); - int (*xDeviceCharacteristics)(sqlite3_file*); - /* Methods above are valid for version 1 */ - int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**); - int (*xShmLock)(sqlite3_file*, int offset, int n, int flags); - void (*xShmBarrier)(sqlite3_file*); - int (*xShmUnmap)(sqlite3_file*, int deleteFlag); - /* Methods above are valid for version 2 */ - /* Additional methods may be added in future releases */ -}; - -/* -** CAPI3REF: Standard File Control Opcodes -** -** These integer constants are opcodes for the xFileControl method -** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] -** interface. -** -** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This -** opcode causes the xFileControl method to write the current state of -** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], -** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) -** into an integer that the pArg argument points to. This capability -** is used during testing and only needs to be supported when SQLITE_TEST -** is defined. -**
      -**
    • [[SQLITE_FCNTL_SIZE_HINT]] -** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS -** layer a hint of how large the database file will grow to be during the -** current transaction. This hint is not guaranteed to be accurate but it -** is often close. The underlying VFS might choose to preallocate database -** file space based on this hint in order to help writes to the database -** file run faster. -** -**
    • [[SQLITE_FCNTL_CHUNK_SIZE]] -** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS -** extends and truncates the database file in chunks of a size specified -** by the user. The fourth argument to [sqlite3_file_control()] should -** point to an integer (type int) containing the new chunk-size to use -** for the nominated database. Allocating database file space in large -** chunks (say 1MB at a time), may reduce file-system fragmentation and -** improve performance on some systems. -** -**
    • [[SQLITE_FCNTL_FILE_POINTER]] -** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer -** to the [sqlite3_file] object associated with a particular database -** connection. See the [sqlite3_file_control()] documentation for -** additional information. -** -**
    • [[SQLITE_FCNTL_SYNC_OMITTED]] -** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by -** SQLite and sent to all VFSes in place of a call to the xSync method -** when the database connection has [PRAGMA synchronous] set to OFF.)^ -** Some specialized VFSes need this signal in order to operate correctly -** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most -** VFSes do not need this signal and should silently ignore this opcode. -** Applications should not call [sqlite3_file_control()] with this -** opcode as doing so may disrupt the operation of the specialized VFSes -** that do require it. -** -**
    • [[SQLITE_FCNTL_WIN32_AV_RETRY]] -** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic -** retry counts and intervals for certain disk I/O operations for the -** windows [VFS] in order to provide robustness in the presence of -** anti-virus programs. By default, the windows VFS will retry file read, -** file write, and file delete operations up to 10 times, with a delay -** of 25 milliseconds before the first retry and with the delay increasing -** by an additional 25 milliseconds with each subsequent retry. This -** opcode allows these two values (10 retries and 25 milliseconds of delay) -** to be adjusted. The values are changed for all database connections -** within the same process. The argument is a pointer to an array of two -** integers where the first integer i the new retry count and the second -** integer is the delay. If either integer is negative, then the setting -** is not changed but instead the prior value of that setting is written -** into the array entry, allowing the current retry settings to be -** interrogated. The zDbName parameter is ignored. -** -**
    • [[SQLITE_FCNTL_PERSIST_WAL]] -** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the -** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary -** write ahead log and shared memory files used for transaction control -** are automatically deleted when the latest connection to the database -** closes. Setting persistent WAL mode causes those files to persist after -** close. Persisting the files is useful when other processes that do not -** have write permission on the directory containing the database file want -** to read the database file, as the WAL and shared memory files must exist -** in order for the database to be readable. The fourth parameter to -** [sqlite3_file_control()] for this opcode should be a pointer to an integer. -** That integer is 0 to disable persistent WAL mode or 1 to enable persistent -** WAL mode. If the integer is -1, then it is overwritten with the current -** WAL persistence setting. -** -**
    • [[SQLITE_FCNTL_POWERSAFE_OVERWRITE]] -** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the -** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting -** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the -** xDeviceCharacteristics methods. The fourth parameter to -** [sqlite3_file_control()] for this opcode should be a pointer to an integer. -** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage -** mode. If the integer is -1, then it is overwritten with the current -** zero-damage mode setting. -** -**
    • [[SQLITE_FCNTL_OVERWRITE]] -** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening -** a write transaction to indicate that, unless it is rolled back for some -** reason, the entire database file will be overwritten by the current -** transaction. This is used by VACUUM operations. -** -**
    • [[SQLITE_FCNTL_VFSNAME]] -** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of -** all [VFSes] in the VFS stack. The names are of all VFS shims and the -** final bottom-level VFS are written into memory obtained from -** [sqlite3_malloc()] and the result is stored in the char* variable -** that the fourth parameter of [sqlite3_file_control()] points to. -** The caller is responsible for freeing the memory when done. As with -** all file-control actions, there is no guarantee that this will actually -** do anything. Callers should initialize the char* variable to a NULL -** pointer in case this file-control is not implemented. This file-control -** is intended for diagnostic use only. -** -**
    • [[SQLITE_FCNTL_PRAGMA]] -** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA] -** file control is sent to the open [sqlite3_file] object corresponding -** to the database file to which the pragma statement refers. ^The argument -** to the [SQLITE_FCNTL_PRAGMA] file control is an array of -** pointers to strings (char**) in which the second element of the array -** is the name of the pragma and the third element is the argument to the -** pragma or NULL if the pragma has no argument. ^The handler for an -** [SQLITE_FCNTL_PRAGMA] file control can optionally make the first element -** of the char** argument point to a string obtained from [sqlite3_mprintf()] -** or the equivalent and that string will become the result of the pragma or -** the error message if the pragma fails. ^If the -** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal -** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA] -** file control returns [SQLITE_OK], then the parser assumes that the -** VFS has handled the PRAGMA itself and the parser generates a no-op -** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns -** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means -** that the VFS encountered an error while handling the [PRAGMA] and the -** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] -** file control occurs at the beginning of pragma statement analysis and so -** it is able to override built-in [PRAGMA] statements. -** -**
    • [[SQLITE_FCNTL_BUSYHANDLER]] -** ^This file-control may be invoked by SQLite on the database file handle -** shortly after it is opened in order to provide a custom VFS with access -** to the connections busy-handler callback. The argument is of type (void **) -** - an array of two (void *) values. The first (void *) actually points -** to a function of type (int (*)(void *)). In order to invoke the connections -** busy-handler, this function should be invoked with the second (void *) in -** the array as the only argument. If it returns non-zero, then the operation -** should be retried. If it returns zero, the custom VFS should abandon the -** current operation. -** -**
    • [[SQLITE_FCNTL_TEMPFILENAME]] -** ^Application can invoke this file-control to have SQLite generate a -** temporary filename using the same algorithm that is followed to generate -** temporary filenames for TEMP tables and other internal uses. The -** argument should be a char** which will be filled with the filename -** written into memory obtained from [sqlite3_malloc()]. The caller should -** invoke [sqlite3_free()] on the result to avoid a memory leak. -** -**
    -*/ -#define SQLITE_FCNTL_LOCKSTATE 1 -#define SQLITE_GET_LOCKPROXYFILE 2 -#define SQLITE_SET_LOCKPROXYFILE 3 -#define SQLITE_LAST_ERRNO 4 -#define SQLITE_FCNTL_SIZE_HINT 5 -#define SQLITE_FCNTL_CHUNK_SIZE 6 -#define SQLITE_FCNTL_FILE_POINTER 7 -#define SQLITE_FCNTL_SYNC_OMITTED 8 -#define SQLITE_FCNTL_WIN32_AV_RETRY 9 -#define SQLITE_FCNTL_PERSIST_WAL 10 -#define SQLITE_FCNTL_OVERWRITE 11 -#define SQLITE_FCNTL_VFSNAME 12 -#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 -#define SQLITE_FCNTL_PRAGMA 14 -#define SQLITE_FCNTL_BUSYHANDLER 15 -#define SQLITE_FCNTL_TEMPFILENAME 16 - -/* -** CAPI3REF: Mutex Handle -** -** The mutex module within SQLite defines [sqlite3_mutex] to be an -** abstract type for a mutex object. The SQLite core never looks -** at the internal representation of an [sqlite3_mutex]. It only -** deals with pointers to the [sqlite3_mutex] object. -** -** Mutexes are created using [sqlite3_mutex_alloc()]. -*/ -typedef struct sqlite3_mutex sqlite3_mutex; - -/* -** CAPI3REF: OS Interface Object -** -** An instance of the sqlite3_vfs object defines the interface between -** the SQLite core and the underlying operating system. The "vfs" -** in the name of the object stands for "virtual file system". See -** the [VFS | VFS documentation] for further information. -** -** The value of the iVersion field is initially 1 but may be larger in -** future versions of SQLite. Additional fields may be appended to this -** object when the iVersion value is increased. Note that the structure -** of the sqlite3_vfs object changes in the transaction between -** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not -** modified. -** -** The szOsFile field is the size of the subclassed [sqlite3_file] -** structure used by this VFS. mxPathname is the maximum length of -** a pathname in this VFS. -** -** Registered sqlite3_vfs objects are kept on a linked list formed by -** the pNext pointer. The [sqlite3_vfs_register()] -** and [sqlite3_vfs_unregister()] interfaces manage this list -** in a thread-safe way. The [sqlite3_vfs_find()] interface -** searches the list. Neither the application code nor the VFS -** implementation should use the pNext pointer. -** -** The pNext field is the only field in the sqlite3_vfs -** structure that SQLite will ever modify. SQLite will only access -** or modify this field while holding a particular static mutex. -** The application should never modify anything within the sqlite3_vfs -** object once the object has been registered. -** -** The zName field holds the name of the VFS module. The name must -** be unique across all VFS modules. -** -** [[sqlite3_vfs.xOpen]] -** ^SQLite guarantees that the zFilename parameter to xOpen -** is either a NULL pointer or string obtained -** from xFullPathname() with an optional suffix added. -** ^If a suffix is added to the zFilename parameter, it will -** consist of a single "-" character followed by no more than -** 11 alphanumeric and/or "-" characters. -** ^SQLite further guarantees that -** the string will be valid and unchanged until xClose() is -** called. Because of the previous sentence, -** the [sqlite3_file] can safely store a pointer to the -** filename if it needs to remember the filename for some reason. -** If the zFilename parameter to xOpen is a NULL pointer then xOpen -** must invent its own temporary name for the file. ^Whenever the -** xFilename parameter is NULL it will also be the case that the -** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. -** -** The flags argument to xOpen() includes all bits set in -** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] -** or [sqlite3_open16()] is used, then flags includes at least -** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. -** If xOpen() opens a file read-only then it sets *pOutFlags to -** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. -** -** ^(SQLite will also add one of the following flags to the xOpen() -** call, depending on the object being opened: -** -**
      -**
    • [SQLITE_OPEN_MAIN_DB] -**
    • [SQLITE_OPEN_MAIN_JOURNAL] -**
    • [SQLITE_OPEN_TEMP_DB] -**
    • [SQLITE_OPEN_TEMP_JOURNAL] -**
    • [SQLITE_OPEN_TRANSIENT_DB] -**
    • [SQLITE_OPEN_SUBJOURNAL] -**
    • [SQLITE_OPEN_MASTER_JOURNAL] -**
    • [SQLITE_OPEN_WAL] -**
    )^ -** -** The file I/O implementation can use the object type flags to -** change the way it deals with files. For example, an application -** that does not care about crash recovery or rollback might make -** the open of a journal file a no-op. Writes to this journal would -** also be no-ops, and any attempt to read the journal would return -** SQLITE_IOERR. Or the implementation might recognize that a database -** file will be doing page-aligned sector reads and writes in a random -** order and set up its I/O subsystem accordingly. -** -** SQLite might also add one of the following flags to the xOpen method: -** -**
      -**
    • [SQLITE_OPEN_DELETEONCLOSE] -**
    • [SQLITE_OPEN_EXCLUSIVE] -**
    -** -** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be -** deleted when it is closed. ^The [SQLITE_OPEN_DELETEONCLOSE] -** will be set for TEMP databases and their journals, transient -** databases, and subjournals. -** -** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction -** with the [SQLITE_OPEN_CREATE] flag, which are both directly -** analogous to the O_EXCL and O_CREAT flags of the POSIX open() -** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the -** SQLITE_OPEN_CREATE, is used to indicate that file should always -** be created, and that it is an error if it already exists. -** It is not used to indicate the file should be opened -** for exclusive access. -** -** ^At least szOsFile bytes of memory are allocated by SQLite -** to hold the [sqlite3_file] structure passed as the third -** argument to xOpen. The xOpen method does not have to -** allocate the structure; it should just fill it in. Note that -** the xOpen method must set the sqlite3_file.pMethods to either -** a valid [sqlite3_io_methods] object or to NULL. xOpen must do -** this even if the open fails. SQLite expects that the sqlite3_file.pMethods -** element will be valid after xOpen returns regardless of the success -** or failure of the xOpen call. -** -** [[sqlite3_vfs.xAccess]] -** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] -** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to -** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] -** to test whether a file is at least readable. The file can be a -** directory. -** -** ^SQLite will always allocate at least mxPathname+1 bytes for the -** output buffer xFullPathname. The exact size of the output buffer -** is also passed as a parameter to both methods. If the output buffer -** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is -** handled as a fatal error by SQLite, vfs implementations should endeavor -** to prevent this by setting mxPathname to a sufficiently large value. -** -** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() -** interfaces are not strictly a part of the filesystem, but they are -** included in the VFS structure for completeness. -** The xRandomness() function attempts to return nBytes bytes -** of good-quality randomness into zOut. The return value is -** the actual number of bytes of randomness obtained. -** The xSleep() method causes the calling thread to sleep for at -** least the number of microseconds given. ^The xCurrentTime() -** method returns a Julian Day Number for the current date and time as -** a floating point value. -** ^The xCurrentTimeInt64() method returns, as an integer, the Julian -** Day Number multiplied by 86400000 (the number of milliseconds in -** a 24-hour day). -** ^SQLite will use the xCurrentTimeInt64() method to get the current -** date and time if that method is available (if iVersion is 2 or -** greater and the function pointer is not NULL) and will fall back -** to xCurrentTime() if xCurrentTimeInt64() is unavailable. -** -** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces -** are not used by the SQLite core. These optional interfaces are provided -** by some VFSes to facilitate testing of the VFS code. By overriding -** system calls with functions under its control, a test program can -** simulate faults and error conditions that would otherwise be difficult -** or impossible to induce. The set of system calls that can be overridden -** varies from one VFS to another, and from one version of the same VFS to the -** next. Applications that use these interfaces must be prepared for any -** or all of these interfaces to be NULL or for their behavior to change -** from one release to the next. Applications must not attempt to access -** any of these methods if the iVersion of the VFS is less than 3. -*/ -typedef struct sqlite3_vfs sqlite3_vfs; -typedef void (*sqlite3_syscall_ptr)(void); -struct sqlite3_vfs { - int iVersion; /* Structure version number (currently 3) */ - int szOsFile; /* Size of subclassed sqlite3_file */ - int mxPathname; /* Maximum file pathname length */ - sqlite3_vfs *pNext; /* Next registered VFS */ - const char *zName; /* Name of this virtual file system */ - void *pAppData; /* Pointer to application-specific data */ - int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, - int flags, int *pOutFlags); - int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); - int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); - int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut); - void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename); - void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg); - void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void); - void (*xDlClose)(sqlite3_vfs*, void*); - int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut); - int (*xSleep)(sqlite3_vfs*, int microseconds); - int (*xCurrentTime)(sqlite3_vfs*, double*); - int (*xGetLastError)(sqlite3_vfs*, int, char *); - /* - ** The methods above are in version 1 of the sqlite_vfs object - ** definition. Those that follow are added in version 2 or later - */ - int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); - /* - ** The methods above are in versions 1 and 2 of the sqlite_vfs object. - ** Those below are for version 3 and greater. - */ - int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr); - sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName); - const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName); - /* - ** The methods above are in versions 1 through 3 of the sqlite_vfs object. - ** New fields may be appended in figure versions. The iVersion - ** value will increment whenever this happens. - */ -}; - -/* -** CAPI3REF: Flags for the xAccess VFS method -** -** These integer constants can be used as the third parameter to -** the xAccess method of an [sqlite3_vfs] object. They determine -** what kind of permissions the xAccess method is looking for. -** With SQLITE_ACCESS_EXISTS, the xAccess method -** simply checks whether the file exists. -** With SQLITE_ACCESS_READWRITE, the xAccess method -** checks whether the named directory is both readable and writable -** (in other words, if files can be added, removed, and renamed within -** the directory). -** The SQLITE_ACCESS_READWRITE constant is currently used only by the -** [temp_store_directory pragma], though this could change in a future -** release of SQLite. -** With SQLITE_ACCESS_READ, the xAccess method -** checks whether the file is readable. The SQLITE_ACCESS_READ constant is -** currently unused, though it might be used in a future release of -** SQLite. -*/ -#define SQLITE_ACCESS_EXISTS 0 -#define SQLITE_ACCESS_READWRITE 1 /* Used by PRAGMA temp_store_directory */ -#define SQLITE_ACCESS_READ 2 /* Unused */ - -/* -** CAPI3REF: Flags for the xShmLock VFS method -** -** These integer constants define the various locking operations -** allowed by the xShmLock method of [sqlite3_io_methods]. The -** following are the only legal combinations of flags to the -** xShmLock method: -** -**
      -**
    • SQLITE_SHM_LOCK | SQLITE_SHM_SHARED -**
    • SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE -**
    • SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED -**
    • SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE -**
    -** -** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as -** was given no the corresponding lock. -** -** The xShmLock method can transition between unlocked and SHARED or -** between unlocked and EXCLUSIVE. It cannot transition between SHARED -** and EXCLUSIVE. -*/ -#define SQLITE_SHM_UNLOCK 1 -#define SQLITE_SHM_LOCK 2 -#define SQLITE_SHM_SHARED 4 -#define SQLITE_SHM_EXCLUSIVE 8 - -/* -** CAPI3REF: Maximum xShmLock index -** -** The xShmLock method on [sqlite3_io_methods] may use values -** between 0 and this upper bound as its "offset" argument. -** The SQLite core will never attempt to acquire or release a -** lock outside of this range -*/ -#define SQLITE_SHM_NLOCK 8 - - -/* -** CAPI3REF: Initialize The SQLite Library -** -** ^The sqlite3_initialize() routine initializes the -** SQLite library. ^The sqlite3_shutdown() routine -** deallocates any resources that were allocated by sqlite3_initialize(). -** These routines are designed to aid in process initialization and -** shutdown on embedded systems. Workstation applications using -** SQLite normally do not need to invoke either of these routines. -** -** A call to sqlite3_initialize() is an "effective" call if it is -** the first time sqlite3_initialize() is invoked during the lifetime of -** the process, or if it is the first time sqlite3_initialize() is invoked -** following a call to sqlite3_shutdown(). ^(Only an effective call -** of sqlite3_initialize() does any initialization. All other calls -** are harmless no-ops.)^ -** -** A call to sqlite3_shutdown() is an "effective" call if it is the first -** call to sqlite3_shutdown() since the last sqlite3_initialize(). ^(Only -** an effective call to sqlite3_shutdown() does any deinitialization. -** All other valid calls to sqlite3_shutdown() are harmless no-ops.)^ -** -** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown() -** is not. The sqlite3_shutdown() interface must only be called from a -** single thread. All open [database connections] must be closed and all -** other SQLite resources must be deallocated prior to invoking -** sqlite3_shutdown(). -** -** Among other things, ^sqlite3_initialize() will invoke -** sqlite3_os_init(). Similarly, ^sqlite3_shutdown() -** will invoke sqlite3_os_end(). -** -** ^The sqlite3_initialize() routine returns [SQLITE_OK] on success. -** ^If for some reason, sqlite3_initialize() is unable to initialize -** the library (perhaps it is unable to allocate a needed resource such -** as a mutex) it returns an [error code] other than [SQLITE_OK]. -** -** ^The sqlite3_initialize() routine is called internally by many other -** SQLite interfaces so that an application usually does not need to -** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] -** calls sqlite3_initialize() so the SQLite library will be automatically -** initialized when [sqlite3_open()] is called if it has not be initialized -** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] -** compile-time option, then the automatic calls to sqlite3_initialize() -** are omitted and the application must call sqlite3_initialize() directly -** prior to using any other SQLite interface. For maximum portability, -** it is recommended that applications always invoke sqlite3_initialize() -** directly prior to using any other SQLite interface. Future releases -** of SQLite may require this. In other words, the behavior exhibited -** when SQLite is compiled with [SQLITE_OMIT_AUTOINIT] might become the -** default behavior in some future release of SQLite. -** -** The sqlite3_os_init() routine does operating-system specific -** initialization of the SQLite library. The sqlite3_os_end() -** routine undoes the effect of sqlite3_os_init(). Typical tasks -** performed by these routines include allocation or deallocation -** of static resources, initialization of global variables, -** setting up a default [sqlite3_vfs] module, or setting up -** a default configuration using [sqlite3_config()]. -** -** The application should never invoke either sqlite3_os_init() -** or sqlite3_os_end() directly. The application should only invoke -** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init() -** interface is called automatically by sqlite3_initialize() and -** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate -** implementations for sqlite3_os_init() and sqlite3_os_end() -** are built into SQLite when it is compiled for Unix, Windows, or OS/2. -** When [custom builds | built for other platforms] -** (using the [SQLITE_OS_OTHER=1] compile-time -** option) the application must supply a suitable implementation for -** sqlite3_os_init() and sqlite3_os_end(). An application-supplied -** implementation of sqlite3_os_init() or sqlite3_os_end() -** must return [SQLITE_OK] on success and some other [error code] upon -** failure. -*/ -SQLITE_API int sqlite3_initialize(void); -SQLITE_API int sqlite3_shutdown(void); -SQLITE_API int sqlite3_os_init(void); -SQLITE_API int sqlite3_os_end(void); - -/* -** CAPI3REF: Configuring The SQLite Library -** -** The sqlite3_config() interface is used to make global configuration -** changes to SQLite in order to tune SQLite to the specific needs of -** the application. The default configuration is recommended for most -** applications and so this routine is usually not necessary. It is -** provided to support rare applications with unusual needs. -** -** The sqlite3_config() interface is not threadsafe. The application -** must insure that no other SQLite interfaces are invoked by other -** threads while sqlite3_config() is running. Furthermore, sqlite3_config() -** may only be invoked prior to library initialization using -** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. -** ^If sqlite3_config() is called after [sqlite3_initialize()] and before -** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. -** Note, however, that ^sqlite3_config() can be called as part of the -** implementation of an application-defined [sqlite3_os_init()]. -** -** The first argument to sqlite3_config() is an integer -** [configuration option] that determines -** what property of SQLite is to be configured. Subsequent arguments -** vary depending on the [configuration option] -** in the first argument. -** -** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. -** ^If the option is unknown or SQLite is unable to set the option -** then this routine returns a non-zero [error code]. -*/ -SQLITE_API int sqlite3_config(int, ...); - -/* -** CAPI3REF: Configure database connections -** -** The sqlite3_db_config() interface is used to make configuration -** changes to a [database connection]. The interface is similar to -** [sqlite3_config()] except that the changes apply to a single -** [database connection] (specified in the first argument). -** -** The second argument to sqlite3_db_config(D,V,...) is the -** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code -** that indicates what aspect of the [database connection] is being configured. -** Subsequent arguments vary depending on the configuration verb. -** -** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if -** the call is considered successful. -*/ -SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); - -/* -** CAPI3REF: Memory Allocation Routines -** -** An instance of this object defines the interface between SQLite -** and low-level memory allocation routines. -** -** This object is used in only one place in the SQLite interface. -** A pointer to an instance of this object is the argument to -** [sqlite3_config()] when the configuration option is -** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC]. -** By creating an instance of this object -** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC]) -** during configuration, an application can specify an alternative -** memory allocation subsystem for SQLite to use for all of its -** dynamic memory needs. -** -** Note that SQLite comes with several [built-in memory allocators] -** that are perfectly adequate for the overwhelming majority of applications -** and that this object is only useful to a tiny minority of applications -** with specialized memory allocation requirements. This object is -** also used during testing of SQLite in order to specify an alternative -** memory allocator that simulates memory out-of-memory conditions in -** order to verify that SQLite recovers gracefully from such -** conditions. -** -** The xMalloc, xRealloc, and xFree methods must work like the -** malloc(), realloc() and free() functions from the standard C library. -** ^SQLite guarantees that the second argument to -** xRealloc is always a value returned by a prior call to xRoundup. -** -** xSize should return the allocated size of a memory allocation -** previously obtained from xMalloc or xRealloc. The allocated size -** is always at least as big as the requested size but may be larger. -** -** The xRoundup method returns what would be the allocated size of -** a memory allocation given a particular requested size. Most memory -** allocators round up memory allocations at least to the next multiple -** of 8. Some allocators round up to a larger multiple or to a power of 2. -** Every memory allocation request coming in through [sqlite3_malloc()] -** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, -** that causes the corresponding memory allocation to fail. -** -** The xInit method initializes the memory allocator. (For example, -** it might allocate any require mutexes or initialize internal data -** structures. The xShutdown method is invoked (indirectly) by -** [sqlite3_shutdown()] and should deallocate any resources acquired -** by xInit. The pAppData pointer is used as the only parameter to -** xInit and xShutdown. -** -** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes -** the xInit method, so the xInit method need not be threadsafe. The -** xShutdown method is only called from [sqlite3_shutdown()] so it does -** not need to be threadsafe either. For all other methods, SQLite -** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the -** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which -** it is by default) and so the methods are automatically serialized. -** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other -** methods must be threadsafe or else make their own arrangements for -** serialization. -** -** SQLite will never invoke xInit() more than once without an intervening -** call to xShutdown(). -*/ -typedef struct sqlite3_mem_methods sqlite3_mem_methods; -struct sqlite3_mem_methods { - void *(*xMalloc)(int); /* Memory allocation function */ - void (*xFree)(void*); /* Free a prior allocation */ - void *(*xRealloc)(void*,int); /* Resize an allocation */ - int (*xSize)(void*); /* Return the size of an allocation */ - int (*xRoundup)(int); /* Round up request size to allocation size */ - int (*xInit)(void*); /* Initialize the memory allocator */ - void (*xShutdown)(void*); /* Deinitialize the memory allocator */ - void *pAppData; /* Argument to xInit() and xShutdown() */ -}; - -/* -** CAPI3REF: Configuration Options -** KEYWORDS: {configuration option} -** -** These constants are the available integer configuration options that -** can be passed as the first argument to the [sqlite3_config()] interface. -** -** New configuration options may be added in future releases of SQLite. -** Existing configuration options might be discontinued. Applications -** should check the return code from [sqlite3_config()] to make sure that -** the call worked. The [sqlite3_config()] interface will return a -** non-zero [error code] if a discontinued or unsupported configuration option -** is invoked. -** -**
    -** [[SQLITE_CONFIG_SINGLETHREAD]]
    SQLITE_CONFIG_SINGLETHREAD
    -**
    There are no arguments to this option. ^This option sets the -** [threading mode] to Single-thread. In other words, it disables -** all mutexing and puts SQLite into a mode where it can only be used -** by a single thread. ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** it is not possible to change the [threading mode] from its default -** value of Single-thread and so [sqlite3_config()] will return -** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD -** configuration option.
    -** -** [[SQLITE_CONFIG_MULTITHREAD]]
    SQLITE_CONFIG_MULTITHREAD
    -**
    There are no arguments to this option. ^This option sets the -** [threading mode] to Multi-thread. In other words, it disables -** mutexing on [database connection] and [prepared statement] objects. -** The application is responsible for serializing access to -** [database connections] and [prepared statements]. But other mutexes -** are enabled so that SQLite will be safe to use in a multi-threaded -** environment as long as no two threads attempt to use the same -** [database connection] at the same time. ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** it is not possible to set the Multi-thread [threading mode] and -** [sqlite3_config()] will return [SQLITE_ERROR] if called with the -** SQLITE_CONFIG_MULTITHREAD configuration option.
    -** -** [[SQLITE_CONFIG_SERIALIZED]]
    SQLITE_CONFIG_SERIALIZED
    -**
    There are no arguments to this option. ^This option sets the -** [threading mode] to Serialized. In other words, this option enables -** all mutexes including the recursive -** mutexes on [database connection] and [prepared statement] objects. -** In this mode (which is the default when SQLite is compiled with -** [SQLITE_THREADSAFE=1]) the SQLite library will itself serialize access -** to [database connections] and [prepared statements] so that the -** application is free to use the same [database connection] or the -** same [prepared statement] in different threads at the same time. -** ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** it is not possible to set the Serialized [threading mode] and -** [sqlite3_config()] will return [SQLITE_ERROR] if called with the -** SQLITE_CONFIG_SERIALIZED configuration option.
    -** -** [[SQLITE_CONFIG_MALLOC]]
    SQLITE_CONFIG_MALLOC
    -**
    ^(This option takes a single argument which is a pointer to an -** instance of the [sqlite3_mem_methods] structure. The argument specifies -** alternative low-level memory allocation routines to be used in place of -** the memory allocation routines built into SQLite.)^ ^SQLite makes -** its own private copy of the content of the [sqlite3_mem_methods] structure -** before the [sqlite3_config()] call returns.
    -** -** [[SQLITE_CONFIG_GETMALLOC]]
    SQLITE_CONFIG_GETMALLOC
    -**
    ^(This option takes a single argument which is a pointer to an -** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] -** structure is filled with the currently defined memory allocation routines.)^ -** This option can be used to overload the default memory allocation -** routines with a wrapper that simulations memory allocation failure or -** tracks memory usage, for example.
    -** -** [[SQLITE_CONFIG_MEMSTATUS]]
    SQLITE_CONFIG_MEMSTATUS
    -**
    ^This option takes single argument of type int, interpreted as a -** boolean, which enables or disables the collection of memory allocation -** statistics. ^(When memory allocation statistics are disabled, the -** following SQLite interfaces become non-operational: -**
      -**
    • [sqlite3_memory_used()] -**
    • [sqlite3_memory_highwater()] -**
    • [sqlite3_soft_heap_limit64()] -**
    • [sqlite3_status()] -**
    )^ -** ^Memory allocation statistics are enabled by default unless SQLite is -** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory -** allocation statistics are disabled by default. -**
    -** -** [[SQLITE_CONFIG_SCRATCH]]
    SQLITE_CONFIG_SCRATCH
    -**
    ^This option specifies a static memory buffer that SQLite can use for -** scratch memory. There are three arguments: A pointer an 8-byte -** aligned memory buffer from which the scratch allocations will be -** drawn, the size of each scratch allocation (sz), -** and the maximum number of scratch allocations (N). The sz -** argument must be a multiple of 16. -** The first argument must be a pointer to an 8-byte aligned buffer -** of at least sz*N bytes of memory. -** ^SQLite will use no more than two scratch buffers per thread. So -** N should be set to twice the expected maximum number of threads. -** ^SQLite will never require a scratch buffer that is more than 6 -** times the database page size. ^If SQLite needs needs additional -** scratch memory beyond what is provided by this configuration option, then -** [sqlite3_malloc()] will be used to obtain the memory needed.
    -** -** [[SQLITE_CONFIG_PAGECACHE]]
    SQLITE_CONFIG_PAGECACHE
    -**
    ^This option specifies a static memory buffer that SQLite can use for -** the database page cache with the default page cache implementation. -** This configuration should not be used if an application-define page -** cache implementation is loaded using the SQLITE_CONFIG_PCACHE2 option. -** There are three arguments to this option: A pointer to 8-byte aligned -** memory, the size of each page buffer (sz), and the number of pages (N). -** The sz argument should be the size of the largest database page -** (a power of two between 512 and 32768) plus a little extra for each -** page header. ^The page header size is 20 to 40 bytes depending on -** the host architecture. ^It is harmless, apart from the wasted memory, -** to make sz a little too large. The first -** argument should point to an allocation of at least sz*N bytes of memory. -** ^SQLite will use the memory provided by the first argument to satisfy its -** memory needs for the first N pages that it adds to cache. ^If additional -** page cache memory is needed beyond what is provided by this option, then -** SQLite goes to [sqlite3_malloc()] for the additional storage space. -** The pointer in the first argument must -** be aligned to an 8-byte boundary or subsequent behavior of SQLite -** will be undefined.
    -** -** [[SQLITE_CONFIG_HEAP]]
    SQLITE_CONFIG_HEAP
    -**
    ^This option specifies a static memory buffer that SQLite will use -** for all of its dynamic memory allocation needs beyond those provided -** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. -** There are three arguments: An 8-byte aligned pointer to the memory, -** the number of bytes in the memory buffer, and the minimum allocation size. -** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts -** to using its default memory allocator (the system malloc() implementation), -** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the -** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or -** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory -** allocator is engaged to handle all of SQLites memory allocation needs. -** The first pointer (the memory pointer) must be aligned to an 8-byte -** boundary or subsequent behavior of SQLite will be undefined. -** The minimum allocation size is capped at 2**12. Reasonable values -** for the minimum allocation size are 2**5 through 2**8.
    -** -** [[SQLITE_CONFIG_MUTEX]]
    SQLITE_CONFIG_MUTEX
    -**
    ^(This option takes a single argument which is a pointer to an -** instance of the [sqlite3_mutex_methods] structure. The argument specifies -** alternative low-level mutex routines to be used in place -** the mutex routines built into SQLite.)^ ^SQLite makes a copy of the -** content of the [sqlite3_mutex_methods] structure before the call to -** [sqlite3_config()] returns. ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** the entire mutexing subsystem is omitted from the build and hence calls to -** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will -** return [SQLITE_ERROR].
    -** -** [[SQLITE_CONFIG_GETMUTEX]]
    SQLITE_CONFIG_GETMUTEX
    -**
    ^(This option takes a single argument which is a pointer to an -** instance of the [sqlite3_mutex_methods] structure. The -** [sqlite3_mutex_methods] -** structure is filled with the currently defined mutex routines.)^ -** This option can be used to overload the default mutex allocation -** routines with a wrapper used to track mutex usage for performance -** profiling or testing, for example. ^If SQLite is compiled with -** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then -** the entire mutexing subsystem is omitted from the build and hence calls to -** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will -** return [SQLITE_ERROR].
    -** -** [[SQLITE_CONFIG_LOOKASIDE]]
    SQLITE_CONFIG_LOOKASIDE
    -**
    ^(This option takes two arguments that determine the default -** memory allocation for the lookaside memory allocator on each -** [database connection]. The first argument is the -** size of each lookaside buffer slot and the second is the number of -** slots allocated to each database connection.)^ ^(This option sets the -** default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] -** verb to [sqlite3_db_config()] can be used to change the lookaside -** configuration on individual connections.)^
    -** -** [[SQLITE_CONFIG_PCACHE2]]
    SQLITE_CONFIG_PCACHE2
    -**
    ^(This option takes a single argument which is a pointer to -** an [sqlite3_pcache_methods2] object. This object specifies the interface -** to a custom page cache implementation.)^ ^SQLite makes a copy of the -** object and uses it for page cache memory allocations.
    -** -** [[SQLITE_CONFIG_GETPCACHE2]]
    SQLITE_CONFIG_GETPCACHE2
    -**
    ^(This option takes a single argument which is a pointer to an -** [sqlite3_pcache_methods2] object. SQLite copies of the current -** page cache implementation into that object.)^
    -** -** [[SQLITE_CONFIG_LOG]]
    SQLITE_CONFIG_LOG
    -**
    ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a -** function with a call signature of void(*)(void*,int,const char*), -** and a pointer to void. ^If the function pointer is not NULL, it is -** invoked by [sqlite3_log()] to process each logging event. ^If the -** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op. -** ^The void pointer that is the second argument to SQLITE_CONFIG_LOG is -** passed through as the first parameter to the application-defined logger -** function whenever that function is invoked. ^The second parameter to -** the logger function is a copy of the first parameter to the corresponding -** [sqlite3_log()] call and is intended to be a [result code] or an -** [extended result code]. ^The third parameter passed to the logger is -** log message after formatting via [sqlite3_snprintf()]. -** The SQLite logging interface is not reentrant; the logger function -** supplied by the application must not invoke any SQLite interface. -** In a multi-threaded application, the application-defined logger -** function must be threadsafe.
    -** -** [[SQLITE_CONFIG_URI]]
    SQLITE_CONFIG_URI -**
    This option takes a single argument of type int. If non-zero, then -** URI handling is globally enabled. If the parameter is zero, then URI handling -** is globally disabled. If URI handling is globally enabled, all filenames -** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or -** specified as part of [ATTACH] commands are interpreted as URIs, regardless -** of whether or not the [SQLITE_OPEN_URI] flag is set when the database -** connection is opened. If it is globally disabled, filenames are -** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the -** database connection is opened. By default, URI handling is globally -** disabled. The default value may be changed by compiling with the -** [SQLITE_USE_URI] symbol defined. -** -** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]]
    SQLITE_CONFIG_COVERING_INDEX_SCAN -**
    This option takes a single integer argument which is interpreted as -** a boolean in order to enable or disable the use of covering indices for -** full table scans in the query optimizer. The default setting is determined -** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on" -** if that compile-time option is omitted. -** The ability to disable the use of covering indices for full table scans -** is because some incorrectly coded legacy applications might malfunction -** malfunction when the optimization is enabled. Providing the ability to -** disable the optimization allows the older, buggy application code to work -** without change even with newer versions of SQLite. -** -** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]] -**
    SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE -**
    These options are obsolete and should not be used by new code. -** They are retained for backwards compatibility but are now no-ops. -**
    -** -** [[SQLITE_CONFIG_SQLLOG]] -**
    SQLITE_CONFIG_SQLLOG -**
    This option is only available if sqlite is compiled with the -** SQLITE_ENABLE_SQLLOG pre-processor macro defined. The first argument should -** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int). -** The second should be of type (void*). The callback is invoked by the library -** in three separate circumstances, identified by the value passed as the -** fourth parameter. If the fourth parameter is 0, then the database connection -** passed as the second argument has just been opened. The third argument -** points to a buffer containing the name of the main database file. If the -** fourth parameter is 1, then the SQL statement that the third parameter -** points to has just been executed. Or, if the fourth parameter is 2, then -** the connection being passed as the second parameter is being closed. The -** third parameter is passed NULL In this case. -** -*/ -#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ -#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ -#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ -#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */ -#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ -#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ -#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ -#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ -#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ -/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ -#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -#define SQLITE_CONFIG_PCACHE 14 /* no-op */ -#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ -#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ -#define SQLITE_CONFIG_URI 17 /* int */ -#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ -#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ -#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ -#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ - -/* -** CAPI3REF: Database Connection Configuration Options -** -** These constants are the available integer configuration options that -** can be passed as the second argument to the [sqlite3_db_config()] interface. -** -** New configuration options may be added in future releases of SQLite. -** Existing configuration options might be discontinued. Applications -** should check the return code from [sqlite3_db_config()] to make sure that -** the call worked. ^The [sqlite3_db_config()] interface will return a -** non-zero [error code] if a discontinued or unsupported configuration option -** is invoked. -** -**
    -**
    SQLITE_DBCONFIG_LOOKASIDE
    -**
    ^This option takes three additional arguments that determine the -** [lookaside memory allocator] configuration for the [database connection]. -** ^The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to a memory buffer to use for lookaside memory. -** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb -** may be NULL in which case SQLite will allocate the -** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the -** size of each lookaside buffer slot. ^The third argument is the number of -** slots. The size of the buffer in the first argument must be greater than -** or equal to the product of the second and third arguments. The buffer -** must be aligned to an 8-byte boundary. ^If the second argument to -** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally -** rounded down to the next smaller multiple of 8. ^(The lookaside memory -** configuration for a database connection can only be changed when that -** connection is not currently using lookaside memory, or in other words -** when the "current value" returned by -** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. -** Any attempt to change the lookaside memory configuration when lookaside -** memory is in use leaves the configuration unchanged and returns -** [SQLITE_BUSY].)^
    -** -**
    SQLITE_DBCONFIG_ENABLE_FKEY
    -**
    ^This option is used to enable or disable the enforcement of -** [foreign key constraints]. There should be two additional arguments. -** The first argument is an integer which is 0 to disable FK enforcement, -** positive to enable FK enforcement or negative to leave FK enforcement -** unchanged. The second parameter is a pointer to an integer into which -** is written 0 or 1 to indicate whether FK enforcement is off or on -** following this call. The second parameter may be a NULL pointer, in -** which case the FK enforcement setting is not reported back.
    -** -**
    SQLITE_DBCONFIG_ENABLE_TRIGGER
    -**
    ^This option is used to enable or disable [CREATE TRIGGER | triggers]. -** There should be two additional arguments. -** The first argument is an integer which is 0 to disable triggers, -** positive to enable triggers or negative to leave the setting unchanged. -** The second parameter is a pointer to an integer into which -** is written 0 or 1 to indicate whether triggers are disabled or enabled -** following this call. The second parameter may be a NULL pointer, in -** which case the trigger setting is not reported back.
    -** -**
    -*/ -#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ -#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ -#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ - - -/* -** CAPI3REF: Enable Or Disable Extended Result Codes -** -** ^The sqlite3_extended_result_codes() routine enables or disables the -** [extended result codes] feature of SQLite. ^The extended result -** codes are disabled by default for historical compatibility. -*/ -SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); - -/* -** CAPI3REF: Last Insert Rowid -** -** ^Each entry in an SQLite table has a unique 64-bit signed -** integer key called the [ROWID | "rowid"]. ^The rowid is always available -** as an undeclared column named ROWID, OID, or _ROWID_ as long as those -** names are not also used by explicitly declared columns. ^If -** the table has a column of type [INTEGER PRIMARY KEY] then that column -** is another alias for the rowid. -** -** ^This routine returns the [rowid] of the most recent -** successful [INSERT] into the database from the [database connection] -** in the first argument. ^As of SQLite version 3.7.7, this routines -** records the last insert rowid of both ordinary tables and [virtual tables]. -** ^If no successful [INSERT]s -** have ever occurred on that database connection, zero is returned. -** -** ^(If an [INSERT] occurs within a trigger or within a [virtual table] -** method, then this routine will return the [rowid] of the inserted -** row as long as the trigger or virtual table method is running. -** But once the trigger or virtual table method ends, the value returned -** by this routine reverts to what it was before the trigger or virtual -** table method began.)^ -** -** ^An [INSERT] that fails due to a constraint violation is not a -** successful [INSERT] and does not change the value returned by this -** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, -** and INSERT OR ABORT make no changes to the return value of this -** routine when their insertion fails. ^(When INSERT OR REPLACE -** encounters a constraint violation, it does not fail. The -** INSERT continues to completion after deleting rows that caused -** the constraint problem so INSERT OR REPLACE will always change -** the return value of this interface.)^ -** -** ^For the purposes of this routine, an [INSERT] is considered to -** be successful even if it is subsequently rolled back. -** -** This function is accessible to SQL statements via the -** [last_insert_rowid() SQL function]. -** -** If a separate thread performs a new [INSERT] on the same -** database connection while the [sqlite3_last_insert_rowid()] -** function is running and thus changes the last insert [rowid], -** then the value returned by [sqlite3_last_insert_rowid()] is -** unpredictable and might not equal either the old or the new -** last insert [rowid]. -*/ -SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); - -/* -** CAPI3REF: Count The Number Of Rows Modified -** -** ^This function returns the number of database rows that were changed -** or inserted or deleted by the most recently completed SQL statement -** on the [database connection] specified by the first parameter. -** ^(Only changes that are directly specified by the [INSERT], [UPDATE], -** or [DELETE] statement are counted. Auxiliary changes caused by -** triggers or [foreign key actions] are not counted.)^ Use the -** [sqlite3_total_changes()] function to find the total number of changes -** including changes caused by triggers and foreign key actions. -** -** ^Changes to a view that are simulated by an [INSTEAD OF trigger] -** are not counted. Only real table changes are counted. -** -** ^(A "row change" is a change to a single row of a single table -** caused by an INSERT, DELETE, or UPDATE statement. Rows that -** are changed as side effects of [REPLACE] constraint resolution, -** rollback, ABORT processing, [DROP TABLE], or by any other -** mechanisms do not count as direct row changes.)^ -** -** A "trigger context" is a scope of execution that begins and -** ends with the script of a [CREATE TRIGGER | trigger]. -** Most SQL statements are -** evaluated outside of any trigger. This is the "top level" -** trigger context. If a trigger fires from the top level, a -** new trigger context is entered for the duration of that one -** trigger. Subtriggers create subcontexts for their duration. -** -** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does -** not create a new trigger context. -** -** ^This function returns the number of direct row changes in the -** most recent INSERT, UPDATE, or DELETE statement within the same -** trigger context. -** -** ^Thus, when called from the top level, this function returns the -** number of changes in the most recent INSERT, UPDATE, or DELETE -** that also occurred at the top level. ^(Within the body of a trigger, -** the sqlite3_changes() interface can be called to find the number of -** changes in the most recently completed INSERT, UPDATE, or DELETE -** statement within the body of the same trigger. -** However, the number returned does not include changes -** caused by subtriggers since those have their own context.)^ -** -** See also the [sqlite3_total_changes()] interface, the -** [count_changes pragma], and the [changes() SQL function]. -** -** If a separate thread makes changes on the same database connection -** while [sqlite3_changes()] is running then the value returned -** is unpredictable and not meaningful. -*/ -SQLITE_API int sqlite3_changes(sqlite3*); - -/* -** CAPI3REF: Total Number Of Rows Modified -** -** ^This function returns the number of row changes caused by [INSERT], -** [UPDATE] or [DELETE] statements since the [database connection] was opened. -** ^(The count returned by sqlite3_total_changes() includes all changes -** from all [CREATE TRIGGER | trigger] contexts and changes made by -** [foreign key actions]. However, -** the count does not include changes used to implement [REPLACE] constraints, -** do rollbacks or ABORT processing, or [DROP TABLE] processing. The -** count does not include rows of views that fire an [INSTEAD OF trigger], -** though if the INSTEAD OF trigger makes changes of its own, those changes -** are counted.)^ -** ^The sqlite3_total_changes() function counts the changes as soon as -** the statement that makes them is completed (when the statement handle -** is passed to [sqlite3_reset()] or [sqlite3_finalize()]). -** -** See also the [sqlite3_changes()] interface, the -** [count_changes pragma], and the [total_changes() SQL function]. -** -** If a separate thread makes changes on the same database connection -** while [sqlite3_total_changes()] is running then the value -** returned is unpredictable and not meaningful. -*/ -SQLITE_API int sqlite3_total_changes(sqlite3*); - -/* -** CAPI3REF: Interrupt A Long-Running Query -** -** ^This function causes any pending database operation to abort and -** return at its earliest opportunity. This routine is typically -** called in response to a user action such as pressing "Cancel" -** or Ctrl-C where the user wants a long query operation to halt -** immediately. -** -** ^It is safe to call this routine from a thread different from the -** thread that is currently running the database operation. But it -** is not safe to call this routine with a [database connection] that -** is closed or might close before sqlite3_interrupt() returns. -** -** ^If an SQL operation is very nearly finished at the time when -** sqlite3_interrupt() is called, then it might not have an opportunity -** to be interrupted and might continue to completion. -** -** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. -** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE -** that is inside an explicit transaction, then the entire transaction -** will be rolled back automatically. -** -** ^The sqlite3_interrupt(D) call is in effect until all currently running -** SQL statements on [database connection] D complete. ^Any new SQL statements -** that are started after the sqlite3_interrupt() call and before the -** running statements reaches zero are interrupted as if they had been -** running prior to the sqlite3_interrupt() call. ^New SQL statements -** that are started after the running statement count reaches zero are -** not effected by the sqlite3_interrupt(). -** ^A call to sqlite3_interrupt(D) that occurs when there are no running -** SQL statements is a no-op and has no effect on SQL statements -** that are started after the sqlite3_interrupt() call returns. -** -** If the database connection closes while [sqlite3_interrupt()] -** is running then bad things will likely happen. -*/ -SQLITE_API void sqlite3_interrupt(sqlite3*); - -/* -** CAPI3REF: Determine If An SQL Statement Is Complete -** -** These routines are useful during command-line input to determine if the -** currently entered text seems to form a complete SQL statement or -** if additional input is needed before sending the text into -** SQLite for parsing. ^These routines return 1 if the input string -** appears to be a complete SQL statement. ^A statement is judged to be -** complete if it ends with a semicolon token and is not a prefix of a -** well-formed CREATE TRIGGER statement. ^Semicolons that are embedded within -** string literals or quoted identifier names or comments are not -** independent tokens (they are part of the token in which they are -** embedded) and thus do not count as a statement terminator. ^Whitespace -** and comments that follow the final semicolon are ignored. -** -** ^These routines return 0 if the statement is incomplete. ^If a -** memory allocation fails, then SQLITE_NOMEM is returned. -** -** ^These routines do not parse the SQL statements thus -** will not detect syntactically incorrect SQL. -** -** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior -** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked -** automatically by sqlite3_complete16(). If that initialization fails, -** then the return value from sqlite3_complete16() will be non-zero -** regardless of whether or not the input SQL is complete.)^ -** -** The input to [sqlite3_complete()] must be a zero-terminated -** UTF-8 string. -** -** The input to [sqlite3_complete16()] must be a zero-terminated -** UTF-16 string in native byte order. -*/ -SQLITE_API int sqlite3_complete(const char *sql); -SQLITE_API int sqlite3_complete16(const void *sql); - -/* -** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors -** -** ^This routine sets a callback function that might be invoked whenever -** an attempt is made to open a database table that another thread -** or process has locked. -** -** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] -** is returned immediately upon encountering the lock. ^If the busy callback -** is not NULL, then the callback might be invoked with two arguments. -** -** ^The first argument to the busy handler is a copy of the void* pointer which -** is the third argument to sqlite3_busy_handler(). ^The second argument to -** the busy handler callback is the number of times that the busy handler has -** been invoked for this locking event. ^If the -** busy callback returns 0, then no additional attempts are made to -** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned. -** ^If the callback returns non-zero, then another attempt -** is made to open the database for reading and the cycle repeats. -** -** The presence of a busy handler does not guarantee that it will be invoked -** when there is lock contention. ^If SQLite determines that invoking the busy -** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] -** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler. -** Consider a scenario where one process is holding a read lock that -** it is trying to promote to a reserved lock and -** a second process is holding a reserved lock that it is trying -** to promote to an exclusive lock. The first process cannot proceed -** because it is blocked by the second and the second process cannot -** proceed because it is blocked by the first. If both processes -** invoke the busy handlers, neither will make any progress. Therefore, -** SQLite returns [SQLITE_BUSY] for the first process, hoping that this -** will induce the first process to release its read lock and allow -** the second process to proceed. -** -** ^The default busy callback is NULL. -** -** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] -** when SQLite is in the middle of a large transaction where all the -** changes will not fit into the in-memory cache. SQLite will -** already hold a RESERVED lock on the database file, but it needs -** to promote this lock to EXCLUSIVE so that it can spill cache -** pages into the database file without harm to concurrent -** readers. ^If it is unable to promote the lock, then the in-memory -** cache will be left in an inconsistent state and so the error -** code is promoted from the relatively benign [SQLITE_BUSY] to -** the more severe [SQLITE_IOERR_BLOCKED]. ^This error code promotion -** forces an automatic rollback of the changes. See the -** -** CorruptionFollowingBusyError wiki page for a discussion of why -** this is important. -** -** ^(There can only be a single busy handler defined for each -** [database connection]. Setting a new busy handler clears any -** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] -** will also set or clear the busy handler. -** -** The busy callback should not take any actions which modify the -** database connection that invoked the busy handler. Any such actions -** result in undefined behavior. -** -** A busy handler must not close the database connection -** or [prepared statement] that invoked the busy handler. -*/ -SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); - -/* -** CAPI3REF: Set A Busy Timeout -** -** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps -** for a specified amount of time when a table is locked. ^The handler -** will sleep multiple times until at least "ms" milliseconds of sleeping -** have accumulated. ^After at least "ms" milliseconds of sleeping, -** the handler returns 0 which causes [sqlite3_step()] to return -** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]. -** -** ^Calling this routine with an argument less than or equal to zero -** turns off all busy handlers. -** -** ^(There can only be a single busy handler for a particular -** [database connection] any any given moment. If another busy handler -** was defined (using [sqlite3_busy_handler()]) prior to calling -** this routine, that other busy handler is cleared.)^ -*/ -SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); - -/* -** CAPI3REF: Convenience Routines For Running Queries -** -** This is a legacy interface that is preserved for backwards compatibility. -** Use of this interface is not recommended. -** -** Definition: A result table is memory data structure created by the -** [sqlite3_get_table()] interface. A result table records the -** complete query results from one or more queries. -** -** The table conceptually has a number of rows and columns. But -** these numbers are not part of the result table itself. These -** numbers are obtained separately. Let N be the number of rows -** and M be the number of columns. -** -** A result table is an array of pointers to zero-terminated UTF-8 strings. -** There are (N+1)*M elements in the array. The first M pointers point -** to zero-terminated strings that contain the names of the columns. -** The remaining entries all point to query results. NULL values result -** in NULL pointers. All other values are in their UTF-8 zero-terminated -** string representation as returned by [sqlite3_column_text()]. -** -** A result table might consist of one or more memory allocations. -** It is not safe to pass a result table directly to [sqlite3_free()]. -** A result table should be deallocated using [sqlite3_free_table()]. -** -** ^(As an example of the result table format, suppose a query result -** is as follows: -** -**
    -**        Name        | Age
    -**        -----------------------
    -**        Alice       | 43
    -**        Bob         | 28
    -**        Cindy       | 21
    -** 
    -** -** There are two column (M==2) and three rows (N==3). Thus the -** result table has 8 entries. Suppose the result table is stored -** in an array names azResult. Then azResult holds this content: -** -**
    -**        azResult[0] = "Name";
    -**        azResult[1] = "Age";
    -**        azResult[2] = "Alice";
    -**        azResult[3] = "43";
    -**        azResult[4] = "Bob";
    -**        azResult[5] = "28";
    -**        azResult[6] = "Cindy";
    -**        azResult[7] = "21";
    -** 
    )^ -** -** ^The sqlite3_get_table() function evaluates one or more -** semicolon-separated SQL statements in the zero-terminated UTF-8 -** string of its 2nd parameter and returns a result table to the -** pointer given in its 3rd parameter. -** -** After the application has finished with the result from sqlite3_get_table(), -** it must pass the result table pointer to sqlite3_free_table() in order to -** release the memory that was malloced. Because of the way the -** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling -** function must not try to call [sqlite3_free()] directly. Only -** [sqlite3_free_table()] is able to release the memory properly and safely. -** -** The sqlite3_get_table() interface is implemented as a wrapper around -** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access -** to any internal data structures of SQLite. It uses only the public -** interface defined here. As a consequence, errors that occur in the -** wrapper layer outside of the internal [sqlite3_exec()] call are not -** reflected in subsequent calls to [sqlite3_errcode()] or -** [sqlite3_errmsg()]. -*/ -SQLITE_API int sqlite3_get_table( - sqlite3 *db, /* An open database */ - const char *zSql, /* SQL to be evaluated */ - char ***pazResult, /* Results of the query */ - int *pnRow, /* Number of result rows written here */ - int *pnColumn, /* Number of result columns written here */ - char **pzErrmsg /* Error msg written here */ -); -SQLITE_API void sqlite3_free_table(char **result); - -/* -** CAPI3REF: Formatted String Printing Functions -** -** These routines are work-alikes of the "printf()" family of functions -** from the standard C library. -** -** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their -** results into memory obtained from [sqlite3_malloc()]. -** The strings returned by these two routines should be -** released by [sqlite3_free()]. ^Both routines return a -** NULL pointer if [sqlite3_malloc()] is unable to allocate enough -** memory to hold the resulting string. -** -** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from -** the standard C library. The result is written into the -** buffer supplied as the second parameter whose size is given by -** the first parameter. Note that the order of the -** first two parameters is reversed from snprintf().)^ This is an -** historical accident that cannot be fixed without breaking -** backwards compatibility. ^(Note also that sqlite3_snprintf() -** returns a pointer to its buffer instead of the number of -** characters actually written into the buffer.)^ We admit that -** the number of characters written would be a more useful return -** value but we cannot change the implementation of sqlite3_snprintf() -** now without breaking compatibility. -** -** ^As long as the buffer size is greater than zero, sqlite3_snprintf() -** guarantees that the buffer is always zero-terminated. ^The first -** parameter "n" is the total size of the buffer, including space for -** the zero terminator. So the longest string that can be completely -** written will be n-1 characters. -** -** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). -** -** These routines all implement some additional formatting -** options that are useful for constructing SQL statements. -** All of the usual printf() formatting options apply. In addition, there -** is are "%q", "%Q", and "%z" options. -** -** ^(The %q option works like %s in that it substitutes a nul-terminated -** string from the argument list. But %q also doubles every '\'' character. -** %q is designed for use inside a string literal.)^ By doubling each '\'' -** character it escapes that character and allows it to be inserted into -** the string. -** -** For example, assume the string variable zText contains text as follows: -** -**
    -**  char *zText = "It's a happy day!";
    -** 
    -** -** One can use this text in an SQL statement as follows: -** -**
    -**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
    -**  sqlite3_exec(db, zSQL, 0, 0, 0);
    -**  sqlite3_free(zSQL);
    -** 
    -** -** Because the %q format string is used, the '\'' character in zText -** is escaped and the SQL generated is as follows: -** -**
    -**  INSERT INTO table1 VALUES('It''s a happy day!')
    -** 
    -** -** This is correct. Had we used %s instead of %q, the generated SQL -** would have looked like this: -** -**
    -**  INSERT INTO table1 VALUES('It's a happy day!');
    -** 
    -** -** This second example is an SQL syntax error. As a general rule you should -** always use %q instead of %s when inserting text into a string literal. -** -** ^(The %Q option works like %q except it also adds single quotes around -** the outside of the total string. Additionally, if the parameter in the -** argument list is a NULL pointer, %Q substitutes the text "NULL" (without -** single quotes).)^ So, for example, one could say: -** -**
    -**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
    -**  sqlite3_exec(db, zSQL, 0, 0, 0);
    -**  sqlite3_free(zSQL);
    -** 
    -** -** The code above will render a correct SQL statement in the zSQL -** variable even if the zText variable is a NULL pointer. -** -** ^(The "%z" formatting option works like "%s" but with the -** addition that after the string has been read and copied into -** the result, [sqlite3_free()] is called on the input string.)^ -*/ -SQLITE_API char *sqlite3_mprintf(const char*,...); -SQLITE_API char *sqlite3_vmprintf(const char*, va_list); -SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); -SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); - -/* -** CAPI3REF: Memory Allocation Subsystem -** -** The SQLite core uses these three routines for all of its own -** internal memory allocation needs. "Core" in the previous sentence -** does not include operating-system specific VFS implementation. The -** Windows VFS uses native malloc() and free() for some operations. -** -** ^The sqlite3_malloc() routine returns a pointer to a block -** of memory at least N bytes in length, where N is the parameter. -** ^If sqlite3_malloc() is unable to obtain sufficient free -** memory, it returns a NULL pointer. ^If the parameter N to -** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns -** a NULL pointer. -** -** ^Calling sqlite3_free() with a pointer previously returned -** by sqlite3_malloc() or sqlite3_realloc() releases that memory so -** that it might be reused. ^The sqlite3_free() routine is -** a no-op if is called with a NULL pointer. Passing a NULL pointer -** to sqlite3_free() is harmless. After being freed, memory -** should neither be read nor written. Even reading previously freed -** memory might result in a segmentation fault or other severe error. -** Memory corruption, a segmentation fault, or other severe error -** might result if sqlite3_free() is called with a non-NULL pointer that -** was not obtained from sqlite3_malloc() or sqlite3_realloc(). -** -** ^(The sqlite3_realloc() interface attempts to resize a -** prior memory allocation to be at least N bytes, where N is the -** second parameter. The memory allocation to be resized is the first -** parameter.)^ ^ If the first parameter to sqlite3_realloc() -** is a NULL pointer then its behavior is identical to calling -** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc(). -** ^If the second parameter to sqlite3_realloc() is zero or -** negative then the behavior is exactly the same as calling -** sqlite3_free(P) where P is the first parameter to sqlite3_realloc(). -** ^sqlite3_realloc() returns a pointer to a memory allocation -** of at least N bytes in size or NULL if sufficient memory is unavailable. -** ^If M is the size of the prior allocation, then min(N,M) bytes -** of the prior allocation are copied into the beginning of buffer returned -** by sqlite3_realloc() and the prior allocation is freed. -** ^If sqlite3_realloc() returns NULL, then the prior allocation -** is not freed. -** -** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() -** is always aligned to at least an 8 byte boundary, or to a -** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time -** option is used. -** -** In SQLite version 3.5.0 and 3.5.1, it was possible to define -** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in -** implementation of these routines to be omitted. That capability -** is no longer provided. Only built-in memory allocators can be used. -** -** Prior to SQLite version 3.7.10, the Windows OS interface layer called -** the system malloc() and free() directly when converting -** filenames between the UTF-8 encoding used by SQLite -** and whatever filename encoding is used by the particular Windows -** installation. Memory allocation errors were detected, but -** they were reported back as [SQLITE_CANTOPEN] or -** [SQLITE_IOERR] rather than [SQLITE_NOMEM]. -** -** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] -** must be either NULL or else pointers obtained from a prior -** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have -** not yet been released. -** -** The application must not read or write any part of -** a block of memory after it has been released using -** [sqlite3_free()] or [sqlite3_realloc()]. -*/ -SQLITE_API void *sqlite3_malloc(int); -SQLITE_API void *sqlite3_realloc(void*, int); -SQLITE_API void sqlite3_free(void*); - -/* -** CAPI3REF: Memory Allocator Statistics -** -** SQLite provides these two interfaces for reporting on the status -** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] -** routines, which form the built-in memory allocation subsystem. -** -** ^The [sqlite3_memory_used()] routine returns the number of bytes -** of memory currently outstanding (malloced but not freed). -** ^The [sqlite3_memory_highwater()] routine returns the maximum -** value of [sqlite3_memory_used()] since the high-water mark -** was last reset. ^The values returned by [sqlite3_memory_used()] and -** [sqlite3_memory_highwater()] include any overhead -** added by SQLite in its implementation of [sqlite3_malloc()], -** but not overhead added by the any underlying system library -** routines that [sqlite3_malloc()] may call. -** -** ^The memory high-water mark is reset to the current value of -** [sqlite3_memory_used()] if and only if the parameter to -** [sqlite3_memory_highwater()] is true. ^The value returned -** by [sqlite3_memory_highwater(1)] is the high-water mark -** prior to the reset. -*/ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void); -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); - -/* -** CAPI3REF: Pseudo-Random Number Generator -** -** SQLite contains a high-quality pseudo-random number generator (PRNG) used to -** select random [ROWID | ROWIDs] when inserting new records into a table that -** already uses the largest possible [ROWID]. The PRNG is also used for -** the build-in random() and randomblob() SQL functions. This interface allows -** applications to access the same PRNG for other purposes. -** -** ^A call to this routine stores N bytes of randomness into buffer P. -** -** ^The first time this routine is invoked (either internally or by -** the application) the PRNG is seeded using randomness obtained -** from the xRandomness method of the default [sqlite3_vfs] object. -** ^On all subsequent invocations, the pseudo-randomness is generated -** internally and without recourse to the [sqlite3_vfs] xRandomness -** method. -*/ -SQLITE_API void sqlite3_randomness(int N, void *P); - -/* -** CAPI3REF: Compile-Time Authorization Callbacks -** -** ^This routine registers an authorizer callback with a particular -** [database connection], supplied in the first argument. -** ^The authorizer callback is invoked as SQL statements are being compiled -** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], -** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. ^At various -** points during the compilation process, as logic is being created -** to perform various actions, the authorizer callback is invoked to -** see if those actions are allowed. ^The authorizer callback should -** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the -** specific action but allow the SQL statement to continue to be -** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be -** rejected with an error. ^If the authorizer callback returns -** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY] -** then the [sqlite3_prepare_v2()] or equivalent call that triggered -** the authorizer will fail with an error message. -** -** When the callback returns [SQLITE_OK], that means the operation -** requested is ok. ^When the callback returns [SQLITE_DENY], the -** [sqlite3_prepare_v2()] or equivalent call that triggered the -** authorizer will fail with an error message explaining that -** access is denied. -** -** ^The first parameter to the authorizer callback is a copy of the third -** parameter to the sqlite3_set_authorizer() interface. ^The second parameter -** to the callback is an integer [SQLITE_COPY | action code] that specifies -** the particular action to be authorized. ^The third through sixth parameters -** to the callback are zero-terminated strings that contain additional -** details about the action to be authorized. -** -** ^If the action code is [SQLITE_READ] -** and the callback returns [SQLITE_IGNORE] then the -** [prepared statement] statement is constructed to substitute -** a NULL value in place of the table column that would have -** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE] -** return can be used to deny an untrusted user access to individual -** columns of a table. -** ^If the action code is [SQLITE_DELETE] and the callback returns -** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the -** [truncate optimization] is disabled and all rows are deleted individually. -** -** An authorizer is used when [sqlite3_prepare | preparing] -** SQL statements from an untrusted source, to ensure that the SQL statements -** do not try to access data they are not allowed to see, or that they do not -** try to execute malicious statements that damage the database. For -** example, an application may allow a user to enter arbitrary -** SQL queries for evaluation by a database. But the application does -** not want the user to be able to make arbitrary changes to the -** database. An authorizer could then be put in place while the -** user-entered SQL is being [sqlite3_prepare | prepared] that -** disallows everything except [SELECT] statements. -** -** Applications that need to process SQL from untrusted sources -** might also consider lowering resource limits using [sqlite3_limit()] -** and limiting database size using the [max_page_count] [PRAGMA] -** in addition to using an authorizer. -** -** ^(Only a single authorizer can be in place on a database connection -** at a time. Each call to sqlite3_set_authorizer overrides the -** previous call.)^ ^Disable the authorizer by installing a NULL callback. -** The authorizer is disabled by default. -** -** The authorizer callback must not do anything that will modify -** the database connection that invoked the authorizer callback. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. -** -** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the -** statement might be re-prepared during [sqlite3_step()] due to a -** schema change. Hence, the application should ensure that the -** correct authorizer callback remains in place during the [sqlite3_step()]. -** -** ^Note that the authorizer callback is invoked only during -** [sqlite3_prepare()] or its variants. Authorization is not -** performed during statement evaluation in [sqlite3_step()], unless -** as stated in the previous paragraph, sqlite3_step() invokes -** sqlite3_prepare_v2() to reprepare a statement after a schema change. -*/ -SQLITE_API int sqlite3_set_authorizer( - sqlite3*, - int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), - void *pUserData -); - -/* -** CAPI3REF: Authorizer Return Codes -** -** The [sqlite3_set_authorizer | authorizer callback function] must -** return either [SQLITE_OK] or one of these two constants in order -** to signal SQLite whether or not the action is permitted. See the -** [sqlite3_set_authorizer | authorizer documentation] for additional -** information. -** -** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code] -** from the [sqlite3_vtab_on_conflict()] interface. -*/ -#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ -#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ - -/* -** CAPI3REF: Authorizer Action Codes -** -** The [sqlite3_set_authorizer()] interface registers a callback function -** that is invoked to authorize certain SQL statement actions. The -** second parameter to the callback is an integer code that specifies -** what action is being authorized. These are the integer action codes that -** the authorizer callback may be passed. -** -** These action code values signify what kind of operation is to be -** authorized. The 3rd and 4th parameters to the authorization -** callback function will be parameters or NULL depending on which of these -** codes is used as the second parameter. ^(The 5th parameter to the -** authorizer callback is the name of the database ("main", "temp", -** etc.) if applicable.)^ ^The 6th parameter to the authorizer callback -** is the name of the inner-most trigger or view that is responsible for -** the access attempt or NULL if this access attempt is directly from -** top-level SQL code. -*/ -/******************************************* 3rd ************ 4th ***********/ -#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ -#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ -#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ -#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ -#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ -#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ -#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ -#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ -#define SQLITE_DELETE 9 /* Table Name NULL */ -#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ -#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ -#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ -#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ -#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ -#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ -#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ -#define SQLITE_DROP_VIEW 17 /* View Name NULL */ -#define SQLITE_INSERT 18 /* Table Name NULL */ -#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ -#define SQLITE_READ 20 /* Table Name Column Name */ -#define SQLITE_SELECT 21 /* NULL NULL */ -#define SQLITE_TRANSACTION 22 /* Operation NULL */ -#define SQLITE_UPDATE 23 /* Table Name Column Name */ -#define SQLITE_ATTACH 24 /* Filename NULL */ -#define SQLITE_DETACH 25 /* Database Name NULL */ -#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */ -#define SQLITE_REINDEX 27 /* Index Name NULL */ -#define SQLITE_ANALYZE 28 /* Table Name NULL */ -#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ -#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ -#define SQLITE_FUNCTION 31 /* NULL Function Name */ -#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ -#define SQLITE_COPY 0 /* No longer used */ - -/* -** CAPI3REF: Tracing And Profiling Functions -** -** These routines register callback functions that can be used for -** tracing and profiling the execution of SQL statements. -** -** ^The callback function registered by sqlite3_trace() is invoked at -** various times when an SQL statement is being run by [sqlite3_step()]. -** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the -** SQL statement text as the statement first begins executing. -** ^(Additional sqlite3_trace() callbacks might occur -** as each triggered subprogram is entered. The callbacks for triggers -** contain a UTF-8 SQL comment that identifies the trigger.)^ -** -** ^The callback function registered by sqlite3_profile() is invoked -** as each SQL statement finishes. ^The profile callback contains -** the original statement text and an estimate of wall-clock time -** of how long that statement took to run. ^The profile callback -** time is in units of nanoseconds, however the current implementation -** is only capable of millisecond resolution so the six least significant -** digits in the time are meaningless. Future versions of SQLite -** might provide greater resolution on the profiler callback. The -** sqlite3_profile() function is considered experimental and is -** subject to change in future versions of SQLite. -*/ -SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); -SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, - void(*xProfile)(void*,const char*,sqlite3_uint64), void*); - -/* -** CAPI3REF: Query Progress Callbacks -** -** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback -** function X to be invoked periodically during long running calls to -** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for -** database connection D. An example use for this -** interface is to keep a GUI updated during a large query. -** -** ^The parameter P is passed through as the only parameter to the -** callback function X. ^The parameter N is the number of -** [virtual machine instructions] that are evaluated between successive -** invocations of the callback X. -** -** ^Only a single progress handler may be defined at one time per -** [database connection]; setting a new progress handler cancels the -** old one. ^Setting parameter X to NULL disables the progress handler. -** ^The progress handler is also disabled by setting N to a value less -** than 1. -** -** ^If the progress callback returns non-zero, the operation is -** interrupted. This feature can be used to implement a -** "Cancel" button on a GUI progress dialog box. -** -** The progress handler callback must not do anything that will modify -** the database connection that invoked the progress handler. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. -** -*/ -SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - -/* -** CAPI3REF: Opening A New Database Connection -** -** ^These routines open an SQLite database file as specified by the -** filename argument. ^The filename argument is interpreted as UTF-8 for -** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte -** order for sqlite3_open16(). ^(A [database connection] handle is usually -** returned in *ppDb, even if an error occurs. The only exception is that -** if SQLite is unable to allocate memory to hold the [sqlite3] object, -** a NULL will be written into *ppDb instead of a pointer to the [sqlite3] -** object.)^ ^(If the database is opened (and/or created) successfully, then -** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The -** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain -** an English language description of the error following a failure of any -** of the sqlite3_open() routines. -** -** ^The default encoding for the database will be UTF-8 if -** sqlite3_open() or sqlite3_open_v2() is called and -** UTF-16 in the native byte order if sqlite3_open16() is used. -** -** Whether or not an error occurs when it is opened, resources -** associated with the [database connection] handle should be released by -** passing it to [sqlite3_close()] when it is no longer required. -** -** The sqlite3_open_v2() interface works like sqlite3_open() -** except that it accepts two additional parameters for additional control -** over the new database connection. ^(The flags parameter to -** sqlite3_open_v2() can take one of -** the following three values, optionally combined with the -** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE], -** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^ -** -**
    -** ^(
    [SQLITE_OPEN_READONLY]
    -**
    The database is opened in read-only mode. If the database does not -** already exist, an error is returned.
    )^ -** -** ^(
    [SQLITE_OPEN_READWRITE]
    -**
    The database is opened for reading and writing if possible, or reading -** only if the file is write protected by the operating system. In either -** case the database must already exist, otherwise an error is returned.
    )^ -** -** ^(
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    -**
    The database is opened for reading and writing, and is created if -** it does not already exist. This is the behavior that is always used for -** sqlite3_open() and sqlite3_open16().
    )^ -**
    -** -** If the 3rd parameter to sqlite3_open_v2() is not one of the -** combinations shown above optionally combined with other -** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] -** then the behavior is undefined. -** -** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection -** opens in the multi-thread [threading mode] as long as the single-thread -** mode has not been set at compile-time or start-time. ^If the -** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens -** in the serialized [threading mode] unless single-thread was -** previously selected at compile-time or start-time. -** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be -** eligible to use [shared cache mode], regardless of whether or not shared -** cache is enabled using [sqlite3_enable_shared_cache()]. ^The -** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not -** participate in [shared cache mode] even if it is enabled. -** -** ^The fourth parameter to sqlite3_open_v2() is the name of the -** [sqlite3_vfs] object that defines the operating system interface that -** the new database connection should use. ^If the fourth parameter is -** a NULL pointer then the default [sqlite3_vfs] object is used. -** -** ^If the filename is ":memory:", then a private, temporary in-memory database -** is created for the connection. ^This in-memory database will vanish when -** the database connection is closed. Future versions of SQLite might -** make use of additional special filenames that begin with the ":" character. -** It is recommended that when a database filename actually does begin with -** a ":" character you should prefix the filename with a pathname such as -** "./" to avoid ambiguity. -** -** ^If the filename is an empty string, then a private, temporary -** on-disk database will be created. ^This private database will be -** automatically deleted as soon as the database connection is closed. -** -** [[URI filenames in sqlite3_open()]]

    URI Filenames

    -** -** ^If [URI filename] interpretation is enabled, and the filename argument -** begins with "file:", then the filename is interpreted as a URI. ^URI -** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is -** set in the fourth argument to sqlite3_open_v2(), or if it has -** been enabled globally using the [SQLITE_CONFIG_URI] option with the -** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. -** As of SQLite version 3.7.7, URI filename interpretation is turned off -** by default, but future releases of SQLite might enable URI filename -** interpretation by default. See "[URI filenames]" for additional -** information. -** -** URI filenames are parsed according to RFC 3986. ^If the URI contains an -** authority, then it must be either an empty string or the string -** "localhost". ^If the authority is not an empty string or "localhost", an -** error is returned to the caller. ^The fragment component of a URI, if -** present, is ignored. -** -** ^SQLite uses the path component of the URI as the name of the disk file -** which contains the database. ^If the path begins with a '/' character, -** then it is interpreted as an absolute path. ^If the path does not begin -** with a '/' (meaning that the authority section is omitted from the URI) -** then the path is interpreted as a relative path. -** ^On windows, the first component of an absolute path -** is a drive specification (e.g. "C:"). -** -** [[core URI query parameters]] -** The query component of a URI may contain parameters that are interpreted -** either by SQLite itself, or by a [VFS | custom VFS implementation]. -** SQLite interprets the following three query parameters: -** -**
      -**
    • vfs: ^The "vfs" parameter may be used to specify the name of -** a VFS object that provides the operating system interface that should -** be used to access the database file on disk. ^If this option is set to -** an empty string the default VFS object is used. ^Specifying an unknown -** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is -** present, then the VFS specified by the option takes precedence over -** the value passed as the fourth parameter to sqlite3_open_v2(). -** -**
    • mode: ^(The mode parameter may be set to either "ro", "rw", -** "rwc", or "memory". Attempting to set it to any other value is -** an error)^. -** ^If "ro" is specified, then the database is opened for read-only -** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the -** third argument to sqlite3_open_v2(). ^If the mode option is set to -** "rw", then the database is opened for read-write (but not create) -** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had -** been set. ^Value "rwc" is equivalent to setting both -** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is -** set to "memory" then a pure [in-memory database] that never reads -** or writes from disk is used. ^It is an error to specify a value for -** the mode parameter that is less restrictive than that specified by -** the flags passed in the third parameter to sqlite3_open_v2(). -** -**
    • cache: ^The cache parameter may be set to either "shared" or -** "private". ^Setting it to "shared" is equivalent to setting the -** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to -** sqlite3_open_v2(). ^Setting the cache parameter to "private" is -** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. -** ^If sqlite3_open_v2() is used and the "cache" parameter is present in -** a URI filename, its value overrides any behavior requested by setting -** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. -**
    -** -** ^Specifying an unknown parameter in the query component of a URI is not an -** error. Future versions of SQLite might understand additional query -** parameters. See "[query parameters with special meaning to SQLite]" for -** additional information. -** -** [[URI filename examples]]

    URI filename examples

    -** -**
    -**
    URI filenames Results -**
    file:data.db -** Open the file "data.db" in the current directory. -**
    file:/home/fred/data.db
    -** file:///home/fred/data.db
    -** file://localhost/home/fred/data.db
    -** Open the database file "/home/fred/data.db". -**
    file://darkstar/home/fred/data.db -** An error. "darkstar" is not a recognized authority. -**
    -** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db -** Windows only: Open the file "data.db" on fred's desktop on drive -** C:. Note that the %20 escaping in this example is not strictly -** necessary - space characters can be used literally -** in URI filenames. -**
    file:data.db?mode=ro&cache=private -** Open file "data.db" in the current directory for read-only access. -** Regardless of whether or not shared-cache mode is enabled by -** default, use a private cache. -**
    file:/home/fred/data.db?vfs=unix-nolock -** Open file "/home/fred/data.db". Use the special VFS "unix-nolock". -**
    file:data.db?mode=readonly -** An error. "readonly" is not a valid option for the "mode" parameter. -**
    -** -** ^URI hexadecimal escape sequences (%HH) are supported within the path and -** query components of a URI. A hexadecimal escape sequence consists of a -** percent sign - "%" - followed by exactly two hexadecimal digits -** specifying an octet value. ^Before the path or query components of a -** URI filename are interpreted, they are encoded using UTF-8 and all -** hexadecimal escape sequences replaced by a single byte containing the -** corresponding octet. If this process generates an invalid UTF-8 encoding, -** the results are undefined. -** -** Note to Windows users: The encoding used for the filename argument -** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever -** codepage is currently defined. Filenames containing international -** characters must be converted to UTF-8 prior to passing them into -** sqlite3_open() or sqlite3_open_v2(). -** -** Note to Windows Runtime users: The temporary directory must be set -** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various -** features that require the use of temporary files may fail. -** -** See also: [sqlite3_temp_directory] -*/ -SQLITE_API int sqlite3_open( - const char *filename, /* Database filename (UTF-8) */ - sqlite3 **ppDb /* OUT: SQLite db handle */ -); -SQLITE_API int sqlite3_open16( - const void *filename, /* Database filename (UTF-16) */ - sqlite3 **ppDb /* OUT: SQLite db handle */ -); -SQLITE_API int sqlite3_open_v2( - const char *filename, /* Database filename (UTF-8) */ - sqlite3 **ppDb, /* OUT: SQLite db handle */ - int flags, /* Flags */ - const char *zVfs /* Name of VFS module to use */ -); - -/* -** CAPI3REF: Obtain Values For URI Parameters -** -** These are utility routines, useful to VFS implementations, that check -** to see if a database file was a URI that contained a specific query -** parameter, and if so obtains the value of that query parameter. -** -** If F is the database filename pointer passed into the xOpen() method of -** a VFS implementation when the flags parameter to xOpen() has one or -** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and -** P is the name of the query parameter, then -** sqlite3_uri_parameter(F,P) returns the value of the P -** parameter if it exists or a NULL pointer if P does not appear as a -** query parameter on F. If P is a query parameter of F -** has no explicit value, then sqlite3_uri_parameter(F,P) returns -** a pointer to an empty string. -** -** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean -** parameter and returns true (1) or false (0) according to the value -** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the -** value of query parameter P is one of "yes", "true", or "on" in any -** case or if the value begins with a non-zero number. The -** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of -** query parameter P is one of "no", "false", or "off" in any case or -** if the value begins with a numeric zero. If P is not a query -** parameter on F or if the value of P is does not match any of the -** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0). -** -** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a -** 64-bit signed integer and returns that integer, or D if P does not -** exist. If the value of P is something other than an integer, then -** zero is returned. -** -** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and -** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and -** is not a database file pathname pointer that SQLite passed into the xOpen -** VFS method, then the behavior of this routine is undefined and probably -** undesirable. -*/ -SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); -SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); -SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); - - -/* -** CAPI3REF: Error Codes And Messages -** -** ^The sqlite3_errcode() interface returns the numeric [result code] or -** [extended result code] for the most recent failed sqlite3_* API call -** associated with a [database connection]. If a prior API call failed -** but the most recent API call succeeded, the return value from -** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() -** interface is the same except that it always returns the -** [extended result code] even when extended result codes are -** disabled. -** -** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language -** text that describes the error, as either UTF-8 or UTF-16 respectively. -** ^(Memory to hold the error message string is managed internally. -** The application does not need to worry about freeing the result. -** However, the error string might be overwritten or deallocated by -** subsequent calls to other SQLite interface functions.)^ -** -** ^The sqlite3_errstr() interface returns the English-language text -** that describes the [result code], as UTF-8. -** ^(Memory to hold the error message string is managed internally -** and must not be freed by the application)^. -** -** When the serialized [threading mode] is in use, it might be the -** case that a second error occurs on a separate thread in between -** the time of the first error and the call to these interfaces. -** When that happens, the second error will be reported since these -** interfaces always report the most recent result. To avoid -** this, each thread can obtain exclusive use of the [database connection] D -** by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning -** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after -** all calls to the interfaces listed here are completed. -** -** If an interface fails with SQLITE_MISUSE, that means the interface -** was invoked incorrectly by the application. In that case, the -** error code and message may or may not be set. -*/ -SQLITE_API int sqlite3_errcode(sqlite3 *db); -SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); -SQLITE_API const char *sqlite3_errmsg(sqlite3*); -SQLITE_API const void *sqlite3_errmsg16(sqlite3*); -SQLITE_API const char *sqlite3_errstr(int); - -/* -** CAPI3REF: SQL Statement Object -** KEYWORDS: {prepared statement} {prepared statements} -** -** An instance of this object represents a single SQL statement. -** This object is variously known as a "prepared statement" or a -** "compiled SQL statement" or simply as a "statement". -** -** The life of a statement object goes something like this: -** -**
      -**
    1. Create the object using [sqlite3_prepare_v2()] or a related -** function. -**
    2. Bind values to [host parameters] using the sqlite3_bind_*() -** interfaces. -**
    3. Run the SQL by calling [sqlite3_step()] one or more times. -**
    4. Reset the statement using [sqlite3_reset()] then go back -** to step 2. Do this zero or more times. -**
    5. Destroy the object using [sqlite3_finalize()]. -**
    -** -** Refer to documentation on individual methods above for additional -** information. -*/ -typedef struct sqlite3_stmt sqlite3_stmt; - -/* -** CAPI3REF: Run-time Limits -** -** ^(This interface allows the size of various constructs to be limited -** on a connection by connection basis. The first parameter is the -** [database connection] whose limit is to be set or queried. The -** second parameter is one of the [limit categories] that define a -** class of constructs to be size limited. The third parameter is the -** new limit for that construct.)^ -** -** ^If the new limit is a negative number, the limit is unchanged. -** ^(For each limit category SQLITE_LIMIT_NAME there is a -** [limits | hard upper bound] -** set at compile-time by a C preprocessor macro called -** [limits | SQLITE_MAX_NAME]. -** (The "_LIMIT_" in the name is changed to "_MAX_".))^ -** ^Attempts to increase a limit above its hard upper bound are -** silently truncated to the hard upper bound. -** -** ^Regardless of whether or not the limit was changed, the -** [sqlite3_limit()] interface returns the prior value of the limit. -** ^Hence, to find the current value of a limit without changing it, -** simply invoke this interface with the third parameter set to -1. -** -** Run-time limits are intended for use in applications that manage -** both their own internal database and also databases that are controlled -** by untrusted external sources. An example application might be a -** web browser that has its own databases for storing history and -** separate databases controlled by JavaScript applications downloaded -** off the Internet. The internal databases can be given the -** large, default limits. Databases managed by external sources can -** be given much smaller limits designed to prevent a denial of service -** attack. Developers might also want to use the [sqlite3_set_authorizer()] -** interface to further control untrusted SQL. The size of the database -** created by an untrusted script can be contained using the -** [max_page_count] [PRAGMA]. -** -** New run-time limit categories may be added in future releases. -*/ -SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); - -/* -** CAPI3REF: Run-Time Limit Categories -** KEYWORDS: {limit category} {*limit categories} -** -** These constants define various performance limits -** that can be lowered at run-time using [sqlite3_limit()]. -** The synopsis of the meanings of the various limits is shown below. -** Additional information is available at [limits | Limits in SQLite]. -** -**
    -** [[SQLITE_LIMIT_LENGTH]] ^(
    SQLITE_LIMIT_LENGTH
    -**
    The maximum size of any string or BLOB or table row, in bytes.
    )^ -** -** [[SQLITE_LIMIT_SQL_LENGTH]] ^(
    SQLITE_LIMIT_SQL_LENGTH
    -**
    The maximum length of an SQL statement, in bytes.
    )^ -** -** [[SQLITE_LIMIT_COLUMN]] ^(
    SQLITE_LIMIT_COLUMN
    -**
    The maximum number of columns in a table definition or in the -** result set of a [SELECT] or the maximum number of columns in an index -** or in an ORDER BY or GROUP BY clause.
    )^ -** -** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(
    SQLITE_LIMIT_EXPR_DEPTH
    -**
    The maximum depth of the parse tree on any expression.
    )^ -** -** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(
    SQLITE_LIMIT_COMPOUND_SELECT
    -**
    The maximum number of terms in a compound SELECT statement.
    )^ -** -** [[SQLITE_LIMIT_VDBE_OP]] ^(
    SQLITE_LIMIT_VDBE_OP
    -**
    The maximum number of instructions in a virtual machine program -** used to implement an SQL statement. This limit is not currently -** enforced, though that might be added in some future release of -** SQLite.
    )^ -** -** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(
    SQLITE_LIMIT_FUNCTION_ARG
    -**
    The maximum number of arguments on a function.
    )^ -** -** [[SQLITE_LIMIT_ATTACHED]] ^(
    SQLITE_LIMIT_ATTACHED
    -**
    The maximum number of [ATTACH | attached databases].)^
    -** -** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]] -** ^(
    SQLITE_LIMIT_LIKE_PATTERN_LENGTH
    -**
    The maximum length of the pattern argument to the [LIKE] or -** [GLOB] operators.
    )^ -** -** [[SQLITE_LIMIT_VARIABLE_NUMBER]] -** ^(
    SQLITE_LIMIT_VARIABLE_NUMBER
    -**
    The maximum index number of any [parameter] in an SQL statement.)^ -** -** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(
    SQLITE_LIMIT_TRIGGER_DEPTH
    -**
    The maximum depth of recursion for triggers.
    )^ -**
    -*/ -#define SQLITE_LIMIT_LENGTH 0 -#define SQLITE_LIMIT_SQL_LENGTH 1 -#define SQLITE_LIMIT_COLUMN 2 -#define SQLITE_LIMIT_EXPR_DEPTH 3 -#define SQLITE_LIMIT_COMPOUND_SELECT 4 -#define SQLITE_LIMIT_VDBE_OP 5 -#define SQLITE_LIMIT_FUNCTION_ARG 6 -#define SQLITE_LIMIT_ATTACHED 7 -#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 -#define SQLITE_LIMIT_VARIABLE_NUMBER 9 -#define SQLITE_LIMIT_TRIGGER_DEPTH 10 - -/* -** CAPI3REF: Compiling An SQL Statement -** KEYWORDS: {SQL statement compiler} -** -** To execute an SQL query, it must first be compiled into a byte-code -** program using one of these routines. -** -** The first argument, "db", is a [database connection] obtained from a -** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or -** [sqlite3_open16()]. The database connection must not have been closed. -** -** The second argument, "zSql", is the statement to be compiled, encoded -** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2() -** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() -** use UTF-16. -** -** ^If the nByte argument is less than zero, then zSql is read up to the -** first zero terminator. ^If nByte is non-negative, then it is the maximum -** number of bytes read from zSql. ^When nByte is non-negative, the -** zSql string ends at either the first '\000' or '\u0000' character or -** the nByte-th byte, whichever comes first. If the caller knows -** that the supplied string is nul-terminated, then there is a small -** performance advantage to be gained by passing an nByte parameter that -** is equal to the number of bytes in the input string including -** the nul-terminator bytes as this saves SQLite from having to -** make a copy of the input string. -** -** ^If pzTail is not NULL then *pzTail is made to point to the first byte -** past the end of the first SQL statement in zSql. These routines only -** compile the first statement in zSql, so *pzTail is left pointing to -** what remains uncompiled. -** -** ^*ppStmt is left pointing to a compiled [prepared statement] that can be -** executed using [sqlite3_step()]. ^If there is an error, *ppStmt is set -** to NULL. ^If the input text contains no SQL (if the input is an empty -** string or a comment) then *ppStmt is set to NULL. -** The calling procedure is responsible for deleting the compiled -** SQL statement using [sqlite3_finalize()] after it has finished with it. -** ppStmt may not be NULL. -** -** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; -** otherwise an [error code] is returned. -** -** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are -** recommended for all new programs. The two older interfaces are retained -** for backwards compatibility, but their use is discouraged. -** ^In the "v2" interfaces, the prepared statement -** that is returned (the [sqlite3_stmt] object) contains a copy of the -** original SQL text. This causes the [sqlite3_step()] interface to -** behave differently in three ways: -** -**
      -**
    1. -** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it -** always used to do, [sqlite3_step()] will automatically recompile the SQL -** statement and try to run it again. -**
    2. -** -**
    3. -** ^When an error occurs, [sqlite3_step()] will return one of the detailed -** [error codes] or [extended error codes]. ^The legacy behavior was that -** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code -** and the application would have to make a second call to [sqlite3_reset()] -** in order to find the underlying cause of the problem. With the "v2" prepare -** interfaces, the underlying reason for the error is returned immediately. -**
    4. -** -**
    5. -** ^If the specific value bound to [parameter | host parameter] in the -** WHERE clause might influence the choice of query plan for a statement, -** then the statement will be automatically recompiled, as if there had been -** a schema change, on the first [sqlite3_step()] call following any change -** to the [sqlite3_bind_text | bindings] of that [parameter]. -** ^The specific value of WHERE-clause [parameter] might influence the -** choice of query plan if the parameter is the left-hand side of a [LIKE] -** or [GLOB] operator or if the parameter is compared to an indexed column -** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. -** the -**
    6. -**
    -*/ -SQLITE_API int sqlite3_prepare( - sqlite3 *db, /* Database handle */ - const char *zSql, /* SQL statement, UTF-8 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const char **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare_v2( - sqlite3 *db, /* Database handle */ - const char *zSql, /* SQL statement, UTF-8 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const char **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare16( - sqlite3 *db, /* Database handle */ - const void *zSql, /* SQL statement, UTF-16 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const void **pzTail /* OUT: Pointer to unused portion of zSql */ -); -SQLITE_API int sqlite3_prepare16_v2( - sqlite3 *db, /* Database handle */ - const void *zSql, /* SQL statement, UTF-16 encoded */ - int nByte, /* Maximum length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: Statement handle */ - const void **pzTail /* OUT: Pointer to unused portion of zSql */ -); - -/* -** CAPI3REF: Retrieving Statement SQL -** -** ^This interface can be used to retrieve a saved copy of the original -** SQL text used to create a [prepared statement] if that statement was -** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. -*/ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Determine If An SQL Statement Writes The Database -** -** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if -** and only if the [prepared statement] X makes no direct changes to -** the content of the database file. -** -** Note that [application-defined SQL functions] or -** [virtual tables] might change the database indirectly as a side effect. -** ^(For example, if an application defines a function "eval()" that -** calls [sqlite3_exec()], then the following SQL statement would -** change the database file through side-effects: -** -**
    -**    SELECT eval('DELETE FROM t1') FROM t2;
    -** 
    -** -** But because the [SELECT] statement does not change the database file -** directly, sqlite3_stmt_readonly() would still return true.)^ -** -** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK], -** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, -** since the statements themselves do not actually modify the database but -** rather they control the timing of when other statements modify the -** database. ^The [ATTACH] and [DETACH] statements also cause -** sqlite3_stmt_readonly() to return true since, while those statements -** change the configuration of a database connection, they do not make -** changes to the content of the database files on disk. -*/ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Determine If A Prepared Statement Has Been Reset -** -** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the -** [prepared statement] S has been stepped at least once using -** [sqlite3_step(S)] but has not run to completion and/or has not -** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) -** interface returns false if S is a NULL pointer. If S is not a -** NULL pointer and is not a pointer to a valid [prepared statement] -** object, then the behavior is undefined and probably undesirable. -** -** This interface can be used in combination [sqlite3_next_stmt()] -** to locate all prepared statements associated with a database -** connection that are in need of being reset. This can be used, -** for example, in diagnostic routines to search for prepared -** statements that are holding a transaction open. -*/ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); - -/* -** CAPI3REF: Dynamically Typed Value Object -** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} -** -** SQLite uses the sqlite3_value object to represent all values -** that can be stored in a database table. SQLite uses dynamic typing -** for the values it stores. ^Values stored in sqlite3_value objects -** can be integers, floating point values, strings, BLOBs, or NULL. -** -** An sqlite3_value object may be either "protected" or "unprotected". -** Some interfaces require a protected sqlite3_value. Other interfaces -** will accept either a protected or an unprotected sqlite3_value. -** Every interface that accepts sqlite3_value arguments specifies -** whether or not it requires a protected sqlite3_value. -** -** The terms "protected" and "unprotected" refer to whether or not -** a mutex is held. An internal mutex is held for a protected -** sqlite3_value object but no mutex is held for an unprotected -** sqlite3_value object. If SQLite is compiled to be single-threaded -** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) -** or if SQLite is run in one of reduced mutex modes -** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] -** then there is no distinction between protected and unprotected -** sqlite3_value objects and they can be used interchangeably. However, -** for maximum code portability it is recommended that applications -** still make the distinction between protected and unprotected -** sqlite3_value objects even when not strictly required. -** -** ^The sqlite3_value objects that are passed as parameters into the -** implementation of [application-defined SQL functions] are protected. -** ^The sqlite3_value object returned by -** [sqlite3_column_value()] is unprotected. -** Unprotected sqlite3_value objects may only be used with -** [sqlite3_result_value()] and [sqlite3_bind_value()]. -** The [sqlite3_value_blob | sqlite3_value_type()] family of -** interfaces require protected sqlite3_value objects. -*/ -typedef struct Mem sqlite3_value; - -/* -** CAPI3REF: SQL Function Context Object -** -** The context in which an SQL function executes is stored in an -** sqlite3_context object. ^A pointer to an sqlite3_context object -** is always first parameter to [application-defined SQL functions]. -** The application-defined SQL function implementation will pass this -** pointer through into calls to [sqlite3_result_int | sqlite3_result()], -** [sqlite3_aggregate_context()], [sqlite3_user_data()], -** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()], -** and/or [sqlite3_set_auxdata()]. -*/ -typedef struct sqlite3_context sqlite3_context; - -/* -** CAPI3REF: Binding Values To Prepared Statements -** KEYWORDS: {host parameter} {host parameters} {host parameter name} -** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} -** -** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, -** literals may be replaced by a [parameter] that matches one of following -** templates: -** -**
      -**
    • ? -**
    • ?NNN -**
    • :VVV -**
    • @VVV -**
    • $VVV -**
    -** -** In the templates above, NNN represents an integer literal, -** and VVV represents an alphanumeric identifier.)^ ^The values of these -** parameters (also called "host parameter names" or "SQL parameters") -** can be set using the sqlite3_bind_*() routines defined here. -** -** ^The first argument to the sqlite3_bind_*() routines is always -** a pointer to the [sqlite3_stmt] object returned from -** [sqlite3_prepare_v2()] or its variants. -** -** ^The second argument is the index of the SQL parameter to be set. -** ^The leftmost SQL parameter has an index of 1. ^When the same named -** SQL parameter is used more than once, second and subsequent -** occurrences have the same index as the first occurrence. -** ^The index for named parameters can be looked up using the -** [sqlite3_bind_parameter_index()] API if desired. ^The index -** for "?NNN" parameters is the value of NNN. -** ^The NNN value must be between 1 and the [sqlite3_limit()] -** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). -** -** ^The third argument is the value to bind to the parameter. -** -** ^(In those routines that have a fourth argument, its value is the -** number of bytes in the parameter. To be clear: the value is the -** number of bytes in the value, not the number of characters.)^ -** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() -** is negative, then the length of the string is -** the number of bytes up to the first zero terminator. -** If the fourth parameter to sqlite3_bind_blob() is negative, then -** the behavior is undefined. -** If a non-negative fourth parameter is provided to sqlite3_bind_text() -** or sqlite3_bind_text16() then that parameter must be the byte offset -** where the NUL terminator would occur assuming the string were NUL -** terminated. If any NUL characters occur at byte offsets less than -** the value of the fourth parameter then the resulting string value will -** contain embedded NULs. The result of expressions involving strings -** with embedded NULs is undefined. -** -** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and -** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), -** sqlite3_bind_text(), or sqlite3_bind_text16() fails. -** ^If the fifth argument is -** the special value [SQLITE_STATIC], then SQLite assumes that the -** information is in static, unmanaged space and does not need to be freed. -** ^If the fifth argument has the value [SQLITE_TRANSIENT], then -** SQLite makes its own private copy of the data immediately, before -** the sqlite3_bind_*() routine returns. -** -** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that -** is filled with zeroes. ^A zeroblob uses a fixed amount of memory -** (just an integer to hold its size) while it is being processed. -** Zeroblobs are intended to serve as placeholders for BLOBs whose -** content is later written using -** [sqlite3_blob_open | incremental BLOB I/O] routines. -** ^A negative value for the zeroblob results in a zero-length BLOB. -** -** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer -** for the [prepared statement] or with a prepared statement for which -** [sqlite3_step()] has been called more recently than [sqlite3_reset()], -** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() -** routine is passed a [prepared statement] that has been finalized, the -** result is undefined and probably harmful. -** -** ^Bindings are not cleared by the [sqlite3_reset()] routine. -** ^Unbound parameters are interpreted as NULL. -** -** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an -** [error code] if anything goes wrong. -** ^[SQLITE_RANGE] is returned if the parameter -** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. -** -** See also: [sqlite3_bind_parameter_count()], -** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); -SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); -SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); -SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); -SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); -SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); -SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); - -/* -** CAPI3REF: Number Of SQL Parameters -** -** ^This routine can be used to find the number of [SQL parameters] -** in a [prepared statement]. SQL parameters are tokens of the -** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as -** placeholders for values that are [sqlite3_bind_blob | bound] -** to the parameters at a later time. -** -** ^(This routine actually returns the index of the largest (rightmost) -** parameter. For all forms except ?NNN, this will correspond to the -** number of unique parameters. If parameters of the ?NNN form are used, -** there may be gaps in the list.)^ -** -** See also: [sqlite3_bind_blob|sqlite3_bind()], -** [sqlite3_bind_parameter_name()], and -** [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); - -/* -** CAPI3REF: Name Of A Host Parameter -** -** ^The sqlite3_bind_parameter_name(P,N) interface returns -** the name of the N-th [SQL parameter] in the [prepared statement] P. -** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" -** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" -** respectively. -** In other words, the initial ":" or "$" or "@" or "?" -** is included as part of the name.)^ -** ^Parameters of the form "?" without a following integer have no name -** and are referred to as "nameless" or "anonymous parameters". -** -** ^The first host parameter has an index of 1, not 0. -** -** ^If the value N is out of range or if the N-th parameter is -** nameless, then NULL is returned. ^The returned string is -** always in UTF-8 encoding even if the named parameter was -** originally specified as UTF-16 in [sqlite3_prepare16()] or -** [sqlite3_prepare16_v2()]. -** -** See also: [sqlite3_bind_blob|sqlite3_bind()], -** [sqlite3_bind_parameter_count()], and -** [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); - -/* -** CAPI3REF: Index Of A Parameter With A Given Name -** -** ^Return the index of an SQL parameter given its name. ^The -** index value returned is suitable for use as the second -** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero -** is returned if no matching parameter is found. ^The parameter -** name must be given in UTF-8 even if the original statement -** was prepared from UTF-16 text using [sqlite3_prepare16_v2()]. -** -** See also: [sqlite3_bind_blob|sqlite3_bind()], -** [sqlite3_bind_parameter_count()], and -** [sqlite3_bind_parameter_index()]. -*/ -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); - -/* -** CAPI3REF: Reset All Bindings On A Prepared Statement -** -** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset -** the [sqlite3_bind_blob | bindings] on a [prepared statement]. -** ^Use this routine to reset all host parameters to NULL. -*/ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); - -/* -** CAPI3REF: Number Of Columns In A Result Set -** -** ^Return the number of columns in the result set returned by the -** [prepared statement]. ^This routine returns 0 if pStmt is an SQL -** statement that does not return data (for example an [UPDATE]). -** -** See also: [sqlite3_data_count()] -*/ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Column Names In A Result Set -** -** ^These routines return the name assigned to a particular column -** in the result set of a [SELECT] statement. ^The sqlite3_column_name() -** interface returns a pointer to a zero-terminated UTF-8 string -** and sqlite3_column_name16() returns a pointer to a zero-terminated -** UTF-16 string. ^The first parameter is the [prepared statement] -** that implements the [SELECT] statement. ^The second parameter is the -** column number. ^The leftmost column is number 0. -** -** ^The returned string pointer is valid until either the [prepared statement] -** is destroyed by [sqlite3_finalize()] or until the statement is automatically -** reprepared by the first call to [sqlite3_step()] for a particular run -** or until the next call to -** sqlite3_column_name() or sqlite3_column_name16() on the same column. -** -** ^If sqlite3_malloc() fails during the processing of either routine -** (for example during a conversion from UTF-8 to UTF-16) then a -** NULL pointer is returned. -** -** ^The name of a result column is the value of the "AS" clause for -** that column, if there is an AS clause. If there is no AS clause -** then the name of the column is unspecified and may change from -** one release of SQLite to the next. -*/ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); - -/* -** CAPI3REF: Source Of Data In A Query Result -** -** ^These routines provide a means to determine the database, table, and -** table column that is the origin of a particular result column in -** [SELECT] statement. -** ^The name of the database or table or column can be returned as -** either a UTF-8 or UTF-16 string. ^The _database_ routines return -** the database name, the _table_ routines return the table name, and -** the origin_ routines return the column name. -** ^The returned string is valid until the [prepared statement] is destroyed -** using [sqlite3_finalize()] or until the statement is automatically -** reprepared by the first call to [sqlite3_step()] for a particular run -** or until the same information is requested -** again in a different encoding. -** -** ^The names returned are the original un-aliased names of the -** database, table, and column. -** -** ^The first argument to these interfaces is a [prepared statement]. -** ^These functions return information about the Nth result column returned by -** the statement, where N is the second function argument. -** ^The left-most column is column 0 for these routines. -** -** ^If the Nth column returned by the statement is an expression or -** subquery and is not a column value, then all of these functions return -** NULL. ^These routine might also return NULL if a memory allocation error -** occurs. ^Otherwise, they return the name of the attached database, table, -** or column that query result column was extracted from. -** -** ^As with all other SQLite APIs, those whose names end with "16" return -** UTF-16 encoded strings and the other functions return UTF-8. -** -** ^These APIs are only available if the library was compiled with the -** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol. -** -** If two or more threads call one or more of these routines against the same -** prepared statement and column at the same time then the results are -** undefined. -** -** If two or more threads call one or more -** [sqlite3_column_database_name | column metadata interfaces] -** for the same [prepared statement] and result column -** at the same time then the results are undefined. -*/ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); - -/* -** CAPI3REF: Declared Datatype Of A Query Result -** -** ^(The first parameter is a [prepared statement]. -** If this statement is a [SELECT] statement and the Nth column of the -** returned result set of that [SELECT] is a table column (not an -** expression or subquery) then the declared type of the table -** column is returned.)^ ^If the Nth column of the result set is an -** expression or subquery, then a NULL pointer is returned. -** ^The returned string is always UTF-8 encoded. -** -** ^(For example, given the database schema: -** -** CREATE TABLE t1(c1 VARIANT); -** -** and the following statement to be compiled: -** -** SELECT c1 + 1, c1 FROM t1; -** -** this routine would return the string "VARIANT" for the second result -** column (i==1), and a NULL pointer for the first result column (i==0).)^ -** -** ^SQLite uses dynamic run-time typing. ^So just because a column -** is declared to contain a particular type does not mean that the -** data stored in that column is of the declared type. SQLite is -** strongly typed, but the typing is dynamic not static. ^Type -** is associated with individual values, not with the containers -** used to hold those values. -*/ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); - -/* -** CAPI3REF: Evaluate An SQL Statement -** -** After a [prepared statement] has been prepared using either -** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy -** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function -** must be called one or more times to evaluate the statement. -** -** The details of the behavior of the sqlite3_step() interface depend -** on whether the statement was prepared using the newer "v2" interface -** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy -** interface [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the -** new "v2" interface is recommended for new applications but the legacy -** interface will continue to be supported. -** -** ^In the legacy interface, the return value will be either [SQLITE_BUSY], -** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. -** ^With the "v2" interface, any of the other [result codes] or -** [extended result codes] might be returned as well. -** -** ^[SQLITE_BUSY] means that the database engine was unable to acquire the -** database locks it needs to do its job. ^If the statement is a [COMMIT] -** or occurs outside of an explicit transaction, then you can retry the -** statement. If the statement is not a [COMMIT] and occurs within an -** explicit transaction then you should rollback the transaction before -** continuing. -** -** ^[SQLITE_DONE] means that the statement has finished executing -** successfully. sqlite3_step() should not be called again on this virtual -** machine without first calling [sqlite3_reset()] to reset the virtual -** machine back to its initial state. -** -** ^If the SQL statement being executed returns any data, then [SQLITE_ROW] -** is returned each time a new row of data is ready for processing by the -** caller. The values may be accessed using the [column access functions]. -** sqlite3_step() is called again to retrieve the next row of data. -** -** ^[SQLITE_ERROR] means that a run-time error (such as a constraint -** violation) has occurred. sqlite3_step() should not be called again on -** the VM. More information may be found by calling [sqlite3_errmsg()]. -** ^With the legacy interface, a more specific error code (for example, -** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) -** can be obtained by calling [sqlite3_reset()] on the -** [prepared statement]. ^In the "v2" interface, -** the more specific error code is returned directly by sqlite3_step(). -** -** [SQLITE_MISUSE] means that the this routine was called inappropriately. -** Perhaps it was called on a [prepared statement] that has -** already been [sqlite3_finalize | finalized] or on one that had -** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could -** be the case that the same database connection is being used by two or -** more threads at the same moment in time. -** -** For all versions of SQLite up to and including 3.6.23.1, a call to -** [sqlite3_reset()] was required after sqlite3_step() returned anything -** other than [SQLITE_ROW] before any subsequent invocation of -** sqlite3_step(). Failure to reset the prepared statement using -** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from -** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began -** calling [sqlite3_reset()] automatically in this circumstance rather -** than returning [SQLITE_MISUSE]. This is not considered a compatibility -** break because any application that ever receives an SQLITE_MISUSE error -** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option -** can be used to restore the legacy behavior. -** -** Goofy Interface Alert: In the legacy interface, the sqlite3_step() -** API always returns a generic error code, [SQLITE_ERROR], following any -** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call -** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the -** specific [error codes] that better describes the error. -** We admit that this is a goofy design. The problem has been fixed -** with the "v2" interface. If you prepare all of your SQL statements -** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead -** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, -** then the more specific [error codes] are returned directly -** by sqlite3_step(). The use of the "v2" interface is recommended. -*/ -SQLITE_API int sqlite3_step(sqlite3_stmt*); - -/* -** CAPI3REF: Number of columns in a result set -** -** ^The sqlite3_data_count(P) interface returns the number of columns in the -** current row of the result set of [prepared statement] P. -** ^If prepared statement P does not have results ready to return -** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of -** interfaces) then sqlite3_data_count(P) returns 0. -** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. -** ^The sqlite3_data_count(P) routine returns 0 if the previous call to -** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) -** will return non-zero if previous call to [sqlite3_step](P) returned -** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] -** where it always returns zero since each step of that multi-step -** pragma returns 0 columns of data. -** -** See also: [sqlite3_column_count()] -*/ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Fundamental Datatypes -** KEYWORDS: SQLITE_TEXT -** -** ^(Every value in SQLite has one of five fundamental datatypes: -** -**
      -**
    • 64-bit signed integer -**
    • 64-bit IEEE floating point number -**
    • string -**
    • BLOB -**
    • NULL -**
    )^ -** -** These constants are codes for each of those types. -** -** Note that the SQLITE_TEXT constant was also used in SQLite version 2 -** for a completely different meaning. Software that links against both -** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not -** SQLITE_TEXT. -*/ -#define SQLITE_INTEGER 1 -#define SQLITE_FLOAT 2 -#define SQLITE_BLOB 4 -#define SQLITE_NULL 5 -#ifdef SQLITE_TEXT -# undef SQLITE_TEXT -#else -# define SQLITE_TEXT 3 -#endif -#define SQLITE3_TEXT 3 - -/* -** CAPI3REF: Result Values From A Query -** KEYWORDS: {column access functions} -** -** These routines form the "result set" interface. -** -** ^These routines return information about a single column of the current -** result row of a query. ^In every case the first argument is a pointer -** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] -** that was returned from [sqlite3_prepare_v2()] or one of its variants) -** and the second argument is the index of the column for which information -** should be returned. ^The leftmost column of the result set has the index 0. -** ^The number of columns in the result can be determined using -** [sqlite3_column_count()]. -** -** If the SQL statement does not currently point to a valid row, or if the -** column index is out of range, the result is undefined. -** These routines may only be called when the most recent call to -** [sqlite3_step()] has returned [SQLITE_ROW] and neither -** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently. -** If any of these routines are called after [sqlite3_reset()] or -** [sqlite3_finalize()] or after [sqlite3_step()] has returned -** something other than [SQLITE_ROW], the results are undefined. -** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] -** are called from a different thread while any of these routines -** are pending, then the results are undefined. -** -** ^The sqlite3_column_type() routine returns the -** [SQLITE_INTEGER | datatype code] for the initial data type -** of the result column. ^The returned value is one of [SQLITE_INTEGER], -** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value -** returned by sqlite3_column_type() is only meaningful if no type -** conversions have occurred as described below. After a type conversion, -** the value returned by sqlite3_column_type() is undefined. Future -** versions of SQLite may change the behavior of sqlite3_column_type() -** following a type conversion. -** -** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() -** routine returns the number of bytes in that BLOB or string. -** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts -** the string to UTF-8 and then returns the number of bytes. -** ^If the result is a numeric value then sqlite3_column_bytes() uses -** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns -** the number of bytes in that string. -** ^If the result is NULL, then sqlite3_column_bytes() returns zero. -** -** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() -** routine returns the number of bytes in that BLOB or string. -** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts -** the string to UTF-16 and then returns the number of bytes. -** ^If the result is a numeric value then sqlite3_column_bytes16() uses -** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns -** the number of bytes in that string. -** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. -** -** ^The values returned by [sqlite3_column_bytes()] and -** [sqlite3_column_bytes16()] do not include the zero terminators at the end -** of the string. ^For clarity: the values returned by -** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of -** bytes in the string, not the number of characters. -** -** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), -** even empty strings, are always zero-terminated. ^The return -** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. -** -** ^The object returned by [sqlite3_column_value()] is an -** [unprotected sqlite3_value] object. An unprotected sqlite3_value object -** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. -** If the [unprotected sqlite3_value] object returned by -** [sqlite3_column_value()] is used in any other way, including calls -** to routines like [sqlite3_value_int()], [sqlite3_value_text()], -** or [sqlite3_value_bytes()], then the behavior is undefined. -** -** These routines attempt to convert the value where appropriate. ^For -** example, if the internal representation is FLOAT and a text result -** is requested, [sqlite3_snprintf()] is used internally to perform the -** conversion automatically. ^(The following table details the conversions -** that are applied: -** -**
    -** -**
    Internal
    Type
    Requested
    Type
    Conversion -** -**
    NULL INTEGER Result is 0 -**
    NULL FLOAT Result is 0.0 -**
    NULL TEXT Result is NULL pointer -**
    NULL BLOB Result is NULL pointer -**
    INTEGER FLOAT Convert from integer to float -**
    INTEGER TEXT ASCII rendering of the integer -**
    INTEGER BLOB Same as INTEGER->TEXT -**
    FLOAT INTEGER Convert from float to integer -**
    FLOAT TEXT ASCII rendering of the float -**
    FLOAT BLOB Same as FLOAT->TEXT -**
    TEXT INTEGER Use atoi() -**
    TEXT FLOAT Use atof() -**
    TEXT BLOB No change -**
    BLOB INTEGER Convert to TEXT then use atoi() -**
    BLOB FLOAT Convert to TEXT then use atof() -**
    BLOB TEXT Add a zero terminator if needed -**
    -**
    )^ -** -** The table above makes reference to standard C library functions atoi() -** and atof(). SQLite does not really use these functions. It has its -** own equivalent internal routines. The atoi() and atof() names are -** used in the table for brevity and because they are familiar to most -** C programmers. -** -** Note that when type conversions occur, pointers returned by prior -** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or -** sqlite3_column_text16() may be invalidated. -** Type conversions and pointer invalidations might occur -** in the following cases: -** -**
      -**
    • The initial content is a BLOB and sqlite3_column_text() or -** sqlite3_column_text16() is called. A zero-terminator might -** need to be added to the string.
    • -**
    • The initial content is UTF-8 text and sqlite3_column_bytes16() or -** sqlite3_column_text16() is called. The content must be converted -** to UTF-16.
    • -**
    • The initial content is UTF-16 text and sqlite3_column_bytes() or -** sqlite3_column_text() is called. The content must be converted -** to UTF-8.
    • -**
    -** -** ^Conversions between UTF-16be and UTF-16le are always done in place and do -** not invalidate a prior pointer, though of course the content of the buffer -** that the prior pointer references will have been modified. Other kinds -** of conversion are done in place when it is possible, but sometimes they -** are not possible and in those cases prior pointers are invalidated. -** -** The safest and easiest to remember policy is to invoke these routines -** in one of the following ways: -** -**
      -**
    • sqlite3_column_text() followed by sqlite3_column_bytes()
    • -**
    • sqlite3_column_blob() followed by sqlite3_column_bytes()
    • -**
    • sqlite3_column_text16() followed by sqlite3_column_bytes16()
    • -**
    -** -** In other words, you should call sqlite3_column_text(), -** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result -** into the desired format, then invoke sqlite3_column_bytes() or -** sqlite3_column_bytes16() to find the size of the result. Do not mix calls -** to sqlite3_column_text() or sqlite3_column_blob() with calls to -** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() -** with calls to sqlite3_column_bytes(). -** -** ^The pointers returned are valid until a type conversion occurs as -** described above, or until [sqlite3_step()] or [sqlite3_reset()] or -** [sqlite3_finalize()] is called. ^The memory space used to hold strings -** and BLOBs is freed automatically. Do not pass the pointers returned -** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into -** [sqlite3_free()]. -** -** ^(If a memory allocation error occurs during the evaluation of any -** of these routines, a default value is returned. The default value -** is either the integer 0, the floating point number 0.0, or a NULL -** pointer. Subsequent calls to [sqlite3_errcode()] will return -** [SQLITE_NOMEM].)^ -*/ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); - -/* -** CAPI3REF: Destroy A Prepared Statement Object -** -** ^The sqlite3_finalize() function is called to delete a [prepared statement]. -** ^If the most recent evaluation of the statement encountered no errors -** or if the statement is never been evaluated, then sqlite3_finalize() returns -** SQLITE_OK. ^If the most recent evaluation of statement S failed, then -** sqlite3_finalize(S) returns the appropriate [error code] or -** [extended error code]. -** -** ^The sqlite3_finalize(S) routine can be called at any point during -** the life cycle of [prepared statement] S: -** before statement S is ever evaluated, after -** one or more calls to [sqlite3_reset()], or after any call -** to [sqlite3_step()] regardless of whether or not the statement has -** completed execution. -** -** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. -** -** The application must finalize every [prepared statement] in order to avoid -** resource leaks. It is a grievous error for the application to try to use -** a prepared statement after it has been finalized. Any use of a prepared -** statement after it has been finalized can result in undefined and -** undesirable behavior such as segfaults and heap corruption. -*/ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Reset A Prepared Statement Object -** -** The sqlite3_reset() function is called to reset a [prepared statement] -** object back to its initial state, ready to be re-executed. -** ^Any SQL statement variables that had values bound to them using -** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. -** Use [sqlite3_clear_bindings()] to reset the bindings. -** -** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S -** back to the beginning of its program. -** -** ^If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], -** or if [sqlite3_step(S)] has never before been called on S, -** then [sqlite3_reset(S)] returns [SQLITE_OK]. -** -** ^If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S indicated an error, then -** [sqlite3_reset(S)] returns an appropriate [error code]. -** -** ^The [sqlite3_reset(S)] interface does not change the values -** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. -*/ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Create Or Redefine SQL Functions -** KEYWORDS: {function creation routines} -** KEYWORDS: {application-defined SQL function} -** KEYWORDS: {application-defined SQL functions} -** -** ^These functions (collectively known as "function creation routines") -** are used to add SQL functions or aggregates or to redefine the behavior -** of existing SQL functions or aggregates. The only differences between -** these routines are the text encoding expected for -** the second parameter (the name of the function being created) -** and the presence or absence of a destructor callback for -** the application data pointer. -** -** ^The first parameter is the [database connection] to which the SQL -** function is to be added. ^If an application uses more than one database -** connection then application-defined SQL functions must be added -** to each database connection separately. -** -** ^The second parameter is the name of the SQL function to be created or -** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 -** representation, exclusive of the zero-terminator. ^Note that the name -** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. -** ^Any attempt to create a function with a longer name -** will result in [SQLITE_MISUSE] being returned. -** -** ^The third parameter (nArg) -** is the number of arguments that the SQL function or -** aggregate takes. ^If this parameter is -1, then the SQL function or -** aggregate may take any number of arguments between 0 and the limit -** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third -** parameter is less than -1 or greater than 127 then the behavior is -** undefined. -** -** ^The fourth parameter, eTextRep, specifies what -** [SQLITE_UTF8 | text encoding] this SQL function prefers for -** its parameters. Every SQL function implementation must be able to work -** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be -** more efficient with one encoding than another. ^An application may -** invoke sqlite3_create_function() or sqlite3_create_function16() multiple -** times with the same function but with different values of eTextRep. -** ^When multiple implementations of the same function are available, SQLite -** will pick the one that involves the least amount of data conversion. -** If there is only a single implementation which does not care what text -** encoding is used, then the fourth argument should be [SQLITE_ANY]. -** -** ^(The fifth parameter is an arbitrary pointer. The implementation of the -** function can gain access to this pointer using [sqlite3_user_data()].)^ -** -** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are -** pointers to C-language functions that implement the SQL function or -** aggregate. ^A scalar SQL function requires an implementation of the xFunc -** callback only; NULL pointers must be passed as the xStep and xFinal -** parameters. ^An aggregate SQL function requires an implementation of xStep -** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing -** SQL function or aggregate, pass NULL pointers for all three function -** callbacks. -** -** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL, -** then it is destructor for the application data pointer. -** The destructor is invoked when the function is deleted, either by being -** overloaded or when the database connection closes.)^ -** ^The destructor is also invoked if the call to -** sqlite3_create_function_v2() fails. -** ^When the destructor callback of the tenth parameter is invoked, it -** is passed a single argument which is a copy of the application data -** pointer which was the fifth parameter to sqlite3_create_function_v2(). -** -** ^It is permitted to register multiple implementations of the same -** functions with the same name but with either differing numbers of -** arguments or differing preferred text encodings. ^SQLite will use -** the implementation that most closely matches the way in which the -** SQL function is used. ^A function implementation with a non-negative -** nArg parameter is a better match than a function implementation with -** a negative nArg. ^A function where the preferred text encoding -** matches the database encoding is a better -** match than a function where the encoding is different. -** ^A function where the encoding difference is between UTF16le and UTF16be -** is a closer match than a function where the encoding difference is -** between UTF8 and UTF16. -** -** ^Built-in functions may be overloaded by new application-defined functions. -** -** ^An application-defined function is permitted to call other -** SQLite interfaces. However, such calls must not -** close the database connection nor finalize or reset the prepared -** statement in which the function is running. -*/ -SQLITE_API int sqlite3_create_function( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -); -SQLITE_API int sqlite3_create_function16( - sqlite3 *db, - const void *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*) -); -SQLITE_API int sqlite3_create_function_v2( - sqlite3 *db, - const char *zFunctionName, - int nArg, - int eTextRep, - void *pApp, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*), - void(*xDestroy)(void*) -); - -/* -** CAPI3REF: Text Encodings -** -** These constant define integer codes that represent the various -** text encodings supported by SQLite. -*/ -#define SQLITE_UTF8 1 -#define SQLITE_UTF16LE 2 -#define SQLITE_UTF16BE 3 -#define SQLITE_UTF16 4 /* Use native byte order */ -#define SQLITE_ANY 5 /* sqlite3_create_function only */ -#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ - -/* -** CAPI3REF: Deprecated Functions -** DEPRECATED -** -** These functions are [deprecated]. In order to maintain -** backwards compatibility with older code, these functions continue -** to be supported. However, new applications should avoid -** the use of these functions. To help encourage people to avoid -** using these functions, we are not going to tell you what they do. -*/ -#ifndef SQLITE_OMIT_DEPRECATED -SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); -SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); -SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), - void*,sqlite3_int64); -#endif - -/* -** CAPI3REF: Obtaining SQL Function Parameter Values -** -** The C-language implementation of SQL functions and aggregates uses -** this set of interface routines to access the parameter values on -** the function or aggregate. -** -** The xFunc (for scalar functions) or xStep (for aggregates) parameters -** to [sqlite3_create_function()] and [sqlite3_create_function16()] -** define callbacks that implement the SQL functions and aggregates. -** The 3rd parameter to these callbacks is an array of pointers to -** [protected sqlite3_value] objects. There is one [sqlite3_value] object for -** each parameter to the SQL function. These routines are used to -** extract values from the [sqlite3_value] objects. -** -** These routines work only with [protected sqlite3_value] objects. -** Any attempt to use these routines on an [unprotected sqlite3_value] -** object results in undefined behavior. -** -** ^These routines work just like the corresponding [column access functions] -** except that these routines take a single [protected sqlite3_value] object -** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. -** -** ^The sqlite3_value_text16() interface extracts a UTF-16 string -** in the native byte-order of the host machine. ^The -** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces -** extract UTF-16 strings as big-endian and little-endian respectively. -** -** ^(The sqlite3_value_numeric_type() interface attempts to apply -** numeric affinity to the value. This means that an attempt is -** made to convert the value to an integer or floating point. If -** such a conversion is possible without loss of information (in other -** words, if the value is a string that looks like a number) -** then the conversion is performed. Otherwise no conversion occurs. -** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ -** -** Please pay particular attention to the fact that the pointer returned -** from [sqlite3_value_blob()], [sqlite3_value_text()], or -** [sqlite3_value_text16()] can be invalidated by a subsequent call to -** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], -** or [sqlite3_value_text16()]. -** -** These routines must be called from the same thread as -** the SQL function that supplied the [sqlite3_value*] parameters. -*/ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); -SQLITE_API double sqlite3_value_double(sqlite3_value*); -SQLITE_API int sqlite3_value_int(sqlite3_value*); -SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); -SQLITE_API int sqlite3_value_type(sqlite3_value*); -SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); - -/* -** CAPI3REF: Obtain Aggregate Function Context -** -** Implementations of aggregate SQL functions use this -** routine to allocate memory for storing their state. -** -** ^The first time the sqlite3_aggregate_context(C,N) routine is called -** for a particular aggregate function, SQLite -** allocates N of memory, zeroes out that memory, and returns a pointer -** to the new memory. ^On second and subsequent calls to -** sqlite3_aggregate_context() for the same aggregate function instance, -** the same buffer is returned. Sqlite3_aggregate_context() is normally -** called once for each invocation of the xStep callback and then one -** last time when the xFinal callback is invoked. ^(When no rows match -** an aggregate query, the xStep() callback of the aggregate function -** implementation is never called and xFinal() is called exactly once. -** In those cases, sqlite3_aggregate_context() might be called for the -** first time from within xFinal().)^ -** -** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer -** when first called if N is less than or equal to zero or if a memory -** allocate error occurs. -** -** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is -** determined by the N parameter on first successful call. Changing the -** value of N in subsequent call to sqlite3_aggregate_context() within -** the same aggregate function instance will not resize the memory -** allocation.)^ Within the xFinal callback, it is customary to set -** N=0 in calls to sqlite3_aggregate_context(C,N) so that no -** pointless memory allocations occur. -** -** ^SQLite automatically frees the memory allocated by -** sqlite3_aggregate_context() when the aggregate query concludes. -** -** The first parameter must be a copy of the -** [sqlite3_context | SQL function context] that is the first parameter -** to the xStep or xFinal callback routine that implements the aggregate -** function. -** -** This routine must be called from the same thread in which -** the aggregate SQL function is running. -*/ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); - -/* -** CAPI3REF: User Data For Functions -** -** ^The sqlite3_user_data() interface returns a copy of -** the pointer that was the pUserData parameter (the 5th parameter) -** of the [sqlite3_create_function()] -** and [sqlite3_create_function16()] routines that originally -** registered the application defined function. -** -** This routine must be called from the same thread in which -** the application-defined function is running. -*/ -SQLITE_API void *sqlite3_user_data(sqlite3_context*); - -/* -** CAPI3REF: Database Connection For Functions -** -** ^The sqlite3_context_db_handle() interface returns a copy of -** the pointer to the [database connection] (the 1st parameter) -** of the [sqlite3_create_function()] -** and [sqlite3_create_function16()] routines that originally -** registered the application defined function. -*/ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); - -/* -** CAPI3REF: Function Auxiliary Data -** -** The following two functions may be used by scalar SQL functions to -** associate metadata with argument values. If the same value is passed to -** multiple invocations of the same SQL function during query execution, under -** some circumstances the associated metadata may be preserved. This may -** be used, for example, to add a regular-expression matching scalar -** function. The compiled version of the regular expression is stored as -** metadata associated with the SQL value passed as the regular expression -** pattern. The compiled regular expression can be reused on multiple -** invocations of the same function so that the original pattern string -** does not need to be recompiled on each invocation. -** -** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata -** associated by the sqlite3_set_auxdata() function with the Nth argument -** value to the application-defined function. ^If no metadata has been ever -** been set for the Nth argument of the function, or if the corresponding -** function parameter has changed since the meta-data was set, -** then sqlite3_get_auxdata() returns a NULL pointer. -** -** ^The sqlite3_set_auxdata() interface saves the metadata -** pointed to by its 3rd parameter as the metadata for the N-th -** argument of the application-defined function. Subsequent -** calls to sqlite3_get_auxdata() might return this data, if it has -** not been destroyed. -** ^If it is not NULL, SQLite will invoke the destructor -** function given by the 4th parameter to sqlite3_set_auxdata() on -** the metadata when the corresponding function parameter changes -** or when the SQL statement completes, whichever comes first. -** -** SQLite is free to call the destructor and drop metadata on any -** parameter of any function at any time. ^The only guarantee is that -** the destructor will be called before the metadata is dropped. -** -** ^(In practice, metadata is preserved between function calls for -** expressions that are constant at compile time. This includes literal -** values and [parameters].)^ -** -** These routines must be called from the same thread in which -** the SQL function is running. -*/ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); -SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); - - -/* -** CAPI3REF: Constants Defining Special Destructor Behavior -** -** These are special values for the destructor that is passed in as the -** final argument to routines like [sqlite3_result_blob()]. ^If the destructor -** argument is SQLITE_STATIC, it means that the content pointer is constant -** and will never change. It does not need to be destroyed. ^The -** SQLITE_TRANSIENT value means that the content will likely change in -** the near future and that SQLite should make its own private copy of -** the content before returning. -** -** The typedef is necessary to work around problems in certain -** C++ compilers. See ticket #2191. -*/ -typedef void (*sqlite3_destructor_type)(void*); -#define SQLITE_STATIC ((sqlite3_destructor_type)0) -#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) - -/* -** CAPI3REF: Setting The Result Of An SQL Function -** -** These routines are used by the xFunc or xFinal callbacks that -** implement SQL functions and aggregates. See -** [sqlite3_create_function()] and [sqlite3_create_function16()] -** for additional information. -** -** These functions work very much like the [parameter binding] family of -** functions used to bind values to host parameters in prepared statements. -** Refer to the [SQL parameter] documentation for additional information. -** -** ^The sqlite3_result_blob() interface sets the result from -** an application-defined function to be the BLOB whose content is pointed -** to by the second parameter and which is N bytes long where N is the -** third parameter. -** -** ^The sqlite3_result_zeroblob() interfaces set the result of -** the application-defined function to be a BLOB containing all zero -** bytes and N bytes in size, where N is the value of the 2nd parameter. -** -** ^The sqlite3_result_double() interface sets the result from -** an application-defined function to be a floating point value specified -** by its 2nd argument. -** -** ^The sqlite3_result_error() and sqlite3_result_error16() functions -** cause the implemented SQL function to throw an exception. -** ^SQLite uses the string pointed to by the -** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() -** as the text of an error message. ^SQLite interprets the error -** message string from sqlite3_result_error() as UTF-8. ^SQLite -** interprets the string from sqlite3_result_error16() as UTF-16 in native -** byte order. ^If the third parameter to sqlite3_result_error() -** or sqlite3_result_error16() is negative then SQLite takes as the error -** message all text up through the first zero character. -** ^If the third parameter to sqlite3_result_error() or -** sqlite3_result_error16() is non-negative then SQLite takes that many -** bytes (not characters) from the 2nd parameter as the error message. -** ^The sqlite3_result_error() and sqlite3_result_error16() -** routines make a private copy of the error message text before -** they return. Hence, the calling function can deallocate or -** modify the text after they return without harm. -** ^The sqlite3_result_error_code() function changes the error code -** returned by SQLite as a result of an error in a function. ^By default, -** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() -** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. -** -** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an -** error indicating that a string or BLOB is too long to represent. -** -** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an -** error indicating that a memory allocation failed. -** -** ^The sqlite3_result_int() interface sets the return value -** of the application-defined function to be the 32-bit signed integer -** value given in the 2nd argument. -** ^The sqlite3_result_int64() interface sets the return value -** of the application-defined function to be the 64-bit signed integer -** value given in the 2nd argument. -** -** ^The sqlite3_result_null() interface sets the return value -** of the application-defined function to be NULL. -** -** ^The sqlite3_result_text(), sqlite3_result_text16(), -** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces -** set the return value of the application-defined function to be -** a text string which is represented as UTF-8, UTF-16 native byte order, -** UTF-16 little endian, or UTF-16 big endian, respectively. -** ^SQLite takes the text result from the application from -** the 2nd parameter of the sqlite3_result_text* interfaces. -** ^If the 3rd parameter to the sqlite3_result_text* interfaces -** is negative, then SQLite takes result text from the 2nd parameter -** through the first zero character. -** ^If the 3rd parameter to the sqlite3_result_text* interfaces -** is non-negative, then as many bytes (not characters) of the text -** pointed to by the 2nd parameter are taken as the application-defined -** function result. If the 3rd parameter is non-negative, then it -** must be the byte offset into the string where the NUL terminator would -** appear if the string where NUL terminated. If any NUL characters occur -** in the string at a byte offset that is less than the value of the 3rd -** parameter, then the resulting string will contain embedded NULs and the -** result of expressions operating on strings with embedded NULs is undefined. -** ^If the 4th parameter to the sqlite3_result_text* interfaces -** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that -** function as the destructor on the text or BLOB result when it has -** finished using that result. -** ^If the 4th parameter to the sqlite3_result_text* interfaces or to -** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite -** assumes that the text or BLOB result is in constant space and does not -** copy the content of the parameter nor call a destructor on the content -** when it has finished using that result. -** ^If the 4th parameter to the sqlite3_result_text* interfaces -** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT -** then SQLite makes a copy of the result into space obtained from -** from [sqlite3_malloc()] before it returns. -** -** ^The sqlite3_result_value() interface sets the result of -** the application-defined function to be a copy the -** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The -** sqlite3_result_value() interface makes a copy of the [sqlite3_value] -** so that the [sqlite3_value] specified in the parameter may change or -** be deallocated after sqlite3_result_value() returns without harm. -** ^A [protected sqlite3_value] object may always be used where an -** [unprotected sqlite3_value] object is required, so either -** kind of [sqlite3_value] object can be used with this interface. -** -** If these routines are called from within the different thread -** than the one containing the application-defined function that received -** the [sqlite3_context] pointer, the results are undefined. -*/ -SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_double(sqlite3_context*, double); -SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); -SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); -SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); -SQLITE_API void sqlite3_result_null(sqlite3_context*); -SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); - -/* -** CAPI3REF: Define New Collating Sequences -** -** ^These functions add, remove, or modify a [collation] associated -** with the [database connection] specified as the first argument. -** -** ^The name of the collation is a UTF-8 string -** for sqlite3_create_collation() and sqlite3_create_collation_v2() -** and a UTF-16 string in native byte order for sqlite3_create_collation16(). -** ^Collation names that compare equal according to [sqlite3_strnicmp()] are -** considered to be the same name. -** -** ^(The third argument (eTextRep) must be one of the constants: -**
      -**
    • [SQLITE_UTF8], -**
    • [SQLITE_UTF16LE], -**
    • [SQLITE_UTF16BE], -**
    • [SQLITE_UTF16], or -**
    • [SQLITE_UTF16_ALIGNED]. -**
    )^ -** ^The eTextRep argument determines the encoding of strings passed -** to the collating function callback, xCallback. -** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep -** force strings to be UTF16 with native byte order. -** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin -** on an even byte address. -** -** ^The fourth argument, pArg, is an application data pointer that is passed -** through as the first argument to the collating function callback. -** -** ^The fifth argument, xCallback, is a pointer to the collating function. -** ^Multiple collating functions can be registered using the same name but -** with different eTextRep parameters and SQLite will use whichever -** function requires the least amount of data transformation. -** ^If the xCallback argument is NULL then the collating function is -** deleted. ^When all collating functions having the same name are deleted, -** that collation is no longer usable. -** -** ^The collating function callback is invoked with a copy of the pArg -** application data pointer and with two strings in the encoding specified -** by the eTextRep argument. The collating function must return an -** integer that is negative, zero, or positive -** if the first string is less than, equal to, or greater than the second, -** respectively. A collating function must always return the same answer -** given the same inputs. If two or more collating functions are registered -** to the same collation name (using different eTextRep values) then all -** must give an equivalent answer when invoked with equivalent strings. -** The collating function must obey the following properties for all -** strings A, B, and C: -** -**
      -**
    1. If A==B then B==A. -**
    2. If A==B and B==C then A==C. -**
    3. If A<B THEN B>A. -**
    4. If A<B and B<C then A<C. -**
    -** -** If a collating function fails any of the above constraints and that -** collating function is registered and used, then the behavior of SQLite -** is undefined. -** -** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() -** with the addition that the xDestroy callback is invoked on pArg when -** the collating function is deleted. -** ^Collating functions are deleted when they are overridden by later -** calls to the collation creation functions or when the -** [database connection] is closed using [sqlite3_close()]. -** -** ^The xDestroy callback is not called if the -** sqlite3_create_collation_v2() function fails. Applications that invoke -** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should -** check the return code and dispose of the application data pointer -** themselves rather than expecting SQLite to deal with it for them. -** This is different from every other SQLite interface. The inconsistency -** is unfortunate but cannot be changed without breaking backwards -** compatibility. -** -** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. -*/ -SQLITE_API int sqlite3_create_collation( - sqlite3*, - const char *zName, - int eTextRep, - void *pArg, - int(*xCompare)(void*,int,const void*,int,const void*) -); -SQLITE_API int sqlite3_create_collation_v2( - sqlite3*, - const char *zName, - int eTextRep, - void *pArg, - int(*xCompare)(void*,int,const void*,int,const void*), - void(*xDestroy)(void*) -); -SQLITE_API int sqlite3_create_collation16( - sqlite3*, - const void *zName, - int eTextRep, - void *pArg, - int(*xCompare)(void*,int,const void*,int,const void*) -); - -/* -** CAPI3REF: Collation Needed Callbacks -** -** ^To avoid having to register all collation sequences before a database -** can be used, a single callback function may be registered with the -** [database connection] to be invoked whenever an undefined collation -** sequence is required. -** -** ^If the function is registered using the sqlite3_collation_needed() API, -** then it is passed the names of undefined collation sequences as strings -** encoded in UTF-8. ^If sqlite3_collation_needed16() is used, -** the names are passed as UTF-16 in machine native byte order. -** ^A call to either function replaces the existing collation-needed callback. -** -** ^(When the callback is invoked, the first argument passed is a copy -** of the second argument to sqlite3_collation_needed() or -** sqlite3_collation_needed16(). The second argument is the database -** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], -** or [SQLITE_UTF16LE], indicating the most desirable form of the collation -** sequence function required. The fourth parameter is the name of the -** required collation sequence.)^ -** -** The callback function should register the desired collation using -** [sqlite3_create_collation()], [sqlite3_create_collation16()], or -** [sqlite3_create_collation_v2()]. -*/ -SQLITE_API int sqlite3_collation_needed( - sqlite3*, - void*, - void(*)(void*,sqlite3*,int eTextRep,const char*) -); -SQLITE_API int sqlite3_collation_needed16( - sqlite3*, - void*, - void(*)(void*,sqlite3*,int eTextRep,const void*) -); - -#ifdef SQLITE_HAS_CODEC -/* -** Specify the key for an encrypted database. This routine should be -** called right after sqlite3_open(). -** -** The code to implement this API is not available in the public release -** of SQLite. -*/ -SQLITE_API int sqlite3_key( - sqlite3 *db, /* Database to be rekeyed */ - const void *pKey, int nKey /* The key */ -); - -/* -** Change the key on an open database. If the current database is not -** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the -** database is decrypted. -** -** The code to implement this API is not available in the public release -** of SQLite. -*/ -SQLITE_API int sqlite3_rekey( - sqlite3 *db, /* Database to be rekeyed */ - const void *pKey, int nKey /* The new key */ -); - -/* -** Specify the activation key for a SEE database. Unless -** activated, none of the SEE routines will work. -*/ -SQLITE_API void sqlite3_activate_see( - const char *zPassPhrase /* Activation phrase */ -); -#endif - -#ifdef SQLITE_ENABLE_CEROD -/* -** Specify the activation key for a CEROD database. Unless -** activated, none of the CEROD routines will work. -*/ -SQLITE_API void sqlite3_activate_cerod( - const char *zPassPhrase /* Activation phrase */ -); -#endif - -/* -** CAPI3REF: Suspend Execution For A Short Time -** -** The sqlite3_sleep() function causes the current thread to suspend execution -** for at least a number of milliseconds specified in its parameter. -** -** If the operating system does not support sleep requests with -** millisecond time resolution, then the time will be rounded up to -** the nearest second. The number of milliseconds of sleep actually -** requested from the operating system is returned. -** -** ^SQLite implements this interface by calling the xSleep() -** method of the default [sqlite3_vfs] object. If the xSleep() method -** of the default VFS is not implemented correctly, or not implemented at -** all, then the behavior of sqlite3_sleep() may deviate from the description -** in the previous paragraphs. -*/ -SQLITE_API int sqlite3_sleep(int); - -/* -** CAPI3REF: Name Of The Folder Holding Temporary Files -** -** ^(If this global variable is made to point to a string which is -** the name of a folder (a.k.a. directory), then all temporary files -** created by SQLite when using a built-in [sqlite3_vfs | VFS] -** will be placed in that directory.)^ ^If this variable -** is a NULL pointer, then SQLite performs a search for an appropriate -** temporary file directory. -** -** It is not safe to read or modify this variable in more than one -** thread at a time. It is not safe to read or modify this variable -** if a [database connection] is being used at the same time in a separate -** thread. -** It is intended that this variable be set once -** as part of process initialization and before any SQLite interface -** routines have been called and that this variable remain unchanged -** thereafter. -** -** ^The [temp_store_directory pragma] may modify this variable and cause -** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, -** the [temp_store_directory pragma] always assumes that any string -** that this variable points to is held in memory obtained from -** [sqlite3_malloc] and the pragma may attempt to free that memory -** using [sqlite3_free]. -** Hence, if this variable is modified directly, either it should be -** made NULL or made to point to memory obtained from [sqlite3_malloc] -** or else the use of the [temp_store_directory pragma] should be avoided. -** -** Note to Windows Runtime users: The temporary directory must be set -** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various -** features that require the use of temporary files may fail. Here is an -** example of how to do this using C++ with the Windows Runtime: -** -**
    -** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
    -**       TemporaryFolder->Path->Data();
    -** char zPathBuf[MAX_PATH + 1];
    -** memset(zPathBuf, 0, sizeof(zPathBuf));
    -** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
    -**       NULL, NULL);
    -** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
    -** 
    -*/ -SQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory; - -/* -** CAPI3REF: Name Of The Folder Holding Database Files -** -** ^(If this global variable is made to point to a string which is -** the name of a folder (a.k.a. directory), then all database files -** specified with a relative pathname and created or accessed by -** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed -** to be relative to that directory.)^ ^If this variable is a NULL -** pointer, then SQLite assumes that all database files specified -** with a relative pathname are relative to the current directory -** for the process. Only the windows VFS makes use of this global -** variable; it is ignored by the unix VFS. -** -** Changing the value of this variable while a database connection is -** open can result in a corrupt database. -** -** It is not safe to read or modify this variable in more than one -** thread at a time. It is not safe to read or modify this variable -** if a [database connection] is being used at the same time in a separate -** thread. -** It is intended that this variable be set once -** as part of process initialization and before any SQLite interface -** routines have been called and that this variable remain unchanged -** thereafter. -** -** ^The [data_store_directory pragma] may modify this variable and cause -** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, -** the [data_store_directory pragma] always assumes that any string -** that this variable points to is held in memory obtained from -** [sqlite3_malloc] and the pragma may attempt to free that memory -** using [sqlite3_free]. -** Hence, if this variable is modified directly, either it should be -** made NULL or made to point to memory obtained from [sqlite3_malloc] -** or else the use of the [data_store_directory pragma] should be avoided. -*/ -SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory; - -/* -** CAPI3REF: Test For Auto-Commit Mode -** KEYWORDS: {autocommit mode} -** -** ^The sqlite3_get_autocommit() interface returns non-zero or -** zero if the given database connection is or is not in autocommit mode, -** respectively. ^Autocommit mode is on by default. -** ^Autocommit mode is disabled by a [BEGIN] statement. -** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. -** -** If certain kinds of errors occur on a statement within a multi-statement -** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], -** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the -** transaction might be rolled back automatically. The only way to -** find out whether SQLite automatically rolled back the transaction after -** an error is to use this function. -** -** If another thread changes the autocommit status of the database -** connection while this routine is running, then the return value -** is undefined. -*/ -SQLITE_API int sqlite3_get_autocommit(sqlite3*); - -/* -** CAPI3REF: Find The Database Handle Of A Prepared Statement -** -** ^The sqlite3_db_handle interface returns the [database connection] handle -** to which a [prepared statement] belongs. ^The [database connection] -** returned by sqlite3_db_handle is the same [database connection] -** that was the first argument -** to the [sqlite3_prepare_v2()] call (or its variants) that was used to -** create the statement in the first place. -*/ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); - -/* -** CAPI3REF: Return The Filename For A Database Connection -** -** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename -** associated with database N of connection D. ^The main database file -** has the name "main". If there is no attached database N on the database -** connection D, or if database N is a temporary or in-memory database, then -** a NULL pointer is returned. -** -** ^The filename returned by this function is the output of the -** xFullPathname method of the [VFS]. ^In other words, the filename -** will be an absolute pathname, even if the filename used -** to open the database originally was a URI or relative pathname. -*/ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); - -/* -** CAPI3REF: Determine if a database is read-only -** -** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N -** of connection D is read-only, 0 if it is read/write, or -1 if N is not -** the name of a database on connection D. -*/ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); - -/* -** CAPI3REF: Find the next prepared statement -** -** ^This interface returns a pointer to the next [prepared statement] after -** pStmt associated with the [database connection] pDb. ^If pStmt is NULL -** then this interface returns a pointer to the first prepared statement -** associated with the database connection pDb. ^If no prepared statement -** satisfies the conditions of this routine, it returns NULL. -** -** The [database connection] pointer D in a call to -** [sqlite3_next_stmt(D,S)] must refer to an open database -** connection and in particular must not be a NULL pointer. -*/ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); - -/* -** CAPI3REF: Commit And Rollback Notification Callbacks -** -** ^The sqlite3_commit_hook() interface registers a callback -** function to be invoked whenever a transaction is [COMMIT | committed]. -** ^Any callback set by a previous call to sqlite3_commit_hook() -** for the same database connection is overridden. -** ^The sqlite3_rollback_hook() interface registers a callback -** function to be invoked whenever a transaction is [ROLLBACK | rolled back]. -** ^Any callback set by a previous call to sqlite3_rollback_hook() -** for the same database connection is overridden. -** ^The pArg argument is passed through to the callback. -** ^If the callback on a commit hook function returns non-zero, -** then the commit is converted into a rollback. -** -** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions -** return the P argument from the previous call of the same function -** on the same [database connection] D, or NULL for -** the first call for each function on D. -** -** The commit and rollback hook callbacks are not reentrant. -** The callback implementation must not do anything that will modify -** the database connection that invoked the callback. Any actions -** to modify the database connection must be deferred until after the -** completion of the [sqlite3_step()] call that triggered the commit -** or rollback hook in the first place. -** Note that running any other SQL statements, including SELECT statements, -** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify -** the database connections for the meaning of "modify" in this paragraph. -** -** ^Registering a NULL function disables the callback. -** -** ^When the commit hook callback routine returns zero, the [COMMIT] -** operation is allowed to continue normally. ^If the commit hook -** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK]. -** ^The rollback hook is invoked on a rollback that results from a commit -** hook returning non-zero, just as it would be with any other rollback. -** -** ^For the purposes of this API, a transaction is said to have been -** rolled back if an explicit "ROLLBACK" statement is executed, or -** an error or constraint causes an implicit rollback to occur. -** ^The rollback callback is not invoked if a transaction is -** automatically rolled back because the database connection is closed. -** -** See also the [sqlite3_update_hook()] interface. -*/ -SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); -SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); - -/* -** CAPI3REF: Data Change Notification Callbacks -** -** ^The sqlite3_update_hook() interface registers a callback function -** with the [database connection] identified by the first argument -** to be invoked whenever a row is updated, inserted or deleted. -** ^Any callback set by a previous call to this function -** for the same database connection is overridden. -** -** ^The second argument is a pointer to the function to invoke when a -** row is updated, inserted or deleted. -** ^The first argument to the callback is a copy of the third argument -** to sqlite3_update_hook(). -** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], -** or [SQLITE_UPDATE], depending on the operation that caused the callback -** to be invoked. -** ^The third and fourth arguments to the callback contain pointers to the -** database and table name containing the affected row. -** ^The final callback parameter is the [rowid] of the row. -** ^In the case of an update, this is the [rowid] after the update takes place. -** -** ^(The update hook is not invoked when internal system tables are -** modified (i.e. sqlite_master and sqlite_sequence).)^ -** -** ^In the current implementation, the update hook -** is not invoked when duplication rows are deleted because of an -** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook -** invoked when rows are deleted using the [truncate optimization]. -** The exceptions defined in this paragraph might change in a future -** release of SQLite. -** -** The update hook implementation must not do anything that will modify -** the database connection that invoked the update hook. Any actions -** to modify the database connection must be deferred until after the -** completion of the [sqlite3_step()] call that triggered the update hook. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. -** -** ^The sqlite3_update_hook(D,C,P) function -** returns the P argument from the previous call -** on the same [database connection] D, or NULL for -** the first call on D. -** -** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] -** interfaces. -*/ -SQLITE_API void *sqlite3_update_hook( - sqlite3*, - void(*)(void *,int ,char const *,char const *,sqlite3_int64), - void* -); - -/* -** CAPI3REF: Enable Or Disable Shared Pager Cache -** -** ^(This routine enables or disables the sharing of the database cache -** and schema data structures between [database connection | connections] -** to the same database. Sharing is enabled if the argument is true -** and disabled if the argument is false.)^ -** -** ^Cache sharing is enabled and disabled for an entire process. -** This is a change as of SQLite version 3.5.0. In prior versions of SQLite, -** sharing was enabled or disabled for each thread separately. -** -** ^(The cache sharing mode set by this interface effects all subsequent -** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. -** Existing database connections continue use the sharing mode -** that was in effect at the time they were opened.)^ -** -** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled -** successfully. An [error code] is returned otherwise.)^ -** -** ^Shared cache is disabled by default. But this might change in -** future releases of SQLite. Applications that care about shared -** cache setting should set it explicitly. -** -** This interface is threadsafe on processors where writing a -** 32-bit integer is atomic. -** -** See Also: [SQLite Shared-Cache Mode] -*/ -SQLITE_API int sqlite3_enable_shared_cache(int); - -/* -** CAPI3REF: Attempt To Free Heap Memory -** -** ^The sqlite3_release_memory() interface attempts to free N bytes -** of heap memory by deallocating non-essential memory allocations -** held by the database library. Memory used to cache database -** pages to improve performance is an example of non-essential memory. -** ^sqlite3_release_memory() returns the number of bytes actually freed, -** which might be more or less than the amount requested. -** ^The sqlite3_release_memory() routine is a no-op returning zero -** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. -** -** See also: [sqlite3_db_release_memory()] -*/ -SQLITE_API int sqlite3_release_memory(int); - -/* -** CAPI3REF: Free Memory Used By A Database Connection -** -** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap -** memory as possible from database connection D. Unlike the -** [sqlite3_release_memory()] interface, this interface is effect even -** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is -** omitted. -** -** See also: [sqlite3_release_memory()] -*/ -SQLITE_API int sqlite3_db_release_memory(sqlite3*); - -/* -** CAPI3REF: Impose A Limit On Heap Size -** -** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the -** soft limit on the amount of heap memory that may be allocated by SQLite. -** ^SQLite strives to keep heap memory utilization below the soft heap -** limit by reducing the number of pages held in the page cache -** as heap memory usages approaches the limit. -** ^The soft heap limit is "soft" because even though SQLite strives to stay -** below the limit, it will exceed the limit rather than generate -** an [SQLITE_NOMEM] error. In other words, the soft heap limit -** is advisory only. -** -** ^The return value from sqlite3_soft_heap_limit64() is the size of -** the soft heap limit prior to the call, or negative in the case of an -** error. ^If the argument N is negative -** then no change is made to the soft heap limit. Hence, the current -** size of the soft heap limit can be determined by invoking -** sqlite3_soft_heap_limit64() with a negative argument. -** -** ^If the argument N is zero then the soft heap limit is disabled. -** -** ^(The soft heap limit is not enforced in the current implementation -** if one or more of following conditions are true: -** -**
      -**
    • The soft heap limit is set to zero. -**
    • Memory accounting is disabled using a combination of the -** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and -** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. -**
    • An alternative page cache implementation is specified using -** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). -**
    • The page cache allocates from its own memory pool supplied -** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than -** from the heap. -**
    )^ -** -** Beginning with SQLite version 3.7.3, the soft heap limit is enforced -** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT] -** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT], -** the soft heap limit is enforced on every memory allocation. Without -** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced -** when memory is allocated by the page cache. Testing suggests that because -** the page cache is the predominate memory user in SQLite, most -** applications will achieve adequate soft heap limit enforcement without -** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. -** -** The circumstances under which SQLite will enforce the soft heap limit may -** changes in future releases of SQLite. -*/ -SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); - -/* -** CAPI3REF: Deprecated Soft Heap Limit Interface -** DEPRECATED -** -** This is a deprecated version of the [sqlite3_soft_heap_limit64()] -** interface. This routine is provided for historical compatibility -** only. All new applications should use the -** [sqlite3_soft_heap_limit64()] interface rather than this one. -*/ -SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); - - -/* -** CAPI3REF: Extract Metadata About A Column Of A Table -** -** ^This routine returns metadata about a specific column of a specific -** database table accessible using the [database connection] handle -** passed as the first function argument. -** -** ^The column is identified by the second, third and fourth parameters to -** this function. ^The second parameter is either the name of the database -** (i.e. "main", "temp", or an attached database) containing the specified -** table or NULL. ^If it is NULL, then all attached databases are searched -** for the table using the same algorithm used by the database engine to -** resolve unqualified table references. -** -** ^The third and fourth parameters to this function are the table and column -** name of the desired column, respectively. Neither of these parameters -** may be NULL. -** -** ^Metadata is returned by writing to the memory locations passed as the 5th -** and subsequent parameters to this function. ^Any of these arguments may be -** NULL, in which case the corresponding element of metadata is omitted. -** -** ^(
    -** -**
    Parameter Output
    Type
    Description -** -**
    5th const char* Data type -**
    6th const char* Name of default collation sequence -**
    7th int True if column has a NOT NULL constraint -**
    8th int True if column is part of the PRIMARY KEY -**
    9th int True if column is [AUTOINCREMENT] -**
    -**
    )^ -** -** ^The memory pointed to by the character pointers returned for the -** declaration type and collation sequence is valid only until the next -** call to any SQLite API function. -** -** ^If the specified table is actually a view, an [error code] is returned. -** -** ^If the specified column is "rowid", "oid" or "_rowid_" and an -** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output -** parameters are set for the explicitly declared column. ^(If there is no -** explicitly declared [INTEGER PRIMARY KEY] column, then the output -** parameters are set as follows: -** -**
    -**     data type: "INTEGER"
    -**     collation sequence: "BINARY"
    -**     not null: 0
    -**     primary key: 1
    -**     auto increment: 0
    -** 
    )^ -** -** ^(This function may load one or more schemas from database files. If an -** error occurs during this process, or if the requested table or column -** cannot be found, an [error code] is returned and an error message left -** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^ -** -** ^This API is only available if the library was compiled with the -** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. -*/ -SQLITE_API int sqlite3_table_column_metadata( - sqlite3 *db, /* Connection handle */ - const char *zDbName, /* Database name or NULL */ - const char *zTableName, /* Table name */ - const char *zColumnName, /* Column name */ - char const **pzDataType, /* OUTPUT: Declared data type */ - char const **pzCollSeq, /* OUTPUT: Collation sequence name */ - int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ - int *pPrimaryKey, /* OUTPUT: True if column part of PK */ - int *pAutoinc /* OUTPUT: True if column is auto-increment */ -); - -/* -** CAPI3REF: Load An Extension -** -** ^This interface loads an SQLite extension library from the named file. -** -** ^The sqlite3_load_extension() interface attempts to load an -** SQLite extension library contained in the file zFile. -** -** ^The entry point is zProc. -** ^zProc may be 0, in which case the name of the entry point -** defaults to "sqlite3_extension_init". -** ^The sqlite3_load_extension() interface returns -** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. -** ^If an error occurs and pzErrMsg is not 0, then the -** [sqlite3_load_extension()] interface shall attempt to -** fill *pzErrMsg with error message text stored in memory -** obtained from [sqlite3_malloc()]. The calling function -** should free this memory by calling [sqlite3_free()]. -** -** ^Extension loading must be enabled using -** [sqlite3_enable_load_extension()] prior to calling this API, -** otherwise an error will be returned. -** -** See also the [load_extension() SQL function]. -*/ -SQLITE_API int sqlite3_load_extension( - sqlite3 *db, /* Load the extension into this database connection */ - const char *zFile, /* Name of the shared library containing extension */ - const char *zProc, /* Entry point. Derived from zFile if 0 */ - char **pzErrMsg /* Put error message here if not 0 */ -); - -/* -** CAPI3REF: Enable Or Disable Extension Loading -** -** ^So as not to open security holes in older applications that are -** unprepared to deal with extension loading, and as a means of disabling -** extension loading while evaluating user-entered SQL, the following API -** is provided to turn the [sqlite3_load_extension()] mechanism on and off. -** -** ^Extension loading is off by default. See ticket #1863. -** ^Call the sqlite3_enable_load_extension() routine with onoff==1 -** to turn extension loading on and call it with onoff==0 to turn -** it back off again. -*/ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); - -/* -** CAPI3REF: Automatically Load Statically Linked Extensions -** -** ^This interface causes the xEntryPoint() function to be invoked for -** each new [database connection] that is created. The idea here is that -** xEntryPoint() is the entry point for a statically linked SQLite extension -** that is to be automatically loaded into all new database connections. -** -** ^(Even though the function prototype shows that xEntryPoint() takes -** no arguments and returns void, SQLite invokes xEntryPoint() with three -** arguments and expects and integer result as if the signature of the -** entry point where as follows: -** -**
    -**    int xEntryPoint(
    -**      sqlite3 *db,
    -**      const char **pzErrMsg,
    -**      const struct sqlite3_api_routines *pThunk
    -**    );
    -** 
    )^ -** -** If the xEntryPoint routine encounters an error, it should make *pzErrMsg -** point to an appropriate error message (obtained from [sqlite3_mprintf()]) -** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg -** is NULL before calling the xEntryPoint(). ^SQLite will invoke -** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any -** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], -** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. -** -** ^Calling sqlite3_auto_extension(X) with an entry point X that is already -** on the list of automatic extensions is a harmless no-op. ^No entry point -** will be called more than once for each database connection that is opened. -** -** See also: [sqlite3_reset_auto_extension()]. -*/ -SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); - -/* -** CAPI3REF: Reset Automatic Extension Loading -** -** ^This interface disables all automatic extensions previously -** registered using [sqlite3_auto_extension()]. -*/ -SQLITE_API void sqlite3_reset_auto_extension(void); - -/* -** The interface to the virtual-table mechanism is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - -/* -** Structures used by the virtual table interface -*/ -typedef struct sqlite3_vtab sqlite3_vtab; -typedef struct sqlite3_index_info sqlite3_index_info; -typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; -typedef struct sqlite3_module sqlite3_module; - -/* -** CAPI3REF: Virtual Table Object -** KEYWORDS: sqlite3_module {virtual table module} -** -** This structure, sometimes called a "virtual table module", -** defines the implementation of a [virtual tables]. -** This structure consists mostly of methods for the module. -** -** ^A virtual table module is created by filling in a persistent -** instance of this structure and passing a pointer to that instance -** to [sqlite3_create_module()] or [sqlite3_create_module_v2()]. -** ^The registration remains valid until it is replaced by a different -** module or until the [database connection] closes. The content -** of this structure must not change while it is registered with -** any database connection. -*/ -struct sqlite3_module { - int iVersion; - int (*xCreate)(sqlite3*, void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, char**); - int (*xConnect)(sqlite3*, void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, char**); - int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*); - int (*xDisconnect)(sqlite3_vtab *pVTab); - int (*xDestroy)(sqlite3_vtab *pVTab); - int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor); - int (*xClose)(sqlite3_vtab_cursor*); - int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, - int argc, sqlite3_value **argv); - int (*xNext)(sqlite3_vtab_cursor*); - int (*xEof)(sqlite3_vtab_cursor*); - int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int); - int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid); - int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *); - int (*xBegin)(sqlite3_vtab *pVTab); - int (*xSync)(sqlite3_vtab *pVTab); - int (*xCommit)(sqlite3_vtab *pVTab); - int (*xRollback)(sqlite3_vtab *pVTab); - int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName, - void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), - void **ppArg); - int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); - /* The methods above are in version 1 of the sqlite_module object. Those - ** below are for version 2 and greater. */ - int (*xSavepoint)(sqlite3_vtab *pVTab, int); - int (*xRelease)(sqlite3_vtab *pVTab, int); - int (*xRollbackTo)(sqlite3_vtab *pVTab, int); -}; - -/* -** CAPI3REF: Virtual Table Indexing Information -** KEYWORDS: sqlite3_index_info -** -** The sqlite3_index_info structure and its substructures is used as part -** of the [virtual table] interface to -** pass information into and receive the reply from the [xBestIndex] -** method of a [virtual table module]. The fields under **Inputs** are the -** inputs to xBestIndex and are read-only. xBestIndex inserts its -** results into the **Outputs** fields. -** -** ^(The aConstraint[] array records WHERE clause constraints of the form: -** -**
    column OP expr
    -** -** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is -** stored in aConstraint[].op using one of the -** [SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_ values].)^ -** ^(The index of the column is stored in -** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the -** expr on the right-hand side can be evaluated (and thus the constraint -** is usable) and false if it cannot.)^ -** -** ^The optimizer automatically inverts terms of the form "expr OP column" -** and makes other simplifications to the WHERE clause in an attempt to -** get as many WHERE clause terms into the form shown above as possible. -** ^The aConstraint[] array only reports WHERE clause terms that are -** relevant to the particular virtual table being queried. -** -** ^Information about the ORDER BY clause is stored in aOrderBy[]. -** ^Each term of aOrderBy records a column of the ORDER BY clause. -** -** The [xBestIndex] method must fill aConstraintUsage[] with information -** about what parameters to pass to xFilter. ^If argvIndex>0 then -** the right-hand side of the corresponding aConstraint[] is evaluated -** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit -** is true, then the constraint is assumed to be fully handled by the -** virtual table and is not checked again by SQLite.)^ -** -** ^The idxNum and idxPtr values are recorded and passed into the -** [xFilter] method. -** ^[sqlite3_free()] is used to free idxPtr if and only if -** needToFreeIdxPtr is true. -** -** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in -** the correct order to satisfy the ORDER BY clause so that no separate -** sorting step is required. -** -** ^The estimatedCost value is an estimate of the cost of doing the -** particular lookup. A full scan of a table with N entries should have -** a cost of N. A binary search of a table of N entries should have a -** cost of approximately log(N). -*/ -struct sqlite3_index_info { - /* Inputs */ - int nConstraint; /* Number of entries in aConstraint */ - struct sqlite3_index_constraint { - int iColumn; /* Column on left-hand side of constraint */ - unsigned char op; /* Constraint operator */ - unsigned char usable; /* True if this constraint is usable */ - int iTermOffset; /* Used internally - xBestIndex should ignore */ - } *aConstraint; /* Table of WHERE clause constraints */ - int nOrderBy; /* Number of terms in the ORDER BY clause */ - struct sqlite3_index_orderby { - int iColumn; /* Column number */ - unsigned char desc; /* True for DESC. False for ASC. */ - } *aOrderBy; /* The ORDER BY clause */ - /* Outputs */ - struct sqlite3_index_constraint_usage { - int argvIndex; /* if >0, constraint is part of argv to xFilter */ - unsigned char omit; /* Do not code a test for this constraint */ - } *aConstraintUsage; - int idxNum; /* Number used to identify the index */ - char *idxStr; /* String, possibly obtained from sqlite3_malloc */ - int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ - int orderByConsumed; /* True if output is already ordered */ - double estimatedCost; /* Estimated cost of using this index */ -}; - -/* -** CAPI3REF: Virtual Table Constraint Operator Codes -** -** These macros defined the allowed values for the -** [sqlite3_index_info].aConstraint[].op field. Each value represents -** an operator that is part of a constraint term in the wHERE clause of -** a query that uses a [virtual table]. -*/ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 - -/* -** CAPI3REF: Register A Virtual Table Implementation -** -** ^These routines are used to register a new [virtual table module] name. -** ^Module names must be registered before -** creating a new [virtual table] using the module and before using a -** preexisting [virtual table] for the module. -** -** ^The module name is registered on the [database connection] specified -** by the first parameter. ^The name of the module is given by the -** second parameter. ^The third parameter is a pointer to -** the implementation of the [virtual table module]. ^The fourth -** parameter is an arbitrary client data pointer that is passed through -** into the [xCreate] and [xConnect] methods of the virtual table module -** when a new virtual table is be being created or reinitialized. -** -** ^The sqlite3_create_module_v2() interface has a fifth parameter which -** is a pointer to a destructor for the pClientData. ^SQLite will -** invoke the destructor function (if it is not NULL) when SQLite -** no longer needs the pClientData pointer. ^The destructor will also -** be invoked if the call to sqlite3_create_module_v2() fails. -** ^The sqlite3_create_module() -** interface is equivalent to sqlite3_create_module_v2() with a NULL -** destructor. -*/ -SQLITE_API int sqlite3_create_module( - sqlite3 *db, /* SQLite connection to register module with */ - const char *zName, /* Name of the module */ - const sqlite3_module *p, /* Methods for the module */ - void *pClientData /* Client data for xCreate/xConnect */ -); -SQLITE_API int sqlite3_create_module_v2( - sqlite3 *db, /* SQLite connection to register module with */ - const char *zName, /* Name of the module */ - const sqlite3_module *p, /* Methods for the module */ - void *pClientData, /* Client data for xCreate/xConnect */ - void(*xDestroy)(void*) /* Module destructor function */ -); - -/* -** CAPI3REF: Virtual Table Instance Object -** KEYWORDS: sqlite3_vtab -** -** Every [virtual table module] implementation uses a subclass -** of this object to describe a particular instance -** of the [virtual table]. Each subclass will -** be tailored to the specific needs of the module implementation. -** The purpose of this superclass is to define certain fields that are -** common to all module implementations. -** -** ^Virtual tables methods can set an error message by assigning a -** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should -** take care that any prior string is freed by a call to [sqlite3_free()] -** prior to assigning a new string to zErrMsg. ^After the error message -** is delivered up to the client application, the string will be automatically -** freed by sqlite3_free() and the zErrMsg field will be zeroed. -*/ -struct sqlite3_vtab { - const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* NO LONGER USED */ - char *zErrMsg; /* Error message from sqlite3_mprintf() */ - /* Virtual table implementations will typically add additional fields */ -}; - -/* -** CAPI3REF: Virtual Table Cursor Object -** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} -** -** Every [virtual table module] implementation uses a subclass of the -** following structure to describe cursors that point into the -** [virtual table] and are used -** to loop through the virtual table. Cursors are created using the -** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed -** by the [sqlite3_module.xClose | xClose] method. Cursors are used -** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods -** of the module. Each module implementation will define -** the content of a cursor structure to suit its own needs. -** -** This superclass exists in order to define fields of the cursor that -** are common to all implementations. -*/ -struct sqlite3_vtab_cursor { - sqlite3_vtab *pVtab; /* Virtual table of this cursor */ - /* Virtual table implementations will typically add additional fields */ -}; - -/* -** CAPI3REF: Declare The Schema Of A Virtual Table -** -** ^The [xCreate] and [xConnect] methods of a -** [virtual table module] call this interface -** to declare the format (the names and datatypes of the columns) of -** the virtual tables they implement. -*/ -SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); - -/* -** CAPI3REF: Overload A Function For A Virtual Table -** -** ^(Virtual tables can provide alternative implementations of functions -** using the [xFindFunction] method of the [virtual table module]. -** But global versions of those functions -** must exist in order to be overloaded.)^ -** -** ^(This API makes sure a global version of a function with a particular -** name and number of parameters exists. If no such function exists -** before this API is called, a new function is created.)^ ^The implementation -** of the new function always causes an exception to be thrown. So -** the new function is not good for anything by itself. Its only -** purpose is to be a placeholder function that can be overloaded -** by a [virtual table]. -*/ -SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); - -/* -** The interface to the virtual-table mechanism defined above (back up -** to a comment remarkably similar to this one) is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - -/* -** CAPI3REF: A Handle To An Open BLOB -** KEYWORDS: {BLOB handle} {BLOB handles} -** -** An instance of this object represents an open BLOB on which -** [sqlite3_blob_open | incremental BLOB I/O] can be performed. -** ^Objects of this type are created by [sqlite3_blob_open()] -** and destroyed by [sqlite3_blob_close()]. -** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces -** can be used to read or write small subsections of the BLOB. -** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. -*/ -typedef struct sqlite3_blob sqlite3_blob; - -/* -** CAPI3REF: Open A BLOB For Incremental I/O -** -** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located -** in row iRow, column zColumn, table zTable in database zDb; -** in other words, the same BLOB that would be selected by: -** -**
    -**     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
    -** 
    )^ -** -** ^If the flags parameter is non-zero, then the BLOB is opened for read -** and write access. ^If it is zero, the BLOB is opened for read access. -** ^It is not possible to open a column that is part of an index or primary -** key for writing. ^If [foreign key constraints] are enabled, it is -** not possible to open a column that is part of a [child key] for writing. -** -** ^Note that the database name is not the filename that contains -** the database but rather the symbolic name of the database that -** appears after the AS keyword when the database is connected using [ATTACH]. -** ^For the main database file, the database name is "main". -** ^For TEMP tables, the database name is "temp". -** -** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written -** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set -** to be a null pointer.)^ -** ^This function sets the [database connection] error code and message -** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related -** functions. ^Note that the *ppBlob variable is always initialized in a -** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob -** regardless of the success or failure of this routine. -** -** ^(If the row that a BLOB handle points to is modified by an -** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects -** then the BLOB handle is marked as "expired". -** This is true if any column of the row is changed, even a column -** other than the one the BLOB handle is open on.)^ -** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for -** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. -** ^(Changes written into a BLOB prior to the BLOB expiring are not -** rolled back by the expiration of the BLOB. Such changes will eventually -** commit if the transaction continues to completion.)^ -** -** ^Use the [sqlite3_blob_bytes()] interface to determine the size of -** the opened blob. ^The size of a blob may not be changed by this -** interface. Use the [UPDATE] SQL command to change the size of a -** blob. -** -** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces -** and the built-in [zeroblob] SQL function can be used, if desired, -** to create an empty, zero-filled blob in which to read or write using -** this interface. -** -** To avoid a resource leak, every open [BLOB handle] should eventually -** be released by a call to [sqlite3_blob_close()]. -*/ -SQLITE_API int sqlite3_blob_open( - sqlite3*, - const char *zDb, - const char *zTable, - const char *zColumn, - sqlite3_int64 iRow, - int flags, - sqlite3_blob **ppBlob -); - -/* -** CAPI3REF: Move a BLOB Handle to a New Row -** -** ^This function is used to move an existing blob handle so that it points -** to a different row of the same database table. ^The new row is identified -** by the rowid value passed as the second argument. Only the row can be -** changed. ^The database, table and column on which the blob handle is open -** remain the same. Moving an existing blob handle to a new row can be -** faster than closing the existing handle and opening a new one. -** -** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] - -** it must exist and there must be either a blob or text value stored in -** the nominated column.)^ ^If the new row is not present in the table, or if -** it does not contain a blob or text value, or if another error occurs, an -** SQLite error code is returned and the blob handle is considered aborted. -** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or -** [sqlite3_blob_reopen()] on an aborted blob handle immediately return -** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle -** always returns zero. -** -** ^This function sets the database handle error code and message. -*/ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); - -/* -** CAPI3REF: Close A BLOB Handle -** -** ^Closes an open [BLOB handle]. -** -** ^Closing a BLOB shall cause the current transaction to commit -** if there are no other BLOBs, no pending prepared statements, and the -** database connection is in [autocommit mode]. -** ^If any writes were made to the BLOB, they might be held in cache -** until the close operation if they will fit. -** -** ^(Closing the BLOB often forces the changes -** out to disk and so if any I/O errors occur, they will likely occur -** at the time when the BLOB is closed. Any errors that occur during -** closing are reported as a non-zero return value.)^ -** -** ^(The BLOB is closed unconditionally. Even if this routine returns -** an error code, the BLOB is still closed.)^ -** -** ^Calling this routine with a null pointer (such as would be returned -** by a failed call to [sqlite3_blob_open()]) is a harmless no-op. -*/ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *); - -/* -** CAPI3REF: Return The Size Of An Open BLOB -** -** ^Returns the size in bytes of the BLOB accessible via the -** successfully opened [BLOB handle] in its only argument. ^The -** incremental blob I/O routines can only read or overwriting existing -** blob content; they cannot change the size of a blob. -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite3_blob_open()] and which has not -** been closed by [sqlite3_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -*/ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); - -/* -** CAPI3REF: Read Data From A BLOB Incrementally -** -** ^(This function is used to read data from an open [BLOB handle] into a -** caller-supplied buffer. N bytes of data are copied into buffer Z -** from the open BLOB, starting at offset iOffset.)^ -** -** ^If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is -** less than zero, [SQLITE_ERROR] is returned and no data is read. -** ^The size of the blob (and hence the maximum value of N+iOffset) -** can be determined using the [sqlite3_blob_bytes()] interface. -** -** ^An attempt to read from an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. -** -** ^(On success, sqlite3_blob_read() returns SQLITE_OK. -** Otherwise, an [error code] or an [extended error code] is returned.)^ -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite3_blob_open()] and which has not -** been closed by [sqlite3_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -** -** See also: [sqlite3_blob_write()]. -*/ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); - -/* -** CAPI3REF: Write Data Into A BLOB Incrementally -** -** ^This function is used to write data into an open [BLOB handle] from a -** caller-supplied buffer. ^N bytes of data are copied from the buffer Z -** into the open BLOB, starting at offset iOffset. -** -** ^If the [BLOB handle] passed as the first argument was not opened for -** writing (the flags parameter to [sqlite3_blob_open()] was zero), -** this function returns [SQLITE_READONLY]. -** -** ^This function may only modify the contents of the BLOB; it is -** not possible to increase the size of a BLOB using this API. -** ^If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is written. ^If N is -** less than zero [SQLITE_ERROR] is returned and no data is written. -** The size of the BLOB (and hence the maximum value of N+iOffset) -** can be determined using the [sqlite3_blob_bytes()] interface. -** -** ^An attempt to write to an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred -** before the [BLOB handle] expired are not rolled back by the -** expiration of the handle, though of course those changes might -** have been overwritten by the statement that expired the BLOB handle -** or by other independent statements. -** -** ^(On success, sqlite3_blob_write() returns SQLITE_OK. -** Otherwise, an [error code] or an [extended error code] is returned.)^ -** -** This routine only works on a [BLOB handle] which has been created -** by a prior successful call to [sqlite3_blob_open()] and which has not -** been closed by [sqlite3_blob_close()]. Passing any other pointer in -** to this routine results in undefined and probably undesirable behavior. -** -** See also: [sqlite3_blob_read()]. -*/ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); - -/* -** CAPI3REF: Virtual File System Objects -** -** A virtual filesystem (VFS) is an [sqlite3_vfs] object -** that SQLite uses to interact -** with the underlying operating system. Most SQLite builds come with a -** single default VFS that is appropriate for the host computer. -** New VFSes can be registered and existing VFSes can be unregistered. -** The following interfaces are provided. -** -** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. -** ^Names are case sensitive. -** ^Names are zero-terminated UTF-8 strings. -** ^If there is no match, a NULL pointer is returned. -** ^If zVfsName is NULL then the default VFS is returned. -** -** ^New VFSes are registered with sqlite3_vfs_register(). -** ^Each new VFS becomes the default VFS if the makeDflt flag is set. -** ^The same VFS can be registered multiple times without injury. -** ^To make an existing VFS into the default VFS, register it again -** with the makeDflt flag set. If two different VFSes with the -** same name are registered, the behavior is undefined. If a -** VFS is registered with a name that is NULL or an empty string, -** then the behavior is undefined. -** -** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. -** ^(If the default VFS is unregistered, another VFS is chosen as -** the default. The choice for the new VFS is arbitrary.)^ -*/ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); - -/* -** CAPI3REF: Mutexes -** -** The SQLite core uses these routines for thread -** synchronization. Though they are intended for internal -** use by SQLite, code that links against SQLite is -** permitted to use any of these routines. -** -** The SQLite source code contains multiple implementations -** of these mutex routines. An appropriate implementation -** is selected automatically at compile-time. ^(The following -** implementations are available in the SQLite core: -** -**
      -**
    • SQLITE_MUTEX_PTHREADS -**
    • SQLITE_MUTEX_W32 -**
    • SQLITE_MUTEX_NOOP -**
    )^ -** -** ^The SQLITE_MUTEX_NOOP implementation is a set of routines -** that does no real locking and is appropriate for use in -** a single-threaded application. ^The SQLITE_MUTEX_PTHREADS and -** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix -** and Windows. -** -** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor -** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex -** implementation is included with the library. In this case the -** application must supply a custom mutex implementation using the -** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function -** before calling sqlite3_initialize() or any other public sqlite3_ -** function that calls sqlite3_initialize().)^ -** -** ^The sqlite3_mutex_alloc() routine allocates a new -** mutex and returns a pointer to it. ^If it returns NULL -** that means that a mutex could not be allocated. ^SQLite -** will unwind its stack and return an error. ^(The argument -** to sqlite3_mutex_alloc() is one of these integer constants: -** -**
      -**
    • SQLITE_MUTEX_FAST -**
    • SQLITE_MUTEX_RECURSIVE -**
    • SQLITE_MUTEX_STATIC_MASTER -**
    • SQLITE_MUTEX_STATIC_MEM -**
    • SQLITE_MUTEX_STATIC_MEM2 -**
    • SQLITE_MUTEX_STATIC_PRNG -**
    • SQLITE_MUTEX_STATIC_LRU -**
    • SQLITE_MUTEX_STATIC_LRU2 -**
    )^ -** -** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) -** cause sqlite3_mutex_alloc() to create -** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE -** is used but not necessarily so when SQLITE_MUTEX_FAST is used. -** The mutex implementation does not need to make a distinction -** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does -** not want to. ^SQLite will only request a recursive mutex in -** cases where it really needs one. ^If a faster non-recursive mutex -** implementation is available on the host platform, the mutex subsystem -** might return such a mutex in response to SQLITE_MUTEX_FAST. -** -** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other -** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return -** a pointer to a static preexisting mutex. ^Six static mutexes are -** used by the current version of SQLite. Future versions of SQLite -** may add additional static mutexes. Static mutexes are for internal -** use by SQLite only. Applications that use SQLite mutexes should -** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or -** SQLITE_MUTEX_RECURSIVE. -** -** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST -** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() -** returns a different mutex on every call. ^But for the static -** mutex types, the same mutex is returned on every call that has -** the same type number. -** -** ^The sqlite3_mutex_free() routine deallocates a previously -** allocated dynamic mutex. ^SQLite is careful to deallocate every -** dynamic mutex that it allocates. The dynamic mutexes must not be in -** use when they are deallocated. Attempting to deallocate a static -** mutex results in undefined behavior. ^SQLite never deallocates -** a static mutex. -** -** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt -** to enter a mutex. ^If another thread is already within the mutex, -** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return -** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] -** upon successful entry. ^(Mutexes created using -** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. -** In such cases the, -** mutex must be exited an equal number of times before another thread -** can enter.)^ ^(If the same thread tries to enter any other -** kind of mutex more than once, the behavior is undefined. -** SQLite will never exhibit -** such behavior in its own use of mutexes.)^ -** -** ^(Some systems (for example, Windows 95) do not support the operation -** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() -** will always return SQLITE_BUSY. The SQLite core only ever uses -** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^ -** -** ^The sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. ^(The behavior -** is undefined if the mutex is not currently entered by the -** calling thread or is not currently allocated. SQLite will -** never do either.)^ -** -** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or -** sqlite3_mutex_leave() is a NULL pointer, then all three routines -** behave as no-ops. -** -** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. -*/ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); - -/* -** CAPI3REF: Mutex Methods Object -** -** An instance of this structure defines the low-level routines -** used to allocate and use mutexes. -** -** Usually, the default mutex implementations provided by SQLite are -** sufficient, however the user has the option of substituting a custom -** implementation for specialized deployments or systems for which SQLite -** does not provide a suitable implementation. In this case, the user -** creates and populates an instance of this structure to pass -** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. -** Additionally, an instance of this structure can be used as an -** output variable when querying the system for the current mutex -** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. -** -** ^The xMutexInit method defined by this structure is invoked as -** part of system initialization by the sqlite3_initialize() function. -** ^The xMutexInit routine is called by SQLite exactly once for each -** effective call to [sqlite3_initialize()]. -** -** ^The xMutexEnd method defined by this structure is invoked as -** part of system shutdown by the sqlite3_shutdown() function. The -** implementation of this method is expected to release all outstanding -** resources obtained by the mutex methods implementation, especially -** those obtained by the xMutexInit method. ^The xMutexEnd() -** interface is invoked exactly once for each call to [sqlite3_shutdown()]. -** -** ^(The remaining seven methods defined by this structure (xMutexAlloc, -** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and -** xMutexNotheld) implement the following interfaces (respectively): -** -**
      -**
    • [sqlite3_mutex_alloc()]
    • -**
    • [sqlite3_mutex_free()]
    • -**
    • [sqlite3_mutex_enter()]
    • -**
    • [sqlite3_mutex_try()]
    • -**
    • [sqlite3_mutex_leave()]
    • -**
    • [sqlite3_mutex_held()]
    • -**
    • [sqlite3_mutex_notheld()]
    • -**
    )^ -** -** The only difference is that the public sqlite3_XXX functions enumerated -** above silently ignore any invocations that pass a NULL pointer instead -** of a valid mutex handle. The implementations of the methods defined -** by this structure are not required to handle this case, the results -** of passing a NULL pointer instead of a valid mutex handle are undefined -** (i.e. it is acceptable to provide an implementation that segfaults if -** it is passed a NULL pointer). -** -** The xMutexInit() method must be threadsafe. ^It must be harmless to -** invoke xMutexInit() multiple times within the same process and without -** intervening calls to xMutexEnd(). Second and subsequent calls to -** xMutexInit() must be no-ops. -** -** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] -** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory -** allocation for a static mutex. ^However xMutexAlloc() may use SQLite -** memory allocation for a fast or recursive mutex. -** -** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is -** called, but only if the prior call to xMutexInit returned SQLITE_OK. -** If xMutexInit fails in any way, it is expected to clean up after itself -** prior to returning. -*/ -typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; -struct sqlite3_mutex_methods { - int (*xMutexInit)(void); - int (*xMutexEnd)(void); - sqlite3_mutex *(*xMutexAlloc)(int); - void (*xMutexFree)(sqlite3_mutex *); - void (*xMutexEnter)(sqlite3_mutex *); - int (*xMutexTry)(sqlite3_mutex *); - void (*xMutexLeave)(sqlite3_mutex *); - int (*xMutexHeld)(sqlite3_mutex *); - int (*xMutexNotheld)(sqlite3_mutex *); -}; - -/* -** CAPI3REF: Mutex Verification Routines -** -** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines -** are intended for use inside assert() statements. ^The SQLite core -** never uses these routines except inside an assert() and applications -** are advised to follow the lead of the core. ^The SQLite core only -** provides implementations for these routines when it is compiled -** with the SQLITE_DEBUG flag. ^External mutex implementations -** are only required to provide these routines if SQLITE_DEBUG is -** defined and if NDEBUG is not defined. -** -** ^These routines should return true if the mutex in their argument -** is held or not held, respectively, by the calling thread. -** -** ^The implementation is not required to provide versions of these -** routines that actually work. If the implementation does not provide working -** versions of these routines, it should at least provide stubs that always -** return true so that one does not get spurious assertion failures. -** -** ^If the argument to sqlite3_mutex_held() is a NULL pointer then -** the routine should return 1. This seems counter-intuitive since -** clearly the mutex cannot be held if it does not exist. But -** the reason the mutex does not exist is because the build is not -** using mutexes. And we do not want the assert() containing the -** call to sqlite3_mutex_held() to fail, so a non-zero return is -** the appropriate thing to do. ^The sqlite3_mutex_notheld() -** interface should also return 1 when given a NULL pointer. -*/ -#ifndef NDEBUG -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); -#endif - -/* -** CAPI3REF: Mutex Types -** -** The [sqlite3_mutex_alloc()] interface takes a single argument -** which is one of these integer constants. -** -** The set of static mutexes may change from one SQLite release to the -** next. Applications that override the built-in mutex logic must be -** prepared to accommodate additional static mutexes. -*/ -#define SQLITE_MUTEX_FAST 0 -#define SQLITE_MUTEX_RECURSIVE 1 -#define SQLITE_MUTEX_STATIC_MASTER 2 -#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ -#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ -#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ -#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ -#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ -#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ -#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ - -/* -** CAPI3REF: Retrieve the mutex for a database connection -** -** ^This interface returns a pointer the [sqlite3_mutex] object that -** serializes access to the [database connection] given in the argument -** when the [threading mode] is Serialized. -** ^If the [threading mode] is Single-thread or Multi-thread then this -** routine returns a NULL pointer. -*/ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); - -/* -** CAPI3REF: Low-Level Control Of Database Files -** -** ^The [sqlite3_file_control()] interface makes a direct call to the -** xFileControl method for the [sqlite3_io_methods] object associated -** with a particular database identified by the second argument. ^The -** name of the database is "main" for the main database or "temp" for the -** TEMP database, or the name that appears after the AS keyword for -** databases that are added using the [ATTACH] SQL command. -** ^A NULL pointer can be used in place of "main" to refer to the -** main database file. -** ^The third and fourth parameters to this routine -** are passed directly through to the second and third parameters of -** the xFileControl method. ^The return value of the xFileControl -** method becomes the return value of this routine. -** -** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes -** a pointer to the underlying [sqlite3_file] object to be written into -** the space pointed to by the 4th parameter. ^The SQLITE_FCNTL_FILE_POINTER -** case is a short-circuit path which does not actually invoke the -** underlying sqlite3_io_methods.xFileControl method. -** -** ^If the second parameter (zDbName) does not match the name of any -** open database file, then SQLITE_ERROR is returned. ^This error -** code is not remembered and will not be recalled by [sqlite3_errcode()] -** or [sqlite3_errmsg()]. The underlying xFileControl method might -** also return SQLITE_ERROR. There is no way to distinguish between -** an incorrect zDbName and an SQLITE_ERROR return from the underlying -** xFileControl method. -** -** See also: [SQLITE_FCNTL_LOCKSTATE] -*/ -SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); - -/* -** CAPI3REF: Testing Interface -** -** ^The sqlite3_test_control() interface is used to read out internal -** state of SQLite and to inject faults into SQLite for testing -** purposes. ^The first parameter is an operation code that determines -** the number, meaning, and operation of all subsequent parameters. -** -** This interface is not for use by applications. It exists solely -** for verifying the correct operation of the SQLite library. Depending -** on how the SQLite library is compiled, this interface might not exist. -** -** The details of the operation codes, their meanings, the parameters -** they take, and what they do are all subject to change without notice. -** Unlike most of the SQLite API, this function is not guaranteed to -** operate consistently from one release to the next. -*/ -SQLITE_API int sqlite3_test_control(int op, ...); - -/* -** CAPI3REF: Testing Interface Operation Codes -** -** These constants are the valid operation code parameters used -** as the first argument to [sqlite3_test_control()]. -** -** These parameters and their meanings are subject to change -** without notice. These values are for testing purposes only. -** Applications should not use any of these parameters or the -** [sqlite3_test_control()] interface. -*/ -#define SQLITE_TESTCTRL_FIRST 5 -#define SQLITE_TESTCTRL_PRNG_SAVE 5 -#define SQLITE_TESTCTRL_PRNG_RESTORE 6 -#define SQLITE_TESTCTRL_PRNG_RESET 7 -#define SQLITE_TESTCTRL_BITVEC_TEST 8 -#define SQLITE_TESTCTRL_FAULT_INSTALL 9 -#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 -#define SQLITE_TESTCTRL_PENDING_BYTE 11 -#define SQLITE_TESTCTRL_ASSERT 12 -#define SQLITE_TESTCTRL_ALWAYS 13 -#define SQLITE_TESTCTRL_RESERVE 14 -#define SQLITE_TESTCTRL_OPTIMIZATIONS 15 -#define SQLITE_TESTCTRL_ISKEYWORD 16 -#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 -#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 -#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 -#define SQLITE_TESTCTRL_LAST 19 - -/* -** CAPI3REF: SQLite Runtime Status -** -** ^This interface is used to retrieve runtime status information -** about the performance of SQLite, and optionally to reset various -** highwater marks. ^The first argument is an integer code for -** the specific parameter to measure. ^(Recognized integer codes -** are of the form [status parameters | SQLITE_STATUS_...].)^ -** ^The current value of the parameter is returned into *pCurrent. -** ^The highest recorded value is returned in *pHighwater. ^If the -** resetFlag is true, then the highest record value is reset after -** *pHighwater is written. ^(Some parameters do not record the highest -** value. For those parameters -** nothing is written into *pHighwater and the resetFlag is ignored.)^ -** ^(Other parameters record only the highwater mark and not the current -** value. For these latter parameters nothing is written into *pCurrent.)^ -** -** ^The sqlite3_status() routine returns SQLITE_OK on success and a -** non-zero [error code] on failure. -** -** This routine is threadsafe but is not atomic. This routine can be -** called while other threads are running the same or different SQLite -** interfaces. However the values returned in *pCurrent and -** *pHighwater reflect the status of SQLite at different points in time -** and it is possible that another thread might change the parameter -** in between the times when *pCurrent and *pHighwater are written. -** -** See also: [sqlite3_db_status()] -*/ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); - - -/* -** CAPI3REF: Status Parameters -** KEYWORDS: {status parameters} -** -** These integer constants designate various run-time status parameters -** that can be returned by [sqlite3_status()]. -** -**
    -** [[SQLITE_STATUS_MEMORY_USED]] ^(
    SQLITE_STATUS_MEMORY_USED
    -**
    This parameter is the current amount of memory checked out -** using [sqlite3_malloc()], either directly or indirectly. The -** figure includes calls made to [sqlite3_malloc()] by the application -** and internal memory usage by the SQLite library. Scratch memory -** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache -** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in -** this parameter. The amount returned is the sum of the allocation -** sizes as reported by the xSize method in [sqlite3_mem_methods].
    )^ -** -** [[SQLITE_STATUS_MALLOC_SIZE]] ^(
    SQLITE_STATUS_MALLOC_SIZE
    -**
    This parameter records the largest memory allocation request -** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their -** internal equivalents). Only the value returned in the -** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
    )^ -** -** [[SQLITE_STATUS_MALLOC_COUNT]] ^(
    SQLITE_STATUS_MALLOC_COUNT
    -**
    This parameter records the number of separate memory allocations -** currently checked out.
    )^ -** -** [[SQLITE_STATUS_PAGECACHE_USED]] ^(
    SQLITE_STATUS_PAGECACHE_USED
    -**
    This parameter returns the number of pages used out of the -** [pagecache memory allocator] that was configured using -** [SQLITE_CONFIG_PAGECACHE]. The -** value returned is in pages, not in bytes.
    )^ -** -** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] -** ^(
    SQLITE_STATUS_PAGECACHE_OVERFLOW
    -**
    This parameter returns the number of bytes of page cache -** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] -** buffer and where forced to overflow to [sqlite3_malloc()]. The -** returned value includes allocations that overflowed because they -** where too large (they were larger than the "sz" parameter to -** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because -** no space was left in the page cache.
    )^ -** -** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(
    SQLITE_STATUS_PAGECACHE_SIZE
    -**
    This parameter records the largest memory allocation request -** handed to [pagecache memory allocator]. Only the value returned in the -** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
    )^ -** -** [[SQLITE_STATUS_SCRATCH_USED]] ^(
    SQLITE_STATUS_SCRATCH_USED
    -**
    This parameter returns the number of allocations used out of the -** [scratch memory allocator] configured using -** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not -** in bytes. Since a single thread may only have one scratch allocation -** outstanding at time, this parameter also reports the number of threads -** using scratch memory at the same time.
    )^ -** -** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(
    SQLITE_STATUS_SCRATCH_OVERFLOW
    -**
    This parameter returns the number of bytes of scratch memory -** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] -** buffer and where forced to overflow to [sqlite3_malloc()]. The values -** returned include overflows because the requested allocation was too -** larger (that is, because the requested allocation was larger than the -** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer -** slots were available. -**
    )^ -** -** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(
    SQLITE_STATUS_SCRATCH_SIZE
    -**
    This parameter records the largest memory allocation request -** handed to [scratch memory allocator]. Only the value returned in the -** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
    )^ -** -** [[SQLITE_STATUS_PARSER_STACK]] ^(
    SQLITE_STATUS_PARSER_STACK
    -**
    This parameter records the deepest parser stack. It is only -** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].
    )^ -**
    -** -** New status parameters may be added from time to time. -*/ -#define SQLITE_STATUS_MEMORY_USED 0 -#define SQLITE_STATUS_PAGECACHE_USED 1 -#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2 -#define SQLITE_STATUS_SCRATCH_USED 3 -#define SQLITE_STATUS_SCRATCH_OVERFLOW 4 -#define SQLITE_STATUS_MALLOC_SIZE 5 -#define SQLITE_STATUS_PARSER_STACK 6 -#define SQLITE_STATUS_PAGECACHE_SIZE 7 -#define SQLITE_STATUS_SCRATCH_SIZE 8 -#define SQLITE_STATUS_MALLOC_COUNT 9 - -/* -** CAPI3REF: Database Connection Status -** -** ^This interface is used to retrieve runtime status information -** about a single [database connection]. ^The first argument is the -** database connection object to be interrogated. ^The second argument -** is an integer constant, taken from the set of -** [SQLITE_DBSTATUS options], that -** determines the parameter to interrogate. The set of -** [SQLITE_DBSTATUS options] is likely -** to grow in future releases of SQLite. -** -** ^The current value of the requested parameter is written into *pCur -** and the highest instantaneous value is written into *pHiwtr. ^If -** the resetFlg is true, then the highest instantaneous value is -** reset back down to the current value. -** -** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a -** non-zero [error code] on failure. -** -** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. -*/ -SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); - -/* -** CAPI3REF: Status Parameters for database connections -** KEYWORDS: {SQLITE_DBSTATUS options} -** -** These constants are the available integer "verbs" that can be passed as -** the second argument to the [sqlite3_db_status()] interface. -** -** New verbs may be added in future releases of SQLite. Existing verbs -** might be discontinued. Applications should check the return code from -** [sqlite3_db_status()] to make sure that the call worked. -** The [sqlite3_db_status()] interface will return a non-zero error code -** if a discontinued or unsupported verb is invoked. -** -**
    -** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(
    SQLITE_DBSTATUS_LOOKASIDE_USED
    -**
    This parameter returns the number of lookaside memory slots currently -** checked out.
    )^ -** -** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(
    SQLITE_DBSTATUS_LOOKASIDE_HIT
    -**
    This parameter returns the number malloc attempts that were -** satisfied using lookaside memory. Only the high-water value is meaningful; -** the current value is always zero.)^ -** -** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] -** ^(
    SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
    -**
    This parameter returns the number malloc attempts that might have -** been satisfied using lookaside memory but failed due to the amount of -** memory requested being larger than the lookaside slot size. -** Only the high-water value is meaningful; -** the current value is always zero.)^ -** -** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] -** ^(
    SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
    -**
    This parameter returns the number malloc attempts that might have -** been satisfied using lookaside memory but failed due to all lookaside -** memory already being in use. -** Only the high-water value is meaningful; -** the current value is always zero.)^ -** -** [[SQLITE_DBSTATUS_CACHE_USED]] ^(
    SQLITE_DBSTATUS_CACHE_USED
    -**
    This parameter returns the approximate number of of bytes of heap -** memory used by all pager caches associated with the database connection.)^ -** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. -** -** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
    SQLITE_DBSTATUS_SCHEMA_USED
    -**
    This parameter returns the approximate number of of bytes of heap -** memory used to store the schema for all databases associated -** with the connection - main, temp, and any [ATTACH]-ed databases.)^ -** ^The full amount of memory used by the schemas is reported, even if the -** schema memory is shared with other database connections due to -** [shared cache mode] being enabled. -** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. -** -** [[SQLITE_DBSTATUS_STMT_USED]] ^(
    SQLITE_DBSTATUS_STMT_USED
    -**
    This parameter returns the approximate number of of bytes of heap -** and lookaside memory used by all prepared statements associated with -** the database connection.)^ -** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0. -**
    -** -** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(
    SQLITE_DBSTATUS_CACHE_HIT
    -**
    This parameter returns the number of pager cache hits that have -** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT -** is always 0. -**
    -** -** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(
    SQLITE_DBSTATUS_CACHE_MISS
    -**
    This parameter returns the number of pager cache misses that have -** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS -** is always 0. -**
    -** -** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(
    SQLITE_DBSTATUS_CACHE_WRITE
    -**
    This parameter returns the number of dirty cache entries that have -** been written to disk. Specifically, the number of pages written to the -** wal file in wal mode databases, or the number of pages written to the -** database file in rollback mode databases. Any pages written as part of -** transaction rollback or database recovery operations are not included. -** If an IO or other error occurs while writing a page to disk, the effect -** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The -** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. -**
    -**
    -*/ -#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 -#define SQLITE_DBSTATUS_CACHE_USED 1 -#define SQLITE_DBSTATUS_SCHEMA_USED 2 -#define SQLITE_DBSTATUS_STMT_USED 3 -#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 -#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 -#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 -#define SQLITE_DBSTATUS_CACHE_HIT 7 -#define SQLITE_DBSTATUS_CACHE_MISS 8 -#define SQLITE_DBSTATUS_CACHE_WRITE 9 -#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */ - - -/* -** CAPI3REF: Prepared Statement Status -** -** ^(Each prepared statement maintains various -** [SQLITE_STMTSTATUS counters] that measure the number -** of times it has performed specific operations.)^ These counters can -** be used to monitor the performance characteristics of the prepared -** statements. For example, if the number of table steps greatly exceeds -** the number of table searches or result rows, that would tend to indicate -** that the prepared statement is using a full table scan rather than -** an index. -** -** ^(This interface is used to retrieve and reset counter values from -** a [prepared statement]. The first argument is the prepared statement -** object to be interrogated. The second argument -** is an integer code for a specific [SQLITE_STMTSTATUS counter] -** to be interrogated.)^ -** ^The current value of the requested counter is returned. -** ^If the resetFlg is true, then the counter is reset to zero after this -** interface call returns. -** -** See also: [sqlite3_status()] and [sqlite3_db_status()]. -*/ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); - -/* -** CAPI3REF: Status Parameters for prepared statements -** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters} -** -** These preprocessor macros define integer codes that name counter -** values associated with the [sqlite3_stmt_status()] interface. -** The meanings of the various counters are as follows: -** -**
    -** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]]
    SQLITE_STMTSTATUS_FULLSCAN_STEP
    -**
    ^This is the number of times that SQLite has stepped forward in -** a table as part of a full table scan. Large numbers for this counter -** may indicate opportunities for performance improvement through -** careful use of indices.
    -** -** [[SQLITE_STMTSTATUS_SORT]]
    SQLITE_STMTSTATUS_SORT
    -**
    ^This is the number of sort operations that have occurred. -** A non-zero value in this counter may indicate an opportunity to -** improvement performance through careful use of indices.
    -** -** [[SQLITE_STMTSTATUS_AUTOINDEX]]
    SQLITE_STMTSTATUS_AUTOINDEX
    -**
    ^This is the number of rows inserted into transient indices that -** were created automatically in order to help joins run faster. -** A non-zero value in this counter may indicate an opportunity to -** improvement performance by adding permanent indices that do not -** need to be reinitialized each time the statement is run.
    -**
    -*/ -#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 -#define SQLITE_STMTSTATUS_SORT 2 -#define SQLITE_STMTSTATUS_AUTOINDEX 3 - -/* -** CAPI3REF: Custom Page Cache Object -** -** The sqlite3_pcache type is opaque. It is implemented by -** the pluggable module. The SQLite core has no knowledge of -** its size or internal structure and never deals with the -** sqlite3_pcache object except by holding and passing pointers -** to the object. -** -** See [sqlite3_pcache_methods2] for additional information. -*/ -typedef struct sqlite3_pcache sqlite3_pcache; - -/* -** CAPI3REF: Custom Page Cache Object -** -** The sqlite3_pcache_page object represents a single page in the -** page cache. The page cache will allocate instances of this -** object. Various methods of the page cache use pointers to instances -** of this object as parameters or as their return value. -** -** See [sqlite3_pcache_methods2] for additional information. -*/ -typedef struct sqlite3_pcache_page sqlite3_pcache_page; -struct sqlite3_pcache_page { - void *pBuf; /* The content of the page */ - void *pExtra; /* Extra information associated with the page */ -}; - -/* -** CAPI3REF: Application Defined Page Cache. -** KEYWORDS: {page cache} -** -** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can -** register an alternative page cache implementation by passing in an -** instance of the sqlite3_pcache_methods2 structure.)^ -** In many applications, most of the heap memory allocated by -** SQLite is used for the page cache. -** By implementing a -** custom page cache using this API, an application can better control -** the amount of memory consumed by SQLite, the way in which -** that memory is allocated and released, and the policies used to -** determine exactly which parts of a database file are cached and for -** how long. -** -** The alternative page cache mechanism is an -** extreme measure that is only needed by the most demanding applications. -** The built-in page cache is recommended for most uses. -** -** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an -** internal buffer by SQLite within the call to [sqlite3_config]. Hence -** the application may discard the parameter after the call to -** [sqlite3_config()] returns.)^ -** -** [[the xInit() page cache method]] -** ^(The xInit() method is called once for each effective -** call to [sqlite3_initialize()])^ -** (usually only once during the lifetime of the process). ^(The xInit() -** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^ -** The intent of the xInit() method is to set up global data structures -** required by the custom page cache implementation. -** ^(If the xInit() method is NULL, then the -** built-in default page cache is used instead of the application defined -** page cache.)^ -** -** [[the xShutdown() page cache method]] -** ^The xShutdown() method is called by [sqlite3_shutdown()]. -** It can be used to clean up -** any outstanding resources before process shutdown, if required. -** ^The xShutdown() method may be NULL. -** -** ^SQLite automatically serializes calls to the xInit method, -** so the xInit method need not be threadsafe. ^The -** xShutdown method is only called from [sqlite3_shutdown()] so it does -** not need to be threadsafe either. All other methods must be threadsafe -** in multithreaded applications. -** -** ^SQLite will never invoke xInit() more than once without an intervening -** call to xShutdown(). -** -** [[the xCreate() page cache methods]] -** ^SQLite invokes the xCreate() method to construct a new cache instance. -** SQLite will typically create one cache instance for each open database file, -** though this is not guaranteed. ^The -** first parameter, szPage, is the size in bytes of the pages that must -** be allocated by the cache. ^szPage will always a power of two. ^The -** second parameter szExtra is a number of bytes of extra storage -** associated with each page cache entry. ^The szExtra parameter will -** a number less than 250. SQLite will use the -** extra szExtra bytes on each page to store metadata about the underlying -** database page on disk. The value passed into szExtra depends -** on the SQLite version, the target platform, and how SQLite was compiled. -** ^The third argument to xCreate(), bPurgeable, is true if the cache being -** created will be used to cache database pages of a file stored on disk, or -** false if it is used for an in-memory database. The cache implementation -** does not have to do anything special based with the value of bPurgeable; -** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will -** never invoke xUnpin() except to deliberately delete a page. -** ^In other words, calls to xUnpin() on a cache with bPurgeable set to -** false will always have the "discard" flag set to true. -** ^Hence, a cache created with bPurgeable false will -** never contain any unpinned pages. -** -** [[the xCachesize() page cache method]] -** ^(The xCachesize() method may be called at any time by SQLite to set the -** suggested maximum cache-size (number of pages stored by) the cache -** instance passed as the first argument. This is the value configured using -** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable -** parameter, the implementation is not required to do anything with this -** value; it is advisory only. -** -** [[the xPagecount() page cache methods]] -** The xPagecount() method must return the number of pages currently -** stored in the cache, both pinned and unpinned. -** -** [[the xFetch() page cache methods]] -** The xFetch() method locates a page in the cache and returns a pointer to -** an sqlite3_pcache_page object associated with that page, or a NULL pointer. -** The pBuf element of the returned sqlite3_pcache_page object will be a -** pointer to a buffer of szPage bytes used to store the content of a -** single database page. The pExtra element of sqlite3_pcache_page will be -** a pointer to the szExtra bytes of extra storage that SQLite has requested -** for each entry in the page cache. -** -** The page to be fetched is determined by the key. ^The minimum key value -** is 1. After it has been retrieved using xFetch, the page is considered -** to be "pinned". -** -** If the requested page is already in the page cache, then the page cache -** implementation must return a pointer to the page buffer with its content -** intact. If the requested page is not already in the cache, then the -** cache implementation should use the value of the createFlag -** parameter to help it determined what action to take: -** -** -**
    createFlag Behavior when page is not already in cache -**
    0 Do not allocate a new page. Return NULL. -**
    1 Allocate a new page if it easy and convenient to do so. -** Otherwise return NULL. -**
    2 Make every effort to allocate a new page. Only return -** NULL if allocating a new page is effectively impossible. -**
    -** -** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite -** will only use a createFlag of 2 after a prior call with a createFlag of 1 -** failed.)^ In between the to xFetch() calls, SQLite may -** attempt to unpin one or more cache pages by spilling the content of -** pinned pages to disk and synching the operating system disk cache. -** -** [[the xUnpin() page cache method]] -** ^xUnpin() is called by SQLite with a pointer to a currently pinned page -** as its second argument. If the third parameter, discard, is non-zero, -** then the page must be evicted from the cache. -** ^If the discard parameter is -** zero, then the page may be discarded or retained at the discretion of -** page cache implementation. ^The page cache implementation -** may choose to evict unpinned pages at any time. -** -** The cache must not perform any reference counting. A single -** call to xUnpin() unpins the page regardless of the number of prior calls -** to xFetch(). -** -** [[the xRekey() page cache methods]] -** The xRekey() method is used to change the key value associated with the -** page passed as the second argument. If the cache -** previously contains an entry associated with newKey, it must be -** discarded. ^Any prior cache entry associated with newKey is guaranteed not -** to be pinned. -** -** When SQLite calls the xTruncate() method, the cache must discard all -** existing cache entries with page numbers (keys) greater than or equal -** to the value of the iLimit parameter passed to xTruncate(). If any -** of these pages are pinned, they are implicitly unpinned, meaning that -** they can be safely discarded. -** -** [[the xDestroy() page cache method]] -** ^The xDestroy() method is used to delete a cache allocated by xCreate(). -** All resources associated with the specified cache should be freed. ^After -** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] -** handle invalid, and will not use it with any other sqlite3_pcache_methods2 -** functions. -** -** [[the xShrink() page cache method]] -** ^SQLite invokes the xShrink() method when it wants the page cache to -** free up as much of heap memory as possible. The page cache implementation -** is not obligated to free any memory, but well-behaved implementations should -** do their best. -*/ -typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; -struct sqlite3_pcache_methods2 { - int iVersion; - void *pArg; - int (*xInit)(void*); - void (*xShutdown)(void*); - sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable); - void (*xCachesize)(sqlite3_pcache*, int nCachesize); - int (*xPagecount)(sqlite3_pcache*); - sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); - void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); - void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, - unsigned oldKey, unsigned newKey); - void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); - void (*xDestroy)(sqlite3_pcache*); - void (*xShrink)(sqlite3_pcache*); -}; - -/* -** This is the obsolete pcache_methods object that has now been replaced -** by sqlite3_pcache_methods2. This object is not used by SQLite. It is -** retained in the header file for backwards compatibility only. -*/ -typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; -struct sqlite3_pcache_methods { - void *pArg; - int (*xInit)(void*); - void (*xShutdown)(void*); - sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable); - void (*xCachesize)(sqlite3_pcache*, int nCachesize); - int (*xPagecount)(sqlite3_pcache*); - void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); - void (*xUnpin)(sqlite3_pcache*, void*, int discard); - void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); - void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); - void (*xDestroy)(sqlite3_pcache*); -}; - - -/* -** CAPI3REF: Online Backup Object -** -** The sqlite3_backup object records state information about an ongoing -** online backup operation. ^The sqlite3_backup object is created by -** a call to [sqlite3_backup_init()] and is destroyed by a call to -** [sqlite3_backup_finish()]. -** -** See Also: [Using the SQLite Online Backup API] -*/ -typedef struct sqlite3_backup sqlite3_backup; - -/* -** CAPI3REF: Online Backup API. -** -** The backup API copies the content of one database into another. -** It is useful either for creating backups of databases or -** for copying in-memory databases to or from persistent files. -** -** See Also: [Using the SQLite Online Backup API] -** -** ^SQLite holds a write transaction open on the destination database file -** for the duration of the backup operation. -** ^The source database is read-locked only while it is being read; -** it is not locked continuously for the entire backup operation. -** ^Thus, the backup may be performed on a live source database without -** preventing other database connections from -** reading or writing to the source database while the backup is underway. -** -** ^(To perform a backup operation: -**
      -**
    1. sqlite3_backup_init() is called once to initialize the -** backup, -**
    2. sqlite3_backup_step() is called one or more times to transfer -** the data between the two databases, and finally -**
    3. sqlite3_backup_finish() is called to release all resources -** associated with the backup operation. -**
    )^ -** There should be exactly one call to sqlite3_backup_finish() for each -** successful call to sqlite3_backup_init(). -** -** [[sqlite3_backup_init()]] sqlite3_backup_init() -** -** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the -** [database connection] associated with the destination database -** and the database name, respectively. -** ^The database name is "main" for the main database, "temp" for the -** temporary database, or the name specified after the AS keyword in -** an [ATTACH] statement for an attached database. -** ^The S and M arguments passed to -** sqlite3_backup_init(D,N,S,M) identify the [database connection] -** and database name of the source database, respectively. -** ^The source and destination [database connections] (parameters S and D) -** must be different or else sqlite3_backup_init(D,N,S,M) will fail with -** an error. -** -** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is -** returned and an error code and error message are stored in the -** destination [database connection] D. -** ^The error code and message for the failed call to sqlite3_backup_init() -** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or -** [sqlite3_errmsg16()] functions. -** ^A successful call to sqlite3_backup_init() returns a pointer to an -** [sqlite3_backup] object. -** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and -** sqlite3_backup_finish() functions to perform the specified backup -** operation. -** -** [[sqlite3_backup_step()]] sqlite3_backup_step() -** -** ^Function sqlite3_backup_step(B,N) will copy up to N pages between -** the source and destination databases specified by [sqlite3_backup] object B. -** ^If N is negative, all remaining source pages are copied. -** ^If sqlite3_backup_step(B,N) successfully copies N pages and there -** are still more pages to be copied, then the function returns [SQLITE_OK]. -** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages -** from source to destination, then it returns [SQLITE_DONE]. -** ^If an error occurs while running sqlite3_backup_step(B,N), -** then an [error code] is returned. ^As well as [SQLITE_OK] and -** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], -** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. -** -** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if -**
      -**
    1. the destination database was opened read-only, or -**
    2. the destination database is using write-ahead-log journaling -** and the destination and source page sizes differ, or -**
    3. the destination database is an in-memory database and the -** destination and source page sizes differ. -**
    )^ -** -** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then -** the [sqlite3_busy_handler | busy-handler function] -** is invoked (if one is specified). ^If the -** busy-handler returns non-zero before the lock is available, then -** [SQLITE_BUSY] is returned to the caller. ^In this case the call to -** sqlite3_backup_step() can be retried later. ^If the source -** [database connection] -** is being used to write to the source database when sqlite3_backup_step() -** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this -** case the call to sqlite3_backup_step() can be retried later on. ^(If -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or -** [SQLITE_READONLY] is returned, then -** there is no point in retrying the call to sqlite3_backup_step(). These -** errors are considered fatal.)^ The application must accept -** that the backup operation has failed and pass the backup operation handle -** to the sqlite3_backup_finish() to release associated resources. -** -** ^The first call to sqlite3_backup_step() obtains an exclusive lock -** on the destination file. ^The exclusive lock is not released until either -** sqlite3_backup_finish() is called or the backup operation is complete -** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to -** sqlite3_backup_step() obtains a [shared lock] on the source database that -** lasts for the duration of the sqlite3_backup_step() call. -** ^Because the source database is not locked between calls to -** sqlite3_backup_step(), the source database may be modified mid-way -** through the backup process. ^If the source database is modified by an -** external process or via a database connection other than the one being -** used by the backup operation, then the backup will be automatically -** restarted by the next call to sqlite3_backup_step(). ^If the source -** database is modified by the using the same database connection as is used -** by the backup operation, then the backup database is automatically -** updated at the same time. -** -** [[sqlite3_backup_finish()]] sqlite3_backup_finish() -** -** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the -** application wishes to abandon the backup operation, the application -** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). -** ^The sqlite3_backup_finish() interfaces releases all -** resources associated with the [sqlite3_backup] object. -** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any -** active write-transaction on the destination database is rolled back. -** The [sqlite3_backup] object is invalid -** and may not be used following a call to sqlite3_backup_finish(). -** -** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no -** sqlite3_backup_step() errors occurred, regardless or whether or not -** sqlite3_backup_step() completed. -** ^If an out-of-memory condition or IO error occurred during any prior -** sqlite3_backup_step() call on the same [sqlite3_backup] object, then -** sqlite3_backup_finish() returns the corresponding [error code]. -** -** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() -** is not a permanent error and does not affect the return value of -** sqlite3_backup_finish(). -** -** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] -** sqlite3_backup_remaining() and sqlite3_backup_pagecount() -** -** ^Each call to sqlite3_backup_step() sets two values inside -** the [sqlite3_backup] object: the number of pages still to be backed -** up and the total number of pages in the source database file. -** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces -** retrieve these two values, respectively. -** -** ^The values returned by these functions are only updated by -** sqlite3_backup_step(). ^If the source database is modified during a backup -** operation, then the values are not updated to account for any extra -** pages that need to be updated or the size of the source database file -** changing. -** -** Concurrent Usage of Database Handles -** -** ^The source [database connection] may be used by the application for other -** purposes while a backup operation is underway or being initialized. -** ^If SQLite is compiled and configured to support threadsafe database -** connections, then the source database connection may be used concurrently -** from within other threads. -** -** However, the application must guarantee that the destination -** [database connection] is not passed to any other API (by any thread) after -** sqlite3_backup_init() is called and before the corresponding call to -** sqlite3_backup_finish(). SQLite does not currently check to see -** if the application incorrectly accesses the destination [database connection] -** and so no error code is reported, but the operations may malfunction -** nevertheless. Use of the destination database connection while a -** backup is in progress might also also cause a mutex deadlock. -** -** If running in [shared cache mode], the application must -** guarantee that the shared cache used by the destination database -** is not accessed while the backup is running. In practice this means -** that the application must guarantee that the disk file being -** backed up to is not accessed by any connection within the process, -** not just the specific connection that was passed to sqlite3_backup_init(). -** -** The [sqlite3_backup] object itself is partially threadsafe. Multiple -** threads may safely make multiple concurrent calls to sqlite3_backup_step(). -** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() -** APIs are not strictly speaking threadsafe. If they are invoked at the -** same time as another thread is invoking sqlite3_backup_step() it is -** possible that they return invalid values. -*/ -SQLITE_API sqlite3_backup *sqlite3_backup_init( - sqlite3 *pDest, /* Destination database handle */ - const char *zDestName, /* Destination database name */ - sqlite3 *pSource, /* Source database handle */ - const char *zSourceName /* Source database name */ -); -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); - -/* -** CAPI3REF: Unlock Notification -** -** ^When running in shared-cache mode, a database operation may fail with -** an [SQLITE_LOCKED] error if the required locks on the shared-cache or -** individual tables within the shared-cache cannot be obtained. See -** [SQLite Shared-Cache Mode] for a description of shared-cache locking. -** ^This API may be used to register a callback that SQLite will invoke -** when the connection currently holding the required lock relinquishes it. -** ^This API is only available if the library was compiled with the -** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. -** -** See Also: [Using the SQLite Unlock Notification Feature]. -** -** ^Shared-cache locks are released when a database connection concludes -** its current transaction, either by committing it or rolling it back. -** -** ^When a connection (known as the blocked connection) fails to obtain a -** shared-cache lock and SQLITE_LOCKED is returned to the caller, the -** identity of the database connection (the blocking connection) that -** has locked the required resource is stored internally. ^After an -** application receives an SQLITE_LOCKED error, it may call the -** sqlite3_unlock_notify() method with the blocked connection handle as -** the first argument to register for a callback that will be invoked -** when the blocking connections current transaction is concluded. ^The -** callback is invoked from within the [sqlite3_step] or [sqlite3_close] -** call that concludes the blocking connections transaction. -** -** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, -** there is a chance that the blocking connection will have already -** concluded its transaction by the time sqlite3_unlock_notify() is invoked. -** If this happens, then the specified callback is invoked immediately, -** from within the call to sqlite3_unlock_notify().)^ -** -** ^If the blocked connection is attempting to obtain a write-lock on a -** shared-cache table, and more than one other connection currently holds -** a read-lock on the same table, then SQLite arbitrarily selects one of -** the other connections to use as the blocking connection. -** -** ^(There may be at most one unlock-notify callback registered by a -** blocked connection. If sqlite3_unlock_notify() is called when the -** blocked connection already has a registered unlock-notify callback, -** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is -** called with a NULL pointer as its second argument, then any existing -** unlock-notify callback is canceled. ^The blocked connections -** unlock-notify callback may also be canceled by closing the blocked -** connection using [sqlite3_close()]. -** -** The unlock-notify callback is not reentrant. If an application invokes -** any sqlite3_xxx API functions from within an unlock-notify callback, a -** crash or deadlock may be the result. -** -** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always -** returns SQLITE_OK. -** -** Callback Invocation Details -** -** When an unlock-notify callback is registered, the application provides a -** single void* pointer that is passed to the callback when it is invoked. -** However, the signature of the callback function allows SQLite to pass -** it an array of void* context pointers. The first argument passed to -** an unlock-notify callback is a pointer to an array of void* pointers, -** and the second is the number of entries in the array. -** -** When a blocking connections transaction is concluded, there may be -** more than one blocked connection that has registered for an unlock-notify -** callback. ^If two or more such blocked connections have specified the -** same callback function, then instead of invoking the callback function -** multiple times, it is invoked once with the set of void* context pointers -** specified by the blocked connections bundled together into an array. -** This gives the application an opportunity to prioritize any actions -** related to the set of unblocked database connections. -** -** Deadlock Detection -** -** Assuming that after registering for an unlock-notify callback a -** database waits for the callback to be issued before taking any further -** action (a reasonable assumption), then using this API may cause the -** application to deadlock. For example, if connection X is waiting for -** connection Y's transaction to be concluded, and similarly connection -** Y is waiting on connection X's transaction, then neither connection -** will proceed and the system may remain deadlocked indefinitely. -** -** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock -** detection. ^If a given call to sqlite3_unlock_notify() would put the -** system in a deadlocked state, then SQLITE_LOCKED is returned and no -** unlock-notify callback is registered. The system is said to be in -** a deadlocked state if connection A has registered for an unlock-notify -** callback on the conclusion of connection B's transaction, and connection -** B has itself registered for an unlock-notify callback when connection -** A's transaction is concluded. ^Indirect deadlock is also detected, so -** the system is also considered to be deadlocked if connection B has -** registered for an unlock-notify callback on the conclusion of connection -** C's transaction, where connection C is waiting on connection A. ^Any -** number of levels of indirection are allowed. -** -** The "DROP TABLE" Exception -** -** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost -** always appropriate to call sqlite3_unlock_notify(). There is however, -** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement, -** SQLite checks if there are any currently executing SELECT statements -** that belong to the same connection. If there are, SQLITE_LOCKED is -** returned. In this case there is no "blocking connection", so invoking -** sqlite3_unlock_notify() results in the unlock-notify callback being -** invoked immediately. If the application then re-attempts the "DROP TABLE" -** or "DROP INDEX" query, an infinite loop might be the result. -** -** One way around this problem is to check the extended error code returned -** by an sqlite3_step() call. ^(If there is a blocking connection, then the -** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in -** the special "DROP TABLE/INDEX" case, the extended error code is just -** SQLITE_LOCKED.)^ -*/ -SQLITE_API int sqlite3_unlock_notify( - sqlite3 *pBlocked, /* Waiting connection */ - void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ - void *pNotifyArg /* Argument to pass to xNotify */ -); - - -/* -** CAPI3REF: String Comparison -** -** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications -** and extensions to compare the contents of two buffers containing UTF-8 -** strings in a case-independent fashion, using the same definition of "case -** independence" that SQLite uses internally when comparing identifiers. -*/ -SQLITE_API int sqlite3_stricmp(const char *, const char *); -SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); - -/* -** CAPI3REF: Error Logging Interface -** -** ^The [sqlite3_log()] interface writes a message into the error log -** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. -** ^If logging is enabled, the zFormat string and subsequent arguments are -** used with [sqlite3_snprintf()] to generate the final output string. -** -** The sqlite3_log() interface is intended for use by extensions such as -** virtual tables, collating functions, and SQL functions. While there is -** nothing to prevent an application from calling sqlite3_log(), doing so -** is considered bad form. -** -** The zFormat string must not be NULL. -** -** To avoid deadlocks and other threading problems, the sqlite3_log() routine -** will not use dynamically allocated memory. The log message is stored in -** a fixed-length buffer on the stack. If the log message is longer than -** a few hundred characters, it will be truncated to the length of the -** buffer. -*/ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); - -/* -** CAPI3REF: Write-Ahead Log Commit Hook -** -** ^The [sqlite3_wal_hook()] function is used to register a callback that -** will be invoked each time a database connection commits data to a -** [write-ahead log] (i.e. whenever a transaction is committed in -** [journal_mode | journal_mode=WAL mode]). -** -** ^The callback is invoked by SQLite after the commit has taken place and -** the associated write-lock on the database released, so the implementation -** may read, write or [checkpoint] the database as required. -** -** ^The first parameter passed to the callback function when it is invoked -** is a copy of the third parameter passed to sqlite3_wal_hook() when -** registering the callback. ^The second is a copy of the database handle. -** ^The third parameter is the name of the database that was written to - -** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter -** is the number of pages currently in the write-ahead log file, -** including those that were just committed. -** -** The callback function should normally return [SQLITE_OK]. ^If an error -** code is returned, that error will propagate back up through the -** SQLite code base to cause the statement that provoked the callback -** to report an error, though the commit will have still occurred. If the -** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value -** that does not correspond to any valid SQLite error code, the results -** are undefined. -** -** A single database handle may have at most a single write-ahead log callback -** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any -** previously registered write-ahead log callback. ^Note that the -** [sqlite3_wal_autocheckpoint()] interface and the -** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will -** those overwrite any prior [sqlite3_wal_hook()] settings. -*/ -SQLITE_API void *sqlite3_wal_hook( - sqlite3*, - int(*)(void *,sqlite3*,const char*,int), - void* -); - -/* -** CAPI3REF: Configure an auto-checkpoint -** -** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around -** [sqlite3_wal_hook()] that causes any database on [database connection] D -** to automatically [checkpoint] -** after committing a transaction if there are N or -** more frames in the [write-ahead log] file. ^Passing zero or -** a negative value as the nFrame parameter disables automatic -** checkpoints entirely. -** -** ^The callback registered by this function replaces any existing callback -** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback -** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism -** configured by this function. -** -** ^The [wal_autocheckpoint pragma] can be used to invoke this interface -** from SQL. -** -** ^Every new [database connection] defaults to having the auto-checkpoint -** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] -** pages. The use of this interface -** is only necessary if the default setting is found to be suboptimal -** for a particular application. -*/ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); - -/* -** CAPI3REF: Checkpoint a database -** -** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X -** on [database connection] D to be [checkpointed]. ^If X is NULL or an -** empty string, then a checkpoint is run on all databases of -** connection D. ^If the database connection D is not in -** [WAL | write-ahead log mode] then this interface is a harmless no-op. -** -** ^The [wal_checkpoint pragma] can be used to invoke this interface -** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the -** [wal_autocheckpoint pragma] can be used to cause this interface to be -** run whenever the WAL reaches a certain size threshold. -** -** See also: [sqlite3_wal_checkpoint_v2()] -*/ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); - -/* -** CAPI3REF: Checkpoint a database -** -** Run a checkpoint operation on WAL database zDb attached to database -** handle db. The specific operation is determined by the value of the -** eMode parameter: -** -**
    -**
    SQLITE_CHECKPOINT_PASSIVE
    -** Checkpoint as many frames as possible without waiting for any database -** readers or writers to finish. Sync the db file if all frames in the log -** are checkpointed. This mode is the same as calling -** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked. -** -**
    SQLITE_CHECKPOINT_FULL
    -** This mode blocks (calls the busy-handler callback) until there is no -** database writer and all readers are reading from the most recent database -** snapshot. It then checkpoints all frames in the log file and syncs the -** database file. This call blocks database writers while it is running, -** but not database readers. -** -**
    SQLITE_CHECKPOINT_RESTART
    -** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after -** checkpointing the log file it blocks (calls the busy-handler callback) -** until all readers are reading from the database file only. This ensures -** that the next client to write to the database file restarts the log file -** from the beginning. This call blocks database writers while it is running, -** but not database readers. -**
    -** -** If pnLog is not NULL, then *pnLog is set to the total number of frames in -** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to -** the total number of checkpointed frames (including any that were already -** checkpointed when this function is called). *pnLog and *pnCkpt may be -** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK. -** If no values are available because of an error, they are both set to -1 -** before returning to communicate this to the caller. -** -** All calls obtain an exclusive "checkpoint" lock on the database file. If -** any other process is running a checkpoint operation at the same time, the -** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a -** busy-handler configured, it will not be invoked in this case. -** -** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive -** "writer" lock on the database file. If the writer lock cannot be obtained -** immediately, and a busy-handler is configured, it is invoked and the writer -** lock retried until either the busy-handler returns 0 or the lock is -** successfully obtained. The busy-handler is also invoked while waiting for -** database readers as described above. If the busy-handler returns 0 before -** the writer lock is obtained or while waiting for database readers, the -** checkpoint operation proceeds from that point in the same way as -** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible -** without blocking any further. SQLITE_BUSY is returned in this case. -** -** If parameter zDb is NULL or points to a zero length string, then the -** specified operation is attempted on all WAL databases. In this case the -** values written to output parameters *pnLog and *pnCkpt are undefined. If -** an SQLITE_BUSY error is encountered when processing one or more of the -** attached WAL databases, the operation is still attempted on any remaining -** attached databases and SQLITE_BUSY is returned to the caller. If any other -** error occurs while processing an attached database, processing is abandoned -** and the error code returned to the caller immediately. If no error -** (SQLITE_BUSY or otherwise) is encountered while processing the attached -** databases, SQLITE_OK is returned. -** -** If database zDb is the name of an attached database that is not in WAL -** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If -** zDb is not NULL (or a zero length string) and is not the name of any -** attached database, SQLITE_ERROR is returned to the caller. -*/ -SQLITE_API int sqlite3_wal_checkpoint_v2( - sqlite3 *db, /* Database handle */ - const char *zDb, /* Name of attached database (or NULL) */ - int eMode, /* SQLITE_CHECKPOINT_* value */ - int *pnLog, /* OUT: Size of WAL log in frames */ - int *pnCkpt /* OUT: Total number of frames checkpointed */ -); - -/* -** CAPI3REF: Checkpoint operation parameters -** -** These constants can be used as the 3rd parameter to -** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] -** documentation for additional information about the meaning and use of -** each of these values. -*/ -#define SQLITE_CHECKPOINT_PASSIVE 0 -#define SQLITE_CHECKPOINT_FULL 1 -#define SQLITE_CHECKPOINT_RESTART 2 - -/* -** CAPI3REF: Virtual Table Interface Configuration -** -** This function may be called by either the [xConnect] or [xCreate] method -** of a [virtual table] implementation to configure -** various facets of the virtual table interface. -** -** If this interface is invoked outside the context of an xConnect or -** xCreate virtual table method then the behavior is undefined. -** -** At present, there is only one option that may be configured using -** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options -** may be added in the future. -*/ -SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); - -/* -** CAPI3REF: Virtual Table Configuration Options -** -** These macros define the various options to the -** [sqlite3_vtab_config()] interface that [virtual table] implementations -** can use to customize and optimize their behavior. -** -**
    -**
    SQLITE_VTAB_CONSTRAINT_SUPPORT -**
    Calls of the form -** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, -** where X is an integer. If X is zero, then the [virtual table] whose -** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not -** support constraints. In this configuration (which is the default) if -** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire -** statement is rolled back as if [ON CONFLICT | OR ABORT] had been -** specified as part of the users SQL statement, regardless of the actual -** ON CONFLICT mode specified. -** -** If X is non-zero, then the virtual table implementation guarantees -** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before -** any modifications to internal or persistent data structures have been made. -** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite -** is able to roll back a statement or database transaction, and abandon -** or continue processing the current SQL statement as appropriate. -** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns -** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode -** had been ABORT. -** -** Virtual table implementations that are required to handle OR REPLACE -** must do so within the [xUpdate] method. If a call to the -** [sqlite3_vtab_on_conflict()] function indicates that the current ON -** CONFLICT policy is REPLACE, the virtual table implementation should -** silently replace the appropriate rows within the xUpdate callback and -** return SQLITE_OK. Or, if this is not possible, it may return -** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT -** constraint handling. -**
    -*/ -#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 - -/* -** CAPI3REF: Determine The Virtual Table Conflict Policy -** -** This function may only be called from within a call to the [xUpdate] method -** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The -** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], -** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode -** of the SQL statement that triggered the call to the [xUpdate] method of the -** [virtual table]. -*/ -SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); - -/* -** CAPI3REF: Conflict resolution modes -** -** These constants are returned by [sqlite3_vtab_on_conflict()] to -** inform a [virtual table] implementation what the [ON CONFLICT] mode -** is for the SQL statement being evaluated. -** -** Note that the [SQLITE_IGNORE] constant is also used as a potential -** return value from the [sqlite3_set_authorizer()] callback and that -** [SQLITE_ABORT] is also a [result code]. -*/ -#define SQLITE_ROLLBACK 1 -/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ -#define SQLITE_FAIL 3 -/* #define SQLITE_ABORT 4 // Also an error code */ -#define SQLITE_REPLACE 5 - - - -/* -** Undo the hack that converts floating point types to integer for -** builds on processors without floating point support. -*/ -#ifdef SQLITE_OMIT_FLOATING_POINT -# undef double -#endif - -#ifdef __cplusplus -} /* End of the 'extern "C"' block */ -#endif -#endif - -/* -** 2010 August 30 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -*/ - -#ifndef _SQLITE3RTREE_H_ -#define _SQLITE3RTREE_H_ - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; - -/* -** Register a geometry callback named zGeom that can be used as part of an -** R-Tree geometry query as follows: -** -** SELECT ... FROM WHERE MATCH $zGeom(... params ...) -*/ -SQLITE_API int sqlite3_rtree_geometry_callback( - sqlite3 *db, - const char *zGeom, -#ifdef SQLITE_RTREE_INT_ONLY - int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes), -#else - int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes), -#endif - void *pContext -); - - -/* -** A pointer to a structure of the following type is passed as the first -** argument to callbacks registered using rtree_geometry_callback(). -*/ -struct sqlite3_rtree_geometry { - void *pContext; /* Copy of pContext passed to s_r_g_c() */ - int nParam; /* Size of array aParam[] */ - double *aParam; /* Parameters passed to SQL geom function */ - void *pUser; /* Callback implementation user data */ - void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ -}; - - -#ifdef __cplusplus -} /* end of the 'extern "C"' block */ -#endif - -#endif /* ifndef _SQLITE3RTREE_H_ */ - diff --git a/src/SQLite/urls.txt b/src/SQLite/urls.txt deleted file mode 100644 index 131d70bbf..000000000 --- a/src/SQLite/urls.txt +++ /dev/null @@ -1,9 +0,0 @@ - -SQLite: -http://www.sqlite.org -SQLite is in public domain - -LuaSQLite3: -http://lua.sqlite.org -http://lua.sqlite.org/index.cgi/doc/tip/doc/lsqlite3.wiki -- documentation -License for LuaSQLite is stored in $/install/LuaSQLite3-LICENSE.txt and distributed with the executables \ No newline at end of file diff --git a/src/md5/md5.cpp b/src/md5/md5.cpp deleted file mode 100644 index eae0fc3f2..000000000 --- a/src/md5/md5.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* MD5 - converted to C++ class by Frank Thilo (thilo@unix-ag.org) - for bzflag (http://www.bzflag.org) - - based on: - - md5.h and md5.c - reference implemantion of RFC 1321 - - Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - -*/ - -/* interface header */ -#include "md5.h" - -/* system implementation headers */ -#include - -#ifndef _WIN32 - #include -#endif - - - - - -// Constants for MD5Transform routine. -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - -/////////////////////////////////////////////// - -// F, G, H and I are basic MD5 functions. -inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) { - return x&y | ~x&z; -} - -inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) { - return x&z | y&~z; -} - -inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) { - return x^y^z; -} - -inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) { - return y ^ (x | ~z); -} - -// rotate_left rotates x left n bits. -inline MD5::uint4 MD5::rotate_left(uint4 x, int n) { - return (x << n) | (x >> (32-n)); -} - -// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. -// Rotation is separate from addition to prevent recomputation. -inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; -} - -inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + G(b,c,d) + x + ac, s) + b; -} - -inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + H(b,c,d) + x + ac, s) + b; -} - -inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + I(b,c,d) + x + ac, s) + b; -} - -////////////////////////////////////////////// - -// default ctor, just initailize -MD5::MD5() -{ - init(); -} - -////////////////////////////////////////////// - -// nifty shortcut ctor, compute MD5 for string and finalize it right away -MD5::MD5(const std::string &text) -{ - init(); - update(text.c_str(), text.length()); - finalize(); -} - -////////////////////////////// - -void MD5::init() -{ - finalized=false; - - count[0] = 0; - count[1] = 0; - - // load magic initialization constants. - state[0] = 0x67452301; - state[1] = 0xefcdab89; - state[2] = 0x98badcfe; - state[3] = 0x10325476; -} - -////////////////////////////// - -// decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. -void MD5::decode(uint4 output[], const uint1 input[], size_type len) -{ - for (unsigned int i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | - (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); -} - -////////////////////////////// - -// encodes input (uint4) into output (unsigned char). Assumes len is -// a multiple of 4. -void MD5::encode(uint1 output[], const uint4 input[], size_type len) -{ - for (size_type i = 0, j = 0; j < len; i++, j += 4) { - output[j] = input[i] & 0xff; - output[j+1] = (input[i] >> 8) & 0xff; - output[j+2] = (input[i] >> 16) & 0xff; - output[j+3] = (input[i] >> 24) & 0xff; - } -} - -////////////////////////////// - -// apply MD5 algo on a block -void MD5::transform(const uint1 block[blocksize]) -{ - uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - decode (x, block, blocksize); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - // Zeroize sensitive information. - memset(x, 0, sizeof x); -} - -////////////////////////////// - -// MD5 block update operation. Continues an MD5 message-digest -// operation, processing another message block -void MD5::update(const unsigned char input[], size_type length) -{ - // compute number of bytes mod 64 - size_type index = count[0] / 8 % blocksize; - - // Update number of bits - if ((count[0] += (length << 3)) < (length << 3)) - count[1]++; - count[1] += (length >> 29); - - // number of bytes we need to fill in buffer - size_type firstpart = 64 - index; - - size_type i; - - // transform as many times as possible. - if (length >= firstpart) - { - // fill buffer first, transform - memcpy(&buffer[index], input, firstpart); - transform(buffer); - - // transform chunks of blocksize (64 bytes) - for (i = firstpart; i + blocksize <= length; i += blocksize) - transform(&input[i]); - - index = 0; - } - else - i = 0; - - // buffer remaining input - memcpy(&buffer[index], &input[i], length-i); -} - -////////////////////////////// - -// for convenience provide a verson with signed char -void MD5::update(const char input[], size_type length) -{ - update((const unsigned char*)input, length); -} - -////////////////////////////// - -// MD5 finalization. Ends an MD5 message-digest operation, writing the -// the message digest and zeroizing the context. -MD5& MD5::finalize() -{ - static unsigned char padding[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - if (!finalized) { - // Save number of bits - unsigned char bits[8]; - encode(bits, count, 8); - - // pad out to 56 mod 64. - size_type index = count[0] / 8 % 64; - size_type padLen = (index < 56) ? (56 - index) : (120 - index); - update(padding, padLen); - - // Append length (before padding) - update(bits, 8); - - // Store state in digest - encode(digest, state, 16); - - // Zeroize sensitive information. - memset(buffer, 0, sizeof buffer); - memset(count, 0, sizeof count); - - finalized=true; - } - - return *this; -} - -////////////////////////////// - -// return hex representation of digest as string -std::string MD5::hexdigest() const -{ - if (!finalized) - return ""; - - char buf[33]; - for (int i=0; i<16; i++) - sprintf(buf+i*2, "%02x", digest[i]); - buf[32]=0; - - return std::string(buf); -} - -////////////////////////////// - -std::ostream& operator<<(std::ostream& out, MD5 md5) -{ - return out << md5.hexdigest(); -} - -////////////////////////////// - -std::string md5(const std::string & str) -{ - MD5 md5 = MD5(str); - - return md5.hexdigest(); -} diff --git a/src/md5/md5.h b/src/md5/md5.h deleted file mode 100644 index ad5ad5384..000000000 --- a/src/md5/md5.h +++ /dev/null @@ -1,93 +0,0 @@ -/* MD5 - converted to C++ class by Frank Thilo (thilo@unix-ag.org) - for bzflag (http://www.bzflag.org) - - based on: - - md5.h and md5.c - reference implementation of RFC 1321 - - Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - -*/ - -#ifndef BZF_MD5_H -#define BZF_MD5_H - -#include -#include - - -// a small class for calculating MD5 hashes of strings or byte arrays -// it is not meant to be fast or secure -// -// usage: 1) feed it blocks of uchars with update() -// 2) finalize() -// 3) get hexdigest() string -// or -// MD5(std::string).hexdigest() -// -// assumes that char is 8 bit and int is 32 bit -class MD5 -{ -public: - typedef unsigned int size_type; // must be 32bit - - MD5(); - MD5(const std::string& text); - void update(const unsigned char *buf, size_type length); - void update(const char *buf, size_type length); - MD5& finalize(); - std::string hexdigest() const; - friend std::ostream& operator<<(std::ostream&, MD5 md5); - -private: - void init(); - typedef unsigned char uint1; // 8bit - typedef unsigned int uint4; // 32bit - enum {blocksize = 64}; // VC6 won't eat a const static int here - - void transform(const uint1 block[blocksize]); - static void decode(uint4 output[], const uint1 input[], size_type len); - static void encode(uint1 output[], const uint4 input[], size_type len); - - bool finalized; - uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk - uint4 count[2]; // 64bit counter for number of bits (lo, hi) - uint4 state[4]; // digest so far - uint1 digest[16]; // the result - - // low level logic operations - static inline uint4 F(uint4 x, uint4 y, uint4 z); - static inline uint4 G(uint4 x, uint4 y, uint4 z); - static inline uint4 H(uint4 x, uint4 y, uint4 z); - static inline uint4 I(uint4 x, uint4 y, uint4 z); - static inline uint4 rotate_left(uint4 x, int n); - static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); -}; - -std::string md5(const std::string & str); - -#endif \ No newline at end of file -- cgit v1.2.3 From e3db69c4ae5d373ea1f1b31c0fc1889f328ea2ed Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 24 Nov 2013 18:44:22 +0000 Subject: Attempt to fix compilation --- GNUmakefile | 17 +++++++------- VC2008/MCServer.cbp | 39 ++++++++------------------------ VC2008/MCServer.vcproj | 24 ++++++++++---------- VC2013/MCServer.vcxproj | 22 +++++++++--------- VC2013/MCServer.vcxproj.filters | 22 +++++++++--------- lib/jsoncpp/src/lib_json/json_reader.cpp | 4 ++-- lib/jsoncpp/src/lib_json/json_value.cpp | 4 ++-- lib/jsoncpp/src/lib_json/json_writer.cpp | 2 +- lib/luaexpat/lxplib.c | 6 ++--- lib/sqlite/lsqlite3.c | 4 ++-- lib/tolua++/src/bin/tolua.c | 8 +++---- lib/tolua++/src/lib/tolua_event.c | 2 +- lib/tolua++/src/lib/tolua_is.c | 4 ++-- lib/tolua++/src/lib/tolua_map.c | 4 ++-- lib/tolua++/src/lib/tolua_push.c | 4 ++-- lib/tolua++/src/lib/tolua_to.c | 2 +- src/Authenticator.cpp | 2 +- src/Bindings.cpp | 2 +- src/BlockEntities/ChestEntity.cpp | 2 +- src/BlockEntities/FurnaceEntity.cpp | 2 +- src/BlockEntities/JukeboxEntity.cpp | 2 +- src/BlockEntities/NoteEntity.cpp | 2 +- src/BlockEntities/SignEntity.cpp | 2 +- src/BlockID.cpp | 2 +- src/Chunk.cpp | 4 ++-- src/ChunkMap.cpp | 4 ++-- src/Entities/Player.cpp | 4 ++-- src/Generating/BioGen.cpp | 2 +- src/Generating/ChunkGenerator.cpp | 2 +- src/Generating/CompoGen.cpp | 2 +- src/Generating/ComposableGenerator.cpp | 2 +- src/Generating/DistortedHeightmap.cpp | 2 +- src/Generating/EndGen.cpp | 2 +- src/Generating/HeiGen.cpp | 2 +- src/Generating/Noise3DGenerator.cpp | 2 +- src/GroupManager.cpp | 2 +- src/HTTPServer/HTTPServer.h | 2 +- src/Inventory.cpp | 2 +- src/Item.cpp | 2 +- src/LuaState.cpp | 2 +- src/LuaState.h | 2 +- src/LuaWindow.cpp | 2 +- src/ManualBindings.cpp | 2 +- src/MonsterConfig.cpp | 3 +-- src/OSSupport/GZipFile.h | 2 +- src/PluginLua.cpp | 2 +- src/PluginManager.cpp | 2 +- src/Protocol/ChunkDataSerializer.cpp | 2 +- src/Protocol/Protocol132.cpp | 4 ++-- src/Protocol/Protocol132.h | 4 ++-- src/Protocol/Protocol14x.cpp | 2 +- src/Protocol/Protocol17x.h | 4 ++-- src/RCONServer.cpp | 2 +- src/Root.cpp | 2 +- src/Server.cpp | 4 ++-- src/Server.h | 4 ++-- src/Simulator/Simulator.h | 2 +- src/StringCompression.h | 2 +- src/WebAdmin.h | 2 +- src/World.cpp | 2 +- src/WorldStorage/WSSAnvil.cpp | 2 +- src/WorldStorage/WSSCompact.cpp | 4 ++-- src/WorldStorage/WorldStorage.h | 2 +- src/tolua++.h | 4 ++-- 64 files changed, 132 insertions(+), 153 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index ce5920e5b..2ddfefee9 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -47,9 +47,9 @@ ifeq ($(release),1) # release build - fastest run-time, no gdb support ################ -CC_OPTIONS = -g -O3 -DNDEBUG -CXX_OPTIONS = -g -O3 -DNDEBUG -LNK_OPTIONS = -pthread -O3 +CC_OPTIONS = -g -Ofast -DNDEBUG +CXX_OPTIONS = -g -Ofast -DNDEBUG +LNK_OPTIONS = -pthread -Ofast BUILDDIR = build/release/ else @@ -58,18 +58,18 @@ ifeq ($(profile),1) # profile build - a release build with symbols and profiling engine built in ################ -CC_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG -CXX_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG -LNK_OPTIONS = -pthread -ggdb -O3 -pg +CC_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG +CXX_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG +LNK_OPTIONS = -pthread -ggdb -Ofast -pg BUILDDIR = build/profile/ else ################ # debug build - fully traceable by gdb in C++ code, slowest -# Since C code is used only for supporting libraries (zlib, lua), it is still O3-optimized +# Since C code is used only for supporting libraries (zlib, lua), it is still Ofast-optimized ################ -CC_OPTIONS = -s -ggdb -g -D_DEBUG -O3 +CC_OPTIONS = -s -ggdb -g -D_DEBUG -Ofast CXX_OPTIONS = -s -ggdb -g -D_DEBUG -Og LNK_OPTIONS = -pthread -g -ggdb -Og BUILDDIR = build/debug/ @@ -139,7 +139,6 @@ endif INCLUDE = -I.\ -Isrc\ - -Isrc/md5\ -Isrc/items\ -Isrce/blocks\ -Ilib diff --git a/VC2008/MCServer.cbp b/VC2008/MCServer.cbp index 1758f6410..0d63e9291 100644 --- a/VC2008/MCServer.cbp +++ b/VC2008/MCServer.cbp @@ -23,9 +23,6 @@ - - - @@ -34,9 +31,6 @@ - - - @@ -179,8 +173,8 @@ - - + + @@ -368,10 +362,10 @@ - + - + @@ -479,10 +473,6 @@ - - - - @@ -502,14 +492,14 @@ - + - + - - + + @@ -534,8 +524,6 @@ - - @@ -578,15 +566,8 @@ - - - - - - - - - + + diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index 1b710e1e4..524ffdfd4 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -1685,7 +1685,7 @@ @@ -1696,7 +1696,7 @@ @@ -1707,7 +1707,7 @@ @@ -1718,7 +1718,7 @@ @@ -1848,7 +1848,7 @@ > @@ -2544,7 +2544,7 @@ Name="SQLite" > @@ -2628,7 +2628,7 @@ Name="LuaExpat" > diff --git a/VC2013/MCServer.vcxproj b/VC2013/MCServer.vcxproj index ad0a0c49a..ba2ca72d7 100644 --- a/VC2013/MCServer.vcxproj +++ b/VC2013/MCServer.vcxproj @@ -1,4 +1,4 @@ - + @@ -332,8 +332,8 @@ - - + + @@ -453,8 +453,8 @@ - - + + @@ -754,8 +754,8 @@ - - + + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) @@ -807,7 +807,7 @@ - + @@ -817,7 +817,7 @@ - + @@ -827,7 +827,7 @@ - + @@ -857,7 +857,7 @@ - + diff --git a/VC2013/MCServer.vcxproj.filters b/VC2013/MCServer.vcxproj.filters index 348c725f6..aecbcd557 100644 --- a/VC2013/MCServer.vcxproj.filters +++ b/VC2013/MCServer.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -513,10 +513,10 @@ Source Files\Bindings - + Source Files\External - + Source Files\External @@ -876,10 +876,10 @@ Source Files\Protocol - + Source Files\SQLite - + Source Files\LuaExpat @@ -1474,10 +1474,10 @@ Source Files\Bindings - + Source Files\External - + Source Files\External @@ -1594,13 +1594,13 @@ Source Files\Protocol - + Source Files\SQLite - + Source Files\SQLite - + Source Files\LuaExpat @@ -1659,7 +1659,7 @@ Source Files\Bindings - + Source Files\SQLite diff --git a/lib/jsoncpp/src/lib_json/json_reader.cpp b/lib/jsoncpp/src/lib_json/json_reader.cpp index 7c94e612e..e9d6b88d2 100644 --- a/lib/jsoncpp/src/lib_json/json_reader.cpp +++ b/lib/jsoncpp/src/lib_json/json_reader.cpp @@ -1,5 +1,5 @@ -#include -#include +#include "../../include/json/reader.h" +#include "../../include/json/value.h" #include #include #include diff --git a/lib/jsoncpp/src/lib_json/json_value.cpp b/lib/jsoncpp/src/lib_json/json_value.cpp index 89271cd00..ea033c400 100644 --- a/lib/jsoncpp/src/lib_json/json_value.cpp +++ b/lib/jsoncpp/src/lib_json/json_value.cpp @@ -1,6 +1,6 @@ #include -#include -#include +#include "../../include/json/json.h" +#include "../../include/json/writer.h" #include #include #include diff --git a/lib/jsoncpp/src/lib_json/json_writer.cpp b/lib/jsoncpp/src/lib_json/json_writer.cpp index cdf4188f2..eff3fabeb 100644 --- a/lib/jsoncpp/src/lib_json/json_writer.cpp +++ b/lib/jsoncpp/src/lib_json/json_writer.cpp @@ -1,4 +1,4 @@ -#include +#include "../../include/json/writer.h" #include #include #include diff --git a/lib/luaexpat/lxplib.c b/lib/luaexpat/lxplib.c index e26343ce9..053c59a34 100644 --- a/lib/luaexpat/lxplib.c +++ b/lib/luaexpat/lxplib.c @@ -9,10 +9,10 @@ #include #include -#include "expat.h" +#include "../lib/expat/expat.h" -#include "lua.h" -#include "lauxlib.h" +#include "../lib/lua/src/lua.h" +#include "../lib/lua/src/lauxlib.h" #include "lxplib.h" diff --git a/lib/sqlite/lsqlite3.c b/lib/sqlite/lsqlite3.c index 4c81b5878..8098311ac 100644 --- a/lib/sqlite/lsqlite3.c +++ b/lib/sqlite/lsqlite3.c @@ -44,8 +44,8 @@ extern "C" { #include #define LUA_LIB -#include "lua.h" -#include "lauxlib.h" +#include "../lib/lua/src/lua.h" +#include "../lib/lua/src/lauxlib.h" #if LUA_VERSION_NUM > 501 // diff --git a/lib/tolua++/src/bin/tolua.c b/lib/tolua++/src/bin/tolua.c index cc9f9adcd..fd8e1ed1e 100644 --- a/lib/tolua++/src/bin/tolua.c +++ b/lib/tolua++/src/bin/tolua.c @@ -12,11 +12,11 @@ ** enhancements, or modifications. */ -#include "tolua++.h" +#include "../../include/tolua++.h" -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" +#include "../../../lua/src/lua.h" +#include "../../../lua/src/lualib.h" +#include "../../../lua/src/lauxlib.h" #include #include diff --git a/lib/tolua++/src/lib/tolua_event.c b/lib/tolua++/src/lib/tolua_event.c index 8258867b4..3075a60b3 100644 --- a/lib/tolua++/src/lib/tolua_event.c +++ b/lib/tolua++/src/lib/tolua_event.c @@ -14,7 +14,7 @@ #include -#include "tolua++.h" +#include "../../include/tolua++.h" /* Store at ubox * It stores, creating the corresponding table if needed, diff --git a/lib/tolua++/src/lib/tolua_is.c b/lib/tolua++/src/lib/tolua_is.c index c486b271e..b470477ff 100644 --- a/lib/tolua++/src/lib/tolua_is.c +++ b/lib/tolua++/src/lib/tolua_is.c @@ -12,8 +12,8 @@ ** enhancements, or modifications. */ -#include "tolua++.h" -#include "lauxlib.h" +#include "../../include/tolua++.h" +#include "../../../lua/src/lauxlib.h" #include #include diff --git a/lib/tolua++/src/lib/tolua_map.c b/lib/tolua++/src/lib/tolua_map.c index d00e7069b..7c4a22336 100644 --- a/lib/tolua++/src/lib/tolua_map.c +++ b/lib/tolua++/src/lib/tolua_map.c @@ -12,9 +12,9 @@ ** enhancements, or modifications. */ -#include "tolua++.h" +#include "../../include/tolua++.h" +#include "../../../lua/src/lauxlib.h" #include "tolua_event.h" -#include "lauxlib.h" #include #include diff --git a/lib/tolua++/src/lib/tolua_push.c b/lib/tolua++/src/lib/tolua_push.c index 639414755..947f0e7a5 100644 --- a/lib/tolua++/src/lib/tolua_push.c +++ b/lib/tolua++/src/lib/tolua_push.c @@ -12,8 +12,8 @@ ** enhancements, or modifications. */ -#include "tolua++.h" -#include "lauxlib.h" +#include "../../include/tolua++.h" +#include "../../../lua/src/lauxlib.h" #include diff --git a/lib/tolua++/src/lib/tolua_to.c b/lib/tolua++/src/lib/tolua_to.c index 542ca67d1..04132e901 100644 --- a/lib/tolua++/src/lib/tolua_to.c +++ b/lib/tolua++/src/lib/tolua_to.c @@ -12,7 +12,7 @@ ** enhancements, or modifications. */ -#include "tolua++.h" +#include "../../include/tolua++.h" #include #include diff --git a/src/Authenticator.cpp b/src/Authenticator.cpp index 9a6dcf51b..3371c2a1a 100644 --- a/src/Authenticator.cpp +++ b/src/Authenticator.cpp @@ -6,7 +6,7 @@ #include "Root.h" #include "Server.h" -#include "../iniFile/iniFile.h" +#include "../lib/iniFile/iniFile.h" #include diff --git a/src/Bindings.cpp b/src/Bindings.cpp index ad3ad8423..cbb50a321 100644 --- a/src/Bindings.cpp +++ b/src/Bindings.cpp @@ -16,7 +16,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "tolua_base.h" #include "ChunkDef.h" -#include "../iniFile/iniFile.h" +#include "lib/iniFile/iniFile.h" #include "OSSupport/File.h" #include "BlockID.h" #include "StringUtils.h" diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp index ca2626bc9..3149166fd 100644 --- a/src/BlockEntities/ChestEntity.cpp +++ b/src/BlockEntities/ChestEntity.cpp @@ -5,7 +5,7 @@ #include "../Item.h" #include "../Entities/Player.h" #include "../UI/Window.h" -#include +#include "../lib/jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index ec5ebe8b9..a05dd5956 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -6,7 +6,7 @@ #include "../Entities/Player.h" #include "../Root.h" #include "../Chunk.h" -#include +#include "../lib/jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/JukeboxEntity.cpp b/src/BlockEntities/JukeboxEntity.cpp index aca376dd3..1b5c03826 100644 --- a/src/BlockEntities/JukeboxEntity.cpp +++ b/src/BlockEntities/JukeboxEntity.cpp @@ -3,7 +3,7 @@ #include "JukeboxEntity.h" #include "../World.h" -#include +#include "../lib/jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp index 1b0620299..6bb570133 100644 --- a/src/BlockEntities/NoteEntity.cpp +++ b/src/BlockEntities/NoteEntity.cpp @@ -3,7 +3,7 @@ #include "NoteEntity.h" #include "../World.h" -#include +#include "../lib/jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp index 81f6f6d77..9cbac5679 100644 --- a/src/BlockEntities/SignEntity.cpp +++ b/src/BlockEntities/SignEntity.cpp @@ -4,7 +4,7 @@ // Implements the cSignEntity class representing a single sign in the world #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include +#include "../lib/jsoncpp/include/json/json.h" #include "SignEntity.h" #include "../Entities/Player.h" diff --git a/src/BlockID.cpp b/src/BlockID.cpp index a4a1ab2d8..003f49848 100644 --- a/src/BlockID.cpp +++ b/src/BlockID.cpp @@ -4,7 +4,7 @@ #include "Globals.h" #include "BlockID.h" -#include "../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" #include "Item.h" #include "Mobs/Monster.h" diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 7e71e9ea7..ec60a19bf 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -10,7 +10,7 @@ #include "World.h" #include "ClientHandle.h" #include "Server.h" -#include "zlib.h" +#include "../lib/zlib/zlib.h" #include "Defines.h" #include "BlockEntities/ChestEntity.h" #include "BlockEntities/DispenserEntity.h" @@ -34,7 +34,7 @@ #include "MobSpawner.h" -#include +#include "lib/jsoncpp/include/json/json.h" diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 9d55917e5..5937ee2e4 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -20,8 +20,8 @@ #include // abs #endif -#include "zlib.h" -#include +#include "lib/zlib/zlib.h" +#include "lib/jsoncpp/include/json/json.h" diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index f37a23f22..f4ddd979c 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -24,8 +24,8 @@ #include "../Vector3d.h" #include "../Vector3f.h" -#include "../../iniFile/iniFile.h" -#include +#include "lib/iniFile/iniFile.h" +#include "lib/jsoncpp/include/json/json.h" #define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 926120afc..c05778bb1 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include "BioGen.h" -#include "../../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" #include "../LinearUpscale.h" diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index 33c956eba..a7107e325 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -3,7 +3,7 @@ #include "ChunkGenerator.h" #include "../World.h" -#include "../../iniFile/iniFile.h" +#include "../lib/iniFile/iniFile.h" #include "../Root.h" #include "../PluginManager.h" #include "ChunkDesc.h" diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp index cc2a203af..9b93fcf3b 100644 --- a/src/Generating/CompoGen.cpp +++ b/src/Generating/CompoGen.cpp @@ -12,7 +12,7 @@ #include "../BlockID.h" #include "../Item.h" #include "../LinearUpscale.h" -#include "../../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 2637b64e7..40914605a 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -7,7 +7,7 @@ #include "ComposableGenerator.h" #include "../World.h" -#include "../../iniFile/iniFile.h" +#include "../lib/iniFile/iniFile.h" #include "../Root.h" // Individual composed algorithms: diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index 7e46c251e..ae5cd02e8 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -7,7 +7,7 @@ #include "DistortedHeightmap.h" #include "../OSSupport/File.h" -#include "../../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" #include "../LinearUpscale.h" diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp index 90a8071fa..750a30f91 100644 --- a/src/Generating/EndGen.cpp +++ b/src/Generating/EndGen.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include "EndGen.h" -#include "../../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" #include "../LinearUpscale.h" diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index 5dee181b7..9e656d269 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -6,7 +6,7 @@ #include "Globals.h" #include "HeiGen.h" #include "../LinearUpscale.h" -#include "../../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index 03626a800..36ef55e89 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -6,7 +6,7 @@ #include "Globals.h" #include "Noise3DGenerator.h" #include "../OSSupport/File.h" -#include "../../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" #include "../LinearInterpolation.h" #include "../LinearUpscale.h" diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp index d7332fd0a..55065b38e 100644 --- a/src/GroupManager.cpp +++ b/src/GroupManager.cpp @@ -2,7 +2,7 @@ #include "GroupManager.h" #include "Group.h" -#include "../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" #include "ChatColor.h" #include "Root.h" diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h index fea2a9029..65cdcb3a5 100644 --- a/src/HTTPServer/HTTPServer.h +++ b/src/HTTPServer/HTTPServer.h @@ -11,7 +11,7 @@ #include "../OSSupport/ListenThread.h" #include "../OSSupport/SocketThreads.h" -#include "../../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" diff --git a/src/Inventory.cpp b/src/Inventory.cpp index 90b998358..3939598d7 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -9,7 +9,7 @@ #include "Root.h" #include "World.h" -#include +#include "../lib/jsoncpp/include/json/json.h" #include "Items/ItemHandler.h" diff --git a/src/Item.cpp b/src/Item.cpp index 25664e4df..219edb128 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -2,7 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Item.h" -#include +#include "../lib/jsoncpp/include/json/json.h" #include "Items/ItemHandler.h" diff --git a/src/LuaState.cpp b/src/LuaState.cpp index 644f4972c..3a0008d4f 100644 --- a/src/LuaState.cpp +++ b/src/LuaState.cpp @@ -8,7 +8,7 @@ extern "C" { - #include "lualib.h" + #include"lib/lua/src/lualib.h" } #include "tolua++.h" diff --git a/src/LuaState.h b/src/LuaState.h index aa71ee226..9a486bfd1 100644 --- a/src/LuaState.h +++ b/src/LuaState.h @@ -26,7 +26,7 @@ automatic resource management. extern "C" { - #include "lauxlib.h" + #include "../lib/lua/src/lauxlib.h" } diff --git a/src/LuaWindow.cpp b/src/LuaWindow.cpp index 9011d668c..f49ba2109 100644 --- a/src/LuaWindow.cpp +++ b/src/LuaWindow.cpp @@ -8,7 +8,7 @@ #include "UI/SlotArea.h" #include "PluginLua.h" #include "Entities/Player.h" -#include "lauxlib.h" // Needed for LUA_REFNIL +#include "lib/lua/src/lauxlib.h" // Needed for LUA_REFNIL diff --git a/src/ManualBindings.cpp b/src/ManualBindings.cpp index 02b3347f6..7ea382b76 100644 --- a/src/ManualBindings.cpp +++ b/src/ManualBindings.cpp @@ -17,7 +17,7 @@ #include "BlockEntities/DropperEntity.h" #include "BlockEntities/FurnaceEntity.h" #include "BlockEntities/HopperEntity.h" -#include "md5/md5.h" +#include "lib/md5/md5.h" #include "LuaWindow.h" #include "LineBlockTracer.h" diff --git a/src/MonsterConfig.cpp b/src/MonsterConfig.cpp index a5a1ebd49..b72324124 100644 --- a/src/MonsterConfig.cpp +++ b/src/MonsterConfig.cpp @@ -3,8 +3,7 @@ #include "MonsterConfig.h" #include "Mobs/Monster.h" -#include "../iniFile/iniFile.h" -//#include +#include "../lib/inifile/iniFile.h" diff --git a/src/OSSupport/GZipFile.h b/src/OSSupport/GZipFile.h index e5aa68afa..df86717f6 100644 --- a/src/OSSupport/GZipFile.h +++ b/src/OSSupport/GZipFile.h @@ -9,7 +9,7 @@ #pragma once -#include "zlib.h" +#include "../lib/zlib/zlib.h" diff --git a/src/PluginLua.cpp b/src/PluginLua.cpp index 23d079b05..0afd3844a 100644 --- a/src/PluginLua.cpp +++ b/src/PluginLua.cpp @@ -11,7 +11,7 @@ extern "C" { - #include "lualib.h" + #include"lib/lua/src/lualib.h" } #include "tolua++.h" diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index e08ebe503..f9d8d7d02 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -10,7 +10,7 @@ #include "Server.h" #include "CommandOutput.h" -#include "../iniFile/iniFile.h" +#include "lib/iniFile/iniFile.h" #include "tolua++.h" #include "Entities/Player.h" diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index 2a9230fee..c83a87ca9 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -7,7 +7,7 @@ #include "Globals.h" #include "ChunkDataSerializer.h" -#include "zlib.h" +#include "../lib/zlib/zlib.h" diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index 22eac4312..bfa8eb412 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -4,14 +4,14 @@ // Implements the cProtocol132 class representing the release 1.3.2 protocol (#39) #include "Globals.h" +#include "ChunkDataSerializer.h" #include "Protocol132.h" #include "../Root.h" #include "../Server.h" #include "../World.h" #include "../ClientHandle.h" -#include "../../CryptoPP/randpool.h" +#include "../lib/cryptopp/randpool.h" #include "../Item.h" -#include "ChunkDataSerializer.h" #include "../Entities/Player.h" #include "../Mobs/Monster.h" #include "../UI/Window.h" diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h index dc4d8aeef..58e98c208 100644 --- a/src/Protocol/Protocol132.h +++ b/src/Protocol/Protocol132.h @@ -10,8 +10,8 @@ #pragma once #include "Protocol125.h" -#include "../../CryptoPP/modes.h" -#include "../../CryptoPP/aes.h" +#include "../lib/cryptopp/modes.h" +#include "../lib/cryptopp/aes.h" diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp index d2582458b..9fb2a8133 100644 --- a/src/Protocol/Protocol14x.cpp +++ b/src/Protocol/Protocol14x.cpp @@ -16,7 +16,7 @@ Implements the 1.4.x protocol classes representing these protocols: #include "../Root.h" #include "../Server.h" #include "../ClientHandle.h" -#include "../../CryptoPP/randpool.h" +#include "../lib/cryptopp/randpool.h" #include "../Item.h" #include "ChunkDataSerializer.h" #include "../Entities/Player.h" diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index e3f2ad922..b55bbf80b 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -16,8 +16,8 @@ Declares the 1.7.x protocol classes: #include "Protocol.h" #include "../ByteBuffer.h" -#include "../../CryptoPP/modes.h" -#include "../../CryptoPP/aes.h" +#include "../lib/cryptopp/modes.h" +#include "../lib/cryptopp/aes.h" diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index 3f86a7ca2..f90e9f191 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -4,7 +4,7 @@ // Implements the cRCONServer class representing the RCON server #include "Globals.h" -#include "../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" #include "RCONServer.h" #include "Server.h" #include "Root.h" diff --git a/src/Root.cpp b/src/Root.cpp index 5bb04abfb..282227525 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -19,7 +19,7 @@ #include "DeadlockDetect.h" #include "OSSupport/Timer.h" -#include "../iniFile/iniFile.h" +#include "lib/iniFile/iniFile.h" #ifdef _WIN32 #include diff --git a/src/Server.cpp b/src/Server.cpp index fe8076631..5f0e007d7 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -23,7 +23,7 @@ #include "MersenneTwister.h" -#include "../iniFile/iniFile.h" +#include "lib/iniFile/iniFile.h" #include "Vector3f.h" #include @@ -31,7 +31,7 @@ #include extern "C" { - #include "zlib.h" + #include "lib/zlib/zlib.h" } diff --git a/src/Server.h b/src/Server.h index 1b4848318..93a978654 100644 --- a/src/Server.h +++ b/src/Server.h @@ -11,8 +11,8 @@ #include "OSSupport/SocketThreads.h" #include "OSSupport/ListenThread.h" -#include "CryptoPP/rsa.h" -#include "CryptoPP/randpool.h" +#include "../lib/cryptopp/rsa.h" +#include "../lib/cryptopp/randpool.h" #include "RCONServer.h" diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h index e1d88f1c5..9d3ad4131 100644 --- a/src/Simulator/Simulator.h +++ b/src/Simulator/Simulator.h @@ -2,7 +2,7 @@ #pragma once #include "../Vector3i.h" -#include "../../iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" diff --git a/src/StringCompression.h b/src/StringCompression.h index 8afdf59e0..1257d7d6b 100644 --- a/src/StringCompression.h +++ b/src/StringCompression.h @@ -3,7 +3,7 @@ // Interfaces to the wrapping functions for compression and decompression using AString as their data -#include "zlib.h" // Needed for the Z_XXX return values +#include "../lib/zlib/zlib.h" // Needed for the Z_XXX return values diff --git a/src/WebAdmin.h b/src/WebAdmin.h index dc6ea850e..0bceb9f82 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -7,7 +7,7 @@ #include "OSSupport/Socket.h" #include "LuaState.h" -#include "../iniFile/iniFile.h" +#include "../lib/iniFile/iniFile.h" #include "HTTPServer/HTTPServer.h" #include "HTTPServer/HTTPFormParser.h" diff --git a/src/World.cpp b/src/World.cpp index 531952e37..96111ab57 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -8,7 +8,7 @@ #include "Server.h" #include "Item.h" #include "Root.h" -#include "../iniFile/iniFile.h" +#include "../lib/iniFile/iniFile.h" #include "ChunkMap.h" #include "OSSupport/Timer.h" diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index b2e104a78..ee7511486 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -7,7 +7,7 @@ #include "WSSAnvil.h" #include "NBTChunkSerializer.h" #include "../World.h" -#include "zlib.h" +#include "../lib/zlib/zlib.h" #include "../BlockID.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DispenserEntity.h" diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp index 694f3ed1d..812c60411 100644 --- a/src/WorldStorage/WSSCompact.cpp +++ b/src/WorldStorage/WSSCompact.cpp @@ -6,8 +6,8 @@ #include "Globals.h" #include "WSSCompact.h" #include "../World.h" -#include "zlib.h" -#include +#include "../lib/zlib/zlib.h" +#include "../lib/jsoncpp/include/json/json.h" #include "../StringCompression.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DispenserEntity.h" diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index bf8dbd3d5..384ade081 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -16,7 +16,7 @@ #include "../ChunkDef.h" #include "../OSSupport/IsThread.h" -#include +#include "../lib/jsoncpp/include/json/json.h" diff --git a/src/tolua++.h b/src/tolua++.h index ed5344926..dff1e91be 100644 --- a/src/tolua++.h +++ b/src/tolua++.h @@ -43,8 +43,8 @@ extern "C" { typedef int lua_Object; -#include "lua.h" -#include "lauxlib.h" +#include "../lib/lua/src/lua.h" +#include "../lib/lua/src/lauxlib.h" struct tolua_Error { -- cgit v1.2.3 From 32449f1fca8379d0c7f38e95834b2a01e62de06c Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 25 Nov 2013 18:29:36 +0000 Subject: Further attempts to fix compile --- VC2013/CryptoPP.vcxproj | 4 ++++ VC2013/CryptoPP.vcxproj.filters | 12 ++++++++++++ VC2013/Lua.vcxproj | 4 ++-- VC2013/MCServer.vcxproj | 4 ++-- lib/luaexpat/lxplib.c | 6 +++--- lib/sqlite/lsqlite3.c | 4 ++-- lib/tolua++/include/tolua++.h | 4 ++-- src/BlockEntities/ChestEntity.cpp | 2 +- src/BlockEntities/FurnaceEntity.cpp | 2 +- src/BlockEntities/JukeboxEntity.cpp | 2 +- src/BlockEntities/NoteEntity.cpp | 2 +- src/BlockEntities/SignEntity.cpp | 2 +- src/Generating/BioGen.cpp | 2 +- src/Generating/ChunkGenerator.cpp | 2 +- src/Generating/CompoGen.cpp | 2 +- src/Generating/ComposableGenerator.cpp | 2 +- src/Generating/DistortedHeightmap.cpp | 2 +- src/Generating/EndGen.cpp | 2 +- src/Generating/HeiGen.cpp | 2 +- src/Generating/Noise3DGenerator.cpp | 2 +- src/HTTPServer/HTTPServer.h | 2 +- src/OSSupport/GZipFile.h | 2 +- src/Protocol/ChunkDataSerializer.cpp | 2 +- src/Protocol/Protocol132.cpp | 2 +- src/Protocol/Protocol132.h | 4 ++-- src/Protocol/Protocol14x.cpp | 2 +- src/Protocol/Protocol17x.h | 4 ++-- src/Simulator/Simulator.h | 2 +- src/WorldStorage/WSSAnvil.cpp | 2 +- src/WorldStorage/WSSCompact.cpp | 4 ++-- src/WorldStorage/WorldStorage.h | 2 +- 31 files changed, 54 insertions(+), 38 deletions(-) diff --git a/VC2013/CryptoPP.vcxproj b/VC2013/CryptoPP.vcxproj index 7355ab64b..4a7898e4a 100644 --- a/VC2013/CryptoPP.vcxproj +++ b/VC2013/CryptoPP.vcxproj @@ -814,6 +814,7 @@ + @@ -824,6 +825,7 @@ + @@ -866,6 +868,7 @@ + @@ -888,6 +891,7 @@ + diff --git a/VC2013/CryptoPP.vcxproj.filters b/VC2013/CryptoPP.vcxproj.filters index f066c5ef4..e8bea416f 100644 --- a/VC2013/CryptoPP.vcxproj.filters +++ b/VC2013/CryptoPP.vcxproj.filters @@ -580,6 +580,18 @@ Header Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/VC2013/Lua.vcxproj b/VC2013/Lua.vcxproj index d4395b2d2..24f58f28c 100644 --- a/VC2013/Lua.vcxproj +++ b/VC2013/Lua.vcxproj @@ -1,4 +1,4 @@ - + @@ -138,7 +138,7 @@ - + diff --git a/VC2013/MCServer.vcxproj b/VC2013/MCServer.vcxproj index ba2ca72d7..c9fe5c67c 100644 --- a/VC2013/MCServer.vcxproj +++ b/VC2013/MCServer.vcxproj @@ -1,4 +1,4 @@ - + @@ -85,7 +85,7 @@ /MP %(AdditionalOptions) Disabled - ../zlib-1.2.7;../jsoncpp-src-0.5.0/include;../lua-5.1.4/src;../tolua++-1.0.93/include;..;../expat;%(AdditionalIncludeDirectories) + ../lib/zlib;../lib/jsoncpp/include;../lib/lua/src;../lib/tolua++/include;..;../lib/expat;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CONSOLE;XML_STATIC;%(PreprocessorDefinitions) false EnableFastChecks diff --git a/lib/luaexpat/lxplib.c b/lib/luaexpat/lxplib.c index 053c59a34..a81a434e9 100644 --- a/lib/luaexpat/lxplib.c +++ b/lib/luaexpat/lxplib.c @@ -9,10 +9,10 @@ #include #include -#include "../lib/expat/expat.h" +#include "lib/expat/expat.h" -#include "../lib/lua/src/lua.h" -#include "../lib/lua/src/lauxlib.h" +#include "lib/lua/src/lua.h" +#include "lib/lua/src/lauxlib.h" #include "lxplib.h" diff --git a/lib/sqlite/lsqlite3.c b/lib/sqlite/lsqlite3.c index 8098311ac..f896daf23 100644 --- a/lib/sqlite/lsqlite3.c +++ b/lib/sqlite/lsqlite3.c @@ -44,8 +44,8 @@ extern "C" { #include #define LUA_LIB -#include "../lib/lua/src/lua.h" -#include "../lib/lua/src/lauxlib.h" +#include "lib/lua/src/lua.h" +#include "lib/lua/src/lauxlib.h" #if LUA_VERSION_NUM > 501 // diff --git a/lib/tolua++/include/tolua++.h b/lib/tolua++/include/tolua++.h index ed5344926..a60dde635 100644 --- a/lib/tolua++/include/tolua++.h +++ b/lib/tolua++/include/tolua++.h @@ -43,8 +43,8 @@ extern "C" { typedef int lua_Object; -#include "lua.h" -#include "lauxlib.h" +#include "../../lua/src/lua.h +#include "../../lua/src/lauxlib.h" struct tolua_Error { diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp index 3149166fd..0bde5c9f2 100644 --- a/src/BlockEntities/ChestEntity.cpp +++ b/src/BlockEntities/ChestEntity.cpp @@ -5,7 +5,7 @@ #include "../Item.h" #include "../Entities/Player.h" #include "../UI/Window.h" -#include "../lib/jsoncpp/include/json/json.h" +#include "lib/jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index a05dd5956..d49e4f53b 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -6,7 +6,7 @@ #include "../Entities/Player.h" #include "../Root.h" #include "../Chunk.h" -#include "../lib/jsoncpp/include/json/json.h" +#include "lib/jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/JukeboxEntity.cpp b/src/BlockEntities/JukeboxEntity.cpp index 1b5c03826..8e90ee70d 100644 --- a/src/BlockEntities/JukeboxEntity.cpp +++ b/src/BlockEntities/JukeboxEntity.cpp @@ -3,7 +3,7 @@ #include "JukeboxEntity.h" #include "../World.h" -#include "../lib/jsoncpp/include/json/json.h" +#include "lib/jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp index 6bb570133..493316bce 100644 --- a/src/BlockEntities/NoteEntity.cpp +++ b/src/BlockEntities/NoteEntity.cpp @@ -3,7 +3,7 @@ #include "NoteEntity.h" #include "../World.h" -#include "../lib/jsoncpp/include/json/json.h" +#include "lib/jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp index 9cbac5679..39fcb6efc 100644 --- a/src/BlockEntities/SignEntity.cpp +++ b/src/BlockEntities/SignEntity.cpp @@ -4,7 +4,7 @@ // Implements the cSignEntity class representing a single sign in the world #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "../lib/jsoncpp/include/json/json.h" +#include "lib/jsoncpp/include/json/json.h" #include "SignEntity.h" #include "../Entities/Player.h" diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index c05778bb1..4dff50918 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include "BioGen.h" -#include "../lib/inifile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "../LinearUpscale.h" diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index a7107e325..cb993bb05 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -3,7 +3,7 @@ #include "ChunkGenerator.h" #include "../World.h" -#include "../lib/iniFile/iniFile.h" +#include "lib/iniFile/iniFile.h" #include "../Root.h" #include "../PluginManager.h" #include "ChunkDesc.h" diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp index 9b93fcf3b..7e1ade147 100644 --- a/src/Generating/CompoGen.cpp +++ b/src/Generating/CompoGen.cpp @@ -12,7 +12,7 @@ #include "../BlockID.h" #include "../Item.h" #include "../LinearUpscale.h" -#include "../lib/inifile/iniFile.h" +#include "lib/inifile/iniFile.h" diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 40914605a..496ada52d 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -7,7 +7,7 @@ #include "ComposableGenerator.h" #include "../World.h" -#include "../lib/iniFile/iniFile.h" +#include "lib/iniFile/iniFile.h" #include "../Root.h" // Individual composed algorithms: diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index ae5cd02e8..6fbe7c1d4 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -7,7 +7,7 @@ #include "DistortedHeightmap.h" #include "../OSSupport/File.h" -#include "../lib/inifile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "../LinearUpscale.h" diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp index 750a30f91..121ed4488 100644 --- a/src/Generating/EndGen.cpp +++ b/src/Generating/EndGen.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include "EndGen.h" -#include "../lib/inifile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "../LinearUpscale.h" diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index 9e656d269..9cc1fbbaa 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -6,7 +6,7 @@ #include "Globals.h" #include "HeiGen.h" #include "../LinearUpscale.h" -#include "../lib/inifile/iniFile.h" +#include "lib/inifile/iniFile.h" diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index 36ef55e89..9760ca4ca 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -6,7 +6,7 @@ #include "Globals.h" #include "Noise3DGenerator.h" #include "../OSSupport/File.h" -#include "../lib/inifile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "../LinearInterpolation.h" #include "../LinearUpscale.h" diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h index 65cdcb3a5..ed7a03519 100644 --- a/src/HTTPServer/HTTPServer.h +++ b/src/HTTPServer/HTTPServer.h @@ -11,7 +11,7 @@ #include "../OSSupport/ListenThread.h" #include "../OSSupport/SocketThreads.h" -#include "../lib/inifile/iniFile.h" +#include "lib/inifile/iniFile.h" diff --git a/src/OSSupport/GZipFile.h b/src/OSSupport/GZipFile.h index df86717f6..dcc171a2a 100644 --- a/src/OSSupport/GZipFile.h +++ b/src/OSSupport/GZipFile.h @@ -9,7 +9,7 @@ #pragma once -#include "../lib/zlib/zlib.h" +#include "lib/zlib/zlib.h" diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index c83a87ca9..d406b8b51 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -7,7 +7,7 @@ #include "Globals.h" #include "ChunkDataSerializer.h" -#include "../lib/zlib/zlib.h" +#include "lib/zlib/zlib.h" diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index bfa8eb412..8f7891e4b 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -5,12 +5,12 @@ #include "Globals.h" #include "ChunkDataSerializer.h" +#include "lib/cryptopp/randpool.h" #include "Protocol132.h" #include "../Root.h" #include "../Server.h" #include "../World.h" #include "../ClientHandle.h" -#include "../lib/cryptopp/randpool.h" #include "../Item.h" #include "../Entities/Player.h" #include "../Mobs/Monster.h" diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h index 58e98c208..2023d7960 100644 --- a/src/Protocol/Protocol132.h +++ b/src/Protocol/Protocol132.h @@ -10,8 +10,8 @@ #pragma once #include "Protocol125.h" -#include "../lib/cryptopp/modes.h" -#include "../lib/cryptopp/aes.h" +#include "lib/cryptopp/modes.h" +#include "lib/cryptopp/aes.h" diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp index 9fb2a8133..ea406d7a2 100644 --- a/src/Protocol/Protocol14x.cpp +++ b/src/Protocol/Protocol14x.cpp @@ -16,7 +16,7 @@ Implements the 1.4.x protocol classes representing these protocols: #include "../Root.h" #include "../Server.h" #include "../ClientHandle.h" -#include "../lib/cryptopp/randpool.h" +#include "lib/cryptopp/randpool.h" #include "../Item.h" #include "ChunkDataSerializer.h" #include "../Entities/Player.h" diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index b55bbf80b..449727854 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -16,8 +16,8 @@ Declares the 1.7.x protocol classes: #include "Protocol.h" #include "../ByteBuffer.h" -#include "../lib/cryptopp/modes.h" -#include "../lib/cryptopp/aes.h" +#include "lib/cryptopp/modes.h" +#include "lib/cryptopp/aes.h" diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h index 9d3ad4131..4f555693d 100644 --- a/src/Simulator/Simulator.h +++ b/src/Simulator/Simulator.h @@ -2,7 +2,7 @@ #pragma once #include "../Vector3i.h" -#include "../lib/inifile/iniFile.h" +#include "lib/inifile/iniFile.h" diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index ee7511486..48163e222 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -7,7 +7,7 @@ #include "WSSAnvil.h" #include "NBTChunkSerializer.h" #include "../World.h" -#include "../lib/zlib/zlib.h" +#include "lib/zlib/zlib.h" #include "../BlockID.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DispenserEntity.h" diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp index 812c60411..dafa4d1b5 100644 --- a/src/WorldStorage/WSSCompact.cpp +++ b/src/WorldStorage/WSSCompact.cpp @@ -6,8 +6,8 @@ #include "Globals.h" #include "WSSCompact.h" #include "../World.h" -#include "../lib/zlib/zlib.h" -#include "../lib/jsoncpp/include/json/json.h" +#include "lib/zlib/zlib.h" +#include "lib/jsoncpp/include/json/json.h" #include "../StringCompression.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DispenserEntity.h" diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index 384ade081..b2c6dfb4b 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -16,7 +16,7 @@ #include "../ChunkDef.h" #include "../OSSupport/IsThread.h" -#include "../lib/jsoncpp/include/json/json.h" +#include "lib/jsoncpp/include/json/json.h" -- cgit v1.2.3 From 28b09377f351312887d04a53c5c50693a75d7c9d Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 25 Nov 2013 18:43:46 +0000 Subject: Updated VS includes --- VC2013/MCServer.vcxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/VC2013/MCServer.vcxproj b/VC2013/MCServer.vcxproj index c9fe5c67c..ff3abd68f 100644 --- a/VC2013/MCServer.vcxproj +++ b/VC2013/MCServer.vcxproj @@ -110,7 +110,7 @@ true Speed true - ../zlib-1.2.7;../jsoncpp-src-0.5.0/include;../lua-5.1.4/src;../tolua++-1.0.93/include;..;../expat;%(AdditionalIncludeDirectories) + ../lib/zlib;../lib/jsoncpp/include;../lib/lua/src;../lib/tolua++/include;..;../lib/expat;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;XML_STATIC;%(PreprocessorDefinitions) MultiThreaded Use @@ -136,7 +136,7 @@ true Speed true - ../zlib-1.2.7;../jsoncpp-src-0.5.0/include;../lua-5.1.4/src;../tolua++-1.0.93/include;..;../expat;%(AdditionalIncludeDirectories) + ../lib/zlib;../lib/jsoncpp/include;../lib/lua/src;../lib/tolua++/include;..;../lib/expat;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;XML_STATIC;%(PreprocessorDefinitions) MultiThreaded Use @@ -160,7 +160,7 @@ /MP %(AdditionalOptions) Disabled - ../zlib-1.2.7;../jsoncpp-src-0.5.0/include;../lua-5.1.4/src;../tolua++-1.0.93/include;..;../expat;%(AdditionalIncludeDirectories) + ../lib/zlib;../lib/jsoncpp/include;../lib/lua/src;../lib/tolua++/include;..;../lib/expat;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CONSOLE;XML_STATIC;%(PreprocessorDefinitions) false EnableFastChecks -- cgit v1.2.3 From b93d1b5027afc55be3a5e1c882c023d400f2bbfa Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 25 Nov 2013 20:04:01 +0100 Subject: Implented Spawn Experience Orb packet. --- source/Protocol/Protocol.h | 1 + source/Protocol/Protocol125.cpp | 18 ++++++++++++++++++ source/Protocol/Protocol125.h | 1 + source/Protocol/Protocol17x.cpp | 15 +++++++++++++++ source/Protocol/Protocol17x.h | 1 + source/Protocol/ProtocolRecognizer.cpp | 10 ++++++++++ source/Protocol/ProtocolRecognizer.h | 1 + 7 files changed, 47 insertions(+) diff --git a/source/Protocol/Protocol.h b/source/Protocol/Protocol.h index 542060ece..18cf3676a 100644 --- a/source/Protocol/Protocol.h +++ b/source/Protocol/Protocol.h @@ -86,6 +86,7 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; virtual void SendRespawn (void) = 0; virtual void SendExperience (void) = 0; + virtual void SendExperienceOrb (const cEntity & a_Entity) = 0; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp index 54be65b12..3b753479d 100644 --- a/source/Protocol/Protocol125.cpp +++ b/source/Protocol/Protocol125.cpp @@ -17,6 +17,7 @@ Documentation: #include "../World.h" #include "ChunkDataSerializer.h" #include "../Entities/Entity.h" +#include "../Entities/ExpOrb.h" #include "../Mobs/Monster.h" #include "../Entities/Pickup.h" #include "../Entities/Player.h" @@ -72,6 +73,7 @@ enum PACKET_ENT_STATUS = 0x26, PACKET_ATTACH_ENTITY = 0x27, PACKET_METADATA = 0x28, + PACKET_SPAWN_EXPERIENCE_ORB = 0x1A, PACKET_EXPERIENCE = 0x2b, PACKET_PRE_CHUNK = 0x32, PACKET_MAP_CHUNK = 0x33, @@ -705,6 +707,22 @@ void cProtocol125::SendExperience(void) +void cProtocol125::SendExperienceOrb(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_EXPERIENCE_ORB); + WriteInt(a_Entity.GetUniqueID()); + WriteInt((int) a_Entity.GetPosX()); + WriteInt((int) a_Entity.GetPosY()); + WriteInt((int) a_Entity.GetPosZ()); + WriteShort(((cExpOrb &)a_Entity).GetReward()); + Flush(); +} + + + + + void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) { // Not needed in this protocol version diff --git a/source/Protocol/Protocol125.h b/source/Protocol/Protocol125.h index c5f44c818..2182ea356 100644 --- a/source/Protocol/Protocol125.h +++ b/source/Protocol/Protocol125.h @@ -63,6 +63,7 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendRespawn (void) override; virtual void SendExperience (void) override; + virtual void SendExperienceOrb (const cEntity & a_Entity) override; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; diff --git a/source/Protocol/Protocol17x.cpp b/source/Protocol/Protocol17x.cpp index ae1df7395..cf5c84599 100644 --- a/source/Protocol/Protocol17x.cpp +++ b/source/Protocol/Protocol17x.cpp @@ -17,6 +17,7 @@ Implements the 1.7.x protocol classes: #include "../World.h" #include "../WorldStorage/FastNBT.h" #include "../StringCompression.h" +#include "../Entities/ExpOrb.h" #include "../Entities/Minecart.h" #include "../Entities/FallingBlock.h" #include "../Entities/Pickup.h" @@ -609,6 +610,20 @@ void cProtocol172::SendExperience (void) +void cProtocol172::SendExperienceOrb(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x11); + Pkt.WriteVarInt(a_Entity.GetUniqueID()); + Pkt.WriteInt((int) a_Entity.GetPosX()); + Pkt.WriteInt((int) a_Entity.GetPosY()); + Pkt.WriteInt((int) a_Entity.GetPosZ()); + Pkt.WriteShort(((cExpOrb &)a_Entity).GetReward()); +} + + + + + void cProtocol172::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) // a_Src coords are Block * 8 { cPacketizer Pkt(*this, 0x29); // Sound Effect packet diff --git a/source/Protocol/Protocol17x.h b/source/Protocol/Protocol17x.h index e3f2ad922..6b8d2ce79 100644 --- a/source/Protocol/Protocol17x.h +++ b/source/Protocol/Protocol17x.h @@ -73,6 +73,7 @@ public: virtual void SendRespawn (void) override; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 virtual void SendExperience (void) override; + virtual void SendExperienceOrb (const cEntity & a_Entity) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; virtual void SendSpawnMob (const cMonster & a_Mob) override; diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp index 64bd83075..ad00e8d38 100644 --- a/source/Protocol/ProtocolRecognizer.cpp +++ b/source/Protocol/ProtocolRecognizer.cpp @@ -476,6 +476,16 @@ void cProtocolRecognizer::SendExperience(void) +void cProtocolRecognizer::SendExperienceOrb(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendExperienceOrb(a_Entity); +} + + + + + void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) { ASSERT(m_Protocol != NULL); diff --git a/source/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h index 03f48fb35..62e624980 100644 --- a/source/Protocol/ProtocolRecognizer.h +++ b/source/Protocol/ProtocolRecognizer.h @@ -98,6 +98,7 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendRespawn (void) override; virtual void SendExperience (void) override; + virtual void SendExperienceOrb (const cEntity & a_Entity) override; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; -- cgit v1.2.3 From f86f67907cf3812c09ad10166677e9c91b1f1198 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 25 Nov 2013 20:04:39 +0100 Subject: Implented SendExperienceOrb in ClientHandle. --- source/ClientHandle.cpp | 9 +++++++++ source/ClientHandle.h | 1 + source/Entities/Entity.h | 1 + source/Mobs/Monster.cpp | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp index daf09d4ea..1bc741aa6 100644 --- a/source/ClientHandle.cpp +++ b/source/ClientHandle.cpp @@ -1885,6 +1885,15 @@ void cClientHandle::SendExperience(void) +void cClientHandle::SendExperienceOrb(const cEntity & a_Entity) +{ + m_Protocol->SendExperienceOrb(a_Entity); +} + + + + + void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) { m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); diff --git a/source/ClientHandle.h b/source/ClientHandle.h index b887bb11a..2857b0dcd 100644 --- a/source/ClientHandle.h +++ b/source/ClientHandle.h @@ -121,6 +121,7 @@ public: void SendPlayerSpawn (const cPlayer & a_Player); void SendRespawn (void); void SendExperience (void); + void SendExperienceOrb (const cEntity & a_Entity); void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8 void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data); void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock); diff --git a/source/Entities/Entity.h b/source/Entities/Entity.h index dafda7826..de5f176ae 100644 --- a/source/Entities/Entity.h +++ b/source/Entities/Entity.h @@ -74,6 +74,7 @@ public: etBoat, etTNT, etProjectile, + etExpOrb, // Common variations etMob = etMonster, // DEPRECATED, use etMonster instead! diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp index 8a5717e27..f250e1757 100644 --- a/source/Mobs/Monster.cpp +++ b/source/Mobs/Monster.cpp @@ -7,6 +7,7 @@ #include "../ClientHandle.h" #include "../World.h" #include "../Entities/Player.h" +#include "../Entities/ExpOrb.h" #include "../Defines.h" #include "../MonsterConfig.h" #include "../MersenneTwister.h" @@ -258,6 +259,9 @@ void cMonster::KilledBy(cEntity * a_Killer) { m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); } + // ToDo: Proper Exp per mob. + cExpOrb * ExpOrb = new cExpOrb(GetPosX(), GetPosY(), GetPosZ(), 1); + ExpOrb->Initialize(m_World); m_DestroyTimer = 0; } -- cgit v1.2.3 From 6641db058392dd6d7e170b95cd8616b920add63c Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 25 Nov 2013 20:05:52 +0100 Subject: Implented ExpOrb class. --- source/Entities/ExpOrb.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++ source/Entities/ExpOrb.h | 29 ++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 source/Entities/ExpOrb.cpp create mode 100644 source/Entities/ExpOrb.h diff --git a/source/Entities/ExpOrb.cpp b/source/Entities/ExpOrb.cpp new file mode 100644 index 000000000..200f6e181 --- /dev/null +++ b/source/Entities/ExpOrb.cpp @@ -0,0 +1,60 @@ +#include "Globals.h" + +#include "ExpOrb.h" +#include "Player.h" +#include "../ClientHandle.h" + + +cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward) : + cEntity(etExpOrb, a_X, a_Y, a_Z, 0.98, 0.98), + m_Reward(a_Reward) +{ +} + + + + + +cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward) : + cEntity(etExpOrb, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), + m_Reward(a_Reward) +{ +} + + + + + +void cExpOrb::SpawnOn(cClientHandle & a_Client) +{ + a_Client.SendExperienceOrb(*this); + m_bDirtyPosition = false; + m_bDirtySpeed = false; + m_bDirtyOrientation = false; + m_bDirtyHead = false; +} + + + + + +void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) +{ + cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 3)); + if (a_ClosestPlayer) + { + Vector3f a_PlayerPos(a_ClosestPlayer->GetPosition()); + Vector3f a_Distance(a_PlayerPos - GetPosition()); + if (a_Distance.Length() < 0.1f) + { + a_ClosestPlayer->DeltaExperience(m_Reward); + a_ClosestPlayer->SendExperience(); + Destroy(true); + } + a_Distance.y = 0; + a_Distance.Normalize(); + a_Distance *= 3; + SetSpeedX( a_Distance.x ); + SetSpeedZ( a_Distance.z ); + } +} \ No newline at end of file diff --git a/source/Entities/ExpOrb.h b/source/Entities/ExpOrb.h new file mode 100644 index 000000000..0f1b6ede7 --- /dev/null +++ b/source/Entities/ExpOrb.h @@ -0,0 +1,29 @@ + +#pragma once + +#include "Entity.h" + + + + + +class cExpOrb : + public cEntity +{ + typedef cExpOrb super; + +public: + + cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward); + cExpOrb(const Vector3d & a_Pos, int a_Reward); + + // Override functions + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void SpawnOn(cClientHandle & a_Client) override; + + // cExpOrb functions + short int GetReward(void) const { return m_Reward; } + +protected: + int m_Reward; +} ; \ No newline at end of file -- cgit v1.2.3 From dd76700d8d0491f385efee1a539462501cb41ad4 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 25 Nov 2013 21:03:26 +0100 Subject: Added cWorld::SpawnExperienceOrb function. Most mobs now give proper Exp. --- source/Mobs/Monster.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++++--- source/World.cpp | 11 ++++++++++ source/World.h | 3 +++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp index f250e1757..a74881978 100644 --- a/source/Mobs/Monster.cpp +++ b/source/Mobs/Monster.cpp @@ -259,9 +259,56 @@ void cMonster::KilledBy(cEntity * a_Killer) { m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); } - // ToDo: Proper Exp per mob. - cExpOrb * ExpOrb = new cExpOrb(GetPosX(), GetPosY(), GetPosZ(), 1); - ExpOrb->Initialize(m_World); + int Exp; + switch (m_MobType) + { + // Animals + case cMonster::mtChicken: + case cMonster::mtCow: + case cMonster::mtHorse: + case cMonster::mtPig: + case cMonster::mtSheep: + case cMonster::mtSquid: + case cMonster::mtMooshroom: + case cMonster::mtOcelot: + case cMonster::mtWolf: + { + Exp = m_World->GetTickRandomNumber(2) + 1; + } + + // Monsters + case cMonster::mtCaveSpider: + case cMonster::mtCreeper: + case cMonster::mtEnderman: + case cMonster::mtGhast: + case cMonster::mtSilverfish: + case cMonster::mtSkeleton: + case cMonster::mtSpider: + case cMonster::mtWitch: + case cMonster::mtZombie: + case cMonster::mtZombiePigman: + case cMonster::mtSlime: + case cMonster::mtMagmaCube: + { + Exp = 6 + (m_World->GetTickRandomNumber(2)); + } + + // Bosses + case cMonster::mtEnderDragon: + { + Exp = 12000; + } + case cMonster::mtWither: + { + Exp = 50; + } + + default: + { + Exp = 0; + } + } + m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Exp); m_DestroyTimer = 0; } diff --git a/source/World.cpp b/source/World.cpp index 531952e37..432ab32e9 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -13,6 +13,7 @@ #include "OSSupport/Timer.h" // Entities (except mobs): +#include "Entities/ExpOrb.h" #include "Entities/Pickup.h" #include "Entities/Player.h" #include "Entities/TNTEntity.h" @@ -1561,6 +1562,16 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double +void cWorld::SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward) +{ + cExpOrb * ExpOrb = new cExpOrb(a_X, a_Y, a_Z, a_Reward); + ExpOrb->Initialize(this); +} + + + + + void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff) { cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTimeInSec); diff --git a/source/World.h b/source/World.h index d10aa3b78..9397f8b75 100644 --- a/source/World.h +++ b/source/World.h @@ -353,6 +353,9 @@ public: /// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified: void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false); + /// Spawns an experience orb at the given location with the given reward. + void SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward); + /// Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff = 1); -- cgit v1.2.3 From 18b5ccbc0836dbfb4adcff76dbfbde340bcabbc1 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 25 Nov 2013 21:43:43 +0100 Subject: Fixed Details Xoft gave. Increased the range where orbs (should) track you. Blazes give 10 xp now. --- source/ClientHandle.cpp | 4 ++-- source/ClientHandle.h | 3 ++- source/Entities/ExpOrb.cpp | 2 +- source/Entities/ExpOrb.h | 2 +- source/Mobs/Monster.cpp | 18 +++++++++++------- source/Protocol/Protocol.h | 3 ++- source/Protocol/Protocol125.cpp | 12 ++++++------ source/Protocol/Protocol125.h | 2 +- source/Protocol/Protocol17x.cpp | 12 ++++++------ source/Protocol/Protocol17x.h | 2 +- source/Protocol/ProtocolRecognizer.cpp | 4 ++-- source/Protocol/ProtocolRecognizer.h | 2 +- 12 files changed, 36 insertions(+), 30 deletions(-) diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp index 1bc741aa6..b3e12ce77 100644 --- a/source/ClientHandle.cpp +++ b/source/ClientHandle.cpp @@ -1885,9 +1885,9 @@ void cClientHandle::SendExperience(void) -void cClientHandle::SendExperienceOrb(const cEntity & a_Entity) +void cClientHandle::SendExperienceOrb(const cExpOrb & a_ExpOrb) { - m_Protocol->SendExperienceOrb(a_Entity); + m_Protocol->SendExperienceOrb(a_ExpOrb); } diff --git a/source/ClientHandle.h b/source/ClientHandle.h index 2857b0dcd..b3550110d 100644 --- a/source/ClientHandle.h +++ b/source/ClientHandle.h @@ -25,6 +25,7 @@ class cChunkDataSerializer; class cInventory; class cMonster; class cPawn; +class cExpOrb; class cPickup; class cPlayer; class cProtocol; @@ -121,7 +122,7 @@ public: void SendPlayerSpawn (const cPlayer & a_Player); void SendRespawn (void); void SendExperience (void); - void SendExperienceOrb (const cEntity & a_Entity); + void SendExperienceOrb (const cExpOrb & a_ExpOrb); void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8 void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data); void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock); diff --git a/source/Entities/ExpOrb.cpp b/source/Entities/ExpOrb.cpp index 200f6e181..1e5ee00ce 100644 --- a/source/Entities/ExpOrb.cpp +++ b/source/Entities/ExpOrb.cpp @@ -40,7 +40,7 @@ void cExpOrb::SpawnOn(cClientHandle & a_Client) void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) { - cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 3)); + cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 4)); if (a_ClosestPlayer) { Vector3f a_PlayerPos(a_ClosestPlayer->GetPosition()); diff --git a/source/Entities/ExpOrb.h b/source/Entities/ExpOrb.h index 0f1b6ede7..a062eedd3 100644 --- a/source/Entities/ExpOrb.h +++ b/source/Entities/ExpOrb.h @@ -22,7 +22,7 @@ public: virtual void SpawnOn(cClientHandle & a_Client) override; // cExpOrb functions - short int GetReward(void) const { return m_Reward; } + int GetReward(void) const { return m_Reward; } protected: int m_Reward; diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp index a74881978..091623c8a 100644 --- a/source/Mobs/Monster.cpp +++ b/source/Mobs/Monster.cpp @@ -259,7 +259,7 @@ void cMonster::KilledBy(cEntity * a_Killer) { m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); } - int Exp; + int Reward; switch (m_MobType) { // Animals @@ -273,7 +273,7 @@ void cMonster::KilledBy(cEntity * a_Killer) case cMonster::mtOcelot: case cMonster::mtWolf: { - Exp = m_World->GetTickRandomNumber(2) + 1; + Reward = m_World->GetTickRandomNumber(2) + 1; } // Monsters @@ -290,25 +290,29 @@ void cMonster::KilledBy(cEntity * a_Killer) case cMonster::mtSlime: case cMonster::mtMagmaCube: { - Exp = 6 + (m_World->GetTickRandomNumber(2)); + Reward = 6 + (m_World->GetTickRandomNumber(2)); + } + case cMonster::mtBlaze: + { + Reward = 10; } // Bosses case cMonster::mtEnderDragon: { - Exp = 12000; + Reward = 12000; } case cMonster::mtWither: { - Exp = 50; + Reward = 50; } default: { - Exp = 0; + Reward = 0; } } - m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Exp); + m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); m_DestroyTimer = 0; } diff --git a/source/Protocol/Protocol.h b/source/Protocol/Protocol.h index 18cf3676a..9d8183361 100644 --- a/source/Protocol/Protocol.h +++ b/source/Protocol/Protocol.h @@ -16,6 +16,7 @@ +class cExpOrb; class cPlayer; class cEntity; class cWindow; @@ -86,7 +87,7 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; virtual void SendRespawn (void) = 0; virtual void SendExperience (void) = 0; - virtual void SendExperienceOrb (const cEntity & a_Entity) = 0; + virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp index 3b753479d..b1dd17ea1 100644 --- a/source/Protocol/Protocol125.cpp +++ b/source/Protocol/Protocol125.cpp @@ -707,15 +707,15 @@ void cProtocol125::SendExperience(void) -void cProtocol125::SendExperienceOrb(const cEntity & a_Entity) +void cProtocol125::SendExperienceOrb(const cExpOrb & a_ExpOrb) { cCSLock Lock(m_CSPacket); WriteByte(PACKET_SPAWN_EXPERIENCE_ORB); - WriteInt(a_Entity.GetUniqueID()); - WriteInt((int) a_Entity.GetPosX()); - WriteInt((int) a_Entity.GetPosY()); - WriteInt((int) a_Entity.GetPosZ()); - WriteShort(((cExpOrb &)a_Entity).GetReward()); + WriteInt(a_ExpOrb.GetUniqueID()); + WriteInt((int) a_ExpOrb.GetPosX()); + WriteInt((int) a_ExpOrb.GetPosY()); + WriteInt((int) a_ExpOrb.GetPosZ()); + WriteShort(a_ExpOrb.GetReward()); Flush(); } diff --git a/source/Protocol/Protocol125.h b/source/Protocol/Protocol125.h index 2182ea356..5a9218f5b 100644 --- a/source/Protocol/Protocol125.h +++ b/source/Protocol/Protocol125.h @@ -63,7 +63,7 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendRespawn (void) override; virtual void SendExperience (void) override; - virtual void SendExperienceOrb (const cEntity & a_Entity) override; + virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; diff --git a/source/Protocol/Protocol17x.cpp b/source/Protocol/Protocol17x.cpp index cf5c84599..746e1c127 100644 --- a/source/Protocol/Protocol17x.cpp +++ b/source/Protocol/Protocol17x.cpp @@ -610,14 +610,14 @@ void cProtocol172::SendExperience (void) -void cProtocol172::SendExperienceOrb(const cEntity & a_Entity) +void cProtocol172::SendExperienceOrb(const cExpOrb & a_ExpOrb) { cPacketizer Pkt(*this, 0x11); - Pkt.WriteVarInt(a_Entity.GetUniqueID()); - Pkt.WriteInt((int) a_Entity.GetPosX()); - Pkt.WriteInt((int) a_Entity.GetPosY()); - Pkt.WriteInt((int) a_Entity.GetPosZ()); - Pkt.WriteShort(((cExpOrb &)a_Entity).GetReward()); + Pkt.WriteVarInt(a_ExpOrb.GetUniqueID()); + Pkt.WriteInt((int) a_ExpOrb.GetPosX()); + Pkt.WriteInt((int) a_ExpOrb.GetPosY()); + Pkt.WriteInt((int) a_ExpOrb.GetPosZ()); + Pkt.WriteShort(a_ExpOrb.GetReward()); } diff --git a/source/Protocol/Protocol17x.h b/source/Protocol/Protocol17x.h index 6b8d2ce79..255cb4ef5 100644 --- a/source/Protocol/Protocol17x.h +++ b/source/Protocol/Protocol17x.h @@ -73,7 +73,7 @@ public: virtual void SendRespawn (void) override; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 virtual void SendExperience (void) override; - virtual void SendExperienceOrb (const cEntity & a_Entity) override; + virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; virtual void SendSpawnMob (const cMonster & a_Mob) override; diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp index ad00e8d38..489149d74 100644 --- a/source/Protocol/ProtocolRecognizer.cpp +++ b/source/Protocol/ProtocolRecognizer.cpp @@ -476,10 +476,10 @@ void cProtocolRecognizer::SendExperience(void) -void cProtocolRecognizer::SendExperienceOrb(const cEntity & a_Entity) +void cProtocolRecognizer::SendExperienceOrb(const cExpOrb & a_ExpOrb) { ASSERT(m_Protocol != NULL); - m_Protocol->SendExperienceOrb(a_Entity); + m_Protocol->SendExperienceOrb(a_ExpOrb); } diff --git a/source/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h index 62e624980..9ca0c1c88 100644 --- a/source/Protocol/ProtocolRecognizer.h +++ b/source/Protocol/ProtocolRecognizer.h @@ -98,7 +98,7 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendRespawn (void) override; virtual void SendExperience (void) override; - virtual void SendExperienceOrb (const cEntity & a_Entity) override; + virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; -- cgit v1.2.3 From 960b1654a60dc4ae850b8758b4b1bed1f34a46cb Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 25 Nov 2013 21:48:43 +0100 Subject: Exported cWorld::SpawnExperienceOrb() function to the Lua API. --- source/Bindings.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- source/Bindings.h | 2 +- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/source/Bindings.cpp b/source/Bindings.cpp index ad3ad8423..7bea672da 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 11/23/13 19:57:30. +** Generated automatically by tolua++-1.0.92 on 11/25/13 21:47:32. */ #ifndef __cplusplus @@ -12589,6 +12589,45 @@ tolua_lerror: } #endif //#ifndef TOLUA_DISABLE +/* method: SpawnExperienceOrb of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnExperienceOrb00 +static int tolua_AllToLua_cWorld_SpawnExperienceOrb00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + double a_X = ((double) tolua_tonumber(tolua_S,2,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,4,0)); + int a_Reward = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnExperienceOrb'", NULL); +#endif + { + self->SpawnExperienceOrb(a_X,a_Y,a_Z,a_Reward); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SpawnExperienceOrb'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: SpawnPrimedTNT of class cWorld */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnPrimedTNT00 static int tolua_AllToLua_cWorld_SpawnPrimedTNT00(lua_State* tolua_S) @@ -30430,6 +30469,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"etBoat",cEntity::etBoat); tolua_constant(tolua_S,"etTNT",cEntity::etTNT); tolua_constant(tolua_S,"etProjectile",cEntity::etProjectile); + tolua_constant(tolua_S,"etExpOrb",cEntity::etExpOrb); tolua_constant(tolua_S,"etMob",cEntity::etMob); tolua_function(tolua_S,"GetEntityType",tolua_AllToLua_cEntity_GetEntityType00); tolua_function(tolua_S,"IsPlayer",tolua_AllToLua_cEntity_IsPlayer00); @@ -30801,6 +30841,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta01); tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups00); tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups01); + tolua_function(tolua_S,"SpawnExperienceOrb",tolua_AllToLua_cWorld_SpawnExperienceOrb00); tolua_function(tolua_S,"SpawnPrimedTNT",tolua_AllToLua_cWorld_SpawnPrimedTNT00); tolua_function(tolua_S,"DigBlock",tolua_AllToLua_cWorld_DigBlock00); tolua_function(tolua_S,"SendBlockTo",tolua_AllToLua_cWorld_SendBlockTo00); diff --git a/source/Bindings.h b/source/Bindings.h index bc8589293..8d20bd0dc 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 11/23/13 19:57:31. +** Generated automatically by tolua++-1.0.92 on 11/25/13 21:47:33. */ /* Exported function */ -- cgit v1.2.3 From a0c6342a294077b67f509b6f319468f56e39e344 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 25 Nov 2013 21:51:52 +0100 Subject: Documented SpawnExperienceOrb in cWorld --- MCServer/Plugins/APIDump/APIDesc.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 44e4bb35f..9f558f58c 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2075,6 +2075,7 @@ end { Params = "{{cItems|Pickups}}, X, Y, Z, SpeedX, SpeedY, SpeedZ", Return = "", Notes = "Spawns the specified pickups at the position specified. All the pickups fly away from the spawn position using the specified speed." }, }, SpawnMob = { Params = "X, Y, Z, {{cMonster|MonsterType}}", Return = "EntityID", Notes = "Spawns the specified type of mob at the specified coords. Returns the EntityID of the creates entity, or -1 on failure. " }, + SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "", Notes = "Spawns an {{cExpOrb|experience orb at the specified coords, with the given reward" }, SpawnPrimedTNT = { Params = "X, Y, Z, FuseTimeSecs, InitialVelocityCoeff", Return = "", Notes = "Spawns a {{cTNTEntity|primed TNT entity}} at the specified coords, with the given fuse time. The entity gets a random speed multiplied by the InitialVelocityCoeff, 1 being the default value." }, TryGetHeight = { Params = "BlockX, BlockZ", Return = "IsValid, Height", Notes = "Returns true and height of the highest non-air block if the chunk is loaded, or false otherwise." }, UnloadUnusedChunks = { Params = "", Return = "", Notes = "Unloads chunks that are no longer needed, and are saved. NOTE: This API is deprecated and will be removed soon." }, -- cgit v1.2.3 From 1358a74412b45b1c214bfe58f4c5160ca75f4da7 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 25 Nov 2013 22:09:20 +0100 Subject: Added ExpOrb.cpp and ExpOrb.h to MCServer.vcproj. --- VC2008/MCServer.vcproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index 93a34d90a..f9acdd852 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -1267,6 +1267,14 @@ RelativePath="..\source\Entities\ProjectileEntity.h" > + + + + -- cgit v1.2.3 From 3466d2cad9a5723c327c3a64123b5321df968ba5 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 25 Nov 2013 21:32:09 +0000 Subject: Fixed all outstanding VS compilation issues Also updated VS2008 files. --- VC2008/CryptoPP.vcproj | 30 ++++++++++++------------ VC2008/JsonCpp.vcproj | 12 +++++----- VC2008/Lua.vcproj | 2 +- VC2008/MCServer.vcproj | 20 ++++++++-------- VC2008/ToLua.cbp | 12 +++++----- VC2008/ToLua.vcproj | 8 +++---- VC2013/CryptoPP.vcxproj | 4 ---- VC2013/CryptoPP.vcxproj.filters | 18 +++----------- VC2013/Lua.vcxproj | 2 +- VC2013/Lua.vcxproj.filters | 2 +- VC2013/MCServer.vcxproj | 38 +++++++++++++++++++++++++----- VC2013/MCServer.vcxproj.filters | 52 ++++++++++++++++++++--------------------- VC2013/ToLua.vcxproj | 8 +++---- lib/tolua++/include/tolua++.h | 2 +- 14 files changed, 110 insertions(+), 100 deletions(-) diff --git a/VC2008/CryptoPP.vcproj b/VC2008/CryptoPP.vcproj index 44f5f226c..530467eeb 100644 --- a/VC2008/CryptoPP.vcproj +++ b/VC2008/CryptoPP.vcproj @@ -663,7 +663,7 @@ diff --git a/VC2008/JsonCpp.vcproj b/VC2008/JsonCpp.vcproj index 577507d50..8b35f170b 100644 --- a/VC2008/JsonCpp.vcproj +++ b/VC2008/JsonCpp.vcproj @@ -276,15 +276,15 @@ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > diff --git a/VC2008/Lua.vcproj b/VC2008/Lua.vcproj index a21dadc66..326ba6411 100644 --- a/VC2008/Lua.vcproj +++ b/VC2008/Lua.vcproj @@ -304,7 +304,7 @@ > @@ -1696,7 +1696,7 @@ @@ -1707,7 +1707,7 @@ @@ -1718,7 +1718,7 @@ @@ -1840,11 +1840,11 @@ Name="External" > - + - + @@ -44,11 +44,11 @@ - + - + @@ -66,11 +66,11 @@ - + - + diff --git a/VC2008/ToLua.vcproj b/VC2008/ToLua.vcproj index f174ba528..24a5d8b4f 100644 --- a/VC2008/ToLua.vcproj +++ b/VC2008/ToLua.vcproj @@ -41,7 +41,7 @@ - @@ -825,7 +824,6 @@ - @@ -868,7 +866,6 @@ - @@ -891,7 +888,6 @@ - diff --git a/VC2013/CryptoPP.vcxproj.filters b/VC2013/CryptoPP.vcxproj.filters index e8bea416f..793db552c 100644 --- a/VC2013/CryptoPP.vcxproj.filters +++ b/VC2013/CryptoPP.vcxproj.filters @@ -2,15 +2,15 @@ - {a8e36eec-ccdc-4b5d-acfa-59253ad7d84d} + {21031ece-70f0-4aa6-9069-6947175663ea} .cpp - {edf86ea6-5226-44ca-84cf-4998c10f013d} + {20dd9259-32b4-4f5b-9af8-c689a12627ec} .;.h - {50010e83-45c3-4942-a66b-d959d7f4a15f} + {9a225122-4fcd-43c9-868a-6caf0dd272ce} @@ -580,18 +580,6 @@ Header Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - diff --git a/VC2013/Lua.vcxproj b/VC2013/Lua.vcxproj index 24f58f28c..2488aa6aa 100644 --- a/VC2013/Lua.vcxproj +++ b/VC2013/Lua.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/VC2013/Lua.vcxproj.filters b/VC2013/Lua.vcxproj.filters index 8615188bd..21cc86850 100644 --- a/VC2013/Lua.vcxproj.filters +++ b/VC2013/Lua.vcxproj.filters @@ -31,7 +31,7 @@ Source Files - + Source Files diff --git a/VC2013/MCServer.vcxproj b/VC2013/MCServer.vcxproj index ff3abd68f..ecb568e9a 100644 --- a/VC2013/MCServer.vcxproj +++ b/VC2013/MCServer.vcxproj @@ -332,7 +332,7 @@ - + @@ -492,22 +492,22 @@ true GenerateBindings.cmd - cTorch.h;cStairs.h;cLadder.h;../iniFile/iniFile.h;BlockID.h;PacketID.h;Defines.h;LuaFunctions.h;cStringMap.h;cChatColor.h;cClientHandle.h;cEntity.h;cPawn.h;cPlayer.h;cPluginManager.h;cPlugin.h;cPlugin_NewLua.h;cPlugin_Lua.h;cServer.h;cWorld.h;cInventory.h;cItem.h;cWebAdmin.h;cWebPlugin.h;cWebPlugin_Lua.h;cPickup.h;cRoot.h;cTCPLink.h;Vector3f.h;Vector3d.h;Vector3i.h;Matrix4f.h;cCuboid.h;cMCLogger.h;cTracer.h;cGroup.h;BlockArea.h;packets/cPacket_Login.h;packets/cPacket_BlockDig.h;packets/cPacket_BlockPlace.h;%(AdditionalInputs) + cTorch.h;cStairs.h;cLadder.h;../lib/inifile/iniFile.h;BlockID.h;PacketID.h;Defines.h;LuaFunctions.h;cStringMap.h;cChatColor.h;cClientHandle.h;cEntity.h;cPawn.h;cPlayer.h;cPluginManager.h;cPlugin.h;cPlugin_NewLua.h;cPlugin_Lua.h;cServer.h;cWorld.h;cInventory.h;cItem.h;cWebAdmin.h;cWebPlugin.h;cWebPlugin_Lua.h;cPickup.h;cRoot.h;cTCPLink.h;Vector3f.h;Vector3d.h;Vector3i.h;Matrix4f.h;cCuboid.h;cMCLogger.h;cTracer.h;cGroup.h;BlockArea.h;packets/cPacket_Login.h;packets/cPacket_BlockDig.h;packets/cPacket_BlockPlace.h;%(AdditionalInputs) Bindings.cpp;%(Outputs) true GenerateBindings.cmd - cTorch.h;cStairs.h;cLadder.h;../iniFile/iniFile.h;BlockID.h;PacketID.h;Defines.h;LuaFunctions.h;cStringMap.h;cChatColor.h;cClientHandle.h;cEntity.h;cPawn.h;cPlayer.h;cPluginManager.h;cPlugin.h;cPlugin_NewLua.h;cPlugin_Lua.h;cServer.h;cWorld.h;cInventory.h;cItem.h;cWebAdmin.h;cWebPlugin.h;cWebPlugin_Lua.h;cPickup.h;cRoot.h;cTCPLink.h;Vector3f.h;Vector3d.h;Vector3i.h;Matrix4f.h;cCuboid.h;cMCLogger.h;cTracer.h;cGroup.h;BlockArea.h;packets/cPacket_Login.h;packets/cPacket_BlockDig.h;packets/cPacket_BlockPlace.h;%(AdditionalInputs) + cTorch.h;cStairs.h;cLadder.h;../lib/inifile/iniFile.h;BlockID.h;PacketID.h;Defines.h;LuaFunctions.h;cStringMap.h;cChatColor.h;cClientHandle.h;cEntity.h;cPawn.h;cPlayer.h;cPluginManager.h;cPlugin.h;cPlugin_NewLua.h;cPlugin_Lua.h;cServer.h;cWorld.h;cInventory.h;cItem.h;cWebAdmin.h;cWebPlugin.h;cWebPlugin_Lua.h;cPickup.h;cRoot.h;cTCPLink.h;Vector3f.h;Vector3d.h;Vector3i.h;Matrix4f.h;cCuboid.h;cMCLogger.h;cTracer.h;cGroup.h;BlockArea.h;packets/cPacket_Login.h;packets/cPacket_BlockDig.h;packets/cPacket_BlockPlace.h;%(AdditionalInputs) Bindings.cpp;%(Outputs) true GenerateBindings.cmd - cTorch.h;cStairs.h;cLadder.h;../iniFile/iniFile.h;BlockID.h;PacketID.h;Defines.h;LuaFunctions.h;cStringMap.h;cChatColor.h;cClientHandle.h;cEntity.h;cPawn.h;cPlayer.h;cPluginManager.h;cPlugin.h;cPlugin_NewLua.h;cPlugin_Lua.h;cServer.h;cWorld.h;cInventory.h;cItem.h;cWebAdmin.h;cWebPlugin.h;cWebPlugin_Lua.h;cPickup.h;cRoot.h;cTCPLink.h;Vector3f.h;Vector3d.h;Vector3i.h;Matrix4f.h;cCuboid.h;cMCLogger.h;cTracer.h;cGroup.h;BlockArea.h;packets/cPacket_Login.h;packets/cPacket_BlockDig.h;packets/cPacket_BlockPlace.h;%(AdditionalInputs) + cTorch.h;cStairs.h;cLadder.h;../lib/inifile/iniFile.h;BlockID.h;PacketID.h;Defines.h;LuaFunctions.h;cStringMap.h;cChatColor.h;cClientHandle.h;cEntity.h;cPawn.h;cPlayer.h;cPluginManager.h;cPlugin.h;cPlugin_NewLua.h;cPlugin_Lua.h;cServer.h;cWorld.h;cInventory.h;cItem.h;cWebAdmin.h;cWebPlugin.h;cWebPlugin_Lua.h;cPickup.h;cRoot.h;cTCPLink.h;Vector3f.h;Vector3d.h;Vector3i.h;Matrix4f.h;cCuboid.h;cMCLogger.h;cTracer.h;cGroup.h;BlockArea.h;packets/cPacket_Login.h;packets/cPacket_BlockDig.h;packets/cPacket_BlockPlace.h;%(AdditionalInputs) Bindings.cpp;%(Outputs) true GenerateBindings.cmd - cTorch.h;cStairs.h;cLadder.h;../iniFile/iniFile.h;BlockID.h;PacketID.h;Defines.h;LuaFunctions.h;cStringMap.h;cChatColor.h;cClientHandle.h;cEntity.h;cPawn.h;cPlayer.h;cPluginManager.h;cPlugin.h;cPlugin_NewLua.h;cPlugin_Lua.h;cServer.h;cWorld.h;cInventory.h;cItem.h;cWebAdmin.h;cWebPlugin.h;cWebPlugin_Lua.h;cPickup.h;cRoot.h;cTCPLink.h;Vector3f.h;Vector3d.h;Vector3i.h;Matrix4f.h;cCuboid.h;cMCLogger.h;cTracer.h;cGroup.h;BlockArea.h;packets/cPacket_Login.h;packets/cPacket_BlockDig.h;packets/cPacket_BlockPlace.h;%(AdditionalInputs) + cTorch.h;cStairs.h;cLadder.h;../lib/inifile/iniFile.h;BlockID.h;PacketID.h;Defines.h;LuaFunctions.h;cStringMap.h;cChatColor.h;cClientHandle.h;cEntity.h;cPawn.h;cPlayer.h;cPluginManager.h;cPlugin.h;cPlugin_NewLua.h;cPlugin_Lua.h;cServer.h;cWorld.h;cInventory.h;cItem.h;cWebAdmin.h;cWebPlugin.h;cWebPlugin_Lua.h;cPickup.h;cRoot.h;cTCPLink.h;Vector3f.h;Vector3d.h;Vector3i.h;Matrix4f.h;cCuboid.h;cMCLogger.h;cTracer.h;cGroup.h;BlockArea.h;packets/cPacket_Login.h;packets/cPacket_BlockDig.h;packets/cPacket_BlockPlace.h;%(AdditionalInputs) Bindings.cpp;%(Outputs) @@ -754,7 +754,7 @@ - + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) @@ -861,6 +861,32 @@ + + + {3423ec9a-52e4-4a4d-9753-edebc38785ef} + false + + + {5fcfaf8d-ff2c-456d-a72c-1d76f913ad96} + false + + + {5aaa90b9-946d-4034-83f3-676b06a6e326} + false + + + {082e8185-7b3a-4945-8c82-9132341a329d} + false + + + {eeab54ad-114c-4ab8-8482-0a52d502bd35} + false + + + {ea9d50fd-937a-4ef5-8c37-5f4175af4fea} + false + + diff --git a/VC2013/MCServer.vcxproj.filters b/VC2013/MCServer.vcxproj.filters index aecbcd557..1f213b900 100644 --- a/VC2013/MCServer.vcxproj.filters +++ b/VC2013/MCServer.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -10,73 +10,73 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - {57050e42-039e-409c-83e7-1cc43de721bf} + {0c1dd2bf-04b0-4605-b963-b187e900b4a1} - {7196f6a0-ca4c-4ec0-aaae-b4d130093042} + {5c101216-8e22-4ad4-a27d-1572a4d95947} - {81a47e51-ed9b-484b-b8c0-2d089333846a} + {eb7a158f-f532-4db5-9314-fca01f04a38e} - {05094067-abbe-4828-ba59-cb4b6d904d42} + {c378fbaa-2232-4123-a58d-84f2e2e32376} - {79ed96f3-5cb6-4e5d-b13a-477c45dedf2a} + {be34fc66-075c-45f0-a292-e4ace73a5726} - {dcdcdac7-addc-4ff4-a728-b47992b8865f} + {8dcfacbc-454b-4530-af61-d9db5aebb5bd} - {00f82410-3b82-4104-8a8e-2fab52718f83} + {3fa4b52a-1b33-4e8c-8e0c-5818dbd5de15} - {ffcc04a7-82d0-4107-96eb-18fb7bcc3dcb} + {50cbaa98-dc12-4cc9-9ba6-4e08e885c924} - {2c7fa1c0-770e-45e5-87fc-31747131fe6c} + {889b7953-c303-448a-8cf9-ca624011d131} - {1b2c7b1c-9062-4f04-b234-7015a267696b} + {9313323e-633a-4c87-812c-0dda81b2a509} - {b2a07d02-27fb-42b7-9781-3372b03e1a49} + {61f1ea93-c0b0-4296-81fa-808116542dc8} - {84f254f6-92fc-4734-b767-ea8b92970e16} + {61efdcbf-e2d5-477f-8d67-a81c5ab3d6e6} - {0231c881-e8fc-4372-a820-a2af33f63ad2} + {86f6a355-fd04-4ba5-82b0-0fb57897da8d} - {4ec2206a-9706-40d1-af41-5fc270046963} + {dacfe6a2-94db-4257-8727-0f9a8c70fc8f} - {97f08ad3-5335-411b-9718-4862e80ccde9} + {b41f78d8-61f8-4476-86cf-d08080418dcb} - {266dfb32-2e86-4fb8-b20e-17351fb66385} + {78f41ac0-8ed5-4951-8373-2aed18d9c741} - {6eb82dc3-52a0-437f-9ed9-2af6acdfcaff} + {fc1463dd-e130-49cb-96cf-ee6755cf2de1} - {78a8ea69-767d-40a9-b2bc-2385ca9b3184} + {a50ba11b-40c8-43ff-aa5b-aac2e4285df0} - {3db4dd37-ed4c-4237-8c87-3174ab508e2b} + {37df6f7f-93ff-4c4a-89cf-77bb6feb215f} - {bbd7243f-5abd-411e-82f8-11b4d00d4c42} + {3d8e534e-8cd7-4dc5-9626-a1ce9559c118} - {04cebff1-aaff-4550-8545-603c7bb28662} + {4419315f-3ef3-490d-ba0a-df22565b9a02} - {914562fa-6de0-4ebd-9544-6d2f1ab30b2f} + {3491f495-c715-4a5b-845e-1638a4ef0127} - {300d9995-b846-4b76-9a91-9600805ef621} + {4bb6b059-2004-4795-a0e7-d401affb3063} @@ -513,7 +513,7 @@ Source Files\Bindings - + Source Files\External @@ -1474,7 +1474,7 @@ Source Files\Bindings - + Source Files\External diff --git a/VC2013/ToLua.vcxproj b/VC2013/ToLua.vcxproj index 4f4b8875d..c798af657 100644 --- a/VC2013/ToLua.vcxproj +++ b/VC2013/ToLua.vcxproj @@ -84,7 +84,7 @@ Disabled - ../tolua++-1.0.93/include;../lua-5.1.4/src;%(AdditionalIncludeDirectories) + ../lib/tolua++/include;../lib/lua/src;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks @@ -98,7 +98,7 @@ MaxSpeed true - ../tolua++-1.0.93/include;../lua-5.1.4/src;%(AdditionalIncludeDirectories) + ../lib/tolua++/include;../lib/lua/src;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreaded true @@ -111,7 +111,7 @@ MaxSpeed true - ../tolua++-1.0.93/include;../lua-5.1.4/src;%(AdditionalIncludeDirectories) + ../lib/tolua++/include;../lib/lua/src;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreaded true @@ -123,7 +123,7 @@ Disabled - ../tolua++-1.0.93/include;../lua-5.1.4/src;%(AdditionalIncludeDirectories) + ../lib/tolua++/include;../lib/lua/src;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks diff --git a/lib/tolua++/include/tolua++.h b/lib/tolua++/include/tolua++.h index a60dde635..3ebd8d246 100644 --- a/lib/tolua++/include/tolua++.h +++ b/lib/tolua++/include/tolua++.h @@ -43,7 +43,7 @@ extern "C" { typedef int lua_Object; -#include "../../lua/src/lua.h +#include "../../lua/src/lua.h" #include "../../lua/src/lauxlib.h" struct tolua_Error -- cgit v1.2.3 From 686e0c12e2fa1b8d8e0970306e57cbcfa247f5f5 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Tue, 26 Nov 2013 15:37:15 +0100 Subject: cWorld::SpawnExperienceOrb() now returns the entity ID of the spawned orb. Documented etExpOrb. --- MCServer/Plugins/APIDump/APIDesc.lua | 3 ++- source/Bindings.cpp | 7 ++++--- source/Bindings.h | 2 +- source/World.cpp | 3 ++- source/World.h | 4 ++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 9f558f58c..6545c1f9f 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -769,6 +769,7 @@ end { etBoat = { Notes = "The entity is a {{cBoat}}" }, etEntity = { Notes = "No further specialization available" }, + etExpOrb = { Notes = "The entity is a {{cExpOrb}}" }, etFallingBlock = { Notes = "The entity is a {{cFallingBlock}}" }, etMob = { Notes = "The entity is a {{cMonster}} descendant" }, etMonster = { Notes = "The entity is a {{cMonster}} descendant" }, @@ -2075,7 +2076,7 @@ end { Params = "{{cItems|Pickups}}, X, Y, Z, SpeedX, SpeedY, SpeedZ", Return = "", Notes = "Spawns the specified pickups at the position specified. All the pickups fly away from the spawn position using the specified speed." }, }, SpawnMob = { Params = "X, Y, Z, {{cMonster|MonsterType}}", Return = "EntityID", Notes = "Spawns the specified type of mob at the specified coords. Returns the EntityID of the creates entity, or -1 on failure. " }, - SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "", Notes = "Spawns an {{cExpOrb|experience orb at the specified coords, with the given reward" }, + SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "EntityID", Notes = "Spawns an {{cExpOrb|experience orb}} at the specified coords, with the given reward" }, SpawnPrimedTNT = { Params = "X, Y, Z, FuseTimeSecs, InitialVelocityCoeff", Return = "", Notes = "Spawns a {{cTNTEntity|primed TNT entity}} at the specified coords, with the given fuse time. The entity gets a random speed multiplied by the InitialVelocityCoeff, 1 being the default value." }, TryGetHeight = { Params = "BlockX, BlockZ", Return = "IsValid, Height", Notes = "Returns true and height of the highest non-air block if the chunk is loaded, or false otherwise." }, UnloadUnusedChunks = { Params = "", Return = "", Notes = "Unloads chunks that are no longer needed, and are saved. NOTE: This API is deprecated and will be removed soon." }, diff --git a/source/Bindings.cpp b/source/Bindings.cpp index 7bea672da..62a16b38b 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 11/25/13 21:47:32. +** Generated automatically by tolua++-1.0.92 on 11/26/13 15:29:19. */ #ifndef __cplusplus @@ -12616,10 +12616,11 @@ static int tolua_AllToLua_cWorld_SpawnExperienceOrb00(lua_State* tolua_S) if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnExperienceOrb'", NULL); #endif { - self->SpawnExperienceOrb(a_X,a_Y,a_Z,a_Reward); + int tolua_ret = (int) self->SpawnExperienceOrb(a_X,a_Y,a_Z,a_Reward); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); } } - return 0; + return 1; #ifndef TOLUA_RELEASE tolua_lerror: tolua_error(tolua_S,"#ferror in function 'SpawnExperienceOrb'.",&tolua_err); diff --git a/source/Bindings.h b/source/Bindings.h index 8d20bd0dc..240ceec30 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 11/25/13 21:47:33. +** Generated automatically by tolua++-1.0.92 on 11/26/13 15:29:20. */ /* Exported function */ diff --git a/source/World.cpp b/source/World.cpp index 432ab32e9..de64b1c20 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -1562,10 +1562,11 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double -void cWorld::SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward) +int cWorld::SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward) { cExpOrb * ExpOrb = new cExpOrb(a_X, a_Y, a_Z, a_Reward); ExpOrb->Initialize(this); + return ExpOrb->GetUniqueID(); } diff --git a/source/World.h b/source/World.h index 9397f8b75..284e96bbb 100644 --- a/source/World.h +++ b/source/World.h @@ -353,8 +353,8 @@ public: /// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified: void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false); - /// Spawns an experience orb at the given location with the given reward. - void SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward); + /// Spawns an experience orb at the given location with the given reward. It returns the UniqueID of the spawned experience orb. + int SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward); /// Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff = 1); -- cgit v1.2.3 From f792117ce58c11d110bd3399d1a1e4c90ba61c6f Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 26 Nov 2013 17:04:07 +0000 Subject: Fixed a mis-include --- src/WebAdmin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebAdmin.h b/src/WebAdmin.h index 0bceb9f82..c981d6b06 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -7,7 +7,7 @@ #include "OSSupport/Socket.h" #include "LuaState.h" -#include "../lib/iniFile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "HTTPServer/HTTPServer.h" #include "HTTPServer/HTTPFormParser.h" -- cgit v1.2.3 From 66482c9db7a74ea626ce54405fd7f36184dd1e97 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 26 Nov 2013 17:06:00 +0000 Subject: Fixed another one. --- src/World.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/World.cpp b/src/World.cpp index 96111ab57..96b08a8ec 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -8,7 +8,7 @@ #include "Server.h" #include "Item.h" #include "Root.h" -#include "../lib/iniFile/iniFile.h" +#include "inifile/iniFile.h" #include "ChunkMap.h" #include "OSSupport/Timer.h" -- cgit v1.2.3 From c775011c98cc8061095d85a3eb9b6d27bdea4881 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 26 Nov 2013 17:06:26 +0000 Subject: Fixed another. --- src/WebAdmin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebAdmin.h b/src/WebAdmin.h index c981d6b06..a775acec2 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -7,7 +7,7 @@ #include "OSSupport/Socket.h" #include "LuaState.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "HTTPServer/HTTPServer.h" #include "HTTPServer/HTTPFormParser.h" -- cgit v1.2.3 From a6b67e9cc1277b326037b986ef939896eac7ee90 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 26 Nov 2013 17:06:58 +0000 Subject: And another. --- src/Authenticator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Authenticator.cpp b/src/Authenticator.cpp index 3371c2a1a..4d9e90a01 100644 --- a/src/Authenticator.cpp +++ b/src/Authenticator.cpp @@ -6,7 +6,7 @@ #include "Root.h" #include "Server.h" -#include "../lib/iniFile/iniFile.h" +#include ".inifile/iniFile.h" #include -- cgit v1.2.3 From 0b153f62f04db538d26f27fb280bd9a5dd93d3e4 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 26 Nov 2013 17:09:04 +0000 Subject: Another --- src/Item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Item.cpp b/src/Item.cpp index 219edb128..37277e8c8 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -2,7 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Item.h" -#include "../lib/jsoncpp/include/json/json.h" +#include "jsoncpp/include/json/json.h" #include "Items/ItemHandler.h" -- cgit v1.2.3 From 770ef8c60b1adbbf73a091a0c8225366c32f90b5 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 26 Nov 2013 17:14:46 +0000 Subject: Fixed loads more of them. --- src/BlockID.cpp | 2 +- src/Chunk.cpp | 2 +- src/GroupManager.cpp | 2 +- src/Inventory.cpp | 2 +- src/LuaState.cpp | 2 +- src/LuaState.h | 2 +- src/MonsterConfig.cpp | 2 +- src/RCONServer.cpp | 2 +- src/Server.h | 4 ++-- src/StringCompression.h | 2 +- src/tolua++.h | 4 ++-- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/BlockID.cpp b/src/BlockID.cpp index 003f49848..708618d25 100644 --- a/src/BlockID.cpp +++ b/src/BlockID.cpp @@ -4,7 +4,7 @@ #include "Globals.h" #include "BlockID.h" -#include "../lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "Item.h" #include "Mobs/Monster.h" diff --git a/src/Chunk.cpp b/src/Chunk.cpp index ec60a19bf..83f866e56 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -10,7 +10,7 @@ #include "World.h" #include "ClientHandle.h" #include "Server.h" -#include "../lib/zlib/zlib.h" +#include "zlib/zlib.h" #include "Defines.h" #include "BlockEntities/ChestEntity.h" #include "BlockEntities/DispenserEntity.h" diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp index 55065b38e..df609f05b 100644 --- a/src/GroupManager.cpp +++ b/src/GroupManager.cpp @@ -2,7 +2,7 @@ #include "GroupManager.h" #include "Group.h" -#include "../lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "ChatColor.h" #include "Root.h" diff --git a/src/Inventory.cpp b/src/Inventory.cpp index 3939598d7..b9c7f4aaf 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -9,7 +9,7 @@ #include "Root.h" #include "World.h" -#include "../lib/jsoncpp/include/json/json.h" +#include "jsoncpp/include/json/json.h" #include "Items/ItemHandler.h" diff --git a/src/LuaState.cpp b/src/LuaState.cpp index 3a0008d4f..6be1ee58c 100644 --- a/src/LuaState.cpp +++ b/src/LuaState.cpp @@ -8,7 +8,7 @@ extern "C" { - #include"lib/lua/src/lualib.h" + #include "lua/src/lualib.h" } #include "tolua++.h" diff --git a/src/LuaState.h b/src/LuaState.h index 9a486bfd1..5e7f3d180 100644 --- a/src/LuaState.h +++ b/src/LuaState.h @@ -26,7 +26,7 @@ automatic resource management. extern "C" { - #include "../lib/lua/src/lauxlib.h" + #include "lua/src/lauxlib.h" } diff --git a/src/MonsterConfig.cpp b/src/MonsterConfig.cpp index b72324124..6165606f0 100644 --- a/src/MonsterConfig.cpp +++ b/src/MonsterConfig.cpp @@ -3,7 +3,7 @@ #include "MonsterConfig.h" #include "Mobs/Monster.h" -#include "../lib/inifile/iniFile.h" +#include "inifile/iniFile.h" diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index f90e9f191..72f2d9ba9 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -4,7 +4,7 @@ // Implements the cRCONServer class representing the RCON server #include "Globals.h" -#include "../lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "RCONServer.h" #include "Server.h" #include "Root.h" diff --git a/src/Server.h b/src/Server.h index 93a978654..0d93469a5 100644 --- a/src/Server.h +++ b/src/Server.h @@ -11,8 +11,8 @@ #include "OSSupport/SocketThreads.h" #include "OSSupport/ListenThread.h" -#include "../lib/cryptopp/rsa.h" -#include "../lib/cryptopp/randpool.h" +#include "cryptopp/rsa.h" +#include "cryptopp/randpool.h" #include "RCONServer.h" diff --git a/src/StringCompression.h b/src/StringCompression.h index 1257d7d6b..459e8f568 100644 --- a/src/StringCompression.h +++ b/src/StringCompression.h @@ -3,7 +3,7 @@ // Interfaces to the wrapping functions for compression and decompression using AString as their data -#include "../lib/zlib/zlib.h" // Needed for the Z_XXX return values +#include "zlib/zlib.h" // Needed for the Z_XXX return values diff --git a/src/tolua++.h b/src/tolua++.h index dff1e91be..8da427fe3 100644 --- a/src/tolua++.h +++ b/src/tolua++.h @@ -43,8 +43,8 @@ extern "C" { typedef int lua_Object; -#include "../lib/lua/src/lua.h" -#include "../lib/lua/src/lauxlib.h" +#include "lua/src/lua.h" +#include "lua/src/lauxlib.h" struct tolua_Error { -- cgit v1.2.3 From 1c526ed52b0b771950804959bb890970e561d74b Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 26 Nov 2013 22:11:40 +0000 Subject: Fixed tolua++ files --- src/AllToLua.pkg | 2 +- src/Bindings.cpp | 4 ++-- src/Bindings.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AllToLua.pkg b/src/AllToLua.pkg index ee594be1a..fe9217104 100644 --- a/src/AllToLua.pkg +++ b/src/AllToLua.pkg @@ -15,7 +15,7 @@ typedef unsigned short UInt16; $cfile "ChunkDef.h" -$cfile "../iniFile/iniFile.h" +$cfile "../lib/inifile/iniFile.h" $cfile "OSSupport/File.h" diff --git a/src/Bindings.cpp b/src/Bindings.cpp index 7fc9be379..942ea0eaf 100644 --- a/src/Bindings.cpp +++ b/src/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 11/26/13 15:29:19. +** Generated automatically by tolua++-1.0.92 on 11/26/13 22:11:10. */ #ifndef __cplusplus @@ -16,7 +16,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "tolua_base.h" #include "ChunkDef.h" -#include "lib/iniFile/iniFile.h" +#include "../lib/inifile/iniFile.h" #include "OSSupport/File.h" #include "BlockID.h" #include "StringUtils.h" diff --git a/src/Bindings.h b/src/Bindings.h index 240ceec30..d141484db 100644 --- a/src/Bindings.h +++ b/src/Bindings.h @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 11/26/13 15:29:20. +** Generated automatically by tolua++-1.0.92 on 11/26/13 22:11:11. */ /* Exported function */ -- cgit v1.2.3 From 2dee812667e1f86f65ad25430fae17e23baa784c Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 26 Nov 2013 22:37:44 +0000 Subject: Possibly fixed Linux makefile --- GNUmakefile | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 2ddfefee9..981809ffd 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -47,8 +47,14 @@ ifeq ($(release),1) # release build - fastest run-time, no gdb support ################ -CC_OPTIONS = -g -Ofast -DNDEBUG -CXX_OPTIONS = -g -Ofast -DNDEBUG +ifeq ($(disableasm),1) + CC_OPTIONS = -g -O3 -DNDEBUG + CXX_OPTIONS = -g -O3 -DNDEBUG +else + CC_OPTIONS = -g -Ofast -DNDEBUG + CXX_OPTIONS = -g -Ofast -DNDEBUG +endif + LNK_OPTIONS = -pthread -Ofast BUILDDIR = build/release/ @@ -58,8 +64,14 @@ ifeq ($(profile),1) # profile build - a release build with symbols and profiling engine built in ################ -CC_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG -CXX_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG +ifeq ($(disableasm),1) + CC_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG + CXX_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG +else + CC_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG + CXX_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG +endif + LNK_OPTIONS = -pthread -ggdb -Ofast -pg BUILDDIR = build/profile/ @@ -88,6 +100,7 @@ ifeq ($(shell $(CXX) --version 2>&1 | grep -i -c "clang version"),0) CC_OPTIONS += -Wno-tautological-compare CXX_OPTIONS += -Wno-tautological-compare disableasm = 1 +disableofast = 1 endif @@ -141,7 +154,7 @@ INCLUDE = -I.\ -Isrc\ -Isrc/items\ -Isrce/blocks\ - -Ilib + -Ilib\ -- cgit v1.2.3 From f87bb1619e7cd438913d5c203183ca35f23928ac Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 26 Nov 2013 22:43:22 +0000 Subject: Fixed Linux compile again --- GNUmakefile | 4 ++-- src/Server.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 981809ffd..1e09ade98 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -47,7 +47,7 @@ ifeq ($(release),1) # release build - fastest run-time, no gdb support ################ -ifeq ($(disableasm),1) +ifeq ($(disableofast),1) CC_OPTIONS = -g -O3 -DNDEBUG CXX_OPTIONS = -g -O3 -DNDEBUG else @@ -64,7 +64,7 @@ ifeq ($(profile),1) # profile build - a release build with symbols and profiling engine built in ################ -ifeq ($(disableasm),1) +ifeq ($(disableofast),1) CC_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG CXX_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG else diff --git a/src/Server.cpp b/src/Server.cpp index 5f0e007d7..d889abc53 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -23,7 +23,7 @@ #include "MersenneTwister.h" -#include "lib/iniFile/iniFile.h" +#include "../lib/iniFile/iniFile.h" #include "Vector3f.h" #include -- cgit v1.2.3 From 3e3e63d94663913be2b08b6d1a37b59b8d9ea3ac Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 26 Nov 2013 22:45:08 +0000 Subject: Compile fix again --- src/Server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server.cpp b/src/Server.cpp index d889abc53..183a0648f 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -23,7 +23,7 @@ #include "MersenneTwister.h" -#include "../lib/iniFile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "Vector3f.h" #include -- cgit v1.2.3 From 2ef9cf700d19f1a272c632ed4814efd254550829 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 26 Nov 2013 22:50:33 +0000 Subject: Hopefully fixed last of the Linux compile errors --- src/Entities/Player.cpp | 2 +- src/Generating/ChunkGenerator.cpp | 2 +- src/Generating/ComposableGenerator.cpp | 2 +- src/PluginManager.cpp | 2 +- src/Root.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index f4ddd979c..bc01ba716 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -24,7 +24,7 @@ #include "../Vector3d.h" #include "../Vector3f.h" -#include "lib/iniFile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "lib/jsoncpp/include/json/json.h" #define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index cb993bb05..82ca55388 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -3,7 +3,7 @@ #include "ChunkGenerator.h" #include "../World.h" -#include "lib/iniFile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "../Root.h" #include "../PluginManager.h" #include "ChunkDesc.h" diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 496ada52d..c7875b5a5 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -7,7 +7,7 @@ #include "ComposableGenerator.h" #include "../World.h" -#include "lib/iniFile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "../Root.h" // Individual composed algorithms: diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index f9d8d7d02..1856ce6af 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -10,7 +10,7 @@ #include "Server.h" #include "CommandOutput.h" -#include "lib/iniFile/iniFile.h" +#include "lib/inifile/iniFile.h" #include "tolua++.h" #include "Entities/Player.h" diff --git a/src/Root.cpp b/src/Root.cpp index 282227525..13e0cc9cb 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -19,7 +19,7 @@ #include "DeadlockDetect.h" #include "OSSupport/Timer.h" -#include "lib/iniFile/iniFile.h" +#include "lib/inifile/iniFile.h" #ifdef _WIN32 #include -- cgit v1.2.3 From e1f3cb18797887aeaa288fe282928be959e0599f Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 26 Nov 2013 22:56:35 +0000 Subject: ANOTHER compile fix This one was broken by bear. :P --- src/Authenticator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Authenticator.cpp b/src/Authenticator.cpp index 4d9e90a01..4147e1eb2 100644 --- a/src/Authenticator.cpp +++ b/src/Authenticator.cpp @@ -6,7 +6,7 @@ #include "Root.h" #include "Server.h" -#include ".inifile/iniFile.h" +#include "lib/inifile/iniFile.h" #include -- cgit v1.2.3 From b358032fe9095ddd92df4dd02dbab978ee408eb4 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 26 Nov 2013 23:15:32 +0000 Subject: Makefile fix --- GNUmakefile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 1e09ade98..f36ffe8bb 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -50,12 +50,12 @@ ifeq ($(release),1) ifeq ($(disableofast),1) CC_OPTIONS = -g -O3 -DNDEBUG CXX_OPTIONS = -g -O3 -DNDEBUG + LNK_OPTIONS = -pthread -O3 else CC_OPTIONS = -g -Ofast -DNDEBUG CXX_OPTIONS = -g -Ofast -DNDEBUG + LNK_OPTIONS = -pthread -Ofast endif - -LNK_OPTIONS = -pthread -Ofast BUILDDIR = build/release/ else @@ -67,12 +67,13 @@ ifeq ($(profile),1) ifeq ($(disableofast),1) CC_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG CXX_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG + LNK_OPTIONS = -pthread -ggdb -O3 -pg else CC_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG CXX_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG + LNK_OPTIONS = -pthread -ggdb -Ofast -pg endif -LNK_OPTIONS = -pthread -ggdb -Ofast -pg BUILDDIR = build/profile/ else @@ -81,7 +82,12 @@ else # Since C code is used only for supporting libraries (zlib, lua), it is still Ofast-optimized ################ -CC_OPTIONS = -s -ggdb -g -D_DEBUG -Ofast +ifeq ($(disableofast),1) + CC_OPTIONS = -s -ggdb -g -D_DEBUG -O3 +else + CC_OPTIONS = -s -ggdb -g -D_DEBUG -Ofast +endif + CXX_OPTIONS = -s -ggdb -g -D_DEBUG -Og LNK_OPTIONS = -pthread -g -ggdb -Og BUILDDIR = build/debug/ -- cgit v1.2.3 From 83d582d083c92e51a85fa825907493aebb866af7 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Wed, 27 Nov 2013 07:19:12 +0000 Subject: Removed a -Ofast bit. --- GNUmakefile | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index f36ffe8bb..a963da098 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -47,15 +47,10 @@ ifeq ($(release),1) # release build - fastest run-time, no gdb support ################ -ifeq ($(disableofast),1) - CC_OPTIONS = -g -O3 -DNDEBUG - CXX_OPTIONS = -g -O3 -DNDEBUG - LNK_OPTIONS = -pthread -O3 -else - CC_OPTIONS = -g -Ofast -DNDEBUG - CXX_OPTIONS = -g -Ofast -DNDEBUG - LNK_OPTIONS = -pthread -Ofast -endif +CC_OPTIONS = -g -O3 -DNDEBUG +CXX_OPTIONS = -g -O3 -DNDEBUG +LNK_OPTIONS = -pthread -O3 + BUILDDIR = build/release/ else @@ -64,15 +59,9 @@ ifeq ($(profile),1) # profile build - a release build with symbols and profiling engine built in ################ -ifeq ($(disableofast),1) - CC_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG - CXX_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG - LNK_OPTIONS = -pthread -ggdb -O3 -pg -else - CC_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG - CXX_OPTIONS = -s -g -ggdb -Ofast -pg -DNDEBUG - LNK_OPTIONS = -pthread -ggdb -Ofast -pg -endif +CC_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG +CXX_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG +LNK_OPTIONS = -pthread -ggdb -O3 -pg BUILDDIR = build/profile/ @@ -82,11 +71,7 @@ else # Since C code is used only for supporting libraries (zlib, lua), it is still Ofast-optimized ################ -ifeq ($(disableofast),1) CC_OPTIONS = -s -ggdb -g -D_DEBUG -O3 -else - CC_OPTIONS = -s -ggdb -g -D_DEBUG -Ofast -endif CXX_OPTIONS = -s -ggdb -g -D_DEBUG -Og LNK_OPTIONS = -pthread -g -ggdb -Og -- cgit v1.2.3 From 3b790bbf67861af40947bbb00725fdb22151b61a Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Wed, 27 Nov 2013 07:27:19 +0000 Subject: Fixed some of tiger's derpyness. --- src/Authenticator.cpp | 2 +- src/Bindings.cpp | 2 +- src/Chunk.cpp | 2 +- src/Entities/Player.cpp | 4 ++-- src/HTTPServer/HTTPServer.h | 2 +- src/LuaWindow.cpp | 2 +- src/ManualBindings.cpp | 2 +- src/Protocol/Protocol132.h | 4 ++-- src/Root.cpp | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Authenticator.cpp b/src/Authenticator.cpp index 4147e1eb2..4cbe3beed 100644 --- a/src/Authenticator.cpp +++ b/src/Authenticator.cpp @@ -6,7 +6,7 @@ #include "Root.h" #include "Server.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include diff --git a/src/Bindings.cpp b/src/Bindings.cpp index 942ea0eaf..f06fb4c03 100644 --- a/src/Bindings.cpp +++ b/src/Bindings.cpp @@ -16,7 +16,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "tolua_base.h" #include "ChunkDef.h" -#include "../lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "OSSupport/File.h" #include "BlockID.h" #include "StringUtils.h" diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 83f866e56..c4aafaa8f 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -34,7 +34,7 @@ #include "MobSpawner.h" -#include "lib/jsoncpp/include/json/json.h" +#include "jsoncpp/include/json/json.h" diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index bc01ba716..a27405839 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -24,8 +24,8 @@ #include "../Vector3d.h" #include "../Vector3f.h" -#include "lib/inifile/iniFile.h" -#include "lib/jsoncpp/include/json/json.h" +#include "inifile/iniFile.h" +#include "jsoncpp/include/json/json.h" #define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h index ed7a03519..24baf8c95 100644 --- a/src/HTTPServer/HTTPServer.h +++ b/src/HTTPServer/HTTPServer.h @@ -11,7 +11,7 @@ #include "../OSSupport/ListenThread.h" #include "../OSSupport/SocketThreads.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" diff --git a/src/LuaWindow.cpp b/src/LuaWindow.cpp index f49ba2109..5f112f1e1 100644 --- a/src/LuaWindow.cpp +++ b/src/LuaWindow.cpp @@ -8,7 +8,7 @@ #include "UI/SlotArea.h" #include "PluginLua.h" #include "Entities/Player.h" -#include "lib/lua/src/lauxlib.h" // Needed for LUA_REFNIL +#include "lua/src/lauxlib.h" // Needed for LUA_REFNIL diff --git a/src/ManualBindings.cpp b/src/ManualBindings.cpp index 7ea382b76..02b3347f6 100644 --- a/src/ManualBindings.cpp +++ b/src/ManualBindings.cpp @@ -17,7 +17,7 @@ #include "BlockEntities/DropperEntity.h" #include "BlockEntities/FurnaceEntity.h" #include "BlockEntities/HopperEntity.h" -#include "lib/md5/md5.h" +#include "md5/md5.h" #include "LuaWindow.h" #include "LineBlockTracer.h" diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h index 2023d7960..f76272b8d 100644 --- a/src/Protocol/Protocol132.h +++ b/src/Protocol/Protocol132.h @@ -10,8 +10,8 @@ #pragma once #include "Protocol125.h" -#include "lib/cryptopp/modes.h" -#include "lib/cryptopp/aes.h" +#include "cryptopp/modes.h" +#include "cryptopp/aes.h" diff --git a/src/Root.cpp b/src/Root.cpp index 13e0cc9cb..4a6abaf37 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -19,7 +19,7 @@ #include "DeadlockDetect.h" #include "OSSupport/Timer.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #ifdef _WIN32 #include -- cgit v1.2.3 From d925cf4def76d6db05a46d3ead77e6c76cfdd268 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Wed, 27 Nov 2013 07:40:59 +0000 Subject: Fixed the remaining derps --- src/BlockEntities/ChestEntity.cpp | 2 +- src/BlockEntities/FurnaceEntity.cpp | 2 +- src/BlockEntities/JukeboxEntity.cpp | 2 +- src/BlockEntities/NoteEntity.cpp | 2 +- src/BlockEntities/SignEntity.cpp | 2 +- src/ChunkMap.cpp | 4 ++-- src/Generating/BioGen.cpp | 2 +- src/Generating/ChunkGenerator.cpp | 2 +- src/Generating/CompoGen.cpp | 2 +- src/Generating/ComposableGenerator.cpp | 2 +- src/Generating/DistortedHeightmap.cpp | 2 +- src/Generating/EndGen.cpp | 2 +- src/Generating/HeiGen.cpp | 2 +- src/Generating/Noise3DGenerator.cpp | 2 +- src/OSSupport/GZipFile.h | 2 +- src/PluginLua.cpp | 2 +- src/PluginManager.cpp | 2 +- src/Protocol/ChunkDataSerializer.cpp | 2 +- src/Protocol/Protocol132.cpp | 2 +- src/Protocol/Protocol14x.cpp | 2 +- src/Protocol/Protocol17x.h | 4 ++-- src/Server.cpp | 4 ++-- src/Simulator/Simulator.h | 2 +- src/WorldStorage/WSSAnvil.cpp | 2 +- src/WorldStorage/WSSCompact.cpp | 4 ++-- src/WorldStorage/WorldStorage.h | 2 +- 26 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp index 0bde5c9f2..918a1df2d 100644 --- a/src/BlockEntities/ChestEntity.cpp +++ b/src/BlockEntities/ChestEntity.cpp @@ -5,7 +5,7 @@ #include "../Item.h" #include "../Entities/Player.h" #include "../UI/Window.h" -#include "lib/jsoncpp/include/json/json.h" +#include "jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index d49e4f53b..0123709bc 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -6,7 +6,7 @@ #include "../Entities/Player.h" #include "../Root.h" #include "../Chunk.h" -#include "lib/jsoncpp/include/json/json.h" +#include "jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/JukeboxEntity.cpp b/src/BlockEntities/JukeboxEntity.cpp index 8e90ee70d..b01d01391 100644 --- a/src/BlockEntities/JukeboxEntity.cpp +++ b/src/BlockEntities/JukeboxEntity.cpp @@ -3,7 +3,7 @@ #include "JukeboxEntity.h" #include "../World.h" -#include "lib/jsoncpp/include/json/json.h" +#include "jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp index 493316bce..0bae15777 100644 --- a/src/BlockEntities/NoteEntity.cpp +++ b/src/BlockEntities/NoteEntity.cpp @@ -3,7 +3,7 @@ #include "NoteEntity.h" #include "../World.h" -#include "lib/jsoncpp/include/json/json.h" +#include "jsoncpp/include/json/json.h" diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp index 39fcb6efc..2e1fc1f8c 100644 --- a/src/BlockEntities/SignEntity.cpp +++ b/src/BlockEntities/SignEntity.cpp @@ -4,7 +4,7 @@ // Implements the cSignEntity class representing a single sign in the world #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "lib/jsoncpp/include/json/json.h" +#include "jsoncpp/include/json/json.h" #include "SignEntity.h" #include "../Entities/Player.h" diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 5937ee2e4..c58b6b2fa 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -20,8 +20,8 @@ #include // abs #endif -#include "lib/zlib/zlib.h" -#include "lib/jsoncpp/include/json/json.h" +#include "zlib/zlib.h" +#include "jsoncpp/include/json/json.h" diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 4dff50918..1cd7c70e7 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include "BioGen.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "../LinearUpscale.h" diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index 82ca55388..f28504441 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -3,7 +3,7 @@ #include "ChunkGenerator.h" #include "../World.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "../Root.h" #include "../PluginManager.h" #include "ChunkDesc.h" diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp index 7e1ade147..03a65a457 100644 --- a/src/Generating/CompoGen.cpp +++ b/src/Generating/CompoGen.cpp @@ -12,7 +12,7 @@ #include "../BlockID.h" #include "../Item.h" #include "../LinearUpscale.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index c7875b5a5..01070963c 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -7,7 +7,7 @@ #include "ComposableGenerator.h" #include "../World.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "../Root.h" // Individual composed algorithms: diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index 6fbe7c1d4..95ea812fa 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -7,7 +7,7 @@ #include "DistortedHeightmap.h" #include "../OSSupport/File.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "../LinearUpscale.h" diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp index 121ed4488..1fa0fa121 100644 --- a/src/Generating/EndGen.cpp +++ b/src/Generating/EndGen.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include "EndGen.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "../LinearUpscale.h" diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index 9cc1fbbaa..8aab3fe15 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -6,7 +6,7 @@ #include "Globals.h" #include "HeiGen.h" #include "../LinearUpscale.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index 9760ca4ca..0c68957c0 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -6,7 +6,7 @@ #include "Globals.h" #include "Noise3DGenerator.h" #include "../OSSupport/File.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "../LinearInterpolation.h" #include "../LinearUpscale.h" diff --git a/src/OSSupport/GZipFile.h b/src/OSSupport/GZipFile.h index dcc171a2a..dfb4e8c31 100644 --- a/src/OSSupport/GZipFile.h +++ b/src/OSSupport/GZipFile.h @@ -9,7 +9,7 @@ #pragma once -#include "lib/zlib/zlib.h" +#include "zlib/zlib.h" diff --git a/src/PluginLua.cpp b/src/PluginLua.cpp index 0afd3844a..4ddf191ac 100644 --- a/src/PluginLua.cpp +++ b/src/PluginLua.cpp @@ -11,7 +11,7 @@ extern "C" { - #include"lib/lua/src/lualib.h" + #include "lua/src/lualib.h" } #include "tolua++.h" diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index 1856ce6af..bb6f2a24b 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -10,7 +10,7 @@ #include "Server.h" #include "CommandOutput.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "tolua++.h" #include "Entities/Player.h" diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index d406b8b51..78318a5ee 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -7,7 +7,7 @@ #include "Globals.h" #include "ChunkDataSerializer.h" -#include "lib/zlib/zlib.h" +#include "zlib/zlib.h" diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index 8f7891e4b..d8f450588 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include "ChunkDataSerializer.h" -#include "lib/cryptopp/randpool.h" +#include "cryptopp/randpool.h" #include "Protocol132.h" #include "../Root.h" #include "../Server.h" diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp index ea406d7a2..28122034c 100644 --- a/src/Protocol/Protocol14x.cpp +++ b/src/Protocol/Protocol14x.cpp @@ -16,7 +16,7 @@ Implements the 1.4.x protocol classes representing these protocols: #include "../Root.h" #include "../Server.h" #include "../ClientHandle.h" -#include "lib/cryptopp/randpool.h" +#include "cryptopp/randpool.h" #include "../Item.h" #include "ChunkDataSerializer.h" #include "../Entities/Player.h" diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 6b897fd57..9c1eaa99a 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -16,8 +16,8 @@ Declares the 1.7.x protocol classes: #include "Protocol.h" #include "../ByteBuffer.h" -#include "lib/cryptopp/modes.h" -#include "lib/cryptopp/aes.h" +#include "cryptopp/modes.h" +#include "cryptopp/aes.h" diff --git a/src/Server.cpp b/src/Server.cpp index 183a0648f..5951dc5b5 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -23,7 +23,7 @@ #include "MersenneTwister.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" #include "Vector3f.h" #include @@ -31,7 +31,7 @@ #include extern "C" { - #include "lib/zlib/zlib.h" + #include "zlib/zlib.h" } diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h index 4f555693d..5cd0e8657 100644 --- a/src/Simulator/Simulator.h +++ b/src/Simulator/Simulator.h @@ -2,7 +2,7 @@ #pragma once #include "../Vector3i.h" -#include "lib/inifile/iniFile.h" +#include "inifile/iniFile.h" diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 48163e222..79be4dfa2 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -7,7 +7,7 @@ #include "WSSAnvil.h" #include "NBTChunkSerializer.h" #include "../World.h" -#include "lib/zlib/zlib.h" +#include "zlib/zlib.h" #include "../BlockID.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DispenserEntity.h" diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp index dafa4d1b5..ced84836f 100644 --- a/src/WorldStorage/WSSCompact.cpp +++ b/src/WorldStorage/WSSCompact.cpp @@ -6,8 +6,8 @@ #include "Globals.h" #include "WSSCompact.h" #include "../World.h" -#include "lib/zlib/zlib.h" -#include "lib/jsoncpp/include/json/json.h" +#include "zlib/zlib.h" +#include "jsoncpp/include/json/json.h" #include "../StringCompression.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DispenserEntity.h" diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index b2c6dfb4b..720c04759 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -16,7 +16,7 @@ #include "../ChunkDef.h" #include "../OSSupport/IsThread.h" -#include "lib/jsoncpp/include/json/json.h" +#include "jsoncpp/include/json/json.h" -- cgit v1.2.3 From 2113ca384450eb0155c43452690bda08b62cb6aa Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 27 Nov 2013 09:17:25 +0100 Subject: Fixed VC2008 compilation, normalized include paths. --- GNUmakefile | 3 +-- VC2008/MCServer.vcproj | 26 +++++++++++++------------- lib/luaexpat/lxplib.c | 6 +++--- lib/sqlite/lsqlite3.c | 4 ++-- src/BlockEntities/ChestEntity.cpp | 2 +- src/BlockEntities/FurnaceEntity.cpp | 2 +- src/BlockEntities/JukeboxEntity.cpp | 2 +- src/BlockEntities/NoteEntity.cpp | 2 +- src/BlockEntities/SignEntity.cpp | 2 +- src/Chunk.cpp | 2 +- src/ChunkMap.cpp | 2 +- src/Entities/Player.cpp | 2 +- src/Inventory.cpp | 2 +- src/Item.cpp | 2 +- src/WorldStorage/WSSCompact.cpp | 2 +- src/WorldStorage/WorldStorage.h | 2 +- 16 files changed, 31 insertions(+), 32 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index a963da098..8c452c1cc 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -143,9 +143,8 @@ endif INCLUDE = -I.\ -Isrc\ - -Isrc/items\ - -Isrce/blocks\ -Ilib\ + -Ilib/jsoncpp/include diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index f189da9d7..2a2ddb4ee 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -1,4 +1,4 @@ - + + + + + @@ -1271,14 +1279,6 @@ RelativePath="..\src\Entities\TNTEntity.cpp" > - - - - diff --git a/lib/luaexpat/lxplib.c b/lib/luaexpat/lxplib.c index a81a434e9..1a36104a8 100644 --- a/lib/luaexpat/lxplib.c +++ b/lib/luaexpat/lxplib.c @@ -9,10 +9,10 @@ #include #include -#include "lib/expat/expat.h" +#include "expat/expat.h" -#include "lib/lua/src/lua.h" -#include "lib/lua/src/lauxlib.h" +#include "lua/src/lua.h" +#include "lua/src/lauxlib.h" #include "lxplib.h" diff --git a/lib/sqlite/lsqlite3.c b/lib/sqlite/lsqlite3.c index f896daf23..cf2c62597 100644 --- a/lib/sqlite/lsqlite3.c +++ b/lib/sqlite/lsqlite3.c @@ -44,8 +44,8 @@ extern "C" { #include #define LUA_LIB -#include "lib/lua/src/lua.h" -#include "lib/lua/src/lauxlib.h" +#include "lua/src/lua.h" +#include "lua/src/lauxlib.h" #if LUA_VERSION_NUM > 501 // diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp index 918a1df2d..dfbe6ae87 100644 --- a/src/BlockEntities/ChestEntity.cpp +++ b/src/BlockEntities/ChestEntity.cpp @@ -5,7 +5,7 @@ #include "../Item.h" #include "../Entities/Player.h" #include "../UI/Window.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index 0123709bc..b1409f5cc 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -6,7 +6,7 @@ #include "../Entities/Player.h" #include "../Root.h" #include "../Chunk.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" diff --git a/src/BlockEntities/JukeboxEntity.cpp b/src/BlockEntities/JukeboxEntity.cpp index b01d01391..33042179d 100644 --- a/src/BlockEntities/JukeboxEntity.cpp +++ b/src/BlockEntities/JukeboxEntity.cpp @@ -3,7 +3,7 @@ #include "JukeboxEntity.h" #include "../World.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp index 0bae15777..9a33ead62 100644 --- a/src/BlockEntities/NoteEntity.cpp +++ b/src/BlockEntities/NoteEntity.cpp @@ -3,7 +3,7 @@ #include "NoteEntity.h" #include "../World.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp index 2e1fc1f8c..df8774377 100644 --- a/src/BlockEntities/SignEntity.cpp +++ b/src/BlockEntities/SignEntity.cpp @@ -4,7 +4,7 @@ // Implements the cSignEntity class representing a single sign in the world #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "jsoncpp/include/json/json.h" +#include "json/json.h" #include "SignEntity.h" #include "../Entities/Player.h" diff --git a/src/Chunk.cpp b/src/Chunk.cpp index c4aafaa8f..b3b24e339 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -34,7 +34,7 @@ #include "MobSpawner.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index c58b6b2fa..611e9f24e 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -21,7 +21,7 @@ #endif #include "zlib/zlib.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index a27405839..910613e98 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -25,7 +25,7 @@ #include "../Vector3f.h" #include "inifile/iniFile.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" #define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) diff --git a/src/Inventory.cpp b/src/Inventory.cpp index b9c7f4aaf..a9b4ab9c5 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -9,7 +9,7 @@ #include "Root.h" #include "World.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" #include "Items/ItemHandler.h" diff --git a/src/Item.cpp b/src/Item.cpp index 37277e8c8..196a260ef 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -2,7 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Item.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" #include "Items/ItemHandler.h" diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp index ced84836f..287938b24 100644 --- a/src/WorldStorage/WSSCompact.cpp +++ b/src/WorldStorage/WSSCompact.cpp @@ -7,7 +7,7 @@ #include "WSSCompact.h" #include "../World.h" #include "zlib/zlib.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" #include "../StringCompression.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DispenserEntity.h" diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index 720c04759..98e7e1686 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -16,7 +16,7 @@ #include "../ChunkDef.h" #include "../OSSupport/IsThread.h" -#include "jsoncpp/include/json/json.h" +#include "json/json.h" -- cgit v1.2.3 From 557ebc88d710d7b69865a226d0bd87de702afe93 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 27 Nov 2013 09:32:28 +0100 Subject: Removed faulty .gitattributes. They cause git to think that files have been modified although they have not, and makes it impossible to revert the files to un-changed form. See http://stackoverflow.com/questions/5009096/files-showing-as-modified-directly-after-git-clone 's first answer. --- .gitattributes | 63 ------------------------------------- lib/jsoncpp/src/lib_json/sconscript | 16 +++++----- 2 files changed, 8 insertions(+), 71 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1ff0c4230..000000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/lib/jsoncpp/src/lib_json/sconscript b/lib/jsoncpp/src/lib_json/sconscript index f6520d185..6e7c6c8a0 100644 --- a/lib/jsoncpp/src/lib_json/sconscript +++ b/lib/jsoncpp/src/lib_json/sconscript @@ -1,8 +1,8 @@ -Import( 'env buildLibrary' ) - -buildLibrary( env, Split( """ - json_reader.cpp - json_value.cpp - json_writer.cpp - """ ), - 'json' ) +Import( 'env buildLibrary' ) + +buildLibrary( env, Split( """ + json_reader.cpp + json_value.cpp + json_writer.cpp + """ ), + 'json' ) -- cgit v1.2.3 From 97b03df3724c58acdbbbdd91d6867722c64ae5f9 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 27 Nov 2013 14:24:36 +0100 Subject: Fixed Win nightbuild scripts after folder moves. --- Install/Zip2008_PDBs.list | 2 +- Nightbuild2008.cmd | 12 ++++++------ VC2008/MCServer.vcproj | 8 ++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Install/Zip2008_PDBs.list b/Install/Zip2008_PDBs.list index 578bcd181..b9c822c38 100644 --- a/Install/Zip2008_PDBs.list +++ b/Install/Zip2008_PDBs.list @@ -5,4 +5,4 @@ VC2008\Release\Lua\*.pdb VC2008\Release\ToLua\*.pdb VC2008\Release\webserver\*.pdb VC2008\Release\zlib\*.pdb -source\Bindings.* \ No newline at end of file +src\Bindings.* \ No newline at end of file diff --git a/Nightbuild2008.cmd b/Nightbuild2008.cmd index 6de8d9f67..ad0b415ba 100644 --- a/Nightbuild2008.cmd +++ b/Nightbuild2008.cmd @@ -45,9 +45,9 @@ echo Performing nightbuild of MC-Server set DONOTPAUSE=y :: Update the sources to the latest revision: -del source\Bindings.cpp -del source\Bindings.h -git checkout -- source\Bindings.* +del src\Bindings.cpp +del src\Bindings.h +git checkout -- src\Bindings.* git pull if errorlevel 1 goto haderror @@ -78,11 +78,11 @@ if exist %TAGFILE% ( :: Update the Bindings: -del source\Bindings.cpp -del source\Bindings.h +del src\Bindings.cpp +del src\Bindings.h echo Updating Lua bindings set ALLTOLUA_WAIT=N -cd source +cd src call AllToLua.bat cd .. diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index 2a2ddb4ee..e0c57d50d 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -924,6 +924,14 @@ RelativePath="..\src\Vector3i.h" > + + + + -- cgit v1.2.3 From ac793df2ab14a2b98504d9ea49bc4abcee828052 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 27 Nov 2013 14:26:28 +0100 Subject: Added missing files for Voronoi map generation. --- src/VoronoiMap.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/VoronoiMap.h | 45 ++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 src/VoronoiMap.cpp create mode 100644 src/VoronoiMap.h diff --git a/src/VoronoiMap.cpp b/src/VoronoiMap.cpp new file mode 100644 index 000000000..e95cb0bfa --- /dev/null +++ b/src/VoronoiMap.cpp @@ -0,0 +1,95 @@ + +// VoronoiMap.cpp + +// Implements the cVoronoiMap class that implements a Voronoi algorithm over a noise to produce a map + +#include "Globals.h" +#include "VoronoiMap.h" + + + + + +cVoronoiMap::cVoronoiMap(int a_Seed, int a_CellSize) : + m_Noise(a_Seed * 13 + 7), + m_CellSize(a_CellSize) +{ +} + + + + + +void cVoronoiMap::SetCellSize(int a_CellSize) +{ + m_CellSize = a_CellSize; +} + + + + + +int cVoronoiMap::GetValueAt(int a_X, int a_Y) +{ + int MinDist1, MinDist2; + return GetValueAt(a_X, a_Y, MinDist1, MinDist2); +} + + + + + +int cVoronoiMap::GetValueAt(int a_X, int a_Y, int & a_MinDist) +{ + int MinDist2; + return GetValueAt(a_X, a_Y, a_MinDist, MinDist2); +} + + + + + +int cVoronoiMap::GetValueAt(int a_X, int a_Y, int & a_MinDist1, int & a_MinDist2) +{ + // Note that due to historical reasons, the algorithm uses XZ coords, while the input uses XY coords. + // This is because the algorithm was first implemented directly in the biome generators which use MC coords. + + int CellX = a_X / m_CellSize; + int CellZ = a_Y / m_CellSize; + + // Get 5x5 neighboring cell seeds, compare distance to each. Return the value in the minumim-distance cell + int MinDist = m_CellSize * m_CellSize * 16; // There has to be a cell closer than this + int MinDist2 = MinDist; + int res = 0; // Will be overriden + for (int x = CellX - 2; x <= CellX + 2; x++) + { + int BaseX = x * m_CellSize; + for (int z = CellZ - 2; z < CellZ + 2; z++) + { + int OffsetX = (m_Noise.IntNoise3DInt(x, 16 * x + 32 * z, z) / 8) % m_CellSize; + int OffsetZ = (m_Noise.IntNoise3DInt(x, 32 * x - 16 * z, z) / 8) % m_CellSize; + int SeedX = BaseX + OffsetX; + int SeedZ = z * m_CellSize + OffsetZ; + + int Dist = (SeedX - a_X) * (SeedX - a_X) + (SeedZ - a_Y) * (SeedZ - a_Y); + if (Dist < MinDist) + { + MinDist2 = MinDist; + MinDist = Dist; + res = m_Noise.IntNoise3DInt(x, x - z + 1000, z); + } + else if (Dist < MinDist2) + { + MinDist2 = Dist; + } + } // for z + } // for x + + a_MinDist1 = MinDist; + a_MinDist2 = MinDist2; + return res; +} + + + + diff --git a/src/VoronoiMap.h b/src/VoronoiMap.h new file mode 100644 index 000000000..bcd37f9cf --- /dev/null +++ b/src/VoronoiMap.h @@ -0,0 +1,45 @@ + +// VoronoiMap.h + +// Declares the cVoronoiMap class that implements a Voronoi algorithm over a noise to produce a map + + + + + +#pragma once + +#include "Noise.h" + + + + + +class cVoronoiMap +{ +public: + cVoronoiMap(int a_Seed, int a_CellSize = 128); + + /// Sets the cell size used for generating the Voronoi seeds + void SetCellSize(int a_CellSize); + + /// Returns the value in the cell into which the specified point lies + int GetValueAt(int a_X, int a_Y); + + /// Returns the value in the cell into which the specified point lies, and the distance to the nearest Voronoi seed + int GetValueAt(int a_X, int a_Y, int & a_MinDistance); + + /// Returns the value in the cell into which the specified point lies, and the distances to the 2 nearest Voronoi seeds + int GetValueAt(int a_X, int a_Y, int & a_MinDistance1, int & a_MinDistance2); + +protected: + /// The noise used for generating Voronoi seeds + cNoise m_Noise; + + /// Size of the Voronoi cells (avg X/Y distance between the seeds) + int m_CellSize; +} ; + + + + -- cgit v1.2.3 From a5425e0557ff0e288cf709268397826431b3f389 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Wed, 27 Nov 2013 16:57:28 +0000 Subject: Removed the -Og option, which is rather new. --- GNUmakefile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 8c452c1cc..f83de0ac5 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -71,10 +71,9 @@ else # Since C code is used only for supporting libraries (zlib, lua), it is still Ofast-optimized ################ - CC_OPTIONS = -s -ggdb -g -D_DEBUG -O3 - -CXX_OPTIONS = -s -ggdb -g -D_DEBUG -Og -LNK_OPTIONS = -pthread -g -ggdb -Og +CC_OPTIONS = -s -ggdb -g -D_DEBUG -O3 +CXX_OPTIONS = -s -ggdb -g -D_DEBUG -O1 +LNK_OPTIONS = -pthread -g -ggdb -O1 BUILDDIR = build/debug/ endif endif -- cgit v1.2.3 From 3568adafc2117a453a272beec643d133550318d8 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Wed, 27 Nov 2013 16:59:58 +0000 Subject: Removed the -g option for release builds, as binaries were getting huge. --- GNUmakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index f83de0ac5..b5c3da081 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -47,8 +47,8 @@ ifeq ($(release),1) # release build - fastest run-time, no gdb support ################ -CC_OPTIONS = -g -O3 -DNDEBUG -CXX_OPTIONS = -g -O3 -DNDEBUG +CC_OPTIONS = -O3 -DNDEBUG +CXX_OPTIONS = -O3 -DNDEBUG LNK_OPTIONS = -pthread -O3 BUILDDIR = build/release/ -- cgit v1.2.3 From 596963743e4651d4ae7560bde336852034baa50f Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Wed, 27 Nov 2013 17:18:40 +0000 Subject: Fixed a load of issues, clang autodetection works now. This fixes issue #210. This also removes the disableasm option, so it would be wise to remove it from any scripts that use it. I also removed a random line in the middle of globals.h, why was it there? --- GNUmakefile | 25 ++++++------------------- src/Globals.h | 1 - 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index b5c3da081..4b4bc0386 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -74,7 +74,9 @@ else CC_OPTIONS = -s -ggdb -g -D_DEBUG -O3 CXX_OPTIONS = -s -ggdb -g -D_DEBUG -O1 LNK_OPTIONS = -pthread -g -ggdb -O1 + BUILDDIR = build/debug/ + endif endif @@ -86,11 +88,9 @@ CXX_OPTIONS += -Wall ################################################### # Fix Crypto++ warnings in clang -ifeq ($(shell $(CXX) --version 2>&1 | grep -i -c "clang version"),0) -CC_OPTIONS += -Wno-tautological-compare -CXX_OPTIONS += -Wno-tautological-compare -disableasm = 1 -disableofast = 1 +ifeq ($(shell $(CXX) --version 2>&1 | grep -i -c "clang version"),1) +CC_OPTIONS += -DCRYPTOPP_DISABLE_ASM +CXX_OPTIONS += -DCRYPTOPP_DISABLE_ASM endif @@ -125,23 +125,10 @@ endif -################################################### -# Clang doesn't seem to support CryptoPP's assembly mode, disable it for now (CryptoPP 5.6.2) - -ifeq ($(disableasm),1) - CC_OPTIONS += -DCRYPTOPP_DISABLE_ASM - CXX_OPTIONS += -DCRYPTOPP_DISABLE_ASM -endif - - - - - ################################################### # INCLUDE directories for MCServer -INCLUDE = -I.\ - -Isrc\ +INCLUDE = -Isrc\ -Ilib\ -Ilib/jsoncpp/include diff --git a/src/Globals.h b/src/Globals.h index ef79e4cf1..cb67d9fda 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -118,7 +118,6 @@ typedef unsigned short UInt16; #include #include #include - #include #include #include -- cgit v1.2.3 From 187a0dbaa2106b74c0e137ce4a883683b844d543 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Wed, 27 Nov 2013 17:54:23 +0000 Subject: Removed some useless options. --- GNUmakefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 4b4bc0386..e0e606d90 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -59,8 +59,8 @@ ifeq ($(profile),1) # profile build - a release build with symbols and profiling engine built in ################ -CC_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG -CXX_OPTIONS = -s -g -ggdb -O3 -pg -DNDEBUG +CC_OPTIONS = -g -ggdb -O3 -pg -DNDEBUG +CXX_OPTIONS = -g -ggdb -O3 -pg -DNDEBUG LNK_OPTIONS = -pthread -ggdb -O3 -pg BUILDDIR = build/profile/ @@ -71,8 +71,8 @@ else # Since C code is used only for supporting libraries (zlib, lua), it is still Ofast-optimized ################ -CC_OPTIONS = -s -ggdb -g -D_DEBUG -O3 -CXX_OPTIONS = -s -ggdb -g -D_DEBUG -O1 +CC_OPTIONS = -ggdb -g -D_DEBUG -O3 +CXX_OPTIONS = -ggdb -g -D_DEBUG -O1 LNK_OPTIONS = -pthread -g -ggdb -O1 BUILDDIR = build/debug/ -- cgit v1.2.3 From bec27617a2d335cdb4a1ae350962ca6f92489471 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 27 Nov 2013 21:42:13 +0100 Subject: Voronoi-related biomegens use the new cVoronoiMap class. --- src/Generating/BioGen.cpp | 56 +++++++++-------------------------------------- src/Generating/BioGen.h | 25 +++++++++++++++------ src/VoronoiMap.cpp | 2 +- 3 files changed, 29 insertions(+), 54 deletions(-) diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 1cd7c70e7..8b2b227a8 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -267,7 +267,8 @@ void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & int AbsoluteZ = BaseZ + z; for (int x = 0; x < cChunkDef::Width; x++) { - cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(BaseX + x, AbsoluteZ)); + int VoronoiCellValue = m_Voronoi.GetValueAt(BaseX + x, AbsoluteZ) / 8; + cChunkDef::SetBiome(a_BiomeMap, x, z, m_Biomes[VoronoiCellValue % m_BiomesCount]); } // for x } // for z } @@ -279,45 +280,8 @@ void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & void cBioGenVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) { super::InitializeBiomeGen(a_IniFile); - m_CellSize = a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64); - AString Biomes = a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", ""); - InitializeBiomes(Biomes); -} - - - - - -EMCSBiome cBioGenVoronoi::VoronoiBiome(int a_BlockX, int a_BlockZ) -{ - int CellX = a_BlockX / m_CellSize; - int CellZ = a_BlockZ / m_CellSize; - - // Note that Noise values need to be divided by 8 to gain a uniform modulo-2^n distribution - - // Get 5x5 neighboring cell seeds, compare distance to each. Return the biome in the minumim-distance cell - int MinDist = m_CellSize * m_CellSize * 16; // There has to be a cell closer than this - EMCSBiome res = biPlains; // Will be overriden - for (int x = CellX - 2; x <= CellX + 2; x++) - { - int BaseX = x * m_CellSize; - for (int z = CellZ - 2; z < CellZ + 2; z++) - { - int OffsetX = (m_Noise.IntNoise3DInt(x, 16 * x + 32 * z, z) / 8) % m_CellSize; - int OffsetZ = (m_Noise.IntNoise3DInt(x, 32 * x - 16 * z, z) / 8) % m_CellSize; - int SeedX = BaseX + OffsetX; - int SeedZ = z * m_CellSize + OffsetZ; - - int Dist = (SeedX - a_BlockX) * (SeedX - a_BlockX) + (SeedZ - a_BlockZ) * (SeedZ - a_BlockZ); - if (Dist < MinDist) - { - MinDist = Dist; - res = m_Biomes[(m_Noise.IntNoise3DInt(x, x - z + 1000, z) / 8) % m_BiomesCount]; - } - } // for z - } // for x - - return res; + m_Voronoi.SetCellSize(a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64)); + InitializeBiomes (a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", "")); } @@ -348,8 +312,8 @@ void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::B int AbsoluteZ = BaseZ + z; for (int x = 0; x < cChunkDef::Width; x++) { - // Distort(BaseX + x, AbsoluteZ, DistX, DistZ); - cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(DistortX[x][z], DistortZ[x][z])); + int VoronoiCellValue = m_Voronoi.GetValueAt(DistortX[x][z], DistortZ[x][z]) / 8; + cChunkDef::SetBiome(a_BiomeMap, x, z, m_Biomes[VoronoiCellValue % m_BiomesCount]); } // for x } // for z } @@ -360,10 +324,10 @@ void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::B void cBioGenDistortedVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) { - // Do NOT call super::InitializeBiomeGen(), as it would try to read Voronoi params instead of DistortedVoronoi params - m_CellSize = a_IniFile.GetValueSetI("Generator", "DistortedVoronoiCellSize", 96); - AString Biomes = a_IniFile.GetValueSet ("Generator", "DistortedVoronoiBiomes", ""); - InitializeBiomes(Biomes); + super::InitializeBiomeGen(a_IniFile); + m_CellSize = a_IniFile.GetValueSetI("Generator", "DistortedVoronoiCellSize", 96); + m_Voronoi.SetCellSize(m_CellSize); + InitializeBiomes(a_IniFile.GetValueSet("Generator", "DistortedVoronoiBiomes", "")); } diff --git a/src/Generating/BioGen.h b/src/Generating/BioGen.h index bc70bfab2..892168bb6 100644 --- a/src/Generating/BioGen.h +++ b/src/Generating/BioGen.h @@ -16,6 +16,7 @@ Interfaces to the various biome generators: #include "ComposableGenerator.h" #include "../Noise.h" +#include "../VoronoiMap.h" @@ -123,15 +124,13 @@ class cBioGenVoronoi : public: cBioGenVoronoi(int a_Seed) : - m_Noise(a_Seed) + m_Voronoi(a_Seed) { } protected: - int m_CellSize; + cVoronoiMap m_Voronoi; - cNoise m_Noise; - // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; @@ -144,16 +143,28 @@ protected: class cBioGenDistortedVoronoi : - public cBioGenVoronoi + public cBiomeGenList { - typedef cBioGenVoronoi super; + typedef cBiomeGenList super; + public: cBioGenDistortedVoronoi(int a_Seed) : - cBioGenVoronoi(a_Seed) + m_Noise(a_Seed), + m_Voronoi(a_Seed), + m_CellSize(0) { } protected: + /// Noise used for the distortion + cNoise m_Noise; + + /// The underlying Voronoi map of the biomes + cVoronoiMap m_Voronoi; + + /// Size of the Voronoi cells, also used for distortion amplitude + int m_CellSize; + // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; diff --git a/src/VoronoiMap.cpp b/src/VoronoiMap.cpp index e95cb0bfa..7a36edebc 100644 --- a/src/VoronoiMap.cpp +++ b/src/VoronoiMap.cpp @@ -11,7 +11,7 @@ cVoronoiMap::cVoronoiMap(int a_Seed, int a_CellSize) : - m_Noise(a_Seed * 13 + 7), + m_Noise(a_Seed), m_CellSize(a_CellSize) { } -- cgit v1.2.3